diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/BuiltinsOps.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/BuiltinsOps.cs new file mode 100644 index 0000000000..939b10532d --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/BuiltinsOps.cs @@ -0,0 +1,382 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Runtime.CompilerServices; +using System.Dynamic; +using IronRuby.Builtins; +using IronRuby.Runtime; +using Microsoft.Scripting; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using System.Runtime.InteropServices; + +namespace IronRuby.StandardLibrary.Yaml { + + [RubyClass(Extends = typeof(object))] + public static class YamlObjectOps { + + [RubyMethod("to_yaml_properties")] + public static RubyArray/*!*/ ToYamlProperties(RubyContext/*!*/ context, object self) { + return ArrayOps.SortInPlace(context, null, KernelOps.InstanceVariables(context, self)); + } + + [RubyMethod("to_yaml_style")] + public static object ToYamlStyle(object self) { + return null; + } + + [RubyMethod("to_yaml_node")] + public static object ToYamlProperties(object self, [NotNull]RubyRepresenter/*!*/ rep) { + Hash map = new Hash(rep.Context); + RubyRepresenter.AddYamlProperties(rep.Context, self, map); + return rep.Map(self, map); + } + + [RubyMethod("to_yaml")] + public static object ToYaml(RubyContext/*!*/ context, object self, params object[] args) { + return RubyYaml.DumpAll(context, new object[] { self }, null); + } + + [RubyMethod("taguri")] + public static MutableString TagUri(RubyContext/*!*/ context, object self) { + MutableString str = MutableString.Create("!ruby/object:"); + str.Append(RubyUtils.GetClassName(context, self)); + return str; + } + } + + [RubyClass(Extends = typeof(RubyClass))] + public static class YamlClassOps { + [RubyMethod("to_yaml_node")] + public static Node ToYamlNode(RubyContext/*!*/ context, object self, RubyRepresenter rep) { + throw RubyExceptions.CreateTypeError("can't dump anonymous class " + RubyUtils.GetClassName(context, self)); + } + } + + [RubyClass(Extends = typeof(RubyModule))] + public static class YamlModuleOps { + [RubyMethod("yaml_as")] + public static object YamlAs(RubyScope/*!*/ scope, RubyModule/*!*/ self, object tag) { + RubyModule yamlModule; + scope.RubyContext.TryGetModule(scope.GlobalScope, "YAML", out yamlModule); + return RubyYaml.TagClass(yamlModule, tag, self); + } + } + + [RubyClass(Extends = typeof(Hash))] + public static class YamlHashOps { + [RubyMethod("to_yaml_node")] + public static Node ToYamlNode(Hash/*!*/ self, [NotNull]RubyRepresenter/*!*/ rep) { + return rep.Map(self, self); + } + + [RubyMethod("taguri")] + public static MutableString TagUri(RubyContext/*!*/ context, object self) { + return MutableString.Create("tag:yaml.org,2002:map"); + } + } + + [RubyModule(Extends = typeof(RubyArray))] + public static class YamlArrayOps { + [RubyMethod("to_yaml_node")] + public static Node ToYamlNode(RubyContext/*!*/ context, RubyArray/*!*/ self, [NotNull]RubyRepresenter/*!*/ rep) { + return rep.Sequence(self, self); + } + + [RubyMethod("taguri")] + public static MutableString TagUri(RubyContext/*!*/ context, object self) { + return MutableString.Create("tag:yaml.org,2002:seq"); + } + } + + [RubyModule(Extends = typeof(RubyStruct))] + public static class YamlStructOps { + private static readonly CallSite> _Members = CallSite>.Create(LibrarySites.InstanceCallAction("members")); + private static readonly CallSite> _Values = CallSite>.Create(LibrarySites.InstanceCallAction("values")); + + [RubyMethod("to_yaml_node")] + public static Node ToYamlNode(RubyStruct/*!*/ self, [NotNull]RubyRepresenter/*!*/ rep) { + RubyContext context = self.Class.Context; + RubyArray members = _Members.Target(_Members, context, self); + RubyArray values = _Values.Target(_Values, context, self); + + if (members.Count != values.Count) { + throw new ArgumentException("Struct values and members returned arrays of different lengths"); + } + + Hash map = new Hash(self.Class.Context); + for (int i = 0; i < members.Count; i++) { + IDictionaryOps.SetElement(context, map, members[i], values[i]); + } + RubyRepresenter.AddYamlProperties(context, self, map); + return rep.Map(self, map); + } + + [RubyMethod("taguri")] + public static MutableString TagUri(RubyStruct/*!*/ self) { + MutableString str = MutableString.Create("tag:ruby.yaml.org,2002:struct:"); + string name = self.Class.Name; + string structPrefix = "Struct::"; + if (name.StartsWith(structPrefix)) { + name = name.Substring(structPrefix.Length); + } + return str.Append(name); + } + } + + [RubyModule(Extends = typeof(Exception))] + public static class YamlExceptionOps { + private static readonly CallSite> _Message = CallSite>.Create(LibrarySites.InstanceCallAction("message")); + + [RubyMethod("to_yaml_node")] + public static Node ToYamlNode(Exception/*!*/ self, [NotNull]RubyRepresenter/*!*/ rep) { + Hash map = new Hash(rep.Context); + map.Add(MutableString.Create("message"), _Message.Target(_Message, rep.Context, self)); + RubyRepresenter.AddYamlProperties(rep.Context, self, map); + return rep.Map(self, map); + } + + [RubyMethod("taguri")] + public static MutableString TagUri(RubyContext/*!*/ context, object self) { + MutableString str = MutableString.Create("!ruby/exception:"); + str.Append(RubyUtils.GetClassName(context, self)); + return str; + } + } + + [RubyModule(Extends = typeof(MutableString))] + public static class YamlStringOps { + [RubyMethod("is_complex_yaml?")] + public static bool IsComplexYaml(RubyContext/*!*/ context, MutableString/*!*/ self) { + return RubyOps.IsTrue(RubyRepresenter.ToYamlStyle(context, self)) || + RubyRepresenter.ToYamlProperties(context, self).Count == 0 || + AFTER_NEWLINE(self.ConvertToString()); + } + + // True if has a newline & something is after it + private static bool AFTER_NEWLINE(string str) { + int i = str.IndexOf('\n'); + return i >= 0 && i < str.Length - 1; + } + + private static readonly CallSite> _Empty = CallSite>.Create(LibrarySites.InstanceCallAction("empty?")); + private static readonly CallSite> _IsBinaryData = CallSite>.Create(LibrarySites.InstanceCallAction("is_binary_data?")); + + [RubyMethod("is_binary_data?")] + public static object IsBinaryData(RubyContext/*!*/ context, MutableString/*!*/ self) { + if (RubyOps.IsTrue(_Empty.Target(_Empty, context, self))) { + return null; + } + // TODO: should be self.IndexOf(0)? + return self.IndexOf('\0') != -1; + } + + [RubyMethod("to_yaml_node")] + public static Node ToYamlNode(MutableString/*!*/ self, [NotNull]RubyRepresenter/*!*/ rep) { + if (RubyOps.IsTrue(_IsBinaryData.Target(_IsBinaryData, rep.Context, self))) { + return rep.BaseCreateNode(self.ConvertToBytes()); + } + + string str = self.ConvertToString(); + RubyArray props = RubyRepresenter.ToYamlProperties(rep.Context, self); + if (props.Count == 0) { + MutableString taguri = RubyRepresenter.TagUri(rep.Context, self); + + char style = (char)0; + if (str.StartsWith(":")) { + style = '"'; + } else { + MutableString styleStr = RubyRepresenter.ToYamlStyle(rep.Context, self) as MutableString; + if (styleStr != null && styleStr.Length > 0) { + style = styleStr.GetChar(0); + } + } + + return rep.Scalar(taguri != null ? taguri.ConvertToString() : "", str, style); + } + + Hash map = new Hash(rep.Context); + map.Add(MutableString.Create("str"), str); + RubyRepresenter.AddYamlProperties(rep.Context, self, map, props); + return rep.Map(self, map); + } + + [RubyMethod("taguri")] + public static MutableString TagUri(object self) { + return MutableString.Create("tag:yaml.org,2002:str"); + } + } + + [RubyModule(Extends = typeof(Integer))] + public static class YamlIntegerOps { + [RubyMethod("to_yaml_node")] + public static Node ToYaml(object self, [NotNull]RubyRepresenter/*!*/ rep) { + return rep.Scalar(self, RubySites.ToS(rep.Context, self)); + } + + [RubyMethod("taguri")] + public static MutableString TagUri(object self) { + return MutableString.Create("tag:yaml.org,2002:int"); + } + } + + [RubyModule(Extends = typeof(BigInteger))] + public static class YamlBigIntegerOps { + [RubyMethod("to_yaml_node")] + public static Node ToYaml([NotNull]BigInteger self, [NotNull]RubyRepresenter/*!*/ rep) { + return YamlIntegerOps.ToYaml(self, rep); + } + + [RubyMethod("taguri")] + public static MutableString TagUri([NotNull]BigInteger self) { + return MutableString.Create("tag:yaml.org,2002:int:Bignum"); + } + } + + [RubyModule(Extends = typeof(double))] + public static class YamlDoubleOps { + [RubyMethod("to_yaml_node")] + public static Node/*!*/ ToYaml(double self, [NotNull]RubyRepresenter/*!*/ rep) { + MutableString str = RubySites.ToS(rep.Context, self); + if (str != null) { + if (str.Equals("Infinity")) { + str = MutableString.Create(".Inf"); + } else if (str.Equals("-Infinity")) { + str = MutableString.Create("-.Inf"); + } else if (str.Equals("NaN")) { + str = MutableString.Create(".NaN"); + } + } + return rep.Scalar(self, str); + } + + [RubyMethod("taguri")] + public static MutableString TagUri(double self) { + return MutableString.Create("tag:yaml.org,2002:float"); + } + } + + [RubyModule(Extends = typeof(Range))] + public static class YamlRangeOps { + private static readonly CallSite> _Begin = CallSite>.Create(LibrarySites.InstanceCallAction("begin")); + private static readonly CallSite> _End = CallSite>.Create(LibrarySites.InstanceCallAction("end")); + private static readonly CallSite> _ExcludeEnd = CallSite>.Create(LibrarySites.InstanceCallAction("exclude_end?")); + + [RubyMethod("to_yaml_node")] + public static Node ToYaml(object self, [NotNull]RubyRepresenter/*!*/ rep) { + Hash map = new Hash(rep.Context); + map.Add(MutableString.Create("begin"), _Begin.Target(_Begin, rep.Context, self)); + map.Add(MutableString.Create("end"), _End.Target(_End, rep.Context, self)); + map.Add(MutableString.Create("excl"), _ExcludeEnd.Target(_ExcludeEnd, rep.Context, self)); + RubyRepresenter.AddYamlProperties(rep.Context, self, map); + return rep.Map(self, map); + } + + [RubyMethod("taguri")] + public static MutableString TagUri([NotNull]Range self) { + return MutableString.Create("tag:ruby.yaml.org,2002:range"); + } + } + + [RubyModule(Extends = typeof(RubyRegex))] + public static class YamlRegexpOps { + [RubyMethod("to_yaml_node")] + public static Node ToYaml(object self, [NotNull]RubyRepresenter/*!*/ rep) { + return rep.Scalar(self, RubySites.Inspect(rep.Context, self)); + } + + [RubyMethod("taguri")] + public static MutableString TagUri([NotNull]RubyRegex self) { + return MutableString.Create("tag:ruby.yaml.org,2002:regexp"); + } + } + + [RubyModule(Extends = typeof(DateTime))] + public static class DateTimeOps { + [RubyMethod("to_yaml_node")] + public static Node ToYaml(DateTime self, [NotNull]RubyRepresenter/*!*/ rep) { + string format = (self.Millisecond != 0) ? "yyyy-MM-dd HH:mm:ss.fffffff K" : "yyyy-MM-dd HH:mm:ss K"; + return rep.Scalar(self, MutableString.Create(self.ToString(format))); + } + + [RubyMethod("taguri")] + public static MutableString TagUri(DateTime self) { + return MutableString.Create("tag:yaml.org,2002:timestamp"); + } + } + + [RubyModule(Extends = typeof(SymbolId))] + public static class YamlSymbolOps { + [RubyMethod("to_yaml_node")] + public static Node ToYaml(object self, [NotNull]RubyRepresenter/*!*/ rep) { + return rep.Scalar(self, RubySites.Inspect(rep.Context, self)); + } + + [RubyMethod("taguri")] + public static MutableString TagUri(object self) { + return MutableString.Create("tag:yaml.org,2002:str"); + } + } + + [RubyModule(Extends = typeof(TrueClass))] + public static class YamlTrueOps { + [RubyMethod("to_yaml_node")] + public static Node ToYaml(object self, [NotNull]RubyRepresenter/*!*/ rep) { + return rep.Scalar(self, RubySites.ToS(rep.Context, self)); + } + [RubyMethod("taguri")] + public static MutableString TagUri(object self) { + return MutableString.Create("tag:yaml.org,2002:bool"); + } + } + + [RubyModule(Extends = typeof(FalseClass))] + public static class YamlFalseOps { + [RubyMethod("to_yaml_node")] + public static Node ToYaml(object self, [NotNull]RubyRepresenter/*!*/ rep) { + return rep.Scalar(self, RubySites.ToS(rep.Context, self)); + } + + [RubyMethod("taguri")] + public static MutableString TagUri(object self) { + return MutableString.Create("tag:yaml.org,2002:bool"); + } + } + + [RubyModule(Extends = typeof(Null))] + public static class YamlNilOps { + [RubyMethod("to_yaml_node")] + public static Node ToYaml(object self, [NotNull]RubyRepresenter/*!*/ rep) { + return rep.Scalar(self, null); + } + + [RubyMethod("taguri")] + public static MutableString TagUri(object self) { + return MutableString.Create("tag:yaml.org,2002:null"); + } + } + + [RubyClass(Extends = typeof(Node))] + public static class YamlNodeOps { + [RubyMethod("transform")] + public static object Transform(RubyScope/*!*/ scope, Node/*!*/ self) { + return new RubyConstructor(scope, new SimpleNodeProvider(self)).GetData(); + } + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/BaseConstructor.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/BaseConstructor.cs new file mode 100644 index 0000000000..d7566e8b6b --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/BaseConstructor.cs @@ -0,0 +1,346 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using IronRuby.Builtins; +using IronRuby.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.StandardLibrary.Yaml { + + public delegate void RecursiveFixer(Node node, object real); + public delegate object YamlConstructor(IConstructor self, Node node); + public delegate object YamlMultiConstructor(IConstructor self, string pref, Node node); + + public interface IConstructor : IEnumerable { + bool CheckData(); + object GetData(); + object ConstructDocument(Node node); + object ConstructObject(Node node); + object ConstructPrimitive(Node node); + object ConstructScalar(Node node); + object ConstructPrivateType(Node node); + RubyArray ConstructSequence(Node node); + Hash ConstructMapping(Node node); + object ConstructPairs(Node node); + void DoRecursionFix(Node node, object obj); + void AddFixer(Node node, RecursiveFixer fixer); + + RubyScope/*!*/ Scope { get; } + } + + public class BaseConstructor : IConstructor { + private readonly static Dictionary _yamlConstructors = new Dictionary(); + private readonly static Dictionary _yamlMultiConstructors = new Dictionary(); + private readonly static Dictionary _yamlMultiRegexps = new Dictionary(); + + private readonly Dictionary>/*!*/ _recursiveObjects = new Dictionary>(); + private readonly NodeProvider/*!*/ _nodeProvider; + private readonly RubyScope/*!*/ _scope; + + public BaseConstructor(NodeProvider/*!*/ nodeProvider, RubyScope/*!*/ scope) { + Assert.NotNull(nodeProvider, scope); + _nodeProvider = nodeProvider; + _scope = scope; + } + + public RubyScope/*!*/ Scope { + get { return _scope; } + } + + public virtual YamlConstructor GetYamlConstructor(string key) { + YamlConstructor result; + _yamlConstructors.TryGetValue(key, out result); + return result; + } + + public virtual YamlMultiConstructor GetYamlMultiConstructor(string key) { + YamlMultiConstructor result; + _yamlMultiConstructors.TryGetValue(key, out result); + return result; + } + + public virtual Regex GetYamlMultiRegexp(string key) { + Regex result; + _yamlMultiRegexps.TryGetValue(key, out result); + return result; + } + + public virtual ICollection GetYamlMultiRegexps() { + return _yamlMultiRegexps.Keys; + } + + public static void AddConstructor(string tag, YamlConstructor ctor) { + _yamlConstructors.Add(tag,ctor); + } + + public static void AddMultiConstructor(string tagPrefix, YamlMultiConstructor ctor) { + _yamlMultiConstructors.Add(tagPrefix,ctor); + _yamlMultiRegexps.Add(tagPrefix, new Regex("^" + tagPrefix, RegexOptions.Compiled)); + } + + public bool CheckData() { + return _nodeProvider.CheckNode(); + } + + public object GetData() { + if (_nodeProvider.CheckNode()) { + Node node = _nodeProvider.GetNode(); + if(null != node) { + return ConstructDocument(node); + } + } + return null; + } + + public object ConstructDocument(Node node) { + object data = ConstructObject(node); + _recursiveObjects.Clear(); + return data; + } + + private YamlConstructor yamlMultiAdapter(YamlMultiConstructor ctor, string prefix) { + return delegate(IConstructor self, Node node) { return ctor(self, prefix, node); }; + } + + public static Node GetNullNode() { + return new ScalarNode("tag:yaml.org,2002:null", null, (char)0); + } + + public object ConstructObject(Node node) { + if (node == null) { + node = GetNullNode(); + } + if(_recursiveObjects.ContainsKey(node)) { + return new LinkNode(node); + } + _recursiveObjects.Add(node, new List()); + YamlConstructor ctor = GetYamlConstructor(node.Tag); + if (ctor == null) { + bool through = true; + foreach (string tagPrefix in GetYamlMultiRegexps()) { + Regex reg = GetYamlMultiRegexp(tagPrefix); + if (reg.IsMatch(node.Tag)) { + string tagSuffix = node.Tag.Substring(tagPrefix.Length); + ctor = yamlMultiAdapter(GetYamlMultiConstructor(tagPrefix), tagSuffix); + through = false; + break; + } + } + if (through) { + YamlMultiConstructor xctor = GetYamlMultiConstructor(""); + if(null != xctor) { + ctor = yamlMultiAdapter(xctor,node.Tag); + } else { + ctor = GetYamlConstructor(""); + if(ctor == null) { + ctor = CONSTRUCT_PRIMITIVE; + } + } + } + } + object data = ctor(this, node); + DoRecursionFix(node,data); + return data; + } + + public void DoRecursionFix(Node node, object obj) { + List ll; + if (_recursiveObjects.TryGetValue(node, out ll)) { + _recursiveObjects.Remove(node); + foreach (RecursiveFixer fixer in ll) { + fixer(node, obj); + } + } + } + + public object ConstructPrimitive(Node node) { + if(node is ScalarNode) { + return ConstructScalar(node); + } else if(node is SequenceNode) { + return ConstructSequence(node); + } else if(node is MappingNode) { + return ConstructMapping(node); + } else { + Console.Error.WriteLine(node.Tag); + } + return null; + } + + public object ConstructScalar(Node node) { + ScalarNode scalar = node as ScalarNode; + if (scalar == null) { + MappingNode mapNode = node as MappingNode; + if (mapNode != null) { + foreach (KeyValuePair entry in mapNode.Nodes) { + if ("tag:yaml.org,2002:value" == entry.Key.Tag) { + return ConstructScalar(entry.Value); + } + } + } + throw new ConstructorException("expected a scalar or mapping node, but found: " + node); + } + string value = scalar.Value; + if (value.Length > 1 && value[0] == ':' && scalar.Style == '\0') { + return SymbolTable.StringToId(value.Substring(1)); + } + return value; + } + + public object ConstructPrivateType(Node node) { + object val = null; + ScalarNode scalar = node as ScalarNode; + if (scalar != null) { + val = scalar.Value; + } else if (node is MappingNode) { + val = ConstructMapping(node); + } else if (node is SequenceNode) { + val = ConstructSequence(node); + } else { + throw new ConstructorException("unexpected node type: " + node); + } + return new PrivateType(node.Tag,val); + } + + public RubyArray ConstructSequence(Node sequenceNode) { + SequenceNode seq = sequenceNode as SequenceNode; + if(seq == null) { + throw new ConstructorException("expected a sequence node, but found: " + sequenceNode); + } + IList @internal = seq.Nodes; + RubyArray val = new RubyArray(@internal.Count); + foreach (Node node in @internal) { + object obj = ConstructObject(node); + LinkNode linkNode = obj as LinkNode; + if (linkNode != null) { + int ix = val.Count; + AddFixer(linkNode.Linked, delegate (Node n, object real) { + val[ix] = real; + }); + } + val.Add(obj); + } + return val; + } + + //TODO: remove Ruby-specific stuff from this layer + public Hash ConstructMapping(Node mappingNode) { + MappingNode map = mappingNode as MappingNode; + if (map == null) { + throw new ConstructorException("expected a mapping node, but found: " + mappingNode); + } + Hash mapping = new Hash(_scope.RubyContext); + LinkedList merge = null; + foreach (KeyValuePair entry in map.Nodes) { + Node key_v = entry.Key; + Node value_v = entry.Value; + + if (key_v.Tag == "tag:yaml.org,2002:merge") { + if (merge != null) { + throw new ConstructorException("while constructing a mapping: found duplicate merge key"); + } + SequenceNode sequence; + merge = new LinkedList(); + if (value_v is MappingNode) { + merge.AddLast(ConstructMapping(value_v)); + } else if ((sequence = value_v as SequenceNode) != null) { + foreach (Node subNode in sequence.Nodes) { + if (!(subNode is MappingNode)) { + throw new ConstructorException("while constructing a mapping: expected a mapping for merging, but found: " + subNode); + } + merge.AddFirst(ConstructMapping(subNode)); + } + } else { + throw new ConstructorException("while constructing a mapping: expected a mapping or list of mappings for merging, but found: " + value_v); + } + } else if (key_v.Tag == "tag:yaml.org,2002:value") { + if(mapping.ContainsKey("=")) { + throw new ConstructorException("while construction a mapping: found duplicate value key"); + } + mapping.Add("=", ConstructObject(value_v)); + } else { + object kk = ConstructObject(key_v); + object vv = ConstructObject(value_v); + LinkNode linkNode = vv as LinkNode; + if (linkNode != null) { + AddFixer(linkNode.Linked, delegate (Node node, object real) { + IDictionaryOps.SetElement(_scope.RubyContext, mapping, kk, real); + }); + } + IDictionaryOps.SetElement(_scope.RubyContext, mapping, kk, vv); + } + } + if (null != merge) { + merge.AddLast(mapping); + mapping = new Hash(_scope.RubyContext); + foreach (Hash m in merge) { + foreach (KeyValuePair e in m) { + IDictionaryOps.SetElement(_scope.RubyContext, mapping, e.Key, e.Value); + } + } + } + return mapping; + } + + public void AddFixer(Node node, RecursiveFixer fixer) { + List ll; + if (!_recursiveObjects.TryGetValue(node, out ll)) { + _recursiveObjects.Add(node, ll = new List()); + } + ll.Add(fixer); + } + + public object ConstructPairs(Node mappingNode) { + MappingNode map = mappingNode as MappingNode; + if (map == null) { + throw new ConstructorException("expected a mapping node, but found: " + mappingNode); + } + + List result = new List(); + foreach (KeyValuePair entry in map.Nodes) { + result.Add(new object[] { ConstructObject(entry.Key), ConstructObject(entry.Value) }); + } + return result; + } + + public static YamlConstructor CONSTRUCT_PRIMITIVE = delegate(IConstructor self, Node node) { return self.ConstructPrimitive(node); }; + public static YamlConstructor CONSTRUCT_SCALAR = delegate(IConstructor self, Node node) { return self.ConstructScalar(node); }; + public static YamlConstructor CONSTRUCT_PRIVATE = delegate(IConstructor self, Node node) { return self.ConstructPrivateType(node); }; + public static YamlConstructor CONSTRUCT_SEQUENCE = delegate(IConstructor self, Node node) { return self.ConstructSequence(node); }; + public static YamlConstructor CONSTRUCT_MAPPING = delegate(IConstructor self, Node node) { return self.ConstructMapping(node); }; + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + while (CheckData()) { + yield return GetData(); + } + } + + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + #endregion + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Composer.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Composer.cs new file mode 100644 index 0000000000..b73342612f --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Composer.cs @@ -0,0 +1,135 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections.Generic; + +namespace IronRuby.StandardLibrary.Yaml { + + public class Composer : NodeProvider { + private readonly Parser _parser; + private readonly Dictionary _anchors; + + public Composer(Parser parser) { + _parser = parser; + _anchors = new Dictionary(); + } + + private Node ComposeDocument() { + if (_parser.PeekEvent() is StreamStartEvent) { + //Drop STREAM-START event + _parser.GetEvent(); + } + //Drop DOCUMENT-START event + _parser.GetEvent(); + Node node = ComposeNode(null, null); + //Drop DOCUMENT-END event + _parser.GetEvent(); + this._anchors.Clear(); + return node; + } + + private void AddAnchor(string anchor, Node node) { + if (_anchors.ContainsKey(anchor)) { + _anchors[anchor] = node; + } else { + _anchors.Add(anchor, node); + } + } + + private Node ComposeNode(Node parent, object index) { + Node result; + YamlEvent @event = _parser.PeekEvent(); + NodeEvent nodeEvent = @event as NodeEvent; + + string anchor = (nodeEvent != null) ? nodeEvent.Anchor : null; + + if (nodeEvent is AliasEvent) { + _parser.GetEvent(); + if (!_anchors.TryGetValue(anchor, out result)) { + throw new ComposerException("found undefined alias: " + anchor); + } + return result; + } + + result = null; + //_resolver.descendResolver(parent, index); + if (@event is ScalarEvent) { + ScalarEvent ev = (ScalarEvent)_parser.GetEvent(); + string tag = ev.Tag; + if (tag == null || tag == "!") { + tag = Resolver.Resolve(typeof(ScalarNode), ev.Value, ev.Implicit); + } + result = new ScalarNode(tag, ev.Value, ev.Style); + if (null != anchor) { + AddAnchor(anchor, result); + } + } else if (@event is SequenceStartEvent) { + SequenceStartEvent start = (SequenceStartEvent)_parser.GetEvent(); + string tag = start.Tag; + if (tag == null || tag == "!") { + tag = Resolver.Resolve(typeof(SequenceNode), null, start.Implicit); + } + SequenceNode seqResult = new SequenceNode(tag, new List(), start.FlowStyle); + result = seqResult; + if (null != anchor) { + AddAnchor(anchor, seqResult); + } + int ix = 0; + while (!(_parser.PeekEvent() is SequenceEndEvent)) { + seqResult.Nodes.Add(ComposeNode(seqResult, ix++)); + } + _parser.GetEvent(); + } else if (@event is MappingStartEvent) { + MappingStartEvent start = (MappingStartEvent)_parser.GetEvent(); + string tag = start.Tag; + if (tag == null || tag == "!") { + tag = Resolver.Resolve(typeof(MappingNode), null, start.Implicit); + } + MappingNode mapResult = new MappingNode(tag, new Dictionary(), start.FlowStyle); + result = mapResult; + if (null != anchor) { + AddAnchor(anchor, result); + } + while (!(_parser.PeekEvent() is MappingEndEvent)) { + YamlEvent key = _parser.PeekEvent(); + Node itemKey = ComposeNode(mapResult, null); + Node composed = ComposeNode(mapResult, itemKey); + if (!mapResult.Nodes.ContainsKey(itemKey)) { + mapResult.Nodes.Add(itemKey, composed); + } + } + _parser.GetEvent(); + } + //_resolver.ascendResolver(); + return result; + } + + #region NodeProvider Members + + public override bool CheckNode() { + return !(_parser.PeekEvent() is StreamEndEvent); + } + + public override Node GetNode() { + return CheckNode() ? ComposeDocument() : (Node)null; + } + + #endregion + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Constructor.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Constructor.cs new file mode 100644 index 0000000000..13944b367d --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Constructor.cs @@ -0,0 +1,73 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.Yaml { + + public class Constructor : SafeConstructor { + private readonly static Dictionary _yamlConstructors = new Dictionary(); + private readonly static Dictionary _yamlMultiConstructors = new Dictionary(); + private readonly static Dictionary _yamlMultiRegexps = new Dictionary(); + + public override YamlConstructor GetYamlConstructor(string key) { + YamlConstructor result; + if (_yamlConstructors.TryGetValue(key, out result)) { + return result; + } + return base.GetYamlConstructor(key); + } + + public override YamlMultiConstructor GetYamlMultiConstructor(string key) { + YamlMultiConstructor result; + if (_yamlMultiConstructors.TryGetValue(key, out result)) { + return result; + } + return base.GetYamlMultiConstructor(key); + } + + public override Regex GetYamlMultiRegexp(string key) { + Regex result; + if (_yamlMultiRegexps.TryGetValue(key, out result)) { + return result; + } + return base.GetYamlMultiRegexp(key); + } + + public override ICollection GetYamlMultiRegexps() { + return _yamlMultiRegexps.Keys; + } + + public new static void AddConstructor(string tag, YamlConstructor ctor) { + _yamlConstructors.Add(tag, ctor); + } + + public new static void AddMultiConstructor(string tagPrefix, YamlMultiConstructor ctor) { + _yamlMultiConstructors.Add(tagPrefix, ctor); + _yamlMultiRegexps.Add(tagPrefix, new Regex("^" + tagPrefix, RegexOptions.Compiled)); + } + + public Constructor(NodeProvider/*!*/ nodeProvider, RubyScope/*!*/ scope) + : base(nodeProvider, scope) { + } + + } +} \ No newline at end of file diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Emitter.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Emitter.cs new file mode 100644 index 0000000000..96af7c7f05 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Emitter.cs @@ -0,0 +1,1340 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; + +namespace IronRuby.StandardLibrary.Yaml { + + public interface IEmitter { + void Emit(YamlEvent @event); + } + + public class Emitter : IEmitter { + enum EmitterState { + STREAM_START = 0, + FIRST_DOCUMENT_START, + DOCUMENT_ROOT, + NOTHING, + DOCUMENT_START, + DOCUMENT_END, + FIRST_FLOW_SEQUENCE_ITEM, + FLOW_SEQUENCE_ITEM, + FIRST_FLOW_MAPPING_KEY, + FLOW_MAPPING_SIMPLE_VALUE, + FLOW_MAPPING_VALUE, + FLOW_MAPPING_KEY, + BLOCK_SEQUENCE_ITEM, + FIRST_BLOCK_MAPPING_KEY, + BLOCK_MAPPING_SIMPLE_VALUE, + BLOCK_MAPPING_VALUE, + BLOCK_MAPPING_KEY, + FIRST_BLOCK_SEQUENCE_ITEM, + } + + private class ScalarAnalysis { + internal string Scalar; + internal bool Empty; + internal bool Multiline; + internal bool AllowFlowPlain; + internal bool AllowBlockPlain; + internal bool AllowSingleQuoted; + internal bool AllowDoubleQuoted; + internal bool AllowBlock; + internal bool SpecialCharacters; + internal ScalarAnalysis(string scalar, bool empty, bool multiline, bool allowFlowPlain, bool allowBlockPlain, bool allowSingleQuoted, bool allowDoubleQuoted, bool allowBlock, bool specialCharacters) { + Scalar = scalar; + Empty = empty; + Multiline = multiline; + AllowFlowPlain = allowFlowPlain; + AllowBlockPlain = allowBlockPlain; + AllowSingleQuoted = allowSingleQuoted; + AllowDoubleQuoted = allowDoubleQuoted; + AllowBlock = allowBlock; + SpecialCharacters = specialCharacters; + } + } + + #region private instance fields + + private readonly TextWriter _stream; + private readonly YamlOptions _options; + + private readonly Stack _states = new Stack(); + private EmitterState _state = EmitterState.STREAM_START; + private readonly Queue _events = new Queue(); + private YamlEvent _event; + private int _flowLevel; + private readonly Stack _indents = new Stack(); + private int _indent = -1; + private bool _rootContext; + private bool _sequenceContext; + private bool _mappingContext; + private bool _simpleKeyContext; + + private int _line; + private int _column; + private bool _whitespace; + private bool _indentation; + + private bool canonical; + private int _bestIndent = 2; + private int _bestWidth = 80; + + private char _bestLinebreak = '\n'; + + private readonly Dictionary _tagPrefixes = new Dictionary(); + + private string _preparedAnchor; + private string _preparedTag; + + private ScalarAnalysis _analysis; + private char _style = '\0'; + + private bool _isVersion10; + + #endregion + + public Emitter(TextWriter stream, YamlOptions opts) { + _stream = stream; + _options = opts; + canonical = _options.Canonical; + int propIndent = _options.Indent; + if (propIndent>=2 && propIndent<10) { + _bestIndent = propIndent; + } + int propWidth = _options.BestWidth; + if (propWidth != 0 && propWidth > (_bestIndent*2)) { + _bestWidth = propWidth; + } + } + + public void Emit(YamlEvent @event) { + _events.Enqueue(@event); + while (!needMoreEvents()) { + _event = _events.Dequeue(); + switch (_state) { + case EmitterState.STREAM_START: + expectStreamStart(); + break; + case EmitterState.FIRST_DOCUMENT_START: + expectDocumentStart(true); + break; + case EmitterState.DOCUMENT_ROOT: + expectDocumentRoot(); + break; + case EmitterState.NOTHING: + expectNothing(); + break; + case EmitterState.DOCUMENT_START: + expectDocumentStart(false); + break; + case EmitterState.DOCUMENT_END: + expectDocumentEnd(); + break; + case EmitterState.FIRST_FLOW_SEQUENCE_ITEM: + expectFlowSequenceItem(true); + break; + case EmitterState.FLOW_SEQUENCE_ITEM: + expectFlowSequenceItem(false); + break; + case EmitterState.FIRST_FLOW_MAPPING_KEY: + expectFlowMappingKey(true); + break; + case EmitterState.FLOW_MAPPING_SIMPLE_VALUE: + expectFlowMappingSimpleValue(); + break; + case EmitterState.FLOW_MAPPING_VALUE: + expectFlowMappingValue(); + break; + case EmitterState.FLOW_MAPPING_KEY: + expectFlowMappingKey(false); + break; + case EmitterState.BLOCK_SEQUENCE_ITEM: + expectBlockSequenceItem(false); + break; + case EmitterState.FIRST_BLOCK_MAPPING_KEY: + expectBlockMappingKey(true); + break; + case EmitterState.BLOCK_MAPPING_SIMPLE_VALUE: + expectBlockMappingSimpleValue(); + break; + case EmitterState.BLOCK_MAPPING_VALUE: + expectBlockMappingValue(); + break; + case EmitterState.BLOCK_MAPPING_KEY: + expectBlockMappingKey(false); + break; + case EmitterState.FIRST_BLOCK_SEQUENCE_ITEM: + expectBlockSequenceItem(true); + break; + default: + Debug.Assert(false, "unreachable"); + throw new InvalidOperationException("unreachable"); + } + _event = null; + } + } + + void writeStreamStart() { + } + + void writeStreamEnd() { + _stream.Flush(); + } + + void writeIndicator(string indicator, bool needWhitespace, bool whitespace, bool indentation) { + if (!_whitespace && needWhitespace) { + indicator = " " + indicator; + } + _whitespace = whitespace; + _indentation = _indentation && indentation; + _column += indicator.Length; + _stream.Write(indicator); + } + + void writeIndent() { + int indent = 0; + if (_indent != -1) { + indent = _indent; + } + + if (!_indentation || _column > indent || (_column == indent && !_whitespace)) { + writeLineBreak(); + } + + if (_column < indent) { + _whitespace = true; + string data = new string(' ', indent - _column); + _column = indent; + _stream.Write(data); + } + } + + void writeVersionDirective(string version_text) { + _stream.Write("%Yaml " + version_text); + writeLineBreak(); + } + + void writeTagDirective(string handle, string prefix) { + _stream.Write("%TAG " + handle + " " + prefix); + writeLineBreak(); + } + + void writeDoubleQuoted(string text, bool split) { + writeIndicator("\"",true,false,false); + int start = 0; + int ending = 0; + + string data = null; + while (ending <= text.Length) { + char ch = (char)0; + if (ending < text.Length) { + ch = text[ending]; + } + if (ch==0 || "\"\\".IndexOf(ch) != -1 || !('\u0020' <= ch && ch <= '\u007E')) { + if (start < ending) { + data = text.Substring(start,ending-start); + _column+=data.Length; + _stream.Write(data); + start = ending; + } + if (ch != 0) { + string replace = ESCAPE_REPLACEMENTS(ch); + if (replace != null) { + data = "\\" + replace; + } else if (ch <= '\u00FF') { + string str = ((byte)ch).ToString("X2"); + data = "\\x" + str; + } else { + string str = ((ushort)ch).ToString("X4"); + data = "\\u" + str; + } + _column += data.Length; + _stream.Write(data); + start = ending+1; + } + } + + if ((0 < ending && ending < (text.Length-1)) && (ch == ' ' || start >= ending) && (_column+(ending-start)) > _bestWidth && split) { + if (start < ending) { + data = text.Substring(start, ending-start) + " \\"; + start = ending+1; + } else { + data = "\\"; + } + + _column += data.Length; + _stream.Write(data); + writeIndent(); + _whitespace = false; + _indentation = false; + + if (start < (text.Length+1) && text[start] == ' ') { + _stream.Write('\\'); + } + } + ending += 1; + } + + writeIndicator("\"",false,false,false); + } + + void writeSingleQuoted(string text, bool split) { + writeIndicator("'",true,false,false); + bool spaces = false; + bool breaks = false; + int start=0,ending=0; + char ceh = '\0'; + + string data; + while (ending <= text.Length) { + ceh = '\0'; + if (ending < text.Length) { + ceh = text[ending]; + } + if (spaces) { + if (ceh == 0 || ceh != 32) { + if (start+1 == ending && _column > _bestWidth && split && start != 0 && ending != text.Length) { + writeIndent(); + } else { + data = text.Substring(start,ending-start); + _column += data.Length; + _stream.Write(data); + } + start = ending; + } + } else if (breaks) { + if (ceh == 0 || !('\n' == ceh)) { + data = text.Substring(start,ending-start); + for(int i=0,j=data.Length;i" + chomp, true, false, false); + writeIndent(); + bool leadingSpace = false; + bool spaces = false; + bool breaks = false; + int start=0,ending=0; + + string data; + while (ending <= text.Length) { + char ceh = '\0'; + if (ending < text.Length) { + ceh = text[ending]; + } + if (breaks) { + if (ceh == 0 || !('\n' == ceh)) { + if (!leadingSpace && ceh != 0 && ceh != ' ' && text[start] == '\n') { + writeLineBreak(); + } + leadingSpace = ceh == ' '; + data = text.Substring(start,ending-start); + for(int i=0,j=data.Length;i _bestWidth) { + writeIndent(); + } else { + data = text.Substring(start,ending-start); + _column += data.Length; + _stream.Write(data); + } + start = ending; + } + } else { + if (ceh == 0 || ' ' == ceh || '\n' == ceh) { + data = text.Substring(start,ending-start); + _stream.Write(data); + if (ceh == 0) { + writeLineBreak(); + } + start = ending; + } + } + if (ceh != 0) { + breaks = '\n' == ceh; + spaces = ceh == ' '; + } + ending++; + } + } + + void writeLiteral(string text) { + string chomp = determineChomp(text); + writeIndicator("|" + chomp, true, false, false); + writeIndent(); + bool breaks = false; + int start=0,ending=0; + string data; + while(ending <= text.Length) { + char ceh = '\0'; + if (ending < text.Length) { + ceh = text[ending]; + } + if (breaks) { + if (ceh == 0 || !('\n' == ceh)) { + data = text.Substring(start,ending-start); + for(int i=0,j=data.Length;i _bestWidth && split) { + writeIndent(); + _whitespace = false; + _indentation = false; + } else { + data = text.Substring(start, ending-start); + _column += data.Length; + _stream.Write(data); + } + start = ending; + } + } else if (breaks) { + if (ceh != '\n') { + if (text[start] == '\n') { + writeLineBreak(); + } + data = text.Substring(start, ending-start); + for(int i=0,j=data.Length;i"; + } + } + + private static Regex DOC_INDIC = new Regex("^(---|\\.\\.\\.)", RegexOptions.Compiled); + private static Regex FIRST_SPACE = new Regex("(^|\n) ", RegexOptions.Compiled); + private static string NULL_BL_T_LINEBR = "\0 \t\r\n"; + private static string SPECIAL_INDIC = "#,[]{}#&*!|>'\"%@`"; + private static string FLOW_INDIC = ",?[]{}"; + + static ScalarAnalysis analyzeScalar(string scalar) { + if (scalar == null || scalar.Length == 0) { + return new ScalarAnalysis(scalar,true,false,false,true,true,true,false,false); + } + + bool blockIndicators = false; + bool flowIndicators = false; + bool lineBreaks = false; + bool specialCharacters = false; + + // Whitespaces. + bool inlineBreaks = false; // non-space break+ non-space + bool leadingSpaces = false; // ^ space+ (non-space | $) + bool leadingBreaks = false; // ^ break+ (non-space | $) + bool trailingSpaces = false; // (^ | non-space) space+ $ + bool trailingBreaks = false; // (^ | non-space) break+ $ + bool inlineBreaksSpaces = false; // non-space break+ space+ non-space + bool mixedBreaksSpaces = false; // anything else + + if (DOC_INDIC.IsMatch(scalar)) { + blockIndicators = true; + flowIndicators = true; + } + + bool preceededBySpace = true; + bool followedBySpace = scalar.Length == 1 || NULL_BL_T_LINEBR.IndexOf(scalar[1]) != -1; + + bool spaces = false; + bool breaks = false; + bool mixed = false; + bool leading = false; + + int index = 0; + + while(index < scalar.Length) { + char ceh = scalar[index]; + if (index == 0) { + if (SPECIAL_INDIC.IndexOf(ceh) != -1) { + flowIndicators = true; + blockIndicators = true; + } + if (ceh == '?' || ceh == ':') { + flowIndicators = true; + if (followedBySpace) { + blockIndicators = true; + } + } + if (ceh == '-' && followedBySpace) { + flowIndicators = true; + blockIndicators = true; + } + } else { + if (FLOW_INDIC.IndexOf(ceh) != -1) { + flowIndicators = true; + } + if (ceh == ':') { + flowIndicators = true; + if (followedBySpace) { + blockIndicators = true; + } + } + if (ceh == '#' && preceededBySpace) { + flowIndicators = true; + blockIndicators = true; + } + } + if (ceh == '\n') { + lineBreaks = true; + } + if (!(ceh == '\n' || ('\u0020' <= ceh && ceh <= '\u007E'))) { + specialCharacters = true; + + } + if (' ' == ceh || '\n' == ceh) { + if (spaces && breaks) { + if (ceh != ' ') { + mixed = true; + } + } else if (spaces) { + if (ceh != ' ') { + breaks = true; + mixed = true; + } + } else if (breaks) { + if (ceh == ' ') { + spaces = true; + } + } else { + leading = (index == 0); + if (ceh == ' ') { + spaces = true; + } else { + breaks = true; + } + } + } else if (spaces || breaks) { + if (leading) { + if (spaces && breaks) { + mixedBreaksSpaces = true; + } else if (spaces) { + leadingSpaces = true; + } else if (breaks) { + leadingBreaks = true; + } + } else { + if (mixed) { + mixedBreaksSpaces = true; + } else if (spaces && breaks) { + inlineBreaksSpaces = true; + } else if (breaks) { + inlineBreaks = true; + } + } + spaces = breaks = mixed = leading = false; + } + + if ((spaces || breaks) && (index == scalar.Length-1)) { + if (spaces && breaks) { + mixedBreaksSpaces = true; + } else if (spaces) { + trailingSpaces = true; + if (leading) { + leadingSpaces = true; + } + } else if (breaks) { + trailingBreaks = true; + if (leading) { + leadingBreaks = true; + } + } + spaces = breaks = mixed = leading = false; + } + index++; + preceededBySpace = NULL_BL_T_LINEBR.IndexOf(ceh) != -1; + followedBySpace = index+1 >= scalar.Length || NULL_BL_T_LINEBR.IndexOf(scalar[index+1]) != -1; + } + bool allowFlowPlain = true; + bool allowBlockPlain = true; + bool allowSingleQuoted = true; + bool allowDoubleQuoted = true; + bool allowBlock = true; + + if (leadingSpaces || leadingBreaks || trailingSpaces) { + allowFlowPlain = allowBlockPlain = allowBlock = allowSingleQuoted = false; + } + + if (trailingBreaks) { + allowFlowPlain = allowBlockPlain = false; + } + + if (inlineBreaksSpaces) { + allowFlowPlain = allowBlockPlain = allowSingleQuoted = false; + } + + if (mixedBreaksSpaces || specialCharacters) { + allowFlowPlain = allowBlockPlain = allowSingleQuoted = allowBlock = false; + } + + if (inlineBreaks) { + allowFlowPlain = allowBlockPlain = allowSingleQuoted = false; + } + + if (trailingBreaks) { + allowSingleQuoted = false; + } + + if (lineBreaks) { + allowFlowPlain = allowBlockPlain = false; + } + + if (flowIndicators) { + allowFlowPlain = false; + } + + if (blockIndicators) { + allowBlockPlain = false; + } + + return new ScalarAnalysis(scalar,false,lineBreaks,allowFlowPlain,allowBlockPlain,allowSingleQuoted,allowDoubleQuoted,allowBlock,specialCharacters); + } + + static string determineChomp(string text) { + char ceh = ' '; + char ceh2 = ' '; + if (text.Length > 0) { + ceh = text[text.Length-1]; + if (text.Length > 1) { + ceh2 = text[text.Length-2]; + } + } + return (ceh == '\n') ? ((ceh2 == '\n') ? "+" : "") : "-"; + } + + #region helper methods + + private bool needMoreEvents() { + if (_events.Count == 0) { + return true; + } + _event = _events.Peek(); + if (_event is DocumentStartEvent) { + return needEvents(1); + } else if (_event is SequenceStartEvent) { + return needEvents(2); + } else if (_event is MappingStartEvent) { + return needEvents(3); + } else { + return false; + } + } + + private bool needEvents(int count) { + int level = 0; + foreach (YamlEvent e in _events) { + if (e is DocumentStartEvent || e is CollectionStartEvent) { + level++; + } else if (e is DocumentEndEvent || e is CollectionEndEvent) { + level--; + } else if (e is StreamEndEvent) { + level = -1; + } + if (level < 0) { + return false; + } + } + return _events.Count < count + 1; + } + + private void increaseIndent(bool flow, bool indentless) { + _indents.Push(_indent); + if (_indent == -1) { + if (flow) { + _indent = _bestIndent; + } else { + _indent = 0; + } + } else if (!indentless) { + _indent += _bestIndent; + } + } + + private void expectStreamStart() { + if (_event is StreamStartEvent) { + writeStreamStart(); + _state = EmitterState.FIRST_DOCUMENT_START; + } else { + throw new EmitterException("expected StreamStartEvent, but got " + _event); + } + } + + private void expectNothing() { + throw new EmitterException("expecting nothing, but got " + _event); + } + + private void expectDocumentStart(bool first) { + if (_event is DocumentStartEvent) { + DocumentStartEvent ev = (DocumentStartEvent)_event; + if (first) { + if (null != ev.Version) { + writeVersionDirective(prepareVersion(ev.Version)); + } + + if ((null != ev.Version && ev.Version.Equals(new Version(1, 0))) || _options.Version.Equals(new Version(1, 0))) { + _isVersion10 = true; + _tagPrefixes.Clear(); + _tagPrefixes.Add("tag:yaml.org,2002:", "!"); + } else { + _tagPrefixes.Clear(); + _tagPrefixes.Add("!", "!"); + _tagPrefixes.Add("tag:yaml.org,2002:", "!!"); + } + + if (null != ev.Tags) { + foreach(KeyValuePair tags in new SortedList(ev.Tags)) { + string handle = tags.Key; + string prefix = tags.Value; + _tagPrefixes.Add(prefix, handle); + string handleText = prepareTagHandle(handle); + string prefixText = prepareTagPrefix(prefix); + writeTagDirective(handleText, prefixText); + } + } + } + + bool @implicit = first && !ev.Explicit && !canonical && ev.Version == null && ev.Tags == null && !checkEmptyDocument(); + if (!@implicit) { + if (!first) { + writeIndent(); + } + writeIndicator("--- ", !first, true, false); + if (canonical) { + writeIndent(); + } + } + _state = EmitterState.DOCUMENT_ROOT; + } else if (_event is StreamEndEvent) { + writeStreamEnd(); + _state = EmitterState.NOTHING; + } else { + throw new EmitterException("expected DocumentStartEvent, but got " + _event); + } + } + + private void expectDocumentRoot() { + _states.Push(EmitterState.DOCUMENT_END); + expectNode(true, false, false, false); + } + + private void expectDocumentEnd() { + if (_event is DocumentEndEvent) { + writeIndent(); + if (((DocumentEndEvent)_event).Explicit) { + writeIndicator("...", true, false, false); + writeIndent(); + } + _stream.Flush(); + _state = EmitterState.DOCUMENT_START; + } else { + throw new EmitterException("expected DocumentEndEvent, but got " + _event); + } + } + + private void expectFlowSequenceItem(bool first) { + if (_event is SequenceEndEvent) { + _indent = _indents.Pop(); + _flowLevel--; + if (canonical && !first) { + writeIndicator(",", false, false, false); + writeIndent(); + } + writeIndicator("]", false, false, false); + _state = _states.Pop(); + } else { + if (!first) { + writeIndicator(",", false, false, false); + } + if (canonical || _column > _bestWidth) { + writeIndent(); + } + _states.Push(EmitterState.FLOW_SEQUENCE_ITEM); + expectNode(false, true, false, false); + } + } + + private void expectFlowMappingSimpleValue() { + writeIndicator(": ", false, true, false); + _states.Push(EmitterState.FLOW_MAPPING_KEY); + expectNode(false, false, true, false); + } + + private void expectFlowMappingValue() { + if (canonical || _column > _bestWidth) { + writeIndent(); + } + writeIndicator(": ", false, true, false); + _states.Push(EmitterState.FLOW_MAPPING_KEY); + expectNode(false, false, true, false); + } + + private void expectFlowMappingKey(bool first) { + if (_event is MappingEndEvent) { + _indent = _indents.Pop(); + _flowLevel--; + if (canonical && !first) { + writeIndicator(",", false, false, false); + writeIndent(); + } + writeIndicator("}", false, false, false); + _state = _states.Pop(); + } else { + if (!first) { + writeIndicator(",", false, false, false); + } + if (canonical || _column > _bestWidth) { + writeIndent(); + } + if (!canonical && checkSimpleKey()) { + _states.Push(EmitterState.FLOW_MAPPING_SIMPLE_VALUE); + expectNode(false, false, true, true); + } else { + writeIndicator("?", true, false, false); + _states.Push(EmitterState.FLOW_MAPPING_VALUE); + expectNode(false, false, true, false); + } + } + } + + private void expectBlockSequenceItem(bool first) { + if (!first && _event is SequenceEndEvent) { + _indent = _indents.Pop(); + _state = _states.Pop(); + } else { + writeIndent(); + writeIndicator("-", true, false, true); + _states.Push(EmitterState.BLOCK_SEQUENCE_ITEM); + expectNode(false, true, false, false); + } + } + + private void expectBlockMappingSimpleValue() { + writeIndicator(": ", false, true, false); + _states.Push(EmitterState.BLOCK_MAPPING_KEY); + expectNode(false, false, true, false); + } + + private void expectBlockMappingValue() { + writeIndent(); + writeIndicator(": ", true, true, true); + _states.Push(EmitterState.BLOCK_MAPPING_KEY); + expectNode(false, false, true, false); + } + + private void expectBlockMappingKey(bool first) { + if (!first && _event is MappingEndEvent) { + _indent = _indents.Pop(); + _state = _states.Pop(); + } else { + writeIndent(); + if (checkSimpleKey()) { + _states.Push(EmitterState.BLOCK_MAPPING_SIMPLE_VALUE); + expectNode(false, false, true, true); + } else { + writeIndicator("?", true, false, true); + _states.Push(EmitterState.BLOCK_MAPPING_VALUE); + expectNode(false, false, true, false); + } + } + } + + private void expectNode(bool root, bool sequence, bool mapping, bool simpleKey) { + _rootContext = root; + _sequenceContext = sequence; + _mappingContext = mapping; + _simpleKeyContext = simpleKey; + if (_event is AliasEvent) { + expectAlias(); + } else if (_event is ScalarEvent || _event is CollectionStartEvent) { + processAnchor("&"); + processTag(); + if (_event is ScalarEvent) { + expectScalar(); + } else if (_event is SequenceStartEvent) { + if (_flowLevel != 0 || canonical || ((SequenceStartEvent)_event).FlowStyle || checkEmptySequence()) { + expectFlowSequence(); + } else { + expectBlockSequence(); + } + } else if (_event is MappingStartEvent) { + if (_flowLevel != 0 || canonical || ((MappingStartEvent)_event).FlowStyle || checkEmptyMapping()) { + expectFlowMapping(); + } else { + expectBlockMapping(); + } + } + } else { + throw new EmitterException("expected NodeEvent, but got " + _event); + } + } + + private void expectAlias() { + if (((NodeEvent)_event).Anchor == null) { + throw new EmitterException("anchor is not specified for alias"); + } + processAnchor("*"); + _state = _states.Pop(); + } + + private void expectScalar() { + increaseIndent(true, false); + processScalar(); + _indent = _indents.Pop(); + _state = _states.Pop(); + } + + private void expectFlowSequence() { + writeIndicator("[", true, true, false); + _flowLevel++; + increaseIndent(true, false); + _state = EmitterState.FIRST_FLOW_SEQUENCE_ITEM; + } + + private void expectBlockSequence() { + increaseIndent(false, _mappingContext && !_indentation); + _state = EmitterState.FIRST_BLOCK_SEQUENCE_ITEM; + } + + private void expectFlowMapping() { + writeIndicator("{", true, true, false); + _flowLevel++; + increaseIndent(true, false); + _state = EmitterState.FIRST_FLOW_MAPPING_KEY; + } + + private void expectBlockMapping() { + increaseIndent(false, false); + _state = EmitterState.FIRST_BLOCK_MAPPING_KEY; + } + + private bool checkEmptySequence() { + return _event is SequenceStartEvent && _events.Count != 0 && _events.Peek() is SequenceEndEvent; + } + + private bool checkEmptyMapping() { + return _event is MappingStartEvent && _events.Count != 0 && _events.Peek() is MappingEndEvent; + } + + private bool checkEmptyDocument() { + if (!(_event is DocumentStartEvent) || _events.Count == 0) { + return false; + } + ScalarEvent ev = _events.Peek() as ScalarEvent; + return ev != null && ev.Anchor == null && ev.Tag == null && ev.Implicit != null && ev.Value.Length == 0; + } + + private bool checkSimpleKey() { + int length = 0; + if (_event is NodeEvent && null != ((NodeEvent)_event).Anchor) { + if (null == _preparedAnchor) { + _preparedAnchor = prepareAnchor(((NodeEvent)_event).Anchor); + } + length += _preparedAnchor.Length; + } + string tag = null; + if (_event is ScalarEvent) { + tag = ((ScalarEvent)_event).Tag; + } else if (_event is CollectionStartEvent) { + tag = ((CollectionStartEvent)_event).Tag; + } + if (tag != null) { + if (null == _preparedTag) { + _preparedTag = prepareTag(tag); + } + length += _preparedTag.Length; + } + if (_event is ScalarEvent) { + if (null == _analysis) { + _analysis = analyzeScalar(((ScalarEvent)_event).Value); + length += _analysis.Scalar.Length; + } + } + + return (length < 128 && (_event is AliasEvent || (_event is ScalarEvent && !_analysis.Multiline) || checkEmptySequence() || checkEmptyMapping())); + } + + private void processAnchor(string indicator) { + NodeEvent ev = (NodeEvent)_event; + if (null == ev.Anchor) { + _preparedAnchor = null; + return; + } + if (null == _preparedAnchor) { + _preparedAnchor = prepareAnchor(ev.Anchor); + } + if (!string.IsNullOrEmpty(_preparedAnchor)) { + indicator += _preparedAnchor; + if (ev is CollectionStartEvent) { + _indentation = true; + } + writeIndicator(indicator, true, false, true); + } + _preparedAnchor = null; + } + + private void processTag() { + string tag = null; + if (_event is ScalarEvent) { + ScalarEvent ev = (ScalarEvent)_event; + tag = ev.Tag; + if (_style == 0) { + _style = chooseScalarStyle(); + } + if (((!canonical || tag == null) && ((0 == _style && ev.Implicit[0]) || (0 != _style && ev.Implicit[1])))) { + _preparedTag = null; + return; + } + if (ev.Implicit[0] && null == tag) { + tag = "!"; + _preparedTag = null; + } + } else { + CollectionStartEvent ev = (CollectionStartEvent)_event; + tag = ev.Tag; + if ((!canonical || tag == null) && ev.Implicit) { + _preparedTag = null; + return; + } + _indentation = true; + } + if (tag == null) { + throw new EmitterException("tag is not specified"); + } + if (null == _preparedTag) { + _preparedTag = prepareTag(tag); + } + if (!string.IsNullOrEmpty(_preparedTag)) { + writeIndicator(_preparedTag, true, false, true); + } + _preparedTag = null; + } + + private char chooseScalarStyle() { + ScalarEvent ev = (ScalarEvent)_event; + + if (null == _analysis) { + _analysis = analyzeScalar(ev.Value); + } + + if (ev.Style == '"' || canonical || (_analysis.Empty && ev.Tag == "tag:yaml.org,2002:str")) { + return '"'; + } + + // if (ev.Style == 0 && ev.Implicit[0]) { + if (ev.Style == 0) { + if (!(_simpleKeyContext && (_analysis.Empty || _analysis.Multiline)) && ((_flowLevel != 0 && _analysis.AllowFlowPlain) || (_flowLevel == 0 && _analysis.AllowBlockPlain))) { + return '\0'; + } + } + if (ev.Style == 0 && ev.Implicit[0] && (!(_simpleKeyContext && (_analysis.Empty || _analysis.Multiline)) && (_flowLevel != 0 && _analysis.AllowFlowPlain || (_flowLevel == 0 && _analysis.AllowBlockPlain)))) { + return '\0'; + } + if ((ev.Style == '|' || ev.Style == '>') && _flowLevel == 0 && _analysis.AllowBlock) { + return '\''; + } + if ((ev.Style == 0 || ev.Style == '\'') && (_analysis.AllowSingleQuoted && !(_simpleKeyContext && _analysis.Multiline))) { + return '\''; + } + if (_analysis.Multiline && (FIRST_SPACE.Matches(ev.Value).Count == 0) && !_analysis.SpecialCharacters) { + return '|'; + } + + return '"'; + } + + private void processScalar() { + ScalarEvent ev = (ScalarEvent)_event; + + if (null == _analysis) { + _analysis = analyzeScalar(ev.Value); + } + if (0 == _style) { + _style = chooseScalarStyle(); + } + bool split = !_simpleKeyContext; + if (_style == '"') { + writeDoubleQuoted(_analysis.Scalar, split); + } else if (_style == '\'') { + writeSingleQuoted(_analysis.Scalar, split); + } else if (_style == '>') { + writeFolded(_analysis.Scalar); + } else if (_style == '|') { + writeLiteral(_analysis.Scalar); + } else { + writePlain(_analysis.Scalar, split); + } + _analysis = null; + _style = '\0'; + } + + private string ESCAPE_REPLACEMENTS(char c) { + switch (c) { + case '\0': + return "0"; + case '\u0007': + return "a"; + case '\u0008': + return "b"; + case '\u0009': + return "t"; + case '\n': + return "n"; + case '\u000B': + return "v"; + case '\u000C': + return "f"; + case '\r': + return "r"; + case '\u001B': + return "e"; + case '"': + return "\""; + case '\\': + return "\\"; + case '\u00A0': + return "_"; + default: + return null; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Events.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Events.cs new file mode 100644 index 0000000000..5dda1c92c9 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Events.cs @@ -0,0 +1,192 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections; + +namespace IronRuby.StandardLibrary.Yaml { + public abstract class YamlEvent { + internal YamlEvent() { } + + public override string ToString() { + return "#<" + GetType().Name + ">"; + } + } + + public abstract class NodeEvent : YamlEvent { + private readonly string _anchor; + + internal NodeEvent(string anchor) { + _anchor = anchor; + } + + public string Anchor { get { return _anchor; } } + } + + public abstract class DocumentEvent : YamlEvent { + private readonly bool _explicit; + + internal DocumentEvent(bool @explicit) { + _explicit = @explicit; + } + + public bool Explicit { get { return _explicit; } } + + public override string ToString() { + return string.Format("#<{0} Explicit={1}", GetType().Name, Explicit); + } + } + + public abstract class CollectionStartEvent : NodeEvent { + private readonly string _tag; + private readonly bool _implicit; + private readonly bool _flowStyle; + + internal CollectionStartEvent(string anchor, string tag, bool @implicit, bool flowStyle) + : base(anchor) { + _tag = tag; + _implicit = @implicit; + _flowStyle = flowStyle; + } + + public string Tag { get { return _tag; } } + public bool Implicit { get { return _implicit; } } + public bool FlowStyle { get { return _flowStyle; } } + + public override string ToString() { + return string.Format("#<{0} Tag=\"{1}\", Implicit={2} FlowStyle={3}>", GetType().Name, Tag, Implicit, FlowStyle); + } + } + + public abstract class CollectionEndEvent : YamlEvent { + } + + public sealed class AliasEvent : NodeEvent { + public AliasEvent(string anchor) : base(anchor) { } + } + + public sealed class DocumentEndEvent : DocumentEvent { + public static readonly DocumentEndEvent ExplicitInstance = new DocumentEndEvent(true); + public static readonly DocumentEndEvent ImplicitInstance = new DocumentEndEvent(false); + private DocumentEndEvent(bool @explicit) : base(@explicit) { } + } + + public sealed class DocumentStartEvent : DocumentEvent { + private readonly Version _version; + private readonly Dictionary _tags; + + public DocumentStartEvent(bool @explicit, Version version, Dictionary tags) + : base(@explicit) { + _version = version; + _tags = tags; + } + + public Version Version { + get { return _version; } + } + + public IDictionary Tags { + get { return _tags; } + } + + public override string ToString() { + string tags = "null"; + if (_tags != null) { + StringBuilder str = new StringBuilder('{'); + foreach (KeyValuePair t in _tags) { + if (str.Length != 1) { + str.Append(", "); + } + str.Append('\'').Append(t.Key).Append("': '").Append(t.Value).Append('\''); + } + str.Append('}'); + tags = str.ToString(); + } + return string.Format("#", Version, tags); + } + } + + public sealed class MappingEndEvent : CollectionEndEvent { + public static readonly MappingEndEvent Instance = new MappingEndEvent(); + private MappingEndEvent() { } + } + + public sealed class MappingStartEvent : CollectionStartEvent { + public MappingStartEvent(string anchor, string tag, bool @implicit, bool flowStyle) + : base(anchor, tag, @implicit, flowStyle) { + } + } + + public sealed class ScalarEvent : NodeEvent { + // TODO: can tag, implicit merge with CollectionStartEvent? + private readonly string _tag; + private readonly bool[] _implicit; // TODO: one element array? + private readonly string _value; + private readonly char _style; + + public ScalarEvent(string anchor, string tag, bool[] @implicit, string value, char style) + : base(anchor) { + if (@implicit == null || @implicit.Length != 2) { + throw new ArgumentException("requires a 2 element array", "@implicit"); + } + _tag = tag; + _implicit = @implicit; + _value = value; + _style = style; + } + + public string Tag { get { return _tag; } } + public bool[] Implicit { get { return _implicit; } } + public string Value { get { return _value; } } + public char Style { get { return _style; } } + + + public override string ToString() { + string value = Value.Replace("\r", "\\r").Replace("\n", "\\n"); + if (value.Length > 30) { + value = value.Substring(0, 27) + "..."; + } + string @implicit = (_implicit[0] ? "T" : "F") + "," + (_implicit[1] ? "T" : "F"); + return string.Format("#", Tag, @implicit, Style, value); + } + } + + public sealed class SequenceEndEvent : CollectionEndEvent { + public static readonly SequenceEndEvent Instance = new SequenceEndEvent(); + private SequenceEndEvent() { } + } + + public sealed class SequenceStartEvent : CollectionStartEvent { + public SequenceStartEvent(string anchor, string tag, bool @implicit, bool flowStyle) + : base(anchor, tag, @implicit, flowStyle) { + } + } + + public sealed class StreamStartEvent : YamlEvent { + public static readonly StreamStartEvent Instance = new StreamStartEvent(); + private StreamStartEvent() { } + } + + public sealed class StreamEndEvent : YamlEvent { + public static readonly StreamEndEvent Instance = new StreamEndEvent(); + private StreamEndEvent() { } + } + +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Exceptions.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Exceptions.cs new file mode 100644 index 0000000000..32a253632e --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Exceptions.cs @@ -0,0 +1,112 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; + +namespace IronRuby.StandardLibrary.Yaml { + + public class YamlException : Exception { + public YamlException(string message) + : base(message) { + } + + public YamlException(string message, Exception inner) + : base(message, inner) { + } + } + + public class ScannerException : YamlException { + public ScannerException(string message) + : base(message) { + } + + public ScannerException(string message, Exception inner) + : base(message, inner) { + } + } + + public class ParserException : YamlException { + public ParserException(string message) + : base(message) { + } + + public ParserException(string message, Exception inner) + : base(message, inner) { + } + } + + public class ComposerException : YamlException { + public ComposerException(string message) + : base(message) { + } + + public ComposerException(string message, Exception inner) + : base(message, inner) { + } + } + + public class ResolverException : YamlException { + public ResolverException(string message) + : base(message) { + } + + public ResolverException(string message, Exception inner) + : base(message, inner) { + } + } + + public class ConstructorException : YamlException { + public ConstructorException(string message) + : base(message) { + } + + public ConstructorException(string message, Exception inner) + : base(message, inner) { + } + } + + public class RepresenterException : YamlException { + public RepresenterException(string message) + : base(message) { + } + + public RepresenterException(string message, Exception inner) + : base(message, inner) { + } + } + + public class SerializerException : YamlException { + public SerializerException(string message) + : base(message) { + } + + public SerializerException(string message, Exception inner) + : base(message, inner) { + } + } + + + public class EmitterException : YamlException { + public EmitterException(string message) + : base(message) { + } + + public EmitterException(string message, Exception inner) + : base(message, inner) { + } + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/LiteralParser.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/LiteralParser.cs new file mode 100644 index 0000000000..bceca80ef6 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/LiteralParser.cs @@ -0,0 +1,148 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using Microsoft.Scripting.Math; +using IronRuby.Compiler; + +namespace IronRuby.StandardLibrary.Yaml { + + public delegate object ParseInteger(int sign, string digits, int @base); + + /// + /// This must be hooked to allow big integer parsing + /// + public static class LiteralParser { + /// + /// Parses an integer/biginteger given sign, base, and digit string. + /// The sign, base prefix, and numeric seperator characters are already stripped off + /// + public static ParseInteger ParseInteger = DefaultParseInteger; + + #region simple integer parsing + + public static object DefaultParseInteger(int sign, string digits, int @base) { + int result; + if (!TryParseInteger(sign, digits, @base, out result)) { + return ParseBigInteger(sign, digits, @base); + } + return result; + } + + private static bool HexValue(char ch, out int value) { + switch (ch) { + case '0': + case '\x660': value = 0; break; + case '1': + case '\x661': value = 1; break; + case '2': + case '\x662': value = 2; break; + case '3': + case '\x663': value = 3; break; + case '4': + case '\x664': value = 4; break; + case '5': + case '\x665': value = 5; break; + case '6': + case '\x666': value = 6; break; + case '7': + case '\x667': value = 7; break; + case '8': + case '\x668': value = 8; break; + case '9': + case '\x669': value = 9; break; + case 'a': + case 'A': value = 10; break; + case 'b': + case 'B': value = 11; break; + case 'c': + case 'C': value = 12; break; + case 'd': + case 'D': value = 13; break; + case 'e': + case 'E': value = 14; break; + case 'f': + case 'F': value = 15; break; + default: + value = -1; + return false; + } + return true; + } + + private static int HexValue(char ch) { + int value; + if (!HexValue(ch, out value)) { + throw new ArgumentException("bad char for integer value: " + ch); + } + return value; + } + + private static int CharValue(char ch, int b) { + int val = HexValue(ch); + if (val >= b) { + throw new ArgumentException(String.Format("bad char for the integer value: '{0}' (base {1})", ch, b)); + } + return val; + } + + public static bool TryParseInteger(int sign, string text, int @base, out int ret) { + ret = 0; + long m = sign; + for (int i = text.Length - 1; i >= 0; i--) { + // avoid the exception here. Not only is throwing it expensive, + // but loading the resources for it is also expensive + long lret = (long)ret + m * CharValue(text[i], @base); + if (Int32.MinValue <= lret && lret <= Int32.MaxValue) { + ret = (int)lret; + } else { + return false; + } + + m *= @base; + if (Int32.MinValue > m || m > Int32.MaxValue) { + return false; + } + } + return true; + } + + public sealed class YamlBignumParser : UnsignedBigIntegerParser { + private string _digits; + private int _base; + private int _position; + + public YamlBignumParser(string digits, int @base) { + _digits = digits; + _base = @base; + _position = 0; + } + + protected override int ReadDigit() { + return CharValue(_digits[_position++], _base); + } + } + + public static BigInteger ParseBigInteger(int sign, string text, int @base) { + YamlBignumParser p = new YamlBignumParser(text, @base); + BigInteger ret = p.ParseDecimal(text.Length); + return sign > 0 ? ret : -ret; + } + + #endregion + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/NodeProvider.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/NodeProvider.cs new file mode 100644 index 0000000000..78249da419 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/NodeProvider.cs @@ -0,0 +1,67 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using IronRuby.StandardLibrary.Yaml; +using System.Collections.Generic; + +namespace IronRuby.StandardLibrary.Yaml { + /// + /// Provides YAML nodes for Ruby object constructor. + /// + public abstract class NodeProvider : IEnumerable { + public abstract bool CheckNode(); + public abstract Node GetNode(); + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + while (CheckNode()) { + yield return GetNode(); + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + #endregion + } + + /// + /// Simple NodeProvider implementation. Provides only one given node. + /// + public class SimpleNodeProvider : NodeProvider { + private Node _node; + + public SimpleNodeProvider(Node/*!*/ node) { + _node = node; + } + + public override bool CheckNode() { + return _node != null; + } + + public override Node GetNode() { + if (CheckNode()) { + Node tmp = _node; + _node = null; + return tmp; + } else { + return null; + } + } + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Nodes.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Nodes.cs new file mode 100644 index 0000000000..0d39bb9947 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Nodes.cs @@ -0,0 +1,127 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections.ObjectModel; + +namespace IronRuby.StandardLibrary.Yaml { + + public abstract class Node { + private string _tag; + + internal Node(string tag) { + _tag = tag ?? ""; + } + + public string Tag { + get { return _tag; } + set { _tag = value; } + } + + public override string ToString() { + return string.Format("#<{0} Tag=\"{1}\">", GetType().Name, Tag); + } + } + + public abstract class CollectionNode : Node { + private bool _flowStyle; + + internal CollectionNode(string tag, bool flowStyle) + : base(tag) { + _flowStyle = flowStyle; + } + + public bool FlowStyle { + get { return _flowStyle; } + } + } + + public sealed class ScalarNode : Node { + private readonly char _style; + private readonly string _value; + + public ScalarNode(string tag, string value, char style) + : base(tag) { + _style = style; + _value = value; + } + + public char Style { + get { return _style; } + } + + public string Value { + get { return _value; } + } + + public override string ToString() { + string value = Value.Replace("\r", "\\r").Replace("\n", "\\n"); + if (value.Length > 35) { + value = value.Substring(0, 32) + "..."; + } + return string.Format("#", Tag, Style, value); + } + } + + public sealed class LinkNode : Node { + private Node _linked; + + public LinkNode() + : base(null) { + } + + public LinkNode(Node linked) + : base(null) { + _linked = linked; + } + + public Node Linked { + get { return _linked; } + set { _linked = value; } + } + } + + + public sealed class SequenceNode : CollectionNode { + private readonly IList _nodes; + + public SequenceNode(string tag, IList nodes, bool flowStyle) + : base(tag, flowStyle) { + _nodes = nodes; + } + + public IList Nodes { + get { return _nodes; } + } + } + + public sealed class MappingNode : CollectionNode { + private readonly IDictionary _nodes; + + public MappingNode(string tag, IDictionary nodes, bool flowStyle) + : base(tag, flowStyle) { + _nodes = nodes; + } + + public IDictionary Nodes { + get { return _nodes; } + } + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Parser.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Parser.cs new file mode 100644 index 0000000000..95f36f26e3 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Parser.cs @@ -0,0 +1,644 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace IronRuby.StandardLibrary.Yaml { + public class Parser : IEnumerable { + // Memnonics for the production table + private enum Production { + STREAM = 0, + STREAM_START = 1, // TERMINAL + STREAM_END = 2, // TERMINAL + IMPLICIT_DOCUMENT = 3, + EXPLICIT_DOCUMENT = 4, + DOCUMENT_START = 5, + DOCUMENT_START_IMPLICIT = 6, + DOCUMENT_END = 7, + BLOCK_NODE = 8, + BLOCK_CONTENT = 9, + PROPERTIES = 10, + PROPERTIES_END = 11, + FLOW_CONTENT = 12, + BLOCK_SEQUENCE = 13, + BLOCK_MAPPING = 14, + FLOW_SEQUENCE = 15, + FLOW_MAPPING = 16, + SCALAR = 17, + BLOCK_SEQUENCE_ENTRY = 18, + BLOCK_MAPPING_ENTRY = 19, + BLOCK_MAPPING_ENTRY_VALUE = 20, + BLOCK_NODE_OR_INDENTLESS_SEQUENCE = 21, + BLOCK_SEQUENCE_START = 22, + BLOCK_SEQUENCE_END = 23, + BLOCK_MAPPING_START = 24, + BLOCK_MAPPING_END = 25, + INDENTLESS_BLOCK_SEQUENCE = 26, + BLOCK_INDENTLESS_SEQUENCE_START = 27, + INDENTLESS_BLOCK_SEQUENCE_ENTRY = 28, + BLOCK_INDENTLESS_SEQUENCE_END = 29, + FLOW_SEQUENCE_START = 30, + FLOW_SEQUENCE_ENTRY = 31, + FLOW_SEQUENCE_END = 32, + FLOW_MAPPING_START = 33, + FLOW_MAPPING_ENTRY = 34, + FLOW_MAPPING_END = 35, + FLOW_INTERNAL_MAPPING_START = 36, + FLOW_INTERNAL_CONTENT = 37, + FLOW_INTERNAL_VALUE = 38, + FLOW_INTERNAL_MAPPING_END = 39, + FLOW_ENTRY_MARKER = 40, + FLOW_NODE = 41, + FLOW_MAPPING_INTERNAL_CONTENT = 42, + FLOW_MAPPING_INTERNAL_VALUE = 43, + ALIAS = 44, + EMPTY_SCALAR = 45, + } + + private readonly Stack _parseStack = new Stack(); + private readonly LinkedList _tags = new LinkedList(); + private readonly LinkedList _anchors = new LinkedList(); + private readonly Dictionary _tagHandles = new Dictionary(); + private readonly Scanner _scanner; + private readonly Version _defaultYamlVersion; + + private Version _yamlVersion; + private bool _done; + private YamlEvent _currentEvent; + private string _familyTypePrefix; + + public Parser(Scanner scanner, YamlOptions opts) + : this(scanner, opts.Version) { + } + + public Parser(Scanner scanner, Version defaultYamlVersion) { + _scanner = scanner; + _defaultYamlVersion = defaultYamlVersion; + _parseStack.Push(Production.STREAM); + } + + public YamlEvent PeekEvent() { + if (_currentEvent == null) { + _currentEvent = ParseStreamNext(); + } + return _currentEvent; + } + + public YamlEvent GetEvent() { + YamlEvent value = PeekEvent(); + _currentEvent = null; + return value; + } + + private YamlEvent ParseStreamNext() { + if (_done) { + return null; + } + while (_parseStack.Count > 0) { + YamlEvent value = Produce(); + if (null != value) { + return value; + } + } + _done = true; + return null; + } + + private YamlEvent Produce() { + switch (_parseStack.Pop()) { + case Production.STREAM: { + _parseStack.Push(Production.STREAM_END); + _parseStack.Push(Production.EXPLICIT_DOCUMENT); + _parseStack.Push(Production.IMPLICIT_DOCUMENT); + _parseStack.Push(Production.STREAM_START); + return null; + } + case Production.STREAM_START: { + _scanner.GetToken(); + return StreamStartEvent.Instance; + } + case Production.STREAM_END: { + _scanner.GetToken(); + return StreamEndEvent.Instance; + } + case Production.IMPLICIT_DOCUMENT: { + Token curr = _scanner.PeekToken(); + if (!(curr is DirectiveToken || curr is DocumentStartToken || curr is StreamEndToken)) { + _parseStack.Push(Production.DOCUMENT_END); + _parseStack.Push(Production.BLOCK_NODE); + _parseStack.Push(Production.DOCUMENT_START_IMPLICIT); + } + return null; + } + case Production.EXPLICIT_DOCUMENT: { + if (!(_scanner.PeekToken() is StreamEndToken)) { + _parseStack.Push(Production.EXPLICIT_DOCUMENT); + _parseStack.Push(Production.DOCUMENT_END); + _parseStack.Push(Production.BLOCK_NODE); + _parseStack.Push(Production.DOCUMENT_START); + } + return null; + } + case Production.DOCUMENT_START: { + Token tok = _scanner.PeekToken(); + Version version; + Dictionary tags; + ProcessDirectives(out version, out tags); + if (!(_scanner.PeekToken() is DocumentStartToken)) { + throw new ParserException("expected '', but found: " + tok); + } + _scanner.GetToken(); + return new DocumentStartEvent(true, version, tags); + } + case Production.DOCUMENT_START_IMPLICIT: { + Version version; + Dictionary tags; + ProcessDirectives(out version, out tags); + return new DocumentStartEvent(false, version, tags); + } + case Production.DOCUMENT_END: { + Token tok = _scanner.PeekToken(); + bool @explicit = false; + while (_scanner.PeekToken() is DocumentEndToken) { + _scanner.GetToken(); + @explicit = true; + } + return @explicit ? DocumentEndEvent.ExplicitInstance : DocumentEndEvent.ImplicitInstance; + } + case Production.BLOCK_NODE: { + Token curr = _scanner.PeekToken(); + if (curr is DirectiveToken || curr is DocumentStartToken || curr is DocumentEndToken || curr is StreamEndToken) { + _parseStack.Push(Production.EMPTY_SCALAR); + } else { + if (curr is AliasToken) { + _parseStack.Push(Production.ALIAS); + } else { + _parseStack.Push(Production.PROPERTIES_END); + _parseStack.Push(Production.BLOCK_CONTENT); + _parseStack.Push(Production.PROPERTIES); + } + } + return null; + } + case Production.BLOCK_CONTENT: { + Token tok = _scanner.PeekToken(); + if (tok is BlockSequenceStartToken) { + _parseStack.Push(Production.BLOCK_SEQUENCE); + } else if (tok is BlockMappingStartToken) { + _parseStack.Push(Production.BLOCK_MAPPING); + } else if (tok is FlowSequenceStartToken) { + _parseStack.Push(Production.FLOW_SEQUENCE); + } else if (tok is FlowMappingStartToken) { + _parseStack.Push(Production.FLOW_MAPPING); + } else if (tok is ScalarToken) { + _parseStack.Push(Production.SCALAR); + } else { + // Part of solution for JRUBY-718 + bool[] @implicit = new bool[] { false, false }; + return new ScalarEvent(_anchors.First.Value, _tags.First.Value, @implicit, "", '\''); + } + return null; + } + case Production.PROPERTIES: { + string anchor = null; + string tag = null; + if (_scanner.PeekToken() is AnchorToken) { + anchor = ((AnchorToken)_scanner.GetToken()).Value; + if (_scanner.PeekToken() is TagToken) { + tag = GetTag((TagToken)_scanner.GetToken()); + } + } else if (_scanner.PeekToken() is TagToken) { + tag = GetTag((TagToken)_scanner.GetToken()); + if (_scanner.PeekToken() is AnchorToken) { + anchor = ((AnchorToken)_scanner.GetToken()).Value; + } + } + _anchors.AddFirst(anchor); + _tags.AddFirst(tag); + return null; + } + case Production.PROPERTIES_END: { + _anchors.RemoveFirst(); + _tags.RemoveFirst(); + return null; + } + case Production.FLOW_CONTENT: { + Token tok = _scanner.PeekToken(); + if (tok is FlowSequenceStartToken) { + _parseStack.Push(Production.FLOW_SEQUENCE); + } else if (tok is FlowMappingStartToken) { + _parseStack.Push(Production.FLOW_MAPPING); + } else if (tok is ScalarToken) { + _parseStack.Push(Production.SCALAR); + } else { + throw new ParserException("while scanning a flow node: expected the node content, but found: " + tok); + } + return null; + } + case Production.BLOCK_SEQUENCE: { + _parseStack.Push(Production.BLOCK_SEQUENCE_END); + _parseStack.Push(Production.BLOCK_SEQUENCE_ENTRY); + _parseStack.Push(Production.BLOCK_SEQUENCE_START); + return null; + } + case Production.BLOCK_MAPPING: { + _parseStack.Push(Production.BLOCK_MAPPING_END); + _parseStack.Push(Production.BLOCK_MAPPING_ENTRY); + _parseStack.Push(Production.BLOCK_MAPPING_START); + return null; + } + case Production.FLOW_SEQUENCE: { + _parseStack.Push(Production.FLOW_SEQUENCE_END); + _parseStack.Push(Production.FLOW_SEQUENCE_ENTRY); + _parseStack.Push(Production.FLOW_SEQUENCE_START); + return null; + } + case Production.FLOW_MAPPING: { + _parseStack.Push(Production.FLOW_MAPPING_END); + _parseStack.Push(Production.FLOW_MAPPING_ENTRY); + _parseStack.Push(Production.FLOW_MAPPING_START); + return null; + } + case Production.SCALAR: { + ScalarToken tok = (ScalarToken)_scanner.GetToken(); + bool[] @implicit = null; + if ((tok.Plain && _tags.First.Value == null) || "!" == _tags.First.Value) { + @implicit = new bool[] { true, false }; + } else if (_tags.First.Value == null) { + @implicit = new bool[] { false, true }; + } else { + @implicit = new bool[] { false, false }; + } + return new ScalarEvent(_anchors.First.Value, _tags.First.Value, @implicit, tok.Value, tok.Style); + } + case Production.BLOCK_SEQUENCE_ENTRY: { + if (_scanner.PeekToken() is BlockEntryToken) { + _scanner.GetToken(); + if (!(_scanner.PeekToken() is BlockEntryToken || _scanner.PeekToken() is BlockEndToken)) { + _parseStack.Push(Production.BLOCK_SEQUENCE_ENTRY); + _parseStack.Push(Production.BLOCK_NODE); + } else { + _parseStack.Push(Production.BLOCK_SEQUENCE_ENTRY); + _parseStack.Push(Production.EMPTY_SCALAR); + } + } + return null; + } + case Production.BLOCK_MAPPING_ENTRY: { + if (_scanner.PeekToken() is KeyToken || _scanner.PeekToken() is ValueToken) { + if (_scanner.PeekToken() is KeyToken) { + _scanner.GetToken(); + Token curr = _scanner.PeekToken(); + if (!(curr is KeyToken || curr is ValueToken || curr is BlockEndToken)) { + _parseStack.Push(Production.BLOCK_MAPPING_ENTRY); + _parseStack.Push(Production.BLOCK_MAPPING_ENTRY_VALUE); + _parseStack.Push(Production.BLOCK_NODE_OR_INDENTLESS_SEQUENCE); + } else { + _parseStack.Push(Production.BLOCK_MAPPING_ENTRY); + _parseStack.Push(Production.BLOCK_MAPPING_ENTRY_VALUE); + _parseStack.Push(Production.EMPTY_SCALAR); + } + } else { + _parseStack.Push(Production.BLOCK_MAPPING_ENTRY); + _parseStack.Push(Production.BLOCK_MAPPING_ENTRY_VALUE); + _parseStack.Push(Production.EMPTY_SCALAR); + } + } + return null; + } + case Production.BLOCK_MAPPING_ENTRY_VALUE: { + if (_scanner.PeekToken() is KeyToken || _scanner.PeekToken() is ValueToken) { + if (_scanner.PeekToken() is ValueToken) { + _scanner.GetToken(); + Token curr = _scanner.PeekToken(); + if (!(curr is KeyToken || curr is ValueToken || curr is BlockEndToken)) { + _parseStack.Push(Production.BLOCK_NODE_OR_INDENTLESS_SEQUENCE); + } else { + _parseStack.Push(Production.EMPTY_SCALAR); + } + } else { + _parseStack.Push(Production.EMPTY_SCALAR); + } + } + return null; + } + case Production.BLOCK_NODE_OR_INDENTLESS_SEQUENCE: { + if (_scanner.PeekToken() is AliasToken) { + _parseStack.Push(Production.ALIAS); + } else { + if (_scanner.PeekToken() is BlockEntryToken) { + _parseStack.Push(Production.INDENTLESS_BLOCK_SEQUENCE); + _parseStack.Push(Production.PROPERTIES); + } else { + _parseStack.Push(Production.BLOCK_CONTENT); + _parseStack.Push(Production.PROPERTIES); + } + } + return null; + } + case Production.BLOCK_SEQUENCE_START: { + bool @implicit = _tags.First.Value == null || _tags.First.Value == "!"; + _scanner.GetToken(); + return new SequenceStartEvent(_anchors.First.Value, _tags.First.Value, @implicit, false); + } + case Production.BLOCK_SEQUENCE_END: { + Token tok = null; + if (!(_scanner.PeekToken() is BlockEndToken)) { + tok = _scanner.PeekToken(); + throw new ParserException("while scanning a block collection: expected , but found: " + tok); + } + _scanner.GetToken(); + return SequenceEndEvent.Instance; + } + case Production.BLOCK_MAPPING_START: { + bool @implicit = _tags.First.Value == null || _tags.First.Value == "!"; + _scanner.GetToken(); + return new MappingStartEvent(_anchors.First.Value, _tags.First.Value, @implicit, false); + } + case Production.BLOCK_MAPPING_END: { + Token tok = null; + if (!(_scanner.PeekToken() is BlockEndToken)) { + tok = _scanner.PeekToken(); + throw new ParserException("while scanning a block mapping: expected , but found: " + tok); + } + _scanner.GetToken(); + return MappingEndEvent.Instance; + } + case Production.INDENTLESS_BLOCK_SEQUENCE: { + _parseStack.Push(Production.BLOCK_INDENTLESS_SEQUENCE_END); + _parseStack.Push(Production.INDENTLESS_BLOCK_SEQUENCE_ENTRY); + _parseStack.Push(Production.BLOCK_INDENTLESS_SEQUENCE_START); + return null; + } + case Production.BLOCK_INDENTLESS_SEQUENCE_START: { + bool @implicit = _tags.First.Value == null || _tags.First.Value == "!"; + return new SequenceStartEvent(_anchors.First.Value, _tags.First.Value, @implicit, false); + } + case Production.INDENTLESS_BLOCK_SEQUENCE_ENTRY: { + if (_scanner.PeekToken() is BlockEntryToken) { + _scanner.GetToken(); + Token curr = _scanner.PeekToken(); + if (!(curr is BlockEntryToken || curr is KeyToken || curr is ValueToken || curr is BlockEndToken)) { + _parseStack.Push(Production.INDENTLESS_BLOCK_SEQUENCE_ENTRY); + _parseStack.Push(Production.BLOCK_NODE); + } else { + _parseStack.Push(Production.INDENTLESS_BLOCK_SEQUENCE_ENTRY); + _parseStack.Push(Production.EMPTY_SCALAR); + } + } + return null; + } + case Production.BLOCK_INDENTLESS_SEQUENCE_END: { + return SequenceEndEvent.Instance; + } + case Production.FLOW_SEQUENCE_START: { + bool @implicit = _tags.First.Value == null || _tags.First.Value == "!"; + _scanner.GetToken(); + return new SequenceStartEvent(_anchors.First.Value, _tags.First.Value, @implicit, true); + } + case Production.FLOW_SEQUENCE_ENTRY: { + if (!(_scanner.PeekToken() is FlowSequenceEndToken)) { + if (_scanner.PeekToken() is KeyToken) { + _parseStack.Push(Production.FLOW_SEQUENCE_ENTRY); + _parseStack.Push(Production.FLOW_ENTRY_MARKER); + _parseStack.Push(Production.FLOW_INTERNAL_MAPPING_END); + _parseStack.Push(Production.FLOW_INTERNAL_VALUE); + _parseStack.Push(Production.FLOW_INTERNAL_CONTENT); + _parseStack.Push(Production.FLOW_INTERNAL_MAPPING_START); + } else { + _parseStack.Push(Production.FLOW_SEQUENCE_ENTRY); + _parseStack.Push(Production.FLOW_NODE); + _parseStack.Push(Production.FLOW_ENTRY_MARKER); + } + } + return null; + } + case Production.FLOW_SEQUENCE_END: { + _scanner.GetToken(); + return SequenceEndEvent.Instance; + } + case Production.FLOW_MAPPING_START: { + bool @implicit = _tags.First.Value == null || _tags.First.Value == "!"; + _scanner.GetToken(); + return new MappingStartEvent(_anchors.First.Value, _tags.First.Value, @implicit, true); + } + case Production.FLOW_MAPPING_ENTRY: { + if (!(_scanner.PeekToken() is FlowMappingEndToken)) { + if (_scanner.PeekToken() is KeyToken) { + _parseStack.Push(Production.FLOW_MAPPING_ENTRY); + _parseStack.Push(Production.FLOW_ENTRY_MARKER); + _parseStack.Push(Production.FLOW_MAPPING_INTERNAL_VALUE); + _parseStack.Push(Production.FLOW_MAPPING_INTERNAL_CONTENT); + } else { + _parseStack.Push(Production.FLOW_MAPPING_ENTRY); + _parseStack.Push(Production.FLOW_NODE); + _parseStack.Push(Production.FLOW_ENTRY_MARKER); + } + } + return null; + } + case Production.FLOW_MAPPING_END: { + _scanner.GetToken(); + return MappingEndEvent.Instance; + } + case Production.FLOW_INTERNAL_MAPPING_START: { + _scanner.GetToken(); + return new MappingStartEvent(null, null, true, true); + } + case Production.FLOW_INTERNAL_CONTENT: { + Token curr = _scanner.PeekToken(); + if (!(curr is ValueToken || curr is FlowEntryToken || curr is FlowSequenceEndToken)) { + _parseStack.Push(Production.FLOW_NODE); + } else { + _parseStack.Push(Production.EMPTY_SCALAR); + } + return null; + } + case Production.FLOW_INTERNAL_VALUE: { + if (_scanner.PeekToken() is ValueToken) { + _scanner.GetToken(); + if (!((_scanner.PeekToken() is FlowEntryToken) || (_scanner.PeekToken() is FlowSequenceEndToken))) { + _parseStack.Push(Production.FLOW_NODE); + } else { + _parseStack.Push(Production.EMPTY_SCALAR); + } + } else { + _parseStack.Push(Production.EMPTY_SCALAR); + } + return null; + } + case Production.FLOW_INTERNAL_MAPPING_END: { + return MappingEndEvent.Instance; + } + case Production.FLOW_ENTRY_MARKER: { + if (_scanner.PeekToken() is FlowEntryToken) { + _scanner.GetToken(); + } + return null; + } + case Production.FLOW_NODE: { + if (_scanner.PeekToken() is AliasToken) { + _parseStack.Push(Production.ALIAS); + } else { + _parseStack.Push(Production.PROPERTIES_END); + _parseStack.Push(Production.FLOW_CONTENT); + _parseStack.Push(Production.PROPERTIES); + } + return null; + } + case Production.FLOW_MAPPING_INTERNAL_CONTENT: { + Token curr = _scanner.PeekToken(); + if (!(curr is ValueToken || curr is FlowEntryToken || curr is FlowMappingEndToken)) { + _scanner.GetToken(); + _parseStack.Push(Production.FLOW_NODE); + } else { + _parseStack.Push(Production.EMPTY_SCALAR); + } + return null; + } + case Production.FLOW_MAPPING_INTERNAL_VALUE: { + if (_scanner.PeekToken() is ValueToken) { + _scanner.GetToken(); + if (!(_scanner.PeekToken() is FlowEntryToken || _scanner.PeekToken() is FlowMappingEndToken)) { + _parseStack.Push(Production.FLOW_NODE); + } else { + _parseStack.Push(Production.EMPTY_SCALAR); + } + } else { + _parseStack.Push(Production.EMPTY_SCALAR); + } + return null; + } + case Production.ALIAS: { + AliasToken tok = (AliasToken)_scanner.GetToken(); + return new AliasEvent(tok.Value); + } + case Production.EMPTY_SCALAR: { + return new ScalarEvent(null, null, new bool[] { true, false }, "", (char)0); + } + } + + return null; + } + + private static Regex ONLY_WORD = new Regex("^\\w+$", RegexOptions.Compiled); + + private string GetTag(TagToken tagToken) { + if (tagToken == null) { // check against "!"? + return null; + } + string tag = null; + string handle = tagToken.Handle; + string suffix = tagToken.Suffix; + int ix = -1; + if ((ix = suffix.IndexOf("^")) != -1) { + if (ix > 0) { + _familyTypePrefix = suffix.Substring(0, ix); + } + suffix = _familyTypePrefix + suffix.Substring(ix + 1); + } + if (handle != null) { + if (!_tagHandles.ContainsKey(handle)) { + throw new ParserException("while parsing a node: found undefined tag handle :" + handle); + } + if ((ix = suffix.IndexOf("/")) != -1) { + string before = suffix.Substring(0, ix); + string after = suffix.Substring(ix + 1); + if (ONLY_WORD.IsMatch(before)) { + tag = "tag:" + before + ".yaml.org,2002:" + after; + } else { + if (before.StartsWith("tag:")) { + tag = before + ":" + after; + } else { + tag = "tag:" + before + ":" + after; + } + } + } else { + tag = _tagHandles[handle] + suffix; + } + } else { + tag = suffix; + } + return tag; + } + + private void ProcessDirectives(out Version version, out Dictionary tagHandles) { + while (_scanner.PeekToken() is DirectiveToken) { + DirectiveToken tok = (DirectiveToken)_scanner.GetToken(); + if (tok.Name == "Yaml") { + if (_yamlVersion != null) { + throw new ParserException("found duplicate Yaml directive"); + } + int major = int.Parse(tok.Value[0]); + int minor = int.Parse(tok.Value[1]); + if (major != 1) { + throw new ParserException("found incompatible Yaml document (version 1.* is required)"); + } + _yamlVersion = new Version(major, minor); + + } else if (tok.Name == "TAG") { + string handle = tok.Value[0]; + string prefix = tok.Value[1]; + if (_tagHandles.ContainsKey(handle)) { + throw new ParserException("duplicate tag handle: " + handle); + } + _tagHandles.Add(handle, prefix); + } + } + + version = _yamlVersion ?? _defaultYamlVersion; + tagHandles = (_tagHandles.Count > 0) ? new Dictionary(_tagHandles) : null; + + if (version.Major == 1 && version.Minor == 0) { + // == 1.0 + if (!_tagHandles.ContainsKey("!")) { + _tagHandles.Add("!", "tag:yaml.org,2002:"); + } + if (!_tagHandles.ContainsKey("!!")) { + _tagHandles.Add("!!", ""); + } + } else { + // > 1.0 + if (!_tagHandles.ContainsKey("!")) { + _tagHandles.Add("!", "!"); + } + if (!_tagHandles.ContainsKey("!!")) { + _tagHandles.Add("!!", "tag:yaml.org,2002:"); + } + } + } + + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + YamlEvent e; + while ((e = GetEvent()) != null) { + yield return e; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + #endregion + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/PrivateType.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/PrivateType.cs new file mode 100644 index 0000000000..007d9a7f08 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/PrivateType.cs @@ -0,0 +1,37 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +namespace IronRuby.StandardLibrary.Yaml { + + public class PrivateType { + private readonly string _tag; + private readonly object _value; + + public PrivateType(string tag, object value) { + _tag = tag; + _value = value; + } + + public string Tag { get { return _tag; } } + public object Value { get { return _value; } } + + public override string ToString() { + return "#"; + } + } +} \ No newline at end of file diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/ReferenceEqualityComparer.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/ReferenceEqualityComparer.cs new file mode 100644 index 0000000000..98e16ecb71 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/ReferenceEqualityComparer.cs @@ -0,0 +1,35 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace IronRuby.StandardLibrary.Yaml { + internal class ReferenceEqualityComparer : IEqualityComparer where T : class { + private ReferenceEqualityComparer() {} + + internal static readonly ReferenceEqualityComparer Instance = new ReferenceEqualityComparer(); + + public bool Equals(T x, T y) { + return object.ReferenceEquals(x, y); + } + + public int GetHashCode(T obj) { + return RuntimeHelpers.GetHashCode(obj); + } + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Representer.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Representer.cs new file mode 100644 index 0000000000..52ce6b11a4 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Representer.cs @@ -0,0 +1,229 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.StandardLibrary.Yaml { + + public class Representer { + private readonly ISerializer _serializer; + private readonly char _defaultStyle; + private readonly Dictionary _representedObjects = new Dictionary(ReferenceEqualityComparer.Instance); + private readonly Dictionary> _links = new Dictionary>(ReferenceEqualityComparer.Instance); + private readonly static object _NullKey = new object(); + + public Representer(ISerializer serializer, YamlOptions opts) + : this(serializer, opts.UseDouble ? '"' : (opts.UseSingle ? '\'' : '\0')) { + } + public Representer(ISerializer serializer, char defaultStyle) { + _serializer = serializer; + if (defaultStyle != '"' && defaultStyle != '\'' && defaultStyle != '\0') { + throw new ArgumentException("must be single quote, double quote, or zero", "defaultStyle"); + } + _defaultStyle = defaultStyle; + } + + private Node RepresentData(object data) { + Node node = null; + + bool ignoreAlias = IgnoreAliases(data); + + object dataKey = data ?? _NullKey; + + if (!ignoreAlias) { + if (_representedObjects.TryGetValue(dataKey , out node)) { + if (null == node) { + LinkNode link = new LinkNode(); + List list; + if (!_links.TryGetValue(dataKey, out list)) { + _links.Add(dataKey, list = new List()); + } + list.Add(link); + return link; + } + return node; + } + _representedObjects.Add(dataKey, null); + } + + node = CreateNode(data); + + if (!ignoreAlias) { + _representedObjects[dataKey] = node; + + List list; + if (_links.TryGetValue(dataKey, out list)) { + _links.Remove(dataKey); + foreach (LinkNode n in list) { + n.Linked = node; + } + } + } + return node; + } + + public Node Scalar(string tag, string value, char style) { + return new ScalarNode(tag, value, style == 0 ? _defaultStyle : style); + } + + public Node Sequence(string tag, IList sequence, bool flowStyle) { + List value = new List(sequence.Count); + foreach (object x in sequence) { + value.Add(RepresentData(x)); + } + return new SequenceNode(tag, value, flowStyle); + } + + public Node Map(string tag, IDictionary mapping, bool flowStyle) { + Dictionary value = new Dictionary(mapping.Count); + foreach (DictionaryEntry e in mapping) { + object key = BaseSymbolDictionary.ObjToNull(e.Key); + value.Add(RepresentData(key), RepresentData(e.Value)); + } + return new MappingNode(tag, value, flowStyle); + } + + public void Represent(object data) { + _serializer.Serialize(RepresentData(data)); + _representedObjects.Clear(); + } + + protected virtual bool IgnoreAliases(object data) { + return data == null || data is string || data is bool || data is int || data is float || data is double || data is decimal; + } + + protected virtual Node CreateNode(object data) { + return BaseCreateNode(data); + } + + protected internal Node BaseCreateNode(object data) { + if (data == null) { + return Scalar("tag:yaml.org,2002:null", "", (char)0); + } + + IDictionary map = data as IDictionary; + if (map != null) { + string taguri = map is Dictionary + ? "tag:yaml.org,2002:map" + : "tag:yaml.org,2002:map:" + map.GetType().Name; + return Map(taguri, map, false); + } + + byte[] bytes = data as byte[]; + if (bytes != null) { + return Scalar("tag:yaml.org,2002:binary", Convert.ToBase64String(bytes), (char)0); + } + + IList seq = data as IList; + if (seq != null) { + string taguri = seq is List + ? "tag:yaml.org,2002:seq" + : "tag:yaml.org,2002:seq:" + seq.GetType().Name; + return Sequence(taguri, seq, false); + } + + ICollection set = data as ICollection; + if (set != null) { + Dictionary entries = new Dictionary(set.Count); + foreach (object x in set) { + entries.Add(x, null); + } + return Map("tag:yaml.org,2002:set", entries, false); + } + + PrivateType pt = data as PrivateType; + if (pt != null) { + Node n = RepresentData(pt.Value); + n.Tag = pt.Tag; + return n; + } + + string tag, value; + switch (Type.GetTypeCode(data.GetType())) { + case TypeCode.Boolean: + tag = "tag:yaml.org,2002:bool"; + value = data.ToString(); + break; + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Char: + tag = "tag:yaml.org,2002:int"; + value = data.ToString(); + break; + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + tag = "tag:yaml.org,2002:float"; + value = data.ToString(); + if (value == "Infinity") { + value = ".inf"; + } else if (value == "-Infinity") { + value = "-.inf"; + } else if (value == "NaN") { + value = ".nan"; + } + break; + case TypeCode.String: + tag = "tag:yaml.org,2002:str"; + value = data.ToString(); + break; + case TypeCode.DateTime: + DateTime date = (DateTime)data; + string format = (date.Millisecond != 0) ? "yyyy-MM-dd HH:mm:ss.SSS Z" : "yyyy-MM-dd HH:mm:ss Z"; + value = date.ToString(format); + // TODO: what is this code for? + if (value.Length >= 23) { + value = value.Substring(0, 23) + ":" + value.Substring(23); + } + tag = "tag:yaml.org,2002:timestamp"; + break; + default: + return CreateNodeForObject(data); + } + + return Scalar(tag, value, (char)0); + } + + // TODO: use some type of standard .NET serialization + private Node CreateNodeForObject(object data) { + Dictionary values = new Dictionary(); + + foreach (PropertyInfo prop in data.GetType().GetProperties()) { + MethodInfo getter = prop.GetGetMethod(); + if (getter != null && getter.GetParameters().Length == 0) { + try { + values.Add(prop.Name, prop.GetValue(data, null)); + } catch (Exception) { + values.Add(prop.Name, null); + } + } + } + return Map("!cli/object:" + data.GetType().Name, values, false); + } + } +} \ No newline at end of file diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Resolver.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Resolver.cs new file mode 100644 index 0000000000..2564b855c9 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Resolver.cs @@ -0,0 +1,47 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace IronRuby.StandardLibrary.Yaml { + // TODO: the rest of the ResolverImpl class didn't seem to do anything useful + public static class Resolver { + public static string Resolve(Type kind, string value, bool[] @implicit) { + return Resolve(kind, value, @implicit[0]); + } + + public static string Resolve(Type kind, string value, bool @implicit) { + if (kind == typeof(ScalarNode) && @implicit) { + string resolv = ResolverScanner.Recognize(value); + if (resolv != null) { + return resolv; + } + } + if (kind == typeof(ScalarNode)) { + return "tag:yaml.org,2002:str"; + } else if (kind == typeof(SequenceNode)) { + return "tag:yaml.org,2002:seq"; + } else if (kind == typeof(MappingNode)) { + return "tag:yaml.org,2002:map"; + } + return null; + } + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/ResolverScanner.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/ResolverScanner.cs new file mode 100644 index 0000000000..de98efbbb4 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/ResolverScanner.cs @@ -0,0 +1,487 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +// line 1 "src/org/jvyamlb/resolver_scanner.rl" + +namespace IronRuby.StandardLibrary.Yaml { + + public static class ResolverScanner { + // line 52 "src/org/jvyamlb/resolver_scanner.rl" + + + + // line 13 "src/org/jvyamlb/ResolverScanner.java" + private static void init__resolver_scanner_actions_0(byte[] r) { + r[0] = 0; r[1] = 1; r[2] = 0; r[3] = 1; r[4] = 1; r[5] = 1; r[6] = 2; r[7] = 1; + r[8] = 3; r[9] = 1; r[10] = 4; r[11] = 1; r[12] = 5; r[13] = 1; r[14] = 6; r[15] = 1; + r[16] = 7; + } + + private static byte[] create__resolver_scanner_actions() { + byte[] r = new byte[17]; + init__resolver_scanner_actions_0(r); + return r; + } + + private static byte[] _resolver_scanner_actions = create__resolver_scanner_actions(); + + + private static void init__resolver_scanner_key_offsets_0(short[] r) { + r[0] = 0; r[1] = 0; r[2] = 21; r[3] = 26; r[4] = 30; r[5] = 32; r[6] = 34; r[7] = 38; + r[8] = 40; r[9] = 41; r[10] = 42; r[11] = 43; r[12] = 48; r[13] = 52; r[14] = 56; r[15] = 58; + r[16] = 61; r[17] = 69; r[18] = 73; r[19] = 77; r[20] = 83; r[21] = 85; r[22] = 86; r[23] = 87; + r[24] = 88; r[25] = 90; r[26] = 93; r[27] = 95; r[28] = 101; r[29] = 105; r[30] = 108; r[31] = 109; + r[32] = 111; r[33] = 113; r[34] = 114; r[35] = 116; r[36] = 118; r[37] = 124; r[38] = 129; r[39] = 131; + r[40] = 133; r[41] = 135; r[42] = 142; r[43] = 146; r[44] = 148; r[45] = 149; r[46] = 151; r[47] = 157; + r[48] = 163; r[49] = 168; r[50] = 173; r[51] = 174; r[52] = 176; r[53] = 177; r[54] = 178; r[55] = 179; + r[56] = 180; r[57] = 181; r[58] = 182; r[59] = 186; r[60] = 187; r[61] = 188; r[62] = 189; r[63] = 190; + r[64] = 194; r[65] = 195; r[66] = 196; r[67] = 198; r[68] = 199; r[69] = 200; r[70] = 202; r[71] = 203; + r[72] = 204; r[73] = 205; r[74] = 207; r[75] = 209; r[76] = 210; r[77] = 211; r[78] = 211; r[79] = 215; + r[80] = 217; r[81] = 217; r[82] = 227; r[83] = 235; r[84] = 238; r[85] = 241; r[86] = 249; r[87] = 255; + r[88] = 261; r[89] = 264; r[90] = 265; r[91] = 270; r[92] = 274; r[93] = 276; r[94] = 286; r[95] = 294; + r[96] = 302; r[97] = 311; r[98] = 314; r[99] = 315; r[100] = 315; r[101] = 319; r[102] = 325; r[103] = 331; + r[104] = 337; r[105] = 344; r[106] = 344; r[107] = 344; + } + + private static short[] create__resolver_scanner_key_offsets() { + short[] r = new short[108]; + init__resolver_scanner_key_offsets_0(r); + return r; + } + + private static short[] _resolver_scanner_key_offsets = create__resolver_scanner_key_offsets(); + + + private static void init__resolver_scanner_trans_keys_0(char[] r) { + r[0] = (char)32; r[1] = (char)44; r[2] = (char)46; r[3] = (char)48; r[4] = (char)60; r[5] = (char)61; r[6] = (char)70; r[7] = (char)78; + r[8] = (char)79; r[9] = (char)84; r[10] = (char)89; r[11] = (char)102; r[12] = (char)110; r[13] = (char)111; r[14] = (char)116; r[15] = (char)121; + r[16] = (char)126; r[17] = (char)43; r[18] = (char)45; r[19] = (char)49; r[20] = (char)57; r[21] = (char)44; r[22] = (char)46; r[23] = (char)48; + r[24] = (char)49; r[25] = (char)57; r[26] = (char)44; r[27] = (char)46; r[28] = (char)48; r[29] = (char)57; r[30] = (char)43; r[31] = (char)45; + r[32] = (char)48; r[33] = (char)57; r[34] = (char)73; r[35] = (char)105; r[36] = (char)48; r[37] = (char)57; r[38] = (char)78; r[39] = (char)110; + r[40] = (char)70; r[41] = (char)102; r[42] = (char)110; r[43] = (char)44; r[44] = (char)46; r[45] = (char)58; r[46] = (char)48; r[47] = (char)57; + r[48] = (char)48; r[49] = (char)53; r[50] = (char)54; r[51] = (char)57; r[52] = (char)46; r[53] = (char)58; r[54] = (char)48; r[55] = (char)57; + r[56] = (char)46; r[57] = (char)58; r[58] = (char)95; r[59] = (char)48; r[60] = (char)49; r[61] = (char)44; r[62] = (char)95; r[63] = (char)48; + r[64] = (char)57; r[65] = (char)65; r[66] = (char)70; r[67] = (char)97; r[68] = (char)102; r[69] = (char)48; r[70] = (char)53; r[71] = (char)54; + r[72] = (char)57; r[73] = (char)48; r[74] = (char)53; r[75] = (char)54; r[76] = (char)57; r[77] = (char)73; r[78] = (char)78; r[79] = (char)105; + r[80] = (char)110; r[81] = (char)48; r[82] = (char)57; r[83] = (char)65; r[84] = (char)97; r[85] = (char)78; r[86] = (char)97; r[87] = (char)110; + r[88] = (char)48; r[89] = (char)57; r[90] = (char)45; r[91] = (char)48; r[92] = (char)57; r[93] = (char)48; r[94] = (char)57; r[95] = (char)9; + r[96] = (char)32; r[97] = (char)84; r[98] = (char)116; r[99] = (char)48; r[100] = (char)57; r[101] = (char)9; r[102] = (char)32; r[103] = (char)48; + r[104] = (char)57; r[105] = (char)58; r[106] = (char)48; r[107] = (char)57; r[108] = (char)58; r[109] = (char)48; r[110] = (char)57; r[111] = (char)48; + r[112] = (char)57; r[113] = (char)58; r[114] = (char)48; r[115] = (char)57; r[116] = (char)48; r[117] = (char)57; r[118] = (char)9; r[119] = (char)32; + r[120] = (char)43; r[121] = (char)45; r[122] = (char)46; r[123] = (char)90; r[124] = (char)9; r[125] = (char)32; r[126] = (char)43; r[127] = (char)45; + r[128] = (char)90; r[129] = (char)48; r[130] = (char)57; r[131] = (char)48; r[132] = (char)57; r[133] = (char)48; r[134] = (char)57; r[135] = (char)9; + r[136] = (char)32; r[137] = (char)43; r[138] = (char)45; r[139] = (char)90; r[140] = (char)48; r[141] = (char)57; r[142] = (char)9; r[143] = (char)32; + r[144] = (char)84; r[145] = (char)116; r[146] = (char)48; r[147] = (char)57; r[148] = (char)45; r[149] = (char)48; r[150] = (char)57; r[151] = (char)9; + r[152] = (char)32; r[153] = (char)84; r[154] = (char)116; r[155] = (char)48; r[156] = (char)57; r[157] = (char)44; r[158] = (char)45; r[159] = (char)46; + r[160] = (char)58; r[161] = (char)48; r[162] = (char)57; r[163] = (char)44; r[164] = (char)46; r[165] = (char)58; r[166] = (char)48; r[167] = (char)57; + r[168] = (char)44; r[169] = (char)46; r[170] = (char)58; r[171] = (char)48; r[172] = (char)57; r[173] = (char)60; r[174] = (char)65; r[175] = (char)97; + r[176] = (char)76; r[177] = (char)83; r[178] = (char)69; r[179] = (char)108; r[180] = (char)115; r[181] = (char)101; r[182] = (char)79; r[183] = (char)85; + r[184] = (char)111; r[185] = (char)117; r[186] = (char)76; r[187] = (char)76; r[188] = (char)108; r[189] = (char)108; r[190] = (char)70; r[191] = (char)78; + r[192] = (char)102; r[193] = (char)110; r[194] = (char)70; r[195] = (char)102; r[196] = (char)82; r[197] = (char)114; r[198] = (char)85; r[199] = (char)117; + r[200] = (char)69; r[201] = (char)101; r[202] = (char)83; r[203] = (char)115; r[204] = (char)97; r[205] = (char)111; r[206] = (char)117; r[207] = (char)102; + r[208] = (char)110; r[209] = (char)114; r[210] = (char)101; r[211] = (char)69; r[212] = (char)101; r[213] = (char)48; r[214] = (char)57; r[215] = (char)48; + r[216] = (char)57; r[217] = (char)44; r[218] = (char)46; r[219] = (char)58; r[220] = (char)95; r[221] = (char)98; r[222] = (char)120; r[223] = (char)48; + r[224] = (char)55; r[225] = (char)56; r[226] = (char)57; r[227] = (char)44; r[228] = (char)46; r[229] = (char)58; r[230] = (char)95; r[231] = (char)48; + r[232] = (char)55; r[233] = (char)56; r[234] = (char)57; r[235] = (char)95; r[236] = (char)48; r[237] = (char)55; r[238] = (char)95; r[239] = (char)48; + r[240] = (char)49; r[241] = (char)44; r[242] = (char)95; r[243] = (char)48; r[244] = (char)57; r[245] = (char)65; r[246] = (char)70; r[247] = (char)97; + r[248] = (char)102; r[249] = (char)44; r[250] = (char)46; r[251] = (char)58; r[252] = (char)95; r[253] = (char)48; r[254] = (char)57; r[255] = (char)44; + r[256] = (char)46; r[257] = (char)58; r[258] = (char)95; r[259] = (char)48; r[260] = (char)57; r[261] = (char)58; r[262] = (char)48; r[263] = (char)57; + r[264] = (char)58; r[265] = (char)44; r[266] = (char)58; r[267] = (char)95; r[268] = (char)48; r[269] = (char)57; r[270] = (char)46; r[271] = (char)58; + r[272] = (char)48; r[273] = (char)57; r[274] = (char)46; r[275] = (char)58; r[276] = (char)44; r[277] = (char)46; r[278] = (char)58; r[279] = (char)95; + r[280] = (char)98; r[281] = (char)120; r[282] = (char)48; r[283] = (char)55; r[284] = (char)56; r[285] = (char)57; r[286] = (char)44; r[287] = (char)46; + r[288] = (char)58; r[289] = (char)95; r[290] = (char)48; r[291] = (char)55; r[292] = (char)56; r[293] = (char)57; r[294] = (char)44; r[295] = (char)46; + r[296] = (char)58; r[297] = (char)95; r[298] = (char)48; r[299] = (char)55; r[300] = (char)56; r[301] = (char)57; r[302] = (char)44; r[303] = (char)45; + r[304] = (char)46; r[305] = (char)58; r[306] = (char)95; r[307] = (char)48; r[308] = (char)55; r[309] = (char)56; r[310] = (char)57; r[311] = (char)58; + r[312] = (char)48; r[313] = (char)57; r[314] = (char)58; r[315] = (char)9; r[316] = (char)32; r[317] = (char)84; r[318] = (char)116; r[319] = (char)44; + r[320] = (char)46; r[321] = (char)58; r[322] = (char)95; r[323] = (char)48; r[324] = (char)57; r[325] = (char)44; r[326] = (char)46; r[327] = (char)58; + r[328] = (char)95; r[329] = (char)48; r[330] = (char)57; r[331] = (char)44; r[332] = (char)46; r[333] = (char)58; r[334] = (char)95; r[335] = (char)48; + r[336] = (char)57; r[337] = (char)44; r[338] = (char)45; r[339] = (char)46; r[340] = (char)58; r[341] = (char)95; r[342] = (char)48; r[343] = (char)57; + r[344] = (char)0; + } + + private static char[] create__resolver_scanner_trans_keys() { + char[] r = new char[345]; + init__resolver_scanner_trans_keys_0(r); + return r; + } + + private static char[] _resolver_scanner_trans_keys = create__resolver_scanner_trans_keys(); + + + private static void init__resolver_scanner_single_lengths_0(byte[] r) { + r[0] = 0; r[1] = 17; r[2] = 3; r[3] = 2; r[4] = 2; r[5] = 0; r[6] = 2; r[7] = 2; + r[8] = 1; r[9] = 1; r[10] = 1; r[11] = 3; r[12] = 0; r[13] = 2; r[14] = 2; r[15] = 1; + r[16] = 2; r[17] = 0; r[18] = 0; r[19] = 4; r[20] = 2; r[21] = 1; r[22] = 1; r[23] = 1; + r[24] = 0; r[25] = 1; r[26] = 0; r[27] = 4; r[28] = 2; r[29] = 1; r[30] = 1; r[31] = 0; + r[32] = 0; r[33] = 1; r[34] = 0; r[35] = 0; r[36] = 6; r[37] = 5; r[38] = 0; r[39] = 0; + r[40] = 0; r[41] = 5; r[42] = 4; r[43] = 0; r[44] = 1; r[45] = 0; r[46] = 4; r[47] = 4; + r[48] = 3; r[49] = 3; r[50] = 1; r[51] = 2; r[52] = 1; r[53] = 1; r[54] = 1; r[55] = 1; + r[56] = 1; r[57] = 1; r[58] = 4; r[59] = 1; r[60] = 1; r[61] = 1; r[62] = 1; r[63] = 4; + r[64] = 1; r[65] = 1; r[66] = 2; r[67] = 1; r[68] = 1; r[69] = 2; r[70] = 1; r[71] = 1; + r[72] = 1; r[73] = 2; r[74] = 2; r[75] = 1; r[76] = 1; r[77] = 0; r[78] = 2; r[79] = 0; + r[80] = 0; r[81] = 6; r[82] = 4; r[83] = 1; r[84] = 1; r[85] = 2; r[86] = 4; r[87] = 4; + r[88] = 1; r[89] = 1; r[90] = 3; r[91] = 2; r[92] = 2; r[93] = 6; r[94] = 4; r[95] = 4; + r[96] = 5; r[97] = 1; r[98] = 1; r[99] = 0; r[100] = 4; r[101] = 4; r[102] = 4; r[103] = 4; + r[104] = 5; r[105] = 0; r[106] = 0; r[107] = 0; + } + + private static byte[] create__resolver_scanner_single_lengths() { + byte[] r = new byte[108]; + init__resolver_scanner_single_lengths_0(r); + return r; + } + + private static byte[] _resolver_scanner_single_lengths = create__resolver_scanner_single_lengths(); + + + private static void init__resolver_scanner_range_lengths_0(byte[] r) { + r[0] = 0; r[1] = 2; r[2] = 1; r[3] = 1; r[4] = 0; r[5] = 1; r[6] = 1; r[7] = 0; + r[8] = 0; r[9] = 0; r[10] = 0; r[11] = 1; r[12] = 2; r[13] = 1; r[14] = 0; r[15] = 1; + r[16] = 3; r[17] = 2; r[18] = 2; r[19] = 1; r[20] = 0; r[21] = 0; r[22] = 0; r[23] = 0; + r[24] = 1; r[25] = 1; r[26] = 1; r[27] = 1; r[28] = 1; r[29] = 1; r[30] = 0; r[31] = 1; + r[32] = 1; r[33] = 0; r[34] = 1; r[35] = 1; r[36] = 0; r[37] = 0; r[38] = 1; r[39] = 1; + r[40] = 1; r[41] = 1; r[42] = 0; r[43] = 1; r[44] = 0; r[45] = 1; r[46] = 1; r[47] = 1; + r[48] = 1; r[49] = 1; r[50] = 0; r[51] = 0; r[52] = 0; r[53] = 0; r[54] = 0; r[55] = 0; + r[56] = 0; r[57] = 0; r[58] = 0; r[59] = 0; r[60] = 0; r[61] = 0; r[62] = 0; r[63] = 0; + r[64] = 0; r[65] = 0; r[66] = 0; r[67] = 0; r[68] = 0; r[69] = 0; r[70] = 0; r[71] = 0; + r[72] = 0; r[73] = 0; r[74] = 0; r[75] = 0; r[76] = 0; r[77] = 0; r[78] = 1; r[79] = 1; + r[80] = 0; r[81] = 2; r[82] = 2; r[83] = 1; r[84] = 1; r[85] = 3; r[86] = 1; r[87] = 1; + r[88] = 1; r[89] = 0; r[90] = 1; r[91] = 1; r[92] = 0; r[93] = 2; r[94] = 2; r[95] = 2; + r[96] = 2; r[97] = 1; r[98] = 0; r[99] = 0; r[100] = 0; r[101] = 1; r[102] = 1; r[103] = 1; + r[104] = 1; r[105] = 0; r[106] = 0; r[107] = 0; + } + + private static byte[] create__resolver_scanner_range_lengths() { + byte[] r = new byte[108]; + init__resolver_scanner_range_lengths_0(r); + return r; + } + + private static byte[] _resolver_scanner_range_lengths = create__resolver_scanner_range_lengths(); + + + private static void init__resolver_scanner_index_offsets_0(short[] r) { + r[0] = 0; r[1] = 0; r[2] = 20; r[3] = 25; r[4] = 29; r[5] = 32; r[6] = 34; r[7] = 38; + r[8] = 41; r[9] = 43; r[10] = 45; r[11] = 47; r[12] = 52; r[13] = 55; r[14] = 59; r[15] = 62; + r[16] = 65; r[17] = 71; r[18] = 74; r[19] = 77; r[20] = 83; r[21] = 86; r[22] = 88; r[23] = 90; + r[24] = 92; r[25] = 94; r[26] = 97; r[27] = 99; r[28] = 105; r[29] = 109; r[30] = 112; r[31] = 114; + r[32] = 116; r[33] = 118; r[34] = 120; r[35] = 122; r[36] = 124; r[37] = 131; r[38] = 137; r[39] = 139; + r[40] = 141; r[41] = 143; r[42] = 150; r[43] = 155; r[44] = 157; r[45] = 159; r[46] = 161; r[47] = 167; + r[48] = 173; r[49] = 178; r[50] = 183; r[51] = 185; r[52] = 188; r[53] = 190; r[54] = 192; r[55] = 194; + r[56] = 196; r[57] = 198; r[58] = 200; r[59] = 205; r[60] = 207; r[61] = 209; r[62] = 211; r[63] = 213; + r[64] = 218; r[65] = 220; r[66] = 222; r[67] = 225; r[68] = 227; r[69] = 229; r[70] = 232; r[71] = 234; + r[72] = 236; r[73] = 238; r[74] = 241; r[75] = 244; r[76] = 246; r[77] = 248; r[78] = 249; r[79] = 253; + r[80] = 255; r[81] = 256; r[82] = 265; r[83] = 272; r[84] = 275; r[85] = 278; r[86] = 284; r[87] = 290; + r[88] = 296; r[89] = 299; r[90] = 301; r[91] = 306; r[92] = 310; r[93] = 313; r[94] = 322; r[95] = 329; + r[96] = 336; r[97] = 344; r[98] = 347; r[99] = 349; r[100] = 350; r[101] = 355; r[102] = 361; r[103] = 367; + r[104] = 373; r[105] = 380; r[106] = 381; r[107] = 382; + } + + private static short[] create__resolver_scanner_index_offsets() { + short[] r = new short[108]; + init__resolver_scanner_index_offsets_0(r); + return r; + } + + private static short[] _resolver_scanner_index_offsets = create__resolver_scanner_index_offsets(); + + + private static void init__resolver_scanner_indicies_0(byte[] r) { + r[0] = 0; r[1] = 3; r[2] = 4; r[3] = 5; r[4] = 7; r[5] = 8; r[6] = 9; r[7] = 10; + r[8] = 11; r[9] = 12; r[10] = 13; r[11] = 14; r[12] = 15; r[13] = 16; r[14] = 17; r[15] = 18; + r[16] = 0; r[17] = 2; r[18] = 6; r[19] = 1; r[20] = 3; r[21] = 19; r[22] = 20; r[23] = 21; + r[24] = 1; r[25] = 3; r[26] = 22; r[27] = 3; r[28] = 1; r[29] = 23; r[30] = 23; r[31] = 1; + r[32] = 24; r[33] = 1; r[34] = 25; r[35] = 26; r[36] = 22; r[37] = 1; r[38] = 27; r[39] = 28; + r[40] = 1; r[41] = 29; r[42] = 1; r[43] = 29; r[44] = 1; r[45] = 28; r[46] = 1; r[47] = 3; + r[48] = 22; r[49] = 31; r[50] = 30; r[51] = 1; r[52] = 32; r[53] = 33; r[54] = 1; r[55] = 24; + r[56] = 31; r[57] = 33; r[58] = 1; r[59] = 24; r[60] = 31; r[61] = 1; r[62] = 34; r[63] = 34; + r[64] = 1; r[65] = 35; r[66] = 35; r[67] = 35; r[68] = 35; r[69] = 35; r[70] = 1; r[71] = 36; + r[72] = 37; r[73] = 1; r[74] = 38; r[75] = 39; r[76] = 1; r[77] = 25; r[78] = 40; r[79] = 26; + r[80] = 41; r[81] = 22; r[82] = 1; r[83] = 42; r[84] = 42; r[85] = 1; r[86] = 29; r[87] = 1; + r[88] = 43; r[89] = 1; r[90] = 29; r[91] = 1; r[92] = 44; r[93] = 1; r[94] = 45; r[95] = 46; + r[96] = 1; r[97] = 47; r[98] = 1; r[99] = 48; r[100] = 48; r[101] = 50; r[102] = 50; r[103] = 49; + r[104] = 1; r[105] = 48; r[106] = 48; r[107] = 51; r[108] = 1; r[109] = 53; r[110] = 52; r[111] = 1; + r[112] = 53; r[113] = 1; r[114] = 54; r[115] = 1; r[116] = 55; r[117] = 1; r[118] = 56; r[119] = 1; + r[120] = 57; r[121] = 1; r[122] = 58; r[123] = 1; r[124] = 59; r[125] = 59; r[126] = 60; r[127] = 60; + r[128] = 61; r[129] = 62; r[130] = 1; r[131] = 59; r[132] = 59; r[133] = 60; r[134] = 60; r[135] = 62; + r[136] = 1; r[137] = 63; r[138] = 1; r[139] = 64; r[140] = 1; r[141] = 62; r[142] = 1; r[143] = 59; + r[144] = 59; r[145] = 60; r[146] = 60; r[147] = 62; r[148] = 61; r[149] = 1; r[150] = 48; r[151] = 48; + r[152] = 50; r[153] = 50; r[154] = 1; r[155] = 51; r[156] = 1; r[157] = 65; r[158] = 1; r[159] = 66; + r[160] = 1; r[161] = 48; r[162] = 48; r[163] = 50; r[164] = 50; r[165] = 67; r[166] = 1; r[167] = 3; + r[168] = 68; r[169] = 22; r[170] = 31; r[171] = 30; r[172] = 1; r[173] = 3; r[174] = 22; r[175] = 31; + r[176] = 69; r[177] = 1; r[178] = 3; r[179] = 22; r[180] = 31; r[181] = 70; r[182] = 1; r[183] = 71; + r[184] = 1; r[185] = 72; r[186] = 73; r[187] = 1; r[188] = 74; r[189] = 1; r[190] = 75; r[191] = 1; + r[192] = 76; r[193] = 1; r[194] = 77; r[195] = 1; r[196] = 78; r[197] = 1; r[198] = 76; r[199] = 1; + r[200] = 76; r[201] = 79; r[202] = 76; r[203] = 80; r[204] = 1; r[205] = 81; r[206] = 1; r[207] = 0; + r[208] = 1; r[209] = 82; r[210] = 1; r[211] = 0; r[212] = 1; r[213] = 83; r[214] = 76; r[215] = 84; + r[216] = 76; r[217] = 1; r[218] = 76; r[219] = 1; r[220] = 76; r[221] = 1; r[222] = 85; r[223] = 86; + r[224] = 1; r[225] = 75; r[226] = 1; r[227] = 78; r[228] = 1; r[229] = 87; r[230] = 88; r[231] = 1; + r[232] = 76; r[233] = 1; r[234] = 76; r[235] = 1; r[236] = 73; r[237] = 1; r[238] = 76; r[239] = 80; + r[240] = 1; r[241] = 84; r[242] = 76; r[243] = 1; r[244] = 86; r[245] = 1; r[246] = 88; r[247] = 1; + r[248] = 1; r[249] = 89; r[250] = 89; r[251] = 22; r[252] = 1; r[253] = 24; r[254] = 1; r[255] = 1; + r[256] = 3; r[257] = 22; r[258] = 31; r[259] = 91; r[260] = 92; r[261] = 93; r[262] = 90; r[263] = 30; + r[264] = 1; r[265] = 3; r[266] = 22; r[267] = 31; r[268] = 91; r[269] = 90; r[270] = 30; r[271] = 1; + r[272] = 91; r[273] = 91; r[274] = 1; r[275] = 34; r[276] = 34; r[277] = 1; r[278] = 35; r[279] = 35; + r[280] = 35; r[281] = 35; r[282] = 35; r[283] = 1; r[284] = 94; r[285] = 22; r[286] = 95; r[287] = 96; + r[288] = 21; r[289] = 1; r[290] = 94; r[291] = 22; r[292] = 97; r[293] = 96; r[294] = 94; r[295] = 1; + r[296] = 97; r[297] = 37; r[298] = 1; r[299] = 97; r[300] = 1; r[301] = 96; r[302] = 97; r[303] = 96; + r[304] = 96; r[305] = 1; r[306] = 24; r[307] = 95; r[308] = 39; r[309] = 1; r[310] = 24; r[311] = 95; + r[312] = 1; r[313] = 3; r[314] = 22; r[315] = 31; r[316] = 91; r[317] = 92; r[318] = 93; r[319] = 98; + r[320] = 99; r[321] = 1; r[322] = 3; r[323] = 22; r[324] = 31; r[325] = 91; r[326] = 100; r[327] = 70; + r[328] = 1; r[329] = 3; r[330] = 22; r[331] = 31; r[332] = 91; r[333] = 101; r[334] = 69; r[335] = 1; + r[336] = 3; r[337] = 68; r[338] = 22; r[339] = 31; r[340] = 91; r[341] = 90; r[342] = 30; r[343] = 1; + r[344] = 103; r[345] = 102; r[346] = 1; r[347] = 103; r[348] = 1; r[349] = 1; r[350] = 48; r[351] = 48; + r[352] = 50; r[353] = 50; r[354] = 1; r[355] = 94; r[356] = 22; r[357] = 95; r[358] = 96; r[359] = 104; + r[360] = 1; r[361] = 94; r[362] = 22; r[363] = 95; r[364] = 96; r[365] = 105; r[366] = 1; r[367] = 94; + r[368] = 22; r[369] = 95; r[370] = 96; r[371] = 106; r[372] = 1; r[373] = 94; r[374] = 68; r[375] = 22; + r[376] = 95; r[377] = 96; r[378] = 21; r[379] = 1; r[380] = 1; r[381] = 1; r[382] = 1; r[383] = 0; + } + + private static byte[] create__resolver_scanner_indicies() { + byte[] r = new byte[384]; + init__resolver_scanner_indicies_0(r); + return r; + } + + private static byte[] _resolver_scanner_indicies = create__resolver_scanner_indicies(); + + + private static void init__resolver_scanner_trans_targs_wi_0(byte[] r) { + r[0] = 77; r[1] = 0; r[2] = 2; r[3] = 3; r[4] = 19; r[5] = 93; r[6] = 101; r[7] = 50; + r[8] = 106; r[9] = 51; r[10] = 58; r[11] = 63; r[12] = 66; r[13] = 69; r[14] = 72; r[15] = 73; + r[16] = 74; r[17] = 75; r[18] = 76; r[19] = 6; r[20] = 81; r[21] = 86; r[22] = 78; r[23] = 5; + r[24] = 79; r[25] = 7; r[26] = 10; r[27] = 8; r[28] = 9; r[29] = 80; r[30] = 11; r[31] = 12; + r[32] = 13; r[33] = 14; r[34] = 84; r[35] = 85; r[36] = 88; r[37] = 89; r[38] = 91; r[39] = 92; + r[40] = 20; r[41] = 22; r[42] = 21; r[43] = 23; r[44] = 25; r[45] = 26; r[46] = 44; r[47] = 27; + r[48] = 28; r[49] = 42; r[50] = 43; r[51] = 29; r[52] = 30; r[53] = 31; r[54] = 32; r[55] = 33; + r[56] = 34; r[57] = 35; r[58] = 36; r[59] = 37; r[60] = 38; r[61] = 41; r[62] = 99; r[63] = 97; + r[64] = 40; r[65] = 45; r[66] = 46; r[67] = 100; r[68] = 24; r[69] = 47; r[70] = 48; r[71] = 105; + r[72] = 52; r[73] = 55; r[74] = 53; r[75] = 54; r[76] = 107; r[77] = 56; r[78] = 57; r[79] = 59; + r[80] = 61; r[81] = 60; r[82] = 62; r[83] = 64; r[84] = 65; r[85] = 67; r[86] = 68; r[87] = 70; + r[88] = 71; r[89] = 4; r[90] = 82; r[91] = 83; r[92] = 15; r[93] = 16; r[94] = 87; r[95] = 18; + r[96] = 90; r[97] = 17; r[98] = 94; r[99] = 49; r[100] = 95; r[101] = 96; r[102] = 98; r[103] = 39; + r[104] = 102; r[105] = 103; r[106] = 104; + } + + private static byte[] create__resolver_scanner_trans_targs_wi() { + byte[] r = new byte[107]; + init__resolver_scanner_trans_targs_wi_0(r); + return r; + } + + private static byte[] _resolver_scanner_trans_targs_wi = create__resolver_scanner_trans_targs_wi(); + + + private static void init__resolver_scanner_trans_actions_wi_0(byte[] r) { + r[0] = 0; r[1] = 0; r[2] = 0; r[3] = 0; r[4] = 0; r[5] = 0; r[6] = 0; r[7] = 0; + r[8] = 0; r[9] = 0; r[10] = 0; r[11] = 0; r[12] = 0; r[13] = 0; r[14] = 0; r[15] = 0; + r[16] = 0; r[17] = 0; r[18] = 0; r[19] = 0; r[20] = 0; r[21] = 0; r[22] = 0; r[23] = 0; + r[24] = 0; r[25] = 0; r[26] = 0; r[27] = 0; r[28] = 0; r[29] = 0; r[30] = 0; r[31] = 0; + r[32] = 0; r[33] = 0; r[34] = 0; r[35] = 0; r[36] = 0; r[37] = 0; r[38] = 0; r[39] = 0; + r[40] = 0; r[41] = 0; r[42] = 0; r[43] = 0; r[44] = 0; r[45] = 0; r[46] = 0; r[47] = 0; + r[48] = 0; r[49] = 0; r[50] = 0; r[51] = 0; r[52] = 0; r[53] = 0; r[54] = 0; r[55] = 0; + r[56] = 0; r[57] = 0; r[58] = 0; r[59] = 0; r[60] = 0; r[61] = 0; r[62] = 0; r[63] = 0; + r[64] = 0; r[65] = 0; r[66] = 0; r[67] = 0; r[68] = 0; r[69] = 0; r[70] = 0; r[71] = 0; + r[72] = 0; r[73] = 0; r[74] = 0; r[75] = 0; r[76] = 0; r[77] = 0; r[78] = 0; r[79] = 0; + r[80] = 0; r[81] = 0; r[82] = 0; r[83] = 0; r[84] = 0; r[85] = 0; r[86] = 0; r[87] = 0; + r[88] = 0; r[89] = 0; r[90] = 0; r[91] = 0; r[92] = 0; r[93] = 0; r[94] = 0; r[95] = 0; + r[96] = 0; r[97] = 0; r[98] = 0; r[99] = 0; r[100] = 0; r[101] = 0; r[102] = 0; r[103] = 0; + r[104] = 0; r[105] = 0; r[106] = 0; + } + + private static byte[] create__resolver_scanner_trans_actions_wi() { + byte[] r = new byte[107]; + init__resolver_scanner_trans_actions_wi_0(r); + return r; + } + + private static byte[] _resolver_scanner_trans_actions_wi = create__resolver_scanner_trans_actions_wi(); + + + private static void init__resolver_scanner_eof_actions_0(byte[] r) { + r[0] = 0; r[1] = 0; r[2] = 0; r[3] = 0; r[4] = 0; r[5] = 0; r[6] = 0; r[7] = 0; + r[8] = 0; r[9] = 0; r[10] = 0; r[11] = 0; r[12] = 0; r[13] = 0; r[14] = 0; r[15] = 0; + r[16] = 0; r[17] = 0; r[18] = 0; r[19] = 0; r[20] = 0; r[21] = 0; r[22] = 0; r[23] = 0; + r[24] = 0; r[25] = 0; r[26] = 0; r[27] = 0; r[28] = 0; r[29] = 0; r[30] = 0; r[31] = 0; + r[32] = 0; r[33] = 0; r[34] = 0; r[35] = 0; r[36] = 0; r[37] = 0; r[38] = 0; r[39] = 0; + r[40] = 0; r[41] = 0; r[42] = 0; r[43] = 0; r[44] = 0; r[45] = 0; r[46] = 0; r[47] = 0; + r[48] = 0; r[49] = 0; r[50] = 0; r[51] = 0; r[52] = 0; r[53] = 0; r[54] = 0; r[55] = 0; + r[56] = 0; r[57] = 0; r[58] = 0; r[59] = 0; r[60] = 0; r[61] = 0; r[62] = 0; r[63] = 0; + r[64] = 0; r[65] = 0; r[66] = 0; r[67] = 0; r[68] = 0; r[69] = 0; r[70] = 0; r[71] = 0; + r[72] = 0; r[73] = 0; r[74] = 0; r[75] = 0; r[76] = 0; r[77] = 5; r[78] = 13; r[79] = 13; + r[80] = 13; r[81] = 15; r[82] = 15; r[83] = 15; r[84] = 15; r[85] = 15; r[86] = 15; r[87] = 15; + r[88] = 15; r[89] = 15; r[90] = 15; r[91] = 15; r[92] = 15; r[93] = 15; r[94] = 15; r[95] = 15; + r[96] = 15; r[97] = 9; r[98] = 9; r[99] = 9; r[100] = 7; r[101] = 15; r[102] = 15; r[103] = 15; + r[104] = 15; r[105] = 3; r[106] = 11; r[107] = 1; + } + + private static byte[] create__resolver_scanner_eof_actions() { + byte[] r = new byte[108]; + init__resolver_scanner_eof_actions_0(r); + return r; + } + + private static byte[] _resolver_scanner_eof_actions = create__resolver_scanner_eof_actions(); + + + static int resolver_scanner_start = 1; + + // line 55 "src/org/jvyamlb/resolver_scanner.rl" + + internal static string Recognize(string str) { + // TODO: scanner should be char based + byte[] data = System.Text.Encoding.ASCII.GetBytes(str); + string tag = null; + int cs; + int p = 0; + int pe = data.Length; + + if (pe == 0) { + data = new byte[] { (byte)'~' }; + pe = 1; + } + + // line 372 "src/org/jvyamlb/ResolverScanner.java" + { + cs = resolver_scanner_start; + } + // line 74 "src/org/jvyamlb/resolver_scanner.rl" + + + // line 379 "src/org/jvyamlb/ResolverScanner.java" + { + int _klen; + int _trans; + int _keys; + + if (p != pe) { + if (cs != 0) { + while (true) { + do { + do { + _keys = _resolver_scanner_key_offsets[cs]; + _trans = _resolver_scanner_index_offsets[cs]; + _klen = _resolver_scanner_single_lengths[cs]; + if (_klen > 0) { + int _lower = _keys; + int _mid; + int _upper = _keys + _klen - 1; + while (true) { + if (_upper < _lower) + break; + + _mid = _lower + ((_upper - _lower) >> 1); + if (data[p] < _resolver_scanner_trans_keys[_mid]) + _upper = _mid - 1; + else if (data[p] > _resolver_scanner_trans_keys[_mid]) + _lower = _mid + 1; + else { + _trans += (_mid - _keys); + goto _match; + } + } + _keys += _klen; + _trans += _klen; + } + + _klen = _resolver_scanner_range_lengths[cs]; + if (_klen > 0) { + int _lower = _keys; + int _mid; + int _upper = _keys + (_klen << 1) - 2; + while (true) { + if (_upper < _lower) + break; + + _mid = _lower + (((_upper - _lower) >> 1) & ~1); + if (data[p] < _resolver_scanner_trans_keys[_mid]) + _upper = _mid - 2; + else if (data[p] > _resolver_scanner_trans_keys[_mid + 1]) + _lower = _mid + 2; + else { + _trans += ((_mid - _keys) >> 1); + goto _match; + } + } + _trans += _klen; + } + } while (false); + _match: + _trans = _resolver_scanner_indicies[_trans]; + cs = _resolver_scanner_trans_targs_wi[_trans]; + + } while (false); + if (cs == 0) + goto _resume; + if (++p == pe) + goto _resume; + } + _resume: + ; + } + } + } + // line 76 "src/org/jvyamlb/resolver_scanner.rl" + + + // line 452 "src/org/jvyamlb/ResolverScanner.java" + int _acts = _resolver_scanner_eof_actions[cs]; + int _nacts = (int)_resolver_scanner_actions[_acts++]; + while (_nacts-- > 0) { + switch (_resolver_scanner_actions[_acts++]) { + case 0: + // line 10 "src/org/jvyamlb/resolver_scanner.rl" + { tag = "tag:yaml.org,2002:bool"; } + break; + case 1: + // line 11 "src/org/jvyamlb/resolver_scanner.rl" + { tag = "tag:yaml.org,2002:merge"; } + break; + case 2: + // line 12 "src/org/jvyamlb/resolver_scanner.rl" + { tag = "tag:yaml.org,2002:null"; } + break; + case 3: + // line 13 "src/org/jvyamlb/resolver_scanner.rl" + { tag = "tag:yaml.org,2002:timestamp#ymd"; } + break; + case 4: + // line 14 "src/org/jvyamlb/resolver_scanner.rl" + { tag = "tag:yaml.org,2002:timestamp"; } + break; + case 5: + // line 15 "src/org/jvyamlb/resolver_scanner.rl" + { tag = "tag:yaml.org,2002:value"; } + break; + case 6: + // line 16 "src/org/jvyamlb/resolver_scanner.rl" + { tag = "tag:yaml.org,2002:float"; } + break; + case 7: + // line 17 "src/org/jvyamlb/resolver_scanner.rl" + { tag = "tag:yaml.org,2002:int"; } + break; + // line 489 "src/org/jvyamlb/ResolverScanner.java" + } + } + + // line 78 "src/org/jvyamlb/resolver_scanner.rl" + return tag; + } + } +} \ No newline at end of file diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/SafeConstructor.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/SafeConstructor.cs new file mode 100644 index 0000000000..47ac705cca --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/SafeConstructor.cs @@ -0,0 +1,375 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Text.RegularExpressions; +using IronRuby.Builtins; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.Yaml { + + public class SafeConstructor : BaseConstructor { + private readonly static Dictionary _yamlConstructors = new Dictionary(); + private readonly static Dictionary _yamlMultiConstructors = new Dictionary(); + private readonly static Dictionary _yamlMultiRegexps = new Dictionary(); + + public override YamlConstructor GetYamlConstructor(string key) { + YamlConstructor result; + if (_yamlConstructors.TryGetValue(key, out result)) { + return result; + } + return base.GetYamlConstructor(key); + } + + public override YamlMultiConstructor GetYamlMultiConstructor(string key) { + YamlMultiConstructor result; + if (_yamlMultiConstructors.TryGetValue(key, out result)) { + return result; + } + return base.GetYamlMultiConstructor(key); + } + + public override Regex GetYamlMultiRegexp(string key) { + Regex result; + if (_yamlMultiRegexps.TryGetValue(key, out result)) { + return result; + } + return base.GetYamlMultiRegexp(key); + } + + public override ICollection GetYamlMultiRegexps() { + return _yamlMultiRegexps.Keys; + } + + public new static void AddConstructor(string tag, YamlConstructor ctor) { + _yamlConstructors.Add(tag, ctor); + } + + public new static void AddMultiConstructor(string tagPrefix, YamlMultiConstructor ctor) { + _yamlMultiConstructors.Add(tagPrefix, ctor); + _yamlMultiRegexps.Add(tagPrefix, new Regex("^" + tagPrefix, RegexOptions.Compiled)); + } + + public SafeConstructor(/*!*/NodeProvider nodeProvider, RubyScope/*!*/ scope) + : base(nodeProvider, scope) { + } + + private static Dictionary BOOL_VALUES = new Dictionary(); + + public static object ConstructYamlNull(IConstructor ctor, Node node) { + return null; + } + + public static object ConstructYamlBool(IConstructor ctor, Node node) { + bool result; + if (TryConstructYamlBool(ctor, node, out result)) { + return result; + } + return null; + } + + public static bool TryConstructYamlBool(IConstructor ctor, Node node, out bool result) { + if (BOOL_VALUES.TryGetValue(ctor.ConstructScalar(node).ToString(), out result)) { + return true; + } + return false; + } + + public static object ConstructYamlOmap(IConstructor ctor, Node node) { + return ctor.ConstructPairs(node); + } + + public static object ConstructYamlPairs(IConstructor ctor, Node node) { + return ConstructYamlOmap(ctor, node); + } + + public static ICollection ConstructYamlSet(IConstructor ctor, Node node) { + return ctor.ConstructMapping(node).Keys; + } + + public static string ConstructYamlStr(IConstructor ctor, Node node) { + string value = ctor.ConstructScalar(node).ToString(); + return value.Length != 0 ? value : null; + } + + public static RubyArray ConstructYamlSeq(IConstructor ctor, Node node) { + return ctor.ConstructSequence(node); + } + + public static Hash ConstructYamlMap(IConstructor ctor, Node node) { + return ctor.ConstructMapping(node); + } + + public static object constructUndefined(IConstructor ctor, Node node) { + throw new ConstructorException("could not determine a constructor for the tag: " + node.Tag); + } + + private static Regex TIMESTAMP_REGEXP = new Regex("^(-?[0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:(?:[Tt]|[ \t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \t]*(Z|([-+][0-9][0-9]?)(?::([0-9][0-9])?)?)))?$"); + internal static Regex YMD_REGEXP = new Regex("^(-?[0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)$"); + + public static object ConstructYamlTimestampYMD(IConstructor ctor, Node node) { + ScalarNode scalar = node as ScalarNode; + if (scalar == null) { + throw new ConstructorException("can only contruct timestamp from scalar node"); + } + + Match match = YMD_REGEXP.Match(scalar.Value); + if (match.Success) { + int year_ymd = int.Parse(match.Groups[1].Value); + int month_ymd = int.Parse(match.Groups[2].Value); + int day_ymd = int.Parse(match.Groups[3].Value); + + return new DateTime(year_ymd, month_ymd, day_ymd); + } + throw new ConstructorException("Invalid tag:yaml.org,2002:timestamp#ymd value."); + } + + public static object ConstructYamlTimestamp(IConstructor ctor, Node node) { + ScalarNode scalar = node as ScalarNode; + if (scalar == null) { + throw new ConstructorException("can only contruct timestamp from scalar node"); + } + + Match match = TIMESTAMP_REGEXP.Match(scalar.Value); + + if (!match.Success) { + return ctor.ConstructPrivateType(node); + } + + string year_s = match.Groups[1].Value; + string month_s = match.Groups[2].Value; + string day_s = match.Groups[3].Value; + string hour_s = match.Groups[4].Value; + string min_s = match.Groups[5].Value; + string sec_s = match.Groups[6].Value; + string fract_s = match.Groups[7].Value; + string utc = match.Groups[8].Value; + string timezoneh_s = match.Groups[9].Value; + string timezonem_s = match.Groups[10].Value; + + bool isUtc = utc == "Z" || utc == "z"; + + DateTime dt = new DateTime( + year_s != "" ? int.Parse(year_s) : 0, + month_s != "" ? int.Parse(month_s) : 1, + day_s != "" ? int.Parse(day_s) : 1, + hour_s != "" ? int.Parse(hour_s) : 0, + min_s != "" ? int.Parse(min_s) : 0, + sec_s != "" ? int.Parse(sec_s) : 0, + isUtc? DateTimeKind.Utc : DateTimeKind.Local + ); + + if (!string.IsNullOrEmpty(fract_s)) { + long fract = int.Parse(fract_s); + if (fract > 0) { + while (fract < 1000000) { + fract *= 10; + } + dt = dt.AddTicks(fract); + } + } + + if (!isUtc) { + if (timezoneh_s != "" || timezonem_s != "") { + int zone = 0; + int sign = +1; + if (timezoneh_s != "") { + if (timezoneh_s.StartsWith("-")) { + sign = -1; + } + zone += int.Parse(timezoneh_s.Substring(1)) * 3600000; + } + if (timezonem_s != "") { + zone += int.Parse(timezonem_s) * 60000; + } + double utcOffset = TimeZone.CurrentTimeZone.GetUtcOffset(dt).TotalMilliseconds; + dt = dt.AddMilliseconds(utcOffset - sign * zone); + } + } + return dt; + } + + public static object ConstructYamlInt(IConstructor ctor, Node node) { + string value = ctor.ConstructScalar(node).ToString().Replace("_","").Replace(",",""); + int sign = +1; + char first = value[0]; + if(first == '-') { + sign = -1; + value = value.Substring(1); + } else if(first == '+') { + value = value.Substring(1); + } + int @base = 10; + if (value == "0") { + return 0; + } else if (value.StartsWith("0b")) { + value = value.Substring(2); + @base = 2; + } else if (value.StartsWith("0x")) { + value = value.Substring(2); + @base = 16; + } else if (value.StartsWith("0")) { + value = value.Substring(1); + @base = 8; + } else if (value.IndexOf(':') != -1) { + string[] digits = value.Split(':'); + int bes = 1; + int val = 0; + for (int i = 0, j = digits.Length; i < j; i++) { + val += (int.Parse(digits[(j - i) - 1]) * bes); + bes *= 60; + } + return sign*val; + } + + try { + // LiteralParser.ParseInteger delegate handles parsing & conversion to BigInteger (if needed) + return LiteralParser.ParseInteger(sign, value, @base); + } catch (Exception e) { + throw new ConstructorException(string.Format("Could not parse integer value: '{0}' (sign {1}, base {2})", value, sign, @base), e); + } + } + + public static object ConstructYamlFloat(IConstructor ctor, Node node) { + string value = ctor.ConstructScalar(node).ToString().Replace("_", "").Replace(",", ""); + int sign = +1; + char first = value[0]; + if (first == '-') { + sign = -1; + value = value.Substring(1); + } else if (first == '+') { + value = value.Substring(1); + } + string valLower = value.ToLower(); + if (valLower == ".inf") { + return sign == -1 ? double.NegativeInfinity : double.PositiveInfinity; + } else if (valLower == ".nan") { + return double.NaN; + } else if (value.IndexOf(':') != -1) { + string[] digits = value.Split(':'); + int bes = 1; + double val = 0.0; + for (int i = 0, j = digits.Length; i < j; i++) { + val += (double.Parse(digits[(j - i) - 1]) * bes); + bes *= 60; + } + return sign * val; + } else { + return sign * double.Parse(value); + } + } + + public static byte[] ConstructYamlBinary(IConstructor ctor, Node node) { + string val = ctor.ConstructScalar(node).ToString().Replace("\r", "").Replace("\n", ""); + return Convert.FromBase64String(val); + } + + public static object ConstructSpecializedSequence(IConstructor ctor, string pref, Node node) { + RubyArray result = null; + try { + result = (RubyArray)Type.GetType(pref).GetConstructor(Type.EmptyTypes).Invoke(null); + } catch (Exception e) { + throw new ConstructorException("Can't construct a sequence from class: " + pref, e); + } + foreach (object x in ctor.ConstructSequence(node)) { + result.Add(x); + } + return result; + } + + public static object ConstructSpecializedMap(IConstructor ctor, string pref, Node node) { + Hash result = null; + try { + result = (Hash)Type.GetType(pref).GetConstructor(Type.EmptyTypes).Invoke(null); + } catch (Exception e) { + throw new ConstructorException("Can't construct a mapping from class: " + pref, e); + } + foreach (KeyValuePair e in ctor.ConstructMapping(node)) { + result.Add(e.Key, e.Value); + } + return result; + } + + public static object ConstructCliObject(IConstructor ctor, string pref, Node node) { + // TODO: should this use serialization or some more standard CLR mechanism? + // (it is very ad-hoc) + // TODO: use DLR APIs instead of reflection + try { + Type type = Type.GetType(pref); + object result = type.GetConstructor(Type.EmptyTypes).Invoke(null); + + foreach (KeyValuePair e in ctor.ConstructMapping(node)) { + string name = e.Key.ToString(); + name = "" + char.ToUpper(name[0]) + name.Substring(1); + PropertyInfo prop = type.GetProperty(name); + + prop.SetValue(result, Convert.ChangeType(e.Value, prop.PropertyType), null); + } + return result; + + } catch (Exception e) { + throw new ConstructorException("Can't construct a CLI object from class: " + pref, e); + } + } + + static SafeConstructor() { + BOOL_VALUES.Add("yes", true); + BOOL_VALUES.Add("Yes", true); + BOOL_VALUES.Add("YES", true); + BOOL_VALUES.Add("no", false); + BOOL_VALUES.Add("No", false); + BOOL_VALUES.Add("NO", false); + BOOL_VALUES.Add("true", true); + BOOL_VALUES.Add("True", true); + BOOL_VALUES.Add("TRUE", true); + BOOL_VALUES.Add("false", false); + BOOL_VALUES.Add("False", false); + BOOL_VALUES.Add("FALSE", false); + BOOL_VALUES.Add("on", true); + BOOL_VALUES.Add("On", true); + BOOL_VALUES.Add("ON", true); + BOOL_VALUES.Add("off", false); + BOOL_VALUES.Add("Off", false); + BOOL_VALUES.Add("OFF", false); + + BaseConstructor.AddConstructor("tag:yaml.org,2002:null", ConstructYamlNull); + AddConstructor("tag:yaml.org,2002:bool", ConstructYamlBool); + AddConstructor("tag:yaml.org,2002:omap", ConstructYamlOmap); + AddConstructor("tag:yaml.org,2002:pairs", ConstructYamlPairs); + AddConstructor("tag:yaml.org,2002:set", ConstructYamlSet); + AddConstructor("tag:yaml.org,2002:int", ConstructYamlInt); + AddConstructor("tag:yaml.org,2002:float", ConstructYamlFloat); + AddConstructor("tag:yaml.org,2002:timestamp", ConstructYamlTimestamp); + AddConstructor("tag:yaml.org,2002:timestamp#ymd", ConstructYamlTimestampYMD); + AddConstructor("tag:yaml.org,2002:str", ConstructYamlStr); + AddConstructor("tag:yaml.org,2002:binary", ConstructYamlBinary); + AddConstructor("tag:yaml.org,2002:seq", ConstructYamlSeq); + AddConstructor("tag:yaml.org,2002:map", ConstructYamlMap); + AddConstructor("", BaseConstructor.CONSTRUCT_PRIVATE); + AddMultiConstructor("tag:yaml.org,2002:seq:", ConstructSpecializedSequence); + AddMultiConstructor("tag:yaml.org,2002:map:", ConstructSpecializedMap); + AddMultiConstructor("!cli/object:", ConstructCliObject); + AddMultiConstructor("tag:cli.yaml.org,2002:object:", ConstructCliObject); + } + + } +} \ No newline at end of file diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Scanner.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Scanner.cs new file mode 100644 index 0000000000..a0e3a31f45 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Scanner.cs @@ -0,0 +1,1408 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +namespace IronRuby.StandardLibrary.Yaml { + + public class Scanner : IEnumerable { + + private class SimpleKey { + internal readonly int TokenNumber; + internal readonly bool Required; + internal readonly int Index; + internal readonly int Line; + internal readonly int Column; + + internal SimpleKey(int tokenNumber, bool required, int index, int line, int column) { + TokenNumber = tokenNumber; + Required = required; + Index = index; + Line = line; + Column = column; + } + } + + private bool _done = false; + private int _flowLevel = 0; + private int _tokensTaken = 0; + private int _indent = -1; + private bool _allowSimpleKey = true; + private bool _eof = false; + private int _column = 0; + private bool _docStart = false; + private readonly LinkedList _tokens = new LinkedList(); + private readonly Stack _indents = new Stack(); + private Dictionary _possibleSimpleKeys = new Dictionary(); + private readonly TextReader _reader; + + private char[] _buffer = new char[10]; + private int _count = 0; + private int _pointer = 0; + + public Scanner(TextReader reader) { + _reader = reader; + FetchStreamStart(); + } + + private void Update(int length, bool reset) { + if (!_eof && reset) { + // remove from [0, _pointer) + int newCount = _count - _pointer; + Array.Copy(_buffer, _pointer, _buffer, 0, newCount); + _pointer = 0; + _count = newCount; + } + + int desiredCount = _pointer + length + 1; + EnsureCapacity(desiredCount); + + while (_count < desiredCount) { + int charsRead = 0; + if (!_eof) { + try { + charsRead = _reader.Read(_buffer, _count, desiredCount - _count); + } catch (IOException e) { + throw new YamlException(e.Message, e); + } + if (charsRead == 0) { + _eof = true; + } + } + if (_eof) { + EnsureCapacity(_count + 1); + // TODO: scanner should not be using \0 as an end of stream marker + _buffer[_count++] = '\0'; + return; + } else { + //checkPrintable(_count, charsRead); + _count += charsRead; + } + } + } + + private void EnsureCapacity(int min) { + if (_buffer.Length < min) { + int newSize = Math.Max(_buffer.Length * 2, min); + char[] newBuffer = new char[newSize]; + if (_count > 0) { + Array.Copy(_buffer, 0, newBuffer, 0, _count); + } + _buffer = newBuffer; + } + } + + // This function was a no-op because NON_PRINTABLE was not set in the original code + //private void checkPrintable(int start, int len) { + // for (int i = start; i < start + len; i++) { + // if (NON_PRINTABLE[_buffer[i]]) { + // throw new YamlException("At " + i + " we found: " + _buffer[i] + ". Special characters are not allowed"); + // } + // } + //} + + private bool Ensure(int len, bool reset) { + if (_pointer + len >= _count) { + Update(len, reset); + } + return true; + } + + private char Peek() { + Ensure(1, false); + return _buffer[_pointer]; + } + + private char Peek(int index) { + Ensure(index + 1, false); + return _buffer[_pointer + index]; + } + + private void Forward() { + Ensure(2, true); + char ch1 = _buffer[_pointer++]; + if (ch1 == '\n' || (ch1 == '\r' && _buffer[_pointer] != '\n')) { + _possibleSimpleKeys.Clear(); + _column = 0; + } else { + _column++; + } + } + + private void Forward(int length) { + Ensure(length + 1, true); + for (int i = 0; i < length; i++) { + char ch = _buffer[_pointer]; + _pointer++; + if (ch == '\n' || (ch == '\r' && _buffer[_pointer] != '\n')) { + _possibleSimpleKeys.Clear(); + _column = 0; + } else { + _column++; + } + } + } + + public Token PeekToken() { + return PeekToken(0); + } + + public Token GetToken() { + while (NeedMoreTokens()) { + FetchMoreTokens(); + } + if (_tokens.Count > 0) { + _tokensTaken++; + Token t = _tokens.First.Value; + _tokens.RemoveFirst(); + return t; + } + return null; + } + + + private Token PeekToken(int index) { + while (NeedMoreTokens(index + 1)) { + FetchMoreTokens(); + } + return (_tokens.Count > 0) ? _tokens.First.Value : null; + } + + private bool NeedMoreTokens() { + return NeedMoreTokens(1); + } + + private bool NeedMoreTokens(int needed) { + return !_done && (_tokens.Count < needed || NextPossibleSimpleKey() == _tokensTaken); + } + + private Token AddToken(Token t) { + _tokens.AddLast(t); + return t; + } + + private bool IsEnding() { + Ensure(4, false); + return (_buffer[_pointer]) == '-' && + (_buffer[_pointer + 1]) == '-' && + (_buffer[_pointer + 2]) == '-' && + (_buffer[_pointer + 3] != 0) && + !(_count <= (_pointer + 4) || + ((_buffer[_pointer + 3] == '\n') && + (_buffer[_pointer + 4] == 0))) && + (NULL_BL_T_LINEBR(_buffer[_pointer + 3])); + } + + private bool IsStart() { + Ensure(4, false); + return (_buffer[_pointer]) == '.' && + (_buffer[_pointer + 1]) == '.' && + (_buffer[_pointer + 2]) == '.' && + (NULL_BL_T_LINEBR(_buffer[_pointer + 3])); + } + + private bool IsEndOrStart() { + Ensure(4, false); + return (((_buffer[_pointer]) == '-' && + (_buffer[_pointer + 1]) == '-' && + (_buffer[_pointer + 2]) == '-') || + ((_buffer[_pointer]) == '.' && + (_buffer[_pointer + 1]) == '.' && + (_buffer[_pointer + 2]) == '.')) && + (NULL_BL_T_LINEBR(_buffer[_pointer + 3])); + } + + private Token FetchMoreTokens() { + ScanToNextToken(); + UnwindIndent(_column); + char ch = Peek(); + bool colz = _column == 0; + switch (ch) { + case '\0': return FetchStreamEnd(); + case '\'': return FetchSingle(); + case '"': return FetchDouble(); + case '?': if (_flowLevel != 0 || NULL_BL_T_LINEBR(Peek(1))) { return FetchKey(); } break; + case ':': if (_flowLevel != 0 || NULL_BL_T_LINEBR(Peek(1))) { return FetchValue(); } break; + case '%': if (colz) { return FetchDirective(); } break; + case '-': + if ((colz || _docStart) && IsEnding()) { + return FetchDocumentStart(); + } else if (NULL_BL_T_LINEBR(Peek(1))) { + return FetchBlockEntry(); + } + break; + case '.': + if (colz && IsStart()) { + return FetchDocumentEnd(); + } + break; + case '[': return FetchFlowSequenceStart(); + case '{': return FetchFlowMappingStart(); + case ']': return FetchFlowSequenceEnd(); + case '}': return FetchFlowMappingEnd(); + case ',': return fetchFlowEntry(); + case '*': return FetchAlias(); + case '&': return FetchAnchor(); + case '!': return FetchTag(); + case '|': if (_flowLevel == 0) { return FetchLiteral(); } break; + case '>': if (_flowLevel == 0) { return FetchFolded(); } break; + } + + //TODO: this is probably incorrect... + char c2 = _buffer[_pointer]; + if (NOT_USEFUL_CHAR(c2) || + (Ensure(1, false) && (c2 == '-' || c2 == '?' || c2 == ':') && + !NULL_BL_T_LINEBR(_buffer[_pointer + 1]))) { + return FetchPlain(); + } + + throw new ScannerException("while scanning for the next token: found character " + ch + " (" + (int)ch + ") that cannot start any token"); + } + + private Token FetchStreamStart() { + _docStart = true; + return AddToken(StreamStartToken.Instance); + } + + private Token FetchStreamEnd() { + UnwindIndent(-1); + _allowSimpleKey = false; + _possibleSimpleKeys = new Dictionary(); + _done = true; + _docStart = false; + return AddToken(StreamEndToken.Instance); + } + + private void ScanToNextToken() { + while (true) { + while (Peek() == ' ') { + Forward(); + } + if (Peek() == '#') { + Forward(); + while (!NULL_OR_LINEBR(Peek())) { + Forward(); + } + } + if (ScanLineBreak().Length != 0) { + if (_flowLevel == 0) { + _allowSimpleKey = true; + } + } else { + break; + } + } + } + + private string ScanLineBreak() { + // Transforms: + // '\r\n' : '\n' + // '\r' : '\n' + // '\n' : '\n' + // '\x85' : '\n' + // default : '' + char val = Peek(); + if (FULL_LINEBR(val)) { + Ensure(2, false); + if (_buffer[_pointer] == '\r' && _buffer[_pointer + 1] == '\n') { + Forward(2); + } else { + Forward(); + } + return "\n"; + } else { + return ""; + } + } + + private void UnwindIndent(int col) { + if (_flowLevel != 0) { + return; + } + + while (_indent > col) { + _indent = _indents.Pop(); + _tokens.AddLast(BlockEndToken.Instance); + } + } + + private Token FetchDocumentStart() { + _docStart = false; + return FetchDocumentIndicator(DocumentStartToken.Instance); + } + + private Token FetchDocumentIndicator(Token tok) { + UnwindIndent(-1); + RemovePossibleSimpleKey(); + _allowSimpleKey = false; + Forward(3); + return AddToken(tok); + } + + private Token FetchBlockEntry() { + _docStart = false; + if (_flowLevel == 0) { + if (!_allowSimpleKey) { + throw new ScannerException("sequence entries are not allowed here"); + } + if (AddIndent(_column)) { + _tokens.AddLast(BlockSequenceStartToken.Instance); + } + } + _allowSimpleKey = true; + RemovePossibleSimpleKey(); + Forward(); + return AddToken(BlockEntryToken.Instance); + } + + private bool AddIndent(int col) { + if (_indent < col) { + _indents.Push(_indent); + _indent = col; + return true; + } + return false; + } + + private Token FetchTag() { + _docStart = false; + SavePossibleSimpleKey(); + _allowSimpleKey = false; + return AddToken(ScanTag()); + } + + private void RemovePossibleSimpleKey() { + SimpleKey key; + if (_possibleSimpleKeys.TryGetValue(_flowLevel, out key)) { + _possibleSimpleKeys.Remove(_flowLevel); + if (key.Required) { + throw new ScannerException("while scanning a simple key: could not find expected ':'"); + } + } + } + + private void SavePossibleSimpleKey() { + if (_allowSimpleKey) { + RemovePossibleSimpleKey(); + _possibleSimpleKeys.Add(_flowLevel, new SimpleKey(_tokensTaken + _tokens.Count, (_flowLevel == 0) && _indent == _column, -1, -1, _column)); + } + } + + private Token ScanTag() { + char ch = Peek(1); + string handle = null; + string suffix = null; + if (ch == '<') { + Forward(2); + suffix = ScanTagUri("tag"); + if (Peek() != '>') { + throw new ScannerException("while scanning a tag: expected '>', but found " + Peek() + "(" + (int)Peek() + ")"); + } + Forward(); + } else if (NULL_BL_T_LINEBR(ch)) { + suffix = "!"; + Forward(); + } else { + int length = 1; + bool useHandle = false; + while (!NULL_BL_T_LINEBR(ch)) { + if (ch == '!') { + useHandle = true; + break; + } + length++; + ch = Peek(length); + } + handle = "!"; + if (useHandle) { + handle = ScanTagHandle("tag"); + } else { + handle = "!"; + Forward(); + } + suffix = ScanTagUri("tag"); + } + if (!NULL_BL_LINEBR(Peek())) { + throw new ScannerException("while scanning a tag: expected ' ', but found " + Peek() + "(" + (int)Peek() + ")"); + } + return new TagToken(handle, suffix); + } + + private string ScanTagUri(string name) { + StringBuilder chunks = new StringBuilder(10); + + int length = 0; + char ch = Peek(length); + while (STRANGE_CHAR(ch)) { + if ('%' == ch) { + Ensure(length, false); + chunks.Append(_buffer, _pointer, length); + length = 0; + ScanUriEscapes(chunks, name); + } else { + length++; + } + ch = Peek(length); + } + if (length != 0) { + Ensure(length, false); + chunks.Append(_buffer, _pointer, length); + Forward(length); + } + if (chunks.Length == 0) { + throw new ScannerException("while scanning a " + name + ": expected URI, but found " + ch + "(" + (int)ch + ")"); + } + return chunks.ToString(); + } + + private string ScanTagHandle(string name) { + char ch = Peek(); + if (ch != '!') { + throw new ScannerException("while scanning a " + name + ": expected '!', but found " + ch + "(" + (int)ch + ")"); + } + int length = 1; + ch = Peek(length); + if (ch != ' ') { + while (ALPHA(ch)) { + length++; + ch = Peek(length); + } + if ('!' != ch) { + Forward(length); + throw new ScannerException("while scanning a " + name + ": expected '!', but found " + ch + "(" + ((int)ch) + ")"); + } + length++; + } + Ensure(length, false); + string value = new string(_buffer, _pointer, length); + Forward(length); + return value; + } + + private void ScanUriEscapes(StringBuilder str, string name) { + while (Peek() == '%') { + Forward(); + try { + Ensure(2, false); + str.Append(int.Parse(new string(_buffer, _pointer, 2), NumberStyles.HexNumber)); + } catch (FormatException fe) { + throw new ScannerException("while scanning a " + name + ": expected URI escape sequence of 2 hexadecimal numbers, but found " + Peek(1) + "(" + ((int)Peek(1)) + ") and " + Peek(2) + "(" + ((int)Peek(2)) + ")", fe); + } + Forward(2); + } + } + + private Token FetchPlain() { + _docStart = false; + SavePossibleSimpleKey(); + _allowSimpleKey = false; + return AddToken(ScanPlain()); + } + + private delegate bool CharTest(char c); + + private Token ScanPlain() { + StringBuilder chunks = new StringBuilder(7); + string spaces = ""; + int ind = _indent + 1; + bool f_nzero; + CharTest r_check, r_check2, r_check3; + if (_flowLevel != 0) { + f_nzero = true; + r_check = R_FLOWNONZERO; + r_check2 = ALL_FALSE; + r_check3 = ALL_FALSE; + } else { + f_nzero = false; + r_check = NULL_BL_T_LINEBR; + r_check2 = R_FLOWZERO1; + r_check3 = NULL_BL_T_LINEBR; + } + while (Peek() != '#') { + int length = 0; + for (int i = 0; ; i++) { + Ensure(i + 2, false); + if (r_check(_buffer[_pointer + i]) || (r_check2(_buffer[_pointer + i]) && r_check3(_buffer[_pointer + i + 1]))) { + length = i; + char ch = Peek(length); + if (!(f_nzero && ch == ':' && !S4(Peek(length + 1)))) { + break; + } + } + } + + if (length == 0) { + break; + } + _allowSimpleKey = false; + chunks.Append(spaces); + Ensure(length, false); + chunks.Append(_buffer, _pointer, length); + Forward(length); + spaces = ScanPlainSpaces(ind); + if (spaces == null || (_flowLevel == 0 && _column < ind)) { + break; + } + } + return new ScalarToken(chunks.ToString(), true); + } + + private int NextPossibleSimpleKey() { + foreach (SimpleKey key in _possibleSimpleKeys.Values) { + if (key.TokenNumber > 0) { + return key.TokenNumber; + } + } + return -1; + } + + private string ScanPlainSpaces(int indent) { + StringBuilder chunks = new StringBuilder(); + int length = 0; + while (Peek(length) == ' ') { + length++; + } + char[] whitespaces = new char[length]; + for (int i = 0; i < length; i++) { + whitespaces[i] = ' '; + } + Forward(length); + char ch = Peek(); + if (FULL_LINEBR(ch)) { + string lineBreak = ScanLineBreak(); + _allowSimpleKey = true; + if (IsEndOrStart()) { + return ""; + } + StringBuilder breaks = new StringBuilder(); + while (BLANK_OR_LINEBR(Peek())) { + if (' ' == Peek()) { + Forward(); + } else { + breaks.Append(ScanLineBreak()); + if (IsEndOrStart()) { + return ""; + } + } + } + if (!(lineBreak.Length == 1 && lineBreak[0] == '\n')) { + chunks.Append(lineBreak); + } else if (breaks.Length == 0) { + chunks.Append(" "); + } + chunks.Append(breaks); + } else { + chunks.Append(whitespaces); + } + return chunks.ToString(); + } + + private Token FetchSingle() { + return FetchFlowScalar('\''); + } + + private Token FetchDouble() { + return FetchFlowScalar('"'); + } + + private Token FetchFlowScalar(char style) { + _docStart = false; + SavePossibleSimpleKey(); + _allowSimpleKey = false; + return AddToken(ScanFlowScalar(style)); + } + + private Token ScanFlowScalar(char style) { + bool dbl = style == '"'; + StringBuilder chunks = new StringBuilder(); + + char quote = Peek(); + Forward(); + ScanFlowScalarNonSpaces(chunks, dbl); + while (Peek() != quote) { + ScanFlowScalarSpaces(chunks); + ScanFlowScalarNonSpaces(chunks, dbl); + } + Forward(); + return new ScalarToken(chunks.ToString(), false, style); + } + + private char ParseHexChar(int length) { + Ensure(length, false); + + // TODO: how do we parse 32-bit escape sequences? + string str = new string(_buffer, _pointer, length); + char c; + try { + c = (char)uint.Parse(str, NumberStyles.HexNumber); + } catch (Exception e) { + throw new ScannerException("while scanning a double-quoted scalar: expected escape sequence of " + length + " hexadecimal numbers, but found something else: " + str, e); + } + + Forward(length); + return c; + } + + private void ScanFlowScalarNonSpaces(StringBuilder chunks, bool dbl) { + for (; ; ) { + int length = 0; + while (!SPACES_AND_STUFF(Peek(length))) { + length++; + } + if (length != 0) { + Ensure(length, false); + chunks.Append(_buffer, _pointer, length); + Forward(length); + } + char ch = Peek(); + if (!dbl && ch == '\'' && Peek(1) == '\'') { + chunks.Append('\''); + Forward(2); + } else if ((dbl && ch == '\'') || (!dbl && DOUBLE_ESC(ch))) { + chunks.Append(ch); + Forward(); + } else if (dbl && ch == '\\') { + Forward(); + ch = Peek(); + int result; + if ((result = ESCAPE_REPLACEMENT(ch)) >= 0) { + chunks.Append((char)result); + Forward(); + } else if ((result = ESCAPE_CODES(ch)) >= 0) { + length = result; + Forward(); + chunks.Append(ParseHexChar(length)); + } else if (FULL_LINEBR(ch)) { + ScanLineBreak(); + chunks.Append(ScanFlowScalarBreaks()); + } else { + chunks.Append('\\'); + } + } else { + return; + } + } + } + + private void ScanFlowScalarSpaces(StringBuilder chunks) { + int length = 0; + while (BLANK_T(Peek(length))) { + length++; + } + Ensure(length, false); + string whitespaces = new string(_buffer, _pointer, length); + Forward(length); + char ch = Peek(); + if (ch == '\0') { + throw new ScannerException("while scanning a quoted scalar: found unexpected end of stream"); + } else if (FULL_LINEBR(ch)) { + string lineBreak = ScanLineBreak(); + string breaks = ScanFlowScalarBreaks(); + if (!(lineBreak.Length == 1 && lineBreak[0] == '\n')) { + chunks.Append(lineBreak); + } else if (breaks.Length == 0) { + chunks.Append(" "); + } + chunks.Append(breaks); + } else { + chunks.Append(whitespaces); + } + } + + private string ScanFlowScalarBreaks() { + StringBuilder chunks = new StringBuilder(); + bool colz = true; + for (; ; ) { + if (colz && IsEndOrStart()) { + throw new ScannerException("while scanning a quoted scalar: found unexpected document separator"); + } + while (BLANK_T(Peek())) { + Forward(); + } + if (FULL_LINEBR(Peek())) { + chunks.Append(ScanLineBreak()); + colz = true; + } else if ('\\' == Peek() && BLANK_T(Peek(1))) { + Forward(); + ScanFlowScalarSpaces(chunks); + colz = false; + } else { + return chunks.ToString(); + } + } + } + + private Token FetchValue() { + _docStart = false; + SimpleKey key; + if (!_possibleSimpleKeys.TryGetValue(_flowLevel, out key)) { + if (_flowLevel == 0 && !_allowSimpleKey) { + throw new ScannerException("mapping values are not allowed here"); + } + _allowSimpleKey = _flowLevel == 0; + RemovePossibleSimpleKey(); + } else { + _possibleSimpleKeys.Remove(_flowLevel); + + // find the insertion point + LinkedListNode node = _tokens.First; + int skip = key.TokenNumber - _tokensTaken; + while (skip > 0) { + node = node.Next; + skip--; + } + + node = _tokens.AddBefore(node, KeyToken.Instance); + if (_flowLevel == 0 && AddIndent(key.Column)) { + _tokens.AddBefore(node, BlockMappingStartToken.Instance); + } + _allowSimpleKey = false; + } + Forward(); + return AddToken(ValueToken.Instance); + } + + private Token FetchFlowSequenceStart() { + return FetchFlowCollectionStart(FlowSequenceStartToken.Instance); + } + + private Token FetchFlowMappingStart() { + return FetchFlowCollectionStart(FlowMappingStartToken.Instance); + } + + private Token FetchFlowCollectionStart(Token tok) { + _docStart = false; + SavePossibleSimpleKey(); + _flowLevel++; + _allowSimpleKey = true; + Forward(1); + return AddToken(tok); + } + + + private Token FetchDocumentEnd() { + return FetchDocumentIndicator(DocumentEndToken.Instance); + } + + private Token FetchFlowSequenceEnd() { + return FetchFlowCollectionEnd(FlowSequenceEndToken.Instance); + } + + private Token FetchFlowMappingEnd() { + return FetchFlowCollectionEnd(FlowMappingEndToken.Instance); + } + + private Token FetchFlowCollectionEnd(Token tok) { + RemovePossibleSimpleKey(); + _flowLevel--; + _allowSimpleKey = false; + Forward(1); + return AddToken(tok); + } + + private Token fetchFlowEntry() { + _allowSimpleKey = true; + RemovePossibleSimpleKey(); + Forward(1); + return AddToken(FlowEntryToken.Instance); + } + + private Token FetchLiteral() { + return FetchBlockScalar('|'); + } + + private Token FetchFolded() { + return FetchBlockScalar('>'); + } + + private Token FetchBlockScalar(char style) { + _docStart = false; + _allowSimpleKey = true; + RemovePossibleSimpleKey(); + return AddToken(ScanBlockScalar(style)); + } + + private Token ScanBlockScalar(char style) { + bool folded = style == '>'; + StringBuilder chunks = new StringBuilder(); + Forward(); + + bool? chomping; + int increment; + ScanBlockScalarIndicators(out chomping, out increment); + + bool sameLine = ScanBlockScalarIgnoredLine(); + + int minIndent = _indent + 1; + if (minIndent < 0) { + minIndent = 0; + } + + int maxIndent = 0; + int ind = 0; + if (sameLine) { + bool leadingNonSpace = !BLANK_T(Peek()); + int length = 0; + while (!NULL_OR_LINEBR(Peek(length))) { + length++; + } + Ensure(length, false); + chunks.Append(_buffer, _pointer, length); + Forward(length); + } + + string breaks; + if (increment == -1) { + ScanBlockScalarIndentation(out breaks, out maxIndent); + if (minIndent > maxIndent) { + ind = minIndent; + } else { + ind = maxIndent; + } + } else { + ind = minIndent + increment - 1; + breaks = ScanBlockScalarBreaks(ind); + } + + string lineBreak = ""; + while (_column == ind && Peek() != '\0') { + chunks.Append(breaks); + bool leadingNonSpace = !BLANK_T(Peek()); + int length = 0; + while (!NULL_OR_LINEBR(Peek(length))) { + length++; + } + Ensure(length, false); + chunks.Append(_buffer, _pointer, length); + Forward(length); + lineBreak = ScanLineBreak(); + breaks = ScanBlockScalarBreaks(ind); + if (_column == ind && Peek() != '\0') { + if (folded && lineBreak.Length == 1 && lineBreak[0] == '\n' && leadingNonSpace && !BLANK_T(Peek())) { + if (breaks.Length == 0) { + chunks.Append(" "); + } + } else { + chunks.Append(lineBreak); + } + } else { + break; + } + } + + if (chomping.GetValueOrDefault(true)) { + chunks.Append(lineBreak); + } + if (chomping.GetValueOrDefault(false)) { + chunks.Append(breaks); + } + + return new ScalarToken(chunks.ToString(), false, style); + } + + private string ScanBlockScalarBreaks(int indent) { + StringBuilder chunks = new StringBuilder(); + while (_column < indent && Peek() == ' ') { + Forward(); + } + while (FULL_LINEBR(Peek())) { + chunks.Append(ScanLineBreak()); + while (_column < indent && Peek() == ' ') { + Forward(); + } + } + return chunks.ToString(); + } + + private void ScanBlockScalarIndentation(out string breaks, out int maxIndent) { + StringBuilder chunks = new StringBuilder(); + maxIndent = 0; + while (BLANK_OR_LINEBR(Peek())) { + if (Peek() != ' ') { + chunks.Append(ScanLineBreak()); + } else { + Forward(); + if (_column > maxIndent) { + maxIndent = _column; + } + } + } + breaks = chunks.ToString(); + } + + private void ScanBlockScalarIndicators(out bool? chomping, out int increment) { + chomping = null; + increment = -1; + + char ch = Peek(); + if (ch == '-' || ch == '+') { + chomping = ch == '+' ? true : false; + Forward(); + ch = Peek(); + if (char.IsDigit(ch)) { + increment = ch - '0'; + if (increment == 0) { + throw new ScannerException("while scanning a block scalar: expected indentation indicator in_ the range 1-9, but found 0"); + } + Forward(); + } + } else if (char.IsDigit(ch)) { + increment = ch - '0'; + if (increment == 0) { + throw new ScannerException("while scanning a block scalar: expected indentation indicator in_ the range 1-9, but found 0"); + } + Forward(); + ch = Peek(); + if (ch == '-' || ch == '+') { + chomping = ch == '+' ? true : false; + Forward(); + } + } + if (!NULL_BL_LINEBR(Peek())) { + throw new ScannerException("while scanning a block scalar: expected chomping or indentation indicators, but found " + Peek() + "(" + ((int)Peek()) + ")"); + } + } + + private bool ScanBlockScalarIgnoredLine() { + bool same = true; + while (Peek() == ' ') { + Forward(); + } + if (Peek() == '#') { + while (!NULL_OR_LINEBR(Peek())) { + Forward(); + } + same = false; + } + if (NULL_OR_LINEBR(Peek())) { + ScanLineBreak(); + return false; + } + return same; + } + + + private Token FetchDirective() { + UnwindIndent(-1); + RemovePossibleSimpleKey(); + _allowSimpleKey = false; + return AddToken(ScanDirective()); + } + + private Token FetchKey() { + if (_flowLevel == 0) { + if (!_allowSimpleKey) { + throw new ScannerException("mapping keys are not allowed here"); + } + if (AddIndent(_column)) { + _tokens.AddLast(BlockMappingStartToken.Instance); + } + } + _allowSimpleKey = _flowLevel == 0; + RemovePossibleSimpleKey(); + Forward(); + return AddToken(KeyToken.Instance); + } + + private Token FetchAlias() { + SavePossibleSimpleKey(); + _allowSimpleKey = false; + return AddToken(new AliasToken(ScanAnchor())); + } + + private Token FetchAnchor() { + SavePossibleSimpleKey(); + _allowSimpleKey = false; + return AddToken(new AnchorToken(ScanAnchor())); + } + + private Token ScanDirective() { + Forward(); + string name = ScanDirectiveName(); + string[] value = null; + if (name == "Yaml") { + value = ScanYamlDirectiveValue(); + } else if (name == "TAG") { + value = ScanTagDirectiveValue(); + } else { + while (!NULL_OR_LINEBR(Peek())) { + Forward(); + } + } + ScanDirectiveIgnoredLine(); + return new DirectiveToken(name, value); + } + + private string ScanDirectiveName() { + int length = 0; + char ch = Peek(length); + bool zlen = true; + while (ALPHA(ch)) { + zlen = false; + length++; + ch = Peek(length); + } + if (zlen) { + throw new ScannerException("while scanning a directive: expected alphabetic or numeric character, but found " + ch + "(" + ((int)ch) + ")"); + } + string value = null; + try { + Ensure(length, false); + value = new string(_buffer, _pointer, length); + } catch (Exception) { + } + Forward(length); + if (!NULL_BL_LINEBR(Peek())) { + throw new ScannerException("while scanning a directive: expected alphabetic or numeric character, but found " + ch + "(" + ((int)ch) + ")"); + } + return value; + } + + private string ScanDirectiveIgnoredLine() { + while (Peek() == ' ') { + Forward(); + } + if (Peek() == '"') { + while (!NULL_OR_LINEBR(Peek())) { + Forward(); + } + } + char ch = Peek(); + if (!NULL_OR_LINEBR(ch)) { + throw new ScannerException("while scanning a directive: expected a comment or a line break, but found " + Peek() + "(" + ((int)Peek()) + ")"); + } + return ScanLineBreak(); + } + + private string ScanAnchor() { + char indicator = Peek(); + string name = indicator == '*' ? "alias" : "anchor"; + Forward(); + int length = 0; + while (ALPHA(Peek(length))) { + length++; + } + if (length == 0) { + throw new ScannerException("while scanning an " + name + ": expected alphabetic or numeric character, but found something else..."); + } + string value = null; + try { + Ensure(length, false); + value = new string(_buffer, _pointer, length); + } catch (Exception) { + } + Forward(length); + if (!NON_ALPHA_OR_NUM(Peek())) { + throw new ScannerException("while scanning an " + name + ": expected alphabetic or numeric character, but found " + Peek() + "(" + ((int)Peek()) + ")"); + } + return value; + } + + private string[] ScanYamlDirectiveValue() { + while (Peek() == ' ') { + Forward(); + } + string major = ScanYamlDirectiveNumber(); + if (Peek() != '.') { + throw new ScannerException("while scanning a directive: expected a digit or '.', but found " + Peek() + "(" + ((int)Peek()) + ")"); + } + Forward(); + string minor = ScanYamlDirectiveNumber(); + if (!NULL_BL_LINEBR(Peek())) { + throw new ScannerException("while scanning a directive: expected a digit or ' ', but found " + Peek() + "(" + ((int)Peek()) + ")"); + } + return new string[] { major, minor }; + } + + private string ScanYamlDirectiveNumber() { + char ch = Peek(); + if (!char.IsDigit(ch)) { + throw new ScannerException("while scanning a directive: expected a digit, but found " + ch + "(" + ((int)ch) + ")"); + } + int length = 0; + StringBuilder sb = new StringBuilder(); + while (char.IsDigit(Peek(length))) { + sb.Append(Peek(length)); + length++; + } + Forward(length); + return sb.ToString(); + } + + private string[] ScanTagDirectiveValue() { + while (Peek() == ' ') { + Forward(); + } + string handle = ScanTagDirectiveHandle(); + while (Peek() == ' ') { + Forward(); + } + string prefix = ScanTagDirectivePrefix(); + return new string[] { handle, prefix }; + } + + private string ScanTagDirectiveHandle() { + string value = ScanTagHandle("directive"); + if (Peek() != ' ') { + throw new ScannerException("while scanning a directive: expected ' ', but found " + Peek() + "(" + ((int)Peek()) + ")"); + } + return value; + } + + private string ScanTagDirectivePrefix() { + string value = ScanTagUri("directive"); + if (!NULL_BL_LINEBR(Peek())) { + throw new ScannerException("while scanning a directive: expected ' ', but found " + Peek() + "(" + ((int)Peek()) + ")"); + } + return value; + } + + #region character classes + + private bool NULL_BL_T_LINEBR(char c) { + switch (c) { + case '\0': + case ' ': + case '\t': + case '\r': + case '\n': + return true; + } + return false; + } + + private bool NULL_BL_LINEBR(char c) { + switch (c) { + case '\0': + case ' ': + case '\r': + case '\n': + return true; + } + return false; + } + + private bool FULL_LINEBR(char c) { + return c == '\n' || c == '\r'; + } + + private bool NULL_OR_LINEBR(char c) { + return c == '\n' || c == '\r' || c == '\0'; + } + + private bool BLANK_OR_LINEBR(char c) { + return c == ' ' || c == '\n' || c == '\r'; + } + + private bool BLANK_T(char c) { + return c == ' ' || c == '\t'; + } + + private bool DOUBLE_ESC(char c) { + return c == '\\' || c == '"'; + } + + private bool SPACES_AND_STUFF(char c) { + switch (c) { + case '\0': + case ' ': + case '\t': + case '\r': + case '\n': + case '\\': + case '\'': + case '"': + return true; + } + return false; + } + + private bool NON_ALPHA_OR_NUM(char c) { + switch (c) { + case '\0': + case ' ': + case '\t': + case '\r': + case '\n': + case '?': + case ':': + case ',': + case ']': + case '}': + case '%': + case '@': + case '`': + return true; + } + return false; + } + + private bool NOT_USEFUL_CHAR(char c) { + switch (c) { + case '\0': + case ' ': + case '\t': + case '\r': + case '\n': + case '-': + case '?': + case ':': + case '[': + case ']': + case '{': + case '#': + case '&': + case '*': + case '!': + case '|': + case '\'': + case '"': + case '@': + return false; + } + return true; + } + + private bool S4(char c) { + switch (c) { + case '\0': + case ' ': + case '\t': + case '\r': + case '\n': + case '[': + case ']': + case '{': + case '}': + return true; + } + return false; + } + + private bool ALL_FALSE(char c) { + return false; + } + + private bool R_FLOWZERO1(char c) { + return c == ':'; + } + + private bool R_FLOWNONZERO(char c) { + switch (c) { + case '\0': + case ' ': + case '\t': + case '\r': + case '\n': + case '[': + case ']': + case '{': + case '}': + case ',': + case ':': + case '?': + return true; + } + return false; + } + + private bool ALPHA(char c) { + return char.IsLetterOrDigit(c) || c == '-' || c == '_'; + } + + private bool STRANGE_CHAR(char c) { + switch (c) { + case '-': + case '_': + case '[': + case ']': + case '(': + case ')': + case '\'': + case ';': + case '/': + case '?': + case ':': + case '@': + case '&': + case '=': + case '+': + case '$': + case ',': + case '.': + case '!': + case '~': + case '*': + case '%': + case '^': + case '#': + return true; + } + return char.IsLetterOrDigit(c); + } + + private int ESCAPE_CODES(char c) { + switch (c) { + case 'x': return 2; + case 'u': return 4; + case 'U': return 8; + } + return -1; + } + + private int ESCAPE_REPLACEMENT(char c) { + switch (c) { + case '0': return 0; + case 'a': return 7; + case 'b': return 8; + case 't': return 9; + case '\t': return 9; + case 'n': return 10; + case 'v': return 11; + case 'f': return 12; + case 'r': return 13; + case 'e': return 27; + case '"': return '"'; + case '\\': return '\\'; + case 'N': return 133; + case '_': return 160; + } + return -1; + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + while (PeekToken() != null) { + yield return GetToken(); + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + #endregion + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Serializer.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Serializer.cs new file mode 100644 index 0000000000..99a4309a31 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Serializer.cs @@ -0,0 +1,172 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections.Generic; + +namespace IronRuby.StandardLibrary.Yaml { + + public interface ISerializer { + void Serialize(Node node); + } + + public class Serializer : ISerializer, IDisposable { + private readonly IEmitter _emitter; + private readonly bool _useExplicitStart; + private readonly bool _useExplicitEnd; + private readonly bool _explicitTypes; + private readonly Version _useVersion; + private readonly bool _useTags; + private readonly string _anchorTemplate; + + private readonly Dictionary _serializedNodes = new Dictionary(ReferenceEqualityComparer.Instance); + private readonly Dictionary _anchors = new Dictionary(ReferenceEqualityComparer.Instance); + private int _lastAnchorId; + private bool _closed; + + public Serializer(IEmitter emitter, YamlOptions opts) { + _emitter = emitter; + _useExplicitStart = opts.ExplicitStart; + _useExplicitEnd = opts.ExplicitEnd; + if (opts.UseVersion) { + _useVersion = opts.Version; + } + _explicitTypes = opts.ExplicitTypes; + _useTags = opts.UseHeader; + _anchorTemplate = opts.AnchorFormat ?? "id{0:000}"; + _emitter.Emit(StreamStartEvent.Instance); + } + + protected virtual bool IgnoreAnchor(Node node) { + // This is possibly Ruby specific. + // but it didn't seem worth subclassing Serializer for this one method + return !(node is CollectionNode); + //return false; + } + + public void Dispose() { + if (!_closed) { + _emitter.Emit(StreamEndEvent.Instance); + _closed = true; + } + } + + public void Serialize(Node node) { + if (_closed) { + throw new SerializerException("serializer is closed"); + } + _emitter.Emit(new DocumentStartEvent(_useExplicitStart, _useVersion, null)); + AnchorNode(node); + SerializeNode(node, null, null); + _emitter.Emit(_useExplicitEnd ? DocumentEndEvent.ExplicitInstance : DocumentEndEvent.ImplicitInstance); + + _serializedNodes.Clear(); + _anchors.Clear(); + _lastAnchorId = 0; + } + + private void AnchorNode(Node node) { + while (node is LinkNode) { + node = ((LinkNode)node).Linked; + } + if (!IgnoreAnchor(node)) { + string anchor; + if (_anchors.TryGetValue(node, out anchor)) { + if (null == anchor) { + _anchors[node] = GenerateAnchor(node); + } + } else { + _anchors.Add(node, null); + + SequenceNode seq; + MappingNode map; + if ((seq = node as SequenceNode) != null) { + foreach (Node n in seq.Nodes) { + AnchorNode(n); + } + } else if ((map = node as MappingNode) != null) { + foreach (KeyValuePair e in map.Nodes) { + AnchorNode(e.Key); + AnchorNode(e.Value); + } + } + } + } + } + + private string GenerateAnchor(Node node) { + return string.Format(_anchorTemplate, ++_lastAnchorId); + } + + private void SerializeNode(Node node, Node parent, object index) { + while (node is LinkNode) { + node = ((LinkNode)node).Linked; + } + + string tAlias; + _anchors.TryGetValue(node, out tAlias); + + if (_serializedNodes.ContainsKey(node) && tAlias != null) { + _emitter.Emit(new AliasEvent(tAlias)); + } else { + + _serializedNodes[node] = null; + //_resolver.descendResolver(parent, index); + + ScalarNode scalar; + SequenceNode seq; + MappingNode map; + + if ((scalar = node as ScalarNode) != null) { + string detectedTag = Resolver.Resolve(typeof(ScalarNode), scalar.Value, new bool[] { true, false }); + string defaultTag = Resolver.Resolve(typeof(ScalarNode), scalar.Value, new bool[] { false, true }); + bool[] @implicit = new bool[] { false, false }; + if (!_explicitTypes) { + @implicit[0] = node.Tag == detectedTag || node.Tag.StartsWith(detectedTag); + @implicit[1] = node.Tag == defaultTag; + } + _emitter.Emit(new ScalarEvent(tAlias, node.Tag, @implicit, scalar.Value, scalar.Style)); + + } else if ((seq = node as SequenceNode) != null) { + + bool @implicit = !_explicitTypes && + node.Tag == Resolver.Resolve(typeof(SequenceNode), null, new bool[] { true, true }); + + _emitter.Emit(new SequenceStartEvent(tAlias, node.Tag, @implicit, seq.FlowStyle)); + int ix = 0; + foreach (Node n in seq.Nodes) { + SerializeNode(n, node, ix++); + } + _emitter.Emit(SequenceEndEvent.Instance); + + } else if ((map = node as MappingNode) != null) { + + bool @implicit = !_explicitTypes && + node.Tag == Resolver.Resolve(typeof(MappingNode), null, new bool[] { true, true }); + + _emitter.Emit(new MappingStartEvent(tAlias, node.Tag, @implicit, map.FlowStyle)); + foreach (KeyValuePair e in map.Nodes) { + SerializeNode(e.Key, node, null); + SerializeNode(e.Value, node, e.Key); + } + _emitter.Emit(MappingEndEvent.Instance); + } + } + } + } +} \ No newline at end of file diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Tokens.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Tokens.cs new file mode 100644 index 0000000000..470c866162 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/Tokens.cs @@ -0,0 +1,190 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; + +namespace IronRuby.StandardLibrary.Yaml { + + public abstract class Token { + internal Token() { } + + public override string ToString() { + return "#<" + GetType().Name + ">"; + } + } + + public sealed class AliasToken : Token { + private readonly string _value; + + public AliasToken(string value) { + _value = value; + } + + public string Value { get { return _value; } } + + public override string ToString() { + return string.Format("#", Value); + } + } + + public sealed class AnchorToken : Token { + private readonly string _value; + + public AnchorToken(string value) { + _value = value; + } + + public string Value { get { return _value; } } + + public override string ToString() { + return string.Format("#", Value); + } + } + + public sealed class TagToken : Token { + private readonly string _handle, _suffix; + + public TagToken(string handle, string suffix) { + _handle = handle; + _suffix = suffix; + } + + public string Handle { get { return _handle; } } + public string Suffix { get { return _suffix; } } + + public override string ToString() { + return string.Format("#", Handle, Suffix); + } + } + + public sealed class ScalarToken : Token { + private readonly string _value; + private readonly bool _plain; + private readonly char _style; + + public ScalarToken(string value, bool plain) { + _value = value; + _plain = plain; + } + + public ScalarToken(string value, bool plain, char style) { + _value = value; + _plain = plain; + _style = style; + } + + public string Value { get { return _value; } } + public bool Plain { get { return _plain; } } + public char Style { get { return _style; } } + + public override string ToString() { + string value = Value.Replace("\r", "\\r").Replace("\n", "\\n"); + if (value.Length > 35) { + value = value.Substring(0, 32) + "..."; + } + return string.Format("#", Plain, Style, value); + } + + } + + public sealed class DirectiveToken : Token { + private readonly string _name; + private readonly string[] _value; + + public DirectiveToken(string name, string[] value) { + if (value != null && value.Length != 2) { + throw new ArgumentException("must be null or a 2 element array", "value"); + } + _name = name; + _value = value; + } + + public string Name { get { return _name; } } + public string[] Value { get { return _value; } } + + public override string ToString() { + if (Value != null) { + return string.Format("#", Name, Value[0], Value[1]); + } else { + return string.Format("#", Name); + } + } + } + + public sealed class BlockEndToken : Token { + public static readonly BlockEndToken Instance = new BlockEndToken(); + private BlockEndToken() { } + } + public sealed class BlockEntryToken : Token { + public static readonly BlockEntryToken Instance = new BlockEntryToken(); + private BlockEntryToken() { } + } + public sealed class BlockMappingStartToken : Token { + public static readonly BlockMappingStartToken Instance = new BlockMappingStartToken(); + private BlockMappingStartToken() { } + } + public sealed class BlockSequenceStartToken : Token { + public static readonly BlockSequenceStartToken Instance = new BlockSequenceStartToken(); + private BlockSequenceStartToken() { } + } + public sealed class DocumentEndToken : Token { + public static readonly DocumentEndToken Instance = new DocumentEndToken(); + private DocumentEndToken() { } + } + public sealed class DocumentStartToken : Token { + public static readonly DocumentStartToken Instance = new DocumentStartToken(); + private DocumentStartToken() { } + } + public sealed class FlowEntryToken : Token { + public static readonly FlowEntryToken Instance = new FlowEntryToken(); + private FlowEntryToken() { } + } + public sealed class FlowMappingEndToken : Token { + public static readonly FlowMappingEndToken Instance = new FlowMappingEndToken(); + private FlowMappingEndToken() { } + } + public sealed class FlowMappingStartToken : Token { + public static readonly FlowMappingStartToken Instance = new FlowMappingStartToken(); + private FlowMappingStartToken() { } + } + public sealed class FlowSequenceEndToken : Token { + public static readonly FlowSequenceEndToken Instance = new FlowSequenceEndToken(); + private FlowSequenceEndToken() { } + } + public sealed class FlowSequenceStartToken : Token { + public static readonly FlowSequenceStartToken Instance = new FlowSequenceStartToken(); + private FlowSequenceStartToken() { } + } + public sealed class KeyToken : Token { + public static readonly KeyToken Instance = new KeyToken(); + private KeyToken() { } + } + public sealed class ValueToken : Token { + public static readonly ValueToken Instance = new ValueToken(); + private ValueToken() { } + } + public sealed class StreamEndToken : Token { + public static readonly StreamEndToken Instance = new StreamEndToken(); + private StreamEndToken() { } + } + public sealed class StreamStartToken : Token { + public static readonly StreamStartToken Instance = new StreamStartToken(); + private StreamStartToken() { } + } + +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/YamlConfig.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/YamlConfig.cs new file mode 100644 index 0000000000..0d2ced4ca8 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/YamlConfig.cs @@ -0,0 +1,76 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; + +namespace IronRuby.StandardLibrary.Yaml { + + public class YamlOptions { + private int _indent; + private bool _useHeader; + private bool _useVersion; + private Version _version; + private bool _explicitStart; + private bool _explicitEnd; + private string _anchorFormat; + private bool _explicitTypes; + private bool _canonical; + private int _bestWidth; + private bool _useBlock; + private bool _useFlow; + private bool _usePlain; + private bool _useSingle; + private bool _useDouble; + + internal static readonly YamlOptions DefaultOptions = new YamlOptions(); + + public int Indent { get { return _indent; } set { _indent = value; } } + public bool UseHeader { get { return _useHeader; } set { _useHeader = value; } } + public bool UseVersion { get { return _useVersion; } set { _useVersion = value; } } + public Version Version { get { return _version; } set { _version = value; } } + public bool ExplicitStart { get { return _explicitStart; } set { _explicitStart = value; } } + public bool ExplicitEnd { get { return _explicitEnd; } set { _explicitEnd = value; } } + public string AnchorFormat { get { return _anchorFormat; } set { _anchorFormat = value; } } + public bool ExplicitTypes { get { return _explicitTypes; } set { _explicitTypes = value; } } + public bool Canonical { get { return _canonical; } set { _canonical = value; } } + public int BestWidth { get { return _bestWidth; } set { _bestWidth = value; } } + public bool UseBlock { get { return _useBlock; } set { _useBlock = value; } } + public bool UseFlow { get { return _useFlow; } set { _useFlow = value; } } + public bool UsePlain { get { return _usePlain; } set { _usePlain = value; } } + public bool UseSingle { get { return _useSingle; } set { _useSingle = value; } } + public bool UseDouble { get { return _useDouble; } set { _useDouble = value; } } + + public YamlOptions() { + Indent = 2; + UseHeader = false; + UseVersion = false; + Version = new Version(1, 0); + ExplicitStart = true; + ExplicitEnd = false; + AnchorFormat = "id{0:000}"; + ExplicitTypes = false; + Canonical = false; + BestWidth = 80; + UseBlock = false; + UseFlow = false; + UsePlain = false; + UseSingle = false; + UseDouble = false; + } + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/resolver_scanner.rl b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/resolver_scanner.rl new file mode 100644 index 0000000000..6fb1629391 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Engine/resolver_scanner.rl @@ -0,0 +1,91 @@ + +package org.jvyamlb; + +import org.jruby.util.ByteList; + +public class ResolverScanner { +%%{ + machine resolver_scanner; + + action bool_tag { tag = "tag:yaml.org,2002:bool"; } + action merge_tag { tag = "tag:yaml.org,2002:merge"; } + action null_tag { tag = "tag:yaml.org,2002:null"; } + action timestamp_ymd_tag { tag = "tag:yaml.org,2002:timestamp#ymd"; } + action timestamp_tag { tag = "tag:yaml.org,2002:timestamp"; } + action value_tag { tag = "tag:yaml.org,2002:value"; } + action float_tag { tag = "tag:yaml.org,2002:float"; } + action int_tag { tag = "tag:yaml.org,2002:int"; } + + Bool = ("yes" | "Yes" | "YES" | "no" | "No" | "NO" | + "true" | "True" | "TRUE" | "false" | "False" | "FALSE" | + "on" | "On" | "ON" | "off" | "Off" | "OFF") %/bool_tag; + + Merge = "<<" %/merge_tag; + Value = "=" %/value_tag; + Null = ("~" | "null" | "Null" | "null" | "NULL" | " ") %/null_tag; + + digitF = digit | ","; + digit2 = digit | "_" | ","; + sign = "-" | "+"; + timestampFract = "." digit*; + timestampZone = [ \t]* ("Z" | (sign digit{1,2} ( ":" digit{2} )?)); + TimestampYMD = digit{4} ("-" digit{2}){2} %/timestamp_ymd_tag; + Timestamp = digit{4} ("-" digit{1,2}){2} ([Tt] | [ \t]+) digit{1,2} ":" digit{2} ":" digit{2} timestampFract? timestampZone %/timestamp_tag; + + exp = [eE] sign digit+; + + Float = ((sign? ((digitF+ "." digit* exp?) + | ((digitF+)? "." digit+ exp?) + | (digit+ (":" [0-5]? digit)+ "." digit*) + | "." ("inf" | "Inf" | "INF"))) + | ("." ("nan" | "NaN" | "NAN"))) %/float_tag; + + binaryInt = "0b" [0-1_]+; + octalInt = "0" [0-7_]+; + decimalInt = "0" | + [1-9]digit2* (":" [0-5]? digit)*; + hexaInt = "0x" [0-9a-fA-F_,]+; + Int = sign? (binaryInt | octalInt | decimalInt | hexaInt) %/int_tag; + + Scalar = Bool | Null | Int | Float | Merge | Value | Timestamp | TimestampYMD; + main := Scalar; +}%% + +%% write data nofinal; + + public String recognize(ByteList list) { + String tag = null; + int cs; + int act; + int have = 0; + int nread = 0; + int p=list.begin; + int pe = p+list.realSize; + int tokstart = -1; + int tokend = -1; + + byte[] data = list.bytes; + if(pe == 0) { + data = new byte[]{(byte)'~'}; + pe = 1; + } + +%% write init; + +%% write exec; + +%% write eof; + return tag; + } + + public static void main(String[] args) { + ByteList b = new ByteList(78); + b.append(args[0].getBytes()); +/* + for(int i=0;i 2, :UseHeader => false, :UseVersion => false, :Version => '1.0', + :SortKeys => false, :AnchorFormat => 'id%03d', :ExplicitTypes => false, + :WidthType => 'absolute', :BestWidth => 80, + :UseBlock => false, :UseFold => false, :Encoding => :None + } + + class Error < StandardError; end + + # This is not really correct. Fix pending + def self.parse(obj) + Proxy.new(YAML::load(obj)) + end + + def self.add_domain_type(*args) + warn "YAML::add_domain_type isn't supported on JRuby" + end + + def self.parse_documents(*args) + warn "YAML::parse_documents isn't supported on JRuby" + end + + class Proxy + def initialize(v) + @value = v + end + + def transform + @value + end + end + + class YPath + def self.each_path(*args) + warn "YAML::YPath.each_path isn't supported on JRuby" + end + end + + class Emitter + def initialize + @out = YAML::JvYAML::Out.new self + end + + def reset(opts) + @opts = opts + self + end + + def emit(oid, &proc) + proc.call(@out) + end + + def has_key?(key) + end + end + + class Object + attr_accessor :class, :ivars + def initialize(cl, iv) + @class, @ivars = cl, iv + end + + def to_yaml( opts = {} ) + YAML::quick_emit( object_id, opts ) do |out| + out.map( "tag:ruby.yaml.org,2002:object:#{ @class }", to_yaml_style ) do |map| + @ivars.each do |k,v| + map.add( k, v ) + end + end + end + end + end + + def YAML.emitter; Emitter.new; end + + # + # Allocate an Emitter if needed + # + def YAML.quick_emit( oid, opts = {}, &e ) + out = + if opts.is_a? YAML::Emitter + opts + else + emitter.reset( opts ) + end + out.emit( oid, &e ) + end + + module JvYAML + class Out + attr_accessor :emitter + + def initialize(emitter) + @emitter = emitter + end + + def map(type_id, style = nil) + map = Map.new(type_id, {}, style) + yield map + map + end + + def seq(type_id, style = nil) + seq = Seq.new(type_id, [], style) + yield seq + seq + end + + def scalar(type_id, str, style = nil) + Scalar.new(type_id, str, style) + end + end + + class Node + attr_accessor :value + attr_accessor :style + attr_accessor :type_id + + def to_str + YAML.dump(self) + end + + def to_s + YAML.dump(self) + end + end + + class Scalar < Node + def initialize(type_id, val, style) + @kind = :scalar + self.type_id = type_id + self.value = val + self.style = style + end + + def to_yaml_node(repr) + repr.scalar(self.type_id,self.value,self.style) + end + end + + class Seq < Node + def initialize(type_id, val, style) + @kind = :seq + self.type_id = type_id + self.value = val + self.style = style + end + def add(v) + @value << v + end + + def to_yaml_node(repr) + repr.seq(self.type_id,self.value,self.style) + end + end + + class Map < Node + def initialize(type_id, val, style) + @kind = :map + self.type_id = type_id + self.value = val + self.style = style + end + def add(k, v) + @value[k] = v + end + + def to_yaml_node(repr) + repr.map(self.type_id,self.value,self.style) + end + end + end + + # + # YAML::Stream -- for emitting many documents + # + class Stream + include Enumerable + attr_accessor :documents, :options + def initialize(opts = {}) + @options = opts + @documents = [] + end + + def [](i) + @documents[ i ] + end + + def add(doc) + @documents << doc + end + + def edit(doc_num,doc) + @documents[ doc_num ] = doc + end + + def each(&block) + @documents.each(&block) + end + + def emit + YAML::dump_all(@documents) + end + end + + # + # Default private type + # + class PrivateType + def self.tag_subclasses?; false; end + attr_accessor :type_id, :value + verbose, $VERBOSE = $VERBOSE, nil + def initialize( type, val ) + @type_id = type; @value = val + end + def to_yaml_node(repr) + @value.to_yaml_node(repr) + end + ensure + $VERBOSE = verbose + end + +# From yaml/tag.rb + # A dictionary of taguris which map to + # Ruby classes. + @@tagged_classes = {} + + # + # Associates a taguri _tag_ with a Ruby class _cls_. The taguri is used to give types + # to classes when loading YAML. Taguris are of the form: + # + # tag:authorityName,date:specific + # + # The +authorityName+ is a domain name or email address. The +date+ is the date the type + # was issued in YYYY or YYYY-MM or YYYY-MM-DD format. The +specific+ is a name for + # the type being added. + # + # For example, built-in YAML types have 'yaml.org' as the +authorityName+ and '2002' as the + # +date+. The +specific+ is simply the name of the type: + # + # tag:yaml.org,2002:int + # tag:yaml.org,2002:float + # tag:yaml.org,2002:timestamp + # + # The domain must be owned by you on the +date+ declared. If you don't own any domains on the + # date you declare the type, you can simply use an e-mail address. + # + # tag:why@ruby-lang.org,2004:notes/personal + # + def YAML.tag_class( tag, cls ) + if @@tagged_classes.has_key? tag + warn "class #{ @@tagged_classes[tag] } held ownership of the #{ tag } tag" + end + @@tagged_classes[tag] = cls + end + + # Returns the complete dictionary of taguris, paired with classes. The key for + # the dictionary is the full taguri. The value for each key is the class constant + # associated to that taguri. + # + # YAML.tagged_classes["tag:yaml.org,2002:int"] => Integer + # + def YAML.tagged_classes + @@tagged_classes + end +end + +# From yaml/tag.rb +class Module + # :stopdoc: + + # Adds a taguri _tag_ to a class, used when dumping or loading the class + # in YAML. See YAML::tag_class for detailed information on typing and + # taguris. + def yaml_as( tag, sc = true ) + verbose, $VERBOSE = $VERBOSE, nil + class_eval <<-"end;", __FILE__, __LINE__+1 + attr_writer :taguri + def taguri + if respond_to? :to_yaml_type + YAML::tagurize( to_yaml_type[1..-1] ) + else + return @taguri if defined?(@taguri) and @taguri + tag = #{ tag.dump } + if self.class.yaml_tag_subclasses? and self.class != YAML::tagged_classes[tag] + tag = "\#{ tag }:\#{ self.class.yaml_tag_class_name }" + end + tag + end + end + def self.yaml_tag_subclasses?; #{ sc ? 'true' : 'false' }; end + end; + YAML::tag_class tag, self + ensure + $VERBOSE = verbose + end + # Transforms the subclass name into a name suitable for display + # in a subclassed tag. + def yaml_tag_class_name + self.name + end + # Transforms the subclass name found in the tag into a Ruby + # constant name. + def yaml_tag_read_class( name ) + name + end +end + + Hash::yaml_as "tag:ruby.yaml.org,2002:hash" + Hash::yaml_as "tag:yaml.org,2002:map" + + Array::yaml_as "tag:ruby.yaml.org,2002:array" + Array::yaml_as "tag:yaml.org,2002:seq" + + String::yaml_as "tag:ruby.yaml.org,2002:string" + String::yaml_as "tag:yaml.org,2002:binary" + String::yaml_as "tag:yaml.org,2002:str" + + Range::yaml_as "tag:ruby.yaml.org,2002:range" + + Regexp::yaml_as "tag:ruby.yaml.org,2002:regexp" + + Integer::yaml_as "tag:yaml.org,2002:int", false + + Time::yaml_as "tag:ruby.yaml.org,2002:time" + Time::yaml_as "tag:yaml.org,2002:timestamp" + + #TODO: Date::yaml_as "tag:yaml.org,2002:timestamp#ymd" + + Float::yaml_as "tag:yaml.org,2002:float" + + NilClass::yaml_as "tag:yaml.org,2002:null" + + YAML::tag_class "tag:yaml.org,2002:bool#yes", TrueClass + YAML::tag_class "tag:yaml.org,2002:bool#no", FalseClass + YAML::tag_class "tag:ruby.yaml.org,2002:object", Object + YAML::tag_class "tag:ruby.yaml.org,2002:exception", Exception + YAML::tag_class "tag:ruby.yaml.org,2002:struct", Struct + YAML::tag_class "tag:ruby.yaml.org,2002:symbol", Symbol + YAML::tag_class "tag:ruby.yaml.org,2002:sym", Symbol + +# From yaml/types.rb +module YAML + # + # Builtin collection: !omap + # + class Omap < ::Array + yaml_as "tag:yaml.org,2002:omap" + def self.[]( *vals ) + o = Omap.new + 0.step( vals.length - 1, 2 ) do |i| + o[vals[i]] = vals[i+1] + end + o + end + def []( k ) + self.assoc( k ).to_a[1] + end + def []=( k, *rest ) + val, set = rest.reverse + if ( tmp = self.assoc( k ) ) and not set + tmp[1] = val + else + self << [ k, val ] + end + val + end + def has_key?( k ) + self.assoc( k ) ? true : false + end + def is_complex_yaml? + true + end + def to_yaml_node(repr) + sequ = [] + self.each do |v| + sequ << Hash[ *v ] + end + + repr.seq(taguri,sequ,to_yaml_style) + end + end + + + # + # Builtin collection: !pairs + # + class Pairs < ::Array + yaml_as "tag:yaml.org,2002:pairs" + def self.[]( *vals ) + p = Pairs.new + 0.step( vals.length - 1, 2 ) { |i| + p[vals[i]] = vals[i+1] + } + p + end + def []( k ) + self.assoc( k ).to_a + end + def []=( k, val ) + self << [ k, val ] + val + end + def has_key?( k ) + self.assoc( k ) ? true : false + end + def is_complex_yaml? + true + end + def to_yaml_node(repr) + sequ = [] + self.each do |v| + sequ << Hash[ *v ] + end + repr.seq(taguri,sequ,to_yaml_style) + end + end + + # + # Builtin collection: !set + # + class Set < ::Hash + yaml_as "tag:yaml.org,2002:set" + end +end + diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/GenerateInitializers.cmd b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/GenerateInitializers.cmd new file mode 100644 index 0000000000..4ad9197acd --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/GenerateInitializers.cmd @@ -0,0 +1 @@ +"%MERLIN_ROOT%\Bin\Debug\ClassInitGenerator" "%MERLIN_ROOT%\Bin\Debug\IronRuby.Libraries.Yaml.dll" /libraries:IronRuby.StandardLibrary.Yaml /out:%~dp0\Initializer.Generated.cs diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Initializer.Generated.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Initializer.Generated.cs new file mode 100644 index 0000000000..7ad47b0c75 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Initializer.Generated.cs @@ -0,0 +1,403 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.Yaml.YamlLibraryInitializer))] + +namespace IronRuby.StandardLibrary.Yaml { + public sealed class YamlLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + + + ExtendModule(typeof(IronRuby.Builtins.FalseClass), new System.Action(LoadIronRuby__Builtins__FalseClass_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendClass(typeof(IronRuby.Builtins.Hash), new System.Action(LoadIronRuby__Builtins__Hash_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + ExtendModule(typeof(IronRuby.Builtins.Integer), new System.Action(LoadIronRuby__Builtins__Integer_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendModule(typeof(IronRuby.Builtins.MutableString), new System.Action(LoadIronRuby__Builtins__MutableString_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendModule(typeof(IronRuby.Builtins.Range), new System.Action(LoadIronRuby__Builtins__Range_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendModule(typeof(IronRuby.Builtins.RubyArray), new System.Action(LoadIronRuby__Builtins__RubyArray_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendClass(typeof(IronRuby.Builtins.RubyClass), new System.Action(LoadIronRuby__Builtins__RubyClass_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + ExtendClass(typeof(IronRuby.Builtins.RubyModule), new System.Action(LoadIronRuby__Builtins__RubyModule_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + ExtendModule(typeof(IronRuby.Builtins.RubyRegex), new System.Action(LoadIronRuby__Builtins__RubyRegex_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendModule(typeof(IronRuby.Builtins.RubyStruct), new System.Action(LoadIronRuby__Builtins__RubyStruct_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendModule(typeof(IronRuby.Builtins.TrueClass), new System.Action(LoadIronRuby__Builtins__TrueClass_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendClass(typeof(IronRuby.StandardLibrary.Yaml.Node), new System.Action(LoadIronRuby__StandardLibrary__Yaml__Node_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + ExtendModule(typeof(Microsoft.Scripting.Math.BigInteger), new System.Action(LoadMicrosoft__Scripting__Math__BigInteger_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendModule(typeof(Microsoft.Scripting.SymbolId), new System.Action(LoadMicrosoft__Scripting__SymbolId_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendModule(typeof(System.DateTime), new System.Action(LoadSystem__DateTime_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendModule(typeof(System.Double), new System.Action(LoadSystem__Double_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendModule(typeof(System.Dynamic.Null), new System.Action(LoadSystem__Dynamic__Null_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendModule(typeof(System.Exception), new System.Action(LoadSystem__Exception_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + ExtendClass(typeof(System.Object), new System.Action(LoadSystem__Object_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyModule def1 = DefineGlobalModule("YAML", typeof(IronRuby.StandardLibrary.Yaml.RubyYaml), null, new System.Action(LoadYAML_Class), IronRuby.Builtins.RubyModule.EmptyArray); + IronRuby.Builtins.RubyClass def2 = DefineClass("YAML::Stream", typeof(IronRuby.StandardLibrary.Yaml.YamlStream), Context.ObjectClass, new System.Action(LoadYAML__Stream_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.YamlStreamOps.CreateStream), + }); + def1.SetConstant("Stream", def2); + } + + private void LoadIronRuby__Builtins__FalseClass_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlFalseOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlFalseOps.ToYaml), + }); + + } + + private void LoadIronRuby__Builtins__Hash_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlHashOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlHashOps.ToYamlNode), + }); + + } + + private void LoadIronRuby__Builtins__Integer_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlIntegerOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlIntegerOps.ToYaml), + }); + + } + + private void LoadIronRuby__Builtins__MutableString_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("is_binary_data?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlStringOps.IsBinaryData), + }); + + module.DefineLibraryMethod("is_complex_yaml?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlStringOps.IsComplexYaml), + }); + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlStringOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlStringOps.ToYamlNode), + }); + + } + + private void LoadIronRuby__Builtins__Range_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlRangeOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlRangeOps.ToYaml), + }); + + } + + private void LoadIronRuby__Builtins__RubyArray_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlArrayOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlArrayOps.ToYamlNode), + }); + + } + + private void LoadIronRuby__Builtins__RubyClass_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlClassOps.ToYamlNode), + }); + + } + + private void LoadIronRuby__Builtins__RubyModule_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("yaml_as", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlModuleOps.YamlAs), + }); + + } + + private void LoadIronRuby__Builtins__RubyRegex_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlRegexpOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlRegexpOps.ToYaml), + }); + + } + + private void LoadIronRuby__Builtins__RubyStruct_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlStructOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlStructOps.ToYamlNode), + }); + + } + + private void LoadIronRuby__Builtins__TrueClass_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlTrueOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlTrueOps.ToYaml), + }); + + } + + private void LoadIronRuby__StandardLibrary__Yaml__Node_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("transform", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlNodeOps.Transform), + }); + + } + + private void LoadMicrosoft__Scripting__Math__BigInteger_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlBigIntegerOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlBigIntegerOps.ToYaml), + }); + + } + + private void LoadMicrosoft__Scripting__SymbolId_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlSymbolOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlSymbolOps.ToYaml), + }); + + } + + private void LoadSystem__DateTime_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.DateTimeOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.DateTimeOps.ToYaml), + }); + + } + + private void LoadSystem__Double_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlDoubleOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlDoubleOps.ToYaml), + }); + + } + + private void LoadSystem__Dynamic__Null_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlNilOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlNilOps.ToYaml), + }); + + } + + private void LoadSystem__Exception_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlExceptionOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlExceptionOps.ToYamlNode), + }); + + } + + private void LoadSystem__Object_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("taguri", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlObjectOps.TagUri), + }); + + module.DefineLibraryMethod("to_yaml", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlObjectOps.ToYaml), + }); + + module.DefineLibraryMethod("to_yaml_node", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlObjectOps.ToYamlProperties), + }); + + module.DefineLibraryMethod("to_yaml_properties", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlObjectOps.ToYamlProperties), + }); + + module.DefineLibraryMethod("to_yaml_style", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.YamlObjectOps.ToYamlStyle), + }); + + } + + private void LoadYAML_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("add_domain_type", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.AddDomainType), + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.AddDomainType), + }); + + module.DefineLibraryMethod("dump", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.Dump), + }); + + module.DefineLibraryMethod("dump_all", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.DumpAll), + }); + + module.DefineLibraryMethod("dump_stream", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.DumpStream), + }); + + module.DefineLibraryMethod("each_document", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.EachDocument), + }); + + module.DefineLibraryMethod("each_node", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.ParseDocuments), + }); + + module.DefineLibraryMethod("load", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.Load), + }); + + module.DefineLibraryMethod("load_documents", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.EachDocument), + }); + + module.DefineLibraryMethod("load_file", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.LoadFile), + }); + + module.DefineLibraryMethod("load_stream", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.LoadStream), + }); + + module.DefineLibraryMethod("parse", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.Parse), + }); + + module.DefineLibraryMethod("parse_documents", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.ParseDocuments), + }); + + module.DefineLibraryMethod("parse_file", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.ParseFile), + }); + + module.DefineLibraryMethod("quick_emit", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.QuickEmit), + }); + + module.DefineLibraryMethod("quick_emit_node", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.QuickEmitNode), + }); + + module.DefineLibraryMethod("tag_class", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.TagClass), + }); + + module.DefineLibraryMethod("tagged_classes", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.GetTaggedClasses), + }); + + module.DefineLibraryMethod("tagurize", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.Tagurize), + }); + + } + + private void LoadYAML__Stream_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("[]", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.YamlStreamOps.GetDocument), + }); + + module.DefineLibraryMethod("add", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.YamlStreamOps.Add), + }); + + module.DefineLibraryMethod("documents", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.YamlStreamOps.GetDocuments), + }); + + module.DefineLibraryMethod("documents=", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.YamlStreamOps.SetDocuments), + }); + + module.DefineLibraryMethod("edit", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.YamlStreamOps.EditDocument), + }); + + module.DefineLibraryMethod("emit", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.YamlStreamOps.Emit), + }); + + module.DefineLibraryMethod("inspect", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.YamlStreamOps.Inspect), + }); + + module.DefineLibraryMethod("options", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.YamlStreamOps.GetOptions), + }); + + module.DefineLibraryMethod("options=", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Yaml.RubyYaml.YamlStreamOps.SetOptions), + }); + + } + + } +} + diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/IronRuby.Libraries.Yaml.csproj b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/IronRuby.Libraries.Yaml.csproj new file mode 100644 index 0000000000..f842e21b07 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/IronRuby.Libraries.Yaml.csproj @@ -0,0 +1,112 @@ + + + + Debug + AnyCPU + 9.0.30718 + 2.0 + {AA18A245-E342-4368-A474-83178311A742} + Library + Properties + IronRuby.Libraries.Yaml + IronRuby.Libraries.Yaml + v2.0 + SAK + SAK + SAK + SAK + true + ..\..\..\..\..\Main\MSSharedLibKey.snk + true + + + true + full + false + ..\..\..\..\..\Main\bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\..\Main\bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + Code + + + + + + + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4} + Microsoft.Scripting.Core + + + {77323B06-15A2-4CF4-8A7A-86EAA2B66498} + IronRuby.Libraries + + + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD} + Ruby %28Languages\Ruby\Ruby%29 + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Scripting + + + + + + + + \ No newline at end of file diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/IronRuby.Libraries.Yaml.csproj.vspscc b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/IronRuby.Libraries.Yaml.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/IronRuby.Libraries.Yaml.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/MutableStringReader.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/MutableStringReader.cs new file mode 100644 index 0000000000..8dfbfa2288 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/MutableStringReader.cs @@ -0,0 +1,55 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.IO; +using System.Text; +using IronRuby.Builtins; +using Microsoft.Scripting.Utils; + +namespace IronRuby.StandardLibrary.Yaml { + internal class MutableStringReader : TextReader { + private readonly MutableString/*!*/ _str; + private int _pos = 0; + + internal MutableStringReader(MutableString/*!*/ str) { + Assert.NotNull(str); + _str = str; + } + + public override int Peek() { + return _str.GetChar(_pos); + } + + public override int Read() { + return _pos < _str.Length ? _str.GetChar(_pos++) : -1; + } + + public override int Read(char[]/*!*/ buffer, int index, int count) { + int read = _str.Length - _pos; + if (read > 0) { + if (read > count) { + read = count; + } + _str.ConvertToString().CopyTo(_pos, buffer, index, read); + _pos += read; + } + return read; + } + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/MutableStringWriter.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/MutableStringWriter.cs new file mode 100644 index 0000000000..51883dce27 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/MutableStringWriter.cs @@ -0,0 +1,49 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.IO; +using System.Text; +using IronRuby.Builtins; + +namespace IronRuby.StandardLibrary.Yaml { + + internal class MutableStringWriter : TextWriter { + private readonly MutableString _str = MutableString.Create(""); + + public override Encoding Encoding { + get { + // TODO: return MutableString encoding + throw new NotImplementedException(); + } + } + + public override void Write(char value) { + _str.Append(value); + } + + public override void Write(char[] buffer, int index, int count) { + // TODO: MutableString needs Append(char[], index, count) + _str.Append(new string(buffer), index, count); + } + + internal MutableString String { + get { return _str; } + } + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Properties/AssemblyInfo.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b07528bf98 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security; +using IronRuby.Runtime; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IronRuby.Libraries.Yaml")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("IronRuby.Libraries.Yaml")] +[assembly: AssemblyCopyright("Copyright © MSIT 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a1c8b506-e79a-4013-ae17-2e31618b5baf")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion(RubyContext.IronRubyVersionString)] +[assembly: AssemblyFileVersion(RubyContext.IronRubyVersionString)] +[assembly: AllowPartiallyTrustedCallers] +[assembly: SecurityTransparent] + diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyConstructor.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyConstructor.cs new file mode 100644 index 0000000000..258137ab15 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyConstructor.cs @@ -0,0 +1,308 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.StandardLibrary.Yaml { + + public class RubyConstructor : Constructor { + private readonly static Dictionary _yamlConstructors = new Dictionary(); + private readonly static Dictionary _yamlMultiConstructors = new Dictionary(); + private readonly static Dictionary _yamlMultiRegexps = new Dictionary(); + private readonly static Regex _regexPattern = new Regex("^/(?.+)/(?[eimnosux]*)$", RegexOptions.Compiled); + + private static readonly CallSite> _New = + CallSite>.Create(LibrarySites.InstanceCallAction("new", 3)); + + private static readonly CallSite> _YamlInitialize = + CallSite>.Create(LibrarySites.InstanceCallAction("yaml_initialize", 3)); + + public override YamlConstructor GetYamlConstructor(string key) { + YamlConstructor result; + if (_yamlConstructors.TryGetValue(key, out result)) { + return result; + } + return base.GetYamlConstructor(key); + } + + public override YamlMultiConstructor GetYamlMultiConstructor(string key) { + YamlMultiConstructor result; + if (_yamlMultiConstructors.TryGetValue(key, out result)) { + return result; + } + return base.GetYamlMultiConstructor(key); + } + + public override Regex GetYamlMultiRegexp(string key) { + Regex result; + if (_yamlMultiRegexps.TryGetValue(key, out result)) { + return result; + } + return base.GetYamlMultiRegexp(key); + } + + public override ICollection GetYamlMultiRegexps() { + return _yamlMultiRegexps.Keys; + } + + public new static void AddConstructor(string tag, YamlConstructor ctor) { + if (!_yamlConstructors.ContainsKey(tag)) { + _yamlConstructors.Add(tag, ctor); + } + } + + public new static void AddMultiConstructor(string tagPrefix, YamlMultiConstructor ctor) { + if (!_yamlMultiConstructors.ContainsKey(tagPrefix)) { + _yamlMultiConstructors.Add(tagPrefix, ctor); + _yamlMultiRegexps.Add(tagPrefix, new Regex("^" + tagPrefix, RegexOptions.Compiled)); + } + } + + private class ExternalConstructor { + private BlockParam _block; + + public ExternalConstructor(BlockParam block) { + _block = block; + } + + public object Construct(IConstructor ctor, string tag, Node node) { + object result; + _block.Yield(MutableString.Create(tag), ctor.ConstructPrimitive(node), out result); + return result; + } + + public object Construct(IConstructor ctor, Node node) { + return Construct(ctor, node.Tag, node); + } + } + + public static void AddExternalConstructor(string tag, BlockParam block) { + if (!_yamlConstructors.ContainsKey(tag)) { + _yamlConstructors.Add(tag, new ExternalConstructor(block).Construct); + } + } + + public static void AddExternalMultiConstructor(string regex, BlockParam block) { + if (!_yamlMultiConstructors.ContainsKey(regex)) { + _yamlMultiConstructors.Add(regex, new ExternalConstructor(block).Construct); + _yamlMultiRegexps.Add(regex, new Regex(regex, RegexOptions.Compiled)); + } + } + + public static object ConstructRubyOmap(IConstructor ctor, Node node) { + return ctor.ConstructPairs(node); + } + + public static object ConstructRubyScalar(IConstructor ctor, Node node) { + object value = ctor.ConstructScalar(node); + if (value == null) { + return value; + } + string str = value as string; + if (str != null) { + return MutableString.Create(str); + } + return value; + } + + private static object ParseObject(IConstructor ctor, string value) { + Composer composer = RubyYaml.MakeComposer(new StringReader(value)); + if (composer.CheckNode()) { + return ctor.ConstructObject(composer.GetNode()); + } else { + throw new ConstructorException("Invalid YAML element: " + value); + } + } + + public static Range ConstructRubyRange(IConstructor/*!*/ ctor, Node node) { + object begin = null; + object end = null; + bool excludeEnd = false; + ScalarNode scalar = node as ScalarNode; + if (scalar != null) { + string value = scalar.Value; + int dotsIdx; + if ((dotsIdx = value.IndexOf("...")) != -1) { + begin = ParseObject(ctor, value.Substring(0, dotsIdx)); + end = ParseObject(ctor, value.Substring(dotsIdx + 3)); + excludeEnd = true; + } else if ((dotsIdx = value.IndexOf("..")) != -1) { + begin = ParseObject(ctor, value.Substring(0, dotsIdx)); + end = ParseObject(ctor, value.Substring(dotsIdx + 2)); + } else { + throw new ConstructorException("Invalid Range: " + value); + } + } else { + MappingNode mapping = node as MappingNode; + if (mapping == null) { + throw new ConstructorException("Invalid Range: " + node); + } + foreach (KeyValuePair n in mapping.Nodes) { + string key = ctor.ConstructScalar(n.Key).ToString(); + switch (key) { + case "begin": + begin = ctor.ConstructObject(n.Value); + break; + case "end": + end = ctor.ConstructObject(n.Value); + break; + case "excl": + TryConstructYamlBool(ctor, n.Value, out excludeEnd); + break; + default: + throw new ConstructorException(string.Format("'{0}' is not allowed as an instance variable name for class Range", key)); + } + } + } + return new Range(ctor.Scope.RubyContext, begin, end, excludeEnd); + } + + public static RubyRegex ConstructRubyRegexp(IConstructor ctor, Node node) { + ScalarNode scalar = node as ScalarNode; + if (node == null) { + throw RubyExceptions.CreateTypeError("Can only create regex from scalar node"); + } + Match match = _regexPattern.Match(scalar.Value); + if (!match.Success) { + throw new ConstructorException("Invalid Regular expression: \"" + scalar.Value + "\""); + } + RubyRegexOptions options = new RubyRegexOptions(); + foreach (char c in match.Groups["opts"].Value) { + switch (c) { + case 'i': options |= RubyRegexOptions.IgnoreCase; break; + case 'x': options |= RubyRegexOptions.Extended; break; + case 'm': options |= RubyRegexOptions.Multiline; break; + case 'o': break; + case 'n': options |= RubyRegexOptions.FIXED; break; + case 'e': options |= RubyRegexOptions.EUC; break; + case 's': options |= RubyRegexOptions.SJIS; break; + case 'u': options |= RubyRegexOptions.UTF8; break; + default: + throw new ConstructorException("Unknown regular expression option: '" + c + "'"); + } + } + return new RubyRegex(match.Groups["expr"].Value, options); + } + + public static object ConstructPrivateObject(IConstructor ctor, string className, Node node) { + MappingNode mapping = node as MappingNode; + if (mapping == null) { + throw new ConstructorException("can only construct private type from mapping node"); + } + RubyModule module; + RubyScope scope = ctor.Scope; + if (scope.RubyContext.TryGetModule(scope.GlobalScope, className, out module)) { + if (!module.IsClass) { + throw new ConstructorException("Cannot construct module"); + } + Hash values = ctor.ConstructMapping(mapping); + RubyMethodInfo method = (module.GetMethod("yaml_initialize") as RubyMethodInfo); + if (method != null) { + object result = RubyUtils.CreateObject((RubyClass)module); + _YamlInitialize.Target(_YamlInitialize, scope.RubyContext, result, className, values); + return result; + } else { + return RubyUtils.CreateObject((RubyClass)module, values, true); + } + } else { + //TODO: YAML::Object + throw new NotImplementedError("YAML::Object is not implemented yet"); + } + } + + public static object ConstructRubyStruct(IConstructor ctor, string className, Node node) { + MappingNode mapping = node as MappingNode; + if (mapping == null) { + throw new ConstructorException("can only construct struct from mapping node"); + } + + RubyScope scope = ctor.Scope; + RubyModule module; + RubyClass cls; + if (scope.RubyContext.TryGetModule(scope.GlobalScope, className, out module)) { + cls = module as RubyClass; + if (cls == null) { + throw new ConstructorException("Struct type name must be Ruby class"); + } + } else { + RubyModule structModule = scope.RubyContext.GetModule(typeof(RubyStruct)); + cls = RubyUtils.GetConstant(scope, structModule, className, false) as RubyClass; + if (cls == null) { + throw new ConstructorException(String.Format("Cannot find struct class \"{0}\"", className)); + } + } + + RubyStruct newStruct = RubyStruct.Create(cls); + foreach (var pair in ctor.ConstructMapping(mapping)) { + RubyStructOps.SetValue(newStruct, SymbolTable.StringToId(pair.Key.ToString()), pair.Value); + } + return newStruct; + } + + public static MutableString ConstructRubyBinary(IConstructor ctor, Node node) { + return MutableString.CreateBinary(SafeConstructor.ConstructYamlBinary(ctor, node)); + } + + public static object ConstructRubyTimestampYMD(IConstructor ctor, Node node) { + ScalarNode scalar = node as ScalarNode; + if (scalar == null) { + throw new ConstructorException("Can only contruct timestamp from scalar node."); + } + + Match match = SafeConstructor.YMD_REGEXP.Match(scalar.Value); + if (match.Success) { + int year_ymd = int.Parse(match.Groups[1].Value); + int month_ymd = int.Parse(match.Groups[2].Value); + int day_ymd = int.Parse(match.Groups[3].Value); + + RubyModule module; + RubyScope scope = ctor.Scope; + if (scope.RubyContext.TryGetModule(scope.GlobalScope, "Date", out module)) { + return _New.Target(_New, scope.RubyContext, module, year_ymd, month_ymd, day_ymd); + } else { + throw new ConstructorException("Date class not found."); + } + } + throw new ConstructorException("Invalid tag:yaml.org,2002:timestamp#ymd value."); + } + + public RubyConstructor(RubyScope/*!*/ scope, NodeProvider/*!*/ nodeProvider) + : base(nodeProvider, scope) { + AddConstructor("tag:yaml.org,2002:str", ConstructRubyScalar); + AddConstructor("tag:ruby.yaml.org,2002:range", ConstructRubyRange); + AddConstructor("tag:ruby.yaml.org,2002:regexp", ConstructRubyRegexp); + AddMultiConstructor("tag:ruby.yaml.org,2002:object:", ConstructPrivateObject); + AddMultiConstructor("tag:ruby.yaml.org,2002:struct:", ConstructRubyStruct); + AddConstructor("tag:yaml.org,2002:binary", ConstructRubyBinary); + AddConstructor("tag:yaml.org,2002:timestamp#ymd", ConstructRubyTimestampYMD); + + //AddConstructor("tag:yaml.org,2002:omap", ConstructRubyOmap); + //AddMultiConstructor("tag:yaml.org,2002:seq:", ConstructSpecializedRubySequence); + //AddMultiConstructor("tag:yaml.org,2002:map:", ConstructSpecializedRubyMap); + } + } +} \ No newline at end of file diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyIOReader.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyIOReader.cs new file mode 100644 index 0000000000..6476b8f8e0 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyIOReader.cs @@ -0,0 +1,40 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.IO; +using System.Text; +using IronRuby.Builtins; +using System.Dynamic.Utils; + +namespace IronRuby.StandardLibrary.Yaml { + internal class RubyIOReader : TextReader { + private readonly RubyIO _io; + + internal RubyIOReader(RubyIO io) { + _io = io; + } + + public override int Peek() { + return _io.PeekByteNormalizeEoln(); + } + public override int Read() { + return _io.ReadByteNormalizeEoln(); + } + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyIOWriter.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyIOWriter.cs new file mode 100644 index 0000000000..2bf70ea7f7 --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyIOWriter.cs @@ -0,0 +1,53 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.IO; +using System.Text; +using IronRuby.Builtins; +using Microsoft.Scripting.Utils; + +namespace IronRuby.StandardLibrary.Yaml { + internal class RubyIOWriter : TextWriter { + private readonly RubyIO/*!*/ _io; + + internal RubyIOWriter(RubyIO/*!*/ io) { + Assert.NotNull(io); + _io = io; + } + + public override Encoding Encoding { + get { + // TODO: return RubyIO encoding + throw new NotImplementedException(); + } + } + + public override void Write(char value) { + _io.Write(new string(value, 1)); + } + + public override void Write(char[] buffer, int index, int count) { + _io.Write(buffer, index, count); + } + + public override void Flush() { + _io.Flush(); + } + } +} diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyRepresenter.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyRepresenter.cs new file mode 100644 index 0000000000..8f8bb3e2da --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyRepresenter.cs @@ -0,0 +1,146 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Runtime.CompilerServices; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.StandardLibrary.Yaml { + + public class RubyRepresenter : Representer { + private readonly RubyContext/*!*/ _context; + private RubyMemberInfo _objectToYamlMethod; + + public RubyContext/*!*/ Context { + get { return _context; } + } + + public RubyRepresenter(RubyContext/*!*/ context, ISerializer/*!*/ serializer, YamlOptions/*!*/ opts) + : base(serializer, opts) { + _context = context; + _objectToYamlMethod = context.GetClass(typeof(object)).ResolveMethod("to_yaml", false); + } + + #region dynamic sites + + private static readonly CallSite> _TagUri = CallSite>.Create(LibrarySites.InstanceCallAction("taguri")); + private static readonly CallSite> _ToYamlStyle = CallSite>.Create(LibrarySites.InstanceCallAction("to_yaml_style")); + private static readonly CallSite> _ToYamlNode = CallSite>.Create(LibrarySites.InstanceCallAction("to_yaml_node", 1)); + private static readonly CallSite> _ToYaml = CallSite>.Create(LibrarySites.InstanceCallAction("to_yaml", 1)); + private static CallSite> _ToYamlProperties = CallSite>.Create(LibrarySites.InstanceCallAction("to_yaml_properties")); + + internal static object ToYamlStyle(RubyContext/*!*/ context, object self) { + return _ToYamlStyle.Target(_ToYamlStyle, context, self); + } + + internal static RubyArray ToYamlProperties(RubyContext/*!*/ context, object self) { + return _ToYamlProperties.Target(_ToYamlProperties, context, self); + } + + internal static MutableString TagUri(RubyContext/*!*/ context, object self) { + return _TagUri.Target(_TagUri, context, self); + } + + #endregion + + protected override Node CreateNode(object data) { + RubyMemberInfo method = _context.ResolveMethod(data, "to_yaml", false).InvalidateSitesOnOverride(); + + if (method == _objectToYamlMethod) { + return _ToYamlNode.Target(_ToYamlNode, _context, data, this); + } else { + // TODO: this doesn't seem right + // (we're passing the extra argument, but the callee might not take it?) + return _ToYaml.Target(_ToYaml, _context, data, this); + } + } + + protected override bool IgnoreAliases(object data) { + return RubyUtils.IsRubyValueType(data) || base.IgnoreAliases(data); + } + + internal Node Scalar(MutableString taguri, MutableString value, SymbolId style) { + return Scalar( + taguri != null ? taguri.ConvertToString() : "", + value != null ? value.ConvertToString() : "", + //It's not clear what style argument really means, it seems to be always :plain + //for now we are ignoring it, defaulting to \0 (see Representer class) + '\0' + ); + } + + internal Node Scalar(object self, MutableString value) { + MutableString taguri = _TagUri.Target(_TagUri, _context, self); + MutableString styleStr = ToYamlStyle(_context, self) as MutableString; + + char style = '\0'; + if (!MutableString.IsNullOrEmpty(styleStr)) { + style = styleStr.GetChar(0); + } + + return Scalar( + taguri != null ? taguri.ConvertToString() : "", + value != null ? value.ConvertToString() : "", + style + ); + } + + internal Node Map(object self, Hash map) { + MutableString taguri = _TagUri.Target(_TagUri, _context, self); + object style = _ToYamlStyle.Target(_ToYamlStyle, _context, self); + + return Map( + taguri != null ? taguri.ConvertToString() : "", + map, + RubyOps.IsTrue(style) + ); + } + + internal Node Sequence(object self, RubyArray seq) { + MutableString taguri = _TagUri.Target(_TagUri, _context, self); + object style = _ToYamlStyle.Target(_ToYamlStyle, _context, self); + + return Sequence( + taguri != null ? taguri.ConvertToString() : "", + seq, + RubyOps.IsTrue(style) + ); + } + + + internal static void AddYamlProperties(RubyContext/*!*/ context, object self, Hash map) { + AddYamlProperties(context, self, map, ToYamlProperties(context, self)); + } + + internal static void AddYamlProperties(RubyContext/*!*/ context, object self, Hash map, RubyArray props) { + foreach (object prop in props) { + string p = prop.ToString(); + IDictionaryOps.SetElement( + context, + map, + MutableString.Create(p.Substring(1)), + KernelOps.InstanceVariableGet(context, self, p) + ); + } + } + } +} \ No newline at end of file diff --git a/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyYaml.cs b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyYaml.cs new file mode 100644 index 0000000000..84d112a25d --- /dev/null +++ b/merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml/RubyYaml.cs @@ -0,0 +1,386 @@ +/***** BEGIN LICENSE BLOCK ***** + * Version: CPL 1.0 + * + * The contents of this file are subject to the Common Public + * License Version 1.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.eclipse.org/legal/cpl-v10.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Copyright (C) 2007 Ola Bini + * Copyright (c) Microsoft Corporation. + * + ***** END LICENSE BLOCK *****/ + +using System; +using System.Collections; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using IronRuby.Builtins; +using IronRuby.Runtime; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.StandardLibrary.Yaml { + + [RubyModule("YAML")] + public static class RubyYaml { + private static readonly CallSite> _New = CallSite>.Create(LibrarySites.InstanceCallAction("new", 1)); + private static readonly CallSite> _Add = CallSite>.Create(LibrarySites.InstanceCallAction("add", 1)); + private static readonly CallSite> _Emit = CallSite>.Create(LibrarySites.InstanceCallAction("emit")); + private const string _Stream = "Stream"; + private const string _TaggedClasses = "tagged_classes"; + + // TODO: missing public singleton methods: + //add_builtin_type + //add_private_type + //add_ruby_type + //detect_implicit + //emitter + //generic_parser + //object_maker + //read_type_class + //resolver + //transfer + //try_implicit + //yaml_tag_class_name + //yaml_tag_read_class + + [RubyMethod("tagged_classes", RubyMethodAttributes.PublicSingleton)] + public static object GetTaggedClasses(RubyModule/*!*/ self) { + object taggedClasses; + if (!self.TryGetClassVariable(_TaggedClasses, out taggedClasses)) { + taggedClasses = CreateDefaultTagMapping(self.Context); + self.SetClassVariable(_TaggedClasses, taggedClasses); + } + return taggedClasses; + } + + private static Hash CreateDefaultTagMapping(RubyContext/*!*/ context) { + Hash taggedClasses = new Hash(context.EqualityComparer); + taggedClasses.Add(MutableString.Create("tag:ruby.yaml.org,2002:array"), context.GetClass(typeof(RubyArray))); + taggedClasses.Add(MutableString.Create("tag:ruby.yaml.org,2002:exception"), context.GetClass(typeof(Exception))); + taggedClasses.Add(MutableString.Create("tag:ruby.yaml.org,2002:hash"), context.GetClass(typeof(Hash))); + taggedClasses.Add(MutableString.Create("tag:ruby.yaml.org,2002:object"), context.GetClass(typeof(object))); + taggedClasses.Add(MutableString.Create("tag:ruby.yaml.org,2002:range"), context.GetClass(typeof(Range))); + taggedClasses.Add(MutableString.Create("tag:ruby.yaml.org,2002:regexp"), context.GetClass(typeof(RubyRegex))); + taggedClasses.Add(MutableString.Create("tag:ruby.yaml.org,2002:string"), context.GetClass(typeof(MutableString))); + taggedClasses.Add(MutableString.Create("tag:ruby.yaml.org,2002:struct"), context.GetClass(typeof(RubyStruct))); + taggedClasses.Add(MutableString.Create("tag:ruby.yaml.org,2002:sym"), context.GetClass(typeof(SymbolId))); + taggedClasses.Add(MutableString.Create("tag:ruby.yaml.org,2002:symbol"), context.GetClass(typeof(SymbolId))); + taggedClasses.Add(MutableString.Create("tag:ruby.yaml.org,2002:time"), context.GetClass(typeof(DateTime))); + taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:binary"), context.GetClass(typeof(MutableString))); + taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:bool#no"), context.FalseClass); + taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:bool#yes"), context.TrueClass); + taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:float"), context.GetClass(typeof(Double))); + taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:int"), context.GetClass(typeof(Integer))); + taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:map"), context.GetClass(typeof(Hash))); + taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:null"), context.NilClass); + taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:seq"), context.GetClass(typeof(RubyArray))); + taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:str"), context.GetClass(typeof(MutableString))); + taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:timestamp"), context.GetClass(typeof(DateTime))); + //Currently not supported + //taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:omap"), ec.GetClass(typeof())); + //taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:pairs"),// ec.GetClass(typeof())); + //taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:set"),// ec.GetClass(typeof())); + //taggedClasses.Add(MutableString.Create("tag:yaml.org,2002:timestamp#ymd'"), ); + return taggedClasses; + } + + [RubyMethod("tag_class", RubyMethodAttributes.PublicSingleton)] + public static object TagClass(RubyModule/*!*/ self, object tag, object clazz) { + Hash tagged_classes = (Hash)GetTaggedClasses(self); + return RubyUtils.SetHashElement(self.Context, tagged_classes, tag, clazz); + } + + [RubyMethod("dump", RubyMethodAttributes.PublicSingleton)] + public static object Dump(RubyModule/*!*/ self, object obj, [Optional]RubyIO io) { + return DumpAll(self, new object[] { obj }, io); + } + + [RubyMethod("dump_all", RubyMethodAttributes.PublicSingleton)] + public static object DumpAll(RubyModule/*!*/ self, [NotNull]IEnumerable objs, [Optional]RubyIO io) { + return DumpAll(self.Context, objs, io); + } + + internal static object DumpAll(RubyContext/*!*/ context, [NotNull]IEnumerable objs, [Optional]RubyIO io) { + TextWriter writer; + if (io != null) { + writer = new RubyIOWriter(io); + } else { + writer = new MutableStringWriter(); + } + YamlOptions cfg = YamlOptions.DefaultOptions; + using (Serializer s = new Serializer(new Emitter(writer, cfg), cfg)) { + RubyRepresenter r = new RubyRepresenter(context, s, cfg); + foreach (object obj in objs) { + r.Represent(obj); + } + } + if (null != io) { + return io; + } else { + return ((MutableStringWriter)writer).String; + } + } + + [RubyMethod("load", RubyMethodAttributes.PublicSingleton)] + public static object Load(RubyScope/*!*/ scope, RubyModule/*!*/ self, object io) { + try { + foreach (object obj in MakeConstructor(scope, CheckYamlPort(io))) { + return obj; + } + return null; + } finally { + RubyIO rio = io as RubyIO; + if (rio != null) { + rio.Close(); + } + } + } + + [RubyMethod("load_file", RubyMethodAttributes.PublicSingleton)] + public static object LoadFile(RubyScope/*!*/ scope, RubyModule/*!*/ self, object arg) { + RubyClass file = self.Context.GetClass(typeof(RubyFile)); + object io = RubyFileOps.Open(null, file, arg, MutableString.Create("r")); + return Load(scope, self, io as RubyIO); + } + + [RubyMethod("each_document", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("load_documents", RubyMethodAttributes.PublicSingleton)] + public static object EachDocument(RubyScope/*!*/ scope, BlockParam block, RubyModule/*!*/ self, object io) { + RubyConstructor rc = MakeConstructor(scope, CheckYamlPort(io)); + if (block == null && rc.CheckData()) { + throw RubyExceptions.NoBlockGiven(); + } + + foreach (object obj in rc) { + object result; + if (block.Yield(obj, out result)) { + return result; + } + } + return null; + } + + [RubyMethod("load_stream", RubyMethodAttributes.PublicSingleton)] + public static object LoadStream(RubyScope/*!*/ scope, RubyModule/*!*/ self, object io) { + RubyConstructor rc = MakeConstructor(scope, CheckYamlPort(io)); + object streamClass = RubyUtils.GetConstant(scope, self, _Stream, false); + object stream = _New.Target(_New, scope.RubyContext, streamClass as RubyModule, null); + foreach (object doc in rc) { + _Add.Target(_Add, scope.RubyContext, stream, doc); + } + return stream; + } + + [RubyMethod("parse", RubyMethodAttributes.PublicSingleton)] + public static object Parse(RubyModule self, object io) { + try { + foreach (object obj in MakeComposer(CheckYamlPort(io))) { + return obj; + } + return null; + } finally { + RubyIO rio = io as RubyIO; + if (rio != null) { + rio.Close(); + } + } + } + + [RubyMethod("parse_documents", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("each_node", RubyMethodAttributes.PublicSingleton)] + public static object ParseDocuments(BlockParam block, RubyModule self, object io) { + Composer c = MakeComposer(CheckYamlPort(io)); + if (block == null && c.CheckNode()) { + throw RubyExceptions.NoBlockGiven(); + } + foreach (object obj in c) { + object result; + if (block.Yield(obj, out result)) { + return result; + } + } + return null; + } + + [RubyMethod("parse_file", RubyMethodAttributes.PublicSingleton)] + public static object ParseFile(RubyModule/*!*/ self, object arg) { + RubyClass file = self.Context.GetClass(typeof(RubyFile)); + object io = RubyFileOps.Open(null, file, arg, MutableString.Create("r")); + return Parse(self, io as RubyIO); + } + + [RubyMethod("dump_stream", RubyMethodAttributes.PublicSingleton)] + public static object DumpStream(RubyScope/*!*/ scope, RubyModule/*!*/ self, [NotNull]params object[] args) { + object streamClass = RubyUtils.GetConstant(scope, self, _Stream, false); + object stream = _New.Target(_New, scope.RubyContext, streamClass as RubyModule, null); + foreach (object arg in args) { + _Add.Target(_Add, scope.RubyContext, stream, arg); + } + return _Emit.Target(_Emit, scope.RubyContext, stream); + } + + [RubyMethod("quick_emit_node", RubyMethodAttributes.PublicSingleton)] + public static object QuickEmitNode(BlockParam block, RubyModule/*!*/ self, object arg, params object[] rest) { + if (block != null) { + object result; + block.Yield(arg, out result); + return result; + } + return null; + } + + [RubyMethod("quick_emit", RubyMethodAttributes.PublicSingleton)] + public static object QuickEmit(RubyContext/*!*/ context, BlockParam/*!*/ block, RubyModule/*!*/ self, object objectId, params object[] opts) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + MutableStringWriter writer = new MutableStringWriter(); + //We currently don't support serialization options, so we just ignore opts argument + YamlOptions cfg = YamlOptions.DefaultOptions; + using (Serializer s = new Serializer(new Emitter(writer, cfg), cfg)) { + RubyRepresenter r = new RubyRepresenter(context, s, cfg); + object result; + block.Yield(r, out result); + s.Serialize(result as Node); + return writer.String; + } + } + + [RubyMethod("tagurize", RubyMethodAttributes.PublicSingleton)] + public static object Tagurize(RubyContext context, RubyModule self, object arg) { + if (arg == null) { + return null; + } + if (RubySites.RespondTo(context, arg, "to_str")) { + return MutableString.Create("tag:yaml.org,2002:").Append(Protocols.ConvertToString(context, arg)); + } + return arg; + } + + [RubyMethod("add_domain_type", RubyMethodAttributes.PublicSingleton)] + public static object AddDomainType(RubyContext/*!*/ context, BlockParam/*!*/ block, RubyModule/*!*/ self, + MutableString/*!*/ domainAndDate, MutableString/*!*/ typeName) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + MutableString tag = MutableString.Create("tag:"). + Append(domainAndDate).Append(":"). + Append(typeName); + RubyConstructor.AddExternalConstructor(tag.ConvertToString(), block); + return null; + } + + [RubyMethod("add_domain_type", RubyMethodAttributes.PublicSingleton)] + public static object AddDomainType(RubyContext/*!*/ context, BlockParam/*!*/ block, RubyModule/*!*/ self, + MutableString/*!*/ domainAndDate, RubyRegex/*!*/ typeRegex) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + MutableString tag = MutableString.Create("tag:"). + Append(domainAndDate).Append(":"). + Append(typeRegex.GetPattern()); + RubyConstructor.AddExternalMultiConstructor(tag.ConvertToString(), block); + return null; + } + + private static RubyConstructor/*!*/ MakeConstructor(RubyScope/*!*/ scope, TextReader/*!*/ reader) { + return new RubyConstructor(scope, MakeComposer(reader)); + } + + internal static Composer/*!*/ MakeComposer(TextReader/*!*/ reader) { + return new Composer(new Parser(new Scanner(reader), YamlOptions.DefaultOptions.Version)); + } + + private static TextReader CheckYamlPort(object port) { + MutableString ms = port as MutableString; + if (ms != null) { + return new MutableStringReader(ms); + } + + string str = port as string; + if (str != null) { + return new StringReader(str); + } + + RubyIO io = port as RubyIO; + if (io != null) { + RubyIOOps.Binmode(io); + return new RubyIOReader(io); + } + + throw RubyExceptions.CreateTypeError("instance of IO needed"); + } + + /// + /// YAML documents collection. Allows to collect and emit YAML documents. + /// + [RubyClass("Stream", Extends = typeof(YamlStream))] + public static class YamlStreamOps { + + [RubyConstructor] + public static YamlStream CreateStream(RubyClass/*!*/ self, [Optional]Hash options) { + return new YamlStream(options ?? new Hash(self.Context.EqualityComparer)); + } + + [RubyMethod("add")] + public static RubyArray Add(RubyContext/*!*/ context, YamlStream/*!*/ self, object document) { + return IListOps.Append(context, self.Documents, document) as RubyArray; + } + + [RubyMethod("[]")] + public static object GetDocument(RubyContext/*!*/ context, YamlStream/*!*/ self, object index) { + return IListOps.GetElement(self.Documents, Protocols.CastToFixnum(context, index)); + } + + [RubyMethod("edit")] + public static object EditDocument(RubyContext/*!*/ context, YamlStream/*!*/ self, object index, object document) { + return IListOps.SetElement(context, self.Documents, Protocols.CastToFixnum(context, index), document); + } + + [RubyMethod("documents")] + public static object GetDocuments(RubyContext/*!*/ context, YamlStream/*!*/ self) { + return self.Documents; + } + + [RubyMethod("documents=")] + public static object SetDocuments(RubyContext/*!*/ context, YamlStream/*!*/ self, RubyArray value) { + return self.Documents = value; + } + + [RubyMethod("options")] + public static object GetOptions(RubyContext/*!*/ context, YamlStream/*!*/ self) { + return self.Options; + } + + [RubyMethod("options=")] + public static object SetOptions(RubyContext/*!*/ context, YamlStream/*!*/ self, Hash value) { + return self.Options = value; + } + + [RubyMethod("emit")] + public static object Emit(RubyContext/*!*/ context, YamlStream/*!*/ self, [Optional]RubyIO io) { + return RubyYaml.DumpAll(context, self.Documents, io); + } + + [RubyMethod("inspect")] + public static MutableString Inspect(RubyContext/*!*/ context, YamlStream/*!*/ self) { + MutableString result = MutableString.CreateMutable("#See Kernel.caller for details. Thread local. +alias $ERROR_POSITION $@ + +# The default separator pattern used by String.split. May be +# set from the command line using the -F flag. +alias $FS $; + +# The default separator pattern used by String.split. May be +# set from the command line using the -F flag. +alias $FIELD_SEPARATOR $; + +# The separator string output between the parameters to methods such +# as Kernel.print and Array.join. Defaults to +nil+, +# which adds no text. +alias $OFS $, + +# The separator string output between the parameters to methods such +# as Kernel.print and Array.join. Defaults to +nil+, +# which adds no text. +alias $OUTPUT_FIELD_SEPARATOR $, + +# The input record separator (newline by default). This is the value +# that routines such as Kernel.gets use to determine record +# boundaries. If set to +nil+, +gets+ will read the entire file. +alias $RS $/ + +# The input record separator (newline by default). This is the value +# that routines such as Kernel.gets use to determine record +# boundaries. If set to +nil+, +gets+ will read the entire file. +alias $INPUT_RECORD_SEPARATOR $/ + +# The string appended to the output of every call to methods such as +# Kernel.print and IO.write. The default value is +# +nil+. +alias $ORS $\ + +# The string appended to the output of every call to methods such as +# Kernel.print and IO.write. The default value is +# +nil+. +alias $OUTPUT_RECORD_SEPARATOR $\ + +# The number of the last line read from the current input file. +alias $INPUT_LINE_NUMBER $. + +# The number of the last line read from the current input file. +alias $NR $. + +# The last line read by Kernel.gets or +# Kernel.readline. Many string-related functions in the +# +Kernel+ module operate on $_ by default. The variable is +# local to the current scope. Thread local. +alias $LAST_READ_LINE $_ + +# The destination of output for Kernel.print +# and Kernel.printf. The default value is +# $stdout. +alias $DEFAULT_OUTPUT $> + +# An object that provides access to the concatenation +# of the contents of all the files +# given as command-line arguments, or $stdin +# (in the case where there are no +# arguments). $< supports methods similar to a +# +File+ object: +# +inmode+, +close+, +# closed?, +each+, +# each_byte, each_line, +# +eof+, eof?, +file+, +# +filename+, +fileno+, +# +getc+, +gets+, +lineno+, +# lineno=, +path+, +# +pos+, pos=, +# +read+, +readchar+, +# +readline+, +readlines+, +# +rewind+, +seek+, +skip+, +# +tell+, to_a, to_i, +# to_io, to_s, along with the +# methods in +Enumerable+. The method +file+ +# returns a +File+ object for the file currently +# being read. This may change as $< reads +# through the files on the command line. Read only. +alias $DEFAULT_INPUT $< + +# The process number of the program being executed. Read only. +alias $PID $$ + +# The process number of the program being executed. Read only. +alias $PROCESS_ID $$ + +# The exit status of the last child process to terminate. Read +# only. Thread local. +alias $CHILD_STATUS $? + +# A +MatchData+ object that encapsulates the results of a successful +# pattern match. The variables $&, $`, $', +# and $1 to $9 are all derived from +# $~. Assigning to $~ changes the values of these +# derived variables. This variable is local to the current +# scope. Thread local. +alias $LAST_MATCH_INFO $~ + +# If set to any value apart from +nil+ or +false+, all pattern matches +# will be case insensitive, string comparisons will ignore case, and +# string hash values will be case insensitive. Deprecated +alias $IGNORECASE $= + +# An array of strings containing the command-line +# options from the invocation of the program. Options +# used by the Ruby interpreter will have been +# removed. Read only. Also known simply as +ARGV+. +alias $ARGV $* + +# The string matched by the last successful pattern +# match. This variable is local to the current +# scope. Read only. Thread local. +alias $MATCH $& + +# The string preceding the match in the last +# successful pattern match. This variable is local to +# the current scope. Read only. Thread local. +alias $PREMATCH $` + +# The string following the match in the last +# successful pattern match. This variable is local to +# the current scope. Read only. Thread local. +alias $POSTMATCH $' + +# The contents of the highest-numbered group matched in the last +# successful pattern match. Thus, in "cat" =~ /(c|a)(t|z)/, +# $+ will be set to "t". This variable is local to the +# current scope. Read only. Thread local. +alias $LAST_PAREN_MATCH $+ diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/Env.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/Env.rb new file mode 100644 index 0000000000..452a28659e --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/Env.rb @@ -0,0 +1,18 @@ +# Env.rb -- imports environment variables as global variables, Perlish ;( +# Usage: +# +# require 'Env' +# p $USER +# $USER = "matz" +# p ENV["USER"] + +require 'importenv' + +if __FILE__ == $0 + p $TERM + $TERM = nil + p $TERM + p ENV["TERM"] + $TERM = "foo" + p ENV["TERM"] +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/abbrev.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/abbrev.rb new file mode 100644 index 0000000000..338b89f188 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/abbrev.rb @@ -0,0 +1,103 @@ +#!/usr/bin/env ruby +=begin +# +# Copyright (c) 2001,2003 Akinori MUSHA +# +# All rights reserved. You can redistribute and/or modify it under +# the same terms as Ruby. +# +# $Idaemons: /home/cvs/rb/abbrev.rb,v 1.2 2001/05/30 09:37:45 knu Exp $ +# $RoughId: abbrev.rb,v 1.4 2003/10/14 19:45:42 knu Exp $ +# $Id: abbrev.rb 11708 2007-02-12 23:01:19Z shyouhei $ +=end + +# Calculate the set of unique abbreviations for a given set of strings. +# +# require 'abbrev' +# require 'pp' +# +# pp Abbrev::abbrev(['ruby', 'rules']).sort +# +# Generates: +# +# [["rub", "ruby"], +# ["ruby", "ruby"], +# ["rul", "rules"], +# ["rule", "rules"], +# ["rules", "rules"]] +# +# Also adds an +abbrev+ method to class +Array+. + +module Abbrev + + # Given a set of strings, calculate the set of unambiguous + # abbreviations for those strings, and return a hash where the keys + # are all the possible abbreviations and the values are the full + # strings. Thus, given input of "car" and "cone", the keys pointing + # to "car" would be "ca" and "car", while those pointing to "cone" + # would be "co", "con", and "cone". + # + # The optional +pattern+ parameter is a pattern or a string. Only + # those input strings matching the pattern, or begging the string, + # are considered for inclusion in the output hash + + def abbrev(words, pattern = nil) + table = {} + seen = Hash.new(0) + + if pattern.is_a?(String) + pattern = /^#{Regexp.quote(pattern)}/ # regard as a prefix + end + + words.each do |word| + next if (abbrev = word).empty? + while (len = abbrev.rindex(/[\w\W]\z/)) > 0 + abbrev = word[0,len] + + next if pattern && pattern !~ abbrev + + case seen[abbrev] += 1 + when 1 + table[abbrev] = word + when 2 + table.delete(abbrev) + else + break + end + end + end + + words.each do |word| + next if pattern && pattern !~ word + + table[word] = word + end + + table + end + + module_function :abbrev +end + +class Array + # Calculates the set of unambiguous abbreviations for the strings in + # +self+. If passed a pattern or a string, only the strings matching + # the pattern or starting with the string are considered. + # + # %w{ car cone }.abbrev #=> { "ca" => "car", "car" => "car", + # "co" => "cone", "con" => cone", + # "cone" => "cone" } + def abbrev(pattern = nil) + Abbrev::abbrev(self, pattern) + end +end + +if $0 == __FILE__ + while line = gets + hash = line.split.abbrev + + hash.sort.each do |k, v| + puts "#{k} => #{v}" + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/base64.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/base64.rb new file mode 100644 index 0000000000..8628d611b2 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/base64.rb @@ -0,0 +1,133 @@ +# +# = base64.rb: methods for base64-encoding and -decoding stings +# +# Author:: Yukihiro Matsumoto +# Documentation:: Dave Thomas and Gavin Sinclair +# +# Until Ruby 1.8.1, these methods were defined at the top-level. Now +# they are in the Base64 module but included in the top-level, where +# their usage is deprecated. +# +# See Base64 for documentation. +# + +require "kconv" + + +# The Base64 module provides for the encoding (#encode64) and decoding +# (#decode64) of binary data using a Base64 representation. +# +# The following particular features are also provided: +# - encode into lines of a given length (#b64encode) +# - decode the special format specified in RFC2047 for the +# representation of email headers (decode_b) +# +# == Example +# +# A simple encoding and decoding. +# +# require "base64" +# +# enc = Base64.encode64('Send reinforcements') +# # -> "U2VuZCByZWluZm9yY2VtZW50cw==\n" +# plain = Base64.decode64(enc) +# # -> "Send reinforcements" +# +# The purpose of using base64 to encode data is that it translates any +# binary data into purely printable characters. It is specified in +# RFC 2045 (http://www.faqs.org/rfcs/rfc2045.html). + +module Base64 + module_function + + # Returns the Base64-decoded version of +str+. + # + # require 'base64' + # str = 'VGhpcyBpcyBsaW5lIG9uZQpUaGlzIG' + + # 'lzIGxpbmUgdHdvClRoaXMgaXMgbGlu' + + # 'ZSB0aHJlZQpBbmQgc28gb24uLi4K' + # puts Base64.decode64(str) + # + # Generates: + # + # This is line one + # This is line two + # This is line three + # And so on... + + def decode64(str) + str.unpack("m")[0] + end + + + # Decodes text formatted using a subset of RFC2047 (the one used for + # mime-encoding mail headers). + # + # Only supports an encoding type of 'b' (base 64), and only supports + # the character sets ISO-2022-JP and SHIFT_JIS (so the only two + # encoded word sequences recognized are =?ISO-2022-JP?B?...= and + # =?SHIFT_JIS?B?...=). Recognition of these sequences is case + # insensitive. + + def decode_b(str) + str.gsub!(/=\?ISO-2022-JP\?B\?([!->@-~]+)\?=/i) { + decode64($1) + } + str = Kconv::toeuc(str) + str.gsub!(/=\?SHIFT_JIS\?B\?([!->@-~]+)\?=/i) { + decode64($1) + } + str = Kconv::toeuc(str) + str.gsub!(/\n/, ' ') + str.gsub!(/\0/, '') + str + end + + # Returns the Base64-encoded version of +str+. + # + # require 'base64' + # Base64.b64encode("Now is the time for all good coders\nto learn Ruby") + # + # Generates: + # + # Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g + # UnVieQ== + + def encode64(bin) + [bin].pack("m") + end + + # _Prints_ the Base64 encoded version of +bin+ (a +String+) in lines of + # +len+ (default 60) characters. + # + # require 'base64' + # data = "Now is the time for all good coders\nto learn Ruby" + # Base64.b64encode(data) + # + # Generates: + # + # Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g + # UnVieQ== + + def b64encode(bin, len = 60) + encode64(bin).scan(/.{1,#{len}}/) do + print $&, "\n" + end + end + + + module Deprecated # :nodoc: + include Base64 + + for m in Base64.private_instance_methods(false) + module_eval %{ + def #{m}(*args) + warn("\#{caller(1)[0]}: #{m} is deprecated; use Base64.#{m} instead") + super + end + } + end + end +end + +include Base64::Deprecated diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/benchmark.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/benchmark.rb new file mode 100644 index 0000000000..4e09452eab --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/benchmark.rb @@ -0,0 +1,569 @@ +=begin +# +# benchmark.rb - a performance benchmarking library +# +# $Id: benchmark.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# +# Created by Gotoken (gotoken@notwork.org). +# +# Documentation by Gotoken (original RD), Lyle Johnson (RDoc conversion), and +# Gavin Sinclair (editing). +# +=end + +# == Overview +# +# The Benchmark module provides methods for benchmarking Ruby code, giving +# detailed reports on the time taken for each task. +# + +# The Benchmark module provides methods to measure and report the time +# used to execute Ruby code. +# +# * Measure the time to construct the string given by the expression +# "a"*1_000_000: +# +# require 'benchmark' +# +# puts Benchmark.measure { "a"*1_000_000 } +# +# On my machine (FreeBSD 3.2 on P5, 100MHz) this generates: +# +# 1.166667 0.050000 1.216667 ( 0.571355) +# +# This report shows the user CPU time, system CPU time, the sum of +# the user and system CPU times, and the elapsed real time. The unit +# of time is seconds. +# +# * Do some experiments sequentially using the #bm method: +# +# require 'benchmark' +# +# n = 50000 +# Benchmark.bm do |x| +# x.report { for i in 1..n; a = "1"; end } +# x.report { n.times do ; a = "1"; end } +# x.report { 1.upto(n) do ; a = "1"; end } +# end +# +# The result: +# +# user system total real +# 1.033333 0.016667 1.016667 ( 0.492106) +# 1.483333 0.000000 1.483333 ( 0.694605) +# 1.516667 0.000000 1.516667 ( 0.711077) +# +# * Continuing the previous example, put a label in each report: +# +# require 'benchmark' +# +# n = 50000 +# Benchmark.bm(7) do |x| +# x.report("for:") { for i in 1..n; a = "1"; end } +# x.report("times:") { n.times do ; a = "1"; end } +# x.report("upto:") { 1.upto(n) do ; a = "1"; end } +# end +# +# The result: +# +# user system total real +# for: 1.050000 0.000000 1.050000 ( 0.503462) +# times: 1.533333 0.016667 1.550000 ( 0.735473) +# upto: 1.500000 0.016667 1.516667 ( 0.711239) +# +# +# * The times for some benchmarks depend on the order in which items +# are run. These differences are due to the cost of memory +# allocation and garbage collection. To avoid these discrepancies, +# the #bmbm method is provided. For example, to compare ways to +# sort an array of floats: +# +# require 'benchmark' +# +# array = (1..1000000).map { rand } +# +# Benchmark.bmbm do |x| +# x.report("sort!") { array.dup.sort! } +# x.report("sort") { array.dup.sort } +# end +# +# The result: +# +# Rehearsal ----------------------------------------- +# sort! 11.928000 0.010000 11.938000 ( 12.756000) +# sort 13.048000 0.020000 13.068000 ( 13.857000) +# ------------------------------- total: 25.006000sec +# +# user system total real +# sort! 12.959000 0.010000 12.969000 ( 13.793000) +# sort 12.007000 0.000000 12.007000 ( 12.791000) +# +# +# * Report statistics of sequential experiments with unique labels, +# using the #benchmark method: +# +# require 'benchmark' +# +# n = 50000 +# Benchmark.benchmark(" "*7 + CAPTION, 7, FMTSTR, ">total:", ">avg:") do |x| +# tf = x.report("for:") { for i in 1..n; a = "1"; end } +# tt = x.report("times:") { n.times do ; a = "1"; end } +# tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end } +# [tf+tt+tu, (tf+tt+tu)/3] +# end +# +# The result: +# +# user system total real +# for: 1.016667 0.016667 1.033333 ( 0.485749) +# times: 1.450000 0.016667 1.466667 ( 0.681367) +# upto: 1.533333 0.000000 1.533333 ( 0.722166) +# >total: 4.000000 0.033333 4.033333 ( 1.889282) +# >avg: 1.333333 0.011111 1.344444 ( 0.629761) + +module Benchmark + + BENCHMARK_VERSION = "2002-04-25" #:nodoc" + + def Benchmark::times() # :nodoc: + Process::times() + end + + + # Invokes the block with a Benchmark::Report object, which + # may be used to collect and report on the results of individual + # benchmark tests. Reserves label_width leading spaces for + # labels on each line. Prints _caption_ at the top of the + # report, and uses _fmt_ to format each line. + # If the block returns an array of + # Benchmark::Tms objects, these will be used to format + # additional lines of output. If _label_ parameters are + # given, these are used to label these extra lines. + # + # _Note_: Other methods provide a simpler interface to this one, and are + # suitable for nearly all benchmarking requirements. See the examples in + # Benchmark, and the #bm and #bmbm methods. + # + # Example: + # + # require 'benchmark' + # include Benchmark # we need the CAPTION and FMTSTR constants + # + # n = 50000 + # Benchmark.benchmark(" "*7 + CAPTION, 7, FMTSTR, ">total:", ">avg:") do |x| + # tf = x.report("for:") { for i in 1..n; a = "1"; end } + # tt = x.report("times:") { n.times do ; a = "1"; end } + # tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end } + # [tf+tt+tu, (tf+tt+tu)/3] + # end + # + # Generates: + # + # user system total real + # for: 1.016667 0.016667 1.033333 ( 0.485749) + # times: 1.450000 0.016667 1.466667 ( 0.681367) + # upto: 1.533333 0.000000 1.533333 ( 0.722166) + # >total: 4.000000 0.033333 4.033333 ( 1.889282) + # >avg: 1.333333 0.011111 1.344444 ( 0.629761) + # + + def benchmark(caption = "", label_width = nil, fmtstr = nil, *labels) # :yield: report + sync = STDOUT.sync + STDOUT.sync = true + label_width ||= 0 + fmtstr ||= FMTSTR + raise ArgumentError, "no block" unless iterator? + print caption + results = yield(Report.new(label_width, fmtstr)) + Array === results and results.grep(Tms).each {|t| + print((labels.shift || t.label || "").ljust(label_width), + t.format(fmtstr)) + } + STDOUT.sync = sync + end + + + # A simple interface to the #benchmark method, #bm is generates sequential reports + # with labels. The parameters have the same meaning as for #benchmark. + # + # require 'benchmark' + # + # n = 50000 + # Benchmark.bm(7) do |x| + # x.report("for:") { for i in 1..n; a = "1"; end } + # x.report("times:") { n.times do ; a = "1"; end } + # x.report("upto:") { 1.upto(n) do ; a = "1"; end } + # end + # + # Generates: + # + # user system total real + # for: 1.050000 0.000000 1.050000 ( 0.503462) + # times: 1.533333 0.016667 1.550000 ( 0.735473) + # upto: 1.500000 0.016667 1.516667 ( 0.711239) + # + + def bm(label_width = 0, *labels, &blk) # :yield: report + benchmark(" "*label_width + CAPTION, label_width, FMTSTR, *labels, &blk) + end + + + # Sometimes benchmark results are skewed because code executed + # earlier encounters different garbage collection overheads than + # that run later. #bmbm attempts to minimize this effect by running + # the tests twice, the first time as a rehearsal in order to get the + # runtime environment stable, the second time for + # real. GC.start is executed before the start of each of + # the real timings; the cost of this is not included in the + # timings. In reality, though, there's only so much that #bmbm can + # do, and the results are not guaranteed to be isolated from garbage + # collection and other effects. + # + # Because #bmbm takes two passes through the tests, it can + # calculate the required label width. + # + # require 'benchmark' + # + # array = (1..1000000).map { rand } + # + # Benchmark.bmbm do |x| + # x.report("sort!") { array.dup.sort! } + # x.report("sort") { array.dup.sort } + # end + # + # Generates: + # + # Rehearsal ----------------------------------------- + # sort! 11.928000 0.010000 11.938000 ( 12.756000) + # sort 13.048000 0.020000 13.068000 ( 13.857000) + # ------------------------------- total: 25.006000sec + # + # user system total real + # sort! 12.959000 0.010000 12.969000 ( 13.793000) + # sort 12.007000 0.000000 12.007000 ( 12.791000) + # + # #bmbm yields a Benchmark::Job object and returns an array of + # Benchmark::Tms objects. + # + def bmbm(width = 0, &blk) # :yield: job + job = Job.new(width) + yield(job) + width = job.width + sync = STDOUT.sync + STDOUT.sync = true + + # rehearsal + print "Rehearsal " + puts '-'*(width+CAPTION.length - "Rehearsal ".length) + list = [] + job.list.each{|label,item| + print(label.ljust(width)) + res = Benchmark::measure(&item) + print res.format() + list.push res + } + sum = Tms.new; list.each{|i| sum += i} + ets = sum.format("total: %tsec") + printf("%s %s\n\n", + "-"*(width+CAPTION.length-ets.length-1), ets) + + # take + print ' '*width, CAPTION + list = [] + ary = [] + job.list.each{|label,item| + GC::start + print label.ljust(width) + res = Benchmark::measure(&item) + print res.format() + ary.push res + list.push [label, res] + } + + STDOUT.sync = sync + ary + end + + # + # Returns the time used to execute the given block as a + # Benchmark::Tms object. + # + def measure(label = "") # :yield: + t0, r0 = Benchmark.times, Time.now + yield + t1, r1 = Benchmark.times, Time.now + Benchmark::Tms.new(t1.utime - t0.utime, + t1.stime - t0.stime, + t1.cutime - t0.cutime, + t1.cstime - t0.cstime, + r1.to_f - r0.to_f, + label) + end + + # + # Returns the elapsed real time used to execute the given block. + # + def realtime(&blk) # :yield: + Benchmark::measure(&blk).real + end + + + + # + # A Job is a sequence of labelled blocks to be processed by the + # Benchmark.bmbm method. It is of little direct interest to the user. + # + class Job # :nodoc: + # + # Returns an initialized Job instance. + # Usually, one doesn't call this method directly, as new + # Job objects are created by the #bmbm method. + # _width_ is a initial value for the label offset used in formatting; + # the #bmbm method passes its _width_ argument to this constructor. + # + def initialize(width) + @width = width + @list = [] + end + + # + # Registers the given label and block pair in the job list. + # + def item(label = "", &blk) # :yield: + raise ArgmentError, "no block" unless block_given? + label.concat ' ' + w = label.length + @width = w if @width < w + @list.push [label, blk] + self + end + + alias report item + + # An array of 2-element arrays, consisting of label and block pairs. + attr_reader :list + + # Length of the widest label in the #list, plus one. + attr_reader :width + end + + module_function :benchmark, :measure, :realtime, :bm, :bmbm + + + + # + # This class is used by the Benchmark.benchmark and Benchmark.bm methods. + # It is of little direct interest to the user. + # + class Report # :nodoc: + # + # Returns an initialized Report instance. + # Usually, one doesn't call this method directly, as new + # Report objects are created by the #benchmark and #bm methods. + # _width_ and _fmtstr_ are the label offset and + # format string used by Tms#format. + # + def initialize(width = 0, fmtstr = nil) + @width, @fmtstr = width, fmtstr + end + + # + # Prints the _label_ and measured time for the block, + # formatted by _fmt_. See Tms#format for the + # formatting rules. + # + def item(label = "", *fmt, &blk) # :yield: + print label.ljust(@width) + res = Benchmark::measure(&blk) + print res.format(@fmtstr, *fmt) + res + end + + alias report item + end + + + + # + # A data object, representing the times associated with a benchmark + # measurement. + # + class Tms + CAPTION = " user system total real\n" + FMTSTR = "%10.6u %10.6y %10.6t %10.6r\n" + + # User CPU time + attr_reader :utime + + # System CPU time + attr_reader :stime + + # User CPU time of children + attr_reader :cutime + + # System CPU time of children + attr_reader :cstime + + # Elapsed real time + attr_reader :real + + # Total time, that is _utime_ + _stime_ + _cutime_ + _cstime_ + attr_reader :total + + # Label + attr_reader :label + + # + # Returns an initialized Tms object which has + # _u_ as the user CPU time, _s_ as the system CPU time, + # _cu_ as the children's user CPU time, _cs_ as the children's + # system CPU time, _real_ as the elapsed real time and _l_ + # as the label. + # + def initialize(u = 0.0, s = 0.0, cu = 0.0, cs = 0.0, real = 0.0, l = nil) + @utime, @stime, @cutime, @cstime, @real, @label = u, s, cu, cs, real, l + @total = @utime + @stime + @cutime + @cstime + end + + # + # Returns a new Tms object whose times are the sum of the times for this + # Tms object, plus the time required to execute the code block (_blk_). + # + def add(&blk) # :yield: + self + Benchmark::measure(&blk) + end + + # + # An in-place version of #add. + # + def add! + t = Benchmark::measure(&blk) + @utime = utime + t.utime + @stime = stime + t.stime + @cutime = cutime + t.cutime + @cstime = cstime + t.cstime + @real = real + t.real + self + end + + # + # Returns a new Tms object obtained by memberwise summation + # of the individual times for this Tms object with those of the other + # Tms object. + # This method and #/() are useful for taking statistics. + # + def +(other); memberwise(:+, other) end + + # + # Returns a new Tms object obtained by memberwise subtraction + # of the individual times for the other Tms object from those of this + # Tms object. + # + def -(other); memberwise(:-, other) end + + # + # Returns a new Tms object obtained by memberwise multiplication + # of the individual times for this Tms object by _x_. + # + def *(x); memberwise(:*, x) end + + # + # Returns a new Tms object obtained by memberwise division + # of the individual times for this Tms object by _x_. + # This method and #+() are useful for taking statistics. + # + def /(x); memberwise(:/, x) end + + # + # Returns the contents of this Tms object as + # a formatted string, according to a format string + # like that passed to Kernel.format. In addition, #format + # accepts the following extensions: + # + # %u:: Replaced by the user CPU time, as reported by Tms#utime. + # %y:: Replaced by the system CPU time, as reported by #stime (Mnemonic: y of "s*y*stem") + # %U:: Replaced by the children's user CPU time, as reported by Tms#cutime + # %Y:: Replaced by the children's system CPU time, as reported by Tms#cstime + # %t:: Replaced by the total CPU time, as reported by Tms#total + # %r:: Replaced by the elapsed real time, as reported by Tms#real + # %n:: Replaced by the label string, as reported by Tms#label (Mnemonic: n of "*n*ame") + # + # If _fmtstr_ is not given, FMTSTR is used as default value, detailing the + # user, system and real elapsed time. + # + def format(arg0 = nil, *args) + fmtstr = (arg0 || FMTSTR).dup + fmtstr.gsub!(/(%[-+\.\d]*)n/){"#{$1}s" % label} + fmtstr.gsub!(/(%[-+\.\d]*)u/){"#{$1}f" % utime} + fmtstr.gsub!(/(%[-+\.\d]*)y/){"#{$1}f" % stime} + fmtstr.gsub!(/(%[-+\.\d]*)U/){"#{$1}f" % cutime} + fmtstr.gsub!(/(%[-+\.\d]*)Y/){"#{$1}f" % cstime} + fmtstr.gsub!(/(%[-+\.\d]*)t/){"#{$1}f" % total} + fmtstr.gsub!(/(%[-+\.\d]*)r/){"(#{$1}f)" % real} + arg0 ? Kernel::format(fmtstr, *args) : fmtstr + end + + # + # Same as #format. + # + def to_s + format + end + + # + # Returns a new 6-element array, consisting of the + # label, user CPU time, system CPU time, children's + # user CPU time, children's system CPU time and elapsed + # real time. + # + def to_a + [@label, @utime, @stime, @cutime, @cstime, @real] + end + + protected + def memberwise(op, x) + case x + when Benchmark::Tms + Benchmark::Tms.new(utime.__send__(op, x.utime), + stime.__send__(op, x.stime), + cutime.__send__(op, x.cutime), + cstime.__send__(op, x.cstime), + real.__send__(op, x.real) + ) + else + Benchmark::Tms.new(utime.__send__(op, x), + stime.__send__(op, x), + cutime.__send__(op, x), + cstime.__send__(op, x), + real.__send__(op, x) + ) + end + end + end + + # The default caption string (heading above the output times). + CAPTION = Benchmark::Tms::CAPTION + + # The default format string used to display times. See also Benchmark::Tms#format. + FMTSTR = Benchmark::Tms::FMTSTR +end + +if __FILE__ == $0 + include Benchmark + + n = ARGV[0].to_i.nonzero? || 50000 + puts %Q([#{n} times iterations of `a = "1"']) + benchmark(" " + CAPTION, 7, FMTSTR) do |x| + x.report("for:") {for i in 1..n; a = "1"; end} # Benchmark::measure + x.report("times:") {n.times do ; a = "1"; end} + x.report("upto:") {1.upto(n) do ; a = "1"; end} + end + + benchmark do + [ + measure{for i in 1..n; a = "1"; end}, # Benchmark::measure + measure{n.times do ; a = "1"; end}, + measure{1.upto(n) do ; a = "1"; end} + ] + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/jacobian.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/jacobian.rb new file mode 100644 index 0000000000..d80eeab901 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/jacobian.rb @@ -0,0 +1,85 @@ +# +# require 'bigdecimal/jacobian' +# +# Provides methods to compute the Jacobian matrix of a set of equations at a +# point x. In the methods below: +# +# f is an Object which is used to compute the Jacobian matrix of the equations. +# It must provide the following methods: +# +# f.values(x):: returns the values of all functions at x +# +# f.zero:: returns 0.0 +# f.one:: returns 1.0 +# f.two:: returns 1.0 +# f.ten:: returns 10.0 +# +# f.eps:: returns the convergence criterion (epsilon value) used to determine whether two values are considered equal. If |a-b| < epsilon, the two values are considered equal. +# +# x is the point at which to compute the Jacobian. +# +# fx is f.values(x). +# +module Jacobian + #-- + def isEqual(a,b,zero=0.0,e=1.0e-8) + aa = a.abs + bb = b.abs + if aa == zero && bb == zero then + true + else + if ((a-b)/(aa+bb)).abs < e then + true + else + false + end + end + end + #++ + + # Computes the derivative of f[i] at x[i]. + # fx is the value of f at x. + def dfdxi(f,fx,x,i) + nRetry = 0 + n = x.size + xSave = x[i] + ok = 0 + ratio = f.ten*f.ten*f.ten + dx = x[i].abs/ratio + dx = fx[i].abs/ratio if isEqual(dx,f.zero,f.zero,f.eps) + dx = f.one/f.ten if isEqual(dx,f.zero,f.zero,f.eps) + until ok>0 do + s = f.zero + deriv = [] + if(nRetry>100) then + raize "Singular Jacobian matrix. No change at x[" + i.to_s + "]" + end + dx = dx*f.two + x[i] += dx + fxNew = f.values(x) + for j in 0...n do + if !isEqual(fxNew[j],fx[j],f.zero,f.eps) then + ok += 1 + deriv <<= (fxNew[j]-fx[j])/dx + else + deriv <<= f.zero + end + end + x[i] = xSave + end + deriv + end + + # Computes the Jacobian of f at x. fx is the value of f at x. + def jacobian(f,fx,x) + n = x.size + dfdx = Array::new(n*n) + for i in 0...n do + df = dfdxi(f,fx,x,i) + for j in 0...n do + dfdx[j*n+i] = df[j] + end + end + dfdx + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/ludcmp.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/ludcmp.rb new file mode 100644 index 0000000000..8f4888725e --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/ludcmp.rb @@ -0,0 +1,84 @@ +# +# Solves a*x = b for x, using LU decomposition. +# +module LUSolve + # Performs LU decomposition of the n by n matrix a. + def ludecomp(a,n,zero=0,one=1) + prec = BigDecimal.limit(nil) + ps = [] + scales = [] + for i in 0...n do # pick up largest(abs. val.) element in each row. + ps <<= i + nrmrow = zero + ixn = i*n + for j in 0...n do + biggst = a[ixn+j].abs + nrmrow = biggst if biggst>nrmrow + end + if nrmrow>zero then + scales <<= one.div(nrmrow,prec) + else + raise "Singular matrix" + end + end + n1 = n - 1 + for k in 0...n1 do # Gaussian elimination with partial pivoting. + biggst = zero; + for i in k...n do + size = a[ps[i]*n+k].abs*scales[ps[i]] + if size>biggst then + biggst = size + pividx = i + end + end + raise "Singular matrix" if biggst<=zero + if pividx!=k then + j = ps[k] + ps[k] = ps[pividx] + ps[pividx] = j + end + pivot = a[ps[k]*n+k] + for i in (k+1)...n do + psin = ps[i]*n + a[psin+k] = mult = a[psin+k].div(pivot,prec) + if mult!=zero then + pskn = ps[k]*n + for j in (k+1)...n do + a[psin+j] -= mult.mult(a[pskn+j],prec) + end + end + end + end + raise "Singular matrix" if a[ps[n1]*n+n1] == zero + ps + end + + # Solves a*x = b for x, using LU decomposition. + # + # a is a matrix, b is a constant vector, x is the solution vector. + # + # ps is the pivot, a vector which indicates the permutation of rows performed + # during LU decomposition. + def lusolve(a,b,ps,zero=0.0) + prec = BigDecimal.limit(nil) + n = ps.size + x = [] + for i in 0...n do + dot = zero + psin = ps[i]*n + for j in 0...i do + dot = a[psin+j].mult(x[j],prec) + dot + end + x <<= b[ps[i]] - dot + end + (n-1).downto(0) do |i| + dot = zero + psin = ps[i]*n + for j in (i+1)...n do + dot = a[psin+j].mult(x[j],prec) + dot + end + x[i] = (x[i]-dot).div(a[psin+i],prec) + end + x + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/math.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/math.rb new file mode 100644 index 0000000000..f3248a3c5c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/math.rb @@ -0,0 +1,235 @@ +# +#-- +# Contents: +# sqrt(x, prec) +# sin (x, prec) +# cos (x, prec) +# atan(x, prec) Note: |x|<1, x=0.9999 may not converge. +# exp (x, prec) +# log (x, prec) +# PI (prec) +# E (prec) == exp(1.0,prec) +# +# where: +# x ... BigDecimal number to be computed. +# |x| must be small enough to get convergence. +# prec ... Number of digits to be obtained. +#++ +# +# Provides mathematical functions. +# +# Example: +# +# require "bigdecimal" +# require "bigdecimal/math" +# +# include BigMath +# +# a = BigDecimal((PI(100)/2).to_s) +# puts sin(a,100) # -> 0.10000000000000000000......E1 +# +module BigMath + + # Computes the square root of x to the specified number of digits of + # precision. + # + # BigDecimal.new('2').sqrt(16).to_s + # -> "0.14142135623730950488016887242096975E1" + # + def sqrt(x,prec) + x.sqrt(prec) + end + + # Computes the sine of x to the specified number of digits of precision. + # + # If x is infinite or NaN, returns NaN. + def sin(x, prec) + raise ArgumentError, "Zero or negative precision for sin" if prec <= 0 + return BigDecimal("NaN") if x.infinite? || x.nan? + n = prec + BigDecimal.double_fig + one = BigDecimal("1") + two = BigDecimal("2") + x1 = x + x2 = x.mult(x,n) + sign = 1 + y = x + d = y + i = one + z = one + while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + sign = -sign + x1 = x2.mult(x1,n) + i += two + z *= (i-one) * i + d = sign * x1.div(z,m) + y += d + end + y + end + + # Computes the cosine of x to the specified number of digits of precision. + # + # If x is infinite or NaN, returns NaN. + def cos(x, prec) + raise ArgumentError, "Zero or negative precision for cos" if prec <= 0 + return BigDecimal("NaN") if x.infinite? || x.nan? + n = prec + BigDecimal.double_fig + one = BigDecimal("1") + two = BigDecimal("2") + x1 = one + x2 = x.mult(x,n) + sign = 1 + y = one + d = y + i = BigDecimal("0") + z = one + while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + sign = -sign + x1 = x2.mult(x1,n) + i += two + z *= (i-one) * i + d = sign * x1.div(z,m) + y += d + end + y + end + + # Computes the arctangent of x to the specified number of digits of precision. + # + # If x is infinite or NaN, returns NaN. + # Raises an argument error if x > 1. + def atan(x, prec) + raise ArgumentError, "Zero or negative precision for atan" if prec <= 0 + return BigDecimal("NaN") if x.infinite? || x.nan? + raise ArgumentError, "x.abs must be less than 1.0" if x.abs>=1 + n = prec + BigDecimal.double_fig + y = x + d = y + t = x + r = BigDecimal("3") + x2 = x.mult(x,n) + while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + t = -t.mult(x2,n) + d = t.div(r,m) + y += d + r += 2 + end + y + end + + # Computes the value of e (the base of natural logarithms) raised to the + # power of x, to the specified number of digits of precision. + # + # If x is infinite or NaN, returns NaN. + # + # BigMath::exp(BigDecimal.new('1'), 10).to_s + # -> "0.271828182845904523536028752390026306410273E1" + def exp(x, prec) + raise ArgumentError, "Zero or negative precision for exp" if prec <= 0 + return BigDecimal("NaN") if x.infinite? || x.nan? + n = prec + BigDecimal.double_fig + one = BigDecimal("1") + x1 = one + y = one + d = y + z = one + i = 0 + while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + x1 = x1.mult(x,n) + i += 1 + z *= i + d = x1.div(z,m) + y += d + end + y + end + + # Computes the natural logarithm of x to the specified number of digits + # of precision. + # + # Returns x if x is infinite or NaN. + # + def log(x, prec) + raise ArgumentError, "Zero or negative argument for log" if x <= 0 || prec <= 0 + return x if x.infinite? || x.nan? + one = BigDecimal("1") + two = BigDecimal("2") + n = prec + BigDecimal.double_fig + x = (x - one).div(x + one,n) + x2 = x.mult(x,n) + y = x + d = y + i = one + while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + x = x2.mult(x,n) + i += two + d = x.div(i,m) + y += d + end + y*two + end + + # Computes the value of pi to the specified number of digits of precision. + def PI(prec) + raise ArgumentError, "Zero or negative argument for PI" if prec <= 0 + n = prec + BigDecimal.double_fig + zero = BigDecimal("0") + one = BigDecimal("1") + two = BigDecimal("2") + + m25 = BigDecimal("-0.04") + m57121 = BigDecimal("-57121") + + pi = zero + + d = one + k = one + w = one + t = BigDecimal("-80") + while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + t = t*m25 + d = t.div(k,m) + k = k+two + pi = pi + d + end + + d = one + k = one + w = one + t = BigDecimal("956") + while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + t = t.div(m57121,n) + d = t.div(k,m) + pi = pi + d + k = k+two + end + pi + end + + # Computes e (the base of natural logarithms) to the specified number of + # digits of precision. + def E(prec) + raise ArgumentError, "Zero or negative precision for E" if prec <= 0 + n = prec + BigDecimal.double_fig + one = BigDecimal("1") + y = one + d = y + z = one + i = 0 + while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + i += 1 + z *= i + d = one.div(z,m) + y += d + end + y + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/newton.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/newton.rb new file mode 100644 index 0000000000..59ac0f7f04 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/newton.rb @@ -0,0 +1,77 @@ +# +# newton.rb +# +# Solves the nonlinear algebraic equation system f = 0 by Newton's method. +# This program is not dependent on BigDecimal. +# +# To call: +# n = nlsolve(f,x) +# where n is the number of iterations required, +# x is the initial value vector +# f is an Object which is used to compute the values of the equations to be solved. +# It must provide the following methods: +# +# f.values(x):: returns the values of all functions at x +# +# f.zero:: returns 0.0 +# f.one:: returns 1.0 +# f.two:: returns 1.0 +# f.ten:: returns 10.0 +# +# f.eps:: returns the convergence criterion (epsilon value) used to determine whether two values are considered equal. If |a-b| < epsilon, the two values are considered equal. +# +# On exit, x is the solution vector. +# +require "bigdecimal/ludcmp" +require "bigdecimal/jacobian" + +module Newton + include LUSolve + include Jacobian + + def norm(fv,zero=0.0) + s = zero + n = fv.size + for i in 0...n do + s += fv[i]*fv[i] + end + s + end + + def nlsolve(f,x) + nRetry = 0 + n = x.size + + f0 = f.values(x) + zero = f.zero + one = f.one + two = f.two + p5 = one/two + d = norm(f0,zero) + minfact = f.ten*f.ten*f.ten + minfact = one/minfact + e = f.eps + while d >= e do + nRetry += 1 + # Not yet converged. => Compute Jacobian matrix + dfdx = jacobian(f,f0,x) + # Solve dfdx*dx = -f0 to estimate dx + dx = lusolve(dfdx,f0,ludecomp(dfdx,n,zero,one),zero) + fact = two + xs = x.dup + begin + fact *= p5 + if fact < minfact then + raise "Failed to reduce function values." + end + for i in 0...n do + x[i] = xs[i] - dx[i]*fact + end + f0 = f.values(x) + dn = norm(f0,zero) + end while(dn>=d) + d = dn + end + nRetry + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/util.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/util.rb new file mode 100644 index 0000000000..09e926acd5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/bigdecimal/util.rb @@ -0,0 +1,65 @@ +# +# BigDecimal utility library. +# +# To use these functions, require 'bigdecimal/util' +# +# The following methods are provided to convert other types to BigDecimals: +# +# String#to_d -> BigDecimal +# Float#to_d -> BigDecimal +# Rational#to_d -> BigDecimal +# +# The following method is provided to convert BigDecimals to other types: +# +# BigDecimal#to_r -> Rational +# +# ---------------------------------------------------------------------- +# +class Float < Numeric + def to_d + BigDecimal(self.to_s) + end +end + +class String + def to_d + BigDecimal(self) + end +end + +class BigDecimal < Numeric + # Converts a BigDecimal to a String of the form "nnnnnn.mmm". + # This method is deprecated; use BigDecimal#to_s("F") instead. + def to_digits + if self.nan? || self.infinite? || self.zero? + self.to_s + else + i = self.to_i.to_s + s,f,y,z = self.frac.split + i + "." + ("0"*(-z)) + f + end + end + + # Converts a BigDecimal to a Rational. + def to_r + sign,digits,base,power = self.split + numerator = sign*digits.to_i + denomi_power = power - digits.size # base is always 10 + if denomi_power < 0 + Rational(numerator,base ** (-denomi_power)) + else + Rational(numerator * (base ** denomi_power),1) + end + end +end + +class Rational < Numeric + # Converts a Rational to a BigDecimal + def to_d(nFig=0) + num = self.numerator.to_s + if nFig<=0 + nFig = BigDecimal.double_fig*2+1 + end + BigDecimal.new(num).div(self.denominator,nFig) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/cgi-lib.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/cgi-lib.rb new file mode 100644 index 0000000000..d6b60d66cc --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/cgi-lib.rb @@ -0,0 +1,272 @@ +warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: cgi-lib is deprecated after Ruby 1.8.1; use cgi instead" + +=begin + += simple CGI support library + += example + +== get form values + + require "cgi-lib.rb" + query = CGI.new + query['field'] # <== value of 'field' + query.keys # <== array of fields + +and query has Hash class methods + + +== get cookie values + + require "cgi-lib.rb" + query = CGI.new + query.cookie['name'] # <== cookie value of 'name' + query.cookie.keys # <== all cookie names + +and query.cookie has Hash class methods + + +== print HTTP header and HTML string to $> + + require "cgi-lib.rb" + CGI::print{ + CGI::tag("HTML"){ + CGI::tag("HEAD"){ CGI::tag("TITLE"){"TITLE"} } + + CGI::tag("BODY"){ + CGI::tag("FORM", {"ACTION"=>"test.rb", "METHOD"=>"POST"}){ + CGI::tag("INPUT", {"TYPE"=>"submit", "VALUE"=>"submit"}) + } + + CGI::tag("HR") + } + } + } + + +== make raw cookie string + + require "cgi-lib.rb" + cookie1 = CGI::cookie({'name' => 'name', + 'value' => 'value', + 'path' => 'path', # optional + 'domain' => 'domain', # optional + 'expires' => Time.now, # optional + 'secure' => true # optional + }) + + CGI::print("Content-Type: text/html", cookie1, cookie2){ "string" } + + +== print HTTP header and string to $> + + require "cgi-lib.rb" + CGI::print{ "string" } + # == CGI::print("Content-Type: text/html"){ "string" } + CGI::print("Content-Type: text/html", cookie1, cookie2){ "string" } + + +=== NPH (no-parse-header) mode + + require "cgi-lib.rb" + CGI::print("nph"){ "string" } + # == CGI::print("nph", "Content-Type: text/html"){ "string" } + CGI::print("nph", "Content-Type: text/html", cookie1, cookie2){ "string" } + + +== make HTML tag string + + require "cgi-lib.rb" + CGI::tag("element", {"attribute_name"=>"attribute_value"}){"content"} + + +== make HTTP header string + + require "cgi-lib.rb" + CGI::header # == CGI::header("Content-Type: text/html") + CGI::header("Content-Type: text/html", cookie1, cookie2) + + +=== NPH (no-parse-header) mode + + CGI::header("nph") # == CGI::header("nph", "Content-Type: text/html") + CGI::header("nph", "Content-Type: text/html", cookie1, cookie2) + + +== escape url encode + + require "cgi-lib.rb" + url_encoded_string = CGI::escape("string") + + +== unescape url encoded + + require "cgi-lib.rb" + string = CGI::unescape("url encoded string") + + +== escape HTML &"<> + + require "cgi-lib.rb" + CGI::escapeHTML("string") + + +=end + +require "delegate" + +class CGI < SimpleDelegator + + CR = "\015" + LF = "\012" + EOL = CR + LF + + RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ] + RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ] + + # make rfc1123 date string + def CGI::rfc1123_date(time) + t = time.clone.gmtime + return format("%s, %.2d %s %d %.2d:%.2d:%.2d GMT", + RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year, + t.hour, t.min, t.sec) + end + + # escape url encode + def CGI::escape(str) + str.gsub(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) } + end + + # unescape url encoded + def CGI::unescape(str) + str.gsub(/\+/, ' ').gsub(/%([0-9a-fA-F]{2})/){ [$1.hex].pack("c") } + end + + # escape HTML + def CGI::escapeHTML(str) + str.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/" + + (iterator? ? yield.to_s + "" : "") + end + + # make raw cookie string + def CGI::cookie(options) + "Set-Cookie: " + options['name'] + '=' + escape(options['value']) + + (options['domain'] ? '; domain=' + options['domain'] : '') + + (options['path'] ? '; path=' + options['path'] : '') + + (options['expires'] ? '; expires=' + rfc1123_date(options['expires']) : '') + + (options['secure'] ? '; secure' : '') + end + + # make HTTP header string + def CGI::header(*options) + if defined?(MOD_RUBY) + options.each{|option| + option.sub(/(.*?): (.*)/){ + Apache::request.headers_out[$1] = $2 + } + } + Apache::request.send_http_header + '' + else + if options.delete("nph") or (ENV['SERVER_SOFTWARE'] =~ /IIS/) + [(ENV['SERVER_PROTOCOL'] or "HTTP/1.0") + " 200 OK", + "Date: " + rfc1123_date(Time.now), + "Server: " + (ENV['SERVER_SOFTWARE'] or ""), + "Connection: close"] + + (options.empty? ? ["Content-Type: text/html"] : options) + else + options.empty? ? ["Content-Type: text/html"] : options + end.join(EOL) + EOL + EOL + end + end + + # print HTTP header and string to $> + def CGI::print(*options) + $>.print CGI::header(*options) + yield.to_s + end + + # print message to $> + def CGI::message(message, title = "", header = ["Content-Type: text/html"]) + if message.kind_of?(Hash) + title = message['title'] + header = message['header'] + message = message['body'] + end + CGI::print(*header){ + CGI::tag("HTML"){ + CGI::tag("HEAD"){ CGI.tag("TITLE"){ title } } + + CGI::tag("BODY"){ message } + } + } + true + end + + # print error message to $> and exit + def CGI::error + CGI::message({'title'=>'ERROR', 'body'=> + CGI::tag("PRE"){ + "ERROR: " + CGI::tag("STRONG"){ escapeHTML($!.to_s) } + "\n" + escapeHTML($@.join("\n")) + } + }) + exit + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/cgi.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/cgi.rb new file mode 100644 index 0000000000..d096f907e7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/cgi.rb @@ -0,0 +1,2303 @@ +# +# cgi.rb - cgi support library +# +# Copyright (C) 2000 Network Applied Communication Laboratory, Inc. +# +# Copyright (C) 2000 Information-technology Promotion Agency, Japan +# +# Author: Wakou Aoyama +# +# Documentation: Wakou Aoyama (RDoc'd and embellished by William Webber) +# +# == Overview +# +# The Common Gateway Interface (CGI) is a simple protocol +# for passing an HTTP request from a web server to a +# standalone program, and returning the output to the web +# browser. Basically, a CGI program is called with the +# parameters of the request passed in either in the +# environment (GET) or via $stdin (POST), and everything +# it prints to $stdout is returned to the client. +# +# This file holds the +CGI+ class. This class provides +# functionality for retrieving HTTP request parameters, +# managing cookies, and generating HTML output. See the +# class documentation for more details and examples of use. +# +# The file cgi/session.rb provides session management +# functionality; see that file for more details. +# +# See http://www.w3.org/CGI/ for more information on the CGI +# protocol. + +raise "Please, use ruby 1.5.4 or later." if RUBY_VERSION < "1.5.4" + +require 'English' + +# CGI class. See documentation for the file cgi.rb for an overview +# of the CGI protocol. +# +# == Introduction +# +# CGI is a large class, providing several categories of methods, many of which +# are mixed in from other modules. Some of the documentation is in this class, +# some in the modules CGI::QueryExtension and CGI::HtmlExtension. See +# CGI::Cookie for specific information on handling cookies, and cgi/session.rb +# (CGI::Session) for information on sessions. +# +# For queries, CGI provides methods to get at environmental variables, +# parameters, cookies, and multipart request data. For responses, CGI provides +# methods for writing output and generating HTML. +# +# Read on for more details. Examples are provided at the bottom. +# +# == Queries +# +# The CGI class dynamically mixes in parameter and cookie-parsing +# functionality, environmental variable access, and support for +# parsing multipart requests (including uploaded files) from the +# CGI::QueryExtension module. +# +# === Environmental Variables +# +# The standard CGI environmental variables are available as read-only +# attributes of a CGI object. The following is a list of these variables: +# +# +# AUTH_TYPE HTTP_HOST REMOTE_IDENT +# CONTENT_LENGTH HTTP_NEGOTIATE REMOTE_USER +# CONTENT_TYPE HTTP_PRAGMA REQUEST_METHOD +# GATEWAY_INTERFACE HTTP_REFERER SCRIPT_NAME +# HTTP_ACCEPT HTTP_USER_AGENT SERVER_NAME +# HTTP_ACCEPT_CHARSET PATH_INFO SERVER_PORT +# HTTP_ACCEPT_ENCODING PATH_TRANSLATED SERVER_PROTOCOL +# HTTP_ACCEPT_LANGUAGE QUERY_STRING SERVER_SOFTWARE +# HTTP_CACHE_CONTROL REMOTE_ADDR +# HTTP_FROM REMOTE_HOST +# +# +# For each of these variables, there is a corresponding attribute with the +# same name, except all lower case and without a preceding HTTP_. +# +content_length+ and +server_port+ are integers; the rest are strings. +# +# === Parameters +# +# The method #params() returns a hash of all parameters in the request as +# name/value-list pairs, where the value-list is an Array of one or more +# values. The CGI object itself also behaves as a hash of parameter names +# to values, but only returns a single value (as a String) for each +# parameter name. +# +# For instance, suppose the request contains the parameter +# "favourite_colours" with the multiple values "blue" and "green". The +# following behaviour would occur: +# +# cgi.params["favourite_colours"] # => ["blue", "green"] +# cgi["favourite_colours"] # => "blue" +# +# If a parameter does not exist, the former method will return an empty +# array, the latter an empty string. The simplest way to test for existence +# of a parameter is by the #has_key? method. +# +# === Cookies +# +# HTTP Cookies are automatically parsed from the request. They are available +# from the #cookies() accessor, which returns a hash from cookie name to +# CGI::Cookie object. +# +# === Multipart requests +# +# If a request's method is POST and its content type is multipart/form-data, +# then it may contain uploaded files. These are stored by the QueryExtension +# module in the parameters of the request. The parameter name is the name +# attribute of the file input field, as usual. However, the value is not +# a string, but an IO object, either an IOString for small files, or a +# Tempfile for larger ones. This object also has the additional singleton +# methods: +# +# #local_path():: the path of the uploaded file on the local filesystem +# #original_filename():: the name of the file on the client computer +# #content_type():: the content type of the file +# +# == Responses +# +# The CGI class provides methods for sending header and content output to +# the HTTP client, and mixes in methods for programmatic HTML generation +# from CGI::HtmlExtension and CGI::TagMaker modules. The precise version of HTML +# to use for HTML generation is specified at object creation time. +# +# === Writing output +# +# The simplest way to send output to the HTTP client is using the #out() method. +# This takes the HTTP headers as a hash parameter, and the body content +# via a block. The headers can be generated as a string using the #header() +# method. The output stream can be written directly to using the #print() +# method. +# +# === Generating HTML +# +# Each HTML element has a corresponding method for generating that +# element as a String. The name of this method is the same as that +# of the element, all lowercase. The attributes of the element are +# passed in as a hash, and the body as a no-argument block that evaluates +# to a String. The HTML generation module knows which elements are +# always empty, and silently drops any passed-in body. It also knows +# which elements require matching closing tags and which don't. However, +# it does not know what attributes are legal for which elements. +# +# There are also some additional HTML generation methods mixed in from +# the CGI::HtmlExtension module. These include individual methods for the +# different types of form inputs, and methods for elements that commonly +# take particular attributes where the attributes can be directly specified +# as arguments, rather than via a hash. +# +# == Examples of use +# +# === Get form values +# +# require "cgi" +# cgi = CGI.new +# value = cgi['field_name'] # <== value string for 'field_name' +# # if not 'field_name' included, then return "". +# fields = cgi.keys # <== array of field names +# +# # returns true if form has 'field_name' +# cgi.has_key?('field_name') +# cgi.has_key?('field_name') +# cgi.include?('field_name') +# +# CAUTION! cgi['field_name'] returned an Array with the old +# cgi.rb(included in ruby 1.6) +# +# === Get form values as hash +# +# require "cgi" +# cgi = CGI.new +# params = cgi.params +# +# cgi.params is a hash. +# +# cgi.params['new_field_name'] = ["value"] # add new param +# cgi.params['field_name'] = ["new_value"] # change value +# cgi.params.delete('field_name') # delete param +# cgi.params.clear # delete all params +# +# +# === Save form values to file +# +# require "pstore" +# db = PStore.new("query.db") +# db.transaction do +# db["params"] = cgi.params +# end +# +# +# === Restore form values from file +# +# require "pstore" +# db = PStore.new("query.db") +# db.transaction do +# cgi.params = db["params"] +# end +# +# +# === Get multipart form values +# +# require "cgi" +# cgi = CGI.new +# value = cgi['field_name'] # <== value string for 'field_name' +# value.read # <== body of value +# value.local_path # <== path to local file of value +# value.original_filename # <== original filename of value +# value.content_type # <== content_type of value +# +# and value has StringIO or Tempfile class methods. +# +# === Get cookie values +# +# require "cgi" +# cgi = CGI.new +# values = cgi.cookies['name'] # <== array of 'name' +# # if not 'name' included, then return []. +# names = cgi.cookies.keys # <== array of cookie names +# +# and cgi.cookies is a hash. +# +# === Get cookie objects +# +# require "cgi" +# cgi = CGI.new +# for name, cookie in cgi.cookies +# cookie.expires = Time.now + 30 +# end +# cgi.out("cookie" => cgi.cookies) {"string"} +# +# cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... } +# +# require "cgi" +# cgi = CGI.new +# cgi.cookies['name'].expires = Time.now + 30 +# cgi.out("cookie" => cgi.cookies['name']) {"string"} +# +# === Print http header and html string to $DEFAULT_OUTPUT ($>) +# +# require "cgi" +# cgi = CGI.new("html3") # add HTML generation methods +# cgi.out() do +# cgi.html() do +# cgi.head{ cgi.title{"TITLE"} } + +# cgi.body() do +# cgi.form() do +# cgi.textarea("get_text") + +# cgi.br + +# cgi.submit +# end + +# cgi.pre() do +# CGI::escapeHTML( +# "params: " + cgi.params.inspect + "\n" + +# "cookies: " + cgi.cookies.inspect + "\n" + +# ENV.collect() do |key, value| +# key + " --> " + value + "\n" +# end.join("") +# ) +# end +# end +# end +# end +# +# # add HTML generation methods +# CGI.new("html3") # html3.2 +# CGI.new("html4") # html4.01 (Strict) +# CGI.new("html4Tr") # html4.01 Transitional +# CGI.new("html4Fr") # html4.01 Frameset +# +class CGI + + # :stopdoc: + + # String for carriage return + CR = "\015" + + # String for linefeed + LF = "\012" + + # Standard internet newline sequence + EOL = CR + LF + + REVISION = '$Id: cgi.rb 12340 2007-05-22 21:58:09Z shyouhei $' #:nodoc: + + NEEDS_BINMODE = true if /WIN/ni.match(RUBY_PLATFORM) + + # Path separators in different environments. + PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'} + + # HTTP status codes. + HTTP_STATUS = { + "OK" => "200 OK", + "PARTIAL_CONTENT" => "206 Partial Content", + "MULTIPLE_CHOICES" => "300 Multiple Choices", + "MOVED" => "301 Moved Permanently", + "REDIRECT" => "302 Found", + "NOT_MODIFIED" => "304 Not Modified", + "BAD_REQUEST" => "400 Bad Request", + "AUTH_REQUIRED" => "401 Authorization Required", + "FORBIDDEN" => "403 Forbidden", + "NOT_FOUND" => "404 Not Found", + "METHOD_NOT_ALLOWED" => "405 Method Not Allowed", + "NOT_ACCEPTABLE" => "406 Not Acceptable", + "LENGTH_REQUIRED" => "411 Length Required", + "PRECONDITION_FAILED" => "412 Rrecondition Failed", + "SERVER_ERROR" => "500 Internal Server Error", + "NOT_IMPLEMENTED" => "501 Method Not Implemented", + "BAD_GATEWAY" => "502 Bad Gateway", + "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates" + } + + # Abbreviated day-of-week names specified by RFC 822 + RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ] + + # Abbreviated month names specified by RFC 822 + RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ] + + # :startdoc: + + def env_table + ENV + end + + def stdinput + $stdin + end + + def stdoutput + $DEFAULT_OUTPUT + end + + private :env_table, :stdinput, :stdoutput + + # URL-encode a string. + # url_encoded_string = CGI::escape("'Stop!' said Fred") + # # => "%27Stop%21%27+said+Fred" + def CGI::escape(string) + string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do + '%' + $1.unpack('H2' * $1.size).join('%').upcase + end.tr(' ', '+') + end + + + # URL-decode a string. + # string = CGI::unescape("%27Stop%21%27+said+Fred") + # # => "'Stop!' said Fred" + def CGI::unescape(string) + string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do + [$1.delete('%')].pack('H*') + end + end + + + # Escape special characters in HTML, namely &\"<> + # CGI::escapeHTML('Usage: foo "bar" ') + # # => "Usage: foo "bar" <baz>" + def CGI::escapeHTML(string) + string.gsub(/&/n, '&').gsub(/\"/n, '"').gsub(/>/n, '>').gsub(/ "Usage: foo \"bar\" " + def CGI::unescapeHTML(string) + string.gsub(/&(amp|quot|gt|lt|\#[0-9]+|\#x[0-9A-Fa-f]+);/n) do + match = $1.dup + case match + when 'amp' then '&' + when 'quot' then '"' + when 'gt' then '>' + when 'lt' then '<' + when /\A#0*(\d+)\z/n then + if Integer($1) < 256 + Integer($1).chr + else + if Integer($1) < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U) + [Integer($1)].pack("U") + else + "&##{$1};" + end + end + when /\A#x([0-9a-f]+)\z/ni then + if $1.hex < 256 + $1.hex.chr + else + if $1.hex < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U) + [$1.hex].pack("U") + else + "&#x#{$1};" + end + end + else + "&#{match};" + end + end + end + + + # Escape only the tags of certain HTML elements in +string+. + # + # Takes an element or elements or array of elements. Each element + # is specified by the name of the element, without angle brackets. + # This matches both the start and the end tag of that element. + # The attribute list of the open tag will also be escaped (for + # instance, the double-quotes surrounding attribute values). + # + # print CGI::escapeElement('
', "A", "IMG") + # # "
<A HREF="url"></A>" + # + # print CGI::escapeElement('
', ["A", "IMG"]) + # # "
<A HREF="url"></A>" + def CGI::escapeElement(string, *elements) + elements = elements[0] if elements[0].kind_of?(Array) + unless elements.empty? + string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do + CGI::escapeHTML($&) + end + else + string + end + end + + + # Undo escaping such as that done by CGI::escapeElement() + # + # print CGI::unescapeElement( + # CGI::escapeHTML('
'), "A", "IMG") + # # "<BR>" + # + # print CGI::unescapeElement( + # CGI::escapeHTML('
'), ["A", "IMG"]) + # # "<BR>" + def CGI::unescapeElement(string, *elements) + elements = elements[0] if elements[0].kind_of?(Array) + unless elements.empty? + string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do + CGI::unescapeHTML($&) + end + else + string + end + end + + + # Format a +Time+ object as a String using the format specified by RFC 1123. + # + # CGI::rfc1123_date(Time.now) + # # Sat, 01 Jan 2000 00:00:00 GMT + def CGI::rfc1123_date(time) + t = time.clone.gmtime + return format("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", + RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year, + t.hour, t.min, t.sec) + end + + + # Create an HTTP header block as a string. + # + # Includes the empty line that ends the header block. + # + # +options+ can be a string specifying the Content-Type (defaults + # to text/html), or a hash of header key/value pairs. The following + # header keys are recognized: + # + # type:: the Content-Type header. Defaults to "text/html" + # charset:: the charset of the body, appended to the Content-Type header. + # nph:: a boolean value. If true, prepend protocol string and status code, and + # date; and sets default values for "server" and "connection" if not + # explicitly set. + # status:: the HTTP status code, returned as the Status header. See the + # list of available status codes below. + # server:: the server software, returned as the Server header. + # connection:: the connection type, returned as the Connection header (for + # instance, "close". + # length:: the length of the content that will be sent, returned as the + # Content-Length header. + # language:: the language of the content, returned as the Content-Language + # header. + # expires:: the time on which the current content expires, as a +Time+ + # object, returned as the Expires header. + # cookie:: a cookie or cookies, returned as one or more Set-Cookie headers. + # The value can be the literal string of the cookie; a CGI::Cookie + # object; an Array of literal cookie strings or Cookie objects; or a + # hash all of whose values are literal cookie strings or Cookie objects. + # These cookies are in addition to the cookies held in the + # @output_cookies field. + # + # Other header lines can also be set; they are appended as key: value. + # + # header + # # Content-Type: text/html + # + # header("text/plain") + # # Content-Type: text/plain + # + # header("nph" => true, + # "status" => "OK", # == "200 OK" + # # "status" => "200 GOOD", + # "server" => ENV['SERVER_SOFTWARE'], + # "connection" => "close", + # "type" => "text/html", + # "charset" => "iso-2022-jp", + # # Content-Type: text/html; charset=iso-2022-jp + # "length" => 103, + # "language" => "ja", + # "expires" => Time.now + 30, + # "cookie" => [cookie1, cookie2], + # "my_header1" => "my_value" + # "my_header2" => "my_value") + # + # The status codes are: + # + # "OK" --> "200 OK" + # "PARTIAL_CONTENT" --> "206 Partial Content" + # "MULTIPLE_CHOICES" --> "300 Multiple Choices" + # "MOVED" --> "301 Moved Permanently" + # "REDIRECT" --> "302 Found" + # "NOT_MODIFIED" --> "304 Not Modified" + # "BAD_REQUEST" --> "400 Bad Request" + # "AUTH_REQUIRED" --> "401 Authorization Required" + # "FORBIDDEN" --> "403 Forbidden" + # "NOT_FOUND" --> "404 Not Found" + # "METHOD_NOT_ALLOWED" --> "405 Method Not Allowed" + # "NOT_ACCEPTABLE" --> "406 Not Acceptable" + # "LENGTH_REQUIRED" --> "411 Length Required" + # "PRECONDITION_FAILED" --> "412 Precondition Failed" + # "SERVER_ERROR" --> "500 Internal Server Error" + # "NOT_IMPLEMENTED" --> "501 Method Not Implemented" + # "BAD_GATEWAY" --> "502 Bad Gateway" + # "VARIANT_ALSO_VARIES" --> "506 Variant Also Negotiates" + # + # This method does not perform charset conversion. + # + def header(options = "text/html") + + buf = "" + + case options + when String + options = { "type" => options } + when Hash + options = options.dup + end + + unless options.has_key?("type") + options["type"] = "text/html" + end + + if options.has_key?("charset") + options["type"] += "; charset=" + options.delete("charset") + end + + options.delete("nph") if defined?(MOD_RUBY) + if options.delete("nph") or + (/IIS\/(\d+)/n.match(env_table['SERVER_SOFTWARE']) and $1.to_i < 5) + buf += (env_table["SERVER_PROTOCOL"] or "HTTP/1.0") + " " + + (HTTP_STATUS[options["status"]] or options["status"] or "200 OK") + + EOL + + "Date: " + CGI::rfc1123_date(Time.now) + EOL + + unless options.has_key?("server") + options["server"] = (env_table['SERVER_SOFTWARE'] or "") + end + + unless options.has_key?("connection") + options["connection"] = "close" + end + + options.delete("status") + end + + if options.has_key?("status") + buf += "Status: " + + (HTTP_STATUS[options["status"]] or options["status"]) + EOL + options.delete("status") + end + + if options.has_key?("server") + buf += "Server: " + options.delete("server") + EOL + end + + if options.has_key?("connection") + buf += "Connection: " + options.delete("connection") + EOL + end + + buf += "Content-Type: " + options.delete("type") + EOL + + if options.has_key?("length") + buf += "Content-Length: " + options.delete("length").to_s + EOL + end + + if options.has_key?("language") + buf += "Content-Language: " + options.delete("language") + EOL + end + + if options.has_key?("expires") + buf += "Expires: " + CGI::rfc1123_date( options.delete("expires") ) + EOL + end + + if options.has_key?("cookie") + if options["cookie"].kind_of?(String) or + options["cookie"].kind_of?(Cookie) + buf += "Set-Cookie: " + options.delete("cookie").to_s + EOL + elsif options["cookie"].kind_of?(Array) + options.delete("cookie").each{|cookie| + buf += "Set-Cookie: " + cookie.to_s + EOL + } + elsif options["cookie"].kind_of?(Hash) + options.delete("cookie").each_value{|cookie| + buf += "Set-Cookie: " + cookie.to_s + EOL + } + end + end + if @output_cookies + for cookie in @output_cookies + buf += "Set-Cookie: " + cookie.to_s + EOL + end + end + + options.each{|key, value| + buf += key + ": " + value.to_s + EOL + } + + if defined?(MOD_RUBY) + table = Apache::request.headers_out + buf.scan(/([^:]+): (.+)#{EOL}/n){ |name, value| + warn sprintf("name:%s value:%s\n", name, value) if $DEBUG + case name + when 'Set-Cookie' + table.add(name, value) + when /^status$/ni + Apache::request.status_line = value + Apache::request.status = value.to_i + when /^content-type$/ni + Apache::request.content_type = value + when /^content-encoding$/ni + Apache::request.content_encoding = value + when /^location$/ni + if Apache::request.status == 200 + Apache::request.status = 302 + end + Apache::request.headers_out[name] = value + else + Apache::request.headers_out[name] = value + end + } + Apache::request.send_http_header + '' + else + buf + EOL + end + + end # header() + + + # Print an HTTP header and body to $DEFAULT_OUTPUT ($>) + # + # The header is provided by +options+, as for #header(). + # The body of the document is that returned by the passed- + # in block. This block takes no arguments. It is required. + # + # cgi = CGI.new + # cgi.out{ "string" } + # # Content-Type: text/html + # # Content-Length: 6 + # # + # # string + # + # cgi.out("text/plain") { "string" } + # # Content-Type: text/plain + # # Content-Length: 6 + # # + # # string + # + # cgi.out("nph" => true, + # "status" => "OK", # == "200 OK" + # "server" => ENV['SERVER_SOFTWARE'], + # "connection" => "close", + # "type" => "text/html", + # "charset" => "iso-2022-jp", + # # Content-Type: text/html; charset=iso-2022-jp + # "language" => "ja", + # "expires" => Time.now + (3600 * 24 * 30), + # "cookie" => [cookie1, cookie2], + # "my_header1" => "my_value", + # "my_header2" => "my_value") { "string" } + # + # Content-Length is automatically calculated from the size of + # the String returned by the content block. + # + # If ENV['REQUEST_METHOD'] == "HEAD", then only the header + # is outputted (the content block is still required, but it + # is ignored). + # + # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then + # the content is converted to this charset, and the language is set + # to "ja". + def out(options = "text/html") # :yield: + + options = { "type" => options } if options.kind_of?(String) + content = yield + + if options.has_key?("charset") + require "nkf" + case options["charset"] + when /iso-2022-jp/ni + content = NKF::nkf('-m0 -x -j', content) + options["language"] = "ja" unless options.has_key?("language") + when /euc-jp/ni + content = NKF::nkf('-m0 -x -e', content) + options["language"] = "ja" unless options.has_key?("language") + when /shift_jis/ni + content = NKF::nkf('-m0 -x -s', content) + options["language"] = "ja" unless options.has_key?("language") + end + end + + options["length"] = content.length.to_s + output = stdoutput + output.binmode if defined? output.binmode + output.print header(options) + output.print content unless "HEAD" == env_table['REQUEST_METHOD'] + end + + + # Print an argument or list of arguments to the default output stream + # + # cgi = CGI.new + # cgi.print # default: cgi.print == $DEFAULT_OUTPUT.print + def print(*options) + stdoutput.print(*options) + end + + require "delegate" + + # Class representing an HTTP cookie. + # + # In addition to its specific fields and methods, a Cookie instance + # is a delegator to the array of its values. + # + # See RFC 2965. + # + # == Examples of use + # cookie1 = CGI::Cookie::new("name", "value1", "value2", ...) + # cookie1 = CGI::Cookie::new("name" => "name", "value" => "value") + # cookie1 = CGI::Cookie::new('name' => 'name', + # 'value' => ['value1', 'value2', ...], + # 'path' => 'path', # optional + # 'domain' => 'domain', # optional + # 'expires' => Time.now, # optional + # 'secure' => true # optional + # ) + # + # cgi.out("cookie" => [cookie1, cookie2]) { "string" } + # + # name = cookie1.name + # values = cookie1.value + # path = cookie1.path + # domain = cookie1.domain + # expires = cookie1.expires + # secure = cookie1.secure + # + # cookie1.name = 'name' + # cookie1.value = ['value1', 'value2', ...] + # cookie1.path = 'path' + # cookie1.domain = 'domain' + # cookie1.expires = Time.now + 30 + # cookie1.secure = true + class Cookie < DelegateClass(Array) + + # Create a new CGI::Cookie object. + # + # The contents of the cookie can be specified as a +name+ and one + # or more +value+ arguments. Alternatively, the contents can + # be specified as a single hash argument. The possible keywords of + # this hash are as follows: + # + # name:: the name of the cookie. Required. + # value:: the cookie's value or list of values. + # path:: the path for which this cookie applies. Defaults to the + # base directory of the CGI script. + # domain:: the domain for which this cookie applies. + # expires:: the time at which this cookie expires, as a +Time+ object. + # secure:: whether this cookie is a secure cookie or not (default to + # false). Secure cookies are only transmitted to HTTPS + # servers. + # + # These keywords correspond to attributes of the cookie object. + def initialize(name = "", *value) + options = if name.kind_of?(String) + { "name" => name, "value" => value } + else + name + end + unless options.has_key?("name") + raise ArgumentError, "`name' required" + end + + @name = options["name"] + @value = Array(options["value"]) + # simple support for IE + if options["path"] + @path = options["path"] + else + %r|^(.*/)|.match(ENV["SCRIPT_NAME"]) + @path = ($1 or "") + end + @domain = options["domain"] + @expires = options["expires"] + @secure = options["secure"] == true ? true : false + + super(@value) + end + + attr_accessor("name", "value", "path", "domain", "expires") + attr_reader("secure") + + # Set whether the Cookie is a secure cookie or not. + # + # +val+ must be a boolean. + def secure=(val) + @secure = val if val == true or val == false + @secure + end + + # Convert the Cookie to its string representation. + def to_s + buf = "" + buf += @name + '=' + + if @value.kind_of?(String) + buf += CGI::escape(@value) + else + buf += @value.collect{|v| CGI::escape(v) }.join("&") + end + + if @domain + buf += '; domain=' + @domain + end + + if @path + buf += '; path=' + @path + end + + if @expires + buf += '; expires=' + CGI::rfc1123_date(@expires) + end + + if @secure == true + buf += '; secure' + end + + buf + end + + end # class Cookie + + + # Parse a raw cookie string into a hash of cookie-name=>Cookie + # pairs. + # + # cookies = CGI::Cookie::parse("raw_cookie_string") + # # { "name1" => cookie1, "name2" => cookie2, ... } + # + def Cookie::parse(raw_cookie) + cookies = Hash.new([]) + return cookies unless raw_cookie + + raw_cookie.split(/[;,]\s?/).each do |pairs| + name, values = pairs.split('=',2) + next unless name and values + name = CGI::unescape(name) + values ||= "" + values = values.split('&').collect{|v| CGI::unescape(v) } + if cookies.has_key?(name) + values = cookies[name].value + values + end + cookies[name] = Cookie::new({ "name" => name, "value" => values }) + end + + cookies + end + + # Parse an HTTP query string into a hash of key=>value pairs. + # + # params = CGI::parse("query_string") + # # {"name1" => ["value1", "value2", ...], + # # "name2" => ["value1", "value2", ...], ... } + # + def CGI::parse(query) + params = Hash.new([].freeze) + + query.split(/[&;]/n).each do |pairs| + key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) } + if params.has_key?(key) + params[key].push(value) + else + params[key] = [value] + end + end + + params + end + + # Mixin module. It provides the follow functionality groups: + # + # 1. Access to CGI environment variables as methods. See + # documentation to the CGI class for a list of these variables. + # + # 2. Access to cookies, including the cookies attribute. + # + # 3. Access to parameters, including the params attribute, and overloading + # [] to perform parameter value lookup by key. + # + # 4. The initialize_query method, for initialising the above + # mechanisms, handling multipart forms, and allowing the + # class to be used in "offline" mode. + # + module QueryExtension + + %w[ CONTENT_LENGTH SERVER_PORT ].each do |env| + define_method(env.sub(/^HTTP_/n, '').downcase) do + (val = env_table[env]) && Integer(val) + end + end + + %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO + PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST + REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME + SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE + + HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING + HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST + HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env| + define_method(env.sub(/^HTTP_/n, '').downcase) do + env_table[env] + end + end + + # Get the raw cookies as a string. + def raw_cookie + env_table["HTTP_COOKIE"] + end + + # Get the raw RFC2965 cookies as a string. + def raw_cookie2 + env_table["HTTP_COOKIE2"] + end + + # Get the cookies as a hash of cookie-name=>Cookie pairs. + attr_accessor("cookies") + + # Get the parameters as a hash of name=>values pairs, where + # values is an Array. + attr("params") + + # Set all the parameters. + def params=(hash) + @params.clear + @params.update(hash) + end + + def read_multipart(boundary, content_length) + params = Hash.new([]) + boundary = "--" + boundary + quoted_boundary = Regexp.quote(boundary, "n") + buf = "" + bufsize = 10 * 1024 + boundary_end="" + + # start multipart/form-data + stdinput.binmode if defined? stdinput.binmode + boundary_size = boundary.size + EOL.size + content_length -= boundary_size + status = stdinput.read(boundary_size) + if nil == status + raise EOFError, "no content body" + elsif boundary + EOL != status + raise EOFError, "bad content body" + end + + loop do + head = nil + if 10240 < content_length + require "tempfile" + body = Tempfile.new("CGI") + else + begin + require "stringio" + body = StringIO.new + rescue LoadError + require "tempfile" + body = Tempfile.new("CGI") + end + end + body.binmode if defined? body.binmode + + until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf) + + if (not head) and /#{EOL}#{EOL}/n.match(buf) + buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do + head = $1.dup + "" + end + next + end + + if head and ( (EOL + boundary + EOL).size < buf.size ) + body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)] + buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = "" + end + + c = if bufsize < content_length + stdinput.read(bufsize) + else + stdinput.read(content_length) + end + if c.nil? || c.empty? + raise EOFError, "bad content body" + end + buf.concat(c) + content_length -= c.size + end + + buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do + body.print $1 + if "--" == $2 + content_length = -1 + end + boundary_end = $2.dup + "" + end + + body.rewind + + /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni.match(head) + filename = ($1 or $2 or "") + if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and + /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and + (not /MSIE/ni.match(env_table['HTTP_USER_AGENT'])) + filename = CGI::unescape(filename) + end + + /Content-Type: (.*)/ni.match(head) + content_type = ($1 or "") + + (class << body; self; end).class_eval do + alias local_path path + define_method(:original_filename) {filename.dup.taint} + define_method(:content_type) {content_type.dup.taint} + end + + /Content-Disposition:.* name="?([^\";]*)"?/ni.match(head) + name = $1.dup + + if params.has_key?(name) + params[name].push(body) + else + params[name] = [body] + end + break if buf.size == 0 + break if content_length == -1 + end + raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/ + + params + end # read_multipart + private :read_multipart + + # offline mode. read name=value pairs on standard input. + def read_from_cmdline + require "shellwords" + + string = unless ARGV.empty? + ARGV.join(' ') + else + if STDIN.tty? + STDERR.print( + %|(offline mode: enter name=value pairs on standard input)\n| + ) + end + readlines.join(' ').gsub(/\n/n, '') + end.gsub(/\\=/n, '%3D').gsub(/\\&/n, '%26') + + words = Shellwords.shellwords(string) + + if words.find{|x| /=/n.match(x) } + words.join('&') + else + words.join('+') + end + end + private :read_from_cmdline + + # Initialize the data from the query. + # + # Handles multipart forms (in particular, forms that involve file uploads). + # Reads query parameters in the @params field, and cookies into @cookies. + def initialize_query() + if ("POST" == env_table['REQUEST_METHOD']) and + %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(env_table['CONTENT_TYPE']) + boundary = $1.dup + @multipart = true + @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH'])) + else + @multipart = false + @params = CGI::parse( + case env_table['REQUEST_METHOD'] + when "GET", "HEAD" + if defined?(MOD_RUBY) + Apache::request.args or "" + else + env_table['QUERY_STRING'] or "" + end + when "POST" + stdinput.binmode if defined? stdinput.binmode + stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or '' + else + read_from_cmdline + end + ) + end + + @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE'])) + end + private :initialize_query + + def multipart? + @multipart + end + + module Value # :nodoc: + def set_params(params) + @params = params + end + def [](idx, *args) + if args.size == 0 + warn "#{caller(1)[0]}:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array, use cgi.params['key']" + @params[idx] + else + super[idx,*args] + end + end + def first + warn "#{caller(1)[0]}:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array, use cgi.params['key']" + self + end + alias last first + def to_a + @params || [self] + end + alias to_ary to_a # to be rhs of multiple assignment + end + + # Get the value for the parameter with a given key. + # + # If the parameter has multiple values, only the first will be + # retrieved; use #params() to get the array of values. + def [](key) + params = @params[key] + return '' unless params + value = params[0] + if @multipart + if value + return value + elsif defined? StringIO + StringIO.new("") + else + Tempfile.new("CGI") + end + else + str = if value then value.dup else "" end + str.extend(Value) + str.set_params(params) + str + end + end + + # Return all parameter keys as an array. + def keys(*args) + @params.keys(*args) + end + + # Returns true if a given parameter key exists in the query. + def has_key?(*args) + @params.has_key?(*args) + end + alias key? has_key? + alias include? has_key? + + end # QueryExtension + + + # Prettify (indent) an HTML string. + # + # +string+ is the HTML string to indent. +shift+ is the indentation + # unit to use; it defaults to two spaces. + # + # print CGI::pretty("") + # # + # # + # # + # # + # + # print CGI::pretty("", "\t") + # # + # # + # # + # # + # + def CGI::pretty(string, shift = " ") + lines = string.gsub(/(?!\A)<(?:.|\n)*?>/n, "\n\\0").gsub(/<(?:.|\n)*?>(?!\n)/n, "\\0\n") + end_pos = 0 + while end_pos = lines.index(/^<\/(\w+)/n, end_pos) + element = $1.dup + start_pos = lines.rindex(/^\s*<#{element}/ni, end_pos) + lines[start_pos ... end_pos] = "__" + lines[start_pos ... end_pos].gsub(/\n(?!\z)/n, "\n" + shift) + "__" + end + lines.gsub(/^((?:#{Regexp::quote(shift)})*)__(?=<\/?\w)/n, '\1') + end + + + # Base module for HTML-generation mixins. + # + # Provides methods for code generation for tags following + # the various DTD element types. + module TagMaker # :nodoc: + + # Generate code for an element with required start and end tags. + # + # - - + def nn_element_def(element) + nOE_element_def(element, <<-END) + if block_given? + yield.to_s + else + "" + end + + "" + END + end + + # Generate code for an empty element. + # + # - O EMPTY + def nOE_element_def(element, append = nil) + s = <<-END + "<#{element.upcase}" + attributes.collect{|name, value| + next unless value + " " + CGI::escapeHTML(name) + + if true == value + "" + else + '="' + CGI::escapeHTML(value) + '"' + end + }.to_s + ">" + END + s.sub!(/\Z/, " +") << append if append + s + end + + # Generate code for an element for which the end (and possibly the + # start) tag is optional. + # + # O O or - O + def nO_element_def(element) + nOE_element_def(element, <<-END) + if block_given? + yield.to_s + "" + else + "" + end + END + end + + end # TagMaker + + + # + # Mixin module providing HTML generation methods. + # + # For example, + # cgi.a("http://www.example.com") { "Example" } + # # => "Example" + # + # Modules Http3, Http4, etc., contain more basic HTML-generation methods + # (:title, :center, etc.). + # + # See class CGI for a detailed example. + # + module HtmlExtension + + + # Generate an Anchor element as a string. + # + # +href+ can either be a string, giving the URL + # for the HREF attribute, or it can be a hash of + # the element's attributes. + # + # The body of the element is the string returned by the no-argument + # block passed in. + # + # a("http://www.example.com") { "Example" } + # # => "Example" + # + # a("HREF" => "http://www.example.com", "TARGET" => "_top") { "Example" } + # # => "Example" + # + def a(href = "") # :yield: + attributes = if href.kind_of?(String) + { "HREF" => href } + else + href + end + if block_given? + super(attributes){ yield } + else + super(attributes) + end + end + + # Generate a Document Base URI element as a String. + # + # +href+ can either by a string, giving the base URL for the HREF + # attribute, or it can be a has of the element's attributes. + # + # The passed-in no-argument block is ignored. + # + # base("http://www.example.com/cgi") + # # => "" + def base(href = "") # :yield: + attributes = if href.kind_of?(String) + { "HREF" => href } + else + href + end + if block_given? + super(attributes){ yield } + else + super(attributes) + end + end + + # Generate a BlockQuote element as a string. + # + # +cite+ can either be a string, give the URI for the source of + # the quoted text, or a hash, giving all attributes of the element, + # or it can be omitted, in which case the element has no attributes. + # + # The body is provided by the passed-in no-argument block + # + # blockquote("http://www.example.com/quotes/foo.html") { "Foo!" } + # #=> "
Foo!
+ def blockquote(cite = nil) # :yield: + attributes = if cite.kind_of?(String) + { "CITE" => cite } + else + cite or "" + end + if block_given? + super(attributes){ yield } + else + super(attributes) + end + end + + + # Generate a Table Caption element as a string. + # + # +align+ can be a string, giving the alignment of the caption + # (one of top, bottom, left, or right). It can be a hash of + # all the attributes of the element. Or it can be omitted. + # + # The body of the element is provided by the passed-in no-argument block. + # + # caption("left") { "Capital Cities" } + # # => Capital Cities + def caption(align = nil) # :yield: + attributes = if align.kind_of?(String) + { "ALIGN" => align } + else + align or "" + end + if block_given? + super(attributes){ yield } + else + super(attributes) + end + end + + + # Generate a Checkbox Input element as a string. + # + # The attributes of the element can be specified as three arguments, + # +name+, +value+, and +checked+. +checked+ is a boolean value; + # if true, the CHECKED attribute will be included in the element. + # + # Alternatively, the attributes can be specified as a hash. + # + # checkbox("name") + # # = checkbox("NAME" => "name") + # + # checkbox("name", "value") + # # = checkbox("NAME" => "name", "VALUE" => "value") + # + # checkbox("name", "value", true) + # # = checkbox("NAME" => "name", "VALUE" => "value", "CHECKED" => true) + def checkbox(name = "", value = nil, checked = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "checkbox", "NAME" => name, + "VALUE" => value, "CHECKED" => checked } + else + name["TYPE"] = "checkbox" + name + end + input(attributes) + end + + # Generate a sequence of checkbox elements, as a String. + # + # The checkboxes will all have the same +name+ attribute. + # Each checkbox is followed by a label. + # There will be one checkbox for each value. Each value + # can be specified as a String, which will be used both + # as the value of the VALUE attribute and as the label + # for that checkbox. A single-element array has the + # same effect. + # + # Each value can also be specified as a three-element array. + # The first element is the VALUE attribute; the second is the + # label; and the third is a boolean specifying whether this + # checkbox is CHECKED. + # + # Each value can also be specified as a two-element + # array, by omitting either the value element (defaults + # to the same as the label), or the boolean checked element + # (defaults to false). + # + # checkbox_group("name", "foo", "bar", "baz") + # # foo + # # bar + # # baz + # + # checkbox_group("name", ["foo"], ["bar", true], "baz") + # # foo + # # bar + # # baz + # + # checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz") + # # Foo + # # Bar + # # Baz + # + # checkbox_group("NAME" => "name", + # "VALUES" => ["foo", "bar", "baz"]) + # + # checkbox_group("NAME" => "name", + # "VALUES" => [["foo"], ["bar", true], "baz"]) + # + # checkbox_group("NAME" => "name", + # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"]) + def checkbox_group(name = "", *values) + if name.kind_of?(Hash) + values = name["VALUES"] + name = name["NAME"] + end + values.collect{|value| + if value.kind_of?(String) + checkbox(name, value) + value + else + if value[value.size - 1] == true + checkbox(name, value[0], true) + + value[value.size - 2] + else + checkbox(name, value[0]) + + value[value.size - 1] + end + end + }.to_s + end + + + # Generate an File Upload Input element as a string. + # + # The attributes of the element can be specified as three arguments, + # +name+, +size+, and +maxlength+. +maxlength+ is the maximum length + # of the file's _name_, not of the file's _contents_. + # + # Alternatively, the attributes can be specified as a hash. + # + # See #multipart_form() for forms that include file uploads. + # + # file_field("name") + # # + # + # file_field("name", 40) + # # + # + # file_field("name", 40, 100) + # # + # + # file_field("NAME" => "name", "SIZE" => 40) + # # + def file_field(name = "", size = 20, maxlength = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "file", "NAME" => name, + "SIZE" => size.to_s } + else + name["TYPE"] = "file" + name + end + attributes["MAXLENGTH"] = maxlength.to_s if maxlength + input(attributes) + end + + + # Generate a Form element as a string. + # + # +method+ should be either "get" or "post", and defaults to the latter. + # +action+ defaults to the current CGI script name. +enctype+ + # defaults to "application/x-www-form-urlencoded". + # + # Alternatively, the attributes can be specified as a hash. + # + # See also #multipart_form() for forms that include file uploads. + # + # form{ "string" } + # #
string
+ # + # form("get") { "string" } + # #
string
+ # + # form("get", "url") { "string" } + # #
string
+ # + # form("METHOD" => "post", "ENCTYPE" => "enctype") { "string" } + # #
string
+ def form(method = "post", action = script_name, enctype = "application/x-www-form-urlencoded") + attributes = if method.kind_of?(String) + { "METHOD" => method, "ACTION" => action, + "ENCTYPE" => enctype } + else + unless method.has_key?("METHOD") + method["METHOD"] = "post" + end + unless method.has_key?("ENCTYPE") + method["ENCTYPE"] = enctype + end + method + end + if block_given? + body = yield + else + body = "" + end + if @output_hidden + body += @output_hidden.collect{|k,v| + "" + }.to_s + end + super(attributes){body} + end + + # Generate a Hidden Input element as a string. + # + # The attributes of the element can be specified as two arguments, + # +name+ and +value+. + # + # Alternatively, the attributes can be specified as a hash. + # + # hidden("name") + # # + # + # hidden("name", "value") + # # + # + # hidden("NAME" => "name", "VALUE" => "reset", "ID" => "foo") + # # + def hidden(name = "", value = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "hidden", "NAME" => name, "VALUE" => value } + else + name["TYPE"] = "hidden" + name + end + input(attributes) + end + + # Generate a top-level HTML element as a string. + # + # The attributes of the element are specified as a hash. The + # pseudo-attribute "PRETTY" can be used to specify that the generated + # HTML string should be indented. "PRETTY" can also be specified as + # a string as the sole argument to this method. The pseudo-attribute + # "DOCTYPE", if given, is used as the leading DOCTYPE SGML tag; it + # should include the entire text of this tag, including angle brackets. + # + # The body of the html element is supplied as a block. + # + # html{ "string" } + # # string + # + # html("LANG" => "ja") { "string" } + # # string + # + # html("DOCTYPE" => false) { "string" } + # # string + # + # html("DOCTYPE" => '') { "string" } + # # string + # + # html("PRETTY" => " ") { "" } + # # + # # + # # + # # + # # + # + # html("PRETTY" => "\t") { "" } + # # + # # + # # + # # + # # + # + # html("PRETTY") { "" } + # # = html("PRETTY" => " ") { "" } + # + # html(if $VERBOSE then "PRETTY" end) { "HTML string" } + # + def html(attributes = {}) # :yield: + if nil == attributes + attributes = {} + elsif "PRETTY" == attributes + attributes = { "PRETTY" => true } + end + pretty = attributes.delete("PRETTY") + pretty = " " if true == pretty + buf = "" + + if attributes.has_key?("DOCTYPE") + if attributes["DOCTYPE"] + buf += attributes.delete("DOCTYPE") + else + attributes.delete("DOCTYPE") + end + else + buf += doctype + end + + if block_given? + buf += super(attributes){ yield } + else + buf += super(attributes) + end + + if pretty + CGI::pretty(buf, pretty) + else + buf + end + + end + + # Generate an Image Button Input element as a string. + # + # +src+ is the URL of the image to use for the button. +name+ + # is the input name. +alt+ is the alternative text for the image. + # + # Alternatively, the attributes can be specified as a hash. + # + # image_button("url") + # # + # + # image_button("url", "name", "string") + # # + # + # image_button("SRC" => "url", "ATL" => "strng") + # # + def image_button(src = "", name = nil, alt = nil) + attributes = if src.kind_of?(String) + { "TYPE" => "image", "SRC" => src, "NAME" => name, + "ALT" => alt } + else + src["TYPE"] = "image" + src["SRC"] ||= "" + src + end + input(attributes) + end + + + # Generate an Image element as a string. + # + # +src+ is the URL of the image. +alt+ is the alternative text for + # the image. +width+ is the width of the image, and +height+ is + # its height. + # + # Alternatively, the attributes can be specified as a hash. + # + # img("src", "alt", 100, 50) + # # alt + # + # img("SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50) + # # alt + def img(src = "", alt = "", width = nil, height = nil) + attributes = if src.kind_of?(String) + { "SRC" => src, "ALT" => alt } + else + src + end + attributes["WIDTH"] = width.to_s if width + attributes["HEIGHT"] = height.to_s if height + super(attributes) + end + + + # Generate a Form element with multipart encoding as a String. + # + # Multipart encoding is used for forms that include file uploads. + # + # +action+ is the action to perform. +enctype+ is the encoding + # type, which defaults to "multipart/form-data". + # + # Alternatively, the attributes can be specified as a hash. + # + # multipart_form{ "string" } + # #
string
+ # + # multipart_form("url") { "string" } + # #
string
+ def multipart_form(action = nil, enctype = "multipart/form-data") + attributes = if action == nil + { "METHOD" => "post", "ENCTYPE" => enctype } + elsif action.kind_of?(String) + { "METHOD" => "post", "ACTION" => action, + "ENCTYPE" => enctype } + else + unless action.has_key?("METHOD") + action["METHOD"] = "post" + end + unless action.has_key?("ENCTYPE") + action["ENCTYPE"] = enctype + end + action + end + if block_given? + form(attributes){ yield } + else + form(attributes) + end + end + + + # Generate a Password Input element as a string. + # + # +name+ is the name of the input field. +value+ is its default + # value. +size+ is the size of the input field display. +maxlength+ + # is the maximum length of the inputted password. + # + # Alternatively, attributes can be specified as a hash. + # + # password_field("name") + # # + # + # password_field("name", "value") + # # + # + # password_field("password", "value", 80, 200) + # # + # + # password_field("NAME" => "name", "VALUE" => "value") + # # + def password_field(name = "", value = nil, size = 40, maxlength = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "password", "NAME" => name, + "VALUE" => value, "SIZE" => size.to_s } + else + name["TYPE"] = "password" + name + end + attributes["MAXLENGTH"] = maxlength.to_s if maxlength + input(attributes) + end + + # Generate a Select element as a string. + # + # +name+ is the name of the element. The +values+ are the options that + # can be selected from the Select menu. Each value can be a String or + # a one, two, or three-element Array. If a String or a one-element + # Array, this is both the value of that option and the text displayed for + # it. If a three-element Array, the elements are the option value, displayed + # text, and a boolean value specifying whether this option starts as selected. + # The two-element version omits either the option value (defaults to the same + # as the display text) or the boolean selected specifier (defaults to false). + # + # The attributes and options can also be specified as a hash. In this + # case, options are specified as an array of values as described above, + # with the hash key of "VALUES". + # + # popup_menu("name", "foo", "bar", "baz") + # # + # + # popup_menu("name", ["foo"], ["bar", true], "baz") + # # + # + # popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz") + # # + # + # popup_menu("NAME" => "name", "SIZE" => 2, "MULTIPLE" => true, + # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"]) + # # + def popup_menu(name = "", *values) + + if name.kind_of?(Hash) + values = name["VALUES"] + size = name["SIZE"].to_s if name["SIZE"] + multiple = name["MULTIPLE"] + name = name["NAME"] + else + size = nil + multiple = nil + end + + select({ "NAME" => name, "SIZE" => size, + "MULTIPLE" => multiple }){ + values.collect{|value| + if value.kind_of?(String) + option({ "VALUE" => value }){ value } + else + if value[value.size - 1] == true + option({ "VALUE" => value[0], "SELECTED" => true }){ + value[value.size - 2] + } + else + option({ "VALUE" => value[0] }){ + value[value.size - 1] + } + end + end + }.to_s + } + + end + + # Generates a radio-button Input element. + # + # +name+ is the name of the input field. +value+ is the value of + # the field if checked. +checked+ specifies whether the field + # starts off checked. + # + # Alternatively, the attributes can be specified as a hash. + # + # radio_button("name", "value") + # # + # + # radio_button("name", "value", true) + # # + # + # radio_button("NAME" => "name", "VALUE" => "value", "ID" => "foo") + # # + def radio_button(name = "", value = nil, checked = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "radio", "NAME" => name, + "VALUE" => value, "CHECKED" => checked } + else + name["TYPE"] = "radio" + name + end + input(attributes) + end + + # Generate a sequence of radio button Input elements, as a String. + # + # This works the same as #checkbox_group(). However, it is not valid + # to have more than one radiobutton in a group checked. + # + # radio_group("name", "foo", "bar", "baz") + # # foo + # # bar + # # baz + # + # radio_group("name", ["foo"], ["bar", true], "baz") + # # foo + # # bar + # # baz + # + # radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz") + # # Foo + # # Bar + # # Baz + # + # radio_group("NAME" => "name", + # "VALUES" => ["foo", "bar", "baz"]) + # + # radio_group("NAME" => "name", + # "VALUES" => [["foo"], ["bar", true], "baz"]) + # + # radio_group("NAME" => "name", + # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"]) + def radio_group(name = "", *values) + if name.kind_of?(Hash) + values = name["VALUES"] + name = name["NAME"] + end + values.collect{|value| + if value.kind_of?(String) + radio_button(name, value) + value + else + if value[value.size - 1] == true + radio_button(name, value[0], true) + + value[value.size - 2] + else + radio_button(name, value[0]) + + value[value.size - 1] + end + end + }.to_s + end + + # Generate a reset button Input element, as a String. + # + # This resets the values on a form to their initial values. +value+ + # is the text displayed on the button. +name+ is the name of this button. + # + # Alternatively, the attributes can be specified as a hash. + # + # reset + # # + # + # reset("reset") + # # + # + # reset("VALUE" => "reset", "ID" => "foo") + # # + def reset(value = nil, name = nil) + attributes = if (not value) or value.kind_of?(String) + { "TYPE" => "reset", "VALUE" => value, "NAME" => name } + else + value["TYPE"] = "reset" + value + end + input(attributes) + end + + alias scrolling_list popup_menu + + # Generate a submit button Input element, as a String. + # + # +value+ is the text to display on the button. +name+ is the name + # of the input. + # + # Alternatively, the attributes can be specified as a hash. + # + # submit + # # + # + # submit("ok") + # # + # + # submit("ok", "button1") + # # + # + # submit("VALUE" => "ok", "NAME" => "button1", "ID" => "foo") + # # + def submit(value = nil, name = nil) + attributes = if (not value) or value.kind_of?(String) + { "TYPE" => "submit", "VALUE" => value, "NAME" => name } + else + value["TYPE"] = "submit" + value + end + input(attributes) + end + + # Generate a text field Input element, as a String. + # + # +name+ is the name of the input field. +value+ is its initial + # value. +size+ is the size of the input area. +maxlength+ + # is the maximum length of input accepted. + # + # Alternatively, the attributes can be specified as a hash. + # + # text_field("name") + # # + # + # text_field("name", "value") + # # + # + # text_field("name", "value", 80) + # # + # + # text_field("name", "value", 80, 200) + # # + # + # text_field("NAME" => "name", "VALUE" => "value") + # # + def text_field(name = "", value = nil, size = 40, maxlength = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "text", "NAME" => name, "VALUE" => value, + "SIZE" => size.to_s } + else + name["TYPE"] = "text" + name + end + attributes["MAXLENGTH"] = maxlength.to_s if maxlength + input(attributes) + end + + # Generate a TextArea element, as a String. + # + # +name+ is the name of the textarea. +cols+ is the number of + # columns and +rows+ is the number of rows in the display. + # + # Alternatively, the attributes can be specified as a hash. + # + # The body is provided by the passed-in no-argument block + # + # textarea("name") + # # = textarea("NAME" => "name", "COLS" => 70, "ROWS" => 10) + # + # textarea("name", 40, 5) + # # = textarea("NAME" => "name", "COLS" => 40, "ROWS" => 5) + def textarea(name = "", cols = 70, rows = 10) # :yield: + attributes = if name.kind_of?(String) + { "NAME" => name, "COLS" => cols.to_s, + "ROWS" => rows.to_s } + else + name + end + if block_given? + super(attributes){ yield } + else + super(attributes) + end + end + + end # HtmlExtension + + + # Mixin module for HTML version 3 generation methods. + module Html3 # :nodoc: + + # The DOCTYPE declaration for this version of HTML + def doctype + %|| + end + + # Initialise the HTML generation methods for this version. + def element_init + extend TagMaker + methods = "" + # - - + for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG + DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV center MAP + APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT table TITLE + STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE + CAPTION ] + methods += <<-BEGIN + nn_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + + # - O EMPTY + for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT + ISINDEX META ] + methods += <<-BEGIN + nOE_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + + # O O or - O + for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr + th td ] + methods += <<-BEGIN + nO_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + eval(methods) + end + + end # Html3 + + + # Mixin module for HTML version 4 generation methods. + module Html4 # :nodoc: + + # The DOCTYPE declaration for this version of HTML + def doctype + %|| + end + + # Initialise the HTML generation methods for this version. + def element_init + extend TagMaker + methods = "" + # - - + for element in %w[ TT I B BIG SMALL EM STRONG DFN CODE SAMP KBD + VAR CITE ABBR ACRONYM SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT + H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT OPTGROUP + FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT + TEXTAREA FORM A BLOCKQUOTE CAPTION ] + methods += <<-BEGIN + nn_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + + # - O EMPTY + for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META ] + methods += <<-BEGIN + nOE_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + + # O O or - O + for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY + COLGROUP TR TH TD HEAD] + methods += <<-BEGIN + nO_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + eval(methods) + end + + end # Html4 + + + # Mixin module for HTML version 4 transitional generation methods. + module Html4Tr # :nodoc: + + # The DOCTYPE declaration for this version of HTML + def doctype + %|| + end + + # Initialise the HTML generation methods for this version. + def element_init + extend TagMaker + methods = "" + # - - + for element in %w[ TT I B U S STRIKE BIG SMALL EM STRONG DFN + CODE SAMP KBD VAR CITE ABBR ACRONYM FONT SUB SUP SPAN BDO + ADDRESS DIV CENTER MAP OBJECT APPLET H1 H2 H3 H4 H5 H6 PRE Q + INS DEL DL OL UL DIR MENU LABEL SELECT OPTGROUP FIELDSET + LEGEND BUTTON TABLE IFRAME NOFRAMES TITLE STYLE SCRIPT + NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ] + methods += <<-BEGIN + nn_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + + # - O EMPTY + for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT + COL ISINDEX META ] + methods += <<-BEGIN + nOE_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + + # O O or - O + for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY + COLGROUP TR TH TD HEAD ] + methods += <<-BEGIN + nO_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + eval(methods) + end + + end # Html4Tr + + + # Mixin module for generating HTML version 4 with framesets. + module Html4Fr # :nodoc: + + # The DOCTYPE declaration for this version of HTML + def doctype + %|| + end + + # Initialise the HTML generation methods for this version. + def element_init + methods = "" + # - - + for element in %w[ FRAMESET ] + methods += <<-BEGIN + nn_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + + # - O EMPTY + for element in %w[ FRAME ] + methods += <<-BEGIN + nOE_element_def(element) + <<-END + def #{element.downcase}(attributes = {}) + BEGIN + end + END + end + eval(methods) + end + + end # Html4Fr + + + # Creates a new CGI instance. + # + # +type+ specifies which version of HTML to load the HTML generation + # methods for. The following versions of HTML are supported: + # + # html3:: HTML 3.x + # html4:: HTML 4.0 + # html4Tr:: HTML 4.0 Transitional + # html4Fr:: HTML 4.0 with Framesets + # + # If not specified, no HTML generation methods will be loaded. + # + # If the CGI object is not created in a standard CGI call environment + # (that is, it can't locate REQUEST_METHOD in its environment), then + # it will run in "offline" mode. In this mode, it reads its parameters + # from the command line or (failing that) from standard input. Otherwise, + # cookies and other parameters are parsed automatically from the standard + # CGI locations, which varies according to the REQUEST_METHOD. + def initialize(type = "query") + if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE") + Apache.request.setup_cgi_env + end + + extend QueryExtension + @multipart = false + if defined?(CGI_PARAMS) + warn "do not use CGI_PARAMS and CGI_COOKIES" + @params = CGI_PARAMS.dup + @cookies = CGI_COOKIES.dup + else + initialize_query() # set @params, @cookies + end + @output_cookies = nil + @output_hidden = nil + + case type + when "html3" + extend Html3 + element_init() + extend HtmlExtension + when "html4" + extend Html4 + element_init() + extend HtmlExtension + when "html4Tr" + extend Html4Tr + element_init() + extend HtmlExtension + when "html4Fr" + extend Html4Tr + element_init() + extend Html4Fr + element_init() + extend HtmlExtension + end + end + +end # class CGI diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/cgi/session.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/cgi/session.rb new file mode 100644 index 0000000000..82eb7534d8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/cgi/session.rb @@ -0,0 +1,484 @@ +# +# cgi/session.rb - session support for cgi scripts +# +# Copyright (C) 2001 Yukihiro "Matz" Matsumoto +# Copyright (C) 2000 Network Applied Communication Laboratory, Inc. +# Copyright (C) 2000 Information-technology Promotion Agency, Japan +# +# Author: Yukihiro "Matz" Matsumoto +# +# Documentation: William Webber (william@williamwebber.com) +# +# == Overview +# +# This file provides the +CGI::Session+ class, which provides session +# support for CGI scripts. A session is a sequence of HTTP requests +# and responses linked together and associated with a single client. +# Information associated with the session is stored +# on the server between requests. A session id is passed between client +# and server with every request and response, transparently +# to the user. This adds state information to the otherwise stateless +# HTTP request/response protocol. +# +# See the documentation to the +CGI::Session+ class for more details +# and examples of usage. See cgi.rb for the +CGI+ class itself. + +require 'cgi' +require 'tmpdir' + +class CGI + + # Class representing an HTTP session. See documentation for the file + # cgi/session.rb for an introduction to HTTP sessions. + # + # == Lifecycle + # + # A CGI::Session instance is created from a CGI object. By default, + # this CGI::Session instance will start a new session if none currently + # exists, or continue the current session for this client if one does + # exist. The +new_session+ option can be used to either always or + # never create a new session. See #new() for more details. + # + # #delete() deletes a session from session storage. It + # does not however remove the session id from the client. If the client + # makes another request with the same id, the effect will be to start + # a new session with the old session's id. + # + # == Setting and retrieving session data. + # + # The Session class associates data with a session as key-value pairs. + # This data can be set and retrieved by indexing the Session instance + # using '[]', much the same as hashes (although other hash methods + # are not supported). + # + # When session processing has been completed for a request, the + # session should be closed using the close() method. This will + # store the session's state to persistent storage. If you want + # to store the session's state to persistent storage without + # finishing session processing for this request, call the update() + # method. + # + # == Storing session state + # + # The caller can specify what form of storage to use for the session's + # data with the +database_manager+ option to CGI::Session::new. The + # following storage classes are provided as part of the standard library: + # + # CGI::Session::FileStore:: stores data as plain text in a flat file. Only + # works with String data. This is the default + # storage type. + # CGI::Session::MemoryStore:: stores data in an in-memory hash. The data + # only persists for as long as the current ruby + # interpreter instance does. + # CGI::Session::PStore:: stores data in Marshalled format. Provided by + # cgi/session/pstore.rb. Supports data of any type, + # and provides file-locking and transaction support. + # + # Custom storage types can also be created by defining a class with + # the following methods: + # + # new(session, options) + # restore # returns hash of session data. + # update + # close + # delete + # + # Changing storage type mid-session does not work. Note in particular + # that by default the FileStore and PStore session data files have the + # same name. If your application switches from one to the other without + # making sure that filenames will be different + # and clients still have old sessions lying around in cookies, then + # things will break nastily! + # + # == Maintaining the session id. + # + # Most session state is maintained on the server. However, a session + # id must be passed backwards and forwards between client and server + # to maintain a reference to this session state. + # + # The simplest way to do this is via cookies. The CGI::Session class + # provides transparent support for session id communication via cookies + # if the client has cookies enabled. + # + # If the client has cookies disabled, the session id must be included + # as a parameter of all requests sent by the client to the server. The + # CGI::Session class in conjunction with the CGI class will transparently + # add the session id as a hidden input field to all forms generated + # using the CGI#form() HTML generation method. No built-in support is + # provided for other mechanisms, such as URL re-writing. The caller is + # responsible for extracting the session id from the session_id + # attribute and manually encoding it in URLs and adding it as a hidden + # input to HTML forms created by other mechanisms. Also, session expiry + # is not automatically handled. + # + # == Examples of use + # + # === Setting the user's name + # + # require 'cgi' + # require 'cgi/session' + # require 'cgi/session/pstore' # provides CGI::Session::PStore + # + # cgi = CGI.new("html4") + # + # session = CGI::Session.new(cgi, + # 'database_manager' => CGI::Session::PStore, # use PStore + # 'session_key' => '_rb_sess_id', # custom session key + # 'session_expires' => Time.now + 30 * 60, # 30 minute timeout + # 'prefix' => 'pstore_sid_') # PStore option + # if cgi.has_key?('user_name') and cgi['user_name'] != '' + # # coerce to String: cgi[] returns the + # # string-like CGI::QueryExtension::Value + # session['user_name'] = cgi['user_name'].to_s + # elsif !session['user_name'] + # session['user_name'] = "guest" + # end + # session.close + # + # === Creating a new session safely + # + # require 'cgi' + # require 'cgi/session' + # + # cgi = CGI.new("html4") + # + # # We make sure to delete an old session if one exists, + # # not just to free resources, but to prevent the session + # # from being maliciously hijacked later on. + # begin + # session = CGI::Session.new(cgi, 'new_session' => false) + # session.delete + # rescue ArgumentError # if no old session + # end + # session = CGI::Session.new(cgi, 'new_session' => true) + # session.close + # + class Session + + class NoSession < RuntimeError #:nodoc: + end + + # The id of this session. + attr_reader :session_id, :new_session + + def Session::callback(dbman) #:nodoc: + Proc.new{ + dbman[0].close unless dbman.empty? + } + end + + # Create a new session id. + # + # The session id is an MD5 hash based upon the time, + # a random number, and a constant string. This routine + # is used internally for automatically generated + # session ids. + def create_new_id + require 'digest/md5' + md5 = Digest::MD5::new + now = Time::now + md5.update(now.to_s) + md5.update(String(now.usec)) + md5.update(String(rand(0))) + md5.update(String($$)) + md5.update('foobar') + @new_session = true + md5.hexdigest + end + private :create_new_id + + # Create a new CGI::Session object for +request+. + # + # +request+ is an instance of the +CGI+ class (see cgi.rb). + # +option+ is a hash of options for initialising this + # CGI::Session instance. The following options are + # recognised: + # + # session_key:: the parameter name used for the session id. + # Defaults to '_session_id'. + # session_id:: the session id to use. If not provided, then + # it is retrieved from the +session_key+ parameter + # of the request, or automatically generated for + # a new session. + # new_session:: if true, force creation of a new session. If not set, + # a new session is only created if none currently + # exists. If false, a new session is never created, + # and if none currently exists and the +session_id+ + # option is not set, an ArgumentError is raised. + # database_manager:: the name of the class providing storage facilities + # for session state persistence. Built-in support + # is provided for +FileStore+ (the default), + # +MemoryStore+, and +PStore+ (from + # cgi/session/pstore.rb). See the documentation for + # these classes for more details. + # + # The following options are also recognised, but only apply if the + # session id is stored in a cookie. + # + # session_expires:: the time the current session expires, as a + # +Time+ object. If not set, the session will terminate + # when the user's browser is closed. + # session_domain:: the hostname domain for which this session is valid. + # If not set, defaults to the hostname of the server. + # session_secure:: if +true+, this session will only work over HTTPS. + # session_path:: the path for which this session applies. Defaults + # to the directory of the CGI script. + # + # +option+ is also passed on to the session storage class initialiser; see + # the documentation for each session storage class for the options + # they support. + # + # The retrieved or created session is automatically added to +request+ + # as a cookie, and also to its +output_hidden+ table, which is used + # to add hidden input elements to forms. + # + # *WARNING* the +output_hidden+ + # fields are surrounded by a
tag in HTML 4 generation, which + # is _not_ invisible on many browsers; you may wish to disable the + # use of fieldsets with code similar to the following + # (see http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/37805) + # + # cgi = CGI.new("html4") + # class << cgi + # undef_method :fieldset + # end + # + def initialize(request, option={}) + @new_session = false + session_key = option['session_key'] || '_session_id' + session_id = option['session_id'] + unless session_id + if option['new_session'] + session_id = create_new_id + end + end + unless session_id + if request.key?(session_key) + session_id = request[session_key] + session_id = session_id.read if session_id.respond_to?(:read) + end + unless session_id + session_id, = request.cookies[session_key] + end + unless session_id + unless option.fetch('new_session', true) + raise ArgumentError, "session_key `%s' should be supplied"%session_key + end + session_id = create_new_id + end + end + @session_id = session_id + dbman = option['database_manager'] || FileStore + begin + @dbman = dbman::new(self, option) + rescue NoSession + unless option.fetch('new_session', true) + raise ArgumentError, "invalid session_id `%s'"%session_id + end + session_id = @session_id = create_new_id + retry + end + request.instance_eval do + @output_hidden = {session_key => session_id} unless option['no_hidden'] + @output_cookies = [ + Cookie::new("name" => session_key, + "value" => session_id, + "expires" => option['session_expires'], + "domain" => option['session_domain'], + "secure" => option['session_secure'], + "path" => if option['session_path'] then + option['session_path'] + elsif ENV["SCRIPT_NAME"] then + File::dirname(ENV["SCRIPT_NAME"]) + else + "" + end) + ] unless option['no_cookies'] + end + @dbprot = [@dbman] + ObjectSpace::define_finalizer(self, Session::callback(@dbprot)) + end + + # Retrieve the session data for key +key+. + def [](key) + @data ||= @dbman.restore + @data[key] + end + + # Set the session date for key +key+. + def []=(key, val) + @write_lock ||= true + @data ||= @dbman.restore + @data[key] = val + end + + # Store session data on the server. For some session storage types, + # this is a no-op. + def update + @dbman.update + end + + # Store session data on the server and close the session storage. + # For some session storage types, this is a no-op. + def close + @dbman.close + @dbprot.clear + end + + # Delete the session from storage. Also closes the storage. + # + # Note that the session's data is _not_ automatically deleted + # upon the session expiring. + def delete + @dbman.delete + @dbprot.clear + end + + # File-based session storage class. + # + # Implements session storage as a flat file of 'key=value' values. + # This storage type only works directly with String values; the + # user is responsible for converting other types to Strings when + # storing and from Strings when retrieving. + class FileStore + # Create a new FileStore instance. + # + # This constructor is used internally by CGI::Session. The + # user does not generally need to call it directly. + # + # +session+ is the session for which this instance is being + # created. The session id must only contain alphanumeric + # characters; automatically generated session ids observe + # this requirement. + # + # +option+ is a hash of options for the initialiser. The + # following options are recognised: + # + # tmpdir:: the directory to use for storing the FileStore + # file. Defaults to Dir::tmpdir (generally "/tmp" + # on Unix systems). + # prefix:: the prefix to add to the session id when generating + # the filename for this session's FileStore file. + # Defaults to the empty string. + # suffix:: the prefix to add to the session id when generating + # the filename for this session's FileStore file. + # Defaults to the empty string. + # + # This session's FileStore file will be created if it does + # not exist, or opened if it does. + def initialize(session, option={}) + dir = option['tmpdir'] || Dir::tmpdir + prefix = option['prefix'] || '' + suffix = option['suffix'] || '' + id = session.session_id + require 'digest/md5' + md5 = Digest::MD5.hexdigest(id)[0,16] + @path = dir+"/"+prefix+md5+suffix + if File::exist? @path + @hash = nil + else + unless session.new_session + raise CGI::Session::NoSession, "uninitialized session" + end + @hash = {} + end + end + + # Restore session state from the session's FileStore file. + # + # Returns the session state as a hash. + def restore + unless @hash + @hash = {} + begin + f = File.open(@path, 'r') + f.flock File::LOCK_SH + for line in f + line.chomp! + k, v = line.split('=',2) + @hash[CGI::unescape(k)] = CGI::unescape(v) + end + ensure + f.close unless f.nil? + end + end + @hash + end + + # Save session state to the session's FileStore file. + def update + return unless @hash + begin + f = File.open(@path, File::CREAT|File::TRUNC|File::RDWR, 0600) + f.flock File::LOCK_EX + for k,v in @hash + f.printf "%s=%s\n", CGI::escape(k), CGI::escape(String(v)) + end + ensure + f.close unless f.nil? + end + end + + # Update and close the session's FileStore file. + def close + update + end + + # Close and delete the session's FileStore file. + def delete + File::unlink @path + rescue Errno::ENOENT + end + end + + # In-memory session storage class. + # + # Implements session storage as a global in-memory hash. Session + # data will only persist for as long as the ruby interpreter + # instance does. + class MemoryStore + GLOBAL_HASH_TABLE = {} #:nodoc: + + # Create a new MemoryStore instance. + # + # +session+ is the session this instance is associated with. + # +option+ is a list of initialisation options. None are + # currently recognised. + def initialize(session, option=nil) + @session_id = session.session_id + unless GLOBAL_HASH_TABLE.key?(@session_id) + unless session.new_session + raise CGI::Session::NoSession, "uninitialized session" + end + GLOBAL_HASH_TABLE[@session_id] = {} + end + end + + # Restore session state. + # + # Returns session data as a hash. + def restore + GLOBAL_HASH_TABLE[@session_id] + end + + # Update session state. + # + # A no-op. + def update + # don't need to update; hash is shared + end + + # Close session storage. + # + # A no-op. + def close + # don't need to close + end + + # Delete the session state. + def delete + GLOBAL_HASH_TABLE.delete(@session_id) + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/cgi/session/pstore.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/cgi/session/pstore.rb new file mode 100644 index 0000000000..bd93d0a6ff --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/cgi/session/pstore.rb @@ -0,0 +1,111 @@ +# +# cgi/session/pstore.rb - persistent storage of marshalled session data +# +# Documentation: William Webber (william@williamwebber.com) +# +# == Overview +# +# This file provides the CGI::Session::PStore class, which builds +# persistent of session data on top of the pstore library. See +# cgi/session.rb for more details on session storage managers. + +require 'cgi/session' +require 'pstore' + +class CGI + class Session + # PStore-based session storage class. + # + # This builds upon the top-level PStore class provided by the + # library file pstore.rb. Session data is marshalled and stored + # in a file. File locking and transaction services are provided. + class PStore + # Create a new CGI::Session::PStore instance + # + # This constructor is used internally by CGI::Session. The + # user does not generally need to call it directly. + # + # +session+ is the session for which this instance is being + # created. The session id must only contain alphanumeric + # characters; automatically generated session ids observe + # this requirement. + # + # +option+ is a hash of options for the initialiser. The + # following options are recognised: + # + # tmpdir:: the directory to use for storing the PStore + # file. Defaults to Dir::tmpdir (generally "/tmp" + # on Unix systems). + # prefix:: the prefix to add to the session id when generating + # the filename for this session's PStore file. + # Defaults to the empty string. + # + # This session's PStore file will be created if it does + # not exist, or opened if it does. + def initialize(session, option={}) + dir = option['tmpdir'] || Dir::tmpdir + prefix = option['prefix'] || '' + id = session.session_id + require 'digest/md5' + md5 = Digest::MD5.hexdigest(id)[0,16] + path = dir+"/"+prefix+md5 + path.untaint + if File::exist?(path) + @hash = nil + else + unless session.new_session + raise CGI::Session::NoSession, "uninitialized session" + end + @hash = {} + end + @p = ::PStore.new(path) + @p.transaction do |p| + File.chmod(0600, p.path) + end + end + + # Restore session state from the session's PStore file. + # + # Returns the session state as a hash. + def restore + unless @hash + @p.transaction do + @hash = @p['hash'] || {} + end + end + @hash + end + + # Save session state to the session's PStore file. + def update + @p.transaction do + @p['hash'] = @hash + end + end + + # Update and close the session's PStore file. + def close + update + end + + # Close and delete the session's PStore file. + def delete + path = @p.path + File::unlink path + end + + end + end +end + +if $0 == __FILE__ + # :enddoc: + STDIN.reopen("/dev/null") + cgi = CGI.new + session = CGI::Session.new(cgi, 'database_manager' => CGI::Session::PStore) + session['key'] = {'k' => 'v'} + puts session['key'].class + fail unless Hash === session['key'] + puts session['key'].inspect + fail unless session['key'].inspect == '{"k"=>"v"}' +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/complex.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/complex.rb new file mode 100644 index 0000000000..9300f391e8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/complex.rb @@ -0,0 +1,631 @@ +# +# complex.rb - +# $Release Version: 0.5 $ +# $Revision: 1.3 $ +# $Date: 1998/07/08 10:05:28 $ +# by Keiju ISHITSUKA(SHL Japan Inc.) +# +# ---- +# +# complex.rb implements the Complex class for complex numbers. Additionally, +# some methods in other Numeric classes are redefined or added to allow greater +# interoperability with Complex numbers. +# +# Complex numbers can be created in the following manner: +# - Complex(a, b) +# - Complex.polar(radius, theta) +# +# Additionally, note the following: +# - Complex::I (the mathematical constant i) +# - Numeric#im (e.g. 5.im -> 0+5i) +# +# The following +Math+ module methods are redefined to handle Complex arguments. +# They will work as normal with non-Complex arguments. +# sqrt exp cos sin tan log log10 +# cosh sinh tanh acos asin atan atan2 acosh asinh atanh +# + + +# +# Numeric is a built-in class on which Fixnum, Bignum, etc., are based. Here +# some methods are added so that all number types can be treated to some extent +# as Complex numbers. +# +class Numeric + # + # Returns a Complex number (0,self). + # + def im + Complex(0, self) + end + + # + # The real part of a complex number, i.e. self. + # + def real + self + end + + # + # The imaginary part of a complex number, i.e. 0. + # + def image + 0 + end + alias imag image + + # + # See Complex#arg. + # + def arg + if self >= 0 + return 0 + else + return Math::PI + end + end + alias angle arg + + # + # See Complex#polar. + # + def polar + return abs, arg + end + + # + # See Complex#conjugate (short answer: returns self). + # + def conjugate + self + end + alias conj conjugate +end + + +# +# Creates a Complex number. +a+ and +b+ should be Numeric. The result will be +# a+bi. +# +def Complex(a, b = 0) + if b == 0 and (a.kind_of?(Complex) or defined? Complex::Unify) + a + else + Complex.new( a.real-b.imag, a.imag+b.real ) + end +end + +# +# The complex number class. See complex.rb for an overview. +# +class Complex < Numeric + @RCS_ID='-$Id: complex.rb,v 1.3 1998/07/08 10:05:28 keiju Exp keiju $-' + + undef step + + def Complex.generic?(other) # :nodoc: + other.kind_of?(Integer) or + other.kind_of?(Float) or + (defined?(Rational) and other.kind_of?(Rational)) + end + + # + # Creates a +Complex+ number in terms of +r+ (radius) and +theta+ (angle). + # + def Complex.polar(r, theta) + Complex(r*Math.cos(theta), r*Math.sin(theta)) + end + + # + # Creates a +Complex+ number a+bi. + # + def Complex.new!(a, b=0) + new(a,b) + end + + def initialize(a, b) + raise TypeError, "non numeric 1st arg `#{a.inspect}'" if !a.kind_of? Numeric + raise TypeError, "`#{a.inspect}' for 1st arg" if a.kind_of? Complex + raise TypeError, "non numeric 2nd arg `#{b.inspect}'" if !b.kind_of? Numeric + raise TypeError, "`#{b.inspect}' for 2nd arg" if b.kind_of? Complex + @real = a + @image = b + end + + # + # Addition with real or complex number. + # + def + (other) + if other.kind_of?(Complex) + re = @real + other.real + im = @image + other.image + Complex(re, im) + elsif Complex.generic?(other) + Complex(@real + other, @image) + else + x , y = other.coerce(self) + x + y + end + end + + # + # Subtraction with real or complex number. + # + def - (other) + if other.kind_of?(Complex) + re = @real - other.real + im = @image - other.image + Complex(re, im) + elsif Complex.generic?(other) + Complex(@real - other, @image) + else + x , y = other.coerce(self) + x - y + end + end + + # + # Multiplication with real or complex number. + # + def * (other) + if other.kind_of?(Complex) + re = @real*other.real - @image*other.image + im = @real*other.image + @image*other.real + Complex(re, im) + elsif Complex.generic?(other) + Complex(@real * other, @image * other) + else + x , y = other.coerce(self) + x * y + end + end + + # + # Division by real or complex number. + # + def / (other) + if other.kind_of?(Complex) + self*other.conjugate/other.abs2 + elsif Complex.generic?(other) + Complex(@real/other, @image/other) + else + x, y = other.coerce(self) + x/y + end + end + + # + # Raise this complex number to the given (real or complex) power. + # + def ** (other) + if other == 0 + return Complex(1) + end + if other.kind_of?(Complex) + r, theta = polar + ore = other.real + oim = other.image + nr = Math.exp!(ore*Math.log!(r) - oim * theta) + ntheta = theta*ore + oim*Math.log!(r) + Complex.polar(nr, ntheta) + elsif other.kind_of?(Integer) + if other > 0 + x = self + z = x + n = other - 1 + while n != 0 + while (div, mod = n.divmod(2) + mod == 0) + x = Complex(x.real*x.real - x.image*x.image, 2*x.real*x.image) + n = div + end + z *= x + n -= 1 + end + z + else + if defined? Rational + (Rational(1) / self) ** -other + else + self ** Float(other) + end + end + elsif Complex.generic?(other) + r, theta = polar + Complex.polar(r**other, theta*other) + else + x, y = other.coerce(self) + x**y + end + end + + # + # Remainder after division by a real or complex number. + # + def % (other) + if other.kind_of?(Complex) + Complex(@real % other.real, @image % other.image) + elsif Complex.generic?(other) + Complex(@real % other, @image % other) + else + x , y = other.coerce(self) + x % y + end + end + +#-- +# def divmod(other) +# if other.kind_of?(Complex) +# rdiv, rmod = @real.divmod(other.real) +# idiv, imod = @image.divmod(other.image) +# return Complex(rdiv, idiv), Complex(rmod, rmod) +# elsif Complex.generic?(other) +# Complex(@real.divmod(other), @image.divmod(other)) +# else +# x , y = other.coerce(self) +# x.divmod(y) +# end +# end +#++ + + # + # Absolute value (aka modulus): distance from the zero point on the complex + # plane. + # + def abs + Math.hypot(@real, @image) + end + + # + # Square of the absolute value. + # + def abs2 + @real*@real + @image*@image + end + + # + # Argument (angle from (1,0) on the complex plane). + # + def arg + Math.atan2!(@image, @real) + end + alias angle arg + + # + # Returns the absolute value _and_ the argument. + # + def polar + return abs, arg + end + + # + # Complex conjugate (z + z.conjugate = 2 * z.real). + # + def conjugate + Complex(@real, -@image) + end + alias conj conjugate + + # + # Compares the absolute values of the two numbers. + # + def <=> (other) + self.abs <=> other.abs + end + + # + # Test for numerical equality (a == a + 0i). + # + def == (other) + if other.kind_of?(Complex) + @real == other.real and @image == other.image + elsif Complex.generic?(other) + @real == other and @image == 0 + else + other == self + end + end + + # + # Attempts to coerce +other+ to a Complex number. + # + def coerce(other) + if Complex.generic?(other) + return Complex.new!(other), self + else + super + end + end + + # + # FIXME + # + def denominator + @real.denominator.lcm(@image.denominator) + end + + # + # FIXME + # + def numerator + cd = denominator + Complex(@real.numerator*(cd/@real.denominator), + @image.numerator*(cd/@image.denominator)) + end + + # + # Standard string representation of the complex number. + # + def to_s + if @real != 0 + if defined?(Rational) and @image.kind_of?(Rational) and @image.denominator != 1 + if @image >= 0 + @real.to_s+"+("+@image.to_s+")i" + else + @real.to_s+"-("+(-@image).to_s+")i" + end + else + if @image >= 0 + @real.to_s+"+"+@image.to_s+"i" + else + @real.to_s+"-"+(-@image).to_s+"i" + end + end + else + if defined?(Rational) and @image.kind_of?(Rational) and @image.denominator != 1 + "("+@image.to_s+")i" + else + @image.to_s+"i" + end + end + end + + # + # Returns a hash code for the complex number. + # + def hash + @real.hash ^ @image.hash + end + + # + # Returns "Complex(real, image)". + # + def inspect + sprintf("Complex(%s, %s)", @real.inspect, @image.inspect) + end + + + # + # +I+ is the imaginary number. It exists at point (0,1) on the complex plane. + # + I = Complex(0,1) + + # The real part of a complex number. + attr :real + + # The imaginary part of a complex number. + attr :image + alias imag image + +end + + + + +module Math + alias sqrt! sqrt + alias exp! exp + alias log! log + alias log10! log10 + alias cos! cos + alias sin! sin + alias tan! tan + alias cosh! cosh + alias sinh! sinh + alias tanh! tanh + alias acos! acos + alias asin! asin + alias atan! atan + alias atan2! atan2 + alias acosh! acosh + alias asinh! asinh + alias atanh! atanh + + # Redefined to handle a Complex argument. + def sqrt(z) + if Complex.generic?(z) + if z >= 0 + sqrt!(z) + else + Complex(0,sqrt!(-z)) + end + else + if z.image < 0 + sqrt(z.conjugate).conjugate + else + r = z.abs + x = z.real + Complex( sqrt!((r+x)/2), sqrt!((r-x)/2) ) + end + end + end + + # Redefined to handle a Complex argument. + def exp(z) + if Complex.generic?(z) + exp!(z) + else + Complex(exp!(z.real) * cos!(z.image), exp!(z.real) * sin!(z.image)) + end + end + + # Redefined to handle a Complex argument. + def cos(z) + if Complex.generic?(z) + cos!(z) + else + Complex(cos!(z.real)*cosh!(z.image), + -sin!(z.real)*sinh!(z.image)) + end + end + + # Redefined to handle a Complex argument. + def sin(z) + if Complex.generic?(z) + sin!(z) + else + Complex(sin!(z.real)*cosh!(z.image), + cos!(z.real)*sinh!(z.image)) + end + end + + # Redefined to handle a Complex argument. + def tan(z) + if Complex.generic?(z) + tan!(z) + else + sin(z)/cos(z) + end + end + + def sinh(z) + if Complex.generic?(z) + sinh!(z) + else + Complex( sinh!(z.real)*cos!(z.image), cosh!(z.real)*sin!(z.image) ) + end + end + + def cosh(z) + if Complex.generic?(z) + cosh!(z) + else + Complex( cosh!(z.real)*cos!(z.image), sinh!(z.real)*sin!(z.image) ) + end + end + + def tanh(z) + if Complex.generic?(z) + tanh!(z) + else + sinh(z)/cosh(z) + end + end + + # Redefined to handle a Complex argument. + def log(z) + if Complex.generic?(z) and z >= 0 + log!(z) + else + r, theta = z.polar + Complex(log!(r.abs), theta) + end + end + + # Redefined to handle a Complex argument. + def log10(z) + if Complex.generic?(z) + log10!(z) + else + log(z)/log!(10) + end + end + + def acos(z) + if Complex.generic?(z) and z >= -1 and z <= 1 + acos!(z) + else + -1.0.im * log( z + 1.0.im * sqrt(1.0-z*z) ) + end + end + + def asin(z) + if Complex.generic?(z) and z >= -1 and z <= 1 + asin!(z) + else + -1.0.im * log( 1.0.im * z + sqrt(1.0-z*z) ) + end + end + + def atan(z) + if Complex.generic?(z) + atan!(z) + else + 1.0.im * log( (1.0.im+z) / (1.0.im-z) ) / 2.0 + end + end + + def atan2(y,x) + if Complex.generic?(y) and Complex.generic?(x) + atan2!(y,x) + else + -1.0.im * log( (x+1.0.im*y) / sqrt(x*x+y*y) ) + end + end + + def acosh(z) + if Complex.generic?(z) and z >= 1 + acosh!(z) + else + log( z + sqrt(z*z-1.0) ) + end + end + + def asinh(z) + if Complex.generic?(z) + asinh!(z) + else + log( z + sqrt(1.0+z*z) ) + end + end + + def atanh(z) + if Complex.generic?(z) and z >= -1 and z <= 1 + atanh!(z) + else + log( (1.0+z) / (1.0-z) ) / 2.0 + end + end + + module_function :sqrt! + module_function :sqrt + module_function :exp! + module_function :exp + module_function :log! + module_function :log + module_function :log10! + module_function :log10 + module_function :cosh! + module_function :cosh + module_function :cos! + module_function :cos + module_function :sinh! + module_function :sinh + module_function :sin! + module_function :sin + module_function :tan! + module_function :tan + module_function :tanh! + module_function :tanh + module_function :acos! + module_function :acos + module_function :asin! + module_function :asin + module_function :atan! + module_function :atan + module_function :atan2! + module_function :atan2 + module_function :acosh! + module_function :acosh + module_function :asinh! + module_function :asinh + module_function :atanh! + module_function :atanh + +end + +# Documentation comments: +# - source: original (researched from pickaxe) +# - a couple of fixme's +# - RDoc output for Bignum etc. is a bit short, with nothing but an +# (undocumented) alias. No big deal. diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/csv.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/csv.rb new file mode 100644 index 0000000000..5fb12142cc --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/csv.rb @@ -0,0 +1,992 @@ +# CSV -- module for generating/parsing CSV data. +# Copyright (C) 2000-2004 NAKAMURA, Hiroshi . + +# $Id: csv.rb 11708 2007-02-12 23:01:19Z shyouhei $ + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +class CSV + class IllegalFormatError < RuntimeError; end + + # deprecated + class Cell < String + def initialize(data = "", is_null = false) + super(is_null ? "" : data) + end + + def data + to_s + end + end + + # deprecated + class Row < Array + end + + # Open a CSV formatted file for reading or writing. + # + # For reading. + # + # EXAMPLE 1 + # CSV.open('csvfile.csv', 'r') do |row| + # p row + # end + # + # EXAMPLE 2 + # reader = CSV.open('csvfile.csv', 'r') + # row1 = reader.shift + # row2 = reader.shift + # if row2.empty? + # p 'row2 not find.' + # end + # reader.close + # + # ARGS + # filename: filename to parse. + # col_sep: Column separator. ?, by default. If you want to separate + # fields with semicolon, give ?; here. + # row_sep: Row separator. nil by default. nil means "\r\n or \n". If you + # want to separate records with \r, give ?\r here. + # + # RETURNS + # reader instance. To get parse result, see CSV::Reader#each. + # + # + # For writing. + # + # EXAMPLE 1 + # CSV.open('csvfile.csv', 'w') do |writer| + # writer << ['r1c1', 'r1c2'] + # writer << ['r2c1', 'r2c2'] + # writer << [nil, nil] + # end + # + # EXAMPLE 2 + # writer = CSV.open('csvfile.csv', 'w') + # writer << ['r1c1', 'r1c2'] << ['r2c1', 'r2c2'] << [nil, nil] + # writer.close + # + # ARGS + # filename: filename to generate. + # col_sep: Column separator. ?, by default. If you want to separate + # fields with semicolon, give ?; here. + # row_sep: Row separator. nil by default. nil means "\r\n or \n". If you + # want to separate records with \r, give ?\r here. + # + # RETURNS + # writer instance. See CSV::Writer#<< and CSV::Writer#add_row to know how + # to generate CSV string. + # + def CSV.open(path, mode, fs = nil, rs = nil, &block) + if mode == 'r' or mode == 'rb' + open_reader(path, mode, fs, rs, &block) + elsif mode == 'w' or mode == 'wb' + open_writer(path, mode, fs, rs, &block) + else + raise ArgumentError.new("'mode' must be 'r', 'rb', 'w', or 'wb'") + end + end + + def CSV.foreach(path, rs = nil, &block) + open_reader(path, 'r', ',', rs, &block) + end + + def CSV.read(path, length = nil, offset = nil) + CSV.parse(IO.read(path, length, offset)) + end + + def CSV.readlines(path, rs = nil) + reader = open_reader(path, 'r', ',', rs) + begin + reader.collect { |row| row } + ensure + reader.close + end + end + + def CSV.generate(path, fs = nil, rs = nil, &block) + open_writer(path, 'w', fs, rs, &block) + end + + # Parse lines from given string or stream. Return rows as an Array of Arrays. + def CSV.parse(str_or_readable, fs = nil, rs = nil, &block) + if File.exist?(str_or_readable) + STDERR.puts("CSV.parse(filename) is deprecated." + + " Use CSV.open(filename, 'r') instead.") + return open_reader(str_or_readable, 'r', fs, rs, &block) + end + if block + CSV::Reader.parse(str_or_readable, fs, rs) do |row| + yield(row) + end + nil + else + CSV::Reader.create(str_or_readable, fs, rs).collect { |row| row } + end + end + + # Parse a line from given string. Bear in mind it parses ONE LINE. Rest of + # the string is ignored for example "a,b\r\nc,d" => ['a', 'b'] and the + # second line 'c,d' is ignored. + # + # If you don't know whether a target string to parse is exactly 1 line or + # not, use CSV.parse_row instead of this method. + def CSV.parse_line(src, fs = nil, rs = nil) + fs ||= ',' + if fs.is_a?(Fixnum) + fs = fs.chr + end + if !rs.nil? and rs.is_a?(Fixnum) + rs = rs.chr + end + idx = 0 + res_type = :DT_COLSEP + row = [] + begin + while res_type == :DT_COLSEP + res_type, idx, cell = parse_body(src, idx, fs, rs) + row << cell + end + rescue IllegalFormatError + return [] + end + row + end + + # Create a line from cells. each cell is stringified by to_s. + def CSV.generate_line(row, fs = nil, rs = nil) + if row.size == 0 + return '' + end + fs ||= ',' + if fs.is_a?(Fixnum) + fs = fs.chr + end + if !rs.nil? and rs.is_a?(Fixnum) + rs = rs.chr + end + res_type = :DT_COLSEP + result_str = '' + idx = 0 + while true + generate_body(row[idx], result_str, fs, rs) + idx += 1 + if (idx == row.size) + break + end + generate_separator(:DT_COLSEP, result_str, fs, rs) + end + result_str + end + + # Parse a line from string. Consider using CSV.parse_line instead. + # To parse lines in CSV string, see EXAMPLE below. + # + # EXAMPLE + # src = "a,b\r\nc,d\r\ne,f" + # idx = 0 + # begin + # parsed = [] + # parsed_cells, idx = CSV.parse_row(src, idx, parsed) + # puts "Parsed #{ parsed_cells } cells." + # p parsed + # end while parsed_cells > 0 + # + # ARGS + # src: a CSV data to be parsed. Must respond '[](idx)'. + # src[](idx) must return a char. (Not a string such as 'a', but 97). + # src[](idx_out_of_bounds) must return nil. A String satisfies this + # requirement. + # idx: index of parsing location of 'src'. 0 origin. + # out_dev: buffer for parsed cells. Must respond '<<(aString)'. + # col_sep: Column separator. ?, by default. If you want to separate + # fields with semicolon, give ?; here. + # row_sep: Row separator. nil by default. nil means "\r\n or \n". If you + # want to separate records with \r, give ?\r here. + # + # RETURNS + # parsed_cells: num of parsed cells. + # idx: index of next parsing location of 'src'. + # + def CSV.parse_row(src, idx, out_dev, fs = nil, rs = nil) + fs ||= ',' + if fs.is_a?(Fixnum) + fs = fs.chr + end + if !rs.nil? and rs.is_a?(Fixnum) + rs = rs.chr + end + idx_backup = idx + parsed_cells = 0 + res_type = :DT_COLSEP + begin + while res_type != :DT_ROWSEP + res_type, idx, cell = parse_body(src, idx, fs, rs) + if res_type == :DT_EOS + if idx == idx_backup #((parsed_cells == 0) and cell.nil?) + return 0, 0 + end + res_type = :DT_ROWSEP + end + parsed_cells += 1 + out_dev << cell + end + rescue IllegalFormatError + return 0, 0 + end + return parsed_cells, idx + end + + # Convert a line from cells data to string. Consider using CSV.generate_line + # instead. To generate multi-row CSV string, see EXAMPLE below. + # + # EXAMPLE + # row1 = ['a', 'b'] + # row2 = ['c', 'd'] + # row3 = ['e', 'f'] + # src = [row1, row2, row3] + # buf = '' + # src.each do |row| + # parsed_cells = CSV.generate_row(row, 2, buf) + # puts "Created #{ parsed_cells } cells." + # end + # p buf + # + # ARGS + # src: an Array of String to be converted to CSV string. Must respond to + # 'size' and '[](idx)'. src[idx] must return String. + # cells: num of cells in a line. + # out_dev: buffer for generated CSV string. Must respond to '<<(string)'. + # col_sep: Column separator. ?, by default. If you want to separate + # fields with semicolon, give ?; here. + # row_sep: Row separator. nil by default. nil means "\r\n or \n". If you + # want to separate records with \r, give ?\r here. + # + # RETURNS + # parsed_cells: num of converted cells. + # + def CSV.generate_row(src, cells, out_dev, fs = nil, rs = nil) + fs ||= ',' + if fs.is_a?(Fixnum) + fs = fs.chr + end + if !rs.nil? and rs.is_a?(Fixnum) + rs = rs.chr + end + src_size = src.size + if (src_size == 0) + if cells == 0 + generate_separator(:DT_ROWSEP, out_dev, fs, rs) + end + return 0 + end + res_type = :DT_COLSEP + parsed_cells = 0 + generate_body(src[parsed_cells], out_dev, fs, rs) + parsed_cells += 1 + while ((parsed_cells < cells) and (parsed_cells != src_size)) + generate_separator(:DT_COLSEP, out_dev, fs, rs) + generate_body(src[parsed_cells], out_dev, fs, rs) + parsed_cells += 1 + end + if (parsed_cells == cells) + generate_separator(:DT_ROWSEP, out_dev, fs, rs) + else + generate_separator(:DT_COLSEP, out_dev, fs, rs) + end + parsed_cells + end + + # Private class methods. + class << self + private + + def open_reader(path, mode, fs, rs, &block) + file = File.open(path, mode) + if block + begin + CSV::Reader.parse(file, fs, rs) do |row| + yield(row) + end + ensure + file.close + end + nil + else + reader = CSV::Reader.create(file, fs, rs) + reader.close_on_terminate + reader + end + end + + def open_writer(path, mode, fs, rs, &block) + file = File.open(path, mode) + if block + begin + CSV::Writer.generate(file, fs, rs) do |writer| + yield(writer) + end + ensure + file.close + end + nil + else + writer = CSV::Writer.create(file, fs, rs) + writer.close_on_terminate + writer + end + end + + def parse_body(src, idx, fs, rs) + fs_str = fs + fs_size = fs_str.size + rs_str = rs || "\n" + rs_size = rs_str.size + fs_idx = rs_idx = 0 + cell = Cell.new + state = :ST_START + quoted = cr = false + c = nil + last_idx = idx + while c = src[idx] + unless quoted + fschar = (c == fs_str[fs_idx]) + rschar = (c == rs_str[rs_idx]) + # simple 1 char backtrack + if !fschar and c == fs_str[0] + fs_idx = 0 + fschar = true + if state == :ST_START + state = :ST_DATA + elsif state == :ST_QUOTE + raise IllegalFormatError + end + end + if !rschar and c == rs_str[0] + rs_idx = 0 + rschar = true + if state == :ST_START + state = :ST_DATA + elsif state == :ST_QUOTE + raise IllegalFormatError + end + end + end + if c == ?" + fs_idx = rs_idx = 0 + if cr + raise IllegalFormatError + end + cell << src[last_idx, (idx - last_idx)] + last_idx = idx + if state == :ST_DATA + if quoted + last_idx += 1 + quoted = false + state = :ST_QUOTE + else + raise IllegalFormatError + end + elsif state == :ST_QUOTE + cell << c.chr + last_idx += 1 + quoted = true + state = :ST_DATA + else # :ST_START + quoted = true + last_idx += 1 + state = :ST_DATA + end + elsif fschar or rschar + if fschar + fs_idx += 1 + end + if rschar + rs_idx += 1 + end + sep = nil + if fs_idx == fs_size + if state == :ST_START and rs_idx > 0 and fs_idx < rs_idx + state = :ST_DATA + end + cell << src[last_idx, (idx - last_idx - (fs_size - 1))] + last_idx = idx + fs_idx = rs_idx = 0 + if cr + raise IllegalFormatError + end + sep = :DT_COLSEP + elsif rs_idx == rs_size + if state == :ST_START and fs_idx > 0 and rs_idx < fs_idx + state = :ST_DATA + end + if !(rs.nil? and cr) + cell << src[last_idx, (idx - last_idx - (rs_size - 1))] + last_idx = idx + end + fs_idx = rs_idx = 0 + sep = :DT_ROWSEP + end + if sep + if state == :ST_DATA + return sep, idx + 1, cell; + elsif state == :ST_QUOTE + return sep, idx + 1, cell; + else # :ST_START + return sep, idx + 1, nil + end + end + elsif rs.nil? and c == ?\r + # special \r treatment for backward compatibility + fs_idx = rs_idx = 0 + if cr + raise IllegalFormatError + end + cell << src[last_idx, (idx - last_idx)] + last_idx = idx + if quoted + state = :ST_DATA + else + cr = true + end + else + fs_idx = rs_idx = 0 + if state == :ST_DATA or state == :ST_START + if cr + raise IllegalFormatError + end + state = :ST_DATA + else # :ST_QUOTE + raise IllegalFormatError + end + end + idx += 1 + end + if state == :ST_START + if fs_idx > 0 or rs_idx > 0 + state = :ST_DATA + else + return :DT_EOS, idx, nil + end + elsif quoted + raise IllegalFormatError + elsif cr + raise IllegalFormatError + end + cell << src[last_idx, (idx - last_idx)] + last_idx = idx + return :DT_EOS, idx, cell + end + + def generate_body(cell, out_dev, fs, rs) + if cell.nil? + # empty + else + cell = cell.to_s + row_data = cell.dup + if (row_data.gsub!('"', '""') or + row_data.index(fs) or + (rs and row_data.index(rs)) or + (/[\r\n]/ =~ row_data) or + (cell.empty?)) + out_dev << '"' << row_data << '"' + else + out_dev << row_data + end + end + end + + def generate_separator(type, out_dev, fs, rs) + case type + when :DT_COLSEP + out_dev << fs + when :DT_ROWSEP + out_dev << (rs || "\n") + end + end + end + + + # CSV formatted string/stream reader. + # + # EXAMPLE + # read CSV lines untill the first column is 'stop'. + # + # CSV::Reader.parse(File.open('bigdata', 'rb')) do |row| + # p row + # break if !row[0].is_null && row[0].data == 'stop' + # end + # + class Reader + include Enumerable + + # Parse CSV data and get lines. Given block is called for each parsed row. + # Block value is always nil. Rows are not cached for performance reason. + def Reader.parse(str_or_readable, fs = ',', rs = nil, &block) + reader = Reader.create(str_or_readable, fs, rs) + if block + reader.each do |row| + yield(row) + end + reader.close + nil + else + reader + end + end + + # Returns reader instance. + def Reader.create(str_or_readable, fs = ',', rs = nil) + case str_or_readable + when IO + IOReader.new(str_or_readable, fs, rs) + when String + StringReader.new(str_or_readable, fs, rs) + else + IOReader.new(str_or_readable, fs, rs) + end + end + + def each + while true + row = [] + parsed_cells = get_row(row) + if parsed_cells == 0 + break + end + yield(row) + end + nil + end + + def shift + row = [] + parsed_cells = get_row(row) + row + end + + def close + terminate + end + + private + + def initialize(dev) + raise RuntimeError.new('Do not instanciate this class directly.') + end + + def get_row(row) + raise NotImplementedError.new('Method get_row must be defined in a derived class.') + end + + def terminate + # Define if needed. + end + end + + + class StringReader < Reader + def initialize(string, fs = ',', rs = nil) + @fs = fs + @rs = rs + @dev = string + @idx = 0 + if @dev[0, 3] == "\xef\xbb\xbf" + @idx += 3 + end + end + + private + + def get_row(row) + parsed_cells, next_idx = CSV.parse_row(@dev, @idx, row, @fs, @rs) + if parsed_cells == 0 and next_idx == 0 and @idx != @dev.size + raise IllegalFormatError.new + end + @idx = next_idx + parsed_cells + end + end + + + class IOReader < Reader + def initialize(io, fs = ',', rs = nil) + @io = io + @fs = fs + @rs = rs + @dev = CSV::IOBuf.new(@io) + @idx = 0 + if @dev[0] == 0xef and @dev[1] == 0xbb and @dev[2] == 0xbf + @idx += 3 + end + @close_on_terminate = false + end + + # Tell this reader to close the IO when terminated (Triggered by invoking + # CSV::IOReader#close). + def close_on_terminate + @close_on_terminate = true + end + + private + + def get_row(row) + parsed_cells, next_idx = CSV.parse_row(@dev, @idx, row, @fs, @rs) + if parsed_cells == 0 and next_idx == 0 and !@dev.is_eos? + raise IllegalFormatError.new + end + dropped = @dev.drop(next_idx) + @idx = next_idx - dropped + parsed_cells + end + + def terminate + if @close_on_terminate + @io.close + end + + if @dev + @dev.close + end + end + end + + + # CSV formatted string/stream writer. + # + # EXAMPLE + # Write rows to 'csvout' file. + # + # outfile = File.open('csvout', 'wb') + # CSV::Writer.generate(outfile) do |csv| + # csv << ['c1', nil, '', '"', "\r\n", 'c2'] + # ... + # end + # + # outfile.close + # + class Writer + # Given block is called with the writer instance. str_or_writable must + # handle '<<(string)'. + def Writer.generate(str_or_writable, fs = ',', rs = nil, &block) + writer = Writer.create(str_or_writable, fs, rs) + if block + yield(writer) + writer.close + nil + else + writer + end + end + + # str_or_writable must handle '<<(string)'. + def Writer.create(str_or_writable, fs = ',', rs = nil) + BasicWriter.new(str_or_writable, fs, rs) + end + + # dump CSV stream to the device. argument must be an Array of String. + def <<(row) + CSV.generate_row(row, row.size, @dev, @fs, @rs) + self + end + alias add_row << + + def close + terminate + end + + private + + def initialize(dev) + raise RuntimeError.new('Do not instanciate this class directly.') + end + + def terminate + # Define if needed. + end + end + + + class BasicWriter < Writer + def initialize(str_or_writable, fs = ',', rs = nil) + @fs = fs + @rs = rs + @dev = str_or_writable + @close_on_terminate = false + end + + # Tell this writer to close the IO when terminated (Triggered by invoking + # CSV::BasicWriter#close). + def close_on_terminate + @close_on_terminate = true + end + + private + + def terminate + if @close_on_terminate + @dev.close + end + end + end + +private + + # Buffered stream. + # + # EXAMPLE 1 -- an IO. + # class MyBuf < StreamBuf + # # Do initialize myself before a super class. Super class might call my + # # method 'read'. (Could be awful for C++ user. :-) + # def initialize(s) + # @s = s + # super() + # end + # + # # define my own 'read' method. + # # CAUTION: Returning nil means EnfOfStream. + # def read(size) + # @s.read(size) + # end + # + # # release buffers. in Ruby which has GC, you do not have to call this... + # def terminate + # @s = nil + # super() + # end + # end + # + # buf = MyBuf.new(STDIN) + # my_str = '' + # p buf[0, 0] # => '' (null string) + # p buf[0] # => 97 (char code of 'a') + # p buf[0, 1] # => 'a' + # my_str = buf[0, 5] + # p my_str # => 'abcde' (5 chars) + # p buf[0, 6] # => "abcde\n" (6 chars) + # p buf[0, 7] # => "abcde\n" (6 chars) + # p buf.drop(3) # => 3 (dropped chars) + # p buf.get(0, 2) # => 'de' (2 chars) + # p buf.is_eos? # => false (is not EOS here) + # p buf.drop(5) # => 3 (dropped chars) + # p buf.is_eos? # => true (is EOS here) + # p buf[0] # => nil (is EOS here) + # + # EXAMPLE 2 -- String. + # This is a conceptual example. No pros with this. + # + # class StrBuf < StreamBuf + # def initialize(s) + # @str = s + # @idx = 0 + # super() + # end + # + # def read(size) + # str = @str[@idx, size] + # @idx += str.size + # str + # end + # end + # + class StreamBuf + # get a char or a partial string from the stream. + # idx: index of a string to specify a start point of a string to get. + # unlike String instance, idx < 0 returns nil. + # n: size of a string to get. + # returns char at idx if n == nil. + # returns a partial string, from idx to (idx + n) if n != nil. at EOF, + # the string size could not equal to arg n. + def [](idx, n = nil) + if idx < 0 + return nil + end + if (idx_is_eos?(idx)) + if n and (@offset + idx == buf_size(@cur_buf)) + # Like a String, 'abc'[4, 1] returns nil and + # 'abc'[3, 1] returns '' not nil. + return '' + else + return nil + end + end + my_buf = @cur_buf + my_offset = @offset + next_idx = idx + while (my_offset + next_idx >= buf_size(my_buf)) + if (my_buf == @buf_tail_idx) + unless add_buf + break + end + end + next_idx = my_offset + next_idx - buf_size(my_buf) + my_buf += 1 + my_offset = 0 + end + loc = my_offset + next_idx + if !n + return @buf_list[my_buf][loc] # Fixnum of char code. + elsif (loc + n - 1 < buf_size(my_buf)) + return @buf_list[my_buf][loc, n] # String. + else # should do loop insted of (tail) recursive call... + res = @buf_list[my_buf][loc, BufSize] + size_added = buf_size(my_buf) - loc + if size_added > 0 + idx += size_added + n -= size_added + ret = self[idx, n] + if ret + res << ret + end + end + return res + end + end + alias get [] + + # drop a string from the stream. + # returns dropped size. at EOF, dropped size might not equals to arg n. + # Once you drop the head of the stream, access to the dropped part via [] + # or get returns nil. + def drop(n) + if is_eos? + return 0 + end + size_dropped = 0 + while (n > 0) + if !@is_eos or (@cur_buf != @buf_tail_idx) + if (@offset + n < buf_size(@cur_buf)) + size_dropped += n + @offset += n + n = 0 + else + size = buf_size(@cur_buf) - @offset + size_dropped += size + n -= size + @offset = 0 + unless rel_buf + unless add_buf + break + end + @cur_buf = @buf_tail_idx + end + end + end + end + size_dropped + end + + def is_eos? + return idx_is_eos?(0) + end + + # WARN: Do not instantiate this class directly. Define your own class + # which derives this class and define 'read' instance method. + def initialize + @buf_list = [] + @cur_buf = @buf_tail_idx = -1 + @offset = 0 + @is_eos = false + add_buf + @cur_buf = @buf_tail_idx + end + + protected + + def terminate + while (rel_buf); end + end + + # protected method 'read' must be defined in derived classes. + # CAUTION: Returning a string which size is not equal to 'size' means + # EnfOfStream. When it is not at EOS, you must block the callee, try to + # read and return the sized string. + def read(size) # raise EOFError + raise NotImplementedError.new('Method read must be defined in a derived class.') + end + + private + + def buf_size(idx) + @buf_list[idx].size + end + + def add_buf + if @is_eos + return false + end + begin + str_read = read(BufSize) + rescue EOFError + str_read = nil + rescue + terminate + raise + end + if str_read.nil? + @is_eos = true + @buf_list.push('') + @buf_tail_idx += 1 + false + else + @buf_list.push(str_read) + @buf_tail_idx += 1 + true + end + end + + def rel_buf + if (@cur_buf < 0) + return false + end + @buf_list[@cur_buf] = nil + if (@cur_buf == @buf_tail_idx) + @cur_buf = -1 + return false + else + @cur_buf += 1 + return true + end + end + + def idx_is_eos?(idx) + (@is_eos and ((@cur_buf < 0) or (@cur_buf == @buf_tail_idx))) + end + + BufSize = 1024 * 8 + end + + # Buffered IO. + # + # EXAMPLE + # # File 'bigdata' could be a giga-byte size one! + # buf = CSV::IOBuf.new(File.open('bigdata', 'rb')) + # CSV::Reader.new(buf).each do |row| + # p row + # break if row[0].data == 'admin' + # end + # + class IOBuf < StreamBuf + def initialize(s) + @s = s + super() + end + + def close + terminate + end + + private + + def read(size) + @s.read(size) + end + + def terminate + super() + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/date.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/date.rb new file mode 100644 index 0000000000..6da4d6aa8e --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/date.rb @@ -0,0 +1,1708 @@ +# +# date.rb - date and time library +# +# Author: Tadayoshi Funaba 1998-2006 +# +# Documentation: William Webber +# +#-- +# $Id: date.rb,v 2.30 2006-12-30 21:43:41+09 tadf Exp $ +#++ +# +# == Overview +# +# This file provides two classes for working with +# dates and times. +# +# The first class, Date, represents dates. +# It works with years, months, weeks, and days. +# See the Date class documentation for more details. +# +# The second, DateTime, extends Date to include hours, +# minutes, seconds, and fractions of a second. It +# provides basic support for time zones. See the +# DateTime class documentation for more details. +# +# === Ways of calculating the date. +# +# In common usage, the date is reckoned in years since or +# before the Common Era (CE/BCE, also known as AD/BC), then +# as a month and day-of-the-month within the current year. +# This is known as the *Civil* *Date*, and abbreviated +# as +civil+ in the Date class. +# +# Instead of year, month-of-the-year, and day-of-the-month, +# the date can also be reckoned in terms of year and +# day-of-the-year. This is known as the *Ordinal* *Date*, +# and is abbreviated as +ordinal+ in the Date class. (Note +# that referring to this as the Julian date is incorrect.) +# +# The date can also be reckoned in terms of year, week-of-the-year, +# and day-of-the-week. This is known as the *Commercial* +# *Date*, and is abbreviated as +commercial+ in the +# Date class. The commercial week runs Monday (day-of-the-week +# 1) to Sunday (day-of-the-week 7), in contrast to the civil +# week which runs Sunday (day-of-the-week 0) to Saturday +# (day-of-the-week 6). The first week of the commercial year +# starts on the Monday on or before January 1, and the commercial +# year itself starts on this Monday, not January 1. +# +# For scientific purposes, it is convenient to refer to a date +# simply as a day count, counting from an arbitrary initial +# day. The date first chosen for this was January 1, 4713 BCE. +# A count of days from this date is the *Julian* *Day* *Number* +# or *Julian* *Date*, which is abbreviated as +jd+ in the +# Date class. This is in local time, and counts from midnight +# on the initial day. The stricter usage is in UTC, and counts +# from midday on the initial day. This is referred to in the +# Date class as the *Astronomical* *Julian* *Day* *Number*, and +# abbreviated as +ajd+. In the Date class, the Astronomical +# Julian Day Number includes fractional days. +# +# Another absolute day count is the *Modified* *Julian* *Day* +# *Number*, which takes November 17, 1858 as its initial day. +# This is abbreviated as +mjd+ in the Date class. There +# is also an *Astronomical* *Modified* *Julian* *Day* *Number*, +# which is in UTC and includes fractional days. This is +# abbreviated as +amjd+ in the Date class. Like the Modified +# Julian Day Number (and unlike the Astronomical Julian +# Day Number), it counts from midnight. +# +# Alternative calendars such as the Chinese Lunar Calendar, +# the Islamic Calendar, or the French Revolutionary Calendar +# are not supported by the Date class; nor are calendars that +# are based on an Era different from the Common Era, such as +# the Japanese Imperial Calendar or the Republic of China +# Calendar. +# +# === Calendar Reform +# +# The standard civil year is 365 days long. However, the +# solar year is fractionally longer than this. To account +# for this, a *leap* *year* is occasionally inserted. This +# is a year with 366 days, the extra day falling on February 29. +# In the early days of the civil calendar, every fourth +# year without exception was a leap year. This way of +# reckoning leap years is the *Julian* *Calendar*. +# +# However, the solar year is marginally shorter than 365 1/4 +# days, and so the *Julian* *Calendar* gradually ran slow +# over the centuries. To correct this, every 100th year +# (but not every 400th year) was excluded as a leap year. +# This way of reckoning leap years, which we use today, is +# the *Gregorian* *Calendar*. +# +# The Gregorian Calendar was introduced at different times +# in different regions. The day on which it was introduced +# for a particular region is the *Day* *of* *Calendar* +# *Reform* for that region. This is abbreviated as +sg+ +# (for Start of Gregorian calendar) in the Date class. +# +# Two such days are of particular +# significance. The first is October 15, 1582, which was +# the Day of Calendar Reform for Italy and most Catholic +# countries. The second is September 14, 1752, which was +# the Day of Calendar Reform for England and its colonies +# (including what is now the United States). These two +# dates are available as the constants Date::ITALY and +# Date::ENGLAND, respectively. (By comparison, Germany and +# Holland, less Catholic than Italy but less stubborn than +# England, changed over in 1698; Sweden in 1753; Russia not +# till 1918, after the Revolution; and Greece in 1923. Many +# Orthodox churches still use the Julian Calendar. A complete +# list of Days of Calendar Reform can be found at +# http://www.polysyllabic.com/GregConv.html.) +# +# Switching from the Julian to the Gregorian calendar +# involved skipping a number of days to make up for the +# accumulated lag, and the later the switch was (or is) +# done, the more days need to be skipped. So in 1582 in Italy, +# 4th October was followed by 15th October, skipping 10 days; in 1752 +# in England, 2nd September was followed by 14th September, skipping +# 11 days; and if I decided to switch from Julian to Gregorian +# Calendar this midnight, I would go from 27th July 2003 (Julian) +# today to 10th August 2003 (Gregorian) tomorrow, skipping +# 13 days. The Date class is aware of this gap, and a supposed +# date that would fall in the middle of it is regarded as invalid. +# +# The Day of Calendar Reform is relevant to all date representations +# involving years. It is not relevant to the Julian Day Numbers, +# except for converting between them and year-based representations. +# +# In the Date and DateTime classes, the Day of Calendar Reform or +# +sg+ can be specified a number of ways. First, it can be as +# the Julian Day Number of the Day of Calendar Reform. Second, +# it can be using the constants Date::ITALY or Date::ENGLAND; these +# are in fact the Julian Day Numbers of the Day of Calendar Reform +# of the respective regions. Third, it can be as the constant +# Date::JULIAN, which means to always use the Julian Calendar. +# Finally, it can be as the constant Date::GREGORIAN, which means +# to always use the Gregorian Calendar. +# +# Note: in the Julian Calendar, New Years Day was March 25. The +# Date class does not follow this convention. +# +# === Time Zones +# +# DateTime objects support a simple representation +# of time zones. Time zones are represented as an offset +# from UTC, as a fraction of a day. This offset is the +# how much local time is later (or earlier) than UTC. +# UTC offset 0 is centred on England (also known as GMT). +# As you travel east, the offset increases until you +# reach the dateline in the middle of the Pacific Ocean; +# as you travel west, the offset decreases. This offset +# is abbreviated as +of+ in the Date class. +# +# This simple representation of time zones does not take +# into account the common practice of Daylight Savings +# Time or Summer Time. +# +# Most DateTime methods return the date and the +# time in local time. The two exceptions are +# #ajd() and #amjd(), which return the date and time +# in UTC time, including fractional days. +# +# The Date class does not support time zone offsets, in that +# there is no way to create a Date object with a time zone. +# However, methods of the Date class when used by a +# DateTime instance will use the time zone offset of this +# instance. +# +# == Examples of use +# +# === Print out the date of every Sunday between two dates. +# +# def print_sundays(d1, d2) +# d1 +=1 while (d1.wday != 0) +# d1.step(d2, 7) do |date| +# puts "#{Date::MONTHNAMES[date.mon]} #{date.day}" +# end +# end +# +# print_sundays(Date::civil(2003, 4, 8), Date::civil(2003, 5, 23)) +# +# === Calculate how many seconds to go till midnight on New Year's Day. +# +# def secs_to_new_year(now = DateTime::now()) +# new_year = DateTime.new(now.year + 1, 1, 1) +# dif = new_year - now +# hours, mins, secs, ignore_fractions = Date::day_fraction_to_time(dif) +# return hours * 60 * 60 + mins * 60 + secs +# end +# +# puts secs_to_new_year() + +require 'rational' +require 'date/format' + +# Class representing a date. +# +# See the documentation to the file date.rb for an overview. +# +# Internally, the date is represented as an Astronomical +# Julian Day Number, +ajd+. The Day of Calendar Reform, +sg+, is +# also stored, for conversions to other date formats. (There +# is also an +of+ field for a time zone offset, but this +# is only for the use of the DateTime subclass.) +# +# A new Date object is created using one of the object creation +# class methods named after the corresponding date format, and the +# arguments appropriate to that date format; for instance, +# Date::civil() (aliased to Date::new()) with year, month, +# and day-of-month, or Date::ordinal() with year and day-of-year. +# All of these object creation class methods also take the +# Day of Calendar Reform as an optional argument. +# +# Date objects are immutable once created. +# +# Once a Date has been created, date values +# can be retrieved for the different date formats supported +# using instance methods. For instance, #mon() gives the +# Civil month, #cwday() gives the Commercial day of the week, +# and #yday() gives the Ordinal day of the year. Date values +# can be retrieved in any format, regardless of what format +# was used to create the Date instance. +# +# The Date class includes the Comparable module, allowing +# date objects to be compared and sorted, ranges of dates +# to be created, and so forth. +class Date + + include Comparable + + # Full month names, in English. Months count from 1 to 12; a + # month's numerical representation indexed into this array + # gives the name of that month (hence the first element is nil). + MONTHNAMES = [nil] + %w(January February March April May June July + August September October November December) + + # Full names of days of the week, in English. Days of the week + # count from 0 to 6 (except in the commercial week); a day's numerical + # representation indexed into this array gives the name of that day. + DAYNAMES = %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday) + + # Abbreviated month names, in English. + ABBR_MONTHNAMES = [nil] + %w(Jan Feb Mar Apr May Jun + Jul Aug Sep Oct Nov Dec) + + # Abbreviated day names, in English. + ABBR_DAYNAMES = %w(Sun Mon Tue Wed Thu Fri Sat) + + [MONTHNAMES, DAYNAMES, ABBR_MONTHNAMES, ABBR_DAYNAMES].each do |xs| + xs.each{|x| x.freeze}.freeze + end + + class Infinity < Numeric # :nodoc: + + include Comparable + + def initialize(d=1) @d = d <=> 0 end + + def d() @d end + + protected :d + + def zero? () false end + def finite? () false end + def infinite? () d.nonzero? end + def nan? () d.zero? end + + def abs() self.class.new end + + def -@ () self.class.new(-d) end + def +@ () self.class.new(+d) end + + def <=> (other) + case other + when Infinity; d <=> other.d + when Numeric; d + else + begin + l, r = other.coerce(self) + return l <=> r + rescue NoMethodError + end + end + nil + end + + def coerce(other) + case other + when Numeric; return -d, d + else + super + end + end + + end + + # The Julian Day Number of the Day of Calendar Reform for Italy + # and the Catholic countries. + ITALY = 2299161 # 1582-10-15 + + # The Julian Day Number of the Day of Calendar Reform for England + # and her Colonies. + ENGLAND = 2361222 # 1752-09-14 + + # A constant used to indicate that a Date should always use the + # Julian calendar. + JULIAN = Infinity.new + + # A constant used to indicate that a Date should always use the + # Gregorian calendar. + GREGORIAN = -Infinity.new + + UNIXEPOCH = 2440588 # 1970-01-01 :nodoc: + + # Does a given Julian Day Number fall inside the old-style (Julian) + # calendar? + # + # +jd+ is the Julian Day Number in question. +sg+ may be Date::GREGORIAN, + # in which case the answer is false; it may be Date::JULIAN, in which case + # the answer is true; or it may a number representing the Day of + # Calendar Reform. Date::ENGLAND and Date::ITALY are two possible such + # days. + + def self.julian? (jd, sg) + case sg + when Numeric + jd < sg + else + if $VERBOSE + warn("#{caller.shift.sub(/:in .*/, '')}: " \ +"warning: do not use non-numerical object as julian day number anymore") + end + not sg + end + end + + # Does a given Julian Day Number fall inside the new-style (Gregorian) + # calendar? + # + # The reverse of self.os? See the documentation for that method for + # more details. + def self.gregorian? (jd, sg) !julian?(jd, sg) end + + def self.fix_style(jd, sg) # :nodoc: + if julian?(jd, sg) + then JULIAN + else GREGORIAN end + end + + private_class_method :fix_style + + # Convert an Ordinal Date to a Julian Day Number. + # + # +y+ and +d+ are the year and day-of-year to convert. + # +sg+ specifies the Day of Calendar Reform. + # + # Returns the corresponding Julian Day Number. + def self.ordinal_to_jd(y, d, sg=GREGORIAN) + civil_to_jd(y, 1, d, sg) + end + + # Convert a Julian Day Number to an Ordinal Date. + # + # +jd+ is the Julian Day Number to convert. + # +sg+ specifies the Day of Calendar Reform. + # + # Returns the corresponding Ordinal Date as + # [year, day_of_year] + def self.jd_to_ordinal(jd, sg=GREGORIAN) + y = jd_to_civil(jd, sg)[0] + doy = jd - civil_to_jd(y - 1, 12, 31, fix_style(jd, sg)) + return y, doy + end + + # Convert a Civil Date to a Julian Day Number. + # +y+, +m+, and +d+ are the year, month, and day of the + # month. +sg+ specifies the Day of Calendar Reform. + # + # Returns the corresponding Julian Day Number. + def self.civil_to_jd(y, m, d, sg=GREGORIAN) + if m <= 2 + y -= 1 + m += 12 + end + a = (y / 100.0).floor + b = 2 - a + (a / 4.0).floor + jd = (365.25 * (y + 4716)).floor + + (30.6001 * (m + 1)).floor + + d + b - 1524 + if julian?(jd, sg) + jd -= b + end + jd + end + + # Convert a Julian Day Number to a Civil Date. +jd+ is + # the Julian Day Number. +sg+ specifies the Day of + # Calendar Reform. + # + # Returns the corresponding [year, month, day_of_month] + # as a three-element array. + def self.jd_to_civil(jd, sg=GREGORIAN) + if julian?(jd, sg) + a = jd + else + x = ((jd - 1867216.25) / 36524.25).floor + a = jd + 1 + x - (x / 4.0).floor + end + b = a + 1524 + c = ((b - 122.1) / 365.25).floor + d = (365.25 * c).floor + e = ((b - d) / 30.6001).floor + dom = b - d - (30.6001 * e).floor + if e <= 13 + m = e - 1 + y = c - 4716 + else + m = e - 13 + y = c - 4715 + end + return y, m, dom + end + + # Convert a Commercial Date to a Julian Day Number. + # + # +y+, +w+, and +d+ are the (commercial) year, week of the year, + # and day of the week of the Commercial Date to convert. + # +sg+ specifies the Day of Calendar Reform. + def self.commercial_to_jd(y, w, d, ns=GREGORIAN) + jd = civil_to_jd(y, 1, 4, ns) + (jd - (((jd - 1) + 1) % 7)) + + 7 * (w - 1) + + (d - 1) + end + + # Convert a Julian Day Number to a Commercial Date + # + # +jd+ is the Julian Day Number to convert. + # +sg+ specifies the Day of Calendar Reform. + # + # Returns the corresponding Commercial Date as + # [commercial_year, week_of_year, day_of_week] + def self.jd_to_commercial(jd, sg=GREGORIAN) + ns = fix_style(jd, sg) + a = jd_to_civil(jd - 3, ns)[0] + y = if jd >= commercial_to_jd(a + 1, 1, 1, ns) then a + 1 else a end + w = 1 + ((jd - commercial_to_jd(y, 1, 1, ns)) / 7).floor + d = (jd + 1) % 7 + d = 7 if d == 0 + return y, w, d + end + + def self.weeknum_to_jd(y, w, d, f=0, ns=GREGORIAN) # :nodoc: + a = civil_to_jd(y, 1, 1, ns) + 6 + (a - ((a - f) + 1) % 7 - 7) + 7 * w + d + end + + def self.jd_to_weeknum(jd, f=0, sg=GREGORIAN) # :nodoc: + ns = fix_style(jd, sg) + y, m, d = jd_to_civil(jd, ns) + a = civil_to_jd(y, 1, 1, ns) + 6 + w, d = (jd - (a - ((a - f) + 1) % 7) + 7).divmod(7) + return y, w, d + end + + private_class_method :weeknum_to_jd, :jd_to_weeknum + + # Convert an Astronomical Julian Day Number to a (civil) Julian + # Day Number. + # + # +ajd+ is the Astronomical Julian Day Number to convert. + # +of+ is the offset from UTC as a fraction of a day (defaults to 0). + # + # Returns the (civil) Julian Day Number as [day_number, + # fraction] where +fraction+ is always 1/2. + def self.ajd_to_jd(ajd, of=0) (ajd + of + 1.to_r/2).divmod(1) end + + # Convert a (civil) Julian Day Number to an Astronomical Julian + # Day Number. + # + # +jd+ is the Julian Day Number to convert, and +fr+ is a + # fractional day. + # +of+ is the offset from UTC as a fraction of a day (defaults to 0). + # + # Returns the Astronomical Julian Day Number as a single + # numeric value. + def self.jd_to_ajd(jd, fr, of=0) jd + fr - of - 1.to_r/2 end + + # Convert a fractional day +fr+ to [hours, minutes, seconds, + # fraction_of_a_second] + def self.day_fraction_to_time(fr) + h, fr = fr.divmod(1.to_r/24) + min, fr = fr.divmod(1.to_r/1440) + s, fr = fr.divmod(1.to_r/86400) + return h, min, s, fr + end + + # Convert an +h+ hour, +min+ minutes, +s+ seconds period + # to a fractional day. + def self.time_to_day_fraction(h, min, s) + h.to_r/24 + min.to_r/1440 + s.to_r/86400 + end + + # Convert an Astronomical Modified Julian Day Number to an + # Astronomical Julian Day Number. + def self.amjd_to_ajd(amjd) amjd + 4800001.to_r/2 end + + # Convert an Astronomical Julian Day Number to an + # Astronomical Modified Julian Day Number. + def self.ajd_to_amjd(ajd) ajd - 4800001.to_r/2 end + + # Convert a Modified Julian Day Number to a Julian + # Day Number. + def self.mjd_to_jd(mjd) mjd + 2400001 end + + # Convert a Julian Day Number to a Modified Julian Day + # Number. + def self.jd_to_mjd(jd) jd - 2400001 end + + # Convert a count of the number of days since the adoption + # of the Gregorian Calendar (in Italy) to a Julian Day Number. + def self.ld_to_jd(ld) ld + 2299160 end + + # Convert a Julian Day Number to the number of days since + # the adoption of the Gregorian Calendar (in Italy). + def self.jd_to_ld(jd) jd - 2299160 end + + # Convert a Julian Day Number to the day of the week. + # + # Sunday is day-of-week 0; Saturday is day-of-week 6. + def self.jd_to_wday(jd) (jd + 1) % 7 end + + # Is a year a leap year in the Julian calendar? + # + # All years divisible by 4 are leap years in the Julian calendar. + def self.julian_leap? (y) y % 4 == 0 end + + # Is a year a leap year in the Gregorian calendar? + # + # All years divisible by 4 are leap years in the Gregorian calendar, + # except for years divisible by 100 and not by 400. + def self.gregorian_leap? (y) y % 4 == 0 && y % 100 != 0 || y % 400 == 0 end + + class << self; alias_method :leap?, :gregorian_leap? end + class << self; alias_method :new!, :new end + + # Is +jd+ a valid Julian Day Number? + # + # If it is, returns it. In fact, any value is treated as a valid + # Julian Day Number. + def self.valid_jd? (jd, sg=ITALY) jd end + + # Do the year +y+ and day-of-year +d+ make a valid Ordinal Date? + # Returns the corresponding Julian Day Number if they do, or + # nil if they don't. + # + # +d+ can be a negative number, in which case it counts backwards + # from the end of the year (-1 being the last day of the year). + # No year wraparound is performed, however, so valid values of + # +d+ are -365 .. -1, 1 .. 365 on a non-leap-year, + # -366 .. -1, 1 .. 366 on a leap year. + # A date falling in the period skipped in the Day of Calendar Reform + # adjustment is not valid. + # + # +sg+ specifies the Day of Calendar Reform. + def self.valid_ordinal? (y, d, sg=ITALY) + if d < 0 + ny, = (y + 1).divmod(1) + jd = ordinal_to_jd(ny, d + 1, sg) + ns = fix_style(jd, sg) + return unless [y] == jd_to_ordinal(jd, sg)[0..0] + return unless [ny, 1] == jd_to_ordinal(jd - d, ns) + else + jd = ordinal_to_jd(y, d, sg) + return unless [y, d] == jd_to_ordinal(jd, sg) + end + jd + end + + # Do year +y+, month +m+, and day-of-month +d+ make a + # valid Civil Date? Returns the corresponding Julian + # Day Number if they do, nil if they don't. + # + # +m+ and +d+ can be negative, in which case they count + # backwards from the end of the year and the end of the + # month respectively. No wraparound is performed, however, + # and invalid values cause an ArgumentError to be raised. + # A date falling in the period skipped in the Day of Calendar + # Reform adjustment is not valid. + # + # +sg+ specifies the Day of Calendar Reform. + def self.valid_civil? (y, m, d, sg=ITALY) + if m < 0 + m += 13 + end + if d < 0 + ny, nm = (y * 12 + m).divmod(12) + nm, = (nm + 1).divmod(1) + jd = civil_to_jd(ny, nm, d + 1, sg) + ns = fix_style(jd, sg) + return unless [y, m] == jd_to_civil(jd, sg)[0..1] + return unless [ny, nm, 1] == jd_to_civil(jd - d, ns) + else + jd = civil_to_jd(y, m, d, sg) + return unless [y, m, d] == jd_to_civil(jd, sg) + end + jd + end + + class << self; alias_method :valid_date?, :valid_civil? end + + # Do year +y+, week-of-year +w+, and day-of-week +d+ make a + # valid Commercial Date? Returns the corresponding Julian + # Day Number if they do, nil if they don't. + # + # Monday is day-of-week 1; Sunday is day-of-week 7. + # + # +w+ and +d+ can be negative, in which case they count + # backwards from the end of the year and the end of the + # week respectively. No wraparound is performed, however, + # and invalid values cause an ArgumentError to be raised. + # A date falling in the period skipped in the Day of Calendar + # Reform adjustment is not valid. + # + # +sg+ specifies the Day of Calendar Reform. + def self.valid_commercial? (y, w, d, sg=ITALY) + if d < 0 + d += 8 + end + if w < 0 + ny, nw, nd = + jd_to_commercial(commercial_to_jd(y + 1, 1, 1) + w * 7) + return unless ny == y + w = nw + end + jd = commercial_to_jd(y, w, d) + return unless gregorian?(jd, sg) + return unless [y, w, d] == jd_to_commercial(jd) + jd + end + + def self.valid_weeknum? (y, w, d, f, sg=ITALY) # :nodoc: + if d < 0 + d += 7 + end + if w < 0 + ny, nw, nd, nf = + jd_to_weeknum(weeknum_to_jd(y + 1, 1, f, f) + w * 7, f) + return unless ny == y + w = nw + end + jd = weeknum_to_jd(y, w, d, f) + return unless gregorian?(jd, sg) + return unless [y, w, d] == jd_to_weeknum(jd, f) + jd + end + + private_class_method :valid_weeknum? + + # Do hour +h+, minute +min+, and second +s+ constitute a valid time? + # + # If they do, returns their value as a fraction of a day. If not, + # returns nil. + # + # The 24-hour clock is used. Negative values of +h+, +min+, and + # +sec+ are treating as counting backwards from the end of the + # next larger unit (e.g. a +min+ of -2 is treated as 58). No + # wraparound is performed. + def self.valid_time? (h, min, s) + h += 24 if h < 0 + min += 60 if min < 0 + s += 60 if s < 0 + return unless ((0..23) === h && + (0..59) === min && + (0..59) === s) || + (24 == h && + 0 == min && + 0 == s) + time_to_day_fraction(h, min, s) + end + + # Create a new Date object from a Julian Day Number. + # + # +jd+ is the Julian Day Number; if not specified, it defaults to + # 0. + # +sg+ specifies the Day of Calendar Reform. + def self.jd(jd=0, sg=ITALY) + jd = valid_jd?(jd, sg) + new!(jd_to_ajd(jd, 0, 0), 0, sg) + end + + # Create a new Date object from an Ordinal Date, specified + # by year +y+ and day-of-year +d+. +d+ can be negative, + # in which it counts backwards from the end of the year. + # No year wraparound is performed, however. An invalid + # value for +d+ results in an ArgumentError being raised. + # + # +y+ defaults to -4712, and +d+ to 1; this is Julian Day + # Number day 0. + # + # +sg+ specifies the Day of Calendar Reform. + def self.ordinal(y=-4712, d=1, sg=ITALY) + unless jd = valid_ordinal?(y, d, sg) + raise ArgumentError, 'invalid date' + end + new!(jd_to_ajd(jd, 0, 0), 0, sg) + end + + # Create a new Date object for the Civil Date specified by + # year +y+, month +m+, and day-of-month +d+. + # + # +m+ and +d+ can be negative, in which case they count + # backwards from the end of the year and the end of the + # month respectively. No wraparound is performed, however, + # and invalid values cause an ArgumentError to be raised. + # can be negative + # + # +y+ defaults to -4712, +m+ to 1, and +d+ to 1; this is + # Julian Day Number day 0. + # + # +sg+ specifies the Day of Calendar Reform. + def self.civil(y=-4712, m=1, d=1, sg=ITALY) + unless jd = valid_civil?(y, m, d, sg) + raise ArgumentError, 'invalid date' + end + new!(jd_to_ajd(jd, 0, 0), 0, sg) + end + + class << self; alias_method :new, :civil end + + # Create a new Date object for the Commercial Date specified by + # year +y+, week-of-year +w+, and day-of-week +d+. + # + # Monday is day-of-week 1; Sunday is day-of-week 7. + # + # +w+ and +d+ can be negative, in which case they count + # backwards from the end of the year and the end of the + # week respectively. No wraparound is performed, however, + # and invalid values cause an ArgumentError to be raised. + # + # +y+ defaults to 1582, +w+ to 41, and +d+ to 5, the Day of + # Calendar Reform for Italy and the Catholic countries. + # + # +sg+ specifies the Day of Calendar Reform. + def self.commercial(y=1582, w=41, d=5, sg=ITALY) + unless jd = valid_commercial?(y, w, d, sg) + raise ArgumentError, 'invalid date' + end + new!(jd_to_ajd(jd, 0, 0), 0, sg) + end + + def self.weeknum(y=1582, w=41, d=5, f=0, sg=ITALY) # :nodoc: + unless jd = valid_weeknum?(y, w, d, f, sg) + raise ArgumentError, 'invalid date' + end + new!(jd_to_ajd(jd, 0, 0), 0, sg) + end + + private_class_method :weeknum + + def self.rewrite_frags(elem) # :nodoc: + elem ||= {} + if seconds = elem[:seconds] + d, fr = seconds.divmod(86400) + h, fr = fr.divmod(3600) + min, fr = fr.divmod(60) + s, fr = fr.divmod(1) + elem[:jd] = UNIXEPOCH + d + elem[:hour] = h + elem[:min] = min + elem[:sec] = s + elem[:sec_fraction] = fr + elem.delete(:seconds) + elem.delete(:offset) + end + elem + end + + private_class_method :rewrite_frags + + def self.complete_frags(elem) # :nodoc: + i = 0 + g = [[:time, [:hour, :min, :sec]], + [nil, [:jd]], + [:ordinal, [:year, :yday, :hour, :min, :sec]], + [:civil, [:year, :mon, :mday, :hour, :min, :sec]], + [:commercial, [:cwyear, :cweek, :cwday, :hour, :min, :sec]], + [:wday, [:wday, :hour, :min, :sec]], + [:wnum0, [:year, :wnum0, :wday, :hour, :min, :sec]], + [:wnum1, [:year, :wnum1, :wday, :hour, :min, :sec]], + [nil, [:cwyear, :cweek, :wday, :hour, :min, :sec]], + [nil, [:year, :wnum0, :cwday, :hour, :min, :sec]], + [nil, [:year, :wnum1, :cwday, :hour, :min, :sec]]]. + collect{|k, a| e = elem.values_at(*a).compact; [k, a, e]}. + select{|k, a, e| e.size > 0}. + sort_by{|k, a, e| [e.size, i -= 1]}.last + + d = nil + + if g && g[0] && (g[1].size - g[2].size) != 0 + d ||= Date.today + + case g[0] + when :ordinal + elem[:year] ||= d.year + elem[:yday] ||= 1 + when :civil + g[1].each do |e| + break if elem[e] + elem[e] = d.__send__(e) + end + elem[:mon] ||= 1 + elem[:mday] ||= 1 + when :commercial + g[1].each do |e| + break if elem[e] + elem[e] = d.__send__(e) + end + elem[:cweek] ||= 1 + elem[:cwday] ||= 1 + when :wday + elem[:jd] ||= (d - d.wday + elem[:wday]).jd + when :wnum0 + g[1].each do |e| + break if elem[e] + elem[e] = d.__send__(e) + end + elem[:wnum0] ||= 0 + elem[:wday] ||= 0 + when :wnum1 + g[1].each do |e| + break if elem[e] + elem[e] = d.__send__(e) + end + elem[:wnum1] ||= 0 + elem[:wday] ||= 0 + end + end + + if g && g[0] == :time + if self <= DateTime + d ||= Date.today + elem[:jd] ||= d.jd + end + end + + elem[:hour] ||= 0 + elem[:min] ||= 0 + elem[:sec] ||= 0 + elem[:sec] = [elem[:sec], 59].min + + elem + end + + private_class_method :complete_frags + + def self.valid_date_frags?(elem, sg) # :nodoc: + catch :jd do + a = elem.values_at(:jd) + if a.all? + if jd = valid_jd?(*(a << sg)) + throw :jd, jd + end + end + + a = elem.values_at(:year, :yday) + if a.all? + if jd = valid_ordinal?(*(a << sg)) + throw :jd, jd + end + end + + a = elem.values_at(:year, :mon, :mday) + if a.all? + if jd = valid_civil?(*(a << sg)) + throw :jd, jd + end + end + + a = elem.values_at(:cwyear, :cweek, :cwday) + if a[2].nil? && elem[:wday] + a[2] = elem[:wday].nonzero? || 7 + end + if a.all? + if jd = valid_commercial?(*(a << sg)) + throw :jd, jd + end + end + + a = elem.values_at(:year, :wnum0, :wday) + if a[2].nil? && elem[:cwday] + a[2] = elem[:cwday] % 7 + end + if a.all? + if jd = valid_weeknum?(*(a << 0 << sg)) + throw :jd, jd + end + end + + a = elem.values_at(:year, :wnum1, :wday) + if a[2] + a[2] = (a[2] - 1) % 7 + end + if a[2].nil? && elem[:cwday] + a[2] = (elem[:cwday] - 1) % 7 + end + if a.all? + if jd = valid_weeknum?(*(a << 1 << sg)) + throw :jd, jd + end + end + end + end + + private_class_method :valid_date_frags? + + def self.valid_time_frags? (elem) # :nodoc: + h, min, s = elem.values_at(:hour, :min, :sec) + valid_time?(h, min, s) + end + + private_class_method :valid_time_frags? + + def self.new_by_frags(elem, sg) # :nodoc: + elem = rewrite_frags(elem) + elem = complete_frags(elem) + unless jd = valid_date_frags?(elem, sg) + raise ArgumentError, 'invalid date' + end + new!(jd_to_ajd(jd, 0, 0), 0, sg) + end + + private_class_method :new_by_frags + + # Create a new Date object by parsing from a String + # according to a specified format. + # + # +str+ is a String holding a date representation. + # +fmt+ is the format that the date is in. See + # date/format.rb for details on supported formats. + # + # The default +str+ is '-4712-01-01', and the default + # +fmt+ is '%F', which means Year-Month-Day_of_Month. + # This gives Julian Day Number day 0. + # + # +sg+ specifies the Day of Calendar Reform. + # + # An ArgumentError will be raised if +str+ cannot be + # parsed. + def self.strptime(str='-4712-01-01', fmt='%F', sg=ITALY) + elem = _strptime(str, fmt) + new_by_frags(elem, sg) + end + + # Create a new Date object by parsing from a String, + # without specifying the format. + # + # +str+ is a String holding a date representation. + # +comp+ specifies whether to interpret 2-digit years + # as 19XX (>= 69) or 20XX (< 69); the default is not to. + # The method will attempt to parse a date from the String + # using various heuristics; see #_parse in date/format.rb + # for more details. If parsing fails, an ArgumentError + # will be raised. + # + # The default +str+ is '-4712-01-01'; this is Julian + # Day Number day 0. + # + # +sg+ specifies the Day of Calendar Reform. + def self.parse(str='-4712-01-01', comp=false, sg=ITALY) + elem = _parse(str, comp) + new_by_frags(elem, sg) + end + + class << self + + def once(*ids) # :nodoc: + for id in ids + module_eval <<-"end;" + alias_method :__#{id.to_i}__, :#{id.to_s} + private :__#{id.to_i}__ + def #{id.to_s}(*args, &block) + (@__#{id.to_i}__ ||= [__#{id.to_i}__(*args, &block)])[0] + end + end; + end + end + + private :once + + end + + # *NOTE* this is the documentation for the method new!(). If + # you are reading this as the documentation for new(), that is + # because rdoc doesn't fully support the aliasing of the + # initialize() method. + # new() is in + # fact an alias for #civil(): read the documentation for that + # method instead. + # + # Create a new Date object. + # + # +ajd+ is the Astronomical Julian Day Number. + # +of+ is the offset from UTC as a fraction of a day. + # Both default to 0. + # + # +sg+ specifies the Day of Calendar Reform to use for this + # Date object. + # + # Using one of the factory methods such as Date::civil is + # generally easier and safer. + def initialize(ajd=0, of=0, sg=ITALY) @ajd, @of, @sg = ajd, of, sg end + + # Get the date as an Astronomical Julian Day Number. + def ajd() @ajd end + + # Get the date as an Astronomical Modified Julian Day Number. + def amjd() self.class.ajd_to_amjd(@ajd) end + + once :amjd + + # Get the date as a Julian Day Number. + def jd() self.class.ajd_to_jd(@ajd, @of)[0] end + + # Get any fractional day part of the date. + def day_fraction() self.class.ajd_to_jd(@ajd, @of)[1] end + + # Get the date as a Modified Julian Day Number. + def mjd() self.class.jd_to_mjd(jd) end + + # Get the date as the number of days since the Day of Calendar + # Reform (in Italy and the Catholic countries). + def ld() self.class.jd_to_ld(jd) end + + once :jd, :day_fraction, :mjd, :ld + + # Get the date as a Civil Date, [year, month, day_of_month] + def civil() self.class.jd_to_civil(jd, @sg) end # :nodoc: + + # Get the date as an Ordinal Date, [year, day_of_year] + def ordinal() self.class.jd_to_ordinal(jd, @sg) end # :nodoc: + + # Get the date as a Commercial Date, [year, week_of_year, day_of_week] + def commercial() self.class.jd_to_commercial(jd, @sg) end # :nodoc: + + def weeknum0() self.class.__send__(:jd_to_weeknum, jd, 0, @sg) end # :nodoc: + def weeknum1() self.class.__send__(:jd_to_weeknum, jd, 1, @sg) end # :nodoc: + + once :civil, :ordinal, :commercial, :weeknum0, :weeknum1 + private :civil, :ordinal, :commercial, :weeknum0, :weeknum1 + + # Get the year of this date. + def year() civil[0] end + + # Get the day-of-the-year of this date. + # + # January 1 is day-of-the-year 1 + def yday() ordinal[1] end + + # Get the month of this date. + # + # January is month 1. + def mon() civil[1] end + + # Get the day-of-the-month of this date. + def mday() civil[2] end + + alias_method :month, :mon + alias_method :day, :mday + + def wnum0() weeknum0[1] end # :nodoc: + def wnum1() weeknum1[1] end # :nodoc: + + private :wnum0, :wnum1 + + # Get the time of this date as [hours, minutes, seconds, + # fraction_of_a_second] + def time() self.class.day_fraction_to_time(day_fraction) end # :nodoc: + + once :time + private :time + + # Get the hour of this date. + def hour() time[0] end + + # Get the minute of this date. + def min() time[1] end + + # Get the second of this date. + def sec() time[2] end + + # Get the fraction-of-a-second of this date. The unit is in days. + # I do NOT recommend you to use this method. + def sec_fraction() time[3] end + + private :hour, :min, :sec, :sec_fraction + + def zone() strftime('%:z') end + + private :zone + + # Get the commercial year of this date. See *Commercial* *Date* + # in the introduction for how this differs from the normal year. + def cwyear() commercial[0] end + + # Get the commercial week of the year of this date. + def cweek() commercial[1] end + + # Get the commercial day of the week of this date. Monday is + # commercial day-of-week 1; Sunday is commercial day-of-week 7. + def cwday() commercial[2] end + + # Get the week day of this date. Sunday is day-of-week 0; + # Saturday is day-of-week 6. + def wday() self.class.jd_to_wday(jd) end + + once :wday + +=begin + MONTHNAMES.each_with_index do |n, i| + if n + define_method(n.downcase + '?'){mon == i} + end + end + + DAYNAMES.each_with_index do |n, i| + define_method(n.downcase + '?'){wday == i} + end +=end + + # Is the current date old-style (Julian Calendar)? + def julian? () self.class.julian?(jd, @sg) end + + # Is the current date new-style (Gregorian Calendar)? + def gregorian? () self.class.gregorian?(jd, @sg) end + + once :julian?, :gregorian? + + def fix_style # :nodoc: + if julian? + then self.class::JULIAN + else self.class::GREGORIAN end + end + + private :fix_style + + # Is this a leap year? + def leap? + self.class.jd_to_civil(self.class.civil_to_jd(year, 3, 1, fix_style) - 1, + fix_style)[-1] == 29 + end + + once :leap? + + # When is the Day of Calendar Reform for this Date object? + def start() @sg end + + # Create a copy of this Date object using a new Day of Calendar Reform. + def new_start(sg=self.class::ITALY) self.class.new!(@ajd, @of, sg) end + + # Create a copy of this Date object that uses the Italian/Catholic + # Day of Calendar Reform. + def italy() new_start(self.class::ITALY) end + + # Create a copy of this Date object that uses the English/Colonial + # Day of Calendar Reform. + def england() new_start(self.class::ENGLAND) end + + # Create a copy of this Date object that always uses the Julian + # Calendar. + def julian() new_start(self.class::JULIAN) end + + # Create a copy of this Date object that always uses the Gregorian + # Calendar. + def gregorian() new_start(self.class::GREGORIAN) end + + def offset() @of end + + def new_offset(of=0) + if String === of + of = (self.class.zone_to_diff(of) || 0).to_r/86400 + end + self.class.new!(@ajd, of, @sg) + end + + private :offset, :new_offset + + # Return a new Date object that is +n+ days later than the + # current one. + # + # +n+ may be a negative value, in which case the new Date + # is earlier than the current one; however, #-() might be + # more intuitive. + # + # If +n+ is not a Numeric, a TypeError will be thrown. In + # particular, two Dates cannot be added to each other. + def + (n) + case n + when Numeric; return self.class.new!(@ajd + n, @of, @sg) + end + raise TypeError, 'expected numeric' + end + + # If +x+ is a Numeric value, create a new Date object that is + # +x+ days earlier than the current one. + # + # If +x+ is a Date, return the number of days between the + # two dates; or, more precisely, how many days later the current + # date is than +x+. + # + # If +x+ is neither Numeric nor a Date, a TypeError is raised. + def - (x) + case x + when Numeric; return self.class.new!(@ajd - x, @of, @sg) + when Date; return @ajd - x.ajd + end + raise TypeError, 'expected numeric or date' + end + + # Compare this date with another date. + # + # +other+ can also be a Numeric value, in which case it is + # interpreted as an Astronomical Julian Day Number. + # + # Comparison is by Astronomical Julian Day Number, including + # fractional days. This means that both the time and the + # timezone offset are taken into account when comparing + # two DateTime instances. When comparing a DateTime instance + # with a Date instance, the time of the latter will be + # considered as falling on midnight UTC. + def <=> (other) + case other + when Numeric; return @ajd <=> other + when Date; return @ajd <=> other.ajd + end + nil + end + + # The relationship operator for Date. + # + # Compares dates by Julian Day Number. When comparing + # two DateTime instances, or a DateTime with a Date, + # the instances will be regarded as equivalent if they + # fall on the same date in local time. + def === (other) + case other + when Numeric; return jd == other + when Date; return jd == other.jd + end + false + end + + def next_day(n=1) self + n end +# def prev_day(n=1) self - n end + + private :next_day + + # Return a new Date one day after this one. + def next() next_day end + + alias_method :succ, :next + + # Return a new Date object that is +n+ months later than + # the current one. + # + # If the day-of-the-month of the current Date is greater + # than the last day of the target month, the day-of-the-month + # of the returned Date will be the last day of the target month. + def >> (n) + y, m = (year * 12 + (mon - 1) + n).divmod(12) + m, = (m + 1) .divmod(1) + d = mday + d -= 1 until jd2 = self.class.valid_civil?(y, m, d, fix_style) + self + (jd2 - jd) + end + + # Return a new Date object that is +n+ months earlier than + # the current one. + # + # If the day-of-the-month of the current Date is greater + # than the last day of the target month, the day-of-the-month + # of the returned Date will be the last day of the target month. + def << (n) self >> -n end + +=begin + def next_month(n=1) self >> n end + def prev_month(n=1) self << n end + + def next_year(n=1) self >> n * 12 end + def prev_year(n=1) self << n * 12 end +=end + +# require 'enumerator' + + # Step the current date forward +step+ days at a + # time (or backward, if +step+ is negative) until + # we reach +limit+ (inclusive), yielding the resultant + # date at each step. + def step(limit, step=1) # :yield: date +=begin + unless block_given? + return to_enum(:step, limit, step) + end +=end + da = self + op = %w(- <= >=)[step <=> 0] + while da.__send__(op, limit) + yield da + da += step + end + self + end + + # Step forward one day at a time until we reach +max+ + # (inclusive), yielding each date as we go. + def upto(max, &block) # :yield: date + step(max, +1, &block) + end + + # Step backward one day at a time until we reach +min+ + # (inclusive), yielding each date as we go. + def downto(min, &block) # :yield: date + step(min, -1, &block) + end + + # Is this Date equal to +other+? + # + # +other+ must both be a Date object, and represent the same date. + def eql? (other) Date === other && self == other end + + # Calculate a hash value for this date. + def hash() @ajd.hash end + + # Return internal object state as a programmer-readable string. + def inspect() format('#<%s: %s,%s,%s>', self.class, @ajd, @of, @sg) end + + # Return the date as a human-readable string. + # + # The format used is YYYY-MM-DD. + def to_s() strftime end + + # Dump to Marshal format. + def _dump(limit) Marshal.dump([@ajd, @of, @sg], -1) end + +# def self._load(str) new!(*Marshal.load(str)) end + + # Load from Marshall format. + def self._load(str) + a = Marshal.load(str) + if a.size == 2 + ajd, sg = a + of = 0 + ajd -= 1.to_r/2 + else + ajd, of, sg = a + end + new!(ajd, of, sg) + end + +end + +# Class representing a date and time. +# +# See the documentation to the file date.rb for an overview. +# +# DateTime objects are immutable once created. +# +# == Other methods. +# +# The following methods are defined in Date, but declared private +# there. They are made public in DateTime. They are documented +# here. +# +# === hour() +# +# Get the hour-of-the-day of the time. This is given +# using the 24-hour clock, counting from midnight. The first +# hour after midnight is hour 0; the last hour of the day is +# hour 23. +# +# === min() +# +# Get the minute-of-the-hour of the time. +# +# === sec() +# +# Get the second-of-the-minute of the time. +# +# === sec_fraction() +# +# Get the fraction of a second of the time. This is returned as +# a +Rational+. The unit is in days. +# I do NOT recommend you to use this method. +# +# === zone() +# +# Get the time zone as a String. This is representation of the +# time offset such as "+1000", not the true time-zone name. +# +# === offset() +# +# Get the time zone offset as a fraction of a day. This is returned +# as a +Rational+. +# +# === new_offset(of=0) +# +# Create a new DateTime object, identical to the current one, except +# with a new time zone offset of +of+. +of+ is the new offset from +# UTC as a fraction of a day. +# +class DateTime < Date + + # Create a new DateTime object corresponding to the specified + # Julian Day Number +jd+ and hour +h+, minute +min+, second +s+. + # + # The 24-hour clock is used. Negative values of +h+, +min+, and + # +sec+ are treating as counting backwards from the end of the + # next larger unit (e.g. a +min+ of -2 is treated as 58). No + # wraparound is performed. If an invalid time portion is specified, + # an ArgumentError is raised. + # + # +of+ is the offset from UTC as a fraction of a day (defaults to 0). + # +sg+ specifies the Day of Calendar Reform. + # + # All day/time values default to 0. + def self.jd(jd=0, h=0, min=0, s=0, of=0, sg=ITALY) + unless (jd = valid_jd?(jd, sg)) && + (fr = valid_time?(h, min, s)) + raise ArgumentError, 'invalid date' + end + if String === of + of = (zone_to_diff(of) || 0).to_r/86400 + end + new!(jd_to_ajd(jd, fr, of), of, sg) + end + + # Create a new DateTime object corresponding to the specified + # Ordinal Date and hour +h+, minute +min+, second +s+. + # + # The 24-hour clock is used. Negative values of +h+, +min+, and + # +sec+ are treating as counting backwards from the end of the + # next larger unit (e.g. a +min+ of -2 is treated as 58). No + # wraparound is performed. If an invalid time portion is specified, + # an ArgumentError is raised. + # + # +of+ is the offset from UTC as a fraction of a day (defaults to 0). + # +sg+ specifies the Day of Calendar Reform. + # + # +y+ defaults to -4712, and +d+ to 1; this is Julian Day Number + # day 0. The time values default to 0. + def self.ordinal(y=-4712, d=1, h=0, min=0, s=0, of=0, sg=ITALY) + unless (jd = valid_ordinal?(y, d, sg)) && + (fr = valid_time?(h, min, s)) + raise ArgumentError, 'invalid date' + end + if String === of + of = (zone_to_diff(of) || 0).to_r/86400 + end + new!(jd_to_ajd(jd, fr, of), of, sg) + end + + # Create a new DateTime object corresponding to the specified + # Civil Date and hour +h+, minute +min+, second +s+. + # + # The 24-hour clock is used. Negative values of +h+, +min+, and + # +sec+ are treating as counting backwards from the end of the + # next larger unit (e.g. a +min+ of -2 is treated as 58). No + # wraparound is performed. If an invalid time portion is specified, + # an ArgumentError is raised. + # + # +of+ is the offset from UTC as a fraction of a day (defaults to 0). + # +sg+ specifies the Day of Calendar Reform. + # + # +y+ defaults to -4712, +m+ to 1, and +d+ to 1; this is Julian Day + # Number day 0. The time values default to 0. + def self.civil(y=-4712, m=1, d=1, h=0, min=0, s=0, of=0, sg=ITALY) + unless (jd = valid_civil?(y, m, d, sg)) && + (fr = valid_time?(h, min, s)) + raise ArgumentError, 'invalid date' + end + if String === of + of = (zone_to_diff(of) || 0).to_r/86400 + end + new!(jd_to_ajd(jd, fr, of), of, sg) + end + + class << self; alias_method :new, :civil end + + # Create a new DateTime object corresponding to the specified + # Commercial Date and hour +h+, minute +min+, second +s+. + # + # The 24-hour clock is used. Negative values of +h+, +min+, and + # +sec+ are treating as counting backwards from the end of the + # next larger unit (e.g. a +min+ of -2 is treated as 58). No + # wraparound is performed. If an invalid time portion is specified, + # an ArgumentError is raised. + # + # +of+ is the offset from UTC as a fraction of a day (defaults to 0). + # +sg+ specifies the Day of Calendar Reform. + # + # +y+ defaults to 1582, +w+ to 41, and +d+ to 5; this is the Day of + # Calendar Reform for Italy and the Catholic countries. + # The time values default to 0. + def self.commercial(y=1582, w=41, d=5, h=0, min=0, s=0, of=0, sg=ITALY) + unless (jd = valid_commercial?(y, w, d, sg)) && + (fr = valid_time?(h, min, s)) + raise ArgumentError, 'invalid date' + end + if String === of + of = (zone_to_diff(of) || 0).to_r/86400 + end + new!(jd_to_ajd(jd, fr, of), of, sg) + end + + def self.weeknum(y=1582, w=41, d=5, f=0, h=0, min=0, s=0, of=0, sg=ITALY) # :nodoc: + unless (jd = valid_weeknum?(y, w, d, f, sg)) && + (fr = valid_time?(h, min, s)) + raise ArgumentError, 'invalid date' + end + if String === of + of = (zone_to_diff(of) || 0).to_r/86400 + end + new!(jd_to_ajd(jd, fr, of), of, sg) + end + + private_class_method :weeknum + + def self.new_by_frags(elem, sg) # :nodoc: + elem = rewrite_frags(elem) + elem = complete_frags(elem) + unless (jd = valid_date_frags?(elem, sg)) && + (fr = valid_time_frags?(elem)) + raise ArgumentError, 'invalid date' + end + sf = (elem[:sec_fraction] || 0) + fr += sf/86400 + of = (elem[:offset] || 0) + of = of.to_r/86400 + new!(jd_to_ajd(jd, fr, of), of, sg) + end + + private_class_method :new_by_frags + + # Create a new DateTime object by parsing from a String + # according to a specified format. + # + # +str+ is a String holding a date-time representation. + # +fmt+ is the format that the date-time is in. See + # date/format.rb for details on supported formats. + # + # The default +str+ is '-4712-01-01T00:00:00+00:00', and the default + # +fmt+ is '%FT%T%z'. This gives midnight on Julian Day Number day 0. + # + # +sg+ specifies the Day of Calendar Reform. + # + # An ArgumentError will be raised if +str+ cannot be + # parsed. + def self.strptime(str='-4712-01-01T00:00:00+00:00', fmt='%FT%T%z', sg=ITALY) + elem = _strptime(str, fmt) + new_by_frags(elem, sg) + end + + # Create a new DateTime object by parsing from a String, + # without specifying the format. + # + # +str+ is a String holding a date-time representation. + # +comp+ specifies whether to interpret 2-digit years + # as 19XX (>= 69) or 20XX (< 69); the default is not to. + # The method will attempt to parse a date-time from the String + # using various heuristics; see #_parse in date/format.rb + # for more details. If parsing fails, an ArgumentError + # will be raised. + # + # The default +str+ is '-4712-01-01T00:00:00+00:00'; this is Julian + # Day Number day 0. + # + # +sg+ specifies the Day of Calendar Reform. + def self.parse(str='-4712-01-01T00:00:00+00:00', comp=false, sg=ITALY) + elem = _parse(str, comp) + new_by_frags(elem, sg) + end + + public :hour, :min, :sec, :sec_fraction, :zone, :offset, :new_offset + +end + +class Time + +# def to_time() getlocal end + + def to_date + jd = Date.civil_to_jd(year, mon, mday, Date::ITALY) + Date.new!(Date.jd_to_ajd(jd, 0, 0), 0, Date::ITALY) + end + + def to_datetime + jd = DateTime.civil_to_jd(year, mon, mday, DateTime::ITALY) + fr = DateTime.time_to_day_fraction(hour, min, [sec, 59].min) + + usec.to_r/86400000000 + of = utc_offset.to_r/86400 + DateTime.new!(DateTime.jd_to_ajd(jd, fr, of), of, DateTime::ITALY) + end + + private :to_date, :to_datetime + +end + +class Date + +=begin + def to_time() Time.local(year, mon, mday) end + def to_date() self end + def to_datetime() DateTime.new!(self.class.jd_to_ajd(jd, 0, 0), @of, @sg) end +=end + + # Create a new Date object representing today. + # + # +sg+ specifies the Day of Calendar Reform. + def self.today(sg=ITALY) Time.now.__send__(:to_date) .new_start(sg) end + + # Create a new DateTime object representing the current time. + # + # +sg+ specifies the Day of Calendar Reform. + def self.now (sg=ITALY) Time.now.__send__(:to_datetime).new_start(sg) end + + private_class_method :now + +end + +class DateTime < Date + +=begin + def to_time + d = new_offset(0) + d.instance_eval do + Time.utc(year, mon, mday, hour, min, sec, + (sec_fraction * 86400000000).to_i) + end. + getlocal + end + + def to_date() Date.new!(self.class.jd_to_ajd(jd, 0, 0), 0, @sg) end + def to_datetime() self end +=end + + private_class_method :today + public_class_method :now + +end + +class Date + + [ %w(os? julian?), + %w(ns? gregorian?), + %w(exist1? valid_jd?), + %w(exist2? valid_ordinal?), + %w(exist3? valid_date?), + %w(exist? valid_date?), + %w(existw? valid_commercial?), + %w(new0 new!), + %w(new1 jd), + %w(new2 ordinal), + %w(new3 new), + %w(neww commercial) + ].each do |old, new| + module_eval <<-"end;" + def self.#{old}(*args, &block) + if $VERBOSE + warn("\#{caller.shift.sub(/:in .*/, '')}: " \ + "warning: \#{self}::#{old} is deprecated; " \ + "use \#{self}::#{new}") + end + #{new}(*args, &block) + end + end; + end + + [ %w(os? julian?), + %w(ns? gregorian?), + %w(sg start), + %w(newsg new_start), + %w(of offset), + %w(newof new_offset) + ].each do |old, new| + module_eval <<-"end;" + def #{old}(*args, &block) + if $VERBOSE + warn("\#{caller.shift.sub(/:in .*/, '')}: " \ + "warning: \#{self.class}\##{old} is deprecated; " \ + "use \#{self.class}\##{new}") + end + #{new}(*args, &block) + end + end; + end + + private :of, :newof + +end + +class DateTime < Date + + public :of, :newof + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/date/format.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/date/format.rb new file mode 100644 index 0000000000..8bd14c7fd0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/date/format.rb @@ -0,0 +1,1088 @@ +# format.rb: Written by Tadayoshi Funaba 1999-2007 +# $Id: format.rb,v 2.30 2007-01-07 09:16:24+09 tadf Exp $ + +require 'rational' + +class Date + + module Format # :nodoc: + + MONTHS = { + 'january' => 1, 'february' => 2, 'march' => 3, 'april' => 4, + 'may' => 5, 'june' => 6, 'july' => 7, 'august' => 8, + 'september'=> 9, 'october' =>10, 'november' =>11, 'december' =>12 + } + + DAYS = { + 'sunday' => 0, 'monday' => 1, 'tuesday' => 2, 'wednesday'=> 3, + 'thursday' => 4, 'friday' => 5, 'saturday' => 6 + } + + ABBR_MONTHS = { + 'jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, + 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, + 'sep' => 9, 'oct' =>10, 'nov' =>11, 'dec' =>12 + } + + ABBR_DAYS = { + 'sun' => 0, 'mon' => 1, 'tue' => 2, 'wed' => 3, + 'thu' => 4, 'fri' => 5, 'sat' => 6 + } + + ZONES = { + 'ut' => 0*3600, 'gmt' => 0*3600, 'est' => -5*3600, 'edt' => -4*3600, + 'cst' => -6*3600, 'cdt' => -5*3600, 'mst' => -7*3600, 'mdt' => -6*3600, + 'pst' => -8*3600, 'pdt' => -7*3600, + 'a' => 1*3600, 'b' => 2*3600, 'c' => 3*3600, 'd' => 4*3600, + 'e' => 5*3600, 'f' => 6*3600, 'g' => 7*3600, 'h' => 8*3600, + 'i' => 9*3600, 'k' => 10*3600, 'l' => 11*3600, 'm' => 12*3600, + 'n' => -1*3600, 'o' => -2*3600, 'p' => -3*3600, 'q' => -4*3600, + 'r' => -5*3600, 's' => -6*3600, 't' => -7*3600, 'u' => -8*3600, + 'v' => -9*3600, 'w' =>-10*3600, 'x' =>-11*3600, 'y' =>-12*3600, + 'z' => 0*3600, + 'utc' => 0*3600, 'wet' => 0*3600, 'bst' => 1*3600, 'wat' => -1*3600, + 'at' => -2*3600, 'ast' => -4*3600, 'adt' => -3*3600, 'yst' => -9*3600, + 'ydt' => -8*3600, 'hst' =>-10*3600, 'hdt' => -9*3600, 'cat' =>-10*3600, + 'ahst'=>-10*3600, 'nt' =>-11*3600, 'idlw'=>-12*3600, 'cet' => 1*3600, + 'met' => 1*3600, 'mewt'=> 1*3600, 'mest'=> 2*3600, 'mesz'=> 2*3600, + 'swt' => 1*3600, 'sst' => 2*3600, 'fwt' => 1*3600, 'fst' => 2*3600, + 'eet' => 2*3600, 'bt' => 3*3600, 'zp4' => 4*3600, 'zp5' => 5*3600, + 'zp6' => 6*3600, 'wast'=> 7*3600, 'wadt'=> 8*3600, 'cct' => 8*3600, + 'jst' => 9*3600, 'east'=> 10*3600, 'eadt'=> 11*3600, 'gst' => 10*3600, + 'nzt' => 12*3600, 'nzst'=> 12*3600, 'nzdt'=> 13*3600, 'idle'=> 12*3600, + + 'afghanistan' => 16200, 'alaskan' => -32400, + 'arab' => 10800, 'arabian' => 14400, + 'arabic' => 10800, 'atlantic' => -14400, + 'aus central' => 34200, 'aus eastern' => 36000, + 'azores' => -3600, 'canada central' => -21600, + 'cape verde' => -3600, 'caucasus' => 14400, + 'cen. australia' => 34200, 'central america' => -21600, + 'central asia' => 21600, 'central europe' => 3600, + 'central european' => 3600, 'central pacific' => 39600, + 'central' => -21600, 'china' => 28800, + 'dateline' => -43200, 'e. africa' => 10800, + 'e. australia' => 36000, 'e. europe' => 7200, + 'e. south america' => -10800, 'eastern' => -18000, + 'egypt' => 7200, 'ekaterinburg' => 18000, + 'fiji' => 43200, 'fle' => 7200, + 'greenland' => -10800, 'greenwich' => 0, + 'gtb' => 7200, 'hawaiian' => -36000, + 'india' => 19800, 'iran' => 12600, + 'jerusalem' => 7200, 'korea' => 32400, + 'mexico' => -21600, 'mid-atlantic' => -7200, + 'mountain' => -25200, 'myanmar' => 23400, + 'n. central asia' => 21600, 'nepal' => 20700, + 'new zealand' => 43200, 'newfoundland' => -12600, + 'north asia east' => 28800, 'north asia' => 25200, + 'pacific sa' => -14400, 'pacific' => -28800, + 'romance' => 3600, 'russian' => 10800, + 'sa eastern' => -10800, 'sa pacific' => -18000, + 'sa western' => -14400, 'samoa' => -39600, + 'se asia' => 25200, 'malay peninsula' => 28800, + 'south africa' => 7200, 'sri lanka' => 21600, + 'taipei' => 28800, 'tasmania' => 36000, + 'tokyo' => 32400, 'tonga' => 46800, + 'us eastern' => -18000, 'us mountain' => -25200, + 'vladivostok' => 36000, 'w. australia' => 28800, + 'w. central africa' => 3600, 'w. europe' => 3600, + 'west asia' => 18000, 'west pacific' => 36000, + 'yakutsk' => 32400 + } + + [MONTHS, DAYS, ABBR_MONTHS, ABBR_DAYS, ZONES].each do |x| + x.freeze + end + + class Bag # :nodoc: + + def initialize + @elem = {} + end + + def method_missing(t, *args, &block) + t = t.to_s + set = t.chomp!('=') + t = t.intern + if set + @elem[t] = args[0] + else + @elem[t] + end + end + + def to_hash + @elem.reject{|k, v| /\A_/ =~ k.to_s || v.nil?} + end + + end + + end + + def emit(e, f) # :nodoc: + case e + when Numeric + sign = %w(+ + -)[e <=> 0] + e = e.abs + end + + s = e.to_s + + if f[:s] && f[:p] == '0' + f[:w] -= 1 + end + + if f[:s] && f[:p] == "\s" + s[0,0] = sign + end + + if f[:p] != '-' + s = s.rjust(f[:w], f[:p]) + end + + if f[:s] && f[:p] != "\s" + s[0,0] = sign + end + + s = s.upcase if f[:u] + s = s.downcase if f[:d] + s + end + + def emit_w(e, w, f) # :nodoc: + f[:w] = [f[:w], w].compact.max + emit(e, f) + end + + def emit_n(e, w, f) # :nodoc: + f[:p] ||= '0' + emit_w(e, w, f) + end + + def emit_sn(e, w, f) # :nodoc: + if e < 0 + w += 1 + f[:s] = true + end + emit_n(e, w, f) + end + + def emit_z(e, w, f) # :nodoc: + w += 1 + f[:s] = true + emit_n(e, w, f) + end + + def emit_a(e, w, f) # :nodoc: + f[:p] ||= "\s" + emit_w(e, w, f) + end + + def emit_ad(e, w, f) # :nodoc: + if f[:x] + f[:u] = true + f[:d] = false + end + emit_a(e, w, f) + end + + def emit_au(e, w, f) # :nodoc: + if f[:x] + f[:u] = false + f[:d] = true + end + emit_a(e, w, f) + end + + private :emit, :emit_w, :emit_n, :emit_sn, :emit_z, + :emit_a, :emit_ad, :emit_au + + def strftime(fmt='%F') + fmt.gsub(/%([-_0^#]+)?(\d+)?[EO]?(:{1,3}z|.)/m) do |m| + f = {} + s, w, c = $1, $2, $3 + if s + s.scan(/./) do |k| + case k + when '-'; f[:p] = '-' + when '_'; f[:p] = "\s" + when '0'; f[:p] = '0' + when '^'; f[:u] = true + when '#'; f[:x] = true + end + end + end + if w + f[:w] = w.to_i + end + case c + when 'A'; emit_ad(DAYNAMES[wday], 0, f) + when 'a'; emit_ad(ABBR_DAYNAMES[wday], 0, f) + when 'B'; emit_ad(MONTHNAMES[mon], 0, f) + when 'b'; emit_ad(ABBR_MONTHNAMES[mon], 0, f) + when 'C'; emit_sn((year / 100).floor, 2, f) + when 'c'; emit_a(strftime('%a %b %e %H:%M:%S %Y'), 0, f) + when 'D'; emit_a(strftime('%m/%d/%y'), 0, f) + when 'd'; emit_n(mday, 2, f) + when 'e'; emit_a(mday, 2, f) + when 'F' + if m == '%F' + format('%.4d-%02d-%02d', year, mon, mday) # 4p + else + emit_a(strftime('%Y-%m-%d'), 0, f) + end + when 'G'; emit_sn(cwyear, 4, f) + when 'g'; emit_n(cwyear % 100, 2, f) + when 'H'; emit_n(hour, 2, f) + when 'h'; emit_ad(strftime('%b'), 0, f) + when 'I'; emit_n((hour % 12).nonzero? || 12, 2, f) + when 'j'; emit_n(yday, 3, f) + when 'k'; emit_a(hour, 2, f) + when 'L' + emit_n((sec_fraction / (1.to_r/86400/(10**3))).round, 3, f) + when 'l'; emit_a((hour % 12).nonzero? || 12, 2, f) + when 'M'; emit_n(min, 2, f) + when 'm'; emit_n(mon, 2, f) + when 'N' + emit_n((sec_fraction / (1.to_r/86400/(10**9))).round, 9, f) + when 'n'; "\n" + when 'P'; emit_ad(strftime('%p').downcase, 0, f) + when 'p'; emit_au(if hour < 12 then 'AM' else 'PM' end, 0, f) + when 'Q' + d = ajd - self.class.jd_to_ajd(self.class::UNIXEPOCH, 0) + s = (d * 86400*10**3).to_i + emit_sn(s, 1, f) + when 'R'; emit_a(strftime('%H:%M'), 0, f) + when 'r'; emit_a(strftime('%I:%M:%S %p'), 0, f) + when 'S'; emit_n(sec, 2, f) + when 's' + d = ajd - self.class.jd_to_ajd(self.class::UNIXEPOCH, 0) + s = (d * 86400).to_i + emit_sn(s, 1, f) + when 'T' + if m == '%T' + format('%02d:%02d:%02d', hour, min, sec) # 4p + else + emit_a(strftime('%H:%M:%S'), 0, f) + end + when 't'; "\t" + when 'U', 'W' + emit_n(if c == 'U' then wnum0 else wnum1 end, 2, f) + when 'u'; emit_n(cwday, 1, f) + when 'V'; emit_n(cweek, 2, f) + when 'v'; emit_a(strftime('%e-%b-%Y'), 0, f) + when 'w'; emit_n(wday, 1, f) + when 'X'; emit_a(strftime('%H:%M:%S'), 0, f) + when 'x'; emit_a(strftime('%m/%d/%y'), 0, f) + when 'Y'; emit_sn(year, 4, f) + when 'y'; emit_n(year % 100, 2, f) + when 'Z'; emit_au(strftime('%:z'), 0, f) + when /\A(:{0,3})z/ + t = $1.size + sign = if offset < 0 then -1 else +1 end + fr = offset.abs + hh, fr = fr.divmod(1.to_r/24) + mm, fr = fr.divmod(1.to_r/1440) + ss, fr = fr.divmod(1.to_r/86400) + if t == 3 + if ss.nonzero? then t = 2 + elsif mm.nonzero? then t = 1 + else t = -1 + end + end + case t + when -1 + tail = [] + sep = '' + when 0 + f[:w] -= 2 if f[:w] + tail = ['%02d' % mm] + sep = '' + when 1 + f[:w] -= 3 if f[:w] + tail = ['%02d' % mm] + sep = ':' + when 2 + f[:w] -= 6 if f[:w] + tail = ['%02d' % mm, '%02d' % ss] + sep = ':' + end + ([emit_z(sign * hh, 2, f)] + tail).join(sep) + when '%'; emit_a('%', 0, f) + when '+'; emit_a(strftime('%a %b %e %H:%M:%S %Z %Y'), 0, f) + when '1' + if $VERBOSE + warn("warning: strftime: %1 is deprecated; forget this") + end + emit_n(jd, 1, f) + when '2' + if $VERBOSE + warn("warning: strftime: %2 is deprecated; use '%Y-%j'") + end + emit_a(strftime('%Y-%j'), 0, f) + when '3' + if $VERBOSE + warn("warning: strftime: %3 is deprecated; use '%F'") + end + emit_a(strftime('%F'), 0, f) + else + c + end + end + end + +# alias_method :format, :strftime + + def asctime() strftime('%c') end + + alias_method :ctime, :asctime + +=begin + def iso8601() strftime('%F') end + + def rfc3339() iso8601 end + + def rfc2822() strftime('%a, %-d %b %Y %T %z') end + + alias_method :rfc822, :rfc2822 + + def jisx0301 + if jd < 2405160 + iso8601 + else + case jd + when 2405160...2419614 + g = 'M%02d' % (year - 1867) + when 2419614...2424875 + g = 'T%02d' % (year - 1911) + when 2424875...2447535 + g = 'S%02d' % (year - 1925) + else + g = 'H%02d' % (year - 1988) + end + g + strftime('.%m.%d') + end + end + + def beat(n=0) + i, f = (new_offset(1.to_r/24).day_fraction * 1000).divmod(1) + ('@%03d' % i) + + if n < 1 + '' + else + '.%0*d' % [n, (f / (1.to_r/(10**n))).round] + end + end +=end + + def self.num_pattern? (s) # :nodoc: + /\A%[EO]?[CDdeFGgHIjkLlMmNQRrSsTUuVvWwXxYy\d]/ =~ s || /\A\d/ =~ s + end + + private_class_method :num_pattern? + + def self._strptime_i(str, fmt, e) # :nodoc: + fmt.scan(/%[EO]?(:{1,3}z|.)|(.)/m) do |s, c| + if s + case s + when 'A', 'a' + return unless str.sub!(/\A(#{Format::DAYS.keys.join('|')})/io, '') || + str.sub!(/\A(#{Format::ABBR_DAYS.keys.join('|')})/io, '') + val = Format::DAYS[$1.downcase] || Format::ABBR_DAYS[$1.downcase] + return unless val + e.wday = val + when 'B', 'b', 'h' + return unless str.sub!(/\A(#{Format::MONTHS.keys.join('|')})/io, '') || + str.sub!(/\A(#{Format::ABBR_MONTHS.keys.join('|')})/io, '') + val = Format::MONTHS[$1.downcase] || Format::ABBR_MONTHS[$1.downcase] + return unless val + e.mon = val + when 'C' + return unless str.sub!(if num_pattern?($') + then /\A([-+]?\d{1,2})/ + else /\A([-+]?\d{1,})/ + end, '') + val = $1.to_i + e._cent = val + when 'c' + return unless _strptime_i(str, '%a %b %e %H:%M:%S %Y', e) + when 'D' + return unless _strptime_i(str, '%m/%d/%y', e) + when 'd', 'e' + return unless str.sub!(/\A( \d|\d{1,2})/, '') + val = $1.to_i + return unless (1..31) === val + e.mday = val + when 'F' + return unless _strptime_i(str, '%Y-%m-%d', e) + when 'G' + return unless str.sub!(if num_pattern?($') + then /\A([-+]?\d{1,4})/ + else /\A([-+]?\d{1,})/ + end, '') + val = $1.to_i + e.cwyear = val + when 'g' + return unless str.sub!(/\A(\d{1,2})/, '') + val = $1.to_i + return unless (0..99) === val + e.cwyear = val + e._cent ||= if val >= 69 then 19 else 20 end + when 'H', 'k' + return unless str.sub!(/\A( \d|\d{1,2})/, '') + val = $1.to_i + return unless (0..24) === val + e.hour = val + when 'I', 'l' + return unless str.sub!(/\A( \d|\d{1,2})/, '') + val = $1.to_i + return unless (1..12) === val + e.hour = val + when 'j' + return unless str.sub!(/\A(\d{1,3})/, '') + val = $1.to_i + return unless (1..366) === val + e.yday = val + when 'L' + return unless str.sub!(if num_pattern?($') + then /\A([-+]?\d{1,3})/ + else /\A([-+]?\d{1,})/ + end, '') +# val = $1.to_i.to_r / (10**3) + val = $1.to_i.to_r / (10**$1.size) + e.sec_fraction = val + when 'M' + return unless str.sub!(/\A(\d{1,2})/, '') + val = $1.to_i + return unless (0..59) === val + e.min = val + when 'm' + return unless str.sub!(/\A(\d{1,2})/, '') + val = $1.to_i + return unless (1..12) === val + e.mon = val + when 'N' + return unless str.sub!(if num_pattern?($') + then /\A([-+]?\d{1,9})/ + else /\A([-+]?\d{1,})/ + end, '') +# val = $1.to_i.to_r / (10**9) + val = $1.to_i.to_r / (10**$1.size) + e.sec_fraction = val + when 'n', 't' + return unless _strptime_i(str, "\s", e) + when 'P', 'p' + return unless str.sub!(/\A([ap])(?:m\b|\.m\.)/i, '') + e._merid = if $1.downcase == 'a' then 0 else 12 end + when 'Q' + return unless str.sub!(/\A(-?\d{1,})/, '') + val = $1.to_i.to_r / 10**3 + e.seconds = val + when 'R' + return unless _strptime_i(str, '%H:%M', e) + when 'r' + return unless _strptime_i(str, '%I:%M:%S %p', e) + when 'S' + return unless str.sub!(/\A(\d{1,2})/, '') + val = $1.to_i + return unless (0..60) === val + e.sec = val + when 's' + return unless str.sub!(/\A(-?\d{1,})/, '') + val = $1.to_i + e.seconds = val + when 'T' + return unless _strptime_i(str, '%H:%M:%S', e) + when 'U', 'W' + return unless str.sub!(/\A(\d{1,2})/, '') + val = $1.to_i + return unless (0..53) === val + e.__send__(if s == 'U' then :wnum0= else :wnum1= end, val) + when 'u' + return unless str.sub!(/\A(\d{1})/, '') + val = $1.to_i + return unless (1..7) === val + e.cwday = val + when 'V' + return unless str.sub!(/\A(\d{1,2})/, '') + val = $1.to_i + return unless (1..53) === val + e.cweek = val + when 'v' + return unless _strptime_i(str, '%e-%b-%Y', e) + when 'w' + return unless str.sub!(/\A(\d{1})/, '') + val = $1.to_i + return unless (0..6) === val + e.wday = val + when 'X' + return unless _strptime_i(str, '%H:%M:%S', e) + when 'x' + return unless _strptime_i(str, '%m/%d/%y', e) + when 'Y' + return unless str.sub!(if num_pattern?($') + then /\A([-+]?\d{1,4})/ + else /\A([-+]?\d{1,})/ + end, '') + val = $1.to_i + e.year = val + when 'y' + return unless str.sub!(/\A(\d{1,2})/, '') + val = $1.to_i + return unless (0..99) === val + e.year = val + e._cent ||= if val >= 69 then 19 else 20 end + when 'Z', /\A:{0,3}z/ + return unless str.sub!(/\A((?:gmt|utc?)?[-+]\d+(?:[,.:]\d+(?::\d+)?)? + |[a-z.\s]+(?:standard|daylight)\s+time\b + |[a-z]+(?:\s+dst)?\b + )/ix, '') + val = $1 + e.zone = val + offset = zone_to_diff(val) + e.offset = offset + when '%' + return unless str.sub!(/\A%/, '') + when '+' + return unless _strptime_i(str, '%a %b %e %H:%M:%S %Z %Y', e) + when '1' + if $VERBOSE + warn("warning: strptime: %1 is deprecated; forget this") + end + return unless str.sub!(/\A(\d+)/, '') + val = $1.to_i + e.jd = val + when '2' + if $VERBOSE + warn("warning: strptime: %2 is deprecated; use '%Y-%j'") + end + return unless _strptime_i(str, '%Y-%j', e) + when '3' + if $VERBOSE + warn("warning: strptime: %3 is deprecated; use '%F'") + end + return unless _strptime_i(str, '%F', e) + else + return unless str.sub!(Regexp.new('\\A' + Regexp.quote(s)), '') + end + else + case c + when /\A[\s\v]/ + str.sub!(/\A[\s\v]+/, '') + else + return unless str.sub!(Regexp.new('\\A' + Regexp.quote(c)), '') + end + end + end + end + + private_class_method :_strptime_i + + def self._strptime(str, fmt='%F') + e = Format::Bag.new + return unless _strptime_i(str.dup, fmt, e) + + if e._cent + if e.cwyear + e.cwyear += e._cent * 100 + end + if e.year + e. year += e._cent * 100 + end + end + + if e._merid + if e.hour + e.hour %= 12 + e.hour += e._merid + end + end + + e.to_hash + end + + def self.s3e(e, y, m, d, bc=false) + unless String === m + m = m.to_s + end + + if y == nil + if d && d.size > 2 + y = d + d = nil + end + if d && d[0,1] == "'" + y = d + d = nil + end + end + + if y + y.scan(/(\d+)(.+)?/) + if $2 + y, d = d, $1 + end + end + + if m + if m[0,1] == "'" || m.size > 2 + y, m, d = m, d, y # us -> be + end + end + + if d + if d[0,1] == "'" || d.size > 2 + y, d = d, y + end + end + + if y + y =~ /([-+])?(\d+)/ + if $1 || $2.size > 2 + c = false + end + iy = $&.to_i + if bc + iy = -iy + 1 + end + e.year = iy + end + + if m + m =~ /\d+/ + e.mon = $&.to_i + end + + if d + d =~ /\d+/ + e.mday = $&.to_i + end + + if c != nil + e._comp = c + end + + end + + private_class_method :s3e + + def self._parse_day(str, e) # :nodoc: + if str.sub!(/\b(#{Format::ABBR_DAYS.keys.join('|')})[^-\d\s]*/ino, ' ') + e.wday = Format::ABBR_DAYS[$1.downcase] + true +=begin + elsif str.sub!(/\b(?!\dth)(su|mo|tu|we|th|fr|sa)\b/in, ' ') + e.wday = %w(su mo tu we th fr sa).index($1.downcase) + true +=end + end + end + + def self._parse_time(str, e) # :nodoc: + if str.sub!( + /( + (?: + \d+\s*:\s*\d+ + (?: + \s*:\s*\d+(?:[,.]\d*)? + )? + | + \d+\s*h(?:\s*\d+m?(?:\s*\d+s?)?)? + ) + (?: + \s* + [ap](?:m\b|\.m\.) + )? + | + \d+\s*[ap](?:m\b|\.m\.) + ) + (?: + \s* + ( + (?:gmt|utc?)?[-+]\d+(?:[,.:]\d+(?::\d+)?)? + | + [a-z.\s]+(?:standard|daylight)\stime\b + | + [a-z]+(?:\sdst)?\b + ) + )? + /inx, + ' ') + + t = $1 + e.zone = $2 if $2 + + t =~ /\A(\d+)h? + (?:\s*:?\s*(\d+)m? + (?: + \s*:?\s*(\d+)(?:[,.](\d+))?s? + )? + )? + (?:\s*([ap])(?:m\b|\.m\.))?/inx + + e.hour = $1.to_i + e.min = $2.to_i if $2 + e.sec = $3.to_i if $3 + e.sec_fraction = $4.to_i.to_r / (10**$4.size) if $4 + + if $5 + e.hour %= 12 + if $5.downcase == 'p' + e.hour += 12 + end + end + true + end + end + + def self._parse_beat(str, e) # :nodoc: + if str.sub!(/@\s*(\d+)(?:[,.](\d*))?/, ' ') + beat = $1.to_i.to_r + beat += $2.to_i.to_r / (10**$2.size) if $2 + secs = beat.to_r / 1000 + h, min, s, fr = self.day_fraction_to_time(secs) + e.hour = h + e.min = min + e.sec = s + e.sec_fraction = fr * 86400 + e.zone = '+01:00' + true + end + end + + def self._parse_eu(str, e) # :nodoc: + if str.sub!( + /'?(\d+)[^-\d\s]* + \s* + (#{Format::ABBR_MONTHS.keys.join('|')})[^-\d\s']* + (?: + \s* + (c(?:e|\.e\.)|b(?:ce|\.c\.e\.)|a(?:d|\.d\.)|b(?:c|\.c\.))? + \s* + ('?-?\d+(?:(?:st|nd|rd|th)\b)?) + )? + /inox, + ' ') # ' + s3e(e, $4, Format::ABBR_MONTHS[$2.downcase], $1, + $3 && $3[0,1].downcase == 'b') + true + end + end + + def self._parse_us(str, e) # :nodoc: + if str.sub!( + /\b(#{Format::ABBR_MONTHS.keys.join('|')})[^-\d\s']* + \s* + ('?\d+)[^-\d\s']* + (?: + \s* + (c(?:e|\.e\.)|b(?:ce|\.c\.e\.)|a(?:d|\.d\.)|b(?:c|\.c\.))? + \s* + ('?-?\d+) + )? + /inox, + ' ') # ' + s3e(e, $4, Format::ABBR_MONTHS[$1.downcase], $2, + $3 && $3[0,1].downcase == 'b') + true + end + end + + def self._parse_iso(str, e) # :nodoc: + if str.sub!(/('?[-+]?\d+)-(\d+)-('?-?\d+)/n, ' ') + s3e(e, $1, $2, $3) + true + end + end + + def self._parse_iso2(str, e) # :nodoc: + if str.sub!(/\b(\d{2}|\d{4})?-?w(\d{2})(?:-?(\d+))?/in, ' ') + e.cwyear = $1.to_i if $1 + e.cweek = $2.to_i + e.cwday = $3.to_i if $3 + true + elsif str.sub!(/--(\d{2})-(\d{2})\b/n, ' ') + e.mon = $1.to_i + e.mday = $2.to_i + true + elsif str.sub!(/\b(\d{2}|\d{4})-(\d{2,3})\b/n, ' ') + e.year = $1.to_i + if $2.size < 3 + e.mon = $2.to_i + else + e.yday = $2.to_i + end + true + end + end + + def self._parse_jis(str, e) # :nodoc: + if str.sub!(/\b([MTSH])(\d+)\.(\d+)\.(\d+)/in, ' ') + era = { 'm'=>1867, + 't'=>1911, + 's'=>1925, + 'h'=>1988 + }[$1.downcase] + e.year = $2.to_i + era + e.mon = $3.to_i + e.mday = $4.to_i + true + end + end + + def self._parse_vms(str, e) # :nodoc: + if str.sub!(/('?-?\d+)-(#{Format::ABBR_MONTHS.keys.join('|')})[^-]* + -('?-?\d+)/inox, ' ') + s3e(e, $3, Format::ABBR_MONTHS[$2.downcase], $1) + true + elsif str.sub!(/\b(#{Format::ABBR_MONTHS.keys.join('|')})[^-]* + -('?-?\d+)(?:-('?-?\d+))?/inox, ' ') + s3e(e, $3, Format::ABBR_MONTHS[$1.downcase], $2) + true + end + end + + def self._parse_sla_ja(str, e) # :nodoc: + if str.sub!(%r|('?-?\d+)[/.]\s*('?\d+)(?:[^\d]\s*('?-?\d+))?|n, ' ') # ' + s3e(e, $1, $2, $3) + true + end + end + + def self._parse_sla_eu(str, e) # :nodoc: + if str.sub!(%r|('?-?\d+)[/.]\s*('?\d+)(?:[^\d]\s*('?-?\d+))?|n, ' ') # ' + s3e(e, $3, $2, $1) + true + end + end + + def self._parse_sla_us(str, e) # :nodoc: + if str.sub!(%r|('?-?\d+)[/.]\s*('?\d+)(?:[^\d]\s*('?-?\d+))?|n, ' ') # ' + s3e(e, $3, $1, $2) + true + end + end + + def self._parse_year(str, e) # :nodoc: + if str.sub!(/'(\d+)\b/in, ' ') + e.year = $1.to_i + true + end + end + + def self._parse_mon(str, e) # :nodoc: + if str.sub!(/\b(#{Format::ABBR_MONTHS.keys.join('|')})\S*/ino, ' ') + e.mon = Format::ABBR_MONTHS[$1.downcase] + true + end + end + + def self._parse_mday(str, e) # :nodoc: + if str.sub!(/(\d+)(st|nd|rd|th)\b/in, ' ') + e.mday = $1.to_i + true + end + end + + def self._parse_ddd(str, e) # :nodoc: + if str.sub!( + /([-+]?)(\d{2,14}) + (?: + \s* + T? + \s* + (\d{2,6})(?:[,.](\d*))? + )? + (?: + \s* + ( + Z + | + [-+]\d{1,4} + ) + \b + )? + /inx, + ' ') + case $2.size + when 2 + e.mday = $2[ 0, 2].to_i + when 4 + e.mon = $2[ 0, 2].to_i + e.mday = $2[ 2, 2].to_i + when 6 + e.year = ($1 + $2[ 0, 2]).to_i + e.mon = $2[ 2, 2].to_i + e.mday = $2[ 4, 2].to_i + when 8, 10, 12, 14 + e.year = ($1 + $2[ 0, 4]).to_i + e.mon = $2[ 4, 2].to_i + e.mday = $2[ 6, 2].to_i + e.hour = $2[ 8, 2].to_i if $2.size >= 10 + e.min = $2[10, 2].to_i if $2.size >= 12 + e.sec = $2[12, 2].to_i if $2.size >= 14 + e._comp = false + when 3 + e.yday = $2[ 0, 3].to_i + when 5 + e.year = ($1 + $2[ 0, 2]).to_i + e.yday = $2[ 2, 3].to_i + when 7 + e.year = ($1 + $2[ 0, 4]).to_i + e.yday = $2[ 4, 3].to_i + end + if $3 + case $3.size + when 2, 4, 6 + e.hour = $3[ 0, 2].to_i + e.min = $3[ 2, 2].to_i if $3.size >= 4 + e.sec = $3[ 4, 2].to_i if $3.size >= 6 + end + end + if $4 + e.sec_fraction = $4.to_i.to_r / (10**$4.size) + end + if $5 + e.zone = $5 + end + true + end + end + + private_class_method :_parse_day, :_parse_time, :_parse_beat, + :_parse_eu, :_parse_us, :_parse_iso, :_parse_iso2, + :_parse_jis, :_parse_vms, + :_parse_sla_ja, :_parse_sla_eu, :_parse_sla_us, + :_parse_year, :_parse_mon, :_parse_mday, :_parse_ddd + + def self._parse(str, comp=false) + str = str.dup + + e = Format::Bag.new + + e._comp = comp + + str.gsub!(/[^-+',.\/:0-9@a-z\x80-\xff]+/in, ' ') + + _parse_time(str, e) # || _parse_beat(str, e) + _parse_day(str, e) + + _parse_eu(str, e) || + _parse_us(str, e) || + _parse_iso(str, e) || + _parse_jis(str, e) || + _parse_vms(str, e) || + _parse_sla_us(str, e) || + _parse_iso2(str, e) || + _parse_year(str, e) || + _parse_mon(str, e) || + _parse_mday(str, e) || + _parse_ddd(str, e) + + if str.sub!(/\b(bc\b|bce\b|b\.c\.|b\.c\.e\.)/in, ' ') + if e.year + e.year = -e.year + 1 + end + end + + if str.sub!(/\A\s*(\d{1,2})\s*\z/n, ' ') + if e.hour && !e.mday + v = $1.to_i + if (1..31) === v + e.mday = v + end + end + if e.mday && !e.hour + v = $1.to_i + if (0..24) === v + e.hour = v + end + end + end + + if e._comp and e.year + if e.year >= 0 and e.year <= 99 + if e.year >= 69 + e.year += 1900 + else + e.year += 2000 + end + end + end + + e.offset ||= zone_to_diff(e.zone) if e.zone + + e.to_hash + end + + def self.zone_to_diff(zone) # :nodoc: + zone = zone.downcase + if zone.sub!(/\s+(standard|daylight)\s+time\z/, '') + dst = $1 == 'daylight' + else + dst = zone.sub!(/\s+dst\z/, '') + end + if Format::ZONES.include?(zone) + offset = Format::ZONES[zone] + offset += 3600 if dst + elsif zone.sub!(/\A(?:gmt|utc?)?([-+])/, '') + sign = $1 + if zone.include?(':') + hour, min, sec, = zone.split(':') + elsif zone.include?(',') || zone.include?('.') + hour, fr, = zone.split(/[,.]/) + min = fr.to_i.to_r / (10**fr.size) * 60 + else + case zone.size + when 3 + hour = zone[0,1] + min = zone[1,2] + else + hour = zone[0,2] + min = zone[2,2] + sec = zone[4,2] + end + end + offset = hour.to_i * 3600 + min.to_i * 60 + sec.to_i + offset *= -1 if sign == '-' + end + offset + end + +end + +class DateTime < Date + + def strftime(fmt='%FT%T%:z') + super(fmt) + end + + def self._strptime(str, fmt='%FT%T%z') + super(str, fmt) + end + +=begin + def iso8601_timediv(n) # :nodoc: + strftime('T%T' + + if n < 1 + '' + else + '.%0*d' % [n, (sec_fraction / (1.to_r/86400/(10**n))).round] + end + + '%:z') + end + + private :iso8601_timediv + + def iso8601(n=0) + super() + iso8601_timediv(n) + end + + def rfc3339(n=0) iso8601(n) end + + def jisx0301(n=0) + super() + iso8601_timediv(n) + end +=end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/date2.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/date2.rb new file mode 100644 index 0000000000..f7da78f650 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/date2.rb @@ -0,0 +1,5 @@ +# date2 was overridden by date. +# To be precise, date was overridden by date2, +# and date2 was renamed to date. + +require 'date' diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/debug.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/debug.rb new file mode 100644 index 0000000000..9ae119f8fb --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/debug.rb @@ -0,0 +1,947 @@ +# Copyright (C) 2000 Network Applied Communication Laboratory, Inc. +# Copyright (C) 2000 Information-technology Promotion Agency, Japan +# Copyright (C) 2000-2003 NAKAMURA, Hiroshi + +if $SAFE > 0 + STDERR.print "-r debug.rb is not available in safe mode\n" + exit 1 +end + +require 'tracer' +require 'pp' + +class Tracer + def Tracer.trace_func(*vars) + Single.trace_func(*vars) + end +end + +SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__ + +class DEBUGGER__ +class Mutex + def initialize + @locker = nil + @waiting = [] + @locked = false; + end + + def locked? + @locked + end + + def lock + return if Thread.critical + return if @locker == Thread.current + while (Thread.critical = true; @locked) + @waiting.push Thread.current + Thread.stop + end + @locked = true + @locker = Thread.current + Thread.critical = false + self + end + + def unlock + return if Thread.critical + return unless @locked + unless @locker == Thread.current + raise RuntimeError, "unlocked by other" + end + Thread.critical = true + t = @waiting.shift + @locked = false + @locker = nil + Thread.critical = false + t.run if t + self + end +end +MUTEX = Mutex.new + +class Context + DEBUG_LAST_CMD = [] + + begin + require 'readline' + def readline(prompt, hist) + Readline::readline(prompt, hist) + end + rescue LoadError + def readline(prompt, hist) + STDOUT.print prompt + STDOUT.flush + line = STDIN.gets + exit unless line + line.chomp! + line + end + USE_READLINE = false + end + + def initialize + if Thread.current == Thread.main + @stop_next = 1 + else + @stop_next = 0 + end + @last_file = nil + @file = nil + @line = nil + @no_step = nil + @frames = [] + @finish_pos = 0 + @trace = false + @catch = "StandardError" + @suspend_next = false + end + + def stop_next(n=1) + @stop_next = n + end + + def set_suspend + @suspend_next = true + end + + def clear_suspend + @suspend_next = false + end + + def suspend_all + DEBUGGER__.suspend + end + + def resume_all + DEBUGGER__.resume + end + + def check_suspend + return if Thread.critical + while (Thread.critical = true; @suspend_next) + DEBUGGER__.waiting.push Thread.current + @suspend_next = false + Thread.stop + end + Thread.critical = false + end + + def trace? + @trace + end + + def set_trace(arg) + @trace = arg + end + + def stdout + DEBUGGER__.stdout + end + + def break_points + DEBUGGER__.break_points + end + + def display + DEBUGGER__.display + end + + def context(th) + DEBUGGER__.context(th) + end + + def set_trace_all(arg) + DEBUGGER__.set_trace(arg) + end + + def set_last_thread(th) + DEBUGGER__.set_last_thread(th) + end + + def debug_eval(str, binding) + begin + val = eval(str, binding) + rescue StandardError, ScriptError => e + at = eval("caller(1)", binding) + stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '') + for i in at + stdout.printf "\tfrom %s\n", i + end + throw :debug_error + end + end + + def debug_silent_eval(str, binding) + begin + eval(str, binding) + rescue StandardError, ScriptError + nil + end + end + + def var_list(ary, binding) + ary.sort! + for v in ary + stdout.printf " %s => %s\n", v, eval(v, binding).inspect + end + end + + def debug_variable_info(input, binding) + case input + when /^\s*g(?:lobal)?\s*$/ + var_list(global_variables, binding) + + when /^\s*l(?:ocal)?\s*$/ + var_list(eval("local_variables", binding), binding) + + when /^\s*i(?:nstance)?\s+/ + obj = debug_eval($', binding) + var_list(obj.instance_variables, obj.instance_eval{binding()}) + + when /^\s*c(?:onst(?:ant)?)?\s+/ + obj = debug_eval($', binding) + unless obj.kind_of? Module + stdout.print "Should be Class/Module: ", $', "\n" + else + var_list(obj.constants, obj.module_eval{binding()}) + end + end + end + + def debug_method_info(input, binding) + case input + when /^i(:?nstance)?\s+/ + obj = debug_eval($', binding) + + len = 0 + for v in obj.methods.sort + len += v.size + 1 + if len > 70 + len = v.size + 1 + stdout.print "\n" + end + stdout.print v, " " + end + stdout.print "\n" + + else + obj = debug_eval(input, binding) + unless obj.kind_of? Module + stdout.print "Should be Class/Module: ", input, "\n" + else + len = 0 + for v in obj.instance_methods(false).sort + len += v.size + 1 + if len > 70 + len = v.size + 1 + stdout.print "\n" + end + stdout.print v, " " + end + stdout.print "\n" + end + end + end + + def thnum + num = DEBUGGER__.instance_eval{@thread_list[Thread.current]} + unless num + DEBUGGER__.make_thread_list + num = DEBUGGER__.instance_eval{@thread_list[Thread.current]} + end + num + end + + def debug_command(file, line, id, binding) + MUTEX.lock + unless defined?($debugger_restart) and $debugger_restart + callcc{|c| $debugger_restart = c} + end + set_last_thread(Thread.current) + frame_pos = 0 + binding_file = file + binding_line = line + previous_line = nil + if ENV['EMACS'] + stdout.printf "\032\032%s:%d:\n", binding_file, binding_line + else + stdout.printf "%s:%d:%s", binding_file, binding_line, + line_at(binding_file, binding_line) + end + @frames[0] = [binding, file, line, id] + display_expressions(binding) + prompt = true + while prompt and input = readline("(rdb:%d) "%thnum(), true) + catch(:debug_error) do + if input == "" + next unless DEBUG_LAST_CMD[0] + input = DEBUG_LAST_CMD[0] + stdout.print input, "\n" + else + DEBUG_LAST_CMD[0] = input + end + + case input + when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/ + if defined?( $2 ) + if $1 == 'on' + set_trace_all true + else + set_trace_all false + end + elsif defined?( $1 ) + if $1 == 'on' + set_trace true + else + set_trace false + end + end + if trace? + stdout.print "Trace on.\n" + else + stdout.print "Trace off.\n" + end + + when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/ + pos = $2 + if $1 + klass = debug_silent_eval($1, binding) + file = $1 + end + if pos =~ /^\d+$/ + pname = pos + pos = pos.to_i + else + pname = pos = pos.intern.id2name + end + break_points.push [true, 0, klass || file, pos] + stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname + + when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/ + pos = $2.intern.id2name + klass = debug_eval($1, binding) + break_points.push [true, 0, klass, pos] + stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos + + when /^\s*wat(?:ch)?\s+(.+)$/ + exp = $1 + break_points.push [true, 1, exp] + stdout.printf "Set watchpoint %d:%s\n", break_points.size, exp + + when /^\s*b(?:reak)?$/ + if break_points.find{|b| b[1] == 0} + n = 1 + stdout.print "Breakpoints:\n" + for b in break_points + if b[0] and b[1] == 0 + stdout.printf " %d %s:%s\n", n, b[2], b[3] + end + n += 1 + end + end + if break_points.find{|b| b[1] == 1} + n = 1 + stdout.print "\n" + stdout.print "Watchpoints:\n" + for b in break_points + if b[0] and b[1] == 1 + stdout.printf " %d %s\n", n, b[2] + end + n += 1 + end + end + if break_points.size == 0 + stdout.print "No breakpoints\n" + else + stdout.print "\n" + end + + when /^\s*del(?:ete)?(?:\s+(\d+))?$/ + pos = $1 + unless pos + input = readline("Clear all breakpoints? (y/n) ", false) + if input == "y" + for b in break_points + b[0] = false + end + end + else + pos = pos.to_i + if break_points[pos-1] + break_points[pos-1][0] = false + else + stdout.printf "Breakpoint %d is not defined\n", pos + end + end + + when /^\s*disp(?:lay)?\s+(.+)$/ + exp = $1 + display.push [true, exp] + stdout.printf "%d: ", display.size + display_expression(exp, binding) + + when /^\s*disp(?:lay)?$/ + display_expressions(binding) + + when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/ + pos = $1 + unless pos + input = readline("Clear all expressions? (y/n) ", false) + if input == "y" + for d in display + d[0] = false + end + end + else + pos = pos.to_i + if display[pos-1] + display[pos-1][0] = false + else + stdout.printf "Display expression %d is not defined\n", pos + end + end + + when /^\s*c(?:ont)?$/ + prompt = false + + when /^\s*s(?:tep)?(?:\s+(\d+))?$/ + if $1 + lev = $1.to_i + else + lev = 1 + end + @stop_next = lev + prompt = false + + when /^\s*n(?:ext)?(?:\s+(\d+))?$/ + if $1 + lev = $1.to_i + else + lev = 1 + end + @stop_next = lev + @no_step = @frames.size - frame_pos + prompt = false + + when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/ + display_frames(frame_pos) + + when /^\s*l(?:ist)?(?:\s+(.+))?$/ + if not $1 + b = previous_line ? previous_line + 10 : binding_line - 5 + e = b + 9 + elsif $1 == '-' + b = previous_line ? previous_line - 10 : binding_line - 5 + e = b + 9 + else + b, e = $1.split(/[-,]/) + if e + b = b.to_i + e = e.to_i + else + b = b.to_i - 5 + e = b + 9 + end + end + previous_line = b + display_list(b, e, binding_file, binding_line) + + when /^\s*up(?:\s+(\d+))?$/ + previous_line = nil + if $1 + lev = $1.to_i + else + lev = 1 + end + frame_pos += lev + if frame_pos >= @frames.size + frame_pos = @frames.size - 1 + stdout.print "At toplevel\n" + end + binding, binding_file, binding_line = @frames[frame_pos] + stdout.print format_frame(frame_pos) + + when /^\s*down(?:\s+(\d+))?$/ + previous_line = nil + if $1 + lev = $1.to_i + else + lev = 1 + end + frame_pos -= lev + if frame_pos < 0 + frame_pos = 0 + stdout.print "At stack bottom\n" + end + binding, binding_file, binding_line = @frames[frame_pos] + stdout.print format_frame(frame_pos) + + when /^\s*fin(?:ish)?$/ + if frame_pos == @frames.size + stdout.print "\"finish\" not meaningful in the outermost frame.\n" + else + @finish_pos = @frames.size - frame_pos + frame_pos = 0 + prompt = false + end + + when /^\s*cat(?:ch)?(?:\s+(.+))?$/ + if $1 + excn = $1 + if excn == 'off' + @catch = nil + stdout.print "Clear catchpoint.\n" + else + @catch = excn + stdout.printf "Set catchpoint %s.\n", @catch + end + else + if @catch + stdout.printf "Catchpoint %s.\n", @catch + else + stdout.print "No catchpoint.\n" + end + end + + when /^\s*q(?:uit)?$/ + input = readline("Really quit? (y/n) ", false) + if input == "y" + exit! # exit -> exit!: No graceful way to stop threads... + end + + when /^\s*v(?:ar)?\s+/ + debug_variable_info($', binding) + + when /^\s*m(?:ethod)?\s+/ + debug_method_info($', binding) + + when /^\s*th(?:read)?\s+/ + if DEBUGGER__.debug_thread_info($', binding) == :cont + prompt = false + end + + when /^\s*pp\s+/ + PP.pp(debug_eval($', binding), stdout) + + when /^\s*p\s+/ + stdout.printf "%s\n", debug_eval($', binding).inspect + + when /^\s*r(?:estart)?$/ + $debugger_restart.call + + when /^\s*h(?:elp)?$/ + debug_print_help() + + else + v = debug_eval(input, binding) + stdout.printf "%s\n", v.inspect + end + end + end + MUTEX.unlock + resume_all + end + + def debug_print_help + stdout.print < + b[reak] [class.] + set breakpoint to some position + wat[ch] set watchpoint to some expression + cat[ch] (|off) set catchpoint to an exception + b[reak] list breakpoints + cat[ch] show catchpoint + del[ete][ nnn] delete some or all breakpoints + disp[lay] add expression into display expression list + undisp[lay][ nnn] delete one particular or all display expressions + c[ont] run until program ends or hit breakpoint + s[tep][ nnn] step (into methods) one line or till line nnn + n[ext][ nnn] go over one line or till line nnn + w[here] display frames + f[rame] alias for where + l[ist][ (-|nn-mm)] list program, - lists backwards + nn-mm lists given lines + up[ nn] move to higher frame + down[ nn] move to lower frame + fin[ish] return to outer frame + tr[ace] (on|off) set trace mode of current thread + tr[ace] (on|off) all set trace mode of all threads + q[uit] exit from debugger + v[ar] g[lobal] show global variables + v[ar] l[ocal] show local variables + v[ar] i[nstance] show instance variables of object + v[ar] c[onst] show constants of object + m[ethod] i[nstance] show methods of object + m[ethod] show instance methods of class or module + th[read] l[ist] list all threads + th[read] c[ur[rent]] show current thread + th[read] [sw[itch]] switch thread context to nnn + th[read] stop stop thread nnn + th[read] resume resume thread nnn + p expression evaluate expression and print its value + h[elp] print this help + evaluate +EOHELP + end + + def display_expressions(binding) + n = 1 + for d in display + if d[0] + stdout.printf "%d: ", n + display_expression(d[1], binding) + end + n += 1 + end + end + + def display_expression(exp, binding) + stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s + end + + def frame_set_pos(file, line) + if @frames[0] + @frames[0][1] = file + @frames[0][2] = line + end + end + + def display_frames(pos) + 0.upto(@frames.size - 1) do |n| + if n == pos + stdout.print "--> " + else + stdout.print " " + end + stdout.print format_frame(n) + end + end + + def format_frame(pos) + bind, file, line, id = @frames[pos] + sprintf "#%d %s:%s%s\n", pos + 1, file, line, + (id ? ":in `#{id.id2name}'" : "") + end + + def display_list(b, e, file, line) + stdout.printf "[%d, %d] in %s\n", b, e, file + if lines = SCRIPT_LINES__[file] and lines != true + n = 0 + b.upto(e) do |n| + if n > 0 && lines[n-1] + if n == line + stdout.printf "=> %d %s\n", n, lines[n-1].chomp + else + stdout.printf " %d %s\n", n, lines[n-1].chomp + end + end + end + else + stdout.printf "No sourcefile available for %s\n", file + end + end + + def line_at(file, line) + lines = SCRIPT_LINES__[file] + if lines + return "\n" if lines == true + line = lines[line-1] + return "\n" unless line + return line + end + return "\n" + end + + def debug_funcname(id) + if id.nil? + "toplevel" + else + id.id2name + end + end + + def check_break_points(file, klass, pos, binding, id) + return false if break_points.empty? + n = 1 + for b in break_points + if b[0] # valid + if b[1] == 0 # breakpoint + if (b[2] == file and b[3] == pos) or + (klass and b[2] == klass and b[3] == pos) + stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos + return true + end + elsif b[1] == 1 # watchpoint + if debug_silent_eval(b[2], binding) + stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos + return true + end + end + end + n += 1 + end + return false + end + + def excn_handle(file, line, id, binding) + if $!.class <= SystemExit + set_trace_func nil + exit + end + + if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch }) + stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class + fs = @frames.size + tb = caller(0)[-fs..-1] + if tb + for i in tb + stdout.printf "\tfrom %s\n", i + end + end + suspend_all + debug_command(file, line, id, binding) + end + end + + def trace_func(event, file, line, id, binding, klass) + Tracer.trace_func(event, file, line, id, binding, klass) if trace? + context(Thread.current).check_suspend + @file = file + @line = line + case event + when 'line' + frame_set_pos(file, line) + if !@no_step or @frames.size == @no_step + @stop_next -= 1 + @stop_next = -1 if @stop_next < 0 + elsif @frames.size < @no_step + @stop_next = 0 # break here before leaving... + else + # nothing to do. skipped. + end + if @stop_next == 0 or check_break_points(file, nil, line, binding, id) + @no_step = nil + suspend_all + debug_command(file, line, id, binding) + end + + when 'call' + @frames.unshift [binding, file, line, id] + if check_break_points(file, klass, id.id2name, binding, id) + suspend_all + debug_command(file, line, id, binding) + end + + when 'c-call' + frame_set_pos(file, line) + + when 'class' + @frames.unshift [binding, file, line, id] + + when 'return', 'end' + if @frames.size == @finish_pos + @stop_next = 1 + @finish_pos = 0 + end + @frames.shift + + when 'end' + @frames.shift + + when 'raise' + excn_handle(file, line, id, binding) + + end + @last_file = file + end +end + +trap("INT") { DEBUGGER__.interrupt } +@last_thread = Thread::main +@max_thread = 1 +@thread_list = {Thread::main => 1} +@break_points = [] +@display = [] +@waiting = [] +@stdout = STDOUT + +class << DEBUGGER__ + def stdout + @stdout + end + + def stdout=(s) + @stdout = s + end + + def display + @display + end + + def break_points + @break_points + end + + def waiting + @waiting + end + + def set_trace( arg ) + saved_crit = Thread.critical + Thread.critical = true + make_thread_list + for th, in @thread_list + context(th).set_trace arg + end + Thread.critical = saved_crit + arg + end + + def set_last_thread(th) + @last_thread = th + end + + def suspend + saved_crit = Thread.critical + Thread.critical = true + make_thread_list + for th, in @thread_list + next if th == Thread.current + context(th).set_suspend + end + Thread.critical = saved_crit + # Schedule other threads to suspend as soon as possible. + Thread.pass unless Thread.critical + end + + def resume + saved_crit = Thread.critical + Thread.critical = true + make_thread_list + for th, in @thread_list + next if th == Thread.current + context(th).clear_suspend + end + waiting.each do |th| + th.run + end + waiting.clear + Thread.critical = saved_crit + # Schedule other threads to restart as soon as possible. + Thread.pass + end + + def context(thread=Thread.current) + c = thread[:__debugger_data__] + unless c + thread[:__debugger_data__] = c = Context.new + end + c + end + + def interrupt + context(@last_thread).stop_next + end + + def get_thread(num) + th = @thread_list.index(num) + unless th + @stdout.print "No thread ##{num}\n" + throw :debug_error + end + th + end + + def thread_list(num) + th = get_thread(num) + if th == Thread.current + @stdout.print "+" + else + @stdout.print " " + end + @stdout.printf "%d ", num + @stdout.print th.inspect, "\t" + file = context(th).instance_eval{@file} + if file + @stdout.print file,":",context(th).instance_eval{@line} + end + @stdout.print "\n" + end + + def thread_list_all + for th in @thread_list.values.sort + thread_list(th) + end + end + + def make_thread_list + hash = {} + for th in Thread::list + if @thread_list.key? th + hash[th] = @thread_list[th] + else + @max_thread += 1 + hash[th] = @max_thread + end + end + @thread_list = hash + end + + def debug_thread_info(input, binding) + case input + when /^l(?:ist)?/ + make_thread_list + thread_list_all + + when /^c(?:ur(?:rent)?)?$/ + make_thread_list + thread_list(@thread_list[Thread.current]) + + when /^(?:sw(?:itch)?\s+)?(\d+)/ + make_thread_list + th = get_thread($1.to_i) + if th == Thread.current + @stdout.print "It's the current thread.\n" + else + thread_list(@thread_list[th]) + context(th).stop_next + th.run + return :cont + end + + when /^stop\s+(\d+)/ + make_thread_list + th = get_thread($1.to_i) + if th == Thread.current + @stdout.print "It's the current thread.\n" + elsif th.stop? + @stdout.print "Already stopped.\n" + else + thread_list(@thread_list[th]) + context(th).suspend + end + + when /^resume\s+(\d+)/ + make_thread_list + th = get_thread($1.to_i) + if th == Thread.current + @stdout.print "It's the current thread.\n" + elsif !th.stop? + @stdout.print "Already running." + else + thread_list(@thread_list[th]) + th.run + end + end + end +end + +stdout.printf "Debug.rb\n" +stdout.printf "Emacs support available.\n\n" +set_trace_func proc { |event, file, line, id, binding, klass, *rest| + DEBUGGER__.context.trace_func event, file, line, id, binding, klass +} +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/delegate.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/delegate.rb new file mode 100644 index 0000000000..d810ccad42 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/delegate.rb @@ -0,0 +1,335 @@ +# = delegate -- Support for the Delegation Pattern +# +# Documentation by James Edward Gray II and Gavin Sinclair +# +# == Introduction +# +# This library provides three different ways to delegate method calls to an +# object. The easiest to use is SimpleDelegator. Pass an object to the +# constructor and all methods supported by the object will be delegated. This +# object can be changed later. +# +# Going a step further, the top level DelegateClass method allows you to easily +# setup delegation through class inheritance. This is considerably more +# flexible and thus probably the most common use for this library. +# +# Finally, if you need full control over the delegation scheme, you can inherit +# from the abstract class Delegator and customize as needed. (If you find +# yourself needing this control, have a look at _forwardable_, also in the +# standard library. It may suit your needs better.) +# +# == Notes +# +# Be advised, RDoc will not detect delegated methods. +# +# delegate.rb provides full-class delegation via the +# DelegateClass() method. For single-method delegation via +# def_delegator(), see forwardable.rb. +# +# == Examples +# +# === SimpleDelegator +# +# Here's a simple example that takes advantage of the fact that +# SimpleDelegator's delegation object can be changed at any time. +# +# class Stats +# def initialize +# @source = SimpleDelegator.new([]) +# end +# +# def stats( records ) +# @source.__setobj__(records) +# +# "Elements: #{@source.size}\n" + +# " Non-Nil: #{@source.compact.size}\n" + +# " Unique: #{@source.uniq.size}\n" +# end +# end +# +# s = Stats.new +# puts s.stats(%w{James Edward Gray II}) +# puts +# puts s.stats([1, 2, 3, nil, 4, 5, 1, 2]) +# +# Prints: +# +# Elements: 4 +# Non-Nil: 4 +# Unique: 4 +# +# Elements: 8 +# Non-Nil: 7 +# Unique: 6 +# +# === DelegateClass() +# +# Here's a sample of use from tempfile.rb. +# +# A _Tempfile_ object is really just a _File_ object with a few special rules +# about storage location and/or when the File should be deleted. That makes for +# an almost textbook perfect example of how to use delegation. +# +# class Tempfile < DelegateClass(File) +# # constant and class member data initialization... +# +# def initialize(basename, tmpdir=Dir::tmpdir) +# # build up file path/name in var tmpname... +# +# @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600) +# +# # ... +# +# super(@tmpfile) +# +# # below this point, all methods of File are supported... +# end +# +# # ... +# end +# +# === Delegator +# +# SimpleDelegator's implementation serves as a nice example here. +# +# class SimpleDelegator < Delegator +# def initialize(obj) +# super # pass obj to Delegator constructor, required +# @_sd_obj = obj # store obj for future use +# end +# +# def __getobj__ +# @_sd_obj # return object we are delegating to, required +# end +# +# def __setobj__(obj) +# @_sd_obj = obj # change delegation object, a feature we're providing +# end +# +# # ... +# end + +# +# Delegator is an abstract class used to build delegator pattern objects from +# subclasses. Subclasses should redefine \_\_getobj\_\_. For a concrete +# implementation, see SimpleDelegator. +# +class Delegator + + # + # Pass in the _obj_ to delegate method calls to. All methods supported by + # _obj_ will be delegated to. + # + def initialize(obj) + preserved = ::Kernel.public_instance_methods(false) + preserved -= ["to_s","to_a","inspect","==","=~","==="] + for t in self.class.ancestors + preserved |= t.public_instance_methods(false) + preserved |= t.private_instance_methods(false) + preserved |= t.protected_instance_methods(false) + break if t == Delegator + end + preserved << "singleton_method_added" + for method in obj.methods + next if preserved.include? method + begin + eval <<-EOS + def self.#{method}(*args, &block) + begin + __getobj__.__send__(:#{method}, *args, &block) + rescue Exception + $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #` + $@.delete_if{|s| /^\\(eval\\):/ =~ s} + Kernel::raise + end + end + EOS + rescue SyntaxError + raise NameError, "invalid identifier %s" % method, caller(4) + end + end + end + alias initialize_methods initialize + + # Handles the magic of delegation through \_\_getobj\_\_. + def method_missing(m, *args) + target = self.__getobj__ + unless target.respond_to?(m) + super(m, *args) + end + target.__send__(m, *args) + end + + # + # Checks for a method provided by this the delegate object by fowarding the + # call through \_\_getobj\_\_. + # + def respond_to?(m) + return true if super + return self.__getobj__.respond_to?(m) + end + + # + # This method must be overridden by subclasses and should return the object + # method calls are being delegated to. + # + def __getobj__ + raise NotImplementedError, "need to define `__getobj__'" + end + + # Serialization support for the object returned by \_\_getobj\_\_. + def marshal_dump + __getobj__ + end + # Reinitializes delegation from a serialized object. + def marshal_load(obj) + initialize_methods(obj) + __setobj__(obj) + end +end + +# +# A concrete implementation of Delegator, this class provides the means to +# delegate all supported method calls to the object passed into the constructor +# and even to change the object being delegated to at a later time with +# \_\_setobj\_\_ . +# +class SimpleDelegator Edward + # names.__setobj__(%w{Gavin Sinclair}) + # puts names[1] # => Sinclair + # + def __setobj__(obj) + raise ArgumentError, "cannot delegate to self" if self.equal?(obj) + @_sd_obj = obj + end + + # Clone support for the object returned by \_\_getobj\_\_. + def clone + super + __setobj__(__getobj__.clone) + end + # Duplication support for the object returned by \_\_getobj\_\_. + def dup(obj) + super + __setobj__(__getobj__.dup) + end +end + +# :stopdoc: +# backward compatibility ^_^;;; +Delegater = Delegator +SimpleDelegater = SimpleDelegator +# :startdoc: + +# +# The primary interface to this library. Use to setup delegation when defining +# your class. +# +# class MyClass < DelegateClass( ClassToDelegateTo ) # Step 1 +# def initiaize +# super(obj_of_ClassToDelegateTo) # Step 2 +# end +# end +# +def DelegateClass(superclass) + klass = Class.new + methods = superclass.public_instance_methods(true) + methods -= ::Kernel.public_instance_methods(false) + methods |= ["to_s","to_a","inspect","==","=~","==="] + klass.module_eval { + def initialize(obj) # :nodoc: + @_dc_obj = obj + end + def method_missing(m, *args) # :nodoc: + unless @_dc_obj.respond_to?(m) + super(m, *args) + end + @_dc_obj.__send__(m, *args) + end + def respond_to?(m) # :nodoc: + return true if super + return @_dc_obj.respond_to?(m) + end + def __getobj__ # :nodoc: + @_dc_obj + end + def __setobj__(obj) # :nodoc: + raise ArgumentError, "cannot delegate to self" if self.equal?(obj) + @_dc_obj = obj + end + def clone # :nodoc: + super + __setobj__(__getobj__.clone) + end + def dup # :nodoc: + super + __setobj__(__getobj__.dup) + end + } + for method in methods + begin + klass.module_eval <<-EOS + def #{method}(*args, &block) + begin + @_dc_obj.__send__(:#{method}, *args, &block) + rescue + $@[0,2] = nil + raise + end + end + EOS + rescue SyntaxError + raise NameError, "invalid identifier %s" % method, caller(3) + end + end + return klass +end + +# :enddoc: + +if __FILE__ == $0 + class ExtArray true + foo2.error # raise error! +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/digest.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/digest.rb new file mode 100644 index 0000000000..0c4ee3c2cc --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/digest.rb @@ -0,0 +1,50 @@ +require 'digest.so' + +module Digest + def self.const_missing(name) + case name + when :SHA256, :SHA384, :SHA512 + lib = 'digest/sha2.so' + else + lib = File.join('digest', name.to_s.downcase) + end + + begin + require lib + rescue LoadError => e + raise LoadError, "library not found for class Digest::#{name} -- #{lib}", caller(1) + end + unless Digest.const_defined?(name) + raise NameError, "uninitialized constant Digest::#{name}", caller(1) + end + Digest.const_get(name) + end + + class ::Digest::Class + # creates a digest object and reads a given file, _name_. + # + # p Digest::SHA256.file("X11R6.8.2-src.tar.bz2").hexdigest + # # => "f02e3c85572dc9ad7cb77c2a638e3be24cc1b5bea9fdbb0b0299c9668475c534" + def self.file(name) + new.file(name) + end + end + + module Instance + # updates the digest with the contents of a given file _name_ and + # returns self. + def file(name) + File.open(name, "rb") {|f| + buf = "" + while f.read(16384, buf) + update buf + end + } + self + end + end +end + +def Digest(name) + Digest.const_get(name) +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/digest/sha2.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/digest/sha2.rb new file mode 100644 index 0000000000..c16305dc6c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/digest/sha2.rb @@ -0,0 +1,73 @@ +#-- +# sha2.rb - defines Digest::SHA2 class which wraps up the SHA256, +# SHA384, and SHA512 classes. +#++ +# Copyright (c) 2006 Akinori MUSHA +# +# All rights reserved. You can redistribute and/or modify it under the same +# terms as Ruby. +# +# $Id: sha2.rb 11708 2007-02-12 23:01:19Z shyouhei $ + +require 'digest' + +module Digest + # + # A meta digest provider class for SHA256, SHA384 and SHA512. + # + class SHA2 < Digest::Class + # call-seq: + # Digest::SHA2.new(bitlen = 256) -> digest_obj + # + # Creates a new SHA2 hash object with a given bit length. + def initialize(bitlen = 256) + case bitlen + when 256 + @sha2 = Digest::SHA256.new + when 384 + @sha2 = Digest::SHA384.new + when 512 + @sha2 = Digest::SHA512.new + else + raise ArgumentError, "unsupported bit length: %s" % bitlen.inspect + end + @bitlen = bitlen + end + + # :nodoc: + def reset + @sha2.reset + self + end + + # :nodoc: + def update(str) + @sha2.update(str) + self + end + alias << update + + def finish + @sha2.digest! + end + private :finish + + def block_length + @sha2.block_length + end + + def digest_length + @sha2.digest_length + end + + # :nodoc: + def initialize_copy(other) + @sha2 = other.instance_eval { @sha2.clone } + end + + # :nodoc: + def inspect + "#<%s:%d %s>" % [self.class.name, @bitlen, hexdigest] + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/dl/import.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/dl/import.rb new file mode 100644 index 0000000000..01ee2490e8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/dl/import.rb @@ -0,0 +1,225 @@ +# -*- ruby -*- + +require 'dl' +require 'dl/types' + +module DL + module Importable + LIB_MAP = {} + + module Internal + def init_types() + @types ||= ::DL::Types.new + end + + def init_sym() + @SYM ||= {} + end + + def [](name) + return @SYM[name.to_s][0] + end + + def dlload(*libnames) + if( !defined?(@LIBS) ) + @LIBS = [] + end + libnames.each{|libname| + if( !LIB_MAP[libname] ) + LIB_MAP[libname] = DL.dlopen(libname) + end + @LIBS.push(LIB_MAP[libname]) + } + end + alias dllink :dlload + + def parse_cproto(proto) + proto = proto.gsub(/\s+/, " ").strip + case proto + when /^([\d\w\*_\s]+)\(([\d\w\*_\s\,\[\]]*)\)$/ + ret = $1 + args = $2.strip() + ret = ret.split(/\s+/) + args = args.split(/\s*,\s*/) + func = ret.pop() + if( func =~ /^\*/ ) + func.gsub!(/^\*+/,"") + ret.push("*") + end + ret = ret.join(" ") + return [func, ret, args] + else + raise(RuntimeError,"can't parse the function prototype: #{proto}") + end + end + + # example: + # extern "int strlen(char*)" + # + def extern(proto) + func,ret,args = parse_cproto(proto) + return import(func, ret, args) + end + + # example: + # callback "int method_name(int, char*)" + # + def callback(proto) + func,ret,args = parse_cproto(proto) + + init_types() + init_sym() + + rty,renc,rdec = @types.encode_return_type(ret) + if( !rty ) + raise(TypeError, "unsupported type: #{ret}") + end + ty,enc,dec = encode_argument_types(args) + symty = rty + ty + + module_eval("module_function :#{func}") + sym = module_eval([ + "DL::callback(\"#{symty}\"){|*args|", + " sym,rdec,enc,dec = @SYM['#{func}']", + " args = enc.call(args) if enc", + " r,rs = #{func}(*args)", + " r = renc.call(r) if rdec", + " rs = dec.call(rs) if (dec && rs)", + " @retval = r", + " @args = rs", + " r", + "}", + ].join("\n")) + + @SYM[func] = [sym,rdec,enc,dec] + + return sym + end + + # example: + # typealias("uint", "unsigned int") + # + def typealias(alias_type, ty1, enc1=nil, dec1=nil, ty2=nil, enc2=nil, dec2=nil) + init_types() + @types.typealias(alias_type, ty1, enc1, dec1, + ty2||ty1, enc2, dec2) + end + + # example: + # symbol "foo_value" + # symbol "foo_func", "IIP" + # + def symbol(name, ty = nil) + sym = nil + @LIBS.each{|lib| + begin + if( ty ) + sym = lib[name, ty] + else + sym = lib[name] + end + rescue + next + end + } + if( !sym ) + raise(RuntimeError, "can't find the symbol `#{name}'") + end + return sym + end + + # example: + # import("get_length", "int", ["void*", "int"]) + # + def import(name, rettype, argtypes = nil) + init_types() + init_sym() + + rty,_,rdec = @types.encode_return_type(rettype) + if( !rty ) + raise(TypeError, "unsupported type: #{rettype}") + end + ty,enc,dec = encode_argument_types(argtypes) + symty = rty + ty + + sym = symbol(name, symty) + + mname = name.dup + if( ?A <= mname[0] && mname[0] <= ?Z ) + mname[0,1] = mname[0,1].downcase + end + @SYM[mname] = [sym,rdec,enc,dec] + + module_eval [ + "def #{mname}(*args)", + " sym,rdec,enc,dec = @SYM['#{mname}']", + " args = enc.call(args) if enc", + if( $DEBUG ) + " p \"[DL] call #{mname} with \#{args.inspect}\"" + else + "" + end, + " r,rs = sym.call(*args)", + if( $DEBUG ) + " p \"[DL] retval=\#{r.inspect} args=\#{rs.inspect}\"" + else + "" + end, + " r = rdec.call(r) if rdec", + " rs = dec.call(rs) if dec", + " @retval = r", + " @args = rs", + " return r", + "end", + "module_function :#{mname}", + ].join("\n") + + return sym + end + + def _args_ + return @args + end + + def _retval_ + return @retval + end + + def encode_argument_types(tys) + init_types() + encty = [] + enc = nil + dec = nil + tys.each_with_index{|ty,idx| + ty,c1,c2 = @types.encode_argument_type(ty) + if( !ty ) + raise(TypeError, "unsupported type: #{ty}") + end + encty.push(ty) + if( enc ) + if( c1 ) + conv1 = enc + enc = proc{|v| v = conv1.call(v); v[idx] = c1.call(v[idx]); v} + end + else + if( c1 ) + enc = proc{|v| v[idx] = c1.call(v[idx]); v} + end + end + if( dec ) + if( c2 ) + conv2 = dec + dec = proc{|v| v = conv2.call(v); v[idx] = c2.call(v[idx]); v} + end + else + if( c2 ) + dec = proc{|v| v[idx] = c2.call(v[idx]); v} + end + end + } + return [encty.join, enc, dec] + end + end # end of Internal + include Internal + end # end of Importable +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/dl/struct.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/dl/struct.rb new file mode 100644 index 0000000000..33f303fe22 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/dl/struct.rb @@ -0,0 +1,149 @@ +# -*- ruby -*- + +require 'dl' +require 'dl/import' + +module DL + module Importable + module Internal + def define_struct(contents) + init_types() + Struct.new(@types, contents) + end + alias struct define_struct + + def define_union(contents) + init_types() + Union.new(@types, contents) + end + alias union define_union + + class Memory + def initialize(ptr, names, ty, len, enc, dec) + @ptr = ptr + @names = names + @ty = ty + @len = len + @enc = enc + @dec = dec + + # define methods + @names.each{|name| + instance_eval [ + "def #{name}", + " v = @ptr[\"#{name}\"]", + " if( @len[\"#{name}\"] )", + " v = v.collect{|x| @dec[\"#{name}\"] ? @dec[\"#{name}\"].call(x) : x }", + " else", + " v = @dec[\"#{name}\"].call(v) if @dec[\"#{name}\"]", + " end", + " return v", + "end", + "def #{name}=(v)", + " if( @len[\"#{name}\"] )", + " v = v.collect{|x| @enc[\"#{name}\"] ? @enc[\"#{name}\"].call(x) : x }", + " else", + " v = @enc[\"#{name}\"].call(v) if @enc[\"#{name}\"]", + " end", + " @ptr[\"#{name}\"] = v", + " return v", + "end", + ].join("\n") + } + end + + def to_ptr + return @ptr + end + + def size + return @ptr.size + end + end + + class Struct + def initialize(types, contents) + @names = [] + @ty = {} + @len = {} + @enc = {} + @dec = {} + @size = 0 + @tys = "" + @types = types + parse(contents) + end + + def size + return @size + end + + def members + return @names + end + + # ptr must be a PtrData object. + def new(ptr) + ptr.struct!(@tys, *@names) + mem = Memory.new(ptr, @names, @ty, @len, @enc, @dec) + return mem + end + + def malloc(size = nil) + if( !size ) + size = @size + end + ptr = DL::malloc(size) + return new(ptr) + end + + def parse(contents) + contents.each{|elem| + name,ty,num,enc,dec = parse_elem(elem) + @names.push(name) + @ty[name] = ty + @len[name] = num + @enc[name] = enc + @dec[name] = dec + if( num ) + @tys += "#{ty}#{num}" + else + @tys += ty + end + } + @size = DL.sizeof(@tys) + end + + def parse_elem(elem) + elem.strip! + case elem + when /^([\w\d_\*]+)([\*\s]+)([\w\d_]+)$/ + ty = ($1 + $2).strip + name = $3 + num = nil; + when /^([\w\d_\*]+)([\*\s]+)([\w\d_]+)\[(\d+)\]$/ + ty = ($1 + $2).strip + name = $3 + num = $4.to_i + else + raise(RuntimeError, "invalid element: #{elem}") + end + ty,enc,dec = @types.encode_struct_type(ty) + if( !ty ) + raise(TypeError, "unsupported type: #{ty}") + end + return [name,ty,num,enc,dec] + end + end # class Struct + + class Union < Struct + def new + ptr = DL::malloc(@size) + ptr.union!(@tys, *@names) + mem = Memory.new(ptr, @names, @ty, @len, @enc, @dec) + return mem + end + end + end # module Internal + end # module Importable +end # module DL diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/dl/types.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/dl/types.rb new file mode 100644 index 0000000000..1144917dae --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/dl/types.rb @@ -0,0 +1,245 @@ +# -*- ruby -*- + +require 'dl' + +module DL + class Types + TYPES = [ + # FORMAT: + # ["alias name", + # "type name", encoding_method, decoding_method, for function prototypes + # "type name", encoding_method, decoding_method] for structures (not implemented) + + # for Windows + ["DWORD", "unsigned long", nil, nil, + "unsigned long", nil, nil], + ["PDWORD", "unsigned long *", nil, nil, + "unsigned long *", nil, nil], + ["WORD", "unsigned short", nil, nil, + "unsigned short", nil, nil], + ["PWORD", "unsigned int *", nil, nil, + "unsigned int *", nil, nil], + ["BYTE", "unsigned char", nil, nil, + "unsigned char", nil, nil], + ["PBYTE", "unsigned char *", nil, nil, + "unsigned char *", nil, nil], + ["BOOL", "ibool", nil, nil, + "ibool", nil, nil], + ["ATOM", "int", nil, nil, + "int", nil, nil], + ["BYTE", "unsigned char", nil, nil, + "unsigned char", nil, nil], + ["PBYTE", "unsigned char *", nil, nil, + "unsigned char *", nil, nil], + ["UINT", "unsigned int", nil, nil, + "unsigned int", nil, nil], + ["ULONG", "unsigned long", nil, nil, + "unsigned long", nil, nil], + ["UCHAR", "unsigned char", nil, nil, + "unsigned char", nil, nil], + ["HANDLE", "unsigned long", nil, nil, + "unsigned long", nil, nil], + ["PHANDLE","void*", nil, nil, + "void*", nil, nil], + ["PVOID", "void*", nil, nil, + "void*", nil, nil], + ["LPCSTR", "char*", nil, nil, + "char*", nil, nil], + ["HDC", "unsigned int", nil, nil, + "unsigned int", nil, nil], + ["HWND", "unsigned int", nil, nil, + "unsigned int", nil, nil], + + # Others + ["uint", "unsigned int", nil, nil, + "unsigned int", nil, nil], + ["u_int", "unsigned int", nil, nil, + "unsigned int", nil, nil], + ["ulong", "unsigned long", nil, nil, + "unsigned long", nil, nil], + ["u_long", "unsigned long", nil, nil, + "unsigned long", nil, nil], + + # DL::Importable primitive types + ["ibool", + "I", + proc{|v| v ? 1 : 0}, + proc{|v| (v != 0) ? true : false}, + "I", + proc{|v| v ? 1 : 0 }, + proc{|v| (v != 0) ? true : false} ], + ["cbool", + "C", + proc{|v| v ? 1 : 0}, + proc{|v| (v != 0) ? true : false}, + "C", + proc{|v,len| v ? 1 : 0}, + proc{|v,len| (v != 0) ? true : false}], + ["lbool", + "L", + proc{|v| v ? 1 : 0}, + proc{|v| (v != 0) ? true : false}, + "L", + proc{|v,len| v ? 1 : 0}, + proc{|v,len| (v != 0) ? true : false}], + ["unsigned char", + "C", + proc{|v| [v].pack("C").unpack("c")[0]}, + proc{|v| [v].pack("c").unpack("C")[0]}, + "C", + proc{|v| [v].pack("C").unpack("c")[0]}, + proc{|v| [v].pack("c").unpack("C")[0]}], + ["unsigned short", + "H", + proc{|v| [v].pack("S").unpack("s")[0]}, + proc{|v| [v].pack("s").unpack("S")[0]}, + "H", + proc{|v| [v].pack("S").unpack("s")[0]}, + proc{|v| [v].pack("s").unpack("S")[0]}], + ["unsigned int", + "I", + proc{|v| [v].pack("I").unpack("i")[0]}, + proc{|v| [v].pack("i").unpack("I")[0]}, + "I", + proc{|v| [v].pack("I").unpack("i")[0]}, + proc{|v| [v].pack("i").unpack("I")[0]}], + ["unsigned long", + "L", + proc{|v| [v].pack("L").unpack("l")[0]}, + proc{|v| [v].pack("l").unpack("L")[0]}, + "L", + proc{|v| [v].pack("L").unpack("l")[0]}, + proc{|v| [v].pack("l").unpack("L")[0]}], + ["unsigned char ref", + "c", + proc{|v| [v].pack("C").unpack("c")[0]}, + proc{|v| [v].pack("c").unpack("C")[0]}, + nil, nil, nil], + ["unsigned int ref", + "i", + proc{|v| [v].pack("I").unpack("i")[0]}, + proc{|v| [v].pack("i").unpack("I")[0]}, + nil, nil, nil], + ["unsigned long ref", + "l", + proc{|v| [v].pack("L").unpack("l")[0]}, + proc{|v| [v].pack("l").unpack("L")[0]}, + nil, nil, nil], + ["char ref", "c", nil, nil, + nil, nil, nil], + ["short ref", "h", nil, nil, + nil, nil, nil], + ["int ref", "i", nil, nil, + nil, nil, nil], + ["long ref", "l", nil, nil, + nil, nil, nil], + ["float ref", "f", nil, nil, + nil, nil, nil], + ["double ref","d", nil, nil, + nil, nil, nil], + ["char", "C", nil, nil, + "C", nil, nil], + ["short", "H", nil, nil, + "H", nil, nil], + ["int", "I", nil, nil, + "I", nil, nil], + ["long", "L", nil, nil, + "L", nil, nil], + ["float", "F", nil, nil, + "F", nil, nil], + ["double", "D", nil, nil, + "D", nil, nil], + [/^char\s*\*$/,"s",nil, nil, + "S",nil, nil], + [/^const char\s*\*$/,"S",nil, nil, + "S",nil, nil], + [/^.+\*$/, "P", nil, nil, + "P", nil, nil], + [/^.+\[\]$/, "a", nil, nil, + "a", nil, nil], + ["void", "0", nil, nil, + nil, nil, nil], + ] + + def initialize + init_types() + end + + def typealias(ty1, ty2, enc=nil, dec=nil, ty3=nil, senc=nil, sdec=nil) + @TYDEFS.unshift([ty1, ty2, enc, dec, ty3, senc, sdec]) + end + + def init_types + @TYDEFS = TYPES.dup + end + + def encode_argument_type(alias_type) + proc_encode = nil + proc_decode = nil + @TYDEFS.each{|aty,ty,enc,dec,_,_,_| + if( (aty.is_a?(Regexp) && (aty =~ alias_type)) || (aty == alias_type) ) + alias_type = alias_type.gsub(aty,ty) if ty + alias_type.strip! if alias_type + if( proc_encode ) + if( enc ) + conv1 = proc_encode + proc_encode = proc{|v| enc.call(conv1.call(v))} + end + else + if( enc ) + proc_encode = enc + end + end + if( proc_decode ) + if( dec ) + conv2 = proc_decode + proc_decode = proc{|v| dec.call(conv2.call(v))} + end + else + if( dec ) + proc_decode = dec + end + end + end + } + return [alias_type, proc_encode, proc_decode] + end + + def encode_return_type(ty) + ty, enc, dec = encode_argument_type(ty) + return [ty, enc, dec] + end + + def encode_struct_type(alias_type) + proc_encode = nil + proc_decode = nil + @TYDEFS.each{|aty,_,_,_,ty,enc,dec| + if( (aty.is_a?(Regexp) && (aty =~ alias_type)) || (aty == alias_type) ) + alias_type = alias_type.gsub(aty,ty) if ty + alias_type.strip! if alias_type + if( proc_encode ) + if( enc ) + conv1 = proc_encode + proc_encode = proc{|v| enc.call(conv1.call(v))} + end + else + if( enc ) + proc_encode = enc + end + end + if( proc_decode ) + if( dec ) + conv2 = proc_decode + proc_decode = proc{|v| dec.call(conv2.call(v))} + end + else + if( dec ) + proc_decode = dec + end + end + end + } + return [alias_type, proc_encode, proc_decode] + end + end # end of Types +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/dl/win32.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/dl/win32.rb new file mode 100644 index 0000000000..0fed47c324 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/dl/win32.rb @@ -0,0 +1,25 @@ +# -*- ruby -*- + +require 'dl' + +class Win32API + DLL = {} + + def initialize(dllname, func, import, export = "0") + prototype = (export + import.to_s).tr("VPpNnLlIi", "0SSI").sub(/^(.)0*$/, '\1') + handle = DLL[dllname] ||= DL::Handle.new(dllname) + @sym = handle.sym(func, prototype) + end + + def call(*args) + import = @sym.proto.split("", 2)[1] + args.each_with_index do |x, i| + args[i] = nil if x == 0 and import[i] == ?S + args[i], = [x].pack("I").unpack("i") if import[i] == ?I + end + ret, = @sym.call(*args) + return ret || 0 + end + + alias Call call +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb.rb new file mode 100644 index 0000000000..93cc811e14 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb.rb @@ -0,0 +1,2 @@ +require 'drb/drb' + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/acl.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/acl.rb new file mode 100644 index 0000000000..861c8a514d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/acl.rb @@ -0,0 +1,146 @@ +# acl-2.0 - simple Access Control List +# +# Copyright (c) 2000,2002,2003 Masatoshi SEKI +# +# acl.rb is copyrighted free software by Masatoshi SEKI. +# You can redistribute it and/or modify it under the same terms as Ruby. + +require 'ipaddr' + +class ACL + VERSION=["2.0.0"] + class ACLEntry + def initialize(str) + if str == '*' or str == 'all' + @pat = [:all] + elsif str.include?('*') + @pat = [:name, dot_pat(str)] + else + begin + @pat = [:ip, IPAddr.new(str)] + rescue ArgumentError + @pat = [:name, dot_pat(str)] + end + end + end + + private + def dot_pat_str(str) + list = str.split('.').collect { |s| + (s == '*') ? '.+' : s + } + list.join("\\.") + end + + private + def dot_pat(str) + exp = "^" + dot_pat_str(str) + "$" + Regexp.new(exp) + end + + public + def match(addr) + case @pat[0] + when :all + true + when :ip + begin + ipaddr = IPAddr.new(addr[3]) + ipaddr = ipaddr.ipv4_mapped if @pat[1].ipv6? && ipaddr.ipv4? + rescue ArgumentError + return false + end + (@pat[1].include?(ipaddr)) ? true : false + when :name + (@pat[1] =~ addr[2]) ? true : false + else + false + end + end + end + + class ACLList + def initialize + @list = [] + end + + public + def match(addr) + @list.each do |e| + return true if e.match(addr) + end + false + end + + public + def add(str) + @list.push(ACLEntry.new(str)) + end + end + + DENY_ALLOW = 0 + ALLOW_DENY = 1 + + def initialize(list=nil, order = DENY_ALLOW) + @order = order + @deny = ACLList.new + @allow = ACLList.new + install_list(list) if list + end + + public + def allow_socket?(soc) + allow_addr?(soc.peeraddr) + end + + public + def allow_addr?(addr) + case @order + when DENY_ALLOW + return true if @allow.match(addr) + return false if @deny.match(addr) + return true + when ALLOW_DENY + return false if @deny.match(addr) + return true if @allow.match(addr) + return false + else + false + end + end + + public + def install_list(list) + i = 0 + while i < list.size + permission, domain = list.slice(i,2) + case permission.downcase + when 'allow' + @allow.add(domain) + when 'deny' + @deny.add(domain) + else + raise "Invalid ACL entry #{list.to_s}" + end + i += 2 + end + end +end + +if __FILE__ == $0 + # example + list = %w(deny all + allow 192.168.1.1 + allow ::ffff:192.168.1.2 + allow 192.168.1.3 + ) + + addr = ["AF_INET", 10, "lc630", "192.168.1.3"] + + acl = ACL.new + p acl.allow_addr?(addr) + + acl = ACL.new(list, ACL::DENY_ALLOW) + p acl.allow_addr?(addr) +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/drb.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/drb.rb new file mode 100644 index 0000000000..25fbb3f788 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/drb.rb @@ -0,0 +1,1763 @@ +# +# = drb/drb.rb +# +# Distributed Ruby: _dRuby_ version 2.0.4 +# +# Copyright (c) 1999-2003 Masatoshi SEKI. You can redistribute it and/or +# modify it under the same terms as Ruby. +# +# Author:: Masatoshi SEKI +# +# Documentation:: William Webber (william@williamwebber.com) +# +# == Overview +# +# dRuby is a distributed object system for Ruby. It allows an object in one +# Ruby process to invoke methods on an object in another Ruby process on the +# same or a different machine. +# +# The Ruby standard library contains the core classes of the dRuby package. +# However, the full package also includes access control lists and the +# Rinda tuple-space distributed task management system, as well as a +# large number of samples. The full dRuby package can be downloaded from +# the dRuby home page (see *References*). +# +# For an introduction and examples of usage see the documentation to the +# DRb module. +# +# == References +# +# [http://www2a.biglobe.ne.jp/~seki/ruby/druby.html] +# The dRuby home page, in Japanese. Contains the full dRuby package +# and links to other Japanese-language sources. +# +# [http://www2a.biglobe.ne.jp/~seki/ruby/druby.en.html] +# The English version of the dRuby home page. +# +# [http://www.chadfowler.com/ruby/drb.html] +# A quick tutorial introduction to using dRuby by Chad Fowler. +# +# [http://www.linux-mag.com/2002-09/ruby_05.html] +# A tutorial introduction to dRuby in Linux Magazine by Dave Thomas. +# Includes a discussion of Rinda. +# +# [http://www.eng.cse.dmu.ac.uk/~hgs/ruby/dRuby/] +# Links to English-language Ruby material collected by Hugh Sasse. +# +# [http://www.rubycentral.com/book/ospace.html] +# The chapter from *Programming* *Ruby* by Dave Thomas and Andy Hunt +# which discusses dRuby. +# +# [http://www.clio.ne.jp/home/web-i31s/Flotuard/Ruby/PRC2K_seki/dRuby.en.html] +# Translation of presentation on Ruby by Masatoshi Seki. + +require 'socket' +require 'thread' +require 'fcntl' +require 'drb/eq' + +# +# == Overview +# +# dRuby is a distributed object system for Ruby. It is written in +# pure Ruby and uses its own protocol. No add-in services are needed +# beyond those provided by the Ruby runtime, such as TCP sockets. It +# does not rely on or interoperate with other distributed object +# systems such as CORBA, RMI, or .NET. +# +# dRuby allows methods to be called in one Ruby process upon a Ruby +# object located in another Ruby process, even on another machine. +# References to objects can be passed between processes. Method +# arguments and return values are dumped and loaded in marshalled +# format. All of this is done transparently to both the caller of the +# remote method and the object that it is called upon. +# +# An object in a remote process is locally represented by a +# DRb::DRbObject instance. This acts as a sort of proxy for the +# remote object. Methods called upon this DRbObject instance are +# forwarded to its remote object. This is arranged dynamically at run +# time. There are no statically declared interfaces for remote +# objects, such as CORBA's IDL. +# +# dRuby calls made into a process are handled by a DRb::DRbServer +# instance within that process. This reconstitutes the method call, +# invokes it upon the specified local object, and returns the value to +# the remote caller. Any object can receive calls over dRuby. There +# is no need to implement a special interface, or mixin special +# functionality. Nor, in the general case, does an object need to +# explicitly register itself with a DRbServer in order to receive +# dRuby calls. +# +# One process wishing to make dRuby calls upon another process must +# somehow obtain an initial reference to an object in the remote +# process by some means other than as the return value of a remote +# method call, as there is initially no remote object reference it can +# invoke a method upon. This is done by attaching to the server by +# URI. Each DRbServer binds itself to a URI such as +# 'druby://example.com:8787'. A DRbServer can have an object attached +# to it that acts as the server's *front* *object*. A DRbObject can +# be explicitly created from the server's URI. This DRbObject's +# remote object will be the server's front object. This front object +# can then return references to other Ruby objects in the DRbServer's +# process. +# +# Method calls made over dRuby behave largely the same as normal Ruby +# method calls made within a process. Method calls with blocks are +# supported, as are raising exceptions. In addition to a method's +# standard errors, a dRuby call may also raise one of the +# dRuby-specific errors, all of which are subclasses of DRb::DRbError. +# +# Any type of object can be passed as an argument to a dRuby call or +# returned as its return value. By default, such objects are dumped +# or marshalled at the local end, then loaded or unmarshalled at the +# remote end. The remote end therefore receives a copy of the local +# object, not a distributed reference to it; methods invoked upon this +# copy are executed entirely in the remote process, not passed on to +# the local original. This has semantics similar to pass-by-value. +# +# However, if an object cannot be marshalled, a dRuby reference to it +# is passed or returned instead. This will turn up at the remote end +# as a DRbObject instance. All methods invoked upon this remote proxy +# are forwarded to the local object, as described in the discussion of +# DRbObjects. This has semantics similar to the normal Ruby +# pass-by-reference. +# +# The easiest way to signal that we want an otherwise marshallable +# object to be passed or returned as a DRbObject reference, rather +# than marshalled and sent as a copy, is to include the +# DRb::DRbUndumped mixin module. +# +# dRuby supports calling remote methods with blocks. As blocks (or +# rather the Proc objects that represent them) are not marshallable, +# the block executes in the local, not the remote, context. Each +# value yielded to the block is passed from the remote object to the +# local block, then the value returned by each block invocation is +# passed back to the remote execution context to be collected, before +# the collected values are finally returned to the local context as +# the return value of the method invocation. +# +# == Examples of usage +# +# For more dRuby samples, see the +samples+ directory in the full +# dRuby distribution. +# +# === dRuby in client/server mode +# +# This illustrates setting up a simple client-server drb +# system. Run the server and client code in different terminals, +# starting the server code first. +# +# ==== Server code +# +# require 'drb/drb' +# +# # The URI for the server to connect to +# URI="druby://localhost:8787" +# +# class TimeServer +# +# def get_current_time +# return Time.now +# end +# +# end +# +# # The object that handles requests on the server +# FRONT_OBJECT=TimeServer.new +# +# $SAFE = 1 # disable eval() and friends +# +# DRb.start_service(URI, FRONT_OBJECT) +# # Wait for the drb server thread to finish before exiting. +# DRb.thread.join +# +# ==== Client code +# +# require 'drb/drb' +# +# # The URI to connect to +# SERVER_URI="druby://localhost:8787" +# +# # Start a local DRbServer to handle callbacks. +# # +# # Not necessary for this small example, but will be required +# # as soon as we pass a non-marshallable object as an argument +# # to a dRuby call. +# DRb.start_service +# +# timeserver = DRbObject.new_with_uri(SERVER_URI) +# puts timeserver.get_current_time +# +# === Remote objects under dRuby +# +# This example illustrates returning a reference to an object +# from a dRuby call. The Logger instances live in the server +# process. References to them are returned to the client process, +# where methods can be invoked upon them. These methods are +# executed in the server process. +# +# ==== Server code +# +# require 'drb/drb' +# +# URI="druby://localhost:8787" +# +# class Logger +# +# # Make dRuby send Logger instances as dRuby references, +# # not copies. +# include DRb::DRbUndumped +# +# def initialize(n, fname) +# @name = n +# @filename = fname +# end +# +# def log(message) +# File.open(@filename, "a") do |f| +# f.puts("#{Time.now}: #{@name}: #{message}") +# end +# end +# +# end +# +# # We have a central object for creating and retrieving loggers. +# # This retains a local reference to all loggers created. This +# # is so an existing logger can be looked up by name, but also +# # to prevent loggers from being garbage collected. A dRuby +# # reference to an object is not sufficient to prevent it being +# # garbage collected! +# class LoggerFactory +# +# def initialize(bdir) +# @basedir = bdir +# @loggers = {} +# end +# +# def get_logger(name) +# if !@loggers.has_key? name +# # make the filename safe, then declare it to be so +# fname = name.gsub(/[.\/]/, "_").untaint +# @loggers[name] = Logger.new(name, @basedir + "/" + fname) +# end +# return @loggers[name] +# end +# +# end +# +# FRONT_OBJECT=LoggerFactory.new("/tmp/dlog") +# +# $SAFE = 1 # disable eval() and friends +# +# DRb.start_service(URI, FRONT_OBJECT) +# DRb.thread.join +# +# ==== Client code +# +# require 'drb/drb' +# +# SERVER_URI="druby://localhost:8787" +# +# DRb.start_service +# +# log_service=DRbObject.new_with_uri(SERVER_URI) +# +# ["loga", "logb", "logc"].each do |logname| +# +# logger=log_service.get_logger(logname) +# +# logger.log("Hello, world!") +# logger.log("Goodbye, world!") +# logger.log("=== EOT ===") +# +# end +# +# == Security +# +# As with all network services, security needs to be considered when +# using dRuby. By allowing external access to a Ruby object, you are +# not only allowing outside clients to call the methods you have +# defined for that object, but by default to execute arbitrary Ruby +# code on your server. Consider the following: +# +# # !!! UNSAFE CODE !!! +# ro = DRbObject::new_with_uri("druby://your.server.com:8989") +# class << ro +# undef :instance_eval # force call to be passed to remote object +# end +# ro.instance_eval("`rm -rf *`") +# +# The dangers posed by instance_eval and friends are such that a +# DRbServer should generally be run with $SAFE set to at least +# level 1. This will disable eval() and related calls on strings +# passed across the wire. The sample usage code given above follows +# this practice. +# +# A DRbServer can be configured with an access control list to +# selectively allow or deny access from specified IP addresses. The +# main druby distribution provides the ACL class for this purpose. In +# general, this mechanism should only be used alongside, rather than +# as a replacement for, a good firewall. +# +# == dRuby internals +# +# dRuby is implemented using three main components: a remote method +# call marshaller/unmarshaller; a transport protocol; and an +# ID-to-object mapper. The latter two can be directly, and the first +# indirectly, replaced, in order to provide different behaviour and +# capabilities. +# +# Marshalling and unmarshalling of remote method calls is performed by +# a DRb::DRbMessage instance. This uses the Marshal module to dump +# the method call before sending it over the transport layer, then +# reconstitute it at the other end. There is normally no need to +# replace this component, and no direct way is provided to do so. +# However, it is possible to implement an alternative marshalling +# scheme as part of an implementation of the transport layer. +# +# The transport layer is responsible for opening client and server +# network connections and forwarding dRuby request across them. +# Normally, it uses DRb::DRbMessage internally to manage marshalling +# and unmarshalling. The transport layer is managed by +# DRb::DRbProtocol. Multiple protocols can be installed in +# DRbProtocol at the one time; selection between them is determined by +# the scheme of a dRuby URI. The default transport protocol is +# selected by the scheme 'druby:', and implemented by +# DRb::DRbTCPSocket. This uses plain TCP/IP sockets for +# communication. An alternative protocol, using UNIX domain sockets, +# is implemented by DRb::DRbUNIXSocket in the file drb/unix.rb, and +# selected by the scheme 'drbunix:'. A sample implementation over +# HTTP can be found in the samples accompanying the main dRuby +# distribution. +# +# The ID-to-object mapping component maps dRuby object ids to the +# objects they refer to, and vice versa. The implementation to use +# can be specified as part of a DRb::DRbServer's configuration. The +# default implementation is provided by DRb::DRbIdConv. It uses an +# object's ObjectSpace id as its dRuby id. This means that the dRuby +# reference to that object only remains meaningful for the lifetime of +# the object's process and the lifetime of the object within that +# process. A modified implementation is provided by DRb::TimerIdConv +# in the file drb/timeridconv.rb. This implementation retains a local +# reference to all objects exported over dRuby for a configurable +# period of time (defaulting to ten minutes), to prevent them being +# garbage-collected within this time. Another sample implementation +# is provided in sample/name.rb in the main dRuby distribution. This +# allows objects to specify their own id or "name". A dRuby reference +# can be made persistent across processes by having each process +# register an object using the same dRuby name. +# +module DRb + + # Superclass of all errors raised in the DRb module. + class DRbError < RuntimeError; end + + # Error raised when an error occurs on the underlying communication + # protocol. + class DRbConnError < DRbError; end + + # Class responsible for converting between an object and its id. + # + # This, the default implementation, uses an object's local ObjectSpace + # __id__ as its id. This means that an object's identification over + # drb remains valid only while that object instance remains alive + # within the server runtime. + # + # For alternative mechanisms, see DRb::TimerIdConv in rdb/timeridconv.rb + # and DRbNameIdConv in sample/name.rb in the full drb distribution. + class DRbIdConv + + # Convert an object reference id to an object. + # + # This implementation looks up the reference id in the local object + # space and returns the object it refers to. + def to_obj(ref) + ObjectSpace._id2ref(ref) + end + + # Convert an object into a reference id. + # + # This implementation returns the object's __id__ in the local + # object space. + def to_id(obj) + obj.nil? ? nil : obj.__id__ + end + end + + # Mixin module making an object undumpable or unmarshallable. + # + # If an object which includes this module is returned by method + # called over drb, then the object remains in the server space + # and a reference to the object is returned, rather than the + # object being marshalled and moved into the client space. + module DRbUndumped + def _dump(dummy) # :nodoc: + raise TypeError, 'can\'t dump' + end + end + + # Error raised by the DRb module when an attempt is made to refer to + # the context's current drb server but the context does not have one. + # See #current_server. + class DRbServerNotFound < DRbError; end + + # Error raised by the DRbProtocol module when it cannot find any + # protocol implementation support the scheme specified in a URI. + class DRbBadURI < DRbError; end + + # Error raised by a dRuby protocol when it doesn't support the + # scheme specified in a URI. See DRb::DRbProtocol. + class DRbBadScheme < DRbError; end + + # An exception wrapping a DRb::DRbUnknown object + class DRbUnknownError < DRbError + + # Create a new DRbUnknownError for the DRb::DRbUnknown object +unknown+ + def initialize(unknown) + @unknown = unknown + super(unknown.name) + end + + # Get the wrapped DRb::DRbUnknown object. + attr_reader :unknown + + def self._load(s) # :nodoc: + Marshal::load(s) + end + + def _dump(lv) # :nodoc: + Marshal::dump(@unknown) + end + end + + # An exception wrapping an error object + class DRbRemoteError < DRbError + def initialize(error) + @reason = error.class.to_s + super("#{error.message} (#{error.class})") + set_backtrace(error.backtrace) + end + + # the class of the error, as a string. + attr_reader :reason + end + + # Class wrapping a marshalled object whose type is unknown locally. + # + # If an object is returned by a method invoked over drb, but the + # class of the object is unknown in the client namespace, or + # the object is a constant unknown in the client namespace, then + # the still-marshalled object is returned wrapped in a DRbUnknown instance. + # + # If this object is passed as an argument to a method invoked over + # drb, then the wrapped object is passed instead. + # + # The class or constant name of the object can be read from the + # +name+ attribute. The marshalled object is held in the +buf+ + # attribute. + class DRbUnknown + + # Create a new DRbUnknown object. + # + # +buf+ is a string containing a marshalled object that could not + # be unmarshalled. +err+ is the error message that was raised + # when the unmarshalling failed. It is used to determine the + # name of the unmarshalled object. + def initialize(err, buf) + case err.to_s + when /uninitialized constant (\S+)/ + @name = $1 + when /undefined class\/module (\S+)/ + @name = $1 + else + @name = nil + end + @buf = buf + end + + # The name of the unknown thing. + # + # Class name for unknown objects; variable name for unknown + # constants. + attr_reader :name + + # Buffer contained the marshalled, unknown object. + attr_reader :buf + + def self._load(s) # :nodoc: + begin + Marshal::load(s) + rescue NameError, ArgumentError + DRbUnknown.new($!, s) + end + end + + def _dump(lv) # :nodoc: + @buf + end + + # Attempt to load the wrapped marshalled object again. + # + # If the class of the object is now known locally, the object + # will be unmarshalled and returned. Otherwise, a new + # but identical DRbUnknown object will be returned. + def reload + self.class._load(@buf) + end + + # Create a DRbUnknownError exception containing this object. + def exception + DRbUnknownError.new(self) + end + end + + class DRbArray + def initialize(ary) + @ary = ary.collect { |obj| + if obj.kind_of? DRbUndumped + DRbObject.new(obj) + else + begin + Marshal.dump(obj) + obj + rescue + DRbObject.new(obj) + end + end + } + end + + def self._load(s) + Marshal::load(s) + end + + def _dump(lv) + Marshal.dump(@ary) + end + end + + # Handler for sending and receiving drb messages. + # + # This takes care of the low-level marshalling and unmarshalling + # of drb requests and responses sent over the wire between server + # and client. This relieves the implementor of a new drb + # protocol layer with having to deal with these details. + # + # The user does not have to directly deal with this object in + # normal use. + class DRbMessage + def initialize(config) # :nodoc: + @load_limit = config[:load_limit] + @argc_limit = config[:argc_limit] + end + + def dump(obj, error=false) # :nodoc: + obj = make_proxy(obj, error) if obj.kind_of? DRbUndumped + begin + str = Marshal::dump(obj) + rescue + str = Marshal::dump(make_proxy(obj, error)) + end + [str.size].pack('N') + str + end + + def load(soc) # :nodoc: + begin + sz = soc.read(4) # sizeof (N) + rescue + raise(DRbConnError, $!.message, $!.backtrace) + end + raise(DRbConnError, 'connection closed') if sz.nil? + raise(DRbConnError, 'premature header') if sz.size < 4 + sz = sz.unpack('N')[0] + raise(DRbConnError, "too large packet #{sz}") if @load_limit < sz + begin + str = soc.read(sz) + rescue + raise(DRbConnError, $!.message, $!.backtrace) + end + raise(DRbConnError, 'connection closed') if str.nil? + raise(DRbConnError, 'premature marshal format(can\'t read)') if str.size < sz + Thread.exclusive do + begin + save = Thread.current[:drb_untaint] + Thread.current[:drb_untaint] = [] + Marshal::load(str) + rescue NameError, ArgumentError + DRbUnknown.new($!, str) + ensure + Thread.current[:drb_untaint].each do |x| + x.untaint + end + Thread.current[:drb_untaint] = save + end + end + end + + def send_request(stream, ref, msg_id, arg, b) # :nodoc: + ary = [] + ary.push(dump(ref.__drbref)) + ary.push(dump(msg_id.id2name)) + ary.push(dump(arg.length)) + arg.each do |e| + ary.push(dump(e)) + end + ary.push(dump(b)) + stream.write(ary.join('')) + rescue + raise(DRbConnError, $!.message, $!.backtrace) + end + + def recv_request(stream) # :nodoc: + ref = load(stream) + ro = DRb.to_obj(ref) + msg = load(stream) + argc = load(stream) + raise ArgumentError, 'too many arguments' if @argc_limit < argc + argv = Array.new(argc, nil) + argc.times do |n| + argv[n] = load(stream) + end + block = load(stream) + return ro, msg, argv, block + end + + def send_reply(stream, succ, result) # :nodoc: + stream.write(dump(succ) + dump(result, !succ)) + rescue + raise(DRbConnError, $!.message, $!.backtrace) + end + + def recv_reply(stream) # :nodoc: + succ = load(stream) + result = load(stream) + [succ, result] + end + + private + def make_proxy(obj, error=false) + if error + DRbRemoteError.new(obj) + else + DRbObject.new(obj) + end + end + end + + # Module managing the underlying network protocol(s) used by drb. + # + # By default, drb uses the DRbTCPSocket protocol. Other protocols + # can be defined. A protocol must define the following class methods: + # + # [open(uri, config)] Open a client connection to the server at +uri+, + # using configuration +config+. Return a protocol + # instance for this connection. + # [open_server(uri, config)] Open a server listening at +uri+, + # using configuration +config+. Return a + # protocol instance for this listener. + # [uri_option(uri, config)] Take a URI, possibly containing an option + # component (e.g. a trailing '?param=val'), + # and return a [uri, option] tuple. + # + # All of these methods should raise a DRbBadScheme error if the URI + # does not identify the protocol they support (e.g. "druby:" for + # the standard Ruby protocol). This is how the DRbProtocol module, + # given a URI, determines which protocol implementation serves that + # protocol. + # + # The protocol instance returned by #open_server must have the + # following methods: + # + # [accept] Accept a new connection to the server. Returns a protocol + # instance capable of communicating with the client. + # [close] Close the server connection. + # [uri] Get the URI for this server. + # + # The protocol instance returned by #open must have the following methods: + # + # [send_request (ref, msg_id, arg, b)] + # Send a request to +ref+ with the given message id and arguments. + # This is most easily implemented by calling DRbMessage.send_request, + # providing a stream that sits on top of the current protocol. + # [recv_reply] + # Receive a reply from the server and return it as a [success-boolean, + # reply-value] pair. This is most easily implemented by calling + # DRb.recv_reply, providing a stream that sits on top of the + # current protocol. + # [alive?] + # Is this connection still alive? + # [close] + # Close this connection. + # + # The protocol instance returned by #open_server().accept() must have + # the following methods: + # + # [recv_request] + # Receive a request from the client and return a [object, message, + # args, block] tuple. This is most easily implemented by calling + # DRbMessage.recv_request, providing a stream that sits on top of + # the current protocol. + # [send_reply(succ, result)] + # Send a reply to the client. This is most easily implemented + # by calling DRbMessage.send_reply, providing a stream that sits + # on top of the current protocol. + # [close] + # Close this connection. + # + # A new protocol is registered with the DRbProtocol module using + # the add_protocol method. + # + # For examples of other protocols, see DRbUNIXSocket in drb/unix.rb, + # and HTTP0 in sample/http0.rb and sample/http0serv.rb in the full + # drb distribution. + module DRbProtocol + + # Add a new protocol to the DRbProtocol module. + def add_protocol(prot) + @protocol.push(prot) + end + module_function :add_protocol + + # Open a client connection to +uri+ with the configuration +config+. + # + # The DRbProtocol module asks each registered protocol in turn to + # try to open the URI. Each protocol signals that it does not handle that + # URI by raising a DRbBadScheme error. If no protocol recognises the + # URI, then a DRbBadURI error is raised. If a protocol accepts the + # URI, but an error occurs in opening it, a DRbConnError is raised. + def open(uri, config, first=true) + @protocol.each do |prot| + begin + return prot.open(uri, config) + rescue DRbBadScheme + rescue DRbConnError + raise($!) + rescue + raise(DRbConnError, "#{uri} - #{$!.inspect}") + end + end + if first && (config[:auto_load] != false) + auto_load(uri, config) + return open(uri, config, false) + end + raise DRbBadURI, 'can\'t parse uri:' + uri + end + module_function :open + + # Open a server listening for connections at +uri+ with + # configuration +config+. + # + # The DRbProtocol module asks each registered protocol in turn to + # try to open a server at the URI. Each protocol signals that it does + # not handle that URI by raising a DRbBadScheme error. If no protocol + # recognises the URI, then a DRbBadURI error is raised. If a protocol + # accepts the URI, but an error occurs in opening it, the underlying + # error is passed on to the caller. + def open_server(uri, config, first=true) + @protocol.each do |prot| + begin + return prot.open_server(uri, config) + rescue DRbBadScheme + end + end + if first && (config[:auto_load] != false) + auto_load(uri, config) + return open_server(uri, config, false) + end + raise DRbBadURI, 'can\'t parse uri:' + uri + end + module_function :open_server + + # Parse +uri+ into a [uri, option] pair. + # + # The DRbProtocol module asks each registered protocol in turn to + # try to parse the URI. Each protocol signals that it does not handle that + # URI by raising a DRbBadScheme error. If no protocol recognises the + # URI, then a DRbBadURI error is raised. + def uri_option(uri, config, first=true) + @protocol.each do |prot| + begin + uri, opt = prot.uri_option(uri, config) + # opt = nil if opt == '' + return uri, opt + rescue DRbBadScheme + end + end + if first && (config[:auto_load] != false) + auto_load(uri, config) + return uri_option(uri, config, false) + end + raise DRbBadURI, 'can\'t parse uri:' + uri + end + module_function :uri_option + + def auto_load(uri, config) # :nodoc: + if uri =~ /^drb([a-z0-9]+):/ + require("drb/#{$1}") rescue nil + end + end + module_function :auto_load + end + + # The default drb protocol. + # + # Communicates over a TCP socket. + class DRbTCPSocket + private + def self.parse_uri(uri) + if uri =~ /^druby:\/\/(.*?):(\d+)(\?(.*))?$/ + host = $1 + port = $2.to_i + option = $4 + [host, port, option] + else + raise(DRbBadScheme, uri) unless uri =~ /^druby:/ + raise(DRbBadURI, 'can\'t parse uri:' + uri) + end + end + + public + + # Open a client connection to +uri+ using configuration +config+. + def self.open(uri, config) + host, port, option = parse_uri(uri) + host.untaint + port.untaint + soc = TCPSocket.open(host, port) + self.new(uri, soc, config) + end + + def self.getservername + host = Socket::gethostname + begin + Socket::gethostbyname(host)[0] + rescue + 'localhost' + end + end + + def self.open_server_inaddr_any(host, port) + infos = Socket::getaddrinfo(host, nil, + Socket::AF_UNSPEC, + Socket::SOCK_STREAM, + 0, + Socket::AI_PASSIVE) + family = infos.collect { |af, *_| af }.uniq + case family + when ['AF_INET'] + return TCPServer.open('0.0.0.0', port) + when ['AF_INET6'] + return TCPServer.open('::', port) + else + return TCPServer.open(port) + end + end + + # Open a server listening for connections at +uri+ using + # configuration +config+. + def self.open_server(uri, config) + uri = 'druby://:0' unless uri + host, port, opt = parse_uri(uri) + if host.size == 0 + host = getservername + soc = open_server_inaddr_any(host, port) + else + soc = TCPServer.open(host, port) + end + port = soc.addr[1] if port == 0 + uri = "druby://#{host}:#{port}" + self.new(uri, soc, config) + end + + # Parse +uri+ into a [uri, option] pair. + def self.uri_option(uri, config) + host, port, option = parse_uri(uri) + return "druby://#{host}:#{port}", option + end + + # Create a new DRbTCPSocket instance. + # + # +uri+ is the URI we are connected to. + # +soc+ is the tcp socket we are bound to. +config+ is our + # configuration. + def initialize(uri, soc, config={}) + @uri = uri + @socket = soc + @config = config + @acl = config[:tcp_acl] + @msg = DRbMessage.new(config) + set_sockopt(@socket) + end + + # Get the URI that we are connected to. + attr_reader :uri + + # Get the address of our TCP peer (the other end of the socket + # we are bound to. + def peeraddr + @socket.peeraddr + end + + # Get the socket. + def stream; @socket; end + + # On the client side, send a request to the server. + def send_request(ref, msg_id, arg, b) + @msg.send_request(stream, ref, msg_id, arg, b) + end + + # On the server side, receive a request from the client. + def recv_request + @msg.recv_request(stream) + end + + # On the server side, send a reply to the client. + def send_reply(succ, result) + @msg.send_reply(stream, succ, result) + end + + # On the client side, receive a reply from the server. + def recv_reply + @msg.recv_reply(stream) + end + + public + + # Close the connection. + # + # If this is an instance returned by #open_server, then this stops + # listening for new connections altogether. If this is an instance + # returned by #open or by #accept, then it closes this particular + # client-server session. + def close + if @socket + @socket.close + @socket = nil + end + end + + # On the server side, for an instance returned by #open_server, + # accept a client connection and return a new instance to handle + # the server's side of this client-server session. + def accept + while true + s = @socket.accept + break if (@acl ? @acl.allow_socket?(s) : true) + s.close + end + self.class.new(nil, s, @config) + end + + # Check to see if this connection is alive. + def alive? + return false unless @socket + if IO.select([@socket], nil, nil, 0) + close + return false + end + true + end + + def set_sockopt(soc) # :nodoc: + soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) + soc.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC + end + end + + module DRbProtocol + @protocol = [DRbTCPSocket] # default + end + + class DRbURIOption # :nodoc: I don't understand the purpose of this class... + def initialize(option) + @option = option.to_s + end + attr :option + def to_s; @option; end + + def ==(other) + return false unless DRbURIOption === other + @option == other.option + end + + def hash + @option.hash + end + + alias eql? == + end + + # Object wrapping a reference to a remote drb object. + # + # Method calls on this object are relayed to the remote + # object that this object is a stub for. + class DRbObject + + # Unmarshall a marshalled DRbObject. + # + # If the referenced object is located within the local server, then + # the object itself is returned. Otherwise, a new DRbObject is + # created to act as a stub for the remote referenced object. + def self._load(s) + uri, ref = Marshal.load(s) + + if DRb.here?(uri) + obj = DRb.to_obj(ref) + if ((! obj.tainted?) && Thread.current[:drb_untaint]) + Thread.current[:drb_untaint].push(obj) + end + return obj + end + + self.new_with(uri, ref) + end + + def self.new_with(uri, ref) + it = self.allocate + it.instance_variable_set('@uri', uri) + it.instance_variable_set('@ref', ref) + it + end + + # Create a new DRbObject from a URI alone. + def self.new_with_uri(uri) + self.new(nil, uri) + end + + # Marshall this object. + # + # The URI and ref of the object are marshalled. + def _dump(lv) + Marshal.dump([@uri, @ref]) + end + + # Create a new remote object stub. + # + # +obj+ is the (local) object we want to create a stub for. Normally + # this is +nil+. +uri+ is the URI of the remote object that this + # will be a stub for. + def initialize(obj, uri=nil) + @uri = nil + @ref = nil + if obj.nil? + return if uri.nil? + @uri, option = DRbProtocol.uri_option(uri, DRb.config) + @ref = DRbURIOption.new(option) unless option.nil? + else + @uri = uri ? uri : (DRb.uri rescue nil) + @ref = obj ? DRb.to_id(obj) : nil + end + end + + # Get the URI of the remote object. + def __drburi + @uri + end + + # Get the reference of the object, if local. + def __drbref + @ref + end + + undef :to_s + undef :to_a if respond_to?(:to_a) + + def respond_to?(msg_id, priv=false) + case msg_id + when :_dump + true + when :marshal_dump + false + else + method_missing(:respond_to?, msg_id, priv) + end + end + + # Routes method calls to the referenced object. + def method_missing(msg_id, *a, &b) + if DRb.here?(@uri) + obj = DRb.to_obj(@ref) + DRb.current_server.check_insecure_method(obj, msg_id) + return obj.__send__(msg_id, *a, &b) + end + + succ, result = self.class.with_friend(@uri) do + DRbConn.open(@uri) do |conn| + conn.send_message(self, msg_id, a, b) + end + end + + if succ + return result + elsif DRbUnknown === result + raise result + else + bt = self.class.prepare_backtrace(@uri, result) + result.set_backtrace(bt + caller) + raise result + end + end + + def self.with_friend(uri) + friend = DRb.fetch_server(uri) + return yield() unless friend + + save = Thread.current['DRb'] + Thread.current['DRb'] = { 'server' => friend } + return yield + ensure + Thread.current['DRb'] = save if friend + end + + def self.prepare_backtrace(uri, result) + prefix = "(#{uri}) " + bt = [] + result.backtrace.each do |x| + break if /`__send__'$/ =~ x + if /^\(druby:\/\// =~ x + bt.push(x) + else + bt.push(prefix + x) + end + end + bt + end + + def pretty_print(q) # :nodoc: + q.pp_object(self) + end + + def pretty_print_cycle(q) # :nodoc: + q.object_address_group(self) { + q.breakable + q.text '...' + } + end + end + + # Class handling the connection between a DRbObject and the + # server the real object lives on. + # + # This class maintains a pool of connections, to reduce the + # overhead of starting and closing down connections for each + # method call. + # + # This class is used internally by DRbObject. The user does + # not normally need to deal with it directly. + class DRbConn + POOL_SIZE = 16 # :nodoc: + @mutex = Mutex.new + @pool = [] + + def self.open(remote_uri) # :nodoc: + begin + conn = nil + + @mutex.synchronize do + #FIXME + new_pool = [] + @pool.each do |c| + if conn.nil? and c.uri == remote_uri + conn = c if c.alive? + else + new_pool.push c + end + end + @pool = new_pool + end + + conn = self.new(remote_uri) unless conn + succ, result = yield(conn) + return succ, result + + ensure + if conn + if succ + @mutex.synchronize do + @pool.unshift(conn) + @pool.pop.close while @pool.size > POOL_SIZE + end + else + conn.close + end + end + end + end + + def initialize(remote_uri) # :nodoc: + @uri = remote_uri + @protocol = DRbProtocol.open(remote_uri, DRb.config) + end + attr_reader :uri # :nodoc: + + def send_message(ref, msg_id, arg, block) # :nodoc: + @protocol.send_request(ref, msg_id, arg, block) + @protocol.recv_reply + end + + def close # :nodoc: + @protocol.close + @protocol = nil + end + + def alive? # :nodoc: + @protocol.alive? + end + end + + # Class representing a drb server instance. + # + # A DRbServer must be running in the local process before any incoming + # dRuby calls can be accepted, or any local objects can be passed as + # dRuby references to remote processes, even if those local objects are + # never actually called remotely. You do not need to start a DRbServer + # in the local process if you are only making outgoing dRuby calls + # passing marshalled parameters. + # + # Unless multiple servers are being used, the local DRbServer is normally + # started by calling DRb.start_service. + class DRbServer + @@acl = nil + @@idconv = DRbIdConv.new + @@secondary_server = nil + @@argc_limit = 256 + @@load_limit = 256 * 102400 + @@verbose = false + @@safe_level = 0 + + # Set the default value for the :argc_limit option. + # + # See #new(). The initial default value is 256. + def self.default_argc_limit(argc) + @@argc_limit = argc + end + + # Set the default value for the :load_limit option. + # + # See #new(). The initial default value is 25 MB. + def self.default_load_limit(sz) + @@load_limit = sz + end + + # Set the default value for the :acl option. + # + # See #new(). The initial default value is nil. + def self.default_acl(acl) + @@acl = acl + end + + # Set the default value for the :id_conv option. + # + # See #new(). The initial default value is a DRbIdConv instance. + def self.default_id_conv(idconv) + @@idconv = idconv + end + + def self.default_safe_level(level) + @@safe_level = level + end + + # Set the default value of the :verbose option. + # + # See #new(). The initial default value is false. + def self.verbose=(on) + @@verbose = on + end + + # Get the default value of the :verbose option. + def self.verbose + @@verbose + end + + def self.make_config(hash={}) # :nodoc: + default_config = { + :idconv => @@idconv, + :verbose => @@verbose, + :tcp_acl => @@acl, + :load_limit => @@load_limit, + :argc_limit => @@argc_limit, + :safe_level => @@safe_level + } + default_config.update(hash) + end + + # Create a new DRbServer instance. + # + # +uri+ is the URI to bind to. This is normally of the form + # 'druby://:' where is a hostname of + # the local machine. If nil, then the system's default hostname + # will be bound to, on a port selected by the system; these value + # can be retrieved from the +uri+ attribute. 'druby:' specifies + # the default dRuby transport protocol: another protocol, such + # as 'drbunix:', can be specified instead. + # + # +front+ is the front object for the server, that is, the object + # to which remote method calls on the server will be passed. If + # nil, then the server will not accept remote method calls. + # + # If +config_or_acl+ is a hash, it is the configuration to + # use for this server. The following options are recognised: + # + # :idconv :: an id-to-object conversion object. This defaults + # to an instance of the class DRb::DRbIdConv. + # :verbose :: if true, all unsuccessful remote calls on objects + # in the server will be logged to $stdout. false + # by default. + # :tcp_acl :: the access control list for this server. See + # the ACL class from the main dRuby distribution. + # :load_limit :: the maximum message size in bytes accepted by + # the server. Defaults to 25 MB (26214400). + # :argc_limit :: the maximum number of arguments to a remote + # method accepted by the server. Defaults to + # 256. + # + # The default values of these options can be modified on + # a class-wide basis by the class methods #default_argc_limit, + # #default_load_limit, #default_acl, #default_id_conv, + # and #verbose= + # + # If +config_or_acl+ is not a hash, but is not nil, it is + # assumed to be the access control list for this server. + # See the :tcp_acl option for more details. + # + # If no other server is currently set as the primary server, + # this will become the primary server. + # + # The server will immediately start running in its own thread. + def initialize(uri=nil, front=nil, config_or_acl=nil) + if Hash === config_or_acl + config = config_or_acl.dup + else + acl = config_or_acl || @@acl + config = { + :tcp_acl => acl + } + end + + @config = self.class.make_config(config) + + @protocol = DRbProtocol.open_server(uri, @config) + @uri = @protocol.uri + + @front = front + @idconv = @config[:idconv] + @safe_level = @config[:safe_level] + + @grp = ThreadGroup.new + @thread = run + + DRb.regist_server(self) + end + + # The URI of this DRbServer. + attr_reader :uri + + # The main thread of this DRbServer. + # + # This is the thread that listens for and accepts connections + # from clients, not that handles each client's request-response + # session. + attr_reader :thread + + # The front object of the DRbServer. + # + # This object receives remote method calls made on the server's + # URI alone, with an object id. + attr_reader :front + + # The configuration of this DRbServer + attr_reader :config + + attr_reader :safe_level + + # Set whether to operate in verbose mode. + # + # In verbose mode, failed calls are logged to stdout. + def verbose=(v); @config[:verbose]=v; end + + # Get whether the server is in verbose mode. + # + # In verbose mode, failed calls are logged to stdout. + def verbose; @config[:verbose]; end + + # Is this server alive? + def alive? + @thread.alive? + end + + # Stop this server. + def stop_service + DRb.remove_server(self) + if Thread.current['DRb'] && Thread.current['DRb']['server'] == self + Thread.current['DRb']['stop_service'] = true + else + @thread.kill + end + end + + # Convert a dRuby reference to the local object it refers to. + def to_obj(ref) + return front if ref.nil? + return front[ref.to_s] if DRbURIOption === ref + @idconv.to_obj(ref) + end + + # Convert a local object to a dRuby reference. + def to_id(obj) + return nil if obj.__id__ == front.__id__ + @idconv.to_id(obj) + end + + private + def kill_sub_thread + Thread.new do + grp = ThreadGroup.new + grp.add(Thread.current) + list = @grp.list + while list.size > 0 + list.each do |th| + th.kill if th.alive? + end + list = @grp.list + end + end + end + + def run + Thread.start do + begin + while true + main_loop + end + ensure + @protocol.close if @protocol + kill_sub_thread + end + end + end + + # List of insecure methods. + # + # These methods are not callable via dRuby. + INSECURE_METHOD = [ + :__send__ + ] + + # Has a method been included in the list of insecure methods? + def insecure_method?(msg_id) + INSECURE_METHOD.include?(msg_id) + end + + # Coerce an object to a string, providing our own representation if + # to_s is not defined for the object. + def any_to_s(obj) + obj.to_s + ":#{obj.class}" + rescue + sprintf("#<%s:0x%lx>", obj.class, obj.__id__) + end + + # Check that a method is callable via dRuby. + # + # +obj+ is the object we want to invoke the method on. +msg_id+ is the + # method name, as a Symbol. + # + # If the method is an insecure method (see #insecure_method?) a + # SecurityError is thrown. If the method is private or undefined, + # a NameError is thrown. + def check_insecure_method(obj, msg_id) + return true if Proc === obj && msg_id == :__drb_yield + raise(ArgumentError, "#{any_to_s(msg_id)} is not a symbol") unless Symbol == msg_id.class + raise(SecurityError, "insecure method `#{msg_id}'") if insecure_method?(msg_id) + + if obj.private_methods.include?(msg_id.to_s) + desc = any_to_s(obj) + raise NoMethodError, "private method `#{msg_id}' called for #{desc}" + elsif obj.protected_methods.include?(msg_id.to_s) + desc = any_to_s(obj) + raise NoMethodError, "protected method `#{msg_id}' called for #{desc}" + else + true + end + end + public :check_insecure_method + + class InvokeMethod # :nodoc: + def initialize(drb_server, client) + @drb_server = drb_server + @safe_level = drb_server.safe_level + @client = client + end + + def perform + @result = nil + @succ = false + setup_message + + if $SAFE < @safe_level + info = Thread.current['DRb'] + if @block + @result = Thread.new { + Thread.current['DRb'] = info + $SAFE = @safe_level + perform_with_block + }.value + else + @result = Thread.new { + Thread.current['DRb'] = info + $SAFE = @safe_level + perform_without_block + }.value + end + else + if @block + @result = perform_with_block + else + @result = perform_without_block + end + end + @succ = true + if @msg_id == :to_ary && @result.class == Array + @result = DRbArray.new(@result) + end + return @succ, @result + rescue StandardError, ScriptError, Interrupt + @result = $! + return @succ, @result + end + + private + def init_with_client + obj, msg, argv, block = @client.recv_request + @obj = obj + @msg_id = msg.intern + @argv = argv + @block = block + end + + def check_insecure_method + @drb_server.check_insecure_method(@obj, @msg_id) + end + + def setup_message + init_with_client + check_insecure_method + end + + def perform_without_block + if Proc === @obj && @msg_id == :__drb_yield + if @argv.size == 1 + ary = @argv + else + ary = [@argv] + end + ary.collect(&@obj)[0] + else + @obj.__send__(@msg_id, *@argv) + end + end + + end + + if RUBY_VERSION >= '1.8' + require 'drb/invokemethod' + class InvokeMethod + include InvokeMethod18Mixin + end + else + require 'drb/invokemethod16' + class InvokeMethod + include InvokeMethod16Mixin + end + end + + # The main loop performed by a DRbServer's internal thread. + # + # Accepts a connection from a client, and starts up its own + # thread to handle it. This thread loops, receiving requests + # from the client, invoking them on a local object, and + # returning responses, until the client closes the connection + # or a local method call fails. + def main_loop + Thread.start(@protocol.accept) do |client| + @grp.add Thread.current + Thread.current['DRb'] = { 'client' => client , + 'server' => self } + loop do + begin + succ = false + invoke_method = InvokeMethod.new(self, client) + succ, result = invoke_method.perform + if !succ && verbose + p result + result.backtrace.each do |x| + puts x + end + end + client.send_reply(succ, result) rescue nil + ensure + client.close unless succ + if Thread.current['DRb']['stop_service'] + Thread.new { stop_service } + end + break unless succ + end + end + end + end + end + + @primary_server = nil + + # Start a dRuby server locally. + # + # The new dRuby server will become the primary server, even + # if another server is currently the primary server. + # + # +uri+ is the URI for the server to bind to. If nil, + # the server will bind to random port on the default local host + # name and use the default dRuby protocol. + # + # +front+ is the server's front object. This may be nil. + # + # +config+ is the configuration for the new server. This may + # be nil. + # + # See DRbServer::new. + def start_service(uri=nil, front=nil, config=nil) + @primary_server = DRbServer.new(uri, front, config) + end + module_function :start_service + + # The primary local dRuby server. + # + # This is the server created by the #start_service call. + attr_accessor :primary_server + module_function :primary_server=, :primary_server + + # Get the 'current' server. + # + # In the context of execution taking place within the main + # thread of a dRuby server (typically, as a result of a remote + # call on the server or one of its objects), the current + # server is that server. Otherwise, the current server is + # the primary server. + # + # If the above rule fails to find a server, a DRbServerNotFound + # error is raised. + def current_server + drb = Thread.current['DRb'] + server = (drb && drb['server']) ? drb['server'] : @primary_server + raise DRbServerNotFound unless server + return server + end + module_function :current_server + + # Stop the local dRuby server. + # + # This operates on the primary server. If there is no primary + # server currently running, it is a noop. + def stop_service + @primary_server.stop_service if @primary_server + @primary_server = nil + end + module_function :stop_service + + # Get the URI defining the local dRuby space. + # + # This is the URI of the current server. See #current_server. + def uri + current_server.uri + end + module_function :uri + + # Is +uri+ the URI for the current local server? + def here?(uri) + (current_server.uri rescue nil) == uri + end + module_function :here? + + # Get the configuration of the current server. + # + # If there is no current server, this returns the default configuration. + # See #current_server and DRbServer::make_config. + def config + current_server.config + rescue + DRbServer.make_config + end + module_function :config + + # Get the front object of the current server. + # + # This raises a DRbServerNotFound error if there is no current server. + # See #current_server. + def front + current_server.front + end + module_function :front + + # Convert a reference into an object using the current server. + # + # This raises a DRbServerNotFound error if there is no current server. + # See #current_server. + def to_obj(ref) + current_server.to_obj(ref) + end + + # Get a reference id for an object using the current server. + # + # This raises a DRbServerNotFound error if there is no current server. + # See #current_server. + def to_id(obj) + current_server.to_id(obj) + end + module_function :to_id + module_function :to_obj + + # Get the thread of the primary server. + # + # This returns nil if there is no primary server. See #primary_server. + def thread + @primary_server ? @primary_server.thread : nil + end + module_function :thread + + # Set the default id conv object. + # + # See DRbServer#default_id_conv. + def install_id_conv(idconv) + DRbServer.default_id_conv(idconv) + end + module_function :install_id_conv + + # Set the default acl. + # + # See DRb::DRbServer.default_acl. + def install_acl(acl) + DRbServer.default_acl(acl) + end + module_function :install_acl + + @server = {} + def regist_server(server) + @server[server.uri] = server + Thread.exclusive do + @primary_server = server unless @primary_server + end + end + module_function :regist_server + + def remove_server(server) + @server.delete(server.uri) + end + module_function :remove_server + + def fetch_server(uri) + @server[uri] + end + module_function :fetch_server +end + +DRbObject = DRb::DRbObject +DRbUndumped = DRb::DRbUndumped +DRbIdConv = DRb::DRbIdConv diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/eq.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/eq.rb new file mode 100644 index 0000000000..e24512d6a7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/eq.rb @@ -0,0 +1,16 @@ +require 'drb/drb' + +module DRb + class DRbObject + def ==(other) + return false unless DRbObject === other + (@ref == other.__drbref) && (@uri == other.__drburi) + end + + def hash + [@uri, @ref].hash + end + + alias eql? == + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/extserv.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/extserv.rb new file mode 100644 index 0000000000..7da8130c2b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/extserv.rb @@ -0,0 +1,64 @@ +=begin + external service + Copyright (c) 2000,2002 Masatoshi SEKI +=end + +require 'drb/drb' + +module DRb + class ExtServ + include DRbUndumped + + def initialize(there, name, server=nil) + @server = server || DRb::primary_server + @name = name + ro = DRbObject.new(nil, there) + @invoker = ro.regist(name, DRbObject.new(self, @server.uri)) + end + attr_reader :server + + def front + DRbObject.new(nil, @server.uri) + end + + def stop_service + @invoker.unregist(@name) + server = @server + @server = nil + server.stop_service + true + end + + def alive? + @server ? @server.alive? : false + end + end +end + +if __FILE__ == $0 + class Foo + include DRbUndumped + + def initialize(str) + @str = str + end + + def hello(it) + "#{it}: #{self}" + end + + def to_s + @str + end + end + + cmd = ARGV.shift + case cmd + when 'itest1', 'itest2' + front = Foo.new(cmd) + manager = DRb::DRbServer.new(nil, front) + es = DRb::ExtServ.new(ARGV.shift, ARGV.shift, manager) + es.server.thread.join + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/extservm.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/extservm.rb new file mode 100644 index 0000000000..7066f84c65 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/extservm.rb @@ -0,0 +1,96 @@ +=begin + external service manager + Copyright (c) 2000 Masatoshi SEKI +=end + +require 'drb/drb' +require 'thread' + +module DRb + class ExtServManager + include DRbUndumped + + @@command = {} + + def self.command + @@command + end + + def self.command=(cmd) + @@command = cmd + end + + def initialize + @servers = {} + @waiting = [] + @queue = Queue.new + @thread = invoke_thread + @uri = nil + end + attr_accessor :uri + + def service(name) + while true + server = nil + Thread.exclusive do + server = @servers[name] if @servers[name] + end + return server if server && server.alive? + invoke_service(name) + end + end + + def regist(name, ro) + ary = nil + Thread.exclusive do + @servers[name] = ro + ary = @waiting + @waiting = [] + end + ary.each do |th| + begin + th.run + rescue ThreadError + end + end + self + end + + def unregist(name) + Thread.exclusive do + @servers.delete(name) + end + end + + private + def invoke_thread + Thread.new do + while true + name = @queue.pop + invoke_service_command(name, @@command[name]) + end + end + end + + def invoke_service(name) + Thread.critical = true + @waiting.push Thread.current + @queue.push name + Thread.stop + end + + def invoke_service_command(name, command) + raise "invalid command. name: #{name}" unless command + Thread.exclusive do + return if @servers.include?(name) + @servers[name] = false + end + uri = @uri || DRb.uri + if RUBY_PLATFORM =~ /mswin32/ && /NT/ =~ ENV["OS"] + system(%Q'cmd /c start "ruby" /b #{command} #{uri} #{name}') + else + system("#{command} #{uri} #{name} &") + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/gw.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/gw.rb new file mode 100644 index 0000000000..b7a5f5383f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/gw.rb @@ -0,0 +1,122 @@ +require 'drb/drb' +require 'monitor' + +module DRb + class GWIdConv < DRbIdConv + def to_obj(ref) + if Array === ref && ref[0] == :DRbObject + return DRbObject.new_with(ref[1], ref[2]) + end + super(ref) + end + end + + class GW + include MonitorMixin + def initialize + super() + @hash = {} + end + + def [](key) + synchronize do + @hash[key] + end + end + + def []=(key, v) + synchronize do + @hash[key] = v + end + end + end + + class DRbObject + def self._load(s) + uri, ref = Marshal.load(s) + if DRb.uri == uri + return ref ? DRb.to_obj(ref) : DRb.front + end + + self.new_with(DRb.uri, [:DRbObject, uri, ref]) + end + + def _dump(lv) + if DRb.uri == @uri + if Array === @ref && @ref[0] == :DRbObject + Marshal.dump([@ref[1], @ref[2]]) + else + Marshal.dump([@uri, @ref]) # ?? + end + else + Marshal.dump([DRb.uri, [:DRbObject, @uri, @ref]]) + end + end + end +end + +=begin +DRb.install_id_conv(DRb::GWIdConv.new) + +front = DRb::GW.new + +s1 = DRb::DRbServer.new('drbunix:/tmp/gw_b_a', front) +s2 = DRb::DRbServer.new('drbunix:/tmp/gw_b_c', front) + +s1.thread.join +s2.thread.join +=end + +=begin +# foo.rb + +require 'drb/drb' + +class Foo + include DRbUndumped + def initialize(name, peer=nil) + @name = name + @peer = peer + end + + def ping(obj) + puts "#{@name}: ping: #{obj.inspect}" + @peer.ping(self) if @peer + end +end +=end + +=begin +# gw_a.rb +require 'drb/unix' +require 'foo' + +obj = Foo.new('a') +DRb.start_service("drbunix:/tmp/gw_a", obj) + +robj = DRbObject.new_with_uri('drbunix:/tmp/gw_b_a') +robj[:a] = obj + +DRb.thread.join +=end + +=begin +# gw_c.rb +require 'drb/unix' +require 'foo' + +foo = Foo.new('c', nil) + +DRb.start_service("drbunix:/tmp/gw_c", nil) + +robj = DRbObject.new_with_uri("drbunix:/tmp/gw_b_c") + +puts "c->b" +a = robj[:a] +sleep 2 + +a.ping(foo) + +DRb.thread.join +=end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/invokemethod.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/invokemethod.rb new file mode 100644 index 0000000000..412b2ab9b5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/invokemethod.rb @@ -0,0 +1,36 @@ +# for ruby-1.8.0 + +module DRb + class DRbServer + module InvokeMethod18Mixin + def block_yield(x) + if x.size == 1 && x[0].class == Array + x[0] = DRbArray.new(x[0]) + end + block_value = @block.call(*x) + end + + def perform_with_block + @obj.__send__(@msg_id, *@argv) do |*x| + jump_error = nil + begin + block_value = block_yield(x) + rescue LocalJumpError + jump_error = $! + end + if jump_error + case jump_error.reason + when :retry + retry + when :break + break(jump_error.exit_value) + else + raise jump_error + end + end + block_value + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/observer.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/observer.rb new file mode 100644 index 0000000000..e7f1668c52 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/observer.rb @@ -0,0 +1,22 @@ +require 'observer' + +module DRb + module DRbObservable + include Observable + + def notify_observers(*arg) + if defined? @observer_state and @observer_state + if defined? @observer_peers + for i in @observer_peers.dup + begin + i.update(*arg) + rescue + delete_observer(i) + end + end + end + @observer_state = false + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/ssl.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/ssl.rb new file mode 100644 index 0000000000..58d6b7d1e0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/ssl.rb @@ -0,0 +1,190 @@ +require 'socket' +require 'openssl' +require 'drb/drb' +require 'singleton' + +module DRb + + class DRbSSLSocket < DRbTCPSocket + + class SSLConfig + + DEFAULT = { + :SSLCertificate => nil, + :SSLPrivateKey => nil, + :SSLClientCA => nil, + :SSLCACertificatePath => nil, + :SSLCACertificateFile => nil, + :SSLVerifyMode => ::OpenSSL::SSL::VERIFY_NONE, + :SSLVerifyDepth => nil, + :SSLVerifyCallback => nil, # custom verification + :SSLCertificateStore => nil, + # Must specify if you use auto generated certificate. + :SSLCertName => nil, # e.g. [["CN","fqdn.example.com"]] + :SSLCertComment => "Generated by Ruby/OpenSSL" + } + + def initialize(config) + @config = config + @cert = config[:SSLCertificate] + @pkey = config[:SSLPrivateKey] + @ssl_ctx = nil + end + + def [](key); + @config[key] || DEFAULT[key] + end + + def connect(tcp) + ssl = ::OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx) + ssl.sync = true + ssl.connect + ssl + end + + def accept(tcp) + ssl = OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx) + ssl.sync = true + ssl.accept + ssl + end + + def setup_certificate + if @cert && @pkey + return + end + + rsa = OpenSSL::PKey::RSA.new(512){|p, n| + next unless self[:verbose] + case p + when 0; $stderr.putc "." # BN_generate_prime + when 1; $stderr.putc "+" # BN_generate_prime + when 2; $stderr.putc "*" # searching good prime, + # n = #of try, + # but also data from BN_generate_prime + when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q, + # but also data from BN_generate_prime + else; $stderr.putc "*" # BN_generate_prime + end + } + + cert = OpenSSL::X509::Certificate.new + cert.version = 3 + cert.serial = 0 + name = OpenSSL::X509::Name.new(self[:SSLCertName]) + cert.subject = name + cert.issuer = name + cert.not_before = Time.now + cert.not_after = Time.now + (365*24*60*60) + cert.public_key = rsa.public_key + + ef = OpenSSL::X509::ExtensionFactory.new(nil,cert) + cert.extensions = [ + ef.create_extension("basicConstraints","CA:FALSE"), + ef.create_extension("subjectKeyIdentifier", "hash") ] + ef.issuer_certificate = cert + cert.add_extension(ef.create_extension("authorityKeyIdentifier", + "keyid:always,issuer:always")) + if comment = self[:SSLCertComment] + cert.add_extension(ef.create_extension("nsComment", comment)) + end + cert.sign(rsa, OpenSSL::Digest::SHA1.new) + + @cert = cert + @pkey = rsa + end + + def setup_ssl_context + ctx = ::OpenSSL::SSL::SSLContext.new + ctx.cert = @cert + ctx.key = @pkey + ctx.client_ca = self[:SSLClientCA] + ctx.ca_path = self[:SSLCACertificatePath] + ctx.ca_file = self[:SSLCACertificateFile] + ctx.verify_mode = self[:SSLVerifyMode] + ctx.verify_depth = self[:SSLVerifyDepth] + ctx.verify_callback = self[:SSLVerifyCallback] + ctx.cert_store = self[:SSLCertificateStore] + @ssl_ctx = ctx + end + end + + def self.parse_uri(uri) + if uri =~ /^drbssl:\/\/(.*?):(\d+)(\?(.*))?$/ + host = $1 + port = $2.to_i + option = $4 + [host, port, option] + else + raise(DRbBadScheme, uri) unless uri =~ /^drbssl:/ + raise(DRbBadURI, 'can\'t parse uri:' + uri) + end + end + + def self.open(uri, config) + host, port, option = parse_uri(uri) + host.untaint + port.untaint + soc = TCPSocket.open(host, port) + ssl_conf = SSLConfig::new(config) + ssl_conf.setup_ssl_context + ssl = ssl_conf.connect(soc) + self.new(uri, ssl, ssl_conf, true) + end + + def self.open_server(uri, config) + uri = 'drbssl://:0' unless uri + host, port, opt = parse_uri(uri) + if host.size == 0 + host = getservername + soc = open_server_inaddr_any(host, port) + else + soc = TCPServer.open(host, port) + end + port = soc.addr[1] if port == 0 + @uri = "drbssl://#{host}:#{port}" + + ssl_conf = SSLConfig.new(config) + ssl_conf.setup_certificate + ssl_conf.setup_ssl_context + self.new(@uri, soc, ssl_conf, false) + end + + def self.uri_option(uri, config) + host, port, option = parse_uri(uri) + return "drbssl://#{host}:#{port}", option + end + + def initialize(uri, soc, config, is_established) + @ssl = is_established ? soc : nil + super(uri, soc.to_io, config) + end + + def stream; @ssl; end + + def close + if @ssl + @ssl.close + @ssl = nil + end + super + end + + def accept + begin + while true + soc = @socket.accept + break if (@acl ? @acl.allow_socket?(soc) : true) + soc.close + end + ssl = @config.accept(soc) + self.class.new(uri, ssl, @config, true) + rescue OpenSSL::SSL::SSLError + warn("#{__FILE__}:#{__LINE__}: warning: #{$!.message} (#{$!.class})") if @config[:verbose] + retry + end + end + end + + DRbProtocol.add_protocol(DRbSSLSocket) +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/timeridconv.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/timeridconv.rb new file mode 100644 index 0000000000..bb2c48d528 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/timeridconv.rb @@ -0,0 +1,91 @@ +require 'drb/drb' +require 'monitor' + +module DRb + class TimerIdConv < DRbIdConv + class TimerHolder2 + include MonitorMixin + + class InvalidIndexError < RuntimeError; end + + def initialize(timeout=600) + super() + @sentinel = Object.new + @gc = {} + @curr = {} + @renew = {} + @timeout = timeout + @keeper = keeper + end + + def add(obj) + synchronize do + key = obj.__id__ + @curr[key] = obj + return key + end + end + + def fetch(key, dv=@sentinel) + synchronize do + obj = peek(key) + if obj == @sentinel + return dv unless dv == @sentinel + raise InvalidIndexError + end + @renew[key] = obj # KeepIt + return obj + end + end + + def include?(key) + synchronize do + obj = peek(key) + return false if obj == @sentinel + true + end + end + + def peek(key) + synchronize do + return @curr.fetch(key, @renew.fetch(key, @gc.fetch(key, @sentinel))) + end + end + + private + def alternate + synchronize do + @gc = @curr # GCed + @curr = @renew + @renew = {} + end + end + + def keeper + Thread.new do + loop do + size = alternate + sleep(@timeout) + end + end + end + end + + def initialize(timeout=600) + @holder = TimerHolder2.new(timeout) + end + + def to_obj(ref) + return super if ref.nil? + @holder.fetch(ref) + rescue TimerHolder2::InvalidIndexError + raise "invalid reference" + end + + def to_id(obj) + return @holder.add(obj) + end + end +end + +# DRb.install_id_conv(TimerIdConv.new) diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/unix.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/unix.rb new file mode 100644 index 0000000000..57feed8301 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/drb/unix.rb @@ -0,0 +1,108 @@ +require 'socket' +require 'drb/drb' +require 'tmpdir' + +raise(LoadError, "UNIXServer is required") unless defined?(UNIXServer) + +module DRb + + class DRbUNIXSocket < DRbTCPSocket + def self.parse_uri(uri) + if /^drbunix:(.*?)(\?(.*))?$/ =~ uri + filename = $1 + option = $3 + [filename, option] + else + raise(DRbBadScheme, uri) unless uri =~ /^drbunix:/ + raise(DRbBadURI, 'can\'t parse uri:' + uri) + end + end + + def self.open(uri, config) + filename, option = parse_uri(uri) + filename.untaint + soc = UNIXSocket.open(filename) + self.new(uri, soc, config) + end + + def self.open_server(uri, config) + filename, option = parse_uri(uri) + if filename.size == 0 + soc = temp_server + filename = soc.path + uri = 'drbunix:' + soc.path + else + soc = UNIXServer.open(filename) + end + owner = config[:UNIXFileOwner] + group = config[:UNIXFileGroup] + if owner || group + require 'etc' + owner = Etc.getpwnam( owner ).uid if owner + group = Etc.getgrnam( group ).gid if group + File.chown owner, group, filename + end + mode = config[:UNIXFileMode] + File.chmod(mode, filename) if mode + + self.new(uri, soc, config, true) + end + + def self.uri_option(uri, config) + filename, option = parse_uri(uri) + return "drbunix:#{filename}", option + end + + def initialize(uri, soc, config={}, server_mode = false) + super(uri, soc, config) + set_sockopt(@socket) + @server_mode = server_mode + @acl = nil + end + + # import from tempfile.rb + Max_try = 10 + private + def self.temp_server + tmpdir = Dir::tmpdir + n = 0 + while true + begin + tmpname = sprintf('%s/druby%d.%d', tmpdir, $$, n) + lock = tmpname + '.lock' + unless File.exist?(tmpname) or File.exist?(lock) + Dir.mkdir(lock) + break + end + rescue + raise "cannot generate tempfile `%s'" % tmpname if n >= Max_try + #sleep(1) + end + n += 1 + end + soc = UNIXServer.new(tmpname) + Dir.rmdir(lock) + soc + end + + public + def close + return unless @socket + path = @socket.path if @server_mode + @socket.close + File.unlink(path) if @server_mode + @socket = nil + end + + def accept + s = @socket.accept + self.class.new(nil, s, @config) + end + + def set_sockopt(soc) + soc.fcntl(Fcntl::F_SETFL, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC + end + end + + DRbProtocol.add_protocol(DRbUNIXSocket) +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/e2mmap.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/e2mmap.rb new file mode 100644 index 0000000000..3e2604af5d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/e2mmap.rb @@ -0,0 +1,195 @@ +# +# e2mmap.rb - for ruby 1.1 +# $Release Version: 2.0$ +# $Revision: 1.10 $ +# $Date: 1999/02/17 12:33:17 $ +# by Keiju ISHITSUKA +# +# -- +# Usage: +# +# U1) +# class Foo +# extend Exception2MessageMapper +# def_e2message ExistingExceptionClass, "message..." +# def_exception :NewExceptionClass, "message..."[, superclass] +# ... +# end +# +# U2) +# module Error +# extend Exception2MessageMapper +# def_e2meggage ExistingExceptionClass, "message..." +# def_exception :NewExceptionClass, "message..."[, superclass] +# ... +# end +# class Foo +# include Error +# ... +# end +# +# foo = Foo.new +# foo.Fail .... +# +# U3) +# module Error +# extend Exception2MessageMapper +# def_e2message ExistingExceptionClass, "message..." +# def_exception :NewExceptionClass, "message..."[, superclass] +# ... +# end +# class Foo +# extend Exception2MessageMapper +# include Error +# ... +# end +# +# Foo.Fail NewExceptionClass, arg... +# Foo.Fail ExistingExceptionClass, arg... +# +# +fail "Use Ruby 1.1" if VERSION < "1.1" + +module Exception2MessageMapper + @RCS_ID='-$Id: e2mmap.rb,v 1.10 1999/02/17 12:33:17 keiju Exp keiju $-' + + E2MM = Exception2MessageMapper + + def E2MM.extend_object(cl) + super + cl.bind(self) unless cl == E2MM + end + + # backward compatibility + def E2MM.extend_to(b) + c = eval("self", b) + c.extend(self) + end + + def bind(cl) + self.module_eval %[ + def Raise(err = nil, *rest) + Exception2MessageMapper.Raise(self.class, err, *rest) + end + alias Fail Raise + + def self.included(mod) + mod.extend Exception2MessageMapper + end + ] + end + + # Fail(err, *rest) + # err: exception + # rest: message arguments + # + def Raise(err = nil, *rest) + E2MM.Raise(self, err, *rest) + end + alias Fail Raise + + # backward compatibility + alias fail! fail + def fail(err = nil, *rest) + begin + E2MM.Fail(self, err, *rest) + rescue E2MM::ErrNotRegisteredException + super + end + end + class << self + public :fail + end + + + # def_e2message(c, m) + # c: exception + # m: message_form + # define exception c with message m. + # + def def_e2message(c, m) + E2MM.def_e2message(self, c, m) + end + + # def_exception(n, m, s) + # n: exception_name + # m: message_form + # s: superclass(default: StandardError) + # define exception named ``c'' with message m. + # + def def_exception(n, m, s = StandardError) + E2MM.def_exception(self, n, m, s) + end + + # + # Private definitions. + # + # {[class, exp] => message, ...} + @MessageMap = {} + + # E2MM.def_exception(k, e, m) + # k: class to define exception under. + # e: exception + # m: message_form + # define exception c with message m. + # + def E2MM.def_e2message(k, c, m) + E2MM.instance_eval{@MessageMap[[k, c]] = m} + c + end + + # E2MM.def_exception(k, n, m, s) + # k: class to define exception under. + # n: exception_name + # m: message_form + # s: superclass(default: StandardError) + # define exception named ``c'' with message m. + # + def E2MM.def_exception(k, n, m, s = StandardError) + n = n.id2name if n.kind_of?(Fixnum) + e = Class.new(s) + E2MM.instance_eval{@MessageMap[[k, e]] = m} + k.const_set(n, e) + end + + # Fail(klass, err, *rest) + # klass: class to define exception under. + # err: exception + # rest: message arguments + # + def E2MM.Raise(klass = E2MM, err = nil, *rest) + if form = e2mm_message(klass, err) + $! = err.new(sprintf(form, *rest)) + $@ = caller(1) if $@.nil? + #p $@ + #p __FILE__ + $@.shift if $@[0] =~ /^#{Regexp.quote(__FILE__)}:/ + raise + else + E2MM.Fail E2MM, ErrNotRegisteredException, err.inspect + end + end + class < +# EOF +# puts template.result(binding) +# +# Prints: The value of x is: 42 +# +# More complex examples are given below. +# +# +# == Recognized Tags +# +# ERB recognizes certain tags in the provided template and converts them based +# on the rules below: +# +# <% Ruby code -- inline with output %> +# <%= Ruby expression -- replace with result %> +# <%# comment -- ignored -- useful in testing %> +# % a line of Ruby code -- treated as <% line %> (optional -- see ERB.new) +# %% replaced with % if first thing on a line and % processing is used +# <%% or %%> -- replace with <% or %> respectively +# +# All other text is passed through ERB filtering unchanged. +# +# +# == Options +# +# There are several settings you can change when you use ERB: +# * the nature of the tags that are recognized; +# * the value of $SAFE under which the template is run; +# * the binding used to resolve local variables in the template. +# +# See the ERB.new and ERB#result methods for more detail. +# +# +# == Examples +# +# === Plain Text +# +# ERB is useful for any generic templating situation. Note that in this example, we use the +# convenient "% at start of line" tag, and we quote the template literally with +# %q{...} to avoid trouble with the backslash. +# +# require "erb" +# +# # Create template. +# template = %q{ +# From: James Edward Gray II +# To: <%= to %> +# Subject: Addressing Needs +# +# <%= to[/\w+/] %>: +# +# Just wanted to send a quick note assuring that your needs are being +# addressed. +# +# I want you to know that my team will keep working on the issues, +# especially: +# +# <%# ignore numerous minor requests -- focus on priorities %> +# % priorities.each do |priority| +# * <%= priority %> +# % end +# +# Thanks for your patience. +# +# James Edward Gray II +# }.gsub(/^ /, '') +# +# message = ERB.new(template, 0, "%<>") +# +# # Set up template data. +# to = "Community Spokesman " +# priorities = [ "Run Ruby Quiz", +# "Document Modules", +# "Answer Questions on Ruby Talk" ] +# +# # Produce result. +# email = message.result +# puts email +# +# Generates: +# +# From: James Edward Gray II +# To: Community Spokesman +# Subject: Addressing Needs +# +# Community: +# +# Just wanted to send a quick note assuring that your needs are being addressed. +# +# I want you to know that my team will keep working on the issues, especially: +# +# * Run Ruby Quiz +# * Document Modules +# * Answer Questions on Ruby Talk +# +# Thanks for your patience. +# +# James Edward Gray II +# +# === Ruby in HTML +# +# ERB is often used in .rhtml files (HTML with embedded Ruby). Notice the need in +# this example to provide a special binding when the template is run, so that the instance +# variables in the Product object can be resolved. +# +# require "erb" +# +# # Build template data class. +# class Product +# def initialize( code, name, desc, cost ) +# @code = code +# @name = name +# @desc = desc +# @cost = cost +# +# @features = [ ] +# end +# +# def add_feature( feature ) +# @features << feature +# end +# +# # Support templating of member data. +# def get_binding +# binding +# end +# +# # ... +# end +# +# # Create template. +# template = %{ +# +# Ruby Toys -- <%= @name %> +# +# +#

<%= @name %> (<%= @code %>)

+#

<%= @desc %>

+# +#
    +# <% @features.each do |f| %> +#
  • <%= f %>
  • +# <% end %> +#
+# +#

+# <% if @cost < 10 %> +# Only <%= @cost %>!!! +# <% else %> +# Call for a price, today! +# <% end %> +#

+# +# +# +# }.gsub(/^ /, '') +# +# rhtml = ERB.new(template) +# +# # Set up template data. +# toy = Product.new( "TZ-1002", +# "Rubysapien", +# "Geek's Best Friend! Responds to Ruby commands...", +# 999.95 ) +# toy.add_feature("Listens for verbal commands in the Ruby language!") +# toy.add_feature("Ignores Perl, Java, and all C variants.") +# toy.add_feature("Karate-Chop Action!!!") +# toy.add_feature("Matz signature on left leg.") +# toy.add_feature("Gem studded eyes... Rubies, of course!") +# +# # Produce result. +# rhtml.run(toy.get_binding) +# +# Generates (some blank lines removed): +# +# +# Ruby Toys -- Rubysapien +# +# +#

Rubysapien (TZ-1002)

+#

Geek's Best Friend! Responds to Ruby commands...

+# +#
    +#
  • Listens for verbal commands in the Ruby language!
  • +#
  • Ignores Perl, Java, and all C variants.
  • +#
  • Karate-Chop Action!!!
  • +#
  • Matz signature on left leg.
  • +#
  • Gem studded eyes... Rubies, of course!
  • +#
+# +#

+# Call for a price, today! +#

+# +# +# +# +# +# == Notes +# +# There are a variety of templating solutions available in various Ruby projects: +# * ERB's big brother, eRuby, works the same but is written in C for speed; +# * Amrita (smart at producing HTML/XML); +# * cs/Template (written in C for speed); +# * RDoc, distributed with Ruby, uses its own template engine, which can be reused elsewhere; +# * and others; search the RAA. +# +# Rails, the web application framework, uses ERB to create views. +# +class ERB + Revision = '$Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $' #' + + # Returns revision information for the erb.rb module. + def self.version + "erb.rb [2.0.4 #{ERB::Revision.split[1]}]" + end +end + +#-- +# ERB::Compiler +class ERB + class Compiler # :nodoc: + class PercentLine # :nodoc: + def initialize(str) + @value = str + end + attr_reader :value + alias :to_s :value + end + + class Scanner # :nodoc: + SplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/ + + @scanner_map = {} + def self.regist_scanner(klass, trim_mode, percent) + @scanner_map[[trim_mode, percent]] = klass + end + + def self.default_scanner=(klass) + @default_scanner = klass + end + + def self.make_scanner(src, trim_mode, percent) + klass = @scanner_map.fetch([trim_mode, percent], @default_scanner) + klass.new(src, trim_mode, percent) + end + + def initialize(src, trim_mode, percent) + @src = src + @stag = nil + end + attr_accessor :stag + + def scan; end + end + + class TrimScanner < Scanner # :nodoc: + TrimSplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>\n)|(%>)|(\n)/ + + def initialize(src, trim_mode, percent) + super + @trim_mode = trim_mode + @percent = percent + if @trim_mode == '>' + @scan_line = self.method(:trim_line1) + elsif @trim_mode == '<>' + @scan_line = self.method(:trim_line2) + elsif @trim_mode == '-' + @scan_line = self.method(:explicit_trim_line) + else + @scan_line = self.method(:scan_line) + end + end + attr_accessor :stag + + def scan(&block) + @stag = nil + if @percent + @src.each do |line| + percent_line(line, &block) + end + else + @src.each do |line| + @scan_line.call(line, &block) + end + end + nil + end + + def percent_line(line, &block) + if @stag || line[0] != ?% + return @scan_line.call(line, &block) + end + + line[0] = '' + if line[0] == ?% + @scan_line.call(line, &block) + else + yield(PercentLine.new(line.chomp)) + end + end + + def scan_line(line) + line.split(SplitRegexp).each do |token| + next if token.empty? + yield(token) + end + end + + def trim_line1(line) + line.split(TrimSplitRegexp).each do |token| + next if token.empty? + if token == "%>\n" + yield('%>') + yield(:cr) + break + end + yield(token) + end + end + + def trim_line2(line) + head = nil + line.split(TrimSplitRegexp).each do |token| + next if token.empty? + head = token unless head + if token == "%>\n" + yield('%>') + if is_erb_stag?(head) + yield(:cr) + else + yield("\n") + end + break + end + yield(token) + end + end + + ExplicitTrimRegexp = /(^[ \t]*<%-)|(-%>\n?\z)|(<%-)|(-%>)|(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/ + def explicit_trim_line(line) + line.split(ExplicitTrimRegexp).each do |token| + next if token.empty? + if @stag.nil? && /[ \t]*<%-/ =~ token + yield('<%') + elsif @stag && /-%>\n/ =~ token + yield('%>') + yield(:cr) + elsif @stag && token == '-%>' + yield('%>') + else + yield(token) + end + end + end + + ERB_STAG = %w(<%= <%# <%) + def is_erb_stag?(s) + ERB_STAG.member?(s) + end + end + + Scanner.default_scanner = TrimScanner + + class SimpleScanner < Scanner # :nodoc: + def scan + @src.each do |line| + line.split(SplitRegexp).each do |token| + next if token.empty? + yield(token) + end + end + end + end + + Scanner.regist_scanner(SimpleScanner, nil, false) + + begin + require 'strscan' + class SimpleScanner2 < Scanner # :nodoc: + def scan + stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/ + etag_reg = /(.*?)(%%>|%>|\n|\z)/ + scanner = StringScanner.new(@src) + while ! scanner.eos? + scanner.scan(@stag ? etag_reg : stag_reg) + text = scanner[1] + elem = scanner[2] + yield(text) unless text.empty? + yield(elem) unless elem.empty? + end + end + end + Scanner.regist_scanner(SimpleScanner2, nil, false) + + class PercentScanner < Scanner # :nodoc: + def scan + new_line = true + stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/ + etag_reg = /(.*?)(%%>|%>|\n|\z)/ + scanner = StringScanner.new(@src) + while ! scanner.eos? + if new_line && @stag.nil? + if scanner.scan(/%%/) + yield('%') + new_line = false + next + elsif scanner.scan(/%/) + yield(PercentLine.new(scanner.scan(/.*?(\n|\z)/).chomp)) + next + end + end + scanner.scan(@stag ? etag_reg : stag_reg) + text = scanner[1] + elem = scanner[2] + yield(text) unless text.empty? + yield(elem) unless elem.empty? + new_line = (elem == "\n") + end + end + end + Scanner.regist_scanner(PercentScanner, nil, true) + + class ExplicitScanner < Scanner # :nodoc: + def scan + new_line = true + stag_reg = /(.*?)(<%%|<%=|<%#|<%-|<%|\n|\z)/ + etag_reg = /(.*?)(%%>|-%>|%>|\n|\z)/ + scanner = StringScanner.new(@src) + while ! scanner.eos? + if new_line && @stag.nil? && scanner.scan(/[ \t]*<%-/) + yield('<%') + new_line = false + next + end + scanner.scan(@stag ? etag_reg : stag_reg) + text = scanner[1] + elem = scanner[2] + new_line = (elem == "\n") + yield(text) unless text.empty? + if elem == '-%>' + yield('%>') + if scanner.scan(/(\n|\z)/) + yield(:cr) + new_line = true + end + elsif elem == '<%-' + yield('<%') + else + yield(elem) unless elem.empty? + end + end + end + end + Scanner.regist_scanner(ExplicitScanner, '-', false) + + rescue LoadError + end + + class Buffer # :nodoc: + def initialize(compiler) + @compiler = compiler + @line = [] + @script = "" + @compiler.pre_cmd.each do |x| + push(x) + end + end + attr_reader :script + + def push(cmd) + @line << cmd + end + + def cr + @script << (@line.join('; ')) + @line = [] + @script << "\n" + end + + def close + return unless @line + @compiler.post_cmd.each do |x| + push(x) + end + @script << (@line.join('; ')) + @line = nil + end + end + + def compile(s) + out = Buffer.new(self) + + content = '' + scanner = make_scanner(s) + scanner.scan do |token| + if scanner.stag.nil? + case token + when PercentLine + out.push("#{@put_cmd} #{content.dump}") if content.size > 0 + content = '' + out.push(token.to_s) + out.cr + when :cr + out.cr + when '<%', '<%=', '<%#' + scanner.stag = token + out.push("#{@put_cmd} #{content.dump}") if content.size > 0 + content = '' + when "\n" + content << "\n" + out.push("#{@put_cmd} #{content.dump}") + out.cr + content = '' + when '<%%' + content << '<%' + else + content << token + end + else + case token + when '%>' + case scanner.stag + when '<%' + if content[-1] == ?\n + content.chop! + out.push(content) + out.cr + else + out.push(content) + end + when '<%=' + out.push("#{@insert_cmd}((#{content}).to_s)") + when '<%#' + # out.push("# #{content.dump}") + end + scanner.stag = nil + content = '' + when '%%>' + content << '%>' + else + content << token + end + end + end + out.push("#{@put_cmd} #{content.dump}") if content.size > 0 + out.close + out.script + end + + def prepare_trim_mode(mode) + case mode + when 1 + return [false, '>'] + when 2 + return [false, '<>'] + when 0 + return [false, nil] + when String + perc = mode.include?('%') + if mode.include?('-') + return [perc, '-'] + elsif mode.include?('<>') + return [perc, '<>'] + elsif mode.include?('>') + return [perc, '>'] + else + [perc, nil] + end + else + return [false, nil] + end + end + + def make_scanner(src) + Scanner.make_scanner(src, @trim_mode, @percent) + end + + def initialize(trim_mode) + @percent, @trim_mode = prepare_trim_mode(trim_mode) + @put_cmd = 'print' + @insert_cmd = @put_cmd + @pre_cmd = [] + @post_cmd = [] + end + attr_reader :percent, :trim_mode + attr_accessor :put_cmd, :insert_cmd, :pre_cmd, :post_cmd + end +end + +#-- +# ERB +class ERB + # + # Constructs a new ERB object with the template specified in _str_. + # + # An ERB object works by building a chunk of Ruby code that will output + # the completed template when run. If _safe_level_ is set to a non-nil value, + # ERB code will be run in a separate thread with $SAFE set to the + # provided level. + # + # If _trim_mode_ is passed a String containing one or more of the following + # modifiers, ERB will adjust its code generation as listed: + # + # % enables Ruby code processing for lines beginning with % + # <> omit newline for lines starting with <% and ending in %> + # > omit newline for lines ending in %> + # + # _eoutvar_ can be used to set the name of the variable ERB will build up + # its output in. This is useful when you need to run multiple ERB + # templates through the same binding and/or when you want to control where + # output ends up. Pass the name of the variable to be used inside a String. + # + # === Example + # + # require "erb" + # + # # build data class + # class Listings + # PRODUCT = { :name => "Chicken Fried Steak", + # :desc => "A well messages pattie, breaded and fried.", + # :cost => 9.95 } + # + # attr_reader :product, :price + # + # def initialize( product = "", price = "" ) + # @product = product + # @price = price + # end + # + # def build + # b = binding + # # create and run templates, filling member data variebles + # ERB.new(<<-'END_PRODUCT'.gsub(/^\s+/, ""), 0, "", "@product").result b + # <%= PRODUCT[:name] %> + # <%= PRODUCT[:desc] %> + # END_PRODUCT + # ERB.new(<<-'END_PRICE'.gsub(/^\s+/, ""), 0, "", "@price").result b + # <%= PRODUCT[:name] %> -- <%= PRODUCT[:cost] %> + # <%= PRODUCT[:desc] %> + # END_PRICE + # end + # end + # + # # setup template data + # listings = Listings.new + # listings.build + # + # puts listings.product + "\n" + listings.price + # + # _Generates_ + # + # Chicken Fried Steak + # A well messages pattie, breaded and fried. + # + # Chicken Fried Steak -- 9.95 + # A well messages pattie, breaded and fried. + # + def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout') + @safe_level = safe_level + compiler = ERB::Compiler.new(trim_mode) + set_eoutvar(compiler, eoutvar) + @src = compiler.compile(str) + @filename = nil + end + + # The Ruby code generated by ERB + attr_reader :src + + # The optional _filename_ argument passed to Kernel#eval when the ERB code + # is run + attr_accessor :filename + + # + # Can be used to set _eoutvar_ as described in ERB#new. It's probably easier + # to just use the constructor though, since calling this method requires the + # setup of an ERB _compiler_ object. + # + def set_eoutvar(compiler, eoutvar = '_erbout') + compiler.put_cmd = "#{eoutvar}.concat" + compiler.insert_cmd = "#{eoutvar}.concat" + + cmd = [] + cmd.push "#{eoutvar} = ''" + + compiler.pre_cmd = cmd + + cmd = [] + cmd.push(eoutvar) + + compiler.post_cmd = cmd + end + + # Generate results and print them. (see ERB#result) + def run(b=TOPLEVEL_BINDING) + print self.result(b) + end + + # + # Executes the generated ERB code to produce a completed template, returning + # the results of that code. (See ERB#new for details on how this process can + # be affected by _safe_level_.) + # + # _b_ accepts a Binding or Proc object which is used to set the context of + # code evaluation. + # + def result(b=TOPLEVEL_BINDING) + if @safe_level + th = Thread.start { + $SAFE = @safe_level + eval(@src, b, (@filename || '(erb)'), 1) + } + return th.value + else + return eval(@src, b, (@filename || '(erb)'), 1) + end + end + + def def_method(mod, methodname, fname='(ERB)') # :nodoc: + mod.module_eval("def #{methodname}\n" + self.src + "\nend\n", fname, 0) + end + + def def_module(methodname='erb') # :nodoc: + mod = Module.new + def_method(mod, methodname) + mod + end + + def def_class(superklass=Object, methodname='result') # :nodoc: + cls = Class.new(superklass) + def_method(cls, methodname) + cls + end +end + +#-- +# ERB::Util +class ERB + # A utility module for conversion routines, often handy in HTML generation. + module Util + public + # + # A utility method for escaping HTML tag characters in _s_. + # + # require "erb" + # include ERB::Util + # + # puts html_escape("is a > 0 & a < 10?") + # + # _Generates_ + # + # is a > 0 & a < 10? + # + def html_escape(s) + s.to_s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/, options) +# chmod(mode, list, options) +# chmod_R(mode, list, options) +# chown(user, group, list, options) +# chown_R(user, group, list, options) +# touch(list, options) +# +# The options parameter is a hash of options, taken from the list +# :force, :noop, :preserve, and :verbose. +# :noop means that no changes are made. The other two are obvious. +# Each method documents the options that it honours. +# +# All methods that have the concept of a "source" file or directory can take +# either one file or a list of files in that argument. See the method +# documentation for examples. +# +# There are some `low level' methods, which do not accept any option: +# +# copy_entry(src, dest, preserve = false, dereference = false) +# copy_file(src, dest, preserve = false, dereference = true) +# copy_stream(srcstream, deststream) +# remove_entry(path, force = false) +# remove_entry_secure(path, force = false) +# remove_file(path, force = false) +# compare_file(path_a, path_b) +# compare_stream(stream_a, stream_b) +# uptodate?(file, cmp_list) +# +# == module FileUtils::Verbose +# +# This module has all methods of FileUtils module, but it outputs messages +# before acting. This equates to passing the :verbose flag to methods +# in FileUtils. +# +# == module FileUtils::NoWrite +# +# This module has all methods of FileUtils module, but never changes +# files/directories. This equates to passing the :noop flag to methods +# in FileUtils. +# +# == module FileUtils::DryRun +# +# This module has all methods of FileUtils module, but never changes +# files/directories. This equates to passing the :noop and +# :verbose flags to methods in FileUtils. +# + +module FileUtils + + def self.private_module_function(name) #:nodoc: + module_function name + private_class_method name + end + + # This hash table holds command options. + OPT_TABLE = {} #:nodoc: internal use only + + # + # Options: (none) + # + # Returns the name of the current directory. + # + def pwd + Dir.pwd + end + module_function :pwd + + alias getwd pwd + module_function :getwd + + # + # Options: verbose + # + # Changes the current directory to the directory +dir+. + # + # If this method is called with block, resumes to the old + # working directory after the block execution finished. + # + # FileUtils.cd('/', :verbose => true) # chdir and report it + # + def cd(dir, options = {}, &block) # :yield: dir + fu_check_options options, OPT_TABLE['cd'] + fu_output_message "cd #{dir}" if options[:verbose] + Dir.chdir(dir, &block) + fu_output_message 'cd -' if options[:verbose] and block + end + module_function :cd + + alias chdir cd + module_function :chdir + + OPT_TABLE['cd'] = + OPT_TABLE['chdir'] = [:verbose] + + # + # Options: (none) + # + # Returns true if +newer+ is newer than all +old_list+. + # Non-existent files are older than any file. + # + # FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \ + # system 'make hello.o' + # + def uptodate?(new, old_list, options = nil) + raise ArgumentError, 'uptodate? does not accept any option' if options + + return false unless File.exist?(new) + new_time = File.mtime(new) + old_list.each do |old| + if File.exist?(old) + return false unless new_time > File.mtime(old) + end + end + true + end + module_function :uptodate? + + # + # Options: mode noop verbose + # + # Creates one or more directories. + # + # FileUtils.mkdir 'test' + # FileUtils.mkdir %w( tmp data ) + # FileUtils.mkdir 'notexist', :noop => true # Does not really create. + # FileUtils.mkdir 'tmp', :mode => 0700 + # + def mkdir(list, options = {}) + fu_check_options options, OPT_TABLE['mkdir'] + list = fu_list(list) + fu_output_message "mkdir #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose] + return if options[:noop] + + list.each do |dir| + fu_mkdir dir, options[:mode] + end + end + module_function :mkdir + + OPT_TABLE['mkdir'] = [:mode, :noop, :verbose] + + # + # Options: mode noop verbose + # + # Creates a directory and all its parent directories. + # For example, + # + # FileUtils.mkdir_p '/usr/local/lib/ruby' + # + # causes to make following directories, if it does not exist. + # * /usr + # * /usr/local + # * /usr/local/lib + # * /usr/local/lib/ruby + # + # You can pass several directories at a time in a list. + # + def mkdir_p(list, options = {}) + fu_check_options options, OPT_TABLE['mkdir_p'] + list = fu_list(list) + fu_output_message "mkdir -p #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose] + return *list if options[:noop] + + list.map {|path| path.sub(%r, '') }.each do |path| + # optimize for the most common case + begin + fu_mkdir path, options[:mode] + next + rescue SystemCallError + next if File.directory?(path) + end + + stack = [] + until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/" + stack.push path + path = File.dirname(path) + end + stack.reverse_each do |path| + begin + fu_mkdir path, options[:mode] + rescue SystemCallError => err + raise unless File.directory?(path) + end + end + end + + return *list + end + module_function :mkdir_p + + alias mkpath mkdir_p + alias makedirs mkdir_p + module_function :mkpath + module_function :makedirs + + OPT_TABLE['mkdir_p'] = + OPT_TABLE['mkpath'] = + OPT_TABLE['makedirs'] = [:mode, :noop, :verbose] + + def fu_mkdir(path, mode) #:nodoc: + path = path.sub(%r, '') + if mode + Dir.mkdir path, mode + File.chmod mode, path + else + Dir.mkdir path + end + end + private_module_function :fu_mkdir + + # + # Options: noop, verbose + # + # Removes one or more directories. + # + # FileUtils.rmdir 'somedir' + # FileUtils.rmdir %w(somedir anydir otherdir) + # # Does not really remove directory; outputs message. + # FileUtils.rmdir 'somedir', :verbose => true, :noop => true + # + def rmdir(list, options = {}) + fu_check_options options, OPT_TABLE['rmdir'] + list = fu_list(list) + fu_output_message "rmdir #{list.join ' '}" if options[:verbose] + return if options[:noop] + list.each do |dir| + Dir.rmdir dir.sub(%r, '') + end + end + module_function :rmdir + + OPT_TABLE['rmdir'] = [:noop, :verbose] + + # + # Options: force noop verbose + # + # ln(old, new, options = {}) + # + # Creates a hard link +new+ which points to +old+. + # If +new+ already exists and it is a directory, creates a link +new/old+. + # If +new+ already exists and it is not a directory, raises Errno::EEXIST. + # But if :force option is set, overwrite +new+. + # + # FileUtils.ln 'gcc', 'cc', :verbose => true + # FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs' + # + # ln(list, destdir, options = {}) + # + # Creates several hard links in a directory, with each one pointing to the + # item in +list+. If +destdir+ is not a directory, raises Errno::ENOTDIR. + # + # include FileUtils + # cd '/sbin' + # FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked. + # + def ln(src, dest, options = {}) + fu_check_options options, OPT_TABLE['ln'] + fu_output_message "ln#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] + return if options[:noop] + fu_each_src_dest0(src, dest) do |s,d| + remove_file d, true if options[:force] + File.link s, d + end + end + module_function :ln + + alias link ln + module_function :link + + OPT_TABLE['ln'] = + OPT_TABLE['link'] = [:force, :noop, :verbose] + + # + # Options: force noop verbose + # + # ln_s(old, new, options = {}) + # + # Creates a symbolic link +new+ which points to +old+. If +new+ already + # exists and it is a directory, creates a symbolic link +new/old+. If +new+ + # already exists and it is not a directory, raises Errno::EEXIST. But if + # :force option is set, overwrite +new+. + # + # FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby' + # FileUtils.ln_s 'verylongsourcefilename.c', 'c', :force => true + # + # ln_s(list, destdir, options = {}) + # + # Creates several symbolic links in a directory, with each one pointing to the + # item in +list+. If +destdir+ is not a directory, raises Errno::ENOTDIR. + # + # If +destdir+ is not a directory, raises Errno::ENOTDIR. + # + # FileUtils.ln_s Dir.glob('bin/*.rb'), '/home/aamine/bin' + # + def ln_s(src, dest, options = {}) + fu_check_options options, OPT_TABLE['ln_s'] + fu_output_message "ln -s#{options[:force] ? 'f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] + return if options[:noop] + fu_each_src_dest0(src, dest) do |s,d| + remove_file d, true if options[:force] + File.symlink s, d + end + end + module_function :ln_s + + alias symlink ln_s + module_function :symlink + + OPT_TABLE['ln_s'] = + OPT_TABLE['symlink'] = [:force, :noop, :verbose] + + # + # Options: noop verbose + # + # Same as + # #ln_s(src, dest, :force) + # + def ln_sf(src, dest, options = {}) + fu_check_options options, OPT_TABLE['ln_sf'] + options = options.dup + options[:force] = true + ln_s src, dest, options + end + module_function :ln_sf + + OPT_TABLE['ln_sf'] = [:noop, :verbose] + + # + # Options: preserve noop verbose + # + # Copies a file content +src+ to +dest+. If +dest+ is a directory, + # copies +src+ to +dest/src+. + # + # If +src+ is a list of files, then +dest+ must be a directory. + # + # FileUtils.cp 'eval.c', 'eval.c.org' + # FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6' + # FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true + # FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink + # + def cp(src, dest, options = {}) + fu_check_options options, OPT_TABLE['cp'] + fu_output_message "cp#{options[:preserve] ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] + return if options[:noop] + fu_each_src_dest(src, dest) do |s, d| + copy_file s, d, options[:preserve] + end + end + module_function :cp + + alias copy cp + module_function :copy + + OPT_TABLE['cp'] = + OPT_TABLE['copy'] = [:preserve, :noop, :verbose] + + # + # Options: preserve noop verbose dereference_root remove_destination + # + # Copies +src+ to +dest+. If +src+ is a directory, this method copies + # all its contents recursively. If +dest+ is a directory, copies + # +src+ to +dest/src+. + # + # +src+ can be a list of files. + # + # # Installing ruby library "mylib" under the site_ruby + # FileUtils.rm_r site_ruby + '/mylib', :force + # FileUtils.cp_r 'lib/', site_ruby + '/mylib' + # + # # Examples of copying several files to target directory. + # FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail' + # FileUtils.cp_r Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true + # + # # If you want to copy all contents of a directory instead of the + # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y, + # # use following code. + # FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes src/dest, + # # but this doesn't. + # + def cp_r(src, dest, options = {}) + fu_check_options options, OPT_TABLE['cp_r'] + fu_output_message "cp -r#{options[:preserve] ? 'p' : ''}#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] + return if options[:noop] + options[:dereference_root] = true unless options.key?(:dereference_root) + fu_each_src_dest(src, dest) do |s, d| + copy_entry s, d, options[:preserve], options[:dereference_root], options[:remove_destination] + end + end + module_function :cp_r + + OPT_TABLE['cp_r'] = [:preserve, :noop, :verbose, + :dereference_root, :remove_destination] + + # + # Copies a file system entry +src+ to +dest+. + # If +src+ is a directory, this method copies its contents recursively. + # This method preserves file types, c.f. symlink, directory... + # (FIFO, device files and etc. are not supported yet) + # + # Both of +src+ and +dest+ must be a path name. + # +src+ must exist, +dest+ must not exist. + # + # If +preserve+ is true, this method preserves owner, group, permissions + # and modified time. + # + # If +dereference_root+ is true, this method dereference tree root. + # + # If +remove_destination+ is true, this method removes each destination file before copy. + # + def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false) + Entry_.new(src, nil, dereference_root).traverse do |ent| + destent = Entry_.new(dest, ent.rel, false) + File.unlink destent.path if remove_destination && File.file?(destent.path) + ent.copy destent.path + ent.copy_metadata destent.path if preserve + end + end + module_function :copy_entry + + # + # Copies file contents of +src+ to +dest+. + # Both of +src+ and +dest+ must be a path name. + # + def copy_file(src, dest, preserve = false, dereference = true) + ent = Entry_.new(src, nil, dereference) + ent.copy_file dest + ent.copy_metadata dest if preserve + end + module_function :copy_file + + # + # Copies stream +src+ to +dest+. + # +src+ must respond to #read(n) and + # +dest+ must respond to #write(str). + # + def copy_stream(src, dest) + fu_copy_stream0 src, dest, fu_stream_blksize(src, dest) + end + module_function :copy_stream + + # + # Options: force noop verbose + # + # Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different + # disk partition, the file is copied instead. + # + # FileUtils.mv 'badname.rb', 'goodname.rb' + # FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error + # + # FileUtils.mv %w(junk.txt dust.txt), '/home/aamine/.trash/' + # FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true + # + def mv(src, dest, options = {}) + fu_check_options options, OPT_TABLE['mv'] + fu_output_message "mv#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] + return if options[:noop] + fu_each_src_dest(src, dest) do |s, d| + destent = Entry_.new(d, nil, true) + begin + if destent.exist? + if destent.directory? + raise Errno::EEXIST, dest + else + destent.remove_file if rename_cannot_overwrite_file? + end + end + begin + File.rename s, d + rescue Errno::EXDEV + copy_entry s, d, true + if options[:secure] + remove_entry_secure s, options[:force] + else + remove_entry s, options[:force] + end + end + rescue SystemCallError + raise unless options[:force] + end + end + end + module_function :mv + + alias move mv + module_function :move + + OPT_TABLE['mv'] = + OPT_TABLE['move'] = [:force, :noop, :verbose, :secure] + + def rename_cannot_overwrite_file? #:nodoc: + /djgpp|cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM + end + private_module_function :rename_cannot_overwrite_file? + + # + # Options: force noop verbose + # + # Remove file(s) specified in +list+. This method cannot remove directories. + # All StandardErrors are ignored when the :force option is set. + # + # FileUtils.rm %w( junk.txt dust.txt ) + # FileUtils.rm Dir.glob('*.so') + # FileUtils.rm 'NotExistFile', :force => true # never raises exception + # + def rm(list, options = {}) + fu_check_options options, OPT_TABLE['rm'] + list = fu_list(list) + fu_output_message "rm#{options[:force] ? ' -f' : ''} #{list.join ' '}" if options[:verbose] + return if options[:noop] + + list.each do |path| + remove_file path, options[:force] + end + end + module_function :rm + + alias remove rm + module_function :remove + + OPT_TABLE['rm'] = + OPT_TABLE['remove'] = [:force, :noop, :verbose] + + # + # Options: noop verbose + # + # Equivalent to + # + # #rm(list, :force => true) + # + def rm_f(list, options = {}) + fu_check_options options, OPT_TABLE['rm_f'] + options = options.dup + options[:force] = true + rm list, options + end + module_function :rm_f + + alias safe_unlink rm_f + module_function :safe_unlink + + OPT_TABLE['rm_f'] = + OPT_TABLE['safe_unlink'] = [:noop, :verbose] + + # + # Options: force noop verbose secure + # + # remove files +list+[0] +list+[1]... If +list+[n] is a directory, + # removes its all contents recursively. This method ignores + # StandardError when :force option is set. + # + # FileUtils.rm_r Dir.glob('/tmp/*') + # FileUtils.rm_r '/', :force => true # :-) + # + # WARNING: This method causes local vulnerability + # if one of parent directories or removing directory tree are world + # writable (including /tmp, whose permission is 1777), and the current + # process has strong privilege such as Unix super user (root), and the + # system has symbolic link. For secure removing, read the documentation + # of #remove_entry_secure carefully, and set :secure option to true. + # Default is :secure=>false. + # + # NOTE: This method calls #remove_entry_secure if :secure option is set. + # See also #remove_entry_secure. + # + def rm_r(list, options = {}) + fu_check_options options, OPT_TABLE['rm_r'] + # options[:secure] = true unless options.key?(:secure) + list = fu_list(list) + fu_output_message "rm -r#{options[:force] ? 'f' : ''} #{list.join ' '}" if options[:verbose] + return if options[:noop] + list.each do |path| + if options[:secure] + remove_entry_secure path, options[:force] + else + remove_entry path, options[:force] + end + end + end + module_function :rm_r + + OPT_TABLE['rm_r'] = [:force, :noop, :verbose, :secure] + + # + # Options: noop verbose secure + # + # Equivalent to + # + # #rm_r(list, :force => true) + # + # WARNING: This method causes local vulnerability. + # Read the documentation of #rm_r first. + # + def rm_rf(list, options = {}) + fu_check_options options, OPT_TABLE['rm_rf'] + options = options.dup + options[:force] = true + rm_r list, options + end + module_function :rm_rf + + alias rmtree rm_rf + module_function :rmtree + + OPT_TABLE['rm_rf'] = + OPT_TABLE['rmtree'] = [:noop, :verbose, :secure] + + # + # This method removes a file system entry +path+. +path+ shall be a + # regular file, a directory, or something. If +path+ is a directory, + # remove it recursively. This method is required to avoid TOCTTOU + # (time-of-check-to-time-of-use) local security vulnerability of #rm_r. + # #rm_r causes security hole when: + # + # * Parent directory is world writable (including /tmp). + # * Removing directory tree includes world writable directory. + # * The system has symbolic link. + # + # To avoid this security hole, this method applies special preprocess. + # If +path+ is a directory, this method chown(2) and chmod(2) all + # removing directories. This requires the current process is the + # owner of the removing whole directory tree, or is the super user (root). + # + # WARNING: You must ensure that *ALL* parent directories are not + # world writable. Otherwise this method does not work. + # Only exception is temporary directory like /tmp and /var/tmp, + # whose permission is 1777. + # + # WARNING: Only the owner of the removing directory tree, or Unix super + # user (root) should invoke this method. Otherwise this method does not + # work. + # + # For details of this security vulnerability, see Perl's case: + # + # http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448 + # http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452 + # + # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100]. + # + def remove_entry_secure(path, force = false) + unless fu_have_symlink? + remove_entry path, force + return + end + fullpath = File.expand_path(path) + st = File.lstat(fullpath) + unless st.directory? + File.unlink fullpath + return + end + # is a directory. + parent_st = File.stat(File.dirname(fullpath)) + unless fu_world_writable?(parent_st) + remove_entry path, force + return + end + unless parent_st.sticky? + raise ArgumentError, "parent directory is world writable, FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})" + end + # freeze tree root + euid = Process.euid + File.open(fullpath + '/.') {|f| + unless fu_stat_identical_entry?(st, f.stat) + # symlink (TOC-to-TOU attack?) + File.unlink fullpath + return + end + f.chown euid, -1 + f.chmod 0700 + } + # ---- tree root is frozen ---- + root = Entry_.new(path) + root.preorder_traverse do |ent| + if ent.directory? + ent.chown euid, -1 + ent.chmod 0700 + end + end + root.postorder_traverse do |ent| + begin + ent.remove + rescue + raise unless force + end + end + rescue + raise unless force + end + module_function :remove_entry_secure + + def fu_world_writable?(st) + (st.mode & 0002) != 0 + end + private_module_function :fu_world_writable? + + def fu_have_symlink? #:nodoc + File.symlink nil, nil + rescue NotImplementedError + return false + rescue + return true + end + private_module_function :fu_have_symlink? + + def fu_stat_identical_entry?(a, b) #:nodoc: + a.dev == b.dev and a.ino == b.ino + end + private_module_function :fu_stat_identical_entry? + + # + # This method removes a file system entry +path+. + # +path+ might be a regular file, a directory, or something. + # If +path+ is a directory, remove it recursively. + # + # See also #remove_entry_secure. + # + def remove_entry(path, force = false) + Entry_.new(path).postorder_traverse do |ent| + begin + ent.remove + rescue + raise unless force + end + end + rescue + raise unless force + end + module_function :remove_entry + + # + # Removes a file +path+. + # This method ignores StandardError if +force+ is true. + # + def remove_file(path, force = false) + Entry_.new(path).remove_file + rescue + raise unless force + end + module_function :remove_file + + # + # Removes a directory +dir+ and its contents recursively. + # This method ignores StandardError if +force+ is true. + # + def remove_dir(path, force = false) + remove_entry path, force # FIXME?? check if it is a directory + end + module_function :remove_dir + + # + # Returns true if the contents of a file A and a file B are identical. + # + # FileUtils.compare_file('somefile', 'somefile') #=> true + # FileUtils.compare_file('/bin/cp', '/bin/mv') #=> maybe false + # + def compare_file(a, b) + return false unless File.size(a) == File.size(b) + File.open(a, 'rb') {|fa| + File.open(b, 'rb') {|fb| + return compare_stream(fa, fb) + } + } + end + module_function :compare_file + + alias identical? compare_file + alias cmp compare_file + module_function :identical? + module_function :cmp + + # + # Returns true if the contents of a stream +a+ and +b+ are identical. + # + def compare_stream(a, b) + bsize = fu_stream_blksize(a, b) + sa = sb = nil + while sa == sb + sa = a.read(bsize) + sb = b.read(bsize) + unless sa and sb + if sa.nil? and sb.nil? + return true + end + end + end + false + end + module_function :compare_stream + + # + # Options: mode preserve noop verbose + # + # If +src+ is not same as +dest+, copies it and changes the permission + # mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+. + # This method removes destination before copy. + # + # FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true + # FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true + # + def install(src, dest, options = {}) + fu_check_options options, OPT_TABLE['install'] + fu_output_message "install -c#{options[:preserve] && ' -p'}#{options[:mode] ? (' -m 0%o' % options[:mode]) : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] + return if options[:noop] + fu_each_src_dest(src, dest) do |s, d| + unless File.exist?(d) and compare_file(s, d) + remove_file d, true + st = File.stat(s) if options[:preserve] + copy_file s, d + File.utime st.atime, st.mtime, d if options[:preserve] + File.chmod options[:mode], d if options[:mode] + end + end + end + module_function :install + + OPT_TABLE['install'] = [:mode, :preserve, :noop, :verbose] + + # + # Options: noop verbose + # + # Changes permission bits on the named files (in +list+) to the bit pattern + # represented by +mode+. + # + # FileUtils.chmod 0755, 'somecommand' + # FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb) + # FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true + # + def chmod(mode, list, options = {}) + fu_check_options options, OPT_TABLE['chmod'] + list = fu_list(list) + fu_output_message sprintf('chmod %o %s', mode, list.join(' ')) if options[:verbose] + return if options[:noop] + list.each do |path| + Entry_.new(path).chmod mode + end + end + module_function :chmod + + OPT_TABLE['chmod'] = [:noop, :verbose] + + # + # Options: noop verbose force + # + # Changes permission bits on the named files (in +list+) + # to the bit pattern represented by +mode+. + # + # FileUtils.chmod_R 0700, "/tmp/app.#{$$}" + # + def chmod_R(mode, list, options = {}) + fu_check_options options, OPT_TABLE['chmod_R'] + list = fu_list(list) + fu_output_message sprintf('chmod -R%s %o %s', + (options[:force] ? 'f' : ''), + mode, list.join(' ')) if options[:verbose] + return if options[:noop] + list.each do |root| + Entry_.new(root).traverse do |ent| + begin + ent.chmod mode + rescue + raise unless options[:force] + end + end + end + end + module_function :chmod_R + + OPT_TABLE['chmod_R'] = [:noop, :verbose, :force] + + # + # Options: noop verbose + # + # Changes owner and group on the named files (in +list+) + # to the user +user+ and the group +group+. +user+ and +group+ + # may be an ID (Integer/String) or a name (String). + # If +user+ or +group+ is nil, this method does not change + # the attribute. + # + # FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby' + # FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true + # + def chown(user, group, list, options = {}) + fu_check_options options, OPT_TABLE['chown'] + list = fu_list(list) + fu_output_message sprintf('chown %s%s', + [user,group].compact.join(':') + ' ', + list.join(' ')) if options[:verbose] + return if options[:noop] + uid = fu_get_uid(user) + gid = fu_get_gid(group) + list.each do |path| + Entry_.new(path).chown uid, gid + end + end + module_function :chown + + OPT_TABLE['chown'] = [:noop, :verbose] + + # + # Options: noop verbose force + # + # Changes owner and group on the named files (in +list+) + # to the user +user+ and the group +group+ recursively. + # +user+ and +group+ may be an ID (Integer/String) or + # a name (String). If +user+ or +group+ is nil, this + # method does not change the attribute. + # + # FileUtils.chown_R 'www', 'www', '/var/www/htdocs' + # FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true + # + def chown_R(user, group, list, options = {}) + fu_check_options options, OPT_TABLE['chown_R'] + list = fu_list(list) + fu_output_message sprintf('chown -R%s %s%s', + (options[:force] ? 'f' : ''), + [user,group].compact.join(':') + ' ', + list.join(' ')) if options[:verbose] + return if options[:noop] + uid = fu_get_uid(user) + gid = fu_get_gid(group) + return unless uid or gid + list.each do |root| + Entry_.new(root).traverse do |ent| + begin + ent.chown uid, gid + rescue + raise unless options[:force] + end + end + end + end + module_function :chown_R + + OPT_TABLE['chown_R'] = [:noop, :verbose, :force] + + begin + require 'etc' + + def fu_get_uid(user) #:nodoc: + return nil unless user + user = user.to_s + if /\A\d+\z/ =~ user + then user.to_i + else Etc.getpwnam(user).uid + end + end + private_module_function :fu_get_uid + + def fu_get_gid(group) #:nodoc: + return nil unless group + if /\A\d+\z/ =~ group + then group.to_i + else Etc.getgrnam(group).gid + end + end + private_module_function :fu_get_gid + + rescue LoadError + # need Win32 support??? + + def fu_get_uid(user) #:nodoc: + user # FIXME + end + private_module_function :fu_get_uid + + def fu_get_gid(group) #:nodoc: + group # FIXME + end + private_module_function :fu_get_gid + end + + # + # Options: noop verbose + # + # Updates modification time (mtime) and access time (atime) of file(s) in + # +list+. Files are created if they don't exist. + # + # FileUtils.touch 'timestamp' + # FileUtils.touch Dir.glob('*.c'); system 'make' + # + def touch(list, options = {}) + fu_check_options options, OPT_TABLE['touch'] + list = fu_list(list) + created = nocreate = options[:nocreate] + t = options[:mtime] + if options[:verbose] + fu_output_message "touch #{nocreate ? ' -c' : ''}#{t ? t.strftime(' -t %Y%m%d%H%M.%S') : ''}#{list.join ' '}" + end + return if options[:noop] + list.each do |path| + created = nocreate + begin + File.utime(t, t, path) + rescue Errno::ENOENT + raise if created + File.open(path, 'a') { + ; + } + created = true + retry if t + end + end + end + module_function :touch + + OPT_TABLE['touch'] = [:noop, :verbose, :mtime, :nocreate] + + private + + module StreamUtils_ + private + + def fu_windows? + /mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM + end + + def fu_copy_stream0(src, dest, blksize) #:nodoc: + # FIXME: readpartial? + while s = src.read(blksize) + dest.write s + end + end + + def fu_stream_blksize(*streams) + streams.each do |s| + next unless s.respond_to?(:stat) + size = fu_blksize(s.stat) + return size if size + end + fu_default_blksize() + end + + def fu_blksize(st) + s = st.blksize + return nil unless s + return nil if s == 0 + s + end + + def fu_default_blksize + 1024 + end + end + + include StreamUtils_ + extend StreamUtils_ + + class Entry_ #:nodoc: internal use only + include StreamUtils_ + + def initialize(a, b = nil, deref = false) + @prefix = @rel = @path = nil + if b + @prefix = a + @rel = b + else + @path = a + end + @deref = deref + @stat = nil + @lstat = nil + end + + def inspect + "\#<#{self.class} #{path()}>" + end + + def path + if @path + @path.to_str + else + join(@prefix, @rel) + end + end + + def prefix + @prefix || @path + end + + def rel + @rel + end + + def dereference? + @deref + end + + def exist? + lstat! ? true : false + end + + def file? + s = lstat! + s and s.file? + end + + def directory? + s = lstat! + s and s.directory? + end + + def symlink? + s = lstat! + s and s.symlink? + end + + def chardev? + s = lstat! + s and s.chardev? + end + + def blockdev? + s = lstat! + s and s.blockdev? + end + + def socket? + s = lstat! + s and s.socket? + end + + def pipe? + s = lstat! + s and s.pipe? + end + + S_IF_DOOR = 0xD000 + + def door? + s = lstat! + s and (s.mode & 0xF000 == S_IF_DOOR) + end + + def entries + Dir.entries(path())\ + .reject {|n| n == '.' or n == '..' }\ + .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) } + end + + def stat + return @stat if @stat + if lstat() and lstat().symlink? + @stat = File.stat(path()) + else + @stat = lstat() + end + @stat + end + + def stat! + return @stat if @stat + if lstat! and lstat!.symlink? + @stat = File.stat(path()) + else + @stat = lstat! + end + @stat + rescue SystemCallError + nil + end + + def lstat + if dereference? + @lstat ||= File.stat(path()) + else + @lstat ||= File.lstat(path()) + end + end + + def lstat! + lstat() + rescue SystemCallError + nil + end + + def chmod(mode) + if symlink? + File.lchmod mode, path() if have_lchmod? + else + File.chmod mode, path() + end + end + + def chown(uid, gid) + if symlink? + File.lchown uid, gid, path() if have_lchown? + else + File.chown uid, gid, path() + end + end + + def copy(dest) + case + when file? + copy_file dest + when directory? + begin + Dir.mkdir dest + rescue + raise unless File.directory?(dest) + end + when symlink? + File.symlink File.readlink(path()), dest + when chardev? + raise "cannot handle device file" unless File.respond_to?(:mknod) + mknod dest, ?c, 0666, lstat().rdev + when blockdev? + raise "cannot handle device file" unless File.respond_to?(:mknod) + mknod dest, ?b, 0666, lstat().rdev + when socket? + raise "cannot handle socket" unless File.respond_to?(:mknod) + mknod dest, nil, lstat().mode, 0 + when pipe? + raise "cannot handle FIFO" unless File.respond_to?(:mkfifo) + mkfifo dest, 0666 + when door? + raise "cannot handle door: #{path()}" + else + raise "unknown file type: #{path()}" + end + end + + def copy_file(dest) + st = stat() + File.open(path(), 'rb') {|r| + File.open(dest, 'wb', st.mode) {|w| + fu_copy_stream0 r, w, (fu_blksize(st) || fu_default_blksize()) + } + } + end + + def copy_metadata(path) + st = lstat() + File.utime st.atime, st.mtime, path + begin + File.chown st.uid, st.gid, path + rescue Errno::EPERM + # clear setuid/setgid + File.chmod st.mode & 01777, path + else + File.chmod st.mode, path + end + end + + def remove + if directory? + remove_dir1 + else + remove_file + end + end + + def remove_dir1 + platform_support { + Dir.rmdir path().sub(%r, '') + } + end + + def remove_file + platform_support { + File.unlink path + } + end + + def platform_support + return yield unless fu_windows? + first_time_p = true + begin + yield + rescue Errno::ENOENT + raise + rescue => err + if first_time_p + first_time_p = false + begin + File.chmod 0700, path() # Windows does not have symlink + retry + rescue SystemCallError + end + end + raise err + end + end + + def preorder_traverse + stack = [self] + while ent = stack.pop + yield ent + stack.concat ent.entries.reverse if ent.directory? + end + end + + alias traverse preorder_traverse + + def postorder_traverse + if directory? + entries().each do |ent| + ent.postorder_traverse do |e| + yield e + end + end + end + yield self + end + + private + + $fileutils_rb_have_lchmod = nil + + def have_lchmod? + # This is not MT-safe, but it does not matter. + if $fileutils_rb_have_lchmod == nil + $fileutils_rb_have_lchmod = check_have_lchmod? + end + $fileutils_rb_have_lchmod + end + + def check_have_lchmod? + return false unless File.respond_to?(:lchmod) + File.lchmod 0 + return true + rescue NotImplementedError + return false + end + + $fileutils_rb_have_lchown = nil + + def have_lchown? + # This is not MT-safe, but it does not matter. + if $fileutils_rb_have_lchown == nil + $fileutils_rb_have_lchown = check_have_lchown? + end + $fileutils_rb_have_lchown + end + + def check_have_lchown? + return false unless File.respond_to?(:lchown) + File.lchown nil, nil + return true + rescue NotImplementedError + return false + end + + def join(dir, base) + return dir.to_str if not base or base == '.' + return base.to_str if not dir or dir == '.' + File.join(dir, base) + end + end # class Entry_ + + def fu_list(arg) #:nodoc: + [arg].flatten.map {|path| path.to_str } + end + private_module_function :fu_list + + def fu_each_src_dest(src, dest) #:nodoc: + fu_each_src_dest0(src, dest) do |s, d| + raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d) + yield s, d + end + end + private_module_function :fu_each_src_dest + + def fu_each_src_dest0(src, dest) #:nodoc: + if src.is_a?(Array) + src.each do |s| + s = s.to_str + yield s, File.join(dest, File.basename(s)) + end + else + src = src.to_str + if File.directory?(dest) + yield src, File.join(dest, File.basename(src)) + else + yield src, dest.to_str + end + end + end + private_module_function :fu_each_src_dest0 + + def fu_same?(a, b) #:nodoc: + if fu_have_st_ino? + st1 = File.stat(a) + st2 = File.stat(b) + st1.dev == st2.dev and st1.ino == st2.ino + else + File.expand_path(a) == File.expand_path(b) + end + rescue Errno::ENOENT + return false + end + private_module_function :fu_same? + + def fu_have_st_ino? #:nodoc: + not fu_windows? + end + private_module_function :fu_have_st_ino? + + def fu_check_options(options, optdecl) #:nodoc: + h = options.dup + optdecl.each do |opt| + h.delete opt + end + raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty? + end + private_module_function :fu_check_options + + def fu_update_option(args, new) #:nodoc: + if args.last.is_a?(Hash) + args[-1] = args.last.dup.update(new) + else + args.push new + end + args + end + private_module_function :fu_update_option + + @fileutils_output = $stderr + @fileutils_label = '' + + def fu_output_message(msg) #:nodoc: + @fileutils_output ||= $stderr + @fileutils_label ||= '' + @fileutils_output.puts @fileutils_label + msg + end + private_module_function :fu_output_message + + # + # Returns an Array of method names which have any options. + # + # p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...] + # + def FileUtils.commands + OPT_TABLE.keys + end + + # + # Returns an Array of option names. + # + # p FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"] + # + def FileUtils.options + OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s } + end + + # + # Returns true if the method +mid+ have an option +opt+. + # + # p FileUtils.have_option?(:cp, :noop) #=> true + # p FileUtils.have_option?(:rm, :force) #=> true + # p FileUtils.have_option?(:rm, :perserve) #=> false + # + def FileUtils.have_option?(mid, opt) + li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}" + li.include?(opt) + end + + # + # Returns an Array of option names of the method +mid+. + # + # p FileUtils.options(:rm) #=> ["noop", "verbose", "force"] + # + def FileUtils.options_of(mid) + OPT_TABLE[mid.to_s].map {|sym| sym.to_s } + end + + # + # Returns an Array of method names which have the option +opt+. + # + # p FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"] + # + def FileUtils.collect_method(opt) + OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) } + end + + METHODS = singleton_methods() - %w( private_module_function + commands options have_option? options_of collect_method ) + + # + # This module has all methods of FileUtils module, but it outputs messages + # before acting. This equates to passing the :verbose flag to + # methods in FileUtils. + # + module Verbose + include FileUtils + @fileutils_output = $stderr + @fileutils_label = '' + ::FileUtils.collect_method(:verbose).each do |name| + module_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{name}(*args) + super(*fu_update_option(args, :verbose => true)) + end + private :#{name} + EOS + end + extend self + class << self + ::FileUtils::METHODS.each do |m| + public m + end + end + end + + # + # This module has all methods of FileUtils module, but never changes + # files/directories. This equates to passing the :noop flag + # to methods in FileUtils. + # + module NoWrite + include FileUtils + @fileutils_output = $stderr + @fileutils_label = '' + ::FileUtils.collect_method(:noop).each do |name| + module_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{name}(*args) + super(*fu_update_option(args, :noop => true)) + end + private :#{name} + EOS + end + extend self + class << self + ::FileUtils::METHODS.each do |m| + public m + end + end + end + + # + # This module has all methods of FileUtils module, but never changes + # files/directories, with printing message before acting. + # This equates to passing the :noop and :verbose flag + # to methods in FileUtils. + # + module DryRun + include FileUtils + @fileutils_output = $stderr + @fileutils_label = '' + ::FileUtils.collect_method(:noop).each do |name| + module_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{name}(*args) + super(*fu_update_option(args, :noop => true, :verbose => true)) + end + private :#{name} + EOS + end + extend self + class << self + ::FileUtils::METHODS.each do |m| + public m + end + end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/finalize.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/finalize.rb new file mode 100644 index 0000000000..9b6b302cac --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/finalize.rb @@ -0,0 +1,193 @@ +#-- +# finalizer.rb - +# $Release Version: 0.3$ +# $Revision: 1.4 $ +# $Date: 1998/02/27 05:34:33 $ +# by Keiju ISHITSUKA +#++ +# +# Usage: +# +# add dependency R_method(obj, dependant) +# add(obj, dependant, method = :finalize, *opt) +# add_dependency(obj, dependant, method = :finalize, *opt) +# +# delete dependency R_method(obj, dependant) +# delete(obj_or_id, dependant, method = :finalize) +# delete_dependency(obj_or_id, dependant, method = :finalize) +# +# delete dependency R_*(obj, dependant) +# delete_all_dependency(obj_or_id, dependant) +# +# delete dependency R_method(*, dependant) +# delete_by_dependant(dependant, method = :finalize) +# +# delete dependency R_*(*, dependant) +# delete_all_by_dependant(dependant) +# +# delete all dependency R_*(*, *) +# delete_all +# +# finalize the dependant connected by dependency R_method(obj, dependtant). +# finalize(obj_or_id, dependant, method = :finalize) +# finalize_dependency(obj_or_id, dependant, method = :finalize) +# +# finalize all dependants connected by dependency R_*(obj, dependtant). +# finalize_all_dependency(obj_or_id, dependant) +# +# finalize the dependant connected by dependency R_method(*, dependtant). +# finalize_by_dependant(dependant, method = :finalize) +# +# finalize all dependants connected by dependency R_*(*, dependant). +# finalize_all_by_dependant(dependant) +# +# finalize all dependency registered to the Finalizer. +# finalize_all +# +# stop invoking Finalizer on GC. +# safe{..} +# + +module Finalizer + RCS_ID='-$Id: finalize.rb,v 1.4 1998/02/27 05:34:33 keiju Exp keiju $-' + + class < [[dependant, method, *opt], ...], ...} + + # add dependency R_method(obj, dependant) + def add_dependency(obj, dependant, method = :finalize, *opt) + ObjectSpace.call_finalizer(obj) + method = method.intern unless method.kind_of?(Integer) + assoc = [dependant, method].concat(opt) + if dep = @dependency[obj.object_id] + dep.push assoc + else + @dependency[obj.object_id] = [assoc] + end + end + alias add add_dependency + + # delete dependency R_method(obj, dependant) + def delete_dependency(id, dependant, method = :finalize) + id = id.object_id unless id.kind_of?(Integer) + method = method.intern unless method.kind_of?(Integer) + for assoc in @dependency[id] + assoc.delete_if do + |d, m, *o| + d == dependant && m == method + end + @dependency.delete(id) if assoc.empty? + end + end + alias delete delete_dependency + + # delete dependency R_*(obj, dependant) + def delete_all_dependency(id, dependant) + id = id.object_id unless id.kind_of?(Integer) + method = method.intern unless method.kind_of?(Integer) + for assoc in @dependency[id] + assoc.delete_if do + |d, m, *o| + d == dependant + end + @dependency.delete(id) if assoc.empty? + end + end + + # delete dependency R_method(*, dependant) + def delete_by_dependant(dependant, method = :finalize) + method = method.intern unless method.kind_of?(Integer) + for id in @dependency.keys + delete(id, dependant, method) + end + end + + # delete dependency R_*(*, dependant) + def delete_all_by_dependant(dependant) + for id in @dependency.keys + delete_all_dependency(id, dependant) + end + end + + # finalize the depandant connected by dependency R_method(obj, dependtant) + def finalize_dependency(id, dependant, method = :finalize) + id = id.object_id unless id.kind_of?(Integer) + method = method.intern unless method.kind_of?(Integer) + for assocs in @dependency[id] + assocs.delete_if do + |d, m, *o| + d.send(m, id, *o) if ret = d == dependant && m == method + ret + end + @dependency.delete(id) if assoc.empty? + end + end + alias finalize finalize_dependency + + # finalize all dependants connected by dependency R_*(obj, dependtant) + def finalize_all_dependency(id, dependant) + id = id.object_id unless id.kind_of?(Integer) + method = method.intern unless method.kind_of?(Integer) + for assoc in @dependency[id] + assoc.delete_if do + |d, m, *o| + d.send(m, id, *o) if ret = d == dependant + end + @dependency.delete(id) if assoc.empty? + end + end + + # finalize the dependant connected by dependency R_method(*, dependtant) + def finalize_by_dependant(dependant, method = :finalize) + method = method.intern unless method.kind_of?(Integer) + for id in @dependency.keys + finalize(id, dependant, method) + end + end + + # finalize all dependants connected by dependency R_*(*, dependtant) + def finalize_all_by_dependant(dependant) + for id in @dependency.keys + finalize_all_dependency(id, dependant) + end + end + + # finalize all dependants registered to the Finalizer. + def finalize_all + for id, assocs in @dependency + for dependant, method, *opt in assocs + dependant.send(method, id, *opt) + end + assocs.clear + end + end + + # method to call finalize_* safely. + def safe + old_status = Thread.critical + Thread.critical = true + ObjectSpace.remove_finalizer(@proc) + begin + yield + ensure + ObjectSpace.add_finalizer(@proc) + Thread.critical = old_status + end + end + + private + + # registering function to ObjectSpace#add_finalizer + def final_of(id) + if assocs = @dependency.delete(id) + for dependant, method, *opt in assocs + dependant.send(method, id, *opt) + end + end + end + + end + @dependency = Hash.new + @proc = proc{|id| final_of(id)} + ObjectSpace.add_finalizer(@proc) +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/find.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/find.rb new file mode 100644 index 0000000000..0d22dd62d6 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/find.rb @@ -0,0 +1,79 @@ +# +# find.rb: the Find module for processing all files under a given directory. +# + +# +# The +Find+ module supports the top-down traversal of a set of file paths. +# +# For example, to total the size of all files under your home directory, +# ignoring anything in a "dot" directory (e.g. $HOME/.ssh): +# +# require 'find' +# +# total_size = 0 +# +# Find.find(ENV["HOME"]) do |path| +# if FileTest.directory?(path) +# if File.basename(path)[0] == ?. +# Find.prune # Don't look any further into this directory. +# else +# next +# end +# else +# total_size += FileTest.size(path) +# end +# end +# +module Find + + # + # Calls the associated block with the name of every file and directory listed + # as arguments, then recursively on their subdirectories, and so on. + # + # See the +Find+ module documentation for an example. + # + def find(*paths) # :yield: path + paths.collect!{|d| d.dup} + while file = paths.shift + catch(:prune) do + yield file.dup.taint + next unless File.exist? file + begin + if File.lstat(file).directory? then + d = Dir.open(file) + begin + for f in d + next if f == "." or f == ".." + if File::ALT_SEPARATOR and file =~ /^(?:[\/\\]|[A-Za-z]:[\/\\]?)$/ then + f = file + f + elsif file == "/" then + f = "/" + f + else + f = File.join(file, f) + end + paths.unshift f.untaint + end + ensure + d.close + end + end + rescue Errno::ENOENT, Errno::EACCES + end + end + end + end + + # + # Skips the current file or directory, restarting the loop with the next + # entry. If the current file is a directory, that directory will not be + # recursively entered. Meaningful only within the block associated with + # Find::find. + # + # See the +Find+ module documentation for an example. + # + def prune + throw :prune + end + + module_function :find, :prune +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/forwardable.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/forwardable.rb new file mode 100644 index 0000000000..e2a2727dd7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/forwardable.rb @@ -0,0 +1,218 @@ +# = forwardable - Support for the Delegation Pattern +# +# $Release Version: 1.1$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ishitsuka.com) +# +# Documentation by James Edward Gray II and Gavin Sinclair +# +# == Introduction +# +# This library allows you delegate method calls to an object, on a method by +# method basis. You can use Forwardable to setup this delegation at the class +# level, or SingleForwardable to handle it at the object level. +# +# == Notes +# +# Be advised, RDoc will not detect delegated methods. +# +# forwardable.rb provides single-method delegation via the +# def_delegator() and def_delegators() methods. For full-class +# delegation via DelegateClass(), see delegate.rb. +# +# == Examples +# +# === Forwardable +# +# Forwardable makes building a new class based on existing work, with a proper +# interface, almost trivial. We want to rely on what has come before obviously, +# but with delegation we can take just the methods we need and even rename them +# as appropriate. In many cases this is preferable to inheritance, which gives +# us the entire old interface, even if much of it isn't needed. +# +# class Queue +# extend Forwardable +# +# def initialize +# @q = [ ] # prepare delegate object +# end +# +# # setup prefered interface, enq() and deq()... +# def_delegator :@q, :push, :enq +# def_delegator :@q, :shift, :deq +# +# # support some general Array methods that fit Queues well +# def_delegators :@q, :clear, :first, :push, :shift, :size +# end +# +# q = Queue.new +# q.enq 1, 2, 3, 4, 5 +# q.push 6 +# +# q.shift # => 1 +# while q.size > 0 +# puts q.deq +# end +# +# q.enq "Ruby", "Perl", "Python" +# puts q.first +# q.clear +# puts q.first +# +# Prints: +# +# 2 +# 3 +# 4 +# 5 +# 6 +# Ruby +# nil +# +# === SingleForwardable +# +# printer = String.new +# printer.extend SingleForwardable # prepare object for delegation +# printer.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts() +# printer.puts "Howdy!" +# +# Prints: +# +# Howdy! + +# +# The Forwardable module provides delegation of specified +# methods to a designated object, using the methods #def_delegator +# and #def_delegators. +# +# For example, say you have a class RecordCollection which +# contains an array @records. You could provide the lookup method +# #record_number(), which simply calls #[] on the @records +# array, like this: +# +# class RecordCollection +# extend Forwardable +# def_delegator :@records, :[], :record_number +# end +# +# Further, if you wish to provide the methods #size, #<<, and #map, +# all of which delegate to @records, this is how you can do it: +# +# class RecordCollection +# # extend Forwardable, but we did that above +# def_delegators :@records, :size, :<<, :map +# end +# +# Also see the example at forwardable.rb. +# +module Forwardable + + @debug = nil + class<to/from. + # + def syscopy(from, to) + to = catname(from, to) + + fmode = stat(from).mode + tpath = to + not_exist = !exist?(tpath) + + from = open(from, "rb") + to = open(to, "wb") + + begin + while true + to.syswrite from.sysread(BUFSIZE) + end + rescue EOFError + ret = true + rescue + ret = false + ensure + to.close + from.close + end + chmod(fmode, tpath) if not_exist + ret + end + + # + # Copies a file +from+ to +to+ using #syscopy. If +to+ is a directory, + # copies +from+ to to/from. If +verbose+ is true, from -> to + # is printed. + # + def copy(from, to, verbose = false) + $stderr.print from, " -> ", catname(from, to), "\n" if verbose + syscopy from, to + end + + alias cp copy + + # + # Moves a file +from+ to +to+ using #syscopy. If +to+ is a directory, + # copies from +from+ to to/from. If +verbose+ is true, from -> + # to is printed. + # + def move(from, to, verbose = false) + to = catname(from, to) + $stderr.print from, " -> ", to, "\n" if verbose + + if RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw/ and file? to + unlink to + end + fstat = stat(from) + begin + rename from, to + rescue + begin + symlink readlink(from), to and unlink from + rescue + from_stat = stat(from) + syscopy from, to and unlink from + utime(from_stat.atime, from_stat.mtime, to) + begin + chown(fstat.uid, fstat.gid, to) + rescue + end + end + end + end + + alias mv move + + # + # Returns +true+ if and only if the contents of files +from+ and +to+ are + # identical. If +verbose+ is +true+, from <=> to is printed. + # + def compare(from, to, verbose = false) + $stderr.print from, " <=> ", to, "\n" if verbose + + return false if stat(from).size != stat(to).size + + from = open(from, "rb") + to = open(to, "rb") + + ret = false + fr = tr = '' + + begin + while fr == tr + fr = from.read(BUFSIZE) + if fr + tr = to.read(fr.size) + else + ret = to.read(BUFSIZE) + ret = !ret || ret.length == 0 + break + end + end + rescue + ret = false + ensure + to.close + from.close + end + ret + end + + alias cmp compare + + # + # Removes a list of files. Each parameter should be the name of the file to + # delete. If the last parameter isn't a String, verbose mode will be enabled. + # Returns the number of files deleted. + # + def safe_unlink(*files) + verbose = if files[-1].is_a? String then false else files.pop end + files.each do |file| + begin + unlink file + $stderr.print "removing ", file, "\n" if verbose + rescue Errno::EACCES # for Windows + continue if symlink? file + begin + mode = stat(file).mode + o_chmod mode | 0200, file + unlink file + $stderr.print "removing ", file, "\n" if verbose + rescue + o_chmod mode, file rescue nil + end + rescue + end + end + end + + alias rm_f safe_unlink + + # + # Creates a directory and all its parent directories. + # For example, + # + # File.makedirs '/usr/lib/ruby' + # + # causes the following directories to be made, if they do not exist. + # * /usr + # * /usr/lib + # * /usr/lib/ruby + # + # You can pass several directories, each as a parameter. If the last + # parameter isn't a String, verbose mode will be enabled. + # + def makedirs(*dirs) + verbose = if dirs[-1].is_a? String then false else dirs.pop end + mode = 0755 + for dir in dirs + parent = dirname(dir) + next if parent == dir or directory? dir + makedirs parent unless directory? parent + $stderr.print "mkdir ", dir, "\n" if verbose + if basename(dir) != "" + begin + Dir.mkdir dir, mode + rescue SystemCallError + raise unless directory? dir + end + end + end + end + + alias mkpath makedirs + + alias o_chmod chmod + + vsave, $VERBOSE = $VERBOSE, false + + # + # Changes permission bits on +files+ to the bit pattern represented + # by +mode+. If the last parameter isn't a String, verbose mode will + # be enabled. + # + # File.chmod 0755, 'somecommand' + # File.chmod 0644, 'my.rb', 'your.rb', true + # + def chmod(mode, *files) + verbose = if files[-1].is_a? String then false else files.pop end + $stderr.printf "chmod %04o %s\n", mode, files.join(" ") if verbose + o_chmod mode, *files + end + $VERBOSE = vsave + + # + # If +src+ is not the same as +dest+, copies it and changes the permission + # mode to +mode+. If +dest+ is a directory, destination is dest/src. + # If +mode+ is not set, default is used. If +verbose+ is set to true, the + # name of each file copied will be printed. + # + def install(from, to, mode = nil, verbose = false) + to = catname(from, to) + unless exist? to and cmp from, to + safe_unlink to if exist? to + cp from, to, verbose + chmod mode, to, verbose if mode + end + end + +end + +# vi:set sw=2: diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/generator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/generator.rb new file mode 100644 index 0000000000..fcc2c64a31 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/generator.rb @@ -0,0 +1,380 @@ +#!/usr/bin/env ruby +#-- +# $Idaemons: /home/cvs/rb/generator.rb,v 1.8 2001/10/03 08:54:32 knu Exp $ +# $RoughId: generator.rb,v 1.10 2003/10/14 19:36:58 knu Exp $ +# $Id: generator.rb 11708 2007-02-12 23:01:19Z shyouhei $ +#++ +# +# = generator.rb: convert an internal iterator to an external one +# +# Copyright (c) 2001,2003 Akinori MUSHA +# +# All rights reserved. You can redistribute and/or modify it under +# the same terms as Ruby. +# +# == Overview +# +# This library provides the Generator class, which converts an +# internal iterator (i.e. an Enumerable object) to an external +# iterator. In that form, you can roll many iterators independently. +# +# The SyncEnumerator class, which is implemented using Generator, +# makes it easy to roll many Enumerable objects synchronously. +# +# See the respective classes for examples of usage. + + +# +# Generator converts an internal iterator (i.e. an Enumerable object) +# to an external iterator. +# +# Note that it is not very fast since it is implemented using +# continuations, which are currently slow. +# +# == Example +# +# require 'generator' +# +# # Generator from an Enumerable object +# g = Generator.new(['A', 'B', 'C', 'Z']) +# +# while g.next? +# puts g.next +# end +# +# # Generator from a block +# g = Generator.new { |g| +# for i in 'A'..'C' +# g.yield i +# end +# +# g.yield 'Z' +# } +# +# # The same result as above +# while g.next? +# puts g.next +# end +# +class Generator + include Enumerable + + # Creates a new generator either from an Enumerable object or from a + # block. + # + # In the former, block is ignored even if given. + # + # In the latter, the given block is called with the generator + # itself, and expected to call the +yield+ method for each element. + def initialize(enum = nil, &block) + if enum + @block = proc { |g| + enum.each { |x| g.yield x } + } + else + @block = block + end + + @index = 0 + @queue = [] + @cont_next = @cont_yield = @cont_endp = nil + + if @cont_next = callcc { |c| c } + @block.call(self) + + @cont_endp.call(nil) if @cont_endp + end + + self + end + + # Yields an element to the generator. + def yield(value) + if @cont_yield = callcc { |c| c } + @queue << value + @cont_next.call(nil) + end + + self + end + + # Returns true if the generator has reached the end. + def end?() + if @cont_endp = callcc { |c| c } + @cont_yield.nil? && @queue.empty? + else + @queue.empty? + end + end + + # Returns true if the generator has not reached the end yet. + def next?() + !end? + end + + # Returns the current index (position) counting from zero. + def index() + @index + end + + # Returns the current index (position) counting from zero. + def pos() + @index + end + + # Returns the element at the current position and moves forward. + def next() + if end? + raise EOFError, "no more elements available" + end + + if @cont_next = callcc { |c| c } + @cont_yield.call(nil) if @cont_yield + end + + @index += 1 + + @queue.shift + end + + # Returns the element at the current position. + def current() + if @queue.empty? + raise EOFError, "no more elements available" + end + + @queue.first + end + + # Rewinds the generator. + def rewind() + initialize(nil, &@block) if @index.nonzero? + + self + end + + # Rewinds the generator and enumerates the elements. + def each + rewind + + until end? + yield self.next + end + + self + end +end + +# +# SyncEnumerator creates an Enumerable object from multiple Enumerable +# objects and enumerates them synchronously. +# +# == Example +# +# require 'generator' +# +# s = SyncEnumerator.new([1,2,3], ['a', 'b', 'c']) +# +# # Yields [1, 'a'], [2, 'b'], and [3,'c'] +# s.each { |row| puts row.join(', ') } +# +class SyncEnumerator + include Enumerable + + # Creates a new SyncEnumerator which enumerates rows of given + # Enumerable objects. + def initialize(*enums) + @gens = enums.map { |e| Generator.new(e) } + end + + # Returns the number of enumerated Enumerable objects, i.e. the size + # of each row. + def size + @gens.size + end + + # Returns the number of enumerated Enumerable objects, i.e. the size + # of each row. + def length + @gens.length + end + + # Returns true if the given nth Enumerable object has reached the + # end. If no argument is given, returns true if any of the + # Enumerable objects has reached the end. + def end?(i = nil) + if i.nil? + @gens.detect { |g| g.end? } ? true : false + else + @gens[i].end? + end + end + + # Enumerates rows of the Enumerable objects. + def each + @gens.each { |g| g.rewind } + + loop do + count = 0 + + ret = @gens.map { |g| + if g.end? + count += 1 + nil + else + g.next + end + } + + if count == @gens.size + break + end + + yield ret + end + + self + end +end + +if $0 == __FILE__ + eval DATA.read, nil, $0, __LINE__+4 +end + +__END__ + +require 'test/unit' + +class TC_Generator < Test::Unit::TestCase + def test_block1 + g = Generator.new { |g| + # no yield's + } + + assert_equal(0, g.pos) + assert_raises(EOFError) { g.current } + end + + def test_block2 + g = Generator.new { |g| + for i in 'A'..'C' + g.yield i + end + + g.yield 'Z' + } + + assert_equal(0, g.pos) + assert_equal('A', g.current) + + assert_equal(true, g.next?) + assert_equal(0, g.pos) + assert_equal('A', g.current) + assert_equal(0, g.pos) + assert_equal('A', g.next) + + assert_equal(1, g.pos) + assert_equal(true, g.next?) + assert_equal(1, g.pos) + assert_equal('B', g.current) + assert_equal(1, g.pos) + assert_equal('B', g.next) + + assert_equal(g, g.rewind) + + assert_equal(0, g.pos) + assert_equal('A', g.current) + + assert_equal(true, g.next?) + assert_equal(0, g.pos) + assert_equal('A', g.current) + assert_equal(0, g.pos) + assert_equal('A', g.next) + + assert_equal(1, g.pos) + assert_equal(true, g.next?) + assert_equal(1, g.pos) + assert_equal('B', g.current) + assert_equal(1, g.pos) + assert_equal('B', g.next) + + assert_equal(2, g.pos) + assert_equal(true, g.next?) + assert_equal(2, g.pos) + assert_equal('C', g.current) + assert_equal(2, g.pos) + assert_equal('C', g.next) + + assert_equal(3, g.pos) + assert_equal(true, g.next?) + assert_equal(3, g.pos) + assert_equal('Z', g.current) + assert_equal(3, g.pos) + assert_equal('Z', g.next) + + assert_equal(4, g.pos) + assert_equal(false, g.next?) + assert_raises(EOFError) { g.next } + end + + def test_each + a = [5, 6, 7, 8, 9] + + g = Generator.new(a) + + i = 0 + + g.each { |x| + assert_equal(a[i], x) + + i += 1 + + break if i == 3 + } + + assert_equal(3, i) + + i = 0 + + g.each { |x| + assert_equal(a[i], x) + + i += 1 + } + + assert_equal(5, i) + end +end + +class TC_SyncEnumerator < Test::Unit::TestCase + def test_each + r = ['a'..'f', 1..10, 10..20] + ra = r.map { |x| x.to_a } + + a = (0...(ra.map {|x| x.size}.max)).map { |i| ra.map { |x| x[i] } } + + s = SyncEnumerator.new(*r) + + i = 0 + + s.each { |x| + assert_equal(a[i], x) + + i += 1 + + break if i == 3 + } + + assert_equal(3, i) + + i = 0 + + s.each { |x| + assert_equal(a[i], x) + + i += 1 + } + + assert_equal(a.size, i) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/getoptlong.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/getoptlong.rb new file mode 100644 index 0000000000..4d004419b1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/getoptlong.rb @@ -0,0 +1,621 @@ +# +# GetoptLong for Ruby +# +# Copyright (C) 1998, 1999, 2000 Motoyuki Kasahara. +# +# You may redistribute and/or modify this library under the same license +# terms as Ruby. +# +# See GetoptLong for documentation. +# +# Additional documents and the latest version of `getoptlong.rb' can be +# found at http://www.sra.co.jp/people/m-kasahr/ruby/getoptlong/ + +# The GetoptLong class allows you to parse command line options similarly to +# the GNU getopt_long() C library call. Note, however, that GetoptLong is a +# pure Ruby implementation. +# +# GetoptLong allows for POSIX-style options like --file as well +# as single letter options like -f +# +# The empty option -- (two minus symbols) is used to end option +# processing. This can be particularly important if options have optional +# arguments. +# +# Here is a simple example of usage: +# +# # == Synopsis +# # +# # hello: greets user, demonstrates command line parsing +# # +# # == Usage +# # +# # hello [OPTION] ... DIR +# # +# # -h, --help: +# # show help +# # +# # --repeat x, -n x: +# # repeat x times +# # +# # --name [name]: +# # greet user by name, if name not supplied default is John +# # +# # DIR: The directory in which to issue the greeting. +# +# require 'getoptlong' +# require 'rdoc/usage' +# +# opts = GetoptLong.new( +# [ '--help', '-h', GetoptLong::NO_ARGUMENT ], +# [ '--repeat', '-n', GetoptLong::REQUIRED_ARGUMENT ], +# [ '--name', GetoptLong::OPTIONAL_ARGUMENT ] +# ) +# +# dir = nil +# name = nil +# repetitions = 1 +# opts.each do |opt, arg| +# case opt +# when '--help' +# RDoc::usage +# when '--repeat' +# repetitions = arg.to_i +# when '--name' +# if arg == '' +# name = 'John' +# else +# name = arg +# end +# end +# end +# +# if ARGV.length != 1 +# puts "Missing dir argument (try --help)" +# exit 0 +# end +# +# dir = ARGV.shift +# +# Dir.chdir(dir) +# for i in (1..repetitions) +# print "Hello" +# if name +# print ", #{name}" +# end +# puts +# end +# +# Example command line: +# +# hello -n 6 --name -- /tmp +# +class GetoptLong + # + # Orderings. + # + ORDERINGS = [REQUIRE_ORDER = 0, PERMUTE = 1, RETURN_IN_ORDER = 2] + + # + # Argument flags. + # + ARGUMENT_FLAGS = [NO_ARGUMENT = 0, REQUIRED_ARGUMENT = 1, + OPTIONAL_ARGUMENT = 2] + + # + # Status codes. + # + STATUS_YET, STATUS_STARTED, STATUS_TERMINATED = 0, 1, 2 + + # + # Error types. + # + class Error < StandardError; end + class AmbigousOption < Error; end + class NeedlessArgument < Error; end + class MissingArgument < Error; end + class InvalidOption < Error; end + + # + # Set up option processing. + # + # The options to support are passed to new() as an array of arrays. + # Each sub-array contains any number of String option names which carry + # the same meaning, and one of the following flags: + # + # GetoptLong::NO_ARGUMENT :: Option does not take an argument. + # + # GetoptLong::REQUIRED_ARGUMENT :: Option always takes an argument. + # + # GetoptLong::OPTIONAL_ARGUMENT :: Option may or may not take an argument. + # + # The first option name is considered to be the preferred (canonical) name. + # Other than that, the elements of each sub-array can be in any order. + # + def initialize(*arguments) + # + # Current ordering. + # + if ENV.include?('POSIXLY_CORRECT') + @ordering = REQUIRE_ORDER + else + @ordering = PERMUTE + end + + # + # Hash table of option names. + # Keys of the table are option names, and their values are canonical + # names of the options. + # + @canonical_names = Hash.new + + # + # Hash table of argument flags. + # Keys of the table are option names, and their values are argument + # flags of the options. + # + @argument_flags = Hash.new + + # + # Whether error messages are output to $deferr. + # + @quiet = FALSE + + # + # Status code. + # + @status = STATUS_YET + + # + # Error code. + # + @error = nil + + # + # Error message. + # + @error_message = nil + + # + # Rest of catenated short options. + # + @rest_singles = '' + + # + # List of non-option-arguments. + # Append them to ARGV when option processing is terminated. + # + @non_option_arguments = Array.new + + if 0 < arguments.length + set_options(*arguments) + end + end + + # + # Set the handling of the ordering of options and arguments. + # A RuntimeError is raised if option processing has already started. + # + # The supplied value must be a member of GetoptLong::ORDERINGS. It alters + # the processing of options as follows: + # + # REQUIRE_ORDER : + # + # Options are required to occur before non-options. + # + # Processing of options ends as soon as a word is encountered that has not + # been preceded by an appropriate option flag. + # + # For example, if -a and -b are options which do not take arguments, + # parsing command line arguments of '-a one -b two' would result in + # 'one', '-b', 'two' being left in ARGV, and only ('-a', '') being + # processed as an option/arg pair. + # + # This is the default ordering, if the environment variable + # POSIXLY_CORRECT is set. (This is for compatibility with GNU getopt_long.) + # + # PERMUTE : + # + # Options can occur anywhere in the command line parsed. This is the + # default behavior. + # + # Every sequence of words which can be interpreted as an option (with or + # without argument) is treated as an option; non-option words are skipped. + # + # For example, if -a does not require an argument and -b optionally takes + # an argument, parsing '-a one -b two three' would result in ('-a','') and + # ('-b', 'two') being processed as option/arg pairs, and 'one','three' + # being left in ARGV. + # + # If the ordering is set to PERMUTE but the environment variable + # POSIXLY_CORRECT is set, REQUIRE_ORDER is used instead. This is for + # compatibility with GNU getopt_long. + # + # RETURN_IN_ORDER : + # + # All words on the command line are processed as options. Words not + # preceded by a short or long option flag are passed as arguments + # with an option of '' (empty string). + # + # For example, if -a requires an argument but -b does not, a command line + # of '-a one -b two three' would result in option/arg pairs of ('-a', 'one') + # ('-b', ''), ('', 'two'), ('', 'three') being processed. + # + def ordering=(ordering) + # + # The method is failed if option processing has already started. + # + if @status != STATUS_YET + set_error(ArgumentError, "argument error") + raise RuntimeError, + "invoke ordering=, but option processing has already started" + end + + # + # Check ordering. + # + if !ORDERINGS.include?(ordering) + raise ArgumentError, "invalid ordering `#{ordering}'" + end + if ordering == PERMUTE && ENV.include?('POSIXLY_CORRECT') + @ordering = REQUIRE_ORDER + else + @ordering = ordering + end + end + + # + # Return ordering. + # + attr_reader :ordering + + # + # Set options. Takes the same argument as GetoptLong.new. + # + # Raises a RuntimeError if option processing has already started. + # + def set_options(*arguments) + # + # The method is failed if option processing has already started. + # + if @status != STATUS_YET + raise RuntimeError, + "invoke set_options, but option processing has already started" + end + + # + # Clear tables of option names and argument flags. + # + @canonical_names.clear + @argument_flags.clear + + arguments.each do |arg| + # + # Each argument must be an Array. + # + if !arg.is_a?(Array) + raise ArgumentError, "the option list contains non-Array argument" + end + + # + # Find an argument flag and it set to `argument_flag'. + # + argument_flag = nil + arg.each do |i| + if ARGUMENT_FLAGS.include?(i) + if argument_flag != nil + raise ArgumentError, "too many argument-flags" + end + argument_flag = i + end + end + raise ArgumentError, "no argument-flag" if argument_flag == nil + + canonical_name = nil + arg.each do |i| + # + # Check an option name. + # + next if i == argument_flag + begin + if !i.is_a?(String) || i !~ /^-([^-]|-.+)$/ + raise ArgumentError, "an invalid option `#{i}'" + end + if (@canonical_names.include?(i)) + raise ArgumentError, "option redefined `#{i}'" + end + rescue + @canonical_names.clear + @argument_flags.clear + raise + end + + # + # Register the option (`i') to the `@canonical_names' and + # `@canonical_names' Hashes. + # + if canonical_name == nil + canonical_name = i + end + @canonical_names[i] = canonical_name + @argument_flags[i] = argument_flag + end + raise ArgumentError, "no option name" if canonical_name == nil + end + return self + end + + # + # Set/Unset `quiet' mode. + # + attr_writer :quiet + + # + # Return the flag of `quiet' mode. + # + attr_reader :quiet + + # + # `quiet?' is an alias of `quiet'. + # + alias quiet? quiet + + # + # Explicitly terminate option processing. + # + def terminate + return nil if @status == STATUS_TERMINATED + raise RuntimeError, "an error has occured" if @error != nil + + @status = STATUS_TERMINATED + @non_option_arguments.reverse_each do |argument| + ARGV.unshift(argument) + end + + @canonical_names = nil + @argument_flags = nil + @rest_singles = nil + @non_option_arguments = nil + + return self + end + + # + # Returns true if option processing has terminated, false otherwise. + # + def terminated? + return @status == STATUS_TERMINATED + end + + # + # Set an error (protected). + # + def set_error(type, message) + $deferr.print("#{$0}: #{message}\n") if !@quiet + + @error = type + @error_message = message + @canonical_names = nil + @argument_flags = nil + @rest_singles = nil + @non_option_arguments = nil + + raise type, message + end + protected :set_error + + # + # Examine whether an option processing is failed. + # + attr_reader :error + + # + # `error?' is an alias of `error'. + # + alias error? error + + # Return the appropriate error message in POSIX-defined format. + # If no error has occurred, returns nil. + # + def error_message + return @error_message + end + + # + # Get next option name and its argument, as an Array of two elements. + # + # The option name is always converted to the first (preferred) + # name given in the original options to GetoptLong.new. + # + # Example: ['--option', 'value'] + # + # Returns nil if the processing is complete (as determined by + # STATUS_TERMINATED). + # + def get + option_name, option_argument = nil, '' + + # + # Check status. + # + return nil if @error != nil + case @status + when STATUS_YET + @status = STATUS_STARTED + when STATUS_TERMINATED + return nil + end + + # + # Get next option argument. + # + if 0 < @rest_singles.length + argument = '-' + @rest_singles + elsif (ARGV.length == 0) + terminate + return nil + elsif @ordering == PERMUTE + while 0 < ARGV.length && ARGV[0] !~ /^-./ + @non_option_arguments.push(ARGV.shift) + end + if ARGV.length == 0 + terminate + return nil + end + argument = ARGV.shift + elsif @ordering == REQUIRE_ORDER + if (ARGV[0] !~ /^-./) + terminate + return nil + end + argument = ARGV.shift + else + argument = ARGV.shift + end + + # + # Check the special argument `--'. + # `--' indicates the end of the option list. + # + if argument == '--' && @rest_singles.length == 0 + terminate + return nil + end + + # + # Check for long and short options. + # + if argument =~ /^(--[^=]+)/ && @rest_singles.length == 0 + # + # This is a long style option, which start with `--'. + # + pattern = $1 + if @canonical_names.include?(pattern) + option_name = pattern + else + # + # The option `option_name' is not registered in `@canonical_names'. + # It may be an abbreviated. + # + match_count = 0 + @canonical_names.each_key do |key| + if key.index(pattern) == 0 + option_name = key + match_count += 1 + end + end + if 2 <= match_count + set_error(AmbigousOption, "option `#{argument}' is ambiguous") + elsif match_count == 0 + set_error(InvalidOption, "unrecognized option `#{argument}'") + end + end + + # + # Check an argument to the option. + # + if @argument_flags[option_name] == REQUIRED_ARGUMENT + if argument =~ /=(.*)$/ + option_argument = $1 + elsif 0 < ARGV.length + option_argument = ARGV.shift + else + set_error(MissingArgument, + "option `#{argument}' requires an argument") + end + elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT + if argument =~ /=(.*)$/ + option_argument = $1 + elsif 0 < ARGV.length && ARGV[0] !~ /^-./ + option_argument = ARGV.shift + else + option_argument = '' + end + elsif argument =~ /=(.*)$/ + set_error(NeedlessArgument, + "option `#{option_name}' doesn't allow an argument") + end + + elsif argument =~ /^(-(.))(.*)/ + # + # This is a short style option, which start with `-' (not `--'). + # Short options may be catenated (e.g. `-l -g' is equivalent to + # `-lg'). + # + option_name, ch, @rest_singles = $1, $2, $3 + + if @canonical_names.include?(option_name) + # + # The option `option_name' is found in `@canonical_names'. + # Check its argument. + # + if @argument_flags[option_name] == REQUIRED_ARGUMENT + if 0 < @rest_singles.length + option_argument = @rest_singles + @rest_singles = '' + elsif 0 < ARGV.length + option_argument = ARGV.shift + else + # 1003.2 specifies the format of this message. + set_error(MissingArgument, "option requires an argument -- #{ch}") + end + elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT + if 0 < @rest_singles.length + option_argument = @rest_singles + @rest_singles = '' + elsif 0 < ARGV.length && ARGV[0] !~ /^-./ + option_argument = ARGV.shift + else + option_argument = '' + end + end + else + # + # This is an invalid option. + # 1003.2 specifies the format of this message. + # + if ENV.include?('POSIXLY_CORRECT') + set_error(InvalidOption, "illegal option -- #{ch}") + else + set_error(InvalidOption, "invalid option -- #{ch}") + end + end + else + # + # This is a non-option argument. + # Only RETURN_IN_ORDER falled into here. + # + return '', argument + end + + return @canonical_names[option_name], option_argument + end + + # + # `get_option' is an alias of `get'. + # + alias get_option get + + # Iterator version of `get'. + # + # The block is called repeatedly with two arguments: + # The first is the option name. + # The second is the argument which followed it (if any). + # Example: ('--opt', 'value') + # + # The option name is always converted to the first (preferred) + # name given in the original options to GetoptLong.new. + # + def each + loop do + option_name, option_argument = get_option + break if option_name == nil + yield option_name, option_argument + end + end + + # + # `each_option' is an alias of `each'. + # + alias each_option each +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/getopts.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/getopts.rb new file mode 100644 index 0000000000..a857a42d23 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/getopts.rb @@ -0,0 +1,127 @@ +# +# getopts.rb - +# $Release Version: $ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Yasuo OHBA(SHL Japan Inc. Technology Dept.) +# +# -- +# this is obsolete; use getoptlong +# +# 2000-03-21 +# modified by Minero Aoki +# +# 2002-03-05 +# rewritten by Akinori MUSHA +# + +warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: getopts is deprecated after Ruby 1.8.1; use optparse instead" if caller[0] and $VERBOSE + +$RCS_ID=%q$Header$ + +# getopts is obsolete. Use GetoptLong. + +def getopts(single_options, *options) + boolopts = {} + valopts = {} + + # + # set defaults + # + single_options.scan(/.:?/) do |opt| + if opt.size == 1 + boolopts[opt] = false + else + valopts[opt[0, 1]] = nil + end + end if single_options + + options.each do |arg| + opt, val = arg.split(':', 2) + + if val + valopts[opt] = val.empty? ? nil : val + else + boolopts[opt] = false + end + end + + # + # scan + # + c = 0 + argv = ARGV + + while arg = argv.shift + case arg + when /\A--(.*)/ + if $1.empty? # xinit -- -bpp 24 + break + end + + opt, val = $1.split('=', 2) + + if opt.size == 1 + argv.unshift arg + return nil + elsif valopts.key? opt # imclean --src +trash + valopts[opt] = val || argv.shift or return nil + elsif boolopts.key? opt # ruby --verbose + boolopts[opt] = true + else + argv.unshift arg + return nil + end + + c += 1 + when /\A-(.+)/ + opts = $1 + + until opts.empty? + opt = opts.slice!(0, 1) + + if valopts.key? opt + val = opts + + if val.empty? # ruby -e 'p $:' + valopts[opt] = argv.shift or return nil + else # cc -ohello ... + valopts[opt] = val + end + + c += 1 + break + elsif boolopts.key? opt + boolopts[opt] = true # ruby -h + c += 1 + else + argv.unshift arg + return nil + end + end + else + argv.unshift arg + break + end + end + + # + # set + # + $OPT = {} + + boolopts.each do |opt, val| + $OPT[opt] = val + + sopt = opt.gsub(/[^A-Za-z0-9_]/, '_') + eval "$OPT_#{sopt} = val" + end + valopts.each do |opt, val| + $OPT[opt] = val + + sopt = opt.gsub(/[^A-Za-z0-9_]/, '_') + eval "$OPT_#{sopt} = val" + end + + c +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/gserver.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/gserver.rb new file mode 100644 index 0000000000..eb5f31b7b3 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/gserver.rb @@ -0,0 +1,253 @@ +# +# Copyright (C) 2001 John W. Small All Rights Reserved +# +# Author:: John W. Small +# Documentation:: Gavin Sinclair +# Licence:: Freeware. +# +# See the class GServer for documentation. +# + +require "socket" +require "thread" + +# +# GServer implements a generic server, featuring thread pool management, +# simple logging, and multi-server management. See HttpServer in +# xmlrpc/httpserver.rb in the Ruby standard library for an example of +# GServer in action. +# +# Any kind of application-level server can be implemented using this class. +# It accepts multiple simultaneous connections from clients, up to an optional +# maximum number. Several _services_ (i.e. one service per TCP port) can be +# run simultaneously, and stopped at any time through the class method +# GServer.stop(port). All the threading issues are handled, saving +# you the effort. All events are optionally logged, but you can provide your +# own event handlers if you wish. +# +# === Example +# +# Using GServer is simple. Below we implement a simple time server, run it, +# query it, and shut it down. Try this code in +irb+: +# +# require 'gserver' +# +# # +# # A server that returns the time in seconds since 1970. +# # +# class TimeServer < GServer +# def initialize(port=10001, *args) +# super(port, *args) +# end +# def serve(io) +# io.puts(Time.now.to_i) +# end +# end +# +# # Run the server with logging enabled (it's a separate thread). +# server = TimeServer.new +# server.audit = true # Turn logging on. +# server.start +# +# # *** Now point your browser to http://localhost:10001 to see it working *** +# +# # See if it's still running. +# GServer.in_service?(10001) # -> true +# server.stopped? # -> false +# +# # Shut the server down gracefully. +# server.shutdown +# +# # Alternatively, stop it immediately. +# GServer.stop(10001) +# # or, of course, "server.stop". +# +# All the business of accepting connections and exception handling is taken +# care of. All we have to do is implement the method that actually serves the +# client. +# +# === Advanced +# +# As the example above shows, the way to use GServer is to subclass it to +# create a specific server, overriding the +serve+ method. You can override +# other methods as well if you wish, perhaps to collect statistics, or emit +# more detailed logging. +# +# connecting +# disconnecting +# starting +# stopping +# +# The above methods are only called if auditing is enabled. +# +# You can also override +log+ and +error+ if, for example, you wish to use a +# more sophisticated logging system. +# +class GServer + + DEFAULT_HOST = "127.0.0.1" + + def serve(io) + end + + @@services = {} # Hash of opened ports, i.e. services + @@servicesMutex = Mutex.new + + def GServer.stop(port, host = DEFAULT_HOST) + @@servicesMutex.synchronize { + @@services[host][port].stop + } + end + + def GServer.in_service?(port, host = DEFAULT_HOST) + @@services.has_key?(host) and + @@services[host].has_key?(port) + end + + def stop + @connectionsMutex.synchronize { + if @tcpServerThread + @tcpServerThread.raise "stop" + end + } + end + + def stopped? + @tcpServerThread == nil + end + + def shutdown + @shutdown = true + end + + def connections + @connections.size + end + + def join + @tcpServerThread.join if @tcpServerThread + end + + attr_reader :port, :host, :maxConnections + attr_accessor :stdlog, :audit, :debug + + def connecting(client) + addr = client.peeraddr + log("#{self.class.to_s} #{@host}:#{@port} client:#{addr[1]} " + + "#{addr[2]}<#{addr[3]}> connect") + true + end + + def disconnecting(clientPort) + log("#{self.class.to_s} #{@host}:#{@port} " + + "client:#{clientPort} disconnect") + end + + protected :connecting, :disconnecting + + def starting() + log("#{self.class.to_s} #{@host}:#{@port} start") + end + + def stopping() + log("#{self.class.to_s} #{@host}:#{@port} stop") + end + + protected :starting, :stopping + + def error(detail) + log(detail.backtrace.join("\n")) + end + + def log(msg) + if @stdlog + @stdlog.puts("[#{Time.new.ctime}] %s" % msg) + @stdlog.flush + end + end + + protected :error, :log + + def initialize(port, host = DEFAULT_HOST, maxConnections = 4, + stdlog = $stderr, audit = false, debug = false) + @tcpServerThread = nil + @port = port + @host = host + @maxConnections = maxConnections + @connections = [] + @connectionsMutex = Mutex.new + @connectionsCV = ConditionVariable.new + @stdlog = stdlog + @audit = audit + @debug = debug + end + + def start(maxConnections = -1) + raise "running" if !stopped? + @shutdown = false + @maxConnections = maxConnections if maxConnections > 0 + @@servicesMutex.synchronize { + if GServer.in_service?(@port,@host) + raise "Port already in use: #{host}:#{@port}!" + end + @tcpServer = TCPServer.new(@host,@port) + @port = @tcpServer.addr[1] + @@services[@host] = {} unless @@services.has_key?(@host) + @@services[@host][@port] = self; + } + @tcpServerThread = Thread.new { + begin + starting if @audit + while !@shutdown + @connectionsMutex.synchronize { + while @connections.size >= @maxConnections + @connectionsCV.wait(@connectionsMutex) + end + } + client = @tcpServer.accept + @connections << Thread.new(client) { |myClient| + begin + myPort = myClient.peeraddr[1] + serve(myClient) if !@audit or connecting(myClient) + rescue => detail + error(detail) if @debug + ensure + begin + myClient.close + rescue + end + @connectionsMutex.synchronize { + @connections.delete(Thread.current) + @connectionsCV.signal + } + disconnecting(myPort) if @audit + end + } + end + rescue => detail + error(detail) if @debug + ensure + begin + @tcpServer.close + rescue + end + if @shutdown + @connectionsMutex.synchronize { + while @connections.size > 0 + @connectionsCV.wait(@connectionsMutex) + end + } + else + @connections.each { |c| c.raise "stop" } + end + @tcpServerThread = nil + @@servicesMutex.synchronize { + @@services[@host].delete(@port) + } + stopping if @audit + end + } + self + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/importenv.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/importenv.rb new file mode 100644 index 0000000000..8e1ba33b1d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/importenv.rb @@ -0,0 +1,33 @@ +# importenv.rb -- imports environment variables as global variables, Perlish ;( +# +# Usage: +# +# require 'importenv' +# p $USER +# $USER = "matz" +# p ENV["USER"] + +warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: importenv is deprecated after Ruby 1.8.1 (no replacement)" + +for k,v in ENV + next unless /^[a-zA-Z][_a-zA-Z0-9]*/ =~ k + eval <. +# All rights reserved. +# +# You can redistribute and/or modify it under the same terms as Ruby. +# +# $Id: ipaddr.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# +# TODO: +# - scope_id support +require 'socket' + +unless Socket.const_defined? "AF_INET6" + class Socket + AF_INET6 = Object.new + end + + class << IPSocket + def valid_v4?(addr) + if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr + return $~.captures.all? {|i| i.to_i < 256} + end + return false + end + + def valid_v6?(addr) + # IPv6 (normal) + return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr + return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr + return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr + # IPv6 (IPv4 compat) + return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_v4?($') + return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_v4?($') + return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_v4?($') + + false + end + + def valid?(addr) + valid_v4?(addr) || valid_v6?(addr) + end + + alias getaddress_orig getaddress + def getaddress(s) + if valid?(s) + s + elsif /\A[-A-Za-z\d.]+\Z/ =~ s + getaddress_orig(s) + else + raise ArgumentError, "invalid address" + end + end + end +end + +# IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and +# IPv6 are supported. +# +# == Example +# +# require 'ipaddr' +# +# ipaddr1 = IPAddr.new "3ffe:505:2::1" +# +# p ipaddr1 #=> # +# +# p ipaddr1.to_s #=> "3ffe:505:2::1" +# +# ipaddr2 = ipaddr1.mask(48) #=> # +# +# p ipaddr2.to_s #=> "3ffe:505:2::" +# +# ipaddr3 = IPAddr.new "192.168.2.0/24" +# +# p ipaddr3 #=> # + +class IPAddr + + IN4MASK = 0xffffffff + IN6MASK = 0xffffffffffffffffffffffffffffffff + IN6FORMAT = (["%.4x"] * 8).join(':') + + # Returns the address family of this IP address. + attr :family + + # Creates a new ipaddr containing the given network byte ordered + # string form of an IP address. + def IPAddr::new_ntoh(addr) + return IPAddr.new(IPAddr::ntop(addr)) + end + + # Convert a network byte ordered string form of an IP address into + # human readable form. + def IPAddr::ntop(addr) + case addr.size + when 4 + s = addr.unpack('C4').join('.') + when 16 + s = IN6FORMAT % addr.unpack('n8') + else + raise ArgumentError, "unsupported address family" + end + return s + end + + # Returns a new ipaddr built by bitwise AND. + def &(other) + return self.clone.set(@addr & other.to_i) + end + + # Returns a new ipaddr built by bitwise OR. + def |(other) + return self.clone.set(@addr | other.to_i) + end + + # Returns a new ipaddr built by bitwise right-shift. + def >>(num) + return self.clone.set(@addr >> num) + end + + # Returns a new ipaddr built by bitwise left shift. + def <<(num) + return self.clone.set(addr_mask(@addr << num)) + end + + # Returns a new ipaddr built by bitwise negation. + def ~ + return self.clone.set(addr_mask(~@addr)) + end + + # Returns true if two ipaddr are equal. + def ==(other) + if other.kind_of?(IPAddr) && @family != other.family + return false + end + return (@addr == other.to_i) + end + + # Returns a new ipaddr built by masking IP address with the given + # prefixlen/netmask. (e.g. 8, 64, "255.255.255.0", etc.) + def mask(prefixlen) + return self.clone.mask!(prefixlen) + end + + # Returns true if the given ipaddr is in the range. + # + # e.g.: + # require 'ipaddr' + # net1 = IPAddr.new("192.168.2.0/24") + # p net1.include?(IPAddr.new("192.168.2.0")) #=> true + # p net1.include?(IPAddr.new("192.168.2.255")) #=> true + # p net1.include?(IPAddr.new("192.168.3.0")) #=> false + def include?(other) + if ipv4_mapped? + if (@mask_addr >> 32) != 0xffffffffffffffffffffffff + return false + end + mask_addr = (@mask_addr & IN4MASK) + addr = (@addr & IN4MASK) + family = Socket::AF_INET + else + mask_addr = @mask_addr + addr = @addr + family = @family + end + if other.kind_of?(IPAddr) + if other.ipv4_mapped? + other_addr = (other.to_i & IN4MASK) + other_family = Socket::AF_INET + else + other_addr = other.to_i + other_family = other.family + end + else # Not IPAddr - assume integer in same family as us + other_addr = other.to_i + other_family = family + end + + if family != other_family + return false + end + return ((addr & mask_addr) == (other_addr & mask_addr)) + end + alias === include? + + # Returns the integer representation of the ipaddr. + def to_i + return @addr + end + + # Returns a string containing the IP address representation. + def to_s + str = to_string + return str if ipv4? + + str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1') + loop do + break if str.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::') + break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':') + break if str.sub!(/\b0:0:0:0:0:0\b/, ':') + break if str.sub!(/\b0:0:0:0:0\b/, ':') + break if str.sub!(/\b0:0:0:0\b/, ':') + break if str.sub!(/\b0:0:0\b/, ':') + break if str.sub!(/\b0:0\b/, ':') + break + end + str.sub!(/:{3,}/, '::') + + if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\Z/i =~ str + str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256) + end + + str + end + + # Returns a string containing the IP address representation in + # canonical form. + def to_string + return _to_string(@addr) + end + + # Returns a network byte ordered string form of the IP address. + def hton + case @family + when Socket::AF_INET + return [@addr].pack('N') + when Socket::AF_INET6 + return (0..7).map { |i| + (@addr >> (112 - 16 * i)) & 0xffff + }.pack('n8') + else + raise "unsupported address family" + end + end + + # Returns true if the ipaddr is an IPv4 address. + def ipv4? + return @family == Socket::AF_INET + end + + # Returns true if the ipaddr is an IPv6 address. + def ipv6? + return @family == Socket::AF_INET6 + end + + # Returns true if the ipaddr is an IPv4-mapped IPv6 address. + def ipv4_mapped? + return ipv6? && (@addr >> 32) == 0xffff + end + + # Returns true if the ipaddr is an IPv4-compatible IPv6 address. + def ipv4_compat? + if !ipv6? || (@addr >> 32) != 0 + return false + end + a = (@addr & IN4MASK) + return a != 0 && a != 1 + end + + # Returns a new ipaddr built by converting the native IPv4 address + # into an IPv4-mapped IPv6 address. + def ipv4_mapped + if !ipv4? + raise ArgumentError, "not an IPv4 address" + end + return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6) + end + + # Returns a new ipaddr built by converting the native IPv4 address + # into an IPv4-compatible IPv6 address. + def ipv4_compat + if !ipv4? + raise ArgumentError, "not an IPv4 address" + end + return self.clone.set(@addr, Socket::AF_INET6) + end + + # Returns a new ipaddr built by converting the IPv6 address into a + # native IPv4 address. If the IP address is not an IPv4-mapped or + # IPv4-compatible IPv6 address, returns self. + def native + if !ipv4_mapped? && !ipv4_compat? + return self + end + return self.clone.set(@addr & IN4MASK, Socket::AF_INET) + end + + # Returns a string for DNS reverse lookup. It returns a string in + # RFC3172 form for an IPv6 address. + def reverse + case @family + when Socket::AF_INET + return _reverse + ".in-addr.arpa" + when Socket::AF_INET6 + return ip6_arpa + else + raise "unsupported address family" + end + end + + # Returns a string for DNS reverse lookup compatible with RFC3172. + def ip6_arpa + if !ipv6? + raise ArgumentError, "not an IPv6 address" + end + return _reverse + ".ip6.arpa" + end + + # Returns a string for DNS reverse lookup compatible with RFC1886. + def ip6_int + if !ipv6? + raise ArgumentError, "not an IPv6 address" + end + return _reverse + ".ip6.int" + end + + # Returns a string containing a human-readable representation of the + # ipaddr. ("#") + def inspect + case @family + when Socket::AF_INET + af = "IPv4" + when Socket::AF_INET6 + af = "IPv6" + else + raise "unsupported address family" + end + return sprintf("#<%s: %s:%s/%s>", self.class.name, + af, _to_string(@addr), _to_string(@mask_addr)) + end + + protected + + def set(addr, *family) + case family[0] ? family[0] : @family + when Socket::AF_INET + if addr < 0 || addr > IN4MASK + raise ArgumentError, "invalid address" + end + when Socket::AF_INET6 + if addr < 0 || addr > IN6MASK + raise ArgumentError, "invalid address" + end + else + raise ArgumentError, "unsupported address family" + end + @addr = addr + if family[0] + @family = family[0] + end + return self + end + + def mask!(mask) + if mask.kind_of?(String) + if mask =~ /^\d+$/ + prefixlen = mask.to_i + else + m = IPAddr.new(mask) + if m.family != @family + raise ArgumentError, "address family is not same" + end + @mask_addr = m.to_i + @addr &= @mask_addr + return self + end + else + prefixlen = mask + end + case @family + when Socket::AF_INET + if prefixlen < 0 || prefixlen > 32 + raise ArgumentError, "invalid length" + end + masklen = 32 - prefixlen + @mask_addr = ((IN4MASK >> masklen) << masklen) + when Socket::AF_INET6 + if prefixlen < 0 || prefixlen > 128 + raise ArgumentError, "invalid length" + end + masklen = 128 - prefixlen + @mask_addr = ((IN6MASK >> masklen) << masklen) + else + raise "unsupported address family" + end + @addr = ((@addr >> masklen) << masklen) + return self + end + + private + + # Creates a new ipaddr containing the given human readable form of + # an IP address. It also accepts `address/prefixlen' and + # `address/mask'. When prefixlen or mask is specified, it returns a + # masked ipaddr. IPv6 address may beenclosed with `[' and `]'. + # + # Although an address family is determined automatically from a + # specified address, you can specify an address family explicitly by + # the optional second argument. + def initialize(addr = '::', family = Socket::AF_UNSPEC) + if !addr.kind_of?(String) + if family != Socket::AF_INET6 && family != Socket::AF_INET + raise ArgumentError, "unsupported address family" + end + set(addr, family) + @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK + return + end + prefix, prefixlen = addr.split('/') + if prefix =~ /^\[(.*)\]$/i + prefix = $1 + family = Socket::AF_INET6 + end + # It seems AI_NUMERICHOST doesn't do the job. + #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil, + # Socket::AI_NUMERICHOST) + begin + IPSocket.getaddress(prefix) # test if address is vaild + rescue + raise ArgumentError, "invalid address" + end + @addr = @family = nil + if family == Socket::AF_UNSPEC || family == Socket::AF_INET + @addr = in_addr(prefix) + if @addr + @family = Socket::AF_INET + end + end + if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6) + @addr = in6_addr(prefix) + @family = Socket::AF_INET6 + end + if family != Socket::AF_UNSPEC && @family != family + raise ArgumentError, "address family unmatch" + end + if prefixlen + mask!(prefixlen) + else + @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK + end + end + + def in_addr(addr) + if addr =~ /^\d+\.\d+\.\d+\.\d+$/ + n = 0 + addr.split('.').each { |i| + n <<= 8 + n += i.to_i + } + return n + end + return nil + end + + def in6_addr(left) + case left + when /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i + return in_addr($1) + 0xffff00000000 + when /^::(\d+\.\d+\.\d+\.\d+)$/i + return in_addr($1) + when /[^0-9a-f:]/i + raise ArgumentError, "invalid address" + when /^(.*)::(.*)$/ + left, right = $1, $2 + else + right = '' + end + l = left.split(':') + r = right.split(':') + rest = 8 - l.size - r.size + if rest < 0 + return nil + end + a = [l, Array.new(rest, '0'), r].flatten! + n = 0 + a.each { |i| + n <<= 16 + n += i.hex + } + return n + end + + def addr_mask(addr) + case @family + when Socket::AF_INET + addr &= IN4MASK + when Socket::AF_INET6 + addr &= IN6MASK + else + raise "unsupported address family" + end + return addr + end + + def _reverse + case @family + when Socket::AF_INET + return (0..3).map { |i| + (@addr >> (8 * i)) & 0xff + }.join('.') + when Socket::AF_INET6 + return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.') + else + raise "unsupported address family" + end + end + + def _to_string(addr) + case @family + when Socket::AF_INET + return (0..3).map { |i| + (addr >> (24 - 8 * i)) & 0xff + }.join('.') + when Socket::AF_INET6 + return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:')) + else + raise "unsupported address family" + end + end + +end + +if $0 == __FILE__ + eval DATA.read, nil, $0, __LINE__+4 +end + +__END__ + +require 'test/unit' +require 'test/unit/ui/console/testrunner' + +class TC_IPAddr < Test::Unit::TestCase + def test_s_new + assert_nothing_raised { + IPAddr.new("3FFE:505:ffff::/48") + IPAddr.new("0:0:0:1::") + IPAddr.new("2001:200:300::/48") + } + + a = IPAddr.new + assert_equal("::", a.to_s) + assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string) + assert_equal(Socket::AF_INET6, a.family) + + a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678") + assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s) + assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string) + assert_equal(Socket::AF_INET6, a.family) + + a = IPAddr.new("3ffe:505:2::/48") + assert_equal("3ffe:505:2::", a.to_s) + assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string) + assert_equal(Socket::AF_INET6, a.family) + assert_equal(false, a.ipv4?) + assert_equal(true, a.ipv6?) + assert_equal("#", a.inspect) + + a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::") + assert_equal("3ffe:505:2::", a.to_s) + assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string) + assert_equal(Socket::AF_INET6, a.family) + + a = IPAddr.new("0.0.0.0") + assert_equal("0.0.0.0", a.to_s) + assert_equal("0.0.0.0", a.to_string) + assert_equal(Socket::AF_INET, a.family) + + a = IPAddr.new("192.168.1.2") + assert_equal("192.168.1.2", a.to_s) + assert_equal("192.168.1.2", a.to_string) + assert_equal(Socket::AF_INET, a.family) + assert_equal(true, a.ipv4?) + assert_equal(false, a.ipv6?) + + a = IPAddr.new("192.168.1.2/24") + assert_equal("192.168.1.0", a.to_s) + assert_equal("192.168.1.0", a.to_string) + assert_equal(Socket::AF_INET, a.family) + assert_equal("#", a.inspect) + + a = IPAddr.new("192.168.1.2/255.255.255.0") + assert_equal("192.168.1.0", a.to_s) + assert_equal("192.168.1.0", a.to_string) + assert_equal(Socket::AF_INET, a.family) + + assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s) + assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s) + + assert_equal("2001:200:300::", IPAddr.new("[2001:200:300::]/48").to_s) + + [ + ["fe80::1%fxp0"], + ["::1/255.255.255.0"], + ["::1:192.168.1.2/120"], + [IPAddr.new("::1").to_i], + ["::ffff:192.168.1.2/120", Socket::AF_INET], + ["[192.168.1.2]/120"], + ].each { |args| + assert_raises(ArgumentError) { + IPAddr.new(*args) + } + } + end + + def test_s_new_ntoh + addr = '' + IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte { |c| + addr += sprintf("%02x", c) + } + assert_equal("123456789abcdef0123456789abcdef0", addr) + addr = '' + IPAddr.new("123.45.67.89").hton.each_byte { |c| + addr += sprintf("%02x", c) + } + assert_equal(sprintf("%02x%02x%02x%02x", 123, 45, 67, 89), addr) + a = IPAddr.new("3ffe:505:2::") + assert_equal("3ffe:505:2::", IPAddr.new_ntoh(a.hton).to_s) + a = IPAddr.new("192.168.2.1") + assert_equal("192.168.2.1", IPAddr.new_ntoh(a.hton).to_s) + end + + def test_ipv4_compat + a = IPAddr.new("::192.168.1.2") + assert_equal("::192.168.1.2", a.to_s) + assert_equal("0000:0000:0000:0000:0000:0000:c0a8:0102", a.to_string) + assert_equal(Socket::AF_INET6, a.family) + assert_equal(true, a.ipv4_compat?) + b = a.native + assert_equal("192.168.1.2", b.to_s) + assert_equal(Socket::AF_INET, b.family) + assert_equal(false, b.ipv4_compat?) + + a = IPAddr.new("192.168.1.2") + b = a.ipv4_compat + assert_equal("::192.168.1.2", b.to_s) + assert_equal(Socket::AF_INET6, b.family) + end + + def test_ipv4_mapped + a = IPAddr.new("::ffff:192.168.1.2") + assert_equal("::ffff:192.168.1.2", a.to_s) + assert_equal("0000:0000:0000:0000:0000:ffff:c0a8:0102", a.to_string) + assert_equal(Socket::AF_INET6, a.family) + assert_equal(true, a.ipv4_mapped?) + b = a.native + assert_equal("192.168.1.2", b.to_s) + assert_equal(Socket::AF_INET, b.family) + assert_equal(false, b.ipv4_mapped?) + + a = IPAddr.new("192.168.1.2") + b = a.ipv4_mapped + assert_equal("::ffff:192.168.1.2", b.to_s) + assert_equal(Socket::AF_INET6, b.family) + end + + def test_reverse + assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa", IPAddr.new("3ffe:505:2::f").reverse) + assert_equal("1.2.168.192.in-addr.arpa", IPAddr.new("192.168.2.1").reverse) + end + + def test_ip6_arpa + assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa", IPAddr.new("3ffe:505:2::f").ip6_arpa) + assert_raises(ArgumentError) { + IPAddr.new("192.168.2.1").ip6_arpa + } + end + + def test_ip6_int + assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.int", IPAddr.new("3ffe:505:2::f").ip6_int) + assert_raises(ArgumentError) { + IPAddr.new("192.168.2.1").ip6_int + } + end + + def test_to_s + assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string) + assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s) + end +end + +class TC_Operator < Test::Unit::TestCase + + IN6MASK32 = "ffff:ffff::" + IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" + + def setup + @in6_addr_any = IPAddr.new() + @a = IPAddr.new("3ffe:505:2::/48") + @b = IPAddr.new("0:0:0:1::") + @c = IPAddr.new(IN6MASK32) + end + alias set_up setup + + def test_or + assert_equal("3ffe:505:2:1::", (@a | @b).to_s) + a = @a + a |= @b + assert_equal("3ffe:505:2:1::", a.to_s) + assert_equal("3ffe:505:2::", @a.to_s) + assert_equal("3ffe:505:2:1::", + (@a | 0x00000000000000010000000000000000).to_s) + end + + def test_and + assert_equal("3ffe:505::", (@a & @c).to_s) + a = @a + a &= @c + assert_equal("3ffe:505::", a.to_s) + assert_equal("3ffe:505:2::", @a.to_s) + assert_equal("3ffe:505::", (@a & 0xffffffff000000000000000000000000).to_s) + end + + def test_shift_right + assert_equal("0:3ffe:505:2::", (@a >> 16).to_s) + a = @a + a >>= 16 + assert_equal("0:3ffe:505:2::", a.to_s) + assert_equal("3ffe:505:2::", @a.to_s) + end + + def test_shift_left + assert_equal("505:2::", (@a << 16).to_s) + a = @a + a <<= 16 + assert_equal("505:2::", a.to_s) + assert_equal("3ffe:505:2::", @a.to_s) + end + + def test_carrot + a = ~@in6_addr_any + assert_equal(IN6MASK128, a.to_s) + assert_equal("::", @in6_addr_any.to_s) + end + + def test_equal + assert_equal(true, @a == IPAddr.new("3ffe:505:2::")) + assert_equal(false, @a == IPAddr.new("3ffe:505:3::")) + assert_equal(true, @a != IPAddr.new("3ffe:505:3::")) + assert_equal(false, @a != IPAddr.new("3ffe:505:2::")) + end + + def test_mask + a = @a.mask(32) + assert_equal("3ffe:505::", a.to_s) + assert_equal("3ffe:505:2::", @a.to_s) + end + + def test_include? + assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::"))) + assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::1"))) + assert_equal(false, @a.include?(IPAddr.new("3ffe:505:3::"))) + net1 = IPAddr.new("192.168.2.0/24") + assert_equal(true, net1.include?(IPAddr.new("192.168.2.0"))) + assert_equal(true, net1.include?(IPAddr.new("192.168.2.255"))) + assert_equal(false, net1.include?(IPAddr.new("192.168.3.0"))) + # test with integer parameter + int = (192 << 24) + (168 << 16) + (2 << 8) + 13 + + assert_equal(true, net1.include?(int)) + assert_equal(false, net1.include?(int+255)) + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb.rb new file mode 100644 index 0000000000..9ad7925be0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb.rb @@ -0,0 +1,342 @@ +# +# irb.rb - irb main module +# $Release Version: 0.9.5 $ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require "e2mmap" + +require "irb/init" +require "irb/context" +require "irb/extend-command" +#require "irb/workspace" + +require "irb/ruby-lex" +require "irb/input-method" +require "irb/locale" + +STDOUT.sync = true + +module IRB + @RCS_ID='-$Id: irb.rb 11708 2007-02-12 23:01:19Z shyouhei $-' + + class Abort < Exception;end + + # + @CONF = {} + + def IRB.conf + @CONF + end + + # IRB version method + def IRB.version + if v = @CONF[:VERSION] then return v end + + require "irb/version" + rv = @RELEASE_VERSION.sub(/\.0/, "") + @CONF[:VERSION] = format("irb %s(%s)", rv, @LAST_UPDATE_DATE) + end + + def IRB.CurrentContext + IRB.conf[:MAIN_CONTEXT] + end + + # initialize IRB and start TOP_LEVEL irb + def IRB.start(ap_path = nil) + $0 = File::basename(ap_path, ".rb") if ap_path + + IRB.setup(ap_path) + + if @CONF[:SCRIPT] + irb = Irb.new(nil, @CONF[:SCRIPT]) + else + irb = Irb.new + end + + @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC] + @CONF[:MAIN_CONTEXT] = irb.context + + trap("SIGINT") do + irb.signal_handle + end + + catch(:IRB_EXIT) do + irb.eval_input + end +# print "\n" + end + + def IRB.irb_exit(irb, ret) + throw :IRB_EXIT, ret + end + + def IRB.irb_abort(irb, exception = Abort) + if defined? Thread + irb.context.thread.raise exception, "abort then interrupt!!" + else + raise exception, "abort then interrupt!!" + end + end + + # + # irb interpriter main routine + # + class Irb + def initialize(workspace = nil, input_method = nil, output_method = nil) + @context = Context.new(self, workspace, input_method, output_method) + @context.main.extend ExtendCommandBundle + @signal_status = :IN_IRB + + @scanner = RubyLex.new + @scanner.exception_on_syntax_error = false + end + attr_reader :context + attr_accessor :scanner + + def eval_input + @scanner.set_prompt do + |ltype, indent, continue, line_no| + if ltype + f = @context.prompt_s + elsif continue + f = @context.prompt_c + elsif indent > 0 + f = @context.prompt_n + else @context.prompt_i + f = @context.prompt_i + end + f = "" unless f + if @context.prompting? + @context.io.prompt = p = prompt(f, ltype, indent, line_no) + else + @context.io.prompt = p = "" + end + if @context.auto_indent_mode + unless ltype + ind = prompt(@context.prompt_i, ltype, indent, line_no)[/.*\z/].size + + indent * 2 - p.size + ind += 2 if continue + @context.io.prompt = p + " " * ind if ind > 0 + end + end + end + + @scanner.set_input(@context.io) do + signal_status(:IN_INPUT) do + if l = @context.io.gets + print l if @context.verbose? + else + if @context.ignore_eof? and @context.io.readable_atfer_eof? + l = "\n" + if @context.verbose? + printf "Use \"exit\" to leave %s\n", @context.ap_name + end + end + end + l + end + end + + @scanner.each_top_level_statement do |line, line_no| + signal_status(:IN_EVAL) do + begin + line.untaint + @context.evaluate(line, line_no) + output_value if @context.echo? + rescue StandardError, ScriptError, Abort + $! = RuntimeError.new("unknown exception raised") unless $! + print $!.class, ": ", $!, "\n" + if $@[0] =~ /irb(2)?(\/.*|-.*|\.rb)?:/ && $!.class.to_s !~ /^IRB/ + irb_bug = true + else + irb_bug = false + end + + messages = [] + lasts = [] + levels = 0 + for m in $@ + m = @context.workspace.filter_backtrace(m) unless irb_bug + if m + if messages.size < @context.back_trace_limit + messages.push "\tfrom "+m + else + lasts.push "\tfrom "+m + if lasts.size > @context.back_trace_limit + lasts.shift + levels += 1 + end + end + end + end + print messages.join("\n"), "\n" + unless lasts.empty? + printf "... %d levels...\n", levels if levels > 0 + print lasts.join("\n") + end + print "Maybe IRB bug!!\n" if irb_bug + end + if $SAFE > 2 + warn "Error: irb does not work for $SAFE level higher than 2" + exit 1 + end + end + end + end + + def suspend_name(path = nil, name = nil) + @context.irb_path, back_path = path, @context.irb_path if path + @context.irb_name, back_name = name, @context.irb_name if name + begin + yield back_path, back_name + ensure + @context.irb_path = back_path if path + @context.irb_name = back_name if name + end + end + + def suspend_workspace(workspace) + @context.workspace, back_workspace = workspace, @context.workspace + begin + yield back_workspace + ensure + @context.workspace = back_workspace + end + end + + def suspend_input_method(input_method) + back_io = @context.io + @context.instance_eval{@io = input_method} + begin + yield back_io + ensure + @context.instance_eval{@io = back_io} + end + end + + def suspend_context(context) + @context, back_context = context, @context + begin + yield back_context + ensure + @context = back_context + end + end + + def signal_handle + unless @context.ignore_sigint? + print "\nabort!!\n" if @context.verbose? + exit + end + + case @signal_status + when :IN_INPUT + print "^C\n" + raise RubyLex::TerminateLineInput + when :IN_EVAL + IRB.irb_abort(self) + when :IN_LOAD + IRB.irb_abort(self, LoadAbort) + when :IN_IRB + # ignore + else + # ignore other cases as well + end + end + + def signal_status(status) + return yield if @signal_status == :IN_LOAD + + signal_status_back = @signal_status + @signal_status = status + begin + yield + ensure + @signal_status = signal_status_back + end + end + + def prompt(prompt, ltype, indent, line_no) + p = prompt.dup + p.gsub!(/%([0-9]+)?([a-zA-Z])/) do + case $2 + when "N" + @context.irb_name + when "m" + @context.main.to_s + when "M" + @context.main.inspect + when "l" + ltype + when "i" + if $1 + format("%" + $1 + "d", indent) + else + indent.to_s + end + when "n" + if $1 + format("%" + $1 + "d", line_no) + else + line_no.to_s + end + when "%" + "%" + end + end + p + end + + def output_value + if @context.inspect? + printf @context.return_format, @context.last_value.inspect + else + printf @context.return_format, @context.last_value + end + end + + def inspect + ary = [] + for iv in instance_variables + case iv + when "@signal_status" + ary.push format("%s=:%s", iv, @signal_status.id2name) + when "@context" + ary.push format("%s=%s", iv, eval(iv).__to_s__) + else + ary.push format("%s=%s", iv, eval(iv)) + end + end + format("#<%s: %s>", self.class, ary.join(", ")) + end + end + + # Singleton method + def @CONF.inspect + IRB.version unless self[:VERSION] + + array = [] + for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name} + case k + when :MAIN_CONTEXT, :__TMP__EHV__ + array.push format("CONF[:%s]=...myself...", k.id2name) + when :PROMPT + s = v.collect{ + |kk, vv| + ss = vv.collect{|kkk, vvv| ":#{kkk.id2name}=>#{vvv.inspect}"} + format(":%s=>{%s}", kk.id2name, ss.join(", ")) + } + array.push format("CONF[:%s]={%s}", k.id2name, s.join(", ")) + else + array.push format("CONF[:%s]=%s", k.id2name, v.inspect) + end + end + array.join("\n") + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/cmd/chws.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/cmd/chws.rb new file mode 100644 index 0000000000..772d4da472 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/cmd/chws.rb @@ -0,0 +1,33 @@ +# +# change-ws.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +require "irb/cmd/nop.rb" +require "irb/ext/change-ws.rb" + +module IRB + module ExtendCommand + + class CurrentWorkingWorkspace", "==", "===", "=~", ">", ">=", ">>", + "[]", "[]=", "^",] + + def self.select_message(receiver, message, candidates) + candidates.grep(/^#{message}/).collect do |e| + case e + when /^[a-zA-Z_]/ + receiver + "." + e + when /^[0-9]/ + when *Operators + #receiver + " " + e + end + end + end + end +end + +if Readline.respond_to?("basic_word_break_characters=") + Readline.basic_word_break_characters= " \t\n\"\\'`><=;|&{(" +end +Readline.completion_append_character = nil +Readline.completion_proc = IRB::InputCompletor::CompletionProc diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/context.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/context.rb new file mode 100644 index 0000000000..de70945fd1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/context.rb @@ -0,0 +1,255 @@ +# +# irb/context.rb - irb context +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require "irb/workspace" + +module IRB + class Context + # + # Arguments: + # input_method: nil -- stdin or readline + # String -- File + # other -- using this as InputMethod + # + def initialize(irb, workspace = nil, input_method = nil, output_method = nil) + @irb = irb + if workspace + @workspace = workspace + else + @workspace = WorkSpace.new + end + @thread = Thread.current if defined? Thread +# @irb_level = 0 + + # copy of default configuration + @ap_name = IRB.conf[:AP_NAME] + @rc = IRB.conf[:RC] + @load_modules = IRB.conf[:LOAD_MODULES] + + @use_readline = IRB.conf[:USE_READLINE] + @inspect_mode = IRB.conf[:INSPECT_MODE] + + self.math_mode = IRB.conf[:MATH_MODE] if IRB.conf[:MATH_MODE] + self.use_tracer = IRB.conf[:USE_TRACER] if IRB.conf[:USE_TRACER] + self.use_loader = IRB.conf[:USE_LOADER] if IRB.conf[:USE_LOADER] + self.eval_history = IRB.conf[:EVAL_HISTORY] if IRB.conf[:EVAL_HISTORY] + + @ignore_sigint = IRB.conf[:IGNORE_SIGINT] + @ignore_eof = IRB.conf[:IGNORE_EOF] + + @back_trace_limit = IRB.conf[:BACK_TRACE_LIMIT] + + self.prompt_mode = IRB.conf[:PROMPT_MODE] + + if IRB.conf[:SINGLE_IRB] or !defined?(JobManager) + @irb_name = IRB.conf[:IRB_NAME] + else + @irb_name = "irb#"+IRB.JobManager.n_jobs.to_s + end + @irb_path = "(" + @irb_name + ")" + + case input_method + when nil + case use_readline? + when nil + if (defined?(ReadlineInputMethod) && STDIN.tty? && + IRB.conf[:PROMPT_MODE] != :INF_RUBY) + @io = ReadlineInputMethod.new + else + @io = StdioInputMethod.new + end + when false + @io = StdioInputMethod.new + when true + if defined?(ReadlineInputMethod) + @io = ReadlineInputMethod.new + else + @io = StdioInputMethod.new + end + end + + when String + @io = FileInputMethod.new(input_method) + @irb_name = File.basename(input_method) + @irb_path = input_method + else + @io = input_method + end + self.save_history = IRB.conf[:SAVE_HISTORY] if IRB.conf[:SAVE_HISTORY] + + if output_method + @output_method = output_method + else + @output_method = StdioOutputMethod.new + end + + @verbose = IRB.conf[:VERBOSE] + @echo = IRB.conf[:ECHO] + if @echo.nil? + @echo = true + end + @debug_level = IRB.conf[:DEBUG_LEVEL] + end + + def main + @workspace.main + end + + attr_reader :workspace_home + attr_accessor :workspace + attr_reader :thread + attr_accessor :io + + attr_accessor :irb + attr_accessor :ap_name + attr_accessor :rc + attr_accessor :load_modules + attr_accessor :irb_name + attr_accessor :irb_path + + attr_reader :use_readline + attr_reader :inspect_mode + + attr_reader :prompt_mode + attr_accessor :prompt_i + attr_accessor :prompt_s + attr_accessor :prompt_c + attr_accessor :prompt_n + attr_accessor :auto_indent_mode + attr_accessor :return_format + + attr_accessor :ignore_sigint + attr_accessor :ignore_eof + attr_accessor :echo + attr_accessor :verbose + attr_reader :debug_level + + attr_accessor :back_trace_limit + + alias use_readline? use_readline + alias rc? rc + alias ignore_sigint? ignore_sigint + alias ignore_eof? ignore_eof + alias echo? echo + + def verbose? + if @verbose.nil? + if defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod) + false + elsif !STDIN.tty? or @io.kind_of?(FileInputMethod) + true + else + false + end + end + end + + def prompting? + verbose? || (STDIN.tty? && @io.kind_of?(StdioInputMethod) || + (defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod))) + end + + attr_reader :last_value + + def set_last_value(value) + @last_value = value + @workspace.evaluate self, "_ = IRB.CurrentContext.last_value" + end + + attr_reader :irb_name + + def prompt_mode=(mode) + @prompt_mode = mode + pconf = IRB.conf[:PROMPT][mode] + @prompt_i = pconf[:PROMPT_I] + @prompt_s = pconf[:PROMPT_S] + @prompt_c = pconf[:PROMPT_C] + @prompt_n = pconf[:PROMPT_N] + @return_format = pconf[:RETURN] + if ai = pconf.include?(:AUTO_INDENT) + @auto_indent_mode = ai + else + @auto_indent_mode = IRB.conf[:AUTO_INDENT] + end + end + + def inspect? + @inspect_mode.nil? or @inspect_mode + end + + def file_input? + @io.class == FileInputMethod + end + + def inspect_mode=(opt) + if opt + @inspect_mode = opt + else + @inspect_mode = !@inspect_mode + end + print "Switch to#{unless @inspect_mode; ' non';end} inspect mode.\n" if verbose? + @inspect_mode + end + + def use_readline=(opt) + @use_readline = opt + print "use readline module\n" if @use_readline + end + + def debug_level=(value) + @debug_level = value + RubyLex.debug_level = value + SLex.debug_level = value + end + + def debug? + @debug_level > 0 + end + + def evaluate(line, line_no) + @line_no = line_no + set_last_value(@workspace.evaluate(self, line, irb_path, line_no)) +# @workspace.evaluate("_ = IRB.conf[:MAIN_CONTEXT]._") +# @_ = @workspace.evaluate(line, irb_path, line_no) + end + + alias __exit__ exit + def exit(ret = 0) + IRB.irb_exit(@irb, ret) + end + + NOPRINTING_IVARS = ["@last_value"] + NO_INSPECTING_IVARS = ["@irb", "@io"] + IDNAME_IVARS = ["@prompt_mode"] + + alias __inspect__ inspect + def inspect + array = [] + for ivar in instance_variables.sort{|e1, e2| e1 <=> e2} + name = ivar.sub(/^@(.*)$/){$1} + val = instance_eval(ivar) + case ivar + when *NOPRINTING_IVARS + array.push format("conf.%s=%s", name, "...") + when *NO_INSPECTING_IVARS + array.push format("conf.%s=%s", name, val.to_s) + when *IDNAME_IVARS + array.push format("conf.%s=:%s", name, val.id2name) + else + array.push format("conf.%s=%s", name, val.inspect) + end + end + array.join("\n") + end + alias __to_s__ to_s + alias to_s inspect + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/change-ws.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/change-ws.rb new file mode 100644 index 0000000000..0fbdd81388 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/change-ws.rb @@ -0,0 +1,62 @@ +# +# irb/ext/cb.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +module IRB + class Context + + def home_workspace + if defined? @home_workspace + @home_workspace + else + @home_workspace = @workspace + end + end + + def change_workspace(*_main) + if _main.empty? + @workspace = home_workspace + return main + end + + @workspace = WorkSpace.new(_main[0]) + + if !(class<= 0 + @contents.find{|no, val| no == idx}[1] + else + @contents[idx][1] + end + rescue NameError + nil + end + end + + def push(no, val) + @contents.push [no, val] + @contents.shift if @size != 0 && @contents.size > @size + end + + alias real_inspect inspect + + def inspect + if @contents.empty? + return real_inspect + end + + unless (last = @contents.pop)[1].equal?(self) + @contents.push last + last = nil + end + str = @contents.collect{|no, val| + if val.equal?(self) + "#{no} ...self-history..." + else + "#{no} #{val.inspect}" + end + }.join("\n") + if str == "" + str = "Empty." + end + @contents.push last if last + str + end + end +end + + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/loader.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/loader.rb new file mode 100644 index 0000000000..465a793dc2 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/loader.rb @@ -0,0 +1,120 @@ +# +# loader.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + + +module IRB + class LoadAbort < Exception;end + + module IrbLoader + @RCS_ID='-$Id: loader.rb 11708 2007-02-12 23:01:19Z shyouhei $-' + + alias ruby_load load + alias ruby_require require + + def irb_load(fn, priv = nil) + path = search_file_from_ruby_path(fn) + raise LoadError, "No such file to load -- #{fn}" unless path + + load_file(path, priv) + end + + def search_file_from_ruby_path(fn) + if /^#{Regexp.quote(File::Separator)}/ =~ fn + return fn if File.exist?(fn) + return nil + end + + for path in $: + if File.exist?(f = File.join(path, fn)) + return f + end + end + return nil + end + + def source_file(path) + irb.suspend_name(path, File.basename(path)) do + irb.suspend_input_method(FileInputMethod.new(path)) do + |back_io| + irb.signal_status(:IN_LOAD) do + if back_io.kind_of?(FileInputMethod) + irb.eval_input + else + begin + irb.eval_input + rescue LoadAbort + print "load abort!!\n" + end + end + end + end + end + end + + def load_file(path, priv = nil) + irb.suspend_name(path, File.basename(path)) do + + if priv + ws = WorkSpace.new(Module.new) + else + ws = WorkSpace.new + end + irb.suspend_workspace(ws) do + irb.suspend_input_method(FileInputMethod.new(path)) do + |back_io| + irb.signal_status(:IN_LOAD) do +# p irb.conf + if back_io.kind_of?(FileInputMethod) + irb.eval_input + else + begin + irb.eval_input + rescue LoadAbort + print "load abort!!\n" + end + end + end + end + end + end + end + + def old + back_io = @io + back_path = @irb_path + back_name = @irb_name + back_scanner = @irb.scanner + begin + @io = FileInputMethod.new(path) + @irb_name = File.basename(path) + @irb_path = path + @irb.signal_status(:IN_LOAD) do + if back_io.kind_of?(FileInputMethod) + @irb.eval_input + else + begin + @irb.eval_input + rescue LoadAbort + print "load abort!!\n" + end + end + end + ensure + @io = back_io + @irb_name = back_name + @irb_path = back_path + @irb.scanner = back_scanner + end + end + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/math-mode.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/math-mode.rb new file mode 100644 index 0000000000..ccef2a1288 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/math-mode.rb @@ -0,0 +1,37 @@ +# +# math-mode.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require "mathn" + +module IRB + class Context + attr_reader :math_mode + alias math? math_mode + + def math_mode=(opt) + if @math_mode == true && opt == false + IRB.fail CantReturnToNormalMode + return + end + + @math_mode = opt + if math_mode + main.extend Math + print "start math mode\n" if verbose? + end + end + + def inspect? + @inspect_mode.nil? && !@math_mode or @inspect_mode + end + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/multi-irb.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/multi-irb.rb new file mode 100644 index 0000000000..b0d0ed8db1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/multi-irb.rb @@ -0,0 +1,241 @@ +# +# irb/multi-irb.rb - multiple irb module +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +IRB.fail CantShiftToMultiIrbMode unless defined?(Thread) +require "thread" + +module IRB + # job management class + class JobManager + @RCS_ID='-$Id: multi-irb.rb 11708 2007-02-12 23:01:19Z shyouhei $-' + + def initialize + # @jobs = [[thread, irb],...] + @jobs = [] + @current_job = nil + end + + attr_accessor :current_job + + def n_jobs + @jobs.size + end + + def thread(key) + th, irb = search(key) + th + end + + def irb(key) + th, irb = search(key) + irb + end + + def main_thread + @jobs[0][0] + end + + def main_irb + @jobs[0][1] + end + + def insert(irb) + @jobs.push [Thread.current, irb] + end + + def switch(key) + th, irb = search(key) + IRB.fail IrbAlreadyDead unless th.alive? + IRB.fail IrbSwitchedToCurrentThread if th == Thread.current + @current_job = irb + th.run + Thread.stop + @current_job = irb(Thread.current) + end + + def kill(*keys) + for key in keys + th, irb = search(key) + IRB.fail IrbAlreadyDead unless th.alive? + th.exit + end + end + + def search(key) + case key + when Integer + @jobs[key] + when Irb + @jobs.find{|k, v| v.equal?(key)} + when Thread + @jobs.assoc(key) + else + assoc = @jobs.find{|k, v| v.context.main.equal?(key)} + IRB.fail NoSuchJob, key if assoc.nil? + assoc + end + end + + def delete(key) + case key + when Integer + IRB.fail NoSuchJob, key unless @jobs[key] + @jobs[key] = nil + else + catch(:EXISTS) do + @jobs.each_index do + |i| + if @jobs[i] and (@jobs[i][0] == key || + @jobs[i][1] == key || + @jobs[i][1].context.main.equal?(key)) + @jobs[i] = nil + throw :EXISTS + end + end + IRB.fail NoSuchJob, key + end + end + until assoc = @jobs.pop; end unless @jobs.empty? + @jobs.push assoc + end + + def inspect + ary = [] + @jobs.each_index do + |i| + th, irb = @jobs[i] + next if th.nil? + + if th.alive? + if th.stop? + t_status = "stop" + else + t_status = "running" + end + else + t_status = "exited" + end + ary.push format("#%d->%s on %s (%s: %s)", + i, + irb.context.irb_name, + irb.context.main, + th, + t_status) + end + ary.join("\n") + end + end + + @JobManager = JobManager.new + + def IRB.JobManager + @JobManager + end + + def IRB.CurrentContext + IRB.JobManager.irb(Thread.current).context + end + + # invoke multi-irb + def IRB.irb(file = nil, *main) + workspace = WorkSpace.new(*main) + parent_thread = Thread.current + Thread.start do + begin + irb = Irb.new(workspace, file) + rescue + print "Subirb can't start with context(self): ", workspace.main.inspect, "\n" + print "return to main irb\n" + Thread.pass + Thread.main.wakeup + Thread.exit + end + @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC] + @JobManager.insert(irb) + @JobManager.current_job = irb + begin + system_exit = false + catch(:IRB_EXIT) do + irb.eval_input + end + rescue SystemExit + system_exit = true + raise + #fail + ensure + unless system_exit + @JobManager.delete(irb) + if parent_thread.alive? + @JobManager.current_job = @JobManager.irb(parent_thread) + parent_thread.run + else + @JobManager.current_job = @JobManager.main_irb + @JobManager.main_thread.run + end + end + end + end + Thread.stop + @JobManager.current_job = @JobManager.irb(Thread.current) + end + +# class Context +# def set_last_value(value) +# @last_value = value +# @workspace.evaluate "_ = IRB.JobManager.irb(Thread.current).context.last_value" +# if @eval_history #and !@__.equal?(@last_value) +# @eval_history_values.push @line_no, @last_value +# @workspace.evaluate "__ = IRB.JobManager.irb(Thread.current).context.instance_eval{@eval_history_values}" +# end +# @last_value +# end +# end + +# module ExtendCommand +# def irb_context +# IRB.JobManager.irb(Thread.current).context +# end +# # alias conf irb_context +# end + + @CONF[:SINGLE_IRB_MODE] = false + @JobManager.insert(@CONF[:MAIN_CONTEXT].irb) + @JobManager.current_job = @CONF[:MAIN_CONTEXT].irb + + class Irb + def signal_handle + unless @context.ignore_sigint? + print "\nabort!!\n" if @context.verbose? + exit + end + + case @signal_status + when :IN_INPUT + print "^C\n" + IRB.JobManager.thread(self).raise RubyLex::TerminateLineInput + when :IN_EVAL + IRB.irb_abort(self) + when :IN_LOAD + IRB.irb_abort(self, LoadAbort) + when :IN_IRB + # ignore + else + # ignore other cases as well + end + end + end + + trap("SIGINT") do + @JobManager.current_job.signal_handle + Thread.stop + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/save-history.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/save-history.rb new file mode 100644 index 0000000000..f615ea2485 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/save-history.rb @@ -0,0 +1,85 @@ +#!/usr/local/bin/ruby +# +# save-history.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKAkeiju@ruby-lang.org) +# +# -- +# +# +# + +require "readline" + +module IRB + module HistorySavingAbility + @RCS_ID='-$Id: save-history.rb 11708 2007-02-12 23:01:19Z shyouhei $-' + end + + class Context + def init_save_history + unless (class<<@io;self;end).include?(HistorySavingAbility) + @io.extend(HistorySavingAbility) + end + end + + def save_history + IRB.conf[:SAVE_HISTORY] + end + + def save_history=(val) + IRB.conf[:SAVE_HISTORY] = val + if val + main_context = IRB.conf[:MAIN_CONTEXT] + main_context = self unless main_context + main_context.init_save_history + end + end + + def history_file + IRB.conf[:HISTORY_FILE] + end + + def history_file=(hist) + IRB.conf[:HISTORY_FILE] = hist + end + end + + module HistorySavingAbility + include Readline + + def HistorySavingAbility.create_finalizer + proc do + if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) > 0 + if hf = IRB.conf[:HISTORY_FILE] + file = File.expand_path(hf) + end + file = IRB.rc_file("_history") unless file + open(file, 'w' ) do |f| + hist = HISTORY.to_a + f.puts(hist[-num..-1] || hist) + end + end + end + end + + def HistorySavingAbility.extended(obj) + ObjectSpace.define_finalizer(obj, HistorySavingAbility.create_finalizer) + obj.load_history + obj + end + + def load_history + hist = IRB.conf[:HISTORY_FILE] + hist = IRB.rc_file("_history") unless hist + if File.exist?(hist) + open(hist) do |f| + f.each {|l| HISTORY << l.chomp} + end + end + end + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/tracer.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/tracer.rb new file mode 100644 index 0000000000..a860ffd9e2 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/tracer.rb @@ -0,0 +1,61 @@ +# +# irb/lib/tracer.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require "tracer" + +module IRB + + # initialize tracing function + def IRB.initialize_tracer + Tracer.verbose = false + Tracer.add_filter { + |event, file, line, id, binding, *rests| + /^#{Regexp.quote(@CONF[:IRB_LIB_PATH])}/ !~ file and + File::basename(file) != "irb.rb" + } + end + + class Context + attr_reader :use_tracer + alias use_tracer? use_tracer + + def use_tracer=(opt) + if opt + Tracer.set_get_line_procs(@irb_path) { + |line_no, *rests| + @io.line(line_no) + } + elsif !opt && @use_tracer + Tracer.off + end + @use_tracer=opt + end + end + + class WorkSpace + alias __evaluate__ evaluate + def evaluate(context, statements, file = nil, line = nil) + if context.use_tracer? && file != nil && line != nil + Tracer.on + begin + __evaluate__(context, statements, file, line) + ensure + Tracer.off + end + else + __evaluate__(context, statements, file || __FILE__, line || __LINE__) + end + end + end + + IRB.initialize_tracer +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/use-loader.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/use-loader.rb new file mode 100644 index 0000000000..a709819911 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/use-loader.rb @@ -0,0 +1,65 @@ +# +# use-loader.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +require "irb/cmd/load" +require "irb/ext/loader" + +class Object + alias __original__load__IRB_use_loader__ load + alias __original__require__IRB_use_loader__ require +end + +module IRB + module ExtendCommandBundle + def irb_load(*opts, &b) + ExtendCommand::Load.execute(irb_context, *opts, &b) + end + def irb_require(*opts, &b) + ExtendCommand::Require.execute(irb_context, *opts, &b) + end + end + + class Context + + IRB.conf[:USE_LOADER] = false + + def use_loader + IRB.conf[:USE_LOADER] + end + + alias use_loader? use_loader + + def use_loader=(opt) + + if IRB.conf[:USE_LOADER] != opt + IRB.conf[:USE_LOADER] = opt + if opt + if !$".include?("irb/cmd/load") + end + (class<<@workspace.main;self;end).instance_eval { + alias_method :load, :irb_load + alias_method :require, :irb_require + } + else + (class<<@workspace.main;self;end).instance_eval { + alias_method :load, :__original__load__IRB_use_loader__ + alias_method :require, :__original__require__IRB_use_loader__ + } + end + end + print "Switch to load/require#{unless use_loader; ' non';end} trace mode.\n" if verbose? + opt + end + end +end + + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/workspaces.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/workspaces.rb new file mode 100644 index 0000000000..7bb6411a05 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ext/workspaces.rb @@ -0,0 +1,56 @@ +# +# push-ws.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +module IRB + class Context + + def irb_level + workspace_stack.size + end + + def workspaces + if defined? @workspaces + @workspaces + else + @workspaces = [] + end + end + + def push_workspace(*_main) + if _main.empty? + if workspaces.empty? + print "No other workspace\n" + return nil + end + ws = workspaces.pop + workspaces.push @workspace + @workspace = ws + return workspaces + end + + workspaces.push @workspace + @workspace = WorkSpace.new(@workspace.binding, _main[0]) + if !(class< + def new_alias_name(name, prefix = "__alias_of__", postfix = "__") + base_name = "#{prefix}#{name}#{postfix}" + all_methods = instance_methods(true) + private_instance_methods(true) + same_methods = all_methods.grep(/^#{Regexp.quote(base_name)}[0-9]*$/) + return base_name if same_methods.empty? + no = same_methods.size + while !same_methods.include?(alias_name = base_name + no) + no += 1 + end + alias_name + end + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/frame.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/frame.rb new file mode 100644 index 0000000000..34bf103b45 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/frame.rb @@ -0,0 +1,67 @@ +# +# frame.rb - +# $Release Version: 0.9$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd) +# +# -- +# +# +# + +require "e2mmap" + +module IRB + class Frame + extend Exception2MessageMapper + def_exception :FrameOverflow, "frame overflow" + def_exception :FrameUnderflow, "frame underflow" + + INIT_STACK_TIMES = 3 + CALL_STACK_OFFSET = 3 + + def initialize + @frames = [TOPLEVEL_BINDING] * INIT_STACK_TIMES + end + + def trace_func(event, file, line, id, binding) + case event + when 'call', 'class' + @frames.push binding + when 'return', 'end' + @frames.pop + end + end + + def top(n = 0) + bind = @frames[-(n + CALL_STACK_OFFSET)] + Fail FrameUnderflow unless bind + bind + end + + def bottom(n = 0) + bind = @frames[n] + Fail FrameOverflow unless bind + bind + end + + # singleton functions + def Frame.bottom(n = 0) + @backtrace.bottom(n) + end + + def Frame.top(n = 0) + @backtrace.top(n) + end + + def Frame.sender + eval "self", @backtrace.top + end + + @backtrace = Frame.new + set_trace_func proc{|event, file, line, id, binding, klass| + @backtrace.trace_func(event, file, line, id, binding) + } + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/help.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/help.rb new file mode 100644 index 0000000000..9a32fd859d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/help.rb @@ -0,0 +1,33 @@ +# +# irb/help.rb - print usase module +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ishitsuka.com) +# +# -- +# +# +# + +module IRB + def IRB.print_usage + lc = IRB.conf[:LC_MESSAGES] + path = lc.find("irb/help-message") + space_line = false + File.foreach(path) do + |l| + if /^\s*$/ =~ l + lc.puts l unless space_line + space_line = true + next + end + space_line = false + + l.sub!(/#.*$/, "") + next if /^\s*$/ =~ l + lc.puts l + end + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/init.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/init.rb new file mode 100644 index 0000000000..60974cff00 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/init.rb @@ -0,0 +1,259 @@ +# +# irb/init.rb - irb initialize module +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +module IRB + + # initialize config + def IRB.setup(ap_path) + IRB.init_config(ap_path) + IRB.init_error + IRB.parse_opts + IRB.run_config + IRB.load_modules + + unless @CONF[:PROMPT][@CONF[:PROMPT_MODE]] + IRB.fail(UndefinedPromptMode, @CONF[:PROMPT_MODE]) + end + end + + # @CONF default setting + def IRB.init_config(ap_path) + # class instance variables + @TRACER_INITIALIZED = false + + # default configurations + unless ap_path and @CONF[:AP_NAME] + ap_path = File.join(File.dirname(File.dirname(__FILE__)), "irb.rb") + end + @CONF[:AP_NAME] = File::basename(ap_path, ".rb") + + @CONF[:IRB_NAME] = "irb" + @CONF[:IRB_LIB_PATH] = File.dirname(__FILE__) + + @CONF[:RC] = true + @CONF[:LOAD_MODULES] = [] + @CONF[:IRB_RC] = nil + + @CONF[:MATH_MODE] = false + @CONF[:USE_READLINE] = false unless defined?(ReadlineInputMethod) + @CONF[:INSPECT_MODE] = nil + @CONF[:USE_TRACER] = false + @CONF[:USE_LOADER] = false + @CONF[:IGNORE_SIGINT] = true + @CONF[:IGNORE_EOF] = false + @CONF[:ECHO] = nil + @CONF[:VERBOSE] = nil + + @CONF[:EVAL_HISTORY] = nil + @CONF[:SAVE_HISTORY] = nil + + @CONF[:BACK_TRACE_LIMIT] = 16 + + @CONF[:PROMPT] = { + :NULL => { + :PROMPT_I => nil, + :PROMPT_N => nil, + :PROMPT_S => nil, + :PROMPT_C => nil, + :RETURN => "%s\n" + }, + :DEFAULT => { + :PROMPT_I => "%N(%m):%03n:%i> ", + :PROMPT_N => "%N(%m):%03n:%i> ", + :PROMPT_S => "%N(%m):%03n:%i%l ", + :PROMPT_C => "%N(%m):%03n:%i* ", + :RETURN => "=> %s\n" + }, + :CLASSIC => { + :PROMPT_I => "%N(%m):%03n:%i> ", + :PROMPT_N => "%N(%m):%03n:%i> ", + :PROMPT_S => "%N(%m):%03n:%i%l ", + :PROMPT_C => "%N(%m):%03n:%i* ", + :RETURN => "%s\n" + }, + :SIMPLE => { + :PROMPT_I => ">> ", + :PROMPT_N => ">> ", + :PROMPT_S => nil, + :PROMPT_C => "?> ", + :RETURN => "=> %s\n" + }, + :INF_RUBY => { + :PROMPT_I => "%N(%m):%03n:%i> ", +# :PROMPT_N => "%N(%m):%03n:%i> ", + :PROMPT_N => nil, + :PROMPT_S => nil, + :PROMPT_C => nil, + :RETURN => "%s\n", + :AUTO_INDENT => true + }, + :XMP => { + :PROMPT_I => nil, + :PROMPT_N => nil, + :PROMPT_S => nil, + :PROMPT_C => nil, + :RETURN => " ==>%s\n" + } + } + + @CONF[:PROMPT_MODE] = (STDIN.tty? ? :DEFAULT : :NULL) + @CONF[:AUTO_INDENT] = false + + @CONF[:CONTEXT_MODE] = 3 # use binding in function on TOPLEVEL_BINDING + @CONF[:SINGLE_IRB] = false + +# @CONF[:LC_MESSAGES] = "en" + @CONF[:LC_MESSAGES] = Locale.new + + @CONF[:DEBUG_LEVEL] = 1 + end + + def IRB.init_error + @CONF[:LC_MESSAGES].load("irb/error.rb") + end + + FEATURE_IOPT_CHANGE_VERSION = "1.9.0" + + # option analyzing + def IRB.parse_opts + load_path = [] + while opt = ARGV.shift + case opt + when "-f" + @CONF[:RC] = false + when "-m" + @CONF[:MATH_MODE] = true + when "-d" + $DEBUG = true + when /^-r(.+)?/ + opt = $1 || ARGV.shift + @CONF[:LOAD_MODULES].push opt if opt + when /^-I(.+)?/ + opt = $1 || ARGV.shift + load_path.concat(opt.split(File::PATH_SEPARATOR)) if opt + when /^-K(.)/ + $KCODE = $1 + when "--inspect" + @CONF[:INSPECT_MODE] = true + when "--noinspect" + @CONF[:INSPECT_MODE] = false + when "--readline" + @CONF[:USE_READLINE] = true + when "--noreadline" + @CONF[:USE_READLINE] = false + when "--echo" + @CONF[:ECHO] = true + when "--noecho" + @CONF[:ECHO] = false + when "--verbose" + @CONF[:VERBOSE] = true + when "--noverbose" + @CONF[:VERBOSE] = false + when "--prompt-mode", "--prompt" + prompt_mode = ARGV.shift.upcase.tr("-", "_").intern + @CONF[:PROMPT_MODE] = prompt_mode + when "--noprompt" + @CONF[:PROMPT_MODE] = :NULL + when "--inf-ruby-mode" + @CONF[:PROMPT_MODE] = :INF_RUBY + when "--sample-book-mode", "--simple-prompt" + @CONF[:PROMPT_MODE] = :SIMPLE + when "--tracer" + @CONF[:USE_TRACER] = true + when "--back-trace-limit" + @CONF[:BACK_TRACE_LIMIT] = ARGV.shift.to_i + when "--context-mode" + @CONF[:CONTEXT_MODE] = ARGV.shift.to_i + when "--single-irb" + @CONF[:SINGLE_IRB] = true + when "--irb_debug" + @CONF[:DEBUG_LEVEL] = ARGV.shift.to_i + when "-v", "--version" + print IRB.version, "\n" + exit 0 + when "-h", "--help" + require "irb/help" + IRB.print_usage + exit 0 + when /^-/ + IRB.fail UnrecognizedSwitch, opt + else + @CONF[:SCRIPT] = opt + $0 = opt + break + end + end + if RUBY_VERSION >= FEATURE_IOPT_CHANGE_VERSION + load_path.collect! do |path| + /\A\.\// =~ path ? path : File.expand_path(path) + end + end + $LOAD_PATH.unshift(*load_path) + end + + # running config + def IRB.run_config + if @CONF[:RC] + begin + load rc_file + rescue LoadError, Errno::ENOENT + rescue + print "load error: #{rc_file}\n" + print $!.class, ": ", $!, "\n" + for err in $@[0, $@.size - 2] + print "\t", err, "\n" + end + end + end + end + + IRBRC_EXT = "rc" + def IRB.rc_file(ext = IRBRC_EXT) + if !@CONF[:RC_NAME_GENERATOR] + rc_file_generators do |rcgen| + @CONF[:RC_NAME_GENERATOR] ||= rcgen + if File.exist?(rcgen.call(IRBRC_EXT)) + @CONF[:RC_NAME_GENERATOR] = rcgen + break + end + end + end + @CONF[:RC_NAME_GENERATOR].call ext + end + + # enumerate possible rc-file base name generators + def IRB.rc_file_generators + if irbrc = ENV["IRBRC"] + yield proc{|rc| rc == "rc" ? irbrc : irbrc+rc} + end + if home = ENV["HOME"] + yield proc{|rc| home+"/.irb#{rc}"} + end + home = Dir.pwd + yield proc{|rc| home+"/.irb#{rc}"} + yield proc{|rc| home+"/irb#{rc.sub(/\A_?/, '.')}"} + yield proc{|rc| home+"/_irb#{rc}"} + yield proc{|rc| home+"/$irb#{rc}"} + end + + # loading modules + def IRB.load_modules + for m in @CONF[:LOAD_MODULES] + begin + require m + rescue + print $@[0], ":", $!.class, ": ", $!, "\n" + end + end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/input-method.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/input-method.rb new file mode 100644 index 0000000000..2e50499005 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/input-method.rb @@ -0,0 +1,120 @@ +# +# irb/input-method.rb - input methods used irb +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +module IRB + # + # InputMethod + # StdioInputMethod + # FileInputMethod + # (ReadlineInputMethod) + # + STDIN_FILE_NAME = "(line)" + class InputMethod + @RCS_ID='-$Id: input-method.rb 11708 2007-02-12 23:01:19Z shyouhei $-' + + def initialize(file = STDIN_FILE_NAME) + @file_name = file + end + attr_reader :file_name + + attr_accessor :prompt + + def gets + IRB.fail NotImplementedError, "gets" + end + public :gets + + def readable_atfer_eof? + false + end + end + + class StdioInputMethod < InputMethod + def initialize + super + @line_no = 0 + @line = [] + end + + def gets + print @prompt + @line[@line_no += 1] = $stdin.gets + end + + def eof? + $stdin.eof? + end + + def readable_atfer_eof? + true + end + + def line(line_no) + @line[line_no] + end + end + + class FileInputMethod < InputMethod + def initialize(file) + super + @io = open(file) + end + attr_reader :file_name + + def eof? + @io.eof? + end + + def gets + print @prompt + l = @io.gets +# print @prompt, l + l + end + end + + begin + require "readline" + class ReadlineInputMethod < InputMethod + include Readline + def initialize + super + + @line_no = 0 + @line = [] + @eof = false + end + + def gets + if l = readline(@prompt, false) + HISTORY.push(l) if !l.empty? + @line[@line_no += 1] = l + "\n" + else + @eof = true + l + end + end + + def eof? + @eof + end + + def readable_atfer_eof? + true + end + + def line(line_no) + @line[line_no] + end + end + rescue LoadError + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/lc/error.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/lc/error.rb new file mode 100644 index 0000000000..8054f59ac4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/lc/error.rb @@ -0,0 +1,30 @@ +# +# irb/lc/error.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require "e2mmap" + +module IRB + + # exceptions + extend Exception2MessageMapper + def_exception :UnrecognizedSwitch, "Unrecognized switch: %s" + def_exception :NotImplementedError, "Need to define `%s'" + def_exception :CantReturnToNormalMode, "Can't return to normal mode." + def_exception :IllegalParameter, "Illegal parameter(%s)." + def_exception :IrbAlreadyDead, "Irb is already dead." + def_exception :IrbSwitchedToCurrentThread, "Switched to current thread." + def_exception :NoSuchJob, "No such job(%s)." + def_exception :CantShiftToMultiIrbMode, "Can't shift to multi irb mode." + def_exception :CantChangeBinding, "Can't change binding to (%s)." + def_exception :UndefinedPromptMode, "Undefined prompt mode(%s)." + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/lc/help-message b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/lc/help-message new file mode 100644 index 0000000000..5188ff3ca8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/lc/help-message @@ -0,0 +1,35 @@ +# +# irb/lc/help-message.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +Usage: irb.rb [options] [programfile] [arguments] + -f Suppress read of ~/.irbrc + -m Bc mode (load mathn, fraction or matrix are available) + -d Set $DEBUG to true (same as `ruby -d') + -r load-module Same as `ruby -r' + -I path Specify $LOAD_PATH directory + --inspect Use `inspect' for output (default except for bc mode) + --noinspect Don't use inspect for output + --readline Use Readline extension module + --noreadline Don't use Readline extension module + --prompt prompt-mode + --prompt-mode prompt-mode + Switch prompt mode. Pre-defined prompt modes are + `default', `simple', `xmp' and `inf-ruby' + --inf-ruby-mode Use prompt appropriate for inf-ruby-mode on emacs. + Suppresses --readline. + --simple-prompt Simple prompt mode + --noprompt No prompt mode + --tracer Display trace for each execution of commands. + --back-trace-limit n + Display backtrace top n and tail n. The default + value is 16. + --irb_debug n Set internal debug level to n (not for popular use) + -v, --version Print the version of irb diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/lc/ja/error.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/lc/ja/error.rb new file mode 100644 index 0000000000..b8652b5676 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/lc/ja/error.rb @@ -0,0 +1,27 @@ +# +# irb/lc/ja/error.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require "e2mmap" + +module IRB + # exceptions + extend Exception2MessageMapper + def_exception :UnrecognizedSwitch, '$B%9%$%C%A(B(%s)$B$,J,$j$^$;$s(B' + def_exception :NotImplementedError, '`%s\'$B$NDj5A$,I,MW$G$9(B' + def_exception :CantReturnToNormalMode, 'Normal$B%b!<%I$KLa$l$^$;$s(B.' + def_exception :IllegalParameter, '$B%Q%i%a!<%?(B(%s)$B$,4V0c$C$F$$$^$9(B.' + def_exception :IrbAlreadyDead, 'Irb$B$O4{$K;`$s$G$$$^$9(B.' + def_exception :IrbSwitchedToCurrentThread, '$B%+%l%s%H%9%l%C%I$K@Z$jBX$o$j$^$7$?(B.' + def_exception :NoSuchJob, '$B$=$N$h$&$J%8%g%V(B(%s)$B$O$"$j$^$;$s(B.' + def_exception :CantShiftToMultiIrbMode, 'multi-irb mode$B$K0\$l$^$;$s(B.' + def_exception :CantChangeBinding, '$B%P%$%s%G%#%s%0(B(%s)$B$KJQ99$G$-$^$;$s(B.' + def_exception :UndefinedPromptMode, '$B%W%m%s%W%H%b!<%I(B(%s)$B$ODj5A$5$l$F$$$^$;$s(B.' +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/lc/ja/help-message b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/lc/ja/help-message new file mode 100644 index 0000000000..719796cea9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/lc/ja/help-message @@ -0,0 +1,36 @@ +# +# irb/lc/ja/help-message.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +Usage: irb.rb [options] [programfile] [arguments] + -f ~/.irbrc $B$rFI$_9~$^$J$$(B. + -m bc$B%b!<%I(B($BJ,?t(B, $B9TNs$N7W;;$,$G$-$k(B) + -d $DEBUG $B$r(Btrue$B$K$9$k(B(ruby -d $B$HF1$8(B) + -r load-module ruby -r $B$HF1$8(B. + -I path $LOAD_PATH $B$K(B path $B$rDI2C$9$k(B. + --inspect $B7k2L=PNO$K(Binspect$B$rMQ$$$k(B(bc$B%b!<%I0J30$O%G%U%)%k%H(B). + --noinspect $B7k2L=PNO$K(Binspect$B$rMQ$$$J$$(B. + --readline readline$B%i%$%V%i%j$rMxMQ$9$k(B. + --noreadline readline$B%i%$%V%i%j$rMxMQ$7$J$$(B. + --prompt prompt-mode/--prompt-mode prompt-mode + $B%W%m%s%W%H%b!<%I$r@ZBX$($^$9(B. $B8=:_Dj5A$5$l$F$$$k%W(B + $B%m%s%W%H%b!<%I$O(B, default, simple, xmp, inf-ruby$B$,(B + $BMQ0U$5$l$F$$$^$9(B. + --inf-ruby-mode emacs$B$N(Binf-ruby-mode$BMQ$N%W%m%s%W%HI=<($r9T$J$&(B. $BFC(B + $B$K;XDj$,$J$$8B$j(B, readline$B%i%$%V%i%j$O;H$o$J$/$J$k(B. + --simple-prompt $BHs>o$K%7%s%W%k$J%W%m%s%W%H$rMQ$$$k%b!<%I$G$9(B. + --noprompt $B%W%m%s%W%HI=<($r9T$J$o$J$$(B. + --tracer $B%3%^%s%I(other) + @level <=> other.level + end + + def notify? + @base_notifier.level >= self + end + end + + class NoMsgNotifier [#0- +] + # <Ǿե> (\*|\*[1-9][0-9]*\$|[1-9][0-9]*) + # <>.(\*|\*[1-9][0-9]*\$|[1-9][0-9]*|)? + # #<Ĺʸ>(hh|h|l|ll|L|q|j|z|t) + # <Ѵʸ>[diouxXeEfgGcsb%] + def parse_printf_format(format, opts) + return format, opts if $1.size % 2 == 1 + end + + def foo(format) + pos = 0 + inspects = [] + format.scan(/%[#0\-+ ]?(\*(?=[^0-9])|\*[1-9][0-9]*\$|[1-9][0-9]*(?=[^0-9]))?(\.(\*(?=[^0-9])|\*[1-9][0-9]*\$|[1-9][0-9]*(?=[^0-9])))?(([1-9][0-9]*\$)*)([diouxXeEfgGcsb%])/) {|f, p, pp, pos, new_pos, c| + puts [f, p, pp, pos, new_pos, c].join("!") + pos = new_pos if new_pos + if c == "I" + inspects.push pos.to_i + (f||"")+(p||"")+(pp||"")+(pos||"")+"s" + else + $& + end + } + end + + def puts(*objs) + for obj in objs + print(*obj) + print "\n" + end + end + + def pp(*objs) + puts(*objs.collect{|obj| obj.inspect}) + end + + def ppx(prefix, *objs) + puts(*objs.collect{|obj| prefix+obj.inspect}) + end + + end + + class StdioOutputMethod 0 + end + end + @debug_level = 0 + + def initialize + lex_init + set_input(STDIN) + + @seek = 0 + @exp_line_no = @line_no = 1 + @base_char_no = 0 + @char_no = 0 + @rests = [] + @readed = [] + @here_readed = [] + + @indent = 0 + @indent_stack = [] + @lex_state = EXPR_BEG + @space_seen = false + @here_header = false + + @continue = false + @line = "" + + @skip_space = false + @readed_auto_clean_up = false + @exception_on_syntax_error = true + + @prompt = nil + end + + attr_accessor :skip_space + attr_accessor :readed_auto_clean_up + attr_accessor :exception_on_syntax_error + + attr_reader :seek + attr_reader :char_no + attr_reader :line_no + attr_reader :indent + + # io functions + def set_input(io, p = nil, &block) + @io = io + if p.respond_to?(:call) + @input = p + elsif block_given? + @input = block + else + @input = Proc.new{@io.gets} + end + end + + def get_readed + if idx = @readed.reverse.index("\n") + @base_char_no = idx + else + @base_char_no += @readed.size + end + + readed = @readed.join("") + @readed = [] + readed + end + + def getc + while @rests.empty? +# return nil unless buf_input + @rests.push nil unless buf_input + end + c = @rests.shift + if @here_header + @here_readed.push c + else + @readed.push c + end + @seek += 1 + if c == "\n" + @line_no += 1 + @char_no = 0 + else + @char_no += 1 + end + c + end + + def gets + l = "" + while c = getc + l.concat(c) + break if c == "\n" + end + return nil if l == "" and c.nil? + l + end + + def eof? + @io.eof? + end + + def getc_of_rests + if @rests.empty? + nil + else + getc + end + end + + def ungetc(c = nil) + if @here_readed.empty? + c2 = @readed.pop + else + c2 = @here_readed.pop + end + c = c2 unless c + @rests.unshift c #c = + @seek -= 1 + if c == "\n" + @line_no -= 1 + if idx = @readed.reverse.index("\n") + @char_no = @readed.size - idx + else + @char_no = @base_char_no + @readed.size + end + else + @char_no -= 1 + end + end + + def peek_equal?(str) + chrs = str.split(//) + until @rests.size >= chrs.size + return false unless buf_input + end + @rests[0, chrs.size] == chrs + end + + def peek_match?(regexp) + while @rests.empty? + return false unless buf_input + end + regexp =~ @rests.join("") + end + + def peek(i = 0) + while @rests.size <= i + return nil unless buf_input + end + @rests[i] + end + + def buf_input + prompt + line = @input.call + return nil unless line + @rests.concat line.split(//) + true + end + private :buf_input + + def set_prompt(p = nil, &block) + p = block if block_given? + if p.respond_to?(:call) + @prompt = p + else + @prompt = Proc.new{print p} + end + end + + def prompt + if @prompt + @prompt.call(@ltype, @indent, @continue, @line_no) + end + end + + def initialize_input + @ltype = nil + @quoted = nil + @indent = 0 + @indent_stack = [] + @lex_state = EXPR_BEG + @space_seen = false + @here_header = false + + @continue = false + prompt + + @line = "" + @exp_line_no = @line_no + end + + def each_top_level_statement + initialize_input + catch(:TERM_INPUT) do + loop do + begin + @continue = false + prompt + unless l = lex + throw :TERM_INPUT if @line == '' + else + #p l + @line.concat l + if @ltype or @continue or @indent > 0 + next + end + end + if @line != "\n" + yield @line, @exp_line_no + end + break unless l + @line = '' + @exp_line_no = @line_no + + @indent = 0 + @indent_stack = [] + prompt + rescue TerminateLineInput + initialize_input + prompt + get_readed + end + end + end + end + + def lex + until (((tk = token).kind_of?(TkNL) || tk.kind_of?(TkEND_OF_SCRIPT)) && + !@continue or + tk.nil?) + #p tk + #p @lex_state + #p self + end + line = get_readed + # print self.inspect + if line == "" and tk.kind_of?(TkEND_OF_SCRIPT) || tk.nil? + nil + else + line + end + end + + def token + # require "tracer" + # Tracer.on + @prev_seek = @seek + @prev_line_no = @line_no + @prev_char_no = @char_no + begin + begin + tk = @OP.match(self) + @space_seen = tk.kind_of?(TkSPACE) + rescue SyntaxError + raise if @exception_on_syntax_error + tk = TkError.new(@seek, @line_no, @char_no) + end + end while @skip_space and tk.kind_of?(TkSPACE) + if @readed_auto_clean_up + get_readed + end + # Tracer.off + tk + end + + ENINDENT_CLAUSE = [ + "case", "class", "def", "do", "for", "if", + "module", "unless", "until", "while", "begin" #, "when" + ] + DEINDENT_CLAUSE = ["end" #, "when" + ] + + PERCENT_LTYPE = { + "q" => "\'", + "Q" => "\"", + "x" => "\`", + "r" => "/", + "w" => "]", + "W" => "]", + "s" => ":" + } + + PERCENT_PAREN = { + "{" => "}", + "[" => "]", + "<" => ">", + "(" => ")" + } + + Ltype2Token = { + "\'" => TkSTRING, + "\"" => TkSTRING, + "\`" => TkXSTRING, + "/" => TkREGEXP, + "]" => TkDSTRING, + ":" => TkSYMBOL + } + DLtype2Token = { + "\"" => TkDSTRING, + "\`" => TkDXSTRING, + "/" => TkDREGEXP, + } + + def lex_init() + @OP = IRB::SLex.new + @OP.def_rules("\0", "\004", "\032") do |op, io| + Token(TkEND_OF_SCRIPT) + end + + @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |op, io| + @space_seen = true + while getc =~ /[ \t\f\r\13]/; end + ungetc + Token(TkSPACE) + end + + @OP.def_rule("#") do |op, io| + identify_comment + end + + @OP.def_rule("=begin", + proc{|op, io| @prev_char_no == 0 && peek(0) =~ /\s/}) do + |op, io| + @ltype = "=" + until getc == "\n"; end + until peek_equal?("=end") && peek(4) =~ /\s/ + until getc == "\n"; end + end + gets + @ltype = nil + Token(TkRD_COMMENT) + end + + @OP.def_rule("\n") do |op, io| + print "\\n\n" if RubyLex.debug? + case @lex_state + when EXPR_BEG, EXPR_FNAME, EXPR_DOT + @continue = true + else + @continue = false + @lex_state = EXPR_BEG + until (@indent_stack.empty? || + [TkLPAREN, TkLBRACK, TkLBRACE, + TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last)) + @indent_stack.pop + end + end + @here_header = false + @here_readed = [] + Token(TkNL) + end + + @OP.def_rules("*", "**", + "=", "==", "===", + "=~", "<=>", + "<", "<=", + ">", ">=", ">>") do + |op, io| + case @lex_state + when EXPR_FNAME, EXPR_DOT + @lex_state = EXPR_ARG + else + @lex_state = EXPR_BEG + end + Token(op) + end + + @OP.def_rules("!", "!=", "!~") do + |op, io| + @lex_state = EXPR_BEG + Token(op) + end + + @OP.def_rules("<<") do + |op, io| + tk = nil + if @lex_state != EXPR_END && @lex_state != EXPR_CLASS && + (@lex_state != EXPR_ARG || @space_seen) + c = peek(0) + if /\S/ =~ c && (/["'`]/ =~ c || /[\w_]/ =~ c || c == "-") + tk = identify_here_document + end + end + unless tk + tk = Token(op) + case @lex_state + when EXPR_FNAME, EXPR_DOT + @lex_state = EXPR_ARG + else + @lex_state = EXPR_BEG + end + end + tk + end + + @OP.def_rules("'", '"') do + |op, io| + identify_string(op) + end + + @OP.def_rules("`") do + |op, io| + if @lex_state == EXPR_FNAME + @lex_state = EXPR_END + Token(op) + else + identify_string(op) + end + end + + @OP.def_rules('?') do + |op, io| + if @lex_state == EXPR_END + @lex_state = EXPR_BEG + Token(TkQUESTION) + else + ch = getc + if @lex_state == EXPR_ARG && ch =~ /\s/ + ungetc + @lex_state = EXPR_BEG; + Token(TkQUESTION) + else + if (ch == '\\') + read_escape + end + @lex_state = EXPR_END + Token(TkINTEGER) + end + end + end + + @OP.def_rules("&", "&&", "|", "||") do + |op, io| + @lex_state = EXPR_BEG + Token(op) + end + + @OP.def_rules("+=", "-=", "*=", "**=", + "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do + |op, io| + @lex_state = EXPR_BEG + op =~ /^(.*)=$/ + Token(TkOPASGN, $1) + end + + @OP.def_rule("+@", proc{|op, io| @lex_state == EXPR_FNAME}) do + |op, io| + @lex_state = EXPR_ARG + Token(op) + end + + @OP.def_rule("-@", proc{|op, io| @lex_state == EXPR_FNAME}) do + |op, io| + @lex_state = EXPR_ARG + Token(op) + end + + @OP.def_rules("+", "-") do + |op, io| + catch(:RET) do + if @lex_state == EXPR_ARG + if @space_seen and peek(0) =~ /[0-9]/ + throw :RET, identify_number + else + @lex_state = EXPR_BEG + end + elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/ + throw :RET, identify_number + else + @lex_state = EXPR_BEG + end + Token(op) + end + end + + @OP.def_rule(".") do + |op, io| + @lex_state = EXPR_BEG + if peek(0) =~ /[0-9]/ + ungetc + identify_number + else + # for "obj.if" etc. + @lex_state = EXPR_DOT + Token(TkDOT) + end + end + + @OP.def_rules("..", "...") do + |op, io| + @lex_state = EXPR_BEG + Token(op) + end + + lex_int2 + end + + def lex_int2 + @OP.def_rules("]", "}", ")") do + |op, io| + @lex_state = EXPR_END + @indent -= 1 + @indent_stack.pop + Token(op) + end + + @OP.def_rule(":") do + |op, io| + if @lex_state == EXPR_END || peek(0) =~ /\s/ + @lex_state = EXPR_BEG + Token(TkCOLON) + else + @lex_state = EXPR_FNAME; + Token(TkSYMBEG) + end + end + + @OP.def_rule("::") do + |op, io| +# p @lex_state.id2name, @space_seen + if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen + @lex_state = EXPR_BEG + Token(TkCOLON3) + else + @lex_state = EXPR_DOT + Token(TkCOLON2) + end + end + + @OP.def_rule("/") do + |op, io| + if @lex_state == EXPR_BEG || @lex_state == EXPR_MID + identify_string(op) + elsif peek(0) == '=' + getc + @lex_state = EXPR_BEG + Token(TkOPASGN, "/") #/) + elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/ + identify_string(op) + else + @lex_state = EXPR_BEG + Token("/") #/) + end + end + + @OP.def_rules("^") do + |op, io| + @lex_state = EXPR_BEG + Token("^") + end + + # @OP.def_rules("^=") do + # @lex_state = EXPR_BEG + # Token(OP_ASGN, :^) + # end + + @OP.def_rules(",") do + |op, io| + @lex_state = EXPR_BEG + Token(op) + end + + @OP.def_rules(";") do + |op, io| + @lex_state = EXPR_BEG + until (@indent_stack.empty? || + [TkLPAREN, TkLBRACK, TkLBRACE, + TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last)) + @indent_stack.pop + end + Token(op) + end + + @OP.def_rule("~") do + |op, io| + @lex_state = EXPR_BEG + Token("~") + end + + @OP.def_rule("~@", proc{|op, io| @lex_state == EXPR_FNAME}) do + |op, io| + @lex_state = EXPR_BEG + Token("~") + end + + @OP.def_rule("(") do + |op, io| + @indent += 1 + if @lex_state == EXPR_BEG || @lex_state == EXPR_MID + @lex_state = EXPR_BEG + tk_c = TkfLPAREN + else + @lex_state = EXPR_BEG + tk_c = TkLPAREN + end + @indent_stack.push tk_c + tk = Token(tk_c) + end + + @OP.def_rule("[]", proc{|op, io| @lex_state == EXPR_FNAME}) do + |op, io| + @lex_state = EXPR_ARG + Token("[]") + end + + @OP.def_rule("[]=", proc{|op, io| @lex_state == EXPR_FNAME}) do + |op, io| + @lex_state = EXPR_ARG + Token("[]=") + end + + @OP.def_rule("[") do + |op, io| + @indent += 1 + if @lex_state == EXPR_FNAME + tk_c = TkfLBRACK + else + if @lex_state == EXPR_BEG || @lex_state == EXPR_MID + tk_c = TkLBRACK + elsif @lex_state == EXPR_ARG && @space_seen + tk_c = TkLBRACK + else + tk_c = TkfLBRACK + end + @lex_state = EXPR_BEG + end + @indent_stack.push tk_c + Token(tk_c) + end + + @OP.def_rule("{") do + |op, io| + @indent += 1 + if @lex_state != EXPR_END && @lex_state != EXPR_ARG + tk_c = TkLBRACE + else + tk_c = TkfLBRACE + end + @lex_state = EXPR_BEG + @indent_stack.push tk_c + Token(tk_c) + end + + @OP.def_rule('\\') do + |op, io| + if getc == "\n" + @space_seen = true + @continue = true + Token(TkSPACE) + else + ungetc + Token("\\") + end + end + + @OP.def_rule('%') do + |op, io| + if @lex_state == EXPR_BEG || @lex_state == EXPR_MID + identify_quotation + elsif peek(0) == '=' + getc + Token(TkOPASGN, :%) + elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/ + identify_quotation + else + @lex_state = EXPR_BEG + Token("%") #)) + end + end + + @OP.def_rule('$') do + |op, io| + identify_gvar + end + + @OP.def_rule('@') do + |op, io| + if peek(0) =~ /[\w_@]/ + ungetc + identify_identifier + else + Token("@") + end + end + + # @OP.def_rule("def", proc{|op, io| /\s/ =~ io.peek(0)}) do + # |op, io| + # @indent += 1 + # @lex_state = EXPR_FNAME + # # @lex_state = EXPR_END + # # until @rests[0] == "\n" or @rests[0] == ";" + # # rests.shift + # # end + # end + + @OP.def_rule("") do + |op, io| + printf "MATCH: start %s: %s\n", op, io.inspect if RubyLex.debug? + if peek(0) =~ /[0-9]/ + t = identify_number + elsif peek(0) =~ /[\w_]/ + t = identify_identifier + end + printf "MATCH: end %s: %s\n", op, io.inspect if RubyLex.debug? + t + end + + p @OP if RubyLex.debug? + end + + def identify_gvar + @lex_state = EXPR_END + + case ch = getc + when /[~_*$?!@\/\\;,=:<>".]/ #" + Token(TkGVAR, "$" + ch) + when "-" + Token(TkGVAR, "$-" + getc) + when "&", "`", "'", "+" + Token(TkBACK_REF, "$"+ch) + when /[1-9]/ + while getc =~ /[0-9]/; end + ungetc + Token(TkNTH_REF) + when /\w/ + ungetc + ungetc + identify_identifier + else + ungetc + Token("$") + end + end + + def identify_identifier + token = "" + if peek(0) =~ /[$@]/ + token.concat(c = getc) + if c == "@" and peek(0) == "@" + token.concat getc + end + end + + while (ch = getc) =~ /\w|_/ + print ":", ch, ":" if RubyLex.debug? + token.concat ch + end + ungetc + + if (ch == "!" || ch == "?") && token[0,1] =~ /\w/ && peek(0) != "=" + token.concat getc + end + + # almost fix token + + case token + when /^\$/ + return Token(TkGVAR, token) + when /^\@\@/ + @lex_state = EXPR_END + # p Token(TkCVAR, token) + return Token(TkCVAR, token) + when /^\@/ + @lex_state = EXPR_END + return Token(TkIVAR, token) + end + + if @lex_state != EXPR_DOT + print token, "\n" if RubyLex.debug? + + token_c, *trans = TkReading2Token[token] + if token_c + # reserved word? + + if (@lex_state != EXPR_BEG && + @lex_state != EXPR_FNAME && + trans[1]) + # modifiers + token_c = TkSymbol2Token[trans[1]] + @lex_state = trans[0] + else + if @lex_state != EXPR_FNAME + if ENINDENT_CLAUSE.include?(token) + # check for ``class = val'' etc. + valid = true + case token + when "class" + valid = false unless peek_match?(/^\s*(<<|\w|::)/) + when "def" + valid = false if peek_match?(/^\s*(([+-\/*&\|^]|<<|>>|\|\||\&\&)=|\&\&|\|\|)/) + when "do" + valid = false if peek_match?(/^\s*([+-\/*]?=|\*|<|>|\&)/) + when *ENINDENT_CLAUSE + valid = false if peek_match?(/^\s*([+-\/*]?=|\*|<|>|\&|\|)/) + else + # no nothing + end + if valid + if token == "do" + if ![TkFOR, TkWHILE, TkUNTIL].include?(@indent_stack.last) + @indent += 1 + @indent_stack.push token_c + end + else + @indent += 1 + @indent_stack.push token_c + end +# p @indent_stack + end + + elsif DEINDENT_CLAUSE.include?(token) + @indent -= 1 + @indent_stack.pop + end + @lex_state = trans[0] + else + @lex_state = EXPR_END + end + end + return Token(token_c, token) + end + end + + if @lex_state == EXPR_FNAME + @lex_state = EXPR_END + if peek(0) == '=' + token.concat getc + end + elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT + @lex_state = EXPR_ARG + else + @lex_state = EXPR_END + end + + if token[0, 1] =~ /[A-Z]/ + return Token(TkCONSTANT, token) + elsif token[token.size - 1, 1] =~ /[!?]/ + return Token(TkFID, token) + else + return Token(TkIDENTIFIER, token) + end + end + + def identify_here_document + ch = getc +# if lt = PERCENT_LTYPE[ch] + if ch == "-" + ch = getc + indent = true + end + if /['"`]/ =~ ch + lt = ch + quoted = "" + while (c = getc) && c != lt + quoted.concat c + end + else + lt = '"' + quoted = ch.dup + while (c = getc) && c =~ /\w/ + quoted.concat c + end + ungetc + end + + ltback, @ltype = @ltype, lt + reserve = [] + while ch = getc + reserve.push ch + if ch == "\\" + reserve.push ch = getc + elsif ch == "\n" + break + end + end + + @here_header = false + while l = gets + l = l.sub(/(:?\r)?\n\z/, '') + if (indent ? l.strip : l) == quoted + break + end + end + + @here_header = true + @here_readed.concat reserve + while ch = reserve.pop + ungetc ch + end + + @ltype = ltback + @lex_state = EXPR_END + Token(Ltype2Token[lt]) + end + + def identify_quotation + ch = getc + if lt = PERCENT_LTYPE[ch] + ch = getc + elsif ch =~ /\W/ + lt = "\"" + else + RubyLex.fail SyntaxError, "unknown type of %string" + end +# if ch !~ /\W/ +# ungetc +# next +# end + #@ltype = lt + @quoted = ch unless @quoted = PERCENT_PAREN[ch] + identify_string(lt, @quoted) + end + + def identify_number + @lex_state = EXPR_END + + if peek(0) == "0" && peek(1) !~ /[.eE]/ + getc + case peek(0) + when /[xX]/ + ch = getc + match = /[0-9a-fA-F_]/ + when /[bB]/ + ch = getc + match = /[01_]/ + when /[oO]/ + ch = getc + match = /[0-7_]/ + when /[dD]/ + ch = getc + match = /[0-9_]/ + when /[0-7]/ + match = /[0-7_]/ + when /[89]/ + RubyLex.fail SyntaxError, "Illegal octal digit" + else + return Token(TkINTEGER) + end + + len0 = true + non_digit = false + while ch = getc + if match =~ ch + if ch == "_" + if non_digit + RubyLex.fail SyntaxError, "trailing `#{ch}' in number" + else + non_digit = ch + end + else + non_digit = false + len0 = false + end + else + ungetc + if len0 + RubyLex.fail SyntaxError, "numeric literal without digits" + end + if non_digit + RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number" + end + break + end + end + return Token(TkINTEGER) + end + + type = TkINTEGER + allow_point = true + allow_e = true + non_digit = false + while ch = getc + case ch + when /[0-9]/ + non_digit = false + when "_" + non_digit = ch + when allow_point && "." + if non_digit + RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number" + end + type = TkFLOAT + if peek(0) !~ /[0-9]/ + type = TkINTEGER + ungetc + break + end + allow_point = false + when allow_e && "e", allow_e && "E" + if non_digit + RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number" + end + type = TkFLOAT + if peek(0) =~ /[+-]/ + getc + end + allow_e = false + allow_point = false + non_digit = ch + else + if non_digit + RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number" + end + ungetc + break + end + end + Token(type) + end + + def identify_string(ltype, quoted = ltype) + @ltype = ltype + @quoted = quoted + subtype = nil + begin + nest = 0 + while ch = getc + if @quoted == ch and nest == 0 + break + elsif @ltype != "'" && @ltype != "]" && @ltype != ":" and ch == "#" + subtype = true + elsif ch == '\\' #' + read_escape + end + if PERCENT_PAREN.values.include?(@quoted) + if PERCENT_PAREN[ch] == @quoted + nest += 1 + elsif ch == @quoted + nest -= 1 + end + end + end + if @ltype == "/" + if peek(0) =~ /i|m|x|o|e|s|u|n/ + getc + end + end + if subtype + Token(DLtype2Token[ltype]) + else + Token(Ltype2Token[ltype]) + end + ensure + @ltype = nil + @quoted = nil + @lex_state = EXPR_END + end + end + + def identify_comment + @ltype = "#" + + while ch = getc +# if ch == "\\" #" +# read_escape +# end + if ch == "\n" + @ltype = nil + ungetc + break + end + end + return Token(TkCOMMENT) + end + + def read_escape + case ch = getc + when "\n", "\r", "\f" + when "\\", "n", "t", "r", "f", "v", "a", "e", "b", "s" #" + when /[0-7]/ + ungetc ch + 3.times do + case ch = getc + when /[0-7]/ + when nil + break + else + ungetc + break + end + end + + when "x" + 2.times do + case ch = getc + when /[0-9a-fA-F]/ + when nil + break + else + ungetc + break + end + end + + when "M" + if (ch = getc) != '-' + ungetc + else + if (ch = getc) == "\\" #" + read_escape + end + end + + when "C", "c" #, "^" + if ch == "C" and (ch = getc) != "-" + ungetc + elsif (ch = getc) == "\\" #" + read_escape + end + else + # other characters + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ruby-token.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ruby-token.rb new file mode 100644 index 0000000000..32308bf907 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/ruby-token.rb @@ -0,0 +1,273 @@ +# +# irb/ruby-token.rb - ruby tokens +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +module RubyToken + EXPR_BEG = :EXPR_BEG + EXPR_MID = :EXPR_MID + EXPR_END = :EXPR_END + EXPR_ARG = :EXPR_ARG + EXPR_FNAME = :EXPR_FNAME + EXPR_DOT = :EXPR_DOT + EXPR_CLASS = :EXPR_CLASS + + # for ruby 1.4X + if !defined?(Symbol) + Symbol = Integer + end + + class Token + def initialize(seek, line_no, char_no) + @seek = seek + @line_no = line_no + @char_no = char_no + end + attr :seek + attr :line_no + attr :char_no + end + + class TkNode < Token + def initialize(seek, line_no, char_no) + super + end + attr :node + end + + class TkId < Token + def initialize(seek, line_no, char_no, name) + super(seek, line_no, char_no) + @name = name + end + attr :name + end + + class TkVal < Token + def initialize(seek, line_no, char_no, value = nil) + super(seek, line_no, char_no) + @value = value + end + attr :value + end + + class TkOp < Token + attr :name, true + end + + class TkOPASGN < TkOp + def initialize(seek, line_no, char_no, op) + super(seek, line_no, char_no) + op = TkReading2Token[op][0] unless op.kind_of?(Symbol) + @op = op + end + attr :op + end + + class TkUnknownChar < Token + def initialize(seek, line_no, char_no, id) + super(seek, line_no, char_no) + @name = name + end + attr :name + end + + class TkError < Token + end + + def Token(token, value = nil) + case token + when String + if (tk = TkReading2Token[token]).nil? + IRB.fail TkReading2TokenNoKey, token + end + tk = Token(tk[0], value) + if tk.kind_of?(TkOp) + tk.name = token + end + return tk + when Symbol + if (tk = TkSymbol2Token[token]).nil? + IRB.fail TkSymbol2TokenNoKey, token + end + return Token(tk[0], value) + else + if (token.ancestors & [TkId, TkVal, TkOPASGN, TkUnknownChar]).empty? + token.new(@prev_seek, @prev_line_no, @prev_char_no) + else + token.new(@prev_seek, @prev_line_no, @prev_char_no, value) + end + end + end + + TokenDefinitions = [ + [:TkCLASS, TkId, "class", EXPR_CLASS], + [:TkMODULE, TkId, "module", EXPR_BEG], + [:TkDEF, TkId, "def", EXPR_FNAME], + [:TkUNDEF, TkId, "undef", EXPR_FNAME], + [:TkBEGIN, TkId, "begin", EXPR_BEG], + [:TkRESCUE, TkId, "rescue", EXPR_MID], + [:TkENSURE, TkId, "ensure", EXPR_BEG], + [:TkEND, TkId, "end", EXPR_END], + [:TkIF, TkId, "if", EXPR_BEG, :TkIF_MOD], + [:TkUNLESS, TkId, "unless", EXPR_BEG, :TkUNLESS_MOD], + [:TkTHEN, TkId, "then", EXPR_BEG], + [:TkELSIF, TkId, "elsif", EXPR_BEG], + [:TkELSE, TkId, "else", EXPR_BEG], + [:TkCASE, TkId, "case", EXPR_BEG], + [:TkWHEN, TkId, "when", EXPR_BEG], + [:TkWHILE, TkId, "while", EXPR_BEG, :TkWHILE_MOD], + [:TkUNTIL, TkId, "until", EXPR_BEG, :TkUNTIL_MOD], + [:TkFOR, TkId, "for", EXPR_BEG], + [:TkBREAK, TkId, "break", EXPR_END], + [:TkNEXT, TkId, "next", EXPR_END], + [:TkREDO, TkId, "redo", EXPR_END], + [:TkRETRY, TkId, "retry", EXPR_END], + [:TkIN, TkId, "in", EXPR_BEG], + [:TkDO, TkId, "do", EXPR_BEG], + [:TkRETURN, TkId, "return", EXPR_MID], + [:TkYIELD, TkId, "yield", EXPR_END], + [:TkSUPER, TkId, "super", EXPR_END], + [:TkSELF, TkId, "self", EXPR_END], + [:TkNIL, TkId, "nil", EXPR_END], + [:TkTRUE, TkId, "true", EXPR_END], + [:TkFALSE, TkId, "false", EXPR_END], + [:TkAND, TkId, "and", EXPR_BEG], + [:TkOR, TkId, "or", EXPR_BEG], + [:TkNOT, TkId, "not", EXPR_BEG], + [:TkIF_MOD, TkId], + [:TkUNLESS_MOD, TkId], + [:TkWHILE_MOD, TkId], + [:TkUNTIL_MOD, TkId], + [:TkALIAS, TkId, "alias", EXPR_FNAME], + [:TkDEFINED, TkId, "defined?", EXPR_END], + [:TklBEGIN, TkId, "BEGIN", EXPR_END], + [:TklEND, TkId, "END", EXPR_END], + [:Tk__LINE__, TkId, "__LINE__", EXPR_END], + [:Tk__FILE__, TkId, "__FILE__", EXPR_END], + + [:TkIDENTIFIER, TkId], + [:TkFID, TkId], + [:TkGVAR, TkId], + [:TkCVAR, TkId], + [:TkIVAR, TkId], + [:TkCONSTANT, TkId], + + [:TkINTEGER, TkVal], + [:TkFLOAT, TkVal], + [:TkSTRING, TkVal], + [:TkXSTRING, TkVal], + [:TkREGEXP, TkVal], + [:TkSYMBOL, TkVal], + + [:TkDSTRING, TkNode], + [:TkDXSTRING, TkNode], + [:TkDREGEXP, TkNode], + [:TkNTH_REF, TkNode], + [:TkBACK_REF, TkNode], + + [:TkUPLUS, TkOp, "+@"], + [:TkUMINUS, TkOp, "-@"], + [:TkPOW, TkOp, "**"], + [:TkCMP, TkOp, "<=>"], + [:TkEQ, TkOp, "=="], + [:TkEQQ, TkOp, "==="], + [:TkNEQ, TkOp, "!="], + [:TkGEQ, TkOp, ">="], + [:TkLEQ, TkOp, "<="], + [:TkANDOP, TkOp, "&&"], + [:TkOROP, TkOp, "||"], + [:TkMATCH, TkOp, "=~"], + [:TkNMATCH, TkOp, "!~"], + [:TkDOT2, TkOp, ".."], + [:TkDOT3, TkOp, "..."], + [:TkAREF, TkOp, "[]"], + [:TkASET, TkOp, "[]="], + [:TkLSHFT, TkOp, "<<"], + [:TkRSHFT, TkOp, ">>"], + [:TkCOLON2, TkOp], + [:TkCOLON3, TkOp], +# [:OPASGN, TkOp], # +=, -= etc. # + [:TkASSOC, TkOp, "=>"], + [:TkQUESTION, TkOp, "?"], #? + [:TkCOLON, TkOp, ":"], #: + + [:TkfLPAREN], # func( # + [:TkfLBRACK], # func[ # + [:TkfLBRACE], # func{ # + [:TkSTAR], # *arg + [:TkAMPER], # &arg # + [:TkSYMBEG], # :SYMBOL + + [:TkGT, TkOp, ">"], + [:TkLT, TkOp, "<"], + [:TkPLUS, TkOp, "+"], + [:TkMINUS, TkOp, "-"], + [:TkMULT, TkOp, "*"], + [:TkDIV, TkOp, "/"], + [:TkMOD, TkOp, "%"], + [:TkBITOR, TkOp, "|"], + [:TkBITXOR, TkOp, "^"], + [:TkBITAND, TkOp, "&"], + [:TkBITNOT, TkOp, "~"], + [:TkNOTOP, TkOp, "!"], + + [:TkBACKQUOTE, TkOp, "`"], + + [:TkASSIGN, Token, "="], + [:TkDOT, Token, "."], + [:TkLPAREN, Token, "("], #(exp) + [:TkLBRACK, Token, "["], #[arry] + [:TkLBRACE, Token, "{"], #{hash} + [:TkRPAREN, Token, ")"], + [:TkRBRACK, Token, "]"], + [:TkRBRACE, Token, "}"], + [:TkCOMMA, Token, ","], + [:TkSEMICOLON, Token, ";"], + + [:TkCOMMENT], + [:TkRD_COMMENT], + [:TkSPACE], + [:TkNL], + [:TkEND_OF_SCRIPT], + + [:TkBACKSLASH, TkUnknownChar, "\\"], + [:TkAT, TkUnknownChar, "@"], + [:TkDOLLAR, TkUnknownChar, "$"], + ] + + # {reading => token_class} + # {reading => [token_class, *opt]} + TkReading2Token = {} + TkSymbol2Token = {} + + def RubyToken.def_token(token_n, super_token = Token, reading = nil, *opts) + token_n = token_n.id2name if token_n.kind_of?(Symbol) + if RubyToken.const_defined?(token_n) + IRB.fail AlreadyDefinedToken, token_n + end + token_c = eval("class #{token_n} < #{super_token}; end; #{token_n}") + + if reading + if TkReading2Token[reading] + IRB.fail TkReading2TokenDuplicateError, token_n, reading + end + if opts.empty? + TkReading2Token[reading] = [token_c] + else + TkReading2Token[reading] = [token_c].concat(opts) + end + end + TkSymbol2Token[token_n.intern] = token_c + end + + for defs in TokenDefinitions + def_token(*defs) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/slex.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/slex.rb new file mode 100644 index 0000000000..0b8eb24cf0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/slex.rb @@ -0,0 +1,285 @@ +# +# irb/slex.rb - symple lex analizer +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +require "e2mmap" +require "irb/notifier" + +module IRB + class SLex + @RCS_ID='-$Id: slex.rb 11708 2007-02-12 23:01:19Z shyouhei $-' + + extend Exception2MessageMapper + def_exception :ErrNodeNothing, "node nothing" + def_exception :ErrNodeAlreadyExists, "node already exists" + + DOUT = Notifier::def_notifier("SLex::") + D_WARN = DOUT::def_notifier(1, "Warn: ") + D_DEBUG = DOUT::def_notifier(2, "Debug: ") + D_DETAIL = DOUT::def_notifier(4, "Detail: ") + + DOUT.level = Notifier::D_NOMSG + + def initialize + @head = Node.new("") + end + + def def_rule(token, preproc = nil, postproc = nil, &block) + D_DETAIL.pp token + + postproc = block if block_given? + node = create(token, preproc, postproc) + end + + def def_rules(*tokens, &block) + if block_given? + p = block + end + for token in tokens + def_rule(token, nil, p) + end + end + + def preproc(token, proc) + node = search(token) + node.preproc=proc + end + + #$BMW%A%'%C%/(B? + def postproc(token) + node = search(token, proc) + node.postproc=proc + end + + def search(token) + @head.search(token.split(//)) + end + + def create(token, preproc = nil, postproc = nil) + @head.create_subnode(token.split(//), preproc, postproc) + end + + def match(token) + case token + when Array + when String + return match(token.split(//)) + else + return @head.match_io(token) + end + ret = @head.match(token) + D_DETAIL.exec_if{D_DEATIL.printf "match end: %s:%s\n", ret, token.inspect} + ret + end + + def inspect + format("", @head.inspect) + end + + #---------------------------------------------------------------------- + # + # class Node - + # + #---------------------------------------------------------------------- + class Node + # if postproc is nil, this node is an abstract node. + # if postproc is non-nil, this node is a real node. + def initialize(preproc = nil, postproc = nil) + @Tree = {} + @preproc = preproc + @postproc = postproc + end + + attr_accessor :preproc + attr_accessor :postproc + + def search(chrs, opt = nil) + return self if chrs.empty? + ch = chrs.shift + if node = @Tree[ch] + node.search(chrs, opt) + else + if opt + chrs.unshift ch + self.create_subnode(chrs) + else + SLex.fail ErrNodeNothing + end + end + end + + def create_subnode(chrs, preproc = nil, postproc = nil) + if chrs.empty? + if @postproc + D_DETAIL.pp node + SLex.fail ErrNodeAlreadyExists + else + D_DEBUG.puts "change abstract node to real node." + @preproc = preproc + @postproc = postproc + end + return self + end + + ch = chrs.shift + if node = @Tree[ch] + if chrs.empty? + if node.postproc + DebugLogger.pp node + DebugLogger.pp self + DebugLogger.pp ch + DebugLogger.pp chrs + SLex.fail ErrNodeAlreadyExists + else + D_WARN.puts "change abstract node to real node" + node.preproc = preproc + node.postproc = postproc + end + else + node.create_subnode(chrs, preproc, postproc) + end + else + if chrs.empty? + node = Node.new(preproc, postproc) + else + node = Node.new + node.create_subnode(chrs, preproc, postproc) + end + @Tree[ch] = node + end + node + end + + # + # chrs: String + # character array + # io must have getc()/ungetc(); and ungetc() must be + # able to be called arbitrary number of times. + # + def match(chrs, op = "") + D_DETAIL.print "match>: ", chrs, "op:", op, "\n" + if chrs.empty? + if @preproc.nil? || @preproc.call(op, chrs) + DOUT.printf(D_DETAIL, "op1: %s\n", op) + @postproc.call(op, chrs) + else + nil + end + else + ch = chrs.shift + if node = @Tree[ch] + if ret = node.match(chrs, op+ch) + return ret + else + chrs.unshift ch + if @postproc and @preproc.nil? || @preproc.call(op, chrs) + DOUT.printf(D_DETAIL, "op2: %s\n", op.inspect) + ret = @postproc.call(op, chrs) + return ret + else + return nil + end + end + else + chrs.unshift ch + if @postproc and @preproc.nil? || @preproc.call(op, chrs) + DOUT.printf(D_DETAIL, "op3: %s\n", op) + @postproc.call(op, chrs) + return "" + else + return nil + end + end + end + end + + def match_io(io, op = "") + if op == "" + ch = io.getc + if ch == nil + return nil + end + else + ch = io.getc_of_rests + end + if ch.nil? + if @preproc.nil? || @preproc.call(op, io) + D_DETAIL.printf("op1: %s\n", op) + @postproc.call(op, io) + else + nil + end + else + if node = @Tree[ch] + if ret = node.match_io(io, op+ch) + ret + else + io.ungetc ch + if @postproc and @preproc.nil? || @preproc.call(op, io) + DOUT.exec_if{D_DETAIL.printf "op2: %s\n", op.inspect} + @postproc.call(op, io) + else + nil + end + end + else + io.ungetc ch + if @postproc and @preproc.nil? || @preproc.call(op, io) + D_DETAIL.printf("op3: %s\n", op) + @postproc.call(op, io) + else + nil + end + end + end + end + end + end +end + +SLex=IRB::SLex + +if $0 == __FILE__ + # Tracer.on + case $1 + when "1" + tr = SLex.new + print "0: ", tr.inspect, "\n" + tr.def_rule("=") {print "=\n"} + print "1: ", tr.inspect, "\n" + tr.def_rule("==") {print "==\n"} + print "2: ", tr.inspect, "\n" + + print "case 1:\n" + print tr.match("="), "\n" + print "case 2:\n" + print tr.match("=="), "\n" + print "case 3:\n" + print tr.match("=>"), "\n" + + when "2" + tr = SLex.new + print "0: ", tr.inspect, "\n" + tr.def_rule("=") {print "=\n"} + print "1: ", tr.inspect, "\n" + tr.def_rule("==", proc{false}) {print "==\n"} + print "2: ", tr.inspect, "\n" + + print "case 1:\n" + print tr.match("="), "\n" + print "case 2:\n" + print tr.match("=="), "\n" + print "case 3:\n" + print tr.match("=>"), "\n" + end + exit +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/version.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/version.rb new file mode 100644 index 0000000000..dc3646ef82 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/version.rb @@ -0,0 +1,16 @@ +# +# irb/version.rb - irb version definition file +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ishitsuka.com) +# +# -- +# +# +# + +module IRB + @RELEASE_VERSION = "0.9.5" + @LAST_UPDATE_DATE = "05/04/13" +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/workspace.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/workspace.rb new file mode 100644 index 0000000000..d1a3a3e680 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/irb/workspace.rb @@ -0,0 +1,107 @@ +# +# irb/workspace-binding.rb - +# $Release Version: 0.9.5$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +module IRB + class WorkSpace + # create new workspace. set self to main if specified, otherwise + # inherit main from TOPLEVEL_BINDING. + def initialize(*main) + if main[0].kind_of?(Binding) + @binding = main.shift + elsif IRB.conf[:SINGLE_IRB] + @binding = TOPLEVEL_BINDING + else + case IRB.conf[:CONTEXT_MODE] + when 0 # binding in proc on TOPLEVEL_BINDING + @binding = eval("proc{binding}.call", + TOPLEVEL_BINDING, + __FILE__, + __LINE__) + when 1 # binding in loaded file + require "tempfile" + f = Tempfile.open("irb-binding") + f.print <str to out_code. + # out_code and in_code are given as constants of Kconv. + # + # *Note* + # This method decode MIME encoded string and + # convert halfwidth katakana to fullwidth katakana. + # If you don't want to decode them, use NKF.nkf. + def kconv(str, out_code, in_code = AUTO) + opt = '-' + case in_code + when ::NKF::JIS + opt << 'J' + when ::NKF::EUC + opt << 'E' + when ::NKF::SJIS + opt << 'S' + when ::NKF::UTF8 + opt << 'W' + when ::NKF::UTF16 + opt << 'W16' + end + + case out_code + when ::NKF::JIS + opt << 'j' + when ::NKF::EUC + opt << 'e' + when ::NKF::SJIS + opt << 's' + when ::NKF::UTF8 + opt << 'w' + when ::NKF::UTF16 + opt << 'w16' + when ::NKF::NOCONV + return str + end + + opt = '' if opt == '-' + + ::NKF::nkf(opt, str) + end + module_function :kconv + + # + # Encode to + # + + # call-seq: + # Kconv.tojis(str) -> string + # + # Convert str to ISO-2022-JP + # + # *Note* + # This method decode MIME encoded string and + # convert halfwidth katakana to fullwidth katakana. + # If you don't want it, use NKF.nkf('-jxm0', str). + def tojis(str) + ::NKF::nkf('-jm', str) + end + module_function :tojis + + # call-seq: + # Kconv.toeuc(str) -> string + # + # Convert str to EUC-JP + # + # *Note* + # This method decode MIME encoded string and + # convert halfwidth katakana to fullwidth katakana. + # If you don't want it, use NKF.nkf('-exm0', str). + def toeuc(str) + ::NKF::nkf('-em', str) + end + module_function :toeuc + + # call-seq: + # Kconv.tosjis(str) -> string + # + # Convert str to Shift_JIS + # + # *Note* + # This method decode MIME encoded string and + # convert halfwidth katakana to fullwidth katakana. + # If you don't want it, use NKF.nkf('-sxm0', str). + def tosjis(str) + ::NKF::nkf('-sm', str) + end + module_function :tosjis + + # call-seq: + # Kconv.toutf8(str) -> string + # + # Convert str to UTF-8 + # + # *Note* + # This method decode MIME encoded string and + # convert halfwidth katakana to fullwidth katakana. + # If you don't want it, use NKF.nkf('-wxm0', str). + def toutf8(str) + ::NKF::nkf('-wm', str) + end + module_function :toutf8 + + # call-seq: + # Kconv.toutf16(str) -> string + # + # Convert str to UTF-16 + # + # *Note* + # This method decode MIME encoded string and + # convert halfwidth katakana to fullwidth katakana. + # If you don't want it, use NKF.nkf('-w16xm0', str). + def toutf16(str) + ::NKF::nkf('-w16m', str) + end + module_function :toutf16 + + # + # guess + # + + # call-seq: + # Kconv.guess(str) -> integer + # + # Guess input encoding by NKF.guess2 + def guess(str) + ::NKF::guess(str) + end + module_function :guess + + # call-seq: + # Kconv.guess_old(str) -> integer + # + # Guess input encoding by NKF.guess1 + def guess_old(str) + ::NKF::guess1(str) + end + module_function :guess_old + + # + # isEncoding + # + + # call-seq: + # Kconv.iseuc(str) -> obj or nil + # + # Returns whether input encoding is EUC-JP or not. + # + # *Note* don't expect this return value is MatchData. + def iseuc(str) + RegexpEucjp.match( str ) + end + module_function :iseuc + + # call-seq: + # Kconv.issjis(str) -> obj or nil + # + # Returns whether input encoding is Shift_JIS or not. + # + # *Note* don't expect this return value is MatchData. + def issjis(str) + RegexpShiftjis.match( str ) + end + module_function :issjis + + # call-seq: + # Kconv.isutf8(str) -> obj or nil + # + # Returns whether input encoding is UTF-8 or not. + # + # *Note* don't expect this return value is MatchData. + def isutf8(str) + RegexpUtf8.match( str ) + end + module_function :isutf8 + +end + +class String + # call-seq: + # String#kconv(out_code, in_code = Kconv::AUTO) + # + # Convert self to out_code. + # out_code and in_code are given as constants of Kconv. + # + # *Note* + # This method decode MIME encoded string and + # convert halfwidth katakana to fullwidth katakana. + # If you don't want to decode them, use NKF.nkf. + def kconv(out_code, in_code=Kconv::AUTO) + Kconv::kconv(self, out_code, in_code) + end + + # + # to Encoding + # + + # call-seq: + # String#tojis -> string + # + # Convert self to ISO-2022-JP + # + # *Note* + # This method decode MIME encoded string and + # convert halfwidth katakana to fullwidth katakana. + # If you don't want it, use NKF.nkf('-jxm0', str). + def tojis; Kconv.tojis(self) end + + # call-seq: + # String#toeuc -> string + # + # Convert self to EUC-JP + # + # *Note* + # This method decode MIME encoded string and + # convert halfwidth katakana to fullwidth katakana. + # If you don't want it, use NKF.nkf('-exm0', str). + def toeuc; Kconv.toeuc(self) end + + # call-seq: + # String#tosjis -> string + # + # Convert self to Shift_JIS + # + # *Note* + # This method decode MIME encoded string and + # convert halfwidth katakana to fullwidth katakana. + # If you don't want it, use NKF.nkf('-sxm0', str). + def tosjis; Kconv.tosjis(self) end + + # call-seq: + # String#toutf8 -> string + # + # Convert self to UTF-8 + # + # *Note* + # This method decode MIME encoded string and + # convert halfwidth katakana to fullwidth katakana. + # If you don't want it, use NKF.nkf('-wxm0', str). + def toutf8; Kconv.toutf8(self) end + + # call-seq: + # String#toutf16 -> string + # + # Convert self to UTF-16 + # + # *Note* + # This method decode MIME encoded string and + # convert halfwidth katakana to fullwidth katakana. + # If you don't want it, use NKF.nkf('-w16xm0', str). + def toutf16; Kconv.toutf16(self) end + + # + # is Encoding + # + + # call-seq: + # String#iseuc -> obj or nil + # + # Returns whether self's encoding is EUC-JP or not. + # + # *Note* don't expect this return value is MatchData. + def iseuc; Kconv.iseuc(self) end + + # call-seq: + # String#issjis -> obj or nil + # + # Returns whether self's encoding is Shift_JIS or not. + # + # *Note* don't expect this return value is MatchData. + def issjis; Kconv.issjis(self) end + + # call-seq: + # String#isutf8 -> obj or nil + # + # Returns whether self's encoding is UTF-8 or not. + # + # *Note* don't expect this return value is MatchData. + def isutf8; Kconv.isutf8(self) end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/logger.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/logger.rb new file mode 100644 index 0000000000..6fdd6c8800 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/logger.rb @@ -0,0 +1,703 @@ +# logger.rb - saimple logging utility +# Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi . + +require 'monitor' + +# Simple logging utility. +# +# Author:: NAKAMURA, Hiroshi +# Documentation:: NAKAMURA, Hiroshi and Gavin Sinclair +# License:: +# You can redistribute it and/or modify it under the same terms of Ruby's +# license; either the dual license version in 2003, or any later version. +# Revision:: $Id: logger.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# +# == Description +# +# The Logger class provides a simple but sophisticated logging utility that +# anyone can use because it's included in the Ruby 1.8.x standard library. +# +# The HOWTOs below give a code-based overview of Logger's usage, but the basic +# concept is as follows. You create a Logger object (output to a file or +# elsewhere), and use it to log messages. The messages will have varying +# levels (+info+, +error+, etc), reflecting their varying importance. The +# levels, and their meanings, are: +# +# +FATAL+:: an unhandleable error that results in a program crash +# +ERROR+:: a handleable error condition +# +WARN+:: a warning +# +INFO+:: generic (useful) information about system operation +# +DEBUG+:: low-level information for developers +# +# So each message has a level, and the Logger itself has a level, which acts +# as a filter, so you can control the amount of information emitted from the +# logger without having to remove actual messages. +# +# For instance, in a production system, you may have your logger(s) set to +# +INFO+ (or +WARN+ if you don't want the log files growing large with +# repetitive information). When you are developing it, though, you probably +# want to know about the program's internal state, and would set them to +# +DEBUG+. +# +# === Example +# +# A simple example demonstrates the above explanation: +# +# log = Logger.new(STDOUT) +# log.level = Logger::WARN +# +# log.debug("Created logger") +# log.info("Program started") +# log.warn("Nothing to do!") +# +# begin +# File.each_line(path) do |line| +# unless line =~ /^(\w+) = (.*)$/ +# log.error("Line in wrong format: #{line}") +# end +# end +# rescue => err +# log.fatal("Caught exception; exiting") +# log.fatal(err) +# end +# +# Because the Logger's level is set to +WARN+, only the warning, error, and +# fatal messages are recorded. The debug and info messages are silently +# discarded. +# +# === Features +# +# There are several interesting features that Logger provides, like +# auto-rolling of log files, setting the format of log messages, and +# specifying a program name in conjunction with the message. The next section +# shows you how to achieve these things. +# +# +# == HOWTOs +# +# === How to create a logger +# +# The options below give you various choices, in more or less increasing +# complexity. +# +# 1. Create a logger which logs messages to STDERR/STDOUT. +# +# logger = Logger.new(STDERR) +# logger = Logger.new(STDOUT) +# +# 2. Create a logger for the file which has the specified name. +# +# logger = Logger.new('logfile.log') +# +# 3. Create a logger for the specified file. +# +# file = File.open('foo.log', File::WRONLY | File::APPEND) +# # To create new (and to remove old) logfile, add File::CREAT like; +# # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT) +# logger = Logger.new(file) +# +# 4. Create a logger which ages logfile once it reaches a certain size. Leave +# 10 "old log files" and each file is about 1,024,000 bytes. +# +# logger = Logger.new('foo.log', 10, 1024000) +# +# 5. Create a logger which ages logfile daily/weekly/monthly. +# +# logger = Logger.new('foo.log', 'daily') +# logger = Logger.new('foo.log', 'weekly') +# logger = Logger.new('foo.log', 'monthly') +# +# === How to log a message +# +# Notice the different methods (+fatal+, +error+, +info+) being used to log +# messages of various levels. Other methods in this family are +warn+ and +# +debug+. +add+ is used below to log a message of an arbitrary (perhaps +# dynamic) level. +# +# 1. Message in block. +# +# logger.fatal { "Argument 'foo' not given." } +# +# 2. Message as a string. +# +# logger.error "Argument #{ @foo } mismatch." +# +# 3. With progname. +# +# logger.info('initialize') { "Initializing..." } +# +# 4. With severity. +# +# logger.add(Logger::FATAL) { 'Fatal error!' } +# +# === How to close a logger +# +# logger.close +# +# === Setting severity threshold +# +# 1. Original interface. +# +# logger.sev_threshold = Logger::WARN +# +# 2. Log4r (somewhat) compatible interface. +# +# logger.level = Logger::INFO +# +# DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN +# +# +# == Format +# +# Log messages are rendered in the output stream in a certain format. The +# default format and a sample are shown below: +# +# Log format: +# SeverityID, [Date Time mSec #pid] SeverityLabel -- ProgName: message +# +# Log sample: +# I, [Wed Mar 03 02:34:24 JST 1999 895701 #19074] INFO -- Main: info. +# +# You may change the date and time format in this manner: +# +# logger.datetime_format = "%Y-%m-%d %H:%M:%S" +# # e.g. "2004-01-03 00:54:26" +# +# There is currently no supported way to change the overall format, but you may +# have some luck hacking the Format constant. +# + + +class Logger + VERSION = "1.2.6" + /: (\S+),v (\S+)/ =~ %q$Id: logger.rb 11708 2007-02-12 23:01:19Z shyouhei $ + ProgName = "#{$1}/#{$2}" + + class Error < RuntimeError; end + class ShiftingError < Error; end + + # Logging severity. + module Severity + DEBUG = 0 + INFO = 1 + WARN = 2 + ERROR = 3 + FATAL = 4 + UNKNOWN = 5 + end + include Severity + + # Logging severity threshold (e.g. Logger::INFO). + attr_accessor :level + + # Logging program name. + attr_accessor :progname + + # Logging date-time format (string passed to +strftime+). + def datetime_format=(datetime_format) + @default_formatter.datetime_format = datetime_format + end + + def datetime_format + @default_formatter.datetime_format + end + + # Logging formatter. formatter#call is invoked with 4 arguments; severity, + # time, progname and msg for each log. Bear in mind that time is a Time and + # msg is an Object that user passed and it could not be a String. It is + # expected to return a logdev#write-able Object. Default formatter is used + # when no formatter is set. + attr_accessor :formatter + + alias sev_threshold level + alias sev_threshold= level= + + # Returns +true+ iff the current severity level allows for the printing of + # +DEBUG+ messages. + def debug?; @level <= DEBUG; end + + # Returns +true+ iff the current severity level allows for the printing of + # +INFO+ messages. + def info?; @level <= INFO; end + + # Returns +true+ iff the current severity level allows for the printing of + # +WARN+ messages. + def warn?; @level <= WARN; end + + # Returns +true+ iff the current severity level allows for the printing of + # +ERROR+ messages. + def error?; @level <= ERROR; end + + # Returns +true+ iff the current severity level allows for the printing of + # +FATAL+ messages. + def fatal?; @level <= FATAL; end + + # + # === Synopsis + # + # Logger.new(name, shift_age = 7, shift_size = 1048576) + # Logger.new(name, shift_age = 'weekly') + # + # === Args + # + # +logdev+:: + # The log device. This is a filename (String) or IO object (typically + # +STDOUT+, +STDERR+, or an open file). + # +shift_age+:: + # Number of old log files to keep, *or* frequency of rotation (+daily+, + # +weekly+ or +monthly+). + # +shift_size+:: + # Maximum logfile size (only applies when +shift_age+ is a number). + # + # === Description + # + # Create an instance. + # + def initialize(logdev, shift_age = 0, shift_size = 1048576) + @progname = nil + @level = DEBUG + @default_formatter = Formatter.new + @formatter = nil + @logdev = nil + if logdev + @logdev = LogDevice.new(logdev, :shift_age => shift_age, + :shift_size => shift_size) + end + end + + # + # === Synopsis + # + # Logger#add(severity, message = nil, progname = nil) { ... } + # + # === Args + # + # +severity+:: + # Severity. Constants are defined in Logger namespace: +DEBUG+, +INFO+, + # +WARN+, +ERROR+, +FATAL+, or +UNKNOWN+. + # +message+:: + # The log message. A String or Exception. + # +progname+:: + # Program name string. Can be omitted. Treated as a message if no +message+ and + # +block+ are given. + # +block+:: + # Can be omitted. Called to get a message string if +message+ is nil. + # + # === Return + # + # +true+ if successful, +false+ otherwise. + # + # When the given severity is not high enough (for this particular logger), log + # no message, and return +true+. + # + # === Description + # + # Log a message if the given severity is high enough. This is the generic + # logging method. Users will be more inclined to use #debug, #info, #warn, + # #error, and #fatal. + # + # Message format: +message+ can be any object, but it has to be + # converted to a String in order to log it. Generally, +inspect+ is used + # if the given object is not a String. + # A special case is an +Exception+ object, which will be printed in detail, + # including message, class, and backtrace. See #msg2str for the + # implementation if required. + # + # === Bugs + # + # * Logfile is not locked. + # * Append open does not need to lock file. + # * But on the OS which supports multi I/O, records possibly be mixed. + # + def add(severity, message = nil, progname = nil, &block) + severity ||= UNKNOWN + if @logdev.nil? or severity < @level + return true + end + progname ||= @progname + if message.nil? + if block_given? + message = yield + else + message = progname + progname = @progname + end + end + @logdev.write( + format_message(format_severity(severity), Time.now, progname, message)) + true + end + alias log add + + # + # Dump given message to the log device without any formatting. If no log + # device exists, return +nil+. + # + def <<(msg) + unless @logdev.nil? + @logdev.write(msg) + end + end + + # + # Log a +DEBUG+ message. + # + # See #info for more information. + # + def debug(progname = nil, &block) + add(DEBUG, nil, progname, &block) + end + + # + # Log an +INFO+ message. + # + # The message can come either from the +progname+ argument or the +block+. If + # both are provided, then the +block+ is used as the message, and +progname+ + # is used as the program name. + # + # === Examples + # + # logger.info("MainApp") { "Received connection from #{ip}" } + # # ... + # logger.info "Waiting for input from user" + # # ... + # logger.info { "User typed #{input}" } + # + # You'll probably stick to the second form above, unless you want to provide a + # program name (which you can do with Logger#progname= as well). + # + # === Return + # + # See #add. + # + def info(progname = nil, &block) + add(INFO, nil, progname, &block) + end + + # + # Log a +WARN+ message. + # + # See #info for more information. + # + def warn(progname = nil, &block) + add(WARN, nil, progname, &block) + end + + # + # Log an +ERROR+ message. + # + # See #info for more information. + # + def error(progname = nil, &block) + add(ERROR, nil, progname, &block) + end + + # + # Log a +FATAL+ message. + # + # See #info for more information. + # + def fatal(progname = nil, &block) + add(FATAL, nil, progname, &block) + end + + # + # Log an +UNKNOWN+ message. This will be printed no matter what the logger + # level. + # + # See #info for more information. + # + def unknown(progname = nil, &block) + add(UNKNOWN, nil, progname, &block) + end + + # + # Close the logging device. + # + def close + @logdev.close if @logdev + end + +private + + # Severity label for logging. (max 5 char) + SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY) + + def format_severity(severity) + SEV_LABEL[severity] || 'ANY' + end + + def format_message(severity, datetime, progname, msg) + (@formatter || @default_formatter).call(severity, datetime, progname, msg) + end + + + class Formatter + Format = "%s, [%s#%d] %5s -- %s: %s\n" + + attr_accessor :datetime_format + + def initialize + @datetime_format = nil + end + + def call(severity, time, progname, msg) + Format % [severity[0..0], format_datetime(time), $$, severity, progname, + msg2str(msg)] + end + + private + + def format_datetime(time) + if @datetime_format.nil? + time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec + else + time.strftime(@datetime_format) + end + end + + def msg2str(msg) + case msg + when ::String + msg + when ::Exception + "#{ msg.message } (#{ msg.class })\n" << + (msg.backtrace || []).join("\n") + else + msg.inspect + end + end + end + + + class LogDevice + attr_reader :dev + attr_reader :filename + + class LogDeviceMutex + include MonitorMixin + end + + def initialize(log = nil, opt = {}) + @dev = @filename = @shift_age = @shift_size = nil + @mutex = LogDeviceMutex.new + if log.respond_to?(:write) and log.respond_to?(:close) + @dev = log + else + @dev = open_logfile(log) + @dev.sync = true + @filename = log + @shift_age = opt[:shift_age] || 7 + @shift_size = opt[:shift_size] || 1048576 + end + end + + def write(message) + @mutex.synchronize do + if @shift_age and @dev.respond_to?(:stat) + begin + check_shift_log + rescue + raise Logger::ShiftingError.new("Shifting failed. #{$!}") + end + end + @dev.write(message) + end + end + + def close + @mutex.synchronize do + @dev.close + end + end + + private + + def open_logfile(filename) + if (FileTest.exist?(filename)) + open(filename, (File::WRONLY | File::APPEND)) + else + create_logfile(filename) + end + end + + def create_logfile(filename) + logdev = open(filename, (File::WRONLY | File::APPEND | File::CREAT)) + logdev.sync = true + add_log_header(logdev) + logdev + end + + def add_log_header(file) + file.write( + "# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName] + ) + end + + SiD = 24 * 60 * 60 + + def check_shift_log + if @shift_age.is_a?(Integer) + # Note: always returns false if '0'. + if @filename && (@shift_age > 0) && (@dev.stat.size > @shift_size) + shift_log_age + end + else + now = Time.now + if @dev.stat.mtime <= previous_period_end(now) + shift_log_period(now) + end + end + end + + def shift_log_age + (@shift_age-3).downto(0) do |i| + if FileTest.exist?("#{@filename}.#{i}") + File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}") + end + end + @dev.close + File.rename("#{@filename}", "#{@filename}.0") + @dev = create_logfile(@filename) + return true + end + + def shift_log_period(now) + postfix = previous_period_end(now).strftime("%Y%m%d") # YYYYMMDD + age_file = "#{@filename}.#{postfix}" + if FileTest.exist?(age_file) + raise RuntimeError.new("'#{ age_file }' already exists.") + end + @dev.close + File.rename("#{@filename}", age_file) + @dev = create_logfile(@filename) + return true + end + + def previous_period_end(now) + case @shift_age + when /^daily$/ + eod(now - 1 * SiD) + when /^weekly$/ + eod(now - ((now.wday + 1) * SiD)) + when /^monthly$/ + eod(now - now.mday * SiD) + else + now + end + end + + def eod(t) + Time.mktime(t.year, t.month, t.mday, 23, 59, 59) + end + end + + + # + # == Description + # + # Application -- Add logging support to your application. + # + # == Usage + # + # 1. Define your application class as a sub-class of this class. + # 2. Override 'run' method in your class to do many things. + # 3. Instantiate it and invoke 'start'. + # + # == Example + # + # class FooApp < Application + # def initialize(foo_app, application_specific, arguments) + # super('FooApp') # Name of the application. + # end + # + # def run + # ... + # log(WARN, 'warning', 'my_method1') + # ... + # @log.error('my_method2') { 'Error!' } + # ... + # end + # end + # + # status = FooApp.new(....).start + # + class Application + include Logger::Severity + + attr_reader :appname + attr_reader :logdev + + # + # == Synopsis + # + # Application.new(appname = '') + # + # == Args + # + # +appname+:: Name of the application. + # + # == Description + # + # Create an instance. Log device is +STDERR+ by default. This can be + # changed with #set_log. + # + def initialize(appname = nil) + @appname = appname + @log = Logger.new(STDERR) + @log.progname = @appname + @level = @log.level + end + + # + # Start the application. Return the status code. + # + def start + status = -1 + begin + log(INFO, "Start of #{ @appname }.") + status = run + rescue + log(FATAL, "Detected an exception. Stopping ... #{$!} (#{$!.class})\n" << $@.join("\n")) + ensure + log(INFO, "End of #{ @appname }. (status: #{ status.to_s })") + end + status + end + + # + # Sets the log device for this application. See the class Logger for an + # explanation of the arguments. + # + def set_log(logdev, shift_age = 0, shift_size = 1024000) + @log = Logger.new(logdev, shift_age, shift_size) + @log.progname = @appname + @log.level = @level + end + + def log=(logdev) + set_log(logdev) + end + + # + # Set the logging threshold, just like Logger#level=. + # + def level=(level) + @level = level + @log.level = @level + end + + # + # See Logger#add. This application's +appname+ is used. + # + def log(severity, message = nil, &block) + @log.add(severity, message, @appname, &block) if @log + end + + private + + def run + raise RuntimeError.new('Method run must be defined in the derived class.') + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/mailread.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/mailread.rb new file mode 100644 index 0000000000..08b33c92a4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/mailread.rb @@ -0,0 +1,62 @@ +# The Mail class represents an internet mail message (as per RFC822, RFC2822) +# with headers and a body. +class Mail + + # Create a new Mail where +f+ is either a stream which responds to gets(), + # or a path to a file. If +f+ is a path it will be opened. + # + # The whole message is read so it can be made available through the #header, + # #[] and #body methods. + # + # The "From " line is ignored if the mail is in mbox format. + def initialize(f) + unless defined? f.gets + f = open(f, "r") + opened = true + end + + @header = {} + @body = [] + begin + while line = f.gets() + line.chop! + next if /^From /=~line # skip From-line + break if /^$/=~line # end of header + + if /^(\S+?):\s*(.*)/=~line + (attr = $1).capitalize! + @header[attr] = $2 + elsif attr + line.sub!(/^\s*/, '') + @header[attr] += "\n" + line + end + end + + return unless line + + while line = f.gets() + break if /^From /=~line + @body.push(line) + end + ensure + f.close if opened + end + end + + # Return the headers as a Hash. + def header + return @header + end + + # Return the message body as an Array of lines + def body + return @body + end + + # Return the header corresponding to +field+. + # + # Matching is case-insensitive. + def [](field) + @header[field.capitalize] + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/mathn.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/mathn.rb new file mode 100644 index 0000000000..a5a121c6c6 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/mathn.rb @@ -0,0 +1,308 @@ +# +# mathn.rb - +# $Release Version: 0.5 $ +# $Revision: 1.1.1.1.4.1 $ +# $Date: 1998/01/16 12:36:05 $ +# by Keiju ISHITSUKA(SHL Japan Inc.) +# +# -- +# +# +# + +require "complex.rb" +require "rational.rb" +require "matrix.rb" + +class Integer + + def gcd2(int) + a = self.abs + b = int.abs + a, b = b, a if a < b + + pd_a = a.prime_division + pd_b = b.prime_division + + gcd = 1 + for pair in pd_a + as = pd_b.assoc(pair[0]) + if as + gcd *= as[0] ** [as[1], pair[1]].min + end + end + return gcd + end + + def Integer.from_prime_division(pd) + value = 1 + for prime, index in pd + value *= prime**index + end + value + end + + def prime_division + raise ZeroDivisionError if self == 0 + ps = Prime.new + value = self + pv = [] + for prime in ps + count = 0 + while (value1, mod = value.divmod(prime) + mod) == 0 + value = value1 + count += 1 + end + if count != 0 + pv.push [prime, count] + end + break if prime * prime >= value + end + if value > 1 + pv.push [value, 1] + end + return pv + end +end + +class Prime + include Enumerable + + def initialize + @seed = 1 + @primes = [] + @counts = [] + end + + def succ + i = -1 + size = @primes.size + while i < size + if i == -1 + @seed += 1 + i += 1 + else + while @seed > @counts[i] + @counts[i] += @primes[i] + end + if @seed != @counts[i] + i += 1 + else + i = -1 + end + end + end + @primes.push @seed + @counts.push @seed + @seed + return @seed + end + alias next succ + + def each + loop do + yield succ + end + end +end + +class Fixnum + alias / quo +end + +class Bignum + alias / quo +end + +class Rational + Unify = true + + def inspect + format "%s/%s", numerator.inspect, denominator.inspect + end + + alias power! ** + + def ** (other) + if other.kind_of?(Rational) + other2 = other + if self < 0 + return Complex.new!(self, 0) ** other + elsif other == 0 + return Rational(1,1) + elsif self == 0 + return Rational(0,1) + elsif self == 1 + return Rational(1,1) + end + + npd = numerator.prime_division + dpd = denominator.prime_division + if other < 0 + other = -other + npd, dpd = dpd, npd + end + + for elm in npd + elm[1] = elm[1] * other + if !elm[1].kind_of?(Integer) and elm[1].denominator != 1 + return Float(self) ** other2 + end + elm[1] = elm[1].to_i + end + + for elm in dpd + elm[1] = elm[1] * other + if !elm[1].kind_of?(Integer) and elm[1].denominator != 1 + return Float(self) ** other2 + end + elm[1] = elm[1].to_i + end + + num = Integer.from_prime_division(npd) + den = Integer.from_prime_division(dpd) + + Rational(num,den) + + elsif other.kind_of?(Integer) + if other > 0 + num = numerator ** other + den = denominator ** other + elsif other < 0 + num = denominator ** -other + den = numerator ** -other + elsif other == 0 + num = 1 + den = 1 + end + Rational.new!(num, den) + elsif other.kind_of?(Float) + Float(self) ** other + else + x , y = other.coerce(self) + x ** y + end + end + + def power2(other) + if other.kind_of?(Rational) + if self < 0 + return Complex(self, 0) ** other + elsif other == 0 + return Rational(1,1) + elsif self == 0 + return Rational(0,1) + elsif self == 1 + return Rational(1,1) + end + + dem = nil + x = self.denominator.to_f.to_i + neard = self.denominator.to_f ** (1.0/other.denominator.to_f) + loop do + if (neard**other.denominator == self.denominator) + dem = neaed + break + end + end + nearn = self.numerator.to_f ** (1.0/other.denominator.to_f) + Rational(num,den) + + elsif other.kind_of?(Integer) + if other > 0 + num = numerator ** other + den = denominator ** other + elsif other < 0 + num = denominator ** -other + den = numerator ** -other + elsif other == 0 + num = 1 + den = 1 + end + Rational.new!(num, den) + elsif other.kind_of?(Float) + Float(self) ** other + else + x , y = other.coerce(self) + x ** y + end + end +end + +module Math + def sqrt(a) + if a.kind_of?(Complex) + abs = sqrt(a.real*a.real + a.image*a.image) +# if not abs.kind_of?(Rational) +# return a**Rational(1,2) +# end + x = sqrt((a.real + abs)/Rational(2)) + y = sqrt((-a.real + abs)/Rational(2)) +# if !(x.kind_of?(Rational) and y.kind_of?(Rational)) +# return a**Rational(1,2) +# end + if a.image >= 0 + Complex(x, y) + else + Complex(x, -y) + end + elsif a >= 0 + rsqrt(a) + else + Complex(0,rsqrt(-a)) + end + end + + def rsqrt(a) + if a.kind_of?(Float) + sqrt!(a) + elsif a.kind_of?(Rational) + rsqrt(a.numerator)/rsqrt(a.denominator) + else + src = a + max = 2 ** 32 + byte_a = [src & 0xffffffff] + # ruby's bug + while (src >= max) and (src >>= 32) + byte_a.unshift src & 0xffffffff + end + + answer = 0 + main = 0 + side = 0 + for elm in byte_a + main = (main << 32) + elm + side <<= 16 + if answer != 0 + if main * 4 < side * side + applo = main.div(side) + else + applo = ((sqrt!(side * side + 4 * main) - side)/2.0).to_i + 1 + end + else + applo = sqrt!(main).to_i + 1 + end + + while (x = (side + applo) * applo) > main + applo -= 1 + end + main -= x + answer = (answer << 16) + applo + side += applo * 2 + end + if main == 0 + answer + else + sqrt!(a) + end + end + end + + module_function :sqrt + module_function :rsqrt +end + +class Complex + Unify = true +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/matrix.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/matrix.rb new file mode 100644 index 0000000000..c62acdf9aa --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/matrix.rb @@ -0,0 +1,1272 @@ +#!/usr/local/bin/ruby +#-- +# matrix.rb - +# $Release Version: 1.0$ +# $Revision: 1.11 $ +# $Date: 1999/10/06 11:01:53 $ +# Original Version from Smalltalk-80 version +# on July 23, 1985 at 8:37:17 am +# by Keiju ISHITSUKA +#++ +# +# = matrix.rb +# +# An implementation of Matrix and Vector classes. +# +# Author:: Keiju ISHITSUKA +# Documentation:: Gavin Sinclair (sourced from Ruby in a Nutshell (Matsumoto, O'Reilly)) +# +# See classes Matrix and Vector for documentation. +# + + +require "e2mmap.rb" + +module ExceptionForMatrix # :nodoc: + extend Exception2MessageMapper + def_e2message(TypeError, "wrong argument type %s (expected %s)") + def_e2message(ArgumentError, "Wrong # of arguments(%d for %d)") + + def_exception("ErrDimensionMismatch", "\#{self.name} dimension mismatch") + def_exception("ErrNotRegular", "Not Regular Matrix") + def_exception("ErrOperationNotDefined", "This operation(%s) can\\'t defined") +end + +# +# The +Matrix+ class represents a mathematical matrix, and provides methods for creating +# special-case matrices (zero, identity, diagonal, singular, vector), operating on them +# arithmetically and algebraically, and determining their mathematical properties (trace, rank, +# inverse, determinant). +# +# Note that although matrices should theoretically be rectangular, this is not +# enforced by the class. +# +# Also note that the determinant of integer matrices may be incorrectly calculated unless you +# also require 'mathn'. This may be fixed in the future. +# +# == Method Catalogue +# +# To create a matrix: +# * Matrix[*rows] +# * Matrix.[](*rows) +# * Matrix.rows(rows, copy = true) +# * Matrix.columns(columns) +# * Matrix.diagonal(*values) +# * Matrix.scalar(n, value) +# * Matrix.scalar(n, value) +# * Matrix.identity(n) +# * Matrix.unit(n) +# * Matrix.I(n) +# * Matrix.zero(n) +# * Matrix.row_vector(row) +# * Matrix.column_vector(column) +# +# To access Matrix elements/columns/rows/submatrices/properties: +# * [](i, j) +# * #row_size +# * #column_size +# * #row(i) +# * #column(j) +# * #collect +# * #map +# * #minor(*param) +# +# Properties of a matrix: +# * #regular? +# * #singular? +# * #square? +# +# Matrix arithmetic: +# * *(m) +# * +(m) +# * -(m) +# * #/(m) +# * #inverse +# * #inv +# * ** +# +# Matrix functions: +# * #determinant +# * #det +# * #rank +# * #trace +# * #tr +# * #transpose +# * #t +# +# Conversion to other data types: +# * #coerce(other) +# * #row_vectors +# * #column_vectors +# * #to_a +# +# String representations: +# * #to_s +# * #inspect +# +class Matrix + @RCS_ID='-$Id: matrix.rb,v 1.11 1999/10/06 11:01:53 keiju Exp keiju $-' + +# extend Exception2MessageMapper + include ExceptionForMatrix + + # instance creations + private_class_method :new + + # + # Creates a matrix where each argument is a row. + # Matrix[ [25, 93], [-1, 66] ] + # => 25 93 + # -1 66 + # + def Matrix.[](*rows) + new(:init_rows, rows, false) + end + + # + # Creates a matrix where +rows+ is an array of arrays, each of which is a row + # to the matrix. If the optional argument +copy+ is false, use the given + # arrays as the internal structure of the matrix without copying. + # Matrix.rows([[25, 93], [-1, 66]]) + # => 25 93 + # -1 66 + def Matrix.rows(rows, copy = true) + new(:init_rows, rows, copy) + end + + # + # Creates a matrix using +columns+ as an array of column vectors. + # Matrix.columns([[25, 93], [-1, 66]]) + # => 25 -1 + # 93 66 + # + # + def Matrix.columns(columns) + rows = (0 .. columns[0].size - 1).collect { + |i| + (0 .. columns.size - 1).collect { + |j| + columns[j][i] + } + } + Matrix.rows(rows, false) + end + + # + # Creates a matrix where the diagonal elements are composed of +values+. + # Matrix.diagonal(9, 5, -3) + # => 9 0 0 + # 0 5 0 + # 0 0 -3 + # + def Matrix.diagonal(*values) + size = values.size + rows = (0 .. size - 1).collect { + |j| + row = Array.new(size).fill(0, 0, size) + row[j] = values[j] + row + } + rows(rows, false) + end + + # + # Creates an +n+ by +n+ diagonal matrix where each diagonal element is + # +value+. + # Matrix.scalar(2, 5) + # => 5 0 + # 0 5 + # + def Matrix.scalar(n, value) + Matrix.diagonal(*Array.new(n).fill(value, 0, n)) + end + + # + # Creates an +n+ by +n+ identity matrix. + # Matrix.identity(2) + # => 1 0 + # 0 1 + # + def Matrix.identity(n) + Matrix.scalar(n, 1) + end + class << Matrix + alias unit identity + alias I identity + end + + # + # Creates an +n+ by +n+ zero matrix. + # Matrix.zero(2) + # => 0 0 + # 0 0 + # + def Matrix.zero(n) + Matrix.scalar(n, 0) + end + + # + # Creates a single-row matrix where the values of that row are as given in + # +row+. + # Matrix.row_vector([4,5,6]) + # => 4 5 6 + # + def Matrix.row_vector(row) + case row + when Vector + Matrix.rows([row.to_a], false) + when Array + Matrix.rows([row.dup], false) + else + Matrix.rows([[row]], false) + end + end + + # + # Creates a single-column matrix where the values of that column are as given + # in +column+. + # Matrix.column_vector([4,5,6]) + # => 4 + # 5 + # 6 + # + def Matrix.column_vector(column) + case column + when Vector + Matrix.columns([column.to_a]) + when Array + Matrix.columns([column]) + else + Matrix.columns([[column]]) + end + end + + # + # This method is used by the other methods that create matrices, and is of no + # use to general users. + # + def initialize(init_method, *argv) + self.send(init_method, *argv) + end + + def init_rows(rows, copy) + if copy + @rows = rows.collect{|row| row.dup} + else + @rows = rows + end + self + end + private :init_rows + + # + # Returns element (+i+,+j+) of the matrix. That is: row +i+, column +j+. + # + def [](i, j) + @rows[i][j] + end + + # + # Returns the number of rows. + # + def row_size + @rows.size + end + + # + # Returns the number of columns. Note that it is possible to construct a + # matrix with uneven columns (e.g. Matrix[ [1,2,3], [4,5] ]), but this is + # mathematically unsound. This method uses the first row to determine the + # result. + # + def column_size + @rows[0].size + end + + # + # Returns row vector number +i+ of the matrix as a Vector (starting at 0 like + # an array). When a block is given, the elements of that vector are iterated. + # + def row(i) # :yield: e + if block_given? + for e in @rows[i] + yield e + end + else + Vector.elements(@rows[i]) + end + end + + # + # Returns column vector number +j+ of the matrix as a Vector (starting at 0 + # like an array). When a block is given, the elements of that vector are + # iterated. + # + def column(j) # :yield: e + if block_given? + 0.upto(row_size - 1) do + |i| + yield @rows[i][j] + end + else + col = (0 .. row_size - 1).collect { + |i| + @rows[i][j] + } + Vector.elements(col, false) + end + end + + # + # Returns a matrix that is the result of iteration of the given block over all + # elements of the matrix. + # Matrix[ [1,2], [3,4] ].collect { |i| i**2 } + # => 1 4 + # 9 16 + # + def collect # :yield: e + rows = @rows.collect{|row| row.collect{|e| yield e}} + Matrix.rows(rows, false) + end + alias map collect + + # + # Returns a section of the matrix. The parameters are either: + # * start_row, nrows, start_col, ncols; OR + # * col_range, row_range + # + # Matrix.diagonal(9, 5, -3).minor(0..1, 0..2) + # => 9 0 0 + # 0 5 0 + # + def minor(*param) + case param.size + when 2 + from_row = param[0].first + size_row = param[0].end - from_row + size_row += 1 unless param[0].exclude_end? + from_col = param[1].first + size_col = param[1].end - from_col + size_col += 1 unless param[1].exclude_end? + when 4 + from_row = param[0] + size_row = param[1] + from_col = param[2] + size_col = param[3] + else + Matrix.Raise ArgumentError, param.inspect + end + + rows = @rows[from_row, size_row].collect{ + |row| + row[from_col, size_col] + } + Matrix.rows(rows, false) + end + + #-- + # TESTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Returns +true+ if this is a regular matrix. + # + def regular? + square? and rank == column_size + end + + # + # Returns +true+ is this is a singular (i.e. non-regular) matrix. + # + def singular? + not regular? + end + + # + # Returns +true+ is this is a square matrix. See note in column_size about this + # being unreliable, though. + # + def square? + column_size == row_size + end + + #-- + # OBJECT METHODS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Returns +true+ if and only if the two matrices contain equal elements. + # + def ==(other) + return false unless Matrix === other + + other.compare_by_row_vectors(@rows) + end + alias eql? == + + # + # Not really intended for general consumption. + # + def compare_by_row_vectors(rows) + return false unless @rows.size == rows.size + + 0.upto(@rows.size - 1) do + |i| + return false unless @rows[i] == rows[i] + end + true + end + + # + # Returns a clone of the matrix, so that the contents of each do not reference + # identical objects. + # + def clone + Matrix.rows(@rows) + end + + # + # Returns a hash-code for the matrix. + # + def hash + value = 0 + for row in @rows + for e in row + value ^= e.hash + end + end + return value + end + + #-- + # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Matrix multiplication. + # Matrix[[2,4], [6,8]] * Matrix.identity(2) + # => 2 4 + # 6 8 + # + def *(m) # m is matrix or vector or number + case(m) + when Numeric + rows = @rows.collect { + |row| + row.collect { + |e| + e * m + } + } + return Matrix.rows(rows, false) + when Vector + m = Matrix.column_vector(m) + r = self * m + return r.column(0) + when Matrix + Matrix.Raise ErrDimensionMismatch if column_size != m.row_size + + rows = (0 .. row_size - 1).collect { + |i| + (0 .. m.column_size - 1).collect { + |j| + vij = 0 + 0.upto(column_size - 1) do + |k| + vij += self[i, k] * m[k, j] + end + vij + } + } + return Matrix.rows(rows, false) + else + x, y = m.coerce(self) + return x * y + end + end + + # + # Matrix addition. + # Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]] + # => 6 0 + # -4 12 + # + def +(m) + case m + when Numeric + Matrix.Raise ErrOperationNotDefined, "+" + when Vector + m = Matrix.column_vector(m) + when Matrix + else + x, y = m.coerce(self) + return x + y + end + + Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size + + rows = (0 .. row_size - 1).collect { + |i| + (0 .. column_size - 1).collect { + |j| + self[i, j] + m[i, j] + } + } + Matrix.rows(rows, false) + end + + # + # Matrix subtraction. + # Matrix[[1,5], [4,2]] - Matrix[[9,3], [-4,1]] + # => -8 2 + # 8 1 + # + def -(m) + case m + when Numeric + Matrix.Raise ErrOperationNotDefined, "-" + when Vector + m = Matrix.column_vector(m) + when Matrix + else + x, y = m.coerce(self) + return x - y + end + + Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size + + rows = (0 .. row_size - 1).collect { + |i| + (0 .. column_size - 1).collect { + |j| + self[i, j] - m[i, j] + } + } + Matrix.rows(rows, false) + end + + # + # Matrix division (multiplication by the inverse). + # Matrix[[7,6], [3,9]] / Matrix[[2,9], [3,1]] + # => -7 1 + # -3 -6 + # + def /(other) + case other + when Numeric + rows = @rows.collect { + |row| + row.collect { + |e| + e / other + } + } + return Matrix.rows(rows, false) + when Matrix + return self * other.inverse + else + x, y = other.coerce(self) + rerurn x / y + end + end + + # + # Returns the inverse of the matrix. + # Matrix[[1, 2], [2, 1]].inverse + # => -1 1 + # 0 -1 + # + def inverse + Matrix.Raise ErrDimensionMismatch unless square? + Matrix.I(row_size).inverse_from(self) + end + alias inv inverse + + # + # Not for public consumption? + # + def inverse_from(src) + size = row_size - 1 + a = src.to_a + + for k in 0..size + if (akk = a[k][k]) == 0 + i = k + begin + Matrix.Raise ErrNotRegular if (i += 1) > size + end while a[i][k] == 0 + a[i], a[k] = a[k], a[i] + @rows[i], @rows[k] = @rows[k], @rows[i] + akk = a[k][k] + end + + for i in 0 .. size + next if i == k + q = a[i][k] / akk + a[i][k] = 0 + + (k + 1).upto(size) do + |j| + a[i][j] -= a[k][j] * q + end + 0.upto(size) do + |j| + @rows[i][j] -= @rows[k][j] * q + end + end + + (k + 1).upto(size) do + |j| + a[k][j] /= akk + end + 0.upto(size) do + |j| + @rows[k][j] /= akk + end + end + self + end + #alias reciprocal inverse + + # + # Matrix exponentiation. Defined for integer powers only. Equivalent to + # multiplying the matrix by itself N times. + # Matrix[[7,6], [3,9]] ** 2 + # => 67 96 + # 48 99 + # + def ** (other) + if other.kind_of?(Integer) + x = self + if other <= 0 + x = self.inverse + return Matrix.identity(self.column_size) if other == 0 + other = -other + end + z = x + n = other - 1 + while n != 0 + while (div, mod = n.divmod(2) + mod == 0) + x = x * x + n = div + end + z *= x + n -= 1 + end + z + elsif other.kind_of?(Float) || defined?(Rational) && other.kind_of?(Rational) + Matrix.Raise ErrOperationNotDefined, "**" + else + Matrix.Raise ErrOperationNotDefined, "**" + end + end + + #-- + # MATRIX FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Returns the determinant of the matrix. If the matrix is not square, the + # result is 0. + # Matrix[[7,6], [3,9]].determinant + # => 63 + # + def determinant + return 0 unless square? + + size = row_size - 1 + a = to_a + + det = 1 + k = 0 + begin + if (akk = a[k][k]) == 0 + i = k + begin + return 0 if (i += 1) > size + end while a[i][k] == 0 + a[i], a[k] = a[k], a[i] + akk = a[k][k] + det *= -1 + end + (k + 1).upto(size) do + |i| + q = a[i][k] / akk + (k + 1).upto(size) do + |j| + a[i][j] -= a[k][j] * q + end + end + det *= akk + end while (k += 1) <= size + det + end + alias det determinant + + # + # Returns the rank of the matrix. Beware that using Float values, with their + # usual lack of precision, can affect the value returned by this method. Use + # Rational values instead if this is important to you. + # Matrix[[7,6], [3,9]].rank + # => 2 + # + def rank + if column_size > row_size + a = transpose.to_a + a_column_size = row_size + a_row_size = column_size + else + a = to_a + a_column_size = column_size + a_row_size = row_size + end + rank = 0 + k = 0 + begin + if (akk = a[k][k]) == 0 + i = k + exists = true + begin + if (i += 1) > a_column_size - 1 + exists = false + break + end + end while a[i][k] == 0 + if exists + a[i], a[k] = a[k], a[i] + akk = a[k][k] + else + i = k + exists = true + begin + if (i += 1) > a_row_size - 1 + exists = false + break + end + end while a[k][i] == 0 + if exists + k.upto(a_column_size - 1) do + |j| + a[j][k], a[j][i] = a[j][i], a[j][k] + end + akk = a[k][k] + else + next + end + end + end + (k + 1).upto(a_row_size - 1) do + |i| + q = a[i][k] / akk + (k + 1).upto(a_column_size - 1) do + |j| + a[i][j] -= a[k][j] * q + end + end + rank += 1 + end while (k += 1) <= a_column_size - 1 + return rank + end + + # + # Returns the trace (sum of diagonal elements) of the matrix. + # Matrix[[7,6], [3,9]].trace + # => 16 + # + def trace + tr = 0 + 0.upto(column_size - 1) do + |i| + tr += @rows[i][i] + end + tr + end + alias tr trace + + # + # Returns the transpose of the matrix. + # Matrix[[1,2], [3,4], [5,6]] + # => 1 2 + # 3 4 + # 5 6 + # Matrix[[1,2], [3,4], [5,6]].transpose + # => 1 3 5 + # 2 4 6 + # + def transpose + Matrix.columns(@rows) + end + alias t transpose + + #-- + # CONVERTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # FIXME: describe #coerce. + # + def coerce(other) + case other + when Numeric + return Scalar.new(other), self + else + raise TypeError, "#{self.class} can't be coerced into #{other.class}" + end + end + + # + # Returns an array of the row vectors of the matrix. See Vector. + # + def row_vectors + rows = (0 .. row_size - 1).collect { + |i| + row(i) + } + rows + end + + # + # Returns an array of the column vectors of the matrix. See Vector. + # + def column_vectors + columns = (0 .. column_size - 1).collect { + |i| + column(i) + } + columns + end + + # + # Returns an array of arrays that describe the rows of the matrix. + # + def to_a + @rows.collect{|row| row.collect{|e| e}} + end + + #-- + # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Overrides Object#to_s + # + def to_s + "Matrix[" + @rows.collect{ + |row| + "[" + row.collect{|e| e.to_s}.join(", ") + "]" + }.join(", ")+"]" + end + + # + # Overrides Object#inspect + # + def inspect + "Matrix"+@rows.inspect + end + + # Private CLASS + + class Scalar < Numeric # :nodoc: + include ExceptionForMatrix + + def initialize(value) + @value = value + end + + # ARITHMETIC + def +(other) + case other + when Numeric + Scalar.new(@value + other) + when Vector, Matrix + Scalar.Raise WrongArgType, other.class, "Numeric or Scalar" + when Scalar + Scalar.new(@value + other.value) + else + x, y = other.coerce(self) + x + y + end + end + + def -(other) + case other + when Numeric + Scalar.new(@value - other) + when Vector, Matrix + Scalar.Raise WrongArgType, other.class, "Numeric or Scalar" + when Scalar + Scalar.new(@value - other.value) + else + x, y = other.coerce(self) + x - y + end + end + + def *(other) + case other + when Numeric + Scalar.new(@value * other) + when Vector, Matrix + other.collect{|e| @value * e} + else + x, y = other.coerce(self) + x * y + end + end + + def / (other) + case other + when Numeric + Scalar.new(@value / other) + when Vector + Scalar.Raise WrongArgType, other.class, "Numeric or Scalar or Matrix" + when Matrix + self * _M.inverse + else + x, y = other.coerce(self) + x / y + end + end + + def ** (other) + case other + when Numeric + Scalar.new(@value ** other) + when Vector + Scalar.Raise WrongArgType, other.class, "Numeric or Scalar or Matrix" + when Matrix + other.powered_by(self) + else + x, y = other.coerce(self) + x ** y + end + end + end +end + + +# +# The +Vector+ class represents a mathematical vector, which is useful in its own right, and +# also constitutes a row or column of a Matrix. +# +# == Method Catalogue +# +# To create a Vector: +# * Vector.[](*array) +# * Vector.elements(array, copy = true) +# +# To access elements: +# * [](i) +# +# To enumerate the elements: +# * #each2(v) +# * #collect2(v) +# +# Vector arithmetic: +# * *(x) "is matrix or number" +# * +(v) +# * -(v) +# +# Vector functions: +# * #inner_product(v) +# * #collect +# * #map +# * #map2(v) +# * #r +# * #size +# +# Conversion to other data types: +# * #covector +# * #to_a +# * #coerce(other) +# +# String representations: +# * #to_s +# * #inspect +# +class Vector + include ExceptionForMatrix + + #INSTANCE CREATION + + private_class_method :new + + # + # Creates a Vector from a list of elements. + # Vector[7, 4, ...] + # + def Vector.[](*array) + new(:init_elements, array, copy = false) + end + + # + # Creates a vector from an Array. The optional second argument specifies + # whether the array itself or a copy is used internally. + # + def Vector.elements(array, copy = true) + new(:init_elements, array, copy) + end + + # + # For internal use. + # + def initialize(method, array, copy) + self.send(method, array, copy) + end + + # + # For internal use. + # + def init_elements(array, copy) + if copy + @elements = array.dup + else + @elements = array + end + end + + # ACCESSING + + # + # Returns element number +i+ (starting at zero) of the vector. + # + def [](i) + @elements[i] + end + + # + # Returns the number of elements in the vector. + # + def size + @elements.size + end + + #-- + # ENUMERATIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Iterate over the elements of this vector and +v+ in conjunction. + # + def each2(v) # :yield: e1, e2 + Vector.Raise ErrDimensionMismatch if size != v.size + 0.upto(size - 1) do + |i| + yield @elements[i], v[i] + end + end + + # + # Collects (as in Enumerable#collect) over the elements of this vector and +v+ + # in conjunction. + # + def collect2(v) # :yield: e1, e2 + Vector.Raise ErrDimensionMismatch if size != v.size + (0 .. size - 1).collect do + |i| + yield @elements[i], v[i] + end + end + + #-- + # COMPARING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Returns +true+ iff the two vectors have the same elements in the same order. + # + def ==(other) + return false unless Vector === other + + other.compare_by(@elements) + end + alias eqn? == + + # + # For internal use. + # + def compare_by(elements) + @elements == elements + end + + # + # Return a copy of the vector. + # + def clone + Vector.elements(@elements) + end + + # + # Return a hash-code for the vector. + # + def hash + @elements.hash + end + + #-- + # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Multiplies the vector by +x+, where +x+ is a number or another vector. + # + def *(x) + case x + when Numeric + els = @elements.collect{|e| e * x} + Vector.elements(els, false) + when Matrix + Matrix.column_vector(self) * x + else + s, x = x.coerce(self) + s * x + end + end + + # + # Vector addition. + # + def +(v) + case v + when Vector + Vector.Raise ErrDimensionMismatch if size != v.size + els = collect2(v) { + |v1, v2| + v1 + v2 + } + Vector.elements(els, false) + when Matrix + Matrix.column_vector(self) + v + else + s, x = v.coerce(self) + s + x + end + end + + # + # Vector subtraction. + # + def -(v) + case v + when Vector + Vector.Raise ErrDimensionMismatch if size != v.size + els = collect2(v) { + |v1, v2| + v1 - v2 + } + Vector.elements(els, false) + when Matrix + Matrix.column_vector(self) - v + else + s, x = v.coerce(self) + s - x + end + end + + #-- + # VECTOR FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Returns the inner product of this vector with the other. + # Vector[4,7].inner_product Vector[10,1] => 47 + # + def inner_product(v) + Vector.Raise ErrDimensionMismatch if size != v.size + + p = 0 + each2(v) { + |v1, v2| + p += v1 * v2 + } + p + end + + # + # Like Array#collect. + # + def collect # :yield: e + els = @elements.collect { + |v| + yield v + } + Vector.elements(els, false) + end + alias map collect + + # + # Like Vector#collect2, but returns a Vector instead of an Array. + # + def map2(v) # :yield: e1, e2 + els = collect2(v) { + |v1, v2| + yield v1, v2 + } + Vector.elements(els, false) + end + + # + # Returns the modulus (Pythagorean distance) of the vector. + # Vector[5,8,2].r => 9.643650761 + # + def r + v = 0 + for e in @elements + v += e*e + end + return Math.sqrt(v) + end + + #-- + # CONVERTING + #++ + + # + # Creates a single-row matrix from this vector. + # + def covector + Matrix.row_vector(self) + end + + # + # Returns the elements of the vector in an array. + # + def to_a + @elements.dup + end + + # + # FIXME: describe Vector#coerce. + # + def coerce(other) + case other + when Numeric + return Scalar.new(other), self + else + raise TypeError, "#{self.class} can't be coerced into #{other.class}" + end + end + + #-- + # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Overrides Object#to_s + # + def to_s + "Vector[" + @elements.join(", ") + "]" + end + + # + # Overrides Object#inspect + # + def inspect + str = "Vector"+@elements.inspect + end +end + + +# Documentation comments: +# - Matrix#coerce and Vector#coerce need to be documented diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/md5.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/md5.rb new file mode 100644 index 0000000000..e3fc27888d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/md5.rb @@ -0,0 +1,23 @@ +# just for compatibility; requiring "md5" is obsoleted +# +# $RoughId: md5.rb,v 1.4 2001/07/13 15:38:27 knu Exp $ +# $Id: md5.rb 12008 2007-03-06 10:12:12Z knu $ + +require 'digest/md5' + +class MD5 < Digest::MD5 + class << self + alias orig_new new + def new(str = nil) + if str + orig_new.update(str) + else + orig_new + end + end + + def md5(*args) + new(*args) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/mkmf.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/mkmf.rb new file mode 100644 index 0000000000..f02003fb27 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/mkmf.rb @@ -0,0 +1,1553 @@ +# module to create Makefile for extension modules +# invoke like: ruby -r mkmf extconf.rb + +require 'rbconfig' +require 'fileutils' +require 'shellwords' + +CONFIG = Config::MAKEFILE_CONFIG +ORIG_LIBPATH = ENV['LIB'] + +CXX_EXT = %w[cc cxx cpp] +if /mswin|bccwin|mingw|msdosdjgpp|human|os2/ !~ CONFIG['build_os'] + CXX_EXT.concat(%w[C]) +end +SRC_EXT = %w[c m] << CXX_EXT +$static = $config_h = nil +$default_static = $static + +unless defined? $configure_args + $configure_args = {} + args = CONFIG["configure_args"] + if ENV["CONFIGURE_ARGS"] + args << " " << ENV["CONFIGURE_ARGS"] + end + for arg in Shellwords::shellwords(args) + arg, val = arg.split('=', 2) + next unless arg + arg.tr!('_', '-') + if arg.sub!(/^(?!--)/, '--') + val or next + arg.downcase! + end + next if /^--(?:top|topsrc|src|cur)dir$/ =~ arg + $configure_args[arg] = val || true + end + for arg in ARGV + arg, val = arg.split('=', 2) + next unless arg + arg.tr!('_', '-') + if arg.sub!(/^(?!--)/, '--') + val or next + arg.downcase! + end + $configure_args[arg] = val || true + end +end + +$libdir = CONFIG["libdir"] +$rubylibdir = CONFIG["rubylibdir"] +$archdir = CONFIG["archdir"] +$sitedir = CONFIG["sitedir"] +$sitelibdir = CONFIG["sitelibdir"] +$sitearchdir = CONFIG["sitearchdir"] + +$mswin = /mswin/ =~ RUBY_PLATFORM +$bccwin = /bccwin/ =~ RUBY_PLATFORM +$mingw = /mingw/ =~ RUBY_PLATFORM +$cygwin = /cygwin/ =~ RUBY_PLATFORM +$human = /human/ =~ RUBY_PLATFORM +$netbsd = /netbsd/ =~ RUBY_PLATFORM +$os2 = /os2/ =~ RUBY_PLATFORM +$beos = /beos/ =~ RUBY_PLATFORM +$solaris = /solaris/ =~ RUBY_PLATFORM +$dest_prefix_pattern = (File::PATH_SEPARATOR == ';' ? /\A([[:alpha:]]:)?/ : /\A/) + +def config_string(key, config = CONFIG) + s = config[key] and !s.empty? and block_given? ? yield(s) : s +end + +def dir_re(dir) + Regexp.new('\$(?:\('+dir+'\)|\{'+dir+'\})(?:\$(?:\(target_prefix\)|\{target_prefix\}))?') +end + +INSTALL_DIRS = [ + [dir_re('commondir'), "$(RUBYCOMMONDIR)"], + [dir_re("sitedir"), "$(RUBYCOMMONDIR)"], + [dir_re('rubylibdir'), "$(RUBYLIBDIR)"], + [dir_re('archdir'), "$(RUBYARCHDIR)"], + [dir_re('sitelibdir'), "$(RUBYLIBDIR)"], + [dir_re('sitearchdir'), "$(RUBYARCHDIR)"] +] + +def install_dirs(target_prefix = nil) + if $extout + dirs = [ + ['RUBYCOMMONDIR', '$(extout)/common'], + ['RUBYLIBDIR', '$(RUBYCOMMONDIR)$(target_prefix)'], + ['RUBYARCHDIR', '$(extout)/$(arch)$(target_prefix)'], + ['extout', "#$extout"], + ['extout_prefix', "#$extout_prefix"], + ] + elsif $extmk + dirs = [ + ['RUBYCOMMONDIR', '$(rubylibdir)'], + ['RUBYLIBDIR', '$(rubylibdir)$(target_prefix)'], + ['RUBYARCHDIR', '$(archdir)$(target_prefix)'], + ] + else + dirs = [ + ['RUBYCOMMONDIR', '$(sitedir)$(target_prefix)'], + ['RUBYLIBDIR', '$(sitelibdir)$(target_prefix)'], + ['RUBYARCHDIR', '$(sitearchdir)$(target_prefix)'], + ] + end + dirs << ['target_prefix', (target_prefix ? "/#{target_prefix}" : "")] + dirs +end + +def map_dir(dir, map = nil) + map ||= INSTALL_DIRS + map.inject(dir) {|dir, (orig, new)| dir.gsub(orig, new)} +end + +topdir = File.dirname(libdir = File.dirname(__FILE__)) +extdir = File.expand_path("ext", topdir) +$extmk = File.expand_path($0)[0, extdir.size+1] == extdir+"/" +if not $extmk and File.exist?(Config::CONFIG["archdir"] + "/ruby.h") + $hdrdir = $topdir = Config::CONFIG["archdir"] +elsif File.exist?(($top_srcdir ||= topdir) + "/ruby.h") and + File.exist?(($topdir ||= Config::CONFIG["topdir"]) + "/config.h") + $hdrdir = $top_srcdir +else + abort "can't find header files for ruby." +end + +OUTFLAG = CONFIG['OUTFLAG'] +CPPOUTFILE = CONFIG['CPPOUTFILE'] + +CONFTEST_C = "conftest.c" + +class String + def quote + /\s/ =~ self ? "\"#{self}\"" : self + end +end +class Array + def quote + map {|s| s.quote} + end +end + +def rm_f(*files) + FileUtils.rm_f(Dir[files.join("\0")]) +end + +def modified?(target, times) + (t = File.mtime(target)) rescue return nil + Array === times or times = [times] + t if times.all? {|n| n <= t} +end + +def merge_libs(*libs) + libs.inject([]) do |x, y| + xy = x & y + xn = yn = 0 + y = y.inject([]) {|ary, e| ary.last == e ? ary : ary << e} + y.each_with_index do |v, yi| + if xy.include?(v) + xi = [x.index(v), xn].max() + x[xi, 1] = y[yn..yi] + xn, yn = xi + (yi - yn + 1), yi + 1 + end + end + x.concat(y[yn..-1] || []) + end +end + +module Logging + @log = nil + @logfile = 'mkmf.log' + @orgerr = $stderr.dup + @orgout = $stdout.dup + @postpone = 0 + + def self::open + @log ||= File::open(@logfile, 'w') + @log.sync = true + $stderr.reopen(@log) + $stdout.reopen(@log) + yield + ensure + $stderr.reopen(@orgerr) + $stdout.reopen(@orgout) + end + + def self::message(*s) + @log ||= File::open(@logfile, 'w') + @log.sync = true + @log.printf(*s) + end + + def self::logfile file + @logfile = file + if @log and not @log.closed? + @log.flush + @log.close + @log = nil + end + end + + def self::postpone + tmplog = "mkmftmp#{@postpone += 1}.log" + open do + log, *save = @log, @logfile, @orgout, @orgerr + @log, @logfile, @orgout, @orgerr = nil, tmplog, log, log + begin + log.print(open {yield}) + @log.close + File::open(tmplog) {|t| FileUtils.copy_stream(t, log)} + ensure + @log, @logfile, @orgout, @orgerr = log, *save + @postpone -= 1 + rm_f tmplog + end + end + end +end + +def xsystem command + Logging::open do + puts command.quote + system(command) + end +end + +def xpopen command, *mode, &block + Logging::open do + case mode[0] + when nil, /^r/ + puts "#{command} |" + else + puts "| #{command}" + end + IO.popen(command, *mode, &block) + end +end + +def log_src(src) + src = src.split(/^/) + fmt = "%#{src.size.to_s.size}d: %s" + Logging::message <<"EOM" +checked program was: +/* begin */ +EOM + src.each_with_index {|line, no| Logging::message fmt, no+1, line} + Logging::message <<"EOM" +/* end */ + +EOM +end + +def create_tmpsrc(src) + src = yield(src) if block_given? + src = src.gsub(/[ \t]+$/, '').gsub(/\A\n+|^\n+$/, '').sub(/[^\n]\z/, "\\&\n") + open(CONFTEST_C, "wb") do |cfile| + cfile.print src + end + src +end + +def try_do(src, command, &b) + src = create_tmpsrc(src, &b) + xsystem(command) +ensure + log_src(src) +end + +def link_command(ldflags, opt="", libpath=$DEFLIBPATH|$LIBPATH) + conf = Config::CONFIG.merge('hdrdir' => $hdrdir.quote, + 'src' => CONFTEST_C, + 'INCFLAGS' => $INCFLAGS, + 'CPPFLAGS' => $CPPFLAGS, + 'CFLAGS' => "#$CFLAGS", + 'ARCH_FLAG' => "#$ARCH_FLAG", + 'LDFLAGS' => "#$LDFLAGS #{ldflags}", + 'LIBPATH' => libpathflag(libpath), + 'LOCAL_LIBS' => "#$LOCAL_LIBS #$libs", + 'LIBS' => "#$LIBRUBYARG_STATIC #{opt} #$LIBS") + Config::expand(TRY_LINK.dup, conf) +end + +def cc_command(opt="") + conf = Config::CONFIG.merge('hdrdir' => $hdrdir.quote, 'srcdir' => $srcdir.quote) + Config::expand("$(CC) #$INCFLAGS #$CPPFLAGS #$CFLAGS #$ARCH_FLAG #{opt} -c #{CONFTEST_C}", + conf) +end + +def cpp_command(outfile, opt="") + conf = Config::CONFIG.merge('hdrdir' => $hdrdir.quote, 'srcdir' => $srcdir.quote) + Config::expand("$(CPP) #$INCFLAGS #$CPPFLAGS #$CFLAGS #{opt} #{CONFTEST_C} #{outfile}", + conf) +end + +def libpathflag(libpath=$DEFLIBPATH|$LIBPATH) + libpath.map{|x| + case x + when "$(topdir)", /\A\./ + LIBPATHFLAG + else + LIBPATHFLAG+RPATHFLAG + end % x.quote + }.join +end + +def try_link0(src, opt="", &b) + try_do(src, link_command("", opt), &b) +end + +def try_link(src, opt="", &b) + try_link0(src, opt, &b) +ensure + rm_f "conftest*", "c0x32*" +end + +def try_compile(src, opt="", &b) + try_do(src, cc_command(opt), &b) +ensure + rm_f "conftest*" +end + +def try_cpp(src, opt="", &b) + try_do(src, cpp_command(CPPOUTFILE, opt), &b) +ensure + rm_f "conftest*" +end + +def cpp_include(header) + if header + header = [header] unless header.kind_of? Array + header.map {|h| "#include <#{h}>\n"}.join + else + "" + end +end + +def with_cppflags(flags) + cppflags = $CPPFLAGS + $CPPFLAGS = flags + ret = yield +ensure + $CPPFLAGS = cppflags unless ret +end + +def with_cflags(flags) + cflags = $CFLAGS + $CFLAGS = flags + ret = yield +ensure + $CFLAGS = cflags unless ret +end + +def with_ldflags(flags) + ldflags = $LDFLAGS + $LDFLAGS = flags + ret = yield +ensure + $LDFLAGS = ldflags unless ret +end + +def try_static_assert(expr, headers = nil, opt = "", &b) + headers = cpp_include(headers) + try_compile(< 0", headers, opt) + # positive constant + elsif try_static_assert("#{const} < 0", headers, opt) + neg = true + const = "-(#{const})" + elsif try_static_assert("#{const} == 0", headers, opt) + return 0 + else + # not a constant + return nil + end + upper = 1 + lower = 0 + until try_static_assert("#{const} <= #{upper}", headers, opt) + lower = upper + upper <<= 1 + end + return nil unless lower + while upper > lower + 1 + mid = (upper + lower) / 2 + if try_static_assert("#{const} > #{mid}", headers, opt) + lower = mid + else + upper = mid + end + end + upper = -upper if neg + return upper + else + src = %{#{COMMON_HEADERS} +#{includes} +#include +/*top*/ +int conftest_const = (int)(#{const}); +int main() {printf("%d\\n", conftest_const); return 0;} +} + if try_link0(src, opt, &b) + xpopen("./conftest") do |f| + return Integer(f.gets) + end + end + end + nil +end + +def try_func(func, libs, headers = nil, &b) + headers = cpp_include(headers) + try_link(<<"SRC", libs, &b) or try_link(<<"SRC", libs, &b) +#{COMMON_HEADERS} +#{headers} +/*top*/ +int main() { return 0; } +int t() { void ((*volatile p)()); p = (void ((*)()))#{func}; return 0; } +SRC +#{headers} +/*top*/ +int main() { return 0; } +int t() { #{func}(); return 0; } +SRC +end + +def try_var(var, headers = nil, &b) + headers = cpp_include(headers) + try_compile(<<"SRC", &b) +#{COMMON_HEADERS} +#{headers} +/*top*/ +int main() { return 0; } +int t() { const volatile void *volatile p; p = (void *)&#{var}; return 0; } +SRC +end + +def egrep_cpp(pat, src, opt = "", &b) + src = create_tmpsrc(src, &b) + xpopen(cpp_command('', opt)) do |f| + if Regexp === pat + puts(" ruby -ne 'print if #{pat.inspect}'") + f.grep(pat) {|l| + puts "#{f.lineno}: #{l}" + return true + } + false + else + puts(" egrep '#{pat}'") + begin + stdin = $stdin.dup + $stdin.reopen(f) + system("egrep", pat) + ensure + $stdin.reopen(stdin) + end + end + end +ensure + rm_f "conftest*" + log_src(src) +end + +def macro_defined?(macro, src, opt = "", &b) + src = src.sub(/[^\n]\z/, "\\&\n") + try_compile(src + <<"SRC", opt, &b) +/*top*/ +#ifndef #{macro} +# error +>>>>>> #{macro} undefined <<<<<< +#endif +SRC +end + +def try_run(src, opt = "", &b) + if try_link0(src, opt, &b) + xsystem("./conftest") + else + nil + end +ensure + rm_f "conftest*" +end + +def install_files(mfile, ifiles, map = nil, srcprefix = nil) + ifiles or return + srcprefix ||= '$(srcdir)' + Config::expand(srcdir = srcprefix.dup) + dirs = [] + path = Hash.new {|h, i| h[i] = dirs.push([i])[-1]} + ifiles.each do |files, dir, prefix| + dir = map_dir(dir, map) + prefix = %r|\A#{Regexp.quote(prefix)}/?| if prefix + if /\A\.\// =~ files + # install files which are in current working directory. + files = files[2..-1] + len = nil + else + # install files which are under the $(srcdir). + files = File.join(srcdir, files) + len = srcdir.size + end + f = nil + Dir.glob(files) do |f| + f[0..len] = "" if len + d = File.dirname(f) + d.sub!(prefix, "") if prefix + d = (d.empty? || d == ".") ? dir : File.join(dir, d) + f = File.join(srcprefix, f) if len + path[d] << f + end + unless len or f + d = File.dirname(files) + d.sub!(prefix, "") if prefix + d = (d.empty? || d == ".") ? dir : File.join(dir, d) + path[d] << files + end + end + dirs +end + +def install_rb(mfile, dest, srcdir = nil) + install_files(mfile, [["lib/**/*.rb", dest, "lib"]], nil, srcdir) +end + +def append_library(libs, lib) + format(LIBARG, lib) + " " + libs +end + +def message(*s) + unless $extmk and not $VERBOSE + printf(*s) + $stdout.flush + end +end + +def checking_for(m, fmt = nil) + f = caller[0][/in `(.*)'$/, 1] and f << ": " #` for vim + m = "checking #{/\Acheck/ =~ f ? '' : 'for '}#{m}... " + message "%s", m + a = r = nil + Logging::postpone do + r = yield + a = (fmt ? fmt % r : r ? "yes" : "no") << "\n" + "#{f}#{m}-------------------- #{a}\n" + end + message(a) + Logging::message "--------------------\n\n" + r +end + +def checking_message(target, place = nil, opt = nil) + [["in", place], ["with", opt]].inject("#{target}") do |msg, (pre, noun)| + if noun + [[:to_str], [:join, ","], [:to_s]].each do |meth, *args| + if noun.respond_to?(meth) + break noun = noun.send(meth, *args) + end + end + msg << " #{pre} #{noun}" unless noun.empty? + end + msg + end +end + +# Returns whether or not +macro+ is defined either in the common header +# files or within any +headers+ you provide. +# +# Any options you pass to +opt+ are passed along to the compiler. +# +def have_macro(macro, headers = nil, opt = "", &b) + checking_for checking_message(macro, headers, opt) do + macro_defined?(macro, cpp_include(headers), opt, &b) + end +end + +# Returns whether or not the given entry point +func+ can be found within +# +lib+. If +func+ is nil, the 'main()' entry point is used by default. +# If found, it adds the library to list of libraries to be used when linking +# your extension. +# +# If +headers+ are provided, it will include those header files as the +# header files it looks in when searching for +func+. +# +# Real name of the library to be linked can be altered by +# '--with-FOOlib' configuration option. +# +def have_library(lib, func = nil, headers = nil, &b) + func = "main" if !func or func.empty? + lib = with_config(lib+'lib', lib) + checking_for checking_message("#{func}()", LIBARG%lib) do + if COMMON_LIBS.include?(lib) + true + else + libs = append_library($libs, lib) + if try_func(func, libs, headers, &b) + $libs = libs + true + else + false + end + end + end +end + +# Returns whether or not the entry point +func+ can be found within the library +# +lib+ in one of the +paths+ specified, where +paths+ is an array of strings. +# If +func+ is nil , then the main() function is used as the entry point. +# +# If +lib+ is found, then the path it was found on is added to the list of +# library paths searched and linked against. +# +def find_library(lib, func, *paths, &b) + func = "main" if !func or func.empty? + lib = with_config(lib+'lib', lib) + paths = paths.collect {|path| path.split(File::PATH_SEPARATOR)}.flatten + checking_for "#{func}() in #{LIBARG%lib}" do + libpath = $LIBPATH + libs = append_library($libs, lib) + begin + until r = try_func(func, libs, &b) or paths.empty? + $LIBPATH = libpath | [paths.shift] + end + if r + $libs = libs + libpath = nil + end + ensure + $LIBPATH = libpath if libpath + end + r + end +end + +# Returns whether or not the function +func+ can be found in the common +# header files, or within any +headers+ that you provide. If found, a +# macro is passed as a preprocessor constant to the compiler using the +# function name, in uppercase, prepended with 'HAVE_'. +# +# For example, if have_func('foo') returned true, then the HAVE_FOO +# preprocessor macro would be passed to the compiler. +# +def have_func(func, headers = nil, &b) + checking_for checking_message("#{func}()", headers) do + if try_func(func, $libs, headers, &b) + $defs.push(format("-DHAVE_%s", func.upcase)) + true + else + false + end + end +end + +# Returns whether or not the variable +var+ can be found in the common +# header files, or within any +headers+ that you provide. If found, a +# macro is passed as a preprocessor constant to the compiler using the +# variable name, in uppercase, prepended with 'HAVE_'. +# +# For example, if have_var('foo') returned true, then the HAVE_FOO +# preprocessor macro would be passed to the compiler. +# +def have_var(var, headers = nil, &b) + checking_for checking_message(var, headers) do + if try_var(var, headers, &b) + $defs.push(format("-DHAVE_%s", var.upcase)) + true + else + false + end + end +end + +# Returns whether or not the given +header+ file can be found on your system. +# If found, a macro is passed as a preprocessor constant to the compiler using +# the header file name, in uppercase, prepended with 'HAVE_'. +# +# For example, if have_header('foo.h') returned true, then the HAVE_FOO_H +# preprocessor macro would be passed to the compiler. +# +def have_header(header, &b) + checking_for header do + if try_cpp(cpp_include(header), &b) + $defs.push(format("-DHAVE_%s", header.tr("a-z./\055", "A-Z___"))) + true + else + false + end + end +end + +# Instructs mkmf to search for the given +header+ in any of the +paths+ +# provided, and returns whether or not it was found in those paths. +# +# If the header is found then the path it was found on is added to the list +# of included directories that are sent to the compiler (via the -I switch). +# +def find_header(header, *paths) + header = cpp_include(header) + checking_for header do + if try_cpp(header) + true + else + found = false + paths.each do |dir| + opt = "-I#{dir}".quote + if try_cpp(header, opt) + $INCFLAGS << " " << opt + found = true + break + end + end + found + end + end +end + +# Returns whether or not the struct of type +type+ contains +member+. If +# it does not, or the struct type can't be found, then false is returned. You +# may optionally specify additional +headers+ in which to look for the struct +# (in addition to the common header files). +# +# If found, a macro is passed as a preprocessor constant to the compiler using +# the member name, in uppercase, prepended with 'HAVE_ST_'. +# +# For example, if have_struct_member('foo', 'bar') returned true, then the +# HAVE_ST_BAR preprocessor macro would be passed to the compiler. +# +def have_struct_member(type, member, headers = nil, &b) + checking_for checking_message("#{type}.#{member}", headers) do + if try_compile(<<"SRC", &b) +#{COMMON_HEADERS} +#{cpp_include(headers)} +/*top*/ +int main() { return 0; } +int s = (char *)&((#{type}*)0)->#{member} - (char *)0; +SRC + $defs.push(format("-DHAVE_ST_%s", member.upcase)) + true + else + false + end + end +end + +# Returns whether or not the static type +type+ is defined. You may +# optionally pass additional +headers+ to check against in addition to the +# common header files. +# +# You may also pass additional flags to +opt+ which are then passed along to +# the compiler. +# +# If found, a macro is passed as a preprocessor constant to the compiler using +# the type name, in uppercase, prepended with 'HAVE_TYPE_'. +# +# For example, if have_type('foo') returned true, then the HAVE_TYPE_FOO +# preprocessor macro would be passed to the compiler. +# +def have_type(type, headers = nil, opt = "", &b) + checking_for checking_message(type, headers, opt) do + headers = cpp_include(headers) + if try_compile(<<"SRC", opt, &b) +#{COMMON_HEADERS} +#{headers} +/*top*/ +typedef #{type} conftest_type; +static conftest_type conftestval[sizeof(conftest_type)?1:-1]; +SRC + $defs.push(format("-DHAVE_TYPE_%s", type.strip.upcase.tr_s("^A-Z0-9_", "_"))) + true + else + false + end + end +end + +# Returns the size of the given +type+. You may optionally specify additional +# +headers+ to search in for the +type+. +# +# If found, a macro is passed as a preprocessor constant to the compiler using +# the type name, in uppercase, prepended with 'SIZEOF_', followed by the type +# name, followed by '=X' where 'X' is the actual size. +# +# For example, if check_sizeof('mystruct') returned 12, then the +# SIZEOF_MYSTRUCT=12 preprocessor macro would be passed to the compiler. +# +def check_sizeof(type, headers = nil, &b) + expr = "sizeof(#{type})" + fmt = "%d" + def fmt.%(x) + x ? super : "failed" + end + checking_for checking_message("size of #{type}", headers), fmt do + if size = try_constant(expr, headers, &b) + $defs.push(format("-DSIZEOF_%s=%d", type.upcase.tr_s("^A-Z0-9_", "_"), size)) + size + end + end +end + +def scalar_ptr_type?(type, member = nil, headers = nil, &b) + try_compile(<<"SRC", &b) # pointer +#{COMMON_HEADERS} +#{cpp_include(headers)} +/*top*/ +volatile #{type} conftestval; +int main() { return 0; } +int t() {return (int)(1-*(conftestval#{member ? ".#{member}" : ""}));} +SRC +end + +def scalar_type?(type, member = nil, headers = nil, &b) + try_compile(<<"SRC", &b) # pointer +#{COMMON_HEADERS} +#{cpp_include(headers)} +/*top*/ +volatile #{type} conftestval; +int main() { return 0; } +int t() {return (int)(1-(conftestval#{member ? ".#{member}" : ""}));} +SRC +end + +def what_type?(type, member = nil, headers = nil, &b) + m = "#{type}" + name = type + if member + m << "." << member + name = "(((#{type} *)0)->#{member})" + end + fmt = "seems %s" + def fmt.%(x) + x ? super : "unknown" + end + checking_for checking_message(m, headers), fmt do + if scalar_ptr_type?(type, member, headers, &b) + if try_static_assert("sizeof(*#{name}) == 1", headers) + "string" + end + elsif scalar_type?(type, member, headers, &b) + if try_static_assert("sizeof(#{name}) > sizeof(long)", headers) + "long long" + elsif try_static_assert("sizeof(#{name}) > sizeof(int)", headers) + "long" + elsif try_static_assert("sizeof(#{name}) > sizeof(short)", headers) + "int" + elsif try_static_assert("sizeof(#{name}) > 1", headers) + "short" + else + "char" + end + end + end +end + +def find_executable0(bin, path = nil) + ext = config_string('EXEEXT') + if File.expand_path(bin) == bin + return bin if File.executable?(bin) + ext and File.executable?(file = bin + ext) and return file + return nil + end + if path ||= ENV['PATH'] + path = path.split(File::PATH_SEPARATOR) + else + path = %w[/usr/local/bin /usr/ucb /usr/bin /bin] + end + file = nil + path.each do |dir| + return file if File.executable?(file = File.join(dir, bin)) + return file if ext and File.executable?(file << ext) + end + nil +end + +def find_executable(bin, path = nil) + checking_for checking_message(bin, path) do + find_executable0(bin, path) + end +end + +def arg_config(config, *defaults, &block) + $arg_config << [config, *defaults] + defaults << nil if !block and defaults.empty? + $configure_args.fetch(config.tr('_', '-'), *defaults, &block) +end + +def with_config(config, *defaults) + config = config.sub(/^--with[-_]/, '') + val = arg_config("--with-"+config) do + if arg_config("--without-"+config) + false + elsif block_given? + yield(config, *defaults) + else + break *defaults + end + end + case val + when "yes" + true + when "no" + false + else + val + end +end + +def enable_config(config, *defaults) + if arg_config("--enable-"+config) + true + elsif arg_config("--disable-"+config) + false + elsif block_given? + yield(config, *defaults) + else + return *defaults + end +end + +def create_header(header = "extconf.h") + message "creating %s\n", header + sym = header.tr("a-z./\055", "A-Z___") + hdr = ["#ifndef #{sym}\n#define #{sym}\n"] + for line in $defs + case line + when /^-D([^=]+)(?:=(.*))?/ + hdr << "#define #$1 #{$2 ? Shellwords.shellwords($2)[0] : 1}\n" + when /^-U(.*)/ + hdr << "#undef #$1\n" + end + end + hdr << "#endif\n" + hdr = hdr.join + unless (IO.read(header) == hdr rescue false) + open(header, "w") do |hfile| + hfile.write(hdr) + end + end + $extconf_h = header +end + +# Sets a +target+ name that the user can then use to configure various 'with' +# options with on the command line by using that name. For example, if the +# target is set to "foo", then the user could use the --with-foo-dir command +# line option. +# +# You may pass along additional 'include' or 'lib' defaults via the +idefault+ +# and +ldefault+ parameters, respectively. +# +# Note that dir_config only adds to the list of places to search for libraries +# and include files. It does not link the libraries into your application. +# +def dir_config(target, idefault=nil, ldefault=nil) + if dir = with_config(target + "-dir", (idefault unless ldefault)) + defaults = Array === dir ? dir : dir.split(File::PATH_SEPARATOR) + idefault = ldefault = nil + end + + idir = with_config(target + "-include", idefault) + $arg_config.last[1] ||= "${#{target}-dir}/include" + ldir = with_config(target + "-lib", ldefault) + $arg_config.last[1] ||= "${#{target}-dir}/lib" + + idirs = idir ? Array === idir ? idir : idir.split(File::PATH_SEPARATOR) : [] + if defaults + idirs.concat(defaults.collect {|dir| dir + "/include"}) + idir = ([idir] + idirs).compact.join(File::PATH_SEPARATOR) + end + unless idirs.empty? + idirs.collect! {|dir| "-I" + dir} + idirs -= Shellwords.shellwords($CPPFLAGS) + unless idirs.empty? + $CPPFLAGS = (idirs.quote << $CPPFLAGS).join(" ") + end + end + + ldirs = ldir ? Array === ldir ? ldir : ldir.split(File::PATH_SEPARATOR) : [] + if defaults + ldirs.concat(defaults.collect {|dir| dir + "/lib"}) + ldir = ([ldir] + ldirs).compact.join(File::PATH_SEPARATOR) + end + $LIBPATH = ldirs | $LIBPATH + + [idir, ldir] +end + +def pkg_config(pkg) + if pkgconfig = with_config("#{pkg}-config") and find_executable0(pkgconfig) + # iff package specific config command is given + get = proc {|opt| `#{pkgconfig} --#{opt}`.chomp} + elsif ($PKGCONFIG ||= + (pkgconfig = with_config("pkg-config", ("pkg-config" unless CROSS_COMPILING))) && + find_executable0(pkgconfig) && pkgconfig) and + system("#{$PKGCONFIG} --exists #{pkg}") + # default to pkg-config command + get = proc {|opt| `#{$PKGCONFIG} --#{opt} #{pkg}`.chomp} + elsif find_executable0(pkgconfig = "#{pkg}-config") + # default to package specific config command, as a last resort. + get = proc {|opt| `#{pkgconfig} --#{opt}`.chomp} + end + if get + cflags = get['cflags'] + ldflags = get['libs'] + libs = get['libs-only-l'] + ldflags = (Shellwords.shellwords(ldflags) - Shellwords.shellwords(libs)).quote.join(" ") + $CFLAGS += " " << cflags + $LDFLAGS += " " << ldflags + $libs += " " << libs + Logging::message "package configuration for %s\n", pkg + Logging::message "cflags: %s\nldflags: %s\nlibs: %s\n\n", + cflags, ldflags, libs + [cflags, ldflags, libs] + else + Logging::message "package configuration for %s is not found\n", pkg + nil + end +end + +def with_destdir(dir) + dir = dir.sub($dest_prefix_pattern, '') + /\A\$[\(\{]/ =~ dir ? dir : "$(DESTDIR)"+dir +end + +def winsep(s) + s.tr('/', '\\') +end + +def configuration(srcdir) + mk = [] + vpath = %w[$(srcdir) $(topdir) $(hdrdir)] + if !CROSS_COMPILING + case CONFIG['build_os'] + when 'cygwin' + if CONFIG['target_os'] != 'cygwin' + vpath.each {|p| p.sub!(/.*/, '$(shell cygpath -u \&)')} + end + when 'msdosdjgpp', 'mingw32' + CONFIG['PATH_SEPARATOR'] = ';' + end + end + mk << %{ +SHELL = /bin/sh + +#### Start of system configuration section. #### + +srcdir = #{srcdir.gsub(/\$\((srcdir)\)|\$\{(srcdir)\}/) {CONFIG[$1||$2]}.quote} +topdir = #{($extmk ? CONFIG["topdir"] : $topdir).quote} +hdrdir = #{$extmk ? CONFIG["hdrdir"].quote : '$(topdir)'} +VPATH = #{vpath.join(CONFIG['PATH_SEPARATOR'])} +} + if $extmk + mk << "RUBYLIB = -\nRUBYOPT = -rpurelib.rb\n" + end + if destdir = CONFIG["prefix"][$dest_prefix_pattern, 1] + mk << "\nDESTDIR = #{destdir}\n" + end + CONFIG.each do |key, var| + next unless /prefix$/ =~ key + mk << "#{key} = #{with_destdir(var)}\n" + end + CONFIG.each do |key, var| + next if /^abs_/ =~ key + next unless /^(?:src|top|hdr|(.*))dir$/ =~ key and $1 + mk << "#{key} = #{with_destdir(var)}\n" + end + if !$extmk and !$configure_args.has_key?('--ruby') and + sep = config_string('BUILD_FILE_SEPARATOR') + sep = ":/=#{sep}" + else + sep = "" + end + extconf_h = $extconf_h ? "-DRUBY_EXTCONF_H=\\\"$(RUBY_EXTCONF_H)\\\" " : $defs.join(" ")<<" " + mk << %{ +CC = #{CONFIG['CC']} +LIBRUBY = #{CONFIG['LIBRUBY']} +LIBRUBY_A = #{CONFIG['LIBRUBY_A']} +LIBRUBYARG_SHARED = #$LIBRUBYARG_SHARED +LIBRUBYARG_STATIC = #$LIBRUBYARG_STATIC + +RUBY_EXTCONF_H = #{$extconf_h} +CFLAGS = #{$static ? '' : CONFIG['CCDLFLAGS']} #$CFLAGS #$ARCH_FLAG +INCFLAGS = -I. #$INCFLAGS +CPPFLAGS = #{extconf_h}#{$CPPFLAGS} +CXXFLAGS = $(CFLAGS) #{CONFIG['CXXFLAGS']} +DLDFLAGS = #$LDFLAGS #$DLDFLAGS #$ARCH_FLAG +LDSHARED = #{CONFIG['LDSHARED']} +AR = #{CONFIG['AR']} +EXEEXT = #{CONFIG['EXEEXT']} + +RUBY_INSTALL_NAME = #{CONFIG['RUBY_INSTALL_NAME']} +RUBY_SO_NAME = #{CONFIG['RUBY_SO_NAME']} +arch = #{CONFIG['arch']} +sitearch = #{CONFIG['sitearch']} +ruby_version = #{Config::CONFIG['ruby_version']} +ruby = #{$ruby} +RUBY = $(ruby#{sep}) +RM = #{config_string('RM') || '$(RUBY) -run -e rm -- -f'} +MAKEDIRS = #{config_string('MAKEDIRS') || '@$(RUBY) -run -e mkdir -- -p'} +INSTALL = #{config_string('INSTALL') || '@$(RUBY) -run -e install -- -vp'} +INSTALL_PROG = #{config_string('INSTALL_PROG') || '$(INSTALL) -m 0755'} +INSTALL_DATA = #{config_string('INSTALL_DATA') || '$(INSTALL) -m 0644'} +COPY = #{config_string('CP') || '@$(RUBY) -run -e cp -- -v'} + +#### End of system configuration section. #### + +preload = #{$preload ? $preload.join(' ') : ''} +} + if $nmake == ?b + mk.each do |x| + x.gsub!(/^(MAKEDIRS|INSTALL_(?:PROG|DATA))+\s*=.*\n/) do + "!ifndef " + $1 + "\n" + + $& + + "!endif\n" + end + end + end + mk +end + +def dummy_makefile(srcdir) + configuration(srcdir) << <>$(INSTALLED_LIST)\n" + end + end + end + mfile.print("install-rb: pre-install-rb install-rb-default\n") + mfile.print("install-rb-default: pre-install-rb-default\n") + mfile.print("pre-install-rb: Makefile\n") + mfile.print("pre-install-rb-default: Makefile\n") + for sfx, i in [["-default", [["lib/**/*.rb", "$(RUBYLIBDIR)", "lib"]]], ["", $INSTALLFILES]] + files = install_files(mfile, i, nil, srcprefix) or next + for dir, *files in files + unless dirs.include?(dir) + dirs << dir + mfile.print "pre-install-rb#{sfx}: #{dir}\n" + end + files.each do |f| + dest = "#{dir}/#{File.basename(f)}" + mfile.print("install-rb#{sfx}: #{dest}\n") + mfile.print("#{dest}: #{f}\n\t$(#{$extout ? 'COPY' : 'INSTALL_DATA'}) ") + sep = config_string('BUILD_FILE_SEPARATOR') + if sep + f = f.gsub("/", sep) + sep = ":/="+sep + f = f.gsub(/(\$\(\w+)(\))/) {$1+sep+$2} + f = f.gsub(/(\$\{\w+)(\})/) {$1+sep+$2} + else + sep = "" + end + mfile.print("#{f} $(@D#{sep})\n") + if defined?($installed_list) and !$extout + mfile.print("\t@echo #{dest}>>$(INSTALLED_LIST)\n") + end + end + end + end + dirs.unshift(sodir) if target and !dirs.include?(sodir) + dirs.each {|dir| mfile.print "#{dir}:\n\t$(MAKEDIRS) $@\n"} + + mfile.print <<-SITEINSTALL + +site-install: site-install-so site-install-rb +site-install-so: install-so +site-install-rb: install-rb + + SITEINSTALL + + return unless target + + mfile.puts SRC_EXT.collect {|ext| ".path.#{ext} = $(VPATH)"} if $nmake == ?b + mfile.print ".SUFFIXES: .#{SRC_EXT.join(' .')} .#{$OBJEXT}\n" + mfile.print "\n" + + CXX_EXT.each do |ext| + COMPILE_RULES.each do |rule| + mfile.printf(rule, ext, $OBJEXT) + mfile.printf("\n\t%s\n\n", COMPILE_CXX) + end + end + %w[c].each do |ext| + COMPILE_RULES.each do |rule| + mfile.printf(rule, ext, $OBJEXT) + mfile.printf("\n\t%s\n\n", COMPILE_C) + end + end + + mfile.print "$(RUBYARCHDIR)/" if $extout + mfile.print "$(DLLIB): ", (makedef ? "$(DEFFILE) " : ""), "$(OBJS)\n" + mfile.print "\t@-$(RM) $@\n" + mfile.print "\t@-$(MAKEDIRS) $(@D)\n" if $extout + link_so = LINK_SO.gsub(/^/, "\t") + mfile.print link_so, "\n\n" + unless $static.nil? + mfile.print "$(STATIC_LIB): $(OBJS)\n\t" + mfile.print "$(AR) #{config_string('ARFLAGS') || 'cru '}$@ $(OBJS)" + config_string('RANLIB') do |ranlib| + mfile.print "\n\t@-#{ranlib} $(DLLIB) 2> /dev/null || true" + end + end + mfile.print "\n\n" + if makedef + mfile.print "$(DEFFILE): #{origdef}\n" + mfile.print "\t$(RUBY) #{makedef} #{origdef} > $@\n\n" + end + + depend = File.join(srcdir, "depend") + if File.exist?(depend) + suffixes = [] + depout = [] + open(depend, "r") do |dfile| + mfile.printf "###\n" + cont = implicit = nil + impconv = proc do + COMPILE_RULES.each {|rule| depout << (rule % implicit[0]) << implicit[1]} + implicit = nil + end + ruleconv = proc do |line| + if implicit + if /\A\t/ =~ line + implicit[1] << line + next + else + impconv[] + end + end + if m = /\A\.(\w+)\.(\w+)(?:\s*:)/.match(line) + suffixes << m[1] << m[2] + implicit = [[m[1], m[2]], [m.post_match]] + next + elsif RULE_SUBST and /\A(?!\s*\w+\s*=)[$\w][^#]*:/ =~ line + line.gsub!(%r"(\s)(?!\.)([^$(){}+=:\s\/\\,]+)(?=\s|\z)") {$1 + RULE_SUBST % $2} + end + depout << line + end + while line = dfile.gets() + line.gsub!(/\.o\b/, ".#{$OBJEXT}") + line.gsub!(/\$\(hdrdir\)\/config.h/, $config_h) if $config_h + if /(?:^|[^\\])(?:\\\\)*\\$/ =~ line + (cont ||= []) << line + next + elsif cont + line = (cont << line).join + cont = nil + end + ruleconv.call(line) + end + if cont + ruleconv.call(cont.join) + elsif implicit + impconv.call + end + end + unless suffixes.empty? + mfile.print ".SUFFIXES: .", suffixes.uniq.join(" ."), "\n\n" + end + mfile.print "$(OBJS): $(RUBY_EXTCONF_H)\n\n" if $extconf_h + mfile.print depout + else + headers = %w[ruby.h defines.h] + if RULE_SUBST + headers.each {|h| h.sub!(/.*/) {|*m| RULE_SUBST % m}} + end + headers << $config_h if $config_h + headers << "$(RUBY_EXTCONF_H)" if $extconf_h + mfile.print "$(OBJS): ", headers.join(' '), "\n" + end + + $makefile_created = true +ensure + mfile.close if mfile +end + +def init_mkmf(config = CONFIG) + $makefile_created = false + $arg_config = [] + $enable_shared = config['ENABLE_SHARED'] == 'yes' + $defs = [] + $extconf_h = nil + $CFLAGS = with_config("cflags", arg_config("CFLAGS", config["CFLAGS"])).dup + $ARCH_FLAG = with_config("arch_flag", arg_config("ARCH_FLAG", config["ARCH_FLAG"])).dup + $CPPFLAGS = with_config("cppflags", arg_config("CPPFLAGS", config["CPPFLAGS"])).dup + $LDFLAGS = with_config("ldflags", arg_config("LDFLAGS", config["LDFLAGS"])).dup + $INCFLAGS = "-I$(topdir) -I$(hdrdir) -I$(srcdir)" + $DLDFLAGS = with_config("dldflags", arg_config("DLDFLAGS", config["DLDFLAGS"])).dup + $LIBEXT = config['LIBEXT'].dup + $OBJEXT = config["OBJEXT"].dup + $LIBS = "#{config['LIBS']} #{config['DLDLIBS']}" + $LIBRUBYARG = "" + $LIBRUBYARG_STATIC = config['LIBRUBYARG_STATIC'] + $LIBRUBYARG_SHARED = config['LIBRUBYARG_SHARED'] + $DEFLIBPATH = $extmk ? ["$(topdir)"] : CROSS_COMPILING ? [] : ["$(libdir)"] + $DEFLIBPATH.unshift(".") + $LIBPATH = [] + $INSTALLFILES = nil + + $objs = nil + $srcs = nil + $libs = "" + if $enable_shared or Config.expand(config["LIBRUBY"].dup) != Config.expand(config["LIBRUBY_A"].dup) + $LIBRUBYARG = config['LIBRUBYARG'] + end + + $LOCAL_LIBS = "" + + $cleanfiles = config_string('CLEANFILES') {|s| Shellwords.shellwords(s)} || [] + $cleanfiles << "mkmf.log" + $distcleanfiles = config_string('DISTCLEANFILES') {|s| Shellwords.shellwords(s)} || [] + + $extout ||= nil + $extout_prefix ||= nil + + $arg_config.clear + dir_config("opt") +end + +FailedMessage = <"} +end +COMMON_HEADERS = hdr.join("\n") +COMMON_LIBS = config_string('COMMON_LIBS', &split) || [] + +COMPILE_RULES = config_string('COMPILE_RULES', &split) || %w[.%s.%s:] +RULE_SUBST = config_string('RULE_SUBST') +COMPILE_C = config_string('COMPILE_C') || '$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $<' +COMPILE_CXX = config_string('COMPILE_CXX') || '$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<' +TRY_LINK = config_string('TRY_LINK') || + "$(CC) #{OUTFLAG}conftest $(INCFLAGS) $(CPPFLAGS) " \ + "$(CFLAGS) $(src) $(LIBPATH) $(LDFLAGS) $(ARCH_FLAG) $(LOCAL_LIBS) $(LIBS)" +LINK_SO = config_string('LINK_SO') || + if CONFIG["DLEXT"] == $OBJEXT + "ld $(DLDFLAGS) -r -o $@ $(OBJS)\n" + else + "$(LDSHARED) #{OUTFLAG}$@ $(OBJS) " \ + "$(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)" + end +LIBPATHFLAG = config_string('LIBPATHFLAG') || ' -L"%s"' +RPATHFLAG = config_string('RPATHFLAG') || '' +LIBARG = config_string('LIBARG') || '-l%s' + +sep = config_string('BUILD_FILE_SEPARATOR') {|sep| ":/=#{sep}" if sep != "/"} || "" +CLEANINGS = " +clean: + @-$(RM) $(CLEANLIBS#{sep}) $(CLEANOBJS#{sep}) $(CLEANFILES#{sep}) + +distclean: clean + @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log + @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES#{sep}) + +realclean: distclean +" + +if not $extmk and /\A(extconf|makefile).rb\z/ =~ File.basename($0) + END {mkmf_failed($0)} +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/monitor.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/monitor.rb new file mode 100644 index 0000000000..4c146c8086 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/monitor.rb @@ -0,0 +1,352 @@ +=begin + += monitor.rb + +Copyright (C) 2001 Shugo Maeda + +This library is distributed under the terms of the Ruby license. +You can freely distribute/modify this library. + +== example + +This is a simple example. + + require 'monitor.rb' + + buf = [] + buf.extend(MonitorMixin) + empty_cond = buf.new_cond + + # consumer + Thread.start do + loop do + buf.synchronize do + empty_cond.wait_while { buf.empty? } + print buf.shift + end + end + end + + # producer + while line = ARGF.gets + buf.synchronize do + buf.push(line) + empty_cond.signal + end + end + +The consumer thread waits for the producer thread to push a line +to buf while buf.empty?, and the producer thread (main thread) +reads a line from ARGF and push it to buf, then call +empty_cond.signal. + +=end + + +# +# Adds monitor functionality to an arbitrary object by mixing the module with +# +include+. For example: +# +# require 'monitor.rb' +# +# buf = [] +# buf.extend(MonitorMixin) +# empty_cond = buf.new_cond +# +# # consumer +# Thread.start do +# loop do +# buf.synchronize do +# empty_cond.wait_while { buf.empty? } +# print buf.shift +# end +# end +# end +# +# # producer +# while line = ARGF.gets +# buf.synchronize do +# buf.push(line) +# empty_cond.signal +# end +# end +# +# The consumer thread waits for the producer thread to push a line +# to buf while buf.empty?, and the producer thread (main thread) +# reads a line from ARGF and push it to buf, then call +# empty_cond.signal. +# +module MonitorMixin + # + # FIXME: This isn't documented in Nutshell. + # + # Since MonitorMixin.new_cond returns a ConditionVariable, and the example + # above calls while_wait and signal, this class should be documented. + # + class ConditionVariable + class Timeout < Exception; end + + # Create a new timer with the argument timeout, and add the + # current thread to the list of waiters. Then the thread is + # stopped. It will be resumed when a corresponding #signal + # occurs. + def wait(timeout = nil) + @monitor.instance_eval {mon_check_owner()} + timer = create_timer(timeout) + + Thread.critical = true + count = @monitor.instance_eval {mon_exit_for_cond()} + @waiters.push(Thread.current) + + begin + Thread.stop + return true + rescue Timeout + return false + ensure + Thread.critical = true + begin + if timer && timer.alive? + Thread.kill(timer) + end + if @waiters.include?(Thread.current) # interrupted? + @waiters.delete(Thread.current) + end + @monitor.instance_eval {mon_enter_for_cond(count)} + ensure + Thread.critical = false + end + end + end + + + # call #wait while the supplied block returns +true+. + def wait_while + while yield + wait + end + end + + # call #wait until the supplied block returns +true+. + def wait_until + until yield + wait + end + end + + # Wake up and run the next waiter + def signal + @monitor.instance_eval {mon_check_owner()} + Thread.critical = true + t = @waiters.shift + t.wakeup if t + Thread.critical = false + Thread.pass + end + + # Wake up all the waiters. + def broadcast + @monitor.instance_eval {mon_check_owner()} + Thread.critical = true + for t in @waiters + t.wakeup + end + @waiters.clear + Thread.critical = false + Thread.pass + end + + def count_waiters + return @waiters.length + end + + private + + def initialize(monitor) + @monitor = monitor + @waiters = [] + end + + def create_timer(timeout) + if timeout + waiter = Thread.current + return Thread.start { + Thread.pass + sleep(timeout) + Thread.critical = true + waiter.raise(Timeout.new) + } + else + return nil + end + end + end + + def self.extend_object(obj) + super(obj) + obj.instance_eval {mon_initialize()} + end + + # + # Attempts to enter exclusive section. Returns +false+ if lock fails. + # + def mon_try_enter + result = false + Thread.critical = true + if @mon_owner.nil? + @mon_owner = Thread.current + end + if @mon_owner == Thread.current + @mon_count += 1 + result = true + end + Thread.critical = false + return result + end + # For backward compatibility + alias try_mon_enter mon_try_enter + + # + # Enters exclusive section. + # + def mon_enter + Thread.critical = true + mon_acquire(@mon_entering_queue) + @mon_count += 1 + ensure + Thread.critical = false + end + + # + # Leaves exclusive section. + # + def mon_exit + mon_check_owner + Thread.critical = true + @mon_count -= 1 + if @mon_count == 0 + mon_release + end + Thread.critical = false + Thread.pass + end + + # + # Enters exclusive section and executes the block. Leaves the exclusive + # section automatically when the block exits. See example under + # +MonitorMixin+. + # + def mon_synchronize + mon_enter + begin + yield + ensure + mon_exit + end + end + alias synchronize mon_synchronize + + # + # FIXME: This isn't documented in Nutshell. + # + # Create a new condition variable for this monitor. + # This facilitates control of the monitor with #signal and #wait. + # + def new_cond + return ConditionVariable.new(self) + end + + private + + def initialize(*args) + super + mon_initialize + end + + # called by initialize method to set defaults for instance variables. + def mon_initialize + @mon_owner = nil + @mon_count = 0 + @mon_entering_queue = [] + @mon_waiting_queue = [] + end + + # Throw a ThreadError exception if the current thread + # does't own the monitor + def mon_check_owner + if @mon_owner != Thread.current + raise ThreadError, "current thread not owner" + end + end + + def mon_acquire(queue) + while @mon_owner && @mon_owner != Thread.current + queue.push(Thread.current) + Thread.stop + Thread.critical = true + end + @mon_owner = Thread.current + end + + def mon_release + @mon_owner = nil + t = @mon_waiting_queue.shift + t = @mon_entering_queue.shift unless t + t.wakeup if t + end + + def mon_enter_for_cond(count) + mon_acquire(@mon_waiting_queue) + @mon_count = count + end + + def mon_exit_for_cond + count = @mon_count + @mon_count = 0 + return count + ensure + mon_release + end +end + +# Monitors provide means of mutual exclusion for Thread programming. +# A critical region is created by means of the synchronize method, +# which takes a block. +# The condition variables (created with #new_cond) may be used +# to control the execution of a monitor with #signal and #wait. +# +# the Monitor class wraps MonitorMixin, and provides aliases +# alias try_enter try_mon_enter +# alias enter mon_enter +# alias exit mon_exit +# to access its methods more concisely. +class Monitor + include MonitorMixin + alias try_enter try_mon_enter + alias enter mon_enter + alias exit mon_exit +end + + +# Documentation comments: +# - All documentation comes from Nutshell. +# - MonitorMixin.new_cond appears in the example, but is not documented in +# Nutshell. +# - All the internals (internal modules Accessible and Initializable, class +# ConditionVariable) appear in RDoc. It might be good to hide them, by +# making them private, or marking them :nodoc:, etc. +# - The entire example from the RD section at the top is replicated in the RDoc +# comment for MonitorMixin. Does the RD section need to remain? +# - RDoc doesn't recognise aliases, so we have mon_synchronize documented, but +# not synchronize. +# - mon_owner is in Nutshell, but appears as an accessor in a separate module +# here, so is hard/impossible to RDoc. Some other useful accessors +# (mon_count and some queue stuff) are also in this module, and don't appear +# directly in the RDoc output. +# - in short, it may be worth changing the code layout in this file to make the +# documentation easier + +# Local variables: +# mode: Ruby +# tab-width: 8 +# End: diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/mutex_m.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/mutex_m.rb new file mode 100644 index 0000000000..8e0d42bc8d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/mutex_m.rb @@ -0,0 +1,122 @@ +#-- +# mutex_m.rb - +# $Release Version: 3.0$ +# $Revision: 1.7 $ +# $Date: 1998/02/27 04:28:57 $ +# Original from mutex.rb +# by Keiju ISHITSUKA(keiju@ishitsuka.com) +# modified by matz +# patched by akira yamada +#++ +# +# == Usage +# +# Extend an object and use it like a Mutex object: +# +# require "mutex_m.rb" +# obj = Object.new +# obj.extend Mutex_m +# # ... +# +# Or, include Mutex_m in a class to have its instances behave like a Mutex +# object: +# +# class Foo +# include Mutex_m +# # ... +# end +# +# obj = Foo.new + +module Mutex_m + def Mutex_m.define_aliases(cl) + cl.module_eval %q{ + alias locked? mu_locked? + alias lock mu_lock + alias unlock mu_unlock + alias try_lock mu_try_lock + alias synchronize mu_synchronize + } + end + + def Mutex_m.append_features(cl) + super + define_aliases(cl) unless cl.instance_of?(Module) + end + + def Mutex_m.extend_object(obj) + super + obj.mu_extended + end + + def mu_extended + unless (defined? locked? and + defined? lock and + defined? unlock and + defined? try_lock and + defined? synchronize) + Mutex_m.define_aliases(class<. +# +# Documentation by Gavin Sinclair, sourced from "Programming Ruby" (Hunt/Thomas) +# and "Ruby In a Nutshell" (Matsumoto), used with permission. +# +# This library is distributed under the terms of the Ruby license. +# You can freely distribute/modify this library. +# +# It is included in the Ruby standard library. +# +# See the Net::FTP class for an overview. +# + +require "socket" +require "monitor" + +module Net + + # :stopdoc: + class FTPError < StandardError; end + class FTPReplyError < FTPError; end + class FTPTempError < FTPError; end + class FTPPermError < FTPError; end + class FTPProtoError < FTPError; end + # :startdoc: + + # + # This class implements the File Transfer Protocol. If you have used a + # command-line FTP program, and are familiar with the commands, you will be + # able to use this class easily. Some extra features are included to take + # advantage of Ruby's style and strengths. + # + # == Example + # + # require 'net/ftp' + # + # === Example 1 + # + # ftp = Net::FTP.new('ftp.netlab.co.jp') + # ftp.login + # files = ftp.chdir('pub/lang/ruby/contrib') + # files = ftp.list('n*') + # ftp.getbinaryfile('nif.rb-0.91.gz', 'nif.gz', 1024) + # ftp.close + # + # === Example 2 + # + # Net::FTP.open('ftp.netlab.co.jp') do |ftp| + # ftp.login + # files = ftp.chdir('pub/lang/ruby/contrib') + # files = ftp.list('n*') + # ftp.getbinaryfile('nif.rb-0.91.gz', 'nif.gz', 1024) + # end + # + # == Major Methods + # + # The following are the methods most likely to be useful to users: + # - FTP.open + # - #getbinaryfile + # - #gettextfile + # - #putbinaryfile + # - #puttextfile + # - #chdir + # - #nlst + # - #size + # - #rename + # - #delete + # + class FTP + include MonitorMixin + + # :stopdoc: + FTP_PORT = 21 + CRLF = "\r\n" + DEFAULT_BLOCKSIZE = 4096 + # :startdoc: + + # When +true+, transfers are performed in binary mode. Default: +true+. + attr_accessor :binary + + # When +true+, the connection is in passive mode. Default: +false+. + attr_accessor :passive + + # When +true+, all traffic to and from the server is written + # to +$stdout+. Default: +false+. + attr_accessor :debug_mode + + # Sets or retrieves the +resume+ status, which decides whether incomplete + # transfers are resumed or restarted. Default: +false+. + attr_accessor :resume + + # The server's welcome message. + attr_reader :welcome + + # The server's last response code. + attr_reader :last_response_code + alias lastresp last_response_code + + # The server's last response. + attr_reader :last_response + + # + # A synonym for FTP.new, but with a mandatory host parameter. + # + # If a block is given, it is passed the +FTP+ object, which will be closed + # when the block finishes, or when an exception is raised. + # + def FTP.open(host, user = nil, passwd = nil, acct = nil) + if block_given? + ftp = new(host, user, passwd, acct) + begin + yield ftp + ensure + ftp.close + end + else + new(host, user, passwd, acct) + end + end + + # + # Creates and returns a new +FTP+ object. If a +host+ is given, a connection + # is made. Additionally, if the +user+ is given, the given user name, + # password, and (optionally) account are used to log in. See #login. + # + def initialize(host = nil, user = nil, passwd = nil, acct = nil) + super() + @binary = true + @passive = false + @debug_mode = false + @resume = false + if host + connect(host) + if user + login(user, passwd, acct) + end + end + end + + # Obsolete + def return_code + $stderr.puts("warning: Net::FTP#return_code is obsolete and do nothing") + return "\n" + end + + # Obsolete + def return_code=(s) + $stderr.puts("warning: Net::FTP#return_code= is obsolete and do nothing") + end + + def open_socket(host, port) + if defined? SOCKSsocket and ENV["SOCKS_SERVER"] + @passive = true + return SOCKSsocket.open(host, port) + else + return TCPSocket.open(host, port) + end + end + private :open_socket + + # + # Establishes an FTP connection to host, optionally overriding the default + # port. If the environment variable +SOCKS_SERVER+ is set, sets up the + # connection through a SOCKS proxy. Raises an exception (typically + # Errno::ECONNREFUSED) if the connection cannot be established. + # + def connect(host, port = FTP_PORT) + if @debug_mode + print "connect: ", host, ", ", port, "\n" + end + synchronize do + @sock = open_socket(host, port) + voidresp + end + end + + # + # WRITEME or make private + # + def set_socket(sock, get_greeting = true) + synchronize do + @sock = sock + if get_greeting + voidresp + end + end + end + + def sanitize(s) + if s =~ /^PASS /i + return s[0, 5] + "*" * (s.length - 5) + else + return s + end + end + private :sanitize + + def putline(line) + if @debug_mode + print "put: ", sanitize(line), "\n" + end + line = line + CRLF + @sock.write(line) + end + private :putline + + def getline + line = @sock.readline # if get EOF, raise EOFError + line.sub!(/(\r\n|\n|\r)\z/n, "") + if @debug_mode + print "get: ", sanitize(line), "\n" + end + return line + end + private :getline + + def getmultiline + line = getline + buff = line + if line[3] == ?- + code = line[0, 3] + begin + line = getline + buff << "\n" << line + end until line[0, 3] == code and line[3] != ?- + end + return buff << "\n" + end + private :getmultiline + + def getresp + @last_response = getmultiline + @last_response_code = @last_response[0, 3] + case @last_response_code + when /\A[123]/ + return @last_response + when /\A4/ + raise FTPTempError, @last_response + when /\A5/ + raise FTPPermError, @last_response + else + raise FTPProtoError, @last_response + end + end + private :getresp + + def voidresp + resp = getresp + if resp[0] != ?2 + raise FTPReplyError, resp + end + end + private :voidresp + + # + # Sends a command and returns the response. + # + def sendcmd(cmd) + synchronize do + putline(cmd) + return getresp + end + end + + # + # Sends a command and expect a response beginning with '2'. + # + def voidcmd(cmd) + synchronize do + putline(cmd) + voidresp + end + end + + def sendport(host, port) + af = (@sock.peeraddr)[0] + if af == "AF_INET" + hbytes = host.split(".") + pbytes = [port / 256, port % 256] + bytes = hbytes + pbytes + cmd = "PORT " + bytes.join(",") + elsif af == "AF_INET6" + cmd = "EPRT |2|" + host + "|" + sprintf("%d", port) + "|" + else + raise FTPProtoError, host + end + voidcmd(cmd) + end + private :sendport + + def makeport + sock = TCPServer.open(@sock.addr[3], 0) + port = sock.addr[1] + host = sock.addr[3] + resp = sendport(host, port) + return sock + end + private :makeport + + def makepasv + if @sock.peeraddr[0] == "AF_INET" + host, port = parse227(sendcmd("PASV")) + else + host, port = parse229(sendcmd("EPSV")) + # host, port = parse228(sendcmd("LPSV")) + end + return host, port + end + private :makepasv + + def transfercmd(cmd, rest_offset = nil) + if @passive + host, port = makepasv + conn = open_socket(host, port) + if @resume and rest_offset + resp = sendcmd("REST " + rest_offset.to_s) + if resp[0] != ?3 + raise FTPReplyError, resp + end + end + resp = sendcmd(cmd) + if resp[0] != ?1 + raise FTPReplyError, resp + end + else + sock = makeport + if @resume and rest_offset + resp = sendcmd("REST " + rest_offset.to_s) + if resp[0] != ?3 + raise FTPReplyError, resp + end + end + resp = sendcmd(cmd) + if resp[0] != ?1 + raise FTPReplyError, resp + end + conn = sock.accept + sock.close + end + return conn + end + private :transfercmd + + def getaddress + thishost = Socket.gethostname + if not thishost.index(".") + thishost = Socket.gethostbyname(thishost)[0] + end + if ENV.has_key?("LOGNAME") + realuser = ENV["LOGNAME"] + elsif ENV.has_key?("USER") + realuser = ENV["USER"] + else + realuser = "anonymous" + end + return realuser + "@" + thishost + end + private :getaddress + + # + # Logs in to the remote host. The session must have been previously + # connected. If +user+ is the string "anonymous" and the +password+ is + # +nil+, a password of user@host is synthesized. If the +acct+ + # parameter is not +nil+, an FTP ACCT command is sent following the + # successful login. Raises an exception on error (typically + # Net::FTPPermError). + # + def login(user = "anonymous", passwd = nil, acct = nil) + if user == "anonymous" and passwd == nil + passwd = getaddress + end + + resp = "" + synchronize do + resp = sendcmd('USER ' + user) + if resp[0] == ?3 + resp = sendcmd('PASS ' + passwd) + end + if resp[0] == ?3 + resp = sendcmd('ACCT ' + acct) + end + end + if resp[0] != ?2 + raise FTPReplyError, resp + end + @welcome = resp + end + + # + # Puts the connection into binary (image) mode, issues the given command, + # and fetches the data returned, passing it to the associated block in + # chunks of +blocksize+ characters. Note that +cmd+ is a server command + # (such as "RETR myfile"). + # + def retrbinary(cmd, blocksize, rest_offset = nil) # :yield: data + synchronize do + voidcmd("TYPE I") + conn = transfercmd(cmd, rest_offset) + loop do + data = conn.read(blocksize) + break if data == nil + yield(data) + end + conn.close + voidresp + end + end + + # + # Puts the connection into ASCII (text) mode, issues the given command, and + # passes the resulting data, one line at a time, to the associated block. If + # no block is given, prints the lines. Note that +cmd+ is a server command + # (such as "RETR myfile"). + # + def retrlines(cmd) # :yield: line + synchronize do + voidcmd("TYPE A") + conn = transfercmd(cmd) + loop do + line = conn.gets + break if line == nil + if line[-2, 2] == CRLF + line = line[0 .. -3] + elsif line[-1] == ?\n + line = line[0 .. -2] + end + yield(line) + end + conn.close + voidresp + end + end + + # + # Puts the connection into binary (image) mode, issues the given server-side + # command (such as "STOR myfile"), and sends the contents of the file named + # +file+ to the server. If the optional block is given, it also passes it + # the data, in chunks of +blocksize+ characters. + # + def storbinary(cmd, file, blocksize, rest_offset = nil, &block) # :yield: data + if rest_offset + file.seek(rest_offset, IO::SEEK_SET) + end + synchronize do + voidcmd("TYPE I") + conn = transfercmd(cmd, rest_offset) + loop do + buf = file.read(blocksize) + break if buf == nil + conn.write(buf) + yield(buf) if block + end + conn.close + voidresp + end + end + + # + # Puts the connection into ASCII (text) mode, issues the given server-side + # command (such as "STOR myfile"), and sends the contents of the file + # named +file+ to the server, one line at a time. If the optional block is + # given, it also passes it the lines. + # + def storlines(cmd, file, &block) # :yield: line + synchronize do + voidcmd("TYPE A") + conn = transfercmd(cmd) + loop do + buf = file.gets + break if buf == nil + if buf[-2, 2] != CRLF + buf = buf.chomp + CRLF + end + conn.write(buf) + yield(buf) if block + end + conn.close + voidresp + end + end + + # + # Retrieves +remotefile+ in binary mode, storing the result in +localfile+. + # If a block is supplied, it is passed the retrieved data in +blocksize+ + # chunks. + # + def getbinaryfile(remotefile, localfile = File.basename(remotefile), + blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data + if @resume + rest_offset = File.size?(localfile) + f = open(localfile, "a") + else + rest_offset = nil + f = open(localfile, "w") + end + begin + f.binmode + retrbinary("RETR " + remotefile, blocksize, rest_offset) do |data| + f.write(data) + yield(data) if block + end + ensure + f.close + end + end + + # + # Retrieves +remotefile+ in ASCII (text) mode, storing the result in + # +localfile+. If a block is supplied, it is passed the retrieved data one + # line at a time. + # + def gettextfile(remotefile, localfile = File.basename(remotefile), &block) # :yield: line + f = open(localfile, "w") + begin + retrlines("RETR " + remotefile) do |line| + f.puts(line) + yield(line) if block + end + ensure + f.close + end + end + + # + # Retrieves +remotefile+ in whatever mode the session is set (text or + # binary). See #gettextfile and #getbinaryfile. + # + def get(remotefile, localfile = File.basename(remotefile), + blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data + unless @binary + gettextfile(remotefile, localfile, &block) + else + getbinaryfile(remotefile, localfile, blocksize, &block) + end + end + + # + # Transfers +localfile+ to the server in binary mode, storing the result in + # +remotefile+. If a block is supplied, calls it, passing in the transmitted + # data in +blocksize+ chunks. + # + def putbinaryfile(localfile, remotefile = File.basename(localfile), + blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data + if @resume + begin + rest_offset = size(remotefile) + rescue Net::FTPPermError + rest_offset = nil + end + else + rest_offset = nil + end + f = open(localfile) + begin + f.binmode + storbinary("STOR " + remotefile, f, blocksize, rest_offset, &block) + ensure + f.close + end + end + + # + # Transfers +localfile+ to the server in ASCII (text) mode, storing the result + # in +remotefile+. If callback or an associated block is supplied, calls it, + # passing in the transmitted data one line at a time. + # + def puttextfile(localfile, remotefile = File.basename(localfile), &block) # :yield: line + f = open(localfile) + begin + storlines("STOR " + remotefile, f, &block) + ensure + f.close + end + end + + # + # Transfers +localfile+ to the server in whatever mode the session is set + # (text or binary). See #puttextfile and #putbinaryfile. + # + def put(localfile, remotefile = File.basename(localfile), + blocksize = DEFAULT_BLOCKSIZE, &block) + unless @binary + puttextfile(localfile, remotefile, &block) + else + putbinaryfile(localfile, remotefile, blocksize, &block) + end + end + + # + # Sends the ACCT command. TODO: more info. + # + def acct(account) + cmd = "ACCT " + account + voidcmd(cmd) + end + + # + # Returns an array of filenames in the remote directory. + # + def nlst(dir = nil) + cmd = "NLST" + if dir + cmd = cmd + " " + dir + end + files = [] + retrlines(cmd) do |line| + files.push(line) + end + return files + end + + # + # Returns an array of file information in the directory (the output is like + # `ls -l`). If a block is given, it iterates through the listing. + # + def list(*args, &block) # :yield: line + cmd = "LIST" + args.each do |arg| + cmd = cmd + " " + arg + end + if block + retrlines(cmd, &block) + else + lines = [] + retrlines(cmd) do |line| + lines << line + end + return lines + end + end + alias ls list + alias dir list + + # + # Renames a file on the server. + # + def rename(fromname, toname) + resp = sendcmd("RNFR " + fromname) + if resp[0] != ?3 + raise FTPReplyError, resp + end + voidcmd("RNTO " + toname) + end + + # + # Deletes a file on the server. + # + def delete(filename) + resp = sendcmd("DELE " + filename) + if resp[0, 3] == "250" + return + elsif resp[0] == ?5 + raise FTPPermError, resp + else + raise FTPReplyError, resp + end + end + + # + # Changes the (remote) directory. + # + def chdir(dirname) + if dirname == ".." + begin + voidcmd("CDUP") + return + rescue FTPPermError + if $![0, 3] != "500" + raise FTPPermError, $! + end + end + end + cmd = "CWD " + dirname + voidcmd(cmd) + end + + # + # Returns the size of the given (remote) filename. + # + def size(filename) + voidcmd("TYPE I") + resp = sendcmd("SIZE " + filename) + if resp[0, 3] != "213" + raise FTPReplyError, resp + end + return resp[3..-1].strip.to_i + end + + MDTM_REGEXP = /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/ # :nodoc: + + # + # Returns the last modification time of the (remote) file. If +local+ is + # +true+, it is returned as a local time, otherwise it's a UTC time. + # + def mtime(filename, local = false) + str = mdtm(filename) + ary = str.scan(MDTM_REGEXP)[0].collect {|i| i.to_i} + return local ? Time.local(*ary) : Time.gm(*ary) + end + + # + # Creates a remote directory. + # + def mkdir(dirname) + resp = sendcmd("MKD " + dirname) + return parse257(resp) + end + + # + # Removes a remote directory. + # + def rmdir(dirname) + voidcmd("RMD " + dirname) + end + + # + # Returns the current remote directory. + # + def pwd + resp = sendcmd("PWD") + return parse257(resp) + end + alias getdir pwd + + # + # Returns system information. + # + def system + resp = sendcmd("SYST") + if resp[0, 3] != "215" + raise FTPReplyError, resp + end + return resp[4 .. -1] + end + + # + # Aborts the previous command (ABOR command). + # + def abort + line = "ABOR" + CRLF + print "put: ABOR\n" if @debug_mode + @sock.send(line, Socket::MSG_OOB) + resp = getmultiline + unless ["426", "226", "225"].include?(resp[0, 3]) + raise FTPProtoError, resp + end + return resp + end + + # + # Returns the status (STAT command). + # + def status + line = "STAT" + CRLF + print "put: STAT\n" if @debug_mode + @sock.send(line, Socket::MSG_OOB) + return getresp + end + + # + # Issues the MDTM command. TODO: more info. + # + def mdtm(filename) + resp = sendcmd("MDTM " + filename) + if resp[0, 3] == "213" + return resp[3 .. -1].strip + end + end + + # + # Issues the HELP command. + # + def help(arg = nil) + cmd = "HELP" + if arg + cmd = cmd + " " + arg + end + sendcmd(cmd) + end + + # + # Exits the FTP session. + # + def quit + voidcmd("QUIT") + end + + # + # Issues a NOOP command. + # + def noop + voidcmd("NOOP") + end + + # + # Issues a SITE command. + # + def site(arg) + cmd = "SITE " + arg + voidcmd(cmd) + end + + # + # Closes the connection. Further operations are impossible until you open + # a new connection with #connect. + # + def close + @sock.close if @sock and not @sock.closed? + end + + # + # Returns +true+ iff the connection is closed. + # + def closed? + @sock == nil or @sock.closed? + end + + def parse227(resp) + if resp[0, 3] != "227" + raise FTPReplyError, resp + end + left = resp.index("(") + right = resp.index(")") + if left == nil or right == nil + raise FTPProtoError, resp + end + numbers = resp[left + 1 .. right - 1].split(",") + if numbers.length != 6 + raise FTPProtoError, resp + end + host = numbers[0, 4].join(".") + port = (numbers[4].to_i << 8) + numbers[5].to_i + return host, port + end + private :parse227 + + def parse228(resp) + if resp[0, 3] != "228" + raise FTPReplyError, resp + end + left = resp.index("(") + right = resp.index(")") + if left == nil or right == nil + raise FTPProtoError, resp + end + numbers = resp[left + 1 .. right - 1].split(",") + if numbers[0] == "4" + if numbers.length != 9 || numbers[1] != "4" || numbers[2 + 4] != "2" + raise FTPProtoError, resp + end + host = numbers[2, 4].join(".") + port = (numbers[7].to_i << 8) + numbers[8].to_i + elsif numbers[0] == "6" + if numbers.length != 21 || numbers[1] != "16" || numbers[2 + 16] != "2" + raise FTPProtoError, resp + end + v6 = ["", "", "", "", "", "", "", ""] + for i in 0 .. 7 + v6[i] = sprintf("%02x%02x", numbers[(i * 2) + 2].to_i, + numbers[(i * 2) + 3].to_i) + end + host = v6[0, 8].join(":") + port = (numbers[19].to_i << 8) + numbers[20].to_i + end + return host, port + end + private :parse228 + + def parse229(resp) + if resp[0, 3] != "229" + raise FTPReplyError, resp + end + left = resp.index("(") + right = resp.index(")") + if left == nil or right == nil + raise FTPProtoError, resp + end + numbers = resp[left + 1 .. right - 1].split(resp[left + 1, 1]) + if numbers.length != 4 + raise FTPProtoError, resp + end + port = numbers[3].to_i + host = (@sock.peeraddr())[3] + return host, port + end + private :parse229 + + def parse257(resp) + if resp[0, 3] != "257" + raise FTPReplyError, resp + end + if resp[3, 2] != ' "' + return "" + end + dirname = "" + i = 5 + n = resp.length + while i < n + c = resp[i, 1] + i = i + 1 + if c == '"' + if i > n or resp[i, 1] != '"' + break + end + i = i + 1 + end + dirname = dirname + c + end + return dirname + end + private :parse257 + end + +end + + +# Documentation comments: +# - sourced from pickaxe and nutshell, with improvements (hopefully) +# - three methods should be private (search WRITEME) +# - two methods need more information (search TODO) diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/ftptls.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/ftptls.rb new file mode 100644 index 0000000000..4c4a49c3e7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/ftptls.rb @@ -0,0 +1,43 @@ +=begin += $RCSfile$ -- SSL/TLS enhancement for Net::HTTP. + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2003 Blaz Grilc + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Requirements + += Version + $Id: ftptls.rb 11708 2007-02-12 23:01:19Z shyouhei $ + += Notes + Tested on FreeBSD 5-CURRENT and 4-STABLE + - ruby 1.6.8 (2003-01-17) [i386-freebsd5] + - OpenSSL 0.9.7a Feb 19 2003 + - ruby-openssl-0.2.0.p0 + tested on ftp server: glftpd 1.30 +=end + +require 'socket' +require 'openssl' +require 'net/ftp' + +module Net + class FTPTLS < FTP + def login(user = "anonymous", passwd = nil, acct = nil) + ctx = OpenSSL::SSL::SSLContext.new('SSLv23') + ctx.key = nil + ctx.cert = nil + voidcmd("AUTH TLS") + @sock = OpenSSL::SSL::SSLSocket.new(@sock, ctx) + @sock.connect + super(user, passwd, acct) + voidcmd("PBSZ 0") + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/http.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/http.rb new file mode 100644 index 0000000000..8b63a31e1a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/http.rb @@ -0,0 +1,2286 @@ +# +# = net/http.rb +# +# Copyright (c) 1999-2006 Yukihiro Matsumoto +# Copyright (c) 1999-2006 Minero Aoki +# Copyright (c) 2001 GOTOU Yuuzou +# +# Written and maintained by Minero Aoki . +# HTTPS support added by GOTOU Yuuzou . +# +# This file is derived from "http-access.rb". +# +# Documented by Minero Aoki; converted to RDoc by William Webber. +# +# This program is free software. You can re-distribute and/or +# modify this program under the same terms of ruby itself --- +# Ruby Distribution License or GNU General Public License. +# +# See Net::HTTP for an overview and examples. +# +# NOTE: You can find Japanese version of this document here: +# http://www.ruby-lang.org/ja/man/?cmd=view;name=net%2Fhttp.rb +# +#-- +# $Id: http.rb 13504 2007-09-24 08:12:24Z shyouhei $ +#++ + +require 'net/protocol' +require 'uri' + +module Net #:nodoc: + + # :stopdoc: + class HTTPBadResponse < StandardError; end + class HTTPHeaderSyntaxError < StandardError; end + # :startdoc: + + # == What Is This Library? + # + # This library provides your program functions to access WWW + # documents via HTTP, Hyper Text Transfer Protocol version 1.1. + # For details of HTTP, refer [RFC2616] + # (http://www.ietf.org/rfc/rfc2616.txt). + # + # == Examples + # + # === Getting Document From WWW Server + # + # Example #1: Simple GET+print + # + # require 'net/http' + # Net::HTTP.get_print 'www.example.com', '/index.html' + # + # Example #2: Simple GET+print by URL + # + # require 'net/http' + # require 'uri' + # Net::HTTP.get_print URI.parse('http://www.example.com/index.html') + # + # Example #3: More generic GET+print + # + # require 'net/http' + # require 'uri' + # + # url = URI.parse('http://www.example.com/index.html') + # res = Net::HTTP.start(url.host, url.port) {|http| + # http.get('/index.html') + # } + # puts res.body + # + # Example #4: More generic GET+print + # + # require 'net/http' + # + # url = URI.parse('http://www.example.com/index.html') + # req = Net::HTTP::Get.new(url.path) + # res = Net::HTTP.start(url.host, url.port) {|http| + # http.request(req) + # } + # puts res.body + # + # === Posting Form Data + # + # require 'net/http' + # require 'uri' + # + # #1: Simple POST + # res = Net::HTTP.post_form(URI.parse('http://www.example.com/search.cgi'), + # {'q'=>'ruby', 'max'=>'50'}) + # puts res.body + # + # #2: POST with basic authentication + # res = Net::HTTP.post_form(URI.parse('http://jack:pass@www.example.com/todo.cgi'), + # {'from'=>'2005-01-01', 'to'=>'2005-03-31'}) + # puts res.body + # + # #3: Detailed control + # url = URI.parse('http://www.example.com/todo.cgi') + # req = Net::HTTP::Post.new(url.path) + # req.basic_auth 'jack', 'pass' + # req.set_form_data({'from'=>'2005-01-01', 'to'=>'2005-03-31'}, ';') + # res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) } + # case res + # when Net::HTTPSuccess, Net::HTTPRedirection + # # OK + # else + # res.error! + # end + # + # === Accessing via Proxy + # + # Net::HTTP.Proxy creates http proxy class. It has same + # methods of Net::HTTP but its instances always connect to + # proxy, instead of given host. + # + # require 'net/http' + # + # proxy_addr = 'your.proxy.host' + # proxy_port = 8080 + # : + # Net::HTTP::Proxy(proxy_addr, proxy_port).start('www.example.com') {|http| + # # always connect to your.proxy.addr:8080 + # : + # } + # + # Since Net::HTTP.Proxy returns Net::HTTP itself when proxy_addr is nil, + # there's no need to change code if there's proxy or not. + # + # There are two additional parameters in Net::HTTP.Proxy which allow to + # specify proxy user name and password: + # + # Net::HTTP::Proxy(proxy_addr, proxy_port, proxy_user = nil, proxy_pass = nil) + # + # You may use them to work with authorization-enabled proxies: + # + # require 'net/http' + # require 'uri' + # + # proxy_host = 'your.proxy.host' + # proxy_port = 8080 + # uri = URI.parse(ENV['http_proxy']) + # proxy_user, proxy_pass = uri.userinfo.split(/:/) if uri.userinfo + # Net::HTTP::Proxy(proxy_host, proxy_port, + # proxy_user, proxy_pass).start('www.example.com') {|http| + # # always connect to your.proxy.addr:8080 using specified username and password + # : + # } + # + # Note that net/http never rely on HTTP_PROXY environment variable. + # If you want to use proxy, set it explicitly. + # + # === Following Redirection + # + # require 'net/http' + # require 'uri' + # + # def fetch(uri_str, limit = 10) + # # You should choose better exception. + # raise ArgumentError, 'HTTP redirect too deep' if limit == 0 + # + # response = Net::HTTP.get_response(URI.parse(uri_str)) + # case response + # when Net::HTTPSuccess then response + # when Net::HTTPRedirection then fetch(response['location'], limit - 1) + # else + # response.error! + # end + # end + # + # print fetch('http://www.ruby-lang.org') + # + # Net::HTTPSuccess and Net::HTTPRedirection is a HTTPResponse class. + # All HTTPResponse objects belong to its own response class which + # indicate HTTP result status. For details of response classes, + # see section "HTTP Response Classes". + # + # === Basic Authentication + # + # require 'net/http' + # + # Net::HTTP.start('www.example.com') {|http| + # req = Net::HTTP::Get.new('/secret-page.html') + # req.basic_auth 'account', 'password' + # response = http.request(req) + # print response.body + # } + # + # === HTTP Request Classes + # + # Here is HTTP request class hierarchy. + # + # Net::HTTPRequest + # Net::HTTP::Get + # Net::HTTP::Head + # Net::HTTP::Post + # Net::HTTP::Put + # Net::HTTP::Proppatch + # Net::HTTP::Lock + # Net::HTTP::Unlock + # Net::HTTP::Options + # Net::HTTP::Propfind + # Net::HTTP::Delete + # Net::HTTP::Move + # Net::HTTP::Copy + # Net::HTTP::Mkcol + # Net::HTTP::Trace + # + # === HTTP Response Classes + # + # Here is HTTP response class hierarchy. + # All classes are defined in Net module. + # + # HTTPResponse + # HTTPUnknownResponse + # HTTPInformation # 1xx + # HTTPContinue # 100 + # HTTPSwitchProtocl # 101 + # HTTPSuccess # 2xx + # HTTPOK # 200 + # HTTPCreated # 201 + # HTTPAccepted # 202 + # HTTPNonAuthoritativeInformation # 203 + # HTTPNoContent # 204 + # HTTPResetContent # 205 + # HTTPPartialContent # 206 + # HTTPRedirection # 3xx + # HTTPMultipleChoice # 300 + # HTTPMovedPermanently # 301 + # HTTPFound # 302 + # HTTPSeeOther # 303 + # HTTPNotModified # 304 + # HTTPUseProxy # 305 + # HTTPTemporaryRedirect # 307 + # HTTPClientError # 4xx + # HTTPBadRequest # 400 + # HTTPUnauthorized # 401 + # HTTPPaymentRequired # 402 + # HTTPForbidden # 403 + # HTTPNotFound # 404 + # HTTPMethodNotAllowed # 405 + # HTTPNotAcceptable # 406 + # HTTPProxyAuthenticationRequired # 407 + # HTTPRequestTimeOut # 408 + # HTTPConflict # 409 + # HTTPGone # 410 + # HTTPLengthRequired # 411 + # HTTPPreconditionFailed # 412 + # HTTPRequestEntityTooLarge # 413 + # HTTPRequestURITooLong # 414 + # HTTPUnsupportedMediaType # 415 + # HTTPRequestedRangeNotSatisfiable # 416 + # HTTPExpectationFailed # 417 + # HTTPServerError # 5xx + # HTTPInternalServerError # 500 + # HTTPNotImplemented # 501 + # HTTPBadGateway # 502 + # HTTPServiceUnavailable # 503 + # HTTPGatewayTimeOut # 504 + # HTTPVersionNotSupported # 505 + # + # == Switching Net::HTTP versions + # + # You can use net/http.rb 1.1 features (bundled with Ruby 1.6) + # by calling HTTP.version_1_1. Calling Net::HTTP.version_1_2 + # allows you to use 1.2 features again. + # + # # example + # Net::HTTP.start {|http1| ...(http1 has 1.2 features)... } + # + # Net::HTTP.version_1_1 + # Net::HTTP.start {|http2| ...(http2 has 1.1 features)... } + # + # Net::HTTP.version_1_2 + # Net::HTTP.start {|http3| ...(http3 has 1.2 features)... } + # + # This function is NOT thread-safe. + # + class HTTP < Protocol + + # :stopdoc: + Revision = %q$Revision: 13504 $.split[1] + HTTPVersion = '1.1' + @newimpl = true + # :startdoc: + + # Turns on net/http 1.2 (ruby 1.8) features. + # Defaults to ON in ruby 1.8. + # + # I strongly recommend to call this method always. + # + # require 'net/http' + # Net::HTTP.version_1_2 + # + def HTTP.version_1_2 + @newimpl = true + end + + # Turns on net/http 1.1 (ruby 1.6) features. + # Defaults to OFF in ruby 1.8. + def HTTP.version_1_1 + @newimpl = false + end + + # true if net/http is in version 1.2 mode. + # Defaults to true. + def HTTP.version_1_2? + @newimpl + end + + # true if net/http is in version 1.1 compatible mode. + # Defaults to true. + def HTTP.version_1_1? + not @newimpl + end + + class << HTTP + alias is_version_1_1? version_1_1? #:nodoc: + alias is_version_1_2? version_1_2? #:nodoc: + end + + # + # short cut methods + # + + # + # Get body from target and output it to +$stdout+. The + # target can either be specified as (+uri+), or as + # (+host+, +path+, +port+ = 80); so: + # + # Net::HTTP.get_print URI.parse('http://www.example.com/index.html') + # + # or: + # + # Net::HTTP.get_print 'www.example.com', '/index.html' + # + def HTTP.get_print(uri_or_host, path = nil, port = nil) + get_response(uri_or_host, path, port) {|res| + res.read_body do |chunk| + $stdout.print chunk + end + } + nil + end + + # Send a GET request to the target and return the response + # as a string. The target can either be specified as + # (+uri+), or as (+host+, +path+, +port+ = 80); so: + # + # print Net::HTTP.get(URI.parse('http://www.example.com/index.html')) + # + # or: + # + # print Net::HTTP.get('www.example.com', '/index.html') + # + def HTTP.get(uri_or_host, path = nil, port = nil) + get_response(uri_or_host, path, port).body + end + + # Send a GET request to the target and return the response + # as a Net::HTTPResponse object. The target can either be specified as + # (+uri+), or as (+host+, +path+, +port+ = 80); so: + # + # res = Net::HTTP.get_response(URI.parse('http://www.example.com/index.html')) + # print res.body + # + # or: + # + # res = Net::HTTP.get_response('www.example.com', '/index.html') + # print res.body + # + def HTTP.get_response(uri_or_host, path = nil, port = nil, &block) + if path + host = uri_or_host + new(host, port || HTTP.default_port).start {|http| + return http.request_get(path, &block) + } + else + uri = uri_or_host + new(uri.host, uri.port).start {|http| + return http.request_get(uri.request_uri, &block) + } + end + end + + # Posts HTML form data to the +URL+. + # Form data must be represented as a Hash of String to String, e.g: + # + # { "cmd" => "search", "q" => "ruby", "max" => "50" } + # + # This method also does Basic Authentication iff +URL+.user exists. + # + # Example: + # + # require 'net/http' + # require 'uri' + # + # HTTP.post_form URI.parse('http://www.example.com/search.cgi'), + # { "q" => "ruby", "max" => "50" } + # + def HTTP.post_form(url, params) + req = Post.new(url.path) + req.form_data = params + req.basic_auth url.user, url.password if url.user + new(url.host, url.port).start {|http| + http.request(req) + } + end + + # + # HTTP session management + # + + # The default port to use for HTTP requests; defaults to 80. + def HTTP.default_port + http_default_port() + end + + # The default port to use for HTTP requests; defaults to 80. + def HTTP.http_default_port + 80 + end + + # The default port to use for HTTPS requests; defaults to 443. + def HTTP.https_default_port + 443 + end + + def HTTP.socket_type #:nodoc: obsolete + BufferedIO + end + + # creates a new Net::HTTP object and opens its TCP connection and + # HTTP session. If the optional block is given, the newly + # created Net::HTTP object is passed to it and closed when the + # block finishes. In this case, the return value of this method + # is the return value of the block. If no block is given, the + # return value of this method is the newly created Net::HTTP object + # itself, and the caller is responsible for closing it upon completion. + def HTTP.start(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil, &block) # :yield: +http+ + new(address, port, p_addr, p_port, p_user, p_pass).start(&block) + end + + class << HTTP + alias newobj new + end + + # Creates a new Net::HTTP object. + # If +proxy_addr+ is given, creates an Net::HTTP object with proxy support. + # This method does not open the TCP connection. + def HTTP.new(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil) + h = Proxy(p_addr, p_port, p_user, p_pass).newobj(address, port) + h.instance_eval { + @newimpl = ::Net::HTTP.version_1_2? + } + h + end + + # Creates a new Net::HTTP object for the specified +address+. + # This method does not open the TCP connection. + def initialize(address, port = nil) + @address = address + @port = (port || HTTP.default_port) + @curr_http_version = HTTPVersion + @seems_1_0_server = false + @close_on_empty_response = false + @socket = nil + @started = false + @open_timeout = nil + @read_timeout = 60 + @debug_output = nil + @use_ssl = false + @ssl_context = nil + @enable_post_connection_check = false + end + + def inspect + "#<#{self.class} #{@address}:#{@port} open=#{started?}>" + end + + # *WARNING* This method causes serious security hole. + # Never use this method in production code. + # + # Set an output stream for debugging. + # + # http = Net::HTTP.new + # http.set_debug_output $stderr + # http.start { .... } + # + def set_debug_output(output) + warn 'Net::HTTP#set_debug_output called after HTTP started' if started? + @debug_output = output + end + + # The host name to connect to. + attr_reader :address + + # The port number to connect to. + attr_reader :port + + # Seconds to wait until connection is opened. + # If the HTTP object cannot open a connection in this many seconds, + # it raises a TimeoutError exception. + attr_accessor :open_timeout + + # Seconds to wait until reading one block (by one read(2) call). + # If the HTTP object cannot open a connection in this many seconds, + # it raises a TimeoutError exception. + attr_reader :read_timeout + + # Setter for the read_timeout attribute. + def read_timeout=(sec) + @socket.read_timeout = sec if @socket + @read_timeout = sec + end + + # returns true if the HTTP session is started. + def started? + @started + end + + alias active? started? #:nodoc: obsolete + + attr_accessor :close_on_empty_response + + # returns true if use SSL/TLS with HTTP. + def use_ssl? + false # redefined in net/https + end + + # specify enabling SSL server certificate and hostname checking. + attr_accessor :enable_post_connection_check + + # Opens TCP connection and HTTP session. + # + # When this method is called with block, gives a HTTP object + # to the block and closes the TCP connection / HTTP session + # after the block executed. + # + # When called with a block, returns the return value of the + # block; otherwise, returns self. + # + def start # :yield: http + raise IOError, 'HTTP session already opened' if @started + if block_given? + begin + do_start + return yield(self) + ensure + do_finish + end + end + do_start + self + end + + def do_start + connect + @started = true + end + private :do_start + + def connect + D "opening connection to #{conn_address()}..." + s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) } + D "opened" + if use_ssl? + unless @ssl_context.verify_mode + warn "warning: peer certificate won't be verified in this SSL session" + @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE + end + s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context) + s.sync_close = true + end + @socket = BufferedIO.new(s) + @socket.read_timeout = @read_timeout + @socket.debug_output = @debug_output + if use_ssl? + if proxy? + @socket.writeline sprintf('CONNECT %s:%s HTTP/%s', + @address, @port, HTTPVersion) + @socket.writeline "Host: #{@address}:#{@port}" + if proxy_user + credential = ["#{proxy_user}:#{proxy_pass}"].pack('m') + credential.delete!("\r\n") + @socket.writeline "Proxy-Authorization: Basic #{credential}" + end + @socket.writeline '' + HTTPResponse.read_new(@socket).value + end + s.connect + if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE + begin + s.post_connection_check(@address) + rescue OpenSSL::SSL::SSLError => ex + raise ex if @enable_post_connection_check + warn ex.message + end + end + end + on_connect + end + private :connect + + def on_connect + end + private :on_connect + + # Finishes HTTP session and closes TCP connection. + # Raises IOError if not started. + def finish + raise IOError, 'HTTP session not yet started' unless started? + do_finish + end + + def do_finish + @started = false + @socket.close if @socket and not @socket.closed? + @socket = nil + end + private :do_finish + + # + # proxy + # + + public + + # no proxy + @is_proxy_class = false + @proxy_addr = nil + @proxy_port = nil + @proxy_user = nil + @proxy_pass = nil + + # Creates an HTTP proxy class. + # Arguments are address/port of proxy host and username/password + # if authorization on proxy server is required. + # You can replace the HTTP class with created proxy class. + # + # If ADDRESS is nil, this method returns self (Net::HTTP). + # + # # Example + # proxy_class = Net::HTTP::Proxy('proxy.example.com', 8080) + # : + # proxy_class.start('www.ruby-lang.org') {|http| + # # connecting proxy.foo.org:8080 + # : + # } + # + def HTTP.Proxy(p_addr, p_port = nil, p_user = nil, p_pass = nil) + return self unless p_addr + delta = ProxyDelta + proxyclass = Class.new(self) + proxyclass.module_eval { + include delta + # with proxy + @is_proxy_class = true + @proxy_address = p_addr + @proxy_port = p_port || default_port() + @proxy_user = p_user + @proxy_pass = p_pass + } + proxyclass + end + + class << HTTP + # returns true if self is a class which was created by HTTP::Proxy. + def proxy_class? + @is_proxy_class + end + + attr_reader :proxy_address + attr_reader :proxy_port + attr_reader :proxy_user + attr_reader :proxy_pass + end + + # True if self is a HTTP proxy class. + def proxy? + self.class.proxy_class? + end + + # Address of proxy host. If self does not use a proxy, nil. + def proxy_address + self.class.proxy_address + end + + # Port number of proxy host. If self does not use a proxy, nil. + def proxy_port + self.class.proxy_port + end + + # User name for accessing proxy. If self does not use a proxy, nil. + def proxy_user + self.class.proxy_user + end + + # User password for accessing proxy. If self does not use a proxy, nil. + def proxy_pass + self.class.proxy_pass + end + + alias proxyaddr proxy_address #:nodoc: obsolete + alias proxyport proxy_port #:nodoc: obsolete + + private + + # without proxy + + def conn_address + address() + end + + def conn_port + port() + end + + def edit_path(path) + path + end + + module ProxyDelta #:nodoc: internal use only + private + + def conn_address + proxy_address() + end + + def conn_port + proxy_port() + end + + def edit_path(path) + use_ssl? ? path : "http://#{addr_port()}#{path}" + end + end + + # + # HTTP operations + # + + public + + # Gets data from +path+ on the connected-to host. + # +header+ must be a Hash like { 'Accept' => '*/*', ... }. + # + # In version 1.1 (ruby 1.6), this method returns a pair of objects, + # a Net::HTTPResponse object and the entity body string. + # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse + # object. + # + # If called with a block, yields each fragment of the + # entity body in turn as a string as it is read from + # the socket. Note that in this case, the returned response + # object will *not* contain a (meaningful) body. + # + # +dest+ argument is obsolete. + # It still works but you must not use it. + # + # In version 1.1, this method might raise an exception for + # 3xx (redirect). In this case you can get a HTTPResponse object + # by "anException.response". + # + # In version 1.2, this method never raises exception. + # + # # version 1.1 (bundled with Ruby 1.6) + # response, body = http.get('/index.html') + # + # # version 1.2 (bundled with Ruby 1.8 or later) + # response = http.get('/index.html') + # + # # using block + # File.open('result.txt', 'w') {|f| + # http.get('/~foo/') do |str| + # f.write str + # end + # } + # + def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+ + res = nil + request(Get.new(path, initheader)) {|r| + r.read_body dest, &block + res = r + } + unless @newimpl + res.value + return res, res.body + end + + res + end + + # Gets only the header from +path+ on the connected-to host. + # +header+ is a Hash like { 'Accept' => '*/*', ... }. + # + # This method returns a Net::HTTPResponse object. + # + # In version 1.1, this method might raise an exception for + # 3xx (redirect). On the case you can get a HTTPResponse object + # by "anException.response". + # In version 1.2, this method never raises an exception. + # + # response = nil + # Net::HTTP.start('some.www.server', 80) {|http| + # response = http.head('/index.html') + # } + # p response['content-type'] + # + def head(path, initheader = nil) + res = request(Head.new(path, initheader)) + res.value unless @newimpl + res + end + + # Posts +data+ (must be a String) to +path+. +header+ must be a Hash + # like { 'Accept' => '*/*', ... }. + # + # In version 1.1 (ruby 1.6), this method returns a pair of objects, a + # Net::HTTPResponse object and an entity body string. + # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse object. + # + # If called with a block, yields each fragment of the + # entity body in turn as a string as it are read from + # the socket. Note that in this case, the returned response + # object will *not* contain a (meaningful) body. + # + # +dest+ argument is obsolete. + # It still works but you must not use it. + # + # In version 1.1, this method might raise an exception for + # 3xx (redirect). In this case you can get an HTTPResponse object + # by "anException.response". + # In version 1.2, this method never raises exception. + # + # # version 1.1 + # response, body = http.post('/cgi-bin/search.rb', 'query=foo') + # + # # version 1.2 + # response = http.post('/cgi-bin/search.rb', 'query=foo') + # + # # using block + # File.open('result.txt', 'w') {|f| + # http.post('/cgi-bin/search.rb', 'query=foo') do |str| + # f.write str + # end + # } + # + # You should set Content-Type: header field for POST. + # If no Content-Type: field given, this method uses + # "application/x-www-form-urlencoded" by default. + # + def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+ + res = nil + request(Post.new(path, initheader), data) {|r| + r.read_body dest, &block + res = r + } + unless @newimpl + res.value + return res, res.body + end + res + end + + def put(path, data, initheader = nil) #:nodoc: + res = request(Put.new(path, initheader), data) + res.value unless @newimpl + res + end + + # Sends a PROPPATCH request to the +path+ and gets a response, + # as an HTTPResponse object. + def proppatch(path, body, initheader = nil) + request(Proppatch.new(path, initheader), body) + end + + # Sends a LOCK request to the +path+ and gets a response, + # as an HTTPResponse object. + def lock(path, body, initheader = nil) + request(Lock.new(path, initheader), body) + end + + # Sends a UNLOCK request to the +path+ and gets a response, + # as an HTTPResponse object. + def unlock(path, body, initheader = nil) + request(Unlock.new(path, initheader), body) + end + + # Sends a OPTIONS request to the +path+ and gets a response, + # as an HTTPResponse object. + def options(path, initheader = nil) + request(Options.new(path, initheader)) + end + + # Sends a PROPFIND request to the +path+ and gets a response, + # as an HTTPResponse object. + def propfind(path, body = nil, initheader = {'Depth' => '0'}) + request(Propfind.new(path, initheader), body) + end + + # Sends a DELETE request to the +path+ and gets a response, + # as an HTTPResponse object. + def delete(path, initheader = {'Depth' => 'Infinity'}) + request(Delete.new(path, initheader)) + end + + # Sends a MOVE request to the +path+ and gets a response, + # as an HTTPResponse object. + def move(path, initheader = nil) + request(Move.new(path, initheader)) + end + + # Sends a COPY request to the +path+ and gets a response, + # as an HTTPResponse object. + def copy(path, initheader = nil) + request(Copy.new(path, initheader)) + end + + # Sends a MKCOL request to the +path+ and gets a response, + # as an HTTPResponse object. + def mkcol(path, body = nil, initheader = nil) + request(Mkcol.new(path, initheader), body) + end + + # Sends a TRACE request to the +path+ and gets a response, + # as an HTTPResponse object. + def trace(path, initheader = nil) + request(Trace.new(path, initheader)) + end + + # Sends a GET request to the +path+ and gets a response, + # as an HTTPResponse object. + # + # When called with a block, yields an HTTPResponse object. + # The body of this response will not have been read yet; + # the caller can process it using HTTPResponse#read_body, + # if desired. + # + # Returns the response. + # + # This method never raises Net::* exceptions. + # + # response = http.request_get('/index.html') + # # The entity body is already read here. + # p response['content-type'] + # puts response.body + # + # # using block + # http.request_get('/index.html') {|response| + # p response['content-type'] + # response.read_body do |str| # read body now + # print str + # end + # } + # + def request_get(path, initheader = nil, &block) # :yield: +response+ + request(Get.new(path, initheader), &block) + end + + # Sends a HEAD request to the +path+ and gets a response, + # as an HTTPResponse object. + # + # Returns the response. + # + # This method never raises Net::* exceptions. + # + # response = http.request_head('/index.html') + # p response['content-type'] + # + def request_head(path, initheader = nil, &block) + request(Head.new(path, initheader), &block) + end + + # Sends a POST request to the +path+ and gets a response, + # as an HTTPResponse object. + # + # When called with a block, yields an HTTPResponse object. + # The body of this response will not have been read yet; + # the caller can process it using HTTPResponse#read_body, + # if desired. + # + # Returns the response. + # + # This method never raises Net::* exceptions. + # + # # example + # response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...') + # p response.status + # puts response.body # body is already read + # + # # using block + # http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response| + # p response.status + # p response['content-type'] + # response.read_body do |str| # read body now + # print str + # end + # } + # + def request_post(path, data, initheader = nil, &block) # :yield: +response+ + request Post.new(path, initheader), data, &block + end + + def request_put(path, data, initheader = nil, &block) #:nodoc: + request Put.new(path, initheader), data, &block + end + + alias get2 request_get #:nodoc: obsolete + alias head2 request_head #:nodoc: obsolete + alias post2 request_post #:nodoc: obsolete + alias put2 request_put #:nodoc: obsolete + + + # Sends an HTTP request to the HTTP server. + # This method also sends DATA string if DATA is given. + # + # Returns a HTTPResponse object. + # + # This method never raises Net::* exceptions. + # + # response = http.send_request('GET', '/index.html') + # puts response.body + # + def send_request(name, path, data = nil, header = nil) + r = HTTPGenericRequest.new(name,(data ? true : false),true,path,header) + request r, data + end + + # Sends an HTTPRequest object REQUEST to the HTTP server. + # This method also sends DATA string if REQUEST is a post/put request. + # Giving DATA for get/head request causes ArgumentError. + # + # When called with a block, yields an HTTPResponse object. + # The body of this response will not have been read yet; + # the caller can process it using HTTPResponse#read_body, + # if desired. + # + # Returns a HTTPResponse object. + # + # This method never raises Net::* exceptions. + # + def request(req, body = nil, &block) # :yield: +response+ + unless started? + start { + req['connection'] ||= 'close' + return request(req, body, &block) + } + end + if proxy_user() + unless use_ssl? + req.proxy_basic_auth proxy_user(), proxy_pass() + end + end + + req.set_body_internal body + begin_transport req + req.exec @socket, @curr_http_version, edit_path(req.path) + begin + res = HTTPResponse.read_new(@socket) + end while res.kind_of?(HTTPContinue) + res.reading_body(@socket, req.response_body_permitted?) { + yield res if block_given? + } + end_transport req, res + + res + end + + private + + def begin_transport(req) + if @socket.closed? + connect + end + if @seems_1_0_server + req['connection'] ||= 'close' + end + if not req.response_body_permitted? and @close_on_empty_response + req['connection'] ||= 'close' + end + req['host'] ||= addr_port() + end + + def end_transport(req, res) + @curr_http_version = res.http_version + if not res.body and @close_on_empty_response + D 'Conn close' + @socket.close + elsif keep_alive?(req, res) + D 'Conn keep-alive' + if @socket.closed? + D 'Conn (but seems 1.0 server)' + @seems_1_0_server = true + end + else + D 'Conn close' + @socket.close + end + end + + def keep_alive?(req, res) + return false if /close/i =~ req['connection'].to_s + return false if @seems_1_0_server + return true if /keep-alive/i =~ res['connection'].to_s + return false if /close/i =~ res['connection'].to_s + return true if /keep-alive/i =~ res['proxy-connection'].to_s + return false if /close/i =~ res['proxy-connection'].to_s + (@curr_http_version == '1.1') + end + + # + # utils + # + + private + + def addr_port + if use_ssl? + address() + (port == HTTP.https_default_port ? '' : ":#{port()}") + else + address() + (port == HTTP.http_default_port ? '' : ":#{port()}") + end + end + + def D(msg) + return unless @debug_output + @debug_output << msg + @debug_output << "\n" + end + + end + + HTTPSession = HTTP + + + # + # Header module. + # + # Provides access to @header in the mixed-into class as a hash-like + # object, except with case-insensitive keys. Also provides + # methods for accessing commonly-used header values in a more + # convenient format. + # + module HTTPHeader + + def initialize_http_header(initheader) + @header = {} + return unless initheader + initheader.each do |key, value| + warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE + @header[key.downcase] = [value.strip] + end + end + + def size #:nodoc: obsolete + @header.size + end + + alias length size #:nodoc: obsolete + + # Returns the header field corresponding to the case-insensitive key. + # For example, a key of "Content-Type" might return "text/html" + def [](key) + a = @header[key.downcase] or return nil + a.join(', ') + end + + # Sets the header field corresponding to the case-insensitive key. + def []=(key, val) + unless val + @header.delete key.downcase + return val + end + @header[key.downcase] = [val] + end + + # [Ruby 1.8.3] + # Adds header field instead of replace. + # Second argument +val+ must be a String. + # See also #[]=, #[] and #get_fields. + # + # request.add_field 'X-My-Header', 'a' + # p request['X-My-Header'] #=> "a" + # p request.get_fields('X-My-Header') #=> ["a"] + # request.add_field 'X-My-Header', 'b' + # p request['X-My-Header'] #=> "a, b" + # p request.get_fields('X-My-Header') #=> ["a", "b"] + # request.add_field 'X-My-Header', 'c' + # p request['X-My-Header'] #=> "a, b, c" + # p request.get_fields('X-My-Header') #=> ["a", "b", "c"] + # + def add_field(key, val) + if @header.key?(key.downcase) + @header[key.downcase].push val + else + @header[key.downcase] = [val] + end + end + + # [Ruby 1.8.3] + # Returns an array of header field strings corresponding to the + # case-insensitive +key+. This method allows you to get duplicated + # header fields without any processing. See also #[]. + # + # p response.get_fields('Set-Cookie') + # #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23", + # "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"] + # p response['Set-Cookie'] + # #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23" + # + def get_fields(key) + return nil unless @header[key.downcase] + @header[key.downcase].dup + end + + # Returns the header field corresponding to the case-insensitive key. + # Returns the default value +args+, or the result of the block, or nil, + # if there's no header field named key. See Hash#fetch + def fetch(key, *args, &block) #:yield: +key+ + a = @header.fetch(key.downcase, *args, &block) + a.join(', ') + end + + # Iterates for each header names and values. + def each_header #:yield: +key+, +value+ + @header.each do |k,va| + yield k, va.join(', ') + end + end + + alias each each_header + + # Iterates for each header names. + def each_name(&block) #:yield: +key+ + @header.each_key(&block) + end + + alias each_key each_name + + # Iterates for each capitalized header names. + def each_capitalized_name(&block) #:yield: +key+ + @header.each_key do |k| + yield capitalize(k) + end + end + + # Iterates for each header values. + def each_value #:yield: +value+ + @header.each_value do |va| + yield va.join(', ') + end + end + + # Removes a header field. + def delete(key) + @header.delete(key.downcase) + end + + # true if +key+ header exists. + def key?(key) + @header.key?(key.downcase) + end + + # Returns a Hash consist of header names and values. + def to_hash + @header.dup + end + + # As for #each_header, except the keys are provided in capitalized form. + def each_capitalized + @header.each do |k,v| + yield capitalize(k), v.join(', ') + end + end + + alias canonical_each each_capitalized + + def capitalize(name) + name.split(/-/).map {|s| s.capitalize }.join('-') + end + private :capitalize + + # Returns an Array of Range objects which represents Range: header field, + # or +nil+ if there is no such header. + def range + return nil unless @header['range'] + self['Range'].split(/,/).map {|spec| + m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match(spec) or + raise HTTPHeaderSyntaxError, "wrong Range: #{spec}" + d1 = m[1].to_i + d2 = m[2].to_i + if m[1] and m[2] then d1..d2 + elsif m[1] then d1..-1 + elsif m[2] then -d2..-1 + else + raise HTTPHeaderSyntaxError, 'range is not specified' + end + } + end + + # Set Range: header from Range (arg r) or beginning index and + # length from it (arg idx&len). + # + # req.range = (0..1023) + # req.set_range 0, 1023 + # + def set_range(r, e = nil) + unless r + @header.delete 'range' + return r + end + r = (r...r+e) if e + case r + when Numeric + n = r.to_i + rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}") + when Range + first = r.first + last = r.last + last -= 1 if r.exclude_end? + if last == -1 + rangestr = (first > 0 ? "#{first}-" : "-#{-first}") + else + raise HTTPHeaderSyntaxError, 'range.first is negative' if first < 0 + raise HTTPHeaderSyntaxError, 'range.last is negative' if last < 0 + raise HTTPHeaderSyntaxError, 'must be .first < .last' if first > last + rangestr = "#{first}-#{last}" + end + else + raise TypeError, 'Range/Integer is required' + end + @header['range'] = ["bytes=#{rangestr}"] + r + end + + alias range= set_range + + # Returns an Integer object which represents the Content-Length: header field + # or +nil+ if that field is not provided. + def content_length + return nil unless key?('Content-Length') + len = self['Content-Length'].slice(/\d+/) or + raise HTTPHeaderSyntaxError, 'wrong Content-Length format' + len.to_i + end + + def content_length=(len) + unless len + @header.delete 'content-length' + return nil + end + @header['content-length'] = [len.to_i.to_s] + end + + # Returns "true" if the "transfer-encoding" header is present and + # set to "chunked". This is an HTTP/1.1 feature, allowing the + # the content to be sent in "chunks" without at the outset + # stating the entire content length. + def chunked? + return false unless @header['transfer-encoding'] + field = self['Transfer-Encoding'] + (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false + end + + # Returns a Range object which represents Content-Range: header field. + # This indicates, for a partial entity body, where this fragment + # fits inside the full entity body, as range of byte offsets. + def content_range + return nil unless @header['content-range'] + m = %ri.match(self['Content-Range']) or + raise HTTPHeaderSyntaxError, 'wrong Content-Range format' + m[1].to_i .. m[2].to_i + 1 + end + + # The length of the range represented in Content-Range: header. + def range_length + r = content_range() or return nil + r.end - r.begin + end + + # Returns a content type string such as "text/html". + # This method returns nil if Content-Type: header field does not exist. + def content_type + return nil unless main_type() + if sub_type() + then "#{main_type()}/#{sub_type()}" + else main_type() + end + end + + # Returns a content type string such as "text". + # This method returns nil if Content-Type: header field does not exist. + def main_type + return nil unless @header['content-type'] + self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip + end + + # Returns a content type string such as "html". + # This method returns nil if Content-Type: header field does not exist + # or sub-type is not given (e.g. "Content-Type: text"). + def sub_type + return nil unless @header['content-type'] + main, sub = *self['Content-Type'].split(';').first.to_s.split('/') + return nil unless sub + sub.strip + end + + # Returns content type parameters as a Hash as like + # {"charset" => "iso-2022-jp"}. + def type_params + result = {} + list = self['Content-Type'].to_s.split(';') + list.shift + list.each do |param| + k, v = *param.split('=', 2) + result[k.strip] = v.strip + end + result + end + + # Set Content-Type: header field by +type+ and +params+. + # +type+ must be a String, +params+ must be a Hash. + def set_content_type(type, params = {}) + @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')] + end + + alias content_type= set_content_type + + # Set header fields and a body from HTML form data. + # +params+ should be a Hash containing HTML form data. + # Optional argument +sep+ means data record separator. + # + # This method also set Content-Type: header field to + # application/x-www-form-urlencoded. + def set_form_data(params, sep = '&') + self.body = params.map {|k,v| "#{urlencode(k.to_s)}=#{urlencode(v.to_s)}" }.join(sep) + self.content_type = 'application/x-www-form-urlencoded' + end + + alias form_data= set_form_data + + def urlencode(str) + str.gsub(/[^a-zA-Z0-9_\.\-]/n) {|s| sprintf('%%%02x', s[0]) } + end + private :urlencode + + # Set the Authorization: header for "Basic" authorization. + def basic_auth(account, password) + @header['authorization'] = [basic_encode(account, password)] + end + + # Set Proxy-Authorization: header for "Basic" authorization. + def proxy_basic_auth(account, password) + @header['proxy-authorization'] = [basic_encode(account, password)] + end + + def basic_encode(account, password) + 'Basic ' + ["#{account}:#{password}"].pack('m').delete("\r\n") + end + private :basic_encode + + end + + + # + # Parent of HTTPRequest class. Do not use this directly; use + # a subclass of HTTPRequest. + # + # Mixes in the HTTPHeader module. + # + class HTTPGenericRequest + + include HTTPHeader + + def initialize(m, reqbody, resbody, path, initheader = nil) + @method = m + @request_has_body = reqbody + @response_has_body = resbody + raise ArgumentError, "HTTP request path is empty" if path.empty? + @path = path + initialize_http_header initheader + self['Accept'] ||= '*/*' + @body = nil + @body_stream = nil + end + + attr_reader :method + attr_reader :path + + def inspect + "\#<#{self.class} #{@method}>" + end + + def request_body_permitted? + @request_has_body + end + + def response_body_permitted? + @response_has_body + end + + def body_exist? + warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?" if $VERBOSE + response_body_permitted? + end + + attr_reader :body + + def body=(str) + @body = str + @body_stream = nil + str + end + + attr_reader :body_stream + + def body_stream=(input) + @body = nil + @body_stream = input + input + end + + def set_body_internal(str) #:nodoc: internal use only + raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream) + self.body = str if str + end + + # + # write + # + + def exec(sock, ver, path) #:nodoc: internal use only + if @body + send_request_with_body sock, ver, path, @body + elsif @body_stream + send_request_with_body_stream sock, ver, path, @body_stream + else + write_header sock, ver, path + end + end + + private + + def send_request_with_body(sock, ver, path, body) + self.content_length = body.length + delete 'Transfer-Encoding' + supply_default_content_type + write_header sock, ver, path + sock.write body + end + + def send_request_with_body_stream(sock, ver, path, f) + unless content_length() or chunked? + raise ArgumentError, + "Content-Length not given and Transfer-Encoding is not `chunked'" + end + supply_default_content_type + write_header sock, ver, path + if chunked? + while s = f.read(1024) + sock.write(sprintf("%x\r\n", s.length) << s << "\r\n") + end + sock.write "0\r\n\r\n" + else + while s = f.read(1024) + sock.write s + end + end + end + + def supply_default_content_type + return if content_type() + warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE + set_content_type 'application/x-www-form-urlencoded' + end + + def write_header(sock, ver, path) + buf = "#{@method} #{path} HTTP/#{ver}\r\n" + each_capitalized do |k,v| + buf << "#{k}: #{v}\r\n" + end + buf << "\r\n" + sock.write buf + end + + end + + + # + # HTTP request class. This class wraps request header and entity path. + # You *must* use its subclass, Net::HTTP::Get, Post, Head. + # + class HTTPRequest < HTTPGenericRequest + + # Creates HTTP request object. + def initialize(path, initheader = nil) + super self.class::METHOD, + self.class::REQUEST_HAS_BODY, + self.class::RESPONSE_HAS_BODY, + path, initheader + end + end + + + class HTTP # reopen + # + # HTTP 1.1 methods --- RFC2616 + # + + class Get < HTTPRequest + METHOD = 'GET' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = true + end + + class Head < HTTPRequest + METHOD = 'HEAD' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = false + end + + class Post < HTTPRequest + METHOD = 'POST' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true + end + + class Put < HTTPRequest + METHOD = 'PUT' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true + end + + class Delete < HTTPRequest + METHOD = 'DELETE' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = true + end + + class Options < HTTPRequest + METHOD = 'OPTIONS' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = false + end + + class Trace < HTTPRequest + METHOD = 'TRACE' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = true + end + + # + # WebDAV methods --- RFC2518 + # + + class Propfind < HTTPRequest + METHOD = 'PROPFIND' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true + end + + class Proppatch < HTTPRequest + METHOD = 'PROPPATCH' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true + end + + class Mkcol < HTTPRequest + METHOD = 'MKCOL' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true + end + + class Copy < HTTPRequest + METHOD = 'COPY' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = true + end + + class Move < HTTPRequest + METHOD = 'MOVE' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = true + end + + class Lock < HTTPRequest + METHOD = 'LOCK' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true + end + + class Unlock < HTTPRequest + METHOD = 'UNLOCK' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true + end + end + + + ### + ### Response + ### + + # HTTP exception class. + # You must use its subclasses. + module HTTPExceptions + def initialize(msg, res) #:nodoc: + super msg + @response = res + end + attr_reader :response + alias data response #:nodoc: obsolete + end + class HTTPError < ProtocolError + include HTTPExceptions + end + class HTTPRetriableError < ProtoRetriableError + include HTTPExceptions + end + class HTTPServerException < ProtoServerError + # We cannot use the name "HTTPServerError", it is the name of the response. + include HTTPExceptions + end + class HTTPFatalError < ProtoFatalError + include HTTPExceptions + end + + + # HTTP response class. This class wraps response header and entity. + # Mixes in the HTTPHeader module, which provides access to response + # header values both via hash-like methods and individual readers. + # Note that each possible HTTP response code defines its own + # HTTPResponse subclass. These are listed below. + # All classes are + # defined under the Net module. Indentation indicates inheritance. + # + # xxx HTTPResponse + # + # 1xx HTTPInformation + # 100 HTTPContinue + # 101 HTTPSwitchProtocol + # + # 2xx HTTPSuccess + # 200 HTTPOK + # 201 HTTPCreated + # 202 HTTPAccepted + # 203 HTTPNonAuthoritativeInformation + # 204 HTTPNoContent + # 205 HTTPResetContent + # 206 HTTPPartialContent + # + # 3xx HTTPRedirection + # 300 HTTPMultipleChoice + # 301 HTTPMovedPermanently + # 302 HTTPFound + # 303 HTTPSeeOther + # 304 HTTPNotModified + # 305 HTTPUseProxy + # 307 HTTPTemporaryRedirect + # + # 4xx HTTPClientError + # 400 HTTPBadRequest + # 401 HTTPUnauthorized + # 402 HTTPPaymentRequired + # 403 HTTPForbidden + # 404 HTTPNotFound + # 405 HTTPMethodNotAllowed + # 406 HTTPNotAcceptable + # 407 HTTPProxyAuthenticationRequired + # 408 HTTPRequestTimeOut + # 409 HTTPConflict + # 410 HTTPGone + # 411 HTTPLengthRequired + # 412 HTTPPreconditionFailed + # 413 HTTPRequestEntityTooLarge + # 414 HTTPRequestURITooLong + # 415 HTTPUnsupportedMediaType + # 416 HTTPRequestedRangeNotSatisfiable + # 417 HTTPExpectationFailed + # + # 5xx HTTPServerError + # 500 HTTPInternalServerError + # 501 HTTPNotImplemented + # 502 HTTPBadGateway + # 503 HTTPServiceUnavailable + # 504 HTTPGatewayTimeOut + # 505 HTTPVersionNotSupported + # + # xxx HTTPUnknownResponse + # + class HTTPResponse + # true if the response has body. + def HTTPResponse.body_permitted? + self::HAS_BODY + end + + def HTTPResponse.exception_type # :nodoc: internal use only + self::EXCEPTION_TYPE + end + end # reopened after + + # :stopdoc: + + class HTTPUnknownResponse < HTTPResponse + HAS_BODY = true + EXCEPTION_TYPE = HTTPError + end + class HTTPInformation < HTTPResponse # 1xx + HAS_BODY = false + EXCEPTION_TYPE = HTTPError + end + class HTTPSuccess < HTTPResponse # 2xx + HAS_BODY = true + EXCEPTION_TYPE = HTTPError + end + class HTTPRedirection < HTTPResponse # 3xx + HAS_BODY = true + EXCEPTION_TYPE = HTTPRetriableError + end + class HTTPClientError < HTTPResponse # 4xx + HAS_BODY = true + EXCEPTION_TYPE = HTTPServerException # for backward compatibility + end + class HTTPServerError < HTTPResponse # 5xx + HAS_BODY = true + EXCEPTION_TYPE = HTTPFatalError # for backward compatibility + end + + class HTTPContinue < HTTPInformation # 100 + HAS_BODY = false + end + class HTTPSwitchProtocol < HTTPInformation # 101 + HAS_BODY = false + end + + class HTTPOK < HTTPSuccess # 200 + HAS_BODY = true + end + class HTTPCreated < HTTPSuccess # 201 + HAS_BODY = true + end + class HTTPAccepted < HTTPSuccess # 202 + HAS_BODY = true + end + class HTTPNonAuthoritativeInformation < HTTPSuccess # 203 + HAS_BODY = true + end + class HTTPNoContent < HTTPSuccess # 204 + HAS_BODY = false + end + class HTTPResetContent < HTTPSuccess # 205 + HAS_BODY = false + end + class HTTPPartialContent < HTTPSuccess # 206 + HAS_BODY = true + end + + class HTTPMultipleChoice < HTTPRedirection # 300 + HAS_BODY = true + end + class HTTPMovedPermanently < HTTPRedirection # 301 + HAS_BODY = true + end + class HTTPFound < HTTPRedirection # 302 + HAS_BODY = true + end + HTTPMovedTemporarily = HTTPFound + class HTTPSeeOther < HTTPRedirection # 303 + HAS_BODY = true + end + class HTTPNotModified < HTTPRedirection # 304 + HAS_BODY = false + end + class HTTPUseProxy < HTTPRedirection # 305 + HAS_BODY = false + end + # 306 unused + class HTTPTemporaryRedirect < HTTPRedirection # 307 + HAS_BODY = true + end + + class HTTPBadRequest < HTTPClientError # 400 + HAS_BODY = true + end + class HTTPUnauthorized < HTTPClientError # 401 + HAS_BODY = true + end + class HTTPPaymentRequired < HTTPClientError # 402 + HAS_BODY = true + end + class HTTPForbidden < HTTPClientError # 403 + HAS_BODY = true + end + class HTTPNotFound < HTTPClientError # 404 + HAS_BODY = true + end + class HTTPMethodNotAllowed < HTTPClientError # 405 + HAS_BODY = true + end + class HTTPNotAcceptable < HTTPClientError # 406 + HAS_BODY = true + end + class HTTPProxyAuthenticationRequired < HTTPClientError # 407 + HAS_BODY = true + end + class HTTPRequestTimeOut < HTTPClientError # 408 + HAS_BODY = true + end + class HTTPConflict < HTTPClientError # 409 + HAS_BODY = true + end + class HTTPGone < HTTPClientError # 410 + HAS_BODY = true + end + class HTTPLengthRequired < HTTPClientError # 411 + HAS_BODY = true + end + class HTTPPreconditionFailed < HTTPClientError # 412 + HAS_BODY = true + end + class HTTPRequestEntityTooLarge < HTTPClientError # 413 + HAS_BODY = true + end + class HTTPRequestURITooLong < HTTPClientError # 414 + HAS_BODY = true + end + HTTPRequestURITooLarge = HTTPRequestURITooLong + class HTTPUnsupportedMediaType < HTTPClientError # 415 + HAS_BODY = true + end + class HTTPRequestedRangeNotSatisfiable < HTTPClientError # 416 + HAS_BODY = true + end + class HTTPExpectationFailed < HTTPClientError # 417 + HAS_BODY = true + end + + class HTTPInternalServerError < HTTPServerError # 500 + HAS_BODY = true + end + class HTTPNotImplemented < HTTPServerError # 501 + HAS_BODY = true + end + class HTTPBadGateway < HTTPServerError # 502 + HAS_BODY = true + end + class HTTPServiceUnavailable < HTTPServerError # 503 + HAS_BODY = true + end + class HTTPGatewayTimeOut < HTTPServerError # 504 + HAS_BODY = true + end + class HTTPVersionNotSupported < HTTPServerError # 505 + HAS_BODY = true + end + + # :startdoc: + + + class HTTPResponse # reopen + + CODE_CLASS_TO_OBJ = { + '1' => HTTPInformation, + '2' => HTTPSuccess, + '3' => HTTPRedirection, + '4' => HTTPClientError, + '5' => HTTPServerError + } + CODE_TO_OBJ = { + '100' => HTTPContinue, + '101' => HTTPSwitchProtocol, + + '200' => HTTPOK, + '201' => HTTPCreated, + '202' => HTTPAccepted, + '203' => HTTPNonAuthoritativeInformation, + '204' => HTTPNoContent, + '205' => HTTPResetContent, + '206' => HTTPPartialContent, + + '300' => HTTPMultipleChoice, + '301' => HTTPMovedPermanently, + '302' => HTTPFound, + '303' => HTTPSeeOther, + '304' => HTTPNotModified, + '305' => HTTPUseProxy, + '307' => HTTPTemporaryRedirect, + + '400' => HTTPBadRequest, + '401' => HTTPUnauthorized, + '402' => HTTPPaymentRequired, + '403' => HTTPForbidden, + '404' => HTTPNotFound, + '405' => HTTPMethodNotAllowed, + '406' => HTTPNotAcceptable, + '407' => HTTPProxyAuthenticationRequired, + '408' => HTTPRequestTimeOut, + '409' => HTTPConflict, + '410' => HTTPGone, + '411' => HTTPLengthRequired, + '412' => HTTPPreconditionFailed, + '413' => HTTPRequestEntityTooLarge, + '414' => HTTPRequestURITooLong, + '415' => HTTPUnsupportedMediaType, + '416' => HTTPRequestedRangeNotSatisfiable, + '417' => HTTPExpectationFailed, + + '500' => HTTPInternalServerError, + '501' => HTTPNotImplemented, + '502' => HTTPBadGateway, + '503' => HTTPServiceUnavailable, + '504' => HTTPGatewayTimeOut, + '505' => HTTPVersionNotSupported + } + + class << HTTPResponse + def read_new(sock) #:nodoc: internal use only + httpv, code, msg = read_status_line(sock) + res = response_class(code).new(httpv, code, msg) + each_response_header(sock) do |k,v| + res.add_field k, v + end + res + end + + private + + def read_status_line(sock) + str = sock.readline + m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) or + raise HTTPBadResponse, "wrong status line: #{str.dump}" + m.captures + end + + def response_class(code) + CODE_TO_OBJ[code] or + CODE_CLASS_TO_OBJ[code[0,1]] or + HTTPUnknownResponse + end + + def each_response_header(sock) + while true + line = sock.readuntil("\n", true).sub(/\s+\z/, '') + break if line.empty? + m = /\A([^:]+):\s*/.match(line) or + raise HTTPBadResponse, 'wrong header line format' + yield m[1], m.post_match + end + end + end + + # next is to fix bug in RDoc, where the private inside class << self + # spills out. + public + + include HTTPHeader + + def initialize(httpv, code, msg) #:nodoc: internal use only + @http_version = httpv + @code = code + @message = msg + initialize_http_header nil + @body = nil + @read = false + end + + # The HTTP version supported by the server. + attr_reader :http_version + + # HTTP result code string. For example, '302'. You can also + # determine the response type by which response subclass the + # response object is an instance of. + attr_reader :code + + # HTTP result message. For example, 'Not Found'. + attr_reader :message + alias msg message # :nodoc: obsolete + + def inspect + "#<#{self.class} #{@code} #{@message} readbody=#{@read}>" + end + + # For backward compatibility. + # To allow Net::HTTP 1.1 style assignment + # e.g. + # response, body = Net::HTTP.get(....) + # + def to_ary + warn "net/http.rb: warning: Net::HTTP v1.1 style assignment found at #{caller(1)[0]}; use `response = http.get(...)' instead." if $VERBOSE + res = self.dup + class << res + undef to_ary + end + [res, res.body] + end + + # + # response <-> exception relationship + # + + def code_type #:nodoc: + self.class + end + + def error! #:nodoc: + raise error_type().new(@code + ' ' + @message.dump, self) + end + + def error_type #:nodoc: + self.class::EXCEPTION_TYPE + end + + # Raises HTTP error if the response is not 2xx. + def value + error! unless self.kind_of?(HTTPSuccess) + end + + # + # header (for backward compatibility only; DO NOT USE) + # + + def response #:nodoc: + warn "#{caller(1)[0]}: warning: HTTPResponse#response is obsolete" if $VERBOSE + self + end + + def header #:nodoc: + warn "#{caller(1)[0]}: warning: HTTPResponse#header is obsolete" if $VERBOSE + self + end + + def read_header #:nodoc: + warn "#{caller(1)[0]}: warning: HTTPResponse#read_header is obsolete" if $VERBOSE + self + end + + # + # body + # + + def reading_body(sock, reqmethodallowbody) #:nodoc: internal use only + @socket = sock + @body_exist = reqmethodallowbody && self.class.body_permitted? + begin + yield + self.body # ensure to read body + ensure + @socket = nil + end + end + + # Gets entity body. If the block given, yields it to +block+. + # The body is provided in fragments, as it is read in from the socket. + # + # Calling this method a second or subsequent time will return the + # already read string. + # + # http.request_get('/index.html') {|res| + # puts res.read_body + # } + # + # http.request_get('/index.html') {|res| + # p res.read_body.object_id # 538149362 + # p res.read_body.object_id # 538149362 + # } + # + # # using iterator + # http.request_get('/index.html') {|res| + # res.read_body do |segment| + # print segment + # end + # } + # + def read_body(dest = nil, &block) + if @read + raise IOError, "#{self.class}\#read_body called twice" if dest or block + return @body + end + to = procdest(dest, block) + stream_check + if @body_exist + read_body_0 to + @body = to + else + @body = nil + end + @read = true + + @body + end + + # Returns the entity body. + # + # Calling this method a second or subsequent time will return the + # already read string. + # + # http.request_get('/index.html') {|res| + # puts res.body + # } + # + # http.request_get('/index.html') {|res| + # p res.body.object_id # 538149362 + # p res.body.object_id # 538149362 + # } + # + def body + read_body() + end + + alias entity body #:nodoc: obsolete + + private + + def read_body_0(dest) + if chunked? + read_chunked dest + return + end + clen = content_length() + if clen + @socket.read clen, dest, true # ignore EOF + return + end + clen = range_length() + if clen + @socket.read clen, dest + return + end + @socket.read_all dest + end + + def read_chunked(dest) + len = nil + total = 0 + while true + line = @socket.readline + hexlen = line.slice(/[0-9a-fA-F]+/) or + raise HTTPBadResponse, "wrong chunk size line: #{line}" + len = hexlen.hex + break if len == 0 + @socket.read len, dest; total += len + @socket.read 2 # \r\n + end + until @socket.readline.empty? + # none + end + end + + def stream_check + raise IOError, 'attempt to read body out of block' if @socket.closed? + end + + def procdest(dest, block) + raise ArgumentError, 'both arg and block given for HTTP method' \ + if dest and block + if block + ReadAdapter.new(block) + else + dest || '' + end + end + + end + + + # :enddoc: + + #-- + # for backward compatibility + class HTTP + ProxyMod = ProxyDelta + end + module NetPrivate + HTTPRequest = ::Net::HTTPRequest + end + + HTTPInformationCode = HTTPInformation + HTTPSuccessCode = HTTPSuccess + HTTPRedirectionCode = HTTPRedirection + HTTPRetriableCode = HTTPRedirection + HTTPClientErrorCode = HTTPClientError + HTTPFatalErrorCode = HTTPClientError + HTTPServerErrorCode = HTTPServerError + HTTPResponceReceiver = HTTPResponse + +end # module Net diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/https.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/https.rb new file mode 100644 index 0000000000..a9b182be94 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/https.rb @@ -0,0 +1,173 @@ +=begin + += $RCSfile$ -- SSL/TLS enhancement for Net::HTTP. + +== Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU Yuuzou + All rights reserved. + +== Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + +== Requirements + This program requires Net 1.2.0 or higher version. + You can get it from RAA or Ruby's CVS repository. + +== Version + $Id: https.rb 11708 2007-02-12 23:01:19Z shyouhei $ + + 2001-11-06: Contiributed to Ruby/OpenSSL project. + 2004-03-06: Some code is merged in to net/http. + +== Example + +Here is a simple HTTP client: + + require 'net/http' + require 'uri' + + uri = URI.parse(ARGV[0] || 'http://localhost/') + http = Net::HTTP.new(uri.host, uri.port) + http.start { + http.request_get(uri.path) {|res| + print res.body + } + } + +It can be replaced by the following code: + + require 'net/https' + require 'uri' + + uri = URI.parse(ARGV[0] || 'https://localhost/') + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true if uri.scheme == "https" # enable SSL/TLS + http.start { + http.request_get(uri.path) {|res| + print res.body + } + } + +== class Net::HTTP + +=== Instance Methods + +: use_ssl? + returns true if use SSL/TLS with HTTP. + +: use_ssl=((|true_or_false|)) + sets use_ssl. + +: peer_cert + return the X.509 certificates the server presented. + +: key, key=((|key|)) + Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object. + (This method is appeared in Michal Rokos's OpenSSL extention.) + +: cert, cert=((|cert|)) + Sets an OpenSSL::X509::Certificate object as client certificate + (This method is appeared in Michal Rokos's OpenSSL extention). + +: ca_file, ca_file=((|path|)) + Sets path of a CA certification file in PEM format. + The file can contrain several CA certificats. + +: ca_path, ca_path=((|path|)) + Sets path of a CA certification directory containing certifications + in PEM format. + +: verify_mode, verify_mode=((|mode|)) + Sets the flags for server the certification verification at + begining of SSL/TLS session. + OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER is acceptable. + +: verify_callback, verify_callback=((|proc|)) + Sets the verify callback for the server certification verification. + +: verify_depth, verify_depth=((|num|)) + Sets the maximum depth for the certificate chain verification. + +: cert_store, cert_store=((|store|)) + Sets the X509::Store to verify peer certificate. + +: ssl_timeout, ssl_timeout=((|sec|)) + Sets the SSL timeout seconds. + +=end + +require 'net/http' +require 'openssl' + +module Net + + class HTTP + remove_method :use_ssl? + def use_ssl? + @use_ssl + end + + # For backward compatibility. + alias use_ssl use_ssl? + + # Turn on/off SSL. + # This flag must be set before starting session. + # If you change use_ssl value after session started, + # a Net::HTTP object raises IOError. + def use_ssl=(flag) + flag = (flag ? true : false) + raise IOError, "use_ssl value changed, but session already started" \ + if started? and @use_ssl != flag + if flag and not @ssl_context + @ssl_context = OpenSSL::SSL::SSLContext.new + end + @use_ssl = flag + end + + def self.ssl_context_accessor(name) + module_eval(<<-End, __FILE__, __LINE__ + 1) + def #{name} + return nil unless @ssl_context + @ssl_context.#{name} + end + + def #{name}=(val) + @ssl_context ||= OpenSSL::SSL::SSLContext.new + @ssl_context.#{name} = val + end + End + end + + ssl_context_accessor :key + ssl_context_accessor :cert + ssl_context_accessor :ca_file + ssl_context_accessor :ca_path + ssl_context_accessor :verify_mode + ssl_context_accessor :verify_callback + ssl_context_accessor :verify_depth + ssl_context_accessor :cert_store + + def ssl_timeout + return nil unless @ssl_context + @ssl_context.timeout + end + + def ssl_timeout=(sec) + raise ArgumentError, 'Net::HTTP#ssl_timeout= called but use_ssl=false' \ + unless use_ssl? + @ssl_context ||= OpenSSL::SSL::SSLContext.new + @ssl_context.timeout = sec + end + + # For backward compatibility + alias timeout= ssl_timeout= + + def peer_cert + return nil if not use_ssl? or not @socket + @socket.io.peer_cert + end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/imap.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/imap.rb new file mode 100644 index 0000000000..6436940df6 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/imap.rb @@ -0,0 +1,3370 @@ +# +# = net/imap.rb +# +# Copyright (C) 2000 Shugo Maeda +# +# This library is distributed under the terms of the Ruby license. +# You can freely distribute/modify this library. +# +# Documentation: Shugo Maeda, with RDoc conversion and overview by William +# Webber. +# +# See Net::IMAP for documentation. +# + + +require "socket" +require "monitor" +require "digest/md5" +begin + require "openssl" +rescue LoadError +end + +module Net + + # + # Net::IMAP implements Internet Message Access Protocol (IMAP) client + # functionality. The protocol is described in [IMAP]. + # + # == IMAP Overview + # + # An IMAP client connects to a server, and then authenticates + # itself using either #authenticate() or #login(). Having + # authenticated itself, there is a range of commands + # available to it. Most work with mailboxes, which may be + # arranged in an hierarchical namespace, and each of which + # contains zero or more messages. How this is implemented on + # the server is implementation-dependent; on a UNIX server, it + # will frequently be implemented as a files in mailbox format + # within a hierarchy of directories. + # + # To work on the messages within a mailbox, the client must + # first select that mailbox, using either #select() or (for + # read-only access) #examine(). Once the client has successfully + # selected a mailbox, they enter _selected_ state, and that + # mailbox becomes the _current_ mailbox, on which mail-item + # related commands implicitly operate. + # + # Messages have two sorts of identifiers: message sequence + # numbers, and UIDs. + # + # Message sequence numbers number messages within a mail box + # from 1 up to the number of items in the mail box. If new + # message arrives during a session, it receives a sequence + # number equal to the new size of the mail box. If messages + # are expunged from the mailbox, remaining messages have their + # sequence numbers "shuffled down" to fill the gaps. + # + # UIDs, on the other hand, are permanently guaranteed not to + # identify another message within the same mailbox, even if + # the existing message is deleted. UIDs are required to + # be assigned in ascending (but not necessarily sequential) + # order within a mailbox; this means that if a non-IMAP client + # rearranges the order of mailitems within a mailbox, the + # UIDs have to be reassigned. An IMAP client cannot thus + # rearrange message orders. + # + # == Examples of Usage + # + # === List sender and subject of all recent messages in the default mailbox + # + # imap = Net::IMAP.new('mail.example.com') + # imap.authenticate('LOGIN', 'joe_user', 'joes_password') + # imap.examine('INBOX') + # imap.search(["RECENT"]).each do |message_id| + # envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"] + # puts "#{envelope.from[0].name}: \t#{envelope.subject}" + # end + # + # === Move all messages from April 2003 from "Mail/sent-mail" to "Mail/sent-apr03" + # + # imap = Net::IMAP.new('mail.example.com') + # imap.authenticate('LOGIN', 'joe_user', 'joes_password') + # imap.select('Mail/sent-mail') + # if not imap.list('Mail/', 'sent-apr03') + # imap.create('Mail/sent-apr03') + # end + # imap.search(["BEFORE", "30-Apr-2003", "SINCE", "1-Apr-2003"]).each do |message_id| + # imap.copy(message_id, "Mail/sent-apr03") + # imap.store(message_id, "+FLAGS", [:Deleted]) + # end + # imap.expunge + # + # == Thread Safety + # + # Net::IMAP supports concurrent threads. For example, + # + # imap = Net::IMAP.new("imap.foo.net", "imap2") + # imap.authenticate("cram-md5", "bar", "password") + # imap.select("inbox") + # fetch_thread = Thread.start { imap.fetch(1..-1, "UID") } + # search_result = imap.search(["BODY", "hello"]) + # fetch_result = fetch_thread.value + # imap.disconnect + # + # This script invokes the FETCH command and the SEARCH command concurrently. + # + # == Errors + # + # An IMAP server can send three different types of responses to indicate + # failure: + # + # NO:: the attempted command could not be successfully completed. For + # instance, the username/password used for logging in are incorrect; + # the selected mailbox does not exists; etc. + # + # BAD:: the request from the client does not follow the server's + # understanding of the IMAP protocol. This includes attempting + # commands from the wrong client state; for instance, attempting + # to perform a SEARCH command without having SELECTed a current + # mailbox. It can also signal an internal server + # failure (such as a disk crash) has occurred. + # + # BYE:: the server is saying goodbye. This can be part of a normal + # logout sequence, and can be used as part of a login sequence + # to indicate that the server is (for some reason) unwilling + # to accept our connection. As a response to any other command, + # it indicates either that the server is shutting down, or that + # the server is timing out the client connection due to inactivity. + # + # These three error response are represented by the errors + # Net::IMAP::NoResponseError, Net::IMAP::BadResponseError, and + # Net::IMAP::ByeResponseError, all of which are subclasses of + # Net::IMAP::ResponseError. Essentially, all methods that involve + # sending a request to the server can generate one of these errors. + # Only the most pertinent instances have been documented below. + # + # Because the IMAP class uses Sockets for communication, its methods + # are also susceptible to the various errors that can occur when + # working with sockets. These are generally represented as + # Errno errors. For instance, any method that involves sending a + # request to the server and/or receiving a response from it could + # raise an Errno::EPIPE error if the network connection unexpectedly + # goes down. See the socket(7), ip(7), tcp(7), socket(2), connect(2), + # and associated man pages. + # + # Finally, a Net::IMAP::DataFormatError is thrown if low-level data + # is found to be in an incorrect format (for instance, when converting + # between UTF-8 and UTF-16), and Net::IMAP::ResponseParseError is + # thrown if a server response is non-parseable. + # + # + # == References + # + # [[IMAP]] + # M. Crispin, "INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1", + # RFC 2060, December 1996. (Note: since obsoleted by RFC 3501) + # + # [[LANGUAGE-TAGS]] + # Alvestrand, H., "Tags for the Identification of + # Languages", RFC 1766, March 1995. + # + # [[MD5]] + # Myers, J., and M. Rose, "The Content-MD5 Header Field", RFC + # 1864, October 1995. + # + # [[MIME-IMB]] + # Freed, N., and N. Borenstein, "MIME (Multipurpose Internet + # Mail Extensions) Part One: Format of Internet Message Bodies", RFC + # 2045, November 1996. + # + # [[RFC-822]] + # Crocker, D., "Standard for the Format of ARPA Internet Text + # Messages", STD 11, RFC 822, University of Delaware, August 1982. + # + # [[RFC-2087]] + # Myers, J., "IMAP4 QUOTA extension", RFC 2087, January 1997. + # + # [[RFC-2086]] + # Myers, J., "IMAP4 ACL extension", RFC 2086, January 1997. + # + # [[RFC-2195]] + # Klensin, J., Catoe, R., and Krumviede, P., "IMAP/POP AUTHorize Extension + # for Simple Challenge/Response", RFC 2195, September 1997. + # + # [[SORT-THREAD-EXT]] + # Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - SORT and THREAD + # Extensions", draft-ietf-imapext-sort, May 2003. + # + # [[OSSL]] + # http://www.openssl.org + # + # [[RSSL]] + # http://savannah.gnu.org/projects/rubypki + # + # [[UTF7]] + # Goldsmith, D. and Davis, M., "UTF-7: A Mail-Safe Transformation Format of + # Unicode", RFC 2152, May 1997. + # + class IMAP + include MonitorMixin + if defined?(OpenSSL) + include OpenSSL + include SSL + end + + # Returns an initial greeting response from the server. + attr_reader :greeting + + # Returns recorded untagged responses. For example: + # + # imap.select("inbox") + # p imap.responses["EXISTS"][-1] + # #=> 2 + # p imap.responses["UIDVALIDITY"][-1] + # #=> 968263756 + attr_reader :responses + + # Returns all response handlers. + attr_reader :response_handlers + + # The thread to receive exceptions. + attr_accessor :client_thread + + # Flag indicating a message has been seen + SEEN = :Seen + + # Flag indicating a message has been answered + ANSWERED = :Answered + + # Flag indicating a message has been flagged for special or urgent + # attention + FLAGGED = :Flagged + + # Flag indicating a message has been marked for deletion. This + # will occur when the mailbox is closed or expunged. + DELETED = :Deleted + + # Flag indicating a message is only a draft or work-in-progress version. + DRAFT = :Draft + + # Flag indicating that the message is "recent", meaning that this + # session is the first session in which the client has been notified + # of this message. + RECENT = :Recent + + # Flag indicating that a mailbox context name cannot contain + # children. + NOINFERIORS = :Noinferiors + + # Flag indicating that a mailbox is not selected. + NOSELECT = :Noselect + + # Flag indicating that a mailbox has been marked "interesting" by + # the server; this commonly indicates that the mailbox contains + # new messages. + MARKED = :Marked + + # Flag indicating that the mailbox does not contains new messages. + UNMARKED = :Unmarked + + # Returns the debug mode. + def self.debug + return @@debug + end + + # Sets the debug mode. + def self.debug=(val) + return @@debug = val + end + + # Adds an authenticator for Net::IMAP#authenticate. +auth_type+ + # is the type of authentication this authenticator supports + # (for instance, "LOGIN"). The +authenticator+ is an object + # which defines a process() method to handle authentication with + # the server. See Net::IMAP::LoginAuthenticator and + # Net::IMAP::CramMD5Authenticator for examples. + # + # If +auth_type+ refers to an existing authenticator, it will be + # replaced by the new one. + def self.add_authenticator(auth_type, authenticator) + @@authenticators[auth_type] = authenticator + end + + # Disconnects from the server. + def disconnect + if SSL::SSLSocket === @sock + @sock.io.shutdown + else + @sock.shutdown + end + @receiver_thread.join + @sock.close + end + + # Returns true if disconnected from the server. + def disconnected? + return @sock.closed? + end + + # Sends a CAPABILITY command, and returns an array of + # capabilities that the server supports. Each capability + # is a string. See [IMAP] for a list of possible + # capabilities. + # + # Note that the Net::IMAP class does not modify its + # behaviour according to the capabilities of the server; + # it is up to the user of the class to ensure that + # a certain capability is supported by a server before + # using it. + def capability + synchronize do + send_command("CAPABILITY") + return @responses.delete("CAPABILITY")[-1] + end + end + + # Sends a NOOP command to the server. It does nothing. + def noop + send_command("NOOP") + end + + # Sends a LOGOUT command to inform the server that the client is + # done with the connection. + def logout + send_command("LOGOUT") + end + + # Sends an AUTHENTICATE command to authenticate the client. + # The +auth_type+ parameter is a string that represents + # the authentication mechanism to be used. Currently Net::IMAP + # supports authentication mechanisms: + # + # LOGIN:: login using cleartext user and password. + # CRAM-MD5:: login with cleartext user and encrypted password + # (see [RFC-2195] for a full description). This + # mechanism requires that the server have the user's + # password stored in clear-text password. + # + # For both these mechanisms, there should be two +args+: username + # and (cleartext) password. A server may not support one or other + # of these mechanisms; check #capability() for a capability of + # the form "AUTH=LOGIN" or "AUTH=CRAM-MD5". + # + # Authentication is done using the appropriate authenticator object: + # see @@authenticators for more information on plugging in your own + # authenticator. + # + # For example: + # + # imap.authenticate('LOGIN', user, password) + # + # A Net::IMAP::NoResponseError is raised if authentication fails. + def authenticate(auth_type, *args) + auth_type = auth_type.upcase + unless @@authenticators.has_key?(auth_type) + raise ArgumentError, + format('unknown auth type - "%s"', auth_type) + end + authenticator = @@authenticators[auth_type].new(*args) + send_command("AUTHENTICATE", auth_type) do |resp| + if resp.instance_of?(ContinuationRequest) + data = authenticator.process(resp.data.text.unpack("m")[0]) + s = [data].pack("m").gsub(/\n/, "") + send_string_data(s) + put_string(CRLF) + end + end + end + + # Sends a LOGIN command to identify the client and carries + # the plaintext +password+ authenticating this +user+. Note + # that, unlike calling #authenticate() with an +auth_type+ + # of "LOGIN", #login() does *not* use the login authenticator. + # + # A Net::IMAP::NoResponseError is raised if authentication fails. + def login(user, password) + send_command("LOGIN", user, password) + end + + # Sends a SELECT command to select a +mailbox+ so that messages + # in the +mailbox+ can be accessed. + # + # After you have selected a mailbox, you may retrieve the + # number of items in that mailbox from @responses["EXISTS"][-1], + # and the number of recent messages from @responses["RECENT"][-1]. + # Note that these values can change if new messages arrive + # during a session; see #add_response_handler() for a way of + # detecting this event. + # + # A Net::IMAP::NoResponseError is raised if the mailbox does not + # exist or is for some reason non-selectable. + def select(mailbox) + synchronize do + @responses.clear + send_command("SELECT", mailbox) + end + end + + # Sends a EXAMINE command to select a +mailbox+ so that messages + # in the +mailbox+ can be accessed. Behaves the same as #select(), + # except that the selected +mailbox+ is identified as read-only. + # + # A Net::IMAP::NoResponseError is raised if the mailbox does not + # exist or is for some reason non-examinable. + def examine(mailbox) + synchronize do + @responses.clear + send_command("EXAMINE", mailbox) + end + end + + # Sends a CREATE command to create a new +mailbox+. + # + # A Net::IMAP::NoResponseError is raised if a mailbox with that name + # cannot be created. + def create(mailbox) + send_command("CREATE", mailbox) + end + + # Sends a DELETE command to remove the +mailbox+. + # + # A Net::IMAP::NoResponseError is raised if a mailbox with that name + # cannot be deleted, either because it does not exist or because the + # client does not have permission to delete it. + def delete(mailbox) + send_command("DELETE", mailbox) + end + + # Sends a RENAME command to change the name of the +mailbox+ to + # +newname+. + # + # A Net::IMAP::NoResponseError is raised if a mailbox with the + # name +mailbox+ cannot be renamed to +newname+ for whatever + # reason; for instance, because +mailbox+ does not exist, or + # because there is already a mailbox with the name +newname+. + def rename(mailbox, newname) + send_command("RENAME", mailbox, newname) + end + + # Sends a SUBSCRIBE command to add the specified +mailbox+ name to + # the server's set of "active" or "subscribed" mailboxes as returned + # by #lsub(). + # + # A Net::IMAP::NoResponseError is raised if +mailbox+ cannot be + # subscribed to, for instance because it does not exist. + def subscribe(mailbox) + send_command("SUBSCRIBE", mailbox) + end + + # Sends a UNSUBSCRIBE command to remove the specified +mailbox+ name + # from the server's set of "active" or "subscribed" mailboxes. + # + # A Net::IMAP::NoResponseError is raised if +mailbox+ cannot be + # unsubscribed from, for instance because the client is not currently + # subscribed to it. + def unsubscribe(mailbox) + send_command("UNSUBSCRIBE", mailbox) + end + + # Sends a LIST command, and returns a subset of names from + # the complete set of all names available to the client. + # +refname+ provides a context (for instance, a base directory + # in a directory-based mailbox hierarchy). +mailbox+ specifies + # a mailbox or (via wildcards) mailboxes under that context. + # Two wildcards may be used in +mailbox+: '*', which matches + # all characters *including* the hierarchy delimiter (for instance, + # '/' on a UNIX-hosted directory-based mailbox hierarchy); and '%', + # which matches all characters *except* the hierarchy delimiter. + # + # If +refname+ is empty, +mailbox+ is used directly to determine + # which mailboxes to match. If +mailbox+ is empty, the root + # name of +refname+ and the hierarchy delimiter are returned. + # + # The return value is an array of +Net::IMAP::MailboxList+. For example: + # + # imap.create("foo/bar") + # imap.create("foo/baz") + # p imap.list("", "foo/%") + # #=> [#, \\ + # #, \\ + # #] + def list(refname, mailbox) + synchronize do + send_command("LIST", refname, mailbox) + return @responses.delete("LIST") + end + end + + # Sends the GETQUOTAROOT command along with specified +mailbox+. + # This command is generally available to both admin and user. + # If mailbox exists, returns an array containing objects of + # Net::IMAP::MailboxQuotaRoot and Net::IMAP::MailboxQuota. + def getquotaroot(mailbox) + synchronize do + send_command("GETQUOTAROOT", mailbox) + result = [] + result.concat(@responses.delete("QUOTAROOT")) + result.concat(@responses.delete("QUOTA")) + return result + end + end + + # Sends the GETQUOTA command along with specified +mailbox+. + # If this mailbox exists, then an array containing a + # Net::IMAP::MailboxQuota object is returned. This + # command generally is only available to server admin. + def getquota(mailbox) + synchronize do + send_command("GETQUOTA", mailbox) + return @responses.delete("QUOTA") + end + end + + # Sends a SETQUOTA command along with the specified +mailbox+ and + # +quota+. If +quota+ is nil, then quota will be unset for that + # mailbox. Typically one needs to be logged in as server admin + # for this to work. The IMAP quota commands are described in + # [RFC-2087]. + def setquota(mailbox, quota) + if quota.nil? + data = '()' + else + data = '(STORAGE ' + quota.to_s + ')' + end + send_command("SETQUOTA", mailbox, RawData.new(data)) + end + + # Sends the SETACL command along with +mailbox+, +user+ and the + # +rights+ that user is to have on that mailbox. If +rights+ is nil, + # then that user will be stripped of any rights to that mailbox. + # The IMAP ACL commands are described in [RFC-2086]. + def setacl(mailbox, user, rights) + if rights.nil? + send_command("SETACL", mailbox, user, "") + else + send_command("SETACL", mailbox, user, rights) + end + end + + # Send the GETACL command along with specified +mailbox+. + # If this mailbox exists, an array containing objects of + # Net::IMAP::MailboxACLItem will be returned. + def getacl(mailbox) + synchronize do + send_command("GETACL", mailbox) + return @responses.delete("ACL")[-1] + end + end + + # Sends a LSUB command, and returns a subset of names from the set + # of names that the user has declared as being "active" or + # "subscribed". +refname+ and +mailbox+ are interpreted as + # for #list(). + # The return value is an array of +Net::IMAP::MailboxList+. + def lsub(refname, mailbox) + synchronize do + send_command("LSUB", refname, mailbox) + return @responses.delete("LSUB") + end + end + + # Sends a STATUS command, and returns the status of the indicated + # +mailbox+. +attr+ is a list of one or more attributes that + # we are request the status of. Supported attributes include: + # + # MESSAGES:: the number of messages in the mailbox. + # RECENT:: the number of recent messages in the mailbox. + # UNSEEN:: the number of unseen messages in the mailbox. + # + # The return value is a hash of attributes. For example: + # + # p imap.status("inbox", ["MESSAGES", "RECENT"]) + # #=> {"RECENT"=>0, "MESSAGES"=>44} + # + # A Net::IMAP::NoResponseError is raised if status values + # for +mailbox+ cannot be returned, for instance because it + # does not exist. + def status(mailbox, attr) + synchronize do + send_command("STATUS", mailbox, attr) + return @responses.delete("STATUS")[-1].attr + end + end + + # Sends a APPEND command to append the +message+ to the end of + # the +mailbox+. The optional +flags+ argument is an array of + # flags to initially passing to the new message. The optional + # +date_time+ argument specifies the creation time to assign to the + # new message; it defaults to the current time. + # For example: + # + # imap.append("inbox", <:: a set of message sequence numbers. ',' indicates + # an interval, ':' indicates a range. For instance, + # '2,10:12,15' means "2,10,11,12,15". + # + # BEFORE :: messages with an internal date strictly before + # . The date argument has a format similar + # to 8-Aug-2002. + # + # BODY :: messages that contain within their body. + # + # CC :: messages containing in their CC field. + # + # FROM :: messages that contain in their FROM field. + # + # NEW:: messages with the \Recent, but not the \Seen, flag set. + # + # NOT :: negate the following search key. + # + # OR :: "or" two search keys together. + # + # ON :: messages with an internal date exactly equal to , + # which has a format similar to 8-Aug-2002. + # + # SINCE :: messages with an internal date on or after . + # + # SUBJECT :: messages with in their subject. + # + # TO :: messages with in their TO field. + # + # For example: + # + # p imap.search(["SUBJECT", "hello", "NOT", "NEW"]) + # #=> [1, 6, 7, 8] + def search(keys, charset = nil) + return search_internal("SEARCH", keys, charset) + end + + # As for #search(), but returns unique identifiers. + def uid_search(keys, charset = nil) + return search_internal("UID SEARCH", keys, charset) + end + + # Sends a FETCH command to retrieve data associated with a message + # in the mailbox. The +set+ parameter is a number or an array of + # numbers or a Range object. The number is a message sequence + # number. +attr+ is a list of attributes to fetch; see the + # documentation for Net::IMAP::FetchData for a list of valid + # attributes. + # The return value is an array of Net::IMAP::FetchData. For example: + # + # p imap.fetch(6..8, "UID") + # #=> [#98}>, \\ + # #99}>, \\ + # #100}>] + # p imap.fetch(6, "BODY[HEADER.FIELDS (SUBJECT)]") + # #=> [#"Subject: test\r\n\r\n"}>] + # data = imap.uid_fetch(98, ["RFC822.SIZE", "INTERNALDATE"])[0] + # p data.seqno + # #=> 6 + # p data.attr["RFC822.SIZE"] + # #=> 611 + # p data.attr["INTERNALDATE"] + # #=> "12-Oct-2000 22:40:59 +0900" + # p data.attr["UID"] + # #=> 98 + def fetch(set, attr) + return fetch_internal("FETCH", set, attr) + end + + # As for #fetch(), but +set+ contains unique identifiers. + def uid_fetch(set, attr) + return fetch_internal("UID FETCH", set, attr) + end + + # Sends a STORE command to alter data associated with messages + # in the mailbox, in particular their flags. The +set+ parameter + # is a number or an array of numbers or a Range object. Each number + # is a message sequence number. +attr+ is the name of a data item + # to store: 'FLAGS' means to replace the message's flag list + # with the provided one; '+FLAGS' means to add the provided flags; + # and '-FLAGS' means to remove them. +flags+ is a list of flags. + # + # The return value is an array of Net::IMAP::FetchData. For example: + # + # p imap.store(6..8, "+FLAGS", [:Deleted]) + # #=> [#[:Seen, :Deleted]}>, \\ + # #[:Seen, :Deleted]}>, \\ + # #[:Seen, :Deleted]}>] + def store(set, attr, flags) + return store_internal("STORE", set, attr, flags) + end + + # As for #store(), but +set+ contains unique identifiers. + def uid_store(set, attr, flags) + return store_internal("UID STORE", set, attr, flags) + end + + # Sends a COPY command to copy the specified message(s) to the end + # of the specified destination +mailbox+. The +set+ parameter is + # a number or an array of numbers or a Range object. The number is + # a message sequence number. + def copy(set, mailbox) + copy_internal("COPY", set, mailbox) + end + + # As for #copy(), but +set+ contains unique identifiers. + def uid_copy(set, mailbox) + copy_internal("UID COPY", set, mailbox) + end + + # Sends a SORT command to sort messages in the mailbox. + # Returns an array of message sequence numbers. For example: + # + # p imap.sort(["FROM"], ["ALL"], "US-ASCII") + # #=> [1, 2, 3, 5, 6, 7, 8, 4, 9] + # p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII") + # #=> [6, 7, 8, 1] + # + # See [SORT-THREAD-EXT] for more details. + def sort(sort_keys, search_keys, charset) + return sort_internal("SORT", sort_keys, search_keys, charset) + end + + # As for #sort(), but returns an array of unique identifiers. + def uid_sort(sort_keys, search_keys, charset) + return sort_internal("UID SORT", sort_keys, search_keys, charset) + end + + # Adds a response handler. For example, to detect when + # the server sends us a new EXISTS response (which normally + # indicates new messages being added to the mail box), + # you could add the following handler after selecting the + # mailbox. + # + # imap.add_response_handler { |resp| + # if resp.kind_of?(Net::IMAP::UntaggedResponse) and resp.name == "EXISTS" + # puts "Mailbox now has #{resp.data} messages" + # end + # } + # + def add_response_handler(handler = Proc.new) + @response_handlers.push(handler) + end + + # Removes the response handler. + def remove_response_handler(handler) + @response_handlers.delete(handler) + end + + # As for #search(), but returns message sequence numbers in threaded + # format, as a Net::IMAP::ThreadMember tree. The supported algorithms + # are: + # + # ORDEREDSUBJECT:: split into single-level threads according to subject, + # ordered by date. + # REFERENCES:: split into threads by parent/child relationships determined + # by which message is a reply to which. + # + # Unlike #search(), +charset+ is a required argument. US-ASCII + # and UTF-8 are sample values. + # + # See [SORT-THREAD-EXT] for more details. + def thread(algorithm, search_keys, charset) + return thread_internal("THREAD", algorithm, search_keys, charset) + end + + # As for #thread(), but returns unique identifiers instead of + # message sequence numbers. + def uid_thread(algorithm, search_keys, charset) + return thread_internal("UID THREAD", algorithm, search_keys, charset) + end + + # Decode a string from modified UTF-7 format to UTF-8. + # + # UTF-7 is a 7-bit encoding of Unicode [UTF7]. IMAP uses a + # slightly modified version of this to encode mailbox names + # containing non-ASCII characters; see [IMAP] section 5.1.3. + # + # Net::IMAP does _not_ automatically encode and decode + # mailbox names to and from utf7. + def self.decode_utf7(s) + return s.gsub(/&(.*?)-/n) { + if $1.empty? + "&" + else + base64 = $1.tr(",", "/") + x = base64.length % 4 + if x > 0 + base64.concat("=" * (4 - x)) + end + u16tou8(base64.unpack("m")[0]) + end + } + end + + # Encode a string from UTF-8 format to modified UTF-7. + def self.encode_utf7(s) + return s.gsub(/(&)|([^\x20-\x25\x27-\x7e]+)/n) { |x| + if $1 + "&-" + else + base64 = [u8tou16(x)].pack("m") + "&" + base64.delete("=\n").tr("/", ",") + "-" + end + } + end + + private + + CRLF = "\r\n" # :nodoc: + PORT = 143 # :nodoc: + + @@debug = false + @@authenticators = {} + + # Creates a new Net::IMAP object and connects it to the specified + # +port+ (143 by default) on the named +host+. If +usessl+ is true, + # then an attempt will + # be made to use SSL (now TLS) to connect to the server. For this + # to work OpenSSL [OSSL] and the Ruby OpenSSL [RSSL] + # extensions need to be installed. The +certs+ parameter indicates + # the path or file containing the CA cert of the server, and the + # +verify+ parameter is for the OpenSSL verification callback. + # + # The most common errors are: + # + # Errno::ECONNREFUSED:: connection refused by +host+ or an intervening + # firewall. + # Errno::ETIMEDOUT:: connection timed out (possibly due to packets + # being dropped by an intervening firewall). + # Errno::ENETUNREACH:: there is no route to that network. + # SocketError:: hostname not known or other socket error. + # Net::IMAP::ByeResponseError:: we connected to the host, but they + # immediately said goodbye to us. + def initialize(host, port = PORT, usessl = false, certs = nil, verify = false) + super() + @host = host + @port = port + @tag_prefix = "RUBY" + @tagno = 0 + @parser = ResponseParser.new + @sock = TCPSocket.open(host, port) + if usessl + unless defined?(OpenSSL) + raise "SSL extension not installed" + end + @usessl = true + + # verify the server. + context = SSLContext::new() + context.ca_file = certs if certs && FileTest::file?(certs) + context.ca_path = certs if certs && FileTest::directory?(certs) + context.verify_mode = VERIFY_PEER if verify + if defined?(VerifyCallbackProc) + context.verify_callback = VerifyCallbackProc + end + @sock = SSLSocket.new(@sock, context) + @sock.connect # start ssl session. + else + @usessl = false + end + @responses = Hash.new([].freeze) + @tagged_responses = {} + @response_handlers = [] + @response_arrival = new_cond + @continuation_request = nil + @logout_command_tag = nil + @debug_output_bol = true + + @greeting = get_response + if @greeting.name == "BYE" + @sock.close + raise ByeResponseError, @greeting.raw_data + end + + @client_thread = Thread.current + @receiver_thread = Thread.start { + receive_responses + } + end + + def receive_responses + while true + begin + resp = get_response + rescue Exception + @sock.close + @client_thread.raise($!) + break + end + break unless resp + begin + synchronize do + case resp + when TaggedResponse + @tagged_responses[resp.tag] = resp + @response_arrival.broadcast + if resp.tag == @logout_command_tag + return + end + when UntaggedResponse + record_response(resp.name, resp.data) + if resp.data.instance_of?(ResponseText) && + (code = resp.data.code) + record_response(code.name, code.data) + end + if resp.name == "BYE" && @logout_command_tag.nil? + @sock.close + raise ByeResponseError, resp.raw_data + end + when ContinuationRequest + @continuation_request = resp + @response_arrival.broadcast + end + @response_handlers.each do |handler| + handler.call(resp) + end + end + rescue Exception + @client_thread.raise($!) + end + end + end + + def get_tagged_response(tag) + until @tagged_responses.key?(tag) + @response_arrival.wait + end + return pick_up_tagged_response(tag) + end + + def pick_up_tagged_response(tag) + resp = @tagged_responses.delete(tag) + case resp.name + when /\A(?:NO)\z/ni + raise NoResponseError, resp.data.text + when /\A(?:BAD)\z/ni + raise BadResponseError, resp.data.text + else + return resp + end + end + + def get_response + buff = "" + while true + s = @sock.gets(CRLF) + break unless s + buff.concat(s) + if /\{(\d+)\}\r\n/n =~ s + s = @sock.read($1.to_i) + buff.concat(s) + else + break + end + end + return nil if buff.length == 0 + if @@debug + $stderr.print(buff.gsub(/^/n, "S: ")) + end + return @parser.parse(buff) + end + + def record_response(name, data) + unless @responses.has_key?(name) + @responses[name] = [] + end + @responses[name].push(data) + end + + def send_command(cmd, *args, &block) + synchronize do + tag = Thread.current[:net_imap_tag] = generate_tag + put_string(tag + " " + cmd) + args.each do |i| + put_string(" ") + send_data(i) + end + put_string(CRLF) + if cmd == "LOGOUT" + @logout_command_tag = tag + end + if block + add_response_handler(block) + end + begin + return get_tagged_response(tag) + ensure + if block + remove_response_handler(block) + end + end + end + end + + def generate_tag + @tagno += 1 + return format("%s%04d", @tag_prefix, @tagno) + end + + def put_string(str) + @sock.print(str) + if @@debug + if @debug_output_bol + $stderr.print("C: ") + end + $stderr.print(str.gsub(/\n(?!\z)/n, "\nC: ")) + if /\r\n\z/n.match(str) + @debug_output_bol = true + else + @debug_output_bol = false + end + end + end + + def send_data(data) + case data + when nil + put_string("NIL") + when String + send_string_data(data) + when Integer + send_number_data(data) + when Array + send_list_data(data) + when Time + send_time_data(data) + when Symbol + send_symbol_data(data) + else + data.send_data(self) + end + end + + def send_string_data(str) + case str + when "" + put_string('""') + when /[\x80-\xff\r\n]/n + # literal + send_literal(str) + when /[(){ \x00-\x1f\x7f%*"\\]/n + # quoted string + send_quoted_string(str) + else + put_string(str) + end + end + + def send_quoted_string(str) + put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"') + end + + def send_literal(str) + put_string("{" + str.length.to_s + "}" + CRLF) + while @continuation_request.nil? && + !@tagged_responses.key?(Thread.current[:net_imap_tag]) + @response_arrival.wait + end + if @continuation_request.nil? + pick_up_tagged_response(Thread.current[:net_imap_tag]) + raise ResponseError.new("expected continuation request") + end + @continuation_request = nil + put_string(str) + end + + def send_number_data(num) + if num < 0 || num >= 4294967296 + raise DataFormatError, num.to_s + end + put_string(num.to_s) + end + + def send_list_data(list) + put_string("(") + first = true + list.each do |i| + if first + first = false + else + put_string(" ") + end + send_data(i) + end + put_string(")") + end + + DATE_MONTH = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec) + + def send_time_data(time) + t = time.dup.gmtime + s = format('"%2d-%3s-%4d %02d:%02d:%02d +0000"', + t.day, DATE_MONTH[t.month - 1], t.year, + t.hour, t.min, t.sec) + put_string(s) + end + + def send_symbol_data(symbol) + put_string("\\" + symbol.to_s) + end + + def search_internal(cmd, keys, charset) + if keys.instance_of?(String) + keys = [RawData.new(keys)] + else + normalize_searching_criteria(keys) + end + synchronize do + if charset + send_command(cmd, "CHARSET", charset, *keys) + else + send_command(cmd, *keys) + end + return @responses.delete("SEARCH")[-1] + end + end + + def fetch_internal(cmd, set, attr) + if attr.instance_of?(String) + attr = RawData.new(attr) + end + synchronize do + @responses.delete("FETCH") + send_command(cmd, MessageSet.new(set), attr) + return @responses.delete("FETCH") + end + end + + def store_internal(cmd, set, attr, flags) + if attr.instance_of?(String) + attr = RawData.new(attr) + end + synchronize do + @responses.delete("FETCH") + send_command(cmd, MessageSet.new(set), attr, flags) + return @responses.delete("FETCH") + end + end + + def copy_internal(cmd, set, mailbox) + send_command(cmd, MessageSet.new(set), mailbox) + end + + def sort_internal(cmd, sort_keys, search_keys, charset) + if search_keys.instance_of?(String) + search_keys = [RawData.new(search_keys)] + else + normalize_searching_criteria(search_keys) + end + normalize_searching_criteria(search_keys) + synchronize do + send_command(cmd, sort_keys, charset, *search_keys) + return @responses.delete("SORT")[-1] + end + end + + def thread_internal(cmd, algorithm, search_keys, charset) + if search_keys.instance_of?(String) + search_keys = [RawData.new(search_keys)] + else + normalize_searching_criteria(search_keys) + end + normalize_searching_criteria(search_keys) + send_command(cmd, algorithm, charset, *search_keys) + return @responses.delete("THREAD")[-1] + end + + def normalize_searching_criteria(keys) + keys.collect! do |i| + case i + when -1, Range, Array + MessageSet.new(i) + else + i + end + end + end + + def self.u16tou8(s) + len = s.length + if len < 2 + return "" + end + buf = "" + i = 0 + while i < len + c = s[i] << 8 | s[i + 1] + i += 2 + if c == 0xfeff + next + elsif c < 0x0080 + buf.concat(c) + elsif c < 0x0800 + b2 = c & 0x003f + b1 = c >> 6 + buf.concat(b1 | 0xc0) + buf.concat(b2 | 0x80) + elsif c >= 0xdc00 && c < 0xe000 + raise DataFormatError, "invalid surrogate detected" + elsif c >= 0xd800 && c < 0xdc00 + if i + 2 > len + raise DataFormatError, "invalid surrogate detected" + end + low = s[i] << 8 | s[i + 1] + i += 2 + if low < 0xdc00 || low > 0xdfff + raise DataFormatError, "invalid surrogate detected" + end + c = (((c & 0x03ff)) << 10 | (low & 0x03ff)) + 0x10000 + b4 = c & 0x003f + b3 = (c >> 6) & 0x003f + b2 = (c >> 12) & 0x003f + b1 = c >> 18; + buf.concat(b1 | 0xf0) + buf.concat(b2 | 0x80) + buf.concat(b3 | 0x80) + buf.concat(b4 | 0x80) + else # 0x0800-0xffff + b3 = c & 0x003f + b2 = (c >> 6) & 0x003f + b1 = c >> 12 + buf.concat(b1 | 0xe0) + buf.concat(b2 | 0x80) + buf.concat(b3 | 0x80) + end + end + return buf + end + private_class_method :u16tou8 + + def self.u8tou16(s) + len = s.length + buf = "" + i = 0 + while i < len + c = s[i] + if (c & 0x80) == 0 + buf.concat(0x00) + buf.concat(c) + i += 1 + elsif (c & 0xe0) == 0xc0 && + len >= 2 && + (s[i + 1] & 0xc0) == 0x80 + if c == 0xc0 || c == 0xc1 + raise DataFormatError, format("non-shortest UTF-8 sequence (%02x)", c) + end + u = ((c & 0x1f) << 6) | (s[i + 1] & 0x3f) + buf.concat(u >> 8) + buf.concat(u & 0x00ff) + i += 2 + elsif (c & 0xf0) == 0xe0 && + i + 2 < len && + (s[i + 1] & 0xc0) == 0x80 && + (s[i + 2] & 0xc0) == 0x80 + if c == 0xe0 && s[i + 1] < 0xa0 + raise DataFormatError, format("non-shortest UTF-8 sequence (%02x)", c) + end + u = ((c & 0x0f) << 12) | ((s[i + 1] & 0x3f) << 6) | (s[i + 2] & 0x3f) + # surrogate chars + if u >= 0xd800 && u <= 0xdfff + raise DataFormatError, format("none-UTF-16 char detected (%04x)", u) + end + buf.concat(u >> 8) + buf.concat(u & 0x00ff) + i += 3 + elsif (c & 0xf8) == 0xf0 && + i + 3 < len && + (s[i + 1] & 0xc0) == 0x80 && + (s[i + 2] & 0xc0) == 0x80 && + (s[i + 3] & 0xc0) == 0x80 + if c == 0xf0 && s[i + 1] < 0x90 + raise DataFormatError, format("non-shortest UTF-8 sequence (%02x)", c) + end + u = ((c & 0x07) << 18) | ((s[i + 1] & 0x3f) << 12) | + ((s[i + 2] & 0x3f) << 6) | (s[i + 3] & 0x3f) + if u < 0x10000 + buf.concat(u >> 8) + buf.concat(u & 0x00ff) + elsif u < 0x110000 + high = ((u - 0x10000) >> 10) | 0xd800 + low = (u & 0x03ff) | 0xdc00 + buf.concat(high >> 8) + buf.concat(high & 0x00ff) + buf.concat(low >> 8) + buf.concat(low & 0x00ff) + else + raise DataFormatError, format("none-UTF-16 char detected (%04x)", u) + end + i += 4 + else + raise DataFormatError, format("illegal UTF-8 sequence (%02x)", c) + end + end + return buf + end + private_class_method :u8tou16 + + class RawData # :nodoc: + def send_data(imap) + imap.send(:put_string, @data) + end + + private + + def initialize(data) + @data = data + end + end + + class Atom # :nodoc: + def send_data(imap) + imap.send(:put_string, @data) + end + + private + + def initialize(data) + @data = data + end + end + + class QuotedString # :nodoc: + def send_data(imap) + imap.send(:send_quoted_string, @data) + end + + private + + def initialize(data) + @data = data + end + end + + class Literal # :nodoc: + def send_data(imap) + imap.send(:send_literal, @data) + end + + private + + def initialize(data) + @data = data + end + end + + class MessageSet # :nodoc: + def send_data(imap) + imap.send(:put_string, format_internal(@data)) + end + + private + + def initialize(data) + @data = data + end + + def format_internal(data) + case data + when "*" + return data + when Integer + ensure_nz_number(data) + if data == -1 + return "*" + else + return data.to_s + end + when Range + return format_internal(data.first) + + ":" + format_internal(data.last) + when Array + return data.collect {|i| format_internal(i)}.join(",") + when ThreadMember + return data.seqno.to_s + + ":" + data.children.collect {|i| format_internal(i).join(",")} + else + raise DataFormatError, data.inspect + end + end + + def ensure_nz_number(num) + if num < -1 || num == 0 || num >= 4294967296 + msg = "nz_number must be non-zero unsigned 32-bit integer: " + + num.inspect + raise DataFormatError, msg + end + end + end + + # Net::IMAP::ContinuationRequest represents command continuation requests. + # + # The command continuation request response is indicated by a "+" token + # instead of a tag. This form of response indicates that the server is + # ready to accept the continuation of a command from the client. The + # remainder of this response is a line of text. + # + # continue_req ::= "+" SPACE (resp_text / base64) + # + # ==== Fields: + # + # data:: Returns the data (Net::IMAP::ResponseText). + # + # raw_data:: Returns the raw data string. + ContinuationRequest = Struct.new(:data, :raw_data) + + # Net::IMAP::UntaggedResponse represents untagged responses. + # + # Data transmitted by the server to the client and status responses + # that do not indicate command completion are prefixed with the token + # "*", and are called untagged responses. + # + # response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye / + # mailbox_data / message_data / capability_data) + # + # ==== Fields: + # + # name:: Returns the name such as "FLAGS", "LIST", "FETCH".... + # + # data:: Returns the data such as an array of flag symbols, + # a (()) object.... + # + # raw_data:: Returns the raw data string. + UntaggedResponse = Struct.new(:name, :data, :raw_data) + + # Net::IMAP::TaggedResponse represents tagged responses. + # + # The server completion result response indicates the success or + # failure of the operation. It is tagged with the same tag as the + # client command which began the operation. + # + # response_tagged ::= tag SPACE resp_cond_state CRLF + # + # tag ::= 1* + # + # resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text + # + # ==== Fields: + # + # tag:: Returns the tag. + # + # name:: Returns the name. the name is one of "OK", "NO", "BAD". + # + # data:: Returns the data. See (()). + # + # raw_data:: Returns the raw data string. + # + TaggedResponse = Struct.new(:tag, :name, :data, :raw_data) + + # Net::IMAP::ResponseText represents texts of responses. + # The text may be prefixed by the response code. + # + # resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text) + # ;; text SHOULD NOT begin with "[" or "=" + # + # ==== Fields: + # + # code:: Returns the response code. See (()). + # + # text:: Returns the text. + # + ResponseText = Struct.new(:code, :text) + + # + # Net::IMAP::ResponseCode represents response codes. + # + # resp_text_code ::= "ALERT" / "PARSE" / + # "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" / + # "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + # "UIDVALIDITY" SPACE nz_number / + # "UNSEEN" SPACE nz_number / + # atom [SPACE 1*] + # + # ==== Fields: + # + # name:: Returns the name such as "ALERT", "PERMANENTFLAGS", "UIDVALIDITY".... + # + # data:: Returns the data if it exists. + # + ResponseCode = Struct.new(:name, :data) + + # Net::IMAP::MailboxList represents contents of the LIST response. + # + # mailbox_list ::= "(" #("\Marked" / "\Noinferiors" / + # "\Noselect" / "\Unmarked" / flag_extension) ")" + # SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox + # + # ==== Fields: + # + # attr:: Returns the name attributes. Each name attribute is a symbol + # capitalized by String#capitalize, such as :Noselect (not :NoSelect). + # + # delim:: Returns the hierarchy delimiter + # + # name:: Returns the mailbox name. + # + MailboxList = Struct.new(:attr, :delim, :name) + + # Net::IMAP::MailboxQuota represents contents of GETQUOTA response. + # This object can also be a response to GETQUOTAROOT. In the syntax + # specification below, the delimiter used with the "#" construct is a + # single space (SPACE). + # + # quota_list ::= "(" #quota_resource ")" + # + # quota_resource ::= atom SPACE number SPACE number + # + # quota_response ::= "QUOTA" SPACE astring SPACE quota_list + # + # ==== Fields: + # + # mailbox:: The mailbox with the associated quota. + # + # usage:: Current storage usage of mailbox. + # + # quota:: Quota limit imposed on mailbox. + # + MailboxQuota = Struct.new(:mailbox, :usage, :quota) + + # Net::IMAP::MailboxQuotaRoot represents part of the GETQUOTAROOT + # response. (GETQUOTAROOT can also return Net::IMAP::MailboxQuota.) + # + # quotaroot_response ::= "QUOTAROOT" SPACE astring *(SPACE astring) + # + # ==== Fields: + # + # mailbox:: The mailbox with the associated quota. + # + # quotaroots:: Zero or more quotaroots that effect the quota on the + # specified mailbox. + # + MailboxQuotaRoot = Struct.new(:mailbox, :quotaroots) + + # Net::IMAP::MailboxACLItem represents response from GETACL. + # + # acl_data ::= "ACL" SPACE mailbox *(SPACE identifier SPACE rights) + # + # identifier ::= astring + # + # rights ::= astring + # + # ==== Fields: + # + # user:: Login name that has certain rights to the mailbox + # that was specified with the getacl command. + # + # rights:: The access rights the indicated user has to the + # mailbox. + # + MailboxACLItem = Struct.new(:user, :rights) + + # Net::IMAP::StatusData represents contents of the STATUS response. + # + # ==== Fields: + # + # mailbox:: Returns the mailbox name. + # + # attr:: Returns a hash. Each key is one of "MESSAGES", "RECENT", "UIDNEXT", + # "UIDVALIDITY", "UNSEEN". Each value is a number. + # + StatusData = Struct.new(:mailbox, :attr) + + # Net::IMAP::FetchData represents contents of the FETCH response. + # + # ==== Fields: + # + # seqno:: Returns the message sequence number. + # (Note: not the unique identifier, even for the UID command response.) + # + # attr:: Returns a hash. Each key is a data item name, and each value is + # its value. + # + # The current data items are: + # + # [BODY] + # A form of BODYSTRUCTURE without extension data. + # [BODY[
]<>] + # A string expressing the body contents of the specified section. + # [BODYSTRUCTURE] + # An object that describes the [MIME-IMB] body structure of a message. + # See Net::IMAP::BodyTypeBasic, Net::IMAP::BodyTypeText, + # Net::IMAP::BodyTypeMessage, Net::IMAP::BodyTypeMultipart. + # [ENVELOPE] + # A Net::IMAP::Envelope object that describes the envelope + # structure of a message. + # [FLAGS] + # A array of flag symbols that are set for this message. flag symbols + # are capitalized by String#capitalize. + # [INTERNALDATE] + # A string representing the internal date of the message. + # [RFC822] + # Equivalent to BODY[]. + # [RFC822.HEADER] + # Equivalent to BODY.PEEK[HEADER]. + # [RFC822.SIZE] + # A number expressing the [RFC-822] size of the message. + # [RFC822.TEXT] + # Equivalent to BODY[TEXT]. + # [UID] + # A number expressing the unique identifier of the message. + # + FetchData = Struct.new(:seqno, :attr) + + # Net::IMAP::Envelope represents envelope structures of messages. + # + # ==== Fields: + # + # date:: Returns a string that represents the date. + # + # subject:: Returns a string that represents the subject. + # + # from:: Returns an array of Net::IMAP::Address that represents the from. + # + # sender:: Returns an array of Net::IMAP::Address that represents the sender. + # + # reply_to:: Returns an array of Net::IMAP::Address that represents the reply-to. + # + # to:: Returns an array of Net::IMAP::Address that represents the to. + # + # cc:: Returns an array of Net::IMAP::Address that represents the cc. + # + # bcc:: Returns an array of Net::IMAP::Address that represents the bcc. + # + # in_reply_to:: Returns a string that represents the in-reply-to. + # + # message_id:: Returns a string that represents the message-id. + # + Envelope = Struct.new(:date, :subject, :from, :sender, :reply_to, + :to, :cc, :bcc, :in_reply_to, :message_id) + + # + # Net::IMAP::Address represents electronic mail addresses. + # + # ==== Fields: + # + # name:: Returns the phrase from [RFC-822] mailbox. + # + # route:: Returns the route from [RFC-822] route-addr. + # + # mailbox:: nil indicates end of [RFC-822] group. + # If non-nil and host is nil, returns [RFC-822] group name. + # Otherwise, returns [RFC-822] local-part + # + # host:: nil indicates [RFC-822] group syntax. + # Otherwise, returns [RFC-822] domain name. + # + Address = Struct.new(:name, :route, :mailbox, :host) + + # + # Net::IMAP::ContentDisposition represents Content-Disposition fields. + # + # ==== Fields: + # + # dsp_type:: Returns the disposition type. + # + # param:: Returns a hash that represents parameters of the Content-Disposition + # field. + # + ContentDisposition = Struct.new(:dsp_type, :param) + + # Net::IMAP::ThreadMember represents a thread-node returned + # by Net::IMAP#thread + # + # ==== Fields: + # + # seqno:: The sequence number of this message. + # + # children:: an array of Net::IMAP::ThreadMember objects for mail + # items that are children of this in the thread. + # + ThreadMember = Struct.new(:seqno, :children) + + # Net::IMAP::BodyTypeBasic represents basic body structures of messages. + # + # ==== Fields: + # + # media_type:: Returns the content media type name as defined in [MIME-IMB]. + # + # subtype:: Returns the content subtype name as defined in [MIME-IMB]. + # + # param:: Returns a hash that represents parameters as defined in [MIME-IMB]. + # + # content_id:: Returns a string giving the content id as defined in [MIME-IMB]. + # + # description:: Returns a string giving the content description as defined in + # [MIME-IMB]. + # + # encoding:: Returns a string giving the content transfer encoding as defined in + # [MIME-IMB]. + # + # size:: Returns a number giving the size of the body in octets. + # + # md5:: Returns a string giving the body MD5 value as defined in [MD5]. + # + # disposition:: Returns a Net::IMAP::ContentDisposition object giving + # the content disposition. + # + # language:: Returns a string or an array of strings giving the body + # language value as defined in [LANGUAGE-TAGS]. + # + # extension:: Returns extension data. + # + # multipart?:: Returns false. + # + class BodyTypeBasic < Struct.new(:media_type, :subtype, + :param, :content_id, + :description, :encoding, :size, + :md5, :disposition, :language, + :extension) + def multipart? + return false + end + + # Obsolete: use +subtype+ instead. Calling this will + # generate a warning message to +stderr+, then return + # the value of +subtype+. + def media_subtype + $stderr.printf("warning: media_subtype is obsolete.\n") + $stderr.printf(" use subtype instead.\n") + return subtype + end + end + + # Net::IMAP::BodyTypeText represents TEXT body structures of messages. + # + # ==== Fields: + # + # lines:: Returns the size of the body in text lines. + # + # And Net::IMAP::BodyTypeText has all fields of Net::IMAP::BodyTypeBasic. + # + class BodyTypeText < Struct.new(:media_type, :subtype, + :param, :content_id, + :description, :encoding, :size, + :lines, + :md5, :disposition, :language, + :extension) + def multipart? + return false + end + + # Obsolete: use +subtype+ instead. Calling this will + # generate a warning message to +stderr+, then return + # the value of +subtype+. + def media_subtype + $stderr.printf("warning: media_subtype is obsolete.\n") + $stderr.printf(" use subtype instead.\n") + return subtype + end + end + + # Net::IMAP::BodyTypeMessage represents MESSAGE/RFC822 body structures of messages. + # + # ==== Fields: + # + # envelope:: Returns a Net::IMAP::Envelope giving the envelope structure. + # + # body:: Returns an object giving the body structure. + # + # And Net::IMAP::BodyTypeMessage has all methods of Net::IMAP::BodyTypeText. + # + class BodyTypeMessage < Struct.new(:media_type, :subtype, + :param, :content_id, + :description, :encoding, :size, + :envelope, :body, :lines, + :md5, :disposition, :language, + :extension) + def multipart? + return false + end + + # Obsolete: use +subtype+ instead. Calling this will + # generate a warning message to +stderr+, then return + # the value of +subtype+. + def media_subtype + $stderr.printf("warning: media_subtype is obsolete.\n") + $stderr.printf(" use subtype instead.\n") + return subtype + end + end + + # Net::IMAP::BodyTypeMultipart represents multipart body structures + # of messages. + # + # ==== Fields: + # + # media_type:: Returns the content media type name as defined in [MIME-IMB]. + # + # subtype:: Returns the content subtype name as defined in [MIME-IMB]. + # + # parts:: Returns multiple parts. + # + # param:: Returns a hash that represents parameters as defined in [MIME-IMB]. + # + # disposition:: Returns a Net::IMAP::ContentDisposition object giving + # the content disposition. + # + # language:: Returns a string or an array of strings giving the body + # language value as defined in [LANGUAGE-TAGS]. + # + # extension:: Returns extension data. + # + # multipart?:: Returns true. + # + class BodyTypeMultipart < Struct.new(:media_type, :subtype, + :parts, + :param, :disposition, :language, + :extension) + def multipart? + return true + end + + # Obsolete: use +subtype+ instead. Calling this will + # generate a warning message to +stderr+, then return + # the value of +subtype+. + def media_subtype + $stderr.printf("warning: media_subtype is obsolete.\n") + $stderr.printf(" use subtype instead.\n") + return subtype + end + end + + class ResponseParser # :nodoc: + def parse(str) + @str = str + @pos = 0 + @lex_state = EXPR_BEG + @token = nil + return response + end + + private + + EXPR_BEG = :EXPR_BEG + EXPR_DATA = :EXPR_DATA + EXPR_TEXT = :EXPR_TEXT + EXPR_RTEXT = :EXPR_RTEXT + EXPR_CTEXT = :EXPR_CTEXT + + T_SPACE = :SPACE + T_NIL = :NIL + T_NUMBER = :NUMBER + T_ATOM = :ATOM + T_QUOTED = :QUOTED + T_LPAR = :LPAR + T_RPAR = :RPAR + T_BSLASH = :BSLASH + T_STAR = :STAR + T_LBRA = :LBRA + T_RBRA = :RBRA + T_LITERAL = :LITERAL + T_PLUS = :PLUS + T_PERCENT = :PERCENT + T_CRLF = :CRLF + T_EOF = :EOF + T_TEXT = :TEXT + + BEG_REGEXP = /\G(?:\ +(?# 1: SPACE )( +)|\ +(?# 2: NIL )(NIL)(?=[\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+])|\ +(?# 3: NUMBER )(\d+)(?=[\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+])|\ +(?# 4: ATOM )([^\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+]+)|\ +(?# 5: QUOTED )"((?:[^\x00\r\n"\\]|\\["\\])*)"|\ +(?# 6: LPAR )(\()|\ +(?# 7: RPAR )(\))|\ +(?# 8: BSLASH )(\\)|\ +(?# 9: STAR )(\*)|\ +(?# 10: LBRA )(\[)|\ +(?# 11: RBRA )(\])|\ +(?# 12: LITERAL )\{(\d+)\}\r\n|\ +(?# 13: PLUS )(\+)|\ +(?# 14: PERCENT )(%)|\ +(?# 15: CRLF )(\r\n)|\ +(?# 16: EOF )(\z))/ni + + DATA_REGEXP = /\G(?:\ +(?# 1: SPACE )( )|\ +(?# 2: NIL )(NIL)|\ +(?# 3: NUMBER )(\d+)|\ +(?# 4: QUOTED )"((?:[^\x00\r\n"\\]|\\["\\])*)"|\ +(?# 5: LITERAL )\{(\d+)\}\r\n|\ +(?# 6: LPAR )(\()|\ +(?# 7: RPAR )(\)))/ni + + TEXT_REGEXP = /\G(?:\ +(?# 1: TEXT )([^\x00\r\n]*))/ni + + RTEXT_REGEXP = /\G(?:\ +(?# 1: LBRA )(\[)|\ +(?# 2: TEXT )([^\x00\r\n]*))/ni + + CTEXT_REGEXP = /\G(?:\ +(?# 1: TEXT )([^\x00\r\n\]]*))/ni + + Token = Struct.new(:symbol, :value) + + def response + token = lookahead + case token.symbol + when T_PLUS + result = continue_req + when T_STAR + result = response_untagged + else + result = response_tagged + end + match(T_CRLF) + match(T_EOF) + return result + end + + def continue_req + match(T_PLUS) + match(T_SPACE) + return ContinuationRequest.new(resp_text, @str) + end + + def response_untagged + match(T_STAR) + match(T_SPACE) + token = lookahead + if token.symbol == T_NUMBER + return numeric_response + elsif token.symbol == T_ATOM + case token.value + when /\A(?:OK|NO|BAD|BYE|PREAUTH)\z/ni + return response_cond + when /\A(?:FLAGS)\z/ni + return flags_response + when /\A(?:LIST|LSUB)\z/ni + return list_response + when /\A(?:QUOTA)\z/ni + return getquota_response + when /\A(?:QUOTAROOT)\z/ni + return getquotaroot_response + when /\A(?:ACL)\z/ni + return getacl_response + when /\A(?:SEARCH|SORT)\z/ni + return search_response + when /\A(?:THREAD)\z/ni + return thread_response + when /\A(?:STATUS)\z/ni + return status_response + when /\A(?:CAPABILITY)\z/ni + return capability_response + else + return text_response + end + else + parse_error("unexpected token %s", token.symbol) + end + end + + def response_tagged + tag = atom + match(T_SPACE) + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return TaggedResponse.new(tag, name, resp_text, @str) + end + + def response_cond + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return UntaggedResponse.new(name, resp_text, @str) + end + + def numeric_response + n = number + match(T_SPACE) + token = match(T_ATOM) + name = token.value.upcase + case name + when "EXISTS", "RECENT", "EXPUNGE" + return UntaggedResponse.new(name, n, @str) + when "FETCH" + shift_token + match(T_SPACE) + data = FetchData.new(n, msg_att) + return UntaggedResponse.new(name, data, @str) + end + end + + def msg_att + match(T_LPAR) + attr = {} + while true + token = lookahead + case token.symbol + when T_RPAR + shift_token + break + when T_SPACE + shift_token + token = lookahead + end + case token.value + when /\A(?:ENVELOPE)\z/ni + name, val = envelope_data + when /\A(?:FLAGS)\z/ni + name, val = flags_data + when /\A(?:INTERNALDATE)\z/ni + name, val = internaldate_data + when /\A(?:RFC822(?:\.HEADER|\.TEXT)?)\z/ni + name, val = rfc822_text + when /\A(?:RFC822\.SIZE)\z/ni + name, val = rfc822_size + when /\A(?:BODY(?:STRUCTURE)?)\z/ni + name, val = body_data + when /\A(?:UID)\z/ni + name, val = uid_data + else + parse_error("unknown attribute `%s'", token.value) + end + attr[name] = val + end + return attr + end + + def envelope_data + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return name, envelope + end + + def envelope + @lex_state = EXPR_DATA + token = lookahead + if token.symbol == T_NIL + shift_token + result = nil + else + match(T_LPAR) + date = nstring + match(T_SPACE) + subject = nstring + match(T_SPACE) + from = address_list + match(T_SPACE) + sender = address_list + match(T_SPACE) + reply_to = address_list + match(T_SPACE) + to = address_list + match(T_SPACE) + cc = address_list + match(T_SPACE) + bcc = address_list + match(T_SPACE) + in_reply_to = nstring + match(T_SPACE) + message_id = nstring + match(T_RPAR) + result = Envelope.new(date, subject, from, sender, reply_to, + to, cc, bcc, in_reply_to, message_id) + end + @lex_state = EXPR_BEG + return result + end + + def flags_data + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return name, flag_list + end + + def internaldate_data + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + token = match(T_QUOTED) + return name, token.value + end + + def rfc822_text + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return name, nstring + end + + def rfc822_size + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return name, number + end + + def body_data + token = match(T_ATOM) + name = token.value.upcase + token = lookahead + if token.symbol == T_SPACE + shift_token + return name, body + end + name.concat(section) + token = lookahead + if token.symbol == T_ATOM + name.concat(token.value) + shift_token + end + match(T_SPACE) + data = nstring + return name, data + end + + def body + @lex_state = EXPR_DATA + token = lookahead + if token.symbol == T_NIL + shift_token + result = nil + else + match(T_LPAR) + token = lookahead + if token.symbol == T_LPAR + result = body_type_mpart + else + result = body_type_1part + end + match(T_RPAR) + end + @lex_state = EXPR_BEG + return result + end + + def body_type_1part + token = lookahead + case token.value + when /\A(?:TEXT)\z/ni + return body_type_text + when /\A(?:MESSAGE)\z/ni + return body_type_msg + else + return body_type_basic + end + end + + def body_type_basic + mtype, msubtype = media_type + token = lookahead + if token.symbol == T_RPAR + return BodyTypeBasic.new(mtype, msubtype) + end + match(T_SPACE) + param, content_id, desc, enc, size = body_fields + md5, disposition, language, extension = body_ext_1part + return BodyTypeBasic.new(mtype, msubtype, + param, content_id, + desc, enc, size, + md5, disposition, language, extension) + end + + def body_type_text + mtype, msubtype = media_type + match(T_SPACE) + param, content_id, desc, enc, size = body_fields + match(T_SPACE) + lines = number + md5, disposition, language, extension = body_ext_1part + return BodyTypeText.new(mtype, msubtype, + param, content_id, + desc, enc, size, + lines, + md5, disposition, language, extension) + end + + def body_type_msg + mtype, msubtype = media_type + match(T_SPACE) + param, content_id, desc, enc, size = body_fields + match(T_SPACE) + env = envelope + match(T_SPACE) + b = body + match(T_SPACE) + lines = number + md5, disposition, language, extension = body_ext_1part + return BodyTypeMessage.new(mtype, msubtype, + param, content_id, + desc, enc, size, + env, b, lines, + md5, disposition, language, extension) + end + + def body_type_mpart + parts = [] + while true + token = lookahead + if token.symbol == T_SPACE + shift_token + break + end + parts.push(body) + end + mtype = "MULTIPART" + msubtype = case_insensitive_string + param, disposition, language, extension = body_ext_mpart + return BodyTypeMultipart.new(mtype, msubtype, parts, + param, disposition, language, + extension) + end + + def media_type + mtype = case_insensitive_string + match(T_SPACE) + msubtype = case_insensitive_string + return mtype, msubtype + end + + def body_fields + param = body_fld_param + match(T_SPACE) + content_id = nstring + match(T_SPACE) + desc = nstring + match(T_SPACE) + enc = case_insensitive_string + match(T_SPACE) + size = number + return param, content_id, desc, enc, size + end + + def body_fld_param + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + end + match(T_LPAR) + param = {} + while true + token = lookahead + case token.symbol + when T_RPAR + shift_token + break + when T_SPACE + shift_token + end + name = case_insensitive_string + match(T_SPACE) + val = string + param[name] = val + end + return param + end + + def body_ext_1part + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return nil + end + md5 = nstring + + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return md5 + end + disposition = body_fld_dsp + + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return md5, disposition + end + language = body_fld_lang + + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return md5, disposition, language + end + + extension = body_extensions + return md5, disposition, language, extension + end + + def body_ext_mpart + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return nil + end + param = body_fld_param + + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return param + end + disposition = body_fld_dsp + match(T_SPACE) + language = body_fld_lang + + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return param, disposition, language + end + + extension = body_extensions + return param, disposition, language, extension + end + + def body_fld_dsp + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + end + match(T_LPAR) + dsp_type = case_insensitive_string + match(T_SPACE) + param = body_fld_param + match(T_RPAR) + return ContentDisposition.new(dsp_type, param) + end + + def body_fld_lang + token = lookahead + if token.symbol == T_LPAR + shift_token + result = [] + while true + token = lookahead + case token.symbol + when T_RPAR + shift_token + return result + when T_SPACE + shift_token + end + result.push(case_insensitive_string) + end + else + lang = nstring + if lang + return lang.upcase + else + return lang + end + end + end + + def body_extensions + result = [] + while true + token = lookahead + case token.symbol + when T_RPAR + return result + when T_SPACE + shift_token + end + result.push(body_extension) + end + end + + def body_extension + token = lookahead + case token.symbol + when T_LPAR + shift_token + result = body_extensions + match(T_RPAR) + return result + when T_NUMBER + return number + else + return nstring + end + end + + def section + str = "" + token = match(T_LBRA) + str.concat(token.value) + token = match(T_ATOM, T_NUMBER, T_RBRA) + if token.symbol == T_RBRA + str.concat(token.value) + return str + end + str.concat(token.value) + token = lookahead + if token.symbol == T_SPACE + shift_token + str.concat(token.value) + token = match(T_LPAR) + str.concat(token.value) + while true + token = lookahead + case token.symbol + when T_RPAR + str.concat(token.value) + shift_token + break + when T_SPACE + shift_token + str.concat(token.value) + end + str.concat(format_string(astring)) + end + end + token = match(T_RBRA) + str.concat(token.value) + return str + end + + def format_string(str) + case str + when "" + return '""' + when /[\x80-\xff\r\n]/n + # literal + return "{" + str.length.to_s + "}" + CRLF + str + when /[(){ \x00-\x1f\x7f%*"\\]/n + # quoted string + return '"' + str.gsub(/["\\]/n, "\\\\\\&") + '"' + else + # atom + return str + end + end + + def uid_data + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return name, number + end + + def text_response + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + @lex_state = EXPR_TEXT + token = match(T_TEXT) + @lex_state = EXPR_BEG + return UntaggedResponse.new(name, token.value) + end + + def flags_response + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return UntaggedResponse.new(name, flag_list, @str) + end + + def list_response + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return UntaggedResponse.new(name, mailbox_list, @str) + end + + def mailbox_list + attr = flag_list + match(T_SPACE) + token = match(T_QUOTED, T_NIL) + if token.symbol == T_NIL + delim = nil + else + delim = token.value + end + match(T_SPACE) + name = astring + return MailboxList.new(attr, delim, name) + end + + def getquota_response + # If quota never established, get back + # `NO Quota root does not exist'. + # If quota removed, get `()' after the + # folder spec with no mention of `STORAGE'. + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + mailbox = astring + match(T_SPACE) + match(T_LPAR) + token = lookahead + case token.symbol + when T_RPAR + shift_token + data = MailboxQuota.new(mailbox, nil, nil) + return UntaggedResponse.new(name, data, @str) + when T_ATOM + shift_token + match(T_SPACE) + token = match(T_NUMBER) + usage = token.value + match(T_SPACE) + token = match(T_NUMBER) + quota = token.value + match(T_RPAR) + data = MailboxQuota.new(mailbox, usage, quota) + return UntaggedResponse.new(name, data, @str) + else + parse_error("unexpected token %s", token.symbol) + end + end + + def getquotaroot_response + # Similar to getquota, but only admin can use getquota. + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + mailbox = astring + quotaroots = [] + while true + token = lookahead + break unless token.symbol == T_SPACE + shift_token + quotaroots.push(astring) + end + data = MailboxQuotaRoot.new(mailbox, quotaroots) + return UntaggedResponse.new(name, data, @str) + end + + def getacl_response + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + mailbox = astring + data = [] + token = lookahead + if token.symbol == T_SPACE + shift_token + while true + token = lookahead + case token.symbol + when T_CRLF + break + when T_SPACE + shift_token + end + user = astring + match(T_SPACE) + rights = astring + ##XXX data.push([user, rights]) + data.push(MailboxACLItem.new(user, rights)) + end + end + return UntaggedResponse.new(name, data, @str) + end + + def search_response + token = match(T_ATOM) + name = token.value.upcase + token = lookahead + if token.symbol == T_SPACE + shift_token + data = [] + while true + token = lookahead + case token.symbol + when T_CRLF + break + when T_SPACE + shift_token + end + data.push(number) + end + else + data = [] + end + return UntaggedResponse.new(name, data, @str) + end + + def thread_response + token = match(T_ATOM) + name = token.value.upcase + token = lookahead + + if token.symbol == T_SPACE + threads = [] + + while true + shift_token + token = lookahead + + case token.symbol + when T_LPAR + threads << thread_branch(token) + when T_CRLF + break + end + end + else + # no member + threads = [] + end + + return UntaggedResponse.new(name, threads, @str) + end + + def thread_branch(token) + rootmember = nil + lastmember = nil + + while true + shift_token # ignore first T_LPAR + token = lookahead + + case token.symbol + when T_NUMBER + # new member + newmember = ThreadMember.new(number, []) + if rootmember.nil? + rootmember = newmember + else + lastmember.children << newmember + end + lastmember = newmember + when T_SPACE + # do nothing + when T_LPAR + if rootmember.nil? + # dummy member + lastmember = rootmember = ThreadMember.new(nil, []) + end + + lastmember.children << thread_branch(token) + when T_RPAR + break + end + end + + return rootmember + end + + def status_response + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + mailbox = astring + match(T_SPACE) + match(T_LPAR) + attr = {} + while true + token = lookahead + case token.symbol + when T_RPAR + shift_token + break + when T_SPACE + shift_token + end + token = match(T_ATOM) + key = token.value.upcase + match(T_SPACE) + val = number + attr[key] = val + end + data = StatusData.new(mailbox, attr) + return UntaggedResponse.new(name, data, @str) + end + + def capability_response + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + data = [] + while true + token = lookahead + case token.symbol + when T_CRLF + break + when T_SPACE + shift_token + end + data.push(atom.upcase) + end + return UntaggedResponse.new(name, data, @str) + end + + def resp_text + @lex_state = EXPR_RTEXT + token = lookahead + if token.symbol == T_LBRA + code = resp_text_code + else + code = nil + end + token = match(T_TEXT) + @lex_state = EXPR_BEG + return ResponseText.new(code, token.value) + end + + def resp_text_code + @lex_state = EXPR_BEG + match(T_LBRA) + token = match(T_ATOM) + name = token.value.upcase + case name + when /\A(?:ALERT|PARSE|READ-ONLY|READ-WRITE|TRYCREATE|NOMODSEQ)\z/n + result = ResponseCode.new(name, nil) + when /\A(?:PERMANENTFLAGS)\z/n + match(T_SPACE) + result = ResponseCode.new(name, flag_list) + when /\A(?:UIDVALIDITY|UIDNEXT|UNSEEN)\z/n + match(T_SPACE) + result = ResponseCode.new(name, number) + else + match(T_SPACE) + @lex_state = EXPR_CTEXT + token = match(T_TEXT) + @lex_state = EXPR_BEG + result = ResponseCode.new(name, token.value) + end + match(T_RBRA) + @lex_state = EXPR_RTEXT + return result + end + + def address_list + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + else + result = [] + match(T_LPAR) + while true + token = lookahead + case token.symbol + when T_RPAR + shift_token + break + when T_SPACE + shift_token + end + result.push(address) + end + return result + end + end + + ADDRESS_REGEXP = /\G\ +(?# 1: NAME )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \ +(?# 2: ROUTE )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \ +(?# 3: MAILBOX )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \ +(?# 4: HOST )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)")\ +\)/ni + + def address + match(T_LPAR) + if @str.index(ADDRESS_REGEXP, @pos) + # address does not include literal. + @pos = $~.end(0) + name = $1 + route = $2 + mailbox = $3 + host = $4 + for s in [name, route, mailbox, host] + if s + s.gsub!(/\\(["\\])/n, "\\1") + end + end + else + name = nstring + match(T_SPACE) + route = nstring + match(T_SPACE) + mailbox = nstring + match(T_SPACE) + host = nstring + match(T_RPAR) + end + return Address.new(name, route, mailbox, host) + end + +# def flag_list +# result = [] +# match(T_LPAR) +# while true +# token = lookahead +# case token.symbol +# when T_RPAR +# shift_token +# break +# when T_SPACE +# shift_token +# end +# result.push(flag) +# end +# return result +# end + +# def flag +# token = lookahead +# if token.symbol == T_BSLASH +# shift_token +# token = lookahead +# if token.symbol == T_STAR +# shift_token +# return token.value.intern +# else +# return atom.intern +# end +# else +# return atom +# end +# end + + FLAG_REGEXP = /\ +(?# FLAG )\\([^\x80-\xff(){ \x00-\x1f\x7f%"\\]+)|\ +(?# ATOM )([^\x80-\xff(){ \x00-\x1f\x7f%*"\\]+)/n + + def flag_list + if @str.index(/\(([^)]*)\)/ni, @pos) + @pos = $~.end(0) + return $1.scan(FLAG_REGEXP).collect { |flag, atom| + atom || flag.capitalize.intern + } + else + parse_error("invalid flag list") + end + end + + def nstring + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + else + return string + end + end + + def astring + token = lookahead + if string_token?(token) + return string + else + return atom + end + end + + def string + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + end + token = match(T_QUOTED, T_LITERAL) + return token.value + end + + STRING_TOKENS = [T_QUOTED, T_LITERAL, T_NIL] + + def string_token?(token) + return STRING_TOKENS.include?(token.symbol) + end + + def case_insensitive_string + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + end + token = match(T_QUOTED, T_LITERAL) + return token.value.upcase + end + + def atom + result = "" + while true + token = lookahead + if atom_token?(token) + result.concat(token.value) + shift_token + else + if result.empty? + parse_error("unexpected token %s", token.symbol) + else + return result + end + end + end + end + + ATOM_TOKENS = [ + T_ATOM, + T_NUMBER, + T_NIL, + T_LBRA, + T_RBRA, + T_PLUS + ] + + def atom_token?(token) + return ATOM_TOKENS.include?(token.symbol) + end + + def number + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + end + token = match(T_NUMBER) + return token.value.to_i + end + + def nil_atom + match(T_NIL) + return nil + end + + def match(*args) + token = lookahead + unless args.include?(token.symbol) + parse_error('unexpected token %s (expected %s)', + token.symbol.id2name, + args.collect {|i| i.id2name}.join(" or ")) + end + shift_token + return token + end + + def lookahead + unless @token + @token = next_token + end + return @token + end + + def shift_token + @token = nil + end + + def next_token + case @lex_state + when EXPR_BEG + if @str.index(BEG_REGEXP, @pos) + @pos = $~.end(0) + if $1 + return Token.new(T_SPACE, $+) + elsif $2 + return Token.new(T_NIL, $+) + elsif $3 + return Token.new(T_NUMBER, $+) + elsif $4 + return Token.new(T_ATOM, $+) + elsif $5 + return Token.new(T_QUOTED, + $+.gsub(/\\(["\\])/n, "\\1")) + elsif $6 + return Token.new(T_LPAR, $+) + elsif $7 + return Token.new(T_RPAR, $+) + elsif $8 + return Token.new(T_BSLASH, $+) + elsif $9 + return Token.new(T_STAR, $+) + elsif $10 + return Token.new(T_LBRA, $+) + elsif $11 + return Token.new(T_RBRA, $+) + elsif $12 + len = $+.to_i + val = @str[@pos, len] + @pos += len + return Token.new(T_LITERAL, val) + elsif $13 + return Token.new(T_PLUS, $+) + elsif $14 + return Token.new(T_PERCENT, $+) + elsif $15 + return Token.new(T_CRLF, $+) + elsif $16 + return Token.new(T_EOF, $+) + else + parse_error("[Net::IMAP BUG] BEG_REGEXP is invalid") + end + else + @str.index(/\S*/n, @pos) + parse_error("unknown token - %s", $&.dump) + end + when EXPR_DATA + if @str.index(DATA_REGEXP, @pos) + @pos = $~.end(0) + if $1 + return Token.new(T_SPACE, $+) + elsif $2 + return Token.new(T_NIL, $+) + elsif $3 + return Token.new(T_NUMBER, $+) + elsif $4 + return Token.new(T_QUOTED, + $+.gsub(/\\(["\\])/n, "\\1")) + elsif $5 + len = $+.to_i + val = @str[@pos, len] + @pos += len + return Token.new(T_LITERAL, val) + elsif $6 + return Token.new(T_LPAR, $+) + elsif $7 + return Token.new(T_RPAR, $+) + else + parse_error("[Net::IMAP BUG] DATA_REGEXP is invalid") + end + else + @str.index(/\S*/n, @pos) + parse_error("unknown token - %s", $&.dump) + end + when EXPR_TEXT + if @str.index(TEXT_REGEXP, @pos) + @pos = $~.end(0) + if $1 + return Token.new(T_TEXT, $+) + else + parse_error("[Net::IMAP BUG] TEXT_REGEXP is invalid") + end + else + @str.index(/\S*/n, @pos) + parse_error("unknown token - %s", $&.dump) + end + when EXPR_RTEXT + if @str.index(RTEXT_REGEXP, @pos) + @pos = $~.end(0) + if $1 + return Token.new(T_LBRA, $+) + elsif $2 + return Token.new(T_TEXT, $+) + else + parse_error("[Net::IMAP BUG] RTEXT_REGEXP is invalid") + end + else + @str.index(/\S*/n, @pos) + parse_error("unknown token - %s", $&.dump) + end + when EXPR_CTEXT + if @str.index(CTEXT_REGEXP, @pos) + @pos = $~.end(0) + if $1 + return Token.new(T_TEXT, $+) + else + parse_error("[Net::IMAP BUG] CTEXT_REGEXP is invalid") + end + else + @str.index(/\S*/n, @pos) #/ + parse_error("unknown token - %s", $&.dump) + end + else + parse_error("illegal @lex_state - %s", @lex_state.inspect) + end + end + + def parse_error(fmt, *args) + if IMAP.debug + $stderr.printf("@str: %s\n", @str.dump) + $stderr.printf("@pos: %d\n", @pos) + $stderr.printf("@lex_state: %s\n", @lex_state) + if @token + $stderr.printf("@token.symbol: %s\n", @token.symbol) + $stderr.printf("@token.value: %s\n", @token.value.inspect) + end + end + raise ResponseParseError, format(fmt, *args) + end + end + + # Authenticator for the "LOGIN" authentication type. See + # #authenticate(). + class LoginAuthenticator + def process(data) + case @state + when STATE_USER + @state = STATE_PASSWORD + return @user + when STATE_PASSWORD + return @password + end + end + + private + + STATE_USER = :USER + STATE_PASSWORD = :PASSWORD + + def initialize(user, password) + @user = user + @password = password + @state = STATE_USER + end + end + add_authenticator "LOGIN", LoginAuthenticator + + # Authenticator for the "CRAM-MD5" authentication type. See + # #authenticate(). + class CramMD5Authenticator + def process(challenge) + digest = hmac_md5(challenge, @password) + return @user + " " + digest + end + + private + + def initialize(user, password) + @user = user + @password = password + end + + def hmac_md5(text, key) + if key.length > 64 + key = Digest::MD5.digest(key) + end + + k_ipad = key + "\0" * (64 - key.length) + k_opad = key + "\0" * (64 - key.length) + for i in 0..63 + k_ipad[i] ^= 0x36 + k_opad[i] ^= 0x5c + end + + digest = Digest::MD5.digest(k_ipad + text) + + return Digest::MD5.hexdigest(k_opad + digest) + end + end + add_authenticator "CRAM-MD5", CramMD5Authenticator + + # Superclass of IMAP errors. + class Error < StandardError + end + + # Error raised when data is in the incorrect format. + class DataFormatError < Error + end + + # Error raised when a response from the server is non-parseable. + class ResponseParseError < Error + end + + # Superclass of all errors used to encapsulate "fail" responses + # from the server. + class ResponseError < Error + end + + # Error raised upon a "NO" response from the server, indicating + # that the client command could not be completed successfully. + class NoResponseError < ResponseError + end + + # Error raised upon a "BAD" response from the server, indicating + # that the client command violated the IMAP protocol, or an internal + # server failure has occurred. + class BadResponseError < ResponseError + end + + # Error raised upon a "BYE" response from the server, indicating + # that the client is not being allowed to login, or has been timed + # out due to inactivity. + class ByeResponseError < ResponseError + end + end +end + +if __FILE__ == $0 + # :enddoc: + require "getoptlong" + + $stdout.sync = true + $port = nil + $user = ENV["USER"] || ENV["LOGNAME"] + $auth = "login" + $ssl = false + + def usage + $stderr.print < + + --help print this message + --port=PORT specifies port + --user=USER specifies user + --auth=AUTH specifies auth type + --ssl use ssl +EOF + end + + def get_password + print "password: " + system("stty", "-echo") + begin + return gets.chop + ensure + system("stty", "echo") + print "\n" + end + end + + def get_command + printf("%s@%s> ", $user, $host) + if line = gets + return line.strip.split(/\s+/) + else + return nil + end + end + + parser = GetoptLong.new + parser.set_options(['--debug', GetoptLong::NO_ARGUMENT], + ['--help', GetoptLong::NO_ARGUMENT], + ['--port', GetoptLong::REQUIRED_ARGUMENT], + ['--user', GetoptLong::REQUIRED_ARGUMENT], + ['--auth', GetoptLong::REQUIRED_ARGUMENT], + ['--ssl', GetoptLong::NO_ARGUMENT]) + begin + parser.each_option do |name, arg| + case name + when "--port" + $port = arg + when "--user" + $user = arg + when "--auth" + $auth = arg + when "--ssl" + $ssl = true + when "--debug" + Net::IMAP.debug = true + when "--help" + usage + exit(1) + end + end + rescue + usage + exit(1) + end + + $host = ARGV.shift + unless $host + usage + exit(1) + end + $port ||= $ssl ? 993 : 143 + + imap = Net::IMAP.new($host, $port, $ssl) + begin + password = get_password + imap.authenticate($auth, $user, password) + while true + cmd, *args = get_command + break unless cmd + begin + case cmd + when "list" + for mbox in imap.list("", args[0] || "*") + if mbox.attr.include?(Net::IMAP::NOSELECT) + prefix = "!" + elsif mbox.attr.include?(Net::IMAP::MARKED) + prefix = "*" + else + prefix = " " + end + print prefix, mbox.name, "\n" + end + when "select" + imap.select(args[0] || "inbox") + print "ok\n" + when "close" + imap.close + print "ok\n" + when "summary" + unless messages = imap.responses["EXISTS"][-1] + puts "not selected" + next + end + if messages > 0 + for data in imap.fetch(1..-1, ["ENVELOPE"]) + print data.seqno, ": ", data.attr["ENVELOPE"].subject, "\n" + end + else + puts "no message" + end + when "fetch" + if args[0] + data = imap.fetch(args[0].to_i, ["RFC822.HEADER", "RFC822.TEXT"])[0] + puts data.attr["RFC822.HEADER"] + puts data.attr["RFC822.TEXT"] + else + puts "missing argument" + end + when "logout", "exit", "quit" + break + when "help", "?" + print <. +# +# Documented by William Webber and Minero Aoki. +# +# This program is free software. You can re-distribute and/or +# modify this program under the same terms as Ruby itself, +# Ruby Distribute License or GNU General Public License. +# +# NOTE: You can find Japanese version of this document in +# the doc/net directory of the standard ruby interpreter package. +# +# $Id: pop.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# +# See Net::POP3 for documentation. +# + +require 'net/protocol' +require 'digest/md5' + +module Net + + # Non-authentication POP3 protocol error + # (reply code "-ERR", except authentication). + class POPError < ProtocolError; end + + # POP3 authentication error. + class POPAuthenticationError < ProtoAuthError; end + + # Unexpected response from the server. + class POPBadResponse < POPError; end + + # + # = Net::POP3 + # + # == What is This Library? + # + # This library provides functionality for retrieving + # email via POP3, the Post Office Protocol version 3. For details + # of POP3, see [RFC1939] (http://www.ietf.org/rfc/rfc1939.txt). + # + # == Examples + # + # === Retrieving Messages + # + # This example retrieves messages from the server and deletes them + # on the server. + # + # Messages are written to files named 'inbox/1', 'inbox/2', .... + # Replace 'pop.example.com' with your POP3 server address, and + # 'YourAccount' and 'YourPassword' with the appropriate account + # details. + # + # require 'net/pop' + # + # pop = Net::POP3.new('pop.example.com') + # pop.start('YourAccount', 'YourPassword') # (1) + # if pop.mails.empty? + # puts 'No mail.' + # else + # i = 0 + # pop.each_mail do |m| # or "pop.mails.each ..." # (2) + # File.open("inbox/#{i}", 'w') do |f| + # f.write m.pop + # end + # m.delete + # i += 1 + # end + # puts "#{pop.mails.size} mails popped." + # end + # pop.finish # (3) + # + # 1. Call Net::POP3#start and start POP session. + # 2. Access messages by using POP3#each_mail and/or POP3#mails. + # 3. Close POP session by calling POP3#finish or use the block form of #start. + # + # === Shortened Code + # + # The example above is very verbose. You can shorten the code by using + # some utility methods. First, the block form of Net::POP3.start can + # be used instead of POP3.new, POP3#start and POP3#finish. + # + # require 'net/pop' + # + # Net::POP3.start('pop.example.com', 110, + # 'YourAccount', 'YourPassword') do |pop| + # if pop.mails.empty? + # puts 'No mail.' + # else + # i = 0 + # pop.each_mail do |m| # or "pop.mails.each ..." + # File.open("inbox/#{i}", 'w') do |f| + # f.write m.pop + # end + # m.delete + # i += 1 + # end + # puts "#{pop.mails.size} mails popped." + # end + # end + # + # POP3#delete_all is an alternative for #each_mail and #delete. + # + # require 'net/pop' + # + # Net::POP3.start('pop.example.com', 110, + # 'YourAccount', 'YourPassword') do |pop| + # if pop.mails.empty? + # puts 'No mail.' + # else + # i = 1 + # pop.delete_all do |m| + # File.open("inbox/#{i}", 'w') do |f| + # f.write m.pop + # end + # i += 1 + # end + # end + # end + # + # And here is an even shorter example. + # + # require 'net/pop' + # + # i = 0 + # Net::POP3.delete_all('pop.example.com', 110, + # 'YourAccount', 'YourPassword') do |m| + # File.open("inbox/#{i}", 'w') do |f| + # f.write m.pop + # end + # i += 1 + # end + # + # === Memory Space Issues + # + # All the examples above get each message as one big string. + # This example avoids this. + # + # require 'net/pop' + # + # i = 1 + # Net::POP3.delete_all('pop.example.com', 110, + # 'YourAccount', 'YourPassword') do |m| + # File.open("inbox/#{i}", 'w') do |f| + # m.pop do |chunk| # get a message little by little. + # f.write chunk + # end + # i += 1 + # end + # end + # + # === Using APOP + # + # The net/pop library supports APOP authentication. + # To use APOP, use the Net::APOP class instead of the Net::POP3 class. + # You can use the utility method, Net::POP3.APOP(). For example: + # + # require 'net/pop' + # + # # Use APOP authentication if $isapop == true + # pop = Net::POP3.APOP($is_apop).new('apop.example.com', 110) + # pop.start(YourAccount', 'YourPassword') do |pop| + # # Rest of the code is the same. + # end + # + # === Fetch Only Selected Mail Using 'UIDL' POP Command + # + # If your POP server provides UIDL functionality, + # you can grab only selected mails from the POP server. + # e.g. + # + # def need_pop?( id ) + # # determine if we need pop this mail... + # end + # + # Net::POP3.start('pop.example.com', 110, + # 'Your account', 'Your password') do |pop| + # pop.mails.select { |m| need_pop?(m.unique_id) }.each do |m| + # do_something(m.pop) + # end + # end + # + # The POPMail#unique_id() method returns the unique-id of the message as a + # String. Normally the unique-id is a hash of the message. + # + class POP3 < Protocol + + Revision = %q$Revision: 11708 $.split[1] + + # + # Class Parameters + # + + # The default port for POP3 connections, port 110 + def POP3.default_port + 110 + end + + def POP3.socket_type #:nodoc: obsolete + Net::InternetMessageIO + end + + # + # Utilities + # + + # Returns the APOP class if +isapop+ is true; otherwise, returns + # the POP class. For example: + # + # # Example 1 + # pop = Net::POP3::APOP($is_apop).new(addr, port) + # + # # Example 2 + # Net::POP3::APOP($is_apop).start(addr, port) do |pop| + # .... + # end + # + def POP3.APOP( isapop ) + isapop ? APOP : POP3 + end + + # Starts a POP3 session and iterates over each POPMail object, + # yielding it to the +block+. + # This method is equivalent to: + # + # Net::POP3.start(address, port, account, password) do |pop| + # pop.each_mail do |m| + # yield m + # end + # end + # + # This method raises a POPAuthenticationError if authentication fails. + # + # === Example + # + # Net::POP3.foreach('pop.example.com', 110, + # 'YourAccount', 'YourPassword') do |m| + # file.write m.pop + # m.delete if $DELETE + # end + # + def POP3.foreach( address, port = nil, + account = nil, password = nil, + isapop = false, &block ) # :yields: message + start(address, port, account, password, isapop) {|pop| + pop.each_mail(&block) + } + end + + # Starts a POP3 session and deletes all messages on the server. + # If a block is given, each POPMail object is yielded to it before + # being deleted. + # + # This method raises a POPAuthenticationError if authentication fails. + # + # === Example + # + # Net::POP3.delete_all('pop.example.com', 110, + # 'YourAccount', 'YourPassword') do |m| + # file.write m.pop + # end + # + def POP3.delete_all( address, port = nil, + account = nil, password = nil, + isapop = false, &block ) + start(address, port, account, password, isapop) {|pop| + pop.delete_all(&block) + } + end + + # Opens a POP3 session, attempts authentication, and quits. + # + # This method raises POPAuthenticationError if authentication fails. + # + # === Example: normal POP3 + # + # Net::POP3.auth_only('pop.example.com', 110, + # 'YourAccount', 'YourPassword') + # + # === Example: APOP + # + # Net::POP3.auth_only('pop.example.com', 110, + # 'YourAccount', 'YourPassword', true) + # + def POP3.auth_only( address, port = nil, + account = nil, password = nil, + isapop = false ) + new(address, port, isapop).auth_only account, password + end + + # Starts a pop3 session, attempts authentication, and quits. + # This method must not be called while POP3 session is opened. + # This method raises POPAuthenticationError if authentication fails. + def auth_only( account, password ) + raise IOError, 'opening previously opened POP session' if started? + start(account, password) { + ; + } + end + + # + # Session management + # + + # Creates a new POP3 object and open the connection. Equivalent to + # + # Net::POP3.new(address, port, isapop).start(account, password) + # + # If +block+ is provided, yields the newly-opened POP3 object to it, + # and automatically closes it at the end of the session. + # + # === Example + # + # Net::POP3.start(addr, port, account, password) do |pop| + # pop.each_mail do |m| + # file.write m.pop + # m.delete + # end + # end + # + def POP3.start( address, port = nil, + account = nil, password = nil, + isapop = false, &block ) # :yield: pop + new(address, port, isapop).start(account, password, &block) + end + + # Creates a new POP3 object. + # + # +address+ is the hostname or ip address of your POP3 server. + # + # The optional +port+ is the port to connect to; it defaults to 110. + # + # The optional +isapop+ specifies whether this connection is going + # to use APOP authentication; it defaults to +false+. + # + # This method does *not* open the TCP connection. + def initialize( addr, port = nil, isapop = false ) + @address = addr + @port = port || self.class.default_port + @apop = isapop + + @command = nil + @socket = nil + @started = false + @open_timeout = 30 + @read_timeout = 60 + @debug_output = nil + + @mails = nil + @n_mails = nil + @n_bytes = nil + end + + # Does this instance use APOP authentication? + def apop? + @apop + end + + # Provide human-readable stringification of class state. + def inspect + "#<#{self.class} #{@address}:#{@port} open=#{@started}>" + end + + # *WARNING*: This method causes a serious security hole. + # Use this method only for debugging. + # + # Set an output stream for debugging. + # + # === Example + # + # pop = Net::POP.new(addr, port) + # pop.set_debug_output $stderr + # pop.start(account, passwd) do |pop| + # .... + # end + # + def set_debug_output( arg ) + @debug_output = arg + end + + # The address to connect to. + attr_reader :address + + # The port number to connect to. + attr_reader :port + + # Seconds to wait until a connection is opened. + # If the POP3 object cannot open a connection within this time, + # it raises a TimeoutError exception. + attr_accessor :open_timeout + + # Seconds to wait until reading one block (by one read(1) call). + # If the POP3 object cannot complete a read() within this time, + # it raises a TimeoutError exception. + attr_reader :read_timeout + + # Set the read timeout. + def read_timeout=( sec ) + @command.socket.read_timeout = sec if @command + @read_timeout = sec + end + + # +true+ if the POP3 session has started. + def started? + @started + end + + alias active? started? #:nodoc: obsolete + + # Starts a POP3 session. + # + # When called with block, gives a POP3 object to the block and + # closes the session after block call finishes. + # + # This method raises a POPAuthenticationError if authentication fails. + def start( account, password ) # :yield: pop + raise IOError, 'POP session already started' if @started + + if block_given? + begin + do_start account, password + return yield(self) + ensure + do_finish + end + else + do_start account, password + return self + end + end + + def do_start( account, password ) + @socket = self.class.socket_type.old_open(@address, @port, + @open_timeout, @read_timeout, @debug_output) + on_connect + @command = POP3Command.new(@socket) + if apop? + @command.apop account, password + else + @command.auth account, password + end + @started = true + ensure + do_finish if not @started + end + private :do_start + + def on_connect + end + private :on_connect + + # Finishes a POP3 session and closes TCP connection. + def finish + raise IOError, 'POP session not yet started' unless started? + do_finish + end + + def do_finish + @mails = nil + @command.quit if @command + ensure + @started = false + @command = nil + @socket.close if @socket and not @socket.closed? + @socket = nil + end + private :do_finish + + def command + raise IOError, 'POP session not opened yet' \ + if not @socket or @socket.closed? + @command + end + private :command + + # + # POP protocol wrapper + # + + # Returns the number of messages on the POP server. + def n_mails + return @n_mails if @n_mails + @n_mails, @n_bytes = command().stat + @n_mails + end + + # Returns the total size in bytes of all the messages on the POP server. + def n_bytes + return @n_bytes if @n_bytes + @n_mails, @n_bytes = command().stat + @n_bytes + end + + # Returns an array of Net::POPMail objects, representing all the + # messages on the server. This array is renewed when the session + # restarts; otherwise, it is fetched from the server the first time + # this method is called (directly or indirectly) and cached. + # + # This method raises a POPError if an error occurs. + def mails + return @mails.dup if @mails + if n_mails() == 0 + # some popd raises error for LIST on the empty mailbox. + @mails = [] + return [] + end + + @mails = command().list.map {|num, size| + POPMail.new(num, size, self, command()) + } + @mails.dup + end + + # Yields each message to the passed-in block in turn. + # Equivalent to: + # + # pop3.mails.each do |popmail| + # .... + # end + # + # This method raises a POPError if an error occurs. + def each_mail( &block ) # :yield: message + mails().each(&block) + end + + alias each each_mail + + # Deletes all messages on the server. + # + # If called with a block, yields each message in turn before deleting it. + # + # === Example + # + # n = 1 + # pop.delete_all do |m| + # File.open("inbox/#{n}") do |f| + # f.write m.pop + # end + # n += 1 + # end + # + # This method raises a POPError if an error occurs. + # + def delete_all # :yield: message + mails().each do |m| + yield m if block_given? + m.delete unless m.deleted? + end + end + + # Resets the session. This clears all "deleted" marks from messages. + # + # This method raises a POPError if an error occurs. + def reset + command().rset + mails().each do |m| + m.instance_eval { + @deleted = false + } + end + end + + def set_all_uids #:nodoc: internal use only (called from POPMail#uidl) + command().uidl.each do |num, uid| + @mails.find {|m| m.number == num }.uid = uid + end + end + + end # class POP3 + + # class aliases + POP = POP3 + POPSession = POP3 + POP3Session = POP3 + + # + # This class is equivalent to POP3, except that it uses APOP authentication. + # + class APOP < POP3 + # Always returns true. + def apop? + true + end + end + + # class aliases + APOPSession = APOP + + # + # This class represents a message which exists on the POP server. + # Instances of this class are created by the POP3 class; they should + # not be directly created by the user. + # + class POPMail + + def initialize( num, len, pop, cmd ) #:nodoc: + @number = num + @length = len + @pop = pop + @command = cmd + @deleted = false + @uid = nil + end + + # The sequence number of the message on the server. + attr_reader :number + + # The length of the message in octets. + attr_reader :length + alias size length + + # Provide human-readable stringification of class state. + def inspect + "#<#{self.class} #{@number}#{@deleted ? ' deleted' : ''}>" + end + + # + # This method fetches the message. If called with a block, the + # message is yielded to the block one chunk at a time. If called + # without a block, the message is returned as a String. The optional + # +dest+ argument will be prepended to the returned String; this + # argument is essentially obsolete. + # + # === Example without block + # + # POP3.start('pop.example.com', 110, + # 'YourAccount, 'YourPassword') do |pop| + # n = 1 + # pop.mails.each do |popmail| + # File.open("inbox/#{n}", 'w') do |f| + # f.write popmail.pop + # end + # popmail.delete + # n += 1 + # end + # end + # + # === Example with block + # + # POP3.start('pop.example.com', 110, + # 'YourAccount, 'YourPassword') do |pop| + # n = 1 + # pop.mails.each do |popmail| + # File.open("inbox/#{n}", 'w') do |f| + # popmail.pop do |chunk| #### + # f.write chunk + # end + # end + # n += 1 + # end + # end + # + # This method raises a POPError if an error occurs. + # + def pop( dest = '', &block ) # :yield: message_chunk + if block_given? + @command.retr(@number, &block) + nil + else + @command.retr(@number) do |chunk| + dest << chunk + end + dest + end + end + + alias all pop #:nodoc: obsolete + alias mail pop #:nodoc: obsolete + + # Fetches the message header and +lines+ lines of body. + # + # The optional +dest+ argument is obsolete. + # + # This method raises a POPError if an error occurs. + def top( lines, dest = '' ) + @command.top(@number, lines) do |chunk| + dest << chunk + end + dest + end + + # Fetches the message header. + # + # The optional +dest+ argument is obsolete. + # + # This method raises a POPError if an error occurs. + def header( dest = '' ) + top(0, dest) + end + + # Marks a message for deletion on the server. Deletion does not + # actually occur until the end of the session; deletion may be + # cancelled for _all_ marked messages by calling POP3#reset(). + # + # This method raises a POPError if an error occurs. + # + # === Example + # + # POP3.start('pop.example.com', 110, + # 'YourAccount, 'YourPassword') do |pop| + # n = 1 + # pop.mails.each do |popmail| + # File.open("inbox/#{n}", 'w') do |f| + # f.write popmail.pop + # end + # popmail.delete #### + # n += 1 + # end + # end + # + def delete + @command.dele @number + @deleted = true + end + + alias delete! delete #:nodoc: obsolete + + # True if the mail has been deleted. + def deleted? + @deleted + end + + # Returns the unique-id of the message. + # Normally the unique-id is a hash string of the message. + # + # This method raises a POPError if an error occurs. + def unique_id + return @uid if @uid + @pop.set_all_uids + @uid + end + + alias uidl unique_id + + def uid=( uid ) #:nodoc: internal use only (used from POP3#set_all_uids) + @uid = uid + end + + end # class POPMail + + + class POP3Command #:nodoc: internal use only + + def initialize( sock ) + @socket = sock + @error_occured = false + res = check_response(critical { recv_response() }) + @apop_stamp = res.slice(/<.+>/) + end + + def inspect + "#<#{self.class} socket=#{@socket}>" + end + + def auth( account, password ) + check_response_auth(critical { + check_response_auth(get_response('USER %s', account)) + get_response('PASS %s', password) + }) + end + + def apop( account, password ) + raise POPAuthenticationError, 'not APOP server; cannot login' \ + unless @apop_stamp + check_response_auth(critical { + get_response('APOP %s %s', + account, + Digest::MD5.hexdigest(@apop_stamp + password)) + }) + end + + def list + critical { + getok 'LIST' + list = [] + @socket.each_list_item do |line| + m = /\A(\d+)[ \t]+(\d+)/.match(line) or + raise POPBadResponse, "bad response: #{line}" + list.push [m[1].to_i, m[2].to_i] + end + return list + } + end + + def stat + res = check_response(critical { get_response('STAT') }) + m = /\A\+OK\s+(\d+)\s+(\d+)/.match(res) or + raise POPBadResponse, "wrong response format: #{res}" + [m[1].to_i, m[2].to_i] + end + + def rset + check_response(critical { get_response 'RSET' }) + end + + def top( num, lines = 0, &block ) + critical { + getok('TOP %d %d', num, lines) + @socket.each_message_chunk(&block) + } + end + + def retr( num, &block ) + critical { + getok('RETR %d', num) + @socket.each_message_chunk(&block) + } + end + + def dele( num ) + check_response(critical { get_response('DELE %d', num) }) + end + + def uidl( num = nil ) + if num + res = check_response(critical { get_response('UIDL %d', num) }) + return res.split(/ /)[1] + else + critical { + getok('UIDL') + table = {} + @socket.each_list_item do |line| + num, uid = line.split + table[num.to_i] = uid + end + return table + } + end + end + + def quit + check_response(critical { get_response('QUIT') }) + end + + private + + def getok( fmt, *fargs ) + @socket.writeline sprintf(fmt, *fargs) + check_response(recv_response()) + end + + def get_response( fmt, *fargs ) + @socket.writeline sprintf(fmt, *fargs) + recv_response() + end + + def recv_response + @socket.readline + end + + def check_response( res ) + raise POPError, res unless /\A\+OK/i === res + res + end + + def check_response_auth( res ) + raise POPAuthenticationError, res unless /\A\+OK/i === res + res + end + + def critical + return '+OK dummy ok response' if @error_occured + begin + return yield() + rescue Exception + @error_occured = true + raise + end + end + + end # class POP3Command + +end # module Net + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/protocol.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/protocol.rb new file mode 100644 index 0000000000..b898030093 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/protocol.rb @@ -0,0 +1,390 @@ +# +# = net/protocol.rb +# +#-- +# Copyright (c) 1999-2005 Yukihiro Matsumoto +# Copyright (c) 1999-2005 Minero Aoki +# +# written and maintained by Minero Aoki +# +# This program is free software. You can re-distribute and/or +# modify this program under the same terms as Ruby itself, +# Ruby Distribute License or GNU General Public License. +# +# $Id: protocol.rb 11708 2007-02-12 23:01:19Z shyouhei $ +#++ +# +# WARNING: This file is going to remove. +# Do not rely on the implementation written in this file. +# + +require 'socket' +require 'timeout' + +module Net # :nodoc: + + class Protocol #:nodoc: internal use only + private + def Protocol.protocol_param(name, val) + module_eval(<<-End, __FILE__, __LINE__ + 1) + def #{name} + #{val} + end + End + end + end + + + class ProtocolError < StandardError; end + class ProtoSyntaxError < ProtocolError; end + class ProtoFatalError < ProtocolError; end + class ProtoUnknownError < ProtocolError; end + class ProtoServerError < ProtocolError; end + class ProtoAuthError < ProtocolError; end + class ProtoCommandError < ProtocolError; end + class ProtoRetriableError < ProtocolError; end + ProtocRetryError = ProtoRetriableError + + + class BufferedIO #:nodoc: internal use only + def initialize(io) + @io = io + @read_timeout = 60 + @debug_output = nil + @rbuf = '' + end + + attr_reader :io + attr_accessor :read_timeout + attr_accessor :debug_output + + def inspect + "#<#{self.class} io=#{@io}>" + end + + def closed? + @io.closed? + end + + def close + @io.close + end + + # + # Read + # + + public + + def read(len, dest = '', ignore_eof = false) + LOG "reading #{len} bytes..." + read_bytes = 0 + begin + while read_bytes + @rbuf.size < len + dest << (s = rbuf_consume(@rbuf.size)) + read_bytes += s.size + rbuf_fill + end + dest << (s = rbuf_consume(len - read_bytes)) + read_bytes += s.size + rescue EOFError + raise unless ignore_eof + end + LOG "read #{read_bytes} bytes" + dest + end + + def read_all(dest = '') + LOG 'reading all...' + read_bytes = 0 + begin + while true + dest << (s = rbuf_consume(@rbuf.size)) + read_bytes += s.size + rbuf_fill + end + rescue EOFError + ; + end + LOG "read #{read_bytes} bytes" + dest + end + + def readuntil(terminator, ignore_eof = false) + begin + until idx = @rbuf.index(terminator) + rbuf_fill + end + return rbuf_consume(idx + terminator.size) + rescue EOFError + raise unless ignore_eof + return rbuf_consume(@rbuf.size) + end + end + + def readline + readuntil("\n").chop + end + + private + + def rbuf_fill + timeout(@read_timeout) { + @rbuf << @io.sysread(1024) + } + end + + def rbuf_consume(len) + s = @rbuf.slice!(0, len) + @debug_output << %Q[-> #{s.dump}\n] if @debug_output + s + end + + # + # Write + # + + public + + def write(str) + writing { + write0 str + } + end + + def writeline(str) + writing { + write0 str + "\r\n" + } + end + + private + + def writing + @written_bytes = 0 + @debug_output << '<- ' if @debug_output + yield + @debug_output << "\n" if @debug_output + bytes = @written_bytes + @written_bytes = nil + bytes + end + + def write0(str) + @debug_output << str.dump if @debug_output + len = @io.write(str) + @written_bytes += len + len + end + + # + # Logging + # + + private + + def LOG_off + @save_debug_out = @debug_output + @debug_output = nil + end + + def LOG_on + @debug_output = @save_debug_out + end + + def LOG(msg) + return unless @debug_output + @debug_output << msg + "\n" + end + end + + + class InternetMessageIO < BufferedIO #:nodoc: internal use only + def InternetMessageIO.old_open(addr, port, + open_timeout = nil, read_timeout = nil, debug_output = nil) + debug_output << "opening connection to #{addr}...\n" if debug_output + s = timeout(open_timeout) { TCPsocket.new(addr, port) } + io = new(s) + io.read_timeout = read_timeout + io.debug_output = debug_output + io + end + + def initialize(io) + super + @wbuf = nil + end + + # + # Read + # + + def each_message_chunk + LOG 'reading message...' + LOG_off() + read_bytes = 0 + while (line = readuntil("\r\n")) != ".\r\n" + read_bytes += line.size + yield line.sub(/\A\./, '') + end + LOG_on() + LOG "read message (#{read_bytes} bytes)" + end + + # *library private* (cannot handle 'break') + def each_list_item + while (str = readuntil("\r\n")) != ".\r\n" + yield str.chop + end + end + + def write_message_0(src) + prev = @written_bytes + each_crlf_line(src) do |line| + write0 line.sub(/\A\./, '..') + end + @written_bytes - prev + end + + # + # Write + # + + def write_message(src) + LOG "writing message from #{src.class}" + LOG_off() + len = writing { + using_each_crlf_line { + write_message_0 src + } + } + LOG_on() + LOG "wrote #{len} bytes" + len + end + + def write_message_by_block(&block) + LOG 'writing message from block' + LOG_off() + len = writing { + using_each_crlf_line { + begin + block.call(WriteAdapter.new(self, :write_message_0)) + rescue LocalJumpError + # allow `break' from writer block + end + } + } + LOG_on() + LOG "wrote #{len} bytes" + len + end + + private + + def using_each_crlf_line + @wbuf = '' + yield + if not @wbuf.empty? # unterminated last line + write0 @wbuf.chomp + "\r\n" + elsif @written_bytes == 0 # empty src + write0 "\r\n" + end + write0 ".\r\n" + @wbuf = nil + end + + def each_crlf_line(src) + buffer_filling(@wbuf, src) do + while line = @wbuf.slice!(/\A.*(?:\n|\r\n|\r(?!\z))/n) + yield line.chomp("\n") + "\r\n" + end + end + end + + def buffer_filling(buf, src) + case src + when String # for speeding up. + 0.step(src.size - 1, 1024) do |i| + buf << src[i, 1024] + yield + end + when File # for speeding up. + while s = src.read(1024) + buf << s + yield + end + else # generic reader + src.each do |s| + buf << s + yield if buf.size > 1024 + end + yield unless buf.empty? + end + end + end + + + # + # The writer adapter class + # + class WriteAdapter + def initialize(socket, method) + @socket = socket + @method_id = method + end + + def inspect + "#<#{self.class} socket=#{@socket.inspect}>" + end + + def write(str) + @socket.__send__(@method_id, str) + end + + alias print write + + def <<(str) + write str + self + end + + def puts(str = '') + write str.chomp("\n") + "\n" + end + + def printf(*args) + write sprintf(*args) + end + end + + + class ReadAdapter #:nodoc: internal use only + def initialize(block) + @block = block + end + + def inspect + "#<#{self.class}>" + end + + def <<(str) + call_block(str, &@block) if @block + end + + private + + # This method is needed because @block must be called by yield, + # not Proc#call. You can see difference when using `break' in + # the block. + def call_block(str) + yield str + end + end + + + module NetPrivate #:nodoc: obsolete + Socket = ::Net::InternetMessageIO + end + +end # module Net diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/smtp.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/smtp.rb new file mode 100644 index 0000000000..4cca367360 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/smtp.rb @@ -0,0 +1,697 @@ +# = net/smtp.rb +# +# Copyright (c) 1999-2003 Yukihiro Matsumoto. +# +# Copyright (c) 1999-2003 Minero Aoki. +# +# Written & maintained by Minero Aoki . +# +# Documented by William Webber and Minero Aoki. +# +# This program is free software. You can re-distribute and/or +# modify this program under the same terms as Ruby itself, +# Ruby Distribute License or GNU General Public License. +# +# NOTE: You can find Japanese version of this document in +# the doc/net directory of the standard ruby interpreter package. +# +# $Id: smtp.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# +# See Net::SMTP for documentation. +# + +require 'net/protocol' +require 'digest/md5' + +module Net + + # Module mixed in to all SMTP error classes + module SMTPError + # This *class* is module for some reason. + # In ruby 1.9.x, this module becomes a class. + end + + # Represents an SMTP authentication error. + class SMTPAuthenticationError < ProtoAuthError + include SMTPError + end + + # Represents SMTP error code 420 or 450, a temporary error. + class SMTPServerBusy < ProtoServerError + include SMTPError + end + + # Represents an SMTP command syntax error (error code 500) + class SMTPSyntaxError < ProtoSyntaxError + include SMTPError + end + + # Represents a fatal SMTP error (error code 5xx, except for 500) + class SMTPFatalError < ProtoFatalError + include SMTPError + end + + # Unexpected reply code returned from server. + class SMTPUnknownError < ProtoUnknownError + include SMTPError + end + + # + # = Net::SMTP + # + # == What is This Library? + # + # This library provides functionality to send internet + # mail via SMTP, the Simple Mail Transfer Protocol. For details of + # SMTP itself, see [RFC2821] (http://www.ietf.org/rfc/rfc2821.txt). + # + # == What is This Library NOT? + # + # This library does NOT provide functions to compose internet mails. + # You must create them by yourself. If you want better mail support, + # try RubyMail or TMail. You can get both libraries from RAA. + # (http://www.ruby-lang.org/en/raa.html) + # + # FYI: the official documentation on internet mail is: [RFC2822] (http://www.ietf.org/rfc/rfc2822.txt). + # + # == Examples + # + # === Sending Messages + # + # You must open a connection to an SMTP server before sending messages. + # The first argument is the address of your SMTP server, and the second + # argument is the port number. Using SMTP.start with a block is the simplest + # way to do this. This way, the SMTP connection is closed automatically + # after the block is executed. + # + # require 'net/smtp' + # Net::SMTP.start('your.smtp.server', 25) do |smtp| + # # Use the SMTP object smtp only in this block. + # end + # + # Replace 'your.smtp.server' with your SMTP server. Normally + # your system manager or internet provider supplies a server + # for you. + # + # Then you can send messages. + # + # msgstr = < + # To: Destination Address + # Subject: test message + # Date: Sat, 23 Jun 2001 16:26:43 +0900 + # Message-Id: + # + # This is a test message. + # END_OF_MESSAGE + # + # require 'net/smtp' + # Net::SMTP.start('your.smtp.server', 25) do |smtp| + # smtp.send_message msgstr, + # 'your@mail.address', + # 'his_addess@example.com' + # end + # + # === Closing the Session + # + # You MUST close the SMTP session after sending messages, by calling + # the #finish method: + # + # # using SMTP#finish + # smtp = Net::SMTP.start('your.smtp.server', 25) + # smtp.send_message msgstr, 'from@address', 'to@address' + # smtp.finish + # + # You can also use the block form of SMTP.start/SMTP#start. This closes + # the SMTP session automatically: + # + # # using block form of SMTP.start + # Net::SMTP.start('your.smtp.server', 25) do |smtp| + # smtp.send_message msgstr, 'from@address', 'to@address' + # end + # + # I strongly recommend this scheme. This form is simpler and more robust. + # + # === HELO domain + # + # In almost all situations, you must provide a third argument + # to SMTP.start/SMTP#start. This is the domain name which you are on + # (the host to send mail from). It is called the "HELO domain". + # The SMTP server will judge whether it should send or reject + # the SMTP session by inspecting the HELO domain. + # + # Net::SMTP.start('your.smtp.server', 25, + # 'mail.from.domain') { |smtp| ... } + # + # === SMTP Authentication + # + # The Net::SMTP class supports three authentication schemes; + # PLAIN, LOGIN and CRAM MD5. (SMTP Authentication: [RFC2554]) + # To use SMTP authentication, pass extra arguments to + # SMTP.start/SMTP#start. + # + # # PLAIN + # Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain', + # 'Your Account', 'Your Password', :plain) + # # LOGIN + # Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain', + # 'Your Account', 'Your Password', :login) + # + # # CRAM MD5 + # Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain', + # 'Your Account', 'Your Password', :cram_md5) + # + class SMTP + + Revision = %q$Revision: 11708 $.split[1] + + # The default SMTP port, port 25. + def SMTP.default_port + 25 + end + + # + # Creates a new Net::SMTP object. + # + # +address+ is the hostname or ip address of your SMTP + # server. +port+ is the port to connect to; it defaults to + # port 25. + # + # This method does not open the TCP connection. You can use + # SMTP.start instead of SMTP.new if you want to do everything + # at once. Otherwise, follow SMTP.new with SMTP#start. + # + def initialize( address, port = nil ) + @address = address + @port = (port || SMTP.default_port) + @esmtp = true + @socket = nil + @started = false + @open_timeout = 30 + @read_timeout = 60 + @error_occured = false + @debug_output = nil + end + + # Provide human-readable stringification of class state. + def inspect + "#<#{self.class} #{@address}:#{@port} started=#{@started}>" + end + + # +true+ if the SMTP object uses ESMTP (which it does by default). + def esmtp? + @esmtp + end + + # + # Set whether to use ESMTP or not. This should be done before + # calling #start. Note that if #start is called in ESMTP mode, + # and the connection fails due to a ProtocolError, the SMTP + # object will automatically switch to plain SMTP mode and + # retry (but not vice versa). + # + def esmtp=( bool ) + @esmtp = bool + end + + alias esmtp esmtp? + + # The address of the SMTP server to connect to. + attr_reader :address + + # The port number of the SMTP server to connect to. + attr_reader :port + + # Seconds to wait while attempting to open a connection. + # If the connection cannot be opened within this time, a + # TimeoutError is raised. + attr_accessor :open_timeout + + # Seconds to wait while reading one block (by one read(2) call). + # If the read(2) call does not complete within this time, a + # TimeoutError is raised. + attr_reader :read_timeout + + # Set the number of seconds to wait until timing-out a read(2) + # call. + def read_timeout=( sec ) + @socket.read_timeout = sec if @socket + @read_timeout = sec + end + + # + # WARNING: This method causes serious security holes. + # Use this method for only debugging. + # + # Set an output stream for debug logging. + # You must call this before #start. + # + # # example + # smtp = Net::SMTP.new(addr, port) + # smtp.set_debug_output $stderr + # smtp.start do |smtp| + # .... + # end + # + def set_debug_output( arg ) + @debug_output = arg + end + + # + # SMTP session control + # + + # + # Creates a new Net::SMTP object and connects to the server. + # + # This method is equivalent to: + # + # Net::SMTP.new(address, port).start(helo_domain, account, password, authtype) + # + # === Example + # + # Net::SMTP.start('your.smtp.server') do |smtp| + # smtp.send_message msgstr, 'from@example.com', ['dest@example.com'] + # end + # + # === Block Usage + # + # If called with a block, the newly-opened Net::SMTP object is yielded + # to the block, and automatically closed when the block finishes. If called + # without a block, the newly-opened Net::SMTP object is returned to + # the caller, and it is the caller's responsibility to close it when + # finished. + # + # === Parameters + # + # +address+ is the hostname or ip address of your smtp server. + # + # +port+ is the port to connect to; it defaults to port 25. + # + # +helo+ is the _HELO_ _domain_ provided by the client to the + # server (see overview comments); it defaults to 'localhost.localdomain'. + # + # The remaining arguments are used for SMTP authentication, if required + # or desired. +user+ is the account name; +secret+ is your password + # or other authentication token; and +authtype+ is the authentication + # type, one of :plain, :login, or :cram_md5. See the discussion of + # SMTP Authentication in the overview notes. + # + # === Errors + # + # This method may raise: + # + # * Net::SMTPAuthenticationError + # * Net::SMTPServerBusy + # * Net::SMTPSyntaxError + # * Net::SMTPFatalError + # * Net::SMTPUnknownError + # * IOError + # * TimeoutError + # + def SMTP.start( address, port = nil, + helo = 'localhost.localdomain', + user = nil, secret = nil, authtype = nil, + &block) # :yield: smtp + new(address, port).start(helo, user, secret, authtype, &block) + end + + # +true+ if the SMTP session has been started. + def started? + @started + end + + # + # Opens a TCP connection and starts the SMTP session. + # + # === Parameters + # + # +helo+ is the _HELO_ _domain_ that you'll dispatch mails from; see + # the discussion in the overview notes. + # + # If both of +user+ and +secret+ are given, SMTP authentication + # will be attempted using the AUTH command. +authtype+ specifies + # the type of authentication to attempt; it must be one of + # :login, :plain, and :cram_md5. See the notes on SMTP Authentication + # in the overview. + # + # === Block Usage + # + # When this methods is called with a block, the newly-started SMTP + # object is yielded to the block, and automatically closed after + # the block call finishes. Otherwise, it is the caller's + # responsibility to close the session when finished. + # + # === Example + # + # This is very similar to the class method SMTP.start. + # + # require 'net/smtp' + # smtp = Net::SMTP.new('smtp.mail.server', 25) + # smtp.start(helo_domain, account, password, authtype) do |smtp| + # smtp.send_message msgstr, 'from@example.com', ['dest@example.com'] + # end + # + # The primary use of this method (as opposed to SMTP.start) + # is probably to set debugging (#set_debug_output) or ESMTP + # (#esmtp=), which must be done before the session is + # started. + # + # === Errors + # + # If session has already been started, an IOError will be raised. + # + # This method may raise: + # + # * Net::SMTPAuthenticationError + # * Net::SMTPServerBusy + # * Net::SMTPSyntaxError + # * Net::SMTPFatalError + # * Net::SMTPUnknownError + # * IOError + # * TimeoutError + # + def start( helo = 'localhost.localdomain', + user = nil, secret = nil, authtype = nil ) # :yield: smtp + if block_given? + begin + do_start(helo, user, secret, authtype) + return yield(self) + ensure + do_finish + end + else + do_start(helo, user, secret, authtype) + return self + end + end + + def do_start( helodomain, user, secret, authtype ) + raise IOError, 'SMTP session already started' if @started + check_auth_args user, secret, authtype if user or secret + + @socket = InternetMessageIO.old_open(@address, @port, + @open_timeout, @read_timeout, + @debug_output) + check_response(critical { recv_response() }) + begin + if @esmtp + ehlo helodomain + else + helo helodomain + end + rescue ProtocolError + if @esmtp + @esmtp = false + @error_occured = false + retry + end + raise + end + authenticate user, secret, authtype if user + @started = true + ensure + @socket.close if not @started and @socket and not @socket.closed? + end + private :do_start + + # Finishes the SMTP session and closes TCP connection. + # Raises IOError if not started. + def finish + raise IOError, 'not yet started' unless started? + do_finish + end + + def do_finish + quit if @socket and not @socket.closed? and not @error_occured + ensure + @started = false + @error_occured = false + @socket.close if @socket and not @socket.closed? + @socket = nil + end + private :do_finish + + # + # message send + # + + public + + # + # Sends +msgstr+ as a message. Single CR ("\r") and LF ("\n") found + # in the +msgstr+, are converted into the CR LF pair. You cannot send a + # binary message with this method. +msgstr+ should include both + # the message headers and body. + # + # +from_addr+ is a String representing the source mail address. + # + # +to_addr+ is a String or Strings or Array of Strings, representing + # the destination mail address or addresses. + # + # === Example + # + # Net::SMTP.start('smtp.example.com') do |smtp| + # smtp.send_message msgstr, + # 'from@example.com', + # ['dest@example.com', 'dest2@example.com'] + # end + # + # === Errors + # + # This method may raise: + # + # * Net::SMTPServerBusy + # * Net::SMTPSyntaxError + # * Net::SMTPFatalError + # * Net::SMTPUnknownError + # * IOError + # * TimeoutError + # + def send_message( msgstr, from_addr, *to_addrs ) + send0(from_addr, to_addrs.flatten) { + @socket.write_message msgstr + } + end + + alias send_mail send_message + alias sendmail send_message # obsolete + + # + # Opens a message writer stream and gives it to the block. + # The stream is valid only in the block, and has these methods: + # + # puts(str = ''):: outputs STR and CR LF. + # print(str):: outputs STR. + # printf(fmt, *args):: outputs sprintf(fmt,*args). + # write(str):: outputs STR and returns the length of written bytes. + # <<(str):: outputs STR and returns self. + # + # If a single CR ("\r") or LF ("\n") is found in the message, + # it is converted to the CR LF pair. You cannot send a binary + # message with this method. + # + # === Parameters + # + # +from_addr+ is a String representing the source mail address. + # + # +to_addr+ is a String or Strings or Array of Strings, representing + # the destination mail address or addresses. + # + # === Example + # + # Net::SMTP.start('smtp.example.com', 25) do |smtp| + # smtp.open_message_stream('from@example.com', ['dest@example.com']) do |f| + # f.puts 'From: from@example.com' + # f.puts 'To: dest@example.com' + # f.puts 'Subject: test message' + # f.puts + # f.puts 'This is a test message.' + # end + # end + # + # === Errors + # + # This method may raise: + # + # * Net::SMTPServerBusy + # * Net::SMTPSyntaxError + # * Net::SMTPFatalError + # * Net::SMTPUnknownError + # * IOError + # * TimeoutError + # + def open_message_stream( from_addr, *to_addrs, &block ) # :yield: stream + send0(from_addr, to_addrs.flatten) { + @socket.write_message_by_block(&block) + } + end + + alias ready open_message_stream # obsolete + + private + + def send0( from_addr, to_addrs ) + raise IOError, 'closed session' unless @socket + raise ArgumentError, 'mail destination not given' if to_addrs.empty? + if $SAFE > 0 + raise SecurityError, 'tainted from_addr' if from_addr.tainted? + to_addrs.each do |to| + raise SecurityError, 'tainted to_addr' if to.tainted? + end + end + + mailfrom from_addr + to_addrs.each do |to| + rcptto to + end + res = critical { + check_response(get_response('DATA'), true) + yield + recv_response() + } + check_response(res) + end + + # + # auth + # + + private + + def check_auth_args( user, secret, authtype ) + raise ArgumentError, 'both user and secret are required'\ + unless user and secret + auth_method = "auth_#{authtype || 'cram_md5'}" + raise ArgumentError, "wrong auth type #{authtype}"\ + unless respond_to?(auth_method, true) + end + + def authenticate( user, secret, authtype ) + __send__("auth_#{authtype || 'cram_md5'}", user, secret) + end + + def auth_plain( user, secret ) + res = critical { get_response('AUTH PLAIN %s', + base64_encode("\0#{user}\0#{secret}")) } + raise SMTPAuthenticationError, res unless /\A2../ === res + end + + def auth_login( user, secret ) + res = critical { + check_response(get_response('AUTH LOGIN'), true) + check_response(get_response(base64_encode(user)), true) + get_response(base64_encode(secret)) + } + raise SMTPAuthenticationError, res unless /\A2../ === res + end + + def auth_cram_md5( user, secret ) + # CRAM-MD5: [RFC2195] + res = nil + critical { + res = check_response(get_response('AUTH CRAM-MD5'), true) + challenge = res.split(/ /)[1].unpack('m')[0] + secret = Digest::MD5.digest(secret) if secret.size > 64 + + isecret = secret + "\0" * (64 - secret.size) + osecret = isecret.dup + 0.upto(63) do |i| + isecret[i] ^= 0x36 + osecret[i] ^= 0x5c + end + tmp = Digest::MD5.digest(isecret + challenge) + tmp = Digest::MD5.hexdigest(osecret + tmp) + + res = get_response(base64_encode(user + ' ' + tmp)) + } + raise SMTPAuthenticationError, res unless /\A2../ === res + end + + def base64_encode( str ) + # expects "str" may not become too long + [str].pack('m').gsub(/\s+/, '') + end + + # + # SMTP command dispatcher + # + + private + + def helo( domain ) + getok('HELO %s', domain) + end + + def ehlo( domain ) + getok('EHLO %s', domain) + end + + def mailfrom( fromaddr ) + getok('MAIL FROM:<%s>', fromaddr) + end + + def rcptto( to ) + getok('RCPT TO:<%s>', to) + end + + def quit + getok('QUIT') + end + + # + # row level library + # + + private + + def getok( fmt, *args ) + res = critical { + @socket.writeline sprintf(fmt, *args) + recv_response() + } + return check_response(res) + end + + def get_response( fmt, *args ) + @socket.writeline sprintf(fmt, *args) + recv_response() + end + + def recv_response + res = '' + while true + line = @socket.readline + res << line << "\n" + break unless line[3] == ?- # "210-PIPELINING" + end + res + end + + def check_response( res, allow_continue = false ) + return res if /\A2/ === res + return res if allow_continue and /\A3/ === res + err = case res + when /\A4/ then SMTPServerBusy + when /\A50/ then SMTPSyntaxError + when /\A55/ then SMTPFatalError + else SMTPUnknownError + end + raise err, res + end + + def critical( &block ) + return '200 dummy reply code' if @error_occured + begin + return yield() + rescue Exception + @error_occured = true + raise + end + end + + end # class SMTP + + SMTPSession = SMTP + +end # module Net diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/telnet.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/telnet.rb new file mode 100644 index 0000000000..5037a62c2a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/telnet.rb @@ -0,0 +1,742 @@ +# = net/telnet.rb - Simple Telnet Client Library +# +# Author:: Wakou Aoyama +# Documentation:: William Webber and Wakou Aoyama +# +# This file holds the class Net::Telnet, which provides client-side +# telnet functionality. +# +# For documentation, see Net::Telnet. +# + +require "socket" +require "delegate" +require "timeout" +require "English" + +module Net + + # + # == Net::Telnet + # + # Provides telnet client functionality. + # + # This class also has, through delegation, all the methods of a + # socket object (by default, a +TCPSocket+, but can be set by the + # +Proxy+ option to new()). This provides methods such as + # close() to end the session and sysread() to read + # data directly from the host, instead of via the waitfor() + # mechanism. Note that if you do use sysread() directly + # when in telnet mode, you should probably pass the output through + # preprocess() to extract telnet command sequences. + # + # == Overview + # + # The telnet protocol allows a client to login remotely to a user + # account on a server and execute commands via a shell. The equivalent + # is done by creating a Net::Telnet class with the +Host+ option + # set to your host, calling #login() with your user and password, + # issuing one or more #cmd() calls, and then calling #close() + # to end the session. The #waitfor(), #print(), #puts(), and + # #write() methods, which #cmd() is implemented on top of, are + # only needed if you are doing something more complicated. + # + # A Net::Telnet object can also be used to connect to non-telnet + # services, such as SMTP or HTTP. In this case, you normally + # want to provide the +Port+ option to specify the port to + # connect to, and set the +Telnetmode+ option to false to prevent + # the client from attempting to interpret telnet command sequences. + # Generally, #login() will not work with other protocols, and you + # have to handle authentication yourself. + # + # For some protocols, it will be possible to specify the +Prompt+ + # option once when you create the Telnet object and use #cmd() calls; + # for others, you will have to specify the response sequence to + # look for as the Match option to every #cmd() call, or call + # #puts() and #waitfor() directly; for yet others, you will have + # to use #sysread() instead of #waitfor() and parse server + # responses yourself. + # + # It is worth noting that when you create a new Net::Telnet object, + # you can supply a proxy IO channel via the Proxy option. This + # can be used to attach the Telnet object to other Telnet objects, + # to already open sockets, or to any read-write IO object. This + # can be useful, for instance, for setting up a test fixture for + # unit testing. + # + # == Examples + # + # === Log in and send a command, echoing all output to stdout + # + # localhost = Net::Telnet::new("Host" => "localhost", + # "Timeout" => 10, + # "Prompt" => /[$%#>] \z/n) + # localhost.login("username", "password") { |c| print c } + # localhost.cmd("command") { |c| print c } + # localhost.close + # + # + # === Check a POP server to see if you have mail + # + # pop = Net::Telnet::new("Host" => "your_destination_host_here", + # "Port" => 110, + # "Telnetmode" => false, + # "Prompt" => /^\+OK/n) + # pop.cmd("user " + "your_username_here") { |c| print c } + # pop.cmd("pass " + "your_password_here") { |c| print c } + # pop.cmd("list") { |c| print c } + # + # == References + # + # There are a large number of RFCs relevant to the Telnet protocol. + # RFCs 854-861 define the base protocol. For a complete listing + # of relevant RFCs, see + # http://www.omnifarious.org/~hopper/technical/telnet-rfc.html + # + class Telnet < SimpleDelegator + + # :stopdoc: + IAC = 255.chr # "\377" # "\xff" # interpret as command + DONT = 254.chr # "\376" # "\xfe" # you are not to use option + DO = 253.chr # "\375" # "\xfd" # please, you use option + WONT = 252.chr # "\374" # "\xfc" # I won't use option + WILL = 251.chr # "\373" # "\xfb" # I will use option + SB = 250.chr # "\372" # "\xfa" # interpret as subnegotiation + GA = 249.chr # "\371" # "\xf9" # you may reverse the line + EL = 248.chr # "\370" # "\xf8" # erase the current line + EC = 247.chr # "\367" # "\xf7" # erase the current character + AYT = 246.chr # "\366" # "\xf6" # are you there + AO = 245.chr # "\365" # "\xf5" # abort output--but let prog finish + IP = 244.chr # "\364" # "\xf4" # interrupt process--permanently + BREAK = 243.chr # "\363" # "\xf3" # break + DM = 242.chr # "\362" # "\xf2" # data mark--for connect. cleaning + NOP = 241.chr # "\361" # "\xf1" # nop + SE = 240.chr # "\360" # "\xf0" # end sub negotiation + EOR = 239.chr # "\357" # "\xef" # end of record (transparent mode) + ABORT = 238.chr # "\356" # "\xee" # Abort process + SUSP = 237.chr # "\355" # "\xed" # Suspend process + EOF = 236.chr # "\354" # "\xec" # End of file + SYNCH = 242.chr # "\362" # "\xf2" # for telfunc calls + + OPT_BINARY = 0.chr # "\000" # "\x00" # Binary Transmission + OPT_ECHO = 1.chr # "\001" # "\x01" # Echo + OPT_RCP = 2.chr # "\002" # "\x02" # Reconnection + OPT_SGA = 3.chr # "\003" # "\x03" # Suppress Go Ahead + OPT_NAMS = 4.chr # "\004" # "\x04" # Approx Message Size Negotiation + OPT_STATUS = 5.chr # "\005" # "\x05" # Status + OPT_TM = 6.chr # "\006" # "\x06" # Timing Mark + OPT_RCTE = 7.chr # "\a" # "\x07" # Remote Controlled Trans and Echo + OPT_NAOL = 8.chr # "\010" # "\x08" # Output Line Width + OPT_NAOP = 9.chr # "\t" # "\x09" # Output Page Size + OPT_NAOCRD = 10.chr # "\n" # "\x0a" # Output Carriage-Return Disposition + OPT_NAOHTS = 11.chr # "\v" # "\x0b" # Output Horizontal Tab Stops + OPT_NAOHTD = 12.chr # "\f" # "\x0c" # Output Horizontal Tab Disposition + OPT_NAOFFD = 13.chr # "\r" # "\x0d" # Output Formfeed Disposition + OPT_NAOVTS = 14.chr # "\016" # "\x0e" # Output Vertical Tabstops + OPT_NAOVTD = 15.chr # "\017" # "\x0f" # Output Vertical Tab Disposition + OPT_NAOLFD = 16.chr # "\020" # "\x10" # Output Linefeed Disposition + OPT_XASCII = 17.chr # "\021" # "\x11" # Extended ASCII + OPT_LOGOUT = 18.chr # "\022" # "\x12" # Logout + OPT_BM = 19.chr # "\023" # "\x13" # Byte Macro + OPT_DET = 20.chr # "\024" # "\x14" # Data Entry Terminal + OPT_SUPDUP = 21.chr # "\025" # "\x15" # SUPDUP + OPT_SUPDUPOUTPUT = 22.chr # "\026" # "\x16" # SUPDUP Output + OPT_SNDLOC = 23.chr # "\027" # "\x17" # Send Location + OPT_TTYPE = 24.chr # "\030" # "\x18" # Terminal Type + OPT_EOR = 25.chr # "\031" # "\x19" # End of Record + OPT_TUID = 26.chr # "\032" # "\x1a" # TACACS User Identification + OPT_OUTMRK = 27.chr # "\e" # "\x1b" # Output Marking + OPT_TTYLOC = 28.chr # "\034" # "\x1c" # Terminal Location Number + OPT_3270REGIME = 29.chr # "\035" # "\x1d" # Telnet 3270 Regime + OPT_X3PAD = 30.chr # "\036" # "\x1e" # X.3 PAD + OPT_NAWS = 31.chr # "\037" # "\x1f" # Negotiate About Window Size + OPT_TSPEED = 32.chr # " " # "\x20" # Terminal Speed + OPT_LFLOW = 33.chr # "!" # "\x21" # Remote Flow Control + OPT_LINEMODE = 34.chr # "\"" # "\x22" # Linemode + OPT_XDISPLOC = 35.chr # "#" # "\x23" # X Display Location + OPT_OLD_ENVIRON = 36.chr # "$" # "\x24" # Environment Option + OPT_AUTHENTICATION = 37.chr # "%" # "\x25" # Authentication Option + OPT_ENCRYPT = 38.chr # "&" # "\x26" # Encryption Option + OPT_NEW_ENVIRON = 39.chr # "'" # "\x27" # New Environment Option + OPT_EXOPL = 255.chr # "\377" # "\xff" # Extended-Options-List + + NULL = "\000" + CR = "\015" + LF = "\012" + EOL = CR + LF + REVISION = '$Id: telnet.rb 11708 2007-02-12 23:01:19Z shyouhei $' + # :startdoc: + + # + # Creates a new Net::Telnet object. + # + # Attempts to connect to the host (unless the Proxy option is + # provided: see below). If a block is provided, it is yielded + # status messages on the attempt to connect to the server, of + # the form: + # + # Trying localhost... + # Connected to localhost. + # + # +options+ is a hash of options. The following example lists + # all options and their default values. + # + # host = Net::Telnet::new( + # "Host" => "localhost", # default: "localhost" + # "Port" => 23, # default: 23 + # "Binmode" => false, # default: false + # "Output_log" => "output_log", # default: nil (no output) + # "Dump_log" => "dump_log", # default: nil (no output) + # "Prompt" => /[$%#>] \z/n, # default: /[$%#>] \z/n + # "Telnetmode" => true, # default: true + # "Timeout" => 10, # default: 10 + # # if ignore timeout then set "Timeout" to false. + # "Waittime" => 0, # default: 0 + # "Proxy" => proxy # default: nil + # # proxy is Net::Telnet or IO object + # ) + # + # The options have the following meanings: + # + # Host:: the hostname or IP address of the host to connect to, as a String. + # Defaults to "localhost". + # + # Port:: the port to connect to. Defaults to 23. + # + # Binmode:: if false (the default), newline substitution is performed. + # Outgoing LF is + # converted to CRLF, and incoming CRLF is converted to LF. If + # true, this substitution is not performed. This value can + # also be set with the #binmode() method. The + # outgoing conversion only applies to the #puts() and #print() + # methods, not the #write() method. The precise nature of + # the newline conversion is also affected by the telnet options + # SGA and BIN. + # + # Output_log:: the name of the file to write connection status messages + # and all received traffic to. In the case of a proper + # Telnet session, this will include the client input as + # echoed by the host; otherwise, it only includes server + # responses. Output is appended verbatim to this file. + # By default, no output log is kept. + # + # Dump_log:: as for Output_log, except that output is written in hexdump + # format (16 bytes per line as hex pairs, followed by their + # printable equivalent), with connection status messages + # preceded by '#', sent traffic preceded by '>', and + # received traffic preceded by '<'. By default, not dump log + # is kept. + # + # Prompt:: a regular expression matching the host's command-line prompt + # sequence. This is needed by the Telnet class to determine + # when the output from a command has finished and the host is + # ready to receive a new command. By default, this regular + # expression is /[$%#>] \z/n. + # + # Telnetmode:: a boolean value, true by default. In telnet mode, + # traffic received from the host is parsed for special + # command sequences, and these sequences are escaped + # in outgoing traffic sent using #puts() or #print() + # (but not #write()). If you are using the Net::Telnet + # object to connect to a non-telnet service (such as + # SMTP or POP), this should be set to "false" to prevent + # undesired data corruption. This value can also be set + # by the #telnetmode() method. + # + # Timeout:: the number of seconds to wait before timing out both the + # initial attempt to connect to host (in this constructor), + # and all attempts to read data from the host (in #waitfor(), + # #cmd(), and #login()). Exceeding this timeout causes a + # TimeoutError to be raised. The default value is 10 seconds. + # You can disable the timeout by setting this value to false. + # In this case, the connect attempt will eventually timeout + # on the underlying connect(2) socket call with an + # Errno::ETIMEDOUT error (but generally only after a few + # minutes), but other attempts to read data from the host + # will hand indefinitely if no data is forthcoming. + # + # Waittime:: the amount of time to wait after seeing what looks like a + # prompt (that is, received data that matches the Prompt + # option regular expression) to see if more data arrives. + # If more data does arrive in this time, Net::Telnet assumes + # that what it saw was not really a prompt. This is to try to + # avoid false matches, but it can also lead to missing real + # prompts (if, for instance, a background process writes to + # the terminal soon after the prompt is displayed). By + # default, set to 0, meaning not to wait for more data. + # + # Proxy:: a proxy object to used instead of opening a direct connection + # to the host. Must be either another Net::Telnet object or + # an IO object. If it is another Net::Telnet object, this + # instance will use that one's socket for communication. If an + # IO object, it is used directly for communication. Any other + # kind of object will cause an error to be raised. + # + def initialize(options) # :yield: mesg + @options = options + @options["Host"] = "localhost" unless @options.has_key?("Host") + @options["Port"] = 23 unless @options.has_key?("Port") + @options["Prompt"] = /[$%#>] \z/n unless @options.has_key?("Prompt") + @options["Timeout"] = 10 unless @options.has_key?("Timeout") + @options["Waittime"] = 0 unless @options.has_key?("Waittime") + unless @options.has_key?("Binmode") + @options["Binmode"] = false + else + unless (true == @options["Binmode"] or false == @options["Binmode"]) + raise ArgumentError, "Binmode option must be true or false" + end + end + + unless @options.has_key?("Telnetmode") + @options["Telnetmode"] = true + else + unless (true == @options["Telnetmode"] or false == @options["Telnetmode"]) + raise ArgumentError, "Telnetmode option must be true or false" + end + end + + @telnet_option = { "SGA" => false, "BINARY" => false } + + if @options.has_key?("Output_log") + @log = File.open(@options["Output_log"], 'a+') + @log.sync = true + @log.binmode + end + + if @options.has_key?("Dump_log") + @dumplog = File.open(@options["Dump_log"], 'a+') + @dumplog.sync = true + @dumplog.binmode + def @dumplog.log_dump(dir, x) # :nodoc: + len = x.length + addr = 0 + offset = 0 + while 0 < len + if len < 16 + line = x[offset, len] + else + line = x[offset, 16] + end + hexvals = line.unpack('H*')[0] + hexvals += ' ' * (32 - hexvals.length) + hexvals = format("%s %s %s %s " * 4, *hexvals.unpack('a2' * 16)) + line = line.gsub(/[\000-\037\177-\377]/n, '.') + printf "%s 0x%5.5x: %s%s\n", dir, addr, hexvals, line + addr += 16 + offset += 16 + len -= 16 + end + print "\n" + end + end + + if @options.has_key?("Proxy") + if @options["Proxy"].kind_of?(Net::Telnet) + @sock = @options["Proxy"].sock + elsif @options["Proxy"].kind_of?(IO) + @sock = @options["Proxy"] + else + raise "Error: Proxy must be an instance of Net::Telnet or IO." + end + else + message = "Trying " + @options["Host"] + "...\n" + yield(message) if block_given? + @log.write(message) if @options.has_key?("Output_log") + @dumplog.log_dump('#', message) if @options.has_key?("Dump_log") + + begin + if @options["Timeout"] == false + @sock = TCPSocket.open(@options["Host"], @options["Port"]) + else + timeout(@options["Timeout"]) do + @sock = TCPSocket.open(@options["Host"], @options["Port"]) + end + end + rescue TimeoutError + raise TimeoutError, "timed out while opening a connection to the host" + rescue + @log.write($ERROR_INFO.to_s + "\n") if @options.has_key?("Output_log") + @dumplog.log_dump('#', $ERROR_INFO.to_s + "\n") if @options.has_key?("Dump_log") + raise + end + @sock.sync = true + @sock.binmode + + message = "Connected to " + @options["Host"] + ".\n" + yield(message) if block_given? + @log.write(message) if @options.has_key?("Output_log") + @dumplog.log_dump('#', message) if @options.has_key?("Dump_log") + end + + super(@sock) + end # initialize + + # The socket the Telnet object is using. Note that this object becomes + # a delegate of the Telnet object, so normally you invoke its methods + # directly on the Telnet object. + attr :sock + + # Set telnet command interpretation on (+mode+ == true) or off + # (+mode+ == false), or return the current value (+mode+ not + # provided). It should be on for true telnet sessions, off if + # using Net::Telnet to connect to a non-telnet service such + # as SMTP. + def telnetmode(mode = nil) + case mode + when nil + @options["Telnetmode"] + when true, false + @options["Telnetmode"] = mode + else + raise ArgumentError, "argument must be true or false, or missing" + end + end + + # Turn telnet command interpretation on (true) or off (false). It + # should be on for true telnet sessions, off if using Net::Telnet + # to connect to a non-telnet service such as SMTP. + def telnetmode=(mode) + if (true == mode or false == mode) + @options["Telnetmode"] = mode + else + raise ArgumentError, "argument must be true or false" + end + end + + # Turn newline conversion on (+mode+ == false) or off (+mode+ == true), + # or return the current value (+mode+ is not specified). + def binmode(mode = nil) + case mode + when nil + @options["Binmode"] + when true, false + @options["Binmode"] = mode + else + raise ArgumentError, "argument must be true or false" + end + end + + # Turn newline conversion on (false) or off (true). + def binmode=(mode) + if (true == mode or false == mode) + @options["Binmode"] = mode + else + raise ArgumentError, "argument must be true or false" + end + end + + # Preprocess received data from the host. + # + # Performs newline conversion and detects telnet command sequences. + # Called automatically by #waitfor(). You should only use this + # method yourself if you have read input directly using sysread() + # or similar, and even then only if in telnet mode. + def preprocess(string) + # combine CR+NULL into CR + string = string.gsub(/#{CR}#{NULL}/no, CR) if @options["Telnetmode"] + + # combine EOL into "\n" + string = string.gsub(/#{EOL}/no, "\n") unless @options["Binmode"] + + string.gsub(/#{IAC}( + [#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]| + [#{DO}#{DONT}#{WILL}#{WONT}] + [#{OPT_BINARY}-#{OPT_NEW_ENVIRON}#{OPT_EXOPL}]| + #{SB}[^#{IAC}]*#{IAC}#{SE} + )/xno) do + if IAC == $1 # handle escaped IAC characters + IAC + elsif AYT == $1 # respond to "IAC AYT" (are you there) + self.write("nobody here but us pigeons" + EOL) + '' + elsif DO[0] == $1[0] # respond to "IAC DO x" + if OPT_BINARY[0] == $1[1] + @telnet_option["BINARY"] = true + self.write(IAC + WILL + OPT_BINARY) + else + self.write(IAC + WONT + $1[1..1]) + end + '' + elsif DONT[0] == $1[0] # respond to "IAC DON'T x" with "IAC WON'T x" + self.write(IAC + WONT + $1[1..1]) + '' + elsif WILL[0] == $1[0] # respond to "IAC WILL x" + if OPT_BINARY[0] == $1[1] + self.write(IAC + DO + OPT_BINARY) + elsif OPT_ECHO[0] == $1[1] + self.write(IAC + DO + OPT_ECHO) + elsif OPT_SGA[0] == $1[1] + @telnet_option["SGA"] = true + self.write(IAC + DO + OPT_SGA) + else + self.write(IAC + DONT + $1[1..1]) + end + '' + elsif WONT[0] == $1[0] # respond to "IAC WON'T x" + if OPT_ECHO[0] == $1[1] + self.write(IAC + DONT + OPT_ECHO) + elsif OPT_SGA[0] == $1[1] + @telnet_option["SGA"] = false + self.write(IAC + DONT + OPT_SGA) + else + self.write(IAC + DONT + $1[1..1]) + end + '' + else + '' + end + end + end # preprocess + + # Read data from the host until a certain sequence is matched. + # + # If a block is given, the received data will be yielded as it + # is read in (not necessarily all in one go), or nil if EOF + # occurs before any data is received. Whether a block is given + # or not, all data read will be returned in a single string, or again + # nil if EOF occurs before any data is received. Note that + # received data includes the matched sequence we were looking for. + # + # +options+ can be either a regular expression or a hash of options. + # If a regular expression, this specifies the data to wait for. + # If a hash, this can specify the following options: + # + # Match:: a regular expression, specifying the data to wait for. + # Prompt:: as for Match; used only if Match is not specified. + # String:: as for Match, except a string that will be converted + # into a regular expression. Used only if Match and + # Prompt are not specified. + # Timeout:: the number of seconds to wait for data from the host + # before raising a TimeoutError. If set to false, + # no timeout will occur. If not specified, the + # Timeout option value specified when this instance + # was created will be used, or, failing that, the + # default value of 10 seconds. + # Waittime:: the number of seconds to wait after matching against + # the input data to see if more data arrives. If more + # data arrives within this time, we will judge ourselves + # not to have matched successfully, and will continue + # trying to match. If not specified, the Waittime option + # value specified when this instance was created will be + # used, or, failing that, the default value of 0 seconds, + # which means not to wait for more input. + # + def waitfor(options) # :yield: recvdata + time_out = @options["Timeout"] + waittime = @options["Waittime"] + + if options.kind_of?(Hash) + prompt = if options.has_key?("Match") + options["Match"] + elsif options.has_key?("Prompt") + options["Prompt"] + elsif options.has_key?("String") + Regexp.new( Regexp.quote(options["String"]) ) + end + time_out = options["Timeout"] if options.has_key?("Timeout") + waittime = options["Waittime"] if options.has_key?("Waittime") + else + prompt = options + end + + if time_out == false + time_out = nil + end + + line = '' + buf = '' + rest = '' + until(prompt === line and not IO::select([@sock], nil, nil, waittime)) + unless IO::select([@sock], nil, nil, time_out) + raise TimeoutError, "timed out while waiting for more data" + end + begin + c = @sock.readpartial(1024 * 1024) + @dumplog.log_dump('<', c) if @options.has_key?("Dump_log") + if @options["Telnetmode"] + c = rest + c + if Integer(c.rindex(/#{IAC}#{SE}/no)) < + Integer(c.rindex(/#{IAC}#{SB}/no)) + buf = preprocess(c[0 ... c.rindex(/#{IAC}#{SB}/no)]) + rest = c[c.rindex(/#{IAC}#{SB}/no) .. -1] + elsif pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) + buf = preprocess(c[0 ... pt]) + rest = c[pt .. -1] + else + buf = preprocess(c) + rest = '' + end + else + # Not Telnetmode. + # + # We cannot use preprocess() on this data, because that + # method makes some Telnetmode-specific assumptions. + buf = c + buf.gsub!(/#{EOL}/no, "\n") unless @options["Binmode"] + rest = '' + end + @log.print(buf) if @options.has_key?("Output_log") + line += buf + yield buf if block_given? + rescue EOFError # End of file reached + if line == '' + line = nil + yield nil if block_given? + end + break + end + end + line + end + + # Write +string+ to the host. + # + # Does not perform any conversions on +string+. Will log +string+ to the + # dumplog, if the Dump_log option is set. + def write(string) + length = string.length + while 0 < length + IO::select(nil, [@sock]) + @dumplog.log_dump('>', string[-length..-1]) if @options.has_key?("Dump_log") + length -= @sock.syswrite(string[-length..-1]) + end + end + + # Sends a string to the host. + # + # This does _not_ automatically append a newline to the string. Embedded + # newlines may be converted and telnet command sequences escaped + # depending upon the values of telnetmode, binmode, and telnet options + # set by the host. + def print(string) + string = string.gsub(/#{IAC}/no, IAC + IAC) if @options["Telnetmode"] + + if @options["Binmode"] + self.write(string) + else + if @telnet_option["BINARY"] and @telnet_option["SGA"] + # IAC WILL SGA IAC DO BIN send EOL --> CR + self.write(string.gsub(/\n/n, CR)) + elsif @telnet_option["SGA"] + # IAC WILL SGA send EOL --> CR+NULL + self.write(string.gsub(/\n/n, CR + NULL)) + else + # NONE send EOL --> CR+LF + self.write(string.gsub(/\n/n, EOL)) + end + end + end + + # Sends a string to the host. + # + # Same as #print(), but appends a newline to the string. + def puts(string) + self.print(string + "\n") + end + + # Send a command to the host. + # + # More exactly, sends a string to the host, and reads in all received + # data until is sees the prompt or other matched sequence. + # + # If a block is given, the received data will be yielded to it as + # it is read in. Whether a block is given or not, the received data + # will be return as a string. Note that the received data includes + # the prompt and in most cases the host's echo of our command. + # + # +options+ is either a String, specified the string or command to + # send to the host; or it is a hash of options. If a hash, the + # following options can be specified: + # + # String:: the command or other string to send to the host. + # Match:: a regular expression, the sequence to look for in + # the received data before returning. If not specified, + # the Prompt option value specified when this instance + # was created will be used, or, failing that, the default + # prompt of /[$%#>] \z/n. + # Timeout:: the seconds to wait for data from the host before raising + # a Timeout error. If not specified, the Timeout option + # value specified when this instance was created will be + # used, or, failing that, the default value of 10 seconds. + # + # The command or other string will have the newline sequence appended + # to it. + def cmd(options) # :yield: recvdata + match = @options["Prompt"] + time_out = @options["Timeout"] + + if options.kind_of?(Hash) + string = options["String"] + match = options["Match"] if options.has_key?("Match") + time_out = options["Timeout"] if options.has_key?("Timeout") + else + string = options + end + + self.puts(string) + if block_given? + waitfor({"Prompt" => match, "Timeout" => time_out}){|c| yield c } + else + waitfor({"Prompt" => match, "Timeout" => time_out}) + end + end + + # Login to the host with a given username and password. + # + # The username and password can either be provided as two string + # arguments in that order, or as a hash with keys "Name" and + # "Password". + # + # This method looks for the strings "login" and "Password" from the + # host to determine when to send the username and password. If the + # login sequence does not follow this pattern (for instance, you + # are connecting to a service other than telnet), you will need + # to handle login yourself. + # + # The password can be omitted, either by only + # provided one String argument, which will be used as the username, + # or by providing a has that has no "Password" key. In this case, + # the method will not look for the "Password:" prompt; if it is + # sent, it will have to be dealt with by later calls. + # + # The method returns all data received during the login process from + # the host, including the echoed username but not the password (which + # the host should not echo). If a block is passed in, this received + # data is also yielded to the block as it is received. + def login(options, password = nil) # :yield: recvdata + login_prompt = /[Ll]ogin[: ]*\z/n + password_prompt = /Password[: ]*\z/n + if options.kind_of?(Hash) + username = options["Name"] + password = options["Password"] + login_prompt = options["LoginPrompt"] if options["LoginPrompt"] + password_prompt = options["PasswordPrompt"] if options["PasswordPrompt"] + else + username = options + end + + if block_given? + line = waitfor(login_prompt){|c| yield c } + if password + line += cmd({"String" => username, + "Match" => password_prompt}){|c| yield c } + line += cmd(password){|c| yield c } + else + line += cmd(username){|c| yield c } + end + else + line = waitfor(login_prompt) + if password + line += cmd({"String" => username, + "Match" => password_prompt}) + line += cmd(password) + else + line += cmd(username) + end + end + line + end + + end # class Telnet +end # module Net + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/telnets.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/telnets.rb new file mode 100644 index 0000000000..e39fe5aab9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/net/telnets.rb @@ -0,0 +1,248 @@ +=begin += $RCSfile$ -- SSL/TLS enhancement for Net::Telnet. + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id: telnets.rb 11708 2007-02-12 23:01:19Z shyouhei $ + + 2001/11/06: Contiributed to Ruby/OpenSSL project. + +== class Net::Telnet + +This class will initiate SSL/TLS session automaticaly if the server +sent OPT_STARTTLS. Some options are added for SSL/TLS. + + host = Net::Telnet::new({ + "Host" => "localhost", + "Port" => "telnets", + ## follows are new options. + 'CertFile' => "user.crt", + 'KeyFile' => "user.key", + 'CAFile' => "/some/where/certs/casert.pem", + 'CAPath' => "/some/where/caserts", + 'VerifyMode' => SSL::VERIFY_PEER, + 'VerifyCallback' => verify_proc + }) + +Or, the new options ('Cert', 'Key' and 'CACert') are available from +Michal Rokos's OpenSSL module. + + cert_data = File.open("user.crt"){|io| io.read } + pkey_data = File.open("user.key"){|io| io.read } + cacert_data = File.open("your_ca.pem"){|io| io.read } + host = Net::Telnet::new({ + "Host" => "localhost", + "Port" => "telnets", + 'Cert' => OpenSSL::X509::Certificate.new(cert_data) + 'Key' => OpenSSL::PKey::RSA.new(pkey_data) + 'CACert' => OpenSSL::X509::Certificate.new(cacert_data) + 'CAFile' => "/some/where/certs/casert.pem", + 'CAPath' => "/some/where/caserts", + 'VerifyMode' => SSL::VERIFY_PEER, + 'VerifyCallback' => verify_proc + }) + +This class is expected to be a superset of usual Net::Telnet. +=end + +require "net/telnet" +require "openssl" + +module Net + class Telnet + attr_reader :ssl + + OPT_STARTTLS = 46.chr # "\056" # "\x2e" # Start TLS + TLS_FOLLOWS = 1.chr # "\001" # "\x01" # FOLLOWS (for STARTTLS) + + alias preprocess_orig preprocess + + def ssl?; @ssl; end + + def preprocess(string) + # combine CR+NULL into CR + string = string.gsub(/#{CR}#{NULL}/no, CR) if @options["Telnetmode"] + + # combine EOL into "\n" + string = string.gsub(/#{EOL}/no, "\n") unless @options["Binmode"] + + string.gsub(/#{IAC}( + [#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]| + [#{DO}#{DONT}#{WILL}#{WONT}][#{OPT_BINARY}-#{OPT_EXOPL}]| + #{SB}[#{OPT_BINARY}-#{OPT_EXOPL}] + (#{IAC}#{IAC}|[^#{IAC}])+#{IAC}#{SE} + )/xno) do + if IAC == $1 # handle escaped IAC characters + IAC + elsif AYT == $1 # respond to "IAC AYT" (are you there) + self.write("nobody here but us pigeons" + EOL) + '' + elsif DO[0] == $1[0] # respond to "IAC DO x" + if OPT_BINARY[0] == $1[1] + @telnet_option["BINARY"] = true + self.write(IAC + WILL + OPT_BINARY) + elsif OPT_STARTTLS[0] == $1[1] + self.write(IAC + WILL + OPT_STARTTLS) + self.write(IAC + SB + OPT_STARTTLS + TLS_FOLLOWS + IAC + SE) + else + self.write(IAC + WONT + $1[1..1]) + end + '' + elsif DONT[0] == $1[0] # respond to "IAC DON'T x" with "IAC WON'T x" + self.write(IAC + WONT + $1[1..1]) + '' + elsif WILL[0] == $1[0] # respond to "IAC WILL x" + if OPT_BINARY[0] == $1[1] + self.write(IAC + DO + OPT_BINARY) + elsif OPT_ECHO[0] == $1[1] + self.write(IAC + DO + OPT_ECHO) + elsif OPT_SGA[0] == $1[1] + @telnet_option["SGA"] = true + self.write(IAC + DO + OPT_SGA) + else + self.write(IAC + DONT + $1[1..1]) + end + '' + elsif WONT[0] == $1[0] # respond to "IAC WON'T x" + if OPT_ECHO[0] == $1[1] + self.write(IAC + DONT + OPT_ECHO) + elsif OPT_SGA[0] == $1[1] + @telnet_option["SGA"] = false + self.write(IAC + DONT + OPT_SGA) + else + self.write(IAC + DONT + $1[1..1]) + end + '' + elsif SB[0] == $1[0] # respond to "IAC SB xxx IAC SE" + if OPT_STARTTLS[0] == $1[1] && TLS_FOLLOWS[0] == $2[0] + @sock = OpenSSL::SSL::SSLSocket.new(@sock) + @sock.cert = @options['Cert'] unless @sock.cert + @sock.key = @options['Key'] unless @sock.key + @sock.ca_cert = @options['CACert'] + @sock.ca_file = @options['CAFile'] + @sock.ca_path = @options['CAPath'] + @sock.timeout = @options['Timeout'] + @sock.verify_mode = @options['VerifyMode'] + @sock.verify_callback = @options['VerifyCallback'] + @sock.verify_depth = @options['VerifyDepth'] + @sock.connect + @ssl = true + end + '' + else + '' + end + end + end # preprocess + + alias waitfor_org waitfor + + def waitfor(options) + time_out = @options["Timeout"] + waittime = @options["Waittime"] + + if options.kind_of?(Hash) + prompt = if options.has_key?("Match") + options["Match"] + elsif options.has_key?("Prompt") + options["Prompt"] + elsif options.has_key?("String") + Regexp.new( Regexp.quote(options["String"]) ) + end + time_out = options["Timeout"] if options.has_key?("Timeout") + waittime = options["Waittime"] if options.has_key?("Waittime") + else + prompt = options + end + + if time_out == false + time_out = nil + end + + line = '' + buf = '' + @rest = '' unless @rest + + until(prompt === line and not IO::select([@sock], nil, nil, waittime)) + unless IO::select([@sock], nil, nil, time_out) + raise TimeoutError, "timed-out; wait for the next data" + end + begin + c = @rest + @sock.sysread(1024 * 1024) + @dumplog.log_dump('<', c) if @options.has_key?("Dump_log") + if @options["Telnetmode"] + pos = 0 + catch(:next){ + while true + case c[pos] + when IAC[0] + case c[pos+1] + when DO[0], DONT[0], WILL[0], WONT[0] + throw :next unless c[pos+2] + pos += 3 + when SB[0] + ret = detect_sub_negotiation(c, pos) + throw :next unless ret + pos = ret + when nil + throw :next + else + pos += 2 + end + when nil + throw :next + else + pos += 1 + end + end + } + + buf = preprocess(c[0...pos]) + @rest = c[pos..-1] + end + @log.print(buf) if @options.has_key?("Output_log") + line.concat(buf) + yield buf if block_given? + rescue EOFError # End of file reached + if line == '' + line = nil + yield nil if block_given? + end + break + end + end + line + end + + private + + def detect_sub_negotiation(data, pos) + return nil if data.length < pos+6 # IAC SB x param IAC SE + pos += 3 + while true + case data[pos] + when IAC[0] + if data[pos+1] == SE[0] + pos += 2 + return pos + else + pos += 2 + end + when nil + return nil + else + pos += 1 + end + end + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/observer.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/observer.rb new file mode 100644 index 0000000000..64c7d81351 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/observer.rb @@ -0,0 +1,192 @@ +# +# observer.rb implements the _Observer_ object-oriented design pattern. The +# following documentation is copied, with modifications, from "Programming +# Ruby", by Hunt and Thomas; http://www.rubycentral.com/book/lib_patterns.html. +# +# == About +# +# The Observer pattern, also known as Publish/Subscribe, provides a simple +# mechanism for one object to inform a set of interested third-party objects +# when its state changes. +# +# == Mechanism +# +# In the Ruby implementation, the notifying class mixes in the +Observable+ +# module, which provides the methods for managing the associated observer +# objects. +# +# The observers must implement the +update+ method to receive notifications. +# +# The observable object must: +# * assert that it has +changed+ +# * call +notify_observers+ +# +# == Example +# +# The following example demonstrates this nicely. A +Ticker+, when run, +# continually receives the stock +Price+ for its +@symbol+. A +Warner+ is a +# general observer of the price, and two warners are demonstrated, a +WarnLow+ +# and a +WarnHigh+, which print a warning if the price is below or above their +# set limits, respectively. +# +# The +update+ callback allows the warners to run without being explicitly +# called. The system is set up with the +Ticker+ and several observers, and the +# observers do their duty without the top-level code having to interfere. +# +# Note that the contract between publisher and subscriber (observable and +# observer) is not declared or enforced. The +Ticker+ publishes a time and a +# price, and the warners receive that. But if you don't ensure that your +# contracts are correct, nothing else can warn you. +# +# require "observer" +# +# class Ticker ### Periodically fetch a stock price. +# include Observable +# +# def initialize(symbol) +# @symbol = symbol +# end +# +# def run +# lastPrice = nil +# loop do +# price = Price.fetch(@symbol) +# print "Current price: #{price}\n" +# if price != lastPrice +# changed # notify observers +# lastPrice = price +# notify_observers(Time.now, price) +# end +# sleep 1 +# end +# end +# end +# +# class Price ### A mock class to fetch a stock price (60 - 140). +# def Price.fetch(symbol) +# 60 + rand(80) +# end +# end +# +# class Warner ### An abstract observer of Ticker objects. +# def initialize(ticker, limit) +# @limit = limit +# ticker.add_observer(self) +# end +# end +# +# class WarnLow < Warner +# def update(time, price) # callback for observer +# if price < @limit +# print "--- #{time.to_s}: Price below #@limit: #{price}\n" +# end +# end +# end +# +# class WarnHigh < Warner +# def update(time, price) # callback for observer +# if price > @limit +# print "+++ #{time.to_s}: Price above #@limit: #{price}\n" +# end +# end +# end +# +# ticker = Ticker.new("MSFT") +# WarnLow.new(ticker, 80) +# WarnHigh.new(ticker, 120) +# ticker.run +# +# Produces: +# +# Current price: 83 +# Current price: 75 +# --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 75 +# Current price: 90 +# Current price: 134 +# +++ Sun Jun 09 00:10:25 CDT 2002: Price above 120: 134 +# Current price: 134 +# Current price: 112 +# Current price: 79 +# --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79 + + +# +# Implements the Observable design pattern as a mixin so that other objects can +# be notified of changes in state. See observer.rb for details and an example. +# +module Observable + + # + # Add +observer+ as an observer on this object. +observer+ will now receive + # notifications. + # + def add_observer(observer) + @observer_peers = [] unless defined? @observer_peers + unless observer.respond_to? :update + raise NoMethodError, "observer needs to respond to `update'" + end + @observer_peers.push observer + end + + # + # Delete +observer+ as an observer on this object. It will no longer receive + # notifications. + # + def delete_observer(observer) + @observer_peers.delete observer if defined? @observer_peers + end + + # + # Delete all observers associated with this object. + # + def delete_observers + @observer_peers.clear if defined? @observer_peers + end + + # + # Return the number of observers associated with this object. + # + def count_observers + if defined? @observer_peers + @observer_peers.size + else + 0 + end + end + + # + # Set the changed state of this object. Notifications will be sent only if + # the changed +state+ is +true+. + # + def changed(state=true) + @observer_state = state + end + + # + # Query the changed state of this object. + # + def changed? + if defined? @observer_state and @observer_state + true + else + false + end + end + + # + # If this object's changed state is +true+, invoke the update method in each + # currently associated observer in turn, passing it the given arguments. The + # changed state is then set to +false+. + # + def notify_observers(*arg) + if defined? @observer_state and @observer_state + if defined? @observer_peers + for i in @observer_peers.dup + i.update(*arg) + end + end + @observer_state = false + end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/open-uri.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/open-uri.rb new file mode 100644 index 0000000000..36a0639a80 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/open-uri.rb @@ -0,0 +1,679 @@ +require 'uri' +require 'stringio' +require 'time' + +module Kernel + private + alias open_uri_original_open open # :nodoc: + + # makes possible to open various resources including URIs. + # If the first argument respond to `open' method, + # the method is called with the rest arguments. + # + # If the first argument is a string which begins with xxx://, + # it is parsed by URI.parse. If the parsed object respond to `open' method, + # the method is called with the rest arguments. + # + # Otherwise original open is called. + # + # Since open-uri.rb provides URI::HTTP#open, URI::HTTPS#open and + # URI::FTP#open, + # Kernel[#.]open can accepts such URIs and strings which begins with + # http://, https:// and ftp://. + # In these case, the opened file object is extended by OpenURI::Meta. + def open(name, *rest, &block) # :doc: + if name.respond_to?(:open) + name.open(*rest, &block) + elsif name.respond_to?(:to_str) && + %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name && + (uri = URI.parse(name)).respond_to?(:open) + uri.open(*rest, &block) + else + open_uri_original_open(name, *rest, &block) + end + end + module_function :open +end + +# OpenURI is an easy-to-use wrapper for net/http, net/https and net/ftp. +# +#== Example +# +# It is possible to open http/https/ftp URL as usual like opening a file: +# +# open("http://www.ruby-lang.org/") {|f| +# f.each_line {|line| p line} +# } +# +# The opened file has several methods for meta information as follows since +# it is extended by OpenURI::Meta. +# +# open("http://www.ruby-lang.org/en") {|f| +# f.each_line {|line| p line} +# p f.base_uri # +# p f.content_type # "text/html" +# p f.charset # "iso-8859-1" +# p f.content_encoding # [] +# p f.last_modified # Thu Dec 05 02:45:02 UTC 2002 +# } +# +# Additional header fields can be specified by an optional hash argument. +# +# open("http://www.ruby-lang.org/en/", +# "User-Agent" => "Ruby/#{RUBY_VERSION}", +# "From" => "foo@bar.invalid", +# "Referer" => "http://www.ruby-lang.org/") {|f| +# # ... +# } +# +# The environment variables such as http_proxy, https_proxy and ftp_proxy +# are in effect by default. :proxy => nil disables proxy. +# +# open("http://www.ruby-lang.org/en/raa.html", :proxy => nil) {|f| +# # ... +# } +# +# URI objects can be opened in a similar way. +# +# uri = URI.parse("http://www.ruby-lang.org/en/") +# uri.open {|f| +# # ... +# } +# +# URI objects can be read directly. The returned string is also extended by +# OpenURI::Meta. +# +# str = uri.read +# p str.base_uri +# +# Author:: Tanaka Akira + +module OpenURI + Options = { + :proxy => true, + :progress_proc => true, + :content_length_proc => true, + :http_basic_authentication => true, + } + + def OpenURI.check_options(options) # :nodoc: + options.each {|k, v| + next unless Symbol === k + unless Options.include? k + raise ArgumentError, "unrecognized option: #{k}" + end + } + end + + def OpenURI.scan_open_optional_arguments(*rest) # :nodoc: + if !rest.empty? && (String === rest.first || Integer === rest.first) + mode = rest.shift + if !rest.empty? && Integer === rest.first + perm = rest.shift + end + end + return mode, perm, rest + end + + def OpenURI.open_uri(name, *rest) # :nodoc: + uri = URI::Generic === name ? name : URI.parse(name) + mode, perm, rest = OpenURI.scan_open_optional_arguments(*rest) + options = rest.shift if !rest.empty? && Hash === rest.first + raise ArgumentError.new("extra arguments") if !rest.empty? + options ||= {} + OpenURI.check_options(options) + + unless mode == nil || + mode == 'r' || mode == 'rb' || + mode == File::RDONLY + raise ArgumentError.new("invalid access mode #{mode} (#{uri.class} resource is read only.)") + end + + io = open_loop(uri, options) + if block_given? + begin + yield io + ensure + io.close + end + else + io + end + end + + def OpenURI.open_loop(uri, options) # :nodoc: + case opt_proxy = options.fetch(:proxy, true) + when true + find_proxy = lambda {|u| u.find_proxy} + when nil, false + find_proxy = lambda {|u| nil} + when String + opt_proxy = URI.parse(opt_proxy) + find_proxy = lambda {|u| opt_proxy} + when URI::Generic + find_proxy = lambda {|u| opt_proxy} + else + raise ArgumentError.new("Invalid proxy option: #{opt_proxy}") + end + + uri_set = {} + buf = nil + while true + redirect = catch(:open_uri_redirect) { + buf = Buffer.new + uri.buffer_open(buf, find_proxy.call(uri), options) + nil + } + if redirect + if redirect.relative? + # Although it violates RFC2616, Location: field may have relative + # URI. It is converted to absolute URI using uri as a base URI. + redirect = uri + redirect + end + unless OpenURI.redirectable?(uri, redirect) + raise "redirection forbidden: #{uri} -> #{redirect}" + end + if options.include? :http_basic_authentication + # send authentication only for the URI directly specified. + options = options.dup + options.delete :http_basic_authentication + end + uri = redirect + raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s + uri_set[uri.to_s] = true + else + break + end + end + io = buf.io + io.base_uri = uri + io + end + + def OpenURI.redirectable?(uri1, uri2) # :nodoc: + # This test is intended to forbid a redirection from http://... to + # file:///etc/passwd. + # However this is ad hoc. It should be extensible/configurable. + uri1.scheme.downcase == uri2.scheme.downcase || + (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:http|ftp)\z/i =~ uri2.scheme) + end + + def OpenURI.open_http(buf, target, proxy, options) # :nodoc: + if proxy + raise "Non-HTTP proxy URI: #{proxy}" if proxy.class != URI::HTTP + end + + if target.userinfo && "1.9.0" <= RUBY_VERSION + # don't raise for 1.8 because compatibility. + raise ArgumentError, "userinfo not supported. [RFC3986]" + end + + require 'net/http' + klass = Net::HTTP + if URI::HTTP === target + # HTTP or HTTPS + if proxy + klass = Net::HTTP::Proxy(proxy.host, proxy.port) + end + target_host = target.host + target_port = target.port + request_uri = target.request_uri + else + # FTP over HTTP proxy + target_host = proxy.host + target_port = proxy.port + request_uri = target.to_s + end + + http = klass.new(target_host, target_port) + if target.class == URI::HTTPS + require 'net/https' + http.use_ssl = true + http.enable_post_connection_check = true + http.verify_mode = OpenSSL::SSL::VERIFY_PEER + store = OpenSSL::X509::Store.new + store.set_default_paths + http.cert_store = store + end + + header = {} + options.each {|k, v| header[k] = v if String === k } + + resp = nil + http.start { + req = Net::HTTP::Get.new(request_uri, header) + if options.include? :http_basic_authentication + user, pass = options[:http_basic_authentication] + req.basic_auth user, pass + end + http.request(req) {|response| + resp = response + if options[:content_length_proc] && Net::HTTPSuccess === resp + if resp.key?('Content-Length') + options[:content_length_proc].call(resp['Content-Length'].to_i) + else + options[:content_length_proc].call(nil) + end + end + resp.read_body {|str| + buf << str + if options[:progress_proc] && Net::HTTPSuccess === resp + options[:progress_proc].call(buf.size) + end + } + } + } + io = buf.io + io.rewind + io.status = [resp.code, resp.message] + resp.each {|name,value| buf.io.meta_add_field name, value } + case resp + when Net::HTTPSuccess + when Net::HTTPMovedPermanently, # 301 + Net::HTTPFound, # 302 + Net::HTTPSeeOther, # 303 + Net::HTTPTemporaryRedirect # 307 + throw :open_uri_redirect, URI.parse(resp['location']) + else + raise OpenURI::HTTPError.new(io.status.join(' '), io) + end + end + + class HTTPError < StandardError + def initialize(message, io) + super(message) + @io = io + end + attr_reader :io + end + + class Buffer # :nodoc: + def initialize + @io = StringIO.new + @size = 0 + end + attr_reader :size + + StringMax = 10240 + def <<(str) + @io << str + @size += str.length + if StringIO === @io && StringMax < @size + require 'tempfile' + io = Tempfile.new('open-uri') + io.binmode + Meta.init io, @io if @io.respond_to? :meta + io << @io.string + @io = io + end + end + + def io + Meta.init @io unless @io.respond_to? :meta + @io + end + end + + # Mixin for holding meta-information. + module Meta + def Meta.init(obj, src=nil) # :nodoc: + obj.extend Meta + obj.instance_eval { + @base_uri = nil + @meta = {} + } + if src + obj.status = src.status + obj.base_uri = src.base_uri + src.meta.each {|name, value| + obj.meta_add_field(name, value) + } + end + end + + # returns an Array which consists status code and message. + attr_accessor :status + + # returns a URI which is base of relative URIs in the data. + # It may differ from the URI supplied by a user because redirection. + attr_accessor :base_uri + + # returns a Hash which represents header fields. + # The Hash keys are downcased for canonicalization. + attr_reader :meta + + def meta_add_field(name, value) # :nodoc: + @meta[name.downcase] = value + end + + # returns a Time which represents Last-Modified field. + def last_modified + if v = @meta['last-modified'] + Time.httpdate(v) + else + nil + end + end + + RE_LWS = /[\r\n\t ]+/n + RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n + RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])*"}n + RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n + + def content_type_parse # :nodoc: + v = @meta['content-type'] + # The last (?:;#{RE_LWS}?)? matches extra ";" which violates RFC2045. + if v && %r{\A#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?/(#{RE_TOKEN})#{RE_LWS}?(#{RE_PARAMETERS})(?:;#{RE_LWS}?)?\z}no =~ v + type = $1.downcase + subtype = $2.downcase + parameters = [] + $3.scan(/;#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?=#{RE_LWS}?(?:(#{RE_TOKEN})|(#{RE_QUOTED_STRING}))/no) {|att, val, qval| + val = qval.gsub(/[\r\n\t !#-\[\]-~\x80-\xff]+|(\\[\x00-\x7f])/) { $1 ? $1[1,1] : $& } if qval + parameters << [att.downcase, val] + } + ["#{type}/#{subtype}", *parameters] + else + nil + end + end + + # returns "type/subtype" which is MIME Content-Type. + # It is downcased for canonicalization. + # Content-Type parameters are stripped. + def content_type + type, *parameters = content_type_parse + type || 'application/octet-stream' + end + + # returns a charset parameter in Content-Type field. + # It is downcased for canonicalization. + # + # If charset parameter is not given but a block is given, + # the block is called and its result is returned. + # It can be used to guess charset. + # + # If charset parameter and block is not given, + # nil is returned except text type in HTTP. + # In that case, "iso-8859-1" is returned as defined by RFC2616 3.7.1. + def charset + type, *parameters = content_type_parse + if pair = parameters.assoc('charset') + pair.last.downcase + elsif block_given? + yield + elsif type && %r{\Atext/} =~ type && + @base_uri && /\Ahttp\z/i =~ @base_uri.scheme + "iso-8859-1" # RFC2616 3.7.1 + else + nil + end + end + + # returns a list of encodings in Content-Encoding field + # as an Array of String. + # The encodings are downcased for canonicalization. + def content_encoding + v = @meta['content-encoding'] + if v && %r{\A#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?(?:,#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?)*}o =~ v + v.scan(RE_TOKEN).map {|content_coding| content_coding.downcase} + else + [] + end + end + end + + # Mixin for HTTP and FTP URIs. + module OpenRead + # OpenURI::OpenRead#open provides `open' for URI::HTTP and URI::FTP. + # + # OpenURI::OpenRead#open takes optional 3 arguments as: + # OpenURI::OpenRead#open([mode [, perm]] [, options]) [{|io| ... }] + # + # `mode', `perm' is same as Kernel#open. + # + # However, `mode' must be read mode because OpenURI::OpenRead#open doesn't + # support write mode (yet). + # Also `perm' is just ignored because it is meaningful only for file + # creation. + # + # `options' must be a hash. + # + # Each pairs which key is a string in the hash specify a extra header + # field for HTTP. + # I.e. it is ignored for FTP without HTTP proxy. + # + # The hash may include other options which key is a symbol: + # + # [:proxy] + # Synopsis: + # :proxy => "http://proxy.foo.com:8000/" + # :proxy => URI.parse("http://proxy.foo.com:8000/") + # :proxy => true + # :proxy => false + # :proxy => nil + # + # If :proxy option is specified, the value should be String, URI, + # boolean or nil. + # When String or URI is given, it is treated as proxy URI. + # When true is given or the option itself is not specified, + # environment variable `scheme_proxy' is examined. + # `scheme' is replaced by `http', `https' or `ftp'. + # When false or nil is given, the environment variables are ignored and + # connection will be made to a server directly. + # + # [:http_basic_authentication] + # Synopsis: + # :http_basic_authentication=>[user, password] + # + # If :http_basic_authentication is specified, + # the value should be an array which contains 2 strings: + # username and password. + # It is used for HTTP Basic authentication defined by RFC 2617. + # + # [:content_length_proc] + # Synopsis: + # :content_length_proc => lambda {|content_length| ... } + # + # If :content_length_proc option is specified, the option value procedure + # is called before actual transfer is started. + # It takes one argument which is expected content length in bytes. + # + # If two or more transfer is done by HTTP redirection, the procedure + # is called only one for a last transfer. + # + # When expected content length is unknown, the procedure is called with + # nil. + # It is happen when HTTP response has no Content-Length header. + # + # [:progress_proc] + # Synopsis: + # :progress_proc => lambda {|size| ...} + # + # If :progress_proc option is specified, the proc is called with one + # argument each time when `open' gets content fragment from network. + # The argument `size' `size' is a accumulated transfered size in bytes. + # + # If two or more transfer is done by HTTP redirection, the procedure + # is called only one for a last transfer. + # + # :progress_proc and :content_length_proc are intended to be used for + # progress bar. + # For example, it can be implemented as follows using Ruby/ProgressBar. + # + # pbar = nil + # open("http://...", + # :content_length_proc => lambda {|t| + # if t && 0 < t + # pbar = ProgressBar.new("...", t) + # pbar.file_transfer_mode + # end + # }, + # :progress_proc => lambda {|s| + # pbar.set s if pbar + # }) {|f| ... } + # + # OpenURI::OpenRead#open returns an IO like object if block is not given. + # Otherwise it yields the IO object and return the value of the block. + # The IO object is extended with OpenURI::Meta. + def open(*rest, &block) + OpenURI.open_uri(self, *rest, &block) + end + + # OpenURI::OpenRead#read([options]) reads a content referenced by self and + # returns the content as string. + # The string is extended with OpenURI::Meta. + # The argument `options' is same as OpenURI::OpenRead#open. + def read(options={}) + self.open(options) {|f| + str = f.read + Meta.init str, f + str + } + end + end +end + +module URI + class Generic + # returns a proxy URI. + # The proxy URI is obtained from environment variables such as http_proxy, + # ftp_proxy, no_proxy, etc. + # If there is no proper proxy, nil is returned. + # + # Note that capitalized variables (HTTP_PROXY, FTP_PROXY, NO_PROXY, etc.) + # are examined too. + # + # But http_proxy and HTTP_PROXY is treated specially under CGI environment. + # It's because HTTP_PROXY may be set by Proxy: header. + # So HTTP_PROXY is not used. + # http_proxy is not used too if the variable is case insensitive. + # CGI_HTTP_PROXY can be used instead. + def find_proxy + name = self.scheme.downcase + '_proxy' + proxy_uri = nil + if name == 'http_proxy' && ENV.include?('REQUEST_METHOD') # CGI? + # HTTP_PROXY conflicts with *_proxy for proxy settings and + # HTTP_* for header information in CGI. + # So it should be careful to use it. + pairs = ENV.reject {|k, v| /\Ahttp_proxy\z/i !~ k } + case pairs.length + when 0 # no proxy setting anyway. + proxy_uri = nil + when 1 + k, v = pairs.shift + if k == 'http_proxy' && ENV[k.upcase] == nil + # http_proxy is safe to use because ENV is case sensitive. + proxy_uri = ENV[name] + else + proxy_uri = nil + end + else # http_proxy is safe to use because ENV is case sensitive. + proxy_uri = ENV[name] + end + if !proxy_uri + # Use CGI_HTTP_PROXY. cf. libwww-perl. + proxy_uri = ENV["CGI_#{name.upcase}"] + end + elsif name == 'http_proxy' + unless proxy_uri = ENV[name] + if proxy_uri = ENV[name.upcase] + warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.' + end + end + else + proxy_uri = ENV[name] || ENV[name.upcase] + end + + if proxy_uri && self.host + require 'socket' + begin + addr = IPSocket.getaddress(self.host) + proxy_uri = nil if /\A127\.|\A::1\z/ =~ addr + rescue SocketError + end + end + + if proxy_uri + proxy_uri = URI.parse(proxy_uri) + name = 'no_proxy' + if no_proxy = ENV[name] || ENV[name.upcase] + no_proxy.scan(/([^:,]*)(?::(\d+))?/) {|host, port| + if /(\A|\.)#{Regexp.quote host}\z/i =~ self.host && + (!port || self.port == port.to_i) + proxy_uri = nil + break + end + } + end + proxy_uri + else + nil + end + end + end + + class HTTP + def buffer_open(buf, proxy, options) # :nodoc: + OpenURI.open_http(buf, self, proxy, options) + end + + include OpenURI::OpenRead + end + + class FTP + def buffer_open(buf, proxy, options) # :nodoc: + if proxy + OpenURI.open_http(buf, self, proxy, options) + return + end + require 'net/ftp' + + directories = self.path.split(%r{/}, -1) + directories.shift if directories[0] == '' # strip a field before leading slash + directories.each {|d| + d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") } + } + unless filename = directories.pop + raise ArgumentError, "no filename: #{self.inspect}" + end + directories.each {|d| + if /[\r\n]/ =~ d + raise ArgumentError, "invalid directory: #{d.inspect}" + end + } + if /[\r\n]/ =~ filename + raise ArgumentError, "invalid filename: #{filename.inspect}" + end + typecode = self.typecode + if typecode && /\A[aid]\z/ !~ typecode + raise ArgumentError, "invalid typecode: #{typecode.inspect}" + end + + # The access sequence is defined by RFC 1738 + ftp = Net::FTP.open(self.host) + # todo: extract user/passwd from .netrc. + user = 'anonymous' + passwd = nil + user, passwd = self.userinfo.split(/:/) if self.userinfo + ftp.login(user, passwd) + directories.each {|cwd| + ftp.voidcmd("CWD #{cwd}") + } + if typecode + # xxx: typecode D is not handled. + ftp.voidcmd("TYPE #{typecode.upcase}") + end + if options[:content_length_proc] + options[:content_length_proc].call(ftp.size(filename)) + end + ftp.retrbinary("RETR #{filename}", 4096) { |str| + buf << str + options[:progress_proc].call(buf.size) if options[:progress_proc] + } + ftp.close + buf.io.rewind + end + + include OpenURI::OpenRead + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/open3.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/open3.rb new file mode 100644 index 0000000000..c4dacc9473 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/open3.rb @@ -0,0 +1,101 @@ +# +# = open3.rb: Popen, but with stderr, too +# +# Author:: Yukihiro Matsumoto +# Documentation:: Konrad Meyer +# +# Open3 gives you access to stdin, stdout, and stderr when running other +# programs. +# + +# +# Open3 grants you access to stdin, stdout, and stderr when running another +# program. Example: +# +# require "open3" +# include Open3 +# +# stdin, stdout, stderr = popen3('nroff -man') +# +# Open3.popen3 can also take a block which will receive stdin, stdout and +# stderr as parameters. This ensures stdin, stdout and stderr are closed +# once the block exits. Example: +# +# require "open3" +# +# Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... } +# + +module Open3 + # + # Open stdin, stdout, and stderr streams and start external executable. + # Non-block form: + # + # require 'open3' + # + # [stdin, stdout, stderr] = Open3.popen3(cmd) + # + # Block form: + # + # require 'open3' + # + # Open3.popen3(cmd) { |stdin, stdout, stderr| ... } + # + # The parameter +cmd+ is passed directly to Kernel#exec. + # + def popen3(*cmd) + pw = IO::pipe # pipe[0] for read, pipe[1] for write + pr = IO::pipe + pe = IO::pipe + + pid = fork{ + # child + fork{ + # grandchild + pw[1].close + STDIN.reopen(pw[0]) + pw[0].close + + pr[0].close + STDOUT.reopen(pr[1]) + pr[1].close + + pe[0].close + STDERR.reopen(pe[1]) + pe[1].close + + exec(*cmd) + } + exit!(0) + } + + pw[0].close + pr[1].close + pe[1].close + Process.waitpid(pid) + pi = [pw[1], pr[0], pe[0]] + pw[1].sync = true + if defined? yield + begin + return yield(*pi) + ensure + pi.each{|p| p.close unless p.closed?} + end + end + pi + end + module_function :popen3 +end + +if $0 == __FILE__ + a = Open3.popen3("nroff -man") + Thread.start do + while line = gets + a[0].print line + end + a[0].close + end + while line = a[1].gets + print ":", line + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl.rb new file mode 100644 index 0000000000..411c0db7d0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl.rb @@ -0,0 +1,24 @@ +=begin += $RCSfile$ -- Loader for all OpenSSL C-space and Ruby-space definitions + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id: openssl.rb 11708 2007-02-12 23:01:19Z shyouhei $ +=end + +require 'openssl.so' + +require 'openssl/bn' +require 'openssl/cipher' +require 'openssl/digest' +require 'openssl/ssl' +require 'openssl/x509' + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/bn.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/bn.rb new file mode 100644 index 0000000000..cf44a0943c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/bn.rb @@ -0,0 +1,35 @@ +=begin += $RCSfile$ -- Ruby-space definitions that completes C-space funcs for BN + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id: bn.rb 11708 2007-02-12 23:01:19Z shyouhei $ +=end + +## +# Should we care what if somebody require this file directly? +#require 'openssl' + +module OpenSSL + class BN + include Comparable + end # BN +end # OpenSSL + +## +# Add double dispatch to Integer +# +class Integer + def to_bn + OpenSSL::BN::new(self) + end +end # Integer + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/buffering.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/buffering.rb new file mode 100644 index 0000000000..2c66a75e07 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/buffering.rb @@ -0,0 +1,239 @@ +=begin += $RCSfile$ -- Buffering mix-in module. + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id: buffering.rb 11708 2007-02-12 23:01:19Z shyouhei $ +=end + +module Buffering + include Enumerable + attr_accessor :sync + BLOCK_SIZE = 1024*16 + + def initialize(*args) + @eof = false + @rbuffer = "" + @sync = @io.sync + end + + # + # for reading. + # + private + + def fill_rbuff + begin + @rbuffer << self.sysread(BLOCK_SIZE) + rescue Errno::EAGAIN + retry + rescue EOFError + @eof = true + end + end + + def consume_rbuff(size=nil) + if @rbuffer.empty? + nil + else + size = @rbuffer.size unless size + ret = @rbuffer[0, size] + @rbuffer[0, size] = "" + ret + end + end + + public + + def read(size=nil, buf=nil) + if size == 0 + if buf + buf.clear + else + buf = "" + end + return @eof ? nil : buf + end + until @eof + break if size && size <= @rbuffer.size + fill_rbuff + end + ret = consume_rbuff(size) || "" + if buf + buf.replace(ret) + ret = buf + end + (size && ret.empty?) ? nil : ret + end + + def readpartial(maxlen, buf=nil) + if maxlen == 0 + if buf + buf.clear + else + buf = "" + end + return @eof ? nil : buf + end + if @rbuffer.empty? + begin + return sysread(maxlen, buf) + rescue Errno::EAGAIN + retry + end + end + ret = consume_rbuff(maxlen) + if buf + buf.replace(ret) + ret = buf + end + raise EOFError if ret.empty? + ret + end + + def gets(eol=$/) + idx = @rbuffer.index(eol) + until @eof + break if idx + fill_rbuff + idx = @rbuffer.index(eol) + end + if eol.is_a?(Regexp) + size = idx ? idx+$&.size : nil + else + size = idx ? idx+eol.size : nil + end + consume_rbuff(size) + end + + def each(eol=$/) + while line = self.gets(eol) + yield line + end + end + alias each_line each + + def readlines(eol=$/) + ary = [] + while line = self.gets(eol) + ary << line + end + ary + end + + def readline(eol=$/) + raise EOFError if eof? + gets(eol) + end + + def getc + c = read(1) + c ? c[0] : nil + end + + def each_byte + while c = getc + yield(c) + end + end + + def readchar + raise EOFError if eof? + getc + end + + def ungetc(c) + @rbuffer[0,0] = c.chr + end + + def eof? + fill_rbuff if !@eof && @rbuffer.empty? + @eof && @rbuffer.empty? + end + alias eof eof? + + # + # for writing. + # + private + + def do_write(s) + @wbuffer = "" unless defined? @wbuffer + @wbuffer << s + @sync ||= false + if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/) + remain = idx ? idx + $/.size : @wbuffer.length + nwritten = 0 + while remain > 0 + str = @wbuffer[nwritten,remain] + begin + nwrote = syswrite(str) + rescue Errno::EAGAIN + retry + end + remain -= nwrote + nwritten += nwrote + end + @wbuffer[0,nwritten] = "" + end + end + + public + + def write(s) + do_write(s) + s.length + end + + def << (s) + do_write(s) + self + end + + def puts(*args) + s = "" + if args.empty? + s << "\n" + end + args.each{|arg| + s << arg.to_s + if $/ && /\n\z/ !~ s + s << "\n" + end + } + do_write(s) + nil + end + + def print(*args) + s = "" + args.each{ |arg| s << arg.to_s } + do_write(s) + nil + end + + def printf(s, *args) + do_write(s % args) + nil + end + + def flush + osync = @sync + @sync = true + do_write "" + @sync = osync + end + + def close + flush rescue nil + sysclose + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/cipher.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/cipher.rb new file mode 100644 index 0000000000..fb924e962c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/cipher.rb @@ -0,0 +1,58 @@ +=begin += $RCSfile$ -- Ruby-space predefined Cipher subclasses + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id: cipher.rb 11708 2007-02-12 23:01:19Z shyouhei $ +=end + +## +# Should we care what if somebody require this file directly? +#require 'openssl' + +module OpenSSL + module Cipher + %w(AES CAST5 BF DES IDEA RC2 RC4 RC5).each{|name| + klass = Class.new(Cipher){ + define_method(:initialize){|*args| + cipher_name = args.inject(name){|n, arg| "#{n}-#{arg}" } + super(cipher_name) + } + } + const_set(name, klass) + } + + %w(128 192 256).each{|keylen| + klass = Class.new(Cipher){ + define_method(:initialize){|mode| + mode ||= "CBC" + cipher_name = "AES-#{keylen}-#{mode}" + super(cipher_name) + } + } + const_set("AES#{keylen}", klass) + } + + class Cipher + def random_key + str = OpenSSL::Random.random_bytes(self.key_len) + self.key = str + return str + end + + def random_iv + str = OpenSSL::Random.random_bytes(self.iv_len) + self.iv = str + return str + end + end + end # Cipher +end # OpenSSL diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/digest.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/digest.rb new file mode 100644 index 0000000000..0d7e7513f5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/digest.rb @@ -0,0 +1,49 @@ +=begin += $RCSfile$ -- Ruby-space predefined Digest subclasses + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id: digest.rb 11708 2007-02-12 23:01:19Z shyouhei $ +=end + +## +# Should we care what if somebody require this file directly? +#require 'openssl' + +module OpenSSL + module Digest + + alg = %w(DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA SHA1) + if OPENSSL_VERSION_NUMBER > 0x00908000 + alg += %w(SHA224 SHA256 SHA384 SHA512) + end + + alg.each{|name| + klass = Class.new(Digest){ + define_method(:initialize){|*data| + if data.length > 1 + raise ArgumentError, + "wrong number of arguments (#{data.length} for 1)" + end + super(name, data.first) + } + } + singleton = (class < + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id: ssl.rb 13504 2007-09-24 08:12:24Z shyouhei $ +=end + +require "openssl" +require "openssl/buffering" +require "fcntl" + +module OpenSSL + module SSL + module SocketForwarder + def addr + to_io.addr + end + + def peeraddr + to_io.peeraddr + end + + def setsockopt(level, optname, optval) + to_io.setsockopt(level, optname, optval) + end + + def getsockopt(level, optname) + to_io.getsockopt(level, optname) + end + + def fcntl(*args) + to_io.fcntl(*args) + end + + def closed? + to_io.closed? + end + + def do_not_reverse_lookup=(flag) + to_io.do_not_reverse_lookup = flag + end + end + + module Nonblock + def initialize(*args) + flag = File::NONBLOCK + flag |= @io.fcntl(Fcntl::F_GETFL) if defined?(Fcntl::F_GETFL) + @io.fcntl(Fcntl::F_SETFL, flag) + super + end + end + + class SSLSocket + include Buffering + include SocketForwarder + include Nonblock + + def post_connection_check(hostname) + check_common_name = true + cert = peer_cert + cert.extensions.each{|ext| + next if ext.oid != "subjectAltName" + ext.value.split(/,\s+/).each{|general_name| + if /\ADNS:(.*)/ =~ general_name + check_common_name = false + reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+") + return true if /\A#{reg}\z/i =~ hostname + elsif /\AIP Address:(.*)/ =~ general_name + check_common_name = false + return true if $1 == hostname + end + } + } + if check_common_name + cert.subject.to_a.each{|oid, value| + if oid == "CN" + reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+") + return true if /\A#{reg}\z/i =~ hostname + end + } + end + raise SSLError, "hostname was not match with the server certificate" + end + end + + class SSLServer + include SocketForwarder + attr_accessor :start_immediately + + def initialize(svr, ctx) + @svr = svr + @ctx = ctx + unless ctx.session_id_context + session_id = OpenSSL::Digest::MD5.hexdigest($0) + @ctx.session_id_context = session_id + end + @start_immediately = true + end + + def to_io + @svr + end + + def listen(backlog=5) + @svr.listen(backlog) + end + + def accept + sock = @svr.accept + begin + ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx) + ssl.sync_close = true + ssl.accept if @start_immediately + ssl + rescue SSLError => ex + sock.close + raise ex + end + end + + def close + @svr.close + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/x509.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/x509.rb new file mode 100644 index 0000000000..99f239ce37 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/openssl/x509.rb @@ -0,0 +1,154 @@ +=begin += $RCSfile$ -- Ruby-space definitions that completes C-space funcs for X509 and subclasses + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id: x509.rb 11708 2007-02-12 23:01:19Z shyouhei $ +=end + +require "openssl" + +module OpenSSL + module X509 + class ExtensionFactory + def create_extension(*arg) + if arg.size > 1 + create_ext(*arg) + else + send("create_ext_from_"+arg[0].class.name.downcase, arg[0]) + end + end + + def create_ext_from_array(ary) + raise ExtensionError, "unexpected array form" if ary.size > 3 + create_ext(ary[0], ary[1], ary[2]) + end + + def create_ext_from_string(str) # "oid = critical, value" + oid, value = str.split(/=/, 2) + oid.strip! + value.strip! + create_ext(oid, value) + end + + def create_ext_from_hash(hash) + create_ext(hash["oid"], hash["value"], hash["critical"]) + end + end + + class Extension + def to_s # "oid = critical, value" + str = self.oid + str << " = " + str << "critical, " if self.critical? + str << self.value.gsub(/\n/, ", ") + end + + def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false} + {"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?} + end + + def to_a + [ self.oid, self.value, self.critical? ] + end + end + + class Name + module RFC2253DN + Special = ',=+<>#;' + HexChar = /[0-9a-fA-F]/ + HexPair = /#{HexChar}#{HexChar}/ + HexString = /#{HexPair}+/ + Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/ + StringChar = /[^#{Special}\\"]/ + QuoteChar = /[^\\"]/ + AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/ + AttributeValue = / + (?!["#])((?:#{StringChar}|#{Pair})*)| + \#(#{HexString})| + "((?:#{QuoteChar}|#{Pair})*)" + /x + TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/ + + module_function + + def expand_pair(str) + return nil unless str + return str.gsub(Pair){|pair| + case pair.size + when 2 then pair[1,1] + when 3 then Integer("0x#{pair[1,2]}").chr + else raise OpenSSL::X509::NameError, "invalid pair: #{str}" + end + } + end + + def expand_hexstring(str) + return nil unless str + der = str.gsub(HexPair){|hex| Integer("0x#{hex}").chr } + a1 = OpenSSL::ASN1.decode(der) + return a1.value, a1.tag + end + + def expand_value(str1, str2, str3) + value = expand_pair(str1) + value, tag = expand_hexstring(str2) unless value + value = expand_pair(str3) unless value + return value, tag + end + + def scan(dn) + str = dn + ary = [] + while true + if md = TypeAndValue.match(str) + matched = md.to_s + remain = md.post_match + type = md[1] + value, tag = expand_value(md[2], md[3], md[4]) rescue nil + if value + type_and_value = [type, value] + type_and_value.push(tag) if tag + ary.unshift(type_and_value) + if remain.length > 2 && remain[0] == ?, + str = remain[1..-1] + next + elsif remain.length > 2 && remain[0] == ?+ + raise OpenSSL::X509::NameError, + "multi-valued RDN is not supported: #{dn}" + elsif remain.empty? + break + end + end + end + msg_dn = dn[0, dn.length - str.length] + " =>" + str + raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}" + end + return ary + end + end + + class < "iso-2022-jp", "sjis" => "shift_jis" } +# +# # +# # Return a structure describing the options. +# # +# def self.parse(args) +# # The options specified on the command line will be collected in *options*. +# # We set default values here. +# options = OpenStruct.new +# options.library = [] +# options.inplace = false +# options.encoding = "utf8" +# options.transfer_type = :auto +# options.verbose = false +# +# opts = OptionParser.new do |opts| +# opts.banner = "Usage: example.rb [options]" +# +# opts.separator "" +# opts.separator "Specific options:" +# +# # Mandatory argument. +# opts.on("-r", "--require LIBRARY", +# "Require the LIBRARY before executing your script") do |lib| +# options.library << lib +# end +# +# # Optional argument; multi-line description. +# opts.on("-i", "--inplace [EXTENSION]", +# "Edit ARGV files in place", +# " (make backup if EXTENSION supplied)") do |ext| +# options.inplace = true +# options.extension = ext || '' +# options.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot. +# end +# +# # Cast 'delay' argument to a Float. +# opts.on("--delay N", Float, "Delay N seconds before executing") do |n| +# options.delay = n +# end +# +# # Cast 'time' argument to a Time object. +# opts.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time| +# options.time = time +# end +# +# # Cast to octal integer. +# opts.on("-F", "--irs [OCTAL]", OptionParser::OctalInteger, +# "Specify record separator (default \\0)") do |rs| +# options.record_separator = rs +# end +# +# # List of arguments. +# opts.on("--list x,y,z", Array, "Example 'list' of arguments") do |list| +# options.list = list +# end +# +# # Keyword completion. We are specifying a specific set of arguments (CODES +# # and CODE_ALIASES - notice the latter is a Hash), and the user may provide +# # the shortest unambiguous text. +# code_list = (CODE_ALIASES.keys + CODES).join(',') +# opts.on("--code CODE", CODES, CODE_ALIASES, "Select encoding", +# " (#{code_list})") do |encoding| +# options.encoding = encoding +# end +# +# # Optional argument with keyword completion. +# opts.on("--type [TYPE]", [:text, :binary, :auto], +# "Select transfer type (text, binary, auto)") do |t| +# options.transfer_type = t +# end +# +# # Boolean switch. +# opts.on("-v", "--[no-]verbose", "Run verbosely") do |v| +# options.verbose = v +# end +# +# opts.separator "" +# opts.separator "Common options:" +# +# # No argument, shows at tail. This will print an options summary. +# # Try it and see! +# opts.on_tail("-h", "--help", "Show this message") do +# puts opts +# exit +# end +# +# # Another typical switch to print the version. +# opts.on_tail("--version", "Show version") do +# puts OptionParser::Version.join('.') +# exit +# end +# end +# +# opts.parse!(args) +# options +# end # parse() +# +# end # class OptparseExample +# +# options = OptparseExample.parse(ARGV) +# pp options +# +# === Further documentation +# +# The above examples should be enough to learn how to use this class. If you +# have any questions, email me (gsinclair@soyabean.com.au) and I will update +# this document. +# +class OptionParser + # :stopdoc: + RCSID = %w$Id: optparse.rb 11798 2007-02-20 06:53:16Z knu $[1..-1].each {|s| s.freeze}.freeze + #Version = (RCSID[1].split('.').collect {|s| s.to_i}.extend(Comparable).freeze if RCSID[1]) + if (RCSID[1]) + Version = RCSID[1].split('.').collect {|s| s.to_i} + class << Version + include Comparable + end + Version.freeze + end + #LastModified = (Time.gm(*RCSID[2, 2].join('-').scan(/\d+/).collect {|s| s.to_i}) if RCSID[2]) + LastModified = '123' if RCSID[2] #(Time.gm(*RCSID[2, 2].join('-').scan(/\d+/).collect {|s| s.to_i}) if RCSID[2]) + Release = RCSID[2] + + NoArgument = [NO_ARGUMENT = :NONE, nil].freeze + RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze + OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze + # :startdoc: + + # + # Keyword completion module. This allows partial arguments to be specified + # and resolved against a list of acceptable values. + # + module Completion + def complete(key, icase = false, pat = nil) + pat ||= Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'), + icase) + canon, sw, k, v, cn = nil + candidates = [] + each do |k, *v| + (if Regexp === k + kn = nil + k === key + else + kn = defined?(k.id2name) ? k.id2name : k + pat === kn + end) or next + v << k if v.empty? + candidates << [k, v, kn] + end + candidates = candidates.sort_by {|k, v, kn| kn.size} + if candidates.size == 1 + canon, sw, * = candidates[0] + elsif candidates.size > 1 + canon, sw, cn = candidates.shift + candidates.each do |k, v, kn| + next if sw == v + if String === cn and String === kn + if cn.rindex(kn, 0) + canon, sw, cn = k, v, kn + next + elsif kn.rindex(cn, 0) + next + end + end + throw :ambiguous, key + end + end + if canon + block_given? or return key, *sw + yield(key, *sw) + end + end + + def convert(opt = nil, val = nil, *) + val + end + end + + + # + # Map from option/keyword string to object with completion. + # + class OptionMap < Hash + include Completion + end + + + # + # Individual switch class. Not important to the user. + # + # Defined within Switch are several Switch-derived classes: NoArgument, + # RequiredArgument, etc. + # + class Switch + attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block + + # + # Guesses argument style from +arg+. Returns corresponding + # OptionParser::Switch class (OptionalArgument, etc.). + # + def self.guess(arg) + case arg + when "" + t = self + when /\A=?\[/ + t = Switch::OptionalArgument + when /\A\s+\[/ + t = Switch::PlacedArgument + else + t = Switch::RequiredArgument + end + self >= t or incompatible_argument_styles(arg, t) + t + end + + def self.incompatible_argument_styles(arg, t) + raise ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}" + end + + def self.pattern + NilClass + end + + def initialize(pattern = nil, conv = nil, + short = nil, long = nil, arg = nil, + #desc = ([] if short or long), &b) + desc = ([] if short or long), block = Proc.new) + #block = b.nil? ? Proc.new { } : b + raise if Array === pattern + @pattern, @conv, @short, @long, @arg, @desc, @block = + pattern, conv, short, long, arg, desc, block + end + + # + # Parses +arg+ and returns rest of +arg+ and matched portion to the + # argument pattern. Yields when the pattern doesn't match substring. + # + def parse_arg(arg) + pattern or return nil, arg + unless m = pattern.match(arg) + yield(InvalidArgument, arg) + return arg, nil + end + if String === m + m = [s = m] + else + m = m.to_a + s = m[0] + return nil, m unless String === s + end + raise InvalidArgument, arg unless arg.rindex(s, 0) + return nil, m if s.length == arg.length + yield(InvalidArgument, arg) # didn't match whole arg + return arg[s.length..-1], m + end + private :parse_arg + + # + # Parses argument, converts and returns +arg+, +block+ and result of + # conversion. Yields at semi-error condition instead of raising an + # exception. + # + def conv_arg(arg, val = nil) + if conv + val = conv.call(*val) + else + val = proc {|val| val}.call(*val) + end + return arg, block, val + end + private :conv_arg + + # + # Produces the summary text. Each line of the summary is yielded to the + # block (without newline). + # + # +sdone+:: Already summarized short style options keyed hash. + # +ldone+:: Already summarized long style options keyed hash. + # +width+:: Width of left side (option part). In other words, the right + # side (description part) starts after +width+ columns. + # +max+:: Maximum width of left side -> the options are filled within + # +max+ columns. + # +indent+:: Prefix string indents all summarized lines. + # + def summarize(sdone = [], ldone = [], width = 1, max = width - 1, indent = "") + sopts, lopts, s = [], [], nil + @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short + @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long + return if sopts.empty? and lopts.empty? # completely hidden + + left = [sopts.join(', ')] + right = desc.dup + + while s = lopts.shift + l = left[-1].length + s.length + l += arg.length if left.size == 1 && arg + l < max or left << '' + left[-1] << if left[-1].empty? then ' ' * 4 else ', ' end << s + end + + left[0] << arg if arg + mlen = left.collect {|s| s.length}.max.to_i + while mlen > width and l = left.shift + mlen = left.collect {|s| s.length}.max.to_i if l.length == mlen + yield(indent + l) + end + + while begin l = left.shift; r = right.shift; l or r end + l = l.to_s.ljust(width) + ' ' + r if r and !r.empty? + yield(indent + l) + end + + self + end + + def add_banner(to) # :nodoc: + unless @short or @long + s = desc.join + to << " [" + s + "]..." unless s.empty? + end + to + end + + def match_nonswitch?(str) # :nodoc: + @pattern =~ str unless @short or @long + end + + # + # Main name of the switch. + # + def switch_name + (long.first || short.first).sub(/\A-+(?:\[no-\])?/, '') + end + + # + # Switch that takes no arguments. + # + class NoArgument < self + + # + # Raises an exception if any arguments given. + # + def parse(arg, argv) + yield(NeedlessArgument, arg) if arg + conv_arg(arg) + end + + def self.incompatible_argument_styles(*) + end + + def self.pattern + Object + end + end + + # + # Switch that takes an argument. + # + class RequiredArgument < self + + # + # Raises an exception if argument is not present. + # + def parse(arg, argv) + unless arg + raise MissingArgument if argv.empty? + arg = argv.shift + end + conv_arg(*parse_arg(arg) {|*exc| raise(*exc)}) + end + end + + # + # Switch that can omit argument. + # + class OptionalArgument < self + + # + # Parses argument if given, or uses default value. + # + def parse(arg, argv, &error) + if arg + conv_arg(*parse_arg(arg, &error)) + else + conv_arg(arg) + end + end + end + + # + # Switch that takes an argument, which does not begin with '-'. + # + class PlacedArgument < self + + # + # Returns nil if argument is not present or begins with '-'. + # + def parse(arg, argv, &error) + if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0])) + return nil, block, nil + end + opt = (val = parse_arg(val, &error))[1] + val = conv_arg(*val) + if opt and !arg + argv.shift + else + val[0] = nil + end + val + end + end + end + + # + # Simple option list providing mapping from short and/or long option + # string to OptionParser::Switch and mapping from acceptable argument to + # matching pattern and converter pair. Also provides summary feature. + # + class List + # Map from acceptable argument types to pattern and converter pairs. + attr_reader :atype + + # Map from short style option switches to actual switch objects. + attr_reader :short + + # Map from long style option switches to actual switch objects. + attr_reader :long + + # List of all switches and summary string. + attr_reader :list + + # + # Just initializes all instance variables. + # + def initialize + @atype = {} + @short = OptionMap.new + @long = OptionMap.new + @list = [] + end + + # + # See OptionParser.accept. + # + def accept(t, pat = /.*/nm, &block) + if pat + pat.respond_to?(:match) or raise TypeError, "has no `match'" + else + pat = t if t.respond_to?(:match) + end + unless block + block = pat.method(:convert).to_proc if pat.respond_to?(:convert) + end + @atype[t] = [pat, block] + end + + # + # See OptionParser.reject. + # + def reject(t) + @atype.delete(t) + end + + # + # Adds +sw+ according to +sopts+, +lopts+ and +nlopts+. + # + # +sw+:: OptionParser::Switch instance to be added. + # +sopts+:: Short style option list. + # +lopts+:: Long style option list. + # +nlopts+:: Negated long style options list. + # + def update(sw, sopts, lopts, nsw = nil, nlopts = nil) + o = nil + sopts.each {|o| @short[o] = sw} if sopts + lopts.each {|o| @long[o] = sw} if lopts + nlopts.each {|o| @long[o] = nsw} if nsw and nlopts + used = @short.invert.update(@long.invert) + @list.delete_if {|o| Switch === o and !used[o]} + end + private :update + + # + # Inserts +switch+ at the head of the list, and associates short, long + # and negated long options. Arguments are: + # + # +switch+:: OptionParser::Switch instance to be inserted. + # +short_opts+:: List of short style options. + # +long_opts+:: List of long style options. + # +nolong_opts+:: List of long style options with "no-" prefix. + # + # prepend(switch, short_opts, long_opts, nolong_opts) + # + def prepend(*args) + update(*args) + @list.unshift(args[0]) + end + + # + # Appends +switch+ at the tail of the list, and associates short, long + # and negated long options. Arguments are: + # + # +switch+:: OptionParser::Switch instance to be inserted. + # +short_opts+:: List of short style options. + # +long_opts+:: List of long style options. + # +nolong_opts+:: List of long style options with "no-" prefix. + # + # append(switch, short_opts, long_opts, nolong_opts) + # + def append(*args) + update(*args) + @list.push(args[0]) + end + + # + # Searches +key+ in +id+ list. The result is returned or yielded if a + # block is given. If it isn't found, nil is returned. + # + def search(id, key) + if list = __send__(id) + val = list.fetch(key) {return nil} + block_given? ? yield(val) : val + end + end + + # + # Searches list +id+ for +opt+ and the optional patterns for completion + # +pat+. If +icase+ is true, the search is case insensitive. The result + # is returned or yielded if a block is given. If it isn't found, nil is + # returned. + # + def complete(id, opt, icase = false, *pat, &block) + __send__(id).complete(opt, icase, *pat, &block) + end + + # + # Iterates over each option, passing the option to the +block+. + # + def each_option(&block) + list.each(&block) + end + + # + # Creates the summary table, passing each line to the +block+ (without + # newline). The arguments +args+ are passed along to the summarize + # method which is called on every option. + # + def summarize(*args, &block) + list.each do |opt| + if opt.respond_to?(:summarize) # perhaps OptionParser::Switch + opt.summarize(*args, &block) + elsif !opt or opt.empty? + yield("") + else + opt.each(&block) + end + end + end + + def add_banner(to) # :nodoc: + list.each do |opt| + if opt.respond_to?(:add_banner) + opt.add_banner(to) + end + end + to + end + end + + # + # Hash with completion search feature. See OptionParser::Completion. + # + class CompletingHash < Hash + include Completion + + # + # Completion for hash key. + # + def match(key) + return key, *fetch(key) { + raise AmbiguousArgument, catch(:ambiguous) {return complete(key)} + } + end + end + + # :stopdoc: + + # + # Enumeration of acceptable argument styles. Possible values are: + # + # NO_ARGUMENT:: The switch takes no arguments. (:NONE) + # REQUIRED_ARGUMENT:: The switch requires an argument. (:REQUIRED) + # OPTIONAL_ARGUMENT:: The switch requires an optional argument. (:OPTIONAL) + # + # Use like --switch=argument (long style) or -Xargument (short style). For + # short style, only portion matched to argument pattern is dealed as + # argument. + # + ArgumentStyle = {} + NoArgument.each {|el| ArgumentStyle[el] = Switch::NoArgument} + RequiredArgument.each {|el| ArgumentStyle[el] = Switch::RequiredArgument} + OptionalArgument.each {|el| ArgumentStyle[el] = Switch::OptionalArgument} + ArgumentStyle.freeze + + # + # Switches common used such as '--', and also provides default + # argument classes + # + DefaultList = List.new + DefaultList.short['-'] = Switch::NoArgument.new {} + DefaultList.long[''] = Switch::NoArgument.new {throw :terminate} + + # + # Default options for ARGV, which never appear in option summary. + # + Officious = {} + + # + # --help + # Shows option summary. + # + Officious['help'] = proc do |parser| + Switch::NoArgument.new do + puts parser.help + exit + end + end + + # + # --version + # Shows version string if Version is defined. + # + Officious['version'] = proc do |parser| + Switch::OptionalArgument.new do |pkg| + if pkg + begin + require 'optparse/version' + rescue LoadError + else + show_version(*pkg.split(/,/)) or + abort("#{parser.program_name}: no version found in package #{pkg}") + exit + end + end + v = parser.ver or abort("#{parser.program_name}: version unknown") + puts v + exit + end + end + + # :startdoc: + + # + # Class methods + # + + # + # Initializes a new instance and evaluates the optional block in context + # of the instance. Arguments +args+ are passed to #new, see there for + # description of parameters. + # + # This method is *deprecated*, its behavior corresponds to the older #new + # method. + # + def self.with(*args, &block) + opts = new(*args) + opts.instance_eval(&block) + opts + end + + # + # Returns an incremented value of +default+ according to +arg+. + # + def self.inc(arg, default = nil) + case arg + when Integer + arg.nonzero? + when nil + default.to_i + 1 + end + end + def inc(*args) + self.class.inc(*args) + end + + # + # Initializes the instance and yields itself if called with a block. + # + # +banner+:: Banner message. + # +width+:: Summary width. + # +indent+:: Summary indent. + # + def initialize(banner = nil, width = 32, indent = ' ' * 4) + @stack = [DefaultList, List.new, List.new] + @program_name = nil + @banner = banner + @summary_width = width + @summary_indent = indent + @default_argv = ARGV + add_officious + yield self if block_given? + end + + def add_officious # :nodoc: + list = base() + Officious.each do |opt, block| + list.long[opt] ||= block.call(self) + end + end + + # + # Terminates option parsing. Optional parameter +arg+ is a string pushed + # back to be the first non-option argument. + # + def terminate(arg = nil) + self.class.terminate(arg) + end + def self.terminate(arg = nil) + throw :terminate, arg + end + + @stack = [DefaultList] + def self.top() DefaultList end + + # + # Directs to accept specified class +t+. The argument string is passed to + # the block in which it should be converted to the desired class. + # + # +t+:: Argument class specifier, any object including Class. + # +pat+:: Pattern for argument, defaults to +t+ if it responds to match. + # + # accept(t, pat, &block) + # + def accept(*args, &blk) top.accept(*args, &blk) end + # + # See #accept. + # + def self.accept(*args, &blk) top.accept(*args, &blk) end + + # + # Directs to reject specified class argument. + # + # +t+:: Argument class speficier, any object including Class. + # + # reject(t) + # + def reject(*args, &blk) top.reject(*args, &blk) end + # + # See #reject. + # + def self.reject(*args, &blk) top.reject(*args, &blk) end + + # + # Instance methods + # + + # Heading banner preceding summary. + attr_writer :banner + + # Program name to be emitted in error message and default banner, + # defaults to $0. + attr_writer :program_name + + # Width for option list portion of summary. Must be Numeric. + attr_accessor :summary_width + + # Indentation for summary. Must be String (or have + String method). + attr_accessor :summary_indent + + # Strings to be parsed in default. + attr_accessor :default_argv + + # + # Heading banner preceding summary. + # + def banner + unless @banner + @banner = "Usage: #{program_name} [options]" + visit(:add_banner, @banner) + end + @banner + end + + # + # Program name to be emitted in error message and default banner, defaults + # to $0. + # + def program_name + @program_name || File.basename($0, '.*') + end + + # for experimental cascading :-) + alias set_banner banner= + alias set_program_name program_name= + alias set_summary_width summary_width= + alias set_summary_indent summary_indent= + + # Version + attr_writer :version + # Release code + attr_writer :release + + # + # Version + # + def version + @version || (defined?(::Version) && ::Version) + end + + # + # Release code + # + def release + @release || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE) + end + + # + # Returns version string from program_name, version and release. + # + def ver + if v = version + str = "#{program_name} #{[v].join('.')}" + str << " (#{v})" if v = release + str + end + end + + def warn(mesg = $!) + super("#{program_name}: #{mesg}") + end + + def abort(mesg = $!) + super("#{program_name}: #{mesg}") + end + + # + # Subject of #on / #on_head, #accept / #reject + # + def top + @stack[-1] + end + + # + # Subject of #on_tail. + # + def base + @stack[1] + end + + # + # Pushes a new List. + # + def new + @stack.push(List.new) + if block_given? + yield self + else + self + end + end + + # + # Removes the last List. + # + def remove + @stack.pop + end + + # + # Puts option summary into +to+ and returns +to+. Yields each line if + # a block is given. + # + # +to+:: Output destination, which must have method <<. Defaults to []. + # +width+:: Width of left side, defaults to @summary_width. + # +max+:: Maximum length allowed for left side, defaults to +width+ - 1. + # +indent+:: Indentation, defaults to @summary_indent. + # + def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk) + visit(:summarize, {}, {}, width, max, indent, &(blk || proc {|l| to << l + $/})) + to + end + + # + # Returns option summary string. + # + def help; summarize(banner.to_s.sub(/\n?\z/, "\n")) end + alias to_s help + + # + # Returns option summary list. + # + def to_a; summarize(banner.to_a.dup) end + + # + # Checks if an argument is given twice, in which case an ArgumentError is + # raised. Called from OptionParser#switch only. + # + # +obj+:: New argument. + # +prv+:: Previously specified argument. + # +msg+:: Exception message. + # + def notwice(obj, prv, msg) + unless !prv or prv == obj + begin + raise ArgumentError, "argument #{msg} given twice: #{obj}" + rescue + $@[0, 2] = nil + raise + end + end + obj + end + private :notwice + + # + # Creates an OptionParser::Switch from the parameters. The parsed argument + # value is passed to the given block, where it can be processed. + # + # See at the beginning of OptionParser for some full examples. + # + # +opts+ can include the following elements: + # + # [Argument style:] + # One of the following: + # :NONE, :REQUIRED, :OPTIONAL + # + # [Argument pattern:] + # Acceptable option argument format, must be pre-defined with + # OptionParser.accept or OptionParser#accept, or Regexp. This can appear + # once or assigned as String if not present, otherwise causes an + # ArgumentError. Examples: + # Float, Time, Array + # + # [Possible argument values:] + # Hash or Array. + # [:text, :binary, :auto] + # %w[iso-2022-jp shift_jis euc-jp utf8 binary] + # { "jis" => "iso-2022-jp", "sjis" => "shift_jis" } + # + # [Long style switch:] + # Specifies a long style switch which takes a mandatory, optional or no + # argument. It's a string of the following form: + # "--switch=MANDATORY" or "--switch MANDATORY" + # "--switch[=OPTIONAL]" + # "--switch" + # + # [Short style switch:] + # Specifies short style switch which takes a mandatory, optional or no + # argument. It's a string of the following form: + # "-xMANDATORY" + # "-x[OPTIONAL]" + # "-x" + # There is also a special form which matches character range (not full + # set of regural expression): + # "-[a-z]MANDATORY" + # "-[a-z][OPTIONAL]" + # "-[a-z]" + # + # [Argument style and description:] + # Instead of specifying mandatory or optional orguments directly in the + # switch parameter, this separate parameter can be used. + # "=MANDATORY" + # "=[OPTIONAL]" + # + # [Description:] + # Description string for the option. + # "Run verbosely" + # + # [Handler:] + # Handler for the parsed argument value. Either give a block or pass a + # Proc or Method as an argument. + # + def make_switch(opts, block = nil) + short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], [] + ldesc, sdesc, desc, arg = [], [], [] + default_style = Switch::NoArgument + default_pattern = nil + klass = nil + o = nil + n, q, a = nil + + opts.each do |o| + # argument class + next if search(:atype, o) do |pat, c| + klass = notwice(o, klass, 'type') + if not_style and not_style != Switch::NoArgument + not_pattern, not_conv = pat, c + else + default_pattern, conv = pat, c + end + end + + # directly specified pattern(any object possible to match) + if !(String === o) and o.respond_to?(:match) + pattern = notwice(o, pattern, 'pattern') + conv = (pattern.method(:convert).to_proc if pattern.respond_to?(:convert)) + next + end + + # anything others + case o + when Proc, Method + block = notwice(o, block, 'block') + when Array, Hash + case pattern + when CompletingHash + when nil + pattern = CompletingHash.new + conv = (pattern.method(:convert).to_proc if pattern.respond_to?(:convert)) + else + raise ArgumentError, "argument pattern given twice" + end + o.each {|(o, *v)| pattern[o] = v.fetch(0) {o}} + when Module + raise ArgumentError, "unsupported argument type: #{o}" + when *ArgumentStyle.keys + style = notwice(ArgumentStyle[o], style, 'style') + when /^--no-([^\[\]=\s]*)(.+)?/ + q, a = $1, $2 + o = notwice(a ? Object : TrueClass, klass, 'type') + not_pattern, not_conv = search(:atype, o) unless not_style + not_style = (not_style || default_style).guess(arg = a) if a + default_style = Switch::NoArgument + default_pattern, conv = search(:atype, FalseClass) unless default_pattern + ldesc << "--no-#{q}" + long << 'no-' + (q = q.downcase) + nolong << q + when /^--\[no-\]([^\[\]=\s]*)(.+)?/ + q, a = $1, $2 + o = notwice(a ? Object : TrueClass, klass, 'type') + if a + default_style = default_style.guess(arg = a) + default_pattern, conv = search(:atype, o) unless default_pattern + end + ldesc << "--[no-]#{q}" + long << (o = q.downcase) + not_pattern, not_conv = search(:atype, FalseClass) unless not_style + not_style = Switch::NoArgument + nolong << 'no-' + o + when /^--([^\[\]=\s]*)(.+)?/ + q, a = $1, $2 + if a + o = notwice(NilClass, klass, 'type') + default_style = default_style.guess(arg = a) + default_pattern, conv = search(:atype, o) unless default_pattern + end + ldesc << "--#{q}" + long << (o = q.downcase) + when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/ + q, a = $1, $2 + o = notwice(Object, klass, 'type') + if a + default_style = default_style.guess(arg = a) + default_pattern, conv = search(:atype, o) unless default_pattern + end + sdesc << "-#{q}" + short << Regexp.new(q) + when /^-(.)(.+)?/ + q, a = $1, $2 + if a + o = notwice(NilClass, klass, 'type') + default_style = default_style.guess(arg = a) + default_pattern, conv = search(:atype, o) unless default_pattern + end + sdesc << "-#{q}" + short << q + when /^=/ + style = notwice(default_style.guess(arg = o), style, 'style') + default_pattern, conv = search(:atype, Object) unless default_pattern + else + desc.push(o) + end + end + + default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern + if !(short.empty? and long.empty?) + s = (style || default_style).new(pattern || default_pattern, + conv, sdesc, ldesc, arg, desc, &block) + elsif !block + raise ArgumentError, "no switch given" if style or pattern + s = desc + else + short << pattern + s = (style || default_style).new(pattern, + conv, nil, nil, arg, desc, block) + end + return s, short, long, + (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style), + nolong + end + + def define(*opts, &block) + top.append(*(sw = make_switch(opts, block))) + sw[0] + end + + # + # Add option switch and handler. See #make_switch for an explanation of + # parameters. + # + def on(*opts, &block) + define(*opts, &block) + self + end + alias def_option define + + def define_head(*opts, &block) + top.prepend(*(sw = make_switch(opts, block))) + sw[0] + end + + # + # Add option switch like with #on, but at head of summary. + # + def on_head(*opts, &block) + define_head(*opts, &block) + self + end + alias def_head_option define_head + + def define_tail(*opts, &block) + base.append(*(sw = make_switch(opts, block))) + sw[0] + end + + # + # Add option switch like with #on, but at tail of summary. + # + def on_tail(*opts, &block) + define_tail(*opts, &block) + self + end + alias def_tail_option define_tail + + # + # Add separator in summary. + # + def separator(string) + top.append(string, nil, nil) + end + + # + # Parses command line arguments +argv+ in order. When a block is given, + # each non-option argument is yielded. + # + # Returns the rest of +argv+ left unparsed. + # + def order(*argv, &block) + argv = argv[0].dup if argv.size == 1 and Array === argv[0] + order!(argv, &block) + end + + # + # Same as #order, but removes switches destructively. + # + def order!(argv = default_argv, &nonopt) + parse_in_order(argv, &nonopt) + end + + def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc: + opt, arg, sw, val, rest = nil + nonopt ||= proc {|arg| throw :terminate, arg} + argv.unshift(arg) if arg = catch(:terminate) { + while arg = argv.shift + case arg + # long option + when /\A--([^=]*)(?:=(.*))?/nm + opt, rest = $1, $2 + begin + sw, = complete(:long, opt, true) + rescue ParseError + raise $!.set_option(arg, true) + end + begin + opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)} + val = cb.call(val) if cb + setter.call(sw.switch_name, val) if setter + rescue ParseError + raise $!.set_option(arg, rest) + end + + # short option + when /\A-(.)((=).*|.+)?/nm + opt, has_arg, eq, val, rest = $1, $3, $3, $2, $2 + begin + sw, = search(:short, opt) + unless sw + begin + sw, = complete(:short, opt) + # short option matched. + val = arg.sub(/\A-/, '') + has_arg = true + rescue InvalidOption + # if no short options match, try completion with long + # options. + sw, = complete(:long, opt) + eq ||= !rest + end + end + rescue ParseError + raise $!.set_option(arg, true) + end + begin + opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq} + raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}" + argv.unshift(opt) if opt and (opt = opt.sub(/\A-*/, '-')) != '-' + val = cb.call(val) if cb + setter.call(sw.switch_name, val) if setter + rescue ParseError + raise $!.set_option(arg, arg.length > 2) + end + + # non-option argument + else + catch(:prune) do + visit(:each_option) do |sw| + sw.block.call(arg) if Switch === sw and sw.match_nonswitch?(arg) + end + nonopt.call(arg) + end + end + end + + nil + } + + visit(:search, :short, nil) {|sw| sw.block.call(*argv) if !sw.pattern} + + argv + end + private :parse_in_order + + # + # Parses command line arguments +argv+ in permutation mode and returns + # list of non-option arguments. + # + def permute(*argv) + argv = argv[0].dup if argv.size == 1 and Array === argv[0] + permute!(argv) + end + + # + # Same as #permute, but removes switches destructively. + # + def permute!(argv = default_argv) + nonopts = [] + arg = nil + order!(argv) {|arg| nonopts << arg} + argv[0, 0] = nonopts + argv + end + + # + # Parses command line arguments +argv+ in order when environment variable + # POSIXLY_CORRECT is set, and in permutation mode otherwise. + # + def parse(*argv) + argv = argv[0].dup if argv.size == 1 and Array === argv[0] + parse!(argv) + end + + # + # Same as #parse, but removes switches destructively. + # + def parse!(argv = default_argv) + if ENV.include?('POSIXLY_CORRECT') + order!(argv) + else + permute!(argv) + end + end + + # + # Wrapper method for getopts.rb. + # + # params = ARGV.getopts("ab:", "foo", "bar:") + # # params[:a] = true # -a + # # params[:b] = "1" # -b1 + # # params[:foo] = "1" # --foo + # # params[:bar] = "x" # --bar x + # + def getopts(*args) + argv = Array === args.first ? args.shift : default_argv + single_options, *long_options = *args + + result = {} + + single_options.scan(/(.)(:)?/) do |opt, val| + if val + result[opt] = nil + define("-#{opt} VAL") + else + result[opt] = false + define("-#{opt}") + end + end if single_options + + long_options.each do |arg| + opt, val = arg.split(':', 2) + if val + result[opt] = val.empty? ? nil : val + define("--#{opt} VAL") + else + result[opt] = false + define("--#{opt}") + end + end + + parse_in_order(argv, result.method(:[]=)) + result + end + + # + # See #getopts. + # + def self.getopts(*args) + new.getopts(*args) + end + + # + # Traverses @stack, sending each element method +id+ with +args+ and + # +block+. + # + def visit(id, *args, &block) + el = nil + @stack.reverse_each do |el| + el.send(id, *args, &block) + end + nil + end + private :visit + + # + # Searches +key+ in @stack for +id+ hash and returns or yields the result. + # + def search(id, key) + block_given = block_given? + visit(:search, id, key) do |k| + return block_given ? yield(k) : k + end + end + private :search + + # + # Completes shortened long style option switch and returns pair of + # canonical switch and switch descriptor OptionParser::Switch. + # + # +id+:: Searching table. + # +opt+:: Searching key. + # +icase+:: Search case insensitive if true. + # +pat+:: Optional pattern for completion. + # + def complete(typ, opt, icase = false, *pat) + if pat.empty? + search(typ, opt) {|sw| return [sw, opt]} # exact match or... + end + raise AmbiguousOption, catch(:ambiguous) { + visit(:complete, typ, opt, icase, *pat) {|opt, *sw| return sw} + raise InvalidOption, opt + } + end + private :complete + + # + # Loads options from file names as +filename+. Does nothing when the file + # is not present. Returns whether successfully loaded. + # + # +filename+ defaults to basename of the program without suffix in a + # directory ~/.options. + # + def load(filename = nil) + begin + filename ||= File.expand_path(File.basename($0, '.*'), '~/.options') + rescue + return false + end + begin + parse(*IO.readlines(filename).each {|s| s.chomp!}) + true + rescue Errno::ENOENT, Errno::ENOTDIR + false + end + end + + # + # Parses environment variable +env+ or its uppercase with splitting like a + # shell. + # + # +env+ defaults to the basename of the program. + # + def environment(env = File.basename($0, '.*')) + env = ENV[env] || ENV[env.upcase] or return + parse(*Shellwords.shellwords(env)) + end + + # + # Acceptable argument classes + # + + # + # Any string and no conversion. This is fall-back. + # + accept(Object) {|s,|s or s.nil?} + + accept(NilClass) {|s,|s} + + # + # Any non-empty string, and no conversion. + # + accept(String, /.+/nm) {|s,*|s} + + # + # Ruby/C-like integer, octal for 0-7 sequence, binary for 0b, hexadecimal + # for 0x, and decimal for others; with optional sign prefix. Converts to + # Integer. + # + decimal = '\d+(?:_\d+)*' + binary = 'b[01]+(?:_[01]+)*' + hex = 'x[\da-f]+(?:_[\da-f]+)*' + octal = "0(?:[0-7]*(?:_[0-7]+)*|#{binary}|#{hex})" + integer = "#{octal}|#{decimal}" + accept(Integer, %r"\A[-+]?(?:#{integer})"io) {|s,| Integer(s) if s} + + # + # Float number format, and converts to Float. + # + float = "(?:#{decimal}(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?" + floatpat = %r"\A[-+]?#{float}"io + accept(Float, floatpat) {|s,| s.to_f if s} + + # + # Generic numeric format, converts to Integer for integer format, Float + # for float format. + # + accept(Numeric, %r"\A[-+]?(?:#{octal}|#{float})"io) {|s,| eval(s) if s} + + # + # Decimal integer format, to be converted to Integer. + # + DecimalInteger = /\A[-+]?#{decimal}/io + accept(DecimalInteger) {|s,| s.to_i if s} + + # + # Ruby/C like octal/hexadecimal/binary integer format, to be converted to + # Integer. + # + OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))/io + accept(OctalInteger) {|s,| s.oct if s} + + # + # Decimal integer/float number format, to be converted to Integer for + # integer format, Float for float format. + # + DecimalNumeric = floatpat # decimal integer is allowed as float also. + accept(DecimalNumeric) {|s,| eval(s) if s} + + # + # Boolean switch, which means whether it is present or not, whether it is + # absent or not with prefix no-, or it takes an argument + # yes/no/true/false/+/-. + # + yesno = CompletingHash.new + %w[- no false].each {|el| yesno[el] = false} + %w[+ yes true].each {|el| yesno[el] = true} + yesno['nil'] = false # shoud be nil? + accept(TrueClass, yesno) {|arg, val| val == nil or val} + # + # Similar to TrueClass, but defaults to false. + # + accept(FalseClass, yesno) {|arg, val| val != nil and val} + + # + # List of strings separated by ",". + # + accept(Array) do |s,| + if s + s = s.split(',').collect {|s| s unless s.empty?} + end + s + end + + # + # Regular expression with options. + # + accept(Regexp, %r"\A/((?:\\.|[^\\])*)/([[:alpha:]]+)?\z|.*") do |all, s, o| + f = 0 + if o + f |= Regexp::IGNORECASE if /i/ =~ o + f |= Regexp::MULTILINE if /m/ =~ o + f |= Regexp::EXTENDED if /x/ =~ o + k = o.delete("^imx") + end + Regexp.new(s || all, f, k) + end + + # + # Exceptions + # + + # + # Base class of exceptions from OptionParser. + # + class ParseError < RuntimeError + # Reason which caused the error. + Reason = 'parse error'.freeze + + def initialize(*args) + @args = args + @reason = nil + end + + attr_reader :args + attr_writer :reason + + # + # Pushes back erred argument(s) to +argv+. + # + def recover(argv) + argv[0, 0] = @args + argv + end + + def set_option(opt, eq) + if eq + @args[0] = opt + else + @args.unshift(opt) + end + self + end + + # + # Returns error reason. Override this for I18N. + # + def reason + @reason || self.class::Reason + end + + def inspect + "#<#{self.class.to_s}: #{args.join(' ')}>" + end + + # + # Default stringizing method to emit standard error message. + # + def message + reason + ': ' + args.join(' ') + end + + alias to_s message + end + + # + # Raises when ambiguously completable string is encountered. + # + class AmbiguousOption < ParseError + const_set(:Reason, 'ambiguous option'.freeze) + end + + # + # Raises when there is an argument for a switch which takes no argument. + # + class NeedlessArgument < ParseError + const_set(:Reason, 'needless argument'.freeze) + end + + # + # Raises when a switch with mandatory argument has no argument. + # + class MissingArgument < ParseError + const_set(:Reason, 'missing argument'.freeze) + end + + # + # Raises when switch is undefined. + # + class InvalidOption < ParseError + const_set(:Reason, 'invalid option'.freeze) + end + + # + # Raises when the given argument does not match required format. + # + class InvalidArgument < ParseError + const_set(:Reason, 'invalid argument'.freeze) + end + + # + # Raises when the given argument word can't be completed uniquely. + # + class AmbiguousArgument < InvalidArgument + const_set(:Reason, 'ambiguous argument'.freeze) + end + + # + # Miscellaneous + # + + # + # Extends command line arguments array (ARGV) to parse itself. + # + module Arguable + + # + # Sets OptionParser object, when +opt+ is +false+ or +nil+, methods + # OptionParser::Arguable#options and OptionParser::Arguable#options= are + # undefined. Thus, there is no ways to access the OptionParser object + # via the receiver object. + # + def options=(opt) + unless @optparse = opt + class << self + undef_method(:options) + undef_method(:options=) + end + end + end + + # + # Actual OptionParser object, automatically created if nonexistent. + # + # If called with a block, yields the OptionParser object and returns the + # result of the block. If an OptionParser::ParseError exception occurs + # in the block, it is rescued, a error message printed to STDERR and + # +nil+ returned. + # + def options + @optparse ||= OptionParser.new + @optparse.default_argv = self + block_given? or return @optparse + begin + yield @optparse + rescue ParseError + @optparse.warn $! + nil + end + end + + # + # Parses +self+ destructively in order and returns +self+ containing the + # rest arguments left unparsed. + # + def order!(&blk) options.order!(self, &blk) end + + # + # Parses +self+ destructively in permutation mode and returns +self+ + # containing the rest arguments left unparsed. + # + def permute!() options.permute!(self) end + + # + # Parses +self+ destructively and returns +self+ containing the + # rest arguments left unparsed. + # + def parse!() options.parse!(self) end + + # + # Substitution of getopts is possible as follows. Also see + # OptionParser#getopts. + # + # def getopts(*args) + # ($OPT = ARGV.getopts(*args)).each do |opt, val| + # eval "$OPT_#{opt.gsub(/[^A-Za-z0-9_]/, '_')} = val" + # end + # rescue OptionParser::ParseError + # end + # + def getopts(*args) + options.getopts(self, *args) + end + + # + # Initializes instance variable. + # + def self.extend_object(obj) + super + obj.instance_eval {@optparse = nil} + end + def initialize(*args) + super + @optparse = nil + end + end + + # + # Acceptable argument classes. Now contains DecimalInteger, OctalInteger + # and DecimalNumeric. See Acceptable argument classes (in source code). + # + module Acceptables + const_set(:DecimalInteger, OptionParser::DecimalInteger) + const_set(:OctalInteger, OptionParser::OctalInteger) + const_set(:DecimalNumeric, OptionParser::DecimalNumeric) + end +end + +# ARGV is arguable by OptionParser +#ARGV.extend(OptionParser::Arguable) +class << ARGV + include OptionParser::Arguable +end + +if $0 == __FILE__ + Version = OptionParser::Version + ARGV.options {|q| + q.parse!.empty? or puts "what's #{ARGV.join(' ')}?" + } or abort(ARGV.options.to_s) +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/date.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/date.rb new file mode 100644 index 0000000000..d680559f37 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/date.rb @@ -0,0 +1,17 @@ +require 'optparse' +require 'date' + +OptionParser.accept(DateTime) do |s,| + begin + DateTime.parse(s) if s + rescue ArgumentError + raise OptionParser::InvalidArgument, s + end +end +OptionParser.accept(Date) do |s,| + begin + Date.parse(s) if s + rescue ArgumentError + raise OptionParser::InvalidArgument, s + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/shellwords.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/shellwords.rb new file mode 100644 index 0000000000..0422d7c887 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/shellwords.rb @@ -0,0 +1,6 @@ +# -*- ruby -*- + +require 'shellwords' +require 'optparse' + +OptionParser.accept(Shellwords) {|s,| Shellwords.shellwords(s)} diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/time.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/time.rb new file mode 100644 index 0000000000..402cadcf16 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/time.rb @@ -0,0 +1,10 @@ +require 'optparse' +require 'time' + +OptionParser.accept(Time) do |s,| + begin + (Time.httpdate(s) rescue Time.parse(s)) if s + rescue + raise OptionParser::InvalidArgument, s + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/uri.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/uri.rb new file mode 100644 index 0000000000..024dc69eac --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/uri.rb @@ -0,0 +1,6 @@ +# -*- ruby -*- + +require 'optparse' +require 'uri' + +OptionParser.accept(URI) {|s,| URI.parse(s) if s} diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/version.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/version.rb new file mode 100644 index 0000000000..558d9d710b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/optparse/version.rb @@ -0,0 +1,70 @@ +# OptionParser internal utility + +class << OptionParser + def show_version(*pkg) + progname = ARGV.options.program_name + result = false + show = proc do |klass, cname, version| + str = "#{progname}" + unless klass == ::Object and cname == :VERSION + version = version.join(".") if Array === version + str << ": #{klass}" unless klass == Object + str << " version #{version}" + end + [:Release, :RELEASE].find do |rel| + if klass.const_defined?(rel) + str << " (#{klass.const_get(rel)})" + end + end + puts str + result = true + end + if pkg.size == 1 and pkg[0] == "all" + self.search_const(::Object, /\AV(?:ERSION|ersion)\z/) do |klass, cname, version| + unless cname[1] == ?e and klass.const_defined?(:Version) + show.call(klass, cname.intern, version) + end + end + else + pkg.each do |pkg| + begin + pkg = pkg.split(/::|\//).inject(::Object) {|m, c| m.const_get(c)} + v = case + when pkg.const_defined?(:Version) + pkg.const_get(n = :Version) + when pkg.const_defined?(:VERSION) + pkg.const_get(n = :VERSION) + else + n = nil + "unknown" + end + show.call(pkg, n, v) + rescue NameError + end + end + end + result + end + + def each_const(path, klass = ::Object) + path.split(/::|\//).inject(klass) do |klass, name| + raise NameError, path unless Module === klass + klass.constants.grep(/#{name}/i) do |c| + klass.const_defined?(c) or next + c = klass.const_get(c) + end + end + end + + def search_const(klass, name) + klasses = [klass] + while klass = klasses.shift + klass.constants.each do |cname| + klass.const_defined?(cname) or next + const = klass.const_get(cname) + yield klass, cname, const if name === cname + klasses << const if Module === const and const != ::Object + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/ostruct.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/ostruct.rb new file mode 100644 index 0000000000..6af5bbdac0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/ostruct.rb @@ -0,0 +1,146 @@ +# +# = ostruct.rb: OpenStruct implementation +# +# Author:: Yukihiro Matsumoto +# Documentation:: Gavin Sinclair +# +# OpenStruct allows the creation of data objects with arbitrary attributes. +# See OpenStruct for an example. +# + +# +# OpenStruct allows you to create data objects and set arbitrary attributes. +# For example: +# +# require 'ostruct' +# +# record = OpenStruct.new +# record.name = "John Smith" +# record.age = 70 +# record.pension = 300 +# +# puts record.name # -> "John Smith" +# puts record.address # -> nil +# +# It is like a hash with a different way to access the data. In fact, it is +# implemented with a hash, and you can initialize it with one. +# +# hash = { "country" => "Australia", :population => 20_000_000 } +# data = OpenStruct.new(hash) +# +# p data # -> +# +class OpenStruct + # + # Create a new OpenStruct object. The optional +hash+, if given, will + # generate attributes and values. For example. + # + # require 'ostruct' + # hash = { "country" => "Australia", :population => 20_000_000 } + # data = OpenStruct.new(hash) + # + # p data # -> + # + # By default, the resulting OpenStruct object will have no attributes. + # + def initialize(hash=nil) + @table = {} + if hash + for k,v in hash + @table[k.to_sym] = v + new_ostruct_member(k) + end + end + end + + # Duplicate an OpenStruct object members. + def initialize_copy(orig) + super + @table = @table.dup + end + + def marshal_dump + @table + end + def marshal_load(x) + @table = x + @table.each_key{|key| new_ostruct_member(key)} + end + + def new_ostruct_member(name) + name = name.to_sym + unless self.respond_to?(name) + meta = class << self; self; end + meta.send(:define_method, name) { @table[name] } + meta.send(:define_method, :"#{name}=") { |x| @table[name] = x } + end + end + + def method_missing(mid, *args) # :nodoc: + mname = mid.id2name + len = args.length + if mname =~ /=$/ + if len != 1 + raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1) + end + if self.frozen? + raise TypeError, "can't modify frozen #{self.class}", caller(1) + end + mname.chop! + self.new_ostruct_member(mname) + @table[mname.intern] = args[0] + elsif len == 0 + @table[mid] + else + raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1) + end + end + + # + # Remove the named field from the object. + # + def delete_field(name) + @table.delete name.to_sym + end + + InspectKey = :__inspect_key__ # :nodoc: + + # + # Returns a string containing a detailed summary of the keys and values. + # + def inspect + str = "#<#{self.class}" + + Thread.current[InspectKey] ||= [] + if Thread.current[InspectKey].include?(self) then + str << " ..." + else + first = true + for k,v in @table + str << "," unless first + first = false + + Thread.current[InspectKey] << v + begin + str << " #{k}=#{v.inspect}" + ensure + Thread.current[InspectKey].pop + end + end + end + + str << ">" + end + alias :to_s :inspect + + attr_reader :table # :nodoc: + protected :table + + # + # Compare this object and +other+ for equality. + # + def ==(other) + return false unless(other.kind_of?(OpenStruct)) + return @table == other.table + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/parsearg.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/parsearg.rb new file mode 100644 index 0000000000..9c4d0f1d88 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/parsearg.rb @@ -0,0 +1,87 @@ +# +# parsearg.rb - parse arguments +# $Release Version: $ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Yasuo OHBA(SHL Japan Inc. Technology Dept.) +# +# -- +# +# +# + +warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: parsearg is deprecated after Ruby 1.8.1; use optparse instead" + +$RCS_ID=%q$Header$ + +require "getopts" + +def printUsageAndExit() + if $USAGE + eval($USAGE) + end + exit() +end + +def setParenthesis(ex, opt, c) + if opt != "" + ex = sprintf("%s$OPT_%s%s", ex, opt, c) + else + ex = sprintf("%s%s", ex, c) + end + return ex +end + +def setOrAnd(ex, opt, c) + if opt != "" + ex = sprintf("%s$OPT_%s %s%s ", ex, opt, c, c) + else + ex = sprintf("%s %s%s ", ex, c, c) + end + return ex +end + +def setExpression(ex, opt, op) + if !op + ex = sprintf("%s$OPT_%s", ex, opt) + return ex + end + case op.chr + when "(", ")" + ex = setParenthesis(ex, opt, op.chr) + when "|", "&" + ex = setOrAnd(ex, opt, op.chr) + else + return nil + end + return ex +end + +# parseArgs is obsolete. Use OptionParser instead. + +def parseArgs(argc, nopt, single_opts, *opts) + if (noOptions = getopts(single_opts, *opts)) == nil + printUsageAndExit() + end + if nopt + ex = nil + pos = 0 + for o in nopt.split(/[()|&]/) + pos += o.length + ex = setExpression(ex, o, nopt[pos]) + pos += 1 + end + begin + if !eval(ex) + printUsageAndExit() + end + rescue + print "Format Error!! : \"" + nopt + "\"\t[parseArgs]\n" + exit!(-1) + end + end + if ARGV.length < argc + printUsageAndExit() + end + return noOptions +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/parsedate.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/parsedate.rb new file mode 100644 index 0000000000..b52a79ba47 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/parsedate.rb @@ -0,0 +1,53 @@ +# +# = parsedate.rb: Parses dates +# +# Author:: Tadayoshi Funaba +# Documentation:: Konrad Meyer +# +# ParseDate munches on a date and turns it into an array of values. +# + +# +# ParseDate converts a date into an array of values. +# For example: +# +# require 'parsedate' +# +# ParseDate.parsedate "Tuesday, July 6th, 2007, 18:35:20 UTC" +# # => [2007, 7, 6, 18, 35, 20, "UTC", 2] +# +# The order is of the form [year, month, day of month, hour, minute, second, +# timezone, day of the week]. + +require 'date/format' + +module ParseDate + # + # Parse a string representation of a date into values. + # For example: + # + # require 'parsedate' + # + # ParseDate.parsedate "Tuesday, July 5th, 2007, 18:35:20 UTC" + # # => [2007, 7, 5, 18, 35, 20, "UTC", 2] + # + # The order is of the form [year, month, day of month, hour, minute, + # second, timezone, day of week]. + # + # ParseDate.parsedate can also take a second argument, +comp+, which + # is a boolean telling the method to compensate for dates with years + # expressed as two digits. Example: + # + # require 'parsedate' + # + # ParseDate.parsedate "Mon Dec 25 00 06:53:24 UTC", true + # # => [2000, 12, 25, 6, 53, 24, "UTC", 1] + # + def parsedate(str, comp=false) + Date._parse(str, comp). + values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :wday) + end + + module_function :parsedate + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/pathname.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/pathname.rb new file mode 100644 index 0000000000..e4ca5489ce --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/pathname.rb @@ -0,0 +1,1062 @@ +# +# = pathname.rb +# +# Object-Oriented Pathname Class +# +# Author:: Tanaka Akira +# Documentation:: Author and Gavin Sinclair +# +# For documentation, see class Pathname. +# +# pathname.rb is distributed with Ruby since 1.8.0. +# + +# +# == Pathname +# +# Pathname represents a pathname which locates a file in a filesystem. +# The pathname depends on OS: Unix, Windows, etc. +# Pathname library works with pathnames of local OS. +# However non-Unix pathnames are supported experimentally. +# +# It does not represent the file itself. +# A Pathname can be relative or absolute. It's not until you try to +# reference the file that it even matters whether the file exists or not. +# +# Pathname is immutable. It has no method for destructive update. +# +# The value of this class is to manipulate file path information in a neater +# way than standard Ruby provides. The examples below demonstrate the +# difference. *All* functionality from File, FileTest, and some from Dir and +# FileUtils is included, in an unsurprising way. It is essentially a facade for +# all of these, and more. +# +# == Examples +# +# === Example 1: Using Pathname +# +# require 'pathname' +# p = Pathname.new("/usr/bin/ruby") +# size = p.size # 27662 +# isdir = p.directory? # false +# dir = p.dirname # Pathname:/usr/bin +# base = p.basename # Pathname:ruby +# dir, base = p.split # [Pathname:/usr/bin, Pathname:ruby] +# data = p.read +# p.open { |f| _ } +# p.each_line { |line| _ } +# +# === Example 2: Using standard Ruby +# +# p = "/usr/bin/ruby" +# size = File.size(p) # 27662 +# isdir = File.directory?(p) # false +# dir = File.dirname(p) # "/usr/bin" +# base = File.basename(p) # "ruby" +# dir, base = File.split(p) # ["/usr/bin", "ruby"] +# data = File.read(p) +# File.open(p) { |f| _ } +# File.foreach(p) { |line| _ } +# +# === Example 3: Special features +# +# p1 = Pathname.new("/usr/lib") # Pathname:/usr/lib +# p2 = p1 + "ruby/1.8" # Pathname:/usr/lib/ruby/1.8 +# p3 = p1.parent # Pathname:/usr +# p4 = p2.relative_path_from(p3) # Pathname:lib/ruby/1.8 +# pwd = Pathname.pwd # Pathname:/home/gavin +# pwd.absolute? # true +# p5 = Pathname.new "." # Pathname:. +# p5 = p5 + "music/../articles" # Pathname:music/../articles +# p5.cleanpath # Pathname:articles +# p5.realpath # Pathname:/home/gavin/articles +# p5.children # [Pathname:/home/gavin/articles/linux, ...] +# +# == Breakdown of functionality +# +# === Core methods +# +# These methods are effectively manipulating a String, because that's all a path +# is. Except for #mountpoint?, #children, and #realpath, they don't access the +# filesystem. +# +# - + +# - #join +# - #parent +# - #root? +# - #absolute? +# - #relative? +# - #relative_path_from +# - #each_filename +# - #cleanpath +# - #realpath +# - #children +# - #mountpoint? +# +# === File status predicate methods +# +# These methods are a facade for FileTest: +# - #blockdev? +# - #chardev? +# - #directory? +# - #executable? +# - #executable_real? +# - #exist? +# - #file? +# - #grpowned? +# - #owned? +# - #pipe? +# - #readable? +# - #world_readable? +# - #readable_real? +# - #setgid? +# - #setuid? +# - #size +# - #size? +# - #socket? +# - #sticky? +# - #symlink? +# - #writable? +# - #world_writable? +# - #writable_real? +# - #zero? +# +# === File property and manipulation methods +# +# These methods are a facade for File: +# - #atime +# - #ctime +# - #mtime +# - #chmod(mode) +# - #lchmod(mode) +# - #chown(owner, group) +# - #lchown(owner, group) +# - #fnmatch(pattern, *args) +# - #fnmatch?(pattern, *args) +# - #ftype +# - #make_link(old) +# - #open(*args, &block) +# - #readlink +# - #rename(to) +# - #stat +# - #lstat +# - #make_symlink(old) +# - #truncate(length) +# - #utime(atime, mtime) +# - #basename(*args) +# - #dirname +# - #extname +# - #expand_path(*args) +# - #split +# +# === Directory methods +# +# These methods are a facade for Dir: +# - Pathname.glob(*args) +# - Pathname.getwd / Pathname.pwd +# - #rmdir +# - #entries +# - #each_entry(&block) +# - #mkdir(*args) +# - #opendir(*args) +# +# === IO +# +# These methods are a facade for IO: +# - #each_line(*args, &block) +# - #read(*args) +# - #readlines(*args) +# - #sysopen(*args) +# +# === Utilities +# +# These methods are a mixture of Find, FileUtils, and others: +# - #find(&block) +# - #mkpath +# - #rmtree +# - #unlink / #delete +# +# +# == Method documentation +# +# As the above section shows, most of the methods in Pathname are facades. The +# documentation for these methods generally just says, for instance, "See +# FileTest.writable?", as you should be familiar with the original method +# anyway, and its documentation (e.g. through +ri+) will contain more +# information. In some cases, a brief description will follow. +# +class Pathname + + # :stopdoc: + if RUBY_VERSION < "1.9" + TO_PATH = :to_str + else + # to_path is implemented so Pathname objects are usable with File.open, etc. + TO_PATH = :to_path + end + # :startdoc: + + # + # Create a Pathname object from the given String (or String-like object). + # If +path+ contains a NUL character (\0), an ArgumentError is raised. + # + def initialize(path) + path = path.__send__(TO_PATH) if path.respond_to? TO_PATH + @path = path.dup + + if /\0/ =~ @path + raise ArgumentError, "pathname contains \\0: #{@path.inspect}" + end + + self.taint if @path.tainted? + end + + def freeze() super; @path.freeze; self end + def taint() super; @path.taint; self end + def untaint() super; @path.untaint; self end + + # + # Compare this pathname with +other+. The comparison is string-based. + # Be aware that two different paths (foo.txt and ./foo.txt) + # can refer to the same file. + # + def ==(other) + return false unless Pathname === other + other.to_s == @path + end + alias === == + alias eql? == + + # Provides for comparing pathnames, case-sensitively. + def <=>(other) + return nil unless Pathname === other + @path.tr('/', "\0") <=> other.to_s.tr('/', "\0") + end + + def hash # :nodoc: + @path.hash + end + + # Return the path as a String. + def to_s + @path.dup + end + + # to_path is implemented so Pathname objects are usable with File.open, etc. + alias_method TO_PATH, :to_s + + def inspect # :nodoc: + "#<#{self.class}:#{@path}>" + end + + # Return a pathname which is substituted by String#sub. + def sub(pattern, *rest, &block) + self.class.new(@path.sub(pattern, *rest, &block)) + end + + if File::ALT_SEPARATOR + SEPARATOR_PAT = /[#{Regexp.quote File::ALT_SEPARATOR}#{Regexp.quote File::SEPARATOR}]/ + else + SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/ + end + + # chop_basename(path) -> [pre-basename, basename] or nil + def chop_basename(path) + base = File.basename(path) + if /\A#{SEPARATOR_PAT}?\z/ =~ base + return nil + else + return path[0, path.rindex(base)], base + end + end + private :chop_basename + + # split_names(path) -> prefix, [name, ...] + def split_names(path) + names = [] + while r = chop_basename(path) + path, basename = r + names.unshift basename + end + return path, names + end + private :split_names + + def prepend_prefix(prefix, relpath) + if relpath.empty? + File.dirname(prefix) + elsif /#{SEPARATOR_PAT}/ =~ prefix + prefix = File.dirname(prefix) + prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a' + prefix + relpath + else + prefix + relpath + end + end + private :prepend_prefix + + # Returns clean pathname of +self+ with consecutive slashes and useless dots + # removed. The filesystem is not accessed. + # + # If +consider_symlink+ is +true+, then a more conservative algorithm is used + # to avoid breaking symbolic linkages. This may retain more .. + # entries than absolutely necessary, but without accessing the filesystem, + # this can't be avoided. See #realpath. + # + def cleanpath(consider_symlink=false) + if consider_symlink + cleanpath_conservative + else + cleanpath_aggressive + end + end + + # + # Clean the path simply by resolving and removing excess "." and ".." entries. + # Nothing more, nothing less. + # + def cleanpath_aggressive + path = @path + names = [] + pre = path + while r = chop_basename(pre) + pre, base = r + case base + when '.' + when '..' + names.unshift base + else + if names[0] == '..' + names.shift + else + names.unshift base + end + end + end + if /#{SEPARATOR_PAT}/o =~ File.basename(pre) + names.shift while names[0] == '..' + end + self.class.new(prepend_prefix(pre, File.join(*names))) + end + private :cleanpath_aggressive + + # has_trailing_separator?(path) -> bool + def has_trailing_separator?(path) + if r = chop_basename(path) + pre, basename = r + pre.length + basename.length < path.length + else + false + end + end + private :has_trailing_separator? + + # add_trailing_separator(path) -> path + def add_trailing_separator(path) + if File.basename(path + 'a') == 'a' + path + else + File.join(path, "") # xxx: Is File.join is appropriate to add separator? + end + end + private :add_trailing_separator + + def del_trailing_separator(path) + if r = chop_basename(path) + pre, basename = r + pre + basename + elsif /#{SEPARATOR_PAT}+\z/o =~ path + $` + File.dirname(path)[/#{SEPARATOR_PAT}*\z/o] + else + path + end + end + private :del_trailing_separator + + def cleanpath_conservative + path = @path + names = [] + pre = path + while r = chop_basename(pre) + pre, base = r + names.unshift base if base != '.' + end + if /#{SEPARATOR_PAT}/o =~ File.basename(pre) + names.shift while names[0] == '..' + end + if names.empty? + self.class.new(File.dirname(pre)) + else + if names.last != '..' && File.basename(path) == '.' + names << '.' + end + result = prepend_prefix(pre, File.join(*names)) + if /\A(?:\.|\.\.)\z/ !~ names.last && has_trailing_separator?(path) + self.class.new(add_trailing_separator(result)) + else + self.class.new(result) + end + end + end + private :cleanpath_conservative + + def realpath_rec(prefix, unresolved, h) + resolved = [] + until unresolved.empty? + n = unresolved.shift + if n == '.' + next + elsif n == '..' + resolved.pop + else + path = prepend_prefix(prefix, File.join(*(resolved + [n]))) + if h.include? path + if h[path] == :resolving + raise Errno::ELOOP.new(path) + else + prefix, *resolved = h[path] + end + else + s = File.lstat(path) + if s.symlink? + h[path] = :resolving + link_prefix, link_names = split_names(File.readlink(path)) + if link_prefix == '' + prefix, *resolved = h[path] = realpath_rec(prefix, resolved + link_names, h) + else + prefix, *resolved = h[path] = realpath_rec(link_prefix, link_names, h) + end + else + resolved << n + h[path] = [prefix, *resolved] + end + end + end + end + return prefix, *resolved + end + private :realpath_rec + + # + # Returns a real (absolute) pathname of +self+ in the actual filesystem. + # The real pathname doesn't contain symlinks or useless dots. + # + # No arguments should be given; the old behaviour is *obsoleted*. + # + def realpath + path = @path + prefix, names = split_names(path) + if prefix == '' + prefix, names2 = split_names(Dir.pwd) + names = names2 + names + end + prefix, *names = realpath_rec(prefix, names, {}) + self.class.new(prepend_prefix(prefix, File.join(*names))) + end + + # #parent returns the parent directory. + # + # This is same as self + '..'. + def parent + self + '..' + end + + # #mountpoint? returns +true+ if self points to a mountpoint. + def mountpoint? + begin + stat1 = self.lstat + stat2 = self.parent.lstat + stat1.dev == stat2.dev && stat1.ino == stat2.ino || + stat1.dev != stat2.dev + rescue Errno::ENOENT + false + end + end + + # + # #root? is a predicate for root directories. I.e. it returns +true+ if the + # pathname consists of consecutive slashes. + # + # It doesn't access actual filesystem. So it may return +false+ for some + # pathnames which points to roots such as /usr/... + # + def root? + !!(chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o =~ @path) + end + + # Predicate method for testing whether a path is absolute. + # It returns +true+ if the pathname begins with a slash. + def absolute? + !relative? + end + + # The opposite of #absolute? + def relative? + path = @path + while r = chop_basename(path) + path, basename = r + end + path == '' + end + + # + # Iterates over each component of the path. + # + # Pathname.new("/usr/bin/ruby").each_filename {|filename| ... } + # # yields "usr", "bin", and "ruby". + # + def each_filename # :yield: filename + prefix, names = split_names(@path) + names.each {|filename| yield filename } + nil + end + + # Iterates over and yields a new Pathname object + # for each element in the given path in descending order. + # + # Pathname.new('/path/to/some/file.rb').descend {|v| p v} + # # + # # + # # + # # + # # + # + # Pathname.new('path/to/some/file.rb').descend {|v| p v} + # # + # # + # # + # # + # + # It doesn't access actual filesystem. + # + # This method is available since 1.8.5. + # + def descend + vs = [] + ascend {|v| vs << v } + vs.reverse_each {|v| yield v } + nil + end + + # Iterates over and yields a new Pathname object + # for each element in the given path in ascending order. + # + # Pathname.new('/path/to/some/file.rb').ascend {|v| p v} + # # + # # + # # + # # + # # + # + # Pathname.new('path/to/some/file.rb').ascend {|v| p v} + # # + # # + # # + # # + # + # It doesn't access actual filesystem. + # + # This method is available since 1.8.5. + # + def ascend + path = @path + yield self + while r = chop_basename(path) + path, name = r + break if path.empty? + yield self.class.new(del_trailing_separator(path)) + end + end + + # + # Pathname#+ appends a pathname fragment to this one to produce a new Pathname + # object. + # + # p1 = Pathname.new("/usr") # Pathname:/usr + # p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby + # p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd + # + # This method doesn't access the file system; it is pure string manipulation. + # + def +(other) + other = Pathname.new(other) unless Pathname === other + Pathname.new(plus(@path, other.to_s)) + end + + def plus(path1, path2) # -> path + prefix2 = path2 + index_list2 = [] + basename_list2 = [] + while r2 = chop_basename(prefix2) + prefix2, basename2 = r2 + index_list2.unshift prefix2.length + basename_list2.unshift basename2 + end + return path2 if prefix2 != '' + prefix1 = path1 + while true + while !basename_list2.empty? && basename_list2.first == '.' + index_list2.shift + basename_list2.shift + end + break unless r1 = chop_basename(prefix1) + prefix1, basename1 = r1 + next if basename1 == '.' + if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..' + prefix1 = prefix1 + basename1 + break + end + index_list2.shift + basename_list2.shift + end + r1 = chop_basename(prefix1) + if !r1 && /#{SEPARATOR_PAT}/o =~ File.basename(prefix1) + while !basename_list2.empty? && basename_list2.first == '..' + index_list2.shift + basename_list2.shift + end + end + if !basename_list2.empty? + suffix2 = path2[index_list2.first..-1] + r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2 + else + r1 ? prefix1 : File.dirname(prefix1) + end + end + private :plus + + # + # Pathname#join joins pathnames. + # + # path0.join(path1, ..., pathN) is the same as + # path0 + path1 + ... + pathN. + # + def join(*args) + args.unshift self + result = args.pop + result = Pathname.new(result) unless Pathname === result + return result if result.absolute? + args.reverse_each {|arg| + arg = Pathname.new(arg) unless Pathname === arg + result = arg + result + return result if result.absolute? + } + result + end + + # + # Returns the children of the directory (files and subdirectories, not + # recursive) as an array of Pathname objects. By default, the returned + # pathnames will have enough information to access the files. If you set + # +with_directory+ to +false+, then the returned pathnames will contain the + # filename only. + # + # For example: + # p = Pathname("/usr/lib/ruby/1.8") + # p.children + # # -> [ Pathname:/usr/lib/ruby/1.8/English.rb, + # Pathname:/usr/lib/ruby/1.8/Env.rb, + # Pathname:/usr/lib/ruby/1.8/abbrev.rb, ... ] + # p.children(false) + # # -> [ Pathname:English.rb, Pathname:Env.rb, Pathname:abbrev.rb, ... ] + # + # Note that the result never contain the entries . and .. in + # the directory because they are not children. + # + # This method has existed since 1.8.1. + # + def children(with_directory=true) + with_directory = false if @path == '.' + result = [] + Dir.foreach(@path) {|e| + next if e == '.' || e == '..' + if with_directory + result << self.class.new(File.join(@path, e)) + else + result << self.class.new(e) + end + } + result + end + + # + # #relative_path_from returns a relative path from the argument to the + # receiver. If +self+ is absolute, the argument must be absolute too. If + # +self+ is relative, the argument must be relative too. + # + # #relative_path_from doesn't access the filesystem. It assumes no symlinks. + # + # ArgumentError is raised when it cannot find a relative path. + # + # This method has existed since 1.8.1. + # + def relative_path_from(base_directory) + dest_directory = self.cleanpath.to_s + base_directory = base_directory.cleanpath.to_s + dest_prefix = dest_directory + dest_names = [] + while r = chop_basename(dest_prefix) + dest_prefix, basename = r + dest_names.unshift basename if basename != '.' + end + base_prefix = base_directory + base_names = [] + while r = chop_basename(base_prefix) + base_prefix, basename = r + base_names.unshift basename if basename != '.' + end + if dest_prefix != base_prefix + raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}" + end + while !dest_names.empty? && + !base_names.empty? && + dest_names.first == base_names.first + dest_names.shift + base_names.shift + end + if base_names.include? '..' + raise ArgumentError, "base_directory has ..: #{base_directory.inspect}" + end + base_names.fill('..') + relpath_names = base_names + dest_names + if relpath_names.empty? + Pathname.new('.') + else + Pathname.new(File.join(*relpath_names)) + end + end +end + +class Pathname # * IO * + # + # #each_line iterates over the line in the file. It yields a String object + # for each line. + # + # This method has existed since 1.8.1. + # + def each_line(*args, &block) # :yield: line + IO.foreach(@path, *args, &block) + end + + # Pathname#foreachline is *obsoleted* at 1.8.1. Use #each_line. + def foreachline(*args, &block) + warn "Pathname#foreachline is obsoleted. Use Pathname#each_line." + each_line(*args, &block) + end + + # See IO.read. Returns all the bytes from the file, or the first +N+ + # if specified. + def read(*args) IO.read(@path, *args) end + + # See IO.readlines. Returns all the lines from the file. + def readlines(*args) IO.readlines(@path, *args) end + + # See IO.sysopen. + def sysopen(*args) IO.sysopen(@path, *args) end +end + + +class Pathname # * File * + + # See File.atime. Returns last access time. + def atime() File.atime(@path) end + + # See File.ctime. Returns last (directory entry, not file) change time. + def ctime() File.ctime(@path) end + + # See File.mtime. Returns last modification time. + def mtime() File.mtime(@path) end + + # See File.chmod. Changes permissions. + def chmod(mode) File.chmod(mode, @path) end + + # See File.lchmod. + def lchmod(mode) File.lchmod(mode, @path) end + + # See File.chown. Change owner and group of file. + def chown(owner, group) File.chown(owner, group, @path) end + + # See File.lchown. + def lchown(owner, group) File.lchown(owner, group, @path) end + + # See File.fnmatch. Return +true+ if the receiver matches the given + # pattern. + def fnmatch(pattern, *args) File.fnmatch(pattern, @path, *args) end + + # See File.fnmatch? (same as #fnmatch). + def fnmatch?(pattern, *args) File.fnmatch?(pattern, @path, *args) end + + # See File.ftype. Returns "type" of file ("file", "directory", + # etc). + def ftype() File.ftype(@path) end + + # See File.link. Creates a hard link. + def make_link(old) File.link(old, @path) end + + # See File.open. Opens the file for reading or writing. + def open(*args, &block) # :yield: file + File.open(@path, *args, &block) + end + + # See File.readlink. Read symbolic link. + def readlink() self.class.new(File.readlink(@path)) end + + # See File.rename. Rename the file. + def rename(to) File.rename(@path, to) end + + # See File.stat. Returns a File::Stat object. + def stat() File.stat(@path) end + + # See File.lstat. + def lstat() File.lstat(@path) end + + # See File.symlink. Creates a symbolic link. + def make_symlink(old) File.symlink(old, @path) end + + # See File.truncate. Truncate the file to +length+ bytes. + def truncate(length) File.truncate(@path, length) end + + # See File.utime. Update the access and modification times. + def utime(atime, mtime) File.utime(atime, mtime, @path) end + + # See File.basename. Returns the last component of the path. + def basename(*args) self.class.new(File.basename(@path, *args)) end + + # See File.dirname. Returns all but the last component of the path. + def dirname() self.class.new(File.dirname(@path)) end + + # See File.extname. Returns the file's extension. + def extname() File.extname(@path) end + + # See File.expand_path. + def expand_path(*args) self.class.new(File.expand_path(@path, *args)) end + + # See File.split. Returns the #dirname and the #basename in an + # Array. + def split() File.split(@path).map {|f| self.class.new(f) } end + + # Pathname#link is confusing and *obsoleted* because the receiver/argument + # order is inverted to corresponding system call. + def link(old) + warn 'Pathname#link is obsoleted. Use Pathname#make_link.' + File.link(old, @path) + end + + # Pathname#symlink is confusing and *obsoleted* because the receiver/argument + # order is inverted to corresponding system call. + def symlink(old) + warn 'Pathname#symlink is obsoleted. Use Pathname#make_symlink.' + File.symlink(old, @path) + end +end + + +class Pathname # * FileTest * + + # See FileTest.blockdev?. + def blockdev?() FileTest.blockdev?(@path) end + + # See FileTest.chardev?. + def chardev?() FileTest.chardev?(@path) end + + # See FileTest.executable?. + def executable?() FileTest.executable?(@path) end + + # See FileTest.executable_real?. + def executable_real?() FileTest.executable_real?(@path) end + + # See FileTest.exist?. + def exist?() FileTest.exist?(@path) end + + # See FileTest.grpowned?. + def grpowned?() FileTest.grpowned?(@path) end + + # See FileTest.directory?. + def directory?() FileTest.directory?(@path) end + + # See FileTest.file?. + def file?() FileTest.file?(@path) end + + # See FileTest.pipe?. + def pipe?() FileTest.pipe?(@path) end + + # See FileTest.socket?. + def socket?() FileTest.socket?(@path) end + + # See FileTest.owned?. + def owned?() FileTest.owned?(@path) end + + # See FileTest.readable?. + def readable?() FileTest.readable?(@path) end + + # See FileTest.world_readable?. + def world_readable?() FileTest.world_readable?(@path) end + + # See FileTest.readable_real?. + def readable_real?() FileTest.readable_real?(@path) end + + # See FileTest.setuid?. + def setuid?() FileTest.setuid?(@path) end + + # See FileTest.setgid?. + def setgid?() FileTest.setgid?(@path) end + + # See FileTest.size. + def size() FileTest.size(@path) end + + # See FileTest.size?. + def size?() FileTest.size?(@path) end + + # See FileTest.sticky?. + def sticky?() FileTest.sticky?(@path) end + + # See FileTest.symlink?. + def symlink?() FileTest.symlink?(@path) end + + # See FileTest.writable?. + def writable?() FileTest.writable?(@path) end + + # See FileTest.world_writable?. + def world_writable?() FileTest.world_writable?(@path) end + + # See FileTest.writable_real?. + def writable_real?() FileTest.writable_real?(@path) end + + # See FileTest.zero?. + def zero?() FileTest.zero?(@path) end +end + + +class Pathname # * Dir * + # See Dir.glob. Returns or yields Pathname objects. + def Pathname.glob(*args) # :yield: p + if block_given? + Dir.glob(*args) {|f| yield self.new(f) } + else + Dir.glob(*args).map {|f| self.new(f) } + end + end + + # See Dir.getwd. Returns the current working directory as a Pathname. + def Pathname.getwd() self.new(Dir.getwd) end + class << self; alias pwd getwd end + + # Pathname#chdir is *obsoleted* at 1.8.1. + def chdir(&block) + warn "Pathname#chdir is obsoleted. Use Dir.chdir." + Dir.chdir(@path, &block) + end + + # Pathname#chroot is *obsoleted* at 1.8.1. + def chroot + warn "Pathname#chroot is obsoleted. Use Dir.chroot." + Dir.chroot(@path) + end + + # Return the entries (files and subdirectories) in the directory, each as a + # Pathname object. + def entries() Dir.entries(@path).map {|f| self.class.new(f) } end + + # Iterates over the entries (files and subdirectories) in the directory. It + # yields a Pathname object for each entry. + # + # This method has existed since 1.8.1. + def each_entry(&block) # :yield: p + Dir.foreach(@path) {|f| yield self.class.new(f) } + end + + # Pathname#dir_foreach is *obsoleted* at 1.8.1. + def dir_foreach(*args, &block) + warn "Pathname#dir_foreach is obsoleted. Use Pathname#each_entry." + each_entry(*args, &block) + end + + # See Dir.mkdir. Create the referenced directory. + def mkdir(*args) Dir.mkdir(@path, *args) end + + # See Dir.rmdir. Remove the referenced directory. + def rmdir() Dir.rmdir(@path) end + + # See Dir.open. + def opendir(&block) # :yield: dir + Dir.open(@path, &block) + end +end + + +class Pathname # * Find * + # + # Pathname#find is an iterator to traverse a directory tree in a depth first + # manner. It yields a Pathname for each file under "this" directory. + # + # Since it is implemented by find.rb, Find.prune can be used + # to control the traverse. + # + # If +self+ is ., yielded pathnames begin with a filename in the + # current directory, not ./. + # + def find(&block) # :yield: p + require 'find' + if @path == '.' + Find.find(@path) {|f| yield self.class.new(f.sub(%r{\A\./}, '')) } + else + Find.find(@path) {|f| yield self.class.new(f) } + end + end +end + + +class Pathname # * FileUtils * + # See FileUtils.mkpath. Creates a full path, including any + # intermediate directories that don't yet exist. + def mkpath + require 'fileutils' + FileUtils.mkpath(@path) + nil + end + + # See FileUtils.rm_r. Deletes a directory and all beneath it. + def rmtree + # The name "rmtree" is borrowed from File::Path of Perl. + # File::Path provides "mkpath" and "rmtree". + require 'fileutils' + FileUtils.rm_r(@path) + nil + end +end + + +class Pathname # * mixed * + # Removes a file or directory, using File.unlink or + # Dir.unlink as necessary. + def unlink() + begin + Dir.unlink @path + rescue Errno::ENOTDIR + File.unlink @path + end + end + alias delete unlink + + # This method is *obsoleted* at 1.8.1. Use #each_line or #each_entry. + def foreach(*args, &block) + warn "Pathname#foreach is obsoleted. Use each_line or each_entry." + if FileTest.directory? @path + # For polymorphism between Dir.foreach and IO.foreach, + # Pathname#foreach doesn't yield Pathname object. + Dir.foreach(@path, *args, &block) + else + IO.foreach(@path, *args, &block) + end + end +end + +module Kernel + # create a pathname object. + # + # This method is available since 1.8.5. + def Pathname(path) # :doc: + Pathname.new(path) + end + private :Pathname +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/ping.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/ping.rb new file mode 100644 index 0000000000..c2966b619c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/ping.rb @@ -0,0 +1,64 @@ +# +# = ping.rb: Check a host for upness +# +# Author:: Yukihiro Matsumoto +# Documentation:: Konrad Meyer +# +# Performs the function of the basic network testing tool, ping. +# See: Ping. +# + +require 'timeout' +require "socket" + +# +# Ping contains routines to test for the reachability of remote hosts. +# Currently the only routine implemented is pingecho(). +# +# Ping.pingecho uses a TCP echo (not an ICMP echo) to determine if the +# remote host is reachable. This is usually adequate to tell that a remote +# host is available to telnet, ftp, or ssh to. +# +# Warning: Ping.pingecho may block for a long time if DNS resolution is +# slow. Requiring 'resolv-replace' allows non-blocking name resolution. +# +# Usage: +# +# require 'ping' +# +# puts "'jimmy' is alive and kicking" if Ping.pingecho('jimmy', 10) +# +module Ping + + # + # Return true if we can open a connection to the hostname or IP address + # +host+ on port +service+ (which defaults to the "echo" port) waiting up + # to +timeout+ seconds. + # + # Example: + # + # require 'ping' + # + # Ping.pingecho "google.com", 10, 80 + # + def pingecho(host, timeout=5, service="echo") + begin + timeout(timeout) do + s = TCPSocket.new(host, service) + s.close + end + rescue Errno::ECONNREFUSED + return true + rescue Timeout::Error, StandardError + return false + end + return true + end + module_function :pingecho +end + +if $0 == __FILE__ + host = ARGV[0] + host ||= "localhost" + printf("%s alive? - %s\n", host, Ping::pingecho(host, 5)) +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/pp.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/pp.rb new file mode 100644 index 0000000000..8080d879f9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/pp.rb @@ -0,0 +1,647 @@ +# == Pretty-printer for Ruby objects. +# +# = Which seems better? +# +# non-pretty-printed output by #p is: +# #, @group_queue=#], []]>, @buffer=[], @newline="\n", @group_stack=[#], @buffer_width=0, @indent=0, @maxwidth=79, @output_width=2, @output=#> +# +# pretty-printed output by #pp is: +# #, +# @group_queue= +# #], +# []]>, +# @group_stack= +# [#], +# @indent=0, +# @maxwidth=79, +# @newline="\n", +# @output=#, +# @output_width=2> +# +# I like the latter. If you do too, this library is for you. +# +# = Usage +# +# pp(obj) +# +# output +obj+ to +$>+ in pretty printed format. +# +# It returns +nil+. +# +# = Output Customization +# To define your customized pretty printing function for your classes, +# redefine a method #pretty_print(+pp+) in the class. +# It takes an argument +pp+ which is an instance of the class PP. +# The method should use PP#text, PP#breakable, PP#nest, PP#group and +# PP#pp to print the object. +# +# = Author +# Tanaka Akira + +require 'prettyprint' + +module Kernel + # returns a pretty printed object as a string. + def pretty_inspect + PP.pp(self, '') + end + + private + # prints arguments in pretty form. + # + # pp returns nil. + def pp(*objs) # :doc: + objs.each {|obj| + PP.pp(obj) + } + nil + end + module_function :pp +end + +class PP < PrettyPrint + # Outputs +obj+ to +out+ in pretty printed format of + # +width+ columns in width. + # + # If +out+ is omitted, +$>+ is assumed. + # If +width+ is omitted, 79 is assumed. + # + # PP.pp returns +out+. + def PP.pp(obj, out=$>, width=79) + q = PP.new(out, width) + q.guard_inspect_key {q.pp obj} + q.flush + #$pp = q + out << "\n" + end + + # Outputs +obj+ to +out+ like PP.pp but with no indent and + # newline. + # + # PP.singleline_pp returns +out+. + def PP.singleline_pp(obj, out=$>) + q = SingleLine.new(out) + q.guard_inspect_key {q.pp obj} + q.flush + out + end + + # :stopdoc: + def PP.mcall(obj, mod, meth, *args, &block) + mod.instance_method(meth).bind(obj).call(*args, &block) + end + # :startdoc: + + @sharing_detection = false + class << self + # Returns the sharing detection flag as a boolean value. + # It is false by default. + attr_accessor :sharing_detection + end + + module PPMethods + InspectKey = :__inspect_key__ + + def guard_inspect_key + if Thread.current[InspectKey] == nil + Thread.current[InspectKey] = [] + end + + save = Thread.current[InspectKey] + + begin + Thread.current[InspectKey] = [] + yield + ensure + Thread.current[InspectKey] = save + end + end + + # Adds +obj+ to the pretty printing buffer + # using Object#pretty_print or Object#pretty_print_cycle. + # + # Object#pretty_print_cycle is used when +obj+ is already + # printed, a.k.a the object reference chain has a cycle. + def pp(obj) + id = obj.__id__ + + if Thread.current[InspectKey].include? id + group {obj.pretty_print_cycle self} + return + end + + begin + Thread.current[InspectKey] << id + group {obj.pretty_print self} + ensure + Thread.current[InspectKey].pop unless PP.sharing_detection + end + end + + # A convenience method which is same as follows: + # + # group(1, '#<' + obj.class.name, '>') { ... } + def object_group(obj, &block) # :yield: + group(1, '#<' + obj.class.name, '>', &block) + end + + def object_address_group(obj, &block) + id = "%x" % (obj.__id__ * 2) + id.sub!(/\Af(?=[[:xdigit:]]{2}+\z)/, '') if id.sub!(/\A\.\./, '') + group(1, "\#<#{obj.class}:0x#{id}", '>', &block) + end + + # A convenience method which is same as follows: + # + # text ',' + # breakable + def comma_breakable + text ',' + breakable + end + + # Adds a separated list. + # The list is separated by comma with breakable space, by default. + # + # #seplist iterates the +list+ using +iter_method+. + # It yields each object to the block given for #seplist. + # The procedure +separator_proc+ is called between each yields. + # + # If the iteration is zero times, +separator_proc+ is not called at all. + # + # If +separator_proc+ is nil or not given, + # +lambda { comma_breakable }+ is used. + # If +iter_method+ is not given, :each is used. + # + # For example, following 3 code fragments has similar effect. + # + # q.seplist([1,2,3]) {|v| xxx v } + # + # q.seplist([1,2,3], lambda { comma_breakable }, :each) {|v| xxx v } + # + # xxx 1 + # q.comma_breakable + # xxx 2 + # q.comma_breakable + # xxx 3 + def seplist(list, sep=nil, iter_method=:each) # :yield: element + sep ||= lambda { comma_breakable } + first = true + list.__send__(iter_method) {|*v| + if first + first = false + else + sep.call + end + yield(*v) + } + end + + def pp_object(obj) + object_address_group(obj) { + seplist(obj.pretty_print_instance_variables, lambda { text ',' }) {|v| + breakable + v = v.to_s if Symbol === v + text v + text '=' + group(1) { + breakable '' + pp(obj.instance_eval(v)) + } + } + } + end + + def pp_hash(obj) + group(1, '{', '}') { + seplist(obj, nil, :each_pair) {|k, v| + group { + pp k + text '=>' + group(1) { + breakable '' + pp v + } + } + } + } + end + end + + include PPMethods + + class SingleLine < PrettyPrint::SingleLine + include PPMethods + end + + module ObjectMixin + # 1. specific pretty_print + # 2. specific inspect + # 3. specific to_s if instance variable is empty + # 4. generic pretty_print + + # A default pretty printing method for general objects. + # It calls #pretty_print_instance_variables to list instance variables. + # + # If +self+ has a customized (redefined) #inspect method, + # the result of self.inspect is used but it obviously has no + # line break hints. + # + # This module provides predefined #pretty_print methods for some of + # the most commonly used built-in classes for convenience. + def pretty_print(q) + if /\(Kernel\)#/ !~ method(:inspect).inspect + q.text self.inspect + elsif /\(Kernel\)#/ !~ method(:to_s).inspect && instance_variables.empty? + q.text self.to_s + else + q.pp_object(self) + end + end + + # A default pretty printing method for general objects that are + # detected as part of a cycle. + def pretty_print_cycle(q) + q.object_address_group(self) { + q.breakable + q.text '...' + } + end + + # Returns a sorted array of instance variable names. + # + # This method should return an array of names of instance variables as symbols or strings as: + # +[:@a, :@b]+. + def pretty_print_instance_variables + instance_variables.sort + end + + # Is #inspect implementation using #pretty_print. + # If you implement #pretty_print, it can be used as follows. + # + # alias inspect pretty_print_inspect + # + # However, doing this requires that every class that #inspect is called on + # implement #pretty_print, or a RuntimeError will be raised. + def pretty_print_inspect + if /\(PP::ObjectMixin\)#/ =~ method(:pretty_print).inspect + raise "pretty_print is not overridden for #{self.class}" + end + PP.singleline_pp(self, '') + end + end +end + +class Array + def pretty_print(q) + q.group(1, '[', ']') { + q.seplist(self) {|v| + q.pp v + } + } + end + + def pretty_print_cycle(q) + q.text(empty? ? '[]' : '[...]') + end +end + +class Hash + def pretty_print(q) + q.pp_hash self + end + + def pretty_print_cycle(q) + q.text(empty? ? '{}' : '{...}') + end +end + +class << ENV + def pretty_print(q) + q.pp_hash self + end +end + +class Struct + def pretty_print(q) + q.group(1, '#') { + q.seplist(PP.mcall(self, Struct, :members), lambda { q.text "," }) {|member| + q.breakable + q.text member.to_s + q.text '=' + q.group(1) { + q.breakable '' + q.pp self[member] + } + } + } + end + + def pretty_print_cycle(q) + q.text sprintf("#", PP.mcall(self, Kernel, :class).name) + end +end + +class Range + def pretty_print(q) + q.pp self.begin + q.breakable '' + q.text(self.exclude_end? ? '...' : '..') + q.breakable '' + q.pp self.end + end +end + +class File + class Stat + def pretty_print(q) + require 'etc.so' + q.object_group(self) { + q.breakable + q.text sprintf("dev=0x%x", self.dev); q.comma_breakable + q.text "ino="; q.pp self.ino; q.comma_breakable + q.group { + m = self.mode + q.text sprintf("mode=0%o", m) + q.breakable + q.text sprintf("(%s %c%c%c%c%c%c%c%c%c)", + self.ftype, + (m & 0400 == 0 ? ?- : ?r), + (m & 0200 == 0 ? ?- : ?w), + (m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) : + (m & 04000 == 0 ? ?x : ?s)), + (m & 0040 == 0 ? ?- : ?r), + (m & 0020 == 0 ? ?- : ?w), + (m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) : + (m & 02000 == 0 ? ?x : ?s)), + (m & 0004 == 0 ? ?- : ?r), + (m & 0002 == 0 ? ?- : ?w), + (m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) : + (m & 01000 == 0 ? ?x : ?t))) + } + q.comma_breakable + q.text "nlink="; q.pp self.nlink; q.comma_breakable + q.group { + q.text "uid="; q.pp self.uid + begin + pw = Etc.getpwuid(self.uid) + rescue ArgumentError + end + if pw + q.breakable; q.text "(#{pw.name})" + end + } + q.comma_breakable + q.group { + q.text "gid="; q.pp self.gid + begin + gr = Etc.getgrgid(self.gid) + rescue ArgumentError + end + if gr + q.breakable; q.text "(#{gr.name})" + end + } + q.comma_breakable + q.group { + q.text sprintf("rdev=0x%x", self.rdev) + q.breakable + q.text sprintf('(%d, %d)', self.rdev_major, self.rdev_minor) + } + q.comma_breakable + q.text "size="; q.pp self.size; q.comma_breakable + q.text "blksize="; q.pp self.blksize; q.comma_breakable + q.text "blocks="; q.pp self.blocks; q.comma_breakable + q.group { + t = self.atime + q.text "atime="; q.pp t + q.breakable; q.text "(#{t.tv_sec})" + } + q.comma_breakable + q.group { + t = self.mtime + q.text "mtime="; q.pp t + q.breakable; q.text "(#{t.tv_sec})" + } + q.comma_breakable + q.group { + t = self.ctime + q.text "ctime="; q.pp t + q.breakable; q.text "(#{t.tv_sec})" + } + } + end + end +end + +class MatchData + def pretty_print(q) + q.object_group(self) { + q.breakable + q.seplist(1..self.size, lambda { q.breakable }) {|i| + q.pp self[i-1] + } + } + end +end + +class Object + include PP::ObjectMixin +end + +[Numeric, Symbol, FalseClass, TrueClass, NilClass, Module].each {|c| + c.class_eval { + def pretty_print_cycle(q) + q.text inspect + end + } +} + +[Numeric, FalseClass, TrueClass, Module].each {|c| + c.class_eval { + def pretty_print(q) + q.text inspect + end + } +} + +# :enddoc: +if __FILE__ == $0 + require 'test/unit' + + class PPTest < Test::Unit::TestCase + def test_list0123_12 + assert_equal("[0, 1, 2, 3]\n", PP.pp([0,1,2,3], '', 12)) + end + + def test_list0123_11 + assert_equal("[0,\n 1,\n 2,\n 3]\n", PP.pp([0,1,2,3], '', 11)) + end + + OverriddenStruct = Struct.new("OverriddenStruct", :members, :class) + def test_struct_override_members # [ruby-core:7865] + a = OverriddenStruct.new(1,2) + assert_equal("#\n", PP.pp(a, '')) + end + end + + class HasInspect + def initialize(a) + @a = a + end + + def inspect + return "" + end + end + + class HasPrettyPrint + def initialize(a) + @a = a + end + + def pretty_print(q) + q.text "" + end + end + + class HasBoth + def initialize(a) + @a = a + end + + def inspect + return "" + end + + def pretty_print(q) + q.text "" + end + end + + class PrettyPrintInspect < HasPrettyPrint + alias inspect pretty_print_inspect + end + + class PrettyPrintInspectWithoutPrettyPrint + alias inspect pretty_print_inspect + end + + class PPInspectTest < Test::Unit::TestCase + def test_hasinspect + a = HasInspect.new(1) + assert_equal("\n", PP.pp(a, '')) + end + + def test_hasprettyprint + a = HasPrettyPrint.new(1) + assert_equal("\n", PP.pp(a, '')) + end + + def test_hasboth + a = HasBoth.new(1) + assert_equal("\n", PP.pp(a, '')) + end + + def test_pretty_print_inspect + a = PrettyPrintInspect.new(1) + assert_equal("", a.inspect) + a = PrettyPrintInspectWithoutPrettyPrint.new + assert_raise(RuntimeError) { a.inspect } + end + + def test_proc + a = proc {1} + assert_equal("#{a.inspect}\n", PP.pp(a, '')) + end + + def test_to_s_with_iv + a = Object.new + def a.to_s() "aaa" end + a.instance_eval { @a = nil } + result = PP.pp(a, '') + assert_equal("#{a.inspect}\n", result) + assert_match(/\A#\n\z/m, result) + a = 1.0 + a.instance_eval { @a = nil } + result = PP.pp(a, '') + assert_equal("#{a.inspect}\n", result) + end + + def test_to_s_without_iv + a = Object.new + def a.to_s() "aaa" end + result = PP.pp(a, '') + assert_equal("#{a.inspect}\n", result) + assert_equal("aaa\n", result) + end + end + + class PPCycleTest < Test::Unit::TestCase + def test_array + a = [] + a << a + assert_equal("[[...]]\n", PP.pp(a, '')) + assert_equal("#{a.inspect}\n", PP.pp(a, '')) + end + + def test_hash + a = {} + a[0] = a + assert_equal("{0=>{...}}\n", PP.pp(a, '')) + assert_equal("#{a.inspect}\n", PP.pp(a, '')) + end + + S = Struct.new("S", :a, :b) + def test_struct + a = S.new(1,2) + a.b = a + assert_equal("#>\n", PP.pp(a, '')) + assert_equal("#{a.inspect}\n", PP.pp(a, '')) + end + + def test_object + a = Object.new + a.instance_eval {@a = a} + assert_equal(a.inspect + "\n", PP.pp(a, '')) + end + + def test_anonymous + a = Class.new.new + assert_equal(a.inspect + "\n", PP.pp(a, '')) + end + + def test_withinspect + a = [] + a << HasInspect.new(a) + assert_equal("[]\n", PP.pp(a, '')) + assert_equal("#{a.inspect}\n", PP.pp(a, '')) + end + + def test_share_nil + begin + PP.sharing_detection = true + a = [nil, nil] + assert_equal("[nil, nil]\n", PP.pp(a, '')) + ensure + PP.sharing_detection = false + end + end + end + + class PPSingleLineTest < Test::Unit::TestCase + def test_hash + assert_equal("{1=>1}", PP.singleline_pp({ 1 => 1}, '')) # [ruby-core:02699] + assert_equal("[1#{', 1'*99}]", PP.singleline_pp([1]*100, '')) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/prettyprint.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/prettyprint.rb new file mode 100644 index 0000000000..315c422e9e --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/prettyprint.rb @@ -0,0 +1,896 @@ +# $Id$ + +# This class implements a pretty printing algorithm. It finds line breaks and +# nice indentations for grouped structure. +# +# By default, the class assumes that primitive elements are strings and each +# byte in the strings have single column in width. But it can be used for +# other situations by giving suitable arguments for some methods: +# * newline object and space generation block for PrettyPrint.new +# * optional width argument for PrettyPrint#text +# * PrettyPrint#breakable +# +# There are several candidate uses: +# * text formatting using proportional fonts +# * multibyte characters which has columns different to number of bytes +# * non-string formatting +# +# == Bugs +# * Box based formatting? +# * Other (better) model/algorithm? +# +# == References +# Christian Lindig, Strictly Pretty, March 2000, +# http://www.st.cs.uni-sb.de/~lindig/papers/#pretty +# +# Philip Wadler, A prettier printer, March 1998, +# http://homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier +# +# == Author +# Tanaka Akira +# +class PrettyPrint + + # This is a convenience method which is same as follows: + # + # begin + # q = PrettyPrint.new(output, maxwidth, newline, &genspace) + # ... + # q.flush + # output + # end + # + def PrettyPrint.format(output='', maxwidth=79, newline="\n", genspace=lambda {|n| ' ' * n}) + q = PrettyPrint.new(output, maxwidth, newline, &genspace) + yield q + q.flush + output + end + + # This is similar to PrettyPrint::format but the result has no breaks. + # + # +maxwidth+, +newline+ and +genspace+ are ignored. + # + # The invocation of +breakable+ in the block doesn't break a line and is + # treated as just an invocation of +text+. + # + def PrettyPrint.singleline_format(output='', maxwidth=nil, newline=nil, genspace=nil) + q = SingleLine.new(output) + yield q + output + end + + # Creates a buffer for pretty printing. + # + # +output+ is an output target. If it is not specified, '' is assumed. It + # should have a << method which accepts the first argument +obj+ of + # PrettyPrint#text, the first argument +sep+ of PrettyPrint#breakable, the + # first argument +newline+ of PrettyPrint.new, and the result of a given + # block for PrettyPrint.new. + # + # +maxwidth+ specifies maximum line length. If it is not specified, 79 is + # assumed. However actual outputs may overflow +maxwidth+ if long + # non-breakable texts are provided. + # + # +newline+ is used for line breaks. "\n" is used if it is not specified. + # + # The block is used to generate spaces. {|width| ' ' * width} is used if it + # is not given. + # + def initialize(output='', maxwidth=79, newline="\n", &genspace) + @output = output + @maxwidth = maxwidth + @newline = newline + @genspace = genspace || lambda {|n| ' ' * n} + + @output_width = 0 + @buffer_width = 0 + @buffer = [] + + root_group = Group.new(0) + @group_stack = [root_group] + @group_queue = GroupQueue.new(root_group) + @indent = 0 + end + attr_reader :output, :maxwidth, :newline, :genspace + attr_reader :indent, :group_queue + + def current_group + @group_stack.last + end + + # first? is a predicate to test the call is a first call to first? with + # current group. + # + # It is useful to format comma separated values as: + # + # q.group(1, '[', ']') { + # xxx.each {|yyy| + # unless q.first? + # q.text ',' + # q.breakable + # end + # ... pretty printing yyy ... + # } + # } + # + # first? is obsoleted in 1.8.2. + # + def first? + warn "PrettyPrint#first? is obsoleted at 1.8.2." + current_group.first? + end + + def break_outmost_groups + while @maxwidth < @output_width + @buffer_width + return unless group = @group_queue.deq + until group.breakables.empty? + data = @buffer.shift + @output_width = data.output(@output, @output_width) + @buffer_width -= data.width + end + while !@buffer.empty? && Text === @buffer.first + text = @buffer.shift + @output_width = text.output(@output, @output_width) + @buffer_width -= text.width + end + end + end + + # This adds +obj+ as a text of +width+ columns in width. + # + # If +width+ is not specified, obj.length is used. + # + def text(obj, width=obj.length) + if @buffer.empty? + @output << obj + @output_width += width + else + text = @buffer.last + unless Text === text + text = Text.new + @buffer << text + end + text.add(obj, width) + @buffer_width += width + break_outmost_groups + end + end + + def fill_breakable(sep=' ', width=sep.length) + group { breakable sep, width } + end + + # This tells "you can break a line here if necessary", and a +width+\-column + # text +sep+ is inserted if a line is not broken at the point. + # + # If +sep+ is not specified, " " is used. + # + # If +width+ is not specified, +sep.length+ is used. You will have to + # specify this when +sep+ is a multibyte character, for example. + # + def breakable(sep=' ', width=sep.length) + group = @group_stack.last + if group.break? + flush + @output << @newline + @output << @genspace.call(@indent) + @output_width = @indent + @buffer_width = 0 + else + @buffer << Breakable.new(sep, width, self) + @buffer_width += width + break_outmost_groups + end + end + + # Groups line break hints added in the block. The line break hints are all + # to be used or not. + # + # If +indent+ is specified, the method call is regarded as nested by + # nest(indent) { ... }. + # + # If +open_obj+ is specified, text open_obj, open_width is called + # before grouping. If +close_obj+ is specified, text close_obj, + # close_width is called after grouping. + # + def group(indent=0, open_obj='', close_obj='', open_width=open_obj.length, close_width=close_obj.length) + text open_obj, open_width + group_sub { + nest(indent) { + yield + } + } + text close_obj, close_width + end + + def group_sub + group = Group.new(@group_stack.last.depth + 1) + @group_stack.push group + @group_queue.enq group + begin + yield + ensure + @group_stack.pop + if group.breakables.empty? + @group_queue.delete group + end + end + end + + # Increases left margin after newline with +indent+ for line breaks added in + # the block. + # + def nest(indent) + @indent += indent + begin + yield + ensure + @indent -= indent + end + end + + # outputs buffered data. + # + def flush + @buffer.each {|data| + @output_width = data.output(@output, @output_width) + } + @buffer.clear + @buffer_width = 0 + end + + class Text + def initialize + @objs = [] + @width = 0 + end + attr_reader :width + + def output(out, output_width) + @objs.each {|obj| out << obj} + output_width + @width + end + + def add(obj, width) + @objs << obj + @width += width + end + end + + class Breakable + def initialize(sep, width, q) + @obj = sep + @width = width + @pp = q + @indent = q.indent + @group = q.current_group + @group.breakables.push self + end + attr_reader :obj, :width, :indent + + def output(out, output_width) + @group.breakables.shift + if @group.break? + out << @pp.newline + out << @pp.genspace.call(@indent) + @indent + else + @pp.group_queue.delete @group if @group.breakables.empty? + out << @obj + output_width + @width + end + end + end + + class Group + def initialize(depth) + @depth = depth + @breakables = [] + @break = false + end + attr_reader :depth, :breakables + + def break + @break = true + end + + def break? + @break + end + + def first? + if defined? @first + false + else + @first = false + true + end + end + end + + class GroupQueue + def initialize(*groups) + @queue = [] + groups.each {|g| enq g} + end + + def enq(group) + depth = group.depth + @queue << [] until depth < @queue.length + @queue[depth] << group + end + + def deq + @queue.each {|gs| + (gs.length-1).downto(0) {|i| + unless gs[i].breakables.empty? + group = gs.slice!(i, 1).first + group.break + return group + end + } + gs.each {|group| group.break} + gs.clear + } + return nil + end + + def delete(group) + @queue[group.depth].delete(group) + end + end + + class SingleLine + def initialize(output, maxwidth=nil, newline=nil) + @output = output + @first = [true] + end + + def text(obj, width=nil) + @output << obj + end + + def breakable(sep=' ', width=nil) + @output << sep + end + + def nest(indent) + yield + end + + def group(indent=nil, open_obj='', close_obj='', open_width=nil, close_width=nil) + @first.push true + @output << open_obj + yield + @output << close_obj + @first.pop + end + + def flush + end + + def first? + result = @first[-1] + @first[-1] = false + result + end + end +end + +if __FILE__ == $0 + require 'test/unit' + + class WadlerExample < Test::Unit::TestCase # :nodoc: + def setup + @tree = Tree.new("aaaa", Tree.new("bbbbb", Tree.new("ccc"), + Tree.new("dd")), + Tree.new("eee"), + Tree.new("ffff", Tree.new("gg"), + Tree.new("hhh"), + Tree.new("ii"))) + end + + def hello(width) + PrettyPrint.format('', width) {|hello| + hello.group { + hello.group { + hello.group { + hello.group { + hello.text 'hello' + hello.breakable; hello.text 'a' + } + hello.breakable; hello.text 'b' + } + hello.breakable; hello.text 'c' + } + hello.breakable; hello.text 'd' + } + } + end + + def test_hello_00_06 + expected = <<'End'.chomp +hello +a +b +c +d +End + assert_equal(expected, hello(0)) + assert_equal(expected, hello(6)) + end + + def test_hello_07_08 + expected = <<'End'.chomp +hello a +b +c +d +End + assert_equal(expected, hello(7)) + assert_equal(expected, hello(8)) + end + + def test_hello_09_10 + expected = <<'End'.chomp +hello a b +c +d +End + out = hello(9); assert_equal(expected, out) + out = hello(10); assert_equal(expected, out) + end + + def test_hello_11_12 + expected = <<'End'.chomp +hello a b c +d +End + assert_equal(expected, hello(11)) + assert_equal(expected, hello(12)) + end + + def test_hello_13 + expected = <<'End'.chomp +hello a b c d +End + assert_equal(expected, hello(13)) + end + + def tree(width) + PrettyPrint.format('', width) {|q| @tree.show(q)} + end + + def test_tree_00_19 + expected = <<'End'.chomp +aaaa[bbbbb[ccc, + dd], + eee, + ffff[gg, + hhh, + ii]] +End + assert_equal(expected, tree(0)) + assert_equal(expected, tree(19)) + end + + def test_tree_20_22 + expected = <<'End'.chomp +aaaa[bbbbb[ccc, dd], + eee, + ffff[gg, + hhh, + ii]] +End + assert_equal(expected, tree(20)) + assert_equal(expected, tree(22)) + end + + def test_tree_23_43 + expected = <<'End'.chomp +aaaa[bbbbb[ccc, dd], + eee, + ffff[gg, hhh, ii]] +End + assert_equal(expected, tree(23)) + assert_equal(expected, tree(43)) + end + + def test_tree_44 + assert_equal(<<'End'.chomp, tree(44)) +aaaa[bbbbb[ccc, dd], eee, ffff[gg, hhh, ii]] +End + end + + def tree_alt(width) + PrettyPrint.format('', width) {|q| @tree.altshow(q)} + end + + def test_tree_alt_00_18 + expected = <<'End'.chomp +aaaa[ + bbbbb[ + ccc, + dd + ], + eee, + ffff[ + gg, + hhh, + ii + ] +] +End + assert_equal(expected, tree_alt(0)) + assert_equal(expected, tree_alt(18)) + end + + def test_tree_alt_19_20 + expected = <<'End'.chomp +aaaa[ + bbbbb[ ccc, dd ], + eee, + ffff[ + gg, + hhh, + ii + ] +] +End + assert_equal(expected, tree_alt(19)) + assert_equal(expected, tree_alt(20)) + end + + def test_tree_alt_20_49 + expected = <<'End'.chomp +aaaa[ + bbbbb[ ccc, dd ], + eee, + ffff[ gg, hhh, ii ] +] +End + assert_equal(expected, tree_alt(21)) + assert_equal(expected, tree_alt(49)) + end + + def test_tree_alt_50 + expected = <<'End'.chomp +aaaa[ bbbbb[ ccc, dd ], eee, ffff[ gg, hhh, ii ] ] +End + assert_equal(expected, tree_alt(50)) + end + + class Tree # :nodoc: + def initialize(string, *children) + @string = string + @children = children + end + + def show(q) + q.group { + q.text @string + q.nest(@string.length) { + unless @children.empty? + q.text '[' + q.nest(1) { + first = true + @children.each {|t| + if first + first = false + else + q.text ',' + q.breakable + end + t.show(q) + } + } + q.text ']' + end + } + } + end + + def altshow(q) + q.group { + q.text @string + unless @children.empty? + q.text '[' + q.nest(2) { + q.breakable + first = true + @children.each {|t| + if first + first = false + else + q.text ',' + q.breakable + end + t.altshow(q) + } + } + q.breakable + q.text ']' + end + } + end + + end + end + + class StrictPrettyExample < Test::Unit::TestCase # :nodoc: + def prog(width) + PrettyPrint.format('', width) {|q| + q.group { + q.group {q.nest(2) { + q.text "if"; q.breakable; + q.group { + q.nest(2) { + q.group {q.text "a"; q.breakable; q.text "=="} + q.breakable; q.text "b"}}}} + q.breakable + q.group {q.nest(2) { + q.text "then"; q.breakable; + q.group { + q.nest(2) { + q.group {q.text "a"; q.breakable; q.text "<<"} + q.breakable; q.text "2"}}}} + q.breakable + q.group {q.nest(2) { + q.text "else"; q.breakable; + q.group { + q.nest(2) { + q.group {q.text "a"; q.breakable; q.text "+"} + q.breakable; q.text "b"}}}}} + } + end + + def test_00_04 + expected = <<'End'.chomp +if + a + == + b +then + a + << + 2 +else + a + + + b +End + assert_equal(expected, prog(0)) + assert_equal(expected, prog(4)) + end + + def test_05 + expected = <<'End'.chomp +if + a + == + b +then + a + << + 2 +else + a + + b +End + assert_equal(expected, prog(5)) + end + + def test_06 + expected = <<'End'.chomp +if + a == + b +then + a << + 2 +else + a + + b +End + assert_equal(expected, prog(6)) + end + + def test_07 + expected = <<'End'.chomp +if + a == + b +then + a << + 2 +else + a + b +End + assert_equal(expected, prog(7)) + end + + def test_08 + expected = <<'End'.chomp +if + a == b +then + a << 2 +else + a + b +End + assert_equal(expected, prog(8)) + end + + def test_09 + expected = <<'End'.chomp +if a == b +then + a << 2 +else + a + b +End + assert_equal(expected, prog(9)) + end + + def test_10 + expected = <<'End'.chomp +if a == b +then + a << 2 +else a + b +End + assert_equal(expected, prog(10)) + end + + def test_11_31 + expected = <<'End'.chomp +if a == b +then a << 2 +else a + b +End + assert_equal(expected, prog(11)) + assert_equal(expected, prog(15)) + assert_equal(expected, prog(31)) + end + + def test_32 + expected = <<'End'.chomp +if a == b then a << 2 else a + b +End + assert_equal(expected, prog(32)) + end + + end + + class TailGroup < Test::Unit::TestCase # :nodoc: + def test_1 + out = PrettyPrint.format('', 10) {|q| + q.group { + q.group { + q.text "abc" + q.breakable + q.text "def" + } + q.group { + q.text "ghi" + q.breakable + q.text "jkl" + } + } + } + assert_equal("abc defghi\njkl", out) + end + end + + class NonString < Test::Unit::TestCase # :nodoc: + def format(width) + PrettyPrint.format([], width, 'newline', lambda {|n| "#{n} spaces"}) {|q| + q.text(3, 3) + q.breakable(1, 1) + q.text(3, 3) + } + end + + def test_6 + assert_equal([3, "newline", "0 spaces", 3], format(6)) + end + + def test_7 + assert_equal([3, 1, 3], format(7)) + end + + end + + class Fill < Test::Unit::TestCase # :nodoc: + def format(width) + PrettyPrint.format('', width) {|q| + q.group { + q.text 'abc' + q.fill_breakable + q.text 'def' + q.fill_breakable + q.text 'ghi' + q.fill_breakable + q.text 'jkl' + q.fill_breakable + q.text 'mno' + q.fill_breakable + q.text 'pqr' + q.fill_breakable + q.text 'stu' + } + } + end + + def test_00_06 + expected = <<'End'.chomp +abc +def +ghi +jkl +mno +pqr +stu +End + assert_equal(expected, format(0)) + assert_equal(expected, format(6)) + end + + def test_07_10 + expected = <<'End'.chomp +abc def +ghi jkl +mno pqr +stu +End + assert_equal(expected, format(7)) + assert_equal(expected, format(10)) + end + + def test_11_14 + expected = <<'End'.chomp +abc def ghi +jkl mno pqr +stu +End + assert_equal(expected, format(11)) + assert_equal(expected, format(14)) + end + + def test_15_18 + expected = <<'End'.chomp +abc def ghi jkl +mno pqr stu +End + assert_equal(expected, format(15)) + assert_equal(expected, format(18)) + end + + def test_19_22 + expected = <<'End'.chomp +abc def ghi jkl mno +pqr stu +End + assert_equal(expected, format(19)) + assert_equal(expected, format(22)) + end + + def test_23_26 + expected = <<'End'.chomp +abc def ghi jkl mno pqr +stu +End + assert_equal(expected, format(23)) + assert_equal(expected, format(26)) + end + + def test_27 + expected = <<'End'.chomp +abc def ghi jkl mno pqr stu +End + assert_equal(expected, format(27)) + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/profile.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/profile.rb new file mode 100644 index 0000000000..104cb205b9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/profile.rb @@ -0,0 +1,6 @@ +require 'profiler' + +END { + Profiler__::print_profile(STDERR) +} +Profiler__::start_profile diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/profiler.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/profiler.rb new file mode 100644 index 0000000000..9762fa1181 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/profiler.rb @@ -0,0 +1,59 @@ +module Profiler__ + # internal values + @@start = @@stack = @@map = nil + PROFILE_PROC = proc{|event, file, line, id, binding, klass| + case event + when "call", "c-call" + now = Process.times[0] + @@stack.push [now, 0.0] + when "return", "c-return" + now = Process.times[0] + key = [klass, id] + if tick = @@stack.pop + data = (@@map[key] ||= [0, 0.0, 0.0, key]) + data[0] += 1 + cost = now - tick[0] + data[1] += cost + data[2] += cost - tick[1] + @@stack[-1][1] += cost if @@stack[-1] + end + end + } +module_function + def start_profile + @@start = Process.times[0] + @@stack = [] + @@map = {} + set_trace_func PROFILE_PROC + end + def stop_profile + set_trace_func nil + end + def print_profile(f) + stop_profile + total = Process.times[0] - @@start + if total == 0 then total = 0.01 end + data = @@map.values + data.sort!{|a,b| b[2] <=> a[2]} + sum = 0 + f.printf " %% cumulative self self total\n" + f.printf " time seconds seconds calls ms/call ms/call name\n" + for d in data + sum += d[2] + f.printf "%6.2f %8.2f %8.2f %8d ", d[2]/total*100, sum, d[2], d[0] + f.printf "%8.2f %8.2f %s\n", d[2]*1000/d[0], d[1]*1000/d[0], get_name(*d[3]) + end + f.printf "%6.2f %8.2f %8.2f %8d ", 0.0, total, 0.0, 1 # ??? + f.printf "%8.2f %8.2f %s\n", 0.0, total*1000, "#toplevel" # ??? + end + def get_name(klass, id) + name = klass.to_s || "" + if klass.kind_of? Class + name += "#" + else + name += "." + end + name + id.id2name + end + private :get_name +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/pstore.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/pstore.rb new file mode 100644 index 0000000000..6df64474ab --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/pstore.rb @@ -0,0 +1,395 @@ +# = PStore -- Transactional File Storage for Ruby Objects +# +# pstore.rb - +# originally by matz +# documentation by Kev Jackson and James Edward Gray II +# +# See PStore for documentation. + + +require "fileutils" +require "digest/md5" + +# +# PStore implements a file based persistance mechanism based on a Hash. User +# code can store hierarchies of Ruby objects (values) into the data store file +# by name (keys). An object hierarchy may be just a single object. User code +# may later read values back from the data store or even update data, as needed. +# +# The transactional behavior ensures that any changes succeed or fail together. +# This can be used to ensure that the data store is not left in a transitory +# state, where some values were upated but others were not. +# +# Behind the scenes, Ruby objects are stored to the data store file with +# Marshal. That carries the usual limitations. Proc objects cannot be +# marshalled, for example. +# +# == Usage example: +# +# require "pstore" +# +# # a mock wiki object... +# class WikiPage +# def initialize( page_name, author, contents ) +# @page_name = page_name +# @revisions = Array.new +# +# add_revision(author, contents) +# end +# +# attr_reader :page_name +# +# def add_revision( author, contents ) +# @revisions << { :created => Time.now, +# :author => author, +# :contents => contents } +# end +# +# def wiki_page_references +# [@page_name] + @revisions.last[:contents].scan(/\b(?:[A-Z]+[a-z]+){2,}/) +# end +# +# # ... +# end +# +# # create a new page... +# home_page = WikiPage.new( "HomePage", "James Edward Gray II", +# "A page about the JoysOfDocumentation..." ) +# +# # then we want to update page data and the index together, or not at all... +# wiki = PStore.new("wiki_pages.pstore") +# wiki.transaction do # begin transaction; do all of this or none of it +# # store page... +# wiki[home_page.page_name] = home_page +# # ensure that an index has been created... +# wiki[:wiki_index] ||= Array.new +# # update wiki index... +# wiki[:wiki_index].push(*home_page.wiki_page_references) +# end # commit changes to wiki data store file +# +# ### Some time later... ### +# +# # read wiki data... +# wiki.transaction(true) do # begin read-only transaction, no changes allowed +# wiki.roots.each do |data_root_name| +# p data_root_name +# p wiki[data_root_name] +# end +# end +# +class PStore + binmode = defined?(File::BINARY) ? File::BINARY : 0 + RDWR_ACCESS = File::RDWR | File::CREAT | binmode + RD_ACCESS = File::RDONLY | binmode + WR_ACCESS = File::WRONLY | File::CREAT | File::TRUNC | binmode + + # The error type thrown by all PStore methods. + class Error < StandardError + end + + # + # To construct a PStore object, pass in the _file_ path where you would like + # the data to be stored. + # + def initialize(file) + dir = File::dirname(file) + unless File::directory? dir + raise PStore::Error, format("directory %s does not exist", dir) + end + if File::exist? file and not File::readable? file + raise PStore::Error, format("file %s not readable", file) + end + @transaction = false + @filename = file + @abort = false + end + + # Raises PStore::Error if the calling code is not in a PStore#transaction. + def in_transaction + raise PStore::Error, "not in transaction" unless @transaction + end + # + # Raises PStore::Error if the calling code is not in a PStore#transaction or + # if the code is in a read-only PStore#transaction. + # + def in_transaction_wr() + in_transaction() + raise PStore::Error, "in read-only transaction" if @rdonly + end + private :in_transaction, :in_transaction_wr + + # + # Retrieves a value from the PStore file data, by _name_. The hierarchy of + # Ruby objects stored under that root _name_ will be returned. + # + # *WARNING*: This method is only valid in a PStore#transaction. It will + # raise PStore::Error if called at any other time. + # + def [](name) + in_transaction + @table[name] + end + # + # This method is just like PStore#[], save that you may also provide a + # _default_ value for the object. In the event the specified _name_ is not + # found in the data store, your _default_ will be returned instead. If you do + # not specify a default, PStore::Error will be raised if the object is not + # found. + # + # *WARNING*: This method is only valid in a PStore#transaction. It will + # raise PStore::Error if called at any other time. + # + def fetch(name, default=PStore::Error) + in_transaction + unless @table.key? name + if default==PStore::Error + raise PStore::Error, format("undefined root name `%s'", name) + else + return default + end + end + @table[name] + end + # + # Stores an individual Ruby object or a hierarchy of Ruby objects in the data + # store file under the root _name_. Assigning to a _name_ already in the data + # store clobbers the old data. + # + # == Example: + # + # require "pstore" + # + # store = PStore.new("data_file.pstore") + # store.transaction do # begin transaction + # # load some data into the store... + # store[:single_object] = "My data..." + # store[:obj_heirarchy] = { "Kev Jackson" => ["rational.rb", "pstore.rb"], + # "James Gray" => ["erb.rb", "pstore.rb"] } + # end # commit changes to data store file + # + # *WARNING*: This method is only valid in a PStore#transaction and it cannot + # be read-only. It will raise PStore::Error if called at any other time. + # + def []=(name, value) + in_transaction_wr() + @table[name] = value + end + # + # Removes an object hierarchy from the data store, by _name_. + # + # *WARNING*: This method is only valid in a PStore#transaction and it cannot + # be read-only. It will raise PStore::Error if called at any other time. + # + def delete(name) + in_transaction_wr() + @table.delete name + end + + # + # Returns the names of all object hierarchies currently in the store. + # + # *WARNING*: This method is only valid in a PStore#transaction. It will + # raise PStore::Error if called at any other time. + # + def roots + in_transaction + @table.keys + end + # + # Returns true if the supplied _name_ is currently in the data store. + # + # *WARNING*: This method is only valid in a PStore#transaction. It will + # raise PStore::Error if called at any other time. + # + def root?(name) + in_transaction + @table.key? name + end + # Returns the path to the data store file. + def path + @filename + end + + # + # Ends the current PStore#transaction, committing any changes to the data + # store immediately. + # + # == Example: + # + # require "pstore" + # + # store = PStore.new("data_file.pstore") + # store.transaction do # begin transaction + # # load some data into the store... + # store[:one] = 1 + # store[:two] = 2 + # + # store.commit # end transaction here, committing changes + # + # store[:three] = 3 # this change is never reached + # end + # + # *WARNING*: This method is only valid in a PStore#transaction. It will + # raise PStore::Error if called at any other time. + # + def commit + in_transaction + @abort = false + throw :pstore_abort_transaction + end + # + # Ends the current PStore#transaction, discarding any changes to the data + # store. + # + # == Example: + # + # require "pstore" + # + # store = PStore.new("data_file.pstore") + # store.transaction do # begin transaction + # store[:one] = 1 # this change is not applied, see below... + # store[:two] = 2 # this change is not applied, see below... + # + # store.abort # end transaction here, discard all changes + # + # store[:three] = 3 # this change is never reached + # end + # + # *WARNING*: This method is only valid in a PStore#transaction. It will + # raise PStore::Error if called at any other time. + # + def abort + in_transaction + @abort = true + throw :pstore_abort_transaction + end + + # + # Opens a new transaction for the data store. Code executed inside a block + # passed to this method may read and write data to and from the data store + # file. + # + # At the end of the block, changes are committed to the data store + # automatically. You may exit the transaction early with a call to either + # PStore#commit or PStore#abort. See those methods for details about how + # changes are handled. Raising an uncaught Exception in the block is + # equivalent to calling PStore#abort. + # + # If _read_only_ is set to +true+, you will only be allowed to read from the + # data store during the transaction and any attempts to change the data will + # raise a PStore::Error. + # + # Note that PStore does not support nested transactions. + # + def transaction(read_only=false) # :yields: pstore + raise PStore::Error, "nested transaction" if @transaction + begin + @rdonly = read_only + @abort = false + @transaction = true + value = nil + new_file = @filename + ".new" + + content = nil + unless read_only + file = File.open(@filename, RDWR_ACCESS) + file.flock(File::LOCK_EX) + commit_new(file) if FileTest.exist?(new_file) + content = file.read() + else + begin + file = File.open(@filename, RD_ACCESS) + file.flock(File::LOCK_SH) + content = (File.open(new_file, RD_ACCESS) {|n| n.read} rescue file.read()) + rescue Errno::ENOENT + content = "" + end + end + + if content != "" + @table = load(content) + if !read_only + size = content.size + md5 = Digest::MD5.digest(content) + end + else + @table = {} + end + content = nil # unreference huge data + + begin + catch(:pstore_abort_transaction) do + value = yield(self) + end + rescue Exception + @abort = true + raise + ensure + if !read_only and !@abort + tmp_file = @filename + ".tmp" + content = dump(@table) + if !md5 || size != content.size || md5 != Digest::MD5.digest(content) + File.open(tmp_file, WR_ACCESS) {|t| t.write(content)} + File.rename(tmp_file, new_file) + commit_new(file) + end + content = nil # unreference huge data + end + end + ensure + @table = nil + @transaction = false + file.close if file + end + value + end + + # This method is just a wrapped around Marshal.dump. + def dump(table) # :nodoc: + Marshal::dump(table) + end + + # This method is just a wrapped around Marshal.load. + def load(content) # :nodoc: + Marshal::load(content) + end + + # This method is just a wrapped around Marshal.load. + def load_file(file) # :nodoc: + Marshal::load(file) + end + + private + # Commits changes to the data store file. + def commit_new(f) + f.truncate(0) + f.rewind + new_file = @filename + ".new" + File.open(new_file, RD_ACCESS) do |nf| + FileUtils.copy_stream(nf, f) + end + File.unlink(new_file) + end +end + +# :enddoc: + +if __FILE__ == $0 + db = PStore.new("/tmp/foo") + db.transaction do + p db.roots + ary = db["root"] = [1,2,3,4] + ary[1] = [1,1.5] + end + + 1000.times do + db.transaction do + db["root"][0] += 1 + p db["root"][0] + end + end + + db.transaction(true) do + p db["root"] + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/racc/parser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/racc/parser.rb new file mode 100644 index 0000000000..a7408dd00a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/racc/parser.rb @@ -0,0 +1,442 @@ +# +# $originalId: parser.rb,v 1.8 2006/07/06 11:42:07 aamine Exp $ +# +# Copyright (c) 1999-2006 Minero Aoki +# +# This program is free software. +# You can distribute/modify this program under the same terms of ruby. +# +# As a special exception, when this code is copied by Racc +# into a Racc output file, you may use that output file +# without restriction. +# + +unless defined?(NotImplementedError) + NotImplementedError = NotImplementError +end + +module Racc + class ParseError < StandardError; end +end +unless defined?(::ParseError) + ParseError = Racc::ParseError +end + +module Racc + + unless defined?(Racc_No_Extentions) + Racc_No_Extentions = false + end + + class Parser + + Racc_Runtime_Version = '1.4.5' + Racc_Runtime_Revision = '$originalRevision: 1.8 $'.split[1] + + Racc_Runtime_Core_Version_R = '1.4.5' + Racc_Runtime_Core_Revision_R = '$originalRevision: 1.8 $'.split[1] + begin + require 'racc/cparse' + # Racc_Runtime_Core_Version_C = (defined in extention) + Racc_Runtime_Core_Revision_C = Racc_Runtime_Core_Id_C.split[2] + unless new.respond_to?(:_racc_do_parse_c, true) + raise LoadError, 'old cparse.so' + end + if Racc_No_Extentions + raise LoadError, 'selecting ruby version of racc runtime core' + end + + Racc_Main_Parsing_Routine = :_racc_do_parse_c + Racc_YY_Parse_Method = :_racc_yyparse_c + Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C + Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_C + Racc_Runtime_Type = 'c' + rescue LoadError + Racc_Main_Parsing_Routine = :_racc_do_parse_rb + Racc_YY_Parse_Method = :_racc_yyparse_rb + Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R + Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_R + Racc_Runtime_Type = 'ruby' + end + + def Parser.racc_runtime_type + Racc_Runtime_Type + end + + private + + def _racc_setup + @yydebug = false unless self.class::Racc_debug_parser + @yydebug = false unless defined?(@yydebug) + if @yydebug + @racc_debug_out = $stderr unless defined?(@racc_debug_out) + @racc_debug_out ||= $stderr + end + arg = self.class::Racc_arg + arg[13] = true if arg.size < 14 + arg + end + + def _racc_init_sysvars + @racc_state = [0] + @racc_tstack = [] + @racc_vstack = [] + + @racc_t = nil + @racc_val = nil + + @racc_read_next = true + + @racc_user_yyerror = false + @racc_error_status = 0 + end + + ### + ### do_parse + ### + + def do_parse + __send__(Racc_Main_Parsing_Routine, _racc_setup(), false) + end + + def next_token + raise NotImplementedError, "#{self.class}\#next_token is not defined" + end + + def _racc_do_parse_rb(arg, in_debug) + action_table, action_check, action_default, action_pointer, + goto_table, goto_check, goto_default, goto_pointer, + nt_base, reduce_table, token_table, shift_n, + reduce_n, use_result, * = arg + + _racc_init_sysvars + tok = act = i = nil + nerr = 0 + + catch(:racc_end_parse) { + while true + if i = action_pointer[@racc_state[-1]] + if @racc_read_next + if @racc_t != 0 # not EOF + tok, @racc_val = next_token() + unless tok # EOF + @racc_t = 0 + else + @racc_t = (token_table[tok] or 1) # error token + end + racc_read_token(@racc_t, tok, @racc_val) if @yydebug + @racc_read_next = false + end + end + i += @racc_t + unless i >= 0 and + act = action_table[i] and + action_check[i] == @racc_state[-1] + act = action_default[@racc_state[-1]] + end + else + act = action_default[@racc_state[-1]] + end + while act = _racc_evalact(act, arg) + ; + end + end + } + end + + ### + ### yyparse + ### + + def yyparse(recv, mid) + __send__(Racc_YY_Parse_Method, recv, mid, _racc_setup(), true) + end + + def _racc_yyparse_rb(recv, mid, arg, c_debug) + action_table, action_check, action_default, action_pointer, + goto_table, goto_check, goto_default, goto_pointer, + nt_base, reduce_table, token_table, shift_n, + reduce_n, use_result, * = arg + + _racc_init_sysvars + tok = nil + act = nil + i = nil + nerr = 0 + + catch(:racc_end_parse) { + until i = action_pointer[@racc_state[-1]] + while act = _racc_evalact(action_default[@racc_state[-1]], arg) + ; + end + end + recv.__send__(mid) do |tok, val| + unless tok + @racc_t = 0 + else + @racc_t = (token_table[tok] or 1) # error token + end + @racc_val = val + @racc_read_next = false + + i += @racc_t + unless i >= 0 and + act = action_table[i] and + action_check[i] == @racc_state[-1] + act = action_default[@racc_state[-1]] + end + while act = _racc_evalact(act, arg) + ; + end + + while not (i = action_pointer[@racc_state[-1]]) or + not @racc_read_next or + @racc_t == 0 # $ + unless i and i += @racc_t and + i >= 0 and + act = action_table[i] and + action_check[i] == @racc_state[-1] + act = action_default[@racc_state[-1]] + end + while act = _racc_evalact(act, arg) + ; + end + end + end + } + end + + ### + ### common + ### + + def _racc_evalact(act, arg) + action_table, action_check, action_default, action_pointer, + goto_table, goto_check, goto_default, goto_pointer, + nt_base, reduce_table, token_table, shift_n, + reduce_n, use_result, * = arg + nerr = 0 # tmp + + if act > 0 and act < shift_n + # + # shift + # + if @racc_error_status > 0 + @racc_error_status -= 1 unless @racc_t == 1 # error token + end + @racc_vstack.push @racc_val + @racc_state.push act + @racc_read_next = true + if @yydebug + @racc_tstack.push @racc_t + racc_shift @racc_t, @racc_tstack, @racc_vstack + end + + elsif act < 0 and act > -reduce_n + # + # reduce + # + code = catch(:racc_jump) { + @racc_state.push _racc_do_reduce(arg, act) + false + } + if code + case code + when 1 # yyerror + @racc_user_yyerror = true # user_yyerror + return -reduce_n + when 2 # yyaccept + return shift_n + else + raise '[Racc Bug] unknown jump code' + end + end + + elsif act == shift_n + # + # accept + # + racc_accept if @yydebug + throw :racc_end_parse, @racc_vstack[0] + + elsif act == -reduce_n + # + # error + # + case @racc_error_status + when 0 + unless arg[21] # user_yyerror + nerr += 1 + on_error @racc_t, @racc_val, @racc_vstack + end + when 3 + if @racc_t == 0 # is $ + throw :racc_end_parse, nil + end + @racc_read_next = true + end + @racc_user_yyerror = false + @racc_error_status = 3 + while true + if i = action_pointer[@racc_state[-1]] + i += 1 # error token + if i >= 0 and + (act = action_table[i]) and + action_check[i] == @racc_state[-1] + break + end + end + throw :racc_end_parse, nil if @racc_state.size <= 1 + @racc_state.pop + @racc_vstack.pop + if @yydebug + @racc_tstack.pop + racc_e_pop @racc_state, @racc_tstack, @racc_vstack + end + end + return act + + else + raise "[Racc Bug] unknown action #{act.inspect}" + end + + racc_next_state(@racc_state[-1], @racc_state) if @yydebug + + nil + end + + def _racc_do_reduce(arg, act) + action_table, action_check, action_default, action_pointer, + goto_table, goto_check, goto_default, goto_pointer, + nt_base, reduce_table, token_table, shift_n, + reduce_n, use_result, * = arg + state = @racc_state + vstack = @racc_vstack + tstack = @racc_tstack + + i = act * -3 + len = reduce_table[i] + reduce_to = reduce_table[i+1] + method_id = reduce_table[i+2] + void_array = [] + + tmp_t = tstack[-len, len] if @yydebug + tmp_v = vstack[-len, len] + tstack[-len, len] = void_array if @yydebug + vstack[-len, len] = void_array + state[-len, len] = void_array + + # tstack must be updated AFTER method call + if use_result + vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0]) + else + vstack.push __send__(method_id, tmp_v, vstack) + end + tstack.push reduce_to + + racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug + + k1 = reduce_to - nt_base + if i = goto_pointer[k1] + i += state[-1] + if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1 + return curstate + end + end + goto_default[k1] + end + + def on_error(t, val, vstack) + raise ParseError, sprintf("\nparse error on value %s (%s)", + val.inspect, token_to_str(t) || '?') + end + + def yyerror + throw :racc_jump, 1 + end + + def yyaccept + throw :racc_jump, 2 + end + + def yyerrok + @racc_error_status = 0 + end + + # + # for debugging output + # + + def racc_read_token(t, tok, val) + @racc_debug_out.print 'read ' + @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') ' + @racc_debug_out.puts val.inspect + @racc_debug_out.puts + end + + def racc_shift(tok, tstack, vstack) + @racc_debug_out.puts "shift #{racc_token2str tok}" + racc_print_stacks tstack, vstack + @racc_debug_out.puts + end + + def racc_reduce(toks, sim, tstack, vstack) + out = @racc_debug_out + out.print 'reduce ' + if toks.empty? + out.print ' ' + else + toks.each {|t| out.print ' ', racc_token2str(t) } + end + out.puts " --> #{racc_token2str(sim)}" + + racc_print_stacks tstack, vstack + @racc_debug_out.puts + end + + def racc_accept + @racc_debug_out.puts 'accept' + @racc_debug_out.puts + end + + def racc_e_pop(state, tstack, vstack) + @racc_debug_out.puts 'error recovering mode: pop token' + racc_print_states state + racc_print_stacks tstack, vstack + @racc_debug_out.puts + end + + def racc_next_state(curstate, state) + @racc_debug_out.puts "goto #{curstate}" + racc_print_states state + @racc_debug_out.puts + end + + def racc_print_stacks(t, v) + out = @racc_debug_out + out.print ' [' + t.each_index do |i| + out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')' + end + out.puts ' ]' + end + + def racc_print_states(s) + out = @racc_debug_out + out.print ' [' + s.each {|st| out.print ' ', st } + out.puts ' ]' + end + + def racc_token2str(tok) + self.class::Racc_token_to_s_table[tok] or + raise "[Racc Bug] can't convert token #{tok} to string" + end + + def token_to_str(t) + self.class::Racc_token_to_s_table[t] + end + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rational.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rational.rb new file mode 100644 index 0000000000..ce754cfa3c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rational.rb @@ -0,0 +1,530 @@ +# +# rational.rb - +# $Release Version: 0.5 $ +# $Revision: 1.7 $ +# $Date: 1999/08/24 12:49:28 $ +# by Keiju ISHITSUKA(SHL Japan Inc.) +# +# Documentation by Kevin Jackson and Gavin Sinclair. +# +# When you require 'rational', all interactions between numbers +# potentially return a rational result. For example: +# +# 1.quo(2) # -> 0.5 +# require 'rational' +# 1.quo(2) # -> Rational(1,2) +# +# See Rational for full documentation. +# + + +# +# Creates a Rational number (i.e. a fraction). +a+ and +b+ should be Integers: +# +# Rational(1,3) # -> 1/3 +# +# Note: trying to construct a Rational with floating point or real values +# produces errors: +# +# Rational(1.1, 2.3) # -> NoMethodError +# +def Rational(a, b = 1) + if a.kind_of?(Rational) && b == 1 + a + else + Rational.reduce(a, b) + end +end + +# +# Rational implements a rational class for numbers. +# +# A rational number is a number that can be expressed as a fraction p/q +# where p and q are integers and q != 0. A rational number p/q is said to have +# numerator p and denominator q. Numbers that are not rational are called +# irrational numbers. (http://mathworld.wolfram.com/RationalNumber.html) +# +# To create a Rational Number: +# Rational(a,b) # -> a/b +# Rational.new!(a,b) # -> a/b +# +# Examples: +# Rational(5,6) # -> 5/6 +# Rational(5) # -> 5/1 +# +# Rational numbers are reduced to their lowest terms: +# Rational(6,10) # -> 3/5 +# +# But not if you use the unusual method "new!": +# Rational.new!(6,10) # -> 6/10 +# +# Division by zero is obviously not allowed: +# Rational(3,0) # -> ZeroDivisionError +# +class Rational < Numeric + @RCS_ID='-$Id: rational.rb,v 1.7 1999/08/24 12:49:28 keiju Exp keiju $-' + + # + # Reduces the given numerator and denominator to their lowest terms. Use + # Rational() instead. + # + def Rational.reduce(num, den = 1) + raise ZeroDivisionError, "denominator is zero" if den == 0 + + if den < 0 + num = -num + den = -den + end + gcd = num.gcd(den) + num = num.div(gcd) + den = den.div(gcd) + if den == 1 && defined?(Unify) + num + else + new!(num, den) + end + end + + # + # Implements the constructor. This method does not reduce to lowest terms or + # check for division by zero. Therefore #Rational() should be preferred in + # normal use. + # + def Rational.new!(num, den = 1) + new(num, den) + end + + private_class_method :new + + # + # This method is actually private. + # + def initialize(num, den) + if den < 0 + num = -num + den = -den + end + if num.kind_of?(Integer) and den.kind_of?(Integer) + @numerator = num + @denominator = den + else + @numerator = num.to_i + @denominator = den.to_i + end + end + + # + # Returns the addition of this value and +a+. + # + # Examples: + # r = Rational(3,4) # -> Rational(3,4) + # r + 1 # -> Rational(7,4) + # r + 0.5 # -> 1.25 + # + def + (a) + if a.kind_of?(Rational) + num = @numerator * a.denominator + num_a = a.numerator * @denominator + Rational(num + num_a, @denominator * a.denominator) + elsif a.kind_of?(Integer) + self + Rational.new!(a, 1) + elsif a.kind_of?(Float) + Float(self) + a + else + x, y = a.coerce(self) + x + y + end + end + + # + # Returns the difference of this value and +a+. + # subtracted. + # + # Examples: + # r = Rational(3,4) # -> Rational(3,4) + # r - 1 # -> Rational(-1,4) + # r - 0.5 # -> 0.25 + # + def - (a) + if a.kind_of?(Rational) + num = @numerator * a.denominator + num_a = a.numerator * @denominator + Rational(num - num_a, @denominator*a.denominator) + elsif a.kind_of?(Integer) + self - Rational.new!(a, 1) + elsif a.kind_of?(Float) + Float(self) - a + else + x, y = a.coerce(self) + x - y + end + end + + # + # Returns the product of this value and +a+. + # + # Examples: + # r = Rational(3,4) # -> Rational(3,4) + # r * 2 # -> Rational(3,2) + # r * 4 # -> Rational(3,1) + # r * 0.5 # -> 0.375 + # r * Rational(1,2) # -> Rational(3,8) + # + def * (a) + if a.kind_of?(Rational) + num = @numerator * a.numerator + den = @denominator * a.denominator + Rational(num, den) + elsif a.kind_of?(Integer) + self * Rational.new!(a, 1) + elsif a.kind_of?(Float) + Float(self) * a + else + x, y = a.coerce(self) + x * y + end + end + + # + # Returns the quotient of this value and +a+. + # r = Rational(3,4) # -> Rational(3,4) + # r / 2 # -> Rational(3,8) + # r / 2.0 # -> 0.375 + # r / Rational(1,2) # -> Rational(3,2) + # + def / (a) + if a.kind_of?(Rational) + num = @numerator * a.denominator + den = @denominator * a.numerator + Rational(num, den) + elsif a.kind_of?(Integer) + raise ZeroDivisionError, "division by zero" if a == 0 + self / Rational.new!(a, 1) + elsif a.kind_of?(Float) + Float(self) / a + else + x, y = a.coerce(self) + x / y + end + end + + # + # Returns this value raised to the given power. + # + # Examples: + # r = Rational(3,4) # -> Rational(3,4) + # r ** 2 # -> Rational(9,16) + # r ** 2.0 # -> 0.5625 + # r ** Rational(1,2) # -> 0.866025403784439 + # + def ** (other) + if other.kind_of?(Rational) + Float(self) ** other + elsif other.kind_of?(Integer) + if other > 0 + num = @numerator ** other + den = @denominator ** other + elsif other < 0 + num = @denominator ** -other + den = @numerator ** -other + elsif other == 0 + num = 1 + den = 1 + end + Rational.new!(num, den) + elsif other.kind_of?(Float) + Float(self) ** other + else + x, y = other.coerce(self) + x ** y + end + end + + # + # Returns the remainder when this value is divided by +other+. + # + # Examples: + # r = Rational(7,4) # -> Rational(7,4) + # r % Rational(1,2) # -> Rational(1,4) + # r % 1 # -> Rational(3,4) + # r % Rational(1,7) # -> Rational(1,28) + # r % 0.26 # -> 0.19 + # + def % (other) + value = (self / other).to_i + return self - other * value + end + + # + # Returns the quotient _and_ remainder. + # + # Examples: + # r = Rational(7,4) # -> Rational(7,4) + # r.divmod Rational(1,2) # -> [3, Rational(1,4)] + # + def divmod(other) + value = (self / other).to_i + return value, self - other * value + end + + # + # Returns the absolute value. + # + def abs + if @numerator > 0 + Rational.new!(@numerator, @denominator) + else + Rational.new!(-@numerator, @denominator) + end + end + + # + # Returns +true+ iff this value is numerically equal to +other+. + # + # But beware: + # Rational(1,2) == Rational(4,8) # -> true + # Rational(1,2) == Rational.new!(4,8) # -> false + # + # Don't use Rational.new! + # + def == (other) + if other.kind_of?(Rational) + @numerator == other.numerator and @denominator == other.denominator + elsif other.kind_of?(Integer) + self == Rational.new!(other, 1) + elsif other.kind_of?(Float) + Float(self) == other + else + other == self + end + end + + # + # Standard comparison operator. + # + def <=> (other) + if other.kind_of?(Rational) + num = @numerator * other.denominator + num_a = other.numerator * @denominator + v = num - num_a + if v > 0 + return 1 + elsif v < 0 + return -1 + else + return 0 + end + elsif other.kind_of?(Integer) + return self <=> Rational.new!(other, 1) + elsif other.kind_of?(Float) + return Float(self) <=> other + elsif defined? other.coerce + x, y = other.coerce(self) + return x <=> y + else + return nil + end + end + + def coerce(other) + if other.kind_of?(Float) + return other, self.to_f + elsif other.kind_of?(Integer) + return Rational.new!(other, 1), self + else + super + end + end + + # + # Converts the rational to an Integer. Not the _nearest_ integer, the + # truncated integer. Study the following example carefully: + # Rational(+7,4).to_i # -> 1 + # Rational(-7,4).to_i # -> -2 + # (-1.75).to_i # -> -1 + # + # In other words: + # Rational(-7,4) == -1.75 # -> true + # Rational(-7,4).to_i == (-1.75).to_i # false + # + def to_i + Integer(@numerator.div(@denominator)) + end + + # + # Converts the rational to a Float. + # + def to_f + @numerator.to_f/@denominator.to_f + end + + # + # Returns a string representation of the rational number. + # + # Example: + # Rational(3,4).to_s # "3/4" + # Rational(8).to_s # "8" + # + def to_s + if @denominator == 1 + @numerator.to_s + else + @numerator.to_s+"/"+@denominator.to_s + end + end + + # + # Returns +self+. + # + def to_r + self + end + + # + # Returns a reconstructable string representation: + # + # Rational(5,8).inspect # -> "Rational(5, 8)" + # + def inspect + sprintf("Rational(%s, %s)", @numerator.inspect, @denominator.inspect) + end + + # + # Returns a hash code for the object. + # + def hash + @numerator.hash ^ @denominator.hash + end + + attr :numerator + attr :denominator + + private :initialize +end + +class Integer + # + # In an integer, the value _is_ the numerator of its rational equivalent. + # Therefore, this method returns +self+. + # + def numerator + self + end + + # + # In an integer, the denominator is 1. Therefore, this method returns 1. + # + def denominator + 1 + end + + # + # Returns a Rational representation of this integer. + # + def to_r + Rational(self, 1) + end + + # + # Returns the greatest common denominator of the two numbers (+self+ + # and +n+). + # + # Examples: + # 72.gcd 168 # -> 24 + # 19.gcd 36 # -> 1 + # + # The result is positive, no matter the sign of the arguments. + # + def gcd(other) + min = self.abs + max = other.abs + while min > 0 + tmp = min + min = max % min + max = tmp + end + max + end + + # + # Returns the lowest common multiple (LCM) of the two arguments + # (+self+ and +other+). + # + # Examples: + # 6.lcm 7 # -> 42 + # 6.lcm 9 # -> 18 + # + def lcm(other) + if self.zero? or other.zero? + 0 + else + (self.div(self.gcd(other)) * other).abs + end + end + + # + # Returns the GCD _and_ the LCM (see #gcd and #lcm) of the two arguments + # (+self+ and +other+). This is more efficient than calculating them + # separately. + # + # Example: + # 6.gcdlcm 9 # -> [3, 18] + # + def gcdlcm(other) + gcd = self.gcd(other) + if self.zero? or other.zero? + [gcd, 0] + else + [gcd, (self.div(gcd) * other).abs] + end + end +end + +class Fixnum + undef quo + # If Rational is defined, returns a Rational number instead of a Fixnum. + def quo(other) + Rational.new!(self,1) / other + end + alias rdiv quo + + # Returns a Rational number if the result is in fact rational (i.e. +other+ < 0). + def rpower (other) + if other >= 0 + self.power!(other) + else + Rational.new!(self,1)**other + end + end + + unless defined? 1.power! + alias power! ** + alias ** rpower + end +end + +class Bignum + unless defined? Complex + alias power! ** + end + + undef quo + # If Rational is defined, returns a Rational number instead of a Bignum. + def quo(other) + Rational.new!(self,1) / other + end + alias rdiv quo + + # Returns a Rational number if the result is in fact rational (i.e. +other+ < 0). + def rpower (other) + if other >= 0 + self.power!(other) + else + Rational.new!(self, 1)**other + end + end + + unless defined? Complex + alias ** rpower + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rbconfig.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rbconfig.rb new file mode 100644 index 0000000000..6814412055 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rbconfig.rb @@ -0,0 +1,154 @@ + +# This file was created by mkconfig.rb when ruby was built. Any +# changes made to this file will be lost the next time ruby is built. + +module Config + RUBY_VERSION == "1.8.6" or + raise "ruby lib version (1.8.6) doesn't match executable version (#{RUBY_VERSION})" + + TOPDIR = File.dirname(__FILE__).chomp!("/lib/ruby/1.8/i386-mswin32") + DESTDIR = TOPDIR && TOPDIR[/\A[a-z]:/i] || '' unless defined? DESTDIR + CONFIG = {} + CONFIG["DESTDIR"] = DESTDIR + CONFIG["MAJOR"] = "1" + CONFIG["MINOR"] = "8" + CONFIG["TEENY"] = "6" + CONFIG["PATCHLEVEL"] = "111" + CONFIG["prefix"] = (TOPDIR || DESTDIR + "") + CONFIG["EXEEXT"] = ".exe" + CONFIG["ruby_install_name"] = "ruby" + CONFIG["RUBY_INSTALL_NAME"] = "ruby" + CONFIG["RUBY_SO_NAME"] = "msvcrt-ruby18" + CONFIG["SHELL"] = "$(COMSPEC)" + CONFIG["BUILD_FILE_SEPARATOR"] = "\\" + CONFIG["PATH_SEPARATOR"] = ";" + CONFIG["CFLAGS"] = "-MD -Zi -O2b2xg- -G6" + CONFIG["CPPFLAGS"] = "" + CONFIG["CXXFLAGS"] = "" + CONFIG["FFLAGS"] = "" + CONFIG["LDFLAGS"] = "" + CONFIG["LIBS"] = "oldnames.lib user32.lib advapi32.lib ws2_32.lib " + CONFIG["exec_prefix"] = "$(prefix)" + CONFIG["bindir"] = "$(exec_prefix)/bin" + CONFIG["sbindir"] = "$(exec_prefix)/sbin" + CONFIG["libexecdir"] = "$(exec_prefix)/libexec" + CONFIG["datadir"] = "$(prefix)/share" + CONFIG["sysconfdir"] = "$(prefix)/etc" + CONFIG["sharedstatedir"] = "$(DESTDIR)/etc" + CONFIG["localstatedir"] = "$(DESTDIR)/var" + CONFIG["libdir"] = "$(exec_prefix)/lib" + CONFIG["includedir"] = "$(prefix)/include" + CONFIG["oldincludedir"] = "/usr/include" + CONFIG["infodir"] = "$(prefix)/info" + CONFIG["mandir"] = "$(prefix)/man" + CONFIG["build"] = "i686-pc-mswin32" + CONFIG["build_alias"] = "i686-mswin32" + CONFIG["build_cpu"] = "i686" + CONFIG["build_vendor"] = "pc" + CONFIG["build_os"] = "mswin32" + CONFIG["host"] = "i686-pc-mswin32" + CONFIG["host_alias"] = "i686-mswin32" + CONFIG["host_cpu"] = "i686" + CONFIG["host_vendor"] = "pc" + CONFIG["host_os"] = "mswin32" + CONFIG["target"] = "i386-pc-mswin32" + CONFIG["target_alias"] = "i386-mswin32" + CONFIG["target_cpu"] = "i386" + CONFIG["target_vendor"] = "pc" + CONFIG["target_os"] = "mswin32" + CONFIG["CC"] = "cl -nologo" + CONFIG["CPP"] = "cl -nologo -E" + CONFIG["YACC"] = "byacc" + CONFIG["RANLIB"] = "" + CONFIG["AR"] = "lib -nologo" + CONFIG["ARFLAGS"] = "-machine:x86 -out:" + CONFIG["LN_S"] = "" + CONFIG["SET_MAKE"] = "" + CONFIG["CP"] = "copy > nul" + CONFIG["ALLOCA"] = "" + CONFIG["DEFAULT_KCODE"] = "" + CONFIG["OBJEXT"] = "obj" + CONFIG["XCFLAGS"] = "-DRUBY_EXPORT -I. -IC:/develop/win/ruby/ruby-1.8.6-p111 -IC:/develop/win/ruby/ruby-1.8.6-p111/missing" + CONFIG["XLDFLAGS"] = "-stack:0x2000000" + CONFIG["DLDFLAGS"] = "-link -incremental:no -debug -opt:ref -opt:icf -dll $(LIBPATH) -def:$(DEFFILE) -implib:$(*F:.so=)-$(arch).lib -pdb:$(*F:.so=)-$(arch).pdb" + CONFIG["ARCH_FLAG"] = "" + CONFIG["STATIC"] = "" + CONFIG["CCDLFLAGS"] = "" + CONFIG["LDSHARED"] = "cl -nologo -LD" + CONFIG["DLEXT"] = "so" + CONFIG["DLEXT2"] = "dll" + CONFIG["LIBEXT"] = "lib" + CONFIG["STRIP"] = "" + CONFIG["EXTSTATIC"] = "" + CONFIG["setup"] = "Setup" + CONFIG["MINIRUBY"] = ".\\miniruby.exe " + CONFIG["PREP"] = "miniruby.exe" + CONFIG["RUNRUBY"] = ".\\ruby.exe \"C:/develop/win/ruby/ruby-1.8.6-p111/runruby.rb\" --extout=\".ext\" --" + CONFIG["EXTOUT"] = ".ext" + CONFIG["ARCHFILE"] = "" + CONFIG["RDOCTARGET"] = "install-nodoc" + CONFIG["LIBRUBY_LDSHARED"] = "cl -nologo -LD" + CONFIG["LIBRUBY_DLDFLAGS"] = " -def:msvcrt-ruby18.def" + CONFIG["rubyw_install_name"] = "rubyw" + CONFIG["RUBYW_INSTALL_NAME"] = "rubyw" + CONFIG["LIBRUBY_A"] = "$(RUBY_SO_NAME)-static.lib" + CONFIG["LIBRUBY_SO"] = "$(RUBY_SO_NAME).dll" + CONFIG["LIBRUBY_ALIASES"] = "" + CONFIG["LIBRUBY"] = "$(RUBY_SO_NAME).lib" + CONFIG["LIBRUBYARG"] = "$(LIBRUBYARG_SHARED)" + CONFIG["LIBRUBYARG_STATIC"] = "$(LIBRUBY_A)" + CONFIG["LIBRUBYARG_SHARED"] = "$(LIBRUBY)" + CONFIG["SOLIBS"] = "" + CONFIG["DLDLIBS"] = "" + CONFIG["ENABLE_SHARED"] = "yes" + CONFIG["OUTFLAG"] = "-Fe" + CONFIG["CPPOUTFILE"] = "-P" + CONFIG["LIBPATHFLAG"] = " -libpath:\"%s\"" + CONFIG["RPATHFLAG"] = "" + CONFIG["LIBARG"] = "%s.lib" + CONFIG["LINK_SO"] = "$(LDSHARED) -Fe$(@) $(OBJS) $(LIBS) $(LOCAL_LIBS) $(DLDFLAGS)" + CONFIG["COMPILE_C"] = "$(CC) $(INCFLAGS) $(CFLAGS) $(CPPFLAGS) -c -Tc$(<:\\=/)" + CONFIG["COMPILE_CXX"] = "$(CXX) $(INCFLAGS) $(CXXFLAGS) $(CPPFLAGS) -c -Tp$(<:\\=/)" + CONFIG["COMPILE_RULES"] = "{$(srcdir)}.%s{}.%s: {$(topdir)}.%s{}.%s: {$(hdrdir)}.%s{}.%s: .%s.%s:" + CONFIG["RULE_SUBST"] = "{.;$(srcdir);$(topdir);$(hdrdir)}%s" + CONFIG["TRY_LINK"] = "$(CC) -Feconftest $(INCFLAGS) -I$(hdrdir) $(CPPFLAGS) $(CFLAGS) $(src) $(LOCAL_LIBS) $(LIBS) -link $(LDFLAGS) $(LIBPATH) $(XLDFLAGS)" + CONFIG["COMMON_LIBS"] = "m" + CONFIG["COMMON_MACROS"] = "WIN32_LEAN_AND_MEAN" + CONFIG["COMMON_HEADERS"] = "winsock2.h windows.h" + CONFIG["DISTCLEANFILES"] = "vc*.pdb" + CONFIG["EXPORT_PREFIX"] = " " + CONFIG["arch"] = "i386-mswin32" + CONFIG["sitearch"] = "i386-msvcrt" + CONFIG["sitedir"] = "$(prefix)/lib/ruby/site_ruby" + CONFIG["configure_args"] = "--with-make-prog=nmake --enable-shared --with-winsock2" + CONFIG["ruby_version"] = "$(MAJOR).$(MINOR)" + CONFIG["rubylibdir"] = "$(libdir)/ruby/$(ruby_version)" + CONFIG["archdir"] = "$(rubylibdir)/$(arch)" + CONFIG["sitelibdir"] = "$(sitedir)/$(ruby_version)" + CONFIG["sitearchdir"] = "$(sitelibdir)/$(sitearch)" + CONFIG["topdir"] = File.dirname(__FILE__) + MAKEFILE_CONFIG = {} + CONFIG.each{|k,v| MAKEFILE_CONFIG[k] = v.dup} + def Config::expand(val, config = CONFIG) + val.gsub!(/\$\$|\$\(([^()]+)\)|\$\{([^{}]+)\}/) do |var| + if !(v = $1 || $2) + '$' + elsif key = config[v = v[/\A[^:]+(?=(?::(.*?)=(.*))?\z)/]] + pat, sub = $1, $2 + config[v] = false + Config::expand(key, config) + config[v] = key + key = key.gsub(/#{Regexp.quote(pat)}(?=\s|\z)/n) {sub} if pat + key + else + var + end + end + val + end + CONFIG.each_value do |val| + Config::expand(val) + end +end +RbConfig = Config # compatibility for ruby-1.9 +CROSS_COMPILING = nil unless defined? CROSS_COMPILING diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/code_objects.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/code_objects.rb new file mode 100644 index 0000000000..d6c4f1bdb9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/code_objects.rb @@ -0,0 +1,765 @@ +# We represent the various high-level code constructs that appear +# in Ruby programs: classes, modules, methods, and so on. + +require 'rdoc/tokenstream' + +module RDoc + + + # We contain the common stuff for contexts (which are containers) + # and other elements (methods, attributes and so on) + # + class CodeObject + + attr_accessor :parent + + # We are the model of the code, but we know that at some point + # we will be worked on by viewers. By implementing the Viewable + # protocol, viewers can associated themselves with these objects. + + attr_accessor :viewer + + # are we done documenting (ie, did we come across a :enddoc:)? + + attr_accessor :done_documenting + + # Which section are we in + + attr_accessor :section + + # do we document ourselves? + + attr_reader :document_self + + def document_self=(val) + @document_self = val + if !val + remove_methods_etc + end + end + + # set and cleared by :startdoc: and :enddoc:, this is used to toggle + # the capturing of documentation + def start_doc + @document_self = true + @document_children = true + end + + def stop_doc + @document_self = false + @document_children = false + end + + # do we document ourselves and our children + + attr_reader :document_children + + def document_children=(val) + @document_children = val + if !val + remove_classes_and_modules + end + end + + # Do we _force_ documentation, even is we wouldn't normally show the entity + attr_accessor :force_documentation + + # Default callbacks to nothing, but this is overridden for classes + # and modules + def remove_classes_and_modules + end + + def remove_methods_etc + end + + def initialize + @document_self = true + @document_children = true + @force_documentation = false + @done_documenting = false + end + + # Access the code object's comment + attr_reader :comment + + # Update the comment, but don't overwrite a real comment + # with an empty one + def comment=(comment) + @comment = comment unless comment.empty? + end + + # There's a wee trick we pull. Comment blocks can have directives that + # override the stuff we extract during the parse. So, we have a special + # class method, attr_overridable, that lets code objects list + # those directives. Wehn a comment is assigned, we then extract + # out any matching directives and update our object + + def CodeObject.attr_overridable(name, *aliases) + @overridables ||= {} + + attr_accessor name + + aliases.unshift name + aliases.each do |directive_name| + @overridables[directive_name.to_s] = name + end + end + + end + + # A Context is something that can hold modules, classes, methods, + # attributes, aliases, requires, and includes. Classes, modules, and + # files are all Contexts. + + class Context < CodeObject + attr_reader :name, :method_list, :attributes, :aliases, :constants + attr_reader :requires, :includes, :in_files, :visibility + + attr_reader :sections + + class Section + attr_reader :title, :comment, :sequence + + @@sequence = "SEC00000" + + def initialize(title, comment) + @title = title + @@sequence.succ! + @sequence = @@sequence.dup + set_comment(comment) + end + + private + + # Set the comment for this section from the original comment block + # If the first line contains :section:, strip it and use the rest. Otherwise + # remove lines up to the line containing :section:, and look for + # those lines again at the end and remove them. This lets us write + # + # # --------------------- + # # :SECTION: The title + # # The body + # # --------------------- + + def set_comment(comment) + return unless comment + + if comment =~ /^.*?:section:.*$/ + start = $` + rest = $' + if start.empty? + @comment = rest + else + @comment = rest.sub(/#{start.chomp}\Z/, '') + end + else + @comment = comment + end + @comment = nil if @comment.empty? + end + end + + + def initialize + super() + + @in_files = [] + + @name ||= "unknown" + @comment ||= "" + @parent = nil + @visibility = :public + + @current_section = Section.new(nil, nil) + @sections = [ @current_section ] + + initialize_methods_etc + initialize_classes_and_modules + end + + # map the class hash to an array externally + def classes + @classes.values + end + + # map the module hash to an array externally + def modules + @modules.values + end + + # Change the default visibility for new methods + def ongoing_visibility=(vis) + @visibility = vis + end + + # Given an array +methods+ of method names, set the + # visibility of the corresponding AnyMethod object + + def set_visibility_for(methods, vis, singleton=false) + count = 0 + @method_list.each do |m| + if methods.include?(m.name) && m.singleton == singleton + m.visibility = vis + count += 1 + end + end + + return if count == methods.size || singleton + + # perhaps we need to look at attributes + + @attributes.each do |a| + if methods.include?(a.name) + a.visibility = vis + count += 1 + end + end + end + + # Record the file that we happen to find it in + def record_location(toplevel) + @in_files << toplevel unless @in_files.include?(toplevel) + end + + # Return true if at least part of this thing was defined in +file+ + def defined_in?(file) + @in_files.include?(file) + end + + def add_class(class_type, name, superclass) + add_class_or_module(@classes, class_type, name, superclass) + end + + def add_module(class_type, name) + add_class_or_module(@modules, class_type, name, nil) + end + + def add_method(a_method) + puts "Adding #@visibility method #{a_method.name} to #@name" if $DEBUG + a_method.visibility = @visibility + add_to(@method_list, a_method) + end + + def add_attribute(an_attribute) + add_to(@attributes, an_attribute) + end + + def add_alias(an_alias) + meth = find_instance_method_named(an_alias.old_name) + if meth + new_meth = AnyMethod.new(an_alias.text, an_alias.new_name) + new_meth.is_alias_for = meth + new_meth.singleton = meth.singleton + new_meth.params = meth.params + new_meth.comment = "Alias for \##{meth.name}" + meth.add_alias(new_meth) + add_method(new_meth) + else + add_to(@aliases, an_alias) + end + end + + def add_include(an_include) + add_to(@includes, an_include) + end + + def add_constant(const) + add_to(@constants, const) + end + + # Requires always get added to the top-level (file) context + def add_require(a_require) + if self.kind_of? TopLevel + add_to(@requires, a_require) + else + parent.add_require(a_require) + end + end + + def add_class_or_module(collection, class_type, name, superclass=nil) + cls = collection[name] + if cls + puts "Reusing class/module #{name}" if $DEBUG + else + cls = class_type.new(name, superclass) + puts "Adding class/module #{name} to #@name" if $DEBUG +# collection[name] = cls if @document_self && !@done_documenting + collection[name] = cls if !@done_documenting + cls.parent = self + cls.section = @current_section + end + cls + end + + def add_to(array, thing) + array << thing if @document_self && !@done_documenting + thing.parent = self + thing.section = @current_section + end + + # If a class's documentation is turned off after we've started + # collecting methods etc., we need to remove the ones + # we have + + def remove_methods_etc + initialize_methods_etc + end + + def initialize_methods_etc + @method_list = [] + @attributes = [] + @aliases = [] + @requires = [] + @includes = [] + @constants = [] + end + + # and remove classes and modules when we see a :nodoc: all + def remove_classes_and_modules + initialize_classes_and_modules + end + + def initialize_classes_and_modules + @classes = {} + @modules = {} + end + + # Find a named module + def find_module_named(name) + return self if self.name == name + res = @modules[name] || @classes[name] + return res if res + find_enclosing_module_named(name) + end + + # find a module at a higher scope + def find_enclosing_module_named(name) + parent && parent.find_module_named(name) + end + + # Iterate over all the classes and modules in + # this object + + def each_classmodule + @modules.each_value {|m| yield m} + @classes.each_value {|c| yield c} + end + + def each_method + @method_list.each {|m| yield m} + end + + def each_attribute + @attributes.each {|a| yield a} + end + + def each_constant + @constants.each {|c| yield c} + end + + # Return the toplevel that owns us + + def toplevel + return @toplevel if defined? @toplevel + @toplevel = self + @toplevel = @toplevel.parent until TopLevel === @toplevel + @toplevel + end + + # allow us to sort modules by name + def <=>(other) + name <=> other.name + end + + # Look up the given symbol. If method is non-nil, then + # we assume the symbol references a module that + # contains that method + def find_symbol(symbol, method=nil) + result = nil + case symbol + when /^::(.*)/ + result = toplevel.find_symbol($1) + when /::/ + modules = symbol.split(/::/) + unless modules.empty? + module_name = modules.shift + result = find_module_named(module_name) + if result + modules.each do |module_name| + result = result.find_module_named(module_name) + break unless result + end + end + end + else + # if a method is specified, then we're definitely looking for + # a module, otherwise it could be any symbol + if method + result = find_module_named(symbol) + else + result = find_local_symbol(symbol) + if result.nil? + if symbol =~ /^[A-Z]/ + result = parent + while result && result.name != symbol + result = result.parent + end + end + end + end + end + if result && method + if !result.respond_to?(:find_local_symbol) + p result.name + p method + fail + end + result = result.find_local_symbol(method) + end + result + end + + def find_local_symbol(symbol) + res = find_method_named(symbol) || + find_constant_named(symbol) || + find_attribute_named(symbol) || + find_module_named(symbol) + end + + # Handle sections + + def set_current_section(title, comment) + @current_section = Section.new(title, comment) + @sections << @current_section + end + + private + + # Find a named method, or return nil + def find_method_named(name) + @method_list.find {|meth| meth.name == name} + end + + # Find a named instance method, or return nil + def find_instance_method_named(name) + @method_list.find {|meth| meth.name == name && !meth.singleton} + end + + # Find a named constant, or return nil + def find_constant_named(name) + @constants.find {|m| m.name == name} + end + + # Find a named attribute, or return nil + def find_attribute_named(name) + @attributes.find {|m| m.name == name} + end + + end + + + # A TopLevel context is a source file + + class TopLevel < Context + attr_accessor :file_stat + attr_accessor :file_relative_name + attr_accessor :file_absolute_name + attr_accessor :diagram + + @@all_classes = {} + @@all_modules = {} + + def TopLevel::reset + @@all_classes = {} + @@all_modules = {} + end + + def initialize(file_name) + super() + @name = "TopLevel" + @file_relative_name = file_name + @file_absolute_name = file_name + @file_stat = File.stat(file_name) + @diagram = nil + end + + def full_name + nil + end + + # Adding a class or module to a TopLevel is special, as we only + # want one copy of a particular top-level class. For example, + # if both file A and file B implement class C, we only want one + # ClassModule object for C. This code arranges to share + # classes and modules between files. + + def add_class_or_module(collection, class_type, name, superclass) + cls = collection[name] + if cls + puts "Reusing class/module #{name}" if $DEBUG + else + if class_type == NormalModule + all = @@all_modules + else + all = @@all_classes + end + cls = all[name] + if !cls + cls = class_type.new(name, superclass) + all[name] = cls unless @done_documenting + end + puts "Adding class/module #{name} to #@name" if $DEBUG + collection[name] = cls unless @done_documenting + cls.parent = self + end + cls + end + + def TopLevel.all_classes_and_modules + @@all_classes.values + @@all_modules.values + end + + def TopLevel.find_class_named(name) + @@all_classes.each_value do |c| + res = c.find_class_named(name) + return res if res + end + nil + end + + def find_local_symbol(symbol) + find_class_or_module_named(symbol) || super + end + + def find_class_or_module_named(symbol) + @@all_classes.each_value {|c| return c if c.name == symbol} + @@all_modules.each_value {|m| return m if m.name == symbol} + nil + end + + # Find a named module + def find_module_named(name) + find_class_or_module_named(name) || find_enclosing_module_named(name) + end + + + end + + # ClassModule is the base class for objects representing either a + # class or a module. + + class ClassModule < Context + + attr_reader :superclass + attr_accessor :diagram + + def initialize(name, superclass = nil) + @name = name + @diagram = nil + @superclass = superclass + @comment = "" + super() + end + + # Return the fully qualified name of this class or module + def full_name + if @parent && @parent.full_name + @parent.full_name + "::" + @name + else + @name + end + end + + def http_url(prefix) + path = full_name.split("::") + File.join(prefix, *path) + ".html" + end + + # Return +true+ if this object represents a module + def is_module? + false + end + + # to_s is simply for debugging + def to_s + res = self.class.name + ": " + @name + res << @comment.to_s + res << super + res + end + + def find_class_named(name) + return self if full_name == name + @classes.each_value {|c| return c if c.find_class_named(name) } + nil + end + end + + # Anonymous classes + class AnonClass < ClassModule + end + + # Normal classes + class NormalClass < ClassModule + end + + # Singleton classes + class SingleClass < ClassModule + end + + # Module + class NormalModule < ClassModule + def is_module? + true + end + end + + + # AnyMethod is the base class for objects representing methods + + class AnyMethod < CodeObject + attr_accessor :name + attr_accessor :visibility + attr_accessor :block_params + attr_accessor :dont_rename_initialize + attr_accessor :singleton + attr_reader :aliases # list of other names for this method + attr_accessor :is_alias_for # or a method we're aliasing + + attr_overridable :params, :param, :parameters, :parameter + + attr_accessor :call_seq + + + include TokenStream + + def initialize(text, name) + super() + @text = text + @name = name + @token_stream = nil + @visibility = :public + @dont_rename_initialize = false + @block_params = nil + @aliases = [] + @is_alias_for = nil + @comment = "" + @call_seq = nil + end + + def <=>(other) + @name <=> other.name + end + + def to_s + res = self.class.name + ": " + @name + " (" + @text + ")\n" + res << @comment.to_s + res + end + + def param_seq + p = params.gsub(/\s*\#.*/, '') + p = p.tr("\n", " ").squeeze(" ") + p = "(" + p + ")" unless p[0] == ?( + + if (block = block_params) + # If this method has explicit block parameters, remove any + # explicit &block +$stderr.puts p + p.sub!(/,?\s*&\w+/) +$stderr.puts p + + block.gsub!(/\s*\#.*/, '') + block = block.tr("\n", " ").squeeze(" ") + if block[0] == ?( + block.sub!(/^\(/, '').sub!(/\)/, '') + end + p << " {|#{block}| ...}" + end + p + end + + def add_alias(method) + @aliases << method + end + end + + + # Represent an alias, which is an old_name/ new_name pair associated + # with a particular context + class Alias < CodeObject + attr_accessor :text, :old_name, :new_name, :comment + + def initialize(text, old_name, new_name, comment) + super() + @text = text + @old_name = old_name + @new_name = new_name + self.comment = comment + end + + def to_s + "alias: #{self.old_name} -> #{self.new_name}\n#{self.comment}" + end + end + + # Represent a constant + class Constant < CodeObject + attr_accessor :name, :value + + def initialize(name, value, comment) + super() + @name = name + @value = value + self.comment = comment + end + end + + # Represent attributes + class Attr < CodeObject + attr_accessor :text, :name, :rw, :visibility + + def initialize(text, name, rw, comment) + super() + @text = text + @name = name + @rw = rw + @visibility = :public + self.comment = comment + end + + def to_s + "attr: #{self.name} #{self.rw}\n#{self.comment}" + end + + def <=>(other) + self.name <=> other.name + end + end + + # a required file + + class Require < CodeObject + attr_accessor :name + + def initialize(name, comment) + super() + @name = name.gsub(/'|"/, "") #' + self.comment = comment + end + + end + + # an included module + class Include < CodeObject + attr_accessor :name + + def initialize(name, comment) + super() + @name = name + self.comment = comment + end + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/diagram.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/diagram.rb new file mode 100644 index 0000000000..9fdc49c02e --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/diagram.rb @@ -0,0 +1,335 @@ +# A wonderful hack by to draw package diagrams using the dot package. +# Originally written by Jah, team Enticla. +# +# You must have the V1.7 or later in your path +# http://www.research.att.com/sw/tools/graphviz/ + +require "rdoc/dot/dot" +require 'rdoc/options' + +module RDoc + + # Draw a set of diagrams representing the modules and classes in the + # system. We draw one diagram for each file, and one for each toplevel + # class or module. This means there will be overlap. However, it also + # means that you'll get better context for objects. + # + # To use, simply + # + # d = Diagram.new(info) # pass in collection of top level infos + # d.draw + # + # The results will be written to the +dot+ subdirectory. The process + # also sets the +diagram+ attribute in each object it graphs to + # the name of the file containing the image. This can be used + # by output generators to insert images. + + class Diagram + + FONT = "Arial" + + DOT_PATH = "dot" + + # Pass in the set of top level objects. The method also creates + # the subdirectory to hold the images + + def initialize(info, options) + @info = info + @options = options + @counter = 0 + File.makedirs(DOT_PATH) + @diagram_cache = {} + end + + # Draw the diagrams. We traverse the files, drawing a diagram for + # each. We also traverse each top-level class and module in that + # file drawing a diagram for these too. + + def draw + unless @options.quiet + $stderr.print "Diagrams: " + $stderr.flush + end + + @info.each_with_index do |i, file_count| + @done_modules = {} + @local_names = find_names(i) + @global_names = [] + @global_graph = graph = DOT::DOTDigraph.new('name' => 'TopLevel', + 'fontname' => FONT, + 'fontsize' => '8', + 'bgcolor' => 'lightcyan1', + 'compound' => 'true') + + # it's a little hack %) i'm too lazy to create a separate class + # for default node + graph << DOT::DOTNode.new('name' => 'node', + 'fontname' => FONT, + 'color' => 'black', + 'fontsize' => 8) + + i.modules.each do |mod| + draw_module(mod, graph, true, i.file_relative_name) + end + add_classes(i, graph, i.file_relative_name) + + i.diagram = convert_to_png("f_#{file_count}", graph) + + # now go through and document each top level class and + # module independently + i.modules.each_with_index do |mod, count| + @done_modules = {} + @local_names = find_names(mod) + @global_names = [] + + @global_graph = graph = DOT::DOTDigraph.new('name' => 'TopLevel', + 'fontname' => FONT, + 'fontsize' => '8', + 'bgcolor' => 'lightcyan1', + 'compound' => 'true') + + graph << DOT::DOTNode.new('name' => 'node', + 'fontname' => FONT, + 'color' => 'black', + 'fontsize' => 8) + draw_module(mod, graph, true) + mod.diagram = convert_to_png("m_#{file_count}_#{count}", + graph) + end + end + $stderr.puts unless @options.quiet + end + + ####### + private + ####### + + def find_names(mod) + return [mod.full_name] + mod.classes.collect{|cl| cl.full_name} + + mod.modules.collect{|m| find_names(m)}.flatten + end + + def find_full_name(name, mod) + full_name = name.dup + return full_name if @local_names.include?(full_name) + mod_path = mod.full_name.split('::')[0..-2] + unless mod_path.nil? + until mod_path.empty? + full_name = mod_path.pop + '::' + full_name + return full_name if @local_names.include?(full_name) + end + end + return name + end + + def draw_module(mod, graph, toplevel = false, file = nil) + return if @done_modules[mod.full_name] and not toplevel + + @counter += 1 + url = mod.http_url("classes") + m = DOT::DOTSubgraph.new('name' => "cluster_#{mod.full_name.gsub( /:/,'_' )}", + 'label' => mod.name, + 'fontname' => FONT, + 'color' => 'blue', + 'style' => 'filled', + 'URL' => %{"#{url}"}, + 'fillcolor' => toplevel ? 'palegreen1' : 'palegreen3') + + @done_modules[mod.full_name] = m + add_classes(mod, m, file) + graph << m + + unless mod.includes.empty? + mod.includes.each do |m| + m_full_name = find_full_name(m.name, mod) + if @local_names.include?(m_full_name) + @global_graph << DOT::DOTEdge.new('from' => "#{m_full_name.gsub( /:/,'_' )}", + 'to' => "#{mod.full_name.gsub( /:/,'_' )}", + 'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}", + 'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}") + else + unless @global_names.include?(m_full_name) + path = m_full_name.split("::") + url = File.join('classes', *path) + ".html" + @global_graph << DOT::DOTNode.new('name' => "#{m_full_name.gsub( /:/,'_' )}", + 'shape' => 'box', + 'label' => "#{m_full_name}", + 'URL' => %{"#{url}"}) + @global_names << m_full_name + end + @global_graph << DOT::DOTEdge.new('from' => "#{m_full_name.gsub( /:/,'_' )}", + 'to' => "#{mod.full_name.gsub( /:/,'_' )}", + 'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}") + end + end + end + end + + def add_classes(container, graph, file = nil ) + + use_fileboxes = Options.instance.fileboxes + + files = {} + + # create dummy node (needed if empty and for module includes) + if container.full_name + graph << DOT::DOTNode.new('name' => "#{container.full_name.gsub( /:/,'_' )}", + 'label' => "", + 'width' => (container.classes.empty? and + container.modules.empty?) ? + '0.75' : '0.01', + 'height' => '0.01', + 'shape' => 'plaintext') + end + container.classes.each_with_index do |cl, cl_index| + last_file = cl.in_files[-1].file_relative_name + + if use_fileboxes && !files.include?(last_file) + @counter += 1 + files[last_file] = + DOT::DOTSubgraph.new('name' => "cluster_#{@counter}", + 'label' => "#{last_file}", + 'fontname' => FONT, + 'color'=> + last_file == file ? 'red' : 'black') + end + + next if cl.name == 'Object' || cl.name[0,2] == "<<" + + url = cl.http_url("classes") + + label = cl.name.dup + if use_fileboxes && cl.in_files.length > 1 + label << '\n[' + + cl.in_files.collect {|i| + i.file_relative_name + }.sort.join( '\n' ) + + ']' + end + + attrs = { + 'name' => "#{cl.full_name.gsub( /:/, '_' )}", + 'fontcolor' => 'black', + 'style'=>'filled', + 'color'=>'palegoldenrod', + 'label' => label, + 'shape' => 'ellipse', + 'URL' => %{"#{url}"} + } + + c = DOT::DOTNode.new(attrs) + + if use_fileboxes + files[last_file].push c + else + graph << c + end + end + + if use_fileboxes + files.each_value do |val| + graph << val + end + end + + unless container.classes.empty? + container.classes.each_with_index do |cl, cl_index| + cl.includes.each do |m| + m_full_name = find_full_name(m.name, cl) + if @local_names.include?(m_full_name) + @global_graph << DOT::DOTEdge.new('from' => "#{m_full_name.gsub( /:/,'_' )}", + 'to' => "#{cl.full_name.gsub( /:/,'_' )}", + 'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}") + else + unless @global_names.include?(m_full_name) + path = m_full_name.split("::") + url = File.join('classes', *path) + ".html" + @global_graph << DOT::DOTNode.new('name' => "#{m_full_name.gsub( /:/,'_' )}", + 'shape' => 'box', + 'label' => "#{m_full_name}", + 'URL' => %{"#{url}"}) + @global_names << m_full_name + end + @global_graph << DOT::DOTEdge.new('from' => "#{m_full_name.gsub( /:/,'_' )}", + 'to' => "#{cl.full_name.gsub( /:/, '_')}") + end + end + + sclass = cl.superclass + next if sclass.nil? || sclass == 'Object' + sclass_full_name = find_full_name(sclass,cl) + unless @local_names.include?(sclass_full_name) or @global_names.include?(sclass_full_name) + path = sclass_full_name.split("::") + url = File.join('classes', *path) + ".html" + @global_graph << DOT::DOTNode.new( + 'name' => "#{sclass_full_name.gsub( /:/, '_' )}", + 'label' => sclass_full_name, + 'URL' => %{"#{url}"}) + @global_names << sclass_full_name + end + @global_graph << DOT::DOTEdge.new('from' => "#{sclass_full_name.gsub( /:/,'_' )}", + 'to' => "#{cl.full_name.gsub( /:/, '_')}") + end + end + + container.modules.each do |submod| + draw_module(submod, graph) + end + + end + + def convert_to_png(file_base, graph) + str = graph.to_s + return @diagram_cache[str] if @diagram_cache[str] + op_type = Options.instance.image_format + dotfile = File.join(DOT_PATH, file_base) + src = dotfile + ".dot" + dot = dotfile + "." + op_type + + unless @options.quiet + $stderr.print "." + $stderr.flush + end + + File.open(src, 'w+' ) do |f| + f << str << "\n" + end + + system "dot", "-T#{op_type}", src, "-o", dot + + # Now construct the imagemap wrapper around + # that png + + ret = wrap_in_image_map(src, dot) + @diagram_cache[str] = ret + return ret + end + + # Extract the client-side image map from dot, and use it + # to generate the imagemap proper. Return the whole + # .. combination, suitable for inclusion on + # the page + + def wrap_in_image_map(src, dot) + res = %{\n} + dot_map = `dot -Tismap #{src}` + dot_map.each do |area| + unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/ + $stderr.puts "Unexpected output from dot:\n#{area}" + return nil + end + + xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i] + url, area_name = $5, $6 + + res << %{ #{area_name}\n} + end + res << "\n" +# map_file = src.sub(/.dot/, '.map') +# system("dot -Timap #{src} -o #{map_file}") + res << %{#{dot}} + return res + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/dot/dot.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/dot/dot.rb new file mode 100644 index 0000000000..6dbb7cb237 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/dot/dot.rb @@ -0,0 +1,255 @@ +module DOT + + # these glogal vars are used to make nice graph source + $tab = ' ' + $tab2 = $tab * 2 + + # if we don't like 4 spaces, we can change it any time + def change_tab( t ) + $tab = t + $tab2 = t * 2 + end + + # options for node declaration + NODE_OPTS = [ + 'bgcolor', + 'color', + 'fontcolor', + 'fontname', + 'fontsize', + 'height', + 'width', + 'label', + 'layer', + 'rank', + 'shape', + 'shapefile', + 'style', + 'URL', + ] + + # options for edge declaration + EDGE_OPTS = [ + 'color', + 'decorate', + 'dir', + 'fontcolor', + 'fontname', + 'fontsize', + 'id', + 'label', + 'layer', + 'lhead', + 'ltail', + 'minlen', + 'style', + 'weight' + ] + + # options for graph declaration + GRAPH_OPTS = [ + 'bgcolor', + 'center', + 'clusterrank', + 'color', + 'compound', + 'concentrate', + 'fillcolor', + 'fontcolor', + 'fontname', + 'fontsize', + 'label', + 'layerseq', + 'margin', + 'mclimit', + 'nodesep', + 'nslimit', + 'ordering', + 'orientation', + 'page', + 'rank', + 'rankdir', + 'ranksep', + 'ratio', + 'size', + 'style', + 'URL' + ] + + # a root class for any element in dot notation + class DOTSimpleElement + attr_accessor :name + + def initialize( params = {} ) + @label = params['name'] ? params['name'] : '' + end + + def to_s + @name + end + end + + # an element that has options ( node, edge or graph ) + class DOTElement < DOTSimpleElement + #attr_reader :parent + attr_accessor :name, :options + + def initialize( params = {}, option_list = [] ) + super( params ) + @name = params['name'] ? params['name'] : nil + @parent = params['parent'] ? params['parent'] : nil + @options = {} + option_list.each{ |i| + @options[i] = params[i] if params[i] + } + @options['label'] ||= @name if @name != 'node' + end + + def each_option + @options.each{ |i| yield i } + end + + def each_option_pair + @options.each_pair{ |key, val| yield key, val } + end + + #def parent=( thing ) + # @parent.delete( self ) if defined?( @parent ) and @parent + # @parent = thing + #end + end + + + # this is used when we build nodes that have shape=record + # ports don't have options :) + class DOTPort < DOTSimpleElement + attr_accessor :label + + def initialize( params = {} ) + super( params ) + @name = params['label'] ? params['label'] : '' + end + def to_s + ( @name && @name != "" ? "<#{@name}>" : "" ) + "#{@label}" + end + end + + # node element + class DOTNode < DOTElement + + def initialize( params = {}, option_list = NODE_OPTS ) + super( params, option_list ) + @ports = params['ports'] ? params['ports'] : [] + end + + def each_port + @ports.each{ |i| yield i } + end + + def << ( thing ) + @ports << thing + end + + def push ( thing ) + @ports.push( thing ) + end + + def pop + @ports.pop + end + + def to_s( t = '' ) + + label = @options['shape'] != 'record' && @ports.length == 0 ? + @options['label'] ? + t + $tab + "label = \"#{@options['label']}\"\n" : + '' : + t + $tab + 'label = "' + " \\\n" + + t + $tab2 + "#{@options['label']}| \\\n" + + @ports.collect{ |i| + t + $tab2 + i.to_s + }.join( "| \\\n" ) + " \\\n" + + t + $tab + '"' + "\n" + + t + "#{@name} [\n" + + @options.to_a.collect{ |i| + i[1] && i[0] != 'label' ? + t + $tab + "#{i[0]} = #{i[1]}" : nil + }.compact.join( ",\n" ) + ( label != '' ? ",\n" : "\n" ) + + label + + t + "]\n" + end + end + + # subgraph element is the same to graph, but has another header in dot + # notation + class DOTSubgraph < DOTElement + + def initialize( params = {}, option_list = GRAPH_OPTS ) + super( params, option_list ) + @nodes = params['nodes'] ? params['nodes'] : [] + @dot_string = 'subgraph' + end + + def each_node + @nodes.each{ |i| yield i } + end + + def << ( thing ) + @nodes << thing + end + + def push( thing ) + @nodes.push( thing ) + end + + def pop + @nodes.pop + end + + def to_s( t = '' ) + hdr = t + "#{@dot_string} #{@name} {\n" + + options = @options.to_a.collect{ |name, val| + val && name != 'label' ? + t + $tab + "#{name} = #{val}" : + name ? t + $tab + "#{name} = \"#{val}\"" : nil + }.compact.join( "\n" ) + "\n" + + nodes = @nodes.collect{ |i| + i.to_s( t + $tab ) + }.join( "\n" ) + "\n" + hdr + options + nodes + t + "}\n" + end + end + + # this is graph + class DOTDigraph < DOTSubgraph + def initialize( params = {}, option_list = GRAPH_OPTS ) + super( params, option_list ) + @dot_string = 'digraph' + end + end + + # this is edge + class DOTEdge < DOTElement + attr_accessor :from, :to + def initialize( params = {}, option_list = EDGE_OPTS ) + super( params, option_list ) + @from = params['from'] ? params['from'] : nil + @to = params['to'] ? params['to'] : nil + end + + def to_s( t = '' ) + t + "#{@from} -> #{to} [\n" + + @options.to_a.collect{ |i| + i[1] && i[0] != 'label' ? + t + $tab + "#{i[0]} = #{i[1]}" : + i[1] ? t + $tab + "#{i[0]} = \"#{i[1]}\"" : nil + }.compact.join( "\n" ) + "\n" + t + "]\n" + end + end +end + + + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/chm_generator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/chm_generator.rb new file mode 100644 index 0000000000..51eeda8dd1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/chm_generator.rb @@ -0,0 +1,112 @@ +require 'rdoc/generators/html_generator' + +module Generators + + class CHMGenerator < HTMLGenerator + + HHC_PATH = "c:/Program Files/HTML Help Workshop/hhc.exe" + + # Standard generator factory + def CHMGenerator.for(options) + CHMGenerator.new(options) + end + + + def initialize(*args) + super + @op_name = @options.op_name || "rdoc" + check_for_html_help_workshop + end + + def check_for_html_help_workshop + stat = File.stat(HHC_PATH) + rescue + $stderr << + "\n.chm output generation requires that Microsoft's Html Help\n" << + "Workshop is installed. RDoc looks for it in:\n\n " << + HHC_PATH << + "\n\nYou can download a copy for free from:\n\n" << + " http://msdn.microsoft.com/library/default.asp?" << + "url=/library/en-us/htmlhelp/html/hwMicrosoftHTMLHelpDownloads.asp\n\n" + + exit 99 + end + + # Generate the html as normal, then wrap it + # in a help project + def generate(info) + super + @project_name = @op_name + ".hhp" + create_help_project + end + + # The project contains the project file, a table of contents + # and an index + def create_help_project + create_project_file + create_contents_and_index + compile_project + end + + # The project file links together all the various + # files that go to make up the help. + + def create_project_file + template = TemplatePage.new(RDoc::Page::HPP_FILE) + values = { "title" => @options.title, "opname" => @op_name } + files = [] + @files.each do |f| + files << { "html_file_name" => f.path } + end + + values['all_html_files'] = files + + File.open(@project_name, "w") do |f| + template.write_html_on(f, values) + end + end + + # The contents is a list of all files and modules. + # For each we include as sub-entries the list + # of methods they contain. As we build the contents + # we also build an index file + + def create_contents_and_index + contents = [] + index = [] + + (@files+@classes).sort.each do |entry| + content_entry = { "c_name" => entry.name, "ref" => entry.path } + index << { "name" => entry.name, "aref" => entry.path } + + internals = [] + + methods = entry.build_method_summary_list(entry.path) + + content_entry["methods"] = methods unless methods.empty? + contents << content_entry + index.concat methods + end + + values = { "contents" => contents } + template = TemplatePage.new(RDoc::Page::CONTENTS) + File.open("contents.hhc", "w") do |f| + template.write_html_on(f, values) + end + + values = { "index" => index } + template = TemplatePage.new(RDoc::Page::CHM_INDEX) + File.open("index.hhk", "w") do |f| + template.write_html_on(f, values) + end + end + + # Invoke the windows help compiler to compiler the project + def compile_project + system(HHC_PATH, @project_name) + end + + end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/html_generator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/html_generator.rb new file mode 100644 index 0000000000..1f9b808e8d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/html_generator.rb @@ -0,0 +1,1509 @@ +# We're responsible for generating all the HTML files +# from the object tree defined in code_objects.rb. We +# generate: +# +# [files] an html file for each input file given. These +# input files appear as objects of class +# TopLevel +# +# [classes] an html file for each class or module encountered. +# These classes are not grouped by file: if a file +# contains four classes, we'll generate an html +# file for the file itself, and four html files +# for the individual classes. +# +# [indices] we generate three indices for files, classes, +# and methods. These are displayed in a browser +# like window with three index panes across the +# top and the selected description below +# +# Method descriptions appear in whatever entity (file, class, +# or module) that contains them. +# +# We generate files in a structure below a specified subdirectory, +# normally +doc+. +# +# opdir +# | +# |___ files +# | |__ per file summaries +# | +# |___ classes +# |__ per class/module descriptions +# +# HTML is generated using the Template class. +# + +require 'ftools' + +require 'rdoc/options' +require 'rdoc/template' +require 'rdoc/markup/simple_markup' +require 'rdoc/markup/simple_markup/to_html' +require 'cgi' + +module Generators + + # Name of sub-direcories that hold file and class/module descriptions + + FILE_DIR = "files" + CLASS_DIR = "classes" + CSS_NAME = "rdoc-style.css" + + + ## + # Build a hash of all items that can be cross-referenced. + # This is used when we output required and included names: + # if the names appear in this hash, we can generate + # an html cross reference to the appropriate description. + # We also use this when parsing comment blocks: any decorated + # words matching an entry in this list are hyperlinked. + + class AllReferences + @@refs = {} + + def AllReferences::reset + @@refs = {} + end + + def AllReferences.add(name, html_class) + @@refs[name] = html_class + end + + def AllReferences.[](name) + @@refs[name] + end + + def AllReferences.keys + @@refs.keys + end + end + + + ## + # Subclass of the SM::ToHtml class that supports looking + # up words in the AllReferences list. Those that are + # found (like AllReferences in this comment) will + # be hyperlinked + + class HyperlinkHtml < SM::ToHtml + # We need to record the html path of our caller so we can generate + # correct relative paths for any hyperlinks that we find + def initialize(from_path, context) + super() + @from_path = from_path + + @parent_name = context.parent_name + @parent_name += "::" if @parent_name + @context = context + end + + # We're invoked when any text matches the CROSSREF pattern + # (defined in MarkUp). If we fine the corresponding reference, + # generate a hyperlink. If the name we're looking for contains + # no punctuation, we look for it up the module/class chain. For + # example, HyperlinkHtml is found, even without the Generators:: + # prefix, because we look for it in module Generators first. + + def handle_special_CROSSREF(special) + name = special.text + if name[0,1] == '#' + lookup = name[1..-1] + name = lookup unless Options.instance.show_hash + else + lookup = name + end + + # Find class, module, or method in class or module. + if /([A-Z]\w*)[.\#](\w+[!?=]?)/ =~ lookup + container = $1 + method = $2 + ref = @context.find_symbol(container, method) + elsif /([A-Za-z]\w*)[.\#](\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?)/ =~ lookup + container = $1 + method = $2 + ref = @context.find_symbol(container, method) + else + ref = @context.find_symbol(lookup) + end + + if ref and ref.document_self + "#{name}" + else + name + end + end + + + # Generate a hyperlink for url, labeled with text. Handle the + # special cases for img: and link: described under handle_special_HYPEDLINK + def gen_url(url, text) + if url =~ /([A-Za-z]+):(.*)/ + type = $1 + path = $2 + else + type = "http" + path = url + url = "http://#{url}" + end + + if type == "link" + if path[0,1] == '#' # is this meaningful? + url = path + else + url = HTMLGenerator.gen_url(@from_path, path) + end + end + + if (type == "http" || type == "link") && + url =~ /\.(gif|png|jpg|jpeg|bmp)$/ + + "" + else + "#{text.sub(%r{^#{type}:/*}, '')}" + end + end + + # And we're invoked with a potential external hyperlink mailto: + # just gets inserted. http: links are checked to see if they + # reference an image. If so, that image gets inserted using an + # tag. Otherwise a conventional is used. We also + # support a special type of hyperlink, link:, which is a reference + # to a local file whose path is relative to the --op directory. + + def handle_special_HYPERLINK(special) + url = special.text + gen_url(url, url) + end + + # HEre's a hypedlink where the label is different to the URL + #

/, '') + res.sub!(/<\/p>$/, '') + end + res + end + + # Qualify a stylesheet URL; if if +css_name+ does not begin with '/' or + # 'http[s]://', prepend a prefix relative to +path+. Otherwise, return it + # unmodified. + + def style_url(path, css_name=nil) +# $stderr.puts "style_url( #{path.inspect}, #{css_name.inspect} )" + css_name ||= CSS_NAME + if %r{^(https?:/)?/} =~ css_name + return css_name + else + return HTMLGenerator.gen_url(path, css_name) + end + end + + # Build a webcvs URL with the given 'url' argument. URLs with a '%s' in them + # get the file's path sprintfed into them; otherwise they're just catenated + # together. + + def cvs_url(url, full_path) + if /%s/ =~ url + return sprintf( url, full_path ) + else + return url + full_path + end + end + end + + + ##################################################################### + # + # A Context is built by the parser to represent a container: contexts + # hold classes, modules, methods, require lists and include lists. + # ClassModule and TopLevel are the context objects we process here + # + class ContextUser + + include MarkUp + + attr_reader :context + + def initialize(context, options) + @context = context + @options = options + end + + # convenience method to build a hyperlink + def href(link, cls, name) + %{#{name}} #" + end + + # return a reference to outselves to be used as an href= + # the form depends on whether we're all in one file + # or in multiple files + + def as_href(from_path) + if @options.all_one_file + "#" + path + else + HTMLGenerator.gen_url(from_path, path) + end + end + + # Create a list of HtmlMethod objects for each method + # in the corresponding context object. If the @options.show_all + # variable is set (corresponding to the --all option, + # we include all methods, otherwise just the public ones. + + def collect_methods + list = @context.method_list + unless @options.show_all + list = list.find_all {|m| m.visibility == :public || m.visibility == :protected || m.force_documentation } + end + @methods = list.collect {|m| HtmlMethod.new(m, self, @options) } + end + + # Build a summary list of all the methods in this context + def build_method_summary_list(path_prefix="") + collect_methods unless @methods + meths = @methods.sort + res = [] + meths.each do |meth| + res << { + "name" => CGI.escapeHTML(meth.name), + "aref" => "#{path_prefix}\##{meth.aref}" + } + end + res + end + + + # Build a list of aliases for which we couldn't find a + # corresponding method + def build_alias_summary_list(section) + values = [] + @context.aliases.each do |al| + next unless al.section == section + res = { + 'old_name' => al.old_name, + 'new_name' => al.new_name, + } + if al.comment && !al.comment.empty? + res['desc'] = markup(al.comment, true) + end + values << res + end + values + end + + # Build a list of constants + def build_constants_summary_list(section) + values = [] + @context.constants.each do |co| + next unless co.section == section + res = { + 'name' => co.name, + 'value' => CGI.escapeHTML(co.value) + } + res['desc'] = markup(co.comment, true) if co.comment && !co.comment.empty? + values << res + end + values + end + + def build_requires_list(context) + potentially_referenced_list(context.requires) {|fn| [fn + ".rb"] } + end + + def build_include_list(context) + potentially_referenced_list(context.includes) + end + + # Build a list from an array of Htmlxxx items. Look up each + # in the AllReferences hash: if we find a corresponding entry, + # we generate a hyperlink to it, otherwise just output the name. + # However, some names potentially need massaging. For example, + # you may require a Ruby file without the .rb extension, + # but the file names we know about may have it. To deal with + # this, we pass in a block which performs the massaging, + # returning an array of alternative names to match + + def potentially_referenced_list(array) + res = [] + array.each do |i| + ref = AllReferences[i.name] +# if !ref +# container = @context.parent +# while !ref && container +# name = container.name + "::" + i.name +# ref = AllReferences[name] +# container = container.parent +# end +# end + + ref = @context.find_symbol(i.name) + ref = ref.viewer if ref + + if !ref && block_given? + possibles = yield(i.name) + while !ref and !possibles.empty? + ref = AllReferences[possibles.shift] + end + end + h_name = CGI.escapeHTML(i.name) + if ref and ref.document_self + path = url(ref.path) + res << { "name" => h_name, "aref" => path } + else + res << { "name" => h_name } + end + end + res + end + + # Build an array of arrays of method details. The outer array has up + # to six entries, public, private, and protected for both class + # methods, the other for instance methods. The inner arrays contain + # a hash for each method + + def build_method_detail_list(section) + outer = [] + + methods = @methods.sort + for singleton in [true, false] + for vis in [ :public, :protected, :private ] + res = [] + methods.each do |m| + if m.section == section and + m.document_self and + m.visibility == vis and + m.singleton == singleton + row = {} + if m.call_seq + row["callseq"] = m.call_seq.gsub(/->/, '→') + else + row["name"] = CGI.escapeHTML(m.name) + row["params"] = m.params + end + desc = m.description.strip + row["m_desc"] = desc unless desc.empty? + row["aref"] = m.aref + row["visibility"] = m.visibility.to_s + + alias_names = [] + m.aliases.each do |other| + if other.viewer # won't be if the alias is private + alias_names << { + 'name' => other.name, + 'aref' => other.viewer.as_href(path) + } + end + end + unless alias_names.empty? + row["aka"] = alias_names + end + + if @options.inline_source + code = m.source_code + row["sourcecode"] = code if code + else + code = m.src_url + if code + row["codeurl"] = code + row["imgurl"] = m.img_url + end + end + res << row + end + end + if res.size > 0 + outer << { + "type" => vis.to_s.capitalize, + "category" => singleton ? "Class" : "Instance", + "methods" => res + } + end + end + end + outer + end + + # Build the structured list of classes and modules contained + # in this context. + + def build_class_list(level, from, section, infile=nil) + res = "" + prefix = "  ::" * level; + + from.modules.sort.each do |mod| + next unless mod.section == section + next if infile && !mod.defined_in?(infile) + if mod.document_self + res << + prefix << + "Module " << + href(url(mod.viewer.path), "link", mod.full_name) << + "
\n" << + build_class_list(level + 1, mod, section, infile) + end + end + + from.classes.sort.each do |cls| + next unless cls.section == section + next if infile && !cls.defined_in?(infile) + if cls.document_self + res << + prefix << + "Class " << + href(url(cls.viewer.path), "link", cls.full_name) << + "
\n" << + build_class_list(level + 1, cls, section, infile) + end + end + + res + end + + def url(target) + HTMLGenerator.gen_url(path, target) + end + + def aref_to(target) + if @options.all_one_file + "#" + target + else + url(target) + end + end + + def document_self + @context.document_self + end + + def diagram_reference(diagram) + res = diagram.gsub(/((?:src|href)=")(.*?)"/) { + $1 + url($2) + '"' + } + res + end + + + # Find a symbol in ourselves or our parent + def find_symbol(symbol, method=nil) + res = @context.find_symbol(symbol, method) + if res + res = res.viewer + end + res + end + + # create table of contents if we contain sections + + def add_table_of_sections + toc = [] + @context.sections.each do |section| + if section.title + toc << { + 'secname' => section.title, + 'href' => section.sequence + } + end + end + + @values['toc'] = toc unless toc.empty? + end + + + end + + ##################################################################### + # + # Wrap a ClassModule context + + class HtmlClass < ContextUser + + attr_reader :path + + def initialize(context, html_file, prefix, options) + super(context, options) + + @html_file = html_file + @is_module = context.is_module? + @values = {} + + context.viewer = self + + if options.all_one_file + @path = context.full_name + else + @path = http_url(context.full_name, prefix) + end + + collect_methods + + AllReferences.add(name, self) + end + + # return the relative file name to store this class in, + # which is also its url + def http_url(full_name, prefix) + path = full_name.dup + if path['<<'] + path.gsub!(/<<\s*(\w*)/) { "from-#$1" } + end + File.join(prefix, path.split("::")) + ".html" + end + + + def name + @context.full_name + end + + def parent_name + @context.parent.full_name + end + + def index_name + name + end + + def write_on(f) + value_hash + template = TemplatePage.new(RDoc::Page::BODY, + RDoc::Page::CLASS_PAGE, + RDoc::Page::METHOD_LIST) + template.write_html_on(f, @values) + end + + def value_hash + class_attribute_values + add_table_of_sections + + @values["charset"] = @options.charset + @values["style_url"] = style_url(path, @options.css) + + d = markup(@context.comment) + @values["description"] = d unless d.empty? + + ml = build_method_summary_list + @values["methods"] = ml unless ml.empty? + + il = build_include_list(@context) + @values["includes"] = il unless il.empty? + + @values["sections"] = @context.sections.map do |section| + + secdata = { + "sectitle" => section.title, + "secsequence" => section.sequence, + "seccomment" => markup(section.comment) + } + + al = build_alias_summary_list(section) + secdata["aliases"] = al unless al.empty? + + co = build_constants_summary_list(section) + secdata["constants"] = co unless co.empty? + + al = build_attribute_list(section) + secdata["attributes"] = al unless al.empty? + + cl = build_class_list(0, @context, section) + secdata["classlist"] = cl unless cl.empty? + + mdl = build_method_detail_list(section) + secdata["method_list"] = mdl unless mdl.empty? + + secdata + end + + @values + end + + def build_attribute_list(section) + atts = @context.attributes.sort + res = [] + atts.each do |att| + next unless att.section == section + if att.visibility == :public || att.visibility == :protected || @options.show_all + entry = { + "name" => CGI.escapeHTML(att.name), + "rw" => att.rw, + "a_desc" => markup(att.comment, true) + } + unless att.visibility == :public || att.visibility == :protected + entry["rw"] << "-" + end + res << entry + end + end + res + end + + def class_attribute_values + h_name = CGI.escapeHTML(name) + + @values["classmod"] = @is_module ? "Module" : "Class" + @values["title"] = "#{@values['classmod']}: #{h_name}" + + c = @context + c = c.parent while c and !c.diagram + if c && c.diagram + @values["diagram"] = diagram_reference(c.diagram) + end + + @values["full_name"] = h_name + + parent_class = @context.superclass + + if parent_class + @values["parent"] = CGI.escapeHTML(parent_class) + + if parent_name + lookup = parent_name + "::" + parent_class + else + lookup = parent_class + end + + parent_url = AllReferences[lookup] || AllReferences[parent_class] + + if parent_url and parent_url.document_self + @values["par_url"] = aref_to(parent_url.path) + end + end + + files = [] + @context.in_files.each do |f| + res = {} + full_path = CGI.escapeHTML(f.file_absolute_name) + + res["full_path"] = full_path + res["full_path_url"] = aref_to(f.viewer.path) if f.document_self + + if @options.webcvs + res["cvsurl"] = cvs_url( @options.webcvs, full_path ) + end + + files << res + end + + @values['infiles'] = files + end + + def <=>(other) + self.name <=> other.name + end + + end + + ##################################################################### + # + # Handles the mapping of a file's information to HTML. In reality, + # a file corresponds to a +TopLevel+ object, containing modules, + # classes, and top-level methods. In theory it _could_ contain + # attributes and aliases, but we ignore these for now. + + class HtmlFile < ContextUser + + attr_reader :path + attr_reader :name + + def initialize(context, options, file_dir) + super(context, options) + + @values = {} + + if options.all_one_file + @path = filename_to_label + else + @path = http_url(file_dir) + end + + @name = @context.file_relative_name + + collect_methods + AllReferences.add(name, self) + context.viewer = self + end + + def http_url(file_dir) + File.join(file_dir, @context.file_relative_name.tr('.', '_')) + + ".html" + end + + def filename_to_label + @context.file_relative_name.gsub(/%|\/|\?|\#/) {|s| '%' + ("%x" % s[0]) } + end + + def index_name + name + end + + def parent_name + nil + end + + def value_hash + file_attribute_values + add_table_of_sections + + @values["charset"] = @options.charset + @values["href"] = path + @values["style_url"] = style_url(path, @options.css) + + if @context.comment + d = markup(@context.comment) + @values["description"] = d if d.size > 0 + end + + ml = build_method_summary_list + @values["methods"] = ml unless ml.empty? + + il = build_include_list(@context) + @values["includes"] = il unless il.empty? + + rl = build_requires_list(@context) + @values["requires"] = rl unless rl.empty? + + if @options.promiscuous + file_context = nil + else + file_context = @context + end + + + @values["sections"] = @context.sections.map do |section| + + secdata = { + "sectitle" => section.title, + "secsequence" => section.sequence, + "seccomment" => markup(section.comment) + } + + cl = build_class_list(0, @context, section, file_context) + @values["classlist"] = cl unless cl.empty? + + mdl = build_method_detail_list(section) + secdata["method_list"] = mdl unless mdl.empty? + + al = build_alias_summary_list(section) + secdata["aliases"] = al unless al.empty? + + co = build_constants_summary_list(section) + @values["constants"] = co unless co.empty? + + secdata + end + + @values + end + + def write_on(f) + value_hash + template = TemplatePage.new(RDoc::Page::BODY, + RDoc::Page::FILE_PAGE, + RDoc::Page::METHOD_LIST) + template.write_html_on(f, @values) + end + + def file_attribute_values + full_path = @context.file_absolute_name + short_name = File.basename(full_path) + + @values["title"] = CGI.escapeHTML("File: #{short_name}") + + if @context.diagram + @values["diagram"] = diagram_reference(@context.diagram) + end + + @values["short_name"] = CGI.escapeHTML(short_name) + @values["full_path"] = CGI.escapeHTML(full_path) + @values["dtm_modified"] = @context.file_stat.mtime.to_s + + if @options.webcvs + @values["cvsurl"] = cvs_url( @options.webcvs, @values["full_path"] ) + end + end + + def <=>(other) + self.name <=> other.name + end + end + + ##################################################################### + + class HtmlMethod + include MarkUp + + attr_reader :context + attr_reader :src_url + attr_reader :img_url + attr_reader :source_code + + @@seq = "M000000" + + @@all_methods = [] + + def HtmlMethod::reset + @@all_methods = [] + end + + def initialize(context, html_class, options) + @context = context + @html_class = html_class + @options = options + @@seq = @@seq.succ + @seq = @@seq + @@all_methods << self + + context.viewer = self + + if (ts = @context.token_stream) + @source_code = markup_code(ts) + unless @options.inline_source + @src_url = create_source_code_file(@source_code) + @img_url = HTMLGenerator.gen_url(path, 'source.png') + end + end + + AllReferences.add(name, self) + end + + # return a reference to outselves to be used as an href= + # the form depends on whether we're all in one file + # or in multiple files + + def as_href(from_path) + if @options.all_one_file + "#" + path + else + HTMLGenerator.gen_url(from_path, path) + end + end + + def name + @context.name + end + + def section + @context.section + end + + def index_name + "#{@context.name} (#{@html_class.name})" + end + + def parent_name + if @context.parent.parent + @context.parent.parent.full_name + else + nil + end + end + + def aref + @seq + end + + def path + if @options.all_one_file + aref + else + @html_class.path + "#" + aref + end + end + + def description + markup(@context.comment) + end + + def visibility + @context.visibility + end + + def singleton + @context.singleton + end + + def call_seq + cs = @context.call_seq + if cs + cs.gsub(/\n/, "
\n") + else + nil + end + end + + def params + # params coming from a call-seq in 'C' will start with the + # method name + p = @context.params + if p !~ /^\w/ + p = @context.params.gsub(/\s*\#.*/, '') + p = p.tr("\n", " ").squeeze(" ") + p = "(" + p + ")" unless p[0] == ?( + + if (block = @context.block_params) + # If this method has explicit block parameters, remove any + # explicit &block + + p.sub!(/,?\s*&\w+/, '') + + block.gsub!(/\s*\#.*/, '') + block = block.tr("\n", " ").squeeze(" ") + if block[0] == ?( + block.sub!(/^\(/, '').sub!(/\)/, '') + end + p << " {|#{block.strip}| ...}" + end + end + CGI.escapeHTML(p) + end + + def create_source_code_file(code_body) + meth_path = @html_class.path.sub(/\.html$/, '.src') + File.makedirs(meth_path) + file_path = File.join(meth_path, @seq) + ".html" + + template = TemplatePage.new(RDoc::Page::SRC_PAGE) + File.open(file_path, "w") do |f| + values = { + 'title' => CGI.escapeHTML(index_name), + 'code' => code_body, + 'style_url' => style_url(file_path, @options.css), + 'charset' => @options.charset + } + template.write_html_on(f, values) + end + HTMLGenerator.gen_url(path, file_path) + end + + def HtmlMethod.all_methods + @@all_methods + end + + def <=>(other) + @context <=> other.context + end + + ## + # Given a sequence of source tokens, mark up the source code + # to make it look purty. + + + def markup_code(tokens) + src = "" + tokens.each do |t| + next unless t + # p t.class +# style = STYLE_MAP[t.class] + style = case t + when RubyToken::TkCONSTANT then "ruby-constant" + when RubyToken::TkKW then "ruby-keyword kw" + when RubyToken::TkIVAR then "ruby-ivar" + when RubyToken::TkOp then "ruby-operator" + when RubyToken::TkId then "ruby-identifier" + when RubyToken::TkNode then "ruby-node" + when RubyToken::TkCOMMENT then "ruby-comment cmt" + when RubyToken::TkREGEXP then "ruby-regexp re" + when RubyToken::TkSTRING then "ruby-value str" + when RubyToken::TkVal then "ruby-value" + else + nil + end + + text = CGI.escapeHTML(t.text) + + if style + src << "#{text}" + else + src << text + end + end + + add_line_numbers(src) if Options.instance.include_line_numbers + src + end + + # we rely on the fact that the first line of a source code + # listing has + # # File xxxxx, line dddd + + def add_line_numbers(src) + if src =~ /\A.*, line (\d+)/ + first = $1.to_i - 1 + last = first + src.count("\n") + size = last.to_s.length + real_fmt = "%#{size}d: " + fmt = " " * (size+2) + src.gsub!(/^/) do + res = sprintf(fmt, first) + first += 1 + fmt = real_fmt + res + end + end + end + + def document_self + @context.document_self + end + + def aliases + @context.aliases + end + + def find_symbol(symbol, method=nil) + res = @context.parent.find_symbol(symbol, method) + if res + res = res.viewer + end + res + end + end + + ##################################################################### + + class HTMLGenerator + + include MarkUp + + ## + # convert a target url to one that is relative to a given + # path + + def HTMLGenerator.gen_url(path, target) + from = File.dirname(path) + to, to_file = File.split(target) + + from = from.split("/") + to = to.split("/") + + while from.size > 0 and to.size > 0 and from[0] == to[0] + from.shift + to.shift + end + + from.fill("..") + from.concat(to) + from << to_file + File.join(*from) + end + + # Generators may need to return specific subclasses depending + # on the options they are passed. Because of this + # we create them using a factory + + def HTMLGenerator.for(options) + AllReferences::reset + HtmlMethod::reset + + if options.all_one_file + HTMLGeneratorInOne.new(options) + else + HTMLGenerator.new(options) + end + end + + class < RDoc::Page::FONTS } + template.write_html_on(f, values) + end + end + end + + ## + # See the comments at the top for a description of the + # directory structure + + def gen_sub_directories + File.makedirs(FILE_DIR, CLASS_DIR) + rescue + $stderr.puts $!.message + exit 1 + end + + ## + # Generate: + # + # * a list of HtmlFile objects for each TopLevel object. + # * a list of HtmlClass objects for each first level + # class or module in the TopLevel objects + # * a complete list of all hyperlinkable terms (file, + # class, module, and method names) + + def build_indices + + @toplevels.each do |toplevel| + @files << HtmlFile.new(toplevel, @options, FILE_DIR) + end + + RDoc::TopLevel.all_classes_and_modules.each do |cls| + build_class_list(cls, @files[0], CLASS_DIR) + end + end + + def build_class_list(from, html_file, class_dir) + @classes << HtmlClass.new(from, html_file, class_dir, @options) + from.each_classmodule do |mod| + build_class_list(mod, html_file, class_dir) + end + end + + ## + # Generate all the HTML + # + def generate_html + # the individual descriptions for files and classes + gen_into(@files) + gen_into(@classes) + # and the index files + gen_file_index + gen_class_index + gen_method_index + gen_main_index + + # this method is defined in the template file + write_extra_pages if defined? write_extra_pages + end + + def gen_into(list) + list.each do |item| + if item.document_self + op_file = item.path + File.makedirs(File.dirname(op_file)) + File.open(op_file, "w") { |file| item.write_on(file) } + end + end + + end + + def gen_file_index + gen_an_index(@files, 'Files', + RDoc::Page::FILE_INDEX, + "fr_file_index.html") + end + + def gen_class_index + gen_an_index(@classes, 'Classes', + RDoc::Page::CLASS_INDEX, + "fr_class_index.html") + end + + def gen_method_index + gen_an_index(HtmlMethod.all_methods, 'Methods', + RDoc::Page::METHOD_INDEX, + "fr_method_index.html") + end + + + def gen_an_index(collection, title, template, filename) + template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template) + res = [] + collection.sort.each do |f| + if f.document_self + res << { "href" => f.path, "name" => f.index_name } + end + end + + values = { + "entries" => res, + 'list_title' => CGI.escapeHTML(title), + 'index_url' => main_url, + 'charset' => @options.charset, + 'style_url' => style_url('', @options.css), + } + + File.open(filename, "w") do |f| + template.write_html_on(f, values) + end + end + + # The main index page is mostly a template frameset, but includes + # the initial page. If the --main option was given, + # we use this as our main page, otherwise we use the + # first file specified on the command line. + + def gen_main_index + template = TemplatePage.new(RDoc::Page::INDEX) + File.open("index.html", "w") do |f| + values = { + "initial_page" => main_url, + 'title' => CGI.escapeHTML(@options.title), + 'charset' => @options.charset + } + if @options.inline_source + values['inline_source'] = true + end + template.write_html_on(f, values) + end + end + + # return the url of the main page + def main_url + main_page = @options.main_page + ref = nil + if main_page + ref = AllReferences[main_page] + if ref + ref = ref.path + else + $stderr.puts "Could not find main page #{main_page}" + end + end + + unless ref + for file in @files + if file.document_self + ref = file.path + break + end + end + end + + unless ref + $stderr.puts "Couldn't find anything to document" + $stderr.puts "Perhaps you've used :stopdoc: in all classes" + exit(1) + end + + ref + end + + + end + + + ###################################################################### + + + class HTMLGeneratorInOne < HTMLGenerator + + def initialize(*args) + super + end + + ## + # Build the initial indices and output objects + # based on an array of TopLevel objects containing + # the extracted information. + + def generate(info) + @toplevels = info + @files = [] + @classes = [] + @hyperlinks = {} + + build_indices + generate_xml + end + + + ## + # Generate: + # + # * a list of HtmlFile objects for each TopLevel object. + # * a list of HtmlClass objects for each first level + # class or module in the TopLevel objects + # * a complete list of all hyperlinkable terms (file, + # class, module, and method names) + + def build_indices + + @toplevels.each do |toplevel| + @files << HtmlFile.new(toplevel, @options, FILE_DIR) + end + + RDoc::TopLevel.all_classes_and_modules.each do |cls| + build_class_list(cls, @files[0], CLASS_DIR) + end + end + + def build_class_list(from, html_file, class_dir) + @classes << HtmlClass.new(from, html_file, class_dir, @options) + from.each_classmodule do |mod| + build_class_list(mod, html_file, class_dir) + end + end + + ## + # Generate all the HTML. For the one-file case, we generate + # all the information in to one big hash + # + def generate_xml + values = { + 'charset' => @options.charset, + 'files' => gen_into(@files), + 'classes' => gen_into(@classes), + 'title' => CGI.escapeHTML(@options.title), + } + + # this method is defined in the template file + write_extra_pages if defined? write_extra_pages + + template = TemplatePage.new(RDoc::Page::ONE_PAGE) + + if @options.op_name + opfile = File.open(@options.op_name, "w") + else + opfile = $stdout + end + template.write_html_on(opfile, values) + end + + def gen_into(list) + res = [] + list.each do |item| + res << item.value_hash + end + res + end + + def gen_file_index + gen_an_index(@files, 'Files') + end + + def gen_class_index + gen_an_index(@classes, 'Classes') + end + + def gen_method_index + gen_an_index(HtmlMethod.all_methods, 'Methods') + end + + + def gen_an_index(collection, title) + res = [] + collection.sort.each do |f| + if f.document_self + res << { "href" => f.path, "name" => f.index_name } + end + end + + return { + "entries" => res, + 'list_title' => title, + 'index_url' => main_url, + } + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/ri_generator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/ri_generator.rb new file mode 100644 index 0000000000..c7d0bbd8f0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/ri_generator.rb @@ -0,0 +1,268 @@ +# We're responsible for generating all the HTML files +# from the object tree defined in code_objects.rb. We +# generate: +# +# [files] an html file for each input file given. These +# input files appear as objects of class +# TopLevel +# +# [classes] an html file for each class or module encountered. +# These classes are not grouped by file: if a file +# contains four classes, we'll generate an html +# file for the file itself, and four html files +# for the individual classes. +# +# [indices] we generate three indices for files, classes, +# and methods. These are displayed in a browser +# like window with three index panes across the +# top and the selected description below +# +# Method descriptions appear in whatever entity (file, class, +# or module) that contains them. +# +# We generate files in a structure below a specified subdirectory, +# normally +doc+. +# +# opdir +# | +# |___ files +# | |__ per file summaries +# | +# |___ classes +# |__ per class/module descriptions +# +# HTML is generated using the Template class. +# + +require 'ftools' + +require 'rdoc/options' +require 'rdoc/template' +require 'rdoc/markup/simple_markup' +require 'rdoc/markup/simple_markup/to_flow' +require 'cgi' + +require 'rdoc/ri/ri_cache' +require 'rdoc/ri/ri_reader' +require 'rdoc/ri/ri_writer' +require 'rdoc/ri/ri_descriptions' + +module Generators + + + class RIGenerator + + # Generators may need to return specific subclasses depending + # on the options they are passed. Because of this + # we create them using a factory + + def RIGenerator.for(options) + new(options) + end + + class <\s*)[^\#]/ + content = comment + else + content = comment.gsub(/^\s*(#+)/) { $1.tr('#',' ') } + end + @markup.convert(content, @to_flow) + end + + + # By default we replace existing classes with the + # same name. If the --merge option was given, we instead + # merge this definition into an existing class. We add + # our methods, aliases, etc to that class, but do not + # change the class's description. + + def update_or_replace(cls_desc) + old_cls = nil + + if @options.merge + rdr = RI::RiReader.new(RI::RiCache.new(@options.op_dir)) + + namespace = rdr.top_level_namespace + namespace = rdr.lookup_namespace_in(cls_desc.name, namespace) + if namespace.empty? + $stderr.puts "You asked me to merge this source into existing " + $stderr.puts "documentation. This file references a class or " + $stderr.puts "module called #{cls_desc.name} which I don't" + $stderr.puts "have existing documentation for." + $stderr.puts + $stderr.puts "Perhaps you need to generate its documentation first" + exit 1 + else + old_cls = namespace[0] + end + end + + if old_cls.nil? + # no merge: simply overwrite + @ri_writer.remove_class(cls_desc) + @ri_writer.add_class(cls_desc) + else + # existing class: merge in + old_desc = rdr.get_class(old_cls) + + old_desc.merge_in(cls_desc) + @ri_writer.add_class(old_desc) + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/chm/chm.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/chm/chm.rb new file mode 100644 index 0000000000..4a89c26520 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/chm/chm.rb @@ -0,0 +1,87 @@ +module RDoc +module Page + +require "rdoc/generators/template/html/html" + +# This is a nasty little hack, but hhc doesn't support the /, '') +SRC_PAGE.sub!(/<\?xml.*\?>/, '') + +HPP_FILE = %{ +[OPTIONS] +Auto Index = Yes +Compatibility=1.1 or later +Compiled file=%opname%.chm +Contents file=contents.hhc +Full-text search=Yes +Index file=index.hhk +Language=0x409 English(United States) +Title=%title% + +[FILES] +START:all_html_files +%html_file_name% +END:all_html_files +} + +CONTENTS = %{ + + + + + + + + + + + +

    +START:contents +
  • + + + +IF:methods +
      +START:methods +
    • + + + +END:methods +
    +ENDIF:methods +
  • +END:contents +
+ +} + + +CHM_INDEX = %{ + + + + + + + + + + + +
    +START:index +
  • + + + +END:index +
+ +} +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/hefss.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/hefss.rb new file mode 100644 index 0000000000..e68ca85823 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/hefss.rb @@ -0,0 +1,418 @@ +module RDoc +module Page + + +FONTS = "Verdana, Arial, Helvetica, sans-serif" + +STYLE = %{ +body,p { font-family: Verdana, Arial, Helvetica, sans-serif; + color: #000040; background: #BBBBBB; +} + +td { font-family: Verdana, Arial, Helvetica, sans-serif; + color: #000040; +} + +.attr-rw { font-size: small; color: #444488 } + +.title-row {color: #eeeeff; + background: #BBBBDD; +} + +.big-title-font { color: white; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: large; + height: 50px} + +.small-title-font { color: purple; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: small; } + +.aqua { color: purple } + +.method-name, attr-name { + font-family: monospace; font-weight: bold; +} + +.tablesubtitle { + width: 100%; + margin-top: 1ex; + margin-bottom: .5ex; + padding: 5px 0px 5px 20px; + font-size: large; + color: purple; + background: #BBBBCC; +} + +.tablesubsubtitle { + width: 100%; + margin-top: 1ex; + margin-bottom: .5ex; + padding: 5px 0px 5px 20px; + font-size: medium; + color: white; + background: #BBBBCC; +} + +.name-list { + font-family: monospace; + margin-left: 40px; + margin-bottom: 2ex; + line-height: 140%; +} + +.description { + margin-left: 40px; + margin-bottom: 2ex; + line-height: 140%; +} + +.methodtitle { + font-size: medium; + text_decoration: none; + padding: 3px 3px 3px 20px; + color: #0000AA; +} + +.column-title { + font-size: medium; + font-weight: bold; + text_decoration: none; + padding: 3px 3px 3px 20px; + color: #3333CC; + } + +.variable-name { + font-family: monospace; + font-size: medium; + text_decoration: none; + padding: 3px 3px 3px 20px; + color: #0000AA; +} + +.row-name { + font-size: medium; + font-weight: medium; + font-family: monospace; + text_decoration: none; + padding: 3px 3px 3px 20px; +} + +.paramsig { + font-size: small; +} + +.srcbut { float: right } + +} + + +############################################################################ + + +BODY = %{ + + %title% + + + + + + +!INCLUDE! + +IF:diagram +
+%diagram% +
+ENDIF:diagram + +IF:description +
%description%
+ENDIF:description + +IF:requires + + +
Required files

+
+START:requires +HREF:aref:name: +END:requires +ENDIF:requires +
+ +IF:methods + + +
Subroutines and Functions

+
+START:methods +HREF:aref:name:, +END:methods +
+ENDIF:methods + +IF:attributes + + +
Arguments

+ +START:attributes + +IF:rw + +ENDIF:rw +IFNOT:rw + +ENDIF:rw + + + +END:attributes +
 [%rw%] %name%%a_desc%
+ENDIF:attributes + +IF:classlist + + +
Modules

+%classlist%
+ENDIF:classlist + + !INCLUDE! + + + +} + +############################################################################### + +FILE_PAGE = <<_FILE_PAGE_ + + + + +
+ +
File
%short_name%
+ + + + + + + + +
Path:%full_path% +IF:cvsurl +  (CVS) +ENDIF:cvsurl +
Modified:%dtm_modified%
+

+_FILE_PAGE_ + +################################################################### + +CLASS_PAGE = %{ + + + + + +
+ %classmod%
%full_name% +
+ + + + + +IF:parent + + + + +ENDIF:parent +
In: +START:infiles +HREF:full_path_url:full_path: +IF:cvsurl + (CVS) +ENDIF:cvsurl +END:infiles +
Parent: +IF:par_url + +ENDIF:par_url +%parent% +IF:par_url + +ENDIF:par_url +
+

+} + +################################################################### + +METHOD_LIST = %{ +IF:includes +
Uses

+
+START:includes + HREF:aref:name: +END:includes +
+ENDIF:includes + +IF:method_list +START:method_list +IF:methods + + +
%type% %category% methods
+START:methods + + +
+ +%name%%params% +IF:codeurl +src +ENDIF:codeurl +
+IF:m_desc +
+%m_desc% +
+ENDIF:m_desc +END:methods +ENDIF:methods +END:method_list +ENDIF:method_list +} + +=begin +=end + +########################## Source code ########################## + +SRC_PAGE = %{ + +%title% + + + + +
%code%
+ + +} + +########################## Index ################################ + +FR_INDEX_BODY = %{ +!INCLUDE! +} + +FILE_INDEX = %{ + + + + + + + + +START:entries +%name%
+END:entries + +} + +CLASS_INDEX = FILE_INDEX +METHOD_INDEX = FILE_INDEX + +INDEX = %{ + + + %title% + + + + + + + + + + + + + + + <body bgcolor="#BBBBBB"> + Click <a href="html/index.html">here</a> for a non-frames + version of this page. + </body> + + + + +} + +# and a blank page to use as a target +BLANK = %{ + +} + +def write_extra_pages + template = TemplatePage.new(BLANK) + File.open("blank.html", "w") { |f| template.write_html_on(f, {}) } +end + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/html.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/html.rb new file mode 100644 index 0000000000..7f9e599465 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/html.rb @@ -0,0 +1,711 @@ +# +# = CSS2 RDoc HTML template +# +# This is a template for RDoc that uses XHTML 1.0 Transitional and dictates a +# bit more of the appearance of the output to cascading stylesheets than the +# default. It was designed for clean inline code display, and uses DHTMl to +# toggle the visbility of each method's source with each click on the '[source]' +# link. +# +# == Authors +# +# * Michael Granger +# +# Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved. +# +# This work is licensed under the Creative Commons Attribution License. To view +# a copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or +# send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California +# 94305, USA. +# + +module RDoc + module Page + + FONTS = "Verdana,Arial,Helvetica,sans-serif" + +STYLE = %{ +body { + font-family: Verdana,Arial,Helvetica,sans-serif; + font-size: 90%; + margin: 0; + margin-left: 40px; + padding: 0; + background: white; +} + +h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; } +h1 { font-size: 150%; } +h2,h3,h4 { margin-top: 1em; } + +a { background: #eef; color: #039; text-decoration: none; } +a:hover { background: #039; color: #eef; } + +/* Override the base stylesheet's Anchor inside a table cell */ +td > a { + background: transparent; + color: #039; + text-decoration: none; +} + +/* and inside a section title */ +.section-title > a { + background: transparent; + color: #eee; + text-decoration: none; +} + +/* === Structural elements =================================== */ + +div#index { + margin: 0; + margin-left: -40px; + padding: 0; + font-size: 90%; +} + + +div#index a { + margin-left: 0.7em; +} + +div#index .section-bar { + margin-left: 0px; + padding-left: 0.7em; + background: #ccc; + font-size: small; +} + + +div#classHeader, div#fileHeader { + width: auto; + color: white; + padding: 0.5em 1.5em 0.5em 1.5em; + margin: 0; + margin-left: -40px; + border-bottom: 3px solid #006; +} + +div#classHeader a, div#fileHeader a { + background: inherit; + color: white; +} + +div#classHeader td, div#fileHeader td { + background: inherit; + color: white; +} + + +div#fileHeader { + background: #057; +} + +div#classHeader { + background: #048; +} + + +.class-name-in-header { + font-size: 180%; + font-weight: bold; +} + + +div#bodyContent { + padding: 0 1.5em 0 1.5em; +} + +div#description { + padding: 0.5em 1.5em; + background: #efefef; + border: 1px dotted #999; +} + +div#description h1,h2,h3,h4,h5,h6 { + color: #125;; + background: transparent; +} + +div#validator-badges { + text-align: center; +} +div#validator-badges img { border: 0; } + +div#copyright { + color: #333; + background: #efefef; + font: 0.75em sans-serif; + margin-top: 5em; + margin-bottom: 0; + padding: 0.5em 2em; +} + + +/* === Classes =================================== */ + +table.header-table { + color: white; + font-size: small; +} + +.type-note { + font-size: small; + color: #DEDEDE; +} + +.xxsection-bar { + background: #eee; + color: #333; + padding: 3px; +} + +.section-bar { + color: #333; + border-bottom: 1px solid #999; + margin-left: -20px; +} + + +.section-title { + background: #79a; + color: #eee; + padding: 3px; + margin-top: 2em; + margin-left: -30px; + border: 1px solid #999; +} + +.top-aligned-row { vertical-align: top } +.bottom-aligned-row { vertical-align: bottom } + +/* --- Context section classes ----------------------- */ + +.context-row { } +.context-item-name { font-family: monospace; font-weight: bold; color: black; } +.context-item-value { font-size: small; color: #448; } +.context-item-desc { color: #333; padding-left: 2em; } + +/* --- Method classes -------------------------- */ +.method-detail { + background: #efefef; + padding: 0; + margin-top: 0.5em; + margin-bottom: 1em; + border: 1px dotted #ccc; +} +.method-heading { + color: black; + background: #ccc; + border-bottom: 1px solid #666; + padding: 0.2em 0.5em 0 0.5em; +} +.method-signature { color: black; background: inherit; } +.method-name { font-weight: bold; } +.method-args { font-style: italic; } +.method-description { padding: 0 0.5em 0 0.5em; } + +/* --- Source code sections -------------------- */ + +a.source-toggle { font-size: 90%; } +div.method-source-code { + background: #262626; + color: #ffdead; + margin: 1em; + padding: 0.5em; + border: 1px dashed #999; + overflow: hidden; +} + +div.method-source-code pre { color: #ffdead; overflow: hidden; } + +/* --- Ruby keyword styles --------------------- */ + +.standalone-code { background: #221111; color: #ffdead; overflow: hidden; } + +.ruby-constant { color: #7fffd4; background: transparent; } +.ruby-keyword { color: #00ffff; background: transparent; } +.ruby-ivar { color: #eedd82; background: transparent; } +.ruby-operator { color: #00ffee; background: transparent; } +.ruby-identifier { color: #ffdead; background: transparent; } +.ruby-node { color: #ffa07a; background: transparent; } +.ruby-comment { color: #b22222; font-weight: bold; background: transparent; } +.ruby-regexp { color: #ffa07a; background: transparent; } +.ruby-value { color: #7fffd4; background: transparent; } +} + + +##################################################################### +### H E A D E R T E M P L A T E +##################################################################### + +XHTML_PREAMBLE = %{ + +} + +HEADER = XHTML_PREAMBLE + %{ + + + %title% + + + + + + + +} + + +##################################################################### +### C O N T E X T C O N T E N T T E M P L A T E +##################################################################### + +CONTEXT_CONTENT = %{ +} + + +##################################################################### +### F O O T E R T E M P L A T E +##################################################################### +FOOTER = %{ + + + + +} + + +##################################################################### +### F I L E P A G E H E A D E R T E M P L A T E +##################################################################### + +FILE_PAGE = %{ +
+

%short_name%

+ + + + + + + + + +
Path:%full_path% +IF:cvsurl +  (CVS) +ENDIF:cvsurl +
Last Update:%dtm_modified%
+
+} + + +##################################################################### +### C L A S S P A G E H E A D E R T E M P L A T E +##################################################################### + +CLASS_PAGE = %{ +
+ + + + + + + + + + +IF:parent + + + + +ENDIF:parent +
%classmod%%full_name%
In: +START:infiles +IF:full_path_url + +ENDIF:full_path_url + %full_path% +IF:full_path_url + +ENDIF:full_path_url +IF:cvsurl +  (CVS) +ENDIF:cvsurl +
+END:infiles +
Parent: +IF:par_url + +ENDIF:par_url + %parent% +IF:par_url + +ENDIF:par_url +
+
+} + + +##################################################################### +### M E T H O D L I S T T E M P L A T E +##################################################################### + +METHOD_LIST = %{ + +
+IF:diagram +
+ %diagram% +
+ENDIF:diagram + +IF:description +
+ %description% +
+ENDIF:description + +IF:requires +
+

Required files

+ +
+START:requires + HREF:aref:name:   +END:requires +
+
+ENDIF:requires + +IF:toc +
+

Contents

+ +ENDIF:toc +
+ +IF:methods +
+

Methods

+ +
+START:methods + HREF:aref:name:   +END:methods +
+
+ENDIF:methods + +
+ + + +IF:includes +
+

Included Modules

+ +
+START:includes + HREF:aref:name: +END:includes +
+
+ENDIF:includes + +START:sections +
+IF:sectitle +

%sectitle%

+IF:seccomment +
+ %seccomment% +
+ENDIF:seccomment +ENDIF:sectitle + +IF:classlist +
+

Classes and Modules

+ + %classlist% +
+ENDIF:classlist + +IF:constants +
+

Constants

+ +
+ +START:constants + + + + +IF:desc + + +ENDIF:desc + +END:constants +
%name%=%value% %desc%
+
+
+ENDIF:constants + +IF:aliases +
+

External Aliases

+ +
+ +START:aliases + + + + + +IF:desc + + + + +ENDIF:desc +END:aliases +
%old_name%->%new_name%
 %desc%
+
+
+ENDIF:aliases + + +IF:attributes +
+

Attributes

+ +
+ +START:attributes + + +IF:rw + +ENDIF:rw +IFNOT:rw + +ENDIF:rw + + +END:attributes +
%name% [%rw%]   %a_desc%
+
+
+ENDIF:attributes + + + + +IF:method_list +
+START:method_list +IF:methods +

%type% %category% methods

+ +START:methods +
+ + + + +
+IF:m_desc + %m_desc% +ENDIF:m_desc +IF:sourcecode +

[Source]

+
+
+%sourcecode%
+
+
+ENDIF:sourcecode +
+
+ +END:methods +ENDIF:methods +END:method_list + +
+ENDIF:method_list +END:sections +} + + +##################################################################### +### B O D Y T E M P L A T E +##################################################################### + +BODY = HEADER + %{ + +!INCLUDE! + +
+ +} + METHOD_LIST + %{ + +
+ +} + FOOTER + + + +##################################################################### +### S O U R C E C O D E T E M P L A T E +##################################################################### + +SRC_PAGE = XHTML_PREAMBLE + %{ + + + %title% + + + + +
%code%
+ + +} + + +##################################################################### +### I N D E X F I L E T E M P L A T E S +##################################################################### + +FR_INDEX_BODY = %{ +!INCLUDE! +} + +FILE_INDEX = XHTML_PREAMBLE + %{ + + + + %list_title% + + + + + +
+

%list_title%

+
+START:entries + %name%
+END:entries +
+
+ + +} + +CLASS_INDEX = FILE_INDEX +METHOD_INDEX = FILE_INDEX + +INDEX = %{ + + + + + + %title% + + + + + + + + + + + +} + + + + end # module Page +end # class RDoc + +require 'rdoc/generators/template/html/one_page_html' diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/kilmer.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/kilmer.rb new file mode 100644 index 0000000000..55071fc026 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/kilmer.rb @@ -0,0 +1,435 @@ +module RDoc +module Page + + +FONTS = "Verdana, Arial, Helvetica, sans-serif" + +STYLE = %{ +body,td,p { font-family: %fonts%; + color: #000040; +} + +.attr-rw { font-size: xx-small; color: #444488 } + +.title-row { background-color: #CCCCFF; + color: #000010; +} + +.big-title-font { + color: black; + font-weight: bold; + font-family: %fonts%; + font-size: large; + height: 60px; + padding: 10px 3px 10px 3px; +} + +.small-title-font { color: black; + font-family: %fonts%; + font-size:10; } + +.aqua { color: black } + +.method-name, .attr-name { + font-family: font-family: %fonts%; + font-weight: bold; + font-size: small; + margin-left: 20px; + color: #000033; +} + +.tablesubtitle, .tablesubsubtitle { + width: 100%; + margin-top: 1ex; + margin-bottom: .5ex; + padding: 5px 0px 5px 3px; + font-size: large; + color: black; + background-color: #CCCCFF; + border: thin; +} + +.name-list { + margin-left: 5px; + margin-bottom: 2ex; + line-height: 105%; +} + +.description { + margin-left: 5px; + margin-bottom: 2ex; + line-height: 105%; + font-size: small; +} + +.methodtitle { + font-size: small; + font-weight: bold; + text-decoration: none; + color: #000033; + background-color: white; +} + +.srclink { + font-size: small; + font-weight: bold; + text-decoration: none; + color: #0000DD; + background-color: white; +} + +.paramsig { + font-size: small; +} + +.srcbut { float: right } + +} + + +############################################################################ + + +BODY = %{ + + %title% + + + + + + +!INCLUDE! + +IF:diagram +
+%diagram% +
+ENDIF:diagram + +IF:description +
%description%
+ENDIF:description + +IF:requires + + +
Required files

+
+START:requires +HREF:aref:name: +END:requires +ENDIF:requires +
+ +IF:methods + + +
Methods

+
+START:methods +HREF:aref:name:, +END:methods +
+ENDIF:methods + + +START:sections +
+IF:sectitle +

%sectitle%

+IF:seccomment +
+ %seccomment% +
+ENDIF:seccomment +ENDIF:sectitle + +IF:attributes + + +
Attributes

+ +START:attributes + +IF:rw + +ENDIF:rw +IFNOT:rw + +ENDIF:rw + + + +END:attributes +
 [%rw%] %name%%a_desc%
+ENDIF:attributes + +IF:classlist + + +
Classes and Modules

+%classlist%
+ENDIF:classlist + + !INCLUDE! + +END:sections + + + +} + +############################################################################### + +FILE_PAGE = <<_FILE_PAGE_ + + + + +
+ +
File
%short_name%
+ + + + + + + + +
Path:%full_path% +IF:cvsurl +  (CVS) +ENDIF:cvsurl +
Modified:%dtm_modified%
+

+_FILE_PAGE_ + +################################################################### + +CLASS_PAGE = %{ + + + + + +
+ %classmod%
%full_name% +
+ + + + + +IF:parent + + + + +ENDIF:parent +
In: +START:infiles +HREF:full_path_url:full_path: +IF:cvsurl + (CVS) +ENDIF:cvsurl +END:infiles +
Parent: +IF:par_url + +ENDIF:par_url +%parent% +IF:par_url + +ENDIF:par_url +
+

+} + +################################################################### + +METHOD_LIST = %{ +IF:includes +
Included modules

+
+START:includes + HREF:aref:name: +END:includes +
+ENDIF:includes + +IF:method_list +START:method_list +IF:methods + + +
%type% %category% methods
+START:methods + + +
+ +IF:callseq +%callseq% +ENDIF:callseq +IFNOT:callseq + %name%%params% +ENDIF:callseq +IF:codeurl +src +ENDIF:codeurl +
+IF:m_desc +
+%m_desc% +
+ENDIF:m_desc +IF:aka +
+This method is also aliased as +START:aka +%name% +END:aka +
+ENDIF:aka +IF:sourcecode +
+%sourcecode%
+
+ENDIF:sourcecode +END:methods +ENDIF:methods +END:method_list +ENDIF:method_list +} + +=begin +=end + +########################## Source code ########################## + +SRC_PAGE = %{ + +%title% + + + + +
%code%
+ + +} + +########################## Index ################################ + +FR_INDEX_BODY = %{ +!INCLUDE! +} + +FILE_INDEX = %{ + + + + + + + + +START:entries +%name%
+END:entries + +} + +CLASS_INDEX = FILE_INDEX +METHOD_INDEX = FILE_INDEX + +INDEX = %{ + + + %title% + + + + + + + + + +IF:inline_source + +ENDIF:inline_source +IFNOT:inline_source + + + + +ENDIF:inline_source + + <body bgcolor="white"> + Click <a href="html/index.html">here</a> for a non-frames + version of this page. + </body> + + + + +} + +# and a blank page to use as a target +BLANK = %{ + +} + +def write_extra_pages + template = TemplatePage.new(BLANK) + File.open("blank.html", "w") { |f| template.write_html_on(f, {}) } +end + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/old_html.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/old_html.rb new file mode 100644 index 0000000000..ca66302a08 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/old_html.rb @@ -0,0 +1,728 @@ +module RDoc + +# This is how you define the HTML that RDoc generates. Simply create +# a file in rdoc/generators/html_templates that creates the +# module RDoc::Page and populate it as described below. Then invoke +# rdoc using the --template option, and +# your template will be used. +# +# The constants defining pages use a simple templating system: +# +# * The templating system is passed a hash. Keys in the hash correspond +# to tags on this page. The tag %abc% is looked up in the hash, +# and is replaced by the corresponding hash value. +# +# * Some tags are optional. You can detect this using IF/ENDIF +# +# IF: title +# The value of title is %title% +# ENDIF: title +# +# * Some entries in the hash have values that are arrays, where each +# entry in the array is itself a hash. These are used to generate +# lists using the START: construct. For example, given a hash +# containing +# +# { 'people' => [ { 'name' => 'Fred', 'age' => '12' }, +# { 'name' => 'Mary', 'age' => '21' } ] +# +# You could generate a simple table using +# +# +# START:people +# +# END:people +#
%name%%age%
+# +# These lists can be nested to an arbitrary depth +# +# * the construct HREF:url:name: generates %name% +# if +url+ is defined in the hash, or %name% otherwise. +# +# +# Your file must contain the following constants +# +# [*FONTS*] a list of fonts to be used +# [*STYLE*] a CSS section (without the + + + + +START:entries +%name%
+END:entries + +} + +CLASS_INDEX = FILE_INDEX +METHOD_INDEX = FILE_INDEX + +INDEX = %{ + + + + +%title% + + + + + + + + + + <body bgcolor="white"> + Sorry, RDoc currently only generates HTML using frames. + </body> + + + + +} + +###################################################################### +# +# The following is used for the -1 option +# + +CONTENTS_XML = %{ +IF:description +%description% +ENDIF:description + +IF:requires +

Requires:

+
    +START:requires +IF:aref +
  • %name%
  • +ENDIF:aref +IFNOT:aref +
  • %name%
  • +ENDIF:aref +END:requires +
+ENDIF:requires + +IF:attributes +

Attributes

+ +START:attributes + +END:attributes +
%name%%rw%%a_desc%
+ENDIF:attributes + +IF:includes +

Includes

+
    +START:includes +IF:aref +
  • %name%
  • +ENDIF:aref +IFNOT:aref +
  • %name%
  • +ENDIF:aref +END:includes +
+ENDIF:includes + +IF:method_list +

Methods

+START:method_list +IF:methods +START:methods +

%type% %category% method: %name%%params%

+ +IF:m_desc +%m_desc% +ENDIF:m_desc + +IF:sourcecode +
+%sourcecode%
+
+ENDIF:sourcecode +END:methods +ENDIF:methods +END:method_list +ENDIF:method_list +} + + +end +end + +require 'rdoc/generators/template/html/one_page_html' diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/one_page_html.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/one_page_html.rb new file mode 100644 index 0000000000..19441f4725 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/html/one_page_html.rb @@ -0,0 +1,122 @@ +module RDoc +module Page +###################################################################### +# +# The following is used for the -1 option +# + +CONTENTS_XML = %{ +IF:description +%description% +ENDIF:description + +IF:requires +

Requires:

+
    +START:requires +IF:aref +
  • %name%
  • +ENDIF:aref +IFNOT:aref +
  • %name%
  • +ENDIF:aref +END:requires +
+ENDIF:requires + +IF:attributes +

Attributes

+ +START:attributes + +END:attributes +
%name%%rw%%a_desc%
+ENDIF:attributes + +IF:includes +

Includes

+
    +START:includes +IF:aref +
  • %name%
  • +ENDIF:aref +IFNOT:aref +
  • %name%
  • +ENDIF:aref +END:includes +
+ENDIF:includes + +IF:method_list +

Methods

+START:method_list +IF:methods +START:methods +

%type% %category% method: +IF:callseq +%callseq% +ENDIF:callseq +IFNOT:callseq +%name%%params%

+ENDIF:callseq + +IF:m_desc +%m_desc% +ENDIF:m_desc + +IF:sourcecode +
+%sourcecode%
+
+ENDIF:sourcecode +END:methods +ENDIF:methods +END:method_list +ENDIF:method_list +} + +######################################################################## + +ONE_PAGE = %{ + + + + %title% + + + +START:files +

File: %short_name%

+ + + +
Path:%full_path%
Modified:%dtm_modified%
+} + CONTENTS_XML + %{ +END:files + +IF:classes +

Classes

+START:classes +IF:parent +

%classmod% %full_name% < HREF:par_url:parent:

+ENDIF:parent +IFNOT:parent +

%classmod% %full_name%

+ENDIF:parent + +IF:infiles +(in files +START:infiles +HREF:full_path_url:full_path: +END:infiles +) +ENDIF:infiles +} + CONTENTS_XML + %{ +END:classes +ENDIF:classes + + +} + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/xml/rdf.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/xml/rdf.rb new file mode 100644 index 0000000000..1545d81a2f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/xml/rdf.rb @@ -0,0 +1,112 @@ +module RDoc +module Page + + + +CONTENTS_RDF = %{ +IF:description + +%description% + +ENDIF:description + +IF:requires +START:requires + +END:requires +ENDIF:requires + +IF:attributes +START:attributes + + +IF:rw + %rw% +ENDIF:rw + %a_desc% + + +END:attributes +ENDIF:attributes + +IF:includes + +START:includes + +END:includes + +ENDIF:includes + +IF:method_list +START:method_list +IF:methods +START:methods + + + %params% +IF:m_desc + +%m_desc% + +ENDIF:m_desc +IF:sourcecode + +%sourcecode% + +ENDIF:sourcecode + + +END:methods +ENDIF:methods +END:method_list +ENDIF:method_list + +} + +######################################################################## + +ONE_PAGE = %{ + + + +START:files + + %full_path% + %dtm_modified% +} + CONTENTS_RDF + %{ + +END:files +START:classes + <%classmod% rd:name="%full_name%" rd:id="%full_name%"> + +IF:infiles + +START:infiles + + + +END:infiles + +ENDIF:infiles +IF:parent + HREF:par_url:parent: +ENDIF:parent + +} + CONTENTS_RDF + %{ + +END:classes + + +} + + +end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/xml/xml.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/xml/xml.rb new file mode 100644 index 0000000000..4a0c8c9ac4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/template/xml/xml.rb @@ -0,0 +1,112 @@ +module RDoc +module Page + + + +CONTENTS_XML = %{ +IF:description + +%description% + +ENDIF:description + +IF:requires + +START:requires + +END:requires + +ENDIF:requires +IF:attributes + +START:attributes + +IF:rw + %rw% +ENDIF:rw + %a_desc% + +END:attributes + +ENDIF:attributes +IF:includes + +START:includes + +END:includes + +ENDIF:includes +IF:method_list + +START:method_list +IF:methods +START:methods + + %params% +IF:m_desc + +%m_desc% + +ENDIF:m_desc +IF:sourcecode + +%sourcecode% + +ENDIF:sourcecode + +END:methods +ENDIF:methods +END:method_list + +ENDIF:method_list + +} + +######################################################################## + +ONE_PAGE = %{ + + +START:files + + + %full_path% + %dtm_modified% + +} + CONTENTS_XML + %{ + +END:files + + +START:classes + <%classmod% name="%full_name%" id="%full_name%"> + +IF:infiles + +START:infiles + HREF:full_path_url:full_path: +END:infiles + +ENDIF:infiles +IF:parent + HREF:par_url:parent: +ENDIF:parent + +} + CONTENTS_XML + %{ + +END:classes + + +} + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/xml_generator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/xml_generator.rb new file mode 100644 index 0000000000..8c1a76d62b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/generators/xml_generator.rb @@ -0,0 +1,130 @@ + +require 'ftools' + +require 'rdoc/options' +require 'rdoc/markup/simple_markup' +require 'rdoc/markup/simple_markup/to_html' +require 'rdoc/generators/html_generator' + +module Generators + + # Generate XML output as one big file + + class XMLGenerator < HTMLGenerator + + # Standard generator factory + def XMLGenerator.for(options) + XMLGenerator.new(options) + end + + + def initialize(*args) + super + end + + ## + # Build the initial indices and output objects + # based on an array of TopLevel objects containing + # the extracted information. + + def generate(info) + @info = info + @files = [] + @classes = [] + @hyperlinks = {} + + build_indices + generate_xml + end + + + ## + # Generate: + # + # * a list of HtmlFile objects for each TopLevel object. + # * a list of HtmlClass objects for each first level + # class or module in the TopLevel objects + # * a complete list of all hyperlinkable terms (file, + # class, module, and method names) + + def build_indices + + @info.each do |toplevel| + @files << HtmlFile.new(toplevel, @options, FILE_DIR) + end + + RDoc::TopLevel.all_classes_and_modules.each do |cls| + build_class_list(cls, @files[0], CLASS_DIR) + end + end + + def build_class_list(from, html_file, class_dir) + @classes << HtmlClass.new(from, html_file, class_dir, @options) + from.each_classmodule do |mod| + build_class_list(mod, html_file, class_dir) + end + end + + ## + # Generate all the HTML. For the one-file case, we generate + # all the information in to one big hash + # + def generate_xml + values = { + 'charset' => @options.charset, + 'files' => gen_into(@files), + 'classes' => gen_into(@classes) + } + + # this method is defined in the template file + write_extra_pages if defined? write_extra_pages + + template = TemplatePage.new(RDoc::Page::ONE_PAGE) + + if @options.op_name + opfile = File.open(@options.op_name, "w") + else + opfile = $stdout + end + template.write_html_on(opfile, values) + end + + def gen_into(list) + res = [] + list.each do |item| + res << item.value_hash + end + res + end + + def gen_file_index + gen_an_index(@files, 'Files') + end + + def gen_class_index + gen_an_index(@classes, 'Classes') + end + + def gen_method_index + gen_an_index(HtmlMethod.all_methods, 'Methods') + end + + + def gen_an_index(collection, title) + res = [] + collection.sort.each do |f| + if f.document_self + res << { "href" => f.path, "name" => f.index_name } + end + end + + return { + "entries" => res, + 'list_title' => title, + 'index_url' => main_url, + } + end + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/sample/rdoc2latex.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/sample/rdoc2latex.rb new file mode 100644 index 0000000000..26563b75da --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/sample/rdoc2latex.rb @@ -0,0 +1,16 @@ +#!/usr/local/bin/ruby +# Illustration of a script to convert an RDoc-style file to a LaTeX +# document + +require 'rdoc/markup/simple_markup' +require 'rdoc/markup/simple_markup/to_latex' + +p = SM::SimpleMarkup.new +h = SM::ToLaTeX.new + +#puts "\\documentclass{report}" +#puts "\\usepackage{tabularx}" +#puts "\\usepackage{parskip}" +#puts "\\begin{document}" +puts p.convert(ARGF.read, h) +#puts "\\end{document}" diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/sample/sample.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/sample/sample.rb new file mode 100644 index 0000000000..a375b54564 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/sample/sample.rb @@ -0,0 +1,42 @@ +# This program illustrates the basic use of the SimpleMarkup +# class. It extracts the first comment block from the +# simple_markup.rb file and converts it into HTML on +# standard output. Run it using +# +# % ruby sample.rb +# +# You should be in the sample/ directory when you do this, +# as it hardwires the path to the files it needs to require. +# This isn't necessary in the code you write once you've +# installed the package. +# +# For a better way of formatting code comment blocks (and more) +# see the rdoc package. +# + +$:.unshift "../../.." + +require 'rdoc/markup/simple_markup' +require 'rdoc/markup/simple_markup/to_html' + +# Extract the comment block from the source file + +input_string = "" + +File.foreach("../simple_markup.rb") do |line| + break unless line.gsub!(/^\# ?/, '') + input_string << line +end + +# Create a markup object +markup = SM::SimpleMarkup.new + +# Attach it to an HTML formatter +h = SM::ToHtml.new + +# And convert out comment block to html. Wrap it a body +# tag pair to let browsers view it + +puts "" +puts markup.convert(input_string, h) +puts "" diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup.rb new file mode 100644 index 0000000000..8193ca02d4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup.rb @@ -0,0 +1,476 @@ +# = Introduction +# +# SimpleMarkup parses plain text documents and attempts to decompose +# them into their constituent parts. Some of these parts are high-level: +# paragraphs, chunks of verbatim text, list entries and the like. Other +# parts happen at the character level: a piece of bold text, a word in +# code font. This markup is similar in spirit to that used on WikiWiki +# webs, where folks create web pages using a simple set of formatting +# rules. +# +# SimpleMarkup itself does no output formatting: this is left to a +# different set of classes. +# +# SimpleMarkup is extendable at runtime: you can add new markup +# elements to be recognised in the documents that SimpleMarkup parses. +# +# SimpleMarkup is intended to be the basis for a family of tools which +# share the common requirement that simple, plain-text should be +# rendered in a variety of different output formats and media. It is +# envisaged that SimpleMarkup could be the basis for formating RDoc +# style comment blocks, Wiki entries, and online FAQs. +# +# = Basic Formatting +# +# * SimpleMarkup looks for a document's natural left margin. This is +# used as the initial margin for the document. +# +# * Consecutive lines starting at this margin are considered to be a +# paragraph. +# +# * If a paragraph starts with a "*", "-", or with ".", then it is +# taken to be the start of a list. The margin in increased to be the +# first non-space following the list start flag. Subsequent lines +# should be indented to this new margin until the list ends. For +# example: +# +# * this is a list with three paragraphs in +# the first item. This is the first paragraph. +# +# And this is the second paragraph. +# +# 1. This is an indented, numbered list. +# 2. This is the second item in that list +# +# This is the third conventional paragraph in the +# first list item. +# +# * This is the second item in the original list +# +# * You can also construct labeled lists, sometimes called description +# or definition lists. Do this by putting the label in square brackets +# and indenting the list body: +# +# [cat] a small furry mammal +# that seems to sleep a lot +# +# [ant] a little insect that is known +# to enjoy picnics +# +# A minor variation on labeled lists uses two colons to separate the +# label from the list body: +# +# cat:: a small furry mammal +# that seems to sleep a lot +# +# ant:: a little insect that is known +# to enjoy picnics +# +# This latter style guarantees that the list bodies' left margins are +# aligned: think of them as a two column table. +# +# * Any line that starts to the right of the current margin is treated +# as verbatim text. This is useful for code listings. The example of a +# list above is also verbatim text. +# +# * A line starting with an equals sign (=) is treated as a +# heading. Level one headings have one equals sign, level two headings +# have two,and so on. +# +# * A line starting with three or more hyphens (at the current indent) +# generates a horizontal rule. THe more hyphens, the thicker the rule +# (within reason, and if supported by the output device) +# +# * You can use markup within text (except verbatim) to change the +# appearance of parts of that text. Out of the box, SimpleMarkup +# supports word-based and general markup. +# +# Word-based markup uses flag characters around individual words: +# +# [\*word*] displays word in a *bold* font +# [\_word_] displays word in an _emphasized_ font +# [\+word+] displays word in a +code+ font +# +# General markup affects text between a start delimiter and and end +# delimiter. Not surprisingly, these delimiters look like HTML markup. +# +# [\text...] displays word in a *bold* font +# [\text...] displays word in an _emphasized_ font +# [\text...] displays word in an _emphasized_ font +# [\text...] displays word in a +code+ font +# +# Unlike conventional Wiki markup, general markup can cross line +# boundaries. You can turn off the interpretation of markup by +# preceding the first character with a backslash, so \\\bold +# text and \\\*bold* produce \bold text and \*bold +# respectively. +# +# = Using SimpleMarkup +# +# For information on using SimpleMarkup programatically, +# see SM::SimpleMarkup. +# +# Author:: Dave Thomas, dave@pragmaticprogrammer.com +# Version:: 0.0 +# License:: Ruby license + + + +require 'rdoc/markup/simple_markup/fragments' +require 'rdoc/markup/simple_markup/lines.rb' + +module SM #:nodoc: + + # == Synopsis + # + # This code converts input_string, which is in the format + # described in markup/simple_markup.rb, to HTML. The conversion + # takes place in the +convert+ method, so you can use the same + # SimpleMarkup object to convert multiple input strings. + # + # require 'rdoc/markup/simple_markup' + # require 'rdoc/markup/simple_markup/to_html' + # + # p = SM::SimpleMarkup.new + # h = SM::ToHtml.new + # + # puts p.convert(input_string, h) + # + # You can extend the SimpleMarkup parser to recognise new markup + # sequences, and to add special processing for text that matches a + # regular epxression. Here we make WikiWords significant to the parser, + # and also make the sequences {word} and \text... signify + # strike-through text. When then subclass the HTML output class to deal + # with these: + # + # require 'rdoc/markup/simple_markup' + # require 'rdoc/markup/simple_markup/to_html' + # + # class WikiHtml < SM::ToHtml + # def handle_special_WIKIWORD(special) + # "" + special.text + "" + # end + # end + # + # p = SM::SimpleMarkup.new + # p.add_word_pair("{", "}", :STRIKE) + # p.add_html("no", :STRIKE) + # + # p.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD) + # + # h = WikiHtml.new + # h.add_tag(:STRIKE, "", "") + # + # puts "" + p.convert(ARGF.read, h) + "" + # + # == Output Formatters + # + # _missing_ + # + # + + class SimpleMarkup + + SPACE = ?\s + + # List entries look like: + # * text + # 1. text + # [label] text + # label:: text + # + # Flag it as a list entry, and + # work out the indent for subsequent lines + + SIMPLE_LIST_RE = /^( + ( \* (?# bullet) + |- (?# bullet) + |\d+\. (?# numbered ) + |[A-Za-z]\. (?# alphabetically numbered ) + ) + \s+ + )\S/x + + LABEL_LIST_RE = /^( + ( \[.*?\] (?# labeled ) + |\S.*:: (?# note ) + )(?:\s+|$) + )/x + + + ## + # take a block of text and use various heuristics to determine + # it's structure (paragraphs, lists, and so on). Invoke an + # event handler as we identify significant chunks. + # + + def initialize + @am = AttributeManager.new + @output = nil + end + + ## + # Add to the sequences used to add formatting to an individual word + # (such as *bold*). Matching entries will generate attibutes + # that the output formatters can recognize by their +name+ + + def add_word_pair(start, stop, name) + @am.add_word_pair(start, stop, name) + end + + ## + # Add to the sequences recognized as general markup + # + + def add_html(tag, name) + @am.add_html(tag, name) + end + + ## + # Add to other inline sequences. For example, we could add + # WikiWords using something like: + # + # parser.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD) + # + # Each wiki word will be presented to the output formatter + # via the accept_special method + # + + def add_special(pattern, name) + @am.add_special(pattern, name) + end + + + # We take a string, split it into lines, work out the type of + # each line, and from there deduce groups of lines (for example + # all lines in a paragraph). We then invoke the output formatter + # using a Visitor to display the result + + def convert(str, op) + @lines = Lines.new(str.split(/\r?\n/).collect { |aLine| + Line.new(aLine) }) + return "" if @lines.empty? + @lines.normalize + assign_types_to_lines + group = group_lines + # call the output formatter to handle the result + # group.to_a.each {|i| p i} + group.accept(@am, op) + end + + + ####### + private + ####### + + + ## + # Look through the text at line indentation. We flag each line as being + # Blank, a paragraph, a list element, or verbatim text + # + + def assign_types_to_lines(margin = 0, level = 0) + + while line = @lines.next + if line.isBlank? + line.stamp(Line::BLANK, level) + next + end + + # if a line contains non-blanks before the margin, then it must belong + # to an outer level + + text = line.text + + for i in 0...margin + if text[i] != SPACE + @lines.unget + return + end + end + + active_line = text[margin..-1] + + # Rules (horizontal lines) look like + # + # --- (three or more hyphens) + # + # The more hyphens, the thicker the rule + # + + if /^(---+)\s*$/ =~ active_line + line.stamp(Line::RULE, level, $1.length-2) + next + end + + # Then look for list entries. First the ones that have to have + # text following them (* xxx, - xxx, and dd. xxx) + + if SIMPLE_LIST_RE =~ active_line + + offset = margin + $1.length + prefix = $2 + prefix_length = prefix.length + + flag = case prefix + when "*","-" then ListBase::BULLET + when /^\d/ then ListBase::NUMBER + when /^[A-Z]/ then ListBase::UPPERALPHA + when /^[a-z]/ then ListBase::LOWERALPHA + else raise "Invalid List Type: #{self.inspect}" + end + + line.stamp(Line::LIST, level+1, prefix, flag) + text[margin, prefix_length] = " " * prefix_length + assign_types_to_lines(offset, level + 1) + next + end + + + if LABEL_LIST_RE =~ active_line + offset = margin + $1.length + prefix = $2 + prefix_length = prefix.length + + next if handled_labeled_list(line, level, margin, offset, prefix) + end + + # Headings look like + # = Main heading + # == Second level + # === Third + # + # Headings reset the level to 0 + + if active_line[0] == ?= and active_line =~ /^(=+)\s*(.*)/ + prefix_length = $1.length + prefix_length = 6 if prefix_length > 6 + line.stamp(Line::HEADING, 0, prefix_length) + line.strip_leading(margin + prefix_length) + next + end + + # If the character's a space, then we have verbatim text, + # otherwise + + if active_line[0] == SPACE + line.strip_leading(margin) if margin > 0 + line.stamp(Line::VERBATIM, level) + else + line.stamp(Line::PARAGRAPH, level) + end + end + end + + # Handle labeled list entries, We have a special case + # to deal with. Because the labels can be long, they force + # the remaining block of text over the to right: + # + # this is a long label that I wrote:: and here is the + # block of text with + # a silly margin + # + # So we allow the special case. If the label is followed + # by nothing, and if the following line is indented, then + # we take the indent of that line as the new margin + # + # this is a long label that I wrote:: + # here is a more reasonably indented block which + # will ab attached to the label. + # + + def handled_labeled_list(line, level, margin, offset, prefix) + prefix_length = prefix.length + text = line.text + flag = nil + case prefix + when /^\[/ + flag = ListBase::LABELED + prefix = prefix[1, prefix.length-2] + when /:$/ + flag = ListBase::NOTE + prefix.chop! + else raise "Invalid List Type: #{self.inspect}" + end + + # body is on the next line + + if text.length <= offset + original_line = line + line = @lines.next + return(false) unless line + text = line.text + + for i in 0..margin + if text[i] != SPACE + @lines.unget + return false + end + end + i = margin + i += 1 while text[i] == SPACE + if i >= text.length + @lines.unget + return false + else + offset = i + prefix_length = 0 + @lines.delete(original_line) + end + end + + line.stamp(Line::LIST, level+1, prefix, flag) + text[margin, prefix_length] = " " * prefix_length + assign_types_to_lines(offset, level + 1) + return true + end + + # Return a block consisting of fragments which are + # paragraphs, list entries or verbatim text. We merge consecutive + # lines of the same type and level together. We are also slightly + # tricky with lists: the lines following a list introduction + # look like paragraph lines at the next level, and we remap them + # into list entries instead + + def group_lines + @lines.rewind + + inList = false + wantedType = wantedLevel = nil + + block = LineCollection.new + group = nil + + while line = @lines.next + if line.level == wantedLevel and line.type == wantedType + group.add_text(line.text) + else + group = block.fragment_for(line) + block.add(group) + if line.type == Line::LIST + wantedType = Line::PARAGRAPH + else + wantedType = line.type + end + wantedLevel = line.type == Line::HEADING ? line.param : line.level + end + end + + block.normalize + block + end + + ## for debugging, we allow access to our line contents as text + def content + @lines.as_text + end + public :content + + ## for debugging, return the list of line types + def get_line_types + @lines.line_types + end + public :get_line_types + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/fragments.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/fragments.rb new file mode 100644 index 0000000000..6ca06382ab --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/fragments.rb @@ -0,0 +1,328 @@ +require 'rdoc/markup/simple_markup/lines.rb' +#require 'rdoc/markup/simple_markup/to_flow.rb' + +module SM + + ## + # A Fragment is a chunk of text, subclassed as a paragraph, a list + # entry, or verbatim text + + class Fragment + attr_reader :level, :param, :txt + attr_accessor :type + + def initialize(level, param, type, txt) + @level = level + @param = param + @type = type + @txt = "" + add_text(txt) if txt + end + + def add_text(txt) + @txt << " " if @txt.length > 0 + @txt << txt.tr_s("\n ", " ").strip + end + + def to_s + "L#@level: #{self.class.name.split('::')[-1]}\n#@txt" + end + + ###### + # This is a simple factory system that lets us associate fragement + # types (a string) with a subclass of fragment + + TYPE_MAP = {} + + def Fragment.type_name(name) + TYPE_MAP[name] = self + end + + def Fragment.for(line) + klass = TYPE_MAP[line.type] || + raise("Unknown line type: '#{line.type.inspect}:' '#{line.text}'") + return klass.new(line.level, line.param, line.flag, line.text) + end + end + + ## + # A paragraph is a fragment which gets wrapped to fit. We remove all + # newlines when we're created, and have them put back on output + + class Paragraph < Fragment + type_name Line::PARAGRAPH + end + + class BlankLine < Paragraph + type_name Line::BLANK + end + + class Heading < Paragraph + type_name Line::HEADING + + def head_level + @param.to_i + end + end + + ## + # A List is a fragment with some kind of label + # + + class ListBase < Paragraph + # List types + BULLET = :BULLET + NUMBER = :NUMBER + UPPERALPHA = :UPPERALPHA + LOWERALPHA = :LOWERALPHA + LABELED = :LABELED + NOTE = :NOTE + end + + class ListItem < ListBase + type_name Line::LIST + + # def label + # am = AttributeManager.new(@param) + # am.flow + # end + end + + class ListStart < ListBase + def initialize(level, param, type) + super(level, param, type, nil) + end + end + + class ListEnd < ListBase + def initialize(level, type) + super(level, "", type, nil) + end + end + + ## + # Verbatim code contains lines that don't get wrapped. + + class Verbatim < Fragment + type_name Line::VERBATIM + + def add_text(txt) + @txt << txt.chomp << "\n" + end + + end + + ## + # A horizontal rule + class Rule < Fragment + type_name Line::RULE + end + + + # Collect groups of lines together. Each group + # will end up containing a flow of text + + class LineCollection + + def initialize + @fragments = [] + end + + def add(fragment) + @fragments << fragment + end + + def each(&b) + @fragments.each(&b) + end + + # For testing + def to_a + @fragments.map {|fragment| fragment.to_s} + end + + # Factory for different fragment types + def fragment_for(*args) + Fragment.for(*args) + end + + # tidy up at the end + def normalize + change_verbatim_blank_lines + add_list_start_and_ends + add_list_breaks + tidy_blank_lines + end + + def to_s + @fragments.join("\n----\n") + end + + def accept(am, visitor) + + visitor.start_accepting + + @fragments.each do |fragment| + case fragment + when Verbatim + visitor.accept_verbatim(am, fragment) + when Rule + visitor.accept_rule(am, fragment) + when ListStart + visitor.accept_list_start(am, fragment) + when ListEnd + visitor.accept_list_end(am, fragment) + when ListItem + visitor.accept_list_item(am, fragment) + when BlankLine + visitor.accept_blank_line(am, fragment) + when Heading + visitor.accept_heading(am, fragment) + when Paragraph + visitor.accept_paragraph(am, fragment) + end + end + + visitor.end_accepting + end + ####### + private + ####### + + # If you have: + # + # normal paragraph text. + # + # this is code + # + # and more code + # + # You'll end up with the fragments Paragraph, BlankLine, + # Verbatim, BlankLine, Verbatim, BlankLine, etc + # + # The BlankLine in the middle of the verbatim chunk needs to + # be changed to a real verbatim newline, and the two + # verbatim blocks merged + # + # + def change_verbatim_blank_lines + frag_block = nil + blank_count = 0 + @fragments.each_with_index do |frag, i| + if frag_block.nil? + frag_block = frag if Verbatim === frag + else + case frag + when Verbatim + blank_count.times { frag_block.add_text("\n") } + blank_count = 0 + frag_block.add_text(frag.txt) + @fragments[i] = nil # remove out current fragment + when BlankLine + if frag_block + blank_count += 1 + @fragments[i] = nil + end + else + frag_block = nil + blank_count = 0 + end + end + end + @fragments.compact! + end + + # List nesting is implicit given the level of + # Make it explicit, just to make life a tad + # easier for the output processors + + def add_list_start_and_ends + level = 0 + res = [] + type_stack = [] + + @fragments.each do |fragment| + # $stderr.puts "#{level} : #{fragment.class.name} : #{fragment.level}" + new_level = fragment.level + while (level < new_level) + level += 1 + type = fragment.type + res << ListStart.new(level, fragment.param, type) if type + type_stack.push type + # $stderr.puts "Start: #{level}" + end + + while level > new_level + type = type_stack.pop + res << ListEnd.new(level, type) if type + level -= 1 + # $stderr.puts "End: #{level}, #{type}" + end + + res << fragment + level = fragment.level + end + level.downto(1) do |i| + type = type_stack.pop + res << ListEnd.new(i, type) if type + end + + @fragments = res + end + + # now insert start/ends between list entries at the + # same level that have different element types + + def add_list_breaks + res = @fragments + + @fragments = [] + list_stack = [] + + res.each do |fragment| + case fragment + when ListStart + list_stack.push fragment + when ListEnd + start = list_stack.pop + fragment.type = start.type + when ListItem + l = list_stack.last + if fragment.type != l.type + @fragments << ListEnd.new(l.level, l.type) + start = ListStart.new(l.level, fragment.param, fragment.type) + @fragments << start + list_stack.pop + list_stack.push start + end + else + ; + end + @fragments << fragment + end + end + + # Finally tidy up the blank lines: + # * change Blank/ListEnd into ListEnd/Blank + # * remove blank lines at the front + + def tidy_blank_lines + (@fragments.size - 1).times do |i| + if @fragments[i].kind_of?(BlankLine) and + @fragments[i+1].kind_of?(ListEnd) + @fragments[i], @fragments[i+1] = @fragments[i+1], @fragments[i] + end + end + + # remove leading blanks + @fragments.each_with_index do |f, i| + break unless f.kind_of? BlankLine + @fragments[i] = nil + end + + @fragments.compact! + end + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/inline.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/inline.rb new file mode 100644 index 0000000000..d54fe1e667 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/inline.rb @@ -0,0 +1,340 @@ +module SM + + # We manage a set of attributes. Each attribute has a symbol name + # and a bit value + + class Attribute + SPECIAL = 1 + + @@name_to_bitmap = { :_SPECIAL_ => SPECIAL } + @@next_bitmap = 2 + + def Attribute.bitmap_for(name) + bitmap = @@name_to_bitmap[name] + if !bitmap + bitmap = @@next_bitmap + @@next_bitmap <<= 1 + @@name_to_bitmap[name] = bitmap + end + bitmap + end + + def Attribute.as_string(bitmap) + return "none" if bitmap.zero? + res = [] + @@name_to_bitmap.each do |name, bit| + res << name if (bitmap & bit) != 0 + end + res.join(",") + end + + def Attribute.each_name_of(bitmap) + @@name_to_bitmap.each do |name, bit| + next if bit == SPECIAL + yield name.to_s if (bitmap & bit) != 0 + end + end + end + + + # An AttrChanger records a change in attributes. It contains + # a bitmap of the attributes to turn on, and a bitmap of those to + # turn off + + AttrChanger = Struct.new(:turn_on, :turn_off) + class AttrChanger + def to_s + "Attr: +#{Attribute.as_string(@turn_on)}/-#{Attribute.as_string(@turn_on)}" + end + end + + # An array of attributes which parallels the characters in a string + class AttrSpan + def initialize(length) + @attrs = Array.new(length, 0) + end + + def set_attrs(start, length, bits) + for i in start ... (start+length) + @attrs[i] |= bits + end + end + + def [](n) + @attrs[n] + end + end + + ## + # Hold details of a special sequence + + class Special + attr_reader :type + attr_accessor :text + + def initialize(type, text) + @type, @text = type, text + end + + def ==(o) + self.text == o.text && self.type == o.type + end + + def to_s + "Special: type=#{type}, text=#{text.dump}" + end + end + + class AttributeManager + + NULL = "\000".freeze + + ## + # We work by substituting non-printing characters in to the + # text. For now I'm assuming that I can substitute + # a character in the range 0..8 for a 7 bit character + # without damaging the encoded string, but this might + # be optimistic + # + + A_PROTECT = 004 + PROTECT_ATTR = A_PROTECT.chr + + # This maps delimiters that occur around words (such as + # *bold* or +tt+) where the start and end delimiters + # and the same. This lets us optimize the regexp + MATCHING_WORD_PAIRS = {} + + # And this is used when the delimiters aren't the same. In this + # case the hash maps a pattern to the attribute character + WORD_PAIR_MAP = {} + + # This maps HTML tags to the corresponding attribute char + HTML_TAGS = {} + + # And this maps _special_ sequences to a name. A special sequence + # is something like a WikiWord + SPECIAL = {} + + # Return an attribute object with the given turn_on + # and turn_off bits set + + def attribute(turn_on, turn_off) + AttrChanger.new(turn_on, turn_off) + end + + + def change_attribute(current, new) + diff = current ^ new + attribute(new & diff, current & diff) + end + + def changed_attribute_by_name(current_set, new_set) + current = new = 0 + current_set.each {|name| current |= Attribute.bitmap_for(name) } + new_set.each {|name| new |= Attribute.bitmap_for(name) } + change_attribute(current, new) + end + + def copy_string(start_pos, end_pos) + res = @str[start_pos...end_pos] + res.gsub!(/\000/, '') + res + end + + # Map attributes like textto the sequence \001\002\001\003, + # where is a per-attribute specific character + + def convert_attrs(str, attrs) + # first do matching ones + tags = MATCHING_WORD_PAIRS.keys.join("") + re = "(^|\\W)([#{tags}])([A-Za-z_]+?)\\2(\\W|\$)" +# re = "(^|\\W)([#{tags}])(\\S+?)\\2(\\W|\$)" + 1 while str.gsub!(Regexp.new(re)) { + attr = MATCHING_WORD_PAIRS[$2]; + attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr) + $1 + NULL*$2.length + $3 + NULL*$2.length + $4 + } + + # then non-matching + unless WORD_PAIR_MAP.empty? + WORD_PAIR_MAP.each do |regexp, attr| + str.gsub!(regexp) { + attrs.set_attrs($`.length + $1.length, $2.length, attr) + NULL*$1.length + $2 + NULL*$3.length + } + end + end + end + + def convert_html(str, attrs) + tags = HTML_TAGS.keys.join("|") + re = "<(#{tags})>(.*?)" + 1 while str.gsub!(Regexp.new(re, Regexp::IGNORECASE)) { + attr = HTML_TAGS[$1.downcase] + html_length = $1.length + 2 + seq = NULL * html_length + attrs.set_attrs($`.length + html_length, $2.length, attr) + seq + $2 + seq + NULL + } + end + + def convert_specials(str, attrs) + unless SPECIAL.empty? + SPECIAL.each do |regexp, attr| + str.scan(regexp) do + attrs.set_attrs($`.length, $&.length, attr | Attribute::SPECIAL) + end + end + end + end + + # A \ in front of a character that would normally be + # processed turns off processing. We do this by turning + # \< into <#{PROTECT} + + PROTECTABLE = [ "<" << "\\" ] #" + + + def mask_protected_sequences + protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])") + @str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}") + end + + def unmask_protected_sequences + @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000") + end + + def initialize + add_word_pair("*", "*", :BOLD) + add_word_pair("_", "_", :EM) + add_word_pair("+", "+", :TT) + + add_html("em", :EM) + add_html("i", :EM) + add_html("b", :BOLD) + add_html("tt", :TT) + add_html("code", :TT) + + add_special(//, :COMMENT) + end + + def add_word_pair(start, stop, name) + raise "Word flags may not start '<'" if start[0] == ?< + bitmap = Attribute.bitmap_for(name) + if start == stop + MATCHING_WORD_PAIRS[start] = bitmap + else + pattern = Regexp.new("(" + Regexp.escape(start) + ")" + +# "([A-Za-z]+)" + + "(\\S+)" + + "(" + Regexp.escape(stop) +")") + WORD_PAIR_MAP[pattern] = bitmap + end + PROTECTABLE << start[0,1] + PROTECTABLE.uniq! + end + + def add_html(tag, name) + HTML_TAGS[tag.downcase] = Attribute.bitmap_for(name) + end + + def add_special(pattern, name) + SPECIAL[pattern] = Attribute.bitmap_for(name) + end + + def flow(str) + @str = str + + puts("Before flow, str='#{@str.dump}'") if $DEBUG + mask_protected_sequences + + @attrs = AttrSpan.new(@str.length) + + puts("After protecting, str='#{@str.dump}'") if $DEBUG + convert_attrs(@str, @attrs) + convert_html(@str, @attrs) + convert_specials(str, @attrs) + unmask_protected_sequences + puts("After flow, str='#{@str.dump}'") if $DEBUG + return split_into_flow + end + + def display_attributes + puts + puts @str.tr(NULL, "!") + bit = 1 + 16.times do |bno| + line = "" + @str.length.times do |i| + if (@attrs[i] & bit) == 0 + line << " " + else + if bno.zero? + line << "S" + else + line << ("%d" % (bno+1)) + end + end + end + puts(line) unless line =~ /^ *$/ + bit <<= 1 + end + end + + def split_into_flow + + display_attributes if $DEBUG + + res = [] + current_attr = 0 + str = "" + + + str_len = @str.length + + # skip leading invisible text + i = 0 + i += 1 while i < str_len and @str[i].zero? + start_pos = i + + # then scan the string, chunking it on attribute changes + while i < str_len + new_attr = @attrs[i] + if new_attr != current_attr + if i > start_pos + res << copy_string(start_pos, i) + start_pos = i + end + + res << change_attribute(current_attr, new_attr) + current_attr = new_attr + + if (current_attr & Attribute::SPECIAL) != 0 + i += 1 while i < str_len and (@attrs[i] & Attribute::SPECIAL) != 0 + res << Special.new(current_attr, copy_string(start_pos, i)) + start_pos = i + next + end + end + + # move on, skipping any invisible characters + begin + i += 1 + end while i < str_len and @str[i].zero? + end + + # tidy up trailing text + if start_pos < str_len + res << copy_string(start_pos, str_len) + end + + # and reset to all attributes off + res << change_attribute(current_attr, 0) if current_attr != 0 + + return res + end + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/lines.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/lines.rb new file mode 100644 index 0000000000..4e294f27dc --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/lines.rb @@ -0,0 +1,151 @@ +########################################################################## +# +# We store the lines we're working on as objects of class Line. +# These contain the text of the line, along with a flag indicating the +# line type, and an indentation level + +module SM + + class Line + INFINITY = 9999 + + BLANK = :BLANK + HEADING = :HEADING + LIST = :LIST + RULE = :RULE + PARAGRAPH = :PARAGRAPH + VERBATIM = :VERBATIM + + # line type + attr_accessor :type + + # The indentation nesting level + attr_accessor :level + + # The contents + attr_accessor :text + + # A prefix or parameter. For LIST lines, this is + # the text that introduced the list item (the label) + attr_accessor :param + + # A flag. For list lines, this is the type of the list + attr_accessor :flag + + # the number of leading spaces + attr_accessor :leading_spaces + + # true if this line has been deleted from the list of lines + attr_accessor :deleted + + + def initialize(text) + @text = text.dup + @deleted = false + + # expand tabs + 1 while @text.gsub!(/\t+/) { ' ' * (8*$&.length - $`.length % 8)} && $~ #` + + # Strip trailing whitespace + @text.sub!(/\s+$/, '') + + # and look for leading whitespace + if @text.length > 0 + @text =~ /^(\s*)/ + @leading_spaces = $1.length + else + @leading_spaces = INFINITY + end + end + + # Return true if this line is blank + def isBlank? + @text.length.zero? + end + + # stamp a line with a type, a level, a prefix, and a flag + def stamp(type, level, param="", flag=nil) + @type, @level, @param, @flag = type, level, param, flag + end + + ## + # Strip off the leading margin + # + + def strip_leading(size) + if @text.size > size + @text[0,size] = "" + else + @text = "" + end + end + + def to_s + "#@type#@level: #@text" + end + end + + ############################################################################### + # + # A container for all the lines + # + + class Lines + include Enumerable + + attr_reader :lines # for debugging + + def initialize(lines) + @lines = lines + rewind + end + + def empty? + @lines.size.zero? + end + + def each + @lines.each do |line| + yield line unless line.deleted + end + end + +# def [](index) +# @lines[index] +# end + + def rewind + @nextline = 0 + end + + def next + begin + res = @lines[@nextline] + @nextline += 1 if @nextline < @lines.size + end while res and res.deleted and @nextline < @lines.size + res + end + + def unget + @nextline -= 1 + end + + def delete(a_line) + a_line.deleted = true + end + + def normalize + margin = @lines.collect{|l| l.leading_spaces}.min + margin = 0 if margin == Line::INFINITY + @lines.each {|line| line.strip_leading(margin) } if margin > 0 + end + + def as_text + @lines.map {|l| l.text}.join("\n") + end + + def line_types + @lines.map {|l| l.type } + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/preprocess.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/preprocess.rb new file mode 100644 index 0000000000..101c9bdeb1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/preprocess.rb @@ -0,0 +1,73 @@ +module SM + + ## + # Handle common directives that can occur in a block of text: + # + # : include : filename + # + + class PreProcess + + def initialize(input_file_name, include_path) + @input_file_name = input_file_name + @include_path = include_path + end + + # Look for common options in a chunk of text. Options that + # we don't handle are passed back to our caller + # as |directive, param| + + def handle(text) + text.gsub!(/^([ \t#]*):(\w+):\s*(.+)?\n/) do + prefix = $1 + directive = $2.downcase + param = $3 + + case directive + when "include" + filename = param.split[0] + include_file(filename, prefix) + + else + yield(directive, param) + end + end + end + + ####### + private + ####### + + # Include a file, indenting it correctly + + def include_file(name, indent) + if (full_name = find_include_file(name)) + content = File.open(full_name) {|f| f.read} + # strip leading '#'s, but only if all lines start with them + if content =~ /^[^#]/ + content.gsub(/^/, indent) + else + content.gsub(/^#?/, indent) + end + else + $stderr.puts "Couldn't find file to include: '#{name}'" + '' + end + end + + # Look for the given file in the directory containing the current + # file, and then in each of the directories specified in the + # RDOC_INCLUDE path + + def find_include_file(name) + to_search = [ File.dirname(@input_file_name) ].concat @include_path + to_search.each do |dir| + full_name = File.join(dir, name) + stat = File.stat(full_name) rescue next + return full_name if stat.readable? + end + nil + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/to_flow.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/to_flow.rb new file mode 100644 index 0000000000..048e71abce --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/to_flow.rb @@ -0,0 +1,188 @@ +require 'rdoc/markup/simple_markup/fragments' +require 'rdoc/markup/simple_markup/inline' +require 'cgi' + +module SM + + module Flow + P = Struct.new(:body) + VERB = Struct.new(:body) + RULE = Struct.new(:width) + class LIST + attr_reader :type, :contents + def initialize(type) + @type = type + @contents = [] + end + def <<(stuff) + @contents << stuff + end + end + LI = Struct.new(:label, :body) + H = Struct.new(:level, :text) + end + + class ToFlow + LIST_TYPE_TO_HTML = { + SM::ListBase::BULLET => [ "
    ", "
" ], + SM::ListBase::NUMBER => [ "
    ", "
" ], + SM::ListBase::UPPERALPHA => [ "
    ", "
" ], + SM::ListBase::LOWERALPHA => [ "
    ", "
" ], + SM::ListBase::LABELED => [ "
", "
" ], + SM::ListBase::NOTE => [ "", "
" ], + } + + InlineTag = Struct.new(:bit, :on, :off) + + def initialize + init_tags + end + + ## + # Set up the standard mapping of attributes to HTML tags + # + def init_tags + @attr_tags = [ + InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "", ""), + InlineTag.new(SM::Attribute.bitmap_for(:TT), "", ""), + InlineTag.new(SM::Attribute.bitmap_for(:EM), "", ""), + ] + end + + ## + # Add a new set of HTML tags for an attribute. We allow + # separate start and end tags for flexibility + # + def add_tag(name, start, stop) + @attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop) + end + + ## + # Given an HTML tag, decorate it with class information + # and the like if required. This is a no-op in the base + # class, but is overridden in HTML output classes that + # implement style sheets + + def annotate(tag) + tag + end + + ## + # Here's the client side of the visitor pattern + + def start_accepting + @res = [] + @list_stack = [] + end + + def end_accepting + @res + end + + def accept_paragraph(am, fragment) + @res << Flow::P.new((convert_flow(am.flow(fragment.txt)))) + end + + def accept_verbatim(am, fragment) + @res << Flow::VERB.new((convert_flow(am.flow(fragment.txt)))) + end + + def accept_rule(am, fragment) + size = fragment.param + size = 10 if size > 10 + @res << Flow::RULE.new(size) + end + + def accept_list_start(am, fragment) + @list_stack.push(@res) + list = Flow::LIST.new(fragment.type) + @res << list + @res = list + end + + def accept_list_end(am, fragment) + @res = @list_stack.pop + end + + def accept_list_item(am, fragment) + @res << Flow::LI.new(fragment.param, convert_flow(am.flow(fragment.txt))) + end + + def accept_blank_line(am, fragment) + # @res << annotate("

") << "\n" + end + + def accept_heading(am, fragment) + @res << Flow::H.new(fragment.head_level, convert_flow(am.flow(fragment.txt))) + end + + + ####################################################################### + + private + + ####################################################################### + + def on_tags(res, item) + attr_mask = item.turn_on + return if attr_mask.zero? + + @attr_tags.each do |tag| + if attr_mask & tag.bit != 0 + res << annotate(tag.on) + end + end + end + + def off_tags(res, item) + attr_mask = item.turn_off + return if attr_mask.zero? + + @attr_tags.reverse_each do |tag| + if attr_mask & tag.bit != 0 + res << annotate(tag.off) + end + end + end + + def convert_flow(flow) + res = "" + flow.each do |item| + case item + when String + res << convert_string(item) + when AttrChanger + off_tags(res, item) + on_tags(res, item) + when Special + res << convert_special(item) + else + raise "Unknown flow element: #{item.inspect}" + end + end + res + end + + # some of these patterns are taken from SmartyPants... + + def convert_string(item) + CGI.escapeHTML(item) + end + + def convert_special(special) + handled = false + Attribute.each_name_of(special.type) do |name| + method_name = "handle_special_#{name}" + if self.respond_to? method_name + special.text = send(method_name, special) + handled = true + end + end + raise "Unhandled special: #{special}" unless handled + special.text + end + + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/to_html.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/to_html.rb new file mode 100644 index 0000000000..26b5f4ce70 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/to_html.rb @@ -0,0 +1,289 @@ +require 'rdoc/markup/simple_markup/fragments' +require 'rdoc/markup/simple_markup/inline' + +require 'cgi' + +module SM + + class ToHtml + + LIST_TYPE_TO_HTML = { + ListBase::BULLET => [ "

    ", "
" ], + ListBase::NUMBER => [ "
    ", "
" ], + ListBase::UPPERALPHA => [ "
    ", "
" ], + ListBase::LOWERALPHA => [ "
    ", "
" ], + ListBase::LABELED => [ "
", "
" ], + ListBase::NOTE => [ "", "
" ], + } + + InlineTag = Struct.new(:bit, :on, :off) + + def initialize + init_tags + end + + ## + # Set up the standard mapping of attributes to HTML tags + # + def init_tags + @attr_tags = [ + InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "", ""), + InlineTag.new(SM::Attribute.bitmap_for(:TT), "", ""), + InlineTag.new(SM::Attribute.bitmap_for(:EM), "", ""), + ] + end + + ## + # Add a new set of HTML tags for an attribute. We allow + # separate start and end tags for flexibility + # + def add_tag(name, start, stop) + @attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop) + end + + ## + # Given an HTML tag, decorate it with class information + # and the like if required. This is a no-op in the base + # class, but is overridden in HTML output classes that + # implement style sheets + + def annotate(tag) + tag + end + + ## + # Here's the client side of the visitor pattern + + def start_accepting + @res = "" + @in_list_entry = [] + end + + def end_accepting + @res + end + + def accept_paragraph(am, fragment) + @res << annotate("

") + "\n" + @res << wrap(convert_flow(am.flow(fragment.txt))) + @res << annotate("

") + "\n" + end + + def accept_verbatim(am, fragment) + @res << annotate("
") + "\n"
+      @res << CGI.escapeHTML(fragment.txt)
+      @res << annotate("
") << "\n" + end + + def accept_rule(am, fragment) + size = fragment.param + size = 10 if size > 10 + @res << "
" + end + + def accept_list_start(am, fragment) + @res << html_list_name(fragment.type, true) <<"\n" + @in_list_entry.push false + end + + def accept_list_end(am, fragment) + if tag = @in_list_entry.pop + @res << annotate(tag) << "\n" + end + @res << html_list_name(fragment.type, false) <<"\n" + end + + def accept_list_item(am, fragment) + if tag = @in_list_entry.last + @res << annotate(tag) << "\n" + end + @res << list_item_start(am, fragment) + @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n" + @in_list_entry[-1] = list_end_for(fragment.type) + end + + def accept_blank_line(am, fragment) + # @res << annotate("

") << "\n" + end + + def accept_heading(am, fragment) + @res << convert_heading(fragment.head_level, am.flow(fragment.txt)) + end + + # This is a higher speed (if messier) version of wrap + + def wrap(txt, line_len = 76) + res = "" + sp = 0 + ep = txt.length + while sp < ep + # scan back for a space + p = sp + line_len - 1 + if p >= ep + p = ep + else + while p > sp and txt[p] != ?\s + p -= 1 + end + if p <= sp + p = sp + line_len + while p < ep and txt[p] != ?\s + p += 1 + end + end + end + res << txt[sp...p] << "\n" + sp = p + sp += 1 while sp < ep and txt[sp] == ?\s + end + res + end + + ####################################################################### + + private + + ####################################################################### + + def on_tags(res, item) + attr_mask = item.turn_on + return if attr_mask.zero? + + @attr_tags.each do |tag| + if attr_mask & tag.bit != 0 + res << annotate(tag.on) + end + end + end + + def off_tags(res, item) + attr_mask = item.turn_off + return if attr_mask.zero? + + @attr_tags.reverse_each do |tag| + if attr_mask & tag.bit != 0 + res << annotate(tag.off) + end + end + end + + def convert_flow(flow) + res = "" + flow.each do |item| + case item + when String + res << convert_string(item) + when AttrChanger + off_tags(res, item) + on_tags(res, item) + when Special + res << convert_special(item) + else + raise "Unknown flow element: #{item.inspect}" + end + end + res + end + + # some of these patterns are taken from SmartyPants... + + def convert_string(item) + CGI.escapeHTML(item). + + + # convert -- to em-dash, (-- to en-dash) + gsub(/---?/, '—'). #gsub(/--/, '–'). + + # convert ... to elipsis (and make sure .... becomes .) + gsub(/\.\.\.\./, '.…').gsub(/\.\.\./, '…'). + + # convert single closing quote + gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1’" }. + gsub(%r{\'(?=\W|s\b)}) { "’" }. + + # convert single opening quote + gsub(/'/, '‘'). + + # convert double closing quote + gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}) { "#$1”" }. + + # convert double opening quote + gsub(/'/, '“'). + + # convert copyright + gsub(/\(c\)/, '©'). + + # convert and registered trademark + gsub(/\(r\)/, '®') + + end + + def convert_special(special) + handled = false + Attribute.each_name_of(special.type) do |name| + method_name = "handle_special_#{name}" + if self.respond_to? method_name + special.text = send(method_name, special) + handled = true + end + end + raise "Unhandled special: #{special}" unless handled + special.text + end + + def convert_heading(level, flow) + res = + annotate("") + + convert_flow(flow) + + annotate("\n") + end + + def html_list_name(list_type, is_open_tag) + tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}") + annotate(tags[ is_open_tag ? 0 : 1]) + end + + def list_item_start(am, fragment) + case fragment.type + when ListBase::BULLET, ListBase::NUMBER + annotate("

  • ") + + when ListBase::UPPERALPHA + annotate("
  • ") + + when ListBase::LOWERALPHA + annotate("
  • ") + + when ListBase::LABELED + annotate("
    ") + + convert_flow(am.flow(fragment.param)) + + annotate("
    ") + + annotate("
    ") + + when ListBase::NOTE + annotate("") + + annotate("") + + convert_flow(am.flow(fragment.param)) + + annotate("") + + annotate("") + else + raise "Invalid list type" + end + end + + def list_end_for(fragment_type) + case fragment_type + when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA + "
  • " + when ListBase::LABELED + "" + when ListBase::NOTE + "" + else + raise "Invalid list type" + end + end + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/to_latex.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/to_latex.rb new file mode 100644 index 0000000000..6c16278652 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/simple_markup/to_latex.rb @@ -0,0 +1,333 @@ +require 'rdoc/markup/simple_markup/fragments' +require 'rdoc/markup/simple_markup/inline' + +require 'cgi' + +module SM + + # Convert SimpleMarkup to basic LaTeX report format + + class ToLaTeX + + BS = "\020" # \ + OB = "\021" # { + CB = "\022" # } + DL = "\023" # Dollar + + BACKSLASH = "#{BS}symbol#{OB}92#{CB}" + HAT = "#{BS}symbol#{OB}94#{CB}" + BACKQUOTE = "#{BS}symbol#{OB}0#{CB}" + TILDE = "#{DL}#{BS}sim#{DL}" + LESSTHAN = "#{DL}<#{DL}" + GREATERTHAN = "#{DL}>#{DL}" + + def self.l(str) + str.tr('\\', BS).tr('{', OB).tr('}', CB).tr('$', DL) + end + + def l(arg) + SM::ToLaTeX.l(arg) + end + + LIST_TYPE_TO_LATEX = { + ListBase::BULLET => [ l("\\begin{itemize}"), l("\\end{itemize}") ], + ListBase::NUMBER => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\arabic" ], + ListBase::UPPERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\Alph" ], + ListBase::LOWERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\alph" ], + ListBase::LABELED => [ l("\\begin{description}"), l("\\end{description}") ], + ListBase::NOTE => [ + l("\\begin{tabularx}{\\linewidth}{@{} l X @{}}"), + l("\\end{tabularx}") ], + } + + InlineTag = Struct.new(:bit, :on, :off) + + def initialize + init_tags + @list_depth = 0 + @prev_list_types = [] + end + + ## + # Set up the standard mapping of attributes to LaTeX + # + def init_tags + @attr_tags = [ + InlineTag.new(SM::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")), + InlineTag.new(SM::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")), + InlineTag.new(SM::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")), + ] + end + + ## + # Escape a LaTeX string + def escape(str) +# $stderr.print "FE: ", str + s = str. +# sub(/\s+$/, ''). + gsub(/([_\${}&%#])/, "#{BS}\\1"). + gsub(/\\/, BACKSLASH). + gsub(/\^/, HAT). + gsub(/~/, TILDE). + gsub(//, GREATERTHAN). + gsub(/,,/, ",{},"). + gsub(/\`/, BACKQUOTE) +# $stderr.print "-> ", s, "\n" + s + end + + ## + # Add a new set of LaTeX tags for an attribute. We allow + # separate start and end tags for flexibility + # + def add_tag(name, start, stop) + @attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop) + end + + + ## + # Here's the client side of the visitor pattern + + def start_accepting + @res = "" + @in_list_entry = [] + end + + def end_accepting + @res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$') + end + + def accept_paragraph(am, fragment) + @res << wrap(convert_flow(am.flow(fragment.txt))) + @res << "\n" + end + + def accept_verbatim(am, fragment) + @res << "\n\\begin{code}\n" + @res << fragment.txt.sub(/[\n\s]+\Z/, '') + @res << "\n\\end{code}\n\n" + end + + def accept_rule(am, fragment) + size = fragment.param + size = 10 if size > 10 + @res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n" + end + + def accept_list_start(am, fragment) + @res << list_name(fragment.type, true) <<"\n" + @in_list_entry.push false + end + + def accept_list_end(am, fragment) + if tag = @in_list_entry.pop + @res << tag << "\n" + end + @res << list_name(fragment.type, false) <<"\n" + end + + def accept_list_item(am, fragment) + if tag = @in_list_entry.last + @res << tag << "\n" + end + @res << list_item_start(am, fragment) + @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n" + @in_list_entry[-1] = list_end_for(fragment.type) + end + + def accept_blank_line(am, fragment) + # @res << "\n" + end + + def accept_heading(am, fragment) + @res << convert_heading(fragment.head_level, am.flow(fragment.txt)) + end + + # This is a higher speed (if messier) version of wrap + + def wrap(txt, line_len = 76) + res = "" + sp = 0 + ep = txt.length + while sp < ep + # scan back for a space + p = sp + line_len - 1 + if p >= ep + p = ep + else + while p > sp and txt[p] != ?\s + p -= 1 + end + if p <= sp + p = sp + line_len + while p < ep and txt[p] != ?\s + p += 1 + end + end + end + res << txt[sp...p] << "\n" + sp = p + sp += 1 while sp < ep and txt[sp] == ?\s + end + res + end + + ####################################################################### + + private + + ####################################################################### + + def on_tags(res, item) + attr_mask = item.turn_on + return if attr_mask.zero? + + @attr_tags.each do |tag| + if attr_mask & tag.bit != 0 + res << tag.on + end + end + end + + def off_tags(res, item) + attr_mask = item.turn_off + return if attr_mask.zero? + + @attr_tags.reverse_each do |tag| + if attr_mask & tag.bit != 0 + res << tag.off + end + end + end + + def convert_flow(flow) + res = "" + flow.each do |item| + case item + when String +# $stderr.puts "Converting '#{item}'" + res << convert_string(item) + when AttrChanger + off_tags(res, item) + on_tags(res, item) + when Special + res << convert_special(item) + else + raise "Unknown flow element: #{item.inspect}" + end + end + res + end + + # some of these patterns are taken from SmartyPants... + + def convert_string(item) + + escape(item). + + + # convert ... to elipsis (and make sure .... becomes .) + gsub(/\.\.\.\./, '.\ldots{}').gsub(/\.\.\./, '\ldots{}'). + + # convert single closing quote + gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1'" }. + gsub(%r{\'(?=\W|s\b)}) { "'" }. + + # convert single opening quote + gsub(/'/, '`'). + + # convert double closing quote + gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}) { "#$1''" }. + + # convert double opening quote + gsub(/"/, "``"). + + # convert copyright + gsub(/\(c\)/, '\copyright{}') + + end + + def convert_special(special) + handled = false + Attribute.each_name_of(special.type) do |name| + method_name = "handle_special_#{name}" + if self.respond_to? method_name + special.text = send(method_name, special) + handled = true + end + end + raise "Unhandled special: #{special}" unless handled + special.text + end + + def convert_heading(level, flow) + res = + case level + when 1 then "\\chapter{" + when 2 then "\\section{" + when 3 then "\\subsection{" + when 4 then "\\subsubsection{" + else "\\paragraph{" + end + + convert_flow(flow) + + "}\n" + end + + def list_name(list_type, is_open_tag) + tags = LIST_TYPE_TO_LATEX[list_type] || raise("Invalid list type: #{list_type.inspect}") + if tags[2] # enumerate + if is_open_tag + @list_depth += 1 + if @prev_list_types[@list_depth] != tags[2] + case @list_depth + when 1 + roman = "i" + when 2 + roman = "ii" + when 3 + roman = "iii" + when 4 + roman = "iv" + else + raise("Too deep list: level #{@list_depth}") + end + @prev_list_types[@list_depth] = tags[2] + return l("\\renewcommand{\\labelenum#{roman}}{#{tags[2]}{enum#{roman}}}") + "\n" + tags[0] + end + else + @list_depth -= 1 + end + end + tags[ is_open_tag ? 0 : 1] + end + + def list_item_start(am, fragment) + case fragment.type + when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA + "\\item " + + when ListBase::LABELED + "\\item[" + convert_flow(am.flow(fragment.param)) + "] " + + when ListBase::NOTE + convert_flow(am.flow(fragment.param)) + " & " + else + raise "Invalid list type" + end + end + + def list_end_for(fragment_type) + case fragment_type + when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA, ListBase::LABELED + "" + when ListBase::NOTE + "\\\\\n" + else + raise "Invalid list type" + end + end + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/test/AllTests.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/test/AllTests.rb new file mode 100644 index 0000000000..b9c8c9dfcc --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/test/AllTests.rb @@ -0,0 +1,2 @@ +require 'TestParse.rb' +require 'TestInline.rb' diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/test/TestInline.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/test/TestInline.rb new file mode 100644 index 0000000000..a067d4c24c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/test/TestInline.rb @@ -0,0 +1,154 @@ +require "test/unit" + +$:.unshift "../../.." + +require "rdoc/markup/simple_markup/inline" + +class TestInline < Test::Unit::TestCase + + + def setup + @am = SM::AttributeManager.new + + @bold_on = @am.changed_attribute_by_name([], [:BOLD]) + @bold_off = @am.changed_attribute_by_name([:BOLD], []) + + @tt_on = @am.changed_attribute_by_name([], [:TT]) + @tt_off = @am.changed_attribute_by_name([:TT], []) + + @em_on = @am.changed_attribute_by_name([], [:EM]) + @em_off = @am.changed_attribute_by_name([:EM], []) + + @bold_em_on = @am.changed_attribute_by_name([], [:BOLD] | [:EM]) + @bold_em_off = @am.changed_attribute_by_name([:BOLD] | [:EM], []) + + @em_then_bold = @am.changed_attribute_by_name([:EM], [:EM] | [:BOLD]) + + @em_to_bold = @am.changed_attribute_by_name([:EM], [:BOLD]) + + @am.add_word_pair("{", "}", :WOMBAT) + @wombat_on = @am.changed_attribute_by_name([], [:WOMBAT]) + @wombat_off = @am.changed_attribute_by_name([:WOMBAT], []) + end + + def crossref(text) + [ @am.changed_attribute_by_name([], [:CROSSREF] | [:_SPECIAL_]), + SM::Special.new(33, text), + @am.changed_attribute_by_name([:CROSSREF] | [:_SPECIAL_], []) + ] + end + + def test_special + # class names, variable names, file names, or instance variables + @am.add_special(/( + \b([A-Z]\w+(::\w+)*) + | \#\w+[!?=]? + | \b\w+([_\/\.]+\w+)+[!?=]? + )/x, + :CROSSREF) + + assert_equal(["cat"], @am.flow("cat")) + + assert_equal(["cat ", crossref("#fred"), " dog"].flatten, + @am.flow("cat #fred dog")) + + assert_equal([crossref("#fred"), " dog"].flatten, + @am.flow("#fred dog")) + + assert_equal(["cat ", crossref("#fred")].flatten, @am.flow("cat #fred")) + end + + def test_basic + assert_equal(["cat"], @am.flow("cat")) + + assert_equal(["cat ", @bold_on, "and", @bold_off, " dog"], + @am.flow("cat *and* dog")) + + assert_equal(["cat ", @bold_on, "AND", @bold_off, " dog"], + @am.flow("cat *AND* dog")) + + assert_equal(["cat ", @em_on, "And", @em_off, " dog"], + @am.flow("cat _And_ dog")) + + assert_equal(["cat *and dog*"], @am.flow("cat *and dog*")) + + assert_equal(["*cat and* dog"], @am.flow("*cat and* dog")) + + assert_equal(["cat *and ", @bold_on, "dog", @bold_off], + @am.flow("cat *and *dog*")) + + assert_equal(["cat ", @em_on, "and", @em_off, " dog"], + @am.flow("cat _and_ dog")) + + assert_equal(["cat_and_dog"], + @am.flow("cat_and_dog")) + + assert_equal(["cat ", @tt_on, "and", @tt_off, " dog"], + @am.flow("cat +and+ dog")) + + assert_equal(["cat ", @bold_on, "a_b_c", @bold_off, " dog"], + @am.flow("cat *a_b_c* dog")) + + assert_equal(["cat __ dog"], + @am.flow("cat __ dog")) + + assert_equal(["cat ", @em_on, "_", @em_off, " dog"], + @am.flow("cat ___ dog")) + + end + + def test_combined + assert_equal(["cat ", @em_on, "and", @em_off, " ", @bold_on, "dog", @bold_off], + @am.flow("cat _and_ *dog*")) + + assert_equal(["cat ", @em_on, "a__nd", @em_off, " ", @bold_on, "dog", @bold_off], + @am.flow("cat _a__nd_ *dog*")) + end + + def test_html_like + assert_equal(["cat ", @tt_on, "dog", @tt_off], @am.flow("cat dog")) + + assert_equal(["cat ", @em_on, "and", @em_off, " ", @bold_on, "dog", @bold_off], + @am.flow("cat and dog")) + + assert_equal(["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off], + @am.flow("cat and dog")) + + assert_equal(["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off], + @am.flow("cat and dog")) + + assert_equal(["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off], + @am.flow("cat and dog")) + + assert_equal([@tt_on, "cat", @tt_off, " ", @em_on, "and ", @em_to_bold, "dog", @bold_off], + @am.flow("cat and dog")) + + assert_equal(["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off], + @am.flow("cat and dog")) + + assert_equal(["cat ", @bold_em_on, "and", @bold_em_off, " dog"], + @am.flow("cat and dog")) + + + end + + def test_protect + assert_equal(['cat \\ dog'], @am.flow('cat \\ dog')) + + assert_equal(["cat dog"], @am.flow("cat \\dog")) + + assert_equal(["cat ", @em_on, "and", @em_off, " dog"], + @am.flow("cat and \\dog")) + + assert_equal(["*word* or text"], @am.flow("\\*word* or \\text")) + + assert_equal(["_cat_", @em_on, "dog", @em_off], + @am.flow("\\_cat_dog")) + end + + def test_adding + assert_equal(["cat ", @wombat_on, "and", @wombat_off, " dog" ], + @am.flow("cat {and} dog")) +# assert_equal(["cat {and} dog" ], @am.flow("cat \\{and} dog")) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/test/TestParse.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/test/TestParse.rb new file mode 100644 index 0000000000..3ec541ce7a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/markup/test/TestParse.rb @@ -0,0 +1,503 @@ +require 'test/unit' + +$:.unshift "../../.." + +require 'rdoc/markup/simple_markup' + +include SM + +class TestParse < Test::Unit::TestCase + + class MockOutput + def start_accepting + @res = [] + end + + def end_accepting + @res + end + + def accept_paragraph(am, fragment) + @res << fragment.to_s + end + + def accept_verbatim(am, fragment) + @res << fragment.to_s + end + + def accept_list_start(am, fragment) + @res << fragment.to_s + end + + def accept_list_end(am, fragment) + @res << fragment.to_s + end + + def accept_list_item(am, fragment) + @res << fragment.to_s + end + + def accept_blank_line(am, fragment) + @res << fragment.to_s + end + + def accept_heading(am, fragment) + @res << fragment.to_s + end + + def accept_rule(am, fragment) + @res << fragment.to_s + end + + end + + def basic_conv(str) + sm = SimpleMarkup.new + mock = MockOutput.new + sm.convert(str, mock) + sm.content + end + + def line_types(str, expected) + p = SimpleMarkup.new + mock = MockOutput.new + p.convert(str, mock) + assert_equal(expected, p.get_line_types.map{|type| type.to_s[0,1]}.join('')) + end + + def line_groups(str, expected) + p = SimpleMarkup.new + mock = MockOutput.new + + block = p.convert(str, mock) + + if block != expected + rows = (0...([expected.size, block.size].max)).collect{|i| + [expected[i]||"nil", block[i]||"nil"] + } + printf "\n\n%35s %35s\n", "Expected", "Got" + rows.each {|e,g| printf "%35s %35s\n", e.dump, g.dump } + end + + assert_equal(expected, block) + end + + def test_tabs + str = "hello\n dave" + assert_equal(str, basic_conv(str)) + str = "hello\n\tdave" + assert_equal("hello\n dave", basic_conv(str)) + str = "hello\n \tdave" + assert_equal("hello\n dave", basic_conv(str)) + str = "hello\n \tdave" + assert_equal("hello\n dave", basic_conv(str)) + str = "hello\n \tdave" + assert_equal("hello\n dave", basic_conv(str)) + str = "hello\n \tdave" + assert_equal("hello\n dave", basic_conv(str)) + str = "hello\n \tdave" + assert_equal("hello\n dave", basic_conv(str)) + str = "hello\n \tdave" + assert_equal("hello\n dave", basic_conv(str)) + str = "hello\n \tdave" + assert_equal("hello\n dave", basic_conv(str)) + str = "hello\n \tdave" + assert_equal("hello\n dave", basic_conv(str)) + str = ".\t\t." + assert_equal(". .", basic_conv(str)) + end + + def test_whitespace + assert_equal("hello", basic_conv("hello")) + assert_equal("hello", basic_conv(" hello ")) + assert_equal("hello", basic_conv(" \t \t hello\t\t")) + + assert_equal("1\n 2\n 3", basic_conv("1\n 2\n 3")) + assert_equal("1\n 2\n 3", basic_conv(" 1\n 2\n 3")) + + assert_equal("1\n 2\n 3\n1\n 2", basic_conv("1\n 2\n 3\n1\n 2")) + assert_equal("1\n 2\n 3\n1\n 2", basic_conv(" 1\n 2\n 3\n 1\n 2")) + + assert_equal("1\n 2\n\n 3", basic_conv(" 1\n 2\n\n 3")) + end + + def test_types + str = "now is the time" + line_types(str, 'P') + + str = "now is the time\nfor all good men" + line_types(str, 'PP') + + str = "now is the time\n code\nfor all good men" + line_types(str, 'PVP') + + str = "now is the time\n code\n more code\nfor all good men" + line_types(str, 'PVVP') + + str = "now is\n---\nthe time" + line_types(str, 'PRP') + + str = %{\ + now is + * l1 + * l2 + the time} + line_types(str, 'PLLP') + + str = %{\ + now is + * l1 + l1+ + * l2 + the time} + line_types(str, 'PLPLP') + + str = %{\ + now is + * l1 + * l1.1 + * l2 + the time} + line_types(str, 'PLLLP') + + str = %{\ + now is + * l1 + * l1.1 + text + code + code + + text + * l2 + the time} + line_types(str, 'PLLPVVBPLP') + + str = %{\ + now is + 1. l1 + * l1.1 + 2. l2 + the time} + line_types(str, 'PLLLP') + + str = %{\ + now is + [cat] l1 + * l1.1 + [dog] l2 + the time} + line_types(str, 'PLLLP') + + str = %{\ + now is + [cat] l1 + continuation + [dog] l2 + the time} + line_types(str, 'PLPLP') + end + + def test_groups + str = "now is the time" + line_groups(str, ["L0: Paragraph\nnow is the time"] ) + + str = "now is the time\nfor all good men" + line_groups(str, ["L0: Paragraph\nnow is the time for all good men"] ) + + str = %{\ + now is the time + code _line_ here + for all good men} + + line_groups(str, + [ "L0: Paragraph\nnow is the time", + "L0: Verbatim\n code _line_ here\n", + "L0: Paragraph\nfor all good men" + ] ) + + str = "now is the time\n code\n more code\nfor all good men" + line_groups(str, + [ "L0: Paragraph\nnow is the time", + "L0: Verbatim\n code\n more code\n", + "L0: Paragraph\nfor all good men" + ] ) + + str = %{\ + now is + * l1 + * l2 + the time} + line_groups(str, + [ "L0: Paragraph\nnow is", + "L1: ListStart\n", + "L1: ListItem\nl1", + "L1: ListItem\nl2", + "L1: ListEnd\n", + "L0: Paragraph\nthe time" + ]) + + str = %{\ + now is + * l1 + l1+ + * l2 + the time} + line_groups(str, + [ "L0: Paragraph\nnow is", + "L1: ListStart\n", + "L1: ListItem\nl1 l1+", + "L1: ListItem\nl2", + "L1: ListEnd\n", + "L0: Paragraph\nthe time" + ]) + + str = %{\ + now is + * l1 + * l1.1 + * l2 + the time} + line_groups(str, + [ "L0: Paragraph\nnow is", + "L1: ListStart\n", + "L1: ListItem\nl1", + "L2: ListStart\n", + "L2: ListItem\nl1.1", + "L2: ListEnd\n", + "L1: ListItem\nl2", + "L1: ListEnd\n", + "L0: Paragraph\nthe time" + ]) + + + str = %{\ + now is + * l1 + * l1.1 + text + code + code + + text + * l2 + the time} + line_groups(str, + [ "L0: Paragraph\nnow is", + "L1: ListStart\n", + "L1: ListItem\nl1", + "L2: ListStart\n", + "L2: ListItem\nl1.1 text", + "L2: Verbatim\n code\n code\n", + "L2: Paragraph\ntext", + "L2: ListEnd\n", + "L1: ListItem\nl2", + "L1: ListEnd\n", + "L0: Paragraph\nthe time" + ]) + + + str = %{\ + now is + 1. l1 + * l1.1 + 2. l2 + the time} + line_groups(str, + [ "L0: Paragraph\nnow is", + "L1: ListStart\n", + "L1: ListItem\nl1", + "L2: ListStart\n", + "L2: ListItem\nl1.1", + "L2: ListEnd\n", + "L1: ListItem\nl2", + "L1: ListEnd\n", + "L0: Paragraph\nthe time" + ]) + + str = %{\ + now is + [cat] l1 + * l1.1 + [dog] l2 + the time} + line_groups(str, + [ "L0: Paragraph\nnow is", + "L1: ListStart\n", + "L1: ListItem\nl1", + "L2: ListStart\n", + "L2: ListItem\nl1.1", + "L2: ListEnd\n", + "L1: ListItem\nl2", + "L1: ListEnd\n", + "L0: Paragraph\nthe time" + ]) + + str = %{\ + now is + [cat] l1 + continuation + [dog] l2 + the time} + line_groups(str, + [ "L0: Paragraph\nnow is", + "L1: ListStart\n", + "L1: ListItem\nl1 continuation", + "L1: ListItem\nl2", + "L1: ListEnd\n", + "L0: Paragraph\nthe time" + ]) + + + end + + def test_verbatim_merge + str = %{\ + now is + code + the time} + + line_groups(str, + [ "L0: Paragraph\nnow is", + "L0: Verbatim\n code\n", + "L0: Paragraph\nthe time" + ]) + + + str = %{\ + now is + code + code1 + the time} + + line_groups(str, + [ "L0: Paragraph\nnow is", + "L0: Verbatim\n code\n code1\n", + "L0: Paragraph\nthe time" + ]) + + + str = %{\ + now is + code + + code1 + the time} + + line_groups(str, + [ "L0: Paragraph\nnow is", + "L0: Verbatim\n code\n\n code1\n", + "L0: Paragraph\nthe time" + ]) + + + str = %{\ + now is + code + + code1 + + the time} + + line_groups(str, + [ "L0: Paragraph\nnow is", + "L0: Verbatim\n code\n\n code1\n", + "L0: Paragraph\nthe time" + ]) + + + str = %{\ + now is + code + + code1 + + code2 + the time} + + line_groups(str, + [ "L0: Paragraph\nnow is", + "L0: Verbatim\n code\n\n code1\n\n code2\n", + "L0: Paragraph\nthe time" + ]) + + + # Folds multiple blank lines + str = %{\ + now is + code + + + code1 + + the time} + + line_groups(str, + [ "L0: Paragraph\nnow is", + "L0: Verbatim\n code\n\n code1\n", + "L0: Paragraph\nthe time" + ]) + + + end + + def test_list_split + str = %{\ + now is + * l1 + 1. n1 + 2. n2 + * l2 + the time} + line_groups(str, + [ "L0: Paragraph\nnow is", + "L1: ListStart\n", + "L1: ListItem\nl1", + "L1: ListEnd\n", + "L1: ListStart\n", + "L1: ListItem\nn1", + "L1: ListItem\nn2", + "L1: ListEnd\n", + "L1: ListStart\n", + "L1: ListItem\nl2", + "L1: ListEnd\n", + "L0: Paragraph\nthe time" + ]) + + end + + + def test_headings + str = "= heading one" + line_groups(str, + [ "L0: Heading\nheading one" + ]) + + str = "=== heading three" + line_groups(str, + [ "L0: Heading\nheading three" + ]) + + str = "text\n === heading three" + line_groups(str, + [ "L0: Paragraph\ntext", + "L0: Verbatim\n === heading three\n" + ]) + + str = "text\n code\n === heading three" + line_groups(str, + [ "L0: Paragraph\ntext", + "L0: Verbatim\n code\n === heading three\n" + ]) + + str = "text\n code\n=== heading three" + line_groups(str, + [ "L0: Paragraph\ntext", + "L0: Verbatim\n code\n", + "L0: Heading\nheading three" + ]) + + end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/options.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/options.rb new file mode 100644 index 0000000000..bea7e6bdcd --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/options.rb @@ -0,0 +1,586 @@ +# We handle the parsing of options, and subsequently as a singleton +# object to be queried for option values + +require "rdoc/ri/ri_paths" + +class Options + + require 'singleton' + require 'getoptlong' + + include Singleton + + # files matching this pattern will be excluded + attr_accessor :exclude + + # the name of the output directory + attr_accessor :op_dir + + # the name to use for the output + attr_reader :op_name + + # include private and protected methods in the + # output + attr_accessor :show_all + + # name of the file, class or module to display in + # the initial index page (if not specified + # the first file we encounter is used) + attr_accessor :main_page + + # merge into classes of the name name when generating ri + attr_reader :merge + + # Don't display progress as we process the files + attr_reader :quiet + + # description of the output generator (set with the -fmt + # option + attr_accessor :generator + + # and the list of files to be processed + attr_reader :files + + # array of directories to search for files to satisfy an :include: + attr_reader :rdoc_include + + # title to be used out the output + #attr_writer :title + + # template to be used when generating output + attr_reader :template + + # should diagrams be drawn + attr_reader :diagram + + # should we draw fileboxes in diagrams + attr_reader :fileboxes + + # include the '#' at the front of hyperlinked instance method names + attr_reader :show_hash + + # image format for diagrams + attr_reader :image_format + + # character-set + attr_reader :charset + + # should source code be included inline, or displayed in a popup + attr_reader :inline_source + + # should the output be placed into a single file + attr_reader :all_one_file + + # the number of columns in a tab + attr_reader :tab_width + + # include line numbers in the source listings + attr_reader :include_line_numbers + + # pattern for additional attr_... style methods + attr_reader :extra_accessors + attr_reader :extra_accessor_flags + + # URL of stylesheet + attr_reader :css + + # URL of web cvs frontend + attr_reader :webcvs + + # Are we promiscuous about showing module contents across + # multiple files + attr_reader :promiscuous + + # scan newer sources than the flag file if true. + attr_reader :force_update + + module OptionList + + OPTION_LIST = [ + [ "--accessor", "-A", "accessorname[,..]", + "comma separated list of additional class methods\n" + + "that should be treated like 'attr_reader' and\n" + + "friends. Option may be repeated. Each accessorname\n" + + "may have '=text' appended, in which case that text\n" + + "appears where the r/w/rw appears for normal accessors."], + + [ "--all", "-a", nil, + "include all methods (not just public)\nin the output" ], + + [ "--charset", "-c", "charset", + "specifies HTML character-set" ], + + [ "--debug", "-D", nil, + "displays lots on internal stuff" ], + + [ "--diagram", "-d", nil, + "Generate diagrams showing modules and classes.\n" + + "You need dot V1.8.6 or later to use the --diagram\n" + + "option correctly. Dot is available from\n"+ + "http://www.research.att.com/sw/tools/graphviz/" ], + + [ "--exclude", "-x", "pattern", + "do not process files or directories matching\n" + + "pattern. Files given explicitly on the command\n" + + "line will never be excluded." ], + + [ "--extension", "-E", "new=old", + "Treat files ending with .new as if they ended with\n" + + ".old. Using '-E cgi=rb' will cause xxx.cgi to be\n" + + "parsed as a Ruby file"], + + [ "--fileboxes", "-F", nil, + "classes are put in boxes which represents\n" + + "files, where these classes reside. Classes\n" + + "shared between more than one file are\n" + + "shown with list of files that sharing them.\n" + + "Silently discarded if --diagram is not given\n" + + "Experimental." ], + + [ "--force-update", "-U", nil, + "forces to scan all sources even if newer than\n" + + "the flag file." ], + + [ "--fmt", "-f", "format name", + "set the output formatter (see below)" ], + + [ "--help", "-h", nil, + "you're looking at it" ], + + [ "--help-output", "-O", nil, + "explain the various output options" ], + + [ "--image-format", "-I", "gif/png/jpg/jpeg", + "Sets output image format for diagrams. Can\n" + + "be png, gif, jpeg, jpg. If this option is\n" + + "omitted, png is used. Requires --diagram." ], + + [ "--include", "-i", "dir[,dir...]", + "set (or add to) the list of directories\n" + + "to be searched when satisfying :include:\n" + + "requests. Can be used more than once." ], + + [ "--inline-source", "-S", nil, + "Show method source code inline, rather\n" + + "than via a popup link" ], + + [ "--line-numbers", "-N", nil, + "Include line numbers in the source code" ], + + [ "--main", "-m", "name", + "'name' will be the initial page displayed" ], + + [ "--merge", "-M", nil, + "when creating ri output, merge processed classes\n" + + "into previously documented classes of the name name"], + + [ "--one-file", "-1", nil, + "put all the output into a single file" ], + + [ "--op", "-o", "dir", + "set the output directory" ], + + [ "--opname", "-n", "name", + "Set the 'name' of the output. Has no\n" + + "effect for HTML." ], + + [ "--promiscuous", "-p", nil, + "When documenting a file that contains a module\n" + + "or class also defined in other files, show\n" + + "all stuff for that module/class in each files\n" + + "page. By default, only show stuff defined in\n" + + "that particular file." ], + + [ "--quiet", "-q", nil, + "don't show progress as we parse" ], + + [ "--ri", "-r", nil, + "generate output for use by 'ri.' The files are\n" + + "stored in the '.rdoc' directory under your home\n"+ + "directory unless overridden by a subsequent\n" + + "--op parameter, so no special privileges are needed." ], + + [ "--ri-site", "-R", nil, + "generate output for use by 'ri.' The files are\n" + + "stored in a site-wide directory, making them accessible\n"+ + "to others, so special privileges are needed." ], + + [ "--ri-system", "-Y", nil, + "generate output for use by 'ri.' The files are\n" + + "stored in a system-level directory, making them accessible\n"+ + "to others, so special privileges are needed. This option\n"+ + "is intended to be used during Ruby installations" ], + + [ "--show-hash", "-H", nil, + "A name of the form #name in a comment\n" + + "is a possible hyperlink to an instance\n" + + "method name. When displayed, the '#' is\n" + + "removed unless this option is specified" ], + + [ "--style", "-s", "stylesheet url", + "specifies the URL of a separate stylesheet." ], + + [ "--tab-width", "-w", "n", + "Set the width of tab characters (default 8)"], + + [ "--template", "-T", "template name", + "Set the template used when generating output" ], + + [ "--title", "-t", "text", + "Set 'txt' as the title for the output" ], + + [ "--version", "-v", nil, + "display RDoc's version" ], + + [ "--webcvs", "-W", "url", + "Specify a URL for linking to a web frontend\n" + + "to CVS. If the URL contains a '\%s', the\n" + + "name of the current file will be substituted;\n" + + "if the URL doesn't contain a '\%s', the\n" + + "filename will be appended to it." ], + ] + + def OptionList.options + OPTION_LIST.map do |long, short, arg,| + [ long, + short, + arg ? GetoptLong::REQUIRED_ARGUMENT : GetoptLong::NO_ARGUMENT + ] + end + end + + + def OptionList.strip_output(text) + text =~ /^\s+/ + leading_spaces = $& + text.gsub!(/^#{leading_spaces}/, '') + $stdout.puts text + end + + + # Show an error and exit + + def OptionList.error(msg) + $stderr.puts + $stderr.puts msg + $stderr.puts "\nFor help on options, try 'rdoc --help'\n\n" + exit 1 + end + + # Show usage and exit + + def OptionList.usage(generator_names) + + puts + puts(VERSION_STRING) + puts + + name = File.basename($0) + OptionList.strip_output(<<-EOT) + Usage: + + #{name} [options] [names...] + + Files are parsed, and the information they contain + collected, before any output is produced. This allows cross + references between all files to be resolved. If a name is a + directory, it is traversed. If no names are specified, all + Ruby files in the current directory (and subdirectories) are + processed. + + Options: + + EOT + + OPTION_LIST.each do |long, short, arg, desc| + opt = sprintf("%20s", "#{long}, #{short}") + oparg = sprintf("%-7s", arg) + print "#{opt} #{oparg}" + desc = desc.split("\n") + if arg.nil? || arg.length < 7 + puts desc.shift + else + puts + end + desc.each do |line| + puts(" "*28 + line) + end + puts + end + + puts "\nAvailable output formatters: " + + generator_names.sort.join(', ') + "\n\n" + + puts "For information on where the output goes, use\n\n" + puts " rdoc --help-output\n\n" + + exit 0 + end + + def OptionList.help_output + OptionList.strip_output(<<-EOT) + How RDoc generates output depends on the output formatter being + used, and on the options you give. + + - HTML output is normally produced into a number of separate files + (one per class, module, and file, along with various indices). + These files will appear in the directory given by the --op + option (doc/ by default). + + - XML output by default is written to standard output. If a + --opname option is given, the output will instead be written + to a file with that name in the output directory. + + - .chm files (Windows help files) are written in the --op directory. + If an --opname parameter is present, that name is used, otherwise + the file will be called rdoc.chm. + + For information on other RDoc options, use "rdoc --help". + EOT + exit 0 + end + end + + # Parse command line options. We're passed a hash containing + # output generators, keyed by the generator name + + def parse(argv, generators) + old_argv = ARGV.dup + begin + ARGV.replace(argv) + @op_dir = "doc" + @op_name = nil + @show_all = false + @main_page = nil + @marge = false + @exclude = [] + @quiet = false + @generator_name = 'html' + @generator = generators[@generator_name] + @rdoc_include = [] + @title = nil + @template = nil + @diagram = false + @fileboxes = false + @show_hash = false + @image_format = 'png' + @inline_source = false + @all_one_file = false + @tab_width = 8 + @include_line_numbers = false + @extra_accessor_flags = {} + @promiscuous = false + @force_update = false + + @css = nil + @webcvs = nil + + @charset = case $KCODE + when /^S/i + 'Shift_JIS' + when /^E/i + 'EUC-JP' + else + 'iso-8859-1' + end + + accessors = [] + + go = GetoptLong.new(*OptionList.options) + go.quiet = true + + go.each do |opt, arg| + case opt + when "--all" then @show_all = true + when "--charset" then @charset = arg + when "--debug" then $DEBUG = true + when "--exclude" then @exclude << Regexp.new(arg) + when "--inline-source" then @inline_source = true + when "--line-numbers" then @include_line_numbers = true + when "--main" then @main_page = arg + when "--merge" then @merge = true + when "--one-file" then @all_one_file = @inline_source = true + when "--op" then @op_dir = arg + when "--opname" then @op_name = arg + when "--promiscuous" then @promiscuous = true + when "--quiet" then @quiet = true + when "--show-hash" then @show_hash = true + when "--style" then @css = arg + when "--template" then @template = arg + when "--title" then @title = arg + when "--webcvs" then @webcvs = arg + + when "--accessor" + arg.split(/,/).each do |accessor| + if accessor =~ /^(\w+)(=(.*))?$/ + accessors << $1 + @extra_accessor_flags[$1] = $3 + end + end + + when "--diagram" + check_diagram + @diagram = true + + when "--fileboxes" + @fileboxes = true if @diagram + + when "--fmt" + @generator_name = arg.downcase + setup_generator(generators) + + when "--help" + OptionList.usage(generators.keys) + + when "--help-output" + OptionList.help_output + + when "--image-format" + if ['gif', 'png', 'jpeg', 'jpg'].include?(arg) + @image_format = arg + else + raise GetoptLong::InvalidOption.new("unknown image format: #{arg}") + end + + when "--include" + @rdoc_include.concat arg.split(/\s*,\s*/) + + when "--ri", "--ri-site", "--ri-system" + @generator_name = "ri" + @op_dir = case opt + when "--ri" then RI::Paths::HOMEDIR + when "--ri-site" then RI::Paths::SITEDIR + when "--ri-system" then RI::Paths::SYSDIR + else fail opt + end + setup_generator(generators) + + when "--tab-width" + begin + @tab_width = Integer(arg) + rescue + $stderr.puts "Invalid tab width: '#{arg}'" + exit 1 + end + + when "--extension" + new, old = arg.split(/=/, 2) + OptionList.error("Invalid parameter to '-E'") unless new && old + unless RDoc::ParserFactory.alias_extension(old, new) + OptionList.error("Unknown extension .#{old} to -E") + end + + when "--force-update" + @force_update = true + + when "--version" + puts VERSION_STRING + exit + end + + end + + @files = ARGV.dup + + @rdoc_include << "." if @rdoc_include.empty? + + if @exclude.empty? + @exclude = nil + else + @exclude = Regexp.new(@exclude.join("|")) + end + + check_files + + # If no template was specified, use the default + # template for the output formatter + + @template ||= @generator_name + + # Generate a regexp from the accessors + unless accessors.empty? + re = '^(' + accessors.map{|a| Regexp.quote(a)}.join('|') + ')$' + @extra_accessors = Regexp.new(re) + end + + rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument => error + OptionList.error(error.message) + + ensure + ARGV.replace(old_argv) + end + end + + + def title + @title ||= "RDoc Documentation" + end + + # Set the title, but only if not already set. This means that a title set from + # the command line trumps one set in a source file + + def title=(string) + @title ||= string + end + + + private + + # Set up an output generator for the format in @generator_name + def setup_generator(generators) + @generator = generators[@generator_name] + if !@generator + OptionList.error("Invalid output formatter") + end + + if @generator_name == "xml" + @all_one_file = true + @inline_source = true + end + end + + # Check that the right version of 'dot' is available. + # Unfortuately this doesn't work correctly under Windows NT, + # so we'll bypass the test under Windows + + def check_diagram + return if RUBY_PLATFORM =~ /win/ + + ok = false + ver = nil + IO.popen("dot -V 2>&1") do |io| + ver = io.read + if ver =~ /dot.+version(?:\s+gviz)?\s+(\d+)\.(\d+)/ + ok = ($1.to_i > 1) || ($1.to_i == 1 && $2.to_i >= 8) + end + end + unless ok + if ver =~ /^dot.+version/ + $stderr.puts "Warning: You may need dot V1.8.6 or later to use\n", + "the --diagram option correctly. You have:\n\n ", + ver, + "\nDiagrams might have strange background colors.\n\n" + else + $stderr.puts "You need the 'dot' program to produce diagrams.", + "(see http://www.research.att.com/sw/tools/graphviz/)\n\n" + exit + end +# exit + end + end + + # Check that the files on the command line exist + + def check_files + @files.each do |f| + stat = File.stat f rescue error("File not found: #{f}") + error("File '#{f}' not readable") unless stat.readable? + end + end + + def error(str) + $stderr.puts str + exit(1) + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parse_c.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parse_c.rb new file mode 100644 index 0000000000..25fc66af3f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parse_c.rb @@ -0,0 +1,773 @@ +# Classes and modules built in to the interpreter. We need +# these to define superclasses of user objects + +require "rdoc/code_objects" +require "rdoc/parsers/parserfactory" +require "rdoc/options" +require "rdoc/rdoc" + +module RDoc + + ## + # Ruby's built-in classes. + + KNOWN_CLASSES = { + "rb_cObject" => "Object", + "rb_cArray" => "Array", + "rb_cBignum" => "Bignum", + "rb_cClass" => "Class", + "rb_cDir" => "Dir", + "rb_cData" => "Data", + "rb_cFalseClass" => "FalseClass", + "rb_cFile" => "File", + "rb_cFixnum" => "Fixnum", + "rb_cFloat" => "Float", + "rb_cHash" => "Hash", + "rb_cInteger" => "Integer", + "rb_cIO" => "IO", + "rb_cModule" => "Module", + "rb_cNilClass" => "NilClass", + "rb_cNumeric" => "Numeric", + "rb_cProc" => "Proc", + "rb_cRange" => "Range", + "rb_cRegexp" => "Regexp", + "rb_cString" => "String", + "rb_cSymbol" => "Symbol", + "rb_cThread" => "Thread", + "rb_cTime" => "Time", + "rb_cTrueClass" => "TrueClass", + "rb_cStruct" => "Struct", + "rb_eException" => "Exception", + "rb_eStandardError" => "StandardError", + "rb_eSystemExit" => "SystemExit", + "rb_eInterrupt" => "Interrupt", + "rb_eSignal" => "Signal", + "rb_eFatal" => "Fatal", + "rb_eArgError" => "ArgError", + "rb_eEOFError" => "EOFError", + "rb_eIndexError" => "IndexError", + "rb_eRangeError" => "RangeError", + "rb_eIOError" => "IOError", + "rb_eRuntimeError" => "RuntimeError", + "rb_eSecurityError" => "SecurityError", + "rb_eSystemCallError" => "SystemCallError", + "rb_eTypeError" => "TypeError", + "rb_eZeroDivError" => "ZeroDivError", + "rb_eNotImpError" => "NotImpError", + "rb_eNoMemError" => "NoMemError", + "rb_eFloatDomainError" => "FloatDomainError", + "rb_eScriptError" => "ScriptError", + "rb_eNameError" => "NameError", + "rb_eSyntaxError" => "SyntaxError", + "rb_eLoadError" => "LoadError", + + "rb_mKernel" => "Kernel", + "rb_mComparable" => "Comparable", + "rb_mEnumerable" => "Enumerable", + "rb_mPrecision" => "Precision", + "rb_mErrno" => "Errno", + "rb_mFileTest" => "FileTest", + "rb_mGC" => "GC", + "rb_mMath" => "Math", + "rb_mProcess" => "Process" + } + + ## + # We attempt to parse C extension files. Basically we look for + # the standard patterns that you find in extensions: rb_define_class, + # rb_define_method and so on. We also try to find the corresponding + # C source for the methods and extract comments, but if we fail + # we don't worry too much. + # + # The comments associated with a Ruby method are extracted from the C + # comment block associated with the routine that _implements_ that + # method, that is to say the method whose name is given in the + # rb_define_method call. For example, you might write: + # + # /* + # * Returns a new array that is a one-dimensional flattening of this + # * array (recursively). That is, for every element that is an array, + # * extract its elements into the new array. + # * + # * s = [ 1, 2, 3 ] #=> [1, 2, 3] + # * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]] + # * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10] + # * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + # */ + # static VALUE + # rb_ary_flatten(ary) + # VALUE ary; + # { + # ary = rb_obj_dup(ary); + # rb_ary_flatten_bang(ary); + # return ary; + # } + # + # ... + # + # void + # Init_Array() + # { + # ... + # rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0); + # + # Here RDoc will determine from the rb_define_method line that there's a + # method called "flatten" in class Array, and will look for the implementation + # in the method rb_ary_flatten. It will then use the comment from that + # method in the HTML output. This method must be in the same source file + # as the rb_define_method. + # + # C classes can be diagramed (see /tc/dl/ruby/ruby/error.c), and RDoc + # integrates C and Ruby source into one tree + # + # The comment blocks may include special direcives: + # + # [Document-class: name] + # This comment block is documentation for the given class. Use this + # when the Init_xxx method is not named after the class. + # + # [Document-method: name] + # This comment documents the named method. Use when RDoc cannot + # automatically find the method from it's declaration + # + # [call-seq: text up to an empty line] + # Because C source doesn't give descripive names to Ruby-level parameters, + # you need to document the calling sequence explicitly + # + # In additon, RDoc assumes by default that the C method implementing a + # Ruby function is in the same source file as the rb_define_method call. + # If this isn't the case, add the comment + # + # rb_define_method(....); // in: filename + # + # As an example, we might have an extension that defines multiple classes + # in its Init_xxx method. We could document them using + # + # + # /* + # * Document-class: MyClass + # * + # * Encapsulate the writing and reading of the configuration + # * file. ... + # */ + # + # /* + # * Document-method: read_value + # * + # * call-seq: + # * cfg.read_value(key) -> value + # * cfg.read_value(key} { |key| } -> value + # * + # * Return the value corresponding to +key+ from the configuration. + # * In the second form, if the key isn't found, invoke the + # * block and return its value. + # */ + # + + class C_Parser + + attr_accessor :progress + + extend ParserFactory + parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/) + + @@known_bodies = {} + + # prepare to parse a C file + def initialize(top_level, file_name, body, options, stats) + @known_classes = KNOWN_CLASSES.dup + @body = handle_tab_width(handle_ifdefs_in(body)) + @options = options + @stats = stats + @top_level = top_level + @classes = Hash.new + @file_dir = File.dirname(file_name) + @progress = $stderr unless options.quiet + end + + # Extract the classes/modules and methods from a C file + # and return the corresponding top-level object + def scan + remove_commented_out_lines + do_classes + do_constants + do_methods + do_includes + do_aliases + @top_level + end + + ####### + private + ####### + + def progress(char) + unless @options.quiet + @progress.print(char) + @progress.flush + end + end + + def warn(msg) + $stderr.puts + $stderr.puts msg + $stderr.flush + end + + def remove_private_comments(comment) + comment.gsub!(/\/?\*--(.*?)\/?\*\+\+/m, '') + comment.sub!(/\/?\*--.*/m, '') + end + + ## + # removes lines that are commented out that might otherwise get picked up + # when scanning for classes and methods + + def remove_commented_out_lines + @body.gsub!(%r{//.*rb_define_}, '//') + end + + def handle_class_module(var_name, class_mod, class_name, parent, in_module) + progress(class_mod[0, 1]) + + parent_name = @known_classes[parent] || parent + + if in_module + enclosure = @classes[in_module] + unless enclosure + if enclosure = @known_classes[in_module] + handle_class_module(in_module, (/^rb_m/ =~ in_module ? "module" : "class"), + enclosure, nil, nil) + enclosure = @classes[in_module] + end + end + unless enclosure + warn("Enclosing class/module '#{in_module}' for " + + "#{class_mod} #{class_name} not known") + return + end + else + enclosure = @top_level + end + + if class_mod == "class" + cm = enclosure.add_class(NormalClass, class_name, parent_name) + @stats.num_classes += 1 + else + cm = enclosure.add_module(NormalModule, class_name) + @stats.num_modules += 1 + end + cm.record_location(enclosure.toplevel) + + find_class_comment(cm.full_name, cm) + @classes[var_name] = cm + @known_classes[var_name] = cm.full_name + end + + ## + # Look for class or module documentation above Init_+class_name+(void), + # in a Document-class +class_name+ (or module) comment or above an + # rb_define_class (or module). If a comment is supplied above a matching + # Init_ and a rb_define_class the Init_ comment is used. + # + # /* + # * This is a comment for Foo + # */ + # Init_Foo(void) { + # VALUE cFoo = rb_define_class("Foo", rb_cObject); + # } + # + # /* + # * Document-class: Foo + # * This is a comment for Foo + # */ + # Init_foo(void) { + # VALUE cFoo = rb_define_class("Foo", rb_cObject); + # } + # + # /* + # * This is a comment for Foo + # */ + # VALUE cFoo = rb_define_class("Foo", rb_cObject); + + def find_class_comment(class_name, class_meth) + comment = nil + if @body =~ %r{((?>/\*.*?\*/\s+)) + (static\s+)?void\s+Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)?\)}xmi + comment = $1 + elsif @body =~ %r{Document-(class|module):\s#{class_name}\s*?\n((?>.*?\*/))}m + comment = $2 + else + if @body =~ /rb_define_(class|module)/m then + class_name = class_name.split("::").last + comments = [] + @body.split(/(\/\*.*?\*\/)\s*?\n/m).each_with_index do |chunk, index| + comments[index] = chunk + if chunk =~ /rb_define_(class|module).*?"(#{class_name})"/m then + comment = comments[index-1] + break + end + end + end + end + class_meth.comment = mangle_comment(comment) if comment + end + + ############################################################ + + def do_classes + @body.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do + |var_name, class_name| + handle_class_module(var_name, "module", class_name, nil, nil) + end + + # The '.' lets us handle SWIG-generated files + @body.scan(/([\w\.]+)\s* = \s*rb_define_class\s* + \( + \s*"(\w+)", + \s*(\w+)\s* + \)/mx) do + + |var_name, class_name, parent| + handle_class_module(var_name, "class", class_name, parent, nil) + end + + @body.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do + |var_name, class_name, parent| + parent = nil if parent == "0" + handle_class_module(var_name, "class", class_name, parent, nil) + end + + @body.scan(/(\w+)\s* = \s*rb_define_module_under\s* + \( + \s*(\w+), + \s*"(\w+)" + \s*\)/mx) do + + |var_name, in_module, class_name| + handle_class_module(var_name, "module", class_name, nil, in_module) + end + + @body.scan(/([\w\.]+)\s* = \s*rb_define_class_under\s* + \( + \s*(\w+), + \s*"(\w+)", + \s*(\w+)\s* + \s*\)/mx) do + + |var_name, in_module, class_name, parent| + handle_class_module(var_name, "class", class_name, parent, in_module) + end + + end + + ########################################################### + + def do_constants + @body.scan(%r{\Wrb_define_ + ( + variable | + readonly_variable | + const | + global_const | + ) + \s*\( + (?:\s*(\w+),)? + \s*"(\w+)", + \s*(.*?)\s*\)\s*; + }xm) do + + |type, var_name, const_name, definition| + var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel" + handle_constants(type, var_name, const_name, definition) + end + end + + ############################################################ + + def do_methods + + @body.scan(%r{rb_define_ + ( + singleton_method | + method | + module_function | + private_method + ) + \s*\(\s*([\w\.]+), + \s*"([^"]+)", + \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?, + \s*(-?\w+)\s*\) + (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))? + }xm) do + |type, var_name, meth_name, meth_body, param_count, source_file| + #" + + # Ignore top-object and weird struct.c dynamic stuff + next if var_name == "ruby_top_self" + next if var_name == "nstr" + next if var_name == "envtbl" + next if var_name == "argf" # it'd be nice to handle this one + + var_name = "rb_cObject" if var_name == "rb_mKernel" + handle_method(type, var_name, meth_name, + meth_body, param_count, source_file) + end + + @body.scan(%r{rb_define_attr\( + \s*([\w\.]+), + \s*"([^"]+)", + \s*(\d+), + \s*(\d+)\s*\); + }xm) do #" + |var_name, attr_name, attr_reader, attr_writer| + + #var_name = "rb_cObject" if var_name == "rb_mKernel" + handle_attr(var_name, attr_name, + attr_reader.to_i != 0, + attr_writer.to_i != 0) + end + + @body.scan(%r{rb_define_global_function\s*\( + \s*"([^"]+)", + \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?, + \s*(-?\w+)\s*\) + (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))? + }xm) do #" + |meth_name, meth_body, param_count, source_file| + handle_method("method", "rb_mKernel", meth_name, + meth_body, param_count, source_file) + end + + @body.scan(/define_filetest_function\s*\( + \s*"([^"]+)", + \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?, + \s*(-?\w+)\s*\)/xm) do #" + |meth_name, meth_body, param_count| + + handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count) + handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count) + end + end + + ############################################################ + + def do_aliases + @body.scan(%r{rb_define_alias\s*\(\s*(\w+),\s*"([^"]+)",\s*"([^"]+)"\s*\)}m) do + |var_name, new_name, old_name| + @stats.num_methods += 1 + class_name = @known_classes[var_name] || var_name + class_obj = find_class(var_name, class_name) + + class_obj.add_alias(Alias.new("", old_name, new_name, "")) + end + end + + ## + # Adds constant comments. By providing some_value: at the start ofthe + # comment you can override the C value of the comment to give a friendly + # definition. + # + # /* 300: The perfect score in bowling */ + # rb_define_const(cFoo, "PERFECT", INT2FIX(300); + # + # Will override +INT2FIX(300)+ with the value +300+ in the output RDoc. + # Values may include quotes and escaped colons (\:). + + def handle_constants(type, var_name, const_name, definition) + #@stats.num_constants += 1 + class_name = @known_classes[var_name] + + return unless class_name + + class_obj = find_class(var_name, class_name) + + unless class_obj + warn("Enclosing class/module '#{const_name}' for not known") + return + end + + comment = find_const_comment(type, const_name) + + # In the case of rb_define_const, the definition and comment are in + # "/* definition: comment */" form. The literal ':' and '\' characters + # can be escaped with a backslash. + if type.downcase == 'const' then + elements = mangle_comment(comment).split(':') + if elements.nil? or elements.empty? then + con = Constant.new(const_name, definition, mangle_comment(comment)) + else + new_definition = elements[0..-2].join(':') + if new_definition.empty? then # Default to literal C definition + new_definition = definition + else + new_definition.gsub!("\:", ":") + new_definition.gsub!("\\", '\\') + end + new_definition.sub!(/\A(\s+)/, '') + new_comment = $1.nil? ? elements.last : "#{$1}#{elements.last.lstrip}" + con = Constant.new(const_name, new_definition, + mangle_comment(new_comment)) + end + else + con = Constant.new(const_name, definition, mangle_comment(comment)) + end + + class_obj.add_constant(con) + end + + ## + # Finds a comment matching +type+ and +const_name+ either above the + # comment or in the matching Document- section. + + def find_const_comment(type, const_name) + if @body =~ %r{((?>^\s*/\*.*?\*/\s+)) + rb_define_#{type}\((?:\s*(\w+),)?\s*"#{const_name}"\s*,.*?\)\s*;}xmi + $1 + elsif @body =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m + $1 + else + '' + end + end + + ########################################################### + + def handle_attr(var_name, attr_name, reader, writer) + rw = '' + if reader + #@stats.num_methods += 1 + rw << 'R' + end + if writer + #@stats.num_methods += 1 + rw << 'W' + end + + class_name = @known_classes[var_name] + + return unless class_name + + class_obj = find_class(var_name, class_name) + + if class_obj + comment = find_attr_comment(attr_name) + unless comment.empty? + comment = mangle_comment(comment) + end + att = Attr.new('', attr_name, rw, comment) + class_obj.add_attribute(att) + end + + end + + ########################################################### + + def find_attr_comment(attr_name) + if @body =~ %r{((?>/\*.*?\*/\s+)) + rb_define_attr\((?:\s*(\w+),)?\s*"#{attr_name}"\s*,.*?\)\s*;}xmi + $1 + elsif @body =~ %r{Document-attr:\s#{attr_name}\s*?\n((?>.*?\*/))}m + $1 + else + '' + end + end + + ########################################################### + + def handle_method(type, var_name, meth_name, + meth_body, param_count, source_file = nil) + progress(".") + + @stats.num_methods += 1 + class_name = @known_classes[var_name] + + return unless class_name + + class_obj = find_class(var_name, class_name) + + if class_obj + if meth_name == "initialize" + meth_name = "new" + type = "singleton_method" + end + meth_obj = AnyMethod.new("", meth_name) + meth_obj.singleton = + %w{singleton_method module_function}.include?(type) + + p_count = (Integer(param_count) rescue -1) + + if p_count < 0 + meth_obj.params = "(...)" + elsif p_count == 0 + meth_obj.params = "()" + else + meth_obj.params = "(" + + (1..p_count).map{|i| "p#{i}"}.join(", ") + + ")" + end + + if source_file + file_name = File.join(@file_dir, source_file) + body = (@@known_bodies[source_file] ||= File.read(file_name)) + else + body = @body + end + if find_body(meth_body, meth_obj, body) and meth_obj.document_self + class_obj.add_method(meth_obj) + end + end + end + + ############################################################ + + # Find the C code corresponding to a Ruby method + def find_body(meth_name, meth_obj, body, quiet = false) + case body + when %r{((?>/\*.*?\*/\s*))(?:static\s+)?VALUE\s+#{meth_name} + \s*(\(.*?\)).*?^}xm + comment, params = $1, $2 + body_text = $& + + remove_private_comments(comment) if comment + + # see if we can find the whole body + + re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}' + if Regexp.new(re, Regexp::MULTILINE).match(body) + body_text = $& + end + + # The comment block may have been overridden with a + # 'Document-method' block. This happens in the interpreter + # when multiple methods are vectored through to the same + # C method but those methods are logically distinct (for + # example Kernel.hash and Kernel.object_id share the same + # implementation + + override_comment = find_override_comment(meth_obj.name) + comment = override_comment if override_comment + + find_modifiers(comment, meth_obj) if comment + +# meth_obj.params = params + meth_obj.start_collecting_tokens + meth_obj.add_token(RubyToken::Token.new(1,1).set_text(body_text)) + meth_obj.comment = mangle_comment(comment) + when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m + comment = $1 + find_body($2, meth_obj, body, true) + find_modifiers(comment, meth_obj) + meth_obj.comment = mangle_comment(comment) + meth_obj.comment + when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m + unless find_body($1, meth_obj, body, true) + warn "No definition for #{meth_name}" unless quiet + return false + end + else + + # No body, but might still have an override comment + comment = find_override_comment(meth_obj.name) + + if comment + find_modifiers(comment, meth_obj) + meth_obj.comment = mangle_comment(comment) + else + warn "No definition for #{meth_name}" unless quiet + return false + end + end + true + end + + + ## + # If the comment block contains a section that looks like: + # + # call-seq: + # Array.new + # Array.new(10) + # + # use it for the parameters. + + def find_modifiers(comment, meth_obj) + if comment.sub!(/:nodoc:\s*^\s*\*?\s*$/m, '') or + comment.sub!(/\A\/\*\s*:nodoc:\s*\*\/\Z/, '') + meth_obj.document_self = false + end + if comment.sub!(/call-seq:(.*?)^\s*\*?\s*$/m, '') or + comment.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '') + seq = $1 + seq.gsub!(/^\s*\*\s*/, '') + meth_obj.call_seq = seq + end + end + + ############################################################ + + def find_override_comment(meth_name) + name = Regexp.escape(meth_name) + if @body =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m + $1 + end + end + + ## + # Look for includes of the form: + # + # rb_include_module(rb_cArray, rb_mEnumerable); + + def do_includes + @body.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m| + if cls = @classes[c] + m = @known_classes[m] || m + cls.add_include(Include.new(m, "")) + end + end + end + + ## + # Remove the /*'s and leading asterisks from C comments + + def mangle_comment(comment) + comment.sub!(%r{/\*+}) { " " * $&.length } + comment.sub!(%r{\*+/}) { " " * $&.length } + comment.gsub!(/^[ \t]*\*/m) { " " * $&.length } + comment + end + + def find_class(raw_name, name) + unless @classes[raw_name] + if raw_name =~ /^rb_m/ + @classes[raw_name] = @top_level.add_module(NormalModule, name) + else + @classes[raw_name] = @top_level.add_class(NormalClass, name, nil) + end + end + @classes[raw_name] + end + + def handle_tab_width(body) + if /\t/ =~ body + tab_width = Options.instance.tab_width + body.split(/\n/).map do |line| + 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #` + line + end .join("\n") + else + body + end + end + + ## + # Removes #ifdefs that would otherwise confuse us + + def handle_ifdefs_in(body) + body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m) { $1 } + end + + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parse_f95.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parse_f95.rb new file mode 100644 index 0000000000..f3f6d76103 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parse_f95.rb @@ -0,0 +1,1841 @@ +#= parse_f95.rb - Fortran95 Parser +# +#== Overview +# +#"parse_f95.rb" parses Fortran95 files with suffixes "f90", "F90", "f95" +#and "F95". Fortran95 files are expected to be conformed to Fortran95 +#standards. +# +#== Rules +# +#Fundamental rules are same as that of the Ruby parser. +#But comment markers are '!' not '#'. +# +#=== Correspondence between RDoc documentation and Fortran95 programs +# +#"parse_f95.rb" parses main programs, modules, subroutines, functions, +#derived-types, public variables, public constants, +#defined operators and defined assignments. +#These components are described in items of RDoc documentation, as follows. +# +#Files :: Files (same as Ruby) +#Classes :: Modules +#Methods :: Subroutines, functions, variables, constants, derived-types, defined operators, defined assignments +#Required files :: Files in which imported modules, external subroutines and external functions are defined. +#Included Modules :: List of imported modules +#Attributes :: List of derived-types, List of imported modules all of whose components are published again +# +#Components listed in 'Methods' (subroutines, functions, ...) +#defined in modules are described in the item of 'Classes'. +#On the other hand, components defined in main programs or +#as external procedures are described in the item of 'Files'. +# +#=== Components parsed by default +# +#By default, documentation on public components (subroutines, functions, +#variables, constants, derived-types, defined operators, +#defined assignments) are generated. +#With "--all" option, documentation on all components +#are generated (almost same as the Ruby parser). +# +#=== Information parsed automatically +# +#The following information is automatically parsed. +# +#* Types of arguments +#* Types of variables and constants +#* Types of variables in the derived types, and initial values +#* NAMELISTs and types of variables in them, and initial values +# +#Aliases by interface statement are described in the item of 'Methods'. +# +#Components which are imported from other modules and published again +#are described in the item of 'Methods'. +# +#=== Format of comment blocks +# +#Comment blocks should be written as follows. +#Comment blocks are considered to be ended when the line without '!' +#appears. +#The indentation is not necessary. +# +# ! (Top of file) +# ! +# ! Comment blocks for the files. +# ! +# !-- +# ! The comment described in the part enclosed by +# ! "!--" and "!++" is ignored. +# !++ +# ! +# module hogehoge +# ! +# ! Comment blocks for the modules (or the programs). +# ! +# +# private +# +# logical :: a ! a private variable +# real, public :: b ! a public variable +# integer, parameter :: c = 0 ! a public constant +# +# public :: c +# public :: MULTI_ARRAY +# public :: hoge, foo +# +# type MULTI_ARRAY +# ! +# ! Comment blocks for the derived-types. +# ! +# real, pointer :: var(:) =>null() ! Comments block for the variables. +# integer :: num = 0 +# end type MULTI_ARRAY +# +# contains +# +# subroutine hoge( in, & ! Comment blocks between continuation lines are ignored. +# & out ) +# ! +# ! Comment blocks for the subroutines or functions +# ! +# character(*),intent(in):: in ! Comment blocks for the arguments. +# character(*),intent(out),allocatable,target :: in +# ! Comment blocks can be +# ! written under Fortran statements. +# +# character(32) :: file ! This comment parsed as a variable in below NAMELIST. +# integer :: id +# +# namelist /varinfo_nml/ file, id +# ! +# ! Comment blocks for the NAMELISTs. +# ! Information about variables are described above. +# ! +# +# .... +# +# end subroutine hoge +# +# integer function foo( in ) +# ! +# ! This part is considered as comment block. +# +# ! Comment blocks under blank lines are ignored. +# ! +# integer, intent(in):: inA ! This part is considered as comment block. +# +# ! This part is ignored. +# +# end function foo +# +# subroutine hide( in, & +# & out ) !:nodoc: +# ! +# ! If "!:nodoc:" is described at end-of-line in subroutine +# ! statement as above, the subroutine is ignored. +# ! This assignment can be used to modules, subroutines, +# ! functions, variables, constants, derived-types, +# ! defined operators, defined assignments, +# ! list of imported modules ("use" statement). +# ! +# +# .... +# +# end subroutine hide +# +# end module hogehoge +# + + +require "rdoc/code_objects" + +module RDoc + + class Token + + NO_TEXT = "??".freeze + + def initialize(line_no, char_no) + @line_no = line_no + @char_no = char_no + @text = NO_TEXT + end + # Because we're used in contexts that expect to return a token, + # we set the text string and then return ourselves + def set_text(text) + @text = text + self + end + + attr_reader :line_no, :char_no, :text + + end + + # See rdoc/parsers/parse_f95.rb + + class Fortran95parser + + extend ParserFactory + parse_files_matching(/\.((f|F)9(0|5)|F)$/) + + @@external_aliases = [] + @@public_methods = [] + + # "false":: Comments are below source code + # "true" :: Comments are upper source code + COMMENTS_ARE_UPPER = false + + # Internal alias message + INTERNAL_ALIAS_MES = "Alias for" + + # External alias message + EXTERNAL_ALIAS_MES = "The entity is" + + # prepare to parse a Fortran 95 file + def initialize(top_level, file_name, body, options, stats) + @body = body + @stats = stats + @file_name = file_name + @options = options + @top_level = top_level + @progress = $stderr unless options.quiet + end + + # devine code constructs + def scan + + # remove private comment + remaining_code = remove_private_comments(@body) + + # continuation lines are united to one line + remaining_code = united_to_one_line(remaining_code) + + # semicolons are replaced to line feed + remaining_code = semicolon_to_linefeed(remaining_code) + + # collect comment for file entity + whole_comment, remaining_code = collect_first_comment(remaining_code) + @top_level.comment = whole_comment + + # String "remaining_code" is converted to Array "remaining_lines" + remaining_lines = remaining_code.split("\n") + + # "module" or "program" parts are parsed (new) + # + level_depth = 0 + block_searching_flag = nil + block_searching_lines = [] + pre_comment = [] + module_program_trailing = "" + module_program_name = "" + other_block_level_depth = 0 + other_block_searching_flag = nil + remaining_lines.collect!{|line| + if !block_searching_flag && !other_block_searching_flag + if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i + block_searching_flag = :module + block_searching_lines << line + module_program_name = $1 + module_program_trailing = find_comments($2) + next false + elsif line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i || + line =~ /^\s*?\w/ && !block_start?(line) + block_searching_flag = :program + block_searching_lines << line + module_program_name = $1 || "" + module_program_trailing = find_comments($2) + next false + + elsif block_start?(line) + other_block_searching_flag = true + next line + + elsif line =~ /^\s*?!\s?(.*)/ + pre_comment << line + next line + else + pre_comment = [] + next line + end + elsif other_block_searching_flag + other_block_level_depth += 1 if block_start?(line) + other_block_level_depth -= 1 if block_end?(line) + if other_block_level_depth < 0 + other_block_level_depth = 0 + other_block_searching_flag = nil + end + next line + end + + block_searching_lines << line + level_depth += 1 if block_start?(line) + level_depth -= 1 if block_end?(line) + if level_depth >= 0 + next false + end + + # "module_program_code" is formatted. + # ":nodoc:" flag is checked. + # + module_program_code = block_searching_lines.join("\n") + module_program_code = remove_empty_head_lines(module_program_code) + if module_program_trailing =~ /^:nodoc:/ + # next loop to search next block + level_depth = 0 + block_searching_flag = false + block_searching_lines = [] + pre_comment = [] + next false + end + + # NormalClass is created, and added to @top_level + # + if block_searching_flag == :module + module_name = module_program_name + module_code = module_program_code + module_trailing = module_program_trailing + progress "m" + @stats.num_modules += 1 + f9x_module = @top_level.add_module NormalClass, module_name + f9x_module.record_location @top_level + + f9x_comment = COMMENTS_ARE_UPPER ? + find_comments(pre_comment.join("\n")) + "\n" + module_trailing : + module_trailing + "\n" + find_comments(module_code.sub(/^.*$\n/i, '')) + f9x_module.comment = f9x_comment + parse_program_or_module(f9x_module, module_code) + + TopLevel.all_files.each do |name, toplevel| + if toplevel.include_includes?(module_name, @options.ignore_case) + if !toplevel.include_requires?(@file_name, @options.ignore_case) + toplevel.add_require(Require.new(@file_name, "")) + end + end + toplevel.each_classmodule{|m| + if m.include_includes?(module_name, @options.ignore_case) + if !m.include_requires?(@file_name, @options.ignore_case) + m.add_require(Require.new(@file_name, "")) + end + end + } + end + elsif block_searching_flag == :program + program_name = module_program_name + program_code = module_program_code + program_trailing = module_program_trailing + progress "p" + program_comment = COMMENTS_ARE_UPPER ? + find_comments(pre_comment.join("\n")) + "\n" + program_trailing : + program_trailing + "\n" + find_comments(program_code.sub(/^.*$\n/i, '')) + program_comment = "\n\n= Program #{program_name}\n\n" \ + + program_comment + @top_level.comment << program_comment + parse_program_or_module(@top_level, program_code, :private) + end + + # next loop to search next block + level_depth = 0 + block_searching_flag = false + block_searching_lines = [] + pre_comment = [] + next false + } + + remaining_lines.delete_if{ |line| + line == false + } + + # External subprograms and functions are parsed + # + parse_program_or_module(@top_level, remaining_lines.join("\n"), + :public, true) + + @top_level + end # End of scan + + private + + def parse_program_or_module(container, code, + visibility=:public, external=nil) + return unless container + return unless code + remaining_lines = code.split("\n") + remaining_code = "#{code}" + + # + # Parse variables before "contains" in module + # + level_depth = 0 + before_contains_lines = [] + before_contains_code = nil + before_contains_flag = nil + remaining_lines.each{ |line| + if !before_contains_flag + if line =~ /^\s*?module\s+\w+\s*?(!.*?)?$/i + before_contains_flag = true + end + else + break if line =~ /^\s*?contains\s*?(!.*?)?$/i + level_depth += 1 if block_start?(line) + level_depth -= 1 if block_end?(line) + break if level_depth < 0 + before_contains_lines << line + end + } + before_contains_code = before_contains_lines.join("\n") + if before_contains_code + before_contains_code.gsub!(/^\s*?interface\s+.*?\s+end\s+interface.*?$/im, "") + before_contains_code.gsub!(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "") + end + + # + # Parse global "use" + # + use_check_code = "#{before_contains_code}" + cascaded_modules_list = [] + while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i + use_check_code = $~.pre_match + use_check_code << $~.post_match + used_mod_name = $1.strip.chomp + used_list = $2 || "" + used_trailing = $3 || "" + next if used_trailing =~ /!:nodoc:/ + if !container.include_includes?(used_mod_name, @options.ignore_case) + progress "." + container.add_include Include.new(used_mod_name, "") + end + if ! (used_list =~ /\,\s*?only\s*?:/i ) + cascaded_modules_list << "\#" + used_mod_name + end + end + + # + # Parse public and private, and store information. + # This information is used when "add_method" and + # "set_visibility_for" are called. + # + visibility_default, visibility_info = + parse_visibility(remaining_lines.join("\n"), visibility, container) + @@public_methods.concat visibility_info + if visibility_default == :public + if !cascaded_modules_list.empty? + cascaded_modules = + Attr.new("Cascaded Modules", + "Imported modules all of whose components are published again", + "", + cascaded_modules_list.join(", ")) + container.add_attribute(cascaded_modules) + end + end + + # + # Check rename elements + # + use_check_code = "#{before_contains_code}" + while use_check_code =~ /^\s*?use\s+(\w+)\s*?\,(.+)$/i + use_check_code = $~.pre_match + use_check_code << $~.post_match + used_mod_name = $1.strip.chomp + used_elements = $2.sub(/\s*?only\s*?:\s*?/i, '') + used_elements.split(",").each{ |used| + if /\s*?(\w+)\s*?=>\s*?(\w+)\s*?/ =~ used + local = $1 + org = $2 + @@public_methods.collect!{ |pub_meth| + if local == pub_meth["name"] || + local.upcase == pub_meth["name"].upcase && + @options.ignore_case + pub_meth["name"] = org + pub_meth["local_name"] = local + end + pub_meth + } + end + } + end + + # + # Parse private "use" + # + use_check_code = remaining_lines.join("\n") + while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i + use_check_code = $~.pre_match + use_check_code << $~.post_match + used_mod_name = $1.strip.chomp + used_trailing = $3 || "" + next if used_trailing =~ /!:nodoc:/ + if !container.include_includes?(used_mod_name, @options.ignore_case) + progress "." + container.add_include Include.new(used_mod_name, "") + end + end + + container.each_includes{ |inc| + TopLevel.all_files.each do |name, toplevel| + indicated_mod = toplevel.find_symbol(inc.name, + nil, @options.ignore_case) + if indicated_mod + indicated_name = indicated_mod.parent.file_relative_name + if !container.include_requires?(indicated_name, @options.ignore_case) + container.add_require(Require.new(indicated_name, "")) + end + break + end + end + } + + # + # Parse derived-types definitions + # + derived_types_comment = "" + remaining_code = remaining_lines.join("\n") + while remaining_code =~ /^\s*? + type[\s\,]+(public|private)?\s*?(::)?\s*? + (\w+)\s*?(!.*?)?$ + (.*?) + ^\s*?end\s+type.*?$ + /imx + remaining_code = $~.pre_match + remaining_code << $~.post_match + typename = $3.chomp.strip + type_elements = $5 || "" + type_code = remove_empty_head_lines($&) + type_trailing = find_comments($4) + next if type_trailing =~ /^:nodoc:/ + type_visibility = $1 + type_comment = COMMENTS_ARE_UPPER ? + find_comments($~.pre_match) + "\n" + type_trailing : + type_trailing + "\n" + find_comments(type_code.sub(/^.*$\n/i, '')) + type_element_visibility_public = true + type_code.split("\n").each{ |line| + if /^\s*?private\s*?$/ =~ line + type_element_visibility_public = nil + break + end + } if type_code + + args_comment = "" + type_args_info = nil + + if @options.show_all + args_comment = find_arguments(nil, type_code, true) + else + type_public_args_list = [] + type_args_info = definition_info(type_code) + type_args_info.each{ |arg| + arg_is_public = type_element_visibility_public + arg_is_public = true if arg.include_attr?("public") + arg_is_public = nil if arg.include_attr?("private") + type_public_args_list << arg.varname if arg_is_public + } + args_comment = find_arguments(type_public_args_list, type_code) + end + + type = AnyMethod.new("type #{typename}", typename) + type.singleton = false + type.params = "" + type.comment = " Derived Type :: \n" + type.comment << args_comment if args_comment + type.comment << type_comment if type_comment + progress "t" + @stats.num_methods += 1 + container.add_method type + + set_visibility(container, typename, visibility_default, @@public_methods) + + if type_visibility + type_visibility.gsub!(/\s/,'') + type_visibility.gsub!(/\,/,'') + type_visibility.gsub!(/:/,'') + type_visibility.downcase! + if type_visibility == "public" + container.set_visibility_for([typename], :public) + elsif type_visibility == "private" + container.set_visibility_for([typename], :private) + end + end + + check_public_methods(type, container.name) + + if @options.show_all + derived_types_comment << ", " unless derived_types_comment.empty? + derived_types_comment << typename + else + if type.visibility == :public + derived_types_comment << ", " unless derived_types_comment.empty? + derived_types_comment << typename + end + end + + end + + if !derived_types_comment.empty? + derived_types_table = + Attr.new("Derived Types", "Derived_Types", "", + derived_types_comment) + container.add_attribute(derived_types_table) + end + + # + # move interface scope + # + interface_code = "" + while remaining_code =~ /^\s*? + interface( + \s+\w+ | + \s+operator\s*?\(.*?\) | + \s+assignment\s*?\(\s*?=\s*?\) + )?\s*?$ + (.*?) + ^\s*?end\s+interface.*?$ + /imx + interface_code << remove_empty_head_lines($&) + "\n" + remaining_code = $~.pre_match + remaining_code << $~.post_match + end + + # + # Parse global constants or variables in modules + # + const_var_defs = definition_info(before_contains_code) + const_var_defs.each{|defitem| + next if defitem.nodoc + const_or_var_type = "Variable" + const_or_var_progress = "v" + if defitem.include_attr?("parameter") + const_or_var_type = "Constant" + const_or_var_progress = "c" + end + const_or_var = AnyMethod.new(const_or_var_type, defitem.varname) + const_or_var.singleton = false + const_or_var.params = "" + self_comment = find_arguments([defitem.varname], before_contains_code) + const_or_var.comment = "" + const_or_var_type + " :: \n" + const_or_var.comment << self_comment if self_comment + progress const_or_var_progress + @stats.num_methods += 1 + container.add_method const_or_var + + set_visibility(container, defitem.varname, visibility_default, @@public_methods) + + if defitem.include_attr?("public") + container.set_visibility_for([defitem.varname], :public) + elsif defitem.include_attr?("private") + container.set_visibility_for([defitem.varname], :private) + end + + check_public_methods(const_or_var, container.name) + + } if const_var_defs + + remaining_lines = remaining_code.split("\n") + + # "subroutine" or "function" parts are parsed (new) + # + level_depth = 0 + block_searching_flag = nil + block_searching_lines = [] + pre_comment = [] + procedure_trailing = "" + procedure_name = "" + procedure_params = "" + procedure_prefix = "" + procedure_result_arg = "" + procedure_type = "" + contains_lines = [] + contains_flag = nil + remaining_lines.collect!{|line| + if !block_searching_flag + # subroutine + if line =~ /^\s*? + (recursive|pure|elemental)?\s*? + subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$ + /ix + block_searching_flag = :subroutine + block_searching_lines << line + + procedure_name = $2.chomp.strip + procedure_params = $3 || "" + procedure_prefix = $1 || "" + procedure_trailing = $4 || "!" + next false + + # function + elsif line =~ /^\s*? + (recursive|pure|elemental)?\s*? + ( + character\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + | type\s*?\([\w\s]+?\)\s+ + | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + | double\s+precision\s+ + | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + )? + function\s+(\w+)\s*? + (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$ + /ix + block_searching_flag = :function + block_searching_lines << line + + procedure_prefix = $1 || "" + procedure_type = $2 ? $2.chomp.strip : nil + procedure_name = $8.chomp.strip + procedure_params = $9 || "" + procedure_result_arg = $11 ? $11.chomp.strip : procedure_name + procedure_trailing = $12 || "!" + next false + elsif line =~ /^\s*?!\s?(.*)/ + pre_comment << line + next line + else + pre_comment = [] + next line + end + end + contains_flag = true if line =~ /^\s*?contains\s*?(!.*?)?$/ + block_searching_lines << line + contains_lines << line if contains_flag + + level_depth += 1 if block_start?(line) + level_depth -= 1 if block_end?(line) + if level_depth >= 0 + next false + end + + # "procedure_code" is formatted. + # ":nodoc:" flag is checked. + # + procedure_code = block_searching_lines.join("\n") + procedure_code = remove_empty_head_lines(procedure_code) + if procedure_trailing =~ /^!:nodoc:/ + # next loop to search next block + level_depth = 0 + block_searching_flag = nil + block_searching_lines = [] + pre_comment = [] + procedure_trailing = "" + procedure_name = "" + procedure_params = "" + procedure_prefix = "" + procedure_result_arg = "" + procedure_type = "" + contains_lines = [] + contains_flag = nil + next false + end + + # AnyMethod is created, and added to container + # + subroutine_function = nil + if block_searching_flag == :subroutine + subroutine_prefix = procedure_prefix + subroutine_name = procedure_name + subroutine_params = procedure_params + subroutine_trailing = procedure_trailing + subroutine_code = procedure_code + + subroutine_comment = COMMENTS_ARE_UPPER ? + pre_comment.join("\n") + "\n" + subroutine_trailing : + subroutine_trailing + "\n" + subroutine_code.sub(/^.*$\n/i, '') + subroutine = AnyMethod.new("subroutine", subroutine_name) + parse_subprogram(subroutine, subroutine_params, + subroutine_comment, subroutine_code, + before_contains_code, nil, subroutine_prefix) + progress "s" + @stats.num_methods += 1 + container.add_method subroutine + subroutine_function = subroutine + + elsif block_searching_flag == :function + function_prefix = procedure_prefix + function_type = procedure_type + function_name = procedure_name + function_params_org = procedure_params + function_result_arg = procedure_result_arg + function_trailing = procedure_trailing + function_code_org = procedure_code + + function_comment = COMMENTS_ARE_UPPER ? + pre_comment.join("\n") + "\n" + function_trailing : + function_trailing + "\n " + function_code_org.sub(/^.*$\n/i, '') + + function_code = "#{function_code_org}" + if function_type + function_code << "\n" + function_type + " :: " + function_result_arg + end + + function_params = + function_params_org.sub(/^\(/, "\(#{function_result_arg}, ") + + function = AnyMethod.new("function", function_name) + parse_subprogram(function, function_params, + function_comment, function_code, + before_contains_code, true, function_prefix) + + # Specific modification due to function + function.params.sub!(/\(\s*?#{function_result_arg}\s*?,\s*?/, "\( ") + function.params << " result(" + function_result_arg + ")" + function.start_collecting_tokens + function.add_token Token.new(1,1).set_text(function_code_org) + + progress "f" + @stats.num_methods += 1 + container.add_method function + subroutine_function = function + + end + + # The visibility of procedure is specified + # + set_visibility(container, procedure_name, + visibility_default, @@public_methods) + + # The alias for this procedure from external modules + # + check_external_aliases(procedure_name, + subroutine_function.params, + subroutine_function.comment, subroutine_function) if external + check_public_methods(subroutine_function, container.name) + + + # contains_lines are parsed as private procedures + if contains_flag + parse_program_or_module(container, + contains_lines.join("\n"), :private) + end + + # next loop to search next block + level_depth = 0 + block_searching_flag = nil + block_searching_lines = [] + pre_comment = [] + procedure_trailing = "" + procedure_name = "" + procedure_params = "" + procedure_prefix = "" + procedure_result_arg = "" + contains_lines = [] + contains_flag = nil + next false + } # End of remaining_lines.collect!{|line| + + # Array remains_lines is converted to String remains_code again + # + remaining_code = remaining_lines.join("\n") + + # + # Parse interface + # + interface_scope = false + generic_name = "" + interface_code.split("\n").each{ |line| + if /^\s*? + interface( + \s+\w+| + \s+operator\s*?\(.*?\)| + \s+assignment\s*?\(\s*?=\s*?\) + )? + \s*?(!.*?)?$ + /ix =~ line + generic_name = $1 ? $1.strip.chomp : nil + interface_trailing = $2 || "!" + interface_scope = true + interface_scope = false if interface_trailing =~ /!:nodoc:/ +# if generic_name =~ /operator\s*?\((.*?)\)/i +# operator_name = $1 +# if operator_name && !operator_name.empty? +# generic_name = "#{operator_name}" +# end +# end +# if generic_name =~ /assignment\s*?\((.*?)\)/i +# assignment_name = $1 +# if assignment_name && !assignment_name.empty? +# generic_name = "#{assignment_name}" +# end +# end + end + if /^\s*?end\s+interface/i =~ line + interface_scope = false + generic_name = nil + end + # internal alias + if interface_scope && /^\s*?module\s+procedure\s+(.*?)(!.*?)?$/i =~ line + procedures = $1.strip.chomp + procedures_trailing = $2 || "!" + next if procedures_trailing =~ /!:nodoc:/ + procedures.split(",").each{ |proc| + proc.strip! + proc.chomp! + next if generic_name == proc || !generic_name + old_meth = container.find_symbol(proc, nil, @options.ignore_case) + next if !old_meth + nolink = old_meth.visibility == :private ? true : nil + nolink = nil if @options.show_all + new_meth = + initialize_external_method(generic_name, proc, + old_meth.params, nil, + old_meth.comment, + old_meth.clone.token_stream[0].text, + true, nolink) + new_meth.singleton = old_meth.singleton + + progress "i" + @stats.num_methods += 1 + container.add_method new_meth + + set_visibility(container, generic_name, visibility_default, @@public_methods) + + check_public_methods(new_meth, container.name) + + } + end + + # external aliases + if interface_scope + # subroutine + proc = nil + params = nil + procedures_trailing = nil + if line =~ /^\s*? + (recursive|pure|elemental)?\s*? + subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$ + /ix + proc = $2.chomp.strip + generic_name = proc unless generic_name + params = $3 || "" + procedures_trailing = $4 || "!" + + # function + elsif line =~ /^\s*? + (recursive|pure|elemental)?\s*? + ( + character\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + | type\s*?\([\w\s]+?\)\s+ + | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + | double\s+precision\s+ + | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + )? + function\s+(\w+)\s*? + (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$ + /ix + proc = $8.chomp.strip + generic_name = proc unless generic_name + params = $9 || "" + procedures_trailing = $12 || "!" + else + next + end + next if procedures_trailing =~ /!:nodoc:/ + indicated_method = nil + indicated_file = nil + TopLevel.all_files.each do |name, toplevel| + indicated_method = toplevel.find_local_symbol(proc, @options.ignore_case) + indicated_file = name + break if indicated_method + end + + if indicated_method + external_method = + initialize_external_method(generic_name, proc, + indicated_method.params, + indicated_file, + indicated_method.comment) + + progress "e" + @stats.num_methods += 1 + container.add_method external_method + set_visibility(container, generic_name, visibility_default, @@public_methods) + if !container.include_requires?(indicated_file, @options.ignore_case) + container.add_require(Require.new(indicated_file, "")) + end + check_public_methods(external_method, container.name) + + else + @@external_aliases << { + "new_name" => generic_name, + "old_name" => proc, + "file_or_module" => container, + "visibility" => find_visibility(container, generic_name, @@public_methods) || visibility_default + } + end + end + + } if interface_code # End of interface_code.split("\n").each ... + + # + # Already imported methods are removed from @@public_methods. + # Remainders are assumed to be imported from other modules. + # + @@public_methods.delete_if{ |method| method["entity_is_discovered"]} + + @@public_methods.each{ |pub_meth| + next unless pub_meth["file_or_module"].name == container.name + pub_meth["used_modules"].each{ |used_mod| + TopLevel.all_classes_and_modules.each{ |modules| + if modules.name == used_mod || + modules.name.upcase == used_mod.upcase && + @options.ignore_case + modules.method_list.each{ |meth| + if meth.name == pub_meth["name"] || + meth.name.upcase == pub_meth["name"].upcase && + @options.ignore_case + new_meth = initialize_public_method(meth, + modules.name) + if pub_meth["local_name"] + new_meth.name = pub_meth["local_name"] + end + progress "e" + @stats.num_methods += 1 + container.add_method new_meth + end + } + end + } + } + } + + container + end # End of parse_program_or_module + + # + # Parse arguments, comment, code of subroutine and function. + # Return AnyMethod object. + # + def parse_subprogram(subprogram, params, comment, code, + before_contains=nil, function=nil, prefix=nil) + subprogram.singleton = false + prefix = "" if !prefix + arguments = params.sub(/\(/, "").sub(/\)/, "").split(",") if params + args_comment, params_opt = + find_arguments(arguments, code.sub(/^s*?contains\s*?(!.*?)?$.*/im, ""), + nil, nil, true) + params_opt = "( " + params_opt + " ) " if params_opt + subprogram.params = params_opt || "" + namelist_comment = find_namelists(code, before_contains) + + block_comment = find_comments comment + if function + subprogram.comment = " Function :: #{prefix}\n" + else + subprogram.comment = " Subroutine :: #{prefix}\n" + end + subprogram.comment << args_comment if args_comment + subprogram.comment << block_comment if block_comment + subprogram.comment << namelist_comment if namelist_comment + + # For output source code + subprogram.start_collecting_tokens + subprogram.add_token Token.new(1,1).set_text(code) + + subprogram + end + + # + # Collect comment for file entity + # + def collect_first_comment(body) + comment = "" + not_comment = "" + comment_start = false + comment_end = false + body.split("\n").each{ |line| + if comment_end + not_comment << line + not_comment << "\n" + elsif /^\s*?!\s?(.*)$/i =~ line + comment_start = true + comment << $1 + comment << "\n" + elsif /^\s*?$/i =~ line + comment_end = true if comment_start && COMMENTS_ARE_UPPER + else + comment_end = true + not_comment << line + not_comment << "\n" + end + } + return comment, not_comment + end + + + # Return comments of definitions of arguments + # + # If "all" argument is true, information of all arguments are returned. + # If "modified_params" is true, list of arguments are decorated, + # for exameple, optional arguments are parenthetic as "[arg]". + # + def find_arguments(args, text, all=nil, indent=nil, modified_params=nil) + return unless args || all + indent = "" unless indent + args = ["all"] if all + params = "" if modified_params + comma = "" + return unless text + args_rdocforms = "\n" + remaining_lines = "#{text}" + definitions = definition_info(remaining_lines) + args.each{ |arg| + arg.strip! + arg.chomp! + definitions.each { |defitem| + if arg == defitem.varname.strip.chomp || all + args_rdocforms << <<-"EOF" + +#{indent}#{defitem.varname.chomp.strip}#{defitem.arraysuffix} #{defitem.inivalue} :: +#{indent} #{defitem.types.chomp.strip} +EOF + if !defitem.comment.chomp.strip.empty? + comment = "" + defitem.comment.split("\n").each{ |line| + comment << " " + line + "\n" + } + args_rdocforms << <<-"EOF" + +#{indent} :: +#{indent} +#{indent} #{comment.chomp.strip} +EOF + end + + if modified_params + if defitem.include_attr?("optional") + params << "#{comma}[#{arg}]" + else + params << "#{comma}#{arg}" + end + comma = ", " + end + end + } + } + if modified_params + return args_rdocforms, params + else + return args_rdocforms + end + end + + # Return comments of definitions of namelists + # + def find_namelists(text, before_contains=nil) + return nil if !text + result = "" + lines = "#{text}" + before_contains = "" if !before_contains + while lines =~ /^\s*?namelist\s+\/\s*?(\w+)\s*?\/([\s\w\,]+)$/i + lines = $~.post_match + nml_comment = COMMENTS_ARE_UPPER ? + find_comments($~.pre_match) : find_comments($~.post_match) + nml_name = $1 + nml_args = $2.split(",") + result << "\n\n=== NAMELIST " + nml_name + "\n\n" + result << nml_comment + "\n" if nml_comment + if lines.split("\n")[0] =~ /^\//i + lines = "namelist " + lines + end + result << find_arguments(nml_args, "#{text}" + "\n" + before_contains) + end + return result + end + + # + # Comments just after module or subprogram, or arguments are + # returnd. If "COMMENTS_ARE_UPPER" is true, comments just before + # modules or subprograms are returnd + # + def find_comments text + return "" unless text + lines = text.split("\n") + lines.reverse! if COMMENTS_ARE_UPPER + comment_block = Array.new + lines.each do |line| + break if line =~ /^\s*?\w/ || line =~ /^\s*?$/ + if COMMENTS_ARE_UPPER + comment_block.unshift line.sub(/^\s*?!\s?/,"") + else + comment_block.push line.sub(/^\s*?!\s?/,"") + end + end + nice_lines = comment_block.join("\n").split "\n\s*?\n" + nice_lines[0] ||= "" + nice_lines.shift + end + + def progress(char) + unless @options.quiet + @progress.print(char) + @progress.flush + end + end + + # + # Create method for internal alias + # + def initialize_public_method(method, parent) + return if !method || !parent + + new_meth = AnyMethod.new("External Alias for module", method.name) + new_meth.singleton = method.singleton + new_meth.params = method.params.clone + new_meth.comment = remove_trailing_alias(method.comment.clone) + new_meth.comment << "\n\n#{EXTERNAL_ALIAS_MES} #{parent.strip.chomp}\##{method.name}" + + return new_meth + end + + # + # Create method for external alias + # + # If argument "internal" is true, file is ignored. + # + def initialize_external_method(new, old, params, file, comment, token=nil, + internal=nil, nolink=nil) + return nil unless new || old + + if internal + external_alias_header = "#{INTERNAL_ALIAS_MES} " + external_alias_text = external_alias_header + old + elsif file + external_alias_header = "#{EXTERNAL_ALIAS_MES} " + external_alias_text = external_alias_header + file + "#" + old + else + return nil + end + external_meth = AnyMethod.new(external_alias_text, new) + external_meth.singleton = false + external_meth.params = params + external_comment = remove_trailing_alias(comment) + "\n\n" if comment + external_meth.comment = external_comment || "" + if nolink && token + external_meth.start_collecting_tokens + external_meth.add_token Token.new(1,1).set_text(token) + else + external_meth.comment << external_alias_text + end + + return external_meth + end + + + + # + # Parse visibility + # + def parse_visibility(code, default, container) + result = [] + visibility_default = default || :public + + used_modules = [] + container.includes.each{|i| used_modules << i.name} if container + + remaining_code = code.gsub(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "") + remaining_code.split("\n").each{ |line| + if /^\s*?private\s*?$/ =~ line + visibility_default = :private + break + end + } if remaining_code + + remaining_code.split("\n").each{ |line| + if /^\s*?private\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line + methods = $2.sub(/!.*$/, '') + methods.split(",").each{ |meth| + meth.sub!(/!.*$/, '') + meth.gsub!(/:/, '') + result << { + "name" => meth.chomp.strip, + "visibility" => :private, + "used_modules" => used_modules.clone, + "file_or_module" => container, + "entity_is_discovered" => nil, + "local_name" => nil + } + } + elsif /^\s*?public\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line + methods = $2.sub(/!.*$/, '') + methods.split(",").each{ |meth| + meth.sub!(/!.*$/, '') + meth.gsub!(/:/, '') + result << { + "name" => meth.chomp.strip, + "visibility" => :public, + "used_modules" => used_modules.clone, + "file_or_module" => container, + "entity_is_discovered" => nil, + "local_name" => nil + } + } + end + } if remaining_code + + if container + result.each{ |vis_info| + vis_info["parent"] = container.name + } + end + + return visibility_default, result + end + + # + # Set visibility + # + # "subname" element of "visibility_info" is deleted. + # + def set_visibility(container, subname, visibility_default, visibility_info) + return unless container || subname || visibility_default || visibility_info + not_found = true + visibility_info.collect!{ |info| + if info["name"] == subname || + @options.ignore_case && info["name"].upcase == subname.upcase + if info["file_or_module"].name == container.name + container.set_visibility_for([subname], info["visibility"]) + info["entity_is_discovered"] = true + not_found = false + end + end + info + } + if not_found + return container.set_visibility_for([subname], visibility_default) + else + return container + end + end + + # + # Find visibility + # + def find_visibility(container, subname, visibility_info) + return nil if !subname || !visibility_info + visibility_info.each{ |info| + if info["name"] == subname || + @options.ignore_case && info["name"].upcase == subname.upcase + if info["parent"] == container.name + return info["visibility"] + end + end + } + return nil + end + + # + # Check external aliases + # + def check_external_aliases(subname, params, comment, test=nil) + @@external_aliases.each{ |alias_item| + if subname == alias_item["old_name"] || + subname.upcase == alias_item["old_name"].upcase && + @options.ignore_case + + new_meth = initialize_external_method(alias_item["new_name"], + subname, params, @file_name, + comment) + new_meth.visibility = alias_item["visibility"] + + progress "e" + @stats.num_methods += 1 + alias_item["file_or_module"].add_method(new_meth) + + if !alias_item["file_or_module"].include_requires?(@file_name, @options.ignore_case) + alias_item["file_or_module"].add_require(Require.new(@file_name, "")) + end + end + } + end + + # + # Check public_methods + # + def check_public_methods(method, parent) + return if !method || !parent + @@public_methods.each{ |alias_item| + parent_is_used_module = nil + alias_item["used_modules"].each{ |used_module| + if used_module == parent || + used_module.upcase == parent.upcase && + @options.ignore_case + parent_is_used_module = true + end + } + next if !parent_is_used_module + + if method.name == alias_item["name"] || + method.name.upcase == alias_item["name"].upcase && + @options.ignore_case + + new_meth = initialize_public_method(method, parent) + if alias_item["local_name"] + new_meth.name = alias_item["local_name"] + end + + progress "e" + @stats.num_methods += 1 + alias_item["file_or_module"].add_method new_meth + end + } + end + + # + # Continuous lines are united. + # + # Comments in continuous lines are removed. + # + def united_to_one_line(f90src) + return "" unless f90src + lines = f90src.split("\n") + previous_continuing = false + now_continuing = false + body = "" + lines.each{ |line| + words = line.split("") + next if words.empty? && previous_continuing + commentout = false + brank_flag = true ; brank_char = "" + squote = false ; dquote = false + ignore = false + words.collect! { |char| + if previous_continuing && brank_flag + now_continuing = true + ignore = true + case char + when "!" ; break + when " " ; brank_char << char ; next "" + when "&" + brank_flag = false + now_continuing = false + next "" + else + brank_flag = false + now_continuing = false + ignore = false + next brank_char + char + end + end + ignore = false + + if now_continuing + next "" + elsif !(squote) && !(dquote) && !(commentout) + case char + when "!" ; commentout = true ; next char + when "\""; dquote = true ; next char + when "\'"; squote = true ; next char + when "&" ; now_continuing = true ; next "" + else next char + end + elsif commentout + next char + elsif squote + case char + when "\'"; squote = false ; next char + else next char + end + elsif dquote + case char + when "\""; dquote = false ; next char + else next char + end + end + } + if !ignore && !previous_continuing || !brank_flag + if previous_continuing + body << words.join("") + else + body << "\n" + words.join("") + end + end + previous_continuing = now_continuing ? true : nil + now_continuing = nil + } + return body + end + + + # + # Continuous line checker + # + def continuous_line?(line) + continuous = false + if /&\s*?(!.*)?$/ =~ line + continuous = true + if comment_out?($~.pre_match) + continuous = false + end + end + return continuous + end + + # + # Comment out checker + # + def comment_out?(line) + return nil unless line + commentout = false + squote = false ; dquote = false + line.split("").each { |char| + if !(squote) && !(dquote) + case char + when "!" ; commentout = true ; break + when "\""; dquote = true + when "\'"; squote = true + else next + end + elsif squote + case char + when "\'"; squote = false + else next + end + elsif dquote + case char + when "\""; dquote = false + else next + end + end + } + return commentout + end + + # + # Semicolons are replaced to line feed. + # + def semicolon_to_linefeed(text) + return "" unless text + lines = text.split("\n") + lines.collect!{ |line| + words = line.split("") + commentout = false + squote = false ; dquote = false + words.collect! { |char| + if !(squote) && !(dquote) && !(commentout) + case char + when "!" ; commentout = true ; next char + when "\""; dquote = true ; next char + when "\'"; squote = true ; next char + when ";" ; "\n" + else next char + end + elsif commentout + next char + elsif squote + case char + when "\'"; squote = false ; next char + else next char + end + elsif dquote + case char + when "\""; dquote = false ; next char + else next char + end + end + } + words.join("") + } + return lines.join("\n") + end + + # + # Which "line" is start of block (module, program, block data, + # subroutine, function) statement ? + # + def block_start?(line) + return nil if !line + + if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i || + line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i || + line =~ /^\s*?block\s+data(\s+\w+)?\s*?(!.*?)?$/i || + line =~ \ + /^\s*? + (recursive|pure|elemental)?\s*? + subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$ + /ix || + line =~ \ + /^\s*? + (recursive|pure|elemental)?\s*? + ( + character\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + | type\s*?\([\w\s]+?\)\s+ + | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + | double\s+precision\s+ + | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+ + )? + function\s+(\w+)\s*? + (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$ + /ix + return true + end + + return nil + end + + # + # Which "line" is end of block (module, program, block data, + # subroutine, function) statement ? + # + def block_end?(line) + return nil if !line + + if line =~ /^\s*?end\s*?(!.*?)?$/i || + line =~ /^\s*?end\s+module(\s+\w+)?\s*?(!.*?)?$/i || + line =~ /^\s*?end\s+program(\s+\w+)?\s*?(!.*?)?$/i || + line =~ /^\s*?end\s+block\s+data(\s+\w+)?\s*?(!.*?)?$/i || + line =~ /^\s*?end\s+subroutine(\s+\w+)?\s*?(!.*?)?$/i || + line =~ /^\s*?end\s+function(\s+\w+)?\s*?(!.*?)?$/i + return true + end + + return nil + end + + # + # Remove "Alias for" in end of comments + # + def remove_trailing_alias(text) + return "" if !text + lines = text.split("\n").reverse + comment_block = Array.new + checked = false + lines.each do |line| + if !checked + if /^\s?#{INTERNAL_ALIAS_MES}/ =~ line || + /^\s?#{EXTERNAL_ALIAS_MES}/ =~ line + checked = true + next + end + end + comment_block.unshift line + end + nice_lines = comment_block.join("\n") + nice_lines ||= "" + return nice_lines + end + + # Empty lines in header are removed + def remove_empty_head_lines(text) + return "" unless text + lines = text.split("\n") + header = true + lines.delete_if{ |line| + header = false if /\S/ =~ line + header && /^\s*?$/ =~ line + } + lines.join("\n") + end + + + # header marker "=", "==", ... are removed + def remove_header_marker(text) + return text.gsub(/^\s?(=+)/, '\1') + end + + def remove_private_comments(body) + body.gsub!(/^\s*!--\s*?$.*?^\s*!\+\+\s*?$/m, '') + return body + end + + + # + # Information of arguments of subroutines and functions in Fortran95 + # + class Fortran95Definition + + # Name of variable + # + attr_reader :varname + + # Types of variable + # + attr_reader :types + + # Initial Value + # + attr_reader :inivalue + + # Suffix of array + # + attr_reader :arraysuffix + + # Comments + # + attr_accessor :comment + + # Flag of non documentation + # + attr_accessor :nodoc + + def initialize(varname, types, inivalue, arraysuffix, comment, + nodoc=false) + @varname = varname + @types = types + @inivalue = inivalue + @arraysuffix = arraysuffix + @comment = comment + @nodoc = nodoc + end + + def to_s + return <<-EOF + +EOF + end + + # + # If attr is included, true is returned + # + def include_attr?(attr) + return if !attr + @types.split(",").each{ |type| + return true if type.strip.chomp.upcase == attr.strip.chomp.upcase + } + return nil + end + + end # End of Fortran95Definition + + # + # Parse string argument "text", and Return Array of + # Fortran95Definition object + # + def definition_info(text) + return nil unless text + lines = "#{text}" + defs = Array.new + comment = "" + trailing_comment = "" + under_comment_valid = false + lines.split("\n").each{ |line| + if /^\s*?!\s?(.*)/ =~ line + if COMMENTS_ARE_UPPER + comment << remove_header_marker($1) + comment << "\n" + elsif defs[-1] && under_comment_valid + defs[-1].comment << "\n" + defs[-1].comment << remove_header_marker($1) + end + next + elsif /^\s*?$/ =~ line + comment = "" + under_comment_valid = false + next + end + type = "" + characters = "" + if line =~ /^\s*? + ( + character\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]* + | type\s*?\([\w\s]+?\)[\s\,]* + | integer\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]* + | real\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]* + | double\s+precision[\s\,]* + | logical\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]* + | complex\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]* + ) + (.*?::)? + (.+)$ + /ix + characters = $8 + type = $1 + type << $7.gsub(/::/, '').gsub(/^\s*?\,/, '') if $7 + else + under_comment_valid = false + next + end + squote = false ; dquote = false ; bracket = 0 + iniflag = false; commentflag = false + varname = "" ; arraysuffix = "" ; inivalue = "" + start_pos = defs.size + characters.split("").each { |char| + if !(squote) && !(dquote) && bracket <= 0 && !(iniflag) && !(commentflag) + case char + when "!" ; commentflag = true + when "(" ; bracket += 1 ; arraysuffix = char + when "\""; dquote = true + when "\'"; squote = true + when "=" ; iniflag = true ; inivalue << char + when "," + defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment) + varname = "" ; arraysuffix = "" ; inivalue = "" + under_comment_valid = true + when " " ; next + else ; varname << char + end + elsif commentflag + comment << remove_header_marker(char) + trailing_comment << remove_header_marker(char) + elsif iniflag + if dquote + case char + when "\"" ; dquote = false ; inivalue << char + else ; inivalue << char + end + elsif squote + case char + when "\'" ; squote = false ; inivalue << char + else ; inivalue << char + end + elsif bracket > 0 + case char + when "(" ; bracket += 1 ; inivalue << char + when ")" ; bracket -= 1 ; inivalue << char + else ; inivalue << char + end + else + case char + when "," + defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment) + varname = "" ; arraysuffix = "" ; inivalue = "" + iniflag = false + under_comment_valid = true + when "(" ; bracket += 1 ; inivalue << char + when "\""; dquote = true ; inivalue << char + when "\'"; squote = true ; inivalue << char + when "!" ; commentflag = true + else ; inivalue << char + end + end + elsif !(squote) && !(dquote) && bracket > 0 + case char + when "(" ; bracket += 1 ; arraysuffix << char + when ")" ; bracket -= 1 ; arraysuffix << char + else ; arraysuffix << char + end + elsif squote + case char + when "\'"; squote = false ; inivalue << char + else ; inivalue << char + end + elsif dquote + case char + when "\""; dquote = false ; inivalue << char + else ; inivalue << char + end + end + } + defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment) + if trailing_comment =~ /^:nodoc:/ + defs[start_pos..-1].collect!{ |defitem| + defitem.nodoc = true + } + end + varname = "" ; arraysuffix = "" ; inivalue = "" + comment = "" + under_comment_valid = true + trailing_comment = "" + } + return defs + end + + + end # class Fortran95parser + +end # module RDoc diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parse_rb.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parse_rb.rb new file mode 100644 index 0000000000..58ba06084e --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parse_rb.rb @@ -0,0 +1,2605 @@ +#!/usr/local/bin/ruby + +# Parse a Ruby source file, building a set of objects +# representing the modules, classes, methods, +# requires, and includes we find (these classes +# are defined in code_objects.rb). + +# This file contains stuff stolen outright from: +# +# rtags.rb - +# ruby-lex.rb - ruby lexcal analizer +# ruby-token.rb - ruby tokens +# by Keiju ISHITSUKA (Nippon Rational Inc.) +# + +require "e2mmap" +require "irb/slex" + +require "rdoc/code_objects" +require "rdoc/tokenstream" + +require "rdoc/markup/simple_markup/preprocess" + +require "rdoc/parsers/parserfactory" + +$TOKEN_DEBUG = $DEBUG + +# Definitions of all tokens involved in the lexical analysis + +module RubyToken + EXPR_BEG = :EXPR_BEG + EXPR_MID = :EXPR_MID + EXPR_END = :EXPR_END + EXPR_ARG = :EXPR_ARG + EXPR_FNAME = :EXPR_FNAME + EXPR_DOT = :EXPR_DOT + EXPR_CLASS = :EXPR_CLASS + + class Token + NO_TEXT = "??".freeze + attr :text + + def initialize(line_no, char_no) + @line_no = line_no + @char_no = char_no + @text = NO_TEXT + end + + # Because we're used in contexts that expect to return a token, + # we set the text string and then return ourselves + def set_text(text) + @text = text + self + end + + attr_reader :line_no, :char_no, :text + end + + class TkNode < Token + attr :node + end + + class TkId < Token + def initialize(line_no, char_no, name) + super(line_no, char_no) + @name = name + end + attr :name + end + + class TkKW < TkId + end + + class TkVal < Token + def initialize(line_no, char_no, value = nil) + super(line_no, char_no) + set_text(value) + end + end + + class TkOp < Token + def name + self.class.op_name + end + end + + class TkOPASGN < TkOp + def initialize(line_no, char_no, op) + super(line_no, char_no) + op = TkReading2Token[op] unless op.kind_of?(Symbol) + @op = op + end + attr :op + end + + class TkUnknownChar < Token + def initialize(line_no, char_no, id) + super(line_no, char_no) + @name = char_no.chr + end + attr :name + end + + class TkError < Token + end + + def set_token_position(line, char) + @prev_line_no = line + @prev_char_no = char + end + + def Token(token, value = nil) + tk = nil + case token + when String, Symbol + source = token.kind_of?(String) ? TkReading2Token : TkSymbol2Token + if (tk = source[token]).nil? + IRB.fail TkReading2TokenNoKey, token + end + tk = Token(tk[0], value) + else + tk = if (token.ancestors & [TkId, TkVal, TkOPASGN, TkUnknownChar]).empty? + token.new(@prev_line_no, @prev_char_no) + else + token.new(@prev_line_no, @prev_char_no, value) + end + end + tk + end + + TokenDefinitions = [ + [:TkCLASS, TkKW, "class", EXPR_CLASS], + [:TkMODULE, TkKW, "module", EXPR_BEG], + [:TkDEF, TkKW, "def", EXPR_FNAME], + [:TkUNDEF, TkKW, "undef", EXPR_FNAME], + [:TkBEGIN, TkKW, "begin", EXPR_BEG], + [:TkRESCUE, TkKW, "rescue", EXPR_MID], + [:TkENSURE, TkKW, "ensure", EXPR_BEG], + [:TkEND, TkKW, "end", EXPR_END], + [:TkIF, TkKW, "if", EXPR_BEG, :TkIF_MOD], + [:TkUNLESS, TkKW, "unless", EXPR_BEG, :TkUNLESS_MOD], + [:TkTHEN, TkKW, "then", EXPR_BEG], + [:TkELSIF, TkKW, "elsif", EXPR_BEG], + [:TkELSE, TkKW, "else", EXPR_BEG], + [:TkCASE, TkKW, "case", EXPR_BEG], + [:TkWHEN, TkKW, "when", EXPR_BEG], + [:TkWHILE, TkKW, "while", EXPR_BEG, :TkWHILE_MOD], + [:TkUNTIL, TkKW, "until", EXPR_BEG, :TkUNTIL_MOD], + [:TkFOR, TkKW, "for", EXPR_BEG], + [:TkBREAK, TkKW, "break", EXPR_END], + [:TkNEXT, TkKW, "next", EXPR_END], + [:TkREDO, TkKW, "redo", EXPR_END], + [:TkRETRY, TkKW, "retry", EXPR_END], + [:TkIN, TkKW, "in", EXPR_BEG], + [:TkDO, TkKW, "do", EXPR_BEG], + [:TkRETURN, TkKW, "return", EXPR_MID], + [:TkYIELD, TkKW, "yield", EXPR_END], + [:TkSUPER, TkKW, "super", EXPR_END], + [:TkSELF, TkKW, "self", EXPR_END], + [:TkNIL, TkKW, "nil", EXPR_END], + [:TkTRUE, TkKW, "true", EXPR_END], + [:TkFALSE, TkKW, "false", EXPR_END], + [:TkAND, TkKW, "and", EXPR_BEG], + [:TkOR, TkKW, "or", EXPR_BEG], + [:TkNOT, TkKW, "not", EXPR_BEG], + [:TkIF_MOD, TkKW], + [:TkUNLESS_MOD, TkKW], + [:TkWHILE_MOD, TkKW], + [:TkUNTIL_MOD, TkKW], + [:TkALIAS, TkKW, "alias", EXPR_FNAME], + [:TkDEFINED, TkKW, "defined?", EXPR_END], + [:TklBEGIN, TkKW, "BEGIN", EXPR_END], + [:TklEND, TkKW, "END", EXPR_END], + [:Tk__LINE__, TkKW, "__LINE__", EXPR_END], + [:Tk__FILE__, TkKW, "__FILE__", EXPR_END], + + [:TkIDENTIFIER, TkId], + [:TkFID, TkId], + [:TkGVAR, TkId], + [:TkIVAR, TkId], + [:TkCONSTANT, TkId], + + [:TkINTEGER, TkVal], + [:TkFLOAT, TkVal], + [:TkSTRING, TkVal], + [:TkXSTRING, TkVal], + [:TkREGEXP, TkVal], + [:TkCOMMENT, TkVal], + + [:TkDSTRING, TkNode], + [:TkDXSTRING, TkNode], + [:TkDREGEXP, TkNode], + [:TkNTH_REF, TkId], + [:TkBACK_REF, TkId], + + [:TkUPLUS, TkOp, "+@"], + [:TkUMINUS, TkOp, "-@"], + [:TkPOW, TkOp, "**"], + [:TkCMP, TkOp, "<=>"], + [:TkEQ, TkOp, "=="], + [:TkEQQ, TkOp, "==="], + [:TkNEQ, TkOp, "!="], + [:TkGEQ, TkOp, ">="], + [:TkLEQ, TkOp, "<="], + [:TkANDOP, TkOp, "&&"], + [:TkOROP, TkOp, "||"], + [:TkMATCH, TkOp, "=~"], + [:TkNMATCH, TkOp, "!~"], + [:TkDOT2, TkOp, ".."], + [:TkDOT3, TkOp, "..."], + [:TkAREF, TkOp, "[]"], + [:TkASET, TkOp, "[]="], + [:TkLSHFT, TkOp, "<<"], + [:TkRSHFT, TkOp, ">>"], + [:TkCOLON2, TkOp], + [:TkCOLON3, TkOp], +# [:OPASGN, TkOp], # +=, -= etc. # + [:TkASSOC, TkOp, "=>"], + [:TkQUESTION, TkOp, "?"], #? + [:TkCOLON, TkOp, ":"], #: + + [:TkfLPAREN], # func( # + [:TkfLBRACK], # func[ # + [:TkfLBRACE], # func{ # + [:TkSTAR], # *arg + [:TkAMPER], # &arg # + [:TkSYMBOL, TkId], # :SYMBOL + [:TkSYMBEG, TkId], + [:TkGT, TkOp, ">"], + [:TkLT, TkOp, "<"], + [:TkPLUS, TkOp, "+"], + [:TkMINUS, TkOp, "-"], + [:TkMULT, TkOp, "*"], + [:TkDIV, TkOp, "/"], + [:TkMOD, TkOp, "%"], + [:TkBITOR, TkOp, "|"], + [:TkBITXOR, TkOp, "^"], + [:TkBITAND, TkOp, "&"], + [:TkBITNOT, TkOp, "~"], + [:TkNOTOP, TkOp, "!"], + + [:TkBACKQUOTE, TkOp, "`"], + + [:TkASSIGN, Token, "="], + [:TkDOT, Token, "."], + [:TkLPAREN, Token, "("], #(exp) + [:TkLBRACK, Token, "["], #[arry] + [:TkLBRACE, Token, "{"], #{hash} + [:TkRPAREN, Token, ")"], + [:TkRBRACK, Token, "]"], + [:TkRBRACE, Token, "}"], + [:TkCOMMA, Token, ","], + [:TkSEMICOLON, Token, ";"], + + [:TkRD_COMMENT], + [:TkSPACE], + [:TkNL], + [:TkEND_OF_SCRIPT], + + [:TkBACKSLASH, TkUnknownChar, "\\"], + [:TkAT, TkUnknownChar, "@"], + [:TkDOLLAR, TkUnknownChar, "\$"], #" + ] + + # {reading => token_class} + # {reading => [token_class, *opt]} + TkReading2Token = {} + TkSymbol2Token = {} + + def RubyToken.def_token(token_n, super_token = Token, reading = nil, *opts) + token_n = token_n.id2name unless token_n.kind_of?(String) + if RubyToken.const_defined?(token_n) + IRB.fail AlreadyDefinedToken, token_n + end + + token_c = Class.new super_token + RubyToken.const_set token_n, token_c +# token_c.inspect + + if reading + if TkReading2Token[reading] + IRB.fail TkReading2TokenDuplicateError, token_n, reading + end + if opts.empty? + TkReading2Token[reading] = [token_c] + else + TkReading2Token[reading] = [token_c].concat(opts) + end + end + TkSymbol2Token[token_n.intern] = token_c + + if token_c <= TkOp + token_c.class_eval %{ + def self.op_name; "#{reading}"; end + } + end + end + + for defs in TokenDefinitions + def_token(*defs) + end + + NEWLINE_TOKEN = TkNL.new(0,0) + NEWLINE_TOKEN.set_text("\n") + +end + + + +# Lexical analyzer for Ruby source + +class RubyLex + + ###################################################################### + # + # Read an input stream character by character. We allow for unlimited + # ungetting of characters just read. + # + # We simplify the implementation greatly by reading the entire input + # into a buffer initially, and then simply traversing it using + # pointers. + # + # We also have to allow for the here document diversion. This + # little gem comes about when the lexer encounters a here + # document. At this point we effectively need to split the input + # stream into two parts: one to read the body of the here document, + # the other to read the rest of the input line where the here + # document was initially encountered. For example, we might have + # + # do_something(<<-A, <<-B) + # stuff + # for + # A + # stuff + # for + # B + # + # When the lexer encounters the <= @size + ch = @content[@offset, 1] + + @offset += 1 + @hwm = @offset if @hwm < @offset + + if @newline_pending + @line_num += 1 + @last_newline = @offset - 1 + @newline_pending = false + end + + if ch == "\n" + @newline_pending = true + end + ch + end + + def getc_already_read + getc + end + + def ungetc(ch) + raise "unget past beginning of file" if @offset <= 0 + @offset -= 1 + if @content[@offset] == ?\n + @newline_pending = false + end + end + + def get_read + res = @content[@read_back_offset...@offset] + @read_back_offset = @offset + res + end + + def peek(at) + pos = @offset + at + if pos >= @size + nil + else + @content[pos, 1] + end + end + + def peek_equal(str) + @content[@offset, str.length] == str + end + + def divert_read_from(reserve) + @content[@offset, 0] = reserve + @size = @content.size + end + end + + # end of nested class BufferedReader + + extend Exception2MessageMapper + def_exception(:AlreadyDefinedToken, "Already defined token(%s)") + def_exception(:TkReading2TokenNoKey, "key nothing(key='%s')") + def_exception(:TkSymbol2TokenNoKey, "key nothing(key='%s')") + def_exception(:TkReading2TokenDuplicateError, + "key duplicate(token_n='%s', key='%s')") + def_exception(:SyntaxError, "%s") + + include RubyToken + include IRB + + attr_reader :continue + attr_reader :lex_state + + def RubyLex.debug? + false + end + + def initialize(content) + lex_init + + @reader = BufferedReader.new(content) + + @exp_line_no = @line_no = 1 + @base_char_no = 0 + @indent = 0 + + @ltype = nil + @quoted = nil + @lex_state = EXPR_BEG + @space_seen = false + + @continue = false + @line = "" + + @skip_space = false + @read_auto_clean_up = false + @exception_on_syntax_error = true + end + + attr :skip_space, true + attr :read_auto_clean_up, true + attr :exception_on_syntax_error, true + + attr :indent + + # io functions + def line_no + @reader.line_num + end + + def char_no + @reader.column + end + + def get_read + @reader.get_read + end + + def getc + @reader.getc + end + + def getc_of_rests + @reader.getc_already_read + end + + def gets + c = getc or return + l = "" + begin + l.concat c unless c == "\r" + break if c == "\n" + end while c = getc + l + end + + + def ungetc(c = nil) + @reader.ungetc(c) + end + + def peek_equal?(str) + @reader.peek_equal(str) + end + + def peek(i = 0) + @reader.peek(i) + end + + def lex + until (((tk = token).kind_of?(TkNL) || tk.kind_of?(TkEND_OF_SCRIPT)) && + !@continue or + tk.nil?) + end + line = get_read + + if line == "" and tk.kind_of?(TkEND_OF_SCRIPT) || tk.nil? + nil + else + line + end + end + + def token + set_token_position(line_no, char_no) + begin + begin + tk = @OP.match(self) + @space_seen = tk.kind_of?(TkSPACE) + rescue SyntaxError + abort if @exception_on_syntax_error + tk = TkError.new(line_no, char_no) + end + end while @skip_space and tk.kind_of?(TkSPACE) + if @read_auto_clean_up + get_read + end +# throw :eof unless tk + p tk if $DEBUG + tk + end + + ENINDENT_CLAUSE = [ + "case", "class", "def", "do", "for", "if", + "module", "unless", "until", "while", "begin" #, "when" + ] + DEINDENT_CLAUSE = ["end" #, "when" + ] + + PERCENT_LTYPE = { + "q" => "\'", + "Q" => "\"", + "x" => "\`", + "r" => "/", + "w" => "]" + } + + PERCENT_PAREN = { + "{" => "}", + "[" => "]", + "<" => ">", + "(" => ")" + } + + Ltype2Token = { + "\'" => TkSTRING, + "\"" => TkSTRING, + "\`" => TkXSTRING, + "/" => TkREGEXP, + "]" => TkDSTRING + } + Ltype2Token.default = TkSTRING + + DLtype2Token = { + "\"" => TkDSTRING, + "\`" => TkDXSTRING, + "/" => TkDREGEXP, + } + + def lex_init() + @OP = SLex.new + @OP.def_rules("\0", "\004", "\032") do |chars, io| + Token(TkEND_OF_SCRIPT).set_text(chars) + end + + @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |chars, io| + @space_seen = TRUE + while (ch = getc) =~ /[ \t\f\r\13]/ + chars << ch + end + ungetc + Token(TkSPACE).set_text(chars) + end + + @OP.def_rule("#") do + |op, io| + identify_comment + end + + @OP.def_rule("=begin", proc{@prev_char_no == 0 && peek(0) =~ /\s/}) do + |op, io| + str = op + @ltype = "=" + + + begin + line = "" + begin + ch = getc + line << ch + end until ch == "\n" + str << line + end until line =~ /^=end/ + + ungetc + + @ltype = nil + + if str =~ /\A=begin\s+rdoc/i + str.sub!(/\A=begin.*\n/, '') + str.sub!(/^=end.*/m, '') + Token(TkCOMMENT).set_text(str) + else + Token(TkRD_COMMENT)#.set_text(str) + end + end + + @OP.def_rule("\n") do + print "\\n\n" if RubyLex.debug? + case @lex_state + when EXPR_BEG, EXPR_FNAME, EXPR_DOT + @continue = TRUE + else + @continue = FALSE + @lex_state = EXPR_BEG + end + Token(TkNL).set_text("\n") + end + + @OP.def_rules("*", "**", + "!", "!=", "!~", + "=", "==", "===", + "=~", "<=>", + "<", "<=", + ">", ">=", ">>") do + |op, io| + @lex_state = EXPR_BEG + Token(op).set_text(op) + end + + @OP.def_rules("<<") do + |op, io| + tk = nil + if @lex_state != EXPR_END && @lex_state != EXPR_CLASS && + (@lex_state != EXPR_ARG || @space_seen) + c = peek(0) + if /[-\w_\"\'\`]/ =~ c + tk = identify_here_document + end + end + if !tk + @lex_state = EXPR_BEG + tk = Token(op).set_text(op) + end + tk + end + + @OP.def_rules("'", '"') do + |op, io| + identify_string(op) + end + + @OP.def_rules("`") do + |op, io| + if @lex_state == EXPR_FNAME + Token(op).set_text(op) + else + identify_string(op) + end + end + + @OP.def_rules('?') do + |op, io| + if @lex_state == EXPR_END + @lex_state = EXPR_BEG + Token(TkQUESTION).set_text(op) + else + ch = getc + if @lex_state == EXPR_ARG && ch !~ /\s/ + ungetc + @lex_state = EXPR_BEG; + Token(TkQUESTION).set_text(op) + else + str = op + str << ch + if (ch == '\\') #' + str << read_escape + end + @lex_state = EXPR_END + Token(TkINTEGER).set_text(str) + end + end + end + + @OP.def_rules("&", "&&", "|", "||") do + |op, io| + @lex_state = EXPR_BEG + Token(op).set_text(op) + end + + @OP.def_rules("+=", "-=", "*=", "**=", + "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do + |op, io| + @lex_state = EXPR_BEG + op =~ /^(.*)=$/ + Token(TkOPASGN, $1).set_text(op) + end + + @OP.def_rule("+@", proc{@lex_state == EXPR_FNAME}) do |op, io| + Token(TkUPLUS).set_text(op) + end + + @OP.def_rule("-@", proc{@lex_state == EXPR_FNAME}) do |op, io| + Token(TkUMINUS).set_text(op) + end + + @OP.def_rules("+", "-") do + |op, io| + catch(:RET) do + if @lex_state == EXPR_ARG + if @space_seen and peek(0) =~ /[0-9]/ + throw :RET, identify_number(op) + else + @lex_state = EXPR_BEG + end + elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/ + throw :RET, identify_number(op) + else + @lex_state = EXPR_BEG + end + Token(op).set_text(op) + end + end + + @OP.def_rule(".") do + @lex_state = EXPR_BEG + if peek(0) =~ /[0-9]/ + ungetc + identify_number("") + else + # for obj.if + @lex_state = EXPR_DOT + Token(TkDOT).set_text(".") + end + end + + @OP.def_rules("..", "...") do + |op, io| + @lex_state = EXPR_BEG + Token(op).set_text(op) + end + + lex_int2 + end + + def lex_int2 + @OP.def_rules("]", "}", ")") do + |op, io| + @lex_state = EXPR_END + @indent -= 1 + Token(op).set_text(op) + end + + @OP.def_rule(":") do + if @lex_state == EXPR_END || peek(0) =~ /\s/ + @lex_state = EXPR_BEG + tk = Token(TkCOLON) + else + @lex_state = EXPR_FNAME; + tk = Token(TkSYMBEG) + end + tk.set_text(":") + end + + @OP.def_rule("::") do +# p @lex_state.id2name, @space_seen + if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen + @lex_state = EXPR_BEG + tk = Token(TkCOLON3) + else + @lex_state = EXPR_DOT + tk = Token(TkCOLON2) + end + tk.set_text("::") + end + + @OP.def_rule("/") do + |op, io| + if @lex_state == EXPR_BEG || @lex_state == EXPR_MID + identify_string(op) + elsif peek(0) == '=' + getc + @lex_state = EXPR_BEG + Token(TkOPASGN, :/).set_text("/=") #") + elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/ + identify_string(op) + else + @lex_state = EXPR_BEG + Token("/").set_text(op) + end + end + + @OP.def_rules("^") do + @lex_state = EXPR_BEG + Token("^").set_text("^") + end + + # @OP.def_rules("^=") do + # @lex_state = EXPR_BEG + # Token(TkOPASGN, :^) + # end + + @OP.def_rules(",", ";") do + |op, io| + @lex_state = EXPR_BEG + Token(op).set_text(op) + end + + @OP.def_rule("~") do + @lex_state = EXPR_BEG + Token("~").set_text("~") + end + + @OP.def_rule("~@", proc{@lex_state = EXPR_FNAME}) do + @lex_state = EXPR_BEG + Token("~").set_text("~@") + end + + @OP.def_rule("(") do + @indent += 1 + if @lex_state == EXPR_BEG || @lex_state == EXPR_MID + @lex_state = EXPR_BEG + tk = Token(TkfLPAREN) + else + @lex_state = EXPR_BEG + tk = Token(TkLPAREN) + end + tk.set_text("(") + end + + @OP.def_rule("[]", proc{@lex_state == EXPR_FNAME}) do + Token("[]").set_text("[]") + end + + @OP.def_rule("[]=", proc{@lex_state == EXPR_FNAME}) do + Token("[]=").set_text("[]=") + end + + @OP.def_rule("[") do + @indent += 1 + if @lex_state == EXPR_FNAME + t = Token(TkfLBRACK) + else + if @lex_state == EXPR_BEG || @lex_state == EXPR_MID + t = Token(TkLBRACK) + elsif @lex_state == EXPR_ARG && @space_seen + t = Token(TkLBRACK) + else + t = Token(TkfLBRACK) + end + @lex_state = EXPR_BEG + end + t.set_text("[") + end + + @OP.def_rule("{") do + @indent += 1 + if @lex_state != EXPR_END && @lex_state != EXPR_ARG + t = Token(TkLBRACE) + else + t = Token(TkfLBRACE) + end + @lex_state = EXPR_BEG + t.set_text("{") + end + + @OP.def_rule('\\') do #' + if getc == "\n" + @space_seen = true + @continue = true + Token(TkSPACE).set_text("\\\n") + else + ungetc + Token("\\").set_text("\\") #" + end + end + + @OP.def_rule('%') do + |op, io| + if @lex_state == EXPR_BEG || @lex_state == EXPR_MID + identify_quotation('%') + elsif peek(0) == '=' + getc + Token(TkOPASGN, "%").set_text("%=") + elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/ + identify_quotation('%') + else + @lex_state = EXPR_BEG + Token("%").set_text("%") + end + end + + @OP.def_rule('$') do #' + identify_gvar + end + + @OP.def_rule('@') do + if peek(0) =~ /[@\w_]/ + ungetc + identify_identifier + else + Token("@").set_text("@") + end + end + + # @OP.def_rule("def", proc{|op, io| /\s/ =~ io.peek(0)}) do + # |op, io| + # @indent += 1 + # @lex_state = EXPR_FNAME + # # @lex_state = EXPR_END + # # until @rests[0] == "\n" or @rests[0] == ";" + # # rests.shift + # # end + # end + + @OP.def_rule("__END__", proc{@prev_char_no == 0 && peek(0) =~ /[\r\n]/}) do + throw :eof + end + + @OP.def_rule("") do + |op, io| + printf "MATCH: start %s: %s\n", op, io.inspect if RubyLex.debug? + if peek(0) =~ /[0-9]/ + t = identify_number("") + elsif peek(0) =~ /[\w_]/ + t = identify_identifier + end + printf "MATCH: end %s: %s\n", op, io.inspect if RubyLex.debug? + t + end + + p @OP if RubyLex.debug? + end + + def identify_gvar + @lex_state = EXPR_END + str = "$" + + tk = case ch = getc + when /[~_*$?!@\/\\;,=:<>".]/ #" + str << ch + Token(TkGVAR, str) + + when "-" + str << "-" << getc + Token(TkGVAR, str) + + when "&", "`", "'", "+" + str << ch + Token(TkBACK_REF, str) + + when /[1-9]/ + str << ch + while (ch = getc) =~ /[0-9]/ + str << ch + end + ungetc + Token(TkNTH_REF) + when /\w/ + ungetc + ungetc + return identify_identifier + else + ungetc + Token("$") + end + tk.set_text(str) + end + + def identify_identifier + token = "" + token.concat getc if peek(0) =~ /[$@]/ + token.concat getc if peek(0) == "@" + + while (ch = getc) =~ /\w|_/ + print ":", ch, ":" if RubyLex.debug? + token.concat ch + end + ungetc + + if ch == "!" or ch == "?" + token.concat getc + end + # fix token + + # $stderr.puts "identifier - #{token}, state = #@lex_state" + + case token + when /^\$/ + return Token(TkGVAR, token).set_text(token) + when /^\@/ + @lex_state = EXPR_END + return Token(TkIVAR, token).set_text(token) + end + + if @lex_state != EXPR_DOT + print token, "\n" if RubyLex.debug? + + token_c, *trans = TkReading2Token[token] + if token_c + # reserved word? + + if (@lex_state != EXPR_BEG && + @lex_state != EXPR_FNAME && + trans[1]) + # modifiers + token_c = TkSymbol2Token[trans[1]] + @lex_state = trans[0] + else + if @lex_state != EXPR_FNAME + if ENINDENT_CLAUSE.include?(token) + @indent += 1 + elsif DEINDENT_CLAUSE.include?(token) + @indent -= 1 + end + @lex_state = trans[0] + else + @lex_state = EXPR_END + end + end + return Token(token_c, token).set_text(token) + end + end + + if @lex_state == EXPR_FNAME + @lex_state = EXPR_END + if peek(0) == '=' + token.concat getc + end + elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT + @lex_state = EXPR_ARG + else + @lex_state = EXPR_END + end + + if token[0, 1] =~ /[A-Z]/ + return Token(TkCONSTANT, token).set_text(token) + elsif token[token.size - 1, 1] =~ /[!?]/ + return Token(TkFID, token).set_text(token) + else + return Token(TkIDENTIFIER, token).set_text(token) + end + end + + def identify_here_document + ch = getc + if ch == "-" + ch = getc + indent = true + end + if /['"`]/ =~ ch # ' + lt = ch + quoted = "" + while (c = getc) && c != lt + quoted.concat c + end + else + lt = '"' + quoted = ch.dup + while (c = getc) && c =~ /\w/ + quoted.concat c + end + ungetc + end + + ltback, @ltype = @ltype, lt + reserve = "" + + while ch = getc + reserve << ch + if ch == "\\" #" + ch = getc + reserve << ch + elsif ch == "\n" + break + end + end + + str = "" + while (l = gets) + l.chomp! + l.strip! if indent + break if l == quoted + str << l.chomp << "\n" + end + + @reader.divert_read_from(reserve) + + @ltype = ltback + @lex_state = EXPR_END + Token(Ltype2Token[lt], str).set_text(str.dump) + end + + def identify_quotation(initial_char) + ch = getc + if lt = PERCENT_LTYPE[ch] + initial_char += ch + ch = getc + elsif ch =~ /\W/ + lt = "\"" + else + RubyLex.fail SyntaxError, "unknown type of %string ('#{ch}')" + end +# if ch !~ /\W/ +# ungetc +# next +# end + #@ltype = lt + @quoted = ch unless @quoted = PERCENT_PAREN[ch] + identify_string(lt, @quoted, ch, initial_char) + end + + def identify_number(start) + str = start.dup + + if start == "+" or start == "-" or start == "" + start = getc + str << start + end + + @lex_state = EXPR_END + + if start == "0" + if peek(0) == "x" + ch = getc + str << ch + match = /[0-9a-f_]/ + else + match = /[0-7_]/ + end + while ch = getc + if ch !~ match + ungetc + break + else + str << ch + end + end + return Token(TkINTEGER).set_text(str) + end + + type = TkINTEGER + allow_point = TRUE + allow_e = TRUE + while ch = getc + case ch + when /[0-9_]/ + str << ch + + when allow_point && "." + type = TkFLOAT + if peek(0) !~ /[0-9]/ + ungetc + break + end + str << ch + allow_point = false + + when allow_e && "e", allow_e && "E" + str << ch + type = TkFLOAT + if peek(0) =~ /[+-]/ + str << getc + end + allow_e = false + allow_point = false + else + ungetc + break + end + end + Token(type).set_text(str) + end + + def identify_string(ltype, quoted = ltype, opener=nil, initial_char = nil) + @ltype = ltype + @quoted = quoted + subtype = nil + + str = "" + str << initial_char if initial_char + str << (opener||quoted) + + nest = 0 + begin + while ch = getc + str << ch + if @quoted == ch + if nest == 0 + break + else + nest -= 1 + end + elsif opener == ch + nest += 1 + elsif @ltype != "'" && @ltype != "]" and ch == "#" + ch = getc + if ch == "{" + subtype = true + str << ch << skip_inner_expression + else + ungetc(ch) + end + elsif ch == '\\' #' + str << read_escape + end + end + if @ltype == "/" + if peek(0) =~ /i|o|n|e|s/ + str << getc + end + end + if subtype + Token(DLtype2Token[ltype], str) + else + Token(Ltype2Token[ltype], str) + end.set_text(str) + ensure + @ltype = nil + @quoted = nil + @lex_state = EXPR_END + end + end + + def skip_inner_expression + res = "" + nest = 0 + while (ch = getc) + res << ch + if ch == '}' + break if nest.zero? + nest -= 1 + elsif ch == '{' + nest += 1 + end + end + res + end + + def identify_comment + @ltype = "#" + comment = "#" + while ch = getc + if ch == "\\" + ch = getc + if ch == "\n" + ch = " " + else + comment << "\\" + end + else + if ch == "\n" + @ltype = nil + ungetc + break + end + end + comment << ch + end + return Token(TkCOMMENT).set_text(comment) + end + + def read_escape + res = "" + case ch = getc + when /[0-7]/ + ungetc ch + 3.times do + case ch = getc + when /[0-7]/ + when nil + break + else + ungetc + break + end + res << ch + end + + when "x" + res << ch + 2.times do + case ch = getc + when /[0-9a-fA-F]/ + when nil + break + else + ungetc + break + end + res << ch + end + + when "M" + res << ch + if (ch = getc) != '-' + ungetc + else + res << ch + if (ch = getc) == "\\" #" + res << ch + res << read_escape + else + res << ch + end + end + + when "C", "c" #, "^" + res << ch + if ch == "C" and (ch = getc) != "-" + ungetc + else + res << ch + if (ch = getc) == "\\" #" + res << ch + res << read_escape + else + res << ch + end + end + else + res << ch + end + res + end +end + + + +# Extract code elements from a source file, returning a TopLevel +# object containing the constituent file elements. +# +# This file is based on rtags + +module RDoc + + GENERAL_MODIFIERS = [ 'nodoc' ].freeze + + CLASS_MODIFIERS = GENERAL_MODIFIERS + + ATTR_MODIFIERS = GENERAL_MODIFIERS + + CONSTANT_MODIFIERS = GENERAL_MODIFIERS + + METHOD_MODIFIERS = GENERAL_MODIFIERS + + [ 'arg', 'args', 'yield', 'yields', 'notnew', 'not-new', 'not_new', 'doc' ] + + + class RubyParser + include RubyToken + include TokenStream + + extend ParserFactory + + parse_files_matching(/\.rbw?$/) + + + def initialize(top_level, file_name, content, options, stats) + @options = options + @stats = stats + @size = 0 + @token_listeners = nil + @input_file_name = file_name + @scanner = RubyLex.new(content) + @scanner.exception_on_syntax_error = false + @top_level = top_level + @progress = $stderr unless options.quiet + end + + def scan + @tokens = [] + @unget_read = [] + @read = [] + catch(:eof) do + catch(:enddoc) do + begin + parse_toplevel_statements(@top_level) + rescue Exception => e + $stderr.puts "\n\n" + $stderr.puts "RDoc failure in #@input_file_name at or around " + + "line #{@scanner.line_no} column #{@scanner.char_no}" + $stderr.puts + $stderr.puts "Before reporting this, could you check that the file" + $stderr.puts "you're documenting compiles cleanly--RDoc is not a" + $stderr.puts "full Ruby parser, and gets confused easily if fed" + $stderr.puts "invalid programs." + $stderr.puts + $stderr.puts "The internal error was:\n\n" + + e.set_backtrace(e.backtrace[0,4]) + raise + end + end + end + @top_level + end + + private + + def make_message(msg) + prefix = "\n" + @input_file_name + ":" + if @scanner + prefix << "#{@scanner.line_no}:#{@scanner.char_no}: " + end + return prefix + msg + end + + def warn(msg) + return if @options.quiet + msg = make_message msg + $stderr.puts msg + end + + def error(msg) + msg = make_message msg + $stderr.puts msg + exit(1) + end + + def progress(char) + unless @options.quiet + @progress.print(char) + @progress.flush + end + end + + def add_token_listener(obj) + @token_listeners ||= [] + @token_listeners << obj + end + + def remove_token_listener(obj) + @token_listeners.delete(obj) + end + + def get_tk + tk = nil + if @tokens.empty? + tk = @scanner.token + @read.push @scanner.get_read + puts "get_tk1 => #{tk.inspect}" if $TOKEN_DEBUG + else + @read.push @unget_read.shift + tk = @tokens.shift + puts "get_tk2 => #{tk.inspect}" if $TOKEN_DEBUG + end + + if tk.kind_of?(TkSYMBEG) + set_token_position(tk.line_no, tk.char_no) + tk1 = get_tk + if tk1.kind_of?(TkId) || tk1.kind_of?(TkOp) + tk = Token(TkSYMBOL).set_text(":" + tk1.name) + # remove the identifier we just read (we're about to + # replace it with a symbol) + @token_listeners.each do |obj| + obj.pop_token + end if @token_listeners + else + warn("':' not followed by identifier or operator") + tk = tk1 + end + end + + # inform any listeners of our shiny new token + @token_listeners.each do |obj| + obj.add_token(tk) + end if @token_listeners + + tk + end + + def peek_tk + unget_tk(tk = get_tk) + tk + end + + def unget_tk(tk) + @tokens.unshift tk + @unget_read.unshift @read.pop + + # Remove this token from any listeners + @token_listeners.each do |obj| + obj.pop_token + end if @token_listeners + end + + def skip_tkspace(skip_nl = true) + tokens = [] + while ((tk = get_tk).kind_of?(TkSPACE) || + (skip_nl && tk.kind_of?(TkNL))) + tokens.push tk + end + unget_tk(tk) + tokens + end + + def get_tkread + read = @read.join("") + @read = [] + read + end + + def peek_read + @read.join('') + end + + NORMAL = "::" + SINGLE = "<<" + + # Look for the first comment in a file that isn't + # a shebang line. + + def collect_first_comment + skip_tkspace + res = '' + first_line = true + + tk = get_tk + while tk.kind_of?(TkCOMMENT) + if first_line && tk.text[0,2] == "#!" + skip_tkspace + tk = get_tk + else + res << tk.text << "\n" + tk = get_tk + if tk.kind_of? TkNL + skip_tkspace(false) + tk = get_tk + end + end + first_line = false + end + unget_tk(tk) + res + end + + def parse_toplevel_statements(container) + comment = collect_first_comment + look_for_directives_in(container, comment) + container.comment = comment unless comment.empty? + parse_statements(container, NORMAL, nil, comment) + end + + def parse_statements(container, single=NORMAL, current_method=nil, comment='') + nest = 1 + save_visibility = container.visibility + +# if container.kind_of?(TopLevel) +# else +# comment = '' +# end + + non_comment_seen = true + + while tk = get_tk + + keep_comment = false + + non_comment_seen = true unless tk.kind_of?(TkCOMMENT) + + case tk + + when TkNL + skip_tkspace(true) # Skip blanks and newlines + tk = get_tk + if tk.kind_of?(TkCOMMENT) + if non_comment_seen + comment = '' + non_comment_seen = false + end + while tk.kind_of?(TkCOMMENT) + comment << tk.text << "\n" + tk = get_tk # this is the newline + skip_tkspace(false) # leading spaces + tk = get_tk + end + unless comment.empty? + look_for_directives_in(container, comment) + if container.done_documenting + container.ongoing_visibility = save_visibility +# return + end + end + keep_comment = true + else + non_comment_seen = true + end + unget_tk(tk) + keep_comment = true + + + when TkCLASS + if container.document_children + parse_class(container, single, tk, comment) + else + nest += 1 + end + + when TkMODULE + if container.document_children + parse_module(container, single, tk, comment) + else + nest += 1 + end + + when TkDEF + if container.document_self + parse_method(container, single, tk, comment) + else + nest += 1 + end + + when TkCONSTANT + if container.document_self + parse_constant(container, single, tk, comment) + end + + when TkALIAS + if container.document_self + parse_alias(container, single, tk, comment) + end + + when TkYIELD + if current_method.nil? + warn("Warning: yield outside of method") if container.document_self + else + parse_yield(container, single, tk, current_method) + end + + # Until and While can have a 'do', which shouldn't increas + # the nesting. We can't solve the general case, but we can + # handle most occurrences by ignoring a do at the end of a line + + when TkUNTIL, TkWHILE + nest += 1 + puts "FOUND #{tk.class} in #{container.name}, nest = #{nest}, " + + "line #{tk.line_no}" if $DEBUG + skip_optional_do_after_expression + + # 'for' is trickier + when TkFOR + nest += 1 + puts "FOUND #{tk.class} in #{container.name}, nest = #{nest}, " + + "line #{tk.line_no}" if $DEBUG + skip_for_variable + skip_optional_do_after_expression + + when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN + nest += 1 + puts "Found #{tk.class} in #{container.name}, nest = #{nest}, " + + "line #{tk.line_no}" if $DEBUG + + when TkIDENTIFIER + if nest == 1 and current_method.nil? + case tk.name + when "private", "protected", "public", + "private_class_method", "public_class_method" + parse_visibility(container, single, tk) + keep_comment = true + when "attr" + parse_attr(container, single, tk, comment) + when /^attr_(reader|writer|accessor)$/, @options.extra_accessors + parse_attr_accessor(container, single, tk, comment) + when "alias_method" + if container.document_self + parse_alias(container, single, tk, comment) + end + end + end + + case tk.name + when "require" + parse_require(container, comment) + when "include" + parse_include(container, comment) + end + + + when TkEND + nest -= 1 + puts "Found 'end' in #{container.name}, nest = #{nest}, line #{tk.line_no}" if $DEBUG + puts "Method = #{current_method.name}" if $DEBUG and current_method + if nest == 0 + read_documentation_modifiers(container, CLASS_MODIFIERS) + container.ongoing_visibility = save_visibility + return + end + + end + + comment = '' unless keep_comment + begin + get_tkread + skip_tkspace(false) + end while peek_tk == TkNL + + end + end + + def parse_class(container, single, tk, comment, &block) + progress("c") + + @stats.num_classes += 1 + + container, name_t = get_class_or_module(container) + + case name_t + when TkCONSTANT + name = name_t.name + superclass = "Object" + + if peek_tk.kind_of?(TkLT) + get_tk + skip_tkspace(true) + superclass = get_class_specification + superclass = "" if superclass.empty? + end + + if single == SINGLE + cls_type = SingleClass + else + cls_type = NormalClass + end + + cls = container.add_class(cls_type, name, superclass) + read_documentation_modifiers(cls, CLASS_MODIFIERS) + cls.record_location(@top_level) + parse_statements(cls) + cls.comment = comment + + when TkLSHFT + case name = get_class_specification + when "self", container.name + parse_statements(container, SINGLE, &block) + else + other = TopLevel.find_class_named(name) + unless other +# other = @top_level.add_class(NormalClass, name, nil) +# other.record_location(@top_level) +# other.comment = comment + other = NormalClass.new("Dummy", nil) + end + read_documentation_modifiers(other, CLASS_MODIFIERS) + parse_statements(other, SINGLE, &block) + end + + else + warn("Expected class name or '<<'. Got #{name_t.class}: #{name_t.text.inspect}") + end + end + + def parse_module(container, single, tk, comment) + progress("m") + @stats.num_modules += 1 + container, name_t = get_class_or_module(container) +# skip_tkspace + name = name_t.name + mod = container.add_module(NormalModule, name) + mod.record_location(@top_level) + read_documentation_modifiers(mod, CLASS_MODIFIERS) + parse_statements(mod) + mod.comment = comment + end + + # Look for the name of a class of module (optionally with a leading :: or + # with :: separated named) and return the ultimate name and container + + def get_class_or_module(container) + skip_tkspace + name_t = get_tk + + # class ::A -> A is in the top level + if name_t.kind_of?(TkCOLON2) + name_t = get_tk + container = @top_level + end + + skip_tkspace(false) + + while peek_tk.kind_of?(TkCOLON2) + prev_container = container + container = container.find_module_named(name_t.name) + if !container +# warn("Couldn't find module #{name_t.name}") + container = prev_container.add_module(NormalModule, name_t.name) + end + get_tk + name_t = get_tk + end + skip_tkspace(false) + return [container, name_t] + end + + def parse_constant(container, single, tk, comment) + name = tk.name + skip_tkspace(false) + eq_tk = get_tk + + unless eq_tk.kind_of?(TkASSIGN) + unget_tk(eq_tk) + return + end + + + nest = 0 + get_tkread + + tk = get_tk + if tk.kind_of? TkGT + unget_tk(tk) + unget_tk(eq_tk) + return + end + + loop do + puts("Param: #{tk}, #{@scanner.continue} " + + "#{@scanner.lex_state} #{nest}") if $DEBUG + + case tk + when TkSEMICOLON + break + when TkLPAREN, TkfLPAREN + nest += 1 + when TkRPAREN + nest -= 1 + when TkCOMMENT + if nest <= 0 && @scanner.lex_state == EXPR_END + unget_tk(tk) + break + end + when TkNL + if (@scanner.lex_state == EXPR_END and nest <= 0) || !@scanner.continue + unget_tk(tk) + break + end + end + tk = get_tk + end + + res = get_tkread.tr("\n", " ").strip + res = "" if res == ";" + con = Constant.new(name, res, comment) + read_documentation_modifiers(con, CONSTANT_MODIFIERS) + if con.document_self + container.add_constant(con) + end + end + + def parse_method(container, single, tk, comment) + progress(".") + @stats.num_methods += 1 + line_no = tk.line_no + column = tk.char_no + + start_collecting_tokens + add_token(tk) + add_token_listener(self) + + @scanner.instance_eval{@lex_state = EXPR_FNAME} + skip_tkspace(false) + name_t = get_tk + back_tk = skip_tkspace + meth = nil + added_container = false + + dot = get_tk + if dot.kind_of?(TkDOT) or dot.kind_of?(TkCOLON2) + @scanner.instance_eval{@lex_state = EXPR_FNAME} + skip_tkspace + name_t2 = get_tk + case name_t + when TkSELF + name = name_t2.name + when TkCONSTANT + name = name_t2.name + prev_container = container + container = container.find_module_named(name_t.name) + if !container + added_container = true + obj = name_t.name.split("::").inject(Object) do |state, item| + state.const_get(item) + end rescue nil + + type = obj.class == Class ? NormalClass : NormalModule + if not [Class, Module].include?(obj.class) + warn("Couldn't find #{name_t.name}. Assuming it's a module") + end + + if type == NormalClass then + container = prev_container.add_class(type, name_t.name, obj.superclass.name) + else + container = prev_container.add_module(type, name_t.name) + end + end + else + # warn("Unexpected token '#{name_t2.inspect}'") + # break + skip_method(container) + return + end + meth = AnyMethod.new(get_tkread, name) + meth.singleton = true + else + unget_tk dot + back_tk.reverse_each do + |tk| + unget_tk tk + end + name = name_t.name + + meth = AnyMethod.new(get_tkread, name) + meth.singleton = (single == SINGLE) + end + + remove_token_listener(self) + + meth.start_collecting_tokens + indent = TkSPACE.new(1,1) + indent.set_text(" " * column) + + meth.add_tokens([TkCOMMENT.new(line_no, + 1, + "# File #{@top_level.file_absolute_name}, line #{line_no}"), + NEWLINE_TOKEN, + indent]) + + meth.add_tokens(@token_stream) + + add_token_listener(meth) + + @scanner.instance_eval{@continue = false} + parse_method_parameters(meth) + + if meth.document_self + container.add_method(meth) + elsif added_container + container.document_self = false + end + + # Having now read the method parameters and documentation modifiers, we + # now know whether we have to rename #initialize to ::new + + if name == "initialize" && !meth.singleton + if meth.dont_rename_initialize + meth.visibility = :protected + else + meth.singleton = true + meth.name = "new" + meth.visibility = :public + end + end + + parse_statements(container, single, meth) + + remove_token_listener(meth) + + # Look for a 'call-seq' in the comment, and override the + # normal parameter stuff + + if comment.sub!(/:?call-seq:(.*?)^\s*\#?\s*$/m, '') + seq = $1 + seq.gsub!(/^\s*\#\s*/, '') + meth.call_seq = seq + end + + meth.comment = comment + + end + + def skip_method(container) + meth = AnyMethod.new("", "anon") + parse_method_parameters(meth) + parse_statements(container, false, meth) + end + + # Capture the method's parameters. Along the way, + # look for a comment containing + # + # # yields: .... + # + # and add this as the block_params for the method + + def parse_method_parameters(method) + res = parse_method_or_yield_parameters(method) + res = "(" + res + ")" unless res[0] == ?( + method.params = res unless method.params + if method.block_params.nil? + skip_tkspace(false) + read_documentation_modifiers(method, METHOD_MODIFIERS) + end + end + + def parse_method_or_yield_parameters(method=nil, modifiers=METHOD_MODIFIERS) + skip_tkspace(false) + tk = get_tk + + # Little hack going on here. In the statement + # f = 2*(1+yield) + # We see the RPAREN as the next token, so we need + # to exit early. This still won't catch all cases + # (such as "a = yield + 1" + end_token = case tk + when TkLPAREN, TkfLPAREN + TkRPAREN + when TkRPAREN + return "" + else + TkNL + end + nest = 0 + + loop do + puts("Param: #{tk.inspect}, #{@scanner.continue} " + + "#{@scanner.lex_state} #{nest}") if $DEBUG + case tk + when TkSEMICOLON + break + when TkLBRACE + nest += 1 + when TkRBRACE + # we might have a.each {|i| yield i } + unget_tk(tk) if nest.zero? + nest -= 1 + break if nest <= 0 + when TkLPAREN, TkfLPAREN + nest += 1 + when end_token + if end_token == TkRPAREN + nest -= 1 + break if @scanner.lex_state == EXPR_END and nest <= 0 + else + break unless @scanner.continue + end + when method && method.block_params.nil? && TkCOMMENT + unget_tk(tk) + read_documentation_modifiers(method, modifiers) + end + tk = get_tk + end + res = get_tkread.tr("\n", " ").strip + res = "" if res == ";" + res + end + + # skip the var [in] part of a 'for' statement + def skip_for_variable + skip_tkspace(false) + tk = get_tk + skip_tkspace(false) + tk = get_tk + unget_tk(tk) unless tk.kind_of?(TkIN) + end + + # while, until, and for have an optional + def skip_optional_do_after_expression + skip_tkspace(false) + tk = get_tk + case tk + when TkLPAREN, TkfLPAREN + end_token = TkRPAREN + else + end_token = TkNL + end + + nest = 0 + @scanner.instance_eval{@continue = false} + + loop do + puts("\nWhile: #{tk}, #{@scanner.continue} " + + "#{@scanner.lex_state} #{nest}") if $DEBUG + case tk + when TkSEMICOLON + break + when TkLPAREN, TkfLPAREN + nest += 1 + when TkDO + break if nest.zero? + when end_token + if end_token == TkRPAREN + nest -= 1 + break if @scanner.lex_state == EXPR_END and nest.zero? + else + break unless @scanner.continue + end + end + tk = get_tk + end + skip_tkspace(false) + if peek_tk.kind_of? TkDO + get_tk + end + end + + # Return a superclass, which can be either a constant + # of an expression + + def get_class_specification + tk = get_tk + return "self" if tk.kind_of?(TkSELF) + + res = "" + while tk.kind_of?(TkCOLON2) || + tk.kind_of?(TkCOLON3) || + tk.kind_of?(TkCONSTANT) + + res += tk.text + tk = get_tk + end + + unget_tk(tk) + skip_tkspace(false) + + get_tkread # empty out read buffer + + tk = get_tk + + case tk + when TkNL, TkCOMMENT, TkSEMICOLON + unget_tk(tk) + return res + end + + res += parse_call_parameters(tk) + res + end + + def parse_call_parameters(tk) + + end_token = case tk + when TkLPAREN, TkfLPAREN + TkRPAREN + when TkRPAREN + return "" + else + TkNL + end + nest = 0 + + loop do + puts("Call param: #{tk}, #{@scanner.continue} " + + "#{@scanner.lex_state} #{nest}") if $DEBUG + case tk + when TkSEMICOLON + break + when TkLPAREN, TkfLPAREN + nest += 1 + when end_token + if end_token == TkRPAREN + nest -= 1 + break if @scanner.lex_state == EXPR_END and nest <= 0 + else + break unless @scanner.continue + end + when TkCOMMENT + unget_tk(tk) + break + end + tk = get_tk + end + res = get_tkread.tr("\n", " ").strip + res = "" if res == ";" + res + end + + + # Parse a constant, which might be qualified by + # one or more class or module names + + def get_constant + res = "" + skip_tkspace(false) + tk = get_tk + + while tk.kind_of?(TkCOLON2) || + tk.kind_of?(TkCOLON3) || + tk.kind_of?(TkCONSTANT) + + res += tk.text + tk = get_tk + end + +# if res.empty? +# warn("Unexpected token #{tk} in constant") +# end + unget_tk(tk) + res + end + + # Get a constant that may be surrounded by parens + + def get_constant_with_optional_parens + skip_tkspace(false) + nest = 0 + while (tk = peek_tk).kind_of?(TkLPAREN) || tk.kind_of?(TkfLPAREN) + get_tk + skip_tkspace(true) + nest += 1 + end + + name = get_constant + + while nest > 0 + skip_tkspace(true) + tk = get_tk + nest -= 1 if tk.kind_of?(TkRPAREN) + end + name + end + + # Directives are modifier comments that can appear after class, module, + # or method names. For example + # + # def fred # :yields: a, b + # + # or + # + # class SM # :nodoc: + # + # we return the directive name and any parameters as a two element array + + def read_directive(allowed) + tk = get_tk + puts "directive: #{tk.inspect}" if $DEBUG + result = nil + if tk.kind_of?(TkCOMMENT) + if tk.text =~ /\s*:?(\w+):\s*(.*)/ + directive = $1.downcase + if allowed.include?(directive) + result = [directive, $2] + end + end + else + unget_tk(tk) + end + result + end + + + def read_documentation_modifiers(context, allow) + dir = read_directive(allow) + + case dir[0] + + when "notnew", "not_new", "not-new" + context.dont_rename_initialize = true + + when "nodoc" + context.document_self = false + if dir[1].downcase == "all" + context.document_children = false + end + + when "doc" + context.document_self = true + context.force_documentation = true + + when "yield", "yields" + unless context.params.nil? + context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc + end + context.block_params = dir[1] + + when "arg", "args" + context.params = dir[1] + end if dir + end + + + # Look for directives in a normal comment block: + # + # #-- - don't display comment from this point forward + # + # + # This routine modifies it's parameter + + def look_for_directives_in(context, comment) + + preprocess = SM::PreProcess.new(@input_file_name, + @options.rdoc_include) + + preprocess.handle(comment) do |directive, param| + case directive + when "stopdoc" + context.stop_doc + "" + when "startdoc" + context.start_doc + context.force_documentation = true + "" + + when "enddoc" + #context.done_documenting = true + #"" + throw :enddoc + + when "main" + options = Options.instance + options.main_page = param + "" + + when "title" + options = Options.instance + options.title = param + "" + + when "section" + context.set_current_section(param, comment) + comment.replace("") # 1.8 doesn't support #clear + break + else + warn "Unrecognized directive '#{directive}'" + break + end + end + + remove_private_comments(comment) + end + + def remove_private_comments(comment) + comment.gsub!(/^#--.*?^#\+\+/m, '') + comment.sub!(/^#--.*/m, '') + end + + + + def get_symbol_or_name + tk = get_tk + case tk + when TkSYMBOL + tk.text.sub(/^:/, '') + when TkId, TkOp + tk.name + when TkSTRING + tk.text + else + raise "Name or symbol expected (got #{tk})" + end + end + + def parse_alias(context, single, tk, comment) + skip_tkspace + if (peek_tk.kind_of? TkLPAREN) + get_tk + skip_tkspace + end + new_name = get_symbol_or_name + @scanner.instance_eval{@lex_state = EXPR_FNAME} + skip_tkspace + if (peek_tk.kind_of? TkCOMMA) + get_tk + skip_tkspace + end + old_name = get_symbol_or_name + + al = Alias.new(get_tkread, old_name, new_name, comment) + read_documentation_modifiers(al, ATTR_MODIFIERS) + if al.document_self + context.add_alias(al) + end + end + + def parse_yield_parameters + parse_method_or_yield_parameters + end + + def parse_yield(context, single, tk, method) + if method.block_params.nil? + get_tkread + @scanner.instance_eval{@continue = false} + method.block_params = parse_yield_parameters + end + end + + def parse_require(context, comment) + skip_tkspace_comment + tk = get_tk + if tk.kind_of? TkLPAREN + skip_tkspace_comment + tk = get_tk + end + + name = nil + case tk + when TkSTRING + name = tk.text +# when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR +# name = tk.name + when TkDSTRING + warn "Skipping require of dynamic string: #{tk.text}" + # else + # warn "'require' used as variable" + end + if name + context.add_require(Require.new(name, comment)) + else + unget_tk(tk) + end + end + + def parse_include(context, comment) + loop do + skip_tkspace_comment + name = get_constant_with_optional_parens + unless name.empty? + context.add_include(Include.new(name, comment)) + end + return unless peek_tk.kind_of?(TkCOMMA) + get_tk + end + end + + def get_bool + skip_tkspace + tk = get_tk + case tk + when TkTRUE + true + when TkFALSE, TkNIL + false + else + unget_tk tk + true + end + end + + def parse_attr(context, single, tk, comment) + args = parse_symbol_arg(1) + if args.size > 0 + name = args[0] + rw = "R" + skip_tkspace(false) + tk = get_tk + if tk.kind_of? TkCOMMA + rw = "RW" if get_bool + else + unget_tk tk + end + att = Attr.new(get_tkread, name, rw, comment) + read_documentation_modifiers(att, ATTR_MODIFIERS) + if att.document_self + context.add_attribute(att) + end + else + warn("'attr' ignored - looks like a variable") + end + + end + + def parse_visibility(container, single, tk) + singleton = (single == SINGLE) + vis = case tk.name + when "private" then :private + when "protected" then :protected + when "public" then :public + when "private_class_method" + singleton = true + :private + when "public_class_method" + singleton = true + :public + else raise "Invalid visibility: #{tk.name}" + end + + skip_tkspace_comment(false) + case peek_tk + # Ryan Davis suggested the extension to ignore modifiers, because he + # often writes + # + # protected unless $TESTING + # + when TkNL, TkUNLESS_MOD, TkIF_MOD +# error("Missing argument") if singleton + container.ongoing_visibility = vis + else + args = parse_symbol_arg + container.set_visibility_for(args, vis, singleton) + end + end + + def parse_attr_accessor(context, single, tk, comment) + args = parse_symbol_arg + read = get_tkread + rw = "?" + + # If nodoc is given, don't document any of them + + tmp = CodeObject.new + read_documentation_modifiers(tmp, ATTR_MODIFIERS) + return unless tmp.document_self + + case tk.name + when "attr_reader" then rw = "R" + when "attr_writer" then rw = "W" + when "attr_accessor" then rw = "RW" + else + rw = @options.extra_accessor_flags[tk.name] + end + + for name in args + att = Attr.new(get_tkread, name, rw, comment) + context.add_attribute(att) + end + end + + def skip_tkspace_comment(skip_nl = true) + loop do + skip_tkspace(skip_nl) + return unless peek_tk.kind_of? TkCOMMENT + get_tk + end + end + + def parse_symbol_arg(no = nil) + + args = [] + skip_tkspace_comment + case tk = get_tk + when TkLPAREN + loop do + skip_tkspace_comment + if tk1 = parse_symbol_in_arg + args.push tk1 + break if no and args.size >= no + end + + skip_tkspace_comment + case tk2 = get_tk + when TkRPAREN + break + when TkCOMMA + else + warn("unexpected token: '#{tk2.inspect}'") if $DEBUG + break + end + end + else + unget_tk tk + if tk = parse_symbol_in_arg + args.push tk + return args if no and args.size >= no + end + + loop do +# skip_tkspace_comment(false) + skip_tkspace(false) + + tk1 = get_tk + unless tk1.kind_of?(TkCOMMA) + unget_tk tk1 + break + end + + skip_tkspace_comment + if tk = parse_symbol_in_arg + args.push tk + break if no and args.size >= no + end + end + end + args + end + + def parse_symbol_in_arg + case tk = get_tk + when TkSYMBOL + tk.text.sub(/^:/, '') + when TkSTRING + eval @read[-1] + else + warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG + nil + end + end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parse_simple.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parse_simple.rb new file mode 100644 index 0000000000..3f1a546964 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parse_simple.rb @@ -0,0 +1,41 @@ +# Parse a non-source file. We basically take the whole thing +# as one big comment. If the first character in the file +# is '#', we strip leading pound signs. + + +require "rdoc/code_objects" +require "rdoc/markup/simple_markup/preprocess" + +module RDoc + # See rdoc/parsers/parse_c.rb + + class SimpleParser + + # prepare to parse a plain file + def initialize(top_level, file_name, body, options, stats) + + preprocess = SM::PreProcess.new(file_name, options.rdoc_include) + + preprocess.handle(body) do |directive, param| + $stderr.puts "Unrecognized directive '#{directive}' in #{file_name}" + end + + @body = body + @options = options + @top_level = top_level + end + + # Extract the file contents and attach them to the toplevel as a + # comment + + def scan + # @body.gsub(/^(\s\n)+/, '') + @top_level.comment = remove_private_comments(@body) + @top_level + end + + def remove_private_comments(comment) + comment.gsub(/^--.*?^\+\+/m, '').sub(/^--.*/m, '') + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parserfactory.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parserfactory.rb new file mode 100644 index 0000000000..00a82cf4b1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/parsers/parserfactory.rb @@ -0,0 +1,99 @@ +require "rdoc/parsers/parse_simple" + +module RDoc + + # A parser is simple a class that implements + # + # #initialize(file_name, body, options) + # + # and + # + # #scan + # + # The initialize method takes a file name to be used, the body of the + # file, and an RDoc::Options object. The scan method is then called + # to return an appropriately parsed TopLevel code object. + # + # The ParseFactory is used to redirect to the correct parser given a filename + # extension. This magic works because individual parsers have to register + # themselves with us as they are loaded in. The do this using the following + # incantation + # + # + # require "rdoc/parsers/parsefactory" + # + # module RDoc + # + # class XyzParser + # extend ParseFactory <<<< + # parse_files_matching /\.xyz$/ <<<< + # + # def initialize(file_name, body, options) + # ... + # end + # + # def scan + # ... + # end + # end + # end + # + # Just to make life interesting, if we suspect a plain text file, we + # also look for a shebang line just in case it's a potential + # shell script + + + + module ParserFactory + + @@parsers = [] + + Parsers = Struct.new(:regexp, :parser) + + # Record the fact that a particular class parses files that + # match a given extension + + def parse_files_matching(regexp) + @@parsers.unshift Parsers.new(regexp, self) + end + + # Return a parser that can handle a particular extension + + def ParserFactory.can_parse(file_name) + @@parsers.find {|p| p.regexp.match(file_name) } + end + + # Alias an extension to another extension. After this call, + # files ending "new_ext" will be parsed using the same parser + # as "old_ext" + + def ParserFactory.alias_extension(old_ext, new_ext) + parser = ParserFactory.can_parse("xxx.#{old_ext}") + return false unless parser + @@parsers.unshift Parsers.new(Regexp.new("\\.#{new_ext}$"), parser.parser) + true + end + + # Find the correct parser for a particular file name. Return a + # SimpleParser for ones that we don't know + + def ParserFactory.parser_for(top_level, file_name, body, options, stats) + # If no extension, look for shebang + if file_name !~ /\.\w+$/ && body =~ %r{\A#!(.+)} + shebang = $1 + case shebang + when %r{env\s+ruby}, %r{/ruby} + file_name = "dummy.rb" + end + end + parser_description = can_parse(file_name) + if parser_description + parser = parser_description.parser + else + parser = SimpleParser + end + + parser.new(top_level, file_name, body, options, stats) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/rdoc.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/rdoc.rb new file mode 100644 index 0000000000..91f5611196 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/rdoc.rb @@ -0,0 +1,298 @@ +# See README. +# + + +VERSION_STRING = %{RDoc V1.0.1 - 20041108} + + +require 'rdoc/parsers/parse_rb.rb' +require 'rdoc/parsers/parse_c.rb' +require 'rdoc/parsers/parse_f95.rb' + +require 'rdoc/parsers/parse_simple.rb' +require 'rdoc/options' + +require 'rdoc/diagram' + +require 'find' +require 'ftools' +require 'time' + +# We put rdoc stuff in the RDoc module to avoid namespace +# clutter. +# +# ToDo: This isn't universally true. +# +# :include: README + +module RDoc + + # Name of the dotfile that contains the description of files to be + # processed in the current directory + DOT_DOC_FILENAME = ".document" + + # Simple stats collector + class Stats + attr_accessor :num_files, :num_classes, :num_modules, :num_methods + def initialize + @num_files = @num_classes = @num_modules = @num_methods = 0 + @start = Time.now + end + def print + puts "Files: #@num_files" + puts "Classes: #@num_classes" + puts "Modules: #@num_modules" + puts "Methods: #@num_methods" + puts "Elapsed: " + sprintf("%0.3fs", Time.now - @start) + end + end + + + # Exception thrown by any rdoc error. Only the #message part is + # of use externally. + + class RDocError < Exception + end + + # Encapsulate the production of rdoc documentation. Basically + # you can use this as you would invoke rdoc from the command + # line: + # + # rdoc = RDoc::RDoc.new + # rdoc.document(args) + # + # where _args_ is an array of strings, each corresponding to + # an argument you'd give rdoc on the command line. See rdoc/rdoc.rb + # for details. + + class RDoc + + ## + # This is the list of output generators that we + # support + + Generator = Struct.new(:file_name, :class_name, :key) + + GENERATORS = {} + $:.collect {|d| + File::expand_path(d) + }.find_all {|d| + File::directory?("#{d}/rdoc/generators") + }.each {|dir| + Dir::entries("#{dir}/rdoc/generators").each {|gen| + next unless /(\w+)_generator.rb$/ =~ gen + type = $1 + unless GENERATORS.has_key? type + GENERATORS[type] = Generator.new("rdoc/generators/#{gen}", + "#{type.upcase}Generator".intern, + type) + end + } + } + + ####### + private + ####### + + ## + # Report an error message and exit + + def error(msg) + raise RDocError.new(msg) + end + + ## + # Create an output dir if it doesn't exist. If it does + # exist, but doesn't contain the flag file created.rid + # then we refuse to use it, as we may clobber some + # manually generated documentation + + def setup_output_dir(op_dir, force) + flag_file = output_flag_file(op_dir) + if File.exist?(op_dir) + unless File.directory?(op_dir) + error "'#{op_dir}' exists, and is not a directory" + end + begin + created = File.read(flag_file) + rescue SystemCallError + error "\nDirectory #{op_dir} already exists, but it looks like it\n" + + "isn't an RDoc directory. Because RDoc doesn't want to risk\n" + + "destroying any of your existing files, you'll need to\n" + + "specify a different output directory name (using the\n" + + "--op option).\n\n" + else + last = (Time.parse(created) unless force rescue nil) + end + else + File.makedirs(op_dir) + end + last + end + + # Update the flag file in an output directory. + def update_output_dir(op_dir, time) + File.open(output_flag_file(op_dir), "w") {|f| f.puts time.rfc2822 } + end + + # Return the path name of the flag file in an output directory. + def output_flag_file(op_dir) + File.join(op_dir, "created.rid") + end + + # The .document file contains a list of file and directory name + # patterns, representing candidates for documentation. It may + # also contain comments (starting with '#') + def parse_dot_doc_file(in_dir, filename, options) + # read and strip comments + patterns = File.read(filename).gsub(/#.*/, '') + + result = [] + + patterns.split.each do |patt| + candidates = Dir.glob(File.join(in_dir, patt)) + result.concat(normalized_file_list(options, candidates)) + end + result + end + + + # Given a list of files and directories, create a list + # of all the Ruby files they contain. + # + # If +force_doc+ is true, we always add the given files. + # If false, only add files that we guarantee we can parse + # It is true when looking at files given on the command line, + # false when recursing through subdirectories. + # + # The effect of this is that if you want a file with a non- + # standard extension parsed, you must name it explicity. + # + + def normalized_file_list(options, relative_files, force_doc = false, exclude_pattern=nil) + file_list = [] + + relative_files.each do |rel_file_name| + next if exclude_pattern && exclude_pattern =~ rel_file_name + stat = File.stat(rel_file_name) + case type = stat.ftype + when "file" + next if @last_created and stat.mtime < @last_created + file_list << rel_file_name.sub(/^\.\//, '') if force_doc || ParserFactory.can_parse(rel_file_name) + when "directory" + next if rel_file_name == "CVS" || rel_file_name == ".svn" + dot_doc = File.join(rel_file_name, DOT_DOC_FILENAME) + if File.file?(dot_doc) + file_list.concat(parse_dot_doc_file(rel_file_name, dot_doc, options)) + else + file_list.concat(list_files_in_directory(rel_file_name, options)) + end + else + raise RDocError.new("I can't deal with a #{type} #{rel_file_name}") + end + end + file_list + end + + # Return a list of the files to be processed in + # a directory. We know that this directory doesn't have + # a .document file, so we're looking for real files. However + # we may well contain subdirectories which must + # be tested for .document files + def list_files_in_directory(dir, options) + normalized_file_list(options, Dir.glob(File.join(dir, "*")), false, options.exclude) + end + + + # Parse each file on the command line, recursively entering + # directories + + def parse_files(options) + + file_info = [] + + files = options.files + files = ["."] if files.empty? + + file_list = normalized_file_list(options, files, true) + + file_list.each do |fn| + $stderr.printf("\n%35s: ", File.basename(fn)) unless options.quiet + + content = File.open(fn, "r") {|f| f.read} + + top_level = TopLevel.new(fn) + parser = ParserFactory.parser_for(top_level, fn, content, options, @stats) + file_info << parser.scan + @stats.num_files += 1 + end + + file_info + end + + + public + + ################################################################### + # + # Format up one or more files according to the given arguments. + # For simplicity, _argv_ is an array of strings, equivalent to the + # strings that would be passed on the command line. (This isn't a + # coincidence, as we _do_ pass in ARGV when running + # interactively). For a list of options, see rdoc/rdoc.rb. By + # default, output will be stored in a directory called +doc+ below + # the current directory, so make sure you're somewhere writable + # before invoking. + # + # Throws: RDocError on error + + def document(argv) + + TopLevel::reset + + @stats = Stats.new + + options = Options.instance + options.parse(argv, GENERATORS) + + @last_created = nil + unless options.all_one_file + @last_created = setup_output_dir(options.op_dir, options.force_update) + end + start_time = Time.now + + file_info = parse_files(options) + + if file_info.empty? + $stderr.puts "\nNo newer files." unless options.quiet + else + gen = options.generator + + $stderr.puts "\nGenerating #{gen.key.upcase}..." unless options.quiet + + require gen.file_name + + gen_class = Generators.const_get(gen.class_name) + gen = gen_class.for(options) + + pwd = Dir.pwd + + Dir.chdir(options.op_dir) unless options.all_one_file + + begin + Diagram.new(file_info, options).draw if options.diagram + gen.generate(file_info) + update_output_dir(".", start_time) + ensure + Dir.chdir(pwd) + end + end + + unless options.quiet + puts + @stats.print + end + end + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_cache.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_cache.rb new file mode 100644 index 0000000000..1844ac969e --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_cache.rb @@ -0,0 +1,187 @@ +module RI + + class ClassEntry + + attr_reader :name + attr_reader :path_names + + def initialize(path_name, name, in_class) + @path_names = [ path_name ] + @name = name + @in_class = in_class + @class_methods = [] + @instance_methods = [] + @inferior_classes = [] + end + + # We found this class in more tha one place, so add + # in the name from there. + def add_path(path) + @path_names << path + end + + # read in our methods and any classes + # and modules in our namespace. Methods are + # stored in files called name-c|i.yaml, + # where the 'name' portion is the external + # form of the method name and the c|i is a class|instance + # flag + + def load_from(dir) + Dir.foreach(dir) do |name| + next if name =~ /^\./ + + # convert from external to internal form, and + # extract the instance/class flag + + if name =~ /^(.*?)-(c|i).yaml$/ + external_name = $1 + is_class_method = $2 == "c" + internal_name = RiWriter.external_to_internal(external_name) + list = is_class_method ? @class_methods : @instance_methods + path = File.join(dir, name) + list << MethodEntry.new(path, internal_name, is_class_method, self) + else + full_name = File.join(dir, name) + if File.directory?(full_name) + inf_class = @inferior_classes.find {|c| c.name == name } + if inf_class + inf_class.add_path(full_name) + else + inf_class = ClassEntry.new(full_name, name, self) + @inferior_classes << inf_class + end + inf_class.load_from(full_name) + end + end + end + end + + # Return a list of any classes or modules that we contain + # that match a given string + + def contained_modules_matching(name) + @inferior_classes.find_all {|c| c.name[name]} + end + + def classes_and_modules + @inferior_classes + end + + # Return an exact match to a particular name + def contained_class_named(name) + @inferior_classes.find {|c| c.name == name} + end + + # return the list of local methods matching name + # We're split into two because we need distinct behavior + # when called from the _toplevel_ + def methods_matching(name, is_class_method) + local_methods_matching(name, is_class_method) + end + + # Find methods matching 'name' in ourselves and in + # any classes we contain + def recursively_find_methods_matching(name, is_class_method) + res = local_methods_matching(name, is_class_method) + @inferior_classes.each do |c| + res.concat(c.recursively_find_methods_matching(name, is_class_method)) + end + res + end + + + # Return our full name + def full_name + res = @in_class.full_name + res << "::" unless res.empty? + res << @name + end + + # Return a list of all out method names + def all_method_names + res = @class_methods.map {|m| m.full_name } + @instance_methods.each {|m| res << m.full_name} + res + end + + private + + # Return a list of all our methods matching a given string. + # Is +is_class_methods+ if 'nil', we don't care if the method + # is a class method or not, otherwise we only return + # those methods that match + def local_methods_matching(name, is_class_method) + + list = case is_class_method + when nil then @class_methods + @instance_methods + when true then @class_methods + when false then @instance_methods + else fail "Unknown is_class_method: #{is_class_method.inspect}" + end + + list.find_all {|m| m.name; m.name[name]} + end + end + + # A TopLevelEntry is like a class entry, but when asked to search + # for methods searches all classes, not just itself + + class TopLevelEntry < ClassEntry + def methods_matching(name, is_class_method) + res = recursively_find_methods_matching(name, is_class_method) + end + + def full_name + "" + end + + def module_named(name) + + end + + end + + class MethodEntry + attr_reader :name + attr_reader :path_name + + def initialize(path_name, name, is_class_method, in_class) + @path_name = path_name + @name = name + @is_class_method = is_class_method + @in_class = in_class + end + + def full_name + res = @in_class.full_name + unless res.empty? + if @is_class_method + res << "::" + else + res << "#" + end + end + res << @name + end + end + + # We represent everything know about all 'ri' files + # accessible to this program + + class RiCache + + attr_reader :toplevel + + def initialize(dirs) + # At the top level we have a dummy module holding the + # overall namespace + @toplevel = TopLevelEntry.new('', '::', nil) + + dirs.each do |dir| + @toplevel.load_from(dir) + end + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_descriptions.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_descriptions.rb new file mode 100644 index 0000000000..e5ea9f2fbf --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_descriptions.rb @@ -0,0 +1,154 @@ +require 'yaml' +require 'rdoc/markup/simple_markup/fragments' + +# Descriptions are created by RDoc (in ri_generator) and +# written out in serialized form into the documentation +# tree. ri then reads these to generate the documentation + +module RI + class NamedThing + attr_reader :name + def initialize(name) + @name = name + end + def <=>(other) + @name <=> other.name + end + + def hash + @name.hash + end + + def eql?(other) + @name.eql?(other) + end + end + +# Alias = Struct.new(:old_name, :new_name) + + class AliasName < NamedThing + end + + class Attribute < NamedThing + attr_reader :rw, :comment + def initialize(name, rw, comment) + super(name) + @rw = rw + @comment = comment + end + end + + class Constant < NamedThing + attr_reader :value, :comment + def initialize(name, value, comment) + super(name) + @value = value + @comment = comment + end + end + + class IncludedModule < NamedThing + end + + + class MethodSummary < NamedThing + def initialize(name="") + super + end + end + + + + class Description + attr_accessor :name + attr_accessor :full_name + attr_accessor :comment + + def serialize + self.to_yaml + end + + def Description.deserialize(from) + YAML.load(from) + end + + def <=>(other) + @name <=> other.name + end + end + + class ModuleDescription < Description + + attr_accessor :class_methods + attr_accessor :instance_methods + attr_accessor :attributes + attr_accessor :constants + attr_accessor :includes + + # merge in another class desscription into this one + def merge_in(old) + merge(@class_methods, old.class_methods) + merge(@instance_methods, old.instance_methods) + merge(@attributes, old.attributes) + merge(@constants, old.constants) + merge(@includes, old.includes) + if @comment.nil? || @comment.empty? + @comment = old.comment + else + unless old.comment.nil? or old.comment.empty? then + @comment << SM::Flow::RULE.new + @comment.concat old.comment + end + end + end + + def display_name + "Module" + end + + # the 'ClassDescription' subclass overrides this + # to format up the name of a parent + def superclass_string + nil + end + + private + + def merge(into, from) + names = {} + into.each {|i| names[i.name] = i } + from.each {|i| names[i.name] = i } + into.replace(names.keys.sort.map {|n| names[n]}) + end + end + + class ClassDescription < ModuleDescription + attr_accessor :superclass + + def display_name + "Class" + end + + def superclass_string + if @superclass && @superclass != "Object" + @superclass + else + nil + end + end + end + + + class MethodDescription < Description + + attr_accessor :is_class_method + attr_accessor :visibility + attr_accessor :block_params + attr_accessor :is_singleton + attr_accessor :aliases + attr_accessor :is_alias_for + attr_accessor :params + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_display.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_display.rb new file mode 100644 index 0000000000..67962fc2c1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_display.rb @@ -0,0 +1,255 @@ +require 'rdoc/ri/ri_util' +require 'rdoc/ri/ri_formatter' +require 'rdoc/ri/ri_options' + + +# This is a kind of 'flag' module. If you want to write your +# own 'ri' display module (perhaps because you'r writing +# an IDE or somesuch beast), you simply write a class +# which implements the various 'display' methods in 'DefaultDisplay', +# and include the 'RiDisplay' module in that class. +# +# To access your class from the command line, you can do +# +# ruby -r ../ri .... +# +# If folks _really_ want to do this from the command line, +# I'll build an option in + +module RiDisplay + @@display_class = nil + + def RiDisplay.append_features(display_class) + @@display_class = display_class + end + + def RiDisplay.new(*args) + @@display_class.new(*args) + end +end + +###################################################################### +# +# A paging display module. Uses the ri_formatter class to do the +# actual presentation +# + +class DefaultDisplay + + include RiDisplay + + def initialize(options) + @options = options + @formatter = @options.formatter.new(@options, " ") + end + + + ###################################################################### + + def display_usage + page do + RI::Options::OptionList.usage(short_form=true) + end + end + + + ###################################################################### + + def display_method_info(method) + page do + @formatter.draw_line(method.full_name) + display_params(method) + @formatter.draw_line + display_flow(method.comment) + if method.aliases && !method.aliases.empty? + @formatter.blankline + aka = "(also known as " + aka << method.aliases.map {|a| a.name }.join(", ") + aka << ")" + @formatter.wrap(aka) + end + end + end + + ###################################################################### + + def display_class_info(klass, ri_reader) + page do + superclass = klass.superclass_string + + if superclass + superclass = " < " + superclass + else + superclass = "" + end + + @formatter.draw_line(klass.display_name + ": " + + klass.full_name + superclass) + + display_flow(klass.comment) + @formatter.draw_line + + unless klass.includes.empty? + @formatter.blankline + @formatter.display_heading("Includes:", 2, "") + incs = [] + klass.includes.each do |inc| + inc_desc = ri_reader.find_class_by_name(inc.name) + if inc_desc + str = inc.name + "(" + str << inc_desc.instance_methods.map{|m| m.name}.join(", ") + str << ")" + incs << str + else + incs << inc.name + end + end + @formatter.wrap(incs.sort.join(', ')) + end + + unless klass.constants.empty? + @formatter.blankline + @formatter.display_heading("Constants:", 2, "") + len = 0 + klass.constants.each { |c| len = c.name.length if c.name.length > len } + len += 2 + klass.constants.each do |c| + @formatter.wrap(c.value, + @formatter.indent+((c.name+":").ljust(len))) + end + end + + unless klass.class_methods.empty? + @formatter.blankline + @formatter.display_heading("Class methods:", 2, "") + @formatter.wrap(klass.class_methods.map{|m| m.name}.sort.join(', ')) + end + + unless klass.instance_methods.empty? + @formatter.blankline + @formatter.display_heading("Instance methods:", 2, "") + @formatter.wrap(klass.instance_methods.map{|m| m.name}.sort.join(', ')) + end + + unless klass.attributes.empty? + @formatter.blankline + @formatter.wrap("Attributes:", "") + @formatter.wrap(klass.attributes.map{|a| a.name}.sort.join(', ')) + end + end + end + + ###################################################################### + + # Display a list of method names + + def display_method_list(methods) + page do + puts "More than one method matched your request. You can refine" + puts "your search by asking for information on one of:\n\n" + @formatter.wrap(methods.map {|m| m.full_name} .join(", ")) + end + end + + ###################################################################### + + def display_class_list(namespaces) + page do + puts "More than one class or module matched your request. You can refine" + puts "your search by asking for information on one of:\n\n" + @formatter.wrap(namespaces.map {|m| m.full_name}.join(", ")) + end + end + + ###################################################################### + + def list_known_classes(classes) + if classes.empty? + warn_no_database + else + page do + @formatter.draw_line("Known classes and modules") + @formatter.blankline + @formatter.wrap(classes.sort.join(", ")) + end + end + end + + ###################################################################### + + def list_known_names(names) + if names.empty? + warn_no_database + else + page do + names.each {|n| @formatter.raw_print_line(n)} + end + end + end + + ###################################################################### + + private + + ###################################################################### + + def page + return yield unless pager = setup_pager + begin + save_stdout = STDOUT.clone + STDOUT.reopen(pager) + yield + ensure + STDOUT.reopen(save_stdout) + save_stdout.close + pager.close + end + end + + ###################################################################### + + def setup_pager + unless @options.use_stdout + for pager in [ ENV['PAGER'], "less", "more", 'pager' ].compact.uniq + return IO.popen(pager, "w") rescue nil + end + @options.use_stdout = true + nil + end + end + + ###################################################################### + + def display_params(method) + + params = method.params + + if params[0,1] == "(" + if method.is_singleton + params = method.full_name + params + else + params = method.name + params + end + end + params.split(/\n/).each do |p| + @formatter.wrap(p) + @formatter.break_to_newline + end + end + ###################################################################### + + def display_flow(flow) + if !flow || flow.empty? + @formatter.wrap("(no description...)") + else + @formatter.display_flow(flow) + end + end + + ###################################################################### + + def warn_no_database + puts "Before using ri, you need to generate documentation" + puts "using 'rdoc' with the --ri option" + end +end # class RiDisplay diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_driver.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_driver.rb new file mode 100644 index 0000000000..a00f20ee3b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_driver.rb @@ -0,0 +1,143 @@ +require 'rdoc/ri/ri_paths' +require 'rdoc/usage' +require 'rdoc/ri/ri_cache' +require 'rdoc/ri/ri_util' +require 'rdoc/ri/ri_reader' +require 'rdoc/ri/ri_formatter' +require 'rdoc/ri/ri_options' + + +###################################################################### + +class RiDriver + + def initialize + @options = RI::Options.instance + + args = ARGV + if ENV["RI"] + args = ENV["RI"].split.concat(ARGV) + end + + @options.parse(args) + + path = @options.path + report_missing_documentation @options.raw_path if path.empty? + + @ri_reader = RI::RiReader.new(RI::RiCache.new(path)) + @display = @options.displayer + end + + # Couldn't find documentation in +path+, so tell the user what to do + + def report_missing_documentation(path) + STDERR.puts "No ri documentation found in:" + path.each do |d| + STDERR.puts " #{d}" + end + STDERR.puts "\nWas rdoc run to create documentation?\n\n" + RDoc::usage("Installing Documentation") + end + + ###################################################################### + + # If the list of matching methods contains exactly one entry, or + # if it contains an entry that exactly matches the requested method, + # then display that entry, otherwise display the list of + # matching method names + + def report_method_stuff(requested_method_name, methods) + if methods.size == 1 + method = @ri_reader.get_method(methods[0]) + @display.display_method_info(method) + else + entries = methods.find_all {|m| m.name == requested_method_name} + if entries.size == 1 + method = @ri_reader.get_method(entries[0]) + @display.display_method_info(method) + else + @display.display_method_list(methods) + end + end + end + + ###################################################################### + + def report_class_stuff(namespaces) + if namespaces.size == 1 + klass = @ri_reader.get_class(namespaces[0]) + @display.display_class_info(klass, @ri_reader) + else +# entries = namespaces.find_all {|m| m.full_name == requested_class_name} +# if entries.size == 1 +# klass = @ri_reader.get_class(entries[0]) +# @display.display_class_info(klass, @ri_reader) +# else + @display.display_class_list(namespaces) +# end + end + end + + ###################################################################### + + + def get_info_for(arg) + desc = NameDescriptor.new(arg) + + namespaces = @ri_reader.top_level_namespace + + for class_name in desc.class_names + namespaces = @ri_reader.lookup_namespace_in(class_name, namespaces) + if namespaces.empty? + raise RiError.new("Nothing known about #{arg}") + end + end + + # at this point, if we have multiple possible namespaces, but one + # is an exact match for our requested class, prune down to just it + + full_class_name = desc.full_class_name + entries = namespaces.find_all {|m| m.full_name == full_class_name} + namespaces = entries if entries.size == 1 + + if desc.method_name.nil? + report_class_stuff(namespaces) + else + methods = @ri_reader.find_methods(desc.method_name, + desc.is_class_method, + namespaces) + + if methods.empty? + raise RiError.new("Nothing known about #{arg}") + else + report_method_stuff(desc.method_name, methods) + end + end + end + + ###################################################################### + + def process_args + if @options.list_classes + classes = @ri_reader.full_class_names + @display.list_known_classes(classes) + elsif @options.list_names + names = @ri_reader.all_names + @display.list_known_names(names) + else + if ARGV.size.zero? + @display.display_usage + else + begin + ARGV.each do |arg| + get_info_for(arg) + end + rescue RiError => e + STDERR.puts(e.message) + exit(1) + end + end + end + end + +end # class RiDriver diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_formatter.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_formatter.rb new file mode 100644 index 0000000000..34eb561ca3 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_formatter.rb @@ -0,0 +1,672 @@ +module RI + class TextFormatter + + attr_reader :indent + + def initialize(options, indent) + @options = options + @width = options.width + @indent = indent + end + + + ###################################################################### + + def draw_line(label=nil) + len = @width + len -= (label.size+1) if label + print "-"*len + if label + print(" ") + bold_print(label) + end + puts + end + + ###################################################################### + + def wrap(txt, prefix=@indent, linelen=@width) + return unless txt && !txt.empty? + work = conv_markup(txt) + textLen = linelen - prefix.length + patt = Regexp.new("^(.{0,#{textLen}})[ \n]") + next_prefix = prefix.tr("^ ", " ") + + res = [] + + while work.length > textLen + if work =~ patt + res << $1 + work.slice!(0, $&.length) + else + res << work.slice!(0, textLen) + end + end + res << work if work.length.nonzero? + puts(prefix + res.join("\n" + next_prefix)) + end + + ###################################################################### + + def blankline + puts + end + + ###################################################################### + + # called when we want to ensure a nbew 'wrap' starts on a newline + # Only needed for HtmlFormatter, because the rest do their + # own line breaking + + def break_to_newline + end + + ###################################################################### + + def bold_print(txt) + print txt + end + + ###################################################################### + + def raw_print_line(txt) + puts txt + end + + ###################################################################### + + # convert HTML entities back to ASCII + def conv_html(txt) + txt. + gsub(/>/, '>'). + gsub(/</, '<'). + gsub(/"/, '"'). + gsub(/&/, '&') + + end + + # convert markup into display form + def conv_markup(txt) + txt. + gsub(%r{(.*?)}) { "+#$1+" } . + gsub(%r{(.*?)}) { "+#$1+" } . + gsub(%r{(.*?)}) { "*#$1*" } . + gsub(%r{(.*?)}) { "_#$1_" } + end + + ###################################################################### + + def display_list(list) + case list.type + + when SM::ListBase::BULLET + prefixer = proc { |ignored| @indent + "* " } + + when SM::ListBase::NUMBER, + SM::ListBase::UPPERALPHA, + SM::ListBase::LOWERALPHA + + start = case list.type + when SM::ListBase::NUMBER then 1 + when SM::ListBase::UPPERALPHA then 'A' + when SM::ListBase::LOWERALPHA then 'a' + end + prefixer = proc do |ignored| + res = @indent + "#{start}.".ljust(4) + start = start.succ + res + end + + when SM::ListBase::LABELED + prefixer = proc do |li| + li.label + end + + when SM::ListBase::NOTE + longest = 0 + list.contents.each do |item| + if item.kind_of?(SM::Flow::LI) && item.label.length > longest + longest = item.label.length + end + end + + prefixer = proc do |li| + @indent + li.label.ljust(longest+1) + end + + else + fail "unknown list type" + + end + + list.contents.each do |item| + if item.kind_of? SM::Flow::LI + prefix = prefixer.call(item) + display_flow_item(item, prefix) + else + display_flow_item(item) + end + end + end + + ###################################################################### + + def display_flow_item(item, prefix=@indent) + case item + when SM::Flow::P, SM::Flow::LI + wrap(conv_html(item.body), prefix) + blankline + + when SM::Flow::LIST + display_list(item) + + when SM::Flow::VERB + display_verbatim_flow_item(item, @indent) + + when SM::Flow::H + display_heading(conv_html(item.text), item.level, @indent) + + when SM::Flow::RULE + draw_line + + else + fail "Unknown flow element: #{item.class}" + end + end + + ###################################################################### + + def display_verbatim_flow_item(item, prefix=@indent) + item.body.split(/\n/).each do |line| + print @indent, conv_html(line), "\n" + end + blankline + end + + ###################################################################### + + def display_heading(text, level, indent) + text = strip_attributes(text) + case level + when 1 + ul = "=" * text.length + puts + puts text.upcase + puts ul +# puts + + when 2 + ul = "-" * text.length + puts + puts text + puts ul +# puts + else + print indent, text, "\n" + end + end + + + def display_flow(flow) + flow.each do |f| + display_flow_item(f) + end + end + + def strip_attributes(txt) + tokens = txt.split(%r{()}) + text = [] + attributes = 0 + tokens.each do |tok| + case tok + when %r{^$}, %r{^<(\w+)>$} + ; + else + text << tok + end + end + text.join + end + + + end + + + ###################################################################### + # Handle text with attributes. We're a base class: there are + # different presentation classes (one, for example, uses overstrikes + # to handle bold and underlining, while another using ANSI escape + # sequences + + class AttributeFormatter < TextFormatter + + BOLD = 1 + ITALIC = 2 + CODE = 4 + + ATTR_MAP = { + "b" => BOLD, + "code" => CODE, + "em" => ITALIC, + "i" => ITALIC, + "tt" => CODE + } + + # TODO: struct? + class AttrChar + attr_reader :char + attr_reader :attr + + def initialize(char, attr) + @char = char + @attr = attr + end + end + + + class AttributeString + attr_reader :txt + + def initialize + @txt = [] + @optr = 0 + end + + def <<(char) + @txt << char + end + + def empty? + @optr >= @txt.length + end + + # accept non space, then all following spaces + def next_word + start = @optr + len = @txt.length + + while @optr < len && @txt[@optr].char != " " + @optr += 1 + end + + while @optr < len && @txt[@optr].char == " " + @optr += 1 + end + + @txt[start...@optr] + end + end + + ###################################################################### + # overrides base class. Looks for ... etc sequences + # and generates an array of AttrChars. This array is then used + # as the basis for the split + + def wrap(txt, prefix=@indent, linelen=@width) + return unless txt && !txt.empty? + + txt = add_attributes_to(txt) + next_prefix = prefix.tr("^ ", " ") + linelen -= prefix.size + + line = [] + + until txt.empty? + word = txt.next_word + if word.size + line.size > linelen + write_attribute_text(prefix, line) + prefix = next_prefix + line = [] + end + line.concat(word) + end + + write_attribute_text(prefix, line) if line.length > 0 + end + + protected + + # overridden in specific formatters + + def write_attribute_text(prefix, line) + print prefix + line.each do |achar| + print achar.char + end + puts + end + + # again, overridden + + def bold_print(txt) + print txt + end + + private + + def add_attributes_to(txt) + tokens = txt.split(%r{()}) + text = AttributeString.new + attributes = 0 + tokens.each do |tok| + case tok + when %r{^$} then attributes &= ~(ATTR_MAP[$1]||0) + when %r{^<(\w+)>$} then attributes |= (ATTR_MAP[$1]||0) + else + tok.split(//).each {|ch| text << AttrChar.new(ch, attributes)} + end + end + text + end + + end + + + ################################################## + + # This formatter generates overstrike-style formatting, which + # works with pagers such as man and less. + + class OverstrikeFormatter < AttributeFormatter + + BS = "\C-h" + + def write_attribute_text(prefix, line) + print prefix + line.each do |achar| + attr = achar.attr + if (attr & (ITALIC+CODE)) != 0 + print "_", BS + end + if (attr & BOLD) != 0 + print achar.char, BS + end + print achar.char + end + puts + end + + # draw a string in bold + def bold_print(text) + text.split(//).each do |ch| + print ch, BS, ch + end + end + end + + ################################################## + + # This formatter uses ANSI escape sequences + # to colorize stuff + # works with pages such as man and less. + + class AnsiFormatter < AttributeFormatter + + def initialize(*args) + print "\033[0m" + super + end + + def write_attribute_text(prefix, line) + print prefix + curr_attr = 0 + line.each do |achar| + attr = achar.attr + if achar.attr != curr_attr + update_attributes(achar.attr) + curr_attr = achar.attr + end + print achar.char + end + update_attributes(0) unless curr_attr.zero? + puts + end + + + def bold_print(txt) + print "\033[1m#{txt}\033[m" + end + + HEADINGS = { + 1 => [ "\033[1;32m", "\033[m" ] , + 2 => ["\033[4;32m", "\033[m" ], + 3 => ["\033[32m", "\033[m" ] + } + + def display_heading(text, level, indent) + level = 3 if level > 3 + heading = HEADINGS[level] + print indent + print heading[0] + print strip_attributes(text) + puts heading[1] + end + + private + + ATTR_MAP = { + BOLD => "1", + ITALIC => "33", + CODE => "36" + } + + def update_attributes(attr) + str = "\033[" + for quality in [ BOLD, ITALIC, CODE] + unless (attr & quality).zero? + str << ATTR_MAP[quality] + end + end + print str, "m" + end + end + + ################################################## + + # This formatter uses HTML. + + class HtmlFormatter < AttributeFormatter + + def initialize(*args) + super + end + + def write_attribute_text(prefix, line) + curr_attr = 0 + line.each do |achar| + attr = achar.attr + if achar.attr != curr_attr + update_attributes(curr_attr, achar.attr) + curr_attr = achar.attr + end + print(escape(achar.char)) + end + update_attributes(curr_attr, 0) unless curr_attr.zero? + end + + def draw_line(label=nil) + if label != nil + bold_print(label) + end + puts("
    ") + end + + def bold_print(txt) + tag("b") { txt } + end + + def blankline() + puts("

    ") + end + + def break_to_newline + puts("
    ") + end + + def display_heading(text, level, indent) + level = 4 if level > 4 + tag("h#{level}") { text } + puts + end + + ###################################################################### + + def display_list(list) + + case list.type + when SM::ListBase::BULLET + list_type = "ul" + prefixer = proc { |ignored| "

  • " } + + when SM::ListBase::NUMBER, + SM::ListBase::UPPERALPHA, + SM::ListBase::LOWERALPHA + list_type = "ol" + prefixer = proc { |ignored| "
  • " } + + when SM::ListBase::LABELED + list_type = "dl" + prefixer = proc do |li| + "
    " + escape(li.label) + "
    " + end + + when SM::ListBase::NOTE + list_type = "table" + prefixer = proc do |li| + %{#{li.label.gsub(/ /, ' ')}} + end + else + fail "unknown list type" + end + + print "<#{list_type}>" + list.contents.each do |item| + if item.kind_of? SM::Flow::LI + prefix = prefixer.call(item) + print prefix + display_flow_item(item, prefix) + else + display_flow_item(item) + end + end + print "" + end + + def display_verbatim_flow_item(item, prefix=@indent) + print("
    ")
    +        puts item.body
    +        puts("
    ") + end + + private + + ATTR_MAP = { + BOLD => "b>", + ITALIC => "i>", + CODE => "tt>" + } + + def update_attributes(current, wanted) + str = "" + # first turn off unwanted ones + off = current & ~wanted + for quality in [ BOLD, ITALIC, CODE] + if (off & quality) > 0 + str << "") + print(yield) + print("") + end + + def escape(str) + str. + gsub(/&/n, '&'). + gsub(/\"/n, '"'). + gsub(/>/n, '>'). + gsub(/ AnsiFormatter, + "bs" => OverstrikeFormatter, + "html" => HtmlFormatter, + "plain" => TextFormatter, + "simple" => SimpleFormatter, + } + + def TextFormatter.list + FORMATTERS.keys.sort.join(", ") + end + + def TextFormatter.for(name) + FORMATTERS[name.downcase] + end + + end + +end + + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_options.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_options.rb new file mode 100644 index 0000000000..db9f4afecf --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_options.rb @@ -0,0 +1,313 @@ +# We handle the parsing of options, and subsequently as a singleton +# object to be queried for option values + +module RI + + require 'rdoc/ri/ri_paths' + require 'rdoc/ri/ri_display' + + VERSION_STRING = "ri v1.0.1 - 20041108" + + class Options + + require 'singleton' + require 'getoptlong' + + include Singleton + + # No not use a pager. Writable, because ri sets it if it + # can't find a pager + attr_accessor :use_stdout + + # should we just display a class list and exit + attr_reader :list_classes + + # should we display a list of all names + attr_reader :list_names + + # The width of the output line + attr_reader :width + + # the formatting we apply to the output + attr_reader :formatter + + # the directory we search for original documentation + attr_reader :doc_dir + + module OptionList + + OPTION_LIST = [ + [ "--help", "-h", nil, + "you're looking at it" ], + + [ "--classes", "-c", nil, + "Display the names of classes and modules we\n" + + "know about"], + + [ "--doc-dir", "-d", "", + "A directory to search for documentation. If not\n" + + "specified, we search the standard rdoc/ri directories.\n" + + "May be repeated."], + + [ "--system", nil, nil, + "Include documentation from Ruby's standard library:\n " + + RI::Paths::SYSDIR ], + + [ "--site", nil, nil, + "Include documentation from libraries installed in site_lib:\n " + + RI::Paths::SITEDIR ], + + [ "--home", nil, nil, + "Include documentation stored in ~/.rdoc:\n " + + (RI::Paths::HOMEDIR || "No ~/.rdoc found") ], + + [ "--gems", nil, nil, + "Include documentation from Rubygems:\n " + + (RI::Paths::GEMDIRS ? "#{Gem.path}/doc/*/ri" : + "No Rubygems ri found.") ], + + [ "--format", "-f", "", + "Format to use when displaying output:\n" + + " " + RI::TextFormatter.list + "\n" + + "Use 'bs' (backspace) with most pager programs.\n" + + "To use ANSI, either also use the -T option, or\n" + + "tell your pager to allow control characters\n" + + "(for example using the -R option to less)"], + + [ "--list-names", "-l", nil, + "List all the names known to RDoc, one per line" + ], + + [ "--no-pager", "-T", nil, + "Send output directly to stdout." + ], + + [ "--width", "-w", "output width", + "Set the width of the output" ], + + [ "--version", "-v", nil, + "Display the version of ri" + ], + + ] + + def OptionList.options + OPTION_LIST.map do |long, short, arg,| + option = [] + option << long + option << short unless short.nil? + option << (arg ? GetoptLong::REQUIRED_ARGUMENT : + GetoptLong::NO_ARGUMENT) + option + end + end + + + def OptionList.strip_output(text) + text =~ /^\s+/ + leading_spaces = $& + text.gsub!(/^#{leading_spaces}/, '') + $stdout.puts text + end + + + # Show an error and exit + + def OptionList.error(msg) + $stderr.puts + $stderr.puts msg + $stderr.puts "\nFor help on options, try 'ri --help'\n\n" + exit 1 + end + + # Show usage and exit + + def OptionList.usage(short_form=false) + + puts + puts(RI::VERSION_STRING) + puts + + name = File.basename($0) + + directories = [ + RI::Paths::SYSDIR, + RI::Paths::SITEDIR, + RI::Paths::HOMEDIR + ] + + directories << "#{Gem.path}/doc/*/ri" if RI::Paths::GEMDIRS + + directories = directories.join("\n ") + + OptionList.strip_output(<<-EOT) + Usage: + + #{name} [options] [names...] + + Display information on Ruby classes, modules, and methods. + Give the names of classes or methods to see their documentation. + Partial names may be given: if the names match more than + one entity, a list will be shown, otherwise details on + that entity will be displayed. + + Nested classes and modules can be specified using the normal + Name::Name notation, and instance methods can be distinguished + from class methods using "." (or "#") instead of "::". + + For example: + + ri File + ri File.new + ri F.n + ri zip + + Note that shell quoting may be required for method names + containing punctuation: + + ri 'Array.[]' + ri compact\\! + + By default ri searches for documentation in the following + directories: + + #{directories} + + Specifying the --system, --site, --home, --gems or --doc-dir + options will limit ri to searching only the specified + directories. + + EOT + + if short_form + puts "For help on options, type 'ri -h'" + puts "For a list of classes I know about, type 'ri -c'" + else + puts "Options:\n\n" + OPTION_LIST.each do|long, short, arg, desc| + opt = '' + opt << (short ? sprintf("%15s", "#{long}, #{short}") : + sprintf("%15s", long)) + if arg + opt << " " << arg + end + print opt + desc = desc.split("\n") + if opt.size < 17 + print " "*(18-opt.size) + puts desc.shift + else + puts + end + desc.each do |line| + puts(" "*18 + line) + end + puts + end + puts "Options may also be passed in the 'RI' environment variable" + exit 0 + end + end + end + + # Show the version and exit + def show_version + puts VERSION_STRING + exit(0) + end + + def initialize + @use_stdout = !STDOUT.tty? + @width = 72 + @formatter = RI::TextFormatter.for("plain") + @list_classes = false + @list_names = false + + # By default all paths are used. If any of these are true, only those + # directories are used. + @use_system = false + @use_site = false + @use_home = false + @use_gems = false + @doc_dirs = [] + end + + # Parse command line options. + + def parse(args) + + old_argv = ARGV.dup + + ARGV.replace(args) + + begin + + go = GetoptLong.new(*OptionList.options) + go.quiet = true + + go.each do |opt, arg| + case opt + when "--help" then OptionList.usage + when "--version" then show_version + when "--list-names" then @list_names = true + when "--no-pager" then @use_stdout = true + when "--classes" then @list_classes = true + + when "--system" then @use_system = true + when "--site" then @use_site = true + when "--home" then @use_home = true + when "--gems" then @use_gems = true + + when "--doc-dir" + if File.directory?(arg) + @doc_dirs << arg + else + $stderr.puts "Invalid directory: #{arg}" + exit 1 + end + + when "--format" + @formatter = RI::TextFormatter.for(arg) + unless @formatter + $stderr.print "Invalid formatter (should be one of " + $stderr.puts RI::TextFormatter.list + ")" + exit 1 + end + when "--width" + begin + @width = Integer(arg) + rescue + $stderr.puts "Invalid width: '#{arg}'" + exit 1 + end + end + end + + rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument => error + OptionList.error(error.message) + + end + end + + # Return the selected documentation directories. + + def path + RI::Paths.path(@use_system, @use_site, @use_home, @use_gems, *@doc_dirs) + end + + def raw_path + RI::Paths.raw_path(@use_system, @use_site, @use_home, @use_gems, + *@doc_dirs) + end + + # Return an instance of the displayer (the thing that actually writes + # the information). This allows us to load in new displayer classes + # at runtime (for example to help with IDE integration) + + def displayer + ::RiDisplay.new(self) + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_paths.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_paths.rb new file mode 100644 index 0000000000..32363bf70a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_paths.rb @@ -0,0 +1,80 @@ +module RI + + # Encapsulate all the strangeness to do with finding out + # where to find RDoc files + # + # We basically deal with three directories: + # + # 1. The 'system' documentation directory, which holds + # the documentation distributed with Ruby, and which + # is managed by the Ruby install process + # 2. The 'site' directory, which contains site-wide + # documentation added locally. + # 3. The 'user' documentation directory, stored under the + # user's own home directory. + # + # There's contention about all this, but for now: + # + # system:: $datadir/ri//system/... + # site:: $datadir/ri//site/... + # user:: ~/.rdoc + + module Paths + + #:stopdoc: + require 'rbconfig' + + DOC_DIR = "doc/rdoc" + + version = Config::CONFIG['ruby_version'] + + base = File.join(Config::CONFIG['datadir'], "ri", version) + SYSDIR = File.join(base, "system") + SITEDIR = File.join(base, "site") + homedir = ENV['HOME'] || ENV['USERPROFILE'] || ENV['HOMEPATH'] + + if homedir + HOMEDIR = File.join(homedir, ".rdoc") + else + HOMEDIR = nil + end + + # This is the search path for 'ri' + PATH = [ SYSDIR, SITEDIR, HOMEDIR ].find_all {|p| p && File.directory?(p)} + + begin + require 'rubygems' + GEMDIRS = Dir["#{Gem.path}/doc/*/ri"] + GEMDIRS.each { |path| RI::Paths::PATH << path } + rescue LoadError + GEMDIRS = nil + end + + # Returns the selected documentation directories as an Array, or PATH if no + # overriding directories were given. + + def self.path(use_system, use_site, use_home, use_gems, *extra_dirs) + path = raw_path(use_system, use_site, use_home, use_gems, *extra_dirs) + return path.select { |path| File.directory? path } + end + + # Returns the selected documentation directories including nonexistent + # directories. Used to print out what paths were searched if no ri was + # found. + + def self.raw_path(use_system, use_site, use_home, use_gems, *extra_dirs) + return PATH unless use_system or use_site or use_home or use_gems or + not extra_dirs.empty? + + path = [] + path << extra_dirs unless extra_dirs.empty? + path << RI::Paths::SYSDIR if use_system + path << RI::Paths::SITEDIR if use_site + path << RI::Paths::HOMEDIR if use_home + path << RI::Paths::GEMDIRS if use_gems + + return path.flatten.compact + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_reader.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_reader.rb new file mode 100644 index 0000000000..fb2c373e38 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_reader.rb @@ -0,0 +1,100 @@ +require 'rdoc/ri/ri_descriptions' +require 'rdoc/ri/ri_writer' +require 'rdoc/markup/simple_markup/to_flow' + +module RI + class RiReader + + def initialize(ri_cache) + @cache = ri_cache + end + + def top_level_namespace + [ @cache.toplevel ] + end + + def lookup_namespace_in(target, namespaces) + result = [] + for n in namespaces + result.concat(n.contained_modules_matching(target)) + end + result + end + + def find_class_by_name(full_name) + names = full_name.split(/::/) + ns = @cache.toplevel + for name in names + ns = ns.contained_class_named(name) + return nil if ns.nil? + end + get_class(ns) + end + + def find_methods(name, is_class_method, namespaces) + result = [] + namespaces.each do |ns| + result.concat ns.methods_matching(name, is_class_method) + end + result + end + + # return the MethodDescription for a given MethodEntry + # by deserializing the YAML + def get_method(method_entry) + path = method_entry.path_name + File.open(path) { |f| RI::Description.deserialize(f) } + end + + # Return a class description + def get_class(class_entry) + result = nil + for path in class_entry.path_names + path = RiWriter.class_desc_path(path, class_entry) + desc = File.open(path) {|f| RI::Description.deserialize(f) } + if result + result.merge_in(desc) + else + result = desc + end + end + result + end + + # return the names of all classes and modules + def full_class_names + res = [] + find_classes_in(res, @cache.toplevel) + end + + # return a list of all classes, modules, and methods + def all_names + res = [] + find_names_in(res, @cache.toplevel) + end + + # ---- + private + # ---- + + def find_classes_in(res, klass) + classes = klass.classes_and_modules + for c in classes + res << c.full_name + find_classes_in(res, c) + end + res + end + + def find_names_in(res, klass) + classes = klass.classes_and_modules + for c in classes + res << c.full_name + res.concat c.all_method_names + find_names_in(res, c) + end + res + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_util.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_util.rb new file mode 100644 index 0000000000..8a01255897 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_util.rb @@ -0,0 +1,75 @@ +###################################################################### + +class RiError < Exception; end +# +# Break argument into its constituent class or module names, an +# optional method type, and a method name + +class NameDescriptor + + attr_reader :class_names + attr_reader :method_name + + # true and false have the obvious meaning. nil means we don't care + attr_reader :is_class_method + + # arg may be + # 1. a class or module name (optionally qualified with other class + # or module names (Kernel, File::Stat etc) + # 2. a method name + # 3. a method name qualified by a optionally fully qualified class + # or module name + # + # We're fairly casual about delimiters: folks can say Kernel::puts, + # Kernel.puts, or Kernel\#puts for example. There's one exception: + # if you say IO::read, we look for a class method, but if you + # say IO.read, we look for an instance method + + def initialize(arg) + @class_names = [] + separator = nil + + tokens = arg.split(/(\.|::|#)/) + + # Skip leading '::', '#' or '.', but remember it might + # be a method name qualifier + separator = tokens.shift if tokens[0] =~ /^(\.|::|#)/ + + # Skip leading '::', but remember we potentially have an inst + + # leading stuff must be class names + + while tokens[0] =~ /^[A-Z]/ + @class_names << tokens.shift + unless tokens.empty? + separator = tokens.shift + break unless separator == "::" + end + end + + # Now must have a single token, the method name, or an empty + # array + unless tokens.empty? + @method_name = tokens.shift + # We may now have a trailing !, ?, or = to roll into + # the method name + if !tokens.empty? && tokens[0] =~ /^[!?=]$/ + @method_name << tokens.shift + end + + if @method_name =~ /::|\.|#/ or !tokens.empty? + raise RiError.new("Bad argument: #{arg}") + end + if separator && separator != '.' + @is_class_method = separator == "::" + end + end + end + + # Return the full class name (with '::' between the components) + # or "" if there's no class name + + def full_class_name + @class_names.join("::") + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_writer.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_writer.rb new file mode 100644 index 0000000000..78c68e8409 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/ri/ri_writer.rb @@ -0,0 +1,62 @@ +require 'fileutils' + +module RI + class RiWriter + + def RiWriter.class_desc_path(dir, class_desc) + File.join(dir, "cdesc-" + class_desc.name + ".yaml") + end + + + # Convert a name from internal form (containing punctuation) + # to an external form (where punctuation is replaced + # by %xx) + + def RiWriter.internal_to_external(name) + name.gsub(/\W/) { sprintf("%%%02x", $&[0]) } + end + + # And the reverse operation + def RiWriter.external_to_internal(name) + name.gsub(/%([0-9a-f]{2,2})/) { $1.to_i(16).chr } + end + + def initialize(base_dir) + @base_dir = base_dir + end + + def remove_class(class_desc) + FileUtils.rm_rf(path_to_dir(class_desc.full_name)) + end + + def add_class(class_desc) + dir = path_to_dir(class_desc.full_name) + FileUtils.mkdir_p(dir) + class_file_name = RiWriter.class_desc_path(dir, class_desc) + File.open(class_file_name, "w") do |f| + f.write(class_desc.serialize) + end + end + + def add_method(class_desc, method_desc) + dir = path_to_dir(class_desc.full_name) + file_name = RiWriter.internal_to_external(method_desc.name) + meth_file_name = File.join(dir, file_name) + if method_desc.is_singleton + meth_file_name += "-c.yaml" + else + meth_file_name += "-i.yaml" + end + + File.open(meth_file_name, "w") do |f| + f.write(method_desc.serialize) + end + end + + private + + def path_to_dir(class_name) + File.join(@base_dir, *class_name.split('::')) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/template.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/template.rb new file mode 100644 index 0000000000..469e10fb4b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/template.rb @@ -0,0 +1,234 @@ +# Cheap-n-cheerful HTML page template system. You create a +# template containing: +# +# * variable names between percent signs (%fred%) +# * blocks of repeating stuff: +# +# START:key +# ... stuff +# END:key +# +# You feed the code a hash. For simple variables, the values +# are resolved directly from the hash. For blocks, the hash entry +# corresponding to +key+ will be an array of hashes. The block will +# be generated once for each entry. Blocks can be nested arbitrarily +# deeply. +# +# The template may also contain +# +# IF:key +# ... stuff +# ENDIF:key +# +# _stuff_ will only be included in the output if the corresponding +# key is set in the value hash. +# +# Usage: Given a set of templates T1, T2, etc +# +# values = { "name" => "Dave", state => "TX" } +# +# t = TemplatePage.new(T1, T2, T3) +# File.open(name, "w") {|f| t.write_html_on(f, values)} +# or +# res = '' +# t.write_html_on(res, values) +# +# + +class TemplatePage + + ########## + # A context holds a stack of key/value pairs (like a symbol + # table). When asked to resolve a key, it first searches the top of + # the stack, then the next level, and so on until it finds a match + # (or runs out of entries) + + class Context + def initialize + @stack = [] + end + + def push(hash) + @stack.push(hash) + end + + def pop + @stack.pop + end + + # Find a scalar value, throwing an exception if not found. This + # method is used when substituting the %xxx% constructs + + def find_scalar(key) + @stack.reverse_each do |level| + if val = level[key] + return val unless val.kind_of? Array + end + end + raise "Template error: can't find variable '#{key}'" + end + + # Lookup any key in the stack of hashes + + def lookup(key) + @stack.reverse_each do |level| + val = level[key] + return val if val + end + nil + end + end + + ######### + # Simple class to read lines out of a string + + class LineReader + # we're initialized with an array of lines + def initialize(lines) + @lines = lines + end + + # read the next line + def read + @lines.shift + end + + # Return a list of lines up to the line that matches + # a pattern. That last line is discarded. + def read_up_to(pattern) + res = [] + while line = read + if pattern.match(line) + return LineReader.new(res) + else + res << line + end + end + raise "Missing end tag in template: #{pattern.source}" + end + + # Return a copy of ourselves that can be modified without + # affecting us + def dup + LineReader.new(@lines.dup) + end + end + + + + # +templates+ is an array of strings containing the templates. + # We start at the first, and substitute in subsequent ones + # where the string !INCLUDE! occurs. For example, + # we could have the overall page template containing + # + # + #

    Master

    + # !INCLUDE! + # + # + # and substitute subpages in to it by passing [master, sub_page]. + # This gives us a cheap way of framing pages + + def initialize(*templates) + result = "!INCLUDE!" + templates.each do |content| + result.sub!(/!INCLUDE!/, content) + end + @lines = LineReader.new(result.split($/)) + end + + # Render the templates into HTML, storing the result on +op+ + # using the method <<. The value_hash contains + # key/value pairs used to drive the substitution (as described above) + + def write_html_on(op, value_hash) + @context = Context.new + op << substitute_into(@lines, value_hash).tr("\000", '\\') + end + + + # Substitute a set of key/value pairs into the given template. + # Keys with scalar values have them substituted directly into + # the page. Those with array values invoke substitute_array + # (below), which examples a block of the template once for each + # row in the array. + # + # This routine also copes with the IF:_key_ directive, + # removing chunks of the template if the corresponding key + # does not appear in the hash, and the START: directive, which + # loops its contents for each value in an array + + def substitute_into(lines, values) + @context.push(values) + skip_to = nil + result = [] + + while line = lines.read + + case line + + when /^IF:(\w+)/ + lines.read_up_to(/^ENDIF:#$1/) unless @context.lookup($1) + + when /^IFNOT:(\w+)/ + lines.read_up_to(/^ENDIF:#$1/) if @context.lookup($1) + + when /^ENDIF:/ + ; + + when /^START:(\w+)/ + tag = $1 + body = lines.read_up_to(/^END:#{tag}/) + inner_values = @context.lookup(tag) + raise "unknown tag: #{tag}" unless inner_values + raise "not array: #{tag}" unless inner_values.kind_of?(Array) + inner_values.each do |vals| + result << substitute_into(body.dup, vals) + end + else + result << expand_line(line.dup) + end + end + + @context.pop + + result.join("\n") + end + + # Given an individual line, we look for %xxx% constructs and + # HREF:ref:name: constructs, substituting for each. + + def expand_line(line) + # Generate a cross reference if a reference is given, + # otherwise just fill in the name part + + line.gsub!(/HREF:(\w+?):(\w+?):/) { + ref = @context.lookup($1) + name = @context.find_scalar($2) + + if ref and !ref.kind_of?(Array) + "#{name}" + else + name + end + } + + # Substitute in values for %xxx% constructs. This is made complex + # because the replacement string may contain characters that are + # meaningful to the regexp (like \1) + + line = line.gsub(/%([a-zA-Z]\w*)%/) { + val = @context.find_scalar($1) + val.tr('\\', "\000") + } + + + line + rescue Exception => e + $stderr.puts "Error in template: #{e}" + $stderr.puts "Original line: #{line}" + exit + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/tokenstream.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/tokenstream.rb new file mode 100644 index 0000000000..0a0720d8a9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/tokenstream.rb @@ -0,0 +1,25 @@ +# A TokenStream is a list of tokens, gathered during the parse +# of some entity (say a method). Entities populate these streams +# by being registered with the lexer. Any class can collect tokens +# by including TokenStream. From the outside, you use such an object +# by calling the start_collecting_tokens method, followed by calls +# to add_token and pop_token + +module TokenStream + def token_stream + @token_stream + end + + def start_collecting_tokens + @token_stream = [] + end + def add_token(tk) + @token_stream << tk + end + def add_tokens(tks) + tks.each {|tk| add_token(tk)} + end + def pop_token + @token_stream.pop + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/usage.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/usage.rb new file mode 100644 index 0000000000..def516b3d7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rdoc/usage.rb @@ -0,0 +1,210 @@ +# = Synopsis +# +# This library allows command-line tools to encapsulate their usage +# as a comment at the top of the main file. Calling RDoc::usage +# then displays some or all of that comment, and optionally exits +# the program with an exit status. We always look for the comment +# in the main program file, so it is safe to call this method +# from anywhere in the executing program. +# +# = Usage +# +# RDoc::usage( [ exit_status ], [ section, ...]) +# RDoc::usage_no_exit( [ section, ...]) +# +# where: +# +# exit_status:: +# the integer exit code (default zero). RDoc::usage will exit +# the calling program with this status. +# +# section:: +# an optional list of section names. If specified, only the +# sections with the given names as headings will be output. +# For example, this section is named 'Usage', and the next +# section is named 'Examples'. The section names are case +# insensitive. +# +# = Examples +# +# # Comment block describing usage +# # with (optional) section headings +# # . . . +# +# require 'rdoc/usage' +# +# # Display all usage and exit with a status of 0 +# +# RDoc::usage +# +# # Display all usage and exit with a status of 99 +# +# RDoc::usage(99) +# +# # Display usage in the 'Summary' section only, then +# # exit with a status of 99 +# +# RDoc::usage(99, 'Summary') +# +# # Display information in the Author and Copyright +# # sections, then exit 0. +# +# RDoc::usage('Author', 'Copyright') +# +# # Display information in the Author and Copyright +# # sections, but don't exit +# +# RDoc::usage_no_exit('Author', 'Copyright') +# +# = Author +# +# Dave Thomas, The Pragmatic Programmers, LLC +# +# = Copyright +# +# Copyright (c) 2004 Dave Thomas. +# Licensed under the same terms as Ruby +# + +require 'rdoc/markup/simple_markup' +require 'rdoc/markup/simple_markup/to_flow' +require 'rdoc/ri/ri_formatter' +require 'rdoc/ri/ri_options' + +module RDoc + + # Display usage information from the comment at the top of + # the file. String arguments identify specific sections of the + # comment to display. An optional integer first argument + # specifies the exit status (defaults to 0) + + def RDoc.usage(*args) + exit_code = 0 + + if args.size > 0 + status = args[0] + if status.respond_to?(:to_int) + exit_code = status.to_int + args.shift + end + end + + # display the usage and exit with the given code + usage_no_exit(*args) + exit(exit_code) + end + + # Display usage + def RDoc.usage_no_exit(*args) + main_program_file = caller[-1].sub(/:\d+$/, '') + comment = File.open(main_program_file) do |file| + find_comment(file) + end + + comment = comment.gsub(/^\s*#/, '') + + markup = SM::SimpleMarkup.new + flow_convertor = SM::ToFlow.new + + flow = markup.convert(comment, flow_convertor) + + format = "plain" + + unless args.empty? + flow = extract_sections(flow, args) + end + + options = RI::Options.instance + if args = ENV["RI"] + options.parse(args.split) + end + formatter = options.formatter.new(options, "") + formatter.display_flow(flow) + end + + ###################################################################### + + private + + # Find the first comment in the file (that isn't a shebang line) + # If the file doesn't start with a comment, report the fact + # and return empty string + + def RDoc.gets(file) + if (line = file.gets) && (line =~ /^#!/) # shebang + throw :exit, find_comment(file) + else + line + end + end + + def RDoc.find_comment(file) + catch(:exit) do + # skip leading blank lines + 0 while (line = gets(file)) && (line =~ /^\s*$/) + + comment = [] + while line && line =~ /^\s*#/ + comment << line + line = gets(file) + end + + 0 while line && (line = gets(file)) + return no_comment if comment.empty? + return comment.join + end + end + + + ##### + # Given an array of flow items and an array of section names, extract those + # sections from the flow which have headings corresponding to + # a section name in the list. Return them in the order + # of names in the +sections+ array. + + def RDoc.extract_sections(flow, sections) + result = [] + sections.each do |name| + name = name.downcase + copy_upto_level = nil + + flow.each do |item| + case item + when SM::Flow::H + if copy_upto_level && item.level >= copy_upto_level + copy_upto_level = nil + else + if item.text.downcase == name + result << item + copy_upto_level = item.level + end + end + else + if copy_upto_level + result << item + end + end + end + end + if result.empty? + puts "Note to developer: requested section(s) [#{sections.join(', ')}] " + + "not found" + result = flow + end + result + end + + ##### + # Report the fact that no doc comment count be found + def RDoc.no_comment + $stderr.puts "No usage information available for this program" + "" + end +end + + +if $0 == __FILE__ + + RDoc::usage(*ARGV) + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/readbytes.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/readbytes.rb new file mode 100644 index 0000000000..ebb5fc0bd4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/readbytes.rb @@ -0,0 +1,41 @@ +# TruncatedDataError is raised when IO#readbytes fails to read enough data. + +class TruncatedDataError["210.251.121.21"], :search=>["ruby-lang.org"], :ndots=>1}) + +--- Resolv::DNS.open(config_info=nil) +--- Resolv::DNS.open(config_info=nil) {|dns| ...} + +=== methods +--- Resolv::DNS#close + +--- Resolv::DNS#getaddress(name) +--- Resolv::DNS#getaddresses(name) +--- Resolv::DNS#each_address(name) {|address| ...} + address lookup methods. + + ((|name|)) must be a instance of Resolv::DNS::Name or String. Lookupped + address is represented as an instance of Resolv::IPv4 or Resolv::IPv6. + +--- Resolv::DNS#getname(address) +--- Resolv::DNS#getnames(address) +--- Resolv::DNS#each_name(address) {|name| ...} + hostnames lookup methods. + + ((|address|)) must be a instance of Resolv::IPv4, Resolv::IPv6 or String. + Lookupped name is represented as an instance of Resolv::DNS::Name. + +--- Resolv::DNS#getresource(name, typeclass) +--- Resolv::DNS#getresources(name, typeclass) +--- Resolv::DNS#each_resource(name, typeclass) {|resource| ...} + They lookup DNS resources of ((|name|)). + ((|name|)) must be a instance of Resolv::Name or String. + + ((|typeclass|)) should be one of follows: + * Resolv::DNS::Resource::IN::ANY + * Resolv::DNS::Resource::IN::NS + * Resolv::DNS::Resource::IN::CNAME + * Resolv::DNS::Resource::IN::SOA + * Resolv::DNS::Resource::IN::HINFO + * Resolv::DNS::Resource::IN::MINFO + * Resolv::DNS::Resource::IN::MX + * Resolv::DNS::Resource::IN::TXT + * Resolv::DNS::Resource::IN::ANY + * Resolv::DNS::Resource::IN::A + * Resolv::DNS::Resource::IN::WKS + * Resolv::DNS::Resource::IN::PTR + * Resolv::DNS::Resource::IN::AAAA + + Lookupped resource is represented as an instance of (a subclass of) + Resolv::DNS::Resource. + (Resolv::DNS::Resource::IN::A, etc.) + +== Resolv::DNS::Resource::IN::NS class +--- name +== Resolv::DNS::Resource::IN::CNAME class +--- name +== Resolv::DNS::Resource::IN::SOA class +--- mname +--- rname +--- serial +--- refresh +--- retry +--- expire +--- minimum +== Resolv::DNS::Resource::IN::HINFO class +--- cpu +--- os +== Resolv::DNS::Resource::IN::MINFO class +--- rmailbx +--- emailbx +== Resolv::DNS::Resource::IN::MX class +--- preference +--- exchange +== Resolv::DNS::Resource::IN::TXT class +--- data +== Resolv::DNS::Resource::IN::A class +--- address +== Resolv::DNS::Resource::IN::WKS class +--- address +--- protocol +--- bitmap +== Resolv::DNS::Resource::IN::PTR class +--- name +== Resolv::DNS::Resource::IN::AAAA class +--- address + +== Resolv::DNS::Name class + +=== class methods +--- Resolv::DNS::Name.create(name) + +=== methods +--- Resolv::DNS::Name#to_s + +== Resolv::DNS::Resource class + +== Resolv::IPv4 class +=== class methods +--- Resolv::IPv4.create(address) + +=== methods +--- Resolv::IPv4#to_s +--- Resolv::IPv4#to_name + +=== constants +--- Resolv::IPv4::Regex + regular expression for IPv4 address. + +== Resolv::IPv6 class +=== class methods +--- Resolv::IPv6.create(address) + +=== methods +--- Resolv::IPv6#to_s +--- Resolv::IPv6#to_name + +=== constants +--- Resolv::IPv6::Regex + regular expression for IPv6 address. + +== Bugs +* NIS is not supported. +* /etc/nsswitch.conf is not supported. +* IPv6 is not supported. + +=end + +require 'socket' +require 'fcntl' +require 'timeout' +require 'thread' + +class Resolv + def self.getaddress(name) + DefaultResolver.getaddress(name) + end + + def self.getaddresses(name) + DefaultResolver.getaddresses(name) + end + + def self.each_address(name, &block) + DefaultResolver.each_address(name, &block) + end + + def self.getname(address) + DefaultResolver.getname(address) + end + + def self.getnames(address) + DefaultResolver.getnames(address) + end + + def self.each_name(address, &proc) + DefaultResolver.each_name(address, &proc) + end + + def initialize(resolvers=[Hosts.new, DNS.new]) + @resolvers = resolvers + end + + def getaddress(name) + each_address(name) {|address| return address} + raise ResolvError.new("no address for #{name}") + end + + def getaddresses(name) + ret = [] + each_address(name) {|address| ret << address} + return ret + end + + def each_address(name) + if AddressRegex =~ name + yield name + return + end + yielded = false + @resolvers.each {|r| + r.each_address(name) {|address| + yield address.to_s + yielded = true + } + return if yielded + } + end + + def getname(address) + each_name(address) {|name| return name} + raise ResolvError.new("no name for #{address}") + end + + def getnames(address) + ret = [] + each_name(address) {|name| ret << name} + return ret + end + + def each_name(address) + yielded = false + @resolvers.each {|r| + r.each_name(address) {|name| + yield name.to_s + yielded = true + } + return if yielded + } + end + + class ResolvError < StandardError + end + + class ResolvTimeout < TimeoutError + end + + class Hosts + if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM + require 'win32/resolv' + DefaultFileName = Win32::Resolv.get_hosts_path + else + DefaultFileName = '/etc/hosts' + end + + def initialize(filename = DefaultFileName) + @filename = filename + @mutex = Mutex.new + @initialized = nil + end + + def lazy_initialize + @mutex.synchronize { + unless @initialized + @name2addr = {} + @addr2name = {} + open(@filename) {|f| + f.each {|line| + line.sub!(/#.*/, '') + addr, hostname, *aliases = line.split(/\s+/) + next unless addr + addr.untaint + hostname.untaint + @addr2name[addr] = [] unless @addr2name.include? addr + @addr2name[addr] << hostname + @addr2name[addr] += aliases + @name2addr[hostname] = [] unless @name2addr.include? hostname + @name2addr[hostname] << addr + aliases.each {|n| + n.untaint + @name2addr[n] = [] unless @name2addr.include? n + @name2addr[n] << addr + } + } + } + @name2addr.each {|name, arr| arr.reverse!} + @initialized = true + end + } + self + end + + def getaddress(name) + each_address(name) {|address| return address} + raise ResolvError.new("#{@filename} has no name: #{name}") + end + + def getaddresses(name) + ret = [] + each_address(name) {|address| ret << address} + return ret + end + + def each_address(name, &proc) + lazy_initialize + if @name2addr.include?(name) + @name2addr[name].each(&proc) + end + end + + def getname(address) + each_name(address) {|name| return name} + raise ResolvError.new("#{@filename} has no address: #{address}") + end + + def getnames(address) + ret = [] + each_name(address) {|name| ret << name} + return ret + end + + def each_name(address, &proc) + lazy_initialize + if @addr2name.include?(address) + @addr2name[address].each(&proc) + end + end + end + + class DNS + # STD0013 (RFC 1035, etc.) + # ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters + + Port = 53 + UDPSize = 512 + + DNSThreadGroup = ThreadGroup.new + + def self.open(*args) + dns = new(*args) + return dns unless block_given? + begin + yield dns + ensure + dns.close + end + end + + def initialize(config_info=nil) + @mutex = Mutex.new + @config = Config.new(config_info) + @initialized = nil + end + + def lazy_initialize + @mutex.synchronize { + unless @initialized + @config.lazy_initialize + + if nameserver = @config.single? + @requester = Requester::ConnectedUDP.new(nameserver) + else + @requester = Requester::UnconnectedUDP.new + end + + @initialized = true + end + } + self + end + + def close + @mutex.synchronize { + if @initialized + @requester.close if @requester + @requester = nil + @initialized = false + end + } + end + + def getaddress(name) + each_address(name) {|address| return address} + raise ResolvError.new("DNS result has no information for #{name}") + end + + def getaddresses(name) + ret = [] + each_address(name) {|address| ret << address} + return ret + end + + def each_address(name) + each_resource(name, Resource::IN::A) {|resource| yield resource.address} + end + + def getname(address) + each_name(address) {|name| return name} + raise ResolvError.new("DNS result has no information for #{address}") + end + + def getnames(address) + ret = [] + each_name(address) {|name| ret << name} + return ret + end + + def each_name(address) + case address + when Name + ptr = address + when IPv4::Regex + ptr = IPv4.create(address).to_name + when IPv6::Regex + ptr = IPv6.create(address).to_name + else + raise ResolvError.new("cannot interpret as address: #{address}") + end + each_resource(ptr, Resource::IN::PTR) {|resource| yield resource.name} + end + + def getresource(name, typeclass) + each_resource(name, typeclass) {|resource| return resource} + raise ResolvError.new("DNS result has no information for #{name}") + end + + def getresources(name, typeclass) + ret = [] + each_resource(name, typeclass) {|resource| ret << resource} + return ret + end + + def each_resource(name, typeclass, &proc) + lazy_initialize + q = Queue.new + senders = {} + begin + @config.resolv(name) {|candidate, tout, nameserver| + msg = Message.new + msg.rd = 1 + msg.add_question(candidate, typeclass) + unless sender = senders[[candidate, nameserver]] + sender = senders[[candidate, nameserver]] = + @requester.sender(msg, candidate, q, nameserver) + end + sender.send + reply = reply_name = nil + timeout(tout, ResolvTimeout) { reply, reply_name = q.pop } + case reply.rcode + when RCode::NoError + extract_resources(reply, reply_name, typeclass, &proc) + return + when RCode::NXDomain + raise Config::NXDomain.new(reply_name.to_s) + else + raise Config::OtherResolvError.new(reply_name.to_s) + end + } + ensure + @requester.delete(q) + end + end + + def extract_resources(msg, name, typeclass) + if typeclass < Resource::ANY + n0 = Name.create(name) + msg.each_answer {|n, ttl, data| + yield data if n0 == n + } + end + yielded = false + n0 = Name.create(name) + msg.each_answer {|n, ttl, data| + if n0 == n + case data + when typeclass + yield data + yielded = true + when Resource::CNAME + n0 = data.name + end + end + } + return if yielded + msg.each_answer {|n, ttl, data| + if n0 == n + case data + when typeclass + yield data + end + end + } + end + + class Requester + def initialize + @senders = {} + end + + def close + thread, sock, @thread, @sock = @thread, @sock + begin + if thread + thread.kill + thread.join + end + ensure + sock.close if sock + end + end + + def delete(arg) + case arg + when Sender + @senders.delete_if {|k, s| s == arg } + when Queue + @senders.delete_if {|k, s| s.queue == arg } + else + raise ArgumentError.new("neither Sender or Queue: #{arg}") + end + end + + class Sender + def initialize(msg, data, sock, queue) + @msg = msg + @data = data + @sock = sock + @queue = queue + end + attr_reader :queue + + def recv(msg) + @queue.push([msg, @data]) + end + end + + class UnconnectedUDP < Requester + def initialize + super() + @sock = UDPSocket.new + @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD + @id = {} + @id.default = -1 + @thread = Thread.new { + DNSThreadGroup.add Thread.current + loop { + reply, from = @sock.recvfrom(UDPSize) + msg = begin + Message.decode(reply) + rescue DecodeError + STDERR.print("DNS message decoding error: #{reply.inspect}\n") + next + end + if s = @senders[[[from[3],from[1]],msg.id]] + s.recv msg + else + #STDERR.print("non-handled DNS message: #{msg.inspect} from #{from.inspect}\n") + end + } + } + end + + def sender(msg, data, queue, host, port=Port) + service = [host, port] + id = Thread.exclusive { + @id[service] = (@id[service] + 1) & 0xffff + } + request = msg.encode + request[0,2] = [id].pack('n') + return @senders[[service, id]] = + Sender.new(request, data, @sock, host, port, queue) + end + + class Sender < Requester::Sender + def initialize(msg, data, sock, host, port, queue) + super(msg, data, sock, queue) + @host = host + @port = port + end + + def send + @sock.send(@msg, 0, @host, @port) + end + end + end + + class ConnectedUDP < Requester + def initialize(host, port=Port) + super() + @host = host + @port = port + @sock = UDPSocket.new(host.index(':') ? Socket::AF_INET6 : Socket::AF_INET) + @sock.connect(host, port) + @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD + @id = -1 + @thread = Thread.new { + DNSThreadGroup.add Thread.current + loop { + reply = @sock.recv(UDPSize) + msg = begin + Message.decode(reply) + rescue DecodeError + STDERR.print("DNS message decoding error: #{reply.inspect}") + next + end + if s = @senders[msg.id] + s.recv msg + else + #STDERR.print("non-handled DNS message: #{msg.inspect}") + end + } + } + end + + def sender(msg, data, queue, host=@host, port=@port) + unless host == @host && port == @port + raise RequestError.new("host/port don't match: #{host}:#{port}") + end + id = Thread.exclusive { @id = (@id + 1) & 0xffff } + request = msg.encode + request[0,2] = [id].pack('n') + return @senders[id] = Sender.new(request, data, @sock, queue) + end + + class Sender < Requester::Sender + def send + @sock.send(@msg, 0) + end + end + end + + class TCP < Requester + def initialize(host, port=Port) + super() + @host = host + @port = port + @sock = TCPSocket.new + @sock.connect(host, port) + @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD + @id = -1 + @senders = {} + @thread = Thread.new { + DNSThreadGroup.add Thread.current + loop { + len = @sock.read(2).unpack('n') + reply = @sock.read(len) + msg = begin + Message.decode(reply) + rescue DecodeError + STDERR.print("DNS message decoding error: #{reply.inspect}") + next + end + if s = @senders[msg.id] + s.push msg + else + #STDERR.print("non-handled DNS message: #{msg.inspect}") + end + } + } + end + + def sender(msg, data, queue, host=@host, port=@port) + unless host == @host && port == @port + raise RequestError.new("host/port don't match: #{host}:#{port}") + end + id = Thread.exclusive { @id = (@id + 1) & 0xffff } + request = msg.encode + request[0,2] = [request.length, id].pack('nn') + return @senders[id] = Sender.new(request, data, @sock, queue) + end + + class Sender < Requester::Sender + def send + @sock.print(@msg) + @sock.flush + end + end + end + + class RequestError < StandardError + end + end + + class Config + def initialize(config_info=nil) + @mutex = Mutex.new + @config_info = config_info + @initialized = nil + end + + def Config.parse_resolv_conf(filename) + nameserver = [] + search = nil + ndots = 1 + open(filename) {|f| + f.each {|line| + line.sub!(/[#;].*/, '') + keyword, *args = line.split(/\s+/) + args.each { |arg| + arg.untaint + } + next unless keyword + case keyword + when 'nameserver' + nameserver += args + when 'domain' + next if args.empty? + search = [args[0]] + when 'search' + next if args.empty? + search = args + when 'options' + args.each {|arg| + case arg + when /\Andots:(\d+)\z/ + ndots = $1.to_i + end + } + end + } + } + return { :nameserver => nameserver, :search => search, :ndots => ndots } + end + + def Config.default_config_hash(filename="/etc/resolv.conf") + if File.exist? filename + config_hash = Config.parse_resolv_conf(filename) + else + if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM + search, nameserver = Win32::Resolv.get_resolv_info + config_hash = {} + config_hash[:nameserver] = nameserver if nameserver + config_hash[:search] = [search].flatten if search + end + end + config_hash + end + + def lazy_initialize + @mutex.synchronize { + unless @initialized + @nameserver = [] + @search = nil + @ndots = 1 + case @config_info + when nil + config_hash = Config.default_config_hash + when String + config_hash = Config.parse_resolv_conf(@config_info) + when Hash + config_hash = @config_info.dup + if String === config_hash[:nameserver] + config_hash[:nameserver] = [config_hash[:nameserver]] + end + if String === config_hash[:search] + config_hash[:search] = [config_hash[:search]] + end + else + raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}") + end + @nameserver = config_hash[:nameserver] if config_hash.include? :nameserver + @search = config_hash[:search] if config_hash.include? :search + @ndots = config_hash[:ndots] if config_hash.include? :ndots + + @nameserver = ['0.0.0.0'] if @nameserver.empty? + if @search + @search = @search.map {|arg| Label.split(arg) } + else + hostname = Socket.gethostname + if /\./ =~ hostname + @search = [Label.split($')] + else + @search = [[]] + end + end + + if !@nameserver.kind_of?(Array) || + !@nameserver.all? {|ns| String === ns } + raise ArgumentError.new("invalid nameserver config: #{@nameserver.inspect}") + end + + if !@search.kind_of?(Array) || + !@search.all? {|ls| ls.all? {|l| Label::Str === l } } + raise ArgumentError.new("invalid search config: #{@search.inspect}") + end + + if !@ndots.kind_of?(Integer) + raise ArgumentError.new("invalid ndots config: #{@ndots.inspect}") + end + + @initialized = true + end + } + self + end + + def single? + lazy_initialize + if @nameserver.length == 1 + return @nameserver[0] + else + return nil + end + end + + def generate_candidates(name) + candidates = nil + name = Name.create(name) + if name.absolute? + candidates = [name] + else + if @ndots <= name.length - 1 + candidates = [Name.new(name.to_a)] + else + candidates = [] + end + candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)}) + end + return candidates + end + + InitialTimeout = 5 + + def generate_timeouts + ts = [InitialTimeout] + ts << ts[-1] * 2 / @nameserver.length + ts << ts[-1] * 2 + ts << ts[-1] * 2 + return ts + end + + def resolv(name) + candidates = generate_candidates(name) + timeouts = generate_timeouts + begin + candidates.each {|candidate| + begin + timeouts.each {|tout| + @nameserver.each {|nameserver| + begin + yield candidate, tout, nameserver + rescue ResolvTimeout + end + } + } + raise ResolvError.new("DNS resolv timeout: #{name}") + rescue NXDomain + end + } + rescue ResolvError + end + end + + class NXDomain < ResolvError + end + + class OtherResolvError < ResolvError + end + end + + module OpCode + Query = 0 + IQuery = 1 + Status = 2 + Notify = 4 + Update = 5 + end + + module RCode + NoError = 0 + FormErr = 1 + ServFail = 2 + NXDomain = 3 + NotImp = 4 + Refused = 5 + YXDomain = 6 + YXRRSet = 7 + NXRRSet = 8 + NotAuth = 9 + NotZone = 10 + BADVERS = 16 + BADSIG = 16 + BADKEY = 17 + BADTIME = 18 + BADMODE = 19 + BADNAME = 20 + BADALG = 21 + end + + class DecodeError < StandardError + end + + class EncodeError < StandardError + end + + module Label + def self.split(arg) + labels = [] + arg.scan(/[^\.]+/) {labels << Str.new($&)} + return labels + end + + class Str + def initialize(string) + @string = string + @downcase = string.downcase + end + attr_reader :string, :downcase + + def to_s + return @string + end + + def inspect + return "#<#{self.class} #{self.to_s}>" + end + + def ==(other) + return @downcase == other.downcase + end + + def eql?(other) + return self == other + end + + def hash + return @downcase.hash + end + end + end + + class Name + def self.create(arg) + case arg + when Name + return arg + when String + return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false) + else + raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}") + end + end + + def initialize(labels, absolute=true) + @labels = labels + @absolute = absolute + end + + def inspect + "#<#{self.class}: #{self.to_s}#{@absolute ? '.' : ''}>" + end + + def absolute? + return @absolute + end + + def ==(other) + return false unless Name === other + return @labels == other.to_a && @absolute == other.absolute? + end + alias eql? == + + # tests subdomain-of relation. + # + # domain = Resolv::DNS::Name.create("y.z") + # p Resolv::DNS::Name.create("w.x.y.z").subdomain_of?(domain) #=> true + # p Resolv::DNS::Name.create("x.y.z").subdomain_of?(domain) #=> true + # p Resolv::DNS::Name.create("y.z").subdomain_of?(domain) #=> false + # p Resolv::DNS::Name.create("z").subdomain_of?(domain) #=> false + # p Resolv::DNS::Name.create("x.y.z.").subdomain_of?(domain) #=> false + # p Resolv::DNS::Name.create("w.z").subdomain_of?(domain) #=> false + # + def subdomain_of?(other) + raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other + return false if @absolute != other.absolute? + other_len = other.length + return false if @labels.length <= other_len + return @labels[-other_len, other_len] == other.to_a + end + + def hash + return @labels.hash ^ @absolute.hash + end + + def to_a + return @labels + end + + def length + return @labels.length + end + + def [](i) + return @labels[i] + end + + # returns the domain name as a string. + # + # The domain name doesn't have a trailing dot even if the name object is + # absolute. + # + # p Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z" + # p Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z" + # + def to_s + return @labels.join('.') + end + end + + class Message + @@identifier = -1 + + def initialize(id = (@@identifier += 1) & 0xffff) + @id = id + @qr = 0 + @opcode = 0 + @aa = 0 + @tc = 0 + @rd = 0 # recursion desired + @ra = 0 # recursion available + @rcode = 0 + @question = [] + @answer = [] + @authority = [] + @additional = [] + end + + attr_accessor :id, :qr, :opcode, :aa, :tc, :rd, :ra, :rcode + attr_reader :question, :answer, :authority, :additional + + def ==(other) + return @id == other.id && + @qr == other.qr && + @opcode == other.opcode && + @aa == other.aa && + @tc == other.tc && + @rd == other.rd && + @ra == other.ra && + @rcode == other.rcode && + @question == other.question && + @answer == other.answer && + @authority == other.authority && + @additional == other.additional + end + + def add_question(name, typeclass) + @question << [Name.create(name), typeclass] + end + + def each_question + @question.each {|name, typeclass| + yield name, typeclass + } + end + + def add_answer(name, ttl, data) + @answer << [Name.create(name), ttl, data] + end + + def each_answer + @answer.each {|name, ttl, data| + yield name, ttl, data + } + end + + def add_authority(name, ttl, data) + @authority << [Name.create(name), ttl, data] + end + + def each_authority + @authority.each {|name, ttl, data| + yield name, ttl, data + } + end + + def add_additional(name, ttl, data) + @additional << [Name.create(name), ttl, data] + end + + def each_additional + @additional.each {|name, ttl, data| + yield name, ttl, data + } + end + + def each_resource + each_answer {|name, ttl, data| yield name, ttl, data} + each_authority {|name, ttl, data| yield name, ttl, data} + each_additional {|name, ttl, data| yield name, ttl, data} + end + + def encode + return MessageEncoder.new {|msg| + msg.put_pack('nnnnnn', + @id, + (@qr & 1) << 15 | + (@opcode & 15) << 11 | + (@aa & 1) << 10 | + (@tc & 1) << 9 | + (@rd & 1) << 8 | + (@ra & 1) << 7 | + (@rcode & 15), + @question.length, + @answer.length, + @authority.length, + @additional.length) + @question.each {|q| + name, typeclass = q + msg.put_name(name) + msg.put_pack('nn', typeclass::TypeValue, typeclass::ClassValue) + } + [@answer, @authority, @additional].each {|rr| + rr.each {|r| + name, ttl, data = r + msg.put_name(name) + msg.put_pack('nnN', data.class::TypeValue, data.class::ClassValue, ttl) + msg.put_length16 {data.encode_rdata(msg)} + } + } + }.to_s + end + + class MessageEncoder + def initialize + @data = '' + @names = {} + yield self + end + + def to_s + return @data + end + + def put_bytes(d) + @data << d + end + + def put_pack(template, *d) + @data << d.pack(template) + end + + def put_length16 + length_index = @data.length + @data << "\0\0" + data_start = @data.length + yield + data_end = @data.length + @data[length_index, 2] = [data_end - data_start].pack("n") + end + + def put_string(d) + self.put_pack("C", d.length) + @data << d + end + + def put_string_list(ds) + ds.each {|d| + self.put_string(d) + } + end + + def put_name(d) + put_labels(d.to_a) + end + + def put_labels(d) + d.each_index {|i| + domain = d[i..-1] + if idx = @names[domain] + self.put_pack("n", 0xc000 | idx) + return + else + @names[domain] = @data.length + self.put_label(d[i]) + end + } + @data << "\0" + end + + def put_label(d) + self.put_string(d.string) + end + end + + def Message.decode(m) + o = Message.new(0) + MessageDecoder.new(m) {|msg| + id, flag, qdcount, ancount, nscount, arcount = + msg.get_unpack('nnnnnn') + o.id = id + o.qr = (flag >> 15) & 1 + o.opcode = (flag >> 11) & 15 + o.aa = (flag >> 10) & 1 + o.tc = (flag >> 9) & 1 + o.rd = (flag >> 8) & 1 + o.ra = (flag >> 7) & 1 + o.rcode = flag & 15 + (1..qdcount).each { + name, typeclass = msg.get_question + o.add_question(name, typeclass) + } + (1..ancount).each { + name, ttl, data = msg.get_rr + o.add_answer(name, ttl, data) + } + (1..nscount).each { + name, ttl, data = msg.get_rr + o.add_authority(name, ttl, data) + } + (1..arcount).each { + name, ttl, data = msg.get_rr + o.add_additional(name, ttl, data) + } + } + return o + end + + class MessageDecoder + def initialize(data) + @data = data + @index = 0 + @limit = data.length + yield self + end + + def get_length16 + len, = self.get_unpack('n') + save_limit = @limit + @limit = @index + len + d = yield(len) + if @index < @limit + raise DecodeError.new("junk exists") + elsif @limit < @index + raise DecodeError.new("limit exceeded") + end + @limit = save_limit + return d + end + + def get_bytes(len = @limit - @index) + d = @data[@index, len] + @index += len + return d + end + + def get_unpack(template) + len = 0 + template.each_byte {|byte| + case byte + when ?c, ?C + len += 1 + when ?n + len += 2 + when ?N + len += 4 + else + raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'") + end + } + raise DecodeError.new("limit exceeded") if @limit < @index + len + arr = @data.unpack("@#{@index}#{template}") + @index += len + return arr + end + + def get_string + len = @data[@index] + raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len + d = @data[@index + 1, len] + @index += 1 + len + return d + end + + def get_string_list + strings = [] + while @index < @limit + strings << self.get_string + end + strings + end + + def get_name + return Name.new(self.get_labels) + end + + def get_labels(limit=nil) + limit = @index if !limit || @index < limit + d = [] + while true + case @data[@index] + when 0 + @index += 1 + return d + when 192..255 + idx = self.get_unpack('n')[0] & 0x3fff + if limit <= idx + raise DecodeError.new("non-backward name pointer") + end + save_index = @index + @index = idx + d += self.get_labels(limit) + @index = save_index + return d + else + d << self.get_label + end + end + return d + end + + def get_label + return Label::Str.new(self.get_string) + end + + def get_question + name = self.get_name + type, klass = self.get_unpack("nn") + return name, Resource.get_class(type, klass) + end + + def get_rr + name = self.get_name + type, klass, ttl = self.get_unpack('nnN') + typeclass = Resource.get_class(type, klass) + return name, ttl, self.get_length16 {typeclass.decode_rdata(self)} + end + end + end + + class Query + def encode_rdata(msg) + raise EncodeError.new("#{self.class} is query.") + end + + def self.decode_rdata(msg) + raise DecodeError.new("#{self.class} is query.") + end + end + + class Resource < Query + ClassHash = {} + + def encode_rdata(msg) + raise NotImplementedError.new + end + + def self.decode_rdata(msg) + raise NotImplementedError.new + end + + def ==(other) + return self.class == other.class && + self.instance_variables == other.instance_variables && + self.instance_variables.collect {|name| self.instance_eval name} == + other.instance_variables.collect {|name| other.instance_eval name} + end + + def eql?(other) + return self == other + end + + def hash + h = 0 + self.instance_variables.each {|name| + h ^= self.instance_eval("#{name}.hash") + } + return h + end + + def self.get_class(type_value, class_value) + return ClassHash[[type_value, class_value]] || + Generic.create(type_value, class_value) + end + + class Generic < Resource + def initialize(data) + @data = data + end + attr_reader :data + + def encode_rdata(msg) + msg.put_bytes(data) + end + + def self.decode_rdata(msg) + return self.new(msg.get_bytes) + end + + def self.create(type_value, class_value) + c = Class.new(Generic) + c.const_set(:TypeValue, type_value) + c.const_set(:ClassValue, class_value) + Generic.const_set("Type#{type_value}_Class#{class_value}", c) + ClassHash[[type_value, class_value]] = c + return c + end + end + + class DomainName < Resource + def initialize(name) + @name = name + end + attr_reader :name + + def encode_rdata(msg) + msg.put_name(@name) + end + + def self.decode_rdata(msg) + return self.new(msg.get_name) + end + end + + # Standard (class generic) RRs + ClassValue = nil + + class NS < DomainName + TypeValue = 2 + end + + class CNAME < DomainName + TypeValue = 5 + end + + class SOA < Resource + TypeValue = 6 + + def initialize(mname, rname, serial, refresh, retry_, expire, minimum) + @mname = mname + @rname = rname + @serial = serial + @refresh = refresh + @retry = retry_ + @expire = expire + @minimum = minimum + end + attr_reader :mname, :rname, :serial, :refresh, :retry, :expire, :minimum + + def encode_rdata(msg) + msg.put_name(@mname) + msg.put_name(@rname) + msg.put_pack('NNNNN', @serial, @refresh, @retry, @expire, @minimum) + end + + def self.decode_rdata(msg) + mname = msg.get_name + rname = msg.get_name + serial, refresh, retry_, expire, minimum = msg.get_unpack('NNNNN') + return self.new( + mname, rname, serial, refresh, retry_, expire, minimum) + end + end + + class PTR < DomainName + TypeValue = 12 + end + + class HINFO < Resource + TypeValue = 13 + + def initialize(cpu, os) + @cpu = cpu + @os = os + end + attr_reader :cpu, :os + + def encode_rdata(msg) + msg.put_string(@cpu) + msg.put_string(@os) + end + + def self.decode_rdata(msg) + cpu = msg.get_string + os = msg.get_string + return self.new(cpu, os) + end + end + + class MINFO < Resource + TypeValue = 14 + + def initialize(rmailbx, emailbx) + @rmailbx = rmailbx + @emailbx = emailbx + end + attr_reader :rmailbx, :emailbx + + def encode_rdata(msg) + msg.put_name(@rmailbx) + msg.put_name(@emailbx) + end + + def self.decode_rdata(msg) + rmailbx = msg.get_string + emailbx = msg.get_string + return self.new(rmailbx, emailbx) + end + end + + class MX < Resource + TypeValue= 15 + + def initialize(preference, exchange) + @preference = preference + @exchange = exchange + end + attr_reader :preference, :exchange + + def encode_rdata(msg) + msg.put_pack('n', @preference) + msg.put_name(@exchange) + end + + def self.decode_rdata(msg) + preference, = msg.get_unpack('n') + exchange = msg.get_name + return self.new(preference, exchange) + end + end + + class TXT < Resource + TypeValue = 16 + + def initialize(first_string, *rest_strings) + @strings = [first_string, *rest_strings] + end + attr_reader :strings + + def data + @strings[0] + end + + def encode_rdata(msg) + msg.put_string_list(@strings) + end + + def self.decode_rdata(msg) + strings = msg.get_string_list + return self.new(*strings) + end + end + + class ANY < Query + TypeValue = 255 + end + + ClassInsensitiveTypes = [ + NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, ANY + ] + + # ARPA Internet specific RRs + module IN + ClassValue = 1 + + ClassInsensitiveTypes.each {|s| + c = Class.new(s) + c.const_set(:TypeValue, s::TypeValue) + c.const_set(:ClassValue, ClassValue) + ClassHash[[s::TypeValue, ClassValue]] = c + self.const_set(s.name.sub(/.*::/, ''), c) + } + + class A < Resource + ClassHash[[TypeValue = 1, ClassValue = ClassValue]] = self + + def initialize(address) + @address = IPv4.create(address) + end + attr_reader :address + + def encode_rdata(msg) + msg.put_bytes(@address.address) + end + + def self.decode_rdata(msg) + return self.new(IPv4.new(msg.get_bytes(4))) + end + end + + class WKS < Resource + ClassHash[[TypeValue = 11, ClassValue = ClassValue]] = self + + def initialize(address, protocol, bitmap) + @address = IPv4.create(address) + @protocol = protocol + @bitmap = bitmap + end + attr_reader :address, :protocol, :bitmap + + def encode_rdata(msg) + msg.put_bytes(@address.address) + msg.put_pack("n", @protocol) + msg.put_bytes(@bitmap) + end + + def self.decode_rdata(msg) + address = IPv4.new(msg.get_bytes(4)) + protocol, = msg.get_unpack("n") + bitmap = msg.get_bytes + return self.new(address, protocol, bitmap) + end + end + + class AAAA < Resource + ClassHash[[TypeValue = 28, ClassValue = ClassValue]] = self + + def initialize(address) + @address = IPv6.create(address) + end + attr_reader :address + + def encode_rdata(msg) + msg.put_bytes(@address.address) + end + + def self.decode_rdata(msg) + return self.new(IPv6.new(msg.get_bytes(16))) + end + end + + # SRV resource record defined in RFC 2782 + # + # These records identify the hostname and port that a service is + # available at. + # + # The format is: + # _Service._Proto.Name TTL Class SRV Priority Weight Port Target + # + # The fields specific to SRV are defined in RFC 2782 as meaning: + # - +priority+ The priority of this target host. A client MUST attempt + # to contact the target host with the lowest-numbered priority it can + # reach; target hosts with the same priority SHOULD be tried in an + # order defined by the weight field. The range is 0-65535. Note that + # it is not widely implemented and should be set to zero. + # + # - +weight+ A server selection mechanism. The weight field specifies + # a relative weight for entries with the same priority. Larger weights + # SHOULD be given a proportionately higher probability of being + # selected. The range of this number is 0-65535. Domain administrators + # SHOULD use Weight 0 when there isn't any server selection to do, to + # make the RR easier to read for humans (less noisy). Note that it is + # not widely implemented and should be set to zero. + # + # - +port+ The port on this target host of this service. The range is 0- + # 65535. + # + # - +target+ The domain name of the target host. A target of "." means + # that the service is decidedly not available at this domain. + class SRV < Resource + ClassHash[[TypeValue = 33, ClassValue = ClassValue]] = self + + # Create a SRV resource record. + def initialize(priority, weight, port, target) + @priority = priority.to_int + @weight = weight.to_int + @port = port.to_int + @target = Name.create(target) + end + + attr_reader :priority, :weight, :port, :target + + def encode_rdata(msg) + msg.put_pack("n", @priority) + msg.put_pack("n", @weight) + msg.put_pack("n", @port) + msg.put_name(@target) + end + + def self.decode_rdata(msg) + priority, = msg.get_unpack("n") + weight, = msg.get_unpack("n") + port, = msg.get_unpack("n") + target = msg.get_name + return self.new(priority, weight, port, target) + end + end + + end + end + end + + class IPv4 + Regex = /\A(\d+)\.(\d+)\.(\d+)\.(\d+)\z/ + + def self.create(arg) + case arg + when IPv4 + return arg + when Regex + if (0..255) === (a = $1.to_i) && + (0..255) === (b = $2.to_i) && + (0..255) === (c = $3.to_i) && + (0..255) === (d = $4.to_i) + return self.new([a, b, c, d].pack("CCCC")) + else + raise ArgumentError.new("IPv4 address with invalid value: " + arg) + end + else + raise ArgumentError.new("cannot interpret as IPv4 address: #{arg.inspect}") + end + end + + def initialize(address) + unless address.kind_of?(String) && address.length == 4 + raise ArgumentError.new('IPv4 address must be 4 bytes') + end + @address = address + end + attr_reader :address + + def to_s + return sprintf("%d.%d.%d.%d", *@address.unpack("CCCC")) + end + + def inspect + return "#<#{self.class} #{self.to_s}>" + end + + def to_name + return DNS::Name.create( + '%d.%d.%d.%d.in-addr.arpa.' % @address.unpack('CCCC').reverse) + end + + def ==(other) + return @address == other.address + end + + def eql?(other) + return self == other + end + + def hash + return @address.hash + end + end + + class IPv6 + Regex_8Hex = /\A + (?:[0-9A-Fa-f]{1,4}:){7} + [0-9A-Fa-f]{1,4} + \z/x + + Regex_CompressedHex = /\A + ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) :: + ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) + \z/x + + Regex_6Hex4Dec = /\A + ((?:[0-9A-Fa-f]{1,4}:){6,6}) + (\d+)\.(\d+)\.(\d+)\.(\d+) + \z/x + + Regex_CompressedHex4Dec = /\A + ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) :: + ((?:[0-9A-Fa-f]{1,4}:)*) + (\d+)\.(\d+)\.(\d+)\.(\d+) + \z/x + + Regex = / + (?:#{Regex_8Hex}) | + (?:#{Regex_CompressedHex}) | + (?:#{Regex_6Hex4Dec}) | + (?:#{Regex_CompressedHex4Dec})/x + + def self.create(arg) + case arg + when IPv6 + return arg + when String + address = '' + if Regex_8Hex =~ arg + arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')} + elsif Regex_CompressedHex =~ arg + prefix = $1 + suffix = $2 + a1 = '' + a2 = '' + prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')} + suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')} + omitlen = 16 - a1.length - a2.length + address << a1 << "\0" * omitlen << a2 + elsif Regex_6Hex4Dec =~ arg + prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i + if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d + prefix.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')} + address << [a, b, c, d].pack('CCCC') + else + raise ArgumentError.new("not numeric IPv6 address: " + arg) + end + elsif Regex_CompressedHex4Dec =~ arg + prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i + if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d + a1 = '' + a2 = '' + prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')} + suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')} + omitlen = 12 - a1.length - a2.length + address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC') + else + raise ArgumentError.new("not numeric IPv6 address: " + arg) + end + else + raise ArgumentError.new("not numeric IPv6 address: " + arg) + end + return IPv6.new(address) + else + raise ArgumentError.new("cannot interpret as IPv6 address: #{arg.inspect}") + end + end + + def initialize(address) + unless address.kind_of?(String) && address.length == 16 + raise ArgumentError.new('IPv6 address must be 16 bytes') + end + @address = address + end + attr_reader :address + + def to_s + address = sprintf("%X:%X:%X:%X:%X:%X:%X:%X", *@address.unpack("nnnnnnnn")) + unless address.sub!(/(^|:)0(:0)+(:|$)/, '::') + address.sub!(/(^|:)0(:|$)/, '::') + end + return address + end + + def inspect + return "#<#{self.class} #{self.to_s}>" + end + + def to_name + # ip6.arpa should be searched too. [RFC3152] + return DNS::Name.new( + @address.unpack("H32")[0].split(//).reverse + ['ip6', 'int']) + end + + def ==(other) + return @address == other.address + end + + def eql?(other) + return self == other + end + + def hash + return @address.hash + end + end + + DefaultResolver = self.new + AddressRegex = /(?:#{IPv4::Regex})|(?:#{IPv6::Regex})/ +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/attlistdecl.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/attlistdecl.rb new file mode 100644 index 0000000000..d4b5c38af6 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/attlistdecl.rb @@ -0,0 +1,62 @@ +#vim:ts=2 sw=2 noexpandtab: +require 'rexml/child' +require 'rexml/source' + +module REXML + # This class needs: + # * Documentation + # * Work! Not all types of attlists are intelligently parsed, so we just + # spew back out what we get in. This works, but it would be better if + # we formatted the output ourselves. + # + # AttlistDecls provide *just* enough support to allow namespace + # declarations. If you need some sort of generalized support, or have an + # interesting idea about how to map the hideous, terrible design of DTD + # AttlistDecls onto an intuitive Ruby interface, let me know. I'm desperate + # for anything to make DTDs more palateable. + class AttlistDecl < Child + include Enumerable + + # What is this? Got me. + attr_reader :element_name + + # Create an AttlistDecl, pulling the information from a Source. Notice + # that this isn't very convenient; to create an AttlistDecl, you basically + # have to format it yourself, and then have the initializer parse it. + # Sorry, but for the forseeable future, DTD support in REXML is pretty + # weak on convenience. Have I mentioned how much I hate DTDs? + def initialize(source) + super() + if (source.kind_of? Array) + @element_name, @pairs, @contents = *source + end + end + + # Access the attlist attribute/value pairs. + # value = attlist_decl[ attribute_name ] + def [](key) + @pairs[key] + end + + # Whether an attlist declaration includes the given attribute definition + # if attlist_decl.include? "xmlns:foobar" + def include?(key) + @pairs.keys.include? key + end + + # Itterate over the key/value pairs: + # attlist_decl.each { |attribute_name, attribute_value| ... } + def each(&block) + @pairs.each(&block) + end + + # Write out exactly what we got in. + def write out, indent=-1 + out << @contents + end + + def node_type + :attlistdecl + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/attribute.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/attribute.rb new file mode 100644 index 0000000000..029035d675 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/attribute.rb @@ -0,0 +1,185 @@ +require "rexml/namespace" +require 'rexml/text' + +module REXML + # Defines an Element Attribute; IE, a attribute=value pair, as in: + # . Attributes can be in their own + # namespaces. General users of REXML will not interact with the + # Attribute class much. + class Attribute + include Node + include Namespace + + # The element to which this attribute belongs + attr_reader :element + # The normalized value of this attribute. That is, the attribute with + # entities intact. + attr_writer :normalized + PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\2/um + + # Constructor. + # FIXME: The parser doesn't catch illegal characters in attributes + # + # first:: + # Either: an Attribute, which this new attribute will become a + # clone of; or a String, which is the name of this attribute + # second:: + # If +first+ is an Attribute, then this may be an Element, or nil. + # If nil, then the Element parent of this attribute is the parent + # of the +first+ Attribute. If the first argument is a String, + # then this must also be a String, and is the content of the attribute. + # If this is the content, it must be fully normalized (contain no + # illegal characters). + # parent:: + # Ignored unless +first+ is a String; otherwise, may be the Element + # parent of this attribute, or nil. + # + # + # Attribute.new( attribute_to_clone ) + # Attribute.new( attribute_to_clone, parent_element ) + # Attribute.new( "attr", "attr_value" ) + # Attribute.new( "attr", "attr_value", parent_element ) + def initialize( first, second=nil, parent=nil ) + @normalized = @unnormalized = @element = nil + if first.kind_of? Attribute + self.name = first.expanded_name + @unnormalized = first.value + if second.kind_of? Element + @element = second + else + @element = first.element + end + elsif first.kind_of? String + @element = parent if parent.kind_of? Element + self.name = first + @normalized = second.to_s + else + raise "illegal argument #{first.class.name} to Attribute constructor" + end + end + + # Returns the namespace of the attribute. + # + # e = Element.new( "elns:myelement" ) + # e.add_attribute( "nsa:a", "aval" ) + # e.add_attribute( "b", "bval" ) + # e.attributes.get_attribute( "a" ).prefix # -> "nsa" + # e.attributes.get_attribute( "b" ).prefix # -> "elns" + # a = Attribute.new( "x", "y" ) + # a.prefix # -> "" + def prefix + pf = super + if pf == "" + pf = @element.prefix if @element + end + pf + end + + # Returns the namespace URL, if defined, or nil otherwise + # + # e = Element.new("el") + # e.add_attributes({"xmlns:ns", "http://url"}) + # e.namespace( "ns" ) # -> "http://url" + def namespace arg=nil + arg = prefix if arg.nil? + @element.namespace arg + end + + # Returns true if other is an Attribute and has the same name and value, + # false otherwise. + def ==( other ) + other.kind_of?(Attribute) and other.name==name and other.value==value + end + + # Creates (and returns) a hash from both the name and value + def hash + name.hash + value.hash + end + + # Returns this attribute out as XML source, expanding the name + # + # a = Attribute.new( "x", "y" ) + # a.to_string # -> "x='y'" + # b = Attribute.new( "ns:x", "y" ) + # b.to_string # -> "ns:x='y'" + def to_string + if @element and @element.context and @element.context[:attribute_quote] == :quote + %Q^#@expanded_name="#{to_s().gsub(/"/, '"e;')}"^ + else + "#@expanded_name='#{to_s().gsub(/'/, ''')}'" + end + end + + # Returns the attribute value, with entities replaced + def to_s + return @normalized if @normalized + + doctype = nil + if @element + doc = @element.document + doctype = doc.doctype if doc + end + + @normalized = Text::normalize( @unnormalized, doctype ) + @unnormalized = nil + @normalized + end + + # Returns the UNNORMALIZED value of this attribute. That is, entities + # have been expanded to their values + def value + return @unnormalized if @unnormalized + doctype = nil + if @element + doc = @element.document + doctype = doc.doctype if doc + end + @unnormalized = Text::unnormalize( @normalized, doctype ) + @normalized = nil + @unnormalized + end + + # Returns a copy of this attribute + def clone + Attribute.new self + end + + # Sets the element of which this object is an attribute. Normally, this + # is not directly called. + # + # Returns this attribute + def element=( element ) + @element = element + self + end + + # Removes this Attribute from the tree, and returns true if successfull + # + # This method is usually not called directly. + def remove + @element.attributes.delete self.name unless @element.nil? + end + + # Writes this attribute (EG, puts 'key="value"' to the output) + def write( output, indent=-1 ) + output << to_string + end + + def node_type + :attribute + end + + def inspect + rv = "" + write( rv ) + rv + end + + def xpath + path = @element.xpath + path += "/@#{self.expanded_name}" + return path + end + end +end +#vim:ts=2 sw=2 noexpandtab: diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/cdata.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/cdata.rb new file mode 100644 index 0000000000..efcb71160a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/cdata.rb @@ -0,0 +1,67 @@ +require "rexml/text" + +module REXML + class CData < Text + START = '' + ILLEGAL = /(\]\]>)/ + + # Constructor. CData is data between + # + # _Examples_ + # CData.new( source ) + # CData.new( "Here is some CDATA" ) + # CData.new( "Some unprocessed data", respect_whitespace_TF, parent_element ) + def initialize( first, whitespace=true, parent=nil ) + super( first, whitespace, parent, true, true, ILLEGAL ) + end + + # Make a copy of this object + # + # _Examples_ + # c = CData.new( "Some text" ) + # d = c.clone + # d.to_s # -> "Some text" + def clone + CData.new self + end + + # Returns the content of this CData object + # + # _Examples_ + # c = CData.new( "Some text" ) + # c.to_s # -> "Some text" + def to_s + @string + end + + def value + @string + end + + # == DEPRECATED + # See the rexml/formatters package + # + # Generates XML output of this object + # + # output:: + # Where to write the string. Defaults to $stdout + # indent:: + # The amount to indent this node by + # transitive:: + # Ignored + # ie_hack:: + # Ignored + # + # _Examples_ + # c = CData.new( " Some text " ) + # c.write( $stdout ) #-> + def write( output=$stdout, indent=-1, transitive=false, ie_hack=false ) + Kernel.warn( "#{self.class.name}.write is deprecated" ) + indent( output, indent ) + output << START + output << @string + output << STOP + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/child.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/child.rb new file mode 100644 index 0000000000..6d3c9df5e6 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/child.rb @@ -0,0 +1,96 @@ +require "rexml/node" + +module REXML + ## + # A Child object is something contained by a parent, and this class + # contains methods to support that. Most user code will not use this + # class directly. + class Child + include Node + attr_reader :parent # The Parent of this object + + # Constructor. Any inheritors of this class should call super to make + # sure this method is called. + # parent:: + # if supplied, the parent of this child will be set to the + # supplied value, and self will be added to the parent + def initialize( parent = nil ) + @parent = nil + # Declare @parent, but don't define it. The next line sets the + # parent. + parent.add( self ) if parent + end + + # Replaces this object with another object. Basically, calls + # Parent.replace_child + # + # Returns:: self + def replace_with( child ) + @parent.replace_child( self, child ) + self + end + + # Removes this child from the parent. + # + # Returns:: self + def remove + unless @parent.nil? + @parent.delete self + end + self + end + + # Sets the parent of this child to the supplied argument. + # + # other:: + # Must be a Parent object. If this object is the same object as the + # existing parent of this child, no action is taken. Otherwise, this + # child is removed from the current parent (if one exists), and is added + # to the new parent. + # Returns:: The parent added + def parent=( other ) + return @parent if @parent == other + @parent.delete self if defined? @parent and @parent + @parent = other + end + + alias :next_sibling :next_sibling_node + alias :previous_sibling :previous_sibling_node + + # Sets the next sibling of this child. This can be used to insert a child + # after some other child. + # a = Element.new("a") + # b = a.add_element("b") + # c = Element.new("c") + # b.next_sibling = c + # # => + def next_sibling=( other ) + parent.insert_after self, other + end + + # Sets the previous sibling of this child. This can be used to insert a + # child before some other child. + # a = Element.new("a") + # b = a.add_element("b") + # c = Element.new("c") + # b.previous_sibling = c + # # => + def previous_sibling=(other) + parent.insert_before self, other + end + + # Returns:: the document this child belongs to, or nil if this child + # belongs to no document + def document + return parent.document unless parent.nil? + nil + end + + # This doesn't yet handle encodings + def bytes + encoding = document.encoding + + to_s + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/comment.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/comment.rb new file mode 100644 index 0000000000..2b9b4b89c9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/comment.rb @@ -0,0 +1,80 @@ +require "rexml/child" + +module REXML + ## + # Represents an XML comment; that is, text between \ + class Comment < Child + include Comparable + START = "" + + # The content text + + attr_accessor :string + + ## + # Constructor. The first argument can be one of three types: + # @param first If String, the contents of this comment are set to the + # argument. If Comment, the argument is duplicated. If + # Source, the argument is scanned for a comment. + # @param second If the first argument is a Source, this argument + # should be nil, not supplied, or a Parent to be set as the parent + # of this object + def initialize( first, second = nil ) + #puts "IN COMMENT CONSTRUCTOR; SECOND IS #{second.type}" + super(second) + if first.kind_of? String + @string = first + elsif first.kind_of? Comment + @string = first.string + end + end + + def clone + Comment.new self + end + + # == DEPRECATED + # See REXML::Formatters + # + # output:: + # Where to write the string + # indent:: + # An integer. If -1, no indenting will be used; otherwise, the + # indentation will be this number of spaces, and children will be + # indented an additional amount. + # transitive:: + # Ignored by this class. The contents of comments are never modified. + # ie_hack:: + # Needed for conformity to the child API, but not used by this class. + def write( output, indent=-1, transitive=false, ie_hack=false ) + Kernel.warn("Comment.write is deprecated. See REXML::Formatters") + indent( output, indent ) + output << START + output << @string + output << STOP + end + + alias :to_s :string + + ## + # Compares this Comment to another; the contents of the comment are used + # in the comparison. + def <=>(other) + other.to_s <=> @string + end + + ## + # Compares this Comment to another; the contents of the comment are used + # in the comparison. + def ==( other ) + other.kind_of? Comment and + (other <=> self) == 0 + end + + def node_type + :comment + end + end +end +#vim:ts=2 sw=2 noexpandtab: diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/doctype.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/doctype.rb new file mode 100644 index 0000000000..05cd4ab331 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/doctype.rb @@ -0,0 +1,271 @@ +require "rexml/parent" +require "rexml/parseexception" +require "rexml/namespace" +require 'rexml/entity' +require 'rexml/attlistdecl' +require 'rexml/xmltokens' + +module REXML + # Represents an XML DOCTYPE declaration; that is, the contents of . DOCTYPES can be used to declare the DTD of a document, as well as + # being used to declare entities used in the document. + class DocType < Parent + include XMLTokens + START = "" + SYSTEM = "SYSTEM" + PUBLIC = "PUBLIC" + DEFAULT_ENTITIES = { + 'gt'=>EntityConst::GT, + 'lt'=>EntityConst::LT, + 'quot'=>EntityConst::QUOT, + "apos"=>EntityConst::APOS + } + + # name is the name of the doctype + # external_id is the referenced DTD, if given + attr_reader :name, :external_id, :entities, :namespaces + + # Constructor + # + # dt = DocType.new( 'foo', '-//I/Hate/External/IDs' ) + # # + # dt = DocType.new( doctype_to_clone ) + # # Incomplete. Shallow clone of doctype + # + # +Note+ that the constructor: + # + # Doctype.new( Source.new( "" ) ) + # + # is _deprecated_. Do not use it. It will probably disappear. + def initialize( first, parent=nil ) + @entities = DEFAULT_ENTITIES + @long_name = @uri = nil + if first.kind_of? String + super() + @name = first + @external_id = parent + elsif first.kind_of? DocType + super( parent ) + @name = first.name + @external_id = first.external_id + elsif first.kind_of? Array + super( parent ) + @name = first[0] + @external_id = first[1] + @long_name = first[2] + @uri = first[3] + elsif first.kind_of? Source + super( parent ) + parser = Parsers::BaseParser.new( first ) + event = parser.pull + if event[0] == :start_doctype + @name, @external_id, @long_name, @uri, = event[1..-1] + end + else + super() + end + end + + def node_type + :doctype + end + + def attributes_of element + rv = [] + each do |child| + child.each do |key,val| + rv << Attribute.new(key,val) + end if child.kind_of? AttlistDecl and child.element_name == element + end + rv + end + + def attribute_of element, attribute + att_decl = find do |child| + child.kind_of? AttlistDecl and + child.element_name == element and + child.include? attribute + end + return nil unless att_decl + att_decl[attribute] + end + + def clone + DocType.new self + end + + # output:: + # Where to write the string + # indent:: + # An integer. If -1, no indentation will be used; otherwise, the + # indentation will be this number of spaces, and children will be + # indented an additional amount. + # transitive:: + # Ignored + # ie_hack:: + # Ignored + def write( output, indent=0, transitive=false, ie_hack=false ) + f = REXML::Formatters::Default.new + indent( output, indent ) + output << START + output << ' ' + output << @name + output << " #@external_id" if @external_id + output << " #{@long_name.inspect}" if @long_name + output << " #{@uri.inspect}" if @uri + unless @children.empty? + next_indent = indent + 1 + output << ' [' + child = nil # speed + @children.each { |child| + output << "\n" + f.write( child, output ) + } + output << "\n]" + end + output << STOP + end + + def context + @parent.context + end + + def entity( name ) + @entities[name].unnormalized if @entities[name] + end + + def add child + super(child) + @entities = DEFAULT_ENTITIES.clone if @entities == DEFAULT_ENTITIES + @entities[ child.name ] = child if child.kind_of? Entity + end + + # This method retrieves the public identifier identifying the document's + # DTD. + # + # Method contributed by Henrik Martensson + def public + case @external_id + when "SYSTEM" + nil + when "PUBLIC" + strip_quotes(@long_name) + end + end + + # This method retrieves the system identifier identifying the document's DTD + # + # Method contributed by Henrik Martensson + def system + case @external_id + when "SYSTEM" + strip_quotes(@long_name) + when "PUBLIC" + @uri.kind_of?(String) ? strip_quotes(@uri) : nil + end + end + + # This method returns a list of notations that have been declared in the + # _internal_ DTD subset. Notations in the external DTD subset are not + # listed. + # + # Method contributed by Henrik Martensson + def notations + children().select {|node| node.kind_of?(REXML::NotationDecl)} + end + + # Retrieves a named notation. Only notations declared in the internal + # DTD subset can be retrieved. + # + # Method contributed by Henrik Martensson + def notation(name) + notations.find { |notation_decl| + notation_decl.name == name + } + end + + private + + # Method contributed by Henrik Martensson + def strip_quotes(quoted_string) + quoted_string =~ /^[\'\"].*[\´\"]$/ ? + quoted_string[1, quoted_string.length-2] : + quoted_string + end + end + + # We don't really handle any of these since we're not a validating + # parser, so we can be pretty dumb about them. All we need to be able + # to do is spew them back out on a write() + + # This is an abstract class. You never use this directly; it serves as a + # parent class for the specific declarations. + class Declaration < Child + def initialize src + super() + @string = src + end + + def to_s + @string+'>' + end + + # == DEPRECATED + # See REXML::Formatters + # + def write( output, indent ) + output << to_s + end + end + + public + class ElementDecl < Declaration + def initialize( src ) + super + end + end + + class ExternalEntity < Child + def initialize( src ) + super() + @entity = src + end + def to_s + @entity + end + def write( output, indent ) + output << @entity + end + end + + class NotationDecl < Child + attr_accessor :public, :system + def initialize name, middle, pub, sys + super(nil) + @name = name + @middle = middle + @public = pub + @system = sys + end + + def to_s + "" + end + + def write( output, indent=-1 ) + output << to_s + end + + # This method retrieves the name of the notation. + # + # Method contributed by Henrik Martensson + def name + @name + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/document.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/document.rb new file mode 100644 index 0000000000..00de980e76 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/document.rb @@ -0,0 +1,207 @@ +require "rexml/element" +require "rexml/xmldecl" +require "rexml/source" +require "rexml/comment" +require "rexml/doctype" +require "rexml/instruction" +require "rexml/rexml" +require "rexml/parseexception" +require "rexml/output" +require "rexml/parsers/baseparser" +require "rexml/parsers/streamparser" +require "rexml/parsers/treeparser" + +module REXML + # Represents a full XML document, including PIs, a doctype, etc. A + # Document has a single child that can be accessed by root(). + # Note that if you want to have an XML declaration written for a document + # you create, you must add one; REXML documents do not write a default + # declaration for you. See |DECLARATION| and |write|. + class Document < Element + # A convenient default XML declaration. If you want an XML declaration, + # the easiest way to add one is mydoc << Document::DECLARATION + # +DEPRECATED+ + # Use: mydoc << XMLDecl.default + DECLARATION = XMLDecl.default + + # Constructor + # @param source if supplied, must be a Document, String, or IO. + # Documents have their context and Element attributes cloned. + # Strings are expected to be valid XML documents. IOs are expected + # to be sources of valid XML documents. + # @param context if supplied, contains the context of the document; + # this should be a Hash. + def initialize( source = nil, context = {} ) + super() + @context = context + return if source.nil? + if source.kind_of? Document + @context = source.context + super source + else + build( source ) + end + end + + def node_type + :document + end + + # Should be obvious + def clone + Document.new self + end + + # According to the XML spec, a root node has no expanded name + def expanded_name + '' + #d = doc_type + #d ? d.name : "UNDEFINED" + end + + alias :name :expanded_name + + # We override this, because XMLDecls and DocTypes must go at the start + # of the document + def add( child ) + if child.kind_of? XMLDecl + @children.unshift child + elsif child.kind_of? DocType + # Find first Element or DocType node and insert the decl right + # before it. If there is no such node, just insert the child at the + # end. If there is a child and it is an DocType, then replace it. + insert_before_index = 0 + @children.find { |x| + insert_before_index += 1 + x.kind_of?(Element) || x.kind_of?(DocType) + } + if @children[ insert_before_index ] # Not null = not end of list + if @children[ insert_before_index ].kind_of DocType + @children[ insert_before_index ] = child + else + @children[ index_before_index-1, 0 ] = child + end + else # Insert at end of list + @children[insert_before_index] = child + end + child.parent = self + else + rv = super + raise "attempted adding second root element to document" if @elements.size > 1 + rv + end + end + alias :<< :add + + def add_element(arg=nil, arg2=nil) + rv = super + raise "attempted adding second root element to document" if @elements.size > 1 + rv + end + + # @return the root Element of the document, or nil if this document + # has no children. + def root + elements[1] + #self + #@children.find { |item| item.kind_of? Element } + end + + # @return the DocType child of the document, if one exists, + # and nil otherwise. + def doctype + @children.find { |item| item.kind_of? DocType } + end + + # @return the XMLDecl of this document; if no XMLDecl has been + # set, the default declaration is returned. + def xml_decl + rv = @children[0] + return rv if rv.kind_of? XMLDecl + rv = @children.unshift(XMLDecl.default)[0] + end + + # @return the XMLDecl version of this document as a String. + # If no XMLDecl has been set, returns the default version. + def version + xml_decl().version + end + + # @return the XMLDecl encoding of this document as a String. + # If no XMLDecl has been set, returns the default encoding. + def encoding + xml_decl().encoding + end + + # @return the XMLDecl standalone value of this document as a String. + # If no XMLDecl has been set, returns the default setting. + def stand_alone? + xml_decl().stand_alone? + end + + # Write the XML tree out, optionally with indent. This writes out the + # entire XML document, including XML declarations, doctype declarations, + # and processing instructions (if any are given). + # + # A controversial point is whether Document should always write the XML + # declaration () whether or not one is given by the + # user (or source document). REXML does not write one if one was not + # specified, because it adds unneccessary bandwidth to applications such + # as XML-RPC. + # + # See also the classes in the rexml/formatters package for the proper way + # to change the default formatting of XML output + # + # _Examples_ + # Document.new("").serialize + # + # output_string = "" + # tr = Transitive.new( output_string ) + # Document.new("").serialize( tr ) + # + # output:: + # output an object which supports '<< string'; this is where the + # document will be written. + # indent:: + # An integer. If -1, no indenting will be used; otherwise, the + # indentation will be twice this number of spaces, and children will be + # indented an additional amount. For a value of 3, every item will be + # indented 3 more levels, or 6 more spaces (2 * 3). Defaults to -1 + # trans:: + # If transitive is true and indent is >= 0, then the output will be + # pretty-printed in such a way that the added whitespace does not affect + # the absolute *value* of the document -- that is, it leaves the value + # and number of Text nodes in the document unchanged. + # ie_hack:: + # Internet Explorer is the worst piece of #### to have ever been + # written, with the possible exception of Windows itself. Since IE is + # unable to parse proper XML, we have to provide a hack to generate XML + # that IE's limited abilities can handle. This hack inserts a space + # before the /> on empty tags. Defaults to false + def write( output=$stdout, indent=-1, trans=false, ie_hack=false ) + if xml_decl.encoding != "UTF-8" && !output.kind_of?(Output) + output = Output.new( output, xml_decl.encoding ) + end + formatter = if indent > -1 + if transitive + REXML::Formatters::Transitive.new( indent, ie_hack ) + else + REXML::Formatters::Pretty.new( indent, ie_hack ) + end + else + REXML::Formatters::Default.new( ie_hack ) + end + formatter.write( self, output ) + end + + + def Document::parse_stream( source, listener ) + Parsers::StreamParser.new( source, listener ).parse + end + + private + def build( source ) + Parsers::TreeParser.new( source, self ).parse + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/attlistdecl.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/attlistdecl.rb new file mode 100644 index 0000000000..e176bb0749 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/attlistdecl.rb @@ -0,0 +1,10 @@ +require "rexml/child" +module REXML + module DTD + class AttlistDecl < Child + START = ")/um + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/dtd.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/dtd.rb new file mode 100644 index 0000000000..4f735d4812 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/dtd.rb @@ -0,0 +1,51 @@ +require "rexml/dtd/elementdecl" +require "rexml/dtd/entitydecl" +require "rexml/comment" +require "rexml/dtd/notationdecl" +require "rexml/dtd/attlistdecl" +require "rexml/parent" + +module REXML + module DTD + class Parser + def Parser.parse( input ) + case input + when String + parse_helper input + when File + parse_helper input.read + end + end + + # Takes a String and parses it out + def Parser.parse_helper( input ) + contents = Parent.new + while input.size > 0 + case input + when ElementDecl.PATTERN_RE + match = $& + source = $' + contents << ElementDecl.new( match ) + when AttlistDecl.PATTERN_RE + matchdata = $~ + source = $' + contents << AttlistDecl.new( matchdata ) + when EntityDecl.PATTERN_RE + matchdata = $~ + source = $' + contents << EntityDecl.new( matchdata ) + when Comment.PATTERN_RE + matchdata = $~ + source = $' + contents << Comment.new( matchdata ) + when NotationDecl.PATTERN_RE + matchdata = $~ + source = $' + contents << NotationDecl.new( matchdata ) + end + end + contents + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/elementdecl.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/elementdecl.rb new file mode 100644 index 0000000000..c4e620f389 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/elementdecl.rb @@ -0,0 +1,17 @@ +require "rexml/child" +module REXML + module DTD + class ElementDecl < Child + START = "/um + PATTERN_RE = /^\s*#{START}\s+((?:[:\w_][-\.\w_]*:)?[-!\*\.\w_]*)(.*?)>/ + #\s*((((["']).*?\5)|[^\/'">]*)*?)(\/)?>/um, true) + + def initialize match + @name = match[1] + @rest = match[2] + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/entitydecl.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/entitydecl.rb new file mode 100644 index 0000000000..a5f1520f2b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/entitydecl.rb @@ -0,0 +1,56 @@ +require "rexml/child" +module REXML + module DTD + class EntityDecl < Child + START = "/um + SYSTEM = /^\s*#{START}\s+(?:%\s+)?(\w+)\s+SYSTEM\s+((["']).*?\3)(?:\s+NDATA\s+\w+)?\s*>/um + PLAIN = /^\s*#{START}\s+(\w+)\s+((["']).*?\3)\s*>/um + PERCENT = /^\s*#{START}\s+%\s+(\w+)\s+((["']).*?\3)\s*>/um + # + # + def initialize src + super() + md = nil + if src.match( PUBLIC ) + md = src.match( PUBLIC, true ) + @middle = "PUBLIC" + @content = "#{md[2]} #{md[4]}" + elsif src.match( SYSTEM ) + md = src.match( SYSTEM, true ) + @middle = "SYSTEM" + @content = md[2] + elsif src.match( PLAIN ) + md = src.match( PLAIN, true ) + @middle = "" + @content = md[2] + elsif src.match( PERCENT ) + md = src.match( PERCENT, true ) + @middle = "" + @content = md[2] + end + raise ParseException.new("failed Entity match", src) if md.nil? + @name = md[1] + end + + def to_s + rv = " 0 + rv << @content + rv + end + + def write( output, indent ) + indent( output, indent ) + output << to_s + end + + def EntityDecl.parse_source source, listener + md = source.match( PATTERN_RE, true ) + thing = md[0].squeeze(" \t\n\r") + listener.send inspect.downcase, thing + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/notationdecl.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/notationdecl.rb new file mode 100644 index 0000000000..a47ff8f24b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/dtd/notationdecl.rb @@ -0,0 +1,39 @@ +require "rexml/child" +module REXML + module DTD + class NotationDecl < Child + START = "/um + SYSTEM = /^\s*#{START}\s+(\w[\w-]*)\s+(SYSTEM)\s+((["']).*?\4)\s*>/um + def initialize src + super() + if src.match( PUBLIC ) + md = src.match( PUBLIC, true ) + elsif src.match( SYSTEM ) + md = src.match( SYSTEM, true ) + else + raise ParseException.new( "error parsing notation: no matching pattern", src ) + end + @name = md[1] + @middle = md[2] + @rest = md[3] + end + + def to_s + "" + end + + def write( output, indent ) + indent( output, indent ) + output << to_s + end + + def NotationDecl.parse_source source, listener + md = source.match( PATTERN_RE, true ) + thing = md[0].squeeze(" \t\n\r") + listener.send inspect.downcase, thing + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/element.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/element.rb new file mode 100644 index 0000000000..2c1bb43ad7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/element.rb @@ -0,0 +1,1226 @@ +require "rexml/parent" +require "rexml/namespace" +require "rexml/attribute" +require "rexml/cdata" +require "rexml/xpath" +require "rexml/parseexception" + +module REXML + # An implementation note about namespaces: + # As we parse, when we find namespaces we put them in a hash and assign + # them a unique ID. We then convert the namespace prefix for the node + # to the unique ID. This makes namespace lookup much faster for the + # cost of extra memory use. We save the namespace prefix for the + # context node and convert it back when we write it. + @@namespaces = {} + + # Represents a tagged XML element. Elements are characterized by + # having children, attributes, and names, and can themselves be + # children. + class Element < Parent + include Namespace + + UNDEFINED = "UNDEFINED"; # The default name + + # Mechanisms for accessing attributes and child elements of this + # element. + attr_reader :attributes, :elements + # The context holds information about the processing environment, such as + # whitespace handling. + attr_accessor :context + + # Constructor + # arg:: + # if not supplied, will be set to the default value. + # If a String, the name of this object will be set to the argument. + # If an Element, the object will be shallowly cloned; name, + # attributes, and namespaces will be copied. Children will +not+ be + # copied. + # parent:: + # if supplied, must be a Parent, and will be used as + # the parent of this object. + # context:: + # If supplied, must be a hash containing context items. Context items + # include: + # * :respect_whitespace the value of this is :+all+ or an array of + # strings being the names of the elements to respect + # whitespace for. Defaults to :+all+. + # * :compress_whitespace the value can be :+all+ or an array of + # strings being the names of the elements to ignore whitespace on. + # Overrides :+respect_whitespace+. + # * :ignore_whitespace_nodes the value can be :+all+ or an array + # of strings being the names of the elements in which to ignore + # whitespace-only nodes. If this is set, Text nodes which contain only + # whitespace will not be added to the document tree. + # * :raw can be :+all+, or an array of strings being the names of + # the elements to process in raw mode. In raw mode, special + # characters in text is not converted to or from entities. + def initialize( arg = UNDEFINED, parent=nil, context=nil ) + super(parent) + + @elements = Elements.new(self) + @attributes = Attributes.new(self) + @context = context + + if arg.kind_of? String + self.name = arg + elsif arg.kind_of? Element + self.name = arg.expanded_name + arg.attributes.each_attribute{ |attribute| + @attributes << Attribute.new( attribute ) + } + @context = arg.context + end + end + + def inspect + rv = "<#@expanded_name" + + @attributes.each_attribute do |attr| + rv << " " + attr.write( rv, 0 ) + end + + if children.size > 0 + rv << "> ... " + else + rv << "/>" + end + end + + + # Creates a shallow copy of self. + # d = Document.new "" + # new_a = d.root.clone + # puts new_a # => "" + def clone + self.class.new self + end + + # Evaluates to the root node of the document that this element + # belongs to. If this element doesn't belong to a document, but does + # belong to another Element, the parent's root will be returned, until the + # earliest ancestor is found. + # + # Note that this is not the same as the document element. + # In the following example, is the document element, and the root + # node is the parent node of the document element. You may ask yourself + # why the root node is useful: consider the doctype and XML declaration, + # and any processing instructions before the document element... they + # are children of the root node, or siblings of the document element. + # The only time this isn't true is when an Element is created that is + # not part of any Document. In this case, the ancestor that has no + # parent acts as the root node. + # d = Document.new '' + # a = d[1] ; c = a[1][1] + # d.root_node == d # TRUE + # a.root_node # namely, d + # c.root_node # again, d + def root_node + parent.nil? ? self : parent.root_node + end + + def root + return elements[1] if self.kind_of? Document + return self if parent.kind_of? Document or parent.nil? + return parent.root + end + + # Evaluates to the document to which this element belongs, or nil if this + # element doesn't belong to a document. + def document + rt = root + rt.parent if rt + end + + # Evaluates to +true+ if whitespace is respected for this element. This + # is the case if: + # 1. Neither :+respect_whitespace+ nor :+compress_whitespace+ has any value + # 2. The context has :+respect_whitespace+ set to :+all+ or + # an array containing the name of this element, and + # :+compress_whitespace+ isn't set to :+all+ or an array containing the + # name of this element. + # The evaluation is tested against +expanded_name+, and so is namespace + # sensitive. + def whitespace + @whitespace = nil + if @context + if @context[:respect_whitespace] + @whitespace = (@context[:respect_whitespace] == :all or + @context[:respect_whitespace].include? expanded_name) + end + @whitespace = false if (@context[:compress_whitespace] and + (@context[:compress_whitespace] == :all or + @context[:compress_whitespace].include? expanded_name) + ) + end + @whitespace = true unless @whitespace == false + @whitespace + end + + def ignore_whitespace_nodes + @ignore_whitespace_nodes = false + if @context + if @context[:ignore_whitespace_nodes] + @ignore_whitespace_nodes = + (@context[:ignore_whitespace_nodes] == :all or + @context[:ignore_whitespace_nodes].include? expanded_name) + end + end + end + + # Evaluates to +true+ if raw mode is set for this element. This + # is the case if the context has :+raw+ set to :+all+ or + # an array containing the name of this element. + # + # The evaluation is tested against +expanded_name+, and so is namespace + # sensitive. + def raw + @raw = (@context and @context[:raw] and + (@context[:raw] == :all or + @context[:raw].include? expanded_name)) + @raw + end + + #once :whitespace, :raw, :ignore_whitespace_nodes + + ################################################# + # Namespaces # + ################################################# + + # Evaluates to an +Array+ containing the prefixes (names) of all defined + # namespaces at this context node. + # doc = Document.new("") + # doc.elements['//b'].prefixes # -> ['x', 'y'] + def prefixes + prefixes = [] + prefixes = parent.prefixes if parent + prefixes |= attributes.prefixes + return prefixes + end + + def namespaces + namespaces = {} + namespaces = parent.namespaces if parent + namespaces = namespaces.merge( attributes.namespaces ) + return namespaces + end + + # Evalutas to the URI for a prefix, or the empty string if no such + # namespace is declared for this element. Evaluates recursively for + # ancestors. Returns the default namespace, if there is one. + # prefix:: + # the prefix to search for. If not supplied, returns the default + # namespace if one exists + # Returns:: + # the namespace URI as a String, or nil if no such namespace + # exists. If the namespace is undefined, returns an empty string + # doc = Document.new("") + # b = doc.elements['//b'] + # b.namespace # -> '1' + # b.namespace("y") # -> '2' + def namespace(prefix=nil) + if prefix.nil? + prefix = prefix() + end + if prefix == '' + prefix = "xmlns" + else + prefix = "xmlns:#{prefix}" unless prefix[0,5] == 'xmlns' + end + ns = attributes[ prefix ] + ns = parent.namespace(prefix) if ns.nil? and parent + ns = '' if ns.nil? and prefix == 'xmlns' + return ns + end + + # Adds a namespace to this element. + # prefix:: + # the prefix string, or the namespace URI if +uri+ is not + # supplied + # uri:: + # the namespace URI. May be nil, in which +prefix+ is used as + # the URI + # Evaluates to: this Element + # a = Element.new("a") + # a.add_namespace("xmlns:foo", "bar" ) + # a.add_namespace("foo", "bar") # shorthand for previous line + # a.add_namespace("twiddle") + # puts a #-> + def add_namespace( prefix, uri=nil ) + unless uri + @attributes["xmlns"] = prefix + else + prefix = "xmlns:#{prefix}" unless prefix =~ /^xmlns:/ + @attributes[ prefix ] = uri + end + self + end + + # Removes a namespace from this node. This only works if the namespace is + # actually declared in this node. If no argument is passed, deletes the + # default namespace. + # + # Evaluates to: this element + # doc = Document.new "" + # doc.root.delete_namespace + # puts doc # -> + # doc.root.delete_namespace 'foo' + # puts doc # -> + def delete_namespace namespace="xmlns" + namespace = "xmlns:#{namespace}" unless namespace == 'xmlns' + attribute = attributes.get_attribute(namespace) + attribute.remove unless attribute.nil? + self + end + + ################################################# + # Elements # + ################################################# + + # Adds a child to this element, optionally setting attributes in + # the element. + # element:: + # optional. If Element, the element is added. + # Otherwise, a new Element is constructed with the argument (see + # Element.initialize). + # attrs:: + # If supplied, must be a Hash containing String name,value + # pairs, which will be used to set the attributes of the new Element. + # Returns:: the Element that was added + # el = doc.add_element 'my-tag' + # el = doc.add_element 'my-tag', {'attr1'=>'val1', 'attr2'=>'val2'} + # el = Element.new 'my-tag' + # doc.add_element el + def add_element element, attrs=nil + raise "First argument must be either an element name, or an Element object" if element.nil? + el = @elements.add(element) + attrs.each do |key, value| + el.attributes[key]=Attribute.new(key,value,self) + end if attrs.kind_of? Hash + el + end + + # Deletes a child element. + # element:: + # Must be an +Element+, +String+, or +Integer+. If Element, + # the element is removed. If String, the element is found (via XPath) + # and removed. This means that any parent can remove any + # descendant. If Integer, the Element indexed by that number will be + # removed. + # Returns:: the element that was removed. + # doc.delete_element "/a/b/c[@id='4']" + # doc.delete_element doc.elements["//k"] + # doc.delete_element 1 + def delete_element element + @elements.delete element + end + + # Evaluates to +true+ if this element has at least one child Element + # doc = Document.new "Text" + # doc.root.has_elements # -> true + # doc.elements["/a/b"].has_elements # -> false + # doc.elements["/a/c"].has_elements # -> false + def has_elements? + !@elements.empty? + end + + # Iterates through the child elements, yielding for each Element that + # has a particular attribute set. + # key:: + # the name of the attribute to search for + # value:: + # the value of the attribute + # max:: + # (optional) causes this method to return after yielding + # for this number of matching children + # name:: + # (optional) if supplied, this is an XPath that filters + # the children to check. + # + # doc = Document.new "" + # # Yields b, c, d + # doc.root.each_element_with_attribute( 'id' ) {|e| p e} + # # Yields b, d + # doc.root.each_element_with_attribute( 'id', '1' ) {|e| p e} + # # Yields b + # doc.root.each_element_with_attribute( 'id', '1', 1 ) {|e| p e} + # # Yields d + # doc.root.each_element_with_attribute( 'id', '1', 0, 'd' ) {|e| p e} + def each_element_with_attribute( key, value=nil, max=0, name=nil, &block ) # :yields: Element + each_with_something( proc {|child| + if value.nil? + child.attributes[key] != nil + else + child.attributes[key]==value + end + }, max, name, &block ) + end + + # Iterates through the children, yielding for each Element that + # has a particular text set. + # text:: + # the text to search for. If nil, or not supplied, will itterate + # over all +Element+ children that contain at least one +Text+ node. + # max:: + # (optional) causes this method to return after yielding + # for this number of matching children + # name:: + # (optional) if supplied, this is an XPath that filters + # the children to check. + # + # doc = Document.new 'bbd' + # # Yields b, c, d + # doc.each_element_with_text {|e|p e} + # # Yields b, c + # doc.each_element_with_text('b'){|e|p e} + # # Yields b + # doc.each_element_with_text('b', 1){|e|p e} + # # Yields d + # doc.each_element_with_text(nil, 0, 'd'){|e|p e} + def each_element_with_text( text=nil, max=0, name=nil, &block ) # :yields: Element + each_with_something( proc {|child| + if text.nil? + child.has_text? + else + child.text == text + end + }, max, name, &block ) + end + + # Synonym for Element.elements.each + def each_element( xpath=nil, &block ) # :yields: Element + @elements.each( xpath, &block ) + end + + # Synonym for Element.to_a + # This is a little slower than calling elements.each directly. + # xpath:: any XPath by which to search for elements in the tree + # Returns:: an array of Elements that match the supplied path + def get_elements( xpath ) + @elements.to_a( xpath ) + end + + # Returns the next sibling that is an element, or nil if there is + # no Element sibling after this one + # doc = Document.new 'text' + # doc.root.elements['b'].next_element #-> + # doc.root.elements['c'].next_element #-> nil + def next_element + element = next_sibling + element = element.next_sibling until element.nil? or element.kind_of? Element + return element + end + + # Returns the previous sibling that is an element, or nil if there is + # no Element sibling prior to this one + # doc = Document.new 'text' + # doc.root.elements['c'].previous_element #-> + # doc.root.elements['b'].previous_element #-> nil + def previous_element + element = previous_sibling + element = element.previous_sibling until element.nil? or element.kind_of? Element + return element + end + + + ################################################# + # Text # + ################################################# + + # Evaluates to +true+ if this element has at least one Text child + def has_text? + not text().nil? + end + + # A convenience method which returns the String value of the _first_ + # child text element, if one exists, and +nil+ otherwise. + # + # Note that an element may have multiple Text elements, perhaps + # separated by other children. Be aware that this method only returns + # the first Text node. + # + # This method returns the +value+ of the first text child node, which + # ignores the +raw+ setting, so always returns normalized text. See + # the Text::value documentation. + # + # doc = Document.new "

    some text this is bold! more text

    " + # # The element 'p' has two text elements, "some text " and " more text". + # doc.root.text #-> "some text " + def text( path = nil ) + rv = get_text(path) + return rv.value unless rv.nil? + nil + end + + # Returns the first child Text node, if any, or +nil+ otherwise. + # This method returns the actual +Text+ node, rather than the String content. + # doc = Document.new "

    some text this is bold! more text

    " + # # The element 'p' has two text elements, "some text " and " more text". + # doc.root.get_text.value #-> "some text " + def get_text path = nil + rv = nil + if path + element = @elements[ path ] + rv = element.get_text unless element.nil? + else + rv = @children.find { |node| node.kind_of? Text } + end + return rv + end + + # Sets the first Text child of this object. See text() for a + # discussion about Text children. + # + # If a Text child already exists, the child is replaced by this + # content. This means that Text content can be deleted by calling + # this method with a nil argument. In this case, the next Text + # child becomes the first Text child. In no case is the order of + # any siblings disturbed. + # text:: + # If a String, a new Text child is created and added to + # this Element as the first Text child. If Text, the text is set + # as the first Child element. If nil, then any existing first Text + # child is removed. + # Returns:: this Element. + # doc = Document.new '' + # doc.root.text = 'Sean' #-> 'Sean' + # doc.root.text = 'Elliott' #-> 'Elliott' + # doc.root.add_element 'c' #-> 'Elliott' + # doc.root.text = 'Russell' #-> 'Russell' + # doc.root.text = nil #-> '' + def text=( text ) + if text.kind_of? String + text = Text.new( text, whitespace(), nil, raw() ) + elsif text and !text.kind_of? Text + text = Text.new( text.to_s, whitespace(), nil, raw() ) + end + old_text = get_text + if text.nil? + old_text.remove unless old_text.nil? + else + if old_text.nil? + self << text + else + old_text.replace_with( text ) + end + end + return self + end + + # A helper method to add a Text child. Actual Text instances can + # be added with regular Parent methods, such as add() and <<() + # text:: + # if a String, a new Text instance is created and added + # to the parent. If Text, the object is added directly. + # Returns:: this Element + # e = Element.new('a') #-> + # e.add_text 'foo' #-> foo + # e.add_text Text.new(' bar') #-> foo bar + # Note that at the end of this example, the branch has 3 nodes; the 'e' + # element and 2 Text node children. + def add_text( text ) + if text.kind_of? String + if @children[-1].kind_of? Text + @children[-1] << text + return + end + text = Text.new( text, whitespace(), nil, raw() ) + end + self << text unless text.nil? + return self + end + + def node_type + :element + end + + def xpath + path_elements = [] + cur = self + path_elements << __to_xpath_helper( self ) + while cur.parent + cur = cur.parent + path_elements << __to_xpath_helper( cur ) + end + return path_elements.reverse.join( "/" ) + end + + ################################################# + # Attributes # + ################################################# + + def attribute( name, namespace=nil ) + prefix = nil + prefix = namespaces.index(namespace) if namespace + attributes.get_attribute( "#{prefix ? prefix + ':' : ''}#{name}" ) + end + + # Evaluates to +true+ if this element has any attributes set, false + # otherwise. + def has_attributes? + return !@attributes.empty? + end + + # Adds an attribute to this element, overwriting any existing attribute + # by the same name. + # key:: + # can be either an Attribute or a String. If an Attribute, + # the attribute is added to the list of Element attributes. If String, + # the argument is used as the name of the new attribute, and the value + # parameter must be supplied. + # value:: + # Required if +key+ is a String, and ignored if the first argument is + # an Attribute. This is a String, and is used as the value + # of the new Attribute. This should be the unnormalized value of the + # attribute (without entities). + # Returns:: the Attribute added + # e = Element.new 'e' + # e.add_attribute( 'a', 'b' ) #-> + # e.add_attribute( 'x:a', 'c' ) #-> + # e.add_attribute Attribute.new('b', 'd') #-> + def add_attribute( key, value=nil ) + if key.kind_of? Attribute + @attributes << key + else + @attributes[key] = value + end + end + + # Add multiple attributes to this element. + # hash:: is either a hash, or array of arrays + # el.add_attributes( {"name1"=>"value1", "name2"=>"value2"} ) + # el.add_attributes( [ ["name1","value1"], ["name2"=>"value2"] ] ) + def add_attributes hash + if hash.kind_of? Hash + hash.each_pair {|key, value| @attributes[key] = value } + elsif hash.kind_of? Array + hash.each { |value| @attributes[ value[0] ] = value[1] } + end + end + + # Removes an attribute + # key:: + # either an Attribute or a String. In either case, the + # attribute is found by matching the attribute name to the argument, + # and then removed. If no attribute is found, no action is taken. + # Returns:: + # the attribute removed, or nil if this Element did not contain + # a matching attribute + # e = Element.new('E') + # e.add_attribute( 'name', 'Sean' ) #-> + # r = e.add_attribute( 'sur:name', 'Russell' ) #-> + # e.delete_attribute( 'name' ) #-> + # e.delete_attribute( r ) #-> + def delete_attribute(key) + attr = @attributes.get_attribute(key) + attr.remove unless attr.nil? + end + + ################################################# + # Other Utilities # + ################################################# + + # Get an array of all CData children. + # IMMUTABLE + def cdatas + find_all { |child| child.kind_of? CData }.freeze + end + + # Get an array of all Comment children. + # IMMUTABLE + def comments + find_all { |child| child.kind_of? Comment }.freeze + end + + # Get an array of all Instruction children. + # IMMUTABLE + def instructions + find_all { |child| child.kind_of? Instruction }.freeze + end + + # Get an array of all Text children. + # IMMUTABLE + def texts + find_all { |child| child.kind_of? Text }.freeze + end + + # == DEPRECATED + # See REXML::Formatters + # + # Writes out this element, and recursively, all children. + # output:: + # output an object which supports '<< string'; this is where the + # document will be written. + # indent:: + # An integer. If -1, no indenting will be used; otherwise, the + # indentation will be this number of spaces, and children will be + # indented an additional amount. Defaults to -1 + # transitive:: + # If transitive is true and indent is >= 0, then the output will be + # pretty-printed in such a way that the added whitespace does not affect + # the parse tree of the document + # ie_hack:: + # Internet Explorer is the worst piece of #### to have ever been + # written, with the possible exception of Windows itself. Since IE is + # unable to parse proper XML, we have to provide a hack to generate XML + # that IE's limited abilities can handle. This hack inserts a space + # before the /> on empty tags. Defaults to false + # + # out = '' + # doc.write( out ) #-> doc is written to the string 'out' + # doc.write( $stdout ) #-> doc written to the console + def write(writer=$stdout, indent=-1, transitive=false, ie_hack=false) + Kernel.warn("#{self.class.name}.write is deprecated. See REXML::Formatters") + formatter = if indent > -1 + if transitive + REXML::Formatters::Transitive.new( indent, ie_hack ) + else + REXML::Formatters::Pretty.new( indent, ie_hack ) + end + else + REXML::Formatters::Default.new( ie_hack ) + end + formatter.write( self, output ) + end + + + private + def __to_xpath_helper node + rv = node.expanded_name.clone + if node.parent + results = node.parent.find_all {|n| + n.kind_of?(REXML::Element) and n.expanded_name == node.expanded_name + } + if results.length > 1 + idx = results.index( node ) + rv << "[#{idx+1}]" + end + end + rv + end + + # A private helper method + def each_with_something( test, max=0, name=nil ) + num = 0 + child=nil + @elements.each( name ){ |child| + yield child if test.call(child) and num += 1 + return if max>0 and num == max + } + end + end + + ######################################################################## + # ELEMENTS # + ######################################################################## + + # A class which provides filtering of children for Elements, and + # XPath search support. You are expected to only encounter this class as + # the element.elements object. Therefore, you are + # _not_ expected to instantiate this yourself. + class Elements + include Enumerable + # Constructor + # parent:: the parent Element + def initialize parent + @element = parent + end + + # Fetches a child element. Filters only Element children, regardless of + # the XPath match. + # index:: + # the search parameter. This is either an Integer, which + # will be used to find the index'th child Element, or an XPath, + # which will be used to search for the Element. Because + # of the nature of XPath searches, any element in the connected XML + # document can be fetched through any other element. The + # Integer index is 1-based, not 0-based. This means that the first + # child element is at index 1, not 0, and the +n+th element is at index + # +n+, not n-1. This is because XPath indexes element children + # starting from 1, not 0, and the indexes should be the same. + # name:: + # optional, and only used in the first argument is an + # Integer. In that case, the index'th child Element that has the + # supplied name will be returned. Note again that the indexes start at 1. + # Returns:: the first matching Element, or nil if no child matched + # doc = Document.new '' + # doc.root.elements[1] #-> + # doc.root.elements['c'] #-> + # doc.root.elements[2,'c'] #-> + def []( index, name=nil) + if index.kind_of? Integer + raise "index (#{index}) must be >= 1" if index < 1 + name = literalize(name) if name + num = 0 + child = nil + @element.find { |child| + child.kind_of? Element and + (name.nil? ? true : child.has_name?( name )) and + (num += 1) == index + } + else + return XPath::first( @element, index ) + #{ |element| + # return element if element.kind_of? Element + #} + #return nil + end + end + + # Sets an element, replacing any previous matching element. If no + # existing element is found ,the element is added. + # index:: Used to find a matching element to replace. See [](). + # element:: + # The element to replace the existing element with + # the previous element + # Returns:: nil if no previous element was found. + # + # doc = Document.new '' + # doc.root.elements[10] = Element.new('b') #-> + # doc.root.elements[1] #-> + # doc.root.elements[1] = Element.new('c') #-> + # doc.root.elements['c'] = Element.new('d') #-> + def []=( index, element ) + previous = self[index] + if previous.nil? + @element.add element + else + previous.replace_with element + end + return previous + end + + # Returns +true+ if there are no +Element+ children, +false+ otherwise + def empty? + @element.find{ |child| child.kind_of? Element}.nil? + end + + # Returns the index of the supplied child (starting at 1), or -1 if + # the element is not a child + # element:: an +Element+ child + def index element + rv = 0 + found = @element.find do |child| + child.kind_of? Element and + (rv += 1) and + child == element + end + return rv if found == element + return -1 + end + + # Deletes a child Element + # element:: + # Either an Element, which is removed directly; an + # xpath, where the first matching child is removed; or an Integer, + # where the n'th Element is removed. + # Returns:: the removed child + # doc = Document.new '' + # b = doc.root.elements[1] + # doc.root.elements.delete b #-> + # doc.elements.delete("a/c[@id='1']") #-> + # doc.root.elements.delete 1 #-> + def delete element + if element.kind_of? Element + @element.delete element + else + el = self[element] + el.remove if el + end + end + + # Removes multiple elements. Filters for Element children, regardless of + # XPath matching. + # xpath:: all elements matching this String path are removed. + # Returns:: an Array of Elements that have been removed + # doc = Document.new '' + # deleted = doc.elements.delete_all 'a/c' #-> [, , , ] + def delete_all( xpath ) + rv = [] + XPath::each( @element, xpath) {|element| + rv << element if element.kind_of? Element + } + rv.each do |element| + @element.delete element + element.remove + end + return rv + end + + # Adds an element + # element:: + # if supplied, is either an Element, String, or + # Source (see Element.initialize). If not supplied or nil, a + # new, default Element will be constructed + # Returns:: the added Element + # a = Element.new 'a' + # a.elements.add Element.new 'b' #-> + # a.elements.add 'c' #-> + def add element=nil + rv = nil + if element.nil? + Element.new "", self, @element.context + elsif not element.kind_of?(Element) + Element.new element, self, @element.context + else + @element << element + element.context = @element.context + element + end + end + + alias :<< :add + + # Iterates through all of the child Elements, optionally filtering + # them by a given XPath + # xpath:: + # optional. If supplied, this is a String XPath, and is used to + # filter the children, so that only matching children are yielded. Note + # that XPaths are automatically filtered for Elements, so that + # non-Element children will not be yielded + # doc = Document.new 'sean' + # doc.root.each {|e|p e} #-> Yields b, c, d, b, c, d elements + # doc.root.each('b') {|e|p e} #-> Yields b, b elements + # doc.root.each('child::node()') {|e|p e} + # #-> Yields , , , , , + # XPath.each(doc.root, 'child::node()', &block) + # #-> Yields , , , sean, , , + def each( xpath=nil, &block) + XPath::each( @element, xpath ) {|e| yield e if e.kind_of? Element } + end + + def collect( xpath=nil, &block ) + collection = [] + XPath::each( @element, xpath ) {|e| + collection << yield(e) if e.kind_of?(Element) + } + collection + end + + def inject( xpath=nil, initial=nil, &block ) + first = true + XPath::each( @element, xpath ) {|e| + if (e.kind_of? Element) + if (first and initial == nil) + initial = e + first = false + else + initial = yield( initial, e ) if e.kind_of? Element + end + end + } + initial + end + + # Returns the number of +Element+ children of the parent object. + # doc = Document.new 'seanelliottrussell' + # doc.root.size #-> 6, 3 element and 3 text nodes + # doc.root.elements.size #-> 3 + def size + count = 0 + @element.each {|child| count+=1 if child.kind_of? Element } + count + end + + # Returns an Array of Element children. An XPath may be supplied to + # filter the children. Only Element children are returned, even if the + # supplied XPath matches non-Element children. + # doc = Document.new 'seanelliott' + # doc.root.elements.to_a #-> [ , ] + # doc.root.elements.to_a("child::node()") #-> [ , ] + # XPath.match(doc.root, "child::node()") #-> [ sean, , elliott, ] + def to_a( xpath=nil ) + rv = XPath.match( @element, xpath ) + return rv.find_all{|e| e.kind_of? Element} if xpath + rv + end + + private + # Private helper class. Removes quotes from quoted strings + def literalize name + name = name[1..-2] if name[0] == ?' or name[0] == ?" #' + name + end + end + + ######################################################################## + # ATTRIBUTES # + ######################################################################## + + # A class that defines the set of Attributes of an Element and provides + # operations for accessing elements in that set. + class Attributes < Hash + # Constructor + # element:: the Element of which this is an Attribute + def initialize element + @element = element + end + + # Fetches an attribute value. If you want to get the Attribute itself, + # use get_attribute() + # name:: an XPath attribute name. Namespaces are relevant here. + # Returns:: + # the String value of the matching attribute, or +nil+ if no + # matching attribute was found. This is the unnormalized value + # (with entities expanded). + # + # doc = Document.new "" + # doc.root.attributes['att'] #-> '<' + # doc.root.attributes['bar:att'] #-> '2' + def [](name) + attr = get_attribute(name) + return attr.value unless attr.nil? + return nil + end + + def to_a + values.flatten + end + + # Returns the number of attributes the owning Element contains. + # doc = Document "" + # doc.root.attributes.length #-> 3 + def length + c = 0 + each_attribute { c+=1 } + c + end + alias :size :length + + # Itterates over the attributes of an Element. Yields actual Attribute + # nodes, not String values. + # + # doc = Document.new '' + # doc.root.attributes.each_attribute {|attr| + # p attr.expanded_name+" => "+attr.value + # } + def each_attribute # :yields: attribute + each_value do |val| + if val.kind_of? Attribute + yield val + else + val.each_value { |atr| yield atr } + end + end + end + + # Itterates over each attribute of an Element, yielding the expanded name + # and value as a pair of Strings. + # + # doc = Document.new '' + # doc.root.attributes.each {|name, value| p name+" => "+value } + def each + each_attribute do |attr| + yield attr.expanded_name, attr.value + end + end + + # Fetches an attribute + # name:: + # the name by which to search for the attribute. Can be a + # prefix:name namespace name. + # Returns:: The first matching attribute, or nil if there was none. This + # value is an Attribute node, not the String value of the attribute. + # doc = Document.new '' + # doc.root.attributes.get_attribute("foo").value #-> "2" + # doc.root.attributes.get_attribute("x:foo").value #-> "1" + def get_attribute( name ) + attr = fetch( name, nil ) + if attr.nil? + return nil if name.nil? + # Look for prefix + name =~ Namespace::NAMESPLIT + prefix, n = $1, $2 + if prefix + attr = fetch( n, nil ) + # check prefix + if attr == nil + elsif attr.kind_of? Attribute + return attr if prefix == attr.prefix + else + attr = attr[ prefix ] + return attr + end + end + element_document = @element.document + if element_document and element_document.doctype + expn = @element.expanded_name + expn = element_document.doctype.name if expn.size == 0 + attr_val = element_document.doctype.attribute_of(expn, name) + return Attribute.new( name, attr_val ) if attr_val + end + return nil + end + if attr.kind_of? Hash + attr = attr[ @element.prefix ] + end + return attr + end + + # Sets an attribute, overwriting any existing attribute value by the + # same name. Namespace is significant. + # name:: the name of the attribute + # value:: + # (optional) If supplied, the value of the attribute. If + # nil, any existing matching attribute is deleted. + # Returns:: + # Owning element + # doc = Document.new "" + # doc.root.attributes['y:foo'] = '2' + # doc.root.attributes['foo'] = '4' + # doc.root.attributes['x:foo'] = nil + def []=( name, value ) + if value.nil? # Delete the named attribute + attr = get_attribute(name) + delete attr + return + end + element_document = @element.document + unless value.kind_of? Attribute + if @element.document and @element.document.doctype + value = Text::normalize( value, @element.document.doctype ) + else + value = Text::normalize( value, nil ) + end + value = Attribute.new(name, value) + end + value.element = @element + old_attr = fetch(value.name, nil) + if old_attr.nil? + store(value.name, value) + elsif old_attr.kind_of? Hash + old_attr[value.prefix] = value + elsif old_attr.prefix != value.prefix + # Check for conflicting namespaces + raise ParseException.new( + "Namespace conflict in adding attribute \"#{value.name}\": "+ + "Prefix \"#{old_attr.prefix}\" = "+ + "\"#{@element.namespace(old_attr.prefix)}\" and prefix "+ + "\"#{value.prefix}\" = \"#{@element.namespace(value.prefix)}\"") if + value.prefix != "xmlns" and old_attr.prefix != "xmlns" and + @element.namespace( old_attr.prefix ) == + @element.namespace( value.prefix ) + store value.name, { old_attr.prefix => old_attr, + value.prefix => value } + else + store value.name, value + end + return @element + end + + # Returns an array of Strings containing all of the prefixes declared + # by this set of # attributes. The array does not include the default + # namespace declaration, if one exists. + # doc = Document.new("") + # prefixes = doc.root.attributes.prefixes #-> ['x', 'y'] + def prefixes + ns = [] + each_attribute do |attribute| + ns << attribute.name if attribute.prefix == 'xmlns' + end + if @element.document and @element.document.doctype + expn = @element.expanded_name + expn = @element.document.doctype.name if expn.size == 0 + @element.document.doctype.attributes_of(expn).each { + |attribute| + ns << attribute.name if attribute.prefix == 'xmlns' + } + end + ns + end + + def namespaces + namespaces = {} + each_attribute do |attribute| + namespaces[attribute.name] = attribute.value if attribute.prefix == 'xmlns' or attribute.name == 'xmlns' + end + if @element.document and @element.document.doctype + expn = @element.expanded_name + expn = @element.document.doctype.name if expn.size == 0 + @element.document.doctype.attributes_of(expn).each { + |attribute| + namespaces[attribute.name] = attribute.value if attribute.prefix == 'xmlns' or attribute.name == 'xmlns' + } + end + namespaces + end + + # Removes an attribute + # attribute:: + # either a String, which is the name of the attribute to remove -- + # namespaces are significant here -- or the attribute to remove. + # Returns:: the owning element + # doc = Document.new "" + # doc.root.attributes.delete 'foo' #-> " + # doc.root.attributes.delete 'x:foo' #-> " + # attr = doc.root.attributes.get_attribute('y:foo') + # doc.root.attributes.delete attr #-> " + def delete( attribute ) + name = nil + prefix = nil + if attribute.kind_of? Attribute + name = attribute.name + prefix = attribute.prefix + else + attribute =~ Namespace::NAMESPLIT + prefix, name = $1, $2 + prefix = '' unless prefix + end + old = fetch(name, nil) + attr = nil + if old.kind_of? Hash # the supplied attribute is one of many + attr = old.delete(prefix) + if old.size == 1 + repl = nil + old.each_value{|v| repl = v} + store name, repl + end + elsif old.nil? + return @element + else # the supplied attribute is a top-level one + attr = old + res = super(name) + end + @element + end + + # Adds an attribute, overriding any existing attribute by the + # same name. Namespaces are significant. + # attribute:: An Attribute + def add( attribute ) + self[attribute.name] = attribute + end + + alias :<< :add + + # Deletes all attributes matching a name. Namespaces are significant. + # name:: + # A String; all attributes that match this path will be removed + # Returns:: an Array of the Attributes that were removed + def delete_all( name ) + rv = [] + each_attribute { |attribute| + rv << attribute if attribute.expanded_name == name + } + rv.each{ |attr| attr.remove } + return rv + end + + # The +get_attribute_ns+ method retrieves a method by its namespace + # and name. Thus it is possible to reliably identify an attribute + # even if an XML processor has changed the prefix. + # + # Method contributed by Henrik Martensson + def get_attribute_ns(namespace, name) + each_attribute() { |attribute| + if name == attribute.name && + namespace == attribute.namespace() + return attribute + end + } + nil + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encoding.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encoding.rb new file mode 100644 index 0000000000..6cae6b644d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encoding.rb @@ -0,0 +1,66 @@ +# -*- mode: ruby; ruby-indent-level: 2; indent-tabs-mode: t; tab-width: 2 -*- vim: sw=2 ts=2 +module REXML + module Encoding + @encoding_methods = {} + def self.register(enc, &block) + @encoding_methods[enc] = block + end + def self.apply(obj, enc) + @encoding_methods[enc][obj] + end + def self.encoding_method(enc) + @encoding_methods[enc] + end + + # Native, default format is UTF-8, so it is declared here rather than in + # an encodings/ definition. + UTF_8 = 'UTF-8' + UTF_16 = 'UTF-16' + UNILE = 'UNILE' + + # ID ---> Encoding name + attr_reader :encoding + def encoding=( enc ) + old_verbosity = $VERBOSE + begin + $VERBOSE = false + enc = enc.nil? ? nil : enc.upcase + return false if defined? @encoding and enc == @encoding + if enc and enc != UTF_8 + @encoding = enc + raise ArgumentError, "Bad encoding name #@encoding" unless @encoding =~ /^[\w-]+$/ + @encoding.untaint + begin + require 'rexml/encodings/ICONV.rb' + Encoding.apply(self, "ICONV") + rescue LoadError, Exception + begin + enc_file = File.join( "rexml", "encodings", "#@encoding.rb" ) + require enc_file + Encoding.apply(self, @encoding) + rescue LoadError => err + puts err.message + raise ArgumentError, "No decoder found for encoding #@encoding. Please install iconv." + end + end + else + @encoding = UTF_8 + require 'rexml/encodings/UTF-8.rb' + Encoding.apply(self, @encoding) + end + ensure + $VERBOSE = old_verbosity + end + true + end + + def check_encoding str + # We have to recognize UTF-16, LSB UTF-16, and UTF-8 + return UTF_16 if /\A\xfe\xff/n =~ str + return UNILE if /\A\xff\xfe/n =~ str + str =~ /^\s*<\?xml\s+version\s*=\s*(['"]).*?\1\s+encoding\s*=\s*(["'])(.*?)\2/um + return $3.upcase if $3 + return UTF_8 + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/CP-1252.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/CP-1252.rb new file mode 100644 index 0000000000..8675f9ff98 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/CP-1252.rb @@ -0,0 +1,103 @@ +# +# This class was contributed by Mikko Tiihonen mikko DOT tiihonen AT hut DOT fi +# +module REXML + module Encoding + register( "CP-1252" ) do |o| + class << o + alias encode encode_cp1252 + alias decode decode_cp1252 + end + end + + # Convert from UTF-8 + def encode_cp1252(content) + array_utf8 = content.unpack('U*') + array_enc = [] + array_utf8.each do |num| + case num + # shortcut first bunch basic characters + when 0..0xFF; array_enc << num + # characters added compared to iso-8859-1 + when 0x20AC; array_enc << 0x80 # 0xe2 0x82 0xac + when 0x201A; array_enc << 0x82 # 0xe2 0x82 0x9a + when 0x0192; array_enc << 0x83 # 0xc6 0x92 + when 0x201E; array_enc << 0x84 # 0xe2 0x82 0x9e + when 0x2026; array_enc << 0x85 # 0xe2 0x80 0xa6 + when 0x2020; array_enc << 0x86 # 0xe2 0x80 0xa0 + when 0x2021; array_enc << 0x87 # 0xe2 0x80 0xa1 + when 0x02C6; array_enc << 0x88 # 0xcb 0x86 + when 0x2030; array_enc << 0x89 # 0xe2 0x80 0xb0 + when 0x0160; array_enc << 0x8A # 0xc5 0xa0 + when 0x2039; array_enc << 0x8B # 0xe2 0x80 0xb9 + when 0x0152; array_enc << 0x8C # 0xc5 0x92 + when 0x017D; array_enc << 0x8E # 0xc5 0xbd + when 0x2018; array_enc << 0x91 # 0xe2 0x80 0x98 + when 0x2019; array_enc << 0x92 # 0xe2 0x80 0x99 + when 0x201C; array_enc << 0x93 # 0xe2 0x80 0x9c + when 0x201D; array_enc << 0x94 # 0xe2 0x80 0x9d + when 0x2022; array_enc << 0x95 # 0xe2 0x80 0xa2 + when 0x2013; array_enc << 0x96 # 0xe2 0x80 0x93 + when 0x2014; array_enc << 0x97 # 0xe2 0x80 0x94 + when 0x02DC; array_enc << 0x98 # 0xcb 0x9c + when 0x2122; array_enc << 0x99 # 0xe2 0x84 0xa2 + when 0x0161; array_enc << 0x9A # 0xc5 0xa1 + when 0x203A; array_enc << 0x9B # 0xe2 0x80 0xba + when 0x0152; array_enc << 0x9C # 0xc5 0x93 + when 0x017E; array_enc << 0x9E # 0xc5 0xbe + when 0x0178; array_enc << 0x9F # 0xc5 0xb8 + else + # all remaining basic characters can be used directly + if num <= 0xFF + array_enc << num + else + # Numeric entity (&#nnnn;); shard by Stefan Scholl + array_enc.concat "&\##{num};".unpack('C*') + end + end + end + array_enc.pack('C*') + end + + # Convert to UTF-8 + def decode_cp1252(str) + array_latin9 = str.unpack('C*') + array_enc = [] + array_latin9.each do |num| + case num + # characters that added compared to iso-8859-1 + when 0x80; array_enc << 0x20AC # 0xe2 0x82 0xac + when 0x82; array_enc << 0x201A # 0xe2 0x82 0x9a + when 0x83; array_enc << 0x0192 # 0xc6 0x92 + when 0x84; array_enc << 0x201E # 0xe2 0x82 0x9e + when 0x85; array_enc << 0x2026 # 0xe2 0x80 0xa6 + when 0x86; array_enc << 0x2020 # 0xe2 0x80 0xa0 + when 0x87; array_enc << 0x2021 # 0xe2 0x80 0xa1 + when 0x88; array_enc << 0x02C6 # 0xcb 0x86 + when 0x89; array_enc << 0x2030 # 0xe2 0x80 0xb0 + when 0x8A; array_enc << 0x0160 # 0xc5 0xa0 + when 0x8B; array_enc << 0x2039 # 0xe2 0x80 0xb9 + when 0x8C; array_enc << 0x0152 # 0xc5 0x92 + when 0x8E; array_enc << 0x017D # 0xc5 0xbd + when 0x91; array_enc << 0x2018 # 0xe2 0x80 0x98 + when 0x92; array_enc << 0x2019 # 0xe2 0x80 0x99 + when 0x93; array_enc << 0x201C # 0xe2 0x80 0x9c + when 0x94; array_enc << 0x201D # 0xe2 0x80 0x9d + when 0x95; array_enc << 0x2022 # 0xe2 0x80 0xa2 + when 0x96; array_enc << 0x2013 # 0xe2 0x80 0x93 + when 0x97; array_enc << 0x2014 # 0xe2 0x80 0x94 + when 0x98; array_enc << 0x02DC # 0xcb 0x9c + when 0x99; array_enc << 0x2122 # 0xe2 0x84 0xa2 + when 0x9A; array_enc << 0x0161 # 0xc5 0xa1 + when 0x9B; array_enc << 0x203A # 0xe2 0x80 0xba + when 0x9C; array_enc << 0x0152 # 0xc5 0x93 + when 0x9E; array_enc << 0x017E # 0xc5 0xbe + when 0x9F; array_enc << 0x0178 # 0xc5 0xb8 + else + array_enc << num + end + end + array_enc.pack('U*') + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/EUC-JP.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/EUC-JP.rb new file mode 100644 index 0000000000..db37b6bf0d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/EUC-JP.rb @@ -0,0 +1,35 @@ +module REXML + module Encoding + begin + require 'uconv' + + def decode_eucjp(str) + Uconv::euctou8(str) + end + + def encode_eucjp content + Uconv::u8toeuc(content) + end + rescue LoadError + require 'nkf' + + EUCTOU8 = '-Ewm0' + U8TOEUC = '-Wem0' + + def decode_eucjp(str) + NKF.nkf(EUCTOU8, str) + end + + def encode_eucjp content + NKF.nkf(U8TOEUC, content) + end + end + + register("EUC-JP") do |obj| + class << obj + alias decode decode_eucjp + alias encode encode_eucjp + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/ICONV.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/ICONV.rb new file mode 100644 index 0000000000..172fba7cd1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/ICONV.rb @@ -0,0 +1,22 @@ +require "iconv" +raise LoadError unless defined? Iconv + +module REXML + module Encoding + def decode_iconv(str) + Iconv.conv(UTF_8, @encoding, str) + end + + def encode_iconv(content) + Iconv.conv(@encoding, UTF_8, content) + end + + register("ICONV") do |obj| + Iconv.conv(UTF_8, obj.encoding, nil) + class << obj + alias decode decode_iconv + alias encode encode_iconv + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/ISO-8859-1.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/ISO-8859-1.rb new file mode 100644 index 0000000000..2873d13bf0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/ISO-8859-1.rb @@ -0,0 +1,7 @@ +require 'rexml/encodings/US-ASCII' + +module REXML + module Encoding + register("ISO-8859-1", &encoding_method("US-ASCII")) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/ISO-8859-15.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/ISO-8859-15.rb new file mode 100644 index 0000000000..8dea0d38a4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/ISO-8859-15.rb @@ -0,0 +1,72 @@ +# +# This class was contributed by Mikko Tiihonen mikko DOT tiihonen AT hut DOT fi +# +module REXML + module Encoding + register("ISO-8859-15") do |o| + alias encode to_iso_8859_15 + alias decode from_iso_8859_15 + end + + # Convert from UTF-8 + def to_iso_8859_15(content) + array_utf8 = content.unpack('U*') + array_enc = [] + array_utf8.each do |num| + case num + # shortcut first bunch basic characters + when 0..0xA3; array_enc << num + # characters removed compared to iso-8859-1 + when 0xA4; array_enc << '¤' + when 0xA6; array_enc << '¦' + when 0xA8; array_enc << '¨' + when 0xB4; array_enc << '´' + when 0xB8; array_enc << '¸' + when 0xBC; array_enc << '¼' + when 0xBD; array_enc << '½' + when 0xBE; array_enc << '¾' + # characters added compared to iso-8859-1 + when 0x20AC; array_enc << 0xA4 # 0xe2 0x82 0xac + when 0x0160; array_enc << 0xA6 # 0xc5 0xa0 + when 0x0161; array_enc << 0xA8 # 0xc5 0xa1 + when 0x017D; array_enc << 0xB4 # 0xc5 0xbd + when 0x017E; array_enc << 0xB8 # 0xc5 0xbe + when 0x0152; array_enc << 0xBC # 0xc5 0x92 + when 0x0153; array_enc << 0xBD # 0xc5 0x93 + when 0x0178; array_enc << 0xBE # 0xc5 0xb8 + else + # all remaining basic characters can be used directly + if num <= 0xFF + array_enc << num + else + # Numeric entity (&#nnnn;); shard by Stefan Scholl + array_enc.concat "&\##{num};".unpack('C*') + end + end + end + array_enc.pack('C*') + end + + # Convert to UTF-8 + def from_iso_8859_15(str) + array_latin9 = str.unpack('C*') + array_enc = [] + array_latin9.each do |num| + case num + # characters that differ compared to iso-8859-1 + when 0xA4; array_enc << 0x20AC + when 0xA6; array_enc << 0x0160 + when 0xA8; array_enc << 0x0161 + when 0xB4; array_enc << 0x017D + when 0xB8; array_enc << 0x017E + when 0xBC; array_enc << 0x0152 + when 0xBD; array_enc << 0x0153 + when 0xBE; array_enc << 0x0178 + else + array_enc << num + end + end + array_enc.pack('U*') + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/SHIFT-JIS.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/SHIFT-JIS.rb new file mode 100644 index 0000000000..93c7877afd --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/SHIFT-JIS.rb @@ -0,0 +1,37 @@ +module REXML + module Encoding + begin + require 'uconv' + + def decode_sjis content + Uconv::sjistou8(content) + end + + def encode_sjis(str) + Uconv::u8tosjis(str) + end + rescue LoadError + require 'nkf' + + SJISTOU8 = '-Swm0' + U8TOSJIS = '-Wsm0' + + def decode_sjis(str) + NKF.nkf(SJISTOU8, str) + end + + def encode_sjis content + NKF.nkf(U8TOSJIS, content) + end + end + + b = proc do |obj| + class << obj + alias decode decode_sjis + alias encode encode_sjis + end + end + register("SHIFT-JIS", &b) + register("SHIFT_JIS", &b) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/SHIFT_JIS.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/SHIFT_JIS.rb new file mode 100644 index 0000000000..e355704a7c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/SHIFT_JIS.rb @@ -0,0 +1 @@ +require 'rexml/encodings/SHIFT-JIS' diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/UNILE.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/UNILE.rb new file mode 100644 index 0000000000..d054140c40 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/UNILE.rb @@ -0,0 +1,34 @@ +module REXML + module Encoding + def encode_unile content + array_utf8 = content.unpack("U*") + array_enc = [] + array_utf8.each do |num| + if ((num>>16) > 0) + array_enc << ?? + array_enc << 0 + else + array_enc << (num & 0xFF) + array_enc << (num >> 8) + end + end + array_enc.pack('C*') + end + + def decode_unile(str) + array_enc=str.unpack('C*') + array_utf8 = [] + 0.step(array_enc.size-1, 2){|i| + array_utf8 << (array_enc.at(i) + array_enc.at(i+1)*0x100) + } + array_utf8.pack('U*') + end + + register(UNILE) do |obj| + class << obj + alias decode decode_unile + alias encode encode_unile + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/US-ASCII.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/US-ASCII.rb new file mode 100644 index 0000000000..fb4c217074 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/US-ASCII.rb @@ -0,0 +1,30 @@ +module REXML + module Encoding + # Convert from UTF-8 + def encode_ascii content + array_utf8 = content.unpack('U*') + array_enc = [] + array_utf8.each do |num| + if num <= 0x7F + array_enc << num + else + # Numeric entity (&#nnnn;); shard by Stefan Scholl + array_enc.concat "&\##{num};".unpack('C*') + end + end + array_enc.pack('C*') + end + + # Convert to UTF-8 + def decode_ascii(str) + str.unpack('C*').pack('U*') + end + + register("US-ASCII") do |obj| + class << obj + alias decode decode_ascii + alias encode encode_ascii + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/UTF-16.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/UTF-16.rb new file mode 100644 index 0000000000..007c493d9c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/UTF-16.rb @@ -0,0 +1,35 @@ +module REXML + module Encoding + def encode_utf16 content + array_utf8 = content.unpack("U*") + array_enc = [] + array_utf8.each do |num| + if ((num>>16) > 0) + array_enc << 0 + array_enc << ?? + else + array_enc << (num >> 8) + array_enc << (num & 0xFF) + end + end + array_enc.pack('C*') + end + + def decode_utf16(str) + str = str[2..-1] if /^\376\377/n =~ str + array_enc=str.unpack('C*') + array_utf8 = [] + 0.step(array_enc.size-1, 2){|i| + array_utf8 << (array_enc.at(i+1) + array_enc.at(i)*0x100) + } + array_utf8.pack('U*') + end + + register(UTF_16) do |obj| + class << obj + alias decode decode_utf16 + alias encode encode_utf16 + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/UTF-8.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/UTF-8.rb new file mode 100644 index 0000000000..bb08f44100 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/encodings/UTF-8.rb @@ -0,0 +1,18 @@ +module REXML + module Encoding + def encode_utf8 content + content + end + + def decode_utf8(str) + str + end + + register(UTF_8) do |obj| + class << obj + alias decode decode_utf8 + alias encode encode_utf8 + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/entity.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/entity.rb new file mode 100644 index 0000000000..4f47e65d05 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/entity.rb @@ -0,0 +1,165 @@ +require 'rexml/child' +require 'rexml/source' +require 'rexml/xmltokens' + +module REXML + # God, I hate DTDs. I really do. Why this ##### standard still + # plagues us is beyond me. + class Entity < Child + include XMLTokens + PUBIDCHAR = "\x20\x0D\x0Aa-zA-Z0-9\\-()+,./:=?;!*@$_%#" + SYSTEMLITERAL = %Q{((?:"[^"]*")|(?:'[^']*'))} + PUBIDLITERAL = %Q{("[#{PUBIDCHAR}']*"|'[#{PUBIDCHAR}]*')} + EXTERNALID = "(?:(?:(SYSTEM)\\s+#{SYSTEMLITERAL})|(?:(PUBLIC)\\s+#{PUBIDLITERAL}\\s+#{SYSTEMLITERAL}))" + NDATADECL = "\\s+NDATA\\s+#{NAME}" + PEREFERENCE = "%#{NAME};" + ENTITYVALUE = %Q{((?:"(?:[^%&"]|#{PEREFERENCE}|#{REFERENCE})*")|(?:'([^%&']|#{PEREFERENCE}|#{REFERENCE})*'))} + PEDEF = "(?:#{ENTITYVALUE}|#{EXTERNALID})" + ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))" + PEDECL = "" + GEDECL = "" + ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um + + attr_reader :name, :external, :ref, :ndata, :pubid + + # Create a new entity. Simple entities can be constructed by passing a + # name, value to the constructor; this creates a generic, plain entity + # reference. For anything more complicated, you have to pass a Source to + # the constructor with the entity definiton, or use the accessor methods. + # +WARNING+: There is no validation of entity state except when the entity + # is read from a stream. If you start poking around with the accessors, + # you can easily create a non-conformant Entity. The best thing to do is + # dump the stupid DTDs and use XMLSchema instead. + # + # e = Entity.new( 'amp', '&' ) + def initialize stream, value=nil, parent=nil, reference=false + super(parent) + @ndata = @pubid = @value = @external = nil + if stream.kind_of? Array + @name = stream[1] + if stream[-1] == '%' + @reference = true + stream.pop + else + @reference = false + end + if stream[2] =~ /SYSTEM|PUBLIC/ + @external = stream[2] + if @external == 'SYSTEM' + @ref = stream[3] + @ndata = stream[4] if stream.size == 5 + else + @pubid = stream[3] + @ref = stream[4] + end + else + @value = stream[2] + end + else + @reference = reference + @external = nil + @name = stream + @value = value + end + end + + # Evaluates whether the given string matchs an entity definition, + # returning true if so, and false otherwise. + def Entity::matches? string + (ENTITYDECL =~ string) == 0 + end + + # Evaluates to the unnormalized value of this entity; that is, replacing + # all entities -- both %ent; and &ent; entities. This differs from + # +value()+ in that +value+ only replaces %ent; entities. + def unnormalized + v = value() + return nil if v.nil? + @unnormalized = Text::unnormalize(v, parent) + @unnormalized + end + + #once :unnormalized + + # Returns the value of this entity unprocessed -- raw. This is the + # normalized value; that is, with all %ent; and &ent; entities intact + def normalized + @value + end + + # Write out a fully formed, correct entity definition (assuming the Entity + # object itself is valid.) + # + # out:: + # An object implementing << to which the entity will be + # output + # indent:: + # *DEPRECATED* and ignored + def write out, indent=-1 + out << '' + end + + # Returns this entity as a string. See write(). + def to_s + rv = '' + write rv + rv + end + + PEREFERENCE_RE = /#{PEREFERENCE}/um + # Returns the value of this entity. At the moment, only internal entities + # are processed. If the value contains internal references (IE, + # %blah;), those are replaced with their values. IE, if the doctype + # contains: + # + # + # then: + # doctype.entity('yada').value #-> "nanoo bar nanoo" + def value + if @value + matches = @value.scan(PEREFERENCE_RE) + rv = @value.clone + if @parent + matches.each do |entity_reference| + entity_value = @parent.entity( entity_reference[0] ) + rv.gsub!( /%#{entity_reference};/um, entity_value ) + end + end + return rv + end + nil + end + end + + # This is a set of entity constants -- the ones defined in the XML + # specification. These are +gt+, +lt+, +amp+, +quot+ and +apos+. + module EntityConst + # +>+ + GT = Entity.new( 'gt', '>' ) + # +<+ + LT = Entity.new( 'lt', '<' ) + # +&+ + AMP = Entity.new( 'amp', '&' ) + # +"+ + QUOT = Entity.new( 'quot', '"' ) + # +'+ + APOS = Entity.new( 'apos', "'" ) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/formatters/default.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/formatters/default.rb new file mode 100644 index 0000000000..77381bdf84 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/formatters/default.rb @@ -0,0 +1,109 @@ +module REXML + module Formatters + class Default + # Prints out the XML document with no formatting -- except if id_hack is + # set. + # + # ie_hack:: + # If set to true, then inserts whitespace before the close of an empty + # tag, so that IE's bad XML parser doesn't choke. + def initialize( ie_hack=false ) + @ie_hack = ie_hack + end + + # Writes the node to some output. + # + # node:: + # The node to write + # output:: + # A class implementing <<. Pass in an Output object to + # change the output encoding. + def write( node, output ) + case node + + when Document + if node.xml_decl.encoding != "UTF-8" && !output.kind_of?(Output) + output = Output.new( output, node.xml_decl.encoding ) + end + write_document( node, output ) + + when Element + write_element( node, output ) + + when Declaration, ElementDecl, NotationDecl, ExternalEntity, Entity, + Attribute, AttlistDecl + node.write( output,-1 ) + + when Instruction + write_instruction( node, output ) + + when DocType, XMLDecl + node.write( output ) + + when Comment + write_comment( node, output ) + + when CData + write_cdata( node, output ) + + when Text + write_text( node, output ) + + else + raise Exception.new("XML FORMATTING ERROR") + + end + end + + protected + def write_document( node, output ) + node.children.each { |child| write( child, output ) } + end + + def write_element( node, output ) + output << "<#{node.expanded_name}" + + node.attributes.each_attribute do |attr| + output << " " + attr.write( output ) + end unless node.attributes.empty? + + if node.children.empty? + output << " " if @ie_hack + output << "/" + else + output << ">" + node.children.each { |child| + write( child, output ) + } + output << "" + end + + def write_text( node, output ) + output << node.to_s() + end + + def write_comment( node, output ) + output << Comment::START + output << node.to_s + output << Comment::STOP + end + + def write_cdata( node, output ) + output << CData::START + output << node.to_s + output << CData::STOP + end + + def write_instruction( node, output ) + output << Instruction::START.sub(/\\/u, '') + output << node.target + output << ' ' + output << node.content + output << Instruction::STOP.sub(/\\/u, '') + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/formatters/pretty.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/formatters/pretty.rb new file mode 100644 index 0000000000..22b6d857cd --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/formatters/pretty.rb @@ -0,0 +1,137 @@ +require 'rexml/formatters/default' + +module REXML + module Formatters + # Pretty-prints an XML document. This destroys whitespace in text nodes + # and will insert carriage returns and indentations. + # + # TODO: Add an option to print attributes on new lines + class Pretty < Default + + # If compact is set to true, then the formatter will attempt to use as + # little space as possible + attr_accessor :compact + # The width of a page. Used for formatting text + attr_accessor :width + + # Create a new pretty printer. + # + # output:: + # An object implementing '<<(String)', to which the output will be written. + # indentation:: + # An integer greater than 0. The indentation of each level will be + # this number of spaces. If this is < 1, the behavior of this object + # is undefined. Defaults to 2. + # ie_hack:: + # If true, the printer will insert whitespace before closing empty + # tags, thereby allowing Internet Explorer's feeble XML parser to + # function. Defaults to false. + def initialize( indentation=2, ie_hack=false ) + @indentation = indentation + @level = 0 + @ie_hack = ie_hack + @width = 80 + end + + protected + def write_element(node, output) + output << ' '*@level + output << "<#{node.expanded_name}" + + node.attributes.each_attribute do |attr| + output << " " + attr.write( output ) + end unless node.attributes.empty? + + if node.children.empty? + if @ie_hack + output << " " + end + output << "/" + else + output << ">" + # If compact and all children are text, and if the formatted output + # is less than the specified width, then try to print everything on + # one line + skip = false + if compact + if node.children.inject(true) {|s,c| s & c.kind_of?(Text)} + string = "" + old_level = @level + @level = 0 + node.children.each { |child| write( child, string ) } + @level = old_level + if string.length < @width + output << string + skip = true + end + end + end + unless skip + output << "\n" + @level += @indentation + node.children.each { |child| + next if child.kind_of?(Text) and child.to_s.strip.length == 0 + write( child, output ) + output << "\n" + } + @level -= @indentation + output << ' '*@level + end + output << "" + end + + def write_text( node, output ) + s = node.to_s() + s.gsub!(/\s/,' ') + s.squeeze!(" ") + s = wrap(s, 80-@level) + s = indent_text(s, @level, " ", true) + output << (' '*@level + s) + end + + def write_comment( node, output) + output << ' ' * @level + super + end + + def write_cdata( node, output) + output << ' ' * @level + super + end + + def write_document( node, output ) + # Ok, this is a bit odd. All XML documents have an XML declaration, + # but it may not write itself if the user didn't specifically add it, + # either through the API or in the input document. If it doesn't write + # itself, then we don't need a carriage return... which makes this + # logic more complex. + node.children.each { |child| + next if child == node.children[-1] and child.instance_of?(Text) + unless child == node.children[0] or child.instance_of?(Text) or + (child == node.children[1] and !node.children[0].writethis) + output << "\n" + end + write( child, output ) + } + end + + private + def indent_text(string, level=1, style="\t", indentfirstline=true) + return string if level < 0 + string.gsub(/\n/, "\n#{style*level}") + end + + def wrap(string, width) + # Recursivly wrap string at width. + return string if string.length <= width + place = string.rindex(' ', width) # Position in string with last ' ' before cutoff + return string[0,place] + "\n" + wrap(string[place+1..-1], width) + end + + end + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/formatters/transitive.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/formatters/transitive.rb new file mode 100644 index 0000000000..1d80f21fbb --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/formatters/transitive.rb @@ -0,0 +1,56 @@ +require 'rexml/formatters/pretty' + +module REXML + module Formatters + # The Transitive formatter writes an XML document that parses to an + # identical document as the source document. This means that no extra + # whitespace nodes are inserted, and whitespace within text nodes is + # preserved. Within these constraints, the document is pretty-printed, + # with whitespace inserted into the metadata to introduce formatting. + # + # Note that this is only useful if the original XML is not already + # formatted. Since this formatter does not alter whitespace nodes, the + # results of formatting already formatted XML will be odd. + class Transitive < Default + def initialize( indentation=2 ) + @indentation = indentation + @level = 0 + end + + protected + def write_element( node, output ) + output << "<#{node.expanded_name}" + + node.attributes.each_attribute do |attr| + output << " " + attr.write( output ) + end unless node.attributes.empty? + + output << "\n" + output << ' '*@level + if node.children.empty? + output << "/" + else + output << ">" + # If compact and all children are text, and if the formatted output + # is less than the specified width, then try to print everything on + # one line + skip = false + @level += @indentation + node.children.each { |child| + write( child, output ) + } + @level -= @indentation + output << "" + end + + def write_text( node, output ) + output << node.to_s() + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/functions.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/functions.rb new file mode 100644 index 0000000000..8293e9c5ac --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/functions.rb @@ -0,0 +1,382 @@ +module REXML + # If you add a method, keep in mind two things: + # (1) the first argument will always be a list of nodes from which to + # filter. In the case of context methods (such as position), the function + # should return an array with a value for each child in the array. + # (2) all method calls from XML will have "-" replaced with "_". + # Therefore, in XML, "local-name()" is identical (and actually becomes) + # "local_name()" + module Functions + @@context = nil + @@namespace_context = {} + @@variables = {} + + def Functions::namespace_context=(x) ; @@namespace_context=x ; end + def Functions::variables=(x) ; @@variables=x ; end + def Functions::namespace_context ; @@namespace_context ; end + def Functions::variables ; @@variables ; end + + def Functions::context=(value); @@context = value; end + + def Functions::text( ) + if @@context[:node].node_type == :element + return @@context[:node].find_all{|n| n.node_type == :text}.collect{|n| n.value} + elsif @@context[:node].node_type == :text + return @@context[:node].value + else + return false + end + end + + def Functions::last( ) + @@context[:size] + end + + def Functions::position( ) + @@context[:index] + end + + def Functions::count( node_set ) + node_set.size + end + + # Since REXML is non-validating, this method is not implemented as it + # requires a DTD + def Functions::id( object ) + end + + # UNTESTED + def Functions::local_name( node_set=nil ) + get_namespace( node_set ) do |node| + return node.local_name + end + end + + def Functions::namespace_uri( node_set=nil ) + get_namespace( node_set ) {|node| node.namespace} + end + + def Functions::name( node_set=nil ) + get_namespace( node_set ) do |node| + node.expanded_name + end + end + + # Helper method. + def Functions::get_namespace( node_set = nil ) + if node_set == nil + yield @@context[:node] if defined? @@context[:node].namespace + else + if node_set.respond_to? :each + node_set.each { |node| yield node if defined? node.namespace } + elsif node_set.respond_to? :namespace + yield node_set + end + end + end + + # A node-set is converted to a string by returning the string-value of the + # node in the node-set that is first in document order. If the node-set is + # empty, an empty string is returned. + # + # A number is converted to a string as follows + # + # NaN is converted to the string NaN + # + # positive zero is converted to the string 0 + # + # negative zero is converted to the string 0 + # + # positive infinity is converted to the string Infinity + # + # negative infinity is converted to the string -Infinity + # + # if the number is an integer, the number is represented in decimal form + # as a Number with no decimal point and no leading zeros, preceded by a + # minus sign (-) if the number is negative + # + # otherwise, the number is represented in decimal form as a Number + # including a decimal point with at least one digit before the decimal + # point and at least one digit after the decimal point, preceded by a + # minus sign (-) if the number is negative; there must be no leading zeros + # before the decimal point apart possibly from the one required digit + # immediately before the decimal point; beyond the one required digit + # after the decimal point there must be as many, but only as many, more + # digits as are needed to uniquely distinguish the number from all other + # IEEE 754 numeric values. + # + # The boolean false value is converted to the string false. The boolean + # true value is converted to the string true. + # + # An object of a type other than the four basic types is converted to a + # string in a way that is dependent on that type. + def Functions::string( object=nil ) + #object = @context unless object + if object.instance_of? Array + string( object[0] ) + elsif defined? object.node_type + if object.node_type == :attribute + object.value + elsif object.node_type == :element || object.node_type == :document + string_value(object) + else + object.to_s + end + elsif object.nil? + return "" + else + object.to_s + end + end + + def Functions::string_value( o ) + rv = "" + o.children.each { |e| + if e.node_type == :text + rv << e.to_s + elsif e.node_type == :element + rv << string_value( e ) + end + } + rv + end + + # UNTESTED + def Functions::concat( *objects ) + objects.join + end + + # Fixed by Mike Stok + def Functions::starts_with( string, test ) + string(string).index(string(test)) == 0 + end + + # Fixed by Mike Stok + def Functions::contains( string, test ) + string(string).include?(string(test)) + end + + # Kouhei fixed this + def Functions::substring_before( string, test ) + ruby_string = string(string) + ruby_index = ruby_string.index(string(test)) + if ruby_index.nil? + "" + else + ruby_string[ 0...ruby_index ] + end + end + + # Kouhei fixed this too + def Functions::substring_after( string, test ) + ruby_string = string(string) + test_string = string(test) + return $1 if ruby_string =~ /#{test}(.*)/ + "" + end + + # Take equal portions of Mike Stok and Sean Russell; mix + # vigorously, and pour into a tall, chilled glass. Serves 10,000. + def Functions::substring( string, start, length=nil ) + ruby_string = string(string) + ruby_length = if length.nil? + ruby_string.length.to_f + else + number(length) + end + ruby_start = number(start) + + # Handle the special cases + return '' if ( + ruby_length.nan? or + ruby_start.nan? or + ruby_start.infinite? + ) + + infinite_length = ruby_length.infinite? == 1 + ruby_length = ruby_string.length if infinite_length + + # Now, get the bounds. The XPath bounds are 1..length; the ruby bounds + # are 0..length. Therefore, we have to offset the bounds by one. + ruby_start = ruby_start.round - 1 + ruby_length = ruby_length.round + + if ruby_start < 0 + ruby_length += ruby_start unless infinite_length + ruby_start = 0 + end + return '' if ruby_length <= 0 + ruby_string[ruby_start,ruby_length] + end + + # UNTESTED + def Functions::string_length( string ) + string(string).length + end + + # UNTESTED + def Functions::normalize_space( string=nil ) + string = string(@@context[:node]) if string.nil? + if string.kind_of? Array + string.collect{|x| string.to_s.strip.gsub(/\s+/um, ' ') if string} + else + string.to_s.strip.gsub(/\s+/um, ' ') + end + end + + # This is entirely Mike Stok's beast + def Functions::translate( string, tr1, tr2 ) + from = string(tr1) + to = string(tr2) + + # the map is our translation table. + # + # if a character occurs more than once in the + # from string then we ignore the second & + # subsequent mappings + # + # if a charactcer maps to nil then we delete it + # in the output. This happens if the from + # string is longer than the to string + # + # there's nothing about - or ^ being special in + # http://www.w3.org/TR/xpath#function-translate + # so we don't build ranges or negated classes + + map = Hash.new + 0.upto(from.length - 1) { |pos| + from_char = from[pos] + unless map.has_key? from_char + map[from_char] = + if pos < to.length + to[pos] + else + nil + end + end + } + + string(string).unpack('U*').collect { |c| + if map.has_key? c then map[c] else c end + }.compact.pack('U*') + end + + # UNTESTED + def Functions::boolean( object=nil ) + if object.kind_of? String + if object =~ /\d+/u + return object.to_f != 0 + else + return object.size > 0 + end + elsif object.kind_of? Array + object = object.find{|x| x and true} + end + return object ? true : false + end + + # UNTESTED + def Functions::not( object ) + not boolean( object ) + end + + # UNTESTED + def Functions::true( ) + true + end + + # UNTESTED + def Functions::false( ) + false + end + + # UNTESTED + def Functions::lang( language ) + lang = false + node = @@context[:node] + attr = nil + until node.nil? + if node.node_type == :element + attr = node.attributes["xml:lang"] + unless attr.nil? + lang = compare_language(string(language), attr) + break + else + end + end + node = node.parent + end + lang + end + + def Functions::compare_language lang1, lang2 + lang2.downcase.index(lang1.downcase) == 0 + end + + # a string that consists of optional whitespace followed by an optional + # minus sign followed by a Number followed by whitespace is converted to + # the IEEE 754 number that is nearest (according to the IEEE 754 + # round-to-nearest rule) to the mathematical value represented by the + # string; any other string is converted to NaN + # + # boolean true is converted to 1; boolean false is converted to 0 + # + # a node-set is first converted to a string as if by a call to the string + # function and then converted in the same way as a string argument + # + # an object of a type other than the four basic types is converted to a + # number in a way that is dependent on that type + def Functions::number( object=nil ) + object = @@context[:node] unless object + case object + when true + Float(1) + when false + Float(0) + when Array + number(string( object )) + when Numeric + object.to_f + else + str = string( object ) + # If XPath ever gets scientific notation... + #if str =~ /^\s*-?(\d*\.?\d+|\d+\.)([Ee]\d*)?\s*$/ + if str =~ /^\s*-?(\d*\.?\d+|\d+\.)\s*$/ + str.to_f + else + (0.0 / 0.0) + end + end + end + + def Functions::sum( nodes ) + nodes = [nodes] unless nodes.kind_of? Array + nodes.inject(0) { |r,n| r += number(string(n)) } + end + + def Functions::floor( number ) + number(number).floor + end + + def Functions::ceiling( number ) + number(number).ceil + end + + def Functions::round( number ) + begin + number(number).round + rescue FloatDomainError + number(number) + end + end + + def Functions::processing_instruction( node ) + node.node_type == :processing_instruction + end + + def Functions::method_missing( id ) + puts "METHOD MISSING #{id.id2name}" + XPath.match( @@context[:node], id.id2name ) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/instruction.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/instruction.rb new file mode 100644 index 0000000000..c16b894b4a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/instruction.rb @@ -0,0 +1,70 @@ +require "rexml/child" +require "rexml/source" + +module REXML + # Represents an XML Instruction; IE, + # TODO: Add parent arg (3rd arg) to constructor + class Instruction < Child + START = '<\?' + STOP = '\?>' + + # target is the "name" of the Instruction; IE, the "tag" in + # content is everything else. + attr_accessor :target, :content + + # Constructs a new Instruction + # @param target can be one of a number of things. If String, then + # the target of this instruction is set to this. If an Instruction, + # then the Instruction is shallowly cloned (target and content are + # copied). If a Source, then the source is scanned and parsed for + # an Instruction declaration. + # @param content Must be either a String, or a Parent. Can only + # be a Parent if the target argument is a Source. Otherwise, this + # String is set as the content of this instruction. + def initialize(target, content=nil) + if target.kind_of? String + super() + @target = target + @content = content + elsif target.kind_of? Instruction + super(content) + @target = target.target + @content = target.content + end + @content.strip! if @content + end + + def clone + Instruction.new self + end + + # == DEPRECATED + # See the rexml/formatters package + # + def write writer, indent=-1, transitive=false, ie_hack=false + Kernel.warn( "#{self.class.name}.write is deprecated" ) + indent(writer, indent) + writer << START.sub(/\\/u, '') + writer << @target + writer << ' ' + writer << @content + writer << STOP.sub(/\\/u, '') + end + + # @return true if other is an Instruction, and the content and target + # of the other matches the target and content of this object. + def ==( other ) + other.kind_of? Instruction and + other.target == @target and + other.content == @content + end + + def node_type + :processing_instruction + end + + def inspect + "" + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/light/node.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/light/node.rb new file mode 100644 index 0000000000..943ec5f1a0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/light/node.rb @@ -0,0 +1,196 @@ +require 'rexml/xmltokens' +require 'rexml/light/node' + +# [ :element, parent, name, attributes, children* ] + # a = Node.new + # a << "B" # => B + # a.b # => B + # a.b[1] # => B + # a.b[1]["x"] = "y" # => B + # a.b[0].c # => B + # a.b.c << "D" # => BD +module REXML + module Light + # Represents a tagged XML element. Elements are characterized by + # having children, attributes, and names, and can themselves be + # children. + class Node + NAMESPLIT = /^(?:(#{XMLTokens::NCNAME_STR}):)?(#{XMLTokens::NCNAME_STR})/u + PARENTS = [ :element, :document, :doctype ] + # Create a new element. + def initialize node=nil + @node = node + if node.kind_of? String + node = [ :text, node ] + elsif node.nil? + node = [ :document, nil, nil ] + elsif node[0] == :start_element + node[0] = :element + elsif node[0] == :start_doctype + node[0] = :doctype + elsif node[0] == :start_document + node[0] = :document + end + end + + def size + if PARENTS.include? @node[0] + @node[-1].size + else + 0 + end + end + + def each( &block ) + size.times { |x| yield( at(x+4) ) } + end + + def name + at(2) + end + + def name=( name_str, ns=nil ) + pfx = '' + pfx = "#{prefix(ns)}:" if ns + _old_put(2, "#{pfx}#{name_str}") + end + + def parent=( node ) + _old_put(1,node) + end + + def local_name + namesplit + @name + end + + def local_name=( name_str ) + _old_put( 1, "#@prefix:#{name_str}" ) + end + + def prefix( namespace=nil ) + prefix_of( self, namespace ) + end + + def namespace( prefix=prefix() ) + namespace_of( self, prefix ) + end + + def namespace=( namespace ) + @prefix = prefix( namespace ) + pfx = '' + pfx = "#@prefix:" if @prefix.size > 0 + _old_put(1, "#{pfx}#@name") + end + + def []( reference, ns=nil ) + if reference.kind_of? String + pfx = '' + pfx = "#{prefix(ns)}:" if ns + at(3)["#{pfx}#{reference}"] + elsif reference.kind_of? Range + _old_get( Range.new(4+reference.begin, reference.end, reference.exclude_end?) ) + else + _old_get( 4+reference ) + end + end + + def =~( path ) + XPath.match( self, path ) + end + + # Doesn't handle namespaces yet + def []=( reference, ns, value=nil ) + if reference.kind_of? String + value = ns unless value + at( 3 )[reference] = value + elsif reference.kind_of? Range + _old_put( Range.new(3+reference.begin, reference.end, reference.exclude_end?), ns ) + else + if value + _old_put( 4+reference, ns, value ) + else + _old_put( 4+reference, ns ) + end + end + end + + # Append a child to this element, optionally under a provided namespace. + # The namespace argument is ignored if the element argument is an Element + # object. Otherwise, the element argument is a string, the namespace (if + # provided) is the namespace the element is created in. + def << element + if node_type() == :text + at(-1) << element + else + newnode = Node.new( element ) + newnode.parent = self + self.push( newnode ) + end + at(-1) + end + + def node_type + _old_get(0) + end + + def text=( foo ) + replace = at(4).kind_of?(String)? 1 : 0 + self._old_put(4,replace, normalizefoo) + end + + def root + context = self + context = context.at(1) while context.at(1) + end + + def has_name?( name, namespace = '' ) + at(3) == name and namespace() == namespace + end + + def children + self + end + + def parent + at(1) + end + + def to_s + + end + + private + + def namesplit + return if @name.defined? + at(2) =~ NAMESPLIT + @prefix = '' || $1 + @name = $2 + end + + def namespace_of( node, prefix=nil ) + if not prefix + name = at(2) + name =~ NAMESPLIT + prefix = $1 + end + to_find = 'xmlns' + to_find = "xmlns:#{prefix}" if not prefix.nil? + ns = at(3)[ to_find ] + ns ? ns : namespace_of( @node[0], prefix ) + end + + def prefix_of( node, namespace=nil ) + if not namespace + name = node.name + name =~ NAMESPLIT + $1 + else + ns = at(3).find { |k,v| v == namespace } + ns ? ns : prefix_of( node.parent, namespace ) + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/namespace.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/namespace.rb new file mode 100644 index 0000000000..3e8790580b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/namespace.rb @@ -0,0 +1,47 @@ +require 'rexml/xmltokens' + +module REXML + # Adds named attributes to an object. + module Namespace + # The name of the object, valid if set + attr_reader :name, :expanded_name + # The expanded name of the object, valid if name is set + attr_accessor :prefix + include XMLTokens + NAMESPLIT = /^(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})/u + + # Sets the name and the expanded name + def name=( name ) + @expanded_name = name + name =~ NAMESPLIT + if $1 + @prefix = $1 + else + @prefix = "" + @namespace = "" + end + @name = $2 + end + + # Compares names optionally WITH namespaces + def has_name?( other, ns=nil ) + if ns + return (namespace() == ns and name() == other) + elsif other.include? ":" + return fully_expanded_name == other + else + return name == other + end + end + + alias :local_name :name + + # Fully expand the name, even if the prefix wasn't specified in the + # source file. + def fully_expanded_name + ns = prefix + return "#{ns}:#@name" if ns.size > 0 + return @name + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/node.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/node.rb new file mode 100644 index 0000000000..9780376829 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/node.rb @@ -0,0 +1,75 @@ +require "rexml/parseexception" +require "rexml/formatters/pretty" +require "rexml/formatters/default" + +module REXML + # Represents a node in the tree. Nodes are never encountered except as + # superclasses of other objects. Nodes have siblings. + module Node + # @return the next sibling (nil if unset) + def next_sibling_node + return nil if @parent.nil? + @parent[ @parent.index(self) + 1 ] + end + + # @return the previous sibling (nil if unset) + def previous_sibling_node + return nil if @parent.nil? + ind = @parent.index(self) + return nil if ind == 0 + @parent[ ind - 1 ] + end + + # indent:: + # *DEPRECATED* This parameter is now ignored. See the formatters in the + # REXML::Formatters package for changing the output style. + def to_s indent=nil + unless indent.nil? + Kernel.warn( "#{self.class.name}.to_s(indent) parameter is deprecated" ) + f = REXML::Formatters::Pretty.new( indent ) + f.write( self, rv, indent ) + else + f = REXML::Formatters::Default.new + f.write( self, rv = "" ) + end + return rv + end + + def indent to, ind + if @parent and @parent.context and not @parent.context[:indentstyle].nil? then + indentstyle = @parent.context[:indentstyle] + else + indentstyle = ' ' + end + to << indentstyle*ind unless ind<1 + end + + def parent? + false; + end + + + # Visit all subnodes of +self+ recursively + def each_recursive(&block) # :yields: node + self.elements.each {|node| + block.call(node) + node.each_recursive(&block) + } + end + + # Find (and return) first subnode (recursively) for which the block + # evaluates to true. Returns +nil+ if none was found. + def find_first_recursive(&block) # :yields: node + each_recursive {|node| + return node if block.call(node) + } + return nil + end + + # Returns the position that +self+ holds in its parent's array, indexed + # from 1. + def index_in_parent + parent.index(self)+1 + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/output.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/output.rb new file mode 100644 index 0000000000..be4d23d42d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/output.rb @@ -0,0 +1,24 @@ +require 'rexml/encoding' + +module REXML + class Output + include Encoding + + attr_reader :encoding + + def initialize real_IO, encd="iso-8859-1" + @output = real_IO + self.encoding = encd + + @to_utf = encd == UTF_8 ? false : true + end + + def <<( content ) + @output << (@to_utf ? self.encode(content) : content) + end + + def to_s + "Output[#{encoding}]" + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parent.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parent.rb new file mode 100644 index 0000000000..a20aaaef6b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parent.rb @@ -0,0 +1,166 @@ +require "rexml/child" + +module REXML + # A parent has children, and has methods for accessing them. The Parent + # class is never encountered except as the superclass for some other + # object. + class Parent < Child + include Enumerable + + # Constructor + # @param parent if supplied, will be set as the parent of this object + def initialize parent=nil + super(parent) + @children = [] + end + + def add( object ) + #puts "PARENT GOTS #{size} CHILDREN" + object.parent = self + @children << object + #puts "PARENT NOW GOTS #{size} CHILDREN" + object + end + + alias :push :add + alias :<< :push + + def unshift( object ) + object.parent = self + @children.unshift object + end + + def delete( object ) + found = false + @children.delete_if {|c| c.equal?(object) and found = true } + object.parent = nil if found + end + + def each(&block) + @children.each(&block) + end + + def delete_if( &block ) + @children.delete_if(&block) + end + + def delete_at( index ) + @children.delete_at index + end + + def each_index( &block ) + @children.each_index(&block) + end + + # Fetches a child at a given index + # @param index the Integer index of the child to fetch + def []( index ) + @children[index] + end + + alias :each_child :each + + + + # Set an index entry. See Array.[]= + # @param index the index of the element to set + # @param opt either the object to set, or an Integer length + # @param child if opt is an Integer, this is the child to set + # @return the parent (self) + def []=( *args ) + args[-1].parent = self + @children[*args[0..-2]] = args[-1] + end + + # Inserts an child before another child + # @param child1 this is either an xpath or an Element. If an Element, + # child2 will be inserted before child1 in the child list of the parent. + # If an xpath, child2 will be inserted before the first child to match + # the xpath. + # @param child2 the child to insert + # @return the parent (self) + def insert_before( child1, child2 ) + if child1.kind_of? String + child1 = XPath.first( self, child1 ) + child1.parent.insert_before child1, child2 + else + ind = index(child1) + child2.parent.delete(child2) if child2.parent + @children[ind,0] = child2 + child2.parent = self + end + self + end + + # Inserts an child after another child + # @param child1 this is either an xpath or an Element. If an Element, + # child2 will be inserted after child1 in the child list of the parent. + # If an xpath, child2 will be inserted after the first child to match + # the xpath. + # @param child2 the child to insert + # @return the parent (self) + def insert_after( child1, child2 ) + if child1.kind_of? String + child1 = XPath.first( self, child1 ) + child1.parent.insert_after child1, child2 + else + ind = index(child1)+1 + child2.parent.delete(child2) if child2.parent + @children[ind,0] = child2 + child2.parent = self + end + self + end + + def to_a + @children.dup + end + + # Fetches the index of a given child + # @param child the child to get the index of + # @return the index of the child, or nil if the object is not a child + # of this parent. + def index( child ) + count = -1 + @children.find { |i| count += 1 ; i.hash == child.hash } + count + end + + # @return the number of children of this parent + def size + @children.size + end + + alias :length :size + + # Replaces one child with another, making sure the nodelist is correct + # @param to_replace the child to replace (must be a Child) + # @param replacement the child to insert into the nodelist (must be a + # Child) + def replace_child( to_replace, replacement ) + @children.map! {|c| c.equal?( to_replace ) ? replacement : c } + to_replace.parent = nil + replacement.parent = self + end + + # Deeply clones this object. This creates a complete duplicate of this + # Parent, including all descendants. + def deep_clone + cl = clone() + each do |child| + if child.kind_of? Parent + cl << child.deep_clone + else + cl << child.clone + end + end + cl + end + + alias :children :to_a + + def parent? + true + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parseexception.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parseexception.rb new file mode 100644 index 0000000000..feb7a7e638 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parseexception.rb @@ -0,0 +1,51 @@ +module REXML + class ParseException < RuntimeError + attr_accessor :source, :parser, :continued_exception + + def initialize( message, source=nil, parser=nil, exception=nil ) + super(message) + @source = source + @parser = parser + @continued_exception = exception + end + + def to_s + # Quote the original exception, if there was one + if @continued_exception + err = @continued_exception.inspect + err << "\n" + err << @continued_exception.backtrace.join("\n") + err << "\n...\n" + else + err = "" + end + + # Get the stack trace and error message + err << super + + # Add contextual information + if @source + err << "\nLine: #{line}\n" + err << "Position: #{position}\n" + err << "Last 80 unconsumed characters:\n" + err << @source.buffer[0..80].gsub(/\n/, ' ') + end + + err + end + + def position + @source.current_line[0] if @source and defined? @source.current_line and + @source.current_line + end + + def line + @source.current_line[2] if @source and defined? @source.current_line and + @source.current_line + end + + def context + @source.current_line + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/baseparser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/baseparser.rb new file mode 100644 index 0000000000..3782d61b2c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/baseparser.rb @@ -0,0 +1,463 @@ +require 'rexml/parseexception' +require 'rexml/source' + +module REXML + module Parsers + # = Using the Pull Parser + # This API is experimental, and subject to change. + # parser = PullParser.new( "texttxet" ) + # while parser.has_next? + # res = parser.next + # puts res[1]['att'] if res.start_tag? and res[0] == 'b' + # end + # See the PullEvent class for information on the content of the results. + # The data is identical to the arguments passed for the various events to + # the StreamListener API. + # + # Notice that: + # parser = PullParser.new( "BAD DOCUMENT" ) + # while parser.has_next? + # res = parser.next + # raise res[1] if res.error? + # end + # + # Nat Price gave me some good ideas for the API. + class BaseParser + NCNAME_STR= '[\w:][\-\w\d.]*' + NAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}" + + NAMECHAR = '[\-\w\d\.:]' + NAME = "([\\w:]#{NAMECHAR}*)" + NMTOKEN = "(?:#{NAMECHAR})+" + NMTOKENS = "#{NMTOKEN}(\\s+#{NMTOKEN})*" + REFERENCE = "(?:&#{NAME};|&#\\d+;|&#x[0-9a-fA-F]+;)" + REFERENCE_RE = /#{REFERENCE}/ + + DOCTYPE_START = /\A\s*)/um + ATTRIBUTE_PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\2/um + COMMENT_START = /\A/um + CDATA_START = /\A/um + CDATA_PATTERN = //um + XMLDECL_START = /\A<\?xml\s/u; + XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>/um + INSTRUCTION_START = /\A<\?/u + INSTRUCTION_PATTERN = /<\?(.*?)(\s+.*?)?\?>/um + TAG_MATCH = /^<((?>#{NAME_STR}))\s*((?>\s+#{NAME_STR}\s*=\s*(["']).*?\3)*)\s*(\/)?>/um + CLOSE_MATCH = /^\s*<\/(#{NAME_STR})\s*>/um + + VERSION = /\bversion\s*=\s*["'](.*?)['"]/um + ENCODING = /\bencoding\s*=\s*["'](.*?)['"]/um + STANDALONE = /\bstandalone\s*=\s["'](.*?)['"]/um + + ENTITY_START = /^\s*/um + SYSTEMENTITY = /^\s*(%.*?;)\s*$/um + ENUMERATION = "\\(\\s*#{NMTOKEN}(?:\\s*\\|\\s*#{NMTOKEN})*\\s*\\)" + NOTATIONTYPE = "NOTATION\\s+\\(\\s*#{NAME}(?:\\s*\\|\\s*#{NAME})*\\s*\\)" + ENUMERATEDTYPE = "(?:(?:#{NOTATIONTYPE})|(?:#{ENUMERATION}))" + ATTTYPE = "(CDATA|ID|IDREF|IDREFS|ENTITY|ENTITIES|NMTOKEN|NMTOKENS|#{ENUMERATEDTYPE})" + ATTVALUE = "(?:\"((?:[^<&\"]|#{REFERENCE})*)\")|(?:'((?:[^<&']|#{REFERENCE})*)')" + DEFAULTDECL = "(#REQUIRED|#IMPLIED|(?:(#FIXED\\s+)?#{ATTVALUE}))" + ATTDEF = "\\s+#{NAME}\\s+#{ATTTYPE}\\s+#{DEFAULTDECL}" + ATTDEF_RE = /#{ATTDEF}/ + ATTLISTDECL_START = /^\s*/um + NOTATIONDECL_START = /^\s*/um + SYSTEM = /^\s*/um + + TEXT_PATTERN = /\A([^<]*)/um + + # Entity constants + PUBIDCHAR = "\x20\x0D\x0Aa-zA-Z0-9\\-()+,./:=?;!*@$_%#" + SYSTEMLITERAL = %Q{((?:"[^"]*")|(?:'[^']*'))} + PUBIDLITERAL = %Q{("[#{PUBIDCHAR}']*"|'[#{PUBIDCHAR}]*')} + EXTERNALID = "(?:(?:(SYSTEM)\\s+#{SYSTEMLITERAL})|(?:(PUBLIC)\\s+#{PUBIDLITERAL}\\s+#{SYSTEMLITERAL}))" + NDATADECL = "\\s+NDATA\\s+#{NAME}" + PEREFERENCE = "%#{NAME};" + ENTITYVALUE = %Q{((?:"(?:[^%&"]|#{PEREFERENCE}|#{REFERENCE})*")|(?:'([^%&']|#{PEREFERENCE}|#{REFERENCE})*'))} + PEDEF = "(?:#{ENTITYVALUE}|#{EXTERNALID})" + ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))" + PEDECL = "" + GEDECL = "" + ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um + + EREFERENCE = /&(?!#{NAME};)/ + + DEFAULT_ENTITIES = { + 'gt' => [/>/, '>', '>', />/], + 'lt' => [/</, '<', '<', / [/"/, '"', '"', /"/], + "apos" => [/'/, "'", "'", /'/] + } + + + ###################################################################### + # These are patterns to identify common markup errors, to make the + # error messages more informative. + ###################################################################### + MISSING_ATTRIBUTE_QUOTES = /^<#{NAME_STR}\s+#{NAME_STR}\s*=\s*[^"']/um + + def initialize( source ) + self.stream = source + end + + def add_listener( listener ) + if !defined?(@listeners) or !@listeners + @listeners = [] + instance_eval <<-EOL + alias :_old_pull :pull + def pull + event = _old_pull + @listeners.each do |listener| + listener.receive event + end + event + end + EOL + end + @listeners << listener + end + + attr_reader :source + + def stream=( source ) + @source = SourceFactory.create_from( source ) + @closed = nil + @document_status = nil + @tags = [] + @stack = [] + @entities = [] + end + + def position + if @source.respond_to? :position + @source.position + else + # FIXME + 0 + end + end + + # Returns true if there are no more events + def empty? + return (@source.empty? and @stack.empty?) + end + + # Returns true if there are more events. Synonymous with !empty? + def has_next? + return !(@source.empty? and @stack.empty?) + end + + # Push an event back on the head of the stream. This method + # has (theoretically) infinite depth. + def unshift token + @stack.unshift(token) + end + + # Peek at the +depth+ event in the stack. The first element on the stack + # is at depth 0. If +depth+ is -1, will parse to the end of the input + # stream and return the last event, which is always :end_document. + # Be aware that this causes the stream to be parsed up to the +depth+ + # event, so you can effectively pre-parse the entire document (pull the + # entire thing into memory) using this method. + def peek depth=0 + raise %Q[Illegal argument "#{depth}"] if depth < -1 + temp = [] + if depth == -1 + temp.push(pull()) until empty? + else + while @stack.size+temp.size < depth+1 + temp.push(pull()) + end + end + @stack += temp if temp.size > 0 + @stack[depth] + end + + # Returns the next event. This is a +PullEvent+ object. + def pull + if @closed + x, @closed = @closed, nil + return [ :end_element, x ] + end + return [ :end_document ] if empty? + return @stack.shift if @stack.size > 0 + @source.read if @source.buffer.size<2 + #STDERR.puts "BUFFER = #{@source.buffer.inspect}" + if @document_status == nil + #@source.consume( /^\s*/um ) + word = @source.match( /^((?:\s+)|(?:<[^>]*>))/um ) + word = word[1] unless word.nil? + #STDERR.puts "WORD = #{word.inspect}" + case word + when COMMENT_START + return [ :comment, @source.match( COMMENT_PATTERN, true )[1] ] + when XMLDECL_START + #STDERR.puts "XMLDECL" + results = @source.match( XMLDECL_PATTERN, true )[1] + version = VERSION.match( results ) + version = version[1] unless version.nil? + encoding = ENCODING.match(results) + encoding = encoding[1] unless encoding.nil? + @source.encoding = encoding + standalone = STANDALONE.match(results) + standalone = standalone[1] unless standalone.nil? + return [ :xmldecl, version, encoding, standalone ] + when INSTRUCTION_START + return [ :processing_instruction, *@source.match(INSTRUCTION_PATTERN, true)[1,2] ] + when DOCTYPE_START + md = @source.match( DOCTYPE_PATTERN, true ) + identity = md[1] + close = md[2] + identity =~ IDENTITY + name = $1 + raise REXML::ParseException.new("DOCTYPE is missing a name") if name.nil? + pub_sys = $2.nil? ? nil : $2.strip + long_name = $4.nil? ? nil : $4.strip + uri = $6.nil? ? nil : $6.strip + args = [ :start_doctype, name, pub_sys, long_name, uri ] + if close == ">" + @document_status = :after_doctype + @source.read if @source.buffer.size<2 + md = @source.match(/^\s*/um, true) + @stack << [ :end_doctype ] + else + @document_status = :in_doctype + end + return args + when /^\s+/ + else + @document_status = :after_doctype + @source.read if @source.buffer.size<2 + md = @source.match(/\s*/um, true) + end + end + if @document_status == :in_doctype + md = @source.match(/\s*(.*?>)/um) + case md[1] + when SYSTEMENTITY + match = @source.match( SYSTEMENTITY, true )[1] + return [ :externalentity, match ] + + when ELEMENTDECL_START + return [ :elementdecl, @source.match( ELEMENTDECL_PATTERN, true )[1] ] + + when ENTITY_START + match = @source.match( ENTITYDECL, true ).to_a.compact + match[0] = :entitydecl + ref = false + if match[1] == '%' + ref = true + match.delete_at 1 + end + # Now we have to sort out what kind of entity reference this is + if match[2] == 'SYSTEM' + # External reference + match[3] = match[3][1..-2] # PUBID + match.delete_at(4) if match.size > 4 # Chop out NDATA decl + # match is [ :entity, name, SYSTEM, pubid(, ndata)? ] + elsif match[2] == 'PUBLIC' + # External reference + match[3] = match[3][1..-2] # PUBID + match[4] = match[4][1..-2] # HREF + # match is [ :entity, name, PUBLIC, pubid, href ] + else + match[2] = match[2][1..-2] + match.pop if match.size == 4 + # match is [ :entity, name, value ] + end + match << '%' if ref + return match + when ATTLISTDECL_START + md = @source.match( ATTLISTDECL_PATTERN, true ) + raise REXML::ParseException.new( "Bad ATTLIST declaration!", @source ) if md.nil? + element = md[1] + contents = md[0] + + pairs = {} + values = md[0].scan( ATTDEF_RE ) + values.each do |attdef| + unless attdef[3] == "#IMPLIED" + attdef.compact! + val = attdef[3] + val = attdef[4] if val == "#FIXED " + pairs[attdef[0]] = val + end + end + return [ :attlistdecl, element, pairs, contents ] + when NOTATIONDECL_START + md = nil + if @source.match( PUBLIC ) + md = @source.match( PUBLIC, true ) + vals = [md[1],md[2],md[4],md[6]] + elsif @source.match( SYSTEM ) + md = @source.match( SYSTEM, true ) + vals = [md[1],md[2],nil,md[4]] + else + raise REXML::ParseException.new( "error parsing notation: no matching pattern", @source ) + end + return [ :notationdecl, *vals ] + when CDATA_END + @document_status = :after_doctype + @source.match( CDATA_END, true ) + return [ :end_doctype ] + end + end + begin + if @source.buffer[0] == ?< + if @source.buffer[1] == ?/ + last_tag = @tags.pop + #md = @source.match_to_consume( '>', CLOSE_MATCH) + md = @source.match( CLOSE_MATCH, true ) + raise REXML::ParseException.new( "Missing end tag for "+ + "'#{last_tag}' (got \"#{md[1]}\")", + @source) unless last_tag == md[1] + return [ :end_element, last_tag ] + elsif @source.buffer[1] == ?! + md = @source.match(/\A(\s*[^>]*>)/um) + #STDERR.puts "SOURCE BUFFER = #{source.buffer}, #{source.buffer.size}" + raise REXML::ParseException.new("Malformed node", @source) unless md + if md[0][2] == ?- + md = @source.match( COMMENT_PATTERN, true ) + return [ :comment, md[1] ] if md + else + md = @source.match( CDATA_PATTERN, true ) + return [ :cdata, md[1] ] if md + end + raise REXML::ParseException.new( "Declarations can only occur "+ + "in the doctype declaration.", @source) + elsif @source.buffer[1] == ?? + md = @source.match( INSTRUCTION_PATTERN, true ) + return [ :processing_instruction, md[1], md[2] ] if md + raise REXML::ParseException.new( "Bad instruction declaration", + @source) + else + # Get the next tag + md = @source.match(TAG_MATCH, true) + unless md + # Check for missing attribute quotes + raise REXML::ParseException.new("missing attribute quote", @source) if @source.match(MISSING_ATTRIBUTE_QUOTES ) + raise REXML::ParseException.new("malformed XML: missing tag start", @source) + end + attrs = [] + if md[2].size > 0 + attrs = md[2].scan( ATTRIBUTE_PATTERN ) + raise REXML::ParseException.new( "error parsing attributes: [#{attrs.join ', '}], excess = \"#$'\"", @source) if $' and $'.strip.size > 0 + end + + if md[4] + @closed = md[1] + else + @tags.push( md[1] ) + end + attributes = {} + attrs.each { |a,b,c| attributes[a] = c } + return [ :start_element, md[1], attributes ] + end + else + md = @source.match( TEXT_PATTERN, true ) + if md[0].length == 0 + @source.match( /(\s+)/, true ) + end + #STDERR.puts "GOT #{md[1].inspect}" unless md[0].length == 0 + #return [ :text, "" ] if md[0].length == 0 + # unnormalized = Text::unnormalize( md[1], self ) + # return PullEvent.new( :text, md[1], unnormalized ) + return [ :text, md[1] ] + end + rescue REXML::ParseException + raise + rescue Exception, NameError => error + raise REXML::ParseException.new( "Exception parsing", + @source, self, (error ? error : $!) ) + end + return [ :dummy ] + end + + def entity( reference, entities ) + value = nil + value = entities[ reference ] if entities + if not value + value = DEFAULT_ENTITIES[ reference ] + value = value[2] if value + end + unnormalize( value, entities ) if value + end + + # Escapes all possible entities + def normalize( input, entities=nil, entity_filter=nil ) + copy = input.clone + # Doing it like this rather than in a loop improves the speed + copy.gsub!( EREFERENCE, '&' ) + entities.each do |key, value| + copy.gsub!( value, "&#{key};" ) unless entity_filter and + entity_filter.include?(entity) + end if entities + copy.gsub!( EREFERENCE, '&' ) + DEFAULT_ENTITIES.each do |key, value| + copy.gsub!( value[3], value[1] ) + end + copy + end + + # Unescapes all possible entities + def unnormalize( string, entities=nil, filter=nil ) + rv = string.clone + rv.gsub!( /\r\n?/, "\n" ) + matches = rv.scan( REFERENCE_RE ) + return rv if matches.size == 0 + rv.gsub!( /�*((?:\d+)|(?:x[a-fA-F0-9]+));/ ) {|m| + m=$1 + m = "0#{m}" if m[0] == ?x + [Integer(m)].pack('U*') + } + matches.collect!{|x|x[0]}.compact! + if matches.size > 0 + matches.each do |entity_reference| + unless filter and filter.include?(entity_reference) + entity_value = entity( entity_reference, entities ) + if entity_value + re = /&#{entity_reference};/ + rv.gsub!( re, entity_value ) + end + end + end + matches.each do |entity_reference| + unless filter and filter.include?(entity_reference) + er = DEFAULT_ENTITIES[entity_reference] + rv.gsub!( er[0], er[2] ) if er + end + end + rv.gsub!( /&/, '&' ) + end + rv + end + end + end +end + +=begin + case event[0] + when :start_element + when :text + when :end_element + when :processing_instruction + when :cdata + when :comment + when :xmldecl + when :start_doctype + when :end_doctype + when :externalentity + when :elementdecl + when :entity + when :attlistdecl + when :notationdecl + when :end_doctype + end +=end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/lightparser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/lightparser.rb new file mode 100644 index 0000000000..0f35034993 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/lightparser.rb @@ -0,0 +1,60 @@ +require 'rexml/parsers/streamparser' +require 'rexml/parsers/baseparser' +require 'rexml/light/node' + +module REXML + module Parsers + class LightParser + def initialize stream + @stream = stream + @parser = REXML::Parsers::BaseParser.new( stream ) + end + + def add_listener( listener ) + @parser.add_listener( listener ) + end + + def rewind + @stream.rewind + @parser.stream = @stream + end + + def parse + root = context = [ :document ] + while true + event = @parser.pull + case event[0] + when :end_document + break + when :end_doctype + context = context[1] + when :start_element, :start_doctype + new_node = event + context << new_node + new_node[1,0] = [context] + context = new_node + when :end_element, :end_doctype + context = context[1] + else + new_node = event + context << new_node + new_node[1,0] = [context] + end + end + root + end + end + + # An element is an array. The array contains: + # 0 The parent element + # 1 The tag name + # 2 A hash of attributes + # 3..-1 The child elements + # An element is an array of size > 3 + # Text is a String + # PIs are [ :processing_instruction, target, data ] + # Comments are [ :comment, data ] + # DocTypes are DocType structs + # The root is an array with XMLDecls, Text, DocType, Array, Text + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/pullparser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/pullparser.rb new file mode 100644 index 0000000000..36dc7160c3 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/pullparser.rb @@ -0,0 +1,196 @@ +require 'forwardable' + +require 'rexml/parseexception' +require 'rexml/parsers/baseparser' +require 'rexml/xmltokens' + +module REXML + module Parsers + # = Using the Pull Parser + # This API is experimental, and subject to change. + # parser = PullParser.new( "texttxet" ) + # while parser.has_next? + # res = parser.next + # puts res[1]['att'] if res.start_tag? and res[0] == 'b' + # end + # See the PullEvent class for information on the content of the results. + # The data is identical to the arguments passed for the various events to + # the StreamListener API. + # + # Notice that: + # parser = PullParser.new( "BAD DOCUMENT" ) + # while parser.has_next? + # res = parser.next + # raise res[1] if res.error? + # end + # + # Nat Price gave me some good ideas for the API. + class PullParser + include XMLTokens + extend Forwardable + + def_delegators( :@parser, :has_next? ) + def_delegators( :@parser, :entity ) + def_delegators( :@parser, :empty? ) + def_delegators( :@parser, :source ) + + def initialize stream + @entities = {} + @listeners = nil + @parser = BaseParser.new( stream ) + @my_stack = [] + end + + def add_listener( listener ) + @listeners = [] unless @listeners + @listeners << listener + end + + def each + while has_next? + yield self.pull + end + end + + def peek depth=0 + if @my_stack.length <= depth + (depth - @my_stack.length + 1).times { + e = PullEvent.new(@parser.pull) + @my_stack.push(e) + } + end + @my_stack[depth] + end + + def pull + return @my_stack.shift if @my_stack.length > 0 + + event = @parser.pull + case event[0] + when :entitydecl + @entities[ event[1] ] = + event[2] unless event[2] =~ /PUBLIC|SYSTEM/ + when :text + unnormalized = @parser.unnormalize( event[1], @entities ) + event << unnormalized + end + PullEvent.new( event ) + end + + def unshift token + @my_stack.unshift token + end + end + + # A parsing event. The contents of the event are accessed as an +Array?, + # and the type is given either by the ...? methods, or by accessing the + # +type+ accessor. The contents of this object vary from event to event, + # but are identical to the arguments passed to +StreamListener+s for each + # event. + class PullEvent + # The type of this event. Will be one of :tag_start, :tag_end, :text, + # :processing_instruction, :comment, :doctype, :attlistdecl, :entitydecl, + # :notationdecl, :entity, :cdata, :xmldecl, or :error. + def initialize(arg) + @contents = arg + end + + def []( start, endd=nil) + if start.kind_of? Range + @contents.slice( start.begin+1 .. start.end ) + elsif start.kind_of? Numeric + if endd.nil? + @contents.slice( start+1 ) + else + @contents.slice( start+1, endd ) + end + else + raise "Illegal argument #{start.inspect} (#{start.class})" + end + end + + def event_type + @contents[0] + end + + # Content: [ String tag_name, Hash attributes ] + def start_element? + @contents[0] == :start_element + end + + # Content: [ String tag_name ] + def end_element? + @contents[0] == :end_element + end + + # Content: [ String raw_text, String unnormalized_text ] + def text? + @contents[0] == :text + end + + # Content: [ String text ] + def instruction? + @contents[0] == :processing_instruction + end + + # Content: [ String text ] + def comment? + @contents[0] == :comment + end + + # Content: [ String name, String pub_sys, String long_name, String uri ] + def doctype? + @contents[0] == :start_doctype + end + + # Content: [ String text ] + def attlistdecl? + @contents[0] == :attlistdecl + end + + # Content: [ String text ] + def elementdecl? + @contents[0] == :elementdecl + end + + # Due to the wonders of DTDs, an entity declaration can be just about + # anything. There's no way to normalize it; you'll have to interpret the + # content yourself. However, the following is true: + # + # * If the entity declaration is an internal entity: + # [ String name, String value ] + # Content: [ String text ] + def entitydecl? + @contents[0] == :entitydecl + end + + # Content: [ String text ] + def notationdecl? + @contents[0] == :notationdecl + end + + # Content: [ String text ] + def entity? + @contents[0] == :entity + end + + # Content: [ String text ] + def cdata? + @contents[0] == :cdata + end + + # Content: [ String version, String encoding, String standalone ] + def xmldecl? + @contents[0] == :xmldecl + end + + def error? + @contents[0] == :error + end + + def inspect + @contents[0].to_s + ": " + @contents[1..-1].inspect + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/sax2parser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/sax2parser.rb new file mode 100644 index 0000000000..e402eb7747 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/sax2parser.rb @@ -0,0 +1,238 @@ +require 'rexml/parsers/baseparser' +require 'rexml/parseexception' +require 'rexml/namespace' +require 'rexml/text' + +module REXML + module Parsers + # SAX2Parser + class SAX2Parser + def initialize source + @parser = BaseParser.new(source) + @listeners = [] + @procs = [] + @namespace_stack = [] + @has_listeners = false + @tag_stack = [] + @entities = {} + end + + def source + @parser.source + end + + def add_listener( listener ) + @parser.add_listener( listener ) + end + + # Listen arguments: + # + # Symbol, Array, Block + # Listen to Symbol events on Array elements + # Symbol, Block + # Listen to Symbol events + # Array, Listener + # Listen to all events on Array elements + # Array, Block + # Listen to :start_element events on Array elements + # Listener + # Listen to All events + # + # Symbol can be one of: :start_element, :end_element, + # :start_prefix_mapping, :end_prefix_mapping, :characters, + # :processing_instruction, :doctype, :attlistdecl, :elementdecl, + # :entitydecl, :notationdecl, :cdata, :xmldecl, :comment + # + # There is an additional symbol that can be listened for: :progress. + # This will be called for every event generated, passing in the current + # stream position. + # + # Array contains regular expressions or strings which will be matched + # against fully qualified element names. + # + # Listener must implement the methods in SAX2Listener + # + # Block will be passed the same arguments as a SAX2Listener method would + # be, where the method name is the same as the matched Symbol. + # See the SAX2Listener for more information. + def listen( *args, &blok ) + if args[0].kind_of? Symbol + if args.size == 2 + args[1].each { |match| @procs << [args[0], match, blok] } + else + add( [args[0], nil, blok] ) + end + elsif args[0].kind_of? Array + if args.size == 2 + args[0].each { |match| add( [nil, match, args[1]] ) } + else + args[0].each { |match| add( [ :start_element, match, blok ] ) } + end + else + add([nil, nil, args[0]]) + end + end + + def deafen( listener=nil, &blok ) + if listener + @listeners.delete_if {|item| item[-1] == listener } + @has_listeners = false if @listeners.size == 0 + else + @procs.delete_if {|item| item[-1] == blok } + end + end + + def parse + @procs.each { |sym,match,block| block.call if sym == :start_document } + @listeners.each { |sym,match,block| + block.start_document if sym == :start_document or sym.nil? + } + root = context = [] + while true + event = @parser.pull + case event[0] + when :end_document + handle( :end_document ) + break + when :start_doctype + handle( :doctype, *event[1..-1]) + when :end_doctype + context = context[1] + when :start_element + @tag_stack.push(event[1]) + # find the observers for namespaces + procs = get_procs( :start_prefix_mapping, event[1] ) + listeners = get_listeners( :start_prefix_mapping, event[1] ) + if procs or listeners + # break out the namespace declarations + # The attributes live in event[2] + event[2].each {|n, v| event[2][n] = @parser.normalize(v)} + nsdecl = event[2].find_all { |n, value| n =~ /^xmlns(:|$)/ } + nsdecl.collect! { |n, value| [ n[6..-1], value ] } + @namespace_stack.push({}) + nsdecl.each do |n,v| + @namespace_stack[-1][n] = v + # notify observers of namespaces + procs.each { |ob| ob.call( n, v ) } if procs + listeners.each { |ob| ob.start_prefix_mapping(n, v) } if listeners + end + end + event[1] =~ Namespace::NAMESPLIT + prefix = $1 + local = $2 + uri = get_namespace(prefix) + # find the observers for start_element + procs = get_procs( :start_element, event[1] ) + listeners = get_listeners( :start_element, event[1] ) + # notify observers + procs.each { |ob| ob.call( uri, local, event[1], event[2] ) } if procs + listeners.each { |ob| + ob.start_element( uri, local, event[1], event[2] ) + } if listeners + when :end_element + @tag_stack.pop + event[1] =~ Namespace::NAMESPLIT + prefix = $1 + local = $2 + uri = get_namespace(prefix) + # find the observers for start_element + procs = get_procs( :end_element, event[1] ) + listeners = get_listeners( :end_element, event[1] ) + # notify observers + procs.each { |ob| ob.call( uri, local, event[1] ) } if procs + listeners.each { |ob| + ob.end_element( uri, local, event[1] ) + } if listeners + + namespace_mapping = @namespace_stack.pop + # find the observers for namespaces + procs = get_procs( :end_prefix_mapping, event[1] ) + listeners = get_listeners( :end_prefix_mapping, event[1] ) + if procs or listeners + namespace_mapping.each do |prefix, uri| + # notify observers of namespaces + procs.each { |ob| ob.call( prefix ) } if procs + listeners.each { |ob| ob.end_prefix_mapping(prefix) } if listeners + end + end + when :text + #normalized = @parser.normalize( event[1] ) + #handle( :characters, normalized ) + copy = event[1].clone + @entities.each { |key, value| copy = copy.gsub("&#{key};", value) } + copy.gsub!( Text::NUMERICENTITY ) {|m| + m=$1 + m = "0#{m}" if m[0] == ?x + [Integer(m)].pack('U*') + } + handle( :characters, copy ) + when :entitydecl + @entities[ event[1] ] = event[2] if event.size == 3 + handle( *event ) + when :processing_instruction, :comment, :attlistdecl, + :elementdecl, :cdata, :notationdecl, :xmldecl + handle( *event ) + end + handle( :progress, @parser.position ) + end + end + + private + def handle( symbol, *arguments ) + tag = @tag_stack[-1] + procs = get_procs( symbol, tag ) + listeners = get_listeners( symbol, tag ) + # notify observers + procs.each { |ob| ob.call( *arguments ) } if procs + listeners.each { |l| + l.send( symbol.to_s, *arguments ) + } if listeners + end + + # The following methods are duplicates, but it is faster than using + # a helper + def get_procs( symbol, name ) + return nil if @procs.size == 0 + @procs.find_all do |sym, match, block| + #puts sym.inspect+"=="+symbol.inspect+ "\t"+match.inspect+"=="+name.inspect+ "\t"+( (sym.nil? or symbol == sym) and ((name.nil? and match.nil?) or match.nil? or ( (name == match) or (match.kind_of? Regexp and name =~ match)))).to_s + ( + (sym.nil? or symbol == sym) and + ((name.nil? and match.nil?) or match.nil? or ( + (name == match) or + (match.kind_of? Regexp and name =~ match) + ) + ) + ) + end.collect{|x| x[-1]} + end + def get_listeners( symbol, name ) + return nil if @listeners.size == 0 + @listeners.find_all do |sym, match, block| + ( + (sym.nil? or symbol == sym) and + ((name.nil? and match.nil?) or match.nil? or ( + (name == match) or + (match.kind_of? Regexp and name =~ match) + ) + ) + ) + end.collect{|x| x[-1]} + end + + def add( pair ) + if pair[-1].respond_to? :call + @procs << pair unless @procs.include? pair + else + @listeners << pair unless @listeners.include? pair + @has_listeners = true + end + end + + def get_namespace( prefix ) + uris = (@namespace_stack.find_all { |ns| not ns[prefix].nil? }) || + (@namespace_stack.find { |ns| not ns[nil].nil? }) + uris[-1][prefix] unless uris.nil? or 0 == uris.size + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/streamparser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/streamparser.rb new file mode 100644 index 0000000000..256d0f611c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/streamparser.rb @@ -0,0 +1,46 @@ +module REXML + module Parsers + class StreamParser + def initialize source, listener + @listener = listener + @parser = BaseParser.new( source ) + end + + def add_listener( listener ) + @parser.add_listener( listener ) + end + + def parse + # entity string + while true + event = @parser.pull + case event[0] + when :end_document + return + when :start_element + attrs = event[2].each do |n, v| + event[2][n] = @parser.unnormalize( v ) + end + @listener.tag_start( event[1], attrs ) + when :end_element + @listener.tag_end( event[1] ) + when :text + normalized = @parser.unnormalize( event[1] ) + @listener.text( normalized ) + when :processing_instruction + @listener.instruction( *event[1,2] ) + when :start_doctype + @listener.doctype( *event[1..-1] ) + when :end_doctype + # FIXME: remove this condition for milestone:3.2 + @listener.doctype_end if @listener.respond_to? :doctype_end + when :comment, :attlistdecl, :cdata, :xmldecl, :elementdecl + @listener.send( event[0].to_s, *event[1..-1] ) + when :entitydecl, :notationdecl + @listener.send( event[0].to_s, event[1..-1] ) + end + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/treeparser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/treeparser.rb new file mode 100644 index 0000000000..a53fa41925 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/treeparser.rb @@ -0,0 +1,95 @@ +require 'rexml/validation/validationexception' + +module REXML + module Parsers + class TreeParser + def initialize( source, build_context = Document.new ) + @build_context = build_context + @parser = Parsers::BaseParser.new( source ) + end + + def add_listener( listener ) + @parser.add_listener( listener ) + end + + def parse + tag_stack = [] + in_doctype = false + entities = nil + begin + while true + event = @parser.pull + #STDERR.puts "TREEPARSER GOT #{event.inspect}" + case event[0] + when :end_document + unless tag_stack.empty? + #raise ParseException.new("No close tag for #{tag_stack.inspect}") + raise ParseException.new("No close tag for #{@build_context.xpath}") + end + return + when :start_element + tag_stack.push(event[1]) + # find the observers for namespaces + @build_context = @build_context.add_element( event[1], event[2] ) + when :end_element + tag_stack.pop + @build_context = @build_context.parent + when :text + if not in_doctype + if @build_context[-1].instance_of? Text + @build_context[-1] << event[1] + else + @build_context.add( + Text.new(event[1], @build_context.whitespace, nil, true) + ) unless ( + @build_context.ignore_whitespace_nodes and + event[1].strip.size==0 + ) + end + end + when :comment + c = Comment.new( event[1] ) + @build_context.add( c ) + when :cdata + c = CData.new( event[1] ) + @build_context.add( c ) + when :processing_instruction + @build_context.add( Instruction.new( event[1], event[2] ) ) + when :end_doctype + in_doctype = false + entities.each { |k,v| entities[k] = @build_context.entities[k].value } + @build_context = @build_context.parent + when :start_doctype + doctype = DocType.new( event[1..-1], @build_context ) + @build_context = doctype + entities = {} + in_doctype = true + when :attlistdecl + n = AttlistDecl.new( event[1..-1] ) + @build_context.add( n ) + when :externalentity + n = ExternalEntity.new( event[1] ) + @build_context.add( n ) + when :elementdecl + n = ElementDecl.new( event[1] ) + @build_context.add(n) + when :entitydecl + entities[ event[1] ] = event[2] unless event[2] =~ /PUBLIC|SYSTEM/ + @build_context.add(Entity.new(event)) + when :notationdecl + n = NotationDecl.new( *event[1..-1] ) + @build_context.add( n ) + when :xmldecl + x = XMLDecl.new( event[1], event[2], event[3] ) + @build_context.add( x ) + end + end + rescue REXML::Validation::ValidationException + raise + rescue + raise ParseException.new( $!.message, @parser.source, @parser, $! ) + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/ultralightparser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/ultralightparser.rb new file mode 100644 index 0000000000..adc4af18e2 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/ultralightparser.rb @@ -0,0 +1,56 @@ +require 'rexml/parsers/streamparser' +require 'rexml/parsers/baseparser' + +module REXML + module Parsers + class UltraLightParser + def initialize stream + @stream = stream + @parser = REXML::Parsers::BaseParser.new( stream ) + end + + def add_listener( listener ) + @parser.add_listener( listener ) + end + + def rewind + @stream.rewind + @parser.stream = @stream + end + + def parse + root = context = [] + while true + event = @parser.pull + case event[0] + when :end_document + break + when :end_doctype + context = context[1] + when :start_element, :doctype + context << event + event[1,0] = [context] + context = event + when :end_element + context = context[1] + else + context << event + end + end + root + end + end + + # An element is an array. The array contains: + # 0 The parent element + # 1 The tag name + # 2 A hash of attributes + # 3..-1 The child elements + # An element is an array of size > 3 + # Text is a String + # PIs are [ :processing_instruction, target, data ] + # Comments are [ :comment, data ] + # DocTypes are DocType structs + # The root is an array with XMLDecls, Text, DocType, Array, Text + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/xpathparser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/xpathparser.rb new file mode 100644 index 0000000000..de2530e347 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/parsers/xpathparser.rb @@ -0,0 +1,698 @@ +require 'rexml/namespace' +require 'rexml/xmltokens' + +module REXML + module Parsers + # You don't want to use this class. Really. Use XPath, which is a wrapper + # for this class. Believe me. You don't want to poke around in here. + # There is strange, dark magic at work in this code. Beware. Go back! Go + # back while you still can! + class XPathParser + include XMLTokens + LITERAL = /^'([^']*)'|^"([^"]*)"/u + + def namespaces=( namespaces ) + Functions::namespace_context = namespaces + @namespaces = namespaces + end + + def parse path + path.gsub!(/([\(\[])\s+/, '\1') # Strip ignorable spaces + path.gsub!( /\s+([\]\)])/, '\1' ) + parsed = [] + path = OrExpr(path, parsed) + parsed + end + + def predicate path + parsed = [] + Predicate( "[#{path}]", parsed ) + parsed + end + + def abbreviate( path ) + path = path.kind_of?(String) ? parse( path ) : path + string = "" + document = false + while path.size > 0 + op = path.shift + case op + when :node + when :attribute + string << "/" if string.size > 0 + string << "@" + when :child + string << "/" if string.size > 0 + when :descendant_or_self + string << "/" + when :self + string << "." + when :parent + string << ".." + when :any + string << "*" + when :text + string << "text()" + when :following, :following_sibling, + :ancestor, :ancestor_or_self, :descendant, + :namespace, :preceding, :preceding_sibling + string << "/" unless string.size == 0 + string << op.to_s.tr("_", "-") + string << "::" + when :qname + prefix = path.shift + name = path.shift + string << prefix+":" if prefix.size > 0 + string << name + when :predicate + string << '[' + string << predicate_to_string( path.shift ) {|x| abbreviate( x ) } + string << ']' + when :document + document = true + when :function + string << path.shift + string << "( " + string << predicate_to_string( path.shift[0] ) {|x| abbreviate( x )} + string << " )" + when :literal + string << %Q{ "#{path.shift}" } + else + string << "/" unless string.size == 0 + string << "UNKNOWN(" + string << op.inspect + string << ")" + end + end + string = "/"+string if document + return string + end + + def expand( path ) + path = path.kind_of?(String) ? parse( path ) : path + string = "" + document = false + while path.size > 0 + op = path.shift + case op + when :node + string << "node()" + when :attribute, :child, :following, :following_sibling, + :ancestor, :ancestor_or_self, :descendant, :descendant_or_self, + :namespace, :preceding, :preceding_sibling, :self, :parent + string << "/" unless string.size == 0 + string << op.to_s.tr("_", "-") + string << "::" + when :any + string << "*" + when :qname + prefix = path.shift + name = path.shift + string << prefix+":" if prefix.size > 0 + string << name + when :predicate + string << '[' + string << predicate_to_string( path.shift ) { |x| expand(x) } + string << ']' + when :document + document = true + else + string << "/" unless string.size == 0 + string << "UNKNOWN(" + string << op.inspect + string << ")" + end + end + string = "/"+string if document + return string + end + + def predicate_to_string( path, &block ) + string = "" + case path[0] + when :and, :or, :mult, :plus, :minus, :neq, :eq, :lt, :gt, :lteq, :gteq, :div, :mod, :union + op = path.shift + case op + when :eq + op = "=" + when :lt + op = "<" + when :gt + op = ">" + when :lteq + op = "<=" + when :gteq + op = ">=" + when :neq + op = "!=" + when :union + op = "|" + end + left = predicate_to_string( path.shift, &block ) + right = predicate_to_string( path.shift, &block ) + string << " " + string << left + string << " " + string << op.to_s + string << " " + string << right + string << " " + when :function + path.shift + name = path.shift + string << name + string << "( " + string << predicate_to_string( path.shift, &block ) + string << " )" + when :literal + path.shift + string << " " + string << path.shift.inspect + string << " " + else + string << " " + string << yield( path ) + string << " " + end + return string.squeeze(" ") + end + + private + #LocationPath + # | RelativeLocationPath + # | '/' RelativeLocationPath? + # | '//' RelativeLocationPath + def LocationPath path, parsed + #puts "LocationPath '#{path}'" + path = path.strip + if path[0] == ?/ + parsed << :document + if path[1] == ?/ + parsed << :descendant_or_self + parsed << :node + path = path[2..-1] + else + path = path[1..-1] + end + end + #puts parsed.inspect + return RelativeLocationPath( path, parsed ) if path.size > 0 + end + + #RelativeLocationPath + # | Step + # | (AXIS_NAME '::' | '@' | '') AxisSpecifier + # NodeTest + # Predicate + # | '.' | '..' AbbreviatedStep + # | RelativeLocationPath '/' Step + # | RelativeLocationPath '//' Step + AXIS = /^(ancestor|ancestor-or-self|attribute|child|descendant|descendant-or-self|following|following-sibling|namespace|parent|preceding|preceding-sibling|self)::/ + def RelativeLocationPath path, parsed + #puts "RelativeLocationPath #{path}" + while path.size > 0 + # (axis or @ or ) nodetest predicate > + # OR > / Step + # (. or ..) > + if path[0] == ?. + if path[1] == ?. + parsed << :parent + parsed << :node + path = path[2..-1] + else + parsed << :self + parsed << :node + path = path[1..-1] + end + else + if path[0] == ?@ + #puts "ATTRIBUTE" + parsed << :attribute + path = path[1..-1] + # Goto Nodetest + elsif path =~ AXIS + parsed << $1.tr('-','_').intern + path = $' + # Goto Nodetest + else + parsed << :child + end + + #puts "NODETESTING '#{path}'" + n = [] + path = NodeTest( path, n) + #puts "NODETEST RETURNED '#{path}'" + + if path[0] == ?[ + path = Predicate( path, n ) + end + + parsed.concat(n) + end + + if path.size > 0 + if path[0] == ?/ + if path[1] == ?/ + parsed << :descendant_or_self + parsed << :node + path = path[2..-1] + else + path = path[1..-1] + end + else + return path + end + end + end + return path + end + + # Returns a 1-1 map of the nodeset + # The contents of the resulting array are either: + # true/false, if a positive match + # String, if a name match + #NodeTest + # | ('*' | NCNAME ':' '*' | QNAME) NameTest + # | NODE_TYPE '(' ')' NodeType + # | PI '(' LITERAL ')' PI + # | '[' expr ']' Predicate + NCNAMETEST= /^(#{NCNAME_STR}):\*/u + QNAME = Namespace::NAMESPLIT + NODE_TYPE = /^(comment|text|node)\(\s*\)/m + PI = /^processing-instruction\(/ + def NodeTest path, parsed + #puts "NodeTest with #{path}" + res = nil + case path + when /^\*/ + path = $' + parsed << :any + when NODE_TYPE + type = $1 + path = $' + parsed << type.tr('-', '_').intern + when PI + path = $' + literal = nil + if path !~ /^\s*\)/ + path =~ LITERAL + literal = $1 + path = $' + raise ParseException.new("Missing ')' after processing instruction") if path[0] != ?) + path = path[1..-1] + end + parsed << :processing_instruction + parsed << (literal || '') + when NCNAMETEST + #puts "NCNAMETEST" + prefix = $1 + path = $' + parsed << :namespace + parsed << prefix + when QNAME + #puts "QNAME" + prefix = $1 + name = $2 + path = $' + prefix = "" unless prefix + parsed << :qname + parsed << prefix + parsed << name + end + return path + end + + # Filters the supplied nodeset on the predicate(s) + def Predicate path, parsed + #puts "PREDICATE with #{path}" + return nil unless path[0] == ?[ + predicates = [] + while path[0] == ?[ + path, expr = get_group(path) + predicates << expr[1..-2] if expr + end + #puts "PREDICATES = #{predicates.inspect}" + predicates.each{ |expr| + #puts "ORING #{expr}" + preds = [] + parsed << :predicate + parsed << preds + OrExpr(expr, preds) + } + #puts "PREDICATES = #{predicates.inspect}" + path + end + + # The following return arrays of true/false, a 1-1 mapping of the + # supplied nodeset, except for axe(), which returns a filtered + # nodeset + + #| OrExpr S 'or' S AndExpr + #| AndExpr + def OrExpr path, parsed + #puts "OR >>> #{path}" + n = [] + rest = AndExpr( path, n ) + #puts "OR <<< #{rest}" + if rest != path + while rest =~ /^\s*( or )/ + n = [ :or, n, [] ] + rest = AndExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace(n) + elsif n.size > 0 + parsed << n + end + rest + end + + #| AndExpr S 'and' S EqualityExpr + #| EqualityExpr + def AndExpr path, parsed + #puts "AND >>> #{path}" + n = [] + rest = EqualityExpr( path, n ) + #puts "AND <<< #{rest}" + if rest != path + while rest =~ /^\s*( and )/ + n = [ :and, n, [] ] + #puts "AND >>> #{rest}" + rest = EqualityExpr( $', n[-1] ) + #puts "AND <<< #{rest}" + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace(n) + elsif n.size > 0 + parsed << n + end + rest + end + + #| EqualityExpr ('=' | '!=') RelationalExpr + #| RelationalExpr + def EqualityExpr path, parsed + #puts "EQUALITY >>> #{path}" + n = [] + rest = RelationalExpr( path, n ) + #puts "EQUALITY <<< #{rest}" + if rest != path + while rest =~ /^\s*(!?=)\s*/ + if $1[0] == ?! + n = [ :neq, n, [] ] + else + n = [ :eq, n, [] ] + end + rest = RelationalExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace(n) + elsif n.size > 0 + parsed << n + end + rest + end + + #| RelationalExpr ('<' | '>' | '<=' | '>=') AdditiveExpr + #| AdditiveExpr + def RelationalExpr path, parsed + #puts "RELATION >>> #{path}" + n = [] + rest = AdditiveExpr( path, n ) + #puts "RELATION <<< #{rest}" + if rest != path + while rest =~ /^\s*([<>]=?)\s*/ + if $1[0] == ?< + sym = "lt" + else + sym = "gt" + end + sym << "eq" if $1[-1] == ?= + n = [ sym.intern, n, [] ] + rest = AdditiveExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace(n) + elsif n.size > 0 + parsed << n + end + rest + end + + #| AdditiveExpr ('+' | S '-') MultiplicativeExpr + #| MultiplicativeExpr + def AdditiveExpr path, parsed + #puts "ADDITIVE >>> #{path}" + n = [] + rest = MultiplicativeExpr( path, n ) + #puts "ADDITIVE <<< #{rest}" + if rest != path + while rest =~ /^\s*(\+| -)\s*/ + if $1[0] == ?+ + n = [ :plus, n, [] ] + else + n = [ :minus, n, [] ] + end + rest = MultiplicativeExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace(n) + elsif n.size > 0 + parsed << n + end + rest + end + + #| MultiplicativeExpr ('*' | S ('div' | 'mod') S) UnaryExpr + #| UnaryExpr + def MultiplicativeExpr path, parsed + #puts "MULT >>> #{path}" + n = [] + rest = UnaryExpr( path, n ) + #puts "MULT <<< #{rest}" + if rest != path + while rest =~ /^\s*(\*| div | mod )\s*/ + if $1[0] == ?* + n = [ :mult, n, [] ] + elsif $1.include?( "div" ) + n = [ :div, n, [] ] + else + n = [ :mod, n, [] ] + end + rest = UnaryExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace(n) + elsif n.size > 0 + parsed << n + end + rest + end + + #| '-' UnaryExpr + #| UnionExpr + def UnaryExpr path, parsed + path =~ /^(\-*)/ + path = $' + if $1 and (($1.size % 2) != 0) + mult = -1 + else + mult = 1 + end + parsed << :neg if mult < 0 + + #puts "UNARY >>> #{path}" + n = [] + path = UnionExpr( path, n ) + #puts "UNARY <<< #{path}" + parsed.concat( n ) + path + end + + #| UnionExpr '|' PathExpr + #| PathExpr + def UnionExpr path, parsed + #puts "UNION >>> #{path}" + n = [] + rest = PathExpr( path, n ) + #puts "UNION <<< #{rest}" + if rest != path + while rest =~ /^\s*(\|)\s*/ + n = [ :union, n, [] ] + rest = PathExpr( $', n[-1] ) + end + end + if parsed.size == 0 and n.size != 0 + parsed.replace( n ) + elsif n.size > 0 + parsed << n + end + rest + end + + #| LocationPath + #| FilterExpr ('/' | '//') RelativeLocationPath + def PathExpr path, parsed + path =~ /^\s*/ + path = $' + #puts "PATH >>> #{path}" + n = [] + rest = FilterExpr( path, n ) + #puts "PATH <<< '#{rest}'" + if rest != path + if rest and rest[0] == ?/ + return RelativeLocationPath(rest, n) + end + end + #puts "BEFORE WITH '#{rest}'" + rest = LocationPath(rest, n) if rest =~ /\A[\/\.\@\[\w_*]/ + parsed.concat(n) + return rest + end + + #| FilterExpr Predicate + #| PrimaryExpr + def FilterExpr path, parsed + #puts "FILTER >>> #{path}" + n = [] + path = PrimaryExpr( path, n ) + #puts "FILTER <<< #{path}" + path = Predicate(path, n) if path and path[0] == ?[ + #puts "FILTER <<< #{path}" + parsed.concat(n) + path + end + + #| VARIABLE_REFERENCE + #| '(' expr ')' + #| LITERAL + #| NUMBER + #| FunctionCall + VARIABLE_REFERENCE = /^\$(#{NAME_STR})/u + NUMBER = /^(\d*\.?\d+)/ + NT = /^comment|text|processing-instruction|node$/ + def PrimaryExpr path, parsed + arry = [] + case path + when VARIABLE_REFERENCE + varname = $1 + path = $' + parsed << :variable + parsed << varname + #arry << @variables[ varname ] + when /^(\w[-\w]*)(?:\()/ + #puts "PrimaryExpr :: Function >>> #$1 -- '#$''" + fname = $1 + tmp = $' + #puts "#{fname} =~ #{NT.inspect}" + return path if fname =~ NT + path = tmp + parsed << :function + parsed << fname + path = FunctionCall(path, parsed) + when NUMBER + #puts "LITERAL or NUMBER: #$1" + varname = $1.nil? ? $2 : $1 + path = $' + parsed << :literal + parsed << (varname.include?('.') ? varname.to_f : varname.to_i) + when LITERAL + #puts "LITERAL or NUMBER: #$1" + varname = $1.nil? ? $2 : $1 + path = $' + parsed << :literal + parsed << varname + when /^\(/ #/ + path, contents = get_group(path) + contents = contents[1..-2] + n = [] + OrExpr( contents, n ) + parsed.concat(n) + end + path + end + + #| FUNCTION_NAME '(' ( expr ( ',' expr )* )? ')' + def FunctionCall rest, parsed + path, arguments = parse_args(rest) + argset = [] + for argument in arguments + args = [] + OrExpr( argument, args ) + argset << args + end + parsed << argset + path + end + + # get_group( '[foo]bar' ) -> ['bar', '[foo]'] + def get_group string + ind = 0 + depth = 0 + st = string[0,1] + en = (st == "(" ? ")" : "]") + begin + case string[ind,1] + when st + depth += 1 + when en + depth -= 1 + end + ind += 1 + end while depth > 0 and ind < string.length + return nil unless depth==0 + [string[ind..-1], string[0..ind-1]] + end + + def parse_args( string ) + arguments = [] + ind = 0 + inquot = false + inapos = false + depth = 1 + begin + case string[ind] + when ?" + inquot = !inquot unless inapos + when ?' + inapos = !inapos unless inquot + else + unless inquot or inapos + case string[ind] + when ?( + depth += 1 + if depth == 1 + string = string[1..-1] + ind -= 1 + end + when ?) + depth -= 1 + if depth == 0 + s = string[0,ind].strip + arguments << s unless s == "" + string = string[ind+1..-1] + end + when ?, + if depth == 1 + s = string[0,ind].strip + arguments << s unless s == "" + string = string[ind+1..-1] + ind = -1 + end + end + end + end + ind += 1 + end while depth > 0 and ind < string.length + return nil unless depth==0 + [string,arguments] + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/quickpath.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/quickpath.rb new file mode 100644 index 0000000000..c099db8579 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/quickpath.rb @@ -0,0 +1,266 @@ +require 'rexml/functions' +require 'rexml/xmltokens' + +module REXML + class QuickPath + include Functions + include XMLTokens + + EMPTY_HASH = {} + + def QuickPath::first element, path, namespaces=EMPTY_HASH + match(element, path, namespaces)[0] + end + + def QuickPath::each element, path, namespaces=EMPTY_HASH, &block + path = "*" unless path + match(element, path, namespaces).each( &block ) + end + + def QuickPath::match element, path, namespaces=EMPTY_HASH + raise "nil is not a valid xpath" unless path + results = nil + Functions::namespace_context = namespaces + case path + when /^\/([^\/]|$)/u + # match on root + path = path[1..-1] + return [element.root.parent] if path == '' + results = filter([element.root], path) + when /^[-\w]*::/u + results = filter([element], path) + when /^\*/u + results = filter(element.to_a, path) + when /^[\[!\w:]/u + # match on child + matches = [] + children = element.to_a + results = filter(children, path) + else + results = filter([element], path) + end + return results + end + + # Given an array of nodes it filters the array based on the path. The + # result is that when this method returns, the array will contain elements + # which match the path + def QuickPath::filter elements, path + return elements if path.nil? or path == '' or elements.size == 0 + case path + when /^\/\//u # Descendant + return axe( elements, "descendant-or-self", $' ) + when /^\/?\b(\w[-\w]*)\b::/u # Axe + axe_name = $1 + rest = $' + return axe( elements, $1, $' ) + when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u # Child + rest = $' + results = [] + elements.each do |element| + results |= filter( element.to_a, rest ) + end + return results + when /^\/?(\w[-\w]*)\(/u # / Function + return function( elements, $1, $' ) + when Namespace::NAMESPLIT # Element name + name = $2 + ns = $1 + rest = $' + elements.delete_if do |element| + !(element.kind_of? Element and + (element.expanded_name == name or + (element.name == name and + element.namespace == Functions.namespace_context[ns]))) + end + return filter( elements, rest ) + when /^\/\[/u + matches = [] + elements.each do |element| + matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element + end + return matches + when /^\[/u # Predicate + return predicate( elements, path ) + when /^\/?\.\.\./u # Ancestor + return axe( elements, "ancestor", $' ) + when /^\/?\.\./u # Parent + return filter( elements.collect{|e|e.parent}, $' ) + when /^\/?\./u # Self + return filter( elements, $' ) + when /^\*/u # Any + results = [] + elements.each do |element| + results |= filter( [element], $' ) if element.kind_of? Element + #if element.kind_of? Element + # children = element.to_a + # children.delete_if { |child| !child.kind_of?(Element) } + # results |= filter( children, $' ) + #end + end + return results + end + return [] + end + + def QuickPath::axe( elements, axe_name, rest ) + matches = [] + matches = filter( elements.dup, rest ) if axe_name =~ /-or-self$/u + case axe_name + when /^descendant/u + elements.each do |element| + matches |= filter( element.to_a, "descendant-or-self::#{rest}" ) if element.kind_of? Element + end + when /^ancestor/u + elements.each do |element| + while element.parent + matches << element.parent + element = element.parent + end + end + matches = filter( matches, rest ) + when "self" + matches = filter( elements, rest ) + when "child" + elements.each do |element| + matches |= filter( element.to_a, rest ) if element.kind_of? Element + end + when "attribute" + elements.each do |element| + matches << element.attributes[ rest ] if element.kind_of? Element + end + when "parent" + matches = filter(elements.collect{|element| element.parent}.uniq, rest) + when "following-sibling" + matches = filter(elements.collect{|element| element.next_sibling}.uniq, + rest) + when "previous-sibling" + matches = filter(elements.collect{|element| + element.previous_sibling}.uniq, rest ) + end + return matches.uniq + end + + # A predicate filters a node-set with respect to an axis to produce a + # new node-set. For each node in the node-set to be filtered, the + # PredicateExpr is evaluated with that node as the context node, with + # the number of nodes in the node-set as the context size, and with the + # proximity position of the node in the node-set with respect to the + # axis as the context position; if PredicateExpr evaluates to true for + # that node, the node is included in the new node-set; otherwise, it is + # not included. + # + # A PredicateExpr is evaluated by evaluating the Expr and converting + # the result to a boolean. If the result is a number, the result will + # be converted to true if the number is equal to the context position + # and will be converted to false otherwise; if the result is not a + # number, then the result will be converted as if by a call to the + # boolean function. Thus a location path para[3] is equivalent to + # para[position()=3]. + def QuickPath::predicate( elements, path ) + ind = 1 + bcount = 1 + while bcount > 0 + bcount += 1 if path[ind] == ?[ + bcount -= 1 if path[ind] == ?] + ind += 1 + end + ind -= 1 + predicate = path[1..ind-1] + rest = path[ind+1..-1] + + # have to change 'a [=<>] b [=<>] c' into 'a [=<>] b and b [=<>] c' + predicate.gsub!( /([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)/u ) { + "#$1 #$2 #$3 and #$3 #$4 #$5" + } + # Let's do some Ruby trickery to avoid some work: + predicate.gsub!( /&/u, "&&" ) + predicate.gsub!( /=/u, "==" ) + predicate.gsub!( /@(\w[-\w.]*)/u ) { + "attribute(\"#$1\")" + } + predicate.gsub!( /\bmod\b/u, "%" ) + predicate.gsub!( /\b(\w[-\w.]*\()/u ) { + fname = $1 + fname.gsub( /-/u, "_" ) + } + + Functions.pair = [ 0, elements.size ] + results = [] + elements.each do |element| + Functions.pair[0] += 1 + Functions.node = element + res = eval( predicate ) + case res + when true + results << element + when Fixnum + results << element if Functions.pair[0] == res + when String + results << element + end + end + return filter( results, rest ) + end + + def QuickPath::attribute( name ) + return Functions.node.attributes[name] if Functions.node.kind_of? Element + end + + def QuickPath::name() + return Functions.node.name if Functions.node.kind_of? Element + end + + def QuickPath::method_missing( id, *args ) + begin + Functions.send( id.id2name, *args ) + rescue Exception + raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}" + end + end + + def QuickPath::function( elements, fname, rest ) + args = parse_args( elements, rest ) + Functions.pair = [0, elements.size] + results = [] + elements.each do |element| + Functions.pair[0] += 1 + Functions.node = element + res = Functions.send( fname, *args ) + case res + when true + results << element + when Fixnum + results << element if Functions.pair[0] == res + end + end + return results + end + + def QuickPath::parse_args( element, string ) + # /.*?(?:\)|,)/ + arguments = [] + buffer = "" + while string and string != "" + c = string[0] + string.sub!(/^./u, "") + case c + when ?, + # if depth = 1, then we start a new argument + arguments << evaluate( buffer ) + #arguments << evaluate( string[0..count] ) + when ?( + # start a new method call + function( element, buffer, string ) + buffer = "" + when ?) + # close the method call and return arguments + return arguments + else + buffer << c + end + end + "" + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/rexml.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/rexml.rb new file mode 100644 index 0000000000..19805d61e1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/rexml.rb @@ -0,0 +1,30 @@ +# REXML is an XML toolkit for Ruby[http://www.ruby-lang.org], in Ruby. +# +# REXML is a _pure_ Ruby, XML 1.0 conforming, +# non-validating[http://www.w3.org/TR/2004/REC-xml-20040204/#sec-conformance] +# toolkit with an intuitive API. REXML passes 100% of the non-validating Oasis +# tests[http://www.oasis-open.org/committees/xml-conformance/xml-test-suite.shtml], +# and provides tree, stream, SAX2, pull, and lightweight APIs. REXML also +# includes a full XPath[http://www.w3c.org/tr/xpath] 1.0 implementation. Since +# Ruby 1.8, REXML is included in the standard Ruby distribution. +# +# Main page:: http://www.germane-software.com/software/rexml +# Author:: Sean Russell +# Version:: 3.1.7.1 +# Date:: 2007/209 +# +# This API documentation can be downloaded from the REXML home page, or can +# be accessed online[http://www.germane-software.com/software/rexml_doc] +# +# A tutorial is available in the REXML distribution in docs/tutorial.html, +# or can be accessed +# online[http://www.germane-software.com/software/rexml/docs/tutorial.html] +module REXML + COPYRIGHT = "Copyright © 2001-2007 Sean Russell " + DATE = "2007/209" + VERSION = "3.1.7.1" + REVISION = "$Revision: 1270$".gsub(/\$Revision:|\$/,'').strip + + Copyright = COPYRIGHT + Version = VERSION +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/sax2listener.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/sax2listener.rb new file mode 100644 index 0000000000..8db1389d06 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/sax2listener.rb @@ -0,0 +1,97 @@ +module REXML + # A template for stream parser listeners. + # Note that the declarations (attlistdecl, elementdecl, etc) are trivially + # processed; REXML doesn't yet handle doctype entity declarations, so you + # have to parse them out yourself. + # === Missing methods from SAX2 + # ignorable_whitespace + # === Methods extending SAX2 + # +WARNING+ + # These methods are certainly going to change, until DTDs are fully + # supported. Be aware of this. + # start_document + # end_document + # doctype + # elementdecl + # attlistdecl + # entitydecl + # notationdecl + # cdata + # xmldecl + # comment + module SAX2Listener + def start_document + end + def end_document + end + def start_prefix_mapping prefix, uri + end + def end_prefix_mapping prefix + end + def start_element uri, localname, qname, attributes + end + def end_element uri, localname, qname + end + def characters text + end + def processing_instruction target, data + end + # Handles a doctype declaration. Any attributes of the doctype which are + # not supplied will be nil. # EG, + # @p name the name of the doctype; EG, "me" + # @p pub_sys "PUBLIC", "SYSTEM", or nil. EG, "PUBLIC" + # @p long_name the supplied long name, or nil. EG, "foo" + # @p uri the uri of the doctype, or nil. EG, "bar" + def doctype name, pub_sys, long_name, uri + end + # If a doctype includes an ATTLIST declaration, it will cause this + # method to be called. The content is the declaration itself, unparsed. + # EG, will come to this method as "el + # attr CDATA #REQUIRED". This is the same for all of the .*decl + # methods. + def attlistdecl(element, pairs, contents) + end + # + def elementdecl content + end + # + # The argument passed to this method is an array of the entity + # declaration. It can be in a number of formats, but in general it + # returns (example, result): + # + # ["%", "YN", "'\"Yes\"'", "\""] + # + # ["%", "YN", "'Yes'", "s"] + # + # ["WhatHeSaid", "\"He said %YN;\"", "YN"] + # + # ["open-hatch", "SYSTEM", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""] + # + # ["open-hatch", "PUBLIC", "\"-//Textuality//TEXT Standard open-hatch boilerplate//EN\"", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""] + # + # ["hatch-pic", "SYSTEM", "\"../grafix/OpenHatch.gif\"", "\n\t\t\t\t\t\t\tNDATA gif", "gif"] + def entitydecl name, decl + end + # + def notationdecl content + end + # Called when is encountered in a document. + # @p content "..." + def cdata content + end + # Called when an XML PI is encountered in the document. + # EG: + # @p version the version attribute value. EG, "1.0" + # @p encoding the encoding attribute value, or nil. EG, "utf" + # @p standalone the standalone attribute value, or nil. EG, nil + # @p spaced the declaration is followed by a line break + def xmldecl version, encoding, standalone + end + # Called when a comment is encountered. + # @p comment The content of the comment + def comment comment + end + def progress position + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/source.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/source.rb new file mode 100644 index 0000000000..ada876cde5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/source.rb @@ -0,0 +1,247 @@ +require 'rexml/encoding' + +module REXML + # Generates Source-s. USE THIS CLASS. + class SourceFactory + # Generates a Source object + # @param arg Either a String, or an IO + # @return a Source, or nil if a bad argument was given + def SourceFactory::create_from(arg) + if arg.kind_of? String + Source.new(arg) + elsif arg.respond_to? :read and + arg.respond_to? :readline and + arg.respond_to? :nil? and + arg.respond_to? :eof? + IOSource.new(arg) + elsif arg.kind_of? Source + arg + else + raise "#{source.class} is not a valid input stream. It must walk \n"+ + "like either a String, IO, or Source." + end + end + end + + # A Source can be searched for patterns, and wraps buffers and other + # objects and provides consumption of text + class Source + include Encoding + # The current buffer (what we're going to read next) + attr_reader :buffer + # The line number of the last consumed text + attr_reader :line + attr_reader :encoding + + # Constructor + # @param arg must be a String, and should be a valid XML document + # @param encoding if non-null, sets the encoding of the source to this + # value, overriding all encoding detection + def initialize(arg, encoding=nil) + @orig = @buffer = arg + if encoding + self.encoding = encoding + else + self.encoding = check_encoding( @buffer ) + end + @line = 0 + end + + + # Inherited from Encoding + # Overridden to support optimized en/decoding + def encoding=(enc) + return unless super + @line_break = encode( '>' ) + if enc != UTF_8 + @buffer = decode(@buffer) + @to_utf = true + else + @to_utf = false + end + end + + # Scans the source for a given pattern. Note, that this is not your + # usual scan() method. For one thing, the pattern argument has some + # requirements; for another, the source can be consumed. You can easily + # confuse this method. Originally, the patterns were easier + # to construct and this method more robust, because this method + # generated search regexes on the fly; however, this was + # computationally expensive and slowed down the entire REXML package + # considerably, since this is by far the most commonly called method. + # @param pattern must be a Regexp, and must be in the form of + # /^\s*(#{your pattern, with no groups})(.*)/. The first group + # will be returned; the second group is used if the consume flag is + # set. + # @param consume if true, the pattern returned will be consumed, leaving + # everything after it in the Source. + # @return the pattern, if found, or nil if the Source is empty or the + # pattern is not found. + def scan(pattern, cons=false) + return nil if @buffer.nil? + rv = @buffer.scan(pattern) + @buffer = $' if cons and rv.size>0 + rv + end + + def read + end + + def consume( pattern ) + @buffer = $' if pattern.match( @buffer ) + end + + def match_to( char, pattern ) + return pattern.match(@buffer) + end + + def match_to_consume( char, pattern ) + md = pattern.match(@buffer) + @buffer = $' + return md + end + + def match(pattern, cons=false) + md = pattern.match(@buffer) + @buffer = $' if cons and md + return md + end + + # @return true if the Source is exhausted + def empty? + @buffer == "" + end + + def position + @orig.index( @buffer ) + end + + # @return the current line in the source + def current_line + lines = @orig.split + res = lines.grep @buffer[0..30] + res = res[-1] if res.kind_of? Array + lines.index( res ) if res + end + end + + # A Source that wraps an IO. See the Source class for method + # documentation + class IOSource < Source + #attr_reader :block_size + + # block_size has been deprecated + def initialize(arg, block_size=500, encoding=nil) + @er_source = @source = arg + @to_utf = false + # Determining the encoding is a deceptively difficult issue to resolve. + # First, we check the first two bytes for UTF-16. Then we + # assume that the encoding is at least ASCII enough for the '>', and + # we read until we get one of those. This gives us the XML declaration, + # if there is one. If there isn't one, the file MUST be UTF-8, as per + # the XML spec. If there is one, we can determine the encoding from + # it. + @buffer = "" + str = @source.read( 2 ) + if encoding + self.encoding = encoding + elsif /\A(?:\xfe\xff|\xff\xfe)/n =~ str + self.encoding = check_encoding( str ) + elsif (0xef == str[0] && 0xbb == str[1]) + str += @source.read(1) + str = '' if (0xbf == str[2]) + else + @line_break = '>' + end + super str+@source.readline( @line_break ) + end + + def scan(pattern, cons=false) + rv = super + # You'll notice that this next section is very similar to the same + # section in match(), but just a liiittle different. This is + # because it is a touch faster to do it this way with scan() + # than the way match() does it; enough faster to warrent duplicating + # some code + if rv.size == 0 + until @buffer =~ pattern or @source.nil? + begin + # READLINE OPT + #str = @source.read(@block_size) + str = @source.readline(@line_break) + str = decode(str) if @to_utf and str + @buffer << str + rescue Iconv::IllegalSequence + raise + rescue + @source = nil + end + end + rv = super + end + rv.taint + rv + end + + def read + begin + str = @source.readline(@line_break) + str = decode(str) if @to_utf and str + @buffer << str + rescue Exception, NameError + @source = nil + end + end + + def consume( pattern ) + match( pattern, true ) + end + + def match( pattern, cons=false ) + rv = pattern.match(@buffer) + @buffer = $' if cons and rv + while !rv and @source + begin + str = @source.readline(@line_break) + str = decode(str) if @to_utf and str + @buffer << str + rv = pattern.match(@buffer) + @buffer = $' if cons and rv + rescue + @source = nil + end + end + rv.taint + rv + end + + def empty? + super and ( @source.nil? || @source.eof? ) + end + + def position + @er_source.stat.pipe? ? 0 : @er_source.pos + end + + # @return the current line in the source + def current_line + begin + pos = @er_source.pos # The byte position in the source + lineno = @er_source.lineno # The XML < position in the source + @er_source.rewind + line = 0 # The \r\n position in the source + begin + while @er_source.pos < pos + @er_source.readline + line += 1 + end + rescue + end + rescue IOError + pos = -1 + line = -1 + end + [pos, lineno, line] + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/streamlistener.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/streamlistener.rb new file mode 100644 index 0000000000..6f401125b5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/streamlistener.rb @@ -0,0 +1,92 @@ +module REXML + # A template for stream parser listeners. + # Note that the declarations (attlistdecl, elementdecl, etc) are trivially + # processed; REXML doesn't yet handle doctype entity declarations, so you + # have to parse them out yourself. + module StreamListener + # Called when a tag is encountered. + # @p name the tag name + # @p attrs an array of arrays of attribute/value pairs, suitable for + # use with assoc or rassoc. IE, + # will result in + # tag_start( "tag", # [["attr1","value1"],["attr2","value2"]]) + def tag_start name, attrs + end + # Called when the end tag is reached. In the case of , tag_end + # will be called immidiately after tag_start + # @p the name of the tag + def tag_end name + end + # Called when text is encountered in the document + # @p text the text content. + def text text + end + # Called when an instruction is encountered. EG: + # @p name the instruction name; in the example, "xsl" + # @p instruction the rest of the instruction. In the example, + # "sheet='foo'" + def instruction name, instruction + end + # Called when a comment is encountered. + # @p comment The content of the comment + def comment comment + end + # Handles a doctype declaration. Any attributes of the doctype which are + # not supplied will be nil. # EG, + # @p name the name of the doctype; EG, "me" + # @p pub_sys "PUBLIC", "SYSTEM", or nil. EG, "PUBLIC" + # @p long_name the supplied long name, or nil. EG, "foo" + # @p uri the uri of the doctype, or nil. EG, "bar" + def doctype name, pub_sys, long_name, uri + end + # Called when the doctype is done + def doctype_end + end + # If a doctype includes an ATTLIST declaration, it will cause this + # method to be called. The content is the declaration itself, unparsed. + # EG, will come to this method as "el + # attr CDATA #REQUIRED". This is the same for all of the .*decl + # methods. + def attlistdecl element_name, attributes, raw_content + end + # + def elementdecl content + end + # + # The argument passed to this method is an array of the entity + # declaration. It can be in a number of formats, but in general it + # returns (example, result): + # + # ["%", "YN", "'\"Yes\"'", "\""] + # + # ["%", "YN", "'Yes'", "s"] + # + # ["WhatHeSaid", "\"He said %YN;\"", "YN"] + # + # ["open-hatch", "SYSTEM", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""] + # + # ["open-hatch", "PUBLIC", "\"-//Textuality//TEXT Standard open-hatch boilerplate//EN\"", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""] + # + # ["hatch-pic", "SYSTEM", "\"../grafix/OpenHatch.gif\"", "\n\t\t\t\t\t\t\tNDATA gif", "gif"] + def entitydecl content + end + # + def notationdecl content + end + # Called when %foo; is encountered in a doctype declaration. + # @p content "foo" + def entity content + end + # Called when is encountered in a document. + # @p content "..." + def cdata content + end + # Called when an XML PI is encountered in the document. + # EG: + # @p version the version attribute value. EG, "1.0" + # @p encoding the encoding attribute value, or nil. EG, "utf" + # @p standalone the standalone attribute value, or nil. EG, nil + def xmldecl version, encoding, standalone + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/syncenumerator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/syncenumerator.rb new file mode 100644 index 0000000000..955e006cb2 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/syncenumerator.rb @@ -0,0 +1,33 @@ +module REXML + class SyncEnumerator + include Enumerable + + # Creates a new SyncEnumerator which enumerates rows of given + # Enumerable objects. + def initialize(*enums) + @gens = enums + @biggest = @gens[0] + @gens.each {|x| @biggest = x if x.size > @biggest.size } + end + + # Returns the number of enumerated Enumerable objects, i.e. the size + # of each row. + def size + @gens.size + end + + # Returns the number of enumerated Enumerable objects, i.e. the size + # of each row. + def length + @gens.length + end + + # Enumerates rows of the Enumerable objects. + def each + @biggest.zip( *@gens ) {|a| + yield(*a[1..-1]) + } + self + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/text.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/text.rb new file mode 100644 index 0000000000..9804aa710b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/text.rb @@ -0,0 +1,344 @@ +require 'rexml/entity' +require 'rexml/doctype' +require 'rexml/child' +require 'rexml/doctype' +require 'rexml/parseexception' + +module REXML + # Represents text nodes in an XML document + class Text < Child + include Comparable + # The order in which the substitutions occur + SPECIALS = [ /&(?!#?[\w-]+;)/u, //u, /"/u, /'/u, /\r/u ] + SUBSTITUTES = ['&', '<', '>', '"', ''', ' '] + # Characters which are substituted in written strings + SLAICEPS = [ '<', '>', '"', "'", '&' ] + SETUTITSBUS = [ /</u, />/u, /"/u, /'/u, /&/u ] + + # If +raw+ is true, then REXML leaves the value alone + attr_accessor :raw + + ILLEGAL = /(<|&(?!(#{Entity::NAME})|(#0*((?:\d+)|(?:x[a-fA-F0-9]+)));))/um + NUMERICENTITY = /�*((?:\d+)|(?:x[a-fA-F0-9]+));/ + + # Constructor + # +arg+ if a String, the content is set to the String. If a Text, + # the object is shallowly cloned. + # + # +respect_whitespace+ (boolean, false) if true, whitespace is + # respected + # + # +parent+ (nil) if this is a Parent object, the parent + # will be set to this. + # + # +raw+ (nil) This argument can be given three values. + # If true, then the value of used to construct this object is expected to + # contain no unescaped XML markup, and REXML will not change the text. If + # this value is false, the string may contain any characters, and REXML will + # escape any and all defined entities whose values are contained in the + # text. If this value is nil (the default), then the raw value of the + # parent will be used as the raw value for this node. If there is no raw + # value for the parent, and no value is supplied, the default is false. + # Use this field if you have entities defined for some text, and you don't + # want REXML to escape that text in output. + # Text.new( "<&", false, nil, false ) #-> "<&" + # Text.new( "<&", false, nil, false ) #-> "&lt;&amp;" + # Text.new( "<&", false, nil, true ) #-> Parse exception + # Text.new( "<&", false, nil, true ) #-> "<&" + # # Assume that the entity "s" is defined to be "sean" + # # and that the entity "r" is defined to be "russell" + # Text.new( "sean russell" ) #-> "&s; &r;" + # Text.new( "sean russell", false, nil, true ) #-> "sean russell" + # + # +entity_filter+ (nil) This can be an array of entities to match in the + # supplied text. This argument is only useful if +raw+ is set to false. + # Text.new( "sean russell", false, nil, false, ["s"] ) #-> "&s; russell" + # Text.new( "sean russell", false, nil, true, ["s"] ) #-> "sean russell" + # In the last example, the +entity_filter+ argument is ignored. + # + # +pattern+ INTERNAL USE ONLY + def initialize(arg, respect_whitespace=false, parent=nil, raw=nil, + entity_filter=nil, illegal=ILLEGAL ) + + @raw = false + + if parent + super( parent ) + @raw = parent.raw + else + @parent = nil + end + + @raw = raw unless raw.nil? + @entity_filter = entity_filter + @normalized = @unnormalized = nil + + if arg.kind_of? String + @string = arg.clone + @string.squeeze!(" \n\t") unless respect_whitespace + elsif arg.kind_of? Text + @string = arg.to_s + @raw = arg.raw + elsif + raise "Illegal argument of type #{arg.type} for Text constructor (#{arg})" + end + + @string.gsub!( /\r\n?/, "\n" ) + + # check for illegal characters + if @raw + if @string =~ illegal + raise "Illegal character '#{$1}' in raw string \"#{@string}\"" + end + end + end + + def node_type + :text + end + + def empty? + @string.size==0 + end + + + def clone + return Text.new(self) + end + + + # Appends text to this text node. The text is appended in the +raw+ mode + # of this text node. + def <<( to_append ) + @string << to_append.gsub( /\r\n?/, "\n" ) + end + + + # +other+ a String or a Text + # +returns+ the result of (to_s <=> arg.to_s) + def <=>( other ) + to_s() <=> other.to_s + end + + REFERENCE = /#{Entity::REFERENCE}/ + # Returns the string value of this text node. This string is always + # escaped, meaning that it is a valid XML text node string, and all + # entities that can be escaped, have been inserted. This method respects + # the entity filter set in the constructor. + # + # # Assume that the entity "s" is defined to be "sean", and that the + # # entity "r" is defined to be "russell" + # t = Text.new( "< & sean russell", false, nil, false, ['s'] ) + # t.to_s #-> "< & &s; russell" + # t = Text.new( "< & &s; russell", false, nil, false ) + # t.to_s #-> "< & &s; russell" + # u = Text.new( "sean russell", false, nil, true ) + # u.to_s #-> "sean russell" + def to_s + return @string if @raw + return @normalized if @normalized + + doctype = nil + if @parent + doc = @parent.document + doctype = doc.doctype if doc + end + + @normalized = Text::normalize( @string, doctype, @entity_filter ) + end + + def inspect + @string.inspect + end + + # Returns the string value of this text. This is the text without + # entities, as it might be used programmatically, or printed to the + # console. This ignores the 'raw' attribute setting, and any + # entity_filter. + # + # # Assume that the entity "s" is defined to be "sean", and that the + # # entity "r" is defined to be "russell" + # t = Text.new( "< & sean russell", false, nil, false, ['s'] ) + # t.value #-> "< & sean russell" + # t = Text.new( "< & &s; russell", false, nil, false ) + # t.value #-> "< & sean russell" + # u = Text.new( "sean russell", false, nil, true ) + # u.value #-> "sean russell" + def value + @unnormalized if @unnormalized + doctype = nil + if @parent + doc = @parent.document + doctype = doc.doctype if doc + end + @unnormalized = Text::unnormalize( @string, doctype ) + end + + # Sets the contents of this text node. This expects the text to be + # unnormalized. It returns self. + # + # e = Element.new( "a" ) + # e.add_text( "foo" ) # foo + # e[0].value = "bar" # bar + # e[0].value = "" # <a> + def value=( val ) + @string = val.gsub( /\r\n?/, "\n" ) + @unnormalized = nil + @normalized = nil + @raw = false + end + + def wrap(string, width, addnewline=false) + # Recursivly wrap string at width. + return string if string.length <= width + place = string.rindex(' ', width) # Position in string with last ' ' before cutoff + if addnewline then + return "\n" + string[0,place] + "\n" + wrap(string[place+1..-1], width) + else + return string[0,place] + "\n" + wrap(string[place+1..-1], width) + end + end + + def indent_text(string, level=1, style="\t", indentfirstline=true) + return string if level < 0 + new_string = '' + string.each { |line| + indent_string = style * level + new_line = (indent_string + line).sub(/[\s]+$/,'') + new_string << new_line + } + new_string.strip! unless indentfirstline + return new_string + end + + # == DEPRECATED + # See REXML::Formatters + # + def write( writer, indent=-1, transitive=false, ie_hack=false ) + Kernel.warn("#{self.class.name}.write is deprecated. See REXML::Formatters") + formatter = if indent > -1 + REXML::Formatters::Pretty.new( indent ) + else + REXML::Formatters::Default.new + end + formatter.write( self, writer ) + end + + # FIXME + # This probably won't work properly + def xpath + path = @parent.xpath + path += "/text()" + return path + end + + # Writes out text, substituting special characters beforehand. + # +out+ A String, IO, or any other object supporting <<( String ) + # +input+ the text to substitute and the write out + # + # z=utf8.unpack("U*") + # ascOut="" + # z.each{|r| + # if r < 0x100 + # ascOut.concat(r.chr) + # else + # ascOut.concat(sprintf("&#x%x;", r)) + # end + # } + # puts ascOut + def write_with_substitution out, input + copy = input.clone + # Doing it like this rather than in a loop improves the speed + copy.gsub!( SPECIALS[0], SUBSTITUTES[0] ) + copy.gsub!( SPECIALS[1], SUBSTITUTES[1] ) + copy.gsub!( SPECIALS[2], SUBSTITUTES[2] ) + copy.gsub!( SPECIALS[3], SUBSTITUTES[3] ) + copy.gsub!( SPECIALS[4], SUBSTITUTES[4] ) + copy.gsub!( SPECIALS[5], SUBSTITUTES[5] ) + out << copy + end + + # Reads text, substituting entities + def Text::read_with_substitution( input, illegal=nil ) + copy = input.clone + + if copy =~ illegal + raise ParseException.new( "malformed text: Illegal character #$& in \"#{copy}\"" ) + end if illegal + + copy.gsub!( /\r\n?/, "\n" ) + if copy.include? ?& + copy.gsub!( SETUTITSBUS[0], SLAICEPS[0] ) + copy.gsub!( SETUTITSBUS[1], SLAICEPS[1] ) + copy.gsub!( SETUTITSBUS[2], SLAICEPS[2] ) + copy.gsub!( SETUTITSBUS[3], SLAICEPS[3] ) + copy.gsub!( SETUTITSBUS[4], SLAICEPS[4] ) + copy.gsub!( /�*((?:\d+)|(?:x[a-f0-9]+));/ ) {|m| + m=$1 + #m='0' if m=='' + m = "0#{m}" if m[0] == ?x + [Integer(m)].pack('U*') + } + end + copy + end + + EREFERENCE = /&(?!#{Entity::NAME};)/ + # Escapes all possible entities + def Text::normalize( input, doctype=nil, entity_filter=nil ) + copy = input + # Doing it like this rather than in a loop improves the speed + #copy = copy.gsub( EREFERENCE, '&' ) + copy = copy.gsub( "&", "&" ) + if doctype + # Replace all ampersands that aren't part of an entity + doctype.entities.each_value do |entity| + copy = copy.gsub( entity.value, + "&#{entity.name};" ) if entity.value and + not( entity_filter and entity_filter.include?(entity) ) + end + else + # Replace all ampersands that aren't part of an entity + DocType::DEFAULT_ENTITIES.each_value do |entity| + copy = copy.gsub(entity.value, "&#{entity.name};" ) + end + end + copy + end + + # Unescapes all possible entities + def Text::unnormalize( string, doctype=nil, filter=nil, illegal=nil ) + rv = string.clone + rv.gsub!( /\r\n?/, "\n" ) + matches = rv.scan( REFERENCE ) + return rv if matches.size == 0 + rv.gsub!( NUMERICENTITY ) {|m| + m=$1 + m = "0#{m}" if m[0] == ?x + [Integer(m)].pack('U*') + } + matches.collect!{|x|x[0]}.compact! + if matches.size > 0 + if doctype + matches.each do |entity_reference| + unless filter and filter.include?(entity_reference) + entity_value = doctype.entity( entity_reference ) + re = /&#{entity_reference};/ + rv.gsub!( re, entity_value ) if entity_value + end + end + else + matches.each do |entity_reference| + unless filter and filter.include?(entity_reference) + entity_value = DocType::DEFAULT_ENTITIES[ entity_reference ] + re = /&#{entity_reference};/ + rv.gsub!( re, entity_value.value ) if entity_value + end + end + end + rv.gsub!( /&/, '&' ) + end + rv + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/validation/relaxng.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/validation/relaxng.rb new file mode 100644 index 0000000000..969f51bc95 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/validation/relaxng.rb @@ -0,0 +1,559 @@ +require "rexml/validation/validation" +require "rexml/parsers/baseparser" + +module REXML + module Validation + # Implemented: + # * empty + # * element + # * attribute + # * text + # * optional + # * choice + # * oneOrMore + # * zeroOrMore + # * group + # * value + # * interleave + # * mixed + # * ref + # * grammar + # * start + # * define + # + # Not implemented: + # * data + # * param + # * include + # * externalRef + # * notAllowed + # * anyName + # * nsName + # * except + # * name + class RelaxNG + include Validator + + INFINITY = 1.0 / 0.0 + EMPTY = Event.new( nil ) + TEXT = [:start_element, "text"] + attr_accessor :current + attr_accessor :count + attr_reader :references + + # FIXME: Namespaces + def initialize source + parser = REXML::Parsers::BaseParser.new( source ) + + @count = 0 + @references = {} + @root = @current = Sequence.new(self) + @root.previous = true + states = [ @current ] + begin + event = parser.pull + case event[0] + when :start_element + case event[1] + when "empty" + when "element", "attribute", "text", "value" + states[-1] << event + when "optional" + states << Optional.new( self ) + states[-2] << states[-1] + when "choice" + states << Choice.new( self ) + states[-2] << states[-1] + when "oneOrMore" + states << OneOrMore.new( self ) + states[-2] << states[-1] + when "zeroOrMore" + states << ZeroOrMore.new( self ) + states[-2] << states[-1] + when "group" + states << Sequence.new( self ) + states[-2] << states[-1] + when "interleave" + states << Interleave.new( self ) + states[-2] << states[-1] + when "mixed" + states << Interleave.new( self ) + states[-2] << states[-1] + states[-1] << TEXT + when "define" + states << [ event[2]["name"] ] + when "ref" + states[-1] << Ref.new( event[2]["name"] ) + when "anyName" + states << AnyName.new( self ) + states[-2] << states[-1] + when "nsName" + when "except" + when "name" + when "data" + when "param" + when "include" + when "grammar" + when "start" + when "externalRef" + when "notAllowed" + end + when :end_element + case event[1] + when "element", "attribute" + states[-1] << event + when "zeroOrMore", "oneOrMore", "choice", "optional", + "interleave", "group", "mixed" + states.pop + when "define" + ref = states.pop + @references[ ref.shift ] = ref + #when "empty" + end + when :end_document + states[-1] << event + when :text + states[-1] << event + end + end while event[0] != :end_document + end + + def receive event + validate( event ) + end + end + + class State + def initialize( context ) + @previous = [] + @events = [] + @current = 0 + @count = context.count += 1 + @references = context.references + @value = false + end + + def reset + return if @current == 0 + @current = 0 + @events.each {|s| s.reset if s.kind_of? State } + end + + def previous=( previous ) + @previous << previous + end + + def next( event ) + #print "In next with #{event.inspect}. " + #puts "Next (#@current) is #{@events[@current]}" + #p @previous + return @previous.pop.next( event ) if @events[@current].nil? + expand_ref_in( @events, @current ) if @events[@current].class == Ref + if ( @events[@current].kind_of? State ) + @current += 1 + @events[@current-1].previous = self + return @events[@current-1].next( event ) + end + #puts "Current isn't a state" + if ( @events[@current].matches?(event) ) + @current += 1 + if @events[@current].nil? + #puts "#{inspect[0,5]} 1RETURNING #{@previous.inspect[0,5]}" + return @previous.pop + elsif @events[@current].kind_of? State + @current += 1 + #puts "#{inspect[0,5]} 2RETURNING (#{@current-1}) #{@events[@current-1].inspect[0,5]}; on return, next is #{@events[@current]}" + @events[@current-1].previous = self + return @events[@current-1] + else + #puts "#{inspect[0,5]} RETURNING self w/ next(#@current) = #{@events[@current]}" + return self + end + else + return nil + end + end + + def to_s + # Abbreviated: + self.class.name =~ /(?:::)(\w)\w+$/ + # Full: + #self.class.name =~ /(?:::)(\w+)$/ + "#$1.#@count" + end + + def inspect + "< #{to_s} #{@events.collect{|e| + pre = e == @events[@current] ? '#' : '' + pre + e.inspect unless self == e + }.join(', ')} >" + end + + def expected + return [@events[@current]] + end + + def <<( event ) + add_event_to_arry( @events, event ) + end + + + protected + def expand_ref_in( arry, ind ) + new_events = [] + @references[ arry[ind].to_s ].each{ |evt| + add_event_to_arry(new_events,evt) + } + arry[ind,1] = new_events + end + + def add_event_to_arry( arry, evt ) + evt = generate_event( evt ) + if evt.kind_of? String + arry[-1].event_arg = evt if arry[-1].kind_of? Event and @value + @value = false + else + arry << evt + end + end + + def generate_event( event ) + return event if event.kind_of? State or event.class == Ref + evt = nil + arg = nil + case event[0] + when :start_element + case event[1] + when "element" + evt = :start_element + arg = event[2]["name"] + when "attribute" + evt = :start_attribute + arg = event[2]["name"] + when "text" + evt = :text + when "value" + evt = :text + @value = true + end + when :text + return event[1] + when :end_document + return Event.new( event[0] ) + else # then :end_element + case event[1] + when "element" + evt = :end_element + when "attribute" + evt = :end_attribute + end + end + return Event.new( evt, arg ) + end + end + + + class Sequence < State + def matches?(event) + @events[@current].matches?( event ) + end + end + + + class Optional < State + def next( event ) + if @current == 0 + rv = super + return rv if rv + @prior = @previous.pop + return @prior.next( event ) + end + super + end + + def matches?(event) + @events[@current].matches?(event) || + (@current == 0 and @previous[-1].matches?(event)) + end + + def expected + return [ @prior.expected, @events[0] ].flatten if @current == 0 + return [@events[@current]] + end + end + + + class ZeroOrMore < Optional + def next( event ) + expand_ref_in( @events, @current ) if @events[@current].class == Ref + if ( @events[@current].matches?(event) ) + @current += 1 + if @events[@current].nil? + @current = 0 + return self + elsif @events[@current].kind_of? State + @current += 1 + @events[@current-1].previous = self + return @events[@current-1] + else + return self + end + else + @prior = @previous.pop + return @prior.next( event ) if @current == 0 + return nil + end + end + + def expected + return [ @prior.expected, @events[0] ].flatten if @current == 0 + return [@events[@current]] + end + end + + + class OneOrMore < State + def initialize context + super + @ord = 0 + end + + def reset + super + @ord = 0 + end + + def next( event ) + expand_ref_in( @events, @current ) if @events[@current].class == Ref + if ( @events[@current].matches?(event) ) + @current += 1 + @ord += 1 + if @events[@current].nil? + @current = 0 + return self + elsif @events[@current].kind_of? State + @current += 1 + @events[@current-1].previous = self + return @events[@current-1] + else + return self + end + else + return @previous.pop.next( event ) if @current == 0 and @ord > 0 + return nil + end + end + + def matches?( event ) + @events[@current].matches?(event) || + (@current == 0 and @ord > 0 and @previous[-1].matches?(event)) + end + + def expected + if @current == 0 and @ord > 0 + return [@previous[-1].expected, @events[0]].flatten + else + return [@events[@current]] + end + end + end + + + class Choice < State + def initialize context + super + @choices = [] + end + + def reset + super + @events = [] + @choices.each { |c| c.each { |s| s.reset if s.kind_of? State } } + end + + def <<( event ) + add_event_to_arry( @choices, event ) + end + + def next( event ) + # Make the choice if we haven't + if @events.size == 0 + c = 0 ; max = @choices.size + while c < max + if @choices[c][0].class == Ref + expand_ref_in( @choices[c], 0 ) + @choices += @choices[c] + @choices.delete( @choices[c] ) + max -= 1 + else + c += 1 + end + end + @events = @choices.find { |evt| evt[0].matches? event } + # Remove the references + # Find the events + end + #puts "In next with #{event.inspect}." + #puts "events is #{@events.inspect}" + unless @events + @events = [] + return nil + end + #puts "current = #@current" + super + end + + def matches?( event ) + return @events[@current].matches?( event ) if @events.size > 0 + !@choices.find{|evt| evt[0].matches?(event)}.nil? + end + + def expected + #puts "IN CHOICE EXPECTED" + #puts "EVENTS = #{@events.inspect}" + return [@events[@current]] if @events.size > 0 + return @choices.collect do |x| + if x[0].kind_of? State + x[0].expected + else + x[0] + end + end.flatten + end + + def inspect + "< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' or ')} >" + end + + protected + def add_event_to_arry( arry, evt ) + if evt.kind_of? State or evt.class == Ref + arry << [evt] + elsif evt[0] == :text + if arry[-1] and + arry[-1][-1].kind_of?( Event ) and + arry[-1][-1].event_type == :text and @value + + arry[-1][-1].event_arg = evt[1] + @value = false + end + else + arry << [] if evt[0] == :start_element + arry[-1] << generate_event( evt ) + end + end + end + + + class Interleave < Choice + def initialize context + super + @choice = 0 + end + + def reset + @choice = 0 + end + + def next_current( event ) + # Expand references + c = 0 ; max = @choices.size + while c < max + if @choices[c][0].class == Ref + expand_ref_in( @choices[c], 0 ) + @choices += @choices[c] + @choices.delete( @choices[c] ) + max -= 1 + else + c += 1 + end + end + @events = @choices[@choice..-1].find { |evt| evt[0].matches? event } + @current = 0 + if @events + # reorder the choices + old = @choices[@choice] + idx = @choices.index( @events ) + @choices[@choice] = @events + @choices[idx] = old + @choice += 1 + end + + #puts "In next with #{event.inspect}." + #puts "events is #{@events.inspect}" + @events = [] unless @events + end + + + def next( event ) + # Find the next series + next_current(event) unless @events[@current] + return nil unless @events[@current] + + expand_ref_in( @events, @current ) if @events[@current].class == Ref + #puts "In next with #{event.inspect}." + #puts "Next (#@current) is #{@events[@current]}" + if ( @events[@current].kind_of? State ) + @current += 1 + @events[@current-1].previous = self + return @events[@current-1].next( event ) + end + #puts "Current isn't a state" + return @previous.pop.next( event ) if @events[@current].nil? + if ( @events[@current].matches?(event) ) + @current += 1 + if @events[@current].nil? + #puts "#{inspect[0,5]} 1RETURNING self" unless @choices[@choice].nil? + return self unless @choices[@choice].nil? + #puts "#{inspect[0,5]} 1RETURNING #{@previous[-1].inspect[0,5]}" + return @previous.pop + elsif @events[@current].kind_of? State + @current += 1 + #puts "#{inspect[0,5]} 2RETURNING (#{@current-1}) #{@events[@current-1].inspect[0,5]}; on return, next is #{@events[@current]}" + @events[@current-1].previous = self + return @events[@current-1] + else + #puts "#{inspect[0,5]} RETURNING self w/ next(#@current) = #{@events[@current]}" + return self + end + else + return nil + end + end + + def matches?( event ) + return @events[@current].matches?( event ) if @events[@current] + !@choices[@choice..-1].find{|evt| evt[0].matches?(event)}.nil? + end + + def expected + #puts "IN CHOICE EXPECTED" + #puts "EVENTS = #{@events.inspect}" + return [@events[@current]] if @events[@current] + return @choices[@choice..-1].collect do |x| + if x[0].kind_of? State + x[0].expected + else + x[0] + end + end.flatten + end + + def inspect + "< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' and ')} >" + end + end + + class Ref + def initialize value + @value = value + end + def to_s + @value + end + def inspect + "{#{to_s}}" + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/validation/validation.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/validation/validation.rb new file mode 100644 index 0000000000..160ea96b31 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/validation/validation.rb @@ -0,0 +1,155 @@ +require 'rexml/validation/validationexception' + +module REXML + module Validation + module Validator + NILEVENT = [ nil ] + def reset + @current = @root + @root.reset + @root.previous = true + @attr_stack = [] + self + end + def dump + puts @root.inspect + end + def validate( event ) + #puts "Current: #@current" + #puts "Event: #{event.inspect}" + @attr_stack = [] unless defined? @attr_stack + match = @current.next(event) + raise ValidationException.new( "Validation error. Expected: "+ + @current.expected.join( " or " )+" from #{@current.inspect} "+ + " but got #{Event.new( event[0], event[1] ).inspect}" ) unless match + @current = match + + # Check for attributes + case event[0] + when :start_element + #puts "Checking attributes" + @attr_stack << event[2] + begin + sattr = [:start_attribute, nil] + eattr = [:end_attribute] + text = [:text, nil] + k,v = event[2].find { |k,v| + sattr[1] = k + #puts "Looking for #{sattr.inspect}" + m = @current.next( sattr ) + #puts "Got #{m.inspect}" + if m + # If the state has text children... + #puts "Looking for #{eattr.inspect}" + #puts "Expect #{m.expected}" + if m.matches?( eattr ) + #puts "Got end" + @current = m + else + #puts "Didn't get end" + text[1] = v + #puts "Looking for #{text.inspect}" + m = m.next( text ) + #puts "Got #{m.inspect}" + text[1] = nil + return false unless m + @current = m if m + end + m = @current.next( eattr ) + if m + @current = m + true + else + false + end + else + false + end + } + event[2].delete(k) if k + end while k + when :end_element + attrs = @attr_stack.pop + raise ValidationException.new( "Validation error. Illegal "+ + " attributes: #{attrs.inspect}") if attrs.length > 0 + end + end + end + + class Event + def initialize(event_type, event_arg=nil ) + @event_type = event_type + @event_arg = event_arg + end + + attr_reader :event_type + attr_accessor :event_arg + + def done? + @done + end + + def single? + return (@event_type != :start_element and @event_type != :start_attribute) + end + + def matches?( event ) + #puts "#@event_type =? #{event[0]} && #@event_arg =? #{event[1]} " + return false unless event[0] == @event_type + case event[0] + when nil + return true + when :start_element + return true if event[1] == @event_arg + when :end_element + return true + when :start_attribute + return true if event[1] == @event_arg + when :end_attribute + return true + when :end_document + return true + when :text + return (@event_arg.nil? or @event_arg == event[1]) +=begin + when :processing_instruction + false + when :xmldecl + false + when :start_doctype + false + when :end_doctype + false + when :externalentity + false + when :elementdecl + false + when :entity + false + when :attlistdecl + false + when :notationdecl + false + when :end_doctype + false +=end + else + false + end + end + + def ==( other ) + return false unless other.kind_of? Event + @event_type == other.event_type and @event_arg == other.event_arg + end + + def to_s + inspect + end + + def inspect + "#{@event_type.inspect}( #@event_arg )" + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/validation/validationexception.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/validation/validationexception.rb new file mode 100644 index 0000000000..4723d9e4d3 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/validation/validationexception.rb @@ -0,0 +1,9 @@ +module REXML + module Validation + class ValidationException < RuntimeError + def initialize msg + super + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/xmldecl.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/xmldecl.rb new file mode 100644 index 0000000000..427eb78cf8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/xmldecl.rb @@ -0,0 +1,119 @@ +require 'rexml/encoding' +require 'rexml/source' + +module REXML + # NEEDS DOCUMENTATION + class XMLDecl < Child + include Encoding + + DEFAULT_VERSION = "1.0"; + DEFAULT_ENCODING = "UTF-8"; + DEFAULT_STANDALONE = "no"; + START = '<\?xml'; + STOP = '\?>'; + + attr_accessor :version, :standalone + attr_reader :writeencoding, :writethis + + def initialize(version=DEFAULT_VERSION, encoding=nil, standalone=nil) + @writethis = true + @writeencoding = !encoding.nil? + if version.kind_of? XMLDecl + super() + @version = version.version + self.encoding = version.encoding + @writeencoding = version.writeencoding + @standalone = version.standalone + else + super() + @version = version + self.encoding = encoding + @standalone = standalone + end + @version = DEFAULT_VERSION if @version.nil? + end + + def clone + XMLDecl.new(self) + end + + # indent:: + # Ignored. There must be no whitespace before an XML declaration + # transitive:: + # Ignored + # ie_hack:: + # Ignored + def write(writer, indent=-1, transitive=false, ie_hack=false) + return nil unless @writethis or writer.kind_of? Output + writer << START.sub(/\\/u, '') + if writer.kind_of? Output + writer << " #{content writer.encoding}" + else + writer << " #{content encoding}" + end + writer << STOP.sub(/\\/u, '') + end + + def ==( other ) + other.kind_of?(XMLDecl) and + other.version == @version and + other.encoding == self.encoding and + other.standalone == @standalone + end + + def xmldecl version, encoding, standalone + @version = version + self.encoding = encoding + @standalone = standalone + end + + def node_type + :xmldecl + end + + alias :stand_alone? :standalone + alias :old_enc= :encoding= + + def encoding=( enc ) + if enc.nil? + self.old_enc = "UTF-8" + @writeencoding = false + else + self.old_enc = enc + @writeencoding = true + end + self.dowrite + end + + # Only use this if you do not want the XML declaration to be written; + # this object is ignored by the XML writer. Otherwise, instantiate your + # own XMLDecl and add it to the document. + # + # Note that XML 1.1 documents *must* include an XML declaration + def XMLDecl.default + rv = XMLDecl.new( "1.0" ) + rv.nowrite + rv + end + + def nowrite + @writethis = false + end + + def dowrite + @writethis = true + end + + def inspect + START.sub(/\\/u, '') + " ... " + STOP.sub(/\\/u, '') + end + + private + def content(enc) + rv = "version='#@version'" + rv << " encoding='#{enc}'" if @writeencoding || enc !~ /utf-8/i + rv << " standalone='#@standalone'" if @standalone + rv + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/xmltokens.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/xmltokens.rb new file mode 100644 index 0000000000..6bbe5b07d5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/xmltokens.rb @@ -0,0 +1,18 @@ +module REXML + # Defines a number of tokens used for parsing XML. Not for general + # consumption. + module XMLTokens + NCNAME_STR= '[\w:][\-\w\d.]*' + NAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}" + + NAMECHAR = '[\-\w\d\.:]' + NAME = "([\\w:]#{NAMECHAR}*)" + NMTOKEN = "(?:#{NAMECHAR})+" + NMTOKENS = "#{NMTOKEN}(\\s+#{NMTOKEN})*" + REFERENCE = "(?:&#{NAME};|&#\\d+;|&#x[0-9a-fA-F]+;)" + + #REFERENCE = "(?:#{ENTITYREF}|#{CHARREF})" + #ENTITYREF = "&#{NAME};" + #CHARREF = "&#\\d+;|&#x[0-9a-fA-F]+;" + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/xpath.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/xpath.rb new file mode 100644 index 0000000000..939399e283 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/xpath.rb @@ -0,0 +1,66 @@ +require 'rexml/functions' +require 'rexml/xpath_parser' + +module REXML + # Wrapper class. Use this class to access the XPath functions. + class XPath + include Functions + EMPTY_HASH = {} + + # Finds and returns the first node that matches the supplied xpath. + # element:: + # The context element + # path:: + # The xpath to search for. If not supplied or nil, returns the first + # node matching '*'. + # namespaces:: + # If supplied, a Hash which defines a namespace mapping. + # + # XPath.first( node ) + # XPath.first( doc, "//b"} ) + # XPath.first( node, "a/x:b", { "x"=>"http://doofus" } ) + def XPath::first element, path=nil, namespaces=nil, variables={} + raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash) + raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash) + parser = XPathParser.new + parser.namespaces = namespaces + parser.variables = variables + path = "*" unless path + element = [element] unless element.kind_of? Array + parser.parse(path, element).flatten[0] + end + + # Itterates over nodes that match the given path, calling the supplied + # block with the match. + # element:: + # The context element + # path:: + # The xpath to search for. If not supplied or nil, defaults to '*' + # namespaces:: + # If supplied, a Hash which defines a namespace mapping + # + # XPath.each( node ) { |el| ... } + # XPath.each( node, '/*[@attr='v']' ) { |el| ... } + # XPath.each( node, 'ancestor::x' ) { |el| ... } + def XPath::each element, path=nil, namespaces=nil, variables={}, &block + raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash) + raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash) + parser = XPathParser.new + parser.namespaces = namespaces + parser.variables = variables + path = "*" unless path + element = [element] unless element.kind_of? Array + parser.parse(path, element).each( &block ) + end + + # Returns an array of nodes matching a given XPath. + def XPath::match element, path=nil, namespaces=nil, variables={} + parser = XPathParser.new + parser.namespaces = namespaces + parser.variables = variables + path = "*" unless path + element = [element] unless element.kind_of? Array + parser.parse(path,element) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/xpath_parser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/xpath_parser.rb new file mode 100644 index 0000000000..eb608fdb34 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rexml/xpath_parser.rb @@ -0,0 +1,792 @@ +require 'rexml/namespace' +require 'rexml/xmltokens' +require 'rexml/attribute' +require 'rexml/syncenumerator' +require 'rexml/parsers/xpathparser' + +class Object + def dclone + clone + end +end +class Symbol + def dclone ; self ; end +end +class Fixnum + def dclone ; self ; end +end +class Float + def dclone ; self ; end +end +class Array + def dclone + klone = self.clone + klone.clear + self.each{|v| klone << v.dclone} + klone + end +end + +module REXML + # You don't want to use this class. Really. Use XPath, which is a wrapper + # for this class. Believe me. You don't want to poke around in here. + # There is strange, dark magic at work in this code. Beware. Go back! Go + # back while you still can! + class XPathParser + include XMLTokens + LITERAL = /^'([^']*)'|^"([^"]*)"/u + + def initialize( ) + @parser = REXML::Parsers::XPathParser.new + @namespaces = nil + @variables = {} + end + + def namespaces=( namespaces={} ) + Functions::namespace_context = namespaces + @namespaces = namespaces + end + + def variables=( vars={} ) + Functions::variables = vars + @variables = vars + end + + def parse path, nodeset + #puts "#"*40 + path_stack = @parser.parse( path ) + #puts "PARSE: #{path} => #{path_stack.inspect}" + #puts "PARSE: nodeset = #{nodeset.inspect}" + match( path_stack, nodeset ) + end + + def get_first path, nodeset + #puts "#"*40 + path_stack = @parser.parse( path ) + #puts "PARSE: #{path} => #{path_stack.inspect}" + #puts "PARSE: nodeset = #{nodeset.inspect}" + first( path_stack, nodeset ) + end + + def predicate path, nodeset + path_stack = @parser.parse( path ) + expr( path_stack, nodeset ) + end + + def []=( variable_name, value ) + @variables[ variable_name ] = value + end + + + # Performs a depth-first (document order) XPath search, and returns the + # first match. This is the fastest, lightest way to return a single result. + # + # FIXME: This method is incomplete! + def first( path_stack, node ) + #puts "#{depth}) Entering match( #{path.inspect}, #{tree.inspect} )" + return nil if path.size == 0 + + case path[0] + when :document + # do nothing + return first( path[1..-1], node ) + when :child + for c in node.children + #puts "#{depth}) CHILD checking #{name(c)}" + r = first( path[1..-1], c ) + #puts "#{depth}) RETURNING #{r.inspect}" if r + return r if r + end + when :qname + name = path[2] + #puts "#{depth}) QNAME #{name(tree)} == #{name} (path => #{path.size})" + if node.name == name + #puts "#{depth}) RETURNING #{tree.inspect}" if path.size == 3 + return node if path.size == 3 + return first( path[3..-1], node ) + else + return nil + end + when :descendant_or_self + r = first( path[1..-1], node ) + return r if r + for c in node.children + r = first( path, c ) + return r if r + end + when :node + return first( path[1..-1], node ) + when :any + return first( path[1..-1], node ) + end + return nil + end + + + def match( path_stack, nodeset ) + #puts "MATCH: path_stack = #{path_stack.inspect}" + #puts "MATCH: nodeset = #{nodeset.inspect}" + r = expr( path_stack, nodeset ) + #puts "MAIN EXPR => #{r.inspect}" + r + end + + private + + + # Returns a String namespace for a node, given a prefix + # The rules are: + # + # 1. Use the supplied namespace mapping first. + # 2. If no mapping was supplied, use the context node to look up the namespace + def get_namespace( node, prefix ) + if @namespaces + return @namespaces[prefix] || '' + else + return node.namespace( prefix ) if node.node_type == :element + return '' + end + end + + + # Expr takes a stack of path elements and a set of nodes (either a Parent + # or an Array and returns an Array of matching nodes + ALL = [ :attribute, :element, :text, :processing_instruction, :comment ] + ELEMENTS = [ :element ] + def expr( path_stack, nodeset, context=nil ) + #puts "#"*15 + #puts "In expr with #{path_stack.inspect}" + #puts "Returning" if path_stack.length == 0 || nodeset.length == 0 + node_types = ELEMENTS + return nodeset if path_stack.length == 0 || nodeset.length == 0 + while path_stack.length > 0 + #puts "#"*5 + #puts "Path stack = #{path_stack.inspect}" + #puts "Nodeset is #{nodeset.inspect}" + if nodeset.length == 0 + path_stack.clear + return [] + end + case (op = path_stack.shift) + when :document + nodeset = [ nodeset[0].root_node ] + #puts ":document, nodeset = #{nodeset.inspect}" + + when :qname + #puts "IN QNAME" + prefix = path_stack.shift + name = path_stack.shift + nodeset.delete_if do |node| + # FIXME: This DOUBLES the time XPath searches take + ns = get_namespace( node, prefix ) + #puts "NS = #{ns.inspect}" + #puts "node.node_type == :element => #{node.node_type == :element}" + if node.node_type == :element + #puts "node.name == #{name} => #{node.name == name}" + if node.name == name + #puts "node.namespace == #{ns.inspect} => #{node.namespace == ns}" + end + end + !(node.node_type == :element and + node.name == name and + node.namespace == ns ) + end + node_types = ELEMENTS + + when :any + #puts "ANY 1: nodeset = #{nodeset.inspect}" + #puts "ANY 1: node_types = #{node_types.inspect}" + nodeset.delete_if { |node| !node_types.include?(node.node_type) } + #puts "ANY 2: nodeset = #{nodeset.inspect}" + + when :self + # This space left intentionally blank + + when :processing_instruction + target = path_stack.shift + nodeset.delete_if do |node| + (node.node_type != :processing_instruction) or + ( target!='' and ( node.target != target ) ) + end + + when :text + nodeset.delete_if { |node| node.node_type != :text } + + when :comment + nodeset.delete_if { |node| node.node_type != :comment } + + when :node + # This space left intentionally blank + node_types = ALL + + when :child + new_nodeset = [] + nt = nil + for node in nodeset + nt = node.node_type + new_nodeset += node.children if nt == :element or nt == :document + end + nodeset = new_nodeset + node_types = ELEMENTS + + when :literal + return path_stack.shift + + when :attribute + new_nodeset = [] + case path_stack.shift + when :qname + prefix = path_stack.shift + name = path_stack.shift + for element in nodeset + if element.node_type == :element + #puts "Element name = #{element.name}" + #puts "get_namespace( #{element.inspect}, #{prefix} ) = #{get_namespace(element, prefix)}" + attrib = element.attribute( name, get_namespace(element, prefix) ) + #puts "attrib = #{attrib.inspect}" + new_nodeset << attrib if attrib + end + end + when :any + #puts "ANY" + for element in nodeset + if element.node_type == :element + new_nodeset += element.attributes.to_a + end + end + end + nodeset = new_nodeset + + when :parent + #puts "PARENT 1: nodeset = #{nodeset}" + nodeset = nodeset.collect{|n| n.parent}.compact + #nodeset = expr(path_stack.dclone, nodeset.collect{|n| n.parent}.compact) + #puts "PARENT 2: nodeset = #{nodeset.inspect}" + node_types = ELEMENTS + + when :ancestor + new_nodeset = [] + for node in nodeset + while node.parent + node = node.parent + new_nodeset << node unless new_nodeset.include? node + end + end + nodeset = new_nodeset + node_types = ELEMENTS + + when :ancestor_or_self + new_nodeset = [] + for node in nodeset + if node.node_type == :element + new_nodeset << node + while ( node.parent ) + node = node.parent + new_nodeset << node unless new_nodeset.include? node + end + end + end + nodeset = new_nodeset + node_types = ELEMENTS + + when :predicate + new_nodeset = [] + subcontext = { :size => nodeset.size } + pred = path_stack.shift + nodeset.each_with_index { |node, index| + subcontext[ :node ] = node + #puts "PREDICATE SETTING CONTEXT INDEX TO #{index+1}" + subcontext[ :index ] = index+1 + pc = pred.dclone + #puts "#{node.hash}) Recursing with #{pred.inspect} and [#{node.inspect}]" + result = expr( pc, [node], subcontext ) + result = result[0] if result.kind_of? Array and result.length == 1 + #puts "#{node.hash}) Result = #{result.inspect} (#{result.class.name})" + if result.kind_of? Numeric + #puts "Adding node #{node.inspect}" if result == (index+1) + new_nodeset << node if result == (index+1) + elsif result.instance_of? Array + if result.size > 0 and result.inject(false) {|k,s| s or k} + #puts "Adding node #{node.inspect}" if result.size > 0 + new_nodeset << node if result.size > 0 + end + else + #puts "Adding node #{node.inspect}" if result + new_nodeset << node if result + end + } + #puts "New nodeset = #{new_nodeset.inspect}" + #puts "Path_stack = #{path_stack.inspect}" + nodeset = new_nodeset +=begin + predicate = path_stack.shift + ns = nodeset.clone + result = expr( predicate, ns ) + #puts "Result = #{result.inspect} (#{result.class.name})" + #puts "nodeset = #{nodeset.inspect}" + if result.kind_of? Array + nodeset = result.zip(ns).collect{|m,n| n if m}.compact + else + nodeset = result ? nodeset : [] + end + #puts "Outgoing NS = #{nodeset.inspect}" +=end + + when :descendant_or_self + rv = descendant_or_self( path_stack, nodeset ) + path_stack.clear + nodeset = rv + node_types = ELEMENTS + + when :descendant + results = [] + nt = nil + for node in nodeset + nt = node.node_type + results += expr( path_stack.dclone.unshift( :descendant_or_self ), + node.children ) if nt == :element or nt == :document + end + nodeset = results + node_types = ELEMENTS + + when :following_sibling + #puts "FOLLOWING_SIBLING 1: nodeset = #{nodeset}" + results = [] + nodeset.each do |node| + next if node.parent.nil? + all_siblings = node.parent.children + current_index = all_siblings.index( node ) + following_siblings = all_siblings[ current_index+1 .. -1 ] + results += expr( path_stack.dclone, following_siblings ) + end + #puts "FOLLOWING_SIBLING 2: nodeset = #{nodeset}" + nodeset = results + + when :preceding_sibling + results = [] + nodeset.each do |node| + next if node.parent.nil? + all_siblings = node.parent.children + current_index = all_siblings.index( node ) + preceding_siblings = all_siblings[ 0, current_index ].reverse + results += preceding_siblings + end + nodeset = results + node_types = ELEMENTS + + when :preceding + new_nodeset = [] + for node in nodeset + new_nodeset += preceding( node ) + end + #puts "NEW NODESET => #{new_nodeset.inspect}" + nodeset = new_nodeset + node_types = ELEMENTS + + when :following + new_nodeset = [] + for node in nodeset + new_nodeset += following( node ) + end + nodeset = new_nodeset + node_types = ELEMENTS + + when :namespace + #puts "In :namespace" + new_nodeset = [] + prefix = path_stack.shift + for node in nodeset + if (node.node_type == :element or node.node_type == :attribute) + if @namespaces + namespaces = @namespaces + elsif (node.node_type == :element) + namespaces = node.namespaces + else + namespaces = node.element.namesapces + end + #puts "Namespaces = #{namespaces.inspect}" + #puts "Prefix = #{prefix.inspect}" + #puts "Node.namespace = #{node.namespace}" + if (node.namespace == namespaces[prefix]) + new_nodeset << node + end + end + end + nodeset = new_nodeset + + when :variable + var_name = path_stack.shift + return @variables[ var_name ] + + # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq + # TODO: Special case for :or and :and -- not evaluate the right + # operand if the left alone determines result (i.e. is true for + # :or and false for :and). + when :eq, :neq, :lt, :lteq, :gt, :gteq, :and, :or + left = expr( path_stack.shift, nodeset.dup, context ) + #puts "LEFT => #{left.inspect} (#{left.class.name})" + right = expr( path_stack.shift, nodeset.dup, context ) + #puts "RIGHT => #{right.inspect} (#{right.class.name})" + res = equality_relational_compare( left, op, right ) + #puts "RES => #{res.inspect}" + return res + + when :and + left = expr( path_stack.shift, nodeset.dup, context ) + #puts "LEFT => #{left.inspect} (#{left.class.name})" + if left == false || left.nil? || !left.inject(false) {|a,b| a | b} + return [] + end + right = expr( path_stack.shift, nodeset.dup, context ) + #puts "RIGHT => #{right.inspect} (#{right.class.name})" + res = equality_relational_compare( left, op, right ) + #puts "RES => #{res.inspect}" + return res + + when :div + left = Functions::number(expr(path_stack.shift, nodeset, context)).to_f + right = Functions::number(expr(path_stack.shift, nodeset, context)).to_f + return (left / right) + + when :mod + left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f + right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f + return (left % right) + + when :mult + left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f + right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f + return (left * right) + + when :plus + left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f + right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f + return (left + right) + + when :minus + left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f + right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f + return (left - right) + + when :union + left = expr( path_stack.shift, nodeset, context ) + right = expr( path_stack.shift, nodeset, context ) + return (left | right) + + when :neg + res = expr( path_stack, nodeset, context ) + return -(res.to_f) + + when :not + when :function + func_name = path_stack.shift.tr('-','_') + arguments = path_stack.shift + #puts "FUNCTION 0: #{func_name}(#{arguments.collect{|a|a.inspect}.join(', ')})" + subcontext = context ? nil : { :size => nodeset.size } + + res = [] + cont = context + nodeset.each_with_index { |n, i| + if subcontext + subcontext[:node] = n + subcontext[:index] = i + cont = subcontext + end + arg_clone = arguments.dclone + args = arg_clone.collect { |arg| + #puts "FUNCTION 1: Calling expr( #{arg.inspect}, [#{n.inspect}] )" + expr( arg, [n], cont ) + } + #puts "FUNCTION 2: #{func_name}(#{args.collect{|a|a.inspect}.join(', ')})" + Functions.context = cont + res << Functions.send( func_name, *args ) + #puts "FUNCTION 3: #{res[-1].inspect}" + } + return res + + end + end # while + #puts "EXPR returning #{nodeset.inspect}" + return nodeset + end + + + ########################################################## + # FIXME + # The next two methods are BAD MOJO! + # This is my achilles heel. If anybody thinks of a better + # way of doing this, be my guest. This really sucks, but + # it is a wonder it works at all. + # ######################################################## + + def descendant_or_self( path_stack, nodeset ) + rs = [] + #puts "#"*80 + #puts "PATH_STACK = #{path_stack.inspect}" + #puts "NODESET = #{nodeset.collect{|n|n.inspect}.inspect}" + d_o_s( path_stack, nodeset, rs ) + #puts "RS = #{rs.collect{|n|n.inspect}.inspect}" + document_order(rs.flatten.compact) + #rs.flatten.compact + end + + def d_o_s( p, ns, r ) + #puts "IN DOS with #{ns.inspect}; ALREADY HAVE #{r.inspect}" + nt = nil + ns.each_index do |i| + n = ns[i] + #puts "P => #{p.inspect}" + x = expr( p.dclone, [ n ] ) + nt = n.node_type + d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0 + r.concat(x) if x.size > 0 + end + end + + + # Reorders an array of nodes so that they are in document order + # It tries to do this efficiently. + # + # FIXME: I need to get rid of this, but the issue is that most of the XPath + # interpreter functions as a filter, which means that we lose context going + # in and out of function calls. If I knew what the index of the nodes was, + # I wouldn't have to do this. Maybe add a document IDX for each node? + # Problems with mutable documents. Or, rewrite everything. + def document_order( array_of_nodes ) + new_arry = [] + array_of_nodes.each { |node| + node_idx = [] + np = node.node_type == :attribute ? node.element : node + while np.parent and np.parent.node_type == :element + node_idx << np.parent.index( np ) + np = np.parent + end + new_arry << [ node_idx.reverse, node ] + } + #puts "new_arry = #{new_arry.inspect}" + new_arry.sort{ |s1, s2| s1[0] <=> s2[0] }.collect{ |s| s[1] } + end + + + def recurse( nodeset, &block ) + for node in nodeset + yield node + recurse( node, &block ) if node.node_type == :element + end + end + + + + # Builds a nodeset of all of the preceding nodes of the supplied node, + # in reverse document order + # preceding:: includes every element in the document that precedes this node, + # except for ancestors + def preceding( node ) + #puts "IN PRECEDING" + ancestors = [] + p = node.parent + while p + ancestors << p + p = p.parent + end + + acc = [] + p = preceding_node_of( node ) + #puts "P = #{p.inspect}" + while p + if ancestors.include? p + ancestors.delete(p) + else + acc << p + end + p = preceding_node_of( p ) + #puts "P = #{p.inspect}" + end + acc + end + + def preceding_node_of( node ) + #puts "NODE: #{node.inspect}" + #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}" + #puts "PARENT NODE: #{node.parent}" + psn = node.previous_sibling_node + if psn.nil? + if node.parent.nil? or node.parent.class == Document + return nil + end + return node.parent + #psn = preceding_node_of( node.parent ) + end + while psn and psn.kind_of? Element and psn.children.size > 0 + psn = psn.children[-1] + end + psn + end + + def following( node ) + #puts "IN PRECEDING" + acc = [] + p = next_sibling_node( node ) + #puts "P = #{p.inspect}" + while p + acc << p + p = following_node_of( p ) + #puts "P = #{p.inspect}" + end + acc + end + + def following_node_of( node ) + #puts "NODE: #{node.inspect}" + #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}" + #puts "PARENT NODE: #{node.parent}" + if node.kind_of? Element and node.children.size > 0 + return node.children[0] + end + return next_sibling_node(node) + end + + def next_sibling_node(node) + psn = node.next_sibling_node + while psn.nil? + if node.parent.nil? or node.parent.class == Document + return nil + end + node = node.parent + psn = node.next_sibling_node + #puts "psn = #{psn.inspect}" + end + return psn + end + + def norm b + case b + when true, false + return b + when 'true', 'false' + return Functions::boolean( b ) + when /^\d+(\.\d+)?$/ + return Functions::number( b ) + else + return Functions::string( b ) + end + end + + def equality_relational_compare( set1, op, set2 ) + #puts "EQ_REL_COMP(#{set1.inspect} #{op.inspect} #{set2.inspect})" + if set1.kind_of? Array and set2.kind_of? Array + #puts "#{set1.size} & #{set2.size}" + if set1.size == 1 and set2.size == 1 + set1 = set1[0] + set2 = set2[0] + elsif set1.size == 0 or set2.size == 0 + nd = set1.size==0 ? set2 : set1 + rv = nd.collect { |il| compare( il, op, nil ) } + #puts "RV = #{rv.inspect}" + return rv + else + res = [] + enum = SyncEnumerator.new( set1, set2 ).each { |i1, i2| + #puts "i1 = #{i1.inspect} (#{i1.class.name})" + #puts "i2 = #{i2.inspect} (#{i2.class.name})" + i1 = norm( i1 ) + i2 = norm( i2 ) + res << compare( i1, op, i2 ) + } + return res + end + end + #puts "EQ_REL_COMP: #{set1.inspect} (#{set1.class.name}), #{op}, #{set2.inspect} (#{set2.class.name})" + #puts "COMPARING VALUES" + # If one is nodeset and other is number, compare number to each item + # in nodeset s.t. number op number(string(item)) + # If one is nodeset and other is string, compare string to each item + # in nodeset s.t. string op string(item) + # If one is nodeset and other is boolean, compare boolean to each item + # in nodeset s.t. boolean op boolean(item) + if set1.kind_of? Array or set2.kind_of? Array + #puts "ISA ARRAY" + if set1.kind_of? Array + a = set1 + b = set2 + else + a = set2 + b = set1 + end + + case b + when true, false + return a.collect {|v| compare( Functions::boolean(v), op, b ) } + when Numeric + return a.collect {|v| compare( Functions::number(v), op, b )} + when /^\d+(\.\d+)?$/ + b = Functions::number( b ) + #puts "B = #{b.inspect}" + return a.collect {|v| compare( Functions::number(v), op, b )} + else + #puts "Functions::string( #{b}(#{b.class.name}) ) = #{Functions::string(b)}" + b = Functions::string( b ) + return a.collect { |v| compare( Functions::string(v), op, b ) } + end + else + # If neither is nodeset, + # If op is = or != + # If either boolean, convert to boolean + # If either number, convert to number + # Else, convert to string + # Else + # Convert both to numbers and compare + s1 = set1.to_s + s2 = set2.to_s + #puts "EQ_REL_COMP: #{set1}=>#{s1}, #{set2}=>#{s2}" + if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false' + #puts "Functions::boolean(#{set1})=>#{Functions::boolean(set1)}" + #puts "Functions::boolean(#{set2})=>#{Functions::boolean(set2)}" + set1 = Functions::boolean( set1 ) + set2 = Functions::boolean( set2 ) + else + if op == :eq or op == :neq + if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/ + set1 = Functions::number( s1 ) + set2 = Functions::number( s2 ) + else + set1 = Functions::string( set1 ) + set2 = Functions::string( set2 ) + end + else + set1 = Functions::number( set1 ) + set2 = Functions::number( set2 ) + end + end + #puts "EQ_REL_COMP: #{set1} #{op} #{set2}" + #puts ">>> #{compare( set1, op, set2 )}" + return compare( set1, op, set2 ) + end + return false + end + + def compare a, op, b + #puts "COMPARE #{a.inspect}(#{a.class.name}) #{op} #{b.inspect}(#{b.class.name})" + case op + when :eq + a == b + when :neq + a != b + when :lt + a < b + when :lteq + a <= b + when :gt + a > b + when :gteq + a >= b + when :and + a and b + when :or + a or b + else + false + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rinda/rinda.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rinda/rinda.rb new file mode 100644 index 0000000000..6c59e68654 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rinda/rinda.rb @@ -0,0 +1,283 @@ +require 'drb/drb' +require 'thread' + +## +# A module to implement the Linda distributed computing paradigm in Ruby. +# +# Rinda is part of DRb (dRuby). +# +# == Example(s) +# +# See the sample/drb/ directory in the Ruby distribution, from 1.8.2 onwards. +# +#-- +# TODO +# == Introduction to Linda/rinda? +# +# == Why is this library separate from DRb? + +module Rinda + + ## + # Rinda error base class + + class RindaError < RuntimeError; end + + ## + # Raised when a hash-based tuple has an invalid key. + + class InvalidHashTupleKey < RindaError; end + + ## + # Raised when trying to use a canceled tuple. + + class RequestCanceledError < ThreadError; end + + ## + # Raised when trying to use an expired tuple. + + class RequestExpiredError < ThreadError; end + + ## + # A tuple is the elementary object in Rinda programming. + # Tuples may be matched against templates if the tuple and + # the template are the same size. + + class Tuple + + ## + # Creates a new Tuple from +ary_or_hash+ which must be an Array or Hash. + + def initialize(ary_or_hash) + if hash?(ary_or_hash) + init_with_hash(ary_or_hash) + else + init_with_ary(ary_or_hash) + end + end + + ## + # The number of elements in the tuple. + + def size + @tuple.size + end + + ## + # Accessor method for elements of the tuple. + + def [](k) + @tuple[k] + end + + ## + # Fetches item +k+ from the tuple. + + def fetch(k) + @tuple.fetch(k) + end + + ## + # Iterate through the tuple, yielding the index or key, and the + # value, thus ensuring arrays are iterated similarly to hashes. + + def each # FIXME + if Hash === @tuple + @tuple.each { |k, v| yield(k, v) } + else + @tuple.each_with_index { |v, k| yield(k, v) } + end + end + + ## + # Return the tuple itself + def value + @tuple + end + + private + + def hash?(ary_or_hash) + ary_or_hash.respond_to?(:keys) + end + + ## + # Munges +ary+ into a valid Tuple. + + def init_with_ary(ary) + @tuple = Array.new(ary.size) + @tuple.size.times do |i| + @tuple[i] = ary[i] + end + end + + ## + # Ensures +hash+ is a valid Tuple. + + def init_with_hash(hash) + @tuple = Hash.new + hash.each do |k, v| + raise InvalidHashTupleKey unless String === k + @tuple[k] = v + end + end + + end + + ## + # Templates are used to match tuples in Rinda. + + class Template < Tuple + + ## + # Matches this template against +tuple+. The +tuple+ must be the same + # size as the template. An element with a +nil+ value in a template acts + # as a wildcard, matching any value in the corresponding position in the + # tuple. Elements of the template match the +tuple+ if the are #== or + # #===. + # + # Template.new([:foo, 5]).match Tuple.new([:foo, 5]) # => true + # Template.new([:foo, nil]).match Tuple.new([:foo, 5]) # => true + # Template.new([String]).match Tuple.new(['hello']) # => true + # + # Template.new([:foo]).match Tuple.new([:foo, 5]) # => false + # Template.new([:foo, 6]).match Tuple.new([:foo, 5]) # => false + # Template.new([:foo, nil]).match Tuple.new([:foo]) # => false + # Template.new([:foo, 6]).match Tuple.new([:foo]) # => false + + def match(tuple) + return false unless tuple.respond_to?(:size) + return false unless tuple.respond_to?(:fetch) + return false unless self.size == tuple.size + each do |k, v| + begin + it = tuple.fetch(k) + rescue + return false + end + next if v.nil? + next if v == it + next if v === it + return false + end + return true + end + + ## + # Alias for #match. + + def ===(tuple) + match(tuple) + end + + end + + ## + # Documentation? + + class DRbObjectTemplate + + ## + # Creates a new DRbObjectTemplate that will match against +uri+ and +ref+. + + def initialize(uri=nil, ref=nil) + @drb_uri = uri + @drb_ref = ref + end + + ## + # This DRbObjectTemplate matches +ro+ if the remote object's drburi and + # drbref are the same. +nil+ is used as a wildcard. + + def ===(ro) + return true if super(ro) + unless @drb_uri.nil? + return false unless (@drb_uri === ro.__drburi rescue false) + end + unless @drb_ref.nil? + return false unless (@drb_ref === ro.__drbref rescue false) + end + true + end + + end + + ## + # TupleSpaceProxy allows a remote Tuplespace to appear as local. + + class TupleSpaceProxy + + ## + # Creates a new TupleSpaceProxy to wrap +ts+. + + def initialize(ts) + @ts = ts + end + + ## + # Adds +tuple+ to the proxied TupleSpace. See TupleSpace#write. + + def write(tuple, sec=nil) + @ts.write(tuple, sec) + end + + ## + # Takes +tuple+ from the proxied TupleSpace. See TupleSpace#take. + + def take(tuple, sec=nil, &block) + port = [] + @ts.move(DRbObject.new(port), tuple, sec, &block) + port[0] + end + + ## + # Reads +tuple+ from the proxied TupleSpace. See TupleSpace#read. + + def read(tuple, sec=nil, &block) + @ts.read(tuple, sec, &block) + end + + ## + # Reads all tuples matching +tuple+ from the proxied TupleSpace. See + # TupleSpace#read_all. + + def read_all(tuple) + @ts.read_all(tuple) + end + + ## + # Registers for notifications of event +ev+ on the proxied TupleSpace. + # See TupleSpace#notify + + def notify(ev, tuple, sec=nil) + @ts.notify(ev, tuple, sec) + end + + end + + ## + # An SimpleRenewer allows a TupleSpace to check if a TupleEntry is still + # alive. + + class SimpleRenewer + + include DRbUndumped + + ## + # Creates a new SimpleRenewer that keeps an object alive for another +sec+ + # seconds. + + def initialize(sec=180) + @sec = sec + end + + ## + # Called by the TupleSpace to check if the object is still alive. + + def renew + @sec + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rinda/ring.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rinda/ring.rb new file mode 100644 index 0000000000..ac8a716216 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rinda/ring.rb @@ -0,0 +1,271 @@ +# +# Note: Rinda::Ring API is unstable. +# +require 'drb/drb' +require 'rinda/rinda' +require 'thread' + +module Rinda + + ## + # The default port Ring discovery will use. + + Ring_PORT = 7647 + + ## + # A RingServer allows a Rinda::TupleSpace to be located via UDP broadcasts. + # Service location uses the following steps: + # + # 1. A RingServer begins listening on the broadcast UDP address. + # 2. A RingFinger sends a UDP packet containing the DRb URI where it will + # listen for a reply. + # 3. The RingServer recieves the UDP packet and connects back to the + # provided DRb URI with the DRb service. + + class RingServer + + include DRbUndumped + + ## + # Advertises +ts+ on the UDP broadcast address at +port+. + + def initialize(ts, port=Ring_PORT) + @ts = ts + @soc = UDPSocket.open + @soc.bind('', port) + @w_service = write_service + @r_service = reply_service + end + + ## + # Creates a thread that picks up UDP packets and passes them to do_write + # for decoding. + + def write_service + Thread.new do + loop do + msg = @soc.recv(1024) + do_write(msg) + end + end + end + + ## + # Extracts the response URI from +msg+ and adds it to TupleSpace where it + # will be picked up by +reply_service+ for notification. + + def do_write(msg) + Thread.new do + begin + tuple, sec = Marshal.load(msg) + @ts.write(tuple, sec) + rescue + end + end + end + + ## + # Creates a thread that notifies waiting clients from the TupleSpace. + + def reply_service + Thread.new do + loop do + do_reply + end + end + end + + ## + # Pulls lookup tuples out of the TupleSpace and sends their DRb object the + # address of the local TupleSpace. + + def do_reply + tuple = @ts.take([:lookup_ring, nil]) + Thread.new { tuple[1].call(@ts) rescue nil} + rescue + end + + end + + ## + # RingFinger is used by RingServer clients to discover the RingServer's + # TupleSpace. Typically, all a client needs to do is call + # RingFinger.primary to retrieve the remote TupleSpace, which it can then + # begin using. + + class RingFinger + + @@broadcast_list = ['', 'localhost'] + + @@finger = nil + + ## + # Creates a singleton RingFinger and looks for a RingServer. Returns the + # created RingFinger. + + def self.finger + unless @@finger + @@finger = self.new + @@finger.lookup_ring_any + end + @@finger + end + + ## + # Returns the first advertised TupleSpace. + + def self.primary + finger.primary + end + + ## + # Contains all discoverd TupleSpaces except for the primary. + + def self.to_a + finger.to_a + end + + ## + # The list of addresses where RingFinger will send query packets. + + attr_accessor :broadcast_list + + ## + # The port that RingFinger will send query packets to. + + attr_accessor :port + + ## + # Contain the first advertised TupleSpace after lookup_ring_any is called. + + attr_accessor :primary + + ## + # Creates a new RingFinger that will look for RingServers at +port+ on + # the addresses in +broadcast_list+. + + def initialize(broadcast_list=@@broadcast_list, port=Ring_PORT) + @broadcast_list = broadcast_list || ['localhost'] + @port = port + @primary = nil + @rings = [] + end + + ## + # Contains all discovered TupleSpaces except for the primary. + + def to_a + @rings + end + + ## + # Iterates over all discovered TupleSpaces starting with the primary. + + def each + lookup_ring_any unless @primary + return unless @primary + yield(@primary) + @rings.each { |x| yield(x) } + end + + ## + # Looks up RingServers waiting +timeout+ seconds. RingServers will be + # given +block+ as a callback, which will be called with the remote + # TupleSpace. + + def lookup_ring(timeout=5, &block) + return lookup_ring_any(timeout) unless block_given? + + msg = Marshal.dump([[:lookup_ring, DRbObject.new(block)], timeout]) + @broadcast_list.each do |it| + soc = UDPSocket.open + begin + soc.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true) + soc.send(msg, 0, it, @port) + rescue + nil + ensure + soc.close + end + end + sleep(timeout) + end + + ## + # Returns the first found remote TupleSpace. Any further recovered + # TupleSpaces can be found by calling +to_a+. + + def lookup_ring_any(timeout=5) + queue = Queue.new + + th = Thread.new do + self.lookup_ring(timeout) do |ts| + queue.push(ts) + end + queue.push(nil) + while it = queue.pop + @rings.push(it) + end + end + + @primary = queue.pop + raise('RingNotFound') if @primary.nil? + @primary + end + + end + + ## + # RingProvider uses a RingServer advertised TupleSpace as a name service. + # TupleSpace clients can register themselves with the remote TupleSpace and + # look up other provided services via the remote TupleSpace. + # + # Services are registered with a tuple of the format [:name, klass, + # DRbObject, description]. + + class RingProvider + + ## + # Creates a RingProvider that will provide a +klass+ service running on + # +front+, with a +description+. +renewer+ is optional. + + def initialize(klass, front, desc, renewer = nil) + @tuple = [:name, klass, front, desc] + @renewer = renewer || Rinda::SimpleRenewer.new + end + + ## + # Advertises this service on the primary remote TupleSpace. + + def provide + ts = Rinda::RingFinger.primary + ts.write(@tuple, @renewer) + end + + end + +end + +if __FILE__ == $0 + DRb.start_service + case ARGV.shift + when 's' + require 'rinda/tuplespace' + ts = Rinda::TupleSpace.new + place = Rinda::RingServer.new(ts) + $stdin.gets + when 'w' + finger = Rinda::RingFinger.new(nil) + finger.lookup_ring do |ts| + p ts + ts.write([:hello, :world]) + end + when 'r' + finger = Rinda::RingFinger.new(nil) + finger.lookup_ring do |ts| + p ts + p ts.take([nil, nil]) + end + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rinda/tuplespace.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rinda/tuplespace.rb new file mode 100644 index 0000000000..6d58a0fd15 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rinda/tuplespace.rb @@ -0,0 +1,592 @@ +require 'monitor' +require 'thread' +require 'drb/drb' +require 'rinda/rinda' + +module Rinda + + ## + # A TupleEntry is a Tuple (i.e. a possible entry in some Tuplespace) + # together with expiry and cancellation data. + + class TupleEntry + + include DRbUndumped + + attr_accessor :expires + + ## + # Creates a TupleEntry based on +ary+ with an optional renewer or expiry + # time +sec+. + # + # A renewer must implement the +renew+ method which returns a Numeric, + # nil, or true to indicate when the tuple has expired. + + def initialize(ary, sec=nil) + @cancel = false + @expires = nil + @tuple = make_tuple(ary) + @renewer = nil + renew(sec) + end + + ## + # Marks this TupleEntry as canceled. + + def cancel + @cancel = true + end + + ## + # A TupleEntry is dead when it is canceled or expired. + + def alive? + !canceled? && !expired? + end + + ## + # Return the object which makes up the tuple itself: the Array + # or Hash. + + def value; @tuple.value; end + + ## + # Returns the canceled status. + + def canceled?; @cancel; end + + ## + # Has this tuple expired? (true/false). + # + # A tuple has expired when its expiry timer based on the +sec+ argument to + # #initialize runs out. + + def expired? + return true unless @expires + return false if @expires > Time.now + return true if @renewer.nil? + renew(@renewer) + return true unless @expires + return @expires < Time.now + end + + ## + # Reset the expiry time according to +sec_or_renewer+. + # + # +nil+:: it is set to expire in the far future. + # +false+:: it has expired. + # Numeric:: it will expire in that many seconds. + # + # Otherwise the argument refers to some kind of renewer object + # which will reset its expiry time. + + def renew(sec_or_renewer) + sec, @renewer = get_renewer(sec_or_renewer) + @expires = make_expires(sec) + end + + ## + # Returns an expiry Time based on +sec+ which can be one of: + # Numeric:: +sec+ seconds into the future + # +true+:: the expiry time is the start of 1970 (i.e. expired) + # +nil+:: it is Tue Jan 19 03:14:07 GMT Standard Time 2038 (i.e. when + # UNIX clocks will die) + + def make_expires(sec=nil) + case sec + when Numeric + Time.now + sec + when true + Time.at(1) + when nil + Time.at(2**31-1) + end + end + + ## + # Retrieves +key+ from the tuple. + + def [](key) + @tuple[key] + end + + ## + # Fetches +key+ from the tuple. + + def fetch(key) + @tuple.fetch(key) + end + + ## + # The size of the tuple. + + def size + @tuple.size + end + + ## + # Creates a Rinda::Tuple for +ary+. + + def make_tuple(ary) + Rinda::Tuple.new(ary) + end + + private + + ## + # Returns a valid argument to make_expires and the renewer or nil. + # + # Given +true+, +nil+, or Numeric, returns that value and +nil+ (no actual + # renewer). Otherwise it returns an expiry value from calling +it.renew+ + # and the renewer. + + def get_renewer(it) + case it + when Numeric, true, nil + return it, nil + else + begin + return it.renew, it + rescue Exception + return it, nil + end + end + end + + end + + ## + # A TemplateEntry is a Template together with expiry and cancellation data. + + class TemplateEntry < TupleEntry + ## + # Matches this TemplateEntry against +tuple+. See Template#match for + # details on how a Template matches a Tuple. + + def match(tuple) + @tuple.match(tuple) + end + + alias === match + + def make_tuple(ary) # :nodoc: + Rinda::Template.new(ary) + end + + end + + ## + # Documentation? + + class WaitTemplateEntry < TemplateEntry + + attr_reader :found + + def initialize(place, ary, expires=nil) + super(ary, expires) + @place = place + @cond = place.new_cond + @found = nil + end + + def cancel + super + signal + end + + def wait + @cond.wait + end + + def read(tuple) + @found = tuple + signal + end + + def signal + @place.synchronize do + @cond.signal + end + end + + end + + ## + # A NotifyTemplateEntry is returned by TupleSpace#notify and is notified of + # TupleSpace changes. You may receive either your subscribed event or the + # 'close' event when iterating over notifications. + # + # See TupleSpace#notify_event for valid notification types. + # + # == Example + # + # ts = Rinda::TupleSpace.new + # observer = ts.notify 'write', [nil] + # + # Thread.start do + # observer.each { |t| p t } + # end + # + # 3.times { |i| ts.write [i] } + # + # Outputs: + # + # ['write', [0]] + # ['write', [1]] + # ['write', [2]] + + class NotifyTemplateEntry < TemplateEntry + + ## + # Creates a new NotifyTemplateEntry that watches +place+ for +event+s that + # match +tuple+. + + def initialize(place, event, tuple, expires=nil) + ary = [event, Rinda::Template.new(tuple)] + super(ary, expires) + @queue = Queue.new + @done = false + end + + ## + # Called by TupleSpace to notify this NotifyTemplateEntry of a new event. + + def notify(ev) + @queue.push(ev) + end + + ## + # Retrieves a notification. Raises RequestExpiredError when this + # NotifyTemplateEntry expires. + + def pop + raise RequestExpiredError if @done + it = @queue.pop + @done = true if it[0] == 'close' + return it + end + + ## + # Yields event/tuple pairs until this NotifyTemplateEntry expires. + + def each # :yields: event, tuple + while !@done + it = pop + yield(it) + end + rescue + ensure + cancel + end + + end + + ## + # TupleBag is an unordered collection of tuples. It is the basis + # of Tuplespace. + + class TupleBag + + def initialize # :nodoc: + @hash = {} + end + + ## + # +true+ if the TupleBag to see if it has any expired entries. + + def has_expires? + @hash.each do |k, v| + v.each do |tuple| + return true if tuple.expires + end + end + false + end + + ## + # Add +ary+ to the TupleBag. + + def push(ary) + size = ary.size + @hash[size] ||= [] + @hash[size].push(ary) + end + + ## + # Removes +ary+ from the TupleBag. + + def delete(ary) + size = ary.size + @hash.fetch(size, []).delete(ary) + end + + ## + # Finds all live tuples that match +template+. + + def find_all(template) + @hash.fetch(template.size, []).find_all do |tuple| + tuple.alive? && template.match(tuple) + end + end + + ## + # Finds a live tuple that matches +template+. + + def find(template) + @hash.fetch(template.size, []).find do |tuple| + tuple.alive? && template.match(tuple) + end + end + + ## + # Finds all tuples in the TupleBag which when treated as templates, match + # +tuple+ and are alive. + + def find_all_template(tuple) + @hash.fetch(tuple.size, []).find_all do |template| + template.alive? && template.match(tuple) + end + end + + ## + # Delete tuples which dead tuples from the TupleBag, returning the deleted + # tuples. + + def delete_unless_alive + deleted = [] + @hash.keys.each do |size| + ary = [] + @hash[size].each do |tuple| + if tuple.alive? + ary.push(tuple) + else + deleted.push(tuple) + end + end + @hash[size] = ary + end + deleted + end + + end + + ## + # The Tuplespace manages access to the tuples it contains, + # ensuring mutual exclusion requirements are met. + # + # The +sec+ option for the write, take, move, read and notify methods may + # either be a number of seconds or a Renewer object. + + class TupleSpace + + include DRbUndumped + include MonitorMixin + + ## + # Creates a new TupleSpace. +period+ is used to control how often to look + # for dead tuples after modifications to the TupleSpace. + # + # If no dead tuples are found +period+ seconds after the last + # modification, the TupleSpace will stop looking for dead tuples. + + def initialize(period=60) + super() + @bag = TupleBag.new + @read_waiter = TupleBag.new + @take_waiter = TupleBag.new + @notify_waiter = TupleBag.new + @period = period + @keeper = nil + end + + ## + # Adds +tuple+ + + def write(tuple, sec=nil) + entry = TupleEntry.new(tuple, sec) + synchronize do + if entry.expired? + @read_waiter.find_all_template(entry).each do |template| + template.read(tuple) + end + notify_event('write', entry.value) + notify_event('delete', entry.value) + else + @bag.push(entry) + start_keeper if entry.expires + @read_waiter.find_all_template(entry).each do |template| + template.read(tuple) + end + @take_waiter.find_all_template(entry).each do |template| + template.signal + end + notify_event('write', entry.value) + end + end + entry + end + + ## + # Removes +tuple+ + + def take(tuple, sec=nil, &block) + move(nil, tuple, sec, &block) + end + + ## + # Moves +tuple+ to +port+. + + def move(port, tuple, sec=nil) + template = WaitTemplateEntry.new(self, tuple, sec) + yield(template) if block_given? + synchronize do + entry = @bag.find(template) + if entry + port.push(entry.value) if port + @bag.delete(entry) + notify_event('take', entry.value) + return entry.value + end + raise RequestExpiredError if template.expired? + + begin + @take_waiter.push(template) + start_keeper if template.expires + while true + raise RequestCanceledError if template.canceled? + raise RequestExpiredError if template.expired? + entry = @bag.find(template) + if entry + port.push(entry.value) if port + @bag.delete(entry) + notify_event('take', entry.value) + return entry.value + end + template.wait + end + ensure + @take_waiter.delete(template) + end + end + end + + ## + # Reads +tuple+, but does not remove it. + + def read(tuple, sec=nil) + template = WaitTemplateEntry.new(self, tuple, sec) + yield(template) if block_given? + synchronize do + entry = @bag.find(template) + return entry.value if entry + raise RequestExpiredError if template.expired? + + begin + @read_waiter.push(template) + start_keeper if template.expires + template.wait + raise RequestCanceledError if template.canceled? + raise RequestExpiredError if template.expired? + return template.found + ensure + @read_waiter.delete(template) + end + end + end + + ## + # Returns all tuples matching +tuple+. Does not remove the found tuples. + + def read_all(tuple) + template = WaitTemplateEntry.new(self, tuple, nil) + synchronize do + entry = @bag.find_all(template) + entry.collect do |e| + e.value + end + end + end + + ## + # Registers for notifications of +event+. Returns a NotifyTemplateEntry. + # See NotifyTemplateEntry for examples of how to listen for notifications. + # + # +event+ can be: + # 'write':: A tuple was added + # 'take':: A tuple was taken or moved + # 'delete':: A tuple was lost after being overwritten or expiring + # + # The TupleSpace will also notify you of the 'close' event when the + # NotifyTemplateEntry has expired. + + def notify(event, tuple, sec=nil) + template = NotifyTemplateEntry.new(self, event, tuple, sec) + synchronize do + @notify_waiter.push(template) + end + template + end + + private + + ## + # Removes dead tuples. + + def keep_clean + synchronize do + @read_waiter.delete_unless_alive.each do |e| + e.signal + end + @take_waiter.delete_unless_alive.each do |e| + e.signal + end + @notify_waiter.delete_unless_alive.each do |e| + e.notify(['close']) + end + @bag.delete_unless_alive.each do |e| + notify_event('delete', e.value) + end + end + end + + ## + # Notifies all registered listeners for +event+ of a status change of + # +tuple+. + + def notify_event(event, tuple) + ev = [event, tuple] + @notify_waiter.find_all_template(ev).each do |template| + template.notify(ev) + end + end + + ## + # Creates a thread that scans the tuplespace for expired tuples. + + def start_keeper + return if @keeper && @keeper.alive? + @keeper = Thread.new do + while true + synchronize do + break unless need_keeper? + keep_clean + end + sleep(@period) + end + end + end + + ## + # Checks the tuplespace to see if it needs cleaning. + + def need_keeper? + return true if @bag.has_expires? + return true if @read_waiter.has_expires? + return true if @take_waiter.has_expires? + return true if @notify_waiter.has_expires? + end + + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss.rb new file mode 100644 index 0000000000..495edb1b98 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss.rb @@ -0,0 +1,16 @@ +# Copyright (c) 2003-2005 Kouhei Sutou. You can redistribute it and/or +# modify it under the same terms as Ruby. +# +# Author:: Kouhei Sutou +# Tutorial:: http://www.cozmixng.org/~rwiki/?cmd=view;name=RSS+Parser%3A%3ATutorial.en + +require 'rss/1.0' +require 'rss/2.0' +require 'rss/content' +require 'rss/dublincore' +require 'rss/image' +require 'rss/syndication' +#require 'rss/taxonomy' +require 'rss/trackback' + +require "rss/maker" diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/0.9.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/0.9.rb new file mode 100644 index 0000000000..69e01ddd57 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/0.9.rb @@ -0,0 +1,422 @@ +require "rss/parser" + +module RSS + + module RSS09 + NSPOOL = {} + ELEMENTS = [] + + def self.append_features(klass) + super + + klass.install_must_call_validator('', "") + end + end + + class Rss < Element + + include RSS09 + include RootElementMixin + + %w(channel).each do |name| + install_have_child_element(name, "", nil) + end + + attr_accessor :rss_version, :version, :encoding, :standalone + + def initialize(rss_version, version=nil, encoding=nil, standalone=nil) + super + end + + def items + if @channel + @channel.items + else + [] + end + end + + def image + if @channel + @channel.image + else + nil + end + end + + def textinput + if @channel + @channel.textInput + else + nil + end + end + + def setup_maker_elements(maker) + super + items.each do |item| + item.setup_maker(maker.items) + end + end + + private + def _attrs + [ + ["version", true, "rss_version"], + ] + end + + class Channel < Element + + include RSS09 + + [ + ["title", nil, :text], + ["link", nil, :text], + ["description", nil, :text], + ["language", nil, :text], + ["copyright", "?", :text], + ["managingEditor", "?", :text], + ["webMaster", "?", :text], + ["rating", "?", :text], + ["pubDate", "?", :date, :rfc822], + ["lastBuildDate", "?", :date, :rfc822], + ["docs", "?", :text], + ["cloud", "?", :have_attribute], + ["skipDays", "?", :have_child], + ["skipHours", "?", :have_child], + ["image", nil, :have_child], + ["item", "*", :have_children], + ["textInput", "?", :have_child], + ].each do |name, occurs, type, *args| + __send__("install_#{type}_element", name, "", occurs, name, *args) + end + alias date pubDate + alias date= pubDate= + + private + def maker_target(maker) + maker.channel + end + + def setup_maker_elements(channel) + super + [ + [skipDays, "day"], + [skipHours, "hour"], + ].each do |skip, key| + if skip + skip.__send__("#{key}s").each do |val| + target_skips = channel.__send__("skip#{key.capitalize}s") + new_target = target_skips.__send__("new_#{key}") + new_target.content = val.content + end + end + end + end + + def not_need_to_call_setup_maker_variables + %w(image textInput) + end + + class SkipDays < Element + include RSS09 + + [ + ["day", "*"] + ].each do |name, occurs| + install_have_children_element(name, "", occurs) + end + + class Day < Element + include RSS09 + + content_setup + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.content = args[0] + end + end + + end + + end + + class SkipHours < Element + include RSS09 + + [ + ["hour", "*"] + ].each do |name, occurs| + install_have_children_element(name, "", occurs) + end + + class Hour < Element + include RSS09 + + content_setup(:integer) + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.content = args[0] + end + end + end + + end + + class Image < Element + + include RSS09 + + %w(url title link).each do |name| + install_text_element(name, "", nil) + end + [ + ["width", :integer], + ["height", :integer], + ["description"], + ].each do |name, type| + install_text_element(name, "", "?", name, type) + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.url = args[0] + self.title = args[1] + self.link = args[2] + self.width = args[3] + self.height = args[4] + self.description = args[5] + end + end + + private + def maker_target(maker) + maker.image + end + end + + class Cloud < Element + + include RSS09 + + [ + ["domain", "", true], + ["port", "", true, :integer], + ["path", "", true], + ["registerProcedure", "", true], + ["protocol", "", true], + ].each do |name, uri, required, type| + install_get_attribute(name, uri, required, type) + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.domain = args[0] + self.port = args[1] + self.path = args[2] + self.registerProcedure = args[3] + self.protocol = args[4] + end + end + end + + class Item < Element + + include RSS09 + + [ + ["title", '?', :text], + ["link", '?', :text], + ["description", '?', :text], + ["category", '*', :have_children, "categories"], + ["source", '?', :have_child], + ["enclosure", '?', :have_child], + ].each do |tag, occurs, type, *args| + __send__("install_#{type}_element", tag, "", occurs, tag, *args) + end + + private + def maker_target(items) + if items.respond_to?("items") + # For backward compatibility + items = items.items + end + items.new_item + end + + def setup_maker_element(item) + super + @enclosure.setup_maker(item) if @enclosure + @source.setup_maker(item) if @source + end + + class Source < Element + + include RSS09 + + [ + ["url", "", true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required) + end + + content_setup + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.url = args[0] + self.content = args[1] + end + end + + private + def maker_target(item) + item.source + end + + def setup_maker_attributes(source) + source.url = url + source.content = content + end + end + + class Enclosure < Element + + include RSS09 + + [ + ["url", "", true], + ["length", "", true, :integer], + ["type", "", true], + ].each do |name, uri, required, type| + install_get_attribute(name, uri, required, type) + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.url = args[0] + self.length = args[1] + self.type = args[2] + end + end + + private + def maker_target(item) + item.enclosure + end + + def setup_maker_attributes(enclosure) + enclosure.url = url + enclosure.length = length + enclosure.type = type + end + end + + class Category < Element + + include RSS09 + + [ + ["domain", "", false] + ].each do |name, uri, required| + install_get_attribute(name, uri, required) + end + + content_setup + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.domain = args[0] + self.content = args[1] + end + end + + private + def maker_target(item) + item.new_category + end + + def setup_maker_attributes(category) + category.domain = domain + category.content = content + end + + end + + end + + class TextInput < Element + + include RSS09 + + %w(title description name link).each do |name| + install_text_element(name, "", nil) + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.title = args[0] + self.description = args[1] + self.name = args[2] + self.link = args[3] + end + end + + private + def maker_target(maker) + maker.textinput + end + end + + end + + end + + RSS09::ELEMENTS.each do |name| + BaseListener.install_get_text_element("", name, "#{name}=") + end + + module ListenerMixin + private + def start_rss(tag_name, prefix, attrs, ns) + check_ns(tag_name, prefix, ns, "") + + @rss = Rss.new(attrs['version'], @version, @encoding, @standalone) + @rss.do_validate = @do_validate + @rss.xml_stylesheets = @xml_stylesheets + @last_element = @rss + @proc_stack.push Proc.new { |text, tags| + @rss.validate_for_stream(tags, @ignore_unknown_element) if @do_validate + } + end + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/1.0.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/1.0.rb new file mode 100644 index 0000000000..a945434fbf --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/1.0.rb @@ -0,0 +1,451 @@ +require "rss/parser" + +module RSS + + module RSS10 + NSPOOL = {} + ELEMENTS = [] + + def self.append_features(klass) + super + + klass.install_must_call_validator('', ::RSS::URI) + end + + end + + class RDF < Element + + include RSS10 + include RootElementMixin + + class << self + + def required_uri + URI + end + + end + + @tag_name = 'RDF' + + PREFIX = 'rdf' + URI = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + + install_ns('', ::RSS::URI) + install_ns(PREFIX, URI) + + [ + ["channel", nil], + ["image", "?"], + ["item", "+", :children], + ["textinput", "?"], + ].each do |tag, occurs, type| + type ||= :child + __send__("install_have_#{type}_element", tag, ::RSS::URI, occurs) + end + + attr_accessor :rss_version, :version, :encoding, :standalone + + def initialize(version=nil, encoding=nil, standalone=nil) + super('1.0', version, encoding, standalone) + end + + def full_name + tag_name_with_prefix(PREFIX) + end + + class Li < Element + + include RSS10 + + class << self + def required_uri + URI + end + end + + [ + ["resource", [URI, ""], true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required) + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.resource = args[0] + end + end + + def full_name + tag_name_with_prefix(PREFIX) + end + end + + class Seq < Element + + include RSS10 + + Li = ::RSS::RDF::Li + + class << self + def required_uri + URI + end + end + + @tag_name = 'Seq' + + install_have_children_element("li", URI, "*") + install_must_call_validator('rdf', ::RSS::RDF::URI) + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + @li = args[0] if args[0] + end + end + + def full_name + tag_name_with_prefix(PREFIX) + end + + def setup_maker(target) + lis.each do |li| + target << li.resource + end + end + end + + class Bag < Element + + include RSS10 + + Li = ::RSS::RDF::Li + + class << self + def required_uri + URI + end + end + + @tag_name = 'Bag' + + install_have_children_element("li", URI, "*") + install_must_call_validator('rdf', URI) + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + @li = args[0] if args[0] + end + end + + def full_name + tag_name_with_prefix(PREFIX) + end + + def setup_maker(target) + lis.each do |li| + target << li.resource + end + end + end + + class Channel < Element + + include RSS10 + + class << self + + def required_uri + ::RSS::URI + end + + end + + [ + ["about", URI, true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required, nil, nil, + "#{PREFIX}:#{name}") + end + + [ + ['title', nil, :text], + ['link', nil, :text], + ['description', nil, :text], + ['image', '?', :have_child], + ['items', nil, :have_child], + ['textinput', '?', :have_child], + ].each do |tag, occurs, type| + __send__("install_#{type}_element", tag, ::RSS::URI, occurs) + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.about = args[0] + end + end + + private + def maker_target(maker) + maker.channel + end + + def setup_maker_attributes(channel) + channel.about = about + end + + class Image < Element + + include RSS10 + + class << self + + def required_uri + ::RSS::URI + end + + end + + [ + ["resource", URI, true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required, nil, nil, + "#{PREFIX}:#{name}") + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.resource = args[0] + end + end + end + + class Textinput < Element + + include RSS10 + + class << self + + def required_uri + ::RSS::URI + end + + end + + [ + ["resource", URI, true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required, nil, nil, + "#{PREFIX}:#{name}") + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.resource = args[0] + end + end + end + + class Items < Element + + include RSS10 + + Seq = ::RSS::RDF::Seq + + class << self + + def required_uri + ::RSS::URI + end + + end + + install_have_child_element("Seq", URI, nil) + install_must_call_validator('rdf', URI) + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.Seq = args[0] + end + self.Seq ||= Seq.new + end + + def resources + if @Seq + @Seq.lis.collect do |li| + li.resource + end + else + [] + end + end + end + end + + class Image < Element + + include RSS10 + + class << self + + def required_uri + ::RSS::URI + end + + end + + [ + ["about", URI, true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required, nil, nil, + "#{PREFIX}:#{name}") + end + + %w(title url link).each do |name| + install_text_element(name, ::RSS::URI, nil) + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.about = args[0] + end + end + + private + def maker_target(maker) + maker.image + end + end + + class Item < Element + + include RSS10 + + class << self + + def required_uri + ::RSS::URI + end + + end + + + [ + ["about", URI, true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required, nil, nil, + "#{PREFIX}:#{name}") + end + + [ + ["title", nil], + ["link", nil], + ["description", "?"], + ].each do |tag, occurs| + install_text_element(tag, ::RSS::URI, occurs) + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.about = args[0] + end + end + + private + def maker_target(items) + if items.respond_to?("items") + # For backward compatibility + items = items.items + end + items.new_item + end + end + + class Textinput < Element + + include RSS10 + + class << self + + def required_uri + ::RSS::URI + end + + end + + [ + ["about", URI, true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required, nil, nil, + "#{PREFIX}:#{name}") + end + + %w(title description name link).each do |name| + install_text_element(name, ::RSS::URI, nil) + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.about = args[0] + end + end + + private + def maker_target(maker) + maker.textinput + end + end + + end + + RSS10::ELEMENTS.each do |name| + BaseListener.install_get_text_element(URI, name, "#{name}=") + end + + module ListenerMixin + private + def start_RDF(tag_name, prefix, attrs, ns) + check_ns(tag_name, prefix, ns, RDF::URI) + + @rss = RDF.new(@version, @encoding, @standalone) + @rss.do_validate = @do_validate + @rss.xml_stylesheets = @xml_stylesheets + @last_element = @rss + @proc_stack.push Proc.new { |text, tags| + @rss.validate_for_stream(tags, @ignore_unknown_element) if @do_validate + } + end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/2.0.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/2.0.rb new file mode 100644 index 0000000000..44bdb4f1ae --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/2.0.rb @@ -0,0 +1,111 @@ +require "rss/0.9" + +module RSS + + class Rss + + class Channel + + [ + ["generator"], + ["ttl", :integer], + ].each do |name, type| + install_text_element(name, "", "?", name, type) + end + + [ + %w(category categories), + ].each do |name, plural_name| + install_have_children_element(name, "", "*", name, plural_name) + end + + [ + ["image", "?"], + ["language", "?"], + ].each do |name, occurs| + install_model(name, "", occurs) + end + + Category = Item::Category + + class Item + + [ + ["comments", "?"], + ["author", "?"], + ].each do |name, occurs| + install_text_element(name, "", occurs) + end + + [ + ["pubDate", '?'], + ].each do |name, occurs| + install_date_element(name, "", occurs, name, 'rfc822') + end + alias date pubDate + alias date= pubDate= + + [ + ["guid", '?'], + ].each do |name, occurs| + install_have_child_element(name, "", occurs) + end + + private + alias _setup_maker_element setup_maker_element + def setup_maker_element(item) + _setup_maker_element(item) + @guid.setup_maker(item) if @guid + end + + class Guid < Element + + include RSS09 + + [ + ["isPermaLink", "", false, :boolean] + ].each do |name, uri, required, type| + install_get_attribute(name, uri, required, type) + end + + content_setup + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.isPermaLink = args[0] + self.content = args[1] + end + end + + alias_method :_PermaLink?, :PermaLink? + private :_PermaLink? + def PermaLink? + perma = _PermaLink? + perma or perma.nil? + end + + private + def maker_target(item) + item.guid + end + + def setup_maker_attributes(guid) + guid.isPermaLink = isPermaLink + guid.content = content + end + end + + end + + end + + end + + RSS09::ELEMENTS.each do |name| + BaseListener.install_get_text_element("", name, "#{name}=") + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/content.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/content.rb new file mode 100644 index 0000000000..1b13f39fcf --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/content.rb @@ -0,0 +1,38 @@ +require "rss/1.0" + +module RSS + + CONTENT_PREFIX = 'content' + CONTENT_URI = "http://purl.org/rss/1.0/modules/content/" + + RDF.install_ns(CONTENT_PREFIX, CONTENT_URI) + + module ContentModel + + extend BaseModel + + ELEMENTS = [] + + def self.append_features(klass) + super + + klass.install_must_call_validator(CONTENT_PREFIX, CONTENT_URI) + %w(encoded).each do |name| + klass.install_text_element(name, CONTENT_URI, "?", + "#{CONTENT_PREFIX}_#{name}") + end + end + end + + class RDF + class Item; include ContentModel; end + end + + prefix_size = CONTENT_PREFIX.size + 1 + ContentModel::ELEMENTS.uniq! + ContentModel::ELEMENTS.each do |full_name| + name = full_name[prefix_size..-1] + BaseListener.install_get_text_element(CONTENT_URI, name, "#{full_name}=") + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/converter.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/converter.rb new file mode 100644 index 0000000000..d928c48223 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/converter.rb @@ -0,0 +1,158 @@ +require "rss/utils" + +module RSS + + class Converter + + include Utils + + def initialize(to_enc, from_enc=nil) + normalized_to_enc = to_enc.downcase.gsub(/-/, '_') + from_enc ||= 'utf-8' + normalized_from_enc = from_enc.downcase.gsub(/-/, '_') + if normalized_to_enc == normalized_from_enc + def_same_enc() + else + def_diff_enc = "def_to_#{normalized_to_enc}_from_#{normalized_from_enc}" + if respond_to?(def_diff_enc) + __send__(def_diff_enc) + else + def_else_enc(to_enc, from_enc) + end + end + end + + def convert(value) + value + end + + def def_convert(depth=0) + instance_eval(<<-EOC, *get_file_and_line_from_caller(depth)) + def convert(value) + if value.kind_of?(String) + #{yield('value')} + else + value + end + end + EOC + end + + def def_iconv_convert(to_enc, from_enc, depth=0) + begin + require "iconv" + @iconv = Iconv.new(to_enc, from_enc) + def_convert(depth+1) do |value| + <<-EOC + begin + @iconv.iconv(#{value}) + rescue Iconv::Failure + raise ConversionError.new(#{value}, "#{to_enc}", "#{from_enc}") + end + EOC + end + rescue LoadError, ArgumentError, SystemCallError + raise UnknownConversionMethodError.new(to_enc, from_enc) + end + end + + def def_else_enc(to_enc, from_enc) + def_iconv_convert(to_enc, from_enc, 0) + end + + def def_same_enc() + def_convert do |value| + value + end + end + + def def_uconv_convert_if_can(meth, to_enc, from_enc, nkf_arg) + begin + require "uconv" + def_convert(1) do |value| + <<-EOC + begin + Uconv.#{meth}(#{value}) + rescue Uconv::Error + raise ConversionError.new(#{value}, "#{to_enc}", "#{from_enc}") + end + EOC + end + rescue LoadError + require 'nkf' + def_convert(1) do |value| + "NKF.nkf(#{nkf_arg.dump}, #{value})" + end + end + end + + def def_to_euc_jp_from_utf_8 + def_uconv_convert_if_can('u8toeuc', 'EUC-JP', 'UTF-8', '-We') + end + + def def_to_utf_8_from_euc_jp + def_uconv_convert_if_can('euctou8', 'UTF-8', 'EUC-JP', '-Ew') + end + + def def_to_shift_jis_from_utf_8 + def_uconv_convert_if_can('u8tosjis', 'Shift_JIS', 'UTF-8', '-Ws') + end + + def def_to_utf_8_from_shift_jis + def_uconv_convert_if_can('sjistou8', 'UTF-8', 'Shift_JIS', '-Sw') + end + + def def_to_euc_jp_from_shift_jis + require "nkf" + def_convert do |value| + "NKF.nkf('-Se', #{value})" + end + end + + def def_to_shift_jis_from_euc_jp + require "nkf" + def_convert do |value| + "NKF.nkf('-Es', #{value})" + end + end + + def def_to_euc_jp_from_iso_2022_jp + require "nkf" + def_convert do |value| + "NKF.nkf('-Je', #{value})" + end + end + + def def_to_iso_2022_jp_from_euc_jp + require "nkf" + def_convert do |value| + "NKF.nkf('-Ej', #{value})" + end + end + + def def_to_utf_8_from_iso_8859_1 + def_convert do |value| + "#{value}.unpack('C*').pack('U*')" + end + end + + def def_to_iso_8859_1_from_utf_8 + def_convert do |value| + <<-EOC + array_utf8 = #{value}.unpack('U*') + array_enc = [] + array_utf8.each do |num| + if num <= 0xFF + array_enc << num + else + array_enc.concat "&\#\#{num};".unpack('C*') + end + end + array_enc.pack('C*') + EOC + end + end + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/dublincore.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/dublincore.rb new file mode 100644 index 0000000000..8a4afd4dd9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/dublincore.rb @@ -0,0 +1,154 @@ +require "rss/1.0" + +module RSS + + DC_PREFIX = 'dc' + DC_URI = "http://purl.org/dc/elements/1.1/" + + RDF.install_ns(DC_PREFIX, DC_URI) + + module BaseDublinCoreModel + def append_features(klass) + super + + return if klass.instance_of?(Module) + DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name| + plural = plural_name || "#{name}s" + full_name = "#{DC_PREFIX}_#{name}" + full_plural_name = "#{DC_PREFIX}_#{plural}" + klass_name = "DublinCore#{Utils.to_class_name(name)}" + klass.install_must_call_validator(DC_PREFIX, DC_URI) + klass.install_have_children_element(name, DC_URI, "*", + full_name, full_plural_name) + klass.module_eval(<<-EOC, *get_file_and_line_from_caller(0)) + remove_method :#{full_name} + remove_method :#{full_name}= + remove_method :set_#{full_name} + + def #{full_name} + @#{full_name}.first and @#{full_name}.first.value + end + + def #{full_name}=(new_value) + @#{full_name}[0] = Utils.new_with_value_if_need(#{klass_name}, new_value) + end + alias set_#{full_name} #{full_name}= + EOC + end + klass.module_eval(<<-EOC, *get_file_and_line_from_caller(0)) + alias date #{DC_PREFIX}_date + alias date= #{DC_PREFIX}_date= + EOC + end + end + + module DublinCoreModel + + extend BaseModel + extend BaseDublinCoreModel + + TEXT_ELEMENTS = { + "title" => nil, + "description" => nil, + "creator" => nil, + "subject" => nil, + "publisher" => nil, + "contributor" => nil, + "type" => nil, + "format" => nil, + "identifier" => nil, + "source" => nil, + "language" => nil, + "relation" => nil, + "coverage" => nil, + "rights" => "rightses" # FIXME + } + + DATE_ELEMENTS = { + "date" => "w3cdtf", + } + + ELEMENT_NAME_INFOS = DublinCoreModel::TEXT_ELEMENTS.to_a + DublinCoreModel::DATE_ELEMENTS.each do |name, | + ELEMENT_NAME_INFOS << [name, nil] + end + + ELEMENTS = TEXT_ELEMENTS.keys + DATE_ELEMENTS.keys + + ELEMENTS.each do |name, plural_name| + module_eval(<<-EOC, *get_file_and_line_from_caller(0)) + class DublinCore#{Utils.to_class_name(name)} < Element + include RSS10 + + content_setup + + class << self + def required_prefix + DC_PREFIX + end + + def required_uri + DC_URI + end + end + + @tag_name = #{name.dump} + + alias_method(:value, :content) + alias_method(:value=, :content=) + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.content = args[0] + end + end + + def full_name + tag_name_with_prefix(DC_PREFIX) + end + + def maker_target(target) + target.new_#{name} + end + + def setup_maker_attributes(#{name}) + #{name}.content = content + end + end + EOC + end + + DATE_ELEMENTS.each do |name, type| + module_eval(<<-EOC, *get_file_and_line_from_caller(0)) + class DublinCore#{Utils.to_class_name(name)} < Element + remove_method(:content=) + remove_method(:value=) + + date_writer("content", #{type.dump}, #{name.dump}) + + alias_method(:value=, :content=) + end + EOC + end + end + + # For backward compatibility + DublincoreModel = DublinCoreModel + + class RDF + class Channel; include DublinCoreModel; end + class Image; include DublinCoreModel; end + class Item; include DublinCoreModel; end + class Textinput; include DublinCoreModel; end + end + + DublinCoreModel::ELEMENTS.each do |name| + class_name = Utils.to_class_name(name) + BaseListener.install_class_name(DC_URI, name, "DublinCore#{class_name}") + end + + DublinCoreModel::ELEMENTS.collect! {|name| "#{DC_PREFIX}_#{name}"} +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/image.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/image.rb new file mode 100644 index 0000000000..a9e9e9094e --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/image.rb @@ -0,0 +1,193 @@ +require 'rss/1.0' +require 'rss/dublincore' + +module RSS + + IMAGE_PREFIX = 'image' + IMAGE_URI = 'http://web.resource.org/rss/1.0/modules/image/' + + RDF.install_ns(IMAGE_PREFIX, IMAGE_URI) + + IMAGE_ELEMENTS = [] + + %w(item favicon).each do |name| + class_name = Utils.to_class_name(name) + BaseListener.install_class_name(IMAGE_URI, name, "Image#{class_name}") + IMAGE_ELEMENTS << "#{IMAGE_PREFIX}_#{name}" + end + + module ImageModelUtils + def validate_one_tag_name(ignore_unknown_element, name, tags) + if !ignore_unknown_element + invalid = tags.find {|tag| tag != name} + raise UnknownTagError.new(invalid, IMAGE_URI) if invalid + end + raise TooMuchTagError.new(name, tag_name) if tags.size > 1 + end + end + + module ImageItemModel + include ImageModelUtils + extend BaseModel + + def self.append_features(klass) + super + + klass.install_have_child_element("item", IMAGE_URI, "?", + "#{IMAGE_PREFIX}_item") + klass.install_must_call_validator(IMAGE_PREFIX, IMAGE_URI) + end + + class ImageItem < Element + include RSS10 + include DublinCoreModel + + @tag_name = "item" + + class << self + def required_prefix + IMAGE_PREFIX + end + + def required_uri + IMAGE_URI + end + end + + install_must_call_validator(IMAGE_PREFIX, IMAGE_URI) + + [ + ["about", ::RSS::RDF::URI, true], + ["resource", ::RSS::RDF::URI, false], + ].each do |name, uri, required| + install_get_attribute(name, uri, required, nil, nil, + "#{::RSS::RDF::PREFIX}:#{name}") + end + + %w(width height).each do |tag| + full_name = "#{IMAGE_PREFIX}_#{tag}" + disp_name = "#{IMAGE_PREFIX}:#{tag}" + install_text_element(tag, IMAGE_URI, "?", + full_name, :integer, disp_name) + BaseListener.install_get_text_element(IMAGE_URI, tag, "#{full_name}=") + end + + alias width= image_width= + alias width image_width + alias height= image_height= + alias height image_height + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.about = args[0] + self.resource = args[1] + end + end + + def full_name + tag_name_with_prefix(IMAGE_PREFIX) + end + + private + def maker_target(target) + target.image_item + end + + def setup_maker_attributes(item) + item.about = self.about + item.resource = self.resource + end + end + end + + module ImageFaviconModel + include ImageModelUtils + extend BaseModel + + def self.append_features(klass) + super + + unless klass.class == Module + klass.install_have_child_element("favicon", IMAGE_URI, "?", + "#{IMAGE_PREFIX}_favicon") + klass.install_must_call_validator(IMAGE_PREFIX, IMAGE_URI) + end + end + + class ImageFavicon < Element + include RSS10 + include DublinCoreModel + + @tag_name = "favicon" + + class << self + def required_prefix + IMAGE_PREFIX + end + + def required_uri + IMAGE_URI + end + end + + [ + ["about", ::RSS::RDF::URI, true, ::RSS::RDF::PREFIX], + ["size", IMAGE_URI, true, IMAGE_PREFIX], + ].each do |name, uri, required, prefix| + install_get_attribute(name, uri, required, nil, nil, + "#{prefix}:#{name}") + end + + AVAILABLE_SIZES = %w(small medium large) + alias_method :_size=, :size= + private :_size= + def size=(new_value) + if @do_validate and !new_value.nil? + new_value = new_value.strip + unless AVAILABLE_SIZES.include?(new_value) + attr_name = "#{IMAGE_PREFIX}:size" + raise NotAvailableValueError.new(full_name, new_value, attr_name) + end + end + __send__(:_size=, new_value) + end + + alias image_size= size= + alias image_size size + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.about = args[0] + self.size = args[1] + end + end + + def full_name + tag_name_with_prefix(IMAGE_PREFIX) + end + + private + def maker_target(target) + target.image_favicon + end + + def setup_maker_attributes(favicon) + favicon.about = self.about + favicon.size = self.size + end + end + + end + + class RDF + class Channel; include ImageFaviconModel; end + class Item; include ImageItemModel; end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker.rb new file mode 100644 index 0000000000..9ed799ac7f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker.rb @@ -0,0 +1,37 @@ +require "rss/rss" + +module RSS + + module Maker + + MAKERS = {} + + class << self + def make(version, &block) + maker(version).make(&block) + end + + def maker(version) + MAKERS[version] + end + + def add_maker(version, maker) + MAKERS[version] = maker + end + + def filename_to_version(filename) + File.basename(filename, ".*") + end + end + end + +end + +require "rss/maker/1.0" +require "rss/maker/2.0" +require "rss/maker/content" +require "rss/maker/dublincore" +require "rss/maker/syndication" +require "rss/maker/taxonomy" +require "rss/maker/trackback" +require "rss/maker/image" diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/0.9.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/0.9.rb new file mode 100644 index 0000000000..b82585fb96 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/0.9.rb @@ -0,0 +1,224 @@ +require "rss/0.9" + +require "rss/maker/base" + +module RSS + module Maker + + class RSS09 < RSSBase + + def initialize(rss_version="0.91") + super + end + + private + def make_rss + Rss.new(@rss_version, @version, @encoding, @standalone) + end + + def setup_elements(rss) + setup_channel(rss) + end + + class Channel < ChannelBase + + def to_rss(rss) + channel = Rss::Channel.new + set = setup_values(channel) + if set + rss.channel = channel + setup_items(rss) + setup_image(rss) + setup_textinput(rss) + setup_other_elements(rss) + if rss.channel.image + rss + else + nil + end + elsif variable_is_set? + raise NotSetError.new("maker.channel", not_set_required_variables) + end + end + + def have_required_values? + @title and @link and @description and @language + end + + private + def setup_items(rss) + @maker.items.to_rss(rss) + end + + def setup_image(rss) + @maker.image.to_rss(rss) + end + + def setup_textinput(rss) + @maker.textinput.to_rss(rss) + end + + def variables + super + ["pubDate"] + end + + def required_variable_names + %w(title link description language) + end + + class SkipDays < SkipDaysBase + def to_rss(rss, channel) + unless @days.empty? + skipDays = Rss::Channel::SkipDays.new + channel.skipDays = skipDays + @days.each do |day| + day.to_rss(rss, skipDays.days) + end + end + end + + class Day < DayBase + def to_rss(rss, days) + day = Rss::Channel::SkipDays::Day.new + set = setup_values(day) + if set + days << day + setup_other_elements(rss) + end + end + + def have_required_values? + @content + end + end + end + + class SkipHours < SkipHoursBase + def to_rss(rss, channel) + unless @hours.empty? + skipHours = Rss::Channel::SkipHours.new + channel.skipHours = skipHours + @hours.each do |hour| + hour.to_rss(rss, skipHours.hours) + end + end + end + + class Hour < HourBase + def to_rss(rss, hours) + hour = Rss::Channel::SkipHours::Hour.new + set = setup_values(hour) + if set + hours << hour + setup_other_elements(rss) + end + end + + def have_required_values? + @content + end + end + end + + class Cloud < CloudBase + def to_rss(*args) + end + end + + class Categories < CategoriesBase + def to_rss(*args) + end + + class Category < CategoryBase + end + end + end + + class Image < ImageBase + def to_rss(rss) + image = Rss::Channel::Image.new + set = setup_values(image) + if set + image.link = link + rss.channel.image = image + setup_other_elements(rss) + end + end + + def have_required_values? + @url and @title and link + end + end + + class Items < ItemsBase + def to_rss(rss) + if rss.channel + normalize.each do |item| + item.to_rss(rss) + end + setup_other_elements(rss) + end + end + + class Item < ItemBase + def to_rss(rss) + item = Rss::Channel::Item.new + set = setup_values(item) + if set + rss.items << item + setup_other_elements(rss) + end + end + + private + def have_required_values? + @title and @link + end + + class Guid < GuidBase + def to_rss(*args) + end + end + + class Enclosure < EnclosureBase + def to_rss(*args) + end + end + + class Source < SourceBase + def to_rss(*args) + end + end + + class Categories < CategoriesBase + def to_rss(*args) + end + + class Category < CategoryBase + end + end + + end + end + + class Textinput < TextinputBase + def to_rss(rss) + textInput = Rss::Channel::TextInput.new + set = setup_values(textInput) + if set + rss.channel.textInput = textInput + setup_other_elements(rss) + end + end + + private + def have_required_values? + @title and @description and @name and @link + end + end + end + + add_maker(filename_to_version(__FILE__), RSS09) + add_maker(filename_to_version(__FILE__) + "1", RSS09) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/1.0.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/1.0.rb new file mode 100644 index 0000000000..3e6542a007 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/1.0.rb @@ -0,0 +1,204 @@ +require "rss/1.0" + +require "rss/maker/base" + +module RSS + module Maker + + class RSS10 < RSSBase + + def initialize + super("1.0") + end + + private + def make_rss + RDF.new(@version, @encoding, @standalone) + end + + def setup_elements(rss) + setup_channel(rss) + setup_image(rss) + setup_items(rss) + setup_textinput(rss) + end + + class Channel < ChannelBase + + def to_rss(rss) + set = false + if @about + channel = RDF::Channel.new(@about) + set = setup_values(channel) + if set + channel.dc_dates.clear + rss.channel = channel + setup_items(rss) + setup_image(rss) + setup_textinput(rss) + setup_other_elements(rss) + end + end + + if (!@about or !set) and variable_is_set? + raise NotSetError.new("maker.channel", not_set_required_variables) + end + end + + def have_required_values? + @about and @title and @link and @description + end + + private + def setup_items(rss) + items = RDF::Channel::Items.new + seq = items.Seq + @maker.items.normalize.each do |item| + seq.lis << RDF::Channel::Items::Seq::Li.new(item.link) + end + rss.channel.items = items + end + + def setup_image(rss) + if @maker.image.have_required_values? + rss.channel.image = RDF::Channel::Image.new(@maker.image.url) + end + end + + def setup_textinput(rss) + if @maker.textinput.have_required_values? + textinput = RDF::Channel::Textinput.new(@maker.textinput.link) + rss.channel.textinput = textinput + end + end + + def required_variable_names + %w(about title link description) + end + + class SkipDays < SkipDaysBase + def to_rss(*args) + end + + class Day < DayBase + end + end + + class SkipHours < SkipHoursBase + def to_rss(*args) + end + + class Hour < HourBase + end + end + + class Cloud < CloudBase + def to_rss(*args) + end + end + + class Categories < CategoriesBase + def to_rss(*args) + end + + class Category < CategoryBase + end + end + end + + class Image < ImageBase + def to_rss(rss) + if @url + image = RDF::Image.new(@url) + set = setup_values(image) + if set + rss.image = image + setup_other_elements(rss) + end + end + end + + def have_required_values? + @url and @title and link and @maker.channel.have_required_values? + end + + private + def variables + super + ["link"] + end + end + + class Items < ItemsBase + def to_rss(rss) + if rss.channel + normalize.each do |item| + item.to_rss(rss) + end + setup_other_elements(rss) + end + end + + class Item < ItemBase + def to_rss(rss) + if @link + item = RDF::Item.new(@link) + set = setup_values(item) + if set + item.dc_dates.clear + rss.items << item + setup_other_elements(rss) + end + end + end + + def have_required_values? + @title and @link + end + + class Guid < GuidBase + def to_rss(*args) + end + end + + class Enclosure < EnclosureBase + def to_rss(*args) + end + end + + class Source < SourceBase + def to_rss(*args) + end + end + + class Categories < CategoriesBase + def to_rss(*args) + end + + class Category < CategoryBase + end + end + end + end + + class Textinput < TextinputBase + def to_rss(rss) + if @link + textinput = RDF::Textinput.new(@link) + set = setup_values(textinput) + if set + rss.textinput = textinput + setup_other_elements(rss) + end + end + end + + def have_required_values? + @title and @description and @name and @link and + @maker.channel.have_required_values? + end + end + end + + add_maker(filename_to_version(__FILE__), RSS10) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/2.0.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/2.0.rb new file mode 100644 index 0000000000..a958661614 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/2.0.rb @@ -0,0 +1,168 @@ +require "rss/2.0" + +require "rss/maker/0.9" + +module RSS + module Maker + + class RSS20 < RSS09 + + def initialize(rss_version="2.0") + super + end + + class Channel < RSS09::Channel + + def have_required_values? + @title and @link and @description + end + + def required_variable_names + %w(title link description) + end + + class SkipDays < RSS09::Channel::SkipDays + class Day < RSS09::Channel::SkipDays::Day + end + end + + class SkipHours < RSS09::Channel::SkipHours + class Hour < RSS09::Channel::SkipHours::Hour + end + end + + class Cloud < RSS09::Channel::Cloud + def to_rss(rss, channel) + cloud = Rss::Channel::Cloud.new + set = setup_values(cloud) + if set + channel.cloud = cloud + setup_other_elements(rss) + end + end + + def have_required_values? + @domain and @port and @path and + @registerProcedure and @protocol + end + end + + class Categories < RSS09::Channel::Categories + def to_rss(rss, channel) + @categories.each do |category| + category.to_rss(rss, channel) + end + end + + class Category < RSS09::Channel::Categories::Category + def to_rss(rss, channel) + category = Rss::Channel::Category.new + set = setup_values(category) + if set + channel.categories << category + setup_other_elements(rss) + end + end + + def have_required_values? + @content + end + end + end + + end + + class Image < RSS09::Image + end + + class Items < RSS09::Items + + class Item < RSS09::Items::Item + + def have_required_values? + @title or @description + end + + private + def variables + super + ["pubDate"] + end + + class Guid < RSS09::Items::Item::Guid + def to_rss(rss, item) + guid = Rss::Channel::Item::Guid.new + set = setup_values(guid) + if set + item.guid = guid + setup_other_elements(rss) + end + end + + def have_required_values? + @content + end + end + + class Enclosure < RSS09::Items::Item::Enclosure + def to_rss(rss, item) + enclosure = Rss::Channel::Item::Enclosure.new + set = setup_values(enclosure) + if set + item.enclosure = enclosure + setup_other_elements(rss) + end + end + + def have_required_values? + @url and @length and @type + end + end + + class Source < RSS09::Items::Item::Source + def to_rss(rss, item) + source = Rss::Channel::Item::Source.new + set = setup_values(source) + if set + item.source = source + setup_other_elements(rss) + end + end + + def have_required_values? + @url and @content + end + end + + class Categories < RSS09::Items::Item::Categories + def to_rss(rss, item) + @categories.each do |category| + category.to_rss(rss, item) + end + end + + class Category < RSS09::Items::Item::Categories::Category + def to_rss(rss, item) + category = Rss::Channel::Item::Category.new + set = setup_values(category) + if set + item.categories << category + setup_other_elements(rss) + end + end + + def have_required_values? + @content + end + end + end + end + + end + + class Textinput < RSS09::Textinput + end + end + + add_maker(filename_to_version(__FILE__), RSS20) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/base.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/base.rb new file mode 100644 index 0000000000..2327dd98e4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/base.rb @@ -0,0 +1,546 @@ +require 'forwardable' + +require 'rss/rss' + +module RSS + module Maker + + module Base + + def self.append_features(klass) + super + + klass.module_eval(<<-EOC, __FILE__, __LINE__) + + OTHER_ELEMENTS = [] + NEED_INITIALIZE_VARIABLES = [] + + def self.inherited(subclass) + subclass.const_set("OTHER_ELEMENTS", []) + subclass.const_set("NEED_INITIALIZE_VARIABLES", []) + + subclass.module_eval(<<-EOEOC, __FILE__, __LINE__) + def self.other_elements + OTHER_ELEMENTS + super + end + + def self.need_initialize_variables + NEED_INITIALIZE_VARIABLES + super + end + EOEOC + end + + def self.add_other_element(variable_name) + OTHER_ELEMENTS << variable_name + end + + def self.other_elements + OTHER_ELEMENTS + end + + def self.add_need_initialize_variable(variable_name, init_value="nil") + NEED_INITIALIZE_VARIABLES << [variable_name, init_value] + end + + def self.need_initialize_variables + NEED_INITIALIZE_VARIABLES + end + + def self.def_array_element(name) + include Enumerable + extend Forwardable + + def_delegators("@\#{name}", :<<, :[], :[]=, :first, :last) + def_delegators("@\#{name}", :push, :pop, :shift, :unshift) + def_delegators("@\#{name}", :each, :size) + + add_need_initialize_variable(name, "[]") + end + EOC + end + + def initialize(maker) + @maker = maker + initialize_variables + end + + def have_required_values? + true + end + + private + def initialize_variables + self.class.need_initialize_variables.each do |variable_name, init_value| + instance_eval("@#{variable_name} = #{init_value}", __FILE__, __LINE__) + end + end + + def setup_other_elements(rss) + self.class.other_elements.each do |element| + __send__("setup_#{element}", rss, current_element(rss)) + end + end + + def current_element(rss) + rss + end + + def setup_values(target) + set = false + if have_required_values? + variables.each do |var| + setter = "#{var}=" + if target.respond_to?(setter) + value = __send__(var) + if value + target.__send__(setter, value) + set = true + end + end + end + end + set + end + + def variables + self.class.need_initialize_variables.find_all do |name, init| + "nil" == init + end.collect do |name, init| + name + end + end + + def variable_is_set? + variables.find {|var| !__send__(var).nil?} + end + + def not_set_required_variables + required_variable_names.find_all do |var| + __send__(var).nil? + end + end + + def required_variables_are_set? + required_variable_names.each do |var| + return false if __send__(var).nil? + end + true + end + + end + + class RSSBase + include Base + + class << self + def make(&block) + new.make(&block) + end + end + + %w(xml_stylesheets channel image items textinput).each do |element| + attr_reader element + add_need_initialize_variable(element, "make_#{element}") + module_eval(<<-EOC, __FILE__, __LINE__) + private + def setup_#{element}(rss) + @#{element}.to_rss(rss) + end + + def make_#{element} + self.class::#{Utils.to_class_name(element)}.new(self) + end +EOC + end + + attr_reader :rss_version + attr_accessor :version, :encoding, :standalone + + def initialize(rss_version) + super(self) + @rss_version = rss_version + @version = "1.0" + @encoding = "UTF-8" + @standalone = nil + end + + def make + if block_given? + yield(self) + to_rss + else + nil + end + end + + def to_rss + rss = make_rss + setup_xml_stylesheets(rss) + setup_elements(rss) + setup_other_elements(rss) + if rss.channel + rss + else + nil + end + end + + private + remove_method :make_xml_stylesheets + def make_xml_stylesheets + XMLStyleSheets.new(self) + end + + end + + class XMLStyleSheets + include Base + + def_array_element("xml_stylesheets") + + def to_rss(rss) + @xml_stylesheets.each do |xss| + xss.to_rss(rss) + end + end + + def new_xml_stylesheet + xss = XMLStyleSheet.new(@maker) + @xml_stylesheets << xss + if block_given? + yield xss + else + xss + end + end + + class XMLStyleSheet + include Base + + ::RSS::XMLStyleSheet::ATTRIBUTES.each do |attribute| + attr_accessor attribute + add_need_initialize_variable(attribute) + end + + def to_rss(rss) + xss = ::RSS::XMLStyleSheet.new + guess_type_if_need(xss) + set = setup_values(xss) + if set + rss.xml_stylesheets << xss + end + end + + def have_required_values? + @href and @type + end + + private + def guess_type_if_need(xss) + if @type.nil? + xss.href = @href + @type = xss.type + end + end + end + end + + class ChannelBase + include Base + + %w(cloud categories skipDays skipHours).each do |element| + attr_reader element + add_other_element(element) + add_need_initialize_variable(element, "make_#{element}") + module_eval(<<-EOC, __FILE__, __LINE__) + private + def setup_#{element}(rss, current) + @#{element}.to_rss(rss, current) + end + + def make_#{element} + self.class::#{Utils.to_class_name(element)}.new(@maker) + end +EOC + end + + %w(about title link description language copyright + managingEditor webMaster rating docs date + lastBuildDate generator ttl).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + + alias_method(:pubDate, :date) + alias_method(:pubDate=, :date=) + + def current_element(rss) + rss.channel + end + + class SkipDaysBase + include Base + + def_array_element("days") + + def new_day + day = self.class::Day.new(@maker) + @days << day + if block_given? + yield day + else + day + end + end + + def current_element(rss) + rss.channel.skipDays + end + + class DayBase + include Base + + %w(content).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + + def current_element(rss) + rss.channel.skipDays.last + end + + end + end + + class SkipHoursBase + include Base + + def_array_element("hours") + + def new_hour + hour = self.class::Hour.new(@maker) + @hours << hour + if block_given? + yield hour + else + hour + end + end + + def current_element(rss) + rss.channel.skipHours + end + + class HourBase + include Base + + %w(content).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + + def current_element(rss) + rss.channel.skipHours.last + end + + end + end + + class CloudBase + include Base + + %w(domain port path registerProcedure protocol).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + + def current_element(rss) + rss.channel.cloud + end + + end + + class CategoriesBase + include Base + + def_array_element("categories") + + def new_category + category = self.class::Category.new(@maker) + @categories << category + if block_given? + yield category + else + category + end + end + + class CategoryBase + include Base + + %w(domain content).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + end + end + + class ImageBase + include Base + + %w(title url width height description).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + + def link + @maker.channel.link + end + + def current_element(rss) + rss.image + end + end + + class ItemsBase + include Base + + def_array_element("items") + + attr_accessor :do_sort, :max_size + + def initialize(maker) + super + @do_sort = false + @max_size = -1 + end + + def normalize + if @max_size >= 0 + sort_if_need[0...@max_size] + else + sort_if_need[0..@max_size] + end + end + + def current_element(rss) + rss.items + end + + def new_item + item = self.class::Item.new(@maker) + @items << item + if block_given? + yield item + else + item + end + end + + private + def sort_if_need + if @do_sort.respond_to?(:call) + @items.sort do |x, y| + @do_sort.call(x, y) + end + elsif @do_sort + @items.sort do |x, y| + y <=> x + end + else + @items + end + end + + class ItemBase + include Base + + %w(guid enclosure source categories).each do |element| + attr_reader element + add_other_element(element) + add_need_initialize_variable(element, "make_#{element}") + module_eval(<<-EOC, __FILE__, __LINE__) + private + def setup_#{element}(rss, current) + @#{element}.to_rss(rss, current) + end + + def make_#{element} + self.class::#{Utils.to_class_name(element)}.new(@maker) + end +EOC + end + + %w(title link description date author comments).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + + alias_method(:pubDate, :date) + alias_method(:pubDate=, :date=) + + def <=>(other) + if date and other.date + date <=> other.date + elsif date + 1 + elsif other.date + -1 + else + 0 + end + end + + def current_element(rss) + rss.items.last + end + + class GuidBase + include Base + + %w(isPermaLink content).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + + class EnclosureBase + include Base + + %w(url length type).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + + class SourceBase + include Base + + %w(url content).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + + CategoriesBase = ChannelBase::CategoriesBase + + end + end + + class TextinputBase + include Base + + %w(title description name link).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + + def current_element(rss) + rss.textinput + end + + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/content.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/content.rb new file mode 100644 index 0000000000..18590d0cf8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/content.rb @@ -0,0 +1,29 @@ +require 'rss/content' +require 'rss/maker/1.0' + +module RSS + module Maker + module ContentModel + def self.append_features(klass) + super + + ::RSS::ContentModel::ELEMENTS.each do |element| + klass.add_need_initialize_variable(element) + klass.add_other_element(element) + klass.module_eval(<<-EOC, __FILE__, __LINE__+1) + attr_accessor :#{element} + def setup_#{element}(rss, current) + if #{element} and current.respond_to?(:#{element}=) + current.#{element} = @#{element} if @#{element} + end + end + EOC + end + end + end + + class ItemsBase + class ItemBase; include ContentModel; end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/dublincore.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/dublincore.rb new file mode 100644 index 0000000000..0cf1255e82 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/dublincore.rb @@ -0,0 +1,169 @@ +require 'rss/dublincore' +require 'rss/maker/1.0' + +module RSS + module Maker + module DublinCoreModel + def self.append_features(klass) + super + + ::RSS::DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name| + plural_name ||= "#{name}s" + full_name = "#{RSS::DC_PREFIX}_#{name}" + full_plural_name = "#{RSS::DC_PREFIX}_#{plural_name}" + klass_name = Utils.to_class_name(name) + plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}" + full_plural_klass_name = "self.class::#{plural_klass_name}" + full_klass_name = "#{full_plural_klass_name}::#{klass_name}" + klass.add_need_initialize_variable(full_plural_name, + "make_#{full_plural_name}") + klass.add_other_element(full_plural_name) + klass.module_eval(<<-EOC, __FILE__, __LINE__+1) + attr_accessor :#{full_plural_name} + def make_#{full_plural_name} + #{full_plural_klass_name}.new(@maker) + end + + def setup_#{full_plural_name}(rss, current) + @#{full_plural_name}.to_rss(rss, current) + end + + def #{full_name} + @#{full_plural_name}[0] and @#{full_plural_name}[0].value + end + + def #{full_name}=(new_value) + @#{full_plural_name}[0] = #{full_klass_name}.new(self) + @#{full_plural_name}[0].value = new_value + end +EOC + end + end + + ::RSS::DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name| + plural_name ||= "#{name}s" + klass_name = Utils.to_class_name(name) + plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}" + module_eval(<<-EOC, __FILE__, __LINE__) + class #{plural_klass_name}Base + include Base + + def_array_element(#{plural_name.dump}) + + def new_#{name} + #{name} = self.class::#{klass_name}.new(self) + @#{plural_name} << #{name} + if block_given? + yield #{name} + else + #{name} + end + end + + def to_rss(rss, current) + @#{plural_name}.each do |#{name}| + #{name}.to_rss(rss, current) + end + end + + class #{klass_name}Base + include Base + + attr_accessor :value + add_need_initialize_variable("value") + alias_method(:content, :value) + alias_method(:content=, :value=) + + def have_required_values? + @value + end + end + end + EOC + end + + def self.install_dublin_core(klass) + ::RSS::DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name| + plural_name ||= "#{name}s" + klass_name = Utils.to_class_name(name) + plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}" + full_klass_name = "DublinCore#{klass_name}" + klass.module_eval(<<-EOC, *Utils.get_file_and_line_from_caller(1)) + class #{plural_klass_name} < #{plural_klass_name}Base + class #{klass_name} < #{klass_name}Base + def to_rss(rss, current) + if value and current.respond_to?(:dc_#{name}) + new_item = current.class::#{full_klass_name}.new(value) + current.dc_#{plural_name} << new_item + end + end + end + end +EOC + end + end + end + + class ChannelBase + include DublinCoreModel + + remove_method(:date) + remove_method(:date=) + alias_method(:date, :dc_date) + alias_method(:date=, :dc_date=) + end + + class ImageBase; include DublinCoreModel; end + class ItemsBase + class ItemBase + include DublinCoreModel + + remove_method(:date) + remove_method(:date=) + alias_method(:date, :dc_date) + alias_method(:date=, :dc_date=) + end + end + class TextinputBase; include DublinCoreModel; end + + class RSS10 + class Channel + DublinCoreModel.install_dublin_core(self) + end + + class Image + DublinCoreModel.install_dublin_core(self) + end + + class Items + class Item + DublinCoreModel.install_dublin_core(self) + end + end + + class Textinput + DublinCoreModel.install_dublin_core(self) + end + end + + class RSS09 + class Channel + DublinCoreModel.install_dublin_core(self) + end + + class Image + DublinCoreModel.install_dublin_core(self) + end + + class Items + class Item + DublinCoreModel.install_dublin_core(self) + end + end + + class Textinput + DublinCoreModel.install_dublin_core(self) + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/image.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/image.rb new file mode 100644 index 0000000000..ed51c8ecba --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/image.rb @@ -0,0 +1,145 @@ +require 'rss/image' +require 'rss/maker/1.0' +require 'rss/maker/dublincore' + +module RSS + module Maker + module ImageItemModel + def self.append_features(klass) + super + + name = "#{RSS::IMAGE_PREFIX}_item" + klass.add_need_initialize_variable(name, "make_#{name}") + klass.add_other_element(name) + klass.module_eval(<<-EOC, __FILE__, __LINE__+1) + attr_reader :#{name} + def setup_#{name}(rss, current) + if @#{name} + @#{name}.to_rss(rss, current) + end + end + + def make_#{name} + self.class::#{Utils.to_class_name(name)}.new(@maker) + end +EOC + end + + class ImageItemBase + include Base + include Maker::DublinCoreModel + + attr_accessor :about, :resource, :image_width, :image_height + add_need_initialize_variable("about") + add_need_initialize_variable("resource") + add_need_initialize_variable("image_width") + add_need_initialize_variable("image_height") + alias width= image_width= + alias width image_width + alias height= image_height= + alias height image_height + + def have_required_values? + @about + end + end + end + + module ImageFaviconModel + def self.append_features(klass) + super + + name = "#{RSS::IMAGE_PREFIX}_favicon" + klass.add_need_initialize_variable(name, "make_#{name}") + klass.add_other_element(name) + klass.module_eval(<<-EOC, __FILE__, __LINE__+1) + attr_reader :#{name} + def setup_#{name}(rss, current) + if @#{name} + @#{name}.to_rss(rss, current) + end + end + + def make_#{name} + self.class::#{Utils.to_class_name(name)}.new(@maker) + end +EOC + end + + class ImageFaviconBase + include Base + include Maker::DublinCoreModel + + attr_accessor :about, :image_size + add_need_initialize_variable("about") + add_need_initialize_variable("image_size") + alias size image_size + alias size= image_size= + + def have_required_values? + @about and @image_size + end + end + end + + class ChannelBase; include Maker::ImageFaviconModel; end + + class ItemsBase + class ItemBase; include Maker::ImageItemModel; end + end + + class RSS10 + class Items + class Item + class ImageItem < ImageItemBase + DublinCoreModel.install_dublin_core(self) + def to_rss(rss, current) + if @about + item = ::RSS::ImageItemModel::ImageItem.new(@about, @resource) + setup_values(item) + setup_other_elements(item) + current.image_item = item + end + end + end + end + end + + class Channel + class ImageFavicon < ImageFaviconBase + DublinCoreModel.install_dublin_core(self) + def to_rss(rss, current) + if @about and @image_size + args = [@about, @image_size] + favicon = ::RSS::ImageFaviconModel::ImageFavicon.new(*args) + setup_values(favicon) + setup_other_elements(favicon) + current.image_favicon = favicon + end + end + end + end + end + + class RSS09 + class Items + class Item + class ImageItem < ImageItemBase + DublinCoreModel.install_dublin_core(self) + def to_rss(*args) + end + end + end + end + + class Channel + class ImageFavicon < ImageFaviconBase + DublinCoreModel.install_dublin_core(self) + def to_rss(*args) + end + end + end + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/syndication.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/syndication.rb new file mode 100644 index 0000000000..3717086257 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/syndication.rb @@ -0,0 +1,27 @@ +require 'rss/syndication' +require 'rss/maker/1.0' + +module RSS + module Maker + module SyndicationModel + def self.append_features(klass) + super + + ::RSS::SyndicationModel::ELEMENTS.each do |element| + klass.add_need_initialize_variable(element) + klass.add_other_element(element) + klass.module_eval(<<-EOC, __FILE__, __LINE__+1) + attr_accessor :#{element} + def setup_#{element}(rss, current) + if #{element} and current.respond_to?(:#{element}=) + current.#{element} = @#{element} if @#{element} + end + end + EOC + end + end + end + + class ChannelBase; include SyndicationModel; end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/taxonomy.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/taxonomy.rb new file mode 100644 index 0000000000..f272996581 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/taxonomy.rb @@ -0,0 +1,182 @@ +require 'rss/taxonomy' +require 'rss/maker/1.0' +require 'rss/maker/dublincore' + +module RSS + module Maker + module TaxonomyTopicsModel + def self.append_features(klass) + super + + klass.add_need_initialize_variable("taxo_topics", "make_taxo_topics") + klass.add_other_element("taxo_topics") + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) + attr_reader :taxo_topics + def make_taxo_topics + self.class::TaxonomyTopics.new(@maker) + end + + def setup_taxo_topics(rss, current) + @taxo_topics.to_rss(rss, current) + end +EOC + end + + def self.install_taxo_topics(klass) + klass.module_eval(<<-EOC, *Utils.get_file_and_line_from_caller(1)) + class TaxonomyTopics < TaxonomyTopicsBase + def to_rss(rss, current) + if current.respond_to?(:taxo_topics) + topics = current.class::TaxonomyTopics.new + bag = topics.Bag + @resources.each do |resource| + bag.lis << RDF::Bag::Li.new(resource) + end + current.taxo_topics = topics + end + end + end +EOC + end + + class TaxonomyTopicsBase + include Base + + attr_reader :resources + def_array_element("resources") + end + end + + module TaxonomyTopicModel + def self.append_features(klass) + super + + klass.add_need_initialize_variable("taxo_topics", "make_taxo_topics") + klass.add_other_element("taxo_topics") + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) + attr_reader :taxo_topics + def make_taxo_topics + self.class::TaxonomyTopics.new(@maker) + end + + def setup_taxo_topics(rss, current) + @taxo_topics.to_rss(rss, current) + end + + def taxo_topic + @taxo_topics[0] and @taxo_topics[0].value + end + + def taxo_topic=(new_value) + @taxo_topic[0] = self.class::TaxonomyTopic.new(self) + @taxo_topic[0].value = new_value + end +EOC + end + + def self.install_taxo_topic(klass) + klass.module_eval(<<-EOC, *Utils.get_file_and_line_from_caller(1)) + class TaxonomyTopics < TaxonomyTopicsBase + class TaxonomyTopic < TaxonomyTopicBase + DublinCoreModel.install_dublin_core(self) + TaxonomyTopicsModel.install_taxo_topics(self) + + def to_rss(rss, current) + if current.respond_to?(:taxo_topics) + topic = current.class::TaxonomyTopic.new(value) + topic.taxo_link = value + taxo_topics.to_rss(rss, topic) if taxo_topics + current.taxo_topics << topic + setup_other_elements(rss) + end + end + + def current_element(rss) + super.taxo_topics.last + end + end + end +EOC + end + + class TaxonomyTopicsBase + include Base + + def_array_element("taxo_topics") + + def new_taxo_topic + taxo_topic = self.class::TaxonomyTopic.new(self) + @taxo_topics << taxo_topic + if block_given? + yield taxo_topic + else + taxo_topic + end + end + + def to_rss(rss, current) + @taxo_topics.each do |taxo_topic| + taxo_topic.to_rss(rss, current) + end + end + + class TaxonomyTopicBase + include Base + include DublinCoreModel + include TaxonomyTopicsModel + + attr_accessor :value + add_need_initialize_variable("value") + alias_method(:taxo_link, :value) + alias_method(:taxo_link=, :value=) + + def have_required_values? + @value + end + end + end + end + + class RSSBase + include TaxonomyTopicModel + end + + class ChannelBase + include TaxonomyTopicsModel + end + + class ItemsBase + class ItemBase + include TaxonomyTopicsModel + end + end + + class RSS10 + TaxonomyTopicModel.install_taxo_topic(self) + + class Channel + TaxonomyTopicsModel.install_taxo_topics(self) + end + + class Items + class Item + TaxonomyTopicsModel.install_taxo_topics(self) + end + end + end + + class RSS09 + TaxonomyTopicModel.install_taxo_topic(self) + + class Channel + TaxonomyTopicsModel.install_taxo_topics(self) + end + + class Items + class Item + TaxonomyTopicsModel.install_taxo_topics(self) + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/trackback.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/trackback.rb new file mode 100644 index 0000000000..4ae6164f68 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/maker/trackback.rb @@ -0,0 +1,130 @@ +require 'rss/trackback' +require 'rss/maker/1.0' +require 'rss/maker/2.0' + +module RSS + module Maker + module TrackBackModel + def self.append_features(klass) + super + + name = "#{RSS::TRACKBACK_PREFIX}_ping" + klass.add_need_initialize_variable(name) + klass.add_other_element(name) + klass.module_eval(<<-EOC, __FILE__, __LINE__+1) + attr_accessor :#{name} + def setup_#{name}(rss, current) + if #{name} and current.respond_to?(:#{name}=) + current.#{name} = #{name} + end + end + EOC + + name = "#{RSS::TRACKBACK_PREFIX}_abouts" + klass.add_need_initialize_variable(name, "make_#{name}") + klass.add_other_element(name) + klass.module_eval(<<-EOC, __FILE__, __LINE__+1) + attr_accessor :#{name} + def make_#{name} + self.class::TrackBackAbouts.new(self) + end + + def setup_#{name}(rss, current) + @#{name}.to_rss(rss, current) + end + EOC + end + + class TrackBackAboutsBase + include Base + + def_array_element("abouts") + + def new_about + about = self.class::TrackBackAbout.new(@maker) + @abouts << about + if block_given? + yield about + else + about + end + end + + def to_rss(rss, current) + @abouts.each do |about| + about.to_rss(rss, current) + end + end + + class TrackBackAboutBase + include Base + + attr_accessor :value + add_need_initialize_variable("value") + + alias_method(:resource, :value) + alias_method(:resource=, :value=) + alias_method(:content, :value) + alias_method(:content=, :value=) + + def have_required_values? + @value + end + + end + end + end + + class ItemsBase + class ItemBase; include TrackBackModel; end + end + + class RSS10 + class Items + class Item + class TrackBackAbouts < TrackBackAboutsBase + class TrackBackAbout < TrackBackAboutBase + def to_rss(rss, current) + if resource + about = ::RSS::TrackBackModel10::TrackBackAbout.new(resource) + current.trackback_abouts << about + end + end + end + end + end + end + end + + class RSS09 + class Items + class Item + class TrackBackAbouts < TrackBackAboutsBase + def to_rss(*args) + end + class TrackBackAbout < TrackBackAboutBase + end + end + end + end + end + + class RSS20 + class Items + class Item + class TrackBackAbouts < TrackBackAboutsBase + class TrackBackAbout < TrackBackAboutBase + def to_rss(rss, current) + if content + about = ::RSS::TrackBackModel20::TrackBackAbout.new(content) + current.trackback_abouts << about + end + end + end + end + end + end + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/parser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/parser.rb new file mode 100644 index 0000000000..033bc123aa --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/parser.rb @@ -0,0 +1,476 @@ +require "forwardable" +require "open-uri" + +require "rss/rss" + +module RSS + + class NotWellFormedError < Error + attr_reader :line, :element + + # Create a new NotWellFormedError for an error at +line+ + # in +element+. If a block is given the return value of + # the block ends up in the error message. + def initialize(line=nil, element=nil) + message = "This is not well formed XML" + if element or line + message << "\nerror occurred" + message << " in #{element}" if element + message << " at about #{line} line" if line + end + message << "\n#{yield}" if block_given? + super(message) + end + end + + class XMLParserNotFound < Error + def initialize + super("available XML parser was not found in " << + "#{AVAILABLE_PARSER_LIBRARIES.inspect}.") + end + end + + class NotValidXMLParser < Error + def initialize(parser) + super("#{parser} is not an available XML parser. " << + "Available XML parser"<< + (AVAILABLE_PARSERS.size > 1 ? "s are ": " is ") << + "#{AVAILABLE_PARSERS.inspect}.") + end + end + + class NSError < InvalidRSSError + attr_reader :tag, :prefix, :uri + def initialize(tag, prefix, require_uri) + @tag, @prefix, @uri = tag, prefix, require_uri + super("prefix <#{prefix}> doesn't associate uri " << + "<#{require_uri}> in tag <#{tag}>") + end + end + + class Parser + + extend Forwardable + + class << self + + @@default_parser = nil + + def default_parser + @@default_parser || AVAILABLE_PARSERS.first + end + + # Set @@default_parser to new_value if it is one of the + # available parsers. Else raise NotValidXMLParser error. + def default_parser=(new_value) + if AVAILABLE_PARSERS.include?(new_value) + @@default_parser = new_value + else + raise NotValidXMLParser.new(new_value) + end + end + + def parse(rss, do_validate=true, ignore_unknown_element=true, + parser_class=default_parser) + parser = new(rss, parser_class) + parser.do_validate = do_validate + parser.ignore_unknown_element = ignore_unknown_element + parser.parse + end + end + + def_delegators(:@parser, :parse, :rss, + :ignore_unknown_element, + :ignore_unknown_element=, :do_validate, + :do_validate=) + + def initialize(rss, parser_class=self.class.default_parser) + @parser = parser_class.new(normalize_rss(rss)) + end + + private + + # Try to get the XML associated with +rss+. + # Return +rss+ if it already looks like XML, or treat it as a URI, + # or a file to get the XML, + def normalize_rss(rss) + return rss if maybe_xml?(rss) + + uri = to_uri(rss) + + if uri.respond_to?(:read) + uri.read + elsif !rss.tainted? and File.readable?(rss) + File.open(rss) {|f| f.read} + else + rss + end + end + + # maybe_xml? tests if source is a string that looks like XML. + def maybe_xml?(source) + source.is_a?(String) and / [2, 5]) < 0 + raise LoadError, "needs REXML 2.5 or later (#{REXML::Version})" +end + +module RSS + + class REXMLParser < BaseParser + + class << self + def listener + REXMLListener + end + end + + private + def _parse + begin + REXML::Document.parse_stream(@rss, @listener) + rescue RuntimeError => e + raise NotWellFormedError.new{e.message} + rescue REXML::ParseException => e + context = e.context + line = context[0] if context + raise NotWellFormedError.new(line){e.message} + end + end + + end + + class REXMLListener < BaseListener + + include REXML::StreamListener + include ListenerMixin + + class << self + def raise_for_undefined_entity? + false + end + end + + def xmldecl(version, encoding, standalone) + super(version, encoding, standalone == "yes") + # Encoding is converted to UTF-8 when REXML parse XML. + @encoding = 'UTF-8' + end + + alias_method(:cdata, :text) + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/rss.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/rss.rb new file mode 100644 index 0000000000..a06985af94 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/rss.rb @@ -0,0 +1,1015 @@ +require "time" + +class Time + class << self + unless respond_to?(:w3cdtf) + def w3cdtf(date) + if /\A\s* + (-?\d+)-(\d\d)-(\d\d) + (?:T + (\d\d):(\d\d)(?::(\d\d))? + (\.\d+)? + (Z|[+-]\d\d:\d\d)?)? + \s*\z/ix =~ date and (($5 and $8) or (!$5 and !$8)) + datetime = [$1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i] + datetime << $7.to_f * 1000000 if $7 + if $8 + Time.utc(*datetime) - zone_offset($8) + else + Time.local(*datetime) + end + else + raise ArgumentError.new("invalid date: #{date.inspect}") + end + end + end + end + + unless instance_methods.include?("w3cdtf") + alias w3cdtf iso8601 + end +end + +require "English" +require "rss/utils" +require "rss/converter" +require "rss/xml-stylesheet" + +module RSS + + VERSION = "0.1.6" + + URI = "http://purl.org/rss/1.0/" + + DEBUG = false + + class Error < StandardError; end + + class OverlappedPrefixError < Error + attr_reader :prefix + def initialize(prefix) + @prefix = prefix + end + end + + class InvalidRSSError < Error; end + + class MissingTagError < InvalidRSSError + attr_reader :tag, :parent + def initialize(tag, parent) + @tag, @parent = tag, parent + super("tag <#{tag}> is missing in tag <#{parent}>") + end + end + + class TooMuchTagError < InvalidRSSError + attr_reader :tag, :parent + def initialize(tag, parent) + @tag, @parent = tag, parent + super("tag <#{tag}> is too much in tag <#{parent}>") + end + end + + class MissingAttributeError < InvalidRSSError + attr_reader :tag, :attribute + def initialize(tag, attribute) + @tag, @attribute = tag, attribute + super("attribute <#{attribute}> is missing in tag <#{tag}>") + end + end + + class UnknownTagError < InvalidRSSError + attr_reader :tag, :uri + def initialize(tag, uri) + @tag, @uri = tag, uri + super("tag <#{tag}> is unknown in namespace specified by uri <#{uri}>") + end + end + + class NotExpectedTagError < InvalidRSSError + attr_reader :tag, :uri, :parent + def initialize(tag, uri, parent) + @tag, @uri, @parent = tag, uri, parent + super("tag <{#{uri}}#{tag}> is not expected in tag <#{parent}>") + end + end + # For backward compatibility :X + NotExceptedTagError = NotExpectedTagError + + class NotAvailableValueError < InvalidRSSError + attr_reader :tag, :value, :attribute + def initialize(tag, value, attribute=nil) + @tag, @value, @attribute = tag, value, attribute + message = "value <#{value}> of " + message << "attribute <#{attribute}> of " if attribute + message << "tag <#{tag}> is not available." + super(message) + end + end + + class UnknownConversionMethodError < Error + attr_reader :to, :from + def initialize(to, from) + @to = to + @from = from + super("can't convert to #{to} from #{from}.") + end + end + # for backward compatibility + UnknownConvertMethod = UnknownConversionMethodError + + class ConversionError < Error + attr_reader :string, :to, :from + def initialize(string, to, from) + @string = string + @to = to + @from = from + super("can't convert #{@string} to #{to} from #{from}.") + end + end + + class NotSetError < Error + attr_reader :name, :variables + def initialize(name, variables) + @name = name + @variables = variables + super("required variables of #{@name} are not set: #{@variables.join(', ')}") + end + end + + module BaseModel + + include Utils + + def install_have_child_element(tag_name, uri, occurs, name=nil) + name ||= tag_name + add_need_initialize_variable(name) + install_model(tag_name, uri, occurs, name) + + attr_accessor name + install_element(name) do |n, elem_name| + <<-EOC + if @#{n} + "\#{@#{n}.to_s(need_convert, indent)}" + else + '' + end +EOC + end + end + alias_method(:install_have_attribute_element, :install_have_child_element) + + def install_have_children_element(tag_name, uri, occurs, name=nil, plural_name=nil) + name ||= tag_name + plural_name ||= "#{name}s" + add_have_children_element(name, plural_name) + add_plural_form(name, plural_name) + install_model(tag_name, uri, occurs, plural_name) + + def_children_accessor(name, plural_name) + install_element(name, "s") do |n, elem_name| + <<-EOC + rv = [] + @#{n}.each do |x| + value = "\#{x.to_s(need_convert, indent)}" + rv << value if /\\A\\s*\\z/ !~ value + end + rv.join("\n") +EOC + end + end + + def install_text_element(tag_name, uri, occurs, name=nil, type=nil, disp_name=nil) + name ||= tag_name + disp_name ||= name + self::ELEMENTS << name + add_need_initialize_variable(name) + install_model(tag_name, uri, occurs, name) + + def_corresponded_attr_writer name, type, disp_name + convert_attr_reader name + install_element(name) do |n, elem_name| + <<-EOC + if @#{n} + rv = "\#{indent}<#{elem_name}>" + value = html_escape(@#{n}) + if need_convert + rv << convert(value) + else + rv << value + end + rv << "" + rv + else + '' + end +EOC + end + end + + def install_date_element(tag_name, uri, occurs, name=nil, type=nil, disp_name=nil) + name ||= tag_name + type ||= :w3cdtf + disp_name ||= name + self::ELEMENTS << name + add_need_initialize_variable(name) + install_model(tag_name, uri, occurs, name) + + # accessor + convert_attr_reader name + date_writer(name, type, disp_name) + + install_element(name) do |n, elem_name| + <<-EOC + if @#{n} + rv = "\#{indent}<#{elem_name}>" + value = html_escape(@#{n}.#{type}) + if need_convert + rv << convert(value) + else + rv << value + end + rv << "" + rv + else + '' + end +EOC + end + + end + + private + def install_element(name, postfix="") + elem_name = name.sub('_', ':') + method_name = "#{name}_element#{postfix}" + add_to_element_method(method_name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{method_name}(need_convert=true, indent='') + #{yield(name, elem_name)} + end + private :#{method_name} +EOC + end + + def convert_attr_reader(*attrs) + attrs.each do |attr| + attr = attr.id2name if attr.kind_of?(Integer) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{attr} + if @converter + @converter.convert(@#{attr}) + else + @#{attr} + end + end +EOC + end + end + + def date_writer(name, type, disp_name=name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{name}=(new_value) + if new_value.nil? or new_value.kind_of?(Time) + @#{name} = new_value + else + if @do_validate + begin + @#{name} = Time.__send__('#{type}', new_value) + rescue ArgumentError + raise NotAvailableValueError.new('#{disp_name}', new_value) + end + else + @#{name} = nil + if /\\A\\s*\\z/ !~ new_value.to_s + begin + @#{name} = Time.parse(new_value) + rescue ArgumentError + end + end + end + end + + # Is it need? + if @#{name} + class << @#{name} + undef_method(:to_s) + alias_method(:to_s, :#{type}) + end + end + + end +EOC + end + + def integer_writer(name, disp_name=name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{name}=(new_value) + if new_value.nil? + @#{name} = new_value + else + if @do_validate + begin + @#{name} = Integer(new_value) + rescue ArgumentError + raise NotAvailableValueError.new('#{disp_name}', new_value) + end + else + @#{name} = new_value.to_i + end + end + end +EOC + end + + def positive_integer_writer(name, disp_name=name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{name}=(new_value) + if new_value.nil? + @#{name} = new_value + else + if @do_validate + begin + tmp = Integer(new_value) + raise ArgumentError if tmp <= 0 + @#{name} = tmp + rescue ArgumentError + raise NotAvailableValueError.new('#{disp_name}', new_value) + end + else + @#{name} = new_value.to_i + end + end + end +EOC + end + + def boolean_writer(name, disp_name=name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{name}=(new_value) + if new_value.nil? + @#{name} = new_value + else + if @do_validate and + ![true, false, "true", "false"].include?(new_value) + raise NotAvailableValueError.new('#{disp_name}', new_value) + end + if [true, false].include?(new_value) + @#{name} = new_value + else + @#{name} = new_value == "true" + end + end + end +EOC + end + + def def_children_accessor(accessor_name, plural_name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{plural_name} + @#{accessor_name} + end + + def #{accessor_name}(*args) + if args.empty? + @#{accessor_name}.first + else + @#{accessor_name}[*args] + end + end + + def #{accessor_name}=(*args) + warn("Warning:\#{caller.first.sub(/:in `.*'\z/, '')}: " \ + "Don't use `#{accessor_name} = XXX'/`set_#{accessor_name}(XXX)'. " \ + "Those APIs are not sense of Ruby. " \ + "Use `#{plural_name} << XXX' instead of them.") + if args.size == 1 + @#{accessor_name}.push(args[0]) + else + @#{accessor_name}.__send__("[]=", *args) + end + end + alias_method(:set_#{accessor_name}, :#{accessor_name}=) +EOC + end + end + + class Element + + extend BaseModel + include Utils + + INDENT = " " + + MUST_CALL_VALIDATORS = {} + MODELS = [] + GET_ATTRIBUTES = [] + HAVE_CHILDREN_ELEMENTS = [] + TO_ELEMENT_METHODS = [] + NEED_INITIALIZE_VARIABLES = [] + PLURAL_FORMS = {} + + class << self + + def must_call_validators + MUST_CALL_VALIDATORS + end + def models + MODELS + end + def get_attributes + GET_ATTRIBUTES + end + def have_children_elements + HAVE_CHILDREN_ELEMENTS + end + def to_element_methods + TO_ELEMENT_METHODS + end + def need_initialize_variables + NEED_INITIALIZE_VARIABLES + end + def plural_forms + PLURAL_FORMS + end + + + def inherited(klass) + klass.const_set("MUST_CALL_VALIDATORS", {}) + klass.const_set("MODELS", []) + klass.const_set("GET_ATTRIBUTES", []) + klass.const_set("HAVE_CHILDREN_ELEMENTS", []) + klass.const_set("TO_ELEMENT_METHODS", []) + klass.const_set("NEED_INITIALIZE_VARIABLES", []) + klass.const_set("PLURAL_FORMS", {}) + + klass.module_eval(<<-EOC) + public + + @tag_name = name.split(/::/).last + @tag_name[0,1] = @tag_name[0,1].downcase + @have_content = false + + def self.must_call_validators + super.merge(MUST_CALL_VALIDATORS) + end + def self.models + MODELS + super + end + def self.get_attributes + GET_ATTRIBUTES + super + end + def self.have_children_elements + HAVE_CHILDREN_ELEMENTS + super + end + def self.to_element_methods + TO_ELEMENT_METHODS + super + end + def self.need_initialize_variables + NEED_INITIALIZE_VARIABLES + super + end + def self.plural_forms + super.merge(PLURAL_FORMS) + end + + + def self.install_must_call_validator(prefix, uri) + MUST_CALL_VALIDATORS[uri] = prefix + end + + def self.install_model(tag, uri, occurs=nil, getter=nil) + getter ||= tag + if m = MODELS.find {|t, u, o, g| t == tag and u == uri} + m[2] = occurs + else + MODELS << [tag, uri, occurs, getter] + end + end + + def self.install_get_attribute(name, uri, required=true, + type=nil, disp_name=nil, + element_name=nil) + disp_name ||= name + element_name ||= name + def_corresponded_attr_writer name, type, disp_name + convert_attr_reader name + if type == :boolean and /^is/ =~ name + alias_method "\#{$POSTMATCH}?", name + end + GET_ATTRIBUTES << [name, uri, required, element_name] + add_need_initialize_variable(disp_name) + end + + def self.def_corresponded_attr_writer(name, type=nil, disp_name=name) + case type + when :integer + integer_writer name, disp_name + when :positive_integer + positive_integer_writer name, disp_name + when :boolean + boolean_writer name, disp_name + when :w3cdtf, :rfc822, :rfc2822 + date_writer name, type, disp_name + else + attr_writer name + end + end + + def self.content_setup(type=nil) + def_corresponded_attr_writer "content", type + convert_attr_reader :content + @have_content = true + end + + def self.have_content? + @have_content + end + + def self.add_have_children_element(variable_name, plural_name) + HAVE_CHILDREN_ELEMENTS << [variable_name, plural_name] + end + + def self.add_to_element_method(method_name) + TO_ELEMENT_METHODS << method_name + end + + def self.add_need_initialize_variable(variable_name) + NEED_INITIALIZE_VARIABLES << variable_name + end + + def self.add_plural_form(singular, plural) + PLURAL_FORMS[singular] = plural + end + + EOC + end + + def required_prefix + nil + end + + def required_uri + "" + end + + def install_ns(prefix, uri) + if self::NSPOOL.has_key?(prefix) + raise OverlappedPrefixError.new(prefix) + end + self::NSPOOL[prefix] = uri + end + + def tag_name + @tag_name + end + end + + attr_accessor :do_validate + + def initialize(do_validate=true, attrs={}) + @converter = nil + @do_validate = do_validate + initialize_variables(attrs) + end + + def tag_name + self.class.tag_name + end + + def full_name + tag_name + end + + def converter=(converter) + @converter = converter + targets = children.dup + self.class.have_children_elements.each do |variable_name, plural_name| + targets.concat(__send__(plural_name)) + end + targets.each do |target| + target.converter = converter unless target.nil? + end + end + + def convert(value) + if @converter + @converter.convert(value) + else + value + end + end + + def validate(ignore_unknown_element=true) + validate_attribute + __validate(ignore_unknown_element) + end + + def validate_for_stream(tags, ignore_unknown_element=true) + validate_attribute + __validate(ignore_unknown_element, tags, false) + end + + def setup_maker(maker) + target = maker_target(maker) + unless target.nil? + setup_maker_attributes(target) + setup_maker_element(target) + setup_maker_elements(target) + end + end + + def to_s(need_convert=true, indent='') + if self.class.have_content? + return "" unless @content + rv = tag(indent) do |next_indent| + h(@content) + end + else + rv = tag(indent) do |next_indent| + self.class.to_element_methods.collect do |method_name| + __send__(method_name, false, next_indent) + end + end + end + rv = convert(rv) if need_convert + rv + end + + private + def initialize_variables(attrs) + normalized_attrs = {} + attrs.each do |key, value| + normalized_attrs[key.to_s] = value + end + self.class.need_initialize_variables.each do |variable_name| + value = normalized_attrs[variable_name.to_s] + if value + __send__("#{variable_name}=", value) + else + instance_eval("@#{variable_name} = nil") + end + end + initialize_have_children_elements + @content = "" if self.class.have_content? + end + + def initialize_have_children_elements + self.class.have_children_elements.each do |variable_name, plural_name| + instance_eval("@#{variable_name} = []") + end + end + + def tag(indent, additional_attrs={}, &block) + next_indent = indent + INDENT + + attrs = collect_attrs + return "" if attrs.nil? + + attrs.update(additional_attrs) + start_tag = make_start_tag(indent, next_indent, attrs) + + if block + content = block.call(next_indent) + else + content = [] + end + + if content.is_a?(String) + content = [content] + start_tag << ">" + end_tag = "" + else + content = content.reject{|x| x.empty?} + if content.empty? + end_tag = "/>" + else + start_tag << ">\n" + end_tag = "\n#{indent}" + end + end + + start_tag + content.join("\n") + end_tag + end + + def make_start_tag(indent, next_indent, attrs) + start_tag = ["#{indent}<#{full_name}"] + unless attrs.empty? + start_tag << attrs.collect do |key, value| + %Q[#{h key}="#{h value}"] + end.join("\n#{next_indent}") + end + start_tag.join(" ") + end + + def collect_attrs + attrs = {} + _attrs.each do |name, required, alias_name| + value = __send__(alias_name || name) + return nil if required and value.nil? + next if value.nil? + return nil if attrs.has_key?(name) + attrs[name] = value + end + attrs + end + + def tag_name_with_prefix(prefix) + "#{prefix}:#{tag_name}" + end + + # For backward compatibility + def calc_indent + '' + end + + def maker_target(maker) + nil + end + + def setup_maker_attributes(target) + end + + def setup_maker_element(target) + self.class.need_initialize_variables.each do |var| + value = __send__(var) + if value.respond_to?("setup_maker") and + !not_need_to_call_setup_maker_variables.include?(var) + value.setup_maker(target) + else + setter = "#{var}=" + if target.respond_to?(setter) + target.__send__(setter, value) + end + end + end + end + + def not_need_to_call_setup_maker_variables + [] + end + + def setup_maker_elements(parent) + self.class.have_children_elements.each do |name, plural_name| + if parent.respond_to?(plural_name) + target = parent.__send__(plural_name) + __send__(plural_name).each do |elem| + elem.setup_maker(target) + end + end + end + end + + def set_next_element(tag_name, next_element) + klass = next_element.class + prefix = "" + prefix << "#{klass.required_prefix}_" if klass.required_prefix + key = "#{prefix}#{tag_name}" + if self.class.plural_forms.has_key?(key) + ary = __send__("#{self.class.plural_forms[key]}") + ary << next_element + else + __send__("#{prefix}#{tag_name}=", next_element) + end + end + + def children + rv = [] + self.class.models.each do |name, uri, occurs, getter| + value = __send__(getter) + next if value.nil? + value = [value] unless value.is_a?(Array) + value.each do |v| + rv << v if v.is_a?(Element) + end + end + rv + end + + def _tags + rv = [] + self.class.models.each do |name, uri, occurs, getter| + value = __send__(getter) + next if value.nil? + if value.is_a?(Array) + rv.concat([[uri, name]] * value.size) + else + rv << [uri, name] + end + end + rv + end + + def _attrs + self.class.get_attributes.collect do |name, uri, required, element_name| + [element_name, required, name] + end + end + + def __validate(ignore_unknown_element, tags=_tags, recursive=true) + if recursive + children.compact.each do |child| + child.validate + end + end + must_call_validators = self.class.must_call_validators + tags = tag_filter(tags.dup) + p tags if DEBUG + must_call_validators.each do |uri, prefix| + _validate(ignore_unknown_element, tags[uri], uri) + meth = "#{prefix}_validate" + if respond_to?(meth, true) + __send__(meth, ignore_unknown_element, tags[uri], uri) + end + end + end + + def validate_attribute + _attrs.each do |a_name, required, alias_name| + if required and __send__(alias_name || a_name).nil? + raise MissingAttributeError.new(tag_name, a_name) + end + end + end + + def _validate(ignore_unknown_element, tags, uri, models=self.class.models) + count = 1 + do_redo = false + not_shift = false + tag = nil + models = models.find_all {|model| model[1] == uri} + element_names = models.collect {|model| model[0]} + if tags + tags_size = tags.size + tags = tags.sort_by {|x| element_names.index(x) || tags_size} + end + + models.each_with_index do |model, i| + name, model_uri, occurs, getter = model + + if DEBUG + p "before" + p tags + p model + end + + if not_shift + not_shift = false + elsif tags + tag = tags.shift + end + + if DEBUG + p "mid" + p count + end + + case occurs + when '?' + if count > 2 + raise TooMuchTagError.new(name, tag_name) + else + if name == tag + do_redo = true + else + not_shift = true + end + end + when '*' + if name == tag + do_redo = true + else + not_shift = true + end + when '+' + if name == tag + do_redo = true + else + if count > 1 + not_shift = true + else + raise MissingTagError.new(name, tag_name) + end + end + else + if name == tag + if models[i+1] and models[i+1][0] != name and + tags and tags.first == name + raise TooMuchTagError.new(name, tag_name) + end + else + raise MissingTagError.new(name, tag_name) + end + end + + if DEBUG + p "after" + p not_shift + p do_redo + p tag + end + + if do_redo + do_redo = false + count += 1 + redo + else + count = 1 + end + + end + + if !ignore_unknown_element and !tags.nil? and !tags.empty? + raise NotExpectedTagError.new(tags.first, uri, tag_name) + end + + end + + def tag_filter(tags) + rv = {} + tags.each do |tag| + rv[tag[0]] = [] unless rv.has_key?(tag[0]) + rv[tag[0]].push(tag[1]) + end + rv + end + + end + + module RootElementMixin + + include XMLStyleSheetMixin + + attr_reader :output_encoding + + def initialize(rss_version, version=nil, encoding=nil, standalone=nil) + super() + @rss_version = rss_version + @version = version || '1.0' + @encoding = encoding + @standalone = standalone + @output_encoding = nil + end + + def output_encoding=(enc) + @output_encoding = enc + self.converter = Converter.new(@output_encoding, @encoding) + end + + def setup_maker(maker) + maker.version = version + maker.encoding = encoding + maker.standalone = standalone + + xml_stylesheets.each do |xss| + xss.setup_maker(maker) + end + + setup_maker_elements(maker) + end + + def to_xml(version=nil, &block) + if version.nil? or version == @rss_version + to_s + else + RSS::Maker.make(version) do |maker| + setup_maker(maker) + block.call(maker) if block + end.to_s + end + end + + private + def tag(indent, attrs={}, &block) + rv = xmldecl + xml_stylesheet_pi + rv << super(indent, ns_declarations.merge(attrs), &block) + rv + end + + def xmldecl + rv = %Q[\n" + rv + end + + def ns_declarations + decls = {} + self.class::NSPOOL.collect do |prefix, uri| + prefix = ":#{prefix}" unless prefix.empty? + decls["xmlns#{prefix}"] = uri + end + decls + end + + def setup_maker_elements(maker) + channel.setup_maker(maker) if channel + image.setup_maker(maker) if image + textinput.setup_maker(maker) if textinput + super(maker) + end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/syndication.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/syndication.rb new file mode 100644 index 0000000000..93d35c89a7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/syndication.rb @@ -0,0 +1,64 @@ +require "rss/1.0" + +module RSS + + SY_PREFIX = 'sy' + SY_URI = "http://purl.org/rss/1.0/modules/syndication/" + + RDF.install_ns(SY_PREFIX, SY_URI) + + module SyndicationModel + + extend BaseModel + + ELEMENTS = [] + + def self.append_features(klass) + super + + klass.install_must_call_validator(SY_PREFIX, SY_URI) + klass.module_eval do + [ + ["updatePeriod"], + ["updateFrequency", :positive_integer] + ].each do |name, type| + install_text_element(name, SY_URI, "?", + "#{SY_PREFIX}_#{name}", type, + "#{SY_PREFIX}:#{name}") + end + + %w(updateBase).each do |name| + install_date_element(name, SY_URI, "?", + "#{SY_PREFIX}_#{name}", 'w3cdtf', name) + end + + alias_method(:_sy_updatePeriod=, :sy_updatePeriod=) + def sy_updatePeriod=(new_value) + new_value = new_value.strip + validate_sy_updatePeriod(new_value) if @do_validate + self._sy_updatePeriod = new_value + end + end + end + + private + SY_UPDATEPERIOD_AVAILABLE_VALUES = %w(hourly daily weekly monthly yearly) + def validate_sy_updatePeriod(value) + unless SY_UPDATEPERIOD_AVAILABLE_VALUES.include?(value) + raise NotAvailableValueError.new("updatePeriod", value) + end + end + end + + class RDF + class Channel; include SyndicationModel; end + end + + prefix_size = SY_PREFIX.size + 1 + SyndicationModel::ELEMENTS.uniq! + SyndicationModel::ELEMENTS.each do |full_name| + name = full_name[prefix_size..-1] + BaseListener.install_get_text_element(SY_URI, name, "#{full_name}=") + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/taxonomy.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/taxonomy.rb new file mode 100644 index 0000000000..8caa25e2a4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/taxonomy.rb @@ -0,0 +1,145 @@ +require "rss/1.0" +require "rss/dublincore" + +module RSS + + TAXO_PREFIX = "taxo" + TAXO_URI = "http://purl.org/rss/1.0/modules/taxonomy/" + + RDF.install_ns(TAXO_PREFIX, TAXO_URI) + + TAXO_ELEMENTS = [] + + %w(link).each do |name| + full_name = "#{TAXO_PREFIX}_#{name}" + BaseListener.install_get_text_element(TAXO_URI, name, "#{full_name}=") + TAXO_ELEMENTS << "#{TAXO_PREFIX}_#{name}" + end + + %w(topic topics).each do |name| + class_name = Utils.to_class_name(name) + BaseListener.install_class_name(TAXO_URI, name, "Taxonomy#{class_name}") + TAXO_ELEMENTS << "#{TAXO_PREFIX}_#{name}" + end + + module TaxonomyTopicsModel + extend BaseModel + + def self.append_features(klass) + super + + klass.install_must_call_validator(TAXO_PREFIX, TAXO_URI) + %w(topics).each do |name| + klass.install_have_child_element(name, TAXO_URI, "?", + "#{TAXO_PREFIX}_#{name}") + end + end + + class TaxonomyTopics < Element + include RSS10 + + Bag = ::RSS::RDF::Bag + + class << self + def required_prefix + TAXO_PREFIX + end + + def required_uri + TAXO_URI + end + end + + @tag_name = "topics" + + install_have_child_element("Bag", RDF::URI, nil) + install_must_call_validator('rdf', RDF::URI) + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.Bag = args[0] + end + self.Bag ||= Bag.new + end + + def full_name + tag_name_with_prefix(TAXO_PREFIX) + end + + def maker_target(target) + target.taxo_topics + end + + def resources + if @Bag + @Bag.lis.collect do |li| + li.resource + end + else + [] + end + end + end + end + + module TaxonomyTopicModel + extend BaseModel + + def self.append_features(klass) + super + var_name = "#{TAXO_PREFIX}_topic" + klass.install_have_children_element("topic", TAXO_URI, "*", var_name) + end + + class TaxonomyTopic < Element + include RSS10 + + include DublinCoreModel + include TaxonomyTopicsModel + + class << self + def required_prefix + TAXO_PREFIX + end + + def required_uri + TAXO_URI + end + end + + @tag_name = "topic" + + install_get_attribute("about", ::RSS::RDF::URI, true, nil, nil, + "#{RDF::PREFIX}:about") + install_text_element("link", TAXO_URI, "?", "#{TAXO_PREFIX}_link") + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.about = args[0] + end + end + + def full_name + tag_name_with_prefix(TAXO_PREFIX) + end + + def maker_target(target) + target.new_taxo_topic + end + end + end + + class RDF + include TaxonomyTopicModel + class Channel + include TaxonomyTopicsModel + end + class Item; include TaxonomyTopicsModel; end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/trackback.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/trackback.rb new file mode 100644 index 0000000000..ee2491f332 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/trackback.rb @@ -0,0 +1,288 @@ +require 'rss/1.0' +require 'rss/2.0' + +module RSS + + TRACKBACK_PREFIX = 'trackback' + TRACKBACK_URI = 'http://madskills.com/public/xml/rss/module/trackback/' + + RDF.install_ns(TRACKBACK_PREFIX, TRACKBACK_URI) + Rss.install_ns(TRACKBACK_PREFIX, TRACKBACK_URI) + + module TrackBackUtils + private + def trackback_validate(ignore_unknown_element, tags, uri) + return if tags.nil? + if tags.find {|tag| tag == "about"} and + !tags.find {|tag| tag == "ping"} + raise MissingTagError.new("#{TRACKBACK_PREFIX}:ping", tag_name) + end + end + end + + module BaseTrackBackModel + + ELEMENTS = %w(ping about) + + def append_features(klass) + super + + unless klass.class == Module + klass.module_eval {include TrackBackUtils} + + klass.install_must_call_validator(TRACKBACK_PREFIX, TRACKBACK_URI) + %w(ping).each do |name| + var_name = "#{TRACKBACK_PREFIX}_#{name}" + klass_name = "TrackBack#{Utils.to_class_name(name)}" + klass.install_have_child_element(name, TRACKBACK_URI, "?", var_name) + klass.module_eval(<<-EOC, __FILE__, __LINE__) + remove_method :#{var_name} + def #{var_name} + @#{var_name} and @#{var_name}.value + end + + remove_method :#{var_name}= + def #{var_name}=(value) + @#{var_name} = Utils.new_with_value_if_need(#{klass_name}, value) + end + EOC + end + + [%w(about s)].each do |name, postfix| + var_name = "#{TRACKBACK_PREFIX}_#{name}" + klass_name = "TrackBack#{Utils.to_class_name(name)}" + klass.install_have_children_element(name, TRACKBACK_URI, "*", + var_name) + klass.module_eval(<<-EOC, __FILE__, __LINE__) + remove_method :#{var_name} + def #{var_name}(*args) + if args.empty? + @#{var_name}.first and @#{var_name}.first.value + else + ret = @#{var_name}.__send__("[]", *args) + if ret.is_a?(Array) + ret.collect {|x| x.value} + else + ret.value + end + end + end + + remove_method :#{var_name}= + remove_method :set_#{var_name} + def #{var_name}=(*args) + if args.size == 1 + item = Utils.new_with_value_if_need(#{klass_name}, args[0]) + @#{var_name}.push(item) + else + new_val = args.last + if new_val.is_a?(Array) + new_val = new_value.collect do |val| + Utils.new_with_value_if_need(#{klass_name}, val) + end + else + new_val = Utils.new_with_value_if_need(#{klass_name}, new_val) + end + @#{var_name}.__send__("[]=", *(args[0..-2] + [new_val])) + end + end + alias set_#{var_name} #{var_name}= + EOC + end + end + end + end + + module TrackBackModel10 + extend BaseModel + extend BaseTrackBackModel + + class TrackBackPing < Element + include RSS10 + + class << self + + def required_prefix + TRACKBACK_PREFIX + end + + def required_uri + TRACKBACK_URI + end + + end + + @tag_name = "ping" + + [ + ["resource", ::RSS::RDF::URI, true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required, nil, nil, + "#{::RSS::RDF::PREFIX}:#{name}") + end + + alias_method(:value, :resource) + alias_method(:value=, :resource=) + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.resource = args[0] + end + end + + def full_name + tag_name_with_prefix(TRACKBACK_PREFIX) + end + end + + class TrackBackAbout < Element + include RSS10 + + class << self + + def required_prefix + TRACKBACK_PREFIX + end + + def required_uri + TRACKBACK_URI + end + + end + + @tag_name = "about" + + [ + ["resource", ::RSS::RDF::URI, true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required, nil, nil, + "#{::RSS::RDF::PREFIX}:#{name}") + end + + alias_method(:value, :resource) + alias_method(:value=, :resource=) + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.resource = args[0] + end + end + + def full_name + tag_name_with_prefix(TRACKBACK_PREFIX) + end + + private + def maker_target(abouts) + abouts.new_about + end + + def setup_maker_attributes(about) + about.resource = self.resource + end + + end + end + + module TrackBackModel20 + extend BaseModel + extend BaseTrackBackModel + + class TrackBackPing < Element + include RSS09 + + @tag_name = "ping" + + content_setup + + class << self + + def required_prefix + TRACKBACK_PREFIX + end + + def required_uri + TRACKBACK_URI + end + + end + + alias_method(:value, :content) + alias_method(:value=, :content=) + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.content = args[0] + end + end + + def full_name + tag_name_with_prefix(TRACKBACK_PREFIX) + end + + end + + class TrackBackAbout < Element + include RSS09 + + @tag_name = "about" + + content_setup + + class << self + + def required_prefix + TRACKBACK_PREFIX + end + + def required_uri + TRACKBACK_URI + end + + end + + alias_method(:value, :content) + alias_method(:value=, :content=) + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.content = args[0] + end + end + + def full_name + tag_name_with_prefix(TRACKBACK_PREFIX) + end + + end + end + + class RDF + class Item; include TrackBackModel10; end + end + + class Rss + class Channel + class Item; include TrackBackModel20; end + end + end + + BaseTrackBackModel::ELEMENTS.each do |name| + class_name = Utils.to_class_name(name) + BaseListener.install_class_name(TRACKBACK_URI, name, + "TrackBack#{class_name}") + end + + BaseTrackBackModel::ELEMENTS.collect! {|name| "#{TRACKBACK_PREFIX}_#{name}"} +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/utils.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/utils.rb new file mode 100644 index 0000000000..b242a72292 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/utils.rb @@ -0,0 +1,37 @@ +module RSS + module Utils + module_function + + # Convert a name_with_underscores to CamelCase. + def to_class_name(name) + name.split(/_/).collect do |part| + "#{part[0, 1].upcase}#{part[1..-1]}" + end.join("") + end + + def get_file_and_line_from_caller(i=0) + file, line, = caller[i].split(':') + [file, line.to_i] + end + + # escape '&', '"', '<' and '>' for use in HTML. + def html_escape(s) + s.to_s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/ "text/xsl", + "css" => "text/css", + } + + attr_accessor(*ATTRIBUTES) + attr_accessor(:do_validate) + def initialize(*attrs) + @do_validate = true + ATTRIBUTES.each do |attr| + __send__("#{attr}=", nil) + end + vars = ATTRIBUTES.dup + vars.unshift(:do_validate) + attrs.each do |name, value| + if vars.include?(name.to_s) + __send__("#{name}=", value) + end + end + end + + def to_s + rv = "" + if @href + rv << %Q[] + end + rv + end + + remove_method(:href=) + def href=(value) + @href = value + if @href and @type.nil? + @type = guess_type(@href) + end + @href + end + + remove_method(:alternate=) + def alternate=(value) + if value.nil? or /\A(?:yes|no)\z/ =~ value + @alternate = value + else + if @do_validate + args = ["?xml-stylesheet?", %Q[alternate="#{value}"]] + raise NotAvailableValueError.new(*args) + end + end + @alternate + end + + def setup_maker(maker) + xss = maker.xml_stylesheets.new_xml_stylesheet + ATTRIBUTES.each do |attr| + xss.__send__("#{attr}=", __send__(attr)) + end + end + + private + def guess_type(filename) + /\.([^.]+)$/ =~ filename + GUESS_TABLE[$1] + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/xmlparser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/xmlparser.rb new file mode 100644 index 0000000000..3dfe7d461a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/xmlparser.rb @@ -0,0 +1,93 @@ +begin + require "xml/parser" +rescue LoadError + require "xmlparser" +end + +begin + require "xml/encoding-ja" +rescue LoadError + require "xmlencoding-ja" + if defined?(Kconv) + module XMLEncoding_ja + class SJISHandler + include Kconv + end + end + end +end + +module XML + class Parser + unless defined?(Error) + Error = ::XMLParserError + end + end +end + +module RSS + + class REXMLLikeXMLParser < ::XML::Parser + + include ::XML::Encoding_ja + + def listener=(listener) + @listener = listener + end + + def startElement(name, attrs) + @listener.tag_start(name, attrs) + end + + def endElement(name) + @listener.tag_end(name) + end + + def character(data) + @listener.text(data) + end + + def xmlDecl(version, encoding, standalone) + @listener.xmldecl(version, encoding, standalone == 1) + end + + def processingInstruction(target, content) + @listener.instruction(target, content) + end + + end + + class XMLParserParser < BaseParser + + class << self + def listener + XMLParserListener + end + end + + private + def _parse + begin + parser = REXMLLikeXMLParser.new + parser.listener = @listener + parser.parse(@rss) + rescue ::XML::Parser::Error => e + raise NotWellFormedError.new(parser.line){e.message} + end + end + + end + + class XMLParserListener < BaseListener + + include ListenerMixin + + def xmldecl(version, encoding, standalone) + super + # Encoding is converted to UTF-8 when XMLParser parses XML. + @encoding = 'UTF-8' + end + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/xmlscanner.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/xmlscanner.rb new file mode 100644 index 0000000000..61b9fa6bf4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rss/xmlscanner.rb @@ -0,0 +1,121 @@ +require 'xmlscan/scanner' +require 'stringio' + +module RSS + + class XMLScanParser < BaseParser + + class << self + def listener + XMLScanListener + end + end + + private + def _parse + begin + if @rss.is_a?(String) + input = StringIO.new(@rss) + else + input = @rss + end + scanner = XMLScan::XMLScanner.new(@listener) + scanner.parse(input) + rescue XMLScan::Error => e + lineno = e.lineno || scanner.lineno || input.lineno + raise NotWellFormedError.new(lineno){e.message} + end + end + + end + + class XMLScanListener < BaseListener + + include XMLScan::Visitor + include ListenerMixin + + ENTITIES = { + 'lt' => '<', + 'gt' => '>', + 'amp' => '&', + 'quot' => '"', + 'apos' => '\'' + } + + def on_xmldecl_version(str) + @version = str + end + + def on_xmldecl_encoding(str) + @encoding = str + end + + def on_xmldecl_standalone(str) + @standalone = str + end + + def on_xmldecl_end + xmldecl(@version, @encoding, @standalone == "yes") + end + + alias_method(:on_pi, :instruction) + alias_method(:on_chardata, :text) + alias_method(:on_cdata, :text) + + def on_etag(name) + tag_end(name) + end + + def on_entityref(ref) + text(entity(ref)) + end + + def on_charref(code) + text([code].pack('U')) + end + + alias_method(:on_charref_hex, :on_charref) + + def on_stag(name) + @attrs = {} + end + + def on_attribute(name) + @attrs[name] = @current_attr = '' + end + + def on_attr_value(str) + @current_attr << str + end + + def on_attr_entityref(ref) + @current_attr << entity(ref) + end + + def on_attr_charref(code) + @current_attr << [code].pack('U') + end + + alias_method(:on_attr_charref_hex, :on_attr_charref) + + def on_stag_end(name) + tag_start(name, @attrs) + end + + def on_stag_end_empty(name) + tag_start(name, @attrs) + tag_end(name) + end + + private + def entity(ref) + ent = ENTITIES[ref] + if ent + ent + else + wellformed_error("undefined entity: #{ref}") + end + end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/rubyunit.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rubyunit.rb new file mode 100644 index 0000000000..1aca37864f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/rubyunit.rb @@ -0,0 +1,6 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'runit/testcase' +require 'test/unit' diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/assert.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/assert.rb new file mode 100644 index 0000000000..c752b19a25 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/assert.rb @@ -0,0 +1,73 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/assertions' +require 'runit/error' + +module RUNIT + module Assert + include Test::Unit::Assertions + + def setup_assert + end + + def assert_no_exception(*args, &block) + assert_nothing_raised(*args, &block) + end + + # To deal with the fact that RubyUnit does not check that the + # regular expression is, indeed, a regular expression, if it is + # not, we do our own assertion using the same semantics as + # RubyUnit + def assert_match(actual_string, expected_re, message="") + _wrap_assertion { + full_message = build_message(message, "Expected to match ", actual_string, expected_re) + assert_block(full_message) { + expected_re =~ actual_string + } + Regexp.last_match + } + end + + def assert_not_nil(actual, message="") + assert(!actual.nil?, message) + end + + def assert_not_match(actual_string, expected_re, message="") + assert_no_match(expected_re, actual_string, message) + end + + def assert_matches(*args) + assert_match(*args) + end + + def assert_fail(message="") + flunk(message) + end + + def assert_equal_float(expected, actual, delta, message="") + assert_in_delta(expected, actual, delta, message) + end + + def assert_send(object, method, *args) + super([object, method, *args]) + end + + def assert_exception(exception, message="", &block) + assert_raises(exception, message, &block) + end + + def assert_respond_to(method, object, message="") + if (called_internally?) + super + else + super(object, method, message) + end + end + + def called_internally? + /assertions\.rb/.match(caller[1]) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/cui/testrunner.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/cui/testrunner.rb new file mode 100644 index 0000000000..d521ec16ad --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/cui/testrunner.rb @@ -0,0 +1,51 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/ui/console/testrunner' +require 'runit/testresult' + +module RUNIT + module CUI + class TestRunner < Test::Unit::UI::Console::TestRunner + @@quiet_mode = false + + def self.run(suite) + self.new().run(suite) + end + + def initialize + super nil + end + + def run(suite, quiet_mode=@@quiet_mode) + @suite = suite + def @suite.suite + self + end + @output_level = (quiet_mode ? Test::Unit::UI::PROGRESS_ONLY : Test::Unit::UI::VERBOSE) + start + end + + def create_mediator(suite) + mediator = Test::Unit::UI::TestRunnerMediator.new(suite) + class << mediator + attr_writer :result_delegate + def create_result + return @result_delegate.create_result + end + end + mediator.result_delegate = self + return mediator + end + + def create_result + return RUNIT::TestResult.new + end + + def self.quiet_mode=(boolean) + @@quiet_mode = boolean + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/error.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/error.rb new file mode 100644 index 0000000000..4a727fb02b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/error.rb @@ -0,0 +1,9 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/assertionfailederror.rb' + +module RUNIT + AssertionFailedError = Test::Unit::AssertionFailedError +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/testcase.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/testcase.rb new file mode 100644 index 0000000000..4576cb8644 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/testcase.rb @@ -0,0 +1,45 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'runit/testresult' +require 'runit/testsuite' +require 'runit/assert' +require 'runit/error' +require 'test/unit/testcase' + +module RUNIT + class TestCase < Test::Unit::TestCase + include RUNIT::Assert + + def self.suite + method_names = instance_methods(true) + tests = method_names.delete_if { |method_name| method_name !~ /^test/ } + suite = TestSuite.new(name) + tests.each { + |test| + catch(:invalid_test) { + suite << new(test, name) + } + } + return suite + end + + def initialize(test_name, suite_name=self.class.name) + super(test_name) + end + + def assert_equals(*args) + assert_equal(*args) + end + + def name + super.sub(/^(.*?)\((.*)\)$/, '\2#\1') + end + + def run(result, &progress_block) + progress_block = proc {} unless (block_given?) + super(result, &progress_block) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/testresult.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/testresult.rb new file mode 100644 index 0000000000..7f70778171 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/testresult.rb @@ -0,0 +1,44 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/testresult' + +module RUNIT + class TestResult < Test::Unit::TestResult + attr_reader(:errors, :failures) + def succeed? + return passed? + end + def failure_size + return failure_count + end + def run_asserts + return assertion_count + end + def error_size + return error_count + end + def run_tests + return run_count + end + def add_failure(failure) + def failure.at + return location + end + def failure.err + return message + end + super(failure) + end + def add_error(error) + def error.at + return location + end + def error.err + return exception + end + super(error) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/testsuite.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/testsuite.rb new file mode 100644 index 0000000000..63baf65707 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/testsuite.rb @@ -0,0 +1,26 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/testsuite' + +module RUNIT + class TestSuite < Test::Unit::TestSuite + def add_test(*args) + add(*args) + end + + def add(*args) + self.<<(*args) + end + + def count_test_cases + return size + end + + def run(result, &progress_block) + progress_block = proc {} unless (block_given?) + super(result, &progress_block) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/topublic.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/topublic.rb new file mode 100644 index 0000000000..566f0dd35c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/runit/topublic.rb @@ -0,0 +1,8 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +module RUNIT + module ToPublic + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/scanf.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/scanf.rb new file mode 100644 index 0000000000..a49ce6944e --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/scanf.rb @@ -0,0 +1,702 @@ +# scanf for Ruby +# +# $Revision: 11708 $ +# $Id: scanf.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# $Author: shyouhei $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# +# A product of the Austin Ruby Codefest (Austin, Texas, August 2002) + +=begin + +=scanf for Ruby + +==Description + +scanf for Ruby is an implementation of the C function scanf(3), +modified as necessary for Ruby compatibility. + +The methods provided are String#scanf, IO#scanf, and +Kernel#scanf. Kernel#scanf is a wrapper around STDIN.scanf. IO#scanf +can be used on any IO stream, including file handles and sockets. +scanf can be called either with or without a block. + +scanf for Ruby scans an input string or stream according to a +format, as described below ("Conversions"), and returns an +array of matches between the format and the input. The format is +defined in a string, and is similar (though not identical) to the +formats used in Kernel#printf and Kernel#sprintf. + +The format may contain conversion specifiers, which tell scanf +what form (type) each particular matched substring should be converted +to (e.g., decimal integer, floating point number, literal string, +etc.) The matches and conversions take place from left to right, and +the conversions themselves are returned as an array. + +The format string may also contain characters other than those in the +conversion specifiers. White space (blanks, tabs, or newlines) in the +format string matches any amount of white space, including none, in +the input. Everything else matches only itself. + +Scanning stops, and scanf returns, when any input character fails to +match the specifications in the format string, or when input is +exhausted, or when everything in the format string has been +matched. All matches found up to the stopping point are returned in +the return array (or yielded to the block, if a block was given). + + +==Basic usage + + require 'scanf.rb' + + # String#scanf and IO#scanf take a single argument (a format string) + array = aString.scanf("%d%s") + array = anIO.scanf("%d%s") + + # Kernel#scanf reads from STDIN + array = scanf("%d%s") + +==Block usage + +When called with a block, scanf keeps scanning the input, cycling back +to the beginning of the format string, and yields a new array of +conversions to the block every time the format string is matched +(including partial matches, but not including complete failures). The +actual return value of scanf when called with a block is an array +containing the results of all the executions of the block. + + str = "123 abc 456 def 789 ghi" + str.scanf("%d%s") { |num,str| [ num * 2, str.upcase ] } + # => [[246, "ABC"], [912, "DEF"], [1578, "GHI"]] + +==Conversions + +The single argument to scanf is a format string, which generally +includes one or more conversion specifiers. Conversion specifiers +begin with the percent character ('%') and include information about +what scanf should next scan for (string, decimal number, single +character, etc.). + +There may be an optional maximum field width, expressed as a decimal +integer, between the % and the conversion. If no width is given, a +default of `infinity' is used (with the exception of the %c specifier; +see below). Otherwise, given a field width of n for a given +conversion, at most n characters are scanned in processing +that conversion. Before conversion begins, most conversions skip +white space in the input string; this white space is not counted +against the field width. + +The following conversions are available. (See the files EXAMPLES +and tests/scanftests.rb for examples.) + +[%] + Matches a literal `%'. That is, `%%' in the format string matches a + single input `%' character. No conversion is done, and the resulting + '%' is not included in the return array. + +[d] + Matches an optionally signed decimal integer. + +[u] + Same as d. + +[i] + Matches an optionally signed integer. The integer is read in base + 16 if it begins with `0x' or `0X', in base 8 if it begins with `0', + and in base 10 other- wise. Only characters that correspond to the + base are recognized. + +[o] + Matches an optionally signed octal integer. + +[x,X] + Matches an optionally signed hexadecimal integer, + +[f,g,e,E] + Matches an optionally signed floating-point number. + +[s] + Matches a sequence of non-white-space character. The input string stops at + white space or at the maximum field width, whichever occurs first. + +[c] + Matches a single character, or a sequence of n characters if a + field width of n is specified. The usual skip of leading white + space is suppressed. To skip white space first, use an explicit space in + the format. + +[[] + Matches a nonempty sequence of characters from the specified set + of accepted characters. The usual skip of leading white space is + suppressed. This bracketed sub-expression is interpreted exactly like a + character class in a Ruby regular expression. (In fact, it is placed as-is + in a regular expression.) The matching against the input string ends with + the appearance of a character not in (or, with a circumflex, in) the set, + or when the field width runs out, whichever comes first. + +===Assignment suppression + +To require that a particular match occur, but without including the result +in the return array, place the assignment suppression flag, which is +the star character ('*'), immediately after the leading '%' of a format +specifier (just before the field width, if any). + +==Examples + +See the files EXAMPLES and tests/scanftests.rb. + +==scanf for Ruby compared with scanf in C + +scanf for Ruby is based on the C function scanf(3), but with modifications, +dictated mainly by the underlying differences between the languages. + +===Unimplemented flags and specifiers + +* The only flag implemented in scanf for Ruby is '*' (ignore + upcoming conversion). Many of the flags available in C versions of scanf(4) + have to do with the type of upcoming pointer arguments, and are literally + meaningless in Ruby. + +* The n specifier (store number of characters consumed so far in + next pointer) is not implemented. + +* The p specifier (match a pointer value) is not implemented. + +===Altered specifiers + +[o,u,x,X] + In scanf for Ruby, all of these specifiers scan for an optionally signed + integer, rather than for an unsigned integer like their C counterparts. + +===Return values + +scanf for Ruby returns an array of successful conversions, whereas +scanf(3) returns the number of conversions successfully +completed. (See below for more details on scanf for Ruby's return +values.) + +==Return values + +Without a block, scanf returns an array containing all the conversions +it has found. If none are found, scanf will return an empty array. An +unsuccesful match is never ignored, but rather always signals the end +of the scanning operation. If the first unsuccessful match takes place +after one or more successful matches have already taken place, the +returned array will contain the results of those successful matches. + +With a block scanf returns a 'map'-like array of transformations from +the block -- that is, an array reflecting what the block did with each +yielded result from the iterative scanf operation. (See "Block +usage", above.) + +==Test suite + +scanf for Ruby includes a suite of unit tests (requiring the +TestUnit package), which can be run with the command ruby +tests/scanftests.rb or the command make test. + +==Current limitations and bugs + +When using IO#scanf under Windows, make sure you open your files in +binary mode: + + File.open("filename", "rb") + +so that scanf can keep track of characters correctly. + +Support for character classes is reasonably complete (since it +essentially piggy-backs on Ruby's regular expression handling of +character classes), but users are advised that character class testing +has not been exhaustive, and that they should exercise some caution +in using any of the more complex and/or arcane character class +idioms. + + +==Technical notes + +===Rationale behind scanf for Ruby + +The impetus for a scanf implementation in Ruby comes chiefly from the fact +that existing pattern matching operations, such as Regexp#match and +String#scan, return all results as strings, which have to be converted to +integers or floats explicitly in cases where what's ultimately wanted are +integer or float values. + +===Design of scanf for Ruby + +scanf for Ruby is essentially a -to- converter. + +When scanf is called, a FormatString object is generated from the +format string ("%d%s...") argument. The FormatString object breaks the +format string down into atoms ("%d", "%5f", "blah", etc.), and from +each atom it creates a FormatSpecifier object, which it +saves. + +Each FormatSpecifier has a regular expression fragment and a "handler" +associated with it. For example, the regular expression fragment +associated with the format "%d" is "([-+]?\d+)", and the handler +associated with it is a wrapper around String#to_i. scanf itself calls +FormatString#match, passing in the input string. FormatString#match +iterates through its FormatSpecifiers; for each one, it matches the +corresponding regular expression fragment against the string. If +there's a match, it sends the matched string to the handler associated +with the FormatSpecifier. + +Thus, to follow up the "%d" example: if "123" occurs in the input +string when a FormatSpecifier consisting of "%d" is reached, the "123" +will be matched against "([-+]?\d+)", and the matched string will be +rendered into an integer by a call to to_i. + +The rendered match is then saved to an accumulator array, and the +input string is reduced to the post-match substring. Thus the string +is "eaten" from the left as the FormatSpecifiers are applied in +sequence. (This is done to a duplicate string; the original string is +not altered.) + +As soon as a regular expression fragment fails to match the string, or +when the FormatString object runs out of FormatSpecifiers, scanning +stops and results accumulated so far are returned in an array. + +==License and copyright + +Copyright:: (c) 2002-2003 David Alan Black +License:: Distributed on the same licensing terms as Ruby itself + +==Warranty disclaimer + +This software is provided "as is" and without any express or implied +warranties, including, without limitation, the implied warranties of +merchantibility and fitness for a particular purpose. + +==Credits and acknowledgements + +scanf for Ruby was developed as the major activity of the Austin +Ruby Codefest (Austin, Texas, August 2002). + +Principal author:: David Alan Black (mailto:dblack@superlink.net) +Co-author:: Hal Fulton (mailto:hal9000@hypermetrics.com) +Project contributors:: Nolan Darilek, Jason Johnston + +Thanks to Hal Fulton for hosting the Codefest. + +Thanks to Matz for suggestions about the class design. + +Thanks to Gavin Sinclair for some feedback on the documentation. + +The text for parts of this document, especially the Description and +Conversions sections, above, were adapted from the Linux Programmer's +Manual manpage for scanf(3), dated 1995-11-01. + +==Bugs and bug reports + +scanf for Ruby is based on something of an amalgam of C scanf +implementations and documentation, rather than on a single canonical +description. Suggestions for features and behaviors which appear in +other scanfs, and would be meaningful in Ruby, are welcome, as are +reports of suspicious behaviors and/or bugs. (Please see "Credits and +acknowledgements", above, for email addresses.) + +=end + +module Scanf + + class FormatSpecifier + + attr_reader :re_string, :matched_string, :conversion, :matched + + private + + def skip; /^\s*%\*/.match(@spec_string); end + + def extract_float(s); s.to_f if s &&! skip; end + def extract_decimal(s); s.to_i if s &&! skip; end + def extract_hex(s); s.hex if s &&! skip; end + def extract_octal(s); s.oct if s &&! skip; end + def extract_integer(s); Integer(s) if s &&! skip; end + def extract_plain(s); s unless skip; end + + def nil_proc(s); nil; end + + public + + def to_s + @spec_string + end + + def count_space? + /(?:\A|\S)%\*?\d*c|\[/.match(@spec_string) + end + + def initialize(str) + @spec_string = str + h = '[A-Fa-f0-9]' + + @re_string, @handler = + case @spec_string + + # %[[:...:]] + when /%\*?(\[\[:[a-z]+:\]\])/ + [ "(#{$1}+)", :extract_plain ] + + # %5[[:...:]] + when /%\*?(\d+)(\[\[:[a-z]+:\]\])/ + [ "(#{$2}{1,#{$1}})", :extract_plain ] + + # %[...] + when /%\*?\[([^\]]*)\]/ + yes = $1 + if /^\^/.match(yes) then no = yes[1..-1] else no = '^' + yes end + [ "([#{yes}]+)(?=[#{no}]|\\z)", :extract_plain ] + + # %5[...] + when /%\*?(\d+)\[([^\]]*)\]/ + yes = $2 + w = $1 + [ "([#{yes}]{1,#{w}})", :extract_plain ] + + # %i + when /%\*?i/ + [ "([-+]?(?:(?:0[0-7]+)|(?:0[Xx]#{h}+)|(?:[1-9]\\d+)))", :extract_integer ] + + # %5i + when /%\*?(\d+)i/ + n = $1.to_i + s = "(" + if n > 1 then s += "[1-9]\\d{1,#{n-1}}|" end + if n > 1 then s += "0[0-7]{1,#{n-1}}|" end + if n > 2 then s += "[-+]0[0-7]{1,#{n-2}}|" end + if n > 2 then s += "[-+][1-9]\\d{1,#{n-2}}|" end + if n > 2 then s += "0[Xx]#{h}{1,#{n-2}}|" end + if n > 3 then s += "[-+]0[Xx]#{h}{1,#{n-3}}|" end + s += "\\d" + s += ")" + [ s, :extract_integer ] + + # %d, %u + when /%\*?[du]/ + [ '([-+]?\d+)', :extract_decimal ] + + # %5d, %5u + when /%\*?(\d+)[du]/ + n = $1.to_i + s = "(" + if n > 1 then s += "[-+]\\d{1,#{n-1}}|" end + s += "\\d{1,#{$1}})" + [ s, :extract_decimal ] + + # %x + when /%\*?[Xx]/ + [ "([-+]?(?:0[Xx])?#{h}+)", :extract_hex ] + + # %5x + when /%\*?(\d+)[Xx]/ + n = $1.to_i + s = "(" + if n > 3 then s += "[-+]0[Xx]#{h}{1,#{n-3}}|" end + if n > 2 then s += "0[Xx]#{h}{1,#{n-2}}|" end + if n > 1 then s += "[-+]#{h}{1,#{n-1}}|" end + s += "#{h}{1,#{n}}" + s += ")" + [ s, :extract_hex ] + + # %o + when /%\*?o/ + [ '([-+]?[0-7]+)', :extract_octal ] + + # %5o + when /%\*?(\d+)o/ + [ "([-+][0-7]{1,#{$1.to_i-1}}|[0-7]{1,#{$1}})", :extract_octal ] + + # %f + when /%\*?f/ + [ '([-+]?((\d+(?>(?=[^\d.]|$)))|(\d*(\.(\d*([eE][-+]?\d+)?)))))', :extract_float ] + + # %5f + when /%\*?(\d+)f/ + [ "(\\S{1,#{$1}})", :extract_float ] + + # %5s + when /%\*?(\d+)s/ + [ "(\\S{1,#{$1}})", :extract_plain ] + + # %s + when /%\*?s/ + [ '(\S+)', :extract_plain ] + + # %c + when /\s%\*?c/ + [ "\\s*(.)", :extract_plain ] + + # %c + when /%\*?c/ + [ "(.)", :extract_plain ] + + # %5c (whitespace issues are handled by the count_*_space? methods) + when /%\*?(\d+)c/ + [ "(.{1,#{$1}})", :extract_plain ] + + # %% + when /%%/ + [ '(\s*%)', :nil_proc ] + + # literal characters + else + [ "(#{Regexp.escape(@spec_string)})", :nil_proc ] + end + + @re_string = '\A' + @re_string + end + + def to_re + Regexp.new(@re_string,Regexp::MULTILINE) + end + + def match(str) + @matched = false + s = str.dup + s.sub!(/\A\s+/,'') unless count_space? + res = to_re.match(s) + if res + @conversion = send(@handler, res[1]) + @matched_string = @conversion.to_s + @matched = true + end + res + end + + def letter + /%\*?\d*([a-z\[])/.match(@spec_string).to_a[1] + end + + def width + w = /%\*?(\d+)/.match(@spec_string).to_a[1] + w && w.to_i + end + + def mid_match? + return false unless @matched + cc_no_width = letter == '[' &&! width + c_or_cc_width = (letter == 'c' || letter == '[') && width + width_left = c_or_cc_width && (matched_string.size < width) + + return width_left || cc_no_width + end + + end + + class FormatString + + attr_reader :string_left, :last_spec_tried, + :last_match_tried, :matched_count, :space + + SPECIFIERS = 'diuXxofeEgsc' + REGEX = / + # possible space, followed by... + (?:\s* + # percent sign, followed by... + % + # another percent sign, or... + (?:%| + # optional assignment suppression flag + \*? + # optional maximum field width + \d* + # named character class, ... + (?:\[\[:\w+:\]\]| + # traditional character class, or... + \[[^\]]*\]| + # specifier letter. + [#{SPECIFIERS}])))| + # or miscellaneous characters + [^%\s]+/ix + + def initialize(str) + @specs = [] + @i = 1 + s = str.to_s + return unless /\S/.match(s) + @space = true if /\s\z/.match(s) + @specs.replace s.scan(REGEX).map {|spec| FormatSpecifier.new(spec) } + end + + def to_s + @specs.join('') + end + + def prune(n=matched_count) + n.times { @specs.shift } + end + + def spec_count + @specs.size + end + + def last_spec + @i == spec_count - 1 + end + + def match(str) + accum = [] + @string_left = str + @matched_count = 0 + + @specs.each_with_index do |spec,@i| + @last_spec_tried = spec + @last_match_tried = spec.match(@string_left) + break unless @last_match_tried + @matched_count += 1 + + accum << spec.conversion + + @string_left = @last_match_tried.post_match + break if @string_left.empty? + end + return accum.compact + end + end +end + +class IO + +# The trick here is doing a match where you grab one *line* +# of input at a time. The linebreak may or may not occur +# at the boundary where the string matches a format specifier. +# And if it does, some rule about whitespace may or may not +# be in effect... +# +# That's why this is much more elaborate than the string +# version. +# +# For each line: +# Match succeeds (non-emptily) +# and the last attempted spec/string sub-match succeeded: +# +# could the last spec keep matching? +# yes: save interim results and continue (next line) +# +# The last attempted spec/string did not match: +# +# are we on the next-to-last spec in the string? +# yes: +# is fmt_string.string_left all spaces? +# yes: does current spec care about input space? +# yes: fatal failure +# no: save interim results and continue +# no: continue [this state could be analyzed further] +# +# + + def scanf(str,&b) + return block_scanf(str,&b) if b + return [] unless str.size > 0 + + start_position = pos rescue 0 + matched_so_far = 0 + source_buffer = "" + result_buffer = [] + final_result = [] + + fstr = Scanf::FormatString.new(str) + + loop do + if eof || (tty? &&! fstr.match(source_buffer)) + final_result.concat(result_buffer) + break + end + + source_buffer << gets + + current_match = fstr.match(source_buffer) + + spec = fstr.last_spec_tried + + if spec.matched + if spec.mid_match? + result_buffer.replace(current_match) + next + end + + elsif (fstr.matched_count == fstr.spec_count - 1) + if /\A\s*\z/.match(fstr.string_left) + break if spec.count_space? + result_buffer.replace(current_match) + next + end + end + + final_result.concat(current_match) + + matched_so_far += source_buffer.size + source_buffer.replace(fstr.string_left) + matched_so_far -= source_buffer.size + break if fstr.last_spec + fstr.prune + end + seek(start_position + matched_so_far, IO::SEEK_SET) rescue Errno::ESPIPE + soak_up_spaces if fstr.last_spec && fstr.space + + return final_result + end + + private + + def soak_up_spaces + c = getc + ungetc(c) if c + until eof ||! c || /\S/.match(c.chr) + c = getc + end + ungetc(c) if (c && /\S/.match(c.chr)) + end + + def block_scanf(str) + final = [] +# Sub-ideal, since another FS gets created in scanf. +# But used here to determine the number of specifiers. + fstr = Scanf::FormatString.new(str) + last_spec = fstr.last_spec + begin + current = scanf(str) + break if current.empty? + final.push(yield(current)) + end until eof || fstr.last_spec_tried == last_spec + return final + end +end + +class String + + def scanf(fstr,&b) + if b + block_scanf(fstr,&b) + else + fs = + if fstr.is_a? Scanf::FormatString + fstr + else + Scanf::FormatString.new(fstr) + end + fs.match(self) + end + end + + def block_scanf(fstr,&b) + fs = Scanf::FormatString.new(fstr) + str = self.dup + final = [] + begin + current = str.scanf(fs) + final.push(yield(current)) unless current.empty? + str = fs.string_left + end until current.empty? || str.empty? + return final + end +end + +module Kernel + private + def scanf(fs,&b) + STDIN.scanf(fs,&b) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/set.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/set.rb new file mode 100644 index 0000000000..3c96ceedbc --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/set.rb @@ -0,0 +1,1227 @@ +#!/usr/bin/env ruby +#-- +# set.rb - defines the Set class +#++ +# Copyright (c) 2002 Akinori MUSHA +# +# Documentation by Akinori MUSHA and Gavin Sinclair. +# +# All rights reserved. You can redistribute and/or modify it under the same +# terms as Ruby. +# +# $Id: set.rb 11980 2007-03-03 16:06:45Z knu $ +# +# == Overview +# +# This library provides the Set class, which deals with a collection +# of unordered values with no duplicates. It is a hybrid of Array's +# intuitive inter-operation facilities and Hash's fast lookup. If you +# need to keep values ordered, use the SortedSet class. +# +# The method +to_set+ is added to Enumerable for convenience. +# +# See the Set class for an example of usage. + + +# +# Set implements a collection of unordered values with no duplicates. +# This is a hybrid of Array's intuitive inter-operation facilities and +# Hash's fast lookup. +# +# Several methods accept any Enumerable object (implementing +each+) +# for greater flexibility: new, replace, merge, subtract, |, &, -, ^. +# +# The equality of each couple of elements is determined according to +# Object#eql? and Object#hash, since Set uses Hash as storage. +# +# Finally, if you are using class Set, you can also use Enumerable#to_set +# for convenience. +# +# == Example +# +# require 'set' +# s1 = Set.new [1, 2] # -> # +# s2 = [1, 2].to_set # -> # +# s1 == s2 # -> true +# s1.add("foo") # -> # +# s1.merge([2, 6]) # -> # +# s1.subset? s2 # -> false +# s2.subset? s1 # -> true +# +class Set + include Enumerable + + # Creates a new set containing the given objects. + def self.[](*ary) + new(ary) + end + + # Creates a new set containing the elements of the given enumerable + # object. + # + # If a block is given, the elements of enum are preprocessed by the + # given block. + def initialize(enum = nil, &block) # :yields: o + @hash ||= Hash.new + + enum.nil? and return + + if block + enum.each { |o| add(block[o]) } + else + merge(enum) + end + end + + # Copy internal hash. + def initialize_copy(orig) + @hash = orig.instance_eval{@hash}.dup + end + + # Returns the number of elements. + def size + @hash.size + end + alias length size + + # Returns true if the set contains no elements. + def empty? + @hash.empty? + end + + # Removes all elements and returns self. + def clear + @hash.clear + self + end + + # Replaces the contents of the set with the contents of the given + # enumerable object and returns self. + def replace(enum) + if enum.class == self.class + @hash.replace(enum.instance_eval { @hash }) + else + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + clear + enum.each { |o| add(o) } + end + + self + end + + # Converts the set to an array. The order of elements is uncertain. + def to_a + @hash.keys + end + + def flatten_merge(set, seen = Set.new) + set.each { |e| + if e.is_a?(Set) + if seen.include?(e_id = e.object_id) + raise ArgumentError, "tried to flatten recursive Set" + end + + seen.add(e_id) + flatten_merge(e, seen) + seen.delete(e_id) + else + add(e) + end + } + + self + end + protected :flatten_merge + + # Returns a new set that is a copy of the set, flattening each + # containing set recursively. + def flatten + self.class.new.flatten_merge(self) + end + + # Equivalent to Set#flatten, but replaces the receiver with the + # result in place. Returns nil if no modifications were made. + def flatten! + if detect { |e| e.is_a?(Set) } + replace(flatten()) + else + nil + end + end + + # Returns true if the set contains the given object. + def include?(o) + @hash.include?(o) + end + alias member? include? + + # Returns true if the set is a superset of the given set. + def superset?(set) + set.is_a?(Set) or raise ArgumentError, "value must be a set" + return false if size < set.size + set.all? { |o| include?(o) } + end + + # Returns true if the set is a proper superset of the given set. + def proper_superset?(set) + set.is_a?(Set) or raise ArgumentError, "value must be a set" + return false if size <= set.size + set.all? { |o| include?(o) } + end + + # Returns true if the set is a subset of the given set. + def subset?(set) + set.is_a?(Set) or raise ArgumentError, "value must be a set" + return false if set.size < size + all? { |o| set.include?(o) } + end + + # Returns true if the set is a proper subset of the given set. + def proper_subset?(set) + set.is_a?(Set) or raise ArgumentError, "value must be a set" + return false if set.size <= size + all? { |o| set.include?(o) } + end + + # Calls the given block once for each element in the set, passing + # the element as parameter. + def each + @hash.each_key { |o| yield(o) } + self + end + + # Adds the given object to the set and returns self. Use +merge+ to + # add several elements at once. + def add(o) + @hash[o] = true + self + end + alias << add + + # Adds the given object to the set and returns self. If the + # object is already in the set, returns nil. + def add?(o) + if include?(o) + nil + else + add(o) + end + end + + # Deletes the given object from the set and returns self. Use +subtract+ to + # delete several items at once. + def delete(o) + @hash.delete(o) + self + end + + # Deletes the given object from the set and returns self. If the + # object is not in the set, returns nil. + def delete?(o) + if include?(o) + delete(o) + else + nil + end + end + + # Deletes every element of the set for which block evaluates to + # true, and returns self. + def delete_if + @hash.delete_if { |o,| yield(o) } + self + end + + # Do collect() destructively. + def collect! + set = self.class.new + each { |o| set << yield(o) } + replace(set) + end + alias map! collect! + + # Equivalent to Set#delete_if, but returns nil if no changes were + # made. + def reject! + n = size + delete_if { |o| yield(o) } + size == n ? nil : self + end + + # Merges the elements of the given enumerable object to the set and + # returns self. + def merge(enum) + if enum.is_a?(Set) + @hash.update(enum.instance_eval { @hash }) + else + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + enum.each { |o| add(o) } + end + + self + end + + # Deletes every element that appears in the given enumerable object + # and returns self. + def subtract(enum) + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + enum.each { |o| delete(o) } + self + end + + # Returns a new set built by merging the set and the elements of the + # given enumerable object. + def |(enum) + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + dup.merge(enum) + end + alias + | ## + alias union | ## + + # Returns a new set built by duplicating the set, removing every + # element that appears in the given enumerable object. + def -(enum) + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + dup.subtract(enum) + end + alias difference - ## + + # Returns a new set containing elements common to the set and the + # given enumerable object. + def &(enum) + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + n = self.class.new + enum.each { |o| n.add(o) if include?(o) } + n + end + alias intersection & ## + + # Returns a new set containing elements exclusive between the set + # and the given enumerable object. (set ^ enum) is equivalent to + # ((set | enum) - (set & enum)). + def ^(enum) + enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" + n = Set.new(enum) + each { |o| if n.include?(o) then n.delete(o) else n.add(o) end } + n + end + + # Returns true if two sets are equal. The equality of each couple + # of elements is defined according to Object#eql?. + def ==(set) + equal?(set) and return true + + set.is_a?(Set) && size == set.size or return false + + hash = @hash.dup + set.all? { |o| hash.include?(o) } + end + + def hash # :nodoc: + @hash.hash + end + + def eql?(o) # :nodoc: + return false unless o.is_a?(Set) + @hash.eql?(o.instance_eval{@hash}) + end + + # Classifies the set by the return value of the given block and + # returns a hash of {value => set of elements} pairs. The block is + # called once for each element of the set, passing the element as + # parameter. + # + # e.g.: + # + # require 'set' + # files = Set.new(Dir.glob("*.rb")) + # hash = files.classify { |f| File.mtime(f).year } + # p hash # => {2000=>#, + # # 2001=>#, + # # 2002=>#} + def classify # :yields: o + h = {} + + each { |i| + x = yield(i) + (h[x] ||= self.class.new).add(i) + } + + h + end + + # Divides the set into a set of subsets according to the commonality + # defined by the given block. + # + # If the arity of the block is 2, elements o1 and o2 are in common + # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are + # in common if block.call(o1) == block.call(o2). + # + # e.g.: + # + # require 'set' + # numbers = Set[1, 3, 4, 6, 9, 10, 11] + # set = numbers.divide { |i,j| (i - j).abs == 1 } + # p set # => #, + # # #, + # # #, + # # #}> + def divide(&func) + if func.arity == 2 + require 'tsort' + + class << dig = {} # :nodoc: + include TSort + + alias tsort_each_node each_key + def tsort_each_child(node, &block) + fetch(node).each(&block) + end + end + + each { |u| + dig[u] = a = [] + each{ |v| func.call(u, v) and a << v } + } + + set = Set.new() + dig.each_strongly_connected_component { |css| + set.add(self.class.new(css)) + } + set + else + Set.new(classify(&func).values) + end + end + + InspectKey = :__inspect_key__ # :nodoc: + + # Returns a string containing a human-readable representation of the + # set. ("#") + def inspect + ids = (Thread.current[InspectKey] ||= []) + + if ids.include?(object_id) + return sprintf('#<%s: {...}>', self.class.name) + end + + begin + ids << object_id + return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2]) + ensure + ids.pop + end + end + + def pretty_print(pp) # :nodoc: + pp.text sprintf('#<%s: {', self.class.name) + pp.nest(1) { + pp.seplist(self) { |o| + pp.pp o + } + } + pp.text "}>" + end + + def pretty_print_cycle(pp) # :nodoc: + pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...') + end +end + +# SortedSet implements a set which elements are sorted in order. See Set. +class SortedSet < Set + @@setup = false + + class << self + def [](*ary) # :nodoc: + new(ary) + end + + def setup # :nodoc: + @@setup and return + + module_eval { + # a hack to shut up warning + alias old_init initialize + remove_method :old_init + } + begin + require 'rbtree' + + module_eval %{ + def initialize(*args, &block) + @hash = RBTree.new + super + end + } + rescue LoadError + module_eval %{ + def initialize(*args, &block) + @keys = nil + super + end + + def clear + @keys = nil + super + end + + def replace(enum) + @keys = nil + super + end + + def add(o) + @keys = nil + @hash[o] = true + self + end + alias << add + + def delete(o) + @keys = nil + @hash.delete(o) + self + end + + def delete_if + n = @hash.size + @hash.delete_if { |o,| yield(o) } + @keys = nil if @hash.size != n + self + end + + def merge(enum) + @keys = nil + super + end + + def each + to_a.each { |o| yield(o) } + end + + def to_a + (@keys = @hash.keys).sort! unless @keys + @keys + end + } + end + + @@setup = true + end + end + + def initialize(*args, &block) # :nodoc: + SortedSet.setup + initialize(*args, &block) + end +end + +module Enumerable + # Makes a set from the enumerable object with given arguments. + # Needs to +require "set"+ to use this method. + def to_set(klass = Set, *args, &block) + klass.new(self, *args, &block) + end +end + +# =begin +# == RestricedSet class +# RestricedSet implements a set with restrictions defined by a given +# block. +# +# === Super class +# Set +# +# === Class Methods +# --- RestricedSet::new(enum = nil) { |o| ... } +# --- RestricedSet::new(enum = nil) { |rset, o| ... } +# Creates a new restricted set containing the elements of the given +# enumerable object. Restrictions are defined by the given block. +# +# If the block's arity is 2, it is called with the RestrictedSet +# itself and an object to see if the object is allowed to be put in +# the set. +# +# Otherwise, the block is called with an object to see if the object +# is allowed to be put in the set. +# +# === Instance Methods +# --- restriction_proc +# Returns the restriction procedure of the set. +# +# =end +# +# class RestricedSet < Set +# def initialize(*args, &block) +# @proc = block or raise ArgumentError, "missing a block" +# +# if @proc.arity == 2 +# instance_eval %{ +# def add(o) +# @hash[o] = true if @proc.call(self, o) +# self +# end +# alias << add +# +# def add?(o) +# if include?(o) || !@proc.call(self, o) +# nil +# else +# @hash[o] = true +# self +# end +# end +# +# def replace(enum) +# enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" +# clear +# enum.each { |o| add(o) } +# +# self +# end +# +# def merge(enum) +# enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" +# enum.each { |o| add(o) } +# +# self +# end +# } +# else +# instance_eval %{ +# def add(o) +# if @proc.call(o) +# @hash[o] = true +# end +# self +# end +# alias << add +# +# def add?(o) +# if include?(o) || !@proc.call(o) +# nil +# else +# @hash[o] = true +# self +# end +# end +# } +# end +# +# super(*args) +# end +# +# def restriction_proc +# @proc +# end +# end + +if $0 == __FILE__ + eval DATA.read, nil, $0, __LINE__+4 +end + +__END__ + +require 'test/unit' + +class TC_Set < Test::Unit::TestCase + def test_aref + assert_nothing_raised { + Set[] + Set[nil] + Set[1,2,3] + } + + assert_equal(0, Set[].size) + assert_equal(1, Set[nil].size) + assert_equal(1, Set[[]].size) + assert_equal(1, Set[[nil]].size) + + set = Set[2,4,6,4] + assert_equal(Set.new([2,4,6]), set) + end + + def test_s_new + assert_nothing_raised { + Set.new() + Set.new(nil) + Set.new([]) + Set.new([1,2]) + Set.new('a'..'c') + Set.new('XYZ') + } + assert_raises(ArgumentError) { + Set.new(false) + } + assert_raises(ArgumentError) { + Set.new(1) + } + assert_raises(ArgumentError) { + Set.new(1,2) + } + + assert_equal(0, Set.new().size) + assert_equal(0, Set.new(nil).size) + assert_equal(0, Set.new([]).size) + assert_equal(1, Set.new([nil]).size) + + ary = [2,4,6,4] + set = Set.new(ary) + ary.clear + assert_equal(false, set.empty?) + assert_equal(3, set.size) + + ary = [1,2,3] + + s = Set.new(ary) { |o| o * 2 } + assert_equal([2,4,6], s.sort) + end + + def test_clone + set1 = Set.new + set2 = set1.clone + set1 << 'abc' + assert_equal(Set.new, set2) + end + + def test_dup + set1 = Set[1,2] + set2 = set1.dup + + assert_not_same(set1, set2) + + assert_equal(set1, set2) + + set1.add(3) + + assert_not_equal(set1, set2) + end + + def test_size + assert_equal(0, Set[].size) + assert_equal(2, Set[1,2].size) + assert_equal(2, Set[1,2,1].size) + end + + def test_empty? + assert_equal(true, Set[].empty?) + assert_equal(false, Set[1, 2].empty?) + end + + def test_clear + set = Set[1,2] + ret = set.clear + + assert_same(set, ret) + assert_equal(true, set.empty?) + end + + def test_replace + set = Set[1,2] + ret = set.replace('a'..'c') + + assert_same(set, ret) + assert_equal(Set['a','b','c'], set) + end + + def test_to_a + set = Set[1,2,3,2] + ary = set.to_a + + assert_equal([1,2,3], ary.sort) + end + + def test_flatten + # test1 + set1 = Set[ + 1, + Set[ + 5, + Set[7, + Set[0] + ], + Set[6,2], + 1 + ], + 3, + Set[3,4] + ] + + set2 = set1.flatten + set3 = Set.new(0..7) + + assert_not_same(set2, set1) + assert_equal(set3, set2) + + # test2; destructive + orig_set1 = set1 + set1.flatten! + + assert_same(orig_set1, set1) + assert_equal(set3, set1) + + # test3; multiple occurrences of a set in an set + set1 = Set[1, 2] + set2 = Set[set1, Set[set1, 4], 3] + + assert_nothing_raised { + set2.flatten! + } + + assert_equal(Set.new(1..4), set2) + + # test4; recursion + set2 = Set[] + set1 = Set[1, set2] + set2.add(set1) + + assert_raises(ArgumentError) { + set1.flatten! + } + + # test5; miscellaneous + empty = Set[] + set = Set[Set[empty, "a"],Set[empty, "b"]] + + assert_nothing_raised { + set.flatten + } + + set1 = empty.merge(Set["no_more", set]) + + assert_nil(Set.new(0..31).flatten!) + + x = Set[Set[],Set[1,2]].flatten! + y = Set[1,2] + + assert_equal(x, y) + end + + def test_include? + set = Set[1,2,3] + + assert_equal(true, set.include?(1)) + assert_equal(true, set.include?(2)) + assert_equal(true, set.include?(3)) + assert_equal(false, set.include?(0)) + assert_equal(false, set.include?(nil)) + + set = Set["1",nil,"2",nil,"0","1",false] + assert_equal(true, set.include?(nil)) + assert_equal(true, set.include?(false)) + assert_equal(true, set.include?("1")) + assert_equal(false, set.include?(0)) + assert_equal(false, set.include?(true)) + end + + def test_superset? + set = Set[1,2,3] + + assert_raises(ArgumentError) { + set.superset?() + } + + assert_raises(ArgumentError) { + set.superset?(2) + } + + assert_raises(ArgumentError) { + set.superset?([2]) + } + + assert_equal(true, set.superset?(Set[])) + assert_equal(true, set.superset?(Set[1,2])) + assert_equal(true, set.superset?(Set[1,2,3])) + assert_equal(false, set.superset?(Set[1,2,3,4])) + assert_equal(false, set.superset?(Set[1,4])) + + assert_equal(true, Set[].superset?(Set[])) + end + + def test_proper_superset? + set = Set[1,2,3] + + assert_raises(ArgumentError) { + set.proper_superset?() + } + + assert_raises(ArgumentError) { + set.proper_superset?(2) + } + + assert_raises(ArgumentError) { + set.proper_superset?([2]) + } + + assert_equal(true, set.proper_superset?(Set[])) + assert_equal(true, set.proper_superset?(Set[1,2])) + assert_equal(false, set.proper_superset?(Set[1,2,3])) + assert_equal(false, set.proper_superset?(Set[1,2,3,4])) + assert_equal(false, set.proper_superset?(Set[1,4])) + + assert_equal(false, Set[].proper_superset?(Set[])) + end + + def test_subset? + set = Set[1,2,3] + + assert_raises(ArgumentError) { + set.subset?() + } + + assert_raises(ArgumentError) { + set.subset?(2) + } + + assert_raises(ArgumentError) { + set.subset?([2]) + } + + assert_equal(true, set.subset?(Set[1,2,3,4])) + assert_equal(true, set.subset?(Set[1,2,3])) + assert_equal(false, set.subset?(Set[1,2])) + assert_equal(false, set.subset?(Set[])) + + assert_equal(true, Set[].subset?(Set[1])) + assert_equal(true, Set[].subset?(Set[])) + end + + def test_proper_subset? + set = Set[1,2,3] + + assert_raises(ArgumentError) { + set.proper_subset?() + } + + assert_raises(ArgumentError) { + set.proper_subset?(2) + } + + assert_raises(ArgumentError) { + set.proper_subset?([2]) + } + + assert_equal(true, set.proper_subset?(Set[1,2,3,4])) + assert_equal(false, set.proper_subset?(Set[1,2,3])) + assert_equal(false, set.proper_subset?(Set[1,2])) + assert_equal(false, set.proper_subset?(Set[])) + + assert_equal(false, Set[].proper_subset?(Set[])) + end + + def test_each + ary = [1,3,5,7,10,20] + set = Set.new(ary) + + assert_raises(LocalJumpError) { + set.each + } + + assert_nothing_raised { + set.each { |o| + ary.delete(o) or raise "unexpected element: #{o}" + } + + ary.empty? or raise "forgotten elements: #{ary.join(', ')}" + } + end + + def test_add + set = Set[1,2,3] + + ret = set.add(2) + assert_same(set, ret) + assert_equal(Set[1,2,3], set) + + ret = set.add?(2) + assert_nil(ret) + assert_equal(Set[1,2,3], set) + + ret = set.add(4) + assert_same(set, ret) + assert_equal(Set[1,2,3,4], set) + + ret = set.add?(5) + assert_same(set, ret) + assert_equal(Set[1,2,3,4,5], set) + end + + def test_delete + set = Set[1,2,3] + + ret = set.delete(4) + assert_same(set, ret) + assert_equal(Set[1,2,3], set) + + ret = set.delete?(4) + assert_nil(ret) + assert_equal(Set[1,2,3], set) + + ret = set.delete(2) + assert_equal(set, ret) + assert_equal(Set[1,3], set) + + ret = set.delete?(1) + assert_equal(set, ret) + assert_equal(Set[3], set) + end + + def test_delete_if + set = Set.new(1..10) + ret = set.delete_if { |i| i > 10 } + assert_same(set, ret) + assert_equal(Set.new(1..10), set) + + set = Set.new(1..10) + ret = set.delete_if { |i| i % 3 == 0 } + assert_same(set, ret) + assert_equal(Set[1,2,4,5,7,8,10], set) + end + + def test_collect! + set = Set[1,2,3,'a','b','c',-1..1,2..4] + + ret = set.collect! { |i| + case i + when Numeric + i * 2 + when String + i.upcase + else + nil + end + } + + assert_same(set, ret) + assert_equal(Set[2,4,6,'A','B','C',nil], set) + end + + def test_reject! + set = Set.new(1..10) + + ret = set.reject! { |i| i > 10 } + assert_nil(ret) + assert_equal(Set.new(1..10), set) + + ret = set.reject! { |i| i % 3 == 0 } + assert_same(set, ret) + assert_equal(Set[1,2,4,5,7,8,10], set) + end + + def test_merge + set = Set[1,2,3] + + ret = set.merge([2,4,6]) + assert_same(set, ret) + assert_equal(Set[1,2,3,4,6], set) + end + + def test_subtract + set = Set[1,2,3] + + ret = set.subtract([2,4,6]) + assert_same(set, ret) + assert_equal(Set[1,3], set) + end + + def test_plus + set = Set[1,2,3] + + ret = set + [2,4,6] + assert_not_same(set, ret) + assert_equal(Set[1,2,3,4,6], ret) + end + + def test_minus + set = Set[1,2,3] + + ret = set - [2,4,6] + assert_not_same(set, ret) + assert_equal(Set[1,3], ret) + end + + def test_and + set = Set[1,2,3,4] + + ret = set & [2,4,6] + assert_not_same(set, ret) + assert_equal(Set[2,4], ret) + end + + def test_xor + set = Set[1,2,3,4] + ret = set ^ [2,4,5,5] + assert_not_same(set, ret) + assert_equal(Set[1,3,5], ret) + end + + def test_eq + set1 = Set[2,3,1] + set2 = Set[1,2,3] + + assert_equal(set1, set1) + assert_equal(set1, set2) + assert_not_equal(Set[1], [1]) + + set1 = Class.new(Set)["a", "b"] + set2 = Set["a", "b", set1] + set1 = set1.add(set1.clone) + +# assert_equal(set1, set2) +# assert_equal(set2, set1) + assert_equal(set2, set2.clone) + assert_equal(set1.clone, set1) + + assert_not_equal(Set[Exception.new,nil], Set[Exception.new,Exception.new], "[ruby-dev:26127]") + end + + # def test_hash + # end + + # def test_eql? + # end + + def test_classify + set = Set.new(1..10) + ret = set.classify { |i| i % 3 } + + assert_equal(3, ret.size) + assert_instance_of(Hash, ret) + ret.each_value { |value| assert_instance_of(Set, value) } + assert_equal(Set[3,6,9], ret[0]) + assert_equal(Set[1,4,7,10], ret[1]) + assert_equal(Set[2,5,8], ret[2]) + end + + def test_divide + set = Set.new(1..10) + ret = set.divide { |i| i % 3 } + + assert_equal(3, ret.size) + n = 0 + ret.each { |s| n += s.size } + assert_equal(set.size, n) + assert_equal(set, ret.flatten) + + set = Set[7,10,5,11,1,3,4,9,0] + ret = set.divide { |a,b| (a - b).abs == 1 } + + assert_equal(4, ret.size) + n = 0 + ret.each { |s| n += s.size } + assert_equal(set.size, n) + assert_equal(set, ret.flatten) + ret.each { |s| + if s.include?(0) + assert_equal(Set[0,1], s) + elsif s.include?(3) + assert_equal(Set[3,4,5], s) + elsif s.include?(7) + assert_equal(Set[7], s) + elsif s.include?(9) + assert_equal(Set[9,10,11], s) + else + raise "unexpected group: #{s.inspect}" + end + } + end + + def test_inspect + set1 = Set[1] + + assert_equal('#', set1.inspect) + + set2 = Set[Set[0], 1, 2, set1] + assert_equal(false, set2.inspect.include?('#')) + + set1.add(set2) + assert_equal(true, set1.inspect.include?('#')) + end + + # def test_pretty_print + # end + + # def test_pretty_print_cycle + # end +end + +class TC_SortedSet < Test::Unit::TestCase + def test_sortedset + s = SortedSet[4,5,3,1,2] + + assert_equal([1,2,3,4,5], s.to_a) + + prev = nil + s.each { |o| assert(prev < o) if prev; prev = o } + assert_not_nil(prev) + + s.map! { |o| -2 * o } + + assert_equal([-10,-8,-6,-4,-2], s.to_a) + + prev = nil + s.each { |o| assert(prev < o) if prev; prev = o } + assert_not_nil(prev) + + s = SortedSet.new([2,1,3]) { |o| o * -2 } + assert_equal([-6,-4,-2], s.to_a) + end +end + +class TC_Enumerable < Test::Unit::TestCase + def test_to_set + ary = [2,5,4,3,2,1,3] + + set = ary.to_set + assert_instance_of(Set, set) + assert_equal([1,2,3,4,5], set.sort) + + set = ary.to_set { |o| o * -2 } + assert_instance_of(Set, set) + assert_equal([-10,-8,-6,-4,-2], set.sort) + + set = ary.to_set(SortedSet) + assert_instance_of(SortedSet, set) + assert_equal([1,2,3,4,5], set.to_a) + + set = ary.to_set(SortedSet) { |o| o * -2 } + assert_instance_of(SortedSet, set) + assert_equal([-10,-8,-6,-4,-2], set.sort) + end +end + +# class TC_RestricedSet < Test::Unit::TestCase +# def test_s_new +# assert_raises(ArgumentError) { RestricedSet.new } +# +# s = RestricedSet.new([-1,2,3]) { |o| o > 0 } +# assert_equal([2,3], s.sort) +# end +# +# def test_restriction_proc +# s = RestricedSet.new([-1,2,3]) { |o| o > 0 } +# +# f = s.restriction_proc +# assert_instance_of(Proc, f) +# assert(f[1]) +# assert(!f[0]) +# end +# +# def test_replace +# s = RestricedSet.new(-3..3) { |o| o > 0 } +# assert_equal([1,2,3], s.sort) +# +# s.replace([-2,0,3,4,5]) +# assert_equal([3,4,5], s.sort) +# end +# +# def test_merge +# s = RestricedSet.new { |o| o > 0 } +# s.merge(-5..5) +# assert_equal([1,2,3,4,5], s.sort) +# +# s.merge([10,-10,-8,8]) +# assert_equal([1,2,3,4,5,8,10], s.sort) +# end +# end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/sha1.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/sha1.rb new file mode 100644 index 0000000000..af04158a04 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/sha1.rb @@ -0,0 +1,23 @@ +# just for compatibility; requiring "sha1" is obsoleted +# +# $RoughId: sha1.rb,v 1.4 2001/07/13 15:38:27 knu Exp $ +# $Id: sha1.rb 12008 2007-03-06 10:12:12Z knu $ + +require 'digest/sha1' + +class SHA1 < Digest::SHA1 + class << self + alias orig_new new + def new(str = nil) + if str + orig_new.update(str) + else + orig_new + end + end + + def sha1(*args) + new(*args) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell.rb new file mode 100644 index 0000000000..039f849ef5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell.rb @@ -0,0 +1,269 @@ +# +# shell.rb - +# $Release Version: 0.6.0 $ +# $Revision: 1.8 $ +# $Date: 2001/03/19 09:01:11 $ +# by Keiju ISHITSUKA(Nippon Rational Inc.) +# +# -- +# +# +# + +require "e2mmap" +require "thread" + +require "shell/error" +require "shell/command-processor" +require "shell/process-controller" + +class Shell + @RCS_ID='-$Id: shell.rb,v 1.8 2001/03/19 09:01:11 keiju Exp keiju $-' + + include Error + extend Exception2MessageMapper + +# @cascade = true + # debug: true -> normal debug + # debug: 1 -> eval definition debug + # debug: 2 -> detail inspect debug + @debug = false + @verbose = true + + class << Shell + attr :cascade, true + attr :debug, true + attr :verbose, true + +# alias cascade? cascade + alias debug? debug + alias verbose? verbose + @verbose = true + + def debug=(val) + @debug = val + @verbose = val if val + end + + def cd(path) + sh = new + sh.cd path + sh + end + + def default_system_path + if @default_system_path + @default_system_path + else + ENV["PATH"].split(":") + end + end + + def default_system_path=(path) + @default_system_path = path + end + + def default_record_separator + if @default_record_separator + @default_record_separator + else + $/ + end + end + + def default_record_separator=(rs) + @default_record_separator = rs + end + end + + def initialize + @cwd = Dir.pwd + @dir_stack = [] + @umask = nil + + @system_path = Shell.default_system_path + @record_separator = Shell.default_record_separator + + @command_processor = CommandProcessor.new(self) + @process_controller = ProcessController.new(self) + + @verbose = Shell.verbose + @debug = Shell.debug + end + + attr_reader :system_path + + def system_path=(path) + @system_path = path + rehash + end + + attr :umask, true + attr :record_separator, true + + attr :verbose, true + attr :debug, true + + def debug=(val) + @debug = val + @verbose = val if val + end + + alias verbose? verbose + alias debug? debug + + attr_reader :command_processor + attr_reader :process_controller + + def expand_path(path) + File.expand_path(path, @cwd) + end + + # Most Shell commands are defined via CommandProcessor + + # + # Dir related methods + # + # Shell#cwd/dir/getwd/pwd + # Shell#chdir/cd + # Shell#pushdir/pushd + # Shell#popdir/popd + # Shell#mkdir + # Shell#rmdir + + attr :cwd + alias dir cwd + alias getwd cwd + alias pwd cwd + + attr :dir_stack + alias dirs dir_stack + + # If called as iterator, it restores the current directory when the + # block ends. + def chdir(path = nil) + if iterator? + cwd_old = @cwd + begin + chdir(path) + yield + ensure + chdir(cwd_old) + end + else + path = "~" unless path + @cwd = expand_path(path) + notify "current dir: #{@cwd}" + rehash + self + end + end + alias cd chdir + + def pushdir(path = nil) + if iterator? + pushdir(path) + begin + yield + ensure + popdir + end + elsif path + @dir_stack.push @cwd + chdir path + notify "dir stack: [#{@dir_stack.join ', '}]" + self + else + if pop = @dir_stack.pop + @dir_stack.push @cwd + chdir pop + notify "dir stack: [#{@dir_stack.join ', '}]" + self + else + Shell.Fail DirStackEmpty + end + end + end + alias pushd pushdir + + def popdir + if pop = @dir_stack.pop + chdir pop + notify "dir stack: [#{@dir_stack.join ', '}]" + self + else + Shell.Fail DirStackEmpty + end + end + alias popd popdir + + + # + # process management + # + def jobs + @process_controller.jobs + end + + def kill(sig, command) + @process_controller.kill_job(sig, command) + end + + # + # command definitions + # + def Shell.def_system_command(command, path = command) + CommandProcessor.def_system_command(command, path) + end + + def Shell.undef_system_command(command) + CommandProcessor.undef_system_command(command) + end + + def Shell.alias_command(ali, command, *opts, &block) + CommandProcessor.alias_command(ali, command, *opts, &block) + end + + def Shell.unalias_command(ali) + CommandProcessor.unalias_command(ali) + end + + def Shell.install_system_commands(pre = "sys_") + CommandProcessor.install_system_commands(pre) + end + + # + def inspect + if debug.kind_of?(Integer) && debug > 2 + super + else + to_s + end + end + + def self.notify(*opts, &block) + Thread.exclusive do + if opts[-1].kind_of?(String) + yorn = verbose? + else + yorn = opts.pop + end + return unless yorn + + _head = true + print opts.collect{|mes| + mes = mes.dup + yield mes if iterator? + if _head + _head = false + "shell: " + mes + else + " " + mes + end + }.join("\n")+"\n" + end + end + + CommandProcessor.initialize + CommandProcessor.run_config +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell/builtin-command.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell/builtin-command.rb new file mode 100644 index 0000000000..2e1b361c75 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell/builtin-command.rb @@ -0,0 +1,154 @@ +# +# shell/builtin-command.rb - +# $Release Version: 0.6.0 $ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd) +# +# -- +# +# +# + +require "shell/filter" + +class Shell + class BuiltInCommand + # + def expand_path(path) + @shell.expand_path(path) + end + + # + # File related commands + # Shell#foreach + # Shell#open + # Shell#unlink + # Shell#test + # + # - + # + # CommandProcessor#foreach(path, rs) + # path: String + # rs: String - record separator + # iterator + # Same as: + # File#foreach (when path is file) + # Dir#foreach (when path is directory) + # path is relative to pwd + # + def foreach(path = nil, *rs) + path = "." unless path + path = expand_path(path) + + if File.directory?(path) + Dir.foreach(path){|fn| yield fn} + else + IO.foreach(path, *rs){|l| yield l} + end + end + + # + # CommandProcessor#open(path, mode) + # path: String + # mode: String + # return: File or Dir + # Same as: + # File#open (when path is file) + # Dir#open (when path is directory) + # mode has an effect only when path is a file + # + def open(path, mode) + path = expand_path(path) + if File.directory?(path) + Dir.open(path) + else + effect_umask do + File.open(path, mode) + end + end + end + # public :open + + # + # CommandProcessor#unlink(path) + # same as: + # Dir#unlink (when path is directory) + # File#unlink (when path is file) + # + def unlink(path) + path = expand_path(path) + if File.directory?(path) + Dir.unlink(path) + else + IO.unlink(path) + end + end + + # + # CommandProcessor#test(command, file1, file2) + # CommandProcessor#[command, file1, file2] + # command: char or String or Symbol + # file1: String + # file2: String(optional) + # return: Boolean + # same as: + # test() (when command is char or length 1 string or symbol) + # FileTest.command (others) + # example: + # sh[?e, "foo"] + # sh[:e, "foo"] + # sh["e", "foo"] + # sh[:exists?, "foo"] + # sh["exists?", "foo"] + # + def test(command, file1, file2=nil) + file1 = expand_path(file1) + file2 = expand_path(file2) if file2 + command = command.id2name if command.kind_of?(Symbol) + + case command + when Integer + top_level_test(command, file1, file2) + when String + if command.size == 1 + if file2 + top_level_test(command, file1, file2) + else + top_level_test(command, file1) + end + else + if file2 + FileTest.send(command, file1, file2) + else + FileTest.send(command, file1) + end + end + end + end + alias [] test + + # + # Dir related methods + # + # Shell#mkdir + # Shell#rmdir + # + #-- + # + # CommandProcessor#mkdir(*path) + # path: String + # same as Dir.mkdir() + # + def mkdir(*path) + for dir in path + Dir.mkdir(expand_path(dir)) + end + end + + # + # CommandProcessor#rmdir(*path) + # path: String + # same as Dir.rmdir() + # + def rmdir(*path) + for dir in path + Dir.rmdir(expand_path(dir)) + end + end + + # + # CommandProcessor#system(command, *opts) + # command: String + # opts: String + # return: SystemCommand + # Same as system() function + # example: + # print sh.system("ls", "-l") + # sh.system("ls", "-l") | sh.head > STDOUT + # + def system(command, *opts) + if opts.empty? + if command =~ /\*|\?|\{|\}|\[|\]|<|>|\(|\)|~|&|\||\\|\$|;|'|`|"|\n/ + return SystemCommand.new(@shell, find_system_command("sh"), "-c", command) + else + command, *opts = command.split(/\s+/) + end + end + SystemCommand.new(@shell, find_system_command(command), *opts) + end + + # + # ProcessCommand#rehash + # clear command hash table. + # + def rehash + @system_commands = {} + end + + # + # ProcessCommand#transact + # + def check_point + @shell.process_controller.wait_all_jobs_execution + end + alias finish_all_jobs check_point + + def transact(&block) + begin + @shell.instance_eval(&block) + ensure + check_point + end + end + + # + # internal commands + # + def out(dev = STDOUT, &block) + dev.print transact(&block) + end + + def echo(*strings) + Echo.new(@shell, *strings) + end + + def cat(*filenames) + Cat.new(@shell, *filenames) + end + + # def sort(*filenames) + # Sort.new(self, *filenames) + # end + + def glob(pattern) + Glob.new(@shell, pattern) + end + + def append(to, filter) + case to + when String + AppendFile.new(@shell, to, filter) + when IO + AppendIO.new(@shell, to, filter) + else + Shell.Fail Error::CantApplyMethod, "append", to.class + end + end + + def tee(file) + Tee.new(@shell, file) + end + + def concat(*jobs) + Concat.new(@shell, *jobs) + end + + # %pwd, %cwd -> @pwd + def notify(*opts, &block) + Thread.exclusive do + Shell.notify(*opts) {|mes| + yield mes if iterator? + + mes.gsub!("%pwd", "#{@cwd}") + mes.gsub!("%cwd", "#{@cwd}") + } + end + end + + # + # private functions + # + def effect_umask + if @shell.umask + Thread.critical = true + save = File.umask + begin + yield + ensure + File.umask save + Thread.critical = false + end + else + yield + end + end + private :effect_umask + + def find_system_command(command) + return command if /^\// =~ command + case path = @system_commands[command] + when String + if exists?(path) + return path + else + Shell.Fail Error::CommandNotFound, command + end + when false + Shell.Fail Error::CommandNotFound, command + end + + for p in @shell.system_path + path = join(p, command) + if FileTest.exists?(path) + @system_commands[command] = path + return path + end + end + @system_commands[command] = false + Shell.Fail Error::CommandNotFound, command + end + + # + # CommandProcessor.def_system_command(command, path) + # command: String + # path: String + # define 'command()' method as method. + # + def self.def_system_command(command, path = command) + begin + eval((d = %Q[def #{command}(*opts) + SystemCommand.new(@shell, '#{path}', *opts) + end]), nil, __FILE__, __LINE__ - 1) + rescue SyntaxError + Shell.notify "warn: Can't define #{command} path: #{path}." + end + Shell.notify "Define #{command} path: #{path}.", Shell.debug? + Shell.notify("Definition of #{command}: ", d, + Shell.debug.kind_of?(Integer) && Shell.debug > 1) + end + + def self.undef_system_command(command) + command = command.id2name if command.kind_of?(Symbol) + remove_method(command) + Shell.module_eval{remove_method(command)} + Filter.module_eval{remove_method(command)} + self + end + + # define command alias + # ex) + # def_alias_command("ls_c", "ls", "-C", "-F") + # def_alias_command("ls_c", "ls"){|*opts| ["-C", "-F", *opts]} + # + @alias_map = {} + def self.alias_map + @alias_map + end + def self.alias_command(ali, command, *opts, &block) + ali = ali.id2name if ali.kind_of?(Symbol) + command = command.id2name if command.kind_of?(Symbol) + begin + if iterator? + @alias_map[ali.intern] = proc + + eval((d = %Q[def #{ali}(*opts) + @shell.__send__(:#{command}, + *(CommandProcessor.alias_map[:#{ali}].call *opts)) + end]), nil, __FILE__, __LINE__ - 1) + + else + args = opts.collect{|opt| '"' + opt + '"'}.join(",") + eval((d = %Q[def #{ali}(*opts) + @shell.__send__(:#{command}, #{args}, *opts) + end]), nil, __FILE__, __LINE__ - 1) + end + rescue SyntaxError + Shell.notify "warn: Can't alias #{ali} command: #{command}." + Shell.notify("Definition of #{ali}: ", d) + raise + end + Shell.notify "Define #{ali} command: #{command}.", Shell.debug? + Shell.notify("Definition of #{ali}: ", d, + Shell.debug.kind_of?(Integer) && Shell.debug > 1) + self + end + + def self.unalias_command(ali) + ali = ali.id2name if ali.kind_of?(Symbol) + @alias_map.delete ali.intern + undef_system_command(ali) + end + + # + # CommandProcessor.def_builtin_commands(delegation_class, command_specs) + # delegation_class: Class or Module + # command_specs: [[command_name, [argument,...]],...] + # command_name: String + # arguments: String + # FILENAME?? -> expand_path(filename??) + # *FILENAME?? -> filename??.collect{|f|expand_path(f)}.join(", ") + # define command_name(argument,...) as + # delegation_class.command_name(argument,...) + # + def self.def_builtin_commands(delegation_class, command_specs) + for meth, args in command_specs + arg_str = args.collect{|arg| arg.downcase}.join(", ") + call_arg_str = args.collect{ + |arg| + case arg + when /^(FILENAME.*)$/ + format("expand_path(%s)", $1.downcase) + when /^(\*FILENAME.*)$/ + # \*FILENAME* -> filenames.collect{|fn| expand_path(fn)}.join(", ") + $1.downcase + '.collect{|fn| expand_path(fn)}' + else + arg + end + }.join(", ") + d = %Q[def #{meth}(#{arg_str}) + #{delegation_class}.#{meth}(#{call_arg_str}) + end] + Shell.notify "Define #{meth}(#{arg_str})", Shell.debug? + Shell.notify("Definition of #{meth}: ", d, + Shell.debug.kind_of?(Integer) && Shell.debug > 1) + eval d + end + end + + # + # CommandProcessor.install_system_commands(pre) + # pre: String - command name prefix + # defines every command which belongs in default_system_path via + # CommandProcessor.command(). It doesn't define already defined + # methods twice. By default, "pre_" is prefixes to each method + # name. Characters that may not be used in a method name are + # all converted to '_'. Definition errors are just ignored. + # + def self.install_system_commands(pre = "sys_") + defined_meth = {} + for m in Shell.methods + defined_meth[m] = true + end + sh = Shell.new + for path in Shell.default_system_path + next unless sh.directory? path + sh.cd path + sh.foreach do + |cn| + if !defined_meth[pre + cn] && sh.file?(cn) && sh.executable?(cn) + command = (pre + cn).gsub(/\W/, "_").sub(/^([0-9])/, '_\1') + begin + def_system_command(command, sh.expand_path(cn)) + rescue + Shell.notify "warn: Can't define #{command} path: #{cn}" + end + defined_meth[command] = command + end + end + end + end + + #---------------------------------------------------------------------- + # + # class initializing methods - + # + #---------------------------------------------------------------------- + def self.add_delegate_command_to_shell(id) + id = id.intern if id.kind_of?(String) + name = id.id2name + if Shell.method_defined?(id) + Shell.notify "warn: override definnition of Shell##{name}." + Shell.notify "warn: alias Shell##{name} to Shell##{name}_org.\n" + Shell.module_eval "alias #{name}_org #{name}" + end + Shell.notify "method added: Shell##{name}.", Shell.debug? + Shell.module_eval(%Q[def #{name}(*args, &block) + begin + @command_processor.__send__(:#{name}, *args, &block) + rescue Exception + $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #` + $@.delete_if{|s| /^\\(eval\\):/ =~ s} + raise + end + end], __FILE__, __LINE__) + + if Shell::Filter.method_defined?(id) + Shell.notify "warn: override definnition of Shell::Filter##{name}." + Shell.notify "warn: alias Shell##{name} to Shell::Filter##{name}_org." + Filter.module_eval "alias #{name}_org #{name}" + end + Shell.notify "method added: Shell::Filter##{name}.", Shell.debug? + Filter.module_eval(%Q[def #{name}(*args, &block) + begin + self | @shell.__send__(:#{name}, *args, &block) + rescue Exception + $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #` + $@.delete_if{|s| /^\\(eval\\):/ =~ s} + raise + end + end], __FILE__, __LINE__) + end + + # + # define default builtin commands + # + def self.install_builtin_commands + # method related File. + # (exclude open/foreach/unlink) + normal_delegation_file_methods = [ + ["atime", ["FILENAME"]], + ["basename", ["fn", "*opts"]], + ["chmod", ["mode", "*FILENAMES"]], + ["chown", ["owner", "group", "*FILENAME"]], + ["ctime", ["FILENAMES"]], + ["delete", ["*FILENAMES"]], + ["dirname", ["FILENAME"]], + ["ftype", ["FILENAME"]], + ["join", ["*items"]], + ["link", ["FILENAME_O", "FILENAME_N"]], + ["lstat", ["FILENAME"]], + ["mtime", ["FILENAME"]], + ["readlink", ["FILENAME"]], + ["rename", ["FILENAME_FROM", "FILENAME_TO"]], + # ["size", ["FILENAME"]], + ["split", ["pathname"]], + ["stat", ["FILENAME"]], + ["symlink", ["FILENAME_O", "FILENAME_N"]], + ["truncate", ["FILENAME", "length"]], + ["utime", ["atime", "mtime", "*FILENAMES"]]] + + def_builtin_commands(File, normal_delegation_file_methods) + alias_method :rm, :delete + + # method related FileTest + def_builtin_commands(FileTest, + FileTest.singleton_methods(false).collect{|m| [m, ["FILENAME"]]}) + + # method related ftools + normal_delegation_ftools_methods = [ + ["syscopy", ["FILENAME_FROM", "FILENAME_TO"]], + ["copy", ["FILENAME_FROM", "FILENAME_TO"]], + ["move", ["FILENAME_FROM", "FILENAME_TO"]], + ["compare", ["FILENAME_FROM", "FILENAME_TO"]], + ["safe_unlink", ["*FILENAMES"]], + ["makedirs", ["*FILENAMES"]], + # ["chmod", ["mode", "*FILENAMES"]], + ["install", ["FILENAME_FROM", "FILENAME_TO", "mode"]], + ] + def_builtin_commands(File, + normal_delegation_ftools_methods) + alias_method :cmp, :compare + alias_method :mv, :move + alias_method :cp, :copy + alias_method :rm_f, :safe_unlink + alias_method :mkpath, :makedirs + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell/error.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell/error.rb new file mode 100644 index 0000000000..959f926412 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell/error.rb @@ -0,0 +1,26 @@ +# +# shell/error.rb - +# $Release Version: 0.6.0 $ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd) +# +# -- +# +# +# + +require "e2mmap" + +class Shell + module Error + extend Exception2MessageMapper + def_e2message TypeError, "wrong argument type %s (expected %s)" + + def_exception :DirStackEmpty, "Directory stack empty." + def_exception :CantDefine, "Can't define method(%s, %s)." + def_exception :CantApplyMethod, "This method(%s) does not apply to this type(%s)." + def_exception :CommandNotFound, "Command not found(%s)." + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell/filter.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell/filter.rb new file mode 100644 index 0000000000..1d2c0b9ea0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell/filter.rb @@ -0,0 +1,110 @@ +# +# shell/filter.rb - +# $Release Version: 0.6.0 $ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd) +# +# -- +# +# +# + +class Shell + # + # Filter + # A method to require + # each() + # + class Filter + include Enumerable + + def initialize(sh) + @shell = sh # parent shell + @input = nil # input filter + end + + attr_reader :input + + def input=(filter) + @input = filter + end + + def each(rs = nil) + rs = @shell.record_separator unless rs + if @input + @input.each(rs){|l| yield l} + end + end + + def < (src) + case src + when String + cat = Cat.new(@shell, src) + cat | self + when IO + self.input = src + self + else + Shell.Fail Error::CantApplyMethod, "<", to.class + end + end + + def > (to) + case to + when String + dst = @shell.open(to, "w") + begin + each(){|l| dst << l} + ensure + dst.close + end + when IO + each(){|l| to << l} + else + Shell.Fail Error::CantApplyMethod, ">", to.class + end + self + end + + def >> (to) + begin + Shell.cd(@shell.pwd).append(to, self) + rescue CantApplyMethod + Shell.Fail Error::CantApplyMethod, ">>", to.class + end + end + + def | (filter) + filter.input = self + if active? + @shell.process_controller.start_job filter + end + filter + end + + def + (filter) + Join.new(@shell, self, filter) + end + + def to_a + ary = [] + each(){|l| ary.push l} + ary + end + + def to_s + str = "" + each(){|l| str.concat l} + str + end + + def inspect + if @shell.debug.kind_of?(Integer) && @shell.debug > 2 + super + else + to_s + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell/process-controller.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell/process-controller.rb new file mode 100644 index 0000000000..0ae975c8d1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/shell/process-controller.rb @@ -0,0 +1,260 @@ +# +# shell/process-controller.rb - +# $Release Version: 0.6.0 $ +# $Revision: 12008 $ +# $Date: 2007-03-06 19:12:12 +0900 (Tue, 06 Mar 2007) $ +# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd) +# +# -- +# +# +# + +require "mutex_m" +require "monitor" +require "sync" + +class Shell + class ProcessController + + @ProcessControllers = {} + @ProcessControllers.extend Mutex_m + + class< true +# a.new # NoMethodError - new is private ... +# +# * ``The instance'' is created at instantiation time, in other +# words the first call of Klass.instance(), thus +# +# class OtherKlass +# include Singleton +# # ... +# end +# ObjectSpace.each_object(OtherKlass){} # => 0. +# +# * This behavior is preserved under inheritance and cloning. +# +# +# +# This is achieved by marking +# * Klass.new and Klass.allocate - as private +# +# Providing (or modifying) the class methods +# * Klass.inherited(sub_klass) and Klass.clone() - +# to ensure that the Singleton pattern is properly +# inherited and cloned. +# +# * Klass.instance() - returning ``the instance''. After a +# successful self modifying (normally the first) call the +# method body is a simple: +# +# def Klass.instance() +# return @__instance__ +# end +# +# * Klass._load(str) - calling Klass.instance() +# +# * Klass._instantiate?() - returning ``the instance'' or +# nil. This hook method puts a second (or nth) thread calling +# Klass.instance() on a waiting loop. The return value +# signifies the successful completion or premature termination +# of the first, or more generally, current "instantiation thread". +# +# +# The instance method of Singleton are +# * clone and dup - raising TypeErrors to prevent cloning or duping +# +# * _dump(depth) - returning the empty string. Marshalling strips +# by default all state information, e.g. instance variables and +# taint state, from ``the instance''. Providing custom _load(str) +# and _dump(depth) hooks allows the (partially) resurrections of +# a previous state of ``the instance''. + + + +module Singleton + # disable build-in copying methods + def clone + raise TypeError, "can't clone instance of singleton #{self.class}" + end + def dup + raise TypeError, "can't dup instance of singleton #{self.class}" + end + + private + # default marshalling strategy + def _dump(depth=-1) + '' + end +end + + +class << Singleton + # Method body of first instance call. + FirstInstanceCall = proc do + # @__instance__ takes on one of the following values + # * nil - before and after a failed creation + # * false - during creation + # * sub_class instance - after a successful creation + # the form makes up for the lack of returns in progs + Thread.critical = true + if @__instance__.nil? + @__instance__ = false + Thread.critical = false + begin + @__instance__ = new + ensure + if @__instance__ + class < mes + puts mes +end + + + +puts "\nThreaded example with exception and customized #_instantiate?() hook"; p +Thread.abort_on_exception = false + +class Ups < SomeSingletonClass + def initialize + self.class.__sleep + puts "initialize called by thread ##{Thread.current[:i]}" + end +end + +class << Ups + def _instantiate? + @enter.push Thread.current[:i] + while false.equal?(@__instance__) + Thread.critical = false + sleep 0.08 + Thread.critical = true + end + @leave.push Thread.current[:i] + @__instance__ + end + + def __sleep + sleep(rand(0.08)) + end + + def new + begin + __sleep + raise "boom - thread ##{Thread.current[:i]} failed to create instance" + ensure + # simple flip-flop + class << self + remove_method :new + end + end + end + + def instantiate_all + @enter = [] + @leave = [] + 1.upto(9) {|i| + Thread.new { + begin + Thread.current[:i] = i + __sleep + instance + rescue RuntimeError => mes + puts mes + end + } + } + puts "Before there were #{num_of_instances(self)}" + sleep 3 + puts "Now there is #{num_of_instances(self)}" + puts "#{@enter.join '; '} was the order of threads entering the waiting loop" + puts "#{@leave.join '; '} was the order of threads leaving the waiting loop" + end +end + + +Ups.instantiate_all +# results in message like +# Before there were 0 Ups instance(s) +# boom - thread #6 failed to create instance +# initialize called by thread #3 +# Now there is 1 Ups instance(s) +# 3; 2; 1; 8; 4; 7; 5 was the order of threads entering the waiting loop +# 3; 2; 1; 7; 4; 8; 5 was the order of threads leaving the waiting loop + + +puts "\nLets see if class level cloning really works" +Yup = Ups.clone +def Yup.new + begin + __sleep + raise "boom - thread ##{Thread.current[:i]} failed to create instance" + ensure + # simple flip-flop + class << self + remove_method :new + end + end +end +Yup.instantiate_all + + +puts "\n\n","Customized marshalling" +class A + include Singleton + attr_accessor :persist, :die + def _dump(depth) + # this strips the @die information from the instance + Marshal.dump(@persist,depth) + end +end + +def A._load(str) + instance.persist = Marshal.load(str) + instance +end + +a = A.instance +a.persist = ["persist"] +a.die = "die" +a.taint + +stored_state = Marshal.dump(a) +# change state +a.persist = nil +a.die = nil +b = Marshal.load(stored_state) +p a == b # => true +p a.persist # => ["persist"] +p a.die # => nil + + +puts "\n\nSingleton with overridden default #inherited() hook" +class Up +end +def Up.inherited(sub_klass) + puts "#{sub_klass} subclasses #{self}" +end + + +class Middle < Up + include Singleton +end + +class Down < Middle; end + +puts "and basic \"Down test\" is #{Down.instance == Down.instance}\n +Various exceptions" + +begin + module AModule + include Singleton + end +rescue TypeError => mes + puts mes #=> Inclusion of the OO-Singleton module in module AModule +end + +begin + 'aString'.extend Singleton +rescue NoMethodError => mes + puts mes #=> undefined method `extend_object' for Singleton:Module +end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/attachment.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/attachment.rb new file mode 100644 index 0000000000..1a59b14018 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/attachment.rb @@ -0,0 +1,107 @@ +# soap/attachment.rb: SOAP4R - SwA implementation. +# Copyright (C) 2002, 2003 Jamie Herre and NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/baseData' +require 'soap/mapping' + + +module SOAP + + +class SOAPAttachment < SOAPExternalReference + attr_reader :data + + def initialize(value) + super() + @data = value + end + +private + + def external_contentid + @data.contentid + end +end + + +class Attachment + attr_reader :io + attr_accessor :contenttype + + def initialize(string_or_readable = nil) + @string_or_readable = string_or_readable + @contenttype = "application/octet-stream" + @contentid = nil + end + + def contentid + @contentid ||= Attachment.contentid(self) + end + + def contentid=(contentid) + @contentid = contentid + end + + def mime_contentid + '<' + contentid + '>' + end + + def content + if @content == nil and @string_or_readable != nil + @content = @string_or_readable.respond_to?(:read) ? + @string_or_readable.read : @string_or_readable + end + @content + end + + def to_s + content + end + + def write(out) + out.write(content) + end + + def save(filename) + File.open(filename, "wb") do |f| + write(f) + end + end + + def self.contentid(obj) + # this needs to be fixed + [obj.__id__.to_s, Process.pid.to_s].join('.') + end + + def self.mime_contentid(obj) + '<' + contentid(obj) + '>' + end +end + + +module Mapping + class AttachmentFactory < SOAP::Mapping::Factory + def obj2soap(soap_class, obj, info, map) + soap_obj = soap_class.new(obj) + mark_marshalled_obj(obj, soap_obj) + soap_obj + end + + def soap2obj(obj_class, node, info, map) + obj = node.data + mark_unmarshalled_obj(node, obj) + return true, obj + end + end + + DefaultRegistry.add(::SOAP::Attachment, ::SOAP::SOAPAttachment, + AttachmentFactory.new, nil) +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/baseData.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/baseData.rb new file mode 100644 index 0000000000..0e8b00d450 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/baseData.rb @@ -0,0 +1,942 @@ +# soap/baseData.rb: SOAP4R - Base type library +# Copyright (C) 2000, 2001, 2003-2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/datatypes' +require 'soap/soap' + + +module SOAP + + +### +## Mix-in module for SOAP base type classes. +# +module SOAPModuleUtils + include SOAP + +public + + def decode(elename) + d = self.new + d.elename = elename + d + end +end + + +### +## for SOAP type(base and compound) +# +module SOAPType + attr_accessor :encodingstyle + attr_accessor :elename + attr_accessor :id + attr_reader :precedents + attr_accessor :root + attr_accessor :parent + attr_accessor :position + attr_reader :extraattr + attr_accessor :definedtype + + def initialize(*arg) + super + @encodingstyle = nil + @elename = XSD::QName::EMPTY + @id = nil + @precedents = [] + @root = false + @parent = nil + @position = nil + @definedtype = nil + @extraattr = {} + end + + def inspect + if self.is_a?(XSD::NSDBase) + sprintf("#<%s:0x%x %s %s>", self.class.name, __id__, self.elename, self.type) + else + sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.elename) + end + end + + def rootnode + node = self + while node = node.parent + break if SOAPEnvelope === node + end + node + end +end + + +### +## for SOAP base type +# +module SOAPBasetype + include SOAPType + include SOAP + + def initialize(*arg) + super + end +end + + +### +## for SOAP compound type +# +module SOAPCompoundtype + include SOAPType + include SOAP + + def initialize(*arg) + super + end +end + + +### +## Convenience datatypes. +# +class SOAPReference < XSD::NSDBase + include SOAPBasetype + extend SOAPModuleUtils + +public + + attr_accessor :refid + + # Override the definition in SOAPBasetype. + def initialize(obj = nil) + super() + @type = XSD::QName::EMPTY + @refid = nil + @obj = nil + __setobj__(obj) if obj + end + + def __getobj__ + @obj + end + + def __setobj__(obj) + @obj = obj + @refid = @obj.id || SOAPReference.create_refid(@obj) + @obj.id = @refid unless @obj.id + @obj.precedents << self + # Copies NSDBase information + @obj.type = @type unless @obj.type + end + + # Why don't I use delegate.rb? + # -> delegate requires target object type at initialize time. + # Why don't I use forwardable.rb? + # -> forwardable requires a list of forwarding methods. + # + # ToDo: Maybe I should use forwardable.rb and give it a methods list like + # delegate.rb... + # + def method_missing(msg_id, *params) + if @obj + @obj.send(msg_id, *params) + else + nil + end + end + + def refidstr + '#' + @refid + end + + def self.create_refid(obj) + 'id' + obj.__id__.to_s + end + + def self.decode(elename, refidstr) + if /\A#(.*)\z/ =~ refidstr + refid = $1 + elsif /\Acid:(.*)\z/ =~ refidstr + refid = $1 + else + raise ArgumentError.new("illegal refid #{refidstr}") + end + d = super(elename) + d.refid = refid + d + end +end + + +class SOAPExternalReference < XSD::NSDBase + include SOAPBasetype + extend SOAPModuleUtils + + def initialize + super() + @type = XSD::QName::EMPTY + end + + def referred + rootnode.external_content[external_contentid] = self + end + + def refidstr + 'cid:' + external_contentid + end + +private + + def external_contentid + raise NotImplementedError.new + end +end + + +class SOAPNil < XSD::XSDNil + include SOAPBasetype + extend SOAPModuleUtils +end + +# SOAPRawString is for sending raw string. In contrast to SOAPString, +# SOAP4R does not do XML encoding and does not convert its CES. The string it +# holds is embedded to XML instance directly as a 'xsd:string'. +class SOAPRawString < XSD::XSDString + include SOAPBasetype + extend SOAPModuleUtils +end + + +### +## Basic datatypes. +# +class SOAPAnySimpleType < XSD::XSDAnySimpleType + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPString < XSD::XSDString + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPBoolean < XSD::XSDBoolean + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPDecimal < XSD::XSDDecimal + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPFloat < XSD::XSDFloat + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPDouble < XSD::XSDDouble + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPDuration < XSD::XSDDuration + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPDateTime < XSD::XSDDateTime + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPTime < XSD::XSDTime + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPDate < XSD::XSDDate + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPGYearMonth < XSD::XSDGYearMonth + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPGYear < XSD::XSDGYear + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPGMonthDay < XSD::XSDGMonthDay + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPGDay < XSD::XSDGDay + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPGMonth < XSD::XSDGMonth + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPHexBinary < XSD::XSDHexBinary + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPBase64 < XSD::XSDBase64Binary + include SOAPBasetype + extend SOAPModuleUtils + Type = QName.new(EncodingNamespace, Base64Literal) + +public + # Override the definition in SOAPBasetype. + def initialize(value = nil) + super(value) + @type = Type + end + + def as_xsd + @type = XSD::XSDBase64Binary::Type + end +end + +class SOAPAnyURI < XSD::XSDAnyURI + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPQName < XSD::XSDQName + include SOAPBasetype + extend SOAPModuleUtils +end + + +class SOAPInteger < XSD::XSDInteger + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPNonPositiveInteger < XSD::XSDNonPositiveInteger + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPNegativeInteger < XSD::XSDNegativeInteger + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPLong < XSD::XSDLong + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPInt < XSD::XSDInt + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPShort < XSD::XSDShort + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPByte < XSD::XSDByte + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPNonNegativeInteger < XSD::XSDNonNegativeInteger + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPUnsignedLong < XSD::XSDUnsignedLong + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPUnsignedInt < XSD::XSDUnsignedInt + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPUnsignedShort < XSD::XSDUnsignedShort + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPUnsignedByte < XSD::XSDUnsignedByte + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPPositiveInteger < XSD::XSDPositiveInteger + include SOAPBasetype + extend SOAPModuleUtils +end + + +### +## Compound datatypes. +# +class SOAPStruct < XSD::NSDBase + include SOAPCompoundtype + include Enumerable + +public + + def initialize(type = nil) + super() + @type = type || XSD::QName::EMPTY + @array = [] + @data = [] + end + + def to_s() + str = '' + self.each do |key, data| + str << "#{key}: #{data}\n" + end + str + end + + def add(name, value) + add_member(name, value) + end + + def [](idx) + if idx.is_a?(Range) + @data[idx] + elsif idx.is_a?(Integer) + if (idx > @array.size) + raise ArrayIndexOutOfBoundsError.new('In ' << @type.name) + end + @data[idx] + else + if @array.include?(idx) + @data[@array.index(idx)] + else + nil + end + end + end + + def []=(idx, data) + if @array.include?(idx) + data.parent = self if data.respond_to?(:parent=) + @data[@array.index(idx)] = data + else + add(idx, data) + end + end + + def key?(name) + @array.include?(name) + end + + def members + @array + end + + def to_obj + hash = {} + proptype = {} + each do |k, v| + value = v.respond_to?(:to_obj) ? v.to_obj : v.to_s + case proptype[k] + when :single + hash[k] = [hash[k], value] + proptype[k] = :multi + when :multi + hash[k] << value + else + hash[k] = value + proptype[k] = :single + end + end + hash + end + + def each + idx = 0 + while idx < @array.length + yield(@array[idx], @data[idx]) + idx += 1 + end + end + + def replace + members.each do |member| + self[member] = yield(self[member]) + end + end + + def self.decode(elename, type) + s = SOAPStruct.new(type) + s.elename = elename + s + end + +private + + def add_member(name, value = nil) + value = SOAPNil.new() if value.nil? + @array.push(name) + value.elename = value.elename.dup_name(name) + @data.push(value) + value.parent = self if value.respond_to?(:parent=) + value + end +end + + +# SOAPElement is not typed so it is not derived from NSDBase. +class SOAPElement + include Enumerable + + attr_accessor :encodingstyle + + attr_accessor :elename + attr_accessor :id + attr_reader :precedents + attr_accessor :root + attr_accessor :parent + attr_accessor :position + attr_accessor :extraattr + + attr_accessor :qualified + + def initialize(elename, text = nil) + if !elename.is_a?(XSD::QName) + elename = XSD::QName.new(nil, elename) + end + @encodingstyle = LiteralNamespace + @elename = elename + @id = nil + @precedents = [] + @root = false + @parent = nil + @position = nil + @extraattr = {} + + @qualified = nil + + @array = [] + @data = [] + @text = text + end + + def inspect + sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.elename) + end + + # Text interface. + attr_accessor :text + alias data text + + # Element interfaces. + def add(value) + add_member(value.elename.name, value) + end + + def [](idx) + if @array.include?(idx) + @data[@array.index(idx)] + else + nil + end + end + + def []=(idx, data) + if @array.include?(idx) + data.parent = self if data.respond_to?(:parent=) + @data[@array.index(idx)] = data + else + add(data) + end + end + + def key?(name) + @array.include?(name) + end + + def members + @array + end + + def to_obj + if members.empty? + @text + else + hash = {} + proptype = {} + each do |k, v| + value = v.respond_to?(:to_obj) ? v.to_obj : v.to_s + case proptype[k] + when :single + hash[k] = [hash[k], value] + proptype[k] = :multi + when :multi + hash[k] << value + else + hash[k] = value + proptype[k] = :single + end + end + hash + end + end + + def each + idx = 0 + while idx < @array.length + yield(@array[idx], @data[idx]) + idx += 1 + end + end + + def self.decode(elename) + o = SOAPElement.new(elename) + o + end + + def self.from_obj(obj, namespace = nil) + o = SOAPElement.new(nil) + case obj + when nil + o.text = nil + when Hash + obj.each do |elename, value| + if value.is_a?(Array) + value.each do |subvalue| + child = from_obj(subvalue, namespace) + child.elename = to_elename(elename, namespace) + o.add(child) + end + else + child = from_obj(value, namespace) + child.elename = to_elename(elename, namespace) + o.add(child) + end + end + else + o.text = obj.to_s + end + o + end + + def self.to_elename(obj, namespace = nil) + if obj.is_a?(XSD::QName) + obj + elsif /\A(.+):([^:]+)\z/ =~ obj.to_s + XSD::QName.new($1, $2) + else + XSD::QName.new(namespace, obj.to_s) + end + end + +private + + def add_member(name, value) + add_accessor(name) + @array.push(name) + @data.push(value) + value.parent = self if value.respond_to?(:parent=) + value + end + + if RUBY_VERSION > "1.7.0" + def add_accessor(name) + methodname = name + if self.respond_to?(methodname) + methodname = safe_accessor_name(methodname) + end + Mapping.define_singleton_method(self, methodname) do + @data[@array.index(name)] + end + Mapping.define_singleton_method(self, methodname + '=') do |value| + @data[@array.index(name)] = value + end + end + else + def add_accessor(name) + methodname = safe_accessor_name(name) + instance_eval <<-EOS + def #{methodname} + @data[@array.index(#{name.dump})] + end + + def #{methodname}=(value) + @data[@array.index(#{name.dump})] = value + end + EOS + end + end + + def safe_accessor_name(name) + "var_" << name.gsub(/[^a-zA-Z0-9_]/, '') + end +end + + +class SOAPArray < XSD::NSDBase + include SOAPCompoundtype + include Enumerable + +public + + attr_accessor :sparse + + attr_reader :offset, :rank + attr_accessor :size, :size_fixed + attr_reader :arytype + + def initialize(type = nil, rank = 1, arytype = nil) + super() + @type = type || ValueArrayName + @rank = rank + @data = Array.new + @sparse = false + @offset = Array.new(rank, 0) + @size = Array.new(rank, 0) + @size_fixed = false + @position = nil + @arytype = arytype + end + + def offset=(var) + @offset = var + @sparse = true + end + + def add(value) + self[*(@offset)] = value + end + + def [](*idxary) + if idxary.size != @rank + raise ArgumentError.new("given #{idxary.size} params does not match rank: #{@rank}") + end + + retrieve(idxary) + end + + def []=(*idxary) + value = idxary.slice!(-1) + + if idxary.size != @rank + raise ArgumentError.new("given #{idxary.size} params(#{idxary})" + + " does not match rank: #{@rank}") + end + + idx = 0 + while idx < idxary.size + if idxary[idx] + 1 > @size[idx] + @size[idx] = idxary[idx] + 1 + end + idx += 1 + end + + data = retrieve(idxary[0, idxary.size - 1]) + data[idxary.last] = value + + if value.is_a?(SOAPType) + value.elename = ITEM_NAME + # Sync type + unless @type.name + @type = XSD::QName.new(value.type.namespace, + SOAPArray.create_arytype(value.type.name, @rank)) + end + value.type ||= @type + end + + @offset = idxary + value.parent = self if value.respond_to?(:parent=) + offsetnext + end + + def each + @data.each do |data| + yield(data) + end + end + + def to_a + @data.dup + end + + def replace + @data = deep_map(@data) do |ele| + yield(ele) + end + end + + def deep_map(ary, &block) + ary.collect do |ele| + if ele.is_a?(Array) + deep_map(ele, &block) + else + new_obj = block.call(ele) + new_obj.elename = ITEM_NAME + new_obj + end + end + end + + def include?(var) + traverse_data(@data) do |v, *rank| + if v.is_a?(SOAPBasetype) && v.data == var + return true + end + end + false + end + + def traverse + traverse_data(@data) do |v, *rank| + unless @sparse + yield(v) + else + yield(v, *rank) if v && !v.is_a?(SOAPNil) + end + end + end + + def soap2array(ary) + traverse_data(@data) do |v, *position| + iteary = ary + rank = 1 + while rank < position.size + idx = position[rank - 1] + if iteary[idx].nil? + iteary = iteary[idx] = Array.new + else + iteary = iteary[idx] + end + rank += 1 + end + if block_given? + iteary[position.last] = yield(v) + else + iteary[position.last] = v + end + end + end + + def position + @position + end + +private + + ITEM_NAME = XSD::QName.new(nil, 'item') + + def retrieve(idxary) + data = @data + rank = 1 + while rank <= idxary.size + idx = idxary[rank - 1] + if data[idx].nil? + data = data[idx] = Array.new + else + data = data[idx] + end + rank += 1 + end + data + end + + def traverse_data(data, rank = 1) + idx = 0 + while idx < ranksize(rank) + if rank < @rank + traverse_data(data[idx], rank + 1) do |*v| + v[1, 0] = idx + yield(*v) + end + else + yield(data[idx], idx) + end + idx += 1 + end + end + + def ranksize(rank) + @size[rank - 1] + end + + def offsetnext + move = false + idx = @offset.size - 1 + while !move && idx >= 0 + @offset[idx] += 1 + if @size_fixed + if @offset[idx] < @size[idx] + move = true + else + @offset[idx] = 0 + idx -= 1 + end + else + move = true + end + end + end + + # Module function + +public + + def self.decode(elename, type, arytype) + typestr, nofary = parse_type(arytype.name) + rank = nofary.count(',') + 1 + plain_arytype = XSD::QName.new(arytype.namespace, typestr) + o = SOAPArray.new(type, rank, plain_arytype) + size = [] + nofary.split(',').each do |s| + if s.empty? + size.clear + break + else + size << s.to_i + end + end + unless size.empty? + o.size = size + o.size_fixed = true + end + o.elename = elename + o + end + +private + + def self.create_arytype(typename, rank) + "#{typename}[" << ',' * (rank - 1) << ']' + end + + TypeParseRegexp = Regexp.new('^(.+)\[([\d,]*)\]$') + + def self.parse_type(string) + TypeParseRegexp =~ string + return $1, $2 + end +end + + +require 'soap/mapping/typeMap' + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/element.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/element.rb new file mode 100644 index 0000000000..cc58b5d341 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/element.rb @@ -0,0 +1,258 @@ +# SOAP4R - SOAP elements library +# Copyright (C) 2000, 2001, 2003-2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/qname' +require 'soap/baseData' + + +module SOAP + + +### +## SOAP elements +# +module SOAPEnvelopeElement; end + +class SOAPFault < SOAPStruct + include SOAPEnvelopeElement + include SOAPCompoundtype + +public + + def faultcode + self['faultcode'] + end + + def faultstring + self['faultstring'] + end + + def faultactor + self['faultactor'] + end + + def detail + self['detail'] + end + + def faultcode=(rhs) + self['faultcode'] = rhs + end + + def faultstring=(rhs) + self['faultstring'] = rhs + end + + def faultactor=(rhs) + self['faultactor'] = rhs + end + + def detail=(rhs) + self['detail'] = rhs + end + + def initialize(faultcode = nil, faultstring = nil, faultactor = nil, detail = nil) + super(EleFaultName) + @elename = EleFaultName + @encodingstyle = EncodingNamespace + + if faultcode + self.faultcode = faultcode + self.faultstring = faultstring + self.faultactor = faultactor + self.detail = detail + self.faultcode.elename = EleFaultCodeName if self.faultcode + self.faultstring.elename = EleFaultStringName if self.faultstring + self.faultactor.elename = EleFaultActorName if self.faultactor + self.detail.elename = EleFaultDetailName if self.detail + end + faultcode.parent = self if faultcode + faultstring.parent = self if faultstring + faultactor.parent = self if faultactor + detail.parent = self if detail + end + + def encode(generator, ns, attrs = {}) + SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace) + SOAPGenerator.assign_ns(attrs, ns, EncodingNamespace) + attrs[ns.name(AttrEncodingStyleName)] = EncodingNamespace + name = ns.name(@elename) + generator.encode_tag(name, attrs) + yield(self.faultcode) + yield(self.faultstring) + yield(self.faultactor) + yield(self.detail) if self.detail + generator.encode_tag_end(name, true) + end +end + + +class SOAPBody < SOAPStruct + include SOAPEnvelopeElement + + def initialize(data = nil, is_fault = false) + super(nil) + @elename = EleBodyName + @encodingstyle = nil + if data + if data.respond_to?(:elename) + add(data.elename.name, data) + else + data.to_a.each do |datum| + add(datum.elename.name, datum) + end + end + end + @is_fault = is_fault + end + + def encode(generator, ns, attrs = {}) + name = ns.name(@elename) + generator.encode_tag(name, attrs) + if @is_fault + yield(@data) + else + @data.each do |data| + yield(data) + end + end + generator.encode_tag_end(name, true) + end + + def root_node + @data.each do |node| + if node.root == 1 + return node + end + end + # No specified root... + @data.each do |node| + if node.root != 0 + return node + end + end + + raise Parser::FormatDecodeError.new('no root element') + end +end + + +class SOAPHeaderItem < XSD::NSDBase + include SOAPEnvelopeElement + include SOAPCompoundtype + +public + + attr_accessor :element + attr_accessor :mustunderstand + attr_accessor :encodingstyle + + def initialize(element, mustunderstand = true, encodingstyle = nil) + super() + @type = nil + @element = element + @mustunderstand = mustunderstand + @encodingstyle = encodingstyle + element.parent = self if element + end + + def encode(generator, ns, attrs = {}) + attrs.each do |key, value| + @element.extraattr[key] = value + end + @element.extraattr[ns.name(AttrMustUnderstandName)] = + (@mustunderstand ? '1' : '0') + if @encodingstyle + @element.extraattr[ns.name(AttrEncodingStyleName)] = @encodingstyle + end + @element.encodingstyle = @encodingstyle if !@element.encodingstyle + yield(@element) + end +end + + +class SOAPHeader < SOAPStruct + include SOAPEnvelopeElement + + def initialize + super(nil) + @elename = EleHeaderName + @encodingstyle = nil + end + + def encode(generator, ns, attrs = {}) + name = ns.name(@elename) + generator.encode_tag(name, attrs) + @data.each do |data| + yield(data) + end + generator.encode_tag_end(name, true) + end + + def add(name, value) + mu = (value.extraattr[AttrMustUnderstandName] == '1') + encstyle = value.extraattr[AttrEncodingStyleName] + item = SOAPHeaderItem.new(value, mu, encstyle) + super(name, item) + end + + def length + @data.length + end + alias size length +end + + +class SOAPEnvelope < XSD::NSDBase + include SOAPEnvelopeElement + include SOAPCompoundtype + + attr_reader :header + attr_reader :body + attr_reader :external_content + + def initialize(header = nil, body = nil) + super() + @type = nil + @elename = EleEnvelopeName + @encodingstyle = nil + @header = header + @body = body + @external_content = {} + header.parent = self if header + body.parent = self if body + end + + def header=(header) + header.parent = self + @header = header + end + + def body=(body) + body.parent = self + @body = body + end + + def encode(generator, ns, attrs = {}) + SOAPGenerator.assign_ns(attrs, ns, elename.namespace, SOAPNamespaceTag) + name = ns.name(@elename) + generator.encode_tag(name, attrs) + + yield(@header) if @header and @header.length > 0 + yield(@body) + + generator.encode_tag_end(name, true) + end + + def to_ary + [header, body] + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/encodingstyle/aspDotNetHandler.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/encodingstyle/aspDotNetHandler.rb new file mode 100644 index 0000000000..fd7e0fa217 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/encodingstyle/aspDotNetHandler.rb @@ -0,0 +1,213 @@ +# SOAP4R - ASP.NET EncodingStyle handler library +# Copyright (C) 2001, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/encodingstyle/handler' + + +module SOAP +module EncodingStyle + + +class ASPDotNetHandler < Handler + Namespace = 'http://tempuri.org/ASP.NET' + add_handler + + def initialize(charset = nil) + super(charset) + @textbuf = '' + @decode_typemap = nil + end + + + ### + ## encode interface. + # + def encode_data(generator, ns, data, parent) + attrs = {} + # ASPDotNetHandler is intended to be used for accessing an ASP.NET doc/lit + # service as an rpc/encoded service. in the situation, local elements + # should be qualified. propagate parent's namespace to children. + if data.elename.namespace.nil? + data.elename.namespace = parent.elename.namespace + end + name = generator.encode_name(ns, data, attrs) + case data + when SOAPRawString + generator.encode_tag(name, attrs) + generator.encode_rawstring(data.to_s) + when XSD::XSDString + generator.encode_tag(name, attrs) + generator.encode_string(@charset ? + XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s) + when XSD::XSDAnySimpleType + generator.encode_tag(name, attrs) + generator.encode_string(data.to_s) + when SOAPStruct + generator.encode_tag(name, attrs) + data.each do |key, value| + generator.encode_child(ns, value, data) + end + when SOAPArray + generator.encode_tag(name, attrs) + data.traverse do |child, *rank| + data.position = nil + generator.encode_child(ns, child, data) + end + else + raise EncodingStyleError.new( + "unknown object:#{data} in this encodingStyle") + end + end + + def encode_data_end(generator, ns, data, parent) + name = generator.encode_name_end(ns, data) + cr = data.is_a?(SOAPCompoundtype) + generator.encode_tag_end(name, cr) + end + + + ### + ## decode interface. + # + class SOAPTemporalObject + attr_accessor :parent + + def initialize + @parent = nil + end + end + + class SOAPUnknown < SOAPTemporalObject + def initialize(handler, elename) + super() + @handler = handler + @elename = elename + end + + def as_struct + o = SOAPStruct.decode(@elename, XSD::AnyTypeName) + o.parent = @parent + o.type.name = @name + @handler.decode_parent(@parent, o) + o + end + + def as_string + o = SOAPString.decode(@elename) + o.parent = @parent + @handler.decode_parent(@parent, o) + o + end + + def as_nil + o = SOAPNil.decode(@elename) + o.parent = @parent + @handler.decode_parent(@parent, o) + o + end + end + + def decode_tag(ns, elename, attrs, parent) + @textbuf = '' + o = SOAPUnknown.new(self, elename) + o.parent = parent + o + end + + def decode_tag_end(ns, node) + o = node.node + if o.is_a?(SOAPUnknown) + newnode = o.as_string +# if /\A\s*\z/ =~ @textbuf +# o.as_struct +# else +# o.as_string +# end + node.replace_node(newnode) + o = node.node + end + + decode_textbuf(o) + @textbuf = '' + end + + def decode_text(ns, text) + # @textbuf is set at decode_tag_end. + @textbuf << text + end + + def decode_prologue + end + + def decode_epilogue + end + + def decode_parent(parent, node) + case parent.node + when SOAPUnknown + newparent = parent.node.as_struct + node.parent = newparent + parent.replace_node(newparent) + decode_parent(parent, node) + + when SOAPStruct + data = parent.node[node.elename.name] + case data + when nil + parent.node.add(node.elename.name, node) + when SOAPArray + name, type_ns = node.elename.name, node.type.namespace + data.add(node) + node.elename, node.type.namespace = name, type_ns + else + parent.node[node.elename.name] = SOAPArray.new + name, type_ns = data.elename.name, data.type.namespace + parent.node[node.elename.name].add(data) + data.elename.name, data.type.namespace = name, type_ns + name, type_ns = node.elename.name, node.type.namespace + parent.node[node.elename.name].add(node) + node.elename.name, node.type.namespace = name, type_ns + end + + when SOAPArray + if node.position + parent.node[*(decode_arypos(node.position))] = node + parent.node.sparse = true + else + parent.node.add(node) + end + + when SOAPBasetype + raise EncodingStyleError.new("SOAP base type must not have a child") + + else + # SOAPUnknown does not have parent. + # raise EncodingStyleError.new("illegal parent: #{parent}") + end + end + +private + + def decode_textbuf(node) + if node.is_a?(XSD::XSDString) + if @charset + node.set(XSD::Charset.encoding_from_xml(@textbuf, @charset)) + else + node.set(@textbuf) + end + else + # Nothing to do... + end + end +end + +ASPDotNetHandler.new + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/encodingstyle/handler.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/encodingstyle/handler.rb new file mode 100644 index 0000000000..c015417435 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/encodingstyle/handler.rb @@ -0,0 +1,100 @@ +# SOAP4R - EncodingStyle handler library +# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/soap' +require 'soap/baseData' +require 'soap/element' + + +module SOAP +module EncodingStyle + + +class Handler + @@handlers = {} + + class EncodingStyleError < Error; end + + class << self + def uri + self::Namespace + end + + def handler(uri) + @@handlers[uri] + end + + def each + @@handlers.each do |key, value| + yield(value) + end + end + + private + + def add_handler + @@handlers[self.uri] = self + end + end + + attr_reader :charset + attr_accessor :generate_explicit_type + def decode_typemap=(definedtypes) + @decode_typemap = definedtypes + end + + def initialize(charset) + @charset = charset + @generate_explicit_type = true + @decode_typemap = nil + end + + ### + ## encode interface. + # + # Returns a XML instance as a string. + def encode_data(generator, ns, data, parent) + raise NotImplementError + end + + def encode_data_end(generator, ns, data, parent) + raise NotImplementError + end + + def encode_prologue + end + + def encode_epilogue + end + + ### + ## decode interface. + # + # Returns SOAP/OM data. + def decode_tag(ns, name, attrs, parent) + raise NotImplementError.new('Method decode_tag must be defined in derived class.') + end + + def decode_tag_end(ns, name) + raise NotImplementError.new('Method decode_tag_end must be defined in derived class.') + end + + def decode_text(ns, text) + raise NotImplementError.new('Method decode_text must be defined in derived class.') + end + + def decode_prologue + end + + def decode_epilogue + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/encodingstyle/literalHandler.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/encodingstyle/literalHandler.rb new file mode 100644 index 0000000000..59c7205366 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/encodingstyle/literalHandler.rb @@ -0,0 +1,226 @@ +# SOAP4R - XML Literal EncodingStyle handler library +# Copyright (C) 2001, 2003-2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/encodingstyle/handler' + + +module SOAP +module EncodingStyle + + +class LiteralHandler < Handler + Namespace = SOAP::LiteralNamespace + add_handler + + def initialize(charset = nil) + super(charset) + @textbuf = '' + end + + + ### + ## encode interface. + # + def encode_data(generator, ns, data, parent) + attrs = {} + name = generator.encode_name(ns, data, attrs) + data.extraattr.each do |k, v| + # ToDo: check generator.attributeformdefault here + if k.is_a?(XSD::QName) + if k.namespace + SOAPGenerator.assign_ns(attrs, ns, k.namespace) + k = ns.name(k) + else + k = k.name + end + end + attrs[k] = v + end + case data + when SOAPRawString + generator.encode_tag(name, attrs) + generator.encode_rawstring(data.to_s) + when XSD::XSDString + generator.encode_tag(name, attrs) + str = data.to_s + str = XSD::Charset.encoding_to_xml(str, @charset) if @charset + generator.encode_string(str) + when XSD::XSDAnySimpleType + generator.encode_tag(name, attrs) + generator.encode_string(data.to_s) + when SOAPStruct + generator.encode_tag(name, attrs) + data.each do |key, value| + generator.encode_child(ns, value, data) + end + when SOAPArray + generator.encode_tag(name, attrs) + data.traverse do |child, *rank| + data.position = nil + generator.encode_child(ns, child, data) + end + when SOAPElement + # passes 2 times for simplifying namespace definition + data.each do |key, value| + if value.elename.namespace + SOAPGenerator.assign_ns(attrs, ns, value.elename.namespace) + end + end + generator.encode_tag(name, attrs) + generator.encode_rawstring(data.text) if data.text + data.each do |key, value| + generator.encode_child(ns, value, data) + end + else + raise EncodingStyleError.new( + "unknown object:#{data} in this encodingStyle") + end + end + + def encode_data_end(generator, ns, data, parent) + name = generator.encode_name_end(ns, data) + cr = (data.is_a?(SOAPCompoundtype) or + (data.is_a?(SOAPElement) and !data.text)) + generator.encode_tag_end(name, cr) + end + + + ### + ## decode interface. + # + class SOAPTemporalObject + attr_accessor :parent + + def initialize + @parent = nil + end + end + + class SOAPUnknown < SOAPTemporalObject + def initialize(handler, elename, extraattr) + super() + @handler = handler + @elename = elename + @extraattr = extraattr + end + + def as_element + o = SOAPElement.decode(@elename) + o.parent = @parent + o.extraattr.update(@extraattr) + @handler.decode_parent(@parent, o) + o + end + + def as_string + o = SOAPString.decode(@elename) + o.parent = @parent + o.extraattr.update(@extraattr) + @handler.decode_parent(@parent, o) + o + end + + def as_nil + o = SOAPNil.decode(@elename) + o.parent = @parent + o.extraattr.update(@extraattr) + @handler.decode_parent(@parent, o) + o + end + end + + def decode_tag(ns, elename, attrs, parent) + @textbuf = '' + o = SOAPUnknown.new(self, elename, decode_attrs(ns, attrs)) + o.parent = parent + o + end + + def decode_tag_end(ns, node) + o = node.node + if o.is_a?(SOAPUnknown) + newnode = if /\A\s*\z/ =~ @textbuf + o.as_element + else + o.as_string + end + node.replace_node(newnode) + o = node.node + end + + decode_textbuf(o) + @textbuf = '' + end + + def decode_text(ns, text) + # @textbuf is set at decode_tag_end. + @textbuf << text + end + + def decode_attrs(ns, attrs) + extraattr = {} + attrs.each do |key, value| + qname = ns.parse_local(key) + extraattr[qname] = value + end + extraattr + end + + def decode_prologue + end + + def decode_epilogue + end + + def decode_parent(parent, node) + return unless parent.node + case parent.node + when SOAPUnknown + newparent = parent.node.as_element + node.parent = newparent + parent.replace_node(newparent) + decode_parent(parent, node) + when SOAPElement + parent.node.add(node) + node.parent = parent.node + when SOAPStruct + parent.node.add(node.elename.name, node) + node.parent = parent.node + when SOAPArray + if node.position + parent.node[*(decode_arypos(node.position))] = node + parent.node.sparse = true + else + parent.node.add(node) + end + node.parent = parent.node + else + raise EncodingStyleError.new("illegal parent: #{parent.node}") + end + end + +private + + def decode_textbuf(node) + if node.is_a?(XSD::XSDString) + if @charset + node.set(XSD::Charset.encoding_from_xml(@textbuf, @charset)) + else + node.set(@textbuf) + end + else + # Nothing to do... + end + end +end + +LiteralHandler.new + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/encodingstyle/soapHandler.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/encodingstyle/soapHandler.rb new file mode 100644 index 0000000000..a522392625 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/encodingstyle/soapHandler.rb @@ -0,0 +1,582 @@ +# SOAP4R - SOAP EncodingStyle handler library +# Copyright (C) 2001, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/encodingstyle/handler' + + +module SOAP +module EncodingStyle + + +class SOAPHandler < Handler + Namespace = SOAP::EncodingNamespace + add_handler + + def initialize(charset = nil) + super(charset) + @refpool = [] + @idpool = [] + @textbuf = '' + @is_first_top_ele = true + end + + + ### + ## encode interface. + # + def encode_data(generator, ns, data, parent) + attrs = encode_attrs(generator, ns, data, parent) + if parent && parent.is_a?(SOAPArray) && parent.position + attrs[ns.name(AttrPositionName)] = "[#{parent.position.join(',')}]" + end + name = generator.encode_name(ns, data, attrs) + case data + when SOAPReference + attrs['href'] = data.refidstr + generator.encode_tag(name, attrs) + when SOAPExternalReference + data.referred + attrs['href'] = data.refidstr + generator.encode_tag(name, attrs) + when SOAPRawString + generator.encode_tag(name, attrs) + generator.encode_rawstring(data.to_s) + when XSD::XSDString + generator.encode_tag(name, attrs) + generator.encode_string(@charset ? + XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s) + when XSD::XSDAnySimpleType + generator.encode_tag(name, attrs) + generator.encode_string(data.to_s) + when SOAPStruct + generator.encode_tag(name, attrs) + data.each do |key, value| + generator.encode_child(ns, value, data) + end + when SOAPArray + generator.encode_tag(name, attrs) + data.traverse do |child, *rank| + data.position = data.sparse ? rank : nil + generator.encode_child(ns, child, data) + end + else + raise EncodingStyleError.new( + "unknown object:#{data} in this encodingStyle") + end + end + + def encode_data_end(generator, ns, data, parent) + name = generator.encode_name_end(ns, data) + cr = data.is_a?(SOAPCompoundtype) + generator.encode_tag_end(name, cr) + end + + + ### + ## decode interface. + # + class SOAPTemporalObject + attr_accessor :parent + attr_accessor :position + attr_accessor :id + attr_accessor :root + + def initialize + @parent = nil + @position = nil + @id = nil + @root = nil + end + end + + class SOAPUnknown < SOAPTemporalObject + attr_reader :type + attr_accessor :definedtype + attr_reader :extraattr + + def initialize(handler, elename, type, extraattr) + super() + @handler = handler + @elename = elename + @type = type + @extraattr = extraattr + @definedtype = nil + end + + def as_struct + o = SOAPStruct.decode(@elename, @type) + o.id = @id + o.root = @root + o.parent = @parent + o.position = @position + o.extraattr.update(@extraattr) + @handler.decode_parent(@parent, o) + o + end + + def as_string + o = SOAPString.decode(@elename) + o.id = @id + o.root = @root + o.parent = @parent + o.position = @position + o.extraattr.update(@extraattr) + @handler.decode_parent(@parent, o) + o + end + + def as_nil + o = SOAPNil.decode(@elename) + o.id = @id + o.root = @root + o.parent = @parent + o.position = @position + o.extraattr.update(@extraattr) + @handler.decode_parent(@parent, o) + o + end + end + + def decode_tag(ns, elename, attrs, parent) + @textbuf = '' + is_nil, type, arytype, root, offset, position, href, id, extraattr = + decode_attrs(ns, attrs) + o = nil + if is_nil + o = SOAPNil.decode(elename) + elsif href + o = SOAPReference.decode(elename, href) + @refpool << o + elsif @decode_typemap + o = decode_tag_by_wsdl(ns, elename, type, parent.node, arytype, extraattr) + else + o = decode_tag_by_type(ns, elename, type, parent.node, arytype, extraattr) + end + + if o.is_a?(SOAPArray) + if offset + o.offset = decode_arypos(offset) + o.sparse = true + else + o.sparse = false + end + end + + o.parent = parent + o.id = id + o.root = root + o.position = position + + unless o.is_a?(SOAPTemporalObject) + @idpool << o if o.id + decode_parent(parent, o) + end + o + end + + def decode_tag_end(ns, node) + o = node.node + if o.is_a?(SOAPUnknown) + newnode = if /\A\s*\z/ =~ @textbuf + o.as_struct + else + o.as_string + end + if newnode.id + @idpool << newnode + end + node.replace_node(newnode) + o = node.node + end + decode_textbuf(o) + # unlink definedtype + o.definedtype = nil + end + + def decode_text(ns, text) + @textbuf << text + end + + def decode_prologue + @refpool.clear + @idpool.clear + @is_first_top_ele = true + end + + def decode_epilogue + decode_resolve_id + end + + def decode_parent(parent, node) + return unless parent.node + case parent.node + when SOAPUnknown + newparent = parent.node.as_struct + node.parent = newparent + if newparent.id + @idpool << newparent + end + parent.replace_node(newparent) + decode_parent(parent, node) + when SOAPStruct + parent.node.add(node.elename.name, node) + node.parent = parent.node + when SOAPArray + if node.position + parent.node[*(decode_arypos(node.position))] = node + parent.node.sparse = true + else + parent.node.add(node) + end + node.parent = parent.node + else + raise EncodingStyleError.new("illegal parent: #{parent.node}") + end + end + +private + + def content_ranksize(typename) + typename.scan(/\[[\d,]*\]$/)[0] + end + + def content_typename(typename) + typename.sub(/\[,*\]$/, '') + end + + def create_arytype(ns, data) + XSD::QName.new(data.arytype.namespace, + content_typename(data.arytype.name) + "[#{data.size.join(',')}]") + end + + def encode_attrs(generator, ns, data, parent) + attrs = {} + return attrs if data.is_a?(SOAPReference) + + if !parent || parent.encodingstyle != EncodingNamespace + if @generate_explicit_type + SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace) + attrs[ns.name(AttrEncodingStyleName)] = EncodingNamespace + end + data.encodingstyle = EncodingNamespace + end + + if data.is_a?(SOAPNil) + attrs[ns.name(XSD::AttrNilName)] = XSD::NilValue + elsif @generate_explicit_type + if data.type.namespace + SOAPGenerator.assign_ns(attrs, ns, data.type.namespace) + end + if data.is_a?(SOAPArray) + if data.arytype.namespace + SOAPGenerator.assign_ns(attrs, ns, data.arytype.namespace) + end + SOAPGenerator.assign_ns(attrs, ns, EncodingNamespace) + attrs[ns.name(AttrArrayTypeName)] = ns.name(create_arytype(ns, data)) + if data.type.name + attrs[ns.name(XSD::AttrTypeName)] = ns.name(data.type) + end + elsif parent && parent.is_a?(SOAPArray) && (parent.arytype == data.type) + # No need to add. + elsif !data.type.namespace + # No need to add. + else + attrs[ns.name(XSD::AttrTypeName)] = ns.name(data.type) + end + end + + data.extraattr.each do |key, value| + SOAPGenerator.assign_ns(attrs, ns, key.namespace) + attrs[ns.name(key)] = encode_attr_value(generator, ns, key, value) + end + if data.id + attrs['id'] = data.id + end + attrs + end + + def encode_attr_value(generator, ns, qname, value) + if value.is_a?(SOAPType) + ref = SOAPReference.new(value) + generator.add_reftarget(qname.name, value) + ref.refidstr + else + value.to_s + end + end + + def decode_tag_by_wsdl(ns, elename, typestr, parent, arytypestr, extraattr) + o = nil + if parent.class == SOAPBody + # root element: should branch by root attribute? + if @is_first_top_ele + # Unqualified name is allowed here. + @is_first_top_ele = false + type = @decode_typemap[elename] || + @decode_typemap.find_name(elename.name) + if type + o = SOAPStruct.new(elename) + o.definedtype = type + return o + end + end + # multi-ref element. + if typestr + typename = ns.parse(typestr) + typedef = @decode_typemap[typename] + if typedef + return decode_definedtype(elename, typename, typedef, arytypestr) + end + end + return decode_tag_by_type(ns, elename, typestr, parent, arytypestr, + extraattr) + end + + if parent.type == XSD::AnyTypeName + return decode_tag_by_type(ns, elename, typestr, parent, arytypestr, + extraattr) + end + + # parent.definedtype == nil means the parent is SOAPUnknown. SOAPUnknown + # is generated by decode_tag_by_type when its type is anyType. + parenttype = parent.definedtype || @decode_typemap[parent.type] + unless parenttype + return decode_tag_by_type(ns, elename, typestr, parent, arytypestr, + extraattr) + end + + definedtype_name = parenttype.child_type(elename) + if definedtype_name and (klass = TypeMap[definedtype_name]) + return decode_basetype(klass, elename) + elsif definedtype_name == XSD::AnyTypeName + return decode_tag_by_type(ns, elename, typestr, parent, arytypestr, + extraattr) + end + + if definedtype_name + typedef = @decode_typemap[definedtype_name] + else + typedef = parenttype.child_defined_complextype(elename) + end + decode_definedtype(elename, definedtype_name, typedef, arytypestr) + end + + def decode_definedtype(elename, typename, typedef, arytypestr) + unless typedef + raise EncodingStyleError.new("unknown type '#{typename}'") + end + if typedef.is_a?(::WSDL::XMLSchema::SimpleType) + decode_defined_simpletype(elename, typename, typedef, arytypestr) + else + decode_defined_complextype(elename, typename, typedef, arytypestr) + end + end + + def decode_basetype(klass, elename) + klass.decode(elename) + end + + def decode_defined_simpletype(elename, typename, typedef, arytypestr) + o = decode_basetype(TypeMap[typedef.base], elename) + o.definedtype = typedef + o + end + + def decode_defined_complextype(elename, typename, typedef, arytypestr) + case typedef.compoundtype + when :TYPE_STRUCT, :TYPE_MAP + o = SOAPStruct.decode(elename, typename) + o.definedtype = typedef + return o + when :TYPE_ARRAY + expected_arytype = typedef.find_arytype + if arytypestr + actual_arytype = XSD::QName.new(expected_arytype.namespace, + content_typename(expected_arytype.name) << + content_ranksize(arytypestr)) + o = SOAPArray.decode(elename, typename, actual_arytype) + else + o = SOAPArray.new(typename, 1, expected_arytype) + o.elename = elename + end + o.definedtype = typedef + return o + when :TYPE_EMPTY + o = SOAPNil.decode(elename) + o.definedtype = typedef + return o + else + raise RuntimeError.new( + "Unknown kind of complexType: #{typedef.compoundtype}") + end + nil + end + + def decode_tag_by_type(ns, elename, typestr, parent, arytypestr, extraattr) + if arytypestr + type = typestr ? ns.parse(typestr) : ValueArrayName + node = SOAPArray.decode(elename, type, ns.parse(arytypestr)) + node.extraattr.update(extraattr) + return node + end + + type = nil + if typestr + type = ns.parse(typestr) + elsif parent.is_a?(SOAPArray) + type = parent.arytype + else + # Since it's in dynamic(without any type) encoding process, + # assumes entity as its type itself. + # => type Array in SOAP-ENC. + # => type Country in foo. + type = elename + end + + if (klass = TypeMap[type]) + node = decode_basetype(klass, elename) + node.extraattr.update(extraattr) + return node + end + + # Unknown type... Struct or String + SOAPUnknown.new(self, elename, type, extraattr) + end + + def decode_textbuf(node) + case node + when XSD::XSDHexBinary, XSD::XSDBase64Binary + node.set_encoded(@textbuf) + when XSD::XSDString + if @charset + @textbuf = XSD::Charset.encoding_from_xml(@textbuf, @charset) + end + if node.definedtype + node.definedtype.check_lexical_format(@textbuf) + end + node.set(@textbuf) + when SOAPNil + # Nothing to do. + when SOAPBasetype + node.set(@textbuf) + else + # Nothing to do... + end + @textbuf = '' + end + + NilLiteralMap = { + 'true' => true, + '1' => true, + 'false' => false, + '0' => false + } + RootLiteralMap = { + '1' => 1, + '0' => 0 + } + def decode_attrs(ns, attrs) + is_nil = false + type = nil + arytype = nil + root = nil + offset = nil + position = nil + href = nil + id = nil + extraattr = {} + + attrs.each do |key, value| + qname = ns.parse(key) + case qname.namespace + when XSD::InstanceNamespace + case qname.name + when XSD::NilLiteral + is_nil = NilLiteralMap[value] or + raise EncodingStyleError.new("cannot accept attribute value: #{value} as the value of xsi:#{XSD::NilLiteral} (expected 'true', 'false', '1', or '0')") + next + when XSD::AttrType + type = value + next + end + when EncodingNamespace + case qname.name + when AttrArrayType + arytype = value + next + when AttrRoot + root = RootLiteralMap[value] or + raise EncodingStyleError.new( + "illegal root attribute value: #{value}") + next + when AttrOffset + offset = value + next + when AttrPosition + position = value + next + end + end + if key == 'href' + href = value + next + elsif key == 'id' + id = value + next + end + qname = ns.parse_local(key) + extraattr[qname] = decode_attr_value(ns, qname, value) + end + + return is_nil, type, arytype, root, offset, position, href, id, extraattr + end + + def decode_attr_value(ns, qname, value) + if /\A#/ =~ value + o = SOAPReference.decode(nil, value) + @refpool << o + o + else + value + end + end + + def decode_arypos(position) + /^\[(.+)\]$/ =~ position + $1.split(',').collect { |s| s.to_i } + end + + def decode_resolve_id + count = @refpool.length # To avoid infinite loop + while !@refpool.empty? && count > 0 + @refpool = @refpool.find_all { |ref| + o = @idpool.find { |item| + item.id == ref.refid + } + if o.is_a?(SOAPReference) + true # link of link. + elsif o + ref.__setobj__(o) + false + elsif o = ref.rootnode.external_content[ref.refid] + ref.__setobj__(o) + false + else + raise EncodingStyleError.new("unresolved reference: #{ref.refid}") + end + } + count -= 1 + end + end +end + +SOAPHandler.new + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/generator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/generator.rb new file mode 100644 index 0000000000..f179555e1d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/generator.rb @@ -0,0 +1,268 @@ +# SOAP4R - SOAP XML Instance Generator library. +# Copyright (C) 2001, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/ns' +require 'soap/soap' +require 'soap/baseData' +require 'soap/encodingstyle/handler' + + +module SOAP + + +### +## CAUTION: MT-unsafe +# +class SOAPGenerator + include SOAP + + class FormatEncodeError < Error; end + +public + + attr_accessor :charset + attr_accessor :default_encodingstyle + attr_accessor :generate_explicit_type + attr_accessor :use_numeric_character_reference + + def initialize(opt = {}) + @reftarget = nil + @handlers = {} + @charset = opt[:charset] || XSD::Charset.xml_encoding_label + @default_encodingstyle = opt[:default_encodingstyle] || EncodingNamespace + @generate_explicit_type = + opt.key?(:generate_explicit_type) ? opt[:generate_explicit_type] : true + @elementformdefault = opt[:elementformdefault] + @attributeformdefault = opt[:attributeformdefault] + @use_numeric_character_reference = opt[:use_numeric_character_reference] + @indentstr = opt[:no_indent] ? '' : ' ' + @buf = @indent = @curr = nil + end + + def generate(obj, io = nil) + @buf = io || '' + @indent = '' + + prologue + @handlers.each do |uri, handler| + handler.encode_prologue + end + + ns = XSD::NS.new + @buf << xmldecl + encode_data(ns, obj, nil) + + @handlers.each do |uri, handler| + handler.encode_epilogue + end + epilogue + + @buf + end + + def encode_data(ns, obj, parent) + if obj.is_a?(SOAPEnvelopeElement) + encode_element(ns, obj, parent) + return + end + if @reftarget && !obj.precedents.empty? + add_reftarget(obj.elename.name, obj) + ref = SOAPReference.new(obj) + ref.elename = ref.elename.dup_name(obj.elename.name) + obj.precedents.clear # Avoid cyclic delay. + obj.encodingstyle = parent.encodingstyle + # SOAPReference is encoded here. + obj = ref + end + encodingstyle = obj.encodingstyle + # Children's encodingstyle is derived from its parent. + encodingstyle ||= parent.encodingstyle if parent + obj.encodingstyle = encodingstyle + handler = find_handler(encodingstyle || @default_encodingstyle) + unless handler + raise FormatEncodeError.new("Unknown encodingStyle: #{ encodingstyle }.") + end + if !obj.elename.name + raise FormatEncodeError.new("Element name not defined: #{ obj }.") + end + handler.encode_data(self, ns, obj, parent) + handler.encode_data_end(self, ns, obj, parent) + end + + def add_reftarget(name, node) + unless @reftarget + raise FormatEncodeError.new("Reftarget is not defined.") + end + @reftarget.add(name, node) + end + + def encode_child(ns, child, parent) + indent_backup, @indent = @indent, @indent + @indentstr + encode_data(ns.clone_ns, child, parent) + @indent = indent_backup + end + + def encode_element(ns, obj, parent) + attrs = {} + if obj.is_a?(SOAPBody) + @reftarget = obj + obj.encode(self, ns, attrs) do |child| + indent_backup, @indent = @indent, @indent + @indentstr + encode_data(ns.clone_ns, child, obj) + @indent = indent_backup + end + @reftarget = nil + else + if obj.is_a?(SOAPEnvelope) + # xsi:nil="true" can appear even if dumping without explicit type. + SOAPGenerator.assign_ns(attrs, ns, + XSD::InstanceNamespace, XSINamespaceTag) + if @generate_explicit_type + SOAPGenerator.assign_ns(attrs, ns, XSD::Namespace, XSDNamespaceTag) + end + end + obj.encode(self, ns, attrs) do |child| + indent_backup, @indent = @indent, @indent + @indentstr + encode_data(ns.clone_ns, child, obj) + @indent = indent_backup + end + end + end + + def encode_name(ns, data, attrs) + if element_local?(data) + data.elename.name + else + if element_qualified?(data) + SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace, '') + else + SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace) + end + ns.name(data.elename) + end + end + + def encode_name_end(ns, data) + if element_local?(data) + data.elename.name + else + ns.name(data.elename) + end + end + + def encode_tag(elename, attrs = nil) + if !attrs or attrs.empty? + @buf << "\n#{ @indent }<#{ elename }>" + elsif attrs.size == 1 + key, value = attrs.shift + @buf << %Q[\n#{ @indent }<#{ elename } #{ key }="#{ value }">] + else + @buf << "\n#{ @indent }<#{ elename } " << + attrs.collect { |key, value| + %Q[#{ key }="#{ value }"] + }.join("\n#{ @indent }#{ @indentstr * 2 }") << + '>' + end + end + + def encode_tag_end(elename, cr = nil) + if cr + @buf << "\n#{ @indent }" + else + @buf << "" + end + end + + def encode_rawstring(str) + @buf << str + end + + EncodeMap = { + '&' => '&', + '<' => '<', + '>' => '>', + '"' => '"', + '\'' => ''', + "\r" => ' ' + } + EncodeCharRegexp = Regexp.new("[#{EncodeMap.keys.join}]") + def encode_string(str) + if @use_numeric_character_reference and !XSD::Charset.is_us_ascii(str) + str.gsub!(EncodeCharRegexp) { |c| EncodeMap[c] } + @buf << str.unpack("U*").collect { |c| + if c == 0x9 or c == 0xa or c == 0xd or (c >= 0x20 and c <= 0x7f) + c.chr + else + sprintf("&#x%x;", c) + end + }.join + else + @buf << str.gsub(EncodeCharRegexp) { |c| EncodeMap[c] } + end + end + + def element_local?(element) + element.elename.namespace.nil? + end + + def element_qualified?(element) + if element.respond_to?(:qualified) + if element.qualified.nil? + @elementformdefault + else + element.qualified + end + else + @elementformdefault + end + end + + def self.assign_ns(attrs, ns, namespace, tag = nil) + if namespace.nil? + raise FormatEncodeError.new("empty namespace") + end + unless ns.assigned?(namespace) + tag = ns.assign(namespace, tag) + if tag == '' + attr = 'xmlns' + else + attr = "xmlns:#{tag}" + end + attrs[attr] = namespace + end + end + +private + + def prologue + end + + def epilogue + end + + def find_handler(encodingstyle) + unless @handlers.key?(encodingstyle) + handler = SOAP::EncodingStyle::Handler.handler(encodingstyle).new(@charset) + handler.generate_explicit_type = @generate_explicit_type + handler.encode_prologue + @handlers[encodingstyle] = handler + end + @handlers[encodingstyle] + end + + def xmldecl + if @charset + %Q[] + else + %Q[] + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/header/handler.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/header/handler.rb new file mode 100644 index 0000000000..7da2836e24 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/header/handler.rb @@ -0,0 +1,57 @@ +# SOAP4R - SOAP Header handler item +# Copyright (C) 2003, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/element' + + +module SOAP +module Header + + +class Handler + attr_reader :elename + attr_reader :mustunderstand + attr_reader :encodingstyle + + def initialize(elename) + @elename = elename + @mustunderstand = false + @encodingstyle = nil + end + + # Should return a SOAP/OM, a SOAPHeaderItem or nil. + def on_outbound + nil + end + + # Given header is a SOAPHeaderItem or nil. + def on_inbound(header, mustunderstand = false) + # do something. + end + + def on_outbound_headeritem + item = on_outbound + if item.nil? + nil + elsif item.is_a?(::SOAP::SOAPHeaderItem) + item.elename = @elename + item + else + item.elename = @elename + ::SOAP::SOAPHeaderItem.new(item, @mustunderstand, @encodingstyle) + end + end + + def on_inbound_headeritem(header) + on_inbound(header.element, header.mustunderstand) + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/header/handlerset.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/header/handlerset.rb new file mode 100644 index 0000000000..a8eee03023 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/header/handlerset.rb @@ -0,0 +1,70 @@ +# SOAP4R - SOAP Header handler set +# Copyright (C) 2003, 2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/namedelements' + + +module SOAP +module Header + + +class HandlerSet + def initialize + @store = XSD::NamedElements.new + end + + def dup + obj = HandlerSet.new + obj.store = @store.dup + obj + end + + def add(handler) + @store << handler + end + alias << add + + def delete(handler) + @store.delete(handler) + end + + def include?(handler) + @store.include?(handler) + end + + # returns: Array of SOAPHeaderItem + def on_outbound + @store.collect { |handler| + handler.on_outbound_headeritem + }.compact + end + + # headers: SOAPHeaderItem enumerable object + def on_inbound(headers) + headers.each do |name, item| + handler = @store.find { |handler| + handler.elename == item.element.elename + } + if handler + handler.on_inbound_headeritem(item) + elsif item.mustunderstand + raise UnhandledMustUnderstandHeaderError.new(item.element.elename.to_s) + end + end + end + +protected + + def store=(store) + @store = store + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/header/simplehandler.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/header/simplehandler.rb new file mode 100644 index 0000000000..7b206f77db --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/header/simplehandler.rb @@ -0,0 +1,44 @@ +# SOAP4R - SOAP Simple header item handler +# Copyright (C) 2003-2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/header/handler' +require 'soap/baseData' + + +module SOAP +module Header + + +class SimpleHandler < SOAP::Header::Handler + def initialize(elename) + super(elename) + end + + # Should return a Hash, String or nil. + def on_simple_outbound + nil + end + + # Given header is a Hash, String or nil. + def on_simple_inbound(header, mustunderstand) + end + + def on_outbound + h = on_simple_outbound + h ? SOAPElement.from_obj(h, elename.namespace) : nil + end + + def on_inbound(header, mustunderstand) + h = header.respond_to?(:to_obj) ? header.to_obj : header.data + on_simple_inbound(h, mustunderstand) + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/httpconfigloader.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/httpconfigloader.rb new file mode 100644 index 0000000000..cd7bca8a65 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/httpconfigloader.rb @@ -0,0 +1,119 @@ +# SOAP4R - HTTP config loader. +# Copyright (C) 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/property' + + +module SOAP + + +module HTTPConfigLoader +module_function + + def set_options(client, options) + client.proxy = options["proxy"] + options.add_hook("proxy") do |key, value| + client.proxy = value + end + client.no_proxy = options["no_proxy"] + options.add_hook("no_proxy") do |key, value| + client.no_proxy = value + end + if client.respond_to?(:protocol_version=) + client.protocol_version = options["protocol_version"] + options.add_hook("protocol_version") do |key, value| + client.protocol_version = value + end + end + ssl_config = options["ssl_config"] ||= ::SOAP::Property.new + set_ssl_config(client, ssl_config) + ssl_config.add_hook(true) do |key, value| + set_ssl_config(client, ssl_config) + end + basic_auth = options["basic_auth"] ||= ::SOAP::Property.new + set_basic_auth(client, basic_auth) + basic_auth.add_hook do |key, value| + set_basic_auth(client, basic_auth) + end + options.add_hook("connect_timeout") do |key, value| + client.connect_timeout = value + end + options.add_hook("send_timeout") do |key, value| + client.send_timeout = value + end + options.add_hook("receive_timeout") do |key, value| + client.receive_timeout = value + end + end + + def set_basic_auth(client, basic_auth) + basic_auth.values.each do |url, userid, passwd| + client.set_basic_auth(url, userid, passwd) + end + end + + def set_ssl_config(client, ssl_config) + ssl_config.each do |key, value| + cfg = client.ssl_config + if cfg.nil? + raise NotImplementedError.new("SSL not supported") + end + case key + when 'client_cert' + cfg.client_cert = cert_from_file(value) + when 'client_key' + cfg.client_key = key_from_file(value) + when 'client_ca' + cfg.client_ca = value + when 'ca_path' + cfg.set_trust_ca(value) + when 'ca_file' + cfg.set_trust_ca(value) + when 'crl' + cfg.set_crl(value) + when 'verify_mode' + cfg.verify_mode = ssl_config_int(value) + when 'verify_depth' + cfg.verify_depth = ssl_config_int(value) + when 'options' + cfg.options = value + when 'ciphers' + cfg.ciphers = value + when 'verify_callback' + cfg.verify_callback = value + when 'cert_store' + cfg.cert_store = value + else + raise ArgumentError.new("unknown ssl_config property #{key}") + end + end + end + + def ssl_config_int(value) + if value.nil? or value.to_s.empty? + nil + else + begin + Integer(value) + rescue ArgumentError + ::SOAP::Property::Util.const_from_name(value.to_s) + end + end + end + + def cert_from_file(filename) + OpenSSL::X509::Certificate.new(File.open(filename) { |f| f.read }) + end + + def key_from_file(filename) + OpenSSL::PKey::RSA.new(File.open(filename) { |f| f.read }) + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping.rb new file mode 100644 index 0000000000..b83f8b484a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping.rb @@ -0,0 +1,10 @@ +# SOAP4R - Ruby type mapping utility. +# Copyright (C) 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/mapping/mapping' +require 'soap/mapping/registry' diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/factory.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/factory.rb new file mode 100644 index 0000000000..978b303b3d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/factory.rb @@ -0,0 +1,355 @@ +# SOAP4R - Mapping factory. +# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +module SOAP +module Mapping + + +class Factory + include TraverseSupport + + def initialize + # nothing to do + end + + def obj2soap(soap_class, obj, info, map) + raise NotImplementError.new + # return soap_obj + end + + def soap2obj(obj_class, node, info, map) + raise NotImplementError.new + # return convert_succeeded_or_not, obj + end + + def setiv2obj(obj, node, map) + return if node.nil? + if obj.is_a?(Array) + setiv2ary(obj, node, map) + else + setiv2struct(obj, node, map) + end + end + + def setiv2soap(node, obj, map) + if obj.class.class_variables.include?('@@schema_element') + obj.class.class_eval('@@schema_element').each do |name, info| + type, qname = info + if qname + elename = qname.name + else + elename = Mapping.name2elename(name) + end + node.add(elename, + Mapping._obj2soap(obj.instance_variable_get('@' + name), map)) + end + else + # should we sort instance_variables? + obj.instance_variables.each do |var| + name = var.sub(/^@/, '') + elename = Mapping.name2elename(name) + node.add(elename, + Mapping._obj2soap(obj.instance_variable_get(var), map)) + end + end + end + +private + + def setiv2ary(obj, node, map) + node.each do |name, value| + Array.instance_method(:<<).bind(obj).call(Mapping._soap2obj(value, map)) + end + end + + def setiv2struct(obj, node, map) + vars = {} + node.each do |name, value| + vars[Mapping.elename2name(name)] = Mapping._soap2obj(value, map) + end + Mapping.set_attributes(obj, vars) + end +end + +class StringFactory_ < Factory + def initialize(allow_original_mapping = false) + super() + @allow_original_mapping = allow_original_mapping + end + + def obj2soap(soap_class, obj, info, map) + if !@allow_original_mapping and !obj.instance_variables.empty? + return nil + end + begin + unless XSD::Charset.is_ces(obj, Thread.current[:SOAPExternalCES]) + return nil + end + encoded = XSD::Charset.encoding_conv(obj, + Thread.current[:SOAPExternalCES], XSD::Charset.encoding) + soap_obj = soap_class.new(encoded) + rescue XSD::ValueSpaceError + return nil + end + mark_marshalled_obj(obj, soap_obj) + soap_obj + end + + def soap2obj(obj_class, node, info, map) + obj = Mapping.create_empty_object(obj_class) + decoded = XSD::Charset.encoding_conv(node.data, XSD::Charset.encoding, + Thread.current[:SOAPExternalCES]) + obj.replace(decoded) + mark_unmarshalled_obj(node, obj) + return true, obj + end +end + +class BasetypeFactory_ < Factory + def initialize(allow_original_mapping = false) + super() + @allow_original_mapping = allow_original_mapping + end + + def obj2soap(soap_class, obj, info, map) + if !@allow_original_mapping and !obj.instance_variables.empty? + return nil + end + soap_obj = nil + begin + soap_obj = soap_class.new(obj) + rescue XSD::ValueSpaceError + return nil + end + if @allow_original_mapping + # Basetype except String should not be multiref-ed in SOAP/1.1. + mark_marshalled_obj(obj, soap_obj) + end + soap_obj + end + + def soap2obj(obj_class, node, info, map) + obj = node.data + mark_unmarshalled_obj(node, obj) + return true, obj + end +end + +class DateTimeFactory_ < Factory + def initialize(allow_original_mapping = false) + super() + @allow_original_mapping = allow_original_mapping + end + + def obj2soap(soap_class, obj, info, map) + if !@allow_original_mapping and + Time === obj and !obj.instance_variables.empty? + return nil + end + soap_obj = nil + begin + soap_obj = soap_class.new(obj) + rescue XSD::ValueSpaceError + return nil + end + mark_marshalled_obj(obj, soap_obj) + soap_obj + end + + def soap2obj(obj_class, node, info, map) + if node.respond_to?(:to_obj) + obj = node.to_obj(obj_class) + return false if obj.nil? + mark_unmarshalled_obj(node, obj) + return true, obj + else + return false + end + end +end + +class Base64Factory_ < Factory + def obj2soap(soap_class, obj, info, map) + return nil unless obj.instance_variables.empty? + soap_obj = soap_class.new(obj) + mark_marshalled_obj(obj, soap_obj) if soap_obj + soap_obj + end + + def soap2obj(obj_class, node, info, map) + obj = node.string + mark_unmarshalled_obj(node, obj) + return true, obj + end +end + +class URIFactory_ < Factory + def obj2soap(soap_class, obj, info, map) + soap_obj = soap_class.new(obj) + mark_marshalled_obj(obj, soap_obj) if soap_obj + soap_obj + end + + def soap2obj(obj_class, node, info, map) + obj = node.data + mark_unmarshalled_obj(node, obj) + return true, obj + end +end + +class ArrayFactory_ < Factory + def initialize(allow_original_mapping = false) + super() + @allow_original_mapping = allow_original_mapping + end + + # [[1], [2]] is converted to Array of Array, not 2-D Array. + # To create M-D Array, you must call Mapping.ary2md. + def obj2soap(soap_class, obj, info, map) + if !@allow_original_mapping and !obj.instance_variables.empty? + return nil + end + arytype = Mapping.obj2element(obj) + if arytype.name + arytype.namespace ||= RubyTypeNamespace + else + arytype = XSD::AnyTypeName + end + soap_obj = SOAPArray.new(ValueArrayName, 1, arytype) + mark_marshalled_obj(obj, soap_obj) + obj.each do |item| + soap_obj.add(Mapping._obj2soap(item, map)) + end + soap_obj + end + + def soap2obj(obj_class, node, info, map) + obj = Mapping.create_empty_object(obj_class) + mark_unmarshalled_obj(node, obj) + node.soap2array(obj) do |elem| + elem ? Mapping._soap2obj(elem, map) : nil + end + return true, obj + end +end + +class TypedArrayFactory_ < Factory + def initialize(allow_original_mapping = false) + super() + @allow_original_mapping = allow_original_mapping + end + + def obj2soap(soap_class, obj, info, map) + if !@allow_original_mapping and !obj.instance_variables.empty? + return nil + end + arytype = info[:type] || info[0] + soap_obj = SOAPArray.new(ValueArrayName, 1, arytype) + mark_marshalled_obj(obj, soap_obj) + obj.each do |var| + soap_obj.add(Mapping._obj2soap(var, map)) + end + soap_obj + end + + def soap2obj(obj_class, node, info, map) + if node.rank > 1 + return false + end + arytype = info[:type] || info[0] + unless node.arytype == arytype + return false + end + obj = Mapping.create_empty_object(obj_class) + mark_unmarshalled_obj(node, obj) + node.soap2array(obj) do |elem| + elem ? Mapping._soap2obj(elem, map) : nil + end + return true, obj + end +end + +class TypedStructFactory_ < Factory + def obj2soap(soap_class, obj, info, map) + type = info[:type] || info[0] + soap_obj = soap_class.new(type) + mark_marshalled_obj(obj, soap_obj) + if obj.class <= SOAP::Marshallable + setiv2soap(soap_obj, obj, map) + else + setiv2soap(soap_obj, obj, map) + end + soap_obj + end + + def soap2obj(obj_class, node, info, map) + type = info[:type] || info[0] + unless node.type == type + return false + end + obj = Mapping.create_empty_object(obj_class) + mark_unmarshalled_obj(node, obj) + setiv2obj(obj, node, map) + return true, obj + end +end + +MapQName = XSD::QName.new(ApacheSOAPTypeNamespace, 'Map') +class HashFactory_ < Factory + def initialize(allow_original_mapping = false) + super() + @allow_original_mapping = allow_original_mapping + end + + def obj2soap(soap_class, obj, info, map) + if !@allow_original_mapping and !obj.instance_variables.empty? + return nil + end + if !obj.default.nil? or + (obj.respond_to?(:default_proc) and obj.default_proc) + return nil + end + soap_obj = SOAPStruct.new(MapQName) + mark_marshalled_obj(obj, soap_obj) + obj.each do |key, value| + elem = SOAPStruct.new + elem.add("key", Mapping._obj2soap(key, map)) + elem.add("value", Mapping._obj2soap(value, map)) + # ApacheAxis allows only 'item' here. + soap_obj.add("item", elem) + end + soap_obj + end + + def soap2obj(obj_class, node, info, map) + unless node.type == MapQName + return false + end + if node.class == SOAPStruct and node.key?('default') + return false + end + obj = Mapping.create_empty_object(obj_class) + mark_unmarshalled_obj(node, obj) + if node.class == SOAPStruct + node.each do |key, value| + obj[Mapping._soap2obj(value['key'], map)] = + Mapping._soap2obj(value['value'], map) + end + else + node.each do |value| + obj[Mapping._soap2obj(value['key'], map)] = + Mapping._soap2obj(value['value'], map) + end + end + return true, obj + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/mapping.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/mapping.rb new file mode 100644 index 0000000000..65d6bb4d5b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/mapping.rb @@ -0,0 +1,381 @@ +# SOAP4R - Ruby type mapping utility. +# Copyright (C) 2000, 2001, 2003-2005 NAKAMURA Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/codegen/gensupport' + + +module SOAP + + +module Mapping + RubyTypeNamespace = 'http://www.ruby-lang.org/xmlns/ruby/type/1.6' + RubyTypeInstanceNamespace = + 'http://www.ruby-lang.org/xmlns/ruby/type-instance' + RubyCustomTypeNamespace = 'http://www.ruby-lang.org/xmlns/ruby/type/custom' + ApacheSOAPTypeNamespace = 'http://xml.apache.org/xml-soap' + + + # TraverseSupport breaks following thread variables. + # Thread.current[:SOAPMarshalDataKey] + module TraverseSupport + def mark_marshalled_obj(obj, soap_obj) + raise if obj.nil? + Thread.current[:SOAPMarshalDataKey][obj.__id__] = soap_obj + end + + def mark_unmarshalled_obj(node, obj) + return if obj.nil? + # node.id is not Object#id but SOAPReference#id + Thread.current[:SOAPMarshalDataKey][node.id] = obj + end + end + + + EMPTY_OPT = {} + def self.obj2soap(obj, registry = nil, type = nil, opt = EMPTY_OPT) + registry ||= Mapping::DefaultRegistry + soap_obj = nil + protect_threadvars(:SOAPMarshalDataKey, :SOAPExternalCES, :SOAPMarshalNoReference) do + Thread.current[:SOAPMarshalDataKey] = {} + Thread.current[:SOAPExternalCES] = opt[:external_ces] || $KCODE + Thread.current[:SOAPMarshalNoReference] = opt[:no_reference] + soap_obj = _obj2soap(obj, registry, type) + end + soap_obj + end + + def self.soap2obj(node, registry = nil, klass = nil, opt = EMPTY_OPT) + registry ||= Mapping::DefaultRegistry + obj = nil + protect_threadvars(:SOAPMarshalDataKey, :SOAPExternalCES, :SOAPMarshalNoReference) do + Thread.current[:SOAPMarshalDataKey] = {} + Thread.current[:SOAPExternalCES] = opt[:external_ces] || $KCODE + Thread.current[:SOAPMarshalNoReference] = opt[:no_reference] + obj = _soap2obj(node, registry, klass) + end + obj + end + + def self.ary2soap(ary, type_ns = XSD::Namespace, typename = XSD::AnyTypeLiteral, registry = nil, opt = EMPTY_OPT) + registry ||= Mapping::DefaultRegistry + type = XSD::QName.new(type_ns, typename) + soap_ary = SOAPArray.new(ValueArrayName, 1, type) + protect_threadvars(:SOAPMarshalDataKey, :SOAPExternalCES, :SOAPMarshalNoReference) do + Thread.current[:SOAPMarshalDataKey] = {} + Thread.current[:SOAPExternalCES] = opt[:external_ces] || $KCODE + Thread.current[:SOAPMarshalNoReference] = opt[:no_reference] + ary.each do |ele| + soap_ary.add(_obj2soap(ele, registry, type)) + end + end + soap_ary + end + + def self.ary2md(ary, rank, type_ns = XSD::Namespace, typename = XSD::AnyTypeLiteral, registry = nil, opt = EMPTY_OPT) + registry ||= Mapping::DefaultRegistry + type = XSD::QName.new(type_ns, typename) + md_ary = SOAPArray.new(ValueArrayName, rank, type) + protect_threadvars(:SOAPMarshalDataKey, :SOAPExternalCES, :SOAPMarshalNoReference) do + Thread.current[:SOAPMarshalDataKey] = {} + Thread.current[:SOAPExternalCES] = opt[:external_ces] || $KCODE + Thread.current[:SOAPMarshalNoReference] = opt[:no_reference] + add_md_ary(md_ary, ary, [], registry) + end + md_ary + end + + def self.fault2exception(fault, registry = nil) + registry ||= Mapping::DefaultRegistry + detail = if fault.detail + soap2obj(fault.detail, registry) || "" + else + "" + end + if detail.is_a?(Mapping::SOAPException) + begin + e = detail.to_e + remote_backtrace = e.backtrace + e.set_backtrace(nil) + raise e # ruby sets current caller as local backtrace of e => e2. + rescue Exception => e + e.set_backtrace(remote_backtrace + e.backtrace[1..-1]) + raise + end + else + fault.detail = detail + fault.set_backtrace( + if detail.is_a?(Array) + detail + else + [detail.to_s] + end + ) + raise + end + end + + def self._obj2soap(obj, registry, type = nil) + if referent = Thread.current[:SOAPMarshalDataKey][obj.__id__] and + !Thread.current[:SOAPMarshalNoReference] + SOAPReference.new(referent) + elsif registry + registry.obj2soap(obj, type) + else + raise MappingError.new("no mapping registry given") + end + end + + def self._soap2obj(node, registry, klass = nil) + if node.nil? + return nil + elsif node.is_a?(SOAPReference) + target = node.__getobj__ + # target.id is not Object#id but SOAPReference#id + if referent = Thread.current[:SOAPMarshalDataKey][target.id] and + !Thread.current[:SOAPMarshalNoReference] + return referent + else + return _soap2obj(target, registry, klass) + end + end + return registry.soap2obj(node, klass) + end + + if Object.respond_to?(:allocate) + # ruby/1.7 or later. + def self.create_empty_object(klass) + klass.allocate + end + else + MARSHAL_TAG = { + String => ['"', 1], + Regexp => ['/', 2], + Array => ['[', 1], + Hash => ['{', 1] + } + def self.create_empty_object(klass) + if klass <= Struct + name = klass.name + return ::Marshal.load(sprintf("\004\006S:%c%s\000", name.length + 5, name)) + end + if MARSHAL_TAG.has_key?(klass) + tag, terminate = MARSHAL_TAG[klass] + return ::Marshal.load(sprintf("\004\006%s%s", tag, "\000" * terminate)) + end + MARSHAL_TAG.each do |k, v| + if klass < k + name = klass.name + tag, terminate = v + return ::Marshal.load(sprintf("\004\006C:%c%s%s%s", name.length + 5, name, tag, "\000" * terminate)) + end + end + name = klass.name + ::Marshal.load(sprintf("\004\006o:%c%s\000", name.length + 5, name)) + end + end + + # Allow only (Letter | '_') (Letter | Digit | '-' | '_')* here. + # Caution: '.' is not allowed here. + # To follow XML spec., it should be NCName. + # (denied chars) => .[0-F][0-F] + # ex. a.b => a.2eb + # + def self.name2elename(name) + name.gsub(/([^a-zA-Z0-9:_\-]+)/n) { + '.' << $1.unpack('H2' * $1.size).join('.') + }.gsub(/::/n, '..') + end + + def self.elename2name(name) + name.gsub(/\.\./n, '::').gsub(/((?:\.[0-9a-fA-F]{2})+)/n) { + [$1.delete('.')].pack('H*') + } + end + + def self.const_from_name(name, lenient = false) + const = ::Object + name.sub(/\A::/, '').split('::').each do |const_str| + if XSD::CodeGen::GenSupport.safeconstname?(const_str) + if const.const_defined?(const_str) + const = const.const_get(const_str) + next + end + elsif lenient + const_str = XSD::CodeGen::GenSupport.safeconstname(const_str) + if const.const_defined?(const_str) + const = const.const_get(const_str) + next + end + end + return nil + end + const + end + + def self.class_from_name(name, lenient = false) + const = const_from_name(name, lenient) + if const.is_a?(::Class) + const + else + nil + end + end + + def self.module_from_name(name, lenient = false) + const = const_from_name(name, lenient) + if const.is_a?(::Module) + const + else + nil + end + end + + def self.class2qname(klass) + name = schema_type_definition(klass) + namespace = schema_ns_definition(klass) + XSD::QName.new(namespace, name) + end + + def self.class2element(klass) + type = Mapping.class2qname(klass) + type.name ||= Mapping.name2elename(klass.name) + type.namespace ||= RubyCustomTypeNamespace + type + end + + def self.obj2element(obj) + name = namespace = nil + ivars = obj.instance_variables + if ivars.include?('@schema_type') + name = obj.instance_variable_get('@schema_type') + end + if ivars.include?('@schema_ns') + namespace = obj.instance_variable_get('@schema_ns') + end + if !name or !namespace + class2qname(obj.class) + else + XSD::QName.new(namespace, name) + end + end + + def self.define_singleton_method(obj, name, &block) + sclass = (class << obj; self; end) + sclass.class_eval { + define_method(name, &block) + } + end + + def self.get_attribute(obj, attr_name) + if obj.is_a?(::Hash) + obj[attr_name] || obj[attr_name.intern] + else + name = XSD::CodeGen::GenSupport.safevarname(attr_name) + if obj.instance_variables.include?('@' + name) + obj.instance_variable_get('@' + name) + elsif ((obj.is_a?(::Struct) or obj.is_a?(Marshallable)) and + obj.respond_to?(name)) + obj.__send__(name) + end + end + end + + def self.set_attributes(obj, values) + if obj.is_a?(::SOAP::Mapping::Object) + values.each do |attr_name, value| + obj.__add_xmlele_value(attr_name, value) + end + else + values.each do |attr_name, value| + name = XSD::CodeGen::GenSupport.safevarname(attr_name) + setter = name + "=" + if obj.respond_to?(setter) + obj.__send__(setter, value) + else + obj.instance_variable_set('@' + name, value) + begin + define_attr_accessor(obj, name, + proc { instance_variable_get('@' + name) }, + proc { |value| instance_variable_set('@' + name, value) }) + rescue TypeError + # singleton class may not exist (e.g. Float) + end + end + end + end + end + + def self.define_attr_accessor(obj, name, getterproc, setterproc = nil) + define_singleton_method(obj, name, &getterproc) + define_singleton_method(obj, name + '=', &setterproc) if setterproc + end + + def self.schema_type_definition(klass) + class_schema_variable(:schema_type, klass) + end + + def self.schema_ns_definition(klass) + class_schema_variable(:schema_ns, klass) + end + + def self.schema_element_definition(klass) + schema_element = class_schema_variable(:schema_element, klass) or return nil + schema_ns = schema_ns_definition(klass) + elements = [] + as_array = [] + schema_element.each do |varname, definition| + class_name, name = definition + if /\[\]$/ =~ class_name + class_name = class_name.sub(/\[\]$/, '') + as_array << (name ? name.name : varname) + end + elements << [name || XSD::QName.new(schema_ns, varname), class_name] + end + [elements, as_array] + end + + def self.schema_attribute_definition(klass) + class_schema_variable(:schema_attribute, klass) + end + + class << Mapping + private + + def class_schema_variable(sym, klass) + var = "@@#{sym}" + klass.class_variables.include?(var) ? klass.class_eval(var) : nil + end + + def protect_threadvars(*symbols) + backup = {} + begin + symbols.each do |sym| + backup[sym] = Thread.current[sym] + end + yield + ensure + symbols.each do |sym| + Thread.current[sym] = backup[sym] + end + end + end + + def add_md_ary(md_ary, ary, indices, registry) + for idx in 0..(ary.size - 1) + if ary[idx].is_a?(Array) + add_md_ary(md_ary, ary[idx], indices + [idx], registry) + else + md_ary[*(indices + [idx])] = _obj2soap(ary[idx], registry) + end + end + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/registry.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/registry.rb new file mode 100644 index 0000000000..823e80666d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/registry.rb @@ -0,0 +1,541 @@ +# SOAP4R - Mapping registry. +# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/baseData' +require 'soap/mapping/mapping' +require 'soap/mapping/typeMap' +require 'soap/mapping/factory' +require 'soap/mapping/rubytypeFactory' + + +module SOAP + + +module Marshallable + # @@type_ns = Mapping::RubyCustomTypeNamespace +end + + +module Mapping + + +module MappedException; end + + +RubyTypeName = XSD::QName.new(RubyTypeInstanceNamespace, 'rubyType') +RubyExtendName = XSD::QName.new(RubyTypeInstanceNamespace, 'extends') +RubyIVarName = XSD::QName.new(RubyTypeInstanceNamespace, 'ivars') + + +# Inner class to pass an exception. +class SOAPException; include Marshallable + attr_reader :excn_type_name, :cause + def initialize(e) + @excn_type_name = Mapping.name2elename(e.class.to_s) + @cause = e + end + + def to_e + if @cause.is_a?(::Exception) + @cause.extend(::SOAP::Mapping::MappedException) + return @cause + elsif @cause.respond_to?(:message) and @cause.respond_to?(:backtrace) + e = RuntimeError.new(@cause.message) + e.set_backtrace(@cause.backtrace) + return e + end + klass = Mapping.class_from_name(Mapping.elename2name(@excn_type_name.to_s)) + if klass.nil? or not klass <= ::Exception + return RuntimeError.new(@cause.inspect) + end + obj = klass.new(@cause.message) + obj.extend(::SOAP::Mapping::MappedException) + obj + end +end + + +# For anyType object: SOAP::Mapping::Object not ::Object +class Object; include Marshallable + def initialize + @__xmlele_type = {} + @__xmlele = [] + @__xmlattr = {} + end + + def inspect + sprintf("#<%s:0x%x%s>", self.class.name, __id__, + @__xmlele.collect { |name, value| " #{name}=#{value.inspect}" }.join) + end + + def __xmlattr + @__xmlattr + end + + def __xmlele + @__xmlele + end + + def [](qname) + unless qname.is_a?(XSD::QName) + qname = XSD::QName.new(nil, qname) + end + @__xmlele.each do |k, v| + return v if k == qname + end + # fallback + @__xmlele.each do |k, v| + return v if k.name == qname.name + end + nil + end + + def []=(qname, value) + unless qname.is_a?(XSD::QName) + qname = XSD::QName.new(nil, qname) + end + found = false + @__xmlele.each do |pair| + if pair[0] == qname + found = true + pair[1] = value + end + end + unless found + __define_attr_accessor(qname) + @__xmlele << [qname, value] + end + @__xmlele_type[qname] = :single + end + + def __add_xmlele_value(qname, value) + found = false + @__xmlele.map! do |k, v| + if k == qname + found = true + [k, __set_xmlele_value(k, v, value)] + else + [k, v] + end + end + unless found + __define_attr_accessor(qname) + @__xmlele << [qname, value] + @__xmlele_type[qname] = :single + end + value + end + +private + + if RUBY_VERSION > "1.7.0" + def __define_attr_accessor(qname) + name = XSD::CodeGen::GenSupport.safemethodname(qname.name) + Mapping.define_attr_accessor(self, name, + proc { self[qname] }, + proc { |value| self[qname] = value }) + end + else + def __define_attr_accessor(qname) + name = XSD::CodeGen::GenSupport.safemethodname(qname.name) + instance_eval <<-EOS + def #{name} + self[#{qname.dump}] + end + + def #{name}=(value) + self[#{qname.dump}] = value + end + EOS + end + end + + def __set_xmlele_value(key, org, value) + case @__xmlele_type[key] + when :multi + org << value + org + when :single + @__xmlele_type[key] = :multi + [org, value] + else + raise RuntimeError.new("unknown type") + end + end +end + + +class MappingError < Error; end + + +class Registry + class Map + def initialize(registry) + @obj2soap = {} + @soap2obj = {} + @registry = registry + end + + def obj2soap(obj) + klass = obj.class + if map = @obj2soap[klass] + map.each do |soap_class, factory, info| + ret = factory.obj2soap(soap_class, obj, info, @registry) + return ret if ret + end + end + ancestors = klass.ancestors + ancestors.delete(klass) + ancestors.delete(::Object) + ancestors.delete(::Kernel) + ancestors.each do |klass| + if map = @obj2soap[klass] + map.each do |soap_class, factory, info| + if info[:derived_class] + ret = factory.obj2soap(soap_class, obj, info, @registry) + return ret if ret + end + end + end + end + nil + end + + def soap2obj(node, klass = nil) + if map = @soap2obj[node.class] + map.each do |obj_class, factory, info| + next if klass and obj_class != klass + conv, obj = factory.soap2obj(obj_class, node, info, @registry) + return true, obj if conv + end + end + return false, nil + end + + # Give priority to former entry. + def init(init_map = []) + clear + init_map.reverse_each do |obj_class, soap_class, factory, info| + add(obj_class, soap_class, factory, info) + end + end + + # Give priority to latter entry. + def add(obj_class, soap_class, factory, info) + info ||= {} + (@obj2soap[obj_class] ||= []).unshift([soap_class, factory, info]) + (@soap2obj[soap_class] ||= []).unshift([obj_class, factory, info]) + end + + def clear + @obj2soap.clear + @soap2obj.clear + end + + def find_mapped_soap_class(target_obj_class) + map = @obj2soap[target_obj_class] + map.empty? ? nil : map[0][1] + end + + def find_mapped_obj_class(target_soap_class) + map = @soap2obj[target_soap_class] + map.empty? ? nil : map[0][0] + end + end + + StringFactory = StringFactory_.new + BasetypeFactory = BasetypeFactory_.new + DateTimeFactory = DateTimeFactory_.new + ArrayFactory = ArrayFactory_.new + Base64Factory = Base64Factory_.new + URIFactory = URIFactory_.new + TypedArrayFactory = TypedArrayFactory_.new + TypedStructFactory = TypedStructFactory_.new + + HashFactory = HashFactory_.new + + SOAPBaseMap = [ + [::NilClass, ::SOAP::SOAPNil, BasetypeFactory], + [::TrueClass, ::SOAP::SOAPBoolean, BasetypeFactory], + [::FalseClass, ::SOAP::SOAPBoolean, BasetypeFactory], + [::String, ::SOAP::SOAPString, StringFactory], + [::DateTime, ::SOAP::SOAPDateTime, DateTimeFactory], + [::Date, ::SOAP::SOAPDate, DateTimeFactory], + [::Time, ::SOAP::SOAPDateTime, DateTimeFactory], + [::Time, ::SOAP::SOAPTime, DateTimeFactory], + [::Float, ::SOAP::SOAPDouble, BasetypeFactory, + {:derived_class => true}], + [::Float, ::SOAP::SOAPFloat, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPInt, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPLong, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPShort, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPByte, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNonPositiveInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNegativeInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNonNegativeInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPPositiveInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedLong, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedInt, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedShort, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedByte, BasetypeFactory, + {:derived_class => true}], + [::URI::Generic, ::SOAP::SOAPAnyURI, URIFactory, + {:derived_class => true}], + [::String, ::SOAP::SOAPBase64, Base64Factory], + [::String, ::SOAP::SOAPHexBinary, Base64Factory], + [::String, ::SOAP::SOAPDecimal, BasetypeFactory], + [::String, ::SOAP::SOAPDuration, BasetypeFactory], + [::String, ::SOAP::SOAPGYearMonth, BasetypeFactory], + [::String, ::SOAP::SOAPGYear, BasetypeFactory], + [::String, ::SOAP::SOAPGMonthDay, BasetypeFactory], + [::String, ::SOAP::SOAPGDay, BasetypeFactory], + [::String, ::SOAP::SOAPGMonth, BasetypeFactory], + [::String, ::SOAP::SOAPQName, BasetypeFactory], + + [::Hash, ::SOAP::SOAPArray, HashFactory], + [::Hash, ::SOAP::SOAPStruct, HashFactory], + + [::Array, ::SOAP::SOAPArray, ArrayFactory, + {:derived_class => true}], + + [::SOAP::Mapping::SOAPException, + ::SOAP::SOAPStruct, TypedStructFactory, + {:type => XSD::QName.new(RubyCustomTypeNamespace, "SOAPException")}], + ] + + RubyOriginalMap = [ + [::NilClass, ::SOAP::SOAPNil, BasetypeFactory], + [::TrueClass, ::SOAP::SOAPBoolean, BasetypeFactory], + [::FalseClass, ::SOAP::SOAPBoolean, BasetypeFactory], + [::String, ::SOAP::SOAPString, StringFactory], + [::DateTime, ::SOAP::SOAPDateTime, DateTimeFactory], + [::Date, ::SOAP::SOAPDate, DateTimeFactory], + [::Time, ::SOAP::SOAPDateTime, DateTimeFactory], + [::Time, ::SOAP::SOAPTime, DateTimeFactory], + [::Float, ::SOAP::SOAPDouble, BasetypeFactory, + {:derived_class => true}], + [::Float, ::SOAP::SOAPFloat, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPInt, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPLong, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPShort, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPByte, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNonPositiveInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNegativeInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNonNegativeInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPPositiveInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedLong, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedInt, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedShort, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedByte, BasetypeFactory, + {:derived_class => true}], + [::URI::Generic, ::SOAP::SOAPAnyURI, URIFactory, + {:derived_class => true}], + [::String, ::SOAP::SOAPBase64, Base64Factory], + [::String, ::SOAP::SOAPHexBinary, Base64Factory], + [::String, ::SOAP::SOAPDecimal, BasetypeFactory], + [::String, ::SOAP::SOAPDuration, BasetypeFactory], + [::String, ::SOAP::SOAPGYearMonth, BasetypeFactory], + [::String, ::SOAP::SOAPGYear, BasetypeFactory], + [::String, ::SOAP::SOAPGMonthDay, BasetypeFactory], + [::String, ::SOAP::SOAPGDay, BasetypeFactory], + [::String, ::SOAP::SOAPGMonth, BasetypeFactory], + [::String, ::SOAP::SOAPQName, BasetypeFactory], + + [::Hash, ::SOAP::SOAPArray, HashFactory], + [::Hash, ::SOAP::SOAPStruct, HashFactory], + + # Does not allow Array's subclass here. + [::Array, ::SOAP::SOAPArray, ArrayFactory], + + [::SOAP::Mapping::SOAPException, + ::SOAP::SOAPStruct, TypedStructFactory, + {:type => XSD::QName.new(RubyCustomTypeNamespace, "SOAPException")}], + ] + + attr_accessor :default_factory + attr_accessor :excn_handler_obj2soap + attr_accessor :excn_handler_soap2obj + + def initialize(config = {}) + @config = config + @map = Map.new(self) + if @config[:allow_original_mapping] + @allow_original_mapping = true + @map.init(RubyOriginalMap) + else + @allow_original_mapping = false + @map.init(SOAPBaseMap) + end + @allow_untyped_struct = @config.key?(:allow_untyped_struct) ? + @config[:allow_untyped_struct] : true + @rubytype_factory = RubytypeFactory.new( + :allow_untyped_struct => @allow_untyped_struct, + :allow_original_mapping => @allow_original_mapping + ) + @default_factory = @rubytype_factory + @excn_handler_obj2soap = nil + @excn_handler_soap2obj = nil + end + + def add(obj_class, soap_class, factory, info = nil) + @map.add(obj_class, soap_class, factory, info) + end + alias set add + + # general Registry ignores type_qname + def obj2soap(obj, type_qname = nil) + soap = _obj2soap(obj) + if @allow_original_mapping + addextend2soap(soap, obj) + end + soap + end + + def soap2obj(node, klass = nil) + obj = _soap2obj(node, klass) + if @allow_original_mapping + addextend2obj(obj, node.extraattr[RubyExtendName]) + addiv2obj(obj, node.extraattr[RubyIVarName]) + end + obj + end + + def find_mapped_soap_class(obj_class) + @map.find_mapped_soap_class(obj_class) + end + + def find_mapped_obj_class(soap_class) + @map.find_mapped_obj_class(soap_class) + end + +private + + def _obj2soap(obj) + ret = nil + if obj.is_a?(SOAPStruct) or obj.is_a?(SOAPArray) + obj.replace do |ele| + Mapping._obj2soap(ele, self) + end + return obj + elsif obj.is_a?(SOAPBasetype) + return obj + end + begin + ret = @map.obj2soap(obj) || + @default_factory.obj2soap(nil, obj, nil, self) + return ret if ret + rescue MappingError + end + if @excn_handler_obj2soap + ret = @excn_handler_obj2soap.call(obj) { |yield_obj| + Mapping._obj2soap(yield_obj, self) + } + return ret if ret + end + raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.") + end + + # Might return nil as a mapping result. + def _soap2obj(node, klass = nil) + if node.extraattr.key?(RubyTypeName) + conv, obj = @rubytype_factory.soap2obj(nil, node, nil, self) + return obj if conv + else + conv, obj = @map.soap2obj(node, klass) + return obj if conv + conv, obj = @default_factory.soap2obj(nil, node, nil, self) + return obj if conv + end + if @excn_handler_soap2obj + begin + return @excn_handler_soap2obj.call(node) { |yield_node| + Mapping._soap2obj(yield_node, self) + } + rescue Exception + end + end + raise MappingError.new("Cannot map #{ node.type.name } to Ruby object.") + end + + def addiv2obj(obj, attr) + return unless attr + vars = {} + attr.__getobj__.each do |name, value| + vars[name] = Mapping._soap2obj(value, self) + end + Mapping.set_attributes(obj, vars) + end + + if RUBY_VERSION >= '1.8.0' + def addextend2obj(obj, attr) + return unless attr + attr.split(/ /).reverse_each do |mstr| + obj.extend(Mapping.module_from_name(mstr)) + end + end + else + # (class < false; self; end).ancestors includes "TrueClass" under 1.6... + def addextend2obj(obj, attr) + return unless attr + attr.split(/ /).reverse_each do |mstr| + m = Mapping.module_from_name(mstr) + obj.extend(m) + end + end + end + + def addextend2soap(node, obj) + return if obj.is_a?(Symbol) or obj.is_a?(Fixnum) + list = (class << obj; self; end).ancestors - obj.class.ancestors + unless list.empty? + node.extraattr[RubyExtendName] = list.collect { |c| + if c.name.empty? + raise TypeError.new("singleton can't be dumped #{ obj }") + end + c.name + }.join(" ") + end + end + +end + + +DefaultRegistry = Registry.new +RubyOriginalRegistry = Registry.new(:allow_original_mapping => true) + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/rubytypeFactory.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/rubytypeFactory.rb new file mode 100644 index 0000000000..61c21d8b20 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/rubytypeFactory.rb @@ -0,0 +1,475 @@ +# SOAP4R - Ruby type mapping factory. +# Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +module SOAP +module Mapping + + +class RubytypeFactory < Factory + TYPE_STRING = XSD::QName.new(RubyTypeNamespace, 'String') + TYPE_TIME = XSD::QName.new(RubyTypeNamespace, 'Time') + TYPE_ARRAY = XSD::QName.new(RubyTypeNamespace, 'Array') + TYPE_REGEXP = XSD::QName.new(RubyTypeNamespace, 'Regexp') + TYPE_RANGE = XSD::QName.new(RubyTypeNamespace, 'Range') + TYPE_CLASS = XSD::QName.new(RubyTypeNamespace, 'Class') + TYPE_MODULE = XSD::QName.new(RubyTypeNamespace, 'Module') + TYPE_SYMBOL = XSD::QName.new(RubyTypeNamespace, 'Symbol') + TYPE_STRUCT = XSD::QName.new(RubyTypeNamespace, 'Struct') + TYPE_HASH = XSD::QName.new(RubyTypeNamespace, 'Map') + + def initialize(config = {}) + @config = config + @allow_untyped_struct = @config.key?(:allow_untyped_struct) ? + @config[:allow_untyped_struct] : true + @allow_original_mapping = @config.key?(:allow_original_mapping) ? + @config[:allow_original_mapping] : false + @string_factory = StringFactory_.new(true) + @basetype_factory = BasetypeFactory_.new(true) + @datetime_factory = DateTimeFactory_.new(true) + @array_factory = ArrayFactory_.new(true) + @hash_factory = HashFactory_.new(true) + end + + def obj2soap(soap_class, obj, info, map) + param = nil + case obj + when ::String + unless @allow_original_mapping + return nil + end + param = @string_factory.obj2soap(SOAPString, obj, info, map) + if obj.class != String + param.extraattr[RubyTypeName] = obj.class.name + end + addiv2soapattr(param, obj, map) + when ::Time + unless @allow_original_mapping + return nil + end + param = @datetime_factory.obj2soap(SOAPDateTime, obj, info, map) + if obj.class != Time + param.extraattr[RubyTypeName] = obj.class.name + end + addiv2soapattr(param, obj, map) + when ::Array + unless @allow_original_mapping + return nil + end + param = @array_factory.obj2soap(nil, obj, info, map) + if obj.class != Array + param.extraattr[RubyTypeName] = obj.class.name + end + addiv2soapattr(param, obj, map) + when ::NilClass + unless @allow_original_mapping + return nil + end + param = @basetype_factory.obj2soap(SOAPNil, obj, info, map) + addiv2soapattr(param, obj, map) + when ::FalseClass, ::TrueClass + unless @allow_original_mapping + return nil + end + param = @basetype_factory.obj2soap(SOAPBoolean, obj, info, map) + addiv2soapattr(param, obj, map) + when ::Integer + unless @allow_original_mapping + return nil + end + param = @basetype_factory.obj2soap(SOAPInt, obj, info, map) + param ||= @basetype_factory.obj2soap(SOAPInteger, obj, info, map) + param ||= @basetype_factory.obj2soap(SOAPDecimal, obj, info, map) + addiv2soapattr(param, obj, map) + when ::Float + unless @allow_original_mapping + return nil + end + param = @basetype_factory.obj2soap(SOAPDouble, obj, info, map) + if obj.class != Float + param.extraattr[RubyTypeName] = obj.class.name + end + addiv2soapattr(param, obj, map) + when ::Hash + unless @allow_original_mapping + return nil + end + if obj.respond_to?(:default_proc) && obj.default_proc + raise TypeError.new("cannot dump hash with default proc") + end + param = SOAPStruct.new(TYPE_HASH) + mark_marshalled_obj(obj, param) + if obj.class != Hash + param.extraattr[RubyTypeName] = obj.class.name + end + obj.each do |key, value| + elem = SOAPStruct.new # Undefined type. + elem.add("key", Mapping._obj2soap(key, map)) + elem.add("value", Mapping._obj2soap(value, map)) + param.add("item", elem) + end + param.add('default', Mapping._obj2soap(obj.default, map)) + addiv2soapattr(param, obj, map) + when ::Regexp + unless @allow_original_mapping + return nil + end + param = SOAPStruct.new(TYPE_REGEXP) + mark_marshalled_obj(obj, param) + if obj.class != Regexp + param.extraattr[RubyTypeName] = obj.class.name + end + param.add('source', SOAPBase64.new(obj.source)) + if obj.respond_to?('options') + # Regexp#options is from Ruby/1.7 + options = obj.options + else + options = 0 + obj.inspect.sub(/^.*\//, '').each_byte do |c| + options += case c + when ?i + 1 + when ?x + 2 + when ?m + 4 + when ?n + 16 + when ?e + 32 + when ?s + 48 + when ?u + 64 + end + end + end + param.add('options', SOAPInt.new(options)) + addiv2soapattr(param, obj, map) + when ::Range + unless @allow_original_mapping + return nil + end + param = SOAPStruct.new(TYPE_RANGE) + mark_marshalled_obj(obj, param) + if obj.class != Range + param.extraattr[RubyTypeName] = obj.class.name + end + param.add('begin', Mapping._obj2soap(obj.begin, map)) + param.add('end', Mapping._obj2soap(obj.end, map)) + param.add('exclude_end', SOAP::SOAPBoolean.new(obj.exclude_end?)) + addiv2soapattr(param, obj, map) + when ::Class + unless @allow_original_mapping + return nil + end + if obj.to_s[0] == ?# + raise TypeError.new("can't dump anonymous class #{obj}") + end + param = SOAPStruct.new(TYPE_CLASS) + mark_marshalled_obj(obj, param) + param.add('name', SOAPString.new(obj.name)) + addiv2soapattr(param, obj, map) + when ::Module + unless @allow_original_mapping + return nil + end + if obj.to_s[0] == ?# + raise TypeError.new("can't dump anonymous module #{obj}") + end + param = SOAPStruct.new(TYPE_MODULE) + mark_marshalled_obj(obj, param) + param.add('name', SOAPString.new(obj.name)) + addiv2soapattr(param, obj, map) + when ::Symbol + unless @allow_original_mapping + return nil + end + param = SOAPStruct.new(TYPE_SYMBOL) + mark_marshalled_obj(obj, param) + param.add('id', SOAPString.new(obj.id2name)) + addiv2soapattr(param, obj, map) + when ::Struct + unless @allow_original_mapping + # treat it as an user defined class. [ruby-talk:104980] + #param = unknownobj2soap(soap_class, obj, info, map) + param = SOAPStruct.new(XSD::AnyTypeName) + mark_marshalled_obj(obj, param) + obj.members.each do |member| + param.add(Mapping.name2elename(member), + Mapping._obj2soap(obj[member], map)) + end + else + param = SOAPStruct.new(TYPE_STRUCT) + mark_marshalled_obj(obj, param) + param.add('type', ele_type = SOAPString.new(obj.class.to_s)) + ele_member = SOAPStruct.new + obj.members.each do |member| + ele_member.add(Mapping.name2elename(member), + Mapping._obj2soap(obj[member], map)) + end + param.add('member', ele_member) + addiv2soapattr(param, obj, map) + end + when ::IO, ::Binding, ::Continuation, ::Data, ::Dir, ::File::Stat, + ::MatchData, Method, ::Proc, ::Thread, ::ThreadGroup + # from 1.8: Process::Status, UnboundMethod + return nil + when ::SOAP::Mapping::Object + param = SOAPStruct.new(XSD::AnyTypeName) + mark_marshalled_obj(obj, param) + obj.__xmlele.each do |key, value| + param.add(key.name, Mapping._obj2soap(value, map)) + end + obj.__xmlattr.each do |key, value| + param.extraattr[key] = value + end + when ::Exception + typestr = Mapping.name2elename(obj.class.to_s) + param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, typestr)) + mark_marshalled_obj(obj, param) + param.add('message', Mapping._obj2soap(obj.message, map)) + param.add('backtrace', Mapping._obj2soap(obj.backtrace, map)) + addiv2soapattr(param, obj, map) + else + param = unknownobj2soap(soap_class, obj, info, map) + end + param + end + + def soap2obj(obj_class, node, info, map) + rubytype = node.extraattr[RubyTypeName] + if rubytype or node.type.namespace == RubyTypeNamespace + rubytype2obj(node, info, map, rubytype) + elsif node.type == XSD::AnyTypeName or node.type == XSD::AnySimpleTypeName + anytype2obj(node, info, map) + else + unknowntype2obj(node, info, map) + end + end + +private + + def addiv2soapattr(node, obj, map) + return if obj.instance_variables.empty? + ivars = SOAPStruct.new # Undefined type. + setiv2soap(ivars, obj, map) + node.extraattr[RubyIVarName] = ivars + end + + def unknownobj2soap(soap_class, obj, info, map) + if obj.class.name.empty? + raise TypeError.new("can't dump anonymous class #{obj}") + end + singleton_class = class << obj; self; end + if !singleton_methods_true(obj).empty? or + !singleton_class.instance_variables.empty? + raise TypeError.new("singleton can't be dumped #{obj}") + end + if !(singleton_class.ancestors - obj.class.ancestors).empty? + typestr = Mapping.name2elename(obj.class.to_s) + type = XSD::QName.new(RubyTypeNamespace, typestr) + else + type = Mapping.class2element(obj.class) + end + param = SOAPStruct.new(type) + mark_marshalled_obj(obj, param) + setiv2soap(param, obj, map) + param + end + + if RUBY_VERSION >= '1.8.0' + def singleton_methods_true(obj) + obj.singleton_methods(true) + end + else + def singleton_methods_true(obj) + obj.singleton_methods + end + end + + def rubytype2obj(node, info, map, rubytype) + klass = rubytype ? Mapping.class_from_name(rubytype) : nil + obj = nil + case node + when SOAPString + return @string_factory.soap2obj(klass || String, node, info, map) + when SOAPDateTime + #return @datetime_factory.soap2obj(klass || Time, node, info, map) + klass ||= Time + t = node.to_time + arg = [t.year, t.month, t.mday, t.hour, t.min, t.sec, t.usec] + obj = t.gmt? ? klass.gm(*arg) : klass.local(*arg) + mark_unmarshalled_obj(node, obj) + return true, obj + when SOAPArray + return @array_factory.soap2obj(klass || Array, node, info, map) + when SOAPNil, SOAPBoolean, SOAPInt, SOAPInteger, SOAPDecimal, SOAPDouble + return @basetype_factory.soap2obj(nil, node, info, map) + when SOAPStruct + return rubytypestruct2obj(node, info, map, rubytype) + else + raise + end + end + + def rubytypestruct2obj(node, info, map, rubytype) + klass = rubytype ? Mapping.class_from_name(rubytype) : nil + obj = nil + case node.type + when TYPE_HASH + klass = rubytype ? Mapping.class_from_name(rubytype) : Hash + obj = Mapping.create_empty_object(klass) + mark_unmarshalled_obj(node, obj) + node.each do |key, value| + next unless key == 'item' + obj[Mapping._soap2obj(value['key'], map)] = + Mapping._soap2obj(value['value'], map) + end + if node.key?('default') + obj.default = Mapping._soap2obj(node['default'], map) + end + when TYPE_REGEXP + klass = rubytype ? Mapping.class_from_name(rubytype) : Regexp + obj = Mapping.create_empty_object(klass) + mark_unmarshalled_obj(node, obj) + source = node['source'].string + options = node['options'].data || 0 + Regexp.instance_method(:initialize).bind(obj).call(source, options) + when TYPE_RANGE + klass = rubytype ? Mapping.class_from_name(rubytype) : Range + obj = Mapping.create_empty_object(klass) + mark_unmarshalled_obj(node, obj) + first = Mapping._soap2obj(node['begin'], map) + last = Mapping._soap2obj(node['end'], map) + exclude_end = node['exclude_end'].data + Range.instance_method(:initialize).bind(obj).call(first, last, exclude_end) + when TYPE_CLASS + obj = Mapping.class_from_name(node['name'].data) + when TYPE_MODULE + obj = Mapping.class_from_name(node['name'].data) + when TYPE_SYMBOL + obj = node['id'].data.intern + when TYPE_STRUCT + typestr = Mapping.elename2name(node['type'].data) + klass = Mapping.class_from_name(typestr) + if klass.nil? + return false + end + unless klass <= ::Struct + return false + end + obj = Mapping.create_empty_object(klass) + mark_unmarshalled_obj(node, obj) + node['member'].each do |name, value| + obj[Mapping.elename2name(name)] = Mapping._soap2obj(value, map) + end + else + return unknowntype2obj(node, info, map) + end + return true, obj + end + + def anytype2obj(node, info, map) + case node + when SOAPBasetype + return true, node.data + when SOAPStruct + klass = ::SOAP::Mapping::Object + obj = klass.new + mark_unmarshalled_obj(node, obj) + node.each do |name, value| + obj.__add_xmlele_value(XSD::QName.new(nil, name), + Mapping._soap2obj(value, map)) + end + unless node.extraattr.empty? + obj.instance_variable_set('@__xmlattr', node.extraattr) + end + return true, obj + else + return false + end + end + + def unknowntype2obj(node, info, map) + case node + when SOAPBasetype + return true, node.data + when SOAPArray + return @array_factory.soap2obj(Array, node, info, map) + when SOAPStruct + obj = unknownstruct2obj(node, info, map) + return true, obj if obj + if !@allow_untyped_struct + return false + end + return anytype2obj(node, info, map) + else + # Basetype which is not defined... + return false + end + end + + def unknownstruct2obj(node, info, map) + unless node.type.name + return nil + end + typestr = Mapping.elename2name(node.type.name) + klass = Mapping.class_from_name(typestr) + if klass.nil? and @allow_untyped_struct + klass = Mapping.class_from_name(typestr, true) # lenient + end + if klass.nil? + return nil + end + if klass <= ::Exception + return exception2obj(klass, node, map) + end + klass_type = Mapping.class2qname(klass) + return nil unless node.type.match(klass_type) + obj = nil + begin + obj = Mapping.create_empty_object(klass) + rescue + # type name "data" tries Data.new which raises TypeError + nil + end + mark_unmarshalled_obj(node, obj) + setiv2obj(obj, node, map) + obj + end + + def exception2obj(klass, node, map) + message = Mapping._soap2obj(node['message'], map) + backtrace = Mapping._soap2obj(node['backtrace'], map) + obj = Mapping.create_empty_object(klass) + obj = obj.exception(message) + mark_unmarshalled_obj(node, obj) + obj.set_backtrace(backtrace) + obj + end + + # Only creates empty array. Do String#replace it with real string. + def array2obj(node, map, rubytype) + klass = rubytype ? Mapping.class_from_name(rubytype) : Array + obj = Mapping.create_empty_object(klass) + mark_unmarshalled_obj(node, obj) + obj + end + + # Only creates empty string. Do String#replace it with real string. + def string2obj(node, map, rubytype) + klass = rubytype ? Mapping.class_from_name(rubytype) : String + obj = Mapping.create_empty_object(klass) + mark_unmarshalled_obj(node, obj) + obj + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/typeMap.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/typeMap.rb new file mode 100644 index 0000000000..34db19a5b6 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/typeMap.rb @@ -0,0 +1,50 @@ +# SOAP4R - Base type mapping definition +# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +module SOAP + + +TypeMap = { + XSD::XSDAnySimpleType::Type => SOAPAnySimpleType, + XSD::XSDString::Type => SOAPString, + XSD::XSDBoolean::Type => SOAPBoolean, + XSD::XSDDecimal::Type => SOAPDecimal, + XSD::XSDFloat::Type => SOAPFloat, + XSD::XSDDouble::Type => SOAPDouble, + XSD::XSDDuration::Type => SOAPDuration, + XSD::XSDDateTime::Type => SOAPDateTime, + XSD::XSDTime::Type => SOAPTime, + XSD::XSDDate::Type => SOAPDate, + XSD::XSDGYearMonth::Type => SOAPGYearMonth, + XSD::XSDGYear::Type => SOAPGYear, + XSD::XSDGMonthDay::Type => SOAPGMonthDay, + XSD::XSDGDay::Type => SOAPGDay, + XSD::XSDGMonth::Type => SOAPGMonth, + XSD::XSDHexBinary::Type => SOAPHexBinary, + XSD::XSDBase64Binary::Type => SOAPBase64, + XSD::XSDAnyURI::Type => SOAPAnyURI, + XSD::XSDQName::Type => SOAPQName, + XSD::XSDInteger::Type => SOAPInteger, + XSD::XSDNonPositiveInteger::Type => SOAPNonPositiveInteger, + XSD::XSDNegativeInteger::Type => SOAPNegativeInteger, + XSD::XSDLong::Type => SOAPLong, + XSD::XSDInt::Type => SOAPInt, + XSD::XSDShort::Type => SOAPShort, + XSD::XSDByte::Type => SOAPByte, + XSD::XSDNonNegativeInteger::Type => SOAPNonNegativeInteger, + XSD::XSDUnsignedLong::Type => SOAPUnsignedLong, + XSD::XSDUnsignedInt::Type => SOAPUnsignedInt, + XSD::XSDUnsignedShort::Type => SOAPUnsignedShort, + XSD::XSDUnsignedByte::Type => SOAPUnsignedByte, + XSD::XSDPositiveInteger::Type => SOAPPositiveInteger, + + SOAP::SOAPBase64::Type => SOAPBase64, +} + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/wsdlencodedregistry.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/wsdlencodedregistry.rb new file mode 100644 index 0000000000..4efb60188f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/wsdlencodedregistry.rb @@ -0,0 +1,280 @@ +# SOAP4R - WSDL encoded mapping registry. +# Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/qname' +require 'xsd/namedelements' +require 'soap/baseData' +require 'soap/mapping/mapping' +require 'soap/mapping/typeMap' + + +module SOAP +module Mapping + + +class WSDLEncodedRegistry < Registry + include TraverseSupport + + attr_reader :definedelements + attr_reader :definedtypes + attr_accessor :excn_handler_obj2soap + attr_accessor :excn_handler_soap2obj + + def initialize(definedtypes = XSD::NamedElements::Empty) + @definedtypes = definedtypes + # @definedelements = definedelements needed? + @excn_handler_obj2soap = nil + @excn_handler_soap2obj = nil + # For mapping AnyType element. + @rubytype_factory = RubytypeFactory.new( + :allow_untyped_struct => true, + :allow_original_mapping => true + ) + @schema_element_cache = {} + end + + def obj2soap(obj, qname = nil) + soap_obj = nil + if type = @definedtypes[qname] + soap_obj = obj2typesoap(obj, type) + else + soap_obj = any2soap(obj, qname) + end + return soap_obj if soap_obj + if @excn_handler_obj2soap + soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj| + Mapping._obj2soap(yield_obj, self) + } + return soap_obj if soap_obj + end + if qname + raise MappingError.new("cannot map #{obj.class.name} as #{qname}") + else + raise MappingError.new("cannot map #{obj.class.name} to SOAP/OM") + end + end + + # map anything for now: must refer WSDL while mapping. [ToDo] + def soap2obj(node, obj_class = nil) + begin + return any2obj(node, obj_class) + rescue MappingError + end + if @excn_handler_soap2obj + begin + return @excn_handler_soap2obj.call(node) { |yield_node| + Mapping._soap2obj(yield_node, self) + } + rescue Exception + end + end + raise MappingError.new("cannot map #{node.type.name} to Ruby object") + end + +private + + def any2soap(obj, qname) + if obj.nil? + SOAPNil.new + elsif qname.nil? or qname == XSD::AnyTypeName + @rubytype_factory.obj2soap(nil, obj, nil, self) + elsif obj.is_a?(XSD::NSDBase) + soap2soap(obj, qname) + elsif (type = TypeMap[qname]) + base2soap(obj, type) + else + nil + end + end + + def soap2soap(obj, type_qname) + if obj.is_a?(SOAPBasetype) + obj + elsif obj.is_a?(SOAPStruct) && (type = @definedtypes[type_qname]) + soap_obj = obj + mark_marshalled_obj(obj, soap_obj) + elements2soap(obj, soap_obj, type.content.elements) + soap_obj + elsif obj.is_a?(SOAPArray) && (type = @definedtypes[type_qname]) + soap_obj = obj + contenttype = type.child_type + mark_marshalled_obj(obj, soap_obj) + obj.replace do |ele| + Mapping._obj2soap(ele, self, contenttype) + end + soap_obj + else + nil + end + end + + def obj2typesoap(obj, type) + if type.is_a?(::WSDL::XMLSchema::SimpleType) + simpleobj2soap(obj, type) + else + complexobj2soap(obj, type) + end + end + + def simpleobj2soap(obj, type) + type.check_lexical_format(obj) + return SOAPNil.new if obj.nil? # ToDo: check nillable. + o = base2soap(obj, TypeMap[type.base]) + o + end + + def complexobj2soap(obj, type) + case type.compoundtype + when :TYPE_STRUCT + struct2soap(obj, type.name, type) + when :TYPE_ARRAY + array2soap(obj, type.name, type) + when :TYPE_MAP + map2soap(obj, type.name, type) + when :TYPE_SIMPLE + simpleobj2soap(obj, type.simplecontent) + when :TYPE_EMPTY + raise MappingError.new("should be empty") unless obj.nil? + SOAPNil.new + else + raise MappingError.new("unknown compound type: #{type.compoundtype}") + end + end + + def base2soap(obj, type) + soap_obj = nil + if type <= XSD::XSDString + str = XSD::Charset.encoding_conv(obj.to_s, + Thread.current[:SOAPExternalCES], XSD::Charset.encoding) + soap_obj = type.new(str) + mark_marshalled_obj(obj, soap_obj) + else + soap_obj = type.new(obj) + end + soap_obj + end + + def struct2soap(obj, type_qname, type) + return SOAPNil.new if obj.nil? # ToDo: check nillable. + soap_obj = SOAPStruct.new(type_qname) + unless obj.nil? + mark_marshalled_obj(obj, soap_obj) + elements2soap(obj, soap_obj, type.content.elements) + end + soap_obj + end + + def array2soap(obj, type_qname, type) + return SOAPNil.new if obj.nil? # ToDo: check nillable. + arytype = type.child_type + soap_obj = SOAPArray.new(ValueArrayName, 1, arytype) + unless obj.nil? + mark_marshalled_obj(obj, soap_obj) + obj.each do |item| + soap_obj.add(Mapping._obj2soap(item, self, arytype)) + end + end + soap_obj + end + + MapKeyName = XSD::QName.new(nil, "key") + MapValueName = XSD::QName.new(nil, "value") + def map2soap(obj, type_qname, type) + return SOAPNil.new if obj.nil? # ToDo: check nillable. + keytype = type.child_type(MapKeyName) || XSD::AnyTypeName + valuetype = type.child_type(MapValueName) || XSD::AnyTypeName + soap_obj = SOAPStruct.new(MapQName) + unless obj.nil? + mark_marshalled_obj(obj, soap_obj) + obj.each do |key, value| + elem = SOAPStruct.new + elem.add("key", Mapping._obj2soap(key, self, keytype)) + elem.add("value", Mapping._obj2soap(value, self, valuetype)) + # ApacheAxis allows only 'item' here. + soap_obj.add("item", elem) + end + end + soap_obj + end + + def elements2soap(obj, soap_obj, elements) + elements.each do |element| + name = element.name.name + child_obj = Mapping.get_attribute(obj, name) + soap_obj.add(name, + Mapping._obj2soap(child_obj, self, element.type || element.name)) + end + end + + def any2obj(node, obj_class) + unless obj_class + typestr = XSD::CodeGen::GenSupport.safeconstname(node.elename.name) + obj_class = Mapping.class_from_name(typestr) + end + if obj_class and obj_class.class_variables.include?('@@schema_element') + soap2stubobj(node, obj_class) + else + Mapping._soap2obj(node, Mapping::DefaultRegistry, obj_class) + end + end + + def soap2stubobj(node, obj_class) + obj = Mapping.create_empty_object(obj_class) + unless node.is_a?(SOAPNil) + add_elements2stubobj(node, obj) + end + obj + end + + def add_elements2stubobj(node, obj) + elements, as_array = schema_element_definition(obj.class) + vars = {} + node.each do |name, value| + item = elements.find { |k, v| k.name == name } + if item + elename, class_name = item + if klass = Mapping.class_from_name(class_name) + # klass must be a SOAPBasetype or a class + if klass.ancestors.include?(::SOAP::SOAPBasetype) + if value.respond_to?(:data) + child = klass.new(value.data).data + else + child = klass.new(nil).data + end + else + child = Mapping._soap2obj(value, self, klass) + end + elsif klass = Mapping.module_from_name(class_name) + # simpletype + if value.respond_to?(:data) + child = value.data + else + raise MappingError.new( + "cannot map to a module value: #{class_name}") + end + else + raise MappingError.new("unknown class: #{class_name}") + end + else # untyped element is treated as anyType. + child = Mapping._soap2obj(value, self) + end + vars[name] = child + end + Mapping.set_attributes(obj, vars) + end + + # it caches @@schema_element. this means that @@schema_element must not be + # changed while a lifetime of a WSDLLiteralRegistry. + def schema_element_definition(klass) + @schema_element_cache[klass] ||= Mapping.schema_element_definition(klass) + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/wsdlliteralregistry.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/wsdlliteralregistry.rb new file mode 100644 index 0000000000..7bb8e12203 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mapping/wsdlliteralregistry.rb @@ -0,0 +1,418 @@ +# SOAP4R - WSDL literal mapping registry. +# Copyright (C) 2004, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/baseData' +require 'soap/mapping/mapping' +require 'soap/mapping/typeMap' +require 'xsd/codegen/gensupport' +require 'xsd/namedelements' + + +module SOAP +module Mapping + + +class WSDLLiteralRegistry < Registry + attr_reader :definedelements + attr_reader :definedtypes + attr_accessor :excn_handler_obj2soap + attr_accessor :excn_handler_soap2obj + + def initialize(definedtypes = XSD::NamedElements::Empty, + definedelements = XSD::NamedElements::Empty) + @definedtypes = definedtypes + @definedelements = definedelements + @excn_handler_obj2soap = nil + @excn_handler_soap2obj = nil + @schema_element_cache = {} + @schema_attribute_cache = {} + end + + def obj2soap(obj, qname) + soap_obj = nil + if ele = @definedelements[qname] + soap_obj = obj2elesoap(obj, ele) + elsif type = @definedtypes[qname] + soap_obj = obj2typesoap(obj, type, true) + else + soap_obj = any2soap(obj, qname) + end + return soap_obj if soap_obj + if @excn_handler_obj2soap + soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj| + Mapping.obj2soap(yield_obj, nil, nil, MAPPING_OPT) + } + return soap_obj if soap_obj + end + raise MappingError.new("cannot map #{obj.class.name} as #{qname}") + end + + # node should be a SOAPElement + def soap2obj(node, obj_class = nil) + # obj_class is given when rpc/literal service. but ignored for now. + begin + return any2obj(node) + rescue MappingError + end + if @excn_handler_soap2obj + begin + return @excn_handler_soap2obj.call(node) { |yield_node| + Mapping.soap2obj(yield_node, nil, nil, MAPPING_OPT) + } + rescue Exception + end + end + if node.respond_to?(:type) + raise MappingError.new("cannot map #{node.type.name} to Ruby object") + else + raise MappingError.new("cannot map #{node.elename.name} to Ruby object") + end + end + +private + + MAPPING_OPT = { :no_reference => true } + + def obj2elesoap(obj, ele) + o = nil + qualified = (ele.elementform == 'qualified') + if ele.type + if type = @definedtypes[ele.type] + o = obj2typesoap(obj, type, qualified) + elsif type = TypeMap[ele.type] + o = base2soap(obj, type) + else + raise MappingError.new("cannot find type #{ele.type}") + end + elsif ele.local_complextype + o = obj2typesoap(obj, ele.local_complextype, qualified) + add_attributes2soap(obj, o) + elsif ele.local_simpletype + o = obj2typesoap(obj, ele.local_simpletype, qualified) + else + raise MappingError.new('illegal schema?') + end + o.elename = ele.name + o + end + + def obj2typesoap(obj, type, qualified) + if type.is_a?(::WSDL::XMLSchema::SimpleType) + simpleobj2soap(obj, type) + else + complexobj2soap(obj, type, qualified) + end + end + + def simpleobj2soap(obj, type) + type.check_lexical_format(obj) + return SOAPNil.new if obj.nil? # ToDo: check nillable. + o = base2soap(obj, TypeMap[type.base]) + o + end + + def complexobj2soap(obj, type, qualified) + o = SOAPElement.new(type.name) + o.qualified = qualified + type.each_element do |child_ele| + child = Mapping.get_attribute(obj, child_ele.name.name) + if child.nil? + if child_ele.nillable + # ToDo: test + # add empty element + child_soap = obj2elesoap(nil, child_ele) + o.add(child_soap) + elsif Integer(child_ele.minoccurs) == 0 + # nothing to do + else + raise MappingError.new("nil not allowed: #{child_ele.name.name}") + end + elsif child_ele.map_as_array? + child.each do |item| + child_soap = obj2elesoap(item, child_ele) + o.add(child_soap) + end + else + child_soap = obj2elesoap(child, child_ele) + o.add(child_soap) + end + end + o + end + + def any2soap(obj, qname) + if obj.is_a?(SOAPElement) + obj + elsif obj.class.class_variables.include?('@@schema_element') + stubobj2soap(obj, qname) + elsif obj.is_a?(SOAP::Mapping::Object) + mappingobj2soap(obj, qname) + elsif obj.is_a?(Hash) + ele = SOAPElement.from_obj(obj) + ele.elename = qname + ele + else + # expected to be a basetype or an anyType. + # SOAPStruct, etc. is used instead of SOAPElement. + begin + ele = Mapping.obj2soap(obj, nil, nil, MAPPING_OPT) + ele.elename = qname + ele + rescue MappingError + ele = SOAPElement.new(qname, obj.to_s) + end + if obj.respond_to?(:__xmlattr) + obj.__xmlattr.each do |key, value| + ele.extraattr[key] = value + end + end + ele + end + end + + def stubobj2soap(obj, qname) + ele = SOAPElement.new(qname) + ele.qualified = + (obj.class.class_variables.include?('@@schema_qualified') and + obj.class.class_eval('@@schema_qualified')) + add_elements2soap(obj, ele) + add_attributes2soap(obj, ele) + ele + end + + def mappingobj2soap(obj, qname) + ele = SOAPElement.new(qname) + obj.__xmlele.each do |key, value| + if value.is_a?(::Array) + value.each do |item| + ele.add(obj2soap(item, key)) + end + else + ele.add(obj2soap(value, key)) + end + end + obj.__xmlattr.each do |key, value| + ele.extraattr[key] = value + end + ele + end + + def add_elements2soap(obj, ele) + elements, as_array = schema_element_definition(obj.class) + if elements + elements.each do |elename, type| + if child = Mapping.get_attribute(obj, elename.name) + if as_array.include?(elename.name) + child.each do |item| + ele.add(obj2soap(item, elename)) + end + else + ele.add(obj2soap(child, elename)) + end + elsif obj.is_a?(::Array) and as_array.include?(elename.name) + obj.each do |item| + ele.add(obj2soap(item, elename)) + end + end + end + end + end + + def add_attributes2soap(obj, ele) + attributes = schema_attribute_definition(obj.class) + if attributes + attributes.each do |qname, param| + attr = obj.__send__('xmlattr_' + + XSD::CodeGen::GenSupport.safevarname(qname.name)) + ele.extraattr[qname] = attr + end + end + end + + def base2soap(obj, type) + soap_obj = nil + if type <= XSD::XSDString + str = XSD::Charset.encoding_conv(obj.to_s, + Thread.current[:SOAPExternalCES], XSD::Charset.encoding) + soap_obj = type.new(str) + else + soap_obj = type.new(obj) + end + soap_obj + end + + def anytype2obj(node) + if node.is_a?(::SOAP::SOAPBasetype) + return node.data + end + klass = ::SOAP::Mapping::Object + obj = klass.new + obj + end + + def any2obj(node, obj_class = nil) + unless obj_class + typestr = XSD::CodeGen::GenSupport.safeconstname(node.elename.name) + obj_class = Mapping.class_from_name(typestr) + end + if obj_class and obj_class.class_variables.include?('@@schema_element') + soapele2stubobj(node, obj_class) + elsif node.is_a?(SOAPElement) or node.is_a?(SOAPStruct) + # SOAPArray for literal? + soapele2plainobj(node) + else + obj = Mapping.soap2obj(node, nil, obj_class, MAPPING_OPT) + add_attributes2plainobj(node, obj) + obj + end + end + + def soapele2stubobj(node, obj_class) + obj = Mapping.create_empty_object(obj_class) + add_elements2stubobj(node, obj) + add_attributes2stubobj(node, obj) + obj + end + + def soapele2plainobj(node) + obj = anytype2obj(node) + add_elements2plainobj(node, obj) + add_attributes2plainobj(node, obj) + obj + end + + def add_elements2stubobj(node, obj) + elements, as_array = schema_element_definition(obj.class) + vars = {} + node.each do |name, value| + item = elements.find { |k, v| k.name == name } + if item + elename, class_name = item + if klass = Mapping.class_from_name(class_name) + # klass must be a SOAPBasetype or a class + if klass.ancestors.include?(::SOAP::SOAPBasetype) + if value.respond_to?(:data) + child = klass.new(value.data).data + else + child = klass.new(nil).data + end + else + child = any2obj(value, klass) + end + elsif klass = Mapping.module_from_name(class_name) + # simpletype + if value.respond_to?(:data) + child = value.data + else + raise MappingError.new( + "cannot map to a module value: #{class_name}") + end + else + raise MappingError.new("unknown class/module: #{class_name}") + end + else # untyped element is treated as anyType. + child = any2obj(value) + end + if as_array.include?(elename.name) + (vars[name] ||= []) << child + else + vars[name] = child + end + end + Mapping.set_attributes(obj, vars) + end + + def add_attributes2stubobj(node, obj) + if attributes = schema_attribute_definition(obj.class) + define_xmlattr(obj) + attributes.each do |qname, class_name| + attr = node.extraattr[qname] + next if attr.nil? or attr.empty? + klass = Mapping.class_from_name(class_name) + if klass.ancestors.include?(::SOAP::SOAPBasetype) + child = klass.new(attr).data + else + child = attr + end + obj.__xmlattr[qname] = child + define_xmlattr_accessor(obj, qname) + end + end + end + + def add_elements2plainobj(node, obj) + node.each do |name, value| + obj.__add_xmlele_value(value.elename, any2obj(value)) + end + end + + def add_attributes2plainobj(node, obj) + return if node.extraattr.empty? + define_xmlattr(obj) + node.extraattr.each do |qname, value| + obj.__xmlattr[qname] = value + define_xmlattr_accessor(obj, qname) + end + end + + if RUBY_VERSION > "1.7.0" + def define_xmlattr_accessor(obj, qname) + name = XSD::CodeGen::GenSupport.safemethodname(qname.name) + Mapping.define_attr_accessor(obj, 'xmlattr_' + name, + proc { @__xmlattr[qname] }, + proc { |value| @__xmlattr[qname] = value }) + end + else + def define_xmlattr_accessor(obj, qname) + name = XSD::CodeGen::GenSupport.safemethodname(qname.name) + obj.instance_eval <<-EOS + def #{name} + @__xmlattr[#{qname.dump}] + end + + def #{name}=(value) + @__xmlattr[#{qname.dump}] = value + end + EOS + end + end + + if RUBY_VERSION > "1.7.0" + def define_xmlattr(obj) + obj.instance_variable_set('@__xmlattr', {}) + unless obj.respond_to?(:__xmlattr) + Mapping.define_attr_accessor(obj, :__xmlattr, proc { @__xmlattr }) + end + end + else + def define_xmlattr(obj) + obj.instance_variable_set('@__xmlattr', {}) + unless obj.respond_to?(:__xmlattr) + obj.instance_eval <<-EOS + def __xmlattr + @__xmlattr + end + EOS + end + end + end + + # it caches @@schema_element. this means that @@schema_element must not be + # changed while a lifetime of a WSDLLiteralRegistry. + def schema_element_definition(klass) + @schema_element_cache[klass] ||= Mapping.schema_element_definition(klass) + end + + def schema_attribute_definition(klass) + @schema_attribute_cache[klass] ||= Mapping.schema_attribute_definition(klass) + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/marshal.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/marshal.rb new file mode 100644 index 0000000000..1c3d5b01db --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/marshal.rb @@ -0,0 +1,59 @@ +# SOAP4R - Marshalling/Unmarshalling Ruby's object using SOAP Encoding. +# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require "soap/mapping" +require "soap/processor" + + +module SOAP + + +module Marshal + # Trying xsd:dateTime data to be recovered as aTime. + MarshalMappingRegistry = Mapping::Registry.new( + :allow_original_mapping => true) + MarshalMappingRegistry.add( + Time, + ::SOAP::SOAPDateTime, + ::SOAP::Mapping::Registry::DateTimeFactory + ) + + class << self + public + def dump(obj, io = nil) + marshal(obj, MarshalMappingRegistry, io) + end + + def load(stream) + unmarshal(stream, MarshalMappingRegistry) + end + + def marshal(obj, mapping_registry = MarshalMappingRegistry, io = nil) + elename = Mapping.name2elename(obj.class.to_s) + soap_obj = Mapping.obj2soap(obj, mapping_registry) + body = SOAPBody.new + body.add(elename, soap_obj) + env = SOAPEnvelope.new(nil, body) + SOAP::Processor.marshal(env, {}, io) + end + + def unmarshal(stream, mapping_registry = MarshalMappingRegistry) + env = SOAP::Processor.unmarshal(stream) + if env.nil? + raise ArgumentError.new("Illegal SOAP marshal format.") + end + Mapping.soap2obj(env.body.root_node, mapping_registry) + end + end +end + + +end + + +SOAPMarshal = SOAP::Marshal diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mimemessage.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mimemessage.rb new file mode 100644 index 0000000000..acb4322e11 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/mimemessage.rb @@ -0,0 +1,240 @@ +# SOAP4R - MIME Message implementation. +# Copyright (C) 2002 Jamie Herre. + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/attachment' + + +module SOAP + + +# Classes for MIME message handling. Should be put somewhere else! +# Tried using the 'tmail' module but found that I needed something +# lighter in weight. + + +class MIMEMessage + class MIMEMessageError < StandardError; end + + MultipartContentType = 'multipart/\w+' + + class Header + attr_accessor :str, :key, :root + + def initialize + @attrs = {} + end + + def [](key) + @attrs[key] + end + + def []=(key, value) + @attrs[key] = value + end + + def to_s + @key + ": " + @str + end + end + + class Headers < Hash + def self.parse(str) + new.parse(str) + end + + def parse(str) + header_cache = nil + str.each do |line| + case line + when /^\A[^\: \t]+:\s*.+$/ + parse_line(header_cache) if header_cache + header_cache = line.sub(/\r?\n\z/, '') + when /^\A\s+(.*)$/ + # a continuous line at the beginning line crashes here. + header_cache << line + else + raise RuntimeError.new("unexpected header: #{line.inspect}") + end + end + parse_line(header_cache) if header_cache + self + end + + def parse_line(line) + if /^\A([^\: \t]+):\s*(.+)\z/ =~ line + header = parse_rhs($2.strip) + header.key = $1.strip + self[header.key.downcase] = header + else + raise RuntimeError.new("unexpected header line: #{line.inspect}") + end + end + + def parse_rhs(str) + a = str.split(/;+\s+/) + header = Header.new + header.str = str + header.root = a.shift + a.each do |pair| + if pair =~ /(\w+)\s*=\s*"?([^"]+)"?/ + header[$1.downcase] = $2 + else + raise RuntimeError.new("unexpected header component: #{pair.inspect}") + end + end + header + end + + def add(key, value) + if key != nil and value != nil + header = parse_rhs(value) + header.key = key + self[key.downcase] = header + end + end + + def to_s + self.values.collect { |hdr| + hdr.to_s + }.join("\r\n") + end + end + + class Part + attr_accessor :headers, :body + + def initialize + @headers = Headers.new + @headers.add("Content-Transfer-Encoding", "8bit") + @body = nil + @contentid = nil + end + + def self.parse(str) + new.parse(str) + end + + def parse(str) + headers, body = str.split(/\r\n\r\n/s) + if headers != nil and body != nil + @headers = Headers.parse(headers) + @body = body.sub(/\r\n\z/, '') + else + raise RuntimeError.new("unexpected part: #{str.inspect}") + end + self + end + + def contentid + if @contentid == nil and @headers.key?('content-id') + @contentid = @headers['content-id'].str + @contentid = $1 if @contentid =~ /^<(.+)>$/ + end + @contentid + end + + alias content body + + def to_s + @headers.to_s + "\r\n\r\n" + @body + end + end + + def initialize + @parts = [] + @headers = Headers.new + @root = nil + end + + def self.parse(head, str) + new.parse(head, str) + end + + attr_reader :parts, :headers + + def close + @headers.add( + "Content-Type", + "multipart/related; type=\"text/xml\"; boundary=\"#{boundary}\"; start=\"#{@parts[0].contentid}\"" + ) + end + + def parse(head, str) + @headers = Headers.parse(head + "\r\n" + "From: jfh\r\n") + boundary = @headers['content-type']['boundary'] + if boundary != nil + parts = str.split(/--#{Regexp.quote(boundary)}\s*(?:\r\n|--\r\n)/) + part = parts.shift # preamble must be ignored. + @parts = parts.collect { |part| Part.parse(part) } + else + @parts = [Part.parse(str)] + end + if @parts.length < 1 + raise MIMEMessageError.new("This message contains no valid parts!") + end + self + end + + def root + if @root == nil + start = @headers['content-type']['start'] + @root = (start && @parts.find { |prt| prt.contentid == start }) || + @parts[0] + end + @root + end + + def boundary + if @boundary == nil + @boundary = "----=Part_" + __id__.to_s + rand.to_s + end + @boundary + end + + def add_part(content) + part = Part.new + part.headers.add("Content-Type", + "text/xml; charset=" + XSD::Charset.xml_encoding_label) + part.headers.add("Content-ID", Attachment.contentid(part)) + part.body = content + @parts.unshift(part) + end + + def add_attachment(attach) + part = Part.new + part.headers.add("Content-Type", attach.contenttype) + part.headers.add("Content-ID", attach.mime_contentid) + part.body = attach.content + @parts.unshift(part) + end + + def has_parts? + (@parts.length > 0) + end + + def headers_str + @headers.to_s + end + + def content_str + str = '' + @parts.each do |prt| + str << "--" + boundary + "\r\n" + str << prt.to_s + "\r\n" + end + str << '--' + boundary + "--\r\n" + str + end + + def to_s + str = headers_str + "\r\n\r\n" + conent_str + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/netHttpClient.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/netHttpClient.rb new file mode 100644 index 0000000000..10d68e2a4c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/netHttpClient.rb @@ -0,0 +1,190 @@ +# SOAP4R - net/http wrapper +# Copyright (C) 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'net/http' + + +module SOAP + + +class NetHttpClient + + SSLEnabled = begin + require 'net/https' + true + rescue LoadError + false + end + + attr_reader :proxy + attr_accessor :no_proxy + attr_accessor :debug_dev + attr_accessor :ssl_config # ignored for now. + attr_accessor :protocol_version # ignored for now. + attr_accessor :connect_timeout + attr_accessor :send_timeout # ignored for now. + attr_accessor :receive_timeout + + def initialize(proxy = nil, agent = nil) + @proxy = proxy ? URI.parse(proxy) : nil + @agent = agent + @debug_dev = nil + @session_manager = SessionManager.new + @no_proxy = @ssl_config = @protocol_version = nil + @connect_timeout = @send_timeout = @receive_timeout = nil + end + + def test_loopback_response + raise NotImplementedError.new("not supported for now") + end + + def proxy=(proxy) + if proxy.nil? + @proxy = nil + else + if proxy.is_a?(URI) + @proxy = proxy + else + @proxy = URI.parse(proxy) + end + if @proxy.scheme == nil or @proxy.scheme.downcase != 'http' or + @proxy.host == nil or @proxy.port == nil + raise ArgumentError.new("unsupported proxy `#{proxy}'") + end + end + reset_all + @proxy + end + + def set_basic_auth(uri, user_id, passwd) + # net/http does not handle url. + @basic_auth = [user_id, passwd] + raise NotImplementedError.new("basic_auth is not supported under soap4r + net/http.") + end + + def set_cookie_store(filename) + raise NotImplementedError.new + end + + def save_cookie_store(filename) + raise NotImplementedError.new + end + + def reset(url) + # no persistent connection. ignored. + end + + def reset_all + # no persistent connection. ignored. + end + + def post(url, req_body, header = {}) + unless url.is_a?(URI) + url = URI.parse(url) + end + extra = header.dup + extra['User-Agent'] = @agent if @agent + res = start(url) { |http| + http.post(url.request_uri, req_body, extra) + } + Response.new(res) + end + + def get_content(url, header = {}) + unless url.is_a?(URI) + url = URI.parse(url) + end + extra = header.dup + extra['User-Agent'] = @agent if @agent + res = start(url) { |http| + http.get(url.request_uri, extra) + } + res.body + end + +private + + def start(url) + http = create_connection(url) + response = nil + http.start { |worker| + response = yield(worker) + worker.finish + } + @debug_dev << response.body if @debug_dev + response + end + + def create_connection(url) + proxy_host = proxy_port = nil + unless no_proxy?(url) + proxy_host = @proxy.host + proxy_port = @proxy.port + end + http = Net::HTTP::Proxy(proxy_host, proxy_port).new(url.host, url.port) + if http.respond_to?(:set_debug_output) + http.set_debug_output(@debug_dev) + end + http.open_timeout = @connect_timeout if @connect_timeout + http.read_timeout = @receive_timeout if @receive_timeout + case url + when URI::HTTPS + if SSLEnabled + http.use_ssl = true + else + raise RuntimeError.new("Cannot connect to #{url} (OpenSSL is not installed.)") + end + when URI::HTTP + # OK + else + raise RuntimeError.new("Cannot connect to #{url} (Not HTTP.)") + end + http + end + + NO_PROXY_HOSTS = ['localhost'] + + def no_proxy?(uri) + if !@proxy or NO_PROXY_HOSTS.include?(uri.host) + return true + end + if @no_proxy + @no_proxy.scan(/([^:,]*)(?::(\d+))?/) do |host, port| + if /(\A|\.)#{Regexp.quote(host)}\z/i =~ uri.host && + (!port || uri.port == port.to_i) + return true + end + end + else + false + end + end + + class SessionManager + attr_accessor :connect_timeout + attr_accessor :send_timeout + attr_accessor :receive_timeout + end + + class Response + attr_reader :content + attr_reader :status + attr_reader :reason + attr_reader :contenttype + + def initialize(res) + @status = res.code.to_i + @reason = res.message + @contenttype = res['content-type'] + @content = res.body + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/parser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/parser.rb new file mode 100644 index 0000000000..412fd4855a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/parser.rb @@ -0,0 +1,251 @@ +# SOAP4R - SOAP XML Instance Parser library. +# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/ns' +require 'xsd/xmlparser' +require 'soap/soap' +require 'soap/baseData' +require 'soap/encodingstyle/handler' + + +module SOAP + + +class Parser + include SOAP + + class ParseError < Error; end + class FormatDecodeError < ParseError; end + class UnexpectedElementError < ParseError; end + +private + + class ParseFrame + attr_reader :node + attr_reader :name + attr_reader :ns, :encodingstyle + + class NodeContainer + def initialize(node) + @node = node + end + + def node + @node + end + + def replace_node(node) + @node = node + end + end + + public + + def initialize(ns, name, node, encodingstyle) + @ns = ns + @name = name + self.node = node + @encodingstyle = encodingstyle + end + + def node=(node) + @node = NodeContainer.new(node) + end + end + +public + + attr_accessor :envelopenamespace + attr_accessor :default_encodingstyle + attr_accessor :decode_typemap + attr_accessor :allow_unqualified_element + + def initialize(opt = {}) + @opt = opt + @parser = XSD::XMLParser.create_parser(self, opt) + @parsestack = nil + @lastnode = nil + @handlers = {} + @envelopenamespace = opt[:envelopenamespace] || EnvelopeNamespace + @default_encodingstyle = opt[:default_encodingstyle] || EncodingNamespace + @decode_typemap = opt[:decode_typemap] || nil + @allow_unqualified_element = opt[:allow_unqualified_element] || false + end + + def charset + @parser.charset + end + + def parse(string_or_readable) + @parsestack = [] + @lastnode = nil + + @handlers.each do |uri, handler| + handler.decode_prologue + end + + @parser.do_parse(string_or_readable) + + unless @parsestack.empty? + raise FormatDecodeError.new("Unbalanced tag in XML.") + end + + @handlers.each do |uri, handler| + handler.decode_epilogue + end + + @lastnode + end + + def start_element(name, attrs) + lastframe = @parsestack.last + ns = parent = parent_encodingstyle = nil + if lastframe + ns = lastframe.ns.clone_ns + parent = lastframe.node + parent_encodingstyle = lastframe.encodingstyle + else + ns = XSD::NS.new + parent = ParseFrame::NodeContainer.new(nil) + parent_encodingstyle = nil + end + + attrs = XSD::XMLParser.filter_ns(ns, attrs) + encodingstyle = find_encodingstyle(ns, attrs) + + # Children's encodingstyle is derived from its parent. + if encodingstyle.nil? + if parent.node.is_a?(SOAPHeader) + encodingstyle = LiteralNamespace + else + encodingstyle = parent_encodingstyle || @default_encodingstyle + end + end + + node = decode_tag(ns, name, attrs, parent, encodingstyle) + + @parsestack << ParseFrame.new(ns, name, node, encodingstyle) + end + + def characters(text) + lastframe = @parsestack.last + if lastframe + # Need not to be cloned because character does not have attr. + decode_text(lastframe.ns, text, lastframe.encodingstyle) + else + # Ignore Text outside of SOAP Envelope. + p text if $DEBUG + end + end + + def end_element(name) + lastframe = @parsestack.pop + unless name == lastframe.name + raise UnexpectedElementError.new("Closing element name '#{ name }' does not match with opening element '#{ lastframe.name }'.") + end + decode_tag_end(lastframe.ns, lastframe.node, lastframe.encodingstyle) + @lastnode = lastframe.node.node + end + +private + + def find_encodingstyle(ns, attrs) + attrs.each do |key, value| + if (ns.compare(@envelopenamespace, AttrEncodingStyle, key)) + return value + end + end + nil + end + + def decode_tag(ns, name, attrs, parent, encodingstyle) + ele = ns.parse(name) + + # Envelope based parsing. + if ((ele.namespace == @envelopenamespace) || + (@allow_unqualified_element && ele.namespace.nil?)) + o = decode_soap_envelope(ns, ele, attrs, parent) + return o if o + end + + # Encoding based parsing. + handler = find_handler(encodingstyle) + if handler + return handler.decode_tag(ns, ele, attrs, parent) + else + raise FormatDecodeError.new("Unknown encodingStyle: #{ encodingstyle }.") + end + end + + def decode_tag_end(ns, node, encodingstyle) + return unless encodingstyle + + handler = find_handler(encodingstyle) + if handler + return handler.decode_tag_end(ns, node) + else + raise FormatDecodeError.new("Unknown encodingStyle: #{ encodingstyle }.") + end + end + + def decode_text(ns, text, encodingstyle) + handler = find_handler(encodingstyle) + + if handler + handler.decode_text(ns, text) + else + # How should I do? + end + end + + def decode_soap_envelope(ns, ele, attrs, parent) + o = nil + if ele.name == EleEnvelope + o = SOAPEnvelope.new + if ext = @opt[:external_content] + ext.each do |k, v| + o.external_content[k] = v + end + end + elsif ele.name == EleHeader + unless parent.node.is_a?(SOAPEnvelope) + raise FormatDecodeError.new("Header should be a child of Envelope.") + end + o = SOAPHeader.new + parent.node.header = o + elsif ele.name == EleBody + unless parent.node.is_a?(SOAPEnvelope) + raise FormatDecodeError.new("Body should be a child of Envelope.") + end + o = SOAPBody.new + parent.node.body = o + elsif ele.name == EleFault + unless parent.node.is_a?(SOAPBody) + raise FormatDecodeError.new("Fault should be a child of Body.") + end + o = SOAPFault.new + parent.node.fault = o + end + o + end + + def find_handler(encodingstyle) + unless @handlers.key?(encodingstyle) + handler_factory = SOAP::EncodingStyle::Handler.handler(encodingstyle) || + SOAP::EncodingStyle::Handler.handler(EncodingNamespace) + handler = handler_factory.new(@parser.charset) + handler.decode_typemap = @decode_typemap + handler.decode_prologue + @handlers[encodingstyle] = handler + end + @handlers[encodingstyle] + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/processor.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/processor.rb new file mode 100644 index 0000000000..3c6dbedf2f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/processor.rb @@ -0,0 +1,66 @@ +# SOAP4R - marshal/unmarshal interface. +# Copyright (C) 2000, 2001, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/datatypes' +require 'soap/soap' +require 'soap/element' +require 'soap/parser' +require 'soap/generator' +require 'soap/encodingstyle/soapHandler' +require 'soap/encodingstyle/literalHandler' +require 'soap/encodingstyle/aspDotNetHandler' + + +module SOAP + + +module Processor + @@default_parser_option = {} + + class << self + public + + def marshal(env, opt = {}, io = nil) + generator = create_generator(opt) + marshalled_str = generator.generate(env, io) + unless env.external_content.empty? + opt[:external_content] = env.external_content + end + marshalled_str + end + + def unmarshal(stream, opt = {}) + parser = create_parser(opt) + parser.parse(stream) + end + + def default_parser_option=(rhs) + @@default_parser_option = rhs + end + + def default_parser_option + @@default_parser_option + end + + private + + def create_generator(opt) + SOAPGenerator.new(opt) + end + + def create_parser(opt) + if opt.empty? + opt = @@default_parser_option + end + ::SOAP::Parser.new(opt) + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/property.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/property.rb new file mode 100644 index 0000000000..882dcc6e28 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/property.rb @@ -0,0 +1,333 @@ +# soap/property.rb: SOAP4R - Property implementation. +# Copyright (C) 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +module SOAP + + +# Property stream format: +# +# line separator is \r?\n. 1 line per a property. +# line which begins with '#' is a comment line. empty line is ignored, too. +# key/value separator is ':' or '='. +# '\' as escape character. but line separator cannot be escaped. +# \s at the head/tail of key/value are trimmed. +# +# '[' + key + ']' indicates property section. for example, +# +# [aaa.bbb] +# ccc = ddd +# eee.fff = ggg +# [] +# aaa.hhh = iii +# +# is the same as; +# +# aaa.bbb.ccc = ddd +# aaa.bbb.eee.fff = ggg +# aaa.hhh = iii +# +class Property + FrozenError = (RUBY_VERSION >= "1.9.0") ? RuntimeError : TypeError + + include Enumerable + + module Util + def const_from_name(fqname) + fqname.split("::").inject(Kernel) { |klass, name| klass.const_get(name) } + end + module_function :const_from_name + + def require_from_name(fqname) + require File.join(fqname.split("::").collect { |ele| ele.downcase }) + end + module_function :require_from_name + end + + def self.load(stream) + new.load(stream) + end + + def self.loadproperty(propname) + new.loadproperty(propname) + end + + def initialize + @store = Hash.new + @hook = Hash.new + @self_hook = Array.new + @locked = false + end + + KEY_REGSRC = '([^=:\\\\]*(?:\\\\.[^=:\\\\]*)*)' + DEF_REGSRC = '\\s*' + KEY_REGSRC + '\\s*[=:]\\s*(.*)' + COMMENT_REGEXP = Regexp.new('^(?:#.*|)$') + CATDEF_REGEXP = Regexp.new("^\\[\\s*#{KEY_REGSRC}\\s*\\]$") + LINE_REGEXP = Regexp.new("^#{DEF_REGSRC}$") + def load(stream) + key_prefix = "" + stream.each_with_index do |line, lineno| + line.sub!(/\r?\n\z/, '') + case line + when COMMENT_REGEXP + next + when CATDEF_REGEXP + key_prefix = $1.strip + when LINE_REGEXP + key, value = $1.strip, $2.strip + key = "#{key_prefix}.#{key}" unless key_prefix.empty? + key, value = loadstr(key), loadstr(value) + self[key] = value + else + raise TypeError.new( + "property format error at line #{lineno + 1}: `#{line}'") + end + end + self + end + + # find property from $:. + def loadproperty(propname) + return loadpropertyfile(propname) if File.file?(propname) + $:.each do |path| + if File.file?(file = File.join(path, propname)) + return loadpropertyfile(file) + end + end + nil + end + + # name: a Symbol, String or an Array + def [](name) + referent(name_to_a(name)) + end + + # name: a Symbol, String or an Array + # value: an Object + def []=(name, value) + name_pair = name_to_a(name).freeze + hooks = assign(name_pair, value) + hooks.each do |hook| + hook.call(name_pair, value) + end + value + end + + # value: an Object + # key is generated by property + def <<(value) + self[generate_new_key] = value + end + + # name: a Symbol, String or an Array; nil means hook to the root + # cascade: true/false; for cascading hook of sub key + # hook: block which will be called with 2 args, name and value + def add_hook(name = nil, cascade = false, &hook) + if name == nil or name == true or name == false + cascade = name + assign_self_hook(cascade, &hook) + else + assign_hook(name_to_a(name), cascade, &hook) + end + end + + def each + @store.each do |key, value| + yield(key, value) + end + end + + def empty? + @store.empty? + end + + def keys + @store.keys + end + + def values + @store.values + end + + def lock(cascade = false) + if cascade + each_key do |key| + key.lock(cascade) + end + end + @locked = true + self + end + + def unlock(cascade = false) + @locked = false + if cascade + each_key do |key| + key.unlock(cascade) + end + end + self + end + + def locked? + @locked + end + +protected + + def deref_key(key) + check_lock(key) + ref = @store[key] ||= self.class.new + unless propkey?(ref) + raise ArgumentError.new("key `#{key}' already defined as a value") + end + ref + end + + def local_referent(key) + check_lock(key) + if propkey?(@store[key]) and @store[key].locked? + raise FrozenError.new("cannot split any key from locked property") + end + @store[key] + end + + def local_assign(key, value) + check_lock(key) + if @locked + if propkey?(value) + raise FrozenError.new("cannot add any key to locked property") + elsif propkey?(@store[key]) + raise FrozenError.new("cannot override any key in locked property") + end + end + @store[key] = value + end + + def local_hook(key, direct) + hooks = [] + (@self_hook + (@hook[key] || NO_HOOK)).each do |hook, cascade| + hooks << hook if direct or cascade + end + hooks + end + + def local_assign_hook(key, cascade, &hook) + check_lock(key) + @store[key] ||= nil + (@hook[key] ||= []) << [hook, cascade] + end + +private + + NO_HOOK = [].freeze + + def referent(ary) + ary[0..-2].inject(self) { |ref, name| + ref.deref_key(to_key(name)) + }.local_referent(to_key(ary.last)) + end + + def assign(ary, value) + ref = self + hook = NO_HOOK + ary[0..-2].each do |name| + key = to_key(name) + hook += ref.local_hook(key, false) + ref = ref.deref_key(key) + end + last_key = to_key(ary.last) + ref.local_assign(last_key, value) + hook + ref.local_hook(last_key, true) + end + + def assign_hook(ary, cascade, &hook) + ary[0..-2].inject(self) { |ref, name| + ref.deref_key(to_key(name)) + }.local_assign_hook(to_key(ary.last), cascade, &hook) + end + + def assign_self_hook(cascade, &hook) + check_lock(nil) + @self_hook << [hook, cascade] + end + + def each_key + self.each do |key, value| + if propkey?(value) + yield(value) + end + end + end + + def check_lock(key) + if @locked and (key.nil? or !@store.key?(key)) + raise FrozenError.new("cannot add any key to locked property") + end + end + + def propkey?(value) + value.is_a?(::SOAP::Property) + end + + def name_to_a(name) + case name + when Symbol + [name] + when String + name.scan(/[^.\\]+(?:\\.[^.\\])*/) # split with unescaped '.' + when Array + name + else + raise ArgumentError.new("Unknown name #{name}(#{name.class})") + end + end + + def to_key(name) + name.to_s.downcase + end + + def generate_new_key + if @store.empty? + "0" + else + (key_max + 1).to_s + end + end + + def key_max + (@store.keys.max { |l, r| l.to_s.to_i <=> r.to_s.to_i }).to_s.to_i + end + + def loadpropertyfile(file) + puts "find property at #{file}" if $DEBUG + File.open(file) do |f| + load(f) + end + end + + def loadstr(str) + str.gsub(/\\./) { |c| eval("\"#{c}\"") } + end +end + + +end + + +# for ruby/1.6. +unless Enumerable.instance_methods.include?('inject') + module Enumerable + def inject(init) + result = init + each do |item| + result = yield(result, item) + end + result + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/cgistub.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/cgistub.rb new file mode 100644 index 0000000000..487f05a9bf --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/cgistub.rb @@ -0,0 +1,206 @@ +# SOAP4R - CGI/mod_ruby stub library +# Copyright (C) 2001, 2003-2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/streamHandler' +require 'webrick/httpresponse' +require 'webrick/httpstatus' +require 'logger' +require 'soap/rpc/soaplet' + + +module SOAP +module RPC + + +### +# SYNOPSIS +# CGIStub.new +# +# DESCRIPTION +# To be written... +# +class CGIStub < Logger::Application + include SOAP + include WEBrick + + class SOAPRequest + attr_reader :body + + def [](var); end + + def meta_vars; end + end + + class SOAPStdinRequest < SOAPRequest + attr_reader :body + + def initialize(stream) + size = ENV['CONTENT_LENGTH'].to_i || 0 + @body = stream.read(size) + end + + def [](var) + ENV[var.gsub(/-/, '_').upcase] + end + + def meta_vars + { + 'HTTP_SOAPACTION' => ENV['HTTP_SOAPAction'] + } + end + end + + class SOAPFCGIRequest < SOAPRequest + attr_reader :body + + def initialize(request) + @request = request + @body = @request.in.read + end + + def [](var) + @request.env[var.gsub(/-/, '_').upcase] + end + + def meta_vars + { + 'HTTP_SOAPACTION' => @request.env['HTTP_SOAPAction'] + } + end + end + + def initialize(appname, default_namespace) + super(appname) + set_log(STDERR) + self.level = ERROR + @default_namespace = default_namespace + @remote_host = ENV['REMOTE_HOST'] || ENV['REMOTE_ADDR'] || 'unknown' + @router = ::SOAP::RPC::Router.new(self.class.name) + @soaplet = ::SOAP::RPC::SOAPlet.new(@router) + on_init + end + + def on_init + # do extra initialization in a derived class if needed. + end + + def mapping_registry + @router.mapping_registry + end + + def mapping_registry=(value) + @router.mapping_registry = value + end + + def generate_explicit_type + @router.generate_explicit_type + end + + def generate_explicit_type=(generate_explicit_type) + @router.generate_explicit_type = generate_explicit_type + end + + # servant entry interface + + def add_rpc_servant(obj, namespace = @default_namespace) + @router.add_rpc_servant(obj, namespace) + end + alias add_servant add_rpc_servant + + def add_headerhandler(obj) + @router.add_headerhandler(obj) + end + alias add_rpc_headerhandler add_headerhandler + + # method entry interface + + def add_rpc_method(obj, name, *param) + add_rpc_method_with_namespace_as(@default_namespace, obj, name, name, *param) + end + alias add_method add_rpc_method + + def add_rpc_method_as(obj, name, name_as, *param) + add_rpc_method_with_namespace_as(@default_namespace, obj, name, name_as, *param) + end + alias add_method_as add_rpc_method_as + + def add_rpc_method_with_namespace(namespace, obj, name, *param) + add_rpc_method_with_namespace_as(namespace, obj, name, name, *param) + end + alias add_method_with_namespace add_rpc_method_with_namespace + + def add_rpc_method_with_namespace_as(namespace, obj, name, name_as, *param) + qname = XSD::QName.new(namespace, name_as) + soapaction = nil + param_def = SOAPMethod.derive_rpc_param_def(obj, name, *param) + @router.add_rpc_operation(obj, qname, soapaction, name, param_def) + end + alias add_method_with_namespace_as add_rpc_method_with_namespace_as + + def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {}) + @router.add_rpc_operation(receiver, qname, soapaction, name, param_def, opt) + end + + def add_document_operation(receiver, soapaction, name, param_def, opt = {}) + @router.add_document_operation(receiver, soapaction, name, param_def, opt) + end + + def set_fcgi_request(request) + @fcgi = request + end + +private + + HTTPVersion = WEBrick::HTTPVersion.new('1.0') # dummy; ignored + + def run + res = WEBrick::HTTPResponse.new({:HTTPVersion => HTTPVersion}) + begin + @log.info { "received a request from '#{ @remote_host }'" } + if @fcgi + req = SOAPFCGIRequest.new(@fcgi) + else + req = SOAPStdinRequest.new($stdin) + end + @soaplet.do_POST(req, res) + rescue HTTPStatus::EOFError, HTTPStatus::RequestTimeout => ex + res.set_error(ex) + rescue HTTPStatus::Error => ex + res.set_error(ex) + rescue HTTPStatus::Status => ex + res.status = ex.code + rescue StandardError, NameError => ex # for Ruby 1.6 + res.set_error(ex, true) + ensure + if defined?(MOD_RUBY) + r = Apache.request + r.status = res.status + r.content_type = res.content_type + r.send_http_header + buf = res.body + else + buf = '' + res.send_response(buf) + buf.sub!(/^[^\r]+\r\n/, '') # Trim status line. + end + @log.debug { "SOAP CGI Response:\n#{ buf }" } + if @fcgi + @fcgi.out.print buf + @fcgi.finish + @fcgi = nil + else + print buf + end + end + 0 + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/driver.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/driver.rb new file mode 100644 index 0000000000..0fb4e82488 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/driver.rb @@ -0,0 +1,254 @@ +# SOAP4R - SOAP RPC driver +# Copyright (C) 2000, 2001, 2003-2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/soap' +require 'soap/mapping' +require 'soap/mapping/wsdlliteralregistry' +require 'soap/rpc/rpc' +require 'soap/rpc/proxy' +require 'soap/rpc/element' +require 'soap/streamHandler' +require 'soap/property' +require 'soap/header/handlerset' + + +module SOAP +module RPC + + +class Driver + class << self + if RUBY_VERSION >= "1.7.0" + def __attr_proxy(symbol, assignable = false) + name = symbol.to_s + define_method(name) { + @proxy.__send__(name) + } + if assignable + aname = name + '=' + define_method(aname) { |rhs| + @proxy.__send__(aname, rhs) + } + end + end + else + def __attr_proxy(symbol, assignable = false) + name = symbol.to_s + module_eval <<-EOS + def #{name} + @proxy.#{name} + end + EOS + if assignable + module_eval <<-EOS + def #{name}=(value) + @proxy.#{name} = value + end + EOS + end + end + end + end + + __attr_proxy :endpoint_url, true + __attr_proxy :mapping_registry, true + __attr_proxy :default_encodingstyle, true + __attr_proxy :generate_explicit_type, true + __attr_proxy :allow_unqualified_element, true + __attr_proxy :headerhandler + __attr_proxy :streamhandler + __attr_proxy :test_loopback_response + __attr_proxy :reset_stream + + attr_reader :proxy + attr_reader :options + attr_accessor :soapaction + + def inspect + "#<#{self.class}:#{@proxy.inspect}>" + end + + def httpproxy + options["protocol.http.proxy"] + end + + def httpproxy=(httpproxy) + options["protocol.http.proxy"] = httpproxy + end + + def wiredump_dev + options["protocol.http.wiredump_dev"] + end + + def wiredump_dev=(wiredump_dev) + options["protocol.http.wiredump_dev"] = wiredump_dev + end + + def mandatorycharset + options["protocol.mandatorycharset"] + end + + def mandatorycharset=(mandatorycharset) + options["protocol.mandatorycharset"] = mandatorycharset + end + + def wiredump_file_base + options["protocol.wiredump_file_base"] + end + + def wiredump_file_base=(wiredump_file_base) + options["protocol.wiredump_file_base"] = wiredump_file_base + end + + def initialize(endpoint_url, namespace = nil, soapaction = nil) + @namespace = namespace + @soapaction = soapaction + @options = setup_options + @wiredump_file_base = nil + @proxy = Proxy.new(endpoint_url, @soapaction, @options) + end + + def loadproperty(propertyname) + unless options.loadproperty(propertyname) + raise LoadError.new("No such property to load -- #{propertyname}") + end + end + + def add_rpc_method(name, *params) + add_rpc_method_with_soapaction_as(name, name, @soapaction, *params) + end + + def add_rpc_method_as(name, name_as, *params) + add_rpc_method_with_soapaction_as(name, name_as, @soapaction, *params) + end + + def add_rpc_method_with_soapaction(name, soapaction, *params) + add_rpc_method_with_soapaction_as(name, name, soapaction, *params) + end + + def add_rpc_method_with_soapaction_as(name, name_as, soapaction, *params) + param_def = SOAPMethod.create_rpc_param_def(params) + qname = XSD::QName.new(@namespace, name_as) + @proxy.add_rpc_method(qname, soapaction, name, param_def) + add_rpc_method_interface(name, param_def) + end + + # add_method is for shortcut of typical rpc/encoded method definition. + alias add_method add_rpc_method + alias add_method_as add_rpc_method_as + alias add_method_with_soapaction add_rpc_method_with_soapaction + alias add_method_with_soapaction_as add_rpc_method_with_soapaction_as + + def add_document_method(name, soapaction, req_qname, res_qname) + param_def = SOAPMethod.create_doc_param_def(req_qname, res_qname) + @proxy.add_document_method(soapaction, name, param_def) + add_document_method_interface(name, param_def) + end + + def add_rpc_operation(qname, soapaction, name, param_def, opt = {}) + @proxy.add_rpc_operation(qname, soapaction, name, param_def, opt) + add_rpc_method_interface(name, param_def) + end + + def add_document_operation(soapaction, name, param_def, opt = {}) + @proxy.add_document_operation(soapaction, name, param_def, opt) + add_document_method_interface(name, param_def) + end + + def invoke(headers, body) + if headers and !headers.is_a?(SOAPHeader) + headers = create_header(headers) + end + set_wiredump_file_base(body.elename.name) + env = @proxy.invoke(headers, body) + if env.nil? + return nil, nil + else + return env.header, env.body + end + end + + def call(name, *params) + set_wiredump_file_base(name) + @proxy.call(name, *params) + end + +private + + def set_wiredump_file_base(name) + if @wiredump_file_base + @proxy.set_wiredump_file_base("#{@wiredump_file_base}_#{name}") + end + end + + def create_header(headers) + header = SOAPHeader.new() + headers.each do |content, mustunderstand, encodingstyle| + header.add(SOAPHeaderItem.new(content, mustunderstand, encodingstyle)) + end + header + end + + def setup_options + if opt = Property.loadproperty(::SOAP::PropertyName) + opt = opt["client"] + end + opt ||= Property.new + opt.add_hook("protocol.mandatorycharset") do |key, value| + @proxy.mandatorycharset = value + end + opt.add_hook("protocol.wiredump_file_base") do |key, value| + @wiredump_file_base = value + end + opt["protocol.http.charset"] ||= XSD::Charset.xml_encoding_label + opt["protocol.http.proxy"] ||= Env::HTTP_PROXY + opt["protocol.http.no_proxy"] ||= Env::NO_PROXY + opt + end + + def add_rpc_method_interface(name, param_def) + param_count = RPC::SOAPMethod.param_count(param_def, + RPC::SOAPMethod::IN, RPC::SOAPMethod::INOUT) + add_method_interface(name, param_count) + end + + def add_document_method_interface(name, param_def) + param_count = RPC::SOAPMethod.param_count(param_def, RPC::SOAPMethod::IN) + add_method_interface(name, param_count) + end + + if RUBY_VERSION > "1.7.0" + def add_method_interface(name, param_count) + ::SOAP::Mapping.define_singleton_method(self, name) do |*arg| + unless arg.size == param_count + raise ArgumentError.new( + "wrong number of arguments (#{arg.size} for #{param_count})") + end + call(name, *arg) + end + self.method(name) + end + else + def add_method_interface(name, param_count) + instance_eval <<-EOS + def #{name}(*arg) + unless arg.size == #{param_count} + raise ArgumentError.new( + "wrong number of arguments (\#{arg.size} for #{param_count})") + end + call(#{name.dump}, *arg) + end + EOS + self.method(name) + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/element.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/element.rb new file mode 100644 index 0000000000..c224b03d0d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/element.rb @@ -0,0 +1,325 @@ +# SOAP4R - RPC element definition. +# Copyright (C) 2000, 2001, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/baseData' + + +module SOAP + +# Add method definitions for RPC to common definition in element.rb +class SOAPBody < SOAPStruct + public + + def request + root_node + end + + def response + root = root_node + if !@is_fault + if root.nil? + nil + elsif root.is_a?(SOAPBasetype) + root + else + # Initial element is [retval]. + root[0] + end + else + root + end + end + + def outparams + root = root_node + if !@is_fault and !root.nil? and !root.is_a?(SOAPBasetype) + op = root[1..-1] + op = nil if op && op.empty? + op + else + nil + end + end + + def fault + if @is_fault + self['fault'] + else + nil + end + end + + def fault=(fault) + @is_fault = true + add_member('fault', fault) + end +end + + +module RPC + + +class RPCError < Error; end +class MethodDefinitionError < RPCError; end +class ParameterError < RPCError; end + +class SOAPMethod < SOAPStruct + RETVAL = 'retval' + IN = 'in' + OUT = 'out' + INOUT = 'inout' + + attr_reader :param_def + attr_reader :inparam + attr_reader :outparam + attr_reader :retval_name + attr_reader :retval_class_name + + def initialize(qname, param_def = nil) + super(nil) + @elename = qname + @encodingstyle = nil + + @param_def = param_def + + @signature = [] + @inparam_names = [] + @inoutparam_names = [] + @outparam_names = [] + + @inparam = {} + @outparam = {} + @retval_name = nil + @retval_class_name = nil + + init_param(@param_def) if @param_def + end + + def have_outparam? + @outparam_names.size > 0 + end + + def input_params + collect_params(IN, INOUT) + end + + def output_params + collect_params(OUT, INOUT) + end + + def set_param(params) + params.each do |param, data| + @inparam[param] = data + data.elename.name = param + data.parent = self + end + end + + def set_outparam(params) + params.each do |param, data| + @outparam[param] = data + data.elename.name = param + end + end + + def SOAPMethod.param_count(param_def, *type) + count = 0 + param_def.each do |io_type, name, param_type| + if type.include?(io_type) + count += 1 + end + end + count + end + + def SOAPMethod.derive_rpc_param_def(obj, name, *param) + if param.size == 1 and param[0].is_a?(Array) + return param[0] + end + if param.empty? + method = obj.method(name) + param_names = (1..method.arity.abs).collect { |i| "p#{i}" } + else + param_names = param + end + create_rpc_param_def(param_names) + end + + def SOAPMethod.create_rpc_param_def(param_names) + param_def = [] + param_names.each do |param_name| + param_def.push([IN, param_name, nil]) + end + param_def.push([RETVAL, 'return', nil]) + param_def + end + + def SOAPMethod.create_doc_param_def(req_qnames, res_qnames) + req_qnames = [req_qnames] if req_qnames.is_a?(XSD::QName) + res_qnames = [res_qnames] if res_qnames.is_a?(XSD::QName) + param_def = [] + req_qnames.each do |qname| + param_def << [IN, qname.name, [nil, qname.namespace, qname.name]] + end + res_qnames.each do |qname| + param_def << [OUT, qname.name, [nil, qname.namespace, qname.name]] + end + param_def + end + +private + + def collect_params(*type) + names = [] + @signature.each do |io_type, name, param_type| + names << name if type.include?(io_type) + end + names + end + + def init_param(param_def) + param_def.each do |io_type, name, param_type| + case io_type + when IN + @signature.push([IN, name, param_type]) + @inparam_names.push(name) + when OUT + @signature.push([OUT, name, param_type]) + @outparam_names.push(name) + when INOUT + @signature.push([INOUT, name, param_type]) + @inoutparam_names.push(name) + when RETVAL + if @retval_name + raise MethodDefinitionError.new('duplicated retval') + end + @retval_name = name + @retval_class_name = nil + if param_type + if param_type[0].is_a?(String) + @retval_class_name = Mapping.class_from_name(param_type[0]) + else + @retval_class_name = param_type[0] + end + end + else + raise MethodDefinitionError.new("unknown type: #{io_type}") + end + end + end +end + + +class SOAPMethodRequest < SOAPMethod + attr_accessor :soapaction + + def SOAPMethodRequest.create_request(qname, *params) + param_def = [] + param_value = [] + i = 0 + params.each do |param| + param_name = "p#{i}" + i += 1 + param_def << [IN, param_name, nil] + param_value << [param_name, param] + end + param_def << [RETVAL, 'return', nil] + o = new(qname, param_def) + o.set_param(param_value) + o + end + + def initialize(qname, param_def = nil, soapaction = nil) + check_elename(qname) + super(qname, param_def) + @soapaction = soapaction + end + + def each + input_params.each do |name| + unless @inparam[name] + raise ParameterError.new("parameter: #{name} was not given") + end + yield(name, @inparam[name]) + end + end + + def dup + req = self.class.new(@elename.dup, @param_def, @soapaction) + req.encodingstyle = @encodingstyle + req + end + + def create_method_response(response_name = nil) + response_name ||= + XSD::QName.new(@elename.namespace, @elename.name + 'Response') + SOAPMethodResponse.new(response_name, @param_def) + end + +private + + def check_elename(qname) + # NCName & ruby's method name + unless /\A[\w_][\w\d_\-]*\z/ =~ qname.name + raise MethodDefinitionError.new("element name '#{qname.name}' not allowed") + end + end +end + + +class SOAPMethodResponse < SOAPMethod + + def initialize(qname, param_def = nil) + super(qname, param_def) + @retval = nil + end + + def retval=(retval) + @retval = retval + @retval.elename = @retval.elename.dup_name(@retval_name || 'return') + retval.parent = self + retval + end + + def each + if @retval_name and !@retval.is_a?(SOAPVoid) + yield(@retval_name, @retval) + end + + output_params.each do |name| + unless @outparam[name] + raise ParameterError.new("parameter: #{name} was not given") + end + yield(name, @outparam[name]) + end + end +end + + +# To return(?) void explicitly. +# def foo(input_var) +# ... +# return SOAP::RPC::SOAPVoid.new +# end +class SOAPVoid < XSD::XSDAnySimpleType + include SOAPBasetype + extend SOAPModuleUtils + Name = XSD::QName.new(Mapping::RubyCustomTypeNamespace, nil) + +public + def initialize() + @elename = Name + @id = nil + @precedents = [] + @parent = nil + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/httpserver.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/httpserver.rb new file mode 100644 index 0000000000..6d2a72ebe3 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/httpserver.rb @@ -0,0 +1,129 @@ +# SOAP4R - WEBrick HTTP Server +# Copyright (C) 2003, 2004 by NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'logger' +require 'soap/rpc/soaplet' +require 'soap/streamHandler' +require 'webrick' + + +module SOAP +module RPC + + +class HTTPServer < Logger::Application + attr_reader :server + attr_accessor :default_namespace + + def initialize(config) + super(config[:SOAPHTTPServerApplicationName] || self.class.name) + @default_namespace = config[:SOAPDefaultNamespace] + @webrick_config = config.dup + self.level = Logger::Severity::ERROR # keep silent by default + @webrick_config[:Logger] ||= @log + @log = @webrick_config[:Logger] # sync logger of App and HTTPServer + @router = ::SOAP::RPC::Router.new(self.class.name) + @soaplet = ::SOAP::RPC::SOAPlet.new(@router) + on_init + @server = WEBrick::HTTPServer.new(@webrick_config) + @server.mount('/', @soaplet) + end + + def on_init + # do extra initialization in a derived class if needed. + end + + def status + @server.status if @server + end + + def shutdown + @server.shutdown if @server + end + + def mapping_registry + @router.mapping_registry + end + + def mapping_registry=(mapping_registry) + @router.mapping_registry = mapping_registry + end + + def generate_explicit_type + @router.generate_explicit_type + end + + def generate_explicit_type=(generate_explicit_type) + @router.generate_explicit_type = generate_explicit_type + end + + # servant entry interface + + def add_rpc_request_servant(factory, namespace = @default_namespace) + @router.add_rpc_request_servant(factory, namespace) + end + + def add_rpc_servant(obj, namespace = @default_namespace) + @router.add_rpc_servant(obj, namespace) + end + + def add_request_headerhandler(factory) + @router.add_request_headerhandler(factory) + end + + def add_headerhandler(obj) + @router.add_headerhandler(obj) + end + alias add_rpc_headerhandler add_headerhandler + + # method entry interface + + def add_rpc_method(obj, name, *param) + add_rpc_method_as(obj, name, name, *param) + end + alias add_method add_rpc_method + + def add_rpc_method_as(obj, name, name_as, *param) + qname = XSD::QName.new(@default_namespace, name_as) + soapaction = nil + param_def = SOAPMethod.derive_rpc_param_def(obj, name, *param) + @router.add_rpc_operation(obj, qname, soapaction, name, param_def) + end + alias add_method_as add_rpc_method_as + + def add_document_method(obj, soapaction, name, req_qnames, res_qnames) + param_def = SOAPMethod.create_doc_param_def(req_qnames, res_qnames) + @router.add_document_operation(obj, soapaction, name, param_def) + end + + def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {}) + @router.add_rpc_operation(receiver, qname, soapaction, name, param_def, opt) + end + + def add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt = {}) + @router.add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt) + end + + def add_document_operation(receiver, soapaction, name, param_def, opt = {}) + @router.add_document_operation(receiver, soapaction, name, param_def, opt) + end + + def add_document_request_operation(factory, soapaction, name, param_def, opt = {}) + @router.add_document_request_operation(factory, soapaction, name, param_def, opt) + end + +private + + def run + @server.start + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/proxy.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/proxy.rb new file mode 100644 index 0000000000..7dfda62006 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/proxy.rb @@ -0,0 +1,497 @@ +# SOAP4R - RPC Proxy library. +# Copyright (C) 2000, 2003-2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/soap' +require 'soap/processor' +require 'soap/mapping' +require 'soap/rpc/rpc' +require 'soap/rpc/element' +require 'soap/streamHandler' +require 'soap/mimemessage' + + +module SOAP +module RPC + + +class Proxy + include SOAP + +public + + attr_accessor :soapaction + attr_accessor :mandatorycharset + attr_accessor :allow_unqualified_element + attr_accessor :default_encodingstyle + attr_accessor :generate_explicit_type + attr_reader :headerhandler + attr_reader :streamhandler + + attr_accessor :mapping_registry + attr_accessor :literal_mapping_registry + + attr_reader :operation + + def initialize(endpoint_url, soapaction, options) + @endpoint_url = endpoint_url + @soapaction = soapaction + @options = options + @streamhandler = HTTPStreamHandler.new( + @options["protocol.http"] ||= ::SOAP::Property.new) + @operation = {} + @mandatorycharset = nil + @allow_unqualified_element = true + @default_encodingstyle = nil + @generate_explicit_type = true + @headerhandler = Header::HandlerSet.new + @mapping_registry = nil + @literal_mapping_registry = ::SOAP::Mapping::WSDLLiteralRegistry.new + end + + def inspect + "#<#{self.class}:#{@endpoint_url}>" + end + + def endpoint_url + @endpoint_url + end + + def endpoint_url=(endpoint_url) + @endpoint_url = endpoint_url + reset_stream + end + + def reset_stream + @streamhandler.reset(@endpoint_url) + end + + def set_wiredump_file_base(wiredump_file_base) + @streamhandler.wiredump_file_base = wiredump_file_base + end + + def test_loopback_response + @streamhandler.test_loopback_response + end + + def add_rpc_operation(qname, soapaction, name, param_def, opt = {}) + opt[:request_qname] = qname + opt[:request_style] ||= :rpc + opt[:response_style] ||= :rpc + opt[:request_use] ||= :encoded + opt[:response_use] ||= :encoded + @operation[name] = Operation.new(soapaction, param_def, opt) + end + + def add_document_operation(soapaction, name, param_def, opt = {}) + opt[:request_style] ||= :document + opt[:response_style] ||= :document + opt[:request_use] ||= :literal + opt[:response_use] ||= :literal + # default values of these values are unqualified in XML Schema. + # set true for backward compatibility. + unless opt.key?(:elementformdefault) + opt[:elementformdefault] = true + end + unless opt.key?(:attributeformdefault) + opt[:attributeformdefault] = true + end + @operation[name] = Operation.new(soapaction, param_def, opt) + end + + # add_method is for shortcut of typical rpc/encoded method definition. + alias add_method add_rpc_operation + alias add_rpc_method add_rpc_operation + alias add_document_method add_document_operation + + def invoke(req_header, req_body, opt = nil) + opt ||= create_encoding_opt + route(req_header, req_body, opt, opt) + end + + def call(name, *params) + unless op_info = @operation[name] + raise MethodDefinitionError, "method: #{name} not defined" + end + mapping_opt = create_mapping_opt + req_header = create_request_header + req_body = SOAPBody.new( + op_info.request_body(params, @mapping_registry, + @literal_mapping_registry, mapping_opt) + ) + reqopt = create_encoding_opt( + :soapaction => op_info.soapaction || @soapaction, + :envelopenamespace => @options["soap.envelope.requestnamespace"], + :default_encodingstyle => + @default_encodingstyle || op_info.request_default_encodingstyle, + :elementformdefault => op_info.elementformdefault, + :attributeformdefault => op_info.attributeformdefault + ) + resopt = create_encoding_opt( + :envelopenamespace => @options["soap.envelope.responsenamespace"], + :default_encodingstyle => + @default_encodingstyle || op_info.response_default_encodingstyle, + :elementformdefault => op_info.elementformdefault, + :attributeformdefault => op_info.attributeformdefault + ) + env = route(req_header, req_body, reqopt, resopt) + raise EmptyResponseError unless env + receive_headers(env.header) + begin + check_fault(env.body) + rescue ::SOAP::FaultError => e + op_info.raise_fault(e, @mapping_registry, @literal_mapping_registry) + end + op_info.response_obj(env.body, @mapping_registry, + @literal_mapping_registry, mapping_opt) + end + + def route(req_header, req_body, reqopt, resopt) + req_env = ::SOAP::SOAPEnvelope.new(req_header, req_body) + unless reqopt[:envelopenamespace].nil? + set_envelopenamespace(req_env, reqopt[:envelopenamespace]) + end + reqopt[:external_content] = nil + conn_data = marshal(req_env, reqopt) + if ext = reqopt[:external_content] + mime = MIMEMessage.new + ext.each do |k, v| + mime.add_attachment(v.data) + end + mime.add_part(conn_data.send_string + "\r\n") + mime.close + conn_data.send_string = mime.content_str + conn_data.send_contenttype = mime.headers['content-type'].str + end + conn_data = @streamhandler.send(@endpoint_url, conn_data, + reqopt[:soapaction]) + if conn_data.receive_string.empty? + return nil + end + unmarshal(conn_data, resopt) + end + + def check_fault(body) + if body.fault + raise SOAP::FaultError.new(body.fault) + end + end + +private + + def set_envelopenamespace(env, namespace) + env.elename = XSD::QName.new(namespace, env.elename.name) + if env.header + env.header.elename = XSD::QName.new(namespace, env.header.elename.name) + end + if env.body + env.body.elename = XSD::QName.new(namespace, env.body.elename.name) + end + end + + def create_request_header + headers = @headerhandler.on_outbound + if headers.empty? + nil + else + h = ::SOAP::SOAPHeader.new + headers.each do |header| + h.add(header.elename.name, header) + end + h + end + end + + def receive_headers(headers) + @headerhandler.on_inbound(headers) if headers + end + + def marshal(env, opt) + send_string = Processor.marshal(env, opt) + StreamHandler::ConnectionData.new(send_string) + end + + def unmarshal(conn_data, opt) + contenttype = conn_data.receive_contenttype + if /#{MIMEMessage::MultipartContentType}/i =~ contenttype + opt[:external_content] = {} + mime = MIMEMessage.parse("Content-Type: " + contenttype, + conn_data.receive_string) + mime.parts.each do |part| + value = Attachment.new(part.content) + value.contentid = part.contentid + obj = SOAPAttachment.new(value) + opt[:external_content][value.contentid] = obj if value.contentid + end + opt[:charset] = @mandatorycharset || + StreamHandler.parse_media_type(mime.root.headers['content-type'].str) + env = Processor.unmarshal(mime.root.content, opt) + else + opt[:charset] = @mandatorycharset || + ::SOAP::StreamHandler.parse_media_type(contenttype) + env = Processor.unmarshal(conn_data.receive_string, opt) + end + unless env.is_a?(::SOAP::SOAPEnvelope) + raise ResponseFormatError.new( + "response is not a SOAP envelope: #{conn_data.receive_string}") + end + env + end + + def create_header(headers) + header = SOAPHeader.new() + headers.each do |content, mustunderstand, encodingstyle| + header.add(SOAPHeaderItem.new(content, mustunderstand, encodingstyle)) + end + header + end + + def create_encoding_opt(hash = nil) + opt = {} + opt[:default_encodingstyle] = @default_encodingstyle + opt[:allow_unqualified_element] = @allow_unqualified_element + opt[:generate_explicit_type] = @generate_explicit_type + opt[:no_indent] = @options["soap.envelope.no_indent"] + opt[:use_numeric_character_reference] = + @options["soap.envelope.use_numeric_character_reference"] + opt.update(hash) if hash + opt + end + + def create_mapping_opt(hash = nil) + opt = { + :external_ces => @options["soap.mapping.external_ces"] + } + opt.update(hash) if hash + opt + end + + class Operation + attr_reader :soapaction + attr_reader :request_style + attr_reader :response_style + attr_reader :request_use + attr_reader :response_use + attr_reader :elementformdefault + attr_reader :attributeformdefault + + def initialize(soapaction, param_def, opt) + @soapaction = soapaction + @request_style = opt[:request_style] + @response_style = opt[:response_style] + @request_use = opt[:request_use] + @response_use = opt[:response_use] + # set nil(unqualified) by default + @elementformdefault = opt[:elementformdefault] + @attributeformdefault = opt[:attributeformdefault] + check_style(@request_style) + check_style(@response_style) + check_use(@request_use) + check_use(@response_use) + if @request_style == :rpc + @rpc_request_qname = opt[:request_qname] + if @rpc_request_qname.nil? + raise MethodDefinitionError.new("rpc_request_qname must be given") + end + @rpc_method_factory = + RPC::SOAPMethodRequest.new(@rpc_request_qname, param_def, @soapaction) + else + @doc_request_qnames = [] + @doc_request_qualified = [] + @doc_response_qnames = [] + @doc_response_qualified = [] + param_def.each do |inout, paramname, typeinfo, eleinfo| + klass_not_used, nsdef, namedef = typeinfo + qualified = eleinfo + if namedef.nil? + raise MethodDefinitionError.new("qname must be given") + end + case inout + when SOAPMethod::IN + @doc_request_qnames << XSD::QName.new(nsdef, namedef) + @doc_request_qualified << qualified + when SOAPMethod::OUT + @doc_response_qnames << XSD::QName.new(nsdef, namedef) + @doc_response_qualified << qualified + else + raise MethodDefinitionError.new( + "illegal inout definition for document style: #{inout}") + end + end + end + end + + def request_default_encodingstyle + (@request_use == :encoded) ? EncodingNamespace : LiteralNamespace + end + + def response_default_encodingstyle + (@response_use == :encoded) ? EncodingNamespace : LiteralNamespace + end + + def request_body(values, mapping_registry, literal_mapping_registry, opt) + if @request_style == :rpc + request_rpc(values, mapping_registry, literal_mapping_registry, opt) + else + request_doc(values, mapping_registry, literal_mapping_registry, opt) + end + end + + def response_obj(body, mapping_registry, literal_mapping_registry, opt) + if @response_style == :rpc + response_rpc(body, mapping_registry, literal_mapping_registry, opt) + else + response_doc(body, mapping_registry, literal_mapping_registry, opt) + end + end + + def raise_fault(e, mapping_registry, literal_mapping_registry) + if @response_style == :rpc + Mapping.fault2exception(e, mapping_registry) + else + Mapping.fault2exception(e, literal_mapping_registry) + end + end + + private + + def check_style(style) + unless [:rpc, :document].include?(style) + raise MethodDefinitionError.new("unknown style: #{style}") + end + end + + def check_use(use) + unless [:encoded, :literal].include?(use) + raise MethodDefinitionError.new("unknown use: #{use}") + end + end + + def request_rpc(values, mapping_registry, literal_mapping_registry, opt) + if @request_use == :encoded + request_rpc_enc(values, mapping_registry, opt) + else + request_rpc_lit(values, literal_mapping_registry, opt) + end + end + + def request_doc(values, mapping_registry, literal_mapping_registry, opt) + if @request_use == :encoded + request_doc_enc(values, mapping_registry, opt) + else + request_doc_lit(values, literal_mapping_registry, opt) + end + end + + def request_rpc_enc(values, mapping_registry, opt) + method = @rpc_method_factory.dup + names = method.input_params + obj = create_request_obj(names, values) + soap = Mapping.obj2soap(obj, mapping_registry, @rpc_request_qname, opt) + method.set_param(soap) + method + end + + def request_rpc_lit(values, mapping_registry, opt) + method = @rpc_method_factory.dup + params = {} + idx = 0 + method.input_params.each do |name| + params[name] = Mapping.obj2soap(values[idx], mapping_registry, + XSD::QName.new(nil, name), opt) + idx += 1 + end + method.set_param(params) + method + end + + def request_doc_enc(values, mapping_registry, opt) + (0...values.size).collect { |idx| + ele = Mapping.obj2soap(values[idx], mapping_registry, nil, opt) + ele.elename = @doc_request_qnames[idx] + ele + } + end + + def request_doc_lit(values, mapping_registry, opt) + (0...values.size).collect { |idx| + ele = Mapping.obj2soap(values[idx], mapping_registry, + @doc_request_qnames[idx], opt) + ele.encodingstyle = LiteralNamespace + if ele.respond_to?(:qualified) + ele.qualified = @doc_request_qualified[idx] + end + ele + } + end + + def response_rpc(body, mapping_registry, literal_mapping_registry, opt) + if @response_use == :encoded + response_rpc_enc(body, mapping_registry, opt) + else + response_rpc_lit(body, literal_mapping_registry, opt) + end + end + + def response_doc(body, mapping_registry, literal_mapping_registry, opt) + if @response_use == :encoded + return *response_doc_enc(body, mapping_registry, opt) + else + return *response_doc_lit(body, literal_mapping_registry, opt) + end + end + + def response_rpc_enc(body, mapping_registry, opt) + ret = nil + if body.response + ret = Mapping.soap2obj(body.response, mapping_registry, + @rpc_method_factory.retval_class_name, opt) + end + if body.outparams + outparams = body.outparams.collect { |outparam| + Mapping.soap2obj(outparam, mapping_registry, nil, opt) + } + [ret].concat(outparams) + else + ret + end + end + + def response_rpc_lit(body, mapping_registry, opt) + body.root_node.collect { |key, value| + Mapping.soap2obj(value, mapping_registry, + @rpc_method_factory.retval_class_name, opt) + } + end + + def response_doc_enc(body, mapping_registry, opt) + body.collect { |key, value| + Mapping.soap2obj(value, mapping_registry, nil, opt) + } + end + + def response_doc_lit(body, mapping_registry, opt) + body.collect { |key, value| + Mapping.soap2obj(value, mapping_registry) + } + end + + def create_request_obj(names, params) + o = Object.new + idx = 0 + while idx < params.length + o.instance_variable_set('@' + names[idx], params[idx]) + idx += 1 + end + o + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/router.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/router.rb new file mode 100644 index 0000000000..71c6eb625b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/router.rb @@ -0,0 +1,594 @@ +# SOAP4R - RPC Routing library +# Copyright (C) 2001, 2002, 2004, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/soap' +require 'soap/processor' +require 'soap/mapping' +require 'soap/mapping/wsdlliteralregistry' +require 'soap/rpc/rpc' +require 'soap/rpc/element' +require 'soap/streamHandler' +require 'soap/mimemessage' +require 'soap/header/handlerset' + + +module SOAP +module RPC + + +class Router + include SOAP + + attr_reader :actor + attr_accessor :mapping_registry + attr_accessor :literal_mapping_registry + attr_accessor :generate_explicit_type + attr_accessor :external_ces + + def initialize(actor) + @actor = actor + @mapping_registry = nil + @headerhandler = Header::HandlerSet.new + @literal_mapping_registry = ::SOAP::Mapping::WSDLLiteralRegistry.new + @generate_explicit_type = true + @external_ces = nil + @operation_by_soapaction = {} + @operation_by_qname = {} + @headerhandlerfactory = [] + end + + ### + ## header handler interface + # + def add_request_headerhandler(factory) + unless factory.respond_to?(:create) + raise TypeError.new("factory must respond to 'create'") + end + @headerhandlerfactory << factory + end + + def add_headerhandler(handler) + @headerhandler.add(handler) + end + + ### + ## servant definition interface + # + def add_rpc_request_servant(factory, namespace) + unless factory.respond_to?(:create) + raise TypeError.new("factory must respond to 'create'") + end + obj = factory.create # a dummy instance for introspection + ::SOAP::RPC.defined_methods(obj).each do |name| + begin + qname = XSD::QName.new(namespace, name) + param_def = ::SOAP::RPC::SOAPMethod.derive_rpc_param_def(obj, name) + opt = create_styleuse_option(:rpc, :encoded) + add_rpc_request_operation(factory, qname, nil, name, param_def, opt) + rescue SOAP::RPC::MethodDefinitionError => e + p e if $DEBUG + end + end + end + + def add_rpc_servant(obj, namespace) + ::SOAP::RPC.defined_methods(obj).each do |name| + begin + qname = XSD::QName.new(namespace, name) + param_def = ::SOAP::RPC::SOAPMethod.derive_rpc_param_def(obj, name) + opt = create_styleuse_option(:rpc, :encoded) + add_rpc_operation(obj, qname, nil, name, param_def, opt) + rescue SOAP::RPC::MethodDefinitionError => e + p e if $DEBUG + end + end + end + alias add_servant add_rpc_servant + + ### + ## operation definition interface + # + def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {}) + ensure_styleuse_option(opt, :rpc, :encoded) + opt[:request_qname] = qname + op = ApplicationScopeOperation.new(soapaction, receiver, name, param_def, + opt) + if opt[:request_style] != :rpc + raise RPCRoutingError.new("illegal request_style given") + end + assign_operation(soapaction, qname, op) + end + alias add_method add_rpc_operation + alias add_rpc_method add_rpc_operation + + def add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt = {}) + ensure_styleuse_option(opt, :rpc, :encoded) + opt[:request_qname] = qname + op = RequestScopeOperation.new(soapaction, factory, name, param_def, opt) + if opt[:request_style] != :rpc + raise RPCRoutingError.new("illegal request_style given") + end + assign_operation(soapaction, qname, op) + end + + def add_document_operation(receiver, soapaction, name, param_def, opt = {}) + # + # adopt workaround for doc/lit wrapper method + # (you should consider to simply use rpc/lit service) + # + #unless soapaction + # raise RPCRoutingError.new("soapaction is a must for document method") + #end + ensure_styleuse_option(opt, :document, :literal) + op = ApplicationScopeOperation.new(soapaction, receiver, name, param_def, + opt) + if opt[:request_style] != :document + raise RPCRoutingError.new("illegal request_style given") + end + assign_operation(soapaction, first_input_part_qname(param_def), op) + end + alias add_document_method add_document_operation + + def add_document_request_operation(factory, soapaction, name, param_def, opt = {}) + # + # adopt workaround for doc/lit wrapper method + # (you should consider to simply use rpc/lit service) + # + #unless soapaction + # raise RPCRoutingError.new("soapaction is a must for document method") + #end + ensure_styleuse_option(opt, :document, :literal) + op = RequestScopeOperation.new(soapaction, receiver, name, param_def, opt) + if opt[:request_style] != :document + raise RPCRoutingError.new("illegal request_style given") + end + assign_operation(soapaction, first_input_part_qname(param_def), op) + end + + def route(conn_data) + # we cannot set request_default_encodingsyle before parsing the content. + env = unmarshal(conn_data) + if env.nil? + raise ArgumentError.new("illegal SOAP marshal format") + end + op = lookup_operation(conn_data.soapaction, env.body) + headerhandler = @headerhandler.dup + @headerhandlerfactory.each do |f| + headerhandler.add(f.create) + end + receive_headers(headerhandler, env.header) + soap_response = default_encodingstyle = nil + begin + soap_response = + op.call(env.body, @mapping_registry, @literal_mapping_registry, + create_mapping_opt) + default_encodingstyle = op.response_default_encodingstyle + rescue Exception + soap_response = fault($!) + default_encodingstyle = nil + end + conn_data.is_fault = true if soap_response.is_a?(SOAPFault) + header = call_headers(headerhandler) + body = SOAPBody.new(soap_response) + env = SOAPEnvelope.new(header, body) + marshal(conn_data, env, default_encodingstyle) + end + + # Create fault response string. + def create_fault_response(e) + env = SOAPEnvelope.new(SOAPHeader.new, SOAPBody.new(fault(e))) + opt = {} + opt[:external_content] = nil + response_string = Processor.marshal(env, opt) + conn_data = StreamHandler::ConnectionData.new(response_string) + conn_data.is_fault = true + if ext = opt[:external_content] + mimeize(conn_data, ext) + end + conn_data + end + +private + + def first_input_part_qname(param_def) + param_def.each do |inout, paramname, typeinfo| + if inout == SOAPMethod::IN + klass, nsdef, namedef = typeinfo + return XSD::QName.new(nsdef, namedef) + end + end + nil + end + + def create_styleuse_option(style, use) + opt = {} + opt[:request_style] = opt[:response_style] = style + opt[:request_use] = opt[:response_use] = use + opt + end + + def ensure_styleuse_option(opt, style, use) + opt[:request_style] ||= style + opt[:response_style] ||= style + opt[:request_use] ||= use + opt[:response_use] ||= use + end + + def assign_operation(soapaction, qname, op) + assigned = false + if soapaction and !soapaction.empty? + @operation_by_soapaction[soapaction] = op + assigned = true + end + if qname + @operation_by_qname[qname] = op + assigned = true + end + unless assigned + raise RPCRoutingError.new("cannot assign operation") + end + end + + def lookup_operation(soapaction, body) + if op = @operation_by_soapaction[soapaction] + return op + end + qname = body.root_node.elename + if op = @operation_by_qname[qname] + return op + end + if soapaction + raise RPCRoutingError.new( + "operation: #{soapaction} #{qname} not supported") + else + raise RPCRoutingError.new("operation: #{qname} not supported") + end + end + + def call_headers(headerhandler) + headers = headerhandler.on_outbound + if headers.empty? + nil + else + h = ::SOAP::SOAPHeader.new + headers.each do |header| + h.add(header.elename.name, header) + end + h + end + end + + def receive_headers(headerhandler, headers) + headerhandler.on_inbound(headers) if headers + end + + def unmarshal(conn_data) + opt = {} + contenttype = conn_data.receive_contenttype + if /#{MIMEMessage::MultipartContentType}/i =~ contenttype + opt[:external_content] = {} + mime = MIMEMessage.parse("Content-Type: " + contenttype, + conn_data.receive_string) + mime.parts.each do |part| + value = Attachment.new(part.content) + value.contentid = part.contentid + obj = SOAPAttachment.new(value) + opt[:external_content][value.contentid] = obj if value.contentid + end + opt[:charset] = + StreamHandler.parse_media_type(mime.root.headers['content-type'].str) + env = Processor.unmarshal(mime.root.content, opt) + else + opt[:charset] = ::SOAP::StreamHandler.parse_media_type(contenttype) + env = Processor.unmarshal(conn_data.receive_string, opt) + end + charset = opt[:charset] + conn_data.send_contenttype = "text/xml; charset=\"#{charset}\"" + env + end + + def marshal(conn_data, env, default_encodingstyle = nil) + opt = {} + opt[:external_content] = nil + opt[:default_encodingstyle] = default_encodingstyle + opt[:generate_explicit_type] = @generate_explicit_type + response_string = Processor.marshal(env, opt) + conn_data.send_string = response_string + if ext = opt[:external_content] + mimeize(conn_data, ext) + end + conn_data + end + + def mimeize(conn_data, ext) + mime = MIMEMessage.new + ext.each do |k, v| + mime.add_attachment(v.data) + end + mime.add_part(conn_data.send_string + "\r\n") + mime.close + conn_data.send_string = mime.content_str + conn_data.send_contenttype = mime.headers['content-type'].str + conn_data + end + + # Create fault response. + def fault(e) + detail = Mapping::SOAPException.new(e) + SOAPFault.new( + SOAPString.new('Server'), + SOAPString.new(e.to_s), + SOAPString.new(@actor), + Mapping.obj2soap(detail, @mapping_registry)) + end + + def create_mapping_opt + { :external_ces => @external_ces } + end + + class Operation + attr_reader :name + attr_reader :soapaction + attr_reader :request_style + attr_reader :response_style + attr_reader :request_use + attr_reader :response_use + + def initialize(soapaction, name, param_def, opt) + @soapaction = soapaction + @name = name + @request_style = opt[:request_style] + @response_style = opt[:response_style] + @request_use = opt[:request_use] + @response_use = opt[:response_use] + check_style(@request_style) + check_style(@response_style) + check_use(@request_use) + check_use(@response_use) + if @response_style == :rpc + request_qname = opt[:request_qname] or raise + @rpc_method_factory = + RPC::SOAPMethodRequest.new(request_qname, param_def, @soapaction) + @rpc_response_qname = opt[:response_qname] + else + @doc_request_qnames = [] + @doc_request_qualified = [] + @doc_response_qnames = [] + @doc_response_qualified = [] + param_def.each do |inout, paramname, typeinfo, eleinfo| + klass, nsdef, namedef = typeinfo + qualified = eleinfo + case inout + when SOAPMethod::IN + @doc_request_qnames << XSD::QName.new(nsdef, namedef) + @doc_request_qualified << qualified + when SOAPMethod::OUT + @doc_response_qnames << XSD::QName.new(nsdef, namedef) + @doc_response_qualified << qualified + else + raise ArgumentError.new( + "illegal inout definition for document style: #{inout}") + end + end + end + end + + def request_default_encodingstyle + (@request_use == :encoded) ? EncodingNamespace : LiteralNamespace + end + + def response_default_encodingstyle + (@response_use == :encoded) ? EncodingNamespace : LiteralNamespace + end + + def call(body, mapping_registry, literal_mapping_registry, opt) + if @request_style == :rpc + values = request_rpc(body, mapping_registry, literal_mapping_registry, + opt) + else + values = request_document(body, mapping_registry, + literal_mapping_registry, opt) + end + result = receiver.method(@name.intern).call(*values) + return result if result.is_a?(SOAPFault) + if @response_style == :rpc + response_rpc(result, mapping_registry, literal_mapping_registry, opt) + else + response_doc(result, mapping_registry, literal_mapping_registry, opt) + end + end + + private + + def receiver + raise NotImplementedError.new('must be defined in derived class') + end + + def request_rpc(body, mapping_registry, literal_mapping_registry, opt) + request = body.request + unless request.is_a?(SOAPStruct) + raise RPCRoutingError.new("not an RPC style") + end + if @request_use == :encoded + request_rpc_enc(request, mapping_registry, opt) + else + request_rpc_lit(request, literal_mapping_registry, opt) + end + end + + def request_document(body, mapping_registry, literal_mapping_registry, opt) + # ToDo: compare names with @doc_request_qnames + if @request_use == :encoded + request_doc_enc(body, mapping_registry, opt) + else + request_doc_lit(body, literal_mapping_registry, opt) + end + end + + def request_rpc_enc(request, mapping_registry, opt) + param = Mapping.soap2obj(request, mapping_registry, nil, opt) + request.collect { |key, value| + param[key] + } + end + + def request_rpc_lit(request, mapping_registry, opt) + request.collect { |key, value| + Mapping.soap2obj(value, mapping_registry, nil, opt) + } + end + + def request_doc_enc(body, mapping_registry, opt) + body.collect { |key, value| + Mapping.soap2obj(value, mapping_registry, nil, opt) + } + end + + def request_doc_lit(body, mapping_registry, opt) + body.collect { |key, value| + Mapping.soap2obj(value, mapping_registry, nil, opt) + } + end + + def response_rpc(result, mapping_registry, literal_mapping_registry, opt) + if @response_use == :encoded + response_rpc_enc(result, mapping_registry, opt) + else + response_rpc_lit(result, literal_mapping_registry, opt) + end + end + + def response_doc(result, mapping_registry, literal_mapping_registry, opt) + if @doc_response_qnames.size == 1 and !result.is_a?(Array) + result = [result] + end + if result.size != @doc_response_qnames.size + raise "required #{@doc_response_qnames.size} responses " + + "but #{result.size} given" + end + if @response_use == :encoded + response_doc_enc(result, mapping_registry, opt) + else + response_doc_lit(result, literal_mapping_registry, opt) + end + end + + def response_rpc_enc(result, mapping_registry, opt) + soap_response = + @rpc_method_factory.create_method_response(@rpc_response_qname) + if soap_response.have_outparam? + unless result.is_a?(Array) + raise RPCRoutingError.new("out parameter was not returned") + end + outparams = {} + i = 1 + soap_response.output_params.each do |outparam| + outparams[outparam] = Mapping.obj2soap(result[i], mapping_registry, + nil, opt) + i += 1 + end + soap_response.set_outparam(outparams) + soap_response.retval = Mapping.obj2soap(result[0], mapping_registry, + nil, opt) + else + soap_response.retval = Mapping.obj2soap(result, mapping_registry, nil, + opt) + end + soap_response + end + + def response_rpc_lit(result, mapping_registry, opt) + soap_response = + @rpc_method_factory.create_method_response(@rpc_response_qname) + if soap_response.have_outparam? + unless result.is_a?(Array) + raise RPCRoutingError.new("out parameter was not returned") + end + outparams = {} + i = 1 + soap_response.output_params.each do |outparam| + outparams[outparam] = Mapping.obj2soap(result[i], mapping_registry, + XSD::QName.new(nil, outparam), opt) + i += 1 + end + soap_response.set_outparam(outparams) + soap_response.retval = Mapping.obj2soap(result[0], mapping_registry, + XSD::QName.new(nil, soap_response.elename), opt) + else + soap_response.retval = Mapping.obj2soap(result, mapping_registry, + XSD::QName.new(nil, soap_response.elename), opt) + end + soap_response + end + + def response_doc_enc(result, mapping_registry, opt) + (0...result.size).collect { |idx| + ele = Mapping.obj2soap(result[idx], mapping_registry, nil, opt) + ele.elename = @doc_response_qnames[idx] + ele + } + end + + def response_doc_lit(result, mapping_registry, opt) + (0...result.size).collect { |idx| + ele = Mapping.obj2soap(result[idx], mapping_registry, + @doc_response_qnames[idx]) + ele.encodingstyle = LiteralNamespace + if ele.respond_to?(:qualified) + ele.qualified = @doc_response_qualified[idx] + end + ele + } + end + + def check_style(style) + unless [:rpc, :document].include?(style) + raise ArgumentError.new("unknown style: #{style}") + end + end + + def check_use(use) + unless [:encoded, :literal].include?(use) + raise ArgumentError.new("unknown use: #{use}") + end + end + end + + class ApplicationScopeOperation < Operation + def initialize(soapaction, receiver, name, param_def, opt) + super(soapaction, name, param_def, opt) + @receiver = receiver + end + + private + + def receiver + @receiver + end + end + + class RequestScopeOperation < Operation + def initialize(soapaction, receiver_factory, name, param_def, opt) + super(soapaction, name, param_def, opt) + unless receiver_factory.respond_to?(:create) + raise TypeError.new("factory must respond to 'create'") + end + @receiver_factory = receiver_factory + end + + private + + def receiver + @receiver_factory.create + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/rpc.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/rpc.rb new file mode 100644 index 0000000000..a48b525dbb --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/rpc.rb @@ -0,0 +1,25 @@ +# SOAP4R - RPC utility. +# Copyright (C) 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +module SOAP + + +module RPC + ServerException = Mapping::MappedException + + def self.defined_methods(obj) + if obj.is_a?(Module) + obj.methods - Module.methods + else + obj.methods - Object.instance_methods(true) + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/soaplet.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/soaplet.rb new file mode 100644 index 0000000000..7cccdd3e31 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/soaplet.rb @@ -0,0 +1,162 @@ +# SOAP4R - SOAP handler servlet for WEBrick +# Copyright (C) 2001-2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'webrick/httpservlet/abstract' +require 'webrick/httpstatus' +require 'soap/rpc/router' +require 'soap/streamHandler' +begin + require 'stringio' + require 'zlib' +rescue LoadError + warn("Loading stringio or zlib failed. No gzipped response supported.") if $DEBUG +end + + +warn("Overriding WEBrick::Log#debug") if $DEBUG +require 'webrick/log' +module WEBrick + class Log < BasicLog + alias __debug debug + def debug(msg = nil) + if block_given? and msg.nil? + __debug(yield) + else + __debug(msg) + end + end + end +end + + +module SOAP +module RPC + + +class SOAPlet < WEBrick::HTTPServlet::AbstractServlet +public + attr_reader :options + + def initialize(router = nil) + @router = router || ::SOAP::RPC::Router.new(self.class.name) + @options = {} + @config = {} + end + + # for backward compatibility + def app_scope_router + @router + end + + # for backward compatibility + def add_servant(obj, namespace) + @router.add_rpc_servant(obj, namespace) + end + + def allow_content_encoding_gzip=(allow) + @options[:allow_content_encoding_gzip] = allow + end + + ### + ## Servlet interfaces for WEBrick. + # + def get_instance(config, *options) + @config = config + self + end + + def require_path_info? + false + end + + def do_GET(req, res) + res.header['Allow'] = 'POST' + raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed" + end + + def do_POST(req, res) + logger.debug { "SOAP request: " + req.body } if logger + begin + conn_data = ::SOAP::StreamHandler::ConnectionData.new + setup_req(conn_data, req) + @router.external_ces = @options[:external_ces] + conn_data = @router.route(conn_data) + setup_res(conn_data, req, res) + rescue Exception => e + conn_data = @router.create_fault_response(e) + res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR + res.body = conn_data.send_string + res['content-type'] = conn_data.send_contenttype || "text/xml" + end + if res.body.is_a?(IO) + res.chunked = true + logger.debug { "SOAP response: (chunked response not logged)" } if logger + else + logger.debug { "SOAP response: " + res.body } if logger + end + end + +private + + def logger + @config[:Logger] + end + + def setup_req(conn_data, req) + conn_data.receive_string = req.body + conn_data.receive_contenttype = req['content-type'] + conn_data.soapaction = parse_soapaction(req.meta_vars['HTTP_SOAPACTION']) + end + + def setup_res(conn_data, req, res) + res['content-type'] = conn_data.send_contenttype + if conn_data.is_fault + res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR + end + if outstring = encode_gzip(req, conn_data.send_string) + res['content-encoding'] = 'gzip' + res['content-length'] = outstring.size + res.body = outstring + else + res.body = conn_data.send_string + end + end + + def parse_soapaction(soapaction) + if !soapaction.nil? and !soapaction.empty? + if /^"(.+)"$/ =~ soapaction + return $1 + end + end + nil + end + + def encode_gzip(req, outstring) + unless encode_gzip?(req) + return nil + end + begin + ostream = StringIO.new + gz = Zlib::GzipWriter.new(ostream) + gz.write(outstring) + ostream.string + ensure + gz.close + end + end + + def encode_gzip?(req) + @options[:allow_content_encoding_gzip] and defined?(::Zlib) and + req['accept-encoding'] and + req['accept-encoding'].split(/,\s*/).include?('gzip') + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/standaloneServer.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/standaloneServer.rb new file mode 100644 index 0000000000..080343ba33 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/rpc/standaloneServer.rb @@ -0,0 +1,43 @@ +# SOAP4R - WEBrick Server +# Copyright (C) 2003 by NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/rpc/httpserver' + + +module SOAP +module RPC + + +class StandaloneServer < HTTPServer + def initialize(appname, default_namespace, host = "0.0.0.0", port = 8080) + @appname = appname + @default_namespace = default_namespace + @host = host + @port = port + super(create_config) + end + + alias add_servant add_rpc_servant + alias add_headerhandler add_rpc_headerhandler + +private + + def create_config + { + :BindAddress => @host, + :Port => @port, + :AccessLog => [], + :SOAPDefaultNamespace => @default_namespace, + :SOAPHTTPServerApplicationName => @appname, + } + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/soap.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/soap.rb new file mode 100644 index 0000000000..12e09eccfe --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/soap.rb @@ -0,0 +1,140 @@ +# soap/soap.rb: SOAP4R - Base definitions. +# Copyright (C) 2000-2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/qname' +require 'xsd/charset' + + +module SOAP + + +VERSION = Version = '1.5.5' +PropertyName = 'soap/property' + +EnvelopeNamespace = 'http://schemas.xmlsoap.org/soap/envelope/' +EncodingNamespace = 'http://schemas.xmlsoap.org/soap/encoding/' +LiteralNamespace = 'http://xml.apache.org/xml-soap/literalxml' + +NextActor = 'http://schemas.xmlsoap.org/soap/actor/next' + +EleEnvelope = 'Envelope' +EleHeader = 'Header' +EleBody = 'Body' +EleFault = 'Fault' +EleFaultString = 'faultstring' +EleFaultActor = 'faultactor' +EleFaultCode = 'faultcode' +EleFaultDetail = 'detail' + +AttrMustUnderstand = 'mustUnderstand' +AttrEncodingStyle = 'encodingStyle' +AttrActor = 'actor' +AttrRoot = 'root' +AttrArrayType = 'arrayType' +AttrOffset = 'offset' +AttrPosition = 'position' +ValueArray = 'Array' + +EleEnvelopeName = XSD::QName.new(EnvelopeNamespace, EleEnvelope).freeze +EleHeaderName = XSD::QName.new(EnvelopeNamespace, EleHeader).freeze +EleBodyName = XSD::QName.new(EnvelopeNamespace, EleBody).freeze +EleFaultName = XSD::QName.new(EnvelopeNamespace, EleFault).freeze +EleFaultStringName = XSD::QName.new(nil, EleFaultString).freeze +EleFaultActorName = XSD::QName.new(nil, EleFaultActor).freeze +EleFaultCodeName = XSD::QName.new(nil, EleFaultCode).freeze +EleFaultDetailName = XSD::QName.new(nil, EleFaultDetail).freeze +AttrMustUnderstandName = XSD::QName.new(EnvelopeNamespace, AttrMustUnderstand).freeze +AttrEncodingStyleName = XSD::QName.new(EnvelopeNamespace, AttrEncodingStyle).freeze +AttrRootName = XSD::QName.new(EncodingNamespace, AttrRoot).freeze +AttrArrayTypeName = XSD::QName.new(EncodingNamespace, AttrArrayType).freeze +AttrOffsetName = XSD::QName.new(EncodingNamespace, AttrOffset).freeze +AttrPositionName = XSD::QName.new(EncodingNamespace, AttrPosition).freeze +ValueArrayName = XSD::QName.new(EncodingNamespace, ValueArray).freeze + +Base64Literal = 'base64' + +SOAPNamespaceTag = 'env' +XSDNamespaceTag = 'xsd' +XSINamespaceTag = 'xsi' + +MediaType = 'text/xml' + +class Error < StandardError; end + +class StreamError < Error; end +class HTTPStreamError < StreamError; end +class PostUnavailableError < HTTPStreamError; end +class MPostUnavailableError < HTTPStreamError; end + +class ArrayIndexOutOfBoundsError < Error; end +class ArrayStoreError < Error; end + +class RPCRoutingError < Error; end +class EmptyResponseError < Error; end +class ResponseFormatError < Error; end + +class UnhandledMustUnderstandHeaderError < Error; end + +class FaultError < Error + attr_reader :faultcode + attr_reader :faultstring + attr_reader :faultactor + attr_accessor :detail + + def initialize(fault) + @faultcode = fault.faultcode + @faultstring = fault.faultstring + @faultactor = fault.faultactor + @detail = fault.detail + super(self.to_s) + end + + def to_s + str = nil + if @faultstring and @faultstring.respond_to?('data') + str = @faultstring.data + end + str || '(No faultstring)' + end +end + + +module Env + def self.getenv(name) + ENV[name.downcase] || ENV[name.upcase] + end + + use_proxy = getenv('soap_use_proxy') == 'on' + HTTP_PROXY = use_proxy ? getenv('http_proxy') : nil + NO_PROXY = use_proxy ? getenv('no_proxy') : nil +end + + +end + + +unless Object.respond_to?(:instance_variable_get) + class Object + def instance_variable_get(ivarname) + instance_eval(ivarname) + end + + def instance_variable_set(ivarname, value) + instance_eval("#{ivarname} = value") + end + end +end + + +unless Kernel.respond_to?(:warn) + module Kernel + def warn(msg) + STDERR.puts(msg + "\n") unless $VERBOSE.nil? + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/streamHandler.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/streamHandler.rb new file mode 100644 index 0000000000..672396ecce --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/streamHandler.rb @@ -0,0 +1,229 @@ +# SOAP4R - Stream handler. +# Copyright (C) 2000, 2001, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/soap' +require 'soap/httpconfigloader' +begin + require 'stringio' + require 'zlib' +rescue LoadError + warn("Loading stringio or zlib failed. No gzipped response support.") if $DEBUG +end + + +module SOAP + + +class StreamHandler + RUBY_VERSION_STRING = "ruby #{ RUBY_VERSION } (#{ RUBY_RELEASE_DATE }) [#{ RUBY_PLATFORM }]" + + class ConnectionData + attr_accessor :send_string + attr_accessor :send_contenttype + attr_accessor :receive_string + attr_accessor :receive_contenttype + attr_accessor :is_fault + attr_accessor :soapaction + + def initialize(send_string = nil) + @send_string = send_string + @send_contenttype = nil + @receive_string = nil + @receive_contenttype = nil + @is_fault = false + @soapaction = nil + end + end + + def self.parse_media_type(str) + if /^#{ MediaType }(?:\s*;\s*charset=([^"]+|"[^"]+"))?$/i !~ str + return nil + end + charset = $1 + charset.gsub!(/"/, '') if charset + charset || 'us-ascii' + end + + def self.create_media_type(charset) + "#{ MediaType }; charset=#{ charset }" + end +end + + +class HTTPStreamHandler < StreamHandler + include SOAP + + begin + require 'http-access2' + if HTTPAccess2::VERSION < "2.0" + raise LoadError.new("http-access/2.0 or later is required.") + end + Client = HTTPAccess2::Client + RETRYABLE = true + rescue LoadError + warn("Loading http-access2 failed. Net/http is used.") if $DEBUG + require 'soap/netHttpClient' + Client = SOAP::NetHttpClient + RETRYABLE = false + end + + +public + + attr_reader :client + attr_accessor :wiredump_file_base + + MAX_RETRY_COUNT = 10 # [times] + + def initialize(options) + super() + @client = Client.new(nil, "SOAP4R/#{ Version }") + @wiredump_file_base = nil + @charset = @wiredump_dev = nil + @options = options + set_options + @client.debug_dev = @wiredump_dev + @cookie_store = nil + @accept_encoding_gzip = false + end + + def test_loopback_response + @client.test_loopback_response + end + + def accept_encoding_gzip=(allow) + @accept_encoding_gzip = allow + end + + def inspect + "#<#{self.class}>" + end + + def send(endpoint_url, conn_data, soapaction = nil, charset = @charset) + conn_data.soapaction ||= soapaction # for backward conpatibility + send_post(endpoint_url, conn_data, charset) + end + + def reset(endpoint_url = nil) + if endpoint_url.nil? + @client.reset_all + else + @client.reset(endpoint_url) + end + @client.save_cookie_store if @cookie_store + end + +private + + def set_options + HTTPConfigLoader.set_options(@client, @options) + @charset = @options["charset"] || XSD::Charset.xml_encoding_label + @options.add_hook("charset") do |key, value| + @charset = value + end + @wiredump_dev = @options["wiredump_dev"] + @options.add_hook("wiredump_dev") do |key, value| + @wiredump_dev = value + @client.debug_dev = @wiredump_dev + end + set_cookie_store_file(@options["cookie_store_file"]) + @options.add_hook("cookie_store_file") do |key, value| + set_cookie_store_file(value) + end + ssl_config = @options["ssl_config"] + basic_auth = @options["basic_auth"] + @options.lock(true) + ssl_config.unlock + basic_auth.unlock + end + + def set_cookie_store_file(value) + value = nil if value and value.empty? + @cookie_store = value + @client.set_cookie_store(@cookie_store) if @cookie_store + end + + def send_post(endpoint_url, conn_data, charset) + conn_data.send_contenttype ||= StreamHandler.create_media_type(charset) + + if @wiredump_file_base + filename = @wiredump_file_base + '_request.xml' + f = File.open(filename, "w") + f << conn_data.send_string + f.close + end + + extra = {} + extra['Content-Type'] = conn_data.send_contenttype + extra['SOAPAction'] = "\"#{ conn_data.soapaction }\"" + extra['Accept-Encoding'] = 'gzip' if send_accept_encoding_gzip? + send_string = conn_data.send_string + @wiredump_dev << "Wire dump:\n\n" if @wiredump_dev + begin + retry_count = 0 + while true + res = @client.post(endpoint_url, send_string, extra) + if RETRYABLE and HTTP::Status.redirect?(res.status) + retry_count += 1 + if retry_count >= MAX_RETRY_COUNT + raise HTTPStreamError.new("redirect count exceeded") + end + endpoint_url = res.header["location"][0] + puts "redirected to #{endpoint_url}" if $DEBUG + else + break + end + end + rescue + @client.reset(endpoint_url) + raise + end + @wiredump_dev << "\n\n" if @wiredump_dev + receive_string = res.content + if @wiredump_file_base + filename = @wiredump_file_base + '_response.xml' + f = File.open(filename, "w") + f << receive_string + f.close + end + case res.status + when 405 + raise PostUnavailableError.new("#{ res.status }: #{ res.reason }") + when 200, 500 + # Nothing to do. + else + raise HTTPStreamError.new("#{ res.status }: #{ res.reason }") + end + if res.respond_to?(:header) and !res.header['content-encoding'].empty? and + res.header['content-encoding'][0].downcase == 'gzip' + receive_string = decode_gzip(receive_string) + end + conn_data.receive_string = receive_string + conn_data.receive_contenttype = res.contenttype + conn_data + end + + def send_accept_encoding_gzip? + @accept_encoding_gzip and defined?(::Zlib) + end + + def decode_gzip(instring) + unless send_accept_encoding_gzip? + raise HTTPStreamError.new("Gzipped response content.") + end + begin + gz = Zlib::GzipReader.new(StringIO.new(instring)) + gz.read + ensure + gz.close + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/wsdlDriver.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/wsdlDriver.rb new file mode 100644 index 0000000000..eba9608df5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/soap/wsdlDriver.rb @@ -0,0 +1,575 @@ +# SOAP4R - SOAP WSDL driver +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/parser' +require 'wsdl/importer' +require 'xsd/qname' +require 'xsd/codegen/gensupport' +require 'soap/mapping/wsdlencodedregistry' +require 'soap/mapping/wsdlliteralregistry' +require 'soap/rpc/driver' +require 'wsdl/soap/methodDefCreator' + + +module SOAP + + +class WSDLDriverFactory + class FactoryError < StandardError; end + + attr_reader :wsdl + + def initialize(wsdl) + @wsdl = import(wsdl) + @methoddefcreator = WSDL::SOAP::MethodDefCreator.new(@wsdl) + end + + def inspect + "#<#{self.class}:#{@wsdl.name}>" + end + + def create_rpc_driver(servicename = nil, portname = nil) + port = find_port(servicename, portname) + drv = SOAP::RPC::Driver.new(port.soap_address.location) + init_driver(drv, port) + add_operation(drv, port) + drv + end + + # depricated old interface + def create_driver(servicename = nil, portname = nil) + warn("WSDLDriverFactory#create_driver is depricated. Use create_rpc_driver instead.") + port = find_port(servicename, portname) + WSDLDriver.new(@wsdl, port, nil) + end + + # Backward compatibility. + alias createDriver create_driver + +private + + def find_port(servicename = nil, portname = nil) + service = port = nil + if servicename + service = @wsdl.service( + XSD::QName.new(@wsdl.targetnamespace, servicename)) + else + service = @wsdl.services[0] + end + if service.nil? + raise FactoryError.new("service #{servicename} not found in WSDL") + end + if portname + port = service.ports[XSD::QName.new(@wsdl.targetnamespace, portname)] + if port.nil? + raise FactoryError.new("port #{portname} not found in WSDL") + end + else + port = service.ports.find { |port| !port.soap_address.nil? } + if port.nil? + raise FactoryError.new("no ports have soap:address") + end + end + if port.soap_address.nil? + raise FactoryError.new("soap:address element not found in WSDL") + end + port + end + + def init_driver(drv, port) + wsdl_elements = @wsdl.collect_elements + wsdl_types = @wsdl.collect_complextypes + @wsdl.collect_simpletypes + rpc_decode_typemap = wsdl_types + + @wsdl.soap_rpc_complextypes(port.find_binding) + drv.proxy.mapping_registry = + Mapping::WSDLEncodedRegistry.new(rpc_decode_typemap) + drv.proxy.literal_mapping_registry = + Mapping::WSDLLiteralRegistry.new(wsdl_types, wsdl_elements) + end + + def add_operation(drv, port) + port.find_binding.operations.each do |op_bind| + op_name = op_bind.soapoperation_name + soapaction = op_bind.soapaction || '' + orgname = op_name.name + name = XSD::CodeGen::GenSupport.safemethodname(orgname) + param_def = create_param_def(op_bind) + opt = { + :request_style => op_bind.soapoperation_style, + :response_style => op_bind.soapoperation_style, + :request_use => op_bind.input.soapbody_use, + :response_use => op_bind.output.soapbody_use, + :elementformdefault => false, + :attributeformdefault => false + } + if op_bind.soapoperation_style == :rpc + drv.add_rpc_operation(op_name, soapaction, name, param_def, opt) + else + drv.add_document_operation(soapaction, name, param_def, opt) + end + if orgname != name and orgname.capitalize == name.capitalize + ::SOAP::Mapping.define_singleton_method(drv, orgname) do |*arg| + __send__(name, *arg) + end + end + end + end + + def import(location) + WSDL::Importer.import(location) + end + + def create_param_def(op_bind) + op = op_bind.find_operation + if op_bind.soapoperation_style == :rpc + param_def = @methoddefcreator.collect_rpcparameter(op) + else + param_def = @methoddefcreator.collect_documentparameter(op) + end + # the first element of typedef in param_def is a String like + # "::SOAP::SOAPStruct". turn this String to a class. + param_def.collect { |io, name, typedef| + typedef[0] = Mapping.class_from_name(typedef[0]) + [io, name, typedef] + } + end + + def partqname(part) + if part.type + part.type + else + part.element + end + end + + def param_def(type, name, klass, partqname) + [type, name, [klass, partqname.namespace, partqname.name]] + end + + def filter_parts(partsdef, partssource) + parts = partsdef.split(/\s+/) + partssource.find_all { |part| parts.include?(part.name) } + end +end + + +class WSDLDriver + class << self + if RUBY_VERSION >= "1.7.0" + def __attr_proxy(symbol, assignable = false) + name = symbol.to_s + define_method(name) { + @servant.__send__(name) + } + if assignable + aname = name + '=' + define_method(aname) { |rhs| + @servant.__send__(aname, rhs) + } + end + end + else + def __attr_proxy(symbol, assignable = false) + name = symbol.to_s + module_eval <<-EOS + def #{name} + @servant.#{name} + end + EOS + if assignable + module_eval <<-EOS + def #{name}=(value) + @servant.#{name} = value + end + EOS + end + end + end + end + + __attr_proxy :options + __attr_proxy :headerhandler + __attr_proxy :streamhandler + __attr_proxy :test_loopback_response + __attr_proxy :endpoint_url, true + __attr_proxy :mapping_registry, true # for RPC unmarshal + __attr_proxy :wsdl_mapping_registry, true # for RPC marshal + __attr_proxy :default_encodingstyle, true + __attr_proxy :generate_explicit_type, true + __attr_proxy :allow_unqualified_element, true + + def httpproxy + @servant.options["protocol.http.proxy"] + end + + def httpproxy=(httpproxy) + @servant.options["protocol.http.proxy"] = httpproxy + end + + def wiredump_dev + @servant.options["protocol.http.wiredump_dev"] + end + + def wiredump_dev=(wiredump_dev) + @servant.options["protocol.http.wiredump_dev"] = wiredump_dev + end + + def mandatorycharset + @servant.options["protocol.mandatorycharset"] + end + + def mandatorycharset=(mandatorycharset) + @servant.options["protocol.mandatorycharset"] = mandatorycharset + end + + def wiredump_file_base + @servant.options["protocol.wiredump_file_base"] + end + + def wiredump_file_base=(wiredump_file_base) + @servant.options["protocol.wiredump_file_base"] = wiredump_file_base + end + + def initialize(wsdl, port, logdev) + @servant = Servant__.new(self, wsdl, port, logdev) + end + + def inspect + "#<#{self.class}:#{@servant.port.name}>" + end + + def reset_stream + @servant.reset_stream + end + + # Backward compatibility. + alias generateEncodeType= generate_explicit_type= + + class Servant__ + include SOAP + + attr_reader :options + attr_reader :port + + attr_accessor :soapaction + attr_accessor :default_encodingstyle + attr_accessor :allow_unqualified_element + attr_accessor :generate_explicit_type + attr_accessor :mapping_registry + attr_accessor :wsdl_mapping_registry + + def initialize(host, wsdl, port, logdev) + @host = host + @wsdl = wsdl + @port = port + @logdev = logdev + @soapaction = nil + @options = setup_options + @default_encodingstyle = nil + @allow_unqualified_element = nil + @generate_explicit_type = false + @mapping_registry = nil # for rpc unmarshal + @wsdl_mapping_registry = nil # for rpc marshal + @wiredump_file_base = nil + @mandatorycharset = nil + @wsdl_elements = @wsdl.collect_elements + @wsdl_types = @wsdl.collect_complextypes + @wsdl.collect_simpletypes + @rpc_decode_typemap = @wsdl_types + + @wsdl.soap_rpc_complextypes(port.find_binding) + @wsdl_mapping_registry = Mapping::WSDLEncodedRegistry.new( + @rpc_decode_typemap) + @doc_mapper = Mapping::WSDLLiteralRegistry.new( + @wsdl_types, @wsdl_elements) + endpoint_url = @port.soap_address.location + # Convert a map which key is QName, to a Hash which key is String. + @operation = {} + @port.inputoperation_map.each do |op_name, op_info| + orgname = op_name.name + name = XSD::CodeGen::GenSupport.safemethodname(orgname) + @operation[name] = @operation[orgname] = op_info + add_method_interface(op_info) + end + @proxy = ::SOAP::RPC::Proxy.new(endpoint_url, @soapaction, @options) + end + + def inspect + "#<#{self.class}:#{@proxy.inspect}>" + end + + def endpoint_url + @proxy.endpoint_url + end + + def endpoint_url=(endpoint_url) + @proxy.endpoint_url = endpoint_url + end + + def headerhandler + @proxy.headerhandler + end + + def streamhandler + @proxy.streamhandler + end + + def test_loopback_response + @proxy.test_loopback_response + end + + def reset_stream + @proxy.reset_stream + end + + def rpc_call(name, *values) + set_wiredump_file_base(name) + unless op_info = @operation[name] + raise RuntimeError, "method: #{name} not defined" + end + req_header = create_request_header + req_body = create_request_body(op_info, *values) + reqopt = create_options({ + :soapaction => op_info.soapaction || @soapaction}) + resopt = create_options({ + :decode_typemap => @rpc_decode_typemap}) + env = @proxy.route(req_header, req_body, reqopt, resopt) + raise EmptyResponseError unless env + receive_headers(env.header) + begin + @proxy.check_fault(env.body) + rescue ::SOAP::FaultError => e + Mapping.fault2exception(e) + end + ret = env.body.response ? + Mapping.soap2obj(env.body.response, @mapping_registry) : nil + if env.body.outparams + outparams = env.body.outparams.collect { |outparam| + Mapping.soap2obj(outparam) + } + return [ret].concat(outparams) + else + return ret + end + end + + # req_header: [[element, mustunderstand, encodingstyle(QName/String)], ...] + # req_body: SOAPBasetype/SOAPCompoundtype + def document_send(name, header_obj, body_obj) + set_wiredump_file_base(name) + unless op_info = @operation[name] + raise RuntimeError, "method: #{name} not defined" + end + req_header = header_obj ? header_from_obj(header_obj, op_info) : nil + req_body = body_from_obj(body_obj, op_info) + opt = create_options({ + :soapaction => op_info.soapaction || @soapaction, + :decode_typemap => @wsdl_types}) + env = @proxy.invoke(req_header, req_body, opt) + raise EmptyResponseError unless env + if env.body.fault + raise ::SOAP::FaultError.new(env.body.fault) + end + res_body_obj = env.body.response ? + Mapping.soap2obj(env.body.response, @mapping_registry) : nil + return env.header, res_body_obj + end + + private + + def create_options(hash = nil) + opt = {} + opt[:default_encodingstyle] = @default_encodingstyle + opt[:allow_unqualified_element] = @allow_unqualified_element + opt[:generate_explicit_type] = @generate_explicit_type + opt.update(hash) if hash + opt + end + + def set_wiredump_file_base(name) + if @wiredump_file_base + @proxy.set_wiredump_file_base(@wiredump_file_base + "_#{name}") + end + end + + def create_request_header + headers = @proxy.headerhandler.on_outbound + if headers.empty? + nil + else + h = SOAPHeader.new + headers.each do |header| + h.add(header.elename.name, header) + end + h + end + end + + def receive_headers(headers) + @proxy.headerhandler.on_inbound(headers) if headers + end + + def create_request_body(op_info, *values) + method = create_method_struct(op_info, *values) + SOAPBody.new(method) + end + + def create_method_struct(op_info, *params) + parts_names = op_info.bodyparts.collect { |part| part.name } + obj = create_method_obj(parts_names, params) + method = Mapping.obj2soap(obj, @wsdl_mapping_registry, op_info.op_name) + if method.members.size != parts_names.size + new_method = SOAPStruct.new + method.each do |key, value| + if parts_names.include?(key) + new_method.add(key, value) + end + end + method = new_method + end + method.elename = op_info.op_name + method.type = XSD::QName.new # Request should not be typed. + method + end + + def create_method_obj(names, params) + o = Object.new + idx = 0 + while idx < params.length + o.instance_variable_set('@' + names[idx], params[idx]) + idx += 1 + end + o + end + + def header_from_obj(obj, op_info) + if obj.is_a?(SOAPHeader) + obj + elsif op_info.headerparts.empty? + if obj.nil? + nil + else + raise RuntimeError.new("no header definition in schema: #{obj}") + end + elsif op_info.headerparts.size == 1 + part = op_info.headerparts[0] + header = SOAPHeader.new() + header.add(headeritem_from_obj(obj, part.element || part.eletype)) + header + else + header = SOAPHeader.new() + op_info.headerparts.each do |part| + child = Mapping.get_attribute(obj, part.name) + ele = headeritem_from_obj(child, part.element || part.eletype) + header.add(part.name, ele) + end + header + end + end + + def headeritem_from_obj(obj, name) + if obj.nil? + SOAPElement.new(name) + elsif obj.is_a?(SOAPHeaderItem) + obj + else + Mapping.obj2soap(obj, @doc_mapper, name) + end + end + + def body_from_obj(obj, op_info) + if obj.is_a?(SOAPBody) + obj + elsif op_info.bodyparts.empty? + if obj.nil? + nil + else + raise RuntimeError.new("no body found in schema") + end + elsif op_info.bodyparts.size == 1 + part = op_info.bodyparts[0] + ele = bodyitem_from_obj(obj, part.element || part.type) + SOAPBody.new(ele) + else + body = SOAPBody.new + op_info.bodyparts.each do |part| + child = Mapping.get_attribute(obj, part.name) + ele = bodyitem_from_obj(child, part.element || part.type) + body.add(ele.elename.name, ele) + end + body + end + end + + def bodyitem_from_obj(obj, name) + if obj.nil? + SOAPElement.new(name) + elsif obj.is_a?(SOAPElement) + obj + else + Mapping.obj2soap(obj, @doc_mapper, name) + end + end + + def add_method_interface(op_info) + name = XSD::CodeGen::GenSupport.safemethodname(op_info.op_name.name) + orgname = op_info.op_name.name + parts_names = op_info.bodyparts.collect { |part| part.name } + case op_info.style + when :document + if orgname != name and orgname.capitalize == name.capitalize + add_document_method_interface(orgname, parts_names) + end + add_document_method_interface(name, parts_names) + when :rpc + if orgname != name and orgname.capitalize == name.capitalize + add_rpc_method_interface(orgname, parts_names) + end + add_rpc_method_interface(name, parts_names) + else + raise RuntimeError.new("unknown style: #{op_info.style}") + end + end + + def add_rpc_method_interface(name, parts_names) + ::SOAP::Mapping.define_singleton_method(@host, name) do |*arg| + unless arg.size == parts_names.size + raise ArgumentError.new( + "wrong number of arguments (#{arg.size} for #{parts_names.size})") + end + @servant.rpc_call(name, *arg) + end + @host.method(name) + end + + def add_document_method_interface(name, parts_names) + ::SOAP::Mapping.define_singleton_method(@host, name) do |h, b| + @servant.document_send(name, h, b) + end + @host.method(name) + end + + def setup_options + if opt = Property.loadproperty(::SOAP::PropertyName) + opt = opt["client"] + end + opt ||= Property.new + opt.add_hook("protocol.mandatorycharset") do |key, value| + @mandatorycharset = value + end + opt.add_hook("protocol.wiredump_file_base") do |key, value| + @wiredump_file_base = value + end + opt["protocol.http.charset"] ||= XSD::Charset.xml_encoding_label + opt["protocol.http.proxy"] ||= Env::HTTP_PROXY + opt["protocol.http.no_proxy"] ||= Env::NO_PROXY + opt + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/sync.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/sync.rb new file mode 100644 index 0000000000..ae46c48d74 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/sync.rb @@ -0,0 +1,311 @@ +# +# sync.rb - 2 phase lock with counter +# $Release Version: 1.0$ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ishitsuka.com) +# +# -- +# Sync_m, Synchronizer_m +# Usage: +# obj.extend(Sync_m) +# or +# class Foo +# include Sync_m +# : +# end +# +# Sync_m#sync_mode +# Sync_m#sync_locked?, locked? +# Sync_m#sync_shared?, shared? +# Sync_m#sync_exclusive?, sync_exclusive? +# Sync_m#sync_try_lock, try_lock +# Sync_m#sync_lock, lock +# Sync_m#sync_unlock, unlock +# +# Sync, Synchronicer: +# include Sync_m +# Usage: +# sync = Sync.new +# +# Sync#mode +# Sync#locked? +# Sync#shared? +# Sync#exclusive? +# Sync#try_lock(mode) -- mode = :EX, :SH, :UN +# Sync#lock(mode) -- mode = :EX, :SH, :UN +# Sync#unlock +# Sync#synchronize(mode) {...} +# +# + +unless defined? Thread + fail "Thread not available for this ruby interpreter" +end + +module Sync_m + RCS_ID='-$Header$-' + + # lock mode + UN = :UN + SH = :SH + EX = :EX + + # exceptions + class Err < StandardError + def Err.Fail(*opt) + fail self, sprintf(self::Message, *opt) + end + + class UnknownLocker < Err + Message = "Thread(%s) not locked." + def UnknownLocker.Fail(th) + super(th.inspect) + end + end + + class LockModeFailer < Err + Message = "Unknown lock mode(%s)" + def LockModeFailer.Fail(mode) + if mode.id2name + mode = id2name + end + super(mode) + end + end + end + + def Sync_m.define_aliases(cl) + cl.module_eval %q{ + alias locked? sync_locked? + alias shared? sync_shared? + alias exclusive? sync_exclusive? + alias lock sync_lock + alias unlock sync_unlock + alias try_lock sync_try_lock + alias synchronize sync_synchronize + } + end + + def Sync_m.append_features(cl) + super + unless cl.instance_of?(Module) + # do nothing for Modules + # make aliases and include the proper module. + define_aliases(cl) + end + end + + def Sync_m.extend_object(obj) + super + obj.sync_extended + end + + def sync_extended + unless (defined? locked? and + defined? shared? and + defined? exclusive? and + defined? lock and + defined? unlock and + defined? try_lock and + defined? synchronize) + Sync_m.define_aliases(class< 0 + for k, v in sync_upgrade_waiting + sync_sh_locker[k] = v + end + wait = sync_upgrade_waiting + self.sync_upgrade_waiting = [] + Thread.critical = false + + for w, v in wait + w.run + end + else + wait = sync_waiting + self.sync_waiting = [] + Thread.critical = false + for w in wait + w.run + end + end + end + + Thread.critical = false + self + end + + def sync_synchronize(mode = EX) + begin + sync_lock(mode) + yield + ensure + sync_unlock + end + end + + attr :sync_mode, true + + attr :sync_waiting, true + attr :sync_upgrade_waiting, true + attr :sync_sh_locker, true + attr :sync_ex_locker, true + attr :sync_ex_count, true + + private + + def sync_initialize + @sync_mode = UN + @sync_waiting = [] + @sync_upgrade_waiting = [] + @sync_sh_locker = Hash.new + @sync_ex_locker = nil + @sync_ex_count = 0 + end + + def initialize(*args) + sync_initialize + super + end + + def sync_try_lock_sub(m) + case m + when SH + case sync_mode + when UN + self.sync_mode = m + sync_sh_locker[Thread.current] = 1 + ret = true + when SH + count = 0 unless count = sync_sh_locker[Thread.current] + sync_sh_locker[Thread.current] = count + 1 + ret = true + when EX + # in EX mode, lock will upgrade to EX lock + if sync_ex_locker == Thread.current + self.sync_ex_count = sync_ex_count + 1 + ret = true + else + ret = false + end + end + when EX + if sync_mode == UN or + sync_mode == SH && sync_sh_locker.size == 1 && sync_sh_locker.include?(Thread.current) + self.sync_mode = m + self.sync_ex_locker = Thread.current + self.sync_ex_count = 1 + ret = true + elsif sync_mode == EX && sync_ex_locker == Thread.current + self.sync_ex_count = sync_ex_count + 1 + ret = true + else + ret = false + end + else + Thread.critical = false + Err::LockModeFailer.Fail mode + end + return ret + end +end +Synchronizer_m = Sync_m + +class Sync + #Sync_m.extend_class self + include Sync_m + + def initialize + super + end + +end +Synchronizer = Sync diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/tempfile.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/tempfile.rb new file mode 100644 index 0000000000..b02b2491c2 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/tempfile.rb @@ -0,0 +1,193 @@ +# +# tempfile - manipulates temporary files +# +# $Id: tempfile.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# + +require 'delegate' +require 'tmpdir' + +# A class for managing temporary files. This library is written to be +# thread safe. +class Tempfile < DelegateClass(File) + MAX_TRY = 10 + @@cleanlist = [] + + # Creates a temporary file of mode 0600 in the temporary directory + # whose name is basename.pid.n and opens with mode "w+". A Tempfile + # object works just like a File object. + # + # If tmpdir is omitted, the temporary directory is determined by + # Dir::tmpdir provided by 'tmpdir.rb'. + # When $SAFE > 0 and the given tmpdir is tainted, it uses + # /tmp. (Note that ENV values are tainted by default) + def initialize(basename, tmpdir=Dir::tmpdir) + if $SAFE > 0 and tmpdir.tainted? + tmpdir = '/tmp' + end + + lock = nil + n = failure = 0 + + begin + Thread.critical = true + + begin + tmpname = File.join(tmpdir, make_tmpname(basename, n)) + lock = tmpname + '.lock' + n += 1 + end while @@cleanlist.include?(tmpname) or + File.exist?(lock) or File.exist?(tmpname) + + Dir.mkdir(lock) + rescue + failure += 1 + retry if failure < MAX_TRY + raise "cannot generate tempfile `%s'" % tmpname + ensure + Thread.critical = false + end + + @data = [tmpname] + @clean_proc = Tempfile.callback(@data) + ObjectSpace.define_finalizer(self, @clean_proc) + + @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600) + @tmpname = tmpname + @@cleanlist << @tmpname + @data[1] = @tmpfile + @data[2] = @@cleanlist + + super(@tmpfile) + + # Now we have all the File/IO methods defined, you must not + # carelessly put bare puts(), etc. after this. + + Dir.rmdir(lock) + end + + def make_tmpname(basename, n) + sprintf('%s.%d.%d', basename, $$, n) + end + private :make_tmpname + + # Opens or reopens the file with mode "r+". + def open + @tmpfile.close if @tmpfile + @tmpfile = File.open(@tmpname, 'r+') + @data[1] = @tmpfile + __setobj__(@tmpfile) + end + + def _close # :nodoc: + @tmpfile.close if @tmpfile + @data[1] = @tmpfile = nil + end + protected :_close + + # Closes the file. If the optional flag is true, unlinks the file + # after closing. + # + # If you don't explicitly unlink the temporary file, the removal + # will be delayed until the object is finalized. + def close(unlink_now=false) + if unlink_now + close! + else + _close + end + end + + # Closes and unlinks the file. + def close! + _close + @clean_proc.call + ObjectSpace.undefine_finalizer(self) + end + + # Unlinks the file. On UNIX-like systems, it is often a good idea + # to unlink a temporary file immediately after creating and opening + # it, because it leaves other programs zero chance to access the + # file. + def unlink + # keep this order for thread safeness + begin + File.unlink(@tmpname) if File.exist?(@tmpname) + @@cleanlist.delete(@tmpname) + @data = @tmpname = nil + ObjectSpace.undefine_finalizer(self) + rescue Errno::EACCES + # may not be able to unlink on Windows; just ignore + end + end + alias delete unlink + + # Returns the full path name of the temporary file. + def path + @tmpname + end + + # Returns the size of the temporary file. As a side effect, the IO + # buffer is flushed before determining the size. + def size + if @tmpfile + @tmpfile.flush + @tmpfile.stat.size + else + 0 + end + end + alias length size + + class << self + def callback(data) # :nodoc: + pid = $$ + lambda{ + if pid == $$ + path, tmpfile, cleanlist = *data + + print "removing ", path, "..." if $DEBUG + + tmpfile.close if tmpfile + + # keep this order for thread safeness + File.unlink(path) if File.exist?(path) + cleanlist.delete(path) if cleanlist + + print "done\n" if $DEBUG + end + } + end + + # If no block is given, this is a synonym for new(). + # + # If a block is given, it will be passed tempfile as an argument, + # and the tempfile will automatically be closed when the block + # terminates. In this case, open() returns nil. + def open(*args) + tempfile = new(*args) + + if block_given? + begin + yield(tempfile) + ensure + tempfile.close + end + + nil + else + tempfile + end + end + end +end + +if __FILE__ == $0 +# $DEBUG = true + f = Tempfile.new("foo") + f.print("foo\n") + f.close + f.open + p f.gets # => "foo\n" + f.close! +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit.rb new file mode 100644 index 0000000000..b71f644566 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit.rb @@ -0,0 +1,280 @@ +require 'test/unit/testcase' +require 'test/unit/autorunner' + +module Test # :nodoc: + # + # = Test::Unit - Ruby Unit Testing Framework + # + # == Introduction + # + # Unit testing is making waves all over the place, largely due to the + # fact that it is a core practice of XP. While XP is great, unit testing + # has been around for a long time and has always been a good idea. One + # of the keys to good unit testing, though, is not just writing tests, + # but having tests. What's the difference? Well, if you just _write_ a + # test and throw it away, you have no guarantee that something won't + # change later which breaks your code. If, on the other hand, you _have_ + # tests (obviously you have to write them first), and run them as often + # as possible, you slowly build up a wall of things that cannot break + # without you immediately knowing about it. This is when unit testing + # hits its peak usefulness. + # + # Enter Test::Unit, a framework for unit testing in Ruby, helping you to + # design, debug and evaluate your code by making it easy to write and + # have tests for it. + # + # + # == Notes + # + # Test::Unit has grown out of and superceded Lapidary. + # + # + # == Feedback + # + # I like (and do my best to practice) XP, so I value early releases, + # user feedback, and clean, simple, expressive code. There is always + # room for improvement in everything I do, and Test::Unit is no + # exception. Please, let me know what you think of Test::Unit as it + # stands, and what you'd like to see expanded/changed/improved/etc. If + # you find a bug, let me know ASAP; one good way to let me know what the + # bug is is to submit a new test that catches it :-) Also, I'd love to + # hear about any successes you have with Test::Unit, and any + # documentation you might add will be greatly appreciated. My contact + # info is below. + # + # + # == Contact Information + # + # A lot of discussion happens about Ruby in general on the ruby-talk + # mailing list (http://www.ruby-lang.org/en/ml.html), and you can ask + # any questions you might have there. I monitor the list, as do many + # other helpful Rubyists, and you're sure to get a quick answer. Of + # course, you're also welcome to email me (Nathaniel Talbott) directly + # at mailto:testunit@talbott.ws, and I'll do my best to help you out. + # + # + # == Credits + # + # I'd like to thank... + # + # Matz, for a great language! + # + # Masaki Suketa, for his work on RubyUnit, which filled a vital need in + # the Ruby world for a very long time. I'm also grateful for his help in + # polishing Test::Unit and getting the RubyUnit compatibility layer + # right. His graciousness in allowing Test::Unit to supercede RubyUnit + # continues to be a challenge to me to be more willing to defer my own + # rights. + # + # Ken McKinlay, for his interest and work on unit testing, and for his + # willingness to dialog about it. He was also a great help in pointing + # out some of the holes in the RubyUnit compatibility layer. + # + # Dave Thomas, for the original idea that led to the extremely simple + # "require 'test/unit'", plus his code to improve it even more by + # allowing the selection of tests from the command-line. Also, without + # RDoc, the documentation for Test::Unit would stink a lot more than it + # does now. + # + # Everyone who's helped out with bug reports, feature ideas, + # encouragement to continue, etc. It's a real privilege to be a part of + # the Ruby community. + # + # The guys at RoleModel Software, for putting up with me repeating, "But + # this would be so much easier in Ruby!" whenever we're coding in Java. + # + # My Creator, for giving me life, and giving it more abundantly. + # + # + # == License + # + # Test::Unit is copyright (c) 2000-2003 Nathaniel Talbott. It is free + # software, and is distributed under the Ruby license. See the COPYING + # file in the standard Ruby distribution for details. + # + # + # == Warranty + # + # This software is provided "as is" and without any express or + # implied warranties, including, without limitation, the implied + # warranties of merchantibility and fitness for a particular + # purpose. + # + # + # == Author + # + # Nathaniel Talbott. + # Copyright (c) 2000-2003, Nathaniel Talbott + # + # ---- + # + # = Usage + # + # The general idea behind unit testing is that you write a _test_ + # _method_ that makes certain _assertions_ about your code, working + # against a _test_ _fixture_. A bunch of these _test_ _methods_ are + # bundled up into a _test_ _suite_ and can be run any time the + # developer wants. The results of a run are gathered in a _test_ + # _result_ and displayed to the user through some UI. So, lets break + # this down and see how Test::Unit provides each of these necessary + # pieces. + # + # + # == Assertions + # + # These are the heart of the framework. Think of an assertion as a + # statement of expected outcome, i.e. "I assert that x should be equal + # to y". If, when the assertion is executed, it turns out to be + # correct, nothing happens, and life is good. If, on the other hand, + # your assertion turns out to be false, an error is propagated with + # pertinent information so that you can go back and make your + # assertion succeed, and, once again, life is good. For an explanation + # of the current assertions, see Test::Unit::Assertions. + # + # + # == Test Method & Test Fixture + # + # Obviously, these assertions have to be called within a context that + # knows about them and can do something meaningful with their + # pass/fail value. Also, it's handy to collect a bunch of related + # tests, each test represented by a method, into a common test class + # that knows how to run them. The tests will be in a separate class + # from the code they're testing for a couple of reasons. First of all, + # it allows your code to stay uncluttered with test code, making it + # easier to maintain. Second, it allows the tests to be stripped out + # for deployment, since they're really there for you, the developer, + # and your users don't need them. Third, and most importantly, it + # allows you to set up a common test fixture for your tests to run + # against. + # + # What's a test fixture? Well, tests do not live in a vacuum; rather, + # they're run against the code they are testing. Often, a collection + # of tests will run against a common set of data, also called a + # fixture. If they're all bundled into the same test class, they can + # all share the setting up and tearing down of that data, eliminating + # unnecessary duplication and making it much easier to add related + # tests. + # + # Test::Unit::TestCase wraps up a collection of test methods together + # and allows you to easily set up and tear down the same test fixture + # for each test. This is done by overriding #setup and/or #teardown, + # which will be called before and after each test method that is + # run. The TestCase also knows how to collect the results of your + # assertions into a Test::Unit::TestResult, which can then be reported + # back to you... but I'm getting ahead of myself. To write a test, + # follow these steps: + # + # * Make sure Test::Unit is in your library path. + # * require 'test/unit' in your test script. + # * Create a class that subclasses Test::Unit::TestCase. + # * Add a method that begins with "test" to your class. + # * Make assertions in your test method. + # * Optionally define #setup and/or #teardown to set up and/or tear + # down your common test fixture. + # * You can now run your test as you would any other Ruby + # script... try it and see! + # + # A really simple test might look like this (#setup and #teardown are + # commented out to indicate that they are completely optional): + # + # require 'test/unit' + # + # class TC_MyTest < Test::Unit::TestCase + # # def setup + # # end + # + # # def teardown + # # end + # + # def test_fail + # assert(false, 'Assertion was false.') + # end + # end + # + # + # == Test Runners + # + # So, now you have this great test class, but you still need a way to + # run it and view any failures that occur during the run. This is + # where Test::Unit::UI::Console::TestRunner (and others, such as + # Test::Unit::UI::GTK::TestRunner) comes into play. The console test + # runner is automatically invoked for you if you require 'test/unit' + # and simply run the file. To use another runner, or to manually + # invoke a runner, simply call its run class method and pass in an + # object that responds to the suite message with a + # Test::Unit::TestSuite. This can be as simple as passing in your + # TestCase class (which has a class suite method). It might look + # something like this: + # + # require 'test/unit/ui/console/testrunner' + # Test::Unit::UI::Console::TestRunner.run(TC_MyTest) + # + # + # == Test Suite + # + # As more and more unit tests accumulate for a given project, it + # becomes a real drag running them one at a time, and it also + # introduces the potential to overlook a failing test because you + # forget to run it. Suddenly it becomes very handy that the + # TestRunners can take any object that returns a Test::Unit::TestSuite + # in response to a suite method. The TestSuite can, in turn, contain + # other TestSuites or individual tests (typically created by a + # TestCase). In other words, you can easily wrap up a group of + # TestCases and TestSuites like this: + # + # require 'test/unit/testsuite' + # require 'tc_myfirsttests' + # require 'tc_moretestsbyme' + # require 'ts_anothersetoftests' + # + # class TS_MyTests + # def self.suite + # suite = Test::Unit::TestSuite.new + # suite << TC_MyFirstTests.suite + # suite << TC_MoreTestsByMe.suite + # suite << TS_AnotherSetOfTests.suite + # return suite + # end + # end + # Test::Unit::UI::Console::TestRunner.run(TS_MyTests) + # + # Now, this is a bit cumbersome, so Test::Unit does a little bit more + # for you, by wrapping these up automatically when you require + # 'test/unit'. What does this mean? It means you could write the above + # test case like this instead: + # + # require 'test/unit' + # require 'tc_myfirsttests' + # require 'tc_moretestsbyme' + # require 'ts_anothersetoftests' + # + # Test::Unit is smart enough to find all the test cases existing in + # the ObjectSpace and wrap them up into a suite for you. It then runs + # the dynamic suite using the console TestRunner. + # + # + # == Questions? + # + # I'd really like to get feedback from all levels of Ruby + # practitioners about typos, grammatical errors, unclear statements, + # missing points, etc., in this document (or any other). + # + + module Unit + # If set to false Test::Unit will not automatically run at exit. + def self.run=(flag) + @run = flag + end + + # Automatically run tests at exit? + def self.run? + @run ||= false + end + end +end + +at_exit do + unless $! || Test::Unit.run? + exit Test::Unit::AutoRunner.run + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/assertionfailederror.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/assertionfailederror.rb new file mode 100644 index 0000000000..a21e4b5870 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/assertionfailederror.rb @@ -0,0 +1,14 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +module Test + module Unit + + # Thrown by Test::Unit::Assertions when an assertion fails. + class AssertionFailedError < StandardError + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/assertions.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/assertions.rb new file mode 100644 index 0000000000..aa97799de8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/assertions.rb @@ -0,0 +1,622 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/assertionfailederror' +require 'test/unit/util/backtracefilter' + +module Test + module Unit + + ## + # Test::Unit::Assertions contains the standard Test::Unit assertions. + # Assertions is included in Test::Unit::TestCase. + # + # To include it in your own code and use its functionality, you simply + # need to rescue Test::Unit::AssertionFailedError. Additionally you may + # override add_assertion to get notified whenever an assertion is made. + # + # Notes: + # * The message to each assertion, if given, will be propagated with the + # failure. + # * It is easy to add your own assertions based on assert_block(). + # + # = Example Custom Assertion + # + # def deny(boolean, message = nil) + # message = build_message message, ' is not false or nil.', boolean + # assert_block message do + # not boolean + # end + # end + + module Assertions + + ## + # The assertion upon which all other assertions are based. Passes if the + # block yields true. + # + # Example: + # assert_block "Couldn't do the thing" do + # do_the_thing + # end + + public + def assert_block(message="assert_block failed.") # :yields: + _wrap_assertion do + if (! yield) + raise AssertionFailedError.new(message.to_s) + end + end + end + + ## + # Asserts that +boolean+ is not false or nil. + # + # Example: + # assert [1, 2].include?(5) + + public + def assert(boolean, message=nil) + _wrap_assertion do + assert_block("assert should not be called with a block.") { !block_given? } + assert_block(build_message(message, " is not true.", boolean)) { boolean } + end + end + + ## + # Passes if +expected+ == +actual. + # + # Note that the ordering of arguments is important, since a helpful + # error message is generated when this one fails that tells you the + # values of expected and actual. + # + # Example: + # assert_equal 'MY STRING', 'my string'.upcase + + public + def assert_equal(expected, actual, message=nil) + full_message = build_message(message, < expected but was +. +EOT + assert_block(full_message) { expected == actual } + end + + private + def _check_exception_class(args) # :nodoc: + args.partition do |klass| + next if klass.instance_of?(Module) + assert(Exception >= klass, "Should expect a class of exception, #{klass}") + true + end + end + + private + def _expected_exception?(actual_exception, exceptions, modules) # :nodoc: + exceptions.include?(actual_exception.class) or + modules.any? {|mod| actual_exception.is_a?(mod)} + end + + ## + # Passes if the block raises one of the given exceptions. + # + # Example: + # assert_raise RuntimeError, LoadError do + # raise 'Boom!!!' + # end + + public + def assert_raise(*args) + _wrap_assertion do + if Module === args.last + message = "" + else + message = args.pop + end + exceptions, modules = _check_exception_class(args) + expected = args.size == 1 ? args.first : args + actual_exception = nil + full_message = build_message(message, " exception expected but none was thrown.", expected) + assert_block(full_message) do + begin + yield + rescue Exception => actual_exception + break + end + false + end + full_message = build_message(message, " exception expected but was\n?", expected, actual_exception) + assert_block(full_message) {_expected_exception?(actual_exception, exceptions, modules)} + actual_exception + end + end + + ## + # Alias of assert_raise. + # + # Will be deprecated in 1.9, and removed in 2.0. + + public + def assert_raises(*args, &block) + assert_raise(*args, &block) + end + + ## + # Passes if +object+ .instance_of? +klass+ + # + # Example: + # assert_instance_of String, 'foo' + + public + def assert_instance_of(klass, object, message="") + _wrap_assertion do + assert_equal(Class, klass.class, "assert_instance_of takes a Class as its first argument") + full_message = build_message(message, < expected to be an instance of + but was +. +EOT + assert_block(full_message){object.instance_of?(klass)} + end + end + + ## + # Passes if +object+ is nil. + # + # Example: + # assert_nil [1, 2].uniq! + + public + def assert_nil(object, message="") + assert_equal(nil, object, message) + end + + ## + # Passes if +object+ .kind_of? +klass+ + # + # Example: + # assert_kind_of Object, 'foo' + + public + def assert_kind_of(klass, object, message="") + _wrap_assertion do + assert(klass.kind_of?(Module), "The first parameter to assert_kind_of should be a kind_of Module.") + full_message = build_message(message, "\nexpected to be kind_of\\?\n but was\n.", object, klass, object.class) + assert_block(full_message){object.kind_of?(klass)} + end + end + + ## + # Passes if +object+ .respond_to? +method+ + # + # Example: + # assert_respond_to 'bugbear', :slice + + public + def assert_respond_to(object, method, message="") + _wrap_assertion do + full_message = build_message(nil, "\ngiven as the method name argument to #assert_respond_to must be a Symbol or #respond_to\\?(:to_str).", method) + + assert_block(full_message) do + method.kind_of?(Symbol) || method.respond_to?(:to_str) + end + full_message = build_message(message, < +of type +expected to respond_to\\?. +EOT + assert_block(full_message) { object.respond_to?(method) } + end + end + + ## + # Passes if +string+ =~ +pattern+. + # + # Example: + # assert_match(/\d+/, 'five, 6, seven') + + public + def assert_match(pattern, string, message="") + _wrap_assertion do + pattern = case(pattern) + when String + Regexp.new(Regexp.escape(pattern)) + else + pattern + end + full_message = build_message(message, " expected to be =~\n.", string, pattern) + assert_block(full_message) { string =~ pattern } + end + end + + ## + # Passes if +actual+ .equal? +expected+ (i.e. they are the same + # instance). + # + # Example: + # o = Object.new + # assert_same o, o + + public + def assert_same(expected, actual, message="") + full_message = build_message(message, < +with id expected to be equal\\? to + +with id . +EOT + assert_block(full_message) { actual.equal?(expected) } + end + + ## + # Compares the +object1+ with +object2+ using +operator+. + # + # Passes if object1.__send__(operator, object2) is true. + # + # Example: + # assert_operator 5, :>=, 4 + + public + def assert_operator(object1, operator, object2, message="") + _wrap_assertion do + full_message = build_message(nil, "\ngiven as the operator for #assert_operator must be a Symbol or #respond_to\\?(:to_str).", operator) + assert_block(full_message){operator.kind_of?(Symbol) || operator.respond_to?(:to_str)} + full_message = build_message(message, < expected to be +? +. +EOT + assert_block(full_message) { object1.__send__(operator, object2) } + end + end + + ## + # Passes if block does not raise an exception. + # + # Example: + # assert_nothing_raised do + # [1, 2].uniq + # end + + public + def assert_nothing_raised(*args) + _wrap_assertion do + if Module === args.last + message = "" + else + message = args.pop + end + exceptions, modules = _check_exception_class(args) + begin + yield + rescue Exception => e + if ((args.empty? && !e.instance_of?(AssertionFailedError)) || + _expected_exception?(e, exceptions, modules)) + assert_block(build_message(message, "Exception raised:\n?", e)){false} + else + raise + end + end + nil + end + end + + ## + # Flunk always fails. + # + # Example: + # flunk 'Not done testing yet.' + + public + def flunk(message="Flunked") + assert_block(build_message(message)){false} + end + + ## + # Passes if ! +actual+ .equal? +expected+ + # + # Example: + # assert_not_same Object.new, Object.new + + public + def assert_not_same(expected, actual, message="") + full_message = build_message(message, < +with id expected to not be equal\\? to + +with id . +EOT + assert_block(full_message) { !actual.equal?(expected) } + end + + ## + # Passes if +expected+ != +actual+ + # + # Example: + # assert_not_equal 'some string', 5 + + public + def assert_not_equal(expected, actual, message="") + full_message = build_message(message, " expected to be != to\n.", expected, actual) + assert_block(full_message) { expected != actual } + end + + ## + # Passes if ! +object+ .nil? + # + # Example: + # assert_not_nil '1 two 3'.sub!(/two/, '2') + + public + def assert_not_nil(object, message="") + full_message = build_message(message, " expected to not be nil.", object) + assert_block(full_message){!object.nil?} + end + + ## + # Passes if +regexp+ !~ +string+ + # + # Example: + # assert_no_match(/two/, 'one 2 three') + + public + def assert_no_match(regexp, string, message="") + _wrap_assertion do + assert_instance_of(Regexp, regexp, "The first argument to assert_no_match should be a Regexp.") + full_message = build_message(message, " expected to not match\n.", regexp, string) + assert_block(full_message) { regexp !~ string } + end + end + + UncaughtThrow = {NameError => /^uncaught throw \`(.+)\'$/, + ThreadError => /^uncaught throw \`(.+)\' in thread /} #` + + ## + # Passes if the block throws +expected_symbol+ + # + # Example: + # assert_throws :done do + # throw :done + # end + + public + def assert_throws(expected_symbol, message="", &proc) + _wrap_assertion do + assert_instance_of(Symbol, expected_symbol, "assert_throws expects the symbol that should be thrown for its first argument") + assert_block("Should have passed a block to assert_throws."){block_given?} + caught = true + begin + catch(expected_symbol) do + proc.call + caught = false + end + full_message = build_message(message, " should have been thrown.", expected_symbol) + assert_block(full_message){caught} + rescue NameError, ThreadError => error + if UncaughtThrow[error.class] !~ error.message + raise error + end + full_message = build_message(message, " expected to be thrown but\n was thrown.", expected_symbol, $1.intern) + flunk(full_message) + end + end + end + + ## + # Passes if block does not throw anything. + # + # Example: + # assert_nothing_thrown do + # [1, 2].uniq + # end + + public + def assert_nothing_thrown(message="", &proc) + _wrap_assertion do + assert(block_given?, "Should have passed a block to assert_nothing_thrown") + begin + proc.call + rescue NameError, ThreadError => error + if UncaughtThrow[error.class] !~ error.message + raise error + end + full_message = build_message(message, " was thrown when nothing was expected", $1.intern) + flunk(full_message) + end + assert(true, "Expected nothing to be thrown") + end + end + + ## + # Passes if +expected_float+ and +actual_float+ are equal + # within +delta+ tolerance. + # + # Example: + # assert_in_delta 0.05, (50000.0 / 10**6), 0.00001 + + public + def assert_in_delta(expected_float, actual_float, delta, message="") + _wrap_assertion do + {expected_float => "first float", actual_float => "second float", delta => "delta"}.each do |float, name| + assert_respond_to(float, :to_f, "The arguments must respond to to_f; the #{name} did not") + end + assert_operator(delta, :>=, 0.0, "The delta should not be negative") + full_message = build_message(message, < and + expected to be within + of each other. +EOT + assert_block(full_message) { (expected_float.to_f - actual_float.to_f).abs <= delta.to_f } + end + end + + ## + # Passes if the method send returns a true value. + # + # +send_array+ is composed of: + # * A receiver + # * A method + # * Arguments to the method + # + # Example: + # assert_send [[1, 2], :include?, 4] + + public + def assert_send(send_array, message="") + _wrap_assertion do + assert_instance_of(Array, send_array, "assert_send requires an array of send information") + assert(send_array.size >= 2, "assert_send requires at least a receiver and a message name") + full_message = build_message(message, < expected to respond to + with a true value. +EOT + assert_block(full_message) { send_array[0].__send__(send_array[1], *send_array[2..-1]) } + end + end + + ## + # Builds a failure message. +head+ is added before the +template+ and + # +arguments+ replaces the '?'s positionally in the template. + + public + def build_message(head, template=nil, *arguments) + template &&= template.chomp + return AssertionMessage.new(head, template, arguments) + end + + private + def _wrap_assertion + @_assertion_wrapped ||= false + unless (@_assertion_wrapped) + @_assertion_wrapped = true + begin + add_assertion + return yield + ensure + @_assertion_wrapped = false + end + else + return yield + end + end + + ## + # Called whenever an assertion is made. Define this in classes that + # include Test::Unit::Assertions to record assertion counts. + + private + def add_assertion + end + + ## + # Select whether or not to use the pretty-printer. If this option is set + # to false before any assertions are made, pp.rb will not be required. + + public + def self.use_pp=(value) + AssertionMessage.use_pp = value + end + + # :stopdoc: + + class AssertionMessage + @use_pp = true + class << self + attr_accessor :use_pp + end + + class Literal + def initialize(value) + @value = value + end + + def inspect + @value.to_s + end + end + + class Template + def self.create(string) + parts = (string ? string.scan(/(?=[^\\])\?|(?:\\\?|[^\?])+/m) : []) + self.new(parts) + end + + attr_reader :count + + def initialize(parts) + @parts = parts + @count = parts.find_all{|e| e == '?'}.size + end + + def result(parameters) + raise "The number of parameters does not match the number of substitutions." if(parameters.size != count) + params = parameters.dup + @parts.collect{|e| e == '?' ? params.shift : e.gsub(/\\\?/m, '?')}.join('') + end + end + + def self.literal(value) + Literal.new(value) + end + + include Util::BacktraceFilter + + def initialize(head, template_string, parameters) + @head = head + @template_string = template_string + @parameters = parameters + end + + def convert(object) + case object + when Exception + < +Message: <#{convert(object.message)}> +---Backtrace--- +#{filter_backtrace(object.backtrace).join("\n")} +--------------- +EOM + else + if(self.class.use_pp) + begin + require 'pp' + rescue LoadError + self.class.use_pp = false + return object.inspect + end unless(defined?(PP)) + PP.pp(object, '').chomp + else + object.inspect + end + end + end + + def template + @template ||= Template.create(@template_string) + end + + def add_period(string) + (string =~ /\.\Z/ ? string : string + '.') + end + + def to_s + message_parts = [] + if (@head) + head = @head.to_s + unless(head.empty?) + message_parts << add_period(head) + end + end + tail = template.result(@parameters.collect{|e| convert(e)}) + message_parts << tail unless(tail.empty?) + message_parts.join("\n") + end + end + + # :startdoc: + + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/autorunner.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/autorunner.rb new file mode 100644 index 0000000000..86c9b12940 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/autorunner.rb @@ -0,0 +1,220 @@ +require 'test/unit' +require 'test/unit/ui/testrunnerutilities' +require 'optparse' + +module Test + module Unit + class AutoRunner + def self.run(force_standalone=false, default_dir=nil, argv=ARGV, &block) + r = new(force_standalone || standalone?, &block) + r.base = default_dir + r.process_args(argv) + r.run + end + + def self.standalone? + return false unless("-e" == $0) + ObjectSpace.each_object(Class) do |klass| + return false if(klass < TestCase) + end + true + end + + RUNNERS = { + :console => proc do |r| + require 'test/unit/ui/console/testrunner' + Test::Unit::UI::Console::TestRunner + end, + :gtk => proc do |r| + require 'test/unit/ui/gtk/testrunner' + Test::Unit::UI::GTK::TestRunner + end, + :gtk2 => proc do |r| + require 'test/unit/ui/gtk2/testrunner' + Test::Unit::UI::GTK2::TestRunner + end, + :fox => proc do |r| + require 'test/unit/ui/fox/testrunner' + Test::Unit::UI::Fox::TestRunner + end, + :tk => proc do |r| + require 'test/unit/ui/tk/testrunner' + Test::Unit::UI::Tk::TestRunner + end, + } + + OUTPUT_LEVELS = [ + [:silent, UI::SILENT], + [:progress, UI::PROGRESS_ONLY], + [:normal, UI::NORMAL], + [:verbose, UI::VERBOSE], + ] + + COLLECTORS = { + :objectspace => proc do |r| + require 'test/unit/collector/objectspace' + c = Collector::ObjectSpace.new + c.filter = r.filters + c.collect($0.sub(/\.rb\Z/, '')) + end, + :dir => proc do |r| + require 'test/unit/collector/dir' + c = Collector::Dir.new + c.filter = r.filters + c.pattern.concat(r.pattern) if(r.pattern) + c.exclude.concat(r.exclude) if(r.exclude) + c.base = r.base + $:.push(r.base) if r.base + c.collect(*(r.to_run.empty? ? ['.'] : r.to_run)) + end, + } + + attr_reader :suite + attr_accessor :output_level, :filters, :to_run, :pattern, :exclude, :base, :workdir + attr_writer :runner, :collector + + def initialize(standalone) + Unit.run = true + @standalone = standalone + @runner = RUNNERS[:console] + @collector = COLLECTORS[(standalone ? :dir : :objectspace)] + @filters = [] + @to_run = [] + @output_level = UI::NORMAL + @workdir = nil + yield(self) if(block_given?) + end + + def process_args(args = ARGV) + begin + options.order!(args) {|arg| @to_run << arg} + rescue OptionParser::ParseError => e + puts e + puts options + $! = nil + abort + else + @filters << proc{false} unless(@filters.empty?) + end + not @to_run.empty? + end + + def options + @options ||= OptionParser.new do |o| + o.banner = "Test::Unit automatic runner." + o.banner << "\nUsage: #{$0} [options] [-- untouched arguments]" + + o.on + o.on('-r', '--runner=RUNNER', RUNNERS, + "Use the given RUNNER.", + "(" + keyword_display(RUNNERS) + ")") do |r| + @runner = r + end + + if(@standalone) + o.on('-b', '--basedir=DIR', "Base directory of test suites.") do |b| + @base = b + end + + o.on('-w', '--workdir=DIR', "Working directory to run tests.") do |w| + @workdir = w + end + + o.on('-a', '--add=TORUN', Array, + "Add TORUN to the list of things to run;", + "can be a file or a directory.") do |a| + @to_run.concat(a) + end + + @pattern = [] + o.on('-p', '--pattern=PATTERN', Regexp, + "Match files to collect against PATTERN.") do |e| + @pattern << e + end + + @exclude = [] + o.on('-x', '--exclude=PATTERN', Regexp, + "Ignore files to collect against PATTERN.") do |e| + @exclude << e + end + end + + o.on('-n', '--name=NAME', String, + "Runs tests matching NAME.", + "(patterns may be used).") do |n| + n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n) + case n + when Regexp + @filters << proc{|t| n =~ t.method_name ? true : nil} + else + @filters << proc{|t| n == t.method_name ? true : nil} + end + end + + o.on('-t', '--testcase=TESTCASE', String, + "Runs tests in TestCases matching TESTCASE.", + "(patterns may be used).") do |n| + n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n) + case n + when Regexp + @filters << proc{|t| n =~ t.class.name ? true : nil} + else + @filters << proc{|t| n == t.class.name ? true : nil} + end + end + + o.on('-I', "--load-path=DIR[#{File::PATH_SEPARATOR}DIR...]", + "Appends directory list to $LOAD_PATH.") do |dirs| + $LOAD_PATH.concat(dirs.split(File::PATH_SEPARATOR)) + end + + o.on('-v', '--verbose=[LEVEL]', OUTPUT_LEVELS, + "Set the output level (default is verbose).", + "(" + keyword_display(OUTPUT_LEVELS) + ")") do |l| + @output_level = l || UI::VERBOSE + end + + o.on('--', + "Stop processing options so that the", + "remaining options will be passed to the", + "test."){o.terminate} + + o.on('-h', '--help', 'Display this help.'){puts o; exit} + + o.on_tail + o.on_tail('Deprecated options:') + + o.on_tail('--console', 'Console runner (use --runner).') do + warn("Deprecated option (--console).") + @runner = RUNNERS[:console] + end + + o.on_tail('--gtk', 'GTK runner (use --runner).') do + warn("Deprecated option (--gtk).") + @runner = RUNNERS[:gtk] + end + + o.on_tail('--fox', 'Fox runner (use --runner).') do + warn("Deprecated option (--fox).") + @runner = RUNNERS[:fox] + end + + o.on_tail + end + end + + def keyword_display(array) + list = array.collect {|e, *| e.to_s} + Array === array or list.sort! + list.collect {|e| e.sub(/^(.)([A-Za-z]+)(?=\w*$)/, '\\1[\\2]')}.join(", ") + end + + def run + @suite = @collector[self] + result = @runner[self] or return false + Dir.chdir(@workdir) if @workdir + result.run(@suite, @output_level).passed? + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/collector.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/collector.rb new file mode 100644 index 0000000000..9e9e654147 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/collector.rb @@ -0,0 +1,43 @@ +module Test + module Unit + module Collector + def initialize + @filters = [] + end + + def filter=(filters) + @filters = case(filters) + when Proc + [filters] + when Array + filters + end + end + + def add_suite(destination, suite) + to_delete = suite.tests.find_all{|t| !include?(t)} + to_delete.each{|t| suite.delete(t)} + destination << suite unless(suite.size == 0) + end + + def include?(test) + return true if(@filters.empty?) + @filters.each do |filter| + result = filter[test] + if(result.nil?) + next + elsif(!result) + return false + else + return true + end + end + true + end + + def sort(suites) + suites.sort_by{|s| s.name} + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/collector/dir.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/collector/dir.rb new file mode 100644 index 0000000000..97c8d28481 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/collector/dir.rb @@ -0,0 +1,107 @@ +require 'test/unit/testsuite' +require 'test/unit/collector' + +module Test + module Unit + module Collector + class Dir + include Collector + + attr_reader :pattern, :exclude + attr_accessor :base + + def initialize(dir=::Dir, file=::File, object_space=::ObjectSpace, req=nil) + super() + @dir = dir + @file = file + @object_space = object_space + @req = req + @pattern = [/\btest_.*\.rb\Z/m] + @exclude = [] + end + + def collect(*from) + basedir = @base + $:.push(basedir) if basedir + if(from.empty?) + recursive_collect('.', find_test_cases) + elsif(from.size == 1) + recursive_collect(from.first, find_test_cases) + else + suites = [] + from.each do |f| + suite = recursive_collect(f, find_test_cases) + suites << suite unless(suite.tests.empty?) + end + suite = TestSuite.new("[#{from.join(', ')}]") + sort(suites).each{|s| suite << s} + suite + end + ensure + $:.delete_at($:.rindex(basedir)) if basedir + end + + def find_test_cases(ignore=[]) + cases = [] + @object_space.each_object(Class) do |c| + cases << c if(c < TestCase && !ignore.include?(c)) + end + ignore.concat(cases) + cases + end + + def recursive_collect(name, already_gathered) + sub_suites = [] + path = realdir(name) + if @file.directory?(path) + dir_name = name unless name == '.' + @dir.entries(path).each do |e| + next if(e == '.' || e == '..') + e_name = dir_name ? @file.join(dir_name, e) : e + if @file.directory?(realdir(e_name)) + next if /\ACVS\z/ =~ e + sub_suite = recursive_collect(e_name, already_gathered) + sub_suites << sub_suite unless(sub_suite.empty?) + else + next if /~\z/ =~ e_name or /\A\.\#/ =~ e + if @pattern and !@pattern.empty? + next unless @pattern.any? {|pat| pat =~ e_name} + end + if @exclude and !@exclude.empty? + next if @exclude.any? {|pat| pat =~ e_name} + end + collect_file(e_name, sub_suites, already_gathered) + end + end + else + collect_file(name, sub_suites, already_gathered) + end + suite = TestSuite.new(@file.basename(name)) + sort(sub_suites).each{|s| suite << s} + suite + end + + def collect_file(name, suites, already_gathered) + dir = @file.dirname(@file.expand_path(name, @base)) + $:.unshift(dir) + if(@req) + @req.require(name) + else + require(name) + end + find_test_cases(already_gathered).each{|t| add_suite(suites, t.suite)} + ensure + $:.delete_at($:.rindex(dir)) if(dir) + end + + def realdir(path) + if @base + @file.join(@base, path) + else + path + end + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/collector/objectspace.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/collector/objectspace.rb new file mode 100644 index 0000000000..d1127a981f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/collector/objectspace.rb @@ -0,0 +1,34 @@ +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/collector' + +module Test + module Unit + module Collector + class ObjectSpace + include Collector + + NAME = 'collected from the ObjectSpace' + + def initialize(source=::ObjectSpace) + super() + @source = source + end + + def collect(name=NAME) + suite = TestSuite.new(name) + sub_suites = [] + @source.each_object(Class) do |klass| + if(Test::Unit::TestCase > klass) + add_suite(sub_suites, klass.suite) + end + end + sort(sub_suites).each{|s| suite << s} + suite + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/error.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/error.rb new file mode 100644 index 0000000000..43a813f7d1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/error.rb @@ -0,0 +1,56 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/util/backtracefilter' + +module Test + module Unit + + # Encapsulates an error in a test. Created by + # Test::Unit::TestCase when it rescues an exception thrown + # during the processing of a test. + class Error + include Util::BacktraceFilter + + attr_reader(:test_name, :exception) + + SINGLE_CHARACTER = 'E' + + # Creates a new Error with the given test_name and + # exception. + def initialize(test_name, exception) + @test_name = test_name + @exception = exception + end + + # Returns a single character representation of an error. + def single_character_display + SINGLE_CHARACTER + end + + # Returns the message associated with the error. + def message + "#{@exception.class.name}: #{@exception.message}" + end + + # Returns a brief version of the error description. + def short_display + "#@test_name: #{message.split("\n")[0]}" + end + + # Returns a verbose version of the error description. + def long_display + backtrace = filter_backtrace(@exception.backtrace).join("\n ") + "Error:\n#@test_name:\n#{message}\n #{backtrace}" + end + + # Overridden to return long_display. + def to_s + long_display + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/failure.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/failure.rb new file mode 100644 index 0000000000..832c99857c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/failure.rb @@ -0,0 +1,51 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +module Test + module Unit + + # Encapsulates a test failure. Created by Test::Unit::TestCase + # when an assertion fails. + class Failure + attr_reader :test_name, :location, :message + + SINGLE_CHARACTER = 'F' + + # Creates a new Failure with the given location and + # message. + def initialize(test_name, location, message) + @test_name = test_name + @location = location + @message = message + end + + # Returns a single character representation of a failure. + def single_character_display + SINGLE_CHARACTER + end + + # Returns a brief version of the error description. + def short_display + "#@test_name: #{@message.split("\n")[0]}" + end + + # Returns a verbose version of the error description. + def long_display + location_display = if(location.size == 1) + location[0].sub(/\A(.+:\d+).*/, ' [\\1]') + else + "\n [#{location.join("\n ")}]" + end + "Failure:\n#@test_name#{location_display}:\n#@message" + end + + # Overridden to return long_display. + def to_s + long_display + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/testcase.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/testcase.rb new file mode 100644 index 0000000000..f53b460c5d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/testcase.rb @@ -0,0 +1,160 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/assertions' +require 'test/unit/failure' +require 'test/unit/error' +require 'test/unit/testsuite' +require 'test/unit/assertionfailederror' +require 'test/unit/util/backtracefilter' + +module Test + module Unit + + # Ties everything together. If you subclass and add your own + # test methods, it takes care of making them into tests and + # wrapping those tests into a suite. It also does the + # nitty-gritty of actually running an individual test and + # collecting its results into a Test::Unit::TestResult object. + class TestCase + include Assertions + include Util::BacktraceFilter + + attr_reader :method_name + + STARTED = name + "::STARTED" + FINISHED = name + "::FINISHED" + + ## + # These exceptions are not caught by #run. + + PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, Interrupt, + SystemExit] + + # Creates a new instance of the fixture for running the + # test represented by test_method_name. + def initialize(test_method_name) + unless(respond_to?(test_method_name) and + (method(test_method_name).arity == 0 || + method(test_method_name).arity == -1)) + throw :invalid_test + end + @method_name = test_method_name + @test_passed = true + end + + # Rolls up all of the test* methods in the fixture into + # one suite, creating a new instance of the fixture for + # each method. + def self.suite + method_names = public_instance_methods(true) + tests = method_names.delete_if {|method_name| method_name !~ /^test./} + suite = TestSuite.new(name) + tests.sort.each do + |test| + catch(:invalid_test) do + suite << new(test) + end + end + if (suite.empty?) + catch(:invalid_test) do + suite << new("default_test") + end + end + return suite + end + + # Runs the individual test method represented by this + # instance of the fixture, collecting statistics, failures + # and errors in result. + def run(result) + yield(STARTED, name) + @_result = result + begin + setup + __send__(@method_name) + rescue AssertionFailedError => e + add_failure(e.message, e.backtrace) + rescue Exception + raise if PASSTHROUGH_EXCEPTIONS.include? $!.class + add_error($!) + ensure + begin + teardown + rescue AssertionFailedError => e + add_failure(e.message, e.backtrace) + rescue Exception + raise if PASSTHROUGH_EXCEPTIONS.include? $!.class + add_error($!) + end + end + result.add_run + yield(FINISHED, name) + end + + # Called before every test method runs. Can be used + # to set up fixture information. + def setup + end + + # Called after every test method runs. Can be used to tear + # down fixture information. + def teardown + end + + def default_test + flunk("No tests were specified") + end + + # Returns whether this individual test passed or + # not. Primarily for use in teardown so that artifacts + # can be left behind if the test fails. + def passed? + return @test_passed + end + private :passed? + + def size + 1 + end + + def add_assertion + @_result.add_assertion + end + private :add_assertion + + def add_failure(message, all_locations=caller()) + @test_passed = false + @_result.add_failure(Failure.new(name, filter_backtrace(all_locations), message)) + end + private :add_failure + + def add_error(exception) + @test_passed = false + @_result.add_error(Error.new(name, exception)) + end + private :add_error + + # Returns a human-readable name for the specific test that + # this instance of TestCase represents. + def name + "#{@method_name}(#{self.class.name})" + end + + # Overridden to return #name. + def to_s + name + end + + # It's handy to be able to compare TestCase instances. + def ==(other) + return false unless(other.kind_of?(self.class)) + return false unless(@method_name == other.method_name) + self.class == other.class + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/testresult.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/testresult.rb new file mode 100644 index 0000000000..e3a472ef7f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/testresult.rb @@ -0,0 +1,80 @@ +#-- +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/util/observable' + +module Test + module Unit + + # Collects Test::Unit::Failure and Test::Unit::Error so that + # they can be displayed to the user. To this end, observers + # can be added to it, allowing the dynamic updating of, say, a + # UI. + class TestResult + include Util::Observable + + CHANGED = "CHANGED" + FAULT = "FAULT" + + attr_reader(:run_count, :assertion_count) + + # Constructs a new, empty TestResult. + def initialize + @run_count, @assertion_count = 0, 0 + @failures, @errors = Array.new, Array.new + end + + # Records a test run. + def add_run + @run_count += 1 + notify_listeners(CHANGED, self) + end + + # Records a Test::Unit::Failure. + def add_failure(failure) + @failures << failure + notify_listeners(FAULT, failure) + notify_listeners(CHANGED, self) + end + + # Records a Test::Unit::Error. + def add_error(error) + @errors << error + notify_listeners(FAULT, error) + notify_listeners(CHANGED, self) + end + + # Records an individual assertion. + def add_assertion + @assertion_count += 1 + notify_listeners(CHANGED, self) + end + + # Returns a string contain the recorded runs, assertions, + # failures and errors in this TestResult. + def to_s + "#{run_count} tests, #{assertion_count} assertions, #{failure_count} failures, #{error_count} errors" + end + + # Returns whether or not this TestResult represents + # successful completion. + def passed? + return @failures.empty? && @errors.empty? + end + + # Returns the number of failures this TestResult has + # recorded. + def failure_count + return @failures.size + end + + # Returns the number of errors this TestResult has + # recorded. + def error_count + return @errors.size + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/testsuite.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/testsuite.rb new file mode 100644 index 0000000000..6fea976c50 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/testsuite.rb @@ -0,0 +1,76 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +module Test + module Unit + + # A collection of tests which can be #run. + # + # Note: It is easy to confuse a TestSuite instance with + # something that has a static suite method; I know because _I_ + # have trouble keeping them straight. Think of something that + # has a suite method as simply providing a way to get a + # meaningful TestSuite instance. + class TestSuite + attr_reader :name, :tests + + STARTED = name + "::STARTED" + FINISHED = name + "::FINISHED" + + # Creates a new TestSuite with the given name. + def initialize(name="Unnamed TestSuite") + @name = name + @tests = [] + end + + # Runs the tests and/or suites contained in this + # TestSuite. + def run(result, &progress_block) + yield(STARTED, name) + @tests.each do |test| + test.run(result, &progress_block) + end + yield(FINISHED, name) + end + + # Adds the test to the suite. + def <<(test) + @tests << test + self + end + + def delete(test) + @tests.delete(test) + end + + # Retuns the rolled up number of tests in this suite; + # i.e. if the suite contains other suites, it counts the + # tests within those suites, not the suites themselves. + def size + total_size = 0 + @tests.each { |test| total_size += test.size } + total_size + end + + def empty? + tests.empty? + end + + # Overridden to return the name given the suite at + # creation. + def to_s + @name + end + + # It's handy to be able to compare TestSuite instances. + def ==(other) + return false unless(other.kind_of?(self.class)) + return false unless(@name == other.name) + @tests == other.tests + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/console/testrunner.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/console/testrunner.rb new file mode 100644 index 0000000000..6b600e319a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/console/testrunner.rb @@ -0,0 +1,127 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/ui/testrunnermediator' +require 'test/unit/ui/testrunnerutilities' + +module Test + module Unit + module UI + module Console + + # Runs a Test::Unit::TestSuite on the console. + class TestRunner + extend TestRunnerUtilities + + # Creates a new TestRunner for running the passed + # suite. If quiet_mode is true, the output while + # running is limited to progress dots, errors and + # failures, and the final result. io specifies + # where runner output should go to; defaults to + # STDOUT. + def initialize(suite, output_level=NORMAL, io=STDOUT) + if (suite.respond_to?(:suite)) + @suite = suite.suite + else + @suite = suite + end + @output_level = output_level + @io = io + @already_outputted = false + @faults = [] + end + + # Begins the test run. + def start + setup_mediator + attach_to_mediator + return start_mediator + end + + private + def setup_mediator + @mediator = create_mediator(@suite) + suite_name = @suite.to_s + if ( @suite.kind_of?(Module) ) + suite_name = @suite.name + end + output("Loaded suite #{suite_name}") + end + + def create_mediator(suite) + return TestRunnerMediator.new(suite) + end + + def attach_to_mediator + @mediator.add_listener(TestResult::FAULT, &method(:add_fault)) + @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started)) + @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished)) + @mediator.add_listener(TestCase::STARTED, &method(:test_started)) + @mediator.add_listener(TestCase::FINISHED, &method(:test_finished)) + end + + def start_mediator + return @mediator.run_suite + end + + def add_fault(fault) + @faults << fault + output_single(fault.single_character_display, PROGRESS_ONLY) + @already_outputted = true + end + + def started(result) + @result = result + output("Started") + end + + def finished(elapsed_time) + nl + output("Finished in #{elapsed_time} seconds.") + @faults.each_with_index do |fault, index| + nl + output("%3d) %s" % [index + 1, fault.long_display]) + end + nl + output(@result) + end + + def test_started(name) + output_single(name + ": ", VERBOSE) + end + + def test_finished(name) + output_single(".", PROGRESS_ONLY) unless (@already_outputted) + nl(VERBOSE) + @already_outputted = false + end + + def nl(level=NORMAL) + output("", level) + end + + def output(something, level=NORMAL) + @io.puts(something) if (output?(level)) + @io.flush + end + + def output_single(something, level=NORMAL) + @io.write(something) if (output?(level)) + @io.flush + end + + def output?(level) + level <= @output_level + end + end + end + end + end +end + +if __FILE__ == $0 + Test::Unit::UI::Console::TestRunner.start_command_line_test +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/fox/testrunner.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/fox/testrunner.rb new file mode 100644 index 0000000000..a23a450567 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/fox/testrunner.rb @@ -0,0 +1,268 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'fox' +require 'test/unit/ui/testrunnermediator' +require 'test/unit/ui/testrunnerutilities' + +include Fox + +module Test + module Unit + module UI + module Fox + + # Runs a Test::Unit::TestSuite in a Fox UI. Obviously, + # this one requires you to have Fox + # (http://www.fox-toolkit.org/fox.html) and the Ruby + # Fox extension (http://fxruby.sourceforge.net/) + # installed. + class TestRunner + + extend TestRunnerUtilities + + RED_STYLE = FXRGBA(0xFF,0,0,0xFF) #0xFF000000 + GREEN_STYLE = FXRGBA(0,0xFF,0,0xFF) #0x00FF0000 + + # Creates a new TestRunner for running the passed + # suite. + def initialize(suite, output_level = NORMAL) + if (suite.respond_to?(:suite)) + @suite = suite.suite + else + @suite = suite + end + + @result = nil + @red = false + end + + # Begins the test run. + def start + setup_ui + setup_mediator + attach_to_mediator + start_ui + @result + end + + def setup_mediator + @mediator = TestRunnerMediator.new(@suite) + suite_name = @suite.to_s + if ( @suite.kind_of?(Module) ) + suite_name = @suite.name + end + @suite_name_entry.text = suite_name + end + + def attach_to_mediator + @mediator.add_listener(TestRunnerMediator::RESET, &method(:reset_ui)) + @mediator.add_listener(TestResult::FAULT, &method(:add_fault)) + @mediator.add_listener(TestResult::CHANGED, &method(:result_changed)) + @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started)) + @mediator.add_listener(TestCase::STARTED, &method(:test_started)) + @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished)) + end + + def start_ui + @application.create + @window.show(PLACEMENT_SCREEN) + @application.addTimeout(1) do + @mediator.run_suite + end + @application.run + end + + def stop + @application.exit(0) + end + + def reset_ui(count) + @test_progress_bar.barColor = GREEN_STYLE + @test_progress_bar.total = count + @test_progress_bar.progress = 0 + @red = false + + @test_count_label.text = "0" + @assertion_count_label.text = "0" + @failure_count_label.text = "0" + @error_count_label.text = "0" + + @fault_list.clearItems + end + + def add_fault(fault) + if ( ! @red ) + @test_progress_bar.barColor = RED_STYLE + @red = true + end + item = FaultListItem.new(fault) + @fault_list.appendItem(item) + end + + def show_fault(fault) + raw_show_fault(fault.long_display) + end + + def raw_show_fault(string) + @detail_text.setText(string) + end + + def clear_fault + raw_show_fault("") + end + + def result_changed(result) + @test_progress_bar.progress = result.run_count + + @test_count_label.text = result.run_count.to_s + @assertion_count_label.text = result.assertion_count.to_s + @failure_count_label.text = result.failure_count.to_s + @error_count_label.text = result.error_count.to_s + + # repaint now! + @info_panel.repaint + @application.flush + end + + def started(result) + @result = result + output_status("Started...") + end + + def test_started(test_name) + output_status("Running #{test_name}...") + end + + def finished(elapsed_time) + output_status("Finished in #{elapsed_time} seconds") + end + + def output_status(string) + @status_entry.text = string + @status_entry.repaint + end + + def setup_ui + @application = create_application + create_tooltip(@application) + + @window = create_window(@application) + + @status_entry = create_entry(@window) + + main_panel = create_main_panel(@window) + + suite_panel = create_suite_panel(main_panel) + create_label(suite_panel, "Suite:") + @suite_name_entry = create_entry(suite_panel) + create_button(suite_panel, "&Run\tRun the current suite", proc { @mediator.run_suite }) + + @test_progress_bar = create_progress_bar(main_panel) + + @info_panel = create_info_panel(main_panel) + create_label(@info_panel, "Tests:") + @test_count_label = create_label(@info_panel, "0") + create_label(@info_panel, "Assertions:") + @assertion_count_label = create_label(@info_panel, "0") + create_label(@info_panel, "Failures:") + @failure_count_label = create_label(@info_panel, "0") + create_label(@info_panel, "Errors:") + @error_count_label = create_label(@info_panel, "0") + + list_panel = create_list_panel(main_panel) + @fault_list = create_fault_list(list_panel) + + detail_panel = create_detail_panel(main_panel) + @detail_text = create_text(detail_panel) + end + + def create_application + app = FXApp.new("TestRunner", "Test::Unit") + app.init([]) + app + end + + def create_window(app) + FXMainWindow.new(app, "Test::Unit TestRunner", nil, nil, DECOR_ALL, 0, 0, 450) + end + + def create_tooltip(app) + FXTooltip.new(app) + end + + def create_main_panel(parent) + panel = FXVerticalFrame.new(parent, LAYOUT_FILL_X | LAYOUT_FILL_Y) + panel.vSpacing = 10 + panel + end + + def create_suite_panel(parent) + FXHorizontalFrame.new(parent, LAYOUT_SIDE_LEFT | LAYOUT_FILL_X) + end + + def create_button(parent, text, action) + FXButton.new(parent, text).connect(SEL_COMMAND, &action) + end + + def create_progress_bar(parent) + FXProgressBar.new(parent, nil, 0, PROGRESSBAR_NORMAL | LAYOUT_FILL_X) + end + + def create_info_panel(parent) + FXMatrix.new(parent, 1, MATRIX_BY_ROWS | LAYOUT_FILL_X) + end + + def create_label(parent, text) + FXLabel.new(parent, text, nil, JUSTIFY_CENTER_X | LAYOUT_FILL_COLUMN) + end + + def create_list_panel(parent) + FXHorizontalFrame.new(parent, LAYOUT_FILL_X | FRAME_SUNKEN | FRAME_THICK) + end + + def create_fault_list(parent) + list = FXList.new(parent, 10, nil, 0, LIST_SINGLESELECT | LAYOUT_FILL_X) #, 0, 0, 0, 150) + list.connect(SEL_COMMAND) do |sender, sel, ptr| + if sender.retrieveItem(sender.currentItem).selected? + show_fault(sender.retrieveItem(sender.currentItem).fault) + else + clear_fault + end + end + list + end + + def create_detail_panel(parent) + FXHorizontalFrame.new(parent, LAYOUT_FILL_X | LAYOUT_FILL_Y | FRAME_SUNKEN | FRAME_THICK) + end + + def create_text(parent) + FXText.new(parent, nil, 0, TEXT_READONLY | LAYOUT_FILL_X | LAYOUT_FILL_Y) + end + + def create_entry(parent) + entry = FXTextField.new(parent, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_SIDE_BOTTOM | LAYOUT_FILL_X) + entry.disable + entry + end + end + + class FaultListItem < FXListItem + attr_reader(:fault) + def initialize(fault) + super(fault.short_display) + @fault = fault + end + end + end + end + end +end + +if __FILE__ == $0 + Test::Unit::UI::Fox::TestRunner.start_command_line_test +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/gtk/testrunner.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/gtk/testrunner.rb new file mode 100644 index 0000000000..994328dc9b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/gtk/testrunner.rb @@ -0,0 +1,416 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'gtk' +require 'test/unit/ui/testrunnermediator' +require 'test/unit/ui/testrunnerutilities' + +module Test + module Unit + module UI + module GTK + + # Runs a Test::Unit::TestSuite in a Gtk UI. Obviously, + # this one requires you to have Gtk + # (http://www.gtk.org/) and the Ruby Gtk extension + # (http://ruby-gnome.sourceforge.net/) installed. + class TestRunner + extend TestRunnerUtilities + + # Creates a new TestRunner for running the passed + # suite. + def initialize(suite, output_level = NORMAL) + if (suite.respond_to?(:suite)) + @suite = suite.suite + else + @suite = suite + end + @result = nil + + @runner = Thread.current + @restart_signal = Class.new(Exception) + @viewer = Thread.start do + @runner.join rescue @runner.run + Gtk.main + end + @viewer.join rescue nil # wait deadlock to handshake + end + + # Begins the test run. + def start + setup_mediator + setup_ui + attach_to_mediator + start_ui + @result + end + + private + def setup_mediator + @mediator = TestRunnerMediator.new(@suite) + suite_name = @suite.to_s + if ( @suite.kind_of?(Module) ) + suite_name = @suite.name + end + suite_name_entry.set_text(suite_name) + end + + def attach_to_mediator + run_button.signal_connect("clicked", nil, &method(:run_test)) + @mediator.add_listener(TestRunnerMediator::RESET, &method(:reset_ui)) + @mediator.add_listener(TestResult::FAULT, &method(:add_fault)) + @mediator.add_listener(TestResult::CHANGED, &method(:result_changed)) + @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started)) + @mediator.add_listener(TestCase::STARTED, &method(:test_started)) + @mediator.add_listener(TestCase::FINISHED, &method(:test_finished)) + @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished)) + end + + def run_test(*) + @runner.raise(@restart_signal) + end + + def start_ui + @viewer.run + running = false + begin + loop do + if (running ^= true) + run_button.child.text = "Stop" + @mediator.run_suite + else + run_button.child.text = "Run" + @viewer.join + break + end + end + rescue @restart_signal + retry + rescue + end + end + + def stop(*) + Gtk.main_quit + end + + def reset_ui(count) + test_progress_bar.set_style(green_style) + test_progress_bar.configure(0, 0, count) + @red = false + + run_count_label.set_text("0") + assertion_count_label.set_text("0") + failure_count_label.set_text("0") + error_count_label.set_text("0") + + fault_list.remove_items(fault_list.children) + end + + def add_fault(fault) + if ( ! @red ) + test_progress_bar.set_style(red_style) + @red = true + end + item = FaultListItem.new(fault) + item.show + fault_list.append_items([item]) + end + + def show_fault(fault) + raw_show_fault(fault.long_display) + end + + def raw_show_fault(string) + fault_detail_label.set_text(string) + outer_detail_sub_panel.queue_resize + end + + def clear_fault + raw_show_fault("") + end + + def result_changed(result) + run_count_label.set_text(result.run_count.to_s) + assertion_count_label.set_text(result.assertion_count.to_s) + failure_count_label.set_text(result.failure_count.to_s) + error_count_label.set_text(result.error_count.to_s) + end + + def started(result) + @result = result + output_status("Started...") + end + + def test_started(test_name) + output_status("Running #{test_name}...") + end + + def test_finished(test_name) + test_progress_bar.set_value(test_progress_bar.get_value + 1) + end + + def finished(elapsed_time) + output_status("Finished in #{elapsed_time} seconds") + end + + def output_status(string) + status_entry.set_text(string) + end + + def setup_ui + main_window.signal_connect("destroy", nil, &method(:stop)) + main_window.show_all + fault_list.signal_connect("select-child", nil) { + | list, item, data | + show_fault(item.fault) + } + fault_list.signal_connect("unselect-child", nil) { + clear_fault + } + @red = false + end + + def main_window + lazy_initialize(:main_window) { + @main_window = Gtk::Window.new(Gtk::WINDOW_TOPLEVEL) + @main_window.set_title("Test::Unit TestRunner") + @main_window.set_usize(800, 600) + @main_window.set_uposition(20, 20) + @main_window.set_policy(true, true, false) + @main_window.add(main_panel) + } + end + + def main_panel + lazy_initialize(:main_panel) { + @main_panel = Gtk::VBox.new(false, 0) + @main_panel.pack_start(suite_panel, false, false, 0) + @main_panel.pack_start(progress_panel, false, false, 0) + @main_panel.pack_start(info_panel, false, false, 0) + @main_panel.pack_start(list_panel, false, false, 0) + @main_panel.pack_start(detail_panel, true, true, 0) + @main_panel.pack_start(status_panel, false, false, 0) + } + end + + def suite_panel + lazy_initialize(:suite_panel) { + @suite_panel = Gtk::HBox.new(false, 10) + @suite_panel.border_width(10) + @suite_panel.pack_start(Gtk::Label.new("Suite:"), false, false, 0) + @suite_panel.pack_start(suite_name_entry, true, true, 0) + @suite_panel.pack_start(run_button, false, false, 0) + } + end + + def suite_name_entry + lazy_initialize(:suite_name_entry) { + @suite_name_entry = Gtk::Entry.new + @suite_name_entry.set_editable(false) + } + end + + def run_button + lazy_initialize(:run_button) { + @run_button = Gtk::Button.new("Run") + } + end + + def progress_panel + lazy_initialize(:progress_panel) { + @progress_panel = Gtk::HBox.new(false, 10) + @progress_panel.border_width(10) + @progress_panel.pack_start(test_progress_bar, true, true, 0) + } + end + + def test_progress_bar + lazy_initialize(:test_progress_bar) { + @test_progress_bar = EnhancedProgressBar.new + @test_progress_bar.set_usize(@test_progress_bar.allocation.width, + info_panel.size_request.height) + @test_progress_bar.set_style(green_style) + } + end + + def green_style + lazy_initialize(:green_style) { + @green_style = Gtk::Style.new + @green_style.set_bg(Gtk::STATE_PRELIGHT, 0x0000, 0xFFFF, 0x0000) + } + end + + def red_style + lazy_initialize(:red_style) { + @red_style = Gtk::Style.new + @red_style.set_bg(Gtk::STATE_PRELIGHT, 0xFFFF, 0x0000, 0x0000) + } + end + + def info_panel + lazy_initialize(:info_panel) { + @info_panel = Gtk::HBox.new(false, 0) + @info_panel.border_width(10) + @info_panel.pack_start(Gtk::Label.new("Runs:"), false, false, 0) + @info_panel.pack_start(run_count_label, true, false, 0) + @info_panel.pack_start(Gtk::Label.new("Assertions:"), false, false, 0) + @info_panel.pack_start(assertion_count_label, true, false, 0) + @info_panel.pack_start(Gtk::Label.new("Failures:"), false, false, 0) + @info_panel.pack_start(failure_count_label, true, false, 0) + @info_panel.pack_start(Gtk::Label.new("Errors:"), false, false, 0) + @info_panel.pack_start(error_count_label, true, false, 0) + } + end + + def run_count_label + lazy_initialize(:run_count_label) { + @run_count_label = Gtk::Label.new("0") + @run_count_label.set_justify(Gtk::JUSTIFY_LEFT) + } + end + + def assertion_count_label + lazy_initialize(:assertion_count_label) { + @assertion_count_label = Gtk::Label.new("0") + @assertion_count_label.set_justify(Gtk::JUSTIFY_LEFT) + } + end + + def failure_count_label + lazy_initialize(:failure_count_label) { + @failure_count_label = Gtk::Label.new("0") + @failure_count_label.set_justify(Gtk::JUSTIFY_LEFT) + } + end + + def error_count_label + lazy_initialize(:error_count_label) { + @error_count_label = Gtk::Label.new("0") + @error_count_label.set_justify(Gtk::JUSTIFY_LEFT) + } + end + + def list_panel + lazy_initialize(:list_panel) { + @list_panel = Gtk::HBox.new + @list_panel.border_width(10) + @list_panel.pack_start(list_scrolled_window, true, true, 0) + } + end + + def list_scrolled_window + lazy_initialize(:list_scrolled_window) { + @list_scrolled_window = Gtk::ScrolledWindow.new + @list_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC) + @list_scrolled_window.set_usize(@list_scrolled_window.allocation.width, 150) + @list_scrolled_window.add_with_viewport(fault_list) + } + end + + def fault_list + lazy_initialize(:fault_list) { + @fault_list = Gtk::List.new + } + end + + def detail_panel + lazy_initialize(:detail_panel) { + @detail_panel = Gtk::HBox.new + @detail_panel.border_width(10) + @detail_panel.pack_start(detail_scrolled_window, true, true, 0) + } + end + + def detail_scrolled_window + lazy_initialize(:detail_scrolled_window) { + @detail_scrolled_window = Gtk::ScrolledWindow.new + @detail_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC) + @detail_scrolled_window.set_usize(400, @detail_scrolled_window.allocation.height) + @detail_scrolled_window.add_with_viewport(outer_detail_sub_panel) + } + end + + def outer_detail_sub_panel + lazy_initialize(:outer_detail_sub_panel) { + @outer_detail_sub_panel = Gtk::VBox.new + @outer_detail_sub_panel.pack_start(inner_detail_sub_panel, false, false, 0) + } + end + + def inner_detail_sub_panel + lazy_initialize(:inner_detail_sub_panel) { + @inner_detail_sub_panel = Gtk::HBox.new + @inner_detail_sub_panel.pack_start(fault_detail_label, false, false, 0) + } + end + + def fault_detail_label + lazy_initialize(:fault_detail_label) { + @fault_detail_label = EnhancedLabel.new("") + style = Gtk::Style.new + font = Gdk::Font.font_load("-*-Courier New-medium-r-normal--*-120-*-*-*-*-*-*") + begin + style.set_font(font) + rescue ArgumentError; end + @fault_detail_label.set_style(style) + @fault_detail_label.set_justify(Gtk::JUSTIFY_LEFT) + @fault_detail_label.set_line_wrap(false) + } + end + + def status_panel + lazy_initialize(:status_panel) { + @status_panel = Gtk::HBox.new + @status_panel.border_width(10) + @status_panel.pack_start(status_entry, true, true, 0) + } + end + + def status_entry + lazy_initialize(:status_entry) { + @status_entry = Gtk::Entry.new + @status_entry.set_editable(false) + } + end + + def lazy_initialize(symbol) + if (!instance_eval("defined?(@#{symbol.to_s})")) + yield + end + return instance_eval("@" + symbol.to_s) + end + end + + class EnhancedProgressBar < Gtk::ProgressBar + def set_style(style) + super + hide + show + end + end + + class EnhancedLabel < Gtk::Label + def set_text(text) + super(text.gsub(/\n\t/, "\n" + (" " * 4))) + end + end + + class FaultListItem < Gtk::ListItem + attr_reader(:fault) + def initialize(fault) + super(fault.short_display) + @fault = fault + end + end + end + end + end +end + +if __FILE__ == $0 + Test::Unit::UI::GTK::TestRunner.start_command_line_test +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/gtk2/testrunner.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/gtk2/testrunner.rb new file mode 100644 index 0000000000..b05549c0e8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/gtk2/testrunner.rb @@ -0,0 +1,465 @@ +#-- +# +# Author:: Kenta MURATA. +# Copyright:: Copyright (c) 2000-2002 Kenta MURATA. All rights reserved. +# License:: Ruby license. + +require "gtk2" +require "test/unit/ui/testrunnermediator" +require "test/unit/ui/testrunnerutilities" + +module Test + module Unit + module UI + module GTK2 + + Gtk.init + + class EnhancedLabel < Gtk::Label + def set_text(text) + super(text.gsub(/\n\t/, "\n ")) + end + end + + class FaultList < Gtk::TreeView + def initialize + @faults = [] + @model = Gtk::ListStore.new(String, String) + super(@model) + column = Gtk::TreeViewColumn.new + column.visible = false + append_column(column) + renderer = Gtk::CellRendererText.new + column = Gtk::TreeViewColumn.new("Failures", renderer, {:text => 1}) + append_column(column) + selection.mode = Gtk::SELECTION_SINGLE + set_rules_hint(true) + set_headers_visible(false) + end # def initialize + + def add_fault(fault) + @faults.push(fault) + iter = @model.append + iter.set_value(0, (@faults.length - 1).to_s) + iter.set_value(1, fault.short_display) + end # def add_fault(fault) + + def get_fault(iter) + @faults[iter.get_value(0).to_i] + end # def get_fault + + def clear + model.clear + end # def clear + end + + class TestRunner + extend TestRunnerUtilities + + def lazy_initialize(symbol) + if !instance_eval("defined?(@#{symbol})") then + yield + end + return instance_eval("@#{symbol}") + end + private :lazy_initialize + + def status_entry + lazy_initialize(:status_entry) do + @status_entry = Gtk::Entry.new + @status_entry.editable = false + end + end + private :status_entry + + def status_panel + lazy_initialize(:status_panel) do + @status_panel = Gtk::HBox.new + @status_panel.border_width = 10 + @status_panel.pack_start(status_entry, true, true, 0) + end + end + private :status_panel + + def fault_detail_label + lazy_initialize(:fault_detail_label) do + @fault_detail_label = EnhancedLabel.new("") +# style = Gtk::Style.new +# font = Gdk::Font. +# font_load("-*-Courier 10 Pitch-medium-r-normal--*-120-*-*-*-*-*-*") +# style.set_font(font) +# @fault_detail_label.style = style + @fault_detail_label.justify = Gtk::JUSTIFY_LEFT + @fault_detail_label.wrap = false + end + end + private :fault_detail_label + + def inner_detail_sub_panel + lazy_initialize(:inner_detail_sub_panel) do + @inner_detail_sub_panel = Gtk::HBox.new + @inner_detail_sub_panel.pack_start(fault_detail_label, false, false, 0) + end + end + private :inner_detail_sub_panel + + def outer_detail_sub_panel + lazy_initialize(:outer_detail_sub_panel) do + @outer_detail_sub_panel = Gtk::VBox.new + @outer_detail_sub_panel.pack_start(inner_detail_sub_panel, false, false, 0) + end + end + private :outer_detail_sub_panel + + def detail_scrolled_window + lazy_initialize(:detail_scrolled_window) do + @detail_scrolled_window = Gtk::ScrolledWindow.new + @detail_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC) + @detail_scrolled_window. + set_size_request(400, @detail_scrolled_window.allocation.height) + @detail_scrolled_window.add_with_viewport(outer_detail_sub_panel) + end + end + private :detail_scrolled_window + + def detail_panel + lazy_initialize(:detail_panel) do + @detail_panel = Gtk::HBox.new + @detail_panel.border_width = 10 + @detail_panel.pack_start(detail_scrolled_window, true, true, 0) + end + end + private :detail_panel + + def fault_list + lazy_initialize(:fault_list) do + @fault_list = FaultList.new + end + end + private :fault_list + + def list_scrolled_window + lazy_initialize(:list_scrolled_window) do + @list_scrolled_window = Gtk::ScrolledWindow.new + @list_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC) + @list_scrolled_window. + set_size_request(@list_scrolled_window.allocation.width, 150) + @list_scrolled_window.add_with_viewport(fault_list) + end + end + private :list_scrolled_window + + def list_panel + lazy_initialize(:list_panel) do + @list_panel = Gtk::HBox.new + @list_panel.border_width = 10 + @list_panel.pack_start(list_scrolled_window, true, true, 0) + end + end + private :list_panel + + def error_count_label + lazy_initialize(:error_count_label) do + @error_count_label = Gtk::Label.new("0") + @error_count_label.justify = Gtk::JUSTIFY_LEFT + end + end + private :error_count_label + + def failure_count_label + lazy_initialize(:failure_count_label) do + @failure_count_label = Gtk::Label.new("0") + @failure_count_label.justify = Gtk::JUSTIFY_LEFT + end + end + private :failure_count_label + + def assertion_count_label + lazy_initialize(:assertion_count_label) do + @assertion_count_label = Gtk::Label.new("0") + @assertion_count_label.justify = Gtk::JUSTIFY_LEFT + end + end + private :assertion_count_label + + def run_count_label + lazy_initialize(:run_count_label) do + @run_count_label = Gtk::Label.new("0") + @run_count_label.justify = Gtk::JUSTIFY_LEFT + end + end + private :run_count_label + + def info_panel + lazy_initialize(:info_panel) do + @info_panel = Gtk::HBox.new(false, 0) + @info_panel.border_width = 10 + @info_panel.pack_start(Gtk::Label.new("Runs:"), false, false, 0) + @info_panel.pack_start(run_count_label, true, false, 0) + @info_panel.pack_start(Gtk::Label.new("Assertions:"), false, false, 0) + @info_panel.pack_start(assertion_count_label, true, false, 0) + @info_panel.pack_start(Gtk::Label.new("Failures:"), false, false, 0) + @info_panel.pack_start(failure_count_label, true, false, 0) + @info_panel.pack_start(Gtk::Label.new("Errors:"), false, false, 0) + @info_panel.pack_start(error_count_label, true, false, 0) + end + end # def info_panel + private :info_panel + + def green_style + lazy_initialize(:green_style) do + @green_style = Gtk::Style.new + @green_style.set_bg(Gtk::STATE_PRELIGHT, 0x0000, 0xFFFF, 0x0000) + end + end # def green_style + private :green_style + + def red_style + lazy_initialize(:red_style) do + @red_style = Gtk::Style.new + @red_style.set_bg(Gtk::STATE_PRELIGHT, 0xFFFF, 0x0000, 0x0000) + end + end # def red_style + private :red_style + + def test_progress_bar + lazy_initialize(:test_progress_bar) { + @test_progress_bar = Gtk::ProgressBar.new + @test_progress_bar.fraction = 0.0 + @test_progress_bar. + set_size_request(@test_progress_bar.allocation.width, + info_panel.size_request[1]) + @test_progress_bar.style = green_style + } + end # def test_progress_bar + private :test_progress_bar + + def progress_panel + lazy_initialize(:progress_panel) do + @progress_panel = Gtk::HBox.new(false, 10) + @progress_panel.border_width = 10 + @progress_panel.pack_start(test_progress_bar, true, true, 0) + end + end # def progress_panel + + def run_button + lazy_initialize(:run_button) do + @run_button = Gtk::Button.new("Run") + end + end # def run_button + + def suite_name_entry + lazy_initialize(:suite_name_entry) do + @suite_name_entry = Gtk::Entry.new + @suite_name_entry.editable = false + end + end # def suite_name_entry + private :suite_name_entry + + def suite_panel + lazy_initialize(:suite_panel) do + @suite_panel = Gtk::HBox.new(false, 10) + @suite_panel.border_width = 10 + @suite_panel.pack_start(Gtk::Label.new("Suite:"), false, false, 0) + @suite_panel.pack_start(suite_name_entry, true, true, 0) + @suite_panel.pack_start(run_button, false, false, 0) + end + end # def suite_panel + private :suite_panel + + def main_panel + lazy_initialize(:main_panel) do + @main_panel = Gtk::VBox.new(false, 0) + @main_panel.pack_start(suite_panel, false, false, 0) + @main_panel.pack_start(progress_panel, false, false, 0) + @main_panel.pack_start(info_panel, false, false, 0) + @main_panel.pack_start(list_panel, false, false, 0) + @main_panel.pack_start(detail_panel, true, true, 0) + @main_panel.pack_start(status_panel, false, false, 0) + end + end # def main_panel + private :main_panel + + def main_window + lazy_initialize(:main_window) do + @main_window = Gtk::Window.new(Gtk::Window::TOPLEVEL) + @main_window.set_title("Test::Unit TestRunner") + @main_window.set_default_size(800, 600) + @main_window.set_resizable(true) + @main_window.add(main_panel) + end + end # def main_window + private :main_window + + def setup_ui + main_window.signal_connect("destroy", nil) { stop } + main_window.show_all + fault_list.selection.signal_connect("changed", nil) do + |selection, data| + if selection.selected then + show_fault(fault_list.get_fault(selection.selected)) + else + clear_fault + end + end + end # def setup_ui + private :setup_ui + + def output_status(string) + status_entry.set_text(string) + end # def output_status(string) + private :output_status + + def finished(elapsed_time) + test_progress_bar.fraction = 1.0 + output_status("Finished in #{elapsed_time} seconds") + end # def finished(elapsed_time) + private :finished + + def test_started(test_name) + output_status("Running #{test_name}...") + end # def test_started(test_name) + private :test_started + + def started(result) + @result = result + output_status("Started...") + end # def started(result) + private :started + + def test_finished(result) + test_progress_bar.fraction += 1.0 / @count + end # def test_finished(result) + + def result_changed(result) + run_count_label.label = result.run_count.to_s + assertion_count_label.label = result.assertion_count.to_s + failure_count_label.label = result.failure_count.to_s + error_count_label.label = result.error_count.to_s + end # def result_changed(result) + private :result_changed + + def clear_fault + raw_show_fault("") + end # def clear_fault + private :clear_fault + + def raw_show_fault(string) + fault_detail_label.set_text(string) + outer_detail_sub_panel.queue_resize + end # def raw_show_fault(string) + private :raw_show_fault + + def show_fault(fault) + raw_show_fault(fault.long_display) + end # def show_fault(fault) + private :show_fault + + def add_fault(fault) + if not @red then + test_progress_bar.style = red_style + @red = true + end + fault_list.add_fault(fault) + end # def add_fault(fault) + private :add_fault + + def reset_ui(count) + test_progress_bar.style = green_style + test_progress_bar.fraction = 0.0 + @count = count + 1 + @red = false + + run_count_label.set_text("0") + assertion_count_label.set_text("0") + failure_count_label.set_text("0") + error_count_label.set_text("0") + + fault_list.clear + end # def reset_ui(count) + private :reset_ui + + def stop + Gtk.main_quit + end # def stop + private :stop + + def run_test + @runner.raise(@restart_signal) + end + private :run_test + + def start_ui + @viewer.run + running = false + begin + loop do + if (running ^= true) + run_button.child.text = "Stop" + @mediator.run_suite + else + run_button.child.text = "Run" + @viewer.join + break + end + end + rescue @restart_signal + retry + rescue + end + end # def start_ui + private :start_ui + + def attach_to_mediator + run_button.signal_connect("clicked", nil) { run_test } + @mediator.add_listener(TestRunnerMediator::RESET, &method(:reset_ui)) + @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started)) + @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished)) + @mediator.add_listener(TestResult::FAULT, &method(:add_fault)) + @mediator.add_listener(TestResult::CHANGED, &method(:result_changed)) + @mediator.add_listener(TestCase::STARTED, &method(:test_started)) + @mediator.add_listener(TestCase::FINISHED, &method(:test_finished)) + end # def attach_to_mediator + private :attach_to_mediator + + def setup_mediator + @mediator = TestRunnerMediator.new(@suite) + suite_name = @suite.to_s + if @suite.kind_of?(Module) then + suite_name = @suite.name + end + suite_name_entry.set_text(suite_name) + end # def setup_mediator + private :setup_mediator + + def start + setup_mediator + setup_ui + attach_to_mediator + start_ui + @result + end # def start + + def initialize(suite, output_level = NORMAL) + if suite.respond_to?(:suite) then + @suite = suite.suite + else + @suite = suite + end + @result = nil + + @runner = Thread.current + @restart_signal = Class.new(Exception) + @viewer = Thread.start do + @runner.join rescue @runner.run + Gtk.main + end + @viewer.join rescue nil # wait deadlock to handshake + end # def initialize(suite) + + end # class TestRunner + + end # module GTK2 + end # module UI + end # module Unit +end # module Test diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/testrunnermediator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/testrunnermediator.rb new file mode 100644 index 0000000000..d34510d1c6 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/testrunnermediator.rb @@ -0,0 +1,68 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit' +require 'test/unit/util/observable' +require 'test/unit/testresult' + +module Test + module Unit + module UI + + # Provides an interface to write any given UI against, + # hopefully making it easy to write new UIs. + class TestRunnerMediator + RESET = name + "::RESET" + STARTED = name + "::STARTED" + FINISHED = name + "::FINISHED" + + include Util::Observable + + # Creates a new TestRunnerMediator initialized to run + # the passed suite. + def initialize(suite) + @suite = suite + end + + # Runs the suite the TestRunnerMediator was created + # with. + def run_suite + Unit.run = true + begin_time = Time.now + notify_listeners(RESET, @suite.size) + result = create_result + notify_listeners(STARTED, result) + result_listener = result.add_listener(TestResult::CHANGED) do |updated_result| + notify_listeners(TestResult::CHANGED, updated_result) + end + + fault_listener = result.add_listener(TestResult::FAULT) do |fault| + notify_listeners(TestResult::FAULT, fault) + end + + @suite.run(result) do |channel, value| + notify_listeners(channel, value) + end + + result.remove_listener(TestResult::FAULT, fault_listener) + result.remove_listener(TestResult::CHANGED, result_listener) + end_time = Time.now + elapsed_time = end_time - begin_time + notify_listeners(FINISHED, elapsed_time) #"Finished in #{elapsed_time} seconds.") + return result + end + + private + # A factory method to create the result the mediator + # should run with. Can be overridden by subclasses if + # one wants to use a different result. + def create_result + return TestResult.new + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/testrunnerutilities.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/testrunnerutilities.rb new file mode 100644 index 0000000000..70b885bd6c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/testrunnerutilities.rb @@ -0,0 +1,46 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +module Test + module Unit + module UI + + SILENT = 0 + PROGRESS_ONLY = 1 + NORMAL = 2 + VERBOSE = 3 + + # Provides some utilities common to most, if not all, + # TestRunners. + # + #-- + # + # Perhaps there ought to be a TestRunner superclass? There + # seems to be a decent amount of shared code between test + # runners. + + module TestRunnerUtilities + + # Creates a new TestRunner and runs the suite. + def run(suite, output_level=NORMAL) + return new(suite, output_level).start + end + + # Takes care of the ARGV parsing and suite + # determination necessary for running one of the + # TestRunners from the command line. + def start_command_line_test + if ARGV.empty? + puts "You should supply the name of a test suite file to the runner" + exit + end + require ARGV[0].gsub(/.+::/, '') + new(eval(ARGV[0])).start + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/tk/testrunner.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/tk/testrunner.rb new file mode 100644 index 0000000000..4d05f2fb22 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/ui/tk/testrunner.rb @@ -0,0 +1,260 @@ +#-- +# +# Original Author:: Nathaniel Talbott. +# Author:: Kazuhiro NISHIYAMA. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# Copyright:: Copyright (c) 2003 Kazuhiro NISHIYAMA. All rights reserved. +# License:: Ruby license. + +require 'tk' +require 'test/unit/ui/testrunnermediator' +require 'test/unit/ui/testrunnerutilities' + +module Test + module Unit + module UI + module Tk + + # Runs a Test::Unit::TestSuite in a Tk UI. Obviously, + # this one requires you to have Tk + # and the Ruby Tk extension installed. + class TestRunner + extend TestRunnerUtilities + + # Creates a new TestRunner for running the passed + # suite. + def initialize(suite, output_level = NORMAL) + if (suite.respond_to?(:suite)) + @suite = suite.suite + else + @suite = suite + end + @result = nil + + @red = false + @fault_detail_list = [] + @runner = Thread.current + @restart_signal = Class.new(Exception) + @viewer = Thread.start do + @runner.join rescue @runner.run + ::Tk.mainloop + end + @viewer.join rescue nil # wait deadlock to handshake + end + + # Begins the test run. + def start + setup_ui + setup_mediator + attach_to_mediator + start_ui + @result + end + + private + def setup_mediator + @mediator = TestRunnerMediator.new(@suite) + suite_name = @suite.to_s + if ( @suite.kind_of?(Module) ) + suite_name = @suite.name + end + @suite_name_entry.value = suite_name + end + + def attach_to_mediator + @run_button.command(method(:run_test)) + @fault_list.bind('ButtonPress-1', proc{|y| + fault = @fault_detail_list[@fault_list.nearest(y)] + if fault + show_fault(fault) + end + }, '%y') + @mediator.add_listener(TestRunnerMediator::RESET, &method(:reset_ui)) + @mediator.add_listener(TestResult::FAULT, &method(:add_fault)) + @mediator.add_listener(TestResult::CHANGED, &method(:result_changed)) + @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started)) + @mediator.add_listener(TestCase::STARTED, &method(:test_started)) + @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished)) + end + + def run_test + @runner.raise(@restart_signal) + end + + def start_ui + @viewer.run + running = false + begin + loop do + if (running ^= true) + @run_button.configure('text'=>'Stop') + @mediator.run_suite + else + @run_button.configure('text'=>'Run') + @viewer.join + break + end + end + rescue @restart_signal + retry + rescue + end + end + + def stop + ::Tk.exit + end + + def reset_ui(count) + @test_total_count = count.to_f + @test_progress_bar.configure('background'=>'green') + @test_progress_bar.place('relwidth'=>(count.zero? ? 0 : 0/count)) + @red = false + + @test_count_label.value = 0 + @assertion_count_label.value = 0 + @failure_count_label.value = 0 + @error_count_label.value = 0 + + @fault_list.delete(0, 'end') + @fault_detail_list = [] + clear_fault + end + + def add_fault(fault) + if ( ! @red ) + @test_progress_bar.configure('background'=>'red') + @red = true + end + @fault_detail_list.push fault + @fault_list.insert('end', fault.short_display) + end + + def show_fault(fault) + raw_show_fault(fault.long_display) + end + + def raw_show_fault(string) + @detail_text.value = string + end + + def clear_fault + raw_show_fault("") + end + + def result_changed(result) + @test_count_label.value = result.run_count + @test_progress_bar.place('relwidth'=>result.run_count/@test_total_count) + @assertion_count_label.value = result.assertion_count + @failure_count_label.value = result.failure_count + @error_count_label.value = result.error_count + end + + def started(result) + @result = result + output_status("Started...") + end + + def test_started(test_name) + output_status("Running #{test_name}...") + end + + def finished(elapsed_time) + output_status("Finished in #{elapsed_time} seconds") + end + + def output_status(string) + @status_entry.value = string + end + + def setup_ui + @status_entry = TkVariable.new + l = TkLabel.new(nil, 'textvariable'=>@status_entry, 'relief'=>'sunken') + l.pack('side'=>'bottom', 'fill'=>'x') + + suite_frame = TkFrame.new.pack('fill'=>'x') + + @run_button = TkButton.new(suite_frame, 'text'=>'Run') + @run_button.pack('side'=>'right') + + TkLabel.new(suite_frame, 'text'=>'Suite:').pack('side'=>'left') + @suite_name_entry = TkVariable.new + l = TkLabel.new(suite_frame, 'textvariable'=>@suite_name_entry, 'relief'=>'sunken') + l.pack('side'=>'left', 'fill'=>'x', 'expand'=>true) + + f = TkFrame.new(nil, 'relief'=>'sunken', 'borderwidth'=>3, 'height'=>20).pack('fill'=>'x', 'padx'=>1) + @test_progress_bar = TkFrame.new(f, 'background'=>'green').place('anchor'=>'nw', 'relwidth'=>0.0, 'relheight'=>1.0) + + info_frame = TkFrame.new.pack('fill'=>'x') + @test_count_label = create_count_label(info_frame, 'Tests:') + @assertion_count_label = create_count_label(info_frame, 'Assertions:') + @failure_count_label = create_count_label(info_frame, 'Failures:') + @error_count_label = create_count_label(info_frame, 'Errors:') + + if (::Tk.info('command', TkPanedWindow::TkCommandNames[0]) != "") + # use panedwindow + paned_frame = TkPanedWindow.new("orient"=>"vertical").pack('fill'=>'both', 'expand'=>true) + + fault_list_frame = TkFrame.new(paned_frame) + detail_frame = TkFrame.new(paned_frame) + + paned_frame.add(fault_list_frame, detail_frame) + else + # no panedwindow + paned_frame = nil + fault_list_frame = TkFrame.new.pack('fill'=>'both', 'expand'=>true) + detail_frame = TkFrame.new.pack('fill'=>'both', 'expand'=>true) + end + + TkGrid.rowconfigure(fault_list_frame, 0, 'weight'=>1, 'minsize'=>0) + TkGrid.columnconfigure(fault_list_frame, 0, 'weight'=>1, 'minsize'=>0) + + fault_scrollbar_y = TkScrollbar.new(fault_list_frame) + fault_scrollbar_x = TkScrollbar.new(fault_list_frame) + @fault_list = TkListbox.new(fault_list_frame) + @fault_list.yscrollbar(fault_scrollbar_y) + @fault_list.xscrollbar(fault_scrollbar_x) + + TkGrid.rowconfigure(detail_frame, 0, 'weight'=>1, 'minsize'=>0) + TkGrid.columnconfigure(detail_frame, 0, 'weight'=>1, 'minsize'=>0) + + ::Tk.grid(@fault_list, fault_scrollbar_y, 'sticky'=>'news') + ::Tk.grid(fault_scrollbar_x, 'sticky'=>'news') + + detail_scrollbar_y = TkScrollbar.new(detail_frame) + detail_scrollbar_x = TkScrollbar.new(detail_frame) + @detail_text = TkText.new(detail_frame, 'height'=>10, 'wrap'=>'none') { + bindtags(bindtags - [TkText]) + } + @detail_text.yscrollbar(detail_scrollbar_y) + @detail_text.xscrollbar(detail_scrollbar_x) + + ::Tk.grid(@detail_text, detail_scrollbar_y, 'sticky'=>'news') + ::Tk.grid(detail_scrollbar_x, 'sticky'=>'news') + + # rubber-style pane + if paned_frame + ::Tk.update + @height = paned_frame.winfo_height + paned_frame.bind('Configure', proc{|h| + paned_frame.sash_place(0, 0, paned_frame.sash_coord(0)[1] * h / @height) + @height = h + }, '%h') + end + end + + def create_count_label(parent, label) + TkLabel.new(parent, 'text'=>label).pack('side'=>'left', 'expand'=>true) + v = TkVariable.new(0) + TkLabel.new(parent, 'textvariable'=>v).pack('side'=>'left', 'expand'=>true) + v + end + end + end + end + end +end + +if __FILE__ == $0 + Test::Unit::UI::Tk::TestRunner.start_command_line_test +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/util/backtracefilter.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/util/backtracefilter.rb new file mode 100644 index 0000000000..7ebec2dfef --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/util/backtracefilter.rb @@ -0,0 +1,40 @@ +module Test + module Unit + module Util + module BacktraceFilter + TESTUNIT_FILE_SEPARATORS = %r{[\\/:]} + TESTUNIT_PREFIX = __FILE__.split(TESTUNIT_FILE_SEPARATORS)[0..-3] + TESTUNIT_RB_FILE = /\.rb\Z/ + + def filter_backtrace(backtrace, prefix=nil) + return ["No backtrace"] unless(backtrace) + split_p = if(prefix) + prefix.split(TESTUNIT_FILE_SEPARATORS) + else + TESTUNIT_PREFIX + end + match = proc do |e| + split_e = e.split(TESTUNIT_FILE_SEPARATORS)[0, split_p.size] + next false unless(split_e[0..-2] == split_p[0..-2]) + split_e[-1].sub(TESTUNIT_RB_FILE, '') == split_p[-1] + end + return backtrace unless(backtrace.detect(&match)) + found_prefix = false + new_backtrace = backtrace.reverse.reject do |e| + if(match[e]) + found_prefix = true + true + elsif(found_prefix) + false + else + true + end + end.reverse + new_backtrace = (new_backtrace.empty? ? backtrace : new_backtrace) + new_backtrace = new_backtrace.reject(&match) + new_backtrace.empty? ? backtrace : new_backtrace + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/util/observable.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/util/observable.rb new file mode 100644 index 0000000000..3567d34271 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/util/observable.rb @@ -0,0 +1,90 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/util/procwrapper' + +module Test + module Unit + module Util + + # This is a utility class that allows anything mixing + # it in to notify a set of listeners about interesting + # events. + module Observable + # We use this for defaults since nil might mean something + NOTHING = "NOTHING/#{__id__}" + + # Adds the passed proc as a listener on the + # channel indicated by channel_name. listener_key + # is used to remove the listener later; if none is + # specified, the proc itself is used. + # + # Whatever is used as the listener_key is + # returned, making it very easy to use the proc + # itself as the listener_key: + # + # listener = add_listener("Channel") { ... } + # remove_listener("Channel", listener) + def add_listener(channel_name, listener_key=NOTHING, &listener) # :yields: value + unless(block_given?) + raise ArgumentError.new("No callback was passed as a listener") + end + + key = listener_key + if (listener_key == NOTHING) + listener_key = listener + key = ProcWrapper.new(listener) + end + + channels[channel_name] ||= {} + channels[channel_name][key] = listener + return listener_key + end + + # Removes the listener indicated by listener_key + # from the channel indicated by + # channel_name. Returns the registered proc, or + # nil if none was found. + def remove_listener(channel_name, listener_key) + channel = channels[channel_name] + return nil unless (channel) + key = listener_key + if (listener_key.instance_of?(Proc)) + key = ProcWrapper.new(listener_key) + end + if (channel.has_key?(key)) + return channel.delete(key) + end + return nil + end + + # Calls all the procs registered on the channel + # indicated by channel_name. If value is + # specified, it is passed in to the procs, + # otherwise they are called with no arguments. + # + #-- + # + # Perhaps this should be private? Would it ever + # make sense for an external class to call this + # method directly? + def notify_listeners(channel_name, *arguments) + channel = channels[channel_name] + return 0 unless (channel) + listeners = channel.values + listeners.each { |listener| listener.call(*arguments) } + return listeners.size + end + + private + def channels + @channels ||= {} + return @channels + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/util/procwrapper.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/util/procwrapper.rb new file mode 100644 index 0000000000..ad7252186d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/test/unit/util/procwrapper.rb @@ -0,0 +1,48 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +module Test + module Unit + module Util + + # Allows the storage of a Proc passed through '&' in a + # hash. + # + # Note: this may be inefficient, since the hash being + # used is not necessarily very good. In Observable, + # efficiency is not too important, since the hash is + # only accessed when adding and removing listeners, + # not when notifying. + + class ProcWrapper + + # Creates a new wrapper for a_proc. + def initialize(a_proc) + @a_proc = a_proc + @hash = a_proc.inspect.sub(/^(#<#{a_proc.class}:)/){''}.sub(/(>)$/){''}.hex + end + + def hash + return @hash + end + + def ==(other) + case(other) + when ProcWrapper + return @a_proc == other.to_proc + else + return super + end + end + alias :eql? :== + + def to_proc + return @a_proc + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/thread.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/thread.rb new file mode 100644 index 0000000000..6c533aba39 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/thread.rb @@ -0,0 +1,5 @@ +unless defined? Thread + fail "Thread not available for this ruby interpreter" +end + +require 'thread.so' diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/thwait.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/thwait.rb new file mode 100644 index 0000000000..95d294a85d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/thwait.rb @@ -0,0 +1,169 @@ +# +# thwait.rb - thread synchronization class +# $Release Version: 0.9 $ +# $Revision: 1.3 $ +# $Date: 1998/06/26 03:19:34 $ +# by Keiju ISHITSUKA(Nihpon Rational Software Co.,Ltd.) +# +# -- +# feature: +# provides synchronization for multiple threads. +# +# class methods: +# * ThreadsWait.all_waits(thread1,...) +# waits until all of specified threads are terminated. +# if a block is supplied for the method, evaluates it for +# each thread termination. +# * th = ThreadsWait.new(thread1,...) +# creates synchronization object, specifying thread(s) to wait. +# +# methods: +# * th.threads +# list threads to be synchronized +# * th.empty? +# is there any thread to be synchronized. +# * th.finished? +# is there already terminated thread. +# * th.join(thread1,...) +# wait for specified thread(s). +# * th.join_nowait(threa1,...) +# specifies thread(s) to wait. non-blocking. +# * th.next_wait +# waits until any of specified threads is terminated. +# * th.all_waits +# waits until all of specified threads are terminated. +# if a block is supplied for the method, evaluates it for +# each thread termination. +# + +require "thread.rb" +require "e2mmap.rb" + +# +# This class watches for termination of multiple threads. Basic functionality +# (wait until specified threads have terminated) can be accessed through the +# class method ThreadsWait::all_waits. Finer control can be gained using +# instance methods. +# +# Example: +# +# ThreadsWait.all_wait(thr1, thr2, ...) do |t| +# STDERR.puts "Thread #{t} has terminated." +# end +# +class ThreadsWait + RCS_ID='-$Id: thwait.rb,v 1.3 1998/06/26 03:19:34 keiju Exp keiju $-' + + Exception2MessageMapper.extend_to(binding) + def_exception("ErrNoWaitingThread", "No threads for waiting.") + def_exception("ErrNoFinishedThread", "No finished threads.") + + # + # Waits until all specified threads have terminated. If a block is provided, + # it is executed for each thread termination. + # + def ThreadsWait.all_waits(*threads) # :yield: thread + tw = ThreadsWait.new(*threads) + if block_given? + tw.all_waits do |th| + yield th + end + else + tw.all_waits + end + end + + # + # Creates a ThreadsWait object, specifying the threads to wait on. + # Non-blocking. + # + def initialize(*threads) + @threads = [] + @wait_queue = Queue.new + join_nowait(*threads) unless threads.empty? + end + + # Returns the array of threads in the wait queue. + attr :threads + + # + # Returns +true+ if there are no threads to be synchronized. + # + def empty? + @threads.empty? + end + + # + # Returns +true+ if any thread has terminated. + # + def finished? + !@wait_queue.empty? + end + + # + # Waits for specified threads to terminate. + # + def join(*threads) + join_nowait(*threads) + next_wait + end + + # + # Specifies the threads that this object will wait for, but does not actually + # wait. + # + def join_nowait(*threads) + threads.flatten! + @threads.concat threads + for th in threads + Thread.start(th) do |t| + begin + t.join + ensure + @wait_queue.push t + end + end + end + end + + # + # Waits until any of the specified threads has terminated, and returns the one + # that does. + # + # If there is no thread to wait, raises +ErrNoWaitingThread+. If +nonblock+ + # is true, and there is no terminated thread, raises +ErrNoFinishedThread+. + # + def next_wait(nonblock = nil) + ThreadsWait.fail ErrNoWaitingThread if @threads.empty? + begin + @threads.delete(th = @wait_queue.pop(nonblock)) + th + rescue ThreadError + ThreadsWait.fail ErrNoFinishedThread + end + end + + # + # Waits until all of the specified threads are terminated. If a block is + # supplied for the method, it is executed for each thread termination. + # + # Raises exceptions in the same manner as +next_wait+. + # + def all_waits + until @threads.empty? + th = next_wait + yield th if block_given? + end + end +end + +ThWait = ThreadsWait + + +# Documentation comments: +# - Source of documentation is evenly split between Nutshell, existing +# comments, and my own rephrasing. +# - I'm not particularly confident that the comments are all exactly correct. +# - The history, etc., up the top appears in the RDoc output. Perhaps it would +# be better to direct that not to appear, and put something else there +# instead. diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/time.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/time.rb new file mode 100644 index 0000000000..dbc25f4193 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/time.rb @@ -0,0 +1,797 @@ + +# +# == Introduction +# +# This library extends the Time class: +# * conversion between date string and time object. +# * date-time defined by RFC 2822 +# * HTTP-date defined by RFC 2616 +# * dateTime defined by XML Schema Part 2: Datatypes (ISO 8601) +# * various formats handled by Date._parse (string to time only) +# +# == Design Issues +# +# === Specialized interface +# +# This library provides methods dedicated to special purposes: +# * RFC 2822, RFC 2616 and XML Schema. +# * They makes usual life easier. +# +# === Doesn't depend on strftime +# +# This library doesn't use +strftime+. Especially #rfc2822 doesn't depend +# on +strftime+ because: +# +# * %a and %b are locale sensitive +# +# Since they are locale sensitive, they may be replaced to +# invalid weekday/month name in some locales. +# Since ruby-1.6 doesn't invoke setlocale by default, +# the problem doesn't arise until some external library invokes setlocale. +# Ruby/GTK is the example of such library. +# +# * %z is not portable +# +# %z is required to generate zone in date-time of RFC 2822 +# but it is not portable. +# +# == Revision Information +# +# $Id$ +# + +require 'parsedate' + +# +# Implements the extensions to the Time class that are described in the +# documentation for the time.rb library. +# +class Time + class << Time + + ZoneOffset = { + 'UTC' => 0, + # ISO 8601 + 'Z' => 0, + # RFC 822 + 'UT' => 0, 'GMT' => 0, + 'EST' => -5, 'EDT' => -4, + 'CST' => -6, 'CDT' => -5, + 'MST' => -7, 'MDT' => -6, + 'PST' => -8, 'PDT' => -7, + # Following definition of military zones is original one. + # See RFC 1123 and RFC 2822 for the error in RFC 822. + 'A' => +1, 'B' => +2, 'C' => +3, 'D' => +4, 'E' => +5, 'F' => +6, + 'G' => +7, 'H' => +8, 'I' => +9, 'K' => +10, 'L' => +11, 'M' => +12, + 'N' => -1, 'O' => -2, 'P' => -3, 'Q' => -4, 'R' => -5, 'S' => -6, + 'T' => -7, 'U' => -8, 'V' => -9, 'W' => -10, 'X' => -11, 'Y' => -12, + } + def zone_offset(zone, year=Time.now.year) + off = nil + zone = zone.upcase + if /\A([+-])(\d\d):?(\d\d)\z/ =~ zone + off = ($1 == '-' ? -1 : 1) * ($2.to_i * 60 + $3.to_i) * 60 + elsif /\A[+-]\d\d\z/ =~ zone + off = zone.to_i * 3600 + elsif ZoneOffset.include?(zone) + off = ZoneOffset[zone] * 3600 + elsif ((t = Time.local(year, 1, 1)).zone.upcase == zone rescue false) + off = t.utc_offset + elsif ((t = Time.local(year, 7, 1)).zone.upcase == zone rescue false) + off = t.utc_offset + end + off + end + + def zone_utc?(zone) + # * +0000 means localtime. [RFC 2822] + # * GMT is a localtime abbreviation in Europe/London, etc. + if /\A(?:-00:00|-0000|-00|UTC|Z|UT)\z/i =~ zone + true + else + false + end + end + private :zone_utc? + + LeapYearMonthDays = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + CommonYearMonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + def month_days(y, m) + if ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0) + LeapYearMonthDays[m-1] + else + CommonYearMonthDays[m-1] + end + end + private :month_days + + def apply_offset(year, mon, day, hour, min, sec, off) + if off < 0 + off = -off + off, o = off.divmod(60) + if o != 0 then sec += o; o, sec = sec.divmod(60); off += o end + off, o = off.divmod(60) + if o != 0 then min += o; o, min = min.divmod(60); off += o end + off, o = off.divmod(24) + if o != 0 then hour += o; o, hour = hour.divmod(24); off += o end + if off != 0 + day += off + if month_days(year, mon) < day + mon += 1 + if 12 < mon + mon = 1 + year += 1 + end + day = 1 + end + end + elsif 0 < off + off, o = off.divmod(60) + if o != 0 then sec -= o; o, sec = sec.divmod(60); off -= o end + off, o = off.divmod(60) + if o != 0 then min -= o; o, min = min.divmod(60); off -= o end + off, o = off.divmod(24) + if o != 0 then hour -= o; o, hour = hour.divmod(24); off -= o end + if off != 0 then + day -= off + if day < 1 + mon -= 1 + if mon < 1 + year -= 1 + mon = 12 + end + day = month_days(year, mon) + end + end + end + return year, mon, day, hour, min, sec + end + private :apply_offset + + def make_time(year, mon, day, hour, min, sec, sec_fraction, zone, now) + usec = nil + usec = (sec_fraction * 1000000).to_i if sec_fraction + if now + begin + break if year; year = now.year + break if mon; mon = now.mon + break if day; day = now.day + break if hour; hour = now.hour + break if min; min = now.min + break if sec; sec = now.sec + break if sec_fraction; usec = now.tv_usec + end until true + end + + year ||= 1970 + mon ||= 1 + day ||= 1 + hour ||= 0 + min ||= 0 + sec ||= 0 + usec ||= 0 + + off = nil + off = zone_offset(zone, year) if zone + + if off + year, mon, day, hour, min, sec = + apply_offset(year, mon, day, hour, min, sec, off) + t = Time.utc(year, mon, day, hour, min, sec, usec) + t.localtime if !zone_utc?(zone) + t + else + self.local(year, mon, day, hour, min, sec, usec) + end + end + private :make_time + + # + # Parses +date+ using Date._parse and converts it to a Time object. + # + # If a block is given, the year described in +date+ is converted by the + # block. For example: + # + # Time.parse(...) {|y| y < 100 ? (y >= 69 ? y + 1900 : y + 2000) : y} + # + # If the upper components of the given time are broken or missing, they are + # supplied with those of +now+. For the lower components, the minimum + # values (1 or 0) are assumed if broken or missing. For example: + # + # # Suppose it is "Thu Nov 29 14:33:20 GMT 2001" now and + # # your timezone is GMT: + # Time.parse("16:30") #=> Thu Nov 29 16:30:00 GMT 2001 + # Time.parse("7/23") #=> Mon Jul 23 00:00:00 GMT 2001 + # Time.parse("Aug 31") #=> Fri Aug 31 00:00:00 GMT 2001 + # + # Since there are numerous conflicts among locally defined timezone + # abbreviations all over the world, this method is not made to + # understand all of them. For example, the abbreviation "CST" is + # used variously as: + # + # -06:00 in America/Chicago, + # -05:00 in America/Havana, + # +08:00 in Asia/Harbin, + # +09:30 in Australia/Darwin, + # +10:30 in Australia/Adelaide, + # etc. + # + # Based on the fact, this method only understands the timezone + # abbreviations described in RFC 822 and the system timezone, in the + # order named. (i.e. a definition in RFC 822 overrides the system + # timezone definition.) The system timezone is taken from + # Time.local(year, 1, 1).zone and + # Time.local(year, 7, 1).zone. + # If the extracted timezone abbreviation does not match any of them, + # it is ignored and the given time is regarded as a local time. + # + # ArgumentError is raised if Date._parse cannot extract information from + # +date+ or Time class cannot represent specified date. + # + # This method can be used as fail-safe for other parsing methods as: + # + # Time.rfc2822(date) rescue Time.parse(date) + # Time.httpdate(date) rescue Time.parse(date) + # Time.xmlschema(date) rescue Time.parse(date) + # + # A failure for Time.parse should be checked, though. + # + def parse(date, now=Time.now) + d = Date._parse(date, false) + year = d[:year] + year = yield(year) if year && block_given? + make_time(year, d[:mon], d[:mday], d[:hour], d[:min], d[:sec], d[:sec_fraction], d[:zone], now) + end + + MonthValue = { + 'JAN' => 1, 'FEB' => 2, 'MAR' => 3, 'APR' => 4, 'MAY' => 5, 'JUN' => 6, + 'JUL' => 7, 'AUG' => 8, 'SEP' => 9, 'OCT' =>10, 'NOV' =>11, 'DEC' =>12 + } + + # + # Parses +date+ as date-time defined by RFC 2822 and converts it to a Time + # object. The format is identical to the date format defined by RFC 822 and + # updated by RFC 1123. + # + # ArgumentError is raised if +date+ is not compliant with RFC 2822 + # or Time class cannot represent specified date. + # + # See #rfc2822 for more information on this format. + # + def rfc2822(date) + if /\A\s* + (?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s*,\s*)? + (\d{1,2})\s+ + (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+ + (\d{2,})\s+ + (\d{2})\s* + :\s*(\d{2})\s* + (?::\s*(\d{2}))?\s+ + ([+-]\d{4}| + UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Z])/ix =~ date + # Since RFC 2822 permit comments, the regexp has no right anchor. + day = $1.to_i + mon = MonthValue[$2.upcase] + year = $3.to_i + hour = $4.to_i + min = $5.to_i + sec = $6 ? $6.to_i : 0 + zone = $7 + + # following year completion is compliant with RFC 2822. + year = if year < 50 + 2000 + year + elsif year < 1000 + 1900 + year + else + year + end + + year, mon, day, hour, min, sec = + apply_offset(year, mon, day, hour, min, sec, zone_offset(zone)) + t = self.utc(year, mon, day, hour, min, sec) + t.localtime if !zone_utc?(zone) + t + else + raise ArgumentError.new("not RFC 2822 compliant date: #{date.inspect}") + end + end + alias rfc822 rfc2822 + + # + # Parses +date+ as HTTP-date defined by RFC 2616 and converts it to a Time + # object. + # + # ArgumentError is raised if +date+ is not compliant with RFC 2616 or Time + # class cannot represent specified date. + # + # See #httpdate for more information on this format. + # + def httpdate(date) + if /\A\s* + (?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\x20 + (\d{2})\x20 + (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\x20 + (\d{4})\x20 + (\d{2}):(\d{2}):(\d{2})\x20 + GMT + \s*\z/ix =~ date + self.rfc2822(date) + elsif /\A\s* + (?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday),\x20 + (\d\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d\d)\x20 + (\d\d):(\d\d):(\d\d)\x20 + GMT + \s*\z/ix =~ date + self.parse(date) + elsif /\A\s* + (?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\x20 + (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\x20 + (\d\d|\x20\d)\x20 + (\d\d):(\d\d):(\d\d)\x20 + (\d{4}) + \s*\z/ix =~ date + self.utc($6.to_i, MonthValue[$1.upcase], $2.to_i, + $3.to_i, $4.to_i, $5.to_i) + else + raise ArgumentError.new("not RFC 2616 compliant date: #{date.inspect}") + end + end + + # + # Parses +date+ as dateTime defined by XML Schema and converts it to a Time + # object. The format is restricted version of the format defined by ISO + # 8601. + # + # ArgumentError is raised if +date+ is not compliant with the format or Time + # class cannot represent specified date. + # + # See #xmlschema for more information on this format. + # + def xmlschema(date) + if /\A\s* + (-?\d+)-(\d\d)-(\d\d) + T + (\d\d):(\d\d):(\d\d) + (\.\d*)? + (Z|[+-]\d\d:\d\d)? + \s*\z/ix =~ date + year = $1.to_i + mon = $2.to_i + day = $3.to_i + hour = $4.to_i + min = $5.to_i + sec = $6.to_i + usec = 0 + usec = $7.to_f * 1000000 if $7 + if $8 + zone = $8 + year, mon, day, hour, min, sec = + apply_offset(year, mon, day, hour, min, sec, zone_offset(zone)) + Time.utc(year, mon, day, hour, min, sec, usec) + else + Time.local(year, mon, day, hour, min, sec, usec) + end + else + raise ArgumentError.new("invalid date: #{date.inspect}") + end + end + alias iso8601 xmlschema + end # class << self + + # + # Returns a string which represents the time as date-time defined by RFC 2822: + # + # day-of-week, DD month-name CCYY hh:mm:ss zone + # + # where zone is [+-]hhmm. + # + # If +self+ is a UTC time, -0000 is used as zone. + # + def rfc2822 + sprintf('%s, %02d %s %d %02d:%02d:%02d ', + RFC2822_DAY_NAME[wday], + day, RFC2822_MONTH_NAME[mon-1], year, + hour, min, sec) + + if utc? + '-0000' + else + off = utc_offset + sign = off < 0 ? '-' : '+' + sprintf('%s%02d%02d', sign, *(off.abs / 60).divmod(60)) + end + end + alias rfc822 rfc2822 + + RFC2822_DAY_NAME = [ + 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' + ] + RFC2822_MONTH_NAME = [ + 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' + ] + + # + # Returns a string which represents the time as rfc1123-date of HTTP-date + # defined by RFC 2616: + # + # day-of-week, DD month-name CCYY hh:mm:ss GMT + # + # Note that the result is always UTC (GMT). + # + def httpdate + t = dup.utc + sprintf('%s, %02d %s %d %02d:%02d:%02d GMT', + RFC2822_DAY_NAME[t.wday], + t.day, RFC2822_MONTH_NAME[t.mon-1], t.year, + t.hour, t.min, t.sec) + end + + # + # Returns a string which represents the time as dateTime defined by XML + # Schema: + # + # CCYY-MM-DDThh:mm:ssTZD + # CCYY-MM-DDThh:mm:ss.sssTZD + # + # where TZD is Z or [+-]hh:mm. + # + # If self is a UTC time, Z is used as TZD. [+-]hh:mm is used otherwise. + # + # +fractional_seconds+ specifies a number of digits of fractional seconds. + # Its default value is 0. + # + def xmlschema(fraction_digits=0) + sprintf('%d-%02d-%02dT%02d:%02d:%02d', + year, mon, day, hour, min, sec) + + if fraction_digits == 0 + '' + elsif fraction_digits <= 6 + '.' + sprintf('%06d', usec)[0, fraction_digits] + else + '.' + sprintf('%06d', usec) + '0' * (fraction_digits - 6) + end + + if utc? + 'Z' + else + off = utc_offset + sign = off < 0 ? '-' : '+' + sprintf('%s%02d:%02d', sign, *(off.abs / 60).divmod(60)) + end + end + alias iso8601 xmlschema +end + +if __FILE__ == $0 + require 'test/unit' + + class TimeExtentionTest < Test::Unit::TestCase # :nodoc: + def test_rfc822 + assert_equal(Time.utc(1976, 8, 26, 14, 30) + 4 * 3600, + Time.rfc2822("26 Aug 76 14:30 EDT")) + assert_equal(Time.utc(1976, 8, 27, 9, 32) + 7 * 3600, + Time.rfc2822("27 Aug 76 09:32 PDT")) + end + + def test_rfc2822 + assert_equal(Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600, + Time.rfc2822("Fri, 21 Nov 1997 09:55:06 -0600")) + assert_equal(Time.utc(2003, 7, 1, 10, 52, 37) - 2 * 3600, + Time.rfc2822("Tue, 1 Jul 2003 10:52:37 +0200")) + assert_equal(Time.utc(1997, 11, 21, 10, 1, 10) + 6 * 3600, + Time.rfc2822("Fri, 21 Nov 1997 10:01:10 -0600")) + assert_equal(Time.utc(1997, 11, 21, 11, 0, 0) + 6 * 3600, + Time.rfc2822("Fri, 21 Nov 1997 11:00:00 -0600")) + assert_equal(Time.utc(1997, 11, 24, 14, 22, 1) + 8 * 3600, + Time.rfc2822("Mon, 24 Nov 1997 14:22:01 -0800")) + begin + Time.at(-1) + rescue ArgumentError + # ignore + else + assert_equal(Time.utc(1969, 2, 13, 23, 32, 54) + 3 * 3600 + 30 * 60, + Time.rfc2822("Thu, 13 Feb 1969 23:32:54 -0330")) + assert_equal(Time.utc(1969, 2, 13, 23, 32, 0) + 3 * 3600 + 30 * 60, + Time.rfc2822(" Thu, + 13 + Feb + 1969 + 23:32 + -0330 (Newfoundland Time)")) + end + assert_equal(Time.utc(1997, 11, 21, 9, 55, 6), + Time.rfc2822("21 Nov 97 09:55:06 GMT")) + assert_equal(Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600, + Time.rfc2822("Fri, 21 Nov 1997 09 : 55 : 06 -0600")) + assert_raise(ArgumentError) { + # inner comment is not supported. + Time.rfc2822("Fri, 21 Nov 1997 09(comment): 55 : 06 -0600") + } + end + + def test_rfc2616 + t = Time.utc(1994, 11, 6, 8, 49, 37) + assert_equal(t, Time.httpdate("Sun, 06 Nov 1994 08:49:37 GMT")) + assert_equal(t, Time.httpdate("Sunday, 06-Nov-94 08:49:37 GMT")) + assert_equal(t, Time.httpdate("Sun Nov 6 08:49:37 1994")) + assert_equal(Time.utc(1995, 11, 15, 6, 25, 24), + Time.httpdate("Wed, 15 Nov 1995 06:25:24 GMT")) + assert_equal(Time.utc(1995, 11, 15, 4, 58, 8), + Time.httpdate("Wed, 15 Nov 1995 04:58:08 GMT")) + assert_equal(Time.utc(1994, 11, 15, 8, 12, 31), + Time.httpdate("Tue, 15 Nov 1994 08:12:31 GMT")) + assert_equal(Time.utc(1994, 12, 1, 16, 0, 0), + Time.httpdate("Thu, 01 Dec 1994 16:00:00 GMT")) + assert_equal(Time.utc(1994, 10, 29, 19, 43, 31), + Time.httpdate("Sat, 29 Oct 1994 19:43:31 GMT")) + assert_equal(Time.utc(1994, 11, 15, 12, 45, 26), + Time.httpdate("Tue, 15 Nov 1994 12:45:26 GMT")) + assert_equal(Time.utc(1999, 12, 31, 23, 59, 59), + Time.httpdate("Fri, 31 Dec 1999 23:59:59 GMT")) + end + + def test_rfc3339 + t = Time.utc(1985, 4, 12, 23, 20, 50, 520000) + s = "1985-04-12T23:20:50.52Z" + assert_equal(t, Time.iso8601(s)) + assert_equal(s, t.iso8601(2)) + + t = Time.utc(1996, 12, 20, 0, 39, 57) + s = "1996-12-19T16:39:57-08:00" + assert_equal(t, Time.iso8601(s)) + # There is no way to generate time string with arbitrary timezone. + s = "1996-12-20T00:39:57Z" + assert_equal(t, Time.iso8601(s)) + assert_equal(s, t.iso8601) + + t = Time.utc(1990, 12, 31, 23, 59, 60) + s = "1990-12-31T23:59:60Z" + assert_equal(t, Time.iso8601(s)) + # leap second is representable only if timezone file has it. + s = "1990-12-31T15:59:60-08:00" + assert_equal(t, Time.iso8601(s)) + + begin + Time.at(-1) + rescue ArgumentError + # ignore + else + t = Time.utc(1937, 1, 1, 11, 40, 27, 870000) + s = "1937-01-01T12:00:27.87+00:20" + assert_equal(t, Time.iso8601(s)) + end + end + + # http://www.w3.org/TR/xmlschema-2/ + def test_xmlschema + assert_equal(Time.utc(1999, 5, 31, 13, 20, 0) + 5 * 3600, + Time.xmlschema("1999-05-31T13:20:00-05:00")) + assert_equal(Time.local(2000, 1, 20, 12, 0, 0), + Time.xmlschema("2000-01-20T12:00:00")) + assert_equal(Time.utc(2000, 1, 20, 12, 0, 0), + Time.xmlschema("2000-01-20T12:00:00Z")) + assert_equal(Time.utc(2000, 1, 20, 12, 0, 0) - 12 * 3600, + Time.xmlschema("2000-01-20T12:00:00+12:00")) + assert_equal(Time.utc(2000, 1, 20, 12, 0, 0) + 13 * 3600, + Time.xmlschema("2000-01-20T12:00:00-13:00")) + assert_equal(Time.utc(2000, 3, 4, 23, 0, 0) - 3 * 3600, + Time.xmlschema("2000-03-04T23:00:00+03:00")) + assert_equal(Time.utc(2000, 3, 4, 20, 0, 0), + Time.xmlschema("2000-03-04T20:00:00Z")) + assert_equal(Time.local(2000, 1, 15, 0, 0, 0), + Time.xmlschema("2000-01-15T00:00:00")) + assert_equal(Time.local(2000, 2, 15, 0, 0, 0), + Time.xmlschema("2000-02-15T00:00:00")) + assert_equal(Time.local(2000, 1, 15, 12, 0, 0), + Time.xmlschema("2000-01-15T12:00:00")) + assert_equal(Time.utc(2000, 1, 16, 12, 0, 0), + Time.xmlschema("2000-01-16T12:00:00Z")) + assert_equal(Time.local(2000, 1, 1, 12, 0, 0), + Time.xmlschema("2000-01-01T12:00:00")) + assert_equal(Time.utc(1999, 12, 31, 23, 0, 0), + Time.xmlschema("1999-12-31T23:00:00Z")) + assert_equal(Time.local(2000, 1, 16, 12, 0, 0), + Time.xmlschema("2000-01-16T12:00:00")) + assert_equal(Time.local(2000, 1, 16, 0, 0, 0), + Time.xmlschema("2000-01-16T00:00:00")) + assert_equal(Time.utc(2000, 1, 12, 12, 13, 14), + Time.xmlschema("2000-01-12T12:13:14Z")) + assert_equal(Time.utc(2001, 4, 17, 19, 23, 17, 300000), + Time.xmlschema("2001-04-17T19:23:17.3Z")) + end + + def test_encode_xmlschema + t = Time.utc(2001, 4, 17, 19, 23, 17, 300000) + assert_equal("2001-04-17T19:23:17Z", t.xmlschema) + assert_equal("2001-04-17T19:23:17.3Z", t.xmlschema(1)) + assert_equal("2001-04-17T19:23:17.300000Z", t.xmlschema(6)) + assert_equal("2001-04-17T19:23:17.3000000Z", t.xmlschema(7)) + + t = Time.utc(2001, 4, 17, 19, 23, 17, 123456) + assert_equal("2001-04-17T19:23:17.1234560Z", t.xmlschema(7)) + assert_equal("2001-04-17T19:23:17.123456Z", t.xmlschema(6)) + assert_equal("2001-04-17T19:23:17.12345Z", t.xmlschema(5)) + assert_equal("2001-04-17T19:23:17.1Z", t.xmlschema(1)) + + begin + Time.at(-1) + rescue ArgumentError + # ignore + else + t = Time.utc(1960, 12, 31, 23, 0, 0, 123456) + assert_equal("1960-12-31T23:00:00.123456Z", t.xmlschema(6)) + end + end + + def test_completion + now = Time.local(2001,11,29,21,26,35) + assert_equal(Time.local( 2001,11,29,21,12), + Time.parse("2001/11/29 21:12", now)) + assert_equal(Time.local( 2001,11,29), + Time.parse("2001/11/29", now)) + assert_equal(Time.local( 2001,11,29), + Time.parse( "11/29", now)) + #assert_equal(Time.local(2001,11,1), Time.parse("Nov", now)) + assert_equal(Time.local( 2001,11,29,10,22), + Time.parse( "10:22", now)) + end + + def test_invalid + # They were actually used in some web sites. + assert_raise(ArgumentError) { Time.httpdate("1 Dec 2001 10:23:57 GMT") } + assert_raise(ArgumentError) { Time.httpdate("Sat, 1 Dec 2001 10:25:42 GMT") } + assert_raise(ArgumentError) { Time.httpdate("Sat, 1-Dec-2001 10:53:55 GMT") } + assert_raise(ArgumentError) { Time.httpdate("Saturday, 01-Dec-2001 10:15:34 GMT") } + assert_raise(ArgumentError) { Time.httpdate("Saturday, 01-Dec-101 11:10:07 GMT") } + assert_raise(ArgumentError) { Time.httpdate("Fri, 30 Nov 2001 21:30:00 JST") } + + # They were actually used in some mails. + assert_raise(ArgumentError) { Time.rfc2822("01-5-20") } + assert_raise(ArgumentError) { Time.rfc2822("7/21/00") } + assert_raise(ArgumentError) { Time.rfc2822("2001-8-28") } + assert_raise(ArgumentError) { Time.rfc2822("00-5-6 1:13:06") } + assert_raise(ArgumentError) { Time.rfc2822("2001-9-27 9:36:49") } + assert_raise(ArgumentError) { Time.rfc2822("2000-12-13 11:01:11") } + assert_raise(ArgumentError) { Time.rfc2822("2001/10/17 04:29:55") } + assert_raise(ArgumentError) { Time.rfc2822("9/4/2001 9:23:19 PM") } + assert_raise(ArgumentError) { Time.rfc2822("01 Nov 2001 09:04:31") } + assert_raise(ArgumentError) { Time.rfc2822("13 Feb 2001 16:4 GMT") } + assert_raise(ArgumentError) { Time.rfc2822("01 Oct 00 5:41:19 PM") } + assert_raise(ArgumentError) { Time.rfc2822("2 Jul 00 00:51:37 JST") } + assert_raise(ArgumentError) { Time.rfc2822("01 11 2001 06:55:57 -0500") } + assert_raise(ArgumentError) { Time.rfc2822("18 \343\366\356\341\370 2000") } + assert_raise(ArgumentError) { Time.rfc2822("Fri, Oct 2001 18:53:32") } + assert_raise(ArgumentError) { Time.rfc2822("Fri, 2 Nov 2001 03:47:54") } + assert_raise(ArgumentError) { Time.rfc2822("Fri, 27 Jul 2001 11.14.14 +0200") } + assert_raise(ArgumentError) { Time.rfc2822("Thu, 2 Nov 2000 04:13:53 -600") } + assert_raise(ArgumentError) { Time.rfc2822("Wed, 5 Apr 2000 22:57:09 JST") } + assert_raise(ArgumentError) { Time.rfc2822("Mon, 11 Sep 2000 19:47:33 00000") } + assert_raise(ArgumentError) { Time.rfc2822("Fri, 28 Apr 2000 20:40:47 +-900") } + assert_raise(ArgumentError) { Time.rfc2822("Fri, 19 Jan 2001 8:15:36 AM -0500") } + assert_raise(ArgumentError) { Time.rfc2822("Thursday, Sep 27 2001 7:42:35 AM EST") } + assert_raise(ArgumentError) { Time.rfc2822("3/11/2001 1:31:57 PM Pacific Daylight Time") } + assert_raise(ArgumentError) { Time.rfc2822("Mi, 28 Mrz 2001 11:51:36") } + assert_raise(ArgumentError) { Time.rfc2822("P, 30 sept 2001 23:03:14") } + assert_raise(ArgumentError) { Time.rfc2822("fr, 11 aug 2000 18:39:22") } + assert_raise(ArgumentError) { Time.rfc2822("Fr, 21 Sep 2001 17:44:03 -1000") } + assert_raise(ArgumentError) { Time.rfc2822("Mo, 18 Jun 2001 19:21:40 -1000") } + assert_raise(ArgumentError) { Time.rfc2822("l\366, 12 aug 2000 18:53:20") } + assert_raise(ArgumentError) { Time.rfc2822("l\366, 26 maj 2001 00:15:58") } + assert_raise(ArgumentError) { Time.rfc2822("Dom, 30 Sep 2001 17:36:30") } + assert_raise(ArgumentError) { Time.rfc2822("%&, 31 %2/ 2000 15:44:47 -0500") } + assert_raise(ArgumentError) { Time.rfc2822("dom, 26 ago 2001 03:57:07 -0300") } + assert_raise(ArgumentError) { Time.rfc2822("ter, 04 set 2001 16:27:58 -0300") } + assert_raise(ArgumentError) { Time.rfc2822("Wen, 3 oct 2001 23:17:49 -0400") } + assert_raise(ArgumentError) { Time.rfc2822("Wen, 3 oct 2001 23:17:49 -0400") } + assert_raise(ArgumentError) { Time.rfc2822("ele, 11 h: 2000 12:42:15 -0500") } + assert_raise(ArgumentError) { Time.rfc2822("Tue, 14 Aug 2001 3:55:3 +0200") } + assert_raise(ArgumentError) { Time.rfc2822("Fri, 25 Aug 2000 9:3:48 +0800") } + assert_raise(ArgumentError) { Time.rfc2822("Fri, 1 Dec 2000 0:57:50 EST") } + assert_raise(ArgumentError) { Time.rfc2822("Mon, 7 May 2001 9:39:51 +0200") } + assert_raise(ArgumentError) { Time.rfc2822("Wed, 1 Aug 2001 16:9:15 +0200") } + assert_raise(ArgumentError) { Time.rfc2822("Wed, 23 Aug 2000 9:17:36 +0800") } + assert_raise(ArgumentError) { Time.rfc2822("Fri, 11 Aug 2000 10:4:42 +0800") } + assert_raise(ArgumentError) { Time.rfc2822("Sat, 15 Sep 2001 13:22:2 +0300") } + assert_raise(ArgumentError) { Time.rfc2822("Wed,16 \276\305\324\302 2001 20:06:25 +0800") } + assert_raise(ArgumentError) { Time.rfc2822("Wed,7 \312\256\322\273\324\302 2001 23:47:22 +0800") } + assert_raise(ArgumentError) { Time.rfc2822("=?iso-8859-1?Q?(=C5=DA),?= 10 2 2001 23:32:26 +0900 (JST)") } + assert_raise(ArgumentError) { Time.rfc2822("\307\341\314\343\332\311, 30 \344\346\335\343\310\321 2001 10:01:06") } + assert_raise(ArgumentError) { Time.rfc2822("=?iso-8859-1?Q?(=BF=E5),?= 12 =?iso-8859-1?Q?9=B7=EE?= 2001 14:52:41\n+0900 (JST)") } + end + + def test_zone_0000 + assert_equal(true, Time.parse("2000-01-01T00:00:00Z").utc?) + assert_equal(true, Time.parse("2000-01-01T00:00:00-00:00").utc?) + assert_equal(false, Time.parse("2000-01-01T00:00:00+00:00").utc?) + assert_equal(false, Time.parse("Sat, 01 Jan 2000 00:00:00 GMT").utc?) + assert_equal(true, Time.parse("Sat, 01 Jan 2000 00:00:00 -0000").utc?) + assert_equal(false, Time.parse("Sat, 01 Jan 2000 00:00:00 +0000").utc?) + assert_equal(false, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 GMT").utc?) + assert_equal(true, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 -0000").utc?) + assert_equal(false, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 +0000").utc?) + assert_equal(true, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 UTC").utc?) + end + + def test_parse_leap_second + t = Time.utc(1998,12,31,23,59,59) + assert_equal(t, Time.parse("Thu Dec 31 23:59:59 UTC 1998")) + assert_equal(t, Time.parse("Fri Dec 31 23:59:59 -0000 1998"));t.localtime + assert_equal(t, Time.parse("Fri Jan 1 08:59:59 +0900 1999")) + assert_equal(t, Time.parse("Fri Jan 1 00:59:59 +0100 1999")) + assert_equal(t, Time.parse("Fri Dec 31 23:59:59 +0000 1998")) + assert_equal(t, Time.parse("Fri Dec 31 22:59:59 -0100 1998"));t.utc + t += 1 + assert_equal(t, Time.parse("Thu Dec 31 23:59:60 UTC 1998")) + assert_equal(t, Time.parse("Fri Dec 31 23:59:60 -0000 1998"));t.localtime + assert_equal(t, Time.parse("Fri Jan 1 08:59:60 +0900 1999")) + assert_equal(t, Time.parse("Fri Jan 1 00:59:60 +0100 1999")) + assert_equal(t, Time.parse("Fri Dec 31 23:59:60 +0000 1998")) + assert_equal(t, Time.parse("Fri Dec 31 22:59:60 -0100 1998"));t.utc + t += 1 if t.sec == 60 + assert_equal(t, Time.parse("Thu Jan 1 00:00:00 UTC 1999")) + assert_equal(t, Time.parse("Fri Jan 1 00:00:00 -0000 1999"));t.localtime + assert_equal(t, Time.parse("Fri Jan 1 09:00:00 +0900 1999")) + assert_equal(t, Time.parse("Fri Jan 1 01:00:00 +0100 1999")) + assert_equal(t, Time.parse("Fri Jan 1 00:00:00 +0000 1999")) + assert_equal(t, Time.parse("Fri Dec 31 23:00:00 -0100 1998")) + end + + def test_rfc2822_leap_second + t = Time.utc(1998,12,31,23,59,59) + assert_equal(t, Time.rfc2822("Thu, 31 Dec 1998 23:59:59 UTC")) + assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:59 -0000"));t.localtime + assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 08:59:59 +0900")) + assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:59:59 +0100")) + assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:59 +0000")) + assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:59 -0100"));t.utc + t += 1 + assert_equal(t, Time.rfc2822("Thu, 31 Dec 1998 23:59:60 UTC")) + assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:60 -0000"));t.localtime + assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 08:59:60 +0900")) + assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:59:60 +0100")) + assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:60 +0000")) + assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:60 -0100"));t.utc + t += 1 if t.sec == 60 + assert_equal(t, Time.rfc2822("Thu, 1 Jan 1999 00:00:00 UTC")) + assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:00:00 -0000"));t.localtime + assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 09:00:00 +0900")) + assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 01:00:00 +0100")) + assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:00:00 +0000")) + assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:00:00 -0100")) + end + + def test_xmlschema_leap_second + t = Time.utc(1998,12,31,23,59,59) + assert_equal(t, Time.xmlschema("1998-12-31T23:59:59Z")) + assert_equal(t, Time.xmlschema("1998-12-31T23:59:59-00:00"));t.localtime + assert_equal(t, Time.xmlschema("1999-01-01T08:59:59+09:00")) + assert_equal(t, Time.xmlschema("1999-01-01T00:59:59+01:00")) + assert_equal(t, Time.xmlschema("1998-12-31T23:59:59+00:00")) + assert_equal(t, Time.xmlschema("1998-12-31T22:59:59-01:00"));t.utc + t += 1 + assert_equal(t, Time.xmlschema("1998-12-31T23:59:60Z")) + assert_equal(t, Time.xmlschema("1998-12-31T23:59:60-00:00"));t.localtime + assert_equal(t, Time.xmlschema("1999-01-01T08:59:60+09:00")) + assert_equal(t, Time.xmlschema("1999-01-01T00:59:60+01:00")) + assert_equal(t, Time.xmlschema("1998-12-31T23:59:60+00:00")) + assert_equal(t, Time.xmlschema("1998-12-31T22:59:60-01:00"));t.utc + t += 1 if t.sec == 60 + assert_equal(t, Time.xmlschema("1999-01-01T00:00:00Z")) + assert_equal(t, Time.xmlschema("1999-01-01T00:00:00-00:00"));t.localtime + assert_equal(t, Time.xmlschema("1999-01-01T09:00:00+09:00")) + assert_equal(t, Time.xmlschema("1999-01-01T01:00:00+01:00")) + assert_equal(t, Time.xmlschema("1999-01-01T00:00:00+00:00")) + assert_equal(t, Time.xmlschema("1998-12-31T23:00:00-01:00")) + end + + def test_ruby_talk_152866 + t = Time::xmlschema('2005-08-30T22:48:00-07:00') + assert_equal(31, t.day) + assert_equal(8, t.mon) + end + + def test_parse_fraction + assert_equal(500000, Time.parse("2000-01-01T00:00:00.5+00:00").tv_usec) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/timeout.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/timeout.rb new file mode 100644 index 0000000000..dc92964c0b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/timeout.rb @@ -0,0 +1,105 @@ +#-- +# = timeout.rb +# +# execution timeout +# +# = Copyright +# +# Copyright:: (C) 2000 Network Applied Communication Laboratory, Inc. +# Copyright:: (C) 2000 Information-technology Promotion Agency, Japan +# +#++ +# +# = Description +# +# A way of performing a potentially long-running operation in a thread, and +# terminating it's execution if it hasn't finished within fixed amount of +# time. +# +# Previous versions of timeout didn't use a module for namespace. This version +# provides both Timeout.timeout, and a backwards-compatible #timeout. +# +# = Synopsis +# +# require 'timeout' +# status = Timeout::timeout(5) { +# # Something that should be interrupted if it takes too much time... +# } +# + +module Timeout + + ## + # Raised by Timeout#timeout when the block times out. + + class Error 0 + tmp = @@systmpdir + else + for dir in [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], + ENV['USERPROFILE'], @@systmpdir, '/tmp'] + if dir and File.directory?(dir) and File.writable?(dir) + tmp = dir + break + end + end + end + File.expand_path(tmp) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/tracer.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/tracer.rb new file mode 100644 index 0000000000..71aa49c306 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/tracer.rb @@ -0,0 +1,167 @@ +# +# tracer.rb - +# $Release Version: 0.2$ +# $Revision: 1.8 $ +# $Date: 1998/05/19 03:42:49 $ +# by Keiju ISHITSUKA(Nippon Rational Inc.) +# +# -- +# +# +# + +# +# tracer main class +# +class Tracer + @RCS_ID='-$Id: tracer.rb,v 1.8 1998/05/19 03:42:49 keiju Exp keiju $-' + + @stdout = STDOUT + @verbose = false + class << self + attr :verbose, true + alias verbose? verbose + attr :stdout, true + end + + EVENT_SYMBOL = { + "line" => "-", + "call" => ">", + "return" => "<", + "class" => "C", + "end" => "E", + "c-call" => ">", + "c-return" => "<", + } + + def initialize + @threads = Hash.new + if defined? Thread.main + @threads[Thread.main.object_id] = 0 + else + @threads[Thread.current.object_id] = 0 + end + + @get_line_procs = {} + + @filters = [] + end + + def stdout + Tracer.stdout + end + + def on + if block_given? + on + begin + yield + ensure + off + end + else + set_trace_func method(:trace_func).to_proc + stdout.print "Trace on\n" if Tracer.verbose? + end + end + + def off + set_trace_func nil + stdout.print "Trace off\n" if Tracer.verbose? + end + + def add_filter(p = proc) + @filters.push p + end + + def set_get_line_procs(file, p = proc) + @get_line_procs[file] = p + end + + def get_line(file, line) + if p = @get_line_procs[file] + return p.call(line) + end + + unless list = SCRIPT_LINES__[file] + begin + f = open(file) + begin + SCRIPT_LINES__[file] = list = f.readlines + ensure + f.close + end + rescue + SCRIPT_LINES__[file] = list = [] + end + end + + if l = list[line - 1] + l + else + "-\n" + end + end + + def get_thread_no + if no = @threads[Thread.current.object_id] + no + else + @threads[Thread.current.object_id] = @threads.size + end + end + + def trace_func(event, file, line, id, binding, klass, *) + return if file == __FILE__ + + for p in @filters + return unless p.call event, file, line, id, binding, klass + end + + saved_crit = Thread.critical + Thread.critical = true + stdout.printf("#%d:%s:%d:%s:%s: %s", + get_thread_no, + file, + line, + klass || '', + EVENT_SYMBOL[event], + get_line(file, line)) + Thread.critical = saved_crit + end + + Single = new + def Tracer.on + if block_given? + Single.on{yield} + else + Single.on + end + end + + def Tracer.off + Single.off + end + + def Tracer.set_get_line_procs(file_name, p = proc) + Single.set_get_line_procs(file_name, p) + end + + def Tracer.add_filter(p = proc) + Single.add_filter(p) + end + +end + +SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__ + +if $0 == __FILE__ + # direct call + + $0 = ARGV[0] + ARGV.shift + Tracer.on + require $0 +elsif caller(0).size == 1 + Tracer.on +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/tsort.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/tsort.rb new file mode 100644 index 0000000000..a014e7f6c2 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/tsort.rb @@ -0,0 +1,290 @@ +#!/usr/bin/env ruby +#-- +# tsort.rb - provides a module for topological sorting and strongly connected components. +#++ +# + +# +# TSort implements topological sorting using Tarjan's algorithm for +# strongly connected components. +# +# TSort is designed to be able to be used with any object which can be +# interpreted as a directed graph. +# +# TSort requires two methods to interpret an object as a graph, +# tsort_each_node and tsort_each_child. +# +# * tsort_each_node is used to iterate for all nodes over a graph. +# * tsort_each_child is used to iterate for child nodes of a given node. +# +# The equality of nodes are defined by eql? and hash since +# TSort uses Hash internally. +# +# == A Simple Example +# +# The following example demonstrates how to mix the TSort module into an +# existing class (in this case, Hash). Here, we're treating each key in +# the hash as a node in the graph, and so we simply alias the required +# #tsort_each_node method to Hash's #each_key method. For each key in the +# hash, the associated value is an array of the node's child nodes. This +# choice in turn leads to our implementation of the required #tsort_each_child +# method, which fetches the array of child nodes and then iterates over that +# array using the user-supplied block. +# +# require 'tsort' +# +# class Hash +# include TSort +# alias tsort_each_node each_key +# def tsort_each_child(node, &block) +# fetch(node).each(&block) +# end +# end +# +# {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort +# #=> [3, 2, 1, 4] +# +# {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components +# #=> [[4], [2, 3], [1]] +# +# == A More Realistic Example +# +# A very simple `make' like tool can be implemented as follows: +# +# require 'tsort' +# +# class Make +# def initialize +# @dep = {} +# @dep.default = [] +# end +# +# def rule(outputs, inputs=[], &block) +# triple = [outputs, inputs, block] +# outputs.each {|f| @dep[f] = [triple]} +# @dep[triple] = inputs +# end +# +# def build(target) +# each_strongly_connected_component_from(target) {|ns| +# if ns.length != 1 +# fs = ns.delete_if {|n| Array === n} +# raise TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}") +# end +# n = ns.first +# if Array === n +# outputs, inputs, block = n +# inputs_time = inputs.map {|f| File.mtime f}.max +# begin +# outputs_time = outputs.map {|f| File.mtime f}.min +# rescue Errno::ENOENT +# outputs_time = nil +# end +# if outputs_time == nil || +# inputs_time != nil && outputs_time <= inputs_time +# sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i +# block.call +# end +# end +# } +# end +# +# def tsort_each_child(node, &block) +# @dep[node].each(&block) +# end +# include TSort +# end +# +# def command(arg) +# print arg, "\n" +# system arg +# end +# +# m = Make.new +# m.rule(%w[t1]) { command 'date > t1' } +# m.rule(%w[t2]) { command 'date > t2' } +# m.rule(%w[t3]) { command 'date > t3' } +# m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' } +# m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' } +# m.build('t5') +# +# == Bugs +# +# * 'tsort.rb' is wrong name because this library uses +# Tarjan's algorithm for strongly connected components. +# Although 'strongly_connected_components.rb' is correct but too long. +# +# == References +# +# R. E. Tarjan, "Depth First Search and Linear Graph Algorithms", +# SIAM Journal on Computing, Vol. 1, No. 2, pp. 146-160, June 1972. +# + +module TSort + class Cyclic < StandardError + end + + # + # Returns a topologically sorted array of nodes. + # The array is sorted from children to parents, i.e. + # the first element has no child and the last node has no parent. + # + # If there is a cycle, TSort::Cyclic is raised. + # + def tsort + result = [] + tsort_each {|element| result << element} + result + end + + # + # The iterator version of the #tsort method. + # obj.tsort_each is similar to obj.tsort.each, but + # modification of _obj_ during the iteration may lead to unexpected results. + # + # #tsort_each returns +nil+. + # If there is a cycle, TSort::Cyclic is raised. + # + def tsort_each # :yields: node + each_strongly_connected_component {|component| + if component.size == 1 + yield component.first + else + raise Cyclic.new("topological sort failed: #{component.inspect}") + end + } + end + + # + # Returns strongly connected components as an array of arrays of nodes. + # The array is sorted from children to parents. + # Each elements of the array represents a strongly connected component. + # + def strongly_connected_components + result = [] + each_strongly_connected_component {|component| result << component} + result + end + + # + # The iterator version of the #strongly_connected_components method. + # obj.each_strongly_connected_component is similar to + # obj.strongly_connected_components.each, but + # modification of _obj_ during the iteration may lead to unexpected results. + # + # + # #each_strongly_connected_component returns +nil+. + # + def each_strongly_connected_component # :yields: nodes + id_map = {} + stack = [] + tsort_each_node {|node| + unless id_map.include? node + each_strongly_connected_component_from(node, id_map, stack) {|c| + yield c + } + end + } + nil + end + + # + # Iterates over strongly connected component in the subgraph reachable from + # _node_. + # + # Return value is unspecified. + # + # #each_strongly_connected_component_from doesn't call #tsort_each_node. + # + def each_strongly_connected_component_from(node, id_map={}, stack=[]) # :yields: nodes + minimum_id = node_id = id_map[node] = id_map.size + stack_length = stack.length + stack << node + + tsort_each_child(node) {|child| + if id_map.include? child + child_id = id_map[child] + minimum_id = child_id if child_id && child_id < minimum_id + else + sub_minimum_id = + each_strongly_connected_component_from(child, id_map, stack) {|c| + yield c + } + minimum_id = sub_minimum_id if sub_minimum_id < minimum_id + end + } + + if node_id == minimum_id + component = stack.slice!(stack_length .. -1) + component.each {|n| id_map[n] = nil} + yield component + end + + minimum_id + end + + # + # Should be implemented by a extended class. + # + # #tsort_each_node is used to iterate for all nodes over a graph. + # + def tsort_each_node # :yields: node + raise NotImplementedError.new + end + + # + # Should be implemented by a extended class. + # + # #tsort_each_child is used to iterate for child nodes of _node_. + # + def tsort_each_child(node) # :yields: child + raise NotImplementedError.new + end +end + +if __FILE__ == $0 + require 'test/unit' + + class TSortHash < Hash # :nodoc: + include TSort + alias tsort_each_node each_key + def tsort_each_child(node, &block) + fetch(node).each(&block) + end + end + + class TSortArray < Array # :nodoc: + include TSort + alias tsort_each_node each_index + def tsort_each_child(node, &block) + fetch(node).each(&block) + end + end + + class TSortTest < Test::Unit::TestCase # :nodoc: + def test_dag + h = TSortHash[{1=>[2, 3], 2=>[3], 3=>[]}] + assert_equal([3, 2, 1], h.tsort) + assert_equal([[3], [2], [1]], h.strongly_connected_components) + end + + def test_cycle + h = TSortHash[{1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}] + assert_equal([[4], [2, 3], [1]], + h.strongly_connected_components.map {|nodes| nodes.sort}) + assert_raise(TSort::Cyclic) { h.tsort } + end + + def test_array + a = TSortArray[[1], [0], [0], [2]] + assert_equal([[0, 1], [2], [3]], + a.strongly_connected_components.map {|nodes| nodes.sort}) + + a = TSortArray[[], [0]] + assert_equal([[0], [1]], + a.strongly_connected_components.map {|nodes| nodes.sort}) + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/un.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/un.rb new file mode 100644 index 0000000000..9bf6c13146 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/un.rb @@ -0,0 +1,235 @@ +# +# = un.rb +# +# Copyright (c) 2003 WATANABE Hirofumi +# +# This program is free software. +# You can distribute/modify this program under the same terms of Ruby. +# +# == Utilities to replace common UNIX commands in Makefiles etc +# +# == SYNOPSIS +# +# ruby -run -e cp -- [OPTION] SOURCE DEST +# ruby -run -e ln -- [OPTION] TARGET LINK_NAME +# ruby -run -e mv -- [OPTION] SOURCE DEST +# ruby -run -e rm -- [OPTION] FILE +# ruby -run -e mkdir -- [OPTION] DIRS +# ruby -run -e rmdir -- [OPTION] DIRS +# ruby -run -e install -- [OPTION] SOURCE DEST +# ruby -run -e chmod -- [OPTION] OCTAL-MODE FILE +# ruby -run -e touch -- [OPTION] FILE +# ruby -run -e help [COMMAND] + +require "fileutils" +require "optparse" + +module FileUtils +# @fileutils_label = "" + @fileutils_output = $stdout +end + +def setup(options = "") + ARGV.map! do |x| + case x + when /^-/ + x.delete "^-#{options}v" + when /[*?\[{]/ + Dir[x] + else + x + end + end + ARGV.flatten! + ARGV.delete_if{|x| x == "-"} + opt_hash = {} + OptionParser.new do |o| + options.scan(/.:?/) do |s| + o.on("-" + s.tr(":", " ")) do |val| + opt_hash[s.delete(":").intern] = val + end + end + o.on("-v") do opt_hash[:verbose] = true end + o.parse! + end + yield ARGV, opt_hash +end + +## +# Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY +# +# ruby -run -e cp -- [OPTION] SOURCE DEST +# +# -p preserve file attributes if possible +# -r copy recursively +# -v verbose +# + +def cp + setup("pr") do |argv, options| + cmd = "cp" + cmd += "_r" if options.delete :r + options[:preserve] = true if options.delete :p + dest = argv.pop + argv = argv[0] if argv.size == 1 + FileUtils.send cmd, argv, dest, options + end +end + +## +# Create a link to the specified TARGET with LINK_NAME. +# +# ruby -run -e ln -- [OPTION] TARGET LINK_NAME +# +# -s make symbolic links instead of hard links +# -f remove existing destination files +# -v verbose +# + +def ln + setup("sf") do |argv, options| + cmd = "ln" + cmd += "_s" if options.delete :s + options[:force] = true if options.delete :f + dest = argv.pop + argv = argv[0] if argv.size == 1 + FileUtils.send cmd, argv, dest, options + end +end + +## +# Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY. +# +# ruby -run -e mv -- [OPTION] SOURCE DEST +# +# -v verbose +# + +def mv + setup do |argv, options| + dest = argv.pop + argv = argv[0] if argv.size == 1 + FileUtils.mv argv, dest, options + end +end + +## +# Remove the FILE +# +# ruby -run -e rm -- [OPTION] FILE +# +# -f ignore nonexistent files +# -r remove the contents of directories recursively +# -v verbose +# + +def rm + setup("fr") do |argv, options| + cmd = "rm" + cmd += "_r" if options.delete :r + options[:force] = true if options.delete :f + FileUtils.send cmd, argv, options + end +end + +## +# Create the DIR, if they do not already exist. +# +# ruby -run -e mkdir -- [OPTION] DIR +# +# -p no error if existing, make parent directories as needed +# -v verbose +# + +def mkdir + setup("p") do |argv, options| + cmd = "mkdir" + cmd += "_p" if options.delete :p + FileUtils.send cmd, argv, options + end +end + +## +# Remove the DIR. +# +# ruby -run -e rmdir -- [OPTION] DIR +# +# -v verbose +# + +def rmdir + setup do |argv, options| + FileUtils.rmdir argv, options + end +end + +## +# Copy SOURCE to DEST. +# +# ruby -run -e install -- [OPTION] SOURCE DEST +# +# -p apply access/modification times of SOURCE files to +# corresponding destination files +# -m set permission mode (as in chmod), instead of 0755 +# -v verbose +# + +def install + setup("pm:") do |argv, options| + options[:mode] = (mode = options.delete :m) ? mode.oct : 0755 + options[:preserve] = true if options.delete :p + dest = argv.pop + argv = argv[0] if argv.size == 1 + FileUtils.install argv, dest, options + end +end + +## +# Change the mode of each FILE to OCTAL-MODE. +# +# ruby -run -e chmod -- [OPTION] OCTAL-MODE FILE +# +# -v verbose +# + +def chmod + setup do |argv, options| + mode = argv.shift.oct + FileUtils.chmod mode, argv, options + end +end + +## +# Update the access and modification times of each FILE to the current time. +# +# ruby -run -e touch -- [OPTION] FILE +# +# -v verbose +# + +def touch + setup do |argv, options| + FileUtils.touch argv, options + end +end + +## +# Display help message. +# +# ruby -run -e help [COMMAND] +# + +def help + setup do |argv,| + all = argv.empty? + open(__FILE__) do |me| + while me.gets("##\n") + if help = me.gets("\n\n") + if all or argv.delete help[/-e \w+/].sub(/-e /, "") + print help.gsub(/^# ?/, "") + end + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri.rb new file mode 100644 index 0000000000..d06fb406c4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri.rb @@ -0,0 +1,28 @@ +# +# URI support for Ruby +# +# Author:: Akira Yamada +# Documentation:: Akira Yamada , Dmitry V. Sabanin +# License:: +# Copyright (c) 2001 akira yamada +# You can redistribute it and/or modify it under the same term as Ruby. +# Revision:: $Id: uri.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# +# See URI for documentation +# + +module URI + # :stopdoc: + VERSION_CODE = '000911'.freeze + VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze + # :startdoc: + +end + +require 'uri/common' +require 'uri/generic' +require 'uri/ftp' +require 'uri/http' +require 'uri/https' +require 'uri/ldap' +require 'uri/mailto' diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/common.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/common.rb new file mode 100644 index 0000000000..dd80e4faf3 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/common.rb @@ -0,0 +1,611 @@ +# = uri/common.rb +# +# Author:: Akira Yamada +# Revision:: $Id: common.rb 11747 2007-02-15 02:41:45Z knu $ +# License:: +# You can redistribute it and/or modify it under the same term as Ruby. +# + +module URI + module REGEXP + # + # Patterns used to parse URI's + # + module PATTERN + # :stopdoc: + + # RFC 2396 (URI Generic Syntax) + # RFC 2732 (IPv6 Literal Addresses in URL's) + # RFC 2373 (IPv6 Addressing Architecture) + + # alpha = lowalpha | upalpha + ALPHA = "a-zA-Z" + # alphanum = alpha | digit + ALNUM = "#{ALPHA}\\d" + + # hex = digit | "A" | "B" | "C" | "D" | "E" | "F" | + # "a" | "b" | "c" | "d" | "e" | "f" + HEX = "a-fA-F\\d" + # escaped = "%" hex hex + ESCAPED = "%[#{HEX}]{2}" + # mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | + # "(" | ")" + # unreserved = alphanum | mark + UNRESERVED = "-_.!~*'()#{ALNUM}" + # reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | + # "$" | "," + # reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | + # "$" | "," | "[" | "]" (RFC 2732) + RESERVED = ";/?:@&=+$,\\[\\]" + + # uric = reserved | unreserved | escaped + URIC = "(?:[#{UNRESERVED}#{RESERVED}]|#{ESCAPED})" + # uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" | + # "&" | "=" | "+" | "$" | "," + URIC_NO_SLASH = "(?:[#{UNRESERVED};?:@&=+$,]|#{ESCAPED})" + # query = *uric + QUERY = "#{URIC}*" + # fragment = *uric + FRAGMENT = "#{URIC}*" + + # domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum + DOMLABEL = "(?:[#{ALNUM}](?:[-#{ALNUM}]*[#{ALNUM}])?)" + # toplabel = alpha | alpha *( alphanum | "-" ) alphanum + TOPLABEL = "(?:[#{ALPHA}](?:[-#{ALNUM}]*[#{ALNUM}])?)" + # hostname = *( domainlabel "." ) toplabel [ "." ] + HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?" + + # RFC 2373, APPENDIX B: + # IPv6address = hexpart [ ":" IPv4address ] + # IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT + # hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ] + # hexseq = hex4 *( ":" hex4) + # hex4 = 1*4HEXDIG + # + # XXX: This definition has a flaw. "::" + IPv4address must be + # allowed too. Here is a replacement. + # + # IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT + IPV4ADDR = "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}" + # hex4 = 1*4HEXDIG + HEX4 = "[#{HEX}]{1,4}" + # lastpart = hex4 | IPv4address + LASTPART = "(?:#{HEX4}|#{IPV4ADDR})" + # hexseq1 = *( hex4 ":" ) hex4 + HEXSEQ1 = "(?:#{HEX4}:)*#{HEX4}" + # hexseq2 = *( hex4 ":" ) lastpart + HEXSEQ2 = "(?:#{HEX4}:)*#{LASTPART}" + # IPv6address = hexseq2 | [ hexseq1 ] "::" [ hexseq2 ] + IPV6ADDR = "(?:#{HEXSEQ2}|(?:#{HEXSEQ1})?::(?:#{HEXSEQ2})?)" + + # IPv6prefix = ( hexseq1 | [ hexseq1 ] "::" [ hexseq1 ] ) "/" 1*2DIGIT + # unused + + # ipv6reference = "[" IPv6address "]" (RFC 2732) + IPV6REF = "\\[#{IPV6ADDR}\\]" + + # host = hostname | IPv4address + # host = hostname | IPv4address | IPv6reference (RFC 2732) + HOST = "(?:#{HOSTNAME}|#{IPV4ADDR}|#{IPV6REF})" + # port = *digit + PORT = '\d*' + # hostport = host [ ":" port ] + HOSTPORT = "#{HOST}(?::#{PORT})?" + + # userinfo = *( unreserved | escaped | + # ";" | ":" | "&" | "=" | "+" | "$" | "," ) + USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})*" + + # pchar = unreserved | escaped | + # ":" | "@" | "&" | "=" | "+" | "$" | "," + PCHAR = "(?:[#{UNRESERVED}:@&=+$,]|#{ESCAPED})" + # param = *pchar + PARAM = "#{PCHAR}*" + # segment = *pchar *( ";" param ) + SEGMENT = "#{PCHAR}*(?:;#{PARAM})*" + # path_segments = segment *( "/" segment ) + PATH_SEGMENTS = "#{SEGMENT}(?:/#{SEGMENT})*" + + # server = [ [ userinfo "@" ] hostport ] + SERVER = "(?:#{USERINFO}@)?#{HOSTPORT}" + # reg_name = 1*( unreserved | escaped | "$" | "," | + # ";" | ":" | "@" | "&" | "=" | "+" ) + REG_NAME = "(?:[#{UNRESERVED}$,;+@&=+]|#{ESCAPED})+" + # authority = server | reg_name + AUTHORITY = "(?:#{SERVER}|#{REG_NAME})" + + # rel_segment = 1*( unreserved | escaped | + # ";" | "@" | "&" | "=" | "+" | "$" | "," ) + REL_SEGMENT = "(?:[#{UNRESERVED};@&=+$,]|#{ESCAPED})+" + + # scheme = alpha *( alpha | digit | "+" | "-" | "." ) + SCHEME = "[#{ALPHA}][-+.#{ALPHA}\\d]*" + + # abs_path = "/" path_segments + ABS_PATH = "/#{PATH_SEGMENTS}" + # rel_path = rel_segment [ abs_path ] + REL_PATH = "#{REL_SEGMENT}(?:#{ABS_PATH})?" + # net_path = "//" authority [ abs_path ] + NET_PATH = "//#{AUTHORITY}(?:#{ABS_PATH})?" + + # hier_part = ( net_path | abs_path ) [ "?" query ] + HIER_PART = "(?:#{NET_PATH}|#{ABS_PATH})(?:\\?(?:#{QUERY}))?" + # opaque_part = uric_no_slash *uric + OPAQUE_PART = "#{URIC_NO_SLASH}#{URIC}*" + + # absoluteURI = scheme ":" ( hier_part | opaque_part ) + ABS_URI = "#{SCHEME}:(?:#{HIER_PART}|#{OPAQUE_PART})" + # relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ] + REL_URI = "(?:#{NET_PATH}|#{ABS_PATH}|#{REL_PATH})(?:\\?#{QUERY})?" + + # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] + URI_REF = "(?:#{ABS_URI}|#{REL_URI})?(?:##{FRAGMENT})?" + + # XXX: + X_ABS_URI = " + (#{PATTERN::SCHEME}): (?# 1: scheme) + (?: + (#{PATTERN::OPAQUE_PART}) (?# 2: opaque) + | + (?:(?: + //(?: + (?:(?:(#{PATTERN::USERINFO})@)? (?# 3: userinfo) + (?:(#{PATTERN::HOST})(?::(\\d*))?))?(?# 4: host, 5: port) + | + (#{PATTERN::REG_NAME}) (?# 6: registry) + ) + | + (?!//)) (?# XXX: '//' is the mark for hostport) + (#{PATTERN::ABS_PATH})? (?# 7: path) + )(?:\\?(#{PATTERN::QUERY}))? (?# 8: query) + ) + (?:\\#(#{PATTERN::FRAGMENT}))? (?# 9: fragment) + " + X_REL_URI = " + (?: + (?: + // + (?: + (?:(#{PATTERN::USERINFO})@)? (?# 1: userinfo) + (#{PATTERN::HOST})?(?::(\\d*))? (?# 2: host, 3: port) + | + (#{PATTERN::REG_NAME}) (?# 4: registry) + ) + ) + | + (#{PATTERN::REL_SEGMENT}) (?# 5: rel_segment) + )? + (#{PATTERN::ABS_PATH})? (?# 6: abs_path) + (?:\\?(#{PATTERN::QUERY}))? (?# 7: query) + (?:\\#(#{PATTERN::FRAGMENT}))? (?# 8: fragment) + " + # :startdoc: + end # PATTERN + + # :stopdoc: + + # for URI::split + ABS_URI = Regexp.new('^' + PATTERN::X_ABS_URI + '$', #' + Regexp::EXTENDED, 'N').freeze + REL_URI = Regexp.new('^' + PATTERN::X_REL_URI + '$', #' + Regexp::EXTENDED, 'N').freeze + + # for URI::extract + URI_REF = Regexp.new(PATTERN::URI_REF, false, 'N').freeze + ABS_URI_REF = Regexp.new(PATTERN::X_ABS_URI, Regexp::EXTENDED, 'N').freeze + REL_URI_REF = Regexp.new(PATTERN::X_REL_URI, Regexp::EXTENDED, 'N').freeze + + # for URI::escape/unescape + ESCAPED = Regexp.new(PATTERN::ESCAPED, false, 'N').freeze + UNSAFE = Regexp.new("[^#{PATTERN::UNRESERVED}#{PATTERN::RESERVED}]", + false, 'N').freeze + + # for Generic#initialize + SCHEME = Regexp.new("^#{PATTERN::SCHEME}$", false, 'N').freeze #" + USERINFO = Regexp.new("^#{PATTERN::USERINFO}$", false, 'N').freeze #" + HOST = Regexp.new("^#{PATTERN::HOST}$", false, 'N').freeze #" + PORT = Regexp.new("^#{PATTERN::PORT}$", false, 'N').freeze #" + OPAQUE = Regexp.new("^#{PATTERN::OPAQUE_PART}$", false, 'N').freeze #" + REGISTRY = Regexp.new("^#{PATTERN::REG_NAME}$", false, 'N').freeze #" + ABS_PATH = Regexp.new("^#{PATTERN::ABS_PATH}$", false, 'N').freeze #" + REL_PATH = Regexp.new("^#{PATTERN::REL_PATH}$", false, 'N').freeze #" + QUERY = Regexp.new("^#{PATTERN::QUERY}$", false, 'N').freeze #" + FRAGMENT = Regexp.new("^#{PATTERN::FRAGMENT}$", false, 'N').freeze #" + # :startdoc: + end # REGEXP + + module Util # :nodoc: + def make_components_hash(klass, array_hash) + tmp = {} + if array_hash.kind_of?(Array) && + array_hash.size == klass.component.size - 1 + klass.component[1..-1].each_index do |i| + begin + tmp[klass.component[i + 1]] = array_hash[i].clone + rescue TypeError + tmp[klass.component[i + 1]] = array_hash[i] + end + end + + elsif array_hash.kind_of?(Hash) + array_hash.each do |key, value| + begin + tmp[key] = value.clone + rescue TypeError + tmp[key] = value + end + end + else + raise ArgumentError, + "expected Array of or Hash of components of #{klass.to_s} (#{klass.component[1..-1].join(', ')})" + end + tmp[:scheme] = klass.to_s.sub(/\A.*::/, '').downcase + + return tmp + end + module_function :make_components_hash + end + + module Escape + include REGEXP + + # + # == Synopsis + # + # URI.escape(str [, unsafe]) + # + # == Args + # + # +str+:: + # String to replaces in. + # +unsafe+:: + # Regexp that matches all symbols that must be replaced with codes. + # By default uses REGEXP::UNSAFE. + # When this argument is a String, it represents a character set. + # + # == Description + # + # Escapes the string, replacing all unsafe characters with codes. + # + # == Usage + # + # require 'uri' + # + # enc_uri = URI.escape("http://example.com/?a=\11\15") + # p enc_uri + # # => "http://example.com/?a=%09%0D" + # + # p URI.unescape(enc_uri) + # # => "http://example.com/?a=\t\r" + # + # p URI.escape("@?@!", "!?") + # # => "@%3F@%21" + # + def escape(str, unsafe = UNSAFE) + unless unsafe.kind_of?(Regexp) + # perhaps unsafe is String object + unsafe = Regexp.new("[#{Regexp.quote(unsafe)}]", false, 'N') + end + str.gsub(unsafe) do |us| + tmp = '' + us.each_byte do |uc| + tmp << sprintf('%%%02X', uc) + end + tmp + end + end + alias encode escape + # + # == Synopsis + # + # URI.unescape(str) + # + # == Args + # + # +str+:: + # Unescapes the string. + # + # == Usage + # + # require 'uri' + # + # enc_uri = URI.escape("http://example.com/?a=\11\15") + # p enc_uri + # # => "http://example.com/?a=%09%0D" + # + # p URI.unescape(enc_uri) + # # => "http://example.com/?a=\t\r" + # + def unescape(str) + str.gsub(ESCAPED) do + $&[1,2].hex.chr + end + end + alias decode unescape + end + + include REGEXP + extend Escape + + @@schemes = {} + + # + # Base class for all URI exceptions. + # + class Error < StandardError; end + # + # Not a URI. + # + class InvalidURIError < Error; end + # + # Not a URI component. + # + class InvalidComponentError < Error; end + # + # URI is valid, bad usage is not. + # + class BadURIError < Error; end + + # + # == Synopsis + # + # URI::split(uri) + # + # == Args + # + # +uri+:: + # String with URI. + # + # == Description + # + # Splits the string on following parts and returns array with result: + # + # * Scheme + # * Userinfo + # * Host + # * Port + # * Registry + # * Path + # * Opaque + # * Query + # * Fragment + # + # == Usage + # + # require 'uri' + # + # p URI.split("http://www.ruby-lang.org/") + # # => ["http", nil, "www.ruby-lang.org", nil, nil, "/", nil, nil, nil] + # + def self.split(uri) + case uri + when '' + # null uri + + when ABS_URI + scheme, opaque, userinfo, host, port, + registry, path, query, fragment = $~[1..-1] + + # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] + + # absoluteURI = scheme ":" ( hier_part | opaque_part ) + # hier_part = ( net_path | abs_path ) [ "?" query ] + # opaque_part = uric_no_slash *uric + + # abs_path = "/" path_segments + # net_path = "//" authority [ abs_path ] + + # authority = server | reg_name + # server = [ [ userinfo "@" ] hostport ] + + if !scheme + raise InvalidURIError, + "bad URI(absolute but no scheme): #{uri}" + end + if !opaque && (!path && (!host && !registry)) + raise InvalidURIError, + "bad URI(absolute but no path): #{uri}" + end + + when REL_URI + scheme = nil + opaque = nil + + userinfo, host, port, registry, + rel_segment, abs_path, query, fragment = $~[1..-1] + if rel_segment && abs_path + path = rel_segment + abs_path + elsif rel_segment + path = rel_segment + elsif abs_path + path = abs_path + end + + # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] + + # relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ] + + # net_path = "//" authority [ abs_path ] + # abs_path = "/" path_segments + # rel_path = rel_segment [ abs_path ] + + # authority = server | reg_name + # server = [ [ userinfo "@" ] hostport ] + + else + raise InvalidURIError, "bad URI(is not URI?): #{uri}" + end + + path = '' if !path && !opaque # (see RFC2396 Section 5.2) + ret = [ + scheme, + userinfo, host, port, # X + registry, # X + path, # Y + opaque, # Y + query, + fragment + ] + return ret + end + + # + # == Synopsis + # + # URI::parse(uri_str) + # + # == Args + # + # +uri_str+:: + # String with URI. + # + # == Description + # + # Creates one of the URI's subclasses instance from the string. + # + # == Raises + # + # URI::InvalidURIError + # Raised if URI given is not a correct one. + # + # == Usage + # + # require 'uri' + # + # uri = URI.parse("http://www.ruby-lang.org/") + # p uri + # # => # + # p uri.scheme + # # => "http" + # p uri.host + # # => "www.ruby-lang.org" + # + def self.parse(uri) + scheme, userinfo, host, port, + registry, path, opaque, query, fragment = self.split(uri) + + if scheme && @@schemes.include?(scheme.upcase) + @@schemes[scheme.upcase].new(scheme, userinfo, host, port, + registry, path, opaque, query, + fragment) + else + Generic.new(scheme, userinfo, host, port, + registry, path, opaque, query, + fragment) + end + end + + # + # == Synopsis + # + # URI::join(str[, str, ...]) + # + # == Args + # + # +str+:: + # String(s) to work with + # + # == Description + # + # Joins URIs. + # + # == Usage + # + # require 'uri' + # + # p URI.join("http://localhost/","main.rbx") + # # => # + # + def self.join(*str) + u = self.parse(str[0]) + str[1 .. -1].each do |x| + u = u.merge(x) + end + u + end + + # + # == Synopsis + # + # URI::extract(str[, schemes][,&blk]) + # + # == Args + # + # +str+:: + # String to extract URIs from. + # +schemes+:: + # Limit URI matching to a specific schemes. + # + # == Description + # + # Extracts URIs from a string. If block given, iterates through all matched URIs. + # Returns nil if block given or array with matches. + # + # == Usage + # + # require "uri" + # + # URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.") + # # => ["http://foo.example.com/bla", "mailto:test@example.com"] + # + def self.extract(str, schemes = nil, &block) + if block_given? + str.scan(regexp(schemes)) { yield $& } + nil + else + result = [] + str.scan(regexp(schemes)) { result.push $& } + result + end + end + + # + # == Synopsis + # + # URI::regexp([match_schemes]) + # + # == Args + # + # +match_schemes+:: + # Array of schemes. If given, resulting regexp matches to URIs + # whose scheme is one of the match_schemes. + # + # == Description + # Returns a Regexp object which matches to URI-like strings. + # The Regexp object returned by this method includes arbitrary + # number of capture group (parentheses). Never rely on it's number. + # + # == Usage + # + # require 'uri' + # + # # extract first URI from html_string + # html_string.slice(URI.regexp) + # + # # remove ftp URIs + # html_string.sub(URI.regexp(['ftp']) + # + # # You should not rely on the number of parentheses + # html_string.scan(URI.regexp) do |*matches| + # p $& + # end + # + def self.regexp(schemes = nil) + unless schemes + ABS_URI_REF + else + /(?=#{Regexp.union(*schemes)}:)#{PATTERN::X_ABS_URI}/xn + end + end + +end + +module Kernel + # alias for URI.parse. + # + # This method is introduced at 1.8.2. + def URI(uri_str) # :doc: + URI.parse(uri_str) + end + module_function :URI +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/ftp.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/ftp.rb new file mode 100644 index 0000000000..4a6ecb8f8c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/ftp.rb @@ -0,0 +1,148 @@ +# +# = uri/ftp.rb +# +# Author:: Akira Yamada +# License:: You can redistribute it and/or modify it under the same term as Ruby. +# Revision:: $Id: ftp.rb 11757 2007-02-15 11:36:28Z knu $ +# + +require 'uri/generic' + +module URI + + # + # RFC1738 section 3.2. + # + class FTP < Generic + DEFAULT_PORT = 21 + + COMPONENT = [ + :scheme, + :userinfo, :host, :port, + :path, :typecode + ].freeze + # + # Typecode is, "a", "i" or "d". + # As for "a" the text, as for "i" binary, + # as for "d" the directory is displayed. + # "A" with the text, as for "i" being binary, + # is because the respective data type was called ASCII and + # IMAGE with the protocol of FTP. + # + TYPECODE = ['a', 'i', 'd'].freeze + TYPECODE_PREFIX = ';type='.freeze + + def self.new2(user, password, host, port, path, + typecode = nil, arg_check = true) + typecode = nil if typecode.size == 0 + if typecode && !TYPECODE.include?(typecode) + raise ArgumentError, + "bad typecode is specified: #{typecode}" + end + + # do escape + + self.new('ftp', + [user, password], + host, port, nil, + typecode ? path + TYPECODE_PREFIX + typecode : path, + nil, nil, nil, arg_check) + end + + # + # == Description + # + # Creates a new URI::FTP object from components of URI::FTP with + # check. It is scheme, userinfo, host, port, path and typecode. It + # provided by an Array or a Hash. typecode is "a", "i" or "d". + # + def self.build(args) + tmp = Util::make_components_hash(self, args) + + if tmp[:typecode] + if tmp[:typecode].size == 1 + tmp[:typecode] = TYPECODE_PREFIX + tmp[:typecode] + end + tmp[:path] << tmp[:typecode] + end + + return super(tmp) + end + + # + # == Description + # + # Create a new URI::FTP object from ``generic'' components with no + # check. + # + # == Usage + # + # require 'uri' + # p ftp = URI.parse("ftp://ftp.ruby-lang.org/pub/ruby/;type=d") + # # => # + # p ftp.typecode + # # => "d" + # + def initialize(*arg) + super(*arg) + @typecode = nil + tmp = @path.index(TYPECODE_PREFIX) + if tmp + typecode = @path[tmp + TYPECODE_PREFIX.size..-1] + self.set_path(@path[0..tmp - 1]) + + if arg[-1] + self.typecode = typecode + else + self.set_typecode(typecode) + end + end + end + attr_reader :typecode + + def check_typecode(v) + if TYPECODE.include?(v) + return true + else + raise InvalidComponentError, + "bad typecode(expected #{TYPECODE.join(', ')}): #{v}" + end + end + private :check_typecode + + def set_typecode(v) + @typecode = v + end + protected :set_typecode + + def typecode=(typecode) + check_typecode(typecode) + set_typecode(typecode) + typecode + end + + def merge(oth) # :nodoc: + tmp = super(oth) + if self != tmp + tmp.set_typecode(oth.typecode) + end + + return tmp + end + + def to_s + save_path = nil + if @typecode + save_path = @path + @path = @path + TYPECODE_PREFIX + @typecode + end + str = super + if @typecode + @path = save_path + end + + return str + end + end + @@schemes['FTP'] = FTP +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/generic.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/generic.rb new file mode 100644 index 0000000000..33e33a6eb7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/generic.rb @@ -0,0 +1,1121 @@ +# +# = uri/generic.rb +# +# Author:: Akira Yamada +# License:: You can redistribute it and/or modify it under the same term as Ruby. +# Revision:: $Id: generic.rb 11751 2007-02-15 07:45:04Z knu $ +# + +require 'uri/common' + +module URI + + # + # Base class for all URI classes. + # Implements generic URI syntax as per RFC 2396. + # + class Generic + include URI + include REGEXP + + DEFAULT_PORT = nil + + # + # Returns default port + # + def self.default_port + self::DEFAULT_PORT + end + + def default_port + self.class.default_port + end + + COMPONENT = [ + :scheme, + :userinfo, :host, :port, :registry, + :path, :opaque, + :query, + :fragment + ].freeze + + # + # Components of the URI in the order. + # + def self.component + self::COMPONENT + end + + USE_REGISTRY = false + + # + # DOC: FIXME! + # + def self.use_registry + self::USE_REGISTRY + end + + # + # == Synopsis + # + # See #new + # + # == Description + # + # At first, tries to create a new URI::Generic instance using + # URI::Generic::build. But, if exception URI::InvalidComponentError is raised, + # then it URI::Escape.escape all URI components and tries again. + # + # + def self.build2(args) + begin + return self.build(args) + rescue InvalidComponentError + if args.kind_of?(Array) + return self.build(args.collect{|x| + if x + URI.escape(x) + else + x + end + }) + elsif args.kind_of?(Hash) + tmp = {} + args.each do |key, value| + tmp[key] = if value + URI.escape(value) + else + value + end + end + return self.build(tmp) + end + end + end + + # + # == Synopsis + # + # See #new + # + # == Description + # + # Creates a new URI::Generic instance from components of URI::Generic + # with check. Components are: scheme, userinfo, host, port, registry, path, + # opaque, query and fragment. You can provide arguments either by an Array or a Hash. + # See #new for hash keys to use or for order of array items. + # + def self.build(args) + if args.kind_of?(Array) && + args.size == ::URI::Generic::COMPONENT.size + tmp = args + elsif args.kind_of?(Hash) + tmp = ::URI::Generic::COMPONENT.collect do |c| + if args.include?(c) + args[c] + else + nil + end + end + else + raise ArgumentError, + "expected Array of or Hash of components of #{self.class} (#{self.class.component.join(', ')})" + end + + tmp << true + return self.new(*tmp) + end + # + # == Args + # + # +scheme+:: + # Protocol scheme, i.e. 'http','ftp','mailto' and so on. + # +userinfo+:: + # User name and password, i.e. 'sdmitry:bla' + # +host+:: + # Server host name + # +port+:: + # Server port + # +registry+:: + # DOC: FIXME! + # +path+:: + # Path on server + # +opaque+:: + # DOC: FIXME! + # +query+:: + # Query data + # +fragment+:: + # A part of URI after '#' sign + # +arg_check+:: + # Check arguments [false by default] + # + # == Description + # + # Creates a new URI::Generic instance from ``generic'' components without check. + # + def initialize(scheme, + userinfo, host, port, registry, + path, opaque, + query, + fragment, + arg_check = false) + @scheme = nil + @user = nil + @password = nil + @host = nil + @port = nil + @path = nil + @query = nil + @opaque = nil + @registry = nil + @fragment = nil + + if arg_check + self.scheme = scheme + self.userinfo = userinfo + self.host = host + self.port = port + self.path = path + self.query = query + self.opaque = opaque + self.registry = registry + self.fragment = fragment + else + self.set_scheme(scheme) + self.set_userinfo(userinfo) + self.set_host(host) + self.set_port(port) + self.set_path(path) + self.set_query(query) + self.set_opaque(opaque) + self.set_registry(registry) + self.set_fragment(fragment) + end + if @registry && !self.class.use_registry + raise InvalidURIError, + "the scheme #{@scheme} does not accept registry part: #{@registry} (or bad hostname?)" + end + + @scheme.freeze if @scheme + self.set_path('') if !@path && !@opaque # (see RFC2396 Section 5.2) + self.set_port(self.default_port) if self.default_port && !@port + end + attr_reader :scheme + attr_reader :host + attr_reader :port + attr_reader :registry + attr_reader :path + attr_reader :query + attr_reader :opaque + attr_reader :fragment + + # replace self by other URI object + def replace!(oth) + if self.class != oth.class + raise ArgumentError, "expected #{self.class} object" + end + + component.each do |c| + self.__send__("#{c}=", oth.__send__(c)) + end + end + private :replace! + + def component + self.class.component + end + + def check_scheme(v) + if v && SCHEME !~ v + raise InvalidComponentError, + "bad component(expected scheme component): #{v}" + end + + return true + end + private :check_scheme + + def set_scheme(v) + @scheme = v + end + protected :set_scheme + + def scheme=(v) + check_scheme(v) + set_scheme(v) + v + end + + def check_userinfo(user, password = nil) + if !password + user, password = split_userinfo(user) + end + check_user(user) + check_password(password, user) + + return true + end + private :check_userinfo + + def check_user(v) + if @registry || @opaque + raise InvalidURIError, + "can not set user with registry or opaque" + end + + return v unless v + + if USERINFO !~ v + raise InvalidComponentError, + "bad component(expected userinfo component or user component): #{v}" + end + + return true + end + private :check_user + + def check_password(v, user = @user) + if @registry || @opaque + raise InvalidURIError, + "can not set password with registry or opaque" + end + return v unless v + + if !user + raise InvalidURIError, + "password component depends user component" + end + + if USERINFO !~ v + raise InvalidComponentError, + "bad component(expected user component): #{v}" + end + + return true + end + private :check_password + + # + # Sets userinfo, argument is string like 'name:pass' + # + def userinfo=(userinfo) + if userinfo.nil? + return nil + end + check_userinfo(*userinfo) + set_userinfo(*userinfo) + # returns userinfo + end + + def user=(user) + check_user(user) + set_user(user) + # returns user + end + + def password=(password) + check_password(password) + set_password(password) + # returns password + end + + def set_userinfo(user, password = nil) + unless password + user, password = split_userinfo(user) + end + @user = user + @password = password if password + + [@user, @password] + end + protected :set_userinfo + + def set_user(v) + set_userinfo(v, @password) + v + end + protected :set_user + + def set_password(v) + @password = v + # returns v + end + protected :set_password + + def split_userinfo(ui) + return nil, nil unless ui + user, password = ui.split(/:/, 2) + + return user, password + end + private :split_userinfo + + def escape_userpass(v) + v = URI.escape(v, /[@:\/]/o) # RFC 1738 section 3.1 #/ + end + private :escape_userpass + + def userinfo + if @user.nil? + nil + elsif @password.nil? + @user + else + @user + ':' + @password + end + end + + def user + @user + end + + def password + @password + end + + def check_host(v) + return v unless v + + if @registry || @opaque + raise InvalidURIError, + "can not set host with registry or opaque" + elsif HOST !~ v + raise InvalidComponentError, + "bad component(expected host component): #{v}" + end + + return true + end + private :check_host + + def set_host(v) + @host = v + end + protected :set_host + + def host=(v) + check_host(v) + set_host(v) + v + end + + def check_port(v) + return v unless v + + if @registry || @opaque + raise InvalidURIError, + "can not set port with registry or opaque" + elsif !v.kind_of?(Fixnum) && PORT !~ v + raise InvalidComponentError, + "bad component(expected port component): #{v}" + end + + return true + end + private :check_port + + def set_port(v) + unless !v || v.kind_of?(Fixnum) + if v.empty? + v = nil + else + v = v.to_i + end + end + @port = v + end + protected :set_port + + def port=(v) + check_port(v) + set_port(v) + port + end + + def check_registry(v) + return v unless v + + # raise if both server and registry are not nil, because: + # authority = server | reg_name + # server = [ [ userinfo "@" ] hostport ] + if @host || @port || @user # userinfo = @user + ':' + @password + raise InvalidURIError, + "can not set registry with host, port, or userinfo" + elsif v && REGISTRY !~ v + raise InvalidComponentError, + "bad component(expected registry component): #{v}" + end + + return true + end + private :check_registry + + def set_registry(v) + @registry = v + end + protected :set_registry + + def registry=(v) + check_registry(v) + set_registry(v) + v + end + + def check_path(v) + # raise if both hier and opaque are not nil, because: + # absoluteURI = scheme ":" ( hier_part | opaque_part ) + # hier_part = ( net_path | abs_path ) [ "?" query ] + if v && @opaque + raise InvalidURIError, + "path conflicts with opaque" + end + + if @scheme + if v && v != '' && ABS_PATH !~ v + raise InvalidComponentError, + "bad component(expected absolute path component): #{v}" + end + else + if v && v != '' && ABS_PATH !~ v && REL_PATH !~ v + raise InvalidComponentError, + "bad component(expected relative path component): #{v}" + end + end + + return true + end + private :check_path + + def set_path(v) + @path = v + end + protected :set_path + + def path=(v) + check_path(v) + set_path(v) + v + end + + def check_query(v) + return v unless v + + # raise if both hier and opaque are not nil, because: + # absoluteURI = scheme ":" ( hier_part | opaque_part ) + # hier_part = ( net_path | abs_path ) [ "?" query ] + if @opaque + raise InvalidURIError, + "query conflicts with opaque" + end + + if v && v != '' && QUERY !~ v + raise InvalidComponentError, + "bad component(expected query component): #{v}" + end + + return true + end + private :check_query + + def set_query(v) + @query = v + end + protected :set_query + + def query=(v) + check_query(v) + set_query(v) + v + end + + def check_opaque(v) + return v unless v + + # raise if both hier and opaque are not nil, because: + # absoluteURI = scheme ":" ( hier_part | opaque_part ) + # hier_part = ( net_path | abs_path ) [ "?" query ] + if @host || @port || @user || @path # userinfo = @user + ':' + @password + raise InvalidURIError, + "can not set opaque with host, port, userinfo or path" + elsif v && OPAQUE !~ v + raise InvalidComponentError, + "bad component(expected opaque component): #{v}" + end + + return true + end + private :check_opaque + + def set_opaque(v) + @opaque = v + end + protected :set_opaque + + def opaque=(v) + check_opaque(v) + set_opaque(v) + v + end + + def check_fragment(v) + return v unless v + + if v && v != '' && FRAGMENT !~ v + raise InvalidComponentError, + "bad component(expected fragment component): #{v}" + end + + return true + end + private :check_fragment + + def set_fragment(v) + @fragment = v + end + protected :set_fragment + + def fragment=(v) + check_fragment(v) + set_fragment(v) + v + end + + # + # Checks if URI has a path + # + def hierarchical? + if @path + true + else + false + end + end + + # + # Checks if URI is an absolute one + # + def absolute? + if @scheme + true + else + false + end + end + alias absolute absolute? + + # + # Checks if URI is relative + # + def relative? + !absolute? + end + + def split_path(path) + path.split(%r{/+}, -1) + end + private :split_path + + def merge_path(base, rel) + # RFC2396, Section 5.2, 5) + if rel[0] == ?/ #/ + # RFC2396, Section 5.2, 5) + return rel + + else + # RFC2396, Section 5.2, 6) + base_path = split_path(base) + rel_path = split_path(rel) + + # RFC2396, Section 5.2, 6), a) + base_path << '' if base_path.last == '..' + while i = base_path.index('..') + base_path.slice!(i - 1, 2) + end + if base_path.empty? + base_path = [''] # keep '/' for root directory + else + base_path.pop + end + + # RFC2396, Section 5.2, 6), c) + # RFC2396, Section 5.2, 6), d) + rel_path.push('') if rel_path.last == '.' || rel_path.last == '..' + rel_path.delete('.') + + # RFC2396, Section 5.2, 6), e) + tmp = [] + rel_path.each do |x| + if x == '..' && + !(tmp.empty? || tmp.last == '..') + tmp.pop + else + tmp << x + end + end + + add_trailer_slash = true + while x = tmp.shift + if x == '..' && base_path.size > 1 + # RFC2396, Section 4 + # a .. or . in an absolute path has no special meaning + base_path.pop + else + # if x == '..' + # valid absolute (but abnormal) path "/../..." + # else + # valid absolute path + # end + base_path << x + tmp.each {|t| base_path << t} + add_trailer_slash = false + break + end + end + base_path.push('') if add_trailer_slash + + return base_path.join('/') + end + end + private :merge_path + + # + # == Args + # + # +oth+:: + # URI or String + # + # == Description + # + # Destructive form of #merge + # + # == Usage + # + # require 'uri' + # + # uri = URI.parse("http://my.example.com") + # uri.merge!("/main.rbx?page=1") + # p uri + # # => # + # + def merge!(oth) + t = merge(oth) + if self == t + nil + else + replace!(t) + self + end + end + + # + # == Args + # + # +oth+:: + # URI or String + # + # == Description + # + # Merges two URI's. + # + # == Usage + # + # require 'uri' + # + # uri = URI.parse("http://my.example.com") + # p uri.merge("/main.rbx?page=1") + # # => # + # + def merge(oth) + begin + base, rel = merge0(oth) + rescue + raise $!.class, $!.message + end + + if base == rel + return base + end + + authority = rel.userinfo || rel.host || rel.port + + # RFC2396, Section 5.2, 2) + if (rel.path.nil? || rel.path.empty?) && !authority && !rel.query + base.set_fragment(rel.fragment) if rel.fragment + return base + end + + base.set_query(nil) + base.set_fragment(nil) + + # RFC2396, Section 5.2, 4) + if !authority + base.set_path(merge_path(base.path, rel.path)) if base.path && rel.path + else + # RFC2396, Section 5.2, 4) + base.set_path(rel.path) if rel.path + end + + # RFC2396, Section 5.2, 7) + base.set_userinfo(rel.userinfo) if rel.userinfo + base.set_host(rel.host) if rel.host + base.set_port(rel.port) if rel.port + base.set_query(rel.query) if rel.query + base.set_fragment(rel.fragment) if rel.fragment + + return base + end # merge + alias + merge + + # return base and rel. + # you can modify `base', but can not `rel'. + def merge0(oth) + case oth + when Generic + when String + oth = URI.parse(oth) + else + raise ArgumentError, + "bad argument(expected URI object or URI string)" + end + + if self.relative? && oth.relative? + raise BadURIError, + "both URI are relative" + end + + if self.absolute? && oth.absolute? + #raise BadURIError, + # "both URI are absolute" + # hmm... should return oth for usability? + return oth, oth + end + + if self.absolute? + return self.dup, oth + else + return oth, oth + end + end + private :merge0 + + def route_from_path(src, dst) + # RFC2396, Section 4.2 + return '' if src == dst + + src_path = split_path(src) + dst_path = split_path(dst) + + # hmm... dst has abnormal absolute path, + # like "/./", "/../", "/x/../", ... + if dst_path.include?('..') || + dst_path.include?('.') + return dst.dup + end + + src_path.pop + + # discard same parts + while dst_path.first == src_path.first + break if dst_path.empty? + + src_path.shift + dst_path.shift + end + + tmp = dst_path.join('/') + + # calculate + if src_path.empty? + if tmp.empty? + return './' + elsif dst_path.first.include?(':') # (see RFC2396 Section 5) + return './' + tmp + else + return tmp + end + end + + return '../' * src_path.size + tmp + end + private :route_from_path + + def route_from0(oth) + case oth + when Generic + when String + oth = URI.parse(oth) + else + raise ArgumentError, + "bad argument(expected URI object or URI string)" + end + + if self.relative? + raise BadURIError, + "relative URI: #{self}" + end + if oth.relative? + raise BadURIError, + "relative URI: #{oth}" + end + + if self.scheme != oth.scheme + return self, self.dup + end + rel = URI::Generic.new(nil, # it is relative URI + self.userinfo, self.host, self.port, + self.registry, self.path, self.opaque, + self.query, self.fragment) + + if rel.userinfo != oth.userinfo || + rel.host.to_s.downcase != oth.host.to_s.downcase || + rel.port != oth.port + if self.userinfo.nil? && self.host.nil? + return self, self.dup + end + rel.set_port(nil) if rel.port == oth.default_port + return rel, rel + end + rel.set_userinfo(nil) + rel.set_host(nil) + rel.set_port(nil) + + if rel.path && rel.path == oth.path + rel.set_path('') + rel.set_query(nil) if rel.query == oth.query + return rel, rel + elsif rel.opaque && rel.opaque == oth.opaque + rel.set_opaque('') + rel.set_query(nil) if rel.query == oth.query + return rel, rel + end + + # you can modify `rel', but can not `oth'. + return oth, rel + end + private :route_from0 + # + # == Args + # + # +oth+:: + # URI or String + # + # == Description + # + # Calculates relative path from oth to self + # + # == Usage + # + # require 'uri' + # + # uri = URI.parse('http://my.example.com/main.rbx?page=1') + # p uri.route_from('http://my.example.com') + # #=> # + # + def route_from(oth) + # you can modify `rel', but can not `oth'. + begin + oth, rel = route_from0(oth) + rescue + raise $!.class, $!.message + end + if oth == rel + return rel + end + + rel.set_path(route_from_path(oth.path, self.path)) + if rel.path == './' && self.query + # "./?foo" -> "?foo" + rel.set_path('') + end + + return rel + end + + alias - route_from + + # + # == Args + # + # +oth+:: + # URI or String + # + # == Description + # + # Calculates relative path to oth from self + # + # == Usage + # + # require 'uri' + # + # uri = URI.parse('http://my.example.com') + # p uri.route_to('http://my.example.com/main.rbx?page=1') + # #=> # + # + def route_to(oth) + case oth + when Generic + when String + oth = URI.parse(oth) + else + raise ArgumentError, + "bad argument(expected URI object or URI string)" + end + + oth.route_from(self) + end + + # + # Returns normalized URI + # + def normalize + uri = dup + uri.normalize! + uri + end + + # + # Destructive version of #normalize + # + def normalize! + if path && path == '' + set_path('/') + end + if host && host != host.downcase + set_host(self.host.downcase) + end + end + + def path_query + str = @path + if @query + str += '?' + @query + end + str + end + private :path_query + + # + # Constructs String from URI + # + def to_s + str = '' + if @scheme + str << @scheme + str << ':' + end + + if @opaque + str << @opaque + + else + if @registry + str << @registry + else + if @host + str << '//' + end + if self.userinfo + str << self.userinfo + str << '@' + end + if @host + str << @host + end + if @port && @port != self.default_port + str << ':' + str << @port.to_s + end + end + + str << path_query + end + + if @fragment + str << '#' + str << @fragment + end + + str + end + + # + # Compares to URI's + # + def ==(oth) + if self.class == oth.class + self.normalize.component_ary == oth.normalize.component_ary + else + false + end + end + + def hash + self.component_ary.hash + end + + def eql?(oth) + self.component_ary.eql?(oth.component_ary) + end + +=begin + +--- URI::Generic#===(oth) + +=end +# def ===(oth) +# raise NotImplementedError +# end + +=begin +=end + def component_ary + component.collect do |x| + self.send(x) + end + end + protected :component_ary + + # == Args + # + # +components+:: + # Multiple Symbol arguments defined in URI::HTTP + # + # == Description + # + # Selects specified components from URI + # + # == Usage + # + # require 'uri' + # + # uri = URI.parse('http://myuser:mypass@my.example.com/test.rbx') + # p uri.select(:userinfo, :host, :path) + # # => ["myuser:mypass", "my.example.com", "/test.rbx"] + # + def select(*components) + components.collect do |c| + if component.include?(c) + self.send(c) + else + raise ArgumentError, + "expected of components of #{self.class} (#{self.class.component.join(', ')})" + end + end + end + + def inspect + sprintf("#<%s:%#0x URL:%s>", self.class.to_s, self.object_id, self.to_s) + end + + def coerce(oth) + case oth + when String + oth = URI.parse(oth) + else + super + end + + return oth, self + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/http.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/http.rb new file mode 100644 index 0000000000..dd2e792a78 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/http.rb @@ -0,0 +1,100 @@ +# +# = uri/http.rb +# +# Author:: Akira Yamada +# License:: You can redistribute it and/or modify it under the same term as Ruby. +# Revision:: $Id: http.rb 11747 2007-02-15 02:41:45Z knu $ +# + +require 'uri/generic' + +module URI + + # + # The syntax of HTTP URIs is defined in RFC1738 section 3.3. + # + # Note that the Ruby URI library allows HTTP URLs containing usernames and + # passwords. This is not legal as per the RFC, but used to be + # supported in Internet Explorer 5 and 6, before the MS04-004 security + # update. See . + # + class HTTP < Generic + DEFAULT_PORT = 80 + + COMPONENT = [ + :scheme, + :userinfo, :host, :port, + :path, + :query, + :fragment + ].freeze + + # + # == Description + # + # Create a new URI::HTTP object from components, with syntax checking. + # + # The components accepted are userinfo, host, port, path, query and + # fragment. + # + # The components should be provided either as an Array, or as a Hash + # with keys formed by preceding the component names with a colon. + # + # If an Array is used, the components must be passed in the order + # [userinfo, host, port, path, query, fragment]. + # + # Example: + # + # newuri = URI::HTTP.build({:host => 'www.example.com', + # :path> => '/foo/bar'}) + # + # newuri = URI::HTTP.build([nil, "www.example.com", nil, "/path", + # "query", 'fragment']) + # + # Currently, if passed userinfo components this method generates + # invalid HTTP URIs as per RFC 1738. + # + def self.build(args) + tmp = Util::make_components_hash(self, args) + return super(tmp) + end + + # + # == Description + # + # Create a new URI::HTTP object from generic URI components as per + # RFC 2396. No HTTP-specific syntax checking (as per RFC 1738) is + # performed. + # + # Arguments are +scheme+, +userinfo+, +host+, +port+, +registry+, +path+, + # +opaque+, +query+ and +fragment+, in that order. + # + # Example: + # + # uri = URI::HTTP.new(['http', nil, "www.example.com", nil, "/path", + # "query", 'fragment']) + # + def initialize(*arg) + super(*arg) + end + + # + # == Description + # + # Returns the full path for an HTTP request, as required by Net::HTTP::Get. + # + # If the URI contains a query, the full path is URI#path + '?' + URI#query. + # Otherwise, the path is simply URI#path. + # + def request_uri + r = path_query + if r[0] != ?/ + r = '/' + r + end + + r + end + end + + @@schemes['HTTP'] = HTTP +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/https.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/https.rb new file mode 100644 index 0000000000..5043ae6a37 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/https.rb @@ -0,0 +1,20 @@ +# +# = uri/https.rb +# +# Author:: Akira Yamada +# License:: You can redistribute it and/or modify it under the same term as Ruby. +# Revision:: $Id: https.rb 11747 2007-02-15 02:41:45Z knu $ +# + +require 'uri/http' + +module URI + + # The default port for HTTPS URIs is 443, and the scheme is 'https:' rather + # than 'http:'. Other than that, HTTPS URIs are identical to HTTP URIs; + # see URI::HTTP. + class HTTPS < HTTP + DEFAULT_PORT = 443 + end + @@schemes['HTTPS'] = HTTPS +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/ldap.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/ldap.rb new file mode 100644 index 0000000000..7c14ff8811 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/ldap.rb @@ -0,0 +1,190 @@ +# +# = uri/ldap.rb +# +# Author:: +# Takaaki Tateishi +# Akira Yamada +# License:: +# URI::LDAP is copyrighted free software by Takaaki Tateishi and Akira Yamada. +# You can redistribute it and/or modify it under the same term as Ruby. +# Revision:: $Id: ldap.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# + +require 'uri/generic' + +module URI + + # + # LDAP URI SCHEMA (described in RFC2255) + # ldap:///[?[?[?[?]]]] + # + class LDAP < Generic + + DEFAULT_PORT = 389 + + COMPONENT = [ + :scheme, + :host, :port, + :dn, + :attributes, + :scope, + :filter, + :extensions, + ].freeze + + SCOPE = [ + SCOPE_ONE = 'one', + SCOPE_SUB = 'sub', + SCOPE_BASE = 'base', + ].freeze + + def self.build(args) + tmp = Util::make_components_hash(self, args) + + if tmp[:dn] + tmp[:path] = tmp[:dn] + end + + query = [] + [:extensions, :filter, :scope, :attributes].collect do |x| + next if !tmp[x] && query.size == 0 + query.unshift(tmp[x]) + end + + tmp[:query] = query.join('?') + + return super(tmp) + end + + def initialize(*arg) + super(*arg) + + if @fragment + raise InvalidURIError, 'bad LDAP URL' + end + + parse_dn + parse_query + end + + def parse_dn + @dn = @path[1..-1] + end + private :parse_dn + + def parse_query + @attributes = nil + @scope = nil + @filter = nil + @extensions = nil + + if @query + attrs, scope, filter, extensions = @query.split('?') + + @attributes = attrs if attrs && attrs.size > 0 + @scope = scope if scope && scope.size > 0 + @filter = filter if filter && filter.size > 0 + @extensions = extensions if extensions && extensions.size > 0 + end + end + private :parse_query + + def build_path_query + @path = '/' + @dn + + query = [] + [@extensions, @filter, @scope, @attributes].each do |x| + next if !x && query.size == 0 + query.unshift(x) + end + @query = query.join('?') + end + private :build_path_query + + def dn + @dn + end + + def set_dn(val) + @dn = val + build_path_query + @dn + end + protected :set_dn + + def dn=(val) + set_dn(val) + val + end + + def attributes + @attributes + end + + def set_attributes(val) + @attributes = val + build_path_query + @attributes + end + protected :set_attributes + + def attributes=(val) + set_attributes(val) + val + end + + def scope + @scope + end + + def set_scope(val) + @scope = val + build_path_query + @scope + end + protected :set_scope + + def scope=(val) + set_scope(val) + val + end + + def filter + @filter + end + + def set_filter(val) + @filter = val + build_path_query + @filter + end + protected :set_filter + + def filter=(val) + set_filter(val) + val + end + + def extensions + @extensions + end + + def set_extensions(val) + @extensions = val + build_path_query + @extensions + end + protected :set_extensions + + def extensions=(val) + set_extensions(val) + val + end + + def hierarchical? + false + end + end + + @@schemes['LDAP'] = LDAP +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/mailto.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/mailto.rb new file mode 100644 index 0000000000..c8d3e3ea99 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/uri/mailto.rb @@ -0,0 +1,266 @@ +# +# = uri/mailto.rb +# +# Author:: Akira Yamada +# License:: You can redistribute it and/or modify it under the same term as Ruby. +# Revision:: $Id: mailto.rb 11747 2007-02-15 02:41:45Z knu $ +# + +require 'uri/generic' + +module URI + + # + # RFC2368, The mailto URL scheme + # + class MailTo < Generic + include REGEXP + + DEFAULT_PORT = nil + + COMPONENT = [ :scheme, :to, :headers ].freeze + + # :stopdoc: + # "hname" and "hvalue" are encodings of an RFC 822 header name and + # value, respectively. As with "to", all URL reserved characters must + # be encoded. + # + # "#mailbox" is as specified in RFC 822 [RFC822]. This means that it + # consists of zero or more comma-separated mail addresses, possibly + # including "phrase" and "comment" components. Note that all URL + # reserved characters in "to" must be encoded: in particular, + # parentheses, commas, and the percent sign ("%"), which commonly occur + # in the "mailbox" syntax. + # + # Within mailto URLs, the characters "?", "=", "&" are reserved. + + # hname = *urlc + # hvalue = *urlc + # header = hname "=" hvalue + HEADER_PATTERN = "(?:[^?=&]*=[^?=&]*)".freeze + HEADER_REGEXP = Regexp.new(HEADER_PATTERN, 'N').freeze + # headers = "?" header *( "&" header ) + # to = #mailbox + # mailtoURL = "mailto:" [ to ] [ headers ] + MAILBOX_PATTERN = "(?:#{PATTERN::ESCAPED}|[^(),%?=&])".freeze + MAILTO_REGEXP = Regexp.new(" # :nodoc: + \\A + (#{MAILBOX_PATTERN}*?) (?# 1: to) + (?: + \\? + (#{HEADER_PATTERN}(?:\\&#{HEADER_PATTERN})*) (?# 2: headers) + )? + (?: + \\# + (#{PATTERN::FRAGMENT}) (?# 3: fragment) + )? + \\z + ", Regexp::EXTENDED, 'N').freeze + # :startdoc: + + # + # == Description + # + # Creates a new URI::MailTo object from components, with syntax checking. + # + # Components can be provided as an Array or Hash. If an Array is used, + # the components must be supplied as [to, headers]. + # + # If a Hash is used, the keys are the component names preceded by colons. + # + # The headers can be supplied as a pre-encoded string, such as + # "subject=subscribe&cc=address", or as an Array of Arrays like + # [['subject', 'subscribe'], ['cc', 'address']] + # + # Examples: + # + # require 'uri' + # + # m1 = URI::MailTo.build(['joe@example.com', 'subject=Ruby']) + # puts m1.to_s -> mailto:joe@example.com?subject=Ruby + # + # m2 = URI::MailTo.build(['john@example.com', [['Subject', 'Ruby'], ['Cc', 'jack@example.com']]]) + # puts m2.to_s -> mailto:john@example.com?Subject=Ruby&Cc=jack@example.com + # + # m3 = URI::MailTo.build({:to => 'listman@example.com', :headers => [['subject', 'subscribe']]}) + # puts m3.to_s -> mailto:listman@example.com?subject=subscribe + # + def self.build(args) + tmp = Util::make_components_hash(self, args) + + if tmp[:to] + tmp[:opaque] = tmp[:to] + else + tmp[:opaque] = '' + end + + if tmp[:headers] + tmp[:opaque] << '?' + + if tmp[:headers].kind_of?(Array) + tmp[:opaque] << tmp[:headers].collect { |x| + if x.kind_of?(Array) + x[0] + '=' + x[1..-1].to_s + else + x.to_s + end + }.join('&') + + elsif tmp[:headers].kind_of?(Hash) + tmp[:opaque] << tmp[:headers].collect { |h,v| + h + '=' + v + }.join('&') + + else + tmp[:opaque] << tmp[:headers].to_s + end + end + + return super(tmp) + end + + # + # == Description + # + # Creates a new URI::MailTo object from generic URL components with + # no syntax checking. + # + # This method is usually called from URI::parse, which checks + # the validity of each component. + # + def initialize(*arg) + super(*arg) + + @to = nil + @headers = [] + + if MAILTO_REGEXP =~ @opaque + if arg[-1] + self.to = $1 + self.headers = $2 + else + set_to($1) + set_headers($2) + end + + else + raise InvalidComponentError, + "unrecognised opaque part for mailtoURL: #{@opaque}" + end + end + + # The primary e-mail address of the URL, as a String + attr_reader :to + + # E-mail headers set by the URL, as an Array of Arrays + attr_reader :headers + + def check_to(v) + return true unless v + return true if v.size == 0 + + if OPAQUE !~ v || /\A#{MAILBOX_PATTERN}*\z/o !~ v + raise InvalidComponentError, + "bad component(expected opaque component): #{v}" + end + + return true + end + private :check_to + + def set_to(v) + @to = v + end + protected :set_to + + def to=(v) + check_to(v) + set_to(v) + v + end + + def check_headers(v) + return true unless v + return true if v.size == 0 + + if OPAQUE !~ v || + /\A(#{HEADER_PATTERN}(?:\&#{HEADER_PATTERN})*)\z/o !~ v + raise InvalidComponentError, + "bad component(expected opaque component): #{v}" + end + + return true + end + private :check_headers + + def set_headers(v) + @headers = [] + if v + v.scan(HEADER_REGEXP) do |x| + @headers << x.split(/=/o, 2) + end + end + end + protected :set_headers + + def headers=(v) + check_headers(v) + set_headers(v) + v + end + + def to_s + @scheme + ':' + + if @to + @to + else + '' + end + + if @headers.size > 0 + '?' + @headers.collect{|x| x.join('=')}.join('&') + else + '' + end + + if @fragment + '#' + @fragment + else + '' + end + end + + # Returns the RFC822 e-mail text equivalent of the URL, as a String. + # + # Example: + # + # require 'uri' + # + # uri = URI.parse("mailto:ruby-list@ruby-lang.org?Subject=subscribe&cc=myaddr") + # uri.to_mailtext + # # => "To: ruby-list@ruby-lang.org\nSubject: subscribe\nCc: myaddr\n\n\n" + # + def to_mailtext + to = URI::unescape(@to) + head = '' + body = '' + @headers.each do |x| + case x[0] + when 'body' + body = URI::unescape(x[1]) + when 'to' + to << ', ' + URI::unescape(x[1]) + else + head << URI::unescape(x[0]).capitalize + ': ' + + URI::unescape(x[1]) + "\n" + end + end + + return "To: #{to} +#{head} +#{body} +" + end + alias to_rfc822text to_mailtext + end + + @@schemes['MAILTO'] = MailTo +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/weakref.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/weakref.rb new file mode 100644 index 0000000000..591819c948 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/weakref.rb @@ -0,0 +1,100 @@ +require "delegate" + +# WeakRef is a class to represent a reference to an object that is not seen by +# the tracing phase of the garbage collector. This allows the referenced +# object to be garbage collected as if nothing is referring to it. Because +# WeakRef delegates method calls to the referenced object, it may be used in +# place of that object, i.e. it is of the same duck type. +# +# Usage: +# +# foo = Object.new +# foo = Object.new +# p foo.to_s # original's class +# foo = WeakRef.new(foo) +# p foo.to_s # should be same class +# ObjectSpace.garbage_collect +# p foo.to_s # should raise exception (recycled) +class WeakRef [ref,...] + @@id_rev_map = {} # ref -> obj + @@final = lambda{|id| + __old_status = Thread.critical + Thread.critical = true + begin + rids = @@id_map[id] + if rids + for rid in rids + @@id_rev_map.delete(rid) + end + @@id_map.delete(id) + end + rid = @@id_rev_map[id] + if rid + @@id_rev_map.delete(id) + @@id_map[rid].delete(id) + @@id_map.delete(rid) if @@id_map[rid].empty? + end + ensure + Thread.critical = __old_status + end + } + + # Create a new WeakRef from +orig+. + def initialize(orig) + super + __setobj__(orig) + end + + # Return the object this WeakRef references. Raises RefError if the object + # has been garbage collected. The object returned is the object to which + # method calls are delegated (see Delegator). + def __getobj__ + unless @@id_rev_map[self.__id__] == @__id + raise RefError, "Illegal Reference - probably recycled", caller(2) + end + begin + ObjectSpace._id2ref(@__id) + rescue RangeError + raise RefError, "Illegal Reference - probably recycled", caller(2) + end + end + + def __setobj__(obj) + @__id = obj.__id__ + __old_status = Thread.critical + begin + Thread.critical = true + unless @@id_rev_map.key?(self) + ObjectSpace.define_finalizer obj, @@final + ObjectSpace.define_finalizer self, @@final + end + @@id_map[@__id] = [] unless @@id_map[@__id] + ensure + Thread.critical = __old_status + end + @@id_map[@__id].push self.__id__ + @@id_rev_map[self.__id__] = @__id + end + + # Returns true if the referenced object still exists, and false if it has + # been garbage collected. + def weakref_alive? + @@id_rev_map[self.__id__] == @__id + end +end + +if __FILE__ == $0 + require 'thread' + foo = Object.new + p foo.to_s # original's class + foo = WeakRef.new(foo) + p foo.to_s # should be same class + ObjectSpace.garbage_collect + p foo.to_s # should raise exception (recycled) +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick.rb new file mode 100644 index 0000000000..8fca81bafb --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick.rb @@ -0,0 +1,29 @@ +# +# WEBrick -- WEB server toolkit. +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: webrick.rb,v 1.12 2002/10/01 17:16:31 gotoyuzo Exp $ + +require 'webrick/compat.rb' + +require 'webrick/version.rb' +require 'webrick/config.rb' +require 'webrick/log.rb' +require 'webrick/server.rb' +require 'webrick/utils.rb' +require 'webrick/accesslog' + +require 'webrick/htmlutils.rb' +require 'webrick/httputils.rb' +require 'webrick/cookie.rb' +require 'webrick/httpversion.rb' +require 'webrick/httpstatus.rb' +require 'webrick/httprequest.rb' +require 'webrick/httpresponse.rb' +require 'webrick/httpserver.rb' +require 'webrick/httpservlet.rb' +require 'webrick/httpauth.rb' diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/accesslog.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/accesslog.rb new file mode 100644 index 0000000000..f97769545e --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/accesslog.rb @@ -0,0 +1,67 @@ +# +# accesslog.rb -- Access log handling utilities +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2002 keita yamaguchi +# Copyright (c) 2002 Internet Programming with Ruby writers +# +# $IPR: accesslog.rb,v 1.1 2002/10/01 17:16:32 gotoyuzo Exp $ + +module WEBrick + module AccessLog + class AccessLogError < StandardError; end + + CLF_TIME_FORMAT = "[%d/%b/%Y:%H:%M:%S %Z]" + COMMON_LOG_FORMAT = "%h %l %u %t \"%r\" %s %b" + CLF = COMMON_LOG_FORMAT + REFERER_LOG_FORMAT = "%{Referer}i -> %U" + AGENT_LOG_FORMAT = "%{User-Agent}i" + COMBINED_LOG_FORMAT = "#{CLF} \"%{Referer}i\" \"%{User-agent}i\"" + + module_function + + # This format specification is a subset of mod_log_config of Apache. + # http://httpd.apache.org/docs/mod/mod_log_config.html#formats + def setup_params(config, req, res) + params = Hash.new("") + params["a"] = req.peeraddr[3] + params["b"] = res.sent_size + params["e"] = ENV + params["f"] = res.filename || "" + params["h"] = req.peeraddr[2] + params["i"] = req + params["l"] = "-" + params["m"] = req.request_method + params["n"] = req.attributes + params["o"] = res + params["p"] = req.port + params["q"] = req.query_string + params["r"] = req.request_line.sub(/\x0d?\x0a\z/o, '') + params["s"] = res.status # won't support "%>s" + params["t"] = req.request_time + params["T"] = Time.now - req.request_time + params["u"] = req.user || "-" + params["U"] = req.unparsed_uri + params["v"] = config[:ServerName] + params + end + + def format(format_string, params) + format_string.gsub(/\%(?:\{(.*?)\})?>?([a-zA-Z%])/){ + param, spec = $1, $2 + case spec[0] + when ?e, ?i, ?n, ?o + raise AccessLogError, + "parameter is required for \"#{spec}\"" unless param + params[spec][param] || "-" + when ?t + params[spec].strftime(param || CLF_TIME_FORMAT) + when ?% + "%" + else + params[spec] + end + } + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/cgi.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/cgi.rb new file mode 100644 index 0000000000..3ece0e0b76 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/cgi.rb @@ -0,0 +1,257 @@ +# +# cgi.rb -- Yet another CGI library +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2003 Internet Programming with Ruby writers. All rights +# reserved. +# +# $Id: cgi.rb 11708 2007-02-12 23:01:19Z shyouhei $ + +require "webrick/httprequest" +require "webrick/httpresponse" +require "webrick/config" +require "stringio" + +module WEBrick + class CGI + CGIError = Class.new(StandardError) + + attr_reader :config, :logger + + def initialize(*args) + if defined?(MOD_RUBY) + unless ENV.has_key?("GATEWAY_INTERFACE") + Apache.request.setup_cgi_env + end + end + if %r{HTTP/(\d+\.\d+)} =~ ENV["SERVER_PROTOCOL"] + httpv = $1 + end + @config = WEBrick::Config::HTTP.dup.update( + :ServerSoftware => ENV["SERVER_SOFTWARE"] || "null", + :HTTPVersion => HTTPVersion.new(httpv || "1.0"), + :RunOnCGI => true, # to detect if it runs on CGI. + :NPH => false # set true to run as NPH script. + ) + if config = args.shift + @config.update(config) + end + @config[:Logger] ||= WEBrick::BasicLog.new($stderr) + @logger = @config[:Logger] + @options = args + end + + def [](key) + @config[key] + end + + def start(env=ENV, stdin=$stdin, stdout=$stdout) + sock = WEBrick::CGI::Socket.new(@config, env, stdin, stdout) + req = HTTPRequest.new(@config) + res = HTTPResponse.new(@config) + unless @config[:NPH] or defined?(MOD_RUBY) + def res.setup_header + unless @header["status"] + phrase = HTTPStatus::reason_phrase(@status) + @header["status"] = "#{@status} #{phrase}" + end + super + end + def res.status_line + "" + end + end + + begin + req.parse(sock) + req.script_name = (env["SCRIPT_NAME"] || File.expand_path($0)).dup + req.path_info = (env["PATH_INFO"] || "").dup + req.query_string = env["QUERY_STRING"] + req.user = env["REMOTE_USER"] + res.request_method = req.request_method + res.request_uri = req.request_uri + res.request_http_version = req.http_version + res.keep_alive = req.keep_alive? + self.service(req, res) + rescue HTTPStatus::Error => ex + res.set_error(ex) + rescue HTTPStatus::Status => ex + res.status = ex.code + rescue Exception => ex + @logger.error(ex) + res.set_error(ex, true) + ensure + req.fixup + if defined?(MOD_RUBY) + res.setup_header + Apache.request.status_line = "#{res.status} #{res.reason_phrase}" + Apache.request.status = res.status + table = Apache.request.headers_out + res.header.each{|key, val| + case key + when /^content-encoding$/i + Apache::request.content_encoding = val + when /^content-type$/i + Apache::request.content_type = val + else + table[key] = val.to_s + end + } + res.cookies.each{|cookie| + table.add("Set-Cookie", cookie.to_s) + } + Apache.request.send_http_header + res.send_body(sock) + else + res.send_response(sock) + end + end + end + + def service(req, res) + method_name = "do_" + req.request_method.gsub(/-/, "_") + if respond_to?(method_name) + __send__(method_name, req, res) + else + raise HTTPStatus::MethodNotAllowed, + "unsupported method `#{req.request_method}'." + end + end + + class Socket + include Enumerable + + private + + def initialize(config, env, stdin, stdout) + @config = config + @env = env + @header_part = StringIO.new + @body_part = stdin + @out_port = stdout + @out_port.binmode + + @server_addr = @env["SERVER_ADDR"] || "0.0.0.0" + @server_name = @env["SERVER_NAME"] + @server_port = @env["SERVER_PORT"] + @remote_addr = @env["REMOTE_ADDR"] + @remote_host = @env["REMOTE_HOST"] || @remote_addr + @remote_port = @env["REMOTE_PORT"] || 0 + + begin + @header_part << request_line << CRLF + setup_header + @header_part << CRLF + @header_part.rewind + rescue Exception => ex + raise CGIError, "invalid CGI environment" + end + end + + def request_line + meth = @env["REQUEST_METHOD"] || "GET" + unless url = @env["REQUEST_URI"] + url = (@env["SCRIPT_NAME"] || File.expand_path($0)).dup + url << @env["PATH_INFO"].to_s + url = WEBrick::HTTPUtils.escape_path(url) + if query_string = @env["QUERY_STRING"] + unless query_string.empty? + url << "?" << query_string + end + end + end + # we cannot get real HTTP version of client ;) + httpv = @config[:HTTPVersion] + return "#{meth} #{url} HTTP/#{httpv}" + end + + def setup_header + add_header("CONTENT_TYPE", "Content-Type") + add_header("CONTENT_LENGTH", "Content-length") + @env.each_key{|name| + if /^HTTP_(.*)/ =~ name + add_header(name, $1.gsub(/_/, "-")) + end + } + end + + def add_header(envname, hdrname) + if value = @env[envname] + unless value.empty? + @header_part << hdrname << ": " << value << CRLF + end + end + end + + def input + @header_part.eof? ? @body_part : @header_part + end + + public + + def peeraddr + [nil, @remote_port, @remote_host, @remote_addr] + end + + def addr + [nil, @server_port, @server_name, @server_addr] + end + + def gets(eol=LF) + input.gets(eol) + end + + def read(size=nil) + input.read(size) + end + + def each + input.each{|line| yield(line) } + end + + def <<(data) + @out_port << data + end + + def cert + return nil unless defined?(OpenSSL) + if pem = @env["SSL_SERVER_CERT"] + OpenSSL::X509::Certificate.new(pem) unless pem.empty? + end + end + + def peer_cert + return nil unless defined?(OpenSSL) + if pem = @env["SSL_CLIENT_CERT"] + OpenSSL::X509::Certificate.new(pem) unless pem.empty? + end + end + + def peer_cert_chain + return nil unless defined?(OpenSSL) + if @env["SSL_CLIENT_CERT_CHAIN_0"] + keys = @env.keys + certs = keys.sort.collect{|k| + if /^SSL_CLIENT_CERT_CHAIN_\d+$/ =~ k + if pem = @env[k] + OpenSSL::X509::Certificate.new(pem) unless pem.empty? + end + end + } + certs.compact + end + end + + def cipher + return nil unless defined?(OpenSSL) + if cipher = @env["SSL_CIPHER"] + ret = [ cipher ] + ret << @env["SSL_PROTOCOL"] + ret << @env["SSL_CIPHER_USEKEYSIZE"] + ret << @env["SSL_CIPHER_ALGKEYSIZE"] + ret + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/compat.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/compat.rb new file mode 100644 index 0000000000..ad7760b640 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/compat.rb @@ -0,0 +1,15 @@ +# +# compat.rb -- cross platform compatibility +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2002 GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: compat.rb,v 1.6 2002/10/01 17:16:32 gotoyuzo Exp $ + +module Errno + class EPROTO < SystemCallError; end + class ECONNRESET < SystemCallError; end + class ECONNABORTED < SystemCallError; end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/config.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/config.rb new file mode 100644 index 0000000000..5897b977d8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/config.rb @@ -0,0 +1,97 @@ +# +# config.rb -- Default configurations. +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2003 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: config.rb,v 1.52 2003/07/22 19:20:42 gotoyuzo Exp $ + +require 'webrick/version' +require 'webrick/httpversion' +require 'webrick/httputils' +require 'webrick/utils' +require 'webrick/log' + +module WEBrick + module Config + LIBDIR = File::dirname(__FILE__) + + # for GenericServer + General = { + :ServerName => Utils::getservername, + :BindAddress => nil, # "0.0.0.0" or "::" or nil + :Port => nil, # users MUST specifiy this!! + :MaxClients => 100, # maximum number of the concurrent connections + :ServerType => nil, # default: WEBrick::SimpleServer + :Logger => nil, # default: WEBrick::Log.new + :ServerSoftware => "WEBrick/#{WEBrick::VERSION} " + + "(Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})", + :TempDir => ENV['TMPDIR']||ENV['TMP']||ENV['TEMP']||'/tmp', + :DoNotListen => false, + :StartCallback => nil, + :StopCallback => nil, + :AcceptCallback => nil, + } + + # for HTTPServer, HTTPRequest, HTTPResponse ... + HTTP = General.dup.update( + :Port => 80, + :RequestTimeout => 30, + :HTTPVersion => HTTPVersion.new("1.1"), + :AccessLog => nil, + :MimeTypes => HTTPUtils::DefaultMimeTypes, + :DirectoryIndex => ["index.html","index.htm","index.cgi","index.rhtml"], + :DocumentRoot => nil, + :DocumentRootOptions => { :FancyIndexing => true }, + :RequestHandler => nil, + :RequestCallback => nil, # alias of :RequestHandler + :ServerAlias => nil, + + # for HTTPProxyServer + :ProxyAuthProc => nil, + :ProxyContentHandler => nil, + :ProxyVia => true, + :ProxyTimeout => true, + :ProxyURI => nil, + + :CGIInterpreter => nil, + :CGIPathEnv => nil, + + # workaround: if Request-URIs contain 8bit chars, + # they should be escaped before calling of URI::parse(). + :Escape8bitURI => false + ) + + FileHandler = { + :NondisclosureName => [".ht*", "*~"], + :FancyIndexing => false, + :HandlerTable => {}, + :HandlerCallback => nil, + :DirectoryCallback => nil, + :FileCallback => nil, + :UserDir => nil, # e.g. "public_html" + :AcceptableLanguages => [] # ["en", "ja", ... ] + } + + BasicAuth = { + :AutoReloadUserDB => true, + } + + DigestAuth = { + :Algorithm => 'MD5-sess', # or 'MD5' + :Domain => nil, # an array includes domain names. + :Qop => [ 'auth' ], # 'auth' or 'auth-int' or both. + :UseOpaque => true, + :UseNextNonce => false, + :CheckNc => false, + :UseAuthenticationInfoHeader => true, + :AutoReloadUserDB => true, + :NonceExpirePeriod => 30*60, + :NonceExpireDelta => 60, + :InternetExplorerHack => true, + :OperaHack => true, + } + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/cookie.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/cookie.rb new file mode 100644 index 0000000000..814e6645a3 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/cookie.rb @@ -0,0 +1,110 @@ +# +# cookie.rb -- Cookie class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: cookie.rb,v 1.16 2002/09/21 12:23:35 gotoyuzo Exp $ + +require 'time' +require 'webrick/httputils' + +module WEBrick + class Cookie + + attr_reader :name + attr_accessor :value, :version + attr_accessor :domain, :path, :secure + attr_accessor :comment, :max_age + #attr_accessor :comment_url, :discard, :port + + def initialize(name, value) + @name = name + @value = value + @version = 0 # Netscape Cookie + + @domain = @path = @secure = @comment = @max_age = + @expires = @comment_url = @discard = @port = nil + end + + def expires=(t) + @expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s) + end + + def expires + @expires && Time.parse(@expires) + end + + def to_s + ret = "" + ret << @name << "=" << @value + ret << "; " << "Version=" << @version.to_s if @version > 0 + ret << "; " << "Domain=" << @domain if @domain + ret << "; " << "Expires=" << @expires if @expires + ret << "; " << "Max-Age=" << @max_age.to_s if @max_age + ret << "; " << "Comment=" << @comment if @comment + ret << "; " << "Path=" << @path if @path + ret << "; " << "Secure" if @secure + ret + end + + # Cookie::parse() + # It parses Cookie field sent from the user agent. + def self.parse(str) + if str + ret = [] + cookie = nil + ver = 0 + str.split(/[;,]\s+/).each{|x| + key, val = x.split(/=/,2) + val = val ? HTTPUtils::dequote(val) : "" + case key + when "$Version"; ver = val.to_i + when "$Path"; cookie.path = val + when "$Domain"; cookie.domain = val + when "$Port"; cookie.port = val + else + ret << cookie if cookie + cookie = self.new(key, val) + cookie.version = ver + end + } + ret << cookie if cookie + ret + end + end + + def self.parse_set_cookie(str) + cookie_elem = str.split(/;/) + first_elem = cookie_elem.shift + first_elem.strip! + key, value = first_elem.split(/=/, 2) + cookie = new(key, HTTPUtils.dequote(value)) + cookie_elem.each{|pair| + pair.strip! + key, value = pair.split(/=/, 2) + if value + value = HTTPUtils.dequote(value.strip) + end + case key.downcase + when "domain" then cookie.domain = value + when "path" then cookie.path = value + when "expires" then cookie.expires = value + when "max-age" then cookie.max_age = Integer(value) + when "comment" then cookie.comment = value + when "version" then cookie.version = Integer(value) + when "secure" then cookie.secure = true + end + } + return cookie + end + + def self.parse_set_cookies(str) + return str.split(/,(?=[^;,]*=)|,$/).collect{|c| + parse_set_cookie(c) + } + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/htmlutils.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/htmlutils.rb new file mode 100644 index 0000000000..cf8d542c09 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/htmlutils.rb @@ -0,0 +1,25 @@ +# +# htmlutils.rb -- HTMLUtils Module +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: htmlutils.rb,v 1.7 2002/09/21 12:23:35 gotoyuzo Exp $ + +module WEBrick + module HTMLUtils + + def escape(string) + str = string ? string.dup : "" + str.gsub!(/&/n, '&') + str.gsub!(/\"/n, '"') + str.gsub!(/>/n, '>') + str.gsub!(/ generate_next_nonce(req), + 'rspauth' => digest_res + } + if @use_opaque + opaque_struct.time = req.request_time + opaque_struct.nonce = auth_info['nextnonce'] + opaque_struct.nc = "%08x" % (auth_req['nc'].hex + 1) + end + if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int" + ['qop','cnonce','nc'].each{|key| + auth_info[key] = auth_req[key] + } + end + res[@resp_info_field] = auth_info.keys.map{|key| + if key == 'nc' + key + '=' + auth_info[key] + else + key + "=" + HTTPUtils::quote(auth_info[key]) + end + }.join(', ') + end + info('%s: authentication scceeded.', auth_req['username']) + req.user = auth_req['username'] + return true + end + + def split_param_value(string) + ret = {} + while string.size != 0 + case string + when /^\s*([\w\-\.\*\%\!]+)=\s*\"((\\.|[^\"])*)\"\s*,?/ + key = $1 + matched = $2 + string = $' + ret[key] = matched.gsub(/\\(.)/, "\\1") + when /^\s*([\w\-\.\*\%\!]+)=\s*([^,\"]*),?/ + key = $1 + matched = $2 + string = $' + ret[key] = matched.clone + when /^s*^,/ + string = $' + else + break + end + end + ret + end + + def generate_next_nonce(req) + now = "%012d" % req.request_time.to_i + pk = hexdigest(now, @instance_key)[0,32] + nonce = [now + ":" + pk].pack("m*").chop # it has 60 length of chars. + nonce + end + + def check_nonce(req, auth_req) + username = auth_req['username'] + nonce = auth_req['nonce'] + + pub_time, pk = nonce.unpack("m*")[0].split(":", 2) + if (!pub_time || !pk) + error("%s: empty nonce is given", username) + return false + elsif (hexdigest(pub_time, @instance_key)[0,32] != pk) + error("%s: invalid private-key: %s for %s", + username, hexdigest(pub_time, @instance_key)[0,32], pk) + return false + end + + diff_time = req.request_time.to_i - pub_time.to_i + if (diff_time < 0) + error("%s: difference of time-stamp is negative.", username) + return false + elsif diff_time > @nonce_expire_period + error("%s: nonce is expired.", username) + return false + end + + return true + end + + def generate_opaque(req) + @mutex.synchronize{ + now = req.request_time + if now - @last_nonce_expire > @nonce_expire_delta + @opaques.delete_if{|key,val| + (now - val.time) > @nonce_expire_period + } + @last_nonce_expire = now + end + begin + opaque = Utils::random_string(16) + end while @opaques[opaque] + @opaques[opaque] = OpaqueInfo.new(now, nil, '00000001') + opaque + } + end + + def check_opaque(opaque_struct, req, auth_req) + if (@use_next_nonce && auth_req['nonce'] != opaque_struct.nonce) + error('%s: nonce unmatched. "%s" for "%s"', + auth_req['username'], auth_req['nonce'], opaque_struct.nonce) + return false + elsif !check_nonce(req, auth_req) + return false + end + if (@check_nc && auth_req['nc'] != opaque_struct.nc) + error('%s: nc unmatched."%s" for "%s"', + auth_req['username'], auth_req['nc'], opaque_struct.nc) + return false + end + true + end + + def check_uri(req, auth_req) + uri = auth_req['uri'] + if uri != req.request_uri.to_s && uri != req.unparsed_uri && + (@internet_explorer_hack && uri != req.path) + error('%s: uri unmatch. "%s" for "%s"', auth_req['username'], + auth_req['uri'], req.request_uri.to_s) + return false + end + true + end + + def hexdigest(*args) + @h.hexdigest(args.join(":")) + end + end + + class ProxyDigestAuth < DigestAuth + include ProxyAuthenticator + + def check_uri(req, auth_req) + return true + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpauth/htdigest.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpauth/htdigest.rb new file mode 100644 index 0000000000..3949756f2b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpauth/htdigest.rb @@ -0,0 +1,91 @@ +# +# httpauth/htdigest.rb -- Apache compatible htdigest file +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2003 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $ + +require 'webrick/httpauth/userdb' +require 'webrick/httpauth/digestauth' +require 'tempfile' + +module WEBrick + module HTTPAuth + class Htdigest + include UserDB + + def initialize(path) + @path = path + @mtime = Time.at(0) + @digest = Hash.new + @mutex = Mutex::new + @auth_type = DigestAuth + open(@path,"a").close unless File::exist?(@path) + reload + end + + def reload + mtime = File::mtime(@path) + if mtime > @mtime + @digest.clear + open(@path){|io| + while line = io.gets + line.chomp! + user, realm, pass = line.split(/:/, 3) + unless @digest[realm] + @digest[realm] = Hash.new + end + @digest[realm][user] = pass + end + } + @mtime = mtime + end + end + + def flush(output=nil) + output ||= @path + tmp = Tempfile.new("htpasswd", File::dirname(output)) + begin + each{|item| tmp.puts(item.join(":")) } + tmp.close + File::rename(tmp.path, output) + rescue + tmp.close(true) + end + end + + def get_passwd(realm, user, reload_db) + reload() if reload_db + if hash = @digest[realm] + hash[user] + end + end + + def set_passwd(realm, user, pass) + @mutex.synchronize{ + unless @digest[realm] + @digest[realm] = Hash.new + end + @digest[realm][user] = make_passwd(realm, user, pass) + } + end + + def delete_passwd(realm, user) + if hash = @digest[realm] + hash.delete(user) + end + end + + def each + @digest.keys.sort.each{|realm| + hash = @digest[realm] + hash.keys.sort.each{|user| + yield([user, realm, hash[user]]) + } + } + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpauth/htgroup.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpauth/htgroup.rb new file mode 100644 index 0000000000..c9270c61cc --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpauth/htgroup.rb @@ -0,0 +1,61 @@ +# +# httpauth/htgroup.rb -- Apache compatible htgroup file +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2003 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: htgroup.rb,v 1.1 2003/02/16 22:22:56 gotoyuzo Exp $ + +require 'tempfile' + +module WEBrick + module HTTPAuth + class Htgroup + def initialize(path) + @path = path + @mtime = Time.at(0) + @group = Hash.new + open(@path,"a").close unless File::exist?(@path) + reload + end + + def reload + if (mtime = File::mtime(@path)) > @mtime + @group.clear + open(@path){|io| + while line = io.gets + line.chomp! + group, members = line.split(/:\s*/) + @group[group] = members.split(/\s+/) + end + } + @mtime = mtime + end + end + + def flush(output=nil) + output ||= @path + tmp = Tempfile.new("htgroup", File::dirname(output)) + begin + @group.keys.sort.each{|group| + tmp.puts(format("%s: %s", group, self.members(group).join(" "))) + } + tmp.close + File::rename(tmp.path, output) + rescue + tmp.close(true) + end + end + + def members(group) + reload + @group[group] || [] + end + + def add(group, members) + @group[group] = members(group) | members + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpauth/htpasswd.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpauth/htpasswd.rb new file mode 100644 index 0000000000..40f9297b05 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpauth/htpasswd.rb @@ -0,0 +1,83 @@ +# +# httpauth/htpasswd -- Apache compatible htpasswd file +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2003 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: htpasswd.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $ + +require 'webrick/httpauth/userdb' +require 'webrick/httpauth/basicauth' +require 'tempfile' + +module WEBrick + module HTTPAuth + class Htpasswd + include UserDB + + def initialize(path) + @path = path + @mtime = Time.at(0) + @passwd = Hash.new + @auth_type = BasicAuth + open(@path,"a").close unless File::exist?(@path) + reload + end + + def reload + mtime = File::mtime(@path) + if mtime > @mtime + @passwd.clear + open(@path){|io| + while line = io.gets + line.chomp! + case line + when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z! + user, pass = line.split(":") + when /:\$/, /:\{SHA\}/ + raise NotImplementedError, + 'MD5, SHA1 .htpasswd file not supported' + else + raise StandardError, 'bad .htpasswd file' + end + @passwd[user] = pass + end + } + @mtime = mtime + end + end + + def flush(output=nil) + output ||= @path + tmp = Tempfile.new("htpasswd", File::dirname(output)) + begin + each{|item| tmp.puts(item.join(":")) } + tmp.close + File::rename(tmp.path, output) + rescue + tmp.close(true) + end + end + + def get_passwd(realm, user, reload_db) + reload() if reload_db + @passwd[user] + end + + def set_passwd(realm, user, pass) + @passwd[user] = make_passwd(realm, user, pass) + end + + def delete_passwd(realm, user) + @passwd.delete(user) + end + + def each + @passwd.keys.sort.each{|user| + yield([user, @passwd[user]]) + } + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpauth/userdb.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpauth/userdb.rb new file mode 100644 index 0000000000..33e01405f4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpauth/userdb.rb @@ -0,0 +1,29 @@ +# +# httpauth/userdb.rb -- UserDB mix-in module. +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2003 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: userdb.rb,v 1.2 2003/02/20 07:15:48 gotoyuzo Exp $ + +module WEBrick + module HTTPAuth + module UserDB + attr_accessor :auth_type # BasicAuth or DigestAuth + + def make_passwd(realm, user, pass) + @auth_type::make_passwd(realm, user, pass) + end + + def set_passwd(realm, user, pass) + self[user] = pass + end + + def get_passwd(realm, user, reload_db=false) + # reload_db is dummy + make_passwd(realm, user, self[user]) + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpproxy.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpproxy.rb new file mode 100644 index 0000000000..14e3499775 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpproxy.rb @@ -0,0 +1,254 @@ +# +# httpproxy.rb -- HTTPProxy Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2002 GOTO Kentaro +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: httpproxy.rb,v 1.18 2003/03/08 18:58:10 gotoyuzo Exp $ +# $kNotwork: straw.rb,v 1.3 2002/02/12 15:13:07 gotoken Exp $ + +require "webrick/httpserver" +require "net/http" + +Net::HTTP::version_1_2 if RUBY_VERSION < "1.7" + +module WEBrick + NullReader = Object.new + class << NullReader + def read(*args) + nil + end + alias gets read + end + + class HTTPProxyServer < HTTPServer + def initialize(config) + super + c = @config + @via = "#{c[:HTTPVersion]} #{c[:ServerName]}:#{c[:Port]}" + end + + def service(req, res) + if req.request_method == "CONNECT" + proxy_connect(req, res) + elsif req.unparsed_uri =~ %r!^http://! + proxy_service(req, res) + else + super(req, res) + end + end + + def proxy_auth(req, res) + if proc = @config[:ProxyAuthProc] + proc.call(req, res) + end + req.header.delete("proxy-authorization") + end + + # Some header fields shuold not be transfered. + HopByHop = %w( connection keep-alive proxy-authenticate upgrade + proxy-authorization te trailers transfer-encoding ) + ShouldNotTransfer = %w( set-cookie proxy-connection ) + def split_field(f) f ? f.split(/,\s+/).collect{|i| i.downcase } : [] end + + def choose_header(src, dst) + connections = split_field(src['connection']) + src.each{|key, value| + key = key.downcase + if HopByHop.member?(key) || # RFC2616: 13.5.1 + connections.member?(key) || # RFC2616: 14.10 + ShouldNotTransfer.member?(key) # pragmatics + @logger.debug("choose_header: `#{key}: #{value}'") + next + end + dst[key] = value + } + end + + # Net::HTTP is stupid about the multiple header fields. + # Here is workaround: + def set_cookie(src, dst) + if str = src['set-cookie'] + cookies = [] + str.split(/,\s*/).each{|token| + if /^[^=]+;/o =~ token + cookies[-1] << ", " << token + elsif /=/o =~ token + cookies << token + else + cookies[-1] << ", " << token + end + } + dst.cookies.replace(cookies) + end + end + + def set_via(h) + if @config[:ProxyVia] + if h['via'] + h['via'] << ", " << @via + else + h['via'] = @via + end + end + end + + def proxy_uri(req, res) + @config[:ProxyURI] + end + + def proxy_service(req, res) + # Proxy Authentication + proxy_auth(req, res) + + # Create Request-URI to send to the origin server + uri = req.request_uri + path = uri.path.dup + path << "?" << uri.query if uri.query + + # Choose header fields to transfer + header = Hash.new + choose_header(req, header) + set_via(header) + + # select upstream proxy server + if proxy = proxy_uri(req, res) + proxy_host = proxy.host + proxy_port = proxy.port + if proxy.userinfo + credentials = "Basic " + [proxy.userinfo].pack("m*") + credentials.chomp! + header['proxy-authorization'] = credentials + end + end + + response = nil + begin + http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port) + http.start{ + if @config[:ProxyTimeout] + ################################## these issues are + http.open_timeout = 30 # secs # necessary (maybe bacause + http.read_timeout = 60 # secs # Ruby's bug, but why?) + ################################## + end + case req.request_method + when "GET" then response = http.get(path, header) + when "POST" then response = http.post(path, req.body || "", header) + when "HEAD" then response = http.head(path, header) + else + raise HTTPStatus::MethodNotAllowed, + "unsupported method `#{req.request_method}'." + end + } + rescue => err + logger.debug("#{err.class}: #{err.message}") + raise HTTPStatus::ServiceUnavailable, err.message + end + + # Persistent connction requirements are mysterious for me. + # So I will close the connection in every response. + res['proxy-connection'] = "close" + res['connection'] = "close" + + # Convert Net::HTTP::HTTPResponse to WEBrick::HTTPProxy + res.status = response.code.to_i + choose_header(response, res) + set_cookie(response, res) + set_via(res) + res.body = response.body + + # Process contents + if handler = @config[:ProxyContentHandler] + handler.call(req, res) + end + end + + def proxy_connect(req, res) + # Proxy Authentication + proxy_auth(req, res) + + ua = Thread.current[:WEBrickSocket] # User-Agent + raise HTTPStatus::InternalServerError, + "[BUG] cannot get socket" unless ua + + host, port = req.unparsed_uri.split(":", 2) + # Proxy authentication for upstream proxy server + if proxy = proxy_uri(req, res) + proxy_request_line = "CONNECT #{host}:#{port} HTTP/1.0" + if proxy.userinfo + credentials = "Basic " + [proxy.userinfo].pack("m*") + credentials.chomp! + end + host, port = proxy.host, proxy.port + end + + begin + @logger.debug("CONNECT: upstream proxy is `#{host}:#{port}'.") + os = TCPSocket.new(host, port) # origin server + + if proxy + @logger.debug("CONNECT: sending a Request-Line") + os << proxy_request_line << CRLF + @logger.debug("CONNECT: > #{proxy_request_line}") + if credentials + @logger.debug("CONNECT: sending a credentials") + os << "Proxy-Authorization: " << credentials << CRLF + end + os << CRLF + proxy_status_line = os.gets(LF) + @logger.debug("CONNECT: read a Status-Line form the upstream server") + @logger.debug("CONNECT: < #{proxy_status_line}") + if %r{^HTTP/\d+\.\d+\s+200\s*} =~ proxy_status_line + while line = os.gets(LF) + break if /\A(#{CRLF}|#{LF})\z/om =~ line + end + else + raise HTTPStatus::BadGateway + end + end + @logger.debug("CONNECT #{host}:#{port}: succeeded") + res.status = HTTPStatus::RC_OK + rescue => ex + @logger.debug("CONNECT #{host}:#{port}: failed `#{ex.message}'") + res.set_error(ex) + raise HTTPStatus::EOFError + ensure + if handler = @config[:ProxyContentHandler] + handler.call(req, res) + end + res.send_response(ua) + access_log(@config, req, res) + + # Should clear request-line not to send the sesponse twice. + # see: HTTPServer#run + req.parse(NullReader) rescue nil + end + + begin + while fds = IO::select([ua, os]) + if fds[0].member?(ua) + buf = ua.sysread(1024); + @logger.debug("CONNECT: #{buf.size} byte from User-Agent") + os.syswrite(buf) + elsif fds[0].member?(os) + buf = os.sysread(1024); + @logger.debug("CONNECT: #{buf.size} byte from #{host}:#{port}") + ua.syswrite(buf) + end + end + rescue => ex + os.close + @logger.debug("CONNECT #{host}:#{port}: closed") + end + + raise HTTPStatus::EOFError + end + + def do_OPTIONS(req, res) + res['allow'] = "GET,HEAD,POST,OPTIONS,CONNECT" + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httprequest.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httprequest.rb new file mode 100644 index 0000000000..1d32293a27 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httprequest.rb @@ -0,0 +1,365 @@ +# +# httprequest.rb -- HTTPRequest Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: httprequest.rb,v 1.64 2003/07/13 17:18:22 gotoyuzo Exp $ + +require 'timeout' +require 'uri' + +require 'webrick/httpversion' +require 'webrick/httpstatus' +require 'webrick/httputils' +require 'webrick/cookie' + +module WEBrick + + class HTTPRequest + BODY_CONTAINABLE_METHODS = [ "POST", "PUT" ] + BUFSIZE = 1024*4 + + # Request line + attr_reader :request_line + attr_reader :request_method, :unparsed_uri, :http_version + + # Request-URI + attr_reader :request_uri, :host, :port, :path + attr_accessor :script_name, :path_info, :query_string + + # Header and entity body + attr_reader :raw_header, :header, :cookies + attr_reader :accept, :accept_charset + attr_reader :accept_encoding, :accept_language + + # Misc + attr_accessor :user + attr_reader :addr, :peeraddr + attr_reader :attributes + attr_reader :keep_alive + attr_reader :request_time + + def initialize(config) + @config = config + @logger = config[:Logger] + + @request_line = @request_method = + @unparsed_uri = @http_version = nil + + @request_uri = @host = @port = @path = nil + @script_name = @path_info = nil + @query_string = nil + @query = nil + @form_data = nil + + @raw_header = Array.new + @header = nil + @cookies = [] + @accept = [] + @accept_charset = [] + @accept_encoding = [] + @accept_language = [] + @body = "" + + @addr = @peeraddr = nil + @attributes = {} + @user = nil + @keep_alive = false + @request_time = nil + + @remaining_size = nil + @socket = nil + end + + def parse(socket=nil) + @socket = socket + begin + @peeraddr = socket.respond_to?(:peeraddr) ? socket.peeraddr : [] + @addr = socket.respond_to?(:addr) ? socket.addr : [] + rescue Errno::ENOTCONN + raise HTTPStatus::EOFError + end + + read_request_line(socket) + if @http_version.major > 0 + read_header(socket) + @header['cookie'].each{|cookie| + @cookies += Cookie::parse(cookie) + } + @accept = HTTPUtils.parse_qvalues(self['accept']) + @accept_charset = HTTPUtils.parse_qvalues(self['accept-charset']) + @accept_encoding = HTTPUtils.parse_qvalues(self['accept-encoding']) + @accept_language = HTTPUtils.parse_qvalues(self['accept-language']) + end + return if @request_method == "CONNECT" + return if @unparsed_uri == "*" + + begin + @request_uri = parse_uri(@unparsed_uri) + @path = HTTPUtils::unescape(@request_uri.path) + @path = HTTPUtils::normalize_path(@path) + @host = @request_uri.host + @port = @request_uri.port + @query_string = @request_uri.query + @script_name = "" + @path_info = @path.dup + rescue + raise HTTPStatus::BadRequest, "bad URI `#{@unparsed_uri}'." + end + + if /close/io =~ self["connection"] + @keep_alive = false + elsif /keep-alive/io =~ self["connection"] + @keep_alive = true + elsif @http_version < "1.1" + @keep_alive = false + else + @keep_alive = true + end + end + + def body(&block) + block ||= Proc.new{|chunk| @body << chunk } + read_body(@socket, block) + @body.empty? ? nil : @body + end + + def query + unless @query + parse_query() + end + @query + end + + def content_length + return Integer(self['content-length']) + end + + def content_type + return self['content-type'] + end + + def [](header_name) + if @header + value = @header[header_name.downcase] + value.empty? ? nil : value.join(", ") + end + end + + def each + @header.each{|k, v| + value = @header[k] + yield(k, value.empty? ? nil : value.join(", ")) + } + end + + def keep_alive? + @keep_alive + end + + def to_s + ret = @request_line.dup + @raw_header.each{|line| ret << line } + ret << CRLF + ret << body if body + ret + end + + def fixup() + begin + body{|chunk| } # read remaining body + rescue HTTPStatus::Error => ex + @logger.error("HTTPRequest#fixup: #{ex.class} occured.") + @keep_alive = false + rescue => ex + @logger.error(ex) + @keep_alive = false + end + end + + def meta_vars + # This method provides the metavariables defined by the revision 3 + # of ``The WWW Common Gateway Interface Version 1.1''. + # (http://Web.Golux.Com/coar/cgi/) + + meta = Hash.new + + cl = self["Content-Length"] + ct = self["Content-Type"] + meta["CONTENT_LENGTH"] = cl if cl.to_i > 0 + meta["CONTENT_TYPE"] = ct.dup if ct + meta["GATEWAY_INTERFACE"] = "CGI/1.1" + meta["PATH_INFO"] = @path_info ? @path_info.dup : "" + #meta["PATH_TRANSLATED"] = nil # no plan to be provided + meta["QUERY_STRING"] = @query_string ? @query_string.dup : "" + meta["REMOTE_ADDR"] = @peeraddr[3] + meta["REMOTE_HOST"] = @peeraddr[2] + #meta["REMOTE_IDENT"] = nil # no plan to be provided + meta["REMOTE_USER"] = @user + meta["REQUEST_METHOD"] = @request_method.dup + meta["REQUEST_URI"] = @request_uri.to_s + meta["SCRIPT_NAME"] = @script_name.dup + meta["SERVER_NAME"] = @host + meta["SERVER_PORT"] = @port.to_s + meta["SERVER_PROTOCOL"] = "HTTP/" + @config[:HTTPVersion].to_s + meta["SERVER_SOFTWARE"] = @config[:ServerSoftware].dup + + self.each{|key, val| + next if /^content-type$/i =~ key + next if /^content-length$/i =~ key + name = "HTTP_" + key + name.gsub!(/-/o, "_") + name.upcase! + meta[name] = val + } + + meta + end + + private + + def read_request_line(socket) + @request_line = read_line(socket) if socket + @request_time = Time.now + raise HTTPStatus::EOFError unless @request_line + if /^(\S+)\s+(\S+)(?:\s+HTTP\/(\d+\.\d+))?\r?\n/mo =~ @request_line + @request_method = $1 + @unparsed_uri = $2 + @http_version = HTTPVersion.new($3 ? $3 : "0.9") + else + rl = @request_line.sub(/\x0d?\x0a\z/o, '') + raise HTTPStatus::BadRequest, "bad Request-Line `#{rl}'." + end + end + + def read_header(socket) + if socket + while line = read_line(socket) + break if /\A(#{CRLF}|#{LF})\z/om =~ line + @raw_header << line + end + end + begin + @header = HTTPUtils::parse_header(@raw_header) + rescue => ex + raise HTTPStatus::BadRequest, ex.message + end + end + + def parse_uri(str, scheme="http") + if @config[:Escape8bitURI] + str = HTTPUtils::escape8bit(str) + end + uri = URI::parse(str) + return uri if uri.absolute? + if self["host"] + pattern = /\A(#{URI::REGEXP::PATTERN::HOST})(?::(\d+))?\z/n + host, port = *self['host'].scan(pattern)[0] + elsif @addr.size > 0 + host, port = @addr[2], @addr[1] + else + host, port = @config[:ServerName], @config[:Port] + end + uri.scheme = scheme + uri.host = host + uri.port = port ? port.to_i : nil + return URI::parse(uri.to_s) + end + + def read_body(socket, block) + return unless socket + if tc = self['transfer-encoding'] + case tc + when /chunked/io then read_chunked(socket, block) + else raise HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}." + end + elsif self['content-length'] || @remaining_size + @remaining_size ||= self['content-length'].to_i + while @remaining_size > 0 + sz = BUFSIZE < @remaining_size ? BUFSIZE : @remaining_size + break unless buf = read_data(socket, sz) + @remaining_size -= buf.size + block.call(buf) + end + if @remaining_size > 0 && @socket.eof? + raise HTTPStatus::BadRequest, "invalid body size." + end + elsif BODY_CONTAINABLE_METHODS.member?(@request_method) + raise HTTPStatus::LengthRequired + end + return @body + end + + def read_chunk_size(socket) + line = read_line(socket) + if /^([0-9a-fA-F]+)(?:;(\S+))?/ =~ line + chunk_size = $1.hex + chunk_ext = $2 + [ chunk_size, chunk_ext ] + else + raise HTTPStatus::BadRequest, "bad chunk `#{line}'." + end + end + + def read_chunked(socket, block) + chunk_size, = read_chunk_size(socket) + while chunk_size > 0 + data = "" + while data.size < chunk_size + tmp = read_data(socket, chunk_size-data.size) # read chunk-data + break unless tmp + data << tmp + end + if data.nil? || data.size != chunk_size + raise BadRequest, "bad chunk data size." + end + read_line(socket) # skip CRLF + block.call(data) + chunk_size, = read_chunk_size(socket) + end + read_header(socket) # trailer + CRLF + @header.delete("transfer-encoding") + @remaining_size = 0 + end + + def _read_data(io, method, arg) + begin + timeout(@config[:RequestTimeout]){ + return io.__send__(method, arg) + } + rescue Errno::ECONNRESET + return nil + rescue TimeoutError + raise HTTPStatus::RequestTimeout + end + end + + def read_line(io) + _read_data(io, :gets, LF) + end + + def read_data(io, size) + _read_data(io, :read, size) + end + + def parse_query() + begin + if @request_method == "GET" || @request_method == "HEAD" + @query = HTTPUtils::parse_query(@query_string) + elsif self['content-type'] =~ /^application\/x-www-form-urlencoded/ + @query = HTTPUtils::parse_query(body) + elsif self['content-type'] =~ /^multipart\/form-data; boundary=(.+)/ + boundary = HTTPUtils::dequote($1) + @query = HTTPUtils::parse_form_data(body, boundary) + else + @query = Hash.new + end + rescue => ex + raise HTTPStatus::BadRequest, ex.message + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpresponse.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpresponse.rb new file mode 100644 index 0000000000..d0f232d1e1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpresponse.rb @@ -0,0 +1,327 @@ +# +# httpresponse.rb -- HTTPResponse Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: httpresponse.rb,v 1.45 2003/07/11 11:02:25 gotoyuzo Exp $ + +require 'time' +require 'webrick/httpversion' +require 'webrick/htmlutils' +require 'webrick/httputils' +require 'webrick/httpstatus' + +module WEBrick + class HTTPResponse + BUFSIZE = 1024*4 + + attr_reader :http_version, :status, :header + attr_reader :cookies + attr_accessor :reason_phrase + attr_accessor :body + + attr_accessor :request_method, :request_uri, :request_http_version + attr_accessor :filename + attr_accessor :keep_alive + attr_reader :config, :sent_size + + def initialize(config) + @config = config + @logger = config[:Logger] + @header = Hash.new + @status = HTTPStatus::RC_OK + @reason_phrase = nil + @http_version = HTTPVersion::convert(@config[:HTTPVersion]) + @body = '' + @keep_alive = true + @cookies = [] + @request_method = nil + @request_uri = nil + @request_http_version = @http_version # temporary + @chunked = false + @filename = nil + @sent_size = 0 + end + + def status_line + "HTTP/#@http_version #@status #@reason_phrase #{CRLF}" + end + + def status=(status) + @status = status + @reason_phrase = HTTPStatus::reason_phrase(status) + end + + def [](field) + @header[field.downcase] + end + + def []=(field, value) + @header[field.downcase] = value.to_s + end + + def content_length + if len = self['content-length'] + return Integer(len) + end + end + + def content_length=(len) + self['content-length'] = len.to_s + end + + def content_type + self['content-type'] + end + + def content_type=(type) + self['content-type'] = type + end + + def each + @header.each{|k, v| yield(k, v) } + end + + def chunked? + @chunked + end + + def chunked=(val) + @chunked = val ? true : false + end + + def keep_alive? + @keep_alive + end + + def send_response(socket) + begin + setup_header() + send_header(socket) + send_body(socket) + rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN => ex + @logger.debug(ex) + @keep_alive = false + rescue Exception => ex + @logger.error(ex) + @keep_alive = false + end + end + + def setup_header() + @reason_phrase ||= HTTPStatus::reason_phrase(@status) + @header['server'] ||= @config[:ServerSoftware] + @header['date'] ||= Time.now.httpdate + + # HTTP/0.9 features + if @request_http_version < "1.0" + @http_version = HTTPVersion.new("0.9") + @keep_alive = false + end + + # HTTP/1.0 features + if @request_http_version < "1.1" + if chunked? + @chunked = false + ver = @request_http_version.to_s + msg = "chunked is set for an HTTP/#{ver} request. (ignored)" + @logger.warn(msg) + end + end + + # Determin the message length (RFC2616 -- 4.4 Message Length) + if @status == 304 || @status == 204 || HTTPStatus::info?(@status) + @header.delete('content-length') + @body = "" + elsif chunked? + @header["transfer-encoding"] = "chunked" + @header.delete('content-length') + elsif %r{^multipart/byteranges} =~ @header['content-type'] + @header.delete('content-length') + elsif @header['content-length'].nil? + unless @body.is_a?(IO) + @header['content-length'] = @body ? @body.size : 0 + end + end + + # Keep-Alive connection. + if @header['connection'] == "close" + @keep_alive = false + elsif keep_alive? + if chunked? || @header['content-length'] + @header['connection'] = "Keep-Alive" + end + else + @header['connection'] = "close" + end + + # Location is a single absoluteURI. + if location = @header['location'] + if @request_uri + @header['location'] = @request_uri.merge(location) + end + end + end + + def send_header(socket) + if @http_version.major > 0 + data = status_line() + @header.each{|key, value| + tmp = key.gsub(/\bwww|^te$|\b\w/){|s| s.upcase } + data << "#{tmp}: #{value}" << CRLF + } + @cookies.each{|cookie| + data << "Set-Cookie: " << cookie.to_s << CRLF + } + data << CRLF + _write_data(socket, data) + end + end + + def send_body(socket) + case @body + when IO then send_body_io(socket) + else send_body_string(socket) + end + end + + def to_s + ret = "" + send_response(ret) + ret + end + + def set_redirect(status, url) + @body = "#{url.to_s}.\n" + @header['location'] = url.to_s + raise status + end + + def set_error(ex, backtrace=false) + case ex + when HTTPStatus::Status + @keep_alive = false if HTTPStatus::error?(ex.code) + self.status = ex.code + else + @keep_alive = false + self.status = HTTPStatus::RC_INTERNAL_SERVER_ERROR + end + @header['content-type'] = "text/html" + + if respond_to?(:create_error_page) + create_error_page() + return + end + + if @request_uri + host, port = @request_uri.host, @request_uri.port + else + host, port = @config[:ServerName], @config[:Port] + end + + @body = '' + @body << <<-_end_of_html_ + + + #{HTMLUtils::escape(@reason_phrase)} + +

    #{HTMLUtils::escape(@reason_phrase)}

    + #{HTMLUtils::escape(ex.message)} +
    + _end_of_html_ + + if backtrace && $DEBUG + @body << "backtrace of `#{HTMLUtils::escape(ex.class.to_s)}' " + @body << "#{HTMLUtils::escape(ex.message)}" + @body << "
    "
    +        ex.backtrace.each{|line| @body << "\t#{line}\n"}
    +        @body << "

    " + end + + @body << <<-_end_of_html_ +
    + #{HTMLUtils::escape(@config[:ServerSoftware])} at + #{host}:#{port} +
    + + + _end_of_html_ + end + + private + + def send_body_io(socket) + begin + if @request_method == "HEAD" + # do nothing + elsif chunked? + while buf = @body.read(BUFSIZE) + next if buf.empty? + data = "" + data << format("%x", buf.size) << CRLF + data << buf << CRLF + _write_data(socket, data) + @sent_size += buf.size + end + _write_data(socket, "0#{CRLF}#{CRLF}") + else + size = @header['content-length'].to_i + _send_file(socket, @body, 0, size) + @sent_size = size + end + ensure + @body.close + end + end + + def send_body_string(socket) + if @request_method == "HEAD" + # do nothing + elsif chunked? + remain = body ? @body.size : 0 + while buf = @body[@sent_size, BUFSIZE] + break if buf.empty? + data = "" + data << format("%x", buf.size) << CRLF + data << buf << CRLF + _write_data(socket, data) + @sent_size += buf.size + end + _write_data(socket, "0#{CRLF}#{CRLF}") + else + if @body && @body.size > 0 + _write_data(socket, @body) + @sent_size = @body.size + end + end + end + + def _send_file(output, input, offset, size) + while offset > 0 + sz = BUFSIZE < offset ? BUFSIZE : offset + buf = input.read(sz) + offset -= buf.size + end + + if size == 0 + while buf = input.read(BUFSIZE) + _write_data(output, buf) + end + else + while size > 0 + sz = BUFSIZE < size ? BUFSIZE : size + buf = input.read(sz) + _write_data(output, buf) + size -= buf.size + end + end + end + + def _write_data(socket, data) + socket << data + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/https.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/https.rb new file mode 100644 index 0000000000..81b65ce803 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/https.rb @@ -0,0 +1,63 @@ +# +# https.rb -- SSL/TLS enhancement for HTTPServer +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2001 GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: https.rb,v 1.15 2003/07/22 19:20:42 gotoyuzo Exp $ + +require 'webrick/ssl' + +module WEBrick + module Config + HTTP.update(SSL) + end + + class HTTPRequest + attr_reader :cipher, :server_cert, :client_cert + + alias orig_parse parse + + def parse(socket=nil) + if socket.respond_to?(:cert) + @server_cert = socket.cert || @config[:SSLCertificate] + @client_cert = socket.peer_cert + @client_cert_chain = socket.peer_cert_chain + @cipher = socket.cipher + end + orig_parse(socket) + end + + alias orig_parse_uri parse_uri + + def parse_uri(str, scheme="https") + if @server_cert + return orig_parse_uri(str, scheme) + end + return orig_parse_uri(str) + end + + alias orig_meta_vars meta_vars + + def meta_vars + meta = orig_meta_vars + if @server_cert + meta["HTTPS"] = "on" + meta["SSL_SERVER_CERT"] = @server_cert.to_pem + meta["SSL_CLIENT_CERT"] = @client_cert ? @client_cert.to_pem : "" + if @client_cert_chain + @client_cert_chain.each_with_index{|cert, i| + meta["SSL_CLIENT_CERT_CHAIN_#{i}"] = cert.to_pem + } + end + meta["SSL_CIPHER"] = @cipher[0] + meta["SSL_PROTOCOL"] = @cipher[1] + meta["SSL_CIPHER_USEKEYSIZE"] = @cipher[2].to_s + meta["SSL_CIPHER_ALGKEYSIZE"] = @cipher[3].to_s + end + meta + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpserver.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpserver.rb new file mode 100644 index 0000000000..9edb0018f8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpserver.rb @@ -0,0 +1,210 @@ +# +# httpserver.rb -- HTTPServer Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: httpserver.rb,v 1.63 2002/10/01 17:16:32 gotoyuzo Exp $ + +require 'webrick/server' +require 'webrick/httputils' +require 'webrick/httpstatus' +require 'webrick/httprequest' +require 'webrick/httpresponse' +require 'webrick/httpservlet' +require 'webrick/accesslog' + +module WEBrick + class HTTPServerError < ServerError; end + + class HTTPServer < ::WEBrick::GenericServer + def initialize(config={}, default=Config::HTTP) + super + @http_version = HTTPVersion::convert(@config[:HTTPVersion]) + + @mount_tab = MountTable.new + if @config[:DocumentRoot] + mount("/", HTTPServlet::FileHandler, @config[:DocumentRoot], + @config[:DocumentRootOptions]) + end + + unless @config[:AccessLog] + @config[:AccessLog] = [ + [ $stderr, AccessLog::COMMON_LOG_FORMAT ], + [ $stderr, AccessLog::REFERER_LOG_FORMAT ] + ] + end + + @virtual_hosts = Array.new + end + + def run(sock) + while true + res = HTTPResponse.new(@config) + req = HTTPRequest.new(@config) + server = self + begin + timeout = @config[:RequestTimeout] + while timeout > 0 + break if IO.select([sock], nil, nil, 0.5) + timeout = 0 if @status != :Running + timeout -= 0.5 + end + raise HTTPStatus::EOFError if timeout <= 0 || sock.eof? + req.parse(sock) + res.request_method = req.request_method + res.request_uri = req.request_uri + res.request_http_version = req.http_version + res.keep_alive = req.keep_alive? + server = lookup_server(req) || self + if callback = server[:RequestCallback] || server[:RequestHandler] + callback.call(req, res) + end + server.service(req, res) + rescue HTTPStatus::EOFError, HTTPStatus::RequestTimeout => ex + res.set_error(ex) + rescue HTTPStatus::Error => ex + @logger.error(ex.message) + res.set_error(ex) + rescue HTTPStatus::Status => ex + res.status = ex.code + rescue StandardError => ex + @logger.error(ex) + res.set_error(ex, true) + ensure + if req.request_line + req.fixup() + res.send_response(sock) + server.access_log(@config, req, res) + end + end + break if @http_version < "1.1" + break unless req.keep_alive? + break unless res.keep_alive? + end + end + + def service(req, res) + if req.unparsed_uri == "*" + if req.request_method == "OPTIONS" + do_OPTIONS(req, res) + raise HTTPStatus::OK + end + raise HTTPStatus::NotFound, "`#{req.unparsed_uri}' not found." + end + + servlet, options, script_name, path_info = search_servlet(req.path) + raise HTTPStatus::NotFound, "`#{req.path}' not found." unless servlet + req.script_name = script_name + req.path_info = path_info + si = servlet.get_instance(self, *options) + @logger.debug(format("%s is invoked.", si.class.name)) + si.service(req, res) + end + + def do_OPTIONS(req, res) + res["allow"] = "GET,HEAD,POST,OPTIONS" + end + + def mount(dir, servlet, *options) + @logger.debug(sprintf("%s is mounted on %s.", servlet.inspect, dir)) + @mount_tab[dir] = [ servlet, options ] + end + + def mount_proc(dir, proc=nil, &block) + proc ||= block + raise HTTPServerError, "must pass a proc or block" unless proc + mount(dir, HTTPServlet::ProcHandler.new(proc)) + end + + def unmount(dir) + @logger.debug(sprintf("unmount %s.", dir)) + @mount_tab.delete(dir) + end + alias umount unmount + + def search_servlet(path) + script_name, path_info = @mount_tab.scan(path) + servlet, options = @mount_tab[script_name] + if servlet + [ servlet, options, script_name, path_info ] + end + end + + def virtual_host(server) + @virtual_hosts << server + @virtual_hosts = @virtual_hosts.sort_by{|s| + num = 0 + num -= 4 if s[:BindAddress] + num -= 2 if s[:Port] + num -= 1 if s[:ServerName] + num + } + end + + def lookup_server(req) + @virtual_hosts.find{|s| + (s[:BindAddress].nil? || req.addr[3] == s[:BindAddress]) && + (s[:Port].nil? || req.port == s[:Port]) && + ((s[:ServerName].nil? || req.host == s[:ServerName]) || + (!s[:ServerAlias].nil? && s[:ServerAlias].find{|h| h === req.host})) + } + end + + def access_log(config, req, res) + param = AccessLog::setup_params(config, req, res) + @config[:AccessLog].each{|logger, fmt| + logger << AccessLog::format(fmt+"\n", param) + } + end + + class MountTable + def initialize + @tab = Hash.new + compile + end + + def [](dir) + dir = normalize(dir) + @tab[dir] + end + + def []=(dir, val) + dir = normalize(dir) + @tab[dir] = val + compile + val + end + + def delete(dir) + dir = normalize(dir) + res = @tab.delete(dir) + compile + res + end + + def scan(path) + @scanner =~ path + [ $&, $' ] + end + + private + + def compile + k = @tab.keys + k.sort! + k.reverse! + k.collect!{|path| Regexp.escape(path) } + @scanner = Regexp.new("^(" + k.join("|") +")(?=/|$)") + end + + def normalize(dir) + ret = dir ? dir.dup : "" + ret.sub!(%r|/+$|, "") + ret + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet.rb new file mode 100644 index 0000000000..ac7c022bd7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet.rb @@ -0,0 +1,22 @@ +# +# httpservlet.rb -- HTTPServlet Utility File +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: httpservlet.rb,v 1.21 2003/02/23 12:24:46 gotoyuzo Exp $ + +require 'webrick/httpservlet/abstract' +require 'webrick/httpservlet/filehandler' +require 'webrick/httpservlet/cgihandler' +require 'webrick/httpservlet/erbhandler' +require 'webrick/httpservlet/prochandler' + +module WEBrick + module HTTPServlet + FileHandler.add_handler("cgi", CGIHandler) + FileHandler.add_handler("rhtml", ERBHandler) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/abstract.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/abstract.rb new file mode 100644 index 0000000000..03861e8fc7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/abstract.rb @@ -0,0 +1,71 @@ +# +# httpservlet.rb -- HTTPServlet Module +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: abstract.rb,v 1.24 2003/07/11 11:16:46 gotoyuzo Exp $ + +require 'thread' + +require 'webrick/htmlutils' +require 'webrick/httputils' +require 'webrick/httpstatus' + +module WEBrick + module HTTPServlet + class HTTPServletError < StandardError; end + + class AbstractServlet + def self.get_instance(config, *options) + self.new(config, *options) + end + + def initialize(server, *options) + @server = @config = server + @logger = @server[:Logger] + @options = options + end + + def service(req, res) + method_name = "do_" + req.request_method.gsub(/-/, "_") + if respond_to?(method_name) + __send__(method_name, req, res) + else + raise HTTPStatus::MethodNotAllowed, + "unsupported method `#{req.request_method}'." + end + end + + def do_GET(req, res) + raise HTTPStatus::NotFound, "not found." + end + + def do_HEAD(req, res) + do_GET(req, res) + end + + def do_OPTIONS(req, res) + m = self.methods.grep(/^do_[A-Z]+$/) + m.collect!{|i| i.sub(/do_/, "") } + m.sort! + res["allow"] = m.join(",") + end + + private + + def redirect_to_directory_uri(req, res) + if req.path[-1] != ?/ + location = req.path + "/" + if req.query_string && req.query_string.size > 0 + location << "?" << req.query_string + end + res.set_redirect(HTTPStatus::MovedPermanently, location) + end + end + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/cgi_runner.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/cgi_runner.rb new file mode 100644 index 0000000000..1069a68d58 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/cgi_runner.rb @@ -0,0 +1,45 @@ +# +# cgi_runner.rb -- CGI launcher. +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: cgi_runner.rb,v 1.9 2002/09/25 11:33:15 gotoyuzo Exp $ + +def sysread(io, size) + buf = "" + while size > 0 + tmp = io.sysread(size) + buf << tmp + size -= tmp.size + end + return buf +end + +STDIN.binmode + +buf = "" +len = sysread(STDIN, 8).to_i +out = sysread(STDIN, len) +STDOUT.reopen(open(out, "w")) + +len = sysread(STDIN, 8).to_i +err = sysread(STDIN, len) +STDERR.reopen(open(err, "w")) + +len = sysread(STDIN, 8).to_i +dump = sysread(STDIN, len) +hash = Marshal.restore(dump) +ENV.keys.each{|name| ENV.delete(name) } +hash.each{|k, v| ENV[k] = v if v } + +dir = File::dirname(ENV["SCRIPT_FILENAME"]) +Dir::chdir dir + +if interpreter = ARGV[0] + exec(interpreter, ENV["SCRIPT_FILENAME"]) + # NOTREACHED +end +exec ENV["SCRIPT_FILENAME"] diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/cgihandler.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/cgihandler.rb new file mode 100644 index 0000000000..a35b59edb8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/cgihandler.rb @@ -0,0 +1,104 @@ +# +# cgihandler.rb -- CGIHandler Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: cgihandler.rb,v 1.27 2003/03/21 19:56:01 gotoyuzo Exp $ + +require 'rbconfig' +require 'tempfile' +require 'webrick/config' +require 'webrick/httpservlet/abstract' + +module WEBrick + module HTTPServlet + + class CGIHandler < AbstractServlet + Ruby = File::join(::Config::CONFIG['bindir'], + ::Config::CONFIG['ruby_install_name']) + Ruby << ::Config::CONFIG['EXEEXT'] + CGIRunner = "\"#{Ruby}\" \"#{Config::LIBDIR}/httpservlet/cgi_runner.rb\"" + + def initialize(server, name) + super + @script_filename = name + @tempdir = server[:TempDir] + @cgicmd = "#{CGIRunner} #{server[:CGIInterpreter]}" + end + + def do_GET(req, res) + data = nil + status = -1 + + cgi_in = IO::popen(@cgicmd, "wb") + cgi_out = Tempfile.new("webrick.cgiout.", @tempdir) + cgi_err = Tempfile.new("webrick.cgierr.", @tempdir) + begin + cgi_in.sync = true + meta = req.meta_vars + meta["SCRIPT_FILENAME"] = @script_filename + meta["PATH"] = @config[:CGIPathEnv] + if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM + meta["SystemRoot"] = ENV["SystemRoot"] + end + dump = Marshal.dump(meta) + + cgi_in.write("%8d" % cgi_out.path.size) + cgi_in.write(cgi_out.path) + cgi_in.write("%8d" % cgi_err.path.size) + cgi_in.write(cgi_err.path) + cgi_in.write("%8d" % dump.size) + cgi_in.write(dump) + + if req.body and req.body.size > 0 + cgi_in.write(req.body) + end + ensure + cgi_in.close + status = $?.exitstatus + sleep 0.1 if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM + data = cgi_out.read + cgi_out.close(true) + if errmsg = cgi_err.read + if errmsg.size > 0 + @logger.error("CGIHandler: #{@script_filename}:\n" + errmsg) + end + end + cgi_err.close(true) + end + + if status != 0 + @logger.error("CGIHandler: #{@script_filename} exit with #{status}") + end + + data = "" unless data + raw_header, body = data.split(/^[\xd\xa]+/on, 2) + raise HTTPStatus::InternalServerError, + "Premature end of script headers: #{@script_filename}" if body.nil? + + begin + header = HTTPUtils::parse_header(raw_header) + if /^(\d+)/ =~ header['status'][0] + res.status = $1.to_i + header.delete('status') + end + if header.has_key?('set-cookie') + header['set-cookie'].each{|k| + res.cookies << Cookie.parse_set_cookie(k) + } + header.delete('set-cookie') + end + header.each{|key, val| res[key] = val.join(", ") } + rescue => ex + raise HTTPStatus::InternalServerError, ex.message + end + res.body = body + end + alias do_POST do_GET + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/erbhandler.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/erbhandler.rb new file mode 100644 index 0000000000..b9d5e65b65 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/erbhandler.rb @@ -0,0 +1,54 @@ +# +# erbhandler.rb -- ERBHandler Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $ + +require 'webrick/httpservlet/abstract.rb' + +require 'erb' + +module WEBrick + module HTTPServlet + + class ERBHandler < AbstractServlet + def initialize(server, name) + super + @script_filename = name + end + + def do_GET(req, res) + unless defined?(ERB) + @logger.warn "#{self.class}: ERB not defined." + raise HTTPStatus::Forbidden, "ERBHandler cannot work." + end + begin + data = open(@script_filename){|io| io.read } + res.body = evaluate(ERB.new(data), req, res) + res['content-type'] = + HTTPUtils::mime_type(@script_filename, @config[:MimeTypes]) + rescue StandardError => ex + raise + rescue Exception => ex + @logger.error(ex) + raise HTTPStatus::InternalServerError, ex.message + end + end + + alias do_POST do_GET + + private + def evaluate(erb, servlet_request, servlet_response) + Module.new.module_eval{ + meta_vars = servlet_request.meta_vars + query = servlet_request.query + erb.result(binding) + } + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/filehandler.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/filehandler.rb new file mode 100644 index 0000000000..410cc6f9a9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/filehandler.rb @@ -0,0 +1,398 @@ +# +# filehandler.rb -- FileHandler Module +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2003 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: filehandler.rb,v 1.44 2003/06/07 01:34:51 gotoyuzo Exp $ + +require 'thread' +require 'time' + +require 'webrick/htmlutils' +require 'webrick/httputils' +require 'webrick/httpstatus' + +module WEBrick + module HTTPServlet + + class DefaultFileHandler < AbstractServlet + def initialize(server, local_path) + super + @local_path = local_path + end + + def do_GET(req, res) + st = File::stat(@local_path) + mtime = st.mtime + res['etag'] = sprintf("%x-%x-%x", st.ino, st.size, st.mtime.to_i) + + if not_modified?(req, res, mtime, res['etag']) + res.body = '' + raise HTTPStatus::NotModified + elsif req['range'] + make_partial_content(req, res, @local_path, st.size) + raise HTTPStatus::PartialContent + else + mtype = HTTPUtils::mime_type(@local_path, @config[:MimeTypes]) + res['content-type'] = mtype + res['content-length'] = st.size + res['last-modified'] = mtime.httpdate + res.body = open(@local_path, "rb") + end + end + + def not_modified?(req, res, mtime, etag) + if ir = req['if-range'] + begin + if Time.httpdate(ir) >= mtime + return true + end + rescue + if HTTPUtils::split_header_value(ir).member?(res['etag']) + return true + end + end + end + + if (ims = req['if-modified-since']) && Time.parse(ims) >= mtime + return true + end + + if (inm = req['if-none-match']) && + HTTPUtils::split_header_value(inm).member?(res['etag']) + return true + end + + return false + end + + def make_partial_content(req, res, filename, filesize) + mtype = HTTPUtils::mime_type(filename, @config[:MimeTypes]) + unless ranges = HTTPUtils::parse_range_header(req['range']) + raise HTTPStatus::BadRequest, + "Unrecognized range-spec: \"#{req['range']}\"" + end + open(filename, "rb"){|io| + if ranges.size > 1 + time = Time.now + boundary = "#{time.sec}_#{time.usec}_#{Process::pid}" + body = '' + ranges.each{|range| + first, last = prepare_range(range, filesize) + next if first < 0 + io.pos = first + content = io.read(last-first+1) + body << "--" << boundary << CRLF + body << "Content-Type: #{mtype}" << CRLF + body << "Content-Range: #{first}-#{last}/#{filesize}" << CRLF + body << CRLF + body << content + body << CRLF + } + raise HTTPStatus::RequestRangeNotSatisfiable if body.empty? + body << "--" << boundary << "--" << CRLF + res["content-type"] = "multipart/byteranges; boundary=#{boundary}" + res.body = body + elsif range = ranges[0] + first, last = prepare_range(range, filesize) + raise HTTPStatus::RequestRangeNotSatisfiable if first < 0 + if last == filesize - 1 + content = io.dup + content.pos = first + else + io.pos = first + content = io.read(last-first+1) + end + res['content-type'] = mtype + res['content-range'] = "#{first}-#{last}/#{filesize}" + res['content-length'] = last - first + 1 + res.body = content + else + raise HTTPStatus::BadRequest + end + } + end + + def prepare_range(range, filesize) + first = range.first < 0 ? filesize + range.first : range.first + return -1, -1 if first < 0 || first >= filesize + last = range.last < 0 ? filesize + range.last : range.last + last = filesize - 1 if last >= filesize + return first, last + end + end + + class FileHandler < AbstractServlet + HandlerTable = Hash.new + + def self.add_handler(suffix, handler) + HandlerTable[suffix] = handler + end + + def self.remove_handler(suffix) + HandlerTable.delete(suffix) + end + + def initialize(server, root, options={}, default=Config::FileHandler) + @config = server.config + @logger = @config[:Logger] + @root = File.expand_path(root) + if options == true || options == false + options = { :FancyIndexing => options } + end + @options = default.dup.update(options) + end + + def service(req, res) + # if this class is mounted on "/" and /~username is requested. + # we're going to override path informations before invoking service. + if defined?(Etc) && @options[:UserDir] && req.script_name.empty? + if %r|^(/~([^/]+))| =~ req.path_info + script_name, user = $1, $2 + path_info = $' + begin + passwd = Etc::getpwnam(user) + @root = File::join(passwd.dir, @options[:UserDir]) + req.script_name = script_name + req.path_info = path_info + rescue + @logger.debug "#{self.class}#do_GET: getpwnam(#{user}) failed" + end + end + end + super(req, res) + end + + def do_GET(req, res) + unless exec_handler(req, res) + set_dir_list(req, res) + end + end + + def do_POST(req, res) + unless exec_handler(req, res) + raise HTTPStatus::NotFound, "`#{req.path}' not found." + end + end + + def do_OPTIONS(req, res) + unless exec_handler(req, res) + super(req, res) + end + end + + # ToDo + # RFC2518: HTTP Extensions for Distributed Authoring -- WEBDAV + # + # PROPFIND PROPPATCH MKCOL DELETE PUT COPY MOVE + # LOCK UNLOCK + + # RFC3253: Versioning Extensions to WebDAV + # (Web Distributed Authoring and Versioning) + # + # VERSION-CONTROL REPORT CHECKOUT CHECK_IN UNCHECKOUT + # MKWORKSPACE UPDATE LABEL MERGE ACTIVITY + + private + + def exec_handler(req, res) + raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root + if set_filename(req, res) + handler = get_handler(req) + call_callback(:HandlerCallback, req, res) + h = handler.get_instance(@config, res.filename) + h.service(req, res) + return true + end + call_callback(:HandlerCallback, req, res) + return false + end + + def get_handler(req) + suffix1 = (/\.(\w+)$/ =~ req.script_name) && $1.downcase + suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ req.script_name) && $1.downcase + handler_table = @options[:HandlerTable] + return handler_table[suffix1] || handler_table[suffix2] || + HandlerTable[suffix1] || HandlerTable[suffix2] || + DefaultFileHandler + end + + def set_filename(req, res) + res.filename = @root.dup + path_info = req.path_info.scan(%r|/[^/]*|) + + path_info.unshift("") # dummy for checking @root dir + while base = path_info.first + check_filename(req, res, base) + break if base == "/" + break unless File.directory?(res.filename + base) + shift_path_info(req, res, path_info) + call_callback(:DirectoryCallback, req, res) + end + + if base = path_info.first + check_filename(req, res, base) + if base == "/" + if file = search_index_file(req, res) + shift_path_info(req, res, path_info, file) + call_callback(:FileCallback, req, res) + return true + end + shift_path_info(req, res, path_info) + elsif file = search_file(req, res, base) + shift_path_info(req, res, path_info, file) + call_callback(:FileCallback, req, res) + return true + else + raise HTTPStatus::NotFound, "`#{req.path}' not found." + end + end + + return false + end + + def check_filename(req, res, name) + @options[:NondisclosureName].each{|pattern| + if File.fnmatch("/#{pattern}", name) + @logger.warn("the request refers nondisclosure name `#{name}'.") + raise HTTPStatus::NotFound, "`#{req.path}' not found." + end + } + end + + def shift_path_info(req, res, path_info, base=nil) + tmp = path_info.shift + base = base || tmp + req.path_info = path_info.join + req.script_name << base + res.filename << base + end + + def search_index_file(req, res) + @config[:DirectoryIndex].each{|index| + if file = search_file(req, res, "/"+index) + return file + end + } + return nil + end + + def search_file(req, res, basename) + langs = @options[:AcceptableLanguages] + path = res.filename + basename + if File.file?(path) + return basename + elsif langs.size > 0 + req.accept_language.each{|lang| + path_with_lang = path + ".#{lang}" + if langs.member?(lang) && File.file?(path_with_lang) + return basename + ".#{lang}" + end + } + (langs - req.accept_language).each{|lang| + path_with_lang = path + ".#{lang}" + if File.file?(path_with_lang) + return basename + ".#{lang}" + end + } + end + return nil + end + + def call_callback(callback_name, req, res) + if cb = @options[callback_name] + cb.call(req, res) + end + end + + def nondisclosure_name?(name) + @options[:NondisclosureName].each{|pattern| + if File.fnmatch(pattern, name) + return true + end + } + return false + end + + def set_dir_list(req, res) + redirect_to_directory_uri(req, res) + unless @options[:FancyIndexing] + raise HTTPStatus::Forbidden, "no access permission to `#{req.path}'" + end + local_path = res.filename + list = Dir::entries(local_path).collect{|name| + next if name == "." || name == ".." + next if nondisclosure_name?(name) + st = (File::stat(local_path + name) rescue nil) + if st.nil? + [ name, nil, -1 ] + elsif st.directory? + [ name + "/", st.mtime, -1 ] + else + [ name, st.mtime, st.size ] + end + } + list.compact! + + if d0 = req.query["N"]; idx = 0 + elsif d0 = req.query["M"]; idx = 1 + elsif d0 = req.query["S"]; idx = 2 + else d0 = "A" ; idx = 0 + end + d1 = (d0 == "A") ? "D" : "A" + + if d0 == "A" + list.sort!{|a,b| a[idx] <=> b[idx] } + else + list.sort!{|a,b| b[idx] <=> a[idx] } + end + + res['content-type'] = "text/html" + + res.body = <<-_end_of_html_ + + + Index of #{HTMLUtils::escape(req.path)} + +

    Index of #{HTMLUtils::escape(req.path)}

    + _end_of_html_ + + res.body << "
    \n"
    +        res.body << " Name                          "
    +        res.body << "Last modified         "
    +        res.body << "Size\n"
    +        res.body << "
    \n" + + list.unshift [ "..", File::mtime(local_path+".."), -1 ] + list.each{ |name, time, size| + if name == ".." + dname = "Parent Directory" + elsif name.size > 25 + dname = name.sub(/^(.{23})(.*)/){ $1 + ".." } + else + dname = name + end + s = " #{dname}" + s << " " * (30 - dname.size) + s << (time ? time.strftime("%Y/%m/%d %H:%M ") : " " * 22) + s << (size >= 0 ? size.to_s : "-") << "\n" + res.body << s + } + res.body << "

    " + + res.body << <<-_end_of_html_ +
    + #{HTMLUtils::escape(@config[:ServerSoftware])}
    + at #{req.host}:#{req.port} +
    + + + _end_of_html_ + end + + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/prochandler.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/prochandler.rb new file mode 100644 index 0000000000..783cb27896 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpservlet/prochandler.rb @@ -0,0 +1,33 @@ +# +# prochandler.rb -- ProcHandler Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: prochandler.rb,v 1.7 2002/09/21 12:23:42 gotoyuzo Exp $ + +require 'webrick/httpservlet/abstract.rb' + +module WEBrick + module HTTPServlet + + class ProcHandler < AbstractServlet + def get_instance(server, *options) + self + end + + def initialize(proc) + @proc = proc + end + + def do_GET(request, response) + @proc.call(request, response) + end + + alias do_POST do_GET + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpstatus.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpstatus.rb new file mode 100644 index 0000000000..0b22c992b3 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpstatus.rb @@ -0,0 +1,126 @@ +# +# httpstatus.rb -- HTTPStatus Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: httpstatus.rb,v 1.11 2003/03/24 20:18:55 gotoyuzo Exp $ + +module WEBrick + + module HTTPStatus + + class Status < StandardError; end + class Info < Status; end + class Success < Status; end + class Redirect < Status; end + class Error < Status; end + class ClientError < Error; end + class ServerError < Error; end + + class EOFError < StandardError; end + + StatusMessage = { + 100, 'Continue', + 101, 'Switching Protocols', + 200, 'OK', + 201, 'Created', + 202, 'Accepted', + 203, 'Non-Authoritative Information', + 204, 'No Content', + 205, 'Reset Content', + 206, 'Partial Content', + 300, 'Multiple Choices', + 301, 'Moved Permanently', + 302, 'Found', + 303, 'See Other', + 304, 'Not Modified', + 305, 'Use Proxy', + 307, 'Temporary Redirect', + 400, 'Bad Request', + 401, 'Unauthorized', + 402, 'Payment Required', + 403, 'Forbidden', + 404, 'Not Found', + 405, 'Method Not Allowed', + 406, 'Not Acceptable', + 407, 'Proxy Authentication Required', + 408, 'Request Timeout', + 409, 'Conflict', + 410, 'Gone', + 411, 'Length Required', + 412, 'Precondition Failed', + 413, 'Request Entity Too Large', + 414, 'Request-URI Too Large', + 415, 'Unsupported Media Type', + 416, 'Request Range Not Satisfiable', + 417, 'Expectation Failed', + 500, 'Internal Server Error', + 501, 'Not Implemented', + 502, 'Bad Gateway', + 503, 'Service Unavailable', + 504, 'Gateway Timeout', + 505, 'HTTP Version Not Supported' + } + + CodeToError = {} + + StatusMessage.each{|code, message| + var_name = message.gsub(/[ \-]/,'_').upcase + err_name = message.gsub(/[ \-]/,'') + + case code + when 100...200; parent = Info + when 200...300; parent = Success + when 300...400; parent = Redirect + when 400...500; parent = ClientError + when 500...600; parent = ServerError + end + + eval %- + RC_#{var_name} = #{code} + class #{err_name} < #{parent} + def self.code() RC_#{var_name} end + def self.reason_phrase() StatusMessage[code] end + def code() self::class::code end + def reason_phrase() self::class::reason_phrase end + alias to_i code + end + - + + CodeToError[code] = const_get(err_name) + } + + def reason_phrase(code) + StatusMessage[code.to_i] + end + def info?(code) + code.to_i >= 100 and code.to_i < 200 + end + def success?(code) + code.to_i >= 200 and code.to_i < 300 + end + def redirect?(code) + code.to_i >= 300 and code.to_i < 400 + end + def error?(code) + code.to_i >= 400 and code.to_i < 600 + end + def client_error?(code) + code.to_i >= 400 and code.to_i < 500 + end + def server_error?(code) + code.to_i >= 500 and code.to_i < 600 + end + + def self.[](code) + CodeToError[code] + end + + module_function :reason_phrase + module_function :info?, :success?, :redirect?, :error? + module_function :client_error?, :server_error? + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httputils.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httputils.rb new file mode 100644 index 0000000000..c57af2c860 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httputils.rb @@ -0,0 +1,399 @@ +# +# httputils.rb -- HTTPUtils Module +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: httputils.rb,v 1.34 2003/06/05 21:34:08 gotoyuzo Exp $ + +require 'socket' +require 'tempfile' + +module WEBrick + CR = "\x0d" + LF = "\x0a" + CRLF = "\x0d\x0a" + + module HTTPUtils + + def normalize_path(path) + raise "abnormal path `#{path}'" if path[0] != ?/ + ret = path.dup + + ret.gsub!(%r{/+}o, '/') # // => / + while ret.sub!(%r{/\.(/|\Z)}o, '/'); end # /. => / + begin # /foo/.. => /foo + match = ret.sub!(%r{/([^/]+)/\.\.(/|\Z)}o){ + if $1 == ".." + raise "abnormal path `#{path}'" + else + "/" + end + } + end while match + + raise "abnormal path `#{path}'" if %r{/\.\.(/|\Z)} =~ ret + ret + end + module_function :normalize_path + + ##### + + DefaultMimeTypes = { + "ai" => "application/postscript", + "asc" => "text/plain", + "avi" => "video/x-msvideo", + "bin" => "application/octet-stream", + "bmp" => "image/bmp", + "class" => "application/octet-stream", + "cer" => "application/pkix-cert", + "crl" => "application/pkix-crl", + "crt" => "application/x-x509-ca-cert", + #"crl" => "application/x-pkcs7-crl", + "css" => "text/css", + "dms" => "application/octet-stream", + "doc" => "application/msword", + "dvi" => "application/x-dvi", + "eps" => "application/postscript", + "etx" => "text/x-setext", + "exe" => "application/octet-stream", + "gif" => "image/gif", + "htm" => "text/html", + "html" => "text/html", + "jpe" => "image/jpeg", + "jpeg" => "image/jpeg", + "jpg" => "image/jpeg", + "lha" => "application/octet-stream", + "lzh" => "application/octet-stream", + "mov" => "video/quicktime", + "mpe" => "video/mpeg", + "mpeg" => "video/mpeg", + "mpg" => "video/mpeg", + "pbm" => "image/x-portable-bitmap", + "pdf" => "application/pdf", + "pgm" => "image/x-portable-graymap", + "png" => "image/png", + "pnm" => "image/x-portable-anymap", + "ppm" => "image/x-portable-pixmap", + "ppt" => "application/vnd.ms-powerpoint", + "ps" => "application/postscript", + "qt" => "video/quicktime", + "ras" => "image/x-cmu-raster", + "rb" => "text/plain", + "rd" => "text/plain", + "rtf" => "application/rtf", + "sgm" => "text/sgml", + "sgml" => "text/sgml", + "tif" => "image/tiff", + "tiff" => "image/tiff", + "txt" => "text/plain", + "xbm" => "image/x-xbitmap", + "xls" => "application/vnd.ms-excel", + "xml" => "text/xml", + "xpm" => "image/x-xpixmap", + "xwd" => "image/x-xwindowdump", + "zip" => "application/zip", + } + + # Load Apache compatible mime.types file. + def load_mime_types(file) + open(file){ |io| + hash = Hash.new + io.each{ |line| + next if /^#/ =~ line + line.chomp! + mimetype, ext0 = line.split(/\s+/, 2) + next unless ext0 + next if ext0.empty? + ext0.split(/\s+/).each{ |ext| hash[ext] = mimetype } + } + hash + } + end + module_function :load_mime_types + + def mime_type(filename, mime_tab) + suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase) + suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ filename && $1.downcase) + mime_tab[suffix1] || mime_tab[suffix2] || "application/octet-stream" + end + module_function :mime_type + + ##### + + def parse_header(raw) + header = Hash.new([].freeze) + field = nil + raw.each{|line| + case line + when /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):\s*(.*?)\s*\z/om + field, value = $1, $2 + field.downcase! + header[field] = [] unless header.has_key?(field) + header[field] << value + when /^\s+(.*?)\s*\z/om + value = $1 + unless field + raise "bad header '#{line.inspect}'." + end + header[field][-1] << " " << value + else + raise "bad header '#{line.inspect}'." + end + } + header.each{|key, values| + values.each{|value| + value.strip! + value.gsub!(/\s+/, " ") + } + } + header + end + module_function :parse_header + + def split_header_value(str) + str.scan(/((?:"(?:\\.|[^"])+?"|[^",]+)+) + (?:,\s*|\Z)/xn).collect{|v| v[0] } + end + module_function :split_header_value + + def parse_range_header(ranges_specifier) + if /^bytes=(.*)/ =~ ranges_specifier + byte_range_set = split_header_value($1) + byte_range_set.collect{|range_spec| + case range_spec + when /^(\d+)-(\d+)/ then $1.to_i .. $2.to_i + when /^(\d+)-/ then $1.to_i .. -1 + when /^-(\d+)/ then -($1.to_i) .. -1 + else return nil + end + } + end + end + module_function :parse_range_header + + def parse_qvalues(value) + tmp = [] + if value + parts = value.split(/,\s*/) + parts.each {|part| + if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part) + val = m[1] + q = (m[2] or 1).to_f + tmp.push([val, q]) + end + } + tmp = tmp.sort_by{|val, q| -q} + tmp.collect!{|val, q| val} + end + return tmp + end + module_function :parse_qvalues + + ##### + + def dequote(str) + ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup + ret.gsub!(/\\(.)/, "\\1") + ret + end + module_function :dequote + + def quote(str) + '"' << str.gsub(/[\\\"]/o, "\\\1") << '"' + end + module_function :quote + + ##### + + class FormData < String + EmptyRawHeader = [].freeze + EmptyHeader = {}.freeze + + attr_accessor :name, :filename, :next_data + protected :next_data + + def initialize(*args) + @name = @filename = @next_data = nil + if args.empty? + @raw_header = [] + @header = nil + super("") + else + @raw_header = EmptyRawHeader + @header = EmptyHeader + super(args.shift) + unless args.empty? + @next_data = self.class.new(*args) + end + end + end + + def [](*key) + begin + @header[key[0].downcase].join(", ") + rescue StandardError, NameError + super + end + end + + def <<(str) + if @header + super + elsif str == CRLF + @header = HTTPUtils::parse_header(@raw_header) + if cd = self['content-disposition'] + if /\s+name="(.*?)"/ =~ cd then @name = $1 end + if /\s+filename="(.*?)"/ =~ cd then @filename = $1 end + end + else + @raw_header << str + end + self + end + + def append_data(data) + tmp = self + while tmp + unless tmp.next_data + tmp.next_data = data + break + end + tmp = tmp.next_data + end + self + end + + def each_data + tmp = self + while tmp + next_data = tmp.next_data + yield(tmp) + tmp = next_data + end + end + + def list + ret = [] + each_data{|data| + ret << data.to_s + } + ret + end + + alias :to_ary :list + + def to_s + String.new(self) + end + end + + def parse_query(str) + query = Hash.new + if str + str.split(/[&;]/).each{|x| + next if x.empty? + key, val = x.split(/=/,2) + key = unescape_form(key) + val = unescape_form(val.to_s) + val = FormData.new(val) + val.name = key + if query.has_key?(key) + query[key].append_data(val) + next + end + query[key] = val + } + end + query + end + module_function :parse_query + + def parse_form_data(io, boundary) + boundary_regexp = /\A--#{boundary}(--)?#{CRLF}\z/ + form_data = Hash.new + return form_data unless io + data = nil + io.each{|line| + if boundary_regexp =~ line + if data + data.chop! + key = data.name + if form_data.has_key?(key) + form_data[key].append_data(data) + else + form_data[key] = data + end + end + data = FormData.new + next + else + if data + data << line + end + end + } + return form_data + end + module_function :parse_form_data + + ##### + + reserved = ';/?:@&=+$,' + num = '0123456789' + lowalpha = 'abcdefghijklmnopqrstuvwxyz' + upalpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + mark = '-_.!~*\'()' + unreserved = num + lowalpha + upalpha + mark + control = (0x0..0x1f).collect{|c| c.chr }.join + "\x7f" + space = " " + delims = '<>#%"' + unwise = '{}|\\^[]`' + nonascii = (0x80..0xff).collect{|c| c.chr }.join + + module_function + + def _make_regex(str) /([#{Regexp.escape(str)}])/n end + def _make_regex!(str) /([^#{Regexp.escape(str)}])/n end + def _escape(str, regex) str.gsub(regex){ "%%%02X" % $1[0] } end + def _unescape(str, regex) str.gsub(regex){ $1.hex.chr } end + + UNESCAPED = _make_regex(control+space+delims+unwise+nonascii) + UNESCAPED_FORM = _make_regex(reserved+control+delims+unwise+nonascii) + NONASCII = _make_regex(nonascii) + ESCAPED = /%([0-9a-fA-F]{2})/ + UNESCAPED_PCHAR = _make_regex!(unreserved+":@&=+$,") + + def escape(str) + _escape(str, UNESCAPED) + end + + def unescape(str) + _unescape(str, ESCAPED) + end + + def escape_form(str) + ret = _escape(str, UNESCAPED_FORM) + ret.gsub!(/ /, "+") + ret + end + + def unescape_form(str) + _unescape(str.gsub(/\+/, " "), ESCAPED) + end + + def escape_path(str) + result = "" + str.scan(%r{/([^/]*)}).each{|i| + result << "/" << _escape(i[0], UNESCAPED_PCHAR) + } + return result + end + + def escape8bit(str) + _escape(str, NONASCII) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpversion.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpversion.rb new file mode 100644 index 0000000000..86907a26bd --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/httpversion.rb @@ -0,0 +1,49 @@ +# +# HTTPVersion.rb -- presentation of HTTP version +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: httpversion.rb,v 1.5 2002/09/21 12:23:37 gotoyuzo Exp $ + +module WEBrick + class HTTPVersion + include Comparable + + attr_accessor :major, :minor + + def self.convert(version) + version.is_a?(self) ? version : new(version) + end + + def initialize(version) + case version + when HTTPVersion + @major, @minor = version.major, version.minor + when String + if /^(\d+)\.(\d+)$/ =~ version + @major, @minor = $1.to_i, $2.to_i + end + end + if @major.nil? || @minor.nil? + raise ArgumentError, + format("cannot convert %s into %s", version.class, self.class) + end + end + + def <=>(other) + unless other.is_a?(self.class) + other = self.class.new(other) + end + if (ret = @major <=> other.major) == 0 + return @minor <=> other.minor + end + return ret + end + + def to_s + format("%d.%d", @major, @minor) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/log.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/log.rb new file mode 100644 index 0000000000..5d4fd0a174 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/log.rb @@ -0,0 +1,88 @@ +# +# log.rb -- Log Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: log.rb,v 1.26 2002/10/06 17:06:10 gotoyuzo Exp $ + +module WEBrick + class BasicLog + # log-level constant + FATAL, ERROR, WARN, INFO, DEBUG = 1, 2, 3, 4, 5 + + attr_accessor :level + + def initialize(log_file=nil, level=nil) + @level = level || INFO + case log_file + when String + @log = open(log_file, "a+") + @log.sync = true + @opened = true + when NilClass + @log = $stderr + else + @log = log_file # requires "<<". (see BasicLog#log) + end + end + + def close + @log.close if @opened + @log = nil + end + + def log(level, data) + if @log && level <= @level + data += "\n" if /\n\Z/ !~ data + @log << data + end + end + + def <<(obj) + log(INFO, obj.to_s) + end + + def fatal(msg) log(FATAL, "FATAL " << format(msg)); end + def error(msg) log(ERROR, "ERROR " << format(msg)); end + def warn(msg) log(WARN, "WARN " << format(msg)); end + def info(msg) log(INFO, "INFO " << format(msg)); end + def debug(msg) log(DEBUG, "DEBUG " << format(msg)); end + + def fatal?; @level >= FATAL; end + def error?; @level >= ERROR; end + def warn?; @level >= WARN; end + def info?; @level >= INFO; end + def debug?; @level >= DEBUG; end + + private + + def format(arg) + str = if arg.is_a?(Exception) + "#{arg.class}: #{arg.message}\n\t" << + arg.backtrace.join("\n\t") << "\n" + elsif arg.respond_to?(:to_str) + arg.to_str + else + arg.inspect + end + end + end + + class Log < BasicLog + attr_accessor :time_format + + def initialize(log_file=nil, level=nil) + super(log_file, level) + @time_format = "[%Y-%m-%d %H:%M:%S]" + end + + def log(level, data) + tmp = Time.now.strftime(@time_format) + tmp << " " << data + super(level, tmp) + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/server.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/server.rb new file mode 100644 index 0000000000..16dbd46f98 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/server.rb @@ -0,0 +1,200 @@ +# +# server.rb -- GenericServer Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: server.rb,v 1.62 2003/07/22 19:20:43 gotoyuzo Exp $ + +require 'thread' +require 'socket' +require 'timeout' +require 'webrick/config' +require 'webrick/log' + +module WEBrick + + class ServerError < StandardError; end + + class SimpleServer + def SimpleServer.start + yield + end + end + + class Daemon + def Daemon.start + exit!(0) if fork + Process::setsid + exit!(0) if fork + Dir::chdir("/") + File::umask(0) + STDIN.reopen("/dev/null") + STDOUT.reopen("/dev/null", "w") + STDERR.reopen("/dev/null", "w") + yield if block_given? + end + end + + class GenericServer + attr_reader :status, :config, :logger, :tokens, :listeners + + def initialize(config={}, default=Config::General) + @config = default.dup.update(config) + @status = :Stop + @config[:Logger] ||= Log::new + @logger = @config[:Logger] + + @tokens = SizedQueue.new(@config[:MaxClients]) + @config[:MaxClients].times{ @tokens.push(nil) } + + webrickv = WEBrick::VERSION + rubyv = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" + @logger.info("WEBrick #{webrickv}") + @logger.info("ruby #{rubyv}") + + @listeners = [] + unless @config[:DoNotListen] + if @config[:Listen] + warn(":Listen option is deprecated; use GenericServer#listen") + end + listen(@config[:BindAddress], @config[:Port]) + if @config[:Port] == 0 + @config[:Port] = @listeners[0].addr[1] + end + end + end + + def [](key) + @config[key] + end + + def listen(address, port) + @listeners += Utils::create_listeners(address, port, @logger) + end + + def start(&block) + raise ServerError, "already started." if @status != :Stop + server_type = @config[:ServerType] || SimpleServer + + server_type.start{ + @logger.info \ + "#{self.class}#start: pid=#{$$} port=#{@config[:Port]}" + call_callback(:StartCallback) + + thgroup = ThreadGroup.new + @status = :Running + while @status == :Running + begin + if svrs = IO.select(@listeners, nil, nil, 2.0) + svrs[0].each{|svr| + @tokens.pop # blocks while no token is there. + if sock = accept_client(svr) + th = start_thread(sock, &block) + th[:WEBrickThread] = true + thgroup.add(th) + else + @tokens.push(nil) + end + } + end + rescue Errno::EBADF, IOError => ex + # if the listening socket was closed in GenericServer#shutdown, + # IO::select raise it. + rescue Exception => ex + msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}" + @logger.error msg + end + end + + @logger.info "going to shutdown ..." + thgroup.list.each{|th| th.join if th[:WEBrickThread] } + call_callback(:StopCallback) + @logger.info "#{self.class}#start done." + @status = :Stop + } + end + + def stop + if @status == :Running + @status = :Shutdown + end + end + + def shutdown + stop + @listeners.each{|s| + if @logger.debug? + addr = s.addr + @logger.debug("close TCPSocket(#{addr[2]}, #{addr[1]})") + end + s.close + } + @listeners.clear + end + + def run(sock) + @logger.fatal "run() must be provided by user." + end + + private + + def accept_client(svr) + sock = nil + begin + sock = svr.accept + sock.sync = true + Utils::set_non_blocking(sock) + Utils::set_close_on_exec(sock) + rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPROTO => ex + # TCP connection was established but RST segment was sent + # from peer before calling TCPServer#accept. + rescue Exception => ex + msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}" + @logger.error msg + end + return sock + end + + def start_thread(sock, &block) + Thread.start{ + begin + Thread.current[:WEBrickSocket] = sock + begin + addr = sock.peeraddr + @logger.debug "accept: #{addr[3]}:#{addr[1]}" + rescue SocketError + @logger.debug "accept:
    " + raise + end + call_callback(:AcceptCallback, sock) + block ? block.call(sock) : run(sock) + rescue Errno::ENOTCONN + @logger.debug "Errno::ENOTCONN raised" + rescue ServerError => ex + msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}" + @logger.error msg + rescue Exception => ex + @logger.error ex + ensure + @tokens.push(nil) + Thread.current[:WEBrickSocket] = nil + if addr + @logger.debug "close: #{addr[3]}:#{addr[1]}" + else + @logger.debug "close:
    " + end + sock.close + end + } + end + + def call_callback(callback_name, *args) + if cb = @config[callback_name] + cb.call(*args) + end + end + end # end of GenericServer +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/ssl.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/ssl.rb new file mode 100644 index 0000000000..4c71c879cf --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/ssl.rb @@ -0,0 +1,126 @@ +# +# ssl.rb -- SSL/TLS enhancement for GenericServer +# +# Copyright (c) 2003 GOTOU Yuuzou All rights reserved. +# +# $Id: ssl.rb 11708 2007-02-12 23:01:19Z shyouhei $ + +require 'webrick' +require 'openssl' + +module WEBrick + module Config + svrsoft = General[:ServerSoftware] + osslv = ::OpenSSL::OPENSSL_VERSION.split[1] + SSL = { + :ServerSoftware => "#{svrsoft} OpenSSL/#{osslv}", + :SSLEnable => false, + :SSLCertificate => nil, + :SSLPrivateKey => nil, + :SSLClientCA => nil, + :SSLExtraChainCert => nil, + :SSLCACertificateFile => nil, + :SSLCACertificatePath => nil, + :SSLCertificateStore => nil, + :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE, + :SSLVerifyDepth => nil, + :SSLVerifyCallback => nil, # custom verification + :SSLTimeout => nil, + :SSLOptions => nil, + :SSLStartImmediately => true, + # Must specify if you use auto generated certificate. + :SSLCertName => nil, + :SSLCertComment => "Generated by Ruby/OpenSSL" + } + General.update(SSL) + end + + module Utils + def create_self_signed_cert(bits, cn, comment) + rsa = OpenSSL::PKey::RSA.new(bits){|p, n| + case p + when 0; $stderr.putc "." # BN_generate_prime + when 1; $stderr.putc "+" # BN_generate_prime + when 2; $stderr.putc "*" # searching good prime, + # n = #of try, + # but also data from BN_generate_prime + when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q, + # but also data from BN_generate_prime + else; $stderr.putc "*" # BN_generate_prime + end + } + cert = OpenSSL::X509::Certificate.new + cert.version = 3 + cert.serial = 0 + name = OpenSSL::X509::Name.new(cn) + cert.subject = name + cert.issuer = name + cert.not_before = Time.now + cert.not_after = Time.now + (365*24*60*60) + cert.public_key = rsa.public_key + + ef = OpenSSL::X509::ExtensionFactory.new(nil,cert) + ef.issuer_certificate = cert + cert.extensions = [ + ef.create_extension("basicConstraints","CA:FALSE"), + ef.create_extension("keyUsage", "keyEncipherment"), + ef.create_extension("subjectKeyIdentifier", "hash"), + ef.create_extension("extendedKeyUsage", "serverAuth"), + ef.create_extension("nsComment", comment), + ] + aki = ef.create_extension("authorityKeyIdentifier", + "keyid:always,issuer:always") + cert.add_extension(aki) + cert.sign(rsa, OpenSSL::Digest::SHA1.new) + + return [ cert, rsa ] + end + module_function :create_self_signed_cert + end + + class GenericServer + def ssl_context + @ssl_context ||= nil + end + + def listen(address, port) + listeners = Utils::create_listeners(address, port, @logger) + if @config[:SSLEnable] + unless ssl_context + @ssl_context = setup_ssl_context(@config) + @logger.info("\n" + @config[:SSLCertificate].to_text) + end + listeners.collect!{|svr| + ssvr = ::OpenSSL::SSL::SSLServer.new(svr, ssl_context) + ssvr.start_immediately = @config[:SSLStartImmediately] + ssvr + } + end + @listeners += listeners + end + + def setup_ssl_context(config) + unless config[:SSLCertificate] + cn = config[:SSLCertName] + comment = config[:SSLCertComment] + cert, key = Utils::create_self_signed_cert(1024, cn, comment) + config[:SSLCertificate] = cert + config[:SSLPrivateKey] = key + end + ctx = OpenSSL::SSL::SSLContext.new + ctx.key = config[:SSLPrivateKey] + ctx.cert = config[:SSLCertificate] + ctx.client_ca = config[:SSLClientCA] + ctx.extra_chain_cert = config[:SSLExtraChainCert] + ctx.ca_file = config[:SSLCACertificateFile] + ctx.ca_path = config[:SSLCACertificatePath] + ctx.cert_store = config[:SSLCertificateStore] + ctx.verify_mode = config[:SSLVerifyClient] + ctx.verify_depth = config[:SSLVerifyDepth] + ctx.verify_callback = config[:SSLVerifyCallback] + ctx.timeout = config[:SSLTimeout] + ctx.options = config[:SSLOptions] + ctx + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/utils.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/utils.rb new file mode 100644 index 0000000000..cf9da6f2ce --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/utils.rb @@ -0,0 +1,100 @@ +# +# utils.rb -- Miscellaneous utilities +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: utils.rb,v 1.10 2003/02/16 22:22:54 gotoyuzo Exp $ + +require 'socket' +require 'fcntl' +begin + require 'etc' +rescue LoadError + nil +end + +module WEBrick + module Utils + def set_non_blocking(io) + flag = File::NONBLOCK + if defined?(Fcntl::F_GETFL) + flag |= io.fcntl(Fcntl::F_GETFL) + end + io.fcntl(Fcntl::F_SETFL, flag) + end + module_function :set_non_blocking + + def set_close_on_exec(io) + if defined?(Fcntl::FD_CLOEXEC) + io.fcntl(Fcntl::FD_CLOEXEC, 1) + end + end + module_function :set_close_on_exec + + def su(user) + if defined?(Etc) + pw = Etc.getpwnam(user) + Process::initgroups(user, pw.gid) + Process::Sys::setgid(pw.gid) + Process::Sys::setuid(pw.uid) + else + warn("WEBrick::Utils::su doesn't work on this platform") + end + end + module_function :su + + def getservername + host = Socket::gethostname + begin + Socket::gethostbyname(host)[0] + rescue + host + end + end + module_function :getservername + + def create_listeners(address, port, logger=nil) + unless port + raise ArgumentError, "must specify port" + end + res = Socket::getaddrinfo(address, port, + Socket::AF_UNSPEC, # address family + Socket::SOCK_STREAM, # socket type + 0, # protocol + Socket::AI_PASSIVE) # flag + last_error = nil + sockets = [] + res.each{|ai| + begin + logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger + sock = TCPServer.new(ai[3], port) + port = sock.addr[1] if port == 0 + Utils::set_close_on_exec(sock) + sockets << sock + rescue => ex + logger.warn("TCPServer Error: #{ex}") if logger + last_error = ex + end + } + raise last_error if sockets.empty? + return sockets + end + module_function :create_listeners + + RAND_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "0123456789" + + "abcdefghijklmnopqrstuvwxyz" + + def random_string(len) + rand_max = RAND_CHARS.size + ret = "" + len.times{ ret << RAND_CHARS[rand(rand_max)] } + ret + end + module_function :random_string + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/version.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/version.rb new file mode 100644 index 0000000000..b2b9fd3b78 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/webrick/version.rb @@ -0,0 +1,13 @@ +# +# version.rb -- version and release date +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU +# Copyright (c) 2003 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: version.rb,v 1.74 2003/07/22 19:20:43 gotoyuzo Exp $ + +module WEBrick + VERSION = "1.3.1" +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/binding.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/binding.rb new file mode 100644 index 0000000000..58a21d820d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/binding.rb @@ -0,0 +1,65 @@ +# WSDL4R - WSDL binding definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'xsd/namedelements' + + +module WSDL + + +class Binding < Info + attr_reader :name # required + attr_reader :type # required + attr_reader :operations + attr_reader :soapbinding + + def initialize + super + @name = nil + @type = nil + @operations = XSD::NamedElements.new + @soapbinding = nil + end + + def targetnamespace + parent.targetnamespace + end + + def parse_element(element) + case element + when OperationName + o = OperationBinding.new + @operations << o + o + when SOAPBindingName + o = WSDL::SOAP::Binding.new + @soapbinding = o + o + when DocumentationName + o = Documentation.new + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when NameAttrName + @name = XSD::QName.new(targetnamespace, value.source) + when TypeAttrName + @type = value + else + nil + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/data.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/data.rb new file mode 100644 index 0000000000..45eaad8526 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/data.rb @@ -0,0 +1,64 @@ +# WSDL4R - WSDL data definitions. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/qname' +require 'wsdl/documentation' +require 'wsdl/definitions' +require 'wsdl/types' +require 'wsdl/message' +require 'wsdl/part' +require 'wsdl/portType' +require 'wsdl/operation' +require 'wsdl/param' +require 'wsdl/binding' +require 'wsdl/operationBinding' +require 'wsdl/service' +require 'wsdl/port' +require 'wsdl/import' + + +module WSDL + + +ArrayTypeAttrName = XSD::QName.new(Namespace, 'arrayType') +BindingName = XSD::QName.new(Namespace, 'binding') +DefinitionsName = XSD::QName.new(Namespace, 'definitions') +DocumentationName = XSD::QName.new(Namespace, 'documentation') +FaultName = XSD::QName.new(Namespace, 'fault') +ImportName = XSD::QName.new(Namespace, 'import') +InputName = XSD::QName.new(Namespace, 'input') +MessageName = XSD::QName.new(Namespace, 'message') +OperationName = XSD::QName.new(Namespace, 'operation') +OutputName = XSD::QName.new(Namespace, 'output') +PartName = XSD::QName.new(Namespace, 'part') +PortName = XSD::QName.new(Namespace, 'port') +PortTypeName = XSD::QName.new(Namespace, 'portType') +ServiceName = XSD::QName.new(Namespace, 'service') +TypesName = XSD::QName.new(Namespace, 'types') + +SchemaName = XSD::QName.new(XSD::Namespace, 'schema') + +SOAPAddressName = XSD::QName.new(SOAPBindingNamespace, 'address') +SOAPBindingName = XSD::QName.new(SOAPBindingNamespace, 'binding') +SOAPHeaderName = XSD::QName.new(SOAPBindingNamespace, 'header') +SOAPBodyName = XSD::QName.new(SOAPBindingNamespace, 'body') +SOAPFaultName = XSD::QName.new(SOAPBindingNamespace, 'fault') +SOAPOperationName = XSD::QName.new(SOAPBindingNamespace, 'operation') + +BindingAttrName = XSD::QName.new(nil, 'binding') +ElementAttrName = XSD::QName.new(nil, 'element') +LocationAttrName = XSD::QName.new(nil, 'location') +MessageAttrName = XSD::QName.new(nil, 'message') +NameAttrName = XSD::QName.new(nil, 'name') +NamespaceAttrName = XSD::QName.new(nil, 'namespace') +ParameterOrderAttrName = XSD::QName.new(nil, 'parameterOrder') +TargetNamespaceAttrName = XSD::QName.new(nil, 'targetNamespace') +TypeAttrName = XSD::QName.new(nil, 'type') + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/definitions.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/definitions.rb new file mode 100644 index 0000000000..5235037cfe --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/definitions.rb @@ -0,0 +1,250 @@ +# WSDL4R - WSDL definitions. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'xsd/namedelements' + + +module WSDL + + +class Definitions < Info + attr_reader :name + attr_reader :targetnamespace + attr_reader :imports + + attr_accessor :location + attr_reader :importedschema + + def initialize + super + @name = nil + @targetnamespace = nil + @location = nil + @importedschema = {} + + @types = nil + @imports = [] + @messages = XSD::NamedElements.new + @porttypes = XSD::NamedElements.new + @bindings = XSD::NamedElements.new + @services = XSD::NamedElements.new + + @anontypes = XSD::NamedElements.new + @root = self + end + + def inspect + sprintf("#<%s:0x%x %s>", self.class.name, __id__, @name || '(unnamed)') + end + + def targetnamespace=(targetnamespace) + @targetnamespace = targetnamespace + if @name + @name = XSD::QName.new(@targetnamespace, @name.name) + end + end + + def collect_attributes + result = XSD::NamedElements.new + if @types + @types.schemas.each do |schema| + result.concat(schema.collect_attributes) + end + end + @imports.each do |import| + result.concat(import.content.collect_attributes) + end + result + end + + def collect_elements + result = XSD::NamedElements.new + if @types + @types.schemas.each do |schema| + result.concat(schema.collect_elements) + end + end + @imports.each do |import| + result.concat(import.content.collect_elements) + end + result + end + + def collect_complextypes + result = @anontypes.dup + if @types + @types.schemas.each do |schema| + result.concat(schema.collect_complextypes) + end + end + @imports.each do |import| + result.concat(import.content.collect_complextypes) + end + result + end + + def collect_simpletypes + result = XSD::NamedElements.new + if @types + @types.schemas.each do |schema| + result.concat(schema.collect_simpletypes) + end + end + @imports.each do |import| + result.concat(import.content.collect_simpletypes) + end + result + end + + # ToDo: simpletype must be accepted... + def add_type(complextype) + @anontypes << complextype + end + + def messages + result = @messages.dup + @imports.each do |import| + result.concat(import.content.messages) if self.class === import.content + end + result + end + + def porttypes + result = @porttypes.dup + @imports.each do |import| + result.concat(import.content.porttypes) if self.class === import.content + end + result + end + + def bindings + result = @bindings.dup + @imports.each do |import| + result.concat(import.content.bindings) if self.class === import.content + end + result + end + + def services + result = @services.dup + @imports.each do |import| + result.concat(import.content.services) if self.class === import.content + end + result + end + + def message(name) + message = @messages[name] + return message if message + @imports.each do |import| + message = import.content.message(name) if self.class === import.content + return message if message + end + nil + end + + def porttype(name) + porttype = @porttypes[name] + return porttype if porttype + @imports.each do |import| + porttype = import.content.porttype(name) if self.class === import.content + return porttype if porttype + end + nil + end + + def binding(name) + binding = @bindings[name] + return binding if binding + @imports.each do |import| + binding = import.content.binding(name) if self.class === import.content + return binding if binding + end + nil + end + + def service(name) + service = @services[name] + return service if service + @imports.each do |import| + service = import.content.service(name) if self.class === import.content + return service if service + end + nil + end + + def porttype_binding(name) + binding = @bindings.find { |item| item.type == name } + return binding if binding + @imports.each do |import| + binding = import.content.porttype_binding(name) if self.class === import.content + return binding if binding + end + nil + end + + def parse_element(element) + case element + when ImportName + o = Import.new + @imports << o + o + when TypesName + o = Types.new + @types = o + o + when MessageName + o = Message.new + @messages << o + o + when PortTypeName + o = PortType.new + @porttypes << o + o + when BindingName + o = Binding.new + @bindings << o + o + when ServiceName + o = Service.new + @services << o + o + when DocumentationName + o = Documentation.new + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when NameAttrName + @name = XSD::QName.new(targetnamespace, value.source) + when TargetNamespaceAttrName + self.targetnamespace = value.source + else + nil + end + end + + def self.parse_element(element) + if element == DefinitionsName + Definitions.new + else + nil + end + end + +private + +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/documentation.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/documentation.rb new file mode 100644 index 0000000000..3a7fd7d23e --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/documentation.rb @@ -0,0 +1,32 @@ +# WSDL4R - WSDL SOAP documentation element. +# Copyright (C) 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL + + +class Documentation < Info + def initialize + super + end + + def parse_element(element) + # Accepts any element. + self + end + + def parse_attr(attr, value) + # Accepts any attribute. + true + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/import.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/import.rb new file mode 100644 index 0000000000..faf60871a5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/import.rb @@ -0,0 +1,80 @@ +# WSDL4R - WSDL import definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'wsdl/importer' + + +module WSDL + + +class Import < Info + attr_reader :namespace + attr_reader :location + attr_reader :content + + def initialize + super + @namespace = nil + @location = nil + @content = nil + @web_client = nil + end + + def parse_element(element) + case element + when DocumentationName + o = Documentation.new + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when NamespaceAttrName + @namespace = value.source + if @content + @content.targetnamespace = @namespace + end + @namespace + when LocationAttrName + @location = URI.parse(value.source) + if @location.relative? and !parent.location.nil? and + !parent.location.relative? + @location = parent.location + @location + end + if root.importedschema.key?(@location) + @content = root.importedschema[@location] + else + root.importedschema[@location] = nil # placeholder + @content = import(@location) + if @content.is_a?(Definitions) + @content.root = root + if @namespace + @content.targetnamespace = @namespace + end + end + root.importedschema[@location] = @content + end + @location + else + nil + end + end + +private + + def import(location) + Importer.import(location, root) + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/importer.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/importer.rb new file mode 100644 index 0000000000..481bd81b25 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/importer.rb @@ -0,0 +1,38 @@ +# WSDL4R - WSDL importer library. +# Copyright (C) 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/xmlSchema/importer' +require 'wsdl/parser' + + +module WSDL + + +class Importer < WSDL::XMLSchema::Importer + def self.import(location, originalroot = nil) + new.import(location, originalroot) + end + +private + + def parse(content, location, originalroot) + opt = { + :location => location, + :originalroot => originalroot + } + begin + WSDL::Parser.new(opt).parse(content) + rescue WSDL::Parser::ParseError + super(content, location, originalroot) + end + end + +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/info.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/info.rb new file mode 100644 index 0000000000..ffd90390a4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/info.rb @@ -0,0 +1,39 @@ +# WSDL4R - WSDL information base. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +module WSDL + + +class Info + attr_accessor :root + attr_accessor :parent + attr_accessor :id + + def initialize + @root = nil + @parent = nil + @id = nil + end + + def inspect + if self.respond_to?(:name) + sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.name) + else + sprintf("#<%s:0x%x>", self.class.name, __id__) + end + end + + def parse_element(element); end # abstract + + def parse_attr(attr, value); end # abstract + + def parse_epilogue; end # abstract +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/message.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/message.rb new file mode 100644 index 0000000000..cecc602da3 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/message.rb @@ -0,0 +1,54 @@ +# WSDL4R - WSDL message definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL + + +class Message < Info + attr_reader :name # required + attr_reader :parts + + def initialize + super + @name = nil + @parts = [] + end + + def targetnamespace + parent.targetnamespace + end + + def parse_element(element) + case element + when PartName + o = Part.new + @parts << o + o + when DocumentationName + o = Documentation.new + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when NameAttrName + @name = XSD::QName.new(parent.targetnamespace, value.source) + else + nil + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/operation.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/operation.rb new file mode 100644 index 0000000000..fb7f4a80f4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/operation.rb @@ -0,0 +1,130 @@ +# WSDL4R - WSDL operation definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL + + +class Operation < Info + class NameInfo + attr_reader :op_name + attr_reader :optype_name + attr_reader :parts + def initialize(op_name, optype_name, parts) + @op_name = op_name + @optype_name = optype_name + @parts = parts + end + end + + attr_reader :name # required + attr_reader :parameter_order # optional + attr_reader :input + attr_reader :output + attr_reader :fault + attr_reader :type # required + + def initialize + super + @name = nil + @type = nil + @parameter_order = nil + @input = nil + @output = nil + @fault = [] + end + + def targetnamespace + parent.targetnamespace + end + + def input_info + typename = input.find_message.name + NameInfo.new(@name, typename, inputparts) + end + + def output_info + typename = output.find_message.name + NameInfo.new(@name, typename, outputparts) + end + + def inputparts + sort_parts(input.find_message.parts) + end + + def inputname + XSD::QName.new(targetnamespace, input.name ? input.name.name : @name.name) + end + + def outputparts + sort_parts(output.find_message.parts) + end + + def outputname + XSD::QName.new(targetnamespace, + output.name ? output.name.name : @name.name + 'Response') + end + + def parse_element(element) + case element + when InputName + o = Param.new + @input = o + o + when OutputName + o = Param.new + @output = o + o + when FaultName + o = Param.new + @fault << o + o + when DocumentationName + o = Documentation.new + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when NameAttrName + @name = XSD::QName.new(targetnamespace, value.source) + when TypeAttrName + @type = value + when ParameterOrderAttrName + @parameter_order = value.source.split(/\s+/) + else + nil + end + end + +private + + def sort_parts(parts) + return parts.dup unless parameter_order + result = [] + parameter_order.each do |orderitem| + if (ele = parts.find { |part| part.name == orderitem }) + result << ele + end + end + if result.length == 0 + return parts.dup + end + # result length can be shorter than parts's. + # return part must not be a part of the parameterOrder. + result + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/operationBinding.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/operationBinding.rb new file mode 100644 index 0000000000..c2b8cd6591 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/operationBinding.rb @@ -0,0 +1,108 @@ +# WSDL4R - WSDL bound operation definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL + + +class OperationBinding < Info + attr_reader :name # required + attr_reader :input + attr_reader :output + attr_reader :fault + attr_reader :soapoperation + + def initialize + super + @name = nil + @input = nil + @output = nil + @fault = [] + @soapoperation = nil + end + + def targetnamespace + parent.targetnamespace + end + + def porttype + root.porttype(parent.type) + end + + def find_operation + porttype.operations[@name] or raise RuntimeError.new("#{@name} not found") + end + + def soapoperation_name + if @soapoperation + @soapoperation.input_info.op_name + else + find_operation.name + end + end + + def soapoperation_style + style = nil + if @soapoperation + style = @soapoperation.operation_style + elsif parent.soapbinding + style = parent.soapbinding.style + else + raise TypeError.new("operation style definition not found") + end + style || :document + end + + def soapaction + if @soapoperation + @soapoperation.soapaction + else + nil + end + end + + def parse_element(element) + case element + when InputName + o = Param.new + @input = o + o + when OutputName + o = Param.new + @output = o + o + when FaultName + o = Param.new + @fault << o + o + when SOAPOperationName + o = WSDL::SOAP::Operation.new + @soapoperation = o + o + when DocumentationName + o = Documentation.new + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when NameAttrName + @name = XSD::QName.new(targetnamespace, value.source) + else + nil + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/param.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/param.rb new file mode 100644 index 0000000000..b6836b7def --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/param.rb @@ -0,0 +1,85 @@ +# WSDL4R - WSDL param definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL + + +class Param < Info + attr_reader :message # required + attr_reader :name # optional but required for fault. + attr_reader :soapbody + attr_reader :soapheader + attr_reader :soapfault + + def initialize + super + @message = nil + @name = nil + @soapbody = nil + @soapheader = [] + @soapfault = nil + end + + def targetnamespace + parent.targetnamespace + end + + def find_message + root.message(@message) or raise RuntimeError.new("#{@message} not found") + end + + def soapbody_use + if @soapbody + @soapbody.use || :literal + else + raise RuntimeError.new("soap:body not found") + end + end + + def parse_element(element) + case element + when SOAPBodyName + o = WSDL::SOAP::Body.new + @soapbody = o + o + when SOAPHeaderName + o = WSDL::SOAP::Header.new + @soapheader << o + o + when SOAPFaultName + o = WSDL::SOAP::Fault.new + @soap_fault = o + o + when DocumentationName + o = Documentation.new + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when MessageAttrName + if value.namespace.nil? + value = XSD::QName.new(targetnamespace, value.source) + end + @message = value + when NameAttrName + @name = XSD::QName.new(targetnamespace, value.source) + else + nil + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/parser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/parser.rb new file mode 100644 index 0000000000..f96e96ee2a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/parser.rb @@ -0,0 +1,163 @@ +# WSDL4R - WSDL XML Instance parser library. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/qname' +require 'xsd/ns' +require 'xsd/charset' +require 'xsd/datatypes' +require 'xsd/xmlparser' +require 'wsdl/wsdl' +require 'wsdl/data' +require 'wsdl/xmlSchema/data' +require 'wsdl/soap/data' + + +module WSDL + + +class Parser + include WSDL + + class ParseError < Error; end + class FormatDecodeError < ParseError; end + class UnknownElementError < FormatDecodeError; end + class UnknownAttributeError < FormatDecodeError; end + class UnexpectedElementError < FormatDecodeError; end + class ElementConstraintError < FormatDecodeError; end + class AttributeConstraintError < FormatDecodeError; end + +private + + class ParseFrame + attr_reader :ns + attr_reader :name + attr_accessor :node + + private + + def initialize(ns, name, node) + @ns = ns + @name = name + @node = node + end + end + +public + + def initialize(opt = {}) + @parser = XSD::XMLParser.create_parser(self, opt) + @parsestack = nil + @lastnode = nil + @ignored = {} + @location = opt[:location] + @originalroot = opt[:originalroot] + end + + def parse(string_or_readable) + @parsestack = [] + @lastnode = nil + @textbuf = '' + @parser.do_parse(string_or_readable) + @lastnode + end + + def charset + @parser.charset + end + + def start_element(name, attrs) + lastframe = @parsestack.last + ns = parent = nil + if lastframe + ns = lastframe.ns.clone_ns + parent = lastframe.node + else + ns = XSD::NS.new + parent = nil + end + attrs = XSD::XMLParser.filter_ns(ns, attrs) + node = decode_tag(ns, name, attrs, parent) + @parsestack << ParseFrame.new(ns, name, node) + end + + def characters(text) + lastframe = @parsestack.last + if lastframe + # Need not to be cloned because character does not have attr. + ns = lastframe.ns + decode_text(ns, text) + else + p text if $DEBUG + end + end + + def end_element(name) + lastframe = @parsestack.pop + unless name == lastframe.name + raise UnexpectedElementError.new("closing element name '#{name}' does not match with opening element '#{lastframe.name}'") + end + decode_tag_end(lastframe.ns, lastframe.node) + @lastnode = lastframe.node + end + +private + + def decode_tag(ns, name, attrs, parent) + o = nil + elename = ns.parse(name) + if !parent + if elename == DefinitionsName + o = Definitions.parse_element(elename) + o.location = @location + else + raise UnknownElementError.new("unknown element: #{elename}") + end + o.root = @originalroot if @originalroot # o.root = o otherwise + else + if elename == XMLSchema::AnnotationName + # only the first annotation element is allowed for each xsd element. + o = XMLSchema::Annotation.new + else + o = parent.parse_element(elename) + end + unless o + unless @ignored.key?(elename) + warn("ignored element: #{elename}") + @ignored[elename] = elename + end + o = Documentation.new # which accepts any element. + end + # node could be a pseudo element. pseudo element has its own parent. + o.root = parent.root + o.parent = parent if o.parent.nil? + end + attrs.each do |key, value| + attr_ele = ns.parse(key, true) + value_ele = ns.parse(value, true) + value_ele.source = value # for recovery; value may not be a QName + unless o.parse_attr(attr_ele, value_ele) + unless @ignored.key?(attr_ele) + warn("ignored attr: #{attr_ele}") + @ignored[attr_ele] = attr_ele + end + end + end + o + end + + def decode_tag_end(ns, node) + node.parse_epilogue + end + + def decode_text(ns, text) + @textbuf << text + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/part.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/part.rb new file mode 100644 index 0000000000..5dafd4ee73 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/part.rb @@ -0,0 +1,52 @@ +# WSDL4R - WSDL part definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL + + +class Part < Info + attr_reader :name # required + attr_reader :element # optional + attr_reader :type # optional + + def initialize + super + @name = nil + @element = nil + @type = nil + end + + def parse_element(element) + case element + when DocumentationName + o = Documentation.new + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when NameAttrName + @name = value.source + when ElementAttrName + @element = value + when TypeAttrName + @type = value + else + nil + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/port.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/port.rb new file mode 100644 index 0000000000..883cc3232d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/port.rb @@ -0,0 +1,84 @@ +# WSDL4R - WSDL port definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL + + +class Port < Info + attr_reader :name # required + attr_reader :binding # required + attr_reader :soap_address + + def initialize + super + @name = nil + @binding = nil + @soap_address = nil + end + + def targetnamespace + parent.targetnamespace + end + + def porttype + root.porttype(find_binding.type) + end + + def find_binding + root.binding(@binding) or raise RuntimeError.new("#{@binding} not found") + end + + def inputoperation_map + result = {} + find_binding.operations.each do |op_bind| + op_info = op_bind.soapoperation.input_info + result[op_info.op_name] = op_info + end + result + end + + def outputoperation_map + result = {} + find_binding.operations.each do |op_bind| + op_info = op_bind.soapoperation.output_info + result[op_info.op_name] = op_info + end + result + end + + def parse_element(element) + case element + when SOAPAddressName + o = WSDL::SOAP::Address.new + @soap_address = o + o + when DocumentationName + o = Documentation.new + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when NameAttrName + @name = XSD::QName.new(targetnamespace, value.source) + when BindingAttrName + @binding = value + else + nil + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/portType.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/portType.rb new file mode 100644 index 0000000000..03e37690a8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/portType.rb @@ -0,0 +1,73 @@ +# WSDL4R - WSDL portType definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'xsd/namedelements' + + +module WSDL + + +class PortType < Info + attr_reader :name # required + attr_reader :operations + + def targetnamespace + parent.targetnamespace + end + + def initialize + super + @name = nil + @operations = XSD::NamedElements.new + end + + def find_binding + root.bindings.find { |item| item.type == @name } or + raise RuntimeError.new("#{@name} not found") + end + + def locations + bind_name = find_binding.name + result = [] + root.services.each do |service| + service.ports.each do |port| + if port.binding == bind_name + result << port.soap_address.location if port.soap_address + end + end + end + result + end + + def parse_element(element) + case element + when OperationName + o = Operation.new + @operations << o + o + when DocumentationName + o = Documentation.new + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when NameAttrName + @name = XSD::QName.new(targetnamespace, value.source) + else + nil + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/service.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/service.rb new file mode 100644 index 0000000000..652b127331 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/service.rb @@ -0,0 +1,61 @@ +# WSDL4R - WSDL service definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'xsd/namedelements' + + +module WSDL + + +class Service < Info + attr_reader :name # required + attr_reader :ports + attr_reader :soap_address + + def initialize + super + @name = nil + @ports = XSD::NamedElements.new + @soap_address = nil + end + + def targetnamespace + parent.targetnamespace + end + + def parse_element(element) + case element + when PortName + o = Port.new + @ports << o + o + when SOAPAddressName + o = WSDL::SOAP::Address.new + @soap_address = o + o + when DocumentationName + o = Documentation.new + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when NameAttrName + @name = XSD::QName.new(targetnamespace, value.source) + else + nil + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/address.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/address.rb new file mode 100644 index 0000000000..0b2b59caf0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/address.rb @@ -0,0 +1,40 @@ +# WSDL4R - WSDL SOAP address definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module SOAP + + +class Address < Info + attr_reader :location + + def initialize + super + @location = nil + end + + def parse_element(element) + nil + end + + def parse_attr(attr, value) + case attr + when LocationAttrName + @location = value.source + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/binding.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/binding.rb new file mode 100644 index 0000000000..7e15a99701 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/binding.rb @@ -0,0 +1,49 @@ +# WSDL4R - WSDL SOAP binding definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module SOAP + + +class Binding < Info + attr_reader :style + attr_reader :transport + + def initialize + super + @style = nil + @transport = nil + end + + def parse_element(element) + nil + end + + def parse_attr(attr, value) + case attr + when StyleAttrName + if ["document", "rpc"].include?(value.source) + @style = value.source.intern + else + raise Parser::AttributeConstraintError.new( + "Unexpected value #{ value }.") + end + when TransportAttrName + @transport = value.source + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/body.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/body.rb new file mode 100644 index 0000000000..824f8121a2 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/body.rb @@ -0,0 +1,56 @@ +# WSDL4R - WSDL SOAP body definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module SOAP + + +class Body < Info + attr_reader :parts + attr_reader :use # required + attr_reader :encodingstyle + attr_reader :namespace + + def initialize + super + @parts = nil + @use = nil + @encodingstyle = nil + @namespace = nil + end + + def parse_element(element) + nil + end + + def parse_attr(attr, value) + case attr + when PartsAttrName + @parts = value.source + when UseAttrName + if ['literal', 'encoded'].include?(value.source) + @use = value.source.intern + else + raise RuntimeError.new("unknown use of soap:body: #{value.source}") + end + when EncodingStyleAttrName + @encodingstyle = value.source + when NamespaceAttrName + @namespace = value.source + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/cgiStubCreator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/cgiStubCreator.rb new file mode 100644 index 0000000000..2c4dff2f62 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/cgiStubCreator.rb @@ -0,0 +1,76 @@ +# WSDL4R - Creating CGI stub code from WSDL. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'wsdl/soap/mappingRegistryCreator' +require 'wsdl/soap/methodDefCreator' +require 'wsdl/soap/classDefCreatorSupport' + + +module WSDL +module SOAP + + +class CGIStubCreator + include ClassDefCreatorSupport + + attr_reader :definitions + + def initialize(definitions) + @definitions = definitions + end + + def dump(service_name) + warn("CGI stub can have only 1 port. Creating stub for the first port... Rests are ignored.") + port = @definitions.service(service_name).ports[0] + dump_porttype(port.porttype.name) + end + +private + + def dump_porttype(name) + class_name = create_class_name(name) + methoddef, types = MethodDefCreator.new(@definitions).dump(name) + mr_creator = MappingRegistryCreator.new(@definitions) + c1 = XSD::CodeGen::ClassDef.new(class_name) + c1.def_require("soap/rpc/cgistub") + c1.def_require("soap/mapping/registry") + c1.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new") + c1.def_code(mr_creator.dump(types)) + c1.def_code <<-EOD +Methods = [ +#{methoddef.gsub(/^/, " ")} +] + EOD + c2 = XSD::CodeGen::ClassDef.new(class_name + "App", + "::SOAP::RPC::CGIStub") + c2.def_method("initialize", "*arg") do + <<-EOD + super(*arg) + servant = #{class_name}.new + #{class_name}::Methods.each do |definitions| + opt = definitions.last + if opt[:request_style] == :document + @router.add_document_operation(servant, *definitions) + else + @router.add_rpc_operation(servant, *definitions) + end + end + self.mapping_registry = #{class_name}::MappingRegistry + self.level = Logger::Severity::ERROR + EOD + end + c1.dump + "\n" + c2.dump + format(<<-EOD) + #{class_name}App.new('app', nil).start + EOD + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/classDefCreator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/classDefCreator.rb new file mode 100644 index 0000000000..aeb67c0613 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/classDefCreator.rb @@ -0,0 +1,314 @@ +# WSDL4R - Creating class definition from WSDL +# Copyright (C) 2002, 2003, 2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/data' +require 'wsdl/soap/classDefCreatorSupport' +require 'xsd/codegen' + + +module WSDL +module SOAP + + +class ClassDefCreator + include ClassDefCreatorSupport + + def initialize(definitions) + @elements = definitions.collect_elements + @simpletypes = definitions.collect_simpletypes + @complextypes = definitions.collect_complextypes + @faulttypes = nil + if definitions.respond_to?(:collect_faulttypes) + @faulttypes = definitions.collect_faulttypes + end + end + + def dump(type = nil) + result = "require 'xsd/qname'\n" + if type + result = dump_classdef(type.name, type) + else + str = dump_element + unless str.empty? + result << "\n" unless result.empty? + result << str + end + str = dump_complextype + unless str.empty? + result << "\n" unless result.empty? + result << str + end + str = dump_simpletype + unless str.empty? + result << "\n" unless result.empty? + result << str + end + end + result + end + +private + + def dump_element + @elements.collect { |ele| + if ele.local_complextype + dump_classdef(ele.name, ele.local_complextype, + ele.elementform == 'qualified') + elsif ele.local_simpletype + dump_simpletypedef(ele.name, ele.local_simpletype) + else + nil + end + }.compact.join("\n") + end + + def dump_simpletype + @simpletypes.collect { |type| + dump_simpletypedef(type.name, type) + }.compact.join("\n") + end + + def dump_complextype + @complextypes.collect { |type| + case type.compoundtype + when :TYPE_STRUCT, :TYPE_EMPTY + dump_classdef(type.name, type) + when :TYPE_ARRAY + dump_arraydef(type) + when :TYPE_SIMPLE + dump_simpleclassdef(type) + when :TYPE_MAP + # mapped as a general Hash + nil + else + raise RuntimeError.new( + "unknown kind of complexContent: #{type.compoundtype}") + end + }.compact.join("\n") + end + + def dump_simpletypedef(qname, simpletype) + if !simpletype.restriction or simpletype.restriction.enumeration.empty? + return nil + end + c = XSD::CodeGen::ModuleDef.new(create_class_name(qname)) + c.comment = "#{qname}" + const = {} + simpletype.restriction.enumeration.each do |value| + constname = safeconstname(value) + const[constname] ||= 0 + if (const[constname] += 1) > 1 + constname += "_#{const[constname]}" + end + c.def_const(constname, ndq(value)) + end + c.dump + end + + def dump_simpleclassdef(type_or_element) + qname = type_or_element.name + base = create_class_name(type_or_element.simplecontent.base) + c = XSD::CodeGen::ClassDef.new(create_class_name(qname), base) + c.comment = "#{qname}" + c.dump + end + + def dump_classdef(qname, typedef, qualified = false) + if @faulttypes and @faulttypes.index(qname) + c = XSD::CodeGen::ClassDef.new(create_class_name(qname), + '::StandardError') + else + c = XSD::CodeGen::ClassDef.new(create_class_name(qname)) + end + c.comment = "#{qname}" + c.def_classvar('schema_type', ndq(qname.name)) + c.def_classvar('schema_ns', ndq(qname.namespace)) + c.def_classvar('schema_qualified', dq('true')) if qualified + schema_element = [] + init_lines = '' + params = [] + typedef.each_element do |element| + if element.type == XSD::AnyTypeName + type = nil + elsif klass = element_basetype(element) + type = klass.name + elsif element.type + type = create_class_name(element.type) + else + type = nil # means anyType. + # do we define a class for local complexType from it's name? + # type = create_class_name(element.name) + # + # + # + # + # + end + name = name_element(element).name + attrname = safemethodname?(name) ? name : safemethodname(name) + varname = safevarname(name) + c.def_attr(attrname, true, varname) + init_lines << "@#{varname} = #{varname}\n" + if element.map_as_array? + params << "#{varname} = []" + type << '[]' if type + else + params << "#{varname} = nil" + end + # nil means @@schema_ns + varname + eleqname = + (varname == name && element.name.namespace == qname.namespace) ? + nil : element.name + schema_element << [varname, eleqname, type] + end + unless typedef.attributes.empty? + define_attribute(c, typedef.attributes) + init_lines << "@__xmlattr = {}\n" + end + c.def_classvar('schema_element', + '[' + + schema_element.collect { |varname, name, type| + '[' + + ( + if name + varname.dump + ', [' + ndq(type) + ', ' + dqname(name) + ']' + else + varname.dump + ', ' + ndq(type) + end + ) + + ']' + }.join(', ') + + ']' + ) + c.def_method('initialize', *params) do + init_lines + end + c.dump + end + + def element_basetype(ele) + if klass = basetype_class(ele.type) + klass + elsif ele.local_simpletype + basetype_class(ele.local_simpletype.base) + else + nil + end + end + + def attribute_basetype(attr) + if klass = basetype_class(attr.type) + klass + elsif attr.local_simpletype + basetype_class(attr.local_simpletype.base) + else + nil + end + end + + def basetype_class(type) + return nil if type.nil? + if simpletype = @simpletypes[type] + basetype_mapped_class(simpletype.base) + else + basetype_mapped_class(type) + end + end + + def define_attribute(c, attributes) + schema_attribute = [] + attributes.each do |attribute| + name = name_attribute(attribute) + if klass = attribute_basetype(attribute) + type = klass.name + else + type = nil + end + methodname = safemethodname('xmlattr_' + name.name) + c.def_method(methodname) do <<-__EOD__ + (@__xmlattr ||= {})[#{dqname(name)}] + __EOD__ + end + c.def_method(methodname + '=', 'value') do <<-__EOD__ + (@__xmlattr ||= {})[#{dqname(name)}] = value + __EOD__ + end + schema_attribute << [name, type] + end + c.def_classvar('schema_attribute', + '{' + + schema_attribute.collect { |name, type| + dqname(name) + ' => ' + ndq(type) + }.join(', ') + + '}' + ) + end + + def name_element(element) + return element.name if element.name + return element.ref if element.ref + raise RuntimeError.new("cannot define name of #{element}") + end + + def name_attribute(attribute) + return attribute.name if attribute.name + return attribute.ref if attribute.ref + raise RuntimeError.new("cannot define name of #{attribute}") + end + + DEFAULT_ITEM_NAME = XSD::QName.new(nil, 'item') + + def dump_arraydef(complextype) + qname = complextype.name + c = XSD::CodeGen::ClassDef.new(create_class_name(qname), '::Array') + c.comment = "#{qname}" + child_type = complextype.child_type + c.def_classvar('schema_type', ndq(child_type.name)) + c.def_classvar('schema_ns', ndq(child_type.namespace)) + child_element = complextype.find_aryelement + schema_element = [] + if child_type == XSD::AnyTypeName + type = nil + elsif child_element and (klass = element_basetype(child_element)) + type = klass.name + elsif child_type + type = create_class_name(child_type) + else + type = nil + end + if child_element + if child_element.map_as_array? + type << '[]' if type + end + child_element_name = child_element.name + else + child_element_name = DEFAULT_ITEM_NAME + end + schema_element << [child_element_name.name, child_element_name, type] + c.def_classvar('schema_element', + '[' + + schema_element.collect { |varname, name, type| + '[' + + ( + if name + varname.dump + ', [' + ndq(type) + ', ' + dqname(name) + ']' + else + varname.dump + ', ' + ndq(type) + end + ) + + ']' + }.join(', ') + + ']' + ) + c.dump + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/classDefCreatorSupport.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/classDefCreatorSupport.rb new file mode 100644 index 0000000000..8f335653c8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/classDefCreatorSupport.rb @@ -0,0 +1,126 @@ +# WSDL4R - Creating class code support from WSDL. +# Copyright (C) 2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'soap/mapping' +require 'soap/mapping/typeMap' +require 'xsd/codegen/gensupport' + + +module WSDL +module SOAP + + +module ClassDefCreatorSupport + include XSD::CodeGen::GenSupport + + def create_class_name(qname) + if klass = basetype_mapped_class(qname) + ::SOAP::Mapping::DefaultRegistry.find_mapped_obj_class(klass).name + else + safeconstname(qname.name) + end + end + + def basetype_mapped_class(name) + ::SOAP::TypeMap[name] + end + + def dump_method_signature(operation) + name = operation.name.name + input = operation.input + output = operation.output + fault = operation.fault + signature = "#{ name }#{ dump_inputparam(input) }" + str = <<__EOD__ +# SYNOPSIS +# #{name}#{dump_inputparam(input)} +# +# ARGS +#{dump_inout_type(input).chomp} +# +# RETURNS +#{dump_inout_type(output).chomp} +# +__EOD__ + unless fault.empty? + faultstr = (fault.collect { |f| dump_inout_type(f).chomp }).join(', ') + str <<<<__EOD__ +# RAISES +# #{faultstr} +# +__EOD__ + end + str + end + + def dq(ele) + ele.dump + end + + def ndq(ele) + ele.nil? ? 'nil' : dq(ele) + end + + def sym(ele) + ':' + ele + end + + def dqname(qname) + qname.dump + end + +private + + def dump_inout_type(param) + if param + message = param.find_message + params = "" + message.parts.each do |part| + name = safevarname(part.name) + if part.type + typename = safeconstname(part.type.name) + params << add_at("# #{name}", "#{typename} - #{part.type}\n", 20) + elsif part.element + typename = safeconstname(part.element.name) + params << add_at("# #{name}", "#{typename} - #{part.element}\n", 20) + end + end + unless params.empty? + return params + end + end + "# N/A\n" + end + + def dump_inputparam(input) + message = input.find_message + params = "" + message.parts.each do |part| + params << ", " unless params.empty? + params << safevarname(part.name) + end + if params.empty? + "" + else + "(#{ params })" + end + end + + def add_at(base, str, pos) + if base.size >= pos + base + ' ' + str + else + base + ' ' * (pos - base.size) + str + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/clientSkeltonCreator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/clientSkeltonCreator.rb new file mode 100644 index 0000000000..916f0d4dc0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/clientSkeltonCreator.rb @@ -0,0 +1,78 @@ +# WSDL4R - Creating client skelton code from WSDL. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'wsdl/soap/classDefCreatorSupport' + + +module WSDL +module SOAP + + +class ClientSkeltonCreator + include ClassDefCreatorSupport + + attr_reader :definitions + + def initialize(definitions) + @definitions = definitions + end + + def dump(service_name) + result = "" + @definitions.service(service_name).ports.each do |port| + result << dump_porttype(port.porttype.name) + result << "\n" + end + result + end + +private + + def dump_porttype(name) + drv_name = create_class_name(name) + + result = "" + result << <<__EOD__ +endpoint_url = ARGV.shift +obj = #{ drv_name }.new(endpoint_url) + +# run ruby with -d to see SOAP wiredumps. +obj.wiredump_dev = STDERR if $DEBUG + +__EOD__ + @definitions.porttype(name).operations.each do |operation| + result << dump_method_signature(operation) + result << dump_input_init(operation.input) << "\n" + result << dump_operation(operation) << "\n\n" + end + result + end + + def dump_operation(operation) + name = operation.name + input = operation.input + "puts obj.#{ safemethodname(name.name) }#{ dump_inputparam(input) }" + end + + def dump_input_init(input) + result = input.find_message.parts.collect { |part| + safevarname(part.name) + }.join(" = ") + if result.empty? + "" + else + result << " = nil" + end + result + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/complexType.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/complexType.rb new file mode 100644 index 0000000000..b2e13d0564 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/complexType.rb @@ -0,0 +1,161 @@ +# WSDL4R - SOAP complexType definition for WSDL. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/xmlSchema/complexType' +require 'soap/mapping' + + +module WSDL +module XMLSchema + + +class ComplexType < Info + def compoundtype + @compoundtype ||= check_type + end + + def check_type + if content + if attributes.empty? and + content.elements.size == 1 and content.elements[0].maxoccurs != '1' + if name == ::SOAP::Mapping::MapQName + :TYPE_MAP + else + :TYPE_ARRAY + end + else + :TYPE_STRUCT + end + elsif complexcontent + if complexcontent.base == ::SOAP::ValueArrayName + :TYPE_ARRAY + else + complexcontent.basetype.check_type + end + elsif simplecontent + :TYPE_SIMPLE + elsif !attributes.empty? + :TYPE_STRUCT + else # empty complexType definition (seen in partner.wsdl of salesforce) + :TYPE_EMPTY + end + end + + def child_type(name = nil) + case compoundtype + when :TYPE_STRUCT + if ele = find_element(name) + ele.type + elsif ele = find_element_by_name(name.name) + ele.type + end + when :TYPE_ARRAY + @contenttype ||= content_arytype + when :TYPE_MAP + item_ele = find_element_by_name("item") or + raise RuntimeError.new("'item' element not found in Map definition.") + content = item_ele.local_complextype or + raise RuntimeError.new("No complexType definition for 'item'.") + if ele = content.find_element(name) + ele.type + elsif ele = content.find_element_by_name(name.name) + ele.type + end + else + raise NotImplementedError.new("Unknown kind of complexType.") + end + end + + def child_defined_complextype(name) + ele = nil + case compoundtype + when :TYPE_STRUCT, :TYPE_MAP + unless ele = find_element(name) + if name.namespace.nil? + ele = find_element_by_name(name.name) + end + end + when :TYPE_ARRAY + if content.elements.size == 1 + ele = content.elements[0] + else + raise RuntimeError.new("Assert: must not reach.") + end + else + raise RuntimeError.new("Assert: Not implemented.") + end + unless ele + raise RuntimeError.new("Cannot find #{name} as a children of #{@name}.") + end + ele.local_complextype + end + + def find_arytype + unless compoundtype == :TYPE_ARRAY + raise RuntimeError.new("Assert: not for array") + end + if complexcontent + complexcontent.attributes.each do |attribute| + if attribute.ref == ::SOAP::AttrArrayTypeName + return attribute.arytype + end + end + if check_array_content(complexcontent.content) + return element_simpletype(complexcontent.content.elements[0]) + end + elsif check_array_content(content) + return element_simpletype(content.elements[0]) + end + raise RuntimeError.new("Assert: Unknown array definition.") + end + + def find_aryelement + unless compoundtype == :TYPE_ARRAY + raise RuntimeError.new("Assert: not for array") + end + if complexcontent + if check_array_content(complexcontent.content) + return complexcontent.content.elements[0] + end + elsif check_array_content(content) + return content.elements[0] + end + nil # use default item name + end + +private + + def element_simpletype(element) + if element.type + element.type + elsif element.local_simpletype + element.local_simpletype.base + else + nil + end + end + + def check_array_content(content) + content and content.elements.size == 1 and + content.elements[0].maxoccurs != '1' + end + + def content_arytype + if arytype = find_arytype + ns = arytype.namespace + name = arytype.name.sub(/\[(?:,)*\]$/, '') + XSD::QName.new(ns, name) + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/data.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/data.rb new file mode 100644 index 0000000000..48512d3751 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/data.rb @@ -0,0 +1,42 @@ +# WSDL4R - WSDL SOAP binding data definitions. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/qname' +require 'wsdl/soap/definitions' +require 'wsdl/soap/binding' +require 'wsdl/soap/operation' +require 'wsdl/soap/body' +require 'wsdl/soap/element' +require 'wsdl/soap/header' +require 'wsdl/soap/headerfault' +require 'wsdl/soap/fault' +require 'wsdl/soap/address' +require 'wsdl/soap/complexType' + + +module WSDL +module SOAP + + +HeaderFaultName = XSD::QName.new(SOAPBindingNamespace, 'headerfault') + +LocationAttrName = XSD::QName.new(nil, 'location') +StyleAttrName = XSD::QName.new(nil, 'style') +TransportAttrName = XSD::QName.new(nil, 'transport') +UseAttrName = XSD::QName.new(nil, 'use') +PartsAttrName = XSD::QName.new(nil, 'parts') +PartAttrName = XSD::QName.new(nil, 'part') +NameAttrName = XSD::QName.new(nil, 'name') +MessageAttrName = XSD::QName.new(nil, 'message') +EncodingStyleAttrName = XSD::QName.new(nil, 'encodingStyle') +NamespaceAttrName = XSD::QName.new(nil, 'namespace') +SOAPActionAttrName = XSD::QName.new(nil, 'soapAction') + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/definitions.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/definitions.rb new file mode 100644 index 0000000000..b014d5af6b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/definitions.rb @@ -0,0 +1,149 @@ +# WSDL4R - WSDL additional definitions for SOAP. +# Copyright (C) 2002-2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'xsd/namedelements' +require 'soap/mapping' + + +module WSDL + + +class Definitions < Info + def self.soap_rpc_complextypes + types = XSD::NamedElements.new + types << array_complextype + types << fault_complextype + types << exception_complextype + types + end + + def self.array_complextype + type = XMLSchema::ComplexType.new(::SOAP::ValueArrayName) + type.complexcontent = XMLSchema::ComplexContent.new + type.complexcontent.base = ::SOAP::ValueArrayName + attr = XMLSchema::Attribute.new + attr.ref = ::SOAP::AttrArrayTypeName + anytype = XSD::AnyTypeName.dup + anytype.name += '[]' + attr.arytype = anytype + type.complexcontent.attributes << attr + type + end + +=begin + + + + + + + + +=end + def self.fault_complextype + type = XMLSchema::ComplexType.new(::SOAP::EleFaultName) + faultcode = XMLSchema::Element.new(::SOAP::EleFaultCodeName, XSD::XSDQName::Type) + faultstring = XMLSchema::Element.new(::SOAP::EleFaultStringName, XSD::XSDString::Type) + faultactor = XMLSchema::Element.new(::SOAP::EleFaultActorName, XSD::XSDAnyURI::Type) + faultactor.minoccurs = 0 + detail = XMLSchema::Element.new(::SOAP::EleFaultDetailName, XSD::AnyTypeName) + detail.minoccurs = 0 + type.all_elements = [faultcode, faultstring, faultactor, detail] + type.final = 'extension' + type + end + + def self.exception_complextype + type = XMLSchema::ComplexType.new(XSD::QName.new( + ::SOAP::Mapping::RubyCustomTypeNamespace, 'SOAPException')) + excn_name = XMLSchema::Element.new(XSD::QName.new(nil, 'excn_type_name'), XSD::XSDString::Type) + cause = XMLSchema::Element.new(XSD::QName.new(nil, 'cause'), XSD::AnyTypeName) + backtrace = XMLSchema::Element.new(XSD::QName.new(nil, 'backtrace'), ::SOAP::ValueArrayName) + message = XMLSchema::Element.new(XSD::QName.new(nil, 'message'), XSD::XSDString::Type) + type.all_elements = [excn_name, cause, backtrace, message] + type + end + + def soap_rpc_complextypes(binding) + types = rpc_operation_complextypes(binding) + types + self.class.soap_rpc_complextypes + end + + def collect_faulttypes + result = [] + collect_fault_messages.each do |name| + faultparts = message(name).parts + if faultparts.size != 1 + raise RuntimeError.new("expecting fault message to have only 1 part") + end + if result.index(faultparts[0].type).nil? + result << faultparts[0].type + end + end + result + end + +private + + def collect_fault_messages + result = [] + porttypes.each do |porttype| + porttype.operations.each do |operation| + operation.fault.each do |fault| + if result.index(fault.message).nil? + result << fault.message + end + end + end + end + result + end + + def rpc_operation_complextypes(binding) + types = XSD::NamedElements.new + binding.operations.each do |op_bind| + if op_bind_rpc?(op_bind) + operation = op_bind.find_operation + if op_bind.input + type = XMLSchema::ComplexType.new(op_bind.soapoperation_name) + message = messages[operation.input.message] + type.sequence_elements = elements_from_message(message) + types << type + end + if op_bind.output + type = XMLSchema::ComplexType.new(operation.outputname) + message = messages[operation.output.message] + type.sequence_elements = elements_from_message(message) + types << type + end + end + end + types + end + + def op_bind_rpc?(op_bind) + op_bind.soapoperation_style == :rpc + end + + def elements_from_message(message) + message.parts.collect { |part| + if part.element + collect_elements[part.element] + elsif part.name.nil? or part.type.nil? + raise RuntimeError.new("part of a message must be an element or typed") + else + qname = XSD::QName.new(nil, part.name) + XMLSchema::Element.new(qname, part.type) + end + } + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/driverCreator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/driverCreator.rb new file mode 100644 index 0000000000..eba7c158a2 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/driverCreator.rb @@ -0,0 +1,95 @@ +# WSDL4R - Creating driver code from WSDL. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'wsdl/soap/mappingRegistryCreator' +require 'wsdl/soap/methodDefCreator' +require 'wsdl/soap/classDefCreatorSupport' +require 'xsd/codegen' + + +module WSDL +module SOAP + + +class DriverCreator + include ClassDefCreatorSupport + + attr_reader :definitions + + def initialize(definitions) + @definitions = definitions + end + + def dump(porttype = nil) + if porttype.nil? + result = "" + @definitions.porttypes.each do |type| + result << dump_porttype(type.name) + result << "\n" + end + else + result = dump_porttype(porttype) + end + result + end + +private + + def dump_porttype(name) + class_name = create_class_name(name) + methoddef, types = MethodDefCreator.new(@definitions).dump(name) + mr_creator = MappingRegistryCreator.new(@definitions) + binding = @definitions.bindings.find { |item| item.type == name } + return '' unless binding.soapbinding # not a SOAP binding + address = @definitions.porttype(name).locations[0] + + c = XSD::CodeGen::ClassDef.new(class_name, "::SOAP::RPC::Driver") + c.def_require("soap/rpc/driver") + c.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new") + c.def_const("DefaultEndpointUrl", ndq(address)) + c.def_code(mr_creator.dump(types)) + c.def_code <<-EOD +Methods = [ +#{methoddef.gsub(/^/, " ")} +] + EOD + c.def_method("initialize", "endpoint_url = nil") do + <<-EOD + endpoint_url ||= DefaultEndpointUrl + super(endpoint_url, nil) + self.mapping_registry = MappingRegistry + init_methods + EOD + end + c.def_privatemethod("init_methods") do + <<-EOD + Methods.each do |definitions| + opt = definitions.last + if opt[:request_style] == :document + add_document_operation(*definitions) + else + add_rpc_operation(*definitions) + qname = definitions[0] + name = definitions[2] + if qname.name != name and qname.name.capitalize == name.capitalize + ::SOAP::Mapping.define_singleton_method(self, qname.name) do |*arg| + __send__(name, *arg) + end + end + end + end + EOD + end + c.dump + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/element.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/element.rb new file mode 100644 index 0000000000..0fa6017c5b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/element.rb @@ -0,0 +1,28 @@ +# WSDL4R - XMLSchema element definition for WSDL. +# Copyright (C) 2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/xmlSchema/element' + + +module WSDL +module XMLSchema + + +class Element < Info + def map_as_array? + maxoccurs != '1' + end + + def attributes + @local_complextype.attributes + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/fault.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/fault.rb new file mode 100644 index 0000000000..2862b659ab --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/fault.rb @@ -0,0 +1,56 @@ +# WSDL4R - WSDL SOAP body definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module SOAP + + +class Fault < Info + attr_reader :name # required + attr_reader :use # required + attr_reader :encodingstyle + attr_reader :namespace + + def initialize + super + @name = nil + @use = nil + @encodingstyle = nil + @namespace = nil + end + + def targetnamespace + parent.targetnamespace + end + + def parse_element(element) + nil + end + + def parse_attr(attr, value) + case attr + when NameAttrName + @name = XSD::QName.new(targetnamespace, value.source) + when UseAttrName + @use = value.source + when EncodingStyleAttrName + @encodingstyle = value.source + when NamespaceAttrName + @namespace = value.source + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/header.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/header.rb new file mode 100644 index 0000000000..8d7c4e9d70 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/header.rb @@ -0,0 +1,86 @@ +# WSDL4R - WSDL SOAP body definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module SOAP + + +class Header < Info + attr_reader :headerfault + + attr_reader :message # required + attr_reader :part # required + attr_reader :use # required + attr_reader :encodingstyle + attr_reader :namespace + + def initialize + super + @message = nil + @part = nil + @use = nil + @encodingstyle = nil + @namespace = nil + @headerfault = nil + end + + def targetnamespace + parent.targetnamespace + end + + def find_message + root.message(@message) or raise RuntimeError.new("#{@message} not found") + end + + def find_part + find_message.parts.each do |part| + if part.name == @part + return part + end + end + raise RuntimeError.new("#{@part} not found") + end + + def parse_element(element) + case element + when HeaderFaultName + o = WSDL::SOAP::HeaderFault.new + @headerfault = o + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when MessageAttrName + if value.namespace.nil? + value = XSD::QName.new(targetnamespace, value.source) + end + @message = value + when PartAttrName + @part = value.source + when UseAttrName + @use = value.source + when EncodingStyleAttrName + @encodingstyle = value.source + when NamespaceAttrName + @namespace = value.source + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/headerfault.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/headerfault.rb new file mode 100644 index 0000000000..d6b14f2646 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/headerfault.rb @@ -0,0 +1,56 @@ +# WSDL4R - WSDL SOAP body definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module SOAP + + +class HeaderFault < Info + attr_reader :message # required + attr_reader :part # required + attr_reader :use # required + attr_reader :encodingstyle + attr_reader :namespace + + def initialize + super + @message = nil + @part = nil + @use = nil + @encodingstyle = nil + @namespace = nil + end + + def parse_element(element) + nil + end + + def parse_attr(attr, value) + case attr + when MessageAttrName + @message = value + when PartAttrName + @part = value.source + when UseAttrName + @use = value.source + when EncodingStyleAttrName + @encodingstyle = value.source + when NamespaceAttrName + @namespace = value.source + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/mappingRegistryCreator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/mappingRegistryCreator.rb new file mode 100644 index 0000000000..8669339ce4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/mappingRegistryCreator.rb @@ -0,0 +1,92 @@ +# WSDL4R - Creating MappingRegistry code from WSDL. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'wsdl/soap/classDefCreatorSupport' + + +module WSDL +module SOAP + + +class MappingRegistryCreator + include ClassDefCreatorSupport + + attr_reader :definitions + + def initialize(definitions) + @definitions = definitions + @complextypes = @definitions.collect_complextypes + @types = nil + end + + def dump(types) + @types = types + map_cache = [] + map = "" + @types.each do |type| + if map_cache.index(type).nil? + map_cache << type + if type.namespace != XSD::Namespace + if typemap = dump_typemap(type) + map << typemap + end + end + end + end + return map + end + +private + + def dump_typemap(type) + if definedtype = @complextypes[type] + case definedtype.compoundtype + when :TYPE_STRUCT + dump_struct_typemap(definedtype) + when :TYPE_ARRAY + dump_array_typemap(definedtype) + when :TYPE_MAP, :TYPE_EMPTY + nil + else + raise NotImplementedError.new("must not reach here") + end + end + end + + def dump_struct_typemap(definedtype) + ele = definedtype.name + return <<__EOD__ +MappingRegistry.set( + #{create_class_name(ele)}, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => #{dqname(ele)} } +) +__EOD__ + end + + def dump_array_typemap(definedtype) + ele = definedtype.name + arytype = definedtype.find_arytype || XSD::AnyTypeName + type = XSD::QName.new(arytype.namespace, arytype.name.sub(/\[(?:,)*\]$/, '')) + @types << type + return <<__EOD__ +MappingRegistry.set( + #{create_class_name(ele)}, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => #{dqname(type)} } +) +__EOD__ + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/methodDefCreator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/methodDefCreator.rb new file mode 100644 index 0000000000..f3ffadbe69 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/methodDefCreator.rb @@ -0,0 +1,228 @@ +# WSDL4R - Creating driver code from WSDL. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'wsdl/soap/classDefCreatorSupport' +require 'soap/rpc/element' + + +module WSDL +module SOAP + + +class MethodDefCreator + include ClassDefCreatorSupport + + attr_reader :definitions + + def initialize(definitions) + @definitions = definitions + @simpletypes = @definitions.collect_simpletypes + @complextypes = @definitions.collect_complextypes + @elements = @definitions.collect_elements + @types = [] + end + + def dump(porttype) + @types.clear + result = "" + operations = @definitions.porttype(porttype).operations + binding = @definitions.porttype_binding(porttype) + operations.each do |operation| + op_bind = binding.operations[operation.name] + next unless op_bind # no binding is defined + next unless op_bind.soapoperation # not a SOAP operation binding + result << ",\n" unless result.empty? + result << dump_method(operation, op_bind).chomp + end + return result, @types + end + + def collect_rpcparameter(operation) + result = operation.inputparts.collect { |part| + collect_type(part.type) + param_set(::SOAP::RPC::SOAPMethod::IN, part.name, rpcdefinedtype(part)) + } + outparts = operation.outputparts + if outparts.size > 0 + retval = outparts[0] + collect_type(retval.type) + result << param_set(::SOAP::RPC::SOAPMethod::RETVAL, retval.name, + rpcdefinedtype(retval)) + cdr(outparts).each { |part| + collect_type(part.type) + result << param_set(::SOAP::RPC::SOAPMethod::OUT, part.name, + rpcdefinedtype(part)) + } + end + result + end + + def collect_documentparameter(operation) + param = [] + operation.inputparts.each do |input| + param << param_set(::SOAP::RPC::SOAPMethod::IN, input.name, + documentdefinedtype(input), elementqualified(input)) + end + operation.outputparts.each do |output| + param << param_set(::SOAP::RPC::SOAPMethod::OUT, output.name, + documentdefinedtype(output), elementqualified(output)) + end + param + end + +private + + def dump_method(operation, binding) + name = safemethodname(operation.name.name) + name_as = operation.name.name + style = binding.soapoperation_style + inputuse = binding.input.soapbody_use + outputuse = binding.output.soapbody_use + namespace = binding.input.soapbody.namespace + if style == :rpc + qname = XSD::QName.new(namespace, name_as) + paramstr = param2str(collect_rpcparameter(operation)) + else + qname = nil + paramstr = param2str(collect_documentparameter(operation)) + end + if paramstr.empty? + paramstr = '[]' + else + paramstr = "[ " << paramstr.split(/\r?\n/).join("\n ") << " ]" + end + definitions = <<__EOD__ +#{ndq(binding.soapaction)}, + #{dq(name)}, + #{paramstr}, + { :request_style => #{sym(style.id2name)}, :request_use => #{sym(inputuse.id2name)}, + :response_style => #{sym(style.id2name)}, :response_use => #{sym(outputuse.id2name)} } +__EOD__ + if style == :rpc + return <<__EOD__ +[ #{qname.dump}, + #{definitions}] +__EOD__ + else + return <<__EOD__ +[ #{definitions}] +__EOD__ + end + end + + def rpcdefinedtype(part) + if mapped = basetype_mapped_class(part.type) + ['::' + mapped.name] + elsif definedtype = @simpletypes[part.type] + ['::' + basetype_mapped_class(definedtype.base).name] + elsif definedtype = @elements[part.element] + #['::SOAP::SOAPStruct', part.element.namespace, part.element.name] + ['nil', part.element.namespace, part.element.name] + elsif definedtype = @complextypes[part.type] + case definedtype.compoundtype + when :TYPE_STRUCT, :TYPE_EMPTY # ToDo: empty should be treated as void. + type = create_class_name(part.type) + [type, part.type.namespace, part.type.name] + when :TYPE_MAP + [Hash.name, part.type.namespace, part.type.name] + when :TYPE_ARRAY + arytype = definedtype.find_arytype || XSD::AnyTypeName + ns = arytype.namespace + name = arytype.name.sub(/\[(?:,)*\]$/, '') + type = create_class_name(XSD::QName.new(ns, name)) + [type + '[]', ns, name] + else + raise NotImplementedError.new("must not reach here") + end + else + raise RuntimeError.new("part: #{part.name} cannot be resolved") + end + end + + def documentdefinedtype(part) + if mapped = basetype_mapped_class(part.type) + ['::' + mapped.name, nil, part.name] + elsif definedtype = @simpletypes[part.type] + ['::' + basetype_mapped_class(definedtype.base).name, nil, part.name] + elsif definedtype = @elements[part.element] + ['::SOAP::SOAPElement', part.element.namespace, part.element.name] + elsif definedtype = @complextypes[part.type] + ['::SOAP::SOAPElement', part.type.namespace, part.type.name] + else + raise RuntimeError.new("part: #{part.name} cannot be resolved") + end + end + + def elementqualified(part) + if mapped = basetype_mapped_class(part.type) + false + elsif definedtype = @simpletypes[part.type] + false + elsif definedtype = @elements[part.element] + definedtype.elementform == 'qualified' + elsif definedtype = @complextypes[part.type] + false + else + raise RuntimeError.new("part: #{part.name} cannot be resolved") + end + end + + def param_set(io_type, name, type, ele = nil) + [io_type, name, type, ele] + end + + def collect_type(type) + # ignore inline type definition. + return if type.nil? + return if @types.include?(type) + @types << type + return unless @complextypes[type] + @complextypes[type].each_element do |element| + collect_type(element.type) + end + end + + def param2str(params) + params.collect { |param| + io, name, type, ele = param + unless ele.nil? + "[#{dq(io)}, #{dq(name)}, #{type2str(type)}, #{ele2str(ele)}]" + else + "[#{dq(io)}, #{dq(name)}, #{type2str(type)}]" + end + }.join(",\n") + end + + def type2str(type) + if type.size == 1 + "[#{dq(type[0])}]" + else + "[#{dq(type[0])}, #{ndq(type[1])}, #{dq(type[2])}]" + end + end + + def ele2str(ele) + qualified = ele + if qualified + "true" + else + "false" + end + end + + def cdr(ary) + result = ary.dup + result.shift + result + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/operation.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/operation.rb new file mode 100644 index 0000000000..502d34a07d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/operation.rb @@ -0,0 +1,122 @@ +# WSDL4R - WSDL SOAP operation definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module SOAP + + +class Operation < Info + class OperationInfo + attr_reader :style + attr_reader :op_name + attr_reader :optype_name + attr_reader :headerparts + attr_reader :bodyparts + attr_reader :faultpart + attr_reader :soapaction + + def initialize(style, op_name, optype_name, headerparts, bodyparts, faultpart, soapaction) + @style = style + @op_name = op_name + @optype_name = optype_name + @headerparts = headerparts + @bodyparts = bodyparts + @faultpart = faultpart + @soapaction = soapaction + end + end + + attr_reader :soapaction + attr_reader :style + + def initialize + super + @soapaction = nil + @style = nil + end + + def parse_element(element) + nil + end + + def parse_attr(attr, value) + case attr + when StyleAttrName + if ["document", "rpc"].include?(value.source) + @style = value.source.intern + else + raise Parser::AttributeConstraintError.new( + "Unexpected value #{ value }.") + end + when SOAPActionAttrName + @soapaction = value.source + else + nil + end + end + + def input_info + name_info = parent.find_operation.input_info + param_info(name_info, parent.input) + end + + def output_info + name_info = parent.find_operation.output_info + param_info(name_info, parent.output) + end + + def operation_style + return @style if @style + if parent_binding.soapbinding + return parent_binding.soapbinding.style + end + nil + end + +private + + def parent_binding + parent.parent + end + + def param_info(name_info, param) + op_name = name_info.op_name + optype_name = name_info.optype_name + + soapheader = param.soapheader + headerparts = soapheader.collect { |item| item.find_part } + + soapbody = param.soapbody + if soapbody.encodingstyle and + soapbody.encodingstyle != ::SOAP::EncodingNamespace + raise NotImplementedError.new( + "EncodingStyle '#{ soapbody.encodingstyle }' not supported.") + end + if soapbody.namespace + op_name = XSD::QName.new(soapbody.namespace, op_name.name) + end + if soapbody.parts + target = soapbody.parts.split(/\s+/) + bodyparts = name_info.parts.find_all { |part| + target.include?(part.name) + } + else + bodyparts = name_info.parts + end + + faultpart = nil + OperationInfo.new(operation_style, op_name, optype_name, headerparts, bodyparts, faultpart, parent.soapaction) + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/servantSkeltonCreator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/servantSkeltonCreator.rb new file mode 100644 index 0000000000..88294ffed8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/servantSkeltonCreator.rb @@ -0,0 +1,67 @@ +# WSDL4R - Creating servant skelton code from WSDL. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'wsdl/soap/classDefCreatorSupport' +require 'xsd/codegen' + + +module WSDL +module SOAP + + +class ServantSkeltonCreator + include ClassDefCreatorSupport + include XSD::CodeGen::GenSupport + + attr_reader :definitions + + def initialize(definitions) + @definitions = definitions + end + + def dump(porttype = nil) + if porttype.nil? + result = "" + @definitions.porttypes.each do |type| + result << dump_porttype(type.name) + result << "\n" + end + else + result = dump_porttype(porttype) + end + result + end + +private + + def dump_porttype(name) + class_name = create_class_name(name) + c = XSD::CodeGen::ClassDef.new(class_name) + operations = @definitions.porttype(name).operations + operations.each do |operation| + name = safemethodname(operation.name.name) + input = operation.input + params = input.find_message.parts.collect { |part| + safevarname(part.name) + } + m = XSD::CodeGen::MethodDef.new(name, params) do <<-EOD + p [#{params.join(", ")}] + raise NotImplementedError.new + EOD + end + m.comment = dump_method_signature(operation) + c.add_method(m) + end + c.dump + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/standaloneServerStubCreator.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/standaloneServerStubCreator.rb new file mode 100644 index 0000000000..0b751b5153 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/standaloneServerStubCreator.rb @@ -0,0 +1,85 @@ +# WSDL4R - Creating standalone server stub code from WSDL. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'wsdl/soap/mappingRegistryCreator' +require 'wsdl/soap/methodDefCreator' +require 'wsdl/soap/classDefCreatorSupport' + + +module WSDL +module SOAP + + +class StandaloneServerStubCreator + include ClassDefCreatorSupport + + attr_reader :definitions + + def initialize(definitions) + @definitions = definitions + end + + def dump(service_name) + warn("- Standalone stub can have only 1 port for now. So creating stub for the first port and rests are ignored.") + warn("- Standalone server stub ignores port location defined in WSDL. Location is http://localhost:10080/ by default. Generated client from WSDL must be configured to point this endpoint manually.") + port = @definitions.service(service_name).ports[0] + dump_porttype(port.porttype.name) + end + +private + + def dump_porttype(name) + class_name = create_class_name(name) + methoddef, types = MethodDefCreator.new(@definitions).dump(name) + mr_creator = MappingRegistryCreator.new(@definitions) + + c1 = XSD::CodeGen::ClassDef.new(class_name) + c1.def_require("soap/rpc/standaloneServer") + c1.def_require("soap/mapping/registry") + c1.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new") + c1.def_code(mr_creator.dump(types)) + c1.def_code <<-EOD +Methods = [ +#{methoddef.gsub(/^/, " ")} +] + EOD + c2 = XSD::CodeGen::ClassDef.new(class_name + "App", + "::SOAP::RPC::StandaloneServer") + c2.def_method("initialize", "*arg") do + <<-EOD + super(*arg) + servant = #{class_name}.new + #{class_name}::Methods.each do |definitions| + opt = definitions.last + if opt[:request_style] == :document + @router.add_document_operation(servant, *definitions) + else + @router.add_rpc_operation(servant, *definitions) + end + end + self.mapping_registry = #{class_name}::MappingRegistry + EOD + end + c1.dump + "\n" + c2.dump + format(<<-EOD) + + if $0 == __FILE__ + # Change listen port. + server = #{class_name}App.new('app', nil, '0.0.0.0', 10080) + trap(:INT) do + server.shutdown + end + server.start + end + EOD + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/wsdl2ruby.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/wsdl2ruby.rb new file mode 100644 index 0000000000..16b05fb032 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/soap/wsdl2ruby.rb @@ -0,0 +1,176 @@ +# WSDL4R - WSDL to ruby mapping library. +# Copyright (C) 2002-2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'logger' +require 'xsd/qname' +require 'wsdl/importer' +require 'wsdl/soap/classDefCreator' +require 'wsdl/soap/servantSkeltonCreator' +require 'wsdl/soap/driverCreator' +require 'wsdl/soap/clientSkeltonCreator' +require 'wsdl/soap/standaloneServerStubCreator' +require 'wsdl/soap/cgiStubCreator' + + +module WSDL +module SOAP + + +class WSDL2Ruby + attr_accessor :location + attr_reader :opt + attr_accessor :logger + attr_accessor :basedir + + def run + unless @location + raise RuntimeError, "WSDL location not given" + end + @wsdl = import(@location) + @name = @wsdl.name ? @wsdl.name.name : 'default' + create_file + end + +private + + def initialize + @location = nil + @opt = {} + @logger = Logger.new(STDERR) + @basedir = nil + @wsdl = nil + @name = nil + end + + def create_file + create_classdef if @opt.key?('classdef') + create_servant_skelton(@opt['servant_skelton']) if @opt.key?('servant_skelton') + create_cgi_stub(@opt['cgi_stub']) if @opt.key?('cgi_stub') + create_standalone_server_stub(@opt['standalone_server_stub']) if @opt.key?('standalone_server_stub') + create_driver(@opt['driver']) if @opt.key?('driver') + create_client_skelton(@opt['client_skelton']) if @opt.key?('client_skelton') + end + + def create_classdef + @logger.info { "Creating class definition." } + @classdef_filename = @name + '.rb' + check_file(@classdef_filename) or return + write_file(@classdef_filename) do |f| + f << WSDL::SOAP::ClassDefCreator.new(@wsdl).dump + end + end + + def create_client_skelton(servicename) + @logger.info { "Creating client skelton." } + servicename ||= @wsdl.services[0].name.name + @client_skelton_filename = servicename + 'Client.rb' + check_file(@client_skelton_filename) or return + write_file(@client_skelton_filename) do |f| + f << shbang << "\n" + f << "require '#{@driver_filename}'\n\n" if @driver_filename + f << WSDL::SOAP::ClientSkeltonCreator.new(@wsdl).dump( + create_name(servicename)) + end + end + + def create_servant_skelton(porttypename) + @logger.info { "Creating servant skelton." } + @servant_skelton_filename = (porttypename || @name + 'Servant') + '.rb' + check_file(@servant_skelton_filename) or return + write_file(@servant_skelton_filename) do |f| + f << "require '#{@classdef_filename}'\n\n" if @classdef_filename + f << WSDL::SOAP::ServantSkeltonCreator.new(@wsdl).dump( + create_name(porttypename)) + end + end + + def create_cgi_stub(servicename) + @logger.info { "Creating CGI stub." } + servicename ||= @wsdl.services[0].name.name + @cgi_stubFilename = servicename + '.cgi' + check_file(@cgi_stubFilename) or return + write_file(@cgi_stubFilename) do |f| + f << shbang << "\n" + if @servant_skelton_filename + f << "require '#{@servant_skelton_filename}'\n\n" + end + f << WSDL::SOAP::CGIStubCreator.new(@wsdl).dump(create_name(servicename)) + end + end + + def create_standalone_server_stub(servicename) + @logger.info { "Creating standalone stub." } + servicename ||= @wsdl.services[0].name.name + @standalone_server_stub_filename = servicename + '.rb' + check_file(@standalone_server_stub_filename) or return + write_file(@standalone_server_stub_filename) do |f| + f << shbang << "\n" + f << "require '#{@servant_skelton_filename}'\n\n" if @servant_skelton_filename + f << WSDL::SOAP::StandaloneServerStubCreator.new(@wsdl).dump( + create_name(servicename)) + end + end + + def create_driver(porttypename) + @logger.info { "Creating driver." } + @driver_filename = (porttypename || @name) + 'Driver.rb' + check_file(@driver_filename) or return + write_file(@driver_filename) do |f| + f << "require '#{@classdef_filename}'\n\n" if @classdef_filename + f << WSDL::SOAP::DriverCreator.new(@wsdl).dump( + create_name(porttypename)) + end + end + + def write_file(filename) + if @basedir + filename = File.join(basedir, filename) + end + File.open(filename, "w") do |f| + yield f + end + end + + def check_file(filename) + if @basedir + filename = File.join(basedir, filename) + end + if FileTest.exist?(filename) + if @opt.key?('force') + @logger.warn { + "File '#{filename}' exists but overrides it." + } + true + else + @logger.warn { + "File '#{filename}' exists. #{$0} did not override it." + } + false + end + else + @logger.info { "Creates file '#{filename}'." } + true + end + end + + def shbang + "#!/usr/bin/env ruby" + end + + def create_name(name) + name ? XSD::QName.new(@wsdl.targetnamespace, name) : nil + end + + def import(location) + WSDL::Importer.import(location) + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/types.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/types.rb new file mode 100644 index 0000000000..96ae5a4988 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/types.rb @@ -0,0 +1,43 @@ +# WSDL4R - WSDL types definition. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL + + +class Types < Info + attr_reader :schemas + + def initialize + super + @schemas = [] + end + + def parse_element(element) + case element + when SchemaName + o = XMLSchema::Schema.new + @schemas << o + o + when DocumentationName + o = Documentation.new + o + else + nil + end + end + + def parse_attr(attr, value) + nil + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/wsdl.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/wsdl.rb new file mode 100644 index 0000000000..eb13c18806 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/wsdl.rb @@ -0,0 +1,23 @@ +# WSDL4R - Base definitions. +# Copyright (C) 2000, 2001, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/qname' + + +module WSDL + + +Version = '0.0.2' + +Namespace = 'http://schemas.xmlsoap.org/wsdl/' +SOAPBindingNamespace ='http://schemas.xmlsoap.org/wsdl/soap/' + +class Error < StandardError; end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/all.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/all.rb new file mode 100644 index 0000000000..bb9566feac --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/all.rb @@ -0,0 +1,69 @@ +# WSDL4R - XMLSchema complexType definition for WSDL. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module XMLSchema + + +class All < Info + attr_reader :minoccurs + attr_reader :maxoccurs + attr_reader :elements + + def initialize + super() + @minoccurs = '1' + @maxoccurs = '1' + @elements = [] + end + + def targetnamespace + parent.targetnamespace + end + + def elementformdefault + parent.elementformdefault + end + + def <<(element) + @elements << element + end + + def parse_element(element) + case element + when AnyName + o = Any.new + @elements << o + o + when ElementName + o = Element.new + @elements << o + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when MaxOccursAttrName + @maxoccurs = value.source + when MinOccursAttrName + @minoccurs = value.source + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/annotation.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/annotation.rb new file mode 100644 index 0000000000..633bd196f1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/annotation.rb @@ -0,0 +1,34 @@ +# WSDL4R - WSDL SOAP documentation element. +# Copyright (C) 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module XMLSchema + + +class Annotation < Info + def initialize + super + end + + def parse_element(element) + # Accepts any element. + self + end + + def parse_attr(attr, value) + # Accepts any attribute. + true + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/any.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/any.rb new file mode 100644 index 0000000000..72d25e8dde --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/any.rb @@ -0,0 +1,56 @@ +# WSDL4R - XMLSchema any definition for WSDL. +# Copyright (C) 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module XMLSchema + + +class Any < Info + attr_accessor :maxoccurs + attr_accessor :minoccurs + attr_accessor :namespace + attr_accessor :process_contents + + def initialize + super() + @maxoccurs = '1' + @minoccurs = '1' + @namespace = '##any' + @process_contents = 'strict' + end + + def targetnamespace + parent.targetnamespace + end + + def parse_element(element) + nil + end + + def parse_attr(attr, value) + case attr + when MaxOccursAttrName + @maxoccurs = value.source + when MinOccursAttrName + @minoccurs = value.source + when NamespaceAttrName + @namespace = value.source + when ProcessContentsAttrName + @process_contents = value.source + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/attribute.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/attribute.rb new file mode 100644 index 0000000000..f9048661a2 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/attribute.rb @@ -0,0 +1,127 @@ +# WSDL4R - XMLSchema attribute definition for WSDL. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module XMLSchema + + +class Attribute < Info + class << self + if RUBY_VERSION > "1.7.0" + def attr_reader_ref(symbol) + name = symbol.to_s + define_method(name) { + instance_variable_get("@#{name}") || + (refelement ? refelement.__send__(name) : nil) + } + end + else + def attr_reader_ref(symbol) + name = symbol.to_s + module_eval <<-EOS + def #{name} + @#{name} || (refelement ? refelement.#{name} : nil) + end + EOS + end + end + end + + attr_writer :use + attr_writer :form + attr_writer :name + attr_writer :type + attr_writer :local_simpletype + attr_writer :default + attr_writer :fixed + + attr_reader_ref :use + attr_reader_ref :form + attr_reader_ref :name + attr_reader_ref :type + attr_reader_ref :local_simpletype + attr_reader_ref :default + attr_reader_ref :fixed + + attr_accessor :ref + attr_accessor :arytype + + def initialize + super + @use = nil + @form = nil + @name = nil + @type = nil + @local_simpletype = nil + @default = nil + @fixed = nil + @ref = nil + @refelement = nil + @arytype = nil + end + + def refelement + @refelement ||= root.collect_attributes[@ref] + end + + def targetnamespace + parent.targetnamespace + end + + def parse_element(element) + case element + when SimpleTypeName + @local_simpletype = SimpleType.new + @local_simpletype + end + end + + def parse_attr(attr, value) + case attr + when RefAttrName + @ref = value + when UseAttrName + @use = value.source + when FormAttrName + @form = value.source + when NameAttrName + if directelement? + @name = XSD::QName.new(targetnamespace, value.source) + else + @name = XSD::QName.new(nil, value.source) + end + when TypeAttrName + @type = value + when DefaultAttrName + @default = value.source + when FixedAttrName + @fixed = value.source + when ArrayTypeAttrName + @arytype = if value.namespace.nil? + XSD::QName.new(XSD::Namespace, value.source) + else + value + end + else + nil + end + end + +private + + def directelement? + parent.is_a?(Schema) + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/choice.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/choice.rb new file mode 100644 index 0000000000..f6d27fa38c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/choice.rb @@ -0,0 +1,69 @@ +# WSDL4R - XMLSchema complexType definition for WSDL. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module XMLSchema + + +class Choice < Info + attr_reader :minoccurs + attr_reader :maxoccurs + attr_reader :elements + + def initialize + super() + @minoccurs = '1' + @maxoccurs = '1' + @elements = [] + end + + def targetnamespace + parent.targetnamespace + end + + def elementformdefault + parent.elementformdefault + end + + def <<(element) + @elements << element + end + + def parse_element(element) + case element + when AnyName + o = Any.new + @elements << o + o + when ElementName + o = Element.new + @elements << o + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when MaxOccursAttrName + @maxoccurs = value.source + when MinOccursAttrName + @minoccurs = value.source + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/complexContent.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/complexContent.rb new file mode 100644 index 0000000000..f7fb6c16b4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/complexContent.rb @@ -0,0 +1,92 @@ +# WSDL4R - XMLSchema complexContent definition for WSDL. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'xsd/namedelements' + + +module WSDL +module XMLSchema + + +class ComplexContent < Info + attr_accessor :base + attr_reader :derivetype + attr_reader :content + attr_reader :attributes + + def initialize + super + @base = nil + @derivetype = nil + @content = nil + @attributes = XSD::NamedElements.new + @basetype = nil + end + + def targetnamespace + parent.targetnamespace + end + + def elementformdefault + parent.elementformdefault + end + + def basetype + @basetype ||= root.collect_complextypes[@base] + end + + def parse_element(element) + case element + when RestrictionName, ExtensionName + @derivetype = element.name + self + when AllName + if @derivetype.nil? + raise Parser::ElementConstraintError.new("base attr not found.") + end + @content = All.new + @content + when SequenceName + if @derivetype.nil? + raise Parser::ElementConstraintError.new("base attr not found.") + end + @content = Sequence.new + @content + when ChoiceName + if @derivetype.nil? + raise Parser::ElementConstraintError.new("base attr not found.") + end + @content = Choice.new + @content + when AttributeName + if @derivetype.nil? + raise Parser::ElementConstraintError.new("base attr not found.") + end + o = Attribute.new + @attributes << o + o + end + end + + def parse_attr(attr, value) + if @derivetype.nil? + return nil + end + case attr + when BaseAttrName + @base = value + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/complexType.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/complexType.rb new file mode 100644 index 0000000000..dc9ec954fc --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/complexType.rb @@ -0,0 +1,139 @@ +# WSDL4R - XMLSchema complexType definition for WSDL. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'wsdl/xmlSchema/content' +require 'wsdl/xmlSchema/element' +require 'xsd/namedelements' + + +module WSDL +module XMLSchema + + +class ComplexType < Info + attr_accessor :name + attr_accessor :complexcontent + attr_accessor :simplecontent + attr_reader :content + attr_accessor :final + attr_accessor :mixed + attr_reader :attributes + + def initialize(name = nil) + super() + @name = name + @complexcontent = nil + @simplecontent = nil + @content = nil + @final = nil + @mixed = false + @attributes = XSD::NamedElements.new + end + + def targetnamespace + # inner elements can be qualified + # parent.is_a?(WSDL::XMLSchema::Element) ? nil : parent.targetnamespace + parent.targetnamespace + end + + def elementformdefault + parent.elementformdefault + end + + AnyAsElement = Element.new(XSD::QName.new(nil, 'any'), XSD::AnyTypeName) + def each_element + if content + content.elements.each do |element| + if element.is_a?(Any) + yield(AnyAsElement) + else + yield(element) + end + end + end + end + + def find_element(name) + if content + content.elements.each do |element| + if element.is_a?(Any) + return AnyAsElement if name == AnyAsElement.name + else + return element if name == element.name + end + end + end + nil + end + + def find_element_by_name(name) + if content + content.elements.each do |element| + if element.is_a?(Any) + return AnyAsElement if name == AnyAsElement.name.name + else + return element if name == element.name.name + end + end + end + nil + end + + def sequence_elements=(elements) + @content = Sequence.new + elements.each do |element| + @content << element + end + end + + def all_elements=(elements) + @content = All.new + elements.each do |element| + @content << element + end + end + + def parse_element(element) + case element + when AllName + @content = All.new + when SequenceName + @content = Sequence.new + when ChoiceName + @content = Choice.new + when ComplexContentName + @complexcontent = ComplexContent.new + when SimpleContentName + @simplecontent = SimpleContent.new + when AttributeName + o = Attribute.new + @attributes << o + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when FinalAttrName + @final = value.source + when MixedAttrName + @mixed = (value.source == 'true') + when NameAttrName + @name = XSD::QName.new(targetnamespace, value.source) + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/content.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/content.rb new file mode 100644 index 0000000000..2f1dfb4b6c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/content.rb @@ -0,0 +1,96 @@ +# WSDL4R - XMLSchema complexType definition for WSDL. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module XMLSchema + + +class Content < Info + attr_accessor :final + attr_accessor :mixed + attr_accessor :type + attr_reader :contents + attr_reader :elements + + def initialize + super() + @final = nil + @mixed = false + @type = nil + @contents = [] + @elements = [] + end + + def targetnamespace + parent.targetnamespace + end + + def <<(content) + @contents << content + update_elements + end + + def each + @contents.each do |content| + yield content + end + end + + def parse_element(element) + case element + when AllName, SequenceName, ChoiceName + o = Content.new + o.type = element.name + @contents << o + o + when AnyName + o = Any.new + @contents << o + o + when ElementName + o = Element.new + @contents << o + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when FinalAttrName + @final = value.source + when MixedAttrName + @mixed = (value.source == 'true') + else + nil + end + end + + def parse_epilogue + update_elements + end + +private + + def update_elements + @elements = [] + @contents.each do |content| + if content.is_a?(Element) + @elements << [content.name, content] + end + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/data.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/data.rb new file mode 100644 index 0000000000..23ab1adf0b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/data.rb @@ -0,0 +1,80 @@ +# WSDL4R - XMLSchema data definitions. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/datatypes' +require 'wsdl/xmlSchema/annotation' +require 'wsdl/xmlSchema/schema' +require 'wsdl/xmlSchema/import' +require 'wsdl/xmlSchema/include' +require 'wsdl/xmlSchema/simpleType' +require 'wsdl/xmlSchema/simpleRestriction' +require 'wsdl/xmlSchema/simpleExtension' +require 'wsdl/xmlSchema/complexType' +require 'wsdl/xmlSchema/complexContent' +require 'wsdl/xmlSchema/simpleContent' +require 'wsdl/xmlSchema/any' +require 'wsdl/xmlSchema/element' +require 'wsdl/xmlSchema/all' +require 'wsdl/xmlSchema/choice' +require 'wsdl/xmlSchema/sequence' +require 'wsdl/xmlSchema/attribute' +require 'wsdl/xmlSchema/unique' +require 'wsdl/xmlSchema/enumeration' +require 'wsdl/xmlSchema/length' +require 'wsdl/xmlSchema/pattern' + +module WSDL +module XMLSchema + + +AllName = XSD::QName.new(XSD::Namespace, 'all') +AnnotationName = XSD::QName.new(XSD::Namespace, 'annotation') +AnyName = XSD::QName.new(XSD::Namespace, 'any') +AttributeName = XSD::QName.new(XSD::Namespace, 'attribute') +ChoiceName = XSD::QName.new(XSD::Namespace, 'choice') +ComplexContentName = XSD::QName.new(XSD::Namespace, 'complexContent') +ComplexTypeName = XSD::QName.new(XSD::Namespace, 'complexType') +ElementName = XSD::QName.new(XSD::Namespace, 'element') +EnumerationName = XSD::QName.new(XSD::Namespace, 'enumeration') +ExtensionName = XSD::QName.new(XSD::Namespace, 'extension') +ImportName = XSD::QName.new(XSD::Namespace, 'import') +IncludeName = XSD::QName.new(XSD::Namespace, 'include') +LengthName = XSD::QName.new(XSD::Namespace, 'length') +PatternName = XSD::QName.new(XSD::Namespace, 'pattern') +RestrictionName = XSD::QName.new(XSD::Namespace, 'restriction') +SequenceName = XSD::QName.new(XSD::Namespace, 'sequence') +SchemaName = XSD::QName.new(XSD::Namespace, 'schema') +SimpleContentName = XSD::QName.new(XSD::Namespace, 'simpleContent') +SimpleTypeName = XSD::QName.new(XSD::Namespace, 'simpleType') +UniqueName = XSD::QName.new(XSD::Namespace, 'unique') + +AttributeFormDefaultAttrName = XSD::QName.new(nil, 'attributeFormDefault') +BaseAttrName = XSD::QName.new(nil, 'base') +DefaultAttrName = XSD::QName.new(nil, 'default') +ElementFormDefaultAttrName = XSD::QName.new(nil, 'elementFormDefault') +FinalAttrName = XSD::QName.new(nil, 'final') +FixedAttrName = XSD::QName.new(nil, 'fixed') +FormAttrName = XSD::QName.new(nil, 'form') +IdAttrName = XSD::QName.new(nil, 'id') +MaxOccursAttrName = XSD::QName.new(nil, 'maxOccurs') +MinOccursAttrName = XSD::QName.new(nil, 'minOccurs') +MixedAttrName = XSD::QName.new(nil, 'mixed') +NameAttrName = XSD::QName.new(nil, 'name') +NamespaceAttrName = XSD::QName.new(nil, 'namespace') +NillableAttrName = XSD::QName.new(nil, 'nillable') +ProcessContentsAttrName = XSD::QName.new(nil, 'processContents') +RefAttrName = XSD::QName.new(nil, 'ref') +SchemaLocationAttrName = XSD::QName.new(nil, 'schemaLocation') +TargetNamespaceAttrName = XSD::QName.new(nil, 'targetNamespace') +TypeAttrName = XSD::QName.new(nil, 'type') +UseAttrName = XSD::QName.new(nil, 'use') +ValueAttrName = XSD::QName.new(nil, 'value') + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/element.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/element.rb new file mode 100644 index 0000000000..fffb6485d0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/element.rb @@ -0,0 +1,154 @@ +# WSDL4R - XMLSchema element definition for WSDL. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module XMLSchema + + +class Element < Info + class << self + if RUBY_VERSION > "1.7.0" + def attr_reader_ref(symbol) + name = symbol.to_s + define_method(name) { + instance_variable_get("@#{name}") || + (refelement ? refelement.__send__(name) : nil) + } + end + else + def attr_reader_ref(symbol) + name = symbol.to_s + module_eval <<-EOS + def #{name} + @#{name} || (refelement ? refelement.#{name} : nil) + end + EOS + end + end + end + + attr_writer :name # required + attr_writer :form + attr_writer :type + attr_writer :local_simpletype + attr_writer :local_complextype + attr_writer :constraint + attr_writer :maxoccurs + attr_writer :minoccurs + attr_writer :nillable + + attr_reader_ref :name + attr_reader_ref :form + attr_reader_ref :type + attr_reader_ref :local_simpletype + attr_reader_ref :local_complextype + attr_reader_ref :constraint + attr_reader_ref :maxoccurs + attr_reader_ref :minoccurs + attr_reader_ref :nillable + + attr_accessor :ref + + def initialize(name = nil, type = nil) + super() + @name = name + @form = nil + @type = type + @local_simpletype = @local_complextype = nil + @constraint = nil + @maxoccurs = '1' + @minoccurs = '1' + @nillable = nil + @ref = nil + @refelement = nil + end + + def refelement + @refelement ||= (@ref ? root.collect_elements[@ref] : nil) + end + + def targetnamespace + parent.targetnamespace + end + + def elementformdefault + parent.elementformdefault + end + + def elementform + self.form.nil? ? parent.elementformdefault : self.form + end + + def parse_element(element) + case element + when SimpleTypeName + @local_simpletype = SimpleType.new + @local_simpletype + when ComplexTypeName + @type = nil + @local_complextype = ComplexType.new + @local_complextype + when UniqueName + @constraint = Unique.new + @constraint + else + nil + end + end + + def parse_attr(attr, value) + case attr + when NameAttrName + # namespace may be nil + if directelement? or elementform == 'qualified' + @name = XSD::QName.new(targetnamespace, value.source) + else + @name = XSD::QName.new(nil, value.source) + end + when FormAttrName + @form = value.source + when TypeAttrName + @type = value + when RefAttrName + @ref = value + when MaxOccursAttrName + if parent.is_a?(All) + if value.source != '1' + raise Parser::AttrConstraintError.new( + "cannot parse #{value} for #{attr}") + end + end + @maxoccurs = value.source + when MinOccursAttrName + if parent.is_a?(All) + unless ['0', '1'].include?(value.source) + raise Parser::AttrConstraintError.new( + "cannot parse #{value} for #{attr}") + end + end + @minoccurs = value.source + when NillableAttrName + @nillable = (value.source == 'true') + else + nil + end + end + +private + + def directelement? + parent.is_a?(Schema) + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/enumeration.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/enumeration.rb new file mode 100644 index 0000000000..5a16476032 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/enumeration.rb @@ -0,0 +1,36 @@ +# WSDL4R - XMLSchema enumeration definition for WSDL. +# Copyright (C) 2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module XMLSchema + + +class Enumeration < Info + def initialize + super + end + + def parse_element(element) + nil + end + + def parse_attr(attr, value) + case attr + when ValueAttrName + parent.enumeration << value.source + value.source + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/import.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/import.rb new file mode 100644 index 0000000000..d3487af934 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/import.rb @@ -0,0 +1,65 @@ +# WSDL4R - XMLSchema import definition. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'wsdl/xmlSchema/importer' + + +module WSDL +module XMLSchema + + +class Import < Info + attr_reader :namespace + attr_reader :schemalocation + attr_reader :content + + def initialize + super + @namespace = nil + @schemalocation = nil + @content = nil + end + + def parse_element(element) + nil + end + + def parse_attr(attr, value) + case attr + when NamespaceAttrName + @namespace = value.source + when SchemaLocationAttrName + @schemalocation = URI.parse(value.source) + if @schemalocation.relative? and !parent.location.nil? and + !parent.location.relative? + @schemalocation = parent.location + @schemalocation + end + if root.importedschema.key?(@schemalocation) + @content = root.importedschema[@schemalocation] + else + root.importedschema[@schemalocation] = nil # placeholder + @content = import(@schemalocation) + root.importedschema[@schemalocation] = @content + end + @schemalocation + else + nil + end + end + +private + + def import(location) + Importer.import(location, root) + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/importer.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/importer.rb new file mode 100644 index 0000000000..f57bfd972a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/importer.rb @@ -0,0 +1,87 @@ +# WSDL4R - XSD importer library. +# Copyright (C) 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/httpconfigloader' +require 'wsdl/xmlSchema/parser' + + +module WSDL +module XMLSchema + + +class Importer + def self.import(location, originalroot = nil) + new.import(location, originalroot) + end + + def initialize + @web_client = nil + end + + def import(location, originalroot = nil) + unless location.is_a?(URI) + location = URI.parse(location) + end + content = parse(fetch(location), location, originalroot) + content.location = location + content + end + +private + + def parse(content, location, originalroot) + opt = { + :location => location, + :originalroot => originalroot + } + WSDL::XMLSchema::Parser.new(opt).parse(content) + end + + def fetch(location) + warn("importing: #{location}") if $DEBUG + content = nil + if location.scheme == 'file' or + (location.relative? and FileTest.exist?(location.path)) + content = File.open(location.path).read + elsif location.scheme and location.scheme.size == 1 and + FileTest.exist?(location.to_s) + # ToDo: remove this ugly workaround for a path with drive letter + # (D://foo/bar) + content = File.open(location.to_s).read + else + client = web_client.new(nil, "WSDL4R") + client.proxy = ::SOAP::Env::HTTP_PROXY + client.no_proxy = ::SOAP::Env::NO_PROXY + if opt = ::SOAP::Property.loadproperty(::SOAP::PropertyName) + ::SOAP::HTTPConfigLoader.set_options(client, + opt["client.protocol.http"]) + end + content = client.get_content(location) + end + content + end + + def web_client + @web_client ||= begin + require 'http-access2' + if HTTPAccess2::VERSION < "2.0" + raise LoadError.new("http-access/2.0 or later is required.") + end + HTTPAccess2::Client + rescue LoadError + warn("Loading http-access2 failed. Net/http is used.") if $DEBUG + require 'soap/netHttpClient' + ::SOAP::NetHttpClient + end + @web_client + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/include.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/include.rb new file mode 100644 index 0000000000..af1ef942bb --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/include.rb @@ -0,0 +1,54 @@ +# WSDL4R - XMLSchema include definition. +# Copyright (C) 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'wsdl/xmlSchema/importer' + + +module WSDL +module XMLSchema + + +class Include < Info + attr_reader :schemalocation + attr_reader :content + + def initialize + super + @schemalocation = nil + @content = nil + end + + def parse_element(element) + nil + end + + def parse_attr(attr, value) + case attr + when SchemaLocationAttrName + @schemalocation = URI.parse(value.source) + if @schemalocation.relative? + @schemalocation = parent.location + @schemalocation + end + @content = import(@schemalocation) + @schemalocation + else + nil + end + end + +private + + def import(location) + Importer.import(location) + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/length.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/length.rb new file mode 100644 index 0000000000..7f61602da9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/length.rb @@ -0,0 +1,35 @@ +# WSDL4R - XMLSchema length definition for WSDL. +# Copyright (C) 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module XMLSchema + + +class Length < Info + def initialize + super + end + + def parse_element(element) + nil + end + + def parse_attr(attr, value) + case attr + when ValueAttrName + value.source + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/parser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/parser.rb new file mode 100644 index 0000000000..057d9d9b70 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/parser.rb @@ -0,0 +1,166 @@ +# WSDL4R - WSDL XML Instance parser library. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/qname' +require 'xsd/ns' +require 'xsd/charset' +require 'xsd/datatypes' +require 'xsd/xmlparser' +require 'wsdl/xmlSchema/data' + + +module WSDL +module XMLSchema + + +class Parser + include XSD + + class ParseError < Error; end + class FormatDecodeError < ParseError; end + class UnknownElementError < FormatDecodeError; end + class UnknownAttributeError < FormatDecodeError; end + class UnexpectedElementError < FormatDecodeError; end + class ElementConstraintError < FormatDecodeError; end + class AttributeConstraintError < FormatDecodeError; end + +private + + class ParseFrame + attr_reader :ns + attr_reader :name + attr_accessor :node + + private + + def initialize(ns, name, node) + @ns = ns + @name = name + @node = node + end + end + +public + + def initialize(opt = {}) + @parser = XSD::XMLParser.create_parser(self, opt) + @parsestack = nil + @lastnode = nil + @ignored = {} + @location = opt[:location] + @originalroot = opt[:originalroot] + end + + def parse(string_or_readable) + @parsestack = [] + @lastnode = nil + @textbuf = '' + @parser.do_parse(string_or_readable) + @lastnode + end + + def charset + @parser.charset + end + + def start_element(name, attrs) + lastframe = @parsestack.last + ns = parent = nil + if lastframe + ns = lastframe.ns.clone_ns + parent = lastframe.node + else + ns = XSD::NS.new + parent = nil + end + attrs = XSD::XMLParser.filter_ns(ns, attrs) + node = decode_tag(ns, name, attrs, parent) + @parsestack << ParseFrame.new(ns, name, node) + end + + def characters(text) + lastframe = @parsestack.last + if lastframe + # Need not to be cloned because character does not have attr. + ns = lastframe.ns + decode_text(ns, text) + else + p text if $DEBUG + end + end + + def end_element(name) + lastframe = @parsestack.pop + unless name == lastframe.name + raise UnexpectedElementError.new("closing element name '#{name}' does not match with opening element '#{lastframe.name}'") + end + decode_tag_end(lastframe.ns, lastframe.node) + @lastnode = lastframe.node + end + +private + + def decode_tag(ns, name, attrs, parent) + o = nil + elename = ns.parse(name) + if !parent + if elename == SchemaName + o = Schema.parse_element(elename) + o.location = @location + else + raise UnknownElementError.new("unknown element: #{elename}") + end + o.root = @originalroot if @originalroot # o.root = o otherwise + else + if elename == AnnotationName + # only the first annotation element is allowed for each element. + o = Annotation.new + else + o = parent.parse_element(elename) + end + unless o + unless @ignored.key?(elename) + warn("ignored element: #{elename} of #{parent.class}") + @ignored[elename] = elename + end + o = Documentation.new # which accepts any element. + end + # node could be a pseudo element. pseudo element has its own parent. + o.root = parent.root + o.parent = parent if o.parent.nil? + end + attrs.each do |key, value| + attr_ele = ns.parse(key, true) + value_ele = ns.parse(value, true) + value_ele.source = value # for recovery; value may not be a QName + if attr_ele == IdAttrName + o.id = value_ele + else + unless o.parse_attr(attr_ele, value_ele) + unless @ignored.key?(attr_ele) + warn("ignored attr: #{attr_ele}") + @ignored[attr_ele] = attr_ele + end + end + end + end + o + end + + def decode_tag_end(ns, node) + node.parse_epilogue + end + + def decode_text(ns, text) + @textbuf << text + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/pattern.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/pattern.rb new file mode 100644 index 0000000000..f826be4578 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/pattern.rb @@ -0,0 +1,36 @@ +# WSDL4R - XMLSchema pattern definition for WSDL. +# Copyright (C) 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module XMLSchema + + +class Pattern < Info + def initialize + super + end + + def parse_element(element) + nil + end + + def parse_attr(attr, value) + case attr + when ValueAttrName + parent.pattern = /\A#{value.source}\z/n + value.source + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/schema.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/schema.rb new file mode 100644 index 0000000000..ec97d07aa5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/schema.rb @@ -0,0 +1,143 @@ +# WSDL4R - XMLSchema schema definition for WSDL. +# Copyright (C) 2002, 2003-2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'xsd/namedelements' + + +module WSDL +module XMLSchema + + +class Schema < Info + attr_reader :targetnamespace # required + attr_reader :complextypes + attr_reader :simpletypes + attr_reader :elements + attr_reader :attributes + attr_reader :imports + attr_accessor :attributeformdefault + attr_accessor :elementformdefault + + attr_reader :importedschema + + def initialize + super + @targetnamespace = nil + @complextypes = XSD::NamedElements.new + @simpletypes = XSD::NamedElements.new + @elements = XSD::NamedElements.new + @attributes = XSD::NamedElements.new + @imports = [] + @attributeformdefault = "unqualified" + @elementformdefault = "unqualified" + @importedschema = {} + @location = nil + @root = self + end + + def location + @location || (root.nil? ? nil : root.location) + end + + def location=(location) + @location = location + end + + def parse_element(element) + case element + when ImportName + o = Import.new + @imports << o + o + when IncludeName + o = Include.new + @imports << o + o + when ComplexTypeName + o = ComplexType.new + @complextypes << o + o + when SimpleTypeName + o = SimpleType.new + @simpletypes << o + o + when ElementName + o = Element.new + @elements << o + o + when AttributeName + o = Attribute.new + @attributes << o + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when TargetNamespaceAttrName + @targetnamespace = value.source + when AttributeFormDefaultAttrName + @attributeformdefault = value.source + when ElementFormDefaultAttrName + @elementformdefault = value.source + else + nil + end + end + + def collect_attributes + result = XSD::NamedElements.new + result.concat(@attributes) + @imports.each do |import| + result.concat(import.content.collect_attributes) if import.content + end + result + end + + def collect_elements + result = XSD::NamedElements.new + result.concat(@elements) + @imports.each do |import| + result.concat(import.content.collect_elements) if import.content + end + result + end + + def collect_complextypes + result = XSD::NamedElements.new + result.concat(@complextypes) + @imports.each do |import| + result.concat(import.content.collect_complextypes) if import.content + end + result + end + + def collect_simpletypes + result = XSD::NamedElements.new + result.concat(@simpletypes) + @imports.each do |import| + result.concat(import.content.collect_simpletypes) if import.content + end + result + end + + def self.parse_element(element) + if element == SchemaName + Schema.new + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/sequence.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/sequence.rb new file mode 100644 index 0000000000..823fa3b7f9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/sequence.rb @@ -0,0 +1,69 @@ +# WSDL4R - XMLSchema complexType definition for WSDL. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module XMLSchema + + +class Sequence < Info + attr_reader :minoccurs + attr_reader :maxoccurs + attr_reader :elements + + def initialize + super() + @minoccurs = '1' + @maxoccurs = '1' + @elements = [] + end + + def targetnamespace + parent.targetnamespace + end + + def elementformdefault + parent.elementformdefault + end + + def <<(element) + @elements << element + end + + def parse_element(element) + case element + when AnyName + o = Any.new + @elements << o + o + when ElementName + o = Element.new + @elements << o + o + else + nil + end + end + + def parse_attr(attr, value) + case attr + when MaxOccursAttrName + @maxoccurs = value.source + when MinOccursAttrName + @minoccurs = value.source + else + nil + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/simpleContent.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/simpleContent.rb new file mode 100644 index 0000000000..e1f35c88b8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/simpleContent.rb @@ -0,0 +1,65 @@ +# WSDL4R - XMLSchema simpleContent definition for WSDL. +# Copyright (C) 2004, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'xsd/namedelements' + + +module WSDL +module XMLSchema + + +class SimpleContent < Info + attr_reader :restriction + attr_reader :extension + + def check_lexical_format(value) + check(value) + end + + def initialize + super + @restriction = nil + @extension = nil + end + + def base + content.base + end + + def targetnamespace + parent.targetnamespace + end + + def parse_element(element) + case element + when RestrictionName + @restriction = SimpleRestriction.new + @restriction + when ExtensionName + @extension = SimpleExtension.new + @extension + end + end + +private + + def content + @restriction || @extension + end + + def check(value) + unless content.valid?(value) + raise XSD::ValueSpaceError.new("#{@name}: cannot accept '#{value}'") + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/simpleExtension.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/simpleExtension.rb new file mode 100644 index 0000000000..3c53a7328c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/simpleExtension.rb @@ -0,0 +1,54 @@ +# WSDL4R - XMLSchema simpleType extension definition for WSDL. +# Copyright (C) 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'xsd/namedelements' + + +module WSDL +module XMLSchema + + +class SimpleExtension < Info + attr_reader :base + attr_reader :attributes + + def initialize + super + @base = nil + @attributes = XSD::NamedElements.new + end + + def targetnamespace + parent.targetnamespace + end + + def valid?(value) + true + end + + def parse_element(element) + case element + when AttributeName + o = Attribute.new + @attributes << o + o + end + end + + def parse_attr(attr, value) + case attr + when BaseAttrName + @base = value + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/simpleRestriction.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/simpleRestriction.rb new file mode 100644 index 0000000000..e8bf3ebfa5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/simpleRestriction.rb @@ -0,0 +1,73 @@ +# WSDL4R - XMLSchema simpleContent restriction definition for WSDL. +# Copyright (C) 2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'xsd/namedelements' + + +module WSDL +module XMLSchema + + +class SimpleRestriction < Info + attr_reader :base + attr_reader :enumeration + attr_accessor :length + attr_accessor :pattern + + def initialize + super + @base = nil + @enumeration = [] # NamedElements? + @length = nil + @pattern = nil + end + + def valid?(value) + return false unless check_restriction(value) + return false unless check_length(value) + return false unless check_pattern(value) + true + end + + def parse_element(element) + case element + when EnumerationName + Enumeration.new # just a parsing handler + when LengthName + Length.new # just a parsing handler + when PatternName + Pattern.new # just a parsing handler + end + end + + def parse_attr(attr, value) + case attr + when BaseAttrName + @base = value + end + end + +private + + def check_restriction(value) + @enumeration.empty? or @enumeration.include?(value) + end + + def check_length(value) + @length.nil? or value.size == @length + end + + def check_pattern(value) + @pattern.nil? or @pattern =~ value + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/simpleType.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/simpleType.rb new file mode 100644 index 0000000000..e808c318c4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/simpleType.rb @@ -0,0 +1,73 @@ +# WSDL4R - XMLSchema simpleType definition for WSDL. +# Copyright (C) 2004, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'xsd/namedelements' + + +module WSDL +module XMLSchema + + +class SimpleType < Info + attr_accessor :name + attr_reader :restriction + + def check_lexical_format(value) + if @restriction + check_restriction(value) + else + raise ArgumentError.new("incomplete simpleType") + end + end + + def base + if @restriction + @restriction.base + else + raise ArgumentError.new("incomplete simpleType") + end + end + + def initialize(name = nil) + super() + @name = name + @restriction = nil + end + + def targetnamespace + parent.targetnamespace + end + + def parse_element(element) + case element + when RestrictionName + @restriction = SimpleRestriction.new + @restriction + end + end + + def parse_attr(attr, value) + case attr + when NameAttrName + @name = XSD::QName.new(targetnamespace, value.source) + end + end + +private + + def check_restriction(value) + unless @restriction.valid?(value) + raise XSD::ValueSpaceError.new("#{@name}: cannot accept '#{value}'") + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/unique.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/unique.rb new file mode 100644 index 0000000000..837ff22b4a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/unique.rb @@ -0,0 +1,34 @@ +# WSDL4R - XMLSchema unique element. +# Copyright (C) 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' + + +module WSDL +module XMLSchema + + +class Unique < Info + def initialize + super + end + + def parse_element(element) + # Accepts any element. + self + end + + def parse_attr(attr, value) + # Accepts any attribute. + true + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/xsd2ruby.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/xsd2ruby.rb new file mode 100644 index 0000000000..afe5fc5ada --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/wsdl/xmlSchema/xsd2ruby.rb @@ -0,0 +1,107 @@ +# XSD4R - XSD to ruby mapping library. +# Copyright (C) 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/codegen/gensupport' +require 'wsdl/xmlSchema/importer' +require 'wsdl/soap/classDefCreator' + + +module WSDL +module XMLSchema + + +class XSD2Ruby + attr_accessor :location + attr_reader :opt + attr_accessor :logger + attr_accessor :basedir + + def run + unless @location + raise RuntimeError, "XML Schema location not given" + end + @xsd = import(@location) + @name = create_classname(@xsd) + create_file + end + +private + + def initialize + @location = nil + @opt = {} + @logger = Logger.new(STDERR) + @basedir = nil + @xsd = nil + @name = nil + end + + def create_file + create_classdef + end + + def create_classdef + @logger.info { "Creating class definition." } + @classdef_filename = @name + '.rb' + check_file(@classdef_filename) or return + write_file(@classdef_filename) do |f| + f << WSDL::SOAP::ClassDefCreator.new(@xsd).dump + end + end + + def write_file(filename) + if @basedir + filename = File.join(basedir, filename) + end + File.open(filename, "w") do |f| + yield f + end + end + + def check_file(filename) + if @basedir + filename = File.join(basedir, filename) + end + if FileTest.exist?(filename) + if @opt.key?('force') + @logger.warn { + "File '#{filename}' exists but overrides it." + } + true + else + @logger.warn { + "File '#{filename}' exists. #{$0} did not override it." + } + false + end + else + @logger.info { "Creates file '#{filename}'." } + true + end + end + + def create_classname(xsd) + name = nil + if xsd.targetnamespace + name = xsd.targetnamespace.scan(/[a-zA-Z0-9]+$/)[0] + end + if name.nil? + 'default' + else + XSD::CodeGen::GenSupport.safevarname(name) + end + end + + def import(location) + WSDL::XMLSchema::Importer.import(location) + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/base64.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/base64.rb new file mode 100644 index 0000000000..0fe5b824b3 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/base64.rb @@ -0,0 +1,81 @@ +=begin += xmlrpc/base64.rb +Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) + +Released under the same term of license as Ruby. + += Classes +* (()) + += XMLRPC::Base64 +== Description +This class is necessary for (('xmlrpc4r')) to determine that a string should +be transmitted base64-encoded and not as a raw-string. +You can use (({XMLRPC::Base64})) on the client and server-side as a +parameter and/or return-value. + +== Class Methods +--- XMLRPC::Base64.new( str, state = :dec ) + Creates a new (({XMLRPC::Base64})) instance with string ((|str|)) as the + internal string. When ((|state|)) is (({:dec})) it assumes that the + string ((|str|)) is not in base64 format (perhaps already decoded), + otherwise if ((|state|)) is (({:enc})) it decodes ((|str|)) + and stores it as the internal string. + +--- XMLRPC::Base64.decode( str ) + Decodes string ((|str|)) with base64 and returns that value. + +--- XMLRPC::Base64.encode( str ) + Encodes string ((|str|)) with base64 and returns that value. + +== Instance Methods +--- XMLRPC::Base64#decoded + Returns the internal string decoded. + +--- XMLRPC::Base64#encoded + Returns the internal string encoded with base64. + +=end + +module XMLRPC + +class Base64 + + def initialize(str, state = :dec) + case state + when :enc + @str = Base64.decode(str) + when :dec + @str = str + else + raise ArgumentError, "wrong argument; either :enc or :dec" + end + end + + def decoded + @str + end + + def encoded + Base64.encode(@str) + end + + + def Base64.decode(str) + str.gsub(/\s+/, "").unpack("m")[0] + end + + def Base64.encode(str) + [str].pack("m") + end + +end + + +end # module XMLRPC + + +=begin += History + $Id: base64.rb 11708 2007-02-12 23:01:19Z shyouhei $ +=end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/client.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/client.rb new file mode 100644 index 0000000000..398b21bd0f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/client.rb @@ -0,0 +1,616 @@ +=begin += xmlrpc/client.rb +Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) + +Released under the same term of license as Ruby. + += Classes +* (()) +* (()) + + += XMLRPC::Client +== Synopsis + require "xmlrpc/client" + + server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80) + begin + param = server.call("michael.add", 4, 5) + puts "4 + 5 = #{param}" + rescue XMLRPC::FaultException => e + puts "Error:" + puts e.faultCode + puts e.faultString + end + +or + + require "xmlrpc/client" + + server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80) + ok, param = server.call2("michael.add", 4, 5) + if ok then + puts "4 + 5 = #{param}" + else + puts "Error:" + puts param.faultCode + puts param.faultString + end + +== Description +Class (({XMLRPC::Client})) provides remote procedure calls to a XML-RPC server. +After setting the connection-parameters with (()) which +creates a new (({XMLRPC::Client})) instance, you can execute a remote procedure +by sending the (()) or (()) +message to this new instance. The given parameters indicate which method to +call on the remote-side and of course the parameters for the remote procedure. + +== Class Methods +--- XMLRPC::Client.new( host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil, user=nil, password=nil, use_ssl=false, timeout =nil) + Creates an object which represents the remote XML-RPC server on the + given host ((|host|)). If the server is CGI-based, ((|path|)) is the + path to the CGI-script, which will be called, otherwise (in the + case of a standalone server) ((|path|)) should be (({"/RPC2"})). + ((|port|)) is the port on which the XML-RPC server listens. + If ((|proxy_host|)) is given, then a proxy server listening at + ((|proxy_host|)) is used. ((|proxy_port|)) is the port of the + proxy server. + + Default values for ((|host|)), ((|path|)) and ((|port|)) are 'localhost', '/RPC2' and + '80' respectively using SSL '443'. + + If ((|user|)) and ((|password|)) are given, each time a request is send, + a Authorization header is send. Currently only Basic Authentification is + implemented no Digest. + + If ((|use_ssl|)) is set to (({true})), comunication over SSL is enabled. + Note, that you need the SSL package from RAA installed. + + Parameter ((|timeout|)) is the time to wait for a XML-RPC response, defaults to 30. + +--- XMLRPC::Client.new2( uri, proxy=nil, timeout=nil) +--- XMLRPC::Client.new_from_uri( uri, proxy=nil, timeout=nil) +: uri + URI specifying protocol (http or https), host, port, path, user and password. + Example: https://user:password@host:port/path + +: proxy + Is of the form "host:port". + +: timeout + Defaults to 30. + +--- XMLRPC::Client.new3( hash={} ) +--- XMLRPC::Client.new_from_hash( hash={} ) + Parameter ((|hash|)) has following case-insensitive keys: + * host + * path + * port + * proxy_host + * proxy_port + * user + * password + * use_ssl + * timeout + + Calls (()) with the corresponding values. + +== Instance Methods +--- XMLRPC::Client#call( method, *args ) + Invokes the method named ((|method|)) with the parameters given by + ((|args|)) on the XML-RPC server. + The parameter ((|method|)) is converted into a (({String})) and should + be a valid XML-RPC method-name. + Each parameter of ((|args|)) must be of one of the following types, + where (({Hash})), (({Struct})) and (({Array})) can contain any of these listed ((:types:)): + * (({Fixnum})), (({Bignum})) + * (({TrueClass})), (({FalseClass})) ((({true})), (({false}))) + * (({String})), (({Symbol})) + * (({Float})) + * (({Hash})), (({Struct})) + * (({Array})) + * (({Date})), (({Time})), (({XMLRPC::DateTime})) + * (({XMLRPC::Base64})) + * A Ruby object which class includes XMLRPC::Marshallable (only if Config::ENABLE_MARSHALLABLE is (({true}))). + That object is converted into a hash, with one additional key/value pair "___class___" which contains the class name + for restoring later that object. + + The method returns the return-value from the RPC + ((-stands for Remote Procedure Call-)). + The type of the return-value is one of the above shown, + only that a (({Bignum})) is only allowed when it fits in 32-bit and + that a XML-RPC (('dateTime.iso8601')) type is always returned as + a ((<(({XMLRPC::DateTime}))|URL:datetime.html>)) object and + a (({Struct})) is never returned, only a (({Hash})), the same for a (({Symbol})), where + always a (({String})) is returned. + A (({XMLRPC::Base64})) is returned as a (({String})) from xmlrpc4r version 1.6.1 on. + + If the remote procedure returned a fault-structure, then a + (({XMLRPC::FaultException})) exception is raised, which has two accessor-methods + (({faultCode})) and (({faultString})) of type (({Integer})) and (({String})). + +--- XMLRPC::Client#call2( method, *args ) + The difference between this method and (()) is, that + this method do ((*not*)) raise a (({XMLRPC::FaultException})) exception. + The method returns an array of two values. The first value indicates if + the second value is a return-value ((({true}))) or an object of type + (({XMLRPC::FaultException})). + Both are explained in (()). + + Simple to remember: The "2" in "call2" denotes the number of values it returns. + +--- XMLRPC::Client#multicall( *methods ) + You can use this method to execute several methods on a XMLRPC server which supports + the multi-call extension. + Example: + + s.multicall( + ['michael.add', 3, 4], + ['michael.sub', 4, 5] + ) + # => [7, -1] + +--- XMLRPC::Client#multicall2( *methods ) + Same as (()), but returns like (()) two parameters + instead of raising an (({XMLRPC::FaultException})). + +--- XMLRPC::Client#proxy( prefix, *args ) + Returns an object of class (({XMLRPC::Client::Proxy})), initialized with + ((|prefix|)) and ((|args|)). A proxy object returned by this method behaves + like (()), i.e. a call on that object will raise a + (({XMLRPC::FaultException})) when a fault-structure is returned by that call. + +--- XMLRPC::Client#proxy2( prefix, *args ) + Almost the same like (()) only that a call on the returned + (({XMLRPC::Client::Proxy})) object behaves like (()), i.e. + a call on that object will return two parameters. + + + + +--- XMLRPC::Client#call_async(...) +--- XMLRPC::Client#call2_async(...) +--- XMLRPC::Client#multicall_async(...) +--- XMLRPC::Client#multicall2_async(...) +--- XMLRPC::Client#proxy_async(...) +--- XMLRPC::Client#proxy2_async(...) + In contrast to corresponding methods without "_async", these can be + called concurrently and use for each request a new connection, where the + non-asynchronous counterparts use connection-alive (one connection for all requests) + if possible. + + Note, that you have to use Threads to call these methods concurrently. + The following example calls two methods concurrently: + + Thread.new { + p client.call_async("michael.add", 4, 5) + } + + Thread.new { + p client.call_async("michael.div", 7, 9) + } + + +--- XMLRPC::Client#timeout +--- XMLRPC::Client#user +--- XMLRPC::Client#password + Return the corresponding attributes. + +--- XMLRPC::Client#timeout= (new_timeout) +--- XMLRPC::Client#user= (new_user) +--- XMLRPC::Client#password= (new_password) + Set the corresponding attributes. + + +--- XMLRPC::Client#set_writer( writer ) + Sets the XML writer to use for generating XML output. + Should be an instance of a class from module (({XMLRPC::XMLWriter})). + If this method is not called, then (({XMLRPC::Config::DEFAULT_WRITER})) is used. + +--- XMLRPC::Client#set_parser( parser ) + Sets the XML parser to use for parsing XML documents. + Should be an instance of a class from module (({XMLRPC::XMLParser})). + If this method is not called, then (({XMLRPC::Config::DEFAULT_PARSER})) is used. + +--- XMLRPC::Client#cookie +--- XMLRPC::Client#cookie= (cookieString) + Get and set the HTTP Cookie header. + +--- XMLRPC::Client#http_header_extra= (additionalHeaders) + Set extra HTTP headers that are included in the request. + +--- XMLRPC::Client#http_header_extra + Access the via (()) assigned header. + +--- XMLRPC::Client#http_last_response + Returns the (({Net::HTTPResponse})) object of the last RPC. + += XMLRPC::Client::Proxy +== Synopsis + require "xmlrpc/client" + + server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80) + + michael = server.proxy("michael") + michael2 = server.proxy("michael", 4) + + # both calls should return the same value '9'. + p michael.add(4,5) + p michael2.add(5) + +== Description +Class (({XMLRPC::Client::Proxy})) makes XML-RPC calls look nicer! +You can call any method onto objects of that class - the object handles +(({method_missing})) and will forward the method call to a XML-RPC server. +Don't use this class directly, but use instead method (()) or +(()). + +== Class Methods +--- XMLRPC::Client::Proxy.new( server, prefix, args=[], meth=:call, delim="." ) + Creates an object which provides (({method_missing})). + + ((|server|)) must be of type (({XMLRPC::Client})), which is the XML-RPC server to be used + for a XML-RPC call. ((|prefix|)) and ((|delim|)) will be prepended to the methodname + called onto this object. + + Parameter ((|meth|)) is the method (call, call2, call_async, call2_async) to use for + a RPC. + + ((|args|)) are arguments which are automatically given + to every XML-RPC call before the arguments provides through (({method_missing})). + +== Instance Methods +Every method call is forwarded to the XML-RPC server defined in (()). + +Note: Inherited methods from class (({Object})) cannot be used as XML-RPC names, because they get around +(({method_missing})). + + + += History + $Id: client.rb 11820 2007-02-23 03:47:59Z knu $ + +=end + + + +require "xmlrpc/parser" +require "xmlrpc/create" +require "xmlrpc/config" +require "xmlrpc/utils" # ParserWriterChooseMixin +require "net/http" + +module XMLRPC + + class Client + + USER_AGENT = "XMLRPC::Client (Ruby #{RUBY_VERSION})" + + include ParserWriterChooseMixin + include ParseContentType + + + # Constructors ------------------------------------------------------------------- + + def initialize(host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil, + user=nil, password=nil, use_ssl=nil, timeout=nil) + + @http_header_extra = nil + @http_last_response = nil + @cookie = nil + + @host = host || "localhost" + @path = path || "/RPC2" + @proxy_host = proxy_host + @proxy_port = proxy_port + @proxy_host ||= 'localhost' if @proxy_port != nil + @proxy_port ||= 8080 if @proxy_host != nil + @use_ssl = use_ssl || false + @timeout = timeout || 30 + + if use_ssl + require "net/https" + @port = port || 443 + else + @port = port || 80 + end + + @user, @password = user, password + + set_auth + + # convert ports to integers + @port = @port.to_i if @port != nil + @proxy_port = @proxy_port.to_i if @proxy_port != nil + + # HTTP object for synchronous calls + Net::HTTP.version_1_2 + @http = Net::HTTP.new(@host, @port, @proxy_host, @proxy_port) + @http.use_ssl = @use_ssl if @use_ssl + @http.read_timeout = @timeout + @http.open_timeout = @timeout + + @parser = nil + @create = nil + end + + + class << self + + def new2(uri, proxy=nil, timeout=nil) + if match = /^([^:]+):\/\/(([^@]+)@)?([^\/]+)(\/.*)?$/.match(uri) + proto = match[1] + user, passwd = (match[3] || "").split(":") + host, port = match[4].split(":") + path = match[5] + + if proto != "http" and proto != "https" + raise "Wrong protocol specified. Only http or https allowed!" + end + + else + raise "Wrong URI as parameter!" + end + + proxy_host, proxy_port = (proxy || "").split(":") + + self.new(host, path, port, proxy_host, proxy_port, user, passwd, (proto == "https"), timeout) + end + + alias new_from_uri new2 + + def new3(hash={}) + + # convert all keys into lowercase strings + h = {} + hash.each { |k,v| h[k.to_s.downcase] = v } + + self.new(h['host'], h['path'], h['port'], h['proxy_host'], h['proxy_port'], h['user'], h['password'], + h['use_ssl'], h['timeout']) + end + + alias new_from_hash new3 + + end + + + # Attribute Accessors ------------------------------------------------------------------- + + # add additional HTTP headers to the request + attr_accessor :http_header_extra + + # makes last HTTP response accessible + attr_reader :http_last_response + + # Cookie support + attr_accessor :cookie + + + attr_reader :timeout, :user, :password + + def timeout=(new_timeout) + @timeout = new_timeout + @http.read_timeout = @timeout + @http.open_timeout = @timeout + end + + def user=(new_user) + @user = new_user + set_auth + end + + def password=(new_password) + @password = new_password + set_auth + end + + # Call methods -------------------------------------------------------------- + + def call(method, *args) + ok, param = call2(method, *args) + if ok + param + else + raise param + end + end + + def call2(method, *args) + request = create().methodCall(method, *args) + data = do_rpc(request, false) + parser().parseMethodResponse(data) + end + + def call_async(method, *args) + ok, param = call2_async(method, *args) + if ok + param + else + raise param + end + end + + def call2_async(method, *args) + request = create().methodCall(method, *args) + data = do_rpc(request, true) + parser().parseMethodResponse(data) + end + + + # Multicall methods -------------------------------------------------------------- + + def multicall(*methods) + ok, params = multicall2(*methods) + if ok + params + else + raise params + end + end + + def multicall2(*methods) + gen_multicall(methods, false) + end + + def multicall_async(*methods) + ok, params = multicall2_async(*methods) + if ok + params + else + raise params + end + end + + def multicall2_async(*methods) + gen_multicall(methods, true) + end + + + # Proxy generating methods ------------------------------------------ + + def proxy(prefix=nil, *args) + Proxy.new(self, prefix, args, :call) + end + + def proxy2(prefix=nil, *args) + Proxy.new(self, prefix, args, :call2) + end + + def proxy_async(prefix=nil, *args) + Proxy.new(self, prefix, args, :call_async) + end + + def proxy2_async(prefix=nil, *args) + Proxy.new(self, prefix, args, :call2_async) + end + + + private # ---------------------------------------------------------- + + def set_auth + if @user.nil? + @auth = nil + else + a = "#@user" + a << ":#@password" if @password != nil + @auth = ("Basic " + [a].pack("m")).chomp + end + end + + def do_rpc(request, async=false) + header = { + "User-Agent" => USER_AGENT, + "Content-Type" => "text/xml; charset=utf-8", + "Content-Length" => request.size.to_s, + "Connection" => (async ? "close" : "keep-alive") + } + + header["Cookie"] = @cookie if @cookie + header.update(@http_header_extra) if @http_header_extra + + if @auth != nil + # add authorization header + header["Authorization"] = @auth + end + + resp = nil + @http_last_response = nil + + if async + # use a new HTTP object for each call + Net::HTTP.version_1_2 + http = Net::HTTP.new(@host, @port, @proxy_host, @proxy_port) + http.use_ssl = @use_ssl if @use_ssl + http.read_timeout = @timeout + http.open_timeout = @timeout + + # post request + http.start { + resp = http.post2(@path, request, header) + } + else + # reuse the HTTP object for each call => connection alive is possible + + # post request + resp = @http.post2(@path, request, header) + end + + @http_last_response = resp + + data = resp.body + + if resp.code == "401" + # Authorization Required + raise "Authorization failed.\nHTTP-Error: #{resp.code} #{resp.message}" + elsif resp.code[0,1] != "2" + raise "HTTP-Error: #{resp.code} #{resp.message}" + end + + ct = parse_content_type(resp["Content-Type"]).first + if ct != "text/xml" + if ct == "text/html" + raise "Wrong content-type: \n#{data}" + else + raise "Wrong content-type" + end + end + + expected = resp["Content-Length"] || "" + if data.nil? or data.size == 0 + raise "Wrong size. Was #{data.size}, should be #{expected}" + elsif expected != "" and expected.to_i != data.size and resp["Transfer-Encoding"].nil? + raise "Wrong size. Was #{data.size}, should be #{expected}" + end + + c = resp["Set-Cookie"] + @cookie = c if c + + return data + end + + def gen_multicall(methods=[], async=false) + meth = :call2 + meth = :call2_async if async + + ok, params = self.send(meth, "system.multicall", + methods.collect {|m| {'methodName' => m[0], 'params' => m[1..-1]} } + ) + + if ok + params = params.collect do |param| + if param.is_a? Array + param[0] + elsif param.is_a? Hash + XMLRPC::FaultException.new(param["faultCode"], param["faultString"]) + else + raise "Wrong multicall return value" + end + end + end + + return ok, params + end + + + + class Proxy + + def initialize(server, prefix, args=[], meth=:call, delim=".") + @server = server + @prefix = prefix ? prefix + delim : "" + @args = args + @meth = meth + end + + def method_missing(mid, *args) + pre = @prefix + mid.to_s + arg = @args + args + @server.send(@meth, pre, *arg) + end + + end # class Proxy + + end # class Client + +end # module XMLRPC + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/config.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/config.rb new file mode 100644 index 0000000000..52f7340aba --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/config.rb @@ -0,0 +1,40 @@ +# +# $Id: config.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# Configuration file for XML-RPC for Ruby +# + +module XMLRPC + + module Config + + DEFAULT_WRITER = XMLWriter::Simple # or XMLWriter::XMLParser + + # available parser: + # * XMLParser::NQXMLTreeParser + # * XMLParser::NQXMLStreamParser + # * XMLParser::XMLTreeParser + # * XMLParser::XMLStreamParser (fastest) + # * XMLParser::REXMLStreamParser + # * XMLParser::XMLScanStreamParser + DEFAULT_PARSER = XMLParser::REXMLStreamParser + + # enable tag + ENABLE_NIL_CREATE = false + ENABLE_NIL_PARSER = false + + # allows integers greater than 32-bit if true + ENABLE_BIGINT = false + + # enable marshalling ruby objects which include XMLRPC::Marshallable + ENABLE_MARSHALLING = true + + # enable multiCall extension by default + ENABLE_MULTICALL = false + + # enable Introspection extension by default + ENABLE_INTROSPECTION = false + + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/create.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/create.rb new file mode 100644 index 0000000000..0b007c5661 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/create.rb @@ -0,0 +1,290 @@ +# +# Creates XML-RPC call/response documents +# +# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) +# +# $Id: create.rb 11820 2007-02-23 03:47:59Z knu $ +# + +require "date" +require "xmlrpc/base64" + +module XMLRPC + + module XMLWriter + + class Abstract + def ele(name, *children) + element(name, nil, *children) + end + + def tag(name, txt) + element(name, nil, text(txt)) + end + end + + + class Simple < Abstract + + def document_to_str(doc) + doc + end + + def document(*params) + params.join("") + end + + def pi(name, *params) + "" + end + + def element(name, attrs, *children) + raise "attributes not yet implemented" unless attrs.nil? + if children.empty? + "<#{name}/>" + else + "<#{name}>" + children.join("") + "" + end + end + + def text(txt) + cleaned = txt.dup + cleaned.gsub!(/&/, '&') + cleaned.gsub!(//, '>') + cleaned + end + + end # class Simple + + + class XMLParser < Abstract + + def initialize + require "xmltreebuilder" + end + + def document_to_str(doc) + doc.to_s + end + + def document(*params) + XML::SimpleTree::Document.new(*params) + end + + def pi(name, *params) + XML::SimpleTree::ProcessingInstruction.new(name, *params) + end + + def element(name, attrs, *children) + XML::SimpleTree::Element.new(name, attrs, *children) + end + + def text(txt) + XML::SimpleTree::Text.new(txt) + end + + end # class XMLParser + + Classes = [Simple, XMLParser] + + # yields an instance of each installed XML writer + def self.each_installed_writer + XMLRPC::XMLWriter::Classes.each do |klass| + begin + yield klass.new + rescue LoadError + end + end + end + + end # module XMLWriter + + class Create + + def initialize(xml_writer = nil) + @writer = xml_writer || Config::DEFAULT_WRITER.new + end + + + def methodCall(name, *params) + name = name.to_s + + if name !~ /[a-zA-Z0-9_.:\/]+/ + raise ArgumentError, "Wrong XML-RPC method-name" + end + + parameter = params.collect do |param| + @writer.ele("param", conv2value(param)) + end + + tree = @writer.document( + @writer.pi("xml", 'version="1.0"'), + @writer.ele("methodCall", + @writer.tag("methodName", name), + @writer.ele("params", *parameter) + ) + ) + + @writer.document_to_str(tree) + "\n" + end + + + + # + # generates a XML-RPC methodResponse document + # + # if is_ret == false then the params array must + # contain only one element, which is a structure + # of a fault return-value. + # + # if is_ret == true then a normal + # return-value of all the given params is created. + # + def methodResponse(is_ret, *params) + + if is_ret + resp = params.collect do |param| + @writer.ele("param", conv2value(param)) + end + + resp = [@writer.ele("params", *resp)] + else + if params.size != 1 or params[0] === XMLRPC::FaultException + raise ArgumentError, "no valid fault-structure given" + end + resp = @writer.ele("fault", conv2value(params[0].to_h)) + end + + + tree = @writer.document( + @writer.pi("xml", 'version="1.0"'), + @writer.ele("methodResponse", resp) + ) + + @writer.document_to_str(tree) + "\n" + end + + + + ##################################### + private + ##################################### + + # + # converts a Ruby object into + # a XML-RPC tag + # + def conv2value(param) + + val = case param + when Fixnum + @writer.tag("i4", param.to_s) + + when Bignum + if Config::ENABLE_BIGINT + @writer.tag("i4", param.to_s) + else + if param >= -(2**31) and param <= (2**31-1) + @writer.tag("i4", param.to_s) + else + raise "Bignum is too big! Must be signed 32-bit integer!" + end + end + when TrueClass, FalseClass + @writer.tag("boolean", param ? "1" : "0") + + when String + @writer.tag("string", param) + + when Symbol + @writer.tag("string", param.to_s) + + when NilClass + if Config::ENABLE_NIL_CREATE + @writer.ele("nil") + else + raise "Wrong type NilClass. Not allowed!" + end + + when Float + @writer.tag("double", param.to_s) + + when Struct + h = param.members.collect do |key| + value = param[key] + @writer.ele("member", + @writer.tag("name", key.to_s), + conv2value(value) + ) + end + + @writer.ele("struct", *h) + + when Hash + # TODO: can a Hash be empty? + + h = param.collect do |key, value| + @writer.ele("member", + @writer.tag("name", key.to_s), + conv2value(value) + ) + end + + @writer.ele("struct", *h) + + when Array + # TODO: can an Array be empty? + a = param.collect {|v| conv2value(v) } + + @writer.ele("array", + @writer.ele("data", *a) + ) + + when Time, Date, ::DateTime + @writer.tag("dateTime.iso8601", param.strftime("%Y%m%dT%H:%M:%S")) + + when XMLRPC::DateTime + @writer.tag("dateTime.iso8601", + format("%.4d%02d%02dT%02d:%02d:%02d", *param.to_a)) + + when XMLRPC::Base64 + @writer.tag("base64", param.encoded) + + else + if Config::ENABLE_MARSHALLING and param.class.included_modules.include? XMLRPC::Marshallable + # convert Ruby object into Hash + ret = {"___class___" => param.class.name} + param.instance_variables.each {|v| + name = v[1..-1] + val = param.instance_variable_get(v) + + if val.nil? + ret[name] = val if Config::ENABLE_NIL_CREATE + else + ret[name] = val + end + } + return conv2value(ret) + else + ok, pa = wrong_type(param) + if ok + return conv2value(pa) + else + raise "Wrong type!" + end + end + end + + @writer.ele("value", val) + end + + def wrong_type(value) + false + end + + + end # class Create + +end # module XMLRPC + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/datetime.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/datetime.rb new file mode 100644 index 0000000000..86342c20b8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/datetime.rb @@ -0,0 +1,142 @@ +=begin += xmlrpc/datetime.rb +Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) + +Released under the same term of license as Ruby. + += Classes +* (()) + += XMLRPC::DateTime +== Description +This class is important to handle XMLRPC (('dateTime.iso8601')) values, +correcly, because normal UNIX-dates (class (({Date}))) only handle dates +from year 1970 on, and class (({Time})) handles dates without the time +component. (({XMLRPC::DateTime})) is able to store a XMLRPC +(('dateTime.iso8601')) value correctly. + +== Class Methods +--- XMLRPC::DateTime.new( year, month, day, hour, min, sec ) + Creates a new (({XMLRPC::DateTime})) instance with the + parameters ((|year|)), ((|month|)), ((|day|)) as date and + ((|hour|)), ((|min|)), ((|sec|)) as time. + Raises (({ArgumentError})) if a parameter is out of range, or ((|year|)) is not + of type (({Integer})). + +== Instance Methods +--- XMLRPC::DateTime#year +--- XMLRPC::DateTime#month +--- XMLRPC::DateTime#day +--- XMLRPC::DateTime#hour +--- XMLRPC::DateTime#min +--- XMLRPC::DateTime#sec + Return the value of the specified date/time component. + +--- XMLRPC::DateTime#mon + Alias for (()). + +--- XMLRPC::DateTime#year=( value ) +--- XMLRPC::DateTime#month=( value ) +--- XMLRPC::DateTime#day=( value ) +--- XMLRPC::DateTime#hour=( value ) +--- XMLRPC::DateTime#min=( value ) +--- XMLRPC::DateTime#sec=( value ) + Set ((|value|)) as the new date/time component. + Raises (({ArgumentError})) if ((|value|)) is out of range, or in the case + of (({XMLRPC::DateTime#year=})) if ((|value|)) is not of type (({Integer})). + +--- XMLRPC::DateTime#mon=( value ) + Alias for (()). + +--- XMLRPC::DateTime#to_time + Return a (({Time})) object of the date/time which (({self})) represents. + If the (('year')) is below 1970, this method returns (({nil})), + because (({Time})) cannot handle years below 1970. + The used timezone is GMT. + +--- XMLRPC::DateTime#to_date + Return a (({Date})) object of the date which (({self})) represents. + The (({Date})) object do ((*not*)) contain the time component (only date). + +--- XMLRPC::DateTime#to_a + Returns all date/time components in an array. + Returns (({[year, month, day, hour, min, sec]})). +=end + +require "date" + +module XMLRPC + +class DateTime + + attr_reader :year, :month, :day, :hour, :min, :sec + + def year= (value) + raise ArgumentError, "date/time out of range" unless value.is_a? Integer + @year = value + end + + def month= (value) + raise ArgumentError, "date/time out of range" unless (1..12).include? value + @month = value + end + + def day= (value) + raise ArgumentError, "date/time out of range" unless (1..31).include? value + @day = value + end + + def hour= (value) + raise ArgumentError, "date/time out of range" unless (0..24).include? value + @hour = value + end + + def min= (value) + raise ArgumentError, "date/time out of range" unless (0..59).include? value + @min = value + end + + def sec= (value) + raise ArgumentError, "date/time out of range" unless (0..59).include? value + @sec = value + end + + alias mon month + alias mon= month= + + + def initialize(year, month, day, hour, min, sec) + self.year, self.month, self.day = year, month, day + self.hour, self.min, self.sec = hour, min, sec + end + + def to_time + if @year >= 1970 + Time.gm(*to_a) + else + nil + end + end + + def to_date + Date.new(*to_a[0,3]) + end + + def to_a + [@year, @month, @day, @hour, @min, @sec] + end + + def ==(o) + Array(self) == Array(o) + end + +end + + +end # module XMLRPC + + +=begin += History + $Id: datetime.rb 11708 2007-02-12 23:01:19Z shyouhei $ +=end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/httpserver.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/httpserver.rb new file mode 100644 index 0000000000..d98db8963a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/httpserver.rb @@ -0,0 +1,178 @@ +# +# Implements a simple HTTP-server by using John W. Small's (jsmall@laser.net) +# ruby-generic-server. +# +# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) +# +# $Id: httpserver.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# + + +require "gserver" + +class HttpServer < GServer + + ## + # handle_obj specifies the object, that receives calls to request_handler + # and ip_auth_handler + def initialize(handle_obj, port = 8080, host = DEFAULT_HOST, maxConnections = 4, + stdlog = $stdout, audit = true, debug = true) + @handler = handle_obj + super(port, host, maxConnections, stdlog, audit, debug) + end + +private + + # Constants ----------------------------------------------- + + CRLF = "\r\n" + HTTP_PROTO = "HTTP/1.0" + SERVER_NAME = "HttpServer (Ruby #{RUBY_VERSION})" + + DEFAULT_HEADER = { + "Server" => SERVER_NAME + } + + ## + # Mapping of status code and error message + # + StatusCodeMapping = { + 200 => "OK", + 400 => "Bad Request", + 403 => "Forbidden", + 405 => "Method Not Allowed", + 411 => "Length Required", + 500 => "Internal Server Error" + } + + # Classes ------------------------------------------------- + + class Request + attr_reader :data, :header, :method, :path, :proto + + def initialize(data, method=nil, path=nil, proto=nil) + @header, @data = Table.new, data + @method, @path, @proto = method, path, proto + end + + def content_length + len = @header['Content-Length'] + return nil if len.nil? + return len.to_i + end + + end + + class Response + attr_reader :header + attr_accessor :body, :status, :status_message + + def initialize(status=200) + @status = status + @status_message = nil + @header = Table.new + end + end + + + ## + # a case-insensitive Hash class for HTTP header + # + class Table + include Enumerable + + def initialize(hash={}) + @hash = hash + update(hash) + end + + def [](key) + @hash[key.to_s.capitalize] + end + + def []=(key, value) + @hash[key.to_s.capitalize] = value + end + + def update(hash) + hash.each {|k,v| self[k] = v} + self + end + + def each + @hash.each {|k,v| yield k.capitalize, v } + end + + def writeTo(port) + each { |k,v| port << "#{k}: #{v}" << CRLF } + end + end # class Table + + + # Helper Methods ------------------------------------------ + + def http_header(header=nil) + new_header = Table.new(DEFAULT_HEADER) + new_header.update(header) unless header.nil? + + new_header["Connection"] = "close" + new_header["Date"] = http_date(Time.now) + + new_header + end + + def http_date( aTime ) + aTime.gmtime.strftime( "%a, %d %b %Y %H:%M:%S GMT" ) + end + + def http_resp(status_code, status_message=nil, header=nil, body=nil) + status_message ||= StatusCodeMapping[status_code] + + str = "" + str << "#{HTTP_PROTO} #{status_code} #{status_message}" << CRLF + http_header(header).writeTo(str) + str << CRLF + str << body unless body.nil? + str + end + + # Main Serve Loop ----------------------------------------- + + def serve(io) + # perform IP authentification + unless @handler.ip_auth_handler(io) + io << http_resp(403, "Forbidden") + return + end + + # parse first line + if io.gets =~ /^(\S+)\s+(\S+)\s+(\S+)/ + request = Request.new(io, $1, $2, $3) + else + io << http_resp(400, "Bad Request") + return + end + + # parse HTTP headers + while (line=io.gets) !~ /^(\n|\r)/ + if line =~ /^([\w-]+):\s*(.*)$/ + request.header[$1] = $2.strip + end + end + + io.binmode + response = Response.new + + # execute request handler + @handler.request_handler(request, response) + + # write response back to the client + io << http_resp(response.status, response.status_message, + response.header, response.body) + + rescue Exception => e + io << http_resp(500, "Internal Server Error") + end + +end # class HttpServer + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/marshal.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/marshal.rb new file mode 100644 index 0000000000..ba8c720a01 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/marshal.rb @@ -0,0 +1,76 @@ +# +# Marshalling of XML-RPC methodCall and methodResponse +# +# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) +# +# $Id: marshal.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# + +require "xmlrpc/parser" +require "xmlrpc/create" +require "xmlrpc/config" +require "xmlrpc/utils" + +module XMLRPC + + class Marshal + include ParserWriterChooseMixin + + # class methods ------------------------------- + + class << self + + def dump_call( methodName, *params ) + new.dump_call( methodName, *params ) + end + + def dump_response( param ) + new.dump_response( param ) + end + + def load_call( stringOrReadable ) + new.load_call( stringOrReadable ) + end + + def load_response( stringOrReadable ) + new.load_response( stringOrReadable ) + end + + alias dump dump_response + alias load load_response + + end # class self + + # instance methods ---------------------------- + + def initialize( parser = nil, writer = nil ) + set_parser( parser ) + set_writer( writer ) + end + + def dump_call( methodName, *params ) + create.methodCall( methodName, *params ) + end + + def dump_response( param ) + create.methodResponse( ! param.kind_of?( XMLRPC::FaultException ) , param ) + end + + ## + # returns [ methodname, params ] + # + def load_call( stringOrReadable ) + parser.parseMethodCall( stringOrReadable ) + end + + ## + # returns paramOrFault + # + def load_response( stringOrReadable ) + parser.parseMethodResponse( stringOrReadable )[1] + end + + end # class Marshal + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/parser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/parser.rb new file mode 100644 index 0000000000..c228e05923 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/parser.rb @@ -0,0 +1,813 @@ +# +# Parser for XML-RPC call and response +# +# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) +# +# $Id: parser.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# + + +require "date" +require "xmlrpc/base64" +require "xmlrpc/datetime" + + +# add some methods to NQXML::Node +module NQXML + class Node + + def removeChild(node) + @children.delete(node) + end + def childNodes + @children + end + def hasChildNodes + not @children.empty? + end + def [] (index) + @children[index] + end + + def nodeType + if @entity.instance_of? NQXML::Text then :TEXT + elsif @entity.instance_of? NQXML::Comment then :COMMENT + #elsif @entity.instance_of? NQXML::Element then :ELEMENT + elsif @entity.instance_of? NQXML::Tag then :ELEMENT + else :ELSE + end + end + + def nodeValue + #TODO: error when wrong Entity-type + @entity.text + end + def nodeName + #TODO: error when wrong Entity-type + @entity.name + end + end # class Node +end # module NQXML + +module XMLRPC + + class FaultException < StandardError + attr_reader :faultCode, :faultString + + alias message faultString + + def initialize(faultCode, faultString) + @faultCode = faultCode + @faultString = faultString + end + + # returns a hash + def to_h + {"faultCode" => @faultCode, "faultString" => @faultString} + end + end + + module Convert + def self.int(str) + str.to_i + end + + def self.boolean(str) + case str + when "0" then false + when "1" then true + else + raise "RPC-value of type boolean is wrong" + end + end + + def self.double(str) + str.to_f + end + + def self.dateTime(str) + case str + when /^(-?\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(?:Z|([+-])(\d\d):?(\d\d))?$/ + a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i} + if $7 + ofs = $8.to_i*3600 + $9.to_i*60 + ofs = -ofs if $7=='+' + utc = Time.utc(a.reverse) + ofs + a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ] + end + XMLRPC::DateTime.new(*a) + when /^(-?\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(Z|([+-]\d\d):(\d\d))?$/ + a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i} + if a[0] < 70 + a[0] += 2000 + else + a[0] += 1900 + end + if $7 + ofs = $8.to_i*3600 + $9.to_i*60 + ofs = -ofs if $7=='+' + utc = Time.utc(a.reverse) + ofs + a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ] + end + XMLRPC::DateTime.new(*a) + else + raise "wrong dateTime.iso8601 format " + str + end + end + + def self.base64(str) + XMLRPC::Base64.decode(str) + end + + def self.struct(hash) + # convert to marhalled object + klass = hash["___class___"] + if klass.nil? or Config::ENABLE_MARSHALLING == false + hash + else + begin + mod = Module + klass.split("::").each {|const| mod = mod.const_get(const.strip)} + + obj = mod.allocate + + hash.delete "___class___" + hash.each {|key, value| + obj.instance_variable_set("@#{ key }", value) if key =~ /^([\w_][\w_0-9]*)$/ + } + obj + rescue + hash + end + end + end + + def self.fault(hash) + if hash.kind_of? Hash and hash.size == 2 and + hash.has_key? "faultCode" and hash.has_key? "faultString" and + hash["faultCode"].kind_of? Integer and hash["faultString"].kind_of? String + + XMLRPC::FaultException.new(hash["faultCode"], hash["faultString"]) + else + raise "wrong fault-structure: #{hash.inspect}" + end + end + + end # module Convert + + module XMLParser + + class AbstractTreeParser + + def parseMethodResponse(str) + methodResponse_document(createCleanedTree(str)) + end + + def parseMethodCall(str) + methodCall_document(createCleanedTree(str)) + end + + private + + # + # remove all whitespaces but in the tags i4, int, boolean.... + # and all comments + # + def removeWhitespacesAndComments(node) + remove = [] + childs = node.childNodes.to_a + childs.each do |nd| + case _nodeType(nd) + when :TEXT + # TODO: add nil? + unless %w(i4 int boolean string double dateTime.iso8601 base64).include? node.nodeName + + if node.nodeName == "value" + if not node.childNodes.to_a.detect {|n| _nodeType(n) == :ELEMENT}.nil? + remove << nd if nd.nodeValue.strip == "" + end + else + remove << nd if nd.nodeValue.strip == "" + end + end + when :COMMENT + remove << nd + else + removeWhitespacesAndComments(nd) + end + end + + remove.each { |i| node.removeChild(i) } + end + + + def nodeMustBe(node, name) + cmp = case name + when Array + name.include?(node.nodeName) + when String + name == node.nodeName + else + raise "error" + end + + if not cmp then + raise "wrong xml-rpc (name)" + end + + node + end + + # + # returns, when successfully the only child-node + # + def hasOnlyOneChild(node, name=nil) + if node.childNodes.to_a.size != 1 + raise "wrong xml-rpc (size)" + end + if name != nil then + nodeMustBe(node.firstChild, name) + end + end + + + def assert(b) + if not b then + raise "assert-fail" + end + end + + # the node `node` has empty string or string + def text_zero_one(node) + nodes = node.childNodes.to_a.size + + if nodes == 1 + text(node.firstChild) + elsif nodes == 0 + "" + else + raise "wrong xml-rpc (size)" + end + end + + + def integer(node) + #TODO: check string for float because to_i returnsa + # 0 when wrong string + nodeMustBe(node, %w(i4 int)) + hasOnlyOneChild(node) + + Convert.int(text(node.firstChild)) + end + + def boolean(node) + nodeMustBe(node, "boolean") + hasOnlyOneChild(node) + + Convert.boolean(text(node.firstChild)) + end + + def v_nil(node) + nodeMustBe(node, "nil") + assert( node.childNodes.to_a.size == 0 ) + nil + end + + def string(node) + nodeMustBe(node, "string") + text_zero_one(node) + end + + def double(node) + #TODO: check string for float because to_f returnsa + # 0.0 when wrong string + nodeMustBe(node, "double") + hasOnlyOneChild(node) + + Convert.double(text(node.firstChild)) + end + + def dateTime(node) + nodeMustBe(node, "dateTime.iso8601") + hasOnlyOneChild(node) + + Convert.dateTime( text(node.firstChild) ) + end + + def base64(node) + nodeMustBe(node, "base64") + #hasOnlyOneChild(node) + + Convert.base64(text_zero_one(node)) + end + + def member(node) + nodeMustBe(node, "member") + assert( node.childNodes.to_a.size == 2 ) + + [ name(node[0]), value(node[1]) ] + end + + def name(node) + nodeMustBe(node, "name") + #hasOnlyOneChild(node) + text_zero_one(node) + end + + def array(node) + nodeMustBe(node, "array") + hasOnlyOneChild(node, "data") + data(node.firstChild) + end + + def data(node) + nodeMustBe(node, "data") + + node.childNodes.to_a.collect do |val| + value(val) + end + end + + def param(node) + nodeMustBe(node, "param") + hasOnlyOneChild(node, "value") + value(node.firstChild) + end + + def methodResponse(node) + nodeMustBe(node, "methodResponse") + hasOnlyOneChild(node, %w(params fault)) + child = node.firstChild + + case child.nodeName + when "params" + [ true, params(child,false) ] + when "fault" + [ false, fault(child) ] + else + raise "unexpected error" + end + + end + + def methodName(node) + nodeMustBe(node, "methodName") + hasOnlyOneChild(node) + text(node.firstChild) + end + + def params(node, call=true) + nodeMustBe(node, "params") + + if call + node.childNodes.to_a.collect do |n| + param(n) + end + else # response (only one param) + hasOnlyOneChild(node) + param(node.firstChild) + end + end + + def fault(node) + nodeMustBe(node, "fault") + hasOnlyOneChild(node, "value") + f = value(node.firstChild) + Convert.fault(f) + end + + + + # _nodeType is defined in the subclass + def text(node) + assert( _nodeType(node) == :TEXT ) + assert( node.hasChildNodes == false ) + assert( node.nodeValue != nil ) + + node.nodeValue.to_s + end + + def struct(node) + nodeMustBe(node, "struct") + + hash = {} + node.childNodes.to_a.each do |me| + n, v = member(me) + hash[n] = v + end + + Convert.struct(hash) + end + + + def value(node) + nodeMustBe(node, "value") + nodes = node.childNodes.to_a.size + if nodes == 0 + return "" + elsif nodes > 1 + raise "wrong xml-rpc (size)" + end + + child = node.firstChild + + case _nodeType(child) + when :TEXT + text_zero_one(node) + when :ELEMENT + case child.nodeName + when "i4", "int" then integer(child) + when "boolean" then boolean(child) + when "string" then string(child) + when "double" then double(child) + when "dateTime.iso8601" then dateTime(child) + when "base64" then base64(child) + when "struct" then struct(child) + when "array" then array(child) + when "nil" + if Config::ENABLE_NIL_PARSER + v_nil(child) + else + raise "wrong/unknown XML-RPC type 'nil'" + end + else + raise "wrong/unknown XML-RPC type" + end + else + raise "wrong type of node" + end + + end + + def methodCall(node) + nodeMustBe(node, "methodCall") + assert( (1..2).include?( node.childNodes.to_a.size ) ) + name = methodName(node[0]) + + if node.childNodes.to_a.size == 2 then + pa = params(node[1]) + else # no parameters given + pa = [] + end + [name, pa] + end + + end # module TreeParserMixin + + class AbstractStreamParser + def parseMethodResponse(str) + parser = @parser_class.new + parser.parse(str) + raise "No valid method response!" if parser.method_name != nil + if parser.fault != nil + # is a fault structure + [false, parser.fault] + else + # is a normal return value + raise "Missing return value!" if parser.params.size == 0 + raise "Too many return values. Only one allowed!" if parser.params.size > 1 + [true, parser.params[0]] + end + end + + def parseMethodCall(str) + parser = @parser_class.new + parser.parse(str) + raise "No valid method call - missing method name!" if parser.method_name.nil? + [parser.method_name, parser.params] + end + end + + module StreamParserMixin + attr_reader :params + attr_reader :method_name + attr_reader :fault + + def initialize(*a) + super(*a) + @params = [] + @values = [] + @val_stack = [] + + @names = [] + @name = [] + + @structs = [] + @struct = {} + + @method_name = nil + @fault = nil + + @data = nil + end + + def startElement(name, attrs=[]) + @data = nil + case name + when "value" + @value = nil + when "nil" + raise "wrong/unknown XML-RPC type 'nil'" unless Config::ENABLE_NIL_PARSER + @value = :nil + when "array" + @val_stack << @values + @values = [] + when "struct" + @names << @name + @name = [] + + @structs << @struct + @struct = {} + end + end + + def endElement(name) + @data ||= "" + case name + when "string" + @value = @data + when "i4", "int" + @value = Convert.int(@data) + when "boolean" + @value = Convert.boolean(@data) + when "double" + @value = Convert.double(@data) + when "dateTime.iso8601" + @value = Convert.dateTime(@data) + when "base64" + @value = Convert.base64(@data) + when "value" + @value = @data if @value.nil? + @values << (@value == :nil ? nil : @value) + when "array" + @value = @values + @values = @val_stack.pop + when "struct" + @value = Convert.struct(@struct) + + @name = @names.pop + @struct = @structs.pop + when "name" + @name[0] = @data + when "member" + @struct[@name[0]] = @values.pop + + when "param" + @params << @values[0] + @values = [] + + when "fault" + @fault = Convert.fault(@values[0]) + + when "methodName" + @method_name = @data + end + + @data = nil + end + + def character(data) + if @data + @data << data + else + @data = data + end + end + + end # module StreamParserMixin + + # --------------------------------------------------------------------------- + class XMLStreamParser < AbstractStreamParser + def initialize + require "xmlparser" + @parser_class = Class.new(::XMLParser) { + include StreamParserMixin + } + end + end # class XMLStreamParser + # --------------------------------------------------------------------------- + class NQXMLStreamParser < AbstractStreamParser + def initialize + require "nqxml/streamingparser" + @parser_class = XMLRPCParser + end + + class XMLRPCParser + include StreamParserMixin + + def parse(str) + parser = NQXML::StreamingParser.new(str) + parser.each do |ele| + case ele + when NQXML::Text + @data = ele.text + #character(ele.text) + when NQXML::Tag + if ele.isTagEnd + endElement(ele.name) + else + startElement(ele.name, ele.attrs) + end + end + end # do + end # method parse + end # class XMLRPCParser + + end # class NQXMLStreamParser + # --------------------------------------------------------------------------- + class XMLTreeParser < AbstractTreeParser + + def initialize + require "xmltreebuilder" + + # The new XMLParser library (0.6.2+) uses a slightly different DOM implementation. + # The following code removes the differences between both versions. + if defined? XML::DOM::Builder + return if defined? XML::DOM::Node::DOCUMENT # code below has been already executed + klass = XML::DOM::Node + klass.const_set("DOCUMENT", klass::DOCUMENT_NODE) + klass.const_set("TEXT", klass::TEXT_NODE) + klass.const_set("COMMENT", klass::COMMENT_NODE) + klass.const_set("ELEMENT", klass::ELEMENT_NODE) + end + end + + private + + def _nodeType(node) + tp = node.nodeType + if tp == XML::SimpleTree::Node::TEXT then :TEXT + elsif tp == XML::SimpleTree::Node::COMMENT then :COMMENT + elsif tp == XML::SimpleTree::Node::ELEMENT then :ELEMENT + else :ELSE + end + end + + + def methodResponse_document(node) + assert( node.nodeType == XML::SimpleTree::Node::DOCUMENT ) + hasOnlyOneChild(node, "methodResponse") + + methodResponse(node.firstChild) + end + + def methodCall_document(node) + assert( node.nodeType == XML::SimpleTree::Node::DOCUMENT ) + hasOnlyOneChild(node, "methodCall") + + methodCall(node.firstChild) + end + + def createCleanedTree(str) + doc = XML::SimpleTreeBuilder.new.parse(str) + doc.documentElement.normalize + removeWhitespacesAndComments(doc) + doc + end + + end # class XMLParser + # --------------------------------------------------------------------------- + class NQXMLTreeParser < AbstractTreeParser + + def initialize + require "nqxml/treeparser" + end + + private + + def _nodeType(node) + node.nodeType + end + + def methodResponse_document(node) + methodResponse(node) + end + + def methodCall_document(node) + methodCall(node) + end + + def createCleanedTree(str) + doc = ::NQXML::TreeParser.new(str).document.rootNode + removeWhitespacesAndComments(doc) + doc + end + + end # class NQXMLTreeParser + # --------------------------------------------------------------------------- + class REXMLStreamParser < AbstractStreamParser + def initialize + require "rexml/document" + @parser_class = StreamListener + end + + class StreamListener + include StreamParserMixin + + alias :tag_start :startElement + alias :tag_end :endElement + alias :text :character + alias :cdata :character + + def method_missing(*a) + # ignore + end + + def parse(str) + parser = REXML::Document.parse_stream(str, self) + end + end + + end + # --------------------------------------------------------------------------- + class XMLScanStreamParser < AbstractStreamParser + def initialize + require "xmlscan/parser" + @parser_class = XMLScanParser + end + + class XMLScanParser + include StreamParserMixin + + Entities = { + "lt" => "<", + "gt" => ">", + "amp" => "&", + "quot" => '"', + "apos" => "'" + } + + def parse(str) + parser = XMLScan::XMLParser.new(self) + parser.parse(str) + end + + alias :on_stag :startElement + alias :on_etag :endElement + + def on_stag_end(name); end + + def on_stag_end_empty(name) + startElement(name) + endElement(name) + end + + def on_chardata(str) + character(str) + end + + def on_cdata(str) + character(str) + end + + def on_entityref(ent) + str = Entities[ent] + if str + character(str) + else + raise "unknown entity" + end + end + + def on_charref(code) + character(code.chr) + end + + def on_charref_hex(code) + character(code.chr) + end + + def method_missing(*a) + end + + # TODO: call/implement? + # valid_name? + # valid_chardata? + # valid_char? + # parse_error + + end + end + # --------------------------------------------------------------------------- + XMLParser = XMLTreeParser + NQXMLParser = NQXMLTreeParser + + Classes = [XMLStreamParser, XMLTreeParser, + NQXMLStreamParser, NQXMLTreeParser, + REXMLStreamParser, XMLScanStreamParser] + + # yields an instance of each installed parser + def self.each_installed_parser + XMLRPC::XMLParser::Classes.each do |klass| + begin + yield klass.new + rescue LoadError + end + end + end + + end # module XMLParser + + +end # module XMLRPC + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/server.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/server.rb new file mode 100644 index 0000000000..123e05f163 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/server.rb @@ -0,0 +1,780 @@ +=begin += xmlrpc/server.rb +Copyright (C) 2001, 2002, 2003, 2005 by Michael Neumann (mneumann@ntecs.de) + +Released under the same term of license as Ruby. + += Classes +* (()) +* (()) +* (()) +* (()) +* (()) + += XMLRPC::BasicServer +== Description +Is the base class for all XML-RPC server-types (CGI, standalone). +You can add handler and set a default handler. +Do not use this server, as this is/should be an abstract class. + +=== How the method to call is found +The arity (number of accepted arguments) of a handler (method or (({Proc})) object) is +compared to the given arguments submitted by the client for a RPC ((-Remote Procedure Call-)). +A handler is only called if it accepts the number of arguments, otherwise the search +for another handler will go on. When at the end no handler was found, +the (()) will be called. +With this technique it is possible to do overloading by number of parameters, but +only for (({Proc})) handler, because you cannot define two methods of the same name in +the same class. + + +== Class Methods +--- XMLRPC::BasicServer.new( class_delim="." ) + Creates a new (({XMLRPC::BasicServer})) instance, which should not be + done, because (({XMLRPC::BasicServer})) is an abstract class. This + method should be called from a subclass indirectly by a (({super})) call + in the method (({initialize})). The paramter ((|class_delim|)) is used + in (()) when an object is + added as handler, to delimit the object-prefix and the method-name. + +== Instance Methods +--- XMLRPC::BasicServer#add_handler( name, signature=nil, help=nil ) { aBlock } + Adds ((|aBlock|)) to the list of handlers, with ((|name|)) as the name of the method. + Parameters ((|signature|)) and ((|help|)) are used by the Introspection method if specified, + where ((|signature|)) is either an Array containing strings each representing a type of it's + signature (the first is the return value) or an Array of Arrays if the method has multiple + signatures. Value type-names are "int, boolean, double, string, dateTime.iso8601, base64, array, struct". + + Parameter ((|help|)) is a String with informations about how to call this method etc. + + A handler method or code-block can return the types listed at + (()). + When a method fails, it can tell it the client by throwing an + (({XMLRPC::FaultException})) like in this example: + s.add_handler("michael.div") do |a,b| + if b == 0 + raise XMLRPC::FaultException.new(1, "division by zero") + else + a / b + end + end + The client gets in the case of (({b==0})) an object back of type + (({XMLRPC::FaultException})) that has a ((|faultCode|)) and ((|faultString|)) + field. + +--- XMLRPC::BasicServer#add_handler( prefix, obj ) + This is the second form of (()). + To add an object write: + server.add_handler("michael", MyHandlerClass.new) + All public methods of (({MyHandlerClass})) are accessible to + the XML-RPC clients by (('michael."name of method"')). This is + where the ((|class_delim|)) in (()) + has it's role, a XML-RPC method-name is defined by + ((|prefix|)) + ((|class_delim|)) + (('"name of method"')). + +--- XMLRPC::BasicServer#add_handler( interface, obj ) + This is the third form of (()). + + Use (({XMLRPC::interface})) to generate an ServiceInterface object, which + represents an interface (with signature and help text) for a handler class. + + Parameter ((|interface|)) must be of type (({XMLRPC::ServiceInterface})). + Adds all methods of ((|obj|)) which are defined in ((|interface|)) to the + server. + + This is the recommended way of adding services to a server! + + +--- XMLRPC::BasicServer#get_default_handler + Returns the default-handler, which is called when no handler for + a method-name is found. + It is a (({Proc})) object or (({nil})). + +--- XMLRPC::BasicServer#set_default_handler ( &handler ) + Sets ((|handler|)) as the default-handler, which is called when + no handler for a method-name is found. ((|handler|)) is a code-block. + The default-handler is called with the (XML-RPC) method-name as first argument, and + the other arguments are the parameters given by the client-call. + + If no block is specified the default of (({XMLRPC::BasicServer})) is used, which raises a + XMLRPC::FaultException saying "method missing". + + +--- XMLRPC::BasicServer#set_writer( writer ) + Sets the XML writer to use for generating XML output. + Should be an instance of a class from module (({XMLRPC::XMLWriter})). + If this method is not called, then (({XMLRPC::Config::DEFAULT_WRITER})) is used. + +--- XMLRPC::BasicServer#set_parser( parser ) + Sets the XML parser to use for parsing XML documents. + Should be an instance of a class from module (({XMLRPC::XMLParser})). + If this method is not called, then (({XMLRPC::Config::DEFAULT_PARSER})) is used. + +--- XMLRPC::BasicServer#add_introspection + Adds the introspection handlers "system.listMethods", "system.methodSignature" and "system.methodHelp", + where only the first one works. + +--- XMLRPC::BasicServer#add_multicall + Adds the multi-call handler "system.multicall". + +--- XMLRPC::BasicServer#get_service_hook + Returns the service-hook, which is called on each service request (RPC) unless it's (({nil})). + +--- XMLRPC::BasicServer#set_service_hook ( &handler ) + A service-hook is called for each service request (RPC). + You can use a service-hook for example to wrap existing methods and catch exceptions of them or + convert values to values recognized by XMLRPC. You can disable it by passing (({nil})) as parameter + ((|handler|)) . + + The service-hook is called with a (({Proc})) object and with the parameters for this (({Proc})). + An example: + + server.set_service_hook {|obj, *args| + begin + ret = obj.call(*args) # call the original service-method + # could convert the return value + resuce + # rescue exceptions + end + } + +=end + + + +require "xmlrpc/parser" +require "xmlrpc/create" +require "xmlrpc/config" +require "xmlrpc/utils" # ParserWriterChooseMixin + + + +module XMLRPC + + +class BasicServer + + include ParserWriterChooseMixin + include ParseContentType + + ERR_METHOD_MISSING = 1 + ERR_UNCAUGHT_EXCEPTION = 2 + ERR_MC_WRONG_PARAM = 3 + ERR_MC_MISSING_PARAMS = 4 + ERR_MC_MISSING_METHNAME = 5 + ERR_MC_RECURSIVE_CALL = 6 + ERR_MC_WRONG_PARAM_PARAMS = 7 + ERR_MC_EXPECTED_STRUCT = 8 + + + def initialize(class_delim=".") + @handler = [] + @default_handler = nil + @service_hook = nil + + @class_delim = class_delim + @create = nil + @parser = nil + + add_multicall if Config::ENABLE_MULTICALL + add_introspection if Config::ENABLE_INTROSPECTION + end + + def add_handler(prefix, obj_or_signature=nil, help=nil, &block) + if block_given? + # proc-handler + @handler << [prefix, block, obj_or_signature, help] + else + if prefix.kind_of? String + # class-handler + raise ArgumentError, "Expected non-nil value" if obj_or_signature.nil? + @handler << [prefix + @class_delim, obj_or_signature] + elsif prefix.kind_of? XMLRPC::Service::BasicInterface + # class-handler with interface + # add all methods + @handler += prefix.get_methods(obj_or_signature, @class_delim) + else + raise ArgumentError, "Wrong type for parameter 'prefix'" + end + end + self + end + + def get_service_hook + @service_hook + end + + def set_service_hook(&handler) + @service_hook = handler + self + end + + def get_default_handler + @default_handler + end + + def set_default_handler (&handler) + @default_handler = handler + self + end + + def add_multicall + add_handler("system.multicall", %w(array array), "Multicall Extension") do |arrStructs| + unless arrStructs.is_a? Array + raise XMLRPC::FaultException.new(ERR_MC_WRONG_PARAM, "system.multicall expects an array") + end + + arrStructs.collect {|call| + if call.is_a? Hash + methodName = call["methodName"] + params = call["params"] + + if params.nil? + multicall_fault(ERR_MC_MISSING_PARAMS, "Missing params") + elsif methodName.nil? + multicall_fault(ERR_MC_MISSING_METHNAME, "Missing methodName") + else + if methodName == "system.multicall" + multicall_fault(ERR_MC_RECURSIVE_CALL, "Recursive system.multicall forbidden") + else + unless params.is_a? Array + multicall_fault(ERR_MC_WRONG_PARAM_PARAMS, "Parameter params have to be an Array") + else + ok, val = call_method(methodName, *params) + if ok + # correct return value + [val] + else + # exception + multicall_fault(val.faultCode, val.faultString) + end + end + end + end + + else + multicall_fault(ERR_MC_EXPECTED_STRUCT, "system.multicall expected struct") + end + } + end # end add_handler + self + end + + def add_introspection + add_handler("system.listMethods",%w(array), "List methods available on this XML-RPC server") do + methods = [] + @handler.each do |name, obj| + if obj.kind_of? Proc + methods << name + else + obj.methods.each {|meth| methods << name + meth} + end + end + methods + end + + add_handler("system.methodSignature", %w(array string), "Returns method signature") do |meth| + sigs = [] + @handler.each do |name, obj, sig| + if obj.kind_of? Proc and sig != nil and name == meth + if sig[0].kind_of? Array + # sig contains multiple signatures, e.g. [["array"], ["array", "string"]] + sig.each {|s| sigs << s} + else + # sig is a single signature, e.g. ["array"] + sigs << sig + end + end + end + sigs.uniq! || sigs # remove eventually duplicated signatures + end + + add_handler("system.methodHelp", %w(string string), "Returns help on using this method") do |meth| + help = nil + @handler.each do |name, obj, sig, hlp| + if obj.kind_of? Proc and name == meth + help = hlp + break + end + end + help || "" + end + + self + end + + + + def process(data) + method, params = parser().parseMethodCall(data) + handle(method, *params) + end + + private # -------------------------------------------------------------- + + def multicall_fault(nr, str) + {"faultCode" => nr, "faultString" => str} + end + + # + # method dispatch + # + def dispatch(methodname, *args) + for name, obj in @handler + if obj.kind_of? Proc + next unless methodname == name + else + next unless methodname =~ /^#{name}(.+)$/ + next unless obj.respond_to? $1 + obj = obj.method($1) + end + + if check_arity(obj, args.size) + if @service_hook.nil? + return obj.call(*args) + else + return @service_hook.call(obj, *args) + end + end + end + + if @default_handler.nil? + raise XMLRPC::FaultException.new(ERR_METHOD_MISSING, "Method #{methodname} missing or wrong number of parameters!") + else + @default_handler.call(methodname, *args) + end + end + + + # + # returns true, if the arity of "obj" matches + # + def check_arity(obj, n_args) + ary = obj.arity + + if ary >= 0 + n_args == ary + else + n_args >= (ary+1).abs + end + end + + + + def call_method(methodname, *args) + begin + [true, dispatch(methodname, *args)] + rescue XMLRPC::FaultException => e + [false, e] + rescue Exception => e + [false, XMLRPC::FaultException.new(ERR_UNCAUGHT_EXCEPTION, "Uncaught exception #{e.message} in method #{methodname}")] + end + end + + # + # + # + def handle(methodname, *args) + create().methodResponse(*call_method(methodname, *args)) + end + + +end + + +=begin += XMLRPC::CGIServer +== Synopsis + require "xmlrpc/server" + + s = XMLRPC::CGIServer.new + + s.add_handler("michael.add") do |a,b| + a + b + end + + s.add_handler("michael.div") do |a,b| + if b == 0 + raise XMLRPC::FaultException.new(1, "division by zero") + else + a / b + end + end + + s.set_default_handler do |name, *args| + raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + + " or wrong number of parameters!") + end + + s.serve + +== Description +Implements a CGI-based XML-RPC server. + +== Superclass +(()) + +== Class Methods +--- XMLRPC::CGIServer.new( *a ) + Creates a new (({XMLRPC::CGIServer})) instance. All parameters given + are by-passed to (()). You can only create + ((*one*)) (({XMLRPC::CGIServer})) instance, because more than one makes + no sense. + +== Instance Methods +--- XMLRPC::CGIServer#serve + Call this after you have added all you handlers to the server. + This method processes a XML-RPC methodCall and sends the answer + back to the client. + Make sure that you don't write to standard-output in a handler, or in + any other part of your program, this would case a CGI-based server to fail! +=end + +class CGIServer < BasicServer + @@obj = nil + + def CGIServer.new(*a) + @@obj = super(*a) if @@obj.nil? + @@obj + end + + def initialize(*a) + super(*a) + end + + def serve + catch(:exit_serve) { + length = ENV['CONTENT_LENGTH'].to_i + + http_error(405, "Method Not Allowed") unless ENV['REQUEST_METHOD'] == "POST" + http_error(400, "Bad Request") unless parse_content_type(ENV['CONTENT_TYPE']).first == "text/xml" + http_error(411, "Length Required") unless length > 0 + + # TODO: do we need a call to binmode? + $stdin.binmode if $stdin.respond_to? :binmode + data = $stdin.read(length) + + http_error(400, "Bad Request") if data.nil? or data.size != length + + http_write(process(data), "Content-type" => "text/xml; charset=utf-8") + } + end + + + private + + def http_error(status, message) + err = "#{status} #{message}" + msg = <<-"MSGEND" + + + #{err} + + +

    #{err}

    +

    Unexpected error occured while processing XML-RPC request!

    + + + MSGEND + + http_write(msg, "Status" => err, "Content-type" => "text/html") + throw :exit_serve # exit from the #serve method + end + + def http_write(body, header) + h = {} + header.each {|key, value| h[key.to_s.capitalize] = value} + h['Status'] ||= "200 OK" + h['Content-length'] ||= body.size.to_s + + str = "" + h.each {|key, value| str << "#{key}: #{value}\r\n"} + str << "\r\n#{body}" + + print str + end + +end + +=begin += XMLRPC::ModRubyServer +== Description +Implements a XML-RPC server, which works with Apache mod_ruby. + +Use it in the same way as CGIServer! + +== Superclass +(()) +=end + +class ModRubyServer < BasicServer + + def initialize(*a) + @ap = Apache::request + super(*a) + end + + def serve + catch(:exit_serve) { + header = {} + @ap.headers_in.each {|key, value| header[key.capitalize] = value} + + length = header['Content-length'].to_i + + http_error(405, "Method Not Allowed") unless @ap.request_method == "POST" + http_error(400, "Bad Request") unless parse_content_type(header['Content-type']).first == "text/xml" + http_error(411, "Length Required") unless length > 0 + + # TODO: do we need a call to binmode? + @ap.binmode + data = @ap.read(length) + + http_error(400, "Bad Request") if data.nil? or data.size != length + + http_write(process(data), 200, "Content-type" => "text/xml; charset=utf-8") + } + end + + + private + + def http_error(status, message) + err = "#{status} #{message}" + msg = <<-"MSGEND" + + + #{err} + + +

    #{err}

    +

    Unexpected error occured while processing XML-RPC request!

    + + + MSGEND + + http_write(msg, status, "Status" => err, "Content-type" => "text/html") + throw :exit_serve # exit from the #serve method + end + + def http_write(body, status, header) + h = {} + header.each {|key, value| h[key.to_s.capitalize] = value} + h['Status'] ||= "200 OK" + h['Content-length'] ||= body.size.to_s + + h.each {|key, value| @ap.headers_out[key] = value } + @ap.content_type = h["Content-type"] + @ap.status = status.to_i + @ap.send_http_header + + @ap.print body + end + +end + +=begin += XMLRPC::Server +== Synopsis + require "xmlrpc/server" + + s = XMLRPC::Server.new(8080) + + s.add_handler("michael.add") do |a,b| + a + b + end + + s.add_handler("michael.div") do |a,b| + if b == 0 + raise XMLRPC::FaultException.new(1, "division by zero") + else + a / b + end + end + + s.set_default_handler do |name, *args| + raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + + " or wrong number of parameters!") + end + + s.serve + +== Description +Implements a standalone XML-RPC server. The method (({serve}))) is left if a SIGHUP is sent to the +program. + +== Superclass +(()) + +== Class Methods +--- XMLRPC::Server.new( port=8080, host="127.0.0.1", maxConnections=4, stdlog=$stdout, audit=true, debug=true, *a ) + Creates a new (({XMLRPC::Server})) instance, which is a XML-RPC server listening on + port ((|port|)) and accepts requests for the host ((|host|)), which is by default only the localhost. + The server is not started, to start it you have to call (()). + + Parameters ((|audit|)) and ((|debug|)) are obsolete! + + All additionally given parameters in ((|*a|)) are by-passed to (()). + +== Instance Methods +--- XMLRPC::Server#serve + Call this after you have added all you handlers to the server. + This method starts the server to listen for XML-RPC requests and answer them. + +--- XMLRPC::Server#shutdown + Stops and shuts the server down. + +=end + +class WEBrickServlet < BasicServer; end # forward declaration + +class Server < WEBrickServlet + + def initialize(port=8080, host="127.0.0.1", maxConnections=4, stdlog=$stdout, audit=true, debug=true, *a) + super(*a) + require 'webrick' + @server = WEBrick::HTTPServer.new(:Port => port, :BindAddress => host, :MaxClients => maxConnections, + :Logger => WEBrick::Log.new(stdlog)) + @server.mount("/", self) + end + + def serve + if RUBY_PLATFORM =~ /mingw|mswin32/ + signal = 1 + else + signal = "HUP" + end + trap(signal) { @server.shutdown } + + @server.start + end + + def shutdown + @server.shutdown + end + +end + +=begin += XMLRPC::WEBrickServlet +== Synopsis + + require "webrick" + require "xmlrpc/server" + + s = XMLRPC::WEBrickServlet.new + s.add_handler("michael.add") do |a,b| + a + b + end + + s.add_handler("michael.div") do |a,b| + if b == 0 + raise XMLRPC::FaultException.new(1, "division by zero") + else + a / b + end + end + + s.set_default_handler do |name, *args| + raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + + " or wrong number of parameters!") + end + + httpserver = WEBrick::HTTPServer.new(:Port => 8080) + httpserver.mount("/RPC2", s) + trap("HUP") { httpserver.shutdown } # use 1 instead of "HUP" on Windows + httpserver.start + +== Instance Methods + +--- XMLRPC::WEBrickServlet#set_valid_ip( *ip_addr ) + Specifies the valid IP addresses that are allowed to connect to the server. + Each IP is either a (({String})) or a (({Regexp})). + +--- XMLRPC::WEBrickServlet#get_valid_ip + Return the via method (()) specified + valid IP addresses. + +== Description +Implements a servlet for use with WEBrick, a pure Ruby (HTTP-) server framework. + +== Superclass +(()) + +=end + +class WEBrickServlet < BasicServer + def initialize(*a) + super + require "webrick/httpstatus" + @valid_ip = nil + end + + # deprecated from WEBrick/1.2.2. + # but does not break anything. + def require_path_info? + false + end + + def get_instance(config, *options) + # TODO: set config & options + self + end + + def set_valid_ip(*ip_addr) + if ip_addr.size == 1 and ip_addr[0].nil? + @valid_ip = nil + else + @valid_ip = ip_addr + end + end + + def get_valid_ip + @valid_ip + end + + def service(request, response) + + if @valid_ip + raise WEBrick::HTTPStatus::Forbidden unless @valid_ip.any? { |ip| request.peeraddr[3] =~ ip } + end + + if request.request_method != "POST" + raise WEBrick::HTTPStatus::MethodNotAllowed, + "unsupported method `#{request.request_method}'." + end + + if parse_content_type(request['Content-type']).first != "text/xml" + raise WEBrick::HTTPStatus::BadRequest + end + + length = (request['Content-length'] || 0).to_i + + raise WEBrick::HTTPStatus::LengthRequired unless length > 0 + + data = request.body + + if data.nil? or data.size != length + raise WEBrick::HTTPStatus::BadRequest + end + + resp = process(data) + if resp.nil? or resp.size <= 0 + raise WEBrick::HTTPStatus::InternalServerError + end + + response.status = 200 + response['Content-Length'] = resp.size + response['Content-Type'] = "text/xml; charset=utf-8" + response.body = resp + end +end + + +end # module XMLRPC + + +=begin += History + $Id: server.rb 11708 2007-02-12 23:01:19Z shyouhei $ +=end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/utils.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/utils.rb new file mode 100644 index 0000000000..d1fdce0c90 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xmlrpc/utils.rb @@ -0,0 +1,165 @@ +# +# Defines ParserWriterChooseMixin, which makes it possible to choose a +# different XML writer and/or XML parser then the default one. +# The Mixin is used in client.rb (class Client) and server.rb (class +# BasicServer) +# +# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) +# +# $Id: utils.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# + +module XMLRPC + + # + # This module enables a user-class to be marshalled + # by XML-RPC for Ruby into a Hash, with one additional + # key/value pair "___class___" => ClassName + # + module Marshallable + end + + + module ParserWriterChooseMixin + + def set_writer(writer) + @create = Create.new(writer) + self + end + + def set_parser(parser) + @parser = parser + self + end + + private + + def create + # if set_writer was not already called then call it now + if @create.nil? then + set_writer(Config::DEFAULT_WRITER.new) + end + @create + end + + def parser + # if set_parser was not already called then call it now + if @parser.nil? then + set_parser(Config::DEFAULT_PARSER.new) + end + @parser + end + + end # module ParserWriterChooseMixin + + + module Service + + # + # base class for Service Interface definitions, used + # by BasicServer#add_handler + # + + class BasicInterface + attr_reader :prefix, :methods + + def initialize(prefix) + @prefix = prefix + @methods = [] + end + + def add_method(sig, help=nil, meth_name=nil) + mname = nil + sig = [sig] if sig.kind_of? String + + sig = sig.collect do |s| + name, si = parse_sig(s) + raise "Wrong signatures!" if mname != nil and name != mname + mname = name + si + end + + @methods << [mname, meth_name || mname, sig, help] + end + + private # --------------------------------- + + def parse_sig(sig) + # sig is a String + if sig =~ /^\s*(\w+)\s+([^(]+)(\(([^)]*)\))?\s*$/ + params = [$1] + name = $2.strip + $4.split(",").each {|i| params << i.strip} if $4 != nil + return name, params + else + raise "Syntax error in signature" + end + end + + end # class BasicInterface + + # + # class which wraps a Service Interface definition, used + # by BasicServer#add_handler + # + class Interface < BasicInterface + def initialize(prefix, &p) + raise "No interface specified" if p.nil? + super(prefix) + instance_eval(&p) + end + + def get_methods(obj, delim=".") + prefix = @prefix + delim + @methods.collect { |name, meth, sig, help| + [prefix + name, obj.method(meth).to_proc, sig, help] + } + end + + private # --------------------------------- + + def meth(*a) + add_method(*a) + end + + end # class Interface + + class PublicInstanceMethodsInterface < BasicInterface + def initialize(prefix) + super(prefix) + end + + def get_methods(obj, delim=".") + prefix = @prefix + delim + obj.class.public_instance_methods(false).collect { |name| + [prefix + name, obj.method(name).to_proc, nil, nil] + } + end + end + + + end # module Service + + + # + # short-form to create a Service::Interface + # + def self.interface(prefix, &p) + Service::Interface.new(prefix, &p) + end + + # short-cut for creating a PublicInstanceMethodsInterface + def self.iPIMethods(prefix) + Service::PublicInstanceMethodsInterface.new(prefix) + end + + + module ParseContentType + def parse_content_type(str) + a, *b = str.split(";") + return a.strip, *b + end + end + +end # module XMLRPC + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/charset.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/charset.rb new file mode 100644 index 0000000000..15d5500fce --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/charset.rb @@ -0,0 +1,187 @@ +# XSD4R - Charset handling library. +# Copyright (C) 2001, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +module XSD + + +module Charset + @internal_encoding = $KCODE + + class XSDError < StandardError; end + class CharsetError < XSDError; end + class UnknownCharsetError < CharsetError; end + class CharsetConversionError < CharsetError; end + +public + + ### + ## Maps + # + EncodingConvertMap = {} + def Charset.init + EncodingConvertMap[['UTF8', 'X_ISO8859_1']] = + Proc.new { |str| str.unpack('U*').pack('C*') } + EncodingConvertMap[['X_ISO8859_1', 'UTF8']] = + Proc.new { |str| str.unpack('C*').pack('U*') } + begin + require 'xsd/iconvcharset' + @internal_encoding = 'UTF8' + sjtag = (/(mswin|bccwin|mingw|cygwin|emx)/ =~ RUBY_PLATFORM) ? 'cp932' : + 'shift_jis' + EncodingConvertMap[['UTF8', 'EUC' ]] = + Proc.new { |str| IconvCharset.safe_iconv("euc-jp", "utf-8", str) } + EncodingConvertMap[['EUC' , 'UTF8']] = + Proc.new { |str| IconvCharset.safe_iconv("utf-8", "euc-jp", str) } + EncodingConvertMap[['EUC' , 'SJIS']] = + Proc.new { |str| IconvCharset.safe_iconv(sjtag, "euc-jp", str) } + EncodingConvertMap[['UTF8', 'SJIS']] = + Proc.new { |str| IconvCharset.safe_iconv(sjtag, "utf-8", str) } + EncodingConvertMap[['SJIS', 'UTF8']] = + Proc.new { |str| IconvCharset.safe_iconv("utf-8", sjtag, str) } + EncodingConvertMap[['SJIS', 'EUC' ]] = + Proc.new { |str| IconvCharset.safe_iconv("euc-jp", sjtag, str) } + rescue LoadError + begin + require 'nkf' + EncodingConvertMap[['EUC' , 'SJIS']] = + Proc.new { |str| NKF.nkf('-sXm0', str) } + EncodingConvertMap[['SJIS', 'EUC' ]] = + Proc.new { |str| NKF.nkf('-eXm0', str) } + rescue LoadError + end + + begin + require 'uconv' + @internal_encoding = 'UTF8' + EncodingConvertMap[['UTF8', 'EUC' ]] = Uconv.method(:u8toeuc) + EncodingConvertMap[['UTF8', 'SJIS']] = Uconv.method(:u8tosjis) + EncodingConvertMap[['EUC' , 'UTF8']] = Uconv.method(:euctou8) + EncodingConvertMap[['SJIS', 'UTF8']] = Uconv.method(:sjistou8) + rescue LoadError + end + end + end + self.init + + CharsetMap = { + 'NONE' => 'us-ascii', + 'EUC' => 'euc-jp', + 'SJIS' => 'shift_jis', + 'UTF8' => 'utf-8', + 'X_ISO_8859_1' => 'iso-8859-1', + 'X_UNKNOWN' => nil, + } + + + ### + ## handlers + # + def Charset.encoding + @internal_encoding + end + + def Charset.encoding=(encoding) + warn("xsd charset is set to #{encoding}") if $DEBUG + @internal_encoding = encoding + end + + def Charset.xml_encoding_label + charset_label(@internal_encoding) + end + + def Charset.encoding_to_xml(str, charset) + encoding_conv(str, @internal_encoding, charset_str(charset)) + end + + def Charset.encoding_from_xml(str, charset) + encoding_conv(str, charset_str(charset), @internal_encoding) + end + + def Charset.encoding_conv(str, enc_from, enc_to) + if enc_from == enc_to or enc_from == 'NONE' or enc_to == 'NONE' + str + elsif converter = EncodingConvertMap[[enc_from, enc_to]] + converter.call(str) + else + raise CharsetConversionError.new( + "Converter not found: #{enc_from} -> #{enc_to}") + end + end + + def Charset.charset_label(encoding) + CharsetMap[encoding.upcase] + end + + def Charset.charset_str(label) + if CharsetMap.respond_to?(:key) + CharsetMap.key(label.downcase) || 'X_UNKNOWN' + else + CharsetMap.index(label.downcase) || 'X_UNKNOWN' + end + end + + # us_ascii = '[\x00-\x7F]' + us_ascii = '[\x9\xa\xd\x20-\x7F]' # XML 1.0 restricted. + USASCIIRegexp = Regexp.new("\\A#{us_ascii}*\\z", nil, "NONE") + + twobytes_euc = '(?:[\x8E\xA1-\xFE][\xA1-\xFE])' + threebytes_euc = '(?:\x8F[\xA1-\xFE][\xA1-\xFE])' + character_euc = "(?:#{us_ascii}|#{twobytes_euc}|#{threebytes_euc})" + EUCRegexp = Regexp.new("\\A#{character_euc}*\\z", nil, "NONE") + + # onebyte_sjis = '[\x00-\x7F\xA1-\xDF]' + onebyte_sjis = '[\x9\xa\xd\x20-\x7F\xA1-\xDF]' # XML 1.0 restricted. + twobytes_sjis = '(?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC])' + character_sjis = "(?:#{onebyte_sjis}|#{twobytes_sjis})" + SJISRegexp = Regexp.new("\\A#{character_sjis}*\\z", nil, "NONE") + + # 0xxxxxxx + # 110yyyyy 10xxxxxx + twobytes_utf8 = '(?:[\xC0-\xDF][\x80-\xBF])' + # 1110zzzz 10yyyyyy 10xxxxxx + threebytes_utf8 = '(?:[\xE0-\xEF][\x80-\xBF][\x80-\xBF])' + # 11110uuu 10uuuzzz 10yyyyyy 10xxxxxx + fourbytes_utf8 = '(?:[\xF0-\xF7][\x80-\xBF][\x80-\xBF][\x80-\xBF])' + character_utf8 = + "(?:#{us_ascii}|#{twobytes_utf8}|#{threebytes_utf8}|#{fourbytes_utf8})" + UTF8Regexp = Regexp.new("\\A#{character_utf8}*\\z", nil, "NONE") + + def Charset.is_us_ascii(str) + USASCIIRegexp =~ str + end + + def Charset.is_utf8(str) + UTF8Regexp =~ str + end + + def Charset.is_euc(str) + EUCRegexp =~ str + end + + def Charset.is_sjis(str) + SJISRegexp =~ str + end + + def Charset.is_ces(str, code = $KCODE) + case code + when 'NONE' + is_us_ascii(str) + when 'UTF8' + is_utf8(str) + when 'EUC' + is_euc(str) + when 'SJIS' + is_sjis(str) + else + raise UnknownCharsetError.new("Unknown charset: #{code}") + end + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen.rb new file mode 100644 index 0000000000..d820ebf1f2 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen.rb @@ -0,0 +1,12 @@ +# XSD4R - Generating code library +# Copyright (C) 2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/codegen/gensupport' +require 'xsd/codegen/moduledef' +require 'xsd/codegen/classdef' +require 'xsd/codegen/methoddef' diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/classdef.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/classdef.rb new file mode 100644 index 0000000000..9eb1ce6607 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/classdef.rb @@ -0,0 +1,203 @@ +# XSD4R - Generating class definition code +# Copyright (C) 2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/codegen/gensupport' +require 'xsd/codegen/moduledef' +require 'xsd/codegen/methoddef' + + +module XSD +module CodeGen + + +class ClassDef < ModuleDef + include GenSupport + + def initialize(name, baseclass = nil) + super(name) + @baseclass = baseclass + @classvar = [] + @attrdef = [] + end + + def def_classvar(var, value) + var = var.sub(/\A@@/, "") + unless safevarname?(var) + raise ArgumentError.new("#{var} seems to be unsafe") + end + @classvar << [var, value] + end + + def def_attr(attrname, writable = true, varname = nil) + unless safevarname?(varname || attrname) + raise ArgumentError.new("#{varname || attrname} seems to be unsafe") + end + @attrdef << [attrname, writable, varname] + end + + def dump + buf = "" + unless @requirepath.empty? + buf << dump_requirepath + end + buf << dump_emptyline unless buf.empty? + package = @name.split(/::/)[0..-2] + buf << dump_package_def(package) unless package.empty? + buf << dump_comment if @comment + buf << dump_class_def + spacer = false + unless @classvar.empty? + spacer = true + buf << dump_classvar + end + unless @const.empty? + buf << dump_emptyline if spacer + spacer = true + buf << dump_const + end + unless @code.empty? + buf << dump_emptyline if spacer + spacer = true + buf << dump_code + end + unless @attrdef.empty? + buf << dump_emptyline if spacer + spacer = true + buf << dump_attributes + end + unless @methoddef.empty? + buf << dump_emptyline if spacer + spacer = true + buf << dump_methods + end + buf << dump_class_def_end + buf << dump_package_def_end(package) unless package.empty? + buf.gsub(/^\s+$/, '') + end + +private + + def dump_class_def + name = @name.to_s.split(/::/) + if @baseclass + format("class #{name.last} < #{@baseclass}") + else + format("class #{name.last}") + end + end + + def dump_class_def_end + str = format("end") + end + + def dump_classvar + dump_static( + @classvar.collect { |var, value| + %Q(@@#{var.sub(/^@@/, "")} = #{dump_value(value)}) + }.join("\n") + ) + end + + def dump_attributes + str = "" + @attrdef.each do |attrname, writable, varname| + varname ||= attrname + if attrname == varname + str << format(dump_accessor(attrname, writable), 2) + end + end + @attrdef.each do |attrname, writable, varname| + varname ||= attrname + if attrname != varname + str << "\n" unless str.empty? + str << format(dump_attribute(attrname, writable, varname), 2) + end + end + str + end + + def dump_accessor(attrname, writable) + if writable + "attr_accessor :#{attrname}" + else + "attr_reader :#{attrname}" + end + end + + def dump_attribute(attrname, writable, varname) + str = nil + mr = MethodDef.new(attrname) + mr.definition = "@#{varname}" + str = mr.dump + if writable + mw = MethodDef.new(attrname + "=", 'value') + mw.definition = "@#{varname} = value" + str << "\n" + mw.dump + end + str + end +end + + +end +end + + +if __FILE__ == $0 + require 'xsd/codegen/classdef' + include XSD::CodeGen + c = ClassDef.new("Foo::Bar::HobbitName", String) + c.def_require("foo/bar") + c.comment = <<-EOD + foo + bar + baz + EOD + c.def_const("FOO", 1) + c.def_classvar("@@foo", "var".dump) + c.def_classvar("baz", "1".dump) + c.def_attr("Foo", true, "foo") + c.def_attr("bar") + c.def_attr("baz", true) + c.def_attr("Foo2", true, "foo2") + c.def_attr("foo3", false, "foo3") + c.def_method("foo") do + <<-EOD + foo.bar = 1 +\tbaz.each do |ele| +\t ele + end + EOD + end + c.def_method("baz", "qux") do + <<-EOD + [1, 2, 3].each do |i| + p i + end + EOD + end + + m = MethodDef.new("qux", "quxx", "quxxx") do + <<-EOD + p quxx + quxxx + EOD + end + m.comment = "hello world\n123" + c.add_method(m) + c.def_code <<-EOD + Foo.new + Bar.z + EOD + c.def_code <<-EOD + Foo.new + Bar.z + EOD + c.def_privatemethod("foo", "baz", "*arg", "&block") + + puts c.dump +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/commentdef.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/commentdef.rb new file mode 100644 index 0000000000..c9493a1363 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/commentdef.rb @@ -0,0 +1,34 @@ +# XSD4R - Generating comment definition code +# Copyright (C) 2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/codegen/gensupport' + + +module XSD +module CodeGen + + +module CommentDef + include GenSupport + + attr_accessor :comment + +private + + def dump_comment + if /\A#/ =~ @comment + format(@comment) + else + format(@comment).gsub(/^/, '# ') + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/gensupport.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/gensupport.rb new file mode 100644 index 0000000000..1e85d3668f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/gensupport.rb @@ -0,0 +1,166 @@ +# XSD4R - Code generation support +# Copyright (C) 2004, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +module XSD +module CodeGen + +# from the file 'keywords' in 1.9. +KEYWORD = {} +%w( +__LINE__ +__FILE__ +BEGIN +END +alias +and +begin +break +case +class +def +defined? +do +else +elsif +end +ensure +false +for +if +in +module +next +nil +not +or +redo +rescue +retry +return +self +super +then +true +undef +unless +until +when +while +yield +).each { |k| KEYWORD[k] = nil } + +module GenSupport + def capitalize(target) + target.sub(/^([a-z])/) { $1.tr!('[a-z]', '[A-Z]') } + end + module_function :capitalize + + def uncapitalize(target) + target.sub(/^([A-Z])/) { $1.tr!('[A-Z]', '[a-z]') } + end + module_function :uncapitalize + + def safeconstname(name) + safename = name.scan(/[a-zA-Z0-9_]+/).collect { |ele| + GenSupport.capitalize(ele) + }.join + if /^[A-Z]/ !~ safename or keyword?(safename) + safename = "C_#{safename}" + end + safename + end + module_function :safeconstname + + def safeconstname?(name) + /\A[A-Z][a-zA-Z0-9_]*\z/ =~ name and !keyword?(name) + end + module_function :safeconstname? + + def safemethodname(name) + safename = name.scan(/[a-zA-Z0-9_]+/).join('_') + safename = uncapitalize(safename) + if /^[a-z]/ !~ safename + safename = "m_#{safename}" + end + safename + end + module_function :safemethodname + + def safemethodname?(name) + /\A[a-zA-Z_][a-zA-Z0-9_]*[=!?]?\z/ =~ name + end + module_function :safemethodname? + + def safevarname(name) + safename = uncapitalize(name.scan(/[a-zA-Z0-9_]+/).join('_')) + if /^[a-z]/ !~ safename or keyword?(safename) + "v_#{safename}" + else + safename + end + end + module_function :safevarname + + def safevarname?(name) + /\A[a-z_][a-zA-Z0-9_]*\z/ =~ name and !keyword?(name) + end + module_function :safevarname? + + def keyword?(word) + KEYWORD.key?(word) + end + module_function :keyword? + + def format(str, indent = nil) + str = trim_eol(str) + str = trim_indent(str) + if indent + str.gsub(/^/, " " * indent) + else + str + end + end + +private + + def trim_eol(str) + str.collect { |line| + line.sub(/\r?\n\z/, "") + "\n" + }.join + end + + def trim_indent(str) + indent = nil + str = str.collect { |line| untab(line) }.join + str.each do |line| + head = line.index(/\S/) + if !head.nil? and (indent.nil? or head < indent) + indent = head + end + end + return str unless indent + str.collect { |line| + line.sub(/^ {0,#{indent}}/, "") + }.join + end + + def untab(line, ts = 8) + while pos = line.index(/\t/) + line = line.sub(/\t/, " " * (ts - (pos % ts))) + end + line + end + + def dump_emptyline + "\n" + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/methoddef.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/methoddef.rb new file mode 100644 index 0000000000..15892fc5bf --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/methoddef.rb @@ -0,0 +1,63 @@ +# XSD4R - Generating method definition code +# Copyright (C) 2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/codegen/gensupport' +require 'xsd/codegen/commentdef' + + +module XSD +module CodeGen + + +class MethodDef + include GenSupport + include CommentDef + + attr_accessor :definition + + def initialize(name, *params) + unless safemethodname?(name) + raise ArgumentError.new("name '#{name}' seems to be unsafe") + end + @name = name + @params = params + @comment = nil + @definition = yield if block_given? + end + + def dump + buf = "" + buf << dump_comment if @comment + buf << dump_method_def + buf << dump_definition if @definition and !@definition.empty? + buf << dump_method_def_end + buf + end + +private + + def dump_method_def + if @params.empty? + format("def #{@name}") + else + format("def #{@name}(#{@params.join(", ")})") + end + end + + def dump_method_def_end + format("end") + end + + def dump_definition + format(@definition, 2) + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/moduledef.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/moduledef.rb new file mode 100644 index 0000000000..744af2ff97 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/codegen/moduledef.rb @@ -0,0 +1,191 @@ +# XSD4R - Generating module definition code +# Copyright (C) 2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/codegen/gensupport' +require 'xsd/codegen/methoddef' +require 'xsd/codegen/commentdef' + + +module XSD +module CodeGen + + +class ModuleDef + include GenSupport + include CommentDef + + def initialize(name) + @name = name + @comment = nil + @const = [] + @code = [] + @requirepath = [] + @methoddef = [] + end + + def def_require(path) + @requirepath << path + end + + def def_const(const, value) + unless safeconstname?(const) + raise ArgumentError.new("#{const} seems to be unsafe") + end + @const << [const, value] + end + + def def_code(code) + @code << code + end + + def def_method(name, *params) + add_method(MethodDef.new(name, *params) { yield if block_given? }, :public) + end + alias def_publicmethod def_method + + def def_protectedmethod(name, *params) + add_method(MethodDef.new(name, *params) { yield if block_given? }, + :protected) + end + + def def_privatemethod(name, *params) + add_method(MethodDef.new(name, *params) { yield if block_given? }, :private) + end + + def add_method(m, visibility = :public) + @methoddef << [visibility, m] + end + + def dump + buf = "" + unless @requirepath.empty? + buf << dump_requirepath + end + buf << dump_emptyline unless buf.empty? + package = @name.split(/::/)[0..-2] + buf << dump_package_def(package) unless package.empty? + buf << dump_comment if @comment + buf << dump_module_def + spacer = false + unless @const.empty? + buf << dump_emptyline if spacer + spacer = true + buf << dump_const + end + unless @code.empty? + buf << dump_emptyline if spacer + spacer = true + buf << dump_code + end + unless @methoddef.empty? + buf << dump_emptyline if spacer + spacer = true + buf << dump_methods + end + buf << dump_module_def_end + buf << dump_package_def_end(package) unless package.empty? + buf.gsub(/^\s+$/, '') + end + +private + + def dump_requirepath + format( + @requirepath.collect { |path| + %Q(require '#{path}') + }.join("\n") + ) + end + + def dump_const + dump_static( + @const.sort.collect { |var, value| + %Q(#{var} = #{dump_value(value)}) + }.join("\n") + ) + end + + def dump_code + dump_static(@code.join("\n")) + end + + def dump_static(str) + format(str, 2) + end + + def dump_methods + methods = {} + @methoddef.each do |visibility, method| + (methods[visibility] ||= []) << method + end + str = "" + [:public, :protected, :private].each do |visibility| + if methods[visibility] + str << "\n" unless str.empty? + str << visibility.to_s << "\n\n" unless visibility == :public + str << methods[visibility].collect { |m| format(m.dump, 2) }.join("\n") + end + end + str + end + + def dump_value(value) + if value.respond_to?(:to_src) + value.to_src + else + value + end + end + + def dump_package_def(package) + format(package.collect { |ele| "module #{ele}" }.join("; ")) + "\n\n" + end + + def dump_package_def_end(package) + "\n\n" + format(package.collect { |ele| "end" }.join("; ")) + end + + def dump_module_def + name = @name.to_s.split(/::/) + format("module #{name.last}") + end + + def dump_module_def_end + format("end") + end +end + + +end +end + + +if __FILE__ == $0 + require 'xsd/codegen/moduledef' + include XSD::CodeGen + m = ModuleDef.new("Foo::Bar::HobbitName") + m.def_require("foo/bar") + m.def_require("baz") + m.comment = <<-EOD + foo + bar + baz + EOD + m.def_method("foo") do + <<-EOD + foo.bar = 1 + baz.each do |ele| + ele + 1 + end + EOD + end + m.def_method("baz", "qux") + #m.def_protectedmethod("aaa") + m.def_privatemethod("bbb") + puts m.dump +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/datatypes.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/datatypes.rb new file mode 100644 index 0000000000..bbe6c8578f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/datatypes.rb @@ -0,0 +1,1269 @@ +# XSD4R - XML Schema Datatype implementation. +# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/qname' +require 'xsd/charset' +require 'uri' + + +### +## XMLSchamaDatatypes general definitions. +# +module XSD + + +Namespace = 'http://www.w3.org/2001/XMLSchema' +InstanceNamespace = 'http://www.w3.org/2001/XMLSchema-instance' + +AttrType = 'type' +NilValue = 'true' + +AnyTypeLiteral = 'anyType' +AnySimpleTypeLiteral = 'anySimpleType' +NilLiteral = 'nil' +StringLiteral = 'string' +BooleanLiteral = 'boolean' +DecimalLiteral = 'decimal' +FloatLiteral = 'float' +DoubleLiteral = 'double' +DurationLiteral = 'duration' +DateTimeLiteral = 'dateTime' +TimeLiteral = 'time' +DateLiteral = 'date' +GYearMonthLiteral = 'gYearMonth' +GYearLiteral = 'gYear' +GMonthDayLiteral = 'gMonthDay' +GDayLiteral = 'gDay' +GMonthLiteral = 'gMonth' +HexBinaryLiteral = 'hexBinary' +Base64BinaryLiteral = 'base64Binary' +AnyURILiteral = 'anyURI' +QNameLiteral = 'QName' + +NormalizedStringLiteral = 'normalizedString' +#3.3.2 token +#3.3.3 language +#3.3.4 NMTOKEN +#3.3.5 NMTOKENS +#3.3.6 Name +#3.3.7 NCName +#3.3.8 ID +#3.3.9 IDREF +#3.3.10 IDREFS +#3.3.11 ENTITY +#3.3.12 ENTITIES +IntegerLiteral = 'integer' +NonPositiveIntegerLiteral = 'nonPositiveInteger' +NegativeIntegerLiteral = 'negativeInteger' +LongLiteral = 'long' +IntLiteral = 'int' +ShortLiteral = 'short' +ByteLiteral = 'byte' +NonNegativeIntegerLiteral = 'nonNegativeInteger' +UnsignedLongLiteral = 'unsignedLong' +UnsignedIntLiteral = 'unsignedInt' +UnsignedShortLiteral = 'unsignedShort' +UnsignedByteLiteral = 'unsignedByte' +PositiveIntegerLiteral = 'positiveInteger' + +AttrTypeName = QName.new(InstanceNamespace, AttrType) +AttrNilName = QName.new(InstanceNamespace, NilLiteral) + +AnyTypeName = QName.new(Namespace, AnyTypeLiteral) +AnySimpleTypeName = QName.new(Namespace, AnySimpleTypeLiteral) + +class Error < StandardError; end +class ValueSpaceError < Error; end + + +### +## The base class of all datatypes with Namespace. +# +class NSDBase + @@types = [] + + attr_accessor :type + + def self.inherited(klass) + @@types << klass + end + + def self.types + @@types + end + + def initialize + end + + def init(type) + @type = type + end +end + + +### +## The base class of XSD datatypes. +# +class XSDAnySimpleType < NSDBase + include XSD + Type = QName.new(Namespace, AnySimpleTypeLiteral) + + # @data represents canonical space (ex. Integer: 123). + attr_reader :data + # @is_nil represents this data is nil or not. + attr_accessor :is_nil + + def initialize(value = nil) + init(Type, value) + end + + # true or raise + def check_lexical_format(value) + screen_data(value) + true + end + + # set accepts a string which follows lexical space (ex. String: "+123"), or + # an object which follows canonical space (ex. Integer: 123). + def set(value) + if value.nil? + @is_nil = true + @data = nil + _set(nil) + else + @is_nil = false + _set(screen_data(value)) + end + end + + # to_s creates a string which follows lexical space (ex. String: "123"). + def to_s() + if @is_nil + "" + else + _to_s + end + end + +private + + def init(type, value) + super(type) + set(value) + end + + # raises ValueSpaceError if check failed + def screen_data(value) + value + end + + def _set(value) + @data = value + end + + def _to_s + @data.to_s + end +end + +class XSDNil < XSDAnySimpleType + Type = QName.new(Namespace, NilLiteral) + Value = 'true' + + def initialize(value = nil) + init(Type, value) + end +end + + +### +## Primitive datatypes. +# +class XSDString < XSDAnySimpleType + Type = QName.new(Namespace, StringLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data(value) + unless XSD::Charset.is_ces(value, XSD::Charset.encoding) + raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.") + end + value + end +end + +class XSDBoolean < XSDAnySimpleType + Type = QName.new(Namespace, BooleanLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data(value) + if value.is_a?(String) + str = value.strip + if str == 'true' || str == '1' + true + elsif str == 'false' || str == '0' + false + else + raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") + end + else + value ? true : false + end + end +end + +class XSDDecimal < XSDAnySimpleType + Type = QName.new(Namespace, DecimalLiteral) + + def initialize(value = nil) + init(Type, value) + end + + def nonzero? + (@number != '0') + end + +private + + def screen_data(d) + if d.is_a?(String) + # Integer("00012") => 10 in Ruby. + d.sub!(/^([+\-]?)0*(?=\d)/, "\\1") + end + screen_data_str(d) + end + + def screen_data_str(str) + /^([+\-]?)(\d*)(?:\.(\d*)?)?$/ =~ str.to_s.strip + unless Regexp.last_match + raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") + end + sign = $1 || '+' + int_part = $2 + frac_part = $3 + int_part = '0' if int_part.empty? + frac_part = frac_part ? frac_part.sub(/0+$/, '') : '' + point = - frac_part.size + number = int_part + frac_part + # normalize + if sign == '+' + sign = '' + elsif sign == '-' + if number == '0' + sign = '' + end + end + [sign, point, number] + end + + def _set(data) + if data.nil? + @sign = @point = @number = @data = nil + return + end + @sign, @point, @number = data + @data = _to_s + @data.freeze + end + + # 0.0 -> 0; right? + def _to_s + str = @number.dup + if @point.nonzero? + str[@number.size + @point, 0] = '.' + end + @sign + str + end +end + +module FloatConstants + NaN = 0.0/0.0 + POSITIVE_INF = +1.0/0.0 + NEGATIVE_INF = -1.0/0.0 + POSITIVE_ZERO = +1.0/POSITIVE_INF + NEGATIVE_ZERO = -1.0/POSITIVE_INF + MIN_POSITIVE_SINGLE = 2.0 ** -149 +end + +class XSDFloat < XSDAnySimpleType + include FloatConstants + Type = QName.new(Namespace, FloatLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data(value) + # "NaN".to_f => 0 in some environment. libc? + if value.is_a?(Float) + return narrow32bit(value) + end + str = value.to_s.strip + if str == 'NaN' + NaN + elsif str == 'INF' + POSITIVE_INF + elsif str == '-INF' + NEGATIVE_INF + else + if /^[+\-\.\deE]+$/ !~ str + raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") + end + # Float("-1.4E") might fail on some system. + str << '0' if /e$/i =~ str + begin + return narrow32bit(Float(str)) + rescue ArgumentError + raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") + end + end + end + + def _to_s + if @data.nan? + 'NaN' + elsif @data.infinite? == 1 + 'INF' + elsif @data.infinite? == -1 + '-INF' + else + sign = XSDFloat.positive?(@data) ? '+' : '-' + sign + sprintf("%.10g", @data.abs).sub(/[eE]([+-])?0+/) { 'e' + $1 } + end + end + + # Convert to single-precision 32-bit floating point value. + def narrow32bit(f) + if f.nan? || f.infinite? + f + elsif f.abs < MIN_POSITIVE_SINGLE + XSDFloat.positive?(f) ? POSITIVE_ZERO : NEGATIVE_ZERO + else + f + end + end + + def self.positive?(value) + (1 / value) > 0.0 + end +end + +# Ruby's Float is double-precision 64-bit floating point value. +class XSDDouble < XSDAnySimpleType + include FloatConstants + Type = QName.new(Namespace, DoubleLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data(value) + # "NaN".to_f => 0 in some environment. libc? + if value.is_a?(Float) + return value + end + str = value.to_s.strip + if str == 'NaN' + NaN + elsif str == 'INF' + POSITIVE_INF + elsif str == '-INF' + NEGATIVE_INF + else + begin + return Float(str) + rescue ArgumentError + # '1.4e' cannot be parsed on some architecture. + if /e\z/i =~ str + begin + return Float(str + '0') + rescue ArgumentError + raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") + end + else + raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") + end + end + end + end + + def _to_s + if @data.nan? + 'NaN' + elsif @data.infinite? == 1 + 'INF' + elsif @data.infinite? == -1 + '-INF' + else + sign = (1 / @data > 0.0) ? '+' : '-' + sign + sprintf("%.16g", @data.abs).sub(/[eE]([+-])?0+/) { 'e' + $1 } + end + end +end + +class XSDDuration < XSDAnySimpleType + Type = QName.new(Namespace, DurationLiteral) + + attr_accessor :sign + attr_accessor :year + attr_accessor :month + attr_accessor :day + attr_accessor :hour + attr_accessor :min + attr_accessor :sec + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data(value) + /^([+\-]?)P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/ =~ value.to_s.strip + unless Regexp.last_match + raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.") + end + if ($5 and ((!$2 and !$3 and !$4) or (!$6 and !$7 and !$8))) + # Should we allow 'PT5S' here? + raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.") + end + sign = $1 + year = $2.to_i + month = $3.to_i + day = $4.to_i + hour = $6.to_i + min = $7.to_i + sec = $8 ? XSDDecimal.new($8) : 0 + [sign, year, month, day, hour, min, sec] + end + + def _set(data) + if data.nil? + @sign = @year = @month = @day = @hour = @min = @sec = @data = nil + return + end + @sign, @year, @month, @day, @hour, @min, @sec = data + @data = _to_s + @data.freeze + end + + def _to_s + str = '' + str << @sign if @sign + str << 'P' + l = '' + l << "#{ @year }Y" if @year.nonzero? + l << "#{ @month }M" if @month.nonzero? + l << "#{ @day }D" if @day.nonzero? + r = '' + r << "#{ @hour }H" if @hour.nonzero? + r << "#{ @min }M" if @min.nonzero? + r << "#{ @sec }S" if @sec.nonzero? + str << l + if l.empty? + str << "0D" + end + unless r.empty? + str << "T" << r + end + str + end +end + + +require 'rational' +require 'date' + +module XSDDateTimeImpl + SecInDay = 86400 # 24 * 60 * 60 + + def to_obj(klass) + if klass == Time + to_time + elsif klass == Date + to_date + elsif klass == DateTime + to_datetime + else + nil + end + end + + def to_time + begin + if @data.offset * SecInDay == Time.now.utc_offset + d = @data + usec = (d.sec_fraction * SecInDay * 1000000).round + Time.local(d.year, d.month, d.mday, d.hour, d.min, d.sec, usec) + else + d = @data.newof + usec = (d.sec_fraction * SecInDay * 1000000).round + Time.gm(d.year, d.month, d.mday, d.hour, d.min, d.sec, usec) + end + rescue ArgumentError + nil + end + end + + def to_date + Date.new0(@data.class.jd_to_ajd(@data.jd, 0, 0), 0, @data.start) + end + + def to_datetime + data + end + + def tz2of(str) + /^(?:Z|(?:([+\-])(\d\d):(\d\d))?)$/ =~ str + sign = $1 + hour = $2.to_i + min = $3.to_i + + of = case sign + when '+' + of = +(hour.to_r * 60 + min) / 1440 # 24 * 60 + when '-' + of = -(hour.to_r * 60 + min) / 1440 # 24 * 60 + else + 0 + end + of + end + + def of2tz(offset) + diffmin = offset * 24 * 60 + if diffmin.zero? + 'Z' + else + ((diffmin < 0) ? '-' : '+') << format('%02d:%02d', + (diffmin.abs / 60.0).to_i, (diffmin.abs % 60.0).to_i) + end + end + + def screen_data(t) + # convert t to a DateTime as an internal representation. + if t.respond_to?(:to_datetime) # 1.9 or later + t.to_datetime + elsif t.is_a?(DateTime) + t + elsif t.is_a?(Date) + t = screen_data_str(t) + t <<= 12 if t.year < 0 + t + elsif t.is_a?(Time) + jd = DateTime.civil_to_jd(t.year, t.mon, t.mday, DateTime::ITALY) + fr = DateTime.time_to_day_fraction(t.hour, t.min, [t.sec, 59].min) + + t.usec.to_r / 1000000 / SecInDay + of = t.utc_offset.to_r / SecInDay + DateTime.new0(DateTime.jd_to_ajd(jd, fr, of), of, DateTime::ITALY) + else + screen_data_str(t) + end + end + + def add_tz(s) + s + of2tz(@data.offset) + end +end + +class XSDDateTime < XSDAnySimpleType + include XSDDateTimeImpl + Type = QName.new(Namespace, DateTimeLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data_str(t) + /^([+\-]?\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d(?:\.(\d*))?)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ t.to_s.strip + unless Regexp.last_match + raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.") + end + if $1 == '0000' + raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.") + end + year = $1.to_i + if year < 0 + year += 1 + end + mon = $2.to_i + mday = $3.to_i + hour = $4.to_i + min = $5.to_i + sec = $6.to_i + secfrac = $7 + zonestr = $8 + data = DateTime.civil(year, mon, mday, hour, min, sec, tz2of(zonestr)) + if secfrac + diffday = secfrac.to_i.to_r / (10 ** secfrac.size) / SecInDay + data += diffday + # FYI: new0 and jd_to_rjd are not necessary to use if you don't have + # exceptional reason. + end + [data, secfrac] + end + + def _set(data) + if data.nil? + @data = @secfrac = nil + return + end + @data, @secfrac = data + end + + def _to_s + year = (@data.year > 0) ? @data.year : @data.year - 1 + s = format('%.4d-%02d-%02dT%02d:%02d:%02d', + year, @data.mon, @data.mday, @data.hour, @data.min, @data.sec) + if @data.sec_fraction.nonzero? + if @secfrac + s << ".#{ @secfrac }" + else + s << sprintf("%.16f", + (@data.sec_fraction * SecInDay).to_f).sub(/^0/, '').sub(/0*$/, '') + end + end + add_tz(s) + end +end + +class XSDTime < XSDAnySimpleType + include XSDDateTimeImpl + Type = QName.new(Namespace, TimeLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data_str(t) + /^(\d\d):(\d\d):(\d\d(?:\.(\d*))?)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ t.to_s.strip + unless Regexp.last_match + raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.") + end + hour = $1.to_i + min = $2.to_i + sec = $3.to_i + secfrac = $4 + zonestr = $5 + data = DateTime.civil(1, 1, 1, hour, min, sec, tz2of(zonestr)) + if secfrac + diffday = secfrac.to_i.to_r / (10 ** secfrac.size) / SecInDay + data += diffday + end + [data, secfrac] + end + + def _set(data) + if data.nil? + @data = @secfrac = nil + return + end + @data, @secfrac = data + end + + def _to_s + s = format('%02d:%02d:%02d', @data.hour, @data.min, @data.sec) + if @data.sec_fraction.nonzero? + if @secfrac + s << ".#{ @secfrac }" + else + s << sprintf("%.16f", + (@data.sec_fraction * SecInDay).to_f).sub(/^0/, '').sub(/0*$/, '') + end + end + add_tz(s) + end +end + +class XSDDate < XSDAnySimpleType + include XSDDateTimeImpl + Type = QName.new(Namespace, DateLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data_str(t) + /^([+\-]?\d{4,})-(\d\d)-(\d\d)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ t.to_s.strip + unless Regexp.last_match + raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.") + end + year = $1.to_i + if year < 0 + year += 1 + end + mon = $2.to_i + mday = $3.to_i + zonestr = $4 + DateTime.civil(year, mon, mday, 0, 0, 0, tz2of(zonestr)) + end + + def _to_s + year = (@data.year > 0) ? @data.year : @data.year - 1 + s = format('%.4d-%02d-%02d', year, @data.mon, @data.mday) + add_tz(s) + end +end + +class XSDGYearMonth < XSDAnySimpleType + include XSDDateTimeImpl + Type = QName.new(Namespace, GYearMonthLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data_str(t) + /^([+\-]?\d{4,})-(\d\d)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ t.to_s.strip + unless Regexp.last_match + raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.") + end + year = $1.to_i + if year < 0 + year += 1 + end + mon = $2.to_i + zonestr = $3 + DateTime.civil(year, mon, 1, 0, 0, 0, tz2of(zonestr)) + end + + def _to_s + year = (@data.year > 0) ? @data.year : @data.year - 1 + s = format('%.4d-%02d', year, @data.mon) + add_tz(s) + end +end + +class XSDGYear < XSDAnySimpleType + include XSDDateTimeImpl + Type = QName.new(Namespace, GYearLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data_str(t) + /^([+\-]?\d{4,})(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ t.to_s.strip + unless Regexp.last_match + raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.") + end + year = $1.to_i + if year < 0 + year += 1 + end + zonestr = $2 + DateTime.civil(year, 1, 1, 0, 0, 0, tz2of(zonestr)) + end + + def _to_s + year = (@data.year > 0) ? @data.year : @data.year - 1 + s = format('%.4d', year) + add_tz(s) + end +end + +class XSDGMonthDay < XSDAnySimpleType + include XSDDateTimeImpl + Type = QName.new(Namespace, GMonthDayLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data_str(t) + /^(\d\d)-(\d\d)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ t.to_s.strip + unless Regexp.last_match + raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.") + end + mon = $1.to_i + mday = $2.to_i + zonestr = $3 + DateTime.civil(1, mon, mday, 0, 0, 0, tz2of(zonestr)) + end + + def _to_s + s = format('%02d-%02d', @data.mon, @data.mday) + add_tz(s) + end +end + +class XSDGDay < XSDAnySimpleType + include XSDDateTimeImpl + Type = QName.new(Namespace, GDayLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data_str(t) + /^(\d\d)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ t.to_s.strip + unless Regexp.last_match + raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.") + end + mday = $1.to_i + zonestr = $2 + DateTime.civil(1, 1, mday, 0, 0, 0, tz2of(zonestr)) + end + + def _to_s + s = format('%02d', @data.mday) + add_tz(s) + end +end + +class XSDGMonth < XSDAnySimpleType + include XSDDateTimeImpl + Type = QName.new(Namespace, GMonthLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data_str(t) + /^(\d\d)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ t.to_s.strip + unless Regexp.last_match + raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.") + end + mon = $1.to_i + zonestr = $2 + DateTime.civil(1, mon, 1, 0, 0, 0, tz2of(zonestr)) + end + + def _to_s + s = format('%02d', @data.mon) + add_tz(s) + end +end + +class XSDHexBinary < XSDAnySimpleType + Type = QName.new(Namespace, HexBinaryLiteral) + + # String in Ruby could be a binary. + def initialize(value = nil) + init(Type, value) + end + + def set_encoded(value) + if /^[0-9a-fA-F]*$/ !~ value + raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.") + end + @data = String.new(value).strip + @is_nil = false + end + + def string + [@data].pack("H*") + end + +private + + def screen_data(value) + value.unpack("H*")[0].tr('a-f', 'A-F') + end +end + +class XSDBase64Binary < XSDAnySimpleType + Type = QName.new(Namespace, Base64BinaryLiteral) + + # String in Ruby could be a binary. + def initialize(value = nil) + init(Type, value) + end + + def set_encoded(value) + if /^[A-Za-z0-9+\/=]*$/ !~ value + raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.") + end + @data = String.new(value).strip + @is_nil = false + end + + def string + @data.unpack("m")[0] + end + +private + + def screen_data(value) + [value].pack("m").strip + end +end + +class XSDAnyURI < XSDAnySimpleType + Type = QName.new(Namespace, AnyURILiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data(value) + begin + URI.parse(value.to_s.strip) + rescue URI::InvalidURIError + raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.") + end + end +end + +class XSDQName < XSDAnySimpleType + Type = QName.new(Namespace, QNameLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data(value) + /^(?:([^:]+):)?([^:]+)$/ =~ value.to_s.strip + unless Regexp.last_match + raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.") + end + prefix = $1 + localpart = $2 + [prefix, localpart] + end + + def _set(data) + if data.nil? + @prefix = @localpart = @data = nil + return + end + @prefix, @localpart = data + @data = _to_s + @data.freeze + end + + def _to_s + if @prefix + "#{ @prefix }:#{ @localpart }" + else + "#{ @localpart }" + end + end +end + + +### +## Derived types +# +class XSDNormalizedString < XSDString + Type = QName.new(Namespace, NormalizedStringLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data(value) + if /[\t\r\n]/ =~ value + raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.") + end + super + end +end + +class XSDInteger < XSDDecimal + Type = QName.new(Namespace, IntegerLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def screen_data_str(str) + begin + data = Integer(str) + rescue ArgumentError + raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") + end + unless validate(data) + raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") + end + data + end + + def _set(value) + @data = value + end + + def _to_s() + @data.to_s + end + + def validate(v) + max = maxinclusive + min = mininclusive + (max.nil? or v <= max) and (min.nil? or v >= min) + end + + def maxinclusive + nil + end + + def mininclusive + nil + end + + PositiveMinInclusive = 1 + def positive(v) + PositiveMinInclusive <= v + end +end + +class XSDNonPositiveInteger < XSDInteger + Type = QName.new(Namespace, NonPositiveIntegerLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + 0 + end + + def mininclusive + nil + end +end + +class XSDNegativeInteger < XSDNonPositiveInteger + Type = QName.new(Namespace, NegativeIntegerLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + -1 + end + + def mininclusive + nil + end +end + +class XSDLong < XSDInteger + Type = QName.new(Namespace, LongLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +9223372036854775807 + end + + def mininclusive + -9223372036854775808 + end +end + +class XSDInt < XSDLong + Type = QName.new(Namespace, IntLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +2147483647 + end + + def mininclusive + -2147483648 + end +end + +class XSDShort < XSDInt + Type = QName.new(Namespace, ShortLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +32767 + end + + def mininclusive + -32768 + end +end + +class XSDByte < XSDShort + Type = QName.new(Namespace, ByteLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +127 + end + + def mininclusive + -128 + end +end + +class XSDNonNegativeInteger < XSDInteger + Type = QName.new(Namespace, NonNegativeIntegerLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + nil + end + + def mininclusive + 0 + end +end + +class XSDUnsignedLong < XSDNonNegativeInteger + Type = QName.new(Namespace, UnsignedLongLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +18446744073709551615 + end + + def mininclusive + 0 + end +end + +class XSDUnsignedInt < XSDUnsignedLong + Type = QName.new(Namespace, UnsignedIntLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +4294967295 + end + + def mininclusive + 0 + end +end + +class XSDUnsignedShort < XSDUnsignedInt + Type = QName.new(Namespace, UnsignedShortLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +65535 + end + + def mininclusive + 0 + end +end + +class XSDUnsignedByte < XSDUnsignedShort + Type = QName.new(Namespace, UnsignedByteLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +255 + end + + def mininclusive + 0 + end +end + +class XSDPositiveInteger < XSDNonNegativeInteger + Type = QName.new(Namespace, PositiveIntegerLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + nil + end + + def mininclusive + 1 + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/datatypes1999.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/datatypes1999.rb new file mode 100644 index 0000000000..c7d6479e54 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/datatypes1999.rb @@ -0,0 +1,20 @@ +# XSD4R - XML Schema Datatype 1999 support +# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/datatypes' + + +module XSD + Namespace.replace('http://www.w3.org/1999/XMLSchema') + InstanceNamespace.replace('http://www.w3.org/1999/XMLSchema-instance') + AnyTypeLiteral.replace('ur-type') + AnySimpleTypeLiteral.replace('ur-type') + NilLiteral.replace('null') + NilValue.replace('1') + DateTimeLiteral.replace('timeInstant') +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/iconvcharset.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/iconvcharset.rb new file mode 100644 index 0000000000..7e629d569b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/iconvcharset.rb @@ -0,0 +1,33 @@ +# XSD4R - Charset handling with iconv. +# Copyright (C) 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'iconv' + + +module XSD + + +class IconvCharset + def self.safe_iconv(to, from, str) + iconv = Iconv.new(to, from) + out = "" + begin + out << iconv.iconv(str) + rescue Iconv::IllegalSequence => e + out << e.success + ch, str = e.failed.split(//, 2) + out << '?' + warn("Failed to convert #{ch}") + retry + end + return out + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/mapping.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/mapping.rb new file mode 100644 index 0000000000..06d30d4cbf --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/mapping.rb @@ -0,0 +1,42 @@ +# XSD4R - XML Mapping for Ruby +# Copyright (C) 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require "soap/parser" +require 'soap/encodingstyle/literalHandler' +require "soap/generator" +require "soap/mapping" +require "soap/mapping/wsdlliteralregistry" + + +module XSD + + +module Mapping + MappingRegistry = SOAP::Mapping::WSDLLiteralRegistry.new + MappingOpt = {:default_encodingstyle => SOAP::LiteralNamespace} + + def self.obj2xml(obj, elename = nil, io = nil) + if !elename.nil? and !elename.is_a?(XSD::QName) + elename = XSD::QName.new(nil, elename) + end + elename ||= XSD::QName.new(nil, SOAP::Mapping.name2elename(obj.class.to_s)) + soap = SOAP::Mapping.obj2soap(obj, MappingRegistry) + soap.elename = elename + generator = SOAP::SOAPGenerator.new(MappingOpt) + generator.generate(soap, io) + end + + def self.xml2obj(stream) + parser = SOAP::Parser.new(MappingOpt) + soap = parser.parse(stream) + SOAP::Mapping.soap2obj(soap, MappingRegistry) + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/namedelements.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/namedelements.rb new file mode 100644 index 0000000000..a13396bb71 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/namedelements.rb @@ -0,0 +1,95 @@ +# XSD4R - WSDL named element collection. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +module XSD + + +class NamedElements + include Enumerable + + def initialize + @elements = [] + @cache = {} + end + + def dup + o = NamedElements.new + o.elements = @elements.dup + o + end + + def freeze + super + @elements.freeze + self + end + + def empty? + size == 0 + end + + def size + @elements.size + end + + def [](idx) + if idx.is_a?(Numeric) + @elements[idx] + else + @cache[idx] ||= @elements.find { |item| item.name == idx } + end + end + + def find_name(name) + @elements.find { |item| item.name.name == name } + end + + def keys + collect { |element| element.name } + end + + def each + @elements.each do |element| + yield(element) + end + end + + def <<(rhs) + @elements << rhs + self + end + + def delete(rhs) + @elements.delete(rhs) + end + + def +(rhs) + o = NamedElements.new + o.elements = @elements + rhs.elements + o + end + + def concat(rhs) + @elements.concat(rhs.elements) + self + end + + Empty = NamedElements.new.freeze + +protected + + def elements=(rhs) + @elements = rhs + end + + def elements + @elements + end +end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/ns.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/ns.rb new file mode 100644 index 0000000000..53eeae7130 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/ns.rb @@ -0,0 +1,140 @@ +# XSD4R - XML Schema Namespace library +# Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/datatypes' + + +module XSD + + +class NS + class Assigner + def initialize + @count = 0 + end + + def assign(ns) + @count += 1 + "n#{@count}" + end + end + + attr_reader :default_namespace + + class FormatError < Error; end + +public + + def initialize(tag2ns = {}) + @tag2ns = tag2ns + @assigner = nil + @ns2tag = {} + @tag2ns.each do |tag, ns| + @ns2tag[ns] = tag + end + @default_namespace = nil + end + + def assign(ns, tag = nil) + if (tag == '') + @default_namespace = ns + tag + else + @assigner ||= Assigner.new + tag ||= @assigner.assign(ns) + @ns2tag[ns] = tag + @tag2ns[tag] = ns + tag + end + end + + def assigned?(ns) + @default_namespace == ns or @ns2tag.key?(ns) + end + + def assigned_tag?(tag) + @tag2ns.key?(tag) + end + + def clone_ns + cloned = NS.new(@tag2ns.dup) + cloned.assigner = @assigner + cloned.assign(@default_namespace, '') if @default_namespace + cloned + end + + def name(name) + if (name.namespace == @default_namespace) + name.name + elsif @ns2tag.key?(name.namespace) + "#{@ns2tag[name.namespace]}:#{name.name}" + else + raise FormatError.new("namespace: #{name.namespace} not defined yet") + end + end + + def compare(ns, name, rhs) + if (ns == @default_namespace) + return true if (name == rhs) + end + @tag2ns.each do |assigned_tag, assigned_ns| + if assigned_ns == ns && "#{assigned_tag}:#{name}" == rhs + return true + end + end + false + end + + # $1 and $2 are necessary. + ParseRegexp = Regexp.new('^([^:]+)(?::(.+))?$') + + def parse(str, local = false) + if ParseRegexp =~ str + if (name = $2) and (ns = @tag2ns[$1]) + return XSD::QName.new(ns, name) + end + end + XSD::QName.new(local ? nil : @default_namespace, str) + end + + # For local attribute key parsing + # + # => + # {}bar, {urn:a}baz + def parse_local(elem) + ParseRegexp =~ elem + if $2 + ns = @tag2ns[$1] + name = $2 + if !ns + raise FormatError.new("unknown namespace qualifier: #{$1}") + end + elsif $1 + ns = nil + name = $1 + else + raise FormatError.new("illegal element format: #{elem}") + end + XSD::QName.new(ns, name) + end + + def each_ns + @ns2tag.each do |ns, tag| + yield(ns, tag) + end + end + +protected + + def assigner=(assigner) + @assigner = assigner + end +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/qname.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/qname.rb new file mode 100644 index 0000000000..bb9763a69a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/qname.rb @@ -0,0 +1,78 @@ +# XSD4R - XML QName definition. +# Copyright (C) 2002, 2003, 2004 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +module XSD + + +class QName + attr_accessor :namespace + attr_accessor :name + attr_accessor :source + + def initialize(namespace = nil, name = nil) + @namespace = namespace + @name = name + @source = nil + end + + def dup_name(name) + XSD::QName.new(@namespace, name) + end + + def dump + ns = @namespace.nil? ? 'nil' : @namespace.dump + name = @name.nil? ? 'nil' : @name.dump + "XSD::QName.new(#{ns}, #{name})" + end + + def match(rhs) + if rhs.namespace and (rhs.namespace != @namespace) + return false + end + if rhs.name and (rhs.name != @name) + return false + end + true + end + + def ==(rhs) + !rhs.nil? and @namespace == rhs.namespace and @name == rhs.name + end + + def ===(rhs) + (self == rhs) + end + + def eql?(rhs) + (self == rhs) + end + + def hash + @namespace.hash ^ @name.hash + end + + def to_s + "{#{ namespace }}#{ name }" + end + + def inspect + sprintf("#<%s:0x%x %s>", self.class.name, __id__, + "{#{ namespace }}#{ name }") + end + + NormalizedNameRegexp = /^\{([^}]*)\}(.*)$/ + def parse(str) + NormalizedNameRegexp =~ str + self.new($1, $2) + end + + EMPTY = QName.new.freeze +end + + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser.rb new file mode 100644 index 0000000000..e79e36c58c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser.rb @@ -0,0 +1,61 @@ +# XSD4R - XML Instance parser library. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/xmlparser/parser' + + +module XSD + + +module XMLParser + def create_parser(host, opt) + XSD::XMLParser::Parser.create_parser(host, opt) + end + module_function :create_parser + + # $1 is necessary. + NSParseRegexp = Regexp.new('^xmlns:?(.*)$') + + def filter_ns(ns, attrs) + return attrs if attrs.nil? or attrs.empty? + newattrs = {} + attrs.each do |key, value| + if (NSParseRegexp =~ key) + # '' means 'default namespace'. + tag = $1 || '' + ns.assign(value, tag) + else + newattrs[key] = value + end + end + newattrs + end + module_function :filter_ns +end + + +end + + +# Try to load XML processor. +loaded = false +[ + 'xsd/xmlparser/xmlparser', + 'xsd/xmlparser/xmlscanner', + 'xsd/xmlparser/rexmlparser', +].each do |lib| + begin + require lib + loaded = true + break + rescue LoadError + end +end +unless loaded + raise RuntimeError.new("XML processor module not found.") +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser/parser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser/parser.rb new file mode 100644 index 0000000000..ad01d55aa0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser/parser.rb @@ -0,0 +1,96 @@ +# XSD4R - XML Instance parser library. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/qname' +require 'xsd/ns' +require 'xsd/charset' + + +module XSD +module XMLParser + + +class Parser + class ParseError < Error; end + class FormatDecodeError < ParseError; end + class UnknownElementError < FormatDecodeError; end + class UnknownAttributeError < FormatDecodeError; end + class UnexpectedElementError < FormatDecodeError; end + class ElementConstraintError < FormatDecodeError; end + + @@parser_factory = nil + + def self.factory + @@parser_factory + end + + def self.create_parser(host, opt = {}) + @@parser_factory.new(host, opt) + end + + def self.add_factory(factory) + if $DEBUG + puts "Set #{ factory } as XML processor." + end + @@parser_factory = factory + end + +public + + attr_accessor :charset + + def initialize(host, opt = {}) + @host = host + @charset = opt[:charset] || nil + end + + def parse(string_or_readable) + @textbuf = '' + prologue + do_parse(string_or_readable) + epilogue + end + +private + + def do_parse(string_or_readable) + raise NotImplementError.new( + 'Method do_parse must be defined in derived class.') + end + + def start_element(name, attrs) + @host.start_element(name, attrs) + end + + def characters(text) + @host.characters(text) + end + + def end_element(name) + @host.end_element(name) + end + + def prologue + end + + def epilogue + end + + def xmldecl_encoding=(charset) + if @charset.nil? + @charset = charset + else + # Definition in a stream (like HTTP) has a priority. + p "encoding definition: #{ charset } is ignored." if $DEBUG + end + end +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser/rexmlparser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser/rexmlparser.rb new file mode 100644 index 0000000000..61da9aafc7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser/rexmlparser.rb @@ -0,0 +1,54 @@ +# XSD4R - REXMLParser XML parser library. +# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/xmlparser' +require 'rexml/streamlistener' +require 'rexml/document' + + +module XSD +module XMLParser + + +class REXMLParser < XSD::XMLParser::Parser + include REXML::StreamListener + + def do_parse(string_or_readable) + source = nil + source = REXML::SourceFactory.create_from(string_or_readable) + source.encoding = charset if charset + # Listener passes a String in utf-8. + @charset = 'utf-8' + REXML::Document.parse_stream(source, self) + end + + def epilogue + end + + def tag_start(name, attrs) + start_element(name, attrs) + end + + def tag_end(name) + end_element(name) + end + + def text(text) + characters(text) + end + + def xmldecl(version, encoding, standalone) + # Version should be checked. + end + + add_factory(self) +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser/xmlparser.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser/xmlparser.rb new file mode 100644 index 0000000000..6db914cd37 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser/xmlparser.rb @@ -0,0 +1,50 @@ +# XSD4R - XMLParser XML parser library. +# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/xmlparser' +require 'xml/parser' + + +module XSD +module XMLParser + + +class XMLParser < XSD::XMLParser::Parser + class Listener < XML::Parser + begin + require 'xml/encoding-ja' + include XML::Encoding_ja + rescue LoadError + # uconv may not be installed. + end + end + + def do_parse(string_or_readable) + # XMLParser passes a String in utf-8. + @charset = 'utf-8' + @parser = Listener.new + @parser.parse(string_or_readable) do |type, name, data| + case type + when XML::Parser::START_ELEM + start_element(name, data) + when XML::Parser::END_ELEM + end_element(name) + when XML::Parser::CDATA + characters(data) + else + raise FormatDecodeError.new("Unexpected XML: #{ type }/#{ name }/#{ data }.") + end + end + end + + add_factory(self) +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser/xmlscanner.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser/xmlscanner.rb new file mode 100644 index 0000000000..c80dc23910 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/xsd/xmlparser/xmlscanner.rb @@ -0,0 +1,147 @@ +# XSD4R - XMLScan XML parser library. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi . + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'xsd/xmlparser' +require 'xmlscan/scanner' + + +module XSD +module XMLParser + + +class XMLScanner < XSD::XMLParser::Parser + include XMLScan::Visitor + + def do_parse(string_or_readable) + @attrs = {} + @curattr = nil + @scanner = XMLScan::XMLScanner.new(self) + @scanner.kcode = XSD::Charset.charset_str(charset) if charset + @scanner.parse(string_or_readable) + end + + def scanner_kcode=(charset) + @scanner.kcode = XSD::Charset.charset_str(charset) if charset + self.xmldecl_encoding = charset + end + + ENTITY_REF_MAP = { + 'lt' => '<', + 'gt' => '>', + 'amp' => '&', + 'quot' => '"', + 'apos' => '\'' + } + + def parse_error(msg) + raise ParseError.new(msg) + end + + def wellformed_error(msg) + raise NotWellFormedError.new(msg) + end + + def valid_error(msg) + raise NotValidError.new(msg) + end + + def warning(msg) + p msg if $DEBUG + end + + # def on_xmldecl; end + + def on_xmldecl_version(str) + # 1.0 expected. + end + + def on_xmldecl_encoding(str) + self.scanner_kcode = str + end + + # def on_xmldecl_standalone(str); end + + # def on_xmldecl_other(name, value); end + + # def on_xmldecl_end; end + + # def on_doctype(root, pubid, sysid); end + + # def on_prolog_space(str); end + + # def on_comment(str); end + + # def on_pi(target, pi); end + + def on_chardata(str) + characters(str) + end + + # def on_cdata(str); end + + def on_etag(name) + end_element(name) + end + + def on_entityref(ref) + characters(ENTITY_REF_MAP[ref]) + end + + def on_charref(code) + characters([code].pack('U')) + end + + def on_charref_hex(code) + on_charref(code) + end + + # def on_start_document; end + + # def on_end_document; end + + def on_stag(name) + @attrs = {} + end + + def on_attribute(name) + @attrs[name] = @curattr = '' + end + + def on_attr_value(str) + @curattr << str + end + + def on_attr_entityref(ref) + @curattr << ENTITY_REF_MAP[ref] + end + + def on_attr_charref(code) + @curattr << [code].pack('U') + end + + def on_attr_charref_hex(code) + on_attr_charref(code) + end + + # def on_attribute_end(name); end + + def on_stag_end_empty(name) + on_stag_end(name) + on_etag(name) + end + + def on_stag_end(name) + start_element(name, @attrs) + end + + add_factory(self) +end + + +end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml.rb new file mode 100644 index 0000000000..9f0e044de3 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml.rb @@ -0,0 +1,436 @@ +# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4 +# $Id: yaml.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# +# = yaml.rb: top-level module with methods for loading and parsing YAML documents +# +# Author:: why the lucky stiff +# + +require 'stringio' +require 'yaml/error' +require 'yaml/syck' +require 'yaml/tag' +require 'yaml/stream' +require 'yaml/constants' + +# == YAML +# +# YAML(tm) (rhymes with 'camel') is a +# straightforward machine parsable data serialization format designed for +# human readability and interaction with scripting languages such as Perl +# and Python. YAML is optimized for data serialization, formatted +# dumping, configuration files, log files, Internet messaging and +# filtering. This specification describes the YAML information model and +# serialization format. Together with the Unicode standard for characters, it +# provides all the information necessary to understand YAML Version 1.0 +# and construct computer programs to process it. +# +# See http://yaml.org/ for more information. For a quick tutorial, please +# visit YAML In Five Minutes (http://yaml.kwiki.org/?YamlInFiveMinutes). +# +# == About This Library +# +# The YAML 1.0 specification outlines four stages of YAML loading and dumping. +# This library honors all four of those stages, although data is really only +# available to you in three stages. +# +# The four stages are: native, representation, serialization, and presentation. +# +# The native stage refers to data which has been loaded completely into Ruby's +# own types. (See +YAML::load+.) +# +# The representation stage means data which has been composed into +# +YAML::BaseNode+ objects. In this stage, the document is available as a +# tree of node objects. You can perform YPath queries and transformations +# at this level. (See +YAML::parse+.) +# +# The serialization stage happens inside the parser. The YAML parser used in +# Ruby is called Syck. Serialized nodes are available in the extension as +# SyckNode structs. +# +# The presentation stage is the YAML document itself. This is accessible +# to you as a string. (See +YAML::dump+.) +# +# For more information about the various information models, see Chapter +# 3 of the YAML 1.0 Specification (http://yaml.org/spec/#id2491269). +# +# The YAML module provides quick access to the most common loading (YAML::load) +# and dumping (YAML::dump) tasks. This module also provides an API for registering +# global types (YAML::add_domain_type). +# +# == Example +# +# A simple round-trip (load and dump) of an object. +# +# require "yaml" +# +# test_obj = ["dogs", "cats", "badgers"] +# +# yaml_obj = YAML::dump( test_obj ) +# # -> --- +# - dogs +# - cats +# - badgers +# ruby_obj = YAML::load( yaml_obj ) +# # => ["dogs", "cats", "badgers"] +# ruby_obj == test_obj +# # => true +# +# To register your custom types with the global resolver, use +add_domain_type+. +# +# YAML::add_domain_type( "your-site.com,2004", "widget" ) do |type, val| +# Widget.new( val ) +# end +# +module YAML + + Resolver = YAML::Syck::Resolver + DefaultResolver = YAML::Syck::DefaultResolver + DefaultResolver.use_types_at( @@tagged_classes ) + GenericResolver = YAML::Syck::GenericResolver + Parser = YAML::Syck::Parser + Emitter = YAML::Syck::Emitter + + # Returns a new default parser + def YAML.parser; Parser.new.set_resolver( YAML.resolver ); end + # Returns a new generic parser + def YAML.generic_parser; Parser.new.set_resolver( GenericResolver ); end + # Returns the default resolver + def YAML.resolver; DefaultResolver; end + # Returns a new default emitter + def YAML.emitter; Emitter.new.set_resolver( YAML.resolver ); end + + # + # Converts _obj_ to YAML and writes the YAML result to _io_. + # + # File.open( 'animals.yaml', 'w' ) do |out| + # YAML.dump( ['badger', 'elephant', 'tiger'], out ) + # end + # + # If no _io_ is provided, a string containing the dumped YAML + # is returned. + # + # YAML.dump( :locked ) + # #=> "--- :locked" + # + def YAML.dump( obj, io = nil ) + obj.to_yaml( io || io2 = StringIO.new ) + io || ( io2.rewind; io2.read ) + end + + # + # Load a document from the current _io_ stream. + # + # File.open( 'animals.yaml' ) { |yf| YAML::load( yf ) } + # #=> ['badger', 'elephant', 'tiger'] + # + # Can also load from a string. + # + # YAML.load( "--- :locked" ) + # #=> :locked + # + def YAML.load( io ) + yp = parser.load( io ) + end + + # + # Load a document from the file located at _filepath_. + # + # YAML.load_file( 'animals.yaml' ) + # #=> ['badger', 'elephant', 'tiger'] + # + def YAML.load_file( filepath ) + File.open( filepath ) do |f| + load( f ) + end + end + + # + # Parse the first document from the current _io_ stream + # + # File.open( 'animals.yaml' ) { |yf| YAML::load( yf ) } + # #=> #, + # #, + # #]> + # + # Can also load from a string. + # + # YAML.parse( "--- :locked" ) + # #=> # + # + def YAML.parse( io ) + yp = generic_parser.load( io ) + end + + # + # Parse a document from the file located at _filepath_. + # + # YAML.parse_file( 'animals.yaml' ) + # #=> #, + # #, + # #]> + # + def YAML.parse_file( filepath ) + File.open( filepath ) do |f| + parse( f ) + end + end + + # + # Calls _block_ with each consecutive document in the YAML + # stream contained in _io_. + # + # File.open( 'many-docs.yaml' ) do |yf| + # YAML.each_document( yf ) do |ydoc| + # ## ydoc contains the single object + # ## from the YAML document + # end + # end + # + def YAML.each_document( io, &block ) + yp = parser.load_documents( io, &block ) + end + + # + # Calls _block_ with each consecutive document in the YAML + # stream contained in _io_. + # + # File.open( 'many-docs.yaml' ) do |yf| + # YAML.load_documents( yf ) do |ydoc| + # ## ydoc contains the single object + # ## from the YAML document + # end + # end + # + def YAML.load_documents( io, &doc_proc ) + YAML.each_document( io, &doc_proc ) + end + + # + # Calls _block_ with a tree of +YAML::BaseNodes+, one tree for + # each consecutive document in the YAML stream contained in _io_. + # + # File.open( 'many-docs.yaml' ) do |yf| + # YAML.each_node( yf ) do |ydoc| + # ## ydoc contains a tree of nodes + # ## from the YAML document + # end + # end + # + def YAML.each_node( io, &doc_proc ) + yp = generic_parser.load_documents( io, &doc_proc ) + end + + # + # Calls _block_ with a tree of +YAML::BaseNodes+, one tree for + # each consecutive document in the YAML stream contained in _io_. + # + # File.open( 'many-docs.yaml' ) do |yf| + # YAML.parse_documents( yf ) do |ydoc| + # ## ydoc contains a tree of nodes + # ## from the YAML document + # end + # end + # + def YAML.parse_documents( io, &doc_proc ) + YAML.each_node( io, &doc_proc ) + end + + # + # Loads all documents from the current _io_ stream, + # returning a +YAML::Stream+ object containing all + # loaded documents. + # + def YAML.load_stream( io ) + d = nil + parser.load_documents( io ) do |doc| + d = YAML::Stream.new if not d + d.add( doc ) + end + return d + end + + # + # Returns a YAML stream containing each of the items in +objs+, + # each having their own document. + # + # YAML.dump_stream( 0, [], {} ) + # #=> --- 0 + # --- [] + # --- {} + # + def YAML.dump_stream( *objs ) + d = YAML::Stream.new + objs.each do |doc| + d.add( doc ) + end + d.emit + end + + # + # Add a global handler for a YAML domain type. + # + def YAML.add_domain_type( domain, type_tag, &transfer_proc ) + resolver.add_type( "tag:#{ domain }:#{ type_tag }", transfer_proc ) + end + + # + # Add a transfer method for a builtin type + # + def YAML.add_builtin_type( type_tag, &transfer_proc ) + resolver.add_type( "tag:yaml.org,2002:#{ type_tag }", transfer_proc ) + end + + # + # Add a transfer method for a builtin type + # + def YAML.add_ruby_type( type_tag, &transfer_proc ) + resolver.add_type( "tag:ruby.yaml.org,2002:#{ type_tag }", transfer_proc ) + end + + # + # Add a private document type + # + def YAML.add_private_type( type_re, &transfer_proc ) + resolver.add_type( "x-private:" + type_re, transfer_proc ) + end + + # + # Detect typing of a string + # + def YAML.detect_implicit( val ) + resolver.detect_implicit( val ) + end + + # + # Convert a type_id to a taguri + # + def YAML.tagurize( val ) + resolver.tagurize( val ) + end + + # + # Apply a transfer method to a Ruby object + # + def YAML.transfer( type_id, obj ) + resolver.transfer( YAML.tagurize( type_id ), obj ) + end + + # + # Apply any implicit a node may qualify for + # + def YAML.try_implicit( obj ) + YAML.transfer( YAML.detect_implicit( obj ), obj ) + end + + # + # Method to extract colon-seperated type and class, returning + # the type and the constant of the class + # + def YAML.read_type_class( type, obj_class ) + scheme, domain, type, tclass = type.split( ':', 4 ) + tclass.split( "::" ).each { |c| obj_class = obj_class.const_get( c ) } if tclass + return [ type, obj_class ] + end + + # + # Allocate blank object + # + def YAML.object_maker( obj_class, val ) + if Hash === val + o = obj_class.allocate + val.each_pair { |k,v| + o.instance_variable_set("@#{k}", v) + } + o + else + raise YAML::Error, "Invalid object explicitly tagged !ruby/Object: " + val.inspect + end + end + + # + # Allocate an Emitter if needed + # + def YAML.quick_emit( oid, opts = {}, &e ) + out = + if opts.is_a? YAML::Emitter + opts + else + emitter.reset( opts ) + end + out.emit( oid, &e ) + end + +end + +require 'yaml/rubytypes' +require 'yaml/types' + +module Kernel + # + # ryan:: You know how Kernel.p is a really convenient way to dump ruby + # structures? The only downside is that it's not as legible as + # YAML. + # + # _why:: (listening) + # + # ryan:: I know you don't want to urinate all over your users' namespaces. + # But, on the other hand, convenience of dumping for debugging is, + # IMO, a big YAML use case. + # + # _why:: Go nuts! Have a pony parade! + # + # ryan:: Either way, I certainly will have a pony parade. + # + + # Prints any supplied _objects_ out in YAML. Intended as + # a variation on +Kernel::p+. + # + # S = Struct.new(:name, :state) + # s = S['dave', 'TX'] + # y s + # + # _produces:_ + # + # --- !ruby/struct:S + # name: dave + # state: TX + # + def y( object, *objects ) + objects.unshift object + puts( if objects.length == 1 + YAML::dump( *objects ) + else + YAML::dump_stream( *objects ) + end ) + end + private :y +end + + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/baseemitter.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/baseemitter.rb new file mode 100644 index 0000000000..1aef152749 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/baseemitter.rb @@ -0,0 +1,247 @@ +# +# BaseEmitter +# + +require 'yaml/constants' +require 'yaml/encoding' +require 'yaml/error' + +module YAML + + module BaseEmitter + + def options( opt = nil ) + if opt + @options[opt] || YAML::DEFAULTS[opt] + else + @options + end + end + + def options=( opt ) + @options = opt + end + + # + # Emit binary data + # + def binary_base64( value ) + self << "!binary " + self.node_text( [value].pack("m"), '|' ) + end + + # + # Emit plain, normal flowing text + # + def node_text( value, block = nil ) + @seq_map = false + valx = value.dup + unless block + block = + if options(:UseBlock) + '|' + elsif not options(:UseFold) and valx =~ /\n[ \t]/ and not valx =~ /#{YAML::ESCAPE_CHAR}/ + '|' + else + '>' + end + + indt = $&.to_i if block =~ /\d+/ + if valx =~ /(\A\n*[ \t#]|^---\s+)/ + indt = options(:Indent) unless indt.to_i > 0 + block += indt.to_s + end + + block += + if valx =~ /\n\Z\n/ + "+" + elsif valx =~ /\Z\n/ + "" + else + "-" + end + end + block += "\n" + if block[0] == ?" + esc_skip = ( "\t\n" unless valx =~ /^[ \t]/ ) || "" + valx = fold( YAML::escape( valx, esc_skip ) + "\"" ).chomp + self << '"' + indent_text( valx, indt, false ) + else + if block[0] == ?> + valx = fold( valx ) + end + #p [block, indt] + self << block + indent_text( valx, indt ) + end + end + + # + # Emit a simple, unqouted string + # + def simple( value ) + @seq_map = false + self << value.to_s + end + + # + # Emit double-quoted string + # + def double( value ) + "\"#{YAML.escape( value )}\"" + end + + # + # Emit single-quoted string + # + def single( value ) + "'#{value}'" + end + + # + # Write a text block with the current indent + # + def indent_text( text, mod, first_line = true ) + return "" if text.to_s.empty? + spacing = indent( mod ) + text = text.gsub( /\A([^\n])/, "#{ spacing }\\1" ) if first_line + return text.gsub( /\n^([^\n])/, "\n#{spacing}\\1" ) + end + + # + # Write a current indent + # + def indent( mod = nil ) + #p [ self.id, level, mod, :INDENT ] + if level <= 0 + mod ||= 0 + else + mod ||= options(:Indent) + mod += ( level - 1 ) * options(:Indent) + end + return " " * mod + end + + # + # Add indent to the buffer + # + def indent! + self << indent + end + + # + # Folding paragraphs within a column + # + def fold( value ) + value.gsub( /(^[ \t]+.*$)|(\S.{0,#{options(:BestWidth) - 1}})(?:[ \t]+|(\n+(?=[ \t]|\Z))|$)/ ) do |s| + $1 || $2 + ( $3 || "\n" ) + end + end + + # + # Quick mapping + # + def map( type, &e ) + val = Mapping.new + e.call( val ) + self << "#{type} " if type.length.nonzero? + + # + # Empty hashes + # + if val.length.zero? + self << "{}" + @seq_map = false + else + # FIXME + # if @buffer.length == 1 and options(:UseHeader) == false and type.length.zero? + # @headless = 1 + # end + + defkey = @options.delete( :DefaultKey ) + if defkey + seq_map_shortcut + self << "= : " + defkey.to_yaml( :Emitter => self ) + end + + # + # Emit the key and value + # + val.each { |v| + seq_map_shortcut + if v[0].is_complex_yaml? + self << "? " + end + v[0].to_yaml( :Emitter => self ) + if v[0].is_complex_yaml? + self << "\n" + indent! + end + self << ": " + v[1].to_yaml( :Emitter => self ) + } + end + end + + def seq_map_shortcut + # FIXME: seq_map needs to work with the new anchoring system + # if @seq_map + # @anchor_extras[@buffer.length - 1] = "\n" + indent + # @seq_map = false + # else + self << "\n" + indent! + # end + end + + # + # Quick sequence + # + def seq( type, &e ) + @seq_map = false + val = Sequence.new + e.call( val ) + self << "#{type} " if type.length.nonzero? + + # + # Empty arrays + # + if val.length.zero? + self << "[]" + else + # FIXME + # if @buffer.length == 1 and options(:UseHeader) == false and type.length.zero? + # @headless = 1 + # end + + # + # Emit the key and value + # + val.each { |v| + self << "\n" + indent! + self << "- " + @seq_map = true if v.class == Hash + v.to_yaml( :Emitter => self ) + } + end + end + + end + + # + # Emitter helper classes + # + class Mapping < Array + def add( k, v ) + push [k, v] + end + end + + class Sequence < Array + def add( v ) + push v + end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/basenode.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/basenode.rb new file mode 100644 index 0000000000..d24f6172e9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/basenode.rb @@ -0,0 +1,216 @@ +# +# YAML::BaseNode class +# +require 'yaml/ypath' + +module YAML + + # + # YAML Generic Model container + # + module BaseNode + + # + # Search for YPath entry and return + # qualified nodes. + # + def select( ypath_str ) + matches = match_path( ypath_str ) + + # + # Create a new generic view of the elements selected + # + if matches + result = [] + matches.each { |m| + result.push m.last + } + YAML.transfer( 'seq', result ) + end + end + + # + # Search for YPath entry and return + # transformed nodes. + # + def select!( ypath_str ) + matches = match_path( ypath_str ) + + # + # Create a new generic view of the elements selected + # + if matches + result = [] + matches.each { |m| + result.push m.last.transform + } + result + end + end + + # + # Search for YPath entry and return a list of + # qualified paths. + # + def search( ypath_str ) + matches = match_path( ypath_str ) + + if matches + matches.collect { |m| + path = [] + m.each_index { |i| + path.push m[i] if ( i % 2 ).zero? + } + "/" + path.compact.join( "/" ) + } + end + end + + def at( seg ) + if Hash === @value + self[seg] + elsif Array === @value and seg =~ /\A\d+\Z/ and @value[seg.to_i] + @value[seg.to_i] + end + end + + # + # YPath search returning a complete depth array + # + def match_path( ypath_str ) + depth = 0 + matches = [] + YPath.each_path( ypath_str ) do |ypath| + seg = match_segment( ypath, 0 ) + matches += seg if seg + end + matches.uniq + end + + # + # Search a node for a single YPath segment + # + def match_segment( ypath, depth ) + deep_nodes = [] + seg = ypath.segments[ depth ] + if seg == "/" + unless String === @value + idx = -1 + @value.collect { |v| + idx += 1 + if Hash === @value + match_init = [v[0].transform, v[1]] + match_deep = v[1].match_segment( ypath, depth ) + else + match_init = [idx, v] + match_deep = v.match_segment( ypath, depth ) + end + if match_deep + match_deep.each { |m| + deep_nodes.push( match_init + m ) + } + end + } + end + depth += 1 + seg = ypath.segments[ depth ] + end + match_nodes = + case seg + when "." + [[nil, self]] + when ".." + [["..", nil]] + when "*" + if @value.is_a? Enumerable + idx = -1 + @value.collect { |h| + idx += 1 + if Hash === @value + [h[0].transform, h[1]] + else + [idx, h] + end + } + end + else + if seg =~ /^"(.*)"$/ + seg = $1 + elsif seg =~ /^'(.*)'$/ + seg = $1 + end + if ( v = at( seg ) ) + [[ seg, v ]] + end + end + return deep_nodes unless match_nodes + pred = ypath.predicates[ depth ] + if pred + case pred + when /^\.=/ + pred = $' # ' + match_nodes.reject! { |n| + n.last.value != pred + } + else + match_nodes.reject! { |n| + n.last.at( pred ).nil? + } + end + end + return match_nodes + deep_nodes unless ypath.segments.length > depth + 1 + + #puts "DEPTH: #{depth + 1}" + deep_nodes = [] + match_nodes.each { |n| + if n[1].is_a? BaseNode + match_deep = n[1].match_segment( ypath, depth + 1 ) + if match_deep + match_deep.each { |m| + deep_nodes.push( n + m ) + } + end + else + deep_nodes = [] + end + } + deep_nodes = nil if deep_nodes.length == 0 + deep_nodes + end + + # + # We want the node to act like as Hash + # if it is. + # + def []( *key ) + if Hash === @value + v = @value.detect { |k,v| k.transform == key.first } + v[1] if v + elsif Array === @value + @value.[]( *key ) + end + end + + def children + if Hash === @value + @value.values.collect { |c| c[1] } + elsif Array === @value + @value + end + end + + def children_with_index + if Hash === @value + @value.keys.collect { |i| [self[i], i] } + elsif Array === @value + i = -1; @value.collect { |v| i += 1; [v, i] } + end + end + + def emit + transform.to_yaml + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/constants.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/constants.rb new file mode 100644 index 0000000000..fb833d3077 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/constants.rb @@ -0,0 +1,45 @@ +# +# Constants used throughout the library +# +module YAML + + # + # Constants + # + VERSION = '0.60' + SUPPORTED_YAML_VERSIONS = ['1.0'] + + # + # Parser tokens + # + WORD_CHAR = 'A-Za-z0-9' + PRINTABLE_CHAR = '-_A-Za-z0-9!?/()$\'". ' + NOT_PLAIN_CHAR = '\x7f\x0-\x1f\x80-\x9f' + ESCAPE_CHAR = '[\\x00-\\x09\\x0b-\\x1f]' + INDICATOR_CHAR = '*&!|\\\\^@%{}[]=' + SPACE_INDICATORS = '-#:,?' + RESTRICTED_INDICATORS = '#:,}]' + DNS_COMP_RE = "\\w(?:[-\\w]*\\w)?" + DNS_NAME_RE = "(?:(?:#{DNS_COMP_RE}\\.)+#{DNS_COMP_RE}|#{DNS_COMP_RE})" + ESCAPES = %w{\x00 \x01 \x02 \x03 \x04 \x05 \x06 \a + \x08 \t \n \v \f \r \x0e \x0f + \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 + \x18 \x19 \x1a \e \x1c \x1d \x1e \x1f + } + UNESCAPES = { + 'a' => "\x07", 'b' => "\x08", 't' => "\x09", + 'n' => "\x0a", 'v' => "\x0b", 'f' => "\x0c", + 'r' => "\x0d", 'e' => "\x1b", '\\' => '\\', + } + + # + # Default settings + # + DEFAULTS = { + :Indent => 2, :UseHeader => false, :UseVersion => false, :Version => '1.0', + :SortKeys => false, :AnchorFormat => 'id%03d', :ExplicitTypes => false, + :WidthType => 'absolute', :BestWidth => 80, + :UseBlock => false, :UseFold => false, :Encoding => :None + } + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/dbm.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/dbm.rb new file mode 100644 index 0000000000..87d6009250 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/dbm.rb @@ -0,0 +1,111 @@ +require 'yaml' +require 'dbm' +# +# YAML + DBM = YDBM +# - Same interface as DBM class +# +module YAML + +class DBM < ::DBM + VERSION = "0.1" + def []( key ) + fetch( key ) + end + def []=( key, val ) + store( key, val ) + end + def fetch( keystr, ifnone = nil ) + begin + val = super( keystr ) + return YAML::load( val ) if String === val + rescue IndexError + end + if block_given? + yield keystr + else + ifnone + end + end + def index( keystr ) + super( keystr.to_yaml ) + end + def values_at( *keys ) + keys.collect { |k| fetch( k ) } + end + def delete( key ) + v = super( key ) + if String === v + v = YAML::load( v ) + end + v + end + def delete_if + del_keys = keys.dup + del_keys.delete_if { |k| yield( k, fetch( k ) ) == false } + del_keys.each { |k| delete( k ) } + self + end + def reject + hsh = self.to_hash + hsh.reject { |k,v| yield k, v } + end + def each_pair + keys.each { |k| yield k, fetch( k ) } + self + end + def each_value + super { |v| yield YAML::load( v ) } + self + end + def values + super.collect { |v| YAML::load( v ) } + end + def has_value?( val ) + each_value { |v| return true if v == val } + return false + end + def invert + h = {} + keys.each { |k| h[ self.fetch( k ) ] = k } + h + end + def replace( hsh ) + clear + update( hsh ) + end + def shift + a = super + a[1] = YAML::load( a[1] ) if a + a + end + def select( *keys ) + if block_given? + self.keys.collect { |k| v = self[k]; [k, v] if yield k, v }.compact + else + values_at( *keys ) + end + end + def store( key, val ) + super( key, val.to_yaml ) + val + end + def update( hsh ) + hsh.keys.each do |k| + self.store( k, hsh.fetch( k ) ) + end + self + end + def to_a + a = [] + keys.each { |k| a.push [ k, self.fetch( k ) ] } + a + end + def to_hash + h = {} + keys.each { |k| h[ k ] = self.fetch( k ) } + h + end + alias :each :each_pair +end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/encoding.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/encoding.rb new file mode 100644 index 0000000000..37f5cfda64 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/encoding.rb @@ -0,0 +1,33 @@ +# +# Handle Unicode-to-Internal conversion +# + +module YAML + + # + # Escape the string, condensing common escapes + # + def YAML.escape( value, skip = "" ) + value.gsub( /\\/, "\\\\\\" ). + gsub( /"/, "\\\"" ). + gsub( /([\x00-\x1f])/ ) do |x| + skip[x] || ESCAPES[ x.unpack("C")[0] ] + end + end + + # + # Unescape the condenses escapes + # + def YAML.unescape( value ) + value.gsub( /\\(?:([nevfbart\\])|0?x([0-9a-fA-F]{2})|u([0-9a-fA-F]{4}))/ ) { |x| + if $3 + ["#$3".hex ].pack('U*') + elsif $2 + [$2].pack( "H2" ) + else + UNESCAPES[$1] + end + } + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/error.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/error.rb new file mode 100644 index 0000000000..15865a9aa9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/error.rb @@ -0,0 +1,34 @@ +# +# Error messages and exception class +# + +module YAML + + # + # Error messages + # + + ERROR_NO_HEADER_NODE = "With UseHeader=false, the node Array or Hash must have elements" + ERROR_NEED_HEADER = "With UseHeader=false, the node must be an Array or Hash" + ERROR_BAD_EXPLICIT = "Unsupported explicit transfer: '%s'" + ERROR_MANY_EXPLICIT = "More than one explicit transfer" + ERROR_MANY_IMPLICIT = "More than one implicit request" + ERROR_NO_ANCHOR = "No anchor for alias '%s'" + ERROR_BAD_ANCHOR = "Invalid anchor: %s" + ERROR_MANY_ANCHOR = "More than one anchor" + ERROR_ANCHOR_ALIAS = "Can't define both an anchor and an alias" + ERROR_BAD_ALIAS = "Invalid alias: %s" + ERROR_MANY_ALIAS = "More than one alias" + ERROR_ZERO_INDENT = "Can't use zero as an indentation width" + ERROR_UNSUPPORTED_VERSION = "This release of YAML.rb does not support YAML version %s" + ERROR_UNSUPPORTED_ENCODING = "Attempt to use unsupported encoding: %s" + + # + # YAML Error classes + # + + class Error < StandardError; end + class ParseError < Error; end + class TypeError < StandardError; end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/loader.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/loader.rb new file mode 100644 index 0000000000..eb0709e103 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/loader.rb @@ -0,0 +1,14 @@ +# +# YAML::Loader class +# .. type handling .. +# +module YAML + class Loader + TRANSFER_DOMAINS = { + 'yaml.org,2002' => {}, + 'ruby.yaml.org,2002' => {} + } + PRIVATE_TYPES = {} + IMPLICIT_TYPES = [ 'null', 'bool', 'time', 'int', 'float' ] + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/rubytypes.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/rubytypes.rb new file mode 100644 index 0000000000..eebf027135 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/rubytypes.rb @@ -0,0 +1,408 @@ +# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4 +require 'date' + +class Class + def to_yaml( opts = {} ) + raise TypeError, "can't dump anonymous class %s" % self.class + end +end + +class Object + yaml_as "tag:ruby.yaml.org,2002:object" + def to_yaml_style; end + def to_yaml_properties; instance_variables.sort; end + def to_yaml( opts = {} ) + YAML::quick_emit( object_id, opts ) do |out| + out.map( taguri, to_yaml_style ) do |map| + to_yaml_properties.each do |m| + map.add( m[1..-1], instance_variable_get( m ) ) + end + end + end + end +end + +class Hash + yaml_as "tag:ruby.yaml.org,2002:hash" + yaml_as "tag:yaml.org,2002:map" + def yaml_initialize( tag, val ) + if Array === val + update Hash.[]( *val ) # Convert the map to a sequence + elsif Hash === val + update val + else + raise YAML::TypeError, "Invalid map explicitly tagged #{ tag }: " + val.inspect + end + end + def to_yaml( opts = {} ) + YAML::quick_emit( object_id, opts ) do |out| + out.map( taguri, to_yaml_style ) do |map| + each do |k, v| + map.add( k, v ) + end + end + end + end +end + +class Struct + yaml_as "tag:ruby.yaml.org,2002:struct" + def self.yaml_tag_class_name; self.name.gsub( "Struct::", "" ); end + def self.yaml_tag_read_class( name ); "Struct::#{ name }"; end + def self.yaml_new( klass, tag, val ) + if Hash === val + struct_type = nil + + # + # Use existing Struct if it exists + # + props = {} + val.delete_if { |k,v| props[k] = v if k =~ /^@/ } + begin + struct_name, struct_type = YAML.read_type_class( tag, Struct ) + rescue NameError + end + if not struct_type + struct_def = [ tag.split( ':', 4 ).last ] + struct_type = Struct.new( *struct_def.concat( val.keys.collect { |k| k.intern } ) ) + end + + # + # Set the Struct properties + # + st = YAML::object_maker( struct_type, {} ) + st.members.each do |m| + st.send( "#{m}=", val[m] ) + end + props.each do |k,v| + st.instance_variable_set(k, v) + end + st + else + raise YAML::TypeError, "Invalid Ruby Struct: " + val.inspect + end + end + def to_yaml( opts = {} ) + YAML::quick_emit( object_id, opts ) do |out| + # + # Basic struct is passed as a YAML map + # + out.map( taguri, to_yaml_style ) do |map| + self.members.each do |m| + map.add( m, self[m] ) + end + self.to_yaml_properties.each do |m| + map.add( m, instance_variable_get( m ) ) + end + end + end + end +end + +class Array + yaml_as "tag:ruby.yaml.org,2002:array" + yaml_as "tag:yaml.org,2002:seq" + def yaml_initialize( tag, val ); concat( val.to_a ); end + def to_yaml( opts = {} ) + YAML::quick_emit( object_id, opts ) do |out| + out.seq( taguri, to_yaml_style ) do |seq| + each do |x| + seq.add( x ) + end + end + end + end +end + +class Exception + yaml_as "tag:ruby.yaml.org,2002:exception" + def Exception.yaml_new( klass, tag, val ) + o = YAML.object_maker( klass, { 'mesg' => val.delete( 'message' ) } ) + val.each_pair do |k,v| + o.instance_variable_set("@#{k}", v) + end + o + end + def to_yaml( opts = {} ) + YAML::quick_emit( object_id, opts ) do |out| + out.map( taguri, to_yaml_style ) do |map| + map.add( 'message', message ) + to_yaml_properties.each do |m| + map.add( m[1..-1], instance_variable_get( m ) ) + end + end + end + end +end + +class String + yaml_as "tag:ruby.yaml.org,2002:string" + yaml_as "tag:yaml.org,2002:binary" + yaml_as "tag:yaml.org,2002:str" + def is_complex_yaml? + to_yaml_style or not to_yaml_properties.empty? or self =~ /\n.+/ + end + def is_binary_data? + ( self.count( "^ -~", "^\r\n" ) / self.size > 0.3 || self.count( "\x00" ) > 0 ) unless empty? + end + def String.yaml_new( klass, tag, val ) + val = val.unpack("m")[0] if tag == "tag:yaml.org,2002:binary" + val = { 'str' => val } if String === val + if Hash === val + s = klass.allocate + # Thank you, NaHi + String.instance_method(:initialize). + bind(s). + call( val.delete( 'str' ) ) + val.each { |k,v| s.instance_variable_set( k, v ) } + s + else + raise YAML::TypeError, "Invalid String: " + val.inspect + end + end + def to_yaml( opts = {} ) + YAML::quick_emit( is_complex_yaml? ? object_id : nil, opts ) do |out| + if is_binary_data? + out.scalar( "tag:yaml.org,2002:binary", [self].pack("m"), :literal ) + elsif to_yaml_properties.empty? + out.scalar( taguri, self, self =~ /^:/ ? :quote2 : to_yaml_style ) + else + out.map( taguri, to_yaml_style ) do |map| + map.add( 'str', "#{self}" ) + to_yaml_properties.each do |m| + map.add( m, instance_variable_get( m ) ) + end + end + end + end + end +end + +class Symbol + yaml_as "tag:ruby.yaml.org,2002:symbol" + yaml_as "tag:ruby.yaml.org,2002:sym" + def Symbol.yaml_new( klass, tag, val ) + if String === val + val = YAML::load( val ) if val =~ /\A(["']).*\1\z/ + val.intern + else + raise YAML::TypeError, "Invalid Symbol: " + val.inspect + end + end + def to_yaml( opts = {} ) + YAML::quick_emit( nil, opts ) do |out| + out.scalar( "tag:yaml.org,2002:str", self.inspect, :plain ) + end + end +end + +class Range + yaml_as "tag:ruby.yaml.org,2002:range" + def Range.yaml_new( klass, tag, val ) + inr = %r'(\w+|[+-]?\d+(?:\.\d+)?(?:e[+-]\d+)?|"(?:[^\\"]|\\.)*")' + opts = {} + if String === val and val =~ /^#{inr}(\.{2,3})#{inr}$/o + r1, rdots, r2 = $1, $2, $3 + opts = { + 'begin' => YAML.load( "--- #{r1}" ), + 'end' => YAML.load( "--- #{r2}" ), + 'excl' => rdots.length == 3 + } + val = {} + elsif Hash === val + opts['begin'] = val.delete('begin') + opts['end'] = val.delete('end') + opts['excl'] = val.delete('excl') + end + if Hash === opts + r = YAML::object_maker( klass, {} ) + # Thank you, NaHi + Range.instance_method(:initialize). + bind(r). + call( opts['begin'], opts['end'], opts['excl'] ) + val.each { |k,v| r.instance_variable_set( k, v ) } + r + else + raise YAML::TypeError, "Invalid Range: " + val.inspect + end + end + def to_yaml( opts = {} ) + YAML::quick_emit( object_id, opts ) do |out| + # if self.begin.is_complex_yaml? or self.begin.respond_to? :to_str or + # self.end.is_complex_yaml? or self.end.respond_to? :to_str or + # not to_yaml_properties.empty? + out.map( taguri, to_yaml_style ) do |map| + map.add( 'begin', self.begin ) + map.add( 'end', self.end ) + map.add( 'excl', self.exclude_end? ) + to_yaml_properties.each do |m| + map.add( m, instance_variable_get( m ) ) + end + end + # else + # out.scalar( taguri ) do |sc| + # sc.embed( self.begin ) + # sc.concat( self.exclude_end? ? "..." : ".." ) + # sc.embed( self.end ) + # end + # end + end + end +end + +class Regexp + yaml_as "tag:ruby.yaml.org,2002:regexp" + def Regexp.yaml_new( klass, tag, val ) + if String === val and val =~ /^\/(.*)\/([mix]*)$/ + val = { 'regexp' => $1, 'mods' => $2 } + end + if Hash === val + mods = nil + unless val['mods'].to_s.empty? + mods = 0x00 + mods |= Regexp::EXTENDED if val['mods'].include?( 'x' ) + mods |= Regexp::IGNORECASE if val['mods'].include?( 'i' ) + mods |= Regexp::MULTILINE if val['mods'].include?( 'm' ) + end + val.delete( 'mods' ) + r = YAML::object_maker( klass, {} ) + Regexp.instance_method(:initialize). + bind(r). + call( val.delete( 'regexp' ), mods ) + val.each { |k,v| r.instance_variable_set( k, v ) } + r + else + raise YAML::TypeError, "Invalid Regular expression: " + val.inspect + end + end + def to_yaml( opts = {} ) + YAML::quick_emit( nil, opts ) do |out| + if to_yaml_properties.empty? + out.scalar( taguri, self.inspect, :plain ) + else + out.map( taguri, to_yaml_style ) do |map| + src = self.inspect + if src =~ /\A\/(.*)\/([a-z]*)\Z/ + map.add( 'regexp', $1 ) + map.add( 'mods', $2 ) + else + raise YAML::TypeError, "Invalid Regular expression: " + src + end + to_yaml_properties.each do |m| + map.add( m, instance_variable_get( m ) ) + end + end + end + end + end +end + +class Time + yaml_as "tag:ruby.yaml.org,2002:time" + yaml_as "tag:yaml.org,2002:timestamp" + def Time.yaml_new( klass, tag, val ) + if Hash === val + t = val.delete( 'at' ) + val.each { |k,v| t.instance_variable_set( k, v ) } + t + else + raise YAML::TypeError, "Invalid Time: " + val.inspect + end + end + def to_yaml( opts = {} ) + YAML::quick_emit( object_id, opts ) do |out| + tz = "Z" + # from the tidy Tobias Peters Thanks! + unless self.utc? + utc_same_instant = self.dup.utc + utc_same_writing = Time.utc(year,month,day,hour,min,sec,usec) + difference_to_utc = utc_same_writing - utc_same_instant + if (difference_to_utc < 0) + difference_sign = '-' + absolute_difference = -difference_to_utc + else + difference_sign = '+' + absolute_difference = difference_to_utc + end + difference_minutes = (absolute_difference/60).round + tz = "%s%02d:%02d" % [ difference_sign, difference_minutes / 60, difference_minutes % 60] + end + standard = self.strftime( "%Y-%m-%d %H:%M:%S" ) + standard += ".%06d" % [usec] if usec.nonzero? + standard += " %s" % [tz] + if to_yaml_properties.empty? + out.scalar( taguri, standard, :plain ) + else + out.map( taguri, to_yaml_style ) do |map| + map.add( 'at', standard ) + to_yaml_properties.each do |m| + map.add( m, instance_variable_get( m ) ) + end + end + end + end + end +end + +class Date + yaml_as "tag:yaml.org,2002:timestamp#ymd" + def to_yaml( opts = {} ) + YAML::quick_emit( object_id, opts ) do |out| + out.scalar( "tag:yaml.org,2002:timestamp", self.to_s, :plain ) + end + end +end + +class Integer + yaml_as "tag:yaml.org,2002:int" + def to_yaml( opts = {} ) + YAML::quick_emit( nil, opts ) do |out| + out.scalar( "tag:yaml.org,2002:int", self.to_s, :plain ) + end + end +end + +class Float + yaml_as "tag:yaml.org,2002:float" + def to_yaml( opts = {} ) + YAML::quick_emit( nil, opts ) do |out| + str = self.to_s + if str == "Infinity" + str = ".Inf" + elsif str == "-Infinity" + str = "-.Inf" + elsif str == "NaN" + str = ".NaN" + end + out.scalar( "tag:yaml.org,2002:float", str, :plain ) + end + end +end + +class TrueClass + yaml_as "tag:yaml.org,2002:bool#yes" + def to_yaml( opts = {} ) + YAML::quick_emit( nil, opts ) do |out| + out.scalar( taguri, "true", :plain ) + end + end +end + +class FalseClass + yaml_as "tag:yaml.org,2002:bool#no" + def to_yaml( opts = {} ) + YAML::quick_emit( nil, opts ) do |out| + out.scalar( taguri, "false", :plain ) + end + end +end + +class NilClass + yaml_as "tag:yaml.org,2002:null" + def to_yaml( opts = {} ) + YAML::quick_emit( nil, opts ) do |out| + out.scalar( taguri, "", :plain ) + end + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/store.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/store.rb new file mode 100644 index 0000000000..2ffa9554b9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/store.rb @@ -0,0 +1,29 @@ +# +# YAML::Store +# +require 'yaml' +require 'pstore' + +class YAML::Store < PStore + def initialize( *o ) + @opt = YAML::DEFAULTS.dup + if String === o.first + super(o.shift) + end + if o.last.is_a? Hash + @opt.update(o.pop) + end + end + + def dump(table) + @table.to_yaml(@opt) + end + + def load(content) + YAML::load(content) + end + + def load_file(file) + YAML::load(file) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/stream.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/stream.rb new file mode 100644 index 0000000000..060fbc4200 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/stream.rb @@ -0,0 +1,40 @@ +module YAML + + # + # YAML::Stream -- for emitting many documents + # + class Stream + + attr_accessor :documents, :options + + def initialize( opts = {} ) + @options = opts + @documents = [] + end + + def []( i ) + @documents[ i ] + end + + def add( doc ) + @documents << doc + end + + def edit( doc_num, doc ) + @documents[ doc_num ] = doc + end + + def emit( io = nil ) + # opts = @options.dup + # opts[:UseHeader] = true if @documents.length > 1 + out = YAML.emitter + out.reset( io || io2 = StringIO.new ) + @documents.each { |v| + v.to_yaml( out ) + } + io || ( io2.rewind; io2.read ) + end + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/stringio.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/stringio.rb new file mode 100644 index 0000000000..8ad949fa2b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/stringio.rb @@ -0,0 +1,83 @@ +# +# Limited StringIO if no core lib is available +# +begin +require 'stringio' +rescue LoadError + # StringIO based on code by MoonWolf + class StringIO + def initialize(string="") + @string=string + @pos=0 + @eof=(string.size==0) + end + def pos + @pos + end + def eof + @eof + end + alias eof? eof + def readline(rs=$/) + if @eof + raise EOFError + else + if p = @string[@pos..-1]=~rs + line = @string[@pos,p+1] + else + line = @string[@pos..-1] + end + @pos+=line.size + @eof =true if @pos==@string.size + $_ = line + end + end + def rewind + seek(0,0) + end + def seek(offset,whence) + case whence + when 0 + @pos=offset + when 1 + @pos+=offset + when 2 + @pos=@string.size+offset + end + @eof=(@pos>=@string.size) + 0 + end + end + + # + # Class method for creating streams + # + def YAML.make_stream( io ) + if String === io + io = StringIO.new( io ) + elsif not IO === io + raise YAML::Error, "YAML stream must be an IO or String object." + end + if YAML::unicode + def io.readline + YAML.utf_to_internal( readline( @ln_sep ), @utf_encoding ) + end + def io.check_unicode + @utf_encoding = YAML.sniff_encoding( read( 4 ) ) + @ln_sep = YAML.enc_separator( @utf_encoding ) + seek( -4, IO::SEEK_CUR ) + end + def io.utf_encoding + @utf_encoding + end + io.check_unicode + else + def io.utf_encoding + :None + end + end + io + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/syck.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/syck.rb new file mode 100644 index 0000000000..faf57e8036 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/syck.rb @@ -0,0 +1,19 @@ +# +# YAML::Syck module +# .. glues syck and yaml.rb together .. +# +require 'syck' +require 'yaml/basenode' + +module YAML + module Syck + + # + # Mixin BaseNode functionality + # + class Node + include YAML::BaseNode + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/tag.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/tag.rb new file mode 100644 index 0000000000..a91f2bd8c4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/tag.rb @@ -0,0 +1,91 @@ +# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4 +# $Id: tag.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# +# = yaml/tag.rb: methods for associating a taguri to a class. +# +# Author:: why the lucky stiff +# +module YAML + # A dictionary of taguris which map to + # Ruby classes. + @@tagged_classes = {} + + # + # Associates a taguri _tag_ with a Ruby class _cls_. The taguri is used to give types + # to classes when loading YAML. Taguris are of the form: + # + # tag:authorityName,date:specific + # + # The +authorityName+ is a domain name or email address. The +date+ is the date the type + # was issued in YYYY or YYYY-MM or YYYY-MM-DD format. The +specific+ is a name for + # the type being added. + # + # For example, built-in YAML types have 'yaml.org' as the +authorityName+ and '2002' as the + # +date+. The +specific+ is simply the name of the type: + # + # tag:yaml.org,2002:int + # tag:yaml.org,2002:float + # tag:yaml.org,2002:timestamp + # + # The domain must be owned by you on the +date+ declared. If you don't own any domains on the + # date you declare the type, you can simply use an e-mail address. + # + # tag:why@ruby-lang.org,2004:notes/personal + # + def YAML.tag_class( tag, cls ) + if @@tagged_classes.has_key? tag + warn "class #{ @@tagged_classes[tag] } held ownership of the #{ tag } tag" + end + @@tagged_classes[tag] = cls + end + + # Returns the complete dictionary of taguris, paired with classes. The key for + # the dictionary is the full taguri. The value for each key is the class constant + # associated to that taguri. + # + # YAML.tagged_classes["tag:yaml.org,2002:int"] => Integer + # + def YAML.tagged_classes + @@tagged_classes + end +end + +class Module + # :stopdoc: + + # Adds a taguri _tag_ to a class, used when dumping or loading the class + # in YAML. See YAML::tag_class for detailed information on typing and + # taguris. + def yaml_as( tag, sc = true ) + verbose, $VERBOSE = $VERBOSE, nil + class_eval <<-"end;", __FILE__, __LINE__+1 + attr_writer :taguri + def taguri + if respond_to? :to_yaml_type + YAML::tagurize( to_yaml_type[1..-1] ) + else + return @taguri if defined?(@taguri) and @taguri + tag = #{ tag.dump } + if self.class.yaml_tag_subclasses? and self.class != YAML::tagged_classes[tag] + tag = "\#{ tag }:\#{ self.class.yaml_tag_class_name }" + end + tag + end + end + def self.yaml_tag_subclasses?; #{ sc ? 'true' : 'false' }; end + end; + YAML::tag_class tag, self + ensure + $VERBOSE = verbose + end + # Transforms the subclass name into a name suitable for display + # in a subclassed tag. + def yaml_tag_class_name + self.name + end + # Transforms the subclass name found in the tag into a Ruby + # constant name. + def yaml_tag_read_class( name ) + name + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/types.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/types.rb new file mode 100644 index 0000000000..05113f216d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/types.rb @@ -0,0 +1,194 @@ +# -*- mode: ruby; ruby-indent-level: 4 -*- vim: sw=4 +# +# Classes required by the full core typeset +# + +module YAML + + # + # Default private type + # + class PrivateType + def self.tag_subclasses?; false; end + attr_accessor :type_id, :value + verbose, $VERBOSE = $VERBOSE, nil + def initialize( type, val ) + @type_id = type; @value = val + @value.taguri = "x-private:#{ @type_id }" + end + def to_yaml( opts = {} ) + @value.to_yaml( opts ) + end + ensure + $VERBOSE = verbose + end + + # + # Default domain type + # + class DomainType + def self.tag_subclasses?; false; end + attr_accessor :domain, :type_id, :value + verbose, $VERBOSE = $VERBOSE, nil + def initialize( domain, type, val ) + @domain = domain; @type_id = type; @value = val + @value.taguri = "tag:#{ @domain }:#{ @type_id }" + end + def to_yaml( opts = {} ) + @value.to_yaml( opts ) + end + ensure + $VERBOSE = verbose + end + + # + # Unresolved objects + # + class Object + def self.tag_subclasses?; false; end + def to_yaml( opts = {} ) + YAML::quick_emit( object_id, opts ) do |out| + out.map( "tag:ruby.yaml.org,2002:object:#{ @class }", to_yaml_style ) do |map| + @ivars.each do |k,v| + map.add( k, v ) + end + end + end + end + end + + # + # YAML Hash class to support comments and defaults + # + class SpecialHash < ::Hash + attr_accessor :default + def inspect + self.default.to_s + end + def to_s + self.default.to_s + end + def update( h ) + if YAML::SpecialHash === h + @default = h.default if h.default + end + super( h ) + end + def to_yaml( opts = {} ) + opts[:DefaultKey] = self.default + super( opts ) + end + end + + # + # Builtin collection: !omap + # + class Omap < ::Array + yaml_as "tag:yaml.org,2002:omap" + def yaml_initialize( tag, val ) + if Array === val + val.each do |v| + if Hash === v + concat( v.to_a ) # Convert the map to a sequence + else + raise YAML::Error, "Invalid !omap entry: " + val.inspect + end + end + else + raise YAML::Error, "Invalid !omap: " + val.inspect + end + self + end + def self.[]( *vals ) + o = Omap.new + 0.step( vals.length - 1, 2 ) do |i| + o[vals[i]] = vals[i+1] + end + o + end + def []( k ) + self.assoc( k ).to_a[1] + end + def []=( k, *rest ) + val, set = rest.reverse + if ( tmp = self.assoc( k ) ) and not set + tmp[1] = val + else + self << [ k, val ] + end + val + end + def has_key?( k ) + self.assoc( k ) ? true : false + end + def is_complex_yaml? + true + end + def to_yaml( opts = {} ) + YAML::quick_emit( self.object_id, opts ) do |out| + out.seq( taguri, to_yaml_style ) do |seq| + self.each do |v| + seq.add( Hash[ *v ] ) + end + end + end + end + end + + # + # Builtin collection: !pairs + # + class Pairs < ::Array + yaml_as "tag:yaml.org,2002:pairs" + def yaml_initialize( tag, val ) + if Array === val + val.each do |v| + if Hash === v + concat( v.to_a ) # Convert the map to a sequence + else + raise YAML::Error, "Invalid !pairs entry: " + val.inspect + end + end + else + raise YAML::Error, "Invalid !pairs: " + val.inspect + end + self + end + def self.[]( *vals ) + p = Pairs.new + 0.step( vals.length - 1, 2 ) { |i| + p[vals[i]] = vals[i+1] + } + p + end + def []( k ) + self.assoc( k ).to_a + end + def []=( k, val ) + self << [ k, val ] + val + end + def has_key?( k ) + self.assoc( k ) ? true : false + end + def is_complex_yaml? + true + end + def to_yaml( opts = {} ) + YAML::quick_emit( self.object_id, opts ) do |out| + out.seq( taguri, to_yaml_style ) do |seq| + self.each do |v| + seq.add( Hash[ *v ] ) + end + end + end + end + end + + # + # Builtin collection: !set + # + class Set < ::Hash + yaml_as "tag:yaml.org,2002:set" + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/yamlnode.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/yamlnode.rb new file mode 100644 index 0000000000..e36a18e694 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/yamlnode.rb @@ -0,0 +1,54 @@ +# +# YAML::YamlNode class +# +require 'yaml/basenode' + +module YAML + + # + # YAML Generic Model container + # + class YamlNode + include BaseNode + attr_accessor :kind, :type_id, :value, :anchor + def initialize( t, v ) + @type_id = t + if Hash === v + @kind = 'map' + @value = {} + v.each { |k,v| + @value[ k.transform ] = [ k, v ] + } + elsif Array === v + @kind = 'seq' + @value = v + elsif String === v + @kind = 'scalar' + @value = v + end + end + + # + # Transform this node fully into a native type + # + def transform + t = nil + if @value.is_a? Hash + t = {} + @value.each { |k,v| + t[ k ] = v[1].transform + } + elsif @value.is_a? Array + t = [] + @value.each { |v| + t.push v.transform + } + else + t = @value + end + YAML.transfer_method( @type_id, t ) + end + + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/ypath.rb b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/ypath.rb new file mode 100644 index 0000000000..81348ca043 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/1.8/yaml/ypath.rb @@ -0,0 +1,52 @@ +# +# YAML::YPath +# + +module YAML + + class YPath + attr_accessor :segments, :predicates, :flags + def initialize( str ) + @segments = [] + @predicates = [] + @flags = nil + while str =~ /^\/?(\/|[^\/\[]+)(?:\[([^\]]+)\])?/ + @segments.push $1 + @predicates.push $2 + str = $' + end + unless str.to_s.empty? + @segments += str.split( "/" ) + end + if @segments.length == 0 + @segments.push "." + end + end + def YPath.each_path( str ) + # + # Find choices + # + paths = [] + str = "(#{ str })" + while str.sub!( /\(([^()]+)\)/, "\n#{ paths.length }\n" ) + paths.push $1.split( '|' ) + end + + # + # Construct all possible paths + # + all = [ str ] + ( paths.length - 1 ).downto( 0 ) do |i| + all = all.collect do |a| + paths[i].collect do |p| + a.gsub( /\n#{ i }\n/, p ) + end + end.flatten.uniq + end + all.collect do |path| + yield YPath.new( path ) + end + end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/gemconfigure.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/gemconfigure.rb new file mode 100644 index 0000000000..81155b8141 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/gemconfigure.rb @@ -0,0 +1,24 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +module Gem + + # Activate the gems specfied by the gem_pairs list. + # + # gem_pairs :: + # List of gem/version pairs. + # Eg. [['rake', '= 0.8.15'], ['RedCloth', '~> 3.0']] + # options :: + # options[:verbose] => print gems as they are required. + # + def self.configure(gem_pairs, options={}) + gem_pairs.each do |name, version| + require 'rubygems' + puts "Activating gem #{name} (version #{version})" if options[:verbose] + gem name, version + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rbconfig/datadir.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rbconfig/datadir.rb new file mode 100644 index 0000000000..5b8f07754a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rbconfig/datadir.rb @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + + +module Config + + # Only define datadir if it doesn't already exist. + unless Config.respond_to?(:datadir) + + # Return the path to the data directory associated with the given + # package name. Normally this is just + # "#{Config::CONFIG['datadir']}/#{package_name}", but may be + # modified by packages like RubyGems to handle versioned data + # directories. + def Config.datadir(package_name) + File.join(CONFIG['datadir'], package_name) + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems.rb new file mode 100644 index 0000000000..458a845b6c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems.rb @@ -0,0 +1,786 @@ +# -*- ruby -*- +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems/rubygems_version' +require 'rubygems/defaults' +require 'thread' + +module Gem + class LoadError < ::LoadError + attr_accessor :name, :version_requirement + end +end + +module Kernel + + ## + # Use Kernel#gem to activate a specific version of +gem_name+. + # + # +version_requirements+ is a list of version requirements that the + # specified gem must match, most commonly "= example.version.number". See + # Gem::Requirement for how to specify a version requirement. + # + # If you will be activating the latest version of a gem, there is no need to + # call Kernel#gem, Kernel#require will do the right thing for you. + # + # Kernel#gem returns true if the gem was activated, otherwise false. If the + # gem could not be found, didn't match the version requirements, or a + # different version was already activated, an exception will be raised. + # + # Kernel#gem should be called *before* any require statements (otherwise + # RubyGems may load a conflicting library version). + # + # In older RubyGems versions, the environment variable GEM_SKIP could be + # used to skip activation of specified gems, for example to test out changes + # that haven't been installed yet. Now RubyGems defers to -I and the + # RUBYLIB environment variable to skip activation of a gem. + # + # Example: + # + # GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb + + def gem(gem_name, *version_requirements) + skip_list = (ENV['GEM_SKIP'] || "").split(/:/) + raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name + Gem.activate(gem_name, *version_requirements) + end + +end + +## +# Main module to hold all RubyGem classes/modules. + +module Gem + + ConfigMap = {} unless defined?(ConfigMap) + require 'rbconfig' + RbConfig = Config unless defined? ::RbConfig + + ConfigMap.merge!( + :BASERUBY => RbConfig::CONFIG["BASERUBY"], + :EXEEXT => RbConfig::CONFIG["EXEEXT"], + :RUBY_INSTALL_NAME => RbConfig::CONFIG["RUBY_INSTALL_NAME"], + :RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"], + :arch => RbConfig::CONFIG["arch"], + :bindir => RbConfig::CONFIG["bindir"], + :datadir => RbConfig::CONFIG["datadir"], + :libdir => RbConfig::CONFIG["libdir"], + :ruby_install_name => RbConfig::CONFIG["ruby_install_name"], + :ruby_version => RbConfig::CONFIG["ruby_version"], + :sitedir => RbConfig::CONFIG["sitedir"], + :sitelibdir => RbConfig::CONFIG["sitelibdir"], + :vendordir => RbConfig::CONFIG["vendordir"] , + :vendorlibdir => RbConfig::CONFIG["vendorlibdir"] + ) + + DIRECTORIES = %w[cache doc gems specifications] unless defined?(DIRECTORIES) + + MUTEX = Mutex.new + + RubyGemsPackageVersion = RubyGemsVersion + + ## + # An Array of Regexps that match windows ruby platforms. + + WIN_PATTERNS = [ + /bccwin/i, + /cygwin/i, + /djgpp/i, + /mingw/i, + /mswin/i, + /wince/i, + ] + + @@source_index = nil + @@win_platform = nil + + @configuration = nil + @loaded_specs = {} + @platforms = [] + @ruby = nil + @sources = [] + + ## + # Activates an installed gem matching +gem+. The gem must satisfy + # +version_requirements+. + # + # Returns true if the gem is activated, false if it is already + # loaded, or an exception otherwise. + # + # Gem#activate adds the library paths in +gem+ to $LOAD_PATH. Before a Gem + # is activated its required Gems are activated. If the version information + # is omitted, the highest version Gem of the supplied name is loaded. If a + # Gem is not found that meets the version requirements or a required Gem is + # not found, a Gem::LoadError is raised. + # + # More information on version requirements can be found in the + # Gem::Requirement and Gem::Version documentation. + + def self.activate(gem, *version_requirements) + if version_requirements.empty? then + version_requirements = Gem::Requirement.default + end + + unless gem.respond_to?(:name) and + gem.respond_to?(:version_requirements) then + gem = Gem::Dependency.new(gem, version_requirements) + end + + matches = Gem.source_index.find_name(gem.name, gem.version_requirements) + report_activate_error(gem) if matches.empty? + + if @loaded_specs[gem.name] then + # This gem is already loaded. If the currently loaded gem is not in the + # list of candidate gems, then we have a version conflict. + existing_spec = @loaded_specs[gem.name] + + unless matches.any? { |spec| spec.version == existing_spec.version } then + raise Gem::Exception, + "can't activate #{gem}, already activated #{existing_spec.full_name}" + end + + return false + end + + # new load + spec = matches.last + return false if spec.loaded? + + spec.loaded = true + @loaded_specs[spec.name] = spec + + # Load dependent gems first + spec.runtime_dependencies.each do |dep_gem| + activate dep_gem + end + + # bin directory must come before library directories + spec.require_paths.unshift spec.bindir if spec.bindir + + require_paths = spec.require_paths.map do |path| + File.join spec.full_gem_path, path + end + + sitelibdir = ConfigMap[:sitelibdir] + + # gem directories must come after -I and ENV['RUBYLIB'] + insert_index = load_path_insert_index + + if insert_index then + # gem directories must come after -I and ENV['RUBYLIB'] + $LOAD_PATH.insert(insert_index, *require_paths) + else + # we are probably testing in core, -I and RUBYLIB don't apply + $LOAD_PATH.unshift(*require_paths) + end + + return true + end + + ## + # An Array of all possible load paths for all versions of all gems in the + # Gem installation. + + def self.all_load_paths + result = [] + + Gem.path.each do |gemdir| + each_load_path all_partials(gemdir) do |load_path| + result << load_path + end + end + + result + end + + ## + # Return all the partial paths in +gemdir+. + + def self.all_partials(gemdir) + Dir[File.join(gemdir, 'gems/*')] + end + + private_class_method :all_partials + + ## + # See if a given gem is available. + + def self.available?(gem, *requirements) + requirements = Gem::Requirement.default if requirements.empty? + + unless gem.respond_to?(:name) and + gem.respond_to?(:version_requirements) then + gem = Gem::Dependency.new gem, requirements + end + + !Gem.source_index.search(gem).empty? + end + + ## + # The mode needed to read a file as straight binary. + + def self.binary_mode + @binary_mode ||= RUBY_VERSION > '1.9' ? 'rb:ascii-8bit' : 'rb' + end + + ## + # The path where gem executables are to be installed. + + def self.bindir(install_dir=Gem.dir) + return File.join(install_dir, 'bin') unless + install_dir.to_s == Gem.default_dir + Gem.default_bindir + end + + ## + # Reset the +dir+ and +path+ values. The next time +dir+ or +path+ + # is requested, the values will be calculated from scratch. This is + # mainly used by the unit tests to provide test isolation. + + def self.clear_paths + @gem_home = nil + @gem_path = nil + @@source_index = nil + MUTEX.synchronize do + @searcher = nil + end + end + + ## + # The path to standard location of the user's .gemrc file. + + def self.config_file + File.join Gem.user_home, '.gemrc' + end + + ## + # The standard configuration object for gems. + + def self.configuration + return @configuration if @configuration + require 'rubygems/config_file' + @configuration = Gem::ConfigFile.new [] + end + + ## + # Use the given configuration object (which implements the ConfigFile + # protocol) as the standard configuration object. + + def self.configuration=(config) + @configuration = config + end + + ## + # The path the the data directory specified by the gem name. If the + # package is not available as a gem, return nil. + + def self.datadir(gem_name) + spec = @loaded_specs[gem_name] + return nil if spec.nil? + File.join(spec.full_gem_path, 'data', gem_name) + end + + ## + # A Zlib::Deflate.deflate wrapper + + def self.deflate(data) + Zlib::Deflate.deflate data + end + + ## + # The path where gems are to be installed. + + def self.dir + @gem_home ||= nil + set_home(ENV['GEM_HOME'] || default_dir) unless @gem_home + @gem_home + end + + ## + # Expand each partial gem path with each of the required paths specified + # in the Gem spec. Each expanded path is yielded. + + def self.each_load_path(partials) + partials.each do |gp| + base = File.basename(gp) + specfn = File.join(dir, "specifications", base + ".gemspec") + if File.exist?(specfn) + spec = eval(File.read(specfn)) + spec.require_paths.each do |rp| + yield(File.join(gp, rp)) + end + else + filename = File.join(gp, 'lib') + yield(filename) if File.exist?(filename) + end + end + end + + private_class_method :each_load_path + + ## + # Quietly ensure the named Gem directory contains all the proper + # subdirectories. If we can't create a directory due to a permission + # problem, then we will silently continue. + + def self.ensure_gem_subdirectories(gemdir) + require 'fileutils' + + Gem::DIRECTORIES.each do |filename| + fn = File.join gemdir, filename + FileUtils.mkdir_p fn rescue nil unless File.exist? fn + end + end + + ## + # Finds the user's home directory. + #-- + # Some comments from the ruby-talk list regarding finding the home + # directory: + # + # I have HOME, USERPROFILE and HOMEDRIVE + HOMEPATH. Ruby seems + # to be depending on HOME in those code samples. I propose that + # it should fallback to USERPROFILE and HOMEDRIVE + HOMEPATH (at + # least on Win32). + + def self.find_home + ['HOME', 'USERPROFILE'].each do |homekey| + return ENV[homekey] if ENV[homekey] + end + + if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then + return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}" + end + + begin + File.expand_path("~") + rescue + if File::ALT_SEPARATOR then + "C:/" + else + "/" + end + end + end + + private_class_method :find_home + + ## + # Zlib::GzipReader wrapper that unzips +data+. + + def self.gunzip(data) + data = StringIO.new data + + Zlib::GzipReader.new(data).read + end + + ## + # Zlib::GzipWriter wrapper that zips +data+. + + def self.gzip(data) + zipped = StringIO.new + + Zlib::GzipWriter.wrap zipped do |io| io.write data end + + zipped.string + end + + ## + # A Zlib::Inflate#inflate wrapper + + def self.inflate(data) + Zlib::Inflate.inflate data + end + + ## + # Return a list of all possible load paths for the latest version for all + # gems in the Gem installation. + + def self.latest_load_paths + result = [] + + Gem.path.each do |gemdir| + each_load_path(latest_partials(gemdir)) do |load_path| + result << load_path + end + end + + result + end + + ## + # Return only the latest partial paths in the given +gemdir+. + + def self.latest_partials(gemdir) + latest = {} + all_partials(gemdir).each do |gp| + base = File.basename(gp) + if base =~ /(.*)-((\d+\.)*\d+)/ then + name, version = $1, $2 + ver = Gem::Version.new(version) + if latest[name].nil? || ver > latest[name][0] + latest[name] = [ver, gp] + end + end + end + latest.collect { |k,v| v[1] } + end + + private_class_method :latest_partials + + ## + # The index to insert activated gem paths into the $LOAD_PATH. + # + # Defaults to the site lib directory unless gem_prelude.rb has loaded paths, + # then it inserts the activated gem's paths before the gem_prelude.rb paths + # so you can override the gem_prelude.rb default $LOAD_PATH paths. + + def self.load_path_insert_index + index = $LOAD_PATH.index ConfigMap[:sitelibdir] + + $LOAD_PATH.each_with_index do |path, i| + if path.instance_variables.include?(:@gem_prelude_index) or + path.instance_variables.include?('@gem_prelude_index') then + index = i + break + end + end + + index + end + + ## + # The file name and line number of the caller of the caller of this method. + + def self.location_of_caller + file, lineno = caller[1].split(':') + lineno = lineno.to_i + [file, lineno] + end + + private_class_method :location_of_caller + + ## + # manage_gems is useless and deprecated. Don't call it anymore. + #-- + # TODO warn w/ RubyGems 1.2.x release. + + def self.manage_gems + #file, lineno = location_of_caller + + #warn "#{file}:#{lineno}:Warning: Gem#manage_gems is deprecated and will be removed on or after September 2008." + end + + ## + # The version of the Marshal format for your Ruby. + + def self.marshal_version + "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}" + end + + ## + # Array of paths to search for Gems. + + def self.path + @gem_path ||= nil + + unless @gem_path then + paths = if ENV['GEM_PATH'] then + [ENV['GEM_PATH']] + else + [default_path] + end + + if defined?(APPLE_GEM_HOME) and not ENV['GEM_PATH'] then + paths << APPLE_GEM_HOME + end + + set_paths paths.compact.join(File::PATH_SEPARATOR) + end + + @gem_path + end + + ## + # Set array of platforms this RubyGems supports (primarily for testing). + + def self.platforms=(platforms) + @platforms = platforms + end + + ## + # Array of platforms this RubyGems supports. + + def self.platforms + @platforms ||= [] + if @platforms.empty? + @platforms = [Gem::Platform::RUBY, Gem::Platform.local] + end + @platforms + end + + ## + # The directory prefix this RubyGems was installed at. + + def self.prefix + prefix = File.dirname File.expand_path(__FILE__) + + if File.dirname(prefix) == File.expand_path(ConfigMap[:sitelibdir]) or + File.dirname(prefix) == File.expand_path(ConfigMap[:libdir]) or + 'lib' != File.basename(prefix) then + nil + else + File.dirname prefix + end + end + + ## + # Refresh source_index from disk and clear searcher. + + def self.refresh + source_index.refresh! + + MUTEX.synchronize do + @searcher = nil + end + end + + ## + # Safely read a file in binary mode on all platforms. + + def self.read_binary(path) + File.open path, binary_mode do |f| f.read end + end + + ## + # Report a load error during activation. The message of load error + # depends on whether it was a version mismatch or if there are not gems of + # any version by the requested name. + + def self.report_activate_error(gem) + matches = Gem.source_index.find_name(gem.name) + + if matches.empty? then + error = Gem::LoadError.new( + "Could not find RubyGem #{gem.name} (#{gem.version_requirements})\n") + else + error = Gem::LoadError.new( + "RubyGem version error: " + + "#{gem.name}(#{matches.first.version} not #{gem.version_requirements})\n") + end + + error.name = gem.name + error.version_requirement = gem.version_requirements + raise error + end + + private_class_method :report_activate_error + + def self.required_location(gemname, libfile, *version_constraints) + version_constraints = Gem::Requirement.default if version_constraints.empty? + matches = Gem.source_index.find_name(gemname, version_constraints) + return nil if matches.empty? + spec = matches.last + spec.require_paths.each do |path| + result = File.join(spec.full_gem_path, path, libfile) + return result if File.exist?(result) + end + nil + end + + ## + # The path to the running Ruby interpreter. + + def self.ruby + if @ruby.nil? then + @ruby = File.join(ConfigMap[:bindir], + ConfigMap[:ruby_install_name]) + @ruby << ConfigMap[:EXEEXT] + end + + @ruby + end + + ## + # A Gem::Version for the currently running ruby. + + def self.ruby_version + return @ruby_version if defined? @ruby_version + version = RUBY_VERSION.dup + version << ".#{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL + @ruby_version = Gem::Version.new version + end + + ## + # The GemPathSearcher object used to search for matching installed gems. + + def self.searcher + MUTEX.synchronize do + @searcher ||= Gem::GemPathSearcher.new + end + end + + ## + # Set the Gem home directory (as reported by Gem.dir). + + def self.set_home(home) + home = home.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR + @gem_home = home + ensure_gem_subdirectories(@gem_home) + end + + private_class_method :set_home + + ## + # Set the Gem search path (as reported by Gem.path). + + def self.set_paths(gpaths) + if gpaths + @gem_path = gpaths.split(File::PATH_SEPARATOR) + + if File::ALT_SEPARATOR then + @gem_path.map! do |path| + path.gsub File::ALT_SEPARATOR, File::SEPARATOR + end + end + + @gem_path << Gem.dir + else + @gem_path = [Gem.dir] + end + + @gem_path.uniq! + @gem_path.each do |gp| ensure_gem_subdirectories(gp) end + end + + private_class_method :set_paths + + ## + # Returns the Gem::SourceIndex of specifications that are in the Gem.path + + def self.source_index + @@source_index ||= SourceIndex.from_installed_gems + end + + ## + # Returns an Array of sources to fetch remote gems from. If the sources + # list is empty, attempts to load the "sources" gem, then uses + # default_sources if it is not installed. + + def self.sources + if @sources.empty? then + begin + gem 'sources', '> 0.0.1' + require 'sources' + rescue LoadError + @sources = default_sources + end + end + + @sources + end + + ## + # Glob pattern for require-able path suffixes. + + def self.suffix_pattern + @suffix_pattern ||= "{#{suffixes.join(',')}}" + end + + ## + # Suffixes for require-able paths. + + def self.suffixes + ['', '.rb', '.rbw', '.so', '.bundle', '.dll', '.sl', '.jar'] + end + + ## + # Use the +home+ and +paths+ values for Gem.dir and Gem.path. Used mainly + # by the unit tests to provide environment isolation. + + def self.use_paths(home, paths=[]) + clear_paths + set_home(home) if home + set_paths(paths.join(File::PATH_SEPARATOR)) if paths + end + + ## + # The home directory for the user. + + def self.user_home + @user_home ||= find_home + end + + ## + # Is this a windows platform? + + def self.win_platform? + if @@win_platform.nil? then + @@win_platform = !!WIN_PATTERNS.find { |r| RUBY_PLATFORM =~ r } + end + + @@win_platform + end + + class << self + + attr_reader :loaded_specs + + # :stopdoc: + + alias cache source_index # an alias for the old name + + # :startdoc: + + end + + MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/" + + YAML_SPEC_DIR = 'quick/' + +end + +module Config + # :stopdoc: + class << self + # Return the path to the data directory associated with the named + # package. If the package is loaded as a gem, return the gem + # specific data directory. Otherwise return a path to the share + # area as define by "#{ConfigMap[:datadir]}/#{package_name}". + def datadir(package_name) + Gem.datadir(package_name) || + File.join(Gem::ConfigMap[:datadir], package_name) + end + end + # :startdoc: +end + +require 'rubygems/exceptions' +require 'rubygems/version' +require 'rubygems/requirement' +require 'rubygems/dependency' +require 'rubygems/gem_path_searcher' # Needed for Kernel#gem +require 'rubygems/source_index' # Needed for Kernel#gem +require 'rubygems/platform' +require 'rubygems/builder' # HACK: Needed for rake's package task. + +begin + require 'rubygems/defaults/operating_system' +rescue LoadError +end + +if defined?(RUBY_ENGINE) then + begin + require "rubygems/defaults/#{RUBY_ENGINE}" + rescue LoadError + end +end + +if RUBY_VERSION < '1.9' then + require 'rubygems/custom_require' +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/builder.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/builder.rb new file mode 100644 index 0000000000..6fd8528f56 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/builder.rb @@ -0,0 +1,88 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +module Gem + + ## + # The Builder class processes RubyGem specification files + # to produce a .gem file. + # + class Builder + + include UserInteraction + ## + # Constructs a builder instance for the provided specification + # + # spec:: [Gem::Specification] The specification instance + # + def initialize(spec) + require "yaml" + require "rubygems/package" + require "rubygems/security" + + @spec = spec + end + + ## + # Builds the gem from the specification. Returns the name of the file + # written. + # + def build + @spec.mark_version + @spec.validate + @signer = sign + write_package + say success + @spec.file_name + end + + def success + <<-EOM + Successfully built RubyGem + Name: #{@spec.name} + Version: #{@spec.version} + File: #{@spec.full_name+'.gem'} +EOM + end + + private + + def sign + # if the signing key was specified, then load the file, and swap + # to the public key (TODO: we should probably just omit the + # signing key in favor of the signing certificate, but that's for + # the future, also the signature algorithm should be configurable) + signer = nil + if @spec.respond_to?(:signing_key) && @spec.signing_key + signer = Gem::Security::Signer.new(@spec.signing_key, @spec.cert_chain) + @spec.signing_key = nil + @spec.cert_chain = signer.cert_chain.map { |cert| cert.to_s } + end + signer + end + + def write_package + open @spec.file_name, 'wb' do |gem_io| + Gem::Package.open gem_io, 'w', @signer do |pkg| + pkg.metadata = @spec.to_yaml + + @spec.files.each do |file| + next if File.directory? file + + stat = File.stat file + mode = stat.mode & 0777 + size = stat.size + + pkg.add_file_simple file, mode, size do |tar_io| + tar_io.write open(file, "rb") { |f| f.read } + end + end + end + end + end + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/command.rb new file mode 100644 index 0000000000..860764e6d5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/command.rb @@ -0,0 +1,406 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'optparse' + +require 'rubygems/user_interaction' + +module Gem + + # Base class for all Gem commands. When creating a new gem command, define + # #arguments, #defaults_str, #description and #usage (as appropriate). + class Command + + include UserInteraction + + # The name of the command. + attr_reader :command + + # The options for the command. + attr_reader :options + + # The default options for the command. + attr_accessor :defaults + + # The name of the command for command-line invocation. + attr_accessor :program_name + + # A short description of the command. + attr_accessor :summary + + # Initializes a generic gem command named +command+. +summary+ is a short + # description displayed in `gem help commands`. +defaults+ are the + # default options. Defaults should be mirrored in #defaults_str, unless + # there are none. + # + # Use add_option to add command-line switches. + def initialize(command, summary=nil, defaults={}) + @command = command + @summary = summary + @program_name = "gem #{command}" + @defaults = defaults + @options = defaults.dup + @option_groups = Hash.new { |h,k| h[k] = [] } + @parser = nil + @when_invoked = nil + end + + # True if +long+ begins with the characters from +short+. + def begins?(long, short) + return false if short.nil? + long[0, short.length] == short + end + + # Override to provide command handling. + def execute + fail "Generic command has no actions" + end + + # Get all gem names from the command line. + def get_all_gem_names + args = options[:args] + + if args.nil? or args.empty? then + raise Gem::CommandLineError, + "Please specify at least one gem name (e.g. gem build GEMNAME)" + end + + gem_names = args.select { |arg| arg !~ /^-/ } + end + + # Get the single gem name from the command line. Fail if there is no gem + # name or if there is more than one gem name given. + def get_one_gem_name + args = options[:args] + + if args.nil? or args.empty? then + raise Gem::CommandLineError, + "Please specify a gem name on the command line (e.g. gem build GEMNAME)" + end + + if args.size > 1 then + raise Gem::CommandLineError, + "Too many gem names (#{args.join(', ')}); please specify only one" + end + + args.first + end + + # Get a single optional argument from the command line. If more than one + # argument is given, return only the first. Return nil if none are given. + def get_one_optional_argument + args = options[:args] || [] + args.first + end + + # Override to provide details of the arguments a command takes. + # It should return a left-justified string, one argument per line. + def arguments + "" + end + + # Override to display the default values of the command + # options. (similar to +arguments+, but displays the default + # values). + def defaults_str + "" + end + + # Override to display a longer description of what this command does. + def description + nil + end + + # Override to display the usage for an individual gem command. + def usage + program_name + end + + # Display the help message for the command. + def show_help + parser.program_name = usage + say parser + end + + # Invoke the command with the given list of arguments. + def invoke(*args) + handle_options(args) + if options[:help] + show_help + elsif @when_invoked + @when_invoked.call(options) + else + execute + end + end + + # Call the given block when invoked. + # + # Normal command invocations just executes the +execute+ method of + # the command. Specifying an invocation block allows the test + # methods to override the normal action of a command to determine + # that it has been invoked correctly. + def when_invoked(&block) + @when_invoked = block + end + + # Add a command-line option and handler to the command. + # + # See OptionParser#make_switch for an explanation of +opts+. + # + # +handler+ will be called with two values, the value of the argument and + # the options hash. + def add_option(*opts, &handler) # :yields: value, options + group_name = Symbol === opts.first ? opts.shift : :options + + @option_groups[group_name] << [opts, handler] + end + + # Remove previously defined command-line argument +name+. + def remove_option(name) + @option_groups.each do |_, option_list| + option_list.reject! { |args, _| args.any? { |x| x =~ /^#{name}/ } } + end + end + + # Merge a set of command options with the set of default options + # (without modifying the default option hash). + def merge_options(new_options) + @options = @defaults.clone + new_options.each do |k,v| @options[k] = v end + end + + # True if the command handles the given argument list. + def handles?(args) + begin + parser.parse!(args.dup) + return true + rescue + return false + end + end + + # Handle the given list of arguments by parsing them and recording + # the results. + def handle_options(args) + args = add_extra_args(args) + @options = @defaults.clone + parser.parse!(args) + @options[:args] = args + end + + def add_extra_args(args) + result = [] + s_extra = Command.specific_extra_args(@command) + extra = Command.extra_args + s_extra + while ! extra.empty? + ex = [] + ex << extra.shift + ex << extra.shift if extra.first.to_s =~ /^[^-]/ + result << ex if handles?(ex) + end + result.flatten! + result.concat(args) + result + end + + private + + # Create on demand parser. + def parser + create_option_parser if @parser.nil? + @parser + end + + def create_option_parser + @parser = OptionParser.new + + @parser.separator("") + regular_options = @option_groups.delete :options + + configure_options "", regular_options + + @option_groups.sort_by { |n,_| n.to_s }.each do |group_name, option_list| + configure_options group_name, option_list + end + + configure_options "Common", Command.common_options + + @parser.separator("") + unless arguments.empty? + @parser.separator(" Arguments:") + arguments.split(/\n/).each do |arg_desc| + @parser.separator(" #{arg_desc}") + end + @parser.separator("") + end + + @parser.separator(" Summary:") + wrap(@summary, 80 - 4).split("\n").each do |line| + @parser.separator(" #{line.strip}") + end + + if description then + formatted = description.split("\n\n").map do |chunk| + wrap(chunk, 80 - 4) + end.join("\n") + + @parser.separator "" + @parser.separator " Description:" + formatted.split("\n").each do |line| + @parser.separator " #{line.rstrip}" + end + end + + unless defaults_str.empty? + @parser.separator("") + @parser.separator(" Defaults:") + defaults_str.split(/\n/).each do |line| + @parser.separator(" #{line}") + end + end + end + + def configure_options(header, option_list) + return if option_list.nil? or option_list.empty? + + header = header.to_s.empty? ? '' : "#{header} " + @parser.separator " #{header}Options:" + + option_list.each do |args, handler| + dashes = args.select { |arg| arg =~ /^-/ } + @parser.on(*args) do |value| + handler.call(value, @options) + end + end + + @parser.separator '' + end + + # Wraps +text+ to +width+ + def wrap(text, width) + text.gsub(/(.{1,#{width}})( +|$\n?)|(.{1,#{width}})/, "\\1\\3\n") + end + + ################################################################## + # Class methods for Command. + class << self + def common_options + @common_options ||= [] + end + + def add_common_option(*args, &handler) + Gem::Command.common_options << [args, handler] + end + + def extra_args + @extra_args ||= [] + end + + def extra_args=(value) + case value + when Array + @extra_args = value + when String + @extra_args = value.split + end + end + + # Return an array of extra arguments for the command. The extra + # arguments come from the gem configuration file read at program + # startup. + def specific_extra_args(cmd) + specific_extra_args_hash[cmd] + end + + # Add a list of extra arguments for the given command. +args+ + # may be an array or a string to be split on white space. + def add_specific_extra_args(cmd,args) + args = args.split(/\s+/) if args.kind_of? String + specific_extra_args_hash[cmd] = args + end + + # Accessor for the specific extra args hash (self initializing). + def specific_extra_args_hash + @specific_extra_args_hash ||= Hash.new do |h,k| + h[k] = Array.new + end + end + end + + # ---------------------------------------------------------------- + # Add the options common to all commands. + + add_common_option('-h', '--help', + 'Get help on this command') do + |value, options| + options[:help] = true + end + + add_common_option('-V', '--[no-]verbose', + 'Set the verbose level of output') do |value, options| + # Set us to "really verbose" so the progress meter works + if Gem.configuration.verbose and value then + Gem.configuration.verbose = 1 + else + Gem.configuration.verbose = value + end + end + + add_common_option('-q', '--quiet', 'Silence commands') do |value, options| + Gem.configuration.verbose = false + end + + # Backtrace and config-file are added so they show up in the help + # commands. Both options are actually handled before the other + # options get parsed. + + add_common_option('--config-file FILE', + "Use this config file instead of default") do + end + + add_common_option('--backtrace', + 'Show stack backtrace on errors') do + end + + add_common_option('--debug', + 'Turn on Ruby debugging') do + end + + # :stopdoc: + HELP = %{ + RubyGems is a sophisticated package manager for Ruby. This is a + basic help message containing pointers to more information. + + Usage: + gem -h/--help + gem -v/--version + gem command [arguments...] [options...] + + Examples: + gem install rake + gem list --local + gem build package.gemspec + gem help install + + Further help: + gem help commands list all 'gem' commands + gem help examples show some examples of usage + gem help platforms show information about platforms + gem help show help on COMMAND + (e.g. 'gem help install') + Further information: + http://rubygems.rubyforge.org + }.gsub(/^ /, "") + + # :startdoc: + + end # class + + # This is where Commands will be placed in the namespace + module Commands; end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/command_manager.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/command_manager.rb new file mode 100644 index 0000000000..dd9a1aee15 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/command_manager.rb @@ -0,0 +1,146 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'timeout' +require 'rubygems/command' +require 'rubygems/user_interaction' + +module Gem + + #################################################################### + # The command manager registers and installs all the individual + # sub-commands supported by the gem command. + class CommandManager + include UserInteraction + + # Return the authoritative instance of the command manager. + def self.instance + @command_manager ||= CommandManager.new + end + + # Register all the subcommands supported by the gem command. + def initialize + @commands = {} + register_command :build + register_command :cert + register_command :check + register_command :cleanup + register_command :contents + register_command :dependency + register_command :environment + register_command :fetch + register_command :generate_index + register_command :help + register_command :install + register_command :list + register_command :lock + register_command :mirror + register_command :outdated + register_command :pristine + register_command :query + register_command :rdoc + register_command :search + register_command :server + register_command :sources + register_command :specification + register_command :stale + register_command :uninstall + register_command :unpack + register_command :update + register_command :which + end + + # Register the command object. + def register_command(command_obj) + @commands[command_obj] = false + end + + # Return the registered command from the command name. + def [](command_name) + command_name = command_name.intern + return nil if @commands[command_name].nil? + @commands[command_name] ||= load_and_instantiate(command_name) + end + + # Return a list of all command names (as strings). + def command_names + @commands.keys.collect {|key| key.to_s}.sort + end + + # Run the config specified by +args+. + def run(args) + process_args(args) + rescue StandardError, Timeout::Error => ex + alert_error "While executing gem ... (#{ex.class})\n #{ex.to_s}" + ui.errs.puts "\t#{ex.backtrace.join "\n\t"}" if + Gem.configuration.backtrace + terminate_interaction(1) + rescue Interrupt + alert_error "Interrupted" + terminate_interaction(1) + end + + def process_args(args) + args = args.to_str.split(/\s+/) if args.respond_to?(:to_str) + if args.size == 0 + say Gem::Command::HELP + terminate_interaction(1) + end + case args[0] + when '-h', '--help' + say Gem::Command::HELP + terminate_interaction(0) + when '-v', '--version' + say Gem::RubyGemsVersion + terminate_interaction(0) + when /^-/ + alert_error "Invalid option: #{args[0]}. See 'gem --help'." + terminate_interaction(1) + else + cmd_name = args.shift.downcase + cmd = find_command(cmd_name) + cmd.invoke(*args) + end + end + + def find_command(cmd_name) + possibilities = find_command_possibilities(cmd_name) + if possibilities.size > 1 + raise "Ambiguous command #{cmd_name} matches [#{possibilities.join(', ')}]" + end + if possibilities.size < 1 + raise "Unknown command #{cmd_name}" + end + + self[possibilities.first] + end + + def find_command_possibilities(cmd_name) + len = cmd_name.length + self.command_names.select { |n| cmd_name == n[0,len] } + end + + private + + def load_and_instantiate(command_name) + command_name = command_name.to_s + retried = false + + begin + const_name = command_name.capitalize.gsub(/_(.)/) { $1.upcase } + Gem::Commands.const_get("#{const_name}Command").new + rescue NameError + if retried then + raise + else + retried = true + require "rubygems/commands/#{command_name}_command" + retry + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/build_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/build_command.rb new file mode 100644 index 0000000000..e1f0122c6c --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/build_command.rb @@ -0,0 +1,53 @@ +require 'rubygems/command' +require 'rubygems/builder' + +class Gem::Commands::BuildCommand < Gem::Command + + def initialize + super('build', 'Build a gem from a gemspec') + end + + def arguments # :nodoc: + "GEMSPEC_FILE gemspec file name to build a gem for" + end + + def usage # :nodoc: + "#{program_name} GEMSPEC_FILE" + end + + def execute + gemspec = get_one_gem_name + if File.exist?(gemspec) + specs = load_gemspecs(gemspec) + specs.each do |spec| + Gem::Builder.new(spec).build + end + else + alert_error "Gemspec file not found: #{gemspec}" + end + end + + def load_gemspecs(filename) + if yaml?(filename) + result = [] + open(filename) do |f| + begin + while not f.eof? and spec = Gem::Specification.from_yaml(f) + result << spec + end + rescue Gem::EndOfYAMLException => e + # OK + end + end + else + result = [Gem::Specification.load(filename)] + end + result + end + + def yaml?(filename) + line = open(filename) { |f| line = f.gets } + result = line =~ %r{!ruby/object:Gem::Specification} + result + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/cert_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/cert_command.rb new file mode 100644 index 0000000000..f5b698855b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/cert_command.rb @@ -0,0 +1,86 @@ +require 'rubygems/command' +require 'rubygems/security' + +class Gem::Commands::CertCommand < Gem::Command + + def initialize + super 'cert', 'Manage RubyGems certificates and signing settings' + + add_option('-a', '--add CERT', + 'Add a trusted certificate.') do |value, options| + cert = OpenSSL::X509::Certificate.new(File.read(value)) + Gem::Security.add_trusted_cert(cert) + say "Added '#{cert.subject.to_s}'" + end + + add_option('-l', '--list', + 'List trusted certificates.') do |value, options| + glob_str = File::join(Gem::Security::OPT[:trust_dir], '*.pem') + Dir::glob(glob_str) do |path| + begin + cert = OpenSSL::X509::Certificate.new(File.read(path)) + # this could probably be formatted more gracefully + say cert.subject.to_s + rescue OpenSSL::X509::CertificateError + next + end + end + end + + add_option('-r', '--remove STRING', + 'Remove trusted certificates containing', + 'STRING.') do |value, options| + trust_dir = Gem::Security::OPT[:trust_dir] + glob_str = File::join(trust_dir, '*.pem') + + Dir::glob(glob_str) do |path| + begin + cert = OpenSSL::X509::Certificate.new(File.read(path)) + if cert.subject.to_s.downcase.index(value) + say "Removed '#{cert.subject.to_s}'" + File.unlink(path) + end + rescue OpenSSL::X509::CertificateError + next + end + end + end + + add_option('-b', '--build EMAIL_ADDR', + 'Build private key and self-signed', + 'certificate for EMAIL_ADDR.') do |value, options| + vals = Gem::Security.build_self_signed_cert(value) + File.chmod 0600, vals[:key_path] + say "Public Cert: #{vals[:cert_path]}" + say "Private Key: #{vals[:key_path]}" + say "Don't forget to move the key file to somewhere private..." + end + + add_option('-C', '--certificate CERT', + 'Certificate for --sign command.') do |value, options| + cert = OpenSSL::X509::Certificate.new(File.read(value)) + Gem::Security::OPT[:issuer_cert] = cert + end + + add_option('-K', '--private-key KEY', + 'Private key for --sign command.') do |value, options| + key = OpenSSL::PKey::RSA.new(File.read(value)) + Gem::Security::OPT[:issuer_key] = key + end + + add_option('-s', '--sign NEWCERT', + 'Sign a certificate with my key and', + 'certificate.') do |value, options| + cert = OpenSSL::X509::Certificate.new(File.read(value)) + my_cert = Gem::Security::OPT[:issuer_cert] + my_key = Gem::Security::OPT[:issuer_key] + cert = Gem::Security.sign_cert(cert, my_key, my_cert) + File.open(value, 'wb') { |file| file.write(cert.to_pem) } + end + end + + def execute + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/check_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/check_command.rb new file mode 100644 index 0000000000..ca5e14b12d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/check_command.rb @@ -0,0 +1,74 @@ +require 'rubygems/command' +require 'rubygems/version_option' +require 'rubygems/validator' + +class Gem::Commands::CheckCommand < Gem::Command + + include Gem::VersionOption + + def initialize + super 'check', 'Check installed gems', + :verify => false, :alien => false + + add_option( '--verify FILE', + 'Verify gem file against its internal', + 'checksum') do |value, options| + options[:verify] = value + end + + add_option('-a', '--alien', "Report 'unmanaged' or rogue files in the", + "gem repository") do |value, options| + options[:alien] = true + end + + add_option('-t', '--test', "Run unit tests for gem") do |value, options| + options[:test] = true + end + + add_version_option 'run tests for' + end + + def execute + if options[:test] + version = options[:version] || Gem::Requirement.default + gem_spec = Gem::SourceIndex.from_installed_gems.search(get_one_gem_name, version).first + Gem::Validator.new.unit_test(gem_spec) + end + + if options[:alien] + say "Performing the 'alien' operation" + Gem::Validator.new.alien.each do |key, val| + if(val.size > 0) + say "#{key} has #{val.size} problems" + val.each do |error_entry| + say "\t#{error_entry.path}:" + say "\t#{error_entry.problem}" + say + end + else + say "#{key} is error-free" + end + say + end + end + + if options[:verify] + gem_name = options[:verify] + unless gem_name + alert_error "Must specify a .gem file with --verify NAME" + return + end + unless File.exist?(gem_name) + alert_error "Unknown file: #{gem_name}." + return + end + say "Verifying gem: '#{gem_name}'" + begin + Gem::Validator.new.verify_gem_file(gem_name) + rescue Exception => e + alert_error "#{gem_name} is invalid." + end + end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/cleanup_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/cleanup_command.rb new file mode 100644 index 0000000000..40dcb9db34 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/cleanup_command.rb @@ -0,0 +1,91 @@ +require 'rubygems/command' +require 'rubygems/source_index' +require 'rubygems/dependency_list' + +class Gem::Commands::CleanupCommand < Gem::Command + + def initialize + super 'cleanup', + 'Clean up old versions of installed gems in the local repository', + :force => false, :test => false, :install_dir => Gem.dir + + add_option('-d', '--dryrun', "") do |value, options| + options[:dryrun] = true + end + end + + def arguments # :nodoc: + "GEMNAME name of gem to cleanup" + end + + def defaults_str # :nodoc: + "--no-dryrun" + end + + def usage # :nodoc: + "#{program_name} [GEMNAME ...]" + end + + def execute + say "Cleaning up installed gems..." + primary_gems = {} + + Gem.source_index.each do |name, spec| + if primary_gems[spec.name].nil? or + primary_gems[spec.name].version < spec.version then + primary_gems[spec.name] = spec + end + end + + gems_to_cleanup = [] + + unless options[:args].empty? then + options[:args].each do |gem_name| + specs = Gem.cache.search(/^#{gem_name}$/i) + specs.each do |spec| + gems_to_cleanup << spec + end + end + else + Gem.source_index.each do |name, spec| + gems_to_cleanup << spec + end + end + + gems_to_cleanup = gems_to_cleanup.select { |spec| + primary_gems[spec.name].version != spec.version + } + + uninstall_command = Gem::CommandManager.instance['uninstall'] + deplist = Gem::DependencyList.new + gems_to_cleanup.uniq.each do |spec| deplist.add spec end + + deps = deplist.strongly_connected_components.flatten.reverse + + deps.each do |spec| + if options[:dryrun] then + say "Dry Run Mode: Would uninstall #{spec.full_name}" + else + say "Attempting to uninstall #{spec.full_name}" + + options[:args] = [spec.name] + options[:version] = "= #{spec.version}" + options[:executables] = false + + uninstaller = Gem::Uninstaller.new spec.name, options + + begin + uninstaller.uninstall + rescue Gem::DependencyRemovalException, + Gem::GemNotInHomeException => e + say "Unable to uninstall #{spec.full_name}:" + say "\t#{e.class}: #{e.message}" + end + end + end + + say "Clean Up Complete" + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/contents_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/contents_command.rb new file mode 100644 index 0000000000..5060403fd8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/contents_command.rb @@ -0,0 +1,74 @@ +require 'rubygems/command' +require 'rubygems/version_option' + +class Gem::Commands::ContentsCommand < Gem::Command + + include Gem::VersionOption + + def initialize + super 'contents', 'Display the contents of the installed gems', + :specdirs => [], :lib_only => false + + add_version_option + + add_option('-s', '--spec-dir a,b,c', Array, + "Search for gems under specific paths") do |spec_dirs, options| + options[:specdirs] = spec_dirs + end + + add_option('-l', '--[no-]lib-only', + "Only return files in the Gem's lib_dirs") do |lib_only, options| + options[:lib_only] = lib_only + end + end + + def arguments # :nodoc: + "GEMNAME name of gem to list contents for" + end + + def defaults_str # :nodoc: + "--no-lib-only" + end + + def usage # :nodoc: + "#{program_name} GEMNAME" + end + + def execute + version = options[:version] || Gem::Requirement.default + gem = get_one_gem_name + + s = options[:specdirs].map do |i| + [i, File.join(i, "specifications")] + end.flatten + + path_kind = if s.empty? then + s = Gem::SourceIndex.installed_spec_directories + "default gem paths" + else + "specified path" + end + + si = Gem::SourceIndex.from_gems_in(*s) + + gem_spec = si.search(/\A#{gem}\z/, version).last + + unless gem_spec then + say "Unable to find gem '#{gem}' in #{path_kind}" + + if Gem.configuration.verbose then + say "\nDirectories searched:" + s.each { |dir| say dir } + end + + terminate_interaction + end + + files = options[:lib_only] ? gem_spec.lib_files : gem_spec.files + files.each do |f| + say File.join(gem_spec.full_gem_path, f) + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/dependency_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/dependency_command.rb new file mode 100644 index 0000000000..44b269bb11 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/dependency_command.rb @@ -0,0 +1,188 @@ +require 'rubygems/command' +require 'rubygems/local_remote_options' +require 'rubygems/version_option' +require 'rubygems/source_info_cache' + +class Gem::Commands::DependencyCommand < Gem::Command + + include Gem::LocalRemoteOptions + include Gem::VersionOption + + def initialize + super 'dependency', + 'Show the dependencies of an installed gem', + :version => Gem::Requirement.default, :domain => :local + + add_version_option + add_platform_option + + add_option('-R', '--[no-]reverse-dependencies', + 'Include reverse dependencies in the output') do + |value, options| + options[:reverse_dependencies] = value + end + + add_option('-p', '--pipe', + "Pipe Format (name --version ver)") do |value, options| + options[:pipe_format] = value + end + + add_local_remote_options + end + + def arguments # :nodoc: + "GEMNAME name of gem to show dependencies for" + end + + def defaults_str # :nodoc: + "--local --version '#{Gem::Requirement.default}' --no-reverse-dependencies" + end + + def usage # :nodoc: + "#{program_name} GEMNAME" + end + + def execute + options[:args] << '' if options[:args].empty? + specs = {} + + source_indexes = Hash.new do |h, source_uri| + h[source_uri] = Gem::SourceIndex.new + end + + pattern = if options[:args].length == 1 and + options[:args].first =~ /\A\/(.*)\/(i)?\z/m then + flags = $2 ? Regexp::IGNORECASE : nil + Regexp.new $1, flags + else + /\A#{Regexp.union(*options[:args])}/ + end + + dependency = Gem::Dependency.new pattern, options[:version] + + if options[:reverse_dependencies] and remote? and not local? then + alert_error 'Only reverse dependencies for local gems are supported.' + terminate_interaction 1 + end + + if local? then + Gem.source_index.search(dependency).each do |spec| + source_indexes[:local].add_spec spec + end + end + + if remote? and not options[:reverse_dependencies] then + fetcher = Gem::SpecFetcher.fetcher + + begin + fetcher.find_matching(dependency).each do |spec_tuple, source_uri| + spec = fetcher.fetch_spec spec_tuple, URI.parse(source_uri) + + source_indexes[source_uri].add_spec spec + end + rescue Gem::RemoteFetcher::FetchError => e + raise unless fetcher.warn_legacy e do + require 'rubygems/source_info_cache' + + specs = Gem::SourceInfoCache.search_with_source dependency, false + + specs.each do |spec, source_uri| + source_indexes[source_uri].add_spec spec + end + end + end + end + + if source_indexes.empty? then + patterns = options[:args].join ',' + say "No gems found matching #{patterns} (#{options[:version]})" if + Gem.configuration.verbose + + terminate_interaction 1 + end + + specs = {} + + source_indexes.values.each do |source_index| + source_index.gems.each do |name, spec| + specs[spec.full_name] = [source_index, spec] + end + end + + reverse = Hash.new { |h, k| h[k] = [] } + + if options[:reverse_dependencies] then + specs.values.each do |_, spec| + reverse[spec.full_name] = find_reverse_dependencies spec + end + end + + if options[:pipe_format] then + specs.values.sort_by { |_, spec| spec }.each do |_, spec| + unless spec.dependencies.empty? + spec.dependencies.each do |dep| + say "#{dep.name} --version '#{dep.version_requirements}'" + end + end + end + else + response = '' + + specs.values.sort_by { |_, spec| spec }.each do |_, spec| + response << print_dependencies(spec) + unless reverse[spec.full_name].empty? then + response << " Used by\n" + reverse[spec.full_name].each do |sp, dep| + response << " #{sp} (#{dep})\n" + end + end + response << "\n" + end + + say response + end + end + + def print_dependencies(spec, level = 0) + response = '' + response << ' ' * level + "Gem #{spec.full_name}\n" + unless spec.dependencies.empty? then + spec.dependencies.each do |dep| + response << ' ' * level + " #{dep}\n" + end + end + response + end + + # Retuns list of [specification, dep] that are satisfied by spec. + def find_reverse_dependencies(spec) + result = [] + + Gem.source_index.each do |name, sp| + sp.dependencies.each do |dep| + dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep + + if spec.name == dep.name and + dep.version_requirements.satisfied_by?(spec.version) then + result << [sp.full_name, dep] + end + end + end + + result + end + + def find_gems(name, source_index) + specs = {} + + spec_list = source_index.search name, options[:version] + + spec_list.each do |spec| + specs[spec.full_name] = [source_index, spec] + end + + specs + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/environment_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/environment_command.rb new file mode 100644 index 0000000000..a67c00bfd6 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/environment_command.rb @@ -0,0 +1,88 @@ +require 'rubygems/command' + +class Gem::Commands::EnvironmentCommand < Gem::Command + + def initialize + super 'environment', 'Display information about the RubyGems environment' + end + + def arguments # :nodoc: + args = <<-EOF + packageversion display the package version + gemdir display the path where gems are installed + gempath display path used to search for gems + version display the gem format version + remotesources display the remote gem servers + display everything + EOF + return args.gsub(/^\s+/, '') + end + + def usage # :nodoc: + "#{program_name} [arg]" + end + + def execute + out = '' + arg = options[:args][0] + case arg + when /^packageversion/ then + out << Gem::RubyGemsPackageVersion + when /^version/ then + out << Gem::RubyGemsVersion + when /^gemdir/, /^gemhome/, /^home/, /^GEM_HOME/ then + out << Gem.dir + when /^gempath/, /^path/, /^GEM_PATH/ then + out << Gem.path.join(File::PATH_SEPARATOR) + when /^remotesources/ then + out << Gem.sources.join("\n") + when nil then + out = "RubyGems Environment:\n" + + out << " - RUBYGEMS VERSION: #{Gem::RubyGemsVersion}\n" + + out << " - RUBY VERSION: #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}" + out << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL + out << ") [#{RUBY_PLATFORM}]\n" + + out << " - INSTALLATION DIRECTORY: #{Gem.dir}\n" + + out << " - RUBYGEMS PREFIX: #{Gem.prefix}\n" unless Gem.prefix.nil? + + out << " - RUBY EXECUTABLE: #{Gem.ruby}\n" + + out << " - EXECUTABLE DIRECTORY: #{Gem.bindir}\n" + + out << " - RUBYGEMS PLATFORMS:\n" + Gem.platforms.each do |platform| + out << " - #{platform}\n" + end + + out << " - GEM PATHS:\n" + out << " - #{Gem.dir}\n" + + path = Gem.path.dup + path.delete Gem.dir + path.each do |p| + out << " - #{p}\n" + end + + out << " - GEM CONFIGURATION:\n" + Gem.configuration.each do |name, value| + out << " - #{name.inspect} => #{value.inspect}\n" + end + + out << " - REMOTE SOURCES:\n" + Gem.sources.each do |s| + out << " - #{s}\n" + end + + else + fail Gem::CommandLineError, "Unknown enviroment option [#{arg}]" + end + say out + true + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/fetch_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/fetch_command.rb new file mode 100644 index 0000000000..76c9924e6b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/fetch_command.rb @@ -0,0 +1,62 @@ +require 'rubygems/command' +require 'rubygems/local_remote_options' +require 'rubygems/version_option' +require 'rubygems/source_info_cache' + +class Gem::Commands::FetchCommand < Gem::Command + + include Gem::LocalRemoteOptions + include Gem::VersionOption + + def initialize + super 'fetch', 'Download a gem and place it in the current directory' + + add_bulk_threshold_option + add_proxy_option + add_source_option + + add_version_option + add_platform_option + end + + def arguments # :nodoc: + 'GEMNAME name of gem to download' + end + + def defaults_str # :nodoc: + "--version '#{Gem::Requirement.default}'" + end + + def usage # :nodoc: + "#{program_name} GEMNAME [GEMNAME ...]" + end + + def execute + version = options[:version] || Gem::Requirement.default + all = Gem::Requirement.default + + gem_names = get_all_gem_names + + gem_names.each do |gem_name| + dep = Gem::Dependency.new gem_name, version + + specs_and_sources = Gem::SpecFetcher.fetcher.fetch dep, all + + specs_and_sources.sort_by { |spec,| spec.version } + + spec, source_uri = specs_and_sources.last + + if spec.nil? then + alert_error "Could not find #{gem_name} in any repository" + next + end + + path = Gem::RemoteFetcher.fetcher.download spec, source_uri + FileUtils.mv path, "#{spec.full_name}.gem" + + say "Downloaded #{spec.full_name}" + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/generate_index_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/generate_index_command.rb new file mode 100644 index 0000000000..1bd87569ed --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/generate_index_command.rb @@ -0,0 +1,57 @@ +require 'rubygems/command' +require 'rubygems/indexer' + +class Gem::Commands::GenerateIndexCommand < Gem::Command + + def initialize + super 'generate_index', + 'Generates the index files for a gem server directory', + :directory => '.' + + add_option '-d', '--directory=DIRNAME', + 'repository base dir containing gems subdir' do |dir, options| + options[:directory] = File.expand_path dir + end + end + + def defaults_str # :nodoc: + "--directory ." + end + + def description # :nodoc: + <<-EOF +The generate_index command creates a set of indexes for serving gems +statically. The command expects a 'gems' directory under the path given to +the --directory option. When done, it will generate a set of files like this: + + gems/ # .gem files you want to index + quick/index + quick/index.rz # quick index manifest + quick/.gemspec.rz # legacy YAML quick index file + quick/Marshal./.gemspec.rz # Marshal quick index file + Marshal. + Marshal..Z # Marshal full index + yaml + yaml.Z # legacy YAML full index + +The .Z and .rz extension files are compressed with the inflate algorithm. The +Marshal version number comes from ruby's Marshal::MAJOR_VERSION and +Marshal::MINOR_VERSION constants. It is used to ensure compatibility. The +yaml indexes exist for legacy RubyGems clients and fallback in case of Marshal +version changes. + EOF + end + + def execute + if not File.exist?(options[:directory]) or + not File.directory?(options[:directory]) then + alert_error "unknown directory name #{directory}." + terminate_interaction 1 + else + indexer = Gem::Indexer.new options[:directory] + indexer.generate_index + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/help_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/help_command.rb new file mode 100644 index 0000000000..05ea3f7a71 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/help_command.rb @@ -0,0 +1,172 @@ +require 'rubygems/command' + +class Gem::Commands::HelpCommand < Gem::Command + + # :stopdoc: + EXAMPLES = <<-EOF +Some examples of 'gem' usage. + +* Install 'rake', either from local directory or remote server: + + gem install rake + +* Install 'rake', only from remote server: + + gem install rake --remote + +* Install 'rake' from remote server, and run unit tests, + and generate RDocs: + + gem install --remote rake --test --rdoc --ri + +* Install 'rake', but only version 0.3.1, even if dependencies + are not met, and into a specific directory: + + gem install rake --version 0.3.1 --force --install-dir $HOME/.gems + +* List local gems whose name begins with 'D': + + gem list D + +* List local and remote gems whose name contains 'log': + + gem search log --both + +* List only remote gems whose name contains 'log': + + gem search log --remote + +* Uninstall 'rake': + + gem uninstall rake + +* Create a gem: + + See http://rubygems.rubyforge.org/wiki/wiki.pl?CreateAGemInTenMinutes + +* See information about RubyGems: + + gem environment + +* Update all gems on your system: + + gem update + EOF + + PLATFORMS = <<-'EOF' +RubyGems platforms are composed of three parts, a CPU, an OS, and a +version. These values are taken from values in rbconfig.rb. You can view +your current platform by running `gem environment`. + +RubyGems matches platforms as follows: + + * The CPU must match exactly, unless one of the platforms has + "universal" as the CPU. + * The OS must match exactly. + * The versions must match exactly unless one of the versions is nil. + +For commands that install, uninstall and list gems, you can override what +RubyGems thinks your platform is with the --platform option. The platform +you pass must match "#{cpu}-#{os}" or "#{cpu}-#{os}-#{version}". On mswin +platforms, the version is the compiler version, not the OS version. (Ruby +compiled with VC6 uses "60" as the compiler version, VC8 uses "80".) + +Example platforms: + + x86-freebsd # Any FreeBSD version on an x86 CPU + universal-darwin-8 # Darwin 8 only gems that run on any CPU + x86-mswin32-80 # Windows gems compiled with VC8 + +When building platform gems, set the platform in the gem specification to +Gem::Platform::CURRENT. This will correctly mark the gem with your ruby's +platform. + EOF + # :startdoc: + + def initialize + super 'help', "Provide help on the 'gem' command" + end + + def arguments # :nodoc: + args = <<-EOF + commands List all 'gem' commands + examples Show examples of 'gem' usage + Show specific help for + EOF + return args.gsub(/^\s+/, '') + end + + def usage # :nodoc: + "#{program_name} ARGUMENT" + end + + def execute + command_manager = Gem::CommandManager.instance + arg = options[:args][0] + + if begins? "commands", arg then + out = [] + out << "GEM commands are:" + out << nil + + margin_width = 4 + + desc_width = command_manager.command_names.map { |n| n.size }.max + 4 + + summary_width = 80 - margin_width - desc_width + wrap_indent = ' ' * (margin_width + desc_width) + format = "#{' ' * margin_width}%-#{desc_width}s%s" + + command_manager.command_names.each do |cmd_name| + summary = command_manager[cmd_name].summary + summary = wrap(summary, summary_width).split "\n" + out << sprintf(format, cmd_name, summary.shift) + until summary.empty? do + out << "#{wrap_indent}#{summary.shift}" + end + end + + out << nil + out << "For help on a particular command, use 'gem help COMMAND'." + out << nil + out << "Commands may be abbreviated, so long as they are unambiguous." + out << "e.g. 'gem i rake' is short for 'gem install rake'." + + say out.join("\n") + + elsif begins? "options", arg then + say Gem::Command::HELP + + elsif begins? "examples", arg then + say EXAMPLES + + elsif begins? "platforms", arg then + say PLATFORMS + + elsif options[:help] then + command = command_manager[options[:help]] + if command + # help with provided command + command.invoke("--help") + else + alert_error "Unknown command #{options[:help]}. Try 'gem help commands'" + end + + elsif arg then + possibilities = command_manager.find_command_possibilities(arg.downcase) + if possibilities.size == 1 + command = command_manager[possibilities.first] + command.invoke("--help") + elsif possibilities.size > 1 + alert_warning "Ambiguous command #{arg} (#{possibilities.join(', ')})" + else + alert_warning "Unknown command #{arg}. Try gem help commands" + end + + else + say Gem::Command::HELP + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/install_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/install_command.rb new file mode 100644 index 0000000000..923d578a15 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/install_command.rb @@ -0,0 +1,133 @@ +require 'rubygems/command' +require 'rubygems/doc_manager' +require 'rubygems/install_update_options' +require 'rubygems/dependency_installer' +require 'rubygems/local_remote_options' +require 'rubygems/validator' +require 'rubygems/version_option' + +class Gem::Commands::InstallCommand < Gem::Command + + include Gem::VersionOption + include Gem::LocalRemoteOptions + include Gem::InstallUpdateOptions + + def initialize + defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({ + :generate_rdoc => true, + :generate_ri => true, + :format_executable => false, + :test => false, + :version => Gem::Requirement.default, + }) + + super 'install', 'Install a gem into the local repository', defaults + + add_install_update_options + add_local_remote_options + add_platform_option + add_version_option + end + + def arguments # :nodoc: + "GEMNAME name of gem to install" + end + + def defaults_str # :nodoc: + "--both --version '#{Gem::Requirement.default}' --rdoc --ri --no-force\n" \ + "--no-test --install-dir #{Gem.dir}" + end + + def usage # :nodoc: + "#{program_name} GEMNAME [GEMNAME ...] [options] -- --build-flags" + end + + def execute + if options[:include_dependencies] then + alert "`gem install -y` is now default and will be removed" + alert "use --ignore-dependencies to install only the gems you list" + end + + installed_gems = [] + + ENV.delete 'GEM_PATH' if options[:install_dir].nil? and RUBY_VERSION > '1.9' + + install_options = { + :env_shebang => options[:env_shebang], + :domain => options[:domain], + :force => options[:force], + :format_executable => options[:format_executable], + :ignore_dependencies => options[:ignore_dependencies], + :install_dir => options[:install_dir], + :security_policy => options[:security_policy], + :wrappers => options[:wrappers], + :bin_dir => options[:bin_dir], + :development => options[:development], + } + + exit_code = 0 + + get_all_gem_names.each do |gem_name| + begin + inst = Gem::DependencyInstaller.new install_options + inst.install gem_name, options[:version] + + inst.installed_gems.each do |spec| + say "Successfully installed #{spec.full_name}" + end + + installed_gems.push(*inst.installed_gems) + rescue Gem::InstallError => e + alert_error "Error installing #{gem_name}:\n\t#{e.message}" + exit_code |= 1 + rescue Gem::GemNotFoundException => e + alert_error e.message + exit_code |= 2 +# rescue => e +# # TODO: Fix this handle to allow the error to propagate to +# # the top level handler. Examine the other errors as +# # well. This implementation here looks suspicious to me -- +# # JimWeirich (4/Jan/05) +# alert_error "Error installing gem #{gem_name}: #{e.message}" +# return + end + end + + unless installed_gems.empty? then + gems = installed_gems.length == 1 ? 'gem' : 'gems' + say "#{installed_gems.length} #{gems} installed" + end + + # NOTE: *All* of the RI documents must be generated first. + # For some reason, RI docs cannot be generated after any RDoc + # documents are generated. + + if options[:generate_ri] then + installed_gems.each do |gem| + Gem::DocManager.new(gem, options[:rdoc_args]).generate_ri + end + end + + if options[:generate_rdoc] then + installed_gems.each do |gem| + Gem::DocManager.new(gem, options[:rdoc_args]).generate_rdoc + end + end + + if options[:test] then + installed_gems.each do |spec| + gem_spec = Gem::SourceIndex.from_installed_gems.search(spec.name, spec.version.version).first + result = Gem::Validator.new.unit_test(gem_spec) + if result and not result.passed? + unless ask_yes_no("...keep Gem?", true) then + Gem::Uninstaller.new(spec.name, :version => spec.version.version).uninstall + end + end + end + end + + raise Gem::SystemExitException, exit_code + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/list_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/list_command.rb new file mode 100644 index 0000000000..f3e5da9551 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/list_command.rb @@ -0,0 +1,35 @@ +require 'rubygems/command' +require 'rubygems/commands/query_command' + +## +# An alternate to Gem::Commands::QueryCommand that searches for gems starting +# with the the supplied argument. + +class Gem::Commands::ListCommand < Gem::Commands::QueryCommand + + def initialize + super 'list', 'Display gems whose name starts with STRING' + + remove_option('--name-matches') + end + + def arguments # :nodoc: + "STRING start of gem name to look for" + end + + def defaults_str # :nodoc: + "--local --no-details" + end + + def usage # :nodoc: + "#{program_name} [STRING]" + end + + def execute + string = get_one_optional_argument || '' + options[:name] = /^#{string}/i + super + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/lock_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/lock_command.rb new file mode 100644 index 0000000000..6be2774e92 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/lock_command.rb @@ -0,0 +1,101 @@ +require 'rubygems/command' + +class Gem::Commands::LockCommand < Gem::Command + + def initialize + super 'lock', 'Generate a lockdown list of gems', + :strict => false + + add_option '-s', '--[no-]strict', + 'fail if unable to satisfy a dependency' do |strict, options| + options[:strict] = strict + end + end + + def arguments # :nodoc: + "GEMNAME name of gem to lock\nVERSION version of gem to lock" + end + + def defaults_str # :nodoc: + "--no-strict" + end + + def description # :nodoc: + <<-EOF +The lock command will generate a list of +gem+ statements that will lock down +the versions for the gem given in the command line. It will specify exact +versions in the requirements list to ensure that the gems loaded will always +be consistent. A full recursive search of all effected gems will be +generated. + +Example: + + gemlock rails-1.0.0 > lockdown.rb + +will produce in lockdown.rb: + + require "rubygems" + gem 'rails', '= 1.0.0' + gem 'rake', '= 0.7.0.1' + gem 'activesupport', '= 1.2.5' + gem 'activerecord', '= 1.13.2' + gem 'actionpack', '= 1.11.2' + gem 'actionmailer', '= 1.1.5' + gem 'actionwebservice', '= 1.0.0' + +Just load lockdown.rb from your application to ensure that the current +versions are loaded. Make sure that lockdown.rb is loaded *before* any +other require statements. + +Notice that rails 1.0.0 only requires that rake 0.6.2 or better be used. +Rake-0.7.0.1 is the most recent version installed that satisfies that, so we +lock it down to the exact version. + EOF + end + + def usage # :nodoc: + "#{program_name} GEMNAME-VERSION [GEMNAME-VERSION ...]" + end + + def complain(message) + if options.strict then + raise message + else + say "# #{message}" + end + end + + def execute + say 'require "rubygems"' + + locked = {} + + pending = options[:args] + + until pending.empty? do + full_name = pending.shift + + spec = Gem::SourceIndex.load_specification spec_path(full_name) + + say "gem '#{spec.name}', '= #{spec.version}'" unless locked[spec.name] + locked[spec.name] = true + + spec.runtime_dependencies.each do |dep| + next if locked[dep.name] + candidates = Gem.source_index.search dep.name, dep.requirement_list + + if candidates.empty? then + complain "Unable to satisfy '#{dep}' from currently installed gems." + else + pending << candidates.last.full_name + end + end + end + end + + def spec_path(gem_full_name) + File.join Gem.path, "specifications", "#{gem_full_name }.gemspec" + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/mirror_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/mirror_command.rb new file mode 100644 index 0000000000..959b8eaec3 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/mirror_command.rb @@ -0,0 +1,111 @@ +require 'yaml' +require 'zlib' + +require 'rubygems/command' +require 'open-uri' + +class Gem::Commands::MirrorCommand < Gem::Command + + def initialize + super 'mirror', 'Mirror a gem repository' + end + + def description # :nodoc: + <<-EOF +The mirror command uses the ~/.gemmirrorrc config file to mirror remote gem +repositories to a local path. The config file is a YAML document that looks +like this: + + --- + - from: http://gems.example.com # source repository URI + to: /path/to/mirror # destination directory + +Multiple sources and destinations may be specified. + EOF + end + + def execute + config_file = File.join Gem.user_home, '.gemmirrorrc' + + raise "Config file #{config_file} not found" unless File.exist? config_file + + mirrors = YAML.load_file config_file + + raise "Invalid config file #{config_file}" unless mirrors.respond_to? :each + + mirrors.each do |mir| + raise "mirror missing 'from' field" unless mir.has_key? 'from' + raise "mirror missing 'to' field" unless mir.has_key? 'to' + + get_from = mir['from'] + save_to = File.expand_path mir['to'] + + raise "Directory not found: #{save_to}" unless File.exist? save_to + raise "Not a directory: #{save_to}" unless File.directory? save_to + + gems_dir = File.join save_to, "gems" + + if File.exist? gems_dir then + raise "Not a directory: #{gems_dir}" unless File.directory? gems_dir + else + Dir.mkdir gems_dir + end + + sourceindex_data = '' + + say "fetching: #{get_from}/Marshal.#{Gem.marshal_version}.Z" + + get_from = URI.parse get_from + + if get_from.scheme.nil? then + get_from = get_from.to_s + elsif get_from.scheme == 'file' then + # check if specified URI contains a drive letter (file:/D:/Temp) + get_from = get_from.to_s + get_from = if get_from =~ /^file:.*[a-z]:/i then + get_from[6..-1] + else + get_from[5..-1] + end + end + + open File.join(get_from.to_s, "Marshal.#{Gem.marshal_version}.Z"), "rb" do |y| + sourceindex_data = Zlib::Inflate.inflate y.read + open File.join(save_to, "Marshal.#{Gem.marshal_version}"), "wb" do |out| + out.write sourceindex_data + end + end + + sourceindex = Marshal.load(sourceindex_data) + + progress = ui.progress_reporter sourceindex.size, + "Fetching #{sourceindex.size} gems" + sourceindex.each do |fullname, gem| + gem_file = "#{fullname}.gem" + gem_dest = File.join gems_dir, gem_file + + unless File.exist? gem_dest then + begin + open "#{get_from}/gems/#{gem_file}", "rb" do |g| + contents = g.read + open gem_dest, "wb" do |out| + out.write contents + end + end + rescue + old_gf = gem_file + gem_file = gem_file.downcase + retry if old_gf != gem_file + alert_error $! + end + end + + progress.updated gem_file + end + + progress.done + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/outdated_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/outdated_command.rb new file mode 100644 index 0000000000..1cd1087dd1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/outdated_command.rb @@ -0,0 +1,33 @@ +require 'rubygems/command' +require 'rubygems/local_remote_options' +require 'rubygems/spec_fetcher' +require 'rubygems/version_option' + +class Gem::Commands::OutdatedCommand < Gem::Command + + include Gem::LocalRemoteOptions + include Gem::VersionOption + + def initialize + super 'outdated', 'Display all gems that need updates' + + add_local_remote_options + add_platform_option + end + + def execute + locals = Gem::SourceIndex.from_installed_gems + + locals.outdated.sort.each do |name| + local = locals.search(/^#{name}$/).last + + dep = Gem::Dependency.new local.name, ">= #{local.version}" + remotes = Gem::SpecFetcher.fetcher.fetch dep + remote = remotes.last.first + + say "#{local.name} (#{local.version} < #{remote.version})" + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/pristine_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/pristine_command.rb new file mode 100644 index 0000000000..3e55a1bb30 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/pristine_command.rb @@ -0,0 +1,93 @@ +require 'fileutils' +require 'rubygems/command' +require 'rubygems/format' +require 'rubygems/installer' +require 'rubygems/version_option' + +class Gem::Commands::PristineCommand < Gem::Command + + include Gem::VersionOption + + def initialize + super 'pristine', + 'Restores installed gems to pristine condition from files located in the gem cache', + :version => Gem::Requirement.default + + add_option('--all', + 'Restore all installed gems to pristine', + 'condition') do |value, options| + options[:all] = value + end + + add_version_option('restore to', 'pristine condition') + end + + def arguments # :nodoc: + "GEMNAME gem to restore to pristine condition (unless --all)" + end + + def defaults_str # :nodoc: + "--all" + end + + def description # :nodoc: + <<-EOF +The pristine command compares the installed gems with the contents of the +cached gem and restores any files that don't match the cached gem's copy. + +If you have made modifications to your installed gems, the pristine command +will revert them. After all the gem's files have been checked all bin stubs +for the gem are regenerated. + +If the cached gem cannot be found, you will need to use `gem install` to +revert the gem. + EOF + end + + def usage # :nodoc: + "#{program_name} [args]" + end + + def execute + gem_name = nil + + specs = if options[:all] then + Gem::SourceIndex.from_installed_gems.map do |name, spec| + spec + end + else + gem_name = get_one_gem_name + Gem::SourceIndex.from_installed_gems.search(gem_name, + options[:version]) + end + + if specs.empty? then + raise Gem::Exception, + "Failed to find gem #{gem_name} #{options[:version]}" + end + + install_dir = Gem.dir # TODO use installer option + + raise Gem::FilePermissionError.new(install_dir) unless + File.writable?(install_dir) + + say "Restoring gem(s) to pristine condition..." + + specs.each do |spec| + gem = Dir[File.join(Gem.dir, 'cache', "#{spec.full_name}.gem")].first + + if gem.nil? then + alert_error "Cached gem for #{spec.full_name} not found, use `gem install` to restore" + next + end + + # TODO use installer options + installer = Gem::Installer.new gem, :wrappers => true, :force => true + installer.install + + say "Restored #{spec.full_name}" + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/query_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/query_command.rb new file mode 100644 index 0000000000..f4d6120bcd --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/query_command.rb @@ -0,0 +1,228 @@ +require 'rubygems/command' +require 'rubygems/local_remote_options' +require 'rubygems/spec_fetcher' +require 'rubygems/version_option' + +class Gem::Commands::QueryCommand < Gem::Command + + include Gem::LocalRemoteOptions + include Gem::VersionOption + + def initialize(name = 'query', + summary = 'Query gem information in local or remote repositories') + super name, summary, + :name => //, :domain => :local, :details => false, :versions => true, + :installed => false, :version => Gem::Requirement.default + + add_option('-i', '--[no-]installed', + 'Check for installed gem') do |value, options| + options[:installed] = value + end + + add_version_option + + add_option('-n', '--name-matches REGEXP', + 'Name of gem(s) to query on matches the', + 'provided REGEXP') do |value, options| + options[:name] = /#{value}/i + end + + add_option('-d', '--[no-]details', + 'Display detailed information of gem(s)') do |value, options| + options[:details] = value + end + + add_option( '--[no-]versions', + 'Display only gem names') do |value, options| + options[:versions] = value + options[:details] = false unless value + end + + add_option('-a', '--all', + 'Display all gem versions') do |value, options| + options[:all] = value + end + + add_local_remote_options + end + + def defaults_str # :nodoc: + "--local --name-matches // --no-details --versions --no-installed" + end + + def execute + exit_code = 0 + + name = options[:name] + + if options[:installed] then + if name.source.empty? then + alert_error "You must specify a gem name" + exit_code |= 4 + elsif installed? name.source, options[:version] then + say "true" + else + say "false" + exit_code |= 1 + end + + raise Gem::SystemExitException, exit_code + end + + if local? then + say + say "*** LOCAL GEMS ***" + say + + specs = Gem.source_index.search name + + spec_tuples = specs.map do |spec| + [[spec.name, spec.version, spec.original_platform, spec], :local] + end + + output_query_results spec_tuples + end + + if remote? then + say + say "*** REMOTE GEMS ***" + say + + all = options[:all] + + dep = Gem::Dependency.new name, Gem::Requirement.default + begin + fetcher = Gem::SpecFetcher.fetcher + spec_tuples = fetcher.find_matching dep, all, false + rescue Gem::RemoteFetcher::FetchError => e + raise unless fetcher.warn_legacy e do + require 'rubygems/source_info_cache' + + dep.name = '' if dep.name == // + + specs = Gem::SourceInfoCache.search_with_source dep, false, all + + spec_tuples = specs.map do |spec, source_uri| + [[spec.name, spec.version, spec.original_platform, spec], + source_uri] + end + end + end + + output_query_results spec_tuples + end + end + + private + + ## + # Check if gem +name+ version +version+ is installed. + + def installed?(name, version = Gem::Requirement.default) + dep = Gem::Dependency.new name, version + !Gem.source_index.search(dep).empty? + end + + def output_query_results(spec_tuples) + output = [] + versions = Hash.new { |h,name| h[name] = [] } + + spec_tuples.each do |spec_tuple, source_uri| + versions[spec_tuple.first] << [spec_tuple, source_uri] + end + + versions = versions.sort_by do |(name,_),_| + name.downcase + end + + versions.each do |gem_name, matching_tuples| + matching_tuples = matching_tuples.sort_by do |(name, version,_),_| + version + end.reverse + + seen = {} + + matching_tuples.delete_if do |(name, version,_),_| + if seen[version] then + true + else + seen[version] = true + false + end + end + + entry = gem_name.dup + + if options[:versions] then + versions = matching_tuples.map { |(name, version,_),_| version }.uniq + entry << " (#{versions.join ', '})" + end + + if options[:details] then + detail_tuple = matching_tuples.first + + spec = if detail_tuple.first.length == 4 then + detail_tuple.first.last + else + uri = URI.parse detail_tuple.last + Gem::SpecFetcher.fetcher.fetch_spec detail_tuple.first, uri + end + + entry << "\n" + authors = "Author#{spec.authors.length > 1 ? 's' : ''}: " + authors << spec.authors.join(', ') + entry << format_text(authors, 68, 4) + + if spec.rubyforge_project and not spec.rubyforge_project.empty? then + rubyforge = "Rubyforge: http://rubyforge.org/projects/#{spec.rubyforge_project}" + entry << "\n" << format_text(rubyforge, 68, 4) + end + + if spec.homepage and not spec.homepage.empty? then + entry << "\n" << format_text("Homepage: #{spec.homepage}", 68, 4) + end + + if spec.loaded_from then + if matching_tuples.length == 1 then + loaded_from = File.dirname File.dirname(spec.loaded_from) + entry << "\n" << " Installed at: #{loaded_from}" + else + label = 'Installed at' + matching_tuples.each do |(_,version,_,s),| + loaded_from = File.dirname File.dirname(s.loaded_from) + entry << "\n" << " #{label} (#{version}): #{loaded_from}" + label = ' ' * label.length + end + end + end + + entry << "\n\n" << format_text(spec.summary, 68, 4) + end + output << entry + end + + say output.join(options[:details] ? "\n\n" : "\n") + end + + ## + # Used for wrapping and indenting text + + def format_text(text, wrap, indent=0) + result = [] + work = text.dup + + while work.length > wrap + if work =~ /^(.{0,#{wrap}})[ \n]/o then + result << $1 + work.slice!(0, $&.length) + else + result << work.slice!(0, wrap) + end + end + + result << work if work.length.nonzero? + result.join("\n").gsub(/^/, " " * indent) + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/rdoc_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/rdoc_command.rb new file mode 100644 index 0000000000..f2e677c115 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/rdoc_command.rb @@ -0,0 +1,78 @@ +require 'rubygems/command' +require 'rubygems/version_option' +require 'rubygems/doc_manager' + +module Gem + module Commands + class RdocCommand < Command + include VersionOption + + def initialize + super('rdoc', + 'Generates RDoc for pre-installed gems', + { + :version => Gem::Requirement.default, + :include_rdoc => true, + :include_ri => true, + }) + add_option('--all', + 'Generate RDoc/RI documentation for all', + 'installed gems') do |value, options| + options[:all] = value + end + add_option('--[no-]rdoc', + 'Include RDoc generated documents') do + |value, options| + options[:include_rdoc] = value + end + add_option('--[no-]ri', + 'Include RI generated documents' + ) do |value, options| + options[:include_ri] = value + end + add_version_option + end + + def arguments # :nodoc: + "GEMNAME gem to generate documentation for (unless --all)" + end + + def defaults_str # :nodoc: + "--version '#{Gem::Requirement.default}' --rdoc --ri" + end + + def usage # :nodoc: + "#{program_name} [args]" + end + + def execute + if options[:all] + specs = Gem::SourceIndex.from_installed_gems.collect { |name, spec| + spec + } + else + gem_name = get_one_gem_name + specs = Gem::SourceIndex.from_installed_gems.search( + gem_name, options[:version]) + end + + if specs.empty? + fail "Failed to find gem #{gem_name} to generate RDoc for #{options[:version]}" + end + if options[:include_ri] + specs.each do |spec| + Gem::DocManager.new(spec).generate_ri + end + end + if options[:include_rdoc] + specs.each do |spec| + Gem::DocManager.new(spec).generate_rdoc + end + end + + true + end + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/search_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/search_command.rb new file mode 100644 index 0000000000..96da19c0f7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/search_command.rb @@ -0,0 +1,37 @@ +require 'rubygems/command' +require 'rubygems/commands/query_command' + +module Gem + module Commands + + class SearchCommand < QueryCommand + + def initialize + super( + 'search', + 'Display all gems whose name contains STRING' + ) + remove_option('--name-matches') + end + + def arguments # :nodoc: + "STRING fragment of gem name to search for" + end + + def defaults_str # :nodoc: + "--local --no-details" + end + + def usage # :nodoc: + "#{program_name} [STRING]" + end + + def execute + string = get_one_optional_argument + options[:name] = /#{string}/i + super + end + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/server_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/server_command.rb new file mode 100644 index 0000000000..992ae1c8f8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/server_command.rb @@ -0,0 +1,48 @@ +require 'rubygems/command' +require 'rubygems/server' + +class Gem::Commands::ServerCommand < Gem::Command + + def initialize + super 'server', 'Documentation and gem repository HTTP server', + :port => 8808, :gemdir => Gem.dir, :daemon => false + + add_option '-p', '--port=PORT', Integer, + 'port to listen on' do |port, options| + options[:port] = port + end + + add_option '-d', '--dir=GEMDIR', + 'directory from which to serve gems' do |gemdir, options| + options[:gemdir] = File.expand_path gemdir + end + + add_option '--[no-]daemon', 'run as a daemon' do |daemon, options| + options[:daemon] = daemon + end + end + + def defaults_str # :nodoc: + "--port 8808 --dir #{Gem.dir} --no-daemon" + end + + def description # :nodoc: + <<-EOF +The server command starts up a web server that hosts the RDoc for your +installed gems and can operate as a server for installation of gems on other +machines. + +The cache files for installed gems must exist to use the server as a source +for gem installation. + +To install gems from a running server, use `gem install GEMNAME --source +http://gem_server_host:8808` + EOF + end + + def execute + Gem::Server.run options + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/sources_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/sources_command.rb new file mode 100644 index 0000000000..9aabb77cb1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/sources_command.rb @@ -0,0 +1,152 @@ +require 'fileutils' +require 'rubygems/command' +require 'rubygems/remote_fetcher' +require 'rubygems/source_info_cache' +require 'rubygems/spec_fetcher' + +class Gem::Commands::SourcesCommand < Gem::Command + + def initialize + super 'sources', + 'Manage the sources and cache file RubyGems uses to search for gems' + + add_option '-a', '--add SOURCE_URI', 'Add source' do |value, options| + options[:add] = value + end + + add_option '-l', '--list', 'List sources' do |value, options| + options[:list] = value + end + + add_option '-r', '--remove SOURCE_URI', 'Remove source' do |value, options| + options[:remove] = value + end + + add_option '-c', '--clear-all', + 'Remove all sources (clear the cache)' do |value, options| + options[:clear_all] = value + end + + add_option '-u', '--update', 'Update source cache' do |value, options| + options[:update] = value + end + end + + def defaults_str + '--list' + end + + def execute + options[:list] = !(options[:add] || + options[:clear_all] || + options[:remove] || + options[:update]) + + if options[:clear_all] then + path = Gem::SpecFetcher.fetcher.dir + FileUtils.rm_rf path + + if not File.exist?(path) then + say "*** Removed specs cache ***" + elsif not File.writable?(path) then + say "*** Unable to remove source cache (write protected) ***" + else + say "*** Unable to remove source cache ***" + end + + sic = Gem::SourceInfoCache + remove_cache_file 'user', sic.user_cache_file + remove_cache_file 'latest user', sic.latest_user_cache_file + remove_cache_file 'system', sic.system_cache_file + remove_cache_file 'latest system', sic.latest_system_cache_file + end + + if options[:add] then + source_uri = options[:add] + uri = URI.parse source_uri + + begin + Gem::SpecFetcher.fetcher.load_specs uri, 'specs' + Gem.sources << source_uri + Gem.configuration.write + + say "#{source_uri} added to sources" + rescue URI::Error, ArgumentError + say "#{source_uri} is not a URI" + rescue Gem::RemoteFetcher::FetchError => e + yaml_uri = uri + 'yaml' + gem_repo = Gem::RemoteFetcher.fetcher.fetch_size yaml_uri rescue false + + if e.uri =~ /specs\.#{Regexp.escape Gem.marshal_version}\.gz$/ and + gem_repo then + + alert_warning <<-EOF +RubyGems 1.2+ index not found for: +\t#{source_uri} + +Will cause RubyGems to revert to legacy indexes, degrading performance. + EOF + + say "#{source_uri} added to sources" + else + say "Error fetching #{source_uri}:\n\t#{e.message}" + end + end + end + + if options[:remove] then + source_uri = options[:remove] + + unless Gem.sources.include? source_uri then + say "source #{source_uri} not present in cache" + else + Gem.sources.delete source_uri + Gem.configuration.write + + say "#{source_uri} removed from sources" + end + end + + if options[:update] then + fetcher = Gem::SpecFetcher.fetcher + + if fetcher.legacy_repos.empty? then + Gem.sources.each do |update_uri| + update_uri = URI.parse update_uri + fetcher.load_specs update_uri, 'specs' + fetcher.load_specs update_uri, 'latest_specs' + end + else + Gem::SourceInfoCache.cache true + Gem::SourceInfoCache.cache.flush + end + + say "source cache successfully updated" + end + + if options[:list] then + say "*** CURRENT SOURCES ***" + say + + Gem.sources.each do |source| + say source + end + end + end + + private + + def remove_cache_file(desc, path) + FileUtils.rm_rf path + + if not File.exist?(path) then + say "*** Removed #{desc} source cache ***" + elsif not File.writable?(path) then + say "*** Unable to remove #{desc} source cache (write protected) ***" + else + say "*** Unable to remove #{desc} source cache ***" + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/specification_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/specification_command.rb new file mode 100644 index 0000000000..689f2560c9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/specification_command.rb @@ -0,0 +1,77 @@ +require 'yaml' +require 'rubygems/command' +require 'rubygems/local_remote_options' +require 'rubygems/version_option' +require 'rubygems/source_info_cache' +require 'rubygems/format' + +class Gem::Commands::SpecificationCommand < Gem::Command + + include Gem::LocalRemoteOptions + include Gem::VersionOption + + def initialize + super 'specification', 'Display gem specification (in yaml)', + :domain => :local, :version => Gem::Requirement.default + + add_version_option('examine') + add_platform_option + + add_option('--all', 'Output specifications for all versions of', + 'the gem') do |value, options| + options[:all] = true + end + + add_local_remote_options + end + + def arguments # :nodoc: + "GEMFILE name of gem to show the gemspec for" + end + + def defaults_str # :nodoc: + "--local --version '#{Gem::Requirement.default}'" + end + + def usage # :nodoc: + "#{program_name} [GEMFILE]" + end + + def execute + specs = [] + gem = get_one_gem_name + + if local? then + if File.exist? gem then + specs << Gem::Format.from_file_by_path(gem).spec rescue nil + end + + if specs.empty? then + specs.push(*Gem.source_index.search(/\A#{gem}\z/, options[:version])) + end + end + + if remote? then + dep = Gem::Dependency.new gem, options[:version] + found = Gem::SpecFetcher.fetcher.fetch dep + + specs.push(*found.map { |spec,| spec }) + end + + if specs.empty? then + alert_error "Unknown gem '#{gem}'" + terminate_interaction 1 + end + + output = lambda { |s| say s.to_yaml; say "\n" } + + if options[:all] then + specs.each(&output) + else + spec = specs.sort_by { |s| s.version }.last + output[spec] + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/stale_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/stale_command.rb new file mode 100644 index 0000000000..78cbdcc00a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/stale_command.rb @@ -0,0 +1,27 @@ +require 'rubygems/command' + +class Gem::Commands::StaleCommand < Gem::Command + def initialize + super('stale', 'List gems along with access times') + end + + def usage # :nodoc: + "#{program_name}" + end + + def execute + gem_to_atime = {} + Gem.source_index.each do |name, spec| + Dir["#{spec.full_gem_path}/**/*.*"].each do |file| + next if File.directory?(file) + stat = File.stat(file) + gem_to_atime[name] ||= stat.atime + gem_to_atime[name] = stat.atime if gem_to_atime[name] < stat.atime + end + end + + gem_to_atime.sort_by { |_, atime| atime }.each do |name, atime| + say "#{name} at #{atime.strftime '%c'}" + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/uninstall_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/uninstall_command.rb new file mode 100644 index 0000000000..3d6e2383bc --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/uninstall_command.rb @@ -0,0 +1,73 @@ +require 'rubygems/command' +require 'rubygems/version_option' +require 'rubygems/uninstaller' + +module Gem + module Commands + class UninstallCommand < Command + + include VersionOption + + def initialize + super 'uninstall', 'Uninstall gems from the local repository', + :version => Gem::Requirement.default + + add_option('-a', '--[no-]all', + 'Uninstall all matching versions' + ) do |value, options| + options[:all] = value + end + + add_option('-I', '--[no-]ignore-dependencies', + 'Ignore dependency requirements while', + 'uninstalling') do |value, options| + options[:ignore] = value + end + + add_option('-x', '--[no-]executables', + 'Uninstall applicable executables without', + 'confirmation') do |value, options| + options[:executables] = value + end + + add_option('-i', '--install-dir DIR', + 'Directory to uninstall gem from') do |value, options| + options[:install_dir] = File.expand_path(value) + end + + add_option('-n', '--bindir DIR', + 'Directory to remove binaries from') do |value, options| + options[:bin_dir] = File.expand_path(value) + end + + add_version_option + add_platform_option + end + + def arguments # :nodoc: + "GEMNAME name of gem to uninstall" + end + + def defaults_str # :nodoc: + "--version '#{Gem::Requirement.default}' --no-force " \ + "--install-dir #{Gem.dir}" + end + + def usage # :nodoc: + "#{program_name} GEMNAME [GEMNAME ...]" + end + + def execute + get_all_gem_names.each do |gem_name| + begin + Gem::Uninstaller.new(gem_name, options).uninstall + rescue Gem::GemNotInHomeException => e + spec = e.spec + alert("In order to remove #{spec.name}, please execute:\n" \ + "\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}") + end + end + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/unpack_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/unpack_command.rb new file mode 100644 index 0000000000..d187f8a9ea --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/unpack_command.rb @@ -0,0 +1,95 @@ +require 'fileutils' +require 'rubygems/command' +require 'rubygems/installer' +require 'rubygems/version_option' + +class Gem::Commands::UnpackCommand < Gem::Command + + include Gem::VersionOption + + def initialize + super 'unpack', 'Unpack an installed gem to the current directory', + :version => Gem::Requirement.default, + :target => Dir.pwd + + add_option('--target', 'target directory for unpacking') do |value, options| + options[:target] = value + end + + add_version_option + end + + def arguments # :nodoc: + "GEMNAME name of gem to unpack" + end + + def defaults_str # :nodoc: + "--version '#{Gem::Requirement.default}'" + end + + def usage # :nodoc: + "#{program_name} GEMNAME" + end + + #-- + # TODO: allow, e.g., 'gem unpack rake-0.3.1'. Find a general solution for + # this, so that it works for uninstall as well. (And check other commands + # at the same time.) + def execute + gemname = get_one_gem_name + path = get_path(gemname, options[:version]) + + if path then + basename = File.basename(path).sub(/\.gem$/, '') + target_dir = File.expand_path File.join(options[:target], basename) + FileUtils.mkdir_p target_dir + Gem::Installer.new(path).unpack target_dir + say "Unpacked gem: '#{target_dir}'" + else + alert_error "Gem '#{gemname}' not installed." + end + end + + # Return the full path to the cached gem file matching the given + # name and version requirement. Returns 'nil' if no match. + # + # Example: + # + # get_path('rake', '> 0.4') # -> '/usr/lib/ruby/gems/1.8/cache/rake-0.4.2.gem' + # get_path('rake', '< 0.1') # -> nil + # get_path('rak') # -> nil (exact name required) + #-- + # TODO: This should be refactored so that it's a general service. I don't + # think any of our existing classes are the right place though. Just maybe + # 'Cache'? + # + # TODO: It just uses Gem.dir for now. What's an easy way to get the list of + # source directories? + def get_path(gemname, version_req) + return gemname if gemname =~ /\.gem$/i + + specs = Gem::source_index.search(/\A#{gemname}\z/, version_req) + + selected = specs.sort_by { |s| s.version }.last + + return nil if selected.nil? + + # We expect to find (basename).gem in the 'cache' directory. + # Furthermore, the name match must be exact (ignoring case). + if gemname =~ /^#{selected.name}$/i + filename = selected.full_name + '.gem' + path = nil + + Gem.path.find do |gem_dir| + path = File.join gem_dir, 'cache', filename + File.exist? path + end + + path + else + nil + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/update_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/update_command.rb new file mode 100644 index 0000000000..78baa8ba56 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/update_command.rb @@ -0,0 +1,173 @@ +require 'rubygems/command' +require 'rubygems/command_manager' +require 'rubygems/install_update_options' +require 'rubygems/local_remote_options' +require 'rubygems/spec_fetcher' +require 'rubygems/version_option' +require 'rubygems/commands/install_command' + +class Gem::Commands::UpdateCommand < Gem::Command + + include Gem::InstallUpdateOptions + include Gem::LocalRemoteOptions + include Gem::VersionOption + + def initialize + super 'update', + 'Update the named gems (or all installed gems) in the local repository', + :generate_rdoc => true, + :generate_ri => true, + :force => false, + :test => false + + add_install_update_options + + add_option('--system', + 'Update the RubyGems system software') do |value, options| + options[:system] = value + end + + add_local_remote_options + + add_platform_option + end + + def arguments # :nodoc: + "GEMNAME name of gem to update" + end + + def defaults_str # :nodoc: + "--rdoc --ri --no-force --no-test --install-dir #{Gem.dir}" + end + + def usage # :nodoc: + "#{program_name} GEMNAME [GEMNAME ...]" + end + + def execute + if options[:system] then + say "Updating RubyGems" + + unless options[:args].empty? then + fail "No gem names are allowed with the --system option" + end + + options[:args] = ["rubygems-update"] + else + say "Updating installed gems" + end + + hig = {} # highest installed gems + + Gem.source_index.each do |name, spec| + if hig[spec.name].nil? or hig[spec.name].version < spec.version then + hig[spec.name] = spec + end + end + + gems_to_update = which_to_update hig, options[:args] + + updated = [] + + installer = Gem::DependencyInstaller.new options + + gems_to_update.uniq.sort.each do |name| + next if updated.any? { |spec| spec.name == name } + + say "Updating #{name}" + installer.install name + + installer.installed_gems.each do |spec| + updated << spec + say "Successfully installed #{spec.full_name}" + end + end + + if gems_to_update.include? "rubygems-update" then + latest_ruby_gem = remote_gemspecs.select do |s| + s.name == 'rubygems-update' + end + + latest_ruby_gem = latest_ruby_gem.sort_by { |s| s.version }.last + + say "Updating version of RubyGems to #{latest_ruby_gem.version}" + installed = do_rubygems_update latest_ruby_gem.version + + say "RubyGems system software updated" if installed + else + if updated.empty? then + say "Nothing to update" + else + say "Gems updated: #{updated.map { |spec| spec.name }.join ', '}" + end + end + end + + def do_rubygems_update(version) + args = [] + args.push '--prefix', Gem.prefix unless Gem.prefix.nil? + args << '--no-rdoc' unless options[:generate_rdoc] + args << '--no-ri' unless options[:generate_ri] + args << '--no-format-executable' if options[:no_format_executable] + + update_dir = File.join Gem.dir, 'gems', "rubygems-update-#{version}" + + success = false + + Dir.chdir update_dir do + say "Installing RubyGems #{version}" + setup_cmd = "#{Gem.ruby} setup.rb #{args.join ' '}" + + # Make sure old rubygems isn't loaded + old = ENV["RUBYOPT"] + ENV.delete("RUBYOPT") + system setup_cmd + ENV["RUBYOPT"] = old if old + end + end + + def which_to_update(highest_installed_gems, gem_names) + result = [] + + highest_installed_gems.each do |l_name, l_spec| + next if not gem_names.empty? and + gem_names.all? { |name| /#{name}/ !~ l_spec.name } + + dependency = Gem::Dependency.new l_spec.name, "> #{l_spec.version}" + + begin + fetcher = Gem::SpecFetcher.fetcher + spec_tuples = fetcher.find_matching dependency + rescue Gem::RemoteFetcher::FetchError => e + raise unless fetcher.warn_legacy e do + require 'rubygems/source_info_cache' + + dependency.name = '' if dependency.name == // + + specs = Gem::SourceInfoCache.search_with_source dependency + + spec_tuples = specs.map do |spec, source_uri| + [[spec.name, spec.version, spec.original_platform], source_uri] + end + end + end + + matching_gems = spec_tuples.select do |(name, version, platform),| + name == l_name and Gem::Platform.match platform + end + + highest_remote_gem = matching_gems.sort_by do |(name, version),| + version + end.last + + if highest_remote_gem and + l_spec.version < highest_remote_gem.first[1] then + result << l_name + end + end + + result + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/which_command.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/which_command.rb new file mode 100644 index 0000000000..b42244ce7d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/commands/which_command.rb @@ -0,0 +1,86 @@ +require 'rubygems/command' +require 'rubygems/gem_path_searcher' + +class Gem::Commands::WhichCommand < Gem::Command + + EXT = %w[.rb .rbw .so .dll] # HACK + + def initialize + super 'which', 'Find the location of a library', + :search_gems_first => false, :show_all => false + + add_option '-a', '--[no-]all', 'show all matching files' do |show_all, options| + options[:show_all] = show_all + end + + add_option '-g', '--[no-]gems-first', + 'search gems before non-gems' do |gems_first, options| + options[:search_gems_first] = gems_first + end + end + + def arguments # :nodoc: + "FILE name of file to find" + end + + def defaults_str # :nodoc: + "--no-gems-first --no-all" + end + + def usage # :nodoc: + "#{program_name} FILE [FILE ...]" + end + + def execute + searcher = Gem::GemPathSearcher.new + + options[:args].each do |arg| + dirs = $LOAD_PATH + spec = searcher.find arg + + if spec then + if options[:search_gems_first] then + dirs = gem_paths(spec) + $LOAD_PATH + else + dirs = $LOAD_PATH + gem_paths(spec) + end + + say "(checking gem #{spec.full_name} for #{arg})" if + Gem.configuration.verbose + end + + paths = find_paths arg, dirs + + if paths.empty? then + say "Can't find #{arg}" + else + say paths + end + end + end + + def find_paths(package_name, dirs) + result = [] + + dirs.each do |dir| + EXT.each do |ext| + full_path = File.join dir, "#{package_name}#{ext}" + if File.exist? full_path then + result << full_path + return result unless options[:show_all] + end + end + end + + result + end + + def gem_paths(spec) + spec.require_paths.collect { |d| File.join spec.full_gem_path, d } + end + + def usage # :nodoc: + "#{program_name} FILE [...]" + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/config_file.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/config_file.rb new file mode 100644 index 0000000000..8cea513790 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/config_file.rb @@ -0,0 +1,241 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'yaml' +require 'rubygems' + +# Store the gem command options specified in the configuration file. The +# config file object acts much like a hash. + +class Gem::ConfigFile + + DEFAULT_BACKTRACE = false + DEFAULT_BENCHMARK = false + DEFAULT_BULK_THRESHOLD = 1000 + DEFAULT_VERBOSITY = true + DEFAULT_UPDATE_SOURCES = true + + system_config_path = + begin + require 'Win32API' + + CSIDL_COMMON_APPDATA = 0x0023 + path = 0.chr * 260 + SHGetFolderPath = Win32API.new 'shell32', 'SHGetFolderPath', 'LLLLP', 'L' + SHGetFolderPath.call 0, CSIDL_COMMON_APPDATA, 0, 1, path + + path.strip + rescue LoadError + '/etc' + end + + SYSTEM_WIDE_CONFIG_FILE = File.join system_config_path, 'gemrc' + + # List of arguments supplied to the config file object. + attr_reader :args + + # True if we print backtraces on errors. + attr_writer :backtrace + + # True if we are benchmarking this run. + attr_accessor :benchmark + + # Bulk threshold value. If the number of missing gems are above + # this threshold value, then a bulk download technique is used. + attr_accessor :bulk_threshold + + # Verbose level of output: + # * false -- No output + # * true -- Normal output + # * :loud -- Extra output + attr_accessor :verbose + + # True if we want to update the SourceInfoCache every time, false otherwise + attr_accessor :update_sources + + # Create the config file object. +args+ is the list of arguments + # from the command line. + # + # The following command line options are handled early here rather + # than later at the time most command options are processed. + # + # * --config-file and --config-file==NAME -- Obviously these need + # to be handled by the ConfigFile object to ensure we get the + # right config file. + # + # * --backtrace -- Backtrace needs to be turned on early so that + # errors before normal option parsing can be properly handled. + # + # * --debug -- Enable Ruby level debug messages. Handled early + # for the same reason as --backtrace. + # + def initialize(arg_list) + @config_file_name = nil + need_config_file_name = false + + arg_list = arg_list.map do |arg| + if need_config_file_name then + @config_file_name = arg + need_config_file_name = false + nil + elsif arg =~ /^--config-file=(.*)/ then + @config_file_name = $1 + nil + elsif arg =~ /^--config-file$/ then + need_config_file_name = true + nil + else + arg + end + end.compact + + @backtrace = DEFAULT_BACKTRACE + @benchmark = DEFAULT_BENCHMARK + @bulk_threshold = DEFAULT_BULK_THRESHOLD + @verbose = DEFAULT_VERBOSITY + @update_sources = DEFAULT_UPDATE_SOURCES + + @hash = load_file(SYSTEM_WIDE_CONFIG_FILE) + @hash.merge!(load_file(config_file_name.dup.untaint)) + + # HACK these override command-line args, which is bad + @backtrace = @hash[:backtrace] if @hash.key? :backtrace + @benchmark = @hash[:benchmark] if @hash.key? :benchmark + @bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold + Gem.sources.replace @hash[:sources] if @hash.key? :sources + @verbose = @hash[:verbose] if @hash.key? :verbose + @update_sources = @hash[:update_sources] if @hash.key? :update_sources + + handle_arguments arg_list + end + + def load_file(filename) + begin + YAML.load(File.read(filename)) if filename and File.exist?(filename) + rescue ArgumentError + warn "Failed to load #{config_file_name}" + rescue Errno::EACCES + warn "Failed to load #{config_file_name} due to permissions problem." + end or {} + end + + # True if the backtrace option has been specified, or debug is on. + def backtrace + @backtrace or $DEBUG + end + + # The name of the configuration file. + def config_file_name + @config_file_name || Gem.config_file + end + + # Delegates to @hash + def each(&block) + hash = @hash.dup + hash.delete :update_sources + hash.delete :verbose + hash.delete :benchmark + hash.delete :backtrace + hash.delete :bulk_threshold + + yield :update_sources, @update_sources + yield :verbose, @verbose + yield :benchmark, @benchmark + yield :backtrace, @backtrace + yield :bulk_threshold, @bulk_threshold + + yield 'config_file_name', @config_file_name if @config_file_name + + hash.each(&block) + end + + # Handle the command arguments. + def handle_arguments(arg_list) + @args = [] + + arg_list.each do |arg| + case arg + when /^--(backtrace|traceback)$/ then + @backtrace = true + when /^--bench(mark)?$/ then + @benchmark = true + when /^--debug$/ then + $DEBUG = true + else + @args << arg + end + end + end + + # Really verbose mode gives you extra output. + def really_verbose + case verbose + when true, false, nil then false + else true + end + end + + # to_yaml only overwrites things you can't override on the command line. + def to_yaml # :nodoc: + yaml_hash = {} + yaml_hash[:backtrace] = @hash.key?(:backtrace) ? @hash[:backtrace] : + DEFAULT_BACKTRACE + yaml_hash[:benchmark] = @hash.key?(:benchmark) ? @hash[:benchmark] : + DEFAULT_BENCHMARK + yaml_hash[:bulk_threshold] = @hash.key?(:bulk_threshold) ? + @hash[:bulk_threshold] : DEFAULT_BULK_THRESHOLD + yaml_hash[:sources] = Gem.sources + yaml_hash[:update_sources] = @hash.key?(:update_sources) ? + @hash[:update_sources] : DEFAULT_UPDATE_SOURCES + yaml_hash[:verbose] = @hash.key?(:verbose) ? @hash[:verbose] : + DEFAULT_VERBOSITY + + keys = yaml_hash.keys.map { |key| key.to_s } + keys << 'debug' + re = Regexp.union(*keys) + + @hash.each do |key, value| + key = key.to_s + next if key =~ re + yaml_hash[key.to_s] = value + end + + yaml_hash.to_yaml + end + + # Writes out this config file, replacing its source. + def write + File.open config_file_name, 'w' do |fp| + fp.write self.to_yaml + end + end + + # Return the configuration information for +key+. + def [](key) + @hash[key.to_s] + end + + # Set configuration option +key+ to +value+. + def []=(key, value) + @hash[key.to_s] = value + end + + def ==(other) # :nodoc: + self.class === other and + @backtrace == other.backtrace and + @benchmark == other.benchmark and + @bulk_threshold == other.bulk_threshold and + @verbose == other.verbose and + @update_sources == other.update_sources and + @hash == other.hash + end + + protected + + attr_reader :hash + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/custom_require.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/custom_require.rb new file mode 100644 index 0000000000..90e6b53959 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/custom_require.rb @@ -0,0 +1,38 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems' + +module Kernel + alias gem_original_require require # :nodoc: + + # + # We replace Ruby's require with our own, which is capable of + # loading gems on demand. + # + # When you call require 'x', this is what happens: + # * If the file can be loaded from the existing Ruby loadpath, it + # is. + # * Otherwise, installed gems are searched for a file that matches. + # If it's found in gem 'y', that gem is activated (added to the + # loadpath). + # + # The normal require functionality of returning false if + # that file has already been loaded is preserved. + # + def require(path) # :nodoc: + gem_original_require path + rescue LoadError => load_error + if load_error.message =~ /#{Regexp.escape path}\z/ and + spec = Gem.searcher.find(path) then + Gem.activate(spec.name, "= #{spec.version}") + gem_original_require path + else + raise load_error + end + end +end # module Kernel + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/defaults.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/defaults.rb new file mode 100644 index 0000000000..914b9f777f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/defaults.rb @@ -0,0 +1,53 @@ +module Gem + + # An Array of the default sources that come with RubyGems. + def self.default_sources + %w[http://gems.rubyforge.org/] + end + + # Default home directory path to be used if an alternate value is not + # specified in the environment. + def self.default_dir + if defined? RUBY_FRAMEWORK_VERSION then + File.join File.dirname(ConfigMap[:sitedir]), 'Gems', + ConfigMap[:ruby_version] + elsif defined? RUBY_ENGINE then + File.join ConfigMap[:libdir], RUBY_ENGINE, 'gems', + ConfigMap[:ruby_version] + else + File.join ConfigMap[:libdir], 'ruby', 'gems', ConfigMap[:ruby_version] + end + end + + # Default gem path. + def self.default_path + default_dir + end + + # Deduce Ruby's --program-prefix and --program-suffix from its install name. + def self.default_exec_format + baseruby = ConfigMap[:BASERUBY] || 'ruby' + ConfigMap[:RUBY_INSTALL_NAME].sub(baseruby, '%s') rescue '%s' + end + + # The default directory for binaries + def self.default_bindir + if defined? RUBY_FRAMEWORK_VERSION then # mac framework support + '/usr/bin' + else # generic install + ConfigMap[:bindir] + end + end + + # The default system-wide source info cache directory. + def self.default_system_source_cache_dir + File.join Gem.dir, 'source_cache' + end + + # The default user-specific source info cache directory. + def self.default_user_source_cache_dir + File.join Gem.user_home, '.gem', 'source_cache' + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/dependency.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/dependency.rb new file mode 100644 index 0000000000..7b9904df55 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/dependency.rb @@ -0,0 +1,119 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems' + +## +# The Dependency class holds a Gem name and a Gem::Requirement + +class Gem::Dependency + + ## + # Valid dependency types. + #-- + # When this list is updated, be sure to change + # Gem::Specification::CURRENT_SPECIFICATION_VERSION as well. + + TYPES = [ + :development, + :runtime, + ] + + ## + # Dependency name or regular expression. + + attr_accessor :name + + ## + # Dependency type. + + attr_reader :type + + ## + # Dependent versions. + + attr_writer :version_requirements + + ## + # Orders dependencies by name only. + + def <=>(other) + [@name] <=> [other.name] + end + + ## + # Constructs a dependency with +name+ and +requirements+. + + def initialize(name, version_requirements, type=:runtime) + @name = name + + unless TYPES.include? type + raise ArgumentError, "Valid types are #{TYPES.inspect}, not #{@type.inspect}" + end + + @type = type + + @version_requirements = Gem::Requirement.create version_requirements + @version_requirement = nil # Avoid warnings. + end + + def version_requirements + normalize if defined? @version_requirement and @version_requirement + @version_requirements + end + + def requirement_list + version_requirements.as_list + end + + alias requirements_list requirement_list + + def normalize + ver = @version_requirement.instance_eval { @version } + @version_requirements = Gem::Requirement.new([ver]) + @version_requirement = nil + end + + def to_s # :nodoc: + "#{name} (#{version_requirements}, #{@type || :runtime})" + end + + def ==(other) # :nodoc: + self.class === other && + self.name == other.name && + self.type == other.type && + self.version_requirements == other.version_requirements + end + + ## + # Uses this dependency as a pattern to compare to the dependency +other+. + # This dependency will match if the name matches the other's name, and other + # has only an equal version requirement that satisfies this dependency. + + def =~(other) + return false unless self.class === other + + pattern = @name + pattern = /\A#{@name}\Z/ unless Regexp === pattern + + return false unless pattern =~ other.name + + reqs = other.version_requirements.requirements + + return false unless reqs.length == 1 + return false unless reqs.first.first == '=' + + version = reqs.first.last + + version_requirements.satisfied_by? version + end + + def hash # :nodoc: + name.hash + type.hash + version_requirements.hash + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/dependency_installer.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/dependency_installer.rb new file mode 100644 index 0000000000..8406844e79 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/dependency_installer.rb @@ -0,0 +1,254 @@ +require 'rubygems' +require 'rubygems/dependency_list' +require 'rubygems/installer' +require 'rubygems/spec_fetcher' +require 'rubygems/user_interaction' + +## +# Installs a gem along with all its dependencies from local and remote gems. + +class Gem::DependencyInstaller + + include Gem::UserInteraction + + attr_reader :gems_to_install + attr_reader :installed_gems + + DEFAULT_OPTIONS = { + :env_shebang => false, + :domain => :both, # HACK dup + :force => false, + :format_executable => false, # HACK dup + :ignore_dependencies => false, + :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low? + :wrappers => true + } + + ## + # Creates a new installer instance. + # + # Options are: + # :cache_dir:: Alternate repository path to store .gem files in. + # :domain:: :local, :remote, or :both. :local only searches gems in the + # current directory. :remote searches only gems in Gem::sources. + # :both searches both. + # :env_shebang:: See Gem::Installer::new. + # :force:: See Gem::Installer#install. + # :format_executable:: See Gem::Installer#initialize. + # :ignore_dependencies:: Don't install any dependencies. + # :install_dir:: See Gem::Installer#install. + # :security_policy:: See Gem::Installer::new and Gem::Security. + # :wrappers:: See Gem::Installer::new + + def initialize(options = {}) + options = DEFAULT_OPTIONS.merge options + + @bin_dir = options[:bin_dir] + @development = options[:development] + @domain = options[:domain] + @env_shebang = options[:env_shebang] + @force = options[:force] + @format_executable = options[:format_executable] + @ignore_dependencies = options[:ignore_dependencies] + @security_policy = options[:security_policy] + @wrappers = options[:wrappers] + + @installed_gems = [] + + @install_dir = options[:install_dir] || Gem.dir + @cache_dir = options[:cache_dir] || @install_dir + + if options[:install_dir] then + spec_dir = File.join @install_dir, 'specifications' + @source_index = Gem::SourceIndex.from_gems_in spec_dir + else + @source_index = Gem.source_index + end + end + + ## + # Returns a list of pairs of gemspecs and source_uris that match + # Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources) + # sources. Gems are sorted with newer gems prefered over older gems, and + # local gems preferred over remote gems. + + def find_gems_with_sources(dep) + gems_and_sources = [] + + if @domain == :both or @domain == :local then + Dir[File.join(Dir.pwd, "#{dep.name}-[0-9]*.gem")].each do |gem_file| + spec = Gem::Format.from_file_by_path(gem_file).spec + gems_and_sources << [spec, gem_file] if spec.name == dep.name + end + end + + if @domain == :both or @domain == :remote then + begin + requirements = dep.version_requirements.requirements.map do |req, ver| + req + end + + all = requirements.length > 1 || + (requirements.first != ">=" and requirements.first != ">") + + found = Gem::SpecFetcher.fetcher.fetch dep, all + gems_and_sources.push(*found) + + rescue Gem::RemoteFetcher::FetchError => e + if Gem.configuration.really_verbose then + say "Error fetching remote data:\t\t#{e.message}" + say "Falling back to local-only install" + end + @domain = :local + end + end + + gems_and_sources.sort_by do |gem, source| + [gem, source =~ /^http:\/\// ? 0 : 1] # local gems win + end + end + + ## + # Gathers all dependencies necessary for the installation from local and + # remote sources unless the ignore_dependencies was given. + + def gather_dependencies + specs = @specs_and_sources.map { |spec,_| spec } + + dependency_list = Gem::DependencyList.new + dependency_list.add(*specs) + + unless @ignore_dependencies then + to_do = specs.dup + seen = {} + + until to_do.empty? do + spec = to_do.shift + next if spec.nil? or seen[spec.name] + seen[spec.name] = true + + deps = spec.runtime_dependencies + deps |= spec.development_dependencies if @development + + deps.each do |dep| + results = find_gems_with_sources(dep).reverse + + results.reject! do |dep_spec,| + to_do.push dep_spec + + @source_index.any? do |_, installed_spec| + dep.name == installed_spec.name and + dep.version_requirements.satisfied_by? installed_spec.version + end + end + + results.each do |dep_spec, source_uri| + next if seen[dep_spec.name] + @specs_and_sources << [dep_spec, source_uri] + dependency_list.add dep_spec + end + end + end + end + + @gems_to_install = dependency_list.dependency_order.reverse + end + + ## + # Finds a spec and the source_uri it came from for gem +gem_name+ and + # +version+. Returns an Array of specs and sources required for + # installation of the gem. + + def find_spec_by_name_and_version gem_name, version = Gem::Requirement.default + spec_and_source = nil + + glob = if File::ALT_SEPARATOR then + gem_name.gsub File::ALT_SEPARATOR, File::SEPARATOR + else + gem_name + end + + local_gems = Dir["#{glob}*"].sort.reverse + + unless local_gems.empty? then + local_gems.each do |gem_file| + next unless gem_file =~ /gem$/ + begin + spec = Gem::Format.from_file_by_path(gem_file).spec + spec_and_source = [spec, gem_file] + break + rescue SystemCallError, Gem::Package::FormatError + end + end + end + + if spec_and_source.nil? then + dep = Gem::Dependency.new gem_name, version + spec_and_sources = find_gems_with_sources(dep).reverse + + spec_and_source = spec_and_sources.find { |spec, source| + Gem::Platform.match spec.platform + } + end + + if spec_and_source.nil? then + raise Gem::GemNotFoundException, + "could not find gem #{gem_name} locally or in a repository" + end + + @specs_and_sources = [spec_and_source] + end + + ## + # Installs the gem and all its dependencies. Returns an Array of installed + # gems specifications. + + def install dep_or_name, version = Gem::Requirement.default + if String === dep_or_name then + find_spec_by_name_and_version dep_or_name, version + else + @specs_and_sources = [find_gems_with_sources(dep_or_name).last] + end + + @installed_gems = [] + + gather_dependencies + + @gems_to_install.each do |spec| + last = spec == @gems_to_install.last + # HACK is this test for full_name acceptable? + next if @source_index.any? { |n,_| n == spec.full_name } and not last + + # TODO: make this sorta_verbose so other users can benefit from it + say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose + + _, source_uri = @specs_and_sources.assoc spec + begin + local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri, + @cache_dir + rescue Gem::RemoteFetcher::FetchError + next if @force + raise + end + + inst = Gem::Installer.new local_gem_path, + :env_shebang => @env_shebang, + :force => @force, + :format_executable => @format_executable, + :ignore_dependencies => @ignore_dependencies, + :install_dir => @install_dir, + :security_policy => @security_policy, + :wrappers => @wrappers, + :bin_dir => @bin_dir, + :development => @development + + spec = inst.install + + @installed_gems << spec + end + + @installed_gems + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/dependency_list.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/dependency_list.rb new file mode 100644 index 0000000000..a129743914 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/dependency_list.rb @@ -0,0 +1,165 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'tsort' + +class Gem::DependencyList + + include TSort + + def self.from_source_index(src_index) + deps = new + + src_index.each do |full_name, spec| + deps.add spec + end + + deps + end + + def initialize + @specs = [] + end + + # Adds +gemspecs+ to the dependency list. + def add(*gemspecs) + @specs.push(*gemspecs) + end + + # Return a list of the specifications in the dependency list, + # sorted in order so that no spec in the list depends on a gem + # earlier in the list. + # + # This is useful when removing gems from a set of installed gems. + # By removing them in the returned order, you don't get into as + # many dependency issues. + # + # If there are circular dependencies (yuck!), then gems will be + # returned in order until only the circular dependents and anything + # they reference are left. Then arbitrary gemspecs will be returned + # until the circular dependency is broken, after which gems will be + # returned in dependency order again. + def dependency_order + sorted = strongly_connected_components.flatten + + result = [] + seen = {} + + sorted.each do |spec| + if index = seen[spec.name] then + if result[index].version < spec.version then + result[index] = spec + end + else + seen[spec.name] = result.length + result << spec + end + end + + result.reverse + end + + def find_name(full_name) + @specs.find { |spec| spec.full_name == full_name } + end + + # Are all the dependencies in the list satisfied? + def ok? + @specs.all? do |spec| + spec.runtime_dependencies.all? do |dep| + @specs.find { |s| s.satisfies_requirement? dep } + end + end + end + + # Is is ok to remove a gem from the dependency list? + # + # If removing the gemspec creates breaks a currently ok dependency, + # then it is NOT ok to remove the gem. + def ok_to_remove?(full_name) + gem_to_remove = find_name full_name + + siblings = @specs.find_all { |s| + s.name == gem_to_remove.name && + s.full_name != gem_to_remove.full_name + } + + deps = [] + + @specs.each do |spec| + spec.dependencies.each do |dep| + deps << dep if gem_to_remove.satisfies_requirement?(dep) + end + end + + deps.all? { |dep| + siblings.any? { |s| + s.satisfies_requirement? dep + } + } + end + + def remove_by_name(full_name) + @specs.delete_if { |spec| spec.full_name == full_name } + end + + # Return a hash of predecessors. result[spec] is an + # Array of gemspecs that have a dependency satisfied by the named + # spec. + def spec_predecessors + result = Hash.new { |h,k| h[k] = [] } + + specs = @specs.sort.reverse + + specs.each do |spec| + specs.each do |other| + next if spec == other + + other.dependencies.each do |dep| + if spec.satisfies_requirement? dep then + result[spec] << other + end + end + end + end + + result + end + + def tsort_each_node(&block) + @specs.each(&block) + end + + def tsort_each_child(node, &block) + specs = @specs.sort.reverse + + node.dependencies.each do |dep| + specs.each do |spec| + if spec.satisfies_requirement? dep then + begin + yield spec + rescue TSort::Cyclic + end + break + end + end + end + end + + private + + # Count the number of gemspecs in the list +specs+ that are not in + # +ignored+. + def active_count(specs, ignored) + result = 0 + specs.each do |spec| + result += 1 unless ignored[spec.full_name] + end + result + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/digest/digest_adapter.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/digest/digest_adapter.rb new file mode 100644 index 0000000000..d5a00b059d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/digest/digest_adapter.rb @@ -0,0 +1,40 @@ +#!/usr/bin/env ruby +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +module Gem + + # There is an incompatibility between the way Ruby 1.8.5 and 1.8.6 + # handles digests. This DigestAdapter will take a pre-1.8.6 digest + # and adapt it to the 1.8.6 API. + # + # Note that only the digest and hexdigest methods are adapted, + # since these are the only functions used by Gems. + # + class DigestAdapter + + # Initialize a digest adapter. + def initialize(digest_class) + @digest_class = digest_class + end + + # Return a new digester. Since we are only implementing the stateless + # methods, we will return ourself as the instance. + def new + self + end + + # Return the digest of +string+ as a hex string. + def hexdigest(string) + @digest_class.new(string).hexdigest + end + + # Return the digest of +string+ as a binary string. + def digest(string) + @digest_class.new(string).digest + end + end +end \ No newline at end of file diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/digest/md5.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/digest/md5.rb new file mode 100644 index 0000000000..f924579c08 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/digest/md5.rb @@ -0,0 +1,23 @@ +#!/usr/bin/env ruby +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'digest/md5' + +# :stopdoc: +module Gem + if RUBY_VERSION >= '1.8.6' + MD5 = Digest::MD5 + else + require 'rubygems/digest/digest_adapter' + MD5 = DigestAdapter.new(Digest::MD5) + def MD5.md5(string) + self.hexdigest(string) + end + end +end +# :startdoc: + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/digest/sha1.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/digest/sha1.rb new file mode 100644 index 0000000000..2a6245dcd9 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/digest/sha1.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'digest/sha1' + +module Gem + if RUBY_VERSION >= '1.8.6' + SHA1 = Digest::SHA1 + else + require 'rubygems/digest/digest_adapter' + SHA1 = DigestAdapter.new(Digest::SHA1) + end +end \ No newline at end of file diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/digest/sha2.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/digest/sha2.rb new file mode 100644 index 0000000000..7bef16aed2 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/digest/sha2.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'digest/sha2' + +module Gem + if RUBY_VERSION >= '1.8.6' + SHA256 = Digest::SHA256 + else + require 'rubygems/digest/digest_adapter' + SHA256 = DigestAdapter.new(Digest::SHA256) + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/doc_manager.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/doc_manager.rb new file mode 100644 index 0000000000..88d7964d85 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/doc_manager.rb @@ -0,0 +1,167 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'fileutils' + +module Gem + + class DocManager + + include UserInteraction + + # Create a document manager for the given gem spec. + # + # spec:: The Gem::Specification object representing the gem. + # rdoc_args:: Optional arguments for RDoc (template etc.) as a String. + # + def initialize(spec, rdoc_args="") + @spec = spec + @doc_dir = File.join(spec.installation_path, "doc", spec.full_name) + @rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split + end + + # Is the RDoc documentation installed? + def rdoc_installed? + return File.exist?(File.join(@doc_dir, "rdoc")) + end + + # Generate the RI documents for this gem spec. + # + # Note that if both RI and RDoc documents are generated from the + # same process, the RI docs should be done first (a likely bug in + # RDoc will cause RI docs generation to fail if run after RDoc). + def generate_ri + if @spec.has_rdoc then + load_rdoc + install_ri # RDoc bug, ri goes first + end + + FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir) + end + + # Generate the RDoc documents for this gem spec. + # + # Note that if both RI and RDoc documents are generated from the + # same process, the RI docs should be done first (a likely bug in + # RDoc will cause RI docs generation to fail if run after RDoc). + def generate_rdoc + if @spec.has_rdoc then + load_rdoc + install_rdoc + end + + FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir) + end + + # Load the RDoc documentation generator library. + def load_rdoc + if File.exist?(@doc_dir) && !File.writable?(@doc_dir) then + raise Gem::FilePermissionError.new(@doc_dir) + end + + FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir) + + begin + gem 'rdoc' + rescue Gem::LoadError + # use built-in RDoc + end + + begin + require 'rdoc/rdoc' + rescue LoadError => e + raise Gem::DocumentError, + "ERROR: RDoc documentation generator not installed!" + end + end + + def install_rdoc + rdoc_dir = File.join @doc_dir, 'rdoc' + + FileUtils.rm_rf rdoc_dir + + say "Installing RDoc documentation for #{@spec.full_name}..." + run_rdoc '--op', rdoc_dir + end + + def install_ri + ri_dir = File.join @doc_dir, 'ri' + + FileUtils.rm_rf ri_dir + + say "Installing ri documentation for #{@spec.full_name}..." + run_rdoc '--ri', '--op', ri_dir + end + + def run_rdoc(*args) + args << @spec.rdoc_options + args << DocManager.configured_args + args << '--quiet' + args << @spec.require_paths.clone + args << @spec.extra_rdoc_files + args = args.flatten.map do |arg| arg.to_s end + + r = RDoc::RDoc.new + + old_pwd = Dir.pwd + Dir.chdir(@spec.full_gem_path) + begin + r.document args + rescue Errno::EACCES => e + dirname = File.dirname e.message.split("-")[1].strip + raise Gem::FilePermissionError.new(dirname) + rescue RuntimeError => ex + alert_error "While generating documentation for #{@spec.full_name}" + ui.errs.puts "... MESSAGE: #{ex}" + ui.errs.puts "... RDOC args: #{args.join(' ')}" + ui.errs.puts "\t#{ex.backtrace.join "\n\t"}" if + Gem.configuration.backtrace + ui.errs.puts "(continuing with the rest of the installation)" + ensure + Dir.chdir(old_pwd) + end + end + + def uninstall_doc + raise Gem::FilePermissionError.new(@spec.installation_path) unless + File.writable? @spec.installation_path + + original_name = [ + @spec.name, @spec.version, @spec.original_platform].join '-' + + doc_dir = File.join @spec.installation_path, 'doc', @spec.full_name + unless File.directory? doc_dir then + doc_dir = File.join @spec.installation_path, 'doc', original_name + end + + FileUtils.rm_rf doc_dir + + ri_dir = File.join @spec.installation_path, 'ri', @spec.full_name + + unless File.directory? ri_dir then + ri_dir = File.join @spec.installation_path, 'ri', original_name + end + + FileUtils.rm_rf ri_dir + end + + class << self + def configured_args + @configured_args ||= [] + end + + def configured_args=(args) + case args + when Array + @configured_args = args + when String + @configured_args = args.split + end + end + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/exceptions.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/exceptions.rb new file mode 100644 index 0000000000..c37507c62a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/exceptions.rb @@ -0,0 +1,84 @@ +require 'rubygems' + +## +# Base exception class for RubyGems. All exception raised by RubyGems are a +# subclass of this one. +class Gem::Exception < RuntimeError; end + +class Gem::CommandLineError < Gem::Exception; end + +class Gem::DependencyError < Gem::Exception; end + +class Gem::DependencyRemovalException < Gem::Exception; end + +## +# Raised when attempting to uninstall a gem that isn't in GEM_HOME. + +class Gem::GemNotInHomeException < Gem::Exception + attr_accessor :spec +end + +class Gem::DocumentError < Gem::Exception; end + +## +# Potentially raised when a specification is validated. +class Gem::EndOfYAMLException < Gem::Exception; end + +## +# Signals that a file permission error is preventing the user from +# installing in the requested directories. +class Gem::FilePermissionError < Gem::Exception + def initialize(path) + super("You don't have write permissions into the #{path} directory.") + end +end + +## +# Used to raise parsing and loading errors +class Gem::FormatException < Gem::Exception + attr_accessor :file_path +end + +class Gem::GemNotFoundException < Gem::Exception; end + +class Gem::InstallError < Gem::Exception; end + +## +# Potentially raised when a specification is validated. +class Gem::InvalidSpecificationException < Gem::Exception; end + +class Gem::OperationNotSupportedError < Gem::Exception; end + +## +# Signals that a remote operation cannot be conducted, probably due to not +# being connected (or just not finding host). +#-- +# TODO: create a method that tests connection to the preferred gems server. +# All code dealing with remote operations will want this. Failure in that +# method should raise this error. +class Gem::RemoteError < Gem::Exception; end + +class Gem::RemoteInstallationCancelled < Gem::Exception; end + +class Gem::RemoteInstallationSkipped < Gem::Exception; end + +## +# Represents an error communicating via HTTP. +class Gem::RemoteSourceException < Gem::Exception; end + +class Gem::VerificationError < Gem::Exception; end + +## +# Raised to indicate that a system exit should occur with the specified +# exit_code + +class Gem::SystemExitException < SystemExit + attr_accessor :exit_code + + def initialize(exit_code) + @exit_code = exit_code + + super "Exiting RubyGems with exit_code #{exit_code}" + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext.rb new file mode 100644 index 0000000000..97ee762a4a --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext.rb @@ -0,0 +1,18 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems' + +## +# Classes for building C extensions live here. + +module Gem::Ext; end + +require 'rubygems/ext/builder' +require 'rubygems/ext/configure_builder' +require 'rubygems/ext/ext_conf_builder' +require 'rubygems/ext/rake_builder' + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext/builder.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext/builder.rb new file mode 100644 index 0000000000..576951a566 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext/builder.rb @@ -0,0 +1,56 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems/ext' + +class Gem::Ext::Builder + + def self.class_name + name =~ /Ext::(.*)Builder/ + $1.downcase + end + + def self.make(dest_path, results) + unless File.exist? 'Makefile' then + raise Gem::InstallError, "Makefile not found:\n\n#{results.join "\n"}" + end + + mf = File.read('Makefile') + mf = mf.gsub(/^RUBYARCHDIR\s*=\s*\$[^$]*/, "RUBYARCHDIR = #{dest_path}") + mf = mf.gsub(/^RUBYLIBDIR\s*=\s*\$[^$]*/, "RUBYLIBDIR = #{dest_path}") + + File.open('Makefile', 'wb') {|f| f.print mf} + + make_program = ENV['make'] + unless make_program then + make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make' + end + + ['', ' install'].each do |target| + cmd = "#{make_program}#{target}" + results << cmd + results << `#{cmd} #{redirector}` + + raise Gem::InstallError, "make#{target} failed:\n\n#{results}" unless + $?.exitstatus.zero? + end + end + + def self.redirector + '2>&1' + end + + def self.run(command, results) + results << command + results << `#{command} #{redirector}` + + unless $?.exitstatus.zero? then + raise Gem::InstallError, "#{class_name} failed:\n\n#{results.join "\n"}" + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext/configure_builder.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext/configure_builder.rb new file mode 100644 index 0000000000..1cde6915a7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext/configure_builder.rb @@ -0,0 +1,24 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems/ext/builder' + +class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder + + def self.build(extension, directory, dest_path, results) + unless File.exist?('Makefile') then + cmd = "sh ./configure --prefix=#{dest_path}" + + run cmd, results + end + + make dest_path, results + + results + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext/ext_conf_builder.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext/ext_conf_builder.rb new file mode 100644 index 0000000000..cbe0e80821 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext/ext_conf_builder.rb @@ -0,0 +1,23 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems/ext/builder' + +class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder + + def self.build(extension, directory, dest_path, results) + cmd = "#{Gem.ruby} #{File.basename extension}" + cmd << " #{ARGV.join ' '}" unless ARGV.empty? + + run cmd, results + + make dest_path, results + + results + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext/rake_builder.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext/rake_builder.rb new file mode 100644 index 0000000000..3772f6a00f --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/ext/rake_builder.rb @@ -0,0 +1,27 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems/ext/builder' + +class Gem::Ext::RakeBuilder < Gem::Ext::Builder + + def self.build(extension, directory, dest_path, results) + if File.basename(extension) =~ /mkrf_conf/i then + cmd = "#{Gem.ruby} #{File.basename extension}" + cmd << " #{ARGV.join " "}" unless ARGV.empty? + run cmd, results + end + + cmd = ENV['rake'] || 'rake' + cmd << " RUBYARCHDIR=#{dest_path} RUBYLIBDIR=#{dest_path}" + + run cmd, results + + results + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/format.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/format.rb new file mode 100644 index 0000000000..7dc127d5f4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/format.rb @@ -0,0 +1,87 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'fileutils' + +require 'rubygems/package' + +module Gem + + ## + # The format class knows the guts of the RubyGem .gem file format + # and provides the capability to read gem files + # + class Format + attr_accessor :spec, :file_entries, :gem_path + extend Gem::UserInteraction + + ## + # Constructs an instance of a Format object, representing the gem's + # data structure. + # + # gem:: [String] The file name of the gem + # + def initialize(gem_path) + @gem_path = gem_path + end + + ## + # Reads the named gem file and returns a Format object, representing + # the data from the gem file + # + # file_path:: [String] Path to the gem file + # + def self.from_file_by_path(file_path, security_policy = nil) + format = nil + + unless File.exist?(file_path) + raise Gem::Exception, "Cannot load gem at [#{file_path}] in #{Dir.pwd}" + end + + # check for old version gem + if File.read(file_path, 20).include?("MD5SUM =") + require 'rubygems/old_format' + + format = OldFormat.from_file_by_path(file_path) + else + open file_path, Gem.binary_mode do |io| + format = from_io io, file_path, security_policy + end + end + + return format + end + + ## + # Reads a gem from an io stream and returns a Format object, representing + # the data from the gem file + # + # io:: [IO] Stream from which to read the gem + # + def self.from_io(io, gem_path="(io)", security_policy = nil) + format = new gem_path + + Package.open io, 'r', security_policy do |pkg| + format.spec = pkg.metadata + format.file_entries = [] + + pkg.each do |entry| + size = entry.header.size + mode = entry.header.mode + + format.file_entries << [{ + "size" => size, "mode" => mode, "path" => entry.full_name, + }, + entry.read + ] + end + end + + format + end + + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_commands.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_commands.rb new file mode 100644 index 0000000000..f4a394cb94 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_commands.rb @@ -0,0 +1,273 @@ +#!/usr/bin/env ruby +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems' +require 'rubygems/command' + +module Gem + + class CommandLineError < Gem::Exception; end + module Commands; end # This is where Commands will be placed in the namespace + + #################################################################### + # The following mixin methods aid in the retrieving of information + # from the command line. + # + module CommandAids + + # Get the single gem name from the command line. Fail if there is + # no gem name or if there is more than one gem name given. + def get_one_gem_name + args = options[:args] + if args.nil? or args.empty? + fail Gem::CommandLineError, + "Please specify a gem name on the command line (e.g. gem build GEMNAME)" + end + if args.size > 1 + fail Gem::CommandLineError, + "Too many gem names (#{args.join(', ')}); please specify only one" + end + args.first + end + + # Get all gem names from the command line. + def get_all_gem_names + args = options[:args] + if args.nil? or args.empty? + raise Gem::CommandLineError, + "Please specify at least one gem name (e.g. gem build GEMNAME)" + end + gem_names = args.select { |arg| arg !~ /^-/ } + end + + # Get a single optional argument from the command line. If more + # than one argument is given, return only the first. Return nil if + # none are given. + def get_one_optional_argument + args = options[:args] || [] + args.first + end + + # True if +long+ begins with the characters from +short+. + def begins?(long, short) + return false if short.nil? + long[0, short.length] == short + end + end + + #################################################################### + # Mixin methods for handling the local/remote command line options. + # + module LocalRemoteOptions + + # Add the local/remote options to the command line parser. + def add_local_remote_options + add_option('-l', '--local', + 'Restrict operations to the LOCAL domain' + ) do |value, options| + options[:domain] = :local + end + + add_option('-r', '--remote', + 'Restrict operations to the REMOTE domain') do + |value, options| + options[:domain] = :remote + end + + add_option('-b', '--both', + 'Allow LOCAL and REMOTE operations') do + |value, options| + options[:domain] = :both + end + end + + # Is local fetching enabled? + def local? + options[:domain] == :local || options[:domain] == :both + end + + # Is remote fetching enabled? + def remote? + options[:domain] == :remote || options[:domain] == :both + end + end + + #################################################################### + # Mixin methods and OptionParser options specific to the gem install + # command. + # + module InstallUpdateOptions + + # Add the install/update options to the option parser. + def add_install_update_options + add_option('-i', '--install-dir DIR', + 'Gem repository directory to get installed', + 'gems.') do |value, options| + options[:install_dir] = File.expand_path(value) + end + + add_option('-d', '--[no-]rdoc', + 'Generate RDoc documentation for the gem on', + 'install') do |value, options| + options[:generate_rdoc] = value + end + + add_option('--[no-]ri', + 'Generate RI documentation for the gem on', + 'install') do |value, options| + options[:generate_ri] = value + end + + add_option('-E', '--env-shebang', + "Rewrite the shebang line on installed", + "scripts to use /usr/bin/env") do |value, options| + options[:env_shebang] = value + end + + add_option('-f', '--[no-]force', + 'Force gem to install, bypassing dependency', + 'checks') do |value, options| + options[:force] = value + end + + add_option('-t', '--[no-]test', + 'Run unit tests prior to installation') do + |value, options| + options[:test] = value + end + + add_option('-w', '--[no-]wrappers', + 'Use bin wrappers for executables', + 'Not available on dosish platforms') do + |value, options| + options[:wrappers] = value + end + + add_option('-P', '--trust-policy POLICY', + 'Specify gem trust policy.') do + |value, options| + options[:security_policy] = value + end + + add_option('--ignore-dependencies', + 'Do not install any required dependent gems') do + |value, options| + options[:ignore_dependencies] = value + end + + add_option('-y', '--include-dependencies', + 'Unconditionally install the required', + 'dependent gems') do |value, options| + options[:include_dependencies] = value + end + end + + # Default options for the gem install command. + def install_update_defaults_str + '--rdoc --no-force --no-test --wrappers' + end + end + + #################################################################### + # Mixin methods for the version command. + # + module VersionOption + + # Add the options to the option parser. + def add_version_option(taskname, *wrap) + add_option('-v', '--version VERSION', + "Specify version of gem to #{taskname}", *wrap) do + |value, options| + options[:version] = value + end + end + + end + def self.load_commands(*command_names) + command_names.each{|name| + require "rubygems/commands/#{name}_command" + } + end +end + +###################################################################### +# Documentation Constants +# +module Gem + + HELP = %{ + RubyGems is a sophisticated package manager for Ruby. This is a + basic help message containing pointers to more information. + + Usage: + gem -h/--help + gem -v/--version + gem command [arguments...] [options...] + + Examples: + gem install rake + gem list --local + gem build package.gemspec + gem help install + + Further help: + gem help commands list all 'gem' commands + gem help examples show some examples of usage + gem help show help on COMMAND + (e.g. 'gem help install') + Further information: + http://rubygems.rubyforge.org + }.gsub(/^ /, "") + + EXAMPLES = %{ + Some examples of 'gem' usage. + + * Install 'rake', either from local directory or remote server: + + gem install rake + + * Install 'rake', only from remote server: + + gem install rake --remote + + * Install 'rake' from remote server, and run unit tests, + and generate RDocs: + + gem install --remote rake --test --rdoc --ri + + * Install 'rake', but only version 0.3.1, even if dependencies + are not met, and into a specific directory: + + gem install rake --version 0.3.1 --force --install-dir $HOME/.gems + + * List local gems whose name begins with 'D': + + gem list D + + * List local and remote gems whose name contains 'log': + + gem search log --both + + * List only remote gems whose name contains 'log': + + gem search log --remote + + * Uninstall 'rake': + + gem uninstall rake + + * Create a gem: + + See http://rubygems.rubyforge.org/wiki/wiki.pl?CreateAGemInTenMinutes + + * See information about RubyGems: + + gem environment + + }.gsub(/^ /, "") + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_open_uri.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_open_uri.rb new file mode 100644 index 0000000000..6e35413b37 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_open_uri.rb @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +if RUBY_VERSION < "1.9" + require 'rubygems/open-uri' +else + require 'open-uri' +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_openssl.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_openssl.rb new file mode 100644 index 0000000000..1456f2d7ce --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_openssl.rb @@ -0,0 +1,83 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +# Some system might not have OpenSSL installed, therefore the core +# library file openssl might not be available. We localize testing +# for the presence of OpenSSL in this file. + +module Gem + class << self + # Is SSL (used by the signing commands) available on this + # platform? + def ssl_available? + require 'rubygems/gem_openssl' + @ssl_available + end + + # Set the value of the ssl_available flag. + attr_writer :ssl_available + + # Ensure that SSL is available. Throw an exception if it is not. + def ensure_ssl_available + unless ssl_available? + fail Gem::Exception, "SSL is not installed on this system" + end + end + end +end + +begin + require 'openssl' + + # Reference a constant defined in the .rb portion of ssl (just to + # make sure that part is loaded too). + + dummy = OpenSSL::Digest::SHA1 + + Gem.ssl_available = true + + class OpenSSL::X509::Certificate # :nodoc: + # Check the validity of this certificate. + def check_validity(issuer_cert = nil, time = Time.now) + ret = if @not_before && @not_before > time + [false, :expired, "not valid before '#@not_before'"] + elsif @not_after && @not_after < time + [false, :expired, "not valid after '#@not_after'"] + elsif issuer_cert && !verify(issuer_cert.public_key) + [false, :issuer, "#{issuer_cert.subject} is not issuer"] + else + [true, :ok, 'Valid certificate'] + end + + # return hash + { :is_valid => ret[0], :error => ret[1], :desc => ret[2] } + end + end + +rescue LoadError, StandardError + Gem.ssl_available = false +end + +module Gem::SSL + + # We make our own versions of the constants here. This allows us + # to reference the constants, even though some systems might not + # have SSL installed in the Ruby core package. + # + # These constants are only used during load time. At runtime, any + # method that makes a direct reference to SSL software must be + # protected with a Gem.ensure_ssl_available call. + # + if Gem.ssl_available? then + PKEY_RSA = OpenSSL::PKey::RSA + DIGEST_SHA1 = OpenSSL::Digest::SHA1 + else + PKEY_RSA = :rsa + DIGEST_SHA1 = :sha1 + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_path_searcher.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_path_searcher.rb new file mode 100644 index 0000000000..dadad66289 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_path_searcher.rb @@ -0,0 +1,84 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems' + +# +# GemPathSearcher has the capability to find loadable files inside +# gems. It generates data up front to speed up searches later. +# +class Gem::GemPathSearcher + + # + # Initialise the data we need to make searches later. + # + def initialize + # We want a record of all the installed gemspecs, in the order + # we wish to examine them. + @gemspecs = init_gemspecs + # Map gem spec to glob of full require_path directories. + # Preparing this information may speed up searches later. + @lib_dirs = {} + @gemspecs.each do |spec| + @lib_dirs[spec.object_id] = lib_dirs_for(spec) + end + end + + # + # Look in all the installed gems until a matching _path_ is found. + # Return the _gemspec_ of the gem where it was found. If no match + # is found, return nil. + # + # The gems are searched in alphabetical order, and in reverse + # version order. + # + # For example: + # + # find('log4r') # -> (log4r-1.1 spec) + # find('log4r.rb') # -> (log4r-1.1 spec) + # find('rake/rdoctask') # -> (rake-0.4.12 spec) + # find('foobarbaz') # -> nil + # + # Matching paths can have various suffixes ('.rb', '.so', and + # others), which may or may not already be attached to _file_. + # This method doesn't care about the full filename that matches; + # only that there is a match. + # + def find(path) + @gemspecs.each do |spec| + return spec if matching_file(spec, path) + end + nil + end + + private + + # Attempts to find a matching path using the require_paths of the + # given _spec_. + # + # Some of the intermediate results are cached in @lib_dirs for + # speed. + def matching_file(spec, path) # :doc: + glob = File.join @lib_dirs[spec.object_id], "#{path}#{Gem.suffix_pattern}" + return true unless Dir[glob].select { |f| File.file?(f.untaint) }.empty? + end + + # Return a list of all installed gemspecs, sorted by alphabetical + # order and in reverse version order. + def init_gemspecs + Gem.source_index.map { |_, spec| spec }.sort { |a,b| + (a.name <=> b.name).nonzero? || (b.version <=> a.version) + } + end + + # Returns library directories glob for a gemspec. For example, + # '/usr/local/lib/ruby/gems/1.8/gems/foobar-1.0/{lib,ext}' + def lib_dirs_for(spec) + "#{spec.full_gem_path}/{#{spec.require_paths.join(',')}}" + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_runner.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_runner.rb new file mode 100644 index 0000000000..5f91398b5b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/gem_runner.rb @@ -0,0 +1,58 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems/command_manager' +require 'rubygems/config_file' +require 'rubygems/doc_manager' + +module Gem + + #################################################################### + # Run an instance of the gem program. + # + class GemRunner + + def initialize(options={}) + @command_manager_class = options[:command_manager] || Gem::CommandManager + @config_file_class = options[:config_file] || Gem::ConfigFile + @doc_manager_class = options[:doc_manager] || Gem::DocManager + end + + # Run the gem command with the following arguments. + def run(args) + start_time = Time.now + do_configuration(args) + cmd = @command_manager_class.instance + cmd.command_names.each do |command_name| + config_args = Gem.configuration[command_name] + config_args = case config_args + when String + config_args.split ' ' + else + Array(config_args) + end + Command.add_specific_extra_args command_name, config_args + end + cmd.run(Gem.configuration.args) + end_time = Time.now + if Gem.configuration.benchmark + printf "\nExecution time: %0.2f seconds.\n", end_time-start_time + puts "Press Enter to finish" + STDIN.gets + end + end + + private + + def do_configuration(args) + Gem.configuration = @config_file_class.new(args) + Gem.use_paths(Gem.configuration[:gemhome], Gem.configuration[:gempath]) + Gem::Command.extra_args = Gem.configuration[:gem] + @doc_manager_class.configured_args = Gem.configuration[:rdoc] + end + + end # class +end # module diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/indexer.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/indexer.rb new file mode 100644 index 0000000000..839e736668 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/indexer.rb @@ -0,0 +1,361 @@ +require 'fileutils' +require 'tmpdir' +require 'zlib' + +require 'rubygems' +require 'rubygems/format' + +begin + require 'builder/xchar' +rescue LoadError +end + +## +# Top level class for building the gem repository index. + +class Gem::Indexer + + include Gem::UserInteraction + + ## + # Index install location + + attr_reader :dest_directory + + ## + # Index build directory + + attr_reader :directory + + ## + # Create an indexer that will index the gems in +directory+. + + def initialize(directory) + unless ''.respond_to? :to_xs then + fail "Gem::Indexer requires that the XML Builder library be installed:" \ + "\n\tgem install builder" + end + + @dest_directory = directory + @directory = File.join Dir.tmpdir, "gem_generate_index_#{$$}" + + marshal_name = "Marshal.#{Gem.marshal_version}" + + @master_index = File.join @directory, 'yaml' + @marshal_index = File.join @directory, marshal_name + + @quick_dir = File.join @directory, 'quick' + + @quick_marshal_dir = File.join @quick_dir, marshal_name + + @quick_index = File.join @quick_dir, 'index' + @latest_index = File.join @quick_dir, 'latest_index' + + @specs_index = File.join @directory, "specs.#{Gem.marshal_version}" + @latest_specs_index = File.join @directory, + "latest_specs.#{Gem.marshal_version}" + + files = [ + @specs_index, + "#{@specs_index}.gz", + @latest_specs_index, + "#{@latest_specs_index}.gz", + @quick_dir, + @master_index, + "#{@master_index}.Z", + @marshal_index, + "#{@marshal_index}.Z", + ] + + @files = files.map do |path| + path.sub @directory, '' + end + end + + ## + # Abbreviate the spec for downloading. Abbreviated specs are only used for + # searching, downloading and related activities and do not need deployment + # specific information (e.g. list of files). So we abbreviate the spec, + # making it much smaller for quicker downloads. + + def abbreviate(spec) + spec.files = [] + spec.test_files = [] + spec.rdoc_options = [] + spec.extra_rdoc_files = [] + spec.cert_chain = [] + spec + end + + ## + # Build various indicies + + def build_indicies(index) + progress = ui.progress_reporter index.size, + "Generating quick index gemspecs for #{index.size} gems", + "Complete" + + index.each do |original_name, spec| + spec_file_name = "#{original_name}.gemspec.rz" + yaml_name = File.join @quick_dir, spec_file_name + marshal_name = File.join @quick_marshal_dir, spec_file_name + + yaml_zipped = Gem.deflate spec.to_yaml + open yaml_name, 'wb' do |io| io.write yaml_zipped end + + marshal_zipped = Gem.deflate Marshal.dump(spec) + open marshal_name, 'wb' do |io| io.write marshal_zipped end + + progress.updated original_name + end + + progress.done + + say "Generating specs index" + + open @specs_index, 'wb' do |io| + specs = index.sort.map do |_, spec| + platform = spec.original_platform + platform = Gem::Platform::RUBY if platform.nil? + [spec.name, spec.version, platform] + end + + specs = compact_specs specs + + Marshal.dump specs, io + end + + say "Generating latest specs index" + + open @latest_specs_index, 'wb' do |io| + specs = index.latest_specs.sort.map do |spec| + [spec.name, spec.version, spec.original_platform] + end + + specs = compact_specs specs + + Marshal.dump specs, io + end + + say "Generating quick index" + + quick_index = File.join @quick_dir, 'index' + open quick_index, 'wb' do |io| + io.puts index.sort.map { |_, spec| spec.original_name } + end + + say "Generating latest index" + + latest_index = File.join @quick_dir, 'latest_index' + open latest_index, 'wb' do |io| + io.puts index.latest_specs.sort.map { |spec| spec.original_name } + end + + say "Generating Marshal master index" + + open @marshal_index, 'wb' do |io| + io.write index.dump + end + + progress = ui.progress_reporter index.size, + "Generating YAML master index for #{index.size} gems (this may take a while)", + "Complete" + + open @master_index, 'wb' do |io| + io.puts "--- !ruby/object:#{index.class}" + io.puts "gems:" + + gems = index.sort_by { |name, gemspec| gemspec.sort_obj } + gems.each do |original_name, gemspec| + yaml = gemspec.to_yaml.gsub(/^/, ' ') + yaml = yaml.sub(/\A ---/, '') # there's a needed extra ' ' here + io.print " #{original_name}:" + io.puts yaml + + progress.updated original_name + end + end + + progress.done + + say "Compressing indicies" + # use gzip for future files. + + compress quick_index, 'rz' + paranoid quick_index, 'rz' + + compress latest_index, 'rz' + paranoid latest_index, 'rz' + + compress @marshal_index, 'Z' + paranoid @marshal_index, 'Z' + + compress @master_index, 'Z' + paranoid @master_index, 'Z' + + gzip @specs_index + gzip @latest_specs_index + end + + ## + # Collect specifications from .gem files from the gem directory. + + def collect_specs + index = Gem::SourceIndex.new + + progress = ui.progress_reporter gem_file_list.size, + "Loading #{gem_file_list.size} gems from #{@dest_directory}", + "Loaded all gems" + + gem_file_list.each do |gemfile| + if File.size(gemfile.to_s) == 0 then + alert_warning "Skipping zero-length gem: #{gemfile}" + next + end + + begin + spec = Gem::Format.from_file_by_path(gemfile).spec + + unless gemfile =~ /\/#{Regexp.escape spec.original_name}.*\.gem\z/i then + alert_warning "Skipping misnamed gem: #{gemfile} => #{spec.full_name} (#{spec.original_name})" + next + end + + abbreviate spec + sanitize spec + + index.gems[spec.original_name] = spec + + progress.updated spec.original_name + + rescue SignalException => e + alert_error "Received signal, exiting" + raise + rescue Exception => e + alert_error "Unable to process #{gemfile}\n#{e.message} (#{e.class})\n\t#{e.backtrace.join "\n\t"}" + end + end + + progress.done + + index + end + + ## + # Compacts Marshal output for the specs index data source by using identical + # objects as much as possible. + + def compact_specs(specs) + names = {} + versions = {} + platforms = {} + + specs.map do |(name, version, platform)| + names[name] = name unless names.include? name + versions[version] = version unless versions.include? version + platforms[platform] = platform unless platforms.include? platform + + [names[name], versions[version], platforms[platform]] + end + end + + ## + # Compress +filename+ with +extension+. + + def compress(filename, extension) + data = Gem.read_binary filename + + zipped = Gem.deflate data + + open "#{filename}.#{extension}", 'wb' do |io| + io.write zipped + end + end + + ## + # List of gem file names to index. + + def gem_file_list + Dir.glob(File.join(@dest_directory, "gems", "*.gem")) + end + + ## + # Builds and installs indexicies. + + def generate_index + FileUtils.rm_rf @directory + FileUtils.mkdir_p @directory, :mode => 0700 + FileUtils.mkdir_p @quick_marshal_dir + + index = collect_specs + build_indicies index + install_indicies + rescue SignalException + ensure + FileUtils.rm_rf @directory + end + + ## + # Zlib::GzipWriter wrapper that gzips +filename+ on disk. + + def gzip(filename) + Zlib::GzipWriter.open "#{filename}.gz" do |io| + io.write Gem.read_binary(filename) + end + end + + ## + # Install generated indicies into the destination directory. + + def install_indicies + verbose = Gem.configuration.really_verbose + + say "Moving index into production dir #{@dest_directory}" if verbose + + @files.each do |file| + src_name = File.join @directory, file + dst_name = File.join @dest_directory, file + + FileUtils.rm_rf dst_name, :verbose => verbose + FileUtils.mv src_name, @dest_directory, :verbose => verbose, + :force => true + end + end + + ## + # Ensure +path+ and path with +extension+ are identical. + + def paranoid(path, extension) + data = Gem.read_binary path + compressed_data = Gem.read_binary "#{path}.#{extension}" + + unless data == Gem.inflate(compressed_data) then + raise "Compressed file #{compressed_path} does not match uncompressed file #{path}" + end + end + + ## + # Sanitize the descriptive fields in the spec. Sometimes non-ASCII + # characters will garble the site index. Non-ASCII characters will + # be replaced by their XML entity equivalent. + + def sanitize(spec) + spec.summary = sanitize_string(spec.summary) + spec.description = sanitize_string(spec.description) + spec.post_install_message = sanitize_string(spec.post_install_message) + spec.authors = spec.authors.collect { |a| sanitize_string(a) } + spec + end + + ## + # Sanitize a single string. + + def sanitize_string(string) + # HACK the #to_s is in here because RSpec has an Array of Arrays of + # Strings for authors. Need a way to disallow bad values on gempsec + # generation. (Probably won't happen.) + string ? string.to_s.to_xs : string + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/install_update_options.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/install_update_options.rb new file mode 100644 index 0000000000..5202c105db --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/install_update_options.rb @@ -0,0 +1,106 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems' +require 'rubygems/security' + +## +# Mixin methods for install and update options for Gem::Commands +module Gem::InstallUpdateOptions + + # Add the install/update options to the option parser. + def add_install_update_options + OptionParser.accept Gem::Security::Policy do |value| + value = Gem::Security::Policies[value] + raise OptionParser::InvalidArgument, value if value.nil? + value + end + + add_option(:"Install/Update", '-i', '--install-dir DIR', + 'Gem repository directory to get installed', + 'gems') do |value, options| + options[:install_dir] = File.expand_path(value) + end + + add_option(:"Install/Update", '-n', '--bindir DIR', + 'Directory where binary files are', + 'located') do |value, options| + options[:bin_dir] = File.expand_path(value) + end + + add_option(:"Install/Update", '-d', '--[no-]rdoc', + 'Generate RDoc documentation for the gem on', + 'install') do |value, options| + options[:generate_rdoc] = value + end + + add_option(:"Install/Update", '--[no-]ri', + 'Generate RI documentation for the gem on', + 'install') do |value, options| + options[:generate_ri] = value + end + + add_option(:"Install/Update", '-E', '--[no-]env-shebang', + "Rewrite the shebang line on installed", + "scripts to use /usr/bin/env") do |value, options| + options[:env_shebang] = value + end + + add_option(:"Install/Update", '-f', '--[no-]force', + 'Force gem to install, bypassing dependency', + 'checks') do |value, options| + options[:force] = value + end + + add_option(:"Install/Update", '-t', '--[no-]test', + 'Run unit tests prior to installation') do |value, options| + options[:test] = value + end + + add_option(:"Install/Update", '-w', '--[no-]wrappers', + 'Use bin wrappers for executables', + 'Not available on dosish platforms') do |value, options| + options[:wrappers] = value + end + + add_option(:"Install/Update", '-P', '--trust-policy POLICY', + Gem::Security::Policy, + 'Specify gem trust policy') do |value, options| + options[:security_policy] = value + end + + add_option(:"Install/Update", '--ignore-dependencies', + 'Do not install any required dependent gems') do |value, options| + options[:ignore_dependencies] = value + end + + add_option(:"Install/Update", '-y', '--include-dependencies', + 'Unconditionally install the required', + 'dependent gems') do |value, options| + options[:include_dependencies] = value + end + + add_option(:"Install/Update", '--[no-]format-executable', + 'Make installed executable names match ruby.', + 'If ruby is ruby18, foo_exec will be', + 'foo_exec18') do |value, options| + options[:format_executable] = value + end + + add_option(:"Install/Update", "--development", + "Install any additional development", + "dependencies") do |value, options| + options[:development] = true + end + end + + # Default options for the gem install command. + def install_update_defaults_str + '--rdoc --no-force --no-test --wrappers' + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/installer.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/installer.rb new file mode 100644 index 0000000000..ae699a90a0 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/installer.rb @@ -0,0 +1,502 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'fileutils' +require 'pathname' +require 'rbconfig' + +require 'rubygems/format' +require 'rubygems/ext' +require 'rubygems/require_paths_builder' + +## +# The installer class processes RubyGem .gem files and installs the +# files contained in the .gem into the Gem.path. +# +# Gem::Installer does the work of putting files in all the right places on the +# filesystem including unpacking the gem into its gem dir, installing the +# gemspec in the specifications dir, storing the cached gem in the cache dir, +# and installing either wrappers or symlinks for executables. +class Gem::Installer + + ## + # Raised when there is an error while building extensions. + # + class ExtensionBuildError < Gem::InstallError; end + + include Gem::UserInteraction + + include Gem::RequirePathsBuilder + + class << self + + attr_writer :exec_format + + # Defaults to use Ruby's program prefix and suffix. + def exec_format + @exec_format ||= Gem.default_exec_format + end + + end + + ## + # Constructs an Installer instance that will install the gem located at + # +gem+. +options+ is a Hash with the following keys: + # + # :env_shebang:: Use /usr/bin/env in bin wrappers. + # :force:: Overrides all version checks and security policy checks, except + # for a signed-gems-only policy. + # :ignore_dependencies:: Don't raise if a dependency is missing. + # :install_dir:: The directory to install the gem into. + # :format_executable:: Format the executable the same as the ruby executable. + # If your ruby is ruby18, foo_exec will be installed as + # foo_exec18. + # :security_policy:: Use the specified security policy. See Gem::Security + # :wrappers:: Install wrappers if true, symlinks if false. + + def initialize(gem, options={}) + @gem = gem + + options = { + :force => false, + :install_dir => Gem.dir, + :exec_format => false, + :env_shebang => false, + :bin_dir => nil + }.merge options + + @env_shebang = options[:env_shebang] + @force = options[:force] + gem_home = options[:install_dir] + @gem_home = Pathname.new(gem_home).expand_path + @ignore_dependencies = options[:ignore_dependencies] + @format_executable = options[:format_executable] + @security_policy = options[:security_policy] + @wrappers = options[:wrappers] + @bin_dir = options[:bin_dir] + @development = options[:development] + + begin + @format = Gem::Format.from_file_by_path @gem, @security_policy + rescue Gem::Package::FormatError + raise Gem::InstallError, "invalid gem format for #{@gem}" + end + + @spec = @format.spec + + @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint + end + + ## + # Installs the gem and returns a loaded Gem::Specification for the installed + # gem. + # + # The gem will be installed with the following structure: + # + # @gem_home/ + # cache/.gem #=> a cached copy of the installed gem + # gems//... #=> extracted files + # specifications/.gemspec #=> the Gem::Specification + + def install + # If we're forcing the install then disable security unless the security + # policy says that we only install singed gems. + @security_policy = nil if @force and @security_policy and + not @security_policy.only_signed + + unless @force then + if rrv = @spec.required_ruby_version then + unless rrv.satisfied_by? Gem.ruby_version then + raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}" + end + end + + if rrgv = @spec.required_rubygems_version then + unless rrgv.satisfied_by? Gem::Version.new(Gem::RubyGemsVersion) then + raise Gem::InstallError, + "#{@spec.name} requires RubyGems version #{rrgv}" + end + end + + unless @ignore_dependencies then + deps = @spec.runtime_dependencies + deps |= @spec.development_dependencies if @development + + deps.each do |dep_gem| + ensure_dependency @spec, dep_gem + end + end + end + + FileUtils.mkdir_p @gem_home unless File.directory? @gem_home + raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home + + Gem.ensure_gem_subdirectories @gem_home + + FileUtils.mkdir_p @gem_dir + + extract_files + generate_bin + build_extensions + write_spec + + write_require_paths_file_if_needed + + # HACK remove? Isn't this done in multiple places? + cached_gem = File.join @gem_home, "cache", @gem.split(/\//).pop + unless File.exist? cached_gem then + FileUtils.cp @gem, File.join(@gem_home, "cache") + end + + say @spec.post_install_message unless @spec.post_install_message.nil? + + @spec.loaded_from = File.join(@gem_home, 'specifications', + "#{@spec.full_name}.gemspec") + + Gem.source_index.add_spec @spec + + return @spec + rescue Zlib::GzipFile::Error + raise Gem::InstallError, "gzip error installing #{@gem}" + end + + ## + # Ensure that the dependency is satisfied by the current installation of + # gem. If it is not an exception is raised. + # + # spec :: Gem::Specification + # dependency :: Gem::Dependency + + def ensure_dependency(spec, dependency) + unless installation_satisfies_dependency? dependency then + raise Gem::InstallError, "#{spec.name} requires #{dependency}" + end + + true + end + + ## + # True if the gems in Gem.source_index satisfy +dependency+. + + def installation_satisfies_dependency?(dependency) + Gem.source_index.find_name(dependency.name, dependency.version_requirements).size > 0 + end + + ## + # Unpacks the gem into the given directory. + + def unpack(directory) + @gem_dir = directory + @format = Gem::Format.from_file_by_path @gem, @security_policy + extract_files + end + + ## + # Writes the .gemspec specification (in Ruby) to the supplied + # spec_path. + # + # spec:: [Gem::Specification] The Gem specification to output + # spec_path:: [String] The location (path) to write the gemspec to + + def write_spec + rubycode = @spec.to_ruby + + file_name = File.join @gem_home, 'specifications', + "#{@spec.full_name}.gemspec" + file_name.untaint + + File.open(file_name, "w") do |file| + file.puts rubycode + end + end + + ## + # Creates windows .bat files for easy running of commands + + def generate_windows_script(bindir, filename) + if Gem.win_platform? then + script_name = filename + ".bat" + script_path = File.join bindir, File.basename(script_name) + File.open script_path, 'w' do |file| + file.puts windows_stub_script(bindir, filename) + end + + say script_path if Gem.configuration.really_verbose + end + end + + def generate_bin + return if @spec.executables.nil? or @spec.executables.empty? + + # If the user has asked for the gem to be installed in a directory that is + # the system gem directory, then use the system bin directory, else create + # (or use) a new bin dir under the gem_home. + bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home) + + Dir.mkdir bindir unless File.exist? bindir + raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir + + @spec.executables.each do |filename| + filename.untaint + bin_path = File.expand_path File.join(@gem_dir, @spec.bindir, filename) + mode = File.stat(bin_path).mode | 0111 + File.chmod mode, bin_path + + if @wrappers then + generate_bin_script filename, bindir + else + generate_bin_symlink filename, bindir + end + end + end + + ## + # Creates the scripts to run the applications in the gem. + #-- + # The Windows script is generated in addition to the regular one due to a + # bug or misfeature in the Windows shell's pipe. See + # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379 + + def generate_bin_script(filename, bindir) + bin_script_path = File.join bindir, formatted_program_filename(filename) + + exec_path = File.join @gem_dir, @spec.bindir, filename + + # HACK some gems don't have #! in their executables, restore 2008/06 + #if File.read(exec_path, 2) == '#!' then + FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers + + File.open bin_script_path, 'w', 0755 do |file| + file.print app_script_text(filename) + end + + say bin_script_path if Gem.configuration.really_verbose + + generate_windows_script bindir, filename + #else + # FileUtils.rm_f bin_script_path + # FileUtils.cp exec_path, bin_script_path, + # :verbose => Gem.configuration.really_verbose + #end + end + + ## + # Creates the symlinks to run the applications in the gem. Moves + # the symlink if the gem being installed has a newer version. + + def generate_bin_symlink(filename, bindir) + if Gem.win_platform? then + alert_warning "Unable to use symlinks on Windows, installing wrapper" + generate_bin_script filename, bindir + return + end + + src = File.join @gem_dir, 'bin', filename + dst = File.join bindir, formatted_program_filename(filename) + + if File.exist? dst then + if File.symlink? dst then + link = File.readlink(dst).split File::SEPARATOR + cur_version = Gem::Version.create(link[-3].sub(/^.*-/, '')) + return if @spec.version < cur_version + end + File.unlink dst + end + + FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose + end + + ## + # Generates a #! line for +bin_file_name+'s wrapper copying arguments if + # necessary. + + def shebang(bin_file_name) + if @env_shebang then + "#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name] + else + path = File.join @gem_dir, @spec.bindir, bin_file_name + + File.open(path, "rb") do |file| + first_line = file.gets + if first_line =~ /^#!/ then + # Preserve extra words on shebang line, like "-w". Thanks RPA. + shebang = first_line.sub(/\A\#!.*?ruby\S*/, "#!#{Gem.ruby}") + else + # Create a plain shebang line. + shebang = "#!#{Gem.ruby}" + end + + shebang.strip # Avoid nasty ^M issues. + end + end + end + + ## + # Return the text for an application file. + + def app_script_text(bin_file_name) + <<-TEXT +#{shebang bin_file_name} +# +# This file was generated by RubyGems. +# +# The application '#{@spec.name}' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = "#{Gem::Requirement.default}" + +if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then + version = $1 + ARGV.shift +end + +gem '#{@spec.name}', version +load '#{bin_file_name}' +TEXT + end + + ## + # return the stub script text used to launch the true ruby script + + def windows_stub_script(bindir, bin_file_name) + <<-TEXT +@ECHO OFF +IF NOT "%~f0" == "~f0" GOTO :WinNT +@"#{File.basename(Gem.ruby)}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9 +GOTO :EOF +:WinNT +@"#{File.basename(Gem.ruby)}" "%~dpn0" %* +TEXT + end + + ## + # Builds extensions. Valid types of extensions are extconf.rb files, + # configure scripts and rakefiles or mkrf_conf files. + + def build_extensions + return if @spec.extensions.empty? + say "Building native extensions. This could take a while..." + start_dir = Dir.pwd + dest_path = File.join @gem_dir, @spec.require_paths.first + ran_rake = false # only run rake once + + @spec.extensions.each do |extension| + break if ran_rake + results = [] + + builder = case extension + when /extconf/ then + Gem::Ext::ExtConfBuilder + when /configure/ then + Gem::Ext::ConfigureBuilder + when /rakefile/i, /mkrf_conf/i then + ran_rake = true + Gem::Ext::RakeBuilder + else + results = ["No builder for extension '#{extension}'"] + nil + end + + begin + Dir.chdir File.join(@gem_dir, File.dirname(extension)) + results = builder.build(extension, @gem_dir, dest_path, results) + + say results.join("\n") if Gem.configuration.really_verbose + + rescue => ex + results = results.join "\n" + + File.open('gem_make.out', 'wb') { |f| f.puts results } + + message = <<-EOF +ERROR: Failed to build gem native extension. + +#{results} + +Gem files will remain installed in #{@gem_dir} for inspection. +Results logged to #{File.join(Dir.pwd, 'gem_make.out')} + EOF + + raise ExtensionBuildError, message + ensure + Dir.chdir start_dir + end + end + end + + ## + # Reads the file index and extracts each file into the gem directory. + # + # Ensures that files can't be installed outside the gem directory. + + def extract_files + expand_and_validate_gem_dir + + raise ArgumentError, "format required to extract from" if @format.nil? + + @format.file_entries.each do |entry, file_data| + path = entry['path'].untaint + + if path =~ /\A\// then # for extra sanity + raise Gem::InstallError, + "attempt to install file into #{entry['path'].inspect}" + end + + path = File.expand_path File.join(@gem_dir, path) + + if path !~ /\A#{Regexp.escape @gem_dir}/ then + msg = "attempt to install file into %p under %p" % + [entry['path'], @gem_dir] + raise Gem::InstallError, msg + end + + FileUtils.mkdir_p File.dirname(path) + + File.open(path, "wb") do |out| + out.write file_data + end + + FileUtils.chmod entry['mode'], path + + say path if Gem.configuration.really_verbose + end + end + + ## + # Prefix and suffix the program filename the same as ruby. + + def formatted_program_filename(filename) + if @format_executable then + self.class.exec_format % File.basename(filename) + else + filename + end + end + + private + + ## + # HACK Pathname is broken on windows. + + def absolute_path? pathname + pathname.absolute? or (Gem.win_platform? and pathname.to_s =~ /\A[a-z]:/i) + end + + def expand_and_validate_gem_dir + @gem_dir = Pathname.new(@gem_dir).expand_path + + unless absolute_path?(@gem_dir) then # HACK is this possible after #expand_path? + raise ArgumentError, "install directory %p not absolute" % @gem_dir + end + + @gem_dir = @gem_dir.to_s + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/local_remote_options.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/local_remote_options.rb new file mode 100644 index 0000000000..799b9d5893 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/local_remote_options.rb @@ -0,0 +1,127 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'uri' +require 'rubygems' + +## +# Mixin methods for local and remote Gem::Command options. + +module Gem::LocalRemoteOptions + + ## + # Allows OptionParser to handle HTTP URIs. + + def accept_uri_http + OptionParser.accept URI::HTTP do |value| + begin + uri = URI.parse value + rescue URI::InvalidURIError + raise OptionParser::InvalidArgument, value + end + + raise OptionParser::InvalidArgument, value unless uri.scheme == 'http' + + value + end + end + + ## + # Add local/remote options to the command line parser. + + def add_local_remote_options + add_option(:"Local/Remote", '-l', '--local', + 'Restrict operations to the LOCAL domain') do |value, options| + options[:domain] = :local + end + + add_option(:"Local/Remote", '-r', '--remote', + 'Restrict operations to the REMOTE domain') do |value, options| + options[:domain] = :remote + end + + add_option(:"Local/Remote", '-b', '--both', + 'Allow LOCAL and REMOTE operations') do |value, options| + options[:domain] = :both + end + + add_bulk_threshold_option + add_source_option + add_proxy_option + add_update_sources_option + end + + ## + # Add the --bulk-threshold option + + def add_bulk_threshold_option + add_option(:"Local/Remote", '-B', '--bulk-threshold COUNT', + "Threshold for switching to bulk", + "synchronization (default #{Gem.configuration.bulk_threshold})") do + |value, options| + Gem.configuration.bulk_threshold = value.to_i + end + end + + ## + # Add the --http-proxy option + + def add_proxy_option + accept_uri_http + + add_option(:"Local/Remote", '-p', '--[no-]http-proxy [URL]', URI::HTTP, + 'Use HTTP proxy for remote operations') do |value, options| + options[:http_proxy] = (value == false) ? :no_proxy : value + Gem.configuration[:http_proxy] = options[:http_proxy] + end + end + + ## + # Add the --source option + + def add_source_option + accept_uri_http + + add_option(:"Local/Remote", '--source URL', URI::HTTP, + 'Use URL as the remote source for gems') do |source, options| + source << '/' if source !~ /\/\z/ + + if options[:added_source] then + Gem.sources << source + else + options[:added_source] = true + Gem.sources.replace [source] + end + end + end + + ## + # Add the --source option + + def add_update_sources_option + + add_option(:"Local/Remote", '-u', '--[no-]update-sources', + 'Update local source cache') do |value, options| + Gem.configuration.update_sources = value + end + end + + ## + # Is local fetching enabled? + + def local? + options[:domain] == :local || options[:domain] == :both + end + + ## + # Is remote fetching enabled? + + def remote? + options[:domain] == :remote || options[:domain] == :both + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/old_format.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/old_format.rb new file mode 100644 index 0000000000..ef5d621f52 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/old_format.rb @@ -0,0 +1,148 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'fileutils' +require 'yaml' +require 'zlib' + +module Gem + + ## + # The format class knows the guts of the RubyGem .gem file format + # and provides the capability to read gem files + # + class OldFormat + attr_accessor :spec, :file_entries, :gem_path + + ## + # Constructs an instance of a Format object, representing the gem's + # data structure. + # + # gem:: [String] The file name of the gem + # + def initialize(gem_path) + @gem_path = gem_path + end + + ## + # Reads the named gem file and returns a Format object, representing + # the data from the gem file + # + # file_path:: [String] Path to the gem file + # + def self.from_file_by_path(file_path) + unless File.exist?(file_path) + raise Gem::Exception, "Cannot load gem file [#{file_path}]" + end + File.open(file_path, 'rb') do |file| + from_io(file, file_path) + end + end + + ## + # Reads a gem from an io stream and returns a Format object, representing + # the data from the gem file + # + # io:: [IO] Stream from which to read the gem + # + def self.from_io(io, gem_path="(io)") + format = self.new(gem_path) + skip_ruby(io) + format.spec = read_spec(io) + format.file_entries = [] + read_files_from_gem(io) do |entry, file_data| + format.file_entries << [entry, file_data] + end + format + end + + private + ## + # Skips the Ruby self-install header. After calling this method, the + # IO index will be set after the Ruby code. + # + # file:: [IO] The IO to process (skip the Ruby code) + # + def self.skip_ruby(file) + end_seen = false + loop { + line = file.gets + if(line == nil || line.chomp == "__END__") then + end_seen = true + break + end + } + if(end_seen == false) then + raise Gem::Exception.new("Failed to find end of ruby script while reading gem") + end + end + + ## + # Reads the specification YAML from the supplied IO and constructs + # a Gem::Specification from it. After calling this method, the + # IO index will be set after the specification header. + # + # file:: [IO] The IO to process + # + def self.read_spec(file) + yaml = '' + begin + read_until_dashes(file) do |line| + yaml << line + end + Specification.from_yaml(yaml) + rescue YAML::Error => e + raise Gem::Exception.new("Failed to parse gem specification out of gem file") + rescue ArgumentError => e + raise Gem::Exception.new("Failed to parse gem specification out of gem file") + end + end + + ## + # Reads lines from the supplied IO until a end-of-yaml (---) is + # reached + # + # file:: [IO] The IO to process + # block:: [String] The read line + # + def self.read_until_dashes(file) + while((line = file.gets) && line.chomp.strip != "---") do + yield line + end + end + + + ## + # Reads the embedded file data from a gem file, yielding an entry + # containing metadata about the file and the file contents themselves + # for each file that's archived in the gem. + # NOTE: Many of these methods should be extracted into some kind of + # Gem file read/writer + # + # gem_file:: [IO] The IO to process + # + def self.read_files_from_gem(gem_file) + errstr = "Error reading files from gem" + header_yaml = '' + begin + self.read_until_dashes(gem_file) do |line| + header_yaml << line + end + header = YAML.load(header_yaml) + raise Gem::Exception.new(errstr) unless header + header.each do |entry| + file_data = '' + self.read_until_dashes(gem_file) do |line| + file_data << line + end + yield [entry, Zlib::Inflate.inflate(file_data.strip.unpack("m")[0])] + end + rescue Exception,Zlib::DataError => e + raise Gem::Exception.new(errstr) + end + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/open-uri.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/open-uri.rb new file mode 100644 index 0000000000..4fd54f0cd1 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/open-uri.rb @@ -0,0 +1,771 @@ +require 'uri' +require 'stringio' +require 'time' + +module Kernel + private + alias rubygems_open_uri_original_open open # :nodoc: + + # makes possible to open various resources including URIs. + # If the first argument respond to `open' method, + # the method is called with the rest arguments. + # + # If the first argument is a string which begins with xxx://, + # it is parsed by URI.parse. If the parsed object respond to `open' method, + # the method is called with the rest arguments. + # + # Otherwise original open is called. + # + # Since open-uri.rb provides URI::HTTP#open, URI::HTTPS#open and + # URI::FTP#open, + # Kernel[#.]open can accepts such URIs and strings which begins with + # http://, https:// and ftp://. + # In these case, the opened file object is extended by OpenURI::Meta. + def open(name, *rest, &block) # :doc: + if name.respond_to?(:open) + name.open(*rest, &block) + elsif name.respond_to?(:to_str) && + %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name && + (uri = URI.parse(name)).respond_to?(:open) + uri.open(*rest, &block) + else + rubygems_open_uri_original_open(name, *rest, &block) + end + end + module_function :open +end + +# OpenURI is an easy-to-use wrapper for net/http, net/https and net/ftp. +# +#== Example +# +# It is possible to open http/https/ftp URL as usual like opening a file: +# +# open("http://www.ruby-lang.org/") {|f| +# f.each_line {|line| p line} +# } +# +# The opened file has several methods for meta information as follows since +# it is extended by OpenURI::Meta. +# +# open("http://www.ruby-lang.org/en") {|f| +# f.each_line {|line| p line} +# p f.base_uri # +# p f.content_type # "text/html" +# p f.charset # "iso-8859-1" +# p f.content_encoding # [] +# p f.last_modified # Thu Dec 05 02:45:02 UTC 2002 +# } +# +# Additional header fields can be specified by an optional hash argument. +# +# open("http://www.ruby-lang.org/en/", +# "User-Agent" => "Ruby/#{RUBY_VERSION}", +# "From" => "foo@bar.invalid", +# "Referer" => "http://www.ruby-lang.org/") {|f| +# # ... +# } +# +# The environment variables such as http_proxy, https_proxy and ftp_proxy +# are in effect by default. :proxy => nil disables proxy. +# +# open("http://www.ruby-lang.org/en/raa.html", :proxy => nil) {|f| +# # ... +# } +# +# URI objects can be opened in a similar way. +# +# uri = URI.parse("http://www.ruby-lang.org/en/") +# uri.open {|f| +# # ... +# } +# +# URI objects can be read directly. The returned string is also extended by +# OpenURI::Meta. +# +# str = uri.read +# p str.base_uri +# +# Author:: Tanaka Akira + +module OpenURI + Options = { + :proxy => true, + :proxy_http_basic_authentication => true, + :progress_proc => true, + :content_length_proc => true, + :http_basic_authentication => true, + :read_timeout => true, + :ssl_ca_cert => nil, + :ssl_verify_mode => nil, + } + + def OpenURI.check_options(options) # :nodoc: + options.each {|k, v| + next unless Symbol === k + unless Options.include? k + raise ArgumentError, "unrecognized option: #{k}" + end + } + end + + def OpenURI.scan_open_optional_arguments(*rest) # :nodoc: + if !rest.empty? && (String === rest.first || Integer === rest.first) + mode = rest.shift + if !rest.empty? && Integer === rest.first + perm = rest.shift + end + end + return mode, perm, rest + end + + def OpenURI.open_uri(name, *rest) # :nodoc: + uri = URI::Generic === name ? name : URI.parse(name) + mode, perm, rest = OpenURI.scan_open_optional_arguments(*rest) + options = rest.shift if !rest.empty? && Hash === rest.first + raise ArgumentError.new("extra arguments") if !rest.empty? + options ||= {} + OpenURI.check_options(options) + + unless mode == nil || + mode == 'r' || mode == 'rb' || + mode == File::RDONLY + raise ArgumentError.new("invalid access mode #{mode} (#{uri.class} resource is read only.)") + end + + io = open_loop(uri, options) + if block_given? + begin + yield io + ensure + io.close + end + else + io + end + end + + def OpenURI.open_loop(uri, options) # :nodoc: + proxy_opts = [] + proxy_opts << :proxy_http_basic_authentication if options.include? :proxy_http_basic_authentication + proxy_opts << :proxy if options.include? :proxy + proxy_opts.compact! + if 1 < proxy_opts.length + raise ArgumentError, "multiple proxy options specified" + end + case proxy_opts.first + when :proxy_http_basic_authentication + opt_proxy, proxy_user, proxy_pass = options.fetch(:proxy_http_basic_authentication) + proxy_user = proxy_user.to_str + proxy_pass = proxy_pass.to_str + if opt_proxy == true + raise ArgumentError.new("Invalid authenticated proxy option: #{options[:proxy_http_basic_authentication].inspect}") + end + when :proxy + opt_proxy = options.fetch(:proxy) + proxy_user = nil + proxy_pass = nil + when nil + opt_proxy = true + proxy_user = nil + proxy_pass = nil + end + case opt_proxy + when true + find_proxy = lambda {|u| pxy = u.find_proxy; pxy ? [pxy, nil, nil] : nil} + when nil, false + find_proxy = lambda {|u| nil} + when String + opt_proxy = URI.parse(opt_proxy) + find_proxy = lambda {|u| [opt_proxy, proxy_user, proxy_pass]} + when URI::Generic + find_proxy = lambda {|u| [opt_proxy, proxy_user, proxy_pass]} + else + raise ArgumentError.new("Invalid proxy option: #{opt_proxy}") + end + + uri_set = {} + buf = nil + while true + redirect = catch(:open_uri_redirect) { + buf = Buffer.new + uri.buffer_open(buf, find_proxy.call(uri), options) + nil + } + if redirect + if redirect.relative? + # Although it violates RFC2616, Location: field may have relative + # URI. It is converted to absolute URI using uri as a base URI. + redirect = uri + redirect + end + unless OpenURI.redirectable?(uri, redirect) + raise "redirection forbidden: #{uri} -> #{redirect}" + end + if options.include? :http_basic_authentication + # send authentication only for the URI directly specified. + options = options.dup + options.delete :http_basic_authentication + end + uri = redirect + raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s + uri_set[uri.to_s] = true + else + break + end + end + io = buf.io + io.base_uri = uri + io + end + + def OpenURI.redirectable?(uri1, uri2) # :nodoc: + # This test is intended to forbid a redirection from http://... to + # file:///etc/passwd. + # However this is ad hoc. It should be extensible/configurable. + uri1.scheme.downcase == uri2.scheme.downcase || + (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:http|ftp)\z/i =~ uri2.scheme) + end + + def OpenURI.open_http(buf, target, proxy, options) # :nodoc: + if proxy + proxy_uri, proxy_user, proxy_pass = proxy + raise "Non-HTTP proxy URI: #{proxy_uri}" if proxy_uri.class != URI::HTTP + end + + if target.userinfo && "1.9.0" <= RUBY_VERSION + # don't raise for 1.8 because compatibility. + raise ArgumentError, "userinfo not supported. [RFC3986]" + end + + header = {} + options.each {|k, v| header[k] = v if String === k } + + require 'net/http' + klass = Net::HTTP + if URI::HTTP === target + # HTTP or HTTPS + if proxy + if proxy_user && proxy_pass + klass = Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_user, proxy_pass) + else + klass = Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port) + end + end + target_host = target.host + target_port = target.port + request_uri = target.request_uri + else + # FTP over HTTP proxy + target_host = proxy_uri.host + target_port = proxy_uri.port + request_uri = target.to_s + if proxy_user && proxy_pass + header["Proxy-Authorization"] = 'Basic ' + ["#{proxy_user}:#{proxy_pass}"].pack('m').delete("\r\n") + end + end + + http = klass.new(target_host, target_port) + if target.class == URI::HTTPS + require 'net/https' + http.use_ssl = true + http.verify_mode = options[:ssl_verify_mode] || OpenSSL::SSL::VERIFY_PEER + store = OpenSSL::X509::Store.new + if options[:ssl_ca_cert] + if File.directory? options[:ssl_ca_cert] + store.add_path options[:ssl_ca_cert] + else + store.add_file options[:ssl_ca_cert] + end + else + store.set_default_paths + end + store.set_default_paths + http.cert_store = store + end + if options.include? :read_timeout + http.read_timeout = options[:read_timeout] + end + + resp = nil + http.start { + if target.class == URI::HTTPS + # xxx: information hiding violation + sock = http.instance_variable_get(:@socket) + if sock.respond_to?(:io) + sock = sock.io # 1.9 + else + sock = sock.instance_variable_get(:@socket) # 1.8 + end + sock.post_connection_check(target_host) + end + req = Net::HTTP::Get.new(request_uri, header) + if options.include? :http_basic_authentication + user, pass = options[:http_basic_authentication] + req.basic_auth user, pass + end + http.request(req) {|response| + resp = response + if options[:content_length_proc] && Net::HTTPSuccess === resp + if resp.key?('Content-Length') + options[:content_length_proc].call(resp['Content-Length'].to_i) + else + options[:content_length_proc].call(nil) + end + end + resp.read_body {|str| + buf << str + if options[:progress_proc] && Net::HTTPSuccess === resp + options[:progress_proc].call(buf.size) + end + } + } + } + io = buf.io + io.rewind + io.status = [resp.code, resp.message] + resp.each {|name,value| buf.io.meta_add_field name, value } + case resp + when Net::HTTPSuccess + when Net::HTTPMovedPermanently, # 301 + Net::HTTPFound, # 302 + Net::HTTPSeeOther, # 303 + Net::HTTPTemporaryRedirect # 307 + throw :open_uri_redirect, URI.parse(resp['location']) + else + raise OpenURI::HTTPError.new(io.status.join(' '), io) + end + end + + class HTTPError < StandardError + def initialize(message, io) + super(message) + @io = io + end + attr_reader :io + end + + class Buffer # :nodoc: + def initialize + @io = StringIO.new + @size = 0 + end + attr_reader :size + + StringMax = 10240 + def <<(str) + @io << str + @size += str.length + if StringIO === @io && StringMax < @size + require 'tempfile' + io = Tempfile.new('open-uri') + io.binmode + Meta.init io, @io if @io.respond_to? :meta + io << @io.string + @io = io + end + end + + def io + Meta.init @io unless @io.respond_to? :meta + @io + end + end + + # Mixin for holding meta-information. + module Meta + def Meta.init(obj, src=nil) # :nodoc: + obj.extend Meta + obj.instance_eval { + @base_uri = nil + @meta = {} + } + if src + obj.status = src.status + obj.base_uri = src.base_uri + src.meta.each {|name, value| + obj.meta_add_field(name, value) + } + end + end + + # returns an Array which consists status code and message. + attr_accessor :status + + # returns a URI which is base of relative URIs in the data. + # It may differ from the URI supplied by a user because redirection. + attr_accessor :base_uri + + # returns a Hash which represents header fields. + # The Hash keys are downcased for canonicalization. + attr_reader :meta + + def meta_add_field(name, value) # :nodoc: + @meta[name.downcase] = value + end + + # returns a Time which represents Last-Modified field. + def last_modified + if v = @meta['last-modified'] + Time.httpdate(v) + else + nil + end + end + + RE_LWS = /[\r\n\t ]+/n + RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n + RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])*"}n + RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n + + def content_type_parse # :nodoc: + v = @meta['content-type'] + # The last (?:;#{RE_LWS}?)? matches extra ";" which violates RFC2045. + if v && %r{\A#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?/(#{RE_TOKEN})#{RE_LWS}?(#{RE_PARAMETERS})(?:;#{RE_LWS}?)?\z}no =~ v + type = $1.downcase + subtype = $2.downcase + parameters = [] + $3.scan(/;#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?=#{RE_LWS}?(?:(#{RE_TOKEN})|(#{RE_QUOTED_STRING}))/no) {|att, val, qval| + val = qval.gsub(/[\r\n\t !#-\[\]-~\x80-\xff]+|(\\[\x00-\x7f])/) { $1 ? $1[1,1] : $& } if qval + parameters << [att.downcase, val] + } + ["#{type}/#{subtype}", *parameters] + else + nil + end + end + + # returns "type/subtype" which is MIME Content-Type. + # It is downcased for canonicalization. + # Content-Type parameters are stripped. + def content_type + type, *parameters = content_type_parse + type || 'application/octet-stream' + end + + # returns a charset parameter in Content-Type field. + # It is downcased for canonicalization. + # + # If charset parameter is not given but a block is given, + # the block is called and its result is returned. + # It can be used to guess charset. + # + # If charset parameter and block is not given, + # nil is returned except text type in HTTP. + # In that case, "iso-8859-1" is returned as defined by RFC2616 3.7.1. + def charset + type, *parameters = content_type_parse + if pair = parameters.assoc('charset') + pair.last.downcase + elsif block_given? + yield + elsif type && %r{\Atext/} =~ type && + @base_uri && /\Ahttp\z/i =~ @base_uri.scheme + "iso-8859-1" # RFC2616 3.7.1 + else + nil + end + end + + # returns a list of encodings in Content-Encoding field + # as an Array of String. + # The encodings are downcased for canonicalization. + def content_encoding + v = @meta['content-encoding'] + if v && %r{\A#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?(?:,#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?)*}o =~ v + v.scan(RE_TOKEN).map {|content_coding| content_coding.downcase} + else + [] + end + end + end + + # Mixin for HTTP and FTP URIs. + module OpenRead + # OpenURI::OpenRead#open provides `open' for URI::HTTP and URI::FTP. + # + # OpenURI::OpenRead#open takes optional 3 arguments as: + # OpenURI::OpenRead#open([mode [, perm]] [, options]) [{|io| ... }] + # + # `mode', `perm' is same as Kernel#open. + # + # However, `mode' must be read mode because OpenURI::OpenRead#open doesn't + # support write mode (yet). + # Also `perm' is just ignored because it is meaningful only for file + # creation. + # + # `options' must be a hash. + # + # Each pairs which key is a string in the hash specify a extra header + # field for HTTP. + # I.e. it is ignored for FTP without HTTP proxy. + # + # The hash may include other options which key is a symbol: + # + # [:proxy] + # Synopsis: + # :proxy => "http://proxy.foo.com:8000/" + # :proxy => URI.parse("http://proxy.foo.com:8000/") + # :proxy => true + # :proxy => false + # :proxy => nil + # + # If :proxy option is specified, the value should be String, URI, + # boolean or nil. + # When String or URI is given, it is treated as proxy URI. + # When true is given or the option itself is not specified, + # environment variable `scheme_proxy' is examined. + # `scheme' is replaced by `http', `https' or `ftp'. + # When false or nil is given, the environment variables are ignored and + # connection will be made to a server directly. + # + # [:proxy_http_basic_authentication] + # Synopsis: + # :proxy_http_basic_authentication => ["http://proxy.foo.com:8000/", "proxy-user", "proxy-password"] + # :proxy_http_basic_authentication => [URI.parse("http://proxy.foo.com:8000/"), "proxy-user", "proxy-password"] + # + # If :proxy option is specified, the value should be an Array with 3 elements. + # It should contain a proxy URI, a proxy user name and a proxy password. + # The proxy URI should be a String, an URI or nil. + # The proxy user name and password should be a String. + # + # If nil is given for the proxy URI, this option is just ignored. + # + # If :proxy and :proxy_http_basic_authentication is specified, + # ArgumentError is raised. + # + # [:http_basic_authentication] + # Synopsis: + # :http_basic_authentication=>[user, password] + # + # If :http_basic_authentication is specified, + # the value should be an array which contains 2 strings: + # username and password. + # It is used for HTTP Basic authentication defined by RFC 2617. + # + # [:content_length_proc] + # Synopsis: + # :content_length_proc => lambda {|content_length| ... } + # + # If :content_length_proc option is specified, the option value procedure + # is called before actual transfer is started. + # It takes one argument which is expected content length in bytes. + # + # If two or more transfer is done by HTTP redirection, the procedure + # is called only one for a last transfer. + # + # When expected content length is unknown, the procedure is called with + # nil. + # It is happen when HTTP response has no Content-Length header. + # + # [:progress_proc] + # Synopsis: + # :progress_proc => lambda {|size| ...} + # + # If :progress_proc option is specified, the proc is called with one + # argument each time when `open' gets content fragment from network. + # The argument `size' `size' is a accumulated transfered size in bytes. + # + # If two or more transfer is done by HTTP redirection, the procedure + # is called only one for a last transfer. + # + # :progress_proc and :content_length_proc are intended to be used for + # progress bar. + # For example, it can be implemented as follows using Ruby/ProgressBar. + # + # pbar = nil + # open("http://...", + # :content_length_proc => lambda {|t| + # if t && 0 < t + # pbar = ProgressBar.new("...", t) + # pbar.file_transfer_mode + # end + # }, + # :progress_proc => lambda {|s| + # pbar.set s if pbar + # }) {|f| ... } + # + # [:read_timeout] + # Synopsis: + # :read_timeout=>nil (no timeout) + # :read_timeout=>10 (10 second) + # + # :read_timeout option specifies a timeout of read for http connections. + # + # [:ssl_ca_cert] + # Synopsis: + # :ssl_ca_cert=>filename + # + # :ssl_ca_cert is used to specify CA certificate for SSL. + # If it is given, default certificates are not used. + # + # [:ssl_verify_mode] + # Synopsis: + # :ssl_verify_mode=>mode + # + # :ssl_verify_mode is used to specify openssl verify mode. + # + # OpenURI::OpenRead#open returns an IO like object if block is not given. + # Otherwise it yields the IO object and return the value of the block. + # The IO object is extended with OpenURI::Meta. + def open(*rest, &block) + OpenURI.open_uri(self, *rest, &block) + end + + # OpenURI::OpenRead#read([options]) reads a content referenced by self and + # returns the content as string. + # The string is extended with OpenURI::Meta. + # The argument `options' is same as OpenURI::OpenRead#open. + def read(options={}) + self.open(options) {|f| + str = f.read + Meta.init str, f + str + } + end + end +end + +module URI + class Generic + # returns a proxy URI. + # The proxy URI is obtained from environment variables such as http_proxy, + # ftp_proxy, no_proxy, etc. + # If there is no proper proxy, nil is returned. + # + # Note that capitalized variables (HTTP_PROXY, FTP_PROXY, NO_PROXY, etc.) + # are examined too. + # + # But http_proxy and HTTP_PROXY is treated specially under CGI environment. + # It's because HTTP_PROXY may be set by Proxy: header. + # So HTTP_PROXY is not used. + # http_proxy is not used too if the variable is case insensitive. + # CGI_HTTP_PROXY can be used instead. + def find_proxy + name = self.scheme.downcase + '_proxy' + proxy_uri = nil + if name == 'http_proxy' && ENV.include?('REQUEST_METHOD') # CGI? + # HTTP_PROXY conflicts with *_proxy for proxy settings and + # HTTP_* for header information in CGI. + # So it should be careful to use it. + pairs = ENV.reject {|k, v| /\Ahttp_proxy\z/i !~ k } + case pairs.length + when 0 # no proxy setting anyway. + proxy_uri = nil + when 1 + k, v = pairs.shift + if k == 'http_proxy' && ENV[k.upcase] == nil + # http_proxy is safe to use because ENV is case sensitive. + proxy_uri = ENV[name] + else + proxy_uri = nil + end + else # http_proxy is safe to use because ENV is case sensitive. + proxy_uri = ENV[name] + end + if !proxy_uri + # Use CGI_HTTP_PROXY. cf. libwww-perl. + proxy_uri = ENV["CGI_#{name.upcase}"] + end + elsif name == 'http_proxy' + unless proxy_uri = ENV[name] + if proxy_uri = ENV[name.upcase] + warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.' + end + end + else + proxy_uri = ENV[name] || ENV[name.upcase] + end + + if proxy_uri && self.host + require 'socket' + begin + addr = IPSocket.getaddress(self.host) + proxy_uri = nil if /\A127\.|\A::1\z/ =~ addr + rescue SocketError + end + end + + if proxy_uri + proxy_uri = URI.parse(proxy_uri) + name = 'no_proxy' + if no_proxy = ENV[name] || ENV[name.upcase] + no_proxy.scan(/([^:,]*)(?::(\d+))?/) {|host, port| + if /(\A|\.)#{Regexp.quote host}\z/i =~ self.host && + (!port || self.port == port.to_i) + proxy_uri = nil + break + end + } + end + proxy_uri + else + nil + end + end + end + + class HTTP + def buffer_open(buf, proxy, options) # :nodoc: + OpenURI.open_http(buf, self, proxy, options) + end + + include OpenURI::OpenRead + end + + class FTP + def buffer_open(buf, proxy, options) # :nodoc: + if proxy + OpenURI.open_http(buf, self, proxy, options) + return + end + require 'net/ftp' + + directories = self.path.split(%r{/}, -1) + directories.shift if directories[0] == '' # strip a field before leading slash + directories.each {|d| + d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") } + } + unless filename = directories.pop + raise ArgumentError, "no filename: #{self.inspect}" + end + directories.each {|d| + if /[\r\n]/ =~ d + raise ArgumentError, "invalid directory: #{d.inspect}" + end + } + if /[\r\n]/ =~ filename + raise ArgumentError, "invalid filename: #{filename.inspect}" + end + typecode = self.typecode + if typecode && /\A[aid]\z/ !~ typecode + raise ArgumentError, "invalid typecode: #{typecode.inspect}" + end + + # The access sequence is defined by RFC 1738 + ftp = Net::FTP.open(self.host) + # todo: extract user/passwd from .netrc. + user = 'anonymous' + passwd = nil + user, passwd = self.userinfo.split(/:/) if self.userinfo + ftp.login(user, passwd) + directories.each {|cwd| + ftp.voidcmd("CWD #{cwd}") + } + if typecode + # xxx: typecode D is not handled. + ftp.voidcmd("TYPE #{typecode.upcase}") + end + if options[:content_length_proc] + options[:content_length_proc].call(ftp.size(filename)) + end + ftp.retrbinary("RETR #{filename}", 4096) { |str| + buf << str + options[:progress_proc].call(buf.size) if options[:progress_proc] + } + ftp.close + buf.io.rewind + end + + include OpenURI::OpenRead + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package.rb new file mode 100644 index 0000000000..9cb393b0c7 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package.rb @@ -0,0 +1,95 @@ +#++ +# Copyright (C) 2004 Mauricio Julio Fernndez Pradier +# See LICENSE.txt for additional licensing information. +#-- + +require 'fileutils' +require 'find' +require 'stringio' +require 'yaml' +require 'zlib' + +require 'rubygems/digest/md5' +require 'rubygems/security' +require 'rubygems/specification' + +# Wrapper for FileUtils meant to provide logging and additional operations if +# needed. +class Gem::FileOperations + + def initialize(logger = nil) + @logger = logger + end + + def method_missing(meth, *args, &block) + case + when FileUtils.respond_to?(meth) + @logger.log "#{meth}: #{args}" if @logger + FileUtils.send meth, *args, &block + when Gem::FileOperations.respond_to?(meth) + @logger.log "#{meth}: #{args}" if @logger + Gem::FileOperations.send meth, *args, &block + else + super + end + end + +end + +module Gem::Package + + class Error < StandardError; end + class NonSeekableIO < Error; end + class ClosedIO < Error; end + class BadCheckSum < Error; end + class TooLongFileName < Error; end + class FormatError < Error; end + + def self.open(io, mode = "r", signer = nil, &block) + tar_type = case mode + when 'r' then TarInput + when 'w' then TarOutput + else + raise "Unknown Package open mode" + end + + tar_type.open(io, signer, &block) + end + + def self.pack(src, destname, signer = nil) + TarOutput.open(destname, signer) do |outp| + dir_class.chdir(src) do + outp.metadata = (file_class.read("RPA/metadata") rescue nil) + find_class.find('.') do |entry| + case + when file_class.file?(entry) + entry.sub!(%r{\./}, "") + next if entry =~ /\ARPA\// + stat = File.stat(entry) + outp.add_file_simple(entry, stat.mode, stat.size) do |os| + file_class.open(entry, "rb") do |f| + os.write(f.read(4096)) until f.eof? + end + end + when file_class.dir?(entry) + entry.sub!(%r{\./}, "") + next if entry == "RPA" + outp.mkdir(entry, file_class.stat(entry).mode) + else + raise "Don't know how to pack this yet!" + end + end + end + end + end + +end + +require 'rubygems/package/f_sync_dir' +require 'rubygems/package/tar_header' +require 'rubygems/package/tar_input' +require 'rubygems/package/tar_output' +require 'rubygems/package/tar_reader' +require 'rubygems/package/tar_reader/entry' +require 'rubygems/package/tar_writer' + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/f_sync_dir.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/f_sync_dir.rb new file mode 100644 index 0000000000..3e2e4a59a8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/f_sync_dir.rb @@ -0,0 +1,24 @@ +#++ +# Copyright (C) 2004 Mauricio Julio Fernndez Pradier +# See LICENSE.txt for additional licensing information. +#-- + +require 'rubygems/package' + +module Gem::Package::FSyncDir + + private + + ## + # make sure this hits the disc + + def fsync_dir(dirname) + dir = open dirname, 'r' + dir.fsync + rescue # ignore IOError if it's an unpatched (old) Ruby + ensure + dir.close if dir rescue nil + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_header.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_header.rb new file mode 100644 index 0000000000..c194cc0530 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_header.rb @@ -0,0 +1,245 @@ +#++ +# Copyright (C) 2004 Mauricio Julio Fernndez Pradier +# See LICENSE.txt for additional licensing information. +#-- + +require 'rubygems/package' + +## +#-- +# struct tarfile_entry_posix { +# char name[100]; # ASCII + (Z unless filled) +# char mode[8]; # 0 padded, octal, null +# char uid[8]; # ditto +# char gid[8]; # ditto +# char size[12]; # 0 padded, octal, null +# char mtime[12]; # 0 padded, octal, null +# char checksum[8]; # 0 padded, octal, null, space +# char typeflag[1]; # file: "0" dir: "5" +# char linkname[100]; # ASCII + (Z unless filled) +# char magic[6]; # "ustar\0" +# char version[2]; # "00" +# char uname[32]; # ASCIIZ +# char gname[32]; # ASCIIZ +# char devmajor[8]; # 0 padded, octal, null +# char devminor[8]; # o padded, octal, null +# char prefix[155]; # ASCII + (Z unless filled) +# }; +#++ + +class Gem::Package::TarHeader + + FIELDS = [ + :checksum, + :devmajor, + :devminor, + :gid, + :gname, + :linkname, + :magic, + :mode, + :mtime, + :name, + :prefix, + :size, + :typeflag, + :uid, + :uname, + :version, + ] + + PACK_FORMAT = 'a100' + # name + 'a8' + # mode + 'a8' + # uid + 'a8' + # gid + 'a12' + # size + 'a12' + # mtime + 'a7a' + # chksum + 'a' + # typeflag + 'a100' + # linkname + 'a6' + # magic + 'a2' + # version + 'a32' + # uname + 'a32' + # gname + 'a8' + # devmajor + 'a8' + # devminor + 'a155' # prefix + + UNPACK_FORMAT = 'A100' + # name + 'A8' + # mode + 'A8' + # uid + 'A8' + # gid + 'A12' + # size + 'A12' + # mtime + 'A8' + # checksum + 'A' + # typeflag + 'A100' + # linkname + 'A6' + # magic + 'A2' + # version + 'A32' + # uname + 'A32' + # gname + 'A8' + # devmajor + 'A8' + # devminor + 'A155' # prefix + + attr_reader(*FIELDS) + + def self.from(stream) + header = stream.read 512 + empty = (header == "\0" * 512) + + fields = header.unpack UNPACK_FORMAT + + name = fields.shift + mode = fields.shift.oct + uid = fields.shift.oct + gid = fields.shift.oct + size = fields.shift.oct + mtime = fields.shift.oct + checksum = fields.shift.oct + typeflag = fields.shift + linkname = fields.shift + magic = fields.shift + version = fields.shift.oct + uname = fields.shift + gname = fields.shift + devmajor = fields.shift.oct + devminor = fields.shift.oct + prefix = fields.shift + + new :name => name, + :mode => mode, + :uid => uid, + :gid => gid, + :size => size, + :mtime => mtime, + :checksum => checksum, + :typeflag => typeflag, + :linkname => linkname, + :magic => magic, + :version => version, + :uname => uname, + :gname => gname, + :devmajor => devmajor, + :devminor => devminor, + :prefix => prefix, + + :empty => empty + + # HACK unfactor for Rubinius + #new :name => fields.shift, + # :mode => fields.shift.oct, + # :uid => fields.shift.oct, + # :gid => fields.shift.oct, + # :size => fields.shift.oct, + # :mtime => fields.shift.oct, + # :checksum => fields.shift.oct, + # :typeflag => fields.shift, + # :linkname => fields.shift, + # :magic => fields.shift, + # :version => fields.shift.oct, + # :uname => fields.shift, + # :gname => fields.shift, + # :devmajor => fields.shift.oct, + # :devminor => fields.shift.oct, + # :prefix => fields.shift, + + # :empty => empty + end + + def initialize(vals) + unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode] then + raise ArgumentError, ":name, :size, :prefix and :mode required" + end + + vals[:uid] ||= 0 + vals[:gid] ||= 0 + vals[:mtime] ||= 0 + vals[:checksum] ||= "" + vals[:typeflag] ||= "0" + vals[:magic] ||= "ustar" + vals[:version] ||= "00" + vals[:uname] ||= "wheel" + vals[:gname] ||= "wheel" + vals[:devmajor] ||= 0 + vals[:devminor] ||= 0 + + FIELDS.each do |name| + instance_variable_set "@#{name}", vals[name] + end + + @empty = vals[:empty] + end + + def empty? + @empty + end + + def ==(other) + self.class === other and + @checksum == other.checksum and + @devmajor == other.devmajor and + @devminor == other.devminor and + @gid == other.gid and + @gname == other.gname and + @linkname == other.linkname and + @magic == other.magic and + @mode == other.mode and + @mtime == other.mtime and + @name == other.name and + @prefix == other.prefix and + @size == other.size and + @typeflag == other.typeflag and + @uid == other.uid and + @uname == other.uname and + @version == other.version + end + + def to_s + update_checksum + header + end + + def update_checksum + header = header " " * 8 + @checksum = oct calculate_checksum(header), 6 + end + + private + + def calculate_checksum(header) + header.unpack("C*").inject { |a, b| a + b } + end + + def header(checksum = @checksum) + header = [ + name, + oct(mode, 7), + oct(uid, 7), + oct(gid, 7), + oct(size, 11), + oct(mtime, 11), + checksum, + " ", + typeflag, + linkname, + magic, + oct(version, 2), + uname, + gname, + oct(devmajor, 7), + oct(devminor, 7), + prefix + ] + + header = header.pack PACK_FORMAT + + header << ("\0" * ((512 - header.size) % 512)) + end + + def oct(num, len) + "%0#{len}o" % num + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_input.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_input.rb new file mode 100644 index 0000000000..2ed3d6b772 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_input.rb @@ -0,0 +1,219 @@ +#++ +# Copyright (C) 2004 Mauricio Julio Fernndez Pradier +# See LICENSE.txt for additional licensing information. +#-- + +require 'rubygems/package' + +class Gem::Package::TarInput + + include Gem::Package::FSyncDir + include Enumerable + + attr_reader :metadata + + private_class_method :new + + def self.open(io, security_policy = nil, &block) + is = new io, security_policy + + yield is + ensure + is.close if is + end + + def initialize(io, security_policy = nil) + @io = io + @tarreader = Gem::Package::TarReader.new @io + has_meta = false + + data_sig, meta_sig, data_dgst, meta_dgst = nil, nil, nil, nil + dgst_algo = security_policy ? Gem::Security::OPT[:dgst_algo] : nil + + @tarreader.each do |entry| + case entry.full_name + when "metadata" + @metadata = load_gemspec entry.read + has_meta = true + when "metadata.gz" + begin + # if we have a security_policy, then pre-read the metadata file + # and calculate it's digest + sio = nil + if security_policy + Gem.ensure_ssl_available + sio = StringIO.new(entry.read) + meta_dgst = dgst_algo.digest(sio.string) + sio.rewind + end + + gzis = Zlib::GzipReader.new(sio || entry) + # YAML wants an instance of IO + @metadata = load_gemspec(gzis) + has_meta = true + ensure + gzis.close unless gzis.nil? + end + when 'metadata.gz.sig' + meta_sig = entry.read + when 'data.tar.gz.sig' + data_sig = entry.read + when 'data.tar.gz' + if security_policy + Gem.ensure_ssl_available + data_dgst = dgst_algo.digest(entry.read) + end + end + end + + if security_policy then + Gem.ensure_ssl_available + + # map trust policy from string to actual class (or a serialized YAML + # file, if that exists) + if String === security_policy then + if Gem::Security::Policy.key? security_policy then + # load one of the pre-defined security policies + security_policy = Gem::Security::Policy[security_policy] + elsif File.exist? security_policy then + # FIXME: this doesn't work yet + security_policy = YAML.load File.read(security_policy) + else + raise Gem::Exception, "Unknown trust policy '#{security_policy}'" + end + end + + if data_sig && data_dgst && meta_sig && meta_dgst then + # the user has a trust policy, and we have a signed gem + # file, so use the trust policy to verify the gem signature + + begin + security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain) + rescue Exception => e + raise "Couldn't verify data signature: #{e}" + end + + begin + security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain) + rescue Exception => e + raise "Couldn't verify metadata signature: #{e}" + end + elsif security_policy.only_signed + raise Gem::Exception, "Unsigned gem" + else + # FIXME: should display warning here (trust policy, but + # either unsigned or badly signed gem file) + end + end + + @tarreader.rewind + @fileops = Gem::FileOperations.new + + raise Gem::Package::FormatError, "No metadata found!" unless has_meta + end + + def close + @io.close + @tarreader.close + end + + def each(&block) + @tarreader.each do |entry| + next unless entry.full_name == "data.tar.gz" + is = zipped_stream entry + + begin + Gem::Package::TarReader.new is do |inner| + inner.each(&block) + end + ensure + is.close if is + end + end + + @tarreader.rewind + end + + def extract_entry(destdir, entry, expected_md5sum = nil) + if entry.directory? then + dest = File.join(destdir, entry.full_name) + + if File.dir? dest then + @fileops.chmod entry.header.mode, dest, :verbose=>false + else + @fileops.mkdir_p dest, :mode => entry.header.mode, :verbose => false + end + + fsync_dir dest + fsync_dir File.join(dest, "..") + + return + end + + # it's a file + md5 = Digest::MD5.new if expected_md5sum + destdir = File.join destdir, File.dirname(entry.full_name) + @fileops.mkdir_p destdir, :mode => 0755, :verbose => false + destfile = File.join destdir, File.basename(entry.full_name) + @fileops.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT + + open destfile, "wb", entry.header.mode do |os| + loop do + data = entry.read 4096 + break unless data + # HACK shouldn't we check the MD5 before writing to disk? + md5 << data if expected_md5sum + os.write(data) + end + + os.fsync + end + + @fileops.chmod entry.header.mode, destfile, :verbose => false + fsync_dir File.dirname(destfile) + fsync_dir File.join(File.dirname(destfile), "..") + + if expected_md5sum && expected_md5sum != md5.hexdigest then + raise Gem::Package::BadCheckSum + end + end + + # Attempt to YAML-load a gemspec from the given _io_ parameter. Return + # nil if it fails. + def load_gemspec(io) + Gem::Specification.from_yaml io + rescue Gem::Exception + nil + end + + ## + # Return an IO stream for the zipped entry. + # + # NOTE: Originally this method used two approaches, Return a GZipReader + # directly, or read the GZipReader into a string and return a StringIO on + # the string. The string IO approach was used for versions of ZLib before + # 1.2.1 to avoid buffer errors on windows machines. Then we found that + # errors happened with 1.2.1 as well, so we changed the condition. Then + # we discovered errors occurred with versions as late as 1.2.3. At this + # point (after some benchmarking to show we weren't seriously crippling + # the unpacking speed) we threw our hands in the air and declared that + # this method would use the String IO approach on all platforms at all + # times. And that's the way it is. + + def zipped_stream(entry) + if defined? Rubinius then + zis = Zlib::GzipReader.new entry + dis = zis.read + is = StringIO.new(dis) + else + # This is Jamis Buck's Zlib workaround for some unknown issue + entry.read(10) # skip the gzip header + zis = Zlib::Inflate.new(-Zlib::MAX_WBITS) + is = StringIO.new(zis.inflate(entry.read)) + end + ensure + zis.finish if zis + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_output.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_output.rb new file mode 100644 index 0000000000..b22f7dd86b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_output.rb @@ -0,0 +1,143 @@ +#++ +# Copyright (C) 2004 Mauricio Julio Fernndez Pradier +# See LICENSE.txt for additional licensing information. +#-- + +require 'rubygems/package' + +## +# TarOutput is a wrapper to TarWriter that builds gem-format tar file. +# +# Gem-format tar files contain the following files: +# [data.tar.gz] A gzipped tar file containing the files that compose the gem +# which will be extracted into the gem/ dir on installation. +# [metadata.gz] A YAML format Gem::Specification. +# [data.tar.gz.sig] A signature for the gem's data.tar.gz. +# [metadata.gz.sig] A signature for the gem's metadata.gz. +# +# See TarOutput::open for usage details. + +class Gem::Package::TarOutput + + ## + # Creates a new TarOutput which will yield a TarWriter object for the + # data.tar.gz portion of a gem-format tar file. + # + # See #initialize for details on +io+ and +signer+. + # + # See #add_gem_contents for details on adding metadata to the tar file. + + def self.open(io, signer = nil, &block) # :yield: data_tar_writer + tar_outputter = new io, signer + tar_outputter.add_gem_contents(&block) + tar_outputter.add_metadata + tar_outputter.add_signatures + + ensure + tar_outputter.close + end + + ## + # Creates a new TarOutput that will write a gem-format tar file to +io+. If + # +signer+ is given, the data.tar.gz and metadata.gz will be signed and + # the signatures will be added to the tar file. + + def initialize(io, signer) + @io = io + @signer = signer + + @tar_writer = Gem::Package::TarWriter.new @io + + @metadata = nil + + @data_signature = nil + @meta_signature = nil + end + + ## + # Yields a TarWriter for the data.tar.gz inside a gem-format tar file. + # The yielded TarWriter has been extended with a #metadata= method for + # attaching a YAML format Gem::Specification which will be written by + # add_metadata. + + def add_gem_contents + @tar_writer.add_file "data.tar.gz", 0644 do |inner| + sio = @signer ? StringIO.new : nil + Zlib::GzipWriter.wrap(sio || inner) do |os| + + Gem::Package::TarWriter.new os do |data_tar_writer| + def data_tar_writer.metadata() @metadata end + def data_tar_writer.metadata=(metadata) @metadata = metadata end + + yield data_tar_writer + + @metadata = data_tar_writer.metadata + end + end + + # if we have a signing key, then sign the data + # digest and return the signature + if @signer then + digest = Gem::Security::OPT[:dgst_algo].digest sio.string + @data_signature = @signer.sign digest + inner.write sio.string + end + end + + self + end + + ## + # Adds metadata.gz to the gem-format tar file which was saved from a + # previous #add_gem_contents call. + + def add_metadata + return if @metadata.nil? + + @tar_writer.add_file "metadata.gz", 0644 do |io| + begin + sio = @signer ? StringIO.new : nil + gzos = Zlib::GzipWriter.new(sio || io) + gzos.write @metadata + ensure + gzos.flush + gzos.finish + + # if we have a signing key, then sign the metadata digest and return + # the signature + if @signer then + digest = Gem::Security::OPT[:dgst_algo].digest sio.string + @meta_signature = @signer.sign digest + io.write sio.string + end + end + end + end + + ## + # Adds data.tar.gz.sig and metadata.gz.sig to the gem-format tar files if + # a Gem::Security::Signer was sent to initialize. + + def add_signatures + if @data_signature then + @tar_writer.add_file 'data.tar.gz.sig', 0644 do |io| + io.write @data_signature + end + end + + if @meta_signature then + @tar_writer.add_file 'metadata.gz.sig', 0644 do |io| + io.write @meta_signature + end + end + end + + ## + # Closes the TarOutput. + + def close + @tar_writer.close + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_reader.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_reader.rb new file mode 100644 index 0000000000..8359399207 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_reader.rb @@ -0,0 +1,86 @@ +#++ +# Copyright (C) 2004 Mauricio Julio Fernndez Pradier +# See LICENSE.txt for additional licensing information. +#-- + +require 'rubygems/package' + +class Gem::Package::TarReader + + include Gem::Package + + class UnexpectedEOF < StandardError; end + + def self.new(io) + reader = super + + return reader unless block_given? + + begin + yield reader + ensure + reader.close + end + + nil + end + + def initialize(io) + @io = io + @init_pos = io.pos + end + + def close + end + + def each + loop do + return if @io.eof? + + header = Gem::Package::TarHeader.from @io + return if header.empty? + + entry = Gem::Package::TarReader::Entry.new header, @io + size = entry.header.size + + yield entry + + skip = (512 - (size % 512)) % 512 + + if @io.respond_to? :seek then + # avoid reading... + @io.seek(size - entry.bytes_read, IO::SEEK_CUR) + else + pending = size - entry.bytes_read + + while pending > 0 do + bread = @io.read([pending, 4096].min).size + raise UnexpectedEOF if @io.eof? + pending -= bread + end + end + + @io.read skip # discard trailing zeros + + # make sure nobody can use #read, #getc or #rewind anymore + entry.close + end + end + + alias each_entry each + + ## + # NOTE: Do not call #rewind during #each + + def rewind + if @init_pos == 0 then + raise Gem::Package::NonSeekableIO unless @io.respond_to? :rewind + @io.rewind + else + raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos= + @io.pos = @init_pos + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_reader/entry.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_reader/entry.rb new file mode 100644 index 0000000000..dcc66153d8 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_reader/entry.rb @@ -0,0 +1,99 @@ +#++ +# Copyright (C) 2004 Mauricio Julio Fernndez Pradier +# See LICENSE.txt for additional licensing information. +#-- + +require 'rubygems/package' + +class Gem::Package::TarReader::Entry + + attr_reader :header + + def initialize(header, io) + @closed = false + @header = header + @io = io + @orig_pos = @io.pos + @read = 0 + end + + def check_closed # :nodoc: + raise IOError, "closed #{self.class}" if closed? + end + + def bytes_read + @read + end + + def close + @closed = true + end + + def closed? + @closed + end + + def eof? + check_closed + + @read >= @header.size + end + + def full_name + if @header.prefix != "" then + File.join @header.prefix, @header.name + else + @header.name + end + end + + def getc + check_closed + + return nil if @read >= @header.size + + ret = @io.getc + @read += 1 if ret + + ret + end + + def directory? + @header.typeflag == "5" + end + + def file? + @header.typeflag == "0" + end + + def pos + check_closed + + bytes_read + end + + def read(len = nil) + check_closed + + return nil if @read >= @header.size + + len ||= @header.size - @read + max_read = [len, @header.size - @read].min + + ret = @io.read max_read + @read += ret.size + + ret + end + + def rewind + check_closed + + raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos= + + @io.pos = @orig_pos + @read = 0 + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_writer.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_writer.rb new file mode 100644 index 0000000000..6e11440e22 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/package/tar_writer.rb @@ -0,0 +1,180 @@ +#++ +# Copyright (C) 2004 Mauricio Julio Fernndez Pradier +# See LICENSE.txt for additional licensing information. +#-- + +require 'rubygems/package' + +class Gem::Package::TarWriter + + class FileOverflow < StandardError; end + + class BoundedStream + + attr_reader :limit, :written + + def initialize(io, limit) + @io = io + @limit = limit + @written = 0 + end + + def write(data) + if data.size + @written > @limit + raise FileOverflow, "You tried to feed more data than fits in the file." + end + @io.write data + @written += data.size + data.size + end + + end + + class RestrictedStream + + def initialize(io) + @io = io + end + + def write(data) + @io.write data + end + + end + + def self.new(io) + writer = super + + return writer unless block_given? + + begin + yield writer + ensure + writer.close + end + + nil + end + + def initialize(io) + @io = io + @closed = false + end + + def add_file(name, mode) + check_closed + + raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos= + + name, prefix = split_name name + + init_pos = @io.pos + @io.write "\0" * 512 # placeholder for the header + + yield RestrictedStream.new(@io) if block_given? + + size = @io.pos - init_pos - 512 + + remainder = (512 - (size % 512)) % 512 + @io.write "\0" * remainder + + final_pos = @io.pos + @io.pos = init_pos + + header = Gem::Package::TarHeader.new :name => name, :mode => mode, + :size => size, :prefix => prefix + + @io.write header + @io.pos = final_pos + + self + end + + def add_file_simple(name, mode, size) + check_closed + + name, prefix = split_name name + + header = Gem::Package::TarHeader.new(:name => name, :mode => mode, + :size => size, :prefix => prefix).to_s + + @io.write header + os = BoundedStream.new @io, size + + yield os if block_given? + + min_padding = size - os.written + @io.write("\0" * min_padding) + + remainder = (512 - (size % 512)) % 512 + @io.write("\0" * remainder) + + self + end + + def check_closed + raise IOError, "closed #{self.class}" if closed? + end + + def close + check_closed + + @io.write "\0" * 1024 + flush + + @closed = true + end + + def closed? + @closed + end + + def flush + check_closed + + @io.flush if @io.respond_to? :flush + end + + def mkdir(name, mode) + check_closed + + name, prefix = split_name(name) + + header = Gem::Package::TarHeader.new :name => name, :mode => mode, + :typeflag => "5", :size => 0, + :prefix => prefix + + @io.write header + + self + end + + def split_name(name) # :nodoc: + raise Gem::Package::TooLongFileName if name.size > 256 + + if name.size <= 100 then + prefix = "" + else + parts = name.split(/\//) + newname = parts.pop + nxt = "" + + loop do + nxt = parts.pop + break if newname.size + 1 + nxt.size > 100 + newname = nxt + "/" + newname + end + + prefix = (parts + [nxt]).join "/" + name = newname + + if name.size > 100 or prefix.size > 155 then + raise Gem::Package::TooLongFileName + end + end + + return name, prefix + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/platform.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/platform.rb new file mode 100644 index 0000000000..5e932cd592 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/platform.rb @@ -0,0 +1,195 @@ +require 'rubygems' + +## +# Available list of platforms for targeting Gem installations. + +class Gem::Platform + + @local = nil + + attr_accessor :cpu + + attr_accessor :os + + attr_accessor :version + + DEPRECATED_CONSTS = [ + :DARWIN, + :LINUX_586, + :MSWIN32, + :PPC_DARWIN, + :WIN32, + :X86_LINUX + ] + + def self.const_missing(name) # TODO remove six months from 2007/12 + if DEPRECATED_CONSTS.include? name then + raise NameError, "#{name} has been removed, use CURRENT instead" + else + super + end + end + + def self.local + arch = Gem::ConfigMap[:arch] + arch = "#{arch}_60" if arch =~ /mswin32$/ + @local ||= new(arch) + end + + def self.match(platform) + Gem.platforms.any? do |local_platform| + platform.nil? or local_platform == platform or + (local_platform != Gem::Platform::RUBY and local_platform =~ platform) + end + end + + def self.new(arch) # :nodoc: + case arch + when Gem::Platform::CURRENT then + Gem::Platform.local + when Gem::Platform::RUBY, nil, '' then + Gem::Platform::RUBY + else + super + end + end + + def initialize(arch) + case arch + when Array then + @cpu, @os, @version = arch + when String then + arch = arch.split '-' + + if arch.length > 2 and arch.last !~ /\d/ then # reassemble x86-linux-gnu + extra = arch.pop + arch.last << "-#{extra}" + end + + cpu = arch.shift + + @cpu = case cpu + when /i\d86/ then 'x86' + else cpu + end + + if arch.length == 2 and arch.last =~ /^\d+$/ then # for command-line + @os, @version = arch + return + end + + os, = arch + @cpu, os = nil, cpu if os.nil? # legacy jruby + + @os, @version = case os + when /aix(\d+)/ then [ 'aix', $1 ] + when /cygwin/ then [ 'cygwin', nil ] + when /darwin(\d+)?/ then [ 'darwin', $1 ] + when /freebsd(\d+)/ then [ 'freebsd', $1 ] + when /hpux(\d+)/ then [ 'hpux', $1 ] + when /^java$/, /^jruby$/ then [ 'java', nil ] + when /^java([\d.]*)/ then [ 'java', $1 ] + when /linux/ then [ 'linux', $1 ] + when /mingw32/ then [ 'mingw32', nil ] + when /(mswin\d+)(\_(\d+))?/ then + os, version = $1, $3 + @cpu = 'x86' if @cpu.nil? and os =~ /32$/ + [os, version] + when /netbsdelf/ then [ 'netbsdelf', nil ] + when /openbsd(\d+\.\d+)/ then [ 'openbsd', $1 ] + when /solaris(\d+\.\d+)/ then [ 'solaris', $1 ] + # test + when /^(\w+_platform)(\d+)/ then [ $1, $2 ] + else [ 'unknown', nil ] + end + when Gem::Platform then + @cpu = arch.cpu + @os = arch.os + @version = arch.version + else + raise ArgumentError, "invalid argument #{arch.inspect}" + end + end + + def inspect + "#<%s:0x%x @cpu=%p, @os=%p, @version=%p>" % [self.class, object_id, *to_a] + end + + def to_a + [@cpu, @os, @version] + end + + def to_s + to_a.compact.join '-' + end + + ## + # Is +other+ equal to this platform? Two platforms are equal if they have + # the same CPU, OS and version. + + def ==(other) + self.class === other and + @cpu == other.cpu and @os == other.os and @version == other.version + end + + ## + # Does +other+ match this platform? Two platforms match if they have the + # same CPU, or either has a CPU of 'universal', they have the same OS, and + # they have the same version, or either has no version. + + def ===(other) + return nil unless Gem::Platform === other + + # cpu + (@cpu == 'universal' or other.cpu == 'universal' or @cpu == other.cpu) and + + # os + @os == other.os and + + # version + (@version.nil? or other.version.nil? or @version == other.version) + end + + ## + # Does +other+ match this platform? If +other+ is a String it will be + # converted to a Gem::Platform first. See #=== for matching rules. + + def =~(other) + case other + when Gem::Platform then # nop + when String then + # This data is from http://gems.rubyforge.org/gems/yaml on 19 Aug 2007 + other = case other + when /^i686-darwin(\d)/ then ['x86', 'darwin', $1] + when /^i\d86-linux/ then ['x86', 'linux', nil] + when 'java', 'jruby' then [nil, 'java', nil] + when /mswin32(\_(\d+))?/ then ['x86', 'mswin32', $2] + when 'powerpc-darwin' then ['powerpc', 'darwin', nil] + when /powerpc-darwin(\d)/ then ['powerpc', 'darwin', $1] + when /sparc-solaris2.8/ then ['sparc', 'solaris', '2.8'] + when /universal-darwin(\d)/ then ['universal', 'darwin', $1] + else other + end + + other = Gem::Platform.new other + else + return nil + end + + self === other + end + + ## + # A pure-ruby gem that may use Gem::Specification#extensions to build + # binary files. + + RUBY = 'ruby' + + ## + # A platform-specific gem that is built for the packaging ruby's platform. + # This will be replaced with Gem::Platform::local. + + CURRENT = 'current' + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/remote_fetcher.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/remote_fetcher.rb new file mode 100644 index 0000000000..7a50365b3b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/remote_fetcher.rb @@ -0,0 +1,353 @@ +require 'net/http' +require 'stringio' +require 'uri' + +require 'rubygems' + +## +# RemoteFetcher handles the details of fetching gems and gem information from +# a remote source. + +class Gem::RemoteFetcher + + include Gem::UserInteraction + + ## + # A FetchError exception wraps up the various possible IO and HTTP failures + # that could happen while downloading from the internet. + + class FetchError < Gem::Exception + + ## + # The URI which was being accessed when the exception happened. + + attr_accessor :uri + + def initialize(message, uri) + super message + @uri = uri + end + + def to_s # :nodoc: + "#{super} (#{uri})" + end + + end + + @fetcher = nil + + ## + # Cached RemoteFetcher instance. + + def self.fetcher + @fetcher ||= self.new Gem.configuration[:http_proxy] + end + + ## + # Initialize a remote fetcher using the source URI and possible proxy + # information. + # + # +proxy+ + # * [String]: explicit specification of proxy; overrides any environment + # variable setting + # * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER, + # HTTP_PROXY_PASS) + # * :no_proxy: ignore environment variables and _don't_ use a proxy + + def initialize(proxy) + Socket.do_not_reverse_lookup = true + + @connections = {} + @requests = Hash.new 0 + @proxy_uri = + case proxy + when :no_proxy then nil + when nil then get_proxy_from_env + when URI::HTTP then proxy + else URI.parse(proxy) + end + end + + ## + # Moves the gem +spec+ from +source_uri+ to the cache dir unless it is + # already there. If the source_uri is local the gem cache dir copy is + # always replaced. + + def download(spec, source_uri, install_dir = Gem.dir) + cache_dir = File.join install_dir, 'cache' + gem_file_name = "#{spec.full_name}.gem" + local_gem_path = File.join cache_dir, gem_file_name + + FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir + + source_uri = URI.parse source_uri unless URI::Generic === source_uri + scheme = source_uri.scheme + + # URI.parse gets confused by MS Windows paths with forward slashes. + scheme = nil if scheme =~ /^[a-z]$/i + + case scheme + when 'http' then + unless File.exist? local_gem_path then + begin + say "Downloading gem #{gem_file_name}" if + Gem.configuration.really_verbose + + remote_gem_path = source_uri + "gems/#{gem_file_name}" + + gem = Gem::RemoteFetcher.fetcher.fetch_path remote_gem_path + rescue Gem::RemoteFetcher::FetchError + raise if spec.original_platform == spec.platform + + alternate_name = "#{spec.original_name}.gem" + + say "Failed, downloading gem #{alternate_name}" if + Gem.configuration.really_verbose + + remote_gem_path = source_uri + "gems/#{alternate_name}" + + gem = Gem::RemoteFetcher.fetcher.fetch_path remote_gem_path + end + + File.open local_gem_path, 'wb' do |fp| + fp.write gem + end + end + when nil, 'file' then # TODO test for local overriding cache + begin + FileUtils.cp source_uri.to_s, local_gem_path + rescue Errno::EACCES + local_gem_path = source_uri.to_s + end + + say "Using local gem #{local_gem_path}" if + Gem.configuration.really_verbose + else + raise Gem::InstallError, "unsupported URI scheme #{source_uri.scheme}" + end + + local_gem_path + end + + ## + # Downloads +uri+ and returns it as a String. + + def fetch_path(uri) + open_uri_or_path(uri) do |input| + input.read + end + rescue FetchError + raise + rescue Timeout::Error + raise FetchError.new('timed out', uri) + rescue IOError, SocketError, SystemCallError => e + raise FetchError.new("#{e.class}: #{e}", uri) + rescue => e + raise FetchError.new("#{e.class}: #{e}", uri) + end + + ## + # Returns the size of +uri+ in bytes. + + def fetch_size(uri) + return File.size(get_file_uri_path(uri)) if file_uri? uri + + uri = URI.parse uri unless URI::Generic === uri + + raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri + + response = request uri, Net::HTTP::Head + + case response + when Net::HTTPOK then + else + raise FetchError.new("bad response #{response.message} #{response.code}", uri) + end + + if response['content-length'] then + return response['content-length'].to_i + else + response = http.get uri.request_uri + return response.body.size + end + + rescue SocketError, SystemCallError, Timeout::Error => e + raise FetchError.new("#{e.message} (#{e.class})\n\tfetching size", uri) + end + + private + + def escape(str) + return unless str + URI.escape(str) + end + + def unescape(str) + return unless str + URI.unescape(str) + end + + ## + # Returns an HTTP proxy URI if one is set in the environment variables. + + def get_proxy_from_env + env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY'] + + return nil if env_proxy.nil? or env_proxy.empty? + + uri = URI.parse env_proxy + + if uri and uri.user.nil? and uri.password.nil? then + # Probably we have http_proxy_* variables? + uri.user = escape(ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER']) + uri.password = escape(ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS']) + end + + uri + end + + ## + # Normalize the URI by adding "http://" if it is missing. + + def normalize_uri(uri) + (uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}" + end + + ## + # Creates or an HTTP connection based on +uri+, or retrieves an existing + # connection, using a proxy if needed. + + def connection_for(uri) + net_http_args = [uri.host, uri.port] + + if @proxy_uri then + net_http_args += [ + @proxy_uri.host, + @proxy_uri.port, + @proxy_uri.user, + @proxy_uri.password + ] + end + + connection_id = net_http_args.join ':' + @connections[connection_id] ||= Net::HTTP.new(*net_http_args) + connection = @connections[connection_id] + + if uri.scheme == 'https' and not connection.started? then + http_obj.use_ssl = true + http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE + end + + connection.start unless connection.started? + + connection + end + + ## + # Read the data from the (source based) URI, but if it is a file:// URI, + # read from the filesystem instead. + + def open_uri_or_path(uri, depth = 0, &block) + if file_uri?(uri) + open(get_file_uri_path(uri), &block) + else + uri = URI.parse uri unless URI::Generic === uri + + response = request uri + + case response + when Net::HTTPOK then + block.call(StringIO.new(response.body)) if block + when Net::HTTPRedirection then + raise FetchError.new('too many redirects', uri) if depth > 10 + + open_uri_or_path(response['Location'], depth + 1, &block) + else + raise FetchError.new("bad response #{response.message} #{response.code}", uri) + end + end + end + + ## + # Performs a Net::HTTP request of type +request_class+ on +uri+ returning + # a Net::HTTP response object. request maintains a table of persistent + # connections to reduce connect overhead. + + def request(uri, request_class = Net::HTTP::Get) + request = request_class.new uri.request_uri + + unless uri.nil? || uri.user.nil? || uri.user.empty? then + request.basic_auth uri.user, uri.password + end + + ua = "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}" + ua << " Ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}" + ua << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL + ua << ")" + + request.add_field 'User-Agent', ua + request.add_field 'Connection', 'keep-alive' + request.add_field 'Keep-Alive', '30' + + connection = connection_for uri + + retried = false + bad_response = false + + # HACK work around EOFError bug in Net::HTTP + # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible + # to install gems. + begin + @requests[connection.object_id] += 1 + response = connection.request request + say "#{request.method} #{response.code} #{response.message}: #{uri}" if + Gem.configuration.really_verbose + rescue Net::HTTPBadResponse + reset connection + + raise FetchError.new('too many bad responses', uri) if bad_response + + bad_response = true + retry + rescue EOFError, Errno::ECONNABORTED, Errno::ECONNRESET + requests = @requests[connection.object_id] + say "connection reset after #{requests} requests, retrying" if + Gem.configuration.really_verbose + + raise FetchError.new('too many connection resets', uri) if retried + + reset connection + + retried = true + retry + end + + response + end + + ## + # Resets HTTP connection +connection+. + + def reset(connection) + @requests.delete connection.object_id + + connection.finish + connection.start + end + + ## + # Checks if the provided string is a file:// URI. + + def file_uri?(uri) + uri =~ %r{\Afile://} + end + + ## + # Given a file:// URI, returns its local path. + + def get_file_uri_path(uri) + uri.sub(%r{\Afile://}, '') + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/remote_installer.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/remote_installer.rb new file mode 100644 index 0000000000..2a86972357 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/remote_installer.rb @@ -0,0 +1,202 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'fileutils' +require 'yaml' + +require 'rubygems' +require 'rubygems/installer' +require 'rubygems/source_info_cache' + +require 'sources' + +module Gem + class DependencyError < Gem::Exception; end + class GemNotFoundException < Gem::Exception; end + class RemoteInstallationCancelled < Gem::Exception; end + class RemoteInstallationSkipped < Gem::Exception; end + + class RemoteInstaller + + include UserInteraction + + # options[:http_proxy]:: + # * [String]: explicit specification of proxy; overrides any + # environment variable setting + # * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER, HTTP_PROXY_PASS) + # * :no_proxy: ignore environment variables and _don't_ + # use a proxy + # + # * :cache_dir: override where downloaded gems are cached. + def initialize(options={}) + @options = options + @source_index_hash = nil + end + + # This method will install package_name onto the local system. + # + # gem_name:: + # [String] Name of the Gem to install + # + # version_requirement:: + # [default = "> 0.0.0"] Gem version requirement to install + # + # Returns:: + # an array of Gem::Specification objects, one for each gem installed. + # + def install(gem_name, version_requirement = "> 0.0.0", force=false, + install_dir=Gem.dir, install_stub=true) + unless version_requirement.respond_to?(:satisfied_by?) + version_requirement = Version::Requirement.new [version_requirement] + end + installed_gems = [] + begin + spec, source = find_gem_to_install(gem_name, version_requirement) + dependencies = find_dependencies_not_installed(spec.dependencies) + + installed_gems << install_dependencies(dependencies, force, install_dir) + + cache_dir = @options[:cache_dir] || File.join(install_dir, "cache") + destination_file = File.join(cache_dir, spec.full_name + ".gem") + + download_gem(destination_file, source, spec) + + installer = new_installer(destination_file) + installed_gems.unshift installer.install(force, install_dir, install_stub) + rescue RemoteInstallationSkipped => e + puts e.message + end + installed_gems.flatten + end + + # Return a hash mapping the available source names to the source + # index of that source. + def source_index_hash + return @source_index_hash if @source_index_hash + @source_index_hash = {} + Gem::SourceInfoCache.cache_data.each do |source_uri, sic_entry| + @source_index_hash[source_uri] = sic_entry.source_index + end + @source_index_hash + end + + # Finds the Gem::Specification objects and the corresponding source URI + # for gems matching +gem_name+ and +version_requirement+ + def specs_n_sources_matching(gem_name, version_requirement) + specs_n_sources = [] + + source_index_hash.each do |source_uri, source_index| + specs = source_index.search(/^#{Regexp.escape gem_name}$/i, + version_requirement) + # TODO move to SourceIndex#search? + ruby_version = Gem::Version.new RUBY_VERSION + specs = specs.select do |spec| + spec.required_ruby_version.nil? or + spec.required_ruby_version.satisfied_by? ruby_version + end + specs.each { |spec| specs_n_sources << [spec, source_uri] } + end + + if specs_n_sources.empty? then + raise GemNotFoundException, "Could not find #{gem_name} (#{version_requirement}) in any repository" + end + + specs_n_sources = specs_n_sources.sort_by { |gs,| gs.version }.reverse + + specs_n_sources + end + + # Find a gem to be installed by interacting with the user. + def find_gem_to_install(gem_name, version_requirement) + specs_n_sources = specs_n_sources_matching gem_name, version_requirement + + top_3_versions = specs_n_sources.map{|gs| gs.first.version}.uniq[0..3] + specs_n_sources.reject!{|gs| !top_3_versions.include?(gs.first.version)} + + binary_gems = specs_n_sources.reject { |item| + item[0].platform.nil? || item[0].platform==Platform::RUBY + } + + # only non-binary gems...return latest + return specs_n_sources.first if binary_gems.empty? + + list = specs_n_sources.collect { |spec, source_uri| + "#{spec.name} #{spec.version} (#{spec.platform})" + } + + list << "Skip this gem" + list << "Cancel installation" + + string, index = choose_from_list( + "Select which gem to install for your platform (#{RUBY_PLATFORM})", + list) + + if index.nil? or index == (list.size - 1) then + raise RemoteInstallationCancelled, "Installation of #{gem_name} cancelled." + end + + if index == (list.size - 2) then + raise RemoteInstallationSkipped, "Installation of #{gem_name} skipped." + end + + specs_n_sources[index] + end + + def find_dependencies_not_installed(dependencies) + to_install = [] + dependencies.each do |dependency| + srcindex = Gem::SourceIndex.from_installed_gems + matches = srcindex.find_name(dependency.name, dependency.requirement_list) + to_install.push dependency if matches.empty? + end + to_install + end + + # Install all the given dependencies. Returns an array of + # Gem::Specification objects, one for each dependency installed. + # + # TODO: For now, we recursively install, but this is not the right + # way to do things (e.g. if a package fails to download, we + # shouldn't install anything). + def install_dependencies(dependencies, force, install_dir) + return if @options[:ignore_dependencies] + installed_gems = [] + dependencies.each do |dep| + if @options[:include_dependencies] || + ask_yes_no("Install required dependency #{dep.name}?", true) + remote_installer = RemoteInstaller.new @options + installed_gems << remote_installer.install(dep.name, + dep.version_requirements, + force, install_dir) + elsif force then + # ignore + else + raise DependencyError, "Required dependency #{dep.name} not installed" + end + end + installed_gems + end + + def download_gem(destination_file, source, spec) + return if File.exist? destination_file + uri = source + "/gems/#{spec.full_name}.gem" + response = Gem::RemoteFetcher.fetcher.fetch_path uri + write_gem_to_file response, destination_file + end + + def write_gem_to_file(body, destination_file) + FileUtils.mkdir_p(File.dirname(destination_file)) unless File.exist?(destination_file) + File.open(destination_file, 'wb') do |out| + out.write(body) + end + end + + def new_installer(gem) + return Installer.new(gem, @options) + end + end + +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/require_paths_builder.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/require_paths_builder.rb new file mode 100644 index 0000000000..fe4f593bf4 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/require_paths_builder.rb @@ -0,0 +1,15 @@ +module Gem + module RequirePathsBuilder + def write_require_paths_file_if_needed(spec = @spec, gem_home = @gem_home) + return if spec.require_paths == ["lib"] && (spec.bindir.nil? || spec.bindir == "bin") + file_name = File.join(gem_home, 'gems', "#{@spec.full_name}", ".require_paths") + file_name.untaint + File.open(file_name, "w") do |file| + spec.require_paths.each do |path| + file.puts path + end + file.puts spec.bindir if spec.bindir + end + end + end +end \ No newline at end of file diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/requirement.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/requirement.rb new file mode 100644 index 0000000000..c9128b5ebc --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/requirement.rb @@ -0,0 +1,163 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems/version' + +## +# Requirement version includes a prefaced comparator in addition +# to a version number. +# +# A Requirement object can actually contain multiple, er, +# requirements, as in (> 1.2, < 2.0). + +class Gem::Requirement + + include Comparable + + attr_reader :requirements + + OPS = { + "=" => lambda { |v, r| v == r }, + "!=" => lambda { |v, r| v != r }, + ">" => lambda { |v, r| v > r }, + "<" => lambda { |v, r| v < r }, + ">=" => lambda { |v, r| v >= r }, + "<=" => lambda { |v, r| v <= r }, + "~>" => lambda { |v, r| v >= r && v < r.bump } + } + + OP_RE = /#{OPS.keys.map{ |k| Regexp.quote k }.join '|'}/o + + ## + # Factory method to create a Gem::Requirement object. Input may be a + # Version, a String, or nil. Intended to simplify client code. + # + # If the input is "weird", the default version requirement is returned. + + def self.create(input) + case input + when Gem::Requirement then + input + when Gem::Version, Array then + new input + else + if input.respond_to? :to_str then + self.new [input.to_str] + else + self.default + end + end + end + + ## + # A default "version requirement" can surely _only_ be '>= 0'. + #-- + # This comment once said: + # + # "A default "version requirement" can surely _only_ be '> 0'." + + def self.default + self.new ['>= 0'] + end + + ## + # Constructs a Requirement from +requirements+ which can be a String, a + # Gem::Version, or an Array of those. See parse for details on the + # formatting of requirement strings. + + def initialize(requirements) + @requirements = case requirements + when Array then + requirements.map do |requirement| + parse(requirement) + end + else + [parse(requirements)] + end + @version = nil # Avoid warnings. + end + + ## + # Marshal raw requirements, rather than the full object + + def marshal_dump # :nodoc: + [@requirements] + end + + ## + # Load custom marshal format + + def marshal_load(array) # :nodoc: + @requirements = array[0] + @version = nil + end + + def to_s # :nodoc: + as_list.join(", ") + end + + def as_list + normalize + @requirements.collect { |req| + "#{req[0]} #{req[1]}" + } + end + + def normalize + return if not defined? @version or @version.nil? + @requirements = [parse(@version)] + @nums = nil + @version = nil + @op = nil + end + + ## + # True if this requirement satisfied by the Gem::Version +version+. + + def satisfied_by?(version) + normalize + @requirements.all? { |op, rv| satisfy?(op, version, rv) } + end + + ## + # Is "+version+ +op+ +required_version+" satisfied? + + def satisfy?(op, version, required_version) + OPS[op].call(version, required_version) + end + + ## + # Parse the version requirement obj returning the operator and version. + # + # The requirement can be a String or a Gem::Version. A String can be an + # operator (<, <=, =, =>, >, !=, ~>), a version number, or both, operator + # first. + + def parse(obj) + case obj + when /^\s*(#{OP_RE})\s*([0-9.]+)\s*$/o then + [$1, Gem::Version.new($2)] + when /^\s*([0-9.]+)\s*$/ then + ['=', Gem::Version.new($1)] + when /^\s*(#{OP_RE})\s*$/o then + [$1, Gem::Version.new('0')] + when Gem::Version then + ['=', obj] + else + fail ArgumentError, "Illformed requirement [#{obj.inspect}]" + end + end + + def <=>(other) # :nodoc: + to_s <=> other.to_s + end + + def hash # :nodoc: + to_s.hash + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/rubygems_version.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/rubygems_version.rb new file mode 100644 index 0000000000..07cdb5c369 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/rubygems_version.rb @@ -0,0 +1,6 @@ +# DO NOT EDIT +# This file is auto-generated by build scripts. +# See: rake update_version +module Gem + RubyGemsVersion = '1.2.0' +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/security.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/security.rb new file mode 100644 index 0000000000..42a3cf8778 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/security.rb @@ -0,0 +1,786 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems' +require 'rubygems/gem_openssl' + +# = Signed Gems README +# +# == Table of Contents +# * Overview +# * Walkthrough +# * Command-Line Options +# * OpenSSL Reference +# * Bugs/TODO +# * About the Author +# +# == Overview +# +# Gem::Security implements cryptographic signatures in RubyGems. The section +# below is a step-by-step guide to using signed gems and generating your own. +# +# == Walkthrough +# +# In order to start signing your gems, you'll need to build a private key and +# a self-signed certificate. Here's how: +# +# # build a private key and certificate for gemmaster@example.com +# $ gem cert --build gemmaster@example.com +# +# This could take anywhere from 5 seconds to 10 minutes, depending on the +# speed of your computer (public key algorithms aren't exactly the speediest +# crypto algorithms in the world). When it's finished, you'll see the files +# "gem-private_key.pem" and "gem-public_cert.pem" in the current directory. +# +# First things first: take the "gem-private_key.pem" file and move it +# somewhere private, preferably a directory only you have access to, a floppy +# (yuck!), a CD-ROM, or something comparably secure. Keep your private key +# hidden; if it's compromised, someone can sign packages as you (note: PKI has +# ways of mitigating the risk of stolen keys; more on that later). +# +# Now, let's sign an existing gem. I'll be using my Imlib2-Ruby bindings, but +# you can use whatever gem you'd like. Open up your existing gemspec file and +# add the following lines: +# +# # signing key and certificate chain +# s.signing_key = '/mnt/floppy/gem-private_key.pem' +# s.cert_chain = ['gem-public_cert.pem'] +# +# (Be sure to replace "/mnt/floppy" with the ultra-secret path to your private +# key). +# +# After that, go ahead and build your gem as usual. Congratulations, you've +# just built your first signed gem! If you peek inside your gem file, you'll +# see a couple of new files have been added: +# +# $ tar tf tar tf Imlib2-Ruby-0.5.0.gem +# data.tar.gz +# data.tar.gz.sig +# metadata.gz +# metadata.gz.sig +# +# Now let's verify the signature. Go ahead and install the gem, but add the +# following options: "-P HighSecurity", like this: +# +# # install the gem with using the security policy "HighSecurity" +# $ sudo gem install Imlib2-Ruby-0.5.0.gem -P HighSecurity +# +# The -P option sets your security policy -- we'll talk about that in just a +# minute. Eh, what's this? +# +# Attempting local installation of 'Imlib2-Ruby-0.5.0.gem' +# ERROR: Error installing gem Imlib2-Ruby-0.5.0.gem[.gem]: Couldn't +# verify data signature: Untrusted Signing Chain Root: cert = +# '/CN=gemmaster/DC=example/DC=com', error = 'path +# "/root/.rubygems/trust/cert-15dbb43a6edf6a70a85d4e784e2e45312cff7030.pem" +# does not exist' +# +# The culprit here is the security policy. RubyGems has several different +# security policies. Let's take a short break and go over the security +# policies. Here's a list of the available security policies, and a brief +# description of each one: +# +# * NoSecurity - Well, no security at all. Signed packages are treated like +# unsigned packages. +# * LowSecurity - Pretty much no security. If a package is signed then +# RubyGems will make sure the signature matches the signing +# certificate, and that the signing certificate hasn't expired, but +# that's it. A malicious user could easily circumvent this kind of +# security. +# * MediumSecurity - Better than LowSecurity and NoSecurity, but still +# fallible. Package contents are verified against the signing +# certificate, and the signing certificate is checked for validity, +# and checked against the rest of the certificate chain (if you don't +# know what a certificate chain is, stay tuned, we'll get to that). +# The biggest improvement over LowSecurity is that MediumSecurity +# won't install packages that are signed by untrusted sources. +# Unfortunately, MediumSecurity still isn't totally secure -- a +# malicious user can still unpack the gem, strip the signatures, and +# distribute the gem unsigned. +# * HighSecurity - Here's the ###### that got us into this mess. +# The HighSecurity policy is identical to the MediumSecurity policy, +# except that it does not allow unsigned gems. A malicious user +# doesn't have a whole lot of options here; he can't modify the +# package contents without invalidating the signature, and he can't +# modify or remove signature or the signing certificate chain, or +# RubyGems will simply refuse to install the package. Oh well, maybe +# he'll have better luck causing problems for CPAN users instead :). +# +# So, the reason RubyGems refused to install our shiny new signed gem was +# because it was from an untrusted source. Well, my code is infallible +# (hah!), so I'm going to add myself as a trusted source. +# +# Here's how: +# +# # add trusted certificate +# gem cert --add gem-public_cert.pem +# +# I've added my public certificate as a trusted source. Now I can install +# packages signed my private key without any hassle. Let's try the install +# command above again: +# +# # install the gem with using the HighSecurity policy (and this time +# # without any shenanigans) +# $ sudo gem install Imlib2-Ruby-0.5.0.gem -P HighSecurity +# +# This time RubyGems should accept your signed package and begin installing. +# While you're waiting for RubyGems to work it's magic, have a look at some of +# the other security commands: +# +# Usage: gem cert [options] +# +# Options: +# -a, --add CERT Add a trusted certificate. +# -l, --list List trusted certificates. +# -r, --remove STRING Remove trusted certificates containing STRING. +# -b, --build EMAIL_ADDR Build private key and self-signed certificate +# for EMAIL_ADDR. +# -C, --certificate CERT Certificate for --sign command. +# -K, --private-key KEY Private key for --sign command. +# -s, --sign NEWCERT Sign a certificate with my key and certificate. +# +# (By the way, you can pull up this list any time you'd like by typing "gem +# cert --help") +# +# Hmm. We've already covered the "--build" option, and the "--add", "--list", +# and "--remove" commands seem fairly straightforward; they allow you to add, +# list, and remove the certificates in your trusted certificate list. But +# what's with this "--sign" option? +# +# To answer that question, let's take a look at "certificate chains", a +# concept I mentioned earlier. There are a couple of problems with +# self-signed certificates: first of all, self-signed certificates don't offer +# a whole lot of security. Sure, the certificate says Yukihiro Matsumoto, but +# how do I know it was actually generated and signed by matz himself unless he +# gave me the certificate in person? +# +# The second problem is scalability. Sure, if there are 50 gem authors, then +# I have 50 trusted certificates, no problem. What if there are 500 gem +# authors? 1000? Having to constantly add new trusted certificates is a +# pain, and it actually makes the trust system less secure by encouraging +# RubyGems users to blindly trust new certificates. +# +# Here's where certificate chains come in. A certificate chain establishes an +# arbitrarily long chain of trust between an issuing certificate and a child +# certificate. So instead of trusting certificates on a per-developer basis, +# we use the PKI concept of certificate chains to build a logical hierarchy of +# trust. Here's a hypothetical example of a trust hierarchy based (roughly) +# on geography: +# +# +# -------------------------- +# | rubygems@rubyforge.org | +# -------------------------- +# | +# ----------------------------------- +# | | +# ---------------------------- ----------------------------- +# | seattle.rb@zenspider.com | | dcrubyists@richkilmer.com | +# ---------------------------- ----------------------------- +# | | | | +# --------------- ---------------- ----------- -------------- +# | alf@seattle | | bob@portland | | pabs@dc | | tomcope@dc | +# --------------- ---------------- ----------- -------------- +# +# +# Now, rather than having 4 trusted certificates (one for alf@seattle, +# bob@portland, pabs@dc, and tomecope@dc), a user could actually get by with 1 +# certificate: the "rubygems@rubyforge.org" certificate. Here's how it works: +# +# I install "Alf2000-Ruby-0.1.0.gem", a package signed by "alf@seattle". I've +# never heard of "alf@seattle", but his certificate has a valid signature from +# the "seattle.rb@zenspider.com" certificate, which in turn has a valid +# signature from the "rubygems@rubyforge.org" certificate. Voila! At this +# point, it's much more reasonable for me to trust a package signed by +# "alf@seattle", because I can establish a chain to "rubygems@rubyforge.org", +# which I do trust. +# +# And the "--sign" option allows all this to happen. A developer creates +# their build certificate with the "--build" option, then has their +# certificate signed by taking it with them to their next regional Ruby meetup +# (in our hypothetical example), and it's signed there by the person holding +# the regional RubyGems signing certificate, which is signed at the next +# RubyConf by the holder of the top-level RubyGems certificate. At each point +# the issuer runs the same command: +# +# # sign a certificate with the specified key and certificate +# # (note that this modifies client_cert.pem!) +# $ gem cert -K /mnt/floppy/issuer-priv_key.pem -C issuer-pub_cert.pem +# --sign client_cert.pem +# +# Then the holder of issued certificate (in this case, our buddy +# "alf@seattle"), can start using this signed certificate to sign RubyGems. +# By the way, in order to let everyone else know about his new fancy signed +# certificate, "alf@seattle" would change his gemspec file to look like this: +# +# # signing key (still kept in an undisclosed location!) +# s.signing_key = '/mnt/floppy/alf-private_key.pem' +# +# # certificate chain (includes the issuer certificate now too) +# s.cert_chain = ['/home/alf/doc/seattlerb-public_cert.pem', +# '/home/alf/doc/alf_at_seattle-public_cert.pem'] +# +# Obviously, this RubyGems trust infrastructure doesn't exist yet. Also, in +# the "real world" issuers actually generate the child certificate from a +# certificate request, rather than sign an existing certificate. And our +# hypothetical infrastructure is missing a certificate revocation system. +# These are that can be fixed in the future... +# +# I'm sure your new signed gem has finished installing by now (unless you're +# installing rails and all it's dependencies, that is ;D). At this point you +# should know how to do all of these new and interesting things: +# +# * build a gem signing key and certificate +# * modify your existing gems to support signing +# * adjust your security policy +# * modify your trusted certificate list +# * sign a certificate +# +# If you've got any questions, feel free to contact me at the email address +# below. The next couple of sections +# +# +# == Command-Line Options +# +# Here's a brief summary of the certificate-related command line options: +# +# gem install +# -P, --trust-policy POLICY Specify gem trust policy. +# +# gem cert +# -a, --add CERT Add a trusted certificate. +# -l, --list List trusted certificates. +# -r, --remove STRING Remove trusted certificates containing +# STRING. +# -b, --build EMAIL_ADDR Build private key and self-signed +# certificate for EMAIL_ADDR. +# -C, --certificate CERT Certificate for --sign command. +# -K, --private-key KEY Private key for --sign command. +# -s, --sign NEWCERT Sign a certificate with my key and +# certificate. +# +# A more detailed description of each options is available in the walkthrough +# above. +# +# +# == OpenSSL Reference +# +# The .pem files generated by --build and --sign are just basic OpenSSL PEM +# files. Here's a couple of useful commands for manipulating them: +# +# # convert a PEM format X509 certificate into DER format: +# # (note: Windows .cer files are X509 certificates in DER format) +# $ openssl x509 -in input.pem -outform der -out output.der +# +# # print out the certificate in a human-readable format: +# $ openssl x509 -in input.pem -noout -text +# +# And you can do the same thing with the private key file as well: +# +# # convert a PEM format RSA key into DER format: +# $ openssl rsa -in input_key.pem -outform der -out output_key.der +# +# # print out the key in a human readable format: +# $ openssl rsa -in input_key.pem -noout -text +# +# == Bugs/TODO +# +# * There's no way to define a system-wide trust list. +# * custom security policies (from a YAML file, etc) +# * Simple method to generate a signed certificate request +# * Support for OCSP, SCVP, CRLs, or some other form of cert +# status check (list is in order of preference) +# * Support for encrypted private keys +# * Some sort of semi-formal trust hierarchy (see long-winded explanation +# above) +# * Path discovery (for gem certificate chains that don't have a self-signed +# root) -- by the way, since we don't have this, THE ROOT OF THE CERTIFICATE +# CHAIN MUST BE SELF SIGNED if Policy#verify_root is true (and it is for the +# MediumSecurity and HighSecurity policies) +# * Better explanation of X509 naming (ie, we don't have to use email +# addresses) +# * Possible alternate signing mechanisms (eg, via PGP). this could be done +# pretty easily by adding a :signing_type attribute to the gemspec, then add +# the necessary support in other places +# * Honor AIA field (see note about OCSP above) +# * Maybe honor restriction extensions? +# * Might be better to store the certificate chain as a PKCS#7 or PKCS#12 +# file, instead of an array embedded in the metadata. ideas? +# * Possibly embed signature and key algorithms into metadata (right now +# they're assumed to be the same as what's set in Gem::Security::OPT) +# +# == About the Author +# +# Paul Duncan +# http://pablotron.org/ + +module Gem::Security + + class Exception < Gem::Exception; end + + # + # default options for most of the methods below + # + OPT = { + # private key options + :key_algo => Gem::SSL::PKEY_RSA, + :key_size => 2048, + + # public cert options + :cert_age => 365 * 24 * 3600, # 1 year + :dgst_algo => Gem::SSL::DIGEST_SHA1, + + # x509 certificate extensions + :cert_exts => { + 'basicConstraints' => 'CA:FALSE', + 'subjectKeyIdentifier' => 'hash', + 'keyUsage' => 'keyEncipherment,dataEncipherment,digitalSignature', + }, + + # save the key and cert to a file in build_self_signed_cert()? + :save_key => true, + :save_cert => true, + + # if you define either of these, then they'll be used instead of + # the output_fmt macro below + :save_key_path => nil, + :save_cert_path => nil, + + # output name format for self-signed certs + :output_fmt => 'gem-%s.pem', + :munge_re => Regexp.new(/[^a-z0-9_.-]+/), + + # output directory for trusted certificate checksums + :trust_dir => File::join(Gem.user_home, '.gem', 'trust'), + + # default permissions for trust directory and certs + :perms => { + :trust_dir => 0700, + :trusted_cert => 0600, + :signing_cert => 0600, + :signing_key => 0600, + }, + } + + # + # A Gem::Security::Policy object encapsulates the settings for verifying + # signed gem files. This is the base class. You can either declare an + # instance of this or use one of the preset security policies below. + # + class Policy + attr_accessor :verify_data, :verify_signer, :verify_chain, + :verify_root, :only_trusted, :only_signed + + # + # Create a new Gem::Security::Policy object with the given mode and + # options. + # + def initialize(policy = {}, opt = {}) + # set options + @opt = Gem::Security::OPT.merge(opt) + + # build policy + policy.each_pair do |key, val| + case key + when :verify_data then @verify_data = val + when :verify_signer then @verify_signer = val + when :verify_chain then @verify_chain = val + when :verify_root then @verify_root = val + when :only_trusted then @only_trusted = val + when :only_signed then @only_signed = val + end + end + end + + # + # Get the path to the file for this cert. + # + def self.trusted_cert_path(cert, opt = {}) + opt = Gem::Security::OPT.merge(opt) + + # get digest algorithm, calculate checksum of root.subject + algo = opt[:dgst_algo] + dgst = algo.hexdigest(cert.subject.to_s) + + # build path to trusted cert file + name = "cert-#{dgst}.pem" + + # join and return path components + File::join(opt[:trust_dir], name) + end + + # + # Verify that the gem data with the given signature and signing chain + # matched this security policy at the specified time. + # + def verify_gem(signature, data, chain, time = Time.now) + Gem.ensure_ssl_available + cert_class = OpenSSL::X509::Certificate + exc = Gem::Security::Exception + chain ||= [] + + chain = chain.map{ |str| cert_class.new(str) } + signer, ch_len = chain[-1], chain.size + + # make sure signature is valid + if @verify_data + # get digest algorithm (TODO: this should be configurable) + dgst = @opt[:dgst_algo] + + # verify the data signature (this is the most important part, so don't + # screw it up :D) + v = signer.public_key.verify(dgst.new, signature, data) + raise exc, "Invalid Gem Signature" unless v + + # make sure the signer is valid + if @verify_signer + # make sure the signing cert is valid right now + v = signer.check_validity(nil, time) + raise exc, "Invalid Signature: #{v[:desc]}" unless v[:is_valid] + end + end + + # make sure the certificate chain is valid + if @verify_chain + # iterate down over the chain and verify each certificate against it's + # issuer + (ch_len - 1).downto(1) do |i| + issuer, cert = chain[i - 1, 2] + v = cert.check_validity(issuer, time) + raise exc, "%s: cert = '%s', error = '%s'" % [ + 'Invalid Signing Chain', cert.subject, v[:desc] + ] unless v[:is_valid] + end + + # verify root of chain + if @verify_root + # make sure root is self-signed + root = chain[0] + raise exc, "%s: %s (subject = '%s', issuer = '%s')" % [ + 'Invalid Signing Chain Root', + 'Subject does not match Issuer for Gem Signing Chain', + root.subject.to_s, + root.issuer.to_s, + ] unless root.issuer.to_s == root.subject.to_s + + # make sure root is valid + v = root.check_validity(root, time) + raise exc, "%s: cert = '%s', error = '%s'" % [ + 'Invalid Signing Chain Root', root.subject, v[:desc] + ] unless v[:is_valid] + + # verify that the chain root is trusted + if @only_trusted + # get digest algorithm, calculate checksum of root.subject + algo = @opt[:dgst_algo] + path = Gem::Security::Policy.trusted_cert_path(root, @opt) + + # check to make sure trusted path exists + raise exc, "%s: cert = '%s', error = '%s'" % [ + 'Untrusted Signing Chain Root', + root.subject.to_s, + "path \"#{path}\" does not exist", + ] unless File.exist?(path) + + # load calculate digest from saved cert file + save_cert = OpenSSL::X509::Certificate.new(File.read(path)) + save_dgst = algo.digest(save_cert.public_key.to_s) + + # create digest of public key + pkey_str = root.public_key.to_s + cert_dgst = algo.digest(pkey_str) + + # now compare the two digests, raise exception + # if they don't match + raise exc, "%s: %s (saved = '%s', root = '%s')" % [ + 'Invalid Signing Chain Root', + "Saved checksum doesn't match root checksum", + save_dgst, cert_dgst, + ] unless save_dgst == cert_dgst + end + end + + # return the signing chain + chain.map { |cert| cert.subject } + end + end + end + + # + # No security policy: all package signature checks are disabled. + # + NoSecurity = Policy.new( + :verify_data => false, + :verify_signer => false, + :verify_chain => false, + :verify_root => false, + :only_trusted => false, + :only_signed => false + ) + + # + # AlmostNo security policy: only verify that the signing certificate is the + # one that actually signed the data. Make no attempt to verify the signing + # certificate chain. + # + # This policy is basically useless. better than nothing, but can still be + # easily spoofed, and is not recommended. + # + AlmostNoSecurity = Policy.new( + :verify_data => true, + :verify_signer => false, + :verify_chain => false, + :verify_root => false, + :only_trusted => false, + :only_signed => false + ) + + # + # Low security policy: only verify that the signing certificate is actually + # the gem signer, and that the signing certificate is valid. + # + # This policy is better than nothing, but can still be easily spoofed, and + # is not recommended. + # + LowSecurity = Policy.new( + :verify_data => true, + :verify_signer => true, + :verify_chain => false, + :verify_root => false, + :only_trusted => false, + :only_signed => false + ) + + # + # Medium security policy: verify the signing certificate, verify the signing + # certificate chain all the way to the root certificate, and only trust root + # certificates that we have explicitly allowed trust for. + # + # This security policy is reasonable, but it allows unsigned packages, so a + # malicious person could simply delete the package signature and pass the + # gem off as unsigned. + # + MediumSecurity = Policy.new( + :verify_data => true, + :verify_signer => true, + :verify_chain => true, + :verify_root => true, + :only_trusted => true, + :only_signed => false + ) + + # + # High security policy: only allow signed gems to be installed, verify the + # signing certificate, verify the signing certificate chain all the way to + # the root certificate, and only trust root certificates that we have + # explicitly allowed trust for. + # + # This security policy is significantly more difficult to bypass, and offers + # a reasonable guarantee that the contents of the gem have not been altered. + # + HighSecurity = Policy.new( + :verify_data => true, + :verify_signer => true, + :verify_chain => true, + :verify_root => true, + :only_trusted => true, + :only_signed => true + ) + + # + # Hash of configured security policies + # + Policies = { + 'NoSecurity' => NoSecurity, + 'AlmostNoSecurity' => AlmostNoSecurity, + 'LowSecurity' => LowSecurity, + 'MediumSecurity' => MediumSecurity, + 'HighSecurity' => HighSecurity, + } + + # + # Sign the cert cert with @signing_key and @signing_cert, using the digest + # algorithm opt[:dgst_algo]. Returns the newly signed certificate. + # + def self.sign_cert(cert, signing_key, signing_cert, opt = {}) + opt = OPT.merge(opt) + + # set up issuer information + cert.issuer = signing_cert.subject + cert.sign(signing_key, opt[:dgst_algo].new) + + cert + end + + # + # Make sure the trust directory exists. If it does exist, make sure it's + # actually a directory. If not, then create it with the appropriate + # permissions. + # + def self.verify_trust_dir(path, perms) + # if the directory exists, then make sure it is in fact a directory. if + # it doesn't exist, then create it with the appropriate permissions + if File.exist?(path) + # verify that the trust directory is actually a directory + unless File.directory?(path) + err = "trust directory #{path} isn't a directory" + raise Gem::Security::Exception, err + end + else + # trust directory doesn't exist, so create it with permissions + FileUtils.mkdir_p(path) + FileUtils.chmod(perms, path) + end + end + + # + # Build a certificate from the given DN and private key. + # + def self.build_cert(name, key, opt = {}) + Gem.ensure_ssl_available + opt = OPT.merge(opt) + + # create new cert + ret = OpenSSL::X509::Certificate.new + + # populate cert attributes + ret.version = 2 + ret.serial = 0 + ret.public_key = key.public_key + ret.not_before = Time.now + ret.not_after = Time.now + opt[:cert_age] + ret.subject = name + + # add certificate extensions + ef = OpenSSL::X509::ExtensionFactory.new(nil, ret) + ret.extensions = opt[:cert_exts].map { |k, v| ef.create_extension(k, v) } + + # sign cert + i_key, i_cert = opt[:issuer_key] || key, opt[:issuer_cert] || ret + ret = sign_cert(ret, i_key, i_cert, opt) + + # return cert + ret + end + + # + # Build a self-signed certificate for the given email address. + # + def self.build_self_signed_cert(email_addr, opt = {}) + Gem.ensure_ssl_available + opt = OPT.merge(opt) + path = { :key => nil, :cert => nil } + + # split email address up + cn, dcs = email_addr.split('@') + dcs = dcs.split('.') + + # munge email CN and DCs + cn = cn.gsub(opt[:munge_re], '_') + dcs = dcs.map { |dc| dc.gsub(opt[:munge_re], '_') } + + # create DN + name = "CN=#{cn}/" << dcs.map { |dc| "DC=#{dc}" }.join('/') + name = OpenSSL::X509::Name::parse(name) + + # build private key + key = opt[:key_algo].new(opt[:key_size]) + + # method name pretty much says it all :) + verify_trust_dir(opt[:trust_dir], opt[:perms][:trust_dir]) + + # if we're saving the key, then write it out + if opt[:save_key] + path[:key] = opt[:save_key_path] || (opt[:output_fmt] % 'private_key') + File.open(path[:key], 'wb') do |file| + file.chmod(opt[:perms][:signing_key]) + file.write(key.to_pem) + end + end + + # build self-signed public cert from key + cert = build_cert(name, key, opt) + + # if we're saving the cert, then write it out + if opt[:save_cert] + path[:cert] = opt[:save_cert_path] || (opt[:output_fmt] % 'public_cert') + File.open(path[:cert], 'wb') do |file| + file.chmod(opt[:perms][:signing_cert]) + file.write(cert.to_pem) + end + end + + # return key, cert, and paths (if applicable) + { :key => key, :cert => cert, + :key_path => path[:key], :cert_path => path[:cert] } + end + + # + # Add certificate to trusted cert list. + # + # Note: At the moment these are stored in OPT[:trust_dir], although that + # directory may change in the future. + # + def self.add_trusted_cert(cert, opt = {}) + opt = OPT.merge(opt) + + # get destination path + path = Gem::Security::Policy.trusted_cert_path(cert, opt) + + # verify trust directory (can't write to nowhere, you know) + verify_trust_dir(opt[:trust_dir], opt[:perms][:trust_dir]) + + # write cert to output file + File.open(path, 'wb') do |file| + file.chmod(opt[:perms][:trusted_cert]) + file.write(cert.to_pem) + end + + # return nil + nil + end + + # + # Basic OpenSSL-based package signing class. + # + class Signer + attr_accessor :key, :cert_chain + + def initialize(key, cert_chain) + Gem.ensure_ssl_available + @algo = Gem::Security::OPT[:dgst_algo] + @key, @cert_chain = key, cert_chain + + # check key, if it's a file, and if it's key, leave it alone + if @key && !@key.kind_of?(OpenSSL::PKey::PKey) + @key = OpenSSL::PKey::RSA.new(File.read(@key)) + end + + # check cert chain, if it's a file, load it, if it's cert data, convert + # it into a cert object, and if it's a cert object, leave it alone + if @cert_chain + @cert_chain = @cert_chain.map do |cert| + # check cert, if it's a file, load it, if it's cert data, convert it + # into a cert object, and if it's a cert object, leave it alone + if cert && !cert.kind_of?(OpenSSL::X509::Certificate) + cert = File.read(cert) if File::exist?(cert) + cert = OpenSSL::X509::Certificate.new(cert) + end + cert + end + end + end + + # + # Sign data with given digest algorithm + # + def sign(data) + @key.sign(@algo.new, data) + end + + end +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/server.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/server.rb new file mode 100644 index 0000000000..2c617ff144 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/server.rb @@ -0,0 +1,629 @@ +require 'webrick' +require 'yaml' +require 'zlib' +require 'erb' + +require 'rubygems' +require 'rubygems/doc_manager' + +## +# Gem::Server and allows users to serve gems for consumption by +# `gem --remote-install`. +# +# gem_server starts an HTTP server on the given port and serves the following: +# * "/" - Browsing of gem spec files for installed gems +# * "/specs.#{Gem.marshal_version}.gz" - specs name/version/platform index +# * "/latest_specs.#{Gem.marshal_version}.gz" - latest specs +# name/version/platform index +# * "/quick/" - Individual gemspecs +# * "/gems" - Direct access to download the installable gems +# * legacy indexes: +# * "/Marshal.#{Gem.marshal_version}" - Full SourceIndex dump of metadata +# for installed gems +# * "/yaml" - YAML dump of metadata for installed gems - deprecated +# +# == Usage +# +# gem_server = Gem::Server.new Gem.dir, 8089, false +# gem_server.run +# +#-- +# TODO Refactor into a real WEBrick servlet to remove code duplication. + +class Gem::Server + + include Gem::UserInteraction + + DOC_TEMPLATE = <<-'WEBPAGE' + + + + + + RubyGems Documentation Index + + + +
    +

    RubyGems Documentation Index

    +
    + + +
    +
    +
    +

    Summary

    +

    There are <%=values["gem_count"]%> gems installed:

    +

    + <%= values["specs"].map { |v| "#{v["name"]}" }.join ', ' %>. +

    Gems

    + +
    + <% values["specs"].each do |spec| %> +
    + <% if spec["first_name_entry"] then %> + "> + <% end %> + + <%=spec["name"]%> <%=spec["version"]%> + + <% if spec["rdoc_installed"] then %> + ">[rdoc] + <% else %> + [rdoc] + <% end %> + + <% if spec["homepage"] then %> + " title="<%=spec["homepage"]%>">[www] + <% else %> + [www] + <% end %> + + <% if spec["has_deps"] then %> + - depends on + <%= spec["dependencies"].map { |v| "#{v["name"]}" }.join ', ' %>. + <% end %> +
    +
    + <%=spec["summary"]%> + <% if spec["executables"] then %> +
    + + <% if spec["only_one_executable"] then %> + Executable is + <% else %> + Executables are + <%end%> + + <%= spec["executables"].map { |v| "#{v["executable"]}"}.join ', ' %>. + + <%end%> +
    +
    +
    + <% end %> +
    + +
    +
    +
    + + + + WEBPAGE + + # CSS is copy & paste from rdoc-style.css, RDoc V1.0.1 - 20041108 + RDOC_CSS = <<-RDOCCSS +body { + font-family: Verdana,Arial,Helvetica,sans-serif; + font-size: 90%; + margin: 0; + margin-left: 40px; + padding: 0; + background: white; +} + +h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; } +h1 { font-size: 150%; } +h2,h3,h4 { margin-top: 1em; } + +a { background: #eef; color: #039; text-decoration: none; } +a:hover { background: #039; color: #eef; } + +/* Override the base stylesheets Anchor inside a table cell */ +td > a { + background: transparent; + color: #039; + text-decoration: none; +} + +/* and inside a section title */ +.section-title > a { + background: transparent; + color: #eee; + text-decoration: none; +} + +/* === Structural elements =================================== */ + +div#index { + margin: 0; + margin-left: -40px; + padding: 0; + font-size: 90%; +} + + +div#index a { + margin-left: 0.7em; +} + +div#index .section-bar { + margin-left: 0px; + padding-left: 0.7em; + background: #ccc; + font-size: small; +} + + +div#classHeader, div#fileHeader { + width: auto; + color: white; + padding: 0.5em 1.5em 0.5em 1.5em; + margin: 0; + margin-left: -40px; + border-bottom: 3px solid #006; +} + +div#classHeader a, div#fileHeader a { + background: inherit; + color: white; +} + +div#classHeader td, div#fileHeader td { + background: inherit; + color: white; +} + + +div#fileHeader { + background: #057; +} + +div#classHeader { + background: #048; +} + + +.class-name-in-header { + font-size: 180%; + font-weight: bold; +} + + +div#bodyContent { + padding: 0 1.5em 0 1.5em; +} + +div#description { + padding: 0.5em 1.5em; + background: #efefef; + border: 1px dotted #999; +} + +div#description h1,h2,h3,h4,h5,h6 { + color: #125;; + background: transparent; +} + +div#validator-badges { + text-align: center; +} +div#validator-badges img { border: 0; } + +div#copyright { + color: #333; + background: #efefef; + font: 0.75em sans-serif; + margin-top: 5em; + margin-bottom: 0; + padding: 0.5em 2em; +} + + +/* === Classes =================================== */ + +table.header-table { + color: white; + font-size: small; +} + +.type-note { + font-size: small; + color: #DEDEDE; +} + +.xxsection-bar { + background: #eee; + color: #333; + padding: 3px; +} + +.section-bar { + color: #333; + border-bottom: 1px solid #999; + margin-left: -20px; +} + + +.section-title { + background: #79a; + color: #eee; + padding: 3px; + margin-top: 2em; + margin-left: -30px; + border: 1px solid #999; +} + +.top-aligned-row { vertical-align: top } +.bottom-aligned-row { vertical-align: bottom } + +/* --- Context section classes ----------------------- */ + +.context-row { } +.context-item-name { font-family: monospace; font-weight: bold; color: black; } +.context-item-value { font-size: small; color: #448; } +.context-item-desc { color: #333; padding-left: 2em; } + +/* --- Method classes -------------------------- */ +.method-detail { + background: #efefef; + padding: 0; + margin-top: 0.5em; + margin-bottom: 1em; + border: 1px dotted #ccc; +} +.method-heading { + color: black; + background: #ccc; + border-bottom: 1px solid #666; + padding: 0.2em 0.5em 0 0.5em; +} +.method-signature { color: black; background: inherit; } +.method-name { font-weight: bold; } +.method-args { font-style: italic; } +.method-description { padding: 0 0.5em 0 0.5em; } + +/* --- Source code sections -------------------- */ + +a.source-toggle { font-size: 90%; } +div.method-source-code { + background: #262626; + color: #ffdead; + margin: 1em; + padding: 0.5em; + border: 1px dashed #999; + overflow: hidden; +} + +div.method-source-code pre { color: #ffdead; overflow: hidden; } + +/* --- Ruby keyword styles --------------------- */ + +.standalone-code { background: #221111; color: #ffdead; overflow: hidden; } + +.ruby-constant { color: #7fffd4; background: transparent; } +.ruby-keyword { color: #00ffff; background: transparent; } +.ruby-ivar { color: #eedd82; background: transparent; } +.ruby-operator { color: #00ffee; background: transparent; } +.ruby-identifier { color: #ffdead; background: transparent; } +.ruby-node { color: #ffa07a; background: transparent; } +.ruby-comment { color: #b22222; font-weight: bold; background: transparent; } +.ruby-regexp { color: #ffa07a; background: transparent; } +.ruby-value { color: #7fffd4; background: transparent; } + RDOCCSS + + def self.run(options) + new(options[:gemdir], options[:port], options[:daemon]).run + end + + def initialize(gem_dir, port, daemon) + Socket.do_not_reverse_lookup = true + + @gem_dir = gem_dir + @port = port + @daemon = daemon + logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL + @server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger + + @spec_dir = File.join @gem_dir, 'specifications' + + unless File.directory? @spec_dir then + raise ArgumentError, "#{@gem_dir} does not appear to be a gem repository" + end + + @source_index = Gem::SourceIndex.from_gems_in @spec_dir + end + + def Marshal(req, res) + @source_index.refresh! + + res['date'] = File.stat(@spec_dir).mtime + + index = Marshal.dump @source_index + + if req.request_method == 'HEAD' then + res['content-length'] = index.length + return + end + + if req.path =~ /Z$/ then + res['content-type'] = 'application/x-deflate' + index = Gem.deflate index + else + res['content-type'] = 'application/octet-stream' + end + + res.body << index + end + + def latest_specs(req, res) + @source_index.refresh! + + res['content-type'] = 'application/x-gzip' + + res['date'] = File.stat(@spec_dir).mtime + + specs = @source_index.latest_specs.sort.map do |spec| + platform = spec.original_platform + platform = Gem::Platform::RUBY if platform.nil? + [spec.name, spec.version, platform] + end + + specs = Marshal.dump specs + + if req.path =~ /\.gz$/ then + specs = Gem.gzip specs + res['content-type'] = 'application/x-gzip' + else + res['content-type'] = 'application/octet-stream' + end + + if req.request_method == 'HEAD' then + res['content-length'] = specs.length + else + res.body << specs + end + end + + def quick(req, res) + @source_index.refresh! + + res['content-type'] = 'text/plain' + res['date'] = File.stat(@spec_dir).mtime + + case req.request_uri.path + when '/quick/index' then + res.body << @source_index.map { |name,| name }.sort.join("\n") + when '/quick/index.rz' then + index = @source_index.map { |name,| name }.sort.join("\n") + res['content-type'] = 'application/x-deflate' + res.body << Gem.deflate(index) + when '/quick/latest_index' then + index = @source_index.latest_specs.map { |spec| spec.full_name } + res.body << index.sort.join("\n") + when '/quick/latest_index.rz' then + index = @source_index.latest_specs.map { |spec| spec.full_name } + res['content-type'] = 'application/x-deflate' + res.body << Gem.deflate(index.sort.join("\n")) + when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+)(-.*?)?\.gemspec\.rz$| then + dep = Gem::Dependency.new $2, $3 + specs = @source_index.search dep + marshal_format = $1 + + selector = [$2, $3, $4].map { |s| s.inspect }.join ' ' + + platform = if $4 then + Gem::Platform.new $4.sub(/^-/, '') + else + Gem::Platform::RUBY + end + + specs = specs.select { |s| s.platform == platform } + + if specs.empty? then + res.status = 404 + res.body = "No gems found matching #{selector}" + elsif specs.length > 1 then + res.status = 500 + res.body = "Multiple gems found matching #{selector}" + elsif marshal_format then + res['content-type'] = 'application/x-deflate' + res.body << Gem.deflate(Marshal.dump(specs.first)) + else # deprecated YAML format + res['content-type'] = 'application/x-deflate' + res.body << Gem.deflate(specs.first.to_yaml) + end + else + raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." + end + end + + def root(req, res) + @source_index.refresh! + res['date'] = File.stat(@spec_dir).mtime + + raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless + req.path == '/' + + specs = [] + total_file_count = 0 + + @source_index.each do |path, spec| + total_file_count += spec.files.size + deps = spec.dependencies.map do |dep| + { "name" => dep.name, + "type" => dep.type, + "version" => dep.version_requirements.to_s, } + end + + deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] } + deps.last["is_last"] = true unless deps.empty? + + # executables + executables = spec.executables.sort.collect { |exec| {"executable" => exec} } + executables = nil if executables.empty? + executables.last["is_last"] = true if executables + + specs << { + "authors" => spec.authors.sort.join(", "), + "date" => spec.date.to_s, + "dependencies" => deps, + "doc_path" => "/doc_root/#{spec.full_name}/rdoc/index.html", + "executables" => executables, + "only_one_executable" => (executables && executables.size == 1), + "full_name" => spec.full_name, + "has_deps" => !deps.empty?, + "homepage" => spec.homepage, + "name" => spec.name, + "rdoc_installed" => Gem::DocManager.new(spec).rdoc_installed?, + "summary" => spec.summary, + "version" => spec.version.to_s, + } + end + + specs << { + "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others", + "dependencies" => [], + "doc_path" => "/doc_root/rubygems-#{Gem::RubyGemsVersion}/rdoc/index.html", + "executables" => [{"executable" => 'gem', "is_last" => true}], + "only_one_executable" => true, + "full_name" => "rubygems-#{Gem::RubyGemsVersion}", + "has_deps" => false, + "homepage" => "http://rubygems.org/", + "name" => 'rubygems', + "rdoc_installed" => true, + "summary" => "RubyGems itself", + "version" => Gem::RubyGemsVersion, + } + + specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] } + specs.last["is_last"] = true + + # tag all specs with first_name_entry + last_spec = nil + specs.each do |spec| + is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase) + spec["first_name_entry"] = is_first + last_spec = spec + end + + # create page from template + template = ERB.new(DOC_TEMPLATE) + res['content-type'] = 'text/html' + + values = { "gem_count" => specs.size.to_s, "specs" => specs, + "total_file_count" => total_file_count.to_s } + + result = template.result binding + res.body = result + end + + def run + @server.listen nil, @port + + say "Starting gem server on http://localhost:#{@port}/" + + WEBrick::Daemon.start if @daemon + + @server.mount_proc "/yaml", method(:yaml) + @server.mount_proc "/yaml.Z", method(:yaml) + + @server.mount_proc "/Marshal.#{Gem.marshal_version}", method(:Marshal) + @server.mount_proc "/Marshal.#{Gem.marshal_version}.Z", method(:Marshal) + + @server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs) + @server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs) + + @server.mount_proc "/latest_specs.#{Gem.marshal_version}", + method(:latest_specs) + @server.mount_proc "/latest_specs.#{Gem.marshal_version}.gz", + method(:latest_specs) + + @server.mount_proc "/quick/", method(:quick) + + @server.mount_proc("/gem-server-rdoc-style.css") do |req, res| + res['content-type'] = 'text/css' + res['date'] = File.stat(@spec_dir).mtime + res.body << RDOC_CSS + end + + @server.mount_proc "/", method(:root) + + paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" } + paths.each do |mount_point, mount_dir| + @server.mount(mount_point, WEBrick::HTTPServlet::FileHandler, + File.join(@gem_dir, mount_dir), true) + end + + trap("INT") { @server.shutdown; exit! } + trap("TERM") { @server.shutdown; exit! } + + @server.start + end + + def specs(req, res) + @source_index.refresh! + + res['date'] = File.stat(@spec_dir).mtime + + specs = @source_index.sort.map do |_, spec| + platform = spec.original_platform + platform = Gem::Platform::RUBY if platform.nil? + [spec.name, spec.version, platform] + end + + specs = Marshal.dump specs + + if req.path =~ /\.gz$/ then + specs = Gem.gzip specs + res['content-type'] = 'application/x-gzip' + else + res['content-type'] = 'application/octet-stream' + end + + if req.request_method == 'HEAD' then + res['content-length'] = specs.length + else + res.body << specs + end + end + + def yaml(req, res) + @source_index.refresh! + + res['date'] = File.stat(@spec_dir).mtime + + index = @source_index.to_yaml + + if req.path =~ /Z$/ then + res['content-type'] = 'application/x-deflate' + index = Gem.deflate index + else + res['content-type'] = 'text/plain' + end + + if req.request_method == 'HEAD' then + res['content-length'] = index.length + return + end + + res.body << index + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/source_index.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/source_index.rb new file mode 100644 index 0000000000..1eefd8c149 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/source_index.rb @@ -0,0 +1,545 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems' +require 'rubygems/user_interaction' +require 'rubygems/specification' +require 'rubygems/spec_fetcher' + +## +# The SourceIndex object indexes all the gems available from a +# particular source (e.g. a list of gem directories, or a remote +# source). A SourceIndex maps a gem full name to a gem +# specification. +# +# NOTE:: The class used to be named Cache, but that became +# confusing when cached source fetchers where introduced. The +# constant Gem::Cache is an alias for this class to allow old +# YAMLized source index objects to load properly. + +class Gem::SourceIndex + + include Enumerable + + include Gem::UserInteraction + + attr_reader :gems # :nodoc: + + ## + # Directories to use to refresh this SourceIndex when calling refresh! + + attr_accessor :spec_dirs + + class << self + include Gem::UserInteraction + + ## + # Factory method to construct a source index instance for a given + # path. + # + # deprecated:: + # If supplied, from_installed_gems will act just like + # +from_gems_in+. This argument is deprecated and is provided + # just for backwards compatibility, and should not generally + # be used. + # + # return:: + # SourceIndex instance + + def from_installed_gems(*deprecated) + if deprecated.empty? + from_gems_in(*installed_spec_directories) + else + from_gems_in(*deprecated) # HACK warn + end + end + + ## + # Returns a list of directories from Gem.path that contain specifications. + + def installed_spec_directories + Gem.path.collect { |dir| File.join(dir, "specifications") } + end + + ## + # Creates a new SourceIndex from the ruby format gem specifications in + # +spec_dirs+. + + def from_gems_in(*spec_dirs) + source_index = new + source_index.spec_dirs = spec_dirs + source_index.refresh! + end + + ## + # Loads a ruby-format specification from +file_name+ and returns the + # loaded spec. + + def load_specification(file_name) + begin + spec_code = File.read(file_name).untaint + gemspec = eval spec_code, binding, file_name + if gemspec.is_a?(Gem::Specification) + gemspec.loaded_from = file_name + return gemspec + end + alert_warning "File '#{file_name}' does not evaluate to a gem specification" + rescue SignalException, SystemExit + raise + rescue SyntaxError => e + alert_warning e + alert_warning spec_code + rescue Exception => e + alert_warning(e.inspect.to_s + "\n" + spec_code) + alert_warning "Invalid .gemspec format in '#{file_name}'" + end + return nil + end + + end + + ## + # Constructs a source index instance from the provided + # specifications + # + # specifications:: + # [Hash] hash of [Gem name, Gem::Specification] pairs + + def initialize(specifications={}) + @gems = specifications + @spec_dirs = nil + end + + ## + # Reconstruct the source index from the specifications in +spec_dirs+. + + def load_gems_in(*spec_dirs) + @gems.clear + + spec_dirs.reverse_each do |spec_dir| + spec_files = Dir.glob File.join(spec_dir, '*.gemspec') + + spec_files.each do |spec_file| + gemspec = self.class.load_specification spec_file.untaint + add_spec gemspec if gemspec + end + end + + self + end + + ## + # Returns an Array specifications for the latest versions of each gem in + # this index. + + def latest_specs + result = Hash.new { |h,k| h[k] = [] } + latest = {} + + sort.each do |_, spec| + name = spec.name + curr_ver = spec.version + prev_ver = latest.key?(name) ? latest[name].version : nil + + next unless prev_ver.nil? or curr_ver >= prev_ver or + latest[name].platform != Gem::Platform::RUBY + + if prev_ver.nil? or + (curr_ver > prev_ver and spec.platform == Gem::Platform::RUBY) then + result[name].clear + latest[name] = spec + end + + if spec.platform != Gem::Platform::RUBY then + result[name].delete_if do |result_spec| + result_spec.platform == spec.platform + end + end + + result[name] << spec + end + + result.values.flatten + end + + ## + # Add a gem specification to the source index. + + def add_spec(gem_spec) + @gems[gem_spec.full_name] = gem_spec + end + + ## + # Add gem specifications to the source index. + + def add_specs(*gem_specs) + gem_specs.each do |spec| + add_spec spec + end + end + + ## + # Remove a gem specification named +full_name+. + + def remove_spec(full_name) + @gems.delete(full_name) + end + + ## + # Iterate over the specifications in the source index. + + def each(&block) # :yields: gem.full_name, gem + @gems.each(&block) + end + + ## + # The gem specification given a full gem spec name. + + def specification(full_name) + @gems[full_name] + end + + ## + # The signature for the source index. Changes in the signature indicate a + # change in the index. + + def index_signature + require 'rubygems/digest/sha2' + + Gem::SHA256.new.hexdigest(@gems.keys.sort.join(',')).to_s + end + + ## + # The signature for the given gem specification. + + def gem_signature(gem_full_name) + require 'rubygems/digest/sha2' + + Gem::SHA256.new.hexdigest(@gems[gem_full_name].to_yaml).to_s + end + + def size + @gems.size + end + alias length size + + ## + # Find a gem by an exact match on the short name. + + def find_name(gem_name, version_requirement = Gem::Requirement.default) + search(/^#{gem_name}$/, version_requirement) + end + + ## + # Search for a gem by Gem::Dependency +gem_pattern+. If +only_platform+ + # is true, only gems matching Gem::Platform.local will be returned. An + # Array of matching Gem::Specification objects is returned. + # + # For backwards compatibility, a String or Regexp pattern may be passed as + # +gem_pattern+, and a Gem::Requirement for +platform_only+. This + # behavior is deprecated and will be removed. + + def search(gem_pattern, platform_only = false) + version_requirement = nil + only_platform = false + + case gem_pattern # TODO warn after 2008/03, remove three months after + when Regexp then + version_requirement = platform_only || Gem::Requirement.default + when Gem::Dependency then + only_platform = platform_only + version_requirement = gem_pattern.version_requirements + gem_pattern = if Regexp === gem_pattern.name then + gem_pattern.name + elsif gem_pattern.name.empty? then + // + else + /^#{Regexp.escape gem_pattern.name}$/ + end + else + version_requirement = platform_only || Gem::Requirement.default + gem_pattern = /#{gem_pattern}/i + end + + unless Gem::Requirement === version_requirement then + version_requirement = Gem::Requirement.create version_requirement + end + + specs = @gems.values.select do |spec| + spec.name =~ gem_pattern and + version_requirement.satisfied_by? spec.version + end + + if only_platform then + specs = specs.select do |spec| + Gem::Platform.match spec.platform + end + end + + specs.sort_by { |s| s.sort_obj } + end + + ## + # Replaces the gems in the source index from specifications in the + # directories this source index was created from. Raises an exception if + # this source index wasn't created from a directory (via from_gems_in or + # from_installed_gems, or having spec_dirs set). + + def refresh! + raise 'source index not created from disk' if @spec_dirs.nil? + load_gems_in(*@spec_dirs) + end + + ## + # Returns an Array of Gem::Specifications that are not up to date. + + def outdated + outdateds = [] + + latest_specs.each do |local| + dependency = Gem::Dependency.new local.name, ">= #{local.version}" + + begin + fetcher = Gem::SpecFetcher.fetcher + remotes = fetcher.find_matching dependency + remotes = remotes.map { |(name, version,_),_| version } + rescue Gem::RemoteFetcher::FetchError => e + raise unless fetcher.warn_legacy e do + require 'rubygems/source_info_cache' + + specs = Gem::SourceInfoCache.search_with_source dependency, true + + remotes = specs.map { |spec,| spec.version } + end + end + + latest = remotes.sort.last + + outdateds << local.name if latest and local.version < latest + end + + outdateds + end + + ## + # Updates this SourceIndex from +source_uri+. If +all+ is false, only the + # latest gems are fetched. + + def update(source_uri, all) + source_uri = URI.parse source_uri unless URI::Generic === source_uri + source_uri.path += '/' unless source_uri.path =~ /\/$/ + + use_incremental = false + + begin + gem_names = fetch_quick_index source_uri, all + remove_extra gem_names + missing_gems = find_missing gem_names + + return false if missing_gems.size.zero? + + say "Missing metadata for #{missing_gems.size} gems" if + missing_gems.size > 0 and Gem.configuration.really_verbose + + use_incremental = missing_gems.size <= Gem.configuration.bulk_threshold + rescue Gem::OperationNotSupportedError => ex + alert_error "Falling back to bulk fetch: #{ex.message}" if + Gem.configuration.really_verbose + use_incremental = false + end + + if use_incremental then + update_with_missing(source_uri, missing_gems) + else + new_index = fetch_bulk_index(source_uri) + @gems.replace(new_index.gems) + end + + true + end + + def ==(other) # :nodoc: + self.class === other and @gems == other.gems + end + + def dump + Marshal.dump(self) + end + + private + + def fetcher + require 'rubygems/remote_fetcher' + + Gem::RemoteFetcher.fetcher + end + + def fetch_index_from(source_uri) + @fetch_error = nil + + indexes = %W[ + Marshal.#{Gem.marshal_version}.Z + Marshal.#{Gem.marshal_version} + yaml.Z + yaml + ] + + indexes.each do |name| + spec_data = nil + index = source_uri + name + begin + spec_data = fetcher.fetch_path index + spec_data = unzip(spec_data) if name =~ /\.Z$/ + + if name =~ /Marshal/ then + return Marshal.load(spec_data) + else + return YAML.load(spec_data) + end + rescue => e + if Gem.configuration.really_verbose then + alert_error "Unable to fetch #{name}: #{e.message}" + end + + @fetch_error = e + end + end + + nil + end + + def fetch_bulk_index(source_uri) + say "Bulk updating Gem source index for: #{source_uri}" if + Gem.configuration.verbose + + index = fetch_index_from(source_uri) + if index.nil? then + raise Gem::RemoteSourceException, + "Error fetching remote gem cache: #{@fetch_error}" + end + @fetch_error = nil + index + end + + ## + # Get the quick index needed for incremental updates. + + def fetch_quick_index(source_uri, all) + index = all ? 'index' : 'latest_index' + + zipped_index = fetcher.fetch_path source_uri + "quick/#{index}.rz" + + unzip(zipped_index).split("\n") + rescue ::Exception => e + unless all then + say "Latest index not found, using quick index" if + Gem.configuration.really_verbose + + fetch_quick_index source_uri, true + else + raise Gem::OperationNotSupportedError, + "No quick index found: #{e.message}" + end + end + + ## + # Make a list of full names for all the missing gemspecs. + + def find_missing(spec_names) + unless defined? @originals then + @originals = {} + each do |full_name, spec| + @originals[spec.original_name] = spec + end + end + + spec_names.find_all { |full_name| + @originals[full_name].nil? + } + end + + def remove_extra(spec_names) + dictionary = spec_names.inject({}) { |h, k| h[k] = true; h } + each do |name, spec| + remove_spec name unless dictionary.include? spec.original_name + end + end + + ## + # Unzip the given string. + + def unzip(string) + require 'zlib' + Gem.inflate string + end + + ## + # Tries to fetch Marshal representation first, then YAML + + def fetch_single_spec(source_uri, spec_name) + @fetch_error = nil + + begin + marshal_uri = source_uri + "quick/Marshal.#{Gem.marshal_version}/#{spec_name}.gemspec.rz" + zipped = fetcher.fetch_path marshal_uri + return Marshal.load(unzip(zipped)) + rescue => ex + @fetch_error = ex + + if Gem.configuration.really_verbose then + say "unable to fetch marshal gemspec #{marshal_uri}: #{ex.class} - #{ex}" + end + end + + begin + yaml_uri = source_uri + "quick/#{spec_name}.gemspec.rz" + zipped = fetcher.fetch_path yaml_uri + return YAML.load(unzip(zipped)) + rescue => ex + @fetch_error = ex + if Gem.configuration.really_verbose then + say "unable to fetch YAML gemspec #{yaml_uri}: #{ex.class} - #{ex}" + end + end + + nil + end + + ## + # Update the cached source index with the missing names. + + def update_with_missing(source_uri, missing_names) + progress = ui.progress_reporter(missing_names.size, + "Updating metadata for #{missing_names.size} gems from #{source_uri}") + missing_names.each do |spec_name| + gemspec = fetch_single_spec(source_uri, spec_name) + if gemspec.nil? then + ui.say "Failed to download spec #{spec_name} from #{source_uri}:\n" \ + "\t#{@fetch_error.message}" + else + add_spec gemspec + progress.updated spec_name + end + @fetch_error = nil + end + progress.done + progress.count + end + +end + +module Gem + + # :stopdoc: + + # Cache is an alias for SourceIndex to allow older YAMLized source index + # objects to load properly. + Cache = SourceIndex + + # :starddoc: + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/source_info_cache.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/source_info_cache.rb new file mode 100644 index 0000000000..ec6928c00d --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/source_info_cache.rb @@ -0,0 +1,384 @@ +require 'fileutils' + +require 'rubygems' +require 'rubygems/source_info_cache_entry' +require 'rubygems/user_interaction' + +## +# SourceInfoCache stores a copy of the gem index for each gem source. +# +# There are two possible cache locations, the system cache and the user cache: +# * The system cache is preferred if it is writable or can be created. +# * The user cache is used otherwise +# +# Once a cache is selected, it will be used for all operations. +# SourceInfoCache will not switch between cache files dynamically. +# +# Cache data is a Hash mapping a source URI to a SourceInfoCacheEntry. +# +#-- +# To keep things straight, this is how the cache objects all fit together: +# +# Gem::SourceInfoCache +# @cache_data = { +# source_uri => Gem::SourceInfoCacheEntry +# @size = source index size +# @source_index = Gem::SourceIndex +# ... +# } + +class Gem::SourceInfoCache + + include Gem::UserInteraction + + ## + # The singleton Gem::SourceInfoCache. If +all+ is true, a full refresh will + # be performed if the singleton instance is being initialized. + + def self.cache(all = false) + return @cache if @cache + @cache = new + @cache.refresh all if Gem.configuration.update_sources + @cache + end + + def self.cache_data + cache.cache_data + end + + ## + # The name of the system cache file. + + def self.latest_system_cache_file + File.join File.dirname(system_cache_file), + "latest_#{File.basename system_cache_file}" + end + + ## + # The name of the latest user cache file. + + def self.latest_user_cache_file + File.join File.dirname(user_cache_file), + "latest_#{File.basename user_cache_file}" + end + + ## + # Reset all singletons, discarding any changes. + + def self.reset + @cache = nil + @system_cache_file = nil + @user_cache_file = nil + end + + ## + # Search all source indexes. See Gem::SourceInfoCache#search. + + def self.search(*args) + cache.search(*args) + end + + ## + # Search all source indexes returning the source_uri. See + # Gem::SourceInfoCache#search_with_source. + + def self.search_with_source(*args) + cache.search_with_source(*args) + end + + ## + # The name of the system cache file. (class method) + + def self.system_cache_file + @system_cache_file ||= Gem.default_system_source_cache_dir + end + + ## + # The name of the user cache file. + + def self.user_cache_file + @user_cache_file ||= + ENV['GEMCACHE'] || Gem.default_user_source_cache_dir + end + + def initialize # :nodoc: + @cache_data = nil + @cache_file = nil + @dirty = false + @only_latest = true + end + + ## + # The most recent cache data. + + def cache_data + return @cache_data if @cache_data + cache_file # HACK writable check + + @only_latest = true + + @cache_data = read_cache_data latest_cache_file + + @cache_data + end + + ## + # The name of the cache file. + + def cache_file + return @cache_file if @cache_file + @cache_file = (try_file(system_cache_file) or + try_file(user_cache_file) or + raise "unable to locate a writable cache file") + end + + ## + # Write the cache to a local file (if it is dirty). + + def flush + write_cache if @dirty + @dirty = false + end + + def latest_cache_data + latest_cache_data = {} + + cache_data.each do |repo, sice| + latest = sice.source_index.latest_specs + + new_si = Gem::SourceIndex.new + new_si.add_specs(*latest) + + latest_sice = Gem::SourceInfoCacheEntry.new new_si, sice.size + latest_cache_data[repo] = latest_sice + end + + latest_cache_data + end + + ## + # The name of the latest cache file. + + def latest_cache_file + File.join File.dirname(cache_file), "latest_#{File.basename cache_file}" + end + + ## + # The name of the latest system cache file. + + def latest_system_cache_file + self.class.latest_system_cache_file + end + + ## + # The name of the latest user cache file. + + def latest_user_cache_file + self.class.latest_user_cache_file + end + + ## + # Merges the complete cache file into this Gem::SourceInfoCache. + + def read_all_cache_data + if @only_latest then + @only_latest = false + all_data = read_cache_data cache_file + + cache_data.update all_data do |source_uri, latest_sice, all_sice| + all_sice.source_index.gems.update latest_sice.source_index.gems + + Gem::SourceInfoCacheEntry.new all_sice.source_index, latest_sice.size + end + + begin + refresh true + rescue Gem::RemoteFetcher::FetchError + end + end + end + + ## + # Reads cached data from +file+. + + def read_cache_data(file) + # Marshal loads 30-40% faster from a String, and 2MB on 20061116 is small + data = open file, 'rb' do |fp| fp.read end + cache_data = Marshal.load data + + cache_data.each do |url, sice| + next unless sice.is_a?(Hash) + update + + cache = sice['cache'] + size = sice['size'] + + if cache.is_a?(Gem::SourceIndex) and size.is_a?(Numeric) then + new_sice = Gem::SourceInfoCacheEntry.new cache, size + cache_data[url] = new_sice + else # irreperable, force refetch. + reset_cache_for url, cache_data + end + end + + cache_data + rescue Errno::ENOENT + {} + rescue => e + if Gem.configuration.really_verbose then + say "Exception during cache_data handling: #{e.class} - #{e}" + say "Cache file was: #{file}" + say "\t#{e.backtrace.join "\n\t"}" + end + + {} + end + + ## + # Refreshes each source in the cache from its repository. If +all+ is + # false, only latest gems are updated. + + def refresh(all) + Gem.sources.each do |source_uri| + cache_entry = cache_data[source_uri] + if cache_entry.nil? then + cache_entry = Gem::SourceInfoCacheEntry.new nil, 0 + cache_data[source_uri] = cache_entry + end + + update if cache_entry.refresh source_uri, all + end + + flush + end + + def reset_cache_for(url, cache_data) + say "Reseting cache for #{url}" if Gem.configuration.really_verbose + + sice = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0 + sice.refresh url, false # HACK may be unnecessary, see ::cache and #refresh + + cache_data[url] = sice + cache_data + end + + def reset_cache_data + @cache_data = nil + @only_latest = true + end + + ## + # Force cache file to be reset, useful for integration testing of rubygems + + def reset_cache_file + @cache_file = nil + end + + ## + # Searches all source indexes. See Gem::SourceIndex#search for details on + # +pattern+ and +platform_only+. If +all+ is set to true, the full index + # will be loaded before searching. + + def search(pattern, platform_only = false, all = false) + read_all_cache_data if all + + cache_data.map do |source_uri, sic_entry| + next unless Gem.sources.include? source_uri + sic_entry.source_index.search pattern, platform_only + end.flatten.compact + end + + # Searches all source indexes for +pattern+. If +only_platform+ is true, + # only gems matching Gem.platforms will be selected. Returns an Array of + # pairs containing the Gem::Specification found and the source_uri it was + # found at. + def search_with_source(pattern, only_platform = false, all = false) + read_all_cache_data if all + + results = [] + + cache_data.map do |source_uri, sic_entry| + next unless Gem.sources.include? source_uri + + sic_entry.source_index.search(pattern, only_platform).each do |spec| + results << [spec, source_uri] + end + end + + results + end + + ## + # Set the source info cache data directly. This is mainly used for unit + # testing when we don't want to read a file system to grab the cached source + # index information. The +hash+ should map a source URL into a + # SourceInfoCacheEntry. + + def set_cache_data(hash) + @cache_data = hash + update + end + + ## + # The name of the system cache file. + + def system_cache_file + self.class.system_cache_file + end + + ## + # Determine if +path+ is a candidate for a cache file. Returns +path+ if + # it is, nil if not. + + def try_file(path) + return path if File.writable? path + return nil if File.exist? path + + dir = File.dirname path + + unless File.exist? dir then + begin + FileUtils.mkdir_p dir + rescue RuntimeError, SystemCallError + return nil + end + end + + return path if File.writable? dir + + nil + end + + ## + # Mark the cache as updated (i.e. dirty). + + def update + @dirty = true + end + + ## + # The name of the user cache file. + + def user_cache_file + self.class.user_cache_file + end + + ## + # Write data to the proper cache files. + + def write_cache + if not File.exist?(cache_file) or not @only_latest then + open cache_file, 'wb' do |io| + io.write Marshal.dump(cache_data) + end + end + + open latest_cache_file, 'wb' do |io| + io.write Marshal.dump(latest_cache_data) + end + end + + reset + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/source_info_cache_entry.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/source_info_cache_entry.rb new file mode 100644 index 0000000000..c3f75e5b99 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/source_info_cache_entry.rb @@ -0,0 +1,56 @@ +require 'rubygems' +require 'rubygems/source_index' +require 'rubygems/remote_fetcher' + +## +# Entries held by a SourceInfoCache. + +class Gem::SourceInfoCacheEntry + + ## + # The source index for this cache entry. + + attr_reader :source_index + + ## + # The size of the of the source entry. Used to determine if the + # source index has changed. + + attr_reader :size + + ## + # Create a cache entry. + + def initialize(si, size) + @source_index = si || Gem::SourceIndex.new({}) + @size = size + @all = false + end + + def refresh(source_uri, all) + begin + marshal_uri = URI.join source_uri.to_s, "Marshal.#{Gem.marshal_version}" + remote_size = Gem::RemoteFetcher.fetcher.fetch_size marshal_uri + rescue Gem::RemoteSourceException + yaml_uri = URI.join source_uri.to_s, 'yaml' + remote_size = Gem::RemoteFetcher.fetcher.fetch_size yaml_uri + end + + # TODO Use index_signature instead of size? + return false if @size == remote_size and @all + + updated = @source_index.update source_uri, all + @size = remote_size + @all = all + + updated + end + + def ==(other) # :nodoc: + self.class === other and + @size == other.size and + @source_index == other.source_index + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/spec_fetcher.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/spec_fetcher.rb new file mode 100644 index 0000000000..29db889af5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/spec_fetcher.rb @@ -0,0 +1,251 @@ +require 'zlib' + +require 'rubygems' +require 'rubygems/remote_fetcher' +require 'rubygems/user_interaction' + +## +# SpecFetcher handles metadata updates from remote gem repositories. + +class Gem::SpecFetcher + + include Gem::UserInteraction + + ## + # The SpecFetcher cache dir. + + attr_reader :dir # :nodoc: + + ## + # Cache of latest specs + + attr_reader :latest_specs # :nodoc: + + ## + # Cache of all spces + + attr_reader :specs # :nodoc: + + @fetcher = nil + + def self.fetcher + @fetcher ||= new + end + + def self.fetcher=(fetcher) # :nodoc: + @fetcher = fetcher + end + + def initialize + @dir = File.join Gem.user_home, '.gem', 'specs' + @update_cache = File.stat(Gem.user_home).uid == Process.uid + + @specs = {} + @latest_specs = {} + + @fetcher = Gem::RemoteFetcher.fetcher + end + + ## + # Retuns the local directory to write +uri+ to. + + def cache_dir(uri) + File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(uri.path) + end + + ## + # Fetch specs matching +dependency+. If +all+ is true, all matching + # versions are returned. If +matching_platform+ is false, all platforms are + # returned. + + def fetch(dependency, all = false, matching_platform = true) + specs_and_sources = find_matching dependency, all, matching_platform + + specs_and_sources.map do |spec_tuple, source_uri| + [fetch_spec(spec_tuple, URI.parse(source_uri)), source_uri] + end + + rescue Gem::RemoteFetcher::FetchError => e + raise unless warn_legacy e do + require 'rubygems/source_info_cache' + + return Gem::SourceInfoCache.search_with_source(dependency, + matching_platform, all) + end + end + + def fetch_spec(spec, source_uri) + spec = spec - [nil, 'ruby', ''] + spec_file_name = "#{spec.join '-'}.gemspec" + + uri = source_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}" + + cache_dir = cache_dir uri + + local_spec = File.join cache_dir, spec_file_name + + if File.exist? local_spec then + spec = Gem.read_binary local_spec + else + uri.path << '.rz' + + spec = @fetcher.fetch_path uri + spec = Gem.inflate spec + + if @update_cache then + FileUtils.mkdir_p cache_dir + + open local_spec, 'wb' do |io| + io.write spec + end + end + end + + # TODO: Investigate setting Gem::Specification#loaded_from to a URI + Marshal.load spec + end + + ## + # Find spec names that match +dependency+. If +all+ is true, all matching + # versions are returned. If +matching_platform+ is false, gems for all + # platforms are returned. + + def find_matching(dependency, all = false, matching_platform = true) + found = {} + + list(all).each do |source_uri, specs| + found[source_uri] = specs.select do |spec_name, version, spec_platform| + dependency =~ Gem::Dependency.new(spec_name, version) and + (not matching_platform or Gem::Platform.match(spec_platform)) + end + end + + specs_and_sources = [] + + found.each do |source_uri, specs| + uri_str = source_uri.to_s + specs_and_sources.push(*specs.map { |spec| [spec, uri_str] }) + end + + specs_and_sources + end + + ## + # Returns Array of gem repositories that were generated with RubyGems less + # than 1.2. + + def legacy_repos + Gem.sources.reject do |source_uri| + source_uri = URI.parse source_uri + spec_path = source_uri + "specs.#{Gem.marshal_version}.gz" + + begin + @fetcher.fetch_size spec_path + rescue Gem::RemoteFetcher::FetchError + begin + @fetcher.fetch_size(source_uri + 'yaml') # re-raise if non-repo + rescue Gem::RemoteFetcher::FetchError + alert_error "#{source_uri} does not appear to be a repository" + raise + end + false + end + end + end + + ## + # Returns a list of gems available for each source in Gem::sources. If + # +all+ is true, all versions are returned instead of only latest versions. + + def list(all = false) + list = {} + + file = all ? 'specs' : 'latest_specs' + + Gem.sources.each do |source_uri| + source_uri = URI.parse source_uri + + if all and @specs.include? source_uri then + list[source_uri] = @specs[source_uri] + elsif @latest_specs.include? source_uri then + list[source_uri] = @latest_specs[source_uri] + else + specs = load_specs source_uri, file + + cache = all ? @specs : @latest_specs + + cache[source_uri] = specs + list[source_uri] = specs + end + end + + list + end + + def load_specs(source_uri, file) + file_name = "#{file}.#{Gem.marshal_version}.gz" + + spec_path = source_uri + file_name + + cache_dir = cache_dir spec_path + + local_file = File.join(cache_dir, file_name).chomp '.gz' + + if File.exist? local_file then + local_size = File.stat(local_file).size + + remote_file = spec_path.dup + remote_file.path = remote_file.path.chomp '.gz' + remote_size = @fetcher.fetch_size remote_file + + spec_dump = Gem.read_binary local_file if remote_size == local_size + end + + unless spec_dump then + loaded = true + + spec_dump_gz = @fetcher.fetch_path spec_path + spec_dump = Gem.gunzip spec_dump_gz + end + + specs = Marshal.load spec_dump + + if loaded and @update_cache then + begin + FileUtils.mkdir_p cache_dir + + open local_file, 'wb' do |io| + Marshal.dump specs, io + end + rescue + end + end + + specs + end + + ## + # Warn about legacy repositories if +exception+ indicates only legacy + # repositories are available, and yield to the block. Returns false if the + # exception indicates some other FetchError. + + def warn_legacy(exception) + uri = exception.uri.to_s + if uri =~ /specs\.#{Regexp.escape Gem.marshal_version}\.gz$/ then + alert_warning <<-EOF +RubyGems 1.2+ index not found for: +\t#{legacy_repos.join "\n\t"} + +RubyGems will revert to legacy indexes degrading performance. + EOF + + yield + + return true + end + + false + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/specification.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/specification.rb new file mode 100644 index 0000000000..f3f38e5033 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/specification.rb @@ -0,0 +1,1053 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems' +require 'rubygems/version' +require 'rubygems/requirement' +require 'rubygems/platform' + +# :stopdoc: +# Time::today has been deprecated in 0.9.5 and will be removed. +if RUBY_VERSION < '1.9' then + def Time.today + t = Time.now + t - ((t.to_f + t.gmt_offset) % 86400) + end unless defined? Time.today +end + +class Date; end # for ruby_code if date.rb wasn't required + +# :startdoc: + +module Gem + + # == Gem::Specification + # + # The Specification class contains the metadata for a Gem. Typically + # defined in a .gemspec file or a Rakefile, and looks like this: + # + # spec = Gem::Specification.new do |s| + # s.name = 'rfoo' + # s.version = '1.0' + # s.summary = 'Example gem specification' + # ... + # end + # + # There are many gemspec attributes, and the best place to learn + # about them in the "Gemspec Reference" linked from the RubyGems wiki. + # + class Specification + + ## + # Allows deinstallation of gems with legacy platforms. + + attr_accessor :original_platform # :nodoc: + + # ------------------------- Specification version constants. + + ## + # The the version number of a specification that does not specify one + # (i.e. RubyGems 0.7 or earlier). + + NONEXISTENT_SPECIFICATION_VERSION = -1 + + ## + # The specification version applied to any new Specification instances + # created. This should be bumped whenever something in the spec format + # changes. + #-- + # When updating this number, be sure to also update #to_ruby. + # + # NOTE RubyGems < 1.2 cannot load specification versions > 2. + + CURRENT_SPECIFICATION_VERSION = 2 + + ## + # An informal list of changes to the specification. The highest-valued + # key should be equal to the CURRENT_SPECIFICATION_VERSION. + + SPECIFICATION_VERSION_HISTORY = { + -1 => ['(RubyGems versions up to and including 0.7 did not have versioned specifications)'], + 1 => [ + 'Deprecated "test_suite_file" in favor of the new, but equivalent, "test_files"', + '"test_file=x" is a shortcut for "test_files=[x]"' + ], + 2 => [ + 'Added "required_rubygems_version"', + 'Now forward-compatible with future versions', + ], + } + + # :stopdoc: + MARSHAL_FIELDS = { -1 => 16, 1 => 16, 2 => 16 } + + now = Time.at(Time.now.to_i) + TODAY = now - ((now.to_i + now.gmt_offset) % 86400) + # :startdoc: + + # ------------------------- Class variables. + + # List of Specification instances. + @@list = [] + + # Optional block used to gather newly defined instances. + @@gather = nil + + # List of attribute names: [:name, :version, ...] + @@required_attributes = [] + + # List of _all_ attributes and default values: [[:name, nil], [:bindir, 'bin'], ...] + @@attributes = [] + + @@nil_attributes = [] + @@non_nil_attributes = [:@original_platform] + + # List of array attributes + @@array_attributes = [] + + # Map of attribute names to default values. + @@default_value = {} + + # ------------------------- Convenience class methods. + + def self.attribute_names + @@attributes.map { |name, default| name } + end + + def self.attribute_defaults + @@attributes.dup + end + + def self.default_value(name) + @@default_value[name] + end + + def self.required_attributes + @@required_attributes.dup + end + + def self.required_attribute?(name) + @@required_attributes.include? name.to_sym + end + + def self.array_attributes + @@array_attributes.dup + end + + # ------------------------- Infrastructure class methods. + + # A list of Specification instances that have been defined in this Ruby instance. + def self.list + @@list + end + + # Used to specify the name and default value of a specification + # attribute. The side effects are: + # * the name and default value are added to the @@attributes list + # and @@default_value map + # * a standard _writer_ method (attribute=) is created + # * a non-standard _reader method (attribute) is created + # + # The reader method behaves like this: + # def attribute + # @attribute ||= (copy of default value) + # end + # + # This allows lazy initialization of attributes to their default + # values. + # + def self.attribute(name, default=nil) + ivar_name = "@#{name}".intern + if default.nil? then + @@nil_attributes << ivar_name + else + @@non_nil_attributes << [ivar_name, default] + end + + @@attributes << [name, default] + @@default_value[name] = default + attr_accessor(name) + end + + # Same as :attribute, but ensures that values assigned to the + # attribute are array values by applying :to_a to the value. + def self.array_attribute(name) + @@non_nil_attributes << ["@#{name}".intern, []] + + @@array_attributes << name + @@attributes << [name, []] + @@default_value[name] = [] + code = %{ + def #{name} + @#{name} ||= [] + end + def #{name}=(value) + @#{name} = Array(value) + end + } + + module_eval code, __FILE__, __LINE__ - 9 + end + + # Same as attribute above, but also records this attribute as mandatory. + def self.required_attribute(*args) + @@required_attributes << args.first + attribute(*args) + end + + # Sometimes we don't want the world to use a setter method for a particular attribute. + # +read_only+ makes it private so we can still use it internally. + def self.read_only(*names) + names.each do |name| + private "#{name}=" + end + end + + # Shortcut for creating several attributes at once (each with a default value of + # +nil+). + def self.attributes(*args) + args.each do |arg| + attribute(arg, nil) + end + end + + # Some attributes require special behaviour when they are accessed. This allows for + # that. + def self.overwrite_accessor(name, &block) + remove_method name + define_method(name, &block) + end + + # Defines a _singular_ version of an existing _plural_ attribute + # (i.e. one whose value is expected to be an array). This means + # just creating a helper method that takes a single value and + # appends it to the array. These are created for convenience, so + # that in a spec, one can write + # + # s.require_path = 'mylib' + # + # instead of + # + # s.require_paths = ['mylib'] + # + # That above convenience is available courtesy of + # + # attribute_alias_singular :require_path, :require_paths + # + def self.attribute_alias_singular(singular, plural) + define_method("#{singular}=") { |val| + send("#{plural}=", [val]) + } + define_method("#{singular}") { + val = send("#{plural}") + val.nil? ? nil : val.first + } + end + + ## + # Dump only crucial instance variables. + #-- + # MAINTAIN ORDER! + + def _dump(limit) + Marshal.dump [ + @rubygems_version, + @specification_version, + @name, + @version, + (Time === @date ? @date : (require 'time'; Time.parse(@date.to_s))), + @summary, + @required_ruby_version, + @required_rubygems_version, + @original_platform, + @dependencies, + @rubyforge_project, + @email, + @authors, + @description, + @homepage, + @has_rdoc, + @new_platform, + ] + end + + ## + # Load custom marshal format, re-initializing defaults as needed + + def self._load(str) + array = Marshal.load str + + spec = Gem::Specification.new + spec.instance_variable_set :@specification_version, array[1] + + current_version = CURRENT_SPECIFICATION_VERSION + + field_count = if spec.specification_version > current_version then + spec.instance_variable_set :@specification_version, + current_version + MARSHAL_FIELDS[current_version] + else + MARSHAL_FIELDS[spec.specification_version] + end + + if array.size < field_count then + raise TypeError, "invalid Gem::Specification format #{array.inspect}" + end + + spec.instance_variable_set :@rubygems_version, array[0] + # spec version + spec.instance_variable_set :@name, array[2] + spec.instance_variable_set :@version, array[3] + spec.instance_variable_set :@date, array[4] + spec.instance_variable_set :@summary, array[5] + spec.instance_variable_set :@required_ruby_version, array[6] + spec.instance_variable_set :@required_rubygems_version, array[7] + spec.instance_variable_set :@original_platform, array[8] + spec.instance_variable_set :@dependencies, array[9] + spec.instance_variable_set :@rubyforge_project, array[10] + spec.instance_variable_set :@email, array[11] + spec.instance_variable_set :@authors, array[12] + spec.instance_variable_set :@description, array[13] + spec.instance_variable_set :@homepage, array[14] + spec.instance_variable_set :@has_rdoc, array[15] + spec.instance_variable_set :@new_platform, array[16] + spec.instance_variable_set :@platform, array[16].to_s + spec.instance_variable_set :@loaded, false + + spec + end + + # REQUIRED gemspec attributes ------------------------------------ + + required_attribute :rubygems_version, Gem::RubyGemsVersion + required_attribute :specification_version, CURRENT_SPECIFICATION_VERSION + required_attribute :name + required_attribute :version + required_attribute :date, TODAY + required_attribute :summary + required_attribute :require_paths, ['lib'] + + # OPTIONAL gemspec attributes ------------------------------------ + + attributes :email, :homepage, :rubyforge_project, :description + attributes :autorequire, :default_executable + + attribute :bindir, 'bin' + attribute :has_rdoc, false + attribute :required_ruby_version, Gem::Requirement.default + attribute :required_rubygems_version, Gem::Requirement.default + attribute :platform, Gem::Platform::RUBY + + attribute :signing_key, nil + attribute :cert_chain, [] + attribute :post_install_message, nil + + array_attribute :authors + array_attribute :files + array_attribute :test_files + array_attribute :rdoc_options + array_attribute :extra_rdoc_files + array_attribute :executables + + # Array of extensions to build. See Gem::Installer#build_extensions for + # valid values. + + array_attribute :extensions + array_attribute :requirements + array_attribute :dependencies + + read_only :dependencies + + def runtime_dependencies + dependencies.select { |d| d.type == :runtime || d.type == nil } + end + + def development_dependencies + dependencies.select { |d| d.type == :development } + end + + # ALIASED gemspec attributes ------------------------------------- + + attribute_alias_singular :executable, :executables + attribute_alias_singular :author, :authors + attribute_alias_singular :require_path, :require_paths + attribute_alias_singular :test_file, :test_files + + # DEPRECATED gemspec attributes ---------------------------------- + + def test_suite_file + warn 'test_suite_file deprecated, use test_files' + test_files.first + end + + def test_suite_file=(val) + warn 'test_suite_file= deprecated, use test_files=' + @test_files = [] unless defined? @test_files + @test_files << val + end + + # true when this gemspec has been loaded from a specifications directory. + # This attribute is not persisted. + + attr_writer :loaded + + # Path this gemspec was loaded from. This attribute is not persisted. + attr_accessor :loaded_from + + # Special accessor behaviours (overwriting default) -------------- + + overwrite_accessor :version= do |version| + @version = Version.create(version) + end + + overwrite_accessor :platform do + @new_platform + end + + overwrite_accessor :platform= do |platform| + if @original_platform.nil? or + @original_platform == Gem::Platform::RUBY then + @original_platform = platform + end + + case platform + when Gem::Platform::CURRENT then + @new_platform = Gem::Platform.local + @original_platform = @new_platform.to_s + + when Gem::Platform then + @new_platform = platform + + # legacy constants + when nil, Gem::Platform::RUBY then + @new_platform = Gem::Platform::RUBY + when 'mswin32' then # was Gem::Platform::WIN32 + @new_platform = Gem::Platform.new 'x86-mswin32' + when 'i586-linux' then # was Gem::Platform::LINUX_586 + @new_platform = Gem::Platform.new 'x86-linux' + when 'powerpc-darwin' then # was Gem::Platform::DARWIN + @new_platform = Gem::Platform.new 'ppc-darwin' + else + @new_platform = Gem::Platform.new platform + end + + @platform = @new_platform.to_s + + @new_platform + end + + overwrite_accessor :required_ruby_version= do |value| + @required_ruby_version = Gem::Requirement.create(value) + end + + overwrite_accessor :required_rubygems_version= do |value| + @required_rubygems_version = Gem::Requirement.create(value) + end + + overwrite_accessor :date= do |date| + # We want to end up with a Time object with one-day resolution. + # This is the cleanest, most-readable, faster-than-using-Date + # way to do it. + case date + when String then + @date = if /\A(\d{4})-(\d{2})-(\d{2})\Z/ =~ date then + Time.local($1.to_i, $2.to_i, $3.to_i) + else + require 'time' + Time.parse date + end + when Time then + @date = Time.local(date.year, date.month, date.day) + when Date then + @date = Time.local(date.year, date.month, date.day) + else + @date = TODAY + end + end + + overwrite_accessor :date do + self.date = nil if @date.nil? # HACK Sets the default value for date + @date + end + + overwrite_accessor :summary= do |str| + @summary = if str then + str.strip. + gsub(/(\w-)\n[ \t]*(\w)/, '\1\2'). + gsub(/\n[ \t]*/, " ") + end + end + + overwrite_accessor :description= do |str| + @description = if str then + str.strip. + gsub(/(\w-)\n[ \t]*(\w)/, '\1\2'). + gsub(/\n[ \t]*/, " ") + end + end + + overwrite_accessor :default_executable do + begin + if defined?(@default_executable) and @default_executable + result = @default_executable + elsif @executables and @executables.size == 1 + result = Array(@executables).first + else + result = nil + end + result + rescue + nil + end + end + + def add_bindir(executables) + return nil if executables.nil? + + if @bindir then + Array(executables).map { |e| File.join(@bindir, e) } + else + executables + end + rescue + return nil + end + + overwrite_accessor :files do + result = [] + result.push(*@files) if defined?(@files) + result.push(*@test_files) if defined?(@test_files) + result.push(*(add_bindir(@executables))) + result.push(*@extra_rdoc_files) if defined?(@extra_rdoc_files) + result.push(*@extensions) if defined?(@extensions) + result.uniq.compact + end + + # Files in the Gem under one of the require_paths + def lib_files + @files.select do |file| + require_paths.any? do |path| + file.index(path) == 0 + end + end + end + + overwrite_accessor :test_files do + # Handle the possibility that we have @test_suite_file but not + # @test_files. This will happen when an old gem is loaded via + # YAML. + if defined? @test_suite_file then + @test_files = [@test_suite_file].flatten + @test_suite_file = nil + end + if defined?(@test_files) and @test_files then + @test_files + else + @test_files = [] + end + end + + # Predicates ----------------------------------------------------- + + def loaded?; @loaded ? true : false ; end + def has_rdoc?; has_rdoc ? true : false ; end + def has_unit_tests?; not test_files.empty?; end + alias has_test_suite? has_unit_tests? # (deprecated) + + # Constructors --------------------------------------------------- + + # Specification constructor. Assigns the default values to the + # attributes, adds this spec to the list of loaded specs (see + # Specification.list), and yields itself for further initialization. + # + def initialize + @new_platform = nil + assign_defaults + @loaded = false + @loaded_from = nil + @@list << self + + yield self if block_given? + + @@gather.call(self) if @@gather + end + + # Each attribute has a default value (possibly nil). Here, we + # initialize all attributes to their default value. This is + # done through the accessor methods, so special behaviours will + # be honored. Furthermore, we take a _copy_ of the default so + # each specification instance has its own empty arrays, etc. + def assign_defaults + @@nil_attributes.each do |name| + instance_variable_set name, nil + end + + @@non_nil_attributes.each do |name, default| + value = case default + when Time, Numeric, Symbol, true, false, nil then default + else default.dup + end + + instance_variable_set name, value + end + + # HACK + instance_variable_set :@new_platform, Gem::Platform::RUBY + end + + # Special loader for YAML files. When a Specification object is + # loaded from a YAML file, it bypasses the normal Ruby object + # initialization routine (#initialize). This method makes up for + # that and deals with gems of different ages. + # + # 'input' can be anything that YAML.load() accepts: String or IO. + # + def self.from_yaml(input) + input = normalize_yaml_input input + spec = YAML.load input + + if spec && spec.class == FalseClass then + raise Gem::EndOfYAMLException + end + + unless Gem::Specification === spec then + raise Gem::Exception, "YAML data doesn't evaluate to gem specification" + end + + unless (spec.instance_variables.include? '@specification_version' or + spec.instance_variables.include? :@specification_version) and + spec.instance_variable_get :@specification_version + spec.instance_variable_set :@specification_version, + NONEXISTENT_SPECIFICATION_VERSION + end + + spec + end + + def self.load(filename) + gemspec = nil + fail "NESTED Specification.load calls not allowed!" if @@gather + @@gather = proc { |gs| gemspec = gs } + data = File.read(filename) + eval(data) + gemspec + ensure + @@gather = nil + end + + # Make sure the yaml specification is properly formatted with dashes. + def self.normalize_yaml_input(input) + result = input.respond_to?(:read) ? input.read : input + result = "--- " + result unless result =~ /^--- / + result + end + + # Instance methods ----------------------------------------------- + + # Sets the rubygems_version to Gem::RubyGemsVersion. + # + def mark_version + @rubygems_version = RubyGemsVersion + end + + # Ignore unknown attributes if the + def method_missing(sym, *a, &b) # :nodoc: + if @specification_version > CURRENT_SPECIFICATION_VERSION and + sym.to_s =~ /=$/ then + warn "ignoring #{sym} loading #{full_name}" if $DEBUG + else + super + end + end + + # Adds a development dependency to this Gem. For example, + # + # spec.add_development_dependency('jabber4r', '> 0.1', '<= 0.5') + # + # Development dependencies aren't installed by default, and + # aren't activated when a gem is required. + # + # gem:: [String or Gem::Dependency] The Gem name/dependency. + # requirements:: [default=">= 0"] The version requirements. + def add_development_dependency(gem, *requirements) + add_dependency_with_type(gem, :development, *requirements) + end + + # Adds a runtime dependency to this Gem. For example, + # + # spec.add_runtime_dependency('jabber4r', '> 0.1', '<= 0.5') + # + # gem:: [String or Gem::Dependency] The Gem name/dependency. + # requirements:: [default=">= 0"] The version requirements. + def add_runtime_dependency(gem, *requirements) + add_dependency_with_type(gem, :runtime, *requirements) + end + + alias add_dependency add_runtime_dependency + + # Returns the full name (name-version) of this Gem. Platform information + # is included (name-version-platform) if it is specified (and not the + # default Ruby platform). + # + def full_name + if platform == Gem::Platform::RUBY or platform.nil? then + "#{@name}-#{@version}" + else + "#{@name}-#{@version}-#{platform}" + end + end + + # Returns the full name (name-version) of this gemspec using the original + # platform. + # + def original_name # :nodoc: + if platform == Gem::Platform::RUBY or platform.nil? then + "#{@name}-#{@version}" + else + "#{@name}-#{@version}-#{@original_platform}" + end + end + + ## + # The full path to the gem (install path + full name). + + def full_gem_path + path = File.join installation_path, 'gems', full_name + return path if File.directory? path + File.join installation_path, 'gems', original_name + end + + ## + # The default (generated) file name of the gem. + + def file_name + full_name + ".gem" + end + + ## + # The directory that this gem was installed into. + + def installation_path + path = File.dirname(@loaded_from).split(File::SEPARATOR)[0..-2] + path = path.join File::SEPARATOR + File.expand_path path + end + + # Checks if this Specification meets the requirement of the supplied + # dependency. + # + # dependency:: [Gem::Dependency] the dependency to check + # return:: [Boolean] true if dependency is met, otherwise false + # + def satisfies_requirement?(dependency) + return @name == dependency.name && + dependency.version_requirements.satisfied_by?(@version) + end + + # Comparison methods --------------------------------------------- + + def sort_obj + [@name, @version.to_ints, @new_platform == Gem::Platform::RUBY ? -1 : 1] + end + + def <=>(other) # :nodoc: + sort_obj <=> other.sort_obj + end + + # Tests specs for equality (across all attributes). + def ==(other) # :nodoc: + self.class === other && same_attributes?(other) + end + + alias eql? == # :nodoc: + + def same_attributes?(other) + @@attributes.each do |name, default| + return false unless self.send(name) == other.send(name) + end + true + end + private :same_attributes? + + def hash # :nodoc: + @@attributes.inject(0) { |hash_code, (name, default_value)| + n = self.send(name).hash + hash_code + n + } + end + + # Export methods (YAML and Ruby code) ---------------------------- + + def to_yaml(opts = {}) # :nodoc: + mark_version + + attributes = @@attributes.map { |name,| name.to_s }.sort + attributes = attributes - %w[name version platform] + + yaml = YAML.quick_emit object_id, opts do |out| + out.map taguri, to_yaml_style do |map| + map.add 'name', @name + map.add 'version', @version + platform = case @original_platform + when nil, '' then + 'ruby' + when String then + @original_platform + else + @original_platform.to_s + end + map.add 'platform', platform + + attributes.each do |name| + map.add name, instance_variable_get("@#{name}") + end + end + end + end + + def yaml_initialize(tag, vals) # :nodoc: + vals.each do |ivar, val| + instance_variable_set "@#{ivar}", val + end + + @original_platform = @platform # for backwards compatibility + self.platform = Gem::Platform.new @platform + end + + ## + # Returns a Ruby code representation of this specification, such that it + # can be eval'ed and reconstruct the same specification later. Attributes + # that still have their default values are omitted. + + def to_ruby + mark_version + result = [] + result << "Gem::Specification.new do |s|" + + result << " s.name = #{ruby_code name}" + result << " s.version = #{ruby_code version}" + unless platform.nil? or platform == Gem::Platform::RUBY then + result << " s.platform = #{ruby_code original_platform}" + end + result << "" + result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version=" + + handled = [ + :dependencies, + :name, + :platform, + :required_rubygems_version, + :specification_version, + :version, + ] + + attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s } + + attributes.each do |attr_name, default| + next if handled.include? attr_name + current_value = self.send(attr_name) + if current_value != default or + self.class.required_attribute? attr_name then + result << " s.#{attr_name} = #{ruby_code current_value}" + end + end + + result << nil + result << " if s.respond_to? :specification_version then" + result << " current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION" + result << " s.specification_version = #{specification_version}" + result << nil + + result << " if current_version >= 3 then" + + unless dependencies.empty? then + dependencies.each do |dep| + version_reqs_param = dep.requirements_list.inspect + dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK + result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{version_reqs_param})" + end + end + + result << " else" + + unless dependencies.empty? then + dependencies.each do |dep| + version_reqs_param = dep.requirements_list.inspect + result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" + end + end + + result << ' end' + + result << " else" + dependencies.each do |dep| + version_reqs_param = dep.requirements_list.inspect + result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" + end + result << " end" + + result << "end" + result << nil + + result.join "\n" + end + + # Validation and normalization methods --------------------------- + + # Checks that the specification contains all required fields, and + # does a very basic sanity check. + # + # Raises InvalidSpecificationException if the spec does not pass + # the checks.. + def validate + extend Gem::UserInteraction + + normalize + + if rubygems_version != RubyGemsVersion then + raise Gem::InvalidSpecificationException, + "expected RubyGems version #{RubyGemsVersion}, was #{rubygems_version}" + end + + @@required_attributes.each do |symbol| + unless self.send symbol then + raise Gem::InvalidSpecificationException, + "missing value for attribute #{symbol}" + end + end + + if require_paths.empty? then + raise Gem::InvalidSpecificationException, + "specification must have at least one require_path" + end + + case platform + when Gem::Platform, Platform::RUBY then # ok + else + raise Gem::InvalidSpecificationException, + "invalid platform #{platform.inspect}, see Gem::Platform" + end + + unless Array === authors and + authors.all? { |author| String === author } then + raise Gem::InvalidSpecificationException, + 'authors must be Array of Strings' + end + + # Warnings + + %w[author email homepage rubyforge_project summary].each do |attribute| + value = self.send attribute + alert_warning "no #{attribute} specified" if value.nil? or value.empty? + end + + alert_warning "RDoc will not be generated (has_rdoc == false)" unless + has_rdoc + + alert_warning "deprecated autorequire specified" if autorequire + + executables.each do |executable| + executable_path = File.join bindir, executable + shebang = File.read(executable_path, 2) == '#!' + + alert_warning "#{executable_path} is missing #! line" unless shebang + end + + true + end + + # Normalize the list of files so that: + # * All file lists have redundancies removed. + # * Files referenced in the extra_rdoc_files are included in the + # package file list. + # + # Also, the summary and description are converted to a normal + # format. + def normalize + if defined?(@extra_rdoc_files) and @extra_rdoc_files then + @extra_rdoc_files.uniq! + @files ||= [] + @files.concat(@extra_rdoc_files) + end + @files.uniq! if @files + end + + # Dependency methods --------------------------------------------- + + # Return a list of all gems that have a dependency on this + # gemspec. The list is structured with entries that conform to: + # + # [depending_gem, dependency, [list_of_gems_that_satisfy_dependency]] + # + # return:: [Array] [[dependent_gem, dependency, [list_of_satisfiers]]] + # + def dependent_gems + out = [] + Gem.source_index.each do |name,gem| + gem.dependencies.each do |dep| + if self.satisfies_requirement?(dep) then + sats = [] + find_all_satisfiers(dep) do |sat| + sats << sat + end + out << [gem, dep, sats] + end + end + end + out + end + + def to_s + "#" + end + + private + + def add_dependency_with_type(dependency, type, *requirements) + requirements = if requirements.empty? then + Gem::Requirement.default + else + requirements.flatten + end + + unless dependency.respond_to?(:name) && + dependency.respond_to?(:version_requirements) + + dependency = Dependency.new(dependency, requirements, type) + end + + dependencies << dependency + end + + def find_all_satisfiers(dep) + Gem.source_index.each do |name,gem| + if(gem.satisfies_requirement?(dep)) then + yield gem + end + end + end + + # Return a string containing a Ruby code representation of the + # given object. + def ruby_code(obj) + case obj + when String then '%q{' + obj + '}' + when Array then obj.inspect + when Gem::Version then obj.to_s.inspect + when Date then '%q{' + obj.strftime('%Y-%m-%d') + '}' + when Time then '%q{' + obj.strftime('%Y-%m-%d') + '}' + when Numeric then obj.inspect + when true, false, nil then obj.inspect + when Gem::Platform then "Gem::Platform.new(#{obj.to_a.inspect})" + when Gem::Requirement then "Gem::Requirement.new(#{obj.to_s.inspect})" + else raise Exception, "ruby_code case not handled: #{obj.class}" + end + end + + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/test_utilities.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/test_utilities.rb new file mode 100644 index 0000000000..0486db2b32 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/test_utilities.rb @@ -0,0 +1,120 @@ +require 'tempfile' +require 'rubygems' +require 'rubygems/remote_fetcher' + +## +# A fake Gem::RemoteFetcher for use in tests or to avoid real live HTTP +# requests when testing code that uses RubyGems. +# +# Example: +# +# @fetcher = Gem::FakeFetcher.new +# @fetcher.data['http://gems.example.com/yaml'] = source_index.to_yaml +# Gem::RemoteFetcher.fetcher = @fetcher +# +# # invoke RubyGems code +# +# paths = @fetcher.paths +# assert_equal 'http://gems.example.com/yaml', paths.shift +# assert paths.empty?, paths.join(', ') +# +# See RubyGems' tests for more examples of FakeFetcher. + +class Gem::FakeFetcher + + attr_reader :data + attr_accessor :paths + + def initialize + @data = {} + @paths = [] + end + + def fetch_path(path) + path = path.to_s + @paths << path + raise ArgumentError, 'need full URI' unless path =~ %r'^http://' + data = @data[path] + + if data.nil? then + raise Gem::RemoteFetcher::FetchError.new('no data', path) + end + + data.respond_to?(:call) ? data.call : data + end + + def fetch_size(path) + path = path.to_s + @paths << path + raise ArgumentError, 'need full URI' unless path =~ %r'^http://' + data = @data[path] + + if data.nil? then + raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", nil) + end + + data.respond_to?(:call) ? data.call : data.length + end + + def download spec, source_uri, install_dir = Gem.dir + name = "#{spec.full_name}.gem" + path = File.join(install_dir, 'cache', name) + + Gem.ensure_gem_subdirectories install_dir + + if source_uri =~ /^http/ then + File.open(path, "wb") do |f| + f.write fetch_path(File.join(source_uri, "gems", name)) + end + else + FileUtils.cp source_uri, path + end + + path + end + +end + +# :stopdoc: +class Gem::RemoteFetcher + + def self.fetcher=(fetcher) + @fetcher = fetcher + end + +end +# :startdoc: + +## +# A StringIO duck-typed class that uses Tempfile instead of String as the +# backing store. +#-- +# This class was added to flush out problems in Rubinius' IO implementation. + +class TempIO + + @@count = 0 + + def initialize(string = '') + @tempfile = Tempfile.new "TempIO-#{@@count += 1}" + @tempfile.binmode + @tempfile.write string + @tempfile.rewind + end + + def method_missing(meth, *args, &block) + @tempfile.send(meth, *args, &block) + end + + def respond_to?(meth) + @tempfile.respond_to? meth + end + + def string + @tempfile.flush + + Gem.read_binary @tempfile.path + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/timer.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/timer.rb new file mode 100644 index 0000000000..06250f26b5 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/timer.rb @@ -0,0 +1,25 @@ +# +# This file defines a $log variable for logging, and a time() method for recording timing +# information. +# +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + + +$log = Object.new +def $log.debug(str) + STDERR.puts str +end + +def time(msg, width=25) + t = Time.now + return_value = yield + elapsed = Time.now.to_f - t.to_f + elapsed = sprintf("%3.3f", elapsed) + $log.debug "#{msg.ljust(width)}: #{elapsed}s" + return_value +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/uninstaller.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/uninstaller.rb new file mode 100644 index 0000000000..2ad961972b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/uninstaller.rb @@ -0,0 +1,208 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'fileutils' +require 'rubygems' +require 'rubygems/dependency_list' +require 'rubygems/doc_manager' +require 'rubygems/user_interaction' + +## +# An Uninstaller. + +class Gem::Uninstaller + + include Gem::UserInteraction + + ## + # Constructs an Uninstaller instance + # + # gem:: [String] The Gem name to uninstall + + def initialize(gem, options = {}) + @gem = gem + @version = options[:version] || Gem::Requirement.default + gem_home = options[:install_dir] || Gem.dir + @gem_home = File.expand_path gem_home + @force_executables = options[:executables] + @force_all = options[:all] + @force_ignore = options[:ignore] + @bin_dir = options[:bin_dir] + end + + ## + # Performs the uninstall of the Gem. This removes the spec, the + # Gem directory, and the cached .gem file, + + def uninstall + list = Gem.source_index.search(/^#{@gem}$/, @version) + + if list.empty? then + raise Gem::InstallError, "Unknown gem #{@gem} #{@version}" + elsif list.size > 1 && @force_all + remove_all(list.dup) + remove_executables(list.last) + elsif list.size > 1 + say + gem_names = list.collect {|gem| gem.full_name} + ["All versions"] + gem_name, index = + choose_from_list("Select gem to uninstall:", gem_names) + if index == list.size + remove_all(list.dup) + remove_executables(list.last) + elsif index >= 0 && index < list.size + to_remove = list[index] + remove(to_remove, list) + remove_executables(to_remove) + else + say "Error: must enter a number [1-#{list.size+1}]" + end + else + remove(list[0], list.dup) + remove_executables(list.last) + end + end + + ## + # Removes installed executables and batch files (windows only) for + # +gemspec+. + + def remove_executables(gemspec) + return if gemspec.nil? + + if gemspec.executables.size > 0 then + bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home) + + list = Gem.source_index.search(gemspec.name).delete_if { |spec| + spec.version == gemspec.version + } + + executables = gemspec.executables.clone + + list.each do |spec| + spec.executables.each do |exe_name| + executables.delete(exe_name) + end + end + + return if executables.size == 0 + + answer = if @force_executables.nil? then + ask_yes_no("Remove executables:\n" \ + "\t#{gemspec.executables.join(", ")}\n\nin addition to the gem?", + true) # " # appease ruby-mode - don't ask + else + @force_executables + end + + unless answer then + say "Executables and scripts will remain installed." + else + raise Gem::FilePermissionError, bindir unless File.writable? bindir + + gemspec.executables.each do |exe_name| + say "Removing #{exe_name}" + FileUtils.rm_f File.join(bindir, exe_name) + FileUtils.rm_f File.join(bindir, "#{exe_name}.bat") + end + end + end + end + + ## + # Removes all gems in +list+. + # + # NOTE: removes uninstalled gems from +list+. + + def remove_all(list) + list.dup.each { |spec| remove spec, list } + end + + ## + # spec:: the spec of the gem to be uninstalled + # list:: the list of all such gems + # + # Warning: this method modifies the +list+ parameter. Once it has + # uninstalled a gem, it is removed from that list. + + def remove(spec, list) + unless dependencies_ok? spec then + raise Gem::DependencyRemovalException, + "Uninstallation aborted due to dependent gem(s)" + end + + unless path_ok? spec then + e = Gem::GemNotInHomeException.new \ + "Gem is not installed in directory #{@gem_home}" + e.spec = spec + + raise e + end + + raise Gem::FilePermissionError, spec.installation_path unless + File.writable?(spec.installation_path) + + FileUtils.rm_rf spec.full_gem_path + + original_platform_name = [ + spec.name, spec.version, spec.original_platform].join '-' + + spec_dir = File.join spec.installation_path, 'specifications' + gemspec = File.join spec_dir, "#{spec.full_name}.gemspec" + + unless File.exist? gemspec then + gemspec = File.join spec_dir, "#{original_platform_name}.gemspec" + end + + FileUtils.rm_rf gemspec + + cache_dir = File.join spec.installation_path, 'cache' + gem = File.join cache_dir, "#{spec.full_name}.gem" + + unless File.exist? gem then + gem = File.join cache_dir, "#{original_platform_name}.gem" + end + + FileUtils.rm_rf gem + + Gem::DocManager.new(spec).uninstall_doc + + say "Successfully uninstalled #{spec.full_name}" + + list.delete spec + end + + def path_ok?(spec) + full_path = File.join @gem_home, 'gems', spec.full_name + original_path = File.join @gem_home, 'gems', spec.original_name + + full_path == spec.full_gem_path || original_path == spec.full_gem_path + end + + def dependencies_ok?(spec) + return true if @force_ignore + + source_index = Gem::SourceIndex.from_installed_gems + deplist = Gem::DependencyList.from_source_index source_index + deplist.ok_to_remove?(spec.full_name) || ask_if_ok(spec) + end + + def ask_if_ok(spec) + msg = [''] + msg << 'You have requested to uninstall the gem:' + msg << "\t#{spec.full_name}" + spec.dependent_gems.each do |gem,dep,satlist| + msg << + ("#{gem.name}-#{gem.version} depends on " + + "[#{dep.name} (#{dep.version_requirements})]") + end + msg << 'If you remove this gems, one or more dependencies will not be met.' + msg << 'Continue with Uninstall?' + return ask_yes_no(msg.join("\n"), true) + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/user_interaction.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/user_interaction.rb new file mode 100644 index 0000000000..30a728c597 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/user_interaction.rb @@ -0,0 +1,360 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +module Gem + + ## + # Module that defines the default UserInteraction. Any class including this + # module will have access to the +ui+ method that returns the default UI. + + module DefaultUserInteraction + + ## + # The default UI is a class variable of the singleton class for this + # module. + + @ui = nil + + ## + # Return the default UI. + + def self.ui + @ui ||= Gem::ConsoleUI.new + end + + ## + # Set the default UI. If the default UI is never explicitly set, a simple + # console based UserInteraction will be used automatically. + + def self.ui=(new_ui) + @ui = new_ui + end + + ## + # Use +new_ui+ for the duration of +block+. + + def self.use_ui(new_ui) + old_ui = @ui + @ui = new_ui + yield + ensure + @ui = old_ui + end + + ## + # See DefaultUserInteraction::ui + + def ui + DefaultUserInteraction.ui + end + + ## + # See DefaultUserInteraction::ui= + + def ui=(new_ui) + DefaultUserInteraction.ui = new_ui + end + + ## + # See DefaultUserInteraction::use_ui + + def use_ui(new_ui, &block) + DefaultUserInteraction.use_ui(new_ui, &block) + end + + end + + ## + # Make the default UI accessable without the "ui." prefix. Classes + # including this module may use the interaction methods on the default UI + # directly. Classes may also reference the ui and ui= methods. + # + # Example: + # + # class X + # include Gem::UserInteraction + # + # def get_answer + # n = ask("What is the meaning of life?") + # end + # end + + module UserInteraction + + include DefaultUserInteraction + + [:alert, + :alert_error, + :alert_warning, + :ask, + :ask_yes_no, + :choose_from_list, + :say, + :terminate_interaction ].each do |methname| + class_eval %{ + def #{methname}(*args) + ui.#{methname}(*args) + end + }, __FILE__, __LINE__ + end + end + + ## + # StreamUI implements a simple stream based user interface. + + class StreamUI + + attr_reader :ins, :outs, :errs + + def initialize(in_stream, out_stream, err_stream=STDERR) + @ins = in_stream + @outs = out_stream + @errs = err_stream + end + + ## + # Choose from a list of options. +question+ is a prompt displayed above + # the list. +list+ is a list of option strings. Returns the pair + # [option_name, option_index]. + + def choose_from_list(question, list) + @outs.puts question + + list.each_with_index do |item, index| + @outs.puts " #{index+1}. #{item}" + end + + @outs.print "> " + @outs.flush + + result = @ins.gets + + return nil, nil unless result + + result = result.strip.to_i - 1 + return list[result], result + end + + ## + # Ask a question. Returns a true for yes, false for no. If not connected + # to a tty, raises an exception if default is nil, otherwise returns + # default. + + def ask_yes_no(question, default=nil) + unless @ins.tty? then + if default.nil? then + raise Gem::OperationNotSupportedError, + "Not connected to a tty and no default specified" + else + return default + end + end + + qstr = case default + when nil + 'yn' + when true + 'Yn' + else + 'yN' + end + + result = nil + + while result.nil? + result = ask("#{question} [#{qstr}]") + result = case result + when /^[Yy].*/ + true + when /^[Nn].*/ + false + when /^$/ + default + else + nil + end + end + + return result + end + + ## + # Ask a question. Returns an answer if connected to a tty, nil otherwise. + + def ask(question) + return nil if not @ins.tty? + + @outs.print(question + " ") + @outs.flush + + result = @ins.gets + result.chomp! if result + result + end + + ## + # Display a statement. + + def say(statement="") + @outs.puts statement + end + + ## + # Display an informational alert. Will ask +question+ if it is not nil. + + def alert(statement, question=nil) + @outs.puts "INFO: #{statement}" + ask(question) if question + end + + ## + # Display a warning in a location expected to get error messages. Will + # ask +question+ if it is not nil. + + def alert_warning(statement, question=nil) + @errs.puts "WARNING: #{statement}" + ask(question) if question + end + + ## + # Display an error message in a location expected to get error messages. + # Will ask +question+ if it is not nil. + + def alert_error(statement, question=nil) + @errs.puts "ERROR: #{statement}" + ask(question) if question + end + + ## + # Terminate the application with exit code +status+, running any exit + # handlers that might have been defined. + + def terminate_interaction(status = 0) + raise Gem::SystemExitException, status + end + + ## + # Return a progress reporter object chosen from the current verbosity. + + def progress_reporter(*args) + case Gem.configuration.verbose + when nil, false + SilentProgressReporter.new(@outs, *args) + when true + SimpleProgressReporter.new(@outs, *args) + else + VerboseProgressReporter.new(@outs, *args) + end + end + + ## + # An absolutely silent progress reporter. + + class SilentProgressReporter + attr_reader :count + + def initialize(out_stream, size, initial_message, terminal_message = nil) + end + + def updated(message) + end + + def done + end + end + + ## + # A basic dotted progress reporter. + + class SimpleProgressReporter + include DefaultUserInteraction + + attr_reader :count + + def initialize(out_stream, size, initial_message, + terminal_message = "complete") + @out = out_stream + @total = size + @count = 0 + @terminal_message = terminal_message + + @out.puts initial_message + end + + ## + # Prints out a dot and ignores +message+. + + def updated(message) + @count += 1 + @out.print "." + @out.flush + end + + ## + # Prints out the terminal message. + + def done + @out.puts "\n#{@terminal_message}" + end + + end + + ## + # A progress reporter that prints out messages about the current progress. + + class VerboseProgressReporter + include DefaultUserInteraction + + attr_reader :count + + def initialize(out_stream, size, initial_message, + terminal_message = 'complete') + @out = out_stream + @total = size + @count = 0 + @terminal_message = terminal_message + + @out.puts initial_message + end + + ## + # Prints out the position relative to the total and the +message+. + + def updated(message) + @count += 1 + @out.puts "#{@count}/#{@total}: #{message}" + end + + ## + # Prints out the terminal message. + + def done + @out.puts @terminal_message + end + end + end + + ## + # Subclass of StreamUI that instantiates the user interaction using STDIN, + # STDOUT, and STDERR. + + class ConsoleUI < StreamUI + def initialize + super(STDIN, STDOUT, STDERR) + end + end + + ## + # SilentUI is a UI choice that is absolutely silent. + + class SilentUI + def method_missing(sym, *args, &block) + self + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/validator.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/validator.rb new file mode 100644 index 0000000000..8aaaa5a413 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/validator.rb @@ -0,0 +1,186 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'find' + +require 'rubygems/digest/md5' +require 'rubygems/format' +require 'rubygems/installer' + +module Gem + + ## + # Validator performs various gem file and gem database validation + class Validator + include UserInteraction + + ## + # Given a gem file's contents, validates against its own MD5 checksum + # gem_data:: [String] Contents of the gem file + def verify_gem(gem_data) + raise VerificationError, 'empty gem file' if gem_data.size == 0 + + unless gem_data =~ /MD5SUM/ then + return # Don't worry about it...this sucks. Need to fix MD5 stuff for + # new format + # FIXME + end + + sum_data = gem_data.gsub(/MD5SUM = "([a-z0-9]+)"/, + "MD5SUM = \"#{"F" * 32}\"") + + unless Gem::MD5.hexdigest(sum_data) == $1.to_s then + raise VerificationError, 'invalid checksum for gem file' + end + end + + ## + # Given the path to a gem file, validates against its own MD5 checksum + # + # gem_path:: [String] Path to gem file + def verify_gem_file(gem_path) + open gem_path, Gem.binary_mode do |file| + gem_data = file.read + verify_gem gem_data + end + rescue Errno::ENOENT + raise Gem::VerificationError.new("missing gem file #{gem_path}") + end + + private + def find_files_for_gem(gem_directory) + installed_files = [] + Find.find(gem_directory) {|file_name| + fn = file_name.slice((gem_directory.size)..(file_name.size-1)).sub(/^\//, "") + if(!(fn =~ /CVS/ || File.directory?(fn) || fn == "")) then + installed_files << fn + end + + } + installed_files + end + + + public + ErrorData = Struct.new(:path, :problem) + + ## + # Checks the gem directory for the following potential + # inconsistencies/problems: + # * Checksum gem itself + # * For each file in each gem, check consistency of installed versions + # * Check for files that aren't part of the gem but are in the gems directory + # * 1 cache - 1 spec - 1 directory. + # + # returns a hash of ErrorData objects, keyed on the problem gem's name. + def alien + errors = {} + Gem::SourceIndex.from_installed_gems.each do |gem_name, gem_spec| + errors[gem_name] ||= [] + gem_path = File.join(Gem.dir, "cache", gem_spec.full_name) + ".gem" + spec_path = File.join(Gem.dir, "specifications", gem_spec.full_name) + ".gemspec" + gem_directory = File.join(Gem.dir, "gems", gem_spec.full_name) + installed_files = find_files_for_gem(gem_directory) + + if(!File.exist?(spec_path)) then + errors[gem_name] << ErrorData.new(spec_path, "Spec file doesn't exist for installed gem") + end + + begin + verify_gem_file(gem_path) + open gem_path, Gem.binary_mode do |file| + format = Gem::Format.from_file_by_path(gem_path) + format.file_entries.each do |entry, data| + # Found this file. Delete it from list + installed_files.delete remove_leading_dot_dir(entry['path']) + + next unless data # HACK `gem check -a mkrf` + + open File.join(gem_directory, entry['path']), Gem.binary_mode do |f| + unless Gem::MD5.hexdigest(f.read).to_s == + Gem::MD5.hexdigest(data).to_s then + errors[gem_name] << ErrorData.new(entry['path'], "installed file doesn't match original from gem") + end + end + end + end + rescue VerificationError => e + errors[gem_name] << ErrorData.new(gem_path, e.message) + end + # Clean out directories that weren't explicitly included in the gemspec + # FIXME: This still allows arbitrary incorrect directories. + installed_files.delete_if {|potential_directory| + File.directory?(File.join(gem_directory, potential_directory)) + } + if(installed_files.size > 0) then + errors[gem_name] << ErrorData.new(gem_path, "Unmanaged files in gem: #{installed_files.inspect}") + end + end + errors + end + + class TestRunner + def initialize(suite, ui) + @suite = suite + @ui = ui + end + + def self.run(suite, ui) + require 'test/unit/ui/testrunnermediator' + return new(suite, ui).start + end + + def start + @mediator = Test::Unit::UI::TestRunnerMediator.new(@suite) + @mediator.add_listener(Test::Unit::TestResult::FAULT, &method(:add_fault)) + return @mediator.run_suite + end + + def add_fault(fault) + if Gem.configuration.verbose then + @ui.say fault.long_display + end + end + end + + autoload :TestRunner, 'test/unit/ui/testrunnerutilities' + + ## + # Runs unit tests for a given gem specification + def unit_test(gem_spec) + start_dir = Dir.pwd + Dir.chdir(gem_spec.full_gem_path) + $: << File.join(Gem.dir, "gems", gem_spec.full_name) + # XXX: why do we need this gem_spec when we've already got 'spec'? + test_files = gem_spec.test_files + if test_files.empty? + say "There are no unit tests to run for #{gem_spec.full_name}" + require 'test/unit/ui/console/testrunner' + return Test::Unit::TestResult.new + end + gem gem_spec.name, "= #{gem_spec.version.version}" + test_files.each do |f| require f end + suite = Test::Unit::TestSuite.new("#{gem_spec.name}-#{gem_spec.version}") + ObjectSpace.each_object(Class) do |klass| + suite << klass.suite if (klass < Test::Unit::TestCase) + end + result = TestRunner.run(suite, ui()) + unless result.passed? + alert_error(result.to_s) + #unless ask_yes_no(result.to_s + "...keep Gem?", true) then + #Gem::Uninstaller.new(gem_spec.name, gem_spec.version.version).uninstall + #end + end + result + ensure + Dir.chdir(start_dir) + end + + def remove_leading_dot_dir(path) + path.sub(/^\.\//, "") + end + end +end diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/version.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/version.rb new file mode 100644 index 0000000000..ff4a7bf079 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/version.rb @@ -0,0 +1,167 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems' + +## +# The Version class processes string versions into comparable values + +class Gem::Version + + include Comparable + + attr_reader :ints + + attr_reader :version + + ## + # Returns true if +version+ is a valid version string. + + def self.correct?(version) + case version + when Integer, /\A\s*(\d+(\.\d+)*)*\s*\z/ then true + else false + end + end + + ## + # Factory method to create a Version object. Input may be a Version or a + # String. Intended to simplify client code. + # + # ver1 = Version.create('1.3.17') # -> (Version object) + # ver2 = Version.create(ver1) # -> (ver1) + # ver3 = Version.create(nil) # -> nil + + def self.create(input) + if input.respond_to? :version then + input + elsif input.nil? then + nil + else + new input + end + end + + ## + # Constructs a Version from the +version+ string. A version string is a + # series of digits separated by dots. + + def initialize(version) + raise ArgumentError, "Malformed version number string #{version}" unless + self.class.correct?(version) + + self.version = version + end + + def inspect # :nodoc: + "#<#{self.class} #{@version.inspect}>" + end + + # Dump only the raw version string, not the complete object + def marshal_dump + [@version] + end + + # Load custom marshal format + def marshal_load(array) + self.version = array[0] + end + + ## + # Strip ignored trailing zeros. + + def normalize + @ints = build_array_from_version_string + + return if @ints.length == 1 + + @ints.pop while @ints.last == 0 + + @ints = [0] if @ints.empty? + end + + ## + # Returns the text representation of the version + # + # return:: [String] version as string + # + def to_s + @version + end + + ## + # Returns an integer array representation of this Version. + + def to_ints + normalize unless @ints + @ints + end + + def to_yaml_properties + ['@version'] + end + + def version=(version) + @version = version.to_s.strip + normalize + end + + def yaml_initialize(tag, values) + self.version = values['version'] + end + + ## + # Compares this version with +other+ returning -1, 0, or 1 if the other + # version is larger, the same, or smaller than this one. + + def <=>(other) + return nil unless self.class === other + return 1 unless other + @ints <=> other.ints + end + + ## + # A Version is only eql? to another version if it has the same version + # string. "1.0" is not the same version as "1". + + def eql?(other) + self.class === other and @version == other.version + end + + def hash # :nodoc: + @version.hash + end + + # Return a new version object where the next to the last revision + # number is one greater. (e.g. 5.3.1 => 5.4) + def bump + ints = build_array_from_version_string + ints.pop if ints.size > 1 + ints[-1] += 1 + self.class.new(ints.join(".")) + end + + def build_array_from_version_string + @version.to_s.scan(/\d+/).map { |s| s.to_i } + end + private :build_array_from_version_string + + #:stopdoc: + + require 'rubygems/requirement' + + # Gem::Requirement's original definition is nested in Version. + # Although an inappropriate place, current gems specs reference the nested + # class name explicitly. To remain compatible with old software loading + # gemspecs, we leave a copy of original definition in Version, but define an + # alias Gem::Requirement for use everywhere else. + + Requirement = ::Gem::Requirement + + # :startdoc: + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/version_option.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/version_option.rb new file mode 100644 index 0000000000..1374018913 --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/rubygems/version_option.rb @@ -0,0 +1,48 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems' + +# Mixin methods for --version and --platform Gem::Command options. +module Gem::VersionOption + + # Add the --platform option to the option parser. + def add_platform_option(task = command, *wrap) + OptionParser.accept Gem::Platform do |value| + if value == Gem::Platform::RUBY then + value + else + Gem::Platform.new value + end + end + + add_option('--platform PLATFORM', Gem::Platform, + "Specify the platform of gem to #{task}", *wrap) do + |value, options| + unless options[:added_platform] then + Gem.platforms = [Gem::Platform::RUBY] + options[:added_platform] = true + end + + Gem.platforms << value unless Gem.platforms.include? value + end + end + + # Add the --version option to the option parser. + def add_version_option(task = command, *wrap) + OptionParser.accept Gem::Requirement do |value| + Gem::Requirement.new value + end + + add_option('-v', '--version VERSION', Gem::Requirement, + "Specify version of gem to #{task}", *wrap) do + |value, options| + options[:version] = value + end + end + +end + diff --git a/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/ubygems.rb b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/ubygems.rb new file mode 100644 index 0000000000..fec880f73b --- /dev/null +++ b/merlin/external/languages/ruby/redist-libs/ruby/site_ruby/1.8/ubygems.rb @@ -0,0 +1,10 @@ +# This file allows for the running of rubygems with a nice +# command line look-and-feel: ruby -rubygems foo.rb +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + + +require 'rubygems' diff --git a/merlin/main/App.config b/merlin/main/App.config new file mode 100644 index 0000000000..887054a7ff --- /dev/null +++ b/merlin/main/App.config @@ -0,0 +1,19 @@ + + + +
    + + + + + + + + + + + + + + + diff --git a/merlin/main/Utilities/GPPG/Source/GPPG.sln b/merlin/main/Utilities/GPPG/Source/GPPG.sln new file mode 100644 index 0000000000..5316a5e00d --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/GPPG.sln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GPPG", "ParserGenerator\GPPG.csproj", "{827282D8-3E75-4545-A5E4-2406F0D3E184}" +EndProject +Global + GlobalSection(TeamFoundationVersionControl) = preSolution + SccNumberOfProjects = 2 + SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} + SccTeamFoundationServer = http://tkbgitvstfat01:8080/ + SccLocalPath0 = . + SccProjectUniqueName1 = ParserGenerator\\GPPG.csproj + SccProjectName1 = ParserGenerator + SccLocalPath1 = ParserGenerator + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {827282D8-3E75-4545-A5E4-2406F0D3E184}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {827282D8-3E75-4545-A5E4-2406F0D3E184}.Debug|Any CPU.Build.0 = Debug|Any CPU + {827282D8-3E75-4545-A5E4-2406F0D3E184}.Release|Any CPU.ActiveCfg = Release|Any CPU + {827282D8-3E75-4545-A5E4-2406F0D3E184}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/merlin/main/Utilities/GPPG/Source/GPPG.vssscc b/merlin/main/Utilities/GPPG/Source/GPPG.vssscc new file mode 100644 index 0000000000..6cb031bcf5 --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/GPPG.vssscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT" +} diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/CodeGenerator.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/CodeGenerator.cs new file mode 100644 index 0000000000..3c818602e9 --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/CodeGenerator.cs @@ -0,0 +1,422 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Diagnostics; + + +namespace gpcc +{ + public class CodeGenerator + { + public Grammar grammar; + + public void Generate(List states, Grammar grammar) + { + StreamWriter tWrtr = null; + StreamWriter sWrtr = null; + TextWriter save = Console.Out; + + this.grammar = grammar; + if (grammar.OutFName != null) + { + try + { + FileStream fStrm = new FileStream(grammar.OutFName, FileMode.Create); + sWrtr = new StreamWriter(fStrm); + Console.WriteLine("GPPG: sending output to {0}", grammar.OutFName); + Console.SetOut(sWrtr); + } + catch (IOException x) + { + Console.Error.WriteLine("GPPG: Error. File redirect failed"); + Console.Error.WriteLine(x.Message); + Console.Error.WriteLine("GPPG: Terminating ..."); + Environment.Exit(1); + } + } + + if (grammar.TokFName != null) // generate token list file + { + try + { + FileStream fStrm = new FileStream(grammar.TokFName, FileMode.Create); + tWrtr = new StreamWriter(fStrm); + tWrtr.WriteLine("// Symbolic tokens for parser"); + } + catch (IOException x) + { + Console.Error.WriteLine("GPPG: Error. Failed to create token namelist file"); + Console.Error.WriteLine(x.Message); + tWrtr = null; + } + } + + InsertCode(grammar.header); + + if (grammar.Namespace != null) + { + Console.WriteLine("namespace {0}", grammar.Namespace); + Console.WriteLine("{"); + } + + GenerateTokens(grammar.terminals, tWrtr); + + GenerateClassHeader(grammar.ParserName); + InsertCode(grammar.prologCode); + GenerateInitializeMethod(states, grammar.productions); + GenerateInitializeMetadata(grammar.productions, grammar.nonTerminals); + GenerateActionMethod(grammar.productions); + InsertCode(grammar.epilogCode); + GenerateClassFooter(); + + if (grammar.Namespace != null) + Console.WriteLine("}"); + + if (tWrtr != null) + { + tWrtr.WriteLine("// End symbolic tokens for parser"); + tWrtr.Close(); // Close the optional token name stream + } + + if (sWrtr != null) + { + Console.SetOut(save); + sWrtr.Close(); + } + } + + private void GenerateTokens(Dictionary terminals, StreamWriter writer) + { + Console.WriteLine("{0} enum {1} {{", grammar.Visibility, grammar.TokenName); + bool first = true; + foreach (Terminal terminal in terminals.Values) + if (terminal.symbolic) + { + if (!first) + { + Console.Write(", "); + } + + if (terminal.num % 8 == 1) + { + Console.WriteLine(); + Console.Write(" "); + } + Console.Write("{0} = {1}", terminal, terminal.num); + + first = false; + if (writer != null) + { + writer.WriteLine("\t{0}.{1} /* {2} */", grammar.TokenName, terminal.ToString(), terminal.num); + } + } + + Console.WriteLine("};"); + Console.WriteLine(); + } + + private void GenerateValueType() + { + if (grammar.unionType != null) + { + if (grammar.ValueTypeName == null) + // we have a "union" type declared, but no type name declared. + grammar.ValueTypeName = Grammar.DefaultValueTypeName; + Console.WriteLine("{0}{1} struct {2}", + grammar.Visibility, grammar.PartialMark, grammar.ValueTypeName); + InsertCode(grammar.unionType); + } + else if (grammar.ValueTypeName == null) + grammar.ValueTypeName = "int"; + // else we have a value type name declared, but no "union" + } + + private void GenerateScannerBaseClass() + { + Console.WriteLine("// Abstract base class for GPLEX scanners"); + Console.WriteLine("public abstract class ScanBase : IScanner<{0},{1}> {2}", + grammar.ValueTypeName, grammar.LocationTypeName, "{ }"); + Console.WriteLine(); + } + + private void GenerateClassHeader(string name) + { + GenerateValueType(); + if (GPCG.FORGPLEX) GenerateScannerBaseClass(); + Console.WriteLine("{2}{3} class {0}: ShiftReduceParser<{1}, {4}>", + name, grammar.ValueTypeName, grammar.Visibility, grammar.PartialMark, grammar.LocationTypeName); + Console.WriteLine("{"); + } + + + private void GenerateClassFooter() + { + Console.WriteLine("}"); + } + + + private void GenerateInitializeMethod(List states, List productions) + { + Console.WriteLine(" protected sealed override void Initialize(ParserTables tables)"); + Console.WriteLine(" {"); + + Console.WriteLine(" tables.ErrorToken = (int){0}.Error;", grammar.TokenName); + Console.WriteLine(" tables.EofToken = (int){0}.EndOfFile;", grammar.TokenName); + Console.WriteLine(); + + Console.WriteLine(" tables.States = BuildStates(new short[] {"); + + GenerateStates(states); + + Console.WriteLine(" });"); + Console.WriteLine(); + Console.Write(" tables.RuleLhsNonTerminals = new byte[] {"); + + // productions are numbered sequentially starting from 1: + for (int i = 0; i < productions.Count; i++) { + Debug.Assert(i == productions[i].num - 1); + Console.Write(-productions[i].lhs.num); + Console.Write(", "); + } + + Console.WriteLine("};"); + + Console.Write(" tables.RuleRhsLengths = new byte[] {"); + + for (int i = 0; i < productions.Count; i++) { + Debug.Assert(i == productions[i].num - 1); + Console.Write(productions[i].rhs.Count); + Console.Write(", "); + } + + Console.WriteLine("};"); + + Console.WriteLine(" }"); + Console.WriteLine(); + } + + private void GenerateInitializeMetadata(List productions, Dictionary nonTerminals) { + // TODO: parameterize #if symbol + Console.WriteLine("#if DEBUG"); + Console.WriteLine(" protected override void InitializeMetadata(ParserTables tables) {"); + + // non-terminal names: + Console.WriteLine(" tables.NonTerminalNames = new string[] {\"\", "); + int length = 37; + foreach (NonTerminal nonTerminal in nonTerminals.Values) + { + string ss = String.Format("\"{0}\", ", nonTerminal.ToString()); + length += ss.Length; + Console.Write(ss); + if (length > 70) + { + Console.WriteLine(); + Console.Write(" "); + length = 0; + } + } + Console.WriteLine(" };"); + + // rule RHS symbols: + Console.WriteLine(" tables.RuleRhsSymbols = new short[] {"); + + for (int i = 0; i < productions.Count; i++) { + Console.Write(" "); + for (int j = 0; j < productions[i].rhs.Count; j++) { + Console.Write(productions[i].rhs[j].num); + Console.Write(", "); + } + Console.WriteLine("// {0}", productions[i].num); + } + + Console.WriteLine(" };"); + Console.WriteLine(" }"); + Console.WriteLine("#endif"); + Console.WriteLine(); + } + + + private void GenerateStates(List states) { + Console.WriteLine(" {0},", states.Count); + + // states are numbered sequentially starting from 0: + for (int i = 0; i < states.Count; i++) { + Debug.Assert(i == states[i].num); + GenerateState(states[i]); + } + } + + private void GenerateState(State state) + { + int defaultAction = state.GetDefaultAction(); + + Console.Write(" "); + + // default action is always < 0 (reduction) + if (defaultAction == 0 || state.nonTerminalTransitions.Count > 0) { + + // actions: + if (defaultAction == 0) { + Console.Write("{0},", state.parseTable.Count); + } else { + Console.Write("0,"); + } + + // gotos: + Console.Write("{0},", state.nonTerminalTransitions.Count); + } + + if (defaultAction == 0) { + Console.Write(" /* actions: */ "); + foreach (KeyValuePair transition in state.parseTable) { + Console.Write("{0},{1},", transition.Key.num, transition.Value.ToNum()); + } + } else { + Console.Write(" /* default action: */ "); + Console.Write("{0},", defaultAction); + } + + if (state.nonTerminalTransitions.Count > 0) { + Console.Write(" /* gotos: */ "); + foreach (Transition transition in state.nonTerminalTransitions.Values) { + Console.Write("{0},{1},", transition.A.num, transition.next.num); + } + } + + Console.WriteLine(); + } + + private void GenerateRuleRhs(Production production) { + Console.Write(" tables.Rules[{0}].Rhs = new int[] {{", production.num, production.lhs.num); + bool first = true; + foreach (Symbol sym in production.rhs) { + if (!first) + Console.Write(","); + else + first = false; + Console.Write("{0}", sym.num); + } + Console.WriteLine("};"); + } + + private void GenerateActionMethod(List productions) + { + // TODO: parameterize; it seems that 0 is optimal though + const int GroupSizeLog = 0; + + int groupSize = 1 << GroupSizeLog; + int mask = groupSize - 1; + int groupCount = (productions.Count >> GroupSizeLog) + ((productions.Count & mask) != 0 ? 1 : 0); + + Console.WriteLine(" protected override void DoAction(int action)"); + Console.WriteLine(" {"); + + List nonEmptyProductionCounts = new List(); + + for (int g = 0; g < groupCount; g++) { + int nonEmptyCount = 0; + for (int i = 0; i < groupSize; i++) { + int index = (g << GroupSizeLog) + i; + if (index >= productions.Count) break; + if (productions[index].semanticAction != null) { + nonEmptyCount++; + } + } + nonEmptyProductionCounts.Add(nonEmptyCount); + } + + Debug.Assert(nonEmptyProductionCounts.Count == groupCount); + + if (groupCount > 1) { + if (groupSize > 1) { + Console.WriteLine(" int mod = action & 0x{0:X};", mask); + Console.WriteLine(" switch (action >> {0})", GroupSizeLog); + } else { + Console.WriteLine(" switch (action)", GroupSizeLog); + } + + Console.WriteLine(" {"); + + for (int g = 0; g < groupCount; g++) { + if (nonEmptyProductionCounts[g] > 0) { + Console.WriteLine(" case {0}: _{0}({1}); return;", g, groupSize > 1 ? "mod" : ""); + } + } + + Console.WriteLine(" }"); + } else { + Console.WriteLine(" _0(action);"); + } + + Console.WriteLine(" }"); + Console.WriteLine(); + + for (int g = 0; g < groupCount; g++) { + if (nonEmptyProductionCounts[g] > 0) { + Console.WriteLine(" private void _{0}({1})", g, groupSize > 1 ? "int mod" : ""); + Console.WriteLine(" {"); + + if (groupSize > 1) { + Console.WriteLine(" switch (mod)"); + Console.WriteLine(" {"); + } + + for (int i = 0; i < groupSize; i++) { + int index = (g << GroupSizeLog) + i; + if (index >= productions.Count) break; + + Production production = productions[index]; + Debug.Assert(index == production.num - 1); + + if (production.semanticAction != null) { + if (groupSize > 1) { + Console.WriteLine(" case {0}:", i); + } + + Console.WriteLine(" // " + production.ToString()); + + production.semanticAction.GenerateCode(this); + + if (groupSize > 1) { + Console.WriteLine(" return;"); + } + } + } + + if (groupSize > 1) { + Console.WriteLine(" default: return;"); + Console.WriteLine(" }"); + } + + Console.WriteLine(" }"); + Console.WriteLine(); + } + } + } + + private void InsertCode(string code) + { + if (code != null) + { + StringReader reader = new StringReader(code); + while (true) + { + string line = reader.ReadLine(); + if (line == null) + break; + Console.WriteLine("{0}", line); + } + } + } + } +} + + + + + + + diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/GPPG.csproj b/merlin/main/Utilities/GPPG/Source/ParserGenerator/GPPG.csproj new file mode 100644 index 0000000000..f0ff9a41ed --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/GPPG.csproj @@ -0,0 +1,112 @@ + + + Local + 8.0.50727 + 2.0 + {827282D8-3E75-4545-A5E4-2406F0D3E184} + Debug + AnyCPU + + + + + gppg + + + JScript + Grid + IE50 + false + Exe + gppg + OnBuildSuccess + + + + + + + SAK + SAK + SAK + SAK + 3.5 + + + ..\..\..\Bin\ + false + 285212672 + false + + + DEBUG;TRACE + + + true + 4096 + false + + + false + false + false + false + 4 + false + + + ..\..\ + false + 285212672 + false + + + TRACE + + + false + 4096 + false + + + true + false + false + false + 4 + + + + + + + + + Code + + + + + + + Code + + + + + + Code + + + + + + + + + + + + + + \ No newline at end of file diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/GPPG.csproj.vspscc b/merlin/main/Utilities/GPPG/Source/ParserGenerator/GPPG.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/GPPG.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/Grammar.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Grammar.cs new file mode 100644 index 0000000000..b38024135c --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Grammar.cs @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + + + + +using System; +using System.Collections.Generic; + + +namespace gpcc +{ + public class Grammar + { + public const string DefaultValueTypeName = "ValueType"; + + public List productions = new List(); + public string unionType; + public int NumActions = 0; + public string header; // before first %% + public string prologCode; // between %{ %} + public string epilogCode; // after last %% + public NonTerminal startSymbol; + public Production rootProduction; + public Dictionary nonTerminals = new Dictionary(); + public Dictionary terminals = new Dictionary(); + + public bool IsPartial = false; + public string OutFName = null; + public string TokFName = null; + public string Namespace; + public string Visibility = "public"; + public string ParserName = "Parser"; + public string TokenName = "Tokens"; + public string ValueTypeName = null; + public string LocationTypeName = "LexLocation"; + public string PartialMark { get { return (IsPartial ? " partial" : ""); } } + + + public Grammar() + { + LookupTerminal(GrammarToken.Symbol, "NONE"); + LookupTerminal(GrammarToken.Symbol, "ERROR"); + LookupTerminal(GrammarToken.Symbol, "END_OF_FILE"); + } + + + public Terminal LookupTerminal(GrammarToken token, string name) + { + if (!terminals.ContainsKey(name)) + terminals[name] = new Terminal(token == GrammarToken.Symbol, name); + + return terminals[name]; + } + + + public NonTerminal LookupNonTerminal(string name) + { + if (!nonTerminals.ContainsKey(name)) + nonTerminals[name] = new NonTerminal(name); + + return nonTerminals[name]; + } + + + public void AddProduction(Production production) + { + productions.Add(production); + production.num = productions.Count; + } + + + public void CreateSpecialProduction(NonTerminal root) + { + rootProduction = new Production(LookupNonTerminal("$accept")); + AddProduction(rootProduction); + rootProduction.rhs.Add(root); + rootProduction.rhs.Add(LookupTerminal(GrammarToken.Symbol, "END_OF_FILE")); + } + + void MarkReachable() + { + Stack work = new Stack(); + rootProduction.lhs.reached = true; // by definition. + work.Push(startSymbol); + startSymbol.reached = true; + while (work.Count > 0) + { + NonTerminal nonT = work.Pop(); + foreach (Production prod in nonT.productions) + { + foreach (Symbol smbl in prod.rhs) + { + NonTerminal rhNt = smbl as NonTerminal; + if (rhNt != null && !rhNt.reached) + { + rhNt.reached = true; + work.Push(rhNt); + } + } + } + } + } + + public bool CheckGrammar() + { + bool ok = true; + NonTerminal nt; + MarkReachable(); + foreach (KeyValuePair pair in nonTerminals) + { + nt = pair.Value; + if (!nt.reached) + Console.Error.WriteLine( + "WARNING: NonTerminal symbol \"{0}\" is unreachable", pair.Key); + + if (nt.productions.Count == 0) + { + ok = false; + Console.Error.WriteLine( + "FATAL: NonTerminal symbol \"{0}\" has no productions", pair.Key); + } + } + return ok; + } + } +} + + + + + + + diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/LALRGenerator.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/LALRGenerator.cs new file mode 100644 index 0000000000..c3321ac075 --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/LALRGenerator.cs @@ -0,0 +1,192 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System; +using System.Collections.Generic; + + +namespace gpcc +{ + public class LALRGenerator: LR0Generator + { + public LALRGenerator(Grammar grammar): base(grammar) + { + } + + + public void ComputeLookAhead() + { + ComputeDRs(); + ComputeReads(); + ComputeIncludes(); + ComputeFollows(); + ComputeLA(); + } + + + private void ComputeDRs() + { + // DR(p,A) = { t | p -> A -> r -> t -> ? } + + foreach (State p in states) + foreach (Transition pA in p.nonTerminalTransitions.Values) + pA.DR = pA.next.terminalTransitions; + } + + + private Stack S; + + // DeRemer and Pennello algorithm to compute Reads + private void ComputeReads() + { + S = new Stack(); + + foreach (State ps in states) + foreach (Transition x in ps.nonTerminalTransitions.Values) + x.N = 0; + + foreach (State ps in states) + foreach (Transition x in ps.nonTerminalTransitions.Values) + if (x.N == 0) + TraverseReads(x, 1); + } + + + private void TraverseReads(Transition x, int k) + { + S.Push(x); + x.N = k; + x.Read = new Set(x.DR); + + // foreach y such that x reads y + foreach (Transition y in x.next.nonTerminalTransitions.Values) + if (y.A.IsNullable()) + { + if (y.N == 0) + TraverseReads(y, k + 1); + + if (y.N < x.N) + x.N = y.N; + + x.Read.AddRange(y.Read); + } + + if (x.N == k) + do + { + S.Peek().N = int.MaxValue; + S.Peek().Read = new Set(x.Read); + } while (S.Pop() != x); + } + + + private void ComputeIncludes() + { + // (p,A) include (q,B) iff B -> Beta A Gamma and Gamma => empty and q -> Beta -> p + + foreach (State q in states) + foreach (Transition qB in q.nonTerminalTransitions.Values) + foreach (Production prod in qB.A.productions) + { + for (int i = prod.rhs.Count - 1; i >= 0; i--) + { + Symbol A = prod.rhs[i]; + if (A is NonTerminal) + { + State p = PathTo(q, prod, i); + p.nonTerminalTransitions[(NonTerminal)A].includes.Add(qB); + } + + if (!A.IsNullable()) + break; + } + } + } + + + private State PathTo(State q, Production prod, int prefix) + { + // q -> prod.rhs[0] ... prod.rhs[prefix] -> ??? + + for (int i = 0; i < prefix; i++) + { + Symbol s = prod.rhs[i]; + if (q.Goto.ContainsKey(s)) + q = q.Goto[s]; + else + return null; + } + + return q; + } + + + // DeRemer and Pennello algorithm to compute Follows + private void ComputeFollows() + { + S = new Stack(); + + foreach (State ps in states) + foreach (Transition x in ps.nonTerminalTransitions.Values) + x.N = 0; + + foreach (State ps in states) + foreach (Transition x in ps.nonTerminalTransitions.Values) + if (x.N == 0) + TraverseFollows(x, 1); + } + + + private void TraverseFollows(Transition x, int k) + { + S.Push(x); + x.N = k; + x.Follow = new Set(x.Read); + + foreach (Transition y in x.includes) + if (x != y) + { + if (y.N == 0) + TraverseFollows(y, k + 1); + + if (y.N < x.N) + x.N = y.N; + + x.Follow.AddRange(y.Follow); + } + + if (x.N == k) + do + { + S.Peek().N = int.MaxValue; + S.Peek().Follow = new Set(x.Follow); + } while (S.Pop() != x); + } + + + private void ComputeLA() + { + // LA(q, A->w) = Union { Follow(p,A) | p -> w -> q } + + foreach (State q in states) + { + foreach (ProductionItem item in q.all_items) + { + if (item.isReduction()) + { + item.LA = new Set(); + foreach (State p in states) + if (PathTo(p, item.production, item.pos) == q) + { + NonTerminal A = item.production.lhs; + if (p.nonTerminalTransitions.ContainsKey(A)) + { + Transition pA = p.nonTerminalTransitions[A]; + item.LA.AddRange(pA.Follow); + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/LR0Generator.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/LR0Generator.cs new file mode 100644 index 0000000000..275fec235e --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/LR0Generator.cs @@ -0,0 +1,189 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System; +using System.Collections.Generic; + + +namespace gpcc +{ + public class LR0Generator + { + protected List states = new List(); + protected Grammar grammar; + private Dictionary> accessedBy = new Dictionary>(); + + + public LR0Generator(Grammar grammar) + { + this.grammar = grammar; + } + + + public List BuildStates() + { + // create state for root production and expand recursively + ExpandState(grammar.rootProduction.lhs, new State(grammar.rootProduction)); + + return states; + } + + + private void ExpandState(Symbol sym, State newState) + { + newState.accessedBy = sym; + states.Add(newState); + + if (!accessedBy.ContainsKey(sym)) + accessedBy[sym] = new List(); + accessedBy[sym].Add(newState); + + newState.AddClosure(); + ComputeGoto(newState); + } + + + private void ComputeGoto(State state) + { + foreach (ProductionItem item in state.all_items) + if (!item.expanded && !item.isReduction()) + { + item.expanded = true; + Symbol s1 = item.production.rhs[item.pos]; + + // Create itemset for new state ... + List itemSet = new List(); + itemSet.Add(new ProductionItem(item.production, item.pos+1)); + + foreach (ProductionItem item2 in state.all_items) + if (!item2.expanded && !item2.isReduction()) + { + Symbol s2 = item2.production.rhs[item2.pos]; + + if (s1 == s2) + { + item2.expanded = true; + itemSet.Add(new ProductionItem(item2.production, item2.pos+1)); + } + } + + State existingState = FindExistingState(s1, itemSet); + + if (existingState == null) + { + State newState = new State(itemSet); + state.AddGoto(s1, newState); + ExpandState(s1, newState); + } + else + state.AddGoto(s1, existingState); + } + } + + + private State FindExistingState(Symbol sym, List itemSet) + { + if (accessedBy.ContainsKey(sym)) + foreach (State state in accessedBy[sym]) + if (ProductionItem.SameProductions(state.kernal_items, itemSet)) + return state; + + return null; + } + + + + + public void BuildParseTable() + { + foreach (State state in states) + { + // Add shift actions ... + foreach (Terminal t in state.terminalTransitions) + state.parseTable[t] = new Shift(state.Goto[t]); + + // Add reduce actions ... + foreach (ProductionItem item in state.all_items) + if (item.isReduction()) + { + // Accept on everything + if (item.production == grammar.rootProduction) + foreach (Terminal t in grammar.terminals.Values) + state.parseTable[t] = new Reduce(item); + + foreach (Terminal t in item.LA) + { + // possible conflict with existing action + if (state.parseTable.ContainsKey(t)) + { + ParserAction other = state.parseTable[t]; + + if (other is Reduce) + { + Console.Error.WriteLine("Reduce/Reduce conflict, state {0}: {1} vs {2} on {3}", + state.num, item.production.num, ((Reduce)other).item.production.num, t); + + // choose in favour of production listed first in the grammar + if (((Reduce)other).item.production.num > item.production.num) + state.parseTable[t] = new Reduce(item); + } + else + { + if (item.production.prec != null && t.prec != null) + { + if (item.production.prec.prec > t.prec.prec || + (item.production.prec.prec == t.prec.prec && + item.production.prec.type == PrecType.left)) + { + // resolve in favour of reduce (without error) + state.parseTable[t] = new Reduce(item); + } + else + { + // resolve in favour of shift (without error) + } + } + else + Console.Error.WriteLine("Shift/Reduce conflict, state {0} on {1}", state.num, t); + // choose in favour of the shift + } + } + else + state.parseTable[t] = new Reduce(item); + } + } + } + } + + + public void Report() + { + Console.WriteLine("Grammar"); + + NonTerminal lhs = null; + foreach (Production production in grammar.productions) + { + if (production.lhs != lhs) + { + lhs = production.lhs; + Console.WriteLine(); + Console.Write("{0,5} {1}: ", production.num, lhs); + } + else + Console.Write("{0,5} {1}| ", production.num, new string(' ', lhs.ToString().Length)); + + for (int i=0; i 0) + Console.WriteLine("{0}", production.rhs[production.rhs.Count-1]); + else + Console.WriteLine("/* empty */"); + } + + Console.WriteLine(); + + foreach (State state in states) + Console.WriteLine(state.ToString()); + } + } +} \ No newline at end of file diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/Main.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Main.cs new file mode 100644 index 0000000000..944a89f7eb --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Main.cs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System; +using System.Collections.Generic; + + +namespace gpcc +{ + class GPCG + { + public static bool LINES = true; + public static bool REPORT = false; + public static bool DEFINES = false; + public static bool FORGPLEX = false; + + private static void Main(string[] args) + { + try + { + string filename = ProcessOptions(args); + + if (filename == null) + return; + + Parser parser = new Parser(); + Grammar grammar = parser.Parse(filename); + + LALRGenerator generator = new LALRGenerator(grammar); + List states = generator.BuildStates(); + generator.ComputeLookAhead(); + generator.BuildParseTable(); + if (!grammar.CheckGrammar()) + throw new Exception("Non-terminating grammar"); + + if (REPORT) + generator.Report(); + else + { + CodeGenerator code = new CodeGenerator(); + code.Generate(states, grammar); + } + } + catch (Scanner.ParseException e) + { + Console.Error.WriteLine("Parse error (line {0}, column {1}): {2}", e.line, e.column, e.Message); + } + catch (System.Exception e) + { + Console.Error.WriteLine("Unexpected Error {0}", e.Message); + } + } + + + private static string ProcessOptions(string[] args) + { + string filename = null; + + foreach (string arg in args) + { + if (arg[0] == '-' || arg[0] == '/') + switch (arg.Substring(1)) + { + case "?": + case "h": + case "help": + DisplayHelp(); + return null; + case "v": + case "version": + DisplayVersion(); + return null; + case "l": + case "no-lines": + LINES = false; + break; + case "r": + case "report": + REPORT = true; + break; + case "d": + case "defines": + DEFINES = true; + break; + case "gplex": + FORGPLEX = true; + break; + } + else + filename = arg; + } + + if (filename == null) + DisplayHelp(); + + return filename; + } + + + private static void DisplayHelp() + { + Console.WriteLine("Usage gppg [options] filename"); + Console.WriteLine(); + Console.WriteLine("/help: Display this help message"); + Console.WriteLine("/version: Display version information"); + Console.WriteLine("/report: Display LALR(1) parsing states"); + Console.WriteLine("/no-lines: Suppress the generation of #line directives"); + Console.WriteLine("/defines: Emit \"tokens\" file with token name list"); + Console.WriteLine("/gplex: Generate scanner base class for GPLEX"); + Console.WriteLine(); + } + + + private static void DisplayVersion() + { + Console.WriteLine("Gardens Point Parser Generator (gppg) 1.0.1.69 23/January/2007"); + Console.WriteLine("Written by Wayne Kelly"); + Console.WriteLine("w.kelly@qut.ed.au"); + Console.WriteLine("Queensland University of Technology"); + Console.WriteLine(); + } + } +} diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/Parser.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Parser.cs new file mode 100644 index 0000000000..1ea0791701 --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Parser.cs @@ -0,0 +1,372 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System; +using System.Collections.Generic; + + +namespace gpcc +{ + public class Parser + { + private Grammar grammar; + private GrammarToken token; + private Scanner scanner; + private string baseName; + + + public Grammar Parse(string filename) + { + scanner = new Scanner(filename); + grammar = new Grammar(); + baseName = System.IO.Path.GetFileNameWithoutExtension(filename); + if (GPCG.DEFINES) grammar.TokFName = baseName + ".tokens"; + Advance(); + ParseHeader(); + ParseDeclarations(); + ParseProductions(); + ParseEpilog(); + return grammar; + } + + private void ParseHeader() + { + grammar.header = scanner.yylval; + Advance(); + } + + private void ParseDeclarations() + { + int prec = 0; + + while (token != GrammarToken.EndOfSection && token != GrammarToken.Eof) + { + switch (token) + { + case GrammarToken.Prolog: + { + grammar.prologCode += scanner.yylval; + Advance(); + break; + } + + case GrammarToken.Start: + { + Advance(); + if (token == GrammarToken.Symbol) + { + grammar.startSymbol = grammar.LookupNonTerminal(scanner.yylval); + Advance(); + } + break; + } + + case GrammarToken.Left: + { + Advance(); + prec += 10; + while (token == GrammarToken.Symbol || token == GrammarToken.Literal) + { + Terminal t = grammar.LookupTerminal(token, scanner.yylval); + t.prec = new Precedence(PrecType.left, prec); + Advance(); + } + break; + } + + case GrammarToken.Right: + { + Advance(); + prec += 10; + while (token == GrammarToken.Symbol || token == GrammarToken.Literal) + { + Terminal t = grammar.LookupTerminal(token, scanner.yylval); + t.prec = new Precedence(PrecType.right, prec); + Advance(); + } + break; + } + + case GrammarToken.NonAssoc: + { + Advance(); + prec += 10; + while (token == GrammarToken.Symbol || token == GrammarToken.Literal) + { + Terminal t = grammar.LookupTerminal(token, scanner.yylval); + t.prec = new Precedence(PrecType.nonassoc, prec); + Advance(); + } + break; + } + + case GrammarToken.Token: + { + Advance(); + string kind = null; + if (token == GrammarToken.Kind) + { + kind = scanner.yylval; + Advance(); + } + while (token == GrammarToken.Symbol) + { + Terminal terminal = grammar.LookupTerminal(token, scanner.yylval); + terminal.kind = kind; + Advance(); + } + break; + } + case GrammarToken.Type: + { + Advance(); + string kind = null; + if (token == GrammarToken.Kind) + { + kind = scanner.yylval; + Advance(); + } + while (token == GrammarToken.Symbol) + { + NonTerminal nonterminal = grammar.LookupNonTerminal(scanner.yylval); + nonterminal.kind = kind; + Advance(); + } + break; + } + case GrammarToken.Union: + { + grammar.unionType = scanner.yylval; + Advance(); + break; + } + case GrammarToken.Namespace: + { + Advance(); + grammar.Namespace = scanner.yylval; + Advance(); + while (scanner.yylval == ".") + { + Advance(); + grammar.Namespace += "." + scanner.yylval; + Advance(); + } + break; + } + + case GrammarToken.Output: + { + grammar.OutFName = scanner.yylval; + Advance(); + break; + } + case GrammarToken.Defines: + { + grammar.TokFName = baseName + ".tokens"; + Advance(); + break; + } + case GrammarToken.Partial: + { + Advance(); + grammar.IsPartial = true; + break; + } + case GrammarToken.ParserName: + { + Advance(); + grammar.ParserName = scanner.yylval; + Advance(); + break; + } + case GrammarToken.TokenName: + { + Advance(); + grammar.TokenName = scanner.yylval; + Advance(); + break; + } + case GrammarToken.LocationTypeName: + { + Advance(); + grammar.LocationTypeName = scanner.yylval; + Advance(); + break; + } + case GrammarToken.ValueTypeName: + { + Advance(); + grammar.ValueTypeName = scanner.yylval; + Advance(); + break; + } + case GrammarToken.Visibility: + { + Advance(); + grammar.Visibility = scanner.yylval; + Advance(); + break; + } + case GrammarToken.Locations: + { + Advance(); // does nothing in this version: location tracking is always on. + break; + } + default: + { + scanner.ReportError("Unexpected token {0} in declaration section", token); + Advance(); + break; + } + } + } + Advance(); + } + + + private void ParseProductions() + { + while (token != GrammarToken.EndOfSection && token != GrammarToken.Eof) + { + while (token == GrammarToken.Symbol) + ParseProduction(); + if (token != GrammarToken.Symbol && token != GrammarToken.EndOfSection && token != GrammarToken.Eof) + { + scanner.ReportError("Unexpected symbol, skipping"); + //do { Advance(); } + //while (token != GrammarToken.Symbol && token != GrammarToken.EndOfSection && token != GrammarToken.Eof); + } + } + Advance(); + } + + + private void ParseProduction() + { + NonTerminal lhs = null; + + if (token == GrammarToken.Symbol) + { + lhs = grammar.LookupNonTerminal(scanner.yylval); + + if (grammar.startSymbol == null) + grammar.startSymbol = lhs; + + if (grammar.productions.Count == 0) + grammar.CreateSpecialProduction(grammar.startSymbol); + } + else + scanner.ReportError("lhs symbol expected"); + + Advance(); + + if (token != GrammarToken.Colon) + scanner.ReportError("Colon expected"); + else + Advance(); + + ParseRhs(lhs); + while (token == GrammarToken.Divider) + { + Advance(); + ParseRhs(lhs); + } + + if (token != GrammarToken.SemiColon) + scanner.ReportError("Semicolon expected"); + else + Advance(); + } + + + private void ParseRhs(NonTerminal lhs) + { + Production production = new Production(lhs); + int pos = 0; + + while (token == GrammarToken.Symbol || token == GrammarToken.Literal || + token == GrammarToken.Action || token == GrammarToken.Prec) + { + switch (token) + { + case GrammarToken.Literal: + { + production.rhs.Add(grammar.LookupTerminal(token, scanner.yylval)); + Advance(); + pos++; + break; + } + case GrammarToken.Symbol: + { + if (grammar.terminals.ContainsKey(scanner.yylval)) + production.rhs.Add(grammar.terminals[scanner.yylval]); + else + production.rhs.Add(grammar.LookupNonTerminal(scanner.yylval)); + Advance(); + pos++; + break; + } + case GrammarToken.Prec: + { + Advance(); + if (token == GrammarToken.Symbol) + { + production.prec = grammar.LookupTerminal(token, scanner.yylval).prec; + Advance(); + } + else + scanner.ReportError("Expected symbol after %prec"); + break; + } + case GrammarToken.Action: + { + SemanticAction semanticAction = new SemanticAction(production, pos, scanner.yylval); + Advance(); + + if (token == GrammarToken.Divider || token == GrammarToken.SemiColon || token == GrammarToken.Prec) // reduce action + production.semanticAction = semanticAction; + else + { + NonTerminal node = grammar.LookupNonTerminal("@" + (++grammar.NumActions).ToString()); + Production nullProduction = new Production(node); + nullProduction.semanticAction = semanticAction; + grammar.AddProduction(nullProduction); + production.rhs.Add(node); + } + pos++; + break; + } + } + } + + grammar.AddProduction(production); + + Precedence.Calculate(production); + } + + + private void ParseEpilog() + { + grammar.epilogCode = scanner.yylval; + + Advance(); + + if (token != GrammarToken.Eof) + scanner.ReportError("Expected EOF"); + } + + + private void Advance() + { + token = scanner.Next(); + //Console.WriteLine("Token: ({0}:{1})", token, scanner.yylval); + } + } +} + + + + + + + + diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/ParserAction.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/ParserAction.cs new file mode 100644 index 0000000000..ee121b20c6 --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/ParserAction.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System; + + +namespace gpcc +{ + public abstract class ParserAction + { + public abstract int ToNum(); + } + + + public class Shift : ParserAction + { + public State next; + + public Shift(State next) + { + this.next = next; + } + + public override string ToString() + { + return "shift, and go to state " + next.num; + } + + public override int ToNum() + { + return next.num; + } + } + + + public class Reduce : ParserAction + { + public ProductionItem item; + + public Reduce(ProductionItem item) + { + this.item = item; + } + + public override string ToString() + { + return "reduce using rule " + item.production.num + " (" + item.production.lhs + ")"; + } + + public override int ToNum() + { + return -item.production.num; + } + } +} \ No newline at end of file diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/Precedence.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Precedence.cs new file mode 100644 index 0000000000..4a86e782e4 --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Precedence.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + + +using System; + + +namespace gpcc +{ + public enum PrecType { left, right, nonassoc }; + + public class Precedence + { + public PrecType type; + public int prec; + + public Precedence(PrecType type, int prec) + { + this.type = type; + this.prec = prec; + } + + public static void Calculate(Production p) + { + // Precedence of a production is that of its rightmost terminal + // unless explicitly labelled with %prec + + if (p.prec == null) + for (int i = p.rhs.Count - 1; i >= 0; i--) + if (p.rhs[i] is Terminal) + { + p.prec = ((Terminal)p.rhs[i]).prec; + break; + } + } + } +} \ No newline at end of file diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/Production.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Production.cs new file mode 100644 index 0000000000..27b9bc2a27 --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Production.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + + +using System; +using System.Text; +using System.Collections.Generic; + + +namespace gpcc +{ + public class Production + { + public int num; + public NonTerminal lhs; + public List rhs = new List(); + public SemanticAction semanticAction; + public Precedence prec = null; + + + public Production(NonTerminal lhs) + { + this.lhs = lhs; + lhs.productions.Add(this); + } + + + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + + builder.AppendFormat("{0} -> ", lhs); + foreach (Symbol s in rhs) + builder.AppendFormat("{0} ", s); + + return builder.ToString(); + } + } +} \ No newline at end of file diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/ProductionItem.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/ProductionItem.cs new file mode 100644 index 0000000000..71086c10f9 --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/ProductionItem.cs @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Text; + + +namespace gpcc +{ + public class ProductionItem + { + public Production production; + public int pos; + public bool expanded = false; + public Set LA = null; + + + public ProductionItem(Production production, int pos) + { + this.production = production; + this.pos = pos; + } + + + public override bool Equals(object obj) + { + ProductionItem item = (ProductionItem)obj; + return item.pos == pos && item.production == production; + } + + public override int GetHashCode() + { + return production.GetHashCode() + pos; + } + + + public static bool SameProductions(List list1, List list2) + { + if (list1.Count != list2.Count) + return false; + + foreach (ProductionItem item1 in list1) + { + bool found = false; + foreach (ProductionItem item2 in list2) + { + if (item1.Equals(item2)) + { + found = true; + break; + } + } + if (!found) + return false; + } + return true; + } + + + public bool isReduction() + { + return pos == production.rhs.Count; + } + + + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + + builder.AppendFormat("{0} {1}: ", production.num, production.lhs); + for (int i = 0; i < production.rhs.Count; i++) + { + if (i == pos) + builder.Append(". "); + builder.AppendFormat("{0} ", production.rhs[i]); + } + + if (pos == production.rhs.Count) + builder.Append("."); + + if (LA != null) + builder.AppendFormat(" {0}", LA); + + return builder.ToString(); + } + } +} \ No newline at end of file diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/Scanner.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Scanner.cs new file mode 100644 index 0000000000..3821fae0ea --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Scanner.cs @@ -0,0 +1,559 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System; +using System.IO; +using System.Text; + + +namespace gpcc +{ + public enum GrammarToken + { + Eof, Symbol, Literal, Action, Divider, Colon, SemiColon, EndOfSection, Union, Type, + Token, Left, Right, NonAssoc, Header, Prolog, Epilog, Kind, LeftCurly, RightCurly, Prec, Start, + Namespace, Visibility, ParserName, TokenName, ValueTypeName, LocationTypeName, Partial, + Using, Locations, Output, Defines + }; + + + class Scanner + { + public string yylval; + + private char next; + private int pos; + private string line; + private int section; + private string filename; + private int linenr; + private StreamReader reader; + private StringBuilder builder; + + + public Scanner(string path) + { + section = 0; + filename = path; + reader = new StreamReader(path); + builder = new StringBuilder(); + pos = 0; + linenr = 0; + line = ""; + Advance(); + } + + + public GrammarToken Next() + { + yylval = null; + + if (next == 0) + { + return GrammarToken.Eof; + } + + if (section == 3) + { + builder.Length = 0; + + if (GPCG.LINES) + { + builder.AppendFormat("#line {0} \"{1}\"", linenr, filename); + builder.AppendLine(); + } + + while (next != 0) + { + builder.Append(next); + Advance(); + } + yylval = builder.ToString(); + return GrammarToken.Epilog; + } + + if (section == 0) + { + builder.Length = 0; + while (next != 0 && !(pos == 0 && line.StartsWith("%%"))) + { + builder.Append(next); + Advance(); + } + yylval = builder.ToString(); + + Advance(); + Advance(); + section++; + return GrammarToken.Header; + } + + if (pos == 0 && line.StartsWith("%%")) + { + Advance(); + Advance(); + section++; + return GrammarToken.EndOfSection; + } + + switch (next) + { + case '/': // Comments + Advance(); + if (next == '/') // C++ style comment + { + while (next != '\n') + { + Advance(); + } + return Next(); + } + else if (next == '*') // C style comment + { + Advance(); + do + { + while (next != '*') + Advance(); + Advance(); + } + while (next != '/'); + Advance(); + + return Next(); + } + else + { + ReportError("unexpected / character, not in comment"); + return Next(); + } + + case '\'': // Character literal + Advance(); + bool backslash = (next == '\\'); + + if (backslash) + Advance(); + + yylval = new string(Escape(backslash, next), 1); + Advance(); + + if (next != '\'') + ReportError("Expected closing character quote"); + else + Advance(); + return GrammarToken.Literal; + + case ' ': // skip Whitespace + case '\t': + case '\n': + Advance(); + return Next(); + + case '%': // %command of some kind + Advance(); + if (next == '{') // %{ Prolog %} + { + Advance(); + builder.Length = 0; + + if (GPCG.LINES) + { + builder.AppendFormat("#line {0} \"{1}\"", linenr, filename); + builder.AppendLine(); + } + + do + { + while (next != '%') + { + builder.Append(next); + Advance(); + } + Advance(); + } while (next != '}'); + Advance(); + + yylval = builder.ToString(); + return GrammarToken.Prolog; + } + else if (Char.IsLetter(next)) + { + builder.Length = 0; + while (Char.IsLetter(next)) + { + builder.Append(next); + Advance(); + } + string keyword = builder.ToString(); + switch (keyword) + { + case "defines": + return GrammarToken.Defines; + case "left": + return GrammarToken.Left; + case "locations": + return GrammarToken.Locations; + case "namespace": + return GrammarToken.Namespace; + case "nonassoc": + return GrammarToken.NonAssoc; + case "output": + yylval = ScanFileName(); + return GrammarToken.Output; + case "partial": + return GrammarToken.Partial; + case "parsertype": + return GrammarToken.ParserName; + case "prec": + return GrammarToken.Prec; + case "right": + return GrammarToken.Right; + case "start": + return GrammarToken.Start; + case "token": + return GrammarToken.Token; + case "tokentype": + return GrammarToken.TokenName; + case "type": + return GrammarToken.Type; + case "union": + yylval = ScanUnion(); + return GrammarToken.Union; + case "using": + return GrammarToken.Using; + case "visibility": + return GrammarToken.Visibility; + case "valuetype": // backward compatability with GPLEX + case "YYSTYPE": + case "SymbolValueType": + return GrammarToken.ValueTypeName; + case "YYLTYPE": + case "SymbolLocationType": + return GrammarToken.LocationTypeName; + default: + ReportError("Unexpected keyword {0}", keyword); + return Next(); + } + } + else + { + ReportError("Unexpected keyword {0}", next); + return Next(); + } + + case '<': // + { + Advance(); + builder.Length = 0; + while (next != '>' && next != 0) + { + builder.Append(next); + Advance(); + } + Advance(); + yylval = builder.ToString(); + return GrammarToken.Kind; + } + + case '|': + Advance(); + return GrammarToken.Divider; + + case ';': + Advance(); + return GrammarToken.SemiColon; + + case ':': + Advance(); + return GrammarToken.Colon; + + case '{': + if (section == 1) + { + Advance(); + return GrammarToken.LeftCurly; + } + else // if (section == 2) + { + yylval = ScanCodeBlock(); + return GrammarToken.Action; + } + + case '}': + Advance(); + return GrammarToken.RightCurly; + + default: + if (Char.IsLetter(next)) + { + builder.Length = 0; + while (Char.IsLetterOrDigit(next) || next == '_' || next == '.') + { + builder.Append(next); + Advance(); + } + yylval = builder.ToString(); + return GrammarToken.Symbol; + } + else + { + + ReportError("Unexpected character '{0}'", next); + Advance(); + return Next(); + } + } + } + + + private void Advance() + { + if (pos + 1 < line.Length) + next = line[++pos]; + else + { + if (reader.EndOfStream) + next = (char)0; + else + { + line = reader.ReadLine() + "\n"; + //Console.WriteLine("\"{0}\"", line); + //line = line + "\n"; + linenr++; + pos = 0; + next = line[pos]; + } + } + } + + private string ScanFileName() + { + if (next != '=') + ReportError("'=' expected"); + Advance(); + builder.Length = 0; + if (next == '"') + { + Advance(); + while (next != '\n' && next != '"') + { + builder.Append(next); Advance(); + } + return builder.ToString(); + } + else + { + for (; ; ) + { + if (Char.IsWhiteSpace(next)) return builder.ToString(); + builder.Append(next); Advance(); + } + } + } + + private string ScanCodeBlock() + { + int startLin = linenr; // Used to report unterminated block. + int startCol = pos; + + builder.Length = 0; + if (GPCG.LINES) + { + builder.AppendFormat("#line {0} \"{1}\"\n", linenr, filename); + builder.Append("\t\t\t"); + } + + builder.Append(next); + Advance(); + int nest = 1; + + while (true) + { + switch (next) + { + case '\0': // Block ended by end of file! + throw new ParseException(startLin, startCol, "Codeblock starting here ended by EOF"); + case '{': + nest++; + builder.Append(next); + Advance(); + break; + case '}': + builder.Append(next); + Advance(); + if (--nest == 0) + return builder.ToString(); + break; + case '/': + builder.Append(next); + Advance(); + if (next == '/') // C++ style comment + { + while (next != 0 && next != '\n') + { + builder.Append(next); + Advance(); + } + } + else if (next == '*') // C style comment + { + builder.Append(next); + Advance(); + do + { + while (next != 0 && next != '*') + { + builder.Append(next); + Advance(); + } + if (next != 0) + { + builder.Append(next); + Advance(); + } + } while (next != 0 && next != '/'); + if (next != 0) + { + builder.Append(next); + Advance(); + } + } + else + { + builder.Append(next); + Advance(); + } + break; + case '"': // string literal + builder.Append(next); + Advance(); + while (next != 0 && next != '"') + { + if (next == '\\') + { + builder.Append(next); + Advance(); + } + if (next != 0) + { + builder.Append(next); + Advance(); + } + } + + if (next != 0) + { + builder.Append(next); + Advance(); + } + break; + case '\'': // character literal + builder.Append(next); + Advance(); + while (next != 0 && next != '\'') + { + if (next == '\\') + { + builder.Append(next); + Advance(); + } + if (next != 0) + { + builder.Append(next); + Advance(); + } + } + + if (next != 0) + { + builder.Append(next); + Advance(); + } + break; + case '@': + builder.Append(next); + Advance(); + if (next == '"') // verbatim string literal + { + builder.Append(next); + Advance(); + while (next != 0 && next != '"') + { + builder.Append(next); + Advance(); + } + + if (next != 0) + { + builder.Append(next); + Advance(); + } + break; + } + break; + default: + builder.Append(next); + Advance(); + break; + } + } + } + + + private string ScanUnion() + { + while (next != '{') + Advance(); + + return ScanCodeBlock(); + } + + + private char Escape(bool backslash, char ch) + { + if (!backslash) + return ch; + else + switch (ch) + { + case 'a': return '\a'; + case 'b': return '\b'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'v': return '\v'; + case '0': return '\0'; + default: + ReportError("Unexpected escape character '\\{0}'", ch); + return ch; + } + } + + + public class ParseException: Exception + { + public int line, column; + + public ParseException(int line, int column, string message) + : base(message) + { + this.line = line; + this.column = column; + } + } + + public void ReportError(string format, params object[] args) + { + throw new ParseException(linenr, pos, string.Format(format, args)); + } + } +} + + + + + + + + diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/SemanticAction.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/SemanticAction.cs new file mode 100644 index 0000000000..a1b91d58cf --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/SemanticAction.cs @@ -0,0 +1,202 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + + +using System; +using System.Text; + + +namespace gpcc +{ + public class SemanticAction + { + private Production production; + private int pos; + private string commands; + + + public SemanticAction(Production production, int pos, string commands) + { + this.production = production; + this.pos = pos; + this.commands = commands; + } + + void ErrReport(string tag, string msg) + { + Console.Error.Write(tag); Console.Error.Write(": "); Console.Error.WriteLine(msg); + } + + + public void GenerateCode(CodeGenerator codeGenerator) + { + int i = 0; + string lineTag = ""; + + if (commands.StartsWith("#line")) { + lineTag = commands.Substring(0, commands.IndexOf('"')).Trim(); + } + + while (i < commands.Length) + { + switch (commands[i]) + { + case '/': + Output(i++); + if (commands[i] == '/') // C++ style comments + { + while (i < commands.Length && commands[i] != '\n') + Output(i++); + if (i < commands.Length) + Output(i++); + } + else if (commands[i] == '*') // C style comments + { + Output(i++); + do + { + while (i < commands.Length && commands[i] != '*') + Output(i++); + if (i < commands.Length) + Output(i++); + } while (i < commands.Length && commands[i] != '/'); + if (i < commands.Length) + Output(i++); + } + break; + + case '"': // start of string literal + Output(i++); + while (i < commands.Length && commands[i] != '"') + { + if (commands[i] == '\\') + Output(i++); + if (i < commands.Length) + Output(i++); + } + if (i < commands.Length) + Output(i++); + break; + + case '@': + // Possible start of verbatim string literal + // but also possible location marker access + if (i + 1 < commands.Length) + { + char la = commands[i + 1]; // lookahead character + if (la == '$') + { + i += 2; // read past '@', '$' + Console.Write("yyloc"); + } + else if (Char.IsDigit(la)) + { + int num = (int)la - (int)'0'; + i += 2; // read past '@', digit + if (i < commands.Length && char.IsDigit(commands[i])) + ErrReport(lineTag, "Only indexes up \"$9\" allowed"); + else if (num > this.production.rhs.Count) + ErrReport(lineTag, String.Format("Index @{0} is out of bounds", num)); + Console.Write("GetLocation({0})", pos - num + 1); + } + else + { + Output(i++); + if (la == '"') + { + Output(i++); + while (i < commands.Length && commands[i] != '"') + Output(i++); + if (i < commands.Length) + Output(i++); + break; + } + } + } + else + ErrReport(lineTag, "Invalid use of '@'"); + break; + + case '\'': // start of char literal + Output(i++); + while (i < commands.Length && commands[i] != '\'') + { + if (commands[i] == '\\') + Output(i++); + if (i < commands.Length) + Output(i++); + } + if (i < commands.Length) + Output(i++); + break; + + case '$': // $$ or $n placeholder + i++; + if (i < commands.Length) + { + string kind = null; + if (commands[i] == '<') // $n + { + i++; + StringBuilder builder = new StringBuilder(); + while (i < commands.Length && commands[i] != '>') + { + builder.Append(commands[i]); + i++; + } + if (i < commands.Length) + { + i++; + kind = builder.ToString(); + } + } + + if (commands[i] == '$') + { + i++; + if (kind == null) + kind = production.lhs.kind; + + Console.Write("yyval"); + + if (kind != null) + Console.Write(".{0}", kind); + } + else if (char.IsDigit(commands[i])) + { + int num = commands[i] - '0'; + i++; + if (i < commands.Length && char.IsDigit(commands[i])) + ErrReport(lineTag, "Only indexes up \"$9\" allowed"); + else if (num > this.production.rhs.Count) + ErrReport(lineTag, String.Format("Index ${0} is out of bounds", num)); + if (kind == null) + kind = production.rhs[num - 1].kind; + + Console.Write("GetValue({0})", pos - num + 1); + + if (kind != null) + Console.Write(".{0}", kind); + } + } + else + ErrReport(lineTag, "Unexpected '$'"); + break; + + default: + Output(i++); + break; + } + } + Console.WriteLine(); + } + + + private void Output(int i) + { + if (commands[i] == '\n') + Console.WriteLine(); + else + Console.Write(commands[i]); + } + } +} \ No newline at end of file diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/Set.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Set.cs new file mode 100644 index 0000000000..52c638023a --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Set.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Text; + + +namespace gpcc +{ + public class Set: IEnumerable + { + private Dictionary elements = new Dictionary(); + + public Set() + { + } + + + public Set(Set items) + { + AddRange(items); + } + + + public void Add(T item) + { + elements[item] = true; + } + + + public void AddRange(Set items) + { + foreach (T item in items) + Add(item); + } + + + public IEnumerator GetEnumerator() + { + return elements.Keys.GetEnumerator(); + } + + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + throw new Exception("The method or operation is not implemented."); + } + + + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + + builder.Append("["); + + foreach (T element in elements.Keys) + builder.AppendFormat("{0}, ", element); + + builder.Append("]"); + + return builder.ToString(); + } + } +} \ No newline at end of file diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/State.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/State.cs new file mode 100644 index 0000000000..0b48d0f617 --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/State.cs @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Text; + + +namespace gpcc +{ + public class State + { + private static int TotalStates = 0; + + public int num; + public Symbol accessedBy = null; + public List kernal_items = new List(); + public List all_items = new List(); + public Dictionary Goto = new Dictionary(); + public Set terminalTransitions = new Set(); + public Dictionary nonTerminalTransitions = new Dictionary(); + public Dictionary parseTable = new Dictionary(); + + public State(Production production) + { + num = TotalStates++; + AddKernal(production, 0); + } + + + public State(List itemSet) + { + num = TotalStates++; + kernal_items.AddRange(itemSet); + all_items.AddRange(itemSet); + } + + + public void AddClosure() + { + foreach (ProductionItem item in kernal_items) + AddClosure(item); + } + + + private void AddClosure(ProductionItem item) + { + if (item.pos < item.production.rhs.Count) + { + Symbol rhs = item.production.rhs[item.pos]; + if (rhs is NonTerminal) + foreach (Production p in ((NonTerminal)rhs).productions) + AddNonKernal(p); + } + } + + + private void AddKernal(Production production, int pos) + { + ProductionItem item = new ProductionItem(production, pos); + kernal_items.Add(item); + all_items.Add(item); + } + + + private void AddNonKernal(Production production) + { + ProductionItem item = new ProductionItem(production, 0); + + if (!all_items.Contains(item)) + { + all_items.Add(item); + AddClosure(item); + } + } + + + public void AddGoto(Symbol s, State next) + { + this.Goto[s] = next; + + if (s is Terminal) + terminalTransitions.Add((Terminal)s); + else + nonTerminalTransitions.Add((NonTerminal)s, new Transition(this, (NonTerminal)s, next)); + } + + public int GetDefaultAction() { + IEnumerator enumerator = parseTable.Values.GetEnumerator(); + enumerator.MoveNext(); + int defaultAction = enumerator.Current.ToNum(); + + if (defaultAction > 0) + return 0; // can't have default shift action + + foreach (KeyValuePair transition in parseTable) + if (transition.Value.ToNum() != defaultAction) + return 0; + + return defaultAction; + } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + + builder.AppendFormat("State {0}", num); + builder.AppendLine(); + builder.AppendLine(); + + foreach (ProductionItem item in kernal_items) + { + builder.AppendFormat(" {0}", item); + builder.AppendLine(); + } + + builder.AppendLine(); + + foreach (KeyValuePair a in parseTable) + { + builder.AppendFormat(" {0,-14} {1}", a.Key, a.Value); + builder.AppendLine(); + } + + builder.AppendLine(); + + foreach (KeyValuePair n in nonTerminalTransitions) + { + builder.AppendFormat(" {0,-14} go to state {1}", n.Key, Goto[n.Key].num); + builder.AppendLine(); + } + + builder.AppendLine(); + + return builder.ToString(); + } + } +} \ No newline at end of file diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/Symbol.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Symbol.cs new file mode 100644 index 0000000000..fdbf03db16 --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Symbol.cs @@ -0,0 +1,155 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + + +using System; +using System.Collections.Generic; +using System.Text; + + +namespace gpcc +{ + public abstract class Symbol + { + private string name; + public string kind; + + public abstract int num + { + get; + } + + + public Symbol(string name) + { + this.name = name; + } + + + public override string ToString() + { + return name; + } + + + public abstract bool IsNullable(); + } + + + public class Terminal : Symbol + { + static int count = 0; + static int max = 0; + + public Precedence prec = null; + private int n; + public bool symbolic; + + public override int num + { + get + { + if (symbolic) + return max + n; + else + return n; + } + } + + public Terminal(bool symbolic, string name) + : base(symbolic ? name : "'" + name.Replace("\n", @"\n") + "'") + { + this.symbolic = symbolic; + + if (symbolic) + this.n = ++count; + else + { + this.n = (int)name[0]; + if (n > max) max = n; + } + } + + + public override bool IsNullable() + { + return false; + } + + public override string ToString() { + string name = base.ToString(); + + // any lower case => return as is: + for (int i = 0; i < name.Length; i++) { + if (Char.IsLower(name[i])) return name; + } + + // capitalize otherwise, remove underscores: + StringBuilder result = new StringBuilder(); + bool doLower = false; + for (int i = 0, j = 0; i < name.Length; i++, j++) { + if (name[i] == '_') { + doLower = false; + } else { + if (doLower) { + result.Append(Char.ToLower(name[i])); + } else { + result.Append(name[i]); + doLower = true; + } + } + } + return result.ToString(); + } + } + + + + public class NonTerminal : Symbol + { + public bool reached = false; + static int count = 0; + private int n; + public List productions = new List(); + + + public NonTerminal(string name) + : base(name) + { + n = ++count; + } + + public override int num + { + get + { + return -n; + } + } + + private object isNullable; + public override bool IsNullable() + { + if (isNullable == null) + { + isNullable = false; + foreach (Production p in productions) + { + bool nullable = true; + foreach (Symbol rhs in p.rhs) + if (!rhs.IsNullable()) + { + nullable = false; + break; + } + if (nullable) + { + isNullable = true; + break; + } + } + } + + return (bool)isNullable; + } + } +} \ No newline at end of file diff --git a/merlin/main/Utilities/GPPG/Source/ParserGenerator/Transition.cs b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Transition.cs new file mode 100644 index 0000000000..d15fbef309 --- /dev/null +++ b/merlin/main/Utilities/GPPG/Source/ParserGenerator/Transition.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +using System; +using System.Collections.Generic; + + +namespace gpcc +{ + public class Transition + { + public int N; + public State p; + public NonTerminal A; + public State next; + + public Set DR; + public List includes = new List(); + public Set Read; + public Set Follow; + + + public Transition(State p, NonTerminal A, State next) + { + this.p = p; + this.A = A; + this.next = next; + } + } +} \ No newline at end of file diff --git a/merlin/main/Utilities/GPPG/gppg.exe b/merlin/main/Utilities/GPPG/gppg.exe new file mode 100644 index 0000000000..e8debda5ca Binary files /dev/null and b/merlin/main/Utilities/GPPG/gppg.exe differ diff --git a/merlin/main/languages/ruby/.Rakefile.swp b/merlin/main/languages/ruby/.Rakefile.swp new file mode 100644 index 0000000000..7b7cdc883a Binary files /dev/null and b/merlin/main/languages/ruby/.Rakefile.swp differ diff --git a/merlin/main/languages/ruby/ClassInitGenerator/ClassInitGenerator.csproj b/merlin/main/languages/ruby/ClassInitGenerator/ClassInitGenerator.csproj new file mode 100644 index 0000000000..d74b43b0b9 --- /dev/null +++ b/merlin/main/languages/ruby/ClassInitGenerator/ClassInitGenerator.csproj @@ -0,0 +1,75 @@ + + + Debug + AnyCPU + 9.0.30718 + 2.0 + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B} + Exe + Properties + ClassInitGenerator + ClassInitGenerator + SAK + SAK + SAK + SAK + 2.0 + + + true + full + false + ..\..\..\Bin\Debug\ + TRACE;DEBUG;SIGNED + prompt + 4 + true + ..\..\..\MSSharedLibKey.snk + true + + + pdbonly + true + bin\Release\ + TRACE;SIGNED + prompt + 4 + true + ..\..\..\MSSharedLibKey.snk + true + + + + + + + + + + + + + + + + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4} + Microsoft.Scripting.Core + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Scripting + + + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD} + Ruby %28Languages\Ruby\Ruby%29 + + + + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/ClassInitGenerator/ClassInitGenerator.csproj.vspscc b/merlin/main/languages/ruby/ClassInitGenerator/ClassInitGenerator.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/merlin/main/languages/ruby/ClassInitGenerator/ClassInitGenerator.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/merlin/main/languages/ruby/ClassInitGenerator/Generator.cs b/merlin/main/languages/ruby/ClassInitGenerator/Generator.cs new file mode 100644 index 0000000000..e265e205e6 --- /dev/null +++ b/merlin/main/languages/ruby/ClassInitGenerator/Generator.cs @@ -0,0 +1,79 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.CodeDom.Compiler; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Dynamic; +using System.Dynamic.Utils; +using System.Text; +using IronRuby; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +internal class Generator { + static void Main(string[]/*!*/ args) { + var list = new List(args); + if (list.IndexOf("/refcache") >= 0 || list.IndexOf("-refcache") >= 0) { + ReflectionCacheGenerator.Create(args).Generate(); + } else { + var generator = InitGenerator.Create(args); + if (generator == null) { + Environment.ExitCode = -1; + return; + } + + generator.Generate(); + } + } + + internal void WriteLicenseStatement(TextWriter/*!*/ writer) { + writer.Write( +@"/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +"); + } + + internal static KeyValuePair ToNameValue(string/*!*/ arg) { + if (arg.StartsWith("/") || arg.StartsWith("-")) { + int colon = arg.IndexOf(':'); + if (colon >= 0) { + return new KeyValuePair(arg.Substring(1, colon - 1), arg.Substring(colon + 1)); + } else { + return new KeyValuePair(arg.Substring(1), String.Empty); + } + } else { + return new KeyValuePair(String.Empty, arg); + } + } +} diff --git a/merlin/main/languages/ruby/ClassInitGenerator/Libraries/InitGenerator.cs b/merlin/main/languages/ruby/ClassInitGenerator/Libraries/InitGenerator.cs new file mode 100644 index 0000000000..7897f78f43 --- /dev/null +++ b/merlin/main/languages/ruby/ClassInitGenerator/Libraries/InitGenerator.cs @@ -0,0 +1,111 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.CodeDom.Compiler; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Dynamic; +using System.Dynamic.Utils; +using System.Text; +using IronRuby; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +internal class InitGenerator : Generator { + private Assembly/*!*/ _assembly; + private string _outFile; + // namespace -> library + private readonly Dictionary/*!*/ _libraries = new Dictionary(); + + private InitGenerator() { + } + + public static InitGenerator Create(string[]/*!*/ args) { + var gen = new InitGenerator(); + + for (int i = 0; i < args.Length; i++) { + KeyValuePair arg = ToNameValue(args[i]); + + switch (arg.Key) { + case "out": + gen._outFile = arg.Value; + break; + + case "libraries": + foreach (string libararyNamespace in arg.Value.Split(';', ',')) { + gen._libraries[libararyNamespace] = new LibraryDef(libararyNamespace); + } + break; + + case "": + try { + gen._assembly = Assembly.LoadFrom(arg.Value); + } catch (Exception e) { + Console.Error.WriteLine(e.Message); + return null; + } + break; + + default: + Console.Error.WriteLine("Unknown option: {0}", arg.Key); + return null; + } + } + + if (gen._outFile == null) { + Console.Error.WriteLine("Output file not specified"); + return null; + } + + return gen; + } + + public void Generate() { + Type[] allTypes = _assembly.GetTypes(); + bool anyErrors = false; + foreach (LibraryDef library in _libraries.Values) { + library.ReflectTypes(allTypes); + anyErrors |= library.AnyErrors; + } + + if (anyErrors) { + Console.Error.WriteLine("Failed."); + return; + } + + using (TextWriter writer = new StreamWriter(File.Open(_outFile, FileMode.Create, FileAccess.Write))) { + IndentedTextWriter output = new IndentedTextWriter(writer, " "); + output.NewLine = "\r\n"; + + WriteLicenseStatement(writer); + + foreach (LibraryDef library in _libraries.Values) { + output.WriteLine("[assembly: {2}(typeof({0}.{1}))]", library._namespace, library._initializerName, LibraryDef.TypeRubyLibraryAttribute); + } + + output.WriteLine(); + + foreach (LibraryDef library in _libraries.Values) { + Console.WriteLine("Library {0}", library._namespace); + library.GenerateCode(output); + } + } + } +} diff --git a/merlin/main/languages/ruby/ClassInitGenerator/Libraries/LibraryDef.cs b/merlin/main/languages/ruby/ClassInitGenerator/Libraries/LibraryDef.cs new file mode 100644 index 0000000000..d76baed103 --- /dev/null +++ b/merlin/main/languages/ruby/ClassInitGenerator/Libraries/LibraryDef.cs @@ -0,0 +1,1214 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.CodeDom.Compiler; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using System.Text; +using IronRuby; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting.Runtime; +using System.Runtime.InteropServices; + +internal class LibraryDef { + + private bool Builtins { get { return _namespace == typeof(RubyClass).Namespace; } } + + public static readonly string/*!*/ TypeAction0 = TypeName(typeof(Action)); + public static readonly string/*!*/ TypeAction1 = GenericTypeName(typeof(Action<>)); + public static readonly string/*!*/ TypeActionN = GenericTypeName(typeof(Action<,>)); + public static readonly string/*!*/ TypeFunction = GenericTypeName(typeof(Func<>)); + public static readonly string/*!*/ TypeDelegate = TypeName(typeof(Delegate)); + public static readonly string/*!*/ TypeRubyModule = TypeName(typeof(RubyModule)); + public static readonly string/*!*/ TypeRubyClass = TypeName(typeof(RubyClass)); + public static readonly string/*!*/ TypeActionOfRubyModule = TypeName(typeof(Action)); + public static readonly string/*!*/ TypeRubyExecutionContext = TypeName(typeof(RubyContext)); + public static readonly string/*!*/ TypeLibraryInitializer = TypeName(typeof(LibraryInitializer)); + public static readonly string/*!*/ TypeRubyLibraryAttribute = TypeName(typeof(RubyLibraryAttribute)); + + // TODO: store defs into LibraryDef + private IDictionary _moduleDefs = new SortedDictionary(new TypeComparer()); // sorted to improve diff quality + private IDictionary _traits = new Dictionary(); + private Dictionary _moduleRefs = new Dictionary(); + private Dictionary _classRefs = new Dictionary(); + + public readonly string/*!*/ _namespace; + public readonly string/*!*/ _initializerName; + public bool AnyErrors; + private IndentedTextWriter _output; + + #region Definitions + + public LibraryDef(string/*!*/ ns) { + _namespace = ns; + _initializerName = LibraryInitializer.GetTypeName(ns); + } + + private class TypeRef { + public ModuleDef Definition; + public Type Type; + public string RefName; + + public TypeRef(Type type) { + Type = type; + RefName = null; + Definition = null; + } + + public TypeRef(ModuleDef def, Type type, string refName) { + Type = type; + RefName = refName; + Definition = def; + } + } + + private struct MixinRef { + public readonly TypeRef/*!*/ Module; + public readonly bool Copy; + + public MixinRef(TypeRef/*!*/ module, bool copy) { + Module = module; + Copy = copy; + } + } + + private enum ModuleKind { + Module, + Singleton, + Class + } + + private enum HiddenMethod { + ClrInvisible, + Undefined + } + + private class ModuleDef { + public string/*!*/ QualifiedName; + public string/*!*/ SimpleName; + public string/*!*/ Id; + public bool IsExtension; + public bool IsException; + public List/*!*/ Aliases = new List(); + + public Type/*!*/ Trait; + public IDictionary/*!*/ InstanceMethods = new SortedDictionary(); + public IDictionary/*!*/ ClassMethods = new SortedDictionary(); + public IDictionary/*!*/ Constants = new SortedDictionary(); + + public IDictionary/*!*/ HiddenInstanceMethods = new SortedDictionary(); + public IDictionary/*!*/ HiddenClassMethods = new SortedDictionary(); + + public List/*!*/ Factories = new List(); + + public List/*!*/ Mixins = new List(); + + // Type real type (same as Trait for non-extension classes): + public Type/*!*/ Extends; + + public ModuleKind Kind; + public bool HasCopyInclusions; + + public TypeRef Super; // non-null for all classes except for object + + // Non-null for modules nested in other Ruby modules. + // Doesn't influence dependency, constants for nested types are defined after all classes have been defined. + public ModuleDef DeclaringModule; + public string DeclaringTypeRef; + + // Whether the module is defined on Object, so we need to set a constant on Object. + public bool IsGlobal; + + // Variable name where definition is stored. + public string Reference; + public string Definition; + + + public string BuildConfig; + + private int _dependencyOrder; + + /// + /// Indicates the order that things should be generated so mixins/subclasses are always definied before + /// they are referenced. Each module gets the value of the max of all the types it refers to + /// plus one. + /// + public int DependencyOrder { + get { + if (_dependencyOrder == 0) { + if (Super != null && Super.Definition != null) { + _dependencyOrder = System.Math.Max(Super.Definition.DependencyOrder, _dependencyOrder); + } + + foreach (MixinRef mixin in Mixins) { + if (mixin.Module.Definition != null) { + _dependencyOrder = System.Math.Max(mixin.Module.Definition.DependencyOrder, _dependencyOrder); + } + } + _dependencyOrder += 1; + } + return _dependencyOrder; + } + } + + public bool IsPrimitive { + get { + return !IsExtension && ( + QualifiedName == RubyClass.MainSingletonName + || QualifiedName == RubyClass.ClassSingletonName + || QualifiedName == RubyClass.ClassSingletonSingletonName + || Extends == typeof(Kernel) + || Extends == typeof(Object) + || Extends == typeof(RubyClass) + || Extends == typeof(RubyModule) + ); + } + } + + public bool HasInstanceInitializer { + get { + return InstanceMethods.Count > 0 || HiddenInstanceMethods.Count > 0 || HasCopyInclusions || IsPrimitive || + Constants.Count > 0; + } + } + + public bool HasClassInitializer { + get { return ClassMethods.Count > 0 || HiddenClassMethods.Count > 0 || HasCopyInclusions || IsPrimitive; } + } + + public const string/*!*/ ObjectClassRef = "Context.ObjectClass"; + public const string/*!*/ KernelModuleRef = "Context.KernelModule"; + public const string/*!*/ ModuleClassRef = "Context.ModuleClass"; + public const string/*!*/ ClassClassRef = "Context.ClassClass"; + + internal string GetReference(ref int defVariableId) { + if (Reference == null) { + if (Extends == typeof(Object)) { + Reference = ObjectClassRef; + } else if (Extends == typeof(Kernel)) { + Reference = KernelModuleRef; + } else if (Extends == typeof(RubyModule)) { + Reference = ModuleClassRef; + } else if (Extends == typeof(RubyClass)) { + Reference = ClassClassRef; + } else { + Definition = Reference = "def" + defVariableId++; + } + } + + return Reference; + } + + public override string/*!*/ ToString() { + return QualifiedName; + } + } + + private class MethodDef { + public string/*!*/ Name; + public List/*!*/ Overloads = new List(); + public string BuildConfig; + public RubyMethodAttributes/*!*/ Attributes; + + public bool IsRuleGenerator { + get { + return Overloads.Count == 1 && Overloads[0].ReturnType == typeof(RuleGenerator) && Overloads[0].GetParameters().Length == 0; + } + } + + public override string/*!*/ ToString() { + return Name; + } + } + + private class ConstantDef { + public readonly string/*!*/ Name; + public readonly MemberInfo/*!*/ Member; + public readonly string/*!*/ BuildConfig; + + public ConstantDef(string/*!*/ name, MemberInfo/*!*/ member, string/*!*/ buildConfig) { + Name = name; + Member = member; + BuildConfig = buildConfig; + } + } + + #endregion + + #region Reflection + + public void ReflectTypes(Type[]/*!*/ allTypes) { + + foreach (Type trait in allTypes) { + if (trait.Namespace == null || !trait.Namespace.StartsWith(_namespace)) { + continue; + } + + object[] attrs; + + try { + attrs = trait.GetCustomAttributes(typeof(RubyModuleAttribute), false); + } catch (Exception e) { + LogError("{0}: Invalid attribute value on type: {1}", trait.FullName, e.Message); + continue; + } + + foreach (RubyModuleAttribute module in attrs) { + ModuleDef def = new ModuleDef(); + + RubySingletonAttribute singleton = module as RubySingletonAttribute; + RubyClassAttribute cls = module as RubyClassAttribute; + + if (cls != null) { + def.Kind = ModuleKind.Class; + def.IsException = module is RubyExceptionAttribute; + } else if (singleton != null) { + def.Kind = ModuleKind.Singleton; + if (module.Extends != null) { + LogError("{0}: Singleton cannot Extend a type", trait.FullName); + module.Extends = null; + } + } else { + def.Kind = ModuleKind.Module; + } + + if (module.Extends != null && module.Name == null) { + // extends a CLR type or an existing Ruby library class/module: + def.IsExtension = true; + def.SimpleName = module.Extends.Name.Replace(ReflectionUtils.GenericArityDelimiter, '_'); + def.QualifiedName = null; + def.DeclaringModule = null; + def.IsGlobal = false; + } else { + def.IsExtension = false; + def.SimpleName = module.Name ?? trait.Name; + def.QualifiedName = null; // to be filled in later + def.DeclaringModule = null; // to be corrected later for nested modules + def.IsGlobal = true; // to be corrected later for nested modules + } + + def.Trait = trait; + def.Extends = (module.Extends != null) ? module.Extends : trait; + def.BuildConfig = module.BuildConfig; + + def.Super = null; + if (cls != null && def.Extends != typeof(object) && !def.Extends.IsInterface && !def.IsExtension) { + if (cls != null && cls.Inherits != null) { + def.Super = new TypeRef(cls.Inherits); + } else { + def.Super = new TypeRef(def.Extends.BaseType); + } + } + + def.HasCopyInclusions = false; + foreach (IncludesAttribute includes in trait.GetCustomAttributes(typeof(IncludesAttribute), false)) { + foreach (Type type in includes.Types) { + def.Mixins.Add(new MixinRef(new TypeRef(type), includes.Copy)); + } + def.HasCopyInclusions |= includes.Copy; + } + + if (cls != null && cls.MixinInterfaces) { + foreach (Type iface in def.Extends.GetInterfaces()) { + ModuleDef mixin; + if (_moduleDefs.TryGetValue(iface, out mixin)) { + def.Mixins.Add(new MixinRef(new TypeRef(mixin.Extends), false)); + } + } + } + + _moduleDefs.Add(def.Extends, def); + _traits.Add(def.Trait, def); + + // added Ruby methods and constants: + ReflectMethods(def); + ReflectFieldConstants(def); + ReflectAliases(def); + + // hidden CLR methods: + foreach (HideMethodAttribute method in trait.GetCustomAttributes(typeof(HideMethodAttribute), false)) { + // TODO: warning, method already removed, method not found ... + if (method.IsStatic) { + def.HiddenClassMethods[method.Name] = HiddenMethod.ClrInvisible; + } else { + def.HiddenInstanceMethods[method.Name] = HiddenMethod.ClrInvisible; + } + } + + // undefined methods: + foreach (UndefineMethodAttribute method in trait.GetCustomAttributes(typeof(UndefineMethodAttribute), false)) { + // TODO: warning, method already removed, method not found ... + if (method.IsStatic) { + def.HiddenClassMethods[method.Name] = HiddenMethod.Undefined; + } else { + def.HiddenInstanceMethods[method.Name] = HiddenMethod.Undefined; + } + } + } + } + + int defVariableId = 1; + + // qualified names, ids, declaring modules: + foreach (ModuleDef def in _moduleDefs.Values) { + if (!def.IsExtension) { + def.QualifiedName = def.SimpleName; + + // finds the inner most Ruby module def containing this module def: + Type declaringType = def.Trait.DeclaringType; + while (declaringType != null) { + ModuleDef declaringDef; + if (_traits.TryGetValue(declaringType, out declaringDef)) { + def.QualifiedName = declaringDef.QualifiedName + "::" + def.SimpleName; + def.DeclaringModule = declaringDef; + def.IsGlobal = false; + + // inherits build config: + if (declaringDef.BuildConfig != null) { + if (def.BuildConfig != null) { + def.BuildConfig = declaringDef.BuildConfig + " && " + def.BuildConfig; + } else { + def.BuildConfig = declaringDef.BuildConfig; + } + } + + // we will need a reference for setting the constant: + def.DeclaringTypeRef = declaringDef.GetReference(ref defVariableId); + def.GetReference(ref defVariableId); + break; + } else { + declaringType = declaringType.DeclaringType; + } + } + } else { + def.QualifiedName = RubyUtils.GetQualifiedName(def.Extends); + } + + if (def.Kind == ModuleKind.Singleton) { + // we need to refer to the singleton object returned from singelton factory: + def.GetReference(ref defVariableId); + + def.Id = "__Singleton_" + def.QualifiedName; + } else { + def.Id = def.QualifiedName.Replace(':', '_'); + } + } + + // wire-up supers and mixins: + foreach (ModuleDef def in _moduleDefs.Values) { + if (def.Super != null) { + ModuleDef superDef; + if (_moduleDefs.TryGetValue(def.Super.Type, out superDef)) { + + // define inheritance relationship: + def.Super.Definition = superDef; + def.Super.RefName = superDef.GetReference(ref defVariableId); + + } else { + + // define a class ref-variable for the type of the super class: + def.Super.RefName = MakeClassReference(def.Super.Type); + + } + } else if (!def.IsExtension && def.Kind == ModuleKind.Class && def.Extends != typeof(object)) { + LogError("Missing super type for type '{0}'", def.QualifiedName); + } + + // wire-up mixins: + foreach (MixinRef mixin in def.Mixins) { + ModuleDef mixinDef; + if (_moduleDefs.TryGetValue(mixin.Module.Type, out mixinDef)) { + + // define mixin relationship: + mixin.Module.Definition = mixinDef; + if (!mixin.Copy) { + mixin.Module.RefName = mixinDef.GetReference(ref defVariableId); + } + + } else if (!mixin.Copy) { + // define a module ref-variable for the type of the mixin: + mixin.Module.RefName = MakeModuleReference(mixin.Module.Type); + } + } + } + + // TODO: checks + // - loops in copy-inclusion + } + + private string/*!*/ MakeModuleReference(Type/*!*/ typeRef) { + string refVariable; + + if (!_moduleRefs.TryGetValue(typeRef, out refVariable)) { + refVariable = "moduleRef" + _moduleRefs.Count; + _moduleRefs.Add(typeRef, refVariable); + } + + return refVariable; + } + + private string/*!*/ MakeClassReference(Type/*!*/ typeRef) { + string refVariable; + + if (!_classRefs.TryGetValue(typeRef, out refVariable)) { + refVariable = "classRef" + _classRefs.Count; + _classRefs.Add(typeRef, refVariable); + } + + return refVariable; + } + + private void ReflectMethods(ModuleDef/*!*/ moduleDef) { + Debug.Assert(moduleDef.Trait != null); + + BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; + foreach (MethodInfo method in moduleDef.Trait.GetMethods(flags)) { + object[] attrs = method.GetCustomAttributes(typeof(RubyMethodAttribute), false); + if (attrs.Length > 0) { + if (!RequireStatic(method)) continue; + foreach (RubyMethodAttribute attr in attrs) { + MethodDef def; + + IDictionary methods = + ((attr.MethodAttributes & RubyMethodAttributes.Instance) != 0) ? moduleDef.InstanceMethods : moduleDef.ClassMethods; + + if (!methods.TryGetValue(attr.Name, out def)) { + def = new MethodDef(); + def.Name = attr.Name; + def.Attributes = attr.MethodAttributes; + + if (Builtins) { + def.Attributes |= RubyMethodAttributes.NoEvent; + } + + def.BuildConfig = attr.BuildConfig; + + methods.Add(attr.Name, def); + } + def.Overloads.Add(method); + } + } + + if (method.IsDefined(typeof(RubyConstructorAttribute), false)) { + if (!RequireStatic(method)) continue; + moduleDef.Factories.Add(method); + } + + if (method.IsDefined(typeof(RubyConstantAttribute), false)) { + if (!RequireStatic(method)) continue; + + var parameters = method.GetParameters(); + if (parameters.Length != 1 || !parameters[0].ParameterType.IsAssignableFrom(typeof(RubyModule)) || + parameters[0].Attributes != ParameterAttributes.None) { + LogError("Method that defines a constant must take a single parameter of type RubyModule: '{0}'", + ReflectionUtils.FormatSignature(new StringBuilder(), method)); + } + + ReflectConstants(moduleDef, method); + } + } + + VerifyMethods(moduleDef); + } + + private void VerifyMethods(ModuleDef/*!*/ moduleDef) { + foreach (var methodDef in moduleDef.InstanceMethods.Values) { + VerifyMethod(moduleDef, methodDef.Overloads, methodDef, true); + } + + foreach (var methodDef in moduleDef.ClassMethods.Values) { + VerifyMethod(moduleDef, methodDef.Overloads, methodDef, false); + } + + VerifyFactory(moduleDef, moduleDef.Factories); + } + + private void VerifyFactory(ModuleDef/*!*/ moduleDef, IList/*!*/ overloads) { + VerifyMethod(moduleDef, overloads, null, false); + } + + private void VerifyMethod(ModuleDef/*!*/ moduleDef, IList/*!*/ overloads, MethodDef methodDef, bool isInstance) { + foreach (var overload in overloads) { + var parameterInfos = overload.GetParameters(); + + if (overload.ReturnType == typeof(RuleGenerator)) { + if (parameterInfos.Length != 0) { + LogMethodError("RuleGenerator must be parameterless", methodDef, overload); + } + + if (overloads.Count != 1) { + LogMethodError("RuleGenerator must have no overloads", methodDef, overload); + } + } else { + + bool hasContext = false; + bool hasBlock = false; + bool hasSelf = false; + for (int i = 0; i < parameterInfos.Length; i++) { + if (parameterInfos[i].ParameterType.IsByRef) { + LogMethodError("has ref/out parameter", methodDef, overload); + } + + var type = parameterInfos[i].ParameterType; + + if (type == typeof(CodeContext)) { + LogMethodError("CodeContext is obsolete use RubyContext instead.", methodDef, overload); + } + + if (type == typeof(RubyContext) || type == typeof(RubyScope)) { + if (hasContext) { + LogMethodError("has multiple context parameters", methodDef, overload); + } + + if (i != 0) { + LogMethodError("Context parameter must be the first parameter", methodDef, overload); + } + + hasContext = true; + } else if (type == typeof(BlockParam)) { + if (hasBlock) { + LogMethodError("has multiple block parameters.", methodDef, overload); + } + + // TODO: sites + + if (hasContext && i != 1) { + LogMethodError("Block parameter must be the first parameter after context parameter", methodDef, overload); + } + + if (!hasContext && i != 0) { + LogMethodError("Block parameter must be the first parameter", methodDef, overload); + } + + // TODO: we should detect a call to the BlockParam.Yield: + //if (overload.ReturnType != typeof(object)) { + // LogMethodWarning("A method that yields to a block must return Object.", methodDef, overload); + //} + + hasBlock = true; + } else if (!hasSelf) { + // self + if (isInstance) { + Debug.Assert(methodDef != null); + + // TODO: + //if (parameterInfos[i].Name != "self") { + // LogMethodWarning("Self parameter should be named 'self'.", methodDef, overload); + //} + } else { + Type requiredType; + + switch (moduleDef.Kind) { + case ModuleKind.Module: requiredType = typeof(RubyModule); break; + case ModuleKind.Singleton: + case ModuleKind.Class: requiredType = typeof(RubyClass); break; + default: throw Assert.Unreachable; + } + + if (!type.IsAssignableFrom(requiredType)) { + LogMethodError("Invalid type of self parameter: it must be assignable from '{0}'.", methodDef, overload, requiredType); + } + + if (parameterInfos[i].Name != "self") { + LogMethodError("Self parameter should be named 'self'.", methodDef, overload); + } + } + + hasSelf = true; + } + } + + if (!hasSelf) { + LogMethodError("Missing self parameter", methodDef, overload); + } + } + } + } + + private void ReflectFieldConstants(ModuleDef/*!*/ moduleDef) { + Debug.Assert(moduleDef.Trait != null); + + BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; + foreach (FieldInfo field in moduleDef.Trait.GetFields(flags)) { + ReflectConstants(moduleDef, field); + } + } + + private void ReflectConstants(ModuleDef/*!*/ moduleDef, MemberInfo/*!*/ member) { + Debug.Assert(member is MethodInfo || member is FieldInfo); + + foreach (RubyConstantAttribute attr in member.GetCustomAttributes(typeof(RubyConstantAttribute), false)) { + string name = attr.Name ?? member.Name; + + ConstantDef existing; + if (moduleDef.Constants.TryGetValue(name, out existing)) { + LogError("Constant '{0}' defined by multiple members: '{1}' and '{2}'", name, existing.Member.Name, member.Name); + continue; + } + + if (String.IsNullOrEmpty(name) || name[0] < 'A' && name[0] > 'Z') { + LogError("Invalid constant name: '{0}' ({1}::{2})", name, existing.Member.DeclaringType.FullName, existing.Member.Name); + continue; + } + + moduleDef.Constants.Add(name, new ConstantDef(name, member, attr.BuildConfig)); + } + } + + private void ReflectAliases(ModuleDef/*!*/ moduleDef) { + // TODO: check for duplicates (in general in the scope of dll) + foreach (RubyConstantAttribute attr in moduleDef.Trait.GetCustomAttributes(typeof(RubyConstantAttribute), false)) { + if (attr.Name == null) { + LogError("Constant alias for module/class must have a name (type '{0}')", moduleDef.Trait.FullName); + } + + if (moduleDef.Aliases.IndexOf(attr.Name) != -1) { + LogError("Duplicate module/class alias '{0}' (type '{1}')", attr.Name, moduleDef.Trait.FullName); + } + + moduleDef.Aliases.Add(attr.Name); + } + } + + #endregion + + private bool RequireStatic(MethodBase/*!*/ method) { + if (!method.IsStatic) { + Console.Error.WriteLine("Instance methods not supported (method '{0}.{1}')", TypeName(method.DeclaringType), method.Name); + AnyErrors = true; + return false; + } + return true; + } + + private void LogError(string/*!*/ message, params object[] args) { + Console.Error.Write("Error: "); + Console.Error.WriteLine(message, args); + AnyErrors = true; + } + + private void LogWarning(string/*!*/ message, params object[] args) { + Console.Error.Write("Warning: "); + Console.Error.WriteLine(message, args); + } + + private void LogMethodError(string/*!*/ message, MethodDef methodDef, MethodBase/*!*/ overload, params object[] args) { + string methodName = (methodDef != null) ? "method \"" + methodDef.Name + '"' : "factory"; + Console.Error.WriteLine("Error: {0}: {1}", methodName, String.Format(message, args)); + Console.Error.WriteLine(" overload: {0}", ReflectionUtils.FormatSignature(new StringBuilder(), overload)); + AnyErrors = true; + } + + private void LogMethodWarning(string/*!*/ message, MethodDef methodDef, MethodBase/*!*/ overload,params object[] args) { + string methodName = (methodDef != null) ? "method \"" + methodDef.Name + '"' : "factory"; + Console.Error.WriteLine("Warning: {0}: {1}", methodName, String.Format(message, args)); + Console.Error.WriteLine(" overload: {0}", ReflectionUtils.FormatSignature(new StringBuilder(), overload)); + } + + #region Code Generation + + public void GenerateCode(IndentedTextWriter/*!*/ output) { + _output = output; + + _output.WriteLine("namespace {0} {{", _namespace); + _output.Indent++; + + _output.WriteLine("public sealed class {0} : {1} {{", _initializerName, TypeLibraryInitializer); + _output.Indent++; + + _output.WriteLine("protected override void LoadModules() {"); + _output.Indent++; + + GenerateModuleRegistrations(); + + _output.Indent--; + _output.WriteLine("}"); + _output.WriteLine(); + + GenerateTraitInitializations(_moduleDefs); + GenerateExceptionFactories(_moduleDefs); + + _output.Indent--; + _output.WriteLine("}"); + + _output.Indent--; + _output.WriteLine("}"); + _output.WriteLine(); + } + + private void GenerateModuleRegistrations() { + + // primitives: + if (Builtins) { + _output.WriteLine("Context.RegisterPrimitives("); + _output.Indent++; + + _output.WriteLine("new {0}(Load{1}_Instance),", TypeActionOfRubyModule, RubyClass.ClassSingletonName); + _output.WriteLine("new {0}(Load{1}_Instance),", TypeActionOfRubyModule, RubyClass.ClassSingletonSingletonName); + _output.WriteLine("new {0}(Load{1}_Instance),", TypeActionOfRubyModule, RubyClass.MainSingletonName); + + _output.WriteLine("new {0}(LoadKernel_Instance),", TypeActionOfRubyModule); + _output.WriteLine("new {0}(LoadObject_Instance),", TypeActionOfRubyModule); + _output.WriteLine("new {0}(LoadModule_Instance),", TypeActionOfRubyModule); + _output.WriteLine("new {0}(LoadClass_Instance),", TypeActionOfRubyModule); + + _output.WriteLine("new {0}(LoadKernel_Class),", TypeActionOfRubyModule); + _output.WriteLine("new {0}(LoadObject_Class),", TypeActionOfRubyModule); + _output.WriteLine("new {0}(LoadModule_Class),", TypeActionOfRubyModule); + _output.WriteLine("new {0}(LoadClass_Class)", TypeActionOfRubyModule); + + _output.Indent--; + _output.WriteLine(");"); + } + + // generate references: + foreach (KeyValuePair moduleRef in _moduleRefs) { + _output.WriteLine("{0} {1} = GetModule(typeof({2}));", TypeRubyModule, moduleRef.Value, moduleRef.Key); + } + + foreach (KeyValuePair classRef in _classRefs) { + _output.WriteLine("{0} {1} = GetClass(typeof({2}));", TypeRubyClass, classRef.Value, classRef.Key); + } + + _output.WriteLine(); + _output.WriteLine(); + + // We need to generate these in proper dependency order + // also, we want the sort to be stable to improve the + // quality of the resulting diff of the generated code. + ModuleDef[] worklist = new ModuleDef[_moduleDefs.Count]; + _moduleDefs.Values.CopyTo(worklist, 0); + Array.Sort(worklist, delegate(ModuleDef x, ModuleDef y) { + // if the dependency order is the same, fall back to the type name + if (x.DependencyOrder == y.DependencyOrder) { + return x.QualifiedName.CompareTo(y.QualifiedName); + } + return x.DependencyOrder - y.DependencyOrder; + }); + + // classes: + foreach (ModuleDef def in worklist) { + GenerateModuleRegistration(def); + } + + // add constants for non-global nested modules, adds aliases: + foreach (ModuleDef def in worklist) { + if (def.IsGlobal && def.Aliases.Count > 0 || def.DeclaringTypeRef != null) { + if (def.BuildConfig != null) { + _output.WriteLine("#if " + def.BuildConfig); + } + + if (def.IsGlobal) { + GenerateAliases(def, ModuleDef.ObjectClassRef); + } else if (def.DeclaringTypeRef != null) { + GenerateAliases(def, def.DeclaringTypeRef); + _output.WriteLine("{0}.SetConstant(\"{1}\", {2});", def.DeclaringTypeRef, def.SimpleName, def.Reference); + } + + if (def.BuildConfig != null) { + _output.WriteLine("#endif"); + } + } + } + } + + private void GenerateAliases(ModuleDef/*!*/ def, string/*!*/ ownerRef) { + foreach (string alias in def.Aliases) { + _output.WriteLine("{0}.SetConstant(\"{1}\", {2});", ownerRef, alias, def.Reference); + } + } + + private void GenerateModuleRegistration(ModuleDef/*!*/ def) { + if (def.IsPrimitive) { + _output.WriteLine("// Skipped primitive: {0}", def.QualifiedName); + return; + } + + if (def.BuildConfig != null) { + _output.WriteLine("#if " + def.BuildConfig); + } + + switch (def.Kind) { + case ModuleKind.Class: + if (def.Definition != null) { + _output.Write("{0} {1} = ", TypeRubyClass, def.Definition); + } + + if (Builtins && !def.IsExtension) { + if (def.QualifiedName == "NilClass") { + _output.Write("Context.NilClass = "); + } else if (def.QualifiedName == "TrueClass") { + _output.Write("Context.TrueClass = "); + } else if (def.QualifiedName == "FalseClass") { + _output.Write("Context.FalseClass = "); + } else if (def.QualifiedName == "Exception") { + _output.Write("Context.ExceptionClass = "); + } else if (def.QualifiedName == "StandardError") { + _output.Write("Context.StandardErrorClass = "); + } + } + +#if TODO + string extensionType = "null"; + if (def.Trait != def.Extends) { + extensionType = string.Format("typeof({0})", TypeName(def.Trait)); + } + pass: extensionType, +#endif + + if (def.IsExtension) { + _output.Write("ExtendClass(typeof({0}), {1}, {2}, ", + TypeName(def.Extends), + (def.HasInstanceInitializer) ? String.Format("new {0}(Load{1}_Instance)", TypeActionOfRubyModule, def.Id) : "null", + (def.HasClassInitializer) ? String.Format("new {0}(Load{1}_Class)", TypeActionOfRubyModule, def.Id) : "null" + ); + } else { + _output.Write("Define{0}Class(\"{1}\", typeof({2}), {3}, {4}, {5}, ", + def.IsGlobal ? "Global" : "", + def.QualifiedName, + TypeName(def.Extends), + def.Super.RefName, + (def.HasInstanceInitializer) ? String.Format("new {0}(Load{1}_Instance)", TypeActionOfRubyModule, def.Id) : "null", + (def.HasClassInitializer) ? String.Format("new {0}(Load{1}_Class)", TypeActionOfRubyModule, def.Id) : "null" + ); + } + + GenerateInclusions(def); + + _output.Write(", "); + + if (def.Factories.Count > 0) { + GenerateDelegatesArrayCreation(def.Factories); + } else if (def.IsException) { + GenerateExceptionFactoryDelegateArray(def); + } else { + _output.Write("null"); + } + + _output.WriteLine(");"); + break; + + case ModuleKind.Module: + if (def.Definition != null) { + _output.Write("{0} {1} = ", TypeRubyModule, def.Definition); + } + + if (def.IsExtension) { + _output.Write("ExtendModule(typeof({0}), {1}, {2}, ", + TypeName(def.Extends), + (def.HasInstanceInitializer) ? String.Format("new {0}(Load{1}_Instance)", TypeActionOfRubyModule, def.Id) : "null", + (def.HasClassInitializer) ? String.Format("new {0}(Load{1}_Class)", TypeActionOfRubyModule, def.Id) : "null" + ); + } else { + _output.Write("Define{0}Module(\"{1}\", typeof({2}), {3}, {4}, ", + def.IsGlobal ? "Global" : "", + def.QualifiedName, + TypeName(def.Extends), + (def.HasInstanceInitializer) ? String.Format("new {0}(Load{1}_Instance)", TypeActionOfRubyModule, def.Id) : "null", + (def.HasClassInitializer) ? String.Format("new {0}(Load{1}_Class)", TypeActionOfRubyModule, def.Id) : "null" + ); + } + + GenerateInclusions(def); + + _output.WriteLine(");"); + break; + + case ModuleKind.Singleton: + if (def.Definition != null) { + _output.Write("object {0} = ", def.Definition); + } + + _output.Write("DefineSingleton({0}, {1}, ", + (def.HasInstanceInitializer) ? String.Format("new {0}(Load{1}_Instance)", TypeActionOfRubyModule, def.Id) : "null", + (def.HasClassInitializer) ? String.Format("new {0}(Load{1}_Class)", TypeActionOfRubyModule, def.Id) : "null" + ); + + GenerateInclusions(def); + + _output.WriteLine(");"); + break; + + default: + throw Assert.Unreachable; + } + + if (def.BuildConfig != null) { + _output.WriteLine("#endif"); + } + } + + private void GenerateInclusions(ModuleDef/*!*/ def) { + List mixinRefs = new List(); + AddMixinRefsRecursive(mixinRefs, def); + + if (mixinRefs.Count > 0) { + _output.Write("new {0}[] {{", TypeRubyModule); + foreach (string mixinRef in mixinRefs) { + _output.Write("{0}, ", mixinRef); + } + _output.Write("}"); + } else { + _output.Write("{0}.EmptyArray", TypeRubyModule); + } + } + + private void AddMixinRefsRecursive(List/*!*/ mixinRefs, ModuleDef/*!*/ def) { + foreach (MixinRef mixin in def.Mixins) { + if (mixin.Copy) { + AddMixinRefsRecursive(mixinRefs, mixin.Module.Definition); + } else { + mixinRefs.Add(mixin.Module.RefName); + } + } + } + + private void GenerateTraitInitializations(IDictionary/*!*/ traitDefs) { + // Sort the items before we generate them to improve diff quality + ModuleDef[] worklist = new ModuleDef[traitDefs.Count]; + traitDefs.Values.CopyTo(worklist, 0); + Array.Sort(worklist, delegate(ModuleDef x, ModuleDef y) { return x.QualifiedName.CompareTo(y.QualifiedName); }); + + foreach (ModuleDef moduleDef in worklist) { + GenerateTraitInitialization(moduleDef); + } + } + + private void GenerateTraitInitialization(ModuleDef/*!*/ moduleDef) { + GenerateTraitInitialization(moduleDef, true); + GenerateTraitInitialization(moduleDef, false); + } + + private void GenerateTraitInitialization(ModuleDef/*!*/ moduleDef, bool isInstance) { + if (isInstance ? moduleDef.HasInstanceInitializer : moduleDef.HasClassInitializer) { + if (moduleDef.BuildConfig != null) { + _output.WriteLine("#if " + moduleDef.BuildConfig); + } + _output.WriteLine("private void Load{0}_{2}({1}/*!*/ module) {{", moduleDef.Id, TypeRubyModule, isInstance ? "Instance" : "Class"); + _output.Indent++; + + if (isInstance) { + GenerateConstants(moduleDef); + } + + GenerateIncludedTraitLoaders(moduleDef, isInstance); + GenerateHiddenMethods(isInstance ? moduleDef.HiddenInstanceMethods : moduleDef.HiddenClassMethods); + GenerateMethods(moduleDef.Trait, isInstance ? moduleDef.InstanceMethods : moduleDef.ClassMethods); + + _output.Indent--; + _output.WriteLine("}"); + if (moduleDef.BuildConfig != null) { + _output.WriteLine("#endif"); + } + _output.WriteLine(); + } + } + + private void GenerateConstants(ModuleDef/*!*/ moduleDef) { + // add constants on module + foreach (var constantDef in moduleDef.Constants.Values) { + if (constantDef.BuildConfig != null) { + _output.WriteLine("#if " + constantDef.BuildConfig); + } + + _output.Write("module.SetConstant(\"{0}\", {1}.{2}", constantDef.Name, + TypeName(constantDef.Member.DeclaringType), constantDef.Member.Name); + + if (constantDef.Member is MethodInfo) { + _output.Write("(module)"); + } + + _output.WriteLine(");"); + + if (constantDef.BuildConfig != null) { + _output.WriteLine("#endif"); + } + } + _output.WriteLine(); + } + + private void GenerateIncludedTraitLoaders(ModuleDef/*!*/ moduleDef, bool isInstance) { + foreach (MixinRef mixin in moduleDef.Mixins) { + ModuleDef def = mixin.Module.Definition; + if (mixin.Copy && (isInstance ? def.HasInstanceInitializer : def.HasClassInitializer)) { + _output.WriteLine("Load{0}_{1}(module);", mixin.Module.Definition.Id, isInstance ? "Instance" : "Class"); + } + } + } + + private void GenerateHiddenMethods(IDictionary/*!*/ methods) { + foreach (KeyValuePair entry in methods) { + if (entry.Value == HiddenMethod.Undefined) { + _output.WriteLine("module.UndefineLibraryMethod(\"{0}\");", entry.Key); + } else { + _output.WriteLine("module.HideMethod(\"{0}\");", entry.Key); + } + } + } + + private void GenerateMethods(Type/*!*/ type, IDictionary/*!*/ methods) { + foreach (MethodDef def in methods.Values) { + if (def.BuildConfig != null) { + _output.WriteLine("#if " + def.BuildConfig); + } + + if (def.IsRuleGenerator) { + _output.WriteLine("module.DefineRuleGenerator(\"{0}\", 0x{1:x}, {2}.{3}());", + def.Name, + (int)def.Attributes, + TypeName(def.Overloads[0].DeclaringType), + def.Overloads[0].Name); + } else { + _output.Write("module.DefineLibraryMethod(\"{0}\", 0x{1:x}", def.Name, (int)def.Attributes); + + _output.Write(", "); + + GenerateDelegatesArrayCreation(def.Overloads); + + _output.WriteLine(");"); + } + + _output.WriteLine(); + + if (def.BuildConfig != null) { + _output.WriteLine("#endif"); + } + } + } + + private void GenerateDelegatesArrayCreation(IEnumerable/*!*/ methods) { + _output.WriteLine("new {0}[] {{", TypeDelegate); + _output.Indent++; + + foreach (MethodInfo method in methods) { + GenerateDelegateCreation(method); + _output.WriteLine(","); + } + + _output.Indent--; + _output.Write("}"); + } + + private const string ExceptionFactoryPrefix = "ExceptionFactory__"; + + private void GenerateExceptionFactoryDelegateArray(ModuleDef/*!*/ moduleDef) { + Debug.Assert(moduleDef.IsException); + _output.Write("new {0}[] {{ new {1}{2}({3}.{4}{5}) }}", + TypeDelegate, + TypeFunction, + ReflectionUtils.FormatTypeArgs(new StringBuilder(), new[] { typeof(RubyClass), typeof(object), typeof(Exception) }), + _initializerName, + ExceptionFactoryPrefix, + moduleDef.Id + ); + } + + private void GenerateExceptionFactories(IDictionary/*!*/ moduleDefs) { + // TODO: sort by name + foreach (var moduleDef in moduleDefs.Values) { + if (moduleDef.IsException) { + if (moduleDef.BuildConfig != null) { + _output.WriteLine("#if " + moduleDef.BuildConfig); + } + + // public static Exception/*!*/ Factory(RubyClass/*!*/ self, [DefaultParameterValue(null)]object message) { + // return InitializeException(new Exception(GetClrMessage(self, message)), message); + // } + + _output.WriteLine("public static {0}/*!*/ {1}{2}({3}/*!*/ self, [{4}(null)]object message) {{", + TypeName(typeof(Exception)), + ExceptionFactoryPrefix, + moduleDef.Id, + TypeRubyClass, + TypeName(typeof(DefaultParameterValueAttribute)) + ); + _output.Indent++; + + _output.WriteLine("return {0}.{2}(new {1}({0}.{3}(self, message), ({4})null), message);", + TypeName(typeof(RubyExceptionData)), + TypeName(moduleDef.Extends), + new Func(RubyExceptionData.InitializeException).Method.Name, + new Func(RubyExceptionData.GetClrMessage).Method.Name, + TypeName(typeof(Exception)) + ); + + _output.Indent--; + _output.WriteLine("}"); + _output.WriteLine(); + + if (moduleDef.BuildConfig != null) { + _output.WriteLine("#endif"); + } + } + } + } + + private void GenerateDelegateCreation(MethodInfo/*!*/ method) { + ParameterInfo[] ps = method.GetParameters(); + Type[] paramTypes = Array.ConvertAll(ps, delegate(ParameterInfo p) { return p.ParameterType; }); + + string delegateType; + if (method.ReturnType != typeof(void)) { + delegateType = TypeFunction; + paramTypes = ArrayUtils.Append(paramTypes, method.ReturnType); + } else if (paramTypes.Length == 0) { + delegateType = TypeAction0; + } else if (paramTypes.Length == 1) { + delegateType = TypeAction1; + } else { + delegateType = TypeActionN; + } + + _output.Write("new {0}{1}({2}.{3})", + delegateType, + ReflectionUtils.FormatTypeArgs(new StringBuilder(), paramTypes), + TypeName(method.DeclaringType), + method.Name + ); + } + + #endregion + + #region Helpers + + private static string/*!*/ TypeName(Type/*!*/ type) { + return ReflectionUtils.FormatTypeName(new StringBuilder(), type).ToString(); + } + + private static string/*!*/ GenericTypeName(Type/*!*/ type) { + if (type.IsGenericTypeDefinition) { + return String.Concat(type.Namespace, ".", type.Name.Substring(0, type.Name.IndexOf('`'))); + } else { + return type.Name; + } + } + + private class TypeComparer : IComparer { + public int Compare(Type/*!*/ x, Type/*!*/ y) { + return x.FullName.CompareTo(y.FullName); + } + } + + #endregion +} + diff --git a/merlin/main/languages/ruby/ClassInitGenerator/Properties/AssemblyInfo.cs b/merlin/main/languages/ruby/ClassInitGenerator/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..283ea9bbdd --- /dev/null +++ b/merlin/main/languages/ruby/ClassInitGenerator/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using IronRuby.Runtime; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ClassInitGenerator")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("ClassInitGenerator")] +[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("450cd1a0-cd00-4b7a-a9f8-bf325b50dc0d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion(RubyContext.IronRubyVersionString)] +[assembly: AssemblyFileVersion(RubyContext.IronRubyVersionString)] diff --git a/merlin/main/languages/ruby/ClassInitGenerator/ReflectionCacheGenerator.cs b/merlin/main/languages/ruby/ClassInitGenerator/ReflectionCacheGenerator.cs new file mode 100644 index 0000000000..23eea20410 --- /dev/null +++ b/merlin/main/languages/ruby/ClassInitGenerator/ReflectionCacheGenerator.cs @@ -0,0 +1,202 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using IronRuby.Runtime; +using System.CodeDom.Compiler; +using System.IO; +using System.Reflection; +using IronRuby.Compiler.Generation; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler; +using IronRuby.Runtime.Calls; + +internal sealed class ReflectionCacheGenerator : Generator { + private static readonly Type MethodCacheType = typeof(IronRuby.Compiler.Methods); + + private string _outFile; + private IndentedTextWriter _output; + private bool _anyError; + + private ReflectionCacheGenerator() { + } + + public static ReflectionCacheGenerator Create(string[]/*!*/ args) { + var gen = new ReflectionCacheGenerator(); + + for (int i = 0; i < args.Length; i++) { + KeyValuePair arg = ToNameValue(args[i]); + + switch (arg.Key) { + case "refcache": + // skip + break; + + case "out": + gen._outFile = arg.Value; + break; + + default: + Console.Error.WriteLine("Unknown option: {0}", arg.Key); + return null; + } + } + + if (gen._outFile == null) { + Console.Error.WriteLine("Output file not specified"); + return null; + } + + return gen; + } + + public void Generate() { + _anyError = false; + + var methods = ReflectMethods(typeof(RubyOps)).Values; + + if (_anyError) { + Environment.ExitCode = 1; + return; + } + + using (TextWriter writer = new StreamWriter(File.Open(_outFile, FileMode.Create, FileAccess.Write))) { + _output = new IndentedTextWriter(writer, " "); + _output.NewLine = "\r\n"; + + WriteLicenseStatement(writer); + + _output.WriteLine("using System.Reflection;"); + _output.WriteLine("using IronRuby.Runtime;"); + _output.WriteLine("using Microsoft.Scripting.Utils;"); + + _output.WriteLine(); + _output.WriteLine("namespace {0} {{", MethodCacheType.Namespace); + _output.Indent++; + + _output.WriteLine("internal static partial class {0} {{", MethodCacheType.Name); + _output.Indent++; + + GenerateOps(methods); + + _output.WriteLine(); + + GenerateStringFactoryOps("CreateRegex"); + GenerateStringFactoryOps("CreateMutableString"); + GenerateStringFactoryOps("CreateSymbol"); + + GenerateOptimizedOps("Yield", BlockDispatcher.MaxBlockArity); + GenerateOptimizedOps("YieldSplat", BlockDispatcher.MaxBlockArity); + + _output.Indent--; + _output.WriteLine("}"); + + _output.Indent--; + _output.WriteLine("}"); + } + } + + private Dictionary/*!*/ ReflectMethods(Type/*!*/ type) { + var result = new Dictionary(); + var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); + foreach (var method in methods) { + if (method.IsDefined(typeof(EmittedAttribute), false)) { + MethodInfo existingMethod; + if (result.TryGetValue(method.Name, out existingMethod)) { + Console.WriteLine("ERROR: Emitted methods should not have overloads: \n\t{0}\n\t{1}", + ReflectionUtils.FormatSignature(new StringBuilder(), existingMethod), + ReflectionUtils.FormatSignature(new StringBuilder(), method)); + _anyError = true; + } else { + result.Add(method.Name, method); + } + } + } + return result; + } + + private void GenerateOps(ICollection/*!*/ methods) { + _output.Write("private static MethodInfo "); + int count = 0; + foreach (var method in methods) { + if (count > 0) { + _output.Write(", "); + } + + count++; + + if (count % 20 == 0) { + _output.WriteLine(); + } + + _output.Write("_"); + _output.Write(method.Name); + } + _output.WriteLine(";"); + + _output.WriteLine(); + + foreach (var method in methods) { + _output.WriteLine("public static MethodInfo/*!*/ {0} {{ get {{ return _{0} ?? (_{0} = GetMethod(typeof(RubyOps), \"{0}\")); }} }}", + method.Name); + } + } + + private void GenerateStringFactoryOps(string/*!*/ baseName) { + _output.WriteLine("public static MethodInfo/*!*/ {0}(string/*!*/ suffix) {{", baseName); + _output.Indent++; + + _output.WriteLine("switch (suffix) {"); + _output.Indent++; + + foreach (string suffix in new[] { "N", "B", "E", "U", "M", "BM", "UM", "EM", "MB", "MU", "ME", "MM" }) { + _output.WriteLine("case \"{1}\": return {0}{1};", baseName, suffix); + } + + _output.Indent--; + _output.WriteLine("}"); + + _output.WriteLine("throw Assert.Unreachable;"); + + _output.Indent--; + _output.WriteLine("}"); + + _output.WriteLine(); + } + + private void GenerateOptimizedOps(string/*!*/ baseName, int maxParamCount) { + _output.WriteLine("public static MethodInfo/*!*/ {0}(int parameterCount) {{", baseName); + _output.Indent++; + + _output.WriteLine("switch (parameterCount) {"); + _output.Indent++; + + for (int i = 0; i <= maxParamCount; i++) { + _output.WriteLine("case {1}: return {0}{1};", baseName, i); + } + + _output.Indent--; + _output.WriteLine("}"); + + _output.WriteLine("return {0}N;", baseName); + + _output.Indent--; + _output.WriteLine("}"); + + _output.WriteLine(); + } +} diff --git a/merlin/main/languages/ruby/Console/Program.cs b/merlin/main/languages/ruby/Console/Program.cs new file mode 100644 index 0000000000..f626356f99 --- /dev/null +++ b/merlin/main/languages/ruby/Console/Program.cs @@ -0,0 +1,47 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Shell; +using IronRuby; +using IronRuby.Hosting; +using IronRuby.Runtime; +using System.Dynamic.Utils; + +internal sealed class RubyConsoleHost : ConsoleHost { + + protected override Type Provider { + get { return typeof(RubyContext); } + } + + protected override CommandLine/*!*/ CreateCommandLine() { + return new RubyCommandLine(); + } + + protected override OptionsParser/*!*/ CreateOptionsParser() { + return new RubyOptionsParser(); + } + + protected override LanguageSetup CreateLanguageSetup() { + return Ruby.CreateRubySetup(); + } + + [STAThread] + [RubyStackTraceHidden] + static int Main(string[] args) { + return new RubyConsoleHost().Run(args); + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Console/Properties/AssemblyInfo.cs b/merlin/main/languages/ruby/Console/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..1fb296ca69 --- /dev/null +++ b/merlin/main/languages/ruby/Console/Properties/AssemblyInfo.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using IronRuby.Runtime; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RubyConsole")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("RubyConsole")] +[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4fcc9697-d2dd-48a1-be74-41db600df98d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion(RubyContext.IronRubyVersionString)] +[assembly: AssemblyFileVersion(RubyContext.IronRubyVersionString)] diff --git a/merlin/main/languages/ruby/Console/Ruby.Console.csproj b/merlin/main/languages/ruby/Console/Ruby.Console.csproj new file mode 100644 index 0000000000..fd1f2924b4 --- /dev/null +++ b/merlin/main/languages/ruby/Console/Ruby.Console.csproj @@ -0,0 +1,76 @@ + + + Debug + AnyCPU + 9.0.30718 + 2.0 + {D6AB587D-A888-4B98-85AC-F15E36F53838} + Exe + Properties + IronRuby + ir + SAK + SAK + SAK + SAK + 2.0 + 618,1685 + + + true + full + false + ..\..\..\Bin\Debug\ + TRACE;DEBUG;SIGNED + prompt + 4 + true + ..\..\..\MSSharedLibKey.snk + true + true + + + pdbonly + true + ..\..\..\Bin\Release\ + TRACE;SIGNED + prompt + 4 + true + ..\..\..\MSSharedLibKey.snk + true + + + + + + + + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4} + Microsoft.Scripting.Core + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Scripting + + + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD} + Ruby %28Languages\Ruby\Ruby%29 + + + + + + + + + + + + diff --git a/merlin/main/languages/ruby/Console/Ruby.Console.csproj.vspscc b/merlin/main/languages/ruby/Console/Ruby.Console.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/merlin/main/languages/ruby/Console/Ruby.Console.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/merlin/main/languages/ruby/Docs/IronRuby.docx b/merlin/main/languages/ruby/Docs/IronRuby.docx new file mode 100644 index 0000000000..b6b67b7f09 Binary files /dev/null and b/merlin/main/languages/ruby/Docs/IronRuby.docx differ diff --git a/merlin/main/languages/ruby/Docs/readme.htm b/merlin/main/languages/ruby/Docs/readme.htm new file mode 100644 index 0000000000..26a3a60ef9 --- /dev/null +++ b/merlin/main/languages/ruby/Docs/readme.htm @@ -0,0 +1,9762 @@ + + + + + + + + + + + + IronRuby Docs - Developer documentation for Microsoft IronRuby + + + + + + + + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Background: #fff
    +Foreground: #000
    +PrimaryPale: #8cf
    +PrimaryLight: #18f
    +PrimaryMid: #04b
    +PrimaryDark: #014
    +SecondaryPale: #ffc
    +SecondaryLight: #fe8
    +SecondaryMid: #db4
    +SecondaryDark: #841
    +TertiaryPale: #eee
    +TertiaryLight: #ccc
    +TertiaryMid: #999
    +TertiaryDark: #666
    +Error: #f88
    +
    +
    +
    /*{{{*/
    +body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
    +
    +a {color:[[ColorPalette::PrimaryMid]];}
    +a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
    +a img {border:0;}
    +
    +h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
    +h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
    +h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
    +
    +.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
    +.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
    +.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
    +
    +.header {background:[[ColorPalette::PrimaryMid]];}
    +.headerShadow {color:[[ColorPalette::Foreground]];}
    +.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
    +.headerForeground {color:[[ColorPalette::Background]];}
    +.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
    +
    +.tabSelected{color:[[ColorPalette::PrimaryDark]];
    +	background:[[ColorPalette::TertiaryPale]];
    +	border-left:1px solid [[ColorPalette::TertiaryLight]];
    +	border-top:1px solid [[ColorPalette::TertiaryLight]];
    +	border-right:1px solid [[ColorPalette::TertiaryLight]];
    +}
    +.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
    +.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
    +.tabContents .button {border:0;}
    +
    +#sidebar {}
    +#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
    +#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
    +#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
    +#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
    +#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
    +
    +.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
    +.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
    +.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
    +.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
    +	border:1px solid [[ColorPalette::PrimaryMid]];}
    +.wizardStep.wizardStepDone {background::[[ColorPalette::TertiaryLight]];}
    +.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
    +.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
    +.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
    +	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
    +.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
    +.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
    +	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
    +
    +#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
    +#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
    +
    +.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
    +
    +.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
    +.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
    +.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
    +.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
    +.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
    +.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
    +.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
    +.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
    +
    +.tiddler .defaultCommand {font-weight:bold;}
    +
    +.shadow .title {color:[[ColorPalette::TertiaryDark]];}
    +
    +.title {color:[[ColorPalette::SecondaryDark]];}
    +.subtitle {color:[[ColorPalette::TertiaryDark]];}
    +
    +.toolbar {color:[[ColorPalette::PrimaryMid]];}
    +.toolbar a {color:[[ColorPalette::TertiaryLight]];}
    +.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
    +.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
    +
    +.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
    +.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
    +.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
    +.tagging .button, .tagged .button {border:none;}
    +
    +.footer {color:[[ColorPalette::TertiaryLight]];}
    +.selected .footer {color:[[ColorPalette::TertiaryMid]];}
    +
    +.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
    +.sparktick {background:[[ColorPalette::PrimaryDark]];}
    +
    +.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
    +.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
    +.lowlight {background:[[ColorPalette::TertiaryLight]];}
    +
    +.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
    +
    +.imageLink, #displayArea .imageLink {background:transparent;}
    +
    +.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
    +
    +.viewer .listTitle {list-style-type:none; margin-left:-2em;}
    +.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
    +.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
    +
    +.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
    +.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
    +.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
    +
    +.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
    +.viewer code {color:[[ColorPalette::SecondaryDark]];}
    +.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
    +
    +.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
    +
    +.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
    +.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
    +.editorFooter {color:[[ColorPalette::TertiaryMid]];}
    +
    +#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
    +#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
    +#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
    +#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
    +#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
    +#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
    +#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
    +.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
    +.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
    +#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
    +/*}}}*/
    +
    +
    +
    /*{{{*/
    +* html .tiddler {height:1%;}
    +
    +body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
    +
    +h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
    +h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
    +h4,h5,h6 {margin-top:1em;}
    +h1 {font-size:1.35em;}
    +h2 {font-size:1.25em;}
    +h3 {font-size:1.1em;}
    +h4 {font-size:1em;}
    +h5 {font-size:.9em;}
    +
    +hr {height:1px;}
    +
    +a {text-decoration:none;}
    +
    +dt {font-weight:bold;}
    +
    +ol {list-style-type:decimal;}
    +ol ol {list-style-type:lower-alpha;}
    +ol ol ol {list-style-type:lower-roman;}
    +ol ol ol ol {list-style-type:decimal;}
    +ol ol ol ol ol {list-style-type:lower-alpha;}
    +ol ol ol ol ol ol {list-style-type:lower-roman;}
    +ol ol ol ol ol ol ol {list-style-type:decimal;}
    +
    +.txtOptionInput {width:11em;}
    +
    +#contentWrapper .chkOptionInput {border:0;}
    +
    +.externalLink {text-decoration:underline;}
    +
    +.indent {margin-left:3em;}
    +.outdent {margin-left:3em; text-indent:-3em;}
    +code.escaped {white-space:nowrap;}
    +
    +.tiddlyLinkExisting {font-weight:bold;}
    +.tiddlyLinkNonExisting {font-style:italic;}
    +
    +/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
    +a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
    +
    +#mainMenu .tiddlyLinkExisting,
    +	#mainMenu .tiddlyLinkNonExisting,
    +	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
    +#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
    +
    +.header {position:relative;}
    +.header a:hover {background:transparent;}
    +.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
    +.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
    +
    +.siteTitle {font-size:3em;}
    +.siteSubtitle {font-size:1.2em;}
    +
    +#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
    +
    +#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
    +#sidebarOptions {padding-top:0.3em;}
    +#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
    +#sidebarOptions input {margin:0.4em 0.5em;}
    +#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
    +#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
    +#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
    +#sidebarTabs .tabContents {width:15em; overflow:hidden;}
    +
    +.wizard {padding:0.1em 1em 0em 2em;}
    +.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
    +.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
    +.wizardStep {padding:1em 1em 1em 1em;}
    +.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
    +.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
    +.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
    +.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
    +
    +#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
    +.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
    +#messageArea a {text-decoration:underline;}
    +
    +.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
    +.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
    +
    +.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
    +.popup .popupMessage {padding:0.4em;}
    +.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
    +.popup li.disabled {padding:0.4em;}
    +.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
    +.listBreak {font-size:1px; line-height:1px;}
    +.listBreak div {margin:2px 0;}
    +
    +.tabset {padding:1em 0em 0em 0.5em;}
    +.tab {margin:0em 0em 0em 0.25em; padding:2px;}
    +.tabContents {padding:0.5em;}
    +.tabContents ul, .tabContents ol {margin:0; padding:0;}
    +.txtMainTab .tabContents li {list-style:none;}
    +.tabContents li.listLink { margin-left:.75em;}
    +
    +#contentWrapper {display:block;}
    +#splashScreen {display:none;}
    +
    +#displayArea {margin:1em 17em 0em 14em;}
    +
    +.toolbar {text-align:right; font-size:.9em;}
    +
    +.tiddler {padding:1em 1em 0em 1em;}
    +
    +.missing .viewer,.missing .title {font-style:italic;}
    +
    +.title {font-size:1.6em; font-weight:bold;}
    +
    +.missing .subtitle {display:none;}
    +.subtitle {font-size:1.1em;}
    +
    +.tiddler .button {padding:0.2em 0.4em;}
    +
    +.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
    +.isTag .tagging {display:block;}
    +.tagged {margin:0.5em; float:right;}
    +.tagging, .tagged {font-size:0.9em; padding:0.25em;}
    +.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
    +.tagClear {clear:both;}
    +
    +.footer {font-size:.9em;}
    +.footer li {display:inline;}
    +
    +.annotation {padding:0.5em; margin:0.5em;}
    +
    +* html .viewer pre {width:99%; padding:0 0 1em 0;}
    +.viewer {line-height:1.4em; padding-top:0.5em;}
    +.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
    +.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
    +.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
    +
    +.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
    +.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
    +table.listView {font-size:0.85em; margin:0.8em 1.0em;}
    +table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
    +
    +.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
    +.viewer code {font-size:1.2em; line-height:1.4em;}
    +
    +.editor {font-size:1.1em;}
    +.editor input, .editor textarea {display:block; width:100%; font:inherit;}
    +.editorFooter {padding:0.25em 0em; font-size:.9em;}
    +.editorFooter .button {padding-top:0px; padding-bottom:0px;}
    +
    +.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
    +
    +.sparkline {line-height:1em;}
    +.sparktick {outline:0;}
    +
    +.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
    +.zoomer div {padding:1em;}
    +
    +* html #backstage {width:99%;}
    +* html #backstageArea {width:99%;}
    +#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
    +#backstageToolbar {position:relative;}
    +#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
    +#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
    +#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
    +#backstage {position:relative; width:100%; z-index:50;}
    +#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
    +.backstagePanelFooter {padding-top:0.2em; float:right;}
    +.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
    +#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
    +
    +.whenBackstage {display:none;}
    +.backstageVisible .whenBackstage {display:block;}
    +/*}}}*/
    +
    +
    +
    /***
    +StyleSheet for use when a translation requires any css style changes.
    +This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
    +***/
    +
    +/*{{{*/
    +body {font-size:0.8em;}
    +
    +#sidebarOptions {font-size:1.05em;}
    +#sidebarOptions a {font-style:normal;}
    +#sidebarOptions .sliderPanel {font-size:0.95em;}
    +
    +.subtitle {font-size:0.8em;}
    +
    +.viewer table.listView {font-size:0.95em;}
    +
    +.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
    +/*}}}*/
    +
    +
    +
    /*{{{*/
    +@media print {
    +#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton {display: none ! important;}
    +#displayArea {margin: 1em 1em 0em 1em;}
    +/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
    +noscript {display:none;}
    +}
    +/*}}}*/
    +
    +
    +
    <!--{{{-->
    +<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
    +<div class='headerShadow'>
    +<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
    +<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
    +</div>
    +<div class='headerForeground'>
    +<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
    +<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
    +</div>
    +</div>
    +<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
    +<div id='sidebar'>
    +<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
    +<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
    +</div>
    +<div id='displayArea'>
    +<div id='messageArea'></div>
    +<div id='tiddlerDisplay'></div>
    +</div>
    +<!--}}}-->
    +
    +
    +
    <!--{{{-->
    +<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
    +<div class='title' macro='view title'></div>
    +<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
    +<div class='tagging' macro='tagging'></div>
    +<div class='tagged' macro='tags'></div>
    +<div class='viewer' macro='view text wikified'></div>
    +<div class='tagClear'></div>
    +<!--}}}-->
    +
    +
    +
    <!--{{{-->
    +<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
    +<div class='title' macro='view title'></div>
    +<div class='editor' macro='edit title'></div>
    +<div macro='annotations'></div>
    +<div class='editor' macro='edit text'></div>
    +<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
    +<!--}}}-->
    +
    +
    +
    To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
    +* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
    +* MainMenu: The menu (usually on the left)
    +* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
    +You'll also need to enter your username for signing your edits: <<option txtUserName>>
    +
    +
    +
    These InterfaceOptions for customising TiddlyWiki are saved in your browser
    +
    +Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
    +
    +<<option txtUserName>>
    +<<option chkSaveBackups>> SaveBackups
    +<<option chkAutoSave>> AutoSave
    +<<option chkRegExpSearch>> RegExpSearch
    +<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
    +<<option chkAnimate>> EnableAnimations
    +
    +----
    +Also see AdvancedOptions
    +
    +
    + +
    +
    +
    !Implementation
    +
    +Let's add a {{{downcase}}} method to the MutableString class. This is the simplest kind of method - it accepts no parameters and returns a MutableString.
    +
    +MutableString lives in Ruby\Builtins\MutableString.cs. Its unit tests live in Tests\Builtins\String\test_string.rb. Here's one possible implementation of this method:
    +
    +{{{
    +[RubyMethodAttribute("downcase", RubyMethodAttributes.PublicInstance)]
    +public static MutableString DownCase(MutableString str) {
    +    MutableString result = new MutableString(str);
    +    DownCaseMutableString(result);
    +    return result;
    +}
    +}}}
    +
    +Notice that the method is a _static_ method. DLR provides its own version of C#
    +3.0's ExtensionMethods. In library code, all methods are implemented as ''static''
    +methods on the type, particularly if its on a type that we define (which is the case with MutableString). 
    +
    +If it's a type that we don't define, such as Int32, we define our extension
    +methods in a class that has the suffix "Ops" attached to the name. In the case
    +of Int32, which is really a Ruby Fixnum type, our extension methods are
    +implemented in a type called FixnumOps.
    +
    +Notice that we have attached a RubyMethodAttribute to this class. This attribute
    +defines the Ruby-callable name of the method since Ruby supports method names
    +that are illegal in C#, such as "downcase!". The RubyMethodAttributes
    +enumeration defines the instancing and visibility of the method; in this case we
    +are defining a public instance method on the String type.
    +
    +!Unit Tests
    +
    +Next, we have to implement the unit tests. In this early implementation of
    +IronRuby, we support a simple form of RSpec tests. The testing framework can be
    +found in Ruby\Tests\Util\simple_test.rb. Here's an implementation of the tests:
    +
    +{{{
    +describe "String#downcase" do
    +  it "returns a copy of self with all uppercase letters downcased" do
    +    "hELLO".downcase.should == "hello"
    +    "hello".downcase.should == "hello"
    +  end
    +  
    +  it "is locale insensitive (only replacing A-Z)" do
    +    "ÄÖÜ".downcase.should == "ÄÖÜ"
    +  end
    +end
    +}}}
    +
    +Next up, lets define a method that AcceptsParameters.
    +
    +
    +
    +
    IronRuby uses four-space indentation. Tabs must be expanded as spaces. No exceptions.
    +
    +Public names are subject to FxCop rules.
    +
    +!!Private class member variables start with underscore “_”:
    +
    +{{{
    +class MyClass {
    +    private string _name;
    +    public MyClass(string name) {
    +        _name = name;
    +    }
    +}
    +}}}
    +
    +!!Static variable names start with underscore followed by upper case letter:
    +
    +{{{
    +_StaticVar
    +}}}
    +
    +!!Open brace on same line as code:
    +
    +{{{
    +if (true) {
    +  DoSomething();
    +}
    +}}}
    +
    +!!Member variables should be declared at the top of a class declaration:
    +
    +{{{
    +class MyClass {
    +    private string _name;
    +    public MyClass(string name) {
    +        _name = name;
    +    }
    +    // Additional Code Here
    +    public string Name {
    +        get { return _name; }
    +        set { _name = value; }
    +    }
    +}
    +}}}
    +
    +Not:
    +
    +{{{
    +class MyClass {
    +    public MyClass(string name) {
    +        _name = name;
    +    }
    +      // Additional Code Here
    +    private string _name;
    +    public string Name {
    +        get { return _name; }
    +        set { _name = value; }
    +    }
    +}
    +}}}
    +
    +IronRuby uses uniform formatting of the C# code. Set your editor to use the IronRuby.vssettings file to set handling of tabs and formatting of the code. This file is located in the root directory of the IronRuby project.
    +
    +To use the team settings in Visual Studio, go to Tools->Options… Expand the “Environment” node, select “Import and Export Settings”. Check the “Use team settings file” and browse to the "IronRuby.vssettings” file located in your workspace.
    +
    +
    +
    You can compile IronRuby by running {{build.cmd}} from the root directory of your IronRuby source distribution. You must have the .NET 2.0 Framework redistributable installed, since the build scripts rely on MSBuild.
    +
    +This will generate a release mode build of IronRuby in the \bin\release directory of your source distribution.
    +
    +
    +
    IronRuby
    +
    +
    +
    Extension methods let us add methods (instance and static) to types that we didn't define. The DLR method dispatch mechanism generates the correct code to call these methods.
    +
    +For more details on C# 3.0 extension methods, see <<some article>>
    +
    +
    +
    Welcome to the IronRuby project! 
    +
    +The current version of IronRuby is the 0.1 Pre-Alpha, released on July 23. 
    +
    +IronRuby is licensed under the terms of the Microsoft Public License (Ms-PL).
    +
    +This release of IronRuby ships only in source form. You must CompileSources first.
    +
    +If you're new here, make sure you read the NewDev page first. We're currently looking for contributions in the StandardLibraries, which you can find under \ironruby\src\ironrubylibs\.
    +
    +
    +
    +
    We are dividing the IronRuby libraries into BuiltIns and StandardLibraries. This is the area of IronRuby where we need external help the most. 
    +
    +Make sure that you follow the CodingConventions. 
    +
    +If this is your first contribution, read the tutorial on how to AddAMethod to an existing type.
    +
    +
    +
    +
    +
    John Lam is an IronRuby core team member, and a Program Manager on the DynamicLanguageRuntime team at Microsoft.
    +
    +John tracks the progress of the IronRuby project on his blog, http://www.iunknown.com
    +
    +
    +
    +
    It ships with the [[.NET 2.0 Framework redist|http://www.microsoft.com/downloads/details.aspx?FamilyID=0856EACB-4362-4B0D-8EDD-AAB15C5E04F5&displaylang=en]]. We have build scripts to help you build the projects and run the unit tests. 
    +
    +
    +
    +
    +
    IronRuby
    +
    +
    +
    The Ms-PL is the least restrictive of the Microsoft source code licenses. It allows licensees to view, modify, and redistribute the source code for either commercial or non-commercial purposes. Under the Ms-PL, licensees may change the source code and share it with others. Licensees may also charge a licensing fee for their modified work if they so wish. Microsoft uses this license most commonly for its developer tools, applications, and components. 
    +
    +!Microsoft Public License (Ms-PL)
    +
    +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
    +
    +!!1. Definitions
    +
    +The terms “reproduce,” “reproduction,” “derivative works,” and “distribution” have the same meaning here as under U.S. copyright law.
    +
    +A “contribution” is the original software, or any additions or changes to the software.
    +
    +A “contributor” is any person that distributes its contribution under this license.
    +
    +“Licensed patents” are a contributor’s patent claims that read directly on its contribution.
    +
    +!!2. Grant of Rights
    +
    +(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
    +
    +(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
    +
    +!!3. Conditions and Limitations
    +
    +(A) No Trademark License- This license does not grant you rights to use any contributors’ name, logo, or trademarks.
    +
    +(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
    +
    +(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
    +
    +(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
    +
    +(E) The software is licensed “as-is.” You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
    +
    +You can always read the full text of the Microsoft Public License on the [[official web site|http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx]]. 
    +
    +
    +
    MutableString is the CLR type that represents a Ruby String. It's currently based on the StringBuilder type, which is a very efficient implementation of a char[] buffer. The current plan of record is to use this type for the near future before we invest in converting this type to a byte[] which is the long term plan.
    +
    +
    +
    New to the project? There are a few things that you should know before you dive in.
    +
    +* We are currently only accepting contributions into the StandardLibraries.
    +* Take some time to familiarize yourself with the SourceCodeLayout. 
    +* Make sure you understand how the UnitTests work.
    +* Work through the RunningFirstTime tutorial.
    +
    +Contributing code
    +
    +* Read the CodingStandards
    +* Read the tutorial on how to AddAMethod to the libraries
    +* Build the solution using MSBuild and verify that the UnitTests pass
    +
    +
    +
    <!--{{{-->
    +<div class='header'>
    +     <div class='gradient' macro='gradient vert #FF8614 #DA4A0D '>
    +	<div class='titleLine' >
    +		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
    +		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
    +	</div>
    +<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
    +    </div>
    +</div>
    +<div id='bodywrapper'>
    +<div id='sidebar'>
    +	<div id='sidebarOptions' refresh='content' 	tiddler='SideBarOptions'></div>
    +	<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
    +</div>
    +<div id='displayArea'>
    +	<div id='messageArea'></div>
    +	<div id='tiddlerDisplay'></div>
    +</div>
    +<div id='contentFooter'>TiddlyWiki</div>
    +</div>
    +
    +<!--}}}-->
    +
    +
    +
    This topic will teach you the basics of how things run in IronRuby.
    +
    +First, we will step through the Ruby.Console application to see what happens when we run a simple program:
    +
    +{{{
    +puts 'Hello, World!'
    +}}}
    +
    +You will see a number of things when we step through this code:
    +
    +# The hosting interfaces
    +# The Ruby parser
    +# The Ruby AST
    +# The transformation of the Ruby AST into the DLR AST
    +# Some interesting execution paths
    +
    +
    +
    +
    +
    +
    /***
    +This CSS by DaveBirss.
    +***/
    +/*{{{*/
    +
    +
    +.tabSelected {
    + background: #fff;
    +}
    +
    +.tabUnselected {
    + background: #eee;
    +}
    +
    +#sidebar {
    + color: #000;
    + background: transparent; 
    +}
    +
    +#sidebarOptions {
    + background: #fff;
    +}
    +
    +#sidebarOptions input {
    +	border: 1px solid #ccc;
    +}
    +
    +#sidebarOptions input:hover, #sidebarOptions input:active,  #sidebarOptions input:focus {
    +	border: 1px solid #000;
    +}
    +
    +#sidebarOptions .button {
    + color: #999;
    +}
    +
    +#sidebarOptions .button:hover {
    + color: #000;
    + background: #fff;
    + border-color:white;
    +}
    +
    +#sidebarOptions .button:active {
    + color: #000;
    + background: #fff;
    +}
    +
    +#sidebarOptions .sliderPanel {
    + background: transparent;
    +}
    +
    +#sidebarOptions .sliderPanel A {
    + color: #999;
    +}
    +
    +#sidebarOptions .sliderPanel A:hover {
    + color: #000;
    + background: #fff;
    +}
    +
    +#sidebarOptions .sliderPanel A:active {
    + color: #000;
    + background: #fff;
    +}
    +
    +.sidebarSubHeading {
    + color: #000;
    +}
    +
    +#sidebarTabs {`
    + background: #fff
    +}
    +
    +#sidebarTabs .tabSelected {
    + color: #000;
    + background: #fff;
    + border-top: solid 1px #ccc;
    + border-left: solid 1px #ccc;
    + border-right: solid 1px #ccc;
    + border-bottom: none;
    +}
    +
    +#sidebarTabs .tabUnselected {
    + color: #999;
    + background: #eee;
    + border-top: solid 1px #ccc;
    + border-left: solid 1px #ccc;
    + border-right: solid 1px #ccc;
    + border-bottom: none;
    +}
    +
    +#sidebarTabs .tabContents {
    + background: #fff;
    +}
    +
    +
    +#sidebarTabs .txtMoreTab .tabSelected {
    + background: #fff;
    +}
    +
    +#sidebarTabs .txtMoreTab .tabUnselected {
    + background: #eee;
    +}
    +
    +#sidebarTabs .txtMoreTab .tabContents {
    + background: #fff;
    +}
    +
    +#sidebarTabs .tabContents .tiddlyLink {
    + color: #999;
    + border:none;
    +}
    +
    +#sidebarTabs .tabContents .tiddlyLink:hover {
    + background: #fff;
    + color: #000;
    + border:none;
    +}
    +
    +#sidebarTabs .tabContents {
    + color: #000;
    +}
    +
    +#sidebarTabs .button {
    + color: #666;
    +}
    +
    +#sidebarTabs .tabContents .button:hover {
    + color: #000;
    + background: #fff;
    +}
    +
    +#sidebar {color:#999;}
    +/*}}}*/
    +
    +
    +
    Developer documentation for Microsoft IronRuby
    +
    +
    +
    IronRuby Docs
    +
    +
    +
    The IronRuby source code has the following layout:
    +{{{
    +\ironruby
    +  src\
    +    ironruby\
    +    ironrubylibs\
    +    microsoft.scripting\
    +  tests\
    +    ironruby\
    +    microsoft.scripting\
    +  utils\
    +    IronRuby.Tests\
    +    ironruby.console\
    +    classinitgenerator\
    +}}}
    +
    +The src\ironruby directory contains the code for the compiler and the BuiltIns. We are currently not AcceptingContributions into the core compiler.
    +
    +The src\ironrubylibs directory contains the code for the StandardLibraries. We are AcceptingContributions into this directory tree.
    +
    +The tests\ironruby directory contains the UnitTests for the compiler, BuiltIns, and StandardLibraries.
    +
    +The tests\microsoft.scripting directory contains the UnitTests for the DynamicLanguageRuntime.
    +
    +The utils\IronRuby.Tests directory contains a set of tests that are useful if you want to step into the core compiler using a debugger.
    +
    +The utils\ironruby.console directory contains a simple console-based host for the IronRuby compiler that supports REPL-style programming.
    +
    +The utils\classinitgenerator directory contains a utility that will generate code from class and method attributes used in the libraries. 
    +
    +
    +
    The StandardLibraries are the same list of libraries described in Chapter 28 of the PickAxe book. These libraries will be compiled into a separate assembly, IronRubyLibs.dll. The sources live in \ironruby\src\ironrubylibs\.
    +
    +Here's a complete list of the libraries that we are looking for contributions in:
    +
    +!Libraries to be ported to C#
    +# [[strscan|http://ruby-doc.org/stdlib/libdoc/strscan/rdoc/classes/StringScanner.html]]: a high-speed string token scanner
    +# syck: high-speed YAML parser
    +# zlib: library for [de]compressing streams and working with gzip files
    +# bigdecimal: variable precision floating point library (GPL) 
    +# readline: wrapper around GNU readline library
    +# curses: wrapper around C-based curses library
    +# enumerator: adds enumeration support to arbitrary objects and mixes in Enumerable
    +# fcntl: symbolic names for fcntl constants based on underlying OS
    +# iconv: translates string between different character encodings
    +# io: adds IO#ready? and IO#wait methods to standard IO class
    +# pty: pseudo-terminal interface
    +# racc: runtime library for code produced by racc [Ruby yacc]
    +# stringio: treat a string as if it were an I/O stream
    +# thread: utility methods for multithreaded programming. Queue utility class is still used  today by some libraries - most functionality superceded by Monitor
    +
    +!Libraries that should delegate functionality to existing .NET libraries
    +# openssl: wrapper around OpenSSL library
    +# digest: various hashing algorithms (md5, rmd160, sha1, sha2) 
    +# dl: access underlying platform's DLL infrastructure [similar to Win32API]
    +# socket: interface with platform socket implementation (unknown license)
    +# Win32API: allows access to arbitrary Win32 functions P/Invoke style wrapper
    +# win32ole: library for manipulating COM objects (GPL or Artistic License)
    +
    +!Libraries that target non-Windows platforms
    +# Dbm: wrapper around DBM databases which are persistent hash-like files 
    +# gdbm: Ruby wrapper around GNU database
    +# nkf: wrapper around Network Kanji Filter library - guess character encodings
    +# sdbm: key-value hashtable persistence library 
    +# etc: used to retrieve data under /etc directory on Unix systems
    +# syslog: wrapper around Unix syslog library 
    +# tk: library for creating cross-platform UIs
    +
    +
    +
    +
    [[SideBarWG]]
    +
    +/***
    +!Top Menu Styles
    +***/
    +/*{{{*/
    +#topMenu br {display:none; }
    +#topMenu { background: #000 ; color:#fff;padding: 1em 1em;}
    +/*}}}*/
    +
    +/***
    +!General
    +***/
    +/*{{{*/
    +body {
    + background: #444;
    + margin: 0 auto;
    +}
    +
    + #contentWrapper{
    + background: #fff;
    + border: 0;
    + margin: 0 auto;
    + width: 792px;
    + padding:0;
    +}
    +/*}}}*/
    +
    +/***
    +!Header rules
    +***/
    +/*{{{*/
    +.titleLine{
    + margin: 80px auto 0em ;
    +margin-left:1.7em;
    +margin-bottom: 40px;
    + padding: 0;
    + text-align: left;
    + color: #fff;
    +}
    +
    +.siteTitle {
    +	font-size: 2em;
    +        font-weight: bold;
    +}
    +
    +.siteSubtitle {
    +	font-size: 1.1em;
    +        display: block;
    +        margin: .5em auto 1em;
    +}
    +
    +.gradient {margin: 0 auto;}
    +
    +
    +
    +.header {
    + background: #fff; 
    + margin: 0 auto;
    + padding:0 12px;
    + width: 768px;
    +}
    +/*}}}*/
    +
    +/***
    +!Display Area
    +***/
    +/*{{{*/
    +#bodywrapper {margin:0 12px; padding:0;background:#fff; height:1%}
    +
    +#displayArea{
    + margin: 0em 16em 0em 1em;
    + text-align: left;
    +}
    +
    +.tiddler {
    +	padding: 1em 1em 0em 0em;
    +}
    +
    +h1,h2,h3,h4,h5 { color: #000; background: transparent; padding-bottom:2px; border-bottom: 1px dotted #666; }
    +.title {color:black; font-size:1.8em; border-bottom:1px solid #333; padding-bottom:0.3px;}
    +.subtitle { font-size:90%; color:#ccc; padding-left:0.25em; margin-top:0.1em; }
    +
    +.shadow .title {
    +	color: #aaa;
    +}
    +
    +.tagClear{
    +	clear: none; 
    +}
    +
    +* html .viewer pre {
    +	margin-left: 0em;
    +}
    +
    +* html .editor textarea, * html .editor input {
    +	width: 98%;
    +}
    +
    +.tiddler {margin-bottom:1em; padding-bottom:0em;}
    +
    +
    +.toolbar .button {color:#bbb; border:none;}
    +.toolbar .button:hover, .toolbar .highlight, .toolbar .marked, .toolbar a.button:active {background:transparent; color:#111; border:none; text-decoration:underline;}
    +
    +#sidebar .highlight, #sidebar .marked {background:transparent;}
    +
    +.tagging, .tagged {
    +	border: 1px solid #eee;
    +	background-color: #F7F7F7;
    +}
    +
    +.selected .tagging, .selected .tagged {
    +	background-color: #eee;
    +	border: 1px solid #bbb;
    +}
    +
    + .tagging .listTitle, .tagged .listTitle {
    +	color: #bbb;
    +}
    +
    +.selected .tagging .listTitle, .selected .tagged .listTitle {
    +	color: #222; 
    +}
    +
    +
    +.tagging .button:hover, .tagged .button:hover {
    +		border: none; background:transparent; text-decoration:underline; color:#000;
    +}
    +
    +.tagging .button, .tagged .button {
    +		color:#aaa;
    +}
    +
    +.selected .tagging .button, .selected .tagged .button {
    +		color:#000;
    +}
    +
    +.viewer blockquote {
    +	border-left: 3px solid #000;
    +}
    +
    +.viewer pre, .viewer code {
    +	border: 1px dashed #ccc;
    +	background: #eee;}
    +
    +.viewer hr {
    +	border: 0;
    +	border-top: solid 1px #333;
    + margin: 0 8em;
    +	color: #333;
    +}
    +
    +.highlight, .marked {background:transparent; color:#111; border:none; text-decoration:underline;}
    +
    +.viewer .highlight, .viewer .marked {text-decoration:none;}
    +
    +#sidebarTabs .highlight, #sidebarTabs .marked {color:#000; text-decoration:none;}
    +
    +.tabSelected {
    + color: #000;
    + background: #fff;
    + border-top: solid 1px #ccc;
    + border-left: solid 1px #ccc;
    + border-right: solid 1px #ccc;
    + border-bottom: none;
    +}
    +
    +.viewer .tabSelected:hover{color:#000;}
    +
    +.viewer .tabSelected {font-weight:bold;}
    +
    +.tabUnselected {
    + color: #999;
    + background: #eee;
    + border-top: solid 1px #ccc;
    + border-left: solid 1px #ccc;
    + border-right: solid 1px #ccc;
    + border-bottom: solid 1px #ccc;
    + padding-bottom:1px;
    +}
    +
    +.tabContents {
    + background: #fff;
    +  color: #000;
    +}
    +/*}}}*/
    +/***
    +!!!Tables
    +***/
    +/*{{{*/
    +.viewer table {
    +	border: 1px solid #000;
    +}
    +
    +.viewer th, thead td {
    +	background: #000;
    +	border: 1px solid #000;
    +	color: #fff;
    +}
    +
    +.viewer td, .viewer tr {
    +	border: 1px solid #111;
    +}
    +/*}}}*/
    +
    +
    +/***
    +!!!Editor area
    +***/
    +/*{{{*/
    +.editor input, .editor textarea {
    +	border: 1px solid #ccc;
    +}
    +
    +.editor {padding-top:0.3em;}
    +
    +.editor textarea:focus, .editor input:focus {
    +	border: 1px solid #333;
    +}
    +/*}}}*/
    +
    +/***
    +!Sidebar
    +***/
    +/*{{{*/
    +#sidebar{
    +position:relative;
    +float:right;
    +margin-bottom:1em;
    +display:inline;
    +width: 16em;
    +}
    +
    +#sidebarOptions .sliderPanel {
    +	background: #eee; border:1px solid #ccc;
    +}
    +
    +/*}}}*/
    +
    +/***
    +!Body Footer rules
    +***/
    +/*{{{*/
    +#contentFooter {
    + text-align: left;
    + clear: both;
    + color:#fff;
    + background: #000;
    + padding: 1em 2em;
    +font-weight:bold;
    +}
    +
    +/*}}}*/
    +/***
    +!Link Styles
    +***/
    +/*{{{*/
    +a{
    +	color: #000;
    +}
    +
    +a:hover{
    +        color: #FF6600;
    +        background:#fff;
    +}
    +
    +
    +.button {
    +	color: #000;
    +	border: 1px solid #fff;
    +}
    +
    +.button:hover {
    +	color: #fff;
    +	background: #ff8614;
    +	border-color: #000;
    +}
    +
    +.button:active {
    +	color: #fff;
    +	background: #ff8614;
    +	border: 1px solid #000;
    +}
    +
    +.tiddlyLink {border-bottom: 1px dotted #000;}
    +.tiddlyLink:hover {border-bottom: 1px dotted #FF6600;} 
    +
    +.titleLine a {border-bottom: 1px dotted #FF9900;}
    +
    +.titleLine a:hover {border-bottom: 1px dotted #fff;}
    +
    +.siteTitle a, .siteSubtitle a{
    + color: #fff;
    +}
    +
    +.viewer .button {border: 1px solid #ff8614; font-weight:bold;}
    +.viewer .button:hover, .viewer .marked, .viewer .highlight{background:#ff8614; color:#fff; font-weight:bold; border: 1px solid #000;}
    +
    +#topMenu .button, #topMenu .tiddlyLink {
    + margin-left:0.5em; margin-right:0.5em;
    + padding-left:3px; padding-right:3px;
    + color:white;
    +}
    +#topMenu .button:hover, #topMenu .tiddlyLink:hover { background:#000; color:#FF8814}
    +
    +#topMenu a{border:none;}
    +/*}}}*/
    +
    +/***
    +!Message Area /%=================================================%/
    +***/
    +/*{{{*/
    +#messageArea {
    +	border: 4px dotted #ff8614;
    +	background: #000;
    +	color: #fff;
    +        font-size:90%;
    +}
    +
    +#messageArea .button {
    +	padding: 0.2em;
    +	color: #000;
    +	background: #fff;
    +        text-decoration:none;
    +        font-weight:bold;
    +        border:1px solid #000; 
    +}
    +
    +#messageArea a {color:#fff;}
    +
    +#messageArea a:hover {color:#ff8614; background:transparent;}
    +
    +#messageArea .button:hover {background: #FF8614; color:#fff; border:1px solid #fff; }
    +
    +/*}}}*/
    +
    +/***
    +!Popup /%=================================================%/
    +***/
    +/*{{{*/
    +.popup {
    +	background: #ff8814;
    +	border: 1px solid #333;
    +}
    +
    +.popup hr {
    +	color: #333;
    +	background: #333;
    +	border-bottom: 1px;
    +}
    +
    +.popup li.disabled {
    +	color: #333;
    +}
    +
    +.popup li a, .popup li a:visited {
    +	color: #eee;
    +	border: none;
    +}
    +
    +.popup li a:hover {
    +	background: #ff8614;
    +	color: #fff;
    +	border: none;
    +        text-decoration:underline;
    +}
    +/*}}}*/
    +
    +.blog h2, .blog h3, .blog h4{
    +  margin:0;
    +  padding:0;
    +border-bottom:none;
    +}
    +.blog {margin-left:1.5em;}  
    +
    +
    +.blog .excerpt {
    +  margin:0;
    +margin-top:0.3em;
    +  padding: 0;
    +  margin-left:1em;
    +  padding-left:1em;
    +  font-size:90%;
    +  border-left:1px solid #ddd;
    +}
    +
    +#tiddlerWhatsNew h1, #tiddlerWhatsNew h2 {border-bottom:none;}
    +div[tags~="RecentUpdates"], div[tags~="lewcidExtension"] {margin-bottom: 2em;}
    +
    +#hoverMenu  .button, #hoverMenu  .tiddlyLink {border:none; font-weight:bold; background:#f37211; color:#fff; padding:0 5px; float:right; margin-bottom:4px;}
    +#hoverMenu .button:hover, #hoverMenu .tiddlyLink:hover {font-weight:bold; border:none; color:#f37211; background:#000; padding:0 5px; float:right; margin-bottom:4px;}
    +
    +#topMenu .fontResizer {float:right;}
    +
    +#topMenu .fontResizer .button{border:1px solid #000;}
    +#topMenu .fontResizer .button:hover {border:1px solid #f37211; color:#fff;}
    +#sidebarTabs .txtMainTab .tiddlyLinkExisting {
    + font-weight: normal;
    + font-style: normal;
    +}
    +
    +#sidebarTabs .txtMoreTab .tiddlyLinkExisting {
    + font-weight: bold;
    + font-style: normal;
    +}
    +
    +.blog h2, .blog h3, .blog h4{
    +  margin:0;
    +  padding:0;
    +border-bottom:none;
    +}
    +.blog {margin-left:1.5em;}  
    +
    +
    +.blog .excerpt {
    +  margin:0;
    +margin-top:0.3em;
    +  padding: 0;
    +  margin-left:1em;
    +  padding-left:1em;
    +  font-size:90%;
    +  border-left:1px solid #ddd;
    +}
    +
    +#tiddlerWhatsNew h1, #tiddlerWhatsNew h2 {border-bottom:none;}
    +div[tags~="RecentUpdates"], div[tags~="lewcidExtension"] {margin-bottom: 2em;}
    +
    +#hoverMenu {background:transparent;}
    +#hoverMenu  .button, #hoverMenu  .tiddlyLink {border:none; font-weight:bold; background:#f37211; color:#fff; padding:0 5px; float:right; margin-bottom:4px;}
    +#hoverMenu .button:hover, #hoverMenu .tiddlyLink:hover {font-weight:bold; border:none; color:#f37211; background:#000; padding:0 5px; float:right; margin-bottom:4px;}
    +
    +#topMenu .fontResizer {float:right;}
    +
    +#topMenu .fontResizer .button{border:1px solid #000;}
    +#topMenu .fontResizer .button:hover {border:1px solid #f37211; color:#fff;}
    +#sidebarTabs .txtMainTab .tiddlyLinkExisting {
    + font-weight: normal;
    + font-style: normal;
    +}
    +
    +#sidebarTabs .txtMoreTab .tiddlyLinkExisting {
    + font-weight: bold;
    + font-style: normal;
    +}
    +
    +
    +
    +
    +
    There are several sets of UnitTests in IronRuby. 
    +
    +If you are contributing to the libraries, you will be interested in the tests under \tests\ruby\builtins. These tests use a minimalistic unit testing framework that can be found in \tests\ruby\util\simple_test.rb.
    +
    +If you have added the \bin\release directory for IronRuby to your PATH, you can run the tests using: 
    +
    +{{{
    +rbx \tests\ruby\builtins\array\test_array.rb
    +}}}
    +
    +
    +
    +
    + + + + + + + + + + diff --git a/merlin/main/languages/ruby/Experimental/Arrays/KernelArray.rb b/merlin/main/languages/ruby/Experimental/Arrays/KernelArray.rb new file mode 100644 index 0000000000..b4b47f28f3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Arrays/KernelArray.rb @@ -0,0 +1,44 @@ +# behavior differs between 1.8 and 1.9 + +class A + def respond_to? name + puts "? #{name}" + false + end +end + +A.new.to_ary rescue p $! +p A.new.to_a rescue p $! + +module Kernel + remove_method :to_a rescue p $! +end + +puts '---' + +p Array(A.new) + +puts '---' + +class B + def to_ary + 'to_ary' + end + + def to_a + 'to_a' + end + + def respond_to? name + puts "? #{name}" + false + end + + def method_missing *args + p args + end +end + +p Array(B.new) rescue p $! + + diff --git a/merlin/main/languages/ruby/Experimental/Arrays/Words.rb b/merlin/main/languages/ruby/Experimental/Arrays/Words.rb new file mode 100644 index 0000000000..b4763b5d37 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Arrays/Words.rb @@ -0,0 +1,17 @@ +$a = 4 +b = 2 + +puts %w{a b c} # ["a","b","c"] +puts "---" +puts %W{a b c} # ["a","b","c"] +puts "---" +puts %w{a #{b} c} # ['a','#{b}','c'] +puts "---" +puts %W{a #{b + 1} c} # ['a','3','c'] +puts "---" +puts %W{$a #{b + 1} c} # ['$a','3','c'] +puts "---" +puts %W{#$a #{b + 1} c} # ['4','3','c'] +puts "---" +puts %w{#$a #{b + 1} c} # ['#$a','#{b','+','1','c'] + diff --git a/merlin/main/languages/ruby/Experimental/Arrays/init.rb b/merlin/main/languages/ruby/Experimental/Arrays/init.rb new file mode 100644 index 0000000000..32bc6ea352 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Arrays/init.rb @@ -0,0 +1,99 @@ +require 'mock' + +class Array + def init *a, &b + initialize *a, &b + end +end + +puts '-- new ----------------------------------------' + +class C +end + +p Array.new() +p Array.new(I.new(1)) +p Array.new(A.new([1,2])) +p Array.new(AI.new([1,2], 1)) + +puts '----' + +p Array.new(I.new(5), C.new) +p Array.new(A.new([1,2]), C.new) rescue p $! + +puts '----' + +p Array.new(I.new(5)) { |x| x + 10 } +p Array.new(A.new([1,2,3])) { |x| raise } +p Array.new(AI.new([1,2,3], 10)) { |x| raise } + +puts '----' + +p Array.new(5) { break 'break result' } + +[false, true].each do |$freeze| + + puts "-- initialize (frozen = #$freeze) -------" + + def test_initialize *a, &b + array = ['o','r','i','g','i','n','a','l'] + + if $freeze + array.freeze + (r = array.init(*a, &b)) rescue p $! + else + r = array.init(*a, &b) + end + p r, array + end + + test_initialize() + test_initialize(I.new(1)) + test_initialize(A.new([1,2])) + test_initialize(AI.new([1,2], 1)) + + puts '----' + + test_initialize(I.new(5), C.new) + test_initialize(A.new([1,2]), C.new) rescue p $! + + puts '----' + + test_initialize(I.new(5)) { |x| x + 10 } + test_initialize(A.new([1,2,3])) { |x| raise } + test_initialize(AI.new([1,2,3], 10)) { |x| raise } + + puts '----' + + p test_initialize(5) { break 'break result' } + +end + +puts '-----------------------------------------------' + +class Class + alias xnew new + alias xallocate allocate + + def new *a, &b + puts 'Class#new' + p a,b + xnew *a, &b + end + + def allocate *a, &b + puts 'Class#allocate' + p a,b + xallocate *a, &b + end +end + +class MA < Array +end + +p a = MA[1,2,3] + +class Class + alias new xnew + alias allocate xallocate +end diff --git a/merlin/main/languages/ruby/Experimental/Arrays/mock.rb b/merlin/main/languages/ruby/Experimental/Arrays/mock.rb new file mode 100644 index 0000000000..72efbdecea --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Arrays/mock.rb @@ -0,0 +1,96 @@ +class I + def initialize val + @val = val + end + + def to_int + puts '!I to_int' + @val + end + + def respond_to? name + puts "?I #{name}" + super + end +end + +class S + def initialize val + @val = val + end + + def respond_to? name + puts "?S #{name}" + super + end + + def to_str + puts '!S to_str' + @val + end +end + +class A + def initialize val + @val = val + end + + def respond_to? name + puts "?A #{name}" + super + end + + def to_ary + puts '!A to_ary' + @val + end +end + +class AI + def initialize array, int + @array = array + @int = int + end + + def respond_to? name + puts "?A #{name}" + super + end + + def to_ary + puts '!A to_ary' + @array + end + + def to_int + puts '!A to_int' + @int + end +end + + +class ES + def initialize val + @val = val + end + + def respond_to? name + puts "?ES #{name}" + super + end + + def to_s + puts '!ES to_s' + @val + end +end + +class W + def write x + puts ">#{x}<" + end +end + +class MyStr < String + +end diff --git a/merlin/main/languages/ruby/Experimental/Arrays/reject.rb b/merlin/main/languages/ruby/Experimental/Arrays/reject.rb new file mode 100644 index 0000000000..cd4973963d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Arrays/reject.rb @@ -0,0 +1,37 @@ +x = [1,2] +q = x.reject! { |z| if z==1 then true; else break x end } +p x,q + +puts '--' + + +x = [1,2] +q = x.reject! { |z| + p z + if z == 1 then + x << 3 + x << 4 + x << 5 + true + else + break x; + end +} +p x,q + +x = [1,2] +q = x.reject! { |z| + if z == 1 then + x << 3 + true + end +} +p x,q + +x = [1,2] +q = x.reject! { |z| false; break 1; } +p x,q + +x = [1,2] +q = x.reject! { |z| false; } +p x,q \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Ast/ParseTree.rb b/merlin/main/languages/ruby/Experimental/Ast/ParseTree.rb new file mode 100644 index 0000000000..e3d3ef3441 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Ast/ParseTree.rb @@ -0,0 +1,89 @@ +if defined? IRONRUBY_VERSION + require 'ir_parse_tree' +else + require 'rubygems' + require 'parse_tree' +end + +class Example + def blah + return 1 + 1 + end +end + +class ParseTree + p ancestors + p instance_methods(false) + class << self + p instance_methods(false) + end + p constants +end + +pt = ParseTree.new false + +# syntax error -> seg fault +p pt.parse_tree_for_string("1+1") + +puts '-----' + +module M +end + +class C + include M + + def C.foo + + end + + define_method(:goo) { + } + + puts 'boo' +end + +C.class_eval { + def bar + end +} + +p pt.parse_tree(C) + +puts '-----' + +module X + def i_x + end +end + +class B + def i_b + end +end + +class A < B + include X + + def i_a + end +end + +# only declared methods are considered +p pt.parse_tree_for_method(A, :i_a) # [...] +p pt.parse_tree_for_method(A, :i_b) # [nil] +p pt.parse_tree_for_method(A, :i_x) # [nil] + +puts '----' + +ns = ParseTree::NODE_NAMES +p ns[ns.index(:scope)] = :foooooooo +p ns[ns.index(:defn)] = :baaaar # bug, hardcoded "defn" +p pt.parse_tree_for_method(A, :i_a) # [...] + +ParseTree::NODE_NAMES = nil +p pt.parse_tree_for_method(A, :i_a) # [...] + + + + diff --git a/merlin/main/languages/ruby/Experimental/Ast/ParseTree2.rb b/merlin/main/languages/ruby/Experimental/Ast/ParseTree2.rb new file mode 100644 index 0000000000..bba1cbdd37 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Ast/ParseTree2.rb @@ -0,0 +1,718 @@ +require 'rubygems' unless defined? IRONRUBY_VERSION +require 'parse_tree' + +pt = ParseTree.new false + +def foo +end + +puts '- 0 -' + +x0 = pt.parse_tree_for_string < 6)") +p pt.parse_tree_for_string("0.foo(1,2,3, *4)") +p pt.parse_tree_for_string("0.foo(1,2,3, 5 => 6, *4)") +p pt.parse_tree_for_string("0.foo(1,2,3, 5 => 6, *4) {}") +p pt.parse_tree_for_string("0.foo(1=>2,3=>4,5=>6)") + +puts '- 2 -' + +p pt.parse_tree_for_string("super") +p pt.parse_tree_for_string("super { }") +p pt.parse_tree_for_string("super()") +p pt.parse_tree_for_string("super() {}") # bug? +p pt.parse_tree_for_string("super(1)") +p pt.parse_tree_for_string("super(1) { }") + +puts '- 3 -' + +p pt.parse_tree_for_string("yield") +p pt.parse_tree_for_string("yield()") +p pt.parse_tree_for_string("yield(1)") # bug? +p pt.parse_tree_for_string("yield(*1)") +p pt.parse_tree_for_string("yield(1,*2)") +p pt.parse_tree_for_string("yield(2=>3)") +p pt.parse_tree_for_string("yield(2=>3,5=>6, *4)") +p pt.parse_tree_for_string("yield(1,2=>3,*4)") + +puts '- 4 -' + +p pt.parse_tree_for_string("foo") +p pt.parse_tree_for_string("foo()") +p pt.parse_tree_for_string("foo(1)") +p pt.parse_tree_for_string("foo(*1)") +p pt.parse_tree_for_string("foo(0,*1)") + +p pt.parse_tree_for_string("x.foo") +p pt.parse_tree_for_string("x.foo()") +p pt.parse_tree_for_string("x.foo(1)") + +puts '- 5 -' + +p pt.parse_tree_for_string("foo(1,&2)") + +puts '- 6 -' + +p pt.parse_tree_for_string("x = 1; x") +p pt.parse_tree_for_string("@x = 1; @x") +p pt.parse_tree_for_string("@@x = 1; @@x") +p pt.parse_tree_for_string("$x = 1; $x") +p pt.parse_tree_for_string("C = 1; C") + +puts '- 7 -' + +p pt.parse_tree_for_string("b[1]") +p pt.parse_tree_for_string("a = b[1]") + +p pt.parse_tree_for_string("x[0] = 1") +p pt.parse_tree_for_string("x[*1] = 2") +p pt.parse_tree_for_string("x[0,*1] = 2") + +p pt.parse_tree_for_string("x[y[0] = 1] = 2") +p pt.parse_tree_for_string("x[*y[2] = 3] = 4") +p pt.parse_tree_for_string("x[y[0] = 1, *y[2] = 3] = 4") + +puts '- 8 -' + +p pt.parse_tree_for_string("Q::y = 1") +p pt.parse_tree_for_string("(Q = 0)::y = 1") +p pt.parse_tree_for_string("(Q::z = 0)::y = 1") + +puts '- 9 -' + +puts '> a' +p pt.parse_tree_for_string("foo() { || }") +p pt.parse_tree_for_string("foo() { |x| }") +p pt.parse_tree_for_string("foo() { |*| }") +p pt.parse_tree_for_string("foo() { |*x| }") +p pt.parse_tree_for_string("foo() { |x,*| }") + +p pt.parse_tree_for_string("foo() { |x,| }") +p pt.parse_tree_for_string("foo() { |(x,)| }") + +puts '> b' +p pt.parse_tree_for_string("foo() { |x,y| }") +p pt.parse_tree_for_string("foo() { |(x,y,)| }") + +puts '> c' +p pt.parse_tree_for_string("foo() { |(x,y),| }") +p pt.parse_tree_for_string("foo() { |(x,y,),| }") + +puts '> d' +p pt.parse_tree_for_string("foo() { |(x,),| }") + +puts '> e' +p pt.parse_tree_for_string("foo() { |x,*| }") +p pt.parse_tree_for_string("foo() { |x,*y| }") + +puts '- 10 -' + +puts '> a' +p pt.parse_tree_for_string("* = 1") +p pt.parse_tree_for_string("*x = 1") +p pt.parse_tree_for_string("x,* = 1") + +p pt.parse_tree_for_string("x, = 1") +p pt.parse_tree_for_string("(x,) = 1") + +puts '> b' +p pt.parse_tree_for_string("x,y = 1") +p pt.parse_tree_for_string("(x,y,) = 1") + +puts '> c' +p pt.parse_tree_for_string("(x,y), = 1") +p pt.parse_tree_for_string("(x,y,), = 1") + +puts '> d' +p pt.parse_tree_for_string("(x,), = 1") + +puts '> e' +p pt.parse_tree_for_string("x,* = 1") +p pt.parse_tree_for_string("x,*y = 1") + + +puts '- 11 -' +p pt.parse_tree_for_string("a = 1; (x[b = a], x[b,b = a,a]) = 2") +p pt.parse_tree_for_string("Q::x,y[0],z[*0] = 1") +p pt.parse_tree_for_string("foo() { |Q::x,y[0],z[*0]| }") + +puts '- 12 -' + +p pt.parse_tree_for_string("a = *x") +p pt.parse_tree_for_string("a = x,y") +p pt.parse_tree_for_string("a = x,*y") + +puts '- 13 -' + +puts '> a' + +p pt.parse_tree_for_string("* = x") +p pt.parse_tree_for_string("* = *x") +p pt.parse_tree_for_string("* = x,*y") +p pt.parse_tree_for_string("* = x,y") + +puts '> b' + +p pt.parse_tree_for_string("*a = x") +p pt.parse_tree_for_string("*a = *x") +p pt.parse_tree_for_string("*a = x,*y") +p pt.parse_tree_for_string("*a = x,y") + +puts '> c' +p pt.parse_tree_for_string("a,b = x") +p pt.parse_tree_for_string("a,*b = x") +p pt.parse_tree_for_string("a,b,c = x") +p pt.parse_tree_for_string("a,b,*c = x") + +puts '> d' +p pt.parse_tree_for_string("a,b = *x") + +puts '> e' +p pt.parse_tree_for_string("a,b = x,y") +p pt.parse_tree_for_string("a,b = x,*y") +p pt.parse_tree_for_string("a,b = x,y,z") +p pt.parse_tree_for_string("a,b = x,y,*z") + +puts '- 14 -' +p pt.parse_tree_for_string("x &&= y") +p pt.parse_tree_for_string("x ||= y") +p pt.parse_tree_for_string("x += y") + +puts '- 15 -' +p pt.parse_tree_for_string("break") +p pt.parse_tree_for_string("break 1") +p pt.parse_tree_for_string("break 1=>2") +p pt.parse_tree_for_string("break 1,2,3") +p pt.parse_tree_for_string("break 1,2,3, 4=>5, *6") + +puts '- 16 -' +p pt.parse_tree_for_string("next") +p pt.parse_tree_for_string("next 1") +p pt.parse_tree_for_string("next 1=>2") +p pt.parse_tree_for_string("next 1,2,3") +p pt.parse_tree_for_string("next 1,2,3, 4=>5, *6") + +puts '- 17 -' +p pt.parse_tree_for_string("return") +p pt.parse_tree_for_string("return 1") +p pt.parse_tree_for_string("return 1=>2") +p pt.parse_tree_for_string("return 1,2,3") +p pt.parse_tree_for_string("return 1,2,3, 4=>5, *6") + +puts '- 18 -' +p pt.parse_tree_for_string("retry") +p pt.parse_tree_for_string("redo") + +puts '- 19 -' +p pt.parse_tree_for_string("not x") +p pt.parse_tree_for_string("1 and 2") +p pt.parse_tree_for_string("1 or 2") + +p pt.parse_tree_for_string("1 && 2") +p pt.parse_tree_for_string("1 || 2") + + +puts '- 20 -' +p pt.parse_tree_for_string("self") + +p pt.parse_tree_for_string("alias foo bar") +p pt.parse_tree_for_string("alias :foo :bar") +p pt.parse_tree_for_string("alias $x $y") + + +p pt.parse_tree_for_string("undef :a1") +p pt.parse_tree_for_string("undef :a1, :a2") +p pt.parse_tree_for_string("undef :a1, :a2, :a3") + +puts '- 21 -' + +puts '> a' +p pt.parse_tree_for_string("nil") +p pt.parse_tree_for_string("true") +p pt.parse_tree_for_string("false") +p pt.parse_tree_for_string("1.0") +p pt.parse_tree_for_string(":'foo'") +p pt.parse_tree_for_string('`x`') +p pt.parse_tree_for_string("'x'") + +puts '> b' +p pt.parse_tree_for_string("1..2") +p pt.parse_tree_for_string("1...2") +p pt.parse_tree_for_string("10000000000000000000000000000000..2000000000000000000000000000000") +p pt.parse_tree_for_string("1.0..2.3") +p pt.parse_tree_for_string("false..nil") +p pt.parse_tree_for_string("'x'..'y'") +p pt.parse_tree_for_string("`x`..`y`") + +puts '> c' +p pt.parse_tree_for_string("1..:b") +p pt.parse_tree_for_string("1..foo") +p pt.parse_tree_for_string("1..{}") +p pt.parse_tree_for_string("1...{}") + +puts '> d' +p pt.parse_tree_for_string("{}") +p pt.parse_tree_for_string("{1,2}") +p pt.parse_tree_for_string("{1=>2}") +p pt.parse_tree_for_string("[]") +p pt.parse_tree_for_string("[1,2]") +p pt.parse_tree_for_string("[1=>2]") + +puts '- 22 -' + +puts '> a' +p pt.parse_tree_for_string('"#{$x}"') +p pt.parse_tree_for_string('"a#{$x}"') +p pt.parse_tree_for_string('"a#{$x}b"') +p pt.parse_tree_for_string('"a#{$x}b#{$y}"') + +puts '> b' +p pt.parse_tree_for_string('"#{}"') +p pt.parse_tree_for_string('"#{x}"') +p pt.parse_tree_for_string('"#{x;y}"') +p pt.parse_tree_for_string('"#{x;y;z}"') + +puts '- 23 -' + +p pt.parse_tree_for_string(':"#{$x}"') +p pt.parse_tree_for_string(':"a#{$x}"') +p pt.parse_tree_for_string(':"a#{$x}b"') +p pt.parse_tree_for_string(':"a#{$x}b#{$y}"') + +puts '- 24 -' + +p pt.parse_tree_for_string('`#{$x}`') +p pt.parse_tree_for_string('`a#{$x}`') +p pt.parse_tree_for_string('`a#{$x}b`') +p pt.parse_tree_for_string('`a#{$x}b#{$y}`') + +puts '- 25 -' + +p pt.parse_tree_for_string('/#{$x}/i') +p pt.parse_tree_for_string('/a#{$x}/i') +p pt.parse_tree_for_string('/a#{$x}b/') +p pt.parse_tree_for_string('/a#{$x}b#{$y}/') + +p pt.parse_tree_for_string('//mixsnue') +p pt.parse_tree_for_string('/a#{"#{$x}"}b#{`#{$y}`}/') + + +puts '- 24 -' + +p pt.parse_tree_for_string('$&'); +p pt.parse_tree_for_string('$~'); +p pt.parse_tree_for_string('$+'); +p pt.parse_tree_for_string('$`'); +p pt.parse_tree_for_string('$\''); +p pt.parse_tree_for_string('$0'); +p pt.parse_tree_for_string('$1'); +p pt.parse_tree_for_string('$2'); + +puts '- 25 -' + +p pt.parse_tree_for_string("defined? C") +p pt.parse_tree_for_string("defined? foo") +p pt.parse_tree_for_string("defined? @x") +p pt.parse_tree_for_string("defined? @@x") +p pt.parse_tree_for_string("defined? $x") +p pt.parse_tree_for_string("defined? 1") +p pt.parse_tree_for_string("defined? 1+1") + +puts '- 26 -' + +p pt.parse_tree_for_string("()") +p pt.parse_tree_for_string("(a)") +p pt.parse_tree_for_string("(a;b)") + +puts '- 27 -' + +p pt.parse_tree_for_string("begin; end") +p pt.parse_tree_for_string("begin; a; end") +p pt.parse_tree_for_string("begin; a; b; end") + +puts '- 28 -' + +p pt.parse_tree_for_string("begin; ensure; end") +p pt.parse_tree_for_string("begin; else; end") +p pt.parse_tree_for_string("begin; rescue; end") +p pt.parse_tree_for_string("begin; rescue; else; end") +p pt.parse_tree_for_string("begin; rescue; else; ensure; end") +p pt.parse_tree_for_string("begin; else; ensure; end") + +puts '- 29 -' + +p pt.parse_tree_for_string("f(begin; a; b; end)") + +puts '- 30 -' + +p pt.parse_tree_for_string("x = ()") +p pt.parse_tree_for_string("x = (a)") +p pt.parse_tree_for_string("x = (a;b)") + +puts '- 31 -' + +p pt.parse_tree_for_string("x = begin; end") +p pt.parse_tree_for_string("x = begin; a; end") +p pt.parse_tree_for_string("x = begin; a; b; end") + +puts '- 32 -' + +p pt.parse_tree_for_string("x = begin; ensure; end") +p pt.parse_tree_for_string("x = begin; else; end") +p pt.parse_tree_for_string("x = begin; rescue; end") +p pt.parse_tree_for_string("x = begin; rescue; else; end") +p pt.parse_tree_for_string("x = begin; rescue; else; ensure; end") +p pt.parse_tree_for_string("x = begin; else; ensure; end") + +puts '- 33 -' + +x = pt.parse_tree_for_string < e + 3 + rescue A,B => e + 4 + rescue A + 5 + rescue A,B + 6 + else + 7 + ensure + 8 + end +END + +p x + +x = pt.parse_tree_for_string < a' +p pt.parse_tree_for_string("x while c") +p pt.parse_tree_for_string("x until c") + +puts '> b' +p pt.parse_tree_for_string("(x;y) while c") + +puts '> c' +p pt.parse_tree_for_string("begin; end while c") +p pt.parse_tree_for_string("begin; x; end while c") +p pt.parse_tree_for_string("begin; x;y; end while c") + +puts '> d' +p pt.parse_tree_for_string("while c do end") +p pt.parse_tree_for_string("while c do x; end") +p pt.parse_tree_for_string("while c do x;y; end") + +puts '> e' +p pt.parse_tree_for_string("until c do end") +p pt.parse_tree_for_string("until c do x; end") +p pt.parse_tree_for_string("until c do x;y; end") + +puts '- 39 -' + +p pt.parse_tree_for_string("for x in a do; end") +p pt.parse_tree_for_string("for x in a do; s1; end") +p pt.parse_tree_for_string("for x in a do; s1; s2; end") + +p pt.parse_tree_for_string("for * in a do; end") +p pt.parse_tree_for_string("for *x in a do; end") +p pt.parse_tree_for_string("for x,*y in a do; end") +p pt.parse_tree_for_string("for x,y in a do; s1; s2; end") + +puts '- 40 -' + +p pt.parse_tree_for_string(" +case v + when ca1,ca2: a + when cb: b +else + e +end +") + +p pt.parse_tree_for_string(" +case v + when ca: +end +") + +p pt.parse_tree_for_string(" +case v + when ca: a +end +") + +p pt.parse_tree_for_string(" +case v + when ca: a;b +else +end +") + +p pt.parse_tree_for_string(" +case v + when ca: +else + e1 + e2 +end +") + +p pt.parse_tree_for_string(" +case + when *x: a +end +") + +puts '- 41 -' + +p pt.parse_tree_for_string("::A") +p pt.parse_tree_for_string("::A::B") +p pt.parse_tree_for_string("A::B") +p pt.parse_tree_for_string("A::foo()::B::C") + +puts '- 42 -' + +if defined? IRONRUBY_VERSION + p pt.parse_tree_for_string("A::foo()::B::C = 1") + p pt.parse_tree_for_string("(::A,x) = 1,2") + p pt.parse_tree_for_string("::A = 1") + p pt.parse_tree_for_string("A::B = 1") +else + puts 'SEGFAULT' + puts 'SEGFAULT' + puts 'SEGFAULT' + puts 'SEGFAULT' +end + +puts '- 43 -' + +p pt.parse_tree_for_string("module M;end") +p pt.parse_tree_for_string("module M;x;end") +p pt.parse_tree_for_string("module M;x;y;end") +p pt.parse_tree_for_string("module A::B::C;end") + +p pt.parse_tree_for_string("class C;end") +p pt.parse_tree_for_string("class C < D;end") +p pt.parse_tree_for_string("class << x;end") +p pt.parse_tree_for_string("module M; a; rescue; b; end") + +puts '- 44 -' + +p pt.parse_tree_for_string("def f; end") +p pt.parse_tree_for_string("def s.f; end") +p pt.parse_tree_for_string("def s.f; 1; end") +p pt.parse_tree_for_string("def C::f; 1; end") +p pt.parse_tree_for_string("def f; a; end") +p pt.parse_tree_for_string("def f; a; rescue; b; end") + +p pt.parse_tree_for_string("def f(); 1; end") +p pt.parse_tree_for_string("def f(a); 1; end") +p pt.parse_tree_for_string("def f(a,b); 1; end") +p pt.parse_tree_for_string("def f(a,*b); 1; end") +p pt.parse_tree_for_string("def f(a,*b,&p); 1; end") +p pt.parse_tree_for_string("def f(x,y=2,z=3,*a,&b); 1; end") + +puts '- 45 -' + +puts '> a' +p pt.parse_tree_for_string("1 if 1 .. 1") +p pt.parse_tree_for_string("x if f(x .. x)") + +puts '> b' +p pt.parse_tree_for_string("1 if 1 .. $x") +p pt.parse_tree_for_string("1 if 1 .. C") +p pt.parse_tree_for_string("1 if 1 .. x") +p pt.parse_tree_for_string("x if 1 .. 100000000000000000000000000") +p pt.parse_tree_for_string("x if 1 .. /xx/") +p pt.parse_tree_for_string("x if 1 .. {}") +p pt.parse_tree_for_string("x if 1 .. []") +p pt.parse_tree_for_string("x if 1 .. 'y'") +p pt.parse_tree_for_string("x if 1 .. :y") +p pt.parse_tree_for_string("x if 1 .. 1.2") +p pt.parse_tree_for_string("x if 1 .. true") +p pt.parse_tree_for_string("x if 1 .. false") +p pt.parse_tree_for_string("x if 1 .. nil") + +puts '> c' +p pt.parse_tree_for_string("x unless a ... b") +p pt.parse_tree_for_string("x while a ... b") +p pt.parse_tree_for_string("x until a ... b") +p pt.parse_tree_for_string("if a ... b; end") +p pt.parse_tree_for_string("unless a ... b; end") +p pt.parse_tree_for_string("while a ... b; end") +p pt.parse_tree_for_string("until a ... b; end") +p pt.parse_tree_for_string("a ... b ? 1 : 2") + +puts '> d' +p pt.parse_tree_for_string("(1..2) .. b ? 1 : 2") +p pt.parse_tree_for_string("if ((x..y) ... b); end") +p pt.parse_tree_for_string("if ((1..2) ... b); end") +p pt.parse_tree_for_string("if begin a ... b end; end") +p pt.parse_tree_for_string("(0;1;TRUE..FALSE) ? 1 : 2") +p pt.parse_tree_for_string("begin 0;1;TRUE..FALSE end ? 1 : 2") + +puts '> e' +p pt.parse_tree_for_string("if (1;2;begin a ... b end); end") +p pt.parse_tree_for_string("if begin a ... b; rescue; end; end") +p pt.parse_tree_for_string("(1;begin 0;1;TRUE..FALSE end) ? 1 : 2") +p pt.parse_tree_for_string("(1;begin 0;1;TRUE..FALSE; rescue; end) ? 1 : 2") +p pt.parse_tree_for_string("if begin a ... b; rescue; end; end") +p pt.parse_tree_for_string("if (1;2;begin a ... b end); end") + +puts '- 46 -' + +p pt.parse_tree_for_string("/foo/.=~('foo')") +p pt.parse_tree_for_string('/foo/ =~ "foo"') +p pt.parse_tree_for_string('/fo#{x}o/ =~ "foo"') +p pt.parse_tree_for_string('/foo/ !~ "foo"') +p pt.parse_tree_for_string('/fo#{x}o/ !~ "foo"') +p pt.parse_tree_for_string("1 if /foo/") +p pt.parse_tree_for_string('1 if /fo#{x}o/') + +puts '- 47 -' + +p pt.parse_tree_for_string("1 if /a/ and /b/") +p pt.parse_tree_for_string("1 if /a/ or /b/") +p pt.parse_tree_for_string("1 if a..b and c..d or e..f") +p pt.parse_tree_for_string("1 if not (a..b)") + +puts '- 48 -' + +class C + def foo + 1 + end + + def self.goo + 2 + end + + class << self + alias agoo goo + end + + alias afoo foo +end + +p pt.parse_tree_for_meth(C, "afoo", false) +p pt.parse_tree_for_meth(C, "agoo", true) + +puts '- 49 -' + +class C + def f + end + + define_method(:b) { } + + alias af f + alias ab b + + define_method(:db, instance_method(:b)) + define_method(:ddb, instance_method(:db)) + define_method(:dab, instance_method(:ab)) + + define_method(:df, instance_method(:f)) + define_method(:ddf, instance_method(:df)) + + alias adf df + alias addf ddf + + define_method(:daf, instance_method(:af)) + define_method(:dadf, instance_method(:adf)) +end + +pt = ParseTree.new false + +C.instance_methods(false).each { |m| + p pt.parse_tree_for_method(C, m) +} + diff --git a/merlin/main/languages/ruby/Experimental/BuiltinsAll.txt b/merlin/main/languages/ruby/Experimental/BuiltinsAll.txt new file mode 100644 index 0000000000..9be08baf0a Binary files /dev/null and b/merlin/main/languages/ruby/Experimental/BuiltinsAll.txt differ diff --git a/merlin/main/languages/ruby/Experimental/CF/Blocks/Locals.rb b/merlin/main/languages/ruby/Experimental/CF/Blocks/Locals.rb new file mode 100644 index 0000000000..c06d7018c9 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Blocks/Locals.rb @@ -0,0 +1,62 @@ +# Ruby 1.9 breaking changes + +# Locals defined in a block are not visible outside the block. +def foo1 + a = 1 + b = 2 + y = 3 + 1.times { |a,b,x| # b,x are fresh locals here with the scope of the block; outer a is shadowed + puts '|a,b,x|' + puts "inside" + puts "a = #{a}" + puts "b = #{b}" + puts "x = #{x}" + puts "y = #{y}" + puts + + z = 4 + x = 5 + b = 6 + } + + puts "after" + puts "a = #{a}" + puts "b = #{b}" + puts "x = #{x}" rescue puts "ERROR(x)" + puts "y = #{y}" + puts "z = #{z}" rescue puts "ERROR(z)" + puts +end + +foo1 + +# only locals can be used in block parameters +class C + def errors + begin + eval('1.times { |$x| }') + rescue Exception + puts $!.message + end + + begin + eval('1.times { |X| }') + rescue Exception + puts $!.message + end + + begin + eval('1.times { |@x| }') + rescue Exception + puts $!.message + end + + begin + eval('1.times { |@@x| }') + rescue Exception + puts $!.message + end + end +end + +C.new.errors diff --git a/merlin/main/languages/ruby/Experimental/CF/Blocks/Locals19.rb b/merlin/main/languages/ruby/Experimental/CF/Blocks/Locals19.rb new file mode 100644 index 0000000000..700712093a --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Blocks/Locals19.rb @@ -0,0 +1,56 @@ +# Observations: +# - block defines a lexical scope +# - variables defined in the block are not visible outside the block +# - variables defined before the block are visible in the block unless they are stated after ; in block params +# - semicolon is similar to comma +# - comma hides outside variables because it represents a local variable assignment +# - the variables following the semicolon are not formal parameters though + +def foo2 + puts '|a;x|' + x = 1 + 1.times { |a;x| + puts "in-x = #{x}" + x = 2 + } + + puts "x = #{x}" + puts +end + +def foo3 + puts '|a,x|' + x = 1 + 1.times { |a,x| + puts "in-x = #{x}" + x = 2 + } + + puts "x = #{x}" + puts +end + +def foo4 + puts '|a|' + x = 1 + 1.times { |a| + puts "in-x = #{x}" + x = 2 + } + + puts "x = #{x}" + puts +end + +foo2 +foo3 +foo4 + +puts '---' + +def goo + yield 1,2 +end + +goo { |x,y| p x,y} +goo { |x;y| p x,y} diff --git a/merlin/main/languages/ruby/Experimental/CF/Blocks/Proc19.rb b/merlin/main/languages/ruby/Experimental/CF/Blocks/Proc19.rb new file mode 100644 index 0000000000..01e944fd81 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Blocks/Proc19.rb @@ -0,0 +1,26 @@ +def foo &q + p = lambda { + puts 'P' + } + + q[1, &p] + q.(1){ puts 'Q' } + + # syntax error: yield(1,&p) + + q.yield(1,&p) + q.yield(1) { puts 'R' } +end + +foo { |a,&q;x,y| + p a + yield rescue puts $! # no block given + q[] + q.() + q.yield +} + +puts '---' + +z = ->(a, b = 5) { puts a + b } +5.times &z diff --git a/merlin/main/languages/ruby/Experimental/CF/Break/PrimaryFrame.rb b/merlin/main/languages/ruby/Experimental/CF/Break/PrimaryFrame.rb new file mode 100644 index 0000000000..d8830de1ce --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Break/PrimaryFrame.rb @@ -0,0 +1,8 @@ +def foo + break +rescue + puts "E: #{$!}" +end + +foo +foo { } \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/Break/RecursiveBreak.rb b/merlin/main/languages/ruby/Experimental/CF/Break/RecursiveBreak.rb new file mode 100644 index 0000000000..8599c680b8 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Break/RecursiveBreak.rb @@ -0,0 +1,93 @@ +def f1 + puts 'foo-begin' + f2 { puts 'break'; break } # break jumps after this statement + puts 'foo-end' +end + +def f2 &p + puts 'bar-begin' + f3 &p + puts 'bar-end' +end + +def f3 + puts 'baz-begin' + yield # unwinds to f1 + puts 'baz-end' +end + +f1 + +puts '------------' +# not all call-sites need to pass block thru +# proc.call also works, not only yield + +def h1 + puts 'foo-begin' + h2 { puts 'break'; break } # break jumps after this statement + puts 'foo-end' +end + +def h2 &p + puts 'bar-begin' + $p = p + h3 + puts 'bar-end' +end + +def h3 + puts 'baz-begin' + $p[] # unwinds to h1 + puts 'baz-end' +end + +h1 + +puts '------------' +# owner of proc != owner of block +# + +def defp &p + $p = p + g2 &p # this is ok +end + +def g1 + puts 'foo-begin' + #$p = lambda do # the same error result if the following line is replaced by lambda call + defp do + begin + puts 'break' # break from proc-closure error + break + rescue LocalJumpError + puts 'ERROR 1' + end + end + puts 'foo-mid' + + g2 &$p # this raises an error #3 + puts 'foo-end' +rescue LocalJumpError + puts 'ERROR 4' +end + +def g2 &p + puts 'bar-begin' + g3 &p + puts 'bar-end' +rescue LocalJumpError + puts 'ERROR 2' +end + +def g3 &p + puts 'baz-begin' + 1.times &p # break returns here, we need to decide now whether we can unwind or not + puts 'baz-end' +rescue LocalJumpError => e + puts 'ERROR 3' # rescued here + puts e + puts e.backtrace +end + +g1 + diff --git a/merlin/main/languages/ruby/Experimental/CF/Break/RecursiveBreak2.rb b/merlin/main/languages/ruby/Experimental/CF/Break/RecursiveBreak2.rb new file mode 100644 index 0000000000..9ee24516ac --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Break/RecursiveBreak2.rb @@ -0,0 +1,13 @@ +def foo &p + puts 'foo1' + 3.times &p + puts 'foo2' +end + +def goo + puts 'goo1' + foo { |x| puts x; break } + puts 'goo2' +end + +goo \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/EH/eh.rb b/merlin/main/languages/ruby/Experimental/CF/EH/eh.rb new file mode 100644 index 0000000000..ae50af0b65 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/EH/eh.rb @@ -0,0 +1,442 @@ +def dumpex e + # this will modify the class name as well in MRI (bug) + #if e.message.length > 0 then + # e.message[0] = 'X' + #end + + puts "inspect: #{e.inspect.inspect}" + puts "class: #{e.class.inspect}" + puts "message: #{e.message.inspect}" + puts "to_s: #{e.to_s.inspect}" + puts +end + +def test0 + puts '> IOError.new("foo")' + dumpex(IOError.new("foo")) + + puts '> IOError.new' + dumpex(IOError.new) + + puts '> IOError.new(nil)' + dumpex(IOError.new(nil)) + + puts '> IOError.new("")' + dumpex(IOError.new("")) + + puts '> Exception.new' + dumpex(Exception.new) + puts +end + +def test1 + $! = IOError.new "1" + test1_helper + puts "E: #{$!.inspect}" # 7 +end + +def test1_helper + begin + $! = IOError.new "2" + begin # t1 = 2 (have rescue) + $! = IOError.new "3" + begin + begin + raise IOError.new("4") + ensure # t2 = 4 + puts "A: #{$!.inspect}" # 4 + $! = IOError.new "8" + end # $! = t2 + ensure # t3 = $! + puts "G: #{$!.inspect}" # 4 + $! = IOError.new "10" + end # $! = t3 + rescue # $! = 4 + puts "B: #{$!.inspect}" # 4 + $! = IOError.new "5" + ensure # $! = t4 = 2 + puts "C: #{$!.inspect}" # 2 + $! = IOError.new "6" + end # $! = t4 + puts "F: #{$!.inspect}" # 2 + $! = IOError.new "8" + ensure # t5 = 8 + puts "D: #{$!.inspect}" # 8 + $! = IOError.new "7" + return + end # $! = t5 (skipped by return) +end + +def test2 + $! = IOError.new "1" + begin + $! = IOError.new "2" + begin + $! = IOError.new "3" + puts $!.inspect + end + puts $!.inspect + end + puts $!.inspect +end + +def test3 + puts $! + $! = IOError.new "1" + begin + puts $! + $! = IOError.new "2" + begin + puts $! + $! = IOError.new "3" + puts $! + ensure + puts $! + end + puts $! + ensure + puts $! + end + puts $! +end + +def test4 + puts "A:#{$!.inspect}" + $! = IOError.new "1" + begin + puts "B:#{$!.inspect}" + $! = IOError.new "2" + begin + puts "C:#{$!.inspect}" + $! = IOError.new "3" + puts "D:#{$!.inspect}" + rescue + ensure + puts "E:#{$!.inspect}" + end + puts "F:#{$!.inspect}" + rescue + puts "G:#{$!.inspect}" + end + puts "H:#{$!.inspect}" +end + +def test5 + g + puts "H:#{$!.inspect}" +end + +def g + f { return } +end + +def f + $! = IOError.new "1" + puts "A:#{$!.inspect}" + while (true) do + begin + puts "C:#{$!.inspect}" + $! = IOError.new "3" + puts "D:#{$!.inspect}" + rescue + ensure + puts "G:#{$!.inspect}" + yield + end + end + puts "H:#{$!.inspect}" +end + +def test6 + r = true + $! = IOError.new "1" + puts "A:#{$!.inspect}" + begin + puts "C:#{$!.inspect}" + $! = IOError.new "3" + puts "D:#{$!.inspect}" + raise + rescue + puts "E:#{$!.inspect}" + if r then + r = false + puts 'retry' + retry + end + else + puts "ELSE:#{$!.inspect}" + ensure + puts "G:#{$!.inspect}" + end + puts "H:#{$!.inspect}" +end + +def test7 + begin + begin + raise IOError.new("1") + ensure + puts $!.inspect + end + rescue + puts $!.inspect + end +end + +def test8 + $! = IOError.new "1" + begin + puts $!.inspect + $! = IOError.new "2" + puts $!.inspect + else + puts "X", $!.inspect + end + puts $!.inspect +end + +def test9 + $! = IOError.new "1" + begin + puts $!.inspect #1 + $! = IOError.new "2" + puts $!.inspect #2 + rescue + else + puts $!.inspect #1 + end + puts $!.inspect #1 +end + +def test10 + $! = IOError.new "1" + begin + rescue + else + puts $!.inspect + end + puts $!.inspect +end + +def test11 + $! = IOError.new "1" + begin + rescue + ensure + puts $!.inspect + end + puts $!.inspect +end + +def test12 + $! = IOError.new "1" + begin + rescue + else + puts $!.inspect + ensure + puts $!.inspect + end + puts $!.inspect +end + +def test13 + test13_helper +rescue +end + +def test13_helper + begin + $! = IOError.new "1" + begin + puts "A: #{$!.inspect}" + raise IOError.new("4") + ensure + puts "B: #{$!.inspect}" + $! = IOError.new "8" + end + ensure + puts "C: #{$!.inspect}" + end +end + +def test14 + $! = IOError.new "1" + test14_helper +end + +def test14_helper + begin + puts "A: #{$!.inspect}" + raise IOError.new("4") + ensure + puts "B: #{$!.inspect}" + return # exception swallowed + end +end + +def test15 + $! = IOError.new "1" + begin + begin + puts "A: #{$!.inspect}" + raise IOError.new("4") + ensure + puts "B: #{$!.inspect}" + $! = IOError.new("5") + end + rescue + puts "C: #{$!.inspect}" + end +end + +def test16 + $! = IOError.new "1" + test16_helper + puts "D: #{$!.inspect}" +end + +def test16_helper + begin + puts "A: #{$!.inspect}" + rescue + else + puts "B: #{$!.inspect}" + $! = IOError.new "2" + raise IOError.new("3") + ensure + puts "C: #{$!.inspect}" + $! = IOError.new("5") + return + end +end + +def test17 + $! = IOError.new "1" + test17_helper + puts "D: #{$!.inspect}" +end + +def test17_helper + begin + puts "A: #{$!.inspect}" + else + puts "B: #{$!.inspect}" + $! = IOError.new "2" + raise IOError.new("3") + ensure + puts "C: #{$!.inspect}" + $! = IOError.new("5") + return + end +end + +def test18 + begin + test18_helper + rescue + puts "C: #{$!.inspect}" + end +end + +def test18_helper + begin + raise IOError.new("1") + rescue (raise IOError.new("2")) + puts "A: #{$!.inspect}" + raise IOError.new("3") + end + puts "B: #{$!.inspect}" +end + +def test19 + $! = IOError.new "1" + begin + else + $! = IOError.new "2" + ensure + $! = IOError.new "3" + end + puts $!.inspect #2 +end + + +$! = nil +puts 'test0' +test0 + +$! = nil +puts 'test1' +test1 + +$! = nil +puts 'test2' +test2 + +$! = nil +puts 'test3' +test3 + +$! = nil +puts 'test4' +test4 + +$! = nil +puts 'test5' +test5 + +$! = nil +puts 'test6' +test6 + +$! = nil +puts 'test7' +test7 + +$! = nil +puts 'test8' +test8 + +$! = nil +puts 'test9' +test9 + +$! = nil +puts 'test10' +test10 + +$! = nil +puts 'test11' +test11 + +$! = nil +puts 'test12' +test12 + +$! = nil +puts 'test13' +test13 + +$! = nil +puts 'test14' +test14 + +$! = nil +puts 'test15' +test15 + +$! = nil +puts 'test16' +test16 + +$! = nil +puts 'test17' +test17 + +$! = nil +puts 'test18' +test18 + +$! = nil +puts 'test19' +test19 diff --git a/merlin/main/languages/ruby/Experimental/CF/EH/ex.rb b/merlin/main/languages/ruby/Experimental/CF/EH/ex.rb new file mode 100644 index 0000000000..244cbab467 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/EH/ex.rb @@ -0,0 +1,73 @@ +class Class + alias old_new new + def new *a,&b + puts "#{self}::new(#{a.inspect}, &#{b.inspect})" + old_new *a,&b + end +end + +class X + def respond_to? name + puts "XXXX?#{name}" + end + + def to_s + '---' + end +end + +puts "#{X.new}::xxx" + +class E < Exception + def initialize *a + puts "#{self}::initialize(#{a.inspect})" + end + + def respond_to? name + puts "?#{name}" + super + end + + def exception *a + puts "#{self}::exception(#{a.inspect})" + super + end + + def backtrace + puts "#{self}::backtrace" + ['goo'] + end + + def backtrace= value + puts "#{self}::backtrace(#{value.inspect})" + value + ['hoo'] + end +end + +class C + def respond_to? name + puts "?#{self}::#{name}" + super + end + + def exception + puts "#{self}::exception" + E.new + end +end + + +#set_trace_func proc { |*args| +# if args[0] == 'call' then +# p args +# end +#} + + +#E.exception("foo", ["x","y"]) + +begin + raise E.new, "foo", ["x","y"] +rescue Exception + p $@ +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/EH/ex2.rb b/merlin/main/languages/ruby/Experimental/CF/EH/ex2.rb new file mode 100644 index 0000000000..ce193195de --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/EH/ex2.rb @@ -0,0 +1,30 @@ +class Ary + def to_ary + ['x','y'] + end +end + +class E < Exception +end + +class C + def exception *a + puts "exception #{a.inspect}" + E.new *a + end + + def set_backtrace *a + puts "set_backtrace #{a.inspect}" + end +end + +x = C.new + +begin + raise x, "msg", ['x','y'] +rescue Exception + p $! + p $@ +end + + diff --git a/merlin/main/languages/ruby/Experimental/CF/EH/ex3.rb b/merlin/main/languages/ruby/Experimental/CF/EH/ex3.rb new file mode 100644 index 0000000000..e20030eaf6 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/EH/ex3.rb @@ -0,0 +1,27 @@ +require 'mock' + +class E < Exception + def initialize *a + puts "#{self.inspect}::initialize #{a.inspect}" + end +end + +x = E.new.exception(123) +p x.message + +x = Exception.new(nil) +p x + +puts '---' + +e = E.new 'foo' +e.set_backtrace ['a', 'b'] +f = e.exception 'bar' +p e,f,e.backtrace,f.backtrace,e.object_id == f.object_id + +puts '---' + +e = E.new +e.set_backtrace ['a', 'b'] +f = e.exception +p e,f,e.backtrace,f.backtrace,e.object_id == f.object_id \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/EH/ex4.rb b/merlin/main/languages/ruby/Experimental/CF/EH/ex4.rb new file mode 100644 index 0000000000..83a6fbddb0 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/EH/ex4.rb @@ -0,0 +1,18 @@ +require 'mock' + +def foo + bar +end + +def bar + e = SystemCallError.new(S.new('foo')) + p e.message + raise e +end + +begin + foo +rescue + p $! + p $@ +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/EH/ex5.rb b/merlin/main/languages/ruby/Experimental/CF/EH/ex5.rb new file mode 100644 index 0000000000..686c57fa4f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/EH/ex5.rb @@ -0,0 +1,49 @@ +require 'socket' + +p Exception.allocate(*[]) + +Exception.allocate("foo") rescue p $! + +p Exception.new +p Exception.new(nil) +p Exception.new(Object.new) +p Exception.new(123) +p Exception.new("foo") + +[ +Exception, +NoMemoryError, +EOFError, +FloatDomainError, +RuntimeError, +ThreadError, +SystemExit, +SignalException, +Interrupt, +LocalJumpError, +ScriptError, +NotImplementedError, +LoadError, +RegexpError, +SyntaxError, +SystemStackError, +StandardError, +ArgumentError, +IOError, +IndexError, +RangeError, +NameError, +NoMethodError, +SecurityError, +TypeError, +ZeroDivisionError, +SystemCallError +].each do |e| + puts "- #{e} ----------------------" + + p e.new.message rescue p $! + p e.new(nil).message rescue p $! + p e.new("foo").message rescue p $! + p e.new(Object.new).message rescue p $! +end + diff --git a/merlin/main/languages/ruby/Experimental/CF/EH/init.rb b/merlin/main/languages/ruby/Experimental/CF/EH/init.rb new file mode 100644 index 0000000000..e488588424 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/EH/init.rb @@ -0,0 +1,64 @@ +# TODO: X.new nil doesn't work for Ruby exceptions + +[ +Exception, +Errno::EDOM, +Errno::EINVAL, +Errno::ENOENT, +Errno::ENOTDIR, +Errno::EACCES, +Errno::EEXIST, +NoMemoryError, +ScriptError, +LoadError, +NotImplementedError, +SyntaxError, +# not supported: SignalException, +# not supported: Interrupt, +StandardError, +ArgumentError, +IOError, +EOFError, +IndexError, +LocalJumpError, +NameError, +NoMethodError, +RangeError, +FloatDomainError, +RegexpError, +RuntimeError, +SecurityError, +SystemCallError, +ThreadError, +TypeError, +ZeroDivisionError, +SystemExit, +SystemStackError, +].each { |c| + puts c.name + begin + x = c.new + rescue + puts "None: Init Error: #{$!}" + else + puts "None: #{x.message}" + end + + begin + x = c.new nil + rescue + puts "Nil: Init Error: #{$!}" + else + puts "Nil: #{x.message}" + end + + begin + x = c.new "foo" + rescue + puts "One: Init Error: #{$!}" + else + puts "One: #{x.message}" + end + + puts +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/EH/init2.rb b/merlin/main/languages/ruby/Experimental/CF/EH/init2.rb new file mode 100644 index 0000000000..7aca29d675 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/EH/init2.rb @@ -0,0 +1,12 @@ +begin + raise IOError.new "foo" +rescue + puts $!.backtrace.inspect + puts $!.message.inspect + + puts 'initialize' + $!.send :initialize + + puts $!.backtrace.inspect + puts $!.message.inspect +end diff --git a/merlin/main/languages/ruby/Experimental/CF/EH/stack_traces.rb b/merlin/main/languages/ruby/Experimental/CF/EH/stack_traces.rb new file mode 100644 index 0000000000..0ec778f17e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/EH/stack_traces.rb @@ -0,0 +1,53 @@ +#require 'y.rb' + +puts '1' +puts '2' +puts '3' + +class C + def initialize + puts caller(0) + + puts '====' + + raise + end +end + +def foo + send :bar +end + +def bar + goo +end + +def goo + 1.times { + C.new + } +end + +def catcher + foo +rescue => e + puts $@ + puts '---' + raise e +end + +def catcher_caller + catcher +end + +begin + catcher_caller +rescue + puts $@ + puts '---' +end + + + + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/EH/throw.rb b/merlin/main/languages/ruby/Experimental/CF/EH/throw.rb new file mode 100644 index 0000000000..303ad1f396 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/EH/throw.rb @@ -0,0 +1,13 @@ +def foo + throw :foo +ensure + puts "A:", $!.inspect +end + +catch :foo do + begin + foo + rescue Exception + puts "B:", $!.inspect + end +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/Libs/Initializers.rb b/merlin/main/languages/ruby/Experimental/CF/Libs/Initializers.rb new file mode 100644 index 0000000000..2853ee40b2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Libs/Initializers.rb @@ -0,0 +1,50 @@ +class A + def initialize *a + puts "initialize: #{self.object_id}, #{a.inspect}" + yield + 100 + end + + def init *a, &p + initialize *a, &p + end +end + +x = A.new { + break 'foo' +} + +puts x + +x = A.new { + break +} + +puts x + +x = A.new { +} + +puts x + +y = x.init { + break 'bar' +} +puts y + +y = x.init { + break +} +puts y + +y = x.init { +} +puts y + + +$i = 0 +y = A.new($i += 1) { + if $i < 5 then + retry + end +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/Libs/StoreAndCallPattern.rb b/merlin/main/languages/ruby/Experimental/CF/Libs/StoreAndCallPattern.rb new file mode 100644 index 0000000000..af0803f7da --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Libs/StoreAndCallPattern.rb @@ -0,0 +1,12 @@ +h = Hash.new { + puts 'foo' + break 'zoo' + 'bar' +} + + +puts h.default_proc +puts h.default + +puts '---' +puts h['bob'] diff --git a/merlin/main/languages/ruby/Experimental/CF/Loops/RetryForLoop.rb b/merlin/main/languages/ruby/Experimental/CF/Loops/RetryForLoop.rb new file mode 100644 index 0000000000..e9b22a3922 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Loops/RetryForLoop.rb @@ -0,0 +1,15 @@ +def foo x + puts "foo(#{x})" + x * ($i + 1) +end + +$i = 0 + +for i in [foo(1), foo(2), foo(3)] do + puts "i = #{i}" + + if $i == 0 then + $i = 1 + retry + end +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/Master.rb b/merlin/main/languages/ruby/Experimental/CF/Master.rb new file mode 100644 index 0000000000..24cbb571bc --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Master.rb @@ -0,0 +1,238 @@ +=begin + Comprehensive test of break, next, redo and return statements semantics. + Doesn't test all interactions with exception handling though. +=end + +def make_block name + %{ + puts '#{name}.begin' + begin + if $statement == :next then + puts $statement + $state = true + next "#{name}" + elsif $statement == :break then + puts $statement + $state = true + break "#{name}" + elsif $statement == :return then + puts $statement + $state = true + return "#{name}" + else + if not $state then + puts $statement + $state = true + redo + end + end + rescue + # why the $! is nil here? + + puts "#{name}.ERROR: #{$!.inspect}" + + ($statement == :return ? " (unexpected return)" : " unreachable") + end + } +end + +def run + puts 'run.begin' + + test :break + test :next + test :redo + test :return + + puts 'run.end' +end + +def test statement + $statement = statement + + puts 'test.begin' + + f :call, :lambda, :owner => :active + f :yield, :lambda, :owner => :active + f :call, :lambda, :owner => :inactive + f :yield, :lambda, :owner => :inactive + + f :call, :proc, :owner => :active, :converter => :active + f :yield, :proc, :owner => :active, :converter => :active + f :call, :proc, :owner => :inactive, :converter => :active + f :yield, :proc, :owner => :inactive, :converter => :active + f :call, :proc, :owner => :active, :converter => :inactive + f :yield, :proc, :owner => :active, :converter => :inactive + f :call, :proc, :owner => :inactive, :converter => :inactive + f :yield, :proc, :owner => :inactive, :converter => :inactive + + puts '-'*55 + puts 'test.end' +ensure + puts 'test.finally' +end + +def f action, kind, frames + $action = action + $kind = kind + $owner = frames[:owner] + $converter = ($kind == :proc) ? frames[:converter] : :inactive + + puts '-'*55, "#{$statement}: #{$action} #{$kind}, owner:#{$owner}, converter:#{$converter}", '-'*55 + + puts 'f.begin' + + r = f0 + puts "result = '#{r}'" + + puts 'f.end' + state_msg +ensure + puts 'f.finally' +end + +def define_procs + if $kind == :lambda then + $proc = lambda do |*a| # owner inactive + puts "args = #{a.inspect}" + a = a + [1] + eval make_block('lambda') + end + else + $proc = Proc.new do |*a| # owner inactive + puts "args = #{a.inspect}" + a = a + [1] + eval make_block('proc') + end + end +end + +def f0 + print 'f0.begin' + + if $owner == :active then + if $converter == :inactive then + puts " <-- owner" + if $kind == :lambda then + $proc = lambda do |*a| # converter (Kernel#lambda) inactive + puts "args = #{a.inspect}" + a = a + [1] + eval make_block('lambda1') + end + else + $proc = Proc.new do |*a| # converter (Proc#new) inactive + puts "args = #{a.inspect}" + a = a + [1] + eval make_block('proc1') + end + end + else + $proc = nil # to be created later + end + else + puts + define_procs + end + + f1 + + puts 'f0.end' + state_msg +ensure + puts 'f0.finally' +end + +def f1 + puts 'f1.begin' + f2 + puts 'f1.end' + state_msg +ensure + puts 'f1.finally' +end + +def f2 + puts 'f2.begin' + r = f3 &$proc + puts "result = '#{r}'" + puts 'f2.end' + state_msg +ensure + puts 'f2.finally' +end + +def f3 &p + $state = false + print 'f3.begin' + + if p == nil then + puts ' <-- owner' + if $kind == :proc then + r = f4 true do |*a| # f4 is the converter + puts "args = #{a.inspect}" + a = a + [1] + eval make_block('proc2') # redo goes here (at the beginning of the block) + end # break unwinds to here + else + r = f4(false, &lambda do |*a| # lambda is the converter => it is inactive! + puts "args = #{a.inspect}" + a = a + [1] + eval make_block('lambda2') # redo goes here (at the beginning of the block) + end) # break does NOT uwind here + end + else + puts + r = f4 false, &p + end + puts "result = '#{r}'" + puts 'f3.end' + state_msg +ensure + puts 'f3.finally' +end + +def f4 is_converter, &p + puts "f4.begin#{is_converter ? ' <-- converter' : ''}" + + b = true + while b do # loop does NOT interfere with stack unwinding + f5 &p + b = false + end + + puts 'f4.end' + state_msg +ensure + puts 'f4.finally' +end + +def f5 &p + puts 'f5.begin' + invoke &p + puts 'f5.end' + state_msg +ensure + puts 'f5.finally' +end + +def invoke &p + puts 'invoke.begin' + if $action == :yield then + puts 'yielding' + puts "yield-result = '#{yield}'" # next returns here + else + puts 'calling' + puts "call-result = '#{p[]}'" # next returns here + end + puts 'invoke.end' + state_msg +rescue + $state = false + puts "invoke.ERROR: #{$!.inspect}" +ensure + puts 'invoke.finally' +end + +def state_msg + if $state then + $state = false + " <---- #{$statement} returned here" + else + "" + end +end + +run + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/NextRedo/LambdaReturn.rb b/merlin/main/languages/ruby/Experimental/CF/NextRedo/LambdaReturn.rb new file mode 100644 index 0000000000..09a712c0e4 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/NextRedo/LambdaReturn.rb @@ -0,0 +1,25 @@ +def y; yield; end + +l = lambda { 1 } +puts l[] + +p = Proc.new { 1 } +puts p[] + +l = lambda { next 1; 2 } +puts l[] # nil (bug ???) + +p = Proc.new { next 1; 2 } +puts p[] + +l = lambda { 1 } +puts y(&l) + +p = Proc.new { 1 } +puts y(&p) + +l = lambda { next 1; 2 } +puts y(&l) + +p = Proc.new { next 1; 2 } +puts y(&p) diff --git a/merlin/main/languages/ruby/Experimental/CF/NextRedo/PrimaryFrame.rb b/merlin/main/languages/ruby/Experimental/CF/NextRedo/PrimaryFrame.rb new file mode 100644 index 0000000000..6e8c628d76 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/NextRedo/PrimaryFrame.rb @@ -0,0 +1,45 @@ +=begin + +Exception not rescuable in the primary frame. + +=end + +def test_next + puts "test_next.begin" + next + puts "test_next.end" +rescue + puts "Unreachable" +end + +def test_redo + puts "test_redo.begin" + redo + puts "test_redo.end" +rescue + puts "Unreachable" +end + +begin + test_next +rescue + puts "E: #{$!}" +end + +begin + test_next {} +rescue + puts "E: #{$!}" +end + +begin + test_redo +rescue + puts "E: #{$!}" +end + +begin + test_redo {} +rescue + puts "E: #{$!}" +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/NextRedo/SimpleLoop.rb b/merlin/main/languages/ruby/Experimental/CF/NextRedo/SimpleLoop.rb new file mode 100644 index 0000000000..5e7d76a475 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/NextRedo/SimpleLoop.rb @@ -0,0 +1,19 @@ +puts '---' + +i = 0 +while begin puts 'c'; i < 3; end do + puts 'a' + i = i + 1 + next + puts 'b' +end + +puts '---' + +i = 0 +while begin puts 'c'; i < 3; end do + puts 'a' + i = i + 1 + redo unless i > 4 + puts 'b' +end diff --git a/merlin/main/languages/ruby/Experimental/CF/Rescue/ClearingException.rb b/merlin/main/languages/ruby/Experimental/CF/Rescue/ClearingException.rb new file mode 100644 index 0000000000..7df95fad1f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Rescue/ClearingException.rb @@ -0,0 +1,45 @@ +def foo + begin + $! = IOError.new + begin + raise StandardError.new("XXX") + rescue + puts "R1: #{$!}" # rescue clears exception + ensure + puts "E1: #{$!}" + return if $return + end + rescue + puts "R2: #{$!}" + ensure + puts "E2: #{$!}" + end +end + +def bar + $! = IOError.new + begin + raise StandardError.new("XXX") + ensure + puts "E: #{$!}" + return if $return + end +rescue + puts "R2: #{$!}" +ensure + puts "E2: #{$!}" +end + +$return = false + +puts '---' +foo +puts '---' +bar + +$return = true + +puts '---' +foo +puts '---' +bar diff --git a/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionTakingProc-Rescue.rb b/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionTakingProc-Rescue.rb new file mode 100644 index 0000000000..097c7a94d0 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionTakingProc-Rescue.rb @@ -0,0 +1,93 @@ +=begin +output: + +A +block called 0 +B +retrying +A +block called 1 +B + +comment: + Retry on Proc returns to the call-site that made the block, if the frame of the call-site is still alive. + +=end + +$i = 0; + +def retry_proc *,&p + if ($i == 0) + puts "retrying '#{p}'" + $i = $i + 1 + retry + end +end + +def block_def + defp(puts('P')) { + puts "P block called" + } + defq(puts('Q')) { + puts "Q block called" + } +end + +def defp *,&p + $p = p +end + +def defq *,&q + $q = q +end + +def g1 *,&x + puts '1.begin' + retry_proc puts('C'), &$p + puts '1.end' +ensure + puts '1.finally' +end + +def g2 *,&x + puts '2.begin' # jumps here, rescue clause interferes + raise +rescue + g1 puts('2.r'), &$p + puts '2.end' +ensure + puts '2.finally' +end + +def g3 *,&x + puts '3.begin' + g2 puts('3.r'), &$p + puts '3.end' +ensure + puts '3.finally' +end + +def g4 *,&x + $p = x + puts '4.begin' + g3 puts('4.r'), &$q + puts '4.end' +ensure + puts '4.finally' +end + +def g5 *,&x + puts '5.begin' + g4(puts('5.r')) { puts 'z' } + puts '5.end' +ensure + puts '5.finally' +end + +block_def +g5 + + + + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionTakingProc1.rb b/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionTakingProc1.rb new file mode 100644 index 0000000000..5bae501e06 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionTakingProc1.rb @@ -0,0 +1,91 @@ +=begin +output: + +A +block called 0 +B +retrying +A +block called 1 +B + +comment: + Retry on Proc returns to the call-site that made the block, if the frame of the call-site is still alive. + +=end + +$i = 0; + +def retry_proc *,&p + if ($i == 0) + puts "retrying '#{p}'" + $i = $i + 1 + retry + end +end + +def block_def + defp(puts('P')) { + puts "P block called" + } + defq(puts('Q')) { + puts "Q block called" + } +end + +def defp *,&p + $p = p +end + +def defq *,&q + $q = q +end + +def g1 *,&x + puts '1.begin' + retry_proc puts('C'), &$p + puts '1.end' +ensure + puts '1.finally' +end + +def g2 *,&x + puts '2.begin' + g1 puts('2.r'), &$p + puts '2.end' +ensure + puts '2.finally' +end + +def g3 *,&x + puts '3.begin' + g2 puts('3.r'), &$p # jumps here, because this function's block differs from the one that is being retried + puts '3.end' +ensure + puts '3.finally' +end + +def g4 *,&x + $p = x + puts '4.begin' + g3 puts('4.r'), &$q + puts '4.end' +ensure + puts '4.finally' +end + +def g5 *,&x + puts '5.begin' + g4(puts('5.r')) { puts 'z' } + puts '5.end' +ensure + puts '5.finally' +end + +block_def +g5 + + + + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionTakingProc2.rb b/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionTakingProc2.rb new file mode 100644 index 0000000000..1a682c5cf4 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionTakingProc2.rb @@ -0,0 +1,53 @@ +=begin +output: + +A +ppt.begin +block called 0 +B +retrying +ppt.finally +A +ppt.begin +block called 1 +B +ppt.end +ppt.finally + +comment: + Let B is the block/proc of the current frame (passed to the current function). + Retry unwinds all frames whose call-sites passed B thru and jumps right before the last call-site that passed B thru + (the next call-site on the stack passed different block). + Ensure clauses are called during stack unwinding. + +=end + +def retry_proc *,&p + if ($i == 0) + puts 'retrying' + $i = $i + 1 + retry + end +end + +def pass_proc_thru *,&p + puts 'ppt.begin' + yield + retry_proc puts('B'), &p + puts 'ppt.end' +ensure + puts 'ppt.finally' +end + +$i = 0; + +def retry_target + pass_proc_thru(puts('A')) { + puts "block called #{$i}" + } +end + +retry_target + + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionWithRescue-Error.rb b/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionWithRescue-Error.rb new file mode 100644 index 0000000000..9e9ad5bae9 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionWithRescue-Error.rb @@ -0,0 +1,22 @@ +$i = 0 + +def foo + if $i == 0 + puts 'retrying' + $i += 1 + retry + end +end + +def goo + puts 'goo.begin' + raise +rescue + foo +end + +goo + + + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionWithRescue-OK.rb b/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionWithRescue-OK.rb new file mode 100644 index 0000000000..0ed02d4d7e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Retry/InFunctionWithRescue-OK.rb @@ -0,0 +1,107 @@ +# setup ############################## + +$i = 0 + +def defp &p + $p = p +end + +defp {} + +def f *,&p + if $i == 0 + puts 'retrying' + $i = 1 + retry + end +end + +####################################### + +def g1 *a + puts 'G' + f puts('F'),&$p +end + +def g2 *a + puts 'G' + raise +rescue + f puts('F'),&$p # retries the try-block, unless this method doesn't get block $p +end + +def g3 *a + puts 'G' + 1.times do + puts 'T' + f puts('F'),&$p # retries the block, unless this method doesn't get block $p + end +end + +$outer_lambda = lambda do + puts 'T' + f puts('F'),&$p +end + +def g4 *a + puts 'G' + 1.times &$outer_lambda # retries the f call in $outer_lambda; we are not in the function g4 here, because the block has been created outside it +end + +def g5 *a + $local_lambda = lambda do + puts 'T' + f puts('F'),&$p + end + + puts 'G' + 1.times &$local_lambda # retries the block; we are in the scope of g5 +end + +def h + puts '- 1 -' + $i = 0 + g1 puts('g') # the function f call in g1 is retried + + puts '- 2 -' + $i = 0 + g1 puts('g'), &$p # this call is retried + + puts '- 3:rescue -' + $i = 0 + g2 puts('g') # the function f call in g2 is retried + + puts '- 4:rescue -' + $i = 0 + g2 puts('g'), &$p # the try-block in g1 is retried (!) + + puts '- 5:block -' + $i = 0 + g3 puts('g') # the function f call in g3 is retried + + puts '- 6:block -' + $i = 0 + g3 puts('g'), &$p # the 1.times call in g3 is retried (!) + + puts '- 7:out-lambda -' + $i = 0 + g4 puts('g') # the function f call in $l is retried + + puts '- 8:out-lambda -' + $i = 0 + g4 puts('g'), &$p # the function f call in $l is retried + + puts '- 9:in-lambda -' + $i = 0 + g5 puts('g') # the function f call in $l is retried + + puts '- A:in-lambda -' + $i = 0 + g5 puts('g'), &$p # the function f call in $l is retried +end + +h + + + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/Retry/RetryInProc.rb b/merlin/main/languages/ruby/Experimental/CF/Retry/RetryInProc.rb new file mode 100644 index 0000000000..43eb1858e0 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Retry/RetryInProc.rb @@ -0,0 +1,137 @@ +=begin + +primary-frame(main): + block: - + + call-site: owner + primary-frame(owner): + block: - + + call-site: Proc.new B1 + primary-frame(Proc.new): + block: B1 + + call-site: foo B1 {} + primary-frame(foo): + block: B1 + + call-site: y B1 {InRescue} + primary-frame(y): + block: B1 + + call-site: yield B1 {} + block-frame(B1): + owner: primary-frame(owner), Inactive + retry + + call-site: B1.call {} + ERROR + +=end + +$i = 0 + +def y *a +# puts 'y.begin' +# raise +#rescue # uncomment -> yield retries this try block + + if $yield then + yield + else + begin + $p[] + rescue + puts "Error: #{$!}" + end + end +end + +def owner + $lambda = lambda do # B1 + puts 'block.begin' + if ($i == 0) + $i = 1 + puts 'retrying' + begin + retry + rescue + puts "Unreachable" + end + end + puts 'block.end' + end + + $proc = Proc.new do # B2 + puts 'block.begin' + if ($i == 0) + $i = 1 + puts 'retrying' + retry + end + puts 'block.end' + end + + $p = $lambda + y puts('Y') + + $p = $proc + y puts('Y') +end + +def foo *a + puts 'foo.begin' + raise +rescue + y puts('Y'),&$p # retries try block ($yield) / Error: retry from proc-closure (!$yield), doesn't depend on whether the block is active or not +ensure + puts 'foo.finally' +end + +puts '-' * 50,'Tests []-call to a lambda and proc from with their owner active','-' * 50 + +$i = 0 +$yield = false +owner + +puts '-' * 50,'Tests yield to a lambda (with inactive owner)','-' * 50 + +$i = 0 +$p = $lambda +$yield = true +foo puts('F'),&$p + +puts '-' * 50,'Tests yield to a proc (with inactive owner)','-' * 50 + +$i = 0 +$p = $proc +$yield = true +foo puts('F'),&$p + +puts '-' * 50,'Tests []-call to a lambda with incactive owner','-' * 50 + +$i = 0 +$p = $lambda +$yield = false +foo puts('F'),&$p + +puts '-' * 50,'Tests []-call to a proc with incactive owner','-' * 50 + +$i = 0 +$p = $proc +$yield = false +foo puts('F'),&$p + +puts '-' * 50,'Tests retry in method w/o block and outside rescue','-' * 50 + +def bar + retry +rescue + puts "E1: #{$!}" +end + +begin + bar +rescue + puts "E2: #{$!}" # exception caught here +end diff --git a/merlin/main/languages/ruby/Experimental/CF/Retry/RetryLoop.rb b/merlin/main/languages/ruby/Experimental/CF/Retry/RetryLoop.rb new file mode 100644 index 0000000000..120bed5611 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Retry/RetryLoop.rb @@ -0,0 +1,30 @@ +=begin +while-loop cannot be retried + +output: +A +times +retrying +A +times + +=end + + +$i = 0; + +begin + puts 'A' + raise +rescue + while true + puts 'times' + if $i == 0 + $i = 1 + puts 'retrying' + retry + end + + break; + end +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/Retry/RetryYieldedBlockInBlock.rb b/merlin/main/languages/ruby/Experimental/CF/Retry/RetryYieldedBlockInBlock.rb new file mode 100644 index 0000000000..b96f1df3e4 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Retry/RetryYieldedBlockInBlock.rb @@ -0,0 +1,53 @@ +def u *a, &p + puts 'u.begin' + $p = p + v +ensure + puts 'u.finally' +end + +def v + puts 'v.begin' + raise +rescue + w &$p # y does retry the try-block +ensure + puts 'v.finally' +end + +def w &b + puts 'w.begin' + y &$p # return value == retry singleton && block == passed block ==> do retry +ensure + puts 'w.finally' +end + +def y + puts 'y.begin' + yield # return reason == retry ==> do retry +ensure + puts 'y.finally' +end + +def foo + puts 'foo.begin' + u puts('Y') do + puts 'outer-block.begin' + yield + puts 'outer-block.end' + end + puts 'foo.end' +end + +$i = true + +foo do + puts 'inner-block.begin' + if $i then + $i = false + puts 'retrying' + retry + end + puts 'inner-block.end' +end + diff --git a/merlin/main/languages/ruby/Experimental/CF/Return/Errors.rb b/merlin/main/languages/ruby/Experimental/CF/Return/Errors.rb new file mode 100644 index 0000000000..4952eab8fa --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Return/Errors.rb @@ -0,0 +1,10 @@ +def foo + $p = Proc.new { return } +end + +def y + yield +end + +foo +y &$p diff --git a/merlin/main/languages/ruby/Experimental/CF/Return/Recursive.rb b/merlin/main/languages/ruby/Experimental/CF/Return/Recursive.rb new file mode 100644 index 0000000000..c9fa2d921b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/Return/Recursive.rb @@ -0,0 +1,72 @@ +$r = lambda { + begin + puts 'returning R'; + return 'r' + rescue + puts 'ERROR 1' + end +} + +def f0 + puts 'f0.begin' + + $q = lambda { + 1.times { + puts 'returning Q'; + return 'q' + } + } + + f1 +ensure + puts 'f0.finally' +end + +def f1 + puts 'f1.begin' + $p = lambda { + puts 'returning P'; + return 'p' + } + + puts "result: #{f2}" + puts 'f1.end' +ensure + puts 'f1.finally' +end + +def f2 + puts 'f2.begin' + f3 + puts 'f2.end' +ensure + puts 'f2.finally' + 1.times { + $q[] # returns to $q's definition (! note difference from $p[]) + puts 'Unreachable' + } +end + +def f3 + puts 'f3.begin' + 1.times { + 2.times { + 3.times { + puts "result: #{$p[]}" # returns right here, different from y &$p (!) + puts "result: #{$r[]}" # returns right here + y &$r # ERROR 1 + y &$p # unwinds stack + } + } + } + puts 'f3.end' +ensure + puts 'f3.finally' +end + +def y + puts 'yielding P' + yield # returns to $p's definition +end + +f0 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/ThrowCatch/test.rb b/merlin/main/languages/ruby/Experimental/CF/ThrowCatch/test.rb new file mode 100644 index 0000000000..176597df3b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/ThrowCatch/test.rb @@ -0,0 +1,61 @@ +class S2 + def to_str + 'x2' + end +end + +def f1 + throw :undefined +rescue Exception + puts "error: #{$!.inspect}" +end + +def f2 + throw S2.new, 'hello' +rescue Exception + puts "error: #{$!.inspect}" +else + puts 'else' +ensure + puts 'ensure' +end + +catch :x1 do + #f1 +end + +x = catch :x2 do + f2 + puts 'bar' +end + +p x + +puts '-'*10 + +catch :a do + catch :b do + catch :c do + throw :d rescue p $! + + begin + throw :a + ensure + throw :b + end + + puts 'in c' + end + puts 'c' + end + puts 'b' +end +puts 'a' + +x = catch :foo do |*args| + p args + 123 +end +p x + + diff --git a/merlin/main/languages/ruby/Experimental/CF/define_method/flow.rb b/merlin/main/languages/ruby/Experimental/CF/define_method/flow.rb new file mode 100644 index 0000000000..1491cd22b1 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/define_method/flow.rb @@ -0,0 +1,120 @@ +$M = {} + +class Class + def def_method(name, &p) + $M[name] = lambda &p + end +end + + +class C + define_method(:foo) { + puts 'breaking foo' + break 'foo' + } + + define_method(:bar, &lambda{ + puts 'breaking bar' + break 'bar' + }) + + define_method(:f1) { + puts 'redoing f1' + if $r > 0 + $r -= 1 + redo + end + } + + define_method(:g1) { + puts 'retrying g1' + if $r > 0 + $r -= 1 + retry + end + } + + def_method(:xfoo) { + puts 'breaking xfoo' + break 'xfoo' + } + + def_method(:xbar, &lambda{ + puts 'breaking xbar' + break 'xbar' + }) + + def_method(:xf1) { + puts 'redoing xf1' + if $r > 0 + $r -= 1 + redo + end + } + + def_method(:xg1) { + puts 'retrying xg1' + if $r > 0 + $r -= 1 + retry + end + } + + def call_xfoo + $M[:xfoo][] + end + + def call_xbar + $M[:xbar][] + end + + def call_xf1 + $M[:xf1][] + end + + def call_xg1 + $M[:xg1][] + end +end + +puts '-- real --' + +puts C.new.foo +puts C.new.bar + +$r = 1; +puts C.new.f1 + +$r = 1; +puts C.new.g1 rescue puts $! # retry from proc-closure + +puts '-- emulated --' + +puts C.new.call_xfoo +puts C.new.call_xbar + +$r = 1; +puts C.new.call_xf1 + +$r = 1; +puts C.new.call_xg1 rescue puts $! # retry from proc-closure + +puts '-- hash --' + +h = Hash.new { + puts 'breaking 2' + break '2' + } + +begin + puts h[1] +rescue + puts $! +end + +h = Hash.new(&lambda { + puts 'breaking 3' + break '3' + }) + +puts h[1] \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/CF/define_method/visibility.rb b/merlin/main/languages/ruby/Experimental/CF/define_method/visibility.rb new file mode 100644 index 0000000000..a79265865b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/CF/define_method/visibility.rb @@ -0,0 +1,16 @@ +class C + + # self is not captured in closure: + define_method :foo do + p self + p self.class + end + + private + define_method :priv do + puts 'private' + end +end + +C.new.foo +C.new.priv rescue puts $! # private method called \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Clone/AllObjects.rb b/merlin/main/languages/ruby/Experimental/Clone/AllObjects.rb new file mode 100644 index 0000000000..a5151cac46 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/AllObjects.rb @@ -0,0 +1,34 @@ +objects = [{}, [], '', Regexp.new('foo'), Object.new, Module.new, Class.new] + +objects.each do |x| + puts x.class.name + + class << x + CONST = 1 + + def foo + 3 + end + + instance_variable_set(:@iv_singleton_x, 2); + end + + x.instance_variable_set(:@iv_x, 4); + y = x.clone + + class << y + # constants copied: + raise unless CONST == 1 + + #singleton class methods not copied + raise unless ((bar;false) rescue true) + + # instance variables not copied: + raise unless instance_variables.size == 0 + end + + # methods copied: + raise unless y.foo == 3 + raise unless y.instance_variable_get(:@iv_x) == 4 + +end diff --git a/merlin/main/languages/ruby/Experimental/Clone/Array.initialize_copy.rb b/merlin/main/languages/ruby/Experimental/Clone/Array.initialize_copy.rb new file mode 100644 index 0000000000..696bd10fee --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/Array.initialize_copy.rb @@ -0,0 +1,45 @@ +class Array + p private_instance_methods(false) +end + +x = Array.new + +def x.foo; end +x[0] = 'source' +x.instance_variable_set(:@bar, 1); +x.taint +x.freeze + +y = [1,1,1] + +p y.send(:initialize_copy, x) + +puts '---' + +y.foo rescue p $! +p y.instance_variables +p y.tainted? +p y.frozen? +p y + +puts '---' + +h = [] +h.freeze +h.send(:initialize_copy, []) rescue p $! + +puts '---' + +class Hash + def initialize_copy *a + puts 'init_copy' + end +end + +x = Array.new +x[1] = 2 +x.instance_variable_set(:@foo, 1) + +y = x.dup +p y +p y.instance_variables diff --git a/merlin/main/languages/ruby/Experimental/Clone/Class.initialize_copy.rb b/merlin/main/languages/ruby/Experimental/Clone/Class.initialize_copy.rb new file mode 100644 index 0000000000..1741da0799 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/Class.initialize_copy.rb @@ -0,0 +1,58 @@ +class Class + p private_instance_methods(false) +end + +class C + def foo + end +end + +class B + def bar + end +end + +p C.send(:initialize_copy, B) rescue p $! +p Class.new.send(:initialize_copy, B) rescue p $! + +class C + def foo + end +end + +class Class + alias xi initialize_copy + + def initialize_copy a + puts "init_copy" + p name + r = xi(a) + p name + r + end +end + +x = C.clone +p x +p x.instance_methods(false) +p x.ancestors + +puts '-----' + +class Class + def initialize_copy a + puts 'init_copy' + end +end + +x = C.clone +p x +p x.instance_methods(false) +p x.ancestors + +x.send :xi, B +p x +p x.instance_methods(false) +p x.ancestors + +x.send :xi, C rescue p $! diff --git a/merlin/main/languages/ruby/Experimental/Clone/Hash.initialize_copy.rb b/merlin/main/languages/ruby/Experimental/Clone/Hash.initialize_copy.rb new file mode 100644 index 0000000000..449a226a42 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/Hash.initialize_copy.rb @@ -0,0 +1,48 @@ +class Hash + p private_instance_methods(false) +end + +x = Hash.new {} + +def x.foo; end +x[1] = 2 +x.instance_variable_set(:@bar, 1); +x.taint +x.freeze + +y = {3 => 4} + +p y.send(:initialize_copy, x) + +puts '---' + +y.foo rescue p $! +p y.instance_variables +p y.tainted? +p y.frozen? +p y.default_proc + +p y + +puts '---' + +h = {} +h.freeze +h.send(:initialize_copy, {}) rescue p $! + +puts '---' + +class Hash + def initialize_copy *a + puts 'init_copy' + end +end + +x = Hash.new {} +x[1] = 2 +x.instance_variable_set(:@foo, 1) + +y = x.dup +p y +p y.instance_variables +p y.default_proc diff --git a/merlin/main/languages/ruby/Experimental/Clone/MatchData.initialize_copy.rb b/merlin/main/languages/ruby/Experimental/Clone/MatchData.initialize_copy.rb new file mode 100644 index 0000000000..5a86138e29 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/MatchData.initialize_copy.rb @@ -0,0 +1,25 @@ +"foo" =~ /(f)(o)(o)/ +m = $~ + +"x" =~ /(x)/ +x = $~ + +p m.captures +p m.dup.captures + +x.freeze +x.send :initialize_copy, m +p x.captures + +class MatchData + def initialize_copy *a + puts 'init_copy' + end +end + +n = m.dup +p n.captures + +# ruby seg fault: +#p n.pre_match + diff --git a/merlin/main/languages/ruby/Experimental/Clone/Module.initialize_copy.rb b/merlin/main/languages/ruby/Experimental/Clone/Module.initialize_copy.rb new file mode 100644 index 0000000000..3dd3f12d66 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/Module.initialize_copy.rb @@ -0,0 +1,67 @@ +module M + def foo + end + + def M.mmm + + end +end + +module N + def bar + end + + def N.nnn + + end +end + +module X +end + +puts '--- cannot initialize frozen module:' +X.freeze +p X.send(:initialize_copy, N) rescue p $! + +puts '--- direct call:' + +M.send(:initialize_copy, N) + +p M.instance_methods(false) +p M.singleton_methods(false) + +puts '--- name:' + +$m = Module.new +$m.send :initialize_copy, M +p $m.name + +########################################## + +class Module + def initialize_copy *a + puts 'init_copy' + end +end + +puts '--- clone:' + +M_clone = M.clone + +p M_clone.instance_methods(false) +p M_clone.singleton_methods(false) + +puts '--- dup:' + +M_dup = M.dup + +p M_dup.instance_methods(false) +p M_dup.singleton_methods(false) + +puts '--- name:' + +$m = Module.new +$m.send :initialize_copy, M +p $m.name + + diff --git a/merlin/main/languages/ruby/Experimental/Clone/Range.initialize_copy.rb b/merlin/main/languages/ruby/Experimental/Clone/Range.initialize_copy.rb new file mode 100644 index 0000000000..12d25054d7 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/Range.initialize_copy.rb @@ -0,0 +1,40 @@ +class Range + p private_instance_methods(false) +end + +x = 1...10 + +def x.foo; end +x.instance_variable_set(:@bar, 1); +x.taint +x.freeze + +y = 4..5 + +p y.send(:initialize_copy, x) + +puts '---' + +y.foo rescue p $! +p y.instance_variables +p y.tainted? +p y.frozen? +p y + +puts '---' + +h = 1..2 +h.freeze +h.send(:initialize_copy, 5..6) rescue p $! + +puts '---' + +class Range + def initialize_copy *a + puts 'init_copy' + end +end + +x = 1..3 +p x.dup +p y \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Clone/String.initialize_copy.rb b/merlin/main/languages/ruby/Experimental/Clone/String.initialize_copy.rb new file mode 100644 index 0000000000..e0fe4be35c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/String.initialize_copy.rb @@ -0,0 +1,55 @@ +class String + p private_instance_methods(false) +end + +x = "xxx" + +def x.foo; end +x.instance_variable_set(:@bar, 1); +x.taint +x.freeze + +y = "yyy" + +p y.send(:initialize_copy, x) + +puts '---' + +y.foo rescue p $! +p y.instance_variables +p y.tainted? +p y.frozen? +p y + +puts '---' + +h = "" +h.freeze +h.send(:initialize_copy, "") rescue p $! + +h = "g" +h.send(:initialize_copy, S.new) rescue p $! +p h + +puts '---' + +class String + def initialize_copy *a + puts 'init_copy' + end +end + +x = "qqq" +x.instance_variable_set(:@foo, 1) + +y = x.dup +p y +p y.instance_variables + +puts '---' + +x = {} +x["foo"] = 1 +p x + + diff --git a/merlin/main/languages/ruby/Experimental/Clone/Struct.initialize_copy.rb b/merlin/main/languages/ruby/Experimental/Clone/Struct.initialize_copy.rb new file mode 100644 index 0000000000..0b2dc1692f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/Struct.initialize_copy.rb @@ -0,0 +1,49 @@ +class << Struct.new(:f,:g) + p private_instance_methods(false) + p instance_methods(false) +end + +x = Struct.new(:a,:b,:c) +y = Struct.new(:x,:y) + + +y.send(:initialize_copy, x) rescue p $! + +ix1 = x[1,2,3] +ix2 = x[10,20,30] +iy1 = y[6,7] +iy2 = y[60,70] + +# invalid arg class: +ix1.send(:initialize_copy, iy1) rescue p $! + +ix1.send(:initialize_copy, ix2) rescue p $! + +p ix1 + +Y = y +class YY < Y +end + +iyy = YY.new + +# error: +iyy.send(:initialize_copy, iy1) rescue p $! + +puts '---' + +class Struct + def initialize_copy *a + puts 'init_copy' + end +end + +x = Struct.new(:a) + +p x.private_instance_methods(false) +#y = x.dup +p y.members + +class MS < String +end +MS.dup diff --git a/merlin/main/languages/ruby/Experimental/Clone/classes.rb b/merlin/main/languages/ruby/Experimental/Clone/classes.rb new file mode 100644 index 0000000000..58632e346a --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/classes.rb @@ -0,0 +1,59 @@ +class S < String + +end + +class A < Array +end + +class H < Hash +end + +class R < Range +end + +class RE < Regexp +end + +class M < Module +end + +class St < Struct +end + +class P < Proc +end + + +[S,A,H,R,RE,M,St,P, +String, Array, Hash, Range, Regexp, Module, Proc, Class, Struct, Object].each { |c| + c.dup +} + +p S.dup.new('foo') +p A.dup.new([1,2]) +p H.dup.new('default')[1] +p R.dup.new(1,2) +p RE.dup.new("foo") +p M.dup.new.name +p St.dup.new(:a,:b).dup[1,2].dup.members +p P.dup.new { 'foo' }[] + +puts '---' + +p String.dup.new('foo') +p Array.dup.new([1,2]) +p Hash.dup.new('default')[1] +p Range.dup.new(1,2) +p Regexp.dup.new("foo") +p Module.dup.new.name +p Struct.dup.new(:a,:b).dup[1,2].dup.members +p Proc.dup.new { 'foo' }[] + + +class Class + def initialize_copy *a + puts 'init_copy' + end +end + +String.dup.new('foo') rescue p $! \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Clone/events.rb b/merlin/main/languages/ruby/Experimental/Clone/events.rb new file mode 100644 index 0000000000..1b72f181d7 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/events.rb @@ -0,0 +1,27 @@ +# no events fired on duplication + +class Class + def inherited other + puts "I: #{self} #{other}" + end +end + +class Module + def method_added name + puts "M: #{name}" + end +end + +class B +end + +class C < B + def foo + end +end + +C1 = C.dup +C2 = C.clone + +p C1.ancestors +p C2.ancestors \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Clone/exception_in_initcpy.rb b/merlin/main/languages/ruby/Experimental/Clone/exception_in_initcpy.rb new file mode 100644 index 0000000000..26ee3c14bf --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/exception_in_initcpy.rb @@ -0,0 +1,13 @@ +class Array + def initialize_copy x + $g = self + raise + end +end + +a = [1,2,3] +a.freeze + +a.clone rescue 0 + +p $g.frozen? \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Clone/initcpy_instance_singleton.rb b/merlin/main/languages/ruby/Experimental/Clone/initcpy_instance_singleton.rb new file mode 100644 index 0000000000..a5151cac46 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Clone/initcpy_instance_singleton.rb @@ -0,0 +1,34 @@ +objects = [{}, [], '', Regexp.new('foo'), Object.new, Module.new, Class.new] + +objects.each do |x| + puts x.class.name + + class << x + CONST = 1 + + def foo + 3 + end + + instance_variable_set(:@iv_singleton_x, 2); + end + + x.instance_variable_set(:@iv_x, 4); + y = x.clone + + class << y + # constants copied: + raise unless CONST == 1 + + #singleton class methods not copied + raise unless ((bar;false) rescue true) + + # instance variables not copied: + raise unless instance_variables.size == 0 + end + + # methods copied: + raise unless y.foo == 3 + raise unless y.instance_variable_get(:@iv_x) == 4 + +end diff --git a/merlin/main/languages/ruby/Experimental/Conversions/cast_to_symbol.rb b/merlin/main/languages/ruby/Experimental/Conversions/cast_to_symbol.rb new file mode 100644 index 0000000000..f0e66c89fe --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Conversions/cast_to_symbol.rb @@ -0,0 +1,49 @@ + +module M + +[1, :X.to_i, nil, 1.2].each do |x| + +puts '='*80, "== #{x.inspect} ==", '='*80 + +instance_variable_get(x) rescue p $! +instance_variable_set(x, nil) rescue p $! +instance_variable_defined?(x) rescue p $! +remove_instance_variable(x) rescue p $! + +puts '-' * 20 + +class_variable_get(x) rescue p $! +class_variable_set(x, nil) rescue p $! +remove_class_variable(x) rescue p $! + +puts '-' * 20 + +const_set(x, nil) rescue p $! +const_get(x) rescue p $! +const_defined?(x) rescue p $! +remove_const(x) rescue p $! + +puts '-' * 20 + +method_defined?(x) rescue p $! +respond_to?(x) rescue p $! +send(x) rescue p $! +method(x) rescue p $! +throw(x) rescue p $! +catch(x) rescue p $! + +puts '-' * 20 + +public(x) rescue p $! +define_method(x, &lambda {}) rescue p $! +attr(x) rescue p $! +attr_accessor(x) rescue p $! +attr_writer(x) rescue p $! +attr_reader(x) rescue p $! +alias_method(x,x) rescue p $! +remove_method(x,x) rescue p $! +undef_method(x,x) rescue p $! +Struct.new(x) rescue p $! + +end +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Defined/All.rb b/merlin/main/languages/ruby/Experimental/Defined/All.rb new file mode 100644 index 0000000000..fbda6753dd --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Defined/All.rb @@ -0,0 +1,153 @@ +class C + def foo + @x = 1 + p defined? @x + + @y = nil + p defined? @y + + p defined? @z + + @@y = 1 + p defined? @@y + + @@y = nil + p defined? @@y + + p defined? @@z + + end +end + +puts 1 +p defined? C + +puts 2 +C.new.foo + +puts 3 +x1 = 1 +1.times {|x2| + p defined? x1 + p defined? x2 +} + +puts 4 +p defined? 1 + +puts 5 +p defined? dummy + +puts 6 +p defined? puts + +puts 7 +p defined? String + +"123" =~ /(1)/ + +puts 8 +p defined? $HELLO +p defined? $- +p defined? $-a +p defined? $-x +p defined? $! +p defined? $@ +p defined? $: +p defined? $; +p defined? $" +p defined? $, +p defined? $; +p defined? $/ +p defined? $\ +p defined? $* +p defined? $$ +p defined? $? +p defined? $= +p defined? $: +p defined? $" +p defined? $< +p defined? $> +p defined? $. +p defined? $0 + +p defined? $~ +p defined? $& +p defined? $` +p defined? $' +p defined? $+ +p defined? $1 + +puts 9 +p defined? Math::PI + +puts 10 +p defined? a = 1 +p defined? a += 1 #assignment +p defined?(a &&= 1) #expression + +puts 11 +p defined? a + +puts 12 +p defined? 42.times +p defined? Kernel::puts + +puts 13 +p defined? 1 + foo +p defined? def foo; end +p defined? lambda {} + +puts 14 +p (s = defined? :foo) +p (t = defined? :bar) +p s.object_id == t.object_id + +puts 15 +p defined? nil +p defined? true +p defined? false +p defined? self + +Q = nil + +puts 16 +p defined? P +p defined? Q + +=begin +TODO: super is complicated - define_method, etc. + +puts 17 +class C + def f + p defined? super + end +end + +class D < C + def f + p defined? super + super + end +end + +D.new.f +=end + +puts 18 + +def g + p defined? yield +end +g +g {} + +puts 19 + +undef g +undef g rescue p $! +p defined? g + + + diff --git a/merlin/main/languages/ruby/Experimental/Defined/ClassVars.rb b/merlin/main/languages/ruby/Experimental/Defined/ClassVars.rb new file mode 100644 index 0000000000..b580e8d606 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Defined/ClassVars.rb @@ -0,0 +1,21 @@ +module M + def foo; @@foo = 1; end + def foo_defined_on_M? + p defined? @@foo + end +end + +module N + def foo_defined? + p defined? @@foo + end +end + +class C + include M,N +end + +c = C.new +c.foo +c.foo_defined? +c.foo_defined_on_M? \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Defined/Constants.rb b/merlin/main/languages/ruby/Experimental/Defined/Constants.rb new file mode 100644 index 0000000000..b5be58a89d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Defined/Constants.rb @@ -0,0 +1,23 @@ +W = 0 + +module M + X = 1 +end + +module N + Y = 2 + def cs_defined? + puts defined? ::W + puts defined? M::X + puts defined? X + puts defined? Y + puts defined? Z + end +end + +class C + include M,N + Z = 3 +end + +C.new.cs_defined? \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Defined/Method.rb b/merlin/main/languages/ruby/Experimental/Defined/Method.rb new file mode 100644 index 0000000000..6e5198a86e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Defined/Method.rb @@ -0,0 +1,27 @@ +# defined? foo behaves like method :foo, not like undef foo + +module M + def foo; end +end + +module N + def foo_defined? + p defined? foo #foo is not in lexical scope, but still this returns "method" + undef foo rescue p $! # erorr + p defined? foo # still defined + end +end + +class C + include M,N +end + +C.new.foo_defined? + +puts '---' + +def foo + p 'foo' +end + +p defined? foo.bar \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Encodings/bom.rb b/merlin/main/languages/ruby/Experimental/Encodings/bom.rb new file mode 100644 index 0000000000..af7a9e0d30 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Encodings/bom.rb @@ -0,0 +1,2 @@ +x = "א" +p x.length \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Encodings/kcode.rb b/merlin/main/languages/ruby/Experimental/Encodings/kcode.rb new file mode 100644 index 0000000000..c7b1065eb2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Encodings/kcode.rb @@ -0,0 +1,144 @@ +def d s + a = [] + if !s.nil? then + s.each_byte { |b| a << b.to_s(16) } + end + p a +end + +aleph = "\xd7\x90" + +puts '-- kcode not used by literal string ctor' + +$KCODE = "u" +u = aleph + +$KCODE = "n" +a = aleph + +/(.)/ =~ u +d $1 + +/(.)/ =~ a +d $1 + +puts '-- kcode not used by string ctor' + +$KCODE = "u" +u = String.new(aleph) + +$KCODE = "n" +a = String.new(aleph) + +/(.)/ =~ u +d $1 + +/(.)/ =~ a +d $1 + +puts '-- kcode not used by regex ctor' + +x = aleph + +$KCODE = "u" +ru = /(.)/ + +$KCODE = "n" +ra = /(.)/ + +ru =~ x +d $1 + +ra =~ x +d $1 + +puts '-- kcode read by regex match' + +x = aleph + +$KCODE = "u" +/(.)/ =~ x +d $1 + +$KCODE = "n" +/(.)/ =~ x +d $1 + +puts '-- regex option --' + +/(.)/u =~ x rescue puts $! +d $1 + +/(.)/n =~ x +d $1 + +puts '-- kcode read directly, not using globals table' + +x = aleph + +$k = "u" +alias $old_KCODE $KCODE +alias $KCODE $k + +p $old_KCODE +p $KCODE + +/(.)/ =~ x +d $1 + +alias $KCODE $old_KCODE # restore + +puts '-- assignment --' +class C + def to_s + "C.new" + end + + def to_str + "u" + end +end + +def try_set value + $KCODE = value +rescue + puts "#{value.inspect} -> error: #{$!}" +else + puts "#{value.inspect} -> #{$KCODE.inspect}" +end + +try_set 1 +try_set nil +try_set C.new +try_set "" +try_set "foo" +try_set "a" +try_set "A" +try_set "n" +try_set "N" +try_set "s" +try_set "S" +try_set "e" +try_set "E" +try_set "u" +try_set "U" +try_set "Uxx" +try_set "Exx" +try_set "Sxx" + +puts '-- mutability --' +$KCODE = "u" +p $KCODE +($KCODE[0] = 'x') rescue puts $! +p $KCODE + +puts '-- KCODE usage --' +x = aleph + +$KCODE = "a" +p x.inspect.length +p x.length + +$KCODE = "u" +p x.inspect.length +p x.length diff --git a/merlin/main/languages/ruby/Experimental/Encodings/kcode_init.rb b/merlin/main/languages/ruby/Experimental/Encodings/kcode_init.rb new file mode 100644 index 0000000000..bd7d66a0fa --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Encodings/kcode_init.rb @@ -0,0 +1 @@ +p $KCODE \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Encodings/nobom.rb b/merlin/main/languages/ruby/Experimental/Encodings/nobom.rb new file mode 100644 index 0000000000..1a1079db87 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Encodings/nobom.rb @@ -0,0 +1,2 @@ +x = "א" +p x.length \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Encodings/utf8.txt b/merlin/main/languages/ruby/Experimental/Encodings/utf8.txt new file mode 100644 index 0000000000..92a5ae3d4e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Encodings/utf8.txt @@ -0,0 +1 @@ +א \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Eval/Binding.rb b/merlin/main/languages/ruby/Experimental/Eval/Binding.rb new file mode 100644 index 0000000000..d4e6f75375 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Eval/Binding.rb @@ -0,0 +1,24 @@ +def y + yield +end + +class C + y { + p self + } + + define_method(:foo) { |&p| + p self + p block_given? + yield rescue puts("ERROR: #{$!}") + binding + } +end + +c = C.new +b = c.foo { puts 'foo block' } + +p c +eval("p self", b) +eval("puts block_given?", b) +eval("yield", b) rescue puts "ERROR: #{$!}" diff --git a/merlin/main/languages/ruby/Experimental/Eval/BindingBlocks.rb b/merlin/main/languages/ruby/Experimental/Eval/BindingBlocks.rb new file mode 100644 index 0000000000..ddaa189aaf --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Eval/BindingBlocks.rb @@ -0,0 +1,57 @@ +def foo + $b0 = binding + x,y = 1,1 + $b1 = binding + + 1.times { |a;x| + x = 2 + z = 1 + $b2 = binding + + 1.times { |a| + eval('x = 4') + eval('u = 1') + $b4 = binding + } + } + + 1.times { |a;x| + x = 3 + $b3 = binding + } +end + +foo + +puts 'x:' +eval('p x',$b0) +eval('p x',$b1) +eval('p x',$b2) +eval('p x',$b3) +eval('p x',$b4) +puts + +puts 'y:' +eval('p y',$b0) +eval('p y',$b1) +eval('p y',$b2) +eval('p y',$b3) +eval('p y',$b4) +puts + +puts 'z:' +eval('p z rescue puts $!',$b0) +eval('p z rescue puts $!',$b1) +eval('p z',$b2) +eval('p z rescue puts $!',$b3) +eval('p z rescue puts $!',$b4) +puts + +puts 'u:' +eval('p u rescue puts $!',$b0) +eval('p u rescue puts $!',$b1) +eval('p u rescue puts $!',$b2) +eval('p u rescue puts $!',$b3) +eval('p u rescue puts $!',$b4) +puts + diff --git a/merlin/main/languages/ruby/Experimental/Eval/ClassDefScope.rb b/merlin/main/languages/ruby/Experimental/Eval/ClassDefScope.rb new file mode 100644 index 0000000000..6b00dcc982 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Eval/ClassDefScope.rb @@ -0,0 +1,12 @@ +z = 1 +class C + x = 1 + eval("y = 1") + puts x + eval("puts y") + #puts z # error - doesn't close over +end + +#puts x #error +#eval("puts y") #error +puts z \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Eval/Closures.rb b/merlin/main/languages/ruby/Experimental/Eval/Closures.rb new file mode 100644 index 0000000000..89c1c39130 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Eval/Closures.rb @@ -0,0 +1,15 @@ +def foo + if (false) + x = 1 + end + + 1.times { + y = 2 + 1.times { + z = 3 + p x,y,z + } + } +end + +foo \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Eval/CondDef.rb b/merlin/main/languages/ruby/Experimental/Eval/CondDef.rb new file mode 100644 index 0000000000..112d5f0f26 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Eval/CondDef.rb @@ -0,0 +1,27 @@ +def foo + $x = $x + 1 + + x = $x + if $d + y { puts x } + end +end + +def y &p + p[] + $p = p +end + +$x = 0 +$d = true + +foo +$p[] + +$d = false +foo +$p[] + + + + diff --git a/merlin/main/languages/ruby/Experimental/Eval/EvalClosure.rb b/merlin/main/languages/ruby/Experimental/Eval/EvalClosure.rb new file mode 100644 index 0000000000..5333bb923c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Eval/EvalClosure.rb @@ -0,0 +1,51 @@ +def foo + x = 1 + eval("y=2") + eval("z = 3; 1.times { puts x,y,z }") + eval("puts x,y,z") +end + +def bar + a = 0 + eval <<-A + x = 1 + + eval <<-B + y = 2 + + puts a,x,y + B + + $b = binding + + # puts y # error: at the time the eval was compiled 'y' wasn't known to be a local + A + + eval <<-A + puts a, x, y + A +end + +def baz + eval <<-A, $b + puts a, x, y + A +end + +def g + 1.times { + x = 1 + eval("y = 1") # y goes to dynamic dictionary on the block's local scope + } + + #puts x #error + eval("puts y") rescue p $! # error +end + +foo +puts '-' * 20 +bar +puts '-' * 20 +baz +puts '-' * 20 +g \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Eval/Locals.py b/merlin/main/languages/ruby/Experimental/Eval/Locals.py new file mode 100644 index 0000000000..297354d8ce --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Eval/Locals.py @@ -0,0 +1,59 @@ +def foo(): + y = 1 + + def bar(): + z1 = 2 + z2 = 3 + + def baz(): + w1 = 4 + w2 = 5 + w3 = 6 + print y,z1,z2 + print eval("y + z1 + w1") + + return baz + + return bar + +b = foo() +c = b() +c() + +print '---' + +def gen(): + x = 1 + yield 1 + print x + yield 2 + print x + yield 3 + print x + +for z in gen(): + print z + +def make_tuple(): + return [10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10] + +def big(): + t = make_tuple() + a0,b0,c0,d0,e0, f0,g0,h0,i0,j0, k0,l0,m0,n0,o0, p0,q0,r0,s0,t0, u0,v0,w0,x0,y0 = t + a1,b1,c1,d1,e1, f1,g1,h1,i1,j1, k1,l1,m1,n1,o1, p1,q1,r1,s1,t1, u1,v1,w1,x1,y1 = t + a2,b2,c2,d2,e2, f2,g2,h2,i2,j2, k2,l2,m2,n2,o2, p2,q2,r2,s2,t2, u2,v2,w2,x2,y2 = t + a3,b3,c3,d3,e3, f3,g3,h3,i3,j3, k3,l3,m3,n3,o3, p3,q3,r3,s3,t3, u3,v3,w3,x3,y3 = t + a4,b4,c4,d4,e4, f4,g4,h4,i4,j4, k4,l4,m4,n4,o4, p4,q4,r4,s4,t4, u4,v4,w4,x4,y4 = t + a5,b5 = 1,2 + a01,b01,c01,d01,e01, f01,g01,h01,i01,j01, k01,l01,m01,n01,o01, p01,q01,r01,s01,t01, u01,v01,w01,x01,y01 = t + a11,b11,c11,d11,e11, f11,g11,h11,i11,j11, k11,l11,m11,n11,o11, p11,q11,r11,s11,t11, u11,v11,w11,x11,y11 = t + a21,b21,c21,d21,e21, f21,g21,h21,i21,j21, k21,l21,m21,n21,o21, p21,q21,r21,s21,t21, u21,v21,w21,x21,y21 = t + a31,b31,c31,d31,e31, f31,g31,h31,i31,j31, k31,l31,m31,n31,o31, p31,q31,r31,s31,t31, u31,v31,w31,x31,y31 = t + a41,b41,c41,d41,e41, f41,g41,h41,i41,j41, k41,l41,m41,n41,o41, p41,q41,r41,s41,t41, u41,v41,w41,x41,y41 = t + a51,b51,c51 = 1,2,3 + + a02 = 1 + exec("print 'big'") + print locals() + +big() \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Eval/Locals.rb b/merlin/main/languages/ruby/Experimental/Eval/Locals.rb new file mode 100644 index 0000000000..1898fe470b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Eval/Locals.rb @@ -0,0 +1,14 @@ +def foo + eval <<-A + x = 1 + eval <<-B + y = 2 + puts x + x = 3 + B + A + + eval('puts x,y'); +end + +foo \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/ARGF.rb b/merlin/main/languages/ruby/Experimental/Globals/ARGF.rb new file mode 100644 index 0000000000..2f0c5e660d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/ARGF.rb @@ -0,0 +1,3 @@ +p $< + +p $<.path \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Alias.rb b/merlin/main/languages/ruby/Experimental/Globals/Alias.rb new file mode 100644 index 0000000000..d7a58633c6 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Alias.rb @@ -0,0 +1,50 @@ +vars = [ + +#regular: +'foo', + +#special: +'_', +'!', +'@', +'~', +',', +';', +'/', +'\\', +'*', +'$', +'?', +'=', +':', +'"', +'<', +'>', +'.', +'0', + +#backrefs: +'&', +'+', +'`', +"'", +'+', +'~', + +#nrefs: +'1', + +] + +puts "Failed:" + +vars.each { |v| + vars.each { |w| + a = "alias $#{v} $#{w}" + begin + eval(a) + rescue SyntaxError + puts a + end + } +} diff --git a/merlin/main/languages/ruby/Experimental/Globals/Aliases.rb b/merlin/main/languages/ruby/Experimental/Globals/Aliases.rb new file mode 100644 index 0000000000..0a758e1ea3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Aliases.rb @@ -0,0 +1,51 @@ +alias $foo $bar + +p defined? $foo +p defined? $bar + +$bar = nil + +p defined? $foo +p defined? $bar + +$bar = 1 + +p defined? $foo +p defined? $bar + +puts $foo + +p defined? $foo +p defined? $bar + +alias $foo2 $bar2 + +$foo2 = 2 +puts $bar2 + +puts '-0-' + +p defined? $-a +alias $dasha $-a +p defined? $dasha + +puts '-1-' + +p defined? $- +alias $dash $- +p defined? $dash + +puts '-2-' + +p defined? $+ +alias $plus1 $+ +p defined? $plus1 + +puts '-3-' + +"x" =~ /(x)/ +p defined? $+ +alias $plus2 $+ +p defined? $plus2 # doesn't go thru alias, but the difference is only in 1.8 + + diff --git a/merlin/main/languages/ruby/Experimental/Globals/Backslash.rb b/merlin/main/languages/ruby/Experimental/Globals/Backslash.rb new file mode 100644 index 0000000000..e9c0444cb5 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Backslash.rb @@ -0,0 +1,29 @@ +$x = "a" +$\ = "b" + +alias $old_slash $\ +alias $\ $x + +p $\ + +puts "---" +print '123' # 123b -> ignores alias +puts "","---" + +p $\.object_id == $\.object_id + +alias $\ $old_slash # restore $\ + +puts '-' * 20 + +$\ = nil +($\ = 1) rescue puts $! + +class S + def to_s; "bob"; end + def to_str; "bob"; end +end + +($\ = S.new) rescue puts $! + + diff --git a/merlin/main/languages/ruby/Experimental/Globals/Comma.rb b/merlin/main/languages/ruby/Experimental/Globals/Comma.rb new file mode 100644 index 0000000000..79f4b2bf40 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Comma.rb @@ -0,0 +1,29 @@ +$x = "a" +$, = "b" + +alias $old_slash $, +alias $, $x + +p $, + +puts "---" +print '123','456' # 123b456 -> ignores alias +puts "","---" + +p $,.object_id == $,.object_id + +alias $, $old_slash # restore $, + +puts '-' * 20 + +$, = nil +($, = 1) rescue puts $! + +class S + def to_s; "bob"; end + def to_str; "bob"; end +end + +($, = S.new) rescue puts $! + + diff --git a/merlin/main/languages/ruby/Experimental/Globals/Constants.rb b/merlin/main/languages/ruby/Experimental/Globals/Constants.rb new file mode 100644 index 0000000000..62f213e739 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Constants.rb @@ -0,0 +1,34 @@ +p ARGV.object_id == $*.object_id + +ARGV = [] # warning + +p ARGV.object_id == $*.object_id + +$foo = [1,2,3] +alias $* $foo + +p ARGV # ignores alias + + +p '-' * 50 + +p DATA.class +p DATA + +p RUBY_PLATFORM +p RUBY_RELEASE_DATE +p RUBY_VERSION + +p PLATFORM +p RELEASE_DATE +p VERSION + +p TOPLEVEL_BINDING + +SCRIPT_LINES__ = {} +require "Require.1.rb" +p SCRIPT_LINES__ + +__END__ +foo +bar \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Dot.rb b/merlin/main/languages/ruby/Experimental/Globals/Dot.rb new file mode 100644 index 0000000000..b1a9de8f7b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Dot.rb @@ -0,0 +1 @@ +puts $. \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Exceptions.rb b/merlin/main/languages/ruby/Experimental/Globals/Exceptions.rb new file mode 100644 index 0000000000..46a52e4fb2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Exceptions.rb @@ -0,0 +1,91 @@ +def foo + raise 'Foo' +end + +puts 1 +p $! +p $@ + +begin + foo +rescue + puts 2 + p $! + p $@ +ensure + puts 3 + p $! + p $@ +end + +puts 4 +p $! +p $@ + +puts +puts '### assignment: ' +puts + +def try_set(value) + begin + $@ = value + rescue + puts "#{value.inspect}: #{$!.class}('#{$!}')" + else + puts "#{value.inspect}: OK" + end +end + +try_set ["A"] +try_set 1 + +begin + foo +rescue + try_set nil + try_set [] + try_set ["foo"] + try_set 1 + try_set [1] + try_set ["foo", 1] + + class BobArray + def to_ary + ['BobArray'] + end + end + + class BobString + def to_s + 'BobString' + end + end + + try_set BobArray.new + try_set [BobString.new] + + class SubArray < Array + end + + class SubString < String + end + + try_set SubArray.new + try_set [SubString.new] + + $! = nil + try_set ["B"] +end + +puts +puts '### identity: ' +puts + +$! = Exception.new +x = ["hello"] +p ($@ = x) rescue puts "Error: #{$!}" +p $@.object_id +p $!.backtrace +p $!.backtrace.object_id +p $!.backtrace.object_id + diff --git a/merlin/main/languages/ruby/Experimental/Globals/Exceptions2.rb b/merlin/main/languages/ruby/Experimental/Globals/Exceptions2.rb new file mode 100644 index 0000000000..02bcad7c5e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Exceptions2.rb @@ -0,0 +1,44 @@ +class Exception + remove_method "backtrace" + #remove_method "backtrace=" # bug terminates process ??? +end + +module M + def backtrace + puts 'getter' + ['foo', 'bar', @x] + end + + def backtrace=(value) + puts 'setter' + p value + end +end + +class C < Exception + include M + + def initialize(x) + @x = x + end +end + +puts 'Hello' + +$! = C.new "0" +p $@ # [foo, bar, 0] +$! = C.new "X" + +alias $! $foo +$foo = C.new "1" +$! = C.new "2" +p $@ # [foo, bar, X] ignores alias + +puts "-"*50 + +#$@ = 123 # ??? bug: segfault? +$@ = ['f','g'] # ??? bug: doesn't call setter +p $@ + +puts 'Done' + diff --git a/merlin/main/languages/ruby/Experimental/Globals/Exceptions3.rb b/merlin/main/languages/ruby/Experimental/Globals/Exceptions3.rb new file mode 100644 index 0000000000..b145f4c6f1 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Exceptions3.rb @@ -0,0 +1,13 @@ +class Exception + remove_method "backtrace" +end + +puts 'Hello' + +begin + raise # crash +rescue + p $@ +end + +puts 'Bye' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Exceptions4.rb b/merlin/main/languages/ruby/Experimental/Globals/Exceptions4.rb new file mode 100644 index 0000000000..425dee1ead --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Exceptions4.rb @@ -0,0 +1,26 @@ +$var = 123 + +alias $true_bang $! +alias $! $var + +begin + raise IOError.new +rescue + puts 'rescue' + p $true_bang + p $! +ensure + # oldEx = GetCurrentException - nil here + puts 'ensure' + p $true_bang + p $! + + $true_bang = Exception.new "1" + $! = Exception.new "2" + + # SetCurrentException(oldEx) - restores nil +end + +puts 'end' +p $true_bang # restored by ensure +p $! # not restored by ensure \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Exceptions5.rb b/merlin/main/languages/ruby/Experimental/Globals/Exceptions5.rb new file mode 100644 index 0000000000..4e04e22724 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Exceptions5.rb @@ -0,0 +1,27 @@ +# rescue ignores $! aliases + +class E < Exception +end + +$goo = 123 +alias $! $goo + +def foo + puts 'compared with foo' + IOError +end + +def bar + puts 'compared with bar' + E +end + +begin + raise E.new +rescue foo + puts 'rescued foo' +rescue bar + puts 'rescued bar' +rescue + puts 'rescued default' +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Exceptions6.rb b/merlin/main/languages/ruby/Experimental/Globals/Exceptions6.rb new file mode 100644 index 0000000000..c94bf2cf96 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Exceptions6.rb @@ -0,0 +1,20 @@ +$foo = 123 + +p $! = IOError.new + +# hides real $! +alias $! $foo + +p $! #123 + +begin + raise $! rescue puts 'Cast error' + raise # uses real $!, not alias +rescue IOError => e + p e.class # fixnum (uses alias) + p $!.class # fixnum (uses alias) + + p $@ # real stack trace (doesn't use alias) +end + + diff --git a/merlin/main/languages/ruby/Experimental/Globals/Filename.rb b/merlin/main/languages/ruby/Experimental/Globals/Filename.rb new file mode 100644 index 0000000000..e010185566 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Filename.rb @@ -0,0 +1,29 @@ +p $<.filename +p $FILENAME + +class << $< + alias old_filename filename + + def filename + "foo" + end +end + +p $<.filename +p $FILENAME # not foo -> doesn't call the method via dynamic dispatch + +class << $< + remove_method :filename +end + +p $<.filename rescue p $! +p $FILENAME + +class << $< + alias filename old_filename +end + +p $<.filename +p $FILENAME + +($FILENAME = "goo") rescue p $! \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Flags.rb b/merlin/main/languages/ruby/Experimental/Globals/Flags.rb new file mode 100644 index 0000000000..7f41a663d2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Flags.rb @@ -0,0 +1,22 @@ +class C +end + +$DEBUG = C.new +p $DEBUG + +p $VERBOSE + +$VERBOSE = C.new +p $VERBOSE + +$VERBOSE = 0 +p $VERBOSE + +$VERBOSE = false +p $VERBOSE + +$VERBOSE = true +p $VERBOSE + +$VERBOSE = nil +p $VERBOSE \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Process.rb b/merlin/main/languages/ruby/Experimental/Globals/Process.rb new file mode 100644 index 0000000000..b765cb9a45 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Process.rb @@ -0,0 +1,3 @@ +p $0 + +gets \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Regex.rb b/merlin/main/languages/ruby/Experimental/Globals/Regex.rb new file mode 100644 index 0000000000..8c44a4cfdb --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Regex.rb @@ -0,0 +1,86 @@ +alias $X $~ + +"prefix-abcdefghijk-suffix" =~ /(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)/ + +puts 'NTHREF:' +puts "$1 = " + $1.inspect +puts "$2 = " + $2.inspect +puts "$3 = " + $3.inspect +puts "$4 = " + $4.inspect +puts "$5 = " + $5.inspect +puts "$6 = " + $6.inspect +puts "$7 = " + $7.inspect +puts "$8 = " + $8.inspect +puts "$9 = " + $9.inspect +puts "$10 = " + $10.inspect +puts "$11 = " + $11.inspect +puts "$12 = " + $12.inspect +puts "$13 = " + $13.inspect + +puts 'BACKREF:' +puts "$& = " + $&.inspect +puts "$` = " + $`.inspect +puts "$' = " + $'.inspect +puts "$+ = " + $+.inspect + +puts "$= = " + $=.inspect +puts "$~ = " + $~.inspect +puts "$X = " + $X.inspect + +puts '-' * 100 + +"PREFIX-ABC-SUFFIX" =~ /(A)(B)(C)/ + +puts 'NTHREF:' +puts "$1 = " + $1.inspect +puts "$2 = " + $2.inspect +puts "$3 = " + $3.inspect +puts "$4 = " + $4.inspect +puts "$5 = " + $5.inspect +puts "$6 = " + $6.inspect +puts "$7 = " + $7.inspect +puts "$8 = " + $8.inspect +puts "$9 = " + $9.inspect +puts "$10 = " + $10.inspect +puts "$11 = " + $11.inspect +puts "$12 = " + $12.inspect +puts "$13 = " + $13.inspect + +puts 'BACKREF:' +puts "$& = " + $&.inspect +puts "$` = " + $`.inspect +puts "$' = " + $'.inspect +puts "$+ = " + $+.inspect + +puts "$= = " + $=.inspect +puts "$~ = " + $~.inspect +puts "$X = " + $X.inspect + +puts '-' * 100 + +puts "$~ = " + $~.inspect +puts "$X = " + $X.inspect + +puts "$~ = " + $~.inspect +puts "$X = " + $X.inspect + +puts '-' * 100 + +def foo + puts "$~ = " + $~.inspect + puts "$X = " + $X.inspect +end + +foo + +puts '-'*100 + +1.times { + "hello" =~ /hello/ +} + +puts "$~ = " + $~.inspect +puts "$X = " + $X.inspect + + + diff --git a/merlin/main/languages/ruby/Experimental/Globals/RegexAlias.rb b/merlin/main/languages/ruby/Experimental/Globals/RegexAlias.rb new file mode 100644 index 0000000000..a2f35bda24 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/RegexAlias.rb @@ -0,0 +1,26 @@ +# aliasing match globals is weird, the aliases could be made but are ignored + +$foo = 123 + +alias $1 $foo +alias $2 $foo +alias $3 $foo + +alias $~ $foo # not-ignored + +alias $& $foo +alias $+ $foo +alias $` $foo + +"abc" =~ /(a)(b)(c)/ + +p $1 +p $2 +p $3 + +p $~ +p $& +p $+ +p $` + +p global_variables.sort \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Require.1.rb b/merlin/main/languages/ruby/Experimental/Globals/Require.1.rb new file mode 100644 index 0000000000..63bb43aa4c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Require.1.rb @@ -0,0 +1 @@ +puts 'Loaded: 1' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Require.2.rb b/merlin/main/languages/ruby/Experimental/Globals/Require.2.rb new file mode 100644 index 0000000000..28509c8b73 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Require.2.rb @@ -0,0 +1 @@ +puts 'Loaded: 2' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Require.rb b/merlin/main/languages/ruby/Experimental/Globals/Require.rb new file mode 100644 index 0000000000..db6732bdc5 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Require.rb @@ -0,0 +1,57 @@ +p $" # [] + +require "Require.1.rb" # Loaded: 1 + +p $" + +$"[0] = 1 +p $" + +require "Require.1.rb" rescue p $! + +$"[0] = nil +p $" + +require "Require.1.rb" rescue p $! + +$"[0] = "Require.1.rb" +p $" + +require "Require.1.rb" # Loaded: 1 + +class S + def to_s; "Require.1.rb"; end +end + +$"[0] = S.new +p $" + +puts '-' * 20 + +require "Require.1.rb" rescue p $! + +$"[0] = "Require.1.rb" +p $" + +puts '-' * 20 + +$foo = ["Require.2.rb"] + +alias $old_q $" +alias $" $foo + +p $" +require "Require.2.rb" + +alias $" $old_q + +puts '-' * 20 + +($" = []) rescue p $! + +$".delete_at 0 +$".delete_at 0 +p $" + +require "Require.1" +p $" \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/RequirePath.rb b/merlin/main/languages/ruby/Experimental/Globals/RequirePath.rb new file mode 100644 index 0000000000..8640e671ae --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/RequirePath.rb @@ -0,0 +1,56 @@ +def try_require path + require path +rescue LoadError + puts $!.class +rescue + p $! +end + +def clear + $:.clear + $".clear +end + +p $: # [] + + +puts '-' * 100 + + +clear + +try_require "Require.1.rb" # Error + +$:[0] = "." +try_require "Require.1.rb" # Loaded: 1 +clear + +$:[0] = 1 +try_require "Require.1.rb" # Error +clear + +class S + def to_s; "Require.1.rb"; end +end + +$:[0] = S.new +try_require "Require.1.rb" # Error +clear + +puts '-' * 100 + +p $: +$foo = [] + +alias $old_colon $: +alias $: $foo + +p $old_colon +p $: + +try_require "Require.1.rb" # Error -> alias ignored + +alias $: $old_colon + + + diff --git a/merlin/main/languages/ruby/Experimental/Globals/Safe.rb b/merlin/main/languages/ruby/Experimental/Globals/Safe.rb new file mode 100644 index 0000000000..6b5a4a45c8 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Safe.rb @@ -0,0 +1,15 @@ +p $SAFE +$SAFE += 1 +p $SAFE +($SAFE -= 1) rescue p $! +p $SAFE + +class I + def to_i + 2 + end +end + +($SAFE = "foo") rescue p $! +($SAFE = I.new) rescue p $! + diff --git a/merlin/main/languages/ruby/Experimental/Globals/Semicolon.rb b/merlin/main/languages/ruby/Experimental/Globals/Semicolon.rb new file mode 100644 index 0000000000..55710d1973 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Semicolon.rb @@ -0,0 +1,39 @@ +puts "Initial value" +p $; + +$x = "a" +$; = "b" + +alias $old_slash $; +alias $; $x + +p $; + +puts "---" +p '123a456b789'.split # [123a456,789] -> ignores alias +puts "","---" + +p $;.object_id == $;.object_id + +alias $; $old_slash # restore $; + +puts '-' * 20 + +$; = nil +p '123a456b789'.split # [123a456b789] + +$; = 5 +p '123a456b789'.split rescue p $! # `split': wrong argument type Fixnum (expected Regexp) + +class S + def to_s; "a"; end + def to_str; "b"; end +end + +$; = S.new +p '123a456b789'.split rescue p $! # ["123a456", "789"] + +puts 'Done' + + + diff --git a/merlin/main/languages/ruby/Experimental/Globals/Singletons.rb b/merlin/main/languages/ruby/Experimental/Globals/Singletons.rb new file mode 100644 index 0000000000..883e77f2c5 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Singletons.rb @@ -0,0 +1,35 @@ +class << Object.new + p included_modules + p instance_methods(false) +end + +puts '---' + +p ENV.class +class << ENV + p included_modules + p instance_methods(false) - Enumerable.instance_methods(false) +end + +puts '---' + +p ENV["TEMP"] + +ENV["TEMP"] = 'goo' +p `echo %TEMP%` + +puts '---' + +p ARGF.class +class << ARGF + p included_modules + p instance_methods(false) - Enumerable.instance_methods(false) +end + +puts '---' + +p ARGV.class +class << ARGV + p included_modules + p instance_methods(false) - Array.instance_methods(false) +end diff --git a/merlin/main/languages/ruby/Experimental/Globals/Specials.rb b/merlin/main/languages/ruby/Experimental/Globals/Specials.rb new file mode 100644 index 0000000000..48635c4eab --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Specials.rb @@ -0,0 +1,27 @@ +alias $! $foo + +$foo = 1 +p $! + +begin + raise +rescue Exception => e + p e.class +end + + +=begin + +($! = 'boo') rescue puts 'Error' + +alias $! $foo + +begin + raise Exception, "foo", ["f", "g", "h"] +rescue Exception => $e + p $e +end + + + +=end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Stdout.rb b/merlin/main/languages/ruby/Experimental/Globals/Stdout.rb new file mode 100644 index 0000000000..9ab4a4f1a8 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Stdout.rb @@ -0,0 +1,40 @@ +$old_out = $> + +out = [] + +out << ($>.object_id == $stdout.object_id) + +$> = File.open("foo.txt", "w+") + +out << ($>.object_id == $stdout.object_id) # true + +$> = $old_out + +puts out +out = [] + +puts '-' * 20 + +$x = 123 +alias $old_gt $> +alias $> $x + +puts 'foo' # ignores alias +p $> # 123 + +alias $> $old_gt #restore + +puts '-' * 20 + +class W + def write x + $old_out.write "[#{x}]" + end +end + +($> = 1) rescue p $! +$> = W.new +puts 'foo' + + + diff --git a/merlin/main/languages/ruby/Experimental/Globals/Under.rb b/merlin/main/languages/ruby/Experimental/Globals/Under.rb new file mode 100644 index 0000000000..646ad55d6a --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Under.rb @@ -0,0 +1,104 @@ +$foo = 123 + +alias $under $_ +alias $_ $foo + +gets +p $_, $foo, $under #123, 123, foo -> gets goes directly to the $_ variable + +puts '-' * 20 + +def foo + p $_, $foo, $under #123, 123, nil -> $_ is on scope +end + +foo + +puts '-' * 20 + +$_ = 'bob' +$_ = 1 + +$/ = 'bob' +($/ = 1) rescue p $! + +alias $goo $/ +($goo = 1) rescue p $! + +class S + def to_s + "foo" + end + def to_str + "bar" + end +end + +($/ = S.new) rescue p $! +$/ = nil + +puts '-' * 20 + +$x = "a" +$/ = "b" + +alias $old_slash $/ +alias $/ $x + +p $/ +p gets # "12a34b" -> doesn't go thru alias + +puts '-' * 20 + +p $/.object_id == $/.object_id + +alias $/ $old_slash # restore $/ + +puts '-' * 20 + +$/ = "\n" +p gets + +p $. #3 +p gets #3 +p $. #4 +p gets #4 +p $. #5 + +$. = 1 +p gets #5 !!! +p $. #2 !!! + +p gets #5 !!! +p $. #2 !!! + +puts '-' * 20 + +($. = 'foo') rescue p $! + +alias $_dot $. +($_dot = 'foo') rescue p $! + +class I + def to_i + 1 + end +end + +($. = I.new) rescue p $! + +puts '-' * 20 + +alias $old_dot $. +alias $. $y + +$old_dot = 20 +$y = 30 + +gets +p $., $y, $old_dot # 30, 30, 21 -> gets ignores alias + +alias $. $old_dot #restore $. + + + diff --git a/merlin/main/languages/ruby/Experimental/Globals/Under.txt b/merlin/main/languages/ruby/Experimental/Globals/Under.txt new file mode 100644 index 0000000000..d442d854f2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Under.txt @@ -0,0 +1,13 @@ +foo +12a34b56 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Under2.rb b/merlin/main/languages/ruby/Experimental/Globals/Under2.rb new file mode 100644 index 0000000000..ec64c7c725 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Under2.rb @@ -0,0 +1,36 @@ +p $* +p $*.object_id == $*.object_id + +puts '-' * 20 + +alias $old_star $* +alias $* $z + +$old_start0 = $old_star[0] +$old_star[0] = "Under.txt" +$z = ["Under2.txt"] + +p gets # "foo" -> alias ignored + +alias $* $old_star #restore $* +$old_star[0] = $old_start0 #restore $*[0] + +p $* +p gets +p gets +p gets +p gets +p gets +p gets +p gets +p gets +p gets +p gets +p gets +p $* +p gets +p $* +p gets +p $* +p gets +p $* \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/Under2.txt b/merlin/main/languages/ruby/Experimental/Globals/Under2.txt new file mode 100644 index 0000000000..e0304e454d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/Under2.txt @@ -0,0 +1,5 @@ +A +B +C +D +E \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/__FILE__/a/b/x.rb b/merlin/main/languages/ruby/Experimental/Globals/__FILE__/a/b/x.rb new file mode 100644 index 0000000000..6cd28ce1e3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/__FILE__/a/b/x.rb @@ -0,0 +1,3 @@ +puts "$0 = #{$0}" +puts "__FILE__ = #{__FILE__}" +p __FILE__ == $0 diff --git a/merlin/main/languages/ruby/Experimental/Globals/__FILE__/a/y.rb b/merlin/main/languages/ruby/Experimental/Globals/__FILE__/a/y.rb new file mode 100644 index 0000000000..22d639255f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/__FILE__/a/y.rb @@ -0,0 +1,5 @@ +puts "$0 = #{$0}" +puts "__FILE__ = #{__FILE__}" +p __FILE__ == $0 + +require 'b\x.rb' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/__FILE__/test.rb b/merlin/main/languages/ruby/Experimental/Globals/__FILE__/test.rb new file mode 100644 index 0000000000..f3543b0cdf --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/__FILE__/test.rb @@ -0,0 +1,8 @@ +$:.clear +$: << '.\a' + +puts "$0 = #{$0}" +puts "__FILE__ = #{__FILE__}" +puts __FILE__ == $0 + +require 'y' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/dash.rb b/merlin/main/languages/ruby/Experimental/Globals/dash.rb new file mode 100644 index 0000000000..018773e6c6 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/dash.rb @@ -0,0 +1,2 @@ +$- = 'hello' +puts $- \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Globals/foo.txt b/merlin/main/languages/ruby/Experimental/Globals/foo.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Experimental/Globals/global_names.rb b/merlin/main/languages/ruby/Experimental/Globals/global_names.rb new file mode 100644 index 0000000000..4fe9eb98db --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/global_names.rb @@ -0,0 +1,19 @@ +(32..255).each { |c| + x = "x" + x[0] = c + + begin + eval("a = $-#{x}") + rescue SyntaxError + val = false + rescue TypeError + val = true + else + val = true + end + + puts "#{c}: #{x}" if val +} + + + diff --git a/merlin/main/languages/ruby/Experimental/Globals/invalid1.rb b/merlin/main/languages/ruby/Experimental/Globals/invalid1.rb new file mode 100644 index 0000000000..aaab261517 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/invalid1.rb @@ -0,0 +1 @@ +$0a = 1 # error diff --git a/merlin/main/languages/ruby/Experimental/Globals/invalid2.rb b/merlin/main/languages/ruby/Experimental/Globals/invalid2.rb new file mode 100644 index 0000000000..de1f2cccd3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Globals/invalid2.rb @@ -0,0 +1 @@ +$0123 = 1 # error diff --git a/merlin/main/languages/ruby/Experimental/IO/FileOpenModes.rb b/merlin/main/languages/ruby/Experimental/IO/FileOpenModes.rb new file mode 100644 index 0000000000..15883e87af --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/IO/FileOpenModes.rb @@ -0,0 +1,50 @@ +path = "C:\\temp\\ruby.txt" + +["rb", +nil, +"", +"r", +"w", +"b", +"t", +"a", + +"+", +"r+", +"w+", +"b+", +"t+", +"a+", + +"rb", +"wb", +"ab", +"rt", +"wt", +"at", + +"rb+", +"wb+", +"ab+", +"rt+", +"wt+", +"at+", + +"br", +"bw", +"ba", +"tr", +"tw", +"bt", + +"rrb+", + +].each { |mode| + begin + open(path, mode) { } + rescue + puts "#{mode.inspect} -> #{$!.inspect}" + else + puts "#{mode.inspect} -> ok" + end +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/IO/Files/cr_lf_eolns.txt b/merlin/main/languages/ruby/Experimental/IO/Files/cr_lf_eolns.txt new file mode 100644 index 0000000000..422c2b7ab3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/IO/Files/cr_lf_eolns.txt @@ -0,0 +1,2 @@ +a +b diff --git a/merlin/main/languages/ruby/Experimental/IO/Files/empty.txt b/merlin/main/languages/ruby/Experimental/IO/Files/empty.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Experimental/IO/mock.rb b/merlin/main/languages/ruby/Experimental/IO/mock.rb new file mode 100644 index 0000000000..f3101972c9 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/IO/mock.rb @@ -0,0 +1,58 @@ +class I + def initialize val + @val = val + end + + def to_int + puts '!I to_int' + @val + end + + def respond_to? name + puts "?I #{name}" + super + end +end + +class S + def initialize val + @val = val + end + + def respond_to? name + puts "?S #{name}" + super + end + + def to_str + puts '!S to_str' + @val + end +end + + +class ES + def initialize val + @val = val + end + + def respond_to? name + puts "?ES #{name}" + super + end + + def to_s + puts '!ES to_s' + @val + end +end + +class W + def write x + puts ">#{x}<" + end +end + +class MyStr < String + +end diff --git a/merlin/main/languages/ruby/Experimental/IO/printf.rb b/merlin/main/languages/ruby/Experimental/IO/printf.rb new file mode 100644 index 0000000000..7f4578d314 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/IO/printf.rb @@ -0,0 +1,15 @@ +$a = [] +class << $stdout + def write *args + $a << args + end +end + +alias $stdout $foo +printf("%d %d", 1, 2) + +class << STDOUT + remove_method :write +end + +p $a \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/IO/read.rb b/merlin/main/languages/ruby/Experimental/IO/read.rb new file mode 100644 index 0000000000..93298ab6a1 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/IO/read.rb @@ -0,0 +1,12 @@ +puts 'mode b' + +File.open("Files/cr_lf_eolns.txt", "rb") { |f| + 6.times { p f.getc() } +} + +puts +puts 'mode t' + +File.open("Files/cr_lf_eolns.txt", "r") { |f| + 6.times { p f.getc() } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/IO/select.rb b/merlin/main/languages/ruby/Experimental/IO/select.rb new file mode 100644 index 0000000000..1321e22fc2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/IO/select.rb @@ -0,0 +1,23 @@ +$: << 'C:\M0\Merlin\Main\Languages\Ruby\Libs' + +require 'socket' + +serv = TCPServer.new("127.0.0.1", 0) +af, port, host, addr = serv.addr + + +c = TCPSocket.new(addr, port) +s = serv.accept + +t1 = Thread.new { + sleep(3) + c.send "aaa", 0 +} + +puts 'waiting' +IO.select([s]) + +p s.recv_nonblock(10) + +t1.join + diff --git a/merlin/main/languages/ruby/Experimental/IO/sockets.rb b/merlin/main/languages/ruby/Experimental/IO/sockets.rb new file mode 100644 index 0000000000..8ed946dfad --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/IO/sockets.rb @@ -0,0 +1,62 @@ +$: << 'C:\M1\Merlin\Main\Languages\Ruby\Libs' + +require 'socket' +require 'fcntl' +require 'thread' + +class WebServer + def initialize + @tokens = SizedQueue.new(5) + 5.times{ @tokens.push(nil) } + end + + def run + thgroup = ThreadGroup.new + @status = :Running + @listeners = [TCPServer.new("127.0.0.1", 3000)] + while @status == :Running + puts "#{Thread.current.inspect}: waiting" + if svrs = IO.select(@listeners, nil, nil, 2.0) + puts "#{Thread.current.inspect}: selected #{svrs[0]}" + + svrs[0].each{|svr| + @tokens.pop # blocks while no token is there. + if sock = accept_client(svr) + th = start_thread(sock) + th[:WEBrickThread] = true + thgroup.add(th) + else + @tokens.push(nil) + end + } + end + end + end + + def accept_client(svr) + puts "#{Thread.current.inspect}: accept_client #{svr}" + sock = nil + sock = svr.accept + puts "#{Thread.current.inspect}: accepted #{sock}" + sock.sync = true + return sock + end + + + def start_thread(sock, &block) + Thread.start{ + Thread.current[:WEBrickSocket] = sock + addr = sock.peeraddr + puts "#{Thread.current.inspect}: working, peer = #{addr.inspect}" + + @tokens.push(nil) + + Thread.current[:WEBrickSocket] = nil + + puts "#{Thread.current.inspect}: close, peer = #{addr.inspect}" + sock.close + } + end +end + +WebServer.new.run diff --git a/merlin/main/languages/ruby/Experimental/IO/warning.rb b/merlin/main/languages/ruby/Experimental/IO/warning.rb new file mode 100644 index 0000000000..c993d04d30 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/IO/warning.rb @@ -0,0 +1,57 @@ +require 'mock' + +p $VERBOSE, $DEBUG + +putc I.new(64) + +class C + def initialize open, close + @out = IO.new(1, "w") + @open = open + @close = close + end + + def write x + @out << @open + @out << x + @out << @close + @out.flush + end +end + +$out = $stdout +$stderr = C.new '<', '>' +$stdout = C.new '[', ']' + +def f *a; end + +puts 'bar' + +# syntax warnings: +eval('f f 1') +eval('f (1)') +eval('f ()') +begin + eval("?\n") +rescue SyntaxError +end + +# runtime warnings: +warn S.new("string warning") +ENV.indices +"".type + +$VERBOSE = false +warn "verbose = #$VERBOSE" + +$VERBOSE = true +warn "verbose = #$VERBOSE" + +$VERBOSE = nil +warn "verbose = #$VERBOSE" # no warning reported + +$XXX = true +alias $VERBOSE $XXX +puts $VERBOSE +warn "verbose = #$VERBOSE" # no warning reported + diff --git a/merlin/main/languages/ruby/Experimental/Loader/Absolute/a.rb b/merlin/main/languages/ruby/Experimental/Loader/Absolute/a.rb new file mode 100644 index 0000000000..1e71fa5e93 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Absolute/a.rb @@ -0,0 +1 @@ +puts 'hello' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Absolute/test.rb b/merlin/main/languages/ruby/Experimental/Loader/Absolute/test.rb new file mode 100644 index 0000000000..3ee2858567 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Absolute/test.rb @@ -0,0 +1,7 @@ +$:.clear + +$".clear +require 'C:\temp\load\Absolute\a' + +$".clear +require 'C:\temp\load\Absolute\a.rb' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Assemblies/short.rb b/merlin/main/languages/ruby/Experimental/Loader/Assemblies/short.rb new file mode 100644 index 0000000000..3a74c27a87 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Assemblies/short.rb @@ -0,0 +1 @@ +require 'System' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/DottedNames/test.rb b/merlin/main/languages/ruby/Experimental/Loader/DottedNames/test.rb new file mode 100644 index 0000000000..8a81728354 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/DottedNames/test.rb @@ -0,0 +1,6 @@ +require 'x.y' +begin + require 'z.rb' +rescue Exception + puts $! +end diff --git a/merlin/main/languages/ruby/Experimental/Loader/DottedNames/x.y.rb b/merlin/main/languages/ruby/Experimental/Loader/DottedNames/x.y.rb new file mode 100644 index 0000000000..b51feb3103 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/DottedNames/x.y.rb @@ -0,0 +1 @@ +puts 'x.y' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/DottedNames/z.rb.rb b/merlin/main/languages/ruby/Experimental/Loader/DottedNames/z.rb.rb new file mode 100644 index 0000000000..67e084735d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/DottedNames/z.rb.rb @@ -0,0 +1 @@ +puts 'z.rb.rb' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions/fcntl.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions/fcntl.rb new file mode 100644 index 0000000000..1e71fa5e93 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions/fcntl.rb @@ -0,0 +1 @@ +puts 'hello' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions/fcntl.so b/merlin/main/languages/ruby/Experimental/Loader/Extensions/fcntl.so new file mode 100644 index 0000000000..f0ae0c5c40 Binary files /dev/null and b/merlin/main/languages/ruby/Experimental/Loader/Extensions/fcntl.so differ diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions/test.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions/test.rb new file mode 100644 index 0000000000..3e1659d029 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions/test.rb @@ -0,0 +1,7 @@ +$:.clear +$: << '.' + +# .rb files take precendce +require 'fcntl' + +puts Fcntl.constants \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions2/A/fcntl.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions2/A/fcntl.rb new file mode 100644 index 0000000000..1e71fa5e93 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions2/A/fcntl.rb @@ -0,0 +1 @@ +puts 'hello' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions2/fcntl.so b/merlin/main/languages/ruby/Experimental/Loader/Extensions2/fcntl.so new file mode 100644 index 0000000000..f0ae0c5c40 Binary files /dev/null and b/merlin/main/languages/ruby/Experimental/Loader/Extensions2/fcntl.so differ diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions2/test.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions2/test.rb new file mode 100644 index 0000000000..e36e9c1420 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions2/test.rb @@ -0,0 +1,12 @@ +$:.clear +$: << '.' +$: << 'A' + +# .rb files take precedence over .dll/.so +$".clear +require 'fcntl' + +puts Fcntl.constants + +$".clear +require 'fcntl' diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions3/A/a.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions3/A/a.rb new file mode 100644 index 0000000000..1e71fa5e93 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions3/A/a.rb @@ -0,0 +1 @@ +puts 'hello' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions3/test.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions3/test.rb new file mode 100644 index 0000000000..27eda51c4c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions3/test.rb @@ -0,0 +1,9 @@ +$:.clear +$: << '.\A' + +p $: +$".clear +require 'a.rb' + +$".clear +require 'a' diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions4/a b/merlin/main/languages/ruby/Experimental/Loader/Extensions4/a new file mode 100644 index 0000000000..218166bd00 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions4/a @@ -0,0 +1 @@ +puts 'a: no extension' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions4/a.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions4/a.rb new file mode 100644 index 0000000000..e857962a71 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions4/a.rb @@ -0,0 +1 @@ +puts 'a.rb' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions4/b b/merlin/main/languages/ruby/Experimental/Loader/Extensions4/b new file mode 100644 index 0000000000..2959c0e7a9 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions4/b @@ -0,0 +1 @@ +puts 'b: no extension' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions4/test.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions4/test.rb new file mode 100644 index 0000000000..90860752dc --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions4/test.rb @@ -0,0 +1,43 @@ +class P + def to_str + #puts 'converting' + "." + end +end + +$:.clear +$: << P.new + +puts '---' + +require 'a' +p $" + +require 'a.rb' +p $" + +puts '---' + +begin + require 'b' +rescue Exception + puts $! +end +p $" + +puts '---' + +$".clear +$" << "a.rb" + +x = 'a' +require x +p $" + +puts '---' + +x = 'a.rb' +require x +p $" + + diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions5/q.dll.dll b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/q.dll.dll new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions5/r b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/r new file mode 100644 index 0000000000..f19bda069f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/r @@ -0,0 +1 @@ +puts __FILE__ \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions5/test.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/test.rb new file mode 100644 index 0000000000..5310eac956 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/test.rb @@ -0,0 +1,13 @@ +def try a + require a +rescue Exception + puts $! +end + +try 'x' +try 'r' +try 'x.rb' +try 'z.rb' +try 'x.dll' +try 'w.dll' +try 'q.dll' diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions5/w.dll.so b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/w.dll.so new file mode 100644 index 0000000000..f19bda069f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/w.dll.so @@ -0,0 +1 @@ +puts __FILE__ \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.dll.exe b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.dll.exe new file mode 100644 index 0000000000..f19bda069f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.dll.exe @@ -0,0 +1 @@ +puts __FILE__ \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.dll.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.dll.rb new file mode 100644 index 0000000000..f19bda069f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.dll.rb @@ -0,0 +1 @@ +puts __FILE__ \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.exe.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.exe.rb new file mode 100644 index 0000000000..f19bda069f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.exe.rb @@ -0,0 +1 @@ +puts __FILE__ \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.rb new file mode 100644 index 0000000000..f19bda069f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.rb @@ -0,0 +1 @@ +puts __FILE__ \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.rb.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.rb.rb new file mode 100644 index 0000000000..f19bda069f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.rb.rb @@ -0,0 +1 @@ +puts __FILE__ \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.so.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.so.rb new file mode 100644 index 0000000000..f19bda069f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/x.so.rb @@ -0,0 +1 @@ +puts __FILE__ \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Extensions5/z.rb.rb b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/z.rb.rb new file mode 100644 index 0000000000..f19bda069f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Extensions5/z.rb.rb @@ -0,0 +1 @@ +puts __FILE__ \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Load/a b/merlin/main/languages/ruby/Experimental/Loader/Load/a new file mode 100644 index 0000000000..1e71fa5e93 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Load/a @@ -0,0 +1 @@ +puts 'hello' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Load/test.rb b/merlin/main/languages/ruby/Experimental/Loader/Load/test.rb new file mode 100644 index 0000000000..ad862ae08e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Load/test.rb @@ -0,0 +1 @@ +load 'a' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/LoadedFiles.rb b/merlin/main/languages/ruby/Experimental/Loader/LoadedFiles.rb new file mode 100644 index 0000000000..1b8f2a4bfd --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/LoadedFiles.rb @@ -0,0 +1,36 @@ +$".clear + +class C + def to_str + "x.rb" + end +end + +$" << C.new +puts '1:' +require "x.rb" +puts '2:' +require "X.rb" # case sensitive +puts '3:' +require 'A\..\x.rb' +puts '4:' + +p $" + +$".clear +$" << nil +require "x.rb" rescue puts $! + + +# $" set after successful require + +puts '5:' +$".clear +p $" +begin + require "p_loaded_files.rb" +rescue + p $" + p 'error' +end +p $" \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/Recursion/q.rb b/merlin/main/languages/ruby/Experimental/Loader/Recursion/q.rb new file mode 100644 index 0000000000..b5f91652fd --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Recursion/q.rb @@ -0,0 +1,5 @@ +puts "q: #{$".inspect}" +$".clear +puts "q.clear: #{$".inspect}" +require 'y' +puts "q.pop y: #{$".inspect}" diff --git a/merlin/main/languages/ruby/Experimental/Loader/Recursion/test.rb b/merlin/main/languages/ruby/Experimental/Loader/Recursion/test.rb new file mode 100644 index 0000000000..8436e5ac2c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Recursion/test.rb @@ -0,0 +1,30 @@ +=begin + + x --> y --> z -(R)-> x + --> q + + z requires x using a different (but equivalent) path each time + +=end + +def require_special(x) + # + # This doesn't trigger a recursion,so the internal stack doesn't contain combined paths + # + # $:[0] = ('.\\' * $loop_count) + '.' + # $".clear + # require x + + $".clear + require ('.\\' * $loop_count) + x +end + + +$:.clear +$:[0] = '.' + +$loop_count = 5 + +puts "test: #{$".inspect}" +require 'x' +puts "test.pop x: #{$".inspect}" diff --git a/merlin/main/languages/ruby/Experimental/Loader/Recursion/x.rb b/merlin/main/languages/ruby/Experimental/Loader/Recursion/x.rb new file mode 100644 index 0000000000..532ec04fdb --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Recursion/x.rb @@ -0,0 +1,7 @@ +puts "x: #{$".inspect}" +require_special('y') +puts "x.pop y: #{$".inspect}" + +$".clear +require 'q' +puts "x.pop q: #{$".inspect}" diff --git a/merlin/main/languages/ruby/Experimental/Loader/Recursion/y.rb b/merlin/main/languages/ruby/Experimental/Loader/Recursion/y.rb new file mode 100644 index 0000000000..134d1c9fe0 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Recursion/y.rb @@ -0,0 +1,3 @@ +puts "y: #{$".inspect}" +require_special('z') +puts "y.pop z: #{$".inspect}" diff --git a/merlin/main/languages/ruby/Experimental/Loader/Recursion/z.rb b/merlin/main/languages/ruby/Experimental/Loader/Recursion/z.rb new file mode 100644 index 0000000000..bb3120bb09 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Recursion/z.rb @@ -0,0 +1,15 @@ +puts "z(#$loop_count): #{$".inspect}" + +if $loop_count > 0 then + $loop_count -= 1 + require_special('x') + puts "q.pop x: #{$".inspect}" +else + puts "Terminating recursion" + + # one more, try to add an extension: + $".clear + require('x.rb') + puts "q.pop x: #{$".inspect}" +end + diff --git a/merlin/main/languages/ruby/Experimental/Loader/Relative/relative.rb b/merlin/main/languages/ruby/Experimental/Loader/Relative/relative.rb new file mode 100644 index 0000000000..ebe90164e9 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Relative/relative.rb @@ -0,0 +1,30 @@ +# load doesn't check the current directory, it only checks $: + +def try_load path + load(path) +rescue Exception + puts "load('#{path}') -> '#{$!}'" +else + puts "load('#{path}') -> OK" +end + +puts '-----' + +$:.clear +p $: +p $" + +try_load('..\x.rb') +try_load('\temp\load\x.rb') +try_load('/temp\load\x.rb') +try_load('C:\temp\load\x.rb') + +puts '-----' + +$: << 'bbb' +p $: +p $" + +try_load('..\x.rb') +try_load('/temp\load\x.rb') +try_load('C:\temp\load\x.rb') diff --git a/merlin/main/languages/ruby/Experimental/Loader/Relative/temp/load/x.rb b/merlin/main/languages/ruby/Experimental/Loader/Relative/temp/load/x.rb new file mode 100644 index 0000000000..fbaf0f6bef --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/Relative/temp/load/x.rb @@ -0,0 +1 @@ +puts 'in C:\temp\load\A\temp\load\x.rb' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Loader/p_loaded_files.rb b/merlin/main/languages/ruby/Experimental/Loader/p_loaded_files.rb new file mode 100644 index 0000000000..b7d7c222f2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/p_loaded_files.rb @@ -0,0 +1,2 @@ +p $" +raise IOError.new('foo') diff --git a/merlin/main/languages/ruby/Experimental/Loader/x.rb b/merlin/main/languages/ruby/Experimental/Loader/x.rb new file mode 100644 index 0000000000..cadbd647ba --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Loader/x.rb @@ -0,0 +1 @@ +puts 'in C:\temp\load\x.rb' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Methods/Alias.rb b/merlin/main/languages/ruby/Experimental/Methods/Alias.rb new file mode 100644 index 0000000000..730af52462 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Alias.rb @@ -0,0 +1,150 @@ +module N + def i_n + puts 'i_m' + end + + def self.c_n + puts 'c_n' + end + + puts '- 0 -' + + (alias xxx i_m; puts 'i_m OK') rescue p $! + (alias xxx c_m; puts 'c_m OK') rescue p $! + (alias xxx i_n; puts 'i_n OK') rescue p $! + (alias xxx c_n; puts 'c_n OK') rescue p $! + puts +end + +module M + def i_m + puts 'i_m' + end + + def self.c_m + puts 'c_m' + end + + puts '- 1 -' + + (alias xxx i_m; puts 'i_m OK') rescue p $! + (alias xxx c_m; puts 'c_m OK') rescue p $! + (alias xxx i_n; puts 'i_n OK') rescue p $! + (alias xxx c_n; puts 'c_n OK') rescue p $! + + puts + + + N.class_eval { + puts '- 2 -' + + (alias xxx i_m; puts 'i_m OK') rescue p $! + (alias xxx c_m; puts 'c_m OK') rescue p $! + (alias xxx i_n; puts 'i_n OK') rescue p $! + (alias xxx c_n; puts 'c_n OK') rescue p $! + puts + } + + def N.test3 + puts '- 3 -' + + (alias xxx i_m; puts 'i_m OK') rescue p $! + (alias xxx c_m; puts 'c_m OK') rescue p $! + (alias xxx i_n; puts 'i_n OK') rescue p $! + (alias xxx c_n; puts 'c_n OK') rescue p $! + puts + end + + N.class_eval { + def N.test4 + + puts '- 4 -' + + (alias xxx i_m; puts 'i_m OK') rescue p $! + (alias xxx c_m; puts 'c_m OK') rescue p $! + (alias xxx i_n; puts 'i_n OK') rescue p $! + (alias xxx c_n; puts 'c_n OK') rescue p $! + puts + end + } + + def N.test5 + N.class_eval { + + puts '- 5 -' + + (alias xxx i_m; puts 'i_m OK') rescue p $! + (alias xxx c_m; puts 'c_m OK') rescue p $! + (alias xxx i_n; puts 'i_n OK') rescue p $! + (alias xxx c_n; puts 'c_n OK') rescue p $! + puts + } + end + + def N.test6 + $p = proc { + + puts '- 6 -' + + (alias xxx i_m; puts 'i_m OK') rescue p $! + (alias xxx c_m; puts 'c_m OK') rescue p $! + (alias xxx i_n; puts 'i_n OK') rescue p $! + (alias xxx c_n; puts 'c_n OK') rescue p $! + puts + } + + 1.times &$p + end +end + +class D + def i_d + puts 'i_d' + end + + def self.c_d + puts 'c_d' + end +end + +class C + def i_c + puts 'i_c' + end + + def self.c_c + puts 'c_c' + end + + D.class_eval { + puts '- !7 -' + + (alias xxx i_c; puts 'i_c OK') rescue p $! + (alias xxx c_c; puts 'c_c OK') rescue p $! + (alias xxx i_d; puts 'i_d OK') rescue p $! + (alias xxx c_d; puts 'c_d OK') rescue p $! + puts + } + + D.send(:define_method, :test7) { + puts '- 8 -' + + (alias xxx i_c; puts 'i_c OK') rescue p $! + (alias xxx c_c; puts 'c_c OK') rescue p $! + (alias xxx i_d; puts 'i_d OK') rescue p $! + (alias xxx c_d; puts 'c_d OK') rescue p $! + puts + } + +end + +N.test3 +N.test4 +N.test5 +N.test6 +D.new.test7 + + + + + diff --git a/merlin/main/languages/ruby/Experimental/Methods/AliasLookup.rb b/merlin/main/languages/ruby/Experimental/Methods/AliasLookup.rb new file mode 100644 index 0000000000..74d152fec8 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/AliasLookup.rb @@ -0,0 +1,47 @@ +p $K = class C; self; end.ancestors[-1] +p $O = class C; self; end.ancestors[-2] + +if Module.nesting.length > 0 then + p $AM = Module.nesting[0] + + AM = $AM + module AM + def i_am + end + end +end + +K = $K +module K + def i_kernel + end +end + +O = $O +class O + def i_object + end +end + +p AM.constants.sort if defined? AM + +module N + + def i_n + end + + module M + #undef i_object rescue puts '!undef i_object' + #undef i_kernel rescue puts '!undef i_kernel' + #undef i_n rescue puts '!undef i_n' + #undef i_am rescue puts '!undef i_am' + + alias x1 i_object rescue puts '!alias i_object' + alias x2 i_kernel rescue puts '!alias i_kernel' + alias x3 i_n rescue puts '!alias i_n' + alias x4 i_am rescue puts '!alias i_am' + + p instance_methods(false).sort + end +end + diff --git a/merlin/main/languages/ruby/Experimental/Methods/AliasLookupLoad.rb b/merlin/main/languages/ruby/Experimental/Methods/AliasLookupLoad.rb new file mode 100644 index 0000000000..d30593aed6 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/AliasLookupLoad.rb @@ -0,0 +1 @@ +load 'AliasLookup.rb', true \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Methods/AliasMethod.rb b/merlin/main/languages/ruby/Experimental/Methods/AliasMethod.rb new file mode 100644 index 0000000000..483bc854ee --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/AliasMethod.rb @@ -0,0 +1,150 @@ +module N + def i_n + puts 'i_m' + end + + def self.c_n + puts 'c_n' + end + + puts '- 0 -' + + (alias_method :xxx, :i_m; puts 'i_m OK') rescue p $! + (alias_method :xxx, :c_m; puts 'c_m OK') rescue p $! + (alias_method :xxx, :i_n; puts 'i_n OK') rescue p $! + (alias_method :xxx, :c_n; puts 'c_n OK') rescue p $! + puts +end + +module M + def i_m + puts 'i_m' + end + + def self.c_m + puts 'c_m' + end + + puts '- 1 -' + + (alias_method :xxx, :i_m; puts 'i_m OK') rescue p $! + (alias_method :xxx, :c_m; puts 'c_m OK') rescue p $! + (alias_method :xxx, :i_n; puts 'i_n OK') rescue p $! + (alias_method :xxx, :c_n; puts 'c_n OK') rescue p $! + + puts + + + N.class_eval { + puts '- 2 -' + + (alias_method :xxx, :i_m; puts 'i_m OK') rescue p $! + (alias_method :xxx, :c_m; puts 'c_m OK') rescue p $! + (alias_method :xxx, :i_n; puts 'i_n OK') rescue p $! + (alias_method :xxx, :c_n; puts 'c_n OK') rescue p $! + puts + } + + def N.test3 + puts '- 3 -' + + (alias_method :xxx, :i_m; puts 'i_m OK') rescue p $! + (alias_method :xxx, :c_m; puts 'c_m OK') rescue p $! + (alias_method :xxx, :i_n; puts 'i_n OK') rescue p $! + (alias_method :xxx, :c_n; puts 'c_n OK') rescue p $! + puts + end + + N.class_eval { + def N.test4 + + puts '- 4 -' + + (alias_method :xxx, :i_m; puts 'i_m OK') rescue p $! + (alias_method :xxx, :c_m; puts 'c_m OK') rescue p $! + (alias_method :xxx, :i_n; puts 'i_n OK') rescue p $! + (alias_method :xxx, :c_n; puts 'c_n OK') rescue p $! + puts + end + } + + def N.test5 + N.class_eval { + + puts '- 5 -' + + (alias_method :xxx, :i_m; puts 'i_m OK') rescue p $! + (alias_method :xxx, :c_m; puts 'c_m OK') rescue p $! + (alias_method :xxx, :i_n; puts 'i_n OK') rescue p $! + (alias_method :xxx, :c_n; puts 'c_n OK') rescue p $! + puts + } + end + + def N.test6 + $p = proc { + + puts '- 6 -' + + (alias_method :xxx, :i_m; puts 'i_m OK') rescue p $! + (alias_method :xxx, :c_m; puts 'c_m OK') rescue p $! + (alias_method :xxx, :i_n; puts 'i_n OK') rescue p $! + (alias_method :xxx, :c_n; puts 'c_n OK') rescue p $! + puts + } + + 1.times &$p + end +end + +class D + def i_d + puts 'i_d' + end + + def self.c_d + puts 'c_d' + end +end + +class C + def i_c + puts 'i_c' + end + + def self.c_c + puts 'c_c' + end + + D.class_eval { + puts '- !7 -' + + (alias_method :xxx, :i_c; puts 'i_c OK') rescue p $! + (alias_method :xxx, :c_c; puts 'c_c OK') rescue p $! + (alias_method :xxx, :i_d; puts 'i_d OK') rescue p $! + (alias_method :xxx, :c_d; puts 'c_d OK') rescue p $! + puts + } + + D.send(:define_method, :test7) { + puts '- 8 -' + + (alias_method :xxx, :i_c; puts 'i_c OK') rescue p $! + (alias_method :xxx, :c_c; puts 'c_c OK') rescue p $! + (alias_method :xxx, :i_d; puts 'i_d OK') rescue p $! + (alias_method :xxx, :c_d; puts 'c_d OK') rescue p $! + puts + } + +end + +N.test3 +N.test4 +N.test5 +N.test6 +D.new.test7 + + + + + diff --git a/merlin/main/languages/ruby/Experimental/Methods/Assignment.rb b/merlin/main/languages/ruby/Experimental/Methods/Assignment.rb new file mode 100644 index 0000000000..fd9b5523a7 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Assignment.rb @@ -0,0 +1,30 @@ +class C + def [](*args) + puts 'read' + p args + 0 + end + + def []=(*args) + puts 'write' + p args + nil + end +end + +def t + puts 't' + C.new +end + +def k + puts 'k' + 0 +end + +def l + puts 'l' + 0 +end + +t[k,l] += 1 diff --git a/merlin/main/languages/ruby/Experimental/Methods/DefinitionScope.rb b/merlin/main/languages/ruby/Experimental/Methods/DefinitionScope.rb new file mode 100644 index 0000000000..479530ab9a --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/DefinitionScope.rb @@ -0,0 +1,30 @@ +module M + $p = lambda { + eval <<-EE + module N + def bar + end + end + EE + } +end + + +class C + define_method :foo,&$p +end + +class D + class_eval &$p +end + +c = C.new +d = D.new + +c.foo + +p M.instance_methods(false) +p M::N.instance_methods(false) +p C.instance_methods(false) +p D.instance_methods(false) + diff --git a/merlin/main/languages/ruby/Experimental/Methods/DefinitionScope1.rb b/merlin/main/languages/ruby/Experimental/Methods/DefinitionScope1.rb new file mode 100644 index 0000000000..9dd1dd688b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/DefinitionScope1.rb @@ -0,0 +1,34 @@ +module M + $p = lambda { + def goo + eval <<-EE + module N + def bar + puts 'bar' + end + end + EE + end + } +end + +class C + define_method :foo,&$p +end + +class D + class_eval &$p +end + +c = C.new +d = D.new + +d.goo +c.foo +c.goo + +p M.instance_methods(false) +p M::N.instance_methods(false) +p C.instance_methods(false) +p D.instance_methods(false) + diff --git a/merlin/main/languages/ruby/Experimental/Methods/Method.rb b/merlin/main/languages/ruby/Experimental/Methods/Method.rb new file mode 100644 index 0000000000..4b4aa2011c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Method.rb @@ -0,0 +1,145 @@ +class Foo + def foo + puts 'foo' + end + + alias bar foo + +end + +f = Foo.new +g = Foo.new + +m1 = f.method :foo +m2 = f.method "bar" +m3 = f.method :bar +m4 = g.method :bar + +p m1 +p m2 +p m3 +p m4 + +# ids are all different +p m1.object_id +p m2.object_id +p m3.object_id +p m4.object_id + +p m1 == m2 # true +p m1 == m3 # true +p m2 == m3 # true + +p m1 == m4 # false +p m2 == m4 # false +p m3 == m4 # false + +puts m1.to_s + +puts '-' * 20 + +u1 = m1.unbind +u2 = m2.unbind +u3 = m3.unbind +u4 = m4.unbind +u44 = m4.unbind + +# ids are all different +p u1.object_id +p u2.object_id +p u3.object_id +p u4.object_id +p u44.object_id + +p u1 +p u2 +p u3 +p u4 +p u44 + +puts '-' * 20 + +p m1.clone +p u1.clone + +class Method + remove_method :clone +end + +class UnboundMethod + remove_method :clone +end + +p m1.clone rescue p $! +p u1.clone rescue p $! + +Object.new.method :foo rescue p $! + +puts '-' * 20 + + +module M + def bar + puts 'bar' + end +end + +class B + def foo a,b + puts a * b + end +end + +class C < B + include M + + def foo a,b + puts a + b + end +end + +class D < C +end + +class E + include M +end + +b = B.new +c = C.new +d = D.new +e = E.new +s = class << d; self; end + +p m = c.method(:foo) +p u = m.unbind +p ud = u.bind(d) + +u.bind(nil) rescue p $! +u.bind(s) rescue p $! +u.bind(b) rescue p $! + +m[1,2] +ud[1,2] + +puts '-'*20 + +mbar = c.method(:bar) +ubar = mbar.unbind + +p mbar +p ubar +ubar.bind(e) rescue p $! + +puts '-'*20 + +im_bar = M.instance_method :bar +p im_bar.bind(e) # Ruby 1.8 displays Object(M), Ruby 1.9 corrects it to E(M) +p im_bar.bind(Object.new) rescue p $! +p im_bar.bind(nil) rescue p $! + + + + + + diff --git a/merlin/main/languages/ruby/Experimental/Methods/SelfInDefinedMethod.rb b/merlin/main/languages/ruby/Experimental/Methods/SelfInDefinedMethod.rb new file mode 100644 index 0000000000..74fd5b1fdd --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/SelfInDefinedMethod.rb @@ -0,0 +1,17 @@ +def l &p; p; end + +class A + def defp + $p = l do + p self + end + end +end + +A.new.defp + +class C + define_method :foo, &$p +end + +C.new.foo \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/basic.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/basic.rb new file mode 100644 index 0000000000..32efb52710 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/basic.rb @@ -0,0 +1,90 @@ +class Object + def s + class << self; self; end + end + + def singleton_method_added name + puts "singleton method added: #{self}##{name}" + end +end + +class Module + def method_added name + puts "method added: #{self}##{name}" + end +end + +def dump obj, methodName + puts "=== #{obj}##{methodName} ===" + + if obj.is_a? Module + puts obj.public_instance_methods.include?(methodName) ? "public" : "-" + puts obj.private_instance_methods.include?(methodName) ? "private" : "-" + end + + puts obj.s.public_instance_methods.include?(methodName) ? "public" : "-" + puts obj.s.private_instance_methods.include?(methodName) ? "private" : "-" +end + +$bob = Object.new + +class Foo + + class << $bob + private + end + + private + def $bob.xxx + end +end + +module M + def yyy + end +end + +dump $bob, "xxx" +dump M, "yyy" + +module M + module_function :yyy +end + +dump M, "yyy" + +module N + module_function + def zzz + end +end + +dump N, "zzz" + +module P + module_function + def initialize + end +end + +dump P, "initialize" + +class Q + public + def initialize + end +end + +dump Q, "initialize" + +class S + def self.initialize + end +end + +dump S, "initialize" + + + + + diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/def.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/def.rb new file mode 100644 index 0000000000..ea0da766cb --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/def.rb @@ -0,0 +1,40 @@ +class Module + public :module_function + + def method_added name + puts "I> #{self}::#{name}" + end + + def singleton_method_added name + puts "S> #{self}::#{name}" + end +end + +class A +end + +class B +end + +class C + A.module_eval do + B.module_eval do # 1.8 1.9 + def def1 # B B + def f1 # C B + end + end + end + end + + A.module_eval do + B.module_eval do + define_method :def2 do # B B + def f2 # B B + end + end + end + end +end + +B.new.def1 +B.new.def2 diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/flags.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/flags.rb new file mode 100644 index 0000000000..69037be3ec --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/flags.rb @@ -0,0 +1,290 @@ +class Module + public :private, :public, :module_function +end + +module M +end + +class C + def foo + 1.times { + M.private + + def pri1 + end + } + + def pubXpri2 # private in 1.9, public in 1.8 (!) + end + end + + new.foo + + def pub3 + end +end + +p C.public_instance_methods(false).sort +p C.private_instance_methods(false).sort +puts '--' + +class D + 1.times { + private + + def pri1 + end + } + + def pubXpri2 # private in 1.9, public in 1.8 (!) + end +end + +p D.public_instance_methods(false).sort +p D.private_instance_methods(false).sort +puts '--' + +class F1 + module_eval { + private + + def pri1 + end + } + + def pub2 # public in 1.8 and 1.9, ok + end +end + +p F1.public_instance_methods(false).sort +p F1.private_instance_methods(false).sort + +puts '--' + +class F2 + define_method(:foo) { + M.private + + def pri1 + end + } + + new.foo + + def pubXpri2 # public in 1.8, private in 1.9 (!) + end +end + +p F2.public_instance_methods(false).sort +p F2.private_instance_methods(false).sort + +puts '--- module_function ---' +puts '- G -' + +class G + 1.times { + M.module_function + + def mf1 + end + } + + def iXmf2 # mf in 1.9, instance in 1.8 (!) + end +rescue + puts $! +end + +p G.singleton_methods(false).sort +p G.private_instance_methods(false).sort +p G.public_instance_methods(false).sort +puts '- H -' + +class H + module_eval { + M.module_function + + def mf1 + end + } + + def i2 + end +rescue + puts $! +end + +p H.singleton_methods(false).sort +p H.private_instance_methods(false).sort +p H.public_instance_methods(false).sort + +puts '- I -' + +class I + define_method(:bar) { + M.module_function + + def mf1 + end + } + + new.bar + + def iXmf2 # instance in 1.8, mf in 1.9 (!) + end +rescue + puts $! +end + +p I.singleton_methods(false).sort +p I.private_instance_methods(false).sort +p I.public_instance_methods(false).sort + +puts '------ flag inheritance ----' +puts '- J -' +class J + private + 1.times { # inherits + 1.times { # inherits + def pri + end + } + } +end + +p J.public_instance_methods(false).sort +p J.private_instance_methods(false).sort + +puts '- K -' +class K + private + module_eval { # doesn't inherit + def pub1 + end + + private + define_method(:priXpub1) { # inherits (!); + def priXpub2 # 1.9: public (!) + end + + M.private + class X # doesn't inherit + def pub3 + end + end + } + } + + new.send :priXpub1 +end + +p K.public_instance_methods(false).sort +p K.private_instance_methods(false).sort +p K::X.public_instance_methods(false).sort +p K::X.private_instance_methods(false).sort + +puts '- L (1.9 bad visibility) -' +module L + module_function + + 1.times { + 1.times { + def mf + end + } + } +end + +p L.public_instance_methods(false).sort +p L.private_instance_methods(false).sort +p L.singleton_methods(false).sort + +puts '- N -' +module N + private + module_function # overrides visibility to public + + module_eval { # doesn't inherit + def mf + end + } +end + +p N.public_instance_methods(false).sort +p N.private_instance_methods(false).sort +p N.singleton_methods(false).sort + +puts '- O (1.8 and 1.9 bugs), -' +module O + module_function + + # 1.8: foo private, not mf (!) + # 1.9: foo public, not mf + define_method(:priXpub1) { # inherits 1.9 (!) + + # 1.8: defines O_C::mf, S(O_C)::mf (!) + # 1.9: defines O::mf, S(O)::mf + def mf + end + } + +end + +class O_C + include O + new.send :priXpub1 +end + +p O.public_instance_methods(false).sort +p O.private_instance_methods(false).sort +p O.singleton_methods(false).sort +p O_C.public_instance_methods(false).sort +p O_C.private_instance_methods(false).sort +p O_C.singleton_methods(false).sort + +puts '- P -' +class P + protected + + module_eval { + def pub1; end # looks for the inner-most module/(real (!) method) scope + private + def pri2; end + } + + define_method(:priXpub0) { + def pro3; end # looks for the inner-most module/(real (!) method) scope + M.private + def pri4; end + } + + new.send :priXpub0 + + def self.bar + private + + module_eval { + def pub5; end # looks for the inner-most module/(real (!) method) scope + public + def pub6; end + } + + define_method(:priXpub10) { + def pri7; end # looks for the inner-most module/(real (!) method) scope + M.public + def pub8; end + } + + new.send :priXpub10 + + def priXpub9; end # 1.8: pri, 1.9: pub (!) + end + + bar + +end + +p P.public_instance_methods(false).sort +p P.private_instance_methods(false).sort +p P.protected_instance_methods(false).sort + diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/mf.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/mf.rb new file mode 100644 index 0000000000..a10ca8a282 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/mf.rb @@ -0,0 +1,18 @@ +module M + module_function + + def foo + end + + def M.bar + end + +end + +class << M + ::SM = self +end + +p M.instance_method(:foo) +p SM.instance_method(:foo) + diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/mf_owner.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/mf_owner.rb new file mode 100644 index 0000000000..9e84fa26f8 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/mf_owner.rb @@ -0,0 +1,41 @@ +class C +end +$c = C.new + +module M + def $c.foo # owner is S($c) + end +end + +p $c.methods(false) + +puts '---' + +module O + module_function + + define_method(:foo) { + + # 1.8: defines private O_C::mf, public S(O_C)::mf (!) + # 1.9: defines public (!) O::mf, public S(O)::mf + # IR: defines private O::mf, public S(O)::mf + def mf + end + } + +end + +class O_C + include O + new.send :foo +end + +p O.public_instance_methods(false) +p O.private_instance_methods(false) +p O.singleton_methods(false) +puts '---' +p O_C.public_instance_methods(false) +p O_C.private_instance_methods(false) +p O_C.singleton_methods(false) + + diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/mf_super.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/mf_super.rb new file mode 100644 index 0000000000..6ad5f59186 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/mf_super.rb @@ -0,0 +1,41 @@ +class Module + public :module_function + + def method_added name + puts "I> #{name}" + end + + def singleton_method_added name + puts "S> #{name}" + end +end + +module M +end + +class Object + def foo + puts "Object::foo" + end +end + +class B + def self.foo + puts "B::foo" + end +end + +class C < B + M.module_function + + def foo + puts "C::foo" + super + end + + p private_instance_methods(false) + p public_instance_methods(false) + p singleton_methods(false) +end + +C.foo diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/s.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/s.rb new file mode 100644 index 0000000000..792acdee1b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/s.rb @@ -0,0 +1,36 @@ +class Module + public :module_function + + def method_added name + puts "I> #{self}::#{name}" + end + + def singleton_method_added name + puts "S> #{self}::#{name}" + end +end + +module M +end + +# module_function affects the current non-block scope + +class C + def foo + 1.times { + M.send :module_function + } + + def yyy # mf on C + end + end + + x = C.new + x.foo + + def xxx # instance on C + end +end + + + diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/simple_visibility_flow.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/simple_visibility_flow.rb new file mode 100644 index 0000000000..99fc20c4ae --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/simple_visibility_flow.rb @@ -0,0 +1,11 @@ +class K + private + module_eval { + def pub1 + end + } + +end + +p K.public_instance_methods(false).sort +p K.private_instance_methods(false).sort \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/u.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/u.rb new file mode 100644 index 0000000000..ab9ccb733d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/u.rb @@ -0,0 +1,32 @@ +class Module + def method_added name + puts "I> #{self}::#{name}" + end + + def singleton_method_added name + puts "S> #{self}::#{name}" + end +end + +module M + module_function + + def initialize + puts 'foo' + end + + private + def self.foo + end + + p private_instance_methods(false).sort + p singleton_methods(false).sort + + class << self + p public_instance_methods(false).map { |x| x.to_s }.include?("foo") + p private_instance_methods(false).map { |x| x.to_s }.include?("foo") + end +end + +M.initialize rescue p$! +M.foo rescue p$! diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/visibility_alias.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/visibility_alias.rb new file mode 100644 index 0000000000..ee26c965a2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/visibility_alias.rb @@ -0,0 +1,49 @@ +class C + private + def foo + end + + public + alias bar foo + + p private_instance_methods(false) + p public_instance_methods(false) + puts '---' + + public :bar + + p private_instance_methods(false) + p public_instance_methods(false) + puts '---' +end + +class D < C + alias baz foo + + p private_instance_methods(false) + p public_instance_methods(false) + puts '---' +end + +class X < D +end + +$baz = X.new.method(:baz) +$u_baz = $baz.unbind + +$bar = X.new.method(:bar) +$u_bar = $baz.unbind + +p $baz, $u_baz, $bar, $u_bar + +puts '---' + +class Y < C + define_method :baz1, $baz rescue p $! + define_method :baz2, $u_baz rescue p $! + define_method :bar1, $bar rescue p $! + define_method :bar2, $u_bar rescue p $! +end + + + diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/visibility_special_names.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/visibility_special_names.rb new file mode 100644 index 0000000000..fce4e8f688 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/visibility_special_names.rb @@ -0,0 +1,35 @@ +module M1 + puts 'accessors:' + + attr_accessor :initialize, :initialize_copy + + p instance_methods(false) + p private_instance_methods(false) + +end + +module M2 + puts 'define_method:' + + define_method :initialize do + end + + define_method :initialize_copy do + end + + p instance_methods(false) + p private_instance_methods(false) +end + +module M3 + puts 'def:' + + def initialize + end + + def initialize_copy + end + + p instance_methods(false) + p private_instance_methods(false) +end diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/x.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/x.rb new file mode 100644 index 0000000000..7c2da4151e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/x.rb @@ -0,0 +1,31 @@ +class Module + def method_added name + puts "#{self}::#{name}" + end +end + +module M + def i_m + end + + module_function + + def mf_m + end + + alias ai_m i_m + alias amf_m mf_m + + puts '---' + + puts 'M:' + p public_instance_methods(false) # 1.9 bug: instance mf public + p private_instance_methods(false) + + class << self + puts 'S(M):' + p instance_methods(false).delete_if { |x| x[-2..-1] != "_m" } + p private_methods(false).delete_if { |x| x[-2..-1] != "_m" } + end + +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Methods/Visibility/y.rb b/merlin/main/languages/ruby/Experimental/Methods/Visibility/y.rb new file mode 100644 index 0000000000..8d194695b0 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/Visibility/y.rb @@ -0,0 +1,61 @@ +class Module + def method_added name + puts "#{self}::#{name}" + end + + def dump + puts '---' + + puts "#{self}:" + puts "public: #{public_instance_methods(false).sort.inspect}" + puts "private: #{private_instance_methods(false).sort.inspect}" + puts "protected: #{protected_instance_methods(false).sort.inspect}" + + puts "S(#{self}):" + class << self + puts "public: #{public_instance_methods(false).delete_if { |x| x[-2..-2] != "_" }.sort.inspect}" + puts "private: #{private_instance_methods(false).delete_if { |x| x[-2..-1] != "_" }.sort.inspect}" + puts "protected: #{protected_instance_methods(false).delete_if { |x| x[-2..-1] != "_" }.sort.inspect}" + end + + puts '---' + end + +end + + +module M + def i_M + end + + module_function + + def c_M + end + + alias ai_M i_M + alias ac_M c_M + + dump +end + +module N + def i_pub_N + end + + private + + def i_pri_N + end + + protected + + def i_pro_N + end + + alias ai_pub_N i_pub_N + alias ai_pri_N i_pri_N + alias ai_pro_N i_pro_N + + dump +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Methods/define_method_method.rb b/merlin/main/languages/ruby/Experimental/Methods/define_method_method.rb new file mode 100644 index 0000000000..d7dbe5a912 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/define_method_method.rb @@ -0,0 +1,49 @@ +class Module + def method_added name + puts "+#{name}" + end +end + +class D + def bar + puts 'bar' + end +end + +class Method + def to_proc + puts 'to_proc' + end + +end + +$m = D.new.method(:bar) +p $m.class +p $m + +$u = $m.unbind + +class C + define_method(:foo, $m) rescue p $! + define_method(:ufoo, $u) rescue p $! +end + +puts 'call' + +C.new.ufoo rescue p $! +C.new.foo rescue p $! + +class C + def x + puts 'x' + end + + p define_method(:y, self.new.method(:x)) + p define_method(:z, self.new.method(:x).unbind) +end + +C.new.y +C.new.z + + + diff --git a/merlin/main/languages/ruby/Experimental/Methods/method_events.rb b/merlin/main/languages/ruby/Experimental/Methods/method_events.rb new file mode 100644 index 0000000000..a91d222058 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/method_events.rb @@ -0,0 +1,80 @@ +class Module + def method_added name + puts "+ #{self}##{name} -> #{self.instance_methods(false).include?(name.to_s)}" + end + + def method_removed name + puts "- #{self}##{name} -> #{self.instance_methods(false).include?(name.to_s)}" + end + + def method_undefined name + puts "U #{self}##{name} -> #{self.instance_methods(false).include?(name.to_s)}" + end +end + +class Object + def foo + end +end + +#require 'yaml' +#require 'thread' + +module M + def foo + end + + puts '> module_function:' + module_function :foo + + puts '> define_method:' + define_method :bar do end + + puts '> alias_method:' + alias_method :baz, :foo + + puts '> redef method:' + def foo + end + + puts '> attr:' + attr :myattr + + puts '> remove_method:' + remove_method :foo + + puts '> undef:' + undef :bar + + + class << self + puts '> def S1.f:' + def f; end + + puts '> def S1.g:' + def g; end + + puts '> undef S1.f:' + undef f + + puts '> remove S1.g:' + remove_method :g + + class << self + puts '> def S2.f:' + def f + end + + puts '> def S2.g:' + def g; end + + puts '> undef S2.f:' + undef f + + puts '> remove S2.g:' + remove_method :g + end + end + +end + diff --git a/merlin/main/languages/ruby/Experimental/Methods/method_events_libraries.rb b/merlin/main/languages/ruby/Experimental/Methods/method_events_libraries.rb new file mode 100644 index 0000000000..23ca23328c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/method_events_libraries.rb @@ -0,0 +1,8 @@ +class Module + def method_added name + puts "#{self.name}##{name}" + end +end + +require 'yaml' +p [1,2,3].to_yaml diff --git a/merlin/main/languages/ruby/Experimental/Methods/singleton_method_events.rb b/merlin/main/languages/ruby/Experimental/Methods/singleton_method_events.rb new file mode 100644 index 0000000000..603d277473 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/singleton_method_events.rb @@ -0,0 +1,20 @@ +module Kernel + def singleton_method_added name + puts "#{self}##{name}" + end +end + +class C + class << self + class << self + def bar + end + end + + def foo + end + end +end + +require 'yaml' +YAML.methods() diff --git a/merlin/main/languages/ruby/Experimental/Methods/singleton_method_events2.rb b/merlin/main/languages/ruby/Experimental/Methods/singleton_method_events2.rb new file mode 100644 index 0000000000..dc2376cdc3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/singleton_method_events2.rb @@ -0,0 +1,55 @@ +class A + def self.m_sa + end +end + +x = Object.new + +module Kernel + def singleton_method_added s + puts "Kernel: #{self}##{s}" + end + + def self.singleton_method_added s + puts "s(Kernel): #{self}##{s}" + end +end + +class << x + class << self + def singleton_method_added s + puts "S(x): #{self}##{s}" + end + end + + def m_sx + end +end + +class C + def self.singleton_method_added s + puts "S(C): #{self}##{s}" + end + + def self.m_sc + end +end + +=begin + + + + +#p Class.send :inherited, 1 + +class B + def self.inherited name + puts name.class + end + +end + +class A < B +end + +=end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Methods/to_proc.rb b/merlin/main/languages/ruby/Experimental/Methods/to_proc.rb new file mode 100644 index 0000000000..f980a81096 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/to_proc.rb @@ -0,0 +1,30 @@ +class B + def foo + puts 'super' + end +end + +class C < B + def foo x + super() + a = 1 + puts 'foo' + end +end + +x = C.new +m = x.method :foo +p m +1.times &m.to_proc + +def foo m + Proc.new { |*args| m.call(*args) } +end + +#q = foo(m) + +z = m.to_proc +eval("p local_variables", z) +eval("p self", z) + + diff --git a/merlin/main/languages/ruby/Experimental/Methods/trace.rb b/merlin/main/languages/ruby/Experimental/Methods/trace.rb new file mode 100644 index 0000000000..06ed6ea2f4 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Methods/trace.rb @@ -0,0 +1,52 @@ +def foo b, a = b+1 + + + return 1 + + +end + +alias :bar :foo + +module M + def m + end +end + +class C + include M + + def c + end + + def C.s + end + +end + +x = C.new + +class << x + def z + end + $sx = self +end + +p x + +def a +end + +set_trace_func(proc { |*args| + if args[0] == "call" + p args + p args[5].class + eval('puts a', args[4]) + end +}) + +bar 1 +x.c +x.m +C.s +x.z diff --git a/merlin/main/languages/ruby/Experimental/Misc/Arguments.rb b/merlin/main/languages/ruby/Experimental/Misc/Arguments.rb new file mode 100644 index 0000000000..53cf52fa1d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Arguments.rb @@ -0,0 +1,13 @@ +proc = lambda { puts 'lambda' } +def block + yield +end + +# proc is ignored, prints "foo" +block &proc { puts 'foo' } + +# lambda +block &proc + +# foo +block { print 'foo' } diff --git a/merlin/main/languages/ruby/Experimental/Misc/Assignments.rb b/merlin/main/languages/ruby/Experimental/Misc/Assignments.rb new file mode 100644 index 0000000000..13d55df266 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Assignments.rb @@ -0,0 +1,18 @@ +a = b = 1 + +(0..255).each do |x| + begin + z = eval("a #{x.chr}= b") + puts "0x#{sprintf('%X', x)}: a #{x.chr}= b: OK"; + rescue SyntaxError: + rescue NoMethodError: + puts "0x#{sprintf('%X', x)}: a #{x.chr}= b: OK"; + end +end + +a ||= b +a &&= b + +# TODO: what is this? +a ,= b + diff --git a/merlin/main/languages/ruby/Experimental/Misc/BlockArgs.rb b/merlin/main/languages/ruby/Experimental/Misc/BlockArgs.rb new file mode 100644 index 0000000000..f17afc2ba3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/BlockArgs.rb @@ -0,0 +1,47 @@ +def a; yield; end +def b; yield 1; end +def c; yield 1,2; end +def d; yield []; end +def e; yield [1]; end +def f; yield [1,2]; end +def g; yield [],[]; end + +a { |x| puts x.inspect } # nil + W +puts '---' +b { |x| puts x.inspect } # 1 +puts '---' +c { |x| puts x.inspect } # [1,2] + W +puts '---' +d { |x| puts x.inspect } # [] +puts '---' +e { |x| puts x.inspect } # [1] +puts '---' +f { |x| puts x.inspect } # [1,2] +puts '---' +g { |x| puts x.inspect } # [[],[]] + W + +puts '***************' + +def a; yield; end +def b; yield 1; end +def c; yield 1,2; end +def d; yield []; end +def e; yield [1]; end +def f; yield [1,2,3,4]; end +def g; yield [1,2],*[3,4]; end + +a { |x,y| puts x.inspect, y.inspect } +puts '---' +b { |x,y| puts x.inspect, y.inspect } +puts '---' +c { |x,y| puts x.inspect, y.inspect } +puts '---' +d { |x,y| puts x.inspect, y.inspect } +puts '---' +e { |x,y| puts x.inspect, y.inspect } +puts '---' +f { |x,y| puts x.inspect, y.inspect } +puts '---' +f { |x,y,*z| puts x.inspect, y.inspect, z.inspect } +puts '---' +g { |x,*y| puts x.inspect, y.inspect } diff --git a/merlin/main/languages/ruby/Experimental/Misc/BlockArgs2.rb b/merlin/main/languages/ruby/Experimental/Misc/BlockArgs2.rb new file mode 100644 index 0000000000..9b06229560 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/BlockArgs2.rb @@ -0,0 +1,66 @@ +def i *a + puts '---' + a.each { |x| print x.inspect, ',' } + puts +end + +x1,(*x2),(x3,*x4) = 1,2,[1,2],[3,4] +i x1,x2,x3,x4 + +puts '***' + +def bar + yield [1,2,3],[4,5], [6] +end + +bar { |(a,*b),*y| i a,b,y } # 1, [2,3], [[4,5],[6]] +bar { |(*a),(*b),(*c)| i a,b,c } # [[1,2,3]], [[4,5,]], [[6]] + +puts '*** baz ***' + +def baz + yield [1,2,3] +end + +baz { |a| i a } # [1,2,3] +baz { |a,b| i a,b } # 1,2 +baz { |a,b,c| i a,b,c } # 1,2,3 +baz { |*a| i a } # [[1,2,3]] (!) +baz { |a,*b| i a,b } # 1, [2,3] + +puts '*** foo ***' + +def foo + yield 1,2,3 +end + +foo { |a| i a } # [1,2,3] W +foo { |a,b| i a,b } # 1,2 +foo { |a,b,c| i a,b,c } # 1,2,3 +foo { |*a| i a } # [1,2,3] +foo { |a,*b| i a,b } # 1, [2,3] + + +puts '*** goo ***' + +def goo + yield *[1,2,3] +end + +goo { |a| i a } # [1,2,3] W +goo { |a,b| i a,b } # 1,2 +goo { |a,b,c| i a,b,c } # 1,2,3 +goo { |*a| i a } # [1,2,3] (!) +goo { |a,*b| i a,b } # 1, [2,3] + +puts '********' + +x = [1,2,3] +*z = x +i z +*z = *x +i z + + + + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Blocks.rb b/merlin/main/languages/ruby/Experimental/Misc/Blocks.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Experimental/Misc/BlocksBreakRedo.rb b/merlin/main/languages/ruby/Experimental/Misc/BlocksBreakRedo.rb new file mode 100644 index 0000000000..0180995efa --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/BlocksBreakRedo.rb @@ -0,0 +1,22 @@ +# should print 0 1 2 break + +a = 3 +10.times { |x| + puts x + a = a - 1 + eval('puts "break"; break') if (a == 0) +} + +puts '---' + +# should print: +# 0 redo 0 redo 0 1 2 3 +# doesn't work in Ruby.NET (hangs since redo is redoing itself) + +a = 10 +4.times { |x| + puts x + a = a - 1 + eval('puts "redo"; redo') if (a > 0) +} + diff --git a/merlin/main/languages/ruby/Experimental/Misc/BlocksRetry.rb b/merlin/main/languages/ruby/Experimental/Misc/BlocksRetry.rb new file mode 100644 index 0000000000..8cafca4065 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/BlocksRetry.rb @@ -0,0 +1,27 @@ +$retry = true + +def do_until(cond) + puts 'begin' + + if cond + $retry = false # nop + return # return + end + + puts 'yield:' + yield + puts 'retry:' + + return # retry + + puts 'end' +end + +i = 0 + +while $retry # nop + do_until(i > 10) do + puts i + i += 1 + end +end # nop diff --git a/merlin/main/languages/ruby/Experimental/Misc/BooleanExprs.rb b/merlin/main/languages/ruby/Experimental/Misc/BooleanExprs.rb new file mode 100644 index 0000000000..16fc6cc2cd --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/BooleanExprs.rb @@ -0,0 +1,14 @@ +def t; print 'T '; true; end +def f; print 'F '; false; end + +puts(t && t) +puts(f && t) +puts(t && f) +puts(f && f) + +puts(t || t) +puts(f || t) +puts(t || f) +puts(f || f) + +puts(f || f && t && t) \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Bracketing.rb b/merlin/main/languages/ruby/Experimental/Misc/Bracketing.rb new file mode 100644 index 0000000000..3c55f71b50 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Bracketing.rb @@ -0,0 +1,57 @@ +puts +(0..255).each do |x| + begin + eval("%q#{x.chr}a#{x.chr}") + puts "0x#{sprintf('%X', x)}('%q#{x.chr}a#{x.chr}'): OK" + rescue SyntaxError: + end +end + +puts +(0..255).each do |x| + begin + eval("%#{x.chr}(a)") unless x.chr == 'x' + puts "0x#{sprintf('%X', x)}('%#{x.chr}(a)'): OK" + rescue SyntaxError: + end +end + +puts %q(a) +puts %q +puts %q{a} +puts %q[a] + +puts "----" + +#nesting: +puts %q(a ((a) b)) + +puts "----" + +#multiline: +puts %q(a ( +(a) b +) +) + +puts "----" + +#multiline: +puts %q-a ( +(as + - + +puts "----" + +puts "dasdas +a + das +das dasd asd as +" + +puts "----" + +#escaping: +puts %q-foo\-bar- +puts %q(foo\(bar) +puts %q(foo\)bar) diff --git a/merlin/main/languages/ruby/Experimental/Misc/Builtins.rb b/merlin/main/languages/ruby/Experimental/Misc/Builtins.rb new file mode 100644 index 0000000000..192f2d13c1 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Builtins.rb @@ -0,0 +1,42 @@ +def dump(mod) + puts "#{mod.instance_of?(Module) ? "module" : "class"} #{mod}" + + puts " class: #{mod.class}" + puts " super: #{mod.superclass}" if mod.method_defined?(:superclass) + + strict_ancestors = mod.ancestors - [mod] + + print ' ancestors: ' + mod.ancestors.each { |a| print "#{a}," } + puts + puts + + puts ' declared private instance methods:' + ms = mod.private_instance_methods(false) + ms.sort.each { |m| puts " #{m}" } + + puts ' declared protected instance methods:' + ms = mod.protected_instance_methods(false) + ms.sort.each { |m| puts " #{m}" } + + puts ' declared public instance methods:' + ms = mod.public_instance_methods(false) + ms.sort.each { |m| puts " #{m}" } + + puts ' declared singleton methods:' + ms = mod.singleton_methods(false) + ms.sort.each { |m| puts " #{m}" } + + puts ' declared class variables:' + ms = mod.class_variables + strict_ancestors.each { |a| ms = ms - a.class_variables } + ms.sort.each { |m| puts " #{m}" } + + puts ' declared constants:' + ms = mod.constants + strict_ancestors.each { |a| ms = ms - a.constants } + ms.sort.each { |m| puts " #{m}" } + + puts 'end' + puts +end diff --git a/merlin/main/languages/ruby/Experimental/Misc/BuiltinsAll.rb b/merlin/main/languages/ruby/Experimental/Misc/BuiltinsAll.rb new file mode 100644 index 0000000000..b34a12ef3c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/BuiltinsAll.rb @@ -0,0 +1,106 @@ +require 'Builtins.rb' +dump(Kernel) +dump(Object) +dump(Module) +dump(Class) +dump(Array) +dump(Numeric) +dump(Integer) +dump(Fixnum) +dump(Bignum) +dump(Enumerable) +dump(String) +dump(Math) +dump(Range) +dump(Regexp) +dump(Hash) + +dump(NilClass) +dump(TrueClass) +dump(FalseClass) +dump(Symbol) + +dump(Exception) + +dump(NoMemoryError); +dump(ScriptError); +dump(LoadError); +dump(NotImplementedError); +dump(SyntaxError); +dump(SignalException); +dump(Interrupt); +dump(StandardError); +dump(ArgumentError); +dump(IOError); +dump(EOFError); +dump(IndexError); +dump(LocalJumpError); +dump(NameError); +dump(NoMethodError); +dump(RangeError); +dump(FloatDomainError); +dump(RegexpError); +dump(RuntimeError); +dump(SecurityError); +dump(SystemCallError); +dump(ThreadError); +dump(TypeError); +dump(ZeroDivisionError); +dump(SystemExit); +dump(SystemStackError); + +dump(Proc); +dump(Method); +dump(UnboundMethod); +dump(Thread); +dump(IO); +dump(File); + +class C + def instC() + end + + def C.clsC() + end +end + +class D < C + def instD() + end + + def C.clsCD() + end + + def D.clsD() + end +end + +module M + private + + def M.private_c + end + + def private_i + end + + public + + def M.public_c + end + + def public_i + end + + protected + + def M.protected_c + end + + def protected_i + end +end + +dump(C) +dump(D) +dump(M) diff --git a/merlin/main/languages/ruby/Experimental/Misc/BuiltinsMy.rb b/merlin/main/languages/ruby/Experimental/Misc/BuiltinsMy.rb new file mode 100644 index 0000000000..3cc564fc4c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/BuiltinsMy.rb @@ -0,0 +1,22 @@ +module MyKernel + private + def my_puts(x) + MyKernel.my_puts + end + + public + def MyKernel.my_puts(x) + puts "my_#{x}" + end +end + +class MyObject + include MyKernel +end + +foo = MyObject.new +#foo.my_puts 'bar' +#foo.puts 'bar' + +require 'Builtins.rb' +dump(MyKernel) \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/CallAmbig.rb b/merlin/main/languages/ruby/Experimental/Misc/CallAmbig.rb new file mode 100644 index 0000000000..14b0ec384d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/CallAmbig.rb @@ -0,0 +1,29 @@ +=begin + +Method call with array argument vs. array item access +IDENTIFIER whitespaceopt '[' arg ']': +1. IDENTIFIER is local variable => array item access +2. whitespace => function call with parameter '[' arg ']' +3. array item access + +=end + +def function(*a) print "F:"; [1,2,3] end +def both(*a) print "B:"; [1,2,3] end + +var = %w[a b c] +both = %w[a b c] + +puts "--" +puts both[1] # array-item-access(both, 1) +puts "--" +puts both [1] # array-item-access(var, 1) +puts "--" +puts var[1] # array-item-access(var, 1) +puts "--" +puts var [1] # array-item-access(var, 1) +puts "--" +puts function[1] # array-item-access(function-call(function), 1) +puts "--" +puts function [1] # function-call(function, [1]) +puts "--" diff --git a/merlin/main/languages/ruby/Experimental/Misc/Comments.rb b/merlin/main/languages/ruby/Experimental/Misc/Comments.rb new file mode 100644 index 0000000000..db2f7c57b6 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Comments.rb @@ -0,0 +1,9 @@ +print 1 + +=begin 2 + +3 + +=end bar 4 + +5 + +#should print 6: lines starting by =begin and ending with =end are ignored + + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Constants.rb b/merlin/main/languages/ruby/Experimental/Misc/Constants.rb new file mode 100644 index 0000000000..2c48262774 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Constants.rb @@ -0,0 +1,53 @@ +module M + C = 1 + + def foo + puts "foo: #{C}" + + def foo2 + puts "foo2: #{C}" + end + end + + define_method(:bob) { puts "bob: #{C}"; } +end + +class C + include M + C = 2 + + def bar + puts "bar: #{C}" + + def bar2 + puts "bar2: #{C}" + end + end + + define_method(:baz) { puts "baz: #{C}" } +end + +x = C.new + +x.foo +x.foo2 +x.bar +x.bar2 +x.bob +x.baz + +M.module_eval('def f; puts "f: #{C}"; end') +C.module_eval('def g; puts "g: #{C}"; end') + +x.f +x.g + +x.instance_eval('def h; puts "h: #{C}"; end') + +x.h + + + + + + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Constants2.rb b/merlin/main/languages/ruby/Experimental/Misc/Constants2.rb new file mode 100644 index 0000000000..502f75fbdd --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Constants2.rb @@ -0,0 +1,31 @@ +class C +end + +module M + class << 'x' + $Sx = self + class << C + $SC = self + @@a = 1 + class_variable_set :@@ea, 1 + end + @@b = 2 + class_variable_set :@@eb, 2 + end + @@c = 3 + class_variable_set :@@em, 3 +end + +p M.class_variables.sort +p $SC.class_variables.sort +p $Sx.class_variables.sort + +class C + @@x = 1 +end + +class D < C + remove_class_variable :@@x rescue puts 'Error' + @@y = 'foo bar baz' + p remove_class_variable(:@@y) +end diff --git a/merlin/main/languages/ruby/Experimental/Misc/CustomUntil.rb b/merlin/main/languages/ruby/Experimental/Misc/CustomUntil.rb new file mode 100644 index 0000000000..3fcb2187d2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/CustomUntil.rb @@ -0,0 +1,11 @@ +def do_until(cond) + if cond then return end + yield + retry +end + +i = 0 +do_until(i > 4) do + puts i + i = i + 1 +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/DefaultArgs.rb b/merlin/main/languages/ruby/Experimental/Misc/DefaultArgs.rb new file mode 100644 index 0000000000..4280182b5e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/DefaultArgs.rb @@ -0,0 +1,20 @@ +def foo + yield + raise +end + +def goo(a = foo { puts 'zoo' }) + puts a +rescue + puts 'rescued in goo' +ensure + puts 'ensure in goo' +end + +begin + goo +rescue + puts 'rescued out' + puts $!.backtrace +end + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Eolns.rb b/merlin/main/languages/ruby/Experimental/Misc/Eolns.rb new file mode 100644 index 0000000000..66093ef898 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Eolns.rb @@ -0,0 +1,67 @@ +x = 1 +y = 2 + +puts 'foo' if +x == +1 + +puts x = if true then false else true end # false + +puts x = +if true then false else true end # false + +puts x = +if +true then false else true end # false + +puts x = +if +true +then false else true end # false + +puts x = +if +true +then +false else true end # false + +puts x = +if +true +then +false +else true end # false + +puts x = +if +true +then +false +else +true end # false + +puts x = +if +true +then +false +else +true +end # false + +puts (if true then false else true end) # false +puts true if true # true + +class C; end +class C; def foo; end; end +class C; def foo() end; end + +# class C; def foo end; end +# ^ +# syntax error, unexpected kEND, expecting '\n' or ';' + +# class C end +# ^ +# syntax error, unexpected kEND, expecting '<' or '\n' or ';' + + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Eval.rb b/merlin/main/languages/ruby/Experimental/Misc/Eval.rb new file mode 100644 index 0000000000..8cd740ab68 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Eval.rb @@ -0,0 +1,8 @@ +module Kernel + alias old_eval eval +end + +x = 1 + +old_eval("puts x") + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Exceptions.rb b/merlin/main/languages/ruby/Experimental/Misc/Exceptions.rb new file mode 100644 index 0000000000..9441369e20 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Exceptions.rb @@ -0,0 +1,22 @@ +puts 'Begin' +x = class A + puts 'Raise' + 1 + raise + puts 'Unreachable' + 2 +rescue IOError + puts 'Rescue1' + 3 +rescue + puts 'Rescue2' + 4 +else + puts 'Else' + 6 +ensure + puts 'Ensure' + 5 +end +puts x +puts 'End' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Exceptions1.rb b/merlin/main/languages/ruby/Experimental/Misc/Exceptions1.rb new file mode 100644 index 0000000000..6f15326795 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Exceptions1.rb @@ -0,0 +1,17 @@ +puts 'Begin' +begin + puts 'Class' + class C + puts 'NoRaise' + rescue + puts 'Rescue' + else + puts 'Else' + ensure + puts 'Ensure' + end + puts 'ClassEnd' +rescue + puts 'OutterRescue' +end +puts 'End' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Exceptions3.rb b/merlin/main/languages/ruby/Experimental/Misc/Exceptions3.rb new file mode 100644 index 0000000000..92812400e4 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Exceptions3.rb @@ -0,0 +1,37 @@ +def foo + puts 'Foo' + retry +end + +puts 'Begin' +i = 0 +begin + puts 'Class' + begin + puts 'InTry' + if i == 0 then + raise + end + puts 'NoRaise' + rescue + puts 'Rescue' + i = i + 1 + puts 'Retrying' + #retry + #eval('eval("retry")') + #reties the block: + #1.times { |*| print 'B'; retry } + foo + puts 'Unreachable' + else + puts 'Else' + #eval('retry'); + ensure + puts 'Ensure' + end + puts 'ClassEnd' +rescue + puts "OutterRescue:" + puts $!.backtrace +end +puts 'End' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Exceptions4.rb b/merlin/main/languages/ruby/Experimental/Misc/Exceptions4.rb new file mode 100644 index 0000000000..6a63c430f0 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Exceptions4.rb @@ -0,0 +1,23 @@ +# retry cannot be "rescued" +# output: ABCA + +i = 0 +begin + puts 'A' + raise if i == 0 +rescue + i += 1 + puts 'B' + begin + puts 'C' + retry + rescue Exception => e + puts 'D' + puts e + end +end + + + + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Exceptions5.rb b/merlin/main/languages/ruby/Experimental/Misc/Exceptions5.rb new file mode 100644 index 0000000000..08e567454d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Exceptions5.rb @@ -0,0 +1,24 @@ +def bar + begin + eval(' + begin + eval("eval(\'retry\')") + rescue Exception => e + puts "E" + end + ') + rescue + puts 'A' + end +rescue LocalJumpError => e + puts 'B' +end + +def foo + bar +rescue LocalJumpError => e + puts 'C' +end + +foo + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Functions.rb b/merlin/main/languages/ruby/Experimental/Misc/Functions.rb new file mode 100644 index 0000000000..dd13a02fbd --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Functions.rb @@ -0,0 +1,4 @@ +# functions are private methods of Object +def foo() 1 end + +puts Object.private_methods.include?("foo") # true \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Hash.rb b/merlin/main/languages/ruby/Experimental/Misc/Hash.rb new file mode 100644 index 0000000000..1bc8acfbb1 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Hash.rb @@ -0,0 +1,18 @@ +class String + alias x dup + alias y dup + def dup + puts 'dup' + x + end + + def freeze + puts 'freeze' + y + end +end + +x = { "sadA", 1} +x["foo"] = 2 + +puts x.keys[0].frozen? \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Heredoc.rb b/merlin/main/languages/ruby/Experimental/Misc/Heredoc.rb new file mode 100644 index 0000000000..a74592a936 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Heredoc.rb @@ -0,0 +1,119 @@ + +x = < +EOT1 + +puts <<'EOT1 blah' +cup<\t> +EOT1 blah + +puts <<"EOT1 blah" +cup<\t> +EOT1 blah + +puts <<"EOT1-bar", <<' EOT2', <<-"EOT3".upcase, <<-'EOT4' +really cool cup <\t> +EOT1-bar +one more cup <\t> + EOT2 +space-indent-ended heredoc here "\t" + EOT3 +one more space-indent-ended heredoc here "\t" + EOT4 + +puts "---------" + +x = <<"EOT".downcase() << "FOO" + "BAR" + <<"GOO" +A +FOO +B +EOT +C +GOO + +puts x + +puts "---------" + +def foo(a,b) + yield + "[#{a} #{b}]" +end + +# block must be on the same line (next line is part of heredoc) +puts x = foo(<<"FOO", <<"BAR") { puts "BLOCK" } +foofoo +FOO +barbar +BAR + +# puts <<-" space" +# doesn't work because lexer eats all whitespace and then doesn't see a space as a part of heredoc-end token +# space + +# no whitespace following <<: +#puts << "EOT1 blah" +#cup<\t> +#EOT1 blah + +#error: +#x = < left shift +2. IDENTIFIER is local variable => left shift +3. here-doc + +see http://seclib.blogspot.com/2005/11/more-on-leftshift-and-heredoc.html + +=end + +HEREDOC = 1 + +def function(*a) print "F:"; a end +def both(*a) print "B:"; a end + +var = 0 +both = 0 + +# whitespace +print function< ok, no method calls +p defined?(x(x(1),2,3)) + +puts '--' + +# invalid number of params -> const undefined +p defined?(y(1,2,3)::C) + +puts '--' + +# invalid number of params -> const undefined +p defined?(e(1)::C) +p defined?((e(1) rescue p $!;Y)::C) +p $! + +puts '--' +p defined?(y(f(1))::C) +puts '--' +p defined?(x(f(1))::y(f(2))::C) +puts '--' +p defined?(y(f(1))::D) +puts '--' +p defined?(x(f(1))::y(f(2))::D) + +puts '--' +p defined?(e(1)::e(2)) +p defined?(x(1,3).y(2,3)) + diff --git a/merlin/main/languages/ruby/Experimental/Misc/IteratorRetry.rb b/merlin/main/languages/ruby/Experimental/Misc/IteratorRetry.rb new file mode 100644 index 0000000000..faa6c5910b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/IteratorRetry.rb @@ -0,0 +1,16 @@ +# calls with blocks -> make arguments and body lambdas? + +def repeat(cond) + yield + retry if not cond +end + +j = 0; +repeat (j >= 10) do + j += 1 + puts j +end + + + + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Jumps.rb b/merlin/main/languages/ruby/Experimental/Misc/Jumps.rb new file mode 100644 index 0000000000..ae166c247c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Jumps.rb @@ -0,0 +1,11 @@ +i = 0 +while i < 10 do + + puts i + + class Foo + break + end + + i += 1 +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/LHS.rb b/merlin/main/languages/ruby/Experimental/Misc/LHS.rb new file mode 100644 index 0000000000..e35b53016c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/LHS.rb @@ -0,0 +1,14 @@ +class C + public + + def foo=(x) + @f = x + end + def foo() + @f + end +end + +x = C.new + +3.times { |x.foo| puts x.foo } \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/LoadOnce.rb b/merlin/main/languages/ruby/Experimental/Misc/LoadOnce.rb new file mode 100644 index 0000000000..1a195a66b4 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/LoadOnce.rb @@ -0,0 +1,5 @@ +$: << "LoadOnceDir" +puts "$: = #{$:}" +puts "$\" = #{$"}" +require 'LoadOnce1' +puts "$\" = #{$"}" diff --git a/merlin/main/languages/ruby/Experimental/Misc/Locals.rb b/merlin/main/languages/ruby/Experimental/Misc/Locals.rb new file mode 100644 index 0000000000..b884058480 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Locals.rb @@ -0,0 +1,9 @@ +def foo() + b = 123 + puts "before eval" + eval("a = b;puts a,b") + puts "after eval" + puts a +end + +foo \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Loops.rb b/merlin/main/languages/ruby/Experimental/Misc/Loops.rb new file mode 100644 index 0000000000..0382c43c11 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Loops.rb @@ -0,0 +1,136 @@ +# prints 1..10, var is not controlling the loop + +for var in 1..10 + puts "var = #{var}" + if var > 5 + var = var + 2 + end +end + +puts '---' + +i = 3 +x = while i > 0 do + puts i + i = i - 1 +end +puts x + +puts '---' + +i = 3 +x = while i > 0 do + puts i + if i == 2 then + eval("break") + end + i = i - 1 +end +puts x + +puts '---' + +i = 3 +x = while i > 0 do + puts i + if i == 2 then + eval("break 'foo'") + end + i = i - 1 +end +puts x + +puts '---' + +i = 3 +j = 2 +x = while i > 0 do + puts i + if i == 2 and j > 0 then + j = j - 1 + eval('redo') + end + i = i - 1 +end +puts x + +puts '---' + +def foo + eval('break') +rescue LocalJumpError => e + puts 'A' +end + +begin + foo +rescue LocalJumpError => e + puts 'B' +end + +puts '---' + +def foo2 + i = 0 + while i < 5 do + eval(" + begin + i += 1 + puts i + eval('break') + rescue LocalJumpError => e + puts 'A' + end"); + end + + eval(" + begin + i += 1 + puts i + eval('break') + rescue LocalJumpError => e + puts 'C' + end"); +end + +begin + foo2 +rescue LocalJumpError => e + puts 'B' +end + +puts '---' + +x = begin 1; 2; end +puts x + +puts '---' + +while begin puts 'foo'; break; true; end do + puts 'bar' +end + +puts '---' + +i = 0 +while begin puts 'foo'; redo; puts 'baz'; true; end do + puts i + i += 1 + if i == 5 then break end +end + +puts '---' + +i = 0 +while begin puts i; i += 1; next unless i > 5; puts 'baz'; i < 10; end do + puts 'bar' + i += 1 +end + +puts '---' + +i = 0 +until begin puts i; i += 1; next unless i > 5; puts 'baz'; i >= 10; end do + puts 'bar' + i += 1 +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/MethodNames.rb b/merlin/main/languages/ruby/Experimental/Misc/MethodNames.rb new file mode 100644 index 0000000000..bbd289c95f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/MethodNames.rb @@ -0,0 +1,13 @@ +def Foo() + 'method Foo' +end + +def Goo() + 'method Goo' +end + +Foo = 'constant Foo' + +puts(Foo) # constant Foo +puts(Foo()) # method Foo +# puts(Goo) # uninitialized constant diff --git a/merlin/main/languages/ruby/Experimental/Misc/Methods.rb b/merlin/main/languages/ruby/Experimental/Misc/Methods.rb new file mode 100644 index 0000000000..cf964dab0d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Methods.rb @@ -0,0 +1,50 @@ +class Class + def new1() + puts 'new1' + end +end + +class C + def a1() + puts '1' + end + def C.a2() + puts '2' + end + def self.a3() + puts '3' + end + def C.new1() + puts 'new1 override' + end +end + +def C.a4() + puts '4' +end + +puts "---> C.instance_methods:" +puts C.instance_methods.sort # a1 + +puts "---> C.methods:" +puts C.methods.sort # a2, a3, a4 + +puts "---> C.class.instance_methods:" +puts C.class.instance_methods.sort + +puts "---> C.class.methods:" +puts C.class.methods.sort + +C.new.a1() # 1 +# C.new.a2() # undefined +# C.new.a3() # undefined +# C.new.a4() # undefined + +# C.a1() # undefined +C.a2() # 2 +C.a3() # 3 +C.a4() # 4 + +Class.new1 #new1 +C.new1 #new1 override + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Modules.rb b/merlin/main/languages/ruby/Experimental/Misc/Modules.rb new file mode 100644 index 0000000000..8b5ce5e0c1 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Modules.rb @@ -0,0 +1,17 @@ +module A + module B + module C + p Module.nesting + end + end + + p Module.nesting +end + +p Module.nesting + +module A + module B::C::D + p Module.nesting + end +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/NestedBlocks.rb b/merlin/main/languages/ruby/Experimental/Misc/NestedBlocks.rb new file mode 100644 index 0000000000..f681847e86 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/NestedBlocks.rb @@ -0,0 +1,11 @@ +def outer + puts 'outer' + yield +end + +def inner + puts 'inner' + yield +end + +outer { inner { puts 'here' } } diff --git a/merlin/main/languages/ruby/Experimental/Misc/NextInBlock.rb b/merlin/main/languages/ruby/Experimental/Misc/NextInBlock.rb new file mode 100644 index 0000000000..a13ea0762e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/NextInBlock.rb @@ -0,0 +1,7 @@ +i = 0 +x = 5.times { |x| + puts x + i = i + 1 + if i < 3 then next end + puts 'bar' +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/NotOperator.rb b/merlin/main/languages/ruby/Experimental/Misc/NotOperator.rb new file mode 100644 index 0000000000..d1d3ede533 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/NotOperator.rb @@ -0,0 +1,40 @@ +x = true +puts !x +puts !!x +puts !!!x + +if not x then print 'A' end +if not not x then print 'B' end +if not !x then print 'C' end +if not not !!!x then print 'D' end +# if !(x**2 == 1) then print 'E' end + +puts + +a = x ? !x : x + +class C + def bar(i, &b) + puts b[i] + self + end +end + +c = C.new +a = !c.bar(1) { |x| x + 1 }.bar(2) { |x| x * 10 }.bar(3) { |x| x * 10 } ? 'X' : 'Y' +puts a + +class D + def foo=() + print 'foo=' + end + + def bar() + print 'bar' + end +end + +d = D.new +d.foo() + + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Operators.rb b/merlin/main/languages/ruby/Experimental/Misc/Operators.rb new file mode 100644 index 0000000000..c6bb44d783 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Operators.rb @@ -0,0 +1,54 @@ +x = false +y = true + + +puts z = x || y # true +puts z # true +puts z = x or y # false +puts z # false +puts (z = x) or y # false +puts z # false +puts ((z = x) or y) # true +puts z # false + +puts + +z = false +puts x or y and z # false +puts x or y && z # false +puts x || y and z # true +puts x || y && z # false + +puts + +puts false || 1 # 1 +puts nil || 2 # 2 +puts true || 3 # true +puts 3 || true # 3 +puts 5 || 4 # 5 +puts x ||= 6 # 6 +puts y ||= 6 # true + +puts + +puts false && 1 # false +puts nil && 2 # nil +puts true && 3 # 3 +puts 3 && true # true +puts 5 && 4 # 4 +puts x &&= 6 # 6 +puts y &&= 6 # 6 + +puts + +class C + def foo=(x) puts "write(#{x})" end + def foo() puts "read()"; "bar" end +end + +c = C.new + +puts c.foo + "1" # read() bar1 +puts c.foo = c.foo + "1" # read() write(bar1) bar1 +puts c.foo += "1" # read() write(bar1) bar1 + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Parameters.rb b/merlin/main/languages/ruby/Experimental/Misc/Parameters.rb new file mode 100644 index 0000000000..ae198289ce --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Parameters.rb @@ -0,0 +1,29 @@ +def y a + yield *a +end + +def i *a + puts '---' + a.each { |x| puts x.inspect } +end + +y [1,2] do |x1,x2,x3,*z| + i x1,x2,x3,z # 1,2,nil,[] +end + +x1,x2,x3,*z = 1,2 +i x1,x2,x3,z + +############## + +y [1,2] do |x| + i x +end + +x = 1,2 +i x + +############## + + + diff --git a/merlin/main/languages/ruby/Experimental/Misc/ProcCalls.rb b/merlin/main/languages/ruby/Experimental/Misc/ProcCalls.rb new file mode 100644 index 0000000000..b7fcba6c5c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/ProcCalls.rb @@ -0,0 +1,10 @@ +def foo &p + puts 'foo.begin' + p[] + puts 'foo.end' +end + +foo { + puts 'block' + break +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Procs.rb b/merlin/main/languages/ruby/Experimental/Misc/Procs.rb new file mode 100644 index 0000000000..647ca31d0c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Procs.rb @@ -0,0 +1,14 @@ +class Proc + def initialize + puts 'initialized' + end +end + +def foo &p + puts p +end + +Proc.new {} # prints "initialized" +foo { } # prints nothing +lambda {} # prints nothing + diff --git a/merlin/main/languages/ruby/Experimental/Misc/RecursiveBreak.rb b/merlin/main/languages/ruby/Experimental/Misc/RecursiveBreak.rb new file mode 100644 index 0000000000..9b866bce12 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/RecursiveBreak.rb @@ -0,0 +1,22 @@ +def foo + puts 'foo-begin' + bar { puts 'block'; break } + puts 'foo-end' +end + +def bar &p + puts 'bar-begin' + # $g = p + baz &p + yield + puts 'bar-end' +end + +def baz &p + puts 'baz-begin' + yield + puts 'baz-end' +end + +foo +# baz &$g \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/RecursiveBreak2.rb b/merlin/main/languages/ruby/Experimental/Misc/RecursiveBreak2.rb new file mode 100644 index 0000000000..9ee24516ac --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/RecursiveBreak2.rb @@ -0,0 +1,13 @@ +def foo &p + puts 'foo1' + 3.times &p + puts 'foo2' +end + +def goo + puts 'goo1' + foo { |x| puts x; break } + puts 'goo2' +end + +goo \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/RecursiveCF.rb b/merlin/main/languages/ruby/Experimental/Misc/RecursiveCF.rb new file mode 100644 index 0000000000..4c7b664eb3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/RecursiveCF.rb @@ -0,0 +1,20 @@ +def foo + puts 'foo-begin' + bar { puts 'block'; break } + puts 'foo-end' +end + +def bar &p + puts 'bar-begin' + $g = p + puts 'bar-end' +end + +def baz &p + puts 'baz-begin' + yield + puts 'baz-end' +end + +foo +baz &$g \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/RecursiveNext.rb b/merlin/main/languages/ruby/Experimental/Misc/RecursiveNext.rb new file mode 100644 index 0000000000..edd5177e64 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/RecursiveNext.rb @@ -0,0 +1,22 @@ +def foo + puts 'foo-begin' + bar { puts 'block'; next } + puts 'foo-end' +end + +def bar &p + puts 'bar-begin' + # $g = p + baz &p + yield + puts 'bar-end' +end + +def baz &p + puts 'baz-begin' + yield + puts 'baz-end' +end + +foo +# baz &$g \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/RegEx.rb b/merlin/main/languages/ruby/Experimental/Misc/RegEx.rb new file mode 100644 index 0000000000..fe414ae12b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/RegEx.rb @@ -0,0 +1,31 @@ +# brackets +puts %r(a*) +puts %r +puts %r{a*} +puts %r[a*] + +puts "# arbitrary separators:" + +puts +(0..255).each do |x| + print "0x#{sprintf('%X', x)}('%r#{x.chr}regex#{x.chr}'): " + begin + print eval("%r#{x.chr}regex#{x.chr}") + rescue SyntaxError: + print "error" + end + puts +end + +puts "# options (note that comments and delimiters are included):" + +puts +(0..255).each do |x| + print "0x#{sprintf('%X', x)}('/regex/#{x.chr}'): " + begin + print eval("/regex/#{x.chr}") + rescue SyntaxError: + print "error" + end + puts +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Require1.a.rb b/merlin/main/languages/ruby/Experimental/Misc/Require1.a.rb new file mode 100644 index 0000000000..8eb1b7fb62 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Require1.a.rb @@ -0,0 +1,13 @@ +def foo() + 'foo' +end + +class C + def bar() + puts 'bar' + end +end + +a = 'bar' +$g = 1 +@f = 'goo' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Require1.main.rb b/merlin/main/languages/ruby/Experimental/Misc/Require1.main.rb new file mode 100644 index 0000000000..f7aa139406 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Require1.main.rb @@ -0,0 +1,22 @@ +a = 'a' + +puts require('Require1.a') # true +puts foo() # foo +# error: puts self.foo() # private method `foo' called +puts instance_variables() # @f +puts a # a +puts $g # 1 +puts @f # goo +puts C # C + +# wrapped + +puts load('Require1.b.rb', true) # true +# error: puts fooB() # undefined +# error: puts self.fooB() # private method `foo' called +puts instance_variables() # @f +# error: puts aB # undefined +puts $g # 1 +puts @fB # nill +# error: puts MB # uninitialized +# error: include MB # uninitialized \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/RescueClause.rb b/merlin/main/languages/ruby/Experimental/Misc/RescueClause.rb new file mode 100644 index 0000000000..3da148b60e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/RescueClause.rb @@ -0,0 +1,17 @@ +class Module + def ===(other) + puts '1:===' + puts $! + true + end +end + +class MyExc < Exception +end + +begin + raise MyExc, "my-message" +rescue IOError + puts $! + puts 'rescued' +end diff --git a/merlin/main/languages/ruby/Experimental/Misc/RescueClause2.rb b/merlin/main/languages/ruby/Experimental/Misc/RescueClause2.rb new file mode 100644 index 0000000000..bcf13fd48b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/RescueClause2.rb @@ -0,0 +1,55 @@ +class Module + alias old === + + def ===(other) + puts "compared: #{self} (T) === #{$!} ($!)" + + if ($i == 0) + $i = 1; + puts 'replacing $!' + $! = MyExc2.new + end + + old other + end +end + +class MyExc1 < Exception +end + +class MyExc2 < Exception +end + +def foo(i) + $i = i + raise MyExc1 +rescue MyExc2 # compares E1 !== E2, sets $! to a MyExc2 instance + puts "rescued A: #{$!.class}" +rescue MyExc1 + puts "rescued B: #{$!.class}" +rescue MyExc2 + puts "rescued C: #{$!.class}" +end + +def bar(i) + $i = i + raise MyExc1 +rescue MyExc2, # compares E1 !== E2, sets $! to a MyExc2 instance + MyExc2 # catches (uses the updated value of $!) + + puts "rescued A: #{$!.class}" +rescue MyExc1 + puts "rescued B: #{$!.class}" +rescue MyExc2 + puts "rescued C: #{$!.class}" +end + + +foo(0) +puts '-------' +foo(1) +puts '-------' +bar(0) +puts '-------' +bar(1) +puts '-------' diff --git a/merlin/main/languages/ruby/Experimental/Misc/RescueClause3.rb b/merlin/main/languages/ruby/Experimental/Misc/RescueClause3.rb new file mode 100644 index 0000000000..7a523a9ea2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/RescueClause3.rb @@ -0,0 +1,26 @@ +class Module + alias old === + + def ===(other) + puts "compared: #{self} (T) === #{$!} ($!)" + + puts 'replacing $!' + $! = MyExc2.new + + old other + end +end + +class MyExc1 < Exception +end + +class MyExc2 < Exception +end + +def foo() + raise MyExc1 +rescue MyExc1 => e + puts "rescued: $! = #{$!}, e = #{e}" +end + +foo \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/RescueClause4.rb b/merlin/main/languages/ruby/Experimental/Misc/RescueClause4.rb new file mode 100644 index 0000000000..6868ec5b16 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/RescueClause4.rb @@ -0,0 +1,27 @@ +# $! is stacked + +class A < Exception; end +class B < Exception; end +class C < Exception; end + +$! = IOError.new +begin + raise A +rescue A + begin + raise B + rescue B + begin + raise C + rescue C + puts $! # C + end + puts $! # B + end + puts $! # A +end +puts $! # IOError + + + + diff --git a/merlin/main/languages/ruby/Experimental/Misc/RescueClause5.rb b/merlin/main/languages/ruby/Experimental/Misc/RescueClause5.rb new file mode 100644 index 0000000000..bdb70a615c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/RescueClause5.rb @@ -0,0 +1,34 @@ +# order of evaluation + +class Module + alias old === + + def ===(other) + puts "cmp(#{self}, #{other})" + + old other + end +end + +class A < Exception; end +class B < Exception; end +class C < Exception; end +class D < Exception; end +class E < Exception; end +class F < Exception; end +class G < Exception; end + +def id(t) + puts "r(#{t})" + t +end + +def foo + raise F +rescue id(A),id(B),id(C) + puts 'rescued 1' # unreachable +rescue id(E),id(F),id(G) + puts 'rescued 2' +end + +foo diff --git a/merlin/main/languages/ruby/Experimental/Misc/Retry.rb b/merlin/main/languages/ruby/Experimental/Misc/Retry.rb new file mode 100644 index 0000000000..758c02f5d8 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Retry.rb @@ -0,0 +1,5 @@ +def foo(a,b, c = a + b, d = c + 1) + puts a,b,c,d +end + +foo(1,2) \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/RetryInBlock.rb b/merlin/main/languages/ruby/Experimental/Misc/RetryInBlock.rb new file mode 100644 index 0000000000..843782627a --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/RetryInBlock.rb @@ -0,0 +1,6 @@ +i = 0 +3.times { |x| + puts x + i = i + 1 + if i == 2 then retry end +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/RetryRecursive.rb b/merlin/main/languages/ruby/Experimental/Misc/RetryRecursive.rb new file mode 100644 index 0000000000..626dbf1412 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/RetryRecursive.rb @@ -0,0 +1,28 @@ +$i = 0 + +def zoo + puts 'zoo' + 1 +end + +def foo + puts 'foo' + bar(zoo) { puts 'block'; retry } +end + +def bar a, &p + puts 'bar' + # $g = p + baz &p +end + +def baz &p + puts 'baz' + $i += 1 + if $i < 10 then + yield + end +end + +foo +# baz &$g \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Return.rb b/merlin/main/languages/ruby/Experimental/Misc/Return.rb new file mode 100644 index 0000000000..674ea7994b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Return.rb @@ -0,0 +1,21 @@ +def p(&p) + p +end + +def y + yield +end + +def goo + $x = p { + puts 'B' + return + } +end + +def zoo + y &$x +end + +goo +zoo \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Return0.rb b/merlin/main/languages/ruby/Experimental/Misc/Return0.rb new file mode 100644 index 0000000000..1413c705d0 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Return0.rb @@ -0,0 +1,76 @@ +def m1() + puts 'm1' + yield +end + +def defb(&b) + $b = b +end + +def defc(&c) + $c = c +end + +def m2(a, &b) + print "m2.begin(#{a})" + if (a == 0) + puts " calling m1(&b)" + m1 &$b + elsif (a == 4) + puts " defining block 'b'" + defb do + puts 'b1' + 1.times { |x| + puts 'b2' + 1.times { |y| + puts 'b3' + eval('return') + } + } + end + m2(a - 1) + elsif (a == 8) + puts + m2(a - 1) + + puts " calling m1(&c)" + m1 &$c + elsif (a == 10) + puts " defining block 'c'" + defc { + puts 'c1' + 1.times { |x| + puts 'c2' + 1.times { |y| + puts 'c3' + return + } + } + } + m2(a - 1) + else + puts + m2(a - 1) + end + puts "m2.end(#{a})" +end + +m2(12) + +puts '-----' + +def br + puts 'd1' + 1.times { |x| + puts 'd2' + 1.times { |y| + puts 'd3' + break + } + } + puts 'd4' + +end + +br + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Return1.rb b/merlin/main/languages/ruby/Experimental/Misc/Return1.rb new file mode 100644 index 0000000000..61c5eca3a0 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Return1.rb @@ -0,0 +1,81 @@ +def y() + puts 'yielding' + yield +end + +def defb(&b) + $b = b +end + +def defc(&c) + $c = c +end + +def defd(&d) + $d = d +end + +def foo(a) + print "foo.begin(#{a})" + if (a == 10) + puts " defining block 'b'" + defb { + puts 'in b' + return + } + foo(a - 1) + elsif (a == 7) + puts " defining block 'c'" + defc { + puts 'in c' + return + } + foo(a - 1) + elsif (a == 4) + puts " calling &c" # try b,c,d + y &$c + foo(a - 1) + elsif (a == 1) + puts " calling &b" # try b,c,d + y &$b + foo(a - 1) + elsif (a == 0) + else + puts + goo(2, a) + end + puts "foo.end(#{a})" +rescue Exception => e + puts 'Caught!' # unreachable +ensure + puts "foo.ensure(#{a})" +end + +def goo(b, a) + print " g.begin(#{a}, #{b})" + if (a == 9 and b == 1) + puts " defining block 'd'" + defd { + puts 'in d' + return + } + goo(b - 1, a) + elsif (a == 5 and b == 2) + puts " calling &b" # try b,c,d + y &$b + goo(b - 1, a) + elsif (b == 0) + puts + foo a - 1 + else + puts + goo(b - 1, a) + end + puts " g.end(#{a}, #{b})" +rescue Exception => e + puts 'Caught!' # unreachable +ensure + puts "goo.ensure(#{a},#{b})" +end + +foo 12 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Return2.rb b/merlin/main/languages/ruby/Experimental/Misc/Return2.rb new file mode 100644 index 0000000000..d9234480eb --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Return2.rb @@ -0,0 +1,35 @@ +def y() + puts 'yielding' + yield +rescue LocalJumpError => e + puts 'A' +end + +def defb(&b) + $b = b +end + +def foo + defb { + puts 'in b'; + begin + return + rescue LocalJumpError => e + puts 'X' + end + } +end + +def goo + y &$b +rescue LocalJumpError => e + puts 'B' +end + +foo + +begin + goo +rescue LocalJumpError => e + puts 'C' +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Return4.rb b/merlin/main/languages/ruby/Experimental/Misc/Return4.rb new file mode 100644 index 0000000000..68601debad --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Return4.rb @@ -0,0 +1,5 @@ +x = class C + return 1 +rescue Exception => e + puts e # unexpected return +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/ReturnFromRescueElse.rb b/merlin/main/languages/ruby/Experimental/Misc/ReturnFromRescueElse.rb new file mode 100644 index 0000000000..1e9427daca --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/ReturnFromRescueElse.rb @@ -0,0 +1,16 @@ +puts 'Begin' +x = class C + puts 'Raise' + 1 +rescue IOError + puts 'Rescue1' + 3 +else + puts 'Else' + 5 +ensure + puts 'Ensure' + 6 +end +puts x +puts 'End' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/ReturnFromThread.rb b/merlin/main/languages/ruby/Experimental/Misc/ReturnFromThread.rb new file mode 100644 index 0000000000..c0a76dfe57 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/ReturnFromThread.rb @@ -0,0 +1,38 @@ +def m + puts 'm' + yield +end + +puts 'A' + +begin + +t = Thread.new { + begin + puts 'B' + m { + begin + puts 'C' + return + rescue ThreadError + puts 'R1' # rescues here + end + } + puts 'D' + rescue ThreadError + puts 'R2' + end +} + +rescue ThreadError + puts 'R3' +end + + +puts 'E' + +begin + t.join +rescue ThreadError + putst 'R4' +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/ReturnNotExpression.rb b/merlin/main/languages/ruby/Experimental/Misc/ReturnNotExpression.rb new file mode 100644 index 0000000000..18be7bea75 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/ReturnNotExpression.rb @@ -0,0 +1,3 @@ +def foo() + x = return 1 +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/ReturnUnwindSimple.rb b/merlin/main/languages/ruby/Experimental/Misc/ReturnUnwindSimple.rb new file mode 100644 index 0000000000..d6728fe039 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/ReturnUnwindSimple.rb @@ -0,0 +1,19 @@ +def foo(a) + puts "begin #{a}" + if a == 3 then + 1.times { |x| + puts 'in block 1' + 1.times { |y| + puts 'in block 2' + return + } + } + end + + foo(a - 1) + puts "end #{a}" +end + +puts 'before' +foo 5 +puts 'after' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/ReturnValues.rb b/merlin/main/languages/ruby/Experimental/Misc/ReturnValues.rb new file mode 100644 index 0000000000..d4fadb855d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/ReturnValues.rb @@ -0,0 +1,31 @@ +def f1; return 1,2,3; end +def f2; return 1 => 2, 2 => 3, 3 => 4; end +def f3; return 1, 2, 3, 1 => 2, 2 => 3, 3 => 4; end +def f4; return 1, 2, 3, 1 => 2, 2 => 3, 3 => 4, *['a', 'b', 'c']; end + +def defb &b + b +end + +def f5 + c = defb { |*| puts 'foo' } + b = defb &c + return 1,2,3, 10=>20, 30=>40, *['a','b'] &['X','Y','Z'] # TODO? semanticS? +end + +# ERROR: block argument should not be given +#def f; return 1 => 2, 2 => 3, 3 => 4, &1 end +#def f; return 1,2,3, &1 end +#def f; return 1,2,3, 4 => 5, &1 end + +# & is an operator applied on the preceding array +def f6; return *['a','b'] &['a','Y','Z'] end + +puts f1.inspect +puts f2.inspect +puts f3.inspect +puts f4.inspect +puts f5.inspect +puts f6.inspect + + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Self.rb b/merlin/main/languages/ruby/Experimental/Misc/Self.rb new file mode 100644 index 0000000000..f6f3cb40c2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Self.rb @@ -0,0 +1,143 @@ +# class C +# +# end +# +# means something like +# +# if !defined(C) then C = Class.new('C') +# _function(C, ) +# +# global code -> function taking self, the self created by load/require +# + +u = nil +a = 1 + +puts u # nil +# error puts v # undefined local + +puts '- D ----------------------------' + +class D + puts ' in D:' + b = 'b' + C = 1 + puts ' locals: ',local_variables() # b + puts ' constants: ',constants() # C +end + +puts '- global ----------------------------' + +@@cls = 'cls' + +puts 'locals in global code: ',local_variables() # u,a,f +# undefined class_variables(): puts 'class vars in global code: ',class_variables() # @@cls +# undefined constants(): puts 'constants in global code: ',constants() # D + +puts '- class << Object.new ----------------------------' + +class << Object.new + puts ' in singleton:' + class E + end + puts ' class vars:',class_variables() # @@cls + puts ' constants: ',constants() # E + class EE + end +end + +puts '- F ----------------------------' + +module FM + def FM.d() + puts 'static d in FM' + end + + def d() + puts 'instance d in FM' + end +end + +class F + puts ' in F:' + puts self #F + + def m() + puts 'instance m in F:',self # + end + + def F.m() + puts 'class m in F:',self #F + end + + def F.k() + puts 'static k in F' + end + + def print(v) + puts 'instance print in F' + end + + include FM + + def g() + puts 'in g():' + m # instance m in F + self.m # instance m in F + # k # undefined + # self.k # undefined + d # instance d in FM + self.d # instance d in FM + # F.d # undefined + FM.d # static d in FM + print 'foo' # instance print in F + end +end + +f = F.new + +def F.n() + puts 'class F.n:',self #F +end + +def f.n() + puts 'instance f.n:',self # +end + +f.m +F.m +f.n +F.n +f.g + +# undefined method n (n is just on object f): F.new.n + +puts '- class self ----------------------------' + +class G + @a = 1 + @@b = 2 + + puts 'self: ', self + puts 'self.class: ', self.class + puts 'instance: ', instance_variables() + puts 'class: ', class_variables() + puts 'self.instance: ', self.instance_variables() + puts 'self.class: ', self.class.instance_variables() +end + +puts '- self in modules --------------------' + +module M + puts self # M + puts self.class # Module +end + +class C + a = include M # C + puts a + puts a.class +end + +puts '- end ----------------------------' + diff --git a/merlin/main/languages/ruby/Experimental/Misc/SplatArgs.rb b/merlin/main/languages/ruby/Experimental/Misc/SplatArgs.rb new file mode 100644 index 0000000000..3df0cf372d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/SplatArgs.rb @@ -0,0 +1,12 @@ +# eventhough it is possible to pass array thru splatting, +# MRI creates a new array + +def foo(*a) + puts a.object_id + puts a.inspect +end + +x = [1,2,3] +puts x.object_id +puts x.inspect +foo *x diff --git a/merlin/main/languages/ruby/Experimental/Misc/Strings.rb b/merlin/main/languages/ruby/Experimental/Misc/Strings.rb new file mode 100644 index 0000000000..318e4e7c6d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Strings.rb @@ -0,0 +1,49 @@ +b = true +a = 1 +$a = 10 + +def h(a) + "[" + a + "]" +end + +# precendence: static concat has higher precedence than a method call (RubyWay is wrong on pg 43) + +puts "[" "a" "b".center(6) + "]" # ' [ab ]' +puts "[" + "a" + "b".center(6) + "]" # '[a b ]' +puts "[" + "a" + ("b".center(6)) + "]" # '[a b ]' + +puts "}" # '}' +puts "#}" # '#}' +puts "#" # '#' +puts "##" # '##' +puts "a#{h" "}b" # 'a[ ]b' +puts "a#{h" 15 + 10 "}b" # 'a[ 15 + 10 ]b' +puts "\#{a}" # '#{a}' +puts "\#a" # '#a' +puts "#{a}" # '1' +puts "#a" # '#a' +puts "\#%\##" # '#%##' + +puts "#{}" # '' +puts "#{if b then 1 else 2 end}" # '1' +puts "#{"#{1 + 1}"}" # '2' +puts "#{x = 1}#{y = x + 2}" # '13' +puts "#{z = "#{x = 3}"}#{y = x + 2}" # '35' + +puts "x#$a x" # 'x10 x' +puts "x\#$a" # 'x#$a' +puts "x#$"a blah 346 ^&(*!@~)_+ "[0] # 'x' +puts "x#$"a blah 346 ^&(*!@~)_+ "[-11..-1] # '^&(*!@~)_+' + +#error puts "#{" + +b = "foo" + +puts %q{abc} # 'abc' +puts %q{a#{b}c} # 'a#{b}c' +puts %Q{abc} # 'abc' +puts %Q{a#{b}c} # 'afooc' +puts %{a#{b}c} # 'afooc' + + + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Variables.rb b/merlin/main/languages/ruby/Experimental/Misc/Variables.rb new file mode 100644 index 0000000000..485862062b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Variables.rb @@ -0,0 +1,14 @@ +a = "1" +b = "2" + +def a(x) "a" + x end +def b(x) "b" + x end + +puts a +puts a a +puts b +puts b b +puts a b b a b a b b b a b a +puts a,b +#error: puts a,b b + diff --git a/merlin/main/languages/ruby/Experimental/Misc/VariablesClass.rb b/merlin/main/languages/ruby/Experimental/Misc/VariablesClass.rb new file mode 100644 index 0000000000..890a55b28b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/VariablesClass.rb @@ -0,0 +1,24 @@ +class C + def run() + puts + (0..255).each do |x| + begin + z = eval("@#{x.chr} = 1") + puts "@#{x.chr}: OK"; + rescue SyntaxError: + end + end + + puts + (0..255).each do |x| + begin + z = eval("@@#{x.chr} = 1") + puts "@@#{x.chr}: OK"; + rescue SyntaxError: + end + end + end +end + +C.new.run() + diff --git a/merlin/main/languages/ruby/Experimental/Misc/VariablesGlobals.rb b/merlin/main/languages/ruby/Experimental/Misc/VariablesGlobals.rb new file mode 100644 index 0000000000..3538d516fd --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/VariablesGlobals.rb @@ -0,0 +1,85 @@ +puts "$#" + +puts +(0..255).each do |x| + begin + z = eval("$#{x.chr}") + puts "case '#{x.chr}': break;"; + rescue SyntaxError: + end +end + +puts "$-#" + +puts +(0..255).each do |x| + begin + z = eval("$-#{x.chr}") + puts "case '#{x.chr}': break;"; + rescue SyntaxError: + end +end + +puts "-------" + +g = [ +"-Z", +"FOO65456", +"_", +"0124364654654", +"_0", +"-0", +"0", +"00", +"100", +"0100", +"000Z", +"104A", +"104545_", +"104545FOO", +"_", +"<", +"&", +] + +puts "-------" + +for x in g + begin + cmd = "$x = $#{x}" + z = eval(cmd) + puts "#{cmd}: OK"; + rescue SyntaxError: + puts "#{cmd}: syntax error"; + rescue NameError: + puts "#{cmd}: name error"; + end +end + +puts "-------" + +for x in g + begin + cmd = "alias $x $#{x}" + z = eval(cmd) + puts "#{cmd}: OK"; + rescue SyntaxError: + puts "#{cmd}: syntax error"; + rescue NameError: + puts "#{cmd}: name error"; + end +end + +puts "-------" + +for x in g + begin + cmd = "alias $#{x} $x" + z = eval(cmd) + puts "#{cmd}: OK"; + rescue SyntaxError: + puts "#{cmd}: syntax error"; + rescue NameError: + puts "#{cmd}: name error"; + end +end diff --git a/merlin/main/languages/ruby/Experimental/Misc/VariablesLocalGlobals.rb b/merlin/main/languages/ruby/Experimental/Misc/VariablesLocalGlobals.rb new file mode 100644 index 0000000000..f4c6d534df --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/VariablesLocalGlobals.rb @@ -0,0 +1,131 @@ +=begin + + The following gobals have local scope: + $1 .. $9 NTH_REF not-aliasable + $& BACK_REF + $+ BACK_REF + $` BACK_REF + $' BACK_REF + $~ GVAR + $_ GVAR + + True globals: + $* + $? + $< + $> + $! + $@ + $; + $= + $. + $/ + $~ + $+ + $_ + $" + $& + $. + $, + $\ + $$ + $' + $` + $$ + $0 + $/ + +=end + +require 'English' + +puts "CHILD_STATUS = '#{$CHILD_STATUS}'" +puts "DEFAULT_INPUT = '#{$DEFAULT_INPUT}'" + + +puts "-------------------------------" + +def foo + "cat" =~ /(c)(a)(t)/ + puts "foo:1" + puts global_variables.join(' ') + puts "$1 = #{$1}" + puts + + bar() + puts "foo:2" + puts global_variables.join(' ') + puts "$1 = #{$1}" + puts + + baz() + puts "foo:3" + puts global_variables.join(' ') + puts "$1 = #{$1}" + puts +end + +def bar + "bo" =~ /(b)(o)/ + puts "bar:1" + puts global_variables.join(' ') + puts "$1 = #{$1}" + puts +end + +def baz + puts "baz:1" + puts global_variables.join(' ') + puts "$1 = #{$1}" + puts +end + +foo + +# $! variable is real global + +def exc + $! = NameError.new "bogus" + begin + begin + $" = 'foo' + rescue NameError + puts "Message 1: #{$!}" + display_message + $! = 'bar' + end + rescue TypeError + puts "Message 3: #{$!}" + end + + puts "Message 4: #{$!}" +end + +def display_message + puts "Message 2: #{$!}" +end + +exc + +puts "Message 5: #{$!}" + +# testing $_ - it's local scoped global yet can be aliased +# => RubyOps.GetGlobal needs to get the current environment + +def test_gets + gets + puts "Gets returned 1: '#{$_}'" + puts "Gets returned 1: '#{$FOO}'" + dump_gets +end + +def dump_gets + puts "Gets returned 2: '#{$_}'" +end + +$X = 'X' +alias $_ $X # alias hides the real global +alias $FOO $_ +alias $BAR $` + +test_gets diff --git a/merlin/main/languages/ruby/Experimental/Misc/Visibility.rb b/merlin/main/languages/ruby/Experimental/Misc/Visibility.rb new file mode 100644 index 0000000000..3f952ca860 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Visibility.rb @@ -0,0 +1,51 @@ +class C + def instance_test() + puts 'instance test:' + + pub # pub + pri # pri + pro # pro + + puts 'instance self test:' + + self.pub # pub + # self.pri # error: private called + self.pro # pro + end + + def C.class_test() + puts 'class test:' + + C.new.pub + # C.new.pri # error: private called + # C.new.pro # error: protected called + end + + public + + def pub() + puts 'pub' + end + + protected + + def pro() + puts 'pro' + end + + private + + def pri() + puts 'pri' + end + +end + +c = C.new + +c.pub # pub +# c.pri # error: private called +# c.pro # error: protected called + +c.instance_test +C.class_test \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/Visibility2.rb b/merlin/main/languages/ruby/Experimental/Misc/Visibility2.rb new file mode 100644 index 0000000000..a2c3ac2e6e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Visibility2.rb @@ -0,0 +1,61 @@ +puts "self = #{self}" +puts private.inspect + +class Module + def my_private *a + puts 'private' + private *a + end +end + +module M + def M.foo + puts "self = #{self}" + puts private.inspect + end + + foo + + def a1 + end + + my_private + + def a2 + end + + private + + def a3 + end + + public + + def a4 + end + + my_private :a4 + + # a3 only, + # scope dependent, LanguageContext needs to encode the flag + puts private_instance_methods.sort.inspect +end + +puts '---' + +module M2 + def a + end + + puts private_instance_methods.sort.inspect +end + +puts '---' + +class C2 + def a + end + + puts private_instance_methods.sort.inspect +end + diff --git a/merlin/main/languages/ruby/Experimental/Misc/Whitespace.rb b/merlin/main/languages/ruby/Experimental/Misc/Whitespace.rb new file mode 100644 index 0000000000..67d2686a3c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/Whitespace.rb @@ -0,0 +1,59 @@ +y = 1 +z = 2 +b = 8 +c = [1,[2,3]] + +def y(a) + a * 10 +end + +def z(a,b) + b +end + +def z?(a) + a == 10 +end + +def z!(a) + a == 20 +end + +# all these are the same (although Ruby Way claims they're different) +puts y + z # 3 +puts y+z # 3 +puts y+ z # 3 +puts y +z # 3 +puts y(+z) # 20 +puts y (+z) # 20 +puts y z # 20, warning: parenthesize argument(s) for future version +puts y -z # -1 +puts -y -z # -3 +puts +y +z # 3 +puts -y(z) # -20 +puts -y (z) # -20 +# error puts -y z +# error puts +y z + +puts z*b # 16 +puts z * b # 16 +puts z* b # 16 +puts z *b # 16 +puts z(*c) # 2\n3 + +puts z?(1) # false +puts z?(10) # true +puts z ? 1 : 2 # 1 +puts z? 10 ?1:2 # false +puts (z? 10) ?1:2 # 1 +puts (z! 20) ?1:2 # 1 +puts (!z? 10) ?1:2 # 2 +puts (!z! 20) ?1:2 # 2 +puts (!z! 20) ?!1:!2 # false + +#error puts z?(1) : 2 + + + + + diff --git a/merlin/main/languages/ruby/Experimental/Misc/YieldProc.rb b/merlin/main/languages/ruby/Experimental/Misc/YieldProc.rb new file mode 100644 index 0000000000..abcd5c8cee --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/YieldProc.rb @@ -0,0 +1,7 @@ +def foo &p + yield 1,2,3,&p +end + +foo { |a,b,c| + puts a,b,c +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/events.rb b/merlin/main/languages/ruby/Experimental/Misc/events.rb new file mode 100644 index 0000000000..e7ecf2d5c8 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/events.rb @@ -0,0 +1,74 @@ +class Form + def initialize + @click = Event.new + end + + def click + puts "TRACE: Click" + @click + end + + def click=(x) + puts "TRACE: Click=#{x.inspect}" + if x.instance_of? MethodHolder then + @click.add(x.method) + else + @click.set(x) + end + end +end + +class MethodHolder + attr :method + + def initialize(m) + @method = m + end +end + +class Event + def initialize + @methods = [] + end + + def +(m) + puts "TRACE: +(#{m.inspect}" + MethodHolder.new(m) + end + + def call + @methods.each { |m| m.call } + end + + def add(m) + @methods << m + end + + def <<(m) + @methods << m + end + + def set(m) + @methods = [m] + end + + def delete(m) + @methods.delete m + end + + alias :add,:push + alias :remove,:delete +end + +def bar + puts 'hello' +end + +def baz + puts 'world' +end + +form = Form.new +form.click += method(:bar) +form.click += method(:baz) +form.click.call diff --git a/merlin/main/languages/ruby/Experimental/Misc/fetch.rb b/merlin/main/languages/ruby/Experimental/Misc/fetch.rb new file mode 100644 index 0000000000..4e605d3243 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/fetch.rb @@ -0,0 +1,8 @@ + +x = [1].fetch(-5) { |i| + puts i + 'missed' + break +} + +puts x \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/initialize_copy.rb b/merlin/main/languages/ruby/Experimental/Misc/initialize_copy.rb new file mode 100644 index 0000000000..f0a6f4c0e4 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/initialize_copy.rb @@ -0,0 +1,27 @@ +class C + def initialize + @x = 1 + @y = 2 + puts "#{self.inspect}.initialize" + end + + def initialize_copy *c + puts "#{self.inspect}.initialize_copy(#{c.inspect})" + end + + def allocate + puts "#{self.inspect}.allocate" + end +end + +c = C.new +c.taint +c.freeze + +puts '-- dup:' +dup_c = c.dup +puts "frozen: #{dup_c.frozen?} tainted: #{dup_c.tainted?}" + +puts '-- clone:' +clone_c = c.clone +puts "frozen: #{clone_c.frozen?} tainted: #{clone_c.tainted?}" diff --git a/merlin/main/languages/ruby/Experimental/Misc/load.rb b/merlin/main/languages/ruby/Experimental/Misc/load.rb new file mode 100644 index 0000000000..323a849889 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/load.rb @@ -0,0 +1,3 @@ +load('load1.rb', true) +puts D +puts C #error \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/load1.rb b/merlin/main/languages/ruby/Experimental/Misc/load1.rb new file mode 100644 index 0000000000..2496bacf2d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/load1.rb @@ -0,0 +1,3 @@ +C = 'hello' + +load 'load2.rb' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/load2.rb b/merlin/main/languages/ruby/Experimental/Misc/load2.rb new file mode 100644 index 0000000000..e33ccce8ed --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/load2.rb @@ -0,0 +1 @@ +D = 'bye' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/requireFile.rb b/merlin/main/languages/ruby/Experimental/Misc/requireFile.rb new file mode 100644 index 0000000000..1bdbcde711 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/requireFile.rb @@ -0,0 +1,3 @@ +require 'require1.rb' + +puts C \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/requireFile1.rb.rb b/merlin/main/languages/ruby/Experimental/Misc/requireFile1.rb.rb new file mode 100644 index 0000000000..a2f77b9d8b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/requireFile1.rb.rb @@ -0,0 +1 @@ +C = 1 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Misc/test_sort.rb b/merlin/main/languages/ruby/Experimental/Misc/test_sort.rb new file mode 100644 index 0000000000..3223201cbe --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Misc/test_sort.rb @@ -0,0 +1,24 @@ +# See how Ruby calls the < and > operators on the result returned by <=> + +h = Array.new(100) {|i| i} + +class Foo + def initialize(a, b) + @a = a, b + @c = a <=> b + puts self.inspect + end + + def < (x) + puts "#{self.inspect} < #{x.inspect}" + @c < x + end + + def > (x) + puts "#{self.inspect} > #{x.inspect}" + @c > x + end +end + +h.sort! { |x,y| Foo.new(x,y) } +puts h.inspect diff --git a/merlin/main/languages/ruby/Experimental/Modules/Extend.rb b/merlin/main/languages/ruby/Experimental/Modules/Extend.rb new file mode 100644 index 0000000000..83650c4325 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Modules/Extend.rb @@ -0,0 +1,65 @@ +module M1; end +module M2; end +module M3; end +module N1; end +module N2; end + +class Module + p (private_instance_methods(false) & ["extended", "included", "append_features", "extend_object"]).sort + + alias old_e extended + alias old_eo extend_object + alias old_i included + alias old_af append_features + + def extended *a + puts "#{self}.extended: #{a.inspect} -> #{old_e(*a).inspect}" + end + + def extend_object *a + puts "#{self}.extend_object: #{a.inspect} -> #{$disable ? '': old_eo(*a).inspect}" + end + + def included *a + puts "#{self}.included: #{a.inspect} -> #{old_i(*a).inspect}" + end + + def append_features *a + puts "#{self}.append_features: #{a.inspect} -> #{old_af(*a).inspect}" + end +end + +puts '---' + +class C + include N1, N2 +end + +x = C.new + +class << x + def to_s + "obj_x" + end +end + +puts '---' + +$disable = false +p x.extend(M1, M2) + +puts '---' + +$disable = true +p x.extend(M3) + +puts '---' + +class C + puts "C.ancestors -> #{ancestors.inspect}" +end + +class << x + puts "S(x).ancestors -> #{ancestors.inspect}" +end + diff --git a/merlin/main/languages/ruby/Experimental/Modules/Struct.rb b/merlin/main/languages/ruby/Experimental/Modules/Struct.rb new file mode 100644 index 0000000000..6ab85185b6 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Modules/Struct.rb @@ -0,0 +1,36 @@ +p i = :foo.to_i +Struct.new(i) + +p Struct.new("S1", :foo,:foo) +p Struct::S1.instance_methods(false) + + +class C + def to_str # + "Bar" + end + + def to_sym + :Baz + end +end + +class D + def to_str # + "one" + end +end + +# TODO: +class MyStruct < Struct +end + + +p Struct.new(C.new, D.new, :two) { |*args| + p args + p self + break 'foo' +} + +p Struct.constants +p Struct::Bar.instance_methods(false) diff --git a/merlin/main/languages/ruby/Experimental/Modules/inherited_event.rb b/merlin/main/languages/ruby/Experimental/Modules/inherited_event.rb new file mode 100644 index 0000000000..91e21eedf0 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Modules/inherited_event.rb @@ -0,0 +1,115 @@ +class Class + def inherited s + p Object.constants.include?(s.name) + + if s.name == "YAML::Syck::Resolver" then + raise IOError + end + if $raise then + $sub = s + puts "raise #{$raise}" + raise $raise + end + + puts "#{s} < #{self}" + end +end + +class B + +end + +class A < B +end + +class << self + class << self + class << self + end + end +end + +puts '-'*25 +puts 'dup' +puts '-'*25 + +C = A.dup # no event + +puts '-'*25 +puts 'Struct' +puts '-'*25 + +S = Struct.new :foo, :bar do + puts self.new.foo + puts 'bar' +end + +puts '-'*25 + +$raise = 'xxx' +begin + S = Struct.new :foo, :bar do + puts self.new.foo + puts 'bar' + end +rescue + p $! +end +p $sub.public_instance_methods(false) +p $sub.private_instance_methods(false) +p $sub.protected_instance_methods(false) +p $sub.singleton_methods(false) +$raise = nil + +puts '-'*25 +puts 'Class.new' +puts '-'*25 + +X = Class.new B do + def bar + end + puts 'bar' +end + +puts '-'*25 + +$raise = 'hello' +begin + X = Class.new B do + def bar + end + puts 'bar' + end +rescue + p $! +end +p $sub.instance_methods(false) +$raise = nil + +puts '-'*25 +puts 'class E < B' +puts '-'*25 + +$raise = 'hello' +begin + class E < B + def bar + end + puts 'bar' + end +rescue + p $! +end +p $sub.instance_methods(false) +$raise = nil + +puts '-'*25 +puts 'yaml' +puts '-'*25 + +begin + require 'yaml' +rescue + p $! +end + diff --git a/merlin/main/languages/ruby/Experimental/Modules/initialize.rb b/merlin/main/languages/ruby/Experimental/Modules/initialize.rb new file mode 100644 index 0000000000..3dcb3445a5 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Modules/initialize.rb @@ -0,0 +1,50 @@ +class M < Module + def init + puts "initialize -> #{initialize.inspect}" + end +end + +Q = M.new + +module Q + def foo + puts 'foo' + end + + C = 1 +end + +p Q.constants +p Q.instance_methods(false) +Q.init +p Q.constants +p Q.instance_methods(false) + +puts '---' + +p Q + + +class C + include Q +end + +C.new.foo + +class << Q + p self +end + +puts '---' + +R = Q.dup + +module R + def bar + end +end + +p R.name +p R.constants +p R.instance_methods(false) +p Q.instance_methods(false) diff --git a/merlin/main/languages/ruby/Experimental/Modules/initialize_from.rb b/merlin/main/languages/ruby/Experimental/Modules/initialize_from.rb new file mode 100644 index 0000000000..8a25c4118e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Modules/initialize_from.rb @@ -0,0 +1,53 @@ +module N +end + +module M + include N + + def foo + end + + C = 1 + + @@x = 1 +end + +def dump m + puts "-- #{m} -----" + p m.ancestors + p m.included_modules + p m.instance_methods(false) + p m.constants() + p m.class_variables() +end + +dump M +dump M.dup + +dump Object.dup +=begin + +class B + include N +end + +class C < B + include M +end + +dump C +dump C.dup + +C.send(:initialize_copy, B) rescue p $! +C.send(:initialize) rescue p $! + +X = Class.new +X.send(:initialize_copy, B) rescue p $! + +Y = Class.new +Y.send(:initialize) rescue p $! + +p M.send(:initialize_copy, N) +p M.send(:initialize) +dump M +=end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Modules/initialize_with_block.rb b/merlin/main/languages/ruby/Experimental/Modules/initialize_with_block.rb new file mode 100644 index 0000000000..f5ac687c2a --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Modules/initialize_with_block.rb @@ -0,0 +1,25 @@ +class Module + public :initialize +end + +X = Module.new { puts 'xxx' } + +puts '-1-' +p(X.initialize) +puts '-2-' +p(X.initialize { puts 'foo' }) +puts '-3-' +p(X.initialize { break 'foo' }) +puts '-4-' +p(Module.new { break 'foo' }) + +class MM < Module + def initialize *a, &b + puts 'init' + end +end + +# doesn't call the block (=> block yield is in initialize, not in factory) +puts '-5-' +p(MM.new { puts 'yyy' }) +p(MM.new { break 'yyy' }) diff --git a/merlin/main/languages/ruby/Experimental/Numeric/KernelFloat.rb b/merlin/main/languages/ruby/Experimental/Numeric/KernelFloat.rb new file mode 100644 index 0000000000..aed23dc62a --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Numeric/KernelFloat.rb @@ -0,0 +1,32 @@ +class A + def respond_to? name + puts "? #{name}" + false + end +end + +p Float(A.new) rescue p $! + +puts '---' + +class B + def to_f + 'to_f' + end + + def respond_to? name + puts "? #{name}" + false + end + + def method_missing *args + p args + end +end + +p Float(B.new) rescue p $! + +p Float("1.3") +p Float(1) +p Float(1.4) +p Float(true) rescue p $! diff --git a/merlin/main/languages/ruby/Experimental/Numeric/KernelInteger.rb b/merlin/main/languages/ruby/Experimental/Numeric/KernelInteger.rb new file mode 100644 index 0000000000..90f440f6ee --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Numeric/KernelInteger.rb @@ -0,0 +1,36 @@ +class A + def respond_to? name + puts "? #{name}" + false + end +end + +p Integer(A.new) rescue p $! + +puts '---' + +class B + def to_f + 'to_f' + end + + def respond_to? name + puts "? #{name}" + false + end + + def method_missing *args + p args + end +end + +p Integer(B.new) rescue p $! + +p Integer("1.3") rescue p $! +p Integer("0o111") rescue p $! +p Integer("0b111") rescue p $! +p Integer("0x111") rescue p $! +p Integer("0d111") rescue p $! +p Integer(1) +p Integer(1.4) +p Integer(true) rescue p $! diff --git a/merlin/main/languages/ruby/Experimental/Numeric/StringToF.rb b/merlin/main/languages/ruby/Experimental/Numeric/StringToF.rb new file mode 100644 index 0000000000..9801cc92db --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Numeric/StringToF.rb @@ -0,0 +1,42 @@ +p "1.".to_f +p ".e1".to_f +p "e+10".to_f +p "1e-_1".to_f +p "1e".to_f +p "1e-".to_f +p "1e-____".to_f + +p "-.1".to_f +p "-.1".to_f + +p "e12".to_f +p "+e12".to_f +p ".".to_f +p " + 12312.4234".to_f +p "+__2".to_f +p "-__2".to_f +p "INF".to_f + +puts '--' + +p "-".to_f +p "+".to_f + +p "+.1".to_f +p ".1".to_f +p "1e12".to_f +p "12312.4234".to_f +p " +12312.4234".to_f +p "_____312".to_f +p "\r\n\t +12_____312.4___234e___21$%$".to_f + +puts '--' + +p "1".to_f +p "1$".to_f +p "1.$".to_f +p "1.0$".to_f +p "1.01$".to_f +p "1.01e$".to_f +p "1.01e+$".to_f +p "1.01e+1$".to_f diff --git a/merlin/main/languages/ruby/Experimental/Operators/Boolean.rb b/merlin/main/languages/ruby/Experimental/Operators/Boolean.rb new file mode 100644 index 0000000000..806d13ae81 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Operators/Boolean.rb @@ -0,0 +1,58 @@ +class C + def method_missing(*args) + p args + end +end + +t = C.new +p (t && 4) + +t = true +p (t && 4) + +p '---' + +t = C.new +p (t &= 4) + +t = true +p (t &= 4) + +p '---' + +t = C.new +p (t &&= 4) # doesn't call method '&&', does t = t && 4 + +t = true +p (t &&= 4) # doesn't call method '&&', does t = t && 4 + +p '---' + +t = true +f = false +m = 0 +n = nil + +t &&= false +f ||= true +m &&= 1 +n ||= 2 + +p t,f,m,n + +p '---' + +class C + def [](*args) + puts "#{args.inspect}" + 1 + end +end + +c = C.new +c[0] &&= 1 + + + + + diff --git a/merlin/main/languages/ruby/Experimental/Procs/Calls.rb b/merlin/main/languages/ruby/Experimental/Procs/Calls.rb new file mode 100644 index 0000000000..70f9060498 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Procs/Calls.rb @@ -0,0 +1,61 @@ +lambda { |x,y| p [x, y] }.call rescue puts 'error' +lambda { |x,y| p [x, y] }.call 1 rescue puts 'error' +lambda { |x,y| p [x, y] }.call 1,2 +lambda { |x,y| p [x, y] }.call [] rescue puts 'error' +lambda { |x,y| p [x, y] }.call [1] rescue puts 'error' +lambda { |x,y| p [x, y] }.call [1,2] rescue puts 'error' +lambda { |x,y| p [x, y] }.call *[1,2] +lambda { |x,y| p [x, y] }.call *[[1]] rescue puts 'error' +lambda { |x,y| p [x, y] }.call *[[1,2]] rescue puts 'error' +lambda { |x,y| p [x, y] }.call *[[1,2,3]] rescue puts 'error' + +puts '---' + +Proc.new { |x,y| p [x, y] }.call rescue puts 'error' +Proc.new { |x,y| p [x, y] }.call 1 rescue puts 'error' +Proc.new { |x,y| p [x, y] }.call 1,2 +Proc.new { |x,y| p [x, y] }.call [] rescue puts 'error' +Proc.new { |x,y| p [x, y] }.call [1] rescue puts 'error' +Proc.new { |x,y| p [x, y] }.call [1,2] rescue puts 'error' +Proc.new { |x,y| p [x, y] }.call *[1,2] +Proc.new { |x,y| p [x, y] }.call *[[1]] rescue puts 'error' +Proc.new { |x,y| p [x, y] }.call *[[1,2]] rescue puts 'error' +Proc.new { |x,y| p [x, y] }.call *[[1,2,3]] rescue puts 'error' + +puts '---' + +def a; yield; end +def b; yield 1; end +def c; yield 1,2; end +def d; yield []; end +def e; yield [1]; end +def f; yield [1,2]; end + +a { |x,y| p [x, y] } +b { |x,y| p [x, y] } +c { |x,y| p [x, y] } +d { |x,y| p [x, y] } +e { |x,y| p [x, y] } +f { |x,y| p [x, y] } + +puts '---' + +Proc.new { |x| puts x.inspect }.call +Proc.new { |x| puts x.inspect }.call 1 +Proc.new { |x| puts x.inspect }.call 1,2 +Proc.new { |x| puts x.inspect }.call [] +Proc.new { |x| puts x.inspect }.call [1] +Proc.new { |x| puts x.inspect }.call [1,2] +Proc.new { |x| puts x.inspect }.call *[1] +Proc.new { |(x,)| puts x.inspect }.call *[] + +puts '---' + +lambda { |x| puts x.inspect }.call +lambda { |x| puts x.inspect }.call 1 +lambda { |x| puts x.inspect }.call 1,2 +lambda { |x| puts x.inspect }.call [] +lambda { |x| puts x.inspect }.call [1] +lambda { |x| puts x.inspect }.call [1,2] +lambda { |x| puts x.inspect }.call *[1] +lambda { |(x,)| puts x.inspect }.call *[] rescue puts 'error' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Procs/ProcNew.rb b/merlin/main/languages/ruby/Experimental/Procs/ProcNew.rb new file mode 100644 index 0000000000..35cf46b5d9 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Procs/ProcNew.rb @@ -0,0 +1,50 @@ +class Class + alias old_new new + + def new *args, &x + puts "Class#new #{args[0]}, #{args[1]}, #{x}" + old_new(*args, &x) + end +end + +class P < Proc + def initialize *args, &x + puts "P#initialize #{args[0]}, #{args[1]}, #{x}" + p x.object_id == $y.object_id + p x + super + end +end + +$q = 1 +class Q < Proc + def initialize *args, &x + puts "Q#initialize #{args[0]}, #{args[1]}, #{x.object_id} #{x.class} #{x[]}" + $q = $q + 1 + retry if $q < 5 + end +end + +puts '-- lambda' +$y = lambda { 'foo' } + +puts '-- P.new ----------------' +x = P.new(&$y) +p x.class +p x.object_id == $y.object_id + +puts '-- Q.new ----------------' +z = Q.new(&x) +p z.class +p z.object_id + +puts '-- Proc.new ----------------' +x = Proc.new(&$y) +p x.class +p x.object_id == $y.object_id + +puts '-- P.sm:' +p P.singleton_methods(false) + +puts '-- Proc.sm:' +p Proc.singleton_methods(false) \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Procs/ProcToLambda.rb b/merlin/main/languages/ruby/Experimental/Procs/ProcToLambda.rb new file mode 100644 index 0000000000..fdf15a3ac4 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Procs/ProcToLambda.rb @@ -0,0 +1,9 @@ +class P < Proc +end + +q = P.new { puts 'foo' } +l1 = lambda &q +l2 = proc &q + +p q.class, l1.class, l2.class + diff --git a/merlin/main/languages/ruby/Experimental/Procs/call_arity.rb b/merlin/main/languages/ruby/Experimental/Procs/call_arity.rb new file mode 100644 index 0000000000..fe99a8d816 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Procs/call_arity.rb @@ -0,0 +1,31 @@ +def test f + puts "#{'-'*20} (#{f.arity}) #{'-'*20}" + p begin f.call();0 end rescue p $! + p begin f.call(1);1 end rescue p $! + p begin f.call(1,2);2 end rescue p $! + p begin f.call(1,2,3);3 end rescue p $! + p begin f.call(1,2,3,4);4 end rescue p $! +end + +test proc { || 1 } # n == 0 +test proc { |*| 1 } # n >= 0 +puts 'A' * 100 +test proc { |x| 1 } # warning if n != 1 +test proc { |x,| 1 } # n == 1 +test proc { |x,*| 1 } # n >= 1 +puts 'B' * 100 +test proc { |(*)| 1 } # n >= 0 +test proc { |(x,)| 1 } # n == 1 +test proc { |(x,*)| 1 } # n >= 1 +test proc { |(x,y)| 1 } # n == 2 +test proc { |(x,y,)| 1 } # n == 2 +test proc { |(x,y,z)| 1 } # n == 3 +puts 'C' * 100 +test proc { |x,y| 1 } # n == 2 +test proc { |x,y,| 1 } # n == 2 +test proc { |x,y,*| 1 } # n >= 2 +puts 'D' * 100 +test proc { |x,y,z| 1 } # n == 3 +test proc { |x,y,z,| 1 } # n == 3 +test proc { |x,y,z,*| 1 } # n >= 3 + diff --git a/merlin/main/languages/ruby/Experimental/Procs/to_proc.rb b/merlin/main/languages/ruby/Experimental/Procs/to_proc.rb new file mode 100644 index 0000000000..c6dd807a9c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Procs/to_proc.rb @@ -0,0 +1,44 @@ +class C + def to_proc + lambda { |x| puts x } + end + + def respond_to? name + puts 'respond?' + true + end +end + +class Proc + def to_proc + puts 'to_proc' + end +end + +#class MyProc < Proc +# def to_proc +# puts 'to_my_proc' +# end +#end + +class D + def to_proc + 1 + end +end + +class X +end + +pr = Proc.new { } + +1.times &C.new +1.times &pr +1.times &lambda {} +1.times { } +1.times &D.new rescue p $! +#1.times &MyProc.new {} +1.times &X.new rescue p $! + + + diff --git a/merlin/main/languages/ruby/Experimental/Procs/yield_call_test.rb b/merlin/main/languages/ruby/Experimental/Procs/yield_call_test.rb new file mode 100644 index 0000000000..b05c7cda6e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Procs/yield_call_test.rb @@ -0,0 +1,19 @@ +def def_lambda + $lambda = lambda { |*| return 'ok' } +end +def_lambda + + +def test_yield + yield +end + +def test_call &p + p[] +end + +puts test_yield(&$lambda) rescue puts "yield: #$!" +puts test_call(&$lambda) + + + diff --git a/merlin/main/languages/ruby/Experimental/Range/flip.rb b/merlin/main/languages/ruby/Experimental/Range/flip.rb new file mode 100644 index 0000000000..f1c4e953fa --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Range/flip.rb @@ -0,0 +1,36 @@ +F = false +T = true +x = X = '!' + +B = [F,F,T,x,T,x,T,x,x,F,F,T,x] +E = [x,x,x,T,x,T,x,F,T,x,x,x,T] + +def b + r = B[$j] + puts "#{$j}: b -> #{r.inspect}" + + $j += 1 + + $continue = !r.nil? + r == X ? raise : r +end + +def e + r = E[$j] + puts "#{$j}: e -> #{r.inspect}" + + $j += 1 + + $continue = !r.nil? + r == X ? raise : r +end + +$j = 0 +$continue = true +while $continue + if b..e + puts "#{$j}: TRUE" + else + puts "#{$j}: FALSE" + end +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Range/flip_eval.rb b/merlin/main/languages/ruby/Experimental/Range/flip_eval.rb new file mode 100644 index 0000000000..3a27b8d901 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Range/flip_eval.rb @@ -0,0 +1,73 @@ +$j = 0 + +T = true +F = false +x = nil + +Foo = [T,x,F,F,F] +Bar = [x,F,F,F,F] + +def foo + r = Foo[$j] + puts "#{$j}: foo #{$j} -> #{r.inspect}" + $j += 1 + r +end + +def bar + r = Bar[$j] + puts "#{$j}: bar #{$j} -> #{r.inspect}" + $j += 1 + r +end + +def test1 + $j = 0 + + puts foo..bar ? 'true' : 'false' + puts foo..bar ? 'true' : 'false' + puts foo..bar ? 'true' : 'false' + puts foo..bar ? 'true' : 'false' +end + +def y; yield; end + +def test2 + $j = 0 + + $p = lambda { + puts foo..bar ? 'true' : 'false' + } + + y &$p + y &$p + y &$p + y &$p +end + +def t(b) + eval(" + puts x + puts foo..bar ? 'true' : 'false' + ", b) +end + +def test3 + $j = 0 + x = 1 + b = binding + t b + x += 1 + t b + x += 1 + t b + x += 1 + t b +end + +puts '--- test1 ---' +test1 +puts '--- test2 ---' +test2 +puts '--- test3 ---' +test3 diff --git a/merlin/main/languages/ruby/Experimental/Regex/EmptyMatchData.rb b/merlin/main/languages/ruby/Experimental/Regex/EmptyMatchData.rb new file mode 100644 index 0000000000..2f73649169 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Regex/EmptyMatchData.rb @@ -0,0 +1,20 @@ +class Class + alias x_new new +end + +def MatchData.new *a + x_new *a +end + +m = MatchData.new + +p m.string + +p m.begin(0) rescue p $! +p m.end(0) rescue p $! +p m.length +p m.captures + +# this crashes MRI: +#p m.pre_match +#p m.post_match diff --git a/merlin/main/languages/ruby/Experimental/Regex/MatchData.rb b/merlin/main/languages/ruby/Experimental/Regex/MatchData.rb new file mode 100644 index 0000000000..7219862ea9 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Regex/MatchData.rb @@ -0,0 +1,13 @@ +MatchData.new rescue p $! + +class M < MatchData; end + +class MatchData + p ancestors # [MatchData, Object, Kernel] + p instance_methods(false) + p private_instance_methods(false) + p protected_instance_methods(false) + p singleton_methods(false) +end + + diff --git a/merlin/main/languages/ruby/Experimental/Regex/MatchScoping.rb b/merlin/main/languages/ruby/Experimental/Regex/MatchScoping.rb new file mode 100644 index 0000000000..003b608e16 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Regex/MatchScoping.rb @@ -0,0 +1,22 @@ +def x + match = /(1)/.match '1' + + eval <<-END + class C + p $~ + + match = /(2)/.match '2' + + module_eval do + define_method :foo do + 1.times { p $~.captures } + 1.times { match = /(3)/.match('3') } + 1.times { p $~.captures } + end + end + end + END +end + +x +C.new.foo diff --git a/merlin/main/languages/ruby/Experimental/Regex/Options.rb b/merlin/main/languages/ruby/Experimental/Regex/Options.rb new file mode 100644 index 0000000000..625d993a51 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Regex/Options.rb @@ -0,0 +1,44 @@ +def try_eval str + puts '-'*20 + puts str + x = eval str + p x + p x.options +rescue SyntaxError + puts '-'*20 + p $! +end + +try_eval '/foo/9' +try_eval '/foo/wiww09' +try_eval '/foo/esnui' +try_eval '/foo/esuiiimmmxxxooo' + +puts '-- all possible options --' + +def try_option x + eval "/foo/" + x +rescue SyntaxError +else + print x +end + +('a'..'z').each { |x| try_option x } +('A'..'Z').each { |x| try_option x } + +puts +puts '-- to_s --' + +r = /foo/eimnosux +p r.to_s +p r.inspect +p r.to_str rescue puts $! + +r = /foo/imx +p r.to_s +p r.inspect + + +p s = /xx#{r}xx#{r}xx/i.to_s +p t = /yy#{s}yy/.to_s + diff --git a/merlin/main/languages/ruby/Experimental/Regex/Slice.rb b/merlin/main/languages/ruby/Experimental/Regex/Slice.rb new file mode 100644 index 0000000000..90d50fe88b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Regex/Slice.rb @@ -0,0 +1,15 @@ +class R < Range + def exclude_end? + p "foooo" + true + end +end + +/(a)(b)(c)(d)(e)?(f)?(g)?(h)?/ =~ "abcd" +p $1, $2, $3, $4, $5, $6, $7, $8, $9, $10 +p $+ +p $~[R.new(1,-1)] + + +x = [1,2,3,4,5,6] +p x[R.new(1,-1)] \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Regex/ctors.rb b/merlin/main/languages/ruby/Experimental/Regex/ctors.rb new file mode 100644 index 0000000000..0a668aa0c2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Regex/ctors.rb @@ -0,0 +1,53 @@ +class Regexp + public :initialize +end + +puts '-- new --' + '-' * 25 + +p Regexp.new() rescue p $! +p Regexp.new(/foo/) +p Regexp.new(/foo/, false) rescue p $! +p Regexp.new(/foo/, false, "") rescue p $! +p Regexp.new("asd", "i", "nu") +p Regexp.new("asd", "mix", "nu") +p Regexp.new("asd", false, "mixnu") +p Regexp.new("asd", false, "umixnu") +p Regexp.new("asdsad", nil, nil, nil) rescue p $! + +puts '-- init --' + '-' * 25 + +p (x = Regexp.new("foo")).initialize() rescue p $! +p x +p (x = Regexp.new("foo")).initialize(/xxx/) +p x +p (x = Regexp.new("foo")).initialize(/xxx/, false) rescue p $! +p x +p (x = Regexp.new("foo")).initialize(/xxx/, false, "") rescue p $! +p x +p (x = Regexp.new("foo")).initialize("xxx", "i", "nu") +p x +p (x = Regexp.new("foo")).initialize("xxx", "mix", "nu") +p x +p (x = Regexp.new("foo")).initialize("xxx", false, "mixnu") +p x +p (x = Regexp.new("foo")).initialize("xxx", false, "umixnu") +p x +p (x = Regexp.new("foo")).initialize("xxx", nil, nil, nil) rescue p $! +p x + +puts '-- literal --' + '-' * 25 + +p /bar/.initialize("foo") rescue p $! + +puts '-- compile --' + '-' * 25 + +p Regexp.compile("asdsad") +p Regexp.compile(/asdsad/, false) rescue p $! +p Regexp.compile("asdsad", true) +p Regexp.compile("asdsad", "m") +p Regexp.compile("asdsad", "m", "n") +p Regexp.compile("asdsad", "m", "e") +p Regexp.compile("asdsad", nil, "e") +p Regexp.compile("asdsad", nil, nil) +p Regexp.compile("asdsad", nil, nil, nil) rescue p $! + diff --git a/merlin/main/languages/ruby/Experimental/Regex/escape.rb b/merlin/main/languages/ruby/Experimental/Regex/escape.rb new file mode 100644 index 0000000000..f022d9a337 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Regex/escape.rb @@ -0,0 +1,8 @@ +(0..255).each { |o| + c = o.chr + + if Regexp.escape(c).length > 1 then + p c + end +} + diff --git a/merlin/main/languages/ruby/Experimental/Regex/groups.rb b/merlin/main/languages/ruby/Experimental/Regex/groups.rb new file mode 100644 index 0000000000..633c7d0c4f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Regex/groups.rb @@ -0,0 +1,10 @@ +/(a)(b)(c)?(d)?(e)?/ =~ "ab" +p $1 +p $2 +p $3 +p $4 +p $5 +p $6 +p $121 +p $+ + diff --git a/merlin/main/languages/ruby/Experimental/Regex/gsub.rb b/merlin/main/languages/ruby/Experimental/Regex/gsub.rb new file mode 100644 index 0000000000..7cbf81974f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Regex/gsub.rb @@ -0,0 +1,28 @@ +x = "foo" +p x.gsub(/(o)/, '1') +p $1 + +r = x.gsub!(/a/) { break '2' } +p r.object_id == x.object_id +r = x.gsub(/a/) { break '2' } +p r.object_id == x.object_id +p r + +p $1 + +x = "foo" +x.freeze + +x.gsub!(/a/) { '2' } +x.gsub!(/a/, '2') rescue p ($!).class + + +puts '---' + +begin + x.gsub!(/o/) { '2' } +rescue + p $!.class +end + +x.gsub!(/o/, '2') rescue p ($!).class diff --git a/merlin/main/languages/ruby/Experimental/Regex/gsub_with_block.rb b/merlin/main/languages/ruby/Experimental/Regex/gsub_with_block.rb new file mode 100644 index 0000000000..bcfbdb8c42 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Regex/gsub_with_block.rb @@ -0,0 +1,90 @@ +def test1 + p self + + /(b)/ =~ "b" + puts "group: #$1" + + str = 'abc' + result = str.gsub!(/(.)/) { |*args| + puts '-- in block --' + puts "args: #{args.inspect}" + puts "self: #{self.inspect}" + puts "group: #{$1.inspect}" + + 'Z'.gsub!(/(.)/) { + puts "inner group: #{$1.inspect}" + } + + puts "group: #{$1.inspect}" + + #break 'goo' + } + + puts '--------------' + + puts "result: #{result.inspect}" + puts "str: #{str.inspect}" + puts "group: #{$1.inspect}" + +end + +def owner + /(x)/ =~ "x" + + $p = Proc.new { + p $1 + } + + $q = Proc.new { + p $1 + } + +end + +# $~ is set in the current scope, not in the block's scope +def test2 + owner + 'y'.gsub!(/(.)/, &$p) + $q.call + p $1 +end + +def test3 + owner + p 'y'.gsub!(/(.)/) { break 'foo' } + p $1 + z = 'z' + + # doesn't check frozen strings before return: + + z.freeze + p z.gsub!(/(.)/) { puts 'goo'; break 'foo' } + p $1 + + # saves $~ on unsuccessful match: + "x".gsub!(/(y)/) { puts 'unreachable'; } + p $~ +end + +def test4 + # returns nil on no match: + p "x".gsub!(/(x)/) { 'x' } + p "y".gsub!(/(x)/) { 'x' } +end + +puts '-1-' +test1 +puts '-2-' +test2 +puts '-3-' +test3 +puts '-4-' +test4 +puts '---' + +def def_lambda + $lambda = lambda { |*| return 'ok' } +end +def_lambda + +"a".gsub!(/(a)/, &$lambda) rescue puts $! diff --git a/merlin/main/languages/ruby/Experimental/Regex/to_s.rb b/merlin/main/languages/ruby/Experimental/Regex/to_s.rb new file mode 100644 index 0000000000..bb695ba1a6 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Regex/to_s.rb @@ -0,0 +1,73 @@ +x = /\A\/(?i-:(say|rails_info|rails\/\/info))(?:\/?\Z|\/([^\/.?]+)(?:\/?\Z|\/([^\/.?]+)\/?))\Z/ + + +puts "x.inspect" +puts x.inspect + +puts "x.to_s" +puts x.to_s + +puts "escape(x.to_s)" +puts Regexp.escape(x.to_s) + +puts "escape(x.inspect)" +puts Regexp.escape(x.inspect) + +puts "new(x)" +puts Regexp.new(x) + +puts "new(x.to_s)" +puts Regexp.new(x.to_s) + +puts "new(x.inspect)" +puts Regexp.new(x.inspect) + +class Regexp + alias :ts :to_s + alias :i :initialize + + def to_s + x = ts + puts "to_s #{ts}" + x + end + + def initialize *s + puts "init: #{s.inspect}" + i *s + end +end + +s = "~!@$# %^&*()_+1-2=[x]{1};':\|\\ \\a\\b\\c\",./<>?" + +puts "- 1 ------" + +z = Regexp.new(s) + +puts "- 2 ------" + +puts z + +puts "- 3 ------" + +puts Regexp.escape(s) + +puts "- 4 ------" + +class R < Regexp +end + +u = R.union("/", "*", "/", /./, /\//) + +puts "- 5 ------" +puts u +puts u.class + +puts "- 6 ------" +u = Regexp.new('/\/[/]') +puts u.to_s + +puts "- 7 ------" +u = Regexp.new('/\/[/]') +puts u.inspect + diff --git a/merlin/main/languages/ruby/Experimental/Regex/union.rb b/merlin/main/languages/ruby/Experimental/Regex/union.rb new file mode 100644 index 0000000000..f94d951297 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Regex/union.rb @@ -0,0 +1,8 @@ +p Regexp.union(/a/mix, /b/i, /c/) +p Regexp.union(/a/mix, /b/i, "asdasd*") +p Regexp.union() +p Regexp.union(/a/mix) +p Regexp.union("a") +p Regexp.union("", /a/mix) +p Regexp.union(//, /a/mix) +p Regexp.union(nil) rescue p $! \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Scope/ClassVars.rb b/merlin/main/languages/ruby/Experimental/Scope/ClassVars.rb new file mode 100644 index 0000000000..051e881fb9 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/ClassVars.rb @@ -0,0 +1,40 @@ +module M + def foo + @@m = 5 + end +end + +class X + include M + + def goo + puts @@m + end +end + +x = X.new +x.foo +x.goo + +class A + @@a = 1 + class B + @@b = 2 + class C + @@c = 3 + module D + @@d = 4 + puts @@a rescue puts "!a" + puts @@b rescue puts "!b" + puts @@c rescue puts "!c" + puts @@d rescue puts "!d" + end + end + end +end + +puts M.class_variables +puts A.class_variables +puts A::B.class_variables +puts A::B::C.class_variables +puts A::B::C::D.class_variables diff --git a/merlin/main/languages/ruby/Experimental/Scope/Constants_Global.rb b/merlin/main/languages/ruby/Experimental/Scope/Constants_Global.rb new file mode 100644 index 0000000000..86aed7d5b2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Constants_Global.rb @@ -0,0 +1,22 @@ +C = 'ON OBJECT' +$S = self + +class << self + $S = self +end + +class P + p Module.nesting +end + +def define_c + p self.object_id + p $S.object_id + p $S1.object_id + p ::C rescue puts $! + eval('::C = "C2"') rescue puts $! +end + +$tlb = TOPLEVEL_BINDING + +load 'Constants_Global_1.rb', true diff --git a/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_1.rb b/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_1.rb new file mode 100644 index 0000000000..d1ac8055e8 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_1.rb @@ -0,0 +1,48 @@ +puts 'self.ancestors:' +class << self + p ancestors + $M = ancestors[0] +end + +$S1 = self + +puts '0:' +p ::C rescue puts $! + +puts '1:' +(::C = 'D') rescue puts $! +puts "Object includes constant C?: " + Object.constants.include?("C").to_s +p $M.constants + +puts '2a:' +p C rescue puts $! +p ::C rescue puts $! +eval('::C = "C1"') rescue puts $! + +puts '2b:' +p ::C rescue puts $! +(::C = "C2") rescue puts $! + +puts '3:' +define_c + +puts '4:' +p ::C rescue puts $! + +puts '5:' +p $tlb.object_id == TOPLEVEL_BINDING.object_id + +puts '6:' + +def foo + puts 'foo' +end + +foo + +puts "Object includes method private foo?: " + Object.private_instance_methods(false).include?("foo").to_s +puts "Object includes method public foo?: " + Object.instance_methods(false).include?("foo").to_s + +p $M.instance_methods(false) +p $M.private_instance_methods(false) +p $M.protected_instance_methods(false) diff --git a/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_WriteBug1.rb b/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_WriteBug1.rb new file mode 100644 index 0000000000..de17f83f46 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_WriteBug1.rb @@ -0,0 +1,2 @@ +C = 'on Object' +load 'Constants_Global_WriteBug1_1.rb', true \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_WriteBug1_1.rb b/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_WriteBug1_1.rb new file mode 100644 index 0000000000..66dd07d616 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_WriteBug1_1.rb @@ -0,0 +1,6 @@ +::C = 'on anonymous module' # BUG? goes to the anonymous module +puts Module.nesting[0].constants # C + +puts C # OK +puts ::C # BUG? goes to Object + diff --git a/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_WriteBug2.rb b/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_WriteBug2.rb new file mode 100644 index 0000000000..7018e49e30 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_WriteBug2.rb @@ -0,0 +1,8 @@ +C = 'on Object' + +def foo + puts C, ::C # goes to Object + eval('::C = "in eval"') # BUG? defines C in the anonymous module +end + +load 'Constants_Global_WriteBug2_2.rb', true \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_WriteBug2_2.rb b/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_WriteBug2_2.rb new file mode 100644 index 0000000000..4b281e19b5 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Constants_Global_WriteBug2_2.rb @@ -0,0 +1,3 @@ +foo + +p Module.nesting[0].constants # C is here there diff --git a/merlin/main/languages/ruby/Experimental/Scope/Constants_Unqualified.rb b/merlin/main/languages/ruby/Experimental/Scope/Constants_Unqualified.rb new file mode 100644 index 0000000000..51aabbdfef --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Constants_Unqualified.rb @@ -0,0 +1,85 @@ +class B + Q = 'Q in B' + T = 'T in B' + W = 'W in B' +end + +class D + D = 'U in D' +end + +module M + P = 'P in M' + R = 'R in M' + V = 'V in M' + + def puts_U + puts U rescue puts $! + puts W rescue puts $! + end + + def self.puts_U + puts U rescue puts $! + puts W rescue puts $! + end +end + +class C < D + S = 'S in C' + Q = 'Q in C' + P = 'P in C' + class C < B + include M + S = 'S in C::C' + + # constants in the current scope chain: + puts C, P, Q, R, S, T + + # constants in base class/mixin of the current scope, + # but not in the current scope chain + puts U rescue puts $! + puts V, W + M.puts_U + + # it doesn't matter whether the constants are accessed + # from instance/class method or the class body -- + # the lookup is driven by the inner most module lexical scope + def ins + puts '-- ins --' + puts U rescue puts $! + puts V, W + puts_U + end + + def self.cls + puts '-- cls --' + puts U rescue puts $! + puts V, W + M.puts_U + end + end +end + +C::C.new.ins +C::C.cls + +module N + Z = 'Z in N' +end + +module O + include N,M + + puts '-- module --' + puts P, Z +end + +class << Object.new + P = 'P in singleton' + class << Object.new + include M + + puts '-- singleton --' + puts P + end +end diff --git a/merlin/main/languages/ruby/Experimental/Scope/DefineMethod.rb b/merlin/main/languages/ruby/Experimental/Scope/DefineMethod.rb new file mode 100644 index 0000000000..3ddfeb1c8c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/DefineMethod.rb @@ -0,0 +1,26 @@ +=begin + +define_method doesn't change constant lookup via lexical scoping + +=end + +S = 'S on Object' + +class D + $p = lambda { + puts S + } +end + +class C + S = 'S on C' + + define_method :f, &$p + + def g + yield + end +end + +C.new.f +C.new.g &$p \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Scope/Load.rb b/merlin/main/languages/ruby/Experimental/Scope/Load.rb new file mode 100644 index 0000000000..899db3c863 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Load.rb @@ -0,0 +1,46 @@ +class << self + $SM = self +end + +$sm = self + +#C = 'TOP' + +def foom + print 'read(C) in instance foom: ' + (p C) rescue puts $! + + print 'read(::C) in instance foom: ' + (p ::C) rescue puts $! +end + +module Kernel + def dump m + p m + p m.constants + p m.private_instance_methods(false) + end +end + +load 'Load_2.rb', true + +puts '----' +dump $G2 + +puts '----' + +p $SM.object_id == $S2.object_id +p $sm.object_id == $s2.object_id + +p $sm +p $s2 + +puts '---' + +dump $SM + +puts '---' + +dump $S2 + + diff --git a/merlin/main/languages/ruby/Experimental/Scope/Load_2.rb b/merlin/main/languages/ruby/Experimental/Scope/Load_2.rb new file mode 100644 index 0000000000..2c7e448ea2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Load_2.rb @@ -0,0 +1,37 @@ +puts +puts '----' + +$G2 = Module.nesting[0] + +class << self + $S2 = self +end + +$s2 = self + +C = 'G2' + +def foo2(fcaller) + print 'read(C) in instance foo2: ' + (p C) rescue puts $! + + print 'read(::C) in instance foo2: ' + (p ::C) rescue puts $! +end + + +foo2 "top-level G2" + +puts '----' + +print 'read(C) in G2 top-level: ' +(p C) rescue puts $! + +print 'read(::C) in G2 top-level: ' +(p ::C) rescue puts $! + +puts '----' + +foom + + diff --git a/merlin/main/languages/ruby/Experimental/Scope/LocalsAndClosures.rb b/merlin/main/languages/ruby/Experimental/Scope/LocalsAndClosures.rb new file mode 100644 index 0000000000..817752569a --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/LocalsAndClosures.rb @@ -0,0 +1,74 @@ +def visibility + x = 1 + 1.times { |i| + z = x + i + 1.times { |j| + w = z + j + } + puts w rescue puts $! + } + puts z rescue puts $! +end + +def init + 5.times { |i| + if i == 0 + x = 1 + else + x = (x.nil? ? 0 : x) + 2 + end + puts x + } +end + +def closure + p = [] + 2.times { |i| + p << lambda { + puts i + } + } + + p[0][] + p[1][] +end + +def closure_binding + p = [] + 2.times { |i| + p << binding + } + + eval('puts i', p[0]) + eval('puts i', p[1]) +end + +def module_scope + eval(' + $p = [] + $i = 0 + while $i < 2 + module M + x = $i + $p << binding + end + $i += 1 + end + ') + + eval('puts x', $p[0]) + eval('puts x', $p[1]) +end + +visibility +puts '---' +init +puts '---' +closure +puts '---' +closure_binding +puts '---' +module_scope +puts '---' + +puts 'done' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Scope/Methods.rb b/merlin/main/languages/ruby/Experimental/Scope/Methods.rb new file mode 100644 index 0000000000..a286981c67 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Methods.rb @@ -0,0 +1,30 @@ +module M + def foo + puts 'foo' + puts self + def goo + puts 'goo' + end + end +end + +class C + include M +end + +x = C.new +x.foo +x.goo + +class D + include M +end + +y = D.new +y.goo + +p M.instance_methods(false) +p D.instance_methods(false) +p C.instance_methods(false) + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Scope/ModuleFunction.rb b/merlin/main/languages/ruby/Experimental/Scope/ModuleFunction.rb new file mode 100644 index 0000000000..c196e6a3d5 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/ModuleFunction.rb @@ -0,0 +1,56 @@ +module N + def x + puts 'x' + end + + def z + end +end + +module NN + include N + def y + puts 'y' + end + + def foo + puts 'foo' + end + + def bar + puts 'bar' + end + + def w + end +end + +module M + include NN + + module_function :x, :y + + # module_function makes singleton :foo public, instance :foo remains private + private :foo + module_function :foo, "bar" + + module_function + def goo + puts 'goo' + end + +end + +p M.methods(false) +p N.methods(false) +p NN.methods(false) + +p M.instance_methods(false) +p N.instance_methods(false) +p NN.instance_methods(false) + +M.foo +M.bar +M.goo +M.x +M.y diff --git a/merlin/main/languages/ruby/Experimental/Scope/SelfInDMProc.rb b/merlin/main/languages/ruby/Experimental/Scope/SelfInDMProc.rb new file mode 100644 index 0000000000..50cc9fb21f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/SelfInDMProc.rb @@ -0,0 +1,17 @@ +class C + def foo + $x = lambda { + p self.class + } + + $x.call + end +end + +C.new.foo + +class D + define_method :goo, &$x +end + +D.new.goo \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Scope/Undef.rb b/merlin/main/languages/ruby/Experimental/Scope/Undef.rb new file mode 100644 index 0000000000..86d1270668 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Undef.rb @@ -0,0 +1,83 @@ +module M + def m1 + puts 'M::m1' + end + + def m2 + puts 'M::m2' + end + + def w + puts 'M::w' + end + + def u + undef :w + end +end + +module N + def m1 + puts 'N::m1' + end + + def m2 + puts 'N::m2' + end +end + + +class C + include M + + def c1 + puts 'c1' + end + + def c2 + puts 'c2' + end + + undef :d2 rescue puts $! +end + +class E < C + undef m1 +end + +class D < E + include N + + def d1 + puts 'd1' + end + + def d2 + puts 'd2' + end + + def w + puts 'D::w' + end + + def method_missing name, *a + puts "missing: #{name}" + end +end + +c = C.new +d = D.new + +c.m1 +c.m2 +c.c1 +c.c2 +d.m1 +d.m2 +d.c1 +d.c2 +d.d1 +d.d2 +d.u +d.w + diff --git a/merlin/main/languages/ruby/Experimental/Scope/Visibility.rb b/merlin/main/languages/ruby/Experimental/Scope/Visibility.rb new file mode 100644 index 0000000000..524bea7947 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Visibility.rb @@ -0,0 +1,70 @@ +class C + def m_public1 + puts 'm_public1' + end + + + $x = lambda { |x| + + private + + def m_private2 + puts 'm_private2' + end + + private + } + + def m_public3 + puts 'm_public3' + end + + private + + 2.times { |x| # we need a copy of scope per each call + + if x == 1 then + + def m_private3 + puts 'm_private3' + end + + else + + def m_private4 + puts 'm_private4' + end + + end + + private + + def m_private1 + puts 'm_private1' + end + + public + } + + public + + 1.times &$x + + def m_public2 + puts 'm_public2' + end + +end + +x = C.new +x.m_private1 rescue puts $! +x.m_private2 rescue puts $! +x.m_private3 rescue puts $! +x.m_private4 rescue puts $! +x.m_public1 +x.m_public2 +x.m_public3 + +p private.class + + diff --git a/merlin/main/languages/ruby/Experimental/Scope/Visibility2.rb b/merlin/main/languages/ruby/Experimental/Scope/Visibility2.rb new file mode 100644 index 0000000000..b3d9db95a6 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Scope/Visibility2.rb @@ -0,0 +1,75 @@ +module M + 1.times { |x| + + # visibility overwrites module_function flag + module_function + private + + def private1; puts '1'; end + + # module_function overwrites visibility flag + public + def public3; puts '3'; end + + # module_function overwrites visibility flag + public + module_function + def private3; puts '3'; end + + # visibility doesn't flow out of the block scope + } + + def public2; puts '2'; end + + 2.times { |x| + if x == 0 + def public4; puts '4'; end + else + def public5; puts '5'; end + end + + private + + # visibility doesn't flow back to the beginning of the block + } + + public + 1.times { + private + 2.times { |x| + # flows visibility from parent scope + + if x == 0 + def private7; puts '7'; end + else + def private8; puts '8'; end + end + + public + } + + def private9; puts '9'; end + } +end + +class C + include M +end + +def call_all(t) + t.private1 rescue puts $! + t.private3 rescue puts $! + t.public2 rescue puts $! + t.public3 rescue puts $! + t.public4 rescue puts $! + t.public5 rescue puts $! + t.private7 rescue puts $! + t.private8 rescue puts $! + t.private9 rescue puts $! +end + +puts '-- instance on C.new -- ' +call_all(C.new) + +puts '-- module functions on M --' +call_all(M) diff --git a/merlin/main/languages/ruby/Experimental/Singletons/19.txt b/merlin/main/languages/ruby/Experimental/Singletons/19.txt new file mode 100644 index 0000000000..78f30b7f92 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/19.txt @@ -0,0 +1 @@ +-- instance_methods(false) ---- diff --git a/merlin/main/languages/ruby/Experimental/Singletons/Alias.rb b/merlin/main/languages/ruby/Experimental/Singletons/Alias.rb new file mode 100644 index 0000000000..76284cf205 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/Alias.rb @@ -0,0 +1,98 @@ +module CLR + def method_missing name, *a + get = :"get_#{name.to_s.capitalize}" + c = self.class == Class ? self : self.class + + if c.method_defined?(get) then + send get, *a + else + raise + end + end +end + + +# CLR class +class C + include CLR + + # Rubd instance method: + def foo + p "C::foo" + end + + # CLR instance method: + def get_Bar + p "C::get_Bar" + end + + class << self + include CLR + + # Rubd class method: + def foo + p "S(C)::foo" + end + + # CLR static method: + def get_Bar + p "S(C)::get_Bar" + end + end + +end + +# Rubd class +class D < C + + alias_method :goo, :foo + alias_method :hoo, :bar rescue puts 'Error hoo/bar' + + alias :goo2 :foo + alias :hoo2 :bar rescue puts 'Error hoo2/bar' + +end + +# Rubd class +class E < C + def method_missing name,*a + puts 'mm' + end +end + +c = C.new +d = D.new +e = E.new + +print 'd.foo -> ' +d.foo + +print 'd.bar -> ' +d.bar + +print 'd.get_Bar -> ' +d.get_Bar + +print 'd.goo -> ' +d.goo + +print 'd.hoo -> ' +d.hoo rescue puts 'Error' + +print 'd.goo2 -> ' +d.goo2 + +print 'd.hoo2 -> ' +d.hoo2 rescue puts 'Error' + +print 'C.foo -> ' +C.foo + +print 'C.bar -> ' +C.bar + +print 'e.foo -> ' +e.foo + +print 'e.bar -> ' +e.bar diff --git a/merlin/main/languages/ruby/Experimental/Singletons/ClassVariables.rb b/merlin/main/languages/ruby/Experimental/Singletons/ClassVariables.rb new file mode 100644 index 0000000000..7ee527b0b8 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/ClassVariables.rb @@ -0,0 +1,48 @@ +# class variable is defined on the lexically inner most non-singleton class/module +$x = "foo" + +module M + class C + $C = self + @@C = 1 + end + + class << C + $SC = self + @@SC = 1 + class_variable_set :@@SC1, 1 + end + + class << $SC + $SSC = self + @@SSC = 1 + class_variable_set :@@SSC1, 1 + end + + class << $x + $Sx = self + @@Sx = 1 + class_variable_set :@@Sx, 1 + end + + class C + puts @@C + puts @@SC rescue puts 'C:!@@SC' + puts @@SSC rescue puts 'C:!@@SSC' + end +end + +module M + puts @@SC, @@SSC +end + +puts '-' * 10 + +puts "M => #{M.class_variables.inspect}" +puts "M::C => #{M::C.class_variables.inspect}" +puts "S(M::C) => #{$SC.class_variables.inspect}" +puts "SS(M::C) => #{$SSC.class_variables.inspect}" +puts "S(x) => #{$Sx.class_variables.inspect}" + +# not supported in IronRuby +#puts "D(M::C) => #{$SSC.superclass.class_variables.inspect}" diff --git a/merlin/main/languages/ruby/Experimental/Singletons/Constants.rb b/merlin/main/languages/ruby/Experimental/Singletons/Constants.rb new file mode 100644 index 0000000000..2dc75774bf --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/Constants.rb @@ -0,0 +1,165 @@ +$InitConsts = Object.constants + +module Kernel + C_Kernel = 1 + + class << self + $SKernel = self + C_SKernel = 1 + end +end + +class Object + C_Object = 1 + + class << self + $SObject = self + C_SObject = 1 + + class << self + $SSObject = self + C_SSObject = 1 + end + end +end + +class Module + C_Module = 1 + + class << self + $SModule = self + C_SModule = 1 + + class << self + $SSModule = self + C_SSModule = 1 + end + end +end + +class Class + C_Class = 1 + + class << self + $SClass = self + C_SClass = 1 + + class << self + $SSClass = self + C_SSClass = 1 + end + end +end + +module M + C_M = 1 + + class << self + $SM = self + C_SM = 1 + class << self + $SSM = self + C_SSM = 1 + end + end +end + +module N + C_N = 1 +end + +module MSC + C_MSC = 1 +end + +module MSSC + C_MSSC = 1 +end + +class C + include M, N + C_C = 1 + + class << self + include MSC + $SC = self + C_SC = 1 + + class << self + include MSSC + $SSC = self + C_SSC = 1 + end + end +end + +class D < C + C_D = 1 + + class << self + $SD = self + C_SD = 1 + + class << self + $SSD = self + C_SSD = 1 + SSD = 1 + end + end +end + +def d name,mod + cons = mod.constants - $InitConsts - ["C","D","M","N","F"] + + #not_found = [] + #make_all(mod).each { |c| + # mod.const_get c rescue not_found << c + #} + + print "#{name}\t\t#{mod.ancestors.inspect}:\t\t#{cons.sort.inspect}" + #print "!#{not_found.inspect}" unless not_found.empty? + puts +end + +def make_all m + result = [] + m.ancestors.each { |c| + result << "C_#{c.name}" + } + result +end + +m = [ +"Kernel", Kernel, +"Object", Object, +"Module", Module, +"Class", Class, +"S(Kernel)", $SKernel, +"S(Object)", $SObject, +"S(Module)", $SModule, +"S(Class)", $SClass, +"SS(Object)", $SSObject, +"SS(Module)", $SSModule, +"SS(Class)", $SSClass, +"C", C, +"D", D, +"S(C)", $SC, +"SS(C)", $SSC, +"D(C)", $SSC.superclass, +"S(D)", $SD, +"SS(D)", $SSD, +"D(D)", $SSD.superclass, +"M", M, +"S(M)", $SM, +"SS(M)", $SSM, +"D(M)", $SSM.superclass, +"Array", Array, +"Hash", Hash, +] + +for i in 0..(m.size/2 - 1) do + d m[i*2], m[i*2+1] +end + +#p $SC.private_instance_methods(false) +p $SC.instance_methods(false) diff --git a/merlin/main/languages/ruby/Experimental/Singletons/Constants2.rb b/merlin/main/languages/ruby/Experimental/Singletons/Constants2.rb new file mode 100644 index 0000000000..2cb0c25618 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/Constants2.rb @@ -0,0 +1,12 @@ +# contrary to class variables, constatns are lexically defined on singleton classes + +$x = "x" +module M + class << $x + $Sx = self + C = 1 + end +end + +p M.constants # [] +p $Sx.constants # ["C"] \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Singletons/Load.rb b/merlin/main/languages/ruby/Experimental/Singletons/Load.rb new file mode 100644 index 0000000000..307c66b339 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/Load.rb @@ -0,0 +1,16 @@ +load 'Load_1.rb', true +$S1 = $S +$T1 = $T + +load 'Load_1.rb', true +$S2 = $S +$T2 = $T + +p self.object_id +p $S1.object_id +p $S2.object_id + +p TOPLEVEL_BINDING.object_id +p $T1.object_id +p $T2.object_id + diff --git a/merlin/main/languages/ruby/Experimental/Singletons/Load_1.rb b/merlin/main/languages/ruby/Experimental/Singletons/Load_1.rb new file mode 100644 index 0000000000..cfb70e5ee2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/Load_1.rb @@ -0,0 +1,2 @@ +$S = self +$T = TOPLEVEL_BINDING diff --git a/merlin/main/languages/ruby/Experimental/Singletons/SingletonTraits.rb b/merlin/main/languages/ruby/Experimental/Singletons/SingletonTraits.rb new file mode 100644 index 0000000000..95487aabaa --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/SingletonTraits.rb @@ -0,0 +1,94 @@ +class C + $C = self + class << self + $SC = self + + def foo + puts 'foo' + end + + class << self + $SSC = self + class << self + $SSSC = self + $DC = self.superclass + end + end + end +end + +$x = C.new +class << $x + $Sx = self + class << self + $SSx = self + class << self + $SSSx = self + $Dx = self.superclass + end + end +end + +$y = C.new +class << $y + $Sy = self +end + +$z = C.new +class << $z + $Sz = self + class << self + $SSz = self + end +end + +class << self + $Smain = self + class << self + $SSmain = self + class << self + $SSSmain = self + $Dmain = self.superclass + end + end +end + +module M + def self.d mod,name + puts "#{name}.private_instance_methods: #{mod.private_instance_methods(false).sort.inspect}" + puts "#{name}.protected_instance_methods: #{mod.protected_instance_methods(false).sort.inspect}" + puts "#{name}.public_instance_methods: #{mod.public_instance_methods(false).sort.inspect}" + puts "#{name}.instance_methods: #{mod.instance_methods(false).sort.inspect}" + puts + end +end + +class << $SC + #undef :nesting +end + +[ +[$Smain, "S(main)"], +[$SSmain, "SS(main)"], +[$SSSmain, "SSS(main)"], +[$Dmain, "D(main)"], + +[$Sx, "S(x)"], +[$SSx, "SS(x)"], +[$SSSx, "SSS(x)"], +[$Dx, "D(x)"], + +[$Sy, "S(y)"], + +[$Sz, "S(z)"], +[$SSz, "SS(z)"], + +[$C, "C"], +[$SC, "S(C)"], +[$SSC, "SS(C)"], +[$SSSC, "SSS(C)"], +[$DC, "D(C)"], + +].each { |x,y| M.d x,y } + + diff --git a/merlin/main/languages/ruby/Experimental/Singletons/SingletonTraits2.rb b/merlin/main/languages/ruby/Experimental/Singletons/SingletonTraits2.rb new file mode 100644 index 0000000000..68c5bb00e3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/SingletonTraits2.rb @@ -0,0 +1,93 @@ +class Object + def iobject + end + + class << self + def cobject + end + + class << self + def ccobject + end + end + + end +end + +class Module + def imodule + end + + class << self + def cmodule + end + + class << self + def ccmodule + end + end + end +end + +class Class + def iclass + end + + class << self + def cclass + end + + class << self + def ccclass + end + end + end +end + +class C + def ifoo + end + + class << self + def cfoo + end + + class << self + def ccfoo + end + end + end +end + +class D < C + def ibar + end + + class << self + def cbar + end + + class << self + def ccbar + end + end + end +end + +x = D.new +class << x + $Sx = self + class << self + $SSx = self + class << self + $SSSx = self + end + end +end + +p $Sx.instance_methods(false).sort +p $SSx.instance_methods(false).sort +p $SSSx.instance_methods(false).sort + +p C.instance_methods(false).sort +p D.instance_methods(false).sort diff --git a/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.Ancestors.rb b/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.Ancestors.rb new file mode 100644 index 0000000000..c22b06351f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.Ancestors.rb @@ -0,0 +1,5 @@ +require 'Ultimate.defs' + +$ordered_names.each { |name| + puts "#{name}:\n#{p $classes[name].ancestors}" +} diff --git a/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.Defs.rb b/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.Defs.rb new file mode 100644 index 0000000000..23787328c3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.Defs.rb @@ -0,0 +1,123 @@ +module N_Object; def f; puts 'N_Object'; super; end; def f_N_Object; end;end +module N_Module; def f; puts 'N_Module'; super; end; def f_N_Module; end;end +module N_Class; def f; puts 'N_Class'; super; end; def f_N_Class; end;end +module N_C; def f; puts 'N_C'; super; end; def f_N_C; end;end +module N_M; def f; puts 'N_M'; super; end; def f_N_M; end;end +module N_NoSM; def f; puts 'N_NoSM'; super; end; def f_N_NoSM; end;end +module N_MModule; def f; puts 'N_MModule'; super; end; def f_N_MModule; end;end + +class Object; def f; puts 'Object'; end; def f_Object; end;include N_Object;end +class Module; def f; puts 'Module'; super; end; def f_Module; end;include N_Module;end +class Class; def f; puts 'Class'; super; end; def f_Class; end;include N_Class; end +class C; def f; puts 'C'; super; end; def f_C; end;include N_C; end +module M; def f; puts 'M'; super; end; def f_M; end;include N_M; end +module NoSM; def f; puts 'NoSM'; super; end; def f_NoSM; end;include N_NoSM; end +class MModule < Module;def f; puts 'MModule'; super; end; def f_MModule; end;include N_MModule;end + +MM = MModule.new +$obj = C.new + +# object without a singleton: +$no_singleton = C.new + +module N_S1_Object; def f; puts 'N_S1_Object'; super; end; def f_N_S1_Object; end;end +module N_S1_Module; def f; puts 'N_S1_Module'; super; end; def f_N_S1_Module; end;end +module N_S1_Class; def f; puts 'N_S1_Class'; super; end; def f_N_S1_Class; end;end +module N_S1_C; def f; puts 'N_S1_C'; super; end; def f_N_S1_C; end;end +module N_S1_M; def f; puts 'N_S1_M'; super; end; def f_N_S1_M; end;end +module N_S1_obj; def f; puts 'N_S1_obj'; super; end; def f_N_S1_obj; end;end + +$S1_Object = class << Object; def f; puts 'S(Object)'; super; end; def f_S1_Object; end; include N_S1_Object;self; end +$S1_Module = class << Module; def f; puts 'S(Module)'; super; end; def f_S1_Module; end; include N_S1_Module;self; end +$S1_Class = class << Class; def f; puts 'S(Class)'; super; end; def f_S1_Class ; end; include N_S1_Class; self; end +$S1_C = class << C; def f; puts 'S(C)'; super; end; def f_S1_C ; end; include N_S1_C; self; end +$S1_M = class << M; def f; puts 'S(M)'; super; end; def f_S1_M ; end; include N_S1_M; self; end +$S1_obj = class << $obj; def f; puts 'S(obj)'; super; end; def f_S1_obj ; end; include N_S1_obj; self; end + +$S2_Object = class << $S1_Object; def f; puts 'S2(Object)'; super; end; def f_S2_Object; end; self; end +$S2_Module = class << $S1_Module; def f; puts 'S2(Module)'; super; end; def f_S2_Module; end; self; end +$S2_Class = class << $S1_Class; def f; puts 'S2(Class)'; super; end; def f_S2_Class ; end; self; end +$S2_C = class << $S1_C; def f; puts 'S2(C)'; super; end; def f_S2_C ; end; self; end +$S2_M = class << $S1_M; def f; puts 'S2(M)'; super; end; def f_S2_M ; end; self; end +$S2_obj = class << $S1_obj; def f; puts 'S2(obj)'; super; end; def f_S2_obj ; end; self; end + +$S3_Object = class << $S2_Object; def f; puts 'S3(Object)'; super; end; def f_S3_Object; end; self; end +$S3_Module = class << $S2_Module; def f; puts 'S3(Module)'; super; end; def f_S3_Module; end; self; end +$S3_Class = class << $S2_Class; def f; puts 'S3(Class)'; super; end; def f_S3_Class ; end; self; end +$S3_C = class << $S2_C; def f; puts 'S3(C)'; super; end; def f_S3_C ; end; self; end +$S3_M = class << $S2_M; def f; puts 'S3(M)'; super; end; def f_S3_M ; end; self; end +$S3_obj = class << $S2_obj; def f; puts 'S3(obj)'; super; end; def f_S3_obj ; end; self; end + +# the following singletons hide Ruby bug in for dummy singletons: method table is shared with the previous singleton +class << $S3_Object; end +class << $S3_Module; end +class << $S3_Class; end +class << $S3_C; end +class << $S3_M; end +class << $S3_obj; end + +$classes = { + 'Object' => Object, + 'S1_Object' => $S1_Object, + 'S2_Object' => $S2_Object, + 'S3_Object' => $S3_Object, + 'Module' => Module, + 'S1_Module' => $S1_Module, + 'S2_Module' => $S2_Module, + 'S3_Module' => $S3_Module, + 'Class' => Class, + 'S1_Class' => $S1_Class, + 'S2_Class' => $S2_Class, + 'S3_Class' => $S3_Class, + 'S1_obj' => $S1_obj, + 'S2_obj' => $S2_obj, + 'S3_obj' => $S2_obj, + 'M' => M, + 'S1_M' => $S1_M, + 'S2_M' => $S2_M, + 'S3_M' => $S3_M, + 'C' => C, + 'S1_C' => $S1_C, + 'S2_C' => $S2_C, + 'S3_C' => $S3_C, + 'NoSM' => NoSM, + 'MModule' => MModule, + 'MM' => MM +} + +$ordered_names = [ + 'Object', + 'S1_Object', + 'S2_Object', + 'S3_Object', + 'Module', + 'S1_Module', + 'S2_Module', + 'S3_Module', + 'Class', + 'S1_Class', + 'S2_Class', + 'S3_Class', + 'S1_obj', + 'S2_obj', + 'S3_obj', + 'M', + 'S1_M', + 'S2_M', + 'S3_M', + 'C', + 'S1_C', + 'S2_C', + 'S3_C', + 'NoSM', + 'MModule', + 'MM' +] + +# define a distinct method on each module: +#$classes.each { |name,cls| +# cls.module_eval { +# define_method("f_" + name) {} +# } +#} + diff --git a/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.MethodLookup.rb b/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.MethodLookup.rb new file mode 100644 index 0000000000..6deea88c24 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.MethodLookup.rb @@ -0,0 +1,120 @@ +module Kernel + def i_k + puts 'i_k' + end + + def self.c_k + puts 'c_k' + end +end + +module X + def i_x + puts 'i_x' + end + + def self.c_x + puts 'c_x' + end +end + +class Object + include X + + def i_o + puts 'i_o' + end + + def self.c_o + puts 'c_o' + end +end + +class Class + def i_c + puts 'i_c' + end + + def self.c_c + puts 'c_c' + end +end + +class Module + def i_m + puts 'i_m' + end + + def self.c_m + puts 'c_m' + end +end + +$modules = [ + module MyModule; self; end, + module MyModule; class << self; self; end; end, + class MyClass; self; end, + class << Object.new; self; end, +] + +def test met, x, y + + $modules.each { |m| + puts "-- #{m} --------------" + + m.module_eval do + puts send(met, *[:ai_k, :i_k][x,y]) rescue p $! + puts send(met, *[:ai_x, :i_x][x,y]) rescue p $! + puts send(met, *[:ai_o, :i_o][x,y]) rescue p $! + + # errors: + + puts send(met, *[:ac_k, :c_k][x,y]) rescue p $! + puts send(met, *[:ac_x, :c_x][x,y]) rescue p $! + puts send(met, *[:ac_o, :c_o][x,y]) rescue p $! + + puts send(met, *[:ai_c, :i_c][x,y]) rescue p $! + puts send(met, *[:ac_c, :c_c][x,y]) rescue p $! + puts send(met, *[:ai_m, :i_m][x,y]) rescue p $! + puts send(met, *[:ac_m, :c_m][x,y]) rescue p $! + + end + + puts + } +end + +def quick_test met, x, y + object_lookup = ((MyModule.send(met, *[:ai_x, :i_x][x,y]); true) rescue false) + instance_lookup = ((MyModule.send(met, *[:ai_m, :i_m][x,y]); true) rescue false) + + print "#{met}: ", + instance_lookup ? ' instance' : '', + object_lookup ? ' object' : '' + + puts +end + +$methods2 = [:alias_method] +$methods1 = [:method_defined?, :method, :public, :undef_method] + +# quick test: +$methods2.each { |met| quick_test met, 0, 2 } +$methods1.each { |met| quick_test met, 1, 2 } + +# full test: +$methods2.each { |met| + puts + puts "== #{met} =" + ('=' * 50) + puts + puts + test met, 0, 2 +} + +$methods1.each { |met| + puts + puts "== #{met} =" + ('=' * 50) + puts + puts + test met, 1, 2 +} diff --git a/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.Reflection.rb b/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.Reflection.rb new file mode 100644 index 0000000000..9df7dba4b9 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.Reflection.rb @@ -0,0 +1,75 @@ +=begin + +Output matches Ruby 1.8 except for + - [allocate, superclass, new] on high-order singletons + - inherited == false ==> [allocate, constants, nesting, new, superclass] are not included (they might be only on dummy singleton) + - methods on singletons next to the dummy singletons (S3) + +=end + +require 'Ultimate.Defs' + +$clr_only = ["of", "[]", "to_clr_type"] +$all = false + +def dump ms + if $all then + ((ms - $clr_only).sort).inspect + else + ms.delete_if { |name| name.index("f_") != 0} + ms.sort.inspect + end +end + + +puts '-- Module#instance_methods(false) ----' +$ordered_names.each { |name| + puts "#{name}:\n#{dump $classes[name].instance_methods(false)}" +} + +puts '-- Module#instance_methods(true) ----' +$ordered_names.each { |name| + puts "#{name}:\n#{dump $classes[name].instance_methods(true)}" +} + +puts '-- Kernel#singleton_methods(false) ----' +$ordered_names.each { |name| + puts "#{name}:\n#{dump $classes[name].singleton_methods(false)}" +} +puts "obj:\n#{dump $obj.singleton_methods(false)}" +puts "no_singleton:\n#{dump $no_singleton.singleton_methods(false)}" + +puts '-- Kernel#singleton_methods(true) ----' +$ordered_names.each { |name| + puts "#{name}:\n#{dump $classes[name].singleton_methods(true)}" +} +puts "obj:\n#{dump $obj.singleton_methods(true)}" +puts "no_singleton:\n#{dump $no_singleton.singleton_methods(true)}" + +puts '-- Kernel#methods(false) ----' +$ordered_names.each { |name| + puts "#{name}:\n#{dump $classes[name].methods(false)}" +} +puts "obj:\n#{dump $obj.methods(false)}" +puts "no_singleton:\n#{dump $no_singleton.methods(false)}" + +puts '-- Kernel#methods(true) ----' +$ordered_names.each { |name| + puts "#{name}:\n#{dump $classes[name].methods(true)}" +} +puts "obj:\n#{dump $obj.methods(true)}" +puts "no_singleton:\n#{dump $no_singleton.methods(true)}" + +puts '-- Kernel#public_methods(false) ----' +$ordered_names.each { |name| + puts "#{name}:\n#{dump $classes[name].public_methods(false)}" +} +puts "obj:\n#{dump $obj.public_methods(true)}" +puts "no_singleton:\n#{dump $no_singleton.public_methods(true)}" + +puts '-- Kernel#public_methods(true) ----' +$ordered_names.each { |name| + puts "#{name}:\n#{dump $classes[name].public_methods(true)}" +} +puts "obj:\n#{dump $obj.public_methods(true)}" +puts "no_singleton:\n#{dump $no_singleton.public_methods(true)}" diff --git a/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.rb b/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.rb new file mode 100644 index 0000000000..6f3f77746d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/Ultimate.rb @@ -0,0 +1,67 @@ +class Object; def f; puts 'Object'; end; end +class Module; def f; puts 'Module'; super; end; end +class Class; def f; puts 'Class'; super; end; end +class C; def f; puts 'C'; super; end; end +module M; def f; puts 'M'; super; end; end +$obj = C.new + +$S1_Object = class << Object; def f; puts 'S(Object)'; super; end; self; end +$S1_Module = class << Module; def f; puts 'S(Module)'; super; end; self; end +$S1_Class = class << Class; def f; puts 'S(Class)'; super; end; self; end +$S1_C = class << C; def f; puts 'S(C)'; super; end; self; end +$S1_M = class << M; def f; puts 'S(M)'; super; end; self; end +$S1_obj = class << $obj; def f; puts 'S(obj)'; super; end; self; end + +$S2_Object = class << $S1_Object; def f; puts 'S2(Object)'; super; end; self; end +$S2_Module = class << $S1_Module; def f; puts 'S2(Module)'; super; end; self; end +$S2_Class = class << $S1_Class; def f; puts 'S2(Class)'; super; end; self; end +$S2_C = class << $S1_C; def f; puts 'S2(C)'; super; end; self; end +$S2_M = class << $S1_M; def f; puts 'S2(M)'; super; end; self; end +$S2_obj = class << $S1_obj; def f; puts 'S2(obj)'; super; end; self; end + +$S3_Object = class << $S2_Object; def f; puts 'S3(Object)'; super; end; self; end +$S3_Module = class << $S2_Module; def f; puts 'S3(Module)'; super; end; self; end +$S3_Class = class << $S2_Class; def f; puts 'S3(Class)'; super; end; self; end +$S3_C = class << $S2_C; def f; puts 'S3(C)'; super; end; self; end +$S3_M = class << $S2_M; def f; puts 'S3(M)'; super; end; self; end +$S3_obj = class << $S2_obj; def f; puts 'S3(obj)'; super; end; self; end + +def t(name, start) + puts + puts "#{name}.f:" + start.f +end + +t 'obj', $obj +t 'S1(obj)', $S1_obj +t 'S2(obj)', $S2_obj + +puts '-' * 20 + +t 'C', C +t 'S1(C)', $S1_C +t 'S2(C)', $S2_C + +puts '-' * 20 + +t 'M', M +t 'S1(M)', $S1_M +t 'S2(M)', $S2_M + +puts '-' * 20 + +t 'Class', Class +t 'S1(Class)', $S1_Class +t 'S2(Class)', $S2_Class + +puts '-' * 20 + +t 'Module', Module +t 'S1(Module)',$S1_Module +t 'S2(Module)',$S2_Module + +puts '-' * 20 + +t 'Object', Object +t 'S1(Object)',$S1_Object +t 'S2(Object)',$S2_Object diff --git a/merlin/main/languages/ruby/Experimental/Singletons/calls.rb b/merlin/main/languages/ruby/Experimental/Singletons/calls.rb new file mode 100644 index 0000000000..06e8c12390 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/calls.rb @@ -0,0 +1,209 @@ +require 'def' + +def probe_methods(obj, name) + di = [] + + di << obj.i_Sx rescue 1 + di << obj.i_Sx1 rescue 1 + + di << obj.i_C rescue 1 + di << obj.i_S1 rescue 1 + di << obj.i_S2 rescue 1 + + di << obj.i_D rescue 1 + di << obj.i_T1 rescue 1 + di << obj.i_T2 rescue 1 + di << obj.i_T3 rescue 1 + + di << obj.i_Object rescue 1 + di << obj.i_Object1 rescue 1 + + di << obj.i_Module rescue 1 + di << obj.i_Module1 rescue 1 + + di << obj.i_Class rescue 1 + di << obj.i_Class1 rescue 1 + + di << obj.i_SMx rescue 1 + di << obj.i_SM1 rescue 1 + di << obj.i_SM1_1 rescue 1 + + da = [] + + da << obj.ai_Sx rescue 1 + da << obj.ai_Sx1 rescue 1 + + da << obj.ai_C rescue 1 + da << obj.ai_S1 rescue 1 + da << obj.ai_S2 rescue 1 + + da << obj.ai_D rescue 1 + da << obj.ai_T1 rescue 1 + da << obj.ai_T2 rescue 1 + da << obj.ai_T3 rescue 1 + + da << obj.ai_Object rescue 1 + da << obj.ai_Object1 rescue 1 + + da << obj.ai_Module rescue 1 + da << obj.ai_Module1 rescue 1 + + da << obj.ai_Class rescue 1 + da << obj.ai_Class1 rescue 1 + + da << obj.ai_SMx rescue 1 + da << obj.ai_SM1 rescue 1 + da << obj.ai_SM1_1 rescue 1 + + dc = [] + + dc << obj.c_Sx rescue 1 + dc << obj.c_Sx1 rescue 1 + + dc << obj.c_C rescue 1 + dc << obj.c_S1 rescue 1 + dc << obj.c_S2 rescue 1 + + dc << obj.c_D rescue 1 + dc << obj.c_T1 rescue 1 + dc << obj.c_T2 rescue 1 + dc << obj.c_T3 rescue 1 + + dc << obj.c_Object rescue 1 + dc << obj.c_Object1 rescue 1 + + dc << obj.c_Module rescue 1 + dc << obj.c_Module1 rescue 1 + + dc << obj.c_Class rescue 1 + dc << obj.c_Class1 rescue 1 + + dc << obj.c_SMx rescue 1 + dc << obj.c_SM1 rescue 1 + dc << obj.c_SM1_1 rescue 1 + + ms = [ +:i_C, +:i_Sx, +:i_Sx1, +:i_S1, +:i_S2, +:i_D, +:i_T1, +:i_T2, +:i_SM1, +:i_Object, +:i_Object1, +:i_Module, +:i_Module1, +:i_Class, +:i_Class1, +:allocate, +:superclass, +:new, +:constants, +:nesting, + ] + + + dm = [] + ms.each { |x| + begin + obj.instance_method(x) + rescue + else + dm << x + end + } + + begin + im = obj.instance_methods() + im = im.find_all { |x| x[0..1] == "i_" || x[0..1] == "c_"} + rescue + im = "N/A" + end + + puts "#{name} instance: #{di.inspect}" + puts "#{name} instance aliases: #{(da == di) ? "matches" : da.inspect}" + puts "#{name} class: #{dc.inspect}" + puts "#{name} instance_method: #{dm.inspect}" + puts "#{name} instance_methods: #{im.inspect}" + +end + +def probe_consts(obj, name) + c = [ +:CONST_Sx, +:CONST_Sx1, +:CONST_C, +:CONST_S1, +:CONST_S2, +:CONST_S3, +:CONST_D, +:CONST_T1, +:CONST_T2, +:CONST_T3, +:CONST_Object, +:CONST_Object1, +:CONST_Module, +:CONST_Module1, +:CONST_Class, +:CONST_Class1, +:CONST_SMx, +:CONST_SM1, +:CONST_SM1_1, + ] + d = [] + + c.each { |x| + begin + obj.const_get(x) + rescue + else + d << x + end + } + + puts "#{name} constants: #{d.inspect}" + puts +end + +probe_methods($X, "X"); +puts + +probe_methods($Sx, "Sx"); +probe_consts($Sx, "Sx"); + +probe_methods($Sx1, "Sx1"); +probe_consts($Sx1, "Sx1"); + +probe_methods($S1, "S1"); +probe_consts($S1, "S1"); + +probe_methods($S2, "S2"); +probe_consts($S2, "S2"); + +probe_methods($S3, "S3"); +probe_consts($S3, "S3"); + +probe_methods($SM1, "SM1"); +probe_consts($SM1, "SM1"); + +probe_methods($Class, "Class"); +probe_consts($Class, "Class"); + +probe_methods($Module, "Module"); +probe_consts($Module, "Module"); + +probe_methods($Object, "Object"); +probe_consts($Object, "Object"); + +probe_methods($Module, "Class1"); +probe_consts($Module, "Class1"); + +probe_methods($Module, "Module1"); +probe_consts($Module, "Module1"); + +probe_methods($Object, "Object1"); +probe_consts($Object, "Object1"); + diff --git a/merlin/main/languages/ruby/Experimental/Singletons/class.rb b/merlin/main/languages/ruby/Experimental/Singletons/class.rb new file mode 100644 index 0000000000..a3c3111f7f --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/class.rb @@ -0,0 +1,28 @@ +require 'def.rb' + +def c x + puts "#{x.name}: #{x.inspect}, name = '#{x.name}', id = #{x.object_id}" + s = x.class + puts "#{x.name}.class: #{s.inspect}, name = '#{s.name}', id = #{s.object_id}" + s = s.class + puts "#{x.name}.class.class: #{s.inspect}, name = '#{s.name}', id = #{s.object_id}" +end + +puts '-- classes of C --' +c C +puts '-- classes of Sx --' +c Sx +puts '-- classes of Sy --' +c Sy +puts '-- classes of S1 --' +c S1 +puts '-- classes of S2 --' +c S2 +puts '-- classes of S3 --' +c S3 +puts '-- classes of S4 --' +c S4 +puts '-- classes of T1 --' +c T1 +puts '-- classes of T2 --' +c T2 diff --git a/merlin/main/languages/ruby/Experimental/Singletons/class_dummy.rb b/merlin/main/languages/ruby/Experimental/Singletons/class_dummy.rb new file mode 100644 index 0000000000..e48f376d05 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/class_dummy.rb @@ -0,0 +1,32 @@ +class C +end + +class << C + ::DUMMY = self.superclass +end + +class DUMMY + def foo + end + + def self.goo + end +end + +puts 'ancestors:' +p DUMMY.ancestors +puts 'instance:' +p DUMMY.instance_methods(false) +puts 'private:' +p DUMMY.private_methods(false) +puts 'protected:' +p DUMMY.protected_methods(false) +puts 'singleton:' +p DUMMY.singleton_methods(false) + +p DUMMY.allocate rescue p $! +p DUMMY.new rescue p $! +p DUMMY.superclass +p DUMMY.superclass.object_id +p DUMMY.name +p DUMMY.object_id diff --git a/merlin/main/languages/ruby/Experimental/Singletons/class_prot.rb b/merlin/main/languages/ruby/Experimental/Singletons/class_prot.rb new file mode 100644 index 0000000000..61902782df --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/class_prot.rb @@ -0,0 +1,22 @@ +class C +end + +class << C + ::CP = self +end + +puts 'ancestors:' +p CP.ancestors +puts 'instance:' +p CP.instance_methods(false) +puts 'private:' +p CP.private_methods(false) +puts 'protected:' +p CP.protected_methods(false) +puts 'singleton:' +p CP.singleton_methods(false) + +p CP.allocate rescue p $! +p CP.new rescue p $! +p CP.superclass +p CP diff --git a/merlin/main/languages/ruby/Experimental/Singletons/consts.rb b/merlin/main/languages/ruby/Experimental/Singletons/consts.rb new file mode 100644 index 0000000000..df7d2eaf08 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/consts.rb @@ -0,0 +1,80 @@ +module M1 + CM1 = "CM1" + + module M2 + CM12 = "CM12" + + puts "M1::M2#.cctor (self = #{self.inspect}):" + puts CM1 + puts CM12 + puts + end + + def bar + puts "M1#bar (self = #{self.inspect}):" + puts CA rescue puts "CA - error: #{$!}" + puts B::CAB rescue puts "B - error: #{$!}" + puts ::A::CA + puts ::A::B::CAB + puts CM1 rescue puts "CM1 - error: #{$!}" + puts CM12 rescue puts "CM12 - error: #{$!}" + puts + end + + def self.c_bar + puts "M1#c_bar (self = #{self.inspect}):" + puts CA rescue puts "CA - error: #{$!}" + puts B::CAB rescue puts "B - error: #{$!}" + puts ::A::CA + puts ::A::B::CAB + puts CM1 rescue puts "CM1 - error: #{$!}" + puts CM12 rescue puts "CM12 - error: #{$!}" + puts + end +end + +class A + include M1 + CA = "CA" + + class B + CAB = "CAB" + + puts "A::B#.cctor (self = #{self.inspect}):" + puts CA + puts CAB + puts + end + + def error + #parse error (dynamic constant assignment): ABarC = "ABarC" + end + + def foo + puts "A#foo (self = #{self.inspect}):" + puts CA + puts B::CAB + puts ::A::CA + puts ::A::B::CAB + puts CM1 + puts CM12 rescue puts "CM12 - error: #{$!}" + puts + end + + def self.c_foo + puts "A#c_foo (self = #{self.inspect}):" + puts CA + puts B::CAB + puts ::A::CA + puts ::A::B::CAB + puts CM1 + puts CM12 rescue puts "CM12 - error: #{$!}" + puts + end +end + +A.new.foo +A.new.bar +A.c_foo +M1.c_bar + diff --git a/merlin/main/languages/ruby/Experimental/Singletons/consts_singletons.rb b/merlin/main/languages/ruby/Experimental/Singletons/consts_singletons.rb new file mode 100644 index 0000000000..c42cd8cc8c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/consts_singletons.rb @@ -0,0 +1,232 @@ +# == Object =========== + +class Object + $Object0 = self + CObject0 = 'CObject0' +end + +class << $Object0 + $Object1 = self + CObject1 = 'CObject1' +end + +class << $Object1 + $Object2 = self + CObject2 = 'CObject2' +end + +class << $Object2 + $Object3 = self + CObject3 = 'CObject3' +end + +class << $Object3 + $Object4 = self + CObject4 = 'CObject4' +end + +$ObjectD = $Object4.superclass + +# == Module =========== + +class Module + $Module0 = self + CModule0 = 'CModule0' +end + +class << $Module0 + $Module1 = self + CModule1 = 'CModule1' +end + +class << $Module1 + $Module2 = self + CModule2 = 'CModule2' +end + +class << $Module2 + $Module3 = self + CModule3 = 'CModule3' +end + +class << $Module3 + $Module4 = self + CModule4 = 'CModule4' +end + +$ModuleD = $Module4.superclass + +# == Class ============ + +class Class + $Class0 = self + CClass0 = 'CClass0' +end + +class << $Class0 + $Class1 = self + CClass1 = 'CClass1' +end + +class << $Class1 + $Class2 = self + CClass2 = 'CClass2' +end + +class << $Class2 + $Class3 = self + CClass3 = 'CClass3' +end + +class << $Class3 + $Class4 = self + CClass4 = 'CClass4' +end + +$ClassD = $Class4.superclass + +# == D ================ + +class D + $D0 = self + CD0 = 'CD0' +end + +class << $D0 + $D1 = self + CD1 = 'CD1' +end + +class << $D1 + $D2 = self + CD2 = 'CD2' +end + +class << $D2 + $D3 = self + CD3 = 'CD3' +end + +class << $D3 + $D4 = self + CD4 = 'CD4' +end + +$DD = $D4.superclass + +# == C ================ + +class C < D + $C0 = self + CC0 = 'CC0' +end + +class << $C0 + $C1 = self + CC1 = 'CC1' +end + +class << $C1 + $C2 = self + CC2 = 'CC2' +end + +class << $C2 + $C3 = self + CC3 = 'CC3' +end + +class << $C3 + $C4 = self + CC4 = 'CC4' +end + +$CD = $C4.superclass + +# == X ================ + +$X0 = C.new + +class << $X0 + $X1 = self + CX1 = 'CX1' +end + +class << $X1 + $X2 = self + CX2 = 'CX2' +end + +class << $X2 + $X3 = self + CX3 = 'CX3' +end + +class << $X3 + $X4 = self + CX4 = 'CX4' +end + +$XD = $X4.superclass + +# == Y ================ + +$Y0 = C.new + +class << $Y0 + $Y1 = self + CY1 = 'CY1' +end + +$YD = $Y1.superclass + +# ===================== + +classes = [ +$C0,$C1,$C2,$C3,$C4,$CD, +$D0,$D1,$D2,$D3,$D4,$DD, +$X0,$X1,$X2,$X3,$X4,$XD, +$Y0,$Y1,$YD, +$Object0,$Object1,$Object2,$Object3,$Object4,$ObjectD, +$Module0,$Module1,$Module2,$Module3,$Module4,$ModuleD, +$Class0,$Class1,$Class2,$Class3,$Class4,$ClassD, +] + +# TODO: remove +#dummies = [$CD, $DD, $XD, $YD, $ObjectD, $ModuleD, $ClassD] + +names = [ +:C0,:C1,:C2,:C3,:C4,:CD, +:D0,:D1,:D2,:D3,:D4,:DD, +:X0,:X1,:X2,:X3,:X4,:XD, +:Y0,:Y1,:YD, +:Object0,:Object1,:Object2,:Object3,:Object4,:ObjectD, +:Module0,:Module1,:Module2,:Module3,:Module4,:ModuleD, +:Class0,:Class1,:Class2,:Class3,:Class4,:ClassD, +] + +consts = [ +:CC0,:CC1,:CC2,:CC3,:CC4, +:CD0,:CD1,:CD2,:CD3,:CD4, +:CX1,:CX2,:CX3,:CX4, +:CY1, +:CObject0,:CObject1,:CObject2,:CObject3,:CObject4,:CObjectD, +:CModule0,:CModule1,:CModule2,:CModule3,:CModule4,:CModuleD, +:CClass0,:CClass1,:CClass2,:CClass3,:CClass4,:CClassD, +] + +for i in 0..classes.size-1 do + cls = classes[i] + d = [] + consts.each { |cst| + begin + cls.const_get(cst) + rescue + #puts "!#{names[i]}::#{cst}" + else + d << cst + end + } + puts "#{names[i]}: #{d.inspect}" +end + diff --git a/merlin/main/languages/ruby/Experimental/Singletons/def.rb b/merlin/main/languages/ruby/Experimental/Singletons/def.rb new file mode 100644 index 0000000000..18fbdb4ac3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/def.rb @@ -0,0 +1,344 @@ +module SMx + CONST_SMx = 1 + $SMx = self + + def i_SMx + :'i_SMx' + end + + def SMx.c_SMx + :'c_SMx' + end +end + + +module SM1 + CONST_SM1 = 1 + $SM1 = self + + def i_SM1 + :'i_SM1' + end + + def SM1.c_SM1 + :'c_SM1' + end +end + +class << $SM1 + CONST_SM1_1 = 1 + + $SM1_1 = self + + def i_SM1_1 + :'i_SM1_1' + end + + def self.c_SM1_1 + :'c_SM1_1' + end +end + +class D + CONST_D = 1 + $D = self + + def i_D + :'i_D' + end + + def self.c_D + :'c_D' + end +end + +class C < D + CONST_C = 1 + $C = self + + def i_C + :'i_C' + end + + def self.c_C + :'c_C' + end + +# def order +# puts "1.A" +# super +# end + +# def self.order +# puts "1.B" +# super +# end +end + +$X = C.new + +class << $X + CONST_Sx = 1 + + $Sx = self + + include SMx + + def i_Sx + :'i_Sx' + end + + def self.c_Sx + :'c_Sx' + end +end + +class << $Sx + CONST_Sx1 = 1 + + $Sx1 = self + + def i_Sx1 + :'i_Sx1' + end + + def self.c_Sx1 + :'c_Sx1' + end +end + +class << C + CONST_S1 = 1 + + $S1 = self + + include SM1 + + def i_S1 + :'i_S1' + end + + def self.c_S1 + :'c_S1' + end + +# def order +# puts "2.A" +# super +# end + +# def self.order +# puts "2.B" +# super +# end + + + # the method allocate is defined in each singleton class: + #undef allocate + #undef superclass +end + +class << $S1 + CONST_S2 = 1 + + $S2 = self + + def i_S2 + :'i_S2' + end + + def self.c_S2 + :'c_S2' + end + +end + +class << $S2 + CONST_S3 = 1 + + $S3 = self + + def i_S3 + :'i_S3' + end + + def self.c_S3 + :'c_S3' + end +end + +class << $S3 + CONST_S4 = 1 + + $S4 = self + + def i_S4 + :'i_S4' + end + + def self.c_S4 + :'c_S4' + end +end + +class << D + CONST_T1 = 1 + + $T1 = self + + def i_T1 + :'i_T1' + end + + def self.c_S1 + :'c_T1' + end + +# def order +# puts 3 +# super +# end +end + +class << $T1 + CONST_T2 = 1 + + $T2 = self + + def i_T2 + :'i_T2' + end + + def self.c_T2 + :'c_T2' + end +end + +class << $T2 + CONST_T3 = 1 + + $T3 = self + + def i_T3 + :'i_T3' + end + + def self.c_T3 + :'c_T3' + end +end + +class Object + CONST_Object = 1 + $Object = self + + def i_Object + :'i_Object' + end + + def self.c_Object + :'c_Object' + end +end + +class Module + CONST_Module = 1 + $Module = self + + def i_Module + :'i_Module' + end + + def self.c_Module + :'c_Module' + end +end + +class Class + CONST_Class = 1 + $Class = self + + def i_Class + :'i_Class' + end + + def self.c_Class + :'c_Class' + end +end + +class << Object + CONST_Object1 = 1 + + $Object1 = self + + def i_Object1 + :'i_Object1' + end + + def self.c_Object1 + :'c_Object1' + end + +# def order +# puts 4 +# super rescue puts $!.class +# end +end + +class << Module + CONST_Module1 = 1 + + $Module1 = self + + def i_Module1 + :'i_Module1' + end + + def self.c_Module1 + :'c_Module1' + end +end + +class << Class + CONST_Class1 = 1 + + $Class1 = self + + def i_Class1 + :'i_Class1' + end + + def self.c_Class1 + :'c_Class1' + end +end + +# aliases: + +d = [ +[$Sx, :ai_Sx, :i_Sx], +[$Sx1, :ai_Sx1, :i_Sx1], +[$C, :ai_C, :i_C], +[$S1, :ai_S1, :i_S1], +[$S2, :ai_S2, :i_S2], +[$S3, :ai_S3, :i_S3], +[$D, :ai_D, :i_D], +[$T1, :ai_T1, :i_T1], +[$T2, :ai_T2, :i_T2], +[$T3, :ai_T3, :i_T3], +[$Object, :ai_Object, :i_Object], +[$Object1, :ai_Object1, :i_Object1], +[$Module, :ai_Module, :i_Module], +[$Module1, :ai_Module1, :i_Module1], +[$Class, :ai_Class, :i_Class], +[$Class1, :ai_Class1, :i_Class1], +[$SMx, :ai_SMx, :i_SMx], +[$SM1, :ai_SM1, :i_SM1], +[$SM1_1, :ai_SM1_1, :i_SM1_1], +] + +d.each { |c, a, m| + c.send(:alias_method, a, m) +} + diff --git a/merlin/main/languages/ruby/Experimental/Singletons/dummy.rb b/merlin/main/languages/ruby/Experimental/Singletons/dummy.rb new file mode 100644 index 0000000000..63c01931c1 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/dummy.rb @@ -0,0 +1,55 @@ +$c = Object.new +i = 0 +all = [] +all_ids = [] +while i < 10 do + all << $c + all_ids << $c.object_id + + puts "#{i}: #{$c.object_id}" + + class << $c + $c = self + end + + i += 1 +end + +$c = all[1] +puts i, all_ids.uniq.size + +puts $c.object_id % 1000 +puts $c.superclass.object_id % 1000 +puts $c.superclass.superclass.object_id % 1000 +puts $c.superclass.superclass.superclass.object_id % 1000 +puts $c.superclass.superclass.superclass.superclass.object_id % 1000 +puts $c.superclass.superclass.superclass.superclass.superclass.object_id % 1000 +puts $c.superclass.superclass.superclass.superclass.superclass.superclass.object_id % 1000 +puts $c.superclass.superclass.superclass.superclass.superclass.superclass.superclass.object_id % 1000 +puts $c.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.object_id % 1000 +puts $c.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.object_id % 1000 +puts '---' +# one before dummy +$d = all[9] +# dummy +$e = $c.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass.superclass +puts $d.object_id % 1000 +puts $e.object_id % 1000 + +class << $e + $f = self +end + +puts '---1' +puts $d.object_id % 1000 +puts $e.object_id % 1000 +puts $f.object_id % 1000 +puts '---2' +puts $d.superclass.object_id % 1000 +puts $e.superclass.object_id % 1000 +puts $f.superclass.object_id % 1000 + +puts '---3' +class << $d + puts self.object_id % 1000 +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Singletons/dummy_supers.rb b/merlin/main/languages/ruby/Experimental/Singletons/dummy_supers.rb new file mode 100644 index 0000000000..8854499ca9 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/dummy_supers.rb @@ -0,0 +1,12 @@ +$C = Object.new + +class << $C + $C1 = self +end + +puts $C1.object_id % 1000 +puts $C1.superclass.object_id % 1000 +puts $C1.superclass.superclass.object_id % 1000 +puts $C1.superclass.superclass.superclass.object_id % 1000 +puts $C1.superclass.superclass.superclass.superclass.object_id % 1000 +puts $C1.superclass.superclass.superclass.superclass.superclass.object_id # end!!! diff --git a/merlin/main/languages/ruby/Experimental/Singletons/global.rb b/merlin/main/languages/ruby/Experimental/Singletons/global.rb new file mode 100644 index 0000000000..ff0d0fd19d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/global.rb @@ -0,0 +1,52 @@ +puts '-- global instance methods --' + +class << self + ::S0 = self +end + +puts 'ancestors:' +p S0.ancestors +puts 'instance:' +p S0.instance_methods(false) +puts 'private:' +p S0.private_methods(false) +puts 'protected:' +p S0.protected_methods(false) +puts 'singleton:' +p S0.singleton_methods(false) + +puts '-' * 20 + +module M1 + def foo + puts 'foo' + end +end + +module M2 + def bar + puts 'bar' + end +end + +# 'global' methods +puts 'global:' +class S0 + # undefines "include" instance method on main singleton + #undef include + + # calls Module::include + puts 'including' + p include(M1,M2) + p ancestors + p inherited(1) +end + +# tries to call "include" on main singleton and fails if it has been undefined +puts 'including' +p include(M1,M2) rescue puts $!.class + +foo +bar + +p Module.private_methods.include?("include") diff --git a/merlin/main/languages/ruby/Experimental/Singletons/lists.rb b/merlin/main/languages/ruby/Experimental/Singletons/lists.rb new file mode 100644 index 0000000000..5ed7e58d30 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/lists.rb @@ -0,0 +1,52 @@ +require 'def.rb' + +puts '------------------------ instance methods ------------------------' + +puts 'C:' +p C.instance_methods(false) +puts 'Sx:' +p Sx.instance_methods(false) +puts 'S1:' +p S1.instance_methods(false) +puts 'S2:' +p S2.instance_methods(false) +puts 'S3:' +p S3.instance_methods(false) +puts 'DUMMY:' +p DUMMY.instance_methods(false) +puts 'Class:' +p Class.instance_methods(false) + +puts '------------------------ class methods -----------------------------' + +puts 'C:' +p C.singleton_methods(false) +puts 'Sx:' +p Sx.singleton_methods(false) +puts 'S1:' +p S1.singleton_methods(false) +puts 'S2:' +p S2.singleton_methods(false) +puts 'S3:' +p S3.singleton_methods(false) +puts 'DUMMY:' +p DUMMY.singleton_methods(false) +puts 'Class:' +p Class.singleton_methods(false) + +puts '------------------------ inherited instance methods ------------------------' + +puts 'C:' +p C.instance_methods() - Object.instance_methods() +puts "Sx:" +p Sx.instance_methods() - Object.instance_methods() +puts "S1:" +p S1.instance_methods() - Object.instance_methods() +puts "S2:" +p S2.instance_methods() - Object.instance_methods() +puts "S3:" +p S3.instance_methods() - Object.instance_methods() +puts 'DUMMY:' +p DUMMY.instance_methods() - Object.instance_methods() +puts 'Class:' +p Class.instance_methods() - Object.instance_methods() diff --git a/merlin/main/languages/ruby/Experimental/Singletons/methods_singletons.rb b/merlin/main/languages/ruby/Experimental/Singletons/methods_singletons.rb new file mode 100644 index 0000000000..eec3ed7ba8 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/methods_singletons.rb @@ -0,0 +1,232 @@ +# == Object =========== + +class Object + $Object0 = self + def self.c_Object0; :c_Object0; end +end + +class << $Object0 + $Object1 = self + def self.c_Object1; :c_Object1; end +end + +class << $Object1 + $Object2 = self + def self.c_Object2; :c_Object2; end +end + +class << $Object2 + $Object3 = self + def self.c_Object3; :c_Object3; end +end + +class << $Object3 + $Object4 = self + def self.c_Object4; :c_Object4; end +end + +$ObjectD = $Object4.superclass + +# == Module =========== + +class Module + $Module0 = self + def self.c_Module0; :c_Module0; end +end + +class << $Module0 + $Module1 = self + def self.c_Module1; :c_Module1; end +end + +class << $Module1 + $Module2 = self + def self.c_Module2; :c_Module2; end +end + +class << $Module2 + $Module3 = self + def self.c_Module3; :c_Module3; end +end + +class << $Module3 + $Module4 = self + def self.c_Module4; :c_Module4; end +end + +$ModuleD = $Module4.superclass + +# == Class ============ + +class Class + $Class0 = self + def self.c_Class0; :c_Class0; end +end + +class << $Class0 + $Class1 = self + def self.c_Class1; :c_Class1; end +end + +class << $Class1 + $Class2 = self + def self.c_Class2; :c_Class2; end +end + +class << $Class2 + $Class3 = self + def self.c_Class3; :c_Class3; end +end + +class << $Class3 + $Class4 = self + def self.c_Class4; :c_Class4; end +end + +$ClassD = $Class4.superclass + +# == D ================ + +class D + $D0 = self + def self.c_D0; :c_D0; end +end + +class << $D0 + $D1 = self + def self.c_D1; :c_D1; end +end + +class << $D1 + $D2 = self + def self.c_D2; :c_D2; end +end + +class << $D2 + $D3 = self + def self.c_D3; :c_D3; end +end + +class << $D3 + $D4 = self + def self.c_D4; :c_D4; end +end + +$DD = $D4.superclass + +# == C ================ + +class C < D + $C0 = self + def self.c_C0; :c_C0; end +end + +class << $C0 + $C1 = self + def self.c_C1; :c_C1; end +end + +class << $C1 + $C2 = self + def self.c_C2; :c_C2; end +end + +class << $C2 + $C3 = self + def self.c_C3; :c_C3; end +end + +class << $C3 + $C4 = self + def self.c_C4; :c_C4; end +end + +$CD = $C4.superclass + +# == X ================ + +$X0 = C.new + +class << $X0 + $X1 = self + def self.c_X1; :c_X1; end +end + +class << $X1 + $X2 = self + def self.c_X2; :c_X2; end +end + +class << $X2 + $X3 = self + def self.c_X3; :c_X3; end +end + +class << $X3 + $X4 = self + def self.c_X4; :c_X4; end +end + +$XD = $X4.superclass + +# == Y ================ + +$Y0 = C.new + +class << $Y0 + $Y1 = self + def self.c_Y1; :c_Y1; end +end + +$YD = $Y1.superclass + +# ===================== + +classes = [ +$C0,$C1,$C2,$C3,$C4, +$D0,$D1,$D2,$D3,$D4, +$X0,$X1,$X2,$X3,$X4, +$Y0,$Y1, +$Object0,$Object1,$Object2,$Object3,$Object4, +$Module0,$Module1,$Module2,$Module3,$Module4, +$Class0,$Class1,$Class2,$Class3,$Class4, +] + +# IronRuby doesn't support member table sharing on dummies, so the results are different from MRI: +dummies = [$CD, $DD, $XD, $YD, $ClassD, $ModuleD, $ObjectD] + +names = [ +:C0,:C1,:C2,:C3,:C4, +:D0,:D1,:D2,:D3,:D4, +:X0,:X1,:X2,:X3,:X4, +:Y0,:Y1, +:Object0,:Object1,:Object2,:Object3,:Object4, +:Module0,:Module1,:Module2,:Module3,:Module4, +:Class0,:Class1,:Class2,:Class3,:Class4, +] + +methods = [ +:c_C0,:c_C1,:c_C2,:c_C3,:c_C4, +:c_D0,:c_D1,:c_D2,:c_D3,:c_D4, +:c_X1,:c_X2,:c_X3,:c_X4, +:c_Y1, +:c_Object0,:c_Object1,:c_Object2,:c_Object3,:c_Object4, +:c_Module0,:c_Module1,:c_Module2,:c_Module3,:c_Module4, +:c_Class0,:c_Class1,:c_Class2,:c_Class3,:c_Class4, +] + +for i in 0..classes.size-1 do + cls = classes[i] + d = [] + methods.each { |m| + begin + cls.public_class_method(m) + rescue + #puts "!#{names[i]}::#{m}" + else + d << m + end + } + puts "#{names[i]}: #{d.inspect}" +end + diff --git a/merlin/main/languages/ruby/Experimental/Singletons/obj_prot.rb b/merlin/main/languages/ruby/Experimental/Singletons/obj_prot.rb new file mode 100644 index 0000000000..78da9ce5e8 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/obj_prot.rb @@ -0,0 +1,14 @@ +class C +end + +x = C.new + +class << x + ::OP = self +end + +p OP.instance_methods(false) +p OP.private_methods(false) +p OP.protected_methods(false) +p OP.singleton_methods(false) +p OP.ancestors diff --git a/merlin/main/languages/ruby/Experimental/Singletons/order.rb b/merlin/main/languages/ruby/Experimental/Singletons/order.rb new file mode 100644 index 0000000000..77589b176b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/order.rb @@ -0,0 +1,6 @@ +#["i_S1", "c_C", "i_O1", "superclass", "new", "i_T1"] + +require 'def.rb' + +C.order + diff --git a/merlin/main/languages/ruby/Experimental/Singletons/supers.rb b/merlin/main/languages/ruby/Experimental/Singletons/supers.rb new file mode 100644 index 0000000000..0d5c434829 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/supers.rb @@ -0,0 +1,37 @@ +require 'def.rb' + +def d x + puts "#{x.object_id % 1000}: #{x.inspect}, name = '#{x.name}', id = #{x.object_id % 1000}, ancestors = #{x.ancestors.inspect}" + s = x.superclass + return if s == nil + puts "#{x.object_id % 1000}.super: #{s.inspect}, name = '#{s.name}', id = #{s.object_id % 1000}" + s = s.superclass + return if s == nil + puts "#{x.object_id % 1000}.super.super: #{s.inspect}, name = '#{s.name}', id = #{s.object_id % 1000}" + s = s.superclass + return if s == nil + puts "#{x.object_id % 1000}.super.super.super: #{s.inspect}, name = '#{s.name}', id = #{s.object_id % 1000}" + s = s.superclass + return if s == nil + puts "#{x.object_id % 1000}.super.super.super.super: #{s.inspect}, name = '#{s.name}', id = #{s.object_id % 1000}" +end + +puts '-- superclasses of C --' +d $C +puts '-- superclasses of Sx --' +d $Sx +puts '-- superclasses of Sx1 --' +d $Sx1 +puts '-- superclasses of S1 --' +d $S1 +puts '-- superclasses of S2 --' +d $S2 +puts '-- superclasses of S3 --' +d $S3 +puts '-- superclasses of S4 --' +d $S4 +puts '-- superclasses of T1 --' +d $T1 +puts '-- superclasses of T2 --' +d $T2 + diff --git a/merlin/main/languages/ruby/Experimental/Singletons/to_s.rb b/merlin/main/languages/ruby/Experimental/Singletons/to_s.rb new file mode 100644 index 0000000000..e550bcf096 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Singletons/to_s.rb @@ -0,0 +1,60 @@ +class C +end + +p x = C.new +class << x + def to_s + 'XXX' + end + + class << self + p self + end +end + +puts '----' + +class << C + p self + class << self + p self + end +end + +puts '----' + +module M +end + +class << M + p self + class << self + p self + end +end + + +puts '---' + +class Module + def to_s + 'goo' + end +end + +class << Class + def to_s + 'hoo' + end +end + +p Class +p C +p M +class << M + p self + class << self + p self + end +end + diff --git a/merlin/main/languages/ruby/Experimental/Strings/NewSubclass.rb b/merlin/main/languages/ruby/Experimental/Strings/NewSubclass.rb new file mode 100644 index 0000000000..e93b8fbef7 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Strings/NewSubclass.rb @@ -0,0 +1,24 @@ +class Class + alias old_new new + alias old_allocate allocate + + def new *args, &x + puts "new #{args[0]}" + old_new(*args, &x) + end + + def allocate *args, &x + puts "allocate #{args[0]}" + old_allocate(*args, &x) + end +end + +class S < String +end + +x = S.new("foo") + +p x.dump.class +p x.downcase.class +p x.upcase.class +p x.center(10).class \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Strings/Quotations.rb b/merlin/main/languages/ruby/Experimental/Strings/Quotations.rb new file mode 100644 index 0000000000..87bdf969b2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Strings/Quotations.rb @@ -0,0 +1,16 @@ +def try_eval str + puts '-'*20 + puts str + p eval str +rescue SyntaxError + puts '-'*20 + p $! +end + +#terminators must not be alphanumeric: +try_eval '%! !' +try_eval '%__' +try_eval '%_foo bar_' + +#alphanumeric terminators: + diff --git a/merlin/main/languages/ruby/Experimental/Strings/Reverse.rb b/merlin/main/languages/ruby/Experimental/Strings/Reverse.rb new file mode 100644 index 0000000000..be3d5aff15 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Strings/Reverse.rb @@ -0,0 +1,35 @@ +class String + alias old_dup dup + + def dup + puts 'duped' + old_dup + end +end + +class S < String +end + +["", "a", "ab", "abc", "abcd"].each { |s| + puts "-- '#{s}' -- " + + p s.reverse + p s + p s.reverse! + p s +} + +puts '---' + +s = S.new("xyz") +s.taint +s.freeze + +r = s.reverse + +p r.class +p r.tainted? +p r.frozen? + +s.reverse! rescue puts $! + diff --git a/merlin/main/languages/ruby/Experimental/Strings/pack.rb b/merlin/main/languages/ruby/Experimental/Strings/pack.rb new file mode 100644 index 0000000000..7b46b3fbc4 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Strings/pack.rb @@ -0,0 +1,48 @@ +p [61].pack("h") rescue p $! +p [nil].pack("H*") + +p ["616263"].pack("H*") + +p [""].pack("H*") +p ["6"].pack("H*") +p ["60"].pack("H*") +p ["61"].pack("H*") +p ["616"].pack("H*") +p ["6160"].pack("H*") +p ["6162"].pack("H*") + +puts '---' + +p [""].pack("h*") +p ["6"].pack("h*") +p ["06"].pack("h*") +p ["16"].pack("h*") +p ["161"].pack("h*") +p ["1606"].pack("h*") +p ["1626"].pack("h*") + +puts '---' + + +p ["6162"].pack("H0") +p ["6162"].pack("H1") +p ["6162"].pack("H2") +p ["6162"].pack("H3") +p ["6162"].pack("H4") +p ["6162"].pack("H5") +p ["6162"].pack("H6") +p ["6162"].pack("H7") +p ["6162"].pack("H8") +p ["6162"].pack("H20") + +puts '---' + +(0..255).each { |b| + puts "#{b.chr.inspect} -> #{[b.chr].pack("h1")[0]}" rescue p $! +} + + +p u = 239.chr.unpack("H*") +p u.pack("H*")[0] + + diff --git a/merlin/main/languages/ruby/Experimental/Strings/pack_integers.rb b/merlin/main/languages/ruby/Experimental/Strings/pack_integers.rb new file mode 100644 index 0000000000..5d3bbe9e8d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Strings/pack_integers.rb @@ -0,0 +1,42 @@ +[ +"l", "L", +"i", "I", +"s", "S", +"n", "N", +"v", "V", +"c", "C", +].each { |f| + +puts "-- #{f} --" +p [0xffffffff].pack(f) rescue p $! +p [0x100000000].pack(f) rescue p $! +p [-1].pack(f) rescue p $! + +} + +puts "========================" + +["q", "Q"].each { |f| + +puts "-- #{f} --" +p [0xffffffff].pack(f) rescue p $! +p [0x100000000].pack(f) rescue p $! +p [0xffffffffffffffff].pack(f) rescue p $! +p [0x10000000000000000].pack(f) rescue p $! +p [-1].pack(f) rescue p $! + +} + +puts "========================" + +["l", "L", +"i", "I", +"s", "S", +"n", "N", +"v", "V", +"c", "C", +].each { |f| + puts "-- #{f} --" + p [0x12345678].pack(f).unpack("H*") rescue p $! + p [0x1234].pack(f).unpack("H*") rescue p $! +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Strings/sub_scan_regex.rb b/merlin/main/languages/ruby/Experimental/Strings/sub_scan_regex.rb new file mode 100644 index 0000000000..73aaab50fb --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Strings/sub_scan_regex.rb @@ -0,0 +1,27 @@ +def m + $p = lambda do + p $1 + end + + "ab".sub(/(.)/) do + $p[] + p $1 + "x".match(/(.)/) + p $1 + end + + p $1 + + "12".scan(/(.)/) do + "y".match(/(.)/) + end + p $1 + + "12".scan(/(.)/) do + "y".match(/(.)/) + break + end + p $1 +end + +m \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Strings/to_s.rb b/merlin/main/languages/ruby/Experimental/Strings/to_s.rb new file mode 100644 index 0000000000..c8200cfeab --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Strings/to_s.rb @@ -0,0 +1,23 @@ + +(0..255).each { |o| + puts o.chr.to_s +} + +puts '---' + +(0..255).each { |o| + puts o.chr.inspect +} + +puts '---' + +(0..255).each { |o| + s = "##{o.chr}" + if s.inspect[0..2] == '"\\#' then + puts s.inspect + end + + if s.to_s[0..1] == '\\#' then + puts s.to_s + end +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Strings/unpack.rb b/merlin/main/languages/ruby/Experimental/Strings/unpack.rb new file mode 100644 index 0000000000..c608b0e471 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Strings/unpack.rb @@ -0,0 +1,33 @@ + +p "abc".unpack("h") +p "abc".unpack("h0") +p "abc".unpack("h1") +p "abc".unpack("h2") +p "abc".unpack("h3") +p "abc".unpack("h4") +p "abc".unpack("h5") +p "abc".unpack("h6") +p "abc".unpack("h7") + +puts '---' + +p "abc".unpack("H") +p "abc".unpack("H0") +p "abc".unpack("H1") +p "abc".unpack("H2") +p "abc".unpack("H3") +p "abc".unpack("H4") +p "abc".unpack("H5") +p "abc".unpack("H6") +p "abc".unpack("H7") + +puts '---' + +p "\xef".unpack("H7") +p "\xef".unpack("h7") + +puts '---' + +p "abcd".unpack("H*") +p "abcd".unpack("H*") + diff --git a/merlin/main/languages/ruby/Experimental/Strings/whitespace.rb b/merlin/main/languages/ruby/Experimental/Strings/whitespace.rb new file mode 100644 index 0000000000..b39f5594c0 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Strings/whitespace.rb @@ -0,0 +1,23 @@ +str = " "*255 +255.times { |x| str[x] = x } + +255.times { |x| + s = str.lstrip + if s != str + puts x + end + str = str[1..-1] +} + +puts '---' + +str = " "*255 +255.times { |x| str[x] = x } + +255.times { |x| + s = str.rstrip + if s != str + puts 254-x + end + str = str[0..-2] +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Strings/whitespace2.rb b/merlin/main/languages/ruby/Experimental/Strings/whitespace2.rb new file mode 100644 index 0000000000..374351db6c --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Strings/whitespace2.rb @@ -0,0 +1,9 @@ +require 'mscorlib' +0xffff.times { |i| + c = System::Convert.ToChar(i) + if System::Char.IsWhiteSpace(c) + puts System::Text::Encoding.UTF8.GetBytes("foo".to_clr_string) + end +} + + diff --git a/merlin/main/languages/ruby/Experimental/Strings/whitespace3.rb b/merlin/main/languages/ruby/Experimental/Strings/whitespace3.rb new file mode 100644 index 0000000000..720f043a50 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Strings/whitespace3.rb @@ -0,0 +1,9 @@ +# coding:UTF-8 + +p __ENCODING__ + +p " foo ".strip + +a = "\xe1\x9a\x80foo\000" +p a.encoding +p a.strip \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Struct/struct1.rb b/merlin/main/languages/ruby/Experimental/Struct/struct1.rb new file mode 100644 index 0000000000..a8f3b06ad3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Struct/struct1.rb @@ -0,0 +1,53 @@ +class C + def to_str + "CCC" + end +end + +class D + def to_sym + :DDD + end +end + +puts '-- anonymous --' + +p Struct.new(:Foo)["f"] +p Struct.new(:Foo, :bar)["f", "g"] +p Struct.new(nil, :bar)["f"] +p Struct.new(:X.to_i)["f"] +p Struct.new(:X.to_i, :bar)["f", "g"] + +puts '-- named --' + +p Struct.new("Foo") +p Struct.new("Foo", :Foo)["f"] +p Struct.new("Foo", nil) rescue p $! +p Struct.new(C.new, :Foo)["f"] +p Struct.new(D.new, :Foo)["f"] rescue p $! + +puts '-- on derived struct --' + +class S1 < Struct +end + +class S < S1 +end + +p S.new(:Foo)["f"] + +p S.new(:Foo, :bar)["f", "g"] +p S.new(nil, :bar)["f"] +p S.new(:X.to_i)["f"] +p S.new(:X.to_i, :bar)["f", "g"] + +p S.new("Foo") +p S.new("Foo", :Foo)["f"] +p S.new("Foo", nil) rescue p $! +p S.new(C.new, :Foo)["f"] + +puts '-' * 20 + +Customer = Struct.new(:name) +p Customer["joe"].class +p Customer["joe"].class.superclass diff --git a/merlin/main/languages/ruby/Experimental/Struct/struct2.rb b/merlin/main/languages/ruby/Experimental/Struct/struct2.rb new file mode 100644 index 0000000000..4e72d0b393 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Struct/struct2.rb @@ -0,0 +1,107 @@ +p Struct.singleton_methods(false) + +class Class + alias :old_new :new + + def new *a + puts "Class#new: #{self}.new(#{a.inspect})" + begin + x = old_new *a + rescue + puts "!" + p $! + end + x + end +end + +class Struct + alias :init :initialize + + def initialize *a + puts "Struct#initialize: #{a.inspect}" + puts " #{self.inspect}" + init *a + end + + #class << self + # remove_method :new + #end +end + +class S < Struct + def initialize *a + puts "S#initialize: #{a.inspect}" + puts " #{self.inspect}" + super + end +end + +p Struct.singleton_methods(false) +p Class.instance_methods(false) + +begin + + puts '# no initializer called yet' + I = S.new(:foo, :bar) + + class I + p superclass + p singleton_methods(false) + p instance_methods(false) + p private_instance_methods(false) + + class << self + alias :old_new :new + + def new *a + puts "I#new: #{self}.new(#{a.inspect})" + old_new *a + end + + def [] *a + puts "I#[]: #{self}.[](#{a.inspect})" + old_new *a + end + end + + def initialize *a + puts "I#initialize: #{a.inspect}" + puts " #{self.inspect}" + super + end + end + + puts "---" + puts '# now it calls initializer (via new)' + p I.new("x", "y") + + puts "---" + puts '# now it calls initializer (via [])' + p I["x", "y"] + + puts "---" + puts '# remove new methods' + + class I + class << self + remove_method :new + end + end + + p I.new("U", "V") # calls Struct#new + puts '---' + + class Struct + class << self + remove_method :new + end + end + + p I.new("U", "V") # creates the struct instance :) + puts '---' + +rescue + p $! +end + diff --git a/merlin/main/languages/ruby/Experimental/Struct/struct3.rb b/merlin/main/languages/ruby/Experimental/Struct/struct3.rb new file mode 100644 index 0000000000..4ff63262f1 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Struct/struct3.rb @@ -0,0 +1,11 @@ +class Struct + def i *a + initialize *a + end +end + +x = Struct.new(:foo, &lambda { puts 'foo' }).new("foo") + +p x +x.i("bar") +p x diff --git a/merlin/main/languages/ruby/Experimental/Struct/struct4.rb b/merlin/main/languages/ruby/Experimental/Struct/struct4.rb new file mode 100644 index 0000000000..9da7b48857 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Struct/struct4.rb @@ -0,0 +1,38 @@ +def dump_class x + puts "-- #{x.name}" + p x.instance_methods(false).sort + p x.singleton_methods(false).sort + p x.ancestors + puts +end + +dump_class Struct + +StructC = Struct.new(:foo) +dump_class StructC + +C = Class.new(StructC) +dump_class C + +puts "-- C.new" +p C.new +puts + +# remove new() on StructC's singleton: +class StructC + class << self + remove_method :new + end +end + +# create a structure that derives from an existing one: +StructD = StructC.new(:xxx, :yyy) + +dump_class StructD + +D = Class.new(StructD) +dump_class D + +puts "-- D" +p D.new +puts diff --git a/merlin/main/languages/ruby/Experimental/Struct/struct_dup.rb b/merlin/main/languages/ruby/Experimental/Struct/struct_dup.rb new file mode 100644 index 0000000000..fb46783713 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Struct/struct_dup.rb @@ -0,0 +1,17 @@ +p S = Struct.new(:foo) +p S.dup +p S.hash == S.dup.hash + +puts '---' + +p iS = S['f'] +p diS = iS.dup +p iS.hash == diS.hash + +puts '---' + +p iS = S['f'] +p cis = iS.clone +p iS.hash == cis.hash + + diff --git a/merlin/main/languages/ruby/Experimental/Super/Alias.rb b/merlin/main/languages/ruby/Experimental/Super/Alias.rb new file mode 100644 index 0000000000..e4bb8458b8 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Super/Alias.rb @@ -0,0 +1,21 @@ +class C + def f + puts 'C.f' + end + + def g + puts 'C.g' + end +end + +class D < C + def f + puts 'D.f' + super + end + + alias g f +end + +p D.instance_methods(false) +D.new.g \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Super/Scoping.rb b/merlin/main/languages/ruby/Experimental/Super/Scoping.rb new file mode 100644 index 0000000000..67227a4722 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Super/Scoping.rb @@ -0,0 +1,94 @@ +class D + def foo(a,b) + puts "D::foo(#{a},#{b})" + end + + def goo(a,b) + puts "D::goo(#{a},#{b})" + end + + def hoo(a,b) + puts "D::hoo(#{a},#{b})" + end + + def ioo(a,b) + puts "D::ioo(#{a},#{b})" + end + + def joo(a,b) + puts "D::joo(#{a},#{b})" + end + + def super_joo(a,b) + puts "D::joo(#{a},#{b})" + end + + def koo(a,b) + puts "D::koo(#{a},#{b})" + end +end + +class C < D + def foo(a,b) + puts "C::foo(#{a},#{b})" + super + end + + define_method(:goo) { |a,b| + puts "C::goo(#{a},#{b})" + super + } + + def hoo(a,b) + puts "C::hoo(#{a},#{b})" + yoo(5,6) { |x,y| super } + end + + def yoo(a,b) + yield a,b + end + + def ioo(a,b) + puts "C::ioo(#{a},#{b})" + p = koo { |x, y| + if $emulate + if $s.nil? then super(a,b) else send(:"super_#{$s}", x, y) end + else + super + end + } + C::yoo2(5,6,&p) + end + + def koo &p + p + end + + def self.yoo2(a,b,&p) + if $emulate + $p = p + def joo(x,y) + $s = :joo + $p.call(x,y) + end + else + define_method(:joo, &p) + end + end +end + +c = C.new +c.foo(1,2) +puts '---' +c.goo(1,2) +puts '---' +c.hoo(1,2) +puts '---' + +$emulate = false +c.ioo(10,20) +c.joo(100,200) + +$emulate = true +c.ioo(10,20) +c.joo(100,200) diff --git a/merlin/main/languages/ruby/Experimental/Super/Scoping2.rb b/merlin/main/languages/ruby/Experimental/Super/Scoping2.rb new file mode 100644 index 0000000000..f9bbe5d166 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Super/Scoping2.rb @@ -0,0 +1,27 @@ +class C + def foo + puts 'C.foo' + end +end + +module M + def foo + puts 'D.foo' + + eval <<-END + class E + def foo + puts 'E.foo' + end + super + end + END + end +end + +class D < C + include M +end + +d = D.new +d.foo diff --git a/merlin/main/languages/ruby/Experimental/Super/Scoping3.rb b/merlin/main/languages/ruby/Experimental/Super/Scoping3.rb new file mode 100644 index 0000000000..13a9aedf09 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Super/Scoping3.rb @@ -0,0 +1,81 @@ +=begin + +Shows that super is not bound to the the current lexical method scope. + +=end + +def y &p; yield; p; end + +class C + def f *a + puts "C.f #{a.inspect}" + end + + def e *a + puts "C.e #{a.inspect}" + end +end + +class E + def g *a + puts "E.g #{a.inspect}" + end +end + +class A + def h *a + puts "A.h #{a.inspect}" + end +end + +class F < E + def g *a + puts "F.g #{a.inspect}" + + 1.times { + super + $p = y { + 1.times { + super + y &$q + } + } + } + + y &$p + end +end + +class B < A + def h *a + puts "B.h #{a.inspect}" + $q = y { + super + } + end +end + +B.new.h 1 + +puts '--' + +F.new.g 2 + +puts '--' + +class D < C + define_method :f, &$p + + def e *a + puts "D.e #{a.inspect}" + y &$p + end +end + +D.new.f(3) + +puts '--' + +D.new.e(4) + + diff --git a/merlin/main/languages/ruby/Experimental/Super/Scoping4.rb b/merlin/main/languages/ruby/Experimental/Super/Scoping4.rb new file mode 100644 index 0000000000..8bbd557aed --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Super/Scoping4.rb @@ -0,0 +1,27 @@ +class A + def foo + puts 'A::foo' + end +end + +class B < A + def foo + puts 'B::foo' + end +end + +class C < B + def foo + A.module_eval { + super + } + A.new.instance_eval { + super + } + end +end + + +C.new.foo + + diff --git a/merlin/main/languages/ruby/Experimental/Symbols/inspect.rb b/merlin/main/languages/ruby/Experimental/Symbols/inspect.rb new file mode 100644 index 0000000000..8c28803ee4 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Symbols/inspect.rb @@ -0,0 +1,30 @@ +[ +:|, :^, :&, :==, :===, :=~, :>, :<, :>=, :<=, :<<, :>>, :+, :-, :*, :/, :%, :**, :~, :+@, :-@, :[], :[]=, :`, +:_, :idf, :idf_0, :nil, :true, :false, :while, +:C, :@x, :@@x, :"@#{'@'}y", :$x, +:$`, :$0, :$1123, :$+, :$~, :$+, :$`, :$:, :$", :$$, :$=, :$-D, :$-0, + +:'$0X', +:'$1X', +:'$--', +:'$-&^%*@#@-', :"=>", +].each { |s| p s } + + +begin + eval(':""') +rescue Exception + p $! +end + +begin + eval(":''") +rescue Exception + p $! +end + +begin + p eval(':"#{}"') +rescue Exception + p $! +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Symbols/instance_variables.rb b/merlin/main/languages/ruby/Experimental/Symbols/instance_variables.rb new file mode 100644 index 0000000000..4017591314 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Symbols/instance_variables.rb @@ -0,0 +1,33 @@ +require 'mock' + +class C + def test_iv name + instance_variable_set(name, 0) rescue p $! + instance_variable_get(name) rescue p $! + instance_variable_defined?(name) rescue p $! + remove_instance_variable(name) rescue p $! + end + + def foo + end +end + +class SSym < String + def respond_to? name + puts "?Sym #{name}" + super + end +end + +[ + Sym.new(:@foo), + SSym.new("@foo"), + :@foo, + :@foo.to_i, + Sym.new(:@foo, :@foo), + 1111111111111111111111111111, + 1.23 +].each { |value| + C.new.test_iv value + puts '---' +} diff --git a/merlin/main/languages/ruby/Experimental/Symbols/mock.rb b/merlin/main/languages/ruby/Experimental/Symbols/mock.rb new file mode 100644 index 0000000000..e1a0626841 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Symbols/mock.rb @@ -0,0 +1,80 @@ +class I + def initialize val + @val = val + end + + def to_int + puts '!I to_int' + @val + end + + def respond_to? name + puts "?I #{name}" + super + end +end + +class S + def initialize val + @val = val + end + + def respond_to? name + puts "?S #{name}" + super + end + + def to_str + puts '!S to_str' + @val + end +end + +class Sym + def initialize sym, str = sym.to_s + @str = str + @sym = sym + end + + def respond_to? name + puts "?Sym #{name}" + super + end + + def to_str + puts '!Sym to_str' + @str + end + + def to_sym + puts '!Sym to_sym' + @sym + end +end + + +class ES + def initialize val + @val = val + end + + def respond_to? name + puts "?ES #{name}" + super + end + + def to_s + puts '!ES to_s' + @val + end +end + +class W + def write x + puts ">#{x}<" + end +end + +class MyStr < String + +end diff --git a/merlin/main/languages/ruby/Experimental/Thread/CV.rb b/merlin/main/languages/ruby/Experimental/Thread/CV.rb new file mode 100644 index 0000000000..8c59597e82 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Thread/CV.rb @@ -0,0 +1,33 @@ +require 'thread' unless defined? IRONRUBY_VERSION + +mutex = Mutex.new +resource = ConditionVariable.new + +i = 0 + +a = Thread.new { + 50.times do + mutex.synchronize { + resource.wait(mutex) while i == 0 + puts "C: #{i}" + i -= 1 + } + + sleep(0.15) + end +} + +b = Thread.new { + 50.times do + mutex.synchronize { + i += 1 + puts "P: #{i}" + resource.broadcast + } + + sleep(0.1) + end +} + +a.join +b.join \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Thread/Mutex.rb b/merlin/main/languages/ruby/Experimental/Thread/Mutex.rb new file mode 100644 index 0000000000..0cd0b13541 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Thread/Mutex.rb @@ -0,0 +1,40 @@ +p $: +require 'thread' unless defined? IRONRUBY_VERSION + +m = Mutex.new + +x = m.synchronize { |*args| + p args + p self + # error -> yield semantics: return 2121 + break 'x' + #123 +} + +p x +puts '---' +p m.try_lock +p m.try_lock +p m.try_lock +p m.try_lock +puts '---' +p m.unlock +p m.lock +puts '---' + +t1 = Thread.new { + p m.unlock unless defined? IRONRUBY_VERSION +} + +t1.join + +puts '---' +p m.lock +p m.locked? + +# non-recursive +p m.lock if defined? IRONRUBY_VERSION + + + + diff --git a/merlin/main/languages/ruby/Experimental/Thread/ProdCons.rb b/merlin/main/languages/ruby/Experimental/Thread/ProdCons.rb new file mode 100644 index 0000000000..792341a39a --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Thread/ProdCons.rb @@ -0,0 +1,21 @@ +require 'thread' unless defined? IRONRUBY_VERSION + +queue = Queue.new + +producer = Thread.new do + 5.times do |i| + sleep i/2 # simulate expense + queue << i + puts "#{i} produced" + end +end + +consumer = Thread.new do + 5.times do |i| + value = queue.pop + sleep i/2 # simulate expense + puts "consumed #{value}" + end +end + +consumer.join \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Thread/Queue.rb b/merlin/main/languages/ruby/Experimental/Thread/Queue.rb new file mode 100644 index 0000000000..3004301f7d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Thread/Queue.rb @@ -0,0 +1,89 @@ +require 'thread' unless defined? IRONRUBY_VERSION + +q = Queue.new +p q.enq(1) +p q.push(2) +p q << 3 + +p q.size +puts '---' + +p q.deq +p q.pop +p q.shift + +p q.size +p q.enq 1 +p q.clear +p q.empty? + +puts '---' + +class Queue + p private_instance_methods(false).include? "initialize" + p ancestors +end + +puts '---' + +class C + def to_int + 5 + end +end + +q = SizedQueue.new C.new +5.times { |i| q.enq i } + +# deadlock: q.enq 'x' + +s = Queue.new +q = Queue.new +t = Thread.new { s.enq nil; q.deq nil; puts 'randezvous'; s.enq nil; q.deq; } +sleep(1) +s.deq +q.enq nil +puts 'randezvous' +sleep(1) +s.deq +q.enq nil +t.join + +class SizedQueue + p ancestors + #p methods(false) + p singleton_methods(false) + p public_instance_methods(false) + p private_instance_methods(false) + p protected_instance_methods(false) +end + +class Q < SizedQueue + def initialize a + p super a + end + + def init a + initialize a + end +end + +q = Q.new 4 +q.enq 1 +q.enq 2 +q.init 6 +p q.size +p q.deq +p q.deq + +puts '--- max ---' + +p q.max = C.new +p q.max + +puts '---' + +#10.times { |i| print i; q.enq i } +#puts + + diff --git a/merlin/main/languages/ruby/Experimental/Thread/abort.rb b/merlin/main/languages/ruby/Experimental/Thread/abort.rb new file mode 100644 index 0000000000..6807d62399 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Thread/abort.rb @@ -0,0 +1,17 @@ +t = Thread.start { + puts 'start' + while true + puts 'tick' + sleep(0.6) + end +} +sleep(0.3) +p t + +p t.kill +p t.kill +p t.kill +p t.kill +p t.kill +p t.kill + diff --git a/merlin/main/languages/ruby/Experimental/Thread/rand.rb b/merlin/main/languages/ruby/Experimental/Thread/rand.rb new file mode 100644 index 0000000000..48700a1531 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Thread/rand.rb @@ -0,0 +1,9 @@ +class C + def to_int + 10000000000000000000000000000000000000000000000000000000000000000000000000000000 + end +end +5.times { p rand(100) } +5.times { p rand() } +5.times { p rand(-1000000000000000000000000000000000000000000000000) } +5.times { p rand(C.new) } diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/Ampersand.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/Ampersand.rb new file mode 100644 index 0000000000..a08d937fda --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/Ampersand.rb @@ -0,0 +1,14 @@ +begin + +foo & bar # warning: useless use of & in void context +foo &bar # warning: `&' interpreted as argument prefix +foo&bar # warning: useless use of & in void context +foo& bar # warning: useless use of & in void context +x = foo & bar +x = foo &bar # warning: `&' interpreted as argument prefix +x = foo&bar +x = foo& bar + +rescue + +end diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/Identifiers.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/Identifiers.rb new file mode 100644 index 0000000000..f246d30f30 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/Identifiers.rb @@ -0,0 +1,20 @@ +def try_eval str + puts str + eval(str) +rescue SyntaxError + p $! +end + +try_eval 'def foo?; end' +try_eval 'def foo?=; end' +try_eval 'def foo!; end' +try_eval 'def foo!=; end' + +def foo? *a +end + +def foo! *a +end + +try_eval 'foo?1' +#try_eval 'foo!=1' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/dot.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/dot.rb new file mode 100644 index 0000000000..ee85c37add --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/dot.rb @@ -0,0 +1 @@ +1. \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/dot_exp.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/dot_exp.rb new file mode 100644 index 0000000000..5e284f0e19 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/dot_exp.rb @@ -0,0 +1 @@ +0.e1 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/dot_under.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/dot_under.rb new file mode 100644 index 0000000000..42bf911524 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/dot_under.rb @@ -0,0 +1 @@ +0._1 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/double_under.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/double_under.rb new file mode 100644 index 0000000000..7ff8b1ed62 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/double_under.rb @@ -0,0 +1 @@ +1_2.4_5e2_2 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/exponent_out_of_range.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/exponent_out_of_range.rb new file mode 100644 index 0000000000..7f03d03880 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/exponent_out_of_range.rb @@ -0,0 +1,23 @@ +1.1e-10 +1.1e-100 +1.1e-1000 +1.1e-1021 +1.1e-1022 +1.1e-1023 +1.1e-1024 +1.1e-1025 +1.1e-1026 +1.1e-10000 +1.1e-100000 + +1.1e10 +1.1e100 +1.1e1000 +1.1e1021 +1.1e1022 +1.1e1023 +1.1e1024 +1.1e1025 +1.1e1026 +1.1e10000 +1.1e100000 diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/illegal_octal.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/illegal_octal.rb new file mode 100644 index 0000000000..abecac114b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/illegal_octal.rb @@ -0,0 +1 @@ +08888 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/no_dot_float1.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/no_dot_float1.rb new file mode 100644 index 0000000000..39290f76ce --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/no_dot_float1.rb @@ -0,0 +1 @@ +0000.0000 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/no_dot_float2.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/no_dot_float2.rb new file mode 100644 index 0000000000..d37d474537 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/no_dot_float2.rb @@ -0,0 +1 @@ +.123 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/numeric_literal_without_digits1.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/numeric_literal_without_digits1.rb new file mode 100644 index 0000000000..5a6a27d6ce --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/numeric_literal_without_digits1.rb @@ -0,0 +1 @@ +0x_ \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/numeric_literal_without_digits2.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/numeric_literal_without_digits2.rb new file mode 100644 index 0000000000..d312061472 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/numeric_literal_without_digits2.rb @@ -0,0 +1 @@ +0x_1 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/numeric_literal_without_digits3.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/numeric_literal_without_digits3.rb new file mode 100644 index 0000000000..99e07d0f06 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/numeric_literal_without_digits3.rb @@ -0,0 +1 @@ +0x \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/numeric_literal_without_digits4.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/numeric_literal_without_digits4.rb new file mode 100644 index 0000000000..5a6a27d6ce --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/numeric_literal_without_digits4.rb @@ -0,0 +1 @@ +0x_ \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/octal_error1.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/octal_error1.rb new file mode 100644 index 0000000000..7d4defd631 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/octal_error1.rb @@ -0,0 +1 @@ +08 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_dot.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_dot.rb new file mode 100644 index 0000000000..5afd512fc2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_dot.rb @@ -0,0 +1 @@ +122312. 1212 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_e.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_e.rb new file mode 100644 index 0000000000..e003236932 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_e.rb @@ -0,0 +1 @@ +0e diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_e2.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_e2.rb new file mode 100644 index 0000000000..3fdb066ef0 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_e2.rb @@ -0,0 +1 @@ +1.2e \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number.rb new file mode 100644 index 0000000000..fb4559146e --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number.rb @@ -0,0 +1 @@ +0_ \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number2.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number2.rb new file mode 100644 index 0000000000..07ab512dde --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number2.rb @@ -0,0 +1 @@ +0_x \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number4.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number4.rb new file mode 100644 index 0000000000..5c9c78f0f2 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number4.rb @@ -0,0 +1 @@ +0_8 diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number5.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number5.rb new file mode 100644 index 0000000000..416227d272 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number5.rb @@ -0,0 +1 @@ +123_.123 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number6.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number6.rb new file mode 100644 index 0000000000..b42ce8791b --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/trailing_under_in_number6.rb @@ -0,0 +1 @@ +12__1212 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/two_zeros_double.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/two_zeros_double.rb new file mode 100644 index 0000000000..af954a0f2d --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/two_zeros_double.rb @@ -0,0 +1 @@ +00e123 diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/zero_one_double.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/zero_one_double.rb new file mode 100644 index 0000000000..3ad7c89e99 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/zero_one_double.rb @@ -0,0 +1 @@ +01234e12 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/zero_one_double2.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/zero_one_double2.rb new file mode 100644 index 0000000000..c5399af1ff --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/zero_one_double2.rb @@ -0,0 +1 @@ +01.20 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Tokenizer/zeroes.rb b/merlin/main/languages/ruby/Experimental/Tokenizer/zeroes.rb new file mode 100644 index 0000000000..c763a09599 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Tokenizer/zeroes.rb @@ -0,0 +1,7 @@ +0 +00 +000000000000000000000000000000 +0e0 +0e0000 +0e1 +0e-000 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Unicode/ascii_unicode_escape.rb b/merlin/main/languages/ruby/Experimental/Unicode/ascii_unicode_escape.rb new file mode 100644 index 0000000000..6452a85dc5 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Unicode/ascii_unicode_escape.rb @@ -0,0 +1,5 @@ +#encoding:ASCII + +x = "sdasd \x7f asd" + +p x \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Unicode/bom_header.rb b/merlin/main/languages/ruby/Experimental/Unicode/bom_header.rb new file mode 100644 index 0000000000..30550456df --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Unicode/bom_header.rb @@ -0,0 +1,5 @@ +# coding=SJIS + +x = "aӁaaa" +p __ENCODING__ +p x.encoding \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Unicode/mixed_miterals.rb b/merlin/main/languages/ruby/Experimental/Unicode/mixed_miterals.rb new file mode 100644 index 0000000000..8ca2ef13fe --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Unicode/mixed_miterals.rb @@ -0,0 +1,82 @@ +#coding: UTF-8 + +#p <<Σ +#foo +#Σ + +#Σ = 1 +#puts Σ + +puts '-- 0.0 --' + +x = "\200\201" +p x.encoding +p x[0],x[1],x[2] + +puts '-- 0.1 --' + +x = "\200Σ\201" +p x.encoding +p x[0],x[1],x[2] + +puts '-- 0.2 --' + +x = "\xCE\xA3" +p x.encoding +p x[0],x[1],x[2] + +puts '-- 0.3 --' + +x = "Σ\xCE\xA3Σ" +p x.encoding +p x[0],x[1],x[2] + +puts '-- 0.4 --' + +x = "Σ" +p x.encoding +p x[0],x[1],x[2] + +puts '-- 1 --' + +begin + eval('"\200\u0343\201"') +rescue Exception + p $! +end + +puts '-- 2 --' + +begin + eval('"\x81\u0343\x82"') +rescue Exception + p $! +end + +puts '-- 3 --' + +begin + eval('"\x81" "\u0343" "\x82"') +rescue Exception + p $! +end + +puts '-- 4 --' + +begin + p "\x81" "\x82" +rescue Exception + p $! +end + +puts '-- 5 --' + +a = "\x81" +b = "\x82" +c = "\u0343" + +begin + z = a + b + c +rescue + puts $! +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Unicode/no_header_uni.rb b/merlin/main/languages/ruby/Experimental/Unicode/no_header_uni.rb new file mode 100644 index 0000000000..32bb244b45 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Unicode/no_header_uni.rb @@ -0,0 +1,12 @@ +#!/usr/bin/ruby + +x='ᴧ' + +p x +p x.length + +begin + puts __ENCODING__ + puts x.encoding +rescue +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Unicode/non-ascii.rb b/merlin/main/languages/ruby/Experimental/Unicode/non-ascii.rb new file mode 100644 index 0000000000..8078aab9bb --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Unicode/non-ascii.rb @@ -0,0 +1,2 @@ +x = "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250" +p x \ No newline at end of file diff --git a/merlin/main/languages/ruby/Experimental/Unicode/uni_header_no_uni_char.rb b/merlin/main/languages/ruby/Experimental/Unicode/uni_header_no_uni_char.rb new file mode 100644 index 0000000000..2e3b77eee3 --- /dev/null +++ b/merlin/main/languages/ruby/Experimental/Unicode/uni_header_no_uni_char.rb @@ -0,0 +1,14 @@ +# coding: UTF-8 + +x='foo' + +p x +p x.length + +puts __ENCODING__ +puts x.encoding + +x[1] = "ᵹ" + +p x +puts x.encoding diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Driver.cs b/merlin/main/languages/ruby/IronRuby.Tests/Driver.cs new file mode 100644 index 0000000000..c926c207da --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Driver.cs @@ -0,0 +1,390 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Security; +using System.Security.Permissions; +using System.Security.Policy; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Hosting; +using IronRuby.Runtime; + +namespace IronRuby.Tests { + public class TestCase { + public Action TestMethod { get; set; } + public RubyCompatibility Compatibility { get; set; } + public string Name { get; set; } + } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RunAttribute : Attribute { + } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class OptionsAttribute : Attribute { + public RubyCompatibility Compatibility { get; set; } + } + + public class TestRuntime { + private readonly Driver/*!*/ _driver; + private readonly string/*!*/ _testName; + private readonly ScriptRuntime/*!*/ _env; + private readonly ScriptEngine/*!*/ _engine; + private readonly RubyContext/*!*/ _context; + + public string/*!*/ TestName { get { return _testName; } } + public ScriptEngine/*!*/ Engine { get { return _engine; } } + public ScriptRuntime/*!*/ ScriptRuntime { get { return _engine.Runtime; } } + public RubyContext/*!*/ Context { get { return _context; } } + + public TestRuntime(Driver/*!*/ driver, TestCase/*!*/ testCase) { + _driver = driver; + _testName = testCase.Name; + + if (_driver.IsDebug) { + Environment.SetEnvironmentVariable("DLR_AssembliesFileName", _testName); + } + + var runtimeSetup = ScriptRuntimeSetup.ReadConfiguration(); + LanguageSetup langaugeSetup = null; + foreach (var language in runtimeSetup.LanguageSetups) { + if (language.TypeName == typeof(RubyContext).AssemblyQualifiedName) { + langaugeSetup = language; + break; + } + } + + // TODO: dynamic modules with symbols are not available in partial trust + runtimeSetup.DebugMode = !driver.PartialTrust; + langaugeSetup.Options["InterpretedMode"] = _driver.Interpret; + langaugeSetup.Options["Verbosity"] = 2; + langaugeSetup.Options["Compatibility"] = testCase.Compatibility; + + _env = Ruby.CreateRuntime(runtimeSetup); + _engine = Ruby.GetEngine(_env); + _context = Ruby.GetExecutionContext(_engine); + } + } + + public class Driver { + + private Tests _tests; + + private TestRuntime _testRuntime; + private static bool _interpretedMode; + private static bool _excludeSelectedCases; + private static bool _isDebug; + private static bool _runTokenizerDriver; + private static bool _displayList; + private static bool _partialTrust; + private static bool _interpret; + + public TestRuntime TestRuntime { + get { return _testRuntime; } + } + + public bool IsDebug { + get { return _isDebug; } + } + + public bool InterpretedMode { + get { return _interpretedMode; } + } + + public bool PartialTrust { + get { return _partialTrust; } + } + + public bool Interpret { + get { return _interpret; } + } + + private static bool ParseArguments(List/*!*/ args) { + if (args.Contains("/help") || args.Contains("-?") || args.Contains("/?") || args.Contains("-help")) { + Console.WriteLine("Run All Tests : [-X:Interpret]"); + Console.WriteLine("Partial trust : /partial"); + Console.WriteLine("Interpret : /interpret"); + Console.WriteLine("Run Specific Tests : [/debug] [/exclude] [test_to_run ...]"); + Console.WriteLine("List Tests : /list"); + Console.WriteLine("Tokenizer baseline : /tokenizer "); + Console.WriteLine("Productions dump : /tokenizer /prod "); + } + + if (args.Contains("/list")) { + _displayList = true; + return true; + } + + if (args.Contains("/debug")) { + args.Remove("/debug"); + _isDebug = true; + } + + if (args.Contains("/partial")) { + args.Remove("/partial"); + _partialTrust = true; + } + + if (args.Contains("/interpret")) { + args.Remove("/interpret"); + _interpret = true; + } + + if (args.Contains("/exclude")) { + _excludeSelectedCases = true; + args.Remove("/exclude"); + } + + if (args.Contains("-X:Interpret")) { + args.Remove("-X:Interpret"); + _interpretedMode = true; + } + + if (args.Contains("/tokenizer")) { + args.Remove("/tokenizer"); + _runTokenizerDriver = true; + } + + return true; + } + + public static void Main(string[]/*!*/ arguments) { + List args = new List(arguments); + if (args.Contains("/partial")) { + Console.WriteLine("Running in partial trust"); + + PermissionSet ps = CreatePermissionSetByName(); + AppDomainSetup setup = new AppDomainSetup(); + setup.ApplicationBase = Environment.CurrentDirectory; + AppDomain domain = AppDomain.CreateDomain("Tests", null, setup, ps); + + Loader loader = new Loader(args); + domain.DoCallBack(new CrossAppDomainDelegate(loader.Run)); + + Environment.ExitCode = loader.ExitCode; + } else { + Environment.ExitCode = Run(args); + } + } + + public sealed class Loader : MarshalByRefObject { + public int ExitCode; + public readonly List/*!*/ Args; + + public Loader(List/*!*/ args) { + Args = args; + } + + public void Run() { + ExitCode = Driver.Run(Args); + } + } + + private static PermissionSet/*!*/ CreatePermissionSetByName() { + string name = "Internet"; + bool foundName = false; + PermissionSet setIntersection = new PermissionSet(PermissionState.Unrestricted); + + // iterate over each policy level + IEnumerator e = SecurityManager.PolicyHierarchy(); + while (e.MoveNext()) { + PolicyLevel level = (PolicyLevel)e.Current; + PermissionSet levelSet = level.GetNamedPermissionSet(name); + if (levelSet != null) { + foundName = true; + setIntersection = setIntersection.Intersect(levelSet); + } + } + + if (setIntersection == null || !foundName) { + setIntersection = new PermissionSet(PermissionState.None); + } else { + setIntersection = new NamedPermissionSet(name, setIntersection); + } + + return setIntersection; + } + + public static int Run(List/*!*/ args) { + if (!ParseArguments(args)) { + return -3; + } + + int status = 0; + + if (_runTokenizerDriver) { + TokenizerTestDriver driver = new TokenizerTestDriver(Ruby.GetExecutionContext(Ruby.CreateRuntime())); + if (!driver.ParseArgs(args)) { + return -3; + } + + status = driver.RunTests(); + } else { + InitializeDomain(); + Driver driver = new Driver(); + + if (Manual.TestCode.Trim().Length == 0) { + status = driver.RunUnitTests(args); + } else { + driver.RunManualTest(); + + // for case the test is forgotten, this would fail the test suite: + status = -2; + } + } + + // return failure on bad filter (any real failures throw) + return status; + } + + private static void InitializeDomain() { + if (_isDebug) { + string _dumpDir = Path.Combine(Path.GetTempPath(), "RubyTests"); + + if (Directory.Exists(_dumpDir)) { + Array.ForEach(Directory.GetFiles(_dumpDir), delegate(string file) { + try { File.Delete(Path.Combine(_dumpDir, file)); } catch { /* nop */ } + }); + } else { + Directory.CreateDirectory(_dumpDir); + } + + Console.WriteLine("Generating binaries to {0}", _dumpDir); + + Environment.SetEnvironmentVariable("DLR_SaveAssemblies", "true"); + Environment.SetEnvironmentVariable("DLR_AssembliesDir", _dumpDir); + } + } + + private void RunManualTest() { + Console.WriteLine("Running hardcoded test case"); + + int failureCount = 0; + + if (Manual.ParseOnly) { + _testRuntime = new TestRuntime(this, new TestCase { Name = "" }); + Tests.GetRubyTokens(_testRuntime.Context, new LoggingErrorSink(false), Manual.TestCode, !Manual.DumpReductions, Manual.DumpReductions); + } else { + try { + + for (int i = 0; i < Manual.RequiredFiles.Length; i += 2) { + File.CreateText(Manual.RequiredFiles[i]).WriteLine(Manual.RequiredFiles[i + 1]); + } + + _tests = new Tests(this); + RunTestCase(new TestCase() { + Name = "$manual$", + TestMethod = () => _tests.CompilerTest(Manual.TestCode), + }, ref failureCount); + + } finally { + for (int i = 0; i < Manual.RequiredFiles.Length; i += 2) { + try { + File.Delete(Manual.RequiredFiles[i]); + } catch { + // nop + } + } + } + } + } + + private int RunUnitTests(List/*!*/ largs) { + + int failureCount = 0; + + _tests = new Tests(this); + + if (_displayList) { + for (int i = 0; i < _tests.TestMethods.Length; i++) { + Console.WriteLine(_tests.TestMethods[i].Method.Name); + } + return -1; + } + + // check whether there is a preselected case: + IList selectedCases = new List(); + + foreach (var m in _tests.TestMethods) { + if (m.Method.IsDefined(typeof(RunAttribute), false)) { + AddTestCases(selectedCases, m); + } + } + + if (selectedCases.Count == 0 && largs.Count > 0) { + foreach (var m in _tests.TestMethods) { + bool caseIsSpecified = largs.Contains(m.Method.Name); + if ((caseIsSpecified && !_excludeSelectedCases) || (!caseIsSpecified && _excludeSelectedCases)) { + AddTestCases(selectedCases, m); + } + } + } else if (selectedCases.Count > 0 && largs.Count > 0) { + Console.WriteLine("Arguments overrided by Run attribute."); + } else if (selectedCases.Count == 0 && largs.Count == 0) { + foreach (var m in _tests.TestMethods) { + AddTestCases(selectedCases, m); + } + } + + foreach (TestCase testCase in selectedCases) { + RunTestCase(testCase, ref failureCount); + } + + return failureCount; + } + + private void AddTestCases(IList/*!*/ cases, Action/*!*/ testMethod) { + var attrs = testMethod.Method.GetCustomAttributes(typeof(OptionsAttribute), false); + if (attrs.Length > 0) { + foreach (OptionsAttribute options in attrs) { + cases.Add(new TestCase { + Name = testMethod.Method.Name, + TestMethod = testMethod, + Compatibility = options.Compatibility, + }); + } + } else { + cases.Add(new TestCase { + Name = testMethod.Method.Name, + TestMethod = testMethod, + }); + } + } + + private void RunTestCase(TestCase/*!*/ testCase, ref int failedCount) { + _testRuntime = new TestRuntime(this, testCase); + + Console.WriteLine("Executing {0}", testCase.Name); + + try { + testCase.TestMethod(); + } catch (Exception e) { + Console.Error.WriteLine(e); + failedCount++; + } finally { + Snippets.SaveAndVerifyAssemblies(); + } + } + + internal string/*!*/ MakeTempDir() { + string dir = Path.Combine(Path.GetTempPath(), _testRuntime.TestName); + Directory.CreateDirectory(dir); + return dir; + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Helpers.cs b/merlin/main/languages/ruby/IronRuby.Tests/Helpers.cs new file mode 100644 index 0000000000..0f696b6740 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Helpers.cs @@ -0,0 +1,293 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Dynamic; +using System.Text; +using System.Text.RegularExpressions; +using Microsoft.Scripting; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler; +using IronRuby.Runtime; + +namespace IronRuby.Tests { + + class ErrorLogRecord { + public string Message; + public SourceSpan Span; + public int Code; + public Severity Severity; + } + + class LoggingErrorSink : ErrorCounter { + + public List Errors = new List(); + private bool _suppressOutput; + + public LoggingErrorSink() { + _suppressOutput = true; + } + + public LoggingErrorSink(bool suppressOutput) { + _suppressOutput = suppressOutput; + } + + public override void Add(SourceUnit source, string/*!*/ message, SourceSpan span, int errorCode, Severity severity) { + base.CountError(severity); + + if (!_suppressOutput) { + Console.Error.WriteLine("{0}({1}:{2}): {3}: RB{4}: {5}", source.Path, span.Start.Line, span.Start.Column, + severity, errorCode, message); + } + + ErrorLogRecord info = new ErrorLogRecord(); + info.Message = message; + info.Span = span; + info.Code = errorCode; + info.Severity = severity; + Errors.Add(info); + } + } + + public partial class Tests { + private readonly Driver/*!*/ _driver; + private readonly Action[]/*!*/ _methods; + + public Action[] TestMethods { get { return _methods; } } + public ScriptRuntime Runtime { get { return _driver.TestRuntime.ScriptRuntime; } } + public ScriptEngine Engine { get { return _driver.TestRuntime.Engine; } } + public RubyContext Context { get { return _driver.TestRuntime.Context; } } + + public string/*!*/ Eval(bool eval, string/*!*/ code) { + if (eval) { + return code.Replace("#<","eval %q{").Replace("#>", "}"); + } else { + return code.Replace("#<", "").Replace("#>", ""); + } + } + + public void CompilerTest(string/*!*/ code) { + CompilerTest(code, 0, 0); + } + + public void CompilerTest(string/*!*/ code, int expectedCompilerWarningCount, int expectedRuntimeWarningCount) { + LoggingErrorSink log = new LoggingErrorSink(true); + CompilerTest(code, log); + Assert(log.ErrorCount == 0 && log.FatalErrorCount == 0, "Compile time error"); + Assert(log.WarningCount == expectedCompilerWarningCount, "Wrong number of compile time errors/warnings"); + Assert(Context.RuntimeErrorSink.WarningCount == expectedRuntimeWarningCount, "Wrong number of runtime warnings"); + } + + public void CompilerTest(string/*!*/ code, ErrorSink/*!*/ sink) { + Debug.Assert(code != null && sink != null); + SourceUnit source; + + string name = _driver.TestRuntime.TestName; + + if (_driver.IsDebug) { + string path = Path.Combine(Snippets.Shared.SnippetsDirectory, name + ".rb"); + Directory.CreateDirectory(Snippets.Shared.SnippetsDirectory); + File.WriteAllText(path, code); + source = _driver.TestRuntime.Context.CreateFileUnit(path); + } else { + source = _driver.TestRuntime.Context.CreateSnippet(code, name + ".rb", SourceCodeKind.File); + } + + ScriptCode compiledCode = source.Compile(new RubyCompilerOptions(), sink); + if (compiledCode != null) { + compiledCode.Run(new Scope()); + } + } + + public List GetRubyTokens(ErrorSink log, string source) { + return GetRubyTokens(log, source, false); + } + + public List GetRubyTokens(ErrorSink log, string source, bool dumpTokens) { + return GetRubyTokens(Context, log, source, dumpTokens, false); + } + + public static List GetRubyTokens(RubyContext context, ErrorSink log, string source, bool dumpTokens, bool dumpReductions) { + Parser parser = new Parser(); + List tokens = new List(); + + if (dumpTokens) { + parser.Tokenizer.EnableLogging(1, Console.Out); + } + + parser.TokenSink = delegate(Tokens token, SourceSpan span) { + tokens.Add(token); + }; + +#if DEBUG + if (dumpReductions) { + DefaultParserLogger.Attach(parser, Console.Out); + } +#endif + parser.Parse(context.CreateSnippet(source, SourceCodeKind.File), new RubyCompilerOptions(), log); + + //tokenizer.Initialize(SourceUnit.CreateSnippet(RB, source)); + //List tokens = new List(); + //Tokens token; + //while ((token = tokenizer.GetNextToken()) != Tokens.EOF) { + // tokens.Add(token); + //} + + return tokens; + } + +#if !SILVERLIGHT + private static int domainId = 0; + + private static AppDomain CreateDomain() { + return AppDomain.CreateDomain("RemoteScripts" + domainId++); + } +#endif + + [Flags] + enum OutputFlags { + None = 0, + Raw = 1, + Match = 2 + } + + private void AssertEquals(object actual, T expected) + where T : IEquatable { + Assert(actual is T && ((T)actual).Equals(expected)); + } + + private void XAssertOutput(Action f, string expectedOutput) { + Console.WriteLine("Assertion check skipped."); + // just run the code + f(); + } + + private void XAssertOutput(Action f, string expectedOutput, OutputFlags flags) { + Console.WriteLine("Assertion check skipped."); + // just run the code + f(); + } + + private void AssertOutput(Action f, string expectedOutput) { + AssertOutput(f, expectedOutput, OutputFlags.None); + } + + private void AssertOutput(Action f, string expectedOutput, OutputFlags flags) { +#if !SILVERLIGHT + StringBuilder builder = new StringBuilder(); + + using (StringWriter output = new StringWriter(builder)) { + RedirectOutput(output, f); + } + + string actualOutput = builder.ToString(); + + if ((flags & OutputFlags.Raw) == 0) { + actualOutput = actualOutput.Trim().Replace("\r", ""); + expectedOutput = expectedOutput.Trim().Replace("\r", ""); + } + + if ((flags & OutputFlags.Match) != 0) { + Regex regex = new Regex(Regex.Escape(expectedOutput).Replace("\\*", ".*").Replace("\\?", ".")); + if (!regex.IsMatch(actualOutput)) { + Assert(false, String.Format("Unexpected output: \n\n'{0}'.", actualOutput)); + } + } else { + int i = 0; + while (i < actualOutput.Length && i < expectedOutput.Length && actualOutput[i] == expectedOutput[i]) i++; + + if (actualOutput != expectedOutput) { + Assert(false, String.Format("Unexpected output: \n\n'{0}'.\n\nFirst difference ({1}):\nactual = '{2}'\nexpected = '{3}'\n", + Escape(builder), i, + (i < actualOutput.Length ? Escape(actualOutput[i]) : ""), + (i < expectedOutput.Length ? Escape(expectedOutput[i]) : "") + )); + } + } +#endif + } + + private static string Escape(char ch) { + return ch.ToString().Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t"); + } + + private static string Escape(string str) { + return str.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t"); + } + + private static string Escape(StringBuilder str) { + return str.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t").ToString(); + } + + private void RedirectOutput(TextWriter/*!*/ output, Action f) { + // TODO: + MemoryStream stream = new MemoryStream(); + Runtime.IO.SetOutput(stream, StringUtils.DefaultEncoding); + Runtime.IO.SetErrorOutput(Console.OpenStandardError(), Console.Error); + + try { + f(); + } finally { + output.Write(StringUtils.DefaultEncoding.GetString(stream.ToArray())); + Runtime.IO.RedirectToConsole(); + } + } + + internal void AssertExceptionThrown(Action f) where T : Exception { + AssertExceptionThrown(f, null); + } + + internal void AssertExceptionThrown(Action f, Predicate condition) where T : Exception { + try { + RedirectOutput(TextWriter.Null, f); + } catch (T e) { + if (condition != null) { + Assert(condition(e), "Exception has been thrown but the condition doesn't hold"); + } + return; + } catch (Exception e) { + Assert(false, "Expecting exception '" + typeof(T) + "', got '" + e.GetType() + "'."); + } + + Assert(false, "Expecting exception '" + typeof(T) + "'."); + } + + /// + /// Asserts two values are equal + /// + internal static void AreEqual(object x, object y) { + if (x == null && y == null) return; + + Assert(x != null && x.Equals(y), String.Format("values aren't equal: {0} and {1}", x, y)); + } + + /// + /// Asserts an condition it true + /// + internal static void Assert(bool condition, string msg) { + if (!condition) throw new Exception(String.Format("Assertion failed: {0}", msg)); + } + + internal static void Assert(bool condition) { + if (!condition) throw new Exception("Assertion failed"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/IronRuby.Tests.csproj b/merlin/main/languages/ruby/IronRuby.Tests/IronRuby.Tests.csproj new file mode 100644 index 0000000000..36783dd86e --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/IronRuby.Tests.csproj @@ -0,0 +1,124 @@ + + + Debug + AnyCPU + 9.0.30718 + 2.0 + {8103D91B-89D8-4A18-9A40-426992602EA2} + Exe + Properties + IronRuby.Tests + IronRuby.Tests + SAK + SAK + SAK + SAK + 2.0 + 618,1685 + + + true + full + false + ..\..\..\bin\Debug\ + TRACE;DEBUG;SIGNED + prompt + 4 + true + ..\..\..\MSSharedLibKey.snk + true + true + + + pdbonly + true + ..\..\..\bin\Release\ + TRACE;SIGNED + prompt + 4 + true + ..\..\..\MSSharedLibKey.snk + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4} + Microsoft.Scripting.Core + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Scripting + + + {77323B06-15A2-4CF4-8A7A-86EAA2B66498} + IronRuby.Libraries + + + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD} + Ruby %28Languages\Ruby\Ruby%29 + + + + + + + + + + + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/IronRuby.Tests/IronRuby.Tests.csproj.vspscc b/merlin/main/languages/ruby/IronRuby.Tests/IronRuby.Tests.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/IronRuby.Tests.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Manual.cs b/merlin/main/languages/ruby/IronRuby.Tests/Manual.cs new file mode 100644 index 0000000000..e0366fdc7f --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Manual.cs @@ -0,0 +1,27 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Tests { + public static class Manual { + public const string TestCode = @" +"; + // "file.rb", "content" + public static string[] RequiredFiles = new string[] { + }; + + public static bool ParseOnly = false; + public static bool DumpReductions = false; + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Parser/AssertTokenizer.cs b/merlin/main/languages/ruby/IronRuby.Tests/Parser/AssertTokenizer.cs new file mode 100644 index 0000000000..d60710f8a0 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Parser/AssertTokenizer.cs @@ -0,0 +1,184 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using IronRuby.Builtins; +using IronRuby.Compiler; +using IronRuby.Runtime; +using System.IO; +using System.Text; +using System.Collections.Generic; + +namespace IronRuby.Tests { + internal class AssertTokenizer { + private readonly RubyContext/*!*/ _context; + private Tokenizer _tokenizer; + private Tokens _actualToken; + private TokenValue _actualValue; + private SourceSpan _actualSpan; + private LoggingErrorSink _log; + private List/*!*/ _allTokens; + private List/*!*/ _allValues; + + public AssertTokenizer(RubyContext/*!*/ context) { + _log = new LoggingErrorSink(); + _context = context; + } + + public AssertTokenizer/*!*/ B { + get { if (Debugger.IsAttached) Debugger.Break(); return this; } + } + + public void EOF() { + Read(Tokens.EndOfFile); + Expect(); + } + + public AssertTokenizer/*!*/ Load(string/*!*/ source) { + Tests.Assert(_log.Errors.Count == 0, "Previous test case reported unexpected error/warning(s)"); + + _tokenizer = new Tokenizer(false, _log); + _tokenizer.Compatibility = _context.RubyOptions.Compatibility; + _tokenizer.Initialize(_context.CreateSnippet(source, SourceCodeKind.File)); + _allTokens = new List(); + _allValues = new List(); + return this; + } + + public AssertTokenizer/*!*/ Load(byte[]/*!*/ source) { + Tests.Assert(_log.Errors.Count == 0, "Previous test case reported unexpected error/warning(s)"); + + _tokenizer = new Tokenizer(false, _log); + _tokenizer.Compatibility = _context.RubyOptions.Compatibility; + _tokenizer.Initialize(_context.CreateSourceUnit( + new BinaryContentProvider(source), null, BinaryEncoding.Instance, SourceCodeKind.File) + ); + _allTokens = new List(); + _allValues = new List(); + return this; + } + + public AssertTokenizer/*!*/ Skip(int count) { + while (count-- > 0) { + Next(); + } + return this; + } + + public AssertTokenizer/*!*/ Next() { + _actualToken = _tokenizer.GetNextToken(); + _actualValue = _tokenizer.TokenValue; + _actualSpan = _tokenizer.TokenSpan; + _allTokens.Add(_actualToken); + _allValues.Add(_actualValue); + return this; + } + + public AssertTokenizer/*!*/ Read(Tokens token) { + Next(); + Tests.Assert(_actualToken == token); + return this; + } + + public AssertTokenizer/*!*/ Read(Tokens token, TokenValueType tokenValue) { + Next(); + Tests.Assert(_actualToken == token); + Tests.Assert(_actualValue.Type == tokenValue); + return this; + } + + public AssertTokenizer/*!*/ Read(int expected) { + Next(); + Tests.Assert(_actualToken == Tokens.Integer); + Tests.Assert(_actualValue.Type == TokenValueType.Integer); + Tests.Assert(expected == _actualValue.Integer); + return this; + } + + public AssertTokenizer/*!*/ Read(string/*!*/ expected) { + Next(); + Tests.Assert(_actualToken == Tokens.StringContent); + Tests.Assert(_actualValue.Type == TokenValueType.String); + Tests.Assert(expected == _actualValue.String); + return this; + } + + public AssertTokenizer/*!*/ ReadSymbol(Tokens token, string expected) { + Next(); + Tests.Assert(_actualToken == token); + Tests.Assert(_actualValue.Type == TokenValueType.String); + Tests.Assert(expected == _actualValue.String); + return this; + } + + public AssertTokenizer/*!*/ Read(RubyRegexOptions expected) { + Next(); + Tests.Assert(_actualToken == Tokens.RegexpEnd); + Tests.Assert(_actualValue.Type == TokenValueType.RegexOptions); + Tests.Assert(expected == _actualValue.RegExOptions); + return this; + } + + public AssertTokenizer/*!*/ ReadBigInteger(string/*!*/ expected, uint @base) { + Next(); + Tests.Assert(_actualToken == Tokens.BigInteger); + Tests.Assert(_actualValue.Type == TokenValueType.BigInteger); + Tests.Assert(StringComparer.OrdinalIgnoreCase.Compare(_actualValue.BigInteger.ToString(@base), expected) == 0); + return this; + } + + public AssertTokenizer/*!*/ Read(double expected) { + Next(); + Tests.Assert(_actualToken == Tokens.Float); + Tests.Assert(_actualValue.Type == TokenValueType.Double); + + if (Double.IsNaN(expected)) { + Tests.Assert(Double.IsNaN(_actualValue.Double)); + } else if (Double.IsNegativeInfinity(expected)) { + Tests.Assert(Double.IsNegativeInfinity(_actualValue.Double)); + } else if (Double.IsPositiveInfinity(expected)) { + Tests.Assert(Double.IsPositiveInfinity(_actualValue.Double)); + } else { + // TODO: is this correct? + Tests.Assert(System.Math.Abs(_actualValue.Double - expected) < Double.Epsilon); + } + return this; + } + + public AssertTokenizer/*!*/ Expect(params ErrorInfo[] errors) { + if (errors == null || errors.Length == 0) { + Tests.Assert(_log.Errors.Count == 0, "Unexpected error/warning(s)"); + } else { + Tests.Assert(_log.Errors.Count == errors.Length, String.Format("Expected {0} error/warning(s)", errors.Length)); + for (int i = 0; i < errors.Length; i++) { + Tests.Assert(_log.Errors[i].Code == errors[i].Code); + } + } + _log.Errors.Clear(); + return this; + } + + public AssertTokenizer/*!*/ this[Tokens token] { + get { return Read(token); } + } + + public AssertTokenizer/*!*/ this[string/*!*/ expected] { + get { return Read(expected); } + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Parser/BinaryContentProvider.cs b/merlin/main/languages/ruby/IronRuby.Tests/Parser/BinaryContentProvider.cs new file mode 100644 index 0000000000..afbe23f009 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Parser/BinaryContentProvider.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using Microsoft.Scripting; + +namespace IronRuby.Tests { + public class BinaryContentProvider : StreamContentProvider { + public byte[] Buffer { get; set; } + + public BinaryContentProvider(byte[]/*!*/ buffer) { + Buffer = buffer; + } + + public override Stream/*!*/ GetStream() { + return new MemoryStream(Buffer); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Parser/CoverageParserLogger.cs b/merlin/main/languages/ruby/IronRuby.Tests/Parser/CoverageParserLogger.cs new file mode 100644 index 0000000000..ad8dc9eea6 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Parser/CoverageParserLogger.cs @@ -0,0 +1,72 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ +#if DEBUG +using System.Collections.Generic; +using System.IO; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler; + +namespace IronRuby.Tests { + /// + /// Create a shadow stack, similar to the one used to keep track of which LALR state you are in + /// + /// Whenever you encounter a 'shift' action push 0 on the shadow stack. Since you shift terimal symbols, we can ignore them. + /// + /// Whenever you encounter a 'reduction' action, pop the top n elements off the shadow stack and log them. So, for example + /// if you encounter a production which is a sequence of four terminals you get "[ 0 0 0 0 ]". + /// + /// Whenever you encounter a 'goto' action (typically immediately after a reduction), push the production ID on the shadow stack. + /// This way, the next time you reduce a production where the non terminal token was you have a production ID instead of 0. + /// + public class CoverageParserLogger : IParserLogger { + private readonly Parser/*!*/ _parser; + private readonly TextWriter/*!*/ _output; + private readonly Stack/*!*/ _rules; + + public CoverageParserLogger(Parser/*!*/ parser, TextWriter/*!*/ output) { + Assert.NotNull(parser, output); + _parser = parser; + _output = output; + _rules = new Stack(); + } + + public void BeforeReduction(int ruleId) { + _output.Write(ruleId); + _output.Write(" ["); + + for (int i = 0; i < _parser.RuleRhsLengths[ruleId]; i++) { + if (i > 0) _output.Write(' '); + _output.Write(_rules.Pop()); + } + + _output.WriteLine("]"); + } + + public void BeforeShift(int stateId, int tokenId, bool isErrorShift) { + _rules.Push(0); + } + + public void BeforeGoto(int stateId, int ruleId) { + _rules.Push(ruleId); + } + + public void StateEntered() { + } + + public void NextToken(int tokenId) { + } + } +} +#endif \ No newline at end of file diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Parser/DefaultParserLogger.cs b/merlin/main/languages/ruby/IronRuby.Tests/Parser/DefaultParserLogger.cs new file mode 100644 index 0000000000..8017dc8a31 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Parser/DefaultParserLogger.cs @@ -0,0 +1,95 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if DEBUG +using System; +using System.Diagnostics; +using System.IO; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler; + +namespace IronRuby.Tests { + internal class DefaultParserLogger : IParserLogger { + private readonly Parser/*!*/ _parser; + private int _logVerbosity; // 0 means logging disabled + private TextWriter _log; + + public DefaultParserLogger(Parser/*!*/ parser, int verbosity, TextWriter/*!*/ output) { + Assert.NotNull(parser, output); + Debug.Assert(verbosity > 0 && verbosity <= 2); + + _parser = parser; + _logVerbosity = verbosity; + _log = output; + } + + public void BeforeReduction(int ruleId) { + LogRule("Reducing by rule", ruleId); + } + + public void BeforeShift(int stateId, int tokenId, bool isErrorShift) { + LogToken(isErrorShift ? "Error shift" : "Shifting token", tokenId); + } + + public void BeforeGoto(int stateId, int ruleId) { + } + + public void StateEntered() { + LogState("Entering state", _parser.CurrentState.Id); + } + + public void NextToken(int tokenId) { + LogToken("Next token", tokenId); + } + + + private void LogRule(string message, int ruleId) { + Log("{0} {1}: {2}", message, ruleId, _parser.RuleToString(ruleId)); + } + + private void LogToken(string message, int tokenId) { + Log("{0}: {1}", message, Parser.TerminalToString(tokenId)); + } + + private void LogState(string/*!*/ action, int stateId) { + Log("{0} {1} ", action, stateId); + } + + private void Log(string/*!*/ format, params object[] args) { + if (_logVerbosity > 0) { + _log.WriteLine(format, args); + } + } + + private void DumpStack() { + if (_logVerbosity > 1) { + _log.WriteLine("State stack:"); + foreach (State state in _parser.StateStack.GetEnumerator()) { + _log.WriteLine(state.Id); + } + _log.WriteLine(); + } + } + + public static void Attach(Parser/*!*/ parser) { + Attach(parser, Console.Out); + } + + public static void Attach(Parser/*!*/ parser, TextWriter/*!*/ output) { + parser.EnableLogging(new DefaultParserLogger(parser, 1, output)); + } + } +} +#endif \ No newline at end of file diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Parser/ParserTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Parser/ParserTests.cs new file mode 100644 index 0000000000..2142484ffb --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Parser/ParserTests.cs @@ -0,0 +1,812 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Dynamic; +using System.Text; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using IronRuby.Builtins; +using IronRuby.Compiler; +using IronRuby.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Ast; +using System.Runtime.CompilerServices; +using IronRuby.Runtime.Calls; + +using MSA = System.Linq.Expressions; +using Ast = System.Linq.Expressions.Expression; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using System.Reflection; + +namespace IronRuby.Tests { + public partial class Tests { + + // manual test, measures initialization and JIT times (hence it must be the first run) + public void Scenario_Startup() { + Stopwatch w = new Stopwatch(); + w.Start(); + Engine.CreateScriptSourceFromString("def foo; puts 2 + 2; end").Execute(Engine.CreateScope()); + w.Stop(); + Console.WriteLine("> total time: {0}ms", w.ElapsedMilliseconds); + } + + // manual test + public void Scenario_ParserLogging() { + if (!_driver.PartialTrust) { + ParserLoggingTest(); + } + } + + private void ParserLoggingTest() { +#if DEBUG + string source = "def foo(a); end"; + var sourceUnit = Context.CreateSnippet(source, SourceCodeKind.Statements); + var options = new RubyCompilerOptions(); + + string temp = Path.Combine(Path.GetTempPath(), "RubyParser"); + Console.WriteLine("> see {0}", temp); + Directory.CreateDirectory(temp); + + Parser parser = new Parser(); + using (TextWriter writer = File.CreateText(Path.Combine(temp, "default.log"))) { + DefaultParserLogger.Attach(parser, writer); + parser.Parse(sourceUnit, options, ErrorSink.Null); + } + + using (TextWriter writer = File.CreateText(Path.Combine(temp, "tables.csv"))) { + parser.DumpTables(writer); + } + + using (TextWriter writer = File.CreateText(Path.Combine(temp, "productions.txt"))) { + for (int i = 0; i < parser.RuleLhsNonTerminals.Length; i++) { + writer.WriteLine("{0}\t{1}", i, parser.RuleToString(i)); + } + } + + parser = new Parser(); + using (TextWriter writer = File.CreateText(Path.Combine(temp, "productions.txt"))) { + for (int i = 0; i < parser.RuleLhsNonTerminals.Length; i++) { + writer.WriteLine("{0}\t{1}", i, parser.RuleToString(i)); + } + } + + using (TextWriter writer = File.CreateText(Path.Combine(temp, "second_order.log"))) { + parser.EnableLogging(new CoverageParserLogger(parser, writer)); + parser.Parse(sourceUnit, options, ErrorSink.Null); + } +#endif + } + + public void Scenario_RubyTokenizer1() { + LoggingErrorSink log = new LoggingErrorSink(); + + List tokens; + tokens = GetRubyTokens(log, "foo (while (f do end) do end) do end"); + + Assert(tokens.Count == 14); + Assert(tokens[0] == Tokens.Identifier); + Assert(tokens[1] == Tokens.LparenArg); + Assert(tokens[2] == Tokens.While); + Assert(tokens[3] == Tokens.LeftParen); + Assert(tokens[4] == Tokens.Identifier); + Assert(tokens[5] == Tokens.Do); + Assert(tokens[6] == Tokens.End); + Assert(tokens[7] == (Tokens)')'); + Assert(tokens[8] == Tokens.LoopDo); + Assert(tokens[9] == Tokens.End); + Assert(tokens[10] == (Tokens)')'); + Assert(tokens[11] == Tokens.BlockDo); + Assert(tokens[12] == Tokens.End); + Assert(tokens[13] == Tokens.EndOfFile); + + log.Errors.Clear(); + + tokens = GetRubyTokens(log, "print 'foo'"); + + Assert(log.Errors.Count == 0); + + Assert(tokens.Count == 5 && + tokens[0] == Tokens.Identifier && + tokens[1] == Tokens.StringBeg && + tokens[2] == Tokens.StringContent && + tokens[3] == Tokens.StringEnd && + tokens[4] == Tokens.EndOfFile); + + Assert(log.Errors.Count == 0); + + tokens = GetRubyTokens(log, "print '"); + + Assert(log.Errors.Count == 1 && + log.Errors[0].Severity == Severity.Error + ); + + Assert(tokens.Count == 4 && + tokens[0] == Tokens.Identifier && + tokens[1] == Tokens.StringBeg && + tokens[2] == Tokens.StringEnd && + tokens[3] == Tokens.EndOfFile); + } + + public void Scenario_RubyCategorizer1() { + TestCategorizer(Engine, "print 'foo' #bar", -1, new TokenInfo[] { + new TokenInfo(new SourceSpan(new SourceLocation(0, 1, 1), new SourceLocation(5, 1, 6)), TokenCategory.Identifier, TokenTriggers.None), + new TokenInfo(new SourceSpan(new SourceLocation(6, 1, 7), new SourceLocation(7, 1, 8)), TokenCategory.StringLiteral, TokenTriggers.None), + new TokenInfo(new SourceSpan(new SourceLocation(7, 1, 8), new SourceLocation(10, 1, 11)), TokenCategory.StringLiteral, TokenTriggers.None), + new TokenInfo(new SourceSpan(new SourceLocation(10, 1, 11), new SourceLocation(11, 1, 12)), TokenCategory.StringLiteral, TokenTriggers.None), + new TokenInfo(new SourceSpan(new SourceLocation(12, 1, 13), new SourceLocation(16, 1, 17)), TokenCategory.LineComment, TokenTriggers.None), + }); + + TestCategorizer(Engine, "a\r\nb", -1, new TokenInfo[] { + new TokenInfo(new SourceSpan(new SourceLocation(0, 1, 1), new SourceLocation(1, 1, 2)), TokenCategory.Identifier, TokenTriggers.None), // a + new TokenInfo(new SourceSpan(new SourceLocation(1, 1, 2), new SourceLocation(3, 1, 4)), TokenCategory.WhiteSpace, TokenTriggers.None), // \r\n + new TokenInfo(new SourceSpan(new SourceLocation(3, 2, 1), new SourceLocation(4, 2, 2)), TokenCategory.Identifier, TokenTriggers.None), // b + }); + + // 11111111 11222222222233 333 + // 012345678901234567 89012345678901 234 + TestCategorizer(Engine, "canvas.Event { |x|\nputs 'string'\n}", -1, new TokenInfo[] { + // 1234567890123456789 12345678901234 12 + // 1111111111 11111 + // line 1 + new TokenInfo(new SourceSpan(new SourceLocation(0, 1, 1), new SourceLocation(6, 1, 7)), TokenCategory.Identifier, TokenTriggers.None), // canvas + new TokenInfo(new SourceSpan(new SourceLocation(6, 1, 7), new SourceLocation(7, 1, 8)), TokenCategory.Delimiter, TokenTriggers.MemberSelect), // . + new TokenInfo(new SourceSpan(new SourceLocation(7, 1, 8), new SourceLocation(12, 1, 13)), TokenCategory.Identifier, TokenTriggers.None), // Event + new TokenInfo(new SourceSpan(new SourceLocation(13, 1, 14), new SourceLocation(14, 1, 15)), TokenCategory.Grouping, TokenTriggers.MatchBraces), // { + new TokenInfo(new SourceSpan(new SourceLocation(15, 1, 16), new SourceLocation(16, 1, 17)), TokenCategory.Grouping, TokenTriggers.MatchBraces), // | + new TokenInfo(new SourceSpan(new SourceLocation(16, 1, 17), new SourceLocation(17, 1, 18)), TokenCategory.Identifier, TokenTriggers.None), // x + new TokenInfo(new SourceSpan(new SourceLocation(17, 1, 18), new SourceLocation(18, 1, 19)), TokenCategory.Grouping, TokenTriggers.MatchBraces), // | + // line 2 + new TokenInfo(new SourceSpan(new SourceLocation(19, 2, 1), new SourceLocation(23, 2, 5)), TokenCategory.Identifier, TokenTriggers.None), // puts + new TokenInfo(new SourceSpan(new SourceLocation(24, 2, 6), new SourceLocation(25, 2, 7)), TokenCategory.StringLiteral, TokenTriggers.None), // ' + new TokenInfo(new SourceSpan(new SourceLocation(25, 2, 7), new SourceLocation(31, 2, 13)), TokenCategory.StringLiteral, TokenTriggers.None), // string + new TokenInfo(new SourceSpan(new SourceLocation(31, 2, 13), new SourceLocation(32, 2, 14)), TokenCategory.StringLiteral, TokenTriggers.None), // ' + new TokenInfo(new SourceSpan(new SourceLocation(32, 2, 14), new SourceLocation(33, 2, 15)), TokenCategory.WhiteSpace, TokenTriggers.None), // \n (significant) + // line 3 + new TokenInfo(new SourceSpan(new SourceLocation(33, 3, 1), new SourceLocation(34, 3, 2)), TokenCategory.Grouping, TokenTriggers.MatchBraces), // } + }); + } + + public void Identifiers1() { + Assert(Tokenizer.IsConstantName("C")); + Assert(Tokenizer.IsConstantName("Cx")); + Assert(Tokenizer.IsConstantName("C9")); + Assert(Tokenizer.IsConstantName("CazAZ0123456789_")); + Assert(Tokenizer.IsConstantName("C_")); + Assert(!Tokenizer.IsConstantName(null)); + Assert(!Tokenizer.IsConstantName("")); + Assert(!Tokenizer.IsConstantName("C=")); + Assert(!Tokenizer.IsConstantName("C?")); + Assert(!Tokenizer.IsConstantName("C!")); + Assert(!Tokenizer.IsConstantName("_")); + Assert(!Tokenizer.IsConstantName("0")); + Assert(!Tokenizer.IsConstantName("c")); + + Assert(Tokenizer.IsMethodName("C")); + Assert(Tokenizer.IsMethodName("Cx")); + Assert(Tokenizer.IsMethodName("CazAZ0123456789_")); + Assert(Tokenizer.IsMethodName("f=")); + Assert(Tokenizer.IsMethodName("f?")); + Assert(Tokenizer.IsMethodName("f!")); + Assert(Tokenizer.IsMethodName("_")); + Assert(Tokenizer.IsMethodName("c")); + Assert(!Tokenizer.IsMethodName("=")); + Assert(!Tokenizer.IsMethodName("?")); + Assert(!Tokenizer.IsMethodName("!")); + Assert(!Tokenizer.IsMethodName(null)); + Assert(!Tokenizer.IsMethodName("")); + + Assert(Tokenizer.IsGlobalVariableName("$x")); + Assert(Tokenizer.IsGlobalVariableName("$XazAZ0123456789_")); + Assert(!Tokenizer.IsGlobalVariableName("$f=")); + Assert(!Tokenizer.IsGlobalVariableName("$f?")); + Assert(!Tokenizer.IsGlobalVariableName("$f!")); + Assert(!Tokenizer.IsGlobalVariableName("$f$")); + Assert(!Tokenizer.IsGlobalVariableName(null)); + Assert(!Tokenizer.IsGlobalVariableName("$")); + Assert(!Tokenizer.IsGlobalVariableName("$$")); + Assert(!Tokenizer.IsGlobalVariableName("f")); + Assert(!Tokenizer.IsGlobalVariableName("ff")); + Assert(!Tokenizer.IsGlobalVariableName("fff")); + + Assert(Tokenizer.IsInstanceVariableName("@x")); + Assert(Tokenizer.IsInstanceVariableName("@XazAZ0123456789_")); + Assert(!Tokenizer.IsInstanceVariableName("@f=")); + Assert(!Tokenizer.IsInstanceVariableName("@f?")); + Assert(!Tokenizer.IsInstanceVariableName("@f!")); + Assert(!Tokenizer.IsInstanceVariableName("@f@")); + Assert(!Tokenizer.IsInstanceVariableName(null)); + Assert(!Tokenizer.IsInstanceVariableName("@")); + Assert(!Tokenizer.IsInstanceVariableName("@@")); + Assert(!Tokenizer.IsInstanceVariableName("@@@")); + Assert(!Tokenizer.IsInstanceVariableName("f")); + Assert(!Tokenizer.IsInstanceVariableName("ff")); + Assert(!Tokenizer.IsInstanceVariableName("fff")); + + Assert(Tokenizer.IsClassVariableName("@@x")); + Assert(Tokenizer.IsClassVariableName("@@XazAZ0123456789_")); + Assert(!Tokenizer.IsClassVariableName("@@f=")); + Assert(!Tokenizer.IsClassVariableName("@@f?")); + Assert(!Tokenizer.IsClassVariableName("@@f!")); + Assert(!Tokenizer.IsClassVariableName("@@f@")); + Assert(!Tokenizer.IsClassVariableName(null)); + Assert(!Tokenizer.IsClassVariableName("@")); + Assert(!Tokenizer.IsClassVariableName("@@")); + Assert(!Tokenizer.IsClassVariableName("@@@")); + Assert(!Tokenizer.IsClassVariableName("f")); + Assert(!Tokenizer.IsClassVariableName("ff")); + Assert(!Tokenizer.IsClassVariableName("fff")); + } + + private void Identifiers2() { + AssertTokenizer t = AssertTokens(); + + // 'variable' non-terminal needs to set $$ even for keywords, + // otherwise the content of previous token is stored in token value and is interpreted as string. + t.Load("//\ntrue")[Tokens.RegexpBeg][Tokens.RegexpEnd][(Tokens)'\n'][Tokens.True].EOF(); + + t.Expect(); + } + + public void Scenario_ParseBigInts1() { + TestBigInt("00010010000_00100000_01001000_00010000_00000010_01001000_11111111_00010000_00100000_00000011", 2); + TestBigInt("000100_afe1_1231_4980_6FEA_1470_5100_afe1_1231_4980_6FEA_1471", 16); + TestBigInt("65_6465477756_5454111111_1112365414_65_4365414564_5463215467_3215456713", 8); + TestBigInt("645456600_000798789_789798798_798789798_798765461_237676891_968734198_467346797", 10); + TestBigInt("hhs15489231dfu89765460z", 36); + TestBigInt("lafkhalkdhflkghalgAKD12", 28); + + for (int i = 3; i <= 36; i++) { + TestBigInt("00010010000_00100000_01001000_00010000_00000010_01001000_11111111_00010000_00100000_00000011", i, 0); + } + } + + private void Scenario_ParseNumbers1() { + AssertTokenizer t = AssertTokens(); + + t.Load("0").Read(0); + t.Load("0000").Read(0); + t.Load("0_0_00000_0_0_00000000000000000000_00_00000000000000000000000000000").Read(0); + + t.Load("0777").Read(Convert.ToInt32("777", 8)); + t.Load("000000000000000000000000000000000000076541").Read(Convert.ToInt32("76541", 8)); + AssertTokenBigInteger("0100000000000000_000000000000000000000076541", 8); + + t.Load("0x0").Read(0); + t.Load("0xa_F").Read(Convert.ToInt32("af", 16)); + t.Load("0x000000_0000000000000000000aF").Read(Convert.ToInt32("af", 16)); + t.Load("0x10000000000_00000000000000aF").ReadBigInteger("1000000000000000000000000aF", 16); + + t.Load("0b0").Read(0); + t.Load("0b000000000000_000000000000000000000000000101").Read(Convert.ToInt32("101", 2)); + t.Load("0b10000000_0000000000000000000000000000000101").ReadBigInteger("100000000000000000000000000000000000000101", 2); + + t.Load("0d0").Read(0); + t.Load("0d00000000_0000000000000000000000000000000101").Read(101); + t.Load("0d10000000_0000000000000000000000_000000000101").ReadBigInteger("100000000000000000000000000000000000000101", 10); + + t.Load("0o0").Read(0); + t.Load("0o000_000000000000000000000000000000000076541").Read(Convert.ToInt32("76541", 8)); + t.Load("0o100000000_000000000000000000000000000076541").ReadBigInteger("100000000000000000000000000000000000076541", 8); + t.Load("0.0").Read(0.0D); + + t.Load("0e-000").Read(0.0D); + t.Load("0e2").Read(0.0D); + t.Load("0e+2").Read(0.0D); + t.Load("1e2").Read(100.0D); + t.Load("1e+2").Read(100.0D); + t.Load("1e-2").Read(0.01D); + + t.Load("3_1_3_2_1_3_2_1_3_5_4_6_5_3_1_3_2.0").Read(31321321354653132.0D); + t.Load("1_3_2.3_1_3_2_1_3").Read(132.313213D); + + t.Load("1.1e-0").Read(1.1D); + t.Load("1.1e+0").Read(1.1D); + t.Load("1.1e0").Read(1.1D); + t.Load("1.1e-1").Read(0.11D); + + t.Load("1.1e-1020").Read(0.0D); + t.Load("1.1e-1021").Read(0.0D).Expect(Errors.FloatOutOfRange); + + t.Load("1.1e1024").Read(Double.PositiveInfinity); + t.Load("1.1e1025").Read(Double.PositiveInfinity).Expect(Errors.FloatOutOfRange); + + t.Load("1.1e-30000").Read(0.0D).Expect(Errors.FloatOutOfRange); + t.Load("1.1e3_1_3_2_1_3_2_1_3_5_4_6_5_3_1_3_2").Read(Double.PositiveInfinity).Expect(Errors.FloatOutOfRange); + t.Load("4.94065645841247e-324").Read(Double.Epsilon); + t.Load("1_2.4_5e2_2").Read(12.45e22); + t.Load("1._1").Read(1); + t.Load("1.").Read(1); + + t.Load("122312. 1212").Read(122312); + t.Load("01234e12").Read(Convert.ToInt32("1234", 8)); + t.Load("12__1212").Read(12).Expect(Errors.TrailingUnderscoreInNumber); + t.Load("123_.123").Read(123).Expect(Errors.TrailingUnderscoreInNumber); + t.Load("08").Read(8).Expect(Errors.IllegalOctalDigit); + t.Load("0_8").Read(0).Expect(Errors.TrailingUnderscoreInNumber); + t.Load("0_x").Read(0).Expect(Errors.TrailingUnderscoreInNumber); + t.Load("0_").Read(0).Expect(Errors.TrailingUnderscoreInNumber); + t.Load("0x_").Read(0).Expect(Errors.NumericLiteralWithoutDigits); + t.Load("0x").Read(0).Expect(Errors.NumericLiteralWithoutDigits); + t.Load("0x_1").Read(0).Expect(Errors.NumericLiteralWithoutDigits); + + t.Load(".20").Read((Tokens)'.', TokenValueType.None).Expect(Errors.NoFloatingLiteral); + t.Load("1.e").Read(1); + t.Load("1.2e").Read(1.2D).Expect(Errors.TrailingEInNumber); + t.Load("1e").Read(1).Expect(Errors.TrailingEInNumber); + t.Load("1e-").Read(1).Expect(Errors.TrailingMinusInNumber); + t.Load("1e+").Read(1).Expect(Errors.TrailingPlusInNumber); + + t.Load("00.0").Read(0).Expect(Errors.NoFloatingLiteral); + t.Load("00.foo").Read(0); + t.Load("00.e-1").Read(0); + t.Load("00.foo").Read(0); + t.Load("0x.0").Read(0).Expect(Errors.NumericLiteralWithoutDigits, Errors.NoFloatingLiteral); + t.Load("0x.foo").Read(0).Expect(Errors.NumericLiteralWithoutDigits); + + t.Expect(); + } + + private void Scenario_ParseInstanceClassVariables1() { + AssertTokenizer t = AssertTokens(); + + t.Load("@").Read((Tokens)'@'); + t.Load("@@").Read((Tokens)'@'); + t.Load("@1").Read((Tokens)'@').Expect(Errors.InvalidInstanceVariableName); + t.Load("@@1").Read((Tokens)'@').Expect(Errors.InvalidClassVariableName); + t.Load("@_").ReadSymbol(Tokens.InstanceVariable, "@_"); + t.Load("@@_").ReadSymbol(Tokens.ClassVariable, "@@_"); + t.Load("@aA1_").ReadSymbol(Tokens.InstanceVariable, "@aA1_"); + t.Load("@@aA1_").ReadSymbol(Tokens.ClassVariable, "@@aA1_"); + + t.Expect(); + } + + private void Scenario_ParseRegex1() { + AssertTokenizer t = AssertTokens(); + + t.Load("//")[Tokens.RegexpBeg][Tokens.RegexpEnd].EOF(); + t.Load("/foo/")[Tokens.RegexpBeg]["foo"][Tokens.RegexpEnd].EOF(); + + t.Load("/foo/aib").Skip(2).Read(RubyRegexOptions.IgnoreCase).Expect(Errors.UnknownRegexOption, Errors.UnknownRegexOption); + t.Load("/foo/9").Skip(2).Read(Tokens.RegexpEnd); // TODO: unexpected token 9 + t.Load("/foo/esuniiimmmxxxooo").Skip(2). + Read(RubyRegexOptions.IgnoreCase | RubyRegexOptions.Multiline | RubyRegexOptions.Extended | RubyRegexOptions.FIXED | RubyRegexOptions.UTF8); + + t.Expect(); + } + + private void StringLiterals1() { + AssertTokenizer t = AssertTokens(); + + for (int i = 0; i < 128; i++) { + switch (i) { + case '(': + case '{': + case '[': + case '<': + case 'Q': + case 'q': + case 'W': + case 'w': + case 'x': + case 'r': + case 's': + break; + + default: + var str = "%" + (char)i + "foo" + (char)i; + + if (Tokenizer.IsDecimalDigit(i)) { + t.Load(str)[(Tokens)'%'][Tokens.Integer][Tokens.Identifier].Expect(Errors.UnknownQuotedStringType).EOF(); + } else if (Tokenizer.IsUpperLetter(i)) { + t.Load(str)[(Tokens)'%'][Tokens.ConstantIdentifier].Expect(Errors.UnknownQuotedStringType).EOF(); + } else if (Tokenizer.IsLowerLetter(i)) { + t.Load(str)[(Tokens)'%'][Tokens.Identifier].Expect(Errors.UnknownQuotedStringType).EOF(); + } else { + t.Load(str)[Tokens.StringBeg]["foo"][Tokens.StringEnd].EOF(); + } + break; + } + } + + t.Expect(); + } + + private void Escapes1() { + AssertTokenizer t = AssertTokens(); + // hexa: + t.Load("\"\\x\n20\"")[Tokens.StringBeg]["?\n20"][Tokens.StringEnd].Expect(Errors.InvalidEscapeCharacter); + t.Load("\"\\x2\n0\"")[Tokens.StringBeg]["\u0002\n0"][Tokens.StringEnd].EOF(); + t.Load("\"\\x20\n\"")[Tokens.StringBeg][" \n"][Tokens.StringEnd].EOF(); + + // octal: + t.Load("\"\\0\n40\"")[Tokens.StringBeg]["\0\n40"][Tokens.StringEnd].EOF(); + t.Load("\"\\04\n0\"")[Tokens.StringBeg]["\u0004\n0"][Tokens.StringEnd].EOF(); + t.Load("\"\\040\n\"")[Tokens.StringBeg][" \n"][Tokens.StringEnd].EOF(); + t.Load("\"\\123\"")[Tokens.StringBeg]["S"][Tokens.StringEnd].EOF(); + + t.Expect(); + } + + [Options(Compatibility = RubyCompatibility.Ruby19)] + private void UnicodeEscapes1() { + AssertTokenizer t = AssertTokens(); + + int[] values = new[] { 0x20, 0x102020, 0x20, 0x20, 0 }; + int[] width = new[] { 2, 6, 6, 5, 1 }; + + for (int i = 0; i < values.Length; i++) { + t.Load(@"""\u{" + i.ToString("x" + width[i]) + @"}""")[Tokens.StringBeg][Char.ConvertFromUtf32(i)][Tokens.StringEnd].EOF(); + } + + t.Load(@":""\u{123456}""")[Tokens.Symbeg][Tokens.StringContent].Expect(Errors.TooLargeUnicodeCodePoint); + t.Load(@":""\u{0}""")[Tokens.Symbeg][Tokens.StringContent].Expect(Errors.NullCharacterInSymbol); + t.Load(@":""\u0000""")[Tokens.Symbeg][Tokens.StringContent].Expect(Errors.NullCharacterInSymbol); + t.Load(@":""\u111""")[Tokens.Symbeg][Tokens.StringContent].Expect(Errors.InvalidEscapeCharacter); + t.Load(@":""\u""")[Tokens.Symbeg][Tokens.StringContent].Expect(Errors.InvalidEscapeCharacter); + t.Load(@":""\u{123""")[Tokens.Symbeg][Tokens.StringContent].Expect(Errors.InvalidEscapeCharacter); + t.Load(@":""\u{123g}""")[Tokens.Symbeg][Tokens.StringContent].Expect(Errors.InvalidEscapeCharacter); + + // regex: + t.Load(@"/\x20/")[Tokens.RegexpBeg][@"\x20"][Tokens.RegexpEnd].EOF(); + t.Load(@"/\u1234/")[Tokens.RegexpBeg][@"\u1234"][Tokens.RegexpEnd].EOF(); + t.Load(@"/\u{101234}/")[Tokens.RegexpBeg][@"\u{101234}"][Tokens.RegexpEnd].EOF(); + + // braces: + t.Load(@"%{{\u{05d0}}}")[Tokens.StringBeg]["{\u05d0}"][Tokens.StringEnd].EOF(); + + // eoln in the middle of \u escape: + t.Load("\"\\u0020\n\"")[Tokens.StringBeg][" \n"][Tokens.StringEnd].EOF(); + t.Load("\"\\u002\n0\"")[Tokens.StringBeg]["?002\n0"][Tokens.StringEnd].Expect(Errors.InvalidEscapeCharacter).EOF(); + t.Load("\"\\u00\n20\"")[Tokens.StringBeg]["?00\n20"][Tokens.StringEnd].Expect(Errors.InvalidEscapeCharacter).EOF(); + t.Load("\"\\u0\n020\"")[Tokens.StringBeg]["?0\n020"][Tokens.StringEnd].Expect(Errors.InvalidEscapeCharacter).EOF(); + t.Load("\"\\u\n0020\"")[Tokens.StringBeg]["?\n0020"][Tokens.StringEnd].Expect(Errors.InvalidEscapeCharacter).EOF(); + + t.Expect(); + } + + [Options(Compatibility = RubyCompatibility.Ruby18)] + private void UnicodeEscapes2() { + AssertTokenizer t = AssertTokens(); + + t.Load(@":""\u{123456789}""")[Tokens.Symbeg][@"u{123456789}"][Tokens.StringEnd].EOF(); + t.Load(@":""\u123456789""")[Tokens.Symbeg][@"u123456789"][Tokens.StringEnd].EOF(); + t.Load(@"/\u1234/")[Tokens.RegexpBeg][@"\u1234"][Tokens.RegexpEnd].EOF(); + t.Load(@"/\u{101234}/")[Tokens.RegexpBeg][@"\u{101234}"][Tokens.RegexpEnd].EOF(); + + t.Expect(); + } + + private void Heredoc1() { + AssertTokenizer t = AssertTokens(); + + t.Load("< source1.Execute(), @"#"); + + // default hosted encoding is UTF8: + var source2 = Context.CreateSnippet("p __ENCODING__", SourceCodeKind.Expression); + AssertOutput(() => source2.Execute(), @"#"); + } + + [Options(Compatibility = RubyCompatibility.Ruby18)] + private void Encoding3() { + AssertExceptionThrown(() => + CompilerTest("__ENCODING__") + ); + + // ignores preamble: + Context.CreateFileUnit("foo.rb", "# enCoding = UNDEFINED_ENCODING").Execute(); + } + + /// + /// The default hosting encoding in 1.8 compat mode is BINARY. + /// + [Options(Compatibility = RubyCompatibility.Ruby18)] + private void Encoding4() { + AssertOutput(() => CompilerTest(@" +p ""a\200\201\202b"" +"), @" +""a\200\201\202b"" +"); + } + + /// + /// The default hosting encoding in 1.9 compat mode is UTF8. + /// + [Options(Compatibility = RubyCompatibility.Ruby19)] + private void Encoding5() { + // TODO: we need to apply encoding in tokenizer on non-unicode escapes are given in an encoded string + XAssertOutput(() => CompilerTest(@" +p ""a\200\201\202"" +"), @" +""a\x80\x81\x82"" +"); + } + + [Options(Compatibility = RubyCompatibility.Ruby19)] + private void Encoding_Host1() { + Encoding_HostHelper(Encoding.UTF8, "\u0394", true); + Encoding_HostHelper(Encoding.UTF32, "\u0394", false); + } + + private void Encoding_HostHelper(Encoding/*!*/ encoding, string/*!*/ specialChars, bool shouldSucceed) { + var preamble = "# coding = " + encoding.WebName + "\r\n"; + var src = "$X = '" + specialChars + "'"; + + var content = new List(); + content.AddRange(BinaryEncoding.Instance.GetBytes(preamble)); + content.AddRange(encoding.GetBytes(src)); + + var source = Engine.CreateScriptSource(new BinaryContentProvider(content.ToArray()), null); + + // test encoding: + try { + var detectedEncoding = source.DetectEncoding(); + Assert(encoding.WebName == detectedEncoding.WebName); + } catch (IOException) { + Assert(!shouldSucceed); + } + + // test content: + try { + var code = source.GetCode(); + Assert(StringComparer.Ordinal.Equals(code, preamble + src)); + } catch (IOException) { + Assert(!shouldSucceed); + } + } + + // Ruby preamble overrides the encoding's preamble (BOM) + [Options(Compatibility = RubyCompatibility.Ruby19)] + private void Encoding_Host2() { + var src = "# encoding: ASCII-8BIT\r\n$X = '\u0394'"; + var binsrc = Encoding.UTF8.GetBytes(src); + + var content = new List(); + content.AddRange(Encoding.UTF8.GetPreamble()); + content.AddRange(binsrc); + var source = Engine.CreateScriptSource(new BinaryContentProvider(content.ToArray()), null); + var encoding = source.DetectEncoding(); + Assert(encoding == BinaryEncoding.Instance); + var actualCode = source.GetCode(); + + // \u0394 is encoded in 2 bytes, which are represented by 2 characters in binary encoding: + Assert(actualCode.Length == src.Length + 1); + Assert(actualCode.Length == binsrc.Length); + } + + // TODO: + public void BlockParameterSyntax1() { + // "yield(&p)" -> block argument should not be given + // "yield &p" -> block argument should not be given + // "yield 1=>2, &p" -> block argument should not be given + } + + private void AstLocations1() { + // DumpExpression uses private reflection: + if (_driver.PartialTrust) return; + + var sourceUnit = Context.CreateSnippet(@" +def add a,b + a + b +end + +add 1, 1 +add 'foo', 'bar' +", SourceCodeKind.Expression); + + var options = new RubyCompilerOptions(); + var parser = new Parser(); + var tokens = new List>(); + + parser.TokenSink = (token, span) => { tokens.Add(new KeyValuePair(span, token)); }; + var ast = parser.Parse(sourceUnit, options, Context.RuntimeErrorSink); + + const int Id = 0x12345678; + + var lambda = CallSiteTracer.Transform(ast, sourceUnit, options, Id); + var code = new ScriptCode(lambda, sourceUnit); + + var locations = new List(); + CallSiteTracer.Register((context, args, result, id, location) => { + locations.Add(location); + Debug.Assert(id == Id); + Debug.Assert(location > 0); + + //Console.WriteLine("-- {0} ---------", location); + //Console.WriteLine(this); + //Console.WriteLine(AstUtils.DumpExpression(result.Restrictions.ToExpression())); + //Console.WriteLine(); + //Console.WriteLine(AstUtils.DumpExpression(result.Expression)); + //Console.WriteLine("----------------"); + }); + + code.Run(); + + Debug.Assert(locations.Count == 4 && locations[0] == 31 && locations[1] == 19 && locations[2] == 41 && locations[3] == 19); + } + + #region Helpers + + private void TestBigInt(string/*!*/ number, int @base) { + TestBigInt(number, @base, 10); + } + + private void TestBigInt(string/*!*/ number, int @base, int repeat) { + Stopwatch optimizedTime = new Stopwatch(); + Stopwatch universalTime = new Stopwatch(); + + StringBuilder n = new StringBuilder(number); + for (int j = 0; j < repeat; j++) { + n.Append(number); + } + number = n.ToString(); + + for (int i = 0; i < number.Length - 1; i++) { + string sub = number.Substring(i); + string s = sub.Replace("_", "").ToUpper(); + BigInteger b; + Tokenizer.BignumParser p = new Tokenizer.BignumParser(); + p.Position = 0; + p.Buffer = s.ToCharArray(); + + optimizedTime.Start(); + b = p.Parse(s.Length, @base); + optimizedTime.Stop(); + + Assert(b.ToString((uint)@base) == s.TrimStart('0')); + + p.Position = 0; + + universalTime.Start(); + b = p.ParseDefault(s.Length, (uint)@base); + universalTime.Stop(); + + Assert(b.ToString((uint)@base) == s.TrimStart('0')); + } + + if (repeat != 0) { + Console.WriteLine("{0}: optimized = {1}ms, universal = {2}ms", + @base, + optimizedTime.ElapsedMilliseconds, + universalTime.ElapsedMilliseconds); + } + } + + public void TestCategorizer(ScriptEngine engine, string src, int charCount, params TokenInfo[] expected) { + if (charCount == -1) charCount = src.Length; + + TokenCategorizer categorizer = engine.GetService(); + + categorizer.Initialize(null, engine.CreateScriptSourceFromString(src), SourceLocation.MinValue); + IEnumerable actual = categorizer.ReadTokens(charCount); + + int i = 0; + foreach (TokenInfo info in actual) { + Assert(i < expected.Length); + if (!info.Equals(expected[i])) { +#if DEBUG + Console.WriteLine(""); // TODO: Fix Ruby +#endif + Assert(false); + } + i++; + } + Assert(i == expected.Length); + } + + private AssertTokenizer/*!*/ AssertTokenBigInteger(string/*!*/ source, uint @base) { + return new AssertTokenizer(Context).Load(source).ReadBigInteger(source.Replace("_", "").TrimStart('0'), @base); + } + + private AssertTokenizer/*!*/ AssertTokens(string/*!*/ source) { + return new AssertTokenizer(Context).Load(source); + } + + private AssertTokenizer/*!*/ AssertTokens() { + return new AssertTokenizer(Context); + } + + #endregion + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Parser/Sources.txt b/merlin/main/languages/ruby/IronRuby.Tests/Parser/Sources.txt new file mode 100644 index 0000000000..7ae7730ebf --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Parser/Sources.txt @@ -0,0 +1,2 @@ +%MERLIN_ROOT%\Languages\Ruby +%MERLIN_ROOT%\..\External\Languages\Ruby diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Parser/TokenizerTestDriver.cs b/merlin/main/languages/ruby/IronRuby.Tests/Parser/TokenizerTestDriver.cs new file mode 100644 index 0000000000..4c2f08ccfb --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Parser/TokenizerTestDriver.cs @@ -0,0 +1,311 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Dynamic; +using System.Text; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using IronRuby.Builtins; +using IronRuby.Compiler; +using IronRuby.Compiler.Ast; +using IronRuby.Runtime; + +namespace IronRuby.Tests { + public class TokenizerTestDriver { + private readonly RubyContext/*!*/ _context; + private string _targetDir; + private string _sources; + private TextWriter _log; + private TextWriter _exceptions; + private TextWriter _regexLog; + private TextWriter _parserLog; + private string _currentSourceFile; + private bool _logProductions; + + public TokenizerTestDriver(RubyContext/*!*/ context) { + _context = context; + _log = Console.Out; + } + + // /tokenizer + public bool ParseArgs(List/*!*/ args) { + int argIndex; + if ((argIndex = args.IndexOf("/prod")) != -1) { + _logProductions = true; + args.RemoveAt(argIndex); + } else { + _logProductions = false; + } + + _targetDir = (args.Count > 0) ? args[0] : @"C:\RubyTokens"; + _sources = (args.Count > 1) ? args[1] : @"..\..\Languages\Ruby\IronRuby.Tests\Parser\Sources.txt"; + return true; + } + + private class AssertLog : TraceListener { + private readonly TokenizerTestDriver/*!*/ _driver; + + public AssertLog(TokenizerTestDriver/*!*/ driver) { + _driver = driver; + } + + public override void Fail(string message, string detail) { + _driver.WriteException("ASSERTION FAILED:\n" + message + "\n" + detail); + } + + public override void Write(string message) { + _driver._exceptions.Write(message); + } + + public override void WriteLine(string message) { + _driver._exceptions.WriteLine(message); + } + } + + public int RunTests() { + + try { + Directory.Delete(_targetDir, true); + } catch (DirectoryNotFoundException) { + } catch (Exception e) { + Console.WriteLine("Output directory cannot be deleted: {0}", e); + return -1; + } + + Directory.CreateDirectory(_targetDir); + _exceptions = File.CreateText(Path.Combine(_targetDir, "Exceptions.log")); + _regexLog = File.CreateText(Path.Combine(_targetDir, "Regex.log")); + _parserLog = File.CreateText(Path.Combine(_targetDir, "Parser.log")); + + // redirect failed assertions to the _exceptions file + Trace.Listeners.Clear(); + Trace.Listeners.Add(new AssertLog(this)); + + string[] lines; + + try { + lines = File.ReadAllLines(_sources); + } catch (Exception e) { + Console.WriteLine("Source file not found: {0}", e); + return -1; + } + + _log.WriteLine("Scanning files ..."); + + List files = new List(); + + for (int i = 0; i < lines.Length; i++) { + string path = lines[i]; + + int comment = path.IndexOf('#'); + if (comment >= 0) { + path = path.Substring(0, comment); + } + + StringBuilder sb = new StringBuilder(path); + foreach (DictionaryEntry envVar in Environment.GetEnvironmentVariables()) { + sb.Replace("%" + (string)envVar.Key + "%", (string)envVar.Value); + } + + path = sb.ToString().Trim(); + + if (path.Length > 0) { + if (File.Exists(path)) { + files.Add(Path.GetFullPath(path)); + } else if (Directory.Exists(path)) { + ScanDirs(files, path); + } else { + Console.Error.WriteLine("Error: {0}({1}): {2} doesn't exist", _sources, i + 1, path); + } + } + } + + _log.WriteLine(); + _log.WriteLine(); + + files.Sort(); + foreach (string file in files) { + TokenizeFile(file); + } + + return 0; + } + + private void ScanDirs(List/*!*/ files, string/*!*/ dir) { + _log.Write(dir); + _log.Write(' '); + + foreach (string file in Directory.GetFiles(dir, "*.rb")) { + files.Add(Path.GetFullPath(Path.Combine(dir, file))); + _log.Write('.'); + } + + _log.WriteLine(); + + foreach (string subdir in Directory.GetDirectories(dir)) { + ScanDirs(files, Path.Combine(dir, subdir)); + } + } + + internal class ErrorLog : ErrorCounter { + private List/*!*/ _errors = new List(); + + public List/*!*/ Errors { get { return _errors; } } + + public override void Add(SourceUnit source, string message, SourceSpan span, int errorCode, Severity severity) { + CountError(severity); + _errors.Add(String.Format("{0}: {1}", severity, message)); + } + } + + public void TokenizeFile(string/*!*/ path) { + _log.WriteLine(path); + + try { + string fullPath = Path.GetFullPath(path); + string root = Path.GetPathRoot(fullPath); + string outputPath = Path.ChangeExtension(Path.Combine(_targetDir, fullPath.Substring(root.Length).TrimEnd('\'', '/')), ".txt"); + Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + + using (TextWriter output = File.CreateText(outputPath)) { + + output.WriteLine(fullPath); + output.WriteLine(); + output.WriteLine("Tokens:"); + output.WriteLine(); + + ErrorLog errors = new ErrorLog(); + Parser parser = new Parser(); + + parser.TokenSink = delegate(Tokens token, SourceSpan span) { + DumpTokenDetail(output, parser.Tokenizer, token); + }; + + if (_logProductions) { +#if DEBUG + parser.EnableLogging(new CoverageParserLogger(parser, _parserLog)); +#endif + } + + _currentSourceFile = path; + + SourceUnitTree ast = null; + try { + ast = parser.Parse(_context.CreateFileUnit(path), new RubyCompilerOptions(), errors); + } catch (Exception e) { + WriteException(e.ToString()); + } + + output.WriteLine(); + + if (errors.ErrorCount + errors.FatalErrorCount + errors.WarningCount > 0) { + output.WriteLine(); + output.WriteLine("Errors:"); + + foreach (string error in errors.Errors) { + output.WriteLine(error); + } + } else { + Debug.Assert(ast != null); + DumpRegularExpressions(ast); + } + + output.WriteLine("."); + } + } catch (Exception e) { + _log.WriteLine("!{0}", e.Message); + } finally { + _currentSourceFile = null; + _regexLog.Flush(); + _parserLog.Flush(); + } + } + + public void DumpTokenDetail(TextWriter/*!*/ output, Tokenizer/*!*/ tokenizer, Tokens token) { + TokenValue value = tokenizer.TokenValue; + + output.Write("{0}: ", Parser.TerminalToString((int)token)); + + switch (value.Type) { + case TokenValueType.None: + break; + + case TokenValueType.Double: + output.Write("{0}D", value.Double); + break; + + case TokenValueType.Integer: + output.Write(value.Integer); + break; + + case TokenValueType.BigInteger: + output.Write("{0}BI", value.BigInteger.ToString(10)); + break; + + case TokenValueType.RegexOptions: + output.Write("RegexOptions({0})", (RubyRegexOptions)value.Integer); + break; + + case TokenValueType.String: + output.Write("String(\"{0}\")", Parser.EscapeString(value.String)); + break; + + case TokenValueType.StringTokenizer: + output.Write(value.StringTokenizer); + break; + } + + output.Write(' '); + output.Write(tokenizer.LexicalState); + output.WriteLine(); + } + + private void WriteException(string/*!*/ str) { + _exceptions.WriteLine(); + _exceptions.WriteLine(_currentSourceFile); + _exceptions.WriteLine(); + _exceptions.WriteLine(str); + _exceptions.WriteLine(new String('-', 50)); + _exceptions.Flush(); + } + + private void DumpRegularExpressions(SourceUnitTree/*!*/ ast) { + new RegexDumper(_regexLog).Walk(ast); + } + + private class RegexDumper : Walker { + private readonly TextWriter/*!*/ _regexLog; + + public RegexDumper(TextWriter/*!*/ regexLog) { + _regexLog = regexLog; + } + + public override bool Enter(RegularExpression/*!*/ node) { + if (node.Pattern.Count == 1) { + var literal = node.Pattern[0] as StringLiteral; + if (literal != null) { + _regexLog.WriteLine("/{0}/{{{1}}}", literal.Value, node.Options); + } + } + return true; + } + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Properties/AssemblyInfo.cs b/merlin/main/languages/ruby/IronRuby.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..3453fa41f6 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; +using IronRuby.Runtime; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RubyTestHost")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("RubyTestHost")] +[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("facfd637-0df3-4eed-8672-0d39c30a5259")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion(RubyContext.IronRubyVersionString)] +[assembly: AssemblyFileVersion(RubyContext.IronRubyVersionString)] +[assembly: AllowPartiallyTrustedCallers] +[assembly: SecurityTransparent] \ No newline at end of file diff --git a/merlin/main/languages/ruby/IronRuby.Tests/RubyTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/RubyTests.cs new file mode 100644 index 0000000000..ab45ad181b --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/RubyTests.cs @@ -0,0 +1,468 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +namespace IronRuby.Tests { + + public partial class Tests { + public Tests(Driver/*!*/ driver) { + _driver = driver; + + _methods = new Action[] { + Scenario_Startup, // must be the first test + Scenario_ParserLogging, + Scenario_RubyTokenizer1, + Identifiers1, + Identifiers2, + Scenario_ParseBigInts1, + Scenario_ParseNumbers1, + Scenario_ParseInstanceClassVariables1, + Scenario_ParseRegex1, + + Scenario_RubyCategorizer1, + Scenario_RubyNameMangling1, + Scenario_RubyNameMangling2, + + OverloadResolution_Block, + + Scenario_RubySimpleCall1, + Scenario_RubySimpleCall2, + Scenario_RubySimpleCall3, + Scenario_RubySimpleCall4, + Scenario_RubySimpleCall5, + + NumericLiterals1, + StringLiterals1, + Escapes1, + UnicodeEscapes1, + UnicodeEscapes2, + + Heredoc1, + Symbols1, + UnicodeIdentifiers1, + + Encoding1, + Encoding2, + Encoding3, + Encoding4, + Encoding5, + Encoding_Host1, + Encoding_Host2, + + AstLocations1, + + Scenario_Globals1, + + Scenario_RubyMethodMissing1, + Scenario_RubyMethodMissing2, + Scenario_RubySingletonConstants1, + Scenario_RubyMath1, + + StringsPlus, + Strings0, + Strings1, + Strings2, + Strings3, + Strings4, + Strings5, + Strings6, + Strings7, + Strings8, + // TODO: Strings9, + + Regex1, + Regex2, + RegexTransform1, + RegexTransform2, + RegexEscape1, + RegexCondition1, + RegexCondition2, + + Scenario_RubyScopeParsing, + Scenario_RubyScopes1, + Scenario_RubyScopes2A, + Scenario_RubyScopes2B, + Scenario_RubyScopes3, + Scenario_RubyScopes4, + Scenario_RubyScopes5, + Scenario_RubyScopes6, + + Send1, + + AttributeAccessors1, + AttributeAccessors2, + + Scenario_RubyDeclarations1, + Scenario_RubyDeclarations1A, + Scenario_RubyDeclarations1B, + Scenario_RubyDeclarations1C, + Scenario_RubyDeclarations2, + Scenario_RubyDeclarations3, + Scenario_RubyDeclarations4, + Scenario_RubyInclusions1, + Scenario_RubyClassVersions1, + Scenario_RubyClassVersions2, + InvokeMemberCache1, + Scenario_RubyBlockExpressions1, + + Constants1A, + Constants1B, + ConstantNames, + Constants3, + Constants4, + UnqualifiedConstants1, + LoadAndGlobalConstants, + GlobalConstantsInterop, + + Scenario_ClassVariables1, + Scenario_ClassVariables2, + Scenario_RubyLocals1, + Scenario_MethodAliases1, + Scenario_MethodAliases2, + Scenario_MethodUndef1, + Scenario_MethodUndef2, + MethodUndefExpression, + Scenario_Assignment1, + SetterCallValue, + SimpleInplaceAsignmentToIndirectLeftValues1, + MemberAssignmentExpression1, + MemberAssignmentExpression2, + MemberAssignmentExpression3, + + Scenario_ParallelAssignment1, + Scenario_ParallelAssignment2, + Scenario_ParallelAssignment4, + Scenario_ParallelAssignment5, + Scenario_ParallelAssignment6, + Scenario_ParallelAssignment7, + Scenario_ParallelAssignment8, + Scenario_ParallelAssignment9, + Scenario_ParallelAssignment10, + + BlockEmpty, + Scenario_RubyBlocks0, + Scenario_RubyBlocks_Params1, + Scenario_RubyBlocks_Params2, + ProcYieldCaching1, + ProcCallCaching1, + ProcSelf1, + Scenario_RubyBlocks2, + Scenario_RubyBlocks3, + Scenario_RubyBlocks5, + Scenario_RubyBlocks6, + Scenario_RubyBlocks7, + Scenario_RubyBlocks8, + Scenario_RubyBlocks9, + Scenario_RubyBlocks10, + Scenario_RubyBlocks11, + Scenario_RubyBlocks12, + Scenario_RubyBlocks13, + Scenario_RubyBlocks14, + Scenario_RubyBlocks15, + Scenario_RubyBlocks16, + Scenario_RubyBlocks17, + BlockArity1, + + Scenario_RubyBlockArgs1, + Scenario_RubyProcYieldArgs1, + Scenario_RubyProcCallArgs1A, + Scenario_RubyProcCallArgs1B, + Scenario_RubyBlockArgs2, + Scenario_RubyProcCallArgs2A, + Scenario_RubyProcCallArgs2B, + Scenario_RubyProcCallArgs2C, + Scenario_RubyProcCallArgs2D, + Scenario_RubyBlockArgs3, + Scenario_RubyBlockArgs4A, + Scenario_RubyBlockArgs4B, + Scenario_RubyBlockArgs5, + Scenario_RubyBlockArgs6, + // TODO: Scenario_RubyBlockArgs7, + Scenario_RubyBlockArgs8, + Scenario_RubyBlockArgs9, + Scenario_RubyBlockArgs10, + Proc_RhsAndBlockArguments1, + + Scenario_RubyProcs1, + RubyProcArgConversion1, + RubyProcArgConversion2, + RubyProcArgConversion3, + RubyProcArgConversion4, + ProcNew1, + ProcNew2, + DefineMethod1, + DefineMethod2, + + Scenario_RubyInitializers0, + Scenario_RubyInitializers1, + Scenario_RubyInitializers2A, + Scenario_RubyInitializers2B, + Scenario_RubyInitializers3, + Scenario_RubyInitializers4A, + Scenario_RubyInitializers4B, + Scenario_RubyInitializers4C, + Scenario_RubyInitializers5, + RubyInitializersCaching1, + RubyInitializersCaching2, + RubyAllocators1, + + Scenario_RubyForLoop1, + // TODO: Python interop: Scenario_RubyForLoop2, + Scenario_RubyWhileLoop1, + Scenario_RubyWhileLoop2, + Scenario_RubyWhileLoop3, + Scenario_RubyWhileLoop4, + Scenario_RubyWhileLoop5, + Scenario_RubyWhileLoop6, + Scenario_RubyUntilLoop1, + Scenario_WhileLoopCondition1, + PostTestWhile1, + PostTestUntil1, + WhileModifier1, + UntilModifier1, + WhileModifier2, + UntilModifier2, + + RangeConditionInclusive1, + RangeConditionExclusive1, + RangeCondition1, + RangeCondition2, + + Scenario_RubyClosures1, + Scenario_RubyParams1, + Scenario_RubyParams2, + Scenario_RubyReturn1, + Scenario_RubyArrays1, + Scenario_RubyArrays2, + Scenario_RubyArrays3, + Scenario_RubyArrays4, + Scenario_RubyArrays5, + Scenario_RubyArrays6, + Scenario_RubyHashes1A, + Scenario_RubyHashes1B, + Scenario_RubyHashes1C, + Scenario_RubyHashes2, + Scenario_RubyHashes3, + Scenario_RubyHashes4, + Scenario_RubyArgSplatting1, + Scenario_RubyArgSplatting2, + Scenario_RubyArgSplatting3, + Scenario_RubyBoolExpressions1, + Scenario_RubyBoolExpressions2, + Scenario_RubyBoolExpressions3, + Scenario_RubyBoolExpressions4, + Scenario_RubyBoolExpressionsWithReturn1, + Scenario_RubyBoolExpressionsWithReturn2, + TernaryConditionalWithJumpStatements1, + TernaryConditionalWithJumpStatements2, + Scenario_RubyBoolAssignment, + Scenario_RubyIfExpression1, + Scenario_RubyIfExpression2, + Scenario_RubyUnlessExpression1, + Scenario_RubyConditionalExpression1, + ConditionalStatement1, + ConditionalStatement2, + + Scenario_UninitializedVars1, + Scenario_UninitializedVars2, + InstanceVariables1, + InstanceVariables2, + RubyHosting1, + RubyHosting2, + RubyHosting3, + RubyHosting4, + + Scenario_RubyConsole1, + // TODO: interop, hosting: Scenario_RubyConsole2, + Scenario_RubyConsole3, + Scenario_RubyConsole4, + ObjectOperations1, + ObjectOperations2, + // TODO (python needs to run this suite): PythonInterop1, + // TODO (python needs to run this suite): PythonInterop2, + CustomTypeDescriptor1, + CustomTypeDescriptor2, + + Loader_Assemblies1, + + Require1, + RequireInterop1, + Load1, + LibraryLoader1, + + ClrFields1, + ClrTypes1, + ClrGenerics1, + ClrMethods1, + ClrMethodsVisibility1, + ClrInterfaces1, + ClrRequireAssembly1, + ClrInclude1, + ClrNew1, + ClrAlias1, + // TODO: ClrEnums1, + ClrDelegates1, + ClrDelegates2, + ClrEvents1, + ClrOverride1, + ClrConstructor1, + // TODO: ClrConstructor2, + Scenario_RubyEngine1, + Scenario_RubyInteractive, + + Scenario_RubyReturnValues1, + Scenario_RubyReturnValues2, + Scenario_RubyReturnValues3, + Scenario_RubyReturnValues4, + Scenario_RubyReturnValues5, + Scenario_RubyReturnValues6, + + Scenario_RubyExceptions1, + Scenario_RubyExceptions1A, + Scenario_RubyExceptions2A, + Scenario_RubyExceptions2B, + Scenario_RubyExceptions2C, + Scenario_RubyExceptions2D, + Scenario_RubyExceptions3, + Scenario_RubyExceptions4, + Scenario_RubyExceptions5, + Scenario_RubyExceptions6, + Scenario_RubyExceptions7, + Scenario_RubyExceptions8, + Scenario_RubyExceptions9, + Scenario_RubyExceptions10, + Scenario_RubyExceptions11, + Scenario_RubyExceptions12, + Scenario_RubyExceptions12A, + Scenario_RubyExceptions13, + Scenario_RubyExceptions14, + Scenario_RubyExceptions15, + Scenario_RubyExceptions16, + Scenario_RubyExceptions_Globals, + Scenario_RubyRescueStatement1, + Scenario_RubyRescueExpression1, + Scenario_RubyRescueExpression2, + ExceptionArg1, + ExceptionArg2, + RescueSplat1, + RescueSplat2, + RescueSplat3, + + ClassVariables1, + UnqualifiedConstants2, + + AliasMethodLookup1, + + UndefMethodLookup, + MethodAdded1, + MethodLookup1, + VisibilityCaching1, + ModuleFunctionVisibility1, + + Scenario_Singletons1, + Scenario_Singletons2, + Scenario_Singletons3, + Scenario_Singletons4, + Scenario_Singletons5, + SingletonCaching1, + Scenario_ClassVariables_Singletons, + AllowedSingletons1, + + Super1, + SuperParameterless1, + SuperParameterless2, + SuperParameterless3, + Super2, + SuperInDefineMethod1, + SuperInDefineMethod2, + // TODO: SuperInDefineMethod3, + SuperInTopLevelCode1, + SuperInAliasedDefinedMethod1, + + Scenario_RubyDefinedOperator_Globals1, + Scenario_RubyDefinedOperator_Globals2, + Scenario_RubyDefinedOperator_Methods1, + Scenario_RubyDefinedOperator_Methods2, + Scenario_RubyDefinedOperator_Constants1, + Scenario_RubyDefinedOperator_Constants2, + Scenario_RubyDefinedOperator_Expressions1, + Scenario_RubyDefinedOperator_InstanceVariables1, + Scenario_RubyDefinedOperator_ClassVariables1, + Scenario_RubyDefinedOperator_ClassVariables2, + Scenario_RubyDefinedOperator_Yield1, + Scenario_RubyDefinedOperator_Locals1, + + Scenario_ModuleOps_Methods, + Scenario_MainSingleton, + + Scenario_RubyThreads1, + Scenario_YieldCodeGen, + Methods, + MethodAliasExpression, + ClassDuplication1, + ClassDuplication2, + ClassDuplication3, + ClassDuplication4, + ClassDuplication5, + StructDup1, + ClassDuplication6, + Clone1, + Dup1, + MetaModules1, + MetaModulesDuplication1, + + // eval, binding: + Eval1, + Eval2, + Eval3, + EvalReturn1, + EvalReturn2, + LocalNames1, + LocalNames2, + LocalNames3, + LocalNames4, + LiftedParameters1, + Binding1, + TopLevelBinding_RubyProgram, + EvalWithProcBinding1, + ModuleEvalProc1, + ModuleEvalProc2, + ModuleEvalProc3, + InstanceEvalProc1, + // TODO: InstanceEvalProc2, + ModuleInstanceEvalProc3, + ModuleClassNew1, + ModuleClassNew2, + ModuleEvalString1, + InstanceEvalString1, + ModuleEvalString2, + InstanceEvalString2, + ModuleInstanceEvalString3, + AliasInModuleEval1, + MethodAliasInModuleEval1, + SuperInModuleEval1, + + SuperEval1, + // TODO: SuperParameterlessEval1, + // TODO: SuperParameterlessEval2, + SuperInDefineMethodEval1, + + Backtrace1, + Backtrace2, + Backtrace3, + }; + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/AliasTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/AliasTests.cs new file mode 100644 index 0000000000..cd599ca75c --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/AliasTests.cs @@ -0,0 +1,226 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using IronRuby.Compiler; +using IronRuby.Compiler.Ast; + +namespace IronRuby.Tests { + + public partial class Tests { + + /// + /// alias keyword binds lexically while alias_method binds to self.class. + /// + public void Scenario_MethodAliases1() { + AssertOutput(delegate() { + CompilerTest(@" +module M + def y + puts 'M::y' + end + + def do_alias + alias aliased_y y + end + + def do_alias_method + self.class.send :alias_method, :method_aliased_y, :y + end +end + +class C + include M + + def y + puts 'C::y' + end +end + +c = C.new +c.do_alias +c.do_alias_method +c.aliased_y +c.method_aliased_y +"); + }, @" +M::y +C::y +"); + } + + public void Scenario_MethodAliases2() { + AssertOutput(delegate() { + CompilerTest(@" +class C + private + def foo; end + + public + alias bar foo + + p private_instance_methods(false).sort + p public_instance_methods(false).sort + + public :bar + + p private_instance_methods(false).sort + p public_instance_methods(false).sort +end +"); + }, @" +[""bar"", ""foo""] +[] +[""foo""] +[""bar""] +"); + } + + /// + /// Alias (unlike undef) looks up the method in Object. + /// + public void AliasMethodLookup1() { + AssertOutput(delegate() { + CompilerTest(@" +module Kernel; def m_kernel; end; end +class Object; def m_object; end; end + +module N + def m_n + end + + module M + alias x m_object rescue puts '!alias m_object' + alias x m_kernel rescue puts '!alias m_kernel' + alias x m_n rescue puts '!alias m_n' + end +end +"); + }, @" +!alias m_n +"); + } + + /// + /// "alias" uses the same lookup algorithm as method definition (see RubyScope.GetMethodDefinitionOwner). + /// + public void AliasInModuleEval1() { + AssertOutput(delegate() { + CompilerTest(@" +class D + def foo + puts 'D::foo' + end +end + +class C + def foo + puts 'C::foo' + end + + D.module_eval { + alias bar foo + } +end + +D.new.bar +"); + }, @" +D::foo +"); + } + + public void MethodAliasInModuleEval1() { + AssertOutput(delegate() { + CompilerTest(@" +class D + def foo + puts 'D::foo' + end +end + +class E + def foo + puts 'E::foo' + end +end + +class C + def foo + puts 'C::foo' + end + + D.module_eval { + E.send :alias_method, :bar, :foo + } +end + +E.new.bar +"); + }, @" +E::foo +"); + } + + public void MethodLookup1() { + AssertOutput(delegate() { + CompilerTest(@" +module Kernel + def k__; end +end + +class Module + def m__; end; +end + +module MyModule +end + +puts x = ((MyModule.send(:alias_method, :x, :k__); true) rescue false) +puts x = ((MyModule.send(:alias_method, :x, :m__); true) rescue false) + +puts x = ((MyModule.send(:public, :k__); true) rescue false) +puts x = ((MyModule.send(:public, :m__); true) rescue false) + +puts x = ((MyModule.send(:method, :k__); true) rescue false) +puts x = ((MyModule.send(:method, :m__); true) rescue false) + +puts x = ((MyModule.send(:undef_method, :k__); true) rescue false) +puts x = ((MyModule.send(:undef_method, :m__); true) rescue false) +"); + }, @" +true +false +true +false +true +true +false +false +"); + } + + public void MethodAliasExpression() { + AssertOutput(delegate() { + CompilerTest(@" +def b; end +p ((alias a b)) +", 1, 0); + }, "nil"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ArrayTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ArrayTests.cs new file mode 100644 index 0000000000..43e8fa2915 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ArrayTests.cs @@ -0,0 +1,100 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace IronRuby.Tests { + public partial class Tests { + + public void Scenario_RubyArrays1() { + AssertOutput(delegate() { + CompilerTest(@" +puts Array.[]('a','b','c') +puts Array[1,2,3] +puts ['e','f','g'] +"); + }, String.Format("a{0}b{0}c{0}1{0}2{0}3{0}e{0}f{0}g{0}", Environment.NewLine)); + } + + public void Scenario_RubyArrays2() { + AssertOutput(delegate() { + CompilerTest(@" +a = ['e',['f'],'g'] +a[2] = a[0] +print a[2] +print a[1][0] +"); + }, "ef"); + } + + public void Scenario_RubyArrays3() { + AssertOutput(delegate() { + CompilerTest(@" +puts %w{hello world} +puts %w cup> +puts %w{hello w#{0}r#{1}d} +puts %W{hello w#{0}r#{1}d} +"); + }, @" +hello +world +cup +cup +hello +w#{0}r#{1}d +hello +w0r1d +"); + } + + public void Scenario_RubyArrays4() { + AssertOutput(delegate() { + CompilerTest(@" +a = [*x = [1,2]] +puts a.object_id == x.object_id +puts a.inspect +"); + }, @" +true +[1, 2]"); + } + + public void Scenario_RubyArrays5() { + AssertOutput(delegate() { + CompilerTest(@" +a = [*4] +b = [*[4]] +c = [*[*4]] +d = [*[*[4]]] +puts a.inspect,b.inspect,c.inspect,d.inspect +"); + }, @" +[4] +[4] +[4] +[4] +"); + } + + public void Scenario_RubyArrays6() { + AssertOutput(delegate() { + CompilerTest(@" +a = [1,*[[],[*4], *[*[]]]] +puts a.inspect +"); + }, @"[1, [], [4]]"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/AssignmentTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/AssignmentTests.cs new file mode 100644 index 0000000000..221d224917 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/AssignmentTests.cs @@ -0,0 +1,416 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Tests { + public partial class Tests { + public void Scenario_Assignment1() { + AssertOutput(delegate() { + CompilerTest(@" +a = 1 +a += 2 +puts a"); + }, @"3"); + } + + /// + /// Order of evaluation. + /// + public void Scenario_ParallelAssignment1() { + AssertOutput(delegate() { + CompilerTest(@" +def one; puts 'one'; 1; end +def two; puts 'two'; 2; end +def three; puts 'three'; 3; end + +a,b = one,two,three +puts a.inspect,b.inspect +"); + }, @" +one +two +three +1 +2 +"); + } + + public void Scenario_ParallelAssignment2() { + AssertOutput(delegate() { + CompilerTest(@" +def foo() + x, y = 3, 4 + puts x, y + x, y = y, x + puts x, y + x, y = 1 + puts x, y + x, y = 5, 6, 7 + puts x, y + z = (x, y = 5, 6, 7) + puts z +end + +foo +"); + }, @" +3 +4 +4 +3 +1 +nil +5 +6 +5 +6 +7"); + } + + public void Scenario_ParallelAssignment4() { + AssertOutput(delegate() { + CompilerTest(@" +ra = (a = *4) +rb = (b = *[4]) +rc = (c = *[*4]) +rd = (d = 1,*4) +re = (e = 1,*[4]) +rf = (f = 1,*[*4]) +puts a.inspect,b.inspect,c.inspect,d.inspect,e.inspect,f.inspect +puts ra.inspect,rb.inspect,rc.inspect,rd.inspect,re.inspect,rf.inspect +"); + }, @" +4 +4 +4 +[1, 4] +[1, 4] +[1, 4] +4 +4 +4 +[1, 4] +[1, 4] +[1, 4] +"); + } + + public void Scenario_ParallelAssignment5() { + AssertOutput(delegate() { + CompilerTest(@" +r = (x,(y,(z1,z2)),(*u),v,w = 1,[*[1,2]],3,*4) + +puts 'r = ' + r.inspect, + 'x = ' + x.inspect, + 'y = ' + y.inspect, + 'z1 = ' + z1.inspect, + 'z2 = ' + z2.inspect, + 'u = ' + u.inspect, + 'v = ' + v.inspect, + 'w = ' + w.inspect +"); + }, @" +r = [1, [1, 2], 3, 4] +x = 1 +y = 1 +z1 = 2 +z2 = nil +u = [3] +v = 4 +w = nil +"); + } + + /// + /// Non-simple LHS, simple RHS. Difference between |LHS| > 0 (r0 and r3) and |LHS| == 0 (r1). + /// + public void Scenario_ParallelAssignment6() { + AssertOutput(delegate() { + CompilerTest(@" +r0 = (x,y = [1,2]) +r1 = (*v = [1,2]) +r2 = (*w = *[1,2]) +r3 = (p,*q = [1,2]) +puts r0.inspect, r1.inspect, r2.inspect, r3.inspect, '*' +puts x.inspect, y.inspect, '*', v.inspect, '*', w.inspect, '*', p.inspect, q.inspect +"); + }, @" +[1, 2] +[[1, 2]] +[1, 2] +[1, 2] +* +1 +2 +* +[[1, 2]] +* +[1, 2] +* +1 +[2] +"); + } + + /// + /// Simple LHS and splat only RHS. + /// + public void Scenario_ParallelAssignment7() { + AssertOutput(delegate() { + CompilerTest(@" +a = (ax = *1) +b = (bx = *[]) +c = (cx = *[1]) +d = (dx = *[1,2]) + +puts a.inspect, ax.inspect, b.inspect, bx.inspect, c.inspect, cx.inspect, d.inspect, dx.inspect +"); + }, @" +1 +1 +nil +nil +1 +1 +[1, 2] +[1, 2] +"); + } + + /// + /// Simple RHS. + /// + public void Scenario_ParallelAssignment8() { + AssertOutput(delegate() { + CompilerTest(@" +r1 = (a = [1,2]) +r2 = (b,c = [1,2]) +r3 = (d,e = *[1,2]) +r4 = (f,g = 1) + +puts r1.inspect, r2.inspect, r3.inspect, r4.inspect +puts b.inspect, c.inspect, d.inspect, e.inspect, f.inspect, g.inspect +"); + }, @" +[1, 2] +[1, 2] +[1, 2] +[1] +1 +2 +1 +2 +1 +nil +"); + } + + /// + /// Inner splat-only LHS. + /// + public void Scenario_ParallelAssignment9() { + AssertOutput(delegate() { + CompilerTest(@" +c = ((*a),(*b) = [1,2],[3,4]) +puts a.inspect, b.inspect, c.inspect +"); + }, @" +[[1, 2]] +[[3, 4]] +[[1, 2], [3, 4]] +"); + } + + /// + /// Recursion in L(1,-). + /// + public void Scenario_ParallelAssignment10() { + AssertOutput(delegate() { + CompilerTest(@" +ra = ((a,) = *[]) +rb = ((b,) = 1,*[]) +puts a.inspect, ra.inspect +puts b.inspect, rb.inspect +"); + }, @" +nil +[] +1 +[1] +"); + } + + /// + /// ArrayItemAccess and AttributeAccess read target ones in an in-place assignment. + /// + public void SimpleInplaceAsignmentToIndirectLeftValues1() { + AssertOutput(delegate { + CompilerTest(@" +class Array + def x; 1; end + def x= value; puts 'x=' end +end + +def foo + puts 'foo' + [0] +end + +p foo[0] += 1 +p foo::x += 2 +p foo[0] &&= 3 +p foo::x &&= 4 +p foo[0] ||= 5 +p foo::x ||= 6 +"); + }, @" +foo +1 +foo +x= +3 +foo +3 +foo +x= +4 +foo +0 +foo +1 +"); + } + + public void SetterCallValue() { + AssertOutput(delegate { + CompilerTest(@" +class C + def x= value + puts value + 'foo' + end + + def x value + puts value + puts 'foo' + end +end + +foo = C.new + +puts(foo.x=('bar')) +puts '---' +puts(foo.x('bar')) +"); + }, @" +bar +bar +--- +bar +foo +nil"); + + } + + private const string/*!*/ MemberAssignmentDefs = @" +class String + def + other + puts ""#{self} + #{other}"" + ""#{self}#{other}"" + end +end + +class C + def x= value + puts ""write: #{value}"" + end + + def x + puts ""read"" + $result + end +end + +def target + puts 'target' + C.new +end + +def rhs + puts 'rhs' + '_rhs' +end +"; + + public void MemberAssignmentExpression1() { + AssertOutput(delegate { + CompilerTest(MemberAssignmentDefs + @" +$result = 'result' +puts(target.x += rhs) +"); + }, @" +target +read +rhs +result + _rhs +write: result_rhs +result_rhs +"); + } + + public void MemberAssignmentExpression2() { + AssertOutput(delegate { + CompilerTest(MemberAssignmentDefs + @" +$result = true +puts(target.x &&= rhs) +puts +$result = false +puts(target.x &&= rhs) +"); + }, @" +target +read +rhs +write: _rhs +_rhs + +target +read +false +"); + } + + public void MemberAssignmentExpression3() { + AssertOutput(delegate { + CompilerTest(MemberAssignmentDefs + @" +$result = true +puts(target.x ||= rhs) +puts +$result = false +puts(target.x ||= rhs) +"); + }, @" +target +read +true + +target +read +rhs +write: _rhs +_rhs +"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/BacktraceTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/BacktraceTests.cs new file mode 100644 index 0000000000..2e0d17c2f5 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/BacktraceTests.cs @@ -0,0 +1,141 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +namespace IronRuby.Tests { + public partial class Tests { + private bool PreciseTraces { + get { return !_driver.PartialTrust || _driver.Interpret; } + } + + public void Backtrace1() { + AssertOutput(delegate() { + Engine.CreateScriptSourceFromString(@" +def goo() + foo() +end + +def foo() + bar() +end + +def bar() + baz() +end + +def baz() + puts caller[0..3] +end + +goo +", "Backtrace1.rb", SourceCodeKind.File).Execute(); + }, PreciseTraces ? @" +Backtrace1.rb:11:in `bar' +Backtrace1.rb:7:in `foo' +Backtrace1.rb:3:in `goo' +Backtrace1.rb:18 +" : @" +Backtrace1.rb:10:in `bar' +Backtrace1.rb:6:in `foo' +Backtrace1.rb:2:in `goo' +:0 +"); + } + + public void Backtrace2() { + AssertOutput(delegate() { + CompilerTest(@" +def goo() + foo() +end + +def foo() + bar() +end + +def bar() + baz() +end + +def baz() + raise +end + +begin + goo +rescue + puts $@[0..4] +end +"); + }, PreciseTraces ? @" +Backtrace2.rb:15:in `baz' +Backtrace2.rb:11:in `bar' +Backtrace2.rb:7:in `foo' +Backtrace2.rb:3:in `goo' +Backtrace2.rb:19 +" : @" +Backtrace2.rb:14:in `baz' +Backtrace2.rb:10:in `bar' +Backtrace2.rb:6:in `foo' +Backtrace2.rb:2:in `goo' +:0 +"); + } + + public void Backtrace3() { + AssertOutput(delegate() { + CompilerTest(@" +def goo() + foo() +rescue + raise +end + +def foo() + bar() +end + +def bar() + baz() +rescue + raise +end + +def baz() + raise +end + +begin + goo +rescue + puts $@[0..4] +end +"); + }, PreciseTraces ? @" +Backtrace3.rb:19:in `baz' +Backtrace3.rb:13:in `bar' +Backtrace3.rb:9:in `foo' +Backtrace3.rb:3:in `goo' +Backtrace3.rb:23 +" : @" +Backtrace3.rb:18:in `baz' +Backtrace3.rb:12:in `bar' +Backtrace3.rb:8:in `foo' +Backtrace3.rb:2:in `goo' +:0 +"); + } + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/BlockTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/BlockTests.cs new file mode 100644 index 0000000000..a80008b883 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/BlockTests.cs @@ -0,0 +1,1246 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace IronRuby.Tests { + + public partial class Tests { + public void BlockEmpty() { + CompilerTest("1.times { }"); + } + + public void Scenario_RubyBlocks0() { + AssertOutput(delegate() { + CompilerTest(@" +3.times { |x| print x } +"); + }, "012"); + } + + public void Scenario_RubyBlocks_Params1() { + AssertOutput(delegate() { + CompilerTest(@" +def y; yield 0,1,2,3,4,5,6,7,8,9; end + +y { |x0,x1,x2,x3,x4,x5,x6,x7,x8,x9| print x0,x1,x2,x3,x4,x5,x6,x7,x8,x9 } +"); + }, "0123456789"); + } + + public void Scenario_RubyBlocks_Params2() { + AssertOutput(delegate() { + CompilerTest(@" +def y; yield 0,1,2,3,4,5,6,7,8,9; end + +y { |x0,x1,x2,x3,x4,x5,x6,x7,*z| print x0,x1,x2,x3,x4,x5,x6,x7,z[0],z[1]; } +"); + }, "0123456789"); + } + + public void ProcYieldCaching1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + yield +end + +foo { print 'A' } +foo { print 'B' } +foo { print 'C' } +"); + }, "ABC"); + } + + public void ProcCallCaching1() { + AssertOutput(delegate() { + CompilerTest(@" +$p = lambda { puts 1 } +$q = lambda { puts 2 } + +$p.call +$q.call +"); + }, @" +1 +2 +"); + } + + public void ProcSelf1() { + AssertOutput(delegate() { + CompilerTest(@" +module M + 1.times { p self } +end +"); + }, @"M"); + } + + public void Scenario_RubyBlocks2() { + AssertExceptionThrown(delegate() { + CompilerTest(@" +3.times { |x| z = 1 } +puts z # undef z +"); + }); + } + + public void Scenario_RubyBlocks3() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def foo + puts 'X',yield(1,2,3) + end +end + +C.new.foo { |a,b,c| puts a,b,c; 'foo' } +"); + }, @" +1 +2 +3 +X +foo +"); + } + + public void Scenario_RubyBlocks5() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def foo + puts block_given? + end +end + +C.new.foo { puts 'goo' } +C.new.foo + "); + }, @" +true +false + "); + } + + /// + /// Return, yield and retry in a method. + /// + public void Scenario_RubyBlocks6() { + AssertOutput(delegate() { + CompilerTest(@" +def do_until(cond) + if cond then return end + yield + retry +end + +i = 0 +do_until(i > 4) do + puts i + i = i + 1 +end +"); + }, @" +0 +1 +2 +3 +4 +"); + } + + /// + /// Break in a block. + /// + public void Scenario_RubyBlocks7() { + AssertOutput(delegate() { + CompilerTest(@" +x = 4.times { |x| + puts x + break 'foo' +} +puts x +"); + }, @" +0 +foo +"); + } + + /// + /// Redo in a block. + /// + public void Scenario_RubyBlocks8() { + AssertOutput(delegate() { + CompilerTest(@" +i = 0 +x = 2.times { |x| + puts x + i = i + 1 + if i < 3 then redo end +} +puts x +"); + }, @" +0 +0 +0 +1 +2 +"); + } + + /// + /// Next in a block. + /// + public void Scenario_RubyBlocks9() { + AssertOutput(delegate() { + CompilerTest(@" +i = 0 +x = 5.times { |x| + puts x + i = i + 1 + if i < 3 then next end + puts 'bar' +} +"); + }, @" +0 +1 +2 +bar +3 +bar +4 +bar +"); + } + + /// + /// Retry in a block. + /// + public void Scenario_RubyBlocks10() { + AssertOutput(delegate() { + CompilerTest(@" +i = 0 +3.times { |x| + puts x + i = i + 1 + if i == 2 then retry end +} +"); + }, @" +0 +1 +0 +1 +2 +"); + } + + /// + /// Return with stack unwinding. + /// + public void Scenario_RubyBlocks11() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + puts 'begin' + 1.times { + 1.times { + puts 'block' + return 'result' + } + } + puts 'end' +ensure + puts 'ensure' +end + +puts foo +"); + }, @" +begin +block +ensure +result +"); + } + + public void Scenario_RubyBlocks12() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + yield 1,2,3 +end + +foo { |a,*b| puts a,'-',b } +"); + }, @" +1 +- +2 +3 +"); + } + + public void Scenario_RubyBlocks13() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + yield 1,2,3 +end + +foo { |a,b| puts a,b } +"); + }, @" +1 +2"); + } + + /// + /// Nested yielding. + /// + public void Scenario_RubyBlocks14() { + AssertOutput(delegate() { + CompilerTest(@" +def bar + yield + yield # shouldn't be called +end + +def foo + bar { + print 'x' + yield + } +end + +foo { + break +}"); + }, @"x"); + } + + /// + /// Retry for-loop: for-loop should behave like x.each { } method call with a block, that is x is reevaluted on retry. + /// + public void Scenario_RubyBlocks15() { + AssertOutput(delegate() { + CompilerTest(@" +def foo x + puts ""foo(#{x})"" + x * ($i + 1) +end + +$i = 0 + +for i in [foo(1), foo(2), foo(3)] do + puts ""i = #{i}"" + + if $i == 0 then + $i = 1 + retry + end +end +"); + }, @" +foo(1) +foo(2) +foo(3) +i = 1 +foo(1) +foo(2) +foo(3) +i = 2 +i = 4 +i = 6 +"); + } + + /// + /// Tests optimization of block break from another block. + /// + /// Yield yields to a block that breaks to its proc-converter, which is foo. + /// So the example should retrun 1 from the foo call. + /// + /// Break is propagated thru yields in two ways: + /// 1) returning ReturnReason == Break via BlockParam (fast path) + /// 2) throwing MethodUnwinder exception (slow path) + /// + /// ReturnReason should be propagated by yields as long as the owner of the block that contains the yield + /// is the target frame for the break. That's the case for for-loop blocks in this test. + /// + public void Scenario_RubyBlocks16() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + for j in [0] + for i in [1] + yield + end + puts 'Unreachable' + end + puts 'Unreachable' +end + +x = foo do + break 1 +end +puts x +"); + }, @"1"); + } + + /// + /// Retry is propagated to the 'each' call. + /// + public void Scenario_RubyBlocks17() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + for i in [1, 2, 3] + puts ""i = #{i}"" + x = yield + end + puts x +end + +def bar + $c = 0 + foo do + puts $c + $c += 1 + retry if $c < 3 + 'done' + end +end + +bar"); + }, @" +i = 1 +0 +i = 1 +1 +i = 1 +2 +i = 2 +3 +i = 3 +4 +done"); + } + + public void Scenario_RubyBlockArgs1() { + AssertOutput(delegate() { + CompilerTest(@" +def a; yield; end +def b; yield 1; end +def c; yield 1,2; end +def d; yield []; end +def e; yield [1]; end +def f; yield [1,2]; end +def g; yield *[]; end; + +a { |x| puts x.inspect } +b { |x| puts x.inspect } +c { |x| puts x.inspect } +d { |x| puts x.inspect } +e { |x| puts x.inspect } +f { |x| puts x.inspect } +g { |(x,)| puts x.inspect } +", 0, 2); // 2 runtime warnings + }, @" +nil +1 +[1, 2] +[] +[1] +[1, 2] +nil +"); + } + + public void Scenario_RubyProcCallArgs1A() { + AssertOutput(delegate() { + CompilerTest(@" +lambda { |x| puts x.inspect }.call +lambda { |x| puts x.inspect }.call 1 +lambda { |x| puts x.inspect }.call 1,2 +lambda { |x| puts x.inspect }.call [] +lambda { |x| puts x.inspect }.call [1] +lambda { |x| puts x.inspect }.call [1,2] +lambda { |x| puts x.inspect }.call *[1] +lambda { |(x,)| puts x.inspect }.call +lambda { |(x,)| puts x.inspect }.call 1,2,3,4 +lambda { |(x,y)| puts x.inspect }.call rescue puts 'error' +", 0, 2); // 2 runtime warnings + }, @" +nil +1 +[1, 2] +[] +[1] +[1, 2] +1 +nil +1 +error +"); + } + + public void Scenario_RubyProcCallArgs1B() { + AssertOutput(delegate() { + CompilerTest(@" +Proc.new { |x| puts x.inspect }.call +Proc.new { |x| puts x.inspect }.call 1 +Proc.new { |x| puts x.inspect }.call 1,2 +Proc.new { |x| puts x.inspect }.call [] +Proc.new { |x| puts x.inspect }.call [1] +Proc.new { |x| puts x.inspect }.call [1,2] +Proc.new { |x| puts x.inspect }.call *[1] +Proc.new { |(x,)| puts x.inspect }.call *[] +", 0, 2); // 2 runtime warnings + }, @" +nil +1 +[1, 2] +[] +[1] +[1, 2] +1 +nil +"); + } + + public void Scenario_RubyBlockArgs2() { + AssertOutput(delegate() { + CompilerTest(@" +def a; yield; end +def b; yield 1; end +def c; yield 1,2; end +def d; yield []; end +def e; yield [1]; end +def f; yield [1,2]; end + +a { |x,y| p [x, y] } +b { |x,y| p [x, y] } +c { |x,y| p [x, y] } +d { |x,y| p [x, y] } +e { |x,y| p [x, y] } +f { |x,y| p [x, y] } +"); + }, @" +[nil, nil] +[1, nil] +[1, 2] +[nil, nil] +[1, nil] +[1, 2] +"); + } + + public void Scenario_RubyProcCallArgs2A() { + AssertOutput(delegate() { + CompilerTest(@" +lambda { |x,y| p [x, y] }.call rescue puts 'error' +lambda { |x,y| p [x, y] }.call 1 rescue puts 'error' +lambda { |x,y| p [x, y] }.call 1,2 +lambda { |x,y| p [x, y] }.call [] rescue puts 'error' +lambda { |x,y| p [x, y] }.call [1] rescue puts 'error' +lambda { |x,y| p [x, y] }.call [1,2] rescue puts 'error' +lambda { |x,y| p [x, y] }.call *[1,2] +lambda { |x,y| p [x, y] }.call *[[1]] rescue puts 'error' +lambda { |x,y| p [x, y] }.call *[[1,2]] rescue puts 'error' +lambda { |x,y| p [x, y] }.call *[[1,2,3]] rescue puts 'error' +"); + }, @" +error +error +[1, 2] +error +error +error +[1, 2] +error +error +error +"); + } + + public void Scenario_RubyProcCallArgs2B() { + AssertOutput(delegate() { + CompilerTest(@" +Proc.new { |x,y| p [x, y] }.call +Proc.new { |x,y| p [x, y] }.call 1 +Proc.new { |x,y| p [x, y] }.call 1,2 +Proc.new { |x,y| p [x, y] }.call [] +Proc.new { |x,y| p [x, y] }.call [1] +Proc.new { |x,y| p [x, y] }.call [1,2] +Proc.new { |x,y| p [x, y] }.call *[1,2] +Proc.new { |x,y| p [x, y] }.call *[[1]] +Proc.new { |x,y| p [x, y] }.call *[[1,2]] +Proc.new { |x,y| p [x, y] }.call *[[1,2,3]] +"); + }, @" +[nil, nil] +[1, nil] +[1, 2] +[nil, nil] +[1, nil] +[1, 2] +[1, 2] +[1, nil] +[1, 2] +[1, 2] +"); + } + + public void Scenario_RubyProcCallArgs2C() { + AssertOutput(delegate() { + CompilerTest(@" +Proc.new { || p [] }.call +Proc.new { |x| p [x] }.call 1 +Proc.new { |x,y| p [x,y] }.call 1,2 +Proc.new { |x,y,z| p [x,y,z] }.call 1,2,3 +Proc.new { |x,y,z,w| p [x,y,z,w] }.call 1,2,3,4 +Proc.new { |x,y,z,w,u| p [x,y,z,w,u] }.call 1,2,3,4,5 +Proc.new { |x,y,z,w,u,v| p [x,y,z,w,u,v] }.call 1,2,3,4,5,6 +"); + }, @" +[] +[1] +[1, 2] +[1, 2, 3] +[1, 2, 3, 4] +[1, 2, 3, 4, 5] +[1, 2, 3, 4, 5, 6] +"); + } + + /// + /// Tests MRI inconsistency in Yield1 vs YieldNoSplat1 when invoked from Call1. + /// + public void Scenario_RubyProcCallArgs2D() { + AssertOutput(delegate() { + CompilerTest(@" +f = proc{|x,| x} +p f.call(1) +p f.call([1]) +p f.call([[1]]) +p f.call([1,2]) +"); + }, @" +1 +[1] +[[1]] +[1, 2] +"); + } + + + public void Scenario_RubyProcYieldArgs1() { + AssertOutput(delegate() { + CompilerTest(@" +def y *a + yield *a +end + +y() { || p [] } +y(1) { |x| p [x] } +y(1,2) { |x,y| p [x,y] } +y(1,2,3) { |x,y,z| p [x,y,z] } +y(1,2,3,4) { |x,y,z,w| p [x,y,z,w] } +y(1,2,3,4,5) { |x,y,z,w,u| p [x,y,z,w,u] } +y(1,2,3,4,5,6) { |x,y,z,w,u,v| p [x,y,z,w,u,v] } +puts '---' +y(1,2,3,4,5,6) { || p [] } +y(1,2,3,4,5,6) { |x| p [x] } +y(1,2,3,4,5,6) { |x,y| p [x,y] } +y(1,2,3,4,5,6) { |x,y,z| p [x,y,z] } +y(1,2,3,4,5,6) { |x,y,z,w| p [x,y,z,w] } +y(1,2,3,4,5,6) { |x,y,z,w,u| p [x,y,z,w,u] } +y(1,2,3,4,5,6) { |x,y,z,w,u,v| p [x,y,z,w,u,v] } +puts '---' +y(1,2,3) { || p [] } +y(1,2,3) { |x| p [x] } +y(1,2,3) { |x,y| p [x,y] } +y(1,2,3) { |x,y,z| p [x,y,z] } +y(1,2,3) { |x,y,z,w| p [x,y,z,w] } +y(1,2,3) { |x,y,z,w,u| p [x,y,z,w,u] } +y(1,2,3) { |x,y,z,w,u,v| p [x,y,z,w,u,v] } +", 0, 2); + }, @" +[] +[1] +[1, 2] +[1, 2, 3] +[1, 2, 3, 4] +[1, 2, 3, 4, 5] +[1, 2, 3, 4, 5, 6] +--- +[] +[[1, 2, 3, 4, 5, 6]] +[1, 2] +[1, 2, 3] +[1, 2, 3, 4] +[1, 2, 3, 4, 5] +[1, 2, 3, 4, 5, 6] +--- +[] +[[1, 2, 3]] +[1, 2] +[1, 2, 3] +[1, 2, 3, nil] +[1, 2, 3, nil, nil] +[1, 2, 3, nil, nil, nil] +"); + } + + /// + /// RHS is list, LHS is not simple, but contains splatting. + /// + public void Scenario_RubyBlockArgs3() { + AssertOutput(delegate() { + CompilerTest(@" +def baz + yield [1,2,3] +end +baz { |*a| puts a.inspect } +"); + }, @"[[1, 2, 3]]"); + } + + /// + /// !L(1,-) && R(0,*), empty array to splat. + /// + public void Scenario_RubyBlockArgs4A() { + AssertOutput(delegate() { + CompilerTest(@" +def y + yield *[] +end + +y { |*a| puts a.inspect } +"); + }, @"[]"); + } + + /// + /// Anonymous unsplat parameters. + /// + public void Scenario_RubyBlockArgs4B() { + AssertOutput(delegate() { + CompilerTest(@" +def y + a = [1,2,3,4,5] + yield a,[6] +end + +y { |(x,y,*),*| p x,y } +puts '-' +y { |(x,y,*a),*| p x,y,a } +puts '-' +y { |(x,y,*),*a| p x,y,a } +"); + }, @" +1 +2 +- +1 +2 +[3, 4, 5] +- +1 +2 +[[6]] +"); + } + + + + /// + /// L(M,*) := R(N,*,=) where M is less then N. + /// + public void Scenario_RubyBlockArgs5() { + AssertOutput(delegate() { + CompilerTest(@" +class C + define_method('[]=') { |a,b,c,*p| + print a,b,c,'|',*p + } +end + +c = C.new +c[1,2,3,4,5,6,7,*[8]] = 9 +"); + }, @"123|456789"); + } + + /// + /// L(M,*) := R(N,*,=) where M is greater then N. + /// + public void Scenario_RubyBlockArgs6() { + AssertOutput(delegate() { + CompilerTest(@" +class C + define_method('[]=') { |a,b,c,*p| + print a,b,c,'|',*p + } +end + +c = C.new +c[1,*[2]] = 3 +"); + }, @"123|"); + } + + /// + /// Wrong number of arguments. + /// + public void Scenario_RubyBlockArgs7() { + AssertExceptionThrown(delegate() { + CompilerTest(@" +class C + define_method('[]=') { |a,b| } +end + +c = C.new +c[1,2,*[]] = 3 +"); + }); + } + + /// + /// L(1, -) := R(0,*0,=) + /// + public void Scenario_RubyBlockArgs8() { + AssertOutput(delegate() { + CompilerTest(@" +class C + define_method('[]=') { |a| + p a + } +end + +c = C.new +c[*[]] = 1 +"); + }, @"1"); + } + + /// + /// L(1, -) := R(N,*,=) + /// + public void Scenario_RubyBlockArgs9() { + AssertOutput(delegate() { + CompilerTest(@" +class C + define_method('[]=') { |a| + p a + } +end + +c = C.new +c[1,*[2]] = 3 +", 0, 1); + }, @"[1, 2, 3]"); + } + + /// + /// L(2..5, -) := R(N,*,=) + /// + public void Scenario_RubyBlockArgs10() { + // L(2, -) := R(N,*,=) + AssertOutput(delegate() { + CompilerTest(@" +class C + define_method('[]=') { |a,b| + print a,',',b + } +end + +c = C.new +c[1,*[]] = 2 +"); + }, @"1,2"); + + // L(3, -) := R(N,*,=) + AssertOutput(delegate() { + CompilerTest(@" +class C + define_method('[]=') { |a,b,c| + print a,',',b,',',c + } +end + +c = C.new +c[1,2,*[]] = 3 +"); + }, @"1,2,3"); + + // L(4, -) := R(N,*,=) + AssertOutput(delegate() { + CompilerTest(@" +class C + define_method('[]=') { |a,b,c,d| + print a,',',b,',',c,',',d + } +end + +c = C.new +c[1,*[2,3]] = 4 +"); + }, @"1,2,3,4"); + + // L(5, -) := R(N,*,=) + AssertOutput(delegate() { + CompilerTest(@" +class C + define_method('[]=') { |a,b,c,d,e| + print a,',',b,',',c,',',d,',',e + } +end + +c = C.new +c[1,2,*[3,4]] = 5 +"); + }, @"1,2,3,4,5"); + } + + public void Scenario_RubyProcs1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo x,y,&f + yield x,y + f[3,4] +end + +foo(1,2) do |a,b| + puts a,b +end +"); + }, @" +1 +2 +3 +4 +"); + } + + public void RubyProcArgConversion1() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def to_proc + lambda { |x| puts x } + end +end + +class D + def to_proc + lambda { |x| puts x + 1 } + end +end + +class E +end + +1.times(&C.new) +1.times(&D.new) +1.times(&E.new) rescue puts 'error' +"); + }, @" +0 +1 +error +"); + } + + public void RubyProcArgConversion2() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def to_proc; 1; end +end + +class D + def to_proc; lambda { puts 'ok2' }; end +end + +1.times(&lambda { puts 'ok1' }) +1.times(&C.new) rescue puts $! +1.times(&D.new) +"); + }, @" +ok1 +C#to_proc should return Proc +ok2 +"); + } + + public void RubyProcArgConversion3() { + AssertOutput(delegate() { + CompilerTest(@" +def foo &b + p b +end + +foo(&nil) +"); + }, @"nil"); + } + + public void RubyProcArgConversion4() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def respond_to? name + puts name + $has_to_proc + end + + def to_proc + lambda { puts 'ok' } + end +end + +c = C.new + +$has_to_proc = false +1.times(&c) rescue puts 'error' + +$has_to_proc = true +1.times(&c) +"); + }, @" +to_proc +error +to_proc +ok +"); + } + + public void ProcNew1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + 1.times { |x| $x = Proc.new } +end + +y = lambda { puts 'foo' } +foo(&y) +p $x.object_id == y.object_id +"); + }, @"true"); + } + + public void ProcNew2() { + AssertOutput(delegate() { + CompilerTest(@" +class P < Proc +end + +def foo + 1.times { |x| $x = P.new } +end + +y = lambda { puts 'foo' } +foo(&y) +p $x.object_id == y.object_id +p $x.class +"); + }, @" +false +P +"); + } + + public void DefineMethod1() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def foo + $x = lambda { + puts self.class + } + + $x.call + end +end + +C.new.foo + +class D + define_method :goo, &$x +end + +D.new.goo +"); + }, @" +C +D"); + } + + /// + /// define_method and class_eval change owner of the method definition. + /// + public void DefineMethod2() { + AssertOutput(delegate() { + CompilerTest(@" +module M + $p = lambda { + def goo + def bar + puts 'bar' + end + end + } +end + +class C + define_method :foo,&$p +end + +class D + class_eval(&$p) +end + +c = C.new +d = D.new + +d.goo +c.foo +c.goo + +p M.instance_methods(false).sort +p C.instance_methods(false).sort +p D.instance_methods(false).sort +"); + }, @" +[""bar""] +[""foo"", ""goo""] +[""goo""] +"); + + } + + public void BlockArity1() { + AssertOutput(delegate() { + CompilerTest(@" +puts '== -4' + +p Proc.new{|(a,b,c,*)|}.arity + +puts '== -2' + +p Proc.new{|(a,b,c,*),*|}.arity + +puts '== -1' + +p Proc.new{|(*)|}.arity +p Proc.new{}.arity +p Proc.new{|*|}.arity + +puts '== 0' + +p Proc.new{||}.arity + +puts '== 1' + +p Proc.new{|x|}.arity +p Proc.new{|x,|}.arity +p Proc.new{|(x,)|}.arity +p Proc.new{|(x,),|}.arity +p Proc.new{|((x,))|}.arity +p Proc.new{|((x,y))|}.arity +p Proc.new{|((x,y,))|}.arity +p Proc.new{|(x,y,),|}.arity +p Proc.new{|(*),|}.arity +p Proc.new{|(x,*),|}.arity + +puts '== 2' + +p Proc.new{|x,y|}.arity +p Proc.new{|x,y,|}.arity +p Proc.new{|x,y,|}.arity +p Proc.new{|(x,y)|}.arity +p Proc.new{|(x,y,)|}.arity + +puts '== 3' + +p Proc.new{|x,y,z|}.arity +p Proc.new{|x,y,z,|}.arity +p Proc.new{|(x,y,z)|}.arity +p Proc.new{|(x,y,z,)|}.arity +"); + }, @" +== -4 +-4 +== -2 +-2 +== -1 +-1 +-1 +-1 +== 0 +0 +== 1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +== 2 +2 +2 +2 +2 +2 +== 3 +3 +3 +3 +3 +"); + } + + public void Proc_RhsAndBlockArguments1() { + AssertOutput(() => CompilerTest(@" +class Proc + alias []= [] +end + +x = Proc.new { |*a| p a, block_given? } + +x.call(1,2,&lambda {}) +x[1,2] = 3 + +"), @" +[1, 2] +false +[1, 2, 3] +false +"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/BoolAndConditionalTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/BoolAndConditionalTests.cs new file mode 100644 index 0000000000..395e3363d9 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/BoolAndConditionalTests.cs @@ -0,0 +1,362 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Tests { + public partial class Tests { + public void Scenario_RubyBoolExpressions1() { + AssertOutput(delegate() { + CompilerTest(@" +puts '!' +puts !false +puts !true +puts !nil +puts !0 +puts !'foo' +puts '!!' +puts !!false +puts !!true +puts !!nil +puts !!0 +puts !!'foo' +puts '!!!' +puts !!!false +puts !!!true +puts !!!nil +puts !!!0 +puts !!!'foo' +"); + }, @" +! +true +false +true +false +false +!! +false +true +false +true +true +!!! +true +false +true +false +false"); + } + + public void Scenario_RubyBoolExpressions2() { + AssertOutput(delegate() { + CompilerTest(@" +def t; print 'T '; true; end +def f; print 'F '; false; end + +puts(t && t) +puts(f && t) +puts(t && f) +puts(f && f) + +puts(t || t) +puts(f || t) +puts(t || f) +puts(f || f) + +puts(f || f && t && t) +"); + }, @" +T T true +F false +T F false +F false +T true +F T true +T true +F F false +F F false +"); + } + + public void Scenario_RubyBoolExpressions3() { + AssertOutput(delegate() { + CompilerTest(@" +def t; print 'T '; true; end +def f; print 'F '; false; end + +puts(x = (t and t)) +puts(x = (f and t)) +puts(x = (t and f)) +puts(x = (f and f)) + +puts(x = (t or t)) +puts(x = (f or t)) +puts(x = (t or f)) +puts(x = (f or f)) + +puts(x = (f or f and t and t)) +"); + }, @" +T T true +F false +T F false +F false +T true +F T true +T true +F F false +F F false +"); + } + + public void Scenario_RubyBoolExpressions4() { + AssertOutput(delegate() { + CompilerTest(@" +x = 'x' +y = 'y' +z = 'z' + +a = ((x = 2.0) and (y = nil) and (z = true)) +puts a,x,y,z +"); + }, @" +nil +2.0 +nil +z +"); + } + + public void Scenario_RubyBoolExpressionsWithReturn1() { + AssertOutput(delegate() { + CompilerTest(@" +def is_true x + x and return 'true' + x or return 'false' + puts 'X' +end + +def run + puts is_true(true) + puts is_true(false) + puts(true, puts('foo'), puts('bar'), (true and return (false or return 'baz')), puts('X')) +end + +puts run +"); + }, @" +true +false +foo +bar +baz +"); + } + + public void Scenario_RubyBoolExpressionsWithReturn2() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + false || return + puts 'unreachable' +end +foo +"); + }, @""); + } + + public void TernaryConditionalWithJumpStatements1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo a + (a ? return : break) while true + puts 'foo' +end + +foo true +foo false +"); + }, @" +foo +"); + } + + public void TernaryConditionalWithJumpStatements2() { + AssertOutput(delegate() { + CompilerTest(@" +def foo a + (a ? 'foo' : return) +end + +def bar a + (a ? return : 'bar') +end + +puts foo(true) +puts foo(false) +puts bar(true) +puts bar(false) +"); + }, @" +foo +nil +nil +bar +"); + } + + public void Scenario_RubyBoolAssignment() { + AssertOutput(delegate() { + CompilerTest(@" +t = true +f = false +m = 0 +n = nil + +t &&= false +f ||= true +m &&= 1 +n ||= 2 + +p t,f,m,n +"); + }, @" +false +true +1 +2 +"); + } + + /// + /// Else-if clauses. + /// + public void Scenario_RubyIfExpression1() { + AssertOutput(delegate() { + CompilerTest(@" +puts(if nil then 1 end) +puts(if 1 then 1 end) +puts(if nil then 1 else 2 end) +puts(if 1 then 1 else 2 end) +puts(if nil then 1 elsif nil then 2 end) +puts(if nil then 1 elsif 1 then 2 end) +puts(if nil then 1 elsif nil then 2 else 3 end) +puts(if nil then 1 elsif 1 then 2 else 3 end) +"); + }, @" +nil +1 +2 +1 +nil +2 +3 +2 +"); + } + + /// + /// Bodies. + /// + public void Scenario_RubyIfExpression2() { + AssertOutput(delegate() { + CompilerTest(@" +puts(if nil then end) +puts(if 1 then end) +puts(if 1 then 1;11;111 end) +puts(if nil then 1 else 2;22 end) +"); + }, @" +nil +nil +111 +22 +"); + } + + public void Scenario_RubyUnlessExpression1() { + AssertOutput(delegate() { + CompilerTest(@" +puts(unless nil then 1 end) +puts(unless 1 then 1 end) +"); + }, @" +1 +nil +"); + } + + public void Scenario_RubyConditionalExpression1() { + AssertOutput(delegate() { + CompilerTest(@" +x = true ? 1 : 'foo' +y = nil ? 2.0 : 'foo' +z = 'foo' || 2 +u = 3 && nil + +puts x,y,z,u +"); + }, @" +1 +foo +foo +nil +"); + } + + public void ConditionalStatement1() { + AssertOutput(delegate() { + CompilerTest(@" +def t; puts 1; true; end +def f; puts 2; false; end + +t unless t +t unless f +f unless t +f unless f +t if t +t if t +f if t +f if f +"); + }, @" +1 +2 +1 +1 +2 +2 +1 +1 +1 +1 +1 +2 +2 +"); + } + + public void ConditionalStatement2() { + AssertOutput(delegate() { + CompilerTest(@" +p x = (1 if true) +p x = (1 if false) +"); + }, @" +1 +nil +"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/CloningTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/CloningTests.cs new file mode 100644 index 0000000000..93de816f06 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/CloningTests.cs @@ -0,0 +1,124 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace IronRuby.Tests { + public partial class Tests { + public void Clone1() { + AssertOutput(delegate() { + CompilerTest(@" +objects = [{}, [], '', Regexp.new('foo'), Object.new, Module.new, Class.new] + +objects.each do |x| + puts x.class.name + + class << x + CONST = 1 + + def foo + 3 + end + + instance_variable_set(:@iv_singleton_x, 2); + end + + x.instance_variable_set(:@iv_x, 4); + y = x.clone + + class << y + raise unless CONST == 1 # singleton constants copied + raise unless instance_variables.size == 0 # singleton instance variables not copied + end + + raise unless y.foo == 3 # singleton methods copied + raise unless y.instance_variable_get(:@iv_x) == 4 # instance variables copied +end +"); + }, @" +Hash +Array +String +Regexp +Object +Module +Class +"); + } + + public void Dup1() { + AssertOutput(delegate() { + CompilerTest(@" +objects = [{}, [], '', Regexp.new('foo'), Object.new] + +objects.each do |x| + puts x.class.name + + class << x + CONST = 1 + + def foo + 3 + end + + instance_variable_set(:@iv_singleton_x, 2); + end + + x.instance_variable_set(:@iv_x, 4); + y = x.dup + + class << y + raise unless ((CONST;false) rescue true) # constants NOT copied + raise unless ((foo;false) rescue true) # singleton class methods not copied + raise unless instance_variables.size == 0 # instance variables on singleton not copied + end + + raise unless ((y.foo;false) rescue true) # methods NOT copied + raise unless y.instance_variable_get(:@iv_x) == 4 # instance variables copied +end +"); + }, @" +Hash +Array +String +Regexp +Object +"); + } + + public void StructDup1() { + AssertOutput(delegate() { + CompilerTest(@" +class St < Struct +end + +p X = St.dup +p Y = X.new(:a,:b) +p Z = Y.dup +p U = Z[1,2] +p V = U.dup +p V.members +"); + }, @" +X +Y +Z +# +# +[""a"", ""b""] +"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ClrTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ClrTests.cs new file mode 100644 index 0000000000..7110a14612 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ClrTests.cs @@ -0,0 +1,564 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.Serialization; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; +using IronRuby.Builtins; + +namespace IronRuby.Tests.Interop.Generics1 { + public class C { + public int Arity { get { return 0; } } + } + + public class C { + public int Arity { get { return 1; } } + } + + public class C { + public int Arity { get { return 2; } } + } +} + +namespace IronRuby.Tests { + public partial class Tests { +#pragma warning disable 169 // private field not used + public class ClassWithFields { + public int Field = 1; + public readonly int RoField = 2; + + public static int StaticField = 3; + public const int ConstField = 4; + + // TODO: + protected string ProtectedField; + private string PrivateField; + } +#pragma warning restore 169 + + public void ClrFields1() { + Context.DefineGlobalVariable("obj", new ClassWithFields()); + + AssertOutput(delegate() { + CompilerTest(@" +C = $obj.class + +puts $obj.field +puts $obj.RoField + +$obj.field = 10 +puts $obj.field + +($obj.RoField = 20) rescue puts $!.class + +puts C.static_field +puts C.const_field + +C.static_field = 30 +puts C.static_field + +(C.const_field = 40) rescue puts $!.class +"); + }, @" +1 +2 +10 +NoMethodError +3 +4 +30 +NoMethodError +"); + } + + public class ClassWithMethods1 { + public int RoProperty { get { return 3; } } + public int RwProperty { get { return 4; } set { } } + public int WoProperty { set { } } + + public int Method() { return 1; } + public static int StaticMethod() { return 2; } + } + + public void ClrMethods1() { + Context.DefineGlobalVariable("obj", new ClassWithMethods1()); + Context.DefineGlobalVariable("cls", typeof(ClassWithMethods1)); + + AssertOutput(delegate() { + CompilerTest(@" +C = $obj.class + +puts $obj.method +puts C.static_method +puts $cls.static_methods rescue puts $!.class + +($obj.RoProperty = 10) rescue puts $!.class +$obj.WoProperty = 20 +$obj.RwProperty = 30 + +puts $obj.RoProperty +puts $obj.WoProperty rescue puts $!.class +puts $obj.RwProperty + +"); + }, @" +1 +2 +NoMethodError +NoMethodError +3 +NoMethodError +4 +"); + } + + public class ClassWithMethods2 { + public string Data; + + protected string ProtectedMethod() { return "ProtectedMethod"; } + private string PrivateMethod() { return "PrivateMethod"; } + + protected string ProtectedProperty { + get { return "ProtectedProperty"; } + set { Data = value; } + } + + private string PrivateProperty { get { return "PrivateProperty"; } set { Data = value; } } + + public string ProtectedGetter { protected get { return "ProtectedGetter"; } set { Data = value; } } + public string PrivateGetter { private get { return "PrivateGetter"; } set { Data = value; } } + public string ProtectedSetter { get { return "ProtectedSetter"; } protected set { Data = value; } } + public string PrivateSetter { get { return "PrivateSetter"; } private set { Data = value; } } + + protected string ProtectedWithPrivateGetter { private get { return "ProtectedWithPrivateGetter"; } set { Data = value; } } + protected string ProtectedWithPrivateSetter { get { return "ProtectedWithPrivateSetter"; } private set { Data = value; } } + } + + public void ClrMethodsVisibility1() { + // TODO: + if (_driver.PartialTrust) return; + + Context.ObjectClass.SetConstant("C", Context.GetClass(typeof(ClassWithMethods2))); + AssertOutput(delegate() { + CompilerTest(@" +class D < C + def test name + self.data = 'none' + sname = name.to_s + + begin + puts 'ok read: ' + send(sname) + rescue + puts 'error read: ' + sname + end + + begin + send(sname + '=', sname) + print 'ok write: ' + sname + ' = ' + rescue + puts 'error write: ' + sname + end + + puts self.data + end + + def foo + puts protected_method + private_method rescue puts 'error call: private_method' + + test :protected_property + test :private_property + + test :protected_getter + test :protected_setter + test :private_getter + test :private_setter + + test :protected_with_private_getter + test :protected_with_private_setter + end +end + +D.new.foo +"); + }, @" +ProtectedMethod +error call: private_method +ok read: ProtectedProperty +ok write: protected_property = protected_property +error read: private_property +error write: private_property +none +ok read: ProtectedGetter +ok write: protected_getter = protected_getter +ok read: ProtectedSetter +ok write: protected_setter = protected_setter +error read: private_getter +ok write: private_getter = private_getter +ok read: PrivateSetter +error write: private_setter +none +error read: protected_with_private_getter +ok write: protected_with_private_getter = protected_with_private_getter +ok read: ProtectedWithPrivateSetter +error write: protected_with_private_setter +none +"); + } + + public class ClassWithInterfaces1 : IEnumerable { + public IEnumerator GetEnumerator() { + yield return 1; + yield return 2; + yield return 3; + } + } + + public class ClassWithInterfaces2 : ClassWithInterfaces1, IComparable { + public int CompareTo(object obj) { + return 0; + } + } + + public void ClrInterfaces1() { + Context.DefineGlobalVariable("obj1", new ClassWithInterfaces1()); + Context.DefineGlobalVariable("obj2", new ClassWithInterfaces2()); + + AssertOutput(delegate() { + CompilerTest(@" +$obj1.each { |x| print x } +puts + +$obj2.each { |x| print x } +puts + +puts($obj2 <=> 1) +"); + }, @" +123 +123 +0 +"); + } + + /// + /// Type represents a class object - it is equivalent to RubyClass. + /// + public void ClrTypes1() { + TestTypeAndTracker(typeof(ClassWithMethods1)); + TestTypeAndTracker(ReflectionCache.GetTypeTracker(typeof(ClassWithMethods1))); + } + + public void TestTypeAndTracker(object type) { + Context.DefineGlobalVariable("type", type); + AssertOutput(delegate() { + CompilerTest(@" +puts $type +puts $type.to_s +puts $type.to_class +puts $type.to_module +puts $type.to_class.static_method +puts $type.static_method rescue puts $!.class +"); + }, String.Format(@" +#<{0}:0x*> +#<{0}:0x*> +{1} +{1} +2 +NoMethodError +", RubyUtils.GetQualifiedName(type.GetType()), RubyUtils.GetQualifiedName(typeof(ClassWithMethods1))), OutputFlags.Match); + } + + public void ClrGenerics1() { + Runtime.LoadAssembly(typeof(Tests).Assembly); + + var generics1 = typeof(Interop.Generics1.C).Namespace.Replace(".", "::"); + + AssertOutput(delegate() { + CompilerTest(@" +include " + generics1 + @" +p C +p C.new.arity +p C[String].new.arity +p C[Fixnum, Fixnum].new.arity +"); + }, @" +TypeGroup of C +0 +1 +2 +"); + } + + /// + /// Require, namespaces. + /// + public void ClrRequireAssembly1() { + AssertOutput(delegate() { + CompilerTest(@" +require 'mscorlib' +puts System::AppDomain.current_domain.friendly_name +"); + }, AppDomain.CurrentDomain.FriendlyName); + } + + /// + /// CLR class re-def and inclusions. + /// + public void ClrInclude1() { + AssertOutput(delegate() { + CompilerTest(@" +require 'mscorlib' +include System::Collections +class ArrayList + puts self.object_id == System::Collections::ArrayList.object_id +end +"); + }, "true"); + } + + /// + /// Instantiation. + /// + public void ClrNew1() { + AssertOutput(delegate() { + CompilerTest(@" +require 'mscorlib' +b = System::Text::StringBuilder.new +b.Append 1 +b.Append '-' +b.Append true +puts b.to_string +puts b.length +"); + }, @" +1-True +6 +"); + } + + /// + /// Alias. + /// + public void ClrAlias1() { + AssertOutput(delegate() { + CompilerTest(@" +require 'mscorlib' +include System::Collections + +class ArrayList + alias :old_add :Add + def foo x + old_add x + end +end + +a = ArrayList.new +a.Add 1 +a.add 2 +a.foo 3 +puts a.count"); + }, "3"); + } + + public class ClassWithEnum { + public enum MyEnum { + Foo = 1, Bar = 2, Baz = 3 + } + + public MyEnum MyProperty { get { return MyEnum.Baz; } set { _p = value; } } + private MyEnum _p; + } + + /// + /// Enums. + /// + public void ClrEnums1() { + // TODO: + Context.DefineGlobalVariable("obj", new ClassWithEnum()); + Context.DefineGlobalVariable("enum", typeof(ClassWithEnum.MyEnum)); + + XAssertOutput(delegate() { + CompilerTest(@" +E = $enum.to_class + +$obj.my_property = E.foo +puts $obj.my_property + +$obj.my_property = 1 +puts $obj.my_property +"); + }, "TODO"); + } + + public delegate int Delegate1(string foo, int bar); + + public void ClrDelegates1() { + Context.DefineGlobalVariable("delegateType", typeof(Delegate1)); + CompilerTest(@" +D = $delegateType.to_class +$d = D.new { |foo, bar| $foo = foo; $bar = bar; 777 } +"); + object d = Context.GetGlobalVariable("d"); + Assert(d is Delegate1); + AssertEquals(((Delegate1)d)("hello", 123), 777); + AssertEquals(Context.GetGlobalVariable("foo"), "hello"); + AssertEquals(Context.GetGlobalVariable("bar"), 123); + } + + public void ClrDelegates2() { + Runtime.LoadAssembly(typeof(Func<>).Assembly); + + var f = Engine.Execute>(@" +System::Func.of(Fixnum, Fixnum).new { |a| a + 1 } +"); + + Assert(f(1) == 2); + } + + public void ClrEvents1() { + // TODO: + if (_driver.PartialTrust) return; + + AssertOutput(delegate() { + CompilerTest(@" +require 'System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +require 'System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' + +Form = System::Windows::Forms::Form + +f = Form.new + +x = 'outer var' + +f.shown do |sender, args| + puts x + sender.close +end + +f.Text = 'hello' +f.BackColor = System::Drawing::Color.Green + +System::Windows::Forms::Application.run f +"); + }, "outer var"); + } + + public class ClassWithVirtuals { + public virtual string M1() { + return "CM1"; + } + public virtual string M2() { + return "CM2"; + } + public virtual string P1 { + get { return "CP1"; } + } + private string _p2 = "CP2"; + public virtual string P2 { + get { return _p2; } + set { _p2 = value; } + } + public virtual string P3 { + get { return "CP3"; } + } + public string Summary { + get { return M1() + M2() + P1 + P2 + P3; } + } + } + + public void ClrOverride1() { + Context.ObjectClass.SetConstant("C", Context.GetClass(typeof(ClassWithVirtuals))); + AssertOutput(delegate() { + CompilerTest(@" +class D < C + def m1 + 'RM1' + end + def p2= value + end + def p3 + 'RP3' + end +end + +$c = C.new +$c.p2 = 'RP2' +puts $c.summary + +$d = D.new +$d.p2 = 'RP2' +puts $d.summary +"); + }, @" +CM1CM2CP1RP2CP3 +RM1CM2CP1CP2RP3 +"); + } + + public class ClassWithNonEmptyConstructor { + public string P { get; set; } + public ClassWithNonEmptyConstructor() { + } + public ClassWithNonEmptyConstructor(string p) { + P = p; + } + } + + private static bool IsAvailable(MethodBase method) { + return method != null && !method.IsPrivate && !method.IsFamilyAndAssembly; + } + + public void ClrConstructor1() { + Context.ObjectClass.SetConstant("C", Context.GetClass(typeof(ClassWithNonEmptyConstructor))); + var cls = Engine.Execute(@" +class D < C; end +D.new +D +"); + var rubyClass = (cls as RubyClass); + Debug.Assert(rubyClass != null); + + Type baseType = rubyClass.GetUnderlyingSystemType(); + Assert(IsAvailable(baseType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(RubyClass) }, null))); + Assert(IsAvailable(baseType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(RubyClass), typeof(string) }, null))); +#if !SILVERLIGHT + Assert(IsAvailable(baseType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { + typeof(SerializationInfo), typeof(StreamingContext) }, null))); +#endif + } + + public void ClrConstructor2() { + // TODO: Requires allocator support + Context.ObjectClass.SetConstant("C", Context.GetClass(typeof(ClassWithNonEmptyConstructor))); + AssertOutput(delegate() { + CompilerTest(@" +class D < C; end + +$d = D.new 'test' +puts $d.p +"); + }, @" +test +"); + } + + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ConstantTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ConstantTests.cs new file mode 100644 index 0000000000..66764e4057 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ConstantTests.cs @@ -0,0 +1,272 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Tests { + + public partial class Tests { + public void Constants1A() { + AssertOutput(delegate() { + CompilerTest(@" +class Const + def get_const + CONST + end + + CONST = 1 +end + +puts Const.new.get_const +"); + }, @"1"); + } + + public void Constants1B() { + AssertOutput(delegate() { + CompilerTest(@" +OUTER_CONST = 99 +class Const + puts CONST = OUTER_CONST + 1 +end + +class Const2 < Const +end + +puts Const::CONST +puts ::OUTER_CONST +puts Const::NEW_CONST = 123 +puts Const2::CONST +"); + }, @" +100 +100 +99 +123 +100"); + } + + public void ConstantNames() { + AssertOutput(delegate() { + CompilerTest(@" +module M + X = 1 +end + +module N + X = 1 + Z = 2 +end + +class C + include M, N + + puts X,Z + Y = 2 + U = 1 +end + +class D < C + U = 3 + W = 4 +end + +p C.constants.sort +p D.constants.sort +p M.constants.sort +"); + }, @" +1 +2 +[""U"", ""X"", ""Y"", ""Z""] +[""U"", ""W"", ""X"", ""Y"", ""Z""] +[""X""] +"); + } + + /// + /// Class/module def makes name of the constant by appending its simple name to the name of the lexically containing class/module. + /// + public void Constants3() { + AssertOutput(delegate() { + CompilerTest(@" +class B + class D + puts self + end +end + +module M + class D + puts self + end +end + +class C < B + include M + + class D + puts self + end +end +"); + }, @" +B::D +M::D +C::D +"); + } + + /// + /// Top-level class/module definition does look-up in Object and all its mixins. + /// + public void Constants4() { + AssertOutput(delegate() { + CompilerTest(@" +module M + class CM + $x1 = self + end +end + +module N + class CN + $y1 = self + end +end + +class Object + include M + + class CM + $x2 = self + end +end + +class Foo + include N + + class CN + $y2 = self + end +end + +puts $x1.object_id == $x2.object_id +puts $y1.object_id != $y2.object_id +"); + }, @" +true +true +"); + } + + /// + /// Unqualified constant lookup should search in lexical scopes from inner most to the outer most + /// looking into modules for constant declared in those modules and their mixins. + /// It shouldn't look to base classes nor mixins. + /// + public void UnqualifiedConstants1() { + AssertOutput(delegate() { + CompilerTest(@" +class B + Q = 'Q in B' +end + +module M + P = 'P in M' +end + +class C + S = 'S in C' + Q = 'Q in C' + P = 'P in C' + class C < B + include M + S = 'S in C::C' + + puts C, P, Q, S + end +end +"); + }, @" +C::C +P in C +Q in C +S in C::C +"); + } + + /// + /// If a constant is not found in the current scope chain, the inner-most module scope's module/class and its ancestors are checked. + /// + public void UnqualifiedConstants2() { + AssertOutput(delegate() { + CompilerTest(@" +class D + D = 'U in D' +end + +class B + W = 'W in B' +end + +module M + V = 'V in M' + + def puts_U + puts U rescue puts $! + puts W rescue puts $! + end +end + +class C < D + class C < B + include M + + def puts_consts + puts U rescue puts $! + puts V, W + puts_U + end + end +end + +C::C.new.puts_consts +"); + }, @" +uninitialized constant C::C::U +V in M +W in B +uninitialized constant M::U +uninitialized constant M::W +"); + } + + /// + /// Global constants defined in loaded code are defined on the anonymous module created by the load. + /// + public void LoadAndGlobalConstants() { + // TODO: + } + + /// + /// Global constants are visible in Runtime.Globals. + /// + public void GlobalConstantsInterop() { + CompilerTest("C = 1"); + object value; + Runtime.Globals.TryGetVariable("C", out value); + AssertEquals(value, 1); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/DeclarationTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/DeclarationTests.cs new file mode 100644 index 0000000000..a23e54b78f --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/DeclarationTests.cs @@ -0,0 +1,183 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +namespace IronRuby.Tests { + public partial class Tests { + + public void Scenario_RubyDeclarations1() { + AssertOutput(delegate() { + CompilerTest(@" +class C + puts 'C' +end +"); + }, "C");} + + public void Scenario_RubyDeclarations1A() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def foo + print 'F' + end +end + +C.new.foo +"); + }, "F"); + } + + public void Scenario_RubyDeclarations1B() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def f + print 'F' + end +end + +class C + def g + print 'G' + end +end + +C.new.f +C.new.g +"); + }, "FG"); + } + + public void Scenario_RubyDeclarations1C() { + AssertOutput(delegate() { + CompilerTest(@" +class C + X = 1 +end + +class D < C + Y = 2 +end + +print D::X, D::Y +"); + }, "12"); + } + + public void Scenario_RubyDeclarations2() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def foo() + puts ""This is foo in C."" + end + def C.sfoo() + puts ""This is static foo in C."" + end +end + +class D < C + def bar() + puts ""This is bar in D"" + foo + end + def D.sbar() + puts ""This is static bar in D."" + sfoo + end +end + +def baz() + puts 'This is baz, a global function' +end + +D.sbar() + +print 'Hello' +Kernel.print "" world"" +puts '!' + +D.new.bar +baz() +"); + }, +@"This is static bar in D. +This is static foo in C. +Hello world! +This is bar in D +This is foo in C. +This is baz, a global function +"); + } + + public void Scenario_RubyDeclarations3() { + AssertOutput(delegate() { + CompilerTest(@" +class C + module M + module N + end + end + + module ::Foo + module X + end + module X::Y + end + end +end + +puts C +puts C::M +puts C::M::N +puts Foo +puts Foo::X +puts Foo::X::Y"); + }, +@" +C +C::M +C::M::N +Foo +Foo::X +Foo::X::Y +"); + } + + public void Scenario_RubyDeclarations4() { + AssertExceptionThrown(() => + CompilerTest(@" +class C + class << self + class D < self # error: can't make subclass of virtual class + end + end +end +")); + + AssertExceptionThrown(() => + CompilerTest(@" +class Module + alias mf module_function +end + +class C + mf # error: module_function must be called for modules +end +")); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/DefinedTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/DefinedTests.cs new file mode 100644 index 0000000000..2130766b5d --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/DefinedTests.cs @@ -0,0 +1,302 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Tests { + public partial class Tests { + public void Scenario_RubyDefinedOperator_Globals1() { + AssertOutput(delegate() { + CompilerTest(@" +p defined? $+ +alias $plus $+ +p defined? $+ +p defined? $plus +" + ); + }, @" +nil +nil +""global-variable"" +"); + } + + public void Scenario_RubyDefinedOperator_Globals2() { + AssertOutput(delegate() { + CompilerTest(@" +alias $foo $bar +p defined? $foo +p defined? $bar +$foo = nil +p defined? $foo +p defined? $bar +$foo = 1 +p defined? $foo +p defined? $bar +" + ); + }, @" +nil +nil +""global-variable"" +""global-variable"" +""global-variable"" +""global-variable"" +"); + } + + public void Scenario_RubyDefinedOperator_Methods1() { + CompilerTest(@" +module M + def foo; end +end + +module N + def foo_defined? + $o1 = defined? foo + undef foo rescue $o2 = 1 + end +end + +class C + include M,N +end + +C.new.foo_defined? +"); + object o = Context.GetGlobalVariable("o1"); + AssertEquals(o.ToString(), "method"); + + o = Context.GetGlobalVariable("o2"); + AssertEquals(o, 1); + } + + public void Scenario_RubyDefinedOperator_Methods2() { + // TODO: visibility + AssertOutput(() => CompilerTest(@" +def foo + puts 'foo' + self +end + +puts defined? 1.+ +puts defined? 1.method_that_doesnt_exist +puts defined? raise.foo +# TODO: puts defined? foo.foo should return nil (visibility) +public :foo +puts defined? foo.foo +"), @" +method +nil +nil +foo +method +"); + } + + public void Scenario_RubyDefinedOperator_Constants1() { + AssertOutput(delegate() { + CompilerTest(@" +W = 0 + +module M + X = 1 +end + +module N + Y = 2 + def cs_defined? + puts defined? ::W + puts defined? ::Undef + puts defined? M::X + puts defined? M::Undef + puts defined? X + puts defined? Y + puts defined? Z + end +end + +class C + include M,N + Z = 3 +end + +C.new.cs_defined? +"); + }, @" +constant +nil +constant +nil +nil +constant +nil +"); + } + + public void Scenario_RubyDefinedOperator_Constants2() { + AssertOutput(delegate() { + CompilerTest(@" +module M + C = 1 +end + +def foo + M +end + +class << Object + def const_missing name + puts ""missing #{name}"" + M + end +end + +puts defined?(X::C) +puts defined?(raise::C) +puts defined?(foo::C) +"); + }, @" +missing X +constant +nil +constant +"); + } + + public void Scenario_RubyDefinedOperator_Expressions1() { + AssertOutput(delegate() { + CompilerTest(@" +puts defined? true +puts defined? false +puts defined? self + +puts defined? 1 +puts defined? 'foo' + +puts defined?((1+3)+1) + +puts defined? x = 1 +puts defined? x &= 1 +puts defined? x &&= 1 +"); + }, @" +true +false +self +expression +expression +method +assignment +assignment +expression +"); + } + + public void Scenario_RubyDefinedOperator_InstanceVariables1() { + AssertOutput(delegate() { + CompilerTest(@" +p defined? @x +@x = nil +p defined? @x +@x = 1 +p defined? @x +"); + }, @" +nil +""instance-variable"" +""instance-variable"" +"); + } + + public void Scenario_RubyDefinedOperator_ClassVariables1() { + AssertOutput(delegate() { + CompilerTest(@" +module M + def foo; @@foo = 1; end + def foo_defined_on_M? + puts defined? @@foo + end +end + +module N + def foo_defined? + puts defined? @@foo + end +end + +class C + include M,N +end + +c = C.new +c.foo +c.foo_defined? +c.foo_defined_on_M? +"); + }, @" +nil +class variable +"); + } + + public void Scenario_RubyDefinedOperator_ClassVariables2() { + AssertOutput(delegate() { + CompilerTest(@" +@@x = 1 +puts defined? @@x +"); + }, @" +class variable +"); + } + + public void Scenario_RubyDefinedOperator_Yield1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + defined? yield +end + +puts foo +puts foo {} +"); + }, @" +nil +yield +"); + } + + public void Scenario_RubyDefinedOperator_Locals1() { + AssertOutput(delegate() { + CompilerTest(@" +def bob a + x = 1 + 1.times { |y| + z = 2 + puts defined? w, defined? x, defined? y, defined? z, defined? a + } +end + +bob 1 +"); + }, @" +nil +local-variable +local-variable +local-variable +local-variable +"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/EvalTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/EvalTests.cs new file mode 100644 index 0000000000..27a8a53573 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/EvalTests.cs @@ -0,0 +1,571 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +namespace IronRuby.Tests { + public partial class Tests { + public void Eval1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo() + a = 1 + eval('puts a') +end + +foo +"); + }, "1"); + } + + public void Eval2() { + AssertOutput(delegate() { + CompilerTest(@" +def b + 'undefined' +end + +def goo + a = 1 + eval('a = 2; b = 3; puts a,b') + puts a,b +end + +goo +"); + }, @" +2 +3 +2 +undefined"); + } + + public void Eval3() { + string body = @" +1.times { + a = 1 + + eval <<-END + b = a + 1 + eval <<-END2 + c = b + 1 + END2 + END + + puts b rescue p $! + + eval <<-END + puts b, c + END +} + +puts a rescue p $! +puts b rescue p $! + +eval <<-END + puts a rescue p $! + puts b rescue p $! +END +"; + string output = @" +# +2 +3 +# +# +# +# +"; + + AssertOutput(() => CompilerTest(String.Format(@"def foo; {0}; end; foo", body)), output, OutputFlags.Match); + AssertOutput(() => CompilerTest(String.Format(@"module M; {0}; end", body)), output, OutputFlags.Match); + AssertOutput(() => CompilerTest(String.Format(@"class C; {0}; end", body)), output, OutputFlags.Match); + AssertOutput(() => CompilerTest(String.Format(@"class << Object.new; {0}; end", body)), output, OutputFlags.Match); + AssertOutput(() => CompilerTest(body), output, OutputFlags.Match); + } + + public void EvalReturn1() { + AssertOutput(delegate() { + CompilerTest(@" +def y + yield +end + +def foo + $b = Proc.new { + eval('return 123') + } + goo +end + +def goo + y(&$b) +end + +p foo +"); + }, @"123"); + } + + public void EvalReturn2() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + eval('return 123') +end + +p foo +"); + }, @"123"); + } + + public void LocalNames1() { + AssertOutput(delegate() { + CompilerTest(@" +def f + a = 1 + puts local_variables.sort + b = 2 +end + +f +"); + }, @" +a +b"); + } + + public void LocalNames2() { + AssertOutput(delegate() { + CompilerTest(@" +def bar + 1.times { |x| + y = 1 + 1.times { |z| + $b = binding + } + } +end + +bar + +eval('puts local_variables.sort', $b) +"); + }, @" +x +y +z +"); + } + + // module scope creates a local dictionary that can be captured by a binding: + public void LocalNames3() { + AssertOutput(delegate() { + CompilerTest(@" +$p = [] +$i = 0 +while $i < 2 + module M + x = $i + $p << binding + end + $i += 1 +end + +eval('puts x', $p[0]) +eval('puts x', $p[1]) +"); + }, @" +0 +1 +"); + } + + // module scope stops local variable lookup + public void LocalNames4() { + AssertOutput(delegate() { + CompilerTest(@" +def bar + 1.times { |x| + eval(' + module M + y = 1 + 1.times { |z| + $b = binding + } + end + '); + } +end + +bar + +eval('puts local_variables.sort', $b) +"); + }, @" +y +z +"); + } + + public void LiftedParameters1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo a + 1.times { + eval('puts a') + } +end + +foo 3 +"); + }, @"3"); + } + + public void Binding1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + x = 1 + y = 2 + $b = binding +end + +foo + +p eval('x+y', $b) +"); + }, "3"); + } + + public void TopLevelBinding_RubyProgram() { + AssertOutput(delegate() { + Engine.CreateScriptSourceFromString(@" +def bar + 4 +end + +def baz + p eval('a+bar', TOPLEVEL_BINDING) + p eval('b', TOPLEVEL_BINDING) +end + +a = 3 +baz +b = 1 +").ExecuteProgram(); + }, @" +7 +nil"); + } + + public void EvalWithProcBinding1() { + AssertOutput(delegate() { + CompilerTest(@" +def goo + x = 1 + y = 2 + lambda { + z = -1 + } +end + +def z + 1 +end + +p eval('x+y+z', goo) +"); + }, @"4"); + } + + /// + /// "self" and arguments are passed in the proc, result is passed out. + /// + public void ModuleEvalProc1() { + AssertOutput(delegate() { + CompilerTest(@" +module M +end + +puts M.module_eval { |*a| + p a, self + 'result' +} +"); + }, @" +[M] +M +result +"); + } + + /// + /// Break from module_eval'd proc works. + /// + public void ModuleEvalProc2() { + AssertOutput(delegate() { + CompilerTest(@" +module M +end + +puts M.module_eval { + break 'result' +} +"); + }, @" +result +"); + } + + public void ModuleInstanceEvalProc3() { + AssertOutput(delegate() { + CompilerTest(@" +class C + D = 1 +end + +C.module_eval { + p Module.nesting + puts D rescue puts 'error' + + def foo + puts 'foo' + end +} + +C.instance_eval { + p Module.nesting + puts D rescue puts 'error' + + def bar + puts 'bar' + end +} + +C.new.foo +C.bar +"); + }, @" +[] +error +[] +error +foo +bar +"); + } + + /// + /// module_eval uses yield semantics for invoking the proc. + /// (return in a yield to inactive lambda doesn't work). + /// + public void ModuleEvalProc3() { + AssertOutput(delegate() { + CompilerTest(@" +module M +end + +$p = lambda { + return 'ok' +} + +puts $p.call +puts M.module_eval(&$p) rescue puts 'error' +"); + }, @" +ok +error +"); + } + + /// + /// instance_eval creates an instance singleton and sets the scope of method definitions and aliases to it. + /// + public void InstanceEvalProc1() { + AssertOutput(delegate() { + CompilerTest(@" +x = Object.new +x.instance_eval { + def foo + puts 'foo' + end +} +x.foo +"); + }, @"foo"); + } + + /// + /// instance_eval sets public visibility flag on the evaluated block scope. + /// + public void InstanceEvalProc2() { + // TODO: + AssertOutput(delegate() { + CompilerTest(@" +x = Object.new +x.instance_eval { + def foo + puts 'foo' + end +} +class << x + puts instance_methods(false) +end +"); + }, @"foo"); + } + + /// + /// module_eval is used in Class.new + /// + public void ModuleClassNew1() { + AssertOutput(delegate() { + CompilerTest(@" +C = Class.new { |*args| + p args + p self + + def foo + puts 'hello' + end +} + +C.new.foo +"); + }, @" +[#] +# +hello +", OutputFlags.Match); + } + + /// + /// Class.new uses yield (as module_eval does). If the block doesn't break the return value is the class. + /// + public void ModuleClassNew2() { + AssertOutput(delegate() { + CompilerTest(@" +class B + def foo + puts 'bye' + end +end + +C = Class.new(B) { + def foo + puts 'hello' + end + + break B +} + +C.new.foo +"); + }, @" +bye +"); + } + + public void ModuleEvalString1() { + AssertOutput(delegate() { + CompilerTest(@" +module N + module M + end +end + +N::M.module_eval('p self, Module.nesting') +"); + }, @" +N::M +[N::M] +"); + } + + public void InstanceEvalString1() { + AssertOutput(delegate() { + CompilerTest(@" +module N + module M + class << self + C = 123 + end + end +end + +N::M.instance_eval('p self, C, Module.nesting') +"); + }, @" +N::M +123 +[#] +"); + } + + public void ModuleEvalString2() { + AssertOutput(delegate() { + CompilerTest(@" +class C +end + +C.module_eval('def foo; puts 1; end') +C.new.foo +"); + }, @"1"); + } + + public void InstanceEvalString2() { + AssertOutput(delegate() { + CompilerTest(@" +class C +end + +C.instance_eval('def foo; puts 1; end') +C.foo +"); + }, @"1"); + } + + public void ModuleInstanceEvalString3() { + AssertOutput(delegate() { + CompilerTest(@" +module M +end + +def a + puts 'method' +end + +def foo + a = 1 + M.module_eval('puts a') + M.instance_eval('puts a') +end + +foo +"); + }, @" +1 +1"); + } + + +// TODO: RFC.InBlock is not set properly +// 1.times { eval('break') } +// Array.new(10) { eval('break 1') } +// define_method(:foo) { eval('break 1') } +// M.module_eval { eval('break 1') } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ExceptionTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ExceptionTests.cs new file mode 100644 index 0000000000..3c00934393 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ExceptionTests.cs @@ -0,0 +1,635 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using IronRuby.Builtins; + +namespace IronRuby.Tests { + public partial class Tests { + public void Scenario_RubyExceptions1() { + AssertExceptionThrown(delegate() { + CompilerTest(@"raise"); + }); + } + + public void Scenario_RubyExceptions1A() { + AssertExceptionThrown(delegate() { + CompilerTest(@"raise 'foo'"); + }); + } + + public void Scenario_RubyExceptions2A() { + AssertExceptionThrown(delegate() { + CompilerTest(@" +$! = NotImplementedError.new +raise"); + }); + } + + public void Scenario_RubyExceptions2B() { + AssertExceptionThrown(delegate() { + CompilerTest("$! = NotImplementedError"); + }); + } + + public void Scenario_RubyExceptions2C() { + AssertOutput(delegate() { + CompilerTest(@" +x = NotImplementedError.new 'hello' +puts x.message + +x.message[0] = ?H +puts x.message + +x.send :initialize +puts x.message + +x.send :initialize, 'Bye' +puts x.message +"); + }, @" +hello +Hello +NotImplementedError +Bye +"); + } + + public void Scenario_RubyExceptions2D() { + AssertOutput(delegate() { + CompilerTest(@" +x = IOError.new 'hello' +puts x.message + +x.message[0] = ?H +puts x.message + +x.send :initialize +puts x.message + +x.send :initialize,'Bye' +puts x.message +"); + }, @" +hello +Hello +IOError +Bye +"); + } + + public void Scenario_RubyExceptions3() { + AssertOutput(delegate() { + CompilerTest(@" +begin + raise + print 'U' +rescue + print 'X' +end +"); + }, "X"); + } + + public void Scenario_RubyExceptions4() { + AssertOutput(delegate() { + CompilerTest(@" +begin + raise + print 'U' +rescue IOError + print 'U' +rescue StandardError + print 'X' +end +"); + }, "X"); + } + + public void Scenario_RubyExceptions5() { + AssertOutput(delegate() { + CompilerTest(@" +begin + raise +rescue StandardError => $x + puts 'Caught' + puts ""$! = '#{$!.class.name}'"" + puts ""$x = '#{$x.class.name}'"" +end +"); + }, @" +Caught +$! = 'RuntimeError' +$x = 'RuntimeError' +"); + } + + public void Scenario_RubyExceptions6() { + AssertOutput(delegate() { + CompilerTest(@" +x = StandardError +begin + raise +rescue x => x + puts 'Caught' + puts ""$! = '#{$!.class.name}'"" + puts ""x = '#{x.class.name}'"" +end +"); + }, @" +Caught +$! = 'RuntimeError' +x = 'RuntimeError' +"); + } + + public void Scenario_RubyExceptions7() { + // TODO: fix stack traces in interpreter + if (Engine.Setup.InterpretedMode) { + return; + } + + AssertOutput(delegate() { + CompilerTest(@" +def foo + raise +end + +begin + foo +rescue Exception => e + $found = false + e.backtrace.each { |frame| if frame.index('foo') != nil then $found = true end } + puts $found +end"); + }, @"true"); + } + + public void Scenario_RubyExceptions8() { + AssertOutput(delegate() { + CompilerTest(@" +puts 'Begin' +x = begin + puts 'Raise' + 1 + raise + puts 'Unreachable' + 2 +rescue IOError + puts 'Rescue1' + 3 +rescue + puts 'Rescue2' + 4 +else + puts 'Else' + 5 +ensure + puts 'Ensure' + 6 +end +puts x +puts 'End' +"); + }, @" +Begin +Raise +Rescue2 +Ensure +4 +End +"); + } + + public void Scenario_RubyExceptions9() { + AssertOutput(delegate() { + CompilerTest(@" +puts 'Begin' +x = class C + puts 'Raise' + 1 +rescue IOError + puts 'Rescue1' + 3 +else + puts 'Else' + 5 +ensure + puts 'Ensure' + 6 +end +puts x +puts 'End' +"); + }, @" +Begin +Raise +Else +Ensure +5 +End +"); + } + + public void Scenario_RubyExceptions10() { + AssertOutput(delegate() { + CompilerTest(@" +puts 'Begin' +begin + puts 'Class' + class C + puts 'NoRaise' + rescue + puts 'Rescue' + else + puts 'Else' + ensure + puts 'Ensure' + end + puts 'ClassEnd' +rescue + puts 'OuterRescue' +end +puts 'End' +"); + }, @" +Begin +Class +NoRaise +Else +Ensure +ClassEnd +End +"); + } + + public void Scenario_RubyExceptions11() { + AssertOutput(delegate() { + CompilerTest(@" +puts 'Begin' +begin + puts 'Class' + class C + puts 'NoRaise' + rescue + puts 'Rescue' + else + puts 'Else' + ensure + puts 'Ensure' + end + puts 'ClassEnd' +rescue + puts 'OutterRescue' +end +puts 'End' +"); + }, @" +Begin +Class +NoRaise +Else +Ensure +ClassEnd +End +"); + } + + public void Scenario_RubyExceptions12() { + AssertOutput(delegate() { + CompilerTest(@" +class A < Exception; end +class B < Exception; end +class C < Exception; end + +begin + raise B +rescue A,B,C => e + puts e.class +end +"); + }, @"B"); + } + + /// + /// Order of evaluation inside rescue clauses. + /// + public void Scenario_RubyExceptions12A() { + AssertOutput(delegate() { + CompilerTest(@" +class Module + alias old === + + def ===(other) + puts ""cmp(#{self}, #{other})"" + + old other + end +end + +class A < Exception; end +class B < Exception; end +class C < Exception; end +class D < Exception; end +class E < Exception; end +class F < Exception; end +class G < Exception; end + +def id(t) + puts ""r(#{t})"" + t +end + +def foo + raise F +rescue id(A),id(B),id(C) + puts 'rescued 1' # unreachable +rescue id(E),id(F),id(G) + puts 'rescued 2' +end + +foo +"); + }, @" +r(A) +r(B) +r(C) +cmp(A, F) +cmp(B, F) +cmp(C, F) +r(E) +r(F) +r(G) +cmp(E, F) +cmp(F, F) +rescued 2 +"); + } + + /// + /// Retry try-catch block. + /// + public void Scenario_RubyExceptions13() { + AssertOutput(delegate() { + CompilerTest(@" +i = 0 +begin + puts i + i = i + 1 + if i < 3 then raise end +rescue + puts 'retrying' + retry +else + puts 'no exception' +ensure + puts 'giving up' +end +"); + }, @" +0 +retrying +1 +retrying +2 +no exception +giving up +"); + } + + public void Scenario_RubyExceptions14() { + AssertOutput(delegate() { + CompilerTest(@" +begin + raise IOError +rescue IOError + puts 'rescued' +end +"); + }, "rescued"); + } + + public void Scenario_RubyExceptions15() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def exception + IOError.new + end +end + +begin + raise C.new +rescue IOError + puts 'rescued' +end +"); + }, @"rescued"); + } + + public void Scenario_RubyExceptions16() { + AssertOutput(delegate() { + CompilerTest(@" +begin + raise Exception.new('foo') +rescue Exception => e + puts e.message +end +"); + }, @"foo"); + } + + public void Scenario_RubyExceptions_Globals() { + AssertOutput(delegate() { + CompilerTest(@" +p $! +p $@ + +$! = Exception.new +p $@ = nil +p $@ = ['foo'] +p $@ = class A < Array; new; end +p $@ = [class S < String; new; end] +"); + }, @" +nil +nil +nil +[""foo""] +[] +[""""] +"); + + // $! must be non-null when assigning to $@ + AssertExceptionThrown(delegate() { + CompilerTest(@"$! = nil; $@ = ['foo']"); + }); + + // $! non-nullity checked before type of backtracce: + AssertExceptionThrown(delegate() { + CompilerTest(@"$! = nil; $@ = 1"); + }); + + // backtrace needs to be an array + AssertExceptionThrown(delegate() { + CompilerTest(@"$! = Exception.new; $@ = 1"); + }); + + // backtrace needs to be an array of strings + AssertExceptionThrown(delegate() { + CompilerTest(@"$! = Exception.new; $@ = ['foo', 1]"); + }); + + // backtrace needs to be an array, no conversion take place: + AssertExceptionThrown(delegate() { + CompilerTest(@" +$! = Exception.new +class B; def to_ary; []; end; end +$@ = B.new +"); + }); + + // backtrace needs to be an array of strings, no item conversion take place: + AssertExceptionThrown(delegate() { + CompilerTest(@" +$! = Exception.new +class T; def to_str; ''; end; end +$@ = [T.new] +"); + }); + } + + public void Scenario_RubyRescueStatement1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + raise IOError.new('1') +end + +foo rescue puts $! +puts '1' rescue puts '2' +"); + }, @" +1 +1"); + } + + public void Scenario_RubyRescueExpression1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + raise +end + +x = foo rescue 2 +puts x +"); + }, @"2"); + } + + public void Scenario_RubyRescueExpression2() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + raise +end + +def bar + x = foo rescue return + puts 'unreachable' +end + +bar +"); + }, @""); + } + + public void ExceptionArg1() { + AssertOutput(delegate() { + CompilerTest(@" +begin + raise SystemExit, 42 +rescue SystemExit + puts $!.status +end +"); + }, @"42"); + } + + public void ExceptionArg2() { + AssertOutput(delegate() { + CompilerTest(@" +begin + raise SystemExit, 'foo' +rescue SystemExit + puts $!.message +end +"); + }, @"foo"); + } + + public void RescueSplat1() { + AssertOutput(delegate() { + CompilerTest(@" +begin + raise IOError +rescue *[IOError] + puts 'ok' +end +"); + }, @"ok"); + } + + public void RescueSplat2() { + AssertOutput(delegate() { + CompilerTest(@" +begin + raise IOError +rescue SyntaxError, *[SyntaxError, SyntaxError, IOError, nil] + puts 'ok' +end +"); + }, @"ok"); + } + + public void RescueSplat3() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def respond_to? name + puts '?' + name.to_s + true + end + + def to_ary + puts 'to_ary' + [SyntaxError, IOError] + end +end + +begin + raise IOError +rescue *C.new + puts 'ok' +end +"); + }, @" +?to_ary +to_ary +ok +"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/HashTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/HashTests.cs new file mode 100644 index 0000000000..025ba3c501 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/HashTests.cs @@ -0,0 +1,110 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace IronRuby.Tests { + public partial class Tests { + + + + public void Scenario_RubyHashes1A() { + AssertOutput(delegate() { + CompilerTest(@" +x = Hash.[](1, 'a', 2, 'b') +print x[1], x[2] +"); + }, @"ab"); + } + + public void Scenario_RubyHashes1B() { + AssertOutput(delegate() { + CompilerTest(@" +puts Hash.[]() +"); + }, ""); + } + + public void Scenario_RubyHashes1C() { + AssertExceptionThrown(delegate() { + CompilerTest(@" +puts Hash.[](1) +"); + }); + } + + public void Scenario_RubyHashes2() { + AssertOutput(delegate() { + CompilerTest(@" +x = { 1 => 'a' } +puts x[1] +"); + }, @" +a +"); + } + + public void Scenario_RubyHashes3() { + AssertOutput(delegate() { + CompilerTest(@" +def foo(a,b,c) + puts a, b, c +end + +foo Hash.[](1 => 'a'), { 2 => 'b' }, 3 => 'c' +"); + }, @" +1a +2b +3c +"); + } + + public void Scenario_RubyHashes4() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def []=(a,b) + print a[1], a[2], b + end +end + +C.new[1 => 'a', 2 => 'b'] = 'c' +"); + }, @"abc"); + } + + + /// + /// Equality comparer doesn't call 'eql?' if the values are reference-equal. + /// + public void EqualityComparer1() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def eql? other + puts 'eql?' + end +end + +c = C.new + +hash = { c => 1 } +p hash[c] +"); + }, @"1"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/HostingTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/HostingTests.cs new file mode 100644 index 0000000000..d7e1b0fb5c --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/HostingTests.cs @@ -0,0 +1,290 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using IronRuby.Builtins; +using System.Diagnostics; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Tests { + public partial class Tests { + public void RubyHosting1() { + ScriptScope scope = Engine.Runtime.CreateScope(); + scope.SetVariable("x", 1); + scope.SetVariable("y", 2); + + // TODO: could we replace def self.z with z = ? - via dynamic binding + Engine.Execute("def self.z; x + y; end", scope); + Engine.Execute("def self.w(a); a + 1; end", scope); + + int z = scope.GetVariable>("z")(); + Assert(z == 3); + + int w = scope.GetVariable>("w")(1); + Assert(w == 2); + } + + public void RubyHosting2() { + Hashtable hash = new Hashtable(); + hash.Add("foo", "bar"); + + ScriptScope scope = Engine.CreateScope(); + scope.SetVariable("h", hash); + + AssertOutput(() => + Engine.Execute(@" +def h.method_missing name + get_Item(name.to_clr_string) +end + +puts h.foo +", scope), @" +bar +"); + } + + public void RubyHosting3() { + var searchPaths = Engine.GetSearchPaths(); + Assert(new List(searchPaths)[searchPaths.Count - 1] == "."); + + // TODO: + // the library paths are incorrect (not combined with location of .exe file) in partial trust: + if (_driver.PartialTrust) return; + + bool result = Engine.RequireRubyFile("fcntl"); + Assert(result == true); + + var module = Runtime.Globals.GetVariable("Fcntl"); + Assert(module is RubyModule && ((RubyModule)module).Name == "Fcntl"); + } + + public void RubyHosting4() { + // TODO: LanguageSetup should have an indexer: + //var ruby = Ruby.CreateEngine((setup) => { + // setup["InterpretedMode"] = true; + // setup["SearchPaths"] = "blah"; + //}); + + var ruby = Ruby.CreateEngine((setup) => { + setup.InterpretedMode = true; + }); + + Debug.Assert(ruby.Setup.InterpretedMode == true); + } + + public void Scenario_RubyEngine1() { + ScriptScope scope = Runtime.CreateScope(); + object x = Engine.CreateScriptSourceFromString("1 + 1").Execute(scope); + AssertEquals(x, 2); + } + + public void Scenario_RubyInteractive() { + ScriptScope scope = Runtime.CreateScope(); + AssertOutput(delegate() { + Engine.CreateScriptSourceFromString("1+1", SourceCodeKind.InteractiveCode).Execute(scope); + }, "=> 2"); + } + + public void Scenario_RubyConsole1() { + ScriptScope module = Runtime.CreateScope(); + AssertOutput(delegate() { + Engine.CreateScriptSourceFromString("class C; def foo() puts 'foo'; end end", SourceCodeKind.InteractiveCode).Execute(module); + Engine.CreateScriptSourceFromString("C.new.foo", SourceCodeKind.InteractiveCode).Execute(module); + }, @" +=> nil +foo +=> nil +"); + } + + public void Scenario_RubyConsole2() { +#if OBSOLETE + // TODO: interop + ScriptScope module = ScriptDomainManager.CurrentManager.CreateModule("Scenario_RubyConsole2"); + module.SetVariable("a", 0); + RB.Execute(module, RB.CreateScriptSourceFromString("10.times { |x| a = a + x + 1}", SourceCodeKind.Statements)); + object a = module.LookupVariable("a"); + Assert((int)a == 55); + + module.SetVariable("b", 1); + RB.Execute(module, RB.CreateScriptSourceFromString("10.times { |x| b = b + x + 1}", SourceCodeKind.Statements)); + object b = module.LookupVariable("b"); + Assert((int)b == 56); +#endif + } + + public void Scenario_RubyConsole3() { + // TODO: bug in top-level scope + + // ScriptModule module = ScriptDomainManager.CurrentManager.CreateModule("Scenario_RubyConsole3"); + // RB.Execute(@" + //for i in [11] do + // j = 1 + //end", module); + // object a = module.LookupVariable("j"); + // Assert((int)a == 1); + } + + public void Scenario_RubyConsole4() { + + // XAssertOutput(delegate() { + // RB.ExecuteInteractiveCode("x = 1"); + // RB.ExecuteInteractiveCode("puts x"); + // }, @" + //=> 1 + //=> nil + //1"); + } + + public void ObjectOperations1() { + var cls = Engine.Execute(@" +class C + def foo *a + p a + end + self +end +"); + var obj = Engine.Operations.CreateInstance(cls) as RubyObject; + Debug.Assert(obj != null && obj.Class.Name == "C"); + + obj = Engine.Operations.InvokeMember(cls, "new") as RubyObject; + Debug.Assert(obj != null && obj.Class.Name == "C"); + + var foo = Engine.Operations.GetMember(obj, "foo") as RubyMethod; + Debug.Assert(foo != null && foo.Name == "foo" && foo.Target == obj); + + AssertOutput(() => Engine.Operations.Call(foo, 1, 2), "[1, 2]"); + AssertOutput(() => Engine.Operations.InvokeMember(obj, "foo", 1, 2), "[1, 2]"); + } + + public void ObjectOperations2() { + var cls = Engine.Execute(@" +class C + def foo *a + p a + end + def self.bar + end + undef_method :freeze + self +end +"); + var names = Engine.Operations.GetMemberNames(cls); + Assert(!names.Contains("foo")); + Assert(names.Contains("taint")); + Assert(names.Contains("bar")); + + var obj = Engine.Operations.CreateInstance(cls); + names = Engine.Operations.GetMemberNames(obj); + Assert(names.Contains("foo")); + Assert(names.Contains("taint")); + Assert(!names.Contains("freeze")); + Assert(!names.Contains("bar")); + } + + public void PythonInterop1() { + var py = Runtime.GetEngine("python"); + Engine.Execute(@" +class C + def foo + puts 'foo' + end +end +"); + + py.CreateScriptSourceFromString(@" +import C +C.new().foo() # TODO: C().foo() +", SourceCodeKind.Statements).Execute(); + } + + public void PythonInterop2() { + var py = Runtime.GetEngine("python"); + + py.CreateScriptSourceFromString(@" +class C(object): + def foo(self): + print 'foo' +", SourceCodeKind.Statements).Execute(Runtime.Globals); + + Engine.Execute(@" +p C #TODO: C.new +"); + } + + public void CustomTypeDescriptor1() { + var cls = Engine.Execute(@" +class C + attr_accessor :a + def b + end + def b= x + @written = true + end + def c + @written + end + def d= x + end + def e x + end + def e= x + end + def initialize + @a = 'Hello world' + @written = false + end + self +end +"); + var obj = Engine.Operations.CreateInstance(cls); + Debug.Assert(obj != null); + var ictd = Engine.Operations.CreateInstance(cls) as ICustomTypeDescriptor; + Debug.Assert(ictd != null); + Assert(ictd.GetClassName() == "C"); + var props = ictd.GetProperties(); + Assert(props.Count == 2); + props[0].SetValue(obj, "abc"); + props[1].SetValue(obj, "abc"); + Assert(Engine.Operations.InvokeMember(obj, "a").Equals("abc")); + Assert(Engine.Operations.InvokeMember(obj, "c").Equals(true)); + } + + public void CustomTypeDescriptor2() { + Context.ObjectClass.SetConstant("C", Context.GetClass(typeof(ArrayList))); + var cls = Engine.Execute(@" +class D < C + attr_accessor :a + self +end +"); + var obj = Engine.Operations.CreateInstance(cls); + Debug.Assert(obj != null); + var ictd = Engine.Operations.CreateInstance(cls) as ICustomTypeDescriptor; + Debug.Assert(ictd != null); + Assert(ictd.GetClassName() == "D"); + var props = ictd.GetProperties(); + Assert(props.Count == 1); + props[0].SetValue(obj, "abc"); + Assert(Engine.Operations.InvokeMember(obj, "a").Equals("abc")); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/InitializerTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/InitializerTests.cs new file mode 100644 index 0000000000..9cbd59e1ad --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/InitializerTests.cs @@ -0,0 +1,267 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Tests { + public partial class Tests { + + /// + /// Tests whether user initializer is properly called (w/o block argument). + /// If this fails all RSpec tests fails. + /// + public void Scenario_RubyInitializers0() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def initialize *a + puts a.inspect + end +end + +C.new 1,2,3 +"); + }, @"[1, 2, 3]"); + } + + public void Scenario_RubyInitializers1() { + AssertOutput(delegate() { + CompilerTest(@" +x = Array.new(5) { break 'foo' } +puts x +"); + }, @"foo"); + } + + public void Scenario_RubyInitializers2A() { + AssertOutput(delegate() { + CompilerTest(@" +class A + def initialize + yield + end +end + +x = A.new { break 'foo' } +puts x +"); + }, @"foo"); + } + + /// + /// Block's proc-converter ("new") is not alive when the block breaks. + /// This tests whether "new" frame is cleaned up correctly. + /// + public void Scenario_RubyInitializers2B() { + AssertOutput(delegate() { + CompilerTest(@" +class A + def initialize &b + $b = b + end +end + +x = A.new { break 'foo' } +1.times(&$b) rescue p $! +"); + }, @"#"); + } + + /// + /// Retry returns from the 'new' frame (skipping initialize frame) as that frame is its proc-converter. + /// The block is not redefined however. Only the proc-converter frame is updated to the new instance of the 'new' frame. + /// + public void Scenario_RubyInitializers3() { + AssertOutput(delegate() { + CompilerTest(@" +class A + def initialize *a + yield + end +end + +$i = 0 +y = A.new($i += 1) { + puts $i + if $i < 3 then + retry + end +} +"); + }, @" +1 +2 +3"); + } + + /// + /// The same case as Scenario_RubyInitializers3, but with initializer defined in library. + /// + public void Scenario_RubyInitializers4A() { + AssertOutput(delegate() { + CompilerTest(@" +$i = 0 +y = Array.new($i += 1) { + puts $i + if $i < 3 then + retry + end +} +puts y.inspect +"); + }, @" +1 +2 +3 +3 +3 +[nil, nil, nil]"); + } + + /// + /// A block is yielded to from "initialize", not from the factory. + /// + public void Scenario_RubyInitializers4B() { + AssertOutput(delegate() { + CompilerTest(@" +class Array + def initialize *args + puts 'init' + p self + end +end +Array.new(10) { puts 'unreachable' } +"); + }, @" +init +[] +"); + } + + public void Scenario_RubyInitializers4C() { + AssertOutput(delegate() { + CompilerTest(@" +p Array.new(10) { break 5 } +"); + }, @"5"); + } + + /// + /// Test initializers with procs (instead of blocks - the proc-converter is different now). + /// + public void Scenario_RubyInitializers5() { + AssertOutput(delegate() { + CompilerTest(@" +def bar &p + x = Array.new(5, &p) + puts ""A: #{x}"" +end + +z = bar { |i| puts ""B: #{i}""; break 'foo' } +puts ""C: #{z}"" +"); + }, @" +B: 0 +C: foo +"); + } + + public void RubyInitializersCaching1() { + AssertOutput(delegate() { + CompilerTest(@" +class C; end +C.new + +class C + def initialize + puts 'init' + end +end +C.new +"); + }, @"init"); + } + + public void RubyInitializersCaching2() { + AssertOutput(delegate() { + CompilerTest(@" +class S < String; end +puts S.new('foo') + +class S + def initialize a + super 'bar' + end +end +puts S.new('baz') +"); + }, @" +foo +bar +"); + } + + public void RubyAllocators1() { + AssertOutput(delegate() { + CompilerTest(@" +p Array.allocate +p Hash.allocate +p Range.allocate +p Regexp.allocate +p String.allocate +p MatchData.allocate +p Object.allocate +p Module.allocate +p Class.allocate +p Struct.new(:f,:g).allocate +p Exception.allocate +p IO.allocate + +p Binding.allocate rescue p $! +p Struct.allocate rescue p $! +p Method.allocate rescue p $! +p UnboundMethod.allocate rescue p $! +p Bignum.allocate rescue p $! +p Fixnum.allocate rescue p $! +p TrueClass.allocate rescue p $! +p FalseClass.allocate rescue p $! +p NilClass.allocate rescue p $! +p Proc.allocate rescue p $! +"); + }, @" +[] +{} +nil..nil +// +"""" +# +# +# +# +# f=nil, g=nil> +# +# +# +# +# +# +# +# +# +# +# +# +", OutputFlags.Match); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/LoaderTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/LoaderTests.cs new file mode 100644 index 0000000000..c2297db9f1 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/LoaderTests.cs @@ -0,0 +1,178 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.IO; +using System.Dynamic; +using IronRuby.Builtins; +using IronRuby.Runtime; + +namespace IronRuby.Tests { + public partial class Tests { + + public void Loader_Assemblies1() { + string assembly; + string type; + string str; + bool b; + + str = "a.rb"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == false); + + str = "IronRuby"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == false); + + str = @"..\foo\bar\a,b.rb"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == false); + + str = "IronRuby.Runtime.RubyContext, IronRuby, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == true && + assembly == "IronRuby, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" && + type == "IronRuby.Runtime.RubyContext" + ); + + str = "IronRuby, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == true && assembly == str && type == null); + + str = "IronRuby, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == true && assembly == str && type == null); + + str = "IronRuby, Version=1.0.0.0"; + b = Loader.TryParseAssemblyName(str, out type, out assembly); + Assert(b == true && assembly == str && type == null); + } + + public void Require1() { + if (_driver.PartialTrust) return; + + try { + string temp = _driver.MakeTempDir(); + Context.Loader.SetLoadPaths(new[] { temp }); + File.WriteAllText(Path.Combine(temp, "a.rb"), @"C = 123"); + + AssertOutput(delegate() { + CompilerTest(@" +puts(require('a')) +puts C +"); + }, @" +true +123 +"); + + AssertOutput(delegate() { + CompilerTest(@" +puts(require('a.rb')) +puts C +"); + }, @" +false +123 +"); + } finally { + File.Delete("a.rb"); + } + } + + public void Load1() { + if (_driver.PartialTrust) return; + + try { + string temp = _driver.MakeTempDir(); + Context.Loader.SetLoadPaths(new[] { temp }); + + File.WriteAllText(Path.Combine(temp, "a.rb"), @"C = 123"); + + AssertOutput(delegate() { + CompilerTest(@" +puts(load('a.rb', true)) +puts C rescue puts 'error' +"); + }, @" +true +error +"); + } finally { + File.Delete("a.rb"); + } + } + + public void RequireInterop1() { + if (_driver.PartialTrust) return; + + try { + string temp = _driver.MakeTempDir(); + Context.Loader.SetLoadPaths(new[] { temp }); + + File.WriteAllText(Path.Combine(temp, "a.py"), @" +print 'Hello from Python' +"); + AssertOutput(delegate() { + CompilerTest(@" +require('a') +"); + }, @" +Hello from Python +"); + + } finally { + File.Delete("b.py"); + } + } + + public class TestLibraryInitializer1 : LibraryInitializer { + protected override void LoadModules() { + Context.ObjectClass.SetConstant("TEST_LIBRARY", "hello from library"); + DefineGlobalModule("Object", typeof(Object), ObjectMonkeyPatch, null, RubyModule.EmptyArray); + } + + private void ObjectMonkeyPatch(RubyModule/*!*/ module) { + Debug.Assert(module == Context.ObjectClass); + + module.DefineLibraryMethod("object_monkey", 0x9, new System.Delegate[] { + new Func(MonkeyWorker), + }); + } + + // TODO: might be called by MI.Invoke -> needs to be public in partial trust + public static string MonkeyWorker(object obj) { + return "This is monkey!"; + } + } + + public void LibraryLoader1() { + Context.DefineGlobalVariable("lib_name", MutableString.Create(typeof(TestLibraryInitializer1).AssemblyQualifiedName)); + + AssertOutput(delegate() { + CompilerTest(@" +require($lib_name) +puts TEST_LIBRARY +puts object_monkey +"); + }, @" +hello from library +This is monkey! +"); + + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/LoopTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/LoopTests.cs new file mode 100644 index 0000000000..f0ce58cc89 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/LoopTests.cs @@ -0,0 +1,317 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Tests { + public partial class Tests { + + + public void Scenario_RubyForLoop1() { + AssertOutput(delegate() { + CompilerTest(@" +for a in [1,2,3] + x = 'ok' + print a +end + +print x # x visible here, for-loop doesn't define a scope +"); + }, "123ok"); + } + + public void Scenario_RubyForLoop2() { +#if OBSOLETE + ScriptScope module = ScriptDomainManager.CurrentManager.CreateModule("x"); + module.SetVariable("list", PY.Execute(module, PY.CreateScriptSourceFromString("[1,2,3]"))); + + AssertOutput(delegate() { + RB.Execute(module, RB.CreateScriptSourceFromString(@" +for a in list + print a +end +", SourceCodeKind.Statements)); + }, "123"); +#endif + } + + public void Scenario_RubyWhileLoop1() { + AssertOutput(delegate() { + CompilerTest(@" +i = 0 +x = while i < 4 do + puts i + i = i + 1 +end +puts x +"); + }, @" +0 +1 +2 +3 +nil +"); + } + + public void Scenario_RubyWhileLoop2() { + AssertOutput(delegate() { + CompilerTest(@" +i = 3 +x = while i > 0 do + puts i + if i == 2 then + break + end + i = i - 1 +end +puts x +"); + }, @" +3 +2 +nil +"); + } + + /// + /// Break in a while loop. + /// + public void Scenario_RubyWhileLoop3() { + AssertOutput(delegate() { + CompilerTest(@" +i = 3 +x = while i > 0 do + puts i + if i == 2 then + break 'foo' + end + i = i - 1 +end +puts x +"); + }, @" +3 +2 +foo +"); + } + + /// + /// Redo in a while loop. + /// + public void Scenario_RubyWhileLoop4() { + AssertOutput(delegate() { + CompilerTest(@" +i = 3 +j = 2 +x = while i > 0 do + puts i + if i == 2 and j > 0 then + j = j - 1 + redo + end + i = i - 1 +end +puts x +"); + }, @" +3 +2 +2 +2 +1 +nil +"); + } + + /// + /// Next in a while loop. + /// + public void Scenario_RubyWhileLoop5() { + AssertOutput(delegate() { + CompilerTest(@" +i = 3 +j = 2 +x = while i > 0 do + puts i + if i == 2 and j > 0 then + j = j - 1 + next 'foo' + end + i = i - 1 +end +puts x +"); + }, @" +3 +2 +2 +2 +1 +nil +"); + } + + /// + /// Loop break from within class declaration. + /// + public void Scenario_RubyWhileLoop6() { + AssertOutput(delegate() { + CompilerTest(@" +i = 0 +while i < 5 do + puts i + + class C + puts 'in C' + break + end + + i = i + 1 +end +"); + }, @" +0 +in C +"); + } + + public void Scenario_RubyUntilLoop1() { + AssertOutput(delegate() { + CompilerTest(@" +i = 1 +until i > 3 do + puts i + i = i + 1 +end +"); + }, @" +1 +2 +3"); + } + + public void Scenario_WhileLoopCondition1() { + AssertOutput(delegate() { + CompilerTest(@" +def w x + while x; p x; break; end +end + +w [] +w 0 +w 1 +w nil +w true +w false +"); + }, @" +[] +0 +1 +true +"); + } + + public void PostTestWhile1() { + AssertOutput(delegate() { + CompilerTest(@" +begin + puts 1 +end while (puts 2) +"); + }, @" +1 +2 +"); + } + + public void PostTestUntil1() { + AssertOutput(delegate() { + CompilerTest(@" +begin + puts 1 +end until (puts(2); true) +"); + }, @" +1 +2 +"); + } + + public void WhileModifier1() { + AssertOutput(delegate() { + CompilerTest(@" +puts 1 while (puts 2) +"); + }, @" +2 +"); + } + + public void UntilModifier1() { + AssertOutput(delegate() { + CompilerTest(@" +puts 1 until (puts(2); true) +"); + }, @" +2 +"); + } + + public void WhileModifier2() { + AssertOutput(delegate() { + CompilerTest(@" +$i = false +def c + puts 'c' + $i = !$i +end + +def x + puts 'x' +end + +(x) while c +"); + }, @" +c +x +c +"); + } + + public void UntilModifier2() { + AssertOutput(delegate() { + CompilerTest(@" +$i = true +def c + puts 'c' + $i = !$i +end + +def x + puts 'x' +end + +(x) until c +"); + }, @" +c +x +c +"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/MethodTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/MethodTests.cs new file mode 100644 index 0000000000..0ecd0b3e7b --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/MethodTests.cs @@ -0,0 +1,274 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace IronRuby.Tests { + public partial class Tests { + public void Scenario_RubySimpleCall1() { + AssertOutput(delegate { + CompilerTest(@" +puts nil +"); + }, "nil"); + + } + + public void Scenario_RubySimpleCall2() { + AssertExceptionThrown(delegate { + CompilerTest(@" +def foo a,c=1,*e +end + +foo + +"); + }); + + } + + public void Scenario_RubySimpleCall3() { + AssertOutput(delegate { + CompilerTest(@" +y = nil +puts y + +x = 123 +puts x +"); + }, @" +nil +123"); + + } + + /// + /// LambdaExpression gets converted to a wrapper. + /// + public void Scenario_RubySimpleCall4() { + AssertOutput(delegate { + CompilerTest(@" +def foo a,b,c,d,e,f,g,h,i,j + puts 123 +end +foo 1,2,3,4,5,6,7,8,9,10 +"); + }, @"123"); + + } + + public void Scenario_RubySimpleCall5() { + AssertOutput(delegate { + Engine.CreateScriptSourceFromString(@" +class A +end + +class B < A +end + +B.new.foo rescue 0 + +class A + def foo + puts 'foo' + end +end + +B.new.foo +").ExecuteProgram(); + }, @"foo"); + } + + public void Send1() { + AssertOutput(delegate { + CompilerTest(@" +class C + def foo *a + puts ""C::foo *#{a.inspect}, &#{block_given?}"" + end + + alias []= :send +end + +x = C.new +q = lambda {} + +x.send :foo +x.send :foo, &q +x.send :foo, &nil +x.send :foo, 1 +x.send :foo, 1, &q +x.send :foo, 1, &nil +x.send :foo, 1, 2 +x.send :foo, 1, 2, &q +x.send :foo, 1, 2, &nil +x.send :foo, 1, 2, 3 +x.send :foo, 1, 2, 3, &q +x.send :foo, 1, 2, 3, &nil + +x.send *[:foo, 1,2,3] +x.send :foo, 1, *[2,3], &q +x[:foo,*[1,2]] = 3 +x[*:foo] = 1 +x[] = :foo +"); + }, @" +C::foo *[], &false +C::foo *[], &true +C::foo *[], &false +C::foo *[1], &false +C::foo *[1], &true +C::foo *[1], &false +C::foo *[1, 2], &false +C::foo *[1, 2], &true +C::foo *[1, 2], &false +C::foo *[1, 2, 3], &false +C::foo *[1, 2, 3], &true +C::foo *[1, 2, 3], &false +C::foo *[1, 2, 3], &false +C::foo *[1, 2, 3], &true +C::foo *[1, 2, 3], &false +C::foo *[1], &false +C::foo *[], &false +"); + } + + /// + /// Send propagates the current scope. + /// + public void Send2() { + AssertOutput(delegate { + CompilerTest(@" +class C + public + send :private + + def foo + end + + p C.private_instance_methods(false) +end +"); + }, @" +[""foo""] +"); + } + + public void AttributeAccessors1() { + AssertOutput(delegate { + CompilerTest(@" +class C + attr_accessor :foo + alias :bar :foo + alias :bar= :foo= +end + +x = C.new +x.foo = 123 +x.bar = x.foo + 1 +puts x.foo, x.bar +"); + }, @" +124 +124 +"); + } + + public void AttributeAccessors2() { + AssertOutput(() => CompilerTest(@" +class C + attr_accessor :foo +end + +x = C.new +p x.send('foo=', 123) +p x.send('foo') + +"), @" +123 +123 +"); + } + + public void MethodAdded1() { + AssertOutput(delegate { + CompilerTest(@" +class Module + def method_added name + puts name + end +end +"); + }, @" +method_added +"); + } + + public void VisibilityCaching1() { + AssertOutput(delegate { + CompilerTest(@" +class C + def foo + puts 'foo' + end + + def method_missing name + puts 'mm' + end +end + +x = C.new +4.times do |$i| + class C + case $i + when 0: private :foo + when 1: public :foo + when 2: private :foo + end + end + x.foo +end +"); + }, @" +mm +foo +mm +mm +"); + } + + public void ModuleFunctionVisibility1() { + AssertOutput(delegate { + CompilerTest(@" +module M + private + def f + end + + module_function :f +end + +p M.singleton_methods(false) +p M.private_instance_methods(false) +p M.public_instance_methods(false) +"); + }, @" +[""f""] +[""f""] +[] +"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/MiscTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/MiscTests.cs new file mode 100644 index 0000000000..4a642fcb52 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/MiscTests.cs @@ -0,0 +1,944 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using IronRuby.Compiler; +using IronRuby.Compiler.Ast; + +namespace IronRuby.Tests { + + public partial class Tests { + public void Scenario_Globals1() { + Context.DefineGlobalVariable("x", 123); + CompilerTest(@"$z = $x"); + object z = Context.GetGlobalVariable("z"); + AssertEquals(z, 123); + } + + public void Scenario_RubyLocals1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo() + x = 1 + y = 2 + puts x + y +end + +foo +"); + }, "3"); + } + + public void Scenario_UninitializedVars1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + x = 1 if false + puts x +end + +foo +"); + }, "nil"); + } + + public void Scenario_UninitializedVars2() { + AssertExceptionThrown(delegate() { + CompilerTest(@" +def foo + puts x +end + +foo +"); + }); + } + + // Fixnum doesn't have identity in Ruby + public void InstanceVariables1() { + AssertOutput(delegate() { + CompilerTest(@" +class Fixnum + def foo= a + @x = a + end + def foo + @x + end +end + +a = 1 +b = 1 +c = 2 + +a.foo = 1 +b.foo = 2 +c.foo = 3 +puts a.foo, b.foo, c.foo +"); + }, + @" +2 +2 +3"); + } + + // Float has an identity in Ruby + public void InstanceVariables2() { + AssertOutput(delegate() { + CompilerTest(@" +class Float + def foo= a + @x = a + end + def foo + @x + end +end + +a = 1.0 +b = 1.0 + +a.foo = 1 +b.foo = 2 +puts a.foo, b.foo +"); + }, + @" +1 +2"); + } + + + public void Scenario_RubyParams1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo(a,b) + puts a + b +end + +puts foo(1,2) +"); + }, + @" +3 +nil"); + } + + public void Scenario_RubyParams2() { + AssertOutput(delegate() { + CompilerTest(@" +def foo(a, b, c = a + b, d = c + 1) + puts a,b,c,d +end + +foo(1,2) +"); + }, + @" +1 +2 +3 +4 +"); + } + + public void Scenario_RubyMethodMissing1() { + AssertOutput(delegate { + CompilerTest(@" +class C + def method_missing(name, *a) + puts name, a + end +end + +C.new.foo 1,2,3 +"); + }, @" +foo +1 +2 +3 +"); + + } + + public void Scenario_RubyMethodMissing2() { + AssertExceptionThrown(delegate { + CompilerTest(@"unknown_method"); + }, delegate(MissingMethodException e) { + return e.Message.StartsWith("undefined method"); + }); + } + + public void Scenario_RubySingletonConstants1() { + AssertOutput(delegate { + CompilerTest(@" +puts nil.to_s +puts true.to_s +puts false.to_s + +puts nil | false +puts true & [] +puts false ^ [] +puts nil.nil? +puts nil.to_i +puts nil.to_f +"); + }, @" + +true +false +false +true +true +true +0 +0.0 +"); + + } + + public void Scenario_RubyMath1() { + AssertOutput(delegate { + CompilerTest(@" +puts Math.acos(1.0) +puts Math.acos(1) +"); + }, @" +0.0 +0.0"); + + } + + public void Scenario_RubyScopeParsing() { + LoggingErrorSink log = new LoggingErrorSink(); + + SourceUnitTree p; + SourceUnit unit; + + unit = Context.CreateSnippet(@" + class c << G + end + ", SourceCodeKind.File); + + p = new Parser().Parse(unit, new RubyCompilerOptions(), log); + Assert(p == null && log.FatalErrorCount == 1); + log.ClearCounters(); + + unit = Context.CreateSnippet(@" + def goo(&b) + end + + class C + def foo() + x.goo() { + goo() { + class << M + def bar() + goo() { + } + end + end + } + } + end + end + + BEGIN { goo1() { } } + END { goo2() { } } + ", SourceCodeKind.File); + + p = new Parser().Parse(unit, new RubyCompilerOptions(), ErrorSink.Null); + Assert(p != null && !log.AnyError); + log.ClearCounters(); + + unit = Context.CreateSnippet(@" + for x in array do + goo() + end + ", SourceCodeKind.File); + + p = new Parser().Parse(unit, new RubyCompilerOptions(), ErrorSink.Null); + Assert(p != null && !log.AnyError); + log.ClearCounters(); + } + + /// + /// Tests that class lexical scopes are applied to method definitions. + /// + public void Scenario_RubyScopes1() { + AssertOutput(delegate() { + CompilerTest(@" +class A; def foo; print 'A'; end; end +class B; def foo; print 'B'; end; end +A.new.foo +B.new.foo +"); + }, @"AB"); + } + + /// + /// Uninitialized local variables. + /// + public void Scenario_RubyScopes2A() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + x = 1 if false + p x +end + +foo +"); + }, @"nil"); + } + + /// + /// Uninitialized local variables in closure. + /// + public void Scenario_RubyScopes2B() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + x = 1 if false + 1.times { p x } +end + +foo +"); + }, @"nil"); + } + + /// + /// Tests that variables defined in module/class locals scope are not visible outside. + /// + public void Scenario_RubyScopes3() { + AssertOutput(delegate() { + CompilerTest(@" +y = 'var' +class C + x = 'var' +end + +def x; 'method'; end +def y; 'method'; end + +puts x +puts y +"); + }, @" +method +var"); + } + + public void Scenario_RubyScopes4() { + AssertOutput(delegate() { + CompilerTest(@" +module Bar + class C + module M + puts self + end + end +end +"); + }, @" +Bar::C::M +"); + } + + public void Scenario_RubyScopes5() { + AssertOutput(delegate() { + CompilerTest(@" +module M + module Z + E = self + end +end + +include M::Z + +puts E +"); + }, @" +M::Z +"); + } + + /// + /// Nested module scopes - they don't have DLR tuples and therefore need to be skipped when looking up a storage from closure. + /// + public void Scenario_RubyScopes6() { + CompilerTest(@" +1.times { + module M + module N + z = 1 + 1.times { + x = z + } + end + end +} +"); + } + + public void NumericLiterals1() { + AssertOutput(delegate() { + CompilerTest(@" +puts(1) +puts(-1) +puts(+1) +puts(1.1) +puts(-1.1) +puts(+1.1) + +x = 2.0 +puts x +puts 2.0 +puts 2.1560 +"); + }, @" +1 +-1 +1 +1.1 +-1.1 +1.1 +2.0 +2.0 +2.156 +"); + } + + public void Scenario_RubyInclusions1() { + AssertOutput(delegate() { + CompilerTest(@" +module M + def foo + puts 'foo' + end +end + +class C + include M +end + +C.new.foo +"); + }, "foo"); + } + + public void Scenario_RubyClassVersions1() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def m + puts 'v1' + end +end + +C.new.m + +class C + def m + puts 'v2' + end +end + +C.new.m +"); + }, +@" +v1 +v2 +"); + } + + public void Scenario_RubyClassVersions2() { + AssertOutput(delegate() { + CompilerTest(@" +module M + def m; puts 'v1'; end +end + +module N + def m; puts 'v2'; end +end + +class C + include M +end + +class D < C +end + +D.new.m + +class D + include N +end + +D.new.m +"); + }, +@" +v1 +v2 +"); + } + + public void InvokeMemberCache1() { + AssertOutput(delegate() { + CompilerTest(@" +def try_call + B.m rescue puts 'error' +end + +class B; end + +try_call # m not defined + +class Class + def m; puts 'ok' end +end + +try_call # m defined on Class +"); + }, +@" +error +ok +"); + } + + public void Scenario_RubyBlockExpressions1() { + AssertOutput(delegate() { + CompilerTest(@" +puts class Foo + 'A' +end + +puts def bar + 'B' +end + +puts module M + 'C' +end + +puts begin + 'D' +end + +x = ('E'; 'F') +puts x +"); + }, @" +A +nil +C +D +F"); + } + + public void Scenario_ClassVariables1() { + AssertOutput(delegate() { + CompilerTest(@" +module M + @@m = 1 + + def goo + @@n = 2 + end +end + +class C + include M + + def foo + @@a = 1 + end + + @@b = 2 + @@c = 1 +end + +class D < C + def bar + @@c = 3 + end + + @@d = 4 +end + +C.new.foo +D.new.bar +C.new.goo + +p C.class_variables.sort +p D.class_variables.sort +p M.class_variables.sort"); + }, @" +[""@@a"", ""@@b"", ""@@c"", ""@@m"", ""@@n""] +[""@@a"", ""@@b"", ""@@c"", ""@@d"", ""@@m"", ""@@n""] +[""@@m"", ""@@n""] +"); + } + + public void Scenario_ClassVariables2() { + AssertOutput(delegate() { + CompilerTest(@" +class C + @@x = 1 +end + +class D < C + remove_class_variable :@@x rescue puts 'Error' + @@y = 'foo' + puts remove_class_variable(:@@y) +end +"); + }, @" +Error +foo +"); + } + + public void Scenario_RubyReturnValues1() { + AssertOutput(delegate() { + CompilerTest(@" +x = while true do + break 1,2,3 +end +puts x +"); + }, @" +1 +2 +3 +"); + } + + public void Scenario_RubyReturnValues2() { + AssertOutput(delegate() { + CompilerTest(@" +x = while true do + break 1 => 2, 3 => 4 +end +puts x +"); + }, @"1234"); + } + + public void Scenario_RubyReturnValues3() { + AssertOutput(delegate() { + CompilerTest(@" +x = while true do + break 'a', 'b', 1 => 2, 3 => 4 +end +puts x +"); + }, @" +a +b +1234"); + } + + public void Scenario_RubyReturnValues4() { + AssertOutput(delegate() { + CompilerTest(@" +x = while true do + break 'a', 'b', 1 => 2, 3 => 4, *['A', 'B'] +end +puts x +"); + }, @" +a +b +1234 +A +B"); + } + + public void Scenario_RubyReturnValues5() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + return 1,2,3, 4 => 5, *[6,7] +end +puts foo +"); + }, @" +1 +2 +3 +45 +6 +7 +"); + } + + public void Scenario_RubyReturnValues6() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + return *$x = [1,2] +end +$y = foo + +puts $x.object_id == $y.object_id +puts $x.inspect +"); + }, @" +true +[1, 2]"); + } + + public void Scenario_RubyClosures1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + y = 10 + 3.times { |x| puts x + y } +end + +foo +"); + }, @" +10 +11 +12"); + } + + public void Scenario_RubyReturn1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo() + return 'foo' +end + +puts foo +"); + }, + "foo"); + } + + public void Scenario_RubyArgSplatting1() { + AssertOutput(delegate() { + CompilerTest(@" +def foo(a,b,c) + print a,b,c +end + +foo(*[1,2,3]) +"); + }, @"123"); + } + + public void Scenario_RubyArgSplatting2() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def []=(a,b,c) + print a,b,c + end +end + +x = [1,2] +C.new[*x] = 3 +C.new[1, *[2]] = 3 +"); + }, @"123123"); + } + + public void Scenario_RubyArgSplatting3() { + AssertOutput(delegate() { + CompilerTest(@" +def foo(a,b,c) + print a,b,c + puts +end + +foo(1,2,*3) +foo(1,2,*nil) +"); + }, @" +123 +12nil"); + } + + public void ClassVariables1() { + AssertOutput(delegate() { + CompilerTest(@" +module M + @@m = 3 + def moo + puts @@m + puts @@a rescue puts '!a' + end +end + +class A + @@a = 1 + + include M + + class B + @@b = 2 + def foo + puts @@b + end + end + + def foo + puts @@a + end +end + +x = A.new +y = A::B.new +x.foo +x.moo +y.foo +"); + }, @" +1 +3 +!a +2 +"); + } + + public void Scenario_RubyThreads1() { + AssertOutput(delegate() { + CompilerTest(@" +t = Thread.new 1,2,3 do |a,b,c| + puts a,b,c +end +t.join +puts 4 +"); + }, @" +1 +2 +3 +4 +"); + } + + public void Scenario_YieldCodeGen() { + AssertOutput(delegate() { + CompilerTest(@" +def foo + puts 1, yield +end +foo { 2 } +"); + }, @" +1 +2 +"); + } + + public void Scenario_MainSingleton() { + AssertOutput(delegate() { + CompilerTest(@" +require 'mscorlib' +include System::Collections +puts ArrayList +"); + }, @"System::Collections::ArrayList"); + } + + public void Scenario_ModuleOps_Methods() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def ifoo + puts 'ifoo' + end +end + +class << C + $C1 = self + + def foo + puts 'foo' + end +end + +class C + alias_method(:bar,:foo) rescue puts 'Error 1' + instance_method(:foo) rescue puts 'Error 2' + puts method_defined?(:foo) + foo + + alias_method(:ibar,:ifoo) + instance_method(:ifoo) + puts method_defined?(:ifoo) + ifoo rescue puts 'Error 3' + + remove_method(:ifoo) +end + +C.new.ifoo rescue puts 'Error 4' +C.new.ibar +"); + }, @" +Error 1 +Error 2 +false +foo +true +Error 3 +Error 4 +ifoo +"); + } + + public void Methods() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def foo a,b + puts a + b + end +end + +class D < C +end + +c = C.new +p m = c.method(:foo) +p u = m.unbind +p n = u.bind(D.new) + +m[1,2] +n[1,2] +"); + }, @" +# +# +# +3 +3 +"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ModuleTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ModuleTests.cs new file mode 100644 index 0000000000..7dcac13aac --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/ModuleTests.cs @@ -0,0 +1,208 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Tests { + public partial class Tests { + public void ClassDuplication1() { + AssertOutput(delegate() { + CompilerTest(@" +class C + X = 'const' + @@x = 'cls-var' + def foo + puts self.class + puts @@x + end +end + +D = C.dup +D.new.foo +puts D::X +"); + }, @" +D +cls-var +const +"); + } + + public void ClassDuplication2() { + AssertOutput(delegate() { + CompilerTest(@" +class Object + def foo + puts 'foo' + end +end + +O = Object.dup + +class Object + remove_method :foo +end + +O.new.foo +"); + }, @" +foo +"); + } + + public void ClassDuplication3() { + AssertOutput(delegate() { + CompilerTest(@" +S = String.dup +p S.ancestors +puts S.instance_methods(false) - String.instance_methods(false) == [] +puts s = S.new('ghk') +puts s.reverse +"); + }, @" +[S, Enumerable, Comparable, Object, Kernel] +true +ghk +khg +"); + } + + /// + /// This is different from MRI. In MRI ancestors of a duplicated Object class are [O, Kernel]. + /// + public void ClassDuplication4() { + AssertOutput(delegate() { + CompilerTest(@" +O = Object.dup +p O.ancestors +p O.constants.include?('Object') +"); + }, @" +[O, Kernel, Object, Kernel] +true +"); + } + + public void ClassDuplication5() { + AssertOutput(delegate() { + CompilerTest(@" +require 'mscorlib' +p System::Collections.dup.constants.include?('IEnumerable') +"); + }, @" +true +"); + } + + public void ClassDuplication6() { + var output = @" +""foo"" +[1, 2] +""default"" +1..2 +/foo/ +"""" +[""a"", ""b""] +""foo"" +"; + + AssertOutput(delegate() { + CompilerTest(@" +p String.dup.new('foo') +p Array.dup.new([1,2]) +p Hash.dup.new('default')[1] +p Range.dup.new(1,2) +p Regexp.dup.new('foo') +p Module.dup.new.name +p Struct.dup.new(:a,:b).dup[1,2].dup.members +p Proc.dup.new { 'foo' }[] +"); + }, output); + + AssertOutput(delegate() { + CompilerTest(@" +class S < String; end +class A < Array; end +class H < Hash; end +class R < Range; end +class RE < Regexp; end +class M < Module; end +class St < Struct; end +class P < Proc; end + +p S.dup.new('foo') +p A.dup.new([1,2]) +p H.dup.new('default')[1] +p R.dup.new(1,2) +p RE.dup.new('foo') +p M.dup.new.name +p St.dup.new(:a,:b).dup[1,2].dup.members +p P.dup.new { 'foo' }[] +"); + }, output); + } + + public void MetaModules1() { + AssertOutput(delegate() { + CompilerTest(@" +class Meta < Module + def bar + puts 'bar' + end +end + +M = Meta.new + +module M + def foo + puts 'foo' + end +end + +class C + include M +end + +C.new.foo +M.bar +"); + }, @" +foo +bar +"); + } + + public void MetaModulesDuplication1() { + AssertOutput(delegate() { + CompilerTest(@" +class Meta < Module +end + +M = Meta.new +puts M.name +puts M.class + +N = M.dup +puts N.name +puts N.class +"); + }, @" +M +Meta +N +Meta +"); + } + + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/OverloadResolutionTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/OverloadResolutionTests.cs new file mode 100644 index 0000000000..7603f4b962 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/OverloadResolutionTests.cs @@ -0,0 +1,98 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using IronRuby.Runtime; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime.Calls; +using System.Dynamic.Binders; + +using MSA = System.Linq.Expressions; +using Ast = System.Linq.Expressions.Expression; +using IronRuby.Builtins; + +namespace IronRuby.Tests { + public partial class Tests { + + #region Block + + public void OverloadResolution_Block() { + var t = GetType(); + + var gse = new GlobalScopeExtension(Context, new Scope(), new object(), true); + var scope = new RubyTopLevelScope(gse, null, new SymbolDictionary()); + var proc = new Proc(ProcKind.Proc, null, scope, new BlockDispatcher0((x, y) => null, BlockSignatureAttributes.None)); + + var scopeArg = new MetaObject(Ast.Constant(proc.LocalScope), Restrictions.Empty, proc.LocalScope); + var contextArg = new MetaObject(Ast.Constant(Context), Restrictions.Empty, Context); + var instanceInt = new MetaObject(Ast.Constant(1), Restrictions.Empty, 1); + var str = "foo"; + var instanceStr = new MetaObject(Ast.Constant(str), Restrictions.Empty, str); + var procArg = new MetaObject(Ast.Constant(proc), Restrictions.Empty, proc); + var nullArg = new MetaObject(Ast.Constant(Ast.Constant(null)), Restrictions.Empty, null); + + var arguments = new[] { + // 1.times + new CallArguments(scopeArg, instanceInt, new MetaObject[0], RubyCallSignature.WithScope(0)), + // 1.times &nil + new CallArguments(scopeArg, instanceInt, new[] { nullArg }, RubyCallSignature.WithScopeAndBlock(0)), + // 1.times &p + new CallArguments(contextArg, instanceInt, new[] { procArg }, RubyCallSignature.WithBlock(0)), + // obj.times &p + new CallArguments(contextArg, instanceStr, new[] { procArg }, RubyCallSignature.WithBlock(0)), + }; + + var results = new[] { + "Times2", + "Times1", + "Times3", + "Times4", + }; + + for (int i = 0; i < arguments.Length; i++) { + var bindingTarget = RubyMethodGroupInfo.ResolveOverload("times", new[] { + t.GetMethod("Times1"), + t.GetMethod("Times2"), + t.GetMethod("Times3"), + t.GetMethod("Times4"), + }, arguments[i], true, false); + + Assert(bindingTarget.Success); + Assert(bindingTarget.Method.Name == results[i]); + } + } + + public static object Times1(BlockParam block, int self) { + return 1; + } + + public static object Times2(int self) { + return 2; + } + + public static object Times3([NotNull]BlockParam/*!*/ block, int self) { + return 3; + } + + public static object Times4(RubyContext/*!*/ context, BlockParam block, object self) { + return 4; + } + + #endregion + + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/RangeTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/RangeTests.cs new file mode 100644 index 0000000000..c148a9caba --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/RangeTests.cs @@ -0,0 +1,181 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using IronRuby.Compiler; +using IronRuby.Compiler.Ast; + +namespace IronRuby.Tests { + + public partial class Tests { + public void RangeConditionInclusive1() { + TestRangeCondition(true, @" +0: b -> false +1: FALSE +1: b -> false +2: FALSE +2: b -> true +3: e -> true +4: TRUE +4: b -> true +5: e -> true +6: TRUE +6: b -> true +7: e -> false +8: TRUE +8: e -> true +9: TRUE +9: b -> false +10: FALSE +10: b -> false +11: FALSE +11: b -> true +12: e -> true +13: TRUE +13: b -> nil +14: FALSE +"); + } + + public void RangeConditionExclusive1() { + TestRangeCondition(false, @" +0: b -> false +1: FALSE +1: b -> false +2: FALSE +2: b -> true +3: TRUE +3: e -> true +4: TRUE +4: b -> true +5: TRUE +5: e -> true +6: TRUE +6: b -> true +7: TRUE +7: e -> false +8: TRUE +8: e -> true +9: TRUE +9: b -> false +10: FALSE +10: b -> false +11: FALSE +11: b -> true +12: TRUE +12: e -> true +13: TRUE +13: b -> nil +14: FALSE +"); + } + + private void TestRangeCondition(bool inclusive, string/*!*/ expected) { + AssertOutput(delegate() { + CompilerTest(@" +F = false +T = true +x = X = '!' + +B = [F,F,T,x,T,x,T,x,x,F,F,T,x] +E = [x,x,x,T,x,T,x,F,T,x,x,x,T] + +def b + r = B[$j] + puts ""#{$j}: b -> #{r.inspect}"" + + $j += 1 + + $continue = !r.nil? + r == X ? raise : r +end + +def e + r = E[$j] + puts ""#{$j}: e -> #{r.inspect}"" + + $j += 1 + + $continue = !r.nil? + r == X ? raise : r +end + +$j = 0 +$continue = true +while $continue + if b<..>e + puts ""#{$j}: TRUE"" + else + puts ""#{$j}: FALSE"" + end +end +".Replace("<..>", inclusive ? ".." : "...")); + }, expected); + } + + /// + /// The state variable of a range condition is lifted to closure when used in a block. + /// + public void RangeCondition1() { + AssertOutput(delegate() { + CompilerTest(@" +$i = 0 + +def foo + $i += 1 + $i == 1 +end + +def test + l = lambda { + puts(foo..false ? 'true' : 'false') + } + + 1.times(&l) + 1.times(&l) + 1.times(&l) + 1.times(&l) +end + +test +"); + }, @" +true +true +true +true +"); + } + + /// + /// Block expressions propagate 'in-condition' property to the last statement. + /// + public void RangeCondition2() { + AssertOutput(delegate() { + CompilerTest(@" +puts((TRUE..FALSE) ? 'true' : 'false') +puts(begin TRUE..FALSE end ? 'true' : 'false') + +#puts((0;1;TRUE..FALSE) ? 'true' : 'false') # TODO: literals are removed with warning +"); + }, @" +true +true +"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/RegexTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/RegexTests.cs new file mode 100644 index 0000000000..73f2f7c85d --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/RegexTests.cs @@ -0,0 +1,210 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Text.RegularExpressions; +using IronRuby.Builtins; + +namespace IronRuby.Tests { + public partial class Tests { + public void Regex1() { + AssertOutput(delegate() { + CompilerTest(@" +r = /foo/imx +puts r.to_s +puts r.inspect + +puts s = /xx#{r}xx#{r}xx/i.to_s +puts t = /yy#{s}yy/.to_s +"); + }, @" +(?mix:foo) +/foo/mix +(?i-mx:xx(?mix:foo)xx(?mix:foo)xx) +(?-mix:yy(?i-mx:xx(?mix:foo)xx(?mix:foo)xx)yy) +"); + } + + public void Regex2() { + AssertOutput(delegate() { + CompilerTest(@" +puts(/#{/a/}/) # MRI: (?-mix:a) +puts(/#{nil}#{/a/}#{nil}/) # MRI: (?-mix:a) +puts(/#{/a/}b/) +puts(/b#{/a/}/) +"); + }, @" +(?-mix:(?-mix:a)) +(?-mix:(?-mix:a)) +(?-mix:(?-mix:a)b) +(?-mix:b(?-mix:a)) +"); + } + + public void RegexTransform1() { + string[] incorrectPatterns = new string[] { + @"\", + @"\", + + @"\\\", + @"\\\", + + @"\x", + @"\x", + + @"\1", // TODO + @"\1", + }; + + string[] correctPatterns = new string[] { + @"", + @"", + + @"\\", + @"\\", + + @"\_", + @"_", + + @"abc\0\01\011\a\sabc\Wabc\w", + @"abc\0\01\011\a\sabc\Wabc\w", + + @"\xd", + @"\x0d", + + @"\xdz", + @"\x0dz", + + @"\*", + @"\*", + + @"\[", + @"\[", + + @"\#", + @"\#", + + @"\0", + @"\0", + + @"\G", + @"\G", + + @"[a\-z]", + @"[a\-z]", + }; + + for (int i = 0; i < incorrectPatterns.Length; i += 2) { + string expected = incorrectPatterns[i + 1]; + string actual = StringRegex.TransformPattern(incorrectPatterns[i], RubyRegexOptions.NONE); + Assert(actual == expected); + } + + for (int i = 0; i < correctPatterns.Length; i += 2) { + string expected = correctPatterns[i + 1]; + string actual = StringRegex.TransformPattern(correctPatterns[i], RubyRegexOptions.NONE); + Assert(actual == expected); + new Regex(expected); + } + } + + public void RegexTransform2() { + string e = @"^ + ([a-zA-Z][-+.a-zA-Z\d]*): (?# 1: scheme) + (?: + ((?:[-_.!~*'()a-zA-Z\d;?:@&=+$,]|%[a-fA-F\d]{2})(?:[-_.!~*'()a-zA-Z\d;/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*) (?# 2: opaque) + | + (?:(?: + //(?: + (?:(?:((?:[-_.!~*'()a-zA-Z\d;:&=+$,]|%[a-fA-F\d]{2})*)@)? (?# 3: userinfo) + (?:((?:(?:(?:[a-zA-Z\d](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-fA-F\d]{1,4}:)*[a-fA-F\d]{1,4})?::(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)\]))(?::(\d*))?))?(?# 4: host, 5: port) + | + ((?:[-_.!~*'()a-zA-Z\d$,;+@&=+]|%[a-fA-F\d]{2})+) (?# 6: registry) + ) + | + (?!//)) (?# XXX: '//' is the mark for hostport) + (/(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*(?:/(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*)*)? (?# 7: path) + )(?:\?((?:[-_.!~*'()a-zA-Z\d;/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))? (?# 8: query) + ) + (?:\#((?:[-_.!~*'()a-zA-Z\d;/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))? (?# 9: fragment) + $"; + string t = StringRegex.TransformPattern(e, RubyRegexOptions.Extended | RubyRegexOptions.Multiline); + Assert(e == t); + new Regex(t); + } + + public void RegexEscape1() { + string[] patterns = new string[] { + @"", + @"", + + @"\", + @"\\", + + @"(*)", + @"\(\*\)", + + "$_^_|_[_]_(_)_\\_._#_-_{_}_*_+_?_\t_\n_\r_\f_\v_\a_\b", + @"\$_\^_\|_\[_\]_\(_\)_\\_\._\#_\-_\{_\}_\*_\+_\?_\t_\n_\r_\f_" + "\v_\a_\b" + }; + + for (int i = 0; i < patterns.Length; i += 2) { + string expected = patterns[i + 1]; + string actual = StringRegex.Escape(patterns[i]); + Assert(actual == expected); + } + } + + public void RegexCondition1() { + AssertOutput(delegate() { + CompilerTest(@" +$_ = 'foo'.taint +if /(foo)/ then + puts $1 + puts $1.tainted? +end +"); + }, @" +foo +true +"); + } + + public void RegexCondition2() { + AssertOutput(delegate() { + CompilerTest(@" +z = /foo/ +puts(z =~ 'xxxfoo') + +class Regexp + def =~ a + '=~' + end +end + +z = /foo/ +puts(z =~ 'foo') + +puts(/foo/ =~ 'xxxfoo') +"); + }, @" +3 +=~ +3 +"); + } + } +} + + diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/RubyUtilsTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/RubyUtilsTests.cs new file mode 100644 index 0000000000..47d031a7c5 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/RubyUtilsTests.cs @@ -0,0 +1,48 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Runtime; + +namespace IronRuby.Tests { + public partial class Tests { + public void Scenario_RubyNameMangling1() { + Assert(RubyUtils.TryUnmangleName("ip_stack") == "IpStack"); // TODO + Assert(RubyUtils.TryUnmangleName("stack") == "Stack"); + Assert(RubyUtils.TryUnmangleName("this_is_my_long_name") == "ThisIsMyLongName"); + Assert(RubyUtils.TryUnmangleName("") == null); + Assert(RubyUtils.TryUnmangleName("f") == "F"); + Assert(RubyUtils.TryUnmangleName("fo") == "Fo"); + Assert(RubyUtils.TryUnmangleName("foo") == "Foo"); + Assert(RubyUtils.TryUnmangleName("foo_bar") == "FooBar"); + Assert(RubyUtils.TryUnmangleName("ma_m") == "MaM"); + Assert(RubyUtils.TryUnmangleName("initialize") == "initialize"); + Assert(RubyUtils.TryUnmangleName("Initialize") == "Initialize"); + } + + public void Scenario_RubyNameMangling2() { + Assert(RubyUtils.MangleName("IPStack") == "ip_stack"); // TODO + Assert(RubyUtils.MangleName("Stack") == "stack"); + Assert(RubyUtils.MangleName("ThisIsMyLongName") == "this_is_my_long_name"); + Assert(RubyUtils.MangleName("") == ""); + Assert(RubyUtils.MangleName("F") == "f"); + Assert(RubyUtils.MangleName("FO") == "fo"); + Assert(RubyUtils.MangleName("FOO") == "foo"); + Assert(RubyUtils.MangleName("FOOBar") == "foo_bar"); + Assert(RubyUtils.MangleName("MaM") == "ma_m"); + Assert(RubyUtils.MangleName("initialize") == "initialize"); + Assert(RubyUtils.MangleName("Initialize") == "Initialize"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/SingletonTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/SingletonTests.cs new file mode 100644 index 0000000000..c00ca6f5e2 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/SingletonTests.cs @@ -0,0 +1,207 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Tests { + public partial class Tests { + public void Scenario_Singletons1() { + AssertOutput(delegate() { + CompilerTest(@" +puts self.to_s +"); + }, "main"); + } + + public void Scenario_Singletons2() { + AssertOutput(delegate() { + CompilerTest(@" +module M + def foo + puts 'foo' + end +end + +include M +foo +"); + }, "foo"); + } + + public void Scenario_Singletons3() { + AssertOutput(delegate() { + CompilerTest(@" +x = Object.new + +class << x + def foo + puts 'foo' + end +end + +x.foo +"); + }, "foo"); + } + + public void Scenario_Singletons4() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def i_C + puts 'i_C' + end + + def self.c_C + puts 'c_C' + end +end + +x = C.new +y = C.new + +class << x + $x1 = self + + def i_x1 + puts 'i_x1' + end + + def self.c_x1 + puts 'c_x1' + end +end + +x.i_x1 +x.i_C + +x.c_x1 rescue puts 'X' +x.c_C rescue puts 'X' +y.i_x1 rescue puts 'X' +"); + }, @" +i_x1 +i_C +X +X +X +"); + } + + public void Scenario_Singletons5() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def foo + puts 'C_foo' + end +end + +class Class + def foo + puts 'Class_foo' + end +end + +x = C.new + +class << x + $C1 = self +end + +$C1.foo +"); + }, @" +Class_foo +"); + } + + public void SingletonCaching1() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def foo + puts 'foo' + end +end + +c = C.new +c.foo + +class << c + def foo + puts 'bar' + end +end +c.foo +"); + }, @" +foo +bar"); + } + + public void Scenario_ClassVariables_Singletons() { + AssertOutput(delegate() { + CompilerTest(@" +class C +end + +module M + class << 'x' + $Sx = self + class << C + $SC = self + @@a = 1 + class_variable_set :@@ea, 1 + end + @@b = 2 + class_variable_set :@@eb, 2 + end + @@c = 3 + class_variable_set :@@em, 3 +end + +p M.class_variables.sort +p $SC.class_variables.sort +p $Sx.class_variables.sort +"); + }, @" +[""@@a"", ""@@b"", ""@@c"", ""@@em""] +[""@@ea""] +[""@@eb""] +"); + } + + public void AllowedSingletons1() { + AssertOutput(delegate() { + CompilerTest(@" +ok = ['x', true, false, nil, //] +error = [1 << 70, 1, 1.0, :foo] + +ok.each { |x| def x.foo; end } + +error.each { |x| + begin + def x.foo; end + rescue + else + raise + end +} + +puts 'ok' +"); + }, @"ok"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/StringTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/StringTests.cs new file mode 100644 index 0000000000..59fccf808b --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/StringTests.cs @@ -0,0 +1,210 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +namespace IronRuby.Tests { + public partial class Tests { + public void StringsPlus() { + // TODO: Ruby raises TypeError (InvalidOperationException) + AssertExceptionThrown(delegate() { + CompilerTest(@" +puts 'foo' + nil +"); + }); + } + + public void Strings0() { + AssertOutput(delegate() { + CompilerTest(@" +puts ""foo"" +puts ""foo"" 'bar' ""baz"" +"); + }, @" +foo +foobarbaz +"); + } + + public void Strings1() { + AssertOutput(delegate() { + CompilerTest(@" +puts ""foo#{""bar""}baz"" +"); + }, @" +foobarbaz +"); + } + + public void Strings2() { + AssertOutput(delegate() { + CompilerTest(@" +puts ""foo#{1;2;3}baz"" +"); + }, @" +foo3baz +"); + } + + public void Strings3() { + AssertOutput(delegate() { + CompilerTest(@" +class String; def to_s; 'S'; end; end +class Fixnum; def to_s; 'N'; end; end + +p """" +puts ""#{1}"" +puts ""#{1}-"" +puts ""-#{1}"" +puts ""-#{1}-"" +puts ""#{1}#{1}"" +puts ""#{1}+#{1}"" +puts ""-#{1}+#{1}"" +puts ""-#{1}+#{1}-"" + +puts ""-#{x = 'bob'}-"" +"); + }, @" +"""" +N +N- +-N +-N- +NN +N+N +-N+N +-N+N- +-bob- +"); + } + + public void Strings4() { + AssertOutput(delegate() { + CompilerTest(@" +p ""#{nil}"" +p ""#{nil}-"" +p ""-#{nil}"" +p ""-#{nil}-"" +p ""#{nil}#{nil}"" +p ""-#{nil}+#{nil}-"" +"); + }, @" +"""" +""-"" +""-"" +""--"" +"""" +""-+-"" +"); + } + + public void Strings5() { + AssertOutput(delegate() { + CompilerTest(@" +class String; def to_s; 'S'; end; end +class Fixnum; def to_s; 'N'; end; end + +puts :""#{1}"" +puts :""#{1}-"" +puts :""-#{1}"" +puts :""-#{1}-"" +puts :""#{1}#{1}"" +puts :""#{1}+#{1}"" +puts :""-#{1}+#{1}"" +puts :""-#{1}+#{1}-"" + +puts ""-#{x = 'bob'}-"" +"); + }, @" +N +N- +-N +-N- +NN +N+N +-N+N +-N+N- +-bob- +"); + } + + public void Strings6() { + AssertOutput(delegate() { + CompilerTest(@" +p :""#{}"" rescue p $! +p :""#{}#{}"" rescue p $! +p :""#{}#{''}#{}"" rescue p $! + +p :""#{nil}a"" +p :""a#{nil}"" +p :""a#{nil}b"" +p :""a#{nil}b#{nil}c"" +"); + }, @" +# +# +# +:a +:a +:ab +:abc +"); + } + + public void Strings7() { + AssertOutput(delegate() { + CompilerTest(@" +puts 'foobarbaz'[3,3] +"); + }, "bar"); + } + + public void Strings8() { + AssertOutput(delegate() { + CompilerTest(@" +puts 'foo bar'.split +"); + }, @" +foo +bar +"); + } + + /// + /// Embedded string does call "to_s" w/o calling "respond_to?" first. + /// + public void Strings9() { + // TODO: + AssertOutput(delegate() { + CompilerTest(@" +class X + def respond_to? name + puts name + end + + def to_s + 'TO_S' + end + + puts ""#{new}"" +end +"); + }, @" +TO_S +"); + } + + + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/SuperTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/SuperTests.cs new file mode 100644 index 0000000000..96de266236 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/SuperTests.cs @@ -0,0 +1,409 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Tests { + public partial class Tests { + private void Super1_Test(bool eval) { + AssertOutput(delegate() { + CompilerTest(Eval(eval, @" +class C + def foo a + puts 'C.foo' + puts a + end +end + +class D < C + def foo + puts 'D.foo' + # + end +end + +D.new.foo +")); + }, @" +D.foo +C.foo +arg"); + } + + public void Super1() { + Super1_Test(false); + } + + public void SuperEval1() { + Super1_Test(true); + } + + public void SuperParameterless1() { + SuperParameterless1_Test(false); + } + + public void SuperParameterlessEval1() { + SuperParameterless1_Test(true); + } + + public void SuperParameterless1_Test(bool eval) { + AssertOutput(delegate() { + CompilerTest(Eval(eval, @" +class C + def foo a + puts 'C.foo' + puts a + end +end + +class D < C + def foo a + puts 'D.foo' + # + end +end + +D.new.foo 'arg' +")); + }, @" +D.foo +C.foo +arg"); + } + + public void SuperParameterless2() { + SuperParameterless2_Test(false); + } + + public void SuperParameterlessEval2() { + SuperParameterless2_Test(true); + } + + public void SuperParameterless2_Test(bool eval) { + AssertOutput(delegate() { + CompilerTest(Eval(eval, @" +class C + def foo a + puts 'C.foo' + puts a + yield + end +end + +class D < C + def foo a + puts 'D.foo' + # { puts 'block' } + end +end + +D.new.foo 'arg' +")); + }, @" +D.foo +C.foo +arg +block +"); + } + + public void SuperParameterless3() { + AssertOutput(delegate() { + CompilerTest(@" +class C + def foo + puts 'C.foo' + end +end + +class D < C + def foo a + super() + end +end + +D.new.foo 'arg' +"); + }, @" +C.foo +"); + } + + /// + /// Calls to super with block and no arguments (was bug in parser/AST). + /// + public void Super2() { + AssertOutput(delegate() { + CompilerTest(@" +$p = proc {} + +class C + def foo *a + p a, block_given? + end +end + +class D < C + def foo a + super # pass 'a' + super() + super &$p + super(&$p) + super { } # pass 'a' + super() { } + end +end + +D.new.foo(1) +puts +D.new.foo(2) { } +", 1, 0); + }, @" +[1] +false +[] +false +[] +true +[] +true +[1] +true +[] +true + +[2] +true +[] +true +[] +true +[] +true +[2] +true +[] +true +"); + } + + // TODO: parameters + public void SuperInDefineMethod1() { + SuperInDefineMethod1_Test(false); + } + + public void SuperInDefineMethodEval1() { + SuperInDefineMethod1_Test(true); + } + + /// + /// Super in a proc invoked via a call to a method defined by define_method uses + /// the method's name and declaring module for super-method lookup. + /// + public void SuperInDefineMethod1_Test(bool eval) { + AssertOutput(delegate() { + CompilerTest(Eval(eval, @" +def def_lambda + 1.times { + $p = lambda { + 1.times { + p self.class + # + } + } + } +end + +class C + def foo + puts 'C.foo' + end +end + +def_lambda + +class D < C + define_method :foo, &$p +end + +D.new.foo +")); + }, @" +D +C.foo +"); + } + + /// + /// Super in a proc invoked via "call" uses the self and parameters captured in closure by the block. + /// + public void SuperInDefineMethod2() { + AssertOutput(delegate() { + CompilerTest(@" +class A + def def_lambda a + puts 'A.def_lambda' + puts a + end +end + +class B < A + def def_lambda a + 1.times { + $p = lambda { + 1.times { + p self.class + super + } + } + } + end +end + +B.new.def_lambda 'arg' +$p.call 'foo' +"); + }, @" +B +A.def_lambda +arg +"); + } + + /// + /// Super call in defined_method's proc: + /// 1.8: parameters are taken from block parameters + /// 1.9: not-supported exception is thrown + /// + public void SuperInDefineMethod3() { + // TODO: + AssertOutput(delegate() { + CompilerTest(@" +class B + def m *a + p a + end +end + +class C < B + define_method :m do |*a| + p a + super + end +end + +C.new.m 1,2 +"); + }, @" +[1, 2] +[1, 2] +"); + } + + public void SuperInTopLevelCode1() { + AssertOutput(delegate() { + CompilerTest(@" +class B + def m + puts 'B::m' + end +end + +class C < B + define_method :m do + super + end +end + +C.new.m +"); + }, @" +B::m +"); + } + + /// + /// Alias doesn't change DeclaringModule of the method => super call uses the class in which the method is defined. + /// Parameters + /// + public void SuperInAliasedDefinedMethod1() { + AssertOutput(delegate() { + CompilerTest(@" +class B + def m *a + puts 'B::m' + end +end + +class C < B + define_method :m do + puts 'C::m' + super + end + + def n + puts 'C::n' + end +end + +class D < C + alias n m +end + +D.new.n +"); + }, @" +C::m +B::m +"); + } + + /// + /// super doesn't use self, declaring-module defined by module_eval/instance_eval. + /// + public void SuperInModuleEval1() { + AssertOutput(delegate() { + CompilerTest(@" +class A + def foo + puts 'A::foo' + end +end + +class B < A + def foo + puts 'B::foo' + end +end + +class C < B + def foo + A.module_eval { + super + } + A.new.instance_eval { + super + } + end +end + +C.new.foo +"); + }, @" +B::foo +B::foo +"); + } + } + +} diff --git a/merlin/main/languages/ruby/IronRuby.Tests/Runtime/UndefTests.cs b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/UndefTests.cs new file mode 100644 index 0000000000..7c3124b8f0 --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.Tests/Runtime/UndefTests.cs @@ -0,0 +1,171 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Tests { + public partial class Tests { + + public void Scenario_MethodUndef1() { + AssertOutput(delegate() { + CompilerTest(@" +module M + def m1 + puts 'M::m1' + end + + def m2 + puts 'M::m2' + end + + def w + puts 'M::w' + end + + def u + undef :w + end +end + +module N + def m1 + puts 'N::m1' + end + + def m2 + puts 'N::m2' + end +end + + +class C + include M + + def c1 + puts 'c1' + end + + def c2 + puts 'c2' + end + + undef :d2 rescue puts $! +end + +class E < C + undef m1 +end + +class D < E + include N + + def d1 + puts 'd1' + end + + def d2 + puts 'd2' + end + + def w + puts 'D::w' + end + + def method_missing name, *a + puts ""missing: #{name}"" + end +end + +c = C.new +d = D.new + +c.m1 +c.m2 +c.c1 +c.c2 +d.m1 +d.m2 +d.c1 +d.c2 +d.d1 +d.d2 +d.u +d.w +"); + }, @" +undefined method `d2' for class `C' +M::m1 +M::m2 +c1 +c2 +N::m1 +N::m2 +c1 +c2 +d1 +d2 +D::w +"); + } + + public void Scenario_MethodUndef2() { + Context.DefineGlobalVariable("o", 0); + CompilerTest(@" +class C + def foo + end + + undef foo rescue $o += 1 + undef foo rescue $o += 10 # blows up here +end +"); + AssertEquals(Context.GetGlobalVariable("o"), 10); + } + + public void MethodUndefExpression() { + AssertOutput(delegate() { + CompilerTest(@" +def u; end +p ((undef u)) +", 1, 0); + }, "nil"); + } + + /// + /// Undef (unlike alias) doesn't look up the method in Object. + /// + public void UndefMethodLookup() { + AssertOutput(delegate() { + CompilerTest(@" +module Kernel; def m_kernel; end; end +class Object; def m_object; end; end + +module N + def m_n + end + + module M + undef m_object rescue puts '!undef m_object' + undef m_kernel rescue puts '!undef m_kernel' + undef m_n rescue puts '!undef m_n' + end +end +"); + }, @" +!undef m_object +!undef m_kernel +!undef m_n +"); + } + } +} diff --git a/merlin/main/languages/ruby/IronRuby.sln b/merlin/main/languages/ruby/IronRuby.sln new file mode 100644 index 0000000000..507a099a2a --- /dev/null +++ b/merlin/main/languages/ruby/IronRuby.sln @@ -0,0 +1,181 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Scripting.Core", "src\Microsoft.Scripting.Core\Microsoft.Scripting.Core.csproj", "{2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Scripting", "src\Microsoft.Scripting\Microsoft.Scripting.csproj", "{EB66B766-6354-4208-A3D4-AACBDCB5C3B3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ruby", "src\ironruby\Ruby.csproj", "{7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronRuby.Libraries", "src\IronRuby.Libraries\IronRuby.Libraries.csproj", "{77323B06-15A2-4CF4-8A7A-86EAA2B66498}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassInitGenerator", "utils\ironruby.classinitgenerator\ClassInitGenerator.csproj", "{166940A1-2C91-4BD0-B72B-A517FBD8BF0B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ruby.Console", "utils\ironruby.console\Ruby.Console.csproj", "{D6AB587D-A888-4B98-85AC-F15E36F53838}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronRuby.Tests", "utils\IronRuby.Tests\IronRuby.Tests.csproj", "{8103D91B-89D8-4A18-9A40-426992602EA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronRuby.Libraries.Yaml", "src\yaml\IronRuby.Libraries.Yaml.csproj", "{AA18A245-E342-4368-A474-83178311A742}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "microsoft.scripting.extensionattribute", "src\microsoft.scripting.core\microsoft.scripting.extensionattribute.csproj", "{8B0F1074-750E-4D64-BF23-A1E0F54261E5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + ExternalDebug|Any CPU = ExternalDebug|Any CPU + ExternalRelease|Any CPU = ExternalRelease|Any CPU + FxCop|Any CPU = FxCop|Any CPU + Release|Any CPU = Release|Any CPU + Silverlight Debug|Any CPU = Silverlight Debug|Any CPU + Silverlight Release|Any CPU = Silverlight Release|Any CPU + SpecSharp|Any CPU = SpecSharp|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.ExternalDebug|Any CPU.ActiveCfg = ExternalDebug|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.ExternalDebug|Any CPU.Build.0 = ExternalDebug|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.ExternalRelease|Any CPU.ActiveCfg = ExternalRelease|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.ExternalRelease|Any CPU.Build.0 = ExternalRelease|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.FxCop|Any CPU.Build.0 = FxCop|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Release|Any CPU.Build.0 = Release|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.SpecSharp|Any CPU.ActiveCfg = SpecSharp|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.SpecSharp|Any CPU.Build.0 = SpecSharp|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.ExternalDebug|Any CPU.ActiveCfg = ExternalDebug|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.ExternalDebug|Any CPU.Build.0 = ExternalDebug|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.ExternalRelease|Any CPU.ActiveCfg = ExternalRelease|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.ExternalRelease|Any CPU.Build.0 = ExternalRelease|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.FxCop|Any CPU.Build.0 = FxCop|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Release|Any CPU.Build.0 = Release|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.SpecSharp|Any CPU.ActiveCfg = SpecSharp|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.SpecSharp|Any CPU.Build.0 = SpecSharp|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.ExternalDebug|Any CPU.ActiveCfg = ExternalDebug|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.ExternalDebug|Any CPU.Build.0 = ExternalDebug|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.ExternalRelease|Any CPU.ActiveCfg = ExternalRelease|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.ExternalRelease|Any CPU.Build.0 = ExternalRelease|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.FxCop|Any CPU.ActiveCfg = Silverlight Release|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.FxCop|Any CPU.Build.0 = Silverlight Release|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Release|Any CPU.Build.0 = Release|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.SpecSharp|Any CPU.ActiveCfg = Silverlight Release|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.SpecSharp|Any CPU.Build.0 = Silverlight Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.ExternalDebug|Any CPU.ActiveCfg = ExternalDebug|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.ExternalDebug|Any CPU.Build.0 = ExternalDebug|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.ExternalRelease|Any CPU.ActiveCfg = ExternalRelease|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.ExternalRelease|Any CPU.Build.0 = ExternalRelease|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.FxCop|Any CPU.Build.0 = Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Release|Any CPU.Build.0 = Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.SpecSharp|Any CPU.ActiveCfg = Silverlight Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.SpecSharp|Any CPU.Build.0 = Silverlight Release|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.ExternalDebug|Any CPU.ActiveCfg = ExternalDebug|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.ExternalDebug|Any CPU.Build.0 = ExternalDebug|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.ExternalRelease|Any CPU.ActiveCfg = ExternalRelease|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.FxCop|Any CPU.Build.0 = Release|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Release|Any CPU.Build.0 = Release|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.SpecSharp|Any CPU.ActiveCfg = ExternalRelease|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.SpecSharp|Any CPU.Build.0 = ExternalRelease|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.ExternalDebug|Any CPU.ActiveCfg = ExternalDebug|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.ExternalDebug|Any CPU.Build.0 = ExternalDebug|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.ExternalRelease|Any CPU.ActiveCfg = ExternalRelease|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.ExternalRelease|Any CPU.Build.0 = ExternalRelease|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.FxCop|Any CPU.Build.0 = Release|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Release|Any CPU.Build.0 = Release|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.SpecSharp|Any CPU.ActiveCfg = ExternalRelease|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.SpecSharp|Any CPU.Build.0 = ExternalRelease|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.ExternalDebug|Any CPU.ActiveCfg = ExternalDebug|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.ExternalDebug|Any CPU.Build.0 = ExternalDebug|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.ExternalRelease|Any CPU.ActiveCfg = ExternalRelease|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.ExternalRelease|Any CPU.Build.0 = ExternalRelease|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.FxCop|Any CPU.Build.0 = Release|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Release|Any CPU.Build.0 = Release|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.SpecSharp|Any CPU.ActiveCfg = ExternalRelease|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.SpecSharp|Any CPU.Build.0 = ExternalRelease|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.ExternalDebug|Any CPU.ActiveCfg = Debug|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.ExternalDebug|Any CPU.Build.0 = Debug|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.ExternalRelease|Any CPU.ActiveCfg = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.ExternalRelease|Any CPU.Build.0 = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.FxCop|Any CPU.Build.0 = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Release|Any CPU.Build.0 = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.SpecSharp|Any CPU.Build.0 = Release|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.ExternalDebug|Any CPU.ActiveCfg = Debug|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.ExternalDebug|Any CPU.Build.0 = Debug|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.ExternalRelease|Any CPU.ActiveCfg = Release|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.ExternalRelease|Any CPU.Build.0 = Release|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.FxCop|Any CPU.Build.0 = FxCop|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Release|Any CPU.Build.0 = Release|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.SpecSharp|Any CPU.ActiveCfg = SpecSharp|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.SpecSharp|Any CPU.Build.0 = SpecSharp|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/BigDecimal/BigDecimal.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/BigDecimal/BigDecimal.cs new file mode 100644 index 0000000000..b5271a5488 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/BigDecimal/BigDecimal.cs @@ -0,0 +1,2536 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Text.RegularExpressions; +using Microsoft.Scripting.Math; +using IronRuby.Builtins; + +namespace IronRuby.StandardLibrary.BigDecimal { + public class BigDecimal : IComparable, IEquatable { + + static BigDecimal() { + CreateSC(); + CreateSR(); + } + + /// + /// This class holds the configuration information for the BigDecimal calculations + /// This information needs to be shared amongst all the instances of BigDecimal + /// but cannot be held in static variable storage since we need to scope more finely + /// than per assembly. + /// + public class Config { + public Config() { + Limit = 0; + RoundingMode = RoundingModes.HalfUp; + OverflowMode = OverflowExceptionModes.None; + } + public int Limit { get; set; } + public RoundingModes RoundingMode { get; set; } + public OverflowExceptionModes OverflowMode { get; set; } + } + + #region Instance Fields + private readonly NumberTypes _type; + private readonly int _sign; + private readonly Fraction _fraction; + private readonly int _exponent; + private readonly int _maxPrecision; + #endregion + + #region Useful Private Constants + private const string NaNString = "NaN"; + private const string InfinityString = "Infinity"; + private const string NegativeInfinityString = "-Infinity"; + private const string ZeroString = "0.0"; + private const string NegativeZeroString = "-0.0"; + #endregion + + #region Useful Enumerations + public enum RoundingModes { + /// + /// No rounding + /// + None = 0, + /// + /// Round away from zero + /// + Up = 1, + /// + /// Round toward zero + /// + Down = 2, + /// + /// Round towards "nearest neighbour" unless both neighbours are equidistant, in which case round up + /// + HalfUp = 3, + /// + /// Round towards "nearest neighbour" unless both neighbours are equidistant, in which case round down + /// + HalfDown = 4, + /// + /// Round toward positive infinity + /// + Ceiling = 5, + /// + /// Round toward negative infinity + /// + Floor = 6, + /// + /// Round towards "nearest neighbour" unless both neighbours are equidistant, in which case round to the even neighbour + /// + HalfEven = 7 + } + + /// + /// Flags of when exceptions should be thrown on special values + /// + /// + /// Currently Infinity, Overflow and ZeroDivide are the same + /// + [Flags] + public enum OverflowExceptionModes { + None = 0, + NaN = 2, + Infinity = 1, + Underflow = 4, + Overflow = 1, + ZeroDivide = 1, + All = 255 + } + + public enum NumberTypes { + NaN = 0, + Finite = 1, + Infinite = 2 + } + + private enum BasicOperations { + Add = 0, + Subtract = 1, + Multiply = 2, + Divide = 3 + } + #endregion + + #region Useful Special BigDecimal Values + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly BigDecimal One = new BigDecimal(1, Fraction.One, 1); + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly BigDecimal Half = new BigDecimal(1, Fraction.Five, 0); + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly BigDecimal NaN = new BigDecimal(NumberTypes.NaN, 0); + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly BigDecimal PositiveInfinity = new BigDecimal(NumberTypes.Infinite, 1); + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly BigDecimal NegativeInfinity = new BigDecimal(NumberTypes.Infinite, -1); + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly BigDecimal PositiveZero = new BigDecimal(1, Fraction.Zero, 1); + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly BigDecimal NegativeZero = new BigDecimal(-1, Fraction.Zero, 1); + public const uint BASE = Fraction.BASE; + public const int BASE_FIG = Fraction.BASE_FIG; + public const int DOUBLE_FIG = 16; + #endregion + + #region Private Constructors + private BigDecimal(int sign, Fraction/*!*/ fraction, int exponent) + : this(sign, fraction, exponent, fraction.Precision) { + } + private BigDecimal(int sign, Fraction/*!*/ fraction, int exponent, int maxPrecision) { + Debug.Assert(sign == 1 || sign == -1); + _type = NumberTypes.Finite; + _sign = sign; + _fraction = fraction; + _exponent = exponent; + // If maxPrecision is 0 then default to the precision of the fraction + _maxPrecision = maxPrecision == 0 ? fraction.Precision : maxPrecision; + } + private BigDecimal(int sign, BigDecimal/*!*/ copyFrom) { + _type = copyFrom._type; + _sign = sign; + _fraction = copyFrom._fraction; + _exponent = copyFrom._exponent; + _maxPrecision = copyFrom._maxPrecision; + } + private BigDecimal(int sign, BigDecimal/*!*/ copyFrom, int maxPrecision) { + _type = copyFrom._type; + _sign = sign; + _fraction = new Fraction(copyFrom._fraction, maxPrecision); + _exponent = copyFrom._exponent; + _maxPrecision = maxPrecision; + } + + private BigDecimal(NumberTypes type, int sign) + : this(type, sign, 1) { + } + private BigDecimal(NumberTypes type, int sign, int maxPrecision) { + Debug.Assert(type != NumberTypes.Finite); + _type = type; + _sign = sign; + _fraction = Fraction.Zero; + _exponent = 0; + _maxPrecision = maxPrecision; + } + #endregion + + #region Public Properties + public NumberTypes NumberType { get { return _type; } } + public int Sign { get { return _sign; } } + public int Exponent { get { return _exponent; } } + public int Precision { get { return _fraction.Precision; } } + public int MaxPrecision { get { return _maxPrecision; } } + public int Digits { get { return _fraction.DigitCount; } } + public int MaxPrecisionDigits { get { return _maxPrecision * BASE_FIG; } } + public int PrecisionDigits { get { return _fraction.Precision * BASE_FIG; } } + #endregion + + #region Create Methods + public static BigDecimal Create(Config/*!*/config, object value) { + return Create(config, value.ToString(), 0); + } + public static BigDecimal Create(Config/*!*/config, string value) { + return Create(config, value, 0); + } + public static BigDecimal Create(Config/*!*/config, string value, int maxPrecision) { + value = value.Trim(); + BigDecimal result = CheckSpecialCases(value); + + if (result == null) { + + Match m = Regex.Match(value, @"^(?[-+]?)(?[\d_]*)\.?(?[\d_]*)([eEdD](?[-+]?[\d_]+))?", RegexOptions.ExplicitCapture); + + int sign = m.Groups["sign"].Value == "-" ? -1 : 1; + + // Get the integer part of the mantissa (stripping off leading zeros and any '_' chars) + string integerStr = (m.Groups["integer"].Value ?? "0").TrimStart('0').Replace("_", ""); + // Get the fraction part of the mantissa (stripping off following zeros and any '_' chars) + string decimalStr = (m.Groups["fraction"].Value ?? "").TrimEnd('0').Replace("_", ""); + // Build the whole of the mantissa string + string fractionStr = (integerStr + decimalStr); + string strippedFractionStr = fractionStr.TrimStart('0'); + + // Finally strip off any excess zeros left over + string trimmedFractionStr = strippedFractionStr.Trim('0'); + + Fraction fraction = Fraction.Create(trimmedFractionStr); + + string exponentStr = m.Groups["exponent"].Value.Replace("_", ""); + int exponent = 0; + if (!fraction.IsZero) { + if (!string.IsNullOrEmpty(exponentStr)) { + try { + exponent = int.Parse(exponentStr); + } catch (OverflowException) { + exponent = exponentStr.StartsWith("-") ? -1 : 1; + return ExponentOverflow(config, sign, exponent); + } + } + // Normalize exponent where mantissa > 1.0 + exponent += integerStr.Length; + // Normalize exponent where mantissa < 1.0 + exponent -= (fractionStr.Length - strippedFractionStr.Length); + } + result = new BigDecimal(sign, fraction, exponent, maxPrecision); + } + return CheckOverflowExceptions(config, result); + } + private static BigDecimal CheckSpecialCases(string value) { + BigDecimal result = null; + if (value == null) { + result = PositiveZero; + } else { + value = value.Trim(); + if (value == "NaN") { + result = NaN; + } else if (value.Contains("Infinity")) { + if (value == "+Infinity" || value == "Infinity") { + result = PositiveInfinity; + } else if (value == "-Infinity") { + result = NegativeInfinity; + } else { + result = PositiveZero; + } + } + } + return result; + } + #endregion + + #region Special Value Tests + public static bool IsNaN(BigDecimal x) { + return x._type == NumberTypes.NaN; + } + public static bool IsInfinite(BigDecimal x) { + return (x._type == NumberTypes.Infinite); + } + public static bool IsFinite(BigDecimal x) { + return x._type == NumberTypes.Finite; + } + public static bool IsZero(BigDecimal/*!*/ x) { + return IsFinite(x) && x._fraction.IsZero; + } + public static bool IsNonZeroFinite(BigDecimal x) { + return IsFinite(x) && !x._fraction.IsZero; + } + public static bool IsPositive(BigDecimal x) { + return x._sign == 1; + } + public static bool IsNegative(BigDecimal x) { + return x._sign == -1; + } + + public static bool IsPositiveInfinite(BigDecimal x) { + return IsInfinite(x) && IsPositive(x); + } + public static bool IsNegativeInfinite(BigDecimal x) { + return IsInfinite(x) && IsNegative(x); + } + public static bool IsPositiveZero(BigDecimal x) { + return IsZero(x) && IsPositive(x); + } + public static bool IsNegativeZero(BigDecimal x) { + return IsZero(x) && IsNegative(x); + } + + public static bool IsOne(BigDecimal x) { + return x._fraction.IsOne && x._exponent == 1; + } + public static bool IsPositiveOne(BigDecimal x) { + return IsOne(x) && IsPositive(x); + } + public static bool IsNegativeOne(BigDecimal x) { + return IsOne(x) && IsPositive(x); + } + #endregion + + #region Public Static Operations + public static double ToFloat(Config config, BigDecimal/*!*/ x) { + try { + if (BigDecimal.IsNegativeZero(x)) { + return -0.0; + } + return Double.Parse(x.ToString(0, "", true)); + } catch (OverflowException) { + return Double.PositiveInfinity; + } + } + public static object ToInteger(Config/*!*/ config, BigDecimal/*!*/ x) { + if (IsFinite(x)) { + BigDecimal i = IntegerPart(config, x); + BigInteger d = BigInteger.Create(0); + string digits = i._fraction.ToString(); + foreach (char c in digits) { + d *= 10; + d += (c - '0'); + } + if (IsNegative(x)) { + d = BigInteger.Negate(d); + } + return BignumOps.Multiply(d, BigInteger.Create(10).Power(i.Exponent - 1)); + } else { + return null; + } + } + public static BigDecimal Abs(Config config, BigDecimal x) { + if (IsNegative(x)) { + return Negate(config, x); + } else { + return x; + } + } + public static BigDecimal Negate(Config config, BigDecimal x) { + if (IsFinite(x)) { + return new BigDecimal(-x._sign, x._fraction, x._exponent); + } else { + return new BigDecimal(x.NumberType, -x.Sign); + } + } + + public static BigDecimal/*!*/ Add(Config/*!*/ config, BigDecimal/*!*/ x, BigDecimal/*!*/ y) { + return Add(config, x, y, 0); + } + public static BigDecimal/*!*/ Add(Config/*!*/ config, BigDecimal/*!*/ x, BigDecimal/*!*/ y, int limit) { + return InternalAdd(config, x, y, limit); + } + + public static BigDecimal/*!*/ Subtract(Config/*!*/ config, BigDecimal/*!*/ x, BigDecimal/*!*/ y) { + return Subtract(config, x, y, 0); + } + public static BigDecimal/*!*/ Subtract(Config config, BigDecimal x, BigDecimal y, int limit) { + return InternalAdd(config, x, BigDecimal.Negate(config, y), limit); + } + + public static BigDecimal Multiply(Config config, BigDecimal x, BigDecimal y) { + return Multiply(config, x, y, 0); + } + public static BigDecimal Multiply(Config config, BigDecimal x, BigDecimal y, int limit) { + BigDecimal result = CheckSpecialResult(config, x, y, BasicOperations.Multiply); + if (result != null) { + return result; + } + + // Default to the global limit + if (limit == 0) { + limit = config.Limit; + } + + // Calculate the number of digits if not specified + if (limit == 0) { + limit = x.Digits + y.Digits; + } + + int sign = y._sign * x._sign; + if (IsOne(x)) { + return LimitPrecision(config, new BigDecimal(sign, y._fraction, y._exponent), limit, config.RoundingMode); + } + if (IsOne(y)) { + return LimitPrecision(config, new BigDecimal(sign, x._fraction, x._exponent), limit, config.RoundingMode); + } + + int exponent; + Fraction fraction; + try { + checked { + int diffDigits1, diffDigits2; + fraction = Fraction.Multiply(x._fraction, y._fraction, out diffDigits1); + fraction = Fraction.LimitPrecision(sign, fraction, limit, config.RoundingMode, out diffDigits2); + exponent = x._exponent + y._exponent + diffDigits1 + diffDigits2; + } + } catch (OverflowException) { + return ExponentOverflow(config, sign, x._exponent); + } + + result = new BigDecimal(sign, fraction, exponent, limit); + return result; + } + + public static BigDecimal Divide(Config config, BigDecimal x, BigDecimal y, int limit, out BigDecimal remainder) { + BigDecimal result = CheckSpecialResult(config, x, y, BasicOperations.Divide); + if (result != null) { + remainder = BigDecimal.PositiveZero; + return result; + } + + // Default to global limit if not specified + if (limit == 0) { + limit = config.Limit; + } + + // Sign + int sign = x._sign * y._sign; + + if (IsOne(y)) { + remainder = BigDecimal.PositiveZero; + return new BigDecimal(sign, x._fraction, x._exponent); + } + + // Fraction + int xOffset, rOffset, limitOffset; + int minPrecision = (int)Math.Ceiling((double)limit / BASE_FIG) + 1; + Fraction rFraction; + Fraction fraction = Fraction.Divide(x._fraction, y._fraction, minPrecision, out rFraction, out xOffset, out rOffset); + if (limit == 0) { + limitOffset = 0; + } else { + fraction = Fraction.LimitPrecision(sign, fraction, limit, config.RoundingMode, out limitOffset); + } + + // Exponent + int exponent, rExponent; + try { + checked { + exponent = x._exponent - y._exponent + xOffset + limitOffset; + rExponent = x._exponent - y._exponent + rOffset; + } + } catch (OverflowException) { + remainder = BigDecimal.PositiveZero; + return ExponentOverflow(config, sign, x._exponent); + } + + remainder = new BigDecimal(sign, rFraction, rExponent, rFraction.MaxPrecision); + result = new BigDecimal(sign, fraction, exponent, fraction.MaxPrecision); + return result; + } + + public static void DivMod(Config/*!*/ config, BigDecimal/*!*/ x, BigDecimal/*!*/ y, out BigDecimal/*!*/ div, out BigDecimal/*!*/ mod) { + // TODO: This is horribly suboptimal! + div = BigDecimal.NaN; + mod = BigDecimal.NaN; + if (BigDecimal.IsFinite(x) && BigDecimal.IsFinite(y)) { + BigDecimal remainder; + BigDecimal quotient = Divide(config, x, y, 0, out remainder); + if (BigDecimal.IsFinite(quotient)) { + // Calculate the div: floor(quotient) + div = LimitPrecision(config, quotient, 0, BigDecimal.RoundingModes.Floor); + // Calculate the mod: x - (div * y) + mod = Subtract(config, x, Multiply(config, div, y)); + } + } + } + + public static BigDecimal/*!*/ Power(Config/*!*/ config, BigDecimal/*!*/ x, int power) { + if (!IsFinite(x)) { + return CheckOverflowExceptions(config, NaN); + } + + if (power == 0) { + return One; + } + + // power is odd => sign = x.Sign + // power is even => sign = 1 + int sign = (power % 2 != 0) ? x.Sign : 1; + + if (IsOne(x)) { + return new BigDecimal(sign, One); + } + + if (IsZero(x)) { + if (power < 0) { + return CheckOverflowExceptions(config, new BigDecimal(sign, PositiveInfinity)); + } else { + return new BigDecimal(sign, PositiveZero); + } + } + + BigDecimal result = x; + int n = Math.Abs(power) - 1; + int precision = x.Precision * (n + 2); + while (n > 0) { + // Copy x into temp ensuring a good precision + BigDecimal temp = new BigDecimal(x.Sign, x, precision); + int s = 2; + int ss = 1; + while (s <= n) { + temp = Multiply(config, temp, temp); + ss = s; + s += s; + } + n -= ss; + result = Multiply(config, result, temp); + } + if (power < 0) { + BigDecimal remainder; + result = Divide(config, One, result, precision * (BASE_FIG + 1), out remainder); + } + return result; + } + + public static BigDecimal/*!*/ SquareRoot(Config/*!*/ config, BigDecimal/*!*/ x, int limit) { + if (limit < 0) { + throw new ArgumentException("argument must be positive"); + } + // Special cases + if (IsZero(x) || IsPositiveOne(x)) { + return x; + } + if (x.Sign < 0) { + throw new FloatDomainError("SQRT(negative value)"); + // Really it seems more consistent to check the overflow exceptions + // return CheckOverflowExceptions(config, NaN); + } + if (IsNaN(x)) { + throw new FloatDomainError("SQRT(NaN)"); + } + if (!IsFinite(x)) { + return CheckOverflowExceptions(config, x); + } + + // Precision + if (limit == 0) { + limit = config.Limit; + } + int prec = x.Exponent; + if (prec > 0) { + ++prec; + } else { + --prec; + } + prec = prec - Math.Max(x.PrecisionDigits, limit + DOUBLE_FIG); + + // Estimate the value using double (this round-about computation is to get round double's exponent limitation. + BigDecimal fraction = new BigDecimal(x._sign, x._fraction, 0, x._maxPrecision); + int exponent = x._exponent / 2; + if (x._exponent - exponent * 2 != 0) { + // The exponent is odd so we have to tweak the values + exponent = (x._exponent + 1) / 2; + fraction = BigDecimal.Multiply(config, fraction, BigDecimal.Create(config, 0.1)); + } + double d = ToFloat(config, fraction); + BigDecimal y = BigDecimal.Create(config, Math.Sqrt(d)); + y = new BigDecimal(y._sign, y._fraction, y._exponent + exponent); + + // Converge on more accurate result + const int max_iterations = 100; + BigDecimal r = PositiveZero; + BigDecimal f = PositiveZero; + int iterations = Math.Max(limit, max_iterations); + int i = 0; + while (i < iterations) { + f = Divide(config, x, y, limit, out r); /* f = x/y */ + r = Subtract(config, f, y, limit); /* r = f - y */ + f = Multiply(config, Half, r, limit); /* f = 0.5*r */ + if (IsZero(f)) { + break; + } + y = Add(config, y, f); /* y = y + f */ + if (f.Exponent <= prec) { + break; + } + i++; + } + return y; + // return LimitPrecision(config, y, limit, config.RoundingMode); + } + + public static BigDecimal/*!*/ FractionalPart(Config/*!*/ config, BigDecimal/*!*/ x) { + if (IsFinite(x)) { + if (x.Exponent > 0) { + if (x.Exponent < x.Digits) { + return new BigDecimal(x.Sign, Fraction.Create(x._fraction.ToString().Substring(x.Exponent)), 0); + } else { + return x.Sign > 0 ? BigDecimal.PositiveZero : BigDecimal.NegativeZero; + } + } else { + return x; + } + } + return x; + } + + public static BigDecimal/*!*/ IntegerPart(Config/*!*/ config, BigDecimal/*!*/ x) { + if (IsFinite(x)) { + if (x.Exponent > 0) { + if (x.Exponent < x._fraction.DigitCount) { + return new BigDecimal(x.Sign, Fraction.Create(x._fraction.ToString().Substring(0, x.Exponent)), x.Exponent); + } else { + return x; + } + } else { + return x.Sign > 0 ? BigDecimal.PositiveZero : BigDecimal.NegativeZero; + } + } else { + return x; + } + } + + public static BigDecimal/*!*/ LimitPrecision(Config/*!*/ config, BigDecimal/*!*/ x, int limit, RoundingModes mode) { + try { + checked { + if (IsFinite(x)) { + int diffDigits; + Fraction fraction = Fraction.LimitPrecision(x._sign, x._fraction, limit + x.Exponent, mode, out diffDigits); + return new BigDecimal(x._sign, fraction, x._exponent + diffDigits); + } else { + return x; + } + } + } catch (OverflowException) { + return ExponentOverflow(config, x._sign, x._exponent); + } + } + #endregion + + #region Private Helpers + private static BigDecimal InternalAdd(Config config, BigDecimal x, BigDecimal y, int limit) { + if (limit < 0) { + throw new ArgumentException("limit must be positive"); + } + if (limit == 0) { + limit = config.Limit; + } + + // This method is used for both adding and subtracting, but the special cases are the same. + BigDecimal result = CheckSpecialResult(config, x, y, BasicOperations.Add); + if (result != null) { + return result; + } + + if (IsZero(x)) { + if (limit == 0) { + return y; + } else { + return LimitPrecision(config, y, limit, config.RoundingMode); + } + } + if (IsZero(y)) { + if (limit == 0) { + return x; + } else { + return LimitPrecision(config, x, limit, config.RoundingMode); + } + } + + int exponent = Math.Max(x._exponent, y._exponent); + int sign = x._sign; + Fraction fraction; + int exponentDiff; + + try { + checked { + exponentDiff = checked(x._exponent - y._exponent); + int diffDigits; + if (x._sign == y._sign) { + fraction = Fraction.Add(x._fraction, y._fraction, exponentDiff, out diffDigits); + exponent = exponent + diffDigits; + sign = x._sign; + } else { + // Signs are different then we have to do a subtraction instead + fraction = Fraction.Subtract(x._fraction, y._fraction, exponentDiff, out diffDigits, out sign); + exponent = exponent + diffDigits; + if (sign == 0) { + return PositiveZero; + } + sign = x._sign * sign; + } + if (limit == 0) { + limit = fraction.DigitCount; + } + fraction = Fraction.LimitPrecision(sign, fraction, limit, config.RoundingMode, out diffDigits); + exponent = exponent + diffDigits; + } + } catch (OverflowException) { + return ExponentOverflow(config, sign, x._exponent); + } + + result = new BigDecimal(sign, fraction, exponent); + return result; + } + + private static BigDecimal ExponentOverflow(Config config, int sign, int exponent) { + if (exponent > 0) { + if ((config.OverflowMode & OverflowExceptionModes.Overflow) == OverflowExceptionModes.Overflow) { + throw new FloatDomainError("Exponent overflow"); + } + } else { + if ((config.OverflowMode & OverflowExceptionModes.Underflow) == OverflowExceptionModes.Underflow) { + throw new FloatDomainError("Exponent underflow"); + } + } + // Always returns Zero on exponent overflow. + // It would seem more appropriate to return +/-Infinity or Zero depending on exponent and sign. + return PositiveZero; + } + #endregion + + #region Special Comparisons + //TODO: There is some redundancy in this table that could be removed. + private static int? CheckSpecialComparison(BigDecimal x, BigDecimal y) { + // Non-zero comparisons are not allowed (since these are not special cases) + Debug.Assert(!IsNonZeroFinite(x) || !IsNonZeroFinite(y)); + + // Encode the array indices + int xSign = x._sign == -1 ? 0 : 1; + int ySign = y._sign == -1 ? 0 : 1; + int xZero = IsZero(x) ? 0 : 1; + int yZero = IsZero(y) ? 0 : 1; + int xType = (int)x._type; + int yType = (int)y._type; + return SC[xSign, xZero, xType, ySign, yZero, yType]; + } + /// + /// Generated by the following ruby code: + //require 'bigdecimal' + + //signs = [:-, :+] + //zeros = [true, false] + //types = [:NaN, :Finite, :Infinite] + //ops = [:+, :-, :*, :/] + + //def CreateBigDecimal(sign, zero, type, finiteValue) + // if zero + // BigDecimal.new("#{sign}0.0") + // else + // if type == :NaN + // BigDecimal.new("NaN") + // elsif type == :Infinite + // BigDecimal.new("#{sign}Infinity") + // else + // BigDecimal.new("#{sign}#{finiteValue}") + // end + // end + //end + + //def map(r, null) + // if r.sign == BigDecimal::SIGN_NaN + // result = 'NaN' + // elsif r.sign == BigDecimal::SIGN_POSITIVE_INFINITE + // result = 'PositiveInfinity' + // elsif r.sign == BigDecimal::SIGN_NEGATIVE_INFINITE + // result = 'NegativeInfinity' + // elsif r.sign == BigDecimal::SIGN_POSITIVE_ZERO + // result = 'PositiveZero' + // elsif r.sign == BigDecimal::SIGN_NEGATIVE_ZERO + // result = 'NegativeZero' + // elsif null + // result = 'null' + // else + // result = 'Finite' + // end + //end + //signs.each do |xSign| + // zeros.each do |xZero| + // types.each do |xType| + // signs.each do |ySign| + // zeros.each do |yZero| + // types.each do |yType| + // x = CreateBigDecimal(xSign, xZero, xType, 7) + // y = CreateBigDecimal(ySign, yZero, yType, 11) + // cmp = (x <=> y) || "null" + // puts "// #{map(x, false)} <=> #{map(y, false)} = #{cmp}" + // puts "SC[#{signs.index(xSign)}, #{zeros.index(xZero)}, #{types.index(xType)}, #{signs.index(ySign)}, #{zeros.index(yZero)}, #{types.index(yType)}] = #{cmp};" + // end + // end + // end + // end + // end + //end + /// + private static readonly int?[, , , , ,] SC = new int?[2, 2, 3, 2, 2, 3]; + private static void CreateSC() { + // xSign, xZero, xType, ySign, yZero, yType + // NegativeZero <=> NegativeZero = 0 + SC[0, 0, 0, 0, 0, 0] = 0; + // NegativeZero <=> NegativeZero = 0 + SC[0, 0, 0, 0, 0, 1] = 0; + // NegativeZero <=> NegativeZero = 0 + SC[0, 0, 0, 0, 0, 2] = 0; + // NegativeZero <=> NaN = null + SC[0, 0, 0, 0, 1, 0] = null; + // NegativeZero <=> Finite = 1 + SC[0, 0, 0, 0, 1, 1] = 1; + // NegativeZero <=> NegativeInfinity = 1 + SC[0, 0, 0, 0, 1, 2] = 1; + // NegativeZero <=> PositiveZero = 0 + SC[0, 0, 0, 1, 0, 0] = 0; + // NegativeZero <=> PositiveZero = 0 + SC[0, 0, 0, 1, 0, 1] = 0; + // NegativeZero <=> PositiveZero = 0 + SC[0, 0, 0, 1, 0, 2] = 0; + // NegativeZero <=> NaN = null + SC[0, 0, 0, 1, 1, 0] = null; + // NegativeZero <=> Finite = -1 + SC[0, 0, 0, 1, 1, 1] = -1; + // NegativeZero <=> PositiveInfinity = -1 + SC[0, 0, 0, 1, 1, 2] = -1; + // NegativeZero <=> NegativeZero = 0 + SC[0, 0, 1, 0, 0, 0] = 0; + // NegativeZero <=> NegativeZero = 0 + SC[0, 0, 1, 0, 0, 1] = 0; + // NegativeZero <=> NegativeZero = 0 + SC[0, 0, 1, 0, 0, 2] = 0; + // NegativeZero <=> NaN = null + SC[0, 0, 1, 0, 1, 0] = null; + // NegativeZero <=> Finite = 1 + SC[0, 0, 1, 0, 1, 1] = 1; + // NegativeZero <=> NegativeInfinity = 1 + SC[0, 0, 1, 0, 1, 2] = 1; + // NegativeZero <=> PositiveZero = 0 + SC[0, 0, 1, 1, 0, 0] = 0; + // NegativeZero <=> PositiveZero = 0 + SC[0, 0, 1, 1, 0, 1] = 0; + // NegativeZero <=> PositiveZero = 0 + SC[0, 0, 1, 1, 0, 2] = 0; + // NegativeZero <=> NaN = null + SC[0, 0, 1, 1, 1, 0] = null; + // NegativeZero <=> Finite = -1 + SC[0, 0, 1, 1, 1, 1] = -1; + // NegativeZero <=> PositiveInfinity = -1 + SC[0, 0, 1, 1, 1, 2] = -1; + // NegativeZero <=> NegativeZero = 0 + SC[0, 0, 2, 0, 0, 0] = 0; + // NegativeZero <=> NegativeZero = 0 + SC[0, 0, 2, 0, 0, 1] = 0; + // NegativeZero <=> NegativeZero = 0 + SC[0, 0, 2, 0, 0, 2] = 0; + // NegativeZero <=> NaN = null + SC[0, 0, 2, 0, 1, 0] = null; + // NegativeZero <=> Finite = 1 + SC[0, 0, 2, 0, 1, 1] = 1; + // NegativeZero <=> NegativeInfinity = 1 + SC[0, 0, 2, 0, 1, 2] = 1; + // NegativeZero <=> PositiveZero = 0 + SC[0, 0, 2, 1, 0, 0] = 0; + // NegativeZero <=> PositiveZero = 0 + SC[0, 0, 2, 1, 0, 1] = 0; + // NegativeZero <=> PositiveZero = 0 + SC[0, 0, 2, 1, 0, 2] = 0; + // NegativeZero <=> NaN = null + SC[0, 0, 2, 1, 1, 0] = null; + // NegativeZero <=> Finite = -1 + SC[0, 0, 2, 1, 1, 1] = -1; + // NegativeZero <=> PositiveInfinity = -1 + SC[0, 0, 2, 1, 1, 2] = -1; + // NaN <=> NegativeZero = null + SC[0, 1, 0, 0, 0, 0] = null; + // NaN <=> NegativeZero = null + SC[0, 1, 0, 0, 0, 1] = null; + // NaN <=> NegativeZero = null + SC[0, 1, 0, 0, 0, 2] = null; + // NaN <=> NaN = null + SC[0, 1, 0, 0, 1, 0] = null; + // NaN <=> Finite = null + SC[0, 1, 0, 0, 1, 1] = null; + // NaN <=> NegativeInfinity = null + SC[0, 1, 0, 0, 1, 2] = null; + // NaN <=> PositiveZero = null + SC[0, 1, 0, 1, 0, 0] = null; + // NaN <=> PositiveZero = null + SC[0, 1, 0, 1, 0, 1] = null; + // NaN <=> PositiveZero = null + SC[0, 1, 0, 1, 0, 2] = null; + // NaN <=> NaN = null + SC[0, 1, 0, 1, 1, 0] = null; + // NaN <=> Finite = null + SC[0, 1, 0, 1, 1, 1] = null; + // NaN <=> PositiveInfinity = null + SC[0, 1, 0, 1, 1, 2] = null; + // Finite <=> NegativeZero = -1 + SC[0, 1, 1, 0, 0, 0] = -1; + // Finite <=> NegativeZero = -1 + SC[0, 1, 1, 0, 0, 1] = -1; + // Finite <=> NegativeZero = -1 + SC[0, 1, 1, 0, 0, 2] = -1; + // Finite <=> NaN = null + SC[0, 1, 1, 0, 1, 0] = null; + // Finite <=> Finite = 1 + SC[0, 1, 1, 0, 1, 1] = 1; + // Finite <=> NegativeInfinity = 1 + SC[0, 1, 1, 0, 1, 2] = 1; + // Finite <=> PositiveZero = -1 + SC[0, 1, 1, 1, 0, 0] = -1; + // Finite <=> PositiveZero = -1 + SC[0, 1, 1, 1, 0, 1] = -1; + // Finite <=> PositiveZero = -1 + SC[0, 1, 1, 1, 0, 2] = -1; + // Finite <=> NaN = null + SC[0, 1, 1, 1, 1, 0] = null; + // Finite <=> Finite = -1 + SC[0, 1, 1, 1, 1, 1] = -1; + // Finite <=> PositiveInfinity = -1 + SC[0, 1, 1, 1, 1, 2] = -1; + // NegativeInfinity <=> NegativeZero = -1 + SC[0, 1, 2, 0, 0, 0] = -1; + // NegativeInfinity <=> NegativeZero = -1 + SC[0, 1, 2, 0, 0, 1] = -1; + // NegativeInfinity <=> NegativeZero = -1 + SC[0, 1, 2, 0, 0, 2] = -1; + // NegativeInfinity <=> NaN = null + SC[0, 1, 2, 0, 1, 0] = null; + // NegativeInfinity <=> Finite = -1 + SC[0, 1, 2, 0, 1, 1] = -1; + // NegativeInfinity <=> NegativeInfinity = 0 + SC[0, 1, 2, 0, 1, 2] = 0; + // NegativeInfinity <=> PositiveZero = -1 + SC[0, 1, 2, 1, 0, 0] = -1; + // NegativeInfinity <=> PositiveZero = -1 + SC[0, 1, 2, 1, 0, 1] = -1; + // NegativeInfinity <=> PositiveZero = -1 + SC[0, 1, 2, 1, 0, 2] = -1; + // NegativeInfinity <=> NaN = null + SC[0, 1, 2, 1, 1, 0] = null; + // NegativeInfinity <=> Finite = -1 + SC[0, 1, 2, 1, 1, 1] = -1; + // NegativeInfinity <=> PositiveInfinity = -1 + SC[0, 1, 2, 1, 1, 2] = -1; + // PositiveZero <=> NegativeZero = 0 + SC[1, 0, 0, 0, 0, 0] = 0; + // PositiveZero <=> NegativeZero = 0 + SC[1, 0, 0, 0, 0, 1] = 0; + // PositiveZero <=> NegativeZero = 0 + SC[1, 0, 0, 0, 0, 2] = 0; + // PositiveZero <=> NaN = null + SC[1, 0, 0, 0, 1, 0] = null; + // PositiveZero <=> Finite = 1 + SC[1, 0, 0, 0, 1, 1] = 1; + // PositiveZero <=> NegativeInfinity = 1 + SC[1, 0, 0, 0, 1, 2] = 1; + // PositiveZero <=> PositiveZero = 0 + SC[1, 0, 0, 1, 0, 0] = 0; + // PositiveZero <=> PositiveZero = 0 + SC[1, 0, 0, 1, 0, 1] = 0; + // PositiveZero <=> PositiveZero = 0 + SC[1, 0, 0, 1, 0, 2] = 0; + // PositiveZero <=> NaN = null + SC[1, 0, 0, 1, 1, 0] = null; + // PositiveZero <=> Finite = -1 + SC[1, 0, 0, 1, 1, 1] = -1; + // PositiveZero <=> PositiveInfinity = -1 + SC[1, 0, 0, 1, 1, 2] = -1; + // PositiveZero <=> NegativeZero = 0 + SC[1, 0, 1, 0, 0, 0] = 0; + // PositiveZero <=> NegativeZero = 0 + SC[1, 0, 1, 0, 0, 1] = 0; + // PositiveZero <=> NegativeZero = 0 + SC[1, 0, 1, 0, 0, 2] = 0; + // PositiveZero <=> NaN = null + SC[1, 0, 1, 0, 1, 0] = null; + // PositiveZero <=> Finite = 1 + SC[1, 0, 1, 0, 1, 1] = 1; + // PositiveZero <=> NegativeInfinity = 1 + SC[1, 0, 1, 0, 1, 2] = 1; + // PositiveZero <=> PositiveZero = 0 + SC[1, 0, 1, 1, 0, 0] = 0; + // PositiveZero <=> PositiveZero = 0 + SC[1, 0, 1, 1, 0, 1] = 0; + // PositiveZero <=> PositiveZero = 0 + SC[1, 0, 1, 1, 0, 2] = 0; + // PositiveZero <=> NaN = null + SC[1, 0, 1, 1, 1, 0] = null; + // PositiveZero <=> Finite = -1 + SC[1, 0, 1, 1, 1, 1] = -1; + // PositiveZero <=> PositiveInfinity = -1 + SC[1, 0, 1, 1, 1, 2] = -1; + // PositiveZero <=> NegativeZero = 0 + SC[1, 0, 2, 0, 0, 0] = 0; + // PositiveZero <=> NegativeZero = 0 + SC[1, 0, 2, 0, 0, 1] = 0; + // PositiveZero <=> NegativeZero = 0 + SC[1, 0, 2, 0, 0, 2] = 0; + // PositiveZero <=> NaN = null + SC[1, 0, 2, 0, 1, 0] = null; + // PositiveZero <=> Finite = 1 + SC[1, 0, 2, 0, 1, 1] = 1; + // PositiveZero <=> NegativeInfinity = 1 + SC[1, 0, 2, 0, 1, 2] = 1; + // PositiveZero <=> PositiveZero = 0 + SC[1, 0, 2, 1, 0, 0] = 0; + // PositiveZero <=> PositiveZero = 0 + SC[1, 0, 2, 1, 0, 1] = 0; + // PositiveZero <=> PositiveZero = 0 + SC[1, 0, 2, 1, 0, 2] = 0; + // PositiveZero <=> NaN = null + SC[1, 0, 2, 1, 1, 0] = null; + // PositiveZero <=> Finite = -1 + SC[1, 0, 2, 1, 1, 1] = -1; + // PositiveZero <=> PositiveInfinity = -1 + SC[1, 0, 2, 1, 1, 2] = -1; + // NaN <=> NegativeZero = null + SC[1, 1, 0, 0, 0, 0] = null; + // NaN <=> NegativeZero = null + SC[1, 1, 0, 0, 0, 1] = null; + // NaN <=> NegativeZero = null + SC[1, 1, 0, 0, 0, 2] = null; + // NaN <=> NaN = null + SC[1, 1, 0, 0, 1, 0] = null; + // NaN <=> Finite = null + SC[1, 1, 0, 0, 1, 1] = null; + // NaN <=> NegativeInfinity = null + SC[1, 1, 0, 0, 1, 2] = null; + // NaN <=> PositiveZero = null + SC[1, 1, 0, 1, 0, 0] = null; + // NaN <=> PositiveZero = null + SC[1, 1, 0, 1, 0, 1] = null; + // NaN <=> PositiveZero = null + SC[1, 1, 0, 1, 0, 2] = null; + // NaN <=> NaN = null + SC[1, 1, 0, 1, 1, 0] = null; + // NaN <=> Finite = null + SC[1, 1, 0, 1, 1, 1] = null; + // NaN <=> PositiveInfinity = null + SC[1, 1, 0, 1, 1, 2] = null; + // Finite <=> NegativeZero = 1 + SC[1, 1, 1, 0, 0, 0] = 1; + // Finite <=> NegativeZero = 1 + SC[1, 1, 1, 0, 0, 1] = 1; + // Finite <=> NegativeZero = 1 + SC[1, 1, 1, 0, 0, 2] = 1; + // Finite <=> NaN = null + SC[1, 1, 1, 0, 1, 0] = null; + // Finite <=> Finite = 1 + SC[1, 1, 1, 0, 1, 1] = 1; + // Finite <=> NegativeInfinity = 1 + SC[1, 1, 1, 0, 1, 2] = 1; + // Finite <=> PositiveZero = 1 + SC[1, 1, 1, 1, 0, 0] = 1; + // Finite <=> PositiveZero = 1 + SC[1, 1, 1, 1, 0, 1] = 1; + // Finite <=> PositiveZero = 1 + SC[1, 1, 1, 1, 0, 2] = 1; + // Finite <=> NaN = null + SC[1, 1, 1, 1, 1, 0] = null; + // Finite <=> Finite = -1 + SC[1, 1, 1, 1, 1, 1] = -1; + // Finite <=> PositiveInfinity = -1 + SC[1, 1, 1, 1, 1, 2] = -1; + // PositiveInfinity <=> NegativeZero = 1 + SC[1, 1, 2, 0, 0, 0] = 1; + // PositiveInfinity <=> NegativeZero = 1 + SC[1, 1, 2, 0, 0, 1] = 1; + // PositiveInfinity <=> NegativeZero = 1 + SC[1, 1, 2, 0, 0, 2] = 1; + // PositiveInfinity <=> NaN = null + SC[1, 1, 2, 0, 1, 0] = null; + // PositiveInfinity <=> Finite = 1 + SC[1, 1, 2, 0, 1, 1] = 1; + // PositiveInfinity <=> NegativeInfinity = 1 + SC[1, 1, 2, 0, 1, 2] = 1; + // PositiveInfinity <=> PositiveZero = 1 + SC[1, 1, 2, 1, 0, 0] = 1; + // PositiveInfinity <=> PositiveZero = 1 + SC[1, 1, 2, 1, 0, 1] = 1; + // PositiveInfinity <=> PositiveZero = 1 + SC[1, 1, 2, 1, 0, 2] = 1; + // PositiveInfinity <=> NaN = null + SC[1, 1, 2, 1, 1, 0] = null; + // PositiveInfinity <=> Finite = 1 + SC[1, 1, 2, 1, 1, 1] = 1; + // PositiveInfinity <=> PositiveInfinity = 0 + SC[1, 1, 2, 1, 1, 2] = 0; + } + #endregion + + #region Special Results + //TODO: Probably don't need the subtract operation since it is symetric with addition operation in terms of special results + /// + /// Look up special results for calculations that involve unusual values such as NaN, infinity and zero. + /// + /// The left hand side of the operation + /// The right hand side of the operation + /// The operation itself + /// The special result or null if the result is not special + private static BigDecimal CheckSpecialResult(Config config, BigDecimal x, BigDecimal y, BasicOperations op) { + // Encode the array indices + int xSign = x._sign == -1 ? 0 : 1; + int ySign = y._sign == -1 ? 0 : 1; + int xZero = IsZero(x) ? 0 : 1; + int yZero = IsZero(y) ? 0 : 1; + int xType = (int)x._type; + int yType = (int)y._type; + int opValue = (int)op; + + // Look up the special result + BigDecimal result = SR[xSign, xZero, xType, opValue, ySign, yZero, yType]; + if (result != null) { + return CheckOverflowExceptions(config, result); + } + return null; + } + + private static BigDecimal/*!*/ CheckOverflowExceptions(Config/*!*/ config, BigDecimal/*!*/ result) { + if (IsNaN(result) && (config.OverflowMode & OverflowExceptionModes.NaN) == OverflowExceptionModes.NaN) { + throw new FloatDomainError("Computation results to 'NaN'"); + } else if (IsInfinite(result) && (config.OverflowMode & OverflowExceptionModes.Infinity) == OverflowExceptionModes.Infinity) { + throw new FloatDomainError("Computation results to 'Infinity'"); + } + return result; + } + + /// + /// Generated by the following ruby code + //require 'bigdecimal' + + //signs = [:-, :+] + //zeros = [true, false] + //types = [:NaN, :Finite, :Infinite] + //ops = [:+, :-, :*, :/] + + //def CreateBigDecimal(sign, zero, type, finiteValue) + // if zero + // BigDecimal.new("#{sign}0.0") + // else + // if type == :NaN + // BigDecimal.new("NaN") + // elsif type == :Infinite + // BigDecimal.new("#{sign}Infinity") + // else + // BigDecimal.new("#{sign}#{finiteValue}") + // end + // end + //end + + //def map(r, null) + // if r.sign == BigDecimal::SIGN_NaN + // result = 'NaN' + // elsif r.sign == BigDecimal::SIGN_POSITIVE_INFINITE + // result = 'PositiveInfinity' + // elsif r.sign == BigDecimal::SIGN_NEGATIVE_INFINITE + // result = 'NegativeInfinity' + // elsif r.sign == BigDecimal::SIGN_POSITIVE_ZERO + // result = 'PositiveZero' + // elsif r.sign == BigDecimal::SIGN_NEGATIVE_ZERO + // result = 'NegativeZero' + // elsif null + // result = 'null' + // else + // result = 'Finite' + // end + //end + + //signs.each do |xSign| + //zeros.each do |xZero| + // types.each do |xType| + // ops.each do |op| + // signs.each do |ySign| + // zeros.each do |yZero| + // types.each do |yType| + // x = CreateBigDecimal(xSign, xZero, xType, 7) + // y = CreateBigDecimal(ySign, yZero, yType, 11) + // r = x.send(op, y) + // result = map(r, true) + // puts "// #{map(x, false)} #{op} #{map(y, false)} = #{result}" + // puts "SR[#{signs.index(xSign)}, #{zeros.index(xZero)}, #{types.index(xType)}, #{ops.index(op)}, #{signs.index(ySign)}, #{zeros.index(yZero)}, #{types.index(yType)}] = #{result};" + // end + // end + // end + // end + // end + //end + //end + /// + private static readonly BigDecimal[, , , , , ,] SR = new BigDecimal[2, 2, 3, 4, 2, 2, 3]; + private static void CreateSR() { + //[xSign, xZero, xType, Op, ySign, yZero, yType] + // NegativeZero + NegativeZero = NegativeZero + SR[0, 0, 0, 0, 0, 0, 0] = NegativeZero; + // NegativeZero + NegativeZero = NegativeZero + SR[0, 0, 0, 0, 0, 0, 1] = NegativeZero; + // NegativeZero + NegativeZero = NegativeZero + SR[0, 0, 0, 0, 0, 0, 2] = NegativeZero; + // NegativeZero + NaN = NaN + SR[0, 0, 0, 0, 0, 1, 0] = NaN; + // NegativeZero + Finite = null + SR[0, 0, 0, 0, 0, 1, 1] = null; + // NegativeZero + NegativeInfinity = NegativeInfinity + SR[0, 0, 0, 0, 0, 1, 2] = NegativeInfinity; + // NegativeZero + PositiveZero = PositiveZero + SR[0, 0, 0, 0, 1, 0, 0] = PositiveZero; + // NegativeZero + PositiveZero = PositiveZero + SR[0, 0, 0, 0, 1, 0, 1] = PositiveZero; + // NegativeZero + PositiveZero = PositiveZero + SR[0, 0, 0, 0, 1, 0, 2] = PositiveZero; + // NegativeZero + NaN = NaN + SR[0, 0, 0, 0, 1, 1, 0] = NaN; + // NegativeZero + Finite = null + SR[0, 0, 0, 0, 1, 1, 1] = null; + // NegativeZero + PositiveInfinity = PositiveInfinity + SR[0, 0, 0, 0, 1, 1, 2] = PositiveInfinity; + // NegativeZero - NegativeZero = PositiveZero + SR[0, 0, 0, 1, 0, 0, 0] = PositiveZero; + // NegativeZero - NegativeZero = PositiveZero + SR[0, 0, 0, 1, 0, 0, 1] = PositiveZero; + // NegativeZero - NegativeZero = PositiveZero + SR[0, 0, 0, 1, 0, 0, 2] = PositiveZero; + // NegativeZero - NaN = NaN + SR[0, 0, 0, 1, 0, 1, 0] = NaN; + // NegativeZero - Finite = null + SR[0, 0, 0, 1, 0, 1, 1] = null; + // NegativeZero - NegativeInfinity = PositiveInfinity + SR[0, 0, 0, 1, 0, 1, 2] = PositiveInfinity; + // NegativeZero - PositiveZero = NegativeZero + SR[0, 0, 0, 1, 1, 0, 0] = NegativeZero; + // NegativeZero - PositiveZero = NegativeZero + SR[0, 0, 0, 1, 1, 0, 1] = NegativeZero; + // NegativeZero - PositiveZero = NegativeZero + SR[0, 0, 0, 1, 1, 0, 2] = NegativeZero; + // NegativeZero - NaN = NaN + SR[0, 0, 0, 1, 1, 1, 0] = NaN; + // NegativeZero - Finite = null + SR[0, 0, 0, 1, 1, 1, 1] = null; + // NegativeZero - PositiveInfinity = NegativeInfinity + SR[0, 0, 0, 1, 1, 1, 2] = NegativeInfinity; + // NegativeZero * NegativeZero = PositiveZero + SR[0, 0, 0, 2, 0, 0, 0] = PositiveZero; + // NegativeZero * NegativeZero = PositiveZero + SR[0, 0, 0, 2, 0, 0, 1] = PositiveZero; + // NegativeZero * NegativeZero = PositiveZero + SR[0, 0, 0, 2, 0, 0, 2] = PositiveZero; + // NegativeZero * NaN = NaN + SR[0, 0, 0, 2, 0, 1, 0] = NaN; + // NegativeZero * Finite = PositiveZero + SR[0, 0, 0, 2, 0, 1, 1] = PositiveZero; + // NegativeZero * NegativeInfinity = NaN + SR[0, 0, 0, 2, 0, 1, 2] = NaN; + // NegativeZero * PositiveZero = NegativeZero + SR[0, 0, 0, 2, 1, 0, 0] = NegativeZero; + // NegativeZero * PositiveZero = NegativeZero + SR[0, 0, 0, 2, 1, 0, 1] = NegativeZero; + // NegativeZero * PositiveZero = NegativeZero + SR[0, 0, 0, 2, 1, 0, 2] = NegativeZero; + // NegativeZero * NaN = NaN + SR[0, 0, 0, 2, 1, 1, 0] = NaN; + // NegativeZero * Finite = NegativeZero + SR[0, 0, 0, 2, 1, 1, 1] = NegativeZero; + // NegativeZero * PositiveInfinity = NaN + SR[0, 0, 0, 2, 1, 1, 2] = NaN; + // NegativeZero / NegativeZero = NaN + SR[0, 0, 0, 3, 0, 0, 0] = NaN; + // NegativeZero / NegativeZero = NaN + SR[0, 0, 0, 3, 0, 0, 1] = NaN; + // NegativeZero / NegativeZero = NaN + SR[0, 0, 0, 3, 0, 0, 2] = NaN; + // NegativeZero / NaN = NaN + SR[0, 0, 0, 3, 0, 1, 0] = NaN; + // NegativeZero / Finite = PositiveZero + SR[0, 0, 0, 3, 0, 1, 1] = PositiveZero; + // NegativeZero / NegativeInfinity = PositiveZero + SR[0, 0, 0, 3, 0, 1, 2] = PositiveZero; + // NegativeZero / PositiveZero = NaN + SR[0, 0, 0, 3, 1, 0, 0] = NaN; + // NegativeZero / PositiveZero = NaN + SR[0, 0, 0, 3, 1, 0, 1] = NaN; + // NegativeZero / PositiveZero = NaN + SR[0, 0, 0, 3, 1, 0, 2] = NaN; + // NegativeZero / NaN = NaN + SR[0, 0, 0, 3, 1, 1, 0] = NaN; + // NegativeZero / Finite = NegativeZero + SR[0, 0, 0, 3, 1, 1, 1] = NegativeZero; + // NegativeZero / PositiveInfinity = NegativeZero + SR[0, 0, 0, 3, 1, 1, 2] = NegativeZero; + // NegativeZero + NegativeZero = NegativeZero + SR[0, 0, 1, 0, 0, 0, 0] = NegativeZero; + // NegativeZero + NegativeZero = NegativeZero + SR[0, 0, 1, 0, 0, 0, 1] = NegativeZero; + // NegativeZero + NegativeZero = NegativeZero + SR[0, 0, 1, 0, 0, 0, 2] = NegativeZero; + // NegativeZero + NaN = NaN + SR[0, 0, 1, 0, 0, 1, 0] = NaN; + // NegativeZero + Finite = null + SR[0, 0, 1, 0, 0, 1, 1] = null; + // NegativeZero + NegativeInfinity = NegativeInfinity + SR[0, 0, 1, 0, 0, 1, 2] = NegativeInfinity; + // NegativeZero + PositiveZero = PositiveZero + SR[0, 0, 1, 0, 1, 0, 0] = PositiveZero; + // NegativeZero + PositiveZero = PositiveZero + SR[0, 0, 1, 0, 1, 0, 1] = PositiveZero; + // NegativeZero + PositiveZero = PositiveZero + SR[0, 0, 1, 0, 1, 0, 2] = PositiveZero; + // NegativeZero + NaN = NaN + SR[0, 0, 1, 0, 1, 1, 0] = NaN; + // NegativeZero + Finite = null + SR[0, 0, 1, 0, 1, 1, 1] = null; + // NegativeZero + PositiveInfinity = PositiveInfinity + SR[0, 0, 1, 0, 1, 1, 2] = PositiveInfinity; + // NegativeZero - NegativeZero = PositiveZero + SR[0, 0, 1, 1, 0, 0, 0] = PositiveZero; + // NegativeZero - NegativeZero = PositiveZero + SR[0, 0, 1, 1, 0, 0, 1] = PositiveZero; + // NegativeZero - NegativeZero = PositiveZero + SR[0, 0, 1, 1, 0, 0, 2] = PositiveZero; + // NegativeZero - NaN = NaN + SR[0, 0, 1, 1, 0, 1, 0] = NaN; + // NegativeZero - Finite = null + SR[0, 0, 1, 1, 0, 1, 1] = null; + // NegativeZero - NegativeInfinity = PositiveInfinity + SR[0, 0, 1, 1, 0, 1, 2] = PositiveInfinity; + // NegativeZero - PositiveZero = NegativeZero + SR[0, 0, 1, 1, 1, 0, 0] = NegativeZero; + // NegativeZero - PositiveZero = NegativeZero + SR[0, 0, 1, 1, 1, 0, 1] = NegativeZero; + // NegativeZero - PositiveZero = NegativeZero + SR[0, 0, 1, 1, 1, 0, 2] = NegativeZero; + // NegativeZero - NaN = NaN + SR[0, 0, 1, 1, 1, 1, 0] = NaN; + // NegativeZero - Finite = null + SR[0, 0, 1, 1, 1, 1, 1] = null; + // NegativeZero - PositiveInfinity = NegativeInfinity + SR[0, 0, 1, 1, 1, 1, 2] = NegativeInfinity; + // NegativeZero * NegativeZero = PositiveZero + SR[0, 0, 1, 2, 0, 0, 0] = PositiveZero; + // NegativeZero * NegativeZero = PositiveZero + SR[0, 0, 1, 2, 0, 0, 1] = PositiveZero; + // NegativeZero * NegativeZero = PositiveZero + SR[0, 0, 1, 2, 0, 0, 2] = PositiveZero; + // NegativeZero * NaN = NaN + SR[0, 0, 1, 2, 0, 1, 0] = NaN; + // NegativeZero * Finite = PositiveZero + SR[0, 0, 1, 2, 0, 1, 1] = PositiveZero; + // NegativeZero * NegativeInfinity = NaN + SR[0, 0, 1, 2, 0, 1, 2] = NaN; + // NegativeZero * PositiveZero = NegativeZero + SR[0, 0, 1, 2, 1, 0, 0] = NegativeZero; + // NegativeZero * PositiveZero = NegativeZero + SR[0, 0, 1, 2, 1, 0, 1] = NegativeZero; + // NegativeZero * PositiveZero = NegativeZero + SR[0, 0, 1, 2, 1, 0, 2] = NegativeZero; + // NegativeZero * NaN = NaN + SR[0, 0, 1, 2, 1, 1, 0] = NaN; + // NegativeZero * Finite = NegativeZero + SR[0, 0, 1, 2, 1, 1, 1] = NegativeZero; + // NegativeZero * PositiveInfinity = NaN + SR[0, 0, 1, 2, 1, 1, 2] = NaN; + // NegativeZero / NegativeZero = NaN + SR[0, 0, 1, 3, 0, 0, 0] = NaN; + // NegativeZero / NegativeZero = NaN + SR[0, 0, 1, 3, 0, 0, 1] = NaN; + // NegativeZero / NegativeZero = NaN + SR[0, 0, 1, 3, 0, 0, 2] = NaN; + // NegativeZero / NaN = NaN + SR[0, 0, 1, 3, 0, 1, 0] = NaN; + // NegativeZero / Finite = PositiveZero + SR[0, 0, 1, 3, 0, 1, 1] = PositiveZero; + // NegativeZero / NegativeInfinity = PositiveZero + SR[0, 0, 1, 3, 0, 1, 2] = PositiveZero; + // NegativeZero / PositiveZero = NaN + SR[0, 0, 1, 3, 1, 0, 0] = NaN; + // NegativeZero / PositiveZero = NaN + SR[0, 0, 1, 3, 1, 0, 1] = NaN; + // NegativeZero / PositiveZero = NaN + SR[0, 0, 1, 3, 1, 0, 2] = NaN; + // NegativeZero / NaN = NaN + SR[0, 0, 1, 3, 1, 1, 0] = NaN; + // NegativeZero / Finite = NegativeZero + SR[0, 0, 1, 3, 1, 1, 1] = NegativeZero; + // NegativeZero / PositiveInfinity = NegativeZero + SR[0, 0, 1, 3, 1, 1, 2] = NegativeZero; + // NegativeZero + NegativeZero = NegativeZero + SR[0, 0, 2, 0, 0, 0, 0] = NegativeZero; + // NegativeZero + NegativeZero = NegativeZero + SR[0, 0, 2, 0, 0, 0, 1] = NegativeZero; + // NegativeZero + NegativeZero = NegativeZero + SR[0, 0, 2, 0, 0, 0, 2] = NegativeZero; + // NegativeZero + NaN = NaN + SR[0, 0, 2, 0, 0, 1, 0] = NaN; + // NegativeZero + Finite = null + SR[0, 0, 2, 0, 0, 1, 1] = null; + // NegativeZero + NegativeInfinity = NegativeInfinity + SR[0, 0, 2, 0, 0, 1, 2] = NegativeInfinity; + // NegativeZero + PositiveZero = PositiveZero + SR[0, 0, 2, 0, 1, 0, 0] = PositiveZero; + // NegativeZero + PositiveZero = PositiveZero + SR[0, 0, 2, 0, 1, 0, 1] = PositiveZero; + // NegativeZero + PositiveZero = PositiveZero + SR[0, 0, 2, 0, 1, 0, 2] = PositiveZero; + // NegativeZero + NaN = NaN + SR[0, 0, 2, 0, 1, 1, 0] = NaN; + // NegativeZero + Finite = null + SR[0, 0, 2, 0, 1, 1, 1] = null; + // NegativeZero + PositiveInfinity = PositiveInfinity + SR[0, 0, 2, 0, 1, 1, 2] = PositiveInfinity; + // NegativeZero - NegativeZero = PositiveZero + SR[0, 0, 2, 1, 0, 0, 0] = PositiveZero; + // NegativeZero - NegativeZero = PositiveZero + SR[0, 0, 2, 1, 0, 0, 1] = PositiveZero; + // NegativeZero - NegativeZero = PositiveZero + SR[0, 0, 2, 1, 0, 0, 2] = PositiveZero; + // NegativeZero - NaN = NaN + SR[0, 0, 2, 1, 0, 1, 0] = NaN; + // NegativeZero - Finite = null + SR[0, 0, 2, 1, 0, 1, 1] = null; + // NegativeZero - NegativeInfinity = PositiveInfinity + SR[0, 0, 2, 1, 0, 1, 2] = PositiveInfinity; + // NegativeZero - PositiveZero = NegativeZero + SR[0, 0, 2, 1, 1, 0, 0] = NegativeZero; + // NegativeZero - PositiveZero = NegativeZero + SR[0, 0, 2, 1, 1, 0, 1] = NegativeZero; + // NegativeZero - PositiveZero = NegativeZero + SR[0, 0, 2, 1, 1, 0, 2] = NegativeZero; + // NegativeZero - NaN = NaN + SR[0, 0, 2, 1, 1, 1, 0] = NaN; + // NegativeZero - Finite = null + SR[0, 0, 2, 1, 1, 1, 1] = null; + // NegativeZero - PositiveInfinity = NegativeInfinity + SR[0, 0, 2, 1, 1, 1, 2] = NegativeInfinity; + // NegativeZero * NegativeZero = PositiveZero + SR[0, 0, 2, 2, 0, 0, 0] = PositiveZero; + // NegativeZero * NegativeZero = PositiveZero + SR[0, 0, 2, 2, 0, 0, 1] = PositiveZero; + // NegativeZero * NegativeZero = PositiveZero + SR[0, 0, 2, 2, 0, 0, 2] = PositiveZero; + // NegativeZero * NaN = NaN + SR[0, 0, 2, 2, 0, 1, 0] = NaN; + // NegativeZero * Finite = PositiveZero + SR[0, 0, 2, 2, 0, 1, 1] = PositiveZero; + // NegativeZero * NegativeInfinity = NaN + SR[0, 0, 2, 2, 0, 1, 2] = NaN; + // NegativeZero * PositiveZero = NegativeZero + SR[0, 0, 2, 2, 1, 0, 0] = NegativeZero; + // NegativeZero * PositiveZero = NegativeZero + SR[0, 0, 2, 2, 1, 0, 1] = NegativeZero; + // NegativeZero * PositiveZero = NegativeZero + SR[0, 0, 2, 2, 1, 0, 2] = NegativeZero; + // NegativeZero * NaN = NaN + SR[0, 0, 2, 2, 1, 1, 0] = NaN; + // NegativeZero * Finite = NegativeZero + SR[0, 0, 2, 2, 1, 1, 1] = NegativeZero; + // NegativeZero * PositiveInfinity = NaN + SR[0, 0, 2, 2, 1, 1, 2] = NaN; + // NegativeZero / NegativeZero = NaN + SR[0, 0, 2, 3, 0, 0, 0] = NaN; + // NegativeZero / NegativeZero = NaN + SR[0, 0, 2, 3, 0, 0, 1] = NaN; + // NegativeZero / NegativeZero = NaN + SR[0, 0, 2, 3, 0, 0, 2] = NaN; + // NegativeZero / NaN = NaN + SR[0, 0, 2, 3, 0, 1, 0] = NaN; + // NegativeZero / Finite = PositiveZero + SR[0, 0, 2, 3, 0, 1, 1] = PositiveZero; + // NegativeZero / NegativeInfinity = PositiveZero + SR[0, 0, 2, 3, 0, 1, 2] = PositiveZero; + // NegativeZero / PositiveZero = NaN + SR[0, 0, 2, 3, 1, 0, 0] = NaN; + // NegativeZero / PositiveZero = NaN + SR[0, 0, 2, 3, 1, 0, 1] = NaN; + // NegativeZero / PositiveZero = NaN + SR[0, 0, 2, 3, 1, 0, 2] = NaN; + // NegativeZero / NaN = NaN + SR[0, 0, 2, 3, 1, 1, 0] = NaN; + // NegativeZero / Finite = NegativeZero + SR[0, 0, 2, 3, 1, 1, 1] = NegativeZero; + // NegativeZero / PositiveInfinity = NegativeZero + SR[0, 0, 2, 3, 1, 1, 2] = NegativeZero; + // NaN + NegativeZero = NaN + SR[0, 1, 0, 0, 0, 0, 0] = NaN; + // NaN + NegativeZero = NaN + SR[0, 1, 0, 0, 0, 0, 1] = NaN; + // NaN + NegativeZero = NaN + SR[0, 1, 0, 0, 0, 0, 2] = NaN; + // NaN + NaN = NaN + SR[0, 1, 0, 0, 0, 1, 0] = NaN; + // NaN + Finite = NaN + SR[0, 1, 0, 0, 0, 1, 1] = NaN; + // NaN + NegativeInfinity = NaN + SR[0, 1, 0, 0, 0, 1, 2] = NaN; + // NaN + PositiveZero = NaN + SR[0, 1, 0, 0, 1, 0, 0] = NaN; + // NaN + PositiveZero = NaN + SR[0, 1, 0, 0, 1, 0, 1] = NaN; + // NaN + PositiveZero = NaN + SR[0, 1, 0, 0, 1, 0, 2] = NaN; + // NaN + NaN = NaN + SR[0, 1, 0, 0, 1, 1, 0] = NaN; + // NaN + Finite = NaN + SR[0, 1, 0, 0, 1, 1, 1] = NaN; + // NaN + PositiveInfinity = NaN + SR[0, 1, 0, 0, 1, 1, 2] = NaN; + // NaN - NegativeZero = NaN + SR[0, 1, 0, 1, 0, 0, 0] = NaN; + // NaN - NegativeZero = NaN + SR[0, 1, 0, 1, 0, 0, 1] = NaN; + // NaN - NegativeZero = NaN + SR[0, 1, 0, 1, 0, 0, 2] = NaN; + // NaN - NaN = NaN + SR[0, 1, 0, 1, 0, 1, 0] = NaN; + // NaN - Finite = NaN + SR[0, 1, 0, 1, 0, 1, 1] = NaN; + // NaN - NegativeInfinity = NaN + SR[0, 1, 0, 1, 0, 1, 2] = NaN; + // NaN - PositiveZero = NaN + SR[0, 1, 0, 1, 1, 0, 0] = NaN; + // NaN - PositiveZero = NaN + SR[0, 1, 0, 1, 1, 0, 1] = NaN; + // NaN - PositiveZero = NaN + SR[0, 1, 0, 1, 1, 0, 2] = NaN; + // NaN - NaN = NaN + SR[0, 1, 0, 1, 1, 1, 0] = NaN; + // NaN - Finite = NaN + SR[0, 1, 0, 1, 1, 1, 1] = NaN; + // NaN - PositiveInfinity = NaN + SR[0, 1, 0, 1, 1, 1, 2] = NaN; + // NaN * NegativeZero = NaN + SR[0, 1, 0, 2, 0, 0, 0] = NaN; + // NaN * NegativeZero = NaN + SR[0, 1, 0, 2, 0, 0, 1] = NaN; + // NaN * NegativeZero = NaN + SR[0, 1, 0, 2, 0, 0, 2] = NaN; + // NaN * NaN = NaN + SR[0, 1, 0, 2, 0, 1, 0] = NaN; + // NaN * Finite = NaN + SR[0, 1, 0, 2, 0, 1, 1] = NaN; + // NaN * NegativeInfinity = NaN + SR[0, 1, 0, 2, 0, 1, 2] = NaN; + // NaN * PositiveZero = NaN + SR[0, 1, 0, 2, 1, 0, 0] = NaN; + // NaN * PositiveZero = NaN + SR[0, 1, 0, 2, 1, 0, 1] = NaN; + // NaN * PositiveZero = NaN + SR[0, 1, 0, 2, 1, 0, 2] = NaN; + // NaN * NaN = NaN + SR[0, 1, 0, 2, 1, 1, 0] = NaN; + // NaN * Finite = NaN + SR[0, 1, 0, 2, 1, 1, 1] = NaN; + // NaN * PositiveInfinity = NaN + SR[0, 1, 0, 2, 1, 1, 2] = NaN; + // NaN / NegativeZero = NaN + SR[0, 1, 0, 3, 0, 0, 0] = NaN; + // NaN / NegativeZero = NaN + SR[0, 1, 0, 3, 0, 0, 1] = NaN; + // NaN / NegativeZero = NaN + SR[0, 1, 0, 3, 0, 0, 2] = NaN; + // NaN / NaN = NaN + SR[0, 1, 0, 3, 0, 1, 0] = NaN; + // NaN / Finite = NaN + SR[0, 1, 0, 3, 0, 1, 1] = NaN; + // NaN / NegativeInfinity = NaN + SR[0, 1, 0, 3, 0, 1, 2] = NaN; + // NaN / PositiveZero = NaN + SR[0, 1, 0, 3, 1, 0, 0] = NaN; + // NaN / PositiveZero = NaN + SR[0, 1, 0, 3, 1, 0, 1] = NaN; + // NaN / PositiveZero = NaN + SR[0, 1, 0, 3, 1, 0, 2] = NaN; + // NaN / NaN = NaN + SR[0, 1, 0, 3, 1, 1, 0] = NaN; + // NaN / Finite = NaN + SR[0, 1, 0, 3, 1, 1, 1] = NaN; + // NaN / PositiveInfinity = NaN + SR[0, 1, 0, 3, 1, 1, 2] = NaN; + // Finite + NegativeZero = null + SR[0, 1, 1, 0, 0, 0, 0] = null; + // Finite + NegativeZero = null + SR[0, 1, 1, 0, 0, 0, 1] = null; + // Finite + NegativeZero = null + SR[0, 1, 1, 0, 0, 0, 2] = null; + // Finite + NaN = NaN + SR[0, 1, 1, 0, 0, 1, 0] = NaN; + // Finite + Finite = null + SR[0, 1, 1, 0, 0, 1, 1] = null; + // Finite + NegativeInfinity = NegativeInfinity + SR[0, 1, 1, 0, 0, 1, 2] = NegativeInfinity; + // Finite + PositiveZero = null + SR[0, 1, 1, 0, 1, 0, 0] = null; + // Finite + PositiveZero = null + SR[0, 1, 1, 0, 1, 0, 1] = null; + // Finite + PositiveZero = null + SR[0, 1, 1, 0, 1, 0, 2] = null; + // Finite + NaN = NaN + SR[0, 1, 1, 0, 1, 1, 0] = NaN; + // Finite + Finite = null + SR[0, 1, 1, 0, 1, 1, 1] = null; + // Finite + PositiveInfinity = PositiveInfinity + SR[0, 1, 1, 0, 1, 1, 2] = PositiveInfinity; + // Finite - NegativeZero = null + SR[0, 1, 1, 1, 0, 0, 0] = null; + // Finite - NegativeZero = null + SR[0, 1, 1, 1, 0, 0, 1] = null; + // Finite - NegativeZero = null + SR[0, 1, 1, 1, 0, 0, 2] = null; + // Finite - NaN = NaN + SR[0, 1, 1, 1, 0, 1, 0] = NaN; + // Finite - Finite = null + SR[0, 1, 1, 1, 0, 1, 1] = null; + // Finite - NegativeInfinity = PositiveInfinity + SR[0, 1, 1, 1, 0, 1, 2] = PositiveInfinity; + // Finite - PositiveZero = null + SR[0, 1, 1, 1, 1, 0, 0] = null; + // Finite - PositiveZero = null + SR[0, 1, 1, 1, 1, 0, 1] = null; + // Finite - PositiveZero = null + SR[0, 1, 1, 1, 1, 0, 2] = null; + // Finite - NaN = NaN + SR[0, 1, 1, 1, 1, 1, 0] = NaN; + // Finite - Finite = null + SR[0, 1, 1, 1, 1, 1, 1] = null; + // Finite - PositiveInfinity = NegativeInfinity + SR[0, 1, 1, 1, 1, 1, 2] = NegativeInfinity; + // Finite * NegativeZero = PositiveZero + SR[0, 1, 1, 2, 0, 0, 0] = PositiveZero; + // Finite * NegativeZero = PositiveZero + SR[0, 1, 1, 2, 0, 0, 1] = PositiveZero; + // Finite * NegativeZero = PositiveZero + SR[0, 1, 1, 2, 0, 0, 2] = PositiveZero; + // Finite * NaN = NaN + SR[0, 1, 1, 2, 0, 1, 0] = NaN; + // Finite * Finite = null + SR[0, 1, 1, 2, 0, 1, 1] = null; + // Finite * NegativeInfinity = PositiveInfinity + SR[0, 1, 1, 2, 0, 1, 2] = PositiveInfinity; + // Finite * PositiveZero = NegativeZero + SR[0, 1, 1, 2, 1, 0, 0] = NegativeZero; + // Finite * PositiveZero = NegativeZero + SR[0, 1, 1, 2, 1, 0, 1] = NegativeZero; + // Finite * PositiveZero = NegativeZero + SR[0, 1, 1, 2, 1, 0, 2] = NegativeZero; + // Finite * NaN = NaN + SR[0, 1, 1, 2, 1, 1, 0] = NaN; + // Finite * Finite = null + SR[0, 1, 1, 2, 1, 1, 1] = null; + // Finite * PositiveInfinity = NegativeInfinity + SR[0, 1, 1, 2, 1, 1, 2] = NegativeInfinity; + // Finite / NegativeZero = PositiveInfinity + SR[0, 1, 1, 3, 0, 0, 0] = PositiveInfinity; + // Finite / NegativeZero = PositiveInfinity + SR[0, 1, 1, 3, 0, 0, 1] = PositiveInfinity; + // Finite / NegativeZero = PositiveInfinity + SR[0, 1, 1, 3, 0, 0, 2] = PositiveInfinity; + // Finite / NaN = NaN + SR[0, 1, 1, 3, 0, 1, 0] = NaN; + // Finite / Finite = null + SR[0, 1, 1, 3, 0, 1, 1] = null; + // Finite / NegativeInfinity = PositiveZero + SR[0, 1, 1, 3, 0, 1, 2] = PositiveZero; + // Finite / PositiveZero = NegativeInfinity + SR[0, 1, 1, 3, 1, 0, 0] = NegativeInfinity; + // Finite / PositiveZero = NegativeInfinity + SR[0, 1, 1, 3, 1, 0, 1] = NegativeInfinity; + // Finite / PositiveZero = NegativeInfinity + SR[0, 1, 1, 3, 1, 0, 2] = NegativeInfinity; + // Finite / NaN = NaN + SR[0, 1, 1, 3, 1, 1, 0] = NaN; + // Finite / Finite = null + SR[0, 1, 1, 3, 1, 1, 1] = null; + // Finite / PositiveInfinity = NegativeZero + SR[0, 1, 1, 3, 1, 1, 2] = NegativeZero; + // NegativeInfinity + NegativeZero = NegativeInfinity + SR[0, 1, 2, 0, 0, 0, 0] = NegativeInfinity; + // NegativeInfinity + NegativeZero = NegativeInfinity + SR[0, 1, 2, 0, 0, 0, 1] = NegativeInfinity; + // NegativeInfinity + NegativeZero = NegativeInfinity + SR[0, 1, 2, 0, 0, 0, 2] = NegativeInfinity; + // NegativeInfinity + NaN = NaN + SR[0, 1, 2, 0, 0, 1, 0] = NaN; + // NegativeInfinity + Finite = NegativeInfinity + SR[0, 1, 2, 0, 0, 1, 1] = NegativeInfinity; + // NegativeInfinity + NegativeInfinity = NegativeInfinity + SR[0, 1, 2, 0, 0, 1, 2] = NegativeInfinity; + // NegativeInfinity + PositiveZero = NegativeInfinity + SR[0, 1, 2, 0, 1, 0, 0] = NegativeInfinity; + // NegativeInfinity + PositiveZero = NegativeInfinity + SR[0, 1, 2, 0, 1, 0, 1] = NegativeInfinity; + // NegativeInfinity + PositiveZero = NegativeInfinity + SR[0, 1, 2, 0, 1, 0, 2] = NegativeInfinity; + // NegativeInfinity + NaN = NaN + SR[0, 1, 2, 0, 1, 1, 0] = NaN; + // NegativeInfinity + Finite = NegativeInfinity + SR[0, 1, 2, 0, 1, 1, 1] = NegativeInfinity; + // NegativeInfinity + PositiveInfinity = NaN + SR[0, 1, 2, 0, 1, 1, 2] = NaN; + // NegativeInfinity - NegativeZero = NegativeInfinity + SR[0, 1, 2, 1, 0, 0, 0] = NegativeInfinity; + // NegativeInfinity - NegativeZero = NegativeInfinity + SR[0, 1, 2, 1, 0, 0, 1] = NegativeInfinity; + // NegativeInfinity - NegativeZero = NegativeInfinity + SR[0, 1, 2, 1, 0, 0, 2] = NegativeInfinity; + // NegativeInfinity - NaN = NaN + SR[0, 1, 2, 1, 0, 1, 0] = NaN; + // NegativeInfinity - Finite = NegativeInfinity + SR[0, 1, 2, 1, 0, 1, 1] = NegativeInfinity; + // NegativeInfinity - NegativeInfinity = NaN + SR[0, 1, 2, 1, 0, 1, 2] = NaN; + // NegativeInfinity - PositiveZero = NegativeInfinity + SR[0, 1, 2, 1, 1, 0, 0] = NegativeInfinity; + // NegativeInfinity - PositiveZero = NegativeInfinity + SR[0, 1, 2, 1, 1, 0, 1] = NegativeInfinity; + // NegativeInfinity - PositiveZero = NegativeInfinity + SR[0, 1, 2, 1, 1, 0, 2] = NegativeInfinity; + // NegativeInfinity - NaN = NaN + SR[0, 1, 2, 1, 1, 1, 0] = NaN; + // NegativeInfinity - Finite = NegativeInfinity + SR[0, 1, 2, 1, 1, 1, 1] = NegativeInfinity; + // NegativeInfinity - PositiveInfinity = NegativeInfinity + SR[0, 1, 2, 1, 1, 1, 2] = NegativeInfinity; + // NegativeInfinity * NegativeZero = NaN + SR[0, 1, 2, 2, 0, 0, 0] = NaN; + // NegativeInfinity * NegativeZero = NaN + SR[0, 1, 2, 2, 0, 0, 1] = NaN; + // NegativeInfinity * NegativeZero = NaN + SR[0, 1, 2, 2, 0, 0, 2] = NaN; + // NegativeInfinity * NaN = NaN + SR[0, 1, 2, 2, 0, 1, 0] = NaN; + // NegativeInfinity * Finite = PositiveInfinity + SR[0, 1, 2, 2, 0, 1, 1] = PositiveInfinity; + // NegativeInfinity * NegativeInfinity = PositiveInfinity + SR[0, 1, 2, 2, 0, 1, 2] = PositiveInfinity; + // NegativeInfinity * PositiveZero = NaN + SR[0, 1, 2, 2, 1, 0, 0] = NaN; + // NegativeInfinity * PositiveZero = NaN + SR[0, 1, 2, 2, 1, 0, 1] = NaN; + // NegativeInfinity * PositiveZero = NaN + SR[0, 1, 2, 2, 1, 0, 2] = NaN; + // NegativeInfinity * NaN = NaN + SR[0, 1, 2, 2, 1, 1, 0] = NaN; + // NegativeInfinity * Finite = NegativeInfinity + SR[0, 1, 2, 2, 1, 1, 1] = NegativeInfinity; + // NegativeInfinity * PositiveInfinity = NegativeInfinity + SR[0, 1, 2, 2, 1, 1, 2] = NegativeInfinity; + // NegativeInfinity / NegativeZero = PositiveInfinity + SR[0, 1, 2, 3, 0, 0, 0] = PositiveInfinity; + // NegativeInfinity / NegativeZero = PositiveInfinity + SR[0, 1, 2, 3, 0, 0, 1] = PositiveInfinity; + // NegativeInfinity / NegativeZero = PositiveInfinity + SR[0, 1, 2, 3, 0, 0, 2] = PositiveInfinity; + // NegativeInfinity / NaN = NaN + SR[0, 1, 2, 3, 0, 1, 0] = NaN; + // NegativeInfinity / Finite = PositiveInfinity + SR[0, 1, 2, 3, 0, 1, 1] = PositiveInfinity; + // NegativeInfinity / NegativeInfinity = NaN + SR[0, 1, 2, 3, 0, 1, 2] = NaN; + // NegativeInfinity / PositiveZero = NegativeInfinity + SR[0, 1, 2, 3, 1, 0, 0] = NegativeInfinity; + // NegativeInfinity / PositiveZero = NegativeInfinity + SR[0, 1, 2, 3, 1, 0, 1] = NegativeInfinity; + // NegativeInfinity / PositiveZero = NegativeInfinity + SR[0, 1, 2, 3, 1, 0, 2] = NegativeInfinity; + // NegativeInfinity / NaN = NaN + SR[0, 1, 2, 3, 1, 1, 0] = NaN; + // NegativeInfinity / Finite = NegativeInfinity + SR[0, 1, 2, 3, 1, 1, 1] = NegativeInfinity; + // NegativeInfinity / PositiveInfinity = NaN + SR[0, 1, 2, 3, 1, 1, 2] = NaN; + // PositiveZero + NegativeZero = PositiveZero + SR[1, 0, 0, 0, 0, 0, 0] = PositiveZero; + // PositiveZero + NegativeZero = PositiveZero + SR[1, 0, 0, 0, 0, 0, 1] = PositiveZero; + // PositiveZero + NegativeZero = PositiveZero + SR[1, 0, 0, 0, 0, 0, 2] = PositiveZero; + // PositiveZero + NaN = NaN + SR[1, 0, 0, 0, 0, 1, 0] = NaN; + // PositiveZero + Finite = null + SR[1, 0, 0, 0, 0, 1, 1] = null; + // PositiveZero + NegativeInfinity = NegativeInfinity + SR[1, 0, 0, 0, 0, 1, 2] = NegativeInfinity; + // PositiveZero + PositiveZero = PositiveZero + SR[1, 0, 0, 0, 1, 0, 0] = PositiveZero; + // PositiveZero + PositiveZero = PositiveZero + SR[1, 0, 0, 0, 1, 0, 1] = PositiveZero; + // PositiveZero + PositiveZero = PositiveZero + SR[1, 0, 0, 0, 1, 0, 2] = PositiveZero; + // PositiveZero + NaN = NaN + SR[1, 0, 0, 0, 1, 1, 0] = NaN; + // PositiveZero + Finite = null + SR[1, 0, 0, 0, 1, 1, 1] = null; + // PositiveZero + PositiveInfinity = PositiveInfinity + SR[1, 0, 0, 0, 1, 1, 2] = PositiveInfinity; + // PositiveZero - NegativeZero = PositiveZero + SR[1, 0, 0, 1, 0, 0, 0] = PositiveZero; + // PositiveZero - NegativeZero = PositiveZero + SR[1, 0, 0, 1, 0, 0, 1] = PositiveZero; + // PositiveZero - NegativeZero = PositiveZero + SR[1, 0, 0, 1, 0, 0, 2] = PositiveZero; + // PositiveZero - NaN = NaN + SR[1, 0, 0, 1, 0, 1, 0] = NaN; + // PositiveZero - Finite = null + SR[1, 0, 0, 1, 0, 1, 1] = null; + // PositiveZero - NegativeInfinity = PositiveInfinity + SR[1, 0, 0, 1, 0, 1, 2] = PositiveInfinity; + // PositiveZero - PositiveZero = PositiveZero + SR[1, 0, 0, 1, 1, 0, 0] = PositiveZero; + // PositiveZero - PositiveZero = PositiveZero + SR[1, 0, 0, 1, 1, 0, 1] = PositiveZero; + // PositiveZero - PositiveZero = PositiveZero + SR[1, 0, 0, 1, 1, 0, 2] = PositiveZero; + // PositiveZero - NaN = NaN + SR[1, 0, 0, 1, 1, 1, 0] = NaN; + // PositiveZero - Finite = null + SR[1, 0, 0, 1, 1, 1, 1] = null; + // PositiveZero - PositiveInfinity = NegativeInfinity + SR[1, 0, 0, 1, 1, 1, 2] = NegativeInfinity; + // PositiveZero * NegativeZero = NegativeZero + SR[1, 0, 0, 2, 0, 0, 0] = NegativeZero; + // PositiveZero * NegativeZero = NegativeZero + SR[1, 0, 0, 2, 0, 0, 1] = NegativeZero; + // PositiveZero * NegativeZero = NegativeZero + SR[1, 0, 0, 2, 0, 0, 2] = NegativeZero; + // PositiveZero * NaN = NaN + SR[1, 0, 0, 2, 0, 1, 0] = NaN; + // PositiveZero * Finite = NegativeZero + SR[1, 0, 0, 2, 0, 1, 1] = NegativeZero; + // PositiveZero * NegativeInfinity = NaN + SR[1, 0, 0, 2, 0, 1, 2] = NaN; + // PositiveZero * PositiveZero = PositiveZero + SR[1, 0, 0, 2, 1, 0, 0] = PositiveZero; + // PositiveZero * PositiveZero = PositiveZero + SR[1, 0, 0, 2, 1, 0, 1] = PositiveZero; + // PositiveZero * PositiveZero = PositiveZero + SR[1, 0, 0, 2, 1, 0, 2] = PositiveZero; + // PositiveZero * NaN = NaN + SR[1, 0, 0, 2, 1, 1, 0] = NaN; + // PositiveZero * Finite = PositiveZero + SR[1, 0, 0, 2, 1, 1, 1] = PositiveZero; + // PositiveZero * PositiveInfinity = NaN + SR[1, 0, 0, 2, 1, 1, 2] = NaN; + // PositiveZero / NegativeZero = NaN + SR[1, 0, 0, 3, 0, 0, 0] = NaN; + // PositiveZero / NegativeZero = NaN + SR[1, 0, 0, 3, 0, 0, 1] = NaN; + // PositiveZero / NegativeZero = NaN + SR[1, 0, 0, 3, 0, 0, 2] = NaN; + // PositiveZero / NaN = NaN + SR[1, 0, 0, 3, 0, 1, 0] = NaN; + // PositiveZero / Finite = NegativeZero + SR[1, 0, 0, 3, 0, 1, 1] = NegativeZero; + // PositiveZero / NegativeInfinity = NegativeZero + SR[1, 0, 0, 3, 0, 1, 2] = NegativeZero; + // PositiveZero / PositiveZero = NaN + SR[1, 0, 0, 3, 1, 0, 0] = NaN; + // PositiveZero / PositiveZero = NaN + SR[1, 0, 0, 3, 1, 0, 1] = NaN; + // PositiveZero / PositiveZero = NaN + SR[1, 0, 0, 3, 1, 0, 2] = NaN; + // PositiveZero / NaN = NaN + SR[1, 0, 0, 3, 1, 1, 0] = NaN; + // PositiveZero / Finite = PositiveZero + SR[1, 0, 0, 3, 1, 1, 1] = PositiveZero; + // PositiveZero / PositiveInfinity = PositiveZero + SR[1, 0, 0, 3, 1, 1, 2] = PositiveZero; + // PositiveZero + NegativeZero = PositiveZero + SR[1, 0, 1, 0, 0, 0, 0] = PositiveZero; + // PositiveZero + NegativeZero = PositiveZero + SR[1, 0, 1, 0, 0, 0, 1] = PositiveZero; + // PositiveZero + NegativeZero = PositiveZero + SR[1, 0, 1, 0, 0, 0, 2] = PositiveZero; + // PositiveZero + NaN = NaN + SR[1, 0, 1, 0, 0, 1, 0] = NaN; + // PositiveZero + Finite = null + SR[1, 0, 1, 0, 0, 1, 1] = null; + // PositiveZero + NegativeInfinity = NegativeInfinity + SR[1, 0, 1, 0, 0, 1, 2] = NegativeInfinity; + // PositiveZero + PositiveZero = PositiveZero + SR[1, 0, 1, 0, 1, 0, 0] = PositiveZero; + // PositiveZero + PositiveZero = PositiveZero + SR[1, 0, 1, 0, 1, 0, 1] = PositiveZero; + // PositiveZero + PositiveZero = PositiveZero + SR[1, 0, 1, 0, 1, 0, 2] = PositiveZero; + // PositiveZero + NaN = NaN + SR[1, 0, 1, 0, 1, 1, 0] = NaN; + // PositiveZero + Finite = null + SR[1, 0, 1, 0, 1, 1, 1] = null; + // PositiveZero + PositiveInfinity = PositiveInfinity + SR[1, 0, 1, 0, 1, 1, 2] = PositiveInfinity; + // PositiveZero - NegativeZero = PositiveZero + SR[1, 0, 1, 1, 0, 0, 0] = PositiveZero; + // PositiveZero - NegativeZero = PositiveZero + SR[1, 0, 1, 1, 0, 0, 1] = PositiveZero; + // PositiveZero - NegativeZero = PositiveZero + SR[1, 0, 1, 1, 0, 0, 2] = PositiveZero; + // PositiveZero - NaN = NaN + SR[1, 0, 1, 1, 0, 1, 0] = NaN; + // PositiveZero - Finite = null + SR[1, 0, 1, 1, 0, 1, 1] = null; + // PositiveZero - NegativeInfinity = PositiveInfinity + SR[1, 0, 1, 1, 0, 1, 2] = PositiveInfinity; + // PositiveZero - PositiveZero = PositiveZero + SR[1, 0, 1, 1, 1, 0, 0] = PositiveZero; + // PositiveZero - PositiveZero = PositiveZero + SR[1, 0, 1, 1, 1, 0, 1] = PositiveZero; + // PositiveZero - PositiveZero = PositiveZero + SR[1, 0, 1, 1, 1, 0, 2] = PositiveZero; + // PositiveZero - NaN = NaN + SR[1, 0, 1, 1, 1, 1, 0] = NaN; + // PositiveZero - Finite = null + SR[1, 0, 1, 1, 1, 1, 1] = null; + // PositiveZero - PositiveInfinity = NegativeInfinity + SR[1, 0, 1, 1, 1, 1, 2] = NegativeInfinity; + // PositiveZero * NegativeZero = NegativeZero + SR[1, 0, 1, 2, 0, 0, 0] = NegativeZero; + // PositiveZero * NegativeZero = NegativeZero + SR[1, 0, 1, 2, 0, 0, 1] = NegativeZero; + // PositiveZero * NegativeZero = NegativeZero + SR[1, 0, 1, 2, 0, 0, 2] = NegativeZero; + // PositiveZero * NaN = NaN + SR[1, 0, 1, 2, 0, 1, 0] = NaN; + // PositiveZero * Finite = NegativeZero + SR[1, 0, 1, 2, 0, 1, 1] = NegativeZero; + // PositiveZero * NegativeInfinity = NaN + SR[1, 0, 1, 2, 0, 1, 2] = NaN; + // PositiveZero * PositiveZero = PositiveZero + SR[1, 0, 1, 2, 1, 0, 0] = PositiveZero; + // PositiveZero * PositiveZero = PositiveZero + SR[1, 0, 1, 2, 1, 0, 1] = PositiveZero; + // PositiveZero * PositiveZero = PositiveZero + SR[1, 0, 1, 2, 1, 0, 2] = PositiveZero; + // PositiveZero * NaN = NaN + SR[1, 0, 1, 2, 1, 1, 0] = NaN; + // PositiveZero * Finite = PositiveZero + SR[1, 0, 1, 2, 1, 1, 1] = PositiveZero; + // PositiveZero * PositiveInfinity = NaN + SR[1, 0, 1, 2, 1, 1, 2] = NaN; + // PositiveZero / NegativeZero = NaN + SR[1, 0, 1, 3, 0, 0, 0] = NaN; + // PositiveZero / NegativeZero = NaN + SR[1, 0, 1, 3, 0, 0, 1] = NaN; + // PositiveZero / NegativeZero = NaN + SR[1, 0, 1, 3, 0, 0, 2] = NaN; + // PositiveZero / NaN = NaN + SR[1, 0, 1, 3, 0, 1, 0] = NaN; + // PositiveZero / Finite = NegativeZero + SR[1, 0, 1, 3, 0, 1, 1] = NegativeZero; + // PositiveZero / NegativeInfinity = NegativeZero + SR[1, 0, 1, 3, 0, 1, 2] = NegativeZero; + // PositiveZero / PositiveZero = NaN + SR[1, 0, 1, 3, 1, 0, 0] = NaN; + // PositiveZero / PositiveZero = NaN + SR[1, 0, 1, 3, 1, 0, 1] = NaN; + // PositiveZero / PositiveZero = NaN + SR[1, 0, 1, 3, 1, 0, 2] = NaN; + // PositiveZero / NaN = NaN + SR[1, 0, 1, 3, 1, 1, 0] = NaN; + // PositiveZero / Finite = PositiveZero + SR[1, 0, 1, 3, 1, 1, 1] = PositiveZero; + // PositiveZero / PositiveInfinity = PositiveZero + SR[1, 0, 1, 3, 1, 1, 2] = PositiveZero; + // PositiveZero + NegativeZero = PositiveZero + SR[1, 0, 2, 0, 0, 0, 0] = PositiveZero; + // PositiveZero + NegativeZero = PositiveZero + SR[1, 0, 2, 0, 0, 0, 1] = PositiveZero; + // PositiveZero + NegativeZero = PositiveZero + SR[1, 0, 2, 0, 0, 0, 2] = PositiveZero; + // PositiveZero + NaN = NaN + SR[1, 0, 2, 0, 0, 1, 0] = NaN; + // PositiveZero + Finite = null + SR[1, 0, 2, 0, 0, 1, 1] = null; + // PositiveZero + NegativeInfinity = NegativeInfinity + SR[1, 0, 2, 0, 0, 1, 2] = NegativeInfinity; + // PositiveZero + PositiveZero = PositiveZero + SR[1, 0, 2, 0, 1, 0, 0] = PositiveZero; + // PositiveZero + PositiveZero = PositiveZero + SR[1, 0, 2, 0, 1, 0, 1] = PositiveZero; + // PositiveZero + PositiveZero = PositiveZero + SR[1, 0, 2, 0, 1, 0, 2] = PositiveZero; + // PositiveZero + NaN = NaN + SR[1, 0, 2, 0, 1, 1, 0] = NaN; + // PositiveZero + Finite = null + SR[1, 0, 2, 0, 1, 1, 1] = null; + // PositiveZero + PositiveInfinity = PositiveInfinity + SR[1, 0, 2, 0, 1, 1, 2] = PositiveInfinity; + // PositiveZero - NegativeZero = PositiveZero + SR[1, 0, 2, 1, 0, 0, 0] = PositiveZero; + // PositiveZero - NegativeZero = PositiveZero + SR[1, 0, 2, 1, 0, 0, 1] = PositiveZero; + // PositiveZero - NegativeZero = PositiveZero + SR[1, 0, 2, 1, 0, 0, 2] = PositiveZero; + // PositiveZero - NaN = NaN + SR[1, 0, 2, 1, 0, 1, 0] = NaN; + // PositiveZero - Finite = null + SR[1, 0, 2, 1, 0, 1, 1] = null; + // PositiveZero - NegativeInfinity = PositiveInfinity + SR[1, 0, 2, 1, 0, 1, 2] = PositiveInfinity; + // PositiveZero - PositiveZero = PositiveZero + SR[1, 0, 2, 1, 1, 0, 0] = PositiveZero; + // PositiveZero - PositiveZero = PositiveZero + SR[1, 0, 2, 1, 1, 0, 1] = PositiveZero; + // PositiveZero - PositiveZero = PositiveZero + SR[1, 0, 2, 1, 1, 0, 2] = PositiveZero; + // PositiveZero - NaN = NaN + SR[1, 0, 2, 1, 1, 1, 0] = NaN; + // PositiveZero - Finite = null + SR[1, 0, 2, 1, 1, 1, 1] = null; + // PositiveZero - PositiveInfinity = NegativeInfinity + SR[1, 0, 2, 1, 1, 1, 2] = NegativeInfinity; + // PositiveZero * NegativeZero = NegativeZero + SR[1, 0, 2, 2, 0, 0, 0] = NegativeZero; + // PositiveZero * NegativeZero = NegativeZero + SR[1, 0, 2, 2, 0, 0, 1] = NegativeZero; + // PositiveZero * NegativeZero = NegativeZero + SR[1, 0, 2, 2, 0, 0, 2] = NegativeZero; + // PositiveZero * NaN = NaN + SR[1, 0, 2, 2, 0, 1, 0] = NaN; + // PositiveZero * Finite = NegativeZero + SR[1, 0, 2, 2, 0, 1, 1] = NegativeZero; + // PositiveZero * NegativeInfinity = NaN + SR[1, 0, 2, 2, 0, 1, 2] = NaN; + // PositiveZero * PositiveZero = PositiveZero + SR[1, 0, 2, 2, 1, 0, 0] = PositiveZero; + // PositiveZero * PositiveZero = PositiveZero + SR[1, 0, 2, 2, 1, 0, 1] = PositiveZero; + // PositiveZero * PositiveZero = PositiveZero + SR[1, 0, 2, 2, 1, 0, 2] = PositiveZero; + // PositiveZero * NaN = NaN + SR[1, 0, 2, 2, 1, 1, 0] = NaN; + // PositiveZero * Finite = PositiveZero + SR[1, 0, 2, 2, 1, 1, 1] = PositiveZero; + // PositiveZero * PositiveInfinity = NaN + SR[1, 0, 2, 2, 1, 1, 2] = NaN; + // PositiveZero / NegativeZero = NaN + SR[1, 0, 2, 3, 0, 0, 0] = NaN; + // PositiveZero / NegativeZero = NaN + SR[1, 0, 2, 3, 0, 0, 1] = NaN; + // PositiveZero / NegativeZero = NaN + SR[1, 0, 2, 3, 0, 0, 2] = NaN; + // PositiveZero / NaN = NaN + SR[1, 0, 2, 3, 0, 1, 0] = NaN; + // PositiveZero / Finite = NegativeZero + SR[1, 0, 2, 3, 0, 1, 1] = NegativeZero; + // PositiveZero / NegativeInfinity = NegativeZero + SR[1, 0, 2, 3, 0, 1, 2] = NegativeZero; + // PositiveZero / PositiveZero = NaN + SR[1, 0, 2, 3, 1, 0, 0] = NaN; + // PositiveZero / PositiveZero = NaN + SR[1, 0, 2, 3, 1, 0, 1] = NaN; + // PositiveZero / PositiveZero = NaN + SR[1, 0, 2, 3, 1, 0, 2] = NaN; + // PositiveZero / NaN = NaN + SR[1, 0, 2, 3, 1, 1, 0] = NaN; + // PositiveZero / Finite = PositiveZero + SR[1, 0, 2, 3, 1, 1, 1] = PositiveZero; + // PositiveZero / PositiveInfinity = PositiveZero + SR[1, 0, 2, 3, 1, 1, 2] = PositiveZero; + // NaN + NegativeZero = NaN + SR[1, 1, 0, 0, 0, 0, 0] = NaN; + // NaN + NegativeZero = NaN + SR[1, 1, 0, 0, 0, 0, 1] = NaN; + // NaN + NegativeZero = NaN + SR[1, 1, 0, 0, 0, 0, 2] = NaN; + // NaN + NaN = NaN + SR[1, 1, 0, 0, 0, 1, 0] = NaN; + // NaN + Finite = NaN + SR[1, 1, 0, 0, 0, 1, 1] = NaN; + // NaN + NegativeInfinity = NaN + SR[1, 1, 0, 0, 0, 1, 2] = NaN; + // NaN + PositiveZero = NaN + SR[1, 1, 0, 0, 1, 0, 0] = NaN; + // NaN + PositiveZero = NaN + SR[1, 1, 0, 0, 1, 0, 1] = NaN; + // NaN + PositiveZero = NaN + SR[1, 1, 0, 0, 1, 0, 2] = NaN; + // NaN + NaN = NaN + SR[1, 1, 0, 0, 1, 1, 0] = NaN; + // NaN + Finite = NaN + SR[1, 1, 0, 0, 1, 1, 1] = NaN; + // NaN + PositiveInfinity = NaN + SR[1, 1, 0, 0, 1, 1, 2] = NaN; + // NaN - NegativeZero = NaN + SR[1, 1, 0, 1, 0, 0, 0] = NaN; + // NaN - NegativeZero = NaN + SR[1, 1, 0, 1, 0, 0, 1] = NaN; + // NaN - NegativeZero = NaN + SR[1, 1, 0, 1, 0, 0, 2] = NaN; + // NaN - NaN = NaN + SR[1, 1, 0, 1, 0, 1, 0] = NaN; + // NaN - Finite = NaN + SR[1, 1, 0, 1, 0, 1, 1] = NaN; + // NaN - NegativeInfinity = NaN + SR[1, 1, 0, 1, 0, 1, 2] = NaN; + // NaN - PositiveZero = NaN + SR[1, 1, 0, 1, 1, 0, 0] = NaN; + // NaN - PositiveZero = NaN + SR[1, 1, 0, 1, 1, 0, 1] = NaN; + // NaN - PositiveZero = NaN + SR[1, 1, 0, 1, 1, 0, 2] = NaN; + // NaN - NaN = NaN + SR[1, 1, 0, 1, 1, 1, 0] = NaN; + // NaN - Finite = NaN + SR[1, 1, 0, 1, 1, 1, 1] = NaN; + // NaN - PositiveInfinity = NaN + SR[1, 1, 0, 1, 1, 1, 2] = NaN; + // NaN * NegativeZero = NaN + SR[1, 1, 0, 2, 0, 0, 0] = NaN; + // NaN * NegativeZero = NaN + SR[1, 1, 0, 2, 0, 0, 1] = NaN; + // NaN * NegativeZero = NaN + SR[1, 1, 0, 2, 0, 0, 2] = NaN; + // NaN * NaN = NaN + SR[1, 1, 0, 2, 0, 1, 0] = NaN; + // NaN * Finite = NaN + SR[1, 1, 0, 2, 0, 1, 1] = NaN; + // NaN * NegativeInfinity = NaN + SR[1, 1, 0, 2, 0, 1, 2] = NaN; + // NaN * PositiveZero = NaN + SR[1, 1, 0, 2, 1, 0, 0] = NaN; + // NaN * PositiveZero = NaN + SR[1, 1, 0, 2, 1, 0, 1] = NaN; + // NaN * PositiveZero = NaN + SR[1, 1, 0, 2, 1, 0, 2] = NaN; + // NaN * NaN = NaN + SR[1, 1, 0, 2, 1, 1, 0] = NaN; + // NaN * Finite = NaN + SR[1, 1, 0, 2, 1, 1, 1] = NaN; + // NaN * PositiveInfinity = NaN + SR[1, 1, 0, 2, 1, 1, 2] = NaN; + // NaN / NegativeZero = NaN + SR[1, 1, 0, 3, 0, 0, 0] = NaN; + // NaN / NegativeZero = NaN + SR[1, 1, 0, 3, 0, 0, 1] = NaN; + // NaN / NegativeZero = NaN + SR[1, 1, 0, 3, 0, 0, 2] = NaN; + // NaN / NaN = NaN + SR[1, 1, 0, 3, 0, 1, 0] = NaN; + // NaN / Finite = NaN + SR[1, 1, 0, 3, 0, 1, 1] = NaN; + // NaN / NegativeInfinity = NaN + SR[1, 1, 0, 3, 0, 1, 2] = NaN; + // NaN / PositiveZero = NaN + SR[1, 1, 0, 3, 1, 0, 0] = NaN; + // NaN / PositiveZero = NaN + SR[1, 1, 0, 3, 1, 0, 1] = NaN; + // NaN / PositiveZero = NaN + SR[1, 1, 0, 3, 1, 0, 2] = NaN; + // NaN / NaN = NaN + SR[1, 1, 0, 3, 1, 1, 0] = NaN; + // NaN / Finite = NaN + SR[1, 1, 0, 3, 1, 1, 1] = NaN; + // NaN / PositiveInfinity = NaN + SR[1, 1, 0, 3, 1, 1, 2] = NaN; + // Finite + NegativeZero = null + SR[1, 1, 1, 0, 0, 0, 0] = null; + // Finite + NegativeZero = null + SR[1, 1, 1, 0, 0, 0, 1] = null; + // Finite + NegativeZero = null + SR[1, 1, 1, 0, 0, 0, 2] = null; + // Finite + NaN = NaN + SR[1, 1, 1, 0, 0, 1, 0] = NaN; + // Finite + Finite = null + SR[1, 1, 1, 0, 0, 1, 1] = null; + // Finite + NegativeInfinity = NegativeInfinity + SR[1, 1, 1, 0, 0, 1, 2] = NegativeInfinity; + // Finite + PositiveZero = null + SR[1, 1, 1, 0, 1, 0, 0] = null; + // Finite + PositiveZero = null + SR[1, 1, 1, 0, 1, 0, 1] = null; + // Finite + PositiveZero = null + SR[1, 1, 1, 0, 1, 0, 2] = null; + // Finite + NaN = NaN + SR[1, 1, 1, 0, 1, 1, 0] = NaN; + // Finite + Finite = null + SR[1, 1, 1, 0, 1, 1, 1] = null; + // Finite + PositiveInfinity = PositiveInfinity + SR[1, 1, 1, 0, 1, 1, 2] = PositiveInfinity; + // Finite - NegativeZero = null + SR[1, 1, 1, 1, 0, 0, 0] = null; + // Finite - NegativeZero = null + SR[1, 1, 1, 1, 0, 0, 1] = null; + // Finite - NegativeZero = null + SR[1, 1, 1, 1, 0, 0, 2] = null; + // Finite - NaN = NaN + SR[1, 1, 1, 1, 0, 1, 0] = NaN; + // Finite - Finite = null + SR[1, 1, 1, 1, 0, 1, 1] = null; + // Finite - NegativeInfinity = PositiveInfinity + SR[1, 1, 1, 1, 0, 1, 2] = PositiveInfinity; + // Finite - PositiveZero = null + SR[1, 1, 1, 1, 1, 0, 0] = null; + // Finite - PositiveZero = null + SR[1, 1, 1, 1, 1, 0, 1] = null; + // Finite - PositiveZero = null + SR[1, 1, 1, 1, 1, 0, 2] = null; + // Finite - NaN = NaN + SR[1, 1, 1, 1, 1, 1, 0] = NaN; + // Finite - Finite = null + SR[1, 1, 1, 1, 1, 1, 1] = null; + // Finite - PositiveInfinity = NegativeInfinity + SR[1, 1, 1, 1, 1, 1, 2] = NegativeInfinity; + // Finite * NegativeZero = NegativeZero + SR[1, 1, 1, 2, 0, 0, 0] = NegativeZero; + // Finite * NegativeZero = NegativeZero + SR[1, 1, 1, 2, 0, 0, 1] = NegativeZero; + // Finite * NegativeZero = NegativeZero + SR[1, 1, 1, 2, 0, 0, 2] = NegativeZero; + // Finite * NaN = NaN + SR[1, 1, 1, 2, 0, 1, 0] = NaN; + // Finite * Finite = null + SR[1, 1, 1, 2, 0, 1, 1] = null; + // Finite * NegativeInfinity = NegativeInfinity + SR[1, 1, 1, 2, 0, 1, 2] = NegativeInfinity; + // Finite * PositiveZero = PositiveZero + SR[1, 1, 1, 2, 1, 0, 0] = PositiveZero; + // Finite * PositiveZero = PositiveZero + SR[1, 1, 1, 2, 1, 0, 1] = PositiveZero; + // Finite * PositiveZero = PositiveZero + SR[1, 1, 1, 2, 1, 0, 2] = PositiveZero; + // Finite * NaN = NaN + SR[1, 1, 1, 2, 1, 1, 0] = NaN; + // Finite * Finite = null + SR[1, 1, 1, 2, 1, 1, 1] = null; + // Finite * PositiveInfinity = PositiveInfinity + SR[1, 1, 1, 2, 1, 1, 2] = PositiveInfinity; + // Finite / NegativeZero = NegativeInfinity + SR[1, 1, 1, 3, 0, 0, 0] = NegativeInfinity; + // Finite / NegativeZero = NegativeInfinity + SR[1, 1, 1, 3, 0, 0, 1] = NegativeInfinity; + // Finite / NegativeZero = NegativeInfinity + SR[1, 1, 1, 3, 0, 0, 2] = NegativeInfinity; + // Finite / NaN = NaN + SR[1, 1, 1, 3, 0, 1, 0] = NaN; + // Finite / Finite = null + SR[1, 1, 1, 3, 0, 1, 1] = null; + // Finite / NegativeInfinity = NegativeZero + SR[1, 1, 1, 3, 0, 1, 2] = NegativeZero; + // Finite / PositiveZero = PositiveInfinity + SR[1, 1, 1, 3, 1, 0, 0] = PositiveInfinity; + // Finite / PositiveZero = PositiveInfinity + SR[1, 1, 1, 3, 1, 0, 1] = PositiveInfinity; + // Finite / PositiveZero = PositiveInfinity + SR[1, 1, 1, 3, 1, 0, 2] = PositiveInfinity; + // Finite / NaN = NaN + SR[1, 1, 1, 3, 1, 1, 0] = NaN; + // Finite / Finite = null + SR[1, 1, 1, 3, 1, 1, 1] = null; + // Finite / PositiveInfinity = PositiveZero + SR[1, 1, 1, 3, 1, 1, 2] = PositiveZero; + // PositiveInfinity + NegativeZero = PositiveInfinity + SR[1, 1, 2, 0, 0, 0, 0] = PositiveInfinity; + // PositiveInfinity + NegativeZero = PositiveInfinity + SR[1, 1, 2, 0, 0, 0, 1] = PositiveInfinity; + // PositiveInfinity + NegativeZero = PositiveInfinity + SR[1, 1, 2, 0, 0, 0, 2] = PositiveInfinity; + // PositiveInfinity + NaN = NaN + SR[1, 1, 2, 0, 0, 1, 0] = NaN; + // PositiveInfinity + Finite = PositiveInfinity + SR[1, 1, 2, 0, 0, 1, 1] = PositiveInfinity; + // PositiveInfinity + NegativeInfinity = NaN + SR[1, 1, 2, 0, 0, 1, 2] = NaN; + // PositiveInfinity + PositiveZero = PositiveInfinity + SR[1, 1, 2, 0, 1, 0, 0] = PositiveInfinity; + // PositiveInfinity + PositiveZero = PositiveInfinity + SR[1, 1, 2, 0, 1, 0, 1] = PositiveInfinity; + // PositiveInfinity + PositiveZero = PositiveInfinity + SR[1, 1, 2, 0, 1, 0, 2] = PositiveInfinity; + // PositiveInfinity + NaN = NaN + SR[1, 1, 2, 0, 1, 1, 0] = NaN; + // PositiveInfinity + Finite = PositiveInfinity + SR[1, 1, 2, 0, 1, 1, 1] = PositiveInfinity; + // PositiveInfinity + PositiveInfinity = PositiveInfinity + SR[1, 1, 2, 0, 1, 1, 2] = PositiveInfinity; + // PositiveInfinity - NegativeZero = PositiveInfinity + SR[1, 1, 2, 1, 0, 0, 0] = PositiveInfinity; + // PositiveInfinity - NegativeZero = PositiveInfinity + SR[1, 1, 2, 1, 0, 0, 1] = PositiveInfinity; + // PositiveInfinity - NegativeZero = PositiveInfinity + SR[1, 1, 2, 1, 0, 0, 2] = PositiveInfinity; + // PositiveInfinity - NaN = NaN + SR[1, 1, 2, 1, 0, 1, 0] = NaN; + // PositiveInfinity - Finite = PositiveInfinity + SR[1, 1, 2, 1, 0, 1, 1] = PositiveInfinity; + // PositiveInfinity - NegativeInfinity = PositiveInfinity + SR[1, 1, 2, 1, 0, 1, 2] = PositiveInfinity; + // PositiveInfinity - PositiveZero = PositiveInfinity + SR[1, 1, 2, 1, 1, 0, 0] = PositiveInfinity; + // PositiveInfinity - PositiveZero = PositiveInfinity + SR[1, 1, 2, 1, 1, 0, 1] = PositiveInfinity; + // PositiveInfinity - PositiveZero = PositiveInfinity + SR[1, 1, 2, 1, 1, 0, 2] = PositiveInfinity; + // PositiveInfinity - NaN = NaN + SR[1, 1, 2, 1, 1, 1, 0] = NaN; + // PositiveInfinity - Finite = PositiveInfinity + SR[1, 1, 2, 1, 1, 1, 1] = PositiveInfinity; + // PositiveInfinity - PositiveInfinity = NaN + SR[1, 1, 2, 1, 1, 1, 2] = NaN; + // PositiveInfinity * NegativeZero = NaN + SR[1, 1, 2, 2, 0, 0, 0] = NaN; + // PositiveInfinity * NegativeZero = NaN + SR[1, 1, 2, 2, 0, 0, 1] = NaN; + // PositiveInfinity * NegativeZero = NaN + SR[1, 1, 2, 2, 0, 0, 2] = NaN; + // PositiveInfinity * NaN = NaN + SR[1, 1, 2, 2, 0, 1, 0] = NaN; + // PositiveInfinity * Finite = NegativeInfinity + SR[1, 1, 2, 2, 0, 1, 1] = NegativeInfinity; + // PositiveInfinity * NegativeInfinity = NegativeInfinity + SR[1, 1, 2, 2, 0, 1, 2] = NegativeInfinity; + // PositiveInfinity * PositiveZero = NaN + SR[1, 1, 2, 2, 1, 0, 0] = NaN; + // PositiveInfinity * PositiveZero = NaN + SR[1, 1, 2, 2, 1, 0, 1] = NaN; + // PositiveInfinity * PositiveZero = NaN + SR[1, 1, 2, 2, 1, 0, 2] = NaN; + // PositiveInfinity * NaN = NaN + SR[1, 1, 2, 2, 1, 1, 0] = NaN; + // PositiveInfinity * Finite = PositiveInfinity + SR[1, 1, 2, 2, 1, 1, 1] = PositiveInfinity; + // PositiveInfinity * PositiveInfinity = PositiveInfinity + SR[1, 1, 2, 2, 1, 1, 2] = PositiveInfinity; + // PositiveInfinity / NegativeZero = NegativeInfinity + SR[1, 1, 2, 3, 0, 0, 0] = NegativeInfinity; + // PositiveInfinity / NegativeZero = NegativeInfinity + SR[1, 1, 2, 3, 0, 0, 1] = NegativeInfinity; + // PositiveInfinity / NegativeZero = NegativeInfinity + SR[1, 1, 2, 3, 0, 0, 2] = NegativeInfinity; + // PositiveInfinity / NaN = NaN + SR[1, 1, 2, 3, 0, 1, 0] = NaN; + // PositiveInfinity / Finite = NegativeInfinity + SR[1, 1, 2, 3, 0, 1, 1] = NegativeInfinity; + // PositiveInfinity / NegativeInfinity = NaN + SR[1, 1, 2, 3, 0, 1, 2] = NaN; + // PositiveInfinity / PositiveZero = PositiveInfinity + SR[1, 1, 2, 3, 1, 0, 0] = PositiveInfinity; + // PositiveInfinity / PositiveZero = PositiveInfinity + SR[1, 1, 2, 3, 1, 0, 1] = PositiveInfinity; + // PositiveInfinity / PositiveZero = PositiveInfinity + SR[1, 1, 2, 3, 1, 0, 2] = PositiveInfinity; + // PositiveInfinity / NaN = NaN + SR[1, 1, 2, 3, 1, 1, 0] = NaN; + // PositiveInfinity / Finite = PositiveInfinity + SR[1, 1, 2, 3, 1, 1, 1] = PositiveInfinity; + // PositiveInfinity / PositiveInfinity = NaN + SR[1, 1, 2, 3, 1, 1, 2] = NaN; + } + #endregion + + #region ToString + public string GetFractionString() { + if (IsFinite(this)) { + return this._fraction.ToString(); + } else if (IsInfinite(this)) { + return InfinityString; + } else { + return NaNString; + } + } + public override string ToString() { + return ToString(0); + } + public string ToString(int separateAt) { + return ToString(separateAt, "", false); + } + public string ToString(int separateAt, string plusSign, bool floatStyleFormat) { + if (separateAt < 0) { + throw new ArgumentException("argument must be positive"); + } + + if (IsFinite(this) && !IsZero(this)) { + StringBuilder sb = new StringBuilder(); + if (IsNegative(this)) { + sb.Append("-"); + } else { + sb.Append(plusSign); + } + if (floatStyleFormat) { + AppendFloatStyle(sb, separateAt); + } else { + AppendEngineeringStyle(sb, separateAt); + } + return sb.ToString(); + } + if (IsPositiveZero(this)) { + return plusSign + ZeroString; + } + if (IsNegativeZero(this)) { + return NegativeZeroString; + } + if (IsPositiveInfinite(this)) { + return plusSign + InfinityString; + } + if (IsNegativeInfinite(this)) { + return NegativeInfinityString; + } + return NaNString; + } + + private void AppendFloatStyle(StringBuilder/*!*/ sb, int separateAt) { + if (_exponent <= 0) { + sb.Append("0."); + AppendDigits(sb, new string('0', -_exponent) + _fraction.ToString(), 0, Digits - _exponent, separateAt); + } else { + int minExpLen = Math.Min(_exponent, Digits); + int expLenDiff = _exponent - Digits; + if (expLenDiff >= 0) { + AppendDigits(sb, _fraction.ToString() + new string('0', expLenDiff), 0, _exponent, separateAt); + sb.Append(".0"); + } else { + AppendDigits(sb, _fraction.ToString(), 0, _exponent, separateAt); + sb.Append("."); + AppendDigits(sb, _fraction.ToString(), _exponent, Digits - _exponent, separateAt); + } + } + } + + private void AppendEngineeringStyle(StringBuilder/*!*/ sb, int separateAt) { + sb.Append("0."); + AppendDigits(sb, _fraction.ToString(), 0, Digits, separateAt); + sb.Append("E"); + sb.AppendFormat("{0}", this._exponent); + } + + private void AppendDigits(StringBuilder/*!*/ sb, string digits, int start, int length, int separateAt) { + int current = start; + if (separateAt > 0) { + while (current + separateAt < start + length) { + sb.Append(digits.Substring(current, separateAt)); + current += separateAt; + sb.Append(" "); + } + } + sb.Append(digits.Substring(current, length - (current - start))); + } + #endregion + + #region Comparison + #region IEquatable Members + + public bool Equals(BigDecimal other) { + return CompareTo(other) == 0; + } + + #endregion + + public int? CompareBigDecimal(BigDecimal other) { + int? result; + if (other == null) { + result = null; + } else if (!IsNonZeroFinite(this) || !IsNonZeroFinite(other)) { + result = CheckSpecialComparison(this, other); + } else { + if (this._sign != other._sign) { + // Different signs + result = this._sign > other._sign ? 1 : -1; + } else { + result = this._sign * (Fraction.Compare(this._fraction, other._fraction, this._exponent - other._exponent)); + } + } + return result; + } + + #region IComparable Members + public int CompareTo(BigDecimal other) { + int? result = CompareBigDecimal(other); + if (result.HasValue) { + return result.Value; + } else { + // Must have NaNs so we map them to Double.CompareTo results + if (IsNaN(this)) { + if (IsNaN(other)) { + // Both NaN so + return 0; + } else { + // this == NaN && other != NaN + return -1; + } + } else { + return 1; + } + } + } + #endregion + #endregion + + #region Hash Code + public int GetSignCode() { + switch (this.NumberType) { + case BigDecimal.NumberTypes.NaN: + return 0; + case BigDecimal.NumberTypes.Infinite: + return this.Sign * 3; + default: + case BigDecimal.NumberTypes.Finite: + if (BigDecimal.IsZero(this)) { + return this.Sign; + } else { + return this.Sign * 2; + } + } + } + public override int GetHashCode() { + int hash = this.GetSignCode(); + // Special values have special hash codes + if (hash == 2) { + int fHash = _fraction.GetHashCode(); + hash = (31 * hash + fHash) ^ fHash + Exponent; + } + return hash; + } + #endregion + + //#region Conversion + //public static implicit operator BigDecimal/*!*/(int i) { + // Config dummy = new Config(); + // return Create(dummy, i.ToString()); + //} + //public static implicit operator BigDecimal/*!*/(BigInteger/*!*/ i) { + // Config dummy = new Config(); + // return Create(dummy, i.ToString()); + //} + //public static implicit operator BigDecimal/*!*/(double d) { + // Config dummy = new Config(); + // return Create(dummy, d.ToString()); + //} + //#endregion + } + +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/BigDecimal/BigDecimalKernelOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/BigDecimal/BigDecimalKernelOps.cs new file mode 100644 index 0000000000..ac5c1a5463 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/BigDecimal/BigDecimalKernelOps.cs @@ -0,0 +1,32 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Runtime.InteropServices; +using IronRuby.Builtins; +using IronRuby.Runtime; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.StandardLibrary.BigDecimal { + [RubyModule(Extends = typeof(Kernel))] + public static class KernelOps { + [RubyMethod("BigDecimal", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("BigDecimal", RubyMethodAttributes.PublicSingleton)] + public static object CreateBigDecimal(RubyContext/*!*/ context, object/*!*/ self, [DefaultProtocol]MutableString value, [Optional]int n) { + return BigDecimal.Create(BigDecimalOps.GetConfig(context), value.ConvertToString(), n); + } + } + +} + diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/BigDecimal/BigDecimalOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/BigDecimal/BigDecimalOps.cs new file mode 100644 index 0000000000..4272559471 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/BigDecimal/BigDecimalOps.cs @@ -0,0 +1,1034 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.Scripting.Runtime; +using System.Text.RegularExpressions; +using IronRuby.Runtime; +using Microsoft.Scripting.Math; +using IronRuby.Builtins; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.StandardLibrary.BigDecimal { + [RubyClass("BigDecimal", Inherits = typeof(Numeric), Extends = typeof(BigDecimal))] + public sealed class BigDecimalOps { + + internal static readonly object BigDecimalOpsClassKey = new object(); + + #region Static Fields + + internal static BigDecimal.Config GetConfig(RubyContext/*!*/ context) { + ContractUtils.RequiresNotNull(context, "context"); + return (BigDecimal.Config)context.GetOrCreateLibraryData(BigDecimalOpsClassKey, () => new BigDecimal.Config()); + } + + #endregion + + #region Construction + + [RubyConstructor] + public static BigDecimal/*!*/ CreateBigDecimal(RubyContext/*!*/ context, RubyClass/*!*/ self, [DefaultProtocol]MutableString/*!*/ value, [Optional]int n) { + return BigDecimal.Create(GetConfig(context), value.ConvertToString(), n); + } + + #endregion + + #region Constants + + [RubyConstant] + public const uint BASE = BigDecimal.BASE; + [RubyConstant] + public const int EXCEPTION_ALL = (int)BigDecimal.OverflowExceptionModes.All; + [RubyConstant] + public const int EXCEPTION_INFINITY = (int)BigDecimal.OverflowExceptionModes.Infinity; + [RubyConstant] + public const int EXCEPTION_NaN = (int)BigDecimal.OverflowExceptionModes.NaN; + [RubyConstant] + public const int EXCEPTION_OVERFLOW = (int)BigDecimal.OverflowExceptionModes.Overflow; + [RubyConstant] + public const int EXCEPTION_UNDERFLOW = (int)BigDecimal.OverflowExceptionModes.Underflow; + [RubyConstant] + public const int EXCEPTION_ZERODIVIDE = (int)BigDecimal.OverflowExceptionModes.ZeroDivide; + [RubyConstant] + public const int ROUND_CEILING = (int)BigDecimal.RoundingModes.Ceiling; + [RubyConstant] + public const int ROUND_DOWN = (int)BigDecimal.RoundingModes.Down; + [RubyConstant] + public const int ROUND_FLOOR = (int)BigDecimal.RoundingModes.Floor; + [RubyConstant] + public const int ROUND_HALF_DOWN = (int)BigDecimal.RoundingModes.HalfDown; + [RubyConstant] + public const int ROUND_HALF_EVEN = (int)BigDecimal.RoundingModes.HalfEven; + [RubyConstant] + public const int ROUND_HALF_UP = (int)BigDecimal.RoundingModes.HalfUp; + [RubyConstant] + public const int ROUND_UP = (int)BigDecimal.RoundingModes.Up; + [RubyConstant] + public const int ROUND_MODE = 256; + [RubyConstant] + public const int SIGN_NEGATIVE_FINITE = -2; + [RubyConstant] + public const int SIGN_NEGATIVE_INFINITE = -3; + [RubyConstant] + public const int SIGN_NEGATIVE_ZERO = -1; + [RubyConstant] + public const int SIGN_NaN = 0; + [RubyConstant] + public const int SIGN_POSITIVE_FINITE = 2; + [RubyConstant] + public const int SIGN_POSITIVE_INFINITE = 3; + [RubyConstant] + public const int SIGN_POSITIVE_ZERO = 1; + #endregion + + #region Singleton Methods + + [RubyMethod("_load", RubyMethodAttributes.PublicSingleton)] + public static BigDecimal/*!*/ Load(RubyContext/*!*/ context, RubyClass/*!*/ self, [DefaultProtocol]MutableString/*!*/ str) { + try { + MutableString[] components = str.Split(new char[] { ':' }, 2, StringSplitOptions.None); + int maxDigits = 0; + int maxPrecision = 1; + string digits = ""; + + if (!(components[0] == null) || components[0].IsEmpty) { + maxDigits = int.Parse(components[0].ToString()); + } + if (maxDigits != 0) { + maxPrecision = maxDigits / BigDecimal.BASE_FIG + (maxDigits % BigDecimal.BASE_FIG == 0 ? 0 : 1); + } + if (components.Length == 2 && components[1] != null) { + digits = components[1].ToString(); + } + + return BigDecimal.Create(GetConfig(context), digits, maxPrecision); + } catch { + throw RubyExceptions.CreateTypeError("load failed: invalid character in the marshaled string."); + } + } + + [RubyMethod("double_fig", RubyMethodAttributes.PublicSingleton)] + public static int DoubleFig(RubyClass/*!*/ self) { + return 16; // This is the number of digits (in the mantissa?) that a Double can hold. + } + + [RubyMethod("mode", RubyMethodAttributes.PublicSingleton)] + public static int Mode(RubyContext/*!*/ context, RubyClass/*!*/ self, int mode) { + if (mode == ROUND_MODE) { + return (int)GetConfig(context).RoundingMode; + } else { + return (int)GetConfig(context).OverflowMode & mode; + } + } + + [RubyMethod("mode", RubyMethodAttributes.PublicSingleton)] + public static int Mode(RubyContext/*!*/ context, RubyClass/*!*/ self, int mode, object value) { + if (value == null) { + return Mode(context, self, mode); + } + if (mode == ROUND_MODE) { + if (value is int) { + GetConfig(context).RoundingMode = (BigDecimal.RoundingModes)value; + return (int)value; + } else { + throw RubyExceptions.CreateTypeError("wrong argument type " + value.ToString() + " (expected Fixnum)"); + } + } else { + if (value is bool) { + BigDecimal.Config config = GetConfig(context); + if (Enum.IsDefined(typeof(BigDecimal.OverflowExceptionModes), mode)) { + BigDecimal.OverflowExceptionModes enumMode = (BigDecimal.OverflowExceptionModes)mode; + if ((bool)value) { + config.OverflowMode = config.OverflowMode | enumMode; + } else { + config.OverflowMode = config.OverflowMode & (BigDecimal.OverflowExceptionModes.All ^ enumMode); + } + } + return (int)config.OverflowMode; + } else { + throw RubyExceptions.CreateTypeError("second argument must be true or false"); + } + } + } + + [RubyMethod("limit", RubyMethodAttributes.PublicSingleton)] + public static int Limit(RubyContext/*!*/ context, RubyClass/*!*/ self, int n) { + BigDecimal.Config config = GetConfig(context); + int limit = config.Limit; + config.Limit = n; + return limit; + } + + [RubyMethod("limit", RubyMethodAttributes.PublicSingleton)] + public static int Limit(RubyContext/*!*/ context, RubyClass/*!*/ self, [Optional]object n) { + if (!(n is Missing)) { + throw RubyExceptions.CreateTypeError("wrong argument type " + RubyUtils.GetClassName(self.Context, n) + " (expected Fixnum)"); + } + return GetConfig(context).Limit; + } + + [RubyMethod("ver", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ Version(RubyClass/*!*/ self) { + return MutableString.Create("1.0.1"); + } + + #region induced_from + + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static BigDecimal InducedFrom(RubyClass/*!*/ self, [NotNull]BigDecimal/*!*/ value) { + return value; + } + + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static BigDecimal InducedFrom(RubyContext/*!*/ context, RubyClass/*!*/ self, int value) { + return BigDecimal.Create(GetConfig(context), value.ToString()); + } + + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static BigDecimal InducedFrom(RubyContext/*!*/ context, RubyClass/*!*/ self, [NotNull]BigInteger/*!*/ value) { + return BigDecimal.Create(GetConfig(context), value.ToString()); + } + + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static BigDecimal InducedFrom(RubyClass/*!*/ self, object value) { + throw RubyExceptions.CreateTypeConversionError(RubyUtils.GetClassName(self.Context, value), self.Name); + } + + #endregion + + #endregion + + #region Instance Methods + + #region Properties + + [RubyMethod("sign")] + public static int Sign(BigDecimal/*!*/ self) { + return self.GetSignCode(); + } + + [RubyMethod("exponent")] + public static int Exponent(BigDecimal/*!*/ self) { + return self.Exponent; + } + + [RubyMethod("precs")] + public static RubyArray/*!*/ Precision(BigDecimal/*!*/ self) { + return RubyOps.MakeArray2(self.Precision * BigDecimal.BASE_FIG, self.MaxPrecision * BigDecimal.BASE_FIG); + } + + [RubyMethod("split")] + public static RubyArray/*!*/ Split(BigDecimal/*!*/ self) { + return RubyOps.MakeArray4(self.Sign, MutableString.Create(self.GetFractionString()), 10, self.Exponent); + } + + [RubyMethod("fix")] + public static BigDecimal/*!*/ Fix(RubyContext/*!*/ context, BigDecimal/*!*/ self) { + return BigDecimal.IntegerPart(GetConfig(context), self); + } + + [RubyMethod("frac")] + public static BigDecimal/*!*/ Fraction(RubyContext/*!*/ context, BigDecimal/*!*/ self) { + return BigDecimal.FractionalPart(GetConfig(context), self); + } + + #endregion + + #region Conversion Operations + + #region to_s + + [RubyMethod("to_s")] + public static MutableString/*!*/ ToString(BigDecimal/*!*/ self) { + return MutableString.Create(self.ToString()); + } + + [RubyMethod("to_s")] + public static MutableString/*!*/ ToString(BigDecimal/*!*/ self, [DefaultProtocol]int separateAt) { + return MutableString.Create(self.ToString(separateAt)); + } + + [RubyMethod("to_s")] + public static MutableString/*!*/ ToString(BigDecimal/*!*/ self, [DefaultProtocol][NotNull]MutableString/*!*/ format) { + string posSign = ""; + int separateAt = 0; + + Match m = Regex.Match(format.ConvertToString(), @"^(?[+ ])?(?\d+)?(?[fF])?", RegexOptions.ExplicitCapture); + Group posSignGroup = m.Groups["posSign"]; + Group separateAtGroup = m.Groups["separateAt"]; + Group floatFormatGroup = m.Groups["floatFormat"]; + + if (posSignGroup.Success) { + posSign = m.Groups["posSign"].Value; + } + if (separateAtGroup.Success) { + separateAt = Int32.Parse(m.Groups["separateAt"].Value); + } + bool floatFormat = floatFormatGroup.Success; + return MutableString.Create(self.ToString(separateAt, posSign, floatFormat)); + } + + #endregion + + [RubyMethod("inspect")] + public static MutableString/*!*/ Inspect(RubyContext/*!*/ context, BigDecimal/*!*/ self) { + MutableString str = MutableString.CreateMutable(); + str.AppendFormat("#<{0}:", context.GetClassOf(self).Name); + RubyUtils.AppendFormatHexObjectId(str, RubyUtils.GetObjectId(context, self)); + str.AppendFormat(",'{0}',", self.ToString(10)); + str.AppendFormat("{0}({1})>", self.PrecisionDigits.ToString(), self.MaxPrecisionDigits.ToString()); + return str; + } + + #region coerce + + [RubyMethod("coerce")] + public static RubyArray/*!*/ Coerce(BigDecimal/*!*/ self, BigDecimal/*!*/ other) { + return RubyOps.MakeArray2(other, self); + } + + [RubyMethod("coerce")] + public static RubyArray/*!*/ Coerce(RubyContext/*!*/ context, BigDecimal/*!*/ self, double other) { + return RubyOps.MakeArray2(other, ToFloat(context, self)); + } + + [RubyMethod("coerce")] + public static RubyArray/*!*/ Coerce(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + return RubyOps.MakeArray2(BigDecimal.Create(GetConfig(context), other.ToString()), self); + } + + [RubyMethod("coerce")] + public static RubyArray/*!*/ Coerce(RubyContext/*!*/ context, BigDecimal/*!*/ self, BigInteger/*!*/ other) { + return RubyOps.MakeArray2(BigDecimal.Create(GetConfig(context), other.ToString()), self); + } + + #endregion + + #region _dump + + [RubyMethod("_dump")] + public static MutableString/*!*/ Dump(BigDecimal/*!*/ self, [Optional]object limit) { + // We ignore the limit value as BigDecimal does not contain other objects. + return MutableString.CreateMutable( + string.Format("{0}:{1}", + self.MaxPrecisionDigits, + self)); + } + + #endregion + + [RubyMethod("to_f")] + public static double ToFloat(RubyContext/*!*/ context, BigDecimal/*!*/ self) { + return BigDecimal.ToFloat(GetConfig(context), self); + } + + [RubyMethod("to_i")] + [RubyMethod("to_int")] + public static object ToI(RubyContext/*!*/ context, BigDecimal/*!*/ self) { + return BigDecimal.ToInteger(GetConfig(context), self); + } + + [RubyMethod("hash")] + public static int Hash(BigDecimal/*!*/ self) { + return self.GetHashCode(); + } + + #endregion + + #region Arithmetic Operations + + #region add, + + + [RubyMethod("+")] + [RubyMethod("add")] + public static BigDecimal/*!*/ Add(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigDecimal/*!*/ other) { + return BigDecimal.Add(GetConfig(context), self, other); + } + + [RubyMethod("+")] + [RubyMethod("add")] + public static BigDecimal/*!*/ Add(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Add(config, self, BigDecimal.Create(config, other)); + } + + [RubyMethod("+")] + [RubyMethod("add")] + public static BigDecimal/*!*/ Add(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Add(config, self, BigDecimal.Create(config, other)); + } + + [RubyMethod("+")] + [RubyMethod("add")] + public static object Add(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Add); + } + + [RubyMethod("add")] + public static BigDecimal/*!*/ Add(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigDecimal/*!*/ other, int n) { + return BigDecimal.Add(GetConfig(context), self, other, n); + } + + [RubyMethod("add")] + public static BigDecimal/*!*/ Add(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other, int n) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Add(config, self, BigDecimal.Create(config, other), n); + } + + [RubyMethod("add")] + public static BigDecimal/*!*/ Add(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other, int n) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Add(config, self, BigDecimal.Create(config, other), n); + } + + [RubyMethod("add")] + public static BigDecimal/*!*/ Add(RubyContext/*!*/ context, BigDecimal/*!*/ self, double other, int n) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Add(config, self, BigDecimal.Create(config, other), n); + } + + [RubyMethod("add")] + public static object Add(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other, [DefaultProtocol]int n) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Add); + } + + #endregion + + #region sub, - + + [RubyMethod("-")] + [RubyMethod("sub")] + public static BigDecimal/*!*/ Subtract(RubyContext/*!*/ context, BigDecimal/*!*/ self, BigDecimal/*!*/ other) { + return BigDecimal.Subtract(GetConfig(context), self, other); + } + + [RubyMethod("-")] + [RubyMethod("sub")] + public static BigDecimal/*!*/ Subtract(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Subtract(config, self, BigDecimal.Create(config, other)); + } + + [RubyMethod("-")] + [RubyMethod("sub")] + public static BigDecimal/*!*/ Subtract(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Subtract(config, self, BigDecimal.Create(config, other)); + } + + [RubyMethod("-")] + [RubyMethod("sub")] + public static object Subtract(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Subtract); + } + + [RubyMethod("sub")] + public static BigDecimal/*!*/ Subtract(RubyContext/*!*/ context, BigDecimal/*!*/ self, BigDecimal/*!*/ other, int n) { + return BigDecimal.Subtract(GetConfig(context), self, other, n); + } + + [RubyMethod("sub")] + public static BigDecimal/*!*/ Subtract(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other, int n) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Subtract(config, self, BigDecimal.Create(config, other), n); + } + + [RubyMethod("sub")] + public static BigDecimal/*!*/ Subtract(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other, int n) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Subtract(config, self, BigDecimal.Create(config, other), n); + } + + [RubyMethod("sub")] + public static BigDecimal/*!*/ Subtract(RubyContext/*!*/ context, BigDecimal/*!*/ self, double other, int n) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Subtract(config, self, BigDecimal.Create(config, other), n); + } + + [RubyMethod("sub")] + public static object Subtract(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other, [DefaultProtocol]int n) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Subtract); + } + + #endregion + + #region mult, * + + [RubyMethod("*")] + [RubyMethod("mult")] + public static BigDecimal/*!*/ Multiply(RubyContext/*!*/ context, BigDecimal/*!*/ self, BigDecimal/*!*/ other) { + return BigDecimal.Multiply(GetConfig(context), self, other); + } + + [RubyMethod("*")] + [RubyMethod("mult")] + public static BigDecimal/*!*/ Multiply(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Multiply(config, self, BigDecimal.Create(config, other)); + } + + [RubyMethod("*")] + [RubyMethod("mult")] + public static BigDecimal/*!*/ Multiply(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Multiply(config, self, BigDecimal.Create(config, other)); + } + + [RubyMethod("*")] + [RubyMethod("mult")] + public static object Multiply(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Multiply); + } + + [RubyMethod("mult")] + public static BigDecimal/*!*/ Multiply(RubyContext/*!*/ context, BigDecimal/*!*/ self, BigDecimal/*!*/ other, int n) { + return BigDecimal.Multiply(GetConfig(context), self, other, n); + } + + [RubyMethod("mult")] + public static BigDecimal/*!*/ Multiply(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other, int n) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Multiply(config, self, BigDecimal.Create(config, other), n); + } + + [RubyMethod("mult")] + public static BigDecimal/*!*/ Multiply(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other, int n) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Multiply(config, self, BigDecimal.Create(config, other), n); + } + + [RubyMethod("mult")] + public static BigDecimal/*!*/ Multiply(RubyContext/*!*/ context, BigDecimal/*!*/ self, double other, int n) { + BigDecimal.Config config = GetConfig(context); + return BigDecimal.Multiply(config, self, BigDecimal.Create(config, other), n); + } + + [RubyMethod("mult")] + public static object Multiply(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other, [DefaultProtocol]int n) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Multiply); + } + + #endregion + + [RubyMethod("/")] + [RubyMethod("quo")] + public static BigDecimal/*!*/ Divide(RubyContext/*!*/ context, BigDecimal/*!*/ self, BigDecimal/*!*/ other) { + BigDecimal remainder; + return BigDecimal.Divide(GetConfig(context), self, other, 0, out remainder); + } + + [RubyMethod("div")] + public static BigDecimal/*!*/ Div(RubyContext/*!*/ context, BigDecimal/*!*/ self, BigDecimal/*!*/ other) { + if (BigDecimal.IsFinite(other)) { + BigDecimal.Config config = GetConfig(context); + BigDecimal remainder; + BigDecimal result = BigDecimal.Divide(config, self, other, 0, out remainder); + if (BigDecimal.IsFinite(result)) { + return BigDecimal.IntegerPart(config, result); + } + } + return BigDecimal.NaN; + } + + [RubyMethod("div")] + public static BigDecimal/*!*/ Div(RubyContext/*!*/ context, BigDecimal/*!*/ self, BigDecimal/*!*/ other, int n) { + if (n < 0) { + throw RubyExceptions.CreateArgumentError("argument must be positive"); + } + BigDecimal remainder; + return BigDecimal.Divide(GetConfig(context), self, other, n, out remainder); + } + + #region %, modulo + + [RubyMethod("%")] + [RubyMethod("modulo")] + public static BigDecimal/*!*/ Modulo(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigDecimal/*!*/ other) { + BigDecimal div, mod; + BigDecimal.DivMod(GetConfig(context), self, other, out div, out mod); + return mod; + } + + [RubyMethod("%")] + [RubyMethod("modulo")] + public static BigDecimal/*!*/ Modulo(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + return Modulo(context, self, BigDecimal.Create(GetConfig(context), other)); + } + + [RubyMethod("%")] + [RubyMethod("modulo")] + public static BigDecimal/*!*/ Modulo(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return Modulo(context, self, BigDecimal.Create(GetConfig(context), other)); + } + + [RubyMethod("modulo")] + public static object Modulo(RubyContext/*!*/ context, BigDecimal/*!*/ self, double other) { + return LibrarySites.Modulo(context, BigDecimal.ToFloat(GetConfig(context), self), other); + } + + [RubyMethod("%")] + public static object ModuloOp(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.ModuloOp); + } + + [RubyMethod("modulo")] + public static object Modulo(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Modulo); + } + + #endregion + + [RubyMethod("**")] + [RubyMethod("power")] + public static BigDecimal/*!*/ Power(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + return BigDecimal.Power(GetConfig(context), self, other); + } + + [RubyMethod("+@")] + public static BigDecimal/*!*/ Identity(BigDecimal/*!*/ self) { + return self; + } + + [RubyMethod("-@")] + public static BigDecimal/*!*/ Negate(RubyContext/*!*/ context, BigDecimal/*!*/ self) { + return BigDecimal.Negate(GetConfig(context), self); + } + + [RubyMethod("abs")] + public static BigDecimal/*!*/ Abs(RubyContext/*!*/ context, BigDecimal/*!*/ self) { + return BigDecimal.Abs(GetConfig(context), self); + } + + #region divmod + + [RubyMethod("divmod")] + public static RubyArray/*!*/ DivMod(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigDecimal/*!*/ other) { + BigDecimal div, mod; + BigDecimal.DivMod(GetConfig(context), self, other, out div, out mod); + return RubyOps.MakeArray2(div, mod); + } + + [RubyMethod("divmod")] + public static RubyArray/*!*/ DivMod(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + return DivMod(context, self, BigDecimal.Create(GetConfig(context), other)); + } + + [RubyMethod("divmod")] + public static RubyArray/*!*/ DivMod(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return DivMod(context, self, BigDecimal.Create(GetConfig(context), other)); + } + + [RubyMethod("divmod")] + public static object DivMod(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.DivMod); + } + + #endregion + + #region remainder + + [RubyMethod("remainder")] + public static BigDecimal/*!*/ Remainder(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigDecimal/*!*/ other) { + BigDecimal mod = Modulo(context, self, other); + if (self.Sign == other.Sign) { + return mod; + } else { + return BigDecimal.Subtract(GetConfig(context), mod, other); + } + } + + [RubyMethod("remainder")] + public static object Remainder(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Remainder); + } + + #endregion + + [RubyMethod("sqrt")] + public static BigDecimal/*!*/ SquareRoot(RubyContext/*!*/ context, BigDecimal/*!*/ self, int n) { + return BigDecimal.SquareRoot(GetConfig(context), self, n); + } + + [RubyMethod("sqrt")] + public static object SquareRoot(RubyContext/*!*/ context, BigDecimal/*!*/ self, object n) { + throw RubyExceptions.CreateTypeError("wrong argument type " + RubyUtils.GetClassName(context, n) + " (expected Fixnum)"); + } + + #endregion + + #region Comparison Operations + + #region <=> + + [RubyMethod("<=>")] + public static object Compare(BigDecimal/*!*/ self, [NotNull]BigDecimal/*!*/ other) { + return self.CompareBigDecimal(other); + } + + [RubyMethod("<=>")] + public static object Compare(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + } + + [RubyMethod("<=>")] + public static object Compare(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + return self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + } + + [RubyMethod("<=>")] + public static object Compare(RubyContext/*!*/ context, BigDecimal/*!*/ self, double other) { + return self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + } + + [RubyMethod("<=>")] + public static object Compare(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + return Protocols.CoerceAndCallCompare(context, self, other); + } + + #endregion + + #region > + + [RubyMethod(">")] + public static object GreaterThan(BigDecimal/*!*/ self, [NotNull]BigDecimal/*!*/ other) { + int? c = self.CompareBigDecimal(other); + if (c.HasValue) { + return c.Value > 0; + } else { + return null; + } + } + + [RubyMethod(">")] + public static object GreaterThan(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other) { + int? c = self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + if (c.HasValue) { + return c.Value > 0; + } else { + return null; + } + } + + [RubyMethod(">")] + public static object GreaterThan(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + int? c = self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + if (c.HasValue) { + return c.Value > 0; + } else { + return null; + } + } + + [RubyMethod(">")] + public static object GreaterThan(RubyContext/*!*/ context, BigDecimal/*!*/ self, double other) { + int? c = self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + if (c.HasValue) { + return c.Value > 0; + } else { + return null; + } + } + + [RubyMethod(">")] + public static object GreaterThan(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + int? c = Protocols.CoerceAndCallCompare(context, self, other) as int?; + if (c.HasValue) { + return c.Value > 0; + } else { + return null; + } + } + + #endregion + + #region >= + + [RubyMethod(">=")] + public static object GreaterThanOrEqual(BigDecimal/*!*/ self, [NotNull]BigDecimal/*!*/ other) { + int? c = self.CompareBigDecimal(other); + if (c.HasValue) { + return c.Value >= 0; + } else { + return null; + } + } + + [RubyMethod(">=")] + public static object GreaterThanOrEqual(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other) { + int? c = self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + if (c.HasValue) { + return c.Value >= 0; + } else { + return null; + } + } + + [RubyMethod(">=")] + public static object GreaterThanOrEqual(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + int? c = self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + if (c.HasValue) { + return c.Value >= 0; + } else { + return null; + } + } + + [RubyMethod(">=")] + public static object GreaterThanOrEqual(RubyContext/*!*/ context, BigDecimal/*!*/ self, double other) { + int? c = self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + if (c.HasValue) { + return c.Value >= 0; + } else { + return null; + } + } + + [RubyMethod(">=")] + public static object GreaterThanOrEqual(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + int? c = Protocols.CoerceAndCallCompare(context, self, other) as int?; + if (c.HasValue) { + return c.Value >= 0; + } else { + return null; + } + } + + #endregion + + #region < + + [RubyMethod("<")] + public static object LessThan(BigDecimal/*!*/ self, [NotNull]BigDecimal/*!*/ other) { + int? c = self.CompareBigDecimal(other); + if (c.HasValue) { + return c.Value < 0; + } else { + return null; + } + } + + [RubyMethod("<")] + public static object LessThan(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other) { + int? c = self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + if (c.HasValue) { + return c.Value < 0; + } else { + return null; + } + } + + [RubyMethod("<")] + public static object LessThan(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + int? c = self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + if (c.HasValue) { + return c.Value < 0; + } else { + return null; + } + } + + [RubyMethod("<")] + public static object LessThan(RubyContext/*!*/ context, BigDecimal/*!*/ self, double other) { + int? c = self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + if (c.HasValue) { + return c.Value < 0; + } else { + return null; + } + } + + [RubyMethod("<")] + public static object LessThan(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + int? c = Protocols.CoerceAndCallCompare(context, self, other) as int?; + if (c.HasValue) { + return c.Value < 0; + } else { + return null; + } + } + + #endregion + + #region <= + [RubyMethod("<=")] + public static object LessThanOrEqual(BigDecimal/*!*/ self, [NotNull]BigDecimal/*!*/ other) { + int? c = self.CompareBigDecimal(other); + if (c.HasValue) { + return c.Value <= 0; + } else { + return null; + } + } + + [RubyMethod("<=")] + public static object LessThanOrEqual(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other) { + int? c = self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + if (c.HasValue) { + return c.Value <= 0; + } else { + return null; + } + } + + [RubyMethod("<=")] + public static object LessThanOrEqual(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + int? c = self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + if (c.HasValue) { + return c.Value <= 0; + } else { + return null; + } + } + + [RubyMethod("<=")] + public static object LessThanOrEqual(RubyContext/*!*/ context, BigDecimal/*!*/ self, double other) { + int? c = self.CompareBigDecimal(BigDecimal.Create(GetConfig(context), other)); + if (c.HasValue) { + return c.Value <= 0; + } else { + return null; + } + } + + [RubyMethod("<=")] + public static object LessThanOrEqual(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + int? c = Protocols.CoerceAndCallCompare(context, self, other) as int?; + if (c.HasValue) { + return c.Value <= 0; + } else { + return null; + } + } + + #endregion + + [RubyMethod("eql?")] + [RubyMethod("==")] + [RubyMethod("===")] + public static object Equal(BigDecimal/*!*/ self, [NotNull]BigDecimal/*!*/ other) { + // This is a hack since normal numeric values return false not nil for NaN comparison + if (BigDecimal.IsNaN(self) || BigDecimal.IsNaN(other)) { + return null; + } + return self.Equals(other); + } + + [RubyMethod("eql?")] + [RubyMethod("==")] + [RubyMethod("===")] + public static bool Equal(RubyContext/*!*/ context, BigDecimal/*!*/ self, int other) { + return self.Equals(BigDecimal.Create(GetConfig(context), other)); + } + + [RubyMethod("eql?")] + [RubyMethod("==")] + [RubyMethod("===")] + public static bool Equal(RubyContext/*!*/ context, BigDecimal/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return self.Equals(BigDecimal.Create(GetConfig(context), other)); + } + + [RubyMethod("eql?")] + [RubyMethod("==")] + [RubyMethod("===")] + public static bool Equal(RubyContext/*!*/ context, BigDecimal/*!*/ self, double other) { + return self.Equals(BigDecimal.Create(GetConfig(context), other)); + } + + [RubyMethod("eql?")] // HACK - this is actually semantically wrong but is what the BigDecimal library does. + [RubyMethod("==")] + [RubyMethod("===")] + public static object Equal(RubyContext/*!*/ context, BigDecimal/*!*/ self, object other) { + // This is a hack since normal numeric values do not return nil for nil (they return false) + if (other == null) { + return null; + } + return Protocols.IsEqual(context, other, self); + } + + #endregion + + #region Rounding Operations + + [RubyMethod("ceil")] + public static BigDecimal/*!*/ Ceil(RubyContext/*!*/ context, BigDecimal/*!*/ self, [Optional]int n) { + return BigDecimal.LimitPrecision(GetConfig(context), self, n, BigDecimal.RoundingModes.Ceiling); + } + + [RubyMethod("floor")] + public static BigDecimal/*!*/ Floor(RubyContext/*!*/ context, BigDecimal/*!*/ self, [Optional]int n) { + return BigDecimal.LimitPrecision(GetConfig(context), self, n, BigDecimal.RoundingModes.Floor); + } + + [RubyMethod("round")] + public static BigDecimal/*!*/ Round(RubyContext/*!*/ context, BigDecimal/*!*/ self, [Optional]int n) { + return BigDecimal.LimitPrecision(GetConfig(context), self, n, GetConfig(context).RoundingMode); + } + + [RubyMethod("round")] + public static BigDecimal/*!*/ Round(RubyContext/*!*/ context, BigDecimal/*!*/ self, int n, int mode) { + return BigDecimal.LimitPrecision(GetConfig(context), self, n, (BigDecimal.RoundingModes)mode); + } + + [RubyMethod("truncate")] + public static BigDecimal/*!*/ Truncate(RubyContext/*!*/ context, BigDecimal/*!*/ self, [Optional]int n) { + return BigDecimal.LimitPrecision(GetConfig(context), self, n, BigDecimal.RoundingModes.Down); + } + + #endregion + + #region Tests + + [RubyMethod("finite?")] + public static bool IsFinite(BigDecimal/*!*/ self) { + return BigDecimal.IsFinite(self); + } + + [RubyMethod("infinite?")] + public static object IsInfinite(BigDecimal/*!*/ self) { + if (BigDecimal.IsInfinite(self)) { + return self.Sign; + } else { + return null; + } + } + + [RubyMethod("nan?")] + public static bool IsNaN(BigDecimal/*!*/ self) { + return BigDecimal.IsNaN(self); + } + + [RubyMethod("nonzero?")] + public static BigDecimal IsNonZero(BigDecimal/*!*/ self) { + if (!BigDecimal.IsZero(self)) { + return self; + } else { + return null; + } + } + + [RubyMethod("zero?")] + public static bool IsZero(BigDecimal/*!*/ self) { + return BigDecimal.IsZero(self); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/BigDecimal/Fraction.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/BigDecimal/Fraction.cs new file mode 100644 index 0000000000..018fc579fe --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/BigDecimal/Fraction.cs @@ -0,0 +1,783 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Text; +using Microsoft.Scripting.Math; + +namespace IronRuby.StandardLibrary.BigDecimal { + public class Fraction { + + #region Constants + // Number of decimal places held in each word [BASE_FIG == log10(BASE) and 10^BASE_FIG == BASE] + public const int BASE_FIG = 9; + // Each word contains a value between 0 and BASE-1 + public const uint BASE = 1000000000; + // Half the BASE value used in rounding + public const uint HALF_BASE = BASE / 2; + // BASE divided by 10 + public const uint BASE_DIV_10 = BASE / 10; + #endregion + + #region Special Values + public static readonly Fraction Zero = new Fraction(new uint[] { 0 }); + public static readonly Fraction One = new Fraction(new uint[] { BASE_DIV_10 }); + public static readonly Fraction Five = new Fraction(new uint[] { 5 * BASE_DIV_10 }); + #endregion + + #region Private Fields + // The digits of the fraction held in words. + // Each word holds BASE_FIG decimal digits. + // The words are stored in most significant word first + // e.g 0.1234567890123456789 will be stored as new uint[] { 123456789, 012345678, 900000000 } + private uint[] _words; + // The current number of elements in use in the _words array + private int _precision; + private readonly static uint[] _powers; + static Fraction() { + // Generate the powers array - should look like [BASE/10, BASE/100, ... , 1] + _powers = new uint[BASE_FIG]; + int i = 0; + for (uint p = BASE/10; p > 0; p /= 10 ) { + _powers[i] = p; + ++i; + } + } + #endregion + + #region Creation + public Fraction(uint[] data) { + this._words = data; + this.Precision = MaxPrecision; + } + + public Fraction(int maxPrecision) + : this(maxPrecision, maxPrecision) { + } + + public Fraction(Fraction/*!*/ copyFrom, int maxPrecision) + : this(maxPrecision, maxPrecision) { + // Copy the fraction + int length = Math.Min(maxPrecision, copyFrom.MaxPrecision); + Array.Copy(copyFrom._words, _words, length); + } + + public Fraction(int maxPrecision, int precision) { + if (maxPrecision <= 0) { + throw new ArgumentException("maxPrecision must be greater than zero"); + } + _words = new uint[maxPrecision]; + Precision = precision; + } + public static Fraction/*!*/ Create(string/*!*/ digits) { + int digitOffset; + return Parse(digits, out digitOffset); + } + #endregion + + #region Public Properties + public int Precision { + get { return _precision; } + set { + if (value < 1 || value > _words.Length) { + throw new ArgumentException("Precision must be in [1,MaxPrecision]"); + } + _precision = value; + } + } + public int MaxPrecision { get { return _words.Length; } } + + public int DigitCount { + get { + // Find last non-zero word + int wordIndex = Precision-1; + while (wordIndex > 0 && _words[wordIndex] == 0) { + --wordIndex; + } + + // Estimate digit count + int digits = (wordIndex+1) * BASE_FIG; + + // Subtract trailing zeros count from digits in last word + uint lastWord = _words[wordIndex]; + uint factor = 10; + while (factor <= BASE && (lastWord % factor) == 0) { + --digits; + factor = factor * 10; + } + return digits; + } + } + + public bool IsOne { + get { return Precision == 1 && _words[0] == BASE_DIV_10; } + } + public bool IsZero { + get { return Precision == 1 && _words[0] == 0; } + } + #endregion + + #region Public Methods + public override string ToString() { + StringBuilder sb = new StringBuilder(Precision * BASE_FIG); + for (int wordIndex = 0; wordIndex < Precision; wordIndex++) { + uint word = _words[wordIndex]; + sb.AppendFormat("{0:D"+BASE_FIG+"}", word); + } + // Trim off last zeros + string str = sb.ToString().TrimEnd('0'); + + // Special case for zero + if (str.Length == 0) { + str = "0"; + } + + return str; + } + + public override int GetHashCode() { + int hash = 0; + for(int i=0; i= upperStart; i--) { + carry += upperWords[i] + zWords[i+1]; + zWords[i+1] = (uint)(carry % BASE); + carry /= BASE; + } + Debug.Assert(carry / BASE == 0); + zWords[0] = (uint)(carry % BASE); + + // We expect there to be BASE_FIG offset when normalizing unless + // the carry overflowed into the top word. + z = Fraction.Normalize(z, out exponentOffset); + exponentOffset += BASE_FIG; + return z; + } + + public static Fraction/*!*/ Subtract(Fraction/*!*/ x, Fraction/*!*/ y, int exponentDiff, out int exponentOffset, out int sign) { + Fraction upper = x; + Fraction lower = y; + + sign = Compare(x, y, exponentDiff); + if (sign== 0) { + exponentOffset = 0; + return new Fraction(1); + } else if (sign < 0) { + exponentDiff = -exponentDiff; + upper = y; + lower = x; + } + + // Calculate the word and digit offsets between upper and lower + int wordOffset = exponentDiff / BASE_FIG; + int digitOffset = exponentDiff % BASE_FIG; + + // If necessary we need to scroll one of the arrays + if (digitOffset != 0) { + lower = ScrollRight(lower, digitOffset); + } + + int lowerStart = wordOffset; + + // We should now have something like: + // UUU UUU UU0 000 000 000 (upperStart=0, upperLength=8) + // 000 0LL LLL LLL LLL LL0 (lowerStart=4, lowerLength=13) + // assuming that exponentDiff is 4 (or -4) and BASE_FIG is 3 + // where each character above is a decimal digit and a space indicates a word boundary + // Also, upper should be larger than lower + + int zPrecision = Math.Max(upper.Precision, lower.Precision + lowerStart); + Fraction z = new Fraction(zPrecision); + + uint[] upperWords = upper._words; + uint[] lowerWords = lower._words; + uint[] zWords = z._words; + + // Copy words of upper straight into z + Array.Copy(upperWords, 0, zWords, 0, upper.Precision); + + //// Subtract words of lower from z, borrowing as necessary + SubtractInPlace(zWords, lowerWords, lower.Precision, lowerStart); + + z = Fraction.Normalize(z, out exponentOffset); + return z; + } + + public static Fraction/*!*/ Multiply(Fraction/*!*/ x, Fraction/*!*/ y, out int offset) { + int xPrecision = x.Precision; + int yPrecision = y.Precision; + int zPrecision = xPrecision + yPrecision; + uint[] xData = x._words; + uint[] yData = y._words; + uint[] zData = new uint[zPrecision]; + Fraction z = new Fraction(zData); + + for (int xIndex = xPrecision-1; xIndex >= 0; xIndex--) { + uint xValue = xData[xIndex]; + int zIndex = zPrecision - (xPrecision - xIndex); + ulong carry = 0; + for (int yIndex = yPrecision-1; yIndex >= 0; yIndex--) { + carry = carry + ((ulong)xValue) * yData[yIndex] + zData[zIndex]; + zData[zIndex--] = (uint)(carry%BASE); + carry /= BASE; + } + while (carry != 0) { + carry += zData[zIndex]; + zData[zIndex--] = (uint)carry; + carry /= BASE; + } + } + z = Fraction.Normalize(z, out offset); + return z; + } + + public static Fraction/*!*/ Divide(Fraction/*!*/ a, Fraction/*!*/ b, int minPrecision, out Fraction/*!*/ r, out int cOffset, out int rOffset) { + int precision = Math.Max(a.MaxPrecision + b.MaxPrecision+1, minPrecision); + Fraction c = new Fraction(precision); + r = new Fraction(precision * 2); + + uint[] aWords = a._words; + uint[] bWords = b._words; + uint[] cWords = c._words; + uint[] rWords = r._words; + + int aSize = a.Precision; + int bSize = b.Precision; + int cSize = c.MaxPrecision; + int rSize = r.MaxPrecision; + + // Initialize remainder (we add an extra word at the front to catch the overflow + Array.Copy(aWords, 0, rWords, 1, Math.Min(aSize, rSize-1)); + + // Setup basic values + ulong b1 = bWords[0]; + ulong b1plus1 = bSize <= 1 ? b1 : b1 + 1; + ulong b1b2 = bSize <= 1 ? (ulong)bWords[0] * BASE : GetDoubleWord(bWords, 0, b.Precision); + ulong b1b2plus1 = bSize <= 2 ? b1b2 : b1b2 + 1; + + // Main loop + int index = 1; + int size = Math.Min(cSize, rSize); + while (index < size) { + if (rWords[index] == 0) { + ++index; + continue; + } + + ulong r1r2 = GetDoubleWord(rWords, index, rSize); + if (r1r2 == b1b2) { + // Iterate through the rest of b comparing words of r and b until they are not equal + int bIndex = 2; + int rIndex = index + 2; + FindNextNonEqual(rWords, bWords, bSize, ref rIndex, ref bIndex); + if (rIndex < rSize && bIndex < bSize && rWords[rIndex] < bWords[bIndex]) { + if (index + 1 > rSize) { + break; + } + InternalDivide(rWords, rSize, r1r2 / b1plus1, bWords, bSize, index, cWords); + } else { + // Quotient is 1, just subtract b from r + SubtractInPlace(rWords, bWords, bSize, index); + cWords[index-1]++; + Carry(cWords, index); + } + } else if (r1r2 >= b1b2plus1) { + InternalDivide(rWords, rSize, r1r2 / b1b2plus1, bWords, bSize, index - 1, cWords); + } else { + InternalDivide(rWords, rSize, r1r2 / b1plus1, bWords, bSize, index, cWords); + } + } + c = Normalize(c, out cOffset); + r = Normalize(r, out rOffset); + // We added artificially an extra word onto r and c to cope with carry overflow (so take away again now) + cOffset += BASE_FIG; + rOffset += BASE_FIG; + return c; + } + + public static int Compare(Fraction/*!*/ x, Fraction/*!*/ y, int exponentDiff) { + if (exponentDiff != 0) { + return exponentDiff > 0 ? 1 : -1; + } + int wordIndex = 0; + while (wordIndex < x.Precision && wordIndex < y.Precision) { + if (x._words[wordIndex] != y._words[wordIndex]) { + return x._words[wordIndex] > y._words[wordIndex] ? 1 : -1; + } + wordIndex++; + } + if (wordIndex == x.Precision) { + while (wordIndex < y.Precision) { + if (y._words[wordIndex] != 0) { + return -1; + } + wordIndex++; + } + } else { + while (wordIndex < x.Precision) { + if (x._words[wordIndex] != 0) { + return 1; + } + wordIndex++; + } + } + return 0; + } + +#if SILVERLIGHT + public static int DivRem(int a, int b, out int result) { + result = a % b; + return (a / b); + } +#endif + + /// + /// Limits the precision of the given Fraction. + /// + /// The sign of the BigDecimal + /// The fraction to limit + /// The number of digits to which we are limiting + /// The rounding mode to use when limiting + /// A new fraction that has no more than digits. + /// + /// Consider a fraction of 123456789 using default HalfUp rounding. + /// Limit : Result + /// 1 1 + /// 2 12 + /// 3 123 + /// 4 1234 + /// 5 12346 + /// 6 123457 + /// 7 1234568 + /// 8 12345679 + /// 9 123456789 + /// 10 123456789 + /// + public static Fraction/*!*/ LimitPrecision(int sign, Fraction/*!*/ fraction, int digits, BigDecimal.RoundingModes mode, out int offset) { + Fraction result; + + if (digits <= 0) { + uint digit = (uint)(fraction.IsZero ? 0 : 1); + if (RoundDigit(sign, digit, 0, mode) > 0) { + offset = 1-digits; + return One; + } else { + offset = 0; + return Zero; + } + } + + if (digits >= fraction.DigitCount) { + offset = 0; + return fraction; + } + + // Calculate offsets of relevant digits + int secondLastDigitIndex; // i.e. fraction[digits-1] + int secondLastWordIndex; + uint secondLastDigit; + int lastDigitIndex; // i.e. fraction[digits] + int lastWordIndex; + uint lastDigit; + +#if SILVERLIGHT + secondLastWordIndex = DivRem(digits - 1, BASE_FIG, out secondLastDigitIndex); +#else + secondLastWordIndex = Math.DivRem(digits - 1, BASE_FIG, out secondLastDigitIndex); +#endif + if (secondLastDigitIndex == BASE_FIG-1) { + lastWordIndex = secondLastWordIndex+1; + lastDigitIndex = 0; + } else { + lastWordIndex = secondLastWordIndex; + lastDigitIndex = secondLastDigitIndex + 1; + } + + // TODO: Extract these calculations out into static readonly arrays + // Mask for last digit. E.g. lastDigitIndex = 3, BASE_FIG = 9 => lastFactor = 1000000 + uint lastFactor=_powers[lastDigitIndex]; + // Mask for second last digit + uint secondLastFactor= _powers[secondLastDigitIndex]; + + // Calculate the current digits and rounding direction + secondLastDigit = (fraction._words[secondLastWordIndex] / secondLastFactor) % 10; + if (lastWordIndex < fraction.MaxPrecision) { + lastDigit = (fraction._words[lastWordIndex] / lastFactor) % 10; + } else { + lastDigit = 0; + } + int round = RoundDigit(sign, lastDigit, secondLastDigit, mode); + + // Create a temporary fraction used to cause the rounding in the original + result = new Fraction(lastWordIndex+1); + Array.Copy(fraction._words, 0, result._words, 0, Math.Min(lastWordIndex+1, fraction.Precision)); + // Clear the digits beyond the second last digit + result._words[secondLastWordIndex] = result._words[secondLastWordIndex] - (fraction._words[secondLastWordIndex] % secondLastFactor); + if (round > 0) { + // Increment the last digit of result by 1 + Fraction temp = new Fraction(secondLastWordIndex + 1); + temp._words[secondLastWordIndex] = secondLastFactor; + result = Fraction.Add(result, temp, 0, out offset); + } else { + offset = 0; + } + + result.Precision = Math.Min(secondLastWordIndex+1, result.MaxPrecision); + return result; + } + #endregion + + #region Private Helpers + private static int RoundDigit(int sign, uint lastDigit, uint secondLastDigit, BigDecimal.RoundingModes roundingMode) { + int result = -1; + switch (roundingMode) { + case BigDecimal.RoundingModes.Up: + if (lastDigit != 0) { + result = 1; + } + break; + case BigDecimal.RoundingModes.HalfUp: + if (lastDigit >= 5) { + result = 1; + } + break; + case BigDecimal.RoundingModes.HalfDown: + if (lastDigit > 5) { + result = 1; + } + break; + case BigDecimal.RoundingModes.HalfEven: + if (lastDigit > 5) { + result = 1; + } else if ((lastDigit == 5) && (secondLastDigit % 2 != 0)) { + result = 1; + } + break; + case BigDecimal.RoundingModes.Ceiling: + if (sign == 1 && lastDigit != 0) { + result = 1; + } + break; + case BigDecimal.RoundingModes.Floor: + if (sign == -1 && lastDigit != 0) { + result = 1; + } + break; + } + return result; + } + + /// + /// Divide r[index, ...] by q + /// + private static void InternalDivide(uint[] rWords, int rSize, ulong q, uint[] bWords, int bSize, int index, uint[] cWords) { + cWords[index] += (uint)q; + SubtractMultiple(rWords, rSize, q, bWords, bSize, index, bSize+index); + } + + // r = r - q * b + private static void SubtractMultiple(uint[] rWords, int rSize, ulong q, uint[] bWords, int bSize, int index, int rIndex) { + int bIndex = bSize - 1; + uint borrow1 = 0; + uint borrow2 = 0; + + while (bIndex >= 0) { + ulong qb = q * bWords[bIndex]; + if (qb < BASE) { + borrow1 = 0; + } else { + borrow1 = (uint)(qb / BASE); + qb -= (ulong)borrow1 * BASE; + } + + if (rWords[rIndex] < qb) { + rWords[rIndex] += (uint)(BASE - qb); + borrow2 += borrow1 + 1; + } else{ + rWords[rIndex] -= (uint)qb; + borrow2 += borrow1; + } + + if (borrow2 != 0) { + if (rWords[rIndex - 1] < borrow2) { + rWords[rIndex - 1] += (BASE - borrow2); + borrow2 = 1; + } else { + rWords[rIndex - 1] -= borrow2; + borrow2 = 0; + } + } + + --rIndex; + --bIndex; + } + } + + private static void FindNextNonEqual(uint[] i, uint[] j, int iSize, ref int iIndex, ref int jIndex) { + while (iIndex < iSize) { + if (i[iIndex] != j[jIndex]) { + break; + } + ++iIndex; + ++jIndex; + } + } + + /// + /// Subtract y from x in place. + /// + /// + /// + private static void SubtractInPlace(uint[] x, uint[] y, int count, int offset) { + uint borrow = 0; + int yIndex = count - 1; + int xIndex = offset + yIndex; + while(yIndex >= 0) { + if(x[xIndex] < y[yIndex] + borrow) { + x[xIndex] = x[xIndex] + (BASE -(y[yIndex] + borrow)); + borrow = 1; + } else { + x[xIndex] = x[xIndex] - (y[yIndex] + borrow); + borrow = 0; + } + --yIndex; + --xIndex; + } + // Continue to borrow as necessary + while (borrow != 0) { + if(x[xIndex] < borrow) { + x[xIndex] = x[xIndex] + (BASE - borrow); + borrow = 1; + } else { + x[xIndex] = x[xIndex] - borrow; + borrow = 0; + } + --xIndex; + } + } + + private static ulong GetDoubleWord(uint[] a, int i, int precision) { + if (i+1 == precision) { + return (ulong)a[i] * BASE; + } else { + return (ulong)a[i] * BASE + a[i + 1]; + } + } + + private static void Carry(uint[] a, int i) { + while (a[i] >= BASE) { + a[i] -= BASE; + --i; + ++a[i]; + } + } + + private static Fraction/*!*/ ScrollLeft(Fraction/*!*/ fraction, int offset) { + // Scrolling left is just the same as scrolling right by (BASE_FIG-offset) + // E.g. Assuming BASE_FIG is 9: + // ScrollRight(123456789, 6) => 000000123 456789000 + // ScrollLeft (123456789, 6) => 000123456 789000000 + return ScrollRight(fraction, BASE_FIG - offset); + } + private static Fraction/*!*/ ScrollRight(Fraction/*!*/ fraction, int offset) { + Debug.Assert(offset <= BASE_FIG); + Debug.Assert(offset >= 0); + + // Don't do anything if offset is not going to change the internal digit layout + if (offset % BASE_FIG == 0) { + return fraction; + } + + int oldPrecision = fraction.Precision; + int newPrecision = oldPrecision + 1; + + Fraction newFraction = new Fraction(newPrecision); + + // Calculate masking values + // divisor is used to mask off and bring top digits down to bottom digits (TTTTTTBBBB / divisor == TTTTTT) + // also divisor is used to mask off the bottom digits (TTTTTTBBBB % divisor == BBBB) + // factor is then used to bring bottom digits up to top digits (BBBB * factor == BBBB000000) + uint downFactor = 1; + for (int i = 0; i < offset; ++i ) { + downFactor *= 10; + } + uint upFactor = BASE / downFactor; + + // Scroll the digits + // We want to go from TTTTTTBBBB TTTTTTBBBB TTTTTTBBBB to 0000TTTTTT BBBBTTTTTT BBBBTTTTTT BBBB000000 + // I.E. We are pushing all the digits to the right by "offset" amount and padding with zeros + uint topDigits = 0; + uint bottomDigits = 0; + int wordIndex = 0; + while(wordIndex < oldPrecision) { + topDigits = fraction._words[wordIndex] / downFactor; + newFraction._words[wordIndex] = bottomDigits + topDigits; + bottomDigits = (fraction._words[wordIndex] % downFactor) * upFactor; + wordIndex++; + } + + // Fix up the last word + newFraction._words[wordIndex] = bottomDigits; + + return newFraction; + } + + private static Fraction/*!*/ Parse(string/*!*/ digits, out int digitOffset) { + // Trim off any trailing zeros + digits = digits.TrimEnd('0'); + + if (digits == "") { + digitOffset = 0; + return Zero; + } + + // Create the new fraction + int precision = digits.Length / BASE_FIG; + int finalDigits = digits.Length % BASE_FIG; + if (finalDigits > 0) { ++precision; } + Fraction/*!*/ fraction = new Fraction(precision); + + // Iterate through groups of BASE_FIG digits + int digitIndex; + int wordIndex = 0; + for (digitIndex = 0; digitIndex+BASE_FIG <= digits.Length; digitIndex+=BASE_FIG) { + fraction._words[wordIndex] = uint.Parse(digits.Substring(digitIndex, BASE_FIG)); + wordIndex++; + } + + // Add on the last few digits, adding extra zeros as necessary + if (finalDigits > 0) { + uint lastWord = uint.Parse(digits.Substring(digitIndex)); + while (finalDigits < BASE_FIG) { + lastWord *= 10; + ++finalDigits; + } + fraction._words[wordIndex] = lastWord; + } + + fraction = Fraction.Normalize(fraction, out digitOffset); + return fraction; + } + + private static Fraction/*!*/ Normalize(Fraction/*!*/ f, out int digitOffset) { + // Count leading zero words + int leadingZeroWords = 0; + while (leadingZeroWords < f.MaxPrecision && f._words[leadingZeroWords] == 0) { + leadingZeroWords++; + } + + if (leadingZeroWords == f.MaxPrecision) { + // The fraction is just all zeros + digitOffset = 0; + return f; + } + + // Count trailing zero words + int trailingZeroWords = 0; + while (f._words[f.Precision - 1 - trailingZeroWords] == 0) { + trailingZeroWords++; + } + // Count leading zero digits (within the first non-zero word) + // and also calculate up and down factors for masking + int leadingZeroDigits = BASE_FIG; + uint firstWord = f._words[leadingZeroWords]; + uint downFactor = 1; + while (firstWord != 0) { + firstWord /= 10; + leadingZeroDigits--; + downFactor *= 10; + } + uint upFactor = BASE / downFactor; + + int newPrecision = f.Precision - leadingZeroWords - trailingZeroWords; + Fraction n = new Fraction(newPrecision); + // Copy the non-zero words across + Array.Copy(f._words, leadingZeroWords, n._words, 0, n.MaxPrecision); + + if (leadingZeroDigits > 0) { + // Scroll the digits within the non-zero words to trim off the first zero digits + uint bottomDigits = n._words[0] * upFactor; + uint topDigits = 0; + int wordIndex = 1; + while (wordIndex < n.Precision) { + topDigits = n._words[wordIndex] / downFactor; + n._words[wordIndex - 1] = bottomDigits + topDigits; + bottomDigits = (n._words[wordIndex] % downFactor) * upFactor; + wordIndex++; + } + + // Fix up the last word + n._words[wordIndex-1] = bottomDigits; + // and the Precision + n.Precision = wordIndex; + } + + + // Return the offset in digits caused by the normalization. + digitOffset = -(leadingZeroWords * BASE_FIG + leadingZeroDigits); + return n; + } + + #endregion + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ArgFilesSingletonOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ArgFilesSingletonOps.cs new file mode 100644 index 0000000000..f7aebc4f62 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ArgFilesSingletonOps.cs @@ -0,0 +1,65 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ +#if !SILVERLIGHT + +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + /// + /// ARGF singleton trait. + /// + [RubyConstant("ARGF")] + [RubySingleton(BuildConfig = "!SILVERLIGHT"), Includes(typeof(Enumerable))] + public static class ArgFilesSingletonOps { + + //fileno + //tell + //file + //eof + //to_s + //each + //readline + //lineno= + //path + //to_i + //getc + //pos + //eof? + //gets + + [RubyMethod("filename")] + public static MutableString/*!*/ GetCurrentFileName(RubyContext/*!*/ context, object self) { + return context.InputProvider.CurrentFileName; + } + + //close + //each_byte + //read + //rewind + //pos= + //to_io + //seek + //skip + //readchar + //closed? + //each_line + //readlines + //binmode + //lineno + } +} +#endif \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ArrayOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ArrayOps.cs new file mode 100644 index 0000000000..9c5abb2397 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ArrayOps.cs @@ -0,0 +1,529 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Actions; +using System.Dynamic.Binders; +using IronRuby.Runtime; +using IronRuby.Compiler; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Builtins { + + /// + /// Array inherits from Object, mixes in Enumerable. + /// Ruby array is basically List{object}. + /// + [RubyClass("Array", Extends = typeof(RubyArray), Inherits = typeof(object)), Includes(typeof(IList), Copy = true)] + public static class ArrayOps { + + #region Constructors + + [RubyConstructor] + public static RubyArray/*!*/ CreateArray(RubyClass/*!*/ self) { + return new RubyArray(); + } + + // Reinitialization. Not called when a factory/non-default ctor is called. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static RubyArray/*!*/ Reinitialize(RubyContext/*!*/ context, RubyArray/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + self.Clear(); + return self; + } + + [RubyConstructor] + public static object CreateArray(BlockParam block, RubyClass/*!*/ self, + [NotNull]object/*!*/ arrayOrSize) { + + IList array = Protocols.AsArray(self.Context, arrayOrSize); + if (array != null) { + // block ignored + return CreateArray(array); + } + + int size = Protocols.CastToFixnum(self.Context, arrayOrSize); + if (block != null) { + return CreateArray(block, size); + } else { + return CreateArray(self, size, null); + } + } + + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static object Reinitialize(RubyContext/*!*/ context, BlockParam block, RubyArray/*!*/ self, + [NotNull]object/*!*/ arrayOrSize) { + RubyUtils.RequiresNotFrozen(context, self); + + IList array = Protocols.AsArray(context, arrayOrSize); + if (array != null) { + // block ignored + return Reinitialize(self, array); + } + + int size = Protocols.CastToFixnum(context, arrayOrSize); + if (block != null) { + return Reinitialize(block, self, size); + } else { + return ReinitializeByRepeatedValue(context, self, size, null); + } + } + + private static RubyArray/*!*/ CreateArray(IList/*!*/ other) { + return Reinitialize(new RubyArray(other.Count), other); + } + + private static RubyArray/*!*/ Reinitialize(RubyArray/*!*/ self, IList/*!*/ other) { + Assert.NotNull(self, other); + if (other != self) { + self.Clear(); + IListOps.AddRange(self, other); + } + return self; + } + + private static object CreateArray([NotNull]BlockParam/*!*/ block, int size) { + return Reinitialize(block, new RubyArray(), size); + } + + private static object Reinitialize([NotNull]BlockParam/*!*/ block, RubyArray/*!*/ self, int size) { + if (size < 0) { + throw RubyExceptions.CreateArgumentError("negative array size"); + } + + self.Clear(); + for (int i = 0; i < size; i++) { + object item; + if (block.Yield(i, out item)) { + return item; + } + self.Add(item); + } + + return self; + } + + [RubyConstructor] + public static RubyArray/*!*/ CreateArray(RubyClass/*!*/ self, [DefaultProtocol]int size, object value) { + if (size < 0) { + throw RubyExceptions.CreateArgumentError("negative array size"); + } + + return new RubyArray().AddMultiple(size, value); + } + + // Reinitialization. Not called when a factory/non-default ctor is called. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static RubyArray/*!*/ ReinitializeByRepeatedValue(RubyContext/*!*/ context, RubyArray/*!*/ self, [DefaultProtocol]int size, object value) { + RubyUtils.RequiresNotFrozen(context, self); + if (size < 0) { + throw RubyExceptions.CreateArgumentError("negative array size"); + } + + self.Clear(); + self.AddMultiple(size, value); + + return self; + } + + [RubyMethod("[]", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ MakeArray(RubyClass/*!*/ self, params object[] args) { + // neither "new" nor "initialize" is called: + RubyArray result = RubyArray.CreateInstance(self); + foreach (object obj in args) { + result.Add(obj); + } + return result; + } + + #endregion + + [RubyMethod("to_a")] + [RubyMethod("to_ary")] + public static RubyArray/*!*/ ToArray(RubyArray/*!*/ self) { + return self; + } + + #region class FormatDirective is used by Array.pack and String.unpack + + internal struct FormatDirective { + internal readonly char Directive; + internal readonly int? Count; + private static readonly Dictionary _native; + + static FormatDirective() { + bool is64bit = (IntPtr.Size == 8); + _native = new Dictionary(6); + _native['s'] = 's'; + _native['S'] = 'S'; + _native['i'] = 'i'; + _native['I'] = 'I'; + _native['l'] = is64bit ? 'i' : 'q'; + _native['L'] = is64bit ? 'I' : 'Q'; + } + + internal FormatDirective(char directive, int? count) { + Directive = directive; + Count = count; + } + + internal static IEnumerable/*!*/ Enumerate(string format) { + for (int i = 0; i < format.Length; i++) { + char c = format[i]; + if (!Char.IsLetter(c) && c != '@') { + continue; + } + i++; + int? count = 1; + char c2 = (i < format.Length) ? format[i] : '\0'; + if (c2 == '_') { + char tmp; + if (!_native.TryGetValue(c, out tmp)) { + throw RubyExceptions.CreateArgumentError("'_' allowed only after types sSiIlL"); + } + c = tmp; + i++; + c2 = (i < format.Length) ? format[i] : '\0'; + } + if (Char.IsDigit(c2)) { + int pos1 = i; + i++; + while (i < format.Length && Char.IsDigit(format[i])) { + i++; + } + count = Int32.Parse(format.Substring(pos1, (i - pos1))); + i--; + } else if (c2 == '*') { + count = null; + } else { + i--; + } + + yield return new FormatDirective(c, count); + } + } + } + + #endregion + + #region pack + + [RubyMethod("pack")] + public static MutableString/*!*/ Pack(RubyContext/*!*/ context, RubyArray/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ format) { + using (MutableStringStream stream = new MutableStringStream()) { + BinaryWriter writer = new BinaryWriter(stream); + int i = 0; + foreach (FormatDirective directive in FormatDirective.Enumerate(format.ConvertToString())) { + int remaining = (self.Count - i); + int count = directive.Count.HasValue ? directive.Count.Value : remaining; + if (count > remaining) { + count = remaining; + } + + MutableString str; + switch (directive.Directive) { + case '@': + count = 0; + stream.Position = directive.Count.HasValue ? directive.Count.Value : 1; + break; + + case 'A': + case 'a': + case 'Z': + count = 1; + char[] cstr = Protocols.CastToString(context, self[i]).ToString().ToCharArray(); + int len1 = directive.Count.HasValue ? directive.Count.Value : cstr.Length; + int len2 = (len1 > cstr.Length) ? cstr.Length : len1; + writer.Write(cstr, 0, len2); + if (len1 > len2) { + byte fill = (directive.Directive == 'A') ? (byte)' ' : (byte)0; + for (int j = 0; j < (len1 - len2); j++) { + writer.Write(fill); + } + } + if (directive.Directive == 'Z' && !directive.Count.HasValue) { + writer.Write((byte)0); + } + break; + + case 'Q': + case 'q': + for (int j = 0; j < count; j++) { + writer.Write(Protocols.CastToUInt64Unchecked(context, self[i + j])); + } + break; + + case 'l': + case 'i': + for (int j = 0; j < count; j++) { + writer.Write(unchecked((int)Protocols.CastToUInt32Unchecked(context, self[i + j]))); + } + break; + + case 'L': + case 'I': + for (int j = 0; j < count; j++) { + writer.Write(Protocols.CastToUInt32Unchecked(context, self[i + j])); + } + break; + + case 'N': // unsigned 4-byte big-endian + WriteUInt32(writer, self, context, i, count, BitConverter.IsLittleEndian); + break; + + case 'n': // unsigned 2-byte big-endian + WriteUInt16(writer, self, context, i, count, BitConverter.IsLittleEndian); + break; + + case 'V': // unsigned 4-byte little-endian + WriteUInt32(writer, self, context, i, count, !BitConverter.IsLittleEndian); + break; + + case 'v': // unsigned 2-byte little-endian + WriteUInt16(writer, self, context, i, count, !BitConverter.IsLittleEndian); + break; + + case 's': + for (int j = 0; j < count; j++) { + writer.Write(unchecked((short)Protocols.CastToUInt32Unchecked(context, self[i + j]))); + } + break; + + case 'S': + for (int j = 0; j < count; j++) { + writer.Write(unchecked((ushort)Protocols.CastToUInt32Unchecked(context, self[i + j]))); + } + break; + + case 'c': + for (int j = 0; j < count; j++) { + writer.Write(unchecked((sbyte)Protocols.CastToUInt32Unchecked(context, self[i + j]))); + } + break; + + case 'C': + for (int j = 0; j < count; j++) { + writer.Write(unchecked((byte)Protocols.CastToUInt32Unchecked(context, self[i + j]))); + } + break; + + case 'm': + count = 1; + str = Protocols.CastToString(context, self[i]); + char[] base64 = Convert.ToBase64String(str.ToByteArray()).ToCharArray(); + for (int j = 0; j < base64.Length; j += 60) { + int len = base64.Length - j; + if (len > 60) { + len = 60; + } + writer.Write(base64, j, len); + writer.Write('\n'); + } + break; + + case 'U': + char[] buffer = new char[count]; + for (int j = 0; j < count; j++) { + buffer[j] = unchecked((char)Protocols.CastToUInt32Unchecked(context, self[i + j])); + } + writer.Write(Encoding.UTF8.GetBytes(buffer)); + break; + + case 'X': + count = 0; + int len3 = directive.Count.HasValue ? directive.Count.Value : 0; + if (len3 > stream.Position) { + throw RubyExceptions.CreateArgumentError("X outside of string"); + } + stream.Position -= len3; + break; + + case 'x': + count = 0; + int len4 = directive.Count.HasValue ? directive.Count.Value : 0; + for (int j = 0; j < len4; j++) { + writer.Write((byte)0); + } + break; + + case 'h': + case 'H': + // MRI skips null, unlike in "m" directive: + if (self[i] != null) { + str = Protocols.CastToString(context, self[i]); + FromHex(writer, str, directive.Count ?? str.GetByteCount(), directive.Directive == 'h'); + } + break; + + default: + throw RubyExceptions.CreateArgumentError( + String.Format("Unknown format directive '{0}'", directive.Directive)); + } + i += count; + if (i >= self.Count) { + break; + } + } + stream.SetLength(stream.Position); + return stream.String; + } + } + + private static void WriteUInt64(BinaryWriter/*!*/ writer, RubyArray/*!*/ self, RubyContext/*!*/ context, int i, int count, bool swap) { + for (int j = 0; j < count; j++) { + uint n = Protocols.CastToUInt32Unchecked(context, self[i + j]); + if (swap) { + writer.Write((byte)(n >> 24)); + writer.Write((byte)((n >> 16) & 0xff)); + writer.Write((byte)((n >> 8) & 0xff)); + writer.Write((byte)(n & 0xff)); + } else { + writer.Write(n); + } + } + } + + private static void WriteUInt32(BinaryWriter/*!*/ writer, RubyArray/*!*/ self, RubyContext/*!*/ context, int i, int count, bool swap) { + for (int j = 0; j < count; j++) { + uint n = Protocols.CastToUInt32Unchecked(context, self[i + j]); + if (swap) { + writer.Write((byte)(n >> 24)); + writer.Write((byte)((n >> 16) & 0xff)); + writer.Write((byte)((n >> 8) & 0xff)); + writer.Write((byte)(n & 0xff)); + } else { + writer.Write(n); + } + } + } + + private static void WriteUInt16(BinaryWriter/*!*/ writer, RubyArray/*!*/ self, RubyContext/*!*/ context, int i, int count, bool swap) { + for (int j = 0; j < count; j++) { + uint n = Protocols.CastToUInt32Unchecked(context, self[i + j]); + if (swap) { + writer.Write((byte)((n >> 8) & 0xff)); + writer.Write((byte)(n & 0xff)); + } else { + writer.Write((ushort)(n & 0xffff)); + } + } + } + + private static void FromHex(BinaryWriter/*!*/ writer, MutableString/*!*/ str, int nibbleCount, bool swap) { + int maxCount = Math.Min(nibbleCount, str.GetByteCount()); + for (int i = 0, j = 0; i < (nibbleCount + 1) / 2; i++, j += 2) { + int hiNibble = (j < maxCount) ? FromHexDigit(str.GetByte(j)) : 0; + int loNibble = (j + 1 < maxCount) ? FromHexDigit(str.GetByte(j + 1)) : 0; + Debug.Assert(hiNibble >= 0 && hiNibble < 16 && loNibble >= 0 && loNibble < 16); + + int c = (swap) ? (loNibble << 4) | hiNibble : (hiNibble << 4) | loNibble; + writer.Write((byte)c); + } + } + + // hexa digits -> values + private static int FromHexDigit(int c) { + c = Tokenizer.ToDigit(c); + if (c < 16) return c; + + // MRI does some magic here: + throw new NotSupportedException("directives `H' and `h' expect hexadecimal digits in input string"); + } + + #endregion + + #region sort!, sort + + private sealed class BreakException : Exception { + } + + [RubyMethod("sort")] + public static RubyArray/*!*/ Sort(RubyContext/*!*/ context, BlockParam block, RubyArray/*!*/ self) { + RubyArray result = self.CreateInstance(); + IListOps.Replace(context, result, self); + return SortInPlace(context, block, result); + } + + [RubyMethod("sort!")] + public static RubyArray/*!*/ SortInPlace(RubyContext/*!*/ context, BlockParam block, RubyArray/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + + // TODO: this does more comparisons (and in a different order) than + // Ruby's sort. Also, control flow won't work because List.Sort wraps + // exceptions from the comparer & rethrows. We need to rewrite a version of quicksort + // that behaves like Ruby's sort. + if (block == null) { + self.Sort(delegate(object x, object y) { + return Protocols.Compare(context, x, y); + }); + } else { + try { + self.Sort(delegate(object x, object y) { + object result; + if (block.Yield(x, y, out result)) { + // TODO: this doesn't work + throw new BreakException(); + } + + if (result == null) { + throw RubyExceptions.MakeComparisonError(context, x, y); + } + + return Protocols.ConvertCompareResult(context, result); + }); + } catch (BreakException) { + } + } + + return self; + } + #endregion + + #region reverse!, reverse_each + + [RubyMethod("reverse!")] + public static RubyArray/*!*/ InPlaceReverse(RubyContext/*!*/ context, RubyArray/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + self.Reverse(); + return self; + } + + [RubyMethod("reverse_each")] + public static object ReverseEach(RubyContext/*!*/ context, BlockParam block, RubyArray/*!*/ self) { + Assert.NotNull(context, self); + + if (self.Count > 0 && block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + for (int i = self.Count - 1; i >= 0; --i) { + object result; + if (block.Yield(self[i], out result)) { + return result; + } + } + return self; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/BigNumOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/BigNumOps.cs new file mode 100644 index 0000000000..6743880074 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/BigNumOps.cs @@ -0,0 +1,863 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Math; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + /// + /// Bignum objects hold integers outside the range of Fixnum. Bignum objects are created automatically when integer calculations would otherwise overflow a Fixnum. When a calculation involving Bignum objects returns a result that will fit in a Fixnum, the result is automatically converted. + /// For the purposes of the bitwise operations and [], a Bignum is treated as if it were an infinite-length bitstring with 2s complement representation. + /// While Fixnum values are immediate, Bignum objects are notassignment and parameter passing work with references to objects, not the objects themselves. + /// + [RubyClass("Bignum", Inherits = typeof(Integer), Extends = typeof(BigInteger))] + public sealed class BignumOps { + + #region Arithmetic Operators, -@, abs, +, -, *, /, div, quo, **, %, modulo, divmod, remainder + + #region -@ + + /// + /// Unary minus (returns a new Bignum whose value is 0-self) + /// + /// 0 minus self + /// Normalizes to a Fixnum if necessary + [RubyMethod("-@")] + public static object Negate(BigInteger/*!*/ self) { + return Protocols.Normalize(BigInteger.Negate(self)); + } + + #endregion + + #region abs + + /// + /// Returns the absolute value of self + /// + /// self if self >= 0; -self if self < 0 + /// Normalizes to a Fixnum if necessary + [RubyMethod("abs")] + public static object Abs(BigInteger/*!*/ self) { + return Protocols.Normalize(self.Abs()); + } + + #endregion + + #region + + + /// + /// Adds self and other, where other is Bignum or Fixnum + /// + /// self + other + /// Normalizes to a Fixnum if necessary + [RubyMethod("+")] + public static object Add(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return Protocols.Normalize(self + other); + } + /// + /// Adds self and other, where other is Float + /// + /// self + other as Float + [RubyMethod("+")] + public static object Add(BigInteger/*!*/ self, double other) { + return self.ToFloat64() + other; + } + /// + /// Adds self and other, where other is not a Float, Fixnum or Bignum + /// + /// self + other + /// Coerces self and other using other.coerce(self) then dynamically invokes + + [RubyMethod("+")] + public static object Add(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Add); + } + + #endregion + + #region - + + /// + /// Subtracts other from self, where other is Bignum or Fixnum + /// + /// self - other + /// Normalizes to a Fixnum if necessary + [RubyMethod("-")] + public static object Subtract(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return Protocols.Normalize(self - other); + } + /// + /// Subtracts other from self, where other is Float + /// + /// self - other as Float + [RubyMethod("-")] + public static object Subtract(BigInteger/*!*/ self, double other) { + return self.ToFloat64() - other; + } + /// + /// Subtracts other from self, where other is not a Float, Fixnum or Bignum + /// + /// self - other + /// Coerces self and other using other.coerce(self) then dynamically invokes - + [RubyMethod("-")] + public static object Subtract(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Subtract); + } + + #endregion + + #region * + + /// + /// Multiplies self by other, where other is Bignum or Fixnum + /// + /// self * other + /// Normalizes to a Fixnum if necessary + [RubyMethod("*")] + public static object Multiply(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return Protocols.Normalize(self * other); + } + /// + /// Multiplies self by other, where other is Float + /// + /// self * other as Float + [RubyMethod("*")] + public static object Multiply(BigInteger/*!*/ self, double other) { + return self.ToFloat64() * other; + } + /// + /// Multiplies self by other, where other is not a Float, Fixnum or Bignum + /// + /// self * other + /// Coerces self and other using other.coerce(self) then dynamically invokes * + [RubyMethod("*")] + public static object Multiply(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Multiply); + } + + #endregion + + #region /, div + /// + /// Divides self by other, where other is Bignum or Fixnum + /// + /// self / other + /// Uses DivMod to do the division (directly). Normalizes to a Fixnum if necessary + [RubyMethod("/"), RubyMethod("div")] + public static object Divide(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return DivMod(self, other)[0]; + } + /// + /// Divides self by other, where other is Float + /// + /// self / other as Float + [RubyMethod("/"), RubyMethod("div")] + public static object Divide(BigInteger/*!*/ self, double other) { + return self.ToFloat64() / other; + } + /// + /// Divides self by other, where other is not a Float, Fixnum or Bignum + /// + /// self / other + /// Coerces self and other using other.coerce(self) then dynamically invokes / + [RubyMethod("/")] + public static object Divide(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Divide); + } + /// + /// Divides self by other, where other is not a Float, Fixnum or Bignum + /// + /// self.div(other) + /// Coerces self and other using other.coerce(self) then dynamically invokes div + [RubyMethod("div")] + public static object Div(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Div); + } + #endregion + + #region quo + /// + /// Returns the floating point result of dividing self by other, where other is Bignum or Fixnum. + /// + /// self divided by other as Float + /// Converts self and other to Float and then divides. + [RubyMethod("quo")] + public static object Quotient(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return Quotient(self, other.ToFloat64()); + } + /// + /// Returns the floating point result of dividing self by other, where other is Float. + /// + /// self divided by other as Float + /// Converts self to Float and then divides. + [RubyMethod("quo")] + public static object Quotient(BigInteger/*!*/ self, double other) { + return self.ToFloat64() / other; + } + /// + /// Returns the floating point result of dividing self by other, where other is not Bignum, Fixnum or Float. + /// + /// self divided by other as Float + /// Coerces self and other using other.coerce(self) then dynamically invokes quo + [RubyMethod("quo")] + public static object Quotient(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Quo); + } + #endregion + + #region ** + + /// + /// Raises self to the exponent power, where exponent is Bignum. + /// + /// self ** exponent as Float + /// Converts self and exponent to Float (directly) and then calls System.Math.Pow + [RubyMethod("**")] + public static object Power(RubyContext/*!*/ context, BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ exponent) { + context.ReportWarning("in a**b, b may be too big"); + double result = System.Math.Pow(self.ToFloat64(), exponent.ToFloat64()); + return result; + } + + /// + /// Raises self to the exponent power, where exponent is Fixnum + /// + /// self ** exponent + /// + /// Returns Bignum or Fixnum if exponent >= 0. + /// Returns Float if exponent < 0 + /// + [RubyMethod("**")] + public static object Power(BigInteger/*!*/ self, int exponent) { + // BigInteger doesn't handle negative exponents. + if (exponent < 0) { + return Power(self, (double)exponent); + } + return Protocols.Normalize(self.Power(exponent)); + } + + /// + /// Raises self to the exponent power, where exponent is Float + /// + /// self ** exponent as Float + /// Converts self to Float (directly) then calls System.Math.Pow + [RubyMethod("**")] + public static object Power(BigInteger/*!*/ self, double exponent) { + return System.Math.Pow(self.ToFloat64(), exponent); + } + + /// + /// Raises self to the exponent power, where exponent is not Fixnum, Bignum or Float + /// + /// self ** exponent + /// Coerces self and other using other.coerce(self) then dynamically invokes ** + [RubyMethod("**")] + public static object Power(RubyContext/*!*/ context, BigInteger/*!*/ self, object exponent) { + return Protocols.CoerceAndCall(context, self, exponent, LibrarySites.Power); + } + + #endregion + + #region modulo, % + + /// + /// Returns self modulo other, where other is Fixnum or Bignum. + /// + /// self modulo other, as Fixnum or Bignum + /// Calls divmod directly to get the modulus. + [RubyMethod("%"), RubyMethod("modulo")] + public static object Modulo(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + RubyArray result = DivMod(self, other); + return result[1]; + } + + /// + /// Returns self % other, where other is not Fixnum or Bignum. + /// + /// self % other, as Fixnum or Bignum + /// Coerces self and other using other.coerce(self) then dynamically invokes % + [RubyMethod("%")] + public static object ModuloOp(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.ModuloOp); + } + + /// + /// Returns self modulo other, where other is not Fixnum or Bignum. + /// + /// self modulo other, as Fixnum or Bignum + /// Coerces self and other using other.coerce(self) then dynamically invokes modulo + [RubyMethod("modulo")] + public static object Modulo(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Modulo); + } + + #endregion + + #region divmod + /// + /// Returns an array containing the quotient and modulus obtained by dividing self by other, where other is Fixnum or Bignum. + /// If q, r = x.divmod(y), then + /// q = floor(float(x)/float(y)) + /// x = q*y + r + /// + /// [self div other, self modulo other] as RubyArray + /// Normalizes div and mod to Fixnum as necessary + [RubyMethod("divmod")] + public static RubyArray DivMod(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + BigInteger mod; + BigInteger div = BigInteger.DivRem(self, other, out mod); + if (self.Sign != other.Sign && !mod.IsZero()) { + div = div - 1; + mod = mod + other; + } + return RubyOps.MakeArray2(Protocols.Normalize(div), Protocols.Normalize(mod)); + } + /// + /// Returns an array containing the quotient and modulus obtained by dividing self by other, where other is not Fixnum or Bignum. + /// If q, r = x.divmod(y), then + /// q = floor(float(x)/float(y)) + /// x = q*y + r + /// + /// [self div other, self modulo other] as RubyArray + /// Coerces self and other using other.coerce(self) then dynamically invokes divmod + [RubyMethod("divmod")] + public static RubyArray DivMod(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + return (RubyArray)Protocols.CoerceAndCall(context, self, other, LibrarySites.DivMod); + } + #endregion + + #region remainder + /// + /// Returns the remainder after dividing self by other, where other is Fixnum or Bignum. + /// + /// + /// -1234567890987654321.remainder(13731) #=> -6966 + /// + /// Fixnum or Bignum + [RubyMethod("remainder")] + public static object Remainder(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + BigInteger remainder; + BigInteger.DivRem(self, other, out remainder); + return Protocols.Normalize(remainder); + } + /// + /// Returns the remainder after dividing self by other, where other is not Fixnum or Bignum. + /// + /// + /// -1234567890987654321.remainder(13731.24) #=> -9906.22531493148 + /// + /// Float, Fixnum or Bignum + /// Coerces self and other using other.coerce(self) then dynamically invokes remainder + [RubyMethod("remainder")] + public static object Remainder(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Remainder); + } + #endregion + + #endregion + + #region Comparisons: <=>, ==, eql? + + #region <=> + + /// + /// Comparison operator, where other is Bignum or Fixnum. This is the basis for the tests in Comparable. + /// + /// + /// Returns -1, 0, or +1 depending on whether self is less than, equal to, or greater than other. + /// + [RubyMethod("<=>")] + public static int Compare(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return BigInteger.Compare(self, other); + } + + /// + /// Comparison operator, where other is Float. This is the basis for the tests in Comparable. + /// + /// + /// Returns -1, 0, or +1 depending on whether self is less than, equal to, or greater than other. + /// + /// + /// Converts self to Float and then directly invokes <=>. + /// Correctly copes if self is too big to fit into a Float, i.e. assumes self is +/-Infinity. + /// + [RubyMethod("<=>")] + public static object Compare(RubyContext/*!*/ context, BigInteger/*!*/ self, double other) { + return FloatOps.Compare(ToFloat(context, self), other); + } + + /// + /// Comparison operator, where other is not Bignum, Fixnum or Float. This is the basis for the tests in Comparable. + /// + /// + /// Returns -1, 0, or +1 depending on whether self is less than, equal to, or greater than other. + /// + /// + /// Dynamically invokes <=>. + /// + [RubyMethod("<=>")] + public static object Compare(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + return Protocols.CoerceAndCallCompare(context, self, other); + } + + #endregion + + #region == + /// + /// Returns true if other has the same value as self, where other is Fixnum or Bignum. + /// Contrast this with Bignum#eql?, which requires other to be a Bignum. + /// + /// true or false + [RubyMethod("==")] + public static bool Equal(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return self == other; + } + + /// + /// Returns true if other has the same value as self, where other is Float. + /// Contrast this with Bignum#eql?, which requires other to be a Bignum. + /// + /// true or false + /// Returns false if other is NaN. + [RubyMethod("==")] + public static bool Equal(BigInteger/*!*/ self, double other) { + if (System.Double.IsNaN(other)) return false; + return (double)self == other; + } + + /// + /// Returns true if other has the same value as self, where other is not Fixnum, Bignum or Float. + /// Contrast this with Bignum#eql?, which requires other to be a Bignum. + /// + /// true or false + /// Dynamically invokes other == self (i.e. swaps self and other around) + [RubyMethod("==")] + public static bool Equal(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + // If we can't convert then swap self and other and try again. + return Protocols.IsEqual(context, other, self); + } + + #endregion + + #region eql? + + /// + /// Returns true only if other is a Bignum with the same value as self. + /// Contrast this with Bignum#==, which performs type conversions. + /// + /// true if other is Bignum and self == other + [RubyMethod("eql?")] + public static bool Eql(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return self == other; + } + + /// + /// Returns true only if other is a Bignum with the same value as self, where other is Fixnum. + /// Contrast this with Bignum#==, which performs type conversions. + /// + /// false + /// + /// Always returns false since other is not Bignum. + /// This overload is necessary otherwise the int will be implicitly cast to BigInteger, + /// even though it should always then return false in that case since other should + /// be too small to be equal to self, it is just a waste of a conversion. + [RubyMethod("eql?")] + public static bool Eql(BigInteger/*!*/ self, int other) { + return false; + } + + /// + /// Returns true only if other is a Bignum with the same value as self, where other is not Bignum or Fixnum. + /// Contrast this with Bignum#==, which performs type conversions. + /// + /// false + /// Always returns false since other is not Bignum + [RubyMethod("eql?")] + public static bool Eql(BigInteger/*!*/ self, object other) { + return false; + } + + #endregion + + #endregion + + #region Bitwise Operators <<, >>, |, &, ^, ~, [] + + #region << + + /// + /// Shifts self to the left by other bits (or to the right if other is negative). + /// + /// self << other, as Bignum or Fixnum + /// + /// If self is negative we have to check for running out of bits, in which case we return -1. + /// This is because Bignum is supposed to look like it is stored in 2s complement format. + /// + [RubyMethod("<<")] + public static object LeftShift(BigInteger/*!*/ self, int other) { + BigInteger result = self << other; + result = ShiftOverflowCheck(self, result); + return Protocols.Normalize(result); + } + + [RubyMethod("<<")] + public static object LeftShift(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + // Dodgy error message but matches MRI + throw RubyExceptions.CreateRangeError("bignum too big to convert into long"); + } + + /// + /// Shifts self to the left by other bits (or to the right if other is negative). + /// + /// self << other, as Bignum or Fixnum + /// other is converted to an Integer by dynamically invoking self.to_int + [RubyMethod("<<")] + public static object LeftShift(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + int fixnum; + BigInteger bignum; + Protocols.ConvertToInteger(context, other, out fixnum, out bignum); + return ((object)bignum != null) ? LeftShift(self, bignum) : LeftShift(self, fixnum); + } + + #endregion + + #region >> + + /// + /// Shifts self to the right by other bits (or to the left if other is negative). + /// + /// self >> other, as Bignum or Fixnum + /// + /// If self is negative we have to check for running out of bits, in which case we return -1. + /// This is because Bignum is supposed to look like it is stored in 2s complement format. + /// + [RubyMethod(">>")] + public static object RightShift(BigInteger/*!*/ self, int other) { + BigInteger result = self >> other; + result = ShiftOverflowCheck(self, result); + return Protocols.Normalize(result); + } + + [RubyMethod(">>")] + public static object RightShift(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + if (self.IsNegative()) { + return -1; + } + return 0; + } + + /// + /// Shifts self to the left by other bits (or to the right if other is negative). + /// + /// self >> other, as Bignum or Fixnum + /// other is converted to an Integer by dynamically invoking self.to_int + [RubyMethod(">>")] + public static object RightShift(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + int fixnum; + BigInteger bignum; + Protocols.ConvertToInteger(context, other, out fixnum, out bignum); + return ((object)bignum != null) ? RightShift(self, bignum) : RightShift(self, fixnum); + } + + #endregion + + #region | + + [RubyMethod("|")] + public static object BitwiseOr(BigInteger/*!*/ self, int other) { + return Protocols.Normalize(self | other); + } + + [RubyMethod("|")] + public static object BitwiseOr(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return Protocols.Normalize(self | other); + } + + /// + /// Performs bitwise or between self and other, where other is not Fixnum or Bignum. + /// + /// other is dynamically converted to an Integer by other.to_int then | is invoked dynamically. E.g. self | (index.to_int) + [RubyMethod("|")] + public static object BitwiseOr(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + int fixnum; + BigInteger bignum; + Protocols.ConvertToInteger(context, other, out fixnum, out bignum); + return ((object)bignum != null) ? BitwiseOr(self, bignum) : BitwiseOr(self, fixnum); + } + + #endregion + + #region & + + [RubyMethod("&")] + public static object And(BigInteger/*!*/ self, int other) { + return Protocols.Normalize(self & other); + } + + [RubyMethod("&")] + public static object And(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return Protocols.Normalize(self & other); + } + + /// + /// Performs bitwise and between self and other, where other is not Fixnum or Bignum. + /// + /// other is dynamically converted to an Integer by other.to_int then & is invoked dynamically. E.g. self & (index.to_int) + [RubyMethod("&")] + public static object And(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + int fixnum; + BigInteger bignum; + Protocols.ConvertToInteger(context, other, out fixnum, out bignum); + return ((object)bignum != null) ? And(self, bignum) : And(self, fixnum); + } + + #endregion + + #region ^ + + [RubyMethod("^")] + public static object Xor(BigInteger/*!*/ self, int other) { + return Protocols.Normalize(self ^ other); + } + + [RubyMethod("^")] + public static object Xor(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return Protocols.Normalize(self ^ other); + } + + /// + /// Performs bitwise xor between self and other, where other is not Fixnum or Bignum. + /// + /// other is dynamically converted to an Integer by other.to_int then ^ is invoked dynamically. E.g. self ^ (index.to_int) + [RubyMethod("^")] + public static object Xor(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + int fixnum; + BigInteger bignum; + Protocols.ConvertToInteger(context, other, out fixnum, out bignum); + return ((object)bignum != null) ? Xor(self, bignum) : Xor(self, fixnum); + } + + #endregion + + #region ~ + + /// + /// Performs bitwise inversion on self. + /// + [RubyMethod("~")] + public static object Invert(BigInteger/*!*/ self) { + return Protocols.Normalize(~self); + } + + #endregion + + #region [] + + /// + /// Returns the Bit value at the reference index, where index is Fixnum + /// + /// + /// + /// a = 9**15 + /// 50.downto(0) do |n| + /// print a[n] + /// end + /// + /// produces: + /// + /// 000101110110100000111000011110010100111100010111001 + /// + /// + /// indexth bit in the (assumed) binary representation of self, where self[0] is the least significant bit. + /// Since representation is supposed to be 2s complement, we return always 1 if self is negative and index is greater than most signifcant bit in BigInteger + [RubyMethod("[]")] + public static int Bit(BigInteger/*!*/ self, int index) { + // If we are outside the range then return 0 ... + if (index < 0) return 0; + + int bytePos = index / 8; + int bitOffset = index % 8; + byte[] data = self.ToByteArray(); + + // ... or 1 if the index is too high and BigInteger is negative. + if (bytePos >= data.Length) return (self.Sign > 0) ? 0 : 1; + + return (data[bytePos] & (1 << bitOffset)) != 0 ? 1 : 0; + } + + /// + /// Returns the Bit value at the reference index, where index is Bignum + /// + /// + /// 0 if index is negative or self is positive + /// 1 otherwise + /// + /// + /// Since representation is supposed to be 2s complement and index must be extremely big, + /// we asssume we can always return 1 if self is negative and 0 otherwise + [RubyMethod("[]")] + public static int Bit(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ index) { + // BigIntegers as indexes are always going to be outside the range. + if (index.IsNegative() || self.IsPositive()) return 0; + return 1; + } + + /// + /// Returns the Bit value at the reference index, where index is not Fixnum or Bignum + /// + /// indexth bit in the (assumed) binary representation of self, where self[0] is the least significant bit. + /// index is dynamically converted to an Integer by index.to_int then [] is invoked dynamically. E.g. self[index.to_int] + [RubyMethod("[]")] + public static int Bit(RubyContext/*!*/ context, BigInteger/*!*/ self, object index) { + try { + object intIndex = Protocols.ConvertToInteger(context, index); + return LibrarySites.BitRef(context, self, intIndex); + } catch (FloatDomainError x) { + throw RubyExceptions.CreateRangeError("float " + x.Message + " out of range of Integer"); + } + } + + #endregion + + #endregion + + #region Conversion methods: coerce, to_f, to_s + + #region coerce + + /// + /// Attempts to coerce other to a Bignum. + /// + /// [other, self] as Bignums + [RubyMethod("coerce")] + public static RubyArray Coerce(BigInteger/*!*/ self, [NotNull]BigInteger/*!*/ other) { + return RubyOps.MakeArray2(other, self); + } + + /// + /// Attempts to coerce other to a Bignum, where other is not Fixnum or Bignum. + /// + /// For any value of other. + [RubyMethod("coerce")] + public static RubyArray Coerce(RubyContext/*!*/ context, BigInteger/*!*/ self, object other) { + throw RubyExceptions.CreateTypeError(String.Format("can't coerce {0} to Bignum", RubyUtils.GetClassName(context, other))); + } + + #endregion + + #region to_f + + /// + /// Converts self to a Float. If self doesnt fit in a Float, the result is infinity. + /// + /// self as a Float + [RubyMethod("to_f")] + public static double ToFloat(RubyContext/*!*/ context, BigInteger/*!*/ self) { + try { + return self.ToFloat64(); + } catch (OverflowException) { + // If the BigInteger is too big for a float then we return infinity. + context.ReportWarning("Bignum out of Float range"); + return self.Sign > 0 ? Double.PositiveInfinity : Double.NegativeInfinity; + } + } + + #endregion + + #region to_s + + /// + /// Returns a string containing the representation of self base 10. + /// + [RubyMethod("to_s")] + public static object ToString(BigInteger/*!*/ self) { + return MutableString.Create(self.ToString()); + } + + /// + /// Returns a string containing the representation of self base radix (2 through 36). + /// + /// An integer between 2 and 36 inclusive + [RubyMethod("to_s")] + public static object ToString(BigInteger/*!*/ self, uint radix) { + if (radix < 2 || radix > 36) { + throw RubyExceptions.CreateArgumentError(String.Format("illegal radix {0}", radix)); + } + + // TODO: Can we do the ToLower in BigInteger? + return MutableString.Create(self.ToString((uint)radix).ToLower()); + } + + #endregion + + #endregion + + #region hash + + /// + /// Compute a hash based on the value of self. + /// + [RubyMethod("hash")] + public static int Hash(BigInteger/*!*/ self) { + return self.GetHashCode(); + } + + #endregion + + #region size + + /// + /// Returns the number of bytes in the machine representation of self. + /// + /// + /// (256**10 - 1).size #=> 12 + /// (256**20 - 1).size #=> 20 + /// (256**40 - 1).size #=> 40 + /// + [RubyMethod("size")] + public static int Size(BigInteger/*!*/ self) { + //TODO: Should we expose the number of bytes per word in a BitInteger? + return self.GetBits().Length * 4; + } + + #endregion + + #region Helpers + + /// + /// Test for shift overflow on negative BigIntegers + /// + /// Value before shifting + /// Value after shifting + /// -1 if we overflowed, otherwise result + /// + /// Negative Bignums are supposed to look like they are stored in 2s complement infinite bit string, + /// a negative number should always have spare 1s available for on the left hand side for right shifting. + /// E.g. 8 == ...0001000; -8 == ...1110111, where the ... means that the left hand value is repeated indefinitely. + /// The test here checks whether we have overflowed into the infinite 1s. + /// [Arguably this should get factored into the BigInteger class.] + /// + private static BigInteger/*!*/ ShiftOverflowCheck(BigInteger/*!*/ self, BigInteger/*!*/ result) { + if (self.IsNegative() && result.IsZero()) { + return -1; + } + return result; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/BindingOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/BindingOps.cs new file mode 100644 index 0000000000..cca488878e --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/BindingOps.cs @@ -0,0 +1,24 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + [RubyClass("Binding", Extends = typeof(Binding))] + public static class BindingOps { + // dup + // clone + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/BuiltinsInitializer.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/BuiltinsInitializer.cs new file mode 100644 index 0000000000..37fbc0a1a0 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/BuiltinsInitializer.cs @@ -0,0 +1,20 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Builtins { + public sealed partial class BuiltinsInitializer : IronRuby.Builtins.LibraryInitializer { + + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ClassOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ClassOps.cs new file mode 100644 index 0000000000..bf11a4a305 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ClassOps.cs @@ -0,0 +1,77 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Builtins { + + [RubyClass("Class", Extends = typeof(RubyClass), Inherits = typeof(RubyModule))] + [UndefineMethod("extend_object")] + [UndefineMethod("append_features")] + [UndefineMethod("module_function")] + public sealed class ClassOps { + + #region Construction + + // factory defined in on RubyClass + + // Reinitialization. Not called when a factory/non-default ctor is called. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static void Reinitialize(BlockParam body, RubyClass/*!*/ self, [Optional]RubyClass superClass) { + // Class cannot be subclassed, so this can only be called directly on an already initialized class: + throw RubyExceptions.CreateTypeError("already initialized class"); + } + + [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)] + public static void InitializeCopy(RubyClass/*!*/ self, [NotNull]RubyClass/*!*/ other) { + self.InitializeClassCopy(other); + } + + + #endregion + + #region Private Instance Methods + + [RubyMethod("inherited", RubyMethodAttributes.PrivateInstance)] + public static void Inherited(RubyClass/*!*/ self, object subclass) { + // nop + } + + #endregion + + #region Public Instance Methods + + [RubyMethod("allocate")] + public static RuleGenerator/*!*/ GetInstanceAllocator() { + return new RuleGenerator(RuleGenerators.InstanceAllocator); + } + + [RubyMethod("new")] + public static RuleGenerator/*!*/ GetInstanceConstructor() { + return new RuleGenerator(RuleGenerators.InstanceConstructor); + } + + [RubyMethod("superclass")] + public static RubyClass GetSuperclass(RubyClass/*!*/ self) { + return self.SuperClass; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Comparable.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Comparable.cs new file mode 100644 index 0000000000..66fd230558 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Comparable.cs @@ -0,0 +1,65 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + [RubyModule("Comparable")] + public static class Comparable { + [RubyMethod("<")] + public static bool Less(RubyContext/*!*/ context, object self, object other) { + return Protocols.Compare(context, self, other) < 0; + } + + [RubyMethod("<=")] + public static bool LessOrEqual(RubyContext/*!*/ context, object self, object other) { + return Protocols.Compare(context, self, other) <= 0; + } + + [RubyMethod(">=")] + public static bool GreaterOrEqual(RubyContext/*!*/ context, object self, object other) { + return Protocols.Compare(context, self, other) >= 0; + } + + [RubyMethod(">")] + public static bool Greater(RubyContext/*!*/ context, object self, object other) { + return Protocols.Compare(context, self, other) > 0; + } + + [RubyMethod("==")] + public static object Equal(RubyContext/*!*/ context, object self, object other) { + // Short circuit long winded comparison if the objects are actually the same. + if (self == other) { + return true; + } + + // TODO: handle exceptions thrown from Compare() + // Compare may return null + object result = RubySites.Compare(context, self, other); + if (result is int) { + return (int)result == 0; + } else { + return null; + } + } + + [RubyMethod("between?")] + public static bool Between(RubyContext/*!*/ context, object self, object min, object max) { + return (!Less(context, self, min) && !Greater(context, self, max)); + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Dir.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Dir.cs new file mode 100644 index 0000000000..1a968d6431 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Dir.cs @@ -0,0 +1,380 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Text; +using Microsoft.Scripting.Actions; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + [RubyClass("Dir", Inherits = typeof(object)), Includes(typeof(Enumerable))] + public class RubyDir { + #region Private fields + + private readonly MutableString/*!*/ _dirName; + private readonly string[]/*!*/ _rawEntries; + private bool _closed; + + // _pos starts from -2 as ".", -1 as "..", + // 0 will be the first item from Directory.GetFileSystemEntries. + private int _pos; + + #endregion + + public RubyDir([NotNull]MutableString/*!*/ dirname) { + string strName = dirname.ConvertToString(); + try { + _rawEntries = Directory.GetFileSystemEntries(strName); + } catch (Exception ex) { + throw ToRubyException(ex, strName, DirectoryOperation.Open); + } + _dirName = MutableString.Create(NormalizePathSeparators(strName)); + _closed = false; + _pos = -2; + } + + #region Singleton Methods + + /// + /// raise a SystemCallError if the target directory does not exist + /// + /// 0 if no block is given; otherwise, the value of the block + [RubyMethod("chdir", RubyMethodAttributes.PublicSingleton)] + public static object ChangeDirectory(BlockParam block, object/*!*/ self, MutableString dir) { + string strDir = dir.ConvertToString(); + + if (block == null) { + SetCurrentDirectory(strDir); + return 0; + } else { + string current = Directory.GetCurrentDirectory(); + try { + SetCurrentDirectory(strDir); + object result; + block.Yield(dir, out result); + return result; + } finally { + SetCurrentDirectory(current); + } + } + } + + private static void SetCurrentDirectory(string/*!*/ dir) { + try { + Directory.SetCurrentDirectory(dir); + } catch (Exception e) { + throw ToRubyException(e, dir, DirectoryOperation.ChangeDir); + } + } + + [RubyMethod("chdir", RubyMethodAttributes.PublicSingleton)] + public static object ChangeDirectory(object self, MutableString/*!*/ dir) { + return ChangeDirectory(null, self, dir); + } + + /// + /// change the directory to the value of the environment variable HOME or LOGDIR + /// + /// + /// + [RubyMethod("chdir", RubyMethodAttributes.PublicSingleton)] + public static object ChangeDirectory(RubyContext/*!*/ context, object self) { +#if !SILVERLIGHT + string defaultDirectory = RubyFileOps.GetHomeDirectory(context); + if (defaultDirectory == null) + throw RubyExceptions.CreateArgumentError("HOME / USERPROFILE not set"); + + return ChangeDirectory(self, MutableString.Create(defaultDirectory)); +#else + throw new InvalidOperationException(); +#endif + } + + [RubyMethod("chroot", RubyMethodAttributes.PublicSingleton)] + public static int ChangeRoot(object self) { + throw new InvalidOperationException(); + } + + [RubyMethod("delete", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("rmdir", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("unlink", RubyMethodAttributes.PublicSingleton)] + public static int RemoveDirectory(object self, [NotNull]MutableString/*!*/ dirname) { + string strDir = dirname.ConvertToString(); + try { + Directory.Delete(strDir); + } catch (Exception ex) { + throw ToRubyException(ex, strDir, DirectoryOperation.Delete); + } + return 0; + } + + [RubyMethod("entries", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ GetEntries(object self, [NotNull]MutableString/*!*/ dirname) { + string strDir = dirname.ConvertToString(); + string[] rawEntries = null; + + try { + rawEntries = Directory.GetFileSystemEntries(strDir); + } catch (Exception ex) { + throw ToRubyException(ex, strDir, DirectoryOperation.Open); + } + + RubyArray ret = new RubyArray(rawEntries.Length + 2); + ret.Add(MutableString.Create(".")); + ret.Add(MutableString.Create("..")); + foreach (string entry in rawEntries) { + ret.Add(MutableString.Create(Path.GetFileName(entry))); + } + return ret; + } + + [RubyMethod("foreach", RubyMethodAttributes.PublicSingleton)] + public static object ForEach(BlockParam block, object self, [NotNull]MutableString/*!*/ dirname) { + // TODO: ??? block == nil + foreach (object entry in GetEntries(self, dirname)) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + // TODO: ??? as + object result; + if (block.Yield(entry as MutableString, out result)) { + return result; + } + } + + return null; + } + + [RubyMethod("getwd", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("pwd", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ GetCurrentDirectory(object self) { + return MutableString.Create(NormalizePathSeparators(Directory.GetCurrentDirectory())); + } + + #region glob + + [RubyMethod("glob", RubyMethodAttributes.PublicSingleton)] + public static object Glob([NotNull]BlockParam/*!*/ block, RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ pattern, [DefaultProtocol, Optional]int flags) { + + foreach (string fileName in IronRuby.Builtins.Glob.GlobResults(self.Context, pattern.ConvertToString(), flags)) { + object result; + if (block.Yield(MutableString.Create(fileName).TaintBy(pattern), out result)) { + return result; + } + } + return null; + } + + [RubyMethod("glob", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("[]", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ Glob(RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ pattern, [DefaultProtocol, Optional]int flags) { + + RubyArray result = new RubyArray(); + foreach (string fileName in IronRuby.Builtins.Glob.GlobResults(self.Context, pattern.ConvertToString(), flags)) { + result.Add(MutableString.Create(fileName).TaintBy(pattern)); + } + + return result; + } + + #endregion + + [RubyMethod("mkdir", RubyMethodAttributes.PublicSingleton)] + public static int MakeDirectory(object self, [NotNull]MutableString/*!*/ dirname, [Optional]object permissions) { + string strDir = dirname.ConvertToString(); + try { + Directory.CreateDirectory(strDir); + } catch (Exception ex) { + throw ToRubyException(ex, strDir, DirectoryOperation.Create); + } + return 0; + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(BlockParam block, object self, [NotNull]MutableString/*!*/ dirname) { + RubyDir rd = new RubyDir(dirname); + + try { + object result; + block.Yield(rd, out result); + return result; + } finally { + Close(rd); + } + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(object self, [NotNull]MutableString/*!*/ dirname) { + return new RubyDir(dirname); + } + + #endregion + + #region Public Instance Methods + + [RubyMethod("close")] + public static void Close(RubyDir/*!*/ self) { + self.ThrowIfClosed(); + + self._closed = true; + } + + [RubyMethod("each")] + public static RubyDir/*!*/ Each(BlockParam block, RubyDir/*!*/ self) { + self.ThrowIfClosed(); + + RubyDir.ForEach(block, self, self._dirName); + return self; + } + + [RubyMethod("path")] + public static MutableString/*!*/ GetPath(RubyDir/*!*/ self) { + self.ThrowIfClosed(); + + return self._dirName; + } + + [RubyMethod("pos")] + [RubyMethod("tell")] + public static int GetCurrentPosition(RubyDir/*!*/ self) { + self.ThrowIfClosed(); + + return self._pos + 2; + } + + /// + /// Synonym for Dir#seek, but returns the position parameter + /// + [RubyMethod("pos=")] + public static int SetPosition(RubyDir/*!*/ self, int pos) { + self.ThrowIfClosed(); + + self._pos = pos - 2; + return pos; + } + + [RubyMethod("read")] + public static MutableString/*!*/ Read(RubyDir/*!*/ self) { + self.ThrowIfClosed(); + + if (self._pos + 1 > self._rawEntries.Length) { + return null; + } + + MutableString ret; + if (self._pos == -2) { + ret = MutableString.Create("."); + } else if (self._pos == -1) { + ret = MutableString.Create(".."); + } else { + ret = MutableString.Create(Path.GetFileName(self._rawEntries[self._pos])); + } + self._pos++; + return ret; + } + + [RubyMethod("rewind")] + public static RubyDir/*!*/ Rewind(RubyDir/*!*/ self) { + self.ThrowIfClosed(); + + self._pos = -2; + return self; + } + + [RubyMethod("seek")] + public static RubyDir/*!*/ Seek(RubyDir/*!*/ self, int pos) { + self.ThrowIfClosed(); + + if (pos < 0) { + self._pos = -2; + } else if (pos > self._rawEntries.Length + 2) { + self._pos = self._rawEntries.Length; + } else { + self._pos = pos - 2; + } + return self; + } + + #endregion + + #region Helpers + + private void ThrowIfClosed() { + if (_closed) { + throw RubyExceptions.CreateIOError("closed directory"); + } + } + + private enum DirectoryOperation { + Delete, + Create, + Open, + ChangeDir, + } + + // TODO: to match the C-Ruby exception + private static Exception ToRubyException(Exception/*!*/ ex, string path, DirectoryOperation op) { + Assert.NotNull(ex); + + Type exceptionType = ex.GetType(); + + switch (op) { + case DirectoryOperation.ChangeDir: + return new Errno.InvalidError(path); + + case DirectoryOperation.Open: + return new Errno.NoEntryError(path); + + case DirectoryOperation.Delete: + if (ex is ArgumentException) { + return new Errno.InvalidError(path); + } + if (ex is IOException) { + return new Errno.AccessError(path); + } + break; + + case DirectoryOperation.Create: + if (ex is ArgumentException) { + return new Errno.InvalidError(path); + } + if (ex is IOException) { + return new Errno.ExistError(path); + } + break; + } + + // throw anyway + return RubyExceptions.CreateSystemCallError(String.Format("unknown scenario - {0}, {1}, {2}", exceptionType, path, op)); + } + + private static string/*!*/ NormalizePathSeparators(string/*!*/ path) { + return path.Replace("\\", "/"); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Enumerable.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Enumerable.cs new file mode 100644 index 0000000000..67832e2996 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Enumerable.cs @@ -0,0 +1,453 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using IronRuby.Runtime; +using Microsoft.Scripting.Runtime; +using Enumerator = IronRuby.StandardLibrary.Enumerator.Enumerable.Enumerator; + +namespace IronRuby.Builtins { + + // TODO: All of these methods use RubySites.Each, which is not ideal (shared DynamicSite). + // We could have one DynamicSite per method, but what we really want is for the + // "each" site to be merged into the calling site (e.g. maybe use ActionOnCallable) + [RubyModule("Enumerable")] + public static class Enumerable { + + private static object Each(RubyContext/*!*/ context, object self, Proc/*!*/ block) { + if (self is Enumerator) { + return ((Enumerator)self).Each(context, block); + } else { + return RubySites.Each(context, self, block); + } + } + + #region all?, any? + + [RubyMethod("all?")] + public static object TrueForAll(RubyContext/*!*/ context, BlockParam predicate, object self) { + return TrueForItems(context, predicate, self, true); + } + + [RubyMethod("any?")] + public static object TrueForAny(RubyContext/*!*/ context, BlockParam predicate, object self) { + return TrueForItems(context, predicate, self, false); + } + + private static object TrueForItems(RubyContext/*!*/ context, BlockParam predicate, object self, bool expected) { + bool result = expected; + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + if (predicate != null) { + if (predicate.Yield(item, out item)) { + return item; + } + } + + bool isTrue = Protocols.IsTrue(item); + if (isTrue != result) { + result = isTrue; + return selfBlock.Break(ScriptingRuntimeHelpers.BooleanToObject(isTrue)); + } + + return null; + })); + + return ScriptingRuntimeHelpers.BooleanToObject(result); + } + + #endregion + + #region collect, map + + [RubyMethod("collect")] + [RubyMethod("map")] + public static RubyArray Map(RubyContext/*!*/ context, BlockParam collector, object self) { + RubyArray result = new RubyArray(); + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + if (collector != null) { + if (collector.Yield(item, out item)) { + return item; + } + } + result.Add(item); + return null; + })); + return result; + } + + #endregion + + #region detect, find + + [RubyMethod("detect")] + [RubyMethod("find")] + public static object Find(RubyContext/*!*/ context, BlockParam predicate, object self, [Optional]object ifNone) { + object result = Missing.Value; + + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + if (predicate == null) { + throw RubyExceptions.NoBlockGiven(); + } + + object blockResult; + if (predicate.Yield(item, out blockResult)) { + result = blockResult; + return selfBlock.Break(blockResult); + } + + if (Protocols.IsTrue(blockResult)) { + result = item; + return selfBlock.Break(item); + } + + return null; + })); + + if (result == Missing.Value) { + if (ifNone == Missing.Value || ifNone == null) { + return null; + } + result = RubySites.Call(context, ifNone); + } + return result; + } + + #endregion + + #region each_with_index + + [RubyMethod("each_with_index")] + public static object EachWithIndex(RubyContext/*!*/ context, BlockParam/*!*/ block, object self) { + // for some reason each_with_index always checks for a block, even if there's nothing to yield + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + int index = 0; + + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + object blockResult; + if (block.Yield(item, index, out blockResult)) { + return blockResult; + } + index += 1; + return null; + })); + + return self; + } + + #endregion + + #region entries, to_a + + [RubyMethod("entries")] + [RubyMethod("to_a")] + public static RubyArray/*!*/ ToArray(RubyContext/*!*/ context, object self) { + RubyArray data = new RubyArray(); + + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + data.Add(item); + return null; + })); + + return data; + } + + #endregion + + #region find_all, select, reject + + [RubyMethod("find_all")] + [RubyMethod("select")] + public static RubyArray/*!*/ Select(RubyContext/*!*/ context, BlockParam predicate, object self) { + return Filter(context, predicate, self, true); + } + + [RubyMethod("reject")] + public static RubyArray/*!*/ Reject(RubyContext/*!*/ context, BlockParam predicate, object self) { + return Filter(context, predicate, self, false); + } + + private static RubyArray/*!*/ Filter(RubyContext/*!*/ context, BlockParam predicate, object self, bool acceptingValue) { + RubyArray result = new RubyArray(); + + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + if (predicate == null) { + throw RubyExceptions.NoBlockGiven(); + } + + object blockResult; + if (predicate.Yield(item, out blockResult)) { + return blockResult; + } + + // Check if the result is what we expect (use true to select, false to reject) + if (Protocols.IsTrue(blockResult) == acceptingValue) { + result.Add(item); + } + return null; + })); + + return result; + } + + #endregion + + #region grep + + [RubyMethod("grep")] + public static RubyArray Grep(RubyContext/*!*/ context, BlockParam action, object self, object pattern) { + RubyArray result = new RubyArray(); + + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + if (RubySites.CaseEqual(context, pattern, item)) { + if (action != null) { + if (action.Yield(item, out item)) { + return item; + } + } + result.Add(item); + } + return null; + })); + + return result; + } + + #endregion + + #region include?, member? + + [RubyMethod("include?")] + [RubyMethod("member?")] + public static bool Contains(RubyContext/*!*/ context, object self, object value) { + bool result = false; + + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + if (Protocols.IsEqual(context, item, value)) { + result = true; + return selfBlock.Break(result); + } + return null; + })); + + return result; + } + + #endregion + + #region inject + + [RubyMethod("inject")] + public static object Inject(RubyContext/*!*/ context, BlockParam operation, object self, [Optional]object initial) { + object result = initial; + + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + if (result == Missing.Value) { + result = item; + return null; + } + + if (operation == null) { + throw RubyExceptions.NoBlockGiven(); + } + + if (operation.Yield(result, item, out result)) { + return result; + } + + return null; + })); + + return result != Missing.Value ? result : null; + } + + #endregion + + #region max, min + + [RubyMethod("max")] + public static object GetMaximum(RubyContext/*!*/ context, BlockParam comparer, object self) { + return GetExtreme(context, comparer, self, -1/*look for max*/); + } + [RubyMethod("min")] + public static object GetMinimum(RubyContext/*!*/ context, BlockParam comparer, object self) { + return GetExtreme(context, comparer, self, 1/*look for min*/); + } + + private static object GetExtreme(RubyContext/*!*/ context, BlockParam comparer, object self, int comparisonValue) { + bool firstItem = true; + object result = null; + + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + // Check for first element + if (firstItem) { + result = item; + firstItem = false; + return null; + } + + int compareResult; + if (comparer != null) { + object blockResult; + if (comparer.Yield(result, item, out blockResult)) { + return blockResult; + } + + if (blockResult == null) { + throw RubyExceptions.MakeComparisonError(context, result, item); + } + + compareResult = Protocols.ConvertCompareResult(context, blockResult); + } else { + compareResult = Protocols.Compare(context, result, item); + } + + // Check if we have found the new minimum or maximum (-1 to select max, 1 to select min) + if (compareResult == comparisonValue) { + result = item; + } + + return null; + })); + return result; + } + #endregion + + #region partition + + [RubyMethod("partition")] + public static RubyArray/*!*/ Partition(RubyContext/*!*/ context, BlockParam predicate, object self) { + RubyArray trueSet = new RubyArray(); + RubyArray falseSet = new RubyArray(); + + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + if (predicate == null) { + throw RubyExceptions.NoBlockGiven(); + } + + object blockResult; + if (predicate.Yield(item, out blockResult)) { + return blockResult; + } + + if (Protocols.IsTrue(blockResult)) { + trueSet.Add(item); + } else { + falseSet.Add(item); + } + + return null; + })); + + RubyArray pair = new RubyArray(2); + pair.Add(trueSet); + pair.Add(falseSet); + return pair; + } + + #endregion + + #region sort, sort_by + + [RubyMethod("sort")] + public static object Sort(RubyContext/*!*/ context, BlockParam keySelector, object self) { + return ArrayOps.SortInPlace(context, keySelector, ToArray(context, self)); + } + + [RubyMethod("sort_by")] + public static RubyArray/*!*/ SortBy(RubyContext/*!*/ context, BlockParam keySelector, object self) { + // collect key, value pairs + List> keyValuePairs = new List>(); + + // Collect the key, value pairs + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + if (keySelector == null) { + throw RubyExceptions.NoBlockGiven(); + } + + object key; + if (keySelector.Yield(item, out key)) { + return key; + } + + keyValuePairs.Add(new KeyValuePair(key, item)); + return null; + })); + + // sort by keys + keyValuePairs.Sort(delegate(KeyValuePair x, KeyValuePair y) { + return Protocols.Compare(context, x.Key, y.Key); + }); + + // return values + RubyArray result = new RubyArray(keyValuePairs.Count); + foreach (KeyValuePair pair in keyValuePairs) { + result.Add(pair.Value); + } + + return result; + } + + #endregion + + #region zip + + [RubyMethod("zip")] + public static RubyArray/*!*/ Zip(RubyContext/*!*/ context, BlockParam block, object self, [NotNull]params object[] args) { + RubyArray results = (block == null) ? new RubyArray() : null; + + // Call to_a on each argument + IList[] otherArrays = new IList[args.Length]; + for (int i = 0; i < args.Length; i++) { + otherArrays[i] = Protocols.ConvertToArray(context, args[i]); + } + + int index = 0; + Each(context, self, Proc.Create(context, delegate(BlockParam/*!*/ selfBlock, object item) { + // Collect items + RubyArray array = new RubyArray(otherArrays.Length + 1); + array.Add(item); + foreach (IList otherArray in otherArrays) { + if (index < otherArray.Count) { + array.Add(otherArray[index]); + } else { + array.Add(null); + } + } + + index += 1; + + if (block != null) { + object blockResult; + if (block.Yield(array, out blockResult)) { + return blockResult; + } + } else { + results.Add(array); + } + return null; + })); + + return results; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/EnvironmentSingletonOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/EnvironmentSingletonOps.cs new file mode 100644 index 0000000000..2295c0c394 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/EnvironmentSingletonOps.cs @@ -0,0 +1,331 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + /// + /// ENV singleton trait. + /// + [RubyConstant("ENV")] + [RubySingleton, Includes(typeof(Enumerable))] + public static class EnvironmentSingletonOps { + + #region Public Instance Methods + + [RubyMethod("[]", RubyMethodAttributes.PublicInstance)] + [RubyMethod("fetch", RubyMethodAttributes.PublicInstance)] + public static MutableString GetVariable(RubyContext/*!*/ context, object/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ name) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + string value = pal.GetEnvironmentVariable(name.ConvertToString()); + return (value != null) ? MutableString.Create(value) : null; + } + + [RubyMethod("[]=", RubyMethodAttributes.PublicInstance)] + [RubyMethod("store", RubyMethodAttributes.PublicInstance)] + public static MutableString SetVariable(RubyContext/*!*/ context, object/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ name, [DefaultProtocol]MutableString value) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + pal.SetEnvironmentVariable(name.ConvertToString(), (value != null) ? value.ConvertToString() : null); + return value; + } + + [RubyMethod("clear")] + public static object Clear(RubyContext/*!*/ context, object/*!*/ self) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + foreach (DictionaryEntry entry in pal.GetEnvironmentVariables()) { + pal.SetEnvironmentVariable(entry.Key.ToString(), null); + } + return self; + } + + [RubyMethod("delete")] + public static object Delete(RubyContext/*!*/ context, object/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ name) { + MutableString result = GetVariable(context, self, name); + if (result != null) { + SetVariable(context, self, name, null); + } + return result; + } + + [RubyMethod("delete_if")] + [RubyMethod("reject!")] + public static object DeleteIf(RubyContext/*!*/ context, BlockParam block, object/*!*/ self) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + IDictionary variables = pal.GetEnvironmentVariables(); + if (variables.Count > 0 && block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + foreach (DictionaryEntry entry in variables) { + MutableString key = MutableString.Create(entry.Key.ToString()).Freeze(); + MutableString value = MutableString.Create(entry.Value.ToString()).Freeze(); + object result; + if (block.Yield(key, value, out result)) { + return result; + } + + if (RubyOps.IsTrue(result)) { + SetVariable(context, self, key, null); + } + } + return self; + } + + [RubyMethod("each")] + [RubyMethod("each_pair")] + public static object Each(RubyContext/*!*/ context, BlockParam block, object/*!*/ self) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + IDictionary variables = pal.GetEnvironmentVariables(); + if (variables.Count > 0 && block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + foreach (DictionaryEntry entry in variables) { + RubyArray array = new RubyArray(2); + array.Add(MutableString.Create(entry.Key.ToString()).Freeze()); + array.Add(MutableString.Create(entry.Value.ToString()).Freeze()); + object result; + if (block.Yield(array, out result)) { + return result; + } + } + return self; + } + + [RubyMethod("each_key")] + public static object EachKey(RubyContext/*!*/ context, BlockParam block, object/*!*/ self) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + IDictionary variables = pal.GetEnvironmentVariables(); + if (variables.Count > 0 && block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + foreach (DictionaryEntry entry in variables) { + MutableString name = MutableString.Create(entry.Key.ToString()).Freeze(); + object result; + if (block.Yield(name, out result)) { + return result; + } + } + return self; + } + + [RubyMethod("each_value")] + public static object EachValue(RubyContext/*!*/ context, BlockParam block, object/*!*/ self) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + IDictionary variables = pal.GetEnvironmentVariables(); + if (variables.Count > 0 && block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + foreach (DictionaryEntry entry in variables) { + MutableString value = MutableString.Create(entry.Value.ToString()).Freeze(); + object result; + if (block.Yield(value, out result)) { + return result; + } + } + return self; + } + + [RubyMethod("empty?")] + public static bool IsEmpty(RubyContext/*!*/ context, object/*!*/ self) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + return (pal.GetEnvironmentVariables().Count == 0); + } + + [RubyMethod("has_key?")] + [RubyMethod("include?")] + [RubyMethod("key?")] + public static bool HasKey(RubyContext/*!*/ context, object/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ key) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + return pal.GetEnvironmentVariable(key.ConvertToString()) != null; + } + + [RubyMethod("has_value?")] + [RubyMethod("value?")] + public static bool HasValue(RubyContext/*!*/ context, object/*!*/ self, object value) { + // MRI doesn't use CastToString conversion here: + var strValue = value as MutableString; + if (value == null) { + return false; + } + + var clrStrValue = strValue.ConvertToString(); + PlatformAdaptationLayer pal = context.DomainManager.Platform; + foreach (DictionaryEntry entry in pal.GetEnvironmentVariables()) { + if (clrStrValue.Equals(entry.Value)) { + return true; + } + } + return false; + } + + [RubyMethod("index")] + public static MutableString Index(RubyContext/*!*/ context, object/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ value) { + string strValue = value.ConvertToString(); + PlatformAdaptationLayer pal = context.DomainManager.Platform; + foreach (DictionaryEntry entry in pal.GetEnvironmentVariables()) { + if (strValue.Equals(entry.Value)) { + return MutableString.Create(entry.Key.ToString()).Freeze(); + } + } + return null; + } + + [RubyMethod("indices")] + public static RubyArray/*!*/ Indices(RubyContext/*!*/ context, object/*!*/ self, [NotNull]params object[]/*!*/ keys) { + context.ReportWarning("ENV.indices is deprecated; use ENV.values_at"); + return ValuesAt(context, self, keys); + } + + [RubyMethod("indexes")] + public static RubyArray/*!*/ Index(RubyContext/*!*/ context, object/*!*/ self, [NotNull]params object[]/*!*/ keys) { + context.ReportWarning("ENV.indexes is deprecated; use ENV.values_at"); + return ValuesAt(context, self, keys); + } + + [RubyMethod("inspect")] + public static MutableString/*!*/ Inspect(RubyContext/*!*/ context, object/*!*/ self) { + return HashOps.Inspect(context, ToHash(context, self)); + } + + [RubyMethod("invert")] + public static Hash/*!*/ Invert(RubyContext/*!*/ context, object/*!*/ self) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + Hash result = new Hash(context); + foreach (DictionaryEntry entry in pal.GetEnvironmentVariables()) { + result.Add(MutableString.Create(entry.Value.ToString()).Freeze(), MutableString.Create(entry.Key.ToString()).Freeze()); + } + return result; + } + + [RubyMethod("keys")] + public static RubyArray/*!*/ Keys(RubyContext/*!*/ context, object/*!*/ self) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + IDictionary variables = pal.GetEnvironmentVariables(); + RubyArray result = new RubyArray(variables.Count); + foreach (DictionaryEntry entry in variables) { + result.Add(MutableString.Create(entry.Key.ToString()).Freeze()); + } + return result; + } + + [RubyMethod("length")] + [RubyMethod("size")] + public static int Length(RubyContext/*!*/ context, object/*!*/ self) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + return pal.GetEnvironmentVariables().Count; + } + + [RubyMethod("rehash")] + public static object Rehash(object/*!*/ self) { + return null; + } + + private static List> ExtractHash(RubyContext/*!*/ context, Hash/*!*/ values) { + var result = new List>(values.Count); + foreach (var pair in values) { + result.Add(new KeyValuePair( + Protocols.CastToString(context, pair.Key).ToString(), + Protocols.CastToString(context, pair.Value).ToString() + )); + } + return result; + } + + private static void Update(RubyContext/*!*/ context, List>/*!*/ values) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + foreach (var pair in values) { + pal.SetEnvironmentVariable(pair.Key, pair.Value); + } + } + + [RubyMethod("replace")] + public static object/*!*/ Replace(RubyContext/*!*/ context, object/*!*/ self, [NotNull]Hash/*!*/ values) { + Clear(context, self); + Update(context, ExtractHash(context, values)); + return self; + } + + [RubyMethod("shift")] + public static object Shift(RubyContext/*!*/ context, object/*!*/ self) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + IDictionary variables = pal.GetEnvironmentVariables(); + if (variables.Count == 0) { + return null; + } + RubyArray result = new RubyArray(2); + foreach (DictionaryEntry entry in pal.GetEnvironmentVariables()) { + string key = entry.Key.ToString(); + result.Add(MutableString.Create(key).Freeze()); + result.Add(MutableString.Create(entry.Value.ToString()).Freeze()); + pal.SetEnvironmentVariable(key, null); + break; + } + return result; + } + + [RubyMethod("to_hash")] + public static Hash/*!*/ ToHash(RubyContext/*!*/ context, object/*!*/ self) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + Hash result = new Hash(context); + foreach (DictionaryEntry entry in pal.GetEnvironmentVariables()) { + result.Add(MutableString.Create(entry.Key.ToString()).Freeze(), MutableString.Create(entry.Value.ToString()).Freeze()); + } + return result; + } + + [RubyMethod("to_s")] + public static MutableString/*!*/ ToString(object/*!*/ self) { + return MutableString.Create("ENV"); + } + + [RubyMethod("update")] + public static object/*!*/ Update(RubyContext/*!*/ context, object/*!*/ self, [NotNull]Hash/*!*/ values) { + Update(context, ExtractHash(context, values)); + return self; + } + + + [RubyMethod("values")] + public static RubyArray/*!*/ Values(RubyContext/*!*/ context, object/*!*/ self) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + IDictionary variables = pal.GetEnvironmentVariables(); + RubyArray result = new RubyArray(variables.Count); + foreach (DictionaryEntry entry in variables) { + result.Add(MutableString.Create(entry.Value.ToString()).Freeze()); + } + return result; + } + + [RubyMethod("values_at")] + public static RubyArray/*!*/ ValuesAt(RubyContext/*!*/ context, object/*!*/ self, [NotNull]params object[]/*!*/ keys) { + RubyArray result = new RubyArray(keys.Length); + foreach (MutableString key in Protocols.CastToStrings(context, keys)) { + result.Add(GetVariable(context, self, key)); + } + return result; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Errno.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Errno.cs new file mode 100644 index 0000000000..b14c6a09fa --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Errno.cs @@ -0,0 +1,219 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + [RubyModule("Errno")] + public static class Errno { + // Errno.constants.sort + + internal static string/*!*/ MakeMessage(string message, string/*!*/ baseMessage) { + Assert.NotNull(baseMessage); + return (message != null) ? String.Concat(baseMessage, " - ", message) : baseMessage; + } + + internal static string/*!*/ MakeMessage(ref MutableString message, string/*!*/ baseMessage) { + Assert.NotNull(baseMessage); + string result = MakeMessage(message != null ? message.ConvertToString() : null, baseMessage); + message = MutableString.Create(result); + return result; + } + + [RubyClass("EADDRINUSE"), Serializable] + public class AddressInUseError : ExternalException { + private const string/*!*/ M = "Domain error"; + + public AddressInUseError() : this(null, null) { } + public AddressInUseError(string message) : this(message, null) { } + public AddressInUseError(string message, Exception inner) : base(MakeMessage(message, M), inner) { } + public AddressInUseError(MutableString message) : base(MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); } + +#if !SILVERLIGHT + protected AddressInUseError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyClass("EDOM"), Serializable] + public class DomainError : ExternalException { + private const string/*!*/ M = "Domain error"; + + public DomainError() : this(null, null) { } + public DomainError(string message) : this(message, null) { } + public DomainError(string message, Exception inner) : base(MakeMessage(message, M), inner) { } + public DomainError(MutableString message) : base(MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); } + +#if !SILVERLIGHT + protected DomainError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyClass("EINVAL"), Serializable] + public class InvalidError : ExternalException { + private const string/*!*/ M = "Invalid argument"; + + public InvalidError() : this(null, null) { } + public InvalidError(string message) : this(message, null) { } + public InvalidError(string message, Exception inner) : base(MakeMessage(message, M), inner) { } + public InvalidError(MutableString message) : base(MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); } + +#if !SILVERLIGHT + protected InvalidError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyClass("ENOENT"), Serializable] + public class NoEntryError : ExternalException { + private const string/*!*/ M = "No such file or directory"; + + public NoEntryError() : this(null, null) { } + public NoEntryError(string message) : this(message, null) { } + public NoEntryError(string message, Exception inner) : base(MakeMessage(message, M), inner) { } + public NoEntryError(MutableString message) : base(MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); } + +#if !SILVERLIGHT + protected NoEntryError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + + [RubyClass("ENOTDIR"), Serializable] + public class NotDirectoryError : ExternalException { + private const string/*!*/ M = "Not a directory"; + + public NotDirectoryError() : this(null, null) { } + public NotDirectoryError(string message) : this(message, null) { } + public NotDirectoryError(string message, Exception inner) : base(MakeMessage(message, M), inner) { } + public NotDirectoryError(MutableString message) : base(MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); } + +#if !SILVERLIGHT + protected NotDirectoryError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyClass("EACCES"), Serializable] + public class AccessError : ExternalException { + private const string/*!*/ M = "Permission denied"; + + public AccessError() : this(null, null) { } + public AccessError(string message) : this(message, null) { } + public AccessError(string message, Exception inner) : base(MakeMessage(message, M), inner) { } + public AccessError(MutableString message) : base(MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); } + +#if !SILVERLIGHT + protected AccessError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyClass("EEXIST"), Serializable] + public class ExistError : ExternalException { + private const string/*!*/ M = "File exists"; + + public ExistError() : this(null, null) { } + public ExistError(string message) : this(message, null) { } + public ExistError(string message, Exception inner) : base(MakeMessage(message, M), inner) { } + public ExistError(MutableString message) : base(MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); } + +#if !SILVERLIGHT + protected ExistError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyClass("EBADF"), Serializable] + public class BadFileDescriptorError : ExternalException { + private const string/*!*/ M = "Bad file descriptor"; + + public BadFileDescriptorError() : this(null, null) { } + public BadFileDescriptorError(string message) : this(message, null) { } + public BadFileDescriptorError(string message, Exception inner) : base(MakeMessage(message, M), inner) { } + public BadFileDescriptorError(MutableString message) : base(MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); } + +#if !SILVERLIGHT + protected BadFileDescriptorError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyClass("EPIPE"), Serializable] + public class PipeError : ExternalException { + private const string/*!*/ M = "Broken pipe"; + + public PipeError() : this(null, null) { } + public PipeError(string message) : this(message, null) { } + public PipeError(string message, Exception inner) : base(MakeMessage(message, M), inner) { } + public PipeError(MutableString message) : base(MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); } + +#if !SILVERLIGHT + protected PipeError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyClass("ENOTCONN"), Serializable] + public class NotConnectedError : ExternalException { + private const string/*!*/ M = "A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied."; + + public NotConnectedError() : this(null, null) { } + public NotConnectedError(string message) : this(message, null) { } + public NotConnectedError(string message, Exception inner) : base(MakeMessage(message, M), inner) { } + public NotConnectedError(MutableString message) : base(MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); } + +#if !SILVERLIGHT + protected NotConnectedError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyClass("ECONNRESET"), Serializable] + public class ConnectionResetError : ExternalException { + private const string/*!*/ M = "An existing connection was forcibly closed by the remote host."; + + public ConnectionResetError() : this(null, null) { } + public ConnectionResetError(string message) : this(message, null) { } + public ConnectionResetError(string message, Exception inner) : base(MakeMessage(message, M), inner) { } + public ConnectionResetError(MutableString message) : base(MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); } + +#if !SILVERLIGHT + protected ConnectionResetError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyClass("ECONNABORTED"), Serializable] + public class ConnectionAbortError : ExternalException { + private const string/*!*/ M = "An established connection was aborted by the software in your host machine."; + + public ConnectionAbortError() : this(null, null) { } + public ConnectionAbortError(string message) : this(message, null) { } + public ConnectionAbortError(string message, Exception inner) : base(MakeMessage(message, M), inner) { } + public ConnectionAbortError(MutableString message) : base(MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); } + +#if !SILVERLIGHT + protected ConnectionAbortError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ExceptionOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ExceptionOps.cs new file mode 100644 index 0000000000..01a79159a8 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ExceptionOps.cs @@ -0,0 +1,182 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Dynamic.Binders; +using System.Diagnostics; +using System.Reflection; +using IronRuby.Compiler.Generation; + +using Ast = System.Linq.Expressions.Expression; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using System.Linq.Expressions; + +namespace IronRuby.Builtins { + + // Exception + // -- fatal + // -- NoMemoryError + // -- ScriptError + // ---- LoadError + // ---- NotImplementedError + // ---- SyntaxError + // -- SignalException (not supported) + // ---- Interrupt (not supported) + // -- StandardError + // ---- ArgumentError + // ---- IOError + // ------ EOFError + // ---- IndexError + // ---- LocalJumpError + // ---- NameError + // ------ NoMethodError + // ---- RangeError + // ------ FloatDomainError + // ---- RegexpError + // ---- RuntimeError + // ---- SecurityError + // ---- SystemCallError + // ------ system-dependent-exceptions Errno::XXX + // ---- SystemStackError + // ---- ThreadError + // ---- TypeError + // ---- ZeroDivisionError + // -- SystemExit + [RubyException("Exception", Extends = typeof(Exception))] + public static class ExceptionOps { + + #region Construction + + [Emitted] + public static string/*!*/ GetClrMessage(RubyClass/*!*/ exceptionClass, object message) { + return RubyExceptionData.GetClrMessage(message, exceptionClass.Name); + } + + [Emitted] + public static Exception/*!*/ ReinitializeException(Exception/*!*/ self, object/*!*/ message) { + var instance = RubyExceptionData.GetInstance(self); + instance.Backtrace = null; + instance.Message = message; + return self; + } + + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static Exception/*!*/ ReinitializeException(RubyContext/*!*/ context, Exception/*!*/ self, [DefaultParameterValue(null)]object message) { + return ReinitializeException(self, message ?? context.GetClassOf(self).Name); + } + + [RubyMethod("exception", RubyMethodAttributes.PublicSingleton)] + public static RuleGenerator/*!*/ CreateException() { + return new RuleGenerator(RuleGenerators.InstanceConstructor); + } + + #endregion + + #region Public Instance Methods + + [RubyMethod("backtrace", RubyMethodAttributes.PublicInstance)] + public static RubyArray GetBacktrace(Exception/*!*/ self) { + return RubyExceptionData.GetInstance(self).Backtrace; + } + + [RubyMethod("set_backtrace", RubyMethodAttributes.PublicInstance)] + public static RubyArray/*!*/ SetBacktrace(Exception/*!*/ self, [NotNull]MutableString/*!*/ backtrace) { + return RubyExceptionData.GetInstance(self).Backtrace = RubyArray.Create(backtrace); + } + + [RubyMethod("set_backtrace", RubyMethodAttributes.PublicInstance)] + public static RubyArray SetBacktrace(Exception/*!*/ self, RubyArray backtrace) { + if (backtrace != null && !CollectionUtils.TrueForAll(backtrace, (item) => item is MutableString)) { + throw RubyExceptions.CreateTypeError("backtrace must be Array of String"); + } + + return RubyExceptionData.GetInstance(self).Backtrace = backtrace; + } + + // signature: (Exception! self, [Optional]object exceptionArg) : Exception! + [RubyMethod("exception", RubyMethodAttributes.PublicInstance)] + public static RuleGenerator/*!*/ GetException() { + return new RuleGenerator((metaBuilder, args, name) => { + Debug.Assert(args.Target is Exception); + + // 1 optional parameter (exceptionArg): + var argsBuilder = new ArgsBuilder(0, 0, 1, false); + argsBuilder.AddCallArguments(metaBuilder, args); + + if (!metaBuilder.Error) { + if (argsBuilder.ExplicitArgumentCount == 0) { + metaBuilder.Result = args.TargetExpression; + } else { + RubyClass cls = args.RubyContext.GetClassOf(args.Target); + var classExpression = Ast.Constant(cls); + args.SetTarget(classExpression, cls); + + ParameterExpression messageVariable = null; + + // ReinitializeException(new (GetClrMessage(, #message = )), #message) + metaBuilder.Result = Ast.Call(null, new Func(ReinitializeException).Method, + cls.MakeAllocatorCall(args, () => + Ast.Call(null, new Func(GetClrMessage).Method, + classExpression, + Ast.Assign(messageVariable = metaBuilder.GetTemporary(typeof(object), "#message"), AstFactory.Box(argsBuilder[0])) + ) + ), + messageVariable ?? AstFactory.Box(argsBuilder[0]) + ); + } + } + }); + } + + [RubyMethod("message")] + public static object GetMessage(Exception/*!*/ self) { + return RubyExceptionData.GetInstance(self).Message; + } + + [RubyMethod("to_s")] + [RubyMethod("to_str")] + public static MutableString/*!*/ GetMessage(RubyContext/*!*/ context, Exception/*!*/ self) { + return Protocols.AsString(context, GetMessage(self)); + } + + [RubyMethod("inspect", RubyMethodAttributes.PublicInstance)] + public static MutableString/*!*/ Inspect(RubyContext/*!*/ context, Exception/*!*/ self) { + + object message = RubyExceptionData.GetInstance(self).Message; + string className = RubyUtils.GetClassName(context, self); + + MutableString result = MutableString.CreateMutable(); + result.Append("#<"); + result.Append(className); + result.Append(": "); + if (message != null) { + result.Append(KernelOps.Inspect(context, message)); + } else { + result.Append(className); + } + result.Append('>'); + return result; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Exceptions.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Exceptions.cs new file mode 100644 index 0000000000..575bb4674d --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Exceptions.cs @@ -0,0 +1,225 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Security; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + [RubyException("NoMemoryError"), Serializable] + public class NoMemoryError : Exception { + public NoMemoryError() : this(null, null) { } + public NoMemoryError(string message): this(message, null) { } + public NoMemoryError(string message, Exception inner) : base(message ?? "NoMemoryError", inner) { } + +#if !SILVERLIGHT + protected NoMemoryError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyException("EOFError"), Serializable] + public class EOFError : IOException { + public EOFError() : this(null, null) { } + public EOFError(string message): this(message, null) { } + public EOFError(string message, Exception inner) : base(message ?? "EOFError", inner) { } + +#if !SILVERLIGHT + protected EOFError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyException("FloatDomainError"), Serializable] + public class FloatDomainError : ArgumentOutOfRangeException { + public FloatDomainError() : this(null, null) { } + public FloatDomainError(string message) : this(message, null) { } + public FloatDomainError(string message, Exception inner) : base(message ?? "FloatDomainError", inner) { } + +#if !SILVERLIGHT + protected FloatDomainError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyException("RuntimeError"), Serializable] + public class RuntimeError : SystemException { + public RuntimeError() : this(null, null) { } + public RuntimeError(string message): this(message, null) { } + public RuntimeError(string message, Exception inner) : base(message ?? "RuntimeError", inner) { } + +#if !SILVERLIGHT + protected RuntimeError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyException("ThreadError"), Serializable] + public class ThreadError : SystemException { + public ThreadError() : this(null, null) { } + public ThreadError(string message): this(message, null) { } + public ThreadError(string message, Exception inner) : base(message ?? "ThreadError", inner) { } + +#if !SILVERLIGHT + protected ThreadError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyException("SystemExit", Extends = typeof(SystemExit))] + public class SystemExitOps : Exception { + [RubyMethod("status")] + public static int GetStatus(SystemExit/*!*/ self) { + return self.Status; + } + + [RubyMethod("success?")] + public static bool IsSuccessful(SystemExit/*!*/ self) { + return self.Status == 0; + } + + [RubyConstructor] + public static SystemExit/*!*/ Factory(RubyClass/*!*/ self, object message) { + return Factory(self, 0, message); + } + + [RubyConstructor] + public static SystemExit/*!*/ Factory(RubyClass/*!*/ self, [Optional]int status, [DefaultParameterValue(null)]object message) { + SystemExit result = new SystemExit(status, RubyExceptionData.GetClrMessage(message, "SystemExit")); + RubyExceptionData.InitializeException(result, message); + return result; + } + } + + [RubyException("SignalException"), Serializable] + public class SignalException : Exception { + public SignalException() : this(null, null) { } + public SignalException(string message): this(message, null) { } + public SignalException(string message, Exception inner) : base(message ?? "SignalException", inner) { } + +#if !SILVERLIGHT + protected SignalException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyException("Interrupt", Inherits = typeof(SignalException)), Serializable] + public class Interrupt : Exception { + public Interrupt() : this(null, null) { } + public Interrupt(string message): this(message, null) { } + public Interrupt(string message, Exception inner) : base(message ?? "Interrupt", inner) { } + +#if !SILVERLIGHT + protected Interrupt(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [RubyException("LocalJumpError", Extends = typeof(LocalJumpError))] + public static class LocalJumpErrorOps { + } + + [RubyException("ScriptError", Extends = typeof(ScriptError))] + public static class ScriptErrorOps { + } + + [RubyException("NotImplementedError", Extends = typeof(NotImplementedError))] + public static class NotImplementedErrorOps { + } + + [RubyException("LoadError", Extends = typeof(LoadError))] + public static class LoadErrorOps { + } + + [RubyException("RegexpError", Extends = typeof(RegexpError))] + public static class RegexpErrorOps { + } + + [RubyException("SyntaxError", Extends = typeof(SyntaxError))] + public static class SyntaxErrorOps { + } + + [RubyException("SystemStackError", Extends = typeof(SystemStackError), Inherits = typeof(SystemException))] + public static class SystemStackErrorOps { + } + + [RubyException("StandardError", Extends = typeof(SystemException), Inherits = typeof(Exception))] + public static class SystemExceptionOps { + } + + [RubyException("ArgumentError", Extends = typeof(ArgumentException), Inherits = typeof(SystemException))] + [HideMethod("message")] + public static class ArgumentErrorOps { + } + + [RubyException("IOError", Extends = typeof(IOException), Inherits = typeof(SystemException))] + public static class IOErrorOps { + } + + [RubyException("IndexError", Extends = typeof(IndexOutOfRangeException), Inherits = typeof(SystemException))] + public static class IndexErrorOps { + } + + [RubyException("RangeError", Extends = typeof(ArgumentOutOfRangeException), Inherits = typeof(SystemException))] + [HideMethod("message")] + public static class RangeErrorOps { + } + + [RubyException("NameError", Extends = typeof(MemberAccessException), Inherits = typeof(SystemException))] + public static class NameErrorOps { + } + + [RubyException("NoMethodError", Extends = typeof(MissingMethodException), Inherits = typeof(MemberAccessException))] + [HideMethod("message")] + public static class NoMethodErrorOps { + } + + [RubyException("SecurityError", Extends = typeof(SecurityException), Inherits = typeof(SystemException))] + public static class SecurityErrorOps { + } + + [RubyException("TypeError", Extends = typeof(InvalidOperationException), Inherits = typeof(SystemException))] + public static class TypeErrorOps { + } + + [RubyException("ZeroDivisionError", Extends = typeof(DivideByZeroException), Inherits = typeof(SystemException))] + public static class ZeroDivisionErrorOps { + } + + // special one: + [RubyException("SystemCallError", Extends = typeof(ExternalException), Inherits = typeof(SystemException))] + public static class SystemCallErrorOps { + [RubyConstructor] + public static ExternalException/*!*/ Factory(RubyClass/*!*/ self, [DefaultProtocol]MutableString message) { + ExternalException result = new ExternalException(Errno.MakeMessage(ref message, "unknown error")); + RubyExceptionData.InitializeException(result, message); + return result; + } + + [RubyConstructor] + public static ExternalException/*!*/ Factory(RubyClass/*!*/ self, int errorCode) { + // TODO: + var message = MutableString.Create("system error #" + errorCode); + + ExternalException result = new ExternalException(Errno.MakeMessage(ref message, "unknown error")); + RubyExceptionData.InitializeException(result, message); + return result; + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/FalseClass.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/FalseClass.cs new file mode 100644 index 0000000000..2ef88b1b62 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/FalseClass.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + [RubyClass("FalseClass")] + public static class FalseClass : Object { + #region Public Instance Methods + + [RubyMethodAttribute("to_s")] + public static MutableString/*!*/ ToString(bool self) { + Debug.Assert(self == false); + return MutableString.Create("false"); + } + + [RubyMethodAttribute("&")] + public static bool And(bool self, object obj) { + Debug.Assert(self == false); + return false; + } + + [RubyMethodAttribute("^")] + public static bool Xor(bool self, object obj) { + Debug.Assert(self == false); + return obj != null; + } + + [RubyMethodAttribute("^")] + public static bool Xor(bool self, bool obj) { + Debug.Assert(self == false); + return obj; + } + + [RubyMethodAttribute("|")] + public static bool Or(bool self, object obj) { + Debug.Assert(self == false); + return obj != null; + } + + [RubyMethodAttribute("|")] + public static bool Or(bool self, bool obj) { + Debug.Assert(self == false); + return obj; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/FileOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/FileOps.cs new file mode 100644 index 0000000000..77e8333366 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/FileOps.cs @@ -0,0 +1,973 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Builtins { + + /// + /// File builtin class. Derives from IO + /// + [RubyClass("File", Extends = typeof(RubyFile))] + public class RubyFileOps { + + [RubyConstructor] + public static RubyIO CreateIO(RubyClass/*!*/ self, MutableString/*!*/ path) { + return new RubyFile(self.Context, path.ConvertToString(), "r"); + } + + [RubyConstructor] + public static RubyIO CreateIO(RubyClass/*!*/ self, MutableString/*!*/ path, MutableString modeString) { + return new RubyFile(self.Context, path.ConvertToString(), + (modeString != null) ? modeString.ConvertToString() : "r"); + } + + [RubyConstructor] + public static RubyIO CreateIO(RubyClass/*!*/ self, MutableString/*!*/ path, MutableString modeString, int permissions) { + // TODO: make this actually do something with permissions + return new RubyFile(self.Context, path.ConvertToString(), + (modeString != null) ? modeString.ConvertToString() : "r"); + } + + [RubyConstructor] + public static RubyIO CreateIO(RubyClass/*!*/ self, MutableString/*!*/ path, int fileMode) { + return new RubyFile(self.Context, path.ConvertToString(), (RubyFileMode)fileMode); + } + + [RubyConstructor] + public static RubyIO CreateIO(RubyClass/*!*/ self, MutableString/*!*/ path, int fileMode, int permissions) { + // TODO: make this actually do something with permissions + return new RubyFile(self.Context, path.ConvertToString(), (RubyFileMode)fileMode); + } + + #region Private Singleton Methods + + #endregion + + #region Public Singleton Methods + + [RubyMethod("atime", RubyMethodAttributes.PublicSingleton)] + public static DateTime AccessTime(RubyClass/*!*/ self, object path) { + return RubyStatOps.AccessTime(RubyStatOps.Create(self.Context, path)); + } + + private static bool WildcardExtensionMatch(string/*!*/ extension, string/*!*/ pattern) { + for (int i = 0; i < pattern.Length; i++) { + if (i >= extension.Length) { + return false; + } + + if (pattern[i] == '*') { + return true; + } + + if (extension[i] != pattern[i]) { + return false; + } + } + return true; + } + + private static MutableString/*!*/ TrimTrailingSlashes(MutableString/*!*/ path) { + int offset = path.Length - 1; + while (offset > 0) { + if (path.GetChar(offset) != '/' && path.GetChar(offset) != '\\') + break; + --offset; + } + return path.GetSlice(0, offset + 1); + } + + [RubyMethod("basename", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ Basename(RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ path, [DefaultProtocol, NotNull, Optional]MutableString extensionFilter) { + + if (path.IsEmpty) { + return path; + } + + MutableString trimmedPath = TrimTrailingSlashes(path); + + // Special cases of drive letters C:\\ or C:/ + if (trimmedPath.Length == 2) { + if (Char.IsLetter(trimmedPath.GetChar(0)) && trimmedPath.GetChar(1) == ':') { + var result = (path.Length > 2 ? MutableString.Create(path.GetChar(2).ToString()) : MutableString.CreateMutable()); + return result.TaintBy(path); + } + } + + string trimmedPathAsString = trimmedPath.ConvertToString(); + if (trimmedPathAsString == "/") { + return trimmedPath; + } + + string filename = Path.GetFileName(trimmedPath.ConvertToString()); + + // Handle UNC host names correctly + string root = Path.GetPathRoot(trimmedPath.ConvertToString()); + if (MutableString.IsNullOrEmpty(extensionFilter)) { + return MutableString.Create(trimmedPathAsString == root ? root : filename); + } + + string fileExtension = Path.GetExtension(filename); + string basename = Path.GetFileNameWithoutExtension(filename); + + string strResult = WildcardExtensionMatch(fileExtension, extensionFilter.ConvertToString()) ? basename : filename; + return Glob.CanonicalizePath(MutableString.Create(strResult)).TaintBy(path); + } + + [RubyMethod("blockdev?", RubyMethodAttributes.PublicSingleton)] + public static bool IsBlockDevice(RubyClass/*!*/ self, object/*!*/ path) { + return RubyStatOps.IsBlockDevice(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("chardev?", RubyMethodAttributes.PublicSingleton)] + public static bool IsCharDevice(RubyClass/*!*/ self, object path) { + return RubyStatOps.IsCharDevice(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("chmod", RubyMethodAttributes.PublicSingleton)] + public static int Chmod(RubyClass/*!*/ self, int permission, MutableString path) { + // TODO: implement this correctly for windows + return 0; + } + + //chown + + [RubyMethod("ctime", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateTime(RubyClass/*!*/ self, object path) { + return RubyStatOps.CreateTime(RubyStatOps.Create(self.Context, path)); + } + + private static bool FileExists(RubyContext/*!*/ context, string/*!*/ path) { + return context.DomainManager.Platform.FileExists(path); + } + + private static bool DirectoryExists(RubyContext/*!*/ context, string/*!*/ path) { + return context.DomainManager.Platform.DirectoryExists(path); + } + + [RubyMethod("delete", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("unlink", RubyMethodAttributes.PublicSingleton)] + public static int Delete(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + string strPath = path.ConvertToString(); + if (!FileExists(self.Context, strPath)) { + throw new Errno.NoEntryError(String.Format("No such file or directory - {0}", strPath)); + } + + File.Delete(strPath); + return 1; + } + + [RubyMethod("delete", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("unlink", RubyMethodAttributes.PublicSingleton)] + public static int Delete(RubyClass/*!*/ self, [NotNull]params object[]/*!*/ paths) { + foreach (MutableString path in Protocols.CastToStrings(self.Context, paths)) { + Delete(self, path); + } + + return paths.Length; + } + + [RubyMethod("directory?", RubyMethodAttributes.PublicSingleton)] + public static bool IsDirectory(RubyContext/*!*/ context, object/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + return DirectoryExists(context, path.ConvertToString()); + } + + [RubyMethod("dirname", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ DirName(RubyClass/*!*/ self, MutableString/*!*/ path) { + string directoryName = path.ConvertToString(); + + if (IsValidPath(path.ConvertToString())) { + directoryName = Path.GetDirectoryName(path.ConvertToString()); + string fileName = Path.GetFileName(path.ConvertToString()); + if (!String.IsNullOrEmpty(fileName)) { + directoryName = StripPathCharacters(path.ConvertToString().Replace(fileName, "")); + } + } else { + if (directoryName.Length > 1) + directoryName = "//"; + } + return Glob.CanonicalizePath(MutableString.Create(String.IsNullOrEmpty(directoryName) ? "." : directoryName)); + } + + private static bool IsValidPath(string path) { + int length = 0; + foreach (char c in path.ToCharArray()) { + if ((c == '/') || (c == '\\')) + continue; + length++; + } + return (length > 0); + + } + + private static string StripPathCharacters(string path) { + int limit = 0; + for (int charIndex = path.Length - 1; charIndex > 0; charIndex--) { + if (!((path[charIndex] == '/') || (path[charIndex] == '\\'))) + break; + limit++; + } + if (limit > 0) { + limit--; + return path.Substring(0, path.Length - limit - 1); + } + return path; + } + + [RubyMethod("executable?", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("executable_real?", RubyMethodAttributes.PublicSingleton)] + public static bool IsExecutable(RubyClass/*!*/ self, object path) { + return RubyStatOps.IsExecutable(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("exist?", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("exists?", RubyMethodAttributes.PublicSingleton)] + public static bool Exists(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + string strPath = path.ConvertToString(); + return FileExists(self.Context, strPath) || DirectoryExists(self.Context, strPath); + } + + [RubyMethod("extname", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ GetExtension(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + return MutableString.Create(Path.GetExtension(path.ConvertToString())).TaintBy(path); + } + + [RubyMethod("file?", RubyMethodAttributes.PublicSingleton)] + public static bool IsAFile(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + return FileExists(self.Context, path.ConvertToString()); + } + + #region fnmatch + + [RubyMethod("fnmatch", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("fnmatch?", RubyMethodAttributes.PublicSingleton)] + public static bool FnMatch(object/*!*/ self, [NotNull]MutableString/*!*/ pattern, [NotNull]MutableString/*!*/ path, [Optional]int flags) { + return Glob.FnMatch(pattern, path, flags); + } + + #endregion + + [RubyMethod("ftype", RubyMethodAttributes.PublicSingleton)] + public static MutableString FileType(RubyClass/*!*/ self, object path) { + return RubyStatOps.FileType(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("grpowned?", RubyMethodAttributes.PublicSingleton)] + public static bool IsGroupOwned(RubyClass/*!*/ self, object path) { + return RubyStatOps.IsGroupOwned(RubyStatOps.Create(self.Context, path)); + } + + //identical? + + [RubyMethod("join", RubyMethodAttributes.PublicSingleton)] + public static MutableString Join(RubyClass/*!*/ self, [NotNull]params object[] strings) { + MutableString result = MutableString.CreateMutable(); + + for (int i = 0; i < strings.Length; ++i) { + result.Append(Protocols.ConvertToString(self.Context, strings[i])); + if (i < strings.Length - 1) + result.Append(SEPARATOR); + } + + return result; + } + + #region expand_path + +#if !SILVERLIGHT + // Algorithm to find HOME equivalents under Windows. This is equivalent to Ruby 1.9 behavior: + // + // 1. Try get HOME + // 2. Try to generate HOME equivalent using HOMEDRIVE + HOMEPATH + // 3. Try to generate HOME equivalent from USERPROFILE + // 4. Try to generate HOME equivalent from Personal special folder + + internal static string/*!*/ GetHomeDirectory(RubyContext/*!*/ context) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + string result = pal.GetEnvironmentVariable("HOME"); + + if (result == null) { + string homeDrive = pal.GetEnvironmentVariable("HOMEDRIVE"); + string homePath = pal.GetEnvironmentVariable("HOMEPATH"); + if (homeDrive == null && homePath == null) { + string userEnvironment = pal.GetEnvironmentVariable("USERPROFILE"); + if (userEnvironment == null) { + // This will always succeed with a non-null string, but it can fail + // if the Personal folder was renamed or deleted. In this case it returns + // an empty string. + result = Environment.GetFolderPath(Environment.SpecialFolder.Personal); + } else { + result = userEnvironment; + } + } else if (homeDrive == null) { + result = homePath; + } else if (homePath == null) { + result = homeDrive; + } else { + result = Path.Combine(homeDrive, homePath); + } + } + return result; + } + + // Expand directory path - these cases exist: + // + // 1. Empty string or nil means return current directory + // 2. ~ with non-existent HOME directory throws exception + // 3. ~, ~/ or ~\ which expands to HOME + // 4. ~foo is left unexpanded + // 5. Expand to full path if path is a relative path + // + // No attempt is made to determine whether the path is valid or not + // Returned path is always canonicalized to forward slashes + + private static MutableString/*!*/ ExpandPath(RubyContext/*!*/ context, MutableString/*!*/ path) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + int length = path.Length; + try { + if (path == null || length == 0) + return Glob.CanonicalizePath(MutableString.Create(Directory.GetCurrentDirectory())); + + if (length == 1 && path.GetChar(0) == '~') + return Glob.CanonicalizePath(MutableString.Create(Path.GetFullPath(pal.GetEnvironmentVariable("HOME")))); + + if (path.GetChar(0) == '~' && (path.GetChar(1) == Path.DirectorySeparatorChar || path.GetChar(1) == Path.AltDirectorySeparatorChar)) { + string homeDirectory = pal.GetEnvironmentVariable("HOME"); + return Glob.CanonicalizePath(length < 3 ? MutableString.Create(homeDirectory) : MutableString.Create(Path.Combine(homeDirectory, path.GetSlice(2).ConvertToString()))); + } else { + return Glob.CanonicalizePath(MutableString.Create(Path.GetFullPath(path.ConvertToString()))); + } + } catch (Exception e) { + // Re-throw exception as a reasonable Ruby exception + throw new Errno.InvalidError(path.ConvertToString(), e); + } + } + + [RubyMethod("expand_path", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static MutableString/*!*/ ExpandPath(RubyContext/*!*/ context, RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path, + [DefaultProtocol, Optional]MutableString basePath) { + + // We ignore basePath parameter if first string starts with a ~ + if (basePath == null || path.GetFirstChar() == '~') { + return ExpandPath(context, path); + } else { + return Glob.CanonicalizePath(MutableString.Create( + Path.GetFullPath(Path.Combine(ExpandPath(context, basePath).ConvertToString(), path.ConvertToString())) + )); + } + } +#endif + + #endregion + + //lchmod + //lchown + //link + + [RubyMethod("lstat", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("stat", RubyMethodAttributes.PublicSingleton)] + public static FileSystemInfo Stat(RubyClass/*!*/ self, object path) { + return RubyStatOps.Create(self.Context, path); + } + + [RubyMethod("mtime", RubyMethodAttributes.PublicSingleton)] + public static DateTime ModifiedTime(RubyClass/*!*/ self, object path) { + return RubyStatOps.ModifiedTime(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(BlockParam/*!*/ block, RubyClass/*!*/ self, [NotNull]MutableString/*!*/ path, [NotNull]MutableString/*!*/ mode) { + RubyIO io = RubyIOOps._CreateIOSharedSite2.Target(RubyIOOps._CreateIOSharedSite2, self.Context, self, path, mode); + return RubyIOOps.TryInvokeOpenBlock(self.Context, block, io); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(BlockParam/*!*/ block, RubyClass/*!*/ self, object path, object mode) { + RubyIO io = RubyIOOps._CreateIOSharedSite2.Target(RubyIOOps._CreateIOSharedSite2, self.Context, self, Protocols.CastToString(self.Context, path), Protocols.CastToString(self.Context, mode)); + return RubyIOOps.TryInvokeOpenBlock(self.Context, block, io); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(BlockParam/*!*/ block, RubyClass/*!*/ self, [NotNull]MutableString/*!*/ path) { + RubyIO io = RubyIOOps._CreateIOSharedSite3.Target(RubyIOOps._CreateIOSharedSite3, self.Context, self, path); + return RubyIOOps.TryInvokeOpenBlock(self.Context, block, io); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(BlockParam/*!*/ block, RubyClass/*!*/ self, object path) { + RubyIO io = RubyIOOps._CreateIOSharedSite3.Target(RubyIOOps._CreateIOSharedSite3, self.Context, self, Protocols.CastToString(self.Context, path)); + return RubyIOOps.TryInvokeOpenBlock(self.Context, block, io); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(BlockParam/*!*/ block, RubyClass/*!*/ self, [NotNull]MutableString/*!*/ path, int fileMode) { + RubyIO io = RubyIOOps._CreateIOSharedSite4.Target(RubyIOOps._CreateIOSharedSite4, self.Context, self, path, fileMode); + return RubyIOOps.TryInvokeOpenBlock(self.Context, block, io); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(BlockParam/*!*/ block, RubyClass/*!*/ self, object path, int fileMode) { + RubyIO io = RubyIOOps._CreateIOSharedSite4.Target(RubyIOOps._CreateIOSharedSite4, self.Context, self, Protocols.CastToString(self.Context, path), fileMode); + return RubyIOOps.TryInvokeOpenBlock(self.Context, block, io); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(BlockParam/*!*/ block, RubyClass/*!*/ self, object path, int fileMode, int permissions) { + RubyIO io = RubyIOOps._CreateIOSharedSite5.Target(RubyIOOps._CreateIOSharedSite5, self.Context, self, Protocols.CastToString(self.Context, path), fileMode, permissions); + return RubyIOOps.TryInvokeOpenBlock(self.Context, block, io); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(BlockParam/*!*/ block, RubyClass/*!*/ self, MutableString/*!*/ path, MutableString/*!*/ mode, int permissions) { + RubyIO io = RubyIOOps._CreateIOSharedSite7.Target(RubyIOOps._CreateIOSharedSite7, self.Context, self, path, mode, permissions); + return RubyIOOps.TryInvokeOpenBlock(self.Context, block, io); + } + + [RubyMethod("owned?", RubyMethodAttributes.PublicSingleton)] + public static bool IsUserOwned(RubyClass/*!*/ self, object path) { + return RubyStatOps.IsUserOwned(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("pipe?", RubyMethodAttributes.PublicSingleton)] + public static bool IsPipe(RubyClass/*!*/ self, object path) { + return RubyStatOps.IsPipe(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("readable?", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("readable_real?", RubyMethodAttributes.PublicSingleton)] + public static bool IsReadable(RubyClass/*!*/ self, object path) { + return RubyStatOps.IsReadable(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("readlink", RubyMethodAttributes.PublicSingleton)] + public static bool Readlink(RubyClass/*!*/ self, [NotNull]MutableString/*!*/ path) { + throw new IronRuby.Builtins.NotImplementedError("readlink() function is unimplemented on this machine"); + } + + [RubyMethod("rename", RubyMethodAttributes.PublicSingleton)] + public static int Rename(RubyContext/*!*/ context, RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ oldPath, [DefaultProtocol, NotNull]MutableString/*!*/ newPath) { + + string strOldPath = oldPath.ConvertToString(); + if (!FileExists(context, strOldPath) && !DirectoryExists(context, strOldPath)) { + throw new Errno.NoEntryError(String.Format("No such file or directory - {0}", oldPath)); + } + + // TODO: Change to raise a SystemCallError instead of a native CLR error + File.Move(strOldPath, newPath.ToString()); + return 0; + } + + [RubyMethod("setgid?", RubyMethodAttributes.PublicSingleton)] + public static bool IsSetGid(RubyClass/*!*/ self, object path) { + return RubyStatOps.IsSetGid(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("setuid?", RubyMethodAttributes.PublicSingleton)] + public static bool IsSetUid(RubyClass/*!*/ self, object path) { + return RubyStatOps.IsSetUid(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("size", RubyMethodAttributes.PublicSingleton)] + public static int Size(RubyClass/*!*/ self, object path) { + return RubyStatOps.Size(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("size?", RubyMethodAttributes.PublicSingleton)] + public static object NullableSize(RubyClass/*!*/ self, object path) { + return RubyStatOps.NullableSize(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("socket?", RubyMethodAttributes.PublicSingleton)] + public static bool IsSocket(RubyClass/*!*/ self, object path) { + return RubyStatOps.IsSocket(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("split", RubyMethodAttributes.PublicSingleton)] + public static RubyArray Split(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + RubyArray result = new RubyArray(2); + result.Add(DirName(self, path)); + result.Add(Basename(self, path, null)); + return result; + } + + [RubyMethod("sticky?", RubyMethodAttributes.PublicSingleton)] + public static bool IsSticky(RubyClass/*!*/ self, object path) { + return RubyStatOps.IsSticky(RubyStatOps.Create(self.Context, path)); + } + + //truncate + //umask + +#if !SILVERLIGHT + [RubyMethod("symlink", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static object SymLink(RubyClass/*!*/ self, object path) { + throw new NotImplementedError("symlnk() function is unimplemented on this machine"); + } + + [RubyMethod("symlink?", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static bool IsSymLink(RubyClass/*!*/ self, object path) { + return RubyStatOps.IsSymLink(RubyStatOps.Create(self.Context, path)); + } + + [RubyMethod("utime", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static int UpdateTimes(RubyClass/*!*/ self, DateTime accessTime, DateTime modifiedTime, [NotNull]MutableString/*!*/ path) { + string strPath = path.ConvertToString(); + if (!FileExists(self.Context, strPath)) { + throw new Errno.NoEntryError(String.Format("No such file or directory - {0}", strPath)); + } + + FileInfo info = new FileInfo(strPath); + info.LastAccessTime = accessTime; + info.LastWriteTime = modifiedTime; + return 1; + } + + [RubyMethod("utime", RubyMethodAttributes.PublicSingleton)] + public static int UpdateTimes(RubyClass/*!*/ self, object accessTime, object modifiedTime, + [NotNull]params object[]/*!*/ paths) { + + DateTime atime = MakeTime(self.Context, accessTime); + DateTime mtime = MakeTime(self.Context, modifiedTime); + + foreach (MutableString path in Protocols.CastToStrings(self.Context, paths)) { + UpdateTimes(self, atime, mtime, path); + } + + return paths.Length; + } +#endif + + private static DateTime MakeTime(RubyContext/*!*/ context, object obj) { + if (obj == null) { + return DateTime.Now; + } else if (obj is DateTime) { + return (DateTime)obj; + } else if (obj is int) { + return TimeOps.Create(typeof(RubyFileOps), (int)obj); + } else { + string name = context.GetClassOf(obj).Name; + throw RubyExceptions.CreateTypeConversionError(name, "time"); + } + } + + private static bool IsWritableImpl(RubyContext/*!*/ context, string/*!*/ path) { + FileSystemInfo fsi; + if (RubyStatOps.TryCreate(context, path, out fsi)) { + return RubyStatOps.IsWritable(fsi); + } else { + return false; + } + } + + [RubyMethod("writable?", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("writable_real?", RubyMethodAttributes.PublicSingleton)] + public static bool IsWritable(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + return IsWritableImpl(self.Context, path.ConvertToString()); + } + + [RubyMethod("zero?", RubyMethodAttributes.PublicSingleton)] + public static bool IsZeroLength(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + string strPath = path.ConvertToString(); + + // NUL/nul is a special-cased filename on Windows + if (strPath.ToLower() == "nul") { + return RubyStatOps.IsZeroLength(RubyStatOps.Create(self.Context, strPath)); + } + + if (DirectoryExists(self.Context, strPath) || !FileExists(self.Context, strPath)) { + return false; + } + + return RubyStatOps.IsZeroLength(RubyStatOps.Create(self.Context, strPath)); + } + + #endregion + + #region Public Instance Methods + + [RubyMethod("atime")] + public static DateTime AccessTime(RubyContext/*!*/ context, RubyFile/*!*/ self) { + return RubyStatOps.AccessTime(RubyStatOps.Create(context, self.Path)); + } + + //chmod + //chown + + [RubyMethod("ctime")] + public static DateTime CreateTime(RubyContext/*!*/ context, RubyFile/*!*/ self) { + return RubyStatOps.CreateTime(RubyStatOps.Create(context, self.Path)); + } + + //flock + + [RubyMethod("lstat")] + public static FileSystemInfo Stat(RubyContext/*!*/ context, RubyFile/*!*/ self) { + return RubyStatOps.Create(context, self.Path); + } + + [RubyMethod("mtime")] + public static DateTime ModifiedTime(RubyContext/*!*/ context, RubyFile/*!*/ self) { + return RubyStatOps.ModifiedTime(RubyStatOps.Create(context, self.Path)); + } + + [RubyMethod("inspect")] + public static MutableString/*!*/ Inspect(RubyContext/*!*/ context, RubyFile/*!*/ self) { + return MutableString.CreateMutable("#'); + } + + [RubyMethod("path")] + public static MutableString/*!*/ GetPath(RubyFile/*!*/ self) { + return MutableString.Create(self.Path); + } + + //truncate + + #endregion + + #region Declared Constants + + [RubyConstant] + public readonly static MutableString ALT_SEPARATOR = MutableString.Create("\\"); + + [RubyConstant] + public readonly static MutableString PATH_SEPARATOR = MutableString.Create(";"); + + private readonly static MutableString INTERNAL_SEPARATOR = MutableString.Create("/"); + + private readonly static string NUL_VALUE = "NUL"; + [RubyConstant] + public readonly static MutableString SEPARATOR = INTERNAL_SEPARATOR; + + [RubyConstant] + public readonly static MutableString Separator = INTERNAL_SEPARATOR; + + [RubyModule("Constants")] + public static class Constants { + [RubyConstant] + public readonly static int APPEND = (int)RubyFileMode.APPEND; + [RubyConstant] + public readonly static int BINARY = (int)RubyFileMode.BINARY; + [RubyConstant] + public readonly static int CREAT = (int)RubyFileMode.CREAT; + [RubyConstant] + public readonly static int EXCL = (int)RubyFileMode.EXCL; + [RubyConstant] + public readonly static int FNM_CASEFOLD = 0x08; + [RubyConstant] + public readonly static int FNM_DOTMATCH = 0x04; + [RubyConstant] + public readonly static int FNM_NOESCAPE = 0x01; + [RubyConstant] + public readonly static int FNM_PATHNAME = 0x02; + [RubyConstant] + public readonly static int FNM_SYSCASE = 0x08; + [RubyConstant] + public readonly static int LOCK_EX = 0x02; + [RubyConstant] + public readonly static int LOCK_NB = 0x04; + [RubyConstant] + public readonly static int LOCK_SH = 0x01; + [RubyConstant] + public readonly static int LOCK_UN = 0x08; + [RubyConstant] + public readonly static int NONBLOCK = (int)RubyFileMode.NONBLOCK; + [RubyConstant] + public readonly static int RDONLY = (int)RubyFileMode.RDONLY; + [RubyConstant] + public readonly static int RDWR = (int)RubyFileMode.RDWR; + [RubyConstant] + public readonly static int SEEK_CUR = 0x01; + [RubyConstant] + public readonly static int SEEK_END = 0x02; + [RubyConstant] + public readonly static int SEEK_SET = 0x00; + [RubyConstant] + public readonly static int TRUNC = (int)RubyFileMode.TRUNC; + [RubyConstant] + public readonly static int WRONLY = (int)RubyFileMode.WRONLY; + } + + #endregion + + #region File::Stat + + /// + /// Stat + /// + [RubyClass("Stat", Extends = typeof(FileSystemInfo), Inherits = typeof(object), BuildConfig = "!SILVERLIGHT"), Includes(typeof(Comparable))] + public class RubyStatOps { + + internal static FileSystemInfo/*!*/ Create(RubyContext/*!*/ context, object path) { + return Create(context, Protocols.CastToString(context, path).ConvertToString()); + } + + internal static FileSystemInfo/*!*/ Create(RubyContext/*!*/ context, string/*!*/ path) { + FileSystemInfo fsi; + if (TryCreate(context, path, out fsi)) { + return fsi; + } else { + throw new Errno.NoEntryError(String.Format("No such file or directory - {0}", path)); + } + } + + internal static bool TryCreate(RubyContext/*!*/ context, string/*!*/ path, out FileSystemInfo result) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + result = null; + if (pal.FileExists(path)) { + result = new FileInfo(path); + } else if (pal.DirectoryExists(path)) { + result = new DirectoryInfo(path); + } else if (path.ToUpper().Equals(NUL_VALUE)) { + result = null; + } else { + return false; + } + return true; + } + + + [RubyConstructor] + public static FileSystemInfo/*!*/ Create(RubyClass/*!*/ self, [NotNull]MutableString/*!*/ path) { + return Create(self.Context, path.ConvertToString()); + } + + [RubyConstructor] + public static FileSystemInfo/*!*/ Create(RubyClass/*!*/ self, object path) { + return Create(self.Context, path); + } + + [RubyMethod("<=>")] + public static int Compare(FileSystemInfo/*!*/ self, FileSystemInfo/*!*/ other) { + return TimeOps.CompareTo(self.LastWriteTime, other.LastWriteTime); + } + + [RubyMethod("<=>")] + public static object Compare(FileSystemInfo/*!*/ self, object other) { + FileSystemInfo otherStat = (other as FileSystemInfo); + if (otherStat == null) { + return null; + } + return Compare(self, otherStat); + } + + [RubyMethod("atime")] + public static DateTime AccessTime(FileSystemInfo/*!*/ self) { + return self.LastAccessTime; + } + + [RubyMethod("blksize")] + public static object BlockSize(FileSystemInfo/*!*/ self) { + return null; + } + + [RubyMethod("blockdev?")] + public static bool IsBlockDevice(FileSystemInfo/*!*/ self) { + return false; + } + + [RubyMethod("blocks")] + public static object Blocks(FileSystemInfo/*!*/ self) { + return null; + } + + [RubyMethod("chardev?")] + public static bool IsCharDevice(FileSystemInfo/*!*/ self) { + return false; + } + + [RubyMethod("ctime")] + public static DateTime CreateTime(FileSystemInfo/*!*/ self) { + return self.CreationTime; + } + + [RubyMethod("dev")] + [RubyMethod("rdev")] + public static object DeviceId(FileSystemInfo/*!*/ self) { + // TODO: Map to drive letter? + return 3; + } + + [RubyMethod("dev_major")] + [RubyMethod("rdev_major")] + public static object DeviceIdMajor(FileSystemInfo/*!*/ self) { + return null; + } + + [RubyMethod("dev_minor")] + [RubyMethod("rdev_minor")] + public static object DeviceIdMinor(FileSystemInfo/*!*/ self) { + return null; + } + + [RubyMethod("directory?")] + public static bool IsDirectory(FileSystemInfo/*!*/ self) { + return (self is DirectoryInfo); + } + + [RubyMethod("executable?")] + [RubyMethod("executable_real?")] + public static bool IsExecutable(FileSystemInfo/*!*/ self) { + // TODO: Fix + return self.Extension.Equals(".exe", StringComparison.InvariantCulture); + } + + [RubyMethod("file?")] + public static bool IsFile(FileSystemInfo/*!*/ self) { + return (self is FileInfo); + } + + [RubyMethod("ftype")] + public static MutableString FileType(FileSystemInfo/*!*/ self) { + string result = IsFile(self) ? "file" : "directory"; + return MutableString.Create(result); + } + + [RubyMethod("gid")] + public static int GroupId(FileSystemInfo/*!*/ self) { + return 0; + } + + [RubyMethod("grpowned?")] + public static bool IsGroupOwned(FileSystemInfo/*!*/ self) { + return false; + } + + [RubyMethod("ino")] + public static int Inode(FileSystemInfo/*!*/ self) { + return 0; + } + + [RubyMethod("inspect")] + public static MutableString Inspect(RubyContext/*!*/ context, FileSystemInfo/*!*/ self) { + string result = String.Format( + "# + /// A Fixnum holds Integer values that can be represented in Int32. + /// If any operation on a Fixnum exceeds this range, the value is automatically converted to a Bignum. + /// Fixnum objects have immediate value. + /// This means that when they are assigned or passed as parameters, the actual object is passed, rather than a reference to that object. + /// Assignment does not alias Fixnum objects. There is effectively only one Fixnum object instance for any given integer value. + /// So, for example, you cannot add a singleton method to a Fixnum. + /// + [RubyClass("Fixnum", Extends = typeof(int), Inherits = typeof(Integer))] + public static class FixnumOps { + + #region Useful Constants + const int FixnumSize = 4; + const int FixnumBits = 32; + const int FixnumMax = Int32.MaxValue; + const int FixnumMin = Int32.MinValue; + #endregion + + #region induced_from + + /// + /// Convert obj to a Fixnum, where obj is Fixnum + /// + /// Just returns the Fixnum + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static int InducedFrom(RubyClass/*!*/ self, int obj) { + return obj; + } + + /// + /// Convert obj to a Fixnum, where obj is Float + /// + /// If obj is small enough casts to Fixnum otherwise throws a RangeError + /// If obj is too large for a Fixnum + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static int InducedFrom(RubyClass/*!*/ self, double obj) { + if (obj <= Int32.MaxValue && obj >= Int32.MinValue) { + return (int)obj; + } + throw RubyExceptions.CreateRangeError("Float " + obj.ToString() + " out of range of integer"); + } + + /// + /// Convert obj to a Fixnum + /// + /// Dynamically invokes to_int on obj, unless obj is null. + /// If obj is Nil + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static int InducedFrom(RubyClass/*!*/ self, object obj) { + return Protocols.CastToFixnum(self.Context, Protocols.ConvertToInteger(self.Context, obj)); + } + + #endregion + + #region size + /// + /// Returns the number of bytes in the machine representation of a Fixnum. + /// + [RubyMethod("size")] + public static int Size(int self) { + return FixnumSize; + } + #endregion + + #region Bitwise Operators + + #region << + + /// + /// Returns the value after shifting to the left (right if count is negative) the value in self by other bits. + /// (where other is Fixnum) + /// + /// The value after the shift + /// Converts to Bignum if the result cannot fit into Fixnum + [RubyMethod("<<")] + public static object LeftShift(int self, int other) { + if (other < 0) { + // Negative shift + return RightShift(self, -other); + } else if (other > FixnumBits || self >> (FixnumSize - other) > 0) { + // Shift overflows Int32 + return BigInteger.LeftShift(self, other); + } else { + return self << other; + } + } + /// + /// Returns the value after shifting to the left (right if count is negative) the value in self by other bits. + /// (where other is not Fixnum) + /// + /// The value after the shift + /// Converts to Bignum if the result cannot fit into Fixnum + [RubyMethod("<<")] + public static object LeftShift(RubyContext/*!*/ context, int self, object other) { + return BignumOps.LeftShift(context, self, other); + } + + #endregion + + #region >> + + /// + /// Returns the value after shifting to the right (left if count is negative) the value in self by other bits. + /// (where other is Fixnum) + /// + /// The value after the shift + /// Converts to Bignum if the result cannot fit into Fixnum + [RubyMethod(">>")] + public static object RightShift(int self, int other) { + if (other < 0) { + // Negative shift + return LeftShift(self, -other); + } else if (other == 0) { + return self; + } else if (other > FixnumBits) { + return self < 0 ? -1 : 0; + } else { + return self >> other; + } + } + + /// + /// Returns the value after shifting to the right (left if count is negative) the value in self by other bits. + /// (where other is not Fixnum) + /// + /// The value after the shift + /// Converts to Bignum if the result cannot fit into Fixnum + [RubyMethod(">>")] + public static object RightShift(RubyContext/*!*/ context, int self, object other) { + return BignumOps.RightShift(context, self, other); + } + + #endregion + + #region [] + + /// + /// Returns the value of the bit at the indexth bit position of self, where index is Fixnum + /// + /// + /// + /// a = 9**15 + /// 50.downto(0) do |n| + /// print a[n] + /// end + /// + /// produces: + /// + /// 000101110110100000111000011110010100111100010111001 + /// + /// + /// indexth bit in the (assumed) binary representation of self, where self[0] is the least significant bit. + /// Since representation is supposed to be 2s complement, we return always 1 if self is negative and index is greater than most signifcant bit in BigInteger + [RubyMethod("[]")] + public static int Bit(int self, int index) { + if (index < 0) { + return 0; + } + if (index > FixnumBits) { + return self < 0 ? 1 : 0; + } + return (self & (1 << index)) != 0 ? 1 : 0; + } + + /// + /// Returns the value of the bit at the indexth bit position of self, where index is Bignum + /// + /// + /// 0 if index is negative or self is positive + /// 1 otherwise + /// + /// + /// Since representation is supposed to be 2s complement and index must be extremely big, + /// we asssume we can always return 1 if self is negative and 0 otherwise + [RubyMethod("[]")] + public static int Bit(int self, [NotNull]BigInteger/*!*/ index) { + // BigIntegers as indexes are always going to be outside the range. + if (index.IsNegative() || self >= 0) { + return 0; + } else { + return 1; + } + } + + /// + /// Returns the value of the bit at the indexth bit position of self, where index is not Fixnum or Bignum + /// + /// indexth bit in the (assumed) binary representation of self, where self[0] is the least significant bit. + /// index is dynamically converted to an Integer by index.to_int then [] is invoked dynamically. E.g. self[index.to_int] + [RubyMethod("[]")] + public static int Bit(RubyContext/*!*/ context, int self, object index) { + try { + object intIndex = Protocols.ConvertToInteger(context, index); + return LibrarySites.BitRef(context, self, intIndex); + } catch (FloatDomainError) { + throw RubyExceptions.CreateRangeError("float " + index.ToString() + " out of range of integer"); + } + } + + #endregion + + #region ^ + /// + /// Performs bitwise XOR on self and other + /// + [RubyMethod("^")] + public static object BitwiseXor(int self, int other) { + return self ^ other; + } + /// + /// Performs bitwise XOR on self and other + /// + [RubyMethod("^")] + public static object BitwiseXor(int self, [NotNull]BigInteger/*!*/ other) { + return other ^ self; + } + /// + /// Performs bitwise XOR on self and other, where other is not Fixnum or Bignum + /// + [RubyMethod("^")] + public static object BitwiseXor(RubyContext/*!*/ context, int self, object other) { + return BignumOps.Xor(context, self, other); + } + #endregion + + #region & + /// + /// Performs bitwise AND on self and other, where other is Fixnum + /// + [RubyMethod("&")] + public static object BitwiseAnd(int self, int other) { + return self & other; + } + /// + /// Performs bitwise AND on self and other, where other is Bignum + /// + [RubyMethod("&")] + public static object BitwiseAnd(int self, [NotNull]BigInteger/*!*/ other) { + return other & self; + } + /// + /// Performs bitwise AND on self and other, where other is not Fixnum or Bignum + /// + [RubyMethod("&")] + public static object BitwiseAnd(RubyContext/*!*/ context, int self, object other) { + return BignumOps.And(context, self, other); + } + #endregion + + #region | + /// + /// Performs bitwise OR on self and other + /// + [RubyMethod("|")] + public static object BitwiseOr(int self, int other) { + return self | other; + } + /// + /// Performs bitwise OR on self and other + /// + [RubyMethod("|")] + public static object BitwiseOr(int self, [NotNull]BigInteger/*!*/ other) { + return other | self; + } + /// + /// Performs bitwise OR on self and other, where other is not Fixnum or Bignum + /// + [RubyMethod("|")] + public static object BitwiseOr(RubyContext/*!*/ context, int self, object other) { + return BignumOps.BitwiseOr(context, self, other); + } + #endregion + + #region ~ + /// + /// Returns the ones complement of self; a number where each bit is flipped. + /// + [RubyMethod("~")] + public static int OnesComplement(int self) { + return ~self; + } + #endregion + + #endregion + + #region Arithmetic Operators + + #region %, modulo + /// + /// Returns self modulo other, where other is Fixnum. See for more information. + /// + [RubyMethod("%"), RubyMethod("modulo")] + public static object ModuloOp(int self, int other) { + RubyArray divmod = DivMod(self, other); + return divmod[1]; + } + /// + /// Returns self % other, where other is not Fixnum. + /// + /// + /// First coerces self on other then calls % on the coerced self value. + /// + [RubyMethod("%")] + public static object ModuloOp(RubyContext/*!*/ context, object self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.ModuloOp); + } + /// + /// Returns self modulo other, where other is not Fixnum. + /// + /// + /// First coerces self on other then calls modulo on the coerced self value. + /// + [RubyMethod("modulo")] + public static object Modulo(RubyContext/*!*/ context, object self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Modulo); + } + #endregion + + #region * + /// + /// Returns self multiplied by other, where other is Fixnum or Bignum. + /// + /// + /// Returns either Fixnum or Bignum if the result is too large for Fixnum. + /// + [RubyMethod("*")] + public static object Multiply(int self, int other) { + try { + return checked(self * other); + } catch (OverflowException) { + return BigInteger.Multiply(self, other); + } + } + /// Returns self multiplied by other, where other is Fixnum or Bignum. + /// + /// + /// Returns either Fixnum or Bignum if the result is too large for Fixnum. + /// + [RubyMethod("*")] + public static object Multiply(int self, [NotNull]BigInteger/*!*/ other) { + return BigInteger.Multiply(self, other); + } + /// + /// Returns self multiplied by other, where other is Float. + /// + /// + /// Returns a Float + /// + /// + /// Converts self to a float and multiplies the two floats directly. + /// + [RubyMethod("*")] + public static double Multiply(int self, double other) { + return (double)self * other; + } + /// + /// Returns self multiplied by other. + /// + /// + /// The class of the resulting object depends on the class of other and on the magnitude of the result. + /// + /// + /// Self is first coerced by other and then the * operator is invoked on the coerced self. + /// + [RubyMethod("*")] + public static object Multiply(RubyContext/*!*/ context, object self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Multiply); + } + #endregion + + #region ** + /// + /// Raises self to the other power, which may be negative. + /// + /// + /// Integer (Bignum or Fixnum) if other is positive + /// Float otherwise. + /// + [RubyMethod("**")] + public static object Power(int self, int other) { + if (other >= 0) { + BigInteger bigSelf = (BigInteger)self; + return Protocols.Normalize(bigSelf.Power(other)); + } else { + return System.Math.Pow(self, other); + } + } + /// + /// Raises self to the other power, which may be negative or fractional. + /// + /// Float + [RubyMethod("**")] + public static double Power(int self, double other) { + return System.Math.Pow(self, other); + } + /// + /// Raises self to the other power, where other is not Integer or Float. + /// + /// + /// Self is first coerced by other and then the ** operator is invoked on the coerced self. + /// + [RubyMethod("**")] + public static object Power(RubyContext/*!*/ context, int self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Power); + } + #endregion + + #region + + /// + /// Returns self added to other, where other is Fixnum. + /// + /// Fixnum or Bignum if result is too large for Fixnum. + [RubyMethod("+")] + public static object Add(int self, int other) { + try { + return checked(self + other); + } catch (OverflowException) { + return BigInteger.Add(self, other); + } + } + /// + /// Returns self added to other, where other is Float + /// + /// Float + /// + /// Converts self to Float and then adds the two floats directly. + /// + [RubyMethod("+")] + public static double Add(int self, double other) { + return (double)self + other; + } + /// + /// Returns self added to other. + /// + /// + /// The class of the resulting object depends on the class of other and on the magnitude of the result. + /// + /// + /// Self is first coerced by other and then the + operator is invoked on the coerced self. + /// + [RubyMethod("+")] + public static object Add(RubyContext/*!*/ context, object self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Add); + } + #endregion + + #region - + /// + /// Subtracts other from self (i.e. self - other), where other is Fixnum. + /// + /// Fixnum, or Bignum if result is too large for Fixnum. + [RubyMethod("-")] + public static object Subtract(int self, int other) { + try { + return checked(self - other); + } catch (OverflowException) { + return BigInteger.Subtract(self, other); + } + } + /// + /// Subtracts other from self (i.e. self - other), where other is Float. + /// + /// Float + /// + /// Converts self to a double then executes the subtraction directly. + /// + [RubyMethod("-")] + public static double Subtract(int self, double other) { + return (double)self - other; + } + /// + /// Subtracts other from self (i.e. self - other), where other is not Fixnum, or Float. + /// + /// + /// The class of the resulting object depends on the class of other and on the magnitude of the result. + /// + /// + /// Self is first coerced by other and then the - operator is invoked on the coerced self. + /// + [RubyMethod("-")] + public static object Subtract(RubyContext/*!*/ context, object self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Subtract); + } + #endregion + + #region -@ + /// + /// Negates self, which might return a Bignum. + /// + [RubyMethod("-@")] + public static int UnaryMinus(int self) { + return -self; + } + #endregion + + #region /, div + /// + /// Divides self by other, where other is a Fixnum. + /// Aliased as / and div + /// + /// Fixnum, or Bignum if result is too large for Fixnum. + /// + /// Since both operands are Integer, the result returned is Integer, rounded toward -Infinity. + /// + [RubyMethod("/"), RubyMethod("div")] + public static object DivideOp(int self, int other) { + RubyArray divmod = DivMod(self, other); + return divmod[0]; + } + /// + /// Divides self by other, where other is not a Fixnum. + /// + /// + /// The class of the resulting object depends on the class of other and on the magnitude of the result. + /// + /// + /// Self is first coerced by other and then the / operator is invoked on the coerced self. + /// + [RubyMethod("/")] + public static object DivideOp(RubyContext/*!*/ context, object self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Divide); + } + /// + /// Divides self by other, where other is not a Fixnum. + /// + /// + /// The class of the resulting object depends on the class of other and on the magnitude of the result. + /// + /// + /// Self is first coerced by other and then the div method is invoked on the coerced self. + /// + [RubyMethod("div")] + public static object Div(RubyContext/*!*/ context, object self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Div); + } + #endregion + + #region abs + /// + /// Returns the absolute value of self. + /// + /// Fixnum + [RubyMethod("abs")] + public static int Abs(int self) { + return self >= 0 ? self : -self; + } + #endregion + + #region divmod + /// + /// Returns an array containing the quotient and modulus obtained by dividing self by other. + /// + /// RubyArray of the form: [div, mod], where div is Integer + /// + /// If q, r = x.divmod(y), then + /// q = floor(float(x)/float(y)) + /// x = q*y + r + /// The quotient is rounded toward -infinity, as shown in the following table: + /// + /// a | b | a.divmod(b) | a/b | a.modulo(b) | a.remainder(b) + /// ------+-----+---------------+---------+-------------+--------------- + /// 13 | 4 | 3, 1 | 3 | 1 | 1 + /// ------+-----+---------------+---------+-------------+--------------- + /// 13 | -4 | -4, -3 | -3 | -3 | 1 + /// ------+-----+---------------+---------+-------------+--------------- + /// -13 | 4 | -4, 3 | -4 | 3 | -1 + /// ------+-----+---------------+---------+-------------+--------------- + /// -13 | -4 | 3, -1 | 3 | -1 | -1 + /// ------+-----+---------------+---------+-------------+--------------- + /// 11.5 | 4 | 2, 3.5 | 2.875 | 3.5 | 3.5 + /// ------+-----+---------------+---------+-------------+--------------- + /// 11.5 | -4 | -3, -0.5 | -2.875 | -0.5 | 3.5 + /// ------+-----+---------------+---------+-------------+--------------- + /// -11.5 | 4 | -3, 0.5 | -2.875 | 0.5 | -3.5 + /// ------+-----+---------------+---------+-------------+--------------- + /// -11.5 | -4 | 2 -3.5 | 2.875 | -3.5 | -3.5 + /// + /// + /// 11.divmod(3) #=> [3, 2] + /// 11.divmod(-3) #=> [-4, -1] + /// 11.divmod(3.5) #=> [3, 0.5] + /// (-11).divmod(3.5) #=> [-4, 3.0] + /// (11.5).divmod(3.5) #=> [3, 1.0] + /// + [RubyMethod("divmod")] + public static RubyArray DivMod(int self, int other) { + if (other == 0) { + throw new DivideByZeroException("divided by zero"); + } + + int div; + // We want to divide two positive numbers and then fix up the sign afterwards. + // i.e. div = sign * self.abs / other.abs + // where sign = 1 if self*other >= 0 + // sign = -1 if self*other < 0 + if (other < 0) { + if (self < 0) { + div = (-self) / (-other); + } else { + div = -(self / (-other)); + } + } else { + if (self < 0) { + div = -((-self) / other); + } else { + div = self / other; + } + } + // We now need to fix up mod and div if (mod * other) is negative + int mod = self - div * other; + if ((mod > 0 && other < 0) || (mod < 0 && other > 0)) { + mod += other; + div -= 1; + } + return RubyOps.MakeArray2(div, mod); + } + /// + /// Returns an array containing the quotient and modulus obtained by dividing self by other. + /// + /// RubyArray of the form: [div, mod], where div is Integer + /// + /// Self is first coerced by other and then the divmod method is invoked on the coerced self. + /// + [RubyMethod("divmod")] + public static RubyArray DivMod(RubyContext/*!*/ context, int self, object other) { + return (RubyArray)Protocols.CoerceAndCall(context, self, other, LibrarySites.DivMod); + } + #endregion + + #region quo + /// + /// Returns the floating point result of dividing self by other, where other is Fixnum. + /// + /// Float + [RubyMethod("quo")] + public static double Quotient(int self, [NotNull]BigInteger/*!*/ other) { + return (double)self / (double)other; + } + /// + /// Returns the floating point result of dividing self by other, where other is not Fixnum. + /// + /// + /// Self is first coerced by other and then the quo method is invoked on the coerced self. + /// + [RubyMethod("quo")] + public static object Quotient(RubyContext/*!*/ context, int self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Quo); + } + #endregion + + #region zero? + /// + /// Returns true if self is zero. + /// + /// True if self is zero, false otherwise. + [RubyMethod("zero?")] + public static bool IsZero(int self) { + return self == 0; + } + #endregion + + #endregion + + #region Comparison Operators + + #region < + /// + /// Returns true if the value of self is less than other, where other is Fixnum. + /// + /// True or false + [RubyMethod("<")] + public static bool LessThan(int self, int other) { + return self < other; + } + /// + /// Returns true if the value of self is less than other, where other is not Fixnum. + /// + /// True or false + /// + /// Self is first coerced by other and then the < operator is invoked on the coerced self. + /// + [RubyMethod("<")] + public static bool LessThan(RubyContext/*!*/ context, int self, object other) { + return Protocols.CoerceAndCallRelationOperator(context, self, other, LibrarySites.LessThan); + } + + [RubyMethod("<")] + public static bool LessThan(RubyContext/*!*/ context, object self, object other) { + return Protocols.CoerceAndCallRelationOperator(context, self, other, LibrarySites.LessThan); + } + #endregion + + #region <= + /// + /// Returns true if the value of self is less than or equal to other, where other is Fixnum. + /// + /// True or false + [RubyMethod("<=")] + public static bool LessThanOrEqual(int self, int other) { + return self <= other; + } + /// + /// Returns true if the value of self is less than or equal to other, where other is not Fixnum. + /// + /// True or false + /// + /// Self is first coerced by other and then the <= operator is invoked on the coerced self. + /// + [RubyMethod("<=")] + public static bool LessThanOrEqual(RubyContext/*!*/ context, int self, object other) { + return Protocols.CoerceAndCallRelationOperator(context, self, other, LibrarySites.LessThanOrEqual); + } + #endregion + + #region <=> + /// + /// Comparison: Returns -1, 0, or +1 depending on whether self is less than, equal to, or greater than other, where other is Fixnum. + /// + /// + /// -1 if self is less than other + /// 0 if self is equal to other + /// +1 if self is greater than other + /// nil if self cannot be compared to other + /// + [RubyMethod("<=>")] + public static int Compare(int self, int other) { + return self.CompareTo(other); + } + /// + /// Comparison: Returns -1, 0, or +1 depending on whether self is less than, equal to, or greater than other, where other is not Fixnum. + /// + /// + /// -1 if self is less than other + /// 0 if self is equal to other + /// +1 if self is greater than other + /// nil if self cannot be compared to other + /// + /// + /// Self is first coerced by other and then the <=> operator is invoked on the coerced self. + /// + [RubyMethod("<=>")] + public static object Compare(RubyContext/*!*/ context, int self, object other) { + return Protocols.CoerceAndCallCompare(context, self, other); + } + #endregion + + #region == + /// + /// Test whether self is numerically equivalent to other. (Does not require type equivalence). + /// + /// True if self and other are numerically equal. + /// + /// Since other is Fixnum here, we just test for direct equality. + /// + [RubyMethod("==")] + public static bool Equal(int self, int other) { + return self == other; + } + /// + /// Test whether self is numerically equivalent to other. (Does not require type equivalence). + /// + /// True if self and other are numerically equal. + /// + /// Since other is not Fixnum, we turn the equivalence check around, + /// i.e. call other == self + /// + [RubyMethod("==")] + public static bool Equal(RubyContext/*!*/ context, int self, object other) { + // If self == other doesn't work then try other == self + return Protocols.IsEqual(context, other, self); + } + #endregion + + #region > + /// + /// Returns true if the value of self is greater than other, where other is Fixnum. + /// + /// True or false + [RubyMethod(">")] + public static bool GreaterThan(int self, int other) { + return self > other; + } + /// + /// Returns true if the value of self is greater than other, where other is not Fixnum. + /// + /// True or false + /// + /// Self is first coerced by other and then the > operator is invoked on the coerced self. + /// + [RubyMethod(">")] + public static bool GreaterThan(RubyContext/*!*/ context, int self, object other) { + return Protocols.CoerceAndCallRelationOperator(context, self, other, LibrarySites.GreaterThan); + } + #endregion + + #region >= + /// + /// Returns true if the value of self is greater than or equal to other, where other is Fixnum. + /// + /// True or false + [RubyMethod(">=")] + public static bool GreaterThanOrEqual(int self, int other) { + return self >= other; + } + /// + /// Returns true if the value of self is greater than or equal to other, where other is not Fixnum. + /// + /// True or false + /// + /// Self is first coerced by other and then the >= operator is invoked on the coerced self. + /// + [RubyMethod(">=")] + public static bool GreaterThanOrEqual(RubyContext/*!*/ context, int self, object other) { + return Protocols.CoerceAndCallRelationOperator(context, self, other, LibrarySites.GreaterThanOrEqual); + } + #endregion + + #endregion + + #region Conversion Methods + + #region to_f + /// + /// Convert self to Float. + /// + /// Float version of self + [RubyMethod("to_f")] + public static double ToFloat(int self) { + return (double)self; + } + #endregion + + #region to_s + /// + /// Returns a string representing the value of self using base 10. + /// + /// MutableString + /// 12345.to_s => "12345" + [RubyMethod("to_s")] + public static object ToString(object self) { + return MutableString.Create(self.ToString()); + } + /// + /// Returns a string representing the value of self using base radix. + /// + /// MutableString + /// + /// 12345.to_s(2) #=> "11000000111001" + /// 12345.to_s(8) #=> "30071" + /// 12345.to_s(10) #=> "12345" + /// 12345.to_s(16) #=> "3039" + /// 12345.to_s(36) #=> "9ix" + /// + [RubyMethod("to_s")] + public static object ToString([NotNull]BigInteger/*!*/ self, int radix) { + if (radix < 2 || radix > 36) { + throw RubyExceptions.CreateArgumentError("illegal radix " + radix.ToString()); + } + // TODO: Should we try to use a Fixnum specific ToString? + // TODO: Can we do the ToLower in BigInteger? + return MutableString.Create(self.ToString((uint)radix).ToLower()); + } + #endregion + + #region to_sym + + /// + /// Returns the Symbol whose integer value is self + /// + /// + /// Symbol or nil if there is no symbol with id of self + /// + /// fred = :fred.to_i + /// fred.id2name #=> "fred" + /// fred.to_sym #=> :fred + /// + [RubyMethod("to_sym")] + public static object ToSymbol(int self) { + SymbolId result; + return RubyOps.TryConvertFixnumToSymbol(self, out result) ? (object)result : null; + } + + #endregion + + #region id2name + /// + /// Returns the name of the object whose symbol id is self. + /// + /// MutableString or nil if there is no symbol with id of self. + /// + /// symbol = :@inst_var #=> :@inst_var + /// id = symbol.to_i #=> 9818 + /// id.id2name #=> "@inst_var" + /// + [RubyMethod("id2name")] + public static object Id2Name(int self) { + SymbolId id = new SymbolId(self); + return SymbolTable.ContainsId(id) ? (object)SymbolOps.ToString(id) : null; + } + #endregion + + #endregion + + } +} + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/FloatOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/FloatOps.cs new file mode 100644 index 0000000000..43fd0217a6 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/FloatOps.cs @@ -0,0 +1,931 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Math; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + [RubyClass("Float", Extends = typeof(double), Inherits = typeof(Numeric)), Includes(typeof(Precision))] + public static class FloatOps { + + #region Constants + /// + /// Smallest Float such that 1.0+EPSILON != 1.0 + /// + /// System.Double.Epsilon is not actually the correct value! + [RubyConstant] + public const double EPSILON = 0.0000000000000002220446049250313080847263336181640625; + /// + /// The smallest Float greater than zero + /// + /// + /// Note this is not double.MinValue, which is most negative Float value. + /// + [RubyConstant] + public const double MIN = 2.2250738585072014e-308; + /// + /// The largest possible value for Float + /// + [RubyConstant] + public const double MAX = double.MaxValue; + + /// + /// The number of digits available in the mantissa (base 10) + /// + [RubyConstant] + public const int DIG = 15; + /// + /// The number of digits available in the mantissa (base 2) + /// + [RubyConstant] + public const int MANT_DIG = 53; + + /// + /// The maximum size of the exponent (base 10) + /// + [RubyConstant] + public const double MAX_10_EXP = 308; + /// + /// The minimum size the the exponent (base 10) + /// + [RubyConstant] + public const double MIN_10_EXP = -307; + + /// + /// The maximum size of the exponent (base 2) + /// + [RubyConstant] + public const int MAX_EXP = 1024; + /// + /// The minimum size of the exponent (base 2) + /// + [RubyConstant] + public const int MIN_EXP = -1021; + + /// + /// The radix or base of the mantissa + /// + [RubyConstant] + public const int RADIX = 2; + /// + /// Rounding mode used by Float + /// + [RubyConstant] + public const int ROUNDS = 1; + #endregion + + #region induced_from + + /// + /// Convert value to Float, where value is Float. + /// + /// Float + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static double InducedFrom(RubyClass/*!*/ self, double value) { + return value; + } + + /// + /// Convert value to Float, where value is Fixnum. + /// + /// Float + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static double InducedFrom(RubyClass/*!*/ self, int value) { + return RubySites.ToF(self.Context, value); + } + + /// + /// Convert value to Float, where value is Bignum. + /// + /// Float + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static double InducedFrom(RubyClass/*!*/ self, [NotNull]BigInteger/*!*/ value) { + return RubySites.ToF(self.Context, value); + } + + /// + /// Convert value to Float, where value is not Float, Fixnum or Bignum. + /// + /// Float + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static double InducedFrom(RubyClass/*!*/ self, object value) { + throw RubyExceptions.CreateTypeError(String.Format("failed to convert {0} into Float", RubyUtils.GetClassName(self.Context, value))); + } + + #endregion + + #region Arithmetic Operators + + #region * + /// + /// Returns a new float which is the product of self * and other, where other is Fixnum. + /// + /// Float + [RubyMethod("*")] + public static double Multiply(double self, int other) { + return self * (double)other; + } + /// + /// Returns a new float which is the product of self * and other, where other is Bignum. + /// + /// Float + [RubyMethod("*")] + public static double Multiply(double self, [NotNull]BigInteger/*!*/ other) { + return self * (double)other; + } + /// + /// Returns a new float which is the product of self * and other, where other is Float. + /// + /// Float + [RubyMethod("*")] + public static double Multiply(double self, double other) { + return self * other; + } + /// + /// Returns a new float which is the product of self * and other, , where other is not Fixnum, Bignum or Float. + /// + /// + [RubyMethod("*")] + public static object Multiply(RubyContext/*!*/ context, double self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Multiply); + } + #endregion + + #region + + + /// + /// Returns a new float which is the sum of self * and other, where other is Fixnum. + /// + /// Float + [RubyMethod("+")] + public static double Add(double self, int other) { + return self + (double)other; + } + + /// + /// Returns a new float which is the sum of self * and other, where other is Bignum. + /// + /// Float + [RubyMethod("+")] + public static double Add(double self, [NotNull]BigInteger/*!*/ other) { + return self + (double)other; + } + + /// + /// Returns a new float which is the sum of self * and other, where other is Float. + /// + /// Float + [RubyMethod("+")] + public static double Add(double self, double other) { + return self + other; + } + + /// + /// Returns a new float which is the sum of self * and other, , where other is not Fixnum, Bignum or Float. + /// + /// + [RubyMethod("+")] + public static object Add(RubyContext/*!*/ context, double self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Add); + } + + #endregion + + #region - + /// + /// Returns a new float which is the difference between self * and other, where other is Fixnum. + /// + /// Float + [RubyMethod("-")] + public static double Subtract(double self, int other) { + return self - (double)other; + } + /// + /// Returns a new float which is the difference between self * and other, where other is Bignum. + /// + /// Float + [RubyMethod("-")] + public static double Subtract(double self, [NotNull]BigInteger/*!*/ other) { + return self - (double)other; + } + /// + /// Returns a new float which is the difference between self * and other, where other is Float. + /// + /// Float + [RubyMethod("-")] + public static double Subtract(double self, double other) { + return self - other; + } + /// + /// Returns a new float which is the difference between self * and other, , where other is not Fixnum, Bignum or Float. + /// + /// + [RubyMethod("-")] + public static object Subtract(RubyContext/*!*/ context, double self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Subtract); + } + #endregion + + #region / + /// + /// Returns a new float which is the result of dividing self * by other, where other is Fixnum. + /// + /// Float + [RubyMethod("/")] + public static double Divide(double self, int other) { + return self / (double)other; + } + /// + /// Returns a new float which is the result of dividing self * by other, where other is Bignum. + /// + /// Float + [RubyMethod("/")] + public static double Divide(double self, [NotNull]BigInteger/*!*/ other) { + return self / (double)other; + } + /// + /// Returns a new float which is the result of dividing self * by other, where other is Float. + /// + /// Float + [RubyMethod("/")] + public static double Divide(double self, double other) { + return self / other; + } + /// + /// Returns a new float which is the result of dividing self * by other, where other is not Fixnum, Bignum or Float. + /// + /// + [RubyMethod("/")] + public static object Divide(RubyContext/*!*/ context, double self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Divide); + } + #endregion + + #region %, modulo + /// + /// Return the modulo after division of self by other, where other is Fixnum. + /// + /// Float + [RubyMethod("%"), RubyMethod("modulo")] + public static double Modulo(double self, int other) { + return (double)InternalDivMod(self, (double)other)[1]; + } + /// + /// Return the modulo after division of self by other, where other is Bignum. + /// + /// Float + [RubyMethod("%"), RubyMethod("modulo")] + public static double Modulo(double self, [NotNull]BigInteger/*!*/ other) { + return (double)InternalDivMod(self, (double)other)[1]; + } + /// + /// Return the modulo after division of self by other, where other is Float. + /// + /// Float + [RubyMethod("%"), RubyMethod("modulo")] + public static double Modulo(double self, double other) { + return (double)InternalDivMod(self, other)[1]; + } + /// + /// Return the modulo after division of self by other, where other is not Fixnum, Bignum or Float. + /// + /// + [RubyMethod("%")] + public static object ModuloOp(RubyContext/*!*/ context, double self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.ModuloOp); + } + /// + /// Return the modulo after division of self by other, where other is not Fixnum, Bignum or Float. + /// + /// + [RubyMethod("modulo")] + public static object Modulo(RubyContext/*!*/ context, double self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Modulo); + } + #endregion + + #region ** + /// + /// Raises self the other power, where other is Fixnum. + /// + [RubyMethod("**")] + public static double Power(double self, int other) { + return System.Math.Pow(self, (double)other); + } + /// + /// Raises self the other power, where other is Bignum. + /// + [RubyMethod("**")] + public static double Power(double self, [NotNull]BigInteger/*!*/ other) { + return System.Math.Pow(self, (double)other); + } + /// + /// Raises self the other power, where other is Float. + /// + [RubyMethod("**")] + public static double Power(double self, double other) { + return System.Math.Pow(self, other); + } + /// + /// Raises self the other power, where other is not Fixnum, Bignum or Float. + /// + [RubyMethod("**")] + public static object Power(RubyContext/*!*/ context, double self, object other) { + return Protocols.CoerceAndCall(context, self, other, LibrarySites.Power); + } + #endregion + + #region divmod + /// + /// Returns an array containing the quotient and modulus obtained by dividing self by other, where other is Fixnum. + /// + /// + /// + /// + /// + /// If q, r = x.divmod(y), then + /// q = floor(float(x)/float(y)) + /// x = q*y + r + /// The quotient is rounded toward -infinity + /// + [RubyMethod("divmod")] + public static RubyArray DivMod(double self, int other) { + return DivMod(self, (double)other); + } + /// + /// Returns an array containing the quotient and modulus obtained by dividing self by other, where other is Bignum. + /// + /// + /// + /// + /// + /// If q, r = x.divmod(y), then + /// q = floor(float(x)/float(y)) + /// x = q*y + r + /// The quotient is rounded toward -infinity + /// + [RubyMethod("divmod")] + public static RubyArray DivMod(double self, [NotNull]BigInteger/*!*/ other) { + return DivMod(self, (double)other); + } + /// + /// Returns an array containing the quotient and modulus obtained by dividing self by other, where other is Float + /// + /// + /// + /// + /// + /// If q, r = x.divmod(y), then + /// q = floor(float(x)/float(y)) + /// x = q*y + r + /// The quotient is rounded toward -infinity + /// + [RubyMethod("divmod")] + public static RubyArray DivMod(double self, double other) { + RubyArray result = InternalDivMod(self, other); + // Unlike modulo, divmod blows up if the quotient or modulus are not finite, so we can't put this inside InternalDivMod + // We only need to test if the quotient is double since it should have been converted to Integer (Fixnum or Bignum) if it was OK. + if (result[0] is double || Double.IsNaN((double)result[1])) { + throw CreateFloatDomainError("NaN"); + } + return result; + } + /// + /// Returns an array containing the quotient and modulus obtained by dividing self by other, where other is not Fixnum, Bignum or Float. + /// + /// + /// + /// + /// + /// If q, r = x.divmod(y), then + /// q = floor(float(x)/float(y)) + /// x = q*y + r + /// The quotient is rounded toward -infinity + /// + [RubyMethod("divmod")] + public static RubyArray DivMod(RubyContext/*!*/ context, double self, object other) { + return (RubyArray)Protocols.CoerceAndCall(context, self, other, LibrarySites.DivMod); + } + #endregion + + #region abs + /// + /// Returns the absolute value of self. + /// + /// + /// (-34.56).abs #=> 34.56 + /// -34.56.abs #=> 34.56 + /// + [RubyMethod("abs")] + public static double Abs(double self) { + return System.Math.Abs(self); + } + #endregion + + #endregion + + #region Conversion Methods + + #region ceil + /// + /// Returns the smallest Integer greater than or equal to self + /// + /// + /// 1.2.ceil #=> 2 + /// 2.0.ceil #=> 2 + /// (-1.2).ceil #=> -1 + /// (-2.0).ceil #=> -2 + /// + [RubyMethod("ceil")] + public static object Ceil(double self) { + double ceil = System.Math.Ceiling(self); + return CastToInteger(ceil); + } + #endregion + + #region floor + /// + /// Returns the largest Integer less than or equal to self. + /// + /// + /// 1.2.floor #=> 1 + /// 2.0.floor #=> 2 + /// (-1.2).floor #=> -2 + /// (-2.0).floor #=> -2 + /// + [RubyMethod("floor")] + public static object Floor(double self) { + double floor = System.Math.Floor(self); + return CastToInteger(floor); + } + #endregion + + #region to_i, to_int, truncate + /// + /// Returns self truncated to an Integer. + /// + [RubyMethod("to_i"), RubyMethod("to_int"), RubyMethod("truncate")] + public static object ToInt(double self) { + if (self >= 0) { + return Floor(self); + } else { + return Ceil(self); + } + } + #endregion + + #region coerce + /// + /// Attempts to coerce other to a Float. + /// + /// [other, self] as Floats + [RubyMethod("coerce")] + public static RubyArray Coerce(RubyContext/*!*/context, double self, object other) { + return RubyOps.MakeArray2(Protocols.ConvertToFloat(context, other), self); + } + #endregion + + #region to_f + /// + /// Converts self to Float + /// + /// + /// As self is already Float, returns self. + /// + [RubyMethod("to_f")] + public static double ToFloat(double self) { + return self; + } + #endregion + + #region round + /// + /// Rounds self to the nearest Integer. + /// + /// + /// This is equivalent to: + /// + /// def round + /// return (self+0.5).floor if self > 0.0 + /// return (self-0.5).ceil if self < 0.0 + /// return 0 + /// end + /// + /// + /// + /// 1.5.round #=> 2 + /// (-1.5).round #=> -2 + /// + [RubyMethod("round")] + public static object Round(double self) { + if (self > 0) { return Floor(self + 0.5); } + if (self < 0) { return Ceil(self - 0.5); } + return 0; + } + #endregion + + #region inspect, to_s + + /// + /// Returns a string containing a representation of self. + /// + /// + /// As well as a fixed or exponential form of the number, the call may return + /// "NaN", "Infinity", and "-Infinity". + /// + [RubyMethod("to_s")] + public static MutableString ToS(RubyContext/*!*/ context, double self) { + StringFormatter sf = new StringFormatter(context, "%.15g", new object[] { self }); + sf.TrailingZeroAfterWholeFloat = true; + return sf.Format(); + } + + #endregion + + #region hash + /// + /// Returns a hash code for self. + /// + [RubyMethod("hash")] + public static int Hash(double self) { + return self.GetHashCode(); + } + #endregion + + #endregion + + #region Comparison Operators + + #region == + /// + /// Returns true only if other has the same value as self, where other is Float + /// + /// True or False + /// + /// Contrast this with Float#eql?, which requires other to be a Float. + /// + [RubyMethod("==")] + public static bool Equal(double self, double other) { + return self == other; + } + + /// + /// Returns true only if other has the same value as self, where other is not a Float + /// + /// True or False + /// + /// Contrast this with Float#eql?, which requires other to be a Float. + /// Dynamically invokes other == self (i.e. swaps operands around). + /// + [RubyMethod("==")] + public static bool Equal(RubyContext/*!*/ context, double self, object other) { + // Call == on the right operand like Float#== does + return Protocols.IsEqual(context, other, self); + } + #endregion + + #region <=> + /// + /// Compares self with other, where other is Float. + /// + /// + /// -1 if self is less than other + /// 0 if self is equal to other + /// +1 if self is greater than other + /// nil if self is not comparable to other (for instance if either is NaN). + /// + /// + /// This is the basis for the tests in Comparable. + /// + [RubyMethod("<=>")] + public static object Compare(double self, double other) { + if (Double.IsNaN(self) || Double.IsNaN(other)) { + return null; + } + return self.CompareTo(other); + } + + /// + /// Compares self with other, where other is Fixnum. + /// + /// + /// -1 if self is less than other + /// 0 if self is equal to other + /// +1 if self is greater than other + /// nil if self is not comparable to other (for instance if either is NaN). + /// + /// + /// This is the basis for the tests in Comparable. + /// + [RubyMethod("<=>")] + public static object Compare(double self, int other) { + if (Double.IsNaN(self)) { + return null; + } + return self.CompareTo((double)other); + } + + /// + /// Compares self with other, where other is Bignum. + /// + /// + /// -1 if self is less than other + /// 0 if self is equal to other + /// +1 if self is greater than other + /// nil if self is not comparable to other (for instance if either is NaN). + /// + /// + /// This is the basis for the tests in Comparable. + /// + [RubyMethod("<=>")] + public static object Compare(double self, [NotNull]BigInteger/*!*/ other) { + if (Double.IsNaN(self)) { + return null; + } + return self.CompareTo((double)other); + } + + /// + /// Compares self with other, where other is not Float, Fixnum or Bignum. + /// + /// + /// -1 if self is less than other + /// 0 if self is equal to other + /// +1 if self is greater than other + /// nil if self is not comparable to other (for instance if either is NaN). + /// + /// + /// This is the basis for the tests in Comparable. + /// + [RubyMethod("<=>")] + public static object Compare(RubyContext/*!*/ context, double self, object other) { + return Protocols.CoerceAndCallCompare(context, self, other); + } + #endregion + + #region < + /// + /// Returns true if self is less than other, where other is Float. + /// + [RubyMethod("<")] + public static bool LessThan(double self, double other) { + if (double.IsNaN(self) || double.IsNaN(other)) { + return false; + } + return self < other; + } + /// + /// Returns true if self is less than other, where other is Fixnum. + /// + [RubyMethod("<")] + public static bool LessThan(double self, int other) { + return LessThan(self, (double)other); + } + /// + /// Returns true if self is less than other, where other is Bignum. + /// + [RubyMethod("<")] + public static bool LessThan(double self, [NotNull]BigInteger/*!*/ other) { + return LessThan(self, (double)other); + } + /// + /// Returns true if self is less than other, where other is not Float, Fixnum or Bignum. + /// + [RubyMethod("<")] + public static bool LessThan(RubyContext/*!*/ context, double self, object other) { + return Protocols.CoerceAndCallRelationOperator(context, self, other, LibrarySites.LessThan); + } + #endregion + + #region <= + /// + /// Returns true if self is less than or equal to other, where other is Float. + /// + [RubyMethod("<=")] + public static bool LessThanOrEqual(double self, double other) { + if (double.IsNaN(self) || double.IsNaN(other)) { + return false; + } + return self <= other; + } + /// + /// Returns true if self is less than or equal to other, where other is Fixnum. + /// + [RubyMethod("<=")] + public static bool LessThanOrEqual(double self, int other) { + return LessThanOrEqual(self, (double)other); + } + /// + /// Returns true if self is less than or equal to other, where other is Bignum. + /// + [RubyMethod("<=")] + public static bool LessThanOrEqual(double self, [NotNull]BigInteger/*!*/ other) { + return LessThanOrEqual(self, (double)other); + } + /// + /// Returns true if self is less than or equal to other, where other is not Float, Fixnum or Bignum. + /// + [RubyMethod("<=")] + public static bool LessThanOrEqual(RubyContext/*!*/ context, double self, object other) { + return Protocols.CoerceAndCallRelationOperator(context, self, other, LibrarySites.LessThanOrEqual); + } + #endregion + + #region > + /// + /// Returns true if self is greater than other, where other is Float. + /// + [RubyMethod(">")] + public static bool GreaterThan(double self, double other) { + if (double.IsNaN(self) || double.IsNaN(other)) { + return false; + } + return self > other; + } + /// + /// Returns true if self is greater than other, where other is Fixnum. + /// + [RubyMethod(">")] + public static bool GreaterThan(double self, int other) { + return GreaterThan(self, (double)other); + } + /// + /// Returns true if self is greater than other, where other is Bignum. + /// + [RubyMethod(">")] + public static bool GreaterThan(double self, [NotNull]BigInteger/*!*/ other) { + return GreaterThan(self, (double)other); + } + /// + /// Returns true if self is greater than other, where other is not Float, Fixnum or Bignum. + /// + [RubyMethod(">")] + public static bool GreaterThan(RubyContext/*!*/ context, double self, object other) { + return Protocols.CoerceAndCallRelationOperator(context, self, other, LibrarySites.GreaterThan); + } + #endregion + + #region >= + /// + /// Returns true if self is greater than or equal to other, where other is Float. + /// + [RubyMethod(">=")] + public static bool GreaterThanOrEqual(double self, double other) { + if (double.IsNaN(self) || double.IsNaN(other)) { + return false; + } + return self >= other; + } + /// + /// Returns true if self is greater than or equal to other, where other is Fixnum. + /// + [RubyMethod(">=")] + public static bool GreaterThanOrEqual(double self, int other) { + return GreaterThanOrEqual(self, (double)other); + } + /// + /// Returns true if self is greater than or equal to other, where other is Bignum. + /// + [RubyMethod(">=")] + public static bool GreaterThanOrEqual(double self, [NotNull]BigInteger/*!*/ other) { + return GreaterThanOrEqual(self, (double)other); + } + /// + /// Returns true if self is greater than or equal to other, where other is not Float, Fixnum or Bignum. + /// + [RubyMethod(">=")] + public static bool GreaterThanOrEqual(RubyContext/*!*/ context, double self, object other) { + return Protocols.CoerceAndCallRelationOperator(context, self, other, LibrarySites.GreaterThanOrEqual); + } + #endregion + + #region finite? + /// + /// Returns true if self is a valid IEEE floating point number + /// (it is not infinite, and nan? is false). + /// + [RubyMethod("finite?")] + public static bool IsFinite(double self) { + return !double.IsInfinity(self); + } + #endregion + + #region infinite? + /// + /// Returns nil, -1, or +1 depending on whether self is finite, -infinity, or +infinity. + /// + /// + /// (0.0).infinite? #=> nil + /// (-1.0/0.0).infinite? #=> -1 + /// (+1.0/0.0).infinite? #=> 1 + /// + [RubyMethod("infinite?")] + public static object IsInfinite(double self) { + if (double.IsInfinity(self)) { + return double.IsPositiveInfinity(self) ? 1 : -1; + } else { + return null; + } + } + #endregion + + #region nan? + /// + /// Returns true if self is an invalid IEEE floating point number. + /// + /// + /// a = -1.0 #=> -1.0 + /// a.nan? #=> false + /// a = 0.0/0.0 #=> NaN + /// a.nan? #=> true + /// + [RubyMethod("nan?")] + public static bool IsNan(double self) { + return double.IsNaN(self); + } + #endregion + + #region zero? + /// + /// Returns true if self is 0.0. + /// + [RubyMethod("zero?")] + public static bool IsZero(double self) { + return self.Equals(0.0); + } + + public static bool IsNegativeZero(double self) { +#if SILVERLIGHT // BitConverter.DoubleToInt64Bits + if ( self != 0.0 ) { + return false; + } + byte[] bits = BitConverter.GetBytes(self); + return (bits[7] == 0x80 && bits[6] == 0x00 && bits[5] == 0x00 && bits[4] == 0x00 + && bits[3] == 0x00 && bits[2] == 0x00 && bits[1] == 0x00 && bits[0] == 0x00); +#else + return (self == 0.0 && 1.0 / self < 0); +#endif + } + + #endregion + #endregion + + #region Helpers + private static RubyArray InternalDivMod(double self, double other) { + double div = System.Math.Floor(self / other); + double mod = self - (div * other); + if (other * mod < 0) { + mod += other; + div -= 1.0; + } + object intDiv = div; + if (!Double.IsInfinity(div) && !Double.IsNaN(div)) { + intDiv = ToInt(div); + } + return RubyOps.MakeArray2(intDiv, mod); + } + + // I did think about whether to put this into Protocols but really it is only checking the FloatDomainErrors + // Also, it is only used in FloatOps.Floor and FloatOps.Ceil. + // These get invoked from Protocols.ConvertToInteger anyway so it would all get a bit circular. + private static object CastToInteger(double value) { + try { + if (Double.IsPositiveInfinity(value)) { + throw new FloatDomainError("Infinity"); + } + if (Double.IsNegativeInfinity(value)) { + throw new FloatDomainError("-Infinity"); + } + if (Double.IsNaN(value)) { + throw new FloatDomainError("NaN"); + } + return System.Convert.ToInt32(value); + } catch (OverflowException) { + return BigInteger.Create(value); + } + } + + public static Exception CreateFloatDomainError(string message) { + return new FloatDomainError("NaN"); + } + public static Exception CreateFloatDomainError(string message, Exception inner) { + return new FloatDomainError("NaN", inner); + } + #endregion + } +} + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/HashOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/HashOps.cs new file mode 100644 index 0000000000..6c3544be2a --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/HashOps.cs @@ -0,0 +1,218 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using IronRuby.Runtime; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Builtins { + + /// + /// Dictionary inherits from Object, mixes in Enumerable. + /// Ruby hash is a Dictionary{object, object}, but it adds default value/proc + /// + /// TODO: Not all .NET types implement the right Equals, GetHashCode semantics (e.g. List{object}) + /// + [RubyClass("Hash", Extends = typeof(Hash), Inherits = typeof(object)), Includes(typeof(IDictionary), Copy = true)] + public static class HashOps { + + [RubyConstructor] + public static Hash/*!*/ Hash(RubyClass/*!*/ self) { + return new Hash(self.Context.EqualityComparer); + } + + [RubyConstructor] + public static Hash/*!*/ Hash(BlockParam block, RubyClass/*!*/ self, object defaultValue) { + if (block != null) { + throw RubyExceptions.CreateArgumentError("wrong number of arguments"); + } + return new Hash(self.Context.EqualityComparer, null, defaultValue); + } + + [RubyConstructor] + public static Hash/*!*/ Hash([NotNull]BlockParam/*!*/ defaultProc, RubyClass/*!*/ self) { + return new Hash(self.Context.EqualityComparer, defaultProc.Proc, null); + } + + // Reinitialization. Not called when a factory/non-default ctor is called. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static Hash/*!*/ Initialize(Hash/*!*/ self) { + Assert.NotNull(self); + return self; + } + + // Reinitialization. Not called when a factory/non-default ctor is called. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static Hash/*!*/ Initialize(BlockParam block, Hash/*!*/ self, object defaultValue) { + Assert.NotNull(self); + if (block != null) { + throw RubyExceptions.CreateArgumentError("wrong number of arguments"); + } + self.DefaultProc = null; + self.DefaultValue = defaultValue; + return self; + } + + // Reinitialization. Not called when a factory/non-default ctor is called. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static Hash/*!*/ Initialize([NotNull]BlockParam/*!*/ defaultProc, Hash/*!*/ self) { + Assert.NotNull(self, defaultProc); + + self.DefaultProc = defaultProc.Proc; + self.DefaultValue = null; + return self; + } + + [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)] + public static Hash/*!*/ InitializeCopy(RubyContext/*!*/ context, Hash/*!*/ self, [NotNull]Hash/*!*/ source) { + RubyUtils.RequiresNotFrozen(context, self); + + self.DefaultProc = source.DefaultProc; + self.DefaultValue = source.DefaultValue; + IDictionaryOps.ReplaceData(self, source); + return self; + } + + #region Singleton Methods + + private static readonly CallSite>/*!*/ + _CreateHashSite = CallSite>.Create(RubySites.InstanceCallAction("new")); + + [RubyMethod("[]", RubyMethodAttributes.PublicSingleton)] + public static Hash/*!*/ CreateHash(RubyClass/*!*/ self, [NotNull]params object[] items) { + // arg0: hash or first elem + // argk: (k-1)th elem + int itemCount = items.Length; + + IDictionary hash = null; + if (itemCount == 0 || itemCount == 1 && (hash = items[0] as IDictionary) != null) { + Hash newHash = _CreateHashSite.Target(_CreateHashSite, self.Context, self); + return hash != null ? IDictionaryOps.ReplaceData(newHash, hash) : newHash; + } + + if (itemCount % 2 != 0) { + throw new ArgumentException("odd number of arguments for Hash"); + } + + return RubyUtils.MakeHash(self.Context, items); + } + + #endregion + + + #region Instance Methods + + private static readonly CallSite>/*!*/ _DefaultProcSite = + CallSite>.Create(RubySites.InstanceCallAction("call", 2)); + + private static readonly CallSite>/*!*/ _DefaultSite = + CallSite>.Create(RubySites.InstanceCallAction("default", 1)); + + [RubyMethod("[]")] + public static object GetElement(RubyContext/*!*/ context, Hash/*!*/ self, object key) { + object result; + if (!self.TryGetValue(BaseSymbolDictionary.NullToObj(key), out result)) { + return _DefaultSite.Target(_DefaultSite, context, self, key); + } + return result; + } + + [RubyMethod("default")] + public static object GetDefaultValue(RubyContext/*!*/ context, Hash/*!*/ self) { + return self.DefaultValue; + } + + [RubyMethod("default")] + public static object GetDefaultValue(RubyContext/*!*/ context, Hash/*!*/ self, object key) { + if (self.DefaultProc != null) { + return _DefaultProcSite.Target(_DefaultProcSite, context, self.DefaultProc, self, key); + } + return self.DefaultValue; + } + + [RubyMethod("default=")] + public static object SetDefaultValue(RubyContext/*!*/ context, Hash/*!*/ self, object value) { + RubyUtils.RequiresNotFrozen(context, self); + self.DefaultProc = null; + return self.DefaultValue = value; + } + + [RubyMethod("default_proc")] + public static Proc GetDefaultProc(Hash/*!*/ self) { + return self.DefaultProc; + } + + [RubyMethod("inspect")] + public static MutableString/*!*/ Inspect(RubyContext/*!*/ context, Hash/*!*/ self) { + using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(self)) { + if (handle == null) { + return MutableString.Create("{...}"); + } + + MutableString str = MutableString.Create("{"); + bool first = true; + foreach (KeyValuePair pair in self) { + if (first) { + first = false; + } else { + str.Append(", "); + } + str.Append(RubySites.Inspect(context, BaseSymbolDictionary.ObjToNull(pair.Key))); + str.Append("=>"); + str.Append(RubySites.Inspect(context, pair.Value)); + } + str.Append('}'); + return str; + } + } + + [RubyMethod("replace")] + public static Hash/*!*/ Replace(RubyContext/*!*/ context, Hash/*!*/ self, object other) { + if (Object.ReferenceEquals(self, other)) + return self; + + RubyUtils.RequiresNotFrozen(context, self); + + // If we are copying from another Hash, copy the default value/block, otherwise set to nil + Hash otherHash = other as Hash; + self.DefaultValue = (otherHash != null) ? otherHash.DefaultValue : null; + self.DefaultProc = (otherHash != null) ? otherHash.DefaultProc : null; + return IDictionaryOps.ReplaceData(self, IDictionaryOps.ConvertToHash(context, other)); + } + + [RubyMethod("shift")] + public static object Shift(RubyContext/*!*/ context, Hash/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + + if (self.Count == 0) { + return _DefaultSite.Target(_DefaultSite, context, self, null); + } + + IEnumerator> e = self.GetEnumerator(); + e.MoveNext(); + KeyValuePair pair = e.Current; + self.Remove(pair.Key); + + return IDictionaryOps.MakeArray(pair); + } + + #endregion + } + +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Integer.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Integer.cs new file mode 100644 index 0000000000..94f72ca89f --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Integer.cs @@ -0,0 +1,360 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using IronRuby.Runtime; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.Builtins { + + [RubyClass("Integer"), Includes(typeof(Precision))] + public abstract class Integer : Numeric { + + #region induced_from + + /// + /// Convert obj to an Integer, where obj is Fixnum + /// + /// Just returns the Fixnum + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static object InducedFrom(RubyClass/*!*/ self, int obj) { + return obj; + } + + /// + /// Convert obj to an Integer, where obj is Bignum + /// + /// Just returns the Bignum + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static object InducedFrom(RubyClass/*!*/ self, [NotNull]BigInteger/*!*/ obj) { + return obj; + } + + /// + /// Convert obj to an Integer, where obj is Float + /// + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static object InducedFrom(RubyClass/*!*/ self, double obj) { + return RubySites.ToI(self.Context, obj); + } + + /// + /// Convert obj to an Integer, where obj is not Fixnum, Bignum or Float + /// + /// Just throws an error + /// Assumption is object cannot be induced to Integer + [RubyMethod("induced_from", RubyMethodAttributes.PublicSingleton)] + public static int InducedFrom(RubyClass/*!*/ self, object obj) { + throw RubyExceptions.CreateTypeError(String.Format("failed to convert {0} into Integer", RubyUtils.GetClassName(self.Context, obj))); + } + + #endregion + + #region to_i, to_int, floor, ceil, round, truncate + + /// + /// As self is already an Integer, just return self. + /// + [RubyMethod("to_i")] + [RubyMethod("to_int")] + [RubyMethod("floor")] + [RubyMethod("ceil")] + [RubyMethod("round")] + [RubyMethod("truncate")] + public static object ToInteger(object/*!*/ self) { + return self; + } + + #endregion + + #region chr + + [RubyMethod("chr")] + public static MutableString/*!*/ ToChr(RubyContext/*!*/ context, object self) { + int intSelf = Protocols.CastToFixnum(context, self); + if (intSelf < 0 || intSelf > 255) { + throw RubyExceptions.CreateRangeError(String.Format("{0} out of char range", intSelf)); + } + return MutableString.CreateBinary(new byte[] { (byte)intSelf }); + } + + #endregion + + #region downto + + /// + /// Iterates block, passing decreasing values from self down to and including other, where both self and other are Fixnum. + /// + /// self + /// + /// 5.downto(1) { |n| print n, ".. " } + /// print " Liftoff!\n" + /// produces: + /// 5.. 4.. 3.. 2.. 1.. Liftoff! + /// + /// + /// Since both self and other are Fixnum then this algorithm doesn't need to worry about overflowing into Bignum. + /// + [RubyMethod("downto")] + public static object DownTo(BlockParam block, int self, int other) { + if (self >= other && block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + int i = self; + while (i >= other) { + object result; + if (block.Yield(i, out result)) { + return result; + } + i--; + } + return self; + } + + /// + /// Iterates block, passing decreasing values from self down to and including other, where other is not Fixnum (probably Bignum or Float). + /// + /// self + /// + /// Dynamically invokes "-" operator to find next item down. + /// Dynamically invokes "<" operator and takes the negation to see if we have reached the bottom. + /// This approach automatically deals with Floats and overflow/underflow between Fixnum and Bignum. + /// + [RubyMethod("downto")] + public static object DownTo(RubyContext/*!*/ context, BlockParam block, object/*!*/ self, object other) { + object i = self; + object compare = null; + while (RubyOps.IsFalse(compare)) { + // Rather than test i >= other we test !(i < other) + compare = LibrarySites.LessThan(context, i, other); + + // If the comparison failed (i.e. returned null) then we throw an error. + if (compare == null) { + throw RubyExceptions.MakeComparisonError(context, i, other); + } + + // If the comparison worked but returned false then we + if (RubyOps.IsFalse(compare)) { + object result; + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + if (block.Yield(i, out result)) { + return result; + } + + i = LibrarySites.Subtract(context, i, 1); + } + } + return self; + } + + #endregion + + #region integer? + + /// + /// Always returns true. + /// + /// true + [RubyMethod("integer?")] + public static new bool IsInteger(object/*!*/ self) { + return true; + } + + #endregion + + #region next, succ + + /// + /// Returns the Integer equal to self + 1, where self is Fixnum. + /// + /// May return Fixnum or Bignum depending on overflow/underflow. + /// + /// 1.next #=> 2 + /// (-1).next #=> 0 + /// + [RubyMethod("succ")] + [RubyMethod("next")] + public static object Next(int self) { + return FixnumOps.Add(self, 1); + } + + /// + /// Returns the Integer equal to self + 1, where self is not Fixnum (probably Bignum). + /// + /// May return Fixnum or Bignum depending on overflow/underflow. + /// Dynamically invokes "+" operator to get next value. + [RubyMethod("succ")] + [RubyMethod("next")] + public static object Next(RubyContext/*!*/ context, object/*!*/ self) { + return LibrarySites.Add(context, self, 1); + } + + #endregion + + #region times + + /// + /// Iterates block self times, passing in values from zero to self - 1, where self is Fixnum. + /// + /// self + /// + /// 5.times do |i| + /// print i, " " + /// end + /// produces: + /// 0 1 2 3 4 + /// + [RubyMethodAttribute("times")] + public static object Times(BlockParam block, int self) { + if (self > 0 && block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + int i = 0; + while (i < self) { + object result; + if (block.Yield(i, out result)) { + return result; + } + i++; + } + + return self; + } + + /// + /// Iterates block self times, passing in values from zero to self - 1, where self is not Fixnum (probably Bignum). + /// + /// self + /// + /// Dynamically invokes "+" operator to find next item. + /// Dynamically invokes "<" operator to check if we have reached self - 1. + /// + [RubyMethodAttribute("times")] + public static object Times(RubyContext/*!*/ context, BlockParam block, object/*!*/ self) { + object i = 0; + while (RubyOps.IsTrue(LibrarySites.LessThan(context, i, self))) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + object result; + if (block.Yield(i, out result)) { + return result; + } + + i = LibrarySites.Add(context, i, 1); + } + return self; + } + + #endregion + + #region upto + + /// + /// Iterates block, passing in integer values from self up to and including other, where both self and other are Fixnum. + /// + /// self + /// + /// 5.upto(10) { |i| print i, " " } + /// produces: + /// 5 6 7 8 9 10 + /// + /// + /// Since both self and other are Fixnum then this algorithm doesn't need to worry about overflowing into Bignum. + /// + [RubyMethod("upto")] + public static object UpTo(BlockParam block, int self, int other) { + if (block == null && self <= other) { + throw RubyExceptions.NoBlockGiven(); + } + + int i = self; + while (i <= other) { + object result; + if (block.Yield(i, out result)) { + return result; + } + i++; + } + return self; + } + + /// + /// Iterates block, passing in integer values from self up to and including other, where both self and other are Fixnum. + /// + /// self + /// + /// Dynamically invokes "+" operator to find next item down. + /// Dynamically invokes ">" operator and takes the negation to see if we have reached the bottom. + /// This approach automatically deals with Floats and overflow/underflow between Fixnum and Bignum. + /// + [RubyMethod("upto")] + public static object UpTo(RubyContext/*!*/ context, BlockParam block, object/*!*/ self, object other) { + object i = self; + object compare = null; + while (RubyOps.IsFalse(compare)) { + // Rather than test i <= other we test !(i > other) + compare = LibrarySites.GreaterThan(context, i, other); + + // If the comparison failed (i.e. returned null) then we throw an error. + if (compare == null) { + throw RubyExceptions.MakeComparisonError(context, i, other); + } + + // If the comparison worked but returned false then we carry on + if (RubyOps.IsFalse(compare)) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + object result; + if (block.Yield(i, out result)) { + return result; + } + i = LibrarySites.Add(context, i, 1); + } + } + return self; + } + + #endregion + + #region Helpers + + public static object TryUnaryMinus(object obj) { + if (obj is int) { + int i = (int)obj; + return (i != Int32.MinValue) ? ScriptingRuntimeHelpers.Int32ToObject(-i) : -BigInteger.Create(i); + } + + BigInteger bignum = obj as BigInteger; + if ((object)bignum != null) { + return -bignum; + } + + return null; + } + + #endregion + } +} + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/IoOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/IoOps.cs new file mode 100644 index 0000000000..0b69aee781 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/IoOps.cs @@ -0,0 +1,960 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using IronRuby.Runtime; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime.Calls; +using System.Diagnostics; + +namespace IronRuby.Builtins { + + /// + /// Implementation of IO builtin class. + /// + [RubyClass("IO", Extends = typeof(RubyIO)), Includes(typeof(RubyFileOps.Constants))] + public class RubyIOOps { + + // Mixins: File::Constants,Enumerable + + #region Constants + + [RubyConstant] + public const int SEEK_SET = RubyIO.SEEK_SET; + + [RubyConstant] + public const int SEEK_CUR = RubyIO.SEEK_CUR; + + [RubyConstant] + public const int SEEK_END = RubyIO.SEEK_END; + + #endregion + + #region Ruby Constructors + + [RubyConstructor] + public static RubyIO/*!*/ CreateIO(RubyClass/*!*/ self) { + // TODO: should create an IO object with uninitialize stream + throw new NotImplementedException(); + } + + [RubyConstructor] + public static RubyIO/*!*/ CreateIO(RubyClass/*!*/ self, + [DefaultProtocol]int fileDescriptor, [DefaultProtocol, NotNull, Optional]MutableString modeString) { + + // TODO: a new RubyIO should be created here + RubyIO result = self.Context.GetDescriptor(fileDescriptor); + if (modeString != null) { + result.ResetIOMode(modeString.ConvertToString()); + } + return result; + } + + //initialize_copy + + #endregion + + #region Public singleton methods + + [RubyMethod("for_fd", RubyMethodAttributes.PublicSingleton)] + public static RubyIO ForFd(RubyClass/*!*/ self, int fileDescriptor, [DefaultProtocol, NotNull]MutableString/*!*/ modeString) { + return CreateIO(self, fileDescriptor, modeString); + } + + [RubyMethod("foreach", RubyMethodAttributes.PublicSingleton)] + public static void ForEach(BlockParam block, RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + ForEach(block, self, path, self.Context.InputSeparator); + } + + [RubyMethod("foreach", RubyMethodAttributes.PublicSingleton)] + public static void ForEach(BlockParam block, RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path, MutableString separator) { + + using (RubyIO io = new RubyIO(self.Context, File.OpenRead(path.ConvertToString()), "r")) { + Each(block, io, separator); + } + } + + // Explicit overloads + + private readonly static CallSite> _CreateIOSharedSite1 = + CallSite>.Create(RubySites.InstanceCallAction("new", 1)); + + private readonly static CallSite> _CreateIOSharedSite11 = + CallSite>.Create(RubySites.InstanceCallAction("new", 2)); + + // TODO: these static fields are marked as internal since they are referenced from FileOps.cs. There is a static initialization problem where _Default.Binder in RubyContext + // isn't being initialized if these sites are stored in the RubyFileOps class. Perhaps we should do the wholesale consolidation of all sites into LibrarySites.cs? + + internal readonly static CallSite> _CreateIOSharedSite2 = + CallSite>.Create(RubySites.InstanceCallAction("new", 2)); + + internal readonly static CallSite> _CreateIOSharedSite3 = + CallSite>.Create(RubySites.InstanceCallAction("new", 1)); + + internal readonly static CallSite> _CreateIOSharedSite4 = + CallSite>.Create(RubySites.InstanceCallAction("new", 2)); + + internal readonly static CallSite> _CreateIOSharedSite5 = + CallSite>.Create(RubySites.InstanceCallAction("new", 3)); + + // TCPSocket.open(host, port) + internal readonly static CallSite> _CreateIOSharedSite6 = + CallSite>.Create(RubySites.InstanceCallAction("new", 2)); + + // File.open(path, mode, perms) + internal readonly static CallSite> _CreateIOSharedSite7 = + CallSite>.Create(RubySites.InstanceCallAction("new", 3)); + + internal static object TryInvokeOpenBlock(RubyContext/*!*/ context, BlockParam/*!*/ block, RubyIO/*!*/ io) { + if (block == null) + return io; + + using (io) { + object result; + block.Yield(io, out result); + io.Close(); + return result; + } + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(RubyContext/*!*/ context, BlockParam/*!*/ block, RubyClass/*!*/ self, int fileDescriptor) { + RubyIO io = _CreateIOSharedSite1.Target(_CreateIOSharedSite1, context, self, fileDescriptor); + return TryInvokeOpenBlock(context, block, io); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(RubyContext/*!*/ context, BlockParam/*!*/ block, RubyClass/*!*/ self, int fileDescriptor, [NotNull]MutableString/*!*/ mode) { + RubyIO io = _CreateIOSharedSite11.Target(_CreateIOSharedSite11, context, self, fileDescriptor, mode); + return TryInvokeOpenBlock(context, block, io); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(RubyContext/*!*/ context, BlockParam/*!*/ block, RubyClass/*!*/ self, int fileDescriptor, object mode) { + RubyIO io = _CreateIOSharedSite11.Target(_CreateIOSharedSite11, context, self, fileDescriptor, Protocols.CastToString(context, mode)); + return TryInvokeOpenBlock(context, block, io); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(RubyContext/*!*/ context, BlockParam/*!*/ block, RubyClass/*!*/ self, MutableString/*!*/ host, int port) { + RubyIO io = _CreateIOSharedSite6.Target(_CreateIOSharedSite6, context, self, host, port); + return TryInvokeOpenBlock(context, block, io); + } + + private static RubyIO OpenFileForRead(RubyContext/*!*/ context, MutableString/*!*/ path) { + string strPath = path.ConvertToString(); + if (!File.Exists(strPath)) { + throw new Errno.NoEntryError(String.Format("No such file or directory - {0}", strPath)); + } + return new RubyIO(context, File.OpenRead(strPath), "r"); + } + +#if !SILVERLIGHT + + //pipe + + [RubyMethod("popen", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static object OpenPipe(RubyContext/*!*/ context, BlockParam block, RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ command, [DefaultProtocol, Optional, NotNull]MutableString modeString) { + + return TryInvokeOpenBlock(context, block, OpenPipe(context, self, command, modeString)); + } + + [RubyMethod("popen", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static RubyIO/*!*/ OpenPipe(RubyContext/*!*/ context, RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ command, [DefaultProtocol, Optional, NotNull]MutableString modeString) { + + bool preserveEndOfLines; + IOMode mode = RubyIO.ParseIOMode(modeString.ConvertToString(), out preserveEndOfLines); + + ProcessStartInfo startInfo = KernelOps.GetShell(context, command); + startInfo.UseShellExecute = false; + + if (mode == IOMode.ReadOnlyFromStart) { + startInfo.RedirectStandardOutput = true; + } else if (mode == IOMode.WriteOnlyAppend || mode == IOMode.WriteOnlyTruncate) { + startInfo.RedirectStandardInput = true; + } else { + startInfo.RedirectStandardOutput = true; + startInfo.RedirectStandardInput = true; + } + + Process process; + try { + process = Process.Start(startInfo); + } catch (Exception e) { + throw new Errno.NoEntryError(startInfo.FileName, e); + } + + context.ChildProcessExitStatus = new RubyProcess.Status(process); + + StreamReader reader = null; + StreamWriter writer = null; + if (startInfo.RedirectStandardOutput) { + reader = process.StandardOutput; + } + + if (startInfo.RedirectStandardInput) { + writer = process.StandardInput; + } + + return new RubyIO(context, reader, writer, modeString.ConvertToString()); + } + +#endif + + [RubyMethod("select", RubyMethodAttributes.PublicSingleton)] + public static RubyArray Select(RubyContext/*!*/ context, object self, RubyArray read, [Optional]RubyArray write, [Optional]RubyArray error) { + return SelectInternal(context, read, write, error, new TimeSpan(0, 0, 0, 0, Timeout.Infinite)); + } + + [RubyMethod("select", RubyMethodAttributes.PublicSingleton)] + public static RubyArray Select(RubyContext/*!*/ context, object self, RubyArray read, RubyArray write, RubyArray error, int timeoutInSeconds) { + if (timeoutInSeconds < 0) { + throw RubyExceptions.CreateArgumentError("time interval must be positive"); + } + return SelectInternal(context, read, write, error, new TimeSpan(0, 0, timeoutInSeconds)); + } + + [RubyMethod("select", RubyMethodAttributes.PublicSingleton)] + public static RubyArray Select(RubyContext/*!*/ context, object self, RubyArray read, RubyArray write, RubyArray error, double timeoutInSeconds) { + if (timeoutInSeconds < 0) { + throw RubyExceptions.CreateArgumentError("time interval must be positive"); + } + return SelectInternal(context, read, write, error, TimeSpan.FromSeconds(timeoutInSeconds)); + } + + private static RubyArray SelectInternal(RubyContext/*!*/ context, RubyArray read, RubyArray write, RubyArray error, TimeSpan timeout) { + WaitHandle[] handles = null; + RubyArray result; + + if (read == null && write == null && error == null) { + Thread.Sleep(timeout); + return null; + } + + try { + handles = GetWaitHandles(context, read, write, error); + int index; + try { +#if SILVERLIGHT + index = WaitHandle.WaitAny(handles, timeout); +#else + index = WaitHandle.WaitAny(handles, timeout, false); +#endif + if (index == WaitHandle.WaitTimeout) { + return null; + } + } catch (Exception e) { + throw new Errno.InvalidError(e.Message, e); + } + + result = new RubyArray(); + int handleIndex = 0; + result.Add(MakeResult(handles, ref handleIndex, index, read)); + result.Add(MakeResult(handles, ref handleIndex, index, write)); + result.Add(MakeResult(handles, ref handleIndex, index, error)); + } finally { + // should we close the handles? + //if (handles != null) { + // for (int i = 0; i < handles.Length; i++) { + // if (handles[i] != null) { + // handles[i].Close(); + // } + // } + //} + } + return result; + } + + private static RubyArray/*!*/ MakeResult(WaitHandle/*!*/[]/*!*/ handles, ref int handleIndex, int signaling, RubyArray ioObjects) { + RubyArray result = new RubyArray(); + if (ioObjects != null) { + for (int i = 0; i < ioObjects.Count; i++) { +#if SILVERLIGHT + if (handleIndex == signaling || handles[handleIndex].WaitOne(0)) { +#else + if (handleIndex == signaling || handles[handleIndex].WaitOne(0, false)) { +#endif + result.Add(ioObjects[i]); + } + handleIndex++; + } + } + return result; + } + + private static WaitHandle/*!*/[]/*!*/ GetWaitHandles(RubyContext/*!*/ context, RubyArray read, RubyArray write, RubyArray error) { + WaitHandle[] handles = new WaitHandle[ + (read != null ? read.Count : 0) + + (write != null ? write.Count : 0) + + (error != null ? error.Count : 0) + ]; + + int i = 0; + if (read != null) { + foreach (object obj in read) { + handles[i++] = ToIo(context, obj).CreateReadWaitHandle(); + } + } + + if (write != null) { + foreach (object obj in write) { + handles[i++] = ToIo(context, obj).CreateWriteWaitHandle(); + } + } + + if (error != null) { + foreach (object obj in error) { + handles[i++] = ToIo(context, obj).CreateErrorWaitHandle(); + } + } + + return handles; + } + + private static RubyIO/*!*/ ToIo(RubyContext/*!*/ context, object obj) { + RubyIO io = obj as RubyIO; + if (io == null) { + throw RubyExceptions.CreateTypeConversionError(RubyUtils.GetClassName(context, obj), "IO"); + } + return io; + } + + //sysopen + + #endregion + + #region Public instance methods + + [RubyMethod("<<")] + public static RubyIO Output(RubyContext/*!*/ context, RubyIO/*!*/ self, object/*!*/ str) { + _WriteSite.Target(_WriteSite, context, self, str); + return self; + } + + [RubyMethod("binmode")] + public static RubyIO/*!*/ Binmode(RubyIO/*!*/ self) { + if (!self.Closed && self.Position == 0) { + self.PreserveEndOfLines = true; + } + return self; + } + + #region close, close_read, close_write, closed?, close_on_exec (1.9) + + [RubyMethod("close")] + public static void Close(RubyIO/*!*/ self) { + if (self.Closed) { + throw RubyExceptions.CreateIOError("closed stream"); + } + + self.Close(); + } + + // TODO: + [RubyMethod("close_read")] + public static void CloseReader(RubyIO/*!*/ self) { + self.CloseReader(); + } + + // TODO: + [RubyMethod("close_write")] + public static void CloseWriter(RubyIO/*!*/ self) { + self.CloseWriter(); + } + + [RubyMethod("closed?")] + public static bool Closed(RubyIO/*!*/ self) { + return self.Closed; + } + + // TODO: 1.9 only + // close_on_exec= + // close_on_exec? + + #endregion + + //reopen + //stat + + [RubyMethod("eof")] + [RubyMethod("eof?")] + public static bool Eof(RubyIO/*!*/ self) { + self.AssertOpenedForReading(); + return self.IsEndOfStream(); + } + + [RubyMethod("fcntl")] + public static int FileControl(RubyIO/*!*/ self, [DefaultProtocol]int commandId, [Optional]MutableString arg) { + return self.FileControl(commandId, (arg != null) ? arg.ConvertToBytes() : null); + } + + [RubyMethod("fcntl")] + public static int FileControl(RubyIO/*!*/ self, [DefaultProtocol]int commandId, int arg) { + return self.FileControl(commandId, arg); + } + + [RubyMethod("pid")] + public static object Pid(RubyIO/*!*/ self) { + return null; // OK to return null on Windows + } + + [RubyMethod("fileno")] + [RubyMethod("to_i")] + public static int FileNo(RubyIO/*!*/ self) { + return self.FileDescriptor; + } + + [RubyMethod("fsync")] + [RubyMethod("flush")] + public static void Flush(RubyIO/*!*/ self) { + self.AssertOpenedForWriting(); + self.Flush(); + } + + //ioctl + + [RubyMethod("isatty")] + [RubyMethod("tty?")] + public static bool IsAtty(RubyIO/*!*/ self) { + return self.IsConsole; + } + + [RubyMethod("sync")] + public static bool Sync(RubyIO/*!*/ self) { + return self.AutoFlush; + } + + [RubyMethod("sync=")] + public static bool Sync(RubyIO/*!*/ self, bool sync) { + self.AutoFlush = sync; + return sync; + } + + [RubyMethod("to_io")] + public static RubyIO/*!*/ ToIO(RubyIO/*!*/ self) { + return self; + } + + #region external_encoding (1.9), internal_encoding (1.9), set_encoding (1.9) + + // TODO: 1.9 only + [RubyMethod("external_encoding")] + public static RubyEncoding GetExternalEncoding(RubyIO/*!*/ self) { + return (self.ExternalEncoding != null) ? RubyEncoding.GetRubyEncoding(self.ExternalEncoding) : null; + } + + // TODO: 1.9 only + [RubyMethod("internal_encoding")] + public static RubyEncoding GetInternalEncoding(RubyIO/*!*/ self) { + return (self.InternalEncoding != null) ? RubyEncoding.GetRubyEncoding(self.InternalEncoding) : null; + } + + // TODO: 1.9 only + // set_encoding + + #endregion + + #region rewind, seek, pos, tell, lineno + + [RubyMethod("rewind")] + public static void Rewind(RubyContext/*!*/ context, RubyIO/*!*/ self) { + Seek(self, 0, 0); + SetLineNo(context, self, 0); + } + + private static void Seek(RubyIO/*!*/ self, long pos, int seekOrigin) { + if (seekOrigin < 0 || seekOrigin > 2) { + throw RubyExceptions.CreateArgumentError("Invalid argument"); + } + + if (self.IsConsoleDescriptor()) { + throw new Errno.BadFileDescriptorError(); + } + + // TODO: make sure we assert stream is not actually closed + if (self.Closed) { + throw RubyExceptions.CreateArgumentError("trying to seek on a non-existent stream?"); + } + + SeekOrigin origin = SeekOrigin.Current; + if (seekOrigin == SEEK_SET) { + origin = SeekOrigin.Begin; + } else if (seekOrigin == SEEK_END) { + origin = SeekOrigin.End; + } + + self.Seek(pos, origin); + } + + [RubyMethod("seek")] + public static int Seek(RubyIO/*!*/ self, [DefaultProtocol]int pos, [DefaultProtocol, DefaultParameterValue(SEEK_SET)]int seekOrigin) { + Seek(self, (long)pos, seekOrigin); + return 0; + } + + [RubyMethod("seek")] + public static int Seek(RubyIO/*!*/ self, [NotNull]BigInteger/*!*/ pos, [DefaultProtocol, DefaultParameterValue(SEEK_SET)]int seekOrigin) { + long longPos; + if (!pos.AsInt64(out longPos)) { + throw RubyExceptions.CreateRangeError("bignum too big to convert into `long'"); + } + Seek(self, longPos, seekOrigin); + return 0; + } + + [RubyMethod("lineno")] + public static int GetLineNo(RubyContext/*!*/ context, RubyIO/*!*/ self) { + return context.InputProvider.LastInputLineNumber; + } + + [RubyMethod("lineno=")] + public static void SetLineNo(RubyContext/*!*/ context, RubyIO/*!*/ self, [DefaultProtocol]int value) { + context.InputProvider.LastInputLineNumber = value; + } + + [RubyMethod("pos")] + [RubyMethod("tell")] + public static object Pos(RubyIO/*!*/ self) { + if (self.IsConsoleDescriptor()) { + throw new Errno.BadFileDescriptorError(); + } + if (self.Closed) { + throw RubyExceptions.CreateIOError("closed stream"); + } + + if (self.Position <= Int32.MaxValue) { + return (int)self.Position; + } + + return (BigInteger)self.Position; + } + + [RubyMethod("pos=")] + public static void Pos(RubyIO/*!*/ self, [DefaultProtocol]int value) { + if (self.IsConsoleDescriptor()) { + throw new Errno.BadFileDescriptorError(); + } + + self.Seek(value, SeekOrigin.Begin); + } + + #endregion + + #region print, puts, putc + + private static CallSite> _WriteSite = CallSite>. + Create(RubyCallAction.Make("write", 1)); + + // print, puts accept an arbitrary self object (it is called from Kernel#print, puts). + + [RubyMethod("print")] + public static void Print(RubyScope/*!*/ scope, object self) { + Print(scope.RubyContext, self, scope.GetInnerMostClosureScope().LastInputLine); + } + + [RubyMethod("print")] + public static void Print(RubyContext/*!*/ context, object self, object val) { + _WriteSite.Target(_WriteSite, context, self, val); + } + + [RubyMethod("print")] + public static void Print(RubyContext/*!*/ context, object self, [NotNull]params object[]/*!*/ args) { + MutableString delimiter = context.OutputSeparator; + for (int i = 0; i < args.Length; i++) { + MutableString str = ToPrintedString(context, args[i]); + if (delimiter != null) { + str.Append(delimiter); + } + Print(context, self, str); + } + } + + [RubyMethod("putc")] + public static MutableString/*!*/ Putc(RubyContext/*!*/ context, object self, [NotNull]MutableString/*!*/ val) { + if (val.IsEmpty) { + throw RubyExceptions.CreateTypeError("can't convert String into Integer"); + } + + // writes a single byte into the output stream: + var c = MutableString.CreateBinary(val.GetBinarySlice(0, 1)); + _WriteSite.Target(_WriteSite, context, self, c); + + return val; + } + + [RubyMethod("putc")] + public static int Putc(RubyContext/*!*/ context, object self, [DefaultProtocol]int c) { + MutableString str = MutableString.CreateBinary(1).Append(unchecked((byte)c)); + _WriteSite.Target(_WriteSite, context, self, str); + return c; + } + + public static MutableString/*!*/ ToPrintedString(RubyContext/*!*/ context, object obj) { + IDictionary hash; + List list; + MutableString str; + + if ((list = obj as List) != null) { + return IListOps.Join(context, list, Environment.NewLine); + } else if ((hash = obj as IDictionary) != null) { + return IDictionaryOps.ToString(context, hash); + } else if (obj == null) { + return MutableString.Create("nil"); + } else if (obj is bool) { + return MutableString.Create((bool)obj ? "true" : "false"); + } else if (obj is double) { + var result = MutableString.Create(obj.ToString()); + if ((double)(int)(double)obj == (double)obj) { + result.Append(".0"); + } + return result; + } else if ((str = obj as MutableString) != null) { + return str; + } else { + return RubySites.ToS(context, obj); + } + } + + [RubyMethod("puts")] + public static void PutsEmptyLine(RubyContext/*!*/ context, object self) { + _WriteSite.Target(_WriteSite, context, self, MutableString.CreateMutable("\n")); + } + + [RubyMethod("puts")] + public static void Puts(RubyContext/*!*/ context, object self, [NotNull]MutableString/*!*/ str) { + _WriteSite.Target(_WriteSite, context, self, str); + + if (!str.EndsWith('\n')) { + PutsEmptyLine(context, self); + } + } + + [RubyMethod("puts")] + public static void Puts(RubyContext/*!*/ context, object self, [NotNull]object/*!*/ val) { + Puts(context, self, ToPrintedString(context, val)); + } + + [RubyMethod("puts")] + public static void Puts(RubyContext/*!*/ context, object self, [NotNull]params object[]/*!*/ vals) { + for (int i = 0; i < vals.Length; ++i) { + Puts(context, self, vals[i]); + } + } + + [RubyMethod("printf")] + public static void PrintFormatted(RubyContext/*!*/ context, RubyIO/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ format, [NotNull]params object[]/*!*/ args) { + + KernelOps.PrintFormatted(context, null, self, format, args); + } + + #endregion + + #region write, write_nonblock + + [RubyMethod("write")] + public static int Write(RubyIO/*!*/ self, [NotNull]MutableString/*!*/ val) { + self.AssertOpenedForWriting(); + int bytesWritten = self.Write(val); + if (self.AutoFlush) { + self.Flush(); + } + return bytesWritten; + } + + [RubyMethod("write")] + public static int Write(RubyContext/*!*/ context, RubyIO/*!*/ self, object obj) { + return Write(self, Protocols.ConvertToString(context, obj)); + } + + //write_nonblock + + #endregion + + #region read, read_nonblock + + private static byte[] ReadAllBytes(RubyIO/*!*/ io) { + var fixedBuffer = new byte[io.Length]; + io.ReadBytes(fixedBuffer, 0, (int)io.Length); + return fixedBuffer; + } + + [RubyMethod("read")] + public static MutableString/*!*/ Read(RubyIO/*!*/ self) { + self.AssertOpenedForReading(); + + if (!self.PreserveEndOfLines) { + MutableString result = MutableString.CreateBinary(); + int c; + while ((c = self.ReadByteNormalizeEoln()) != -1) { + result.Append((byte)c); + } + return result; + } else { + // TODO: change this once Binary mutable string uses resizable byte[] instead of List + return MutableString.CreateBinary(ReadAllBytes(self)); + } + } + + [RubyMethod("read")] + public static MutableString/*!*/ Read(RubyIO/*!*/ self, [DefaultProtocol]int bytes, [DefaultProtocol, Optional]MutableString buffer) { + self.AssertOpenedForReading(); + if (self.IsEndOfStream()) { + return null; + } + + if (buffer == null) { + buffer = MutableString.CreateBinary(); + } + + buffer.Clear(); + if (!self.PreserveEndOfLines) { + for (int i = 0; i < bytes; ++i) { + int c = self.ReadByteNormalizeEoln(); + if (c == -1) { + return buffer; + } else { + buffer.Append((byte)c); + } + } + } else { + var fixedBuffer = new byte[bytes]; + bytes = self.ReadBytes(fixedBuffer, 0, bytes); + buffer.Append(fixedBuffer, 0, bytes); + } + + return buffer; + } + + [RubyMethod("read", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ ReadFile(RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ path) { + + using (RubyIO io = OpenFileForRead(self.Context, path)) { + return Read(io); + } + } + + [RubyMethod("read", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ Read(RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ path, [DefaultProtocol]int length, [DefaultProtocol, Optional]int offset) { + + if (offset < 0) { + throw new Errno.InvalidError(); + } + + if (length < 0) { + throw RubyExceptions.CreateArgumentError(String.Format("negative length {0} given", length)); + } + + using (RubyIO io = OpenFileForRead(self.Context, path)) { + if (offset > 0) { + io.Seek(offset, SeekOrigin.Begin); + } + return Read(io, length, null); + } + } + + //read_nonblock + + #endregion + + #region readchar, readbyte (1.9), readline, readlines + + // returns a string in 1.9 + [RubyMethod("readchar")] + public static int ReadChar(RubyIO/*!*/ self) { + self.AssertOpenedForReading(); + int c = self.ReadByteNormalizeEoln(); + + if (c == -1) { + throw new EOFError("end of file reached"); + } + + return c; + } + + // readbyte + + [RubyMethod("readline")] + public static MutableString/*!*/ ReadLine(RubyScope/*!*/ scope, RubyIO/*!*/ self) { + return ReadLine(scope, self, scope.RubyContext.InputSeparator); + } + + [RubyMethod("readline")] + public static MutableString/*!*/ ReadLine(RubyScope/*!*/ scope, RubyIO/*!*/ self, [DefaultProtocol]MutableString separator) { + // no dynamic call, modifies $_ scope variable: + MutableString result = Gets(scope, self); + if (result == null) { + throw new EOFError("end of file reached"); + } + + return result; + } + + [RubyMethod("readlines")] + public static RubyArray/*!*/ ReadLines(RubyContext/*!*/ context, RubyIO/*!*/ self) { + return ReadLines(context, self, context.InputSeparator); + } + + [RubyMethod("readlines")] + public static RubyArray/*!*/ ReadLines(RubyContext/*!*/ context, RubyIO/*!*/ self, [DefaultProtocol]MutableString separator) { + RubyArray result = new RubyArray(); + + // no dynamic call, doesn't modify $_ scope variable: + MutableString line; + while ((line = self.ReadLineOrParagraph(separator)) != null) { + result.Add(line); + } + + context.InputProvider.LastInputLineNumber += result.Count; + return result; + } + + [RubyMethod("readlines", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ ReadLines(RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ path) { + + return ReadLines(self, path, self.Context.InputSeparator); + } + + [RubyMethod("readlines", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ ReadLines(RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString path, [DefaultProtocol]MutableString separator) { + + using (RubyIO io = new RubyIO(self.Context, File.OpenRead(path.ConvertToString()), "r")) { + return ReadLines(self.Context, io, separator); + } + } + + + + #endregion + + #region getc, gets, ungetc, getbyte (1.9) + + [RubyMethod("getc")] + public static object Getc(RubyIO/*!*/ self) { + self.AssertOpenedForReading(); + int c = self.ReadByteNormalizeEoln(); + return (c != -1) ? ScriptingRuntimeHelpers.Int32ToObject(c) : null; + } + + [RubyMethod("gets")] + public static MutableString Gets(RubyScope/*!*/ scope, RubyIO/*!*/ self) { + return Gets(scope, self, scope.RubyContext.InputSeparator); + } + + [RubyMethod("gets")] + public static MutableString Gets(RubyScope/*!*/ scope, RubyIO/*!*/ self, [DefaultProtocol]MutableString separator) { + + MutableString result = self.ReadLineOrParagraph(separator); + KernelOps.Taint(scope.RubyContext, result); + + scope.GetInnerMostClosureScope().LastInputLine = result; + scope.RubyContext.InputProvider.IncrementLastInputLineNumber(); + + return result; + } + + // TODO: 1.9 only + // getbyte + + //ungetc + + #endregion + + #region each, each_byte, each_line + + [RubyMethod("each")] + [RubyMethod("each_line")] + public static void Each(RubyContext/*!*/ context, BlockParam block, RubyIO/*!*/ self) { + Each(block, self, context.InputSeparator); + } + + [RubyMethod("each")] + [RubyMethod("each_line")] + public static object Each(BlockParam block, RubyIO/*!*/ self, [DefaultProtocol]MutableString separator) { + self.AssertOpenedForReading(); + + MutableString line; + while ((line = self.ReadLineOrParagraph(separator)) != null) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + KernelOps.Taint(block.RubyContext, line); + + object result; + if (block.Yield(line, out result)) { + return result; + } + } + + return self; + } + + [RubyMethod("each_byte")] + public static object EachByte(BlockParam block, RubyIO/*!*/ self) { + self.AssertOpenedForReading(); + object aByte; + while ((aByte = Getc(self)) != null) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + object result; + if (block.Yield((int)aByte, out result)) { + return result; + } + } + return self; + } + + #endregion + + // TODO: 1.9 only + // bytes -> Enumerable::Enumerator + // lines -> Enumerable::Enumerator + + //readpartial + + [RubyMethod("sysread")] + public static MutableString/*!*/ SystemRead(RubyIO/*!*/ self, [DefaultProtocol]int bytes) { + var fixedBuffer = new byte[bytes]; + int len = self.ReadBytes(fixedBuffer, 0, bytes); + if (len == 0) { + throw new EOFError("end of file reached"); + } + + MutableString result = MutableString.CreateBinary(); + result.Append(fixedBuffer, 0, len); + return result; + } + + //sysseek + //syswrite + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/KernelOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/KernelOps.cs new file mode 100644 index 0000000000..d80bd07aab --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/KernelOps.cs @@ -0,0 +1,1412 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Builtins { + + [RubyModule("Kernel", Extends = typeof(Kernel))] + public static class KernelOps { + + #region Private Instance Methods + + [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)] + public static object InitializeCopy(RubyContext/*!*/ context, object self, object source) { + RubyClass selfClass = context.GetClassOf(self); + RubyClass sourceClass = context.GetClassOf(source); + if (sourceClass != selfClass) { + throw RubyExceptions.CreateTypeError("initialize_copy should take same class object"); + } + + if (context.IsObjectFrozen(self)) { + throw RubyExceptions.CreateTypeError(String.Format("can't modify frozen {0}", selfClass.Name)); + } + + return self; + } + + [RubyMethod("singleton_method_added", RubyMethodAttributes.PrivateInstance)] + public static void MethodAdded(object self, object methodName) { + // nop + } + + [RubyMethod("singleton_method_removed", RubyMethodAttributes.PrivateInstance)] + public static void MethodRemoved(object self, object methodName) { + // nop + } + + [RubyMethod("singleton_method_undefined", RubyMethodAttributes.PrivateInstance)] + public static void MethodUndefined(object self, object methodName) { + // nop + } + + #endregion + + #region Private Instance & Singleton Methods + + // TODO: 1.8/1.9 + [RubyMethod("Array", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("Array", RubyMethodAttributes.PublicSingleton)] + public static IList/*!*/ ToArray(RubyContext/*!*/ context, object self, object obj) { + IList result = Protocols.AsArray(context, obj); + if (result != null) { + return result; + } + + result = Protocols.TryConvertToArray(context, obj); + if (result != null) { + return result; + } + + result = new RubyArray(); + result.Add(obj); + return result; + } + + [RubyMethod("Float", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("Float", RubyMethodAttributes.PublicSingleton)] + public static double ToFloat(RubyContext/*!*/ context, object self, object obj) { + return Protocols.ConvertToFloat(context, obj); + } + + [RubyMethod("Integer", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("Integer", RubyMethodAttributes.PublicSingleton)] + public static object/*!*/ ToInteger(RubyContext/*!*/ context, object self, object obj) { + // TODO: MRI converts strings with base prefix ("0x1", "0d1", "0o1") and octals "000" as well + // should the protocol do that or is is a specificity of this method? + return Protocols.ConvertToInteger(context, obj); + } + + [RubyMethod("String", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("String", RubyMethodAttributes.PublicSingleton)] + public static object/*!*/ ToString(RubyContext/*!*/ context, object self, object obj) { + return Protocols.ConvertToString(context, obj); + } + + #region `, exec, system + +#if !SILVERLIGHT + // Looks for RUBYSHELL and then COMSPEC under Windows + // It appears that COMSPEC is a special environment variable that cannot be undefined + internal static ProcessStartInfo/*!*/ GetShell(RubyContext/*!*/ context, MutableString/*!*/ command) { + PlatformAdaptationLayer pal = context.DomainManager.Platform; + string shell = pal.GetEnvironmentVariable("RUBYSHELL"); + if (shell == null) { + shell = pal.GetEnvironmentVariable("COMSPEC"); + } + return new ProcessStartInfo(shell, String.Format("/C \"{0}\"", command.ConvertToString())); + } + + private static MutableString/*!*/ JoinArguments(MutableString/*!*/[]/*!*/ args) { + MutableString result = MutableString.CreateMutable(); + + for (int i = 0; i < args.Length; i++) { + result.Append(args[i]); + if (args.Length > 1 && i < args.Length - 1) { + result.Append(" "); + } + } + + return result; + } + + private static Process/*!*/ ExecuteProcessAndWait(ProcessStartInfo/*!*/ psi) { + psi.UseShellExecute = false; + psi.RedirectStandardError = true; + try { + Process p = Process.Start(psi); + p.WaitForExit(); + return p; + } catch (Exception e) { + throw new Errno.NoEntryError(psi.FileName, e); + } + } + + internal static Process/*!*/ ExecuteProcessCapturingStandardOutput(ProcessStartInfo/*!*/ psi) { + psi.UseShellExecute = false; + psi.RedirectStandardError = true; + psi.RedirectStandardOutput = true; + + try { + return Process.Start(psi); + } catch (Exception e) { + throw new Errno.NoEntryError(psi.FileName, e); + } + } + + // Executes a command in a shell child process + private static Process/*!*/ ExecuteCommandInShell(RubyContext/*!*/ context, MutableString/*!*/ command) { + return ExecuteProcessAndWait(GetShell(context, command)); + } + + // Executes a command directly in a child process - command is the name of the executable + private static Process/*!*/ ExecuteCommand(MutableString/*!*/ command, MutableString[]/*!*/ args) { + return ExecuteProcessAndWait(new ProcessStartInfo(command.ToString(), JoinArguments(args).ToString())); + } + + // Backtick always executes the command in a shell child process + + [RubyMethod("`", RubyMethodAttributes.PrivateInstance, BuildConfig = "!SILVERLIGHT")] + [RubyMethod("`", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static MutableString/*!*/ ExecuteCommand(RubyContext/*!*/ context, object self, [DefaultProtocol, NotNull]MutableString/*!*/ command) { + Process p = ExecuteProcessCapturingStandardOutput(GetShell(context, command)); + MutableString result = MutableString.Create(p.StandardOutput.ReadToEnd()); + context.ChildProcessExitStatus = new RubyProcess.Status(p); + return result; + } + + // Overloads of exec and system will always execute using the Windows shell if there is only the command parameter + // If args parameter is passed, it will execute the command directly without going to the shell. + + [RubyMethod("exec", RubyMethodAttributes.PrivateInstance, BuildConfig = "!SILVERLIGHT")] + [RubyMethod("exec", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static void Execute(RubyContext/*!*/ context, object self, [DefaultProtocol, NotNull]MutableString/*!*/ command) { + Process p = ExecuteCommandInShell(context, command); + context.ChildProcessExitStatus = p.ExitCode; + Exit(self, p.ExitCode); + } + + [RubyMethod("exec", RubyMethodAttributes.PrivateInstance, BuildConfig = "!SILVERLIGHT")] + [RubyMethod("exec", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static void Execute(RubyContext/*!*/ context, object self, [DefaultProtocol, NotNull]MutableString/*!*/ command, [NotNull]params object[]/*!*/ args) { + Process p = ExecuteCommand(command, Protocols.CastToStrings(context, args)); + context.ChildProcessExitStatus = p.ExitCode; + Exit(self, p.ExitCode); + } + + [RubyMethod("system", RubyMethodAttributes.PrivateInstance, BuildConfig = "!SILVERLIGHT")] + [RubyMethod("system", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static bool System(RubyContext/*!*/ context, object self, [DefaultProtocol, NotNull]MutableString/*!*/ command) { + Process p = ExecuteCommandInShell(context, command); + context.ChildProcessExitStatus = p.ExitCode; + return p.ExitCode == 0; + } + + [RubyMethod("system", RubyMethodAttributes.PrivateInstance, BuildConfig = "!SILVERLIGHT")] + [RubyMethod("system", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static bool System(RubyContext/*!*/ context, object self, [DefaultProtocol, NotNull]MutableString/*!*/ command, [NotNull]params object[]/*!*/ args) { + Process p = ExecuteCommand(command, Protocols.CastToStrings(context, args)); + context.ChildProcessExitStatus = p.ExitCode; + return p.ExitCode == 0; + } +#endif + #endregion + + //abort + + [RubyMethod("at_exit", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("at_exit", RubyMethodAttributes.PublicSingleton)] + public static Proc/*!*/ AtExit(BlockParam/*!*/ block, object self) { + if (block == null) { + throw RubyExceptions.CreateArgumentError("called without a block"); + } + + block.RubyContext.RegisterShutdownHandler(block); + return block.Proc; + } + + [RubyMethod("autoload", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("autoload", RubyMethodAttributes.PublicSingleton)] + public static void SetAutoloadedConstant(RubyScope/*!*/ scope, object self, + [DefaultProtocol]string/*!*/ constantName, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + ModuleOps.SetAutoloadedConstant(scope.GetInnerMostModule(), constantName, path); + } + + [RubyMethod("autoload?", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("autoload?", RubyMethodAttributes.PublicSingleton)] + public static MutableString GetAutoloadedConstantPath(RubyScope/*!*/ scope, object self, [DefaultProtocol]string/*!*/ constantName) { + return ModuleOps.GetAutoloadedConstantPath(scope.GetInnerMostModule(), constantName); + } + + [RubyMethod("binding", RubyMethodAttributes.PrivateInstance)] + public static Binding/*!*/ GetLocalScope(RubyScope/*!*/ scope, object self) { + return new Binding(scope); + } + + [RubyMethod("block_given?", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("iterator?", RubyMethodAttributes.PrivateInstance)] + public static bool HasBlock(RubyScope/*!*/ scope, object self) { + var methodScope = scope.GetInnerMostMethodScope(); + return methodScope != null && methodScope.BlockParameter != null; + } + + //callcc + + [RubyMethod("caller", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("caller", RubyMethodAttributes.PublicSingleton)] + [RubyStackTraceHidden] + public static RubyArray/*!*/ GetStackTrace(RubyContext/*!*/ context, object self, [DefaultParameterValue(1)]int skipFrames) { + if (skipFrames < 0) { + return new RubyArray(); + } + + return RubyExceptionData.CreateBacktrace(context, skipFrames); + } + + //chomp + //chomp! + //chop + //chop! + + [RubyMethod("eval", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("eval", RubyMethodAttributes.PublicSingleton)] + public static object Evaluate(RubyScope/*!*/ scope, object self, [NotNull]MutableString/*!*/ code, + [Optional]Binding binding, [Optional, NotNull]MutableString file, [DefaultParameterValue(1)]int line) { + + RubyScope targetScope = (binding != null) ? binding.LocalScope : scope; + return RubyUtils.Evaluate(code, targetScope, targetScope.SelfObject, null, file, line); + } + + [RubyMethod("eval", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("eval", RubyMethodAttributes.PublicSingleton)] + public static object Evaluate(RubyScope/*!*/ scope, object self, [NotNull]MutableString/*!*/ code, + [NotNull]Proc/*!*/ procBinding, [Optional, NotNull]MutableString file, [DefaultParameterValue(1)]int line) { + + return RubyUtils.Evaluate(code, procBinding.LocalScope, procBinding.LocalScope.SelfObject, null, file, line); + } + + [RubyMethod("exit", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("exit", RubyMethodAttributes.PublicSingleton)] + public static void Exit(object self) { + Exit(self, 1); + } + + [RubyMethod("exit", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("exit", RubyMethodAttributes.PublicSingleton)] + public static void Exit(object self, bool isSuccessful) { + Exit(self, isSuccessful ? 0 : 1); + } + + [RubyMethod("exit", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("exit", RubyMethodAttributes.PublicSingleton)] + public static void Exit(object self, int exitCode) { + throw new SystemExit(exitCode, "exit"); + } + + [RubyMethod("exit!", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("exit!", RubyMethodAttributes.PublicSingleton)] + public static void TerminateExecution(RubyContext/*!*/ context, object self) { + TerminateExecution(context, self, 1); + } + + [RubyMethod("exit!", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("exit!", RubyMethodAttributes.PublicSingleton)] + public static void TerminateExecution(RubyContext/*!*/ context, object self, bool isSuccessful) { + TerminateExecution(context, self, isSuccessful ? 0 : 1); + } + + [RubyMethod("exit!", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("exit!", RubyMethodAttributes.PublicSingleton)] + public static void TerminateExecution(RubyContext/*!*/ context, object self, int exitCode) { + context.DomainManager.Platform.TerminateScriptExecution(exitCode); + } + + //fork + + [RubyMethod("global_variables", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("global_variables", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ GetGlobalVariableNames(RubyContext/*!*/ context, object self) { + RubyArray result = new RubyArray(); + lock (context.GlobalVariablesSyncRoot) { + foreach (KeyValuePair global in context.GlobalVariables) { + if (global.Value.IsEnumerated) { + // TODO: Ruby 1.9 returns symbols: + result.Add(MutableString.Create(global.Key)); + } + } + } + return result; + } + + //gsub + //gsub! + + // TODO: in Ruby 1.9, these two methods will do different things so we will likely have to have two + // separate implementations of these methods + [RubyMethod("lambda", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("lambda", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("proc", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("proc", RubyMethodAttributes.PublicSingleton)] + public static Proc/*!*/ CreateLambda(BlockParam/*!*/ block, object self) { + if (block == null) { + throw RubyExceptions.CreateArgumentError("tried to create Proc object without a block"); + } + + // doesn't preserve the class: + return block.Proc.ToLambda(); + } + + #region load, require + + [RubyMethod("load", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("load", RubyMethodAttributes.PublicSingleton)] + public static bool Load(RubyScope/*!*/ scope, object self, + [DefaultProtocol, NotNull]MutableString/*!*/ libraryName, [Optional]bool wrap) { + return scope.RubyContext.Loader.LoadFile(scope.GlobalScope, self, libraryName, wrap ? LoadFlags.LoadIsolated : LoadFlags.None); + } + + [RubyMethod("load_assembly", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("load_assembly", RubyMethodAttributes.PublicSingleton)] + public static bool LoadAssembly(RubyScope/*!*/ scope, object self, + [DefaultProtocol, NotNull]MutableString/*!*/ assemblyName, [DefaultProtocol, Optional, NotNull]MutableString libraryNamespace) { + + string initializer = libraryNamespace != null ? LibraryInitializer.GetFullTypeName(libraryNamespace.ConvertToString()) : null; + return scope.RubyContext.Loader.LoadAssembly(assemblyName.ConvertToString(), initializer, true); + } + + [RubyMethod("require", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("require", RubyMethodAttributes.PublicSingleton)] + public static bool Require(RubyScope/*!*/ scope, object self, + [DefaultProtocol, NotNull]MutableString/*!*/ libraryName) { + return scope.RubyContext.Loader.LoadFile(scope.GlobalScope, self, libraryName, LoadFlags.LoadOnce | LoadFlags.AppendExtensions); + } + + #endregion + + [RubyMethod("local_variables", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("local_variables", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ GetLocalVariableNames(RubyScope/*!*/ scope, object self) { + List names = scope.GetVisibleLocalNames(); + + RubyArray result = new RubyArray(names.Count); + for (int i = 0; i < names.Count; i++) { + result.Add(MutableString.Create(names[i])); + } + return result; + } + + [RubyMethod("loop", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("loop", RubyMethodAttributes.PublicSingleton)] + public static object Loop(BlockParam/*!*/ block, object self) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + while (true) { + object result; + if (block.Yield(out result)) { + return result; + } + } + } + + [RubyMethod("method_missing", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("method_missing", RubyMethodAttributes.PublicSingleton)] + [RubyStackTraceHidden] + public static object MethodMissing(RubyContext/*!*/ context, object/*!*/ self, SymbolId symbol, [NotNull]params object[]/*!*/ args) { + string name = SymbolTable.IdToString(symbol); + throw RubyExceptions.CreateMethodMissing(context, self, name); + } + + #region open + + private static object OpenWithBlock(BlockParam/*!*/ block, RubyIO file) { + try { + object result; + block.Yield(file, out result); + return result; + } finally { + file.Close(); + } + } + + [RubyMethod("open", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static RubyIO/*!*/ Open(RubyContext/*!*/ context, object self, [NotNull]MutableString/*!*/ path, MutableString mode) { + string fileName = path.ConvertToString(); + if (fileName.Length > 0 && fileName[0] == '|') { + throw new NotImplementedError(); + } + return new RubyFile(context, fileName, (mode != null) ? mode.ToString() : "r"); + } + + [RubyMethod("open", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, object self, [NotNull]MutableString/*!*/ path, MutableString mode) { + RubyIO file = Open(context, self, path, mode); + return OpenWithBlock(block, file); + } + + [RubyMethod("open", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static RubyIO/*!*/ Open(RubyContext/*!*/ context, object self, [NotNull]MutableString/*!*/ path, int mode) { + string fileName = path.ConvertToString(); + if (fileName.Length > 0 && fileName[0] == '|') { + throw new NotImplementedError(); + } + return new RubyFile(context, fileName, (RubyFileMode)mode); + } + + [RubyMethod("open", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, object self, [NotNull]MutableString/*!*/ path, int mode) { + RubyIO file = Open(context, self, path, mode); + return OpenWithBlock(block, file); + } + + [RubyMethod("open", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static RubyIO/*!*/ Open(RubyContext/*!*/ context, object self, [NotNull]MutableString/*!*/ path) { + return Open(context, self, path, MutableString.Create("r")); + } + + [RubyMethod("open", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, object self, [NotNull]MutableString/*!*/ path) { + return Open(context, block, self, path, MutableString.Create("r")); + } + + [RubyMethod("open", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static RubyIO/*!*/ Open(RubyContext/*!*/ context, object self, object path) { + return Open(context, self, Protocols.CastToString(context, path), null); + } + + [RubyMethod("open", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, object self, object path) { + return Open(context, block, self, Protocols.CastToString(context, path), null); + } + + [RubyMethod("open", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static RubyIO/*!*/ Open(RubyContext/*!*/ context, object self, object path, [NotNull]object mode) { + return Open(context, self, Protocols.CastToString(context, path), Protocols.CastToString(context, mode)); + } + + [RubyMethod("open", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, object self, object path, [NotNull]object mode) { + return Open(context, block, self, Protocols.CastToString(context, path), Protocols.CastToString(context, mode)); + } + + #endregion + + #region p, print, printf, putc, puts, warn, gets, getc + + [RubyMethod("p", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("p", RubyMethodAttributes.PublicSingleton)] + public static void PrintInspect(RubyContext/*!*/ context, object self, [NotNull]params object[]/*!*/ args) { + for (int i = 0; i < args.Length; i++) { + args[i] = RubySites.Inspect(context, args[i]); + } + + // no dynamic dispatch to "puts": + RubyIOOps.Puts(context, context.StandardOutput, args); + } + + [RubyMethod("print", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("print", RubyMethodAttributes.PublicSingleton)] + public static void Print(RubyScope/*!*/ scope, object self) { + // no dynamic dispatch to "print": + RubyIOOps.Print(scope, scope.RubyContext.StandardOutput); + } + + [RubyMethod("print", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("print", RubyMethodAttributes.PublicSingleton)] + public static void Print(RubyContext/*!*/ context, object self, object val) { + // no dynamic dispatch to "print": + RubyIOOps.Print(context, context.StandardOutput, val); + } + + [RubyMethod("print", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("print", RubyMethodAttributes.PublicSingleton)] + public static void Print(RubyContext/*!*/ context, object self, [NotNull]params object[]/*!*/ args) { + // no dynamic dispatch to "print": + RubyIOOps.Print(context, context.StandardOutput, args); + } + + // this overload is called only if the first parameter is string: + [RubyMethod("printf", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("printf", RubyMethodAttributes.PublicSingleton)] + public static void PrintFormatted(RubyContext/*!*/ context, object self, + [NotNull]MutableString/*!*/ format, [NotNull]params object[]/*!*/ args) { + + PrintFormatted(context, self, context.StandardOutput, format, args); + } + + private static CallSite> _WriteSite = CallSite>. + Create(RubyCallAction.Make("write", 1)); + + [RubyMethod("printf", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("printf", RubyMethodAttributes.PublicSingleton)] + public static void PrintFormatted(RubyContext/*!*/ context, object self, + object io, [NotNull]object/*!*/ format, [NotNull]params object[]/*!*/ args) { + + Debug.Assert(!(io is MutableString)); + + // TODO: BindAsObject attribute on format? + // format cannot be strongly typed to MutableString due to ambiguity between signatures (MS, object) vs (object, MS) + + _WriteSite.Target(_WriteSite, context, io, Sprintf(context, self, Protocols.CastToString(context, format), args)); + } + + [RubyMethod("putc", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("putc", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ Putc(RubyContext/*!*/ context, object self, [NotNull]MutableString/*!*/ arg) { + // no dynamic dispatch: + return RubyIOOps.Putc(context, context.StandardOutput, arg); + } + + [RubyMethod("putc", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("putc", RubyMethodAttributes.PublicSingleton)] + public static int Putc(RubyContext/*!*/ context, object self, [DefaultProtocol]int arg) { + // no dynamic dispatch: + return RubyIOOps.Putc(context, context.StandardOutput, arg); + } + + [RubyMethod("puts", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("puts", RubyMethodAttributes.PublicSingleton)] + public static void PutsEmptyLine(RubyContext/*!*/ context, object self) { + // call directly, no dynamic dispatch to "self": + RubyIOOps.PutsEmptyLine(context, context.StandardOutput); + } + + [RubyMethod("puts", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("puts", RubyMethodAttributes.PublicSingleton)] + public static void PutString(RubyContext/*!*/ context, object self, object arg) { + // call directly, no dynamic dispatch to "self": + RubyIOOps.Puts(context, context.StandardOutput, arg); + } + + [RubyMethod("puts", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("puts", RubyMethodAttributes.PublicSingleton)] + public static void PutString(RubyContext/*!*/ context, object self, [NotNull]MutableString/*!*/ arg) { + // call directly, no dynamic dispatch to "self": + RubyIOOps.Puts(context, context.StandardOutput, arg); + } + + [RubyMethod("puts", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("puts", RubyMethodAttributes.PublicSingleton)] + public static void PutString(RubyContext/*!*/ context, object self, [NotNull]params object[]/*!*/ args) { + // call directly, no dynamic dispatch to "self": + RubyIOOps.Puts(context, context.StandardOutput, args); + } + + [RubyMethod("warn", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("warn", RubyMethodAttributes.PublicSingleton)] + public static void ReportWarning(RubyContext/*!*/ context, object self, object message) { + if (context.Verbose != null) { + // MRI: unlike Kernel#puts this outputs \n even if the message ends with \n: + _WriteSite.Target(_WriteSite, context, context.StandardErrorOutput, RubyIOOps.ToPrintedString(context, message)); + RubyIOOps.PutsEmptyLine(context, context.StandardErrorOutput); + } + } + + private static CallSite> _GetcSite = CallSite>. + Create(RubyCallAction.Make("getc", 0)); + + // TODO: not supported in Ruby 1.9 + [RubyMethod("getc", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("getc", RubyMethodAttributes.PublicSingleton)] + public static object ReadInputCharacter(RubyContext/*!*/ context, object self) { + context.ReportWarning("getc is obsolete; use STDIN.getc instead"); + return _GetcSite.Target(_GetsSite, context, context.StandardInput); + } + + private static CallSite> _GetsSite = CallSite>. + Create(RubyCallAction.Make("gets", 1)); + + [RubyMethod("gets", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("gets", RubyMethodAttributes.PublicSingleton)] + public static object ReadInputLine(RubyContext/*!*/ context, object self) { + return ReadInputLine(context, self, context.InputSeparator); + } + + [RubyMethod("gets", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("gets", RubyMethodAttributes.PublicSingleton)] + public static object ReadInputLine(RubyContext/*!*/ context, object self, [NotNull]MutableString/*!*/ separator) { + return _GetsSite.Target(_GetsSite, context, context.StandardInput, separator); + } + + #endregion + + #region raise, fail + + [RubyMethod("raise", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("raise", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("fail", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("fail", RubyMethodAttributes.PublicSingleton)] + [RubyStackTraceHidden] + public static void RaiseException(RubyContext/*!*/ context, object self) { + Exception exception = context.CurrentException; + if (exception == null) { + exception = new RuntimeError(); + } + + // rethrow semantics, preserves the backtrace associated with the exception: + throw exception; + } + + [RubyMethod("raise", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("raise", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("fail", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("fail", RubyMethodAttributes.PublicSingleton)] + [RubyStackTraceHidden] + public static void RaiseException(object self, [NotNull]MutableString/*!*/ message) { + throw RubyExceptionData.InitializeException(new RuntimeError(message.ToString()), message); + } + + private static readonly CallSite> _ExceptionSharedSite = CallSite>.Create( + RubySites.InstanceCallAction("exception")); + + private static readonly CallSite> _ExceptionWithMessageSharedSite = CallSite>.Create( + RubySites.InstanceCallAction("exception", 1)); + + [RubyMethod("raise", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("raise", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("fail", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("fail", RubyMethodAttributes.PublicSingleton)] + [RubyStackTraceHidden] + public static void RaiseException(RubyContext/*!*/ context, object self, + object/*!*/ obj, [Optional]object arg, [Optional]RubyArray backtrace) { + + if (RubySites.RespondTo(context, obj, "exception")) { + Exception e; + if (arg != Missing.Value) { + e = _ExceptionWithMessageSharedSite.Target(_ExceptionWithMessageSharedSite, context, obj, arg) as Exception; + } else { + e = _ExceptionSharedSite.Target(_ExceptionSharedSite, context, obj) as Exception; + } + + if (e != null) { + if (backtrace != null) { + ExceptionOps.SetBacktrace(e, backtrace); + } + + // rethrow semantics, preserves the backtrace associated with the exception: + throw e; + } + } + + throw RubyExceptions.CreateTypeError("exception class/object expected"); + } + + #endregion + + [RubyMethod("rand", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("rand", RubyMethodAttributes.PublicSingleton)] + public static double Rand(RubyContext/*!*/ context, object self) { + return new Random().NextDouble(); + } + + [RubyMethod("rand", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("rand", RubyMethodAttributes.PublicSingleton)] + public static object Rand(RubyContext/*!*/ context, object self, int limit) { + if (limit == 0) { + return Rand(context, self); + } + + return ScriptingRuntimeHelpers.Int32ToObject((int)(new Random().NextDouble() * (limit - 1))); + } + + [RubyMethod("rand", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("rand", RubyMethodAttributes.PublicSingleton)] + public static object Rand(RubyContext/*!*/ context, object self, double limit) { + if (limit < 1) { + return Rand(context, self); + } + + return ScriptingRuntimeHelpers.Int32ToObject((int)(new Random().NextDouble() * (limit - 1))); + } + + [RubyMethod("rand", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("rand", RubyMethodAttributes.PublicSingleton)] + public static object Rand(RubyContext/*!*/ context, object self, [NotNull]BigInteger/*!*/ limit) { + throw new NotImplementedError("rand(BigInteger) not implemented"); + } + + [RubyMethod("rand", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("rand", RubyMethodAttributes.PublicSingleton)] + public static object Rand(RubyContext/*!*/ context, object self, object limit) { + if (limit == null) { + return Rand(context, self); + } + return Rand(context, self, Protocols.CastToFixnum(context, limit)); + } + + //readline + //readlines + //scan + + [RubyMethod("select", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("select", RubyMethodAttributes.PublicSingleton)] + public static RubyArray Select(RubyContext/*!*/ context, object self, RubyArray read, [Optional]RubyArray write, [Optional]RubyArray error) { + return RubyIOOps.Select(context, null, read, write, error); + } + + [RubyMethod("select", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("select", RubyMethodAttributes.PublicSingleton)] + public static RubyArray Select(RubyContext/*!*/ context, object self, RubyArray read, [Optional]RubyArray write, [Optional]RubyArray error, int timeoutInSeconds) { + return RubyIOOps.Select(context, null, read, write, error, timeoutInSeconds); + } + + [RubyMethod("select", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("select", RubyMethodAttributes.PublicSingleton)] + public static RubyArray Select(RubyContext/*!*/ context, object self, RubyArray read, [Optional]RubyArray write, [Optional]RubyArray error, double timeoutInSeconds) { + return RubyIOOps.Select(context, null, read, write, error, timeoutInSeconds); + } + + [RubyMethod("set_trace_func", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("set_trace_func", RubyMethodAttributes.PublicSingleton)] + public static Proc SetTraceListener(RubyContext/*!*/ context, object self, Proc listener) { + if (listener != null && !context.RubyOptions.EnableTracing) { + throw new NotSupportedException("Tracing is not supported unless -trace option is specified."); + } + return context.TraceListener = listener; + } + + [RubyMethod("sleep", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("sleep", RubyMethodAttributes.PublicSingleton)] + public static void Sleep(object self) { + Thread.Sleep(Timeout.Infinite); + } + + [RubyMethod("sleep", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("sleep", RubyMethodAttributes.PublicSingleton)] + public static int Sleep(object self, int seconds) { + if (seconds < 0) { + throw RubyExceptions.CreateArgumentError("time interval must be positive"); + } + + long ms = seconds * 1000; + Thread.Sleep(ms > Int32.MaxValue ? Timeout.Infinite : (int)ms); + return seconds; + } + + [RubyMethod("sleep", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("sleep", RubyMethodAttributes.PublicSingleton)] + public static double Sleep(object self, double seconds) { + if (seconds < 0) { + throw RubyExceptions.CreateArgumentError("time interval must be positive"); + } + + double ms = seconds * 1000; + Thread.Sleep(ms > Int32.MaxValue ? Timeout.Infinite : (int)ms); + return seconds; + } + + //split + + [RubyMethod("format", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("format", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("sprintf", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("sprintf", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ Sprintf(RubyContext/*!*/ context, object self, + [DefaultProtocol, NotNull]MutableString/*!*/ format, [NotNull]params object[] args) { + + return new StringFormatter(context, format.ConvertToString(), args).Format(); + } + + //srand + //sub + //sub! + //syscall + //test + + //trace_var + +#if !SILVERLIGHT // Signals dont make much sense in Silverlight as cross-process communication is not allowed + [RubyMethod("trap", RubyMethodAttributes.PrivateInstance, BuildConfig = "!SILVERLIGHT")] + [RubyMethod("trap", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static object Trap(RubyContext/*!*/ context, object self, object signalId, Proc proc) { + return Signal.Trap(context, self, signalId, proc); + } + + [RubyMethod("trap", RubyMethodAttributes.PrivateInstance, BuildConfig = "!SILVERLIGHT")] + [RubyMethod("trap", RubyMethodAttributes.PublicSingleton, BuildConfig = "!SILVERLIGHT")] + public static object Trap(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, object self, object signalId) { + return Signal.Trap(context, block, self, signalId); + } + +#endif + + //untrace_var + + #region throw, catch + + private sealed class ThrowCatchUnwinder : StackUnwinder { + public readonly string/*!*/ Label; + + internal ThrowCatchUnwinder(string/*!*/ label, object returnValue) + : base(returnValue) { + Label = label; + } + } + + [ThreadStatic] + private static Stack _catchSymbols; + + [RubyMethod("catch", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("catch", RubyMethodAttributes.PublicSingleton)] + public static object Catch(BlockParam/*!*/ block, object self, [DefaultProtocol]string/*!*/ label) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + try { + if (_catchSymbols == null) { + _catchSymbols = new Stack(); + } + _catchSymbols.Push(label); + + try { + object result; + block.Yield(label, out result); + return result; + } catch (ThrowCatchUnwinder unwinder) { + if (unwinder.Label == label) { + return unwinder.ReturnValue; + } + + throw; + } + } finally { + _catchSymbols.Pop(); + } + } + + [RubyMethod("throw", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("throw", RubyMethodAttributes.PublicSingleton)] + public static void Throw(object self, [DefaultProtocol]string/*!*/ label, [DefaultParameterValue(null)]object returnValue) { + if (_catchSymbols == null || !_catchSymbols.Contains(label)) { + throw RubyExceptions.CreateNameError(String.Format("uncaught throw `{0}'", label)); + } + + throw new ThrowCatchUnwinder(label, returnValue); + } + + #endregion + + #endregion + + #region Public Instance Methods + + #region ==, ===, =~, eql?, equal?, hash + + [RubyMethod("==")] + [RubyMethod("eql?")] + public static bool ValueEquals(object self, object other) { + return RubyUtils.ValueEquals(self, other); + } + + [RubyMethod("=~")] + public static bool Match(object self, object other) { + // Default implementation of match that is overridden in descendents (notably String and Regexp) + return false; + } + + // calls == by default + [RubyMethod("===")] + public static bool HashEquals(RubyContext/*!*/ context, object self, object other) { + return Protocols.IsEqual(context, self, other); + } + + [RubyMethod("hash")] + public static int Hash(object self) { + return RubyUtils.GetHashCode(self); + } + + [RubyMethod("equal?")] + public static bool Equal(object self, object other) { + // Comparing object IDs is (potentially) expensive because it forces us + // to generate InstanceData and a new object ID + //return GetObjectId(self) == GetObjectId(other); + if (self == other) { + return true; + } + + if (RubyUtils.IsRubyValueType(self) && RubyUtils.IsRubyValueType(other)) { + return object.Equals(self, other); + } + + return false; + } + + #endregion + + #region __id__, id, object_id, class + + [RubyMethod("id")] + public static int GetId(RubyContext/*!*/ context, object self) { + context.ReportWarning("Object#id will be deprecated; use Object#object_id"); + return GetObjectId(context, self); + } + + [RubyMethod("__id__")] + [RubyMethod("object_id")] + public static int GetObjectId(RubyContext/*!*/ context, object self) { + return RubyUtils.GetObjectId(context, self); + } + + [RubyMethod("type")] + public static RubyClass/*!*/ GetClassObsolete(RubyContext/*!*/ context, object self) { + context.ReportWarning("Object#type will be deprecated; use Object#class"); + return context.GetClassOf(self); + } + + [RubyMethod("class")] + public static RubyClass/*!*/ GetClass(RubyContext/*!*/ context, object self) { + return context.GetClassOf(self); + } + + #endregion + + #region clone, dup + + [RubyMethod("clone")] + public static object/*!*/ Clone(RubyContext/*!*/ context, object self) { + object result; + if (!RubyUtils.TryDuplicateObject(context, self, true, out result)) { + throw RubyExceptions.CreateTypeError(String.Format("can't clone {0}", RubyUtils.GetClassName(context, self))); + } + return context.TaintObjectBy(result, self); + } + + [RubyMethod("dup")] + public static object/*!*/ Duplicate(RubyContext/*!*/ context, object self) { + object result; + if (!RubyUtils.TryDuplicateObject(context, self, false, out result)) { + throw RubyExceptions.CreateTypeError(String.Format("can't dup {0}", RubyUtils.GetClassName(context, self))); + } + return context.TaintObjectBy(result, self); + } + + #endregion + + [RubyMethod("display")] + public static void Display(RubyContext/*!*/ context, object self) { + // TODO: + } + + [RubyMethod("extend")] + public static object Extend(RubyContext/*!*/ context, object self, [NotNull]RubyModule/*!*/ module, + [NotNull]params RubyModule/*!*/[]/*!*/ modules) { + Assert.NotNull(self, modules); + RubyUtils.RequireNonClasses(modules); + + // Kernel#extend_object inserts the module at the beginning of the object's singleton ancestors list; + // ancestors after extend: [modules[0], modules[1], ..., modules[N-1], self-singleton, ...] + for (int i = modules.Length - 1; i >= 0; i--) { + _ModuleExtentObjectSite.Target(_ModuleExtentObjectSite, context, modules[i], self); + _ModuleExtendedSite.Target(_ModuleExtendedSite, context, modules[i], self); + } + + _ModuleExtentObjectSite.Target(_ModuleExtentObjectSite, context, module, self); + _ModuleExtendedSite.Target(_ModuleExtendedSite, context, module, self); + + return self; + } + + private static readonly CallSite> _ModuleExtentObjectSite = + CallSite>.Create( + RubyCallAction.Make("extend_object", RubyCallSignature.WithImplicitSelf(1)) + ); + + private static readonly CallSite> _ModuleExtendedSite = + CallSite>.Create( + RubyCallAction.Make("extended", RubyCallSignature.WithImplicitSelf(1)) + ); + + + #region frozen?, freeze, tainted?, taint, untaint + + [RubyMethod("frozen?")] + public static bool Frozen([NotNull]MutableString/*!*/ self) { + return self.IsFrozen; + } + + [RubyMethod("frozen?")] + public static bool Frozen(RubyContext/*!*/ context, object self) { + if (RubyUtils.IsRubyValueType(self)) { + return false; // can't freeze value types + } + return context.IsObjectFrozen(self); + } + + [RubyMethod("freeze")] + public static object Freeze(RubyContext/*!*/ context, object self) { + if (RubyUtils.IsRubyValueType(self)) { + return self; // can't freeze value types + } + context.FreezeObject(self); + return self; + } + + [RubyMethod("tainted?")] + public static bool Tainted(RubyContext/*!*/ context, object self) { + if (RubyUtils.IsRubyValueType(self)) { + return false; // can't taint value types + } + return context.IsObjectTainted(self); + } + + [RubyMethod("taint")] + public static object Taint(RubyContext/*!*/ context, object self) { + if (RubyUtils.IsRubyValueType(self)) { + return self; + } + context.SetObjectTaint(self, true); + return self; + } + + [RubyMethod("untaint")] + public static object Untaint(RubyContext/*!*/ context, object self) { + if (RubyUtils.IsRubyValueType(self)) { + return self; + } + context.SetObjectTaint(self, false); + return self; + } + + #endregion + + [RubyMethod("instance_eval")] + public static object Evaluate(RubyScope/*!*/ scope, object self, [NotNull]MutableString/*!*/ code, + [Optional, NotNull]MutableString file, [DefaultParameterValue(1)]int line) { + + RubyClass singleton = scope.RubyContext.CreateSingletonClass(self); + return RubyUtils.Evaluate(code, scope, self, singleton, file, line); + } + + [RubyMethod("instance_eval")] + public static object InstanceEval([NotNull]BlockParam/*!*/ block, object self) { + return RubyUtils.EvaluateInSingleton(self, block); + } + + #region nil?, instance_of?, is_a?, kind_of? + + [RubyMethod("nil?")] + public static bool IsNil(object self) { + return self == null; + } + + [RubyMethod("is_a?")] + [RubyMethod("kind_of?")] + public static bool IsKindOf(object self, RubyModule/*!*/ other) { + ContractUtils.RequiresNotNull(other, "other"); + return other.Context.GetImmediateClassOf(self).HasAncestor(other); + } + + [RubyMethod("instance_of?")] + public static bool IsOfClass(object self, RubyModule/*!*/ other) { + ContractUtils.RequiresNotNull(other, "other"); + return other.Context.GetClassOf(self) == other; + } + + #endregion + + #region instance_variables, instance_variable_defined?, instance_variable_get, instance_variable_set, remove_instance_variable + + [RubyMethod("instance_variables")] + public static RubyArray/*!*/ InstanceVariables(RubyContext/*!*/ context, object self) { + string[] names = context.GetInstanceVariableNames(self); + + RubyArray result = new RubyArray(names.Length); + foreach (string name in names) { + result.Add(MutableString.Create(name)); + } + return result; + } + + [RubyMethod("instance_variable_get")] + public static object InstanceVariableGet(RubyContext/*!*/ context, object self, [DefaultProtocol]string/*!*/ name) { + object value; + if (!context.TryGetInstanceVariable(self, name, out value)) { + // We didn't find it, check if the name is valid + RubyUtils.CheckInstanceVariableName(name); + return null; + } + return value; + } + + [RubyMethod("instance_variable_set")] + public static object InstanceVariableSet(RubyContext/*!*/ context, object self, [DefaultProtocol]string/*!*/ name, object value) { + RubyUtils.CheckInstanceVariableName(name); + context.SetInstanceVariable(self, name, value); + return value; + } + + [RubyMethod("instance_variable_defined?")] + public static bool InstanceVariableDefined(RubyContext/*!*/ context, object self, [DefaultProtocol]string/*!*/ name) { + object value; + if (!context.TryGetInstanceVariable(self, name, out value)) { + // We didn't find it, check if the name is valid + RubyUtils.CheckInstanceVariableName(name); + return false; + } + + return true; + } + + [RubyMethod("remove_instance_variable", RubyMethodAttributes.PrivateInstance)] + public static object RemoveInstanceVariable(RubyContext/*!*/ context, object/*!*/ self, [DefaultProtocol]string/*!*/ name) { + object value; + if (!context.TryRemoveInstanceVariable(self, name, out value)) { + // We didn't find it, check if the name is valid + RubyUtils.CheckInstanceVariableName(name); + + throw RubyExceptions.CreateNameError(String.Format("instance variable `{0}' not defined", name)); + } + + return value; + } + + #endregion + + [RubyMethod("respond_to?")] + public static bool RespondTo(RubyContext/*!*/ context, object self, + [DefaultProtocol]string/*!*/ methodName, [DefaultProtocol, Optional]bool includePrivate) { + + return context.ResolveMethod(self, methodName, includePrivate) != null; + } + + #region __send__, send + + private static readonly Dictionary, CallSite> _sites = + new Dictionary, CallSite>(); + + public static CallSite/*!*/ GetOrCreateSendSite(string/*!*/ methodName, RubyCallSignature callSignature) + where TSite : class { + + lock (_sites) { + CallSite site; + if (_sites.TryGetValue(new KeyValuePair(methodName, callSignature), out site)) { + return (CallSite)site; + } + + var newSite = CallSite.Create(RubyCallAction.Make(methodName, callSignature)); + _sites.Add(new KeyValuePair(methodName, callSignature), newSite); + return newSite; + } + } + + [RubyMethod("send"), RubyMethod("__send__")] + public static object SendMessage(RubyScope/*!*/ scope, object self) { + throw RubyExceptions.CreateArgumentError("no method name given"); + } + + // ARGS: 0 + [RubyMethod("send"), RubyMethod("__send__")] + public static object SendMessage(RubyScope/*!*/ scope, object self, [DefaultProtocol]string/*!*/ methodName) { + var site = GetOrCreateSendSite>( + methodName, new RubyCallSignature(0, RubyCallFlags.HasScope | RubyCallFlags.HasImplicitSelf) + ); + return site.Target(site, scope, self); + } + + // ARGS: 0& + [RubyMethod("send"), RubyMethod("__send__")] + public static object SendMessage(RubyScope/*!*/ scope, BlockParam block, object self, [DefaultProtocol]string/*!*/ methodName) { + var site = GetOrCreateSendSite>( + methodName, new RubyCallSignature(0, RubyCallFlags.HasScope | RubyCallFlags.HasImplicitSelf | RubyCallFlags.HasBlock) + ); + return site.Target(site, scope, self, block != null ? block.Proc : null); + } + + // ARGS: 1 + [RubyMethod("send"), RubyMethod("__send__")] + public static object SendMessage(RubyScope/*!*/ scope, object self, [DefaultProtocol]string/*!*/ methodName, + object arg1) { + + var site = GetOrCreateSendSite>( + methodName, new RubyCallSignature(1, RubyCallFlags.HasScope | RubyCallFlags.HasImplicitSelf) + ); + + return site.Target(site, scope, self, arg1); + } + + // ARGS: 1& + [RubyMethod("send"), RubyMethod("__send__")] + public static object SendMessage(RubyScope/*!*/ scope, BlockParam block, object self, [DefaultProtocol]string/*!*/ methodName, + object arg1) { + + var site = GetOrCreateSendSite>( + methodName, new RubyCallSignature(1, RubyCallFlags.HasScope | RubyCallFlags.HasImplicitSelf | RubyCallFlags.HasBlock) + ); + return site.Target(site, scope, self, block != null ? block.Proc : null, arg1); + } + + // ARGS: 2 + [RubyMethod("send"), RubyMethod("__send__")] + public static object SendMessage(RubyScope/*!*/ scope, object self, [DefaultProtocol]string/*!*/ methodName, + object arg1, object arg2) { + + var site = GetOrCreateSendSite>( + methodName, new RubyCallSignature(2, RubyCallFlags.HasScope | RubyCallFlags.HasImplicitSelf) + ); + + return site.Target(site, scope, self, arg1, arg2); + } + + // ARGS: 2& + [RubyMethod("send"), RubyMethod("__send__")] + public static object SendMessage(RubyScope/*!*/ scope, BlockParam block, object self, [DefaultProtocol]string/*!*/ methodName, + object arg1, object arg2) { + + var site = GetOrCreateSendSite>( + methodName, new RubyCallSignature(2, RubyCallFlags.HasScope | RubyCallFlags.HasImplicitSelf | RubyCallFlags.HasBlock) + ); + return site.Target(site, scope, self, block != null ? block.Proc : null, arg1, arg2); + } + + // ARGS: 3 + [RubyMethod("send"), RubyMethod("__send__")] + public static object SendMessage(RubyScope/*!*/ scope, object self, [DefaultProtocol]string/*!*/ methodName, + object arg1, object arg2, object arg3) { + + var site = GetOrCreateSendSite>( + methodName, new RubyCallSignature(3, RubyCallFlags.HasScope | RubyCallFlags.HasImplicitSelf) + ); + + return site.Target(site, scope, self, arg1, arg2, arg3); + } + + // ARGS: 3& + [RubyMethod("send"), RubyMethod("__send__")] + public static object SendMessage(RubyScope/*!*/ scope, BlockParam block, object self, [DefaultProtocol]string/*!*/ methodName, + object arg1, object arg2, object arg3) { + + var site = GetOrCreateSendSite>( + methodName, new RubyCallSignature(3, RubyCallFlags.HasScope | RubyCallFlags.HasImplicitSelf | RubyCallFlags.HasBlock) + ); + return site.Target(site, scope, self, block != null ? block.Proc : null, arg1, arg2, arg3); + } + + // ARGS: N + [RubyMethod("send"), RubyMethod("__send__")] + public static object SendMessage(RubyScope/*!*/ scope, object self, [DefaultProtocol]string/*!*/ methodName, + [NotNull]params object[] args) { + + var site = GetOrCreateSendSite>( + methodName, new RubyCallSignature(1, RubyCallFlags.HasScope | RubyCallFlags.HasImplicitSelf | RubyCallFlags.HasSplattedArgument) + ); + + return site.Target(site, scope, self, RubyOps.MakeArrayN(args)); + } + + // ARGS: N& + [RubyMethod("send"), RubyMethod("__send__")] + public static object SendMessage(RubyScope/*!*/ scope, BlockParam block, object self, [DefaultProtocol]string/*!*/ methodName, + [NotNull]params object[] args) { + + var site = GetOrCreateSendSite>( + methodName, new RubyCallSignature(1, RubyCallFlags.HasScope | RubyCallFlags.HasImplicitSelf | RubyCallFlags.HasSplattedArgument | + RubyCallFlags.HasBlock) + ); + return site.Target(site, scope, self, block != null ? block.Proc : null, RubyOps.MakeArrayN(args)); + } + + #endregion + + #region method, methods, (private|protected|public|singleton)_methods + + [RubyMethod("method")] + public static RubyMethod/*!*/ GetMethod(RubyContext/*!*/ context, object self, [DefaultProtocol]string/*!*/ name) { + RubyMemberInfo info = context.ResolveMethod(self, name, true); + if (info == null) { + throw RubyExceptions.CreateUndefinedMethodError(context.GetClassOf(self), name); + } + return new RubyMethod(self, info, name); + } + + [RubyMethod("methods")] + public static RubyArray/*!*/ GetMethods(RubyContext/*!*/ context, object self, [DefaultParameterValue(true)]bool inherited) { + RubyClass immediateClass = context.GetImmediateClassOf(self); + if (!inherited && !immediateClass.IsSingletonClass) { + return new RubyArray(); + } + + return ModuleOps.GetMethods(immediateClass, inherited, RubyMethodAttributes.Public | RubyMethodAttributes.Protected); + } + + [RubyMethod("singleton_methods")] + public static RubyArray/*!*/ GetSingletonMethods(RubyContext/*!*/ context, object self, [DefaultParameterValue(true)]bool inherited) { + RubyClass immediateClass = context.GetImmediateClassOf(self); + return ModuleOps.GetMethods(immediateClass, inherited, RubyMethodAttributes.Singleton | RubyMethodAttributes.Public | RubyMethodAttributes.Protected); + } + + [RubyMethod("private_methods")] + public static RubyArray/*!*/ GetPrivateMethods(RubyContext/*!*/ context, object self, [DefaultParameterValue(true)]bool inherited) { + return GetMethods(context, self, inherited, RubyMethodAttributes.PrivateInstance); + } + + [RubyMethod("protected_methods")] + public static RubyArray/*!*/ GetProtectedMethods(RubyContext/*!*/ context, object self, [DefaultParameterValue(true)]bool inherited) { + return GetMethods(context, self, inherited, RubyMethodAttributes.ProtectedInstance); + } + + [RubyMethod("public_methods")] + public static RubyArray/*!*/ GetPublicMethods(RubyContext/*!*/ context, object self, [DefaultParameterValue(true)]bool inherited) { + return GetMethods(context, self, inherited, RubyMethodAttributes.PublicInstance); + } + + private static RubyArray/*!*/ GetMethods(RubyContext/*!*/ context, object self, bool inherited, RubyMethodAttributes attributes) { + RubyClass immediateClass = context.GetImmediateClassOf(self); + return ModuleOps.GetMethods(immediateClass, inherited, attributes); + } + + #endregion + + #region inspect, to_s + + /// + /// Returns a string containing a human-readable representation of obj. + /// If not overridden, uses the to_s method to generate the string. + /// + /// + /// [ 1, 2, 3..4, 'five' ].inspect #=> "[1, 2, 3..4, \"five\"]" + /// Time.new.inspect #=> "Wed Apr 09 08:54:39 CDT 2003" + /// + [RubyMethod("inspect")] + public static MutableString/*!*/ Inspect(RubyContext/*!*/ context, object self) { + if (context.GetClassOf(self) == context.ObjectClass) { + return RubyUtils.ObjectToMutableString(context, self); + } else { + return RubySites.ToS(context, self); + } + } + + [RubyMethod("to_a")] + public static RubyArray/*!*/ ToA(RubyScope/*!*/ scope, object self) { + // Return an array that contains self + RubyArray result = new RubyArray(new object[] { self }); + + return scope.RubyContext.TaintObjectBy(result, self); + } + + [RubyMethod("to_s")] + public static MutableString/*!*/ ToS(RubyContext/*!*/ context, object self) { + return RubyUtils.ObjectToMutableString(context, self).TaintBy(self, context); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/LibrarySites.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/LibrarySites.cs new file mode 100644 index 0000000000..66b2b1ce13 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/LibrarySites.cs @@ -0,0 +1,205 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + public class LibrarySites { + private static CallSite> _EqlSite = CallSite>.Create( + InstanceCallAction("eql?", 1)); + + public static bool Eql(RubyContext/*!*/ context, object self, object other) { + return _EqlSite.Target(_EqlSite, context, self, other); + } + + private static CallSite> GreaterThanSharedSite = CallSite>.Create( + InstanceCallAction(">", 1)); + + public static object GreaterThan(RubyContext/*!*/ context, object lhs, object rhs) { + return GreaterThanSharedSite.Target(GreaterThanSharedSite, context, lhs, rhs); + } + + private static CallSite> GreaterThanOrEqualSharedSite = CallSite>.Create( + InstanceCallAction(">=", 1)); + + public static object GreaterThanOrEqual(RubyContext/*!*/ context, object lhs, object rhs) { + return GreaterThanOrEqualSharedSite.Target(GreaterThanOrEqualSharedSite, context, lhs, rhs); + } + + private static CallSite> LessThanSharedSite = CallSite>.Create( + InstanceCallAction("<", 1)); + + public static object LessThan(RubyContext/*!*/ context, object lhs, object rhs) { + return LessThanSharedSite.Target(LessThanSharedSite, context, lhs, rhs); + } + + private static CallSite> LessThanOrEqualSharedSite = CallSite>.Create( + InstanceCallAction("<=", 1)); + + public static object LessThanOrEqual(RubyContext/*!*/ context, object lhs, object rhs) { + return LessThanOrEqualSharedSite.Target(LessThanOrEqualSharedSite, context, lhs, rhs); + } + + private static CallSite> _CompareSharedSite = CallSite>.Create( + InstanceCallAction("<=>", 1)); + + /// + /// Calls <=>, returning the actual result. You should probably use Protocols.Compare, unless you + /// have some reason for wanting to call <=>directly (e.g. Range.Initialize does this). + /// + public static object Compare(RubyContext/*!*/ context, object lhs, object rhs) { + return _CompareSharedSite.Target(_CompareSharedSite, context, lhs, rhs); + } + + private static CallSite> _ModuloOpSite = CallSite>.Create( + InstanceCallAction("%", 1)); + + public static object ModuloOp(RubyContext/*!*/ context, object self, object other) { + return _ModuloOpSite.Target(_ModuloOpSite, context, self, other); + } + + private static CallSite> _ModuloSite = CallSite>.Create( + InstanceCallAction("modulo", 1)); + + public static object Modulo(RubyContext/*!*/ context, object self, object other) { + return _ModuloSite.Target(_ModuloSite, context, self, other); + } + + private static CallSite> _MultiplySite = CallSite>.Create( + InstanceCallAction("*", 1)); + + public static object Multiply(RubyContext/*!*/ context, object self, object other) { + return _MultiplySite.Target(_MultiplySite, context, self, other); + } + + private static CallSite> _MinusSite = CallSite>.Create( + InstanceCallAction("-", 1)); + public static object Minus(RubyContext/*!*/ context, object self, object other) { + return _MinusSite.Target(_MinusSite, context, self, other); + } + + private static CallSite> _UnaryMinusSite = CallSite>.Create( + InstanceCallAction("-@")); + public static object UnaryMinus(RubyContext/*!*/ context, object self) { + return _UnaryMinusSite.Target(_UnaryMinusSite, context, self); + } + + private static CallSite> _PowerSite = CallSite>.Create( + InstanceCallAction("**", 1)); + + public static object Power(RubyContext/*!*/ context, object self, object other) { + return _PowerSite.Target(_PowerSite, context, self, other); + } + + private static CallSite> _AddSite = CallSite>.Create( + InstanceCallAction("+", 1)); + + public static object Add(RubyContext/*!*/ context, object self, object other) { + return _AddSite.Target(_AddSite, context, self, other); + } + + private static CallSite> _SubtractSite = CallSite>.Create( + InstanceCallAction("-", 1)); + + public static object Subtract(RubyContext/*!*/ context, object self, object other) { + return _SubtractSite.Target(_SubtractSite, context, self, other); + } + + private static CallSite> _DivideSite = CallSite>.Create( + InstanceCallAction("/", 1)); + + public static object Divide(RubyContext/*!*/ context, object self, object other) { + return _DivideSite.Target(_DivideSite, context, self, other); + } + + private static CallSite> _DivSite = CallSite>.Create( + InstanceCallAction("div", 1)); + + public static object Div(RubyContext/*!*/ context, object self, object other) { + return _DivSite.Target(_DivSite, context, self, other); + } + + private static CallSite> _RemainderSite = CallSite>.Create( + InstanceCallAction("remainder", 1)); + + public static object Remainder(RubyContext/*!*/ context, object self, object other) { + return _RemainderSite.Target(_RemainderSite, context, self, other); + } + + private static CallSite> _DivModSite = CallSite>.Create( + InstanceCallAction("divmod", 1)); + public static RubyArray DivMod(RubyContext/*!*/ context, object self, object other) { + return _DivModSite.Target(_DivModSite, context, self, other); + } + + private static CallSite> _QuoSite = CallSite>.Create( + InstanceCallAction("quo", 1)); + public static object Quo(RubyContext/*!*/ context, object self, object other) { + return _QuoSite.Target(_QuoSite, context, self, other); + } + + private static CallSite> _BitRefSite = CallSite>.Create( + InstanceCallAction("[]", 1)); + public static int BitRef(RubyContext/*!*/ context, object self, object other) { + return _BitRefSite.Target(_BitRefSite, context, self, other); + } + + private static CallSite> _IsZeroSite = CallSite>.Create( + InstanceCallAction("zero?")); + public static bool IsZero(RubyContext/*!*/ context, object self) { + return _IsZeroSite.Target(_IsZeroSite, context, self); + } + + private static CallSite> _InducedFromSite = CallSite>.Create( + InstanceCallAction("induced_from", 1)); + public static object InvokeInducedFrom(RubyContext/*!*/ context, RubyClass klass, object value) { + return _InducedFromSite.Target(_InducedFromSite, context, klass, value); + } + + private static CallSite> _PrecSite = CallSite>.Create( + InstanceCallAction("prec", 1)); + public static object InvokePrec(RubyContext/*!*/ context, RubyClass klass, object value) { + return _PrecSite.Target(_PrecSite, context, value, klass); + } + + private static CallSite> _FlattenSite = CallSite>.Create( + InstanceCallAction("flatten", 0)); + public static IList InvokeFlatten(RubyContext/*!*/ context, IList list) { + return _FlattenSite.Target(_FlattenSite, context, list); + } + #region Helpers + + public static RubyCallAction InstanceCallAction(string/*!*/ name, RubyCallSignature signature) { + return RubyCallAction.Make(name, signature); + } + + public static RubyCallAction InstanceCallAction(string/*!*/ name) { + return RubyCallAction.Make(name, RubyCallSignature.Simple(0)); + } + + public static RubyCallAction InstanceCallAction(string/*!*/ name, int argumentCount) { + return RubyCallAction.Make(name, RubyCallSignature.Simple(argumentCount)); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Marshal.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Marshal.cs new file mode 100644 index 0000000000..1ba70e5967 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Marshal.cs @@ -0,0 +1,985 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using IronRuby.Runtime; +using Microsoft.Scripting; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Builtins { + + [RubyModule("Marshal")] + public class RubyMarshal { + + #region Sites + + private const string _MarshalDumpSymbol = "marshal_dump"; + private const string _DumpSymbol = "_dump"; + + private static readonly CallSite> _MarshalDumpSite = + CallSite>.Create(RubySites.InstanceCallAction("marshal_dump")); + + private static readonly CallSite> _DumpSite = + CallSite>.Create(RubySites.InstanceCallAction("_dump", 1)); + + private static readonly CallSite> _MarshalLoadSite = + CallSite>.Create(RubySites.InstanceCallAction("marshal_load", 1)); + + private static readonly CallSite> _LoadSite = + CallSite>.Create(RubySites.InstanceCallAction("_load", 1)); + + private static readonly CallSite> _ReaderProcSite = + CallSite>.Create(RubySites.InstanceCallAction("call", 1)); + + #endregion + + #region Constants + + private static readonly MutableString _positiveInfinityString = MutableString.Create("inf"); + private static readonly MutableString _negativeInfinityString = MutableString.Create("-inf"); + private static readonly MutableString _nanString = MutableString.Create("nan"); + + #endregion + + #region MarshalWriter + + internal class MarshalWriter { + private readonly BinaryWriter/*!*/ _writer; + private readonly RubyContext/*!*/ _context; + private int _recursionLimit; + private readonly Dictionary/*!*/ _symbols; + private readonly Dictionary/*!*/ _objects; +#if !SILVERLIGHT + private readonly StreamingContext/*!*/ _streamingContext; +#endif + + internal MarshalWriter(BinaryWriter/*!*/ writer, RubyContext/*!*/ context, int? limit) { + Assert.NotNull(writer, context); + + _writer = writer; + _context = context; + _recursionLimit = (limit.HasValue ? limit.Value : -1); + _symbols = new Dictionary(); + _objects = new Dictionary(ReferenceEqualityComparer.Instance); +#if !SILVERLIGHT + _streamingContext = new StreamingContext(StreamingContextStates.Other, _context); +#endif + } + + private void WritePreamble() { + _writer.Write((byte)MAJOR_VERSION); + _writer.Write((byte)MINOR_VERSION); + } + + private void WriteBignumValue(BigInteger/*!*/ value) { + char sign; + if (value.Sign > 0) { + sign = '+'; + } else if (value.Sign < 0) { + sign = '-'; + } else { + sign = '0'; + } + _writer.Write((byte)sign); + uint[] bits = value.GetBits(); + int n = bits.Length * 2, mn = bits.Length - 1; + bool truncate = false; + if (bits.Length > 0 && (bits[mn] >> 16) == 0) { + n--; + truncate = true; + } + WriteInt32(n); + for (int i = 0; i < bits.Length; i++) { + if (truncate && i == mn) { + _writer.Write(unchecked((ushort)(bits[i]))); + } else { + _writer.Write(bits[i]); + } + } + } + + private void WriteBignum(BigInteger/*!*/ value) { + _writer.Write((byte)'l'); + WriteBignumValue(value); + } + + private void WriteInt32(int value) { + if (value == 0) { + _writer.Write((byte)0); + } else if (value > 0 && value < 123) { + _writer.Write((byte)(value + 5)); + } else if (value < 0 && value > -124) { + _writer.Write((sbyte)(value - 5)); + } else { + byte[] _buffer = new byte[5]; + _buffer[1] = (byte)value; + _buffer[2] = (byte)(value >> 8); + _buffer[3] = (byte)(value >> 16); + _buffer[4] = (byte)(value >> 24); + + int len = 4; + sbyte lenbyte; + if (value < 0) { + while (_buffer[len] == 0xff) { + len--; + } + lenbyte = (sbyte)-len; + } else { + while (_buffer[len] == 0x00) { + len--; + } + lenbyte = (sbyte)len; + } + _buffer[0] = (byte)lenbyte; + + _writer.Write(_buffer, 0, len + 1); + } + } + + private void WriteFixnum(int value) { + _writer.Write((byte)'i'); + WriteInt32(value); + } + + private void WriteFloat(double value) { + // TODO: Ruby appears to have an optimization that saves the (binary) mantissa at the end of the string + _writer.Write((byte)'f'); + if (Double.IsInfinity(value)) { + if (Double.IsPositiveInfinity(value)) { + WriteStringValue(_positiveInfinityString); + } else { + WriteStringValue(_negativeInfinityString); + } + } else if (Double.IsNaN(value)) { + WriteStringValue(_nanString); + } else { + StringFormatter sf = new StringFormatter(_context, "%.15g", new object[] { value }); + sf.TrailingZeroAfterWholeFloat = false; + WriteStringValue(sf.Format()); + } + } + + class SubclassData { + internal SubclassData(MarshalWriter/*!*/ marshaller, object/*!*/ obj, Type type) { + RubyClass libClass = marshaller._context.GetClass(type); + RubyClass theClass = marshaller._context.GetClassOf(obj); + + if (libClass != theClass && !(obj is RubyStruct)) { + marshaller._writer.Write((byte)'C'); + marshaller.WriteSymbol(theClass.Name); + } + } + } + + private void WriteStringValue(MutableString/*!*/ value) { + // TODO: MutableString should be able to return a byte[] without converting the repr? + byte[] data = MutableString.Create(value).ConvertToBytes(); + WriteInt32(data.Length); + _writer.Write(data); + } + + private void WriteStringValue(string/*!*/ value) { + byte[] data = MutableString.Create(value).ConvertToBytes(); + WriteInt32(data.Length); + _writer.Write(data); + } + + private void WriteString(MutableString/*!*/ value) { + SubclassData instanceWriter = new SubclassData(this, value, typeof(MutableString)); + _writer.Write((byte)'"'); + WriteStringValue(value); + } + + private void WriteRegex(RubyRegex/*!*/ value) { + SubclassData instanceWriter = new SubclassData(this, value, typeof(RubyRegex)); + _writer.Write((byte)'/'); + WriteStringValue(value.GetPattern()); + _writer.Write((byte)value.Options); + } + + private void WriteArray(RubyArray/*!*/ value) { + SubclassData instanceWriter = new SubclassData(this, value, typeof(RubyArray)); + _writer.Write((byte)'['); + WriteInt32(value.Count); + foreach (object obj in value) { + WriteAnObject(obj); + } + } + + private void WriteHash(Hash/*!*/ value) { + if (value.DefaultProc != null) { + throw RubyExceptions.CreateTypeError("can't dump hash with default proc"); + } + + SubclassData instanceWriter = new SubclassData(this, value, typeof(Hash)); + char typeFlag = (value.DefaultValue != null) ? '}' : '{'; + _writer.Write((byte)typeFlag); + WriteInt32(value.Count); + foreach (KeyValuePair pair in value) { + WriteAnObject(pair.Key); + WriteAnObject(pair.Value); + } + if (value.DefaultValue != null) { + WriteAnObject(value.DefaultValue); + } + } + + private void WriteSymbol(string/*!*/ value) { + int position; + if (_symbols.TryGetValue(value, out position)) { + _writer.Write((byte)';'); + WriteInt32(position); + } else { + position = _symbols.Count; + _symbols[value] = position; + _writer.Write((byte)':'); + WriteStringValue(value); + } + } + + private void TestForAnonymous(RubyModule/*!*/ theModule) { + if (theModule.Name == null) { + string objectType = (theModule is RubyClass) ? "class" : "module"; + string displayName = theModule.GetDisplayName(false).ConvertToString(); + string message = String.Format("can't dump anonymous {0} {1}", objectType, displayName); + throw RubyExceptions.CreateTypeError(message); + } + } + + private void WriteObject(object/*!*/ obj) { + _writer.Write((byte)'o'); + RubyClass theClass = _context.GetClassOf(obj); + TestForAnonymous(theClass); + WriteSymbol(theClass.Name); + +#if !SILVERLIGHT + ISerializable serializableObj = (obj as ISerializable); + if (serializableObj != null) { + SerializationInfo info = new SerializationInfo(theClass.GetUnderlyingSystemType(), new FormatterConverter()); + serializableObj.GetObjectData(info, _streamingContext); + int count = info.MemberCount; + try { + // We need this attribute for CLR serialization but it's not compatible with MRI serialization + // Unfortunately, there's no way to test for a value without either iterating over all values + // or throwing an exception if it's not present + if (info.GetValue("#class", typeof(RubyClass)) != null) { + count--; + } + } catch (Exception) { + } + WriteInt32(count); + foreach (SerializationEntry entry in info) { + if (!entry.Name.Equals("#class")) { + WriteSymbol(entry.Name); + WriteAnObject(entry.Value); + } + } + return; + } +#endif + } + + private void WriteUsingDump(object/*!*/ obj) { + _writer.Write((byte)'u'); + RubyClass theClass = _context.GetClassOf(obj); + TestForAnonymous(theClass); + WriteSymbol(theClass.Name); + MutableString dumpResult = _DumpSite.Target(_DumpSite, _context, obj, _recursionLimit) as MutableString; + if (dumpResult == null) { + throw RubyExceptions.CreateTypeError("_dump() must return string"); + } + WriteStringValue(dumpResult); + } + + private void WriteUsingMarshalDump(object/*!*/ obj) { + _writer.Write((byte)'U'); + RubyClass theClass = _context.GetClassOf(obj); + TestForAnonymous(theClass); + WriteSymbol(theClass.Name); + WriteAnObject(_MarshalDumpSite.Target(_MarshalDumpSite, _context, obj)); + } + + private void WriteClass(RubyClass/*!*/ obj) { + _writer.Write((byte)'c'); + TestForAnonymous(obj); + WriteStringValue(obj.Name); + } + + private void WriteModule(RubyModule/*!*/ obj) { + _writer.Write((byte)'m'); + TestForAnonymous(obj); + WriteStringValue(obj.Name); + } + + private void WriteStruct(RubyStruct/*!*/ obj) { + SubclassData instanceWriter = new SubclassData(this, obj, typeof(RubyStruct)); + _writer.Write((byte)'S'); + RubyClass theClass = _context.GetClassOf(obj); + TestForAnonymous(theClass); + WriteSymbol(theClass.Name); + var names = obj.GetNames(); + WriteInt32(names.Count); + foreach (string name in names) { + int index = obj.GetIndex(name); + WriteSymbol(name); + WriteAnObject(obj[index]); + } + } + + private void WriteAnObject(object obj) { + if (_recursionLimit == 0) { + throw RubyExceptions.CreateArgumentError("exceed depth limit"); + } + if (_recursionLimit > 0) { + _recursionLimit--; + } + + if (obj is int) { + int value = (int)obj; + if (value < -(1 << 30) || value >= (1 << 30)) { + obj = (BigInteger)value; + } + } + + // TODO: use RubyUtils.IsRubyValueType? + if (obj == null) { + _writer.Write((byte)'0'); + } else if (obj is bool) { + _writer.Write((byte)((bool)obj ? 'T' : 'F')); + } else if (obj is int) { + WriteFixnum((int)obj); + } else if (obj is SymbolId) { + WriteSymbol(SymbolTable.IdToString((SymbolId)obj)); + } else { + int objectRef; + if (_objects.TryGetValue(obj, out objectRef)) { + _writer.Write((byte)'@'); + WriteInt32(objectRef); + } else { + objectRef = _objects.Count; + _objects[obj] = objectRef; + + // TODO: replace with a table-driven implementation + // TODO: visibility? + bool implementsDump = (_context.ResolveMethod(obj, _DumpSymbol, true) != null); + bool implementsMarshalDump = (_context.ResolveMethod(obj, _MarshalDumpSymbol, true) != null); + + bool writeInstanceData = false; + string[] instanceNames = null; + + if (!implementsDump && !implementsMarshalDump) { + // Neither "_dump" nor "marshal_dump" writes instance vars separately + instanceNames = _context.GetInstanceVariableNames(obj); + if (instanceNames.Length > 0) { + _writer.Write((byte)'I'); + writeInstanceData = true; + } + } + + if (!implementsDump || implementsMarshalDump) { + // "_dump" doesn't write "extend" info but "marshal_dump" does + RubyClass theClass = _context.GetImmediateClassOf(obj); + if (theClass.IsSingletonClass) { + foreach (var mixin in theClass.Mixins) { + _writer.Write((byte)'e'); + WriteSymbol(mixin.Name); + } + } + } + + if (obj is double) { + WriteFloat((double)obj); + } else if (obj is float) { + WriteFloat((double)(float)obj); + } else if (obj is BigInteger) { + WriteBignum((BigInteger)obj); + } else if (implementsMarshalDump) { + WriteUsingMarshalDump(obj); + } else if (implementsDump) { + WriteUsingDump(obj); + } else if (obj is MutableString) { + WriteString((MutableString)obj); + } else if (obj is RubyArray) { + WriteArray((RubyArray)obj); + } else if (obj is Hash) { + WriteHash((Hash)obj); + } else if (obj is RubyRegex) { + WriteRegex((RubyRegex)obj); + } else if (obj is RubyClass) { + WriteClass((RubyClass)obj); + } else if (obj is RubyModule) { + WriteModule((RubyModule)obj); + } else if (obj is RubyStruct) { + WriteStruct((RubyStruct)obj); + } else { + if (writeInstanceData) { + // Overwrite the "I"; we always have instance data + _writer.BaseStream.Seek(-1, SeekOrigin.Current); + } else { + writeInstanceData = true; + } + WriteObject(obj); + } + + if (writeInstanceData) { + WriteInt32(instanceNames.Length); + foreach (string name in instanceNames) { + object value; + if (!_context.TryGetInstanceVariable(obj, name, out value)) { + value = null; + } + WriteSymbol(name); + WriteAnObject(value); + } + } + } + } + if (_recursionLimit >= 0) { + _recursionLimit++; + } + } + + internal void Dump(object obj) { + WritePreamble(); + WriteAnObject(obj); + _writer.BaseStream.Flush(); + } + } + + #endregion + + #region MarshalReader + + internal class MarshalReader { + private readonly BinaryReader/*!*/ _reader; + private readonly RubyContext/*!*/ _context; + private readonly Scope/*!*/ _globalScope; + private readonly Proc _proc; + private readonly Dictionary/*!*/ _symbols; + private readonly Dictionary/*!*/ _objects; + + internal MarshalReader(BinaryReader/*!*/ reader, RubyContext/*!*/ context, Scope/*!*/ globalScope, Proc proc) { + _reader = reader; + _context = context; + _globalScope = globalScope; + _proc = proc; + _symbols = new Dictionary(); + _objects = new Dictionary(); + } + + private void CheckPreamble() { + int major = _reader.ReadByte(); + int minor = _reader.ReadByte(); + if (major != MAJOR_VERSION || minor > MINOR_VERSION) { + string message = String.Format( + "incompatible marshal file format (can't be read)\n\tformat version {0}.{1} required; {2}.{3} given", + MAJOR_VERSION, MINOR_VERSION, major, minor); + throw RubyExceptions.CreateTypeError(message); + } + if (minor < MINOR_VERSION) { + string message = String.Format( + "incompatible marshal file format (can be read)\n\tformat version {0}.{1} required; {2}.{3} given", + MAJOR_VERSION, MINOR_VERSION, major, minor); + _context.ReportWarning(message); + } + } + + private BigInteger/*!*/ ReadBignum() { + int sign; + int csign = _reader.ReadByte(); + if (csign == '+') { + sign = 1; + } else if (csign == '-') { + sign = -1; + } else { + sign = 0; + } + int words = ReadInt32(); + int dwords_lo = words / 2; + int dwords_hi = (words + 1) / 2; + uint[] bits = new uint[dwords_hi]; + for (int i = 0; i < dwords_lo; i++) { + bits[i] = _reader.ReadUInt32(); + } + if (dwords_lo != dwords_hi) { + bits[dwords_lo] = _reader.ReadUInt16(); + } + + return new BigInteger(sign, bits); + } + + private int ReadInt32() { + sbyte first = _reader.ReadSByte(); + if (first == 0) { + return 0; + } else if (first > 4) { + return (first - 5); + } else if (first < -4) { + return (first + 5); + } else { + byte fill; + if (first < 0) { + fill = 0xff; + first = (sbyte)-first; + } else { + fill = 0x00; + } + uint value = 0; + for (int i = 0; i < 4; i++) { + byte nextByte; + if (i < first) { + nextByte = _reader.ReadByte(); + } else { + nextByte = fill; + } + value = value | (uint)(nextByte << (i * 8)); + } + return (int)value; + } + } + + private double ReadFloat() { + MutableString value = ReadString(); + if (value.Equals(_positiveInfinityString)) { + return Double.PositiveInfinity; + } + if (value.Equals(_negativeInfinityString)) { + return Double.NegativeInfinity; + } + if (value.Equals(_nanString)) { + return Double.NaN; + } + + // TODO: MRI appears to have an optimization that saves the (binary) mantissa at the end of the string + int pos = value.IndexOf((byte)0); + if (pos >= 0) { + value.Remove(pos, value.Length - pos); + } + return Protocols.ConvertToFloat(_context, value); + } + + private MutableString/*!*/ ReadString() { + int count = ReadInt32(); + byte[] data = _reader.ReadBytes(count); + return MutableString.CreateBinary(data); + } + + private RubyRegex/*!*/ ReadRegex() { + MutableString pattern = ReadString(); + int flags = _reader.ReadByte(); + return new RubyRegex(pattern, (RubyRegexOptions)flags); + } + + private RubyArray/*!*/ ReadArray() { + int count = ReadInt32(); + RubyArray result = new RubyArray(count); + for (int i = 0; i < count; i++) { + result.Add(ReadAnObject(false)); + } + return result; + } + + private Hash/*!*/ ReadHash(int typeFlag) { + int count = ReadInt32(); + Hash result = new Hash(_context); + for (int i = 0; i < count; i++) { + object key = ReadAnObject(false); + result[key] = ReadAnObject(false); + } + if (typeFlag == '}') { + result.DefaultValue = ReadAnObject(false); + } + return result; + } + + private string ReadSymbol() { + return ReadSymbol(_reader.ReadByte()); + } + + private string ReadSymbol(int typeFlag) { + string result = null; + if (typeFlag == ';') { + int position = ReadInt32(); + if (!_symbols.TryGetValue(position, out result)) { + throw RubyExceptions.CreateArgumentError("bad symbol"); + } + } else { + // Ruby appears to assume that it's a ':' in this context + MutableString symbolName = ReadString(); + result = symbolName.ToString(); + int position = _symbols.Count; + _symbols[position] = result; + } + return result; + } + + private RubyClass/*!*/ ReadType() { + string name = ReadSymbol(); + return (RubyClass)ReadClassOrModule('c', name); + } + + private object/*!*/ UnmarshalNewObject() { + return RubyUtils.CreateObject(ReadType()); + } + + private object/*!*/ ReadObject() { + RubyClass theClass = ReadType(); + int count = ReadInt32(); + Hash attributes = new Hash(_context); + for (int i = 0; i < count; i++) { + string name = ReadSymbol(); + attributes[name] = ReadAnObject(false); + } + return RubyUtils.CreateObject(theClass, attributes, false); + } + + private object/*!*/ ReadUsingLoad() { + RubyClass theClass = ReadType(); + return _LoadSite.Target(_LoadSite, _context, theClass, ReadString()); + } + + private object/*!*/ ReadUsingMarshalLoad() { + object obj = UnmarshalNewObject(); + _MarshalLoadSite.Target(_MarshalLoadSite, _context, obj, ReadAnObject(false)); + return obj; + } + + private object/*!*/ ReadClassOrModule(int typeFlag) { + string name = ReadString().ToString(); + return ReadClassOrModule(typeFlag, name); + } + + private object/*!*/ ReadClassOrModule(int typeFlag, string/*!*/ name) { + RubyModule result; + if (!_context.TryGetModule(_globalScope, name, out result)) { + throw RubyExceptions.CreateArgumentError(String.Format("undefined class/module {0}", name)); + } + + bool isClass = (result is RubyClass); + if (isClass && typeFlag == 'm') { + throw RubyExceptions.CreateArgumentError( + String.Format("{0} does not refer module", name)); + } + if (!isClass && typeFlag == 'c') { + throw RubyExceptions.CreateArgumentError( + String.Format("{0} does not refer class", name)); + } + return result; + } + + private RubyStruct/*!*/ ReadStruct() { + RubyStruct obj = (UnmarshalNewObject() as RubyStruct); + if (obj == null) { + throw RubyExceptions.CreateArgumentError("non-initialized struct"); + } + + var names = obj.GetNames(); + int count = ReadInt32(); + if (count != names.Count) { + throw RubyExceptions.CreateArgumentError("struct size differs"); + } + + for (int i = 0; i < count; i++) { + string name = ReadSymbol(); + if (name != names[i]) { + RubyClass theClass = _context.GetClassOf(obj); + string message = String.Format("struct {0} not compatible ({1} for {2})", theClass.Name, name, names[i]); + throw RubyExceptions.CreateTypeError(message); + } + obj[i] = ReadAnObject(false); + } + + return obj; + } + + private object/*!*/ ReadInstanced() { + object obj = ReadAnObject(true); + int count = ReadInt32(); + for (int i = 0; i < count; i++) { + string name = ReadSymbol(); + _context.SetInstanceVariable(obj, name, ReadAnObject(false)); + } + return obj; + } + + + private object/*!*/ ReadExtended() { + string extensionName = ReadSymbol(); + RubyModule module = (ReadClassOrModule('m', extensionName) as RubyModule); + object obj = ReadAnObject(true); + ModuleOps.ExtendObject(module, obj); + return obj; + } + + private object/*!*/ ReadUserClass() { + object obj = UnmarshalNewObject(); + bool loaded = false; + int typeFlag = _reader.ReadByte(); + switch (typeFlag) { + case '"': + MutableString msc = (obj as MutableString); + if (msc != null) { + msc.Replace(0, msc.Length, ReadString()); + loaded = true; + } + break; + + case '/': + RubyRegex rsc = (obj as RubyRegex); + if (rsc != null) { + RubyRegex regex = ReadRegex(); + rsc.Set(regex.GetPattern(), regex.Options); + loaded = true; + } + break; + + case '[': + RubyArray asc = (obj as RubyArray); + if (asc != null) { + asc.AddRange(ReadArray()); + loaded = true; + } + break; + + case '{': + case '}': + Hash hsc = (obj as Hash); + if (hsc != null) { + Hash hash = ReadHash(typeFlag); + hsc.DefaultProc = hash.DefaultProc; + hsc.DefaultValue = hash.DefaultValue; + foreach (var pair in hash) { + hsc.Add(pair.Key, pair.Value); + } + loaded = true; + } + break; + default: + break; + } + if (!loaded) { + throw RubyExceptions.CreateArgumentError("incompatible base type"); + } + return obj; + } + + private object ReadAnObject(bool noCache) { + object obj = null; + bool runProc = (!noCache && _proc != null); + int typeFlag = _reader.ReadByte(); + switch (typeFlag) { + case '0': + obj = null; + break; + + case 'T': + obj = true; + break; + + case 'F': + obj = false; + break; + + case 'i': + obj = ReadInt32(); + break; + + case ':': + obj = SymbolTable.StringToId(ReadSymbol(typeFlag)); + break; + + case ';': + obj = SymbolTable.StringToId(ReadSymbol(typeFlag)); + runProc = false; + break; + + case '@': + obj = _objects[ReadInt32()]; + runProc = false; + break; + + default: + // Reserve a reference + int objectRef = _objects.Count; + if (!noCache) { + _objects[objectRef] = null; + } + + switch (typeFlag) { + case 'f': + obj = ReadFloat(); + break; + case 'l': + obj = ReadBignum(); + break; + case '"': + obj = ReadString(); + break; + case '/': + obj = ReadRegex(); + break; + case '[': + obj = ReadArray(); + break; + case '{': + case '}': + obj = ReadHash(typeFlag); + break; + case 'o': + obj = ReadObject(); + break; + case 'u': + obj = ReadUsingLoad(); + break; + case 'U': + obj = ReadUsingMarshalLoad(); + break; + case 'c': + case 'm': + obj = ReadClassOrModule(typeFlag); + break; + case 'S': + obj = ReadStruct(); + break; + case 'I': + obj = ReadInstanced(); + break; + case 'e': + obj = ReadExtended(); + break; + case 'C': + obj = ReadUserClass(); + break; + default: + throw RubyExceptions.CreateArgumentError(String.Format("dump format error({0})", (int)typeFlag)); + } + if (!noCache) { + _objects[objectRef] = obj; + } + break; + } + if (runProc) { + _ReaderProcSite.Target(_ReaderProcSite, _context, _proc, obj); + } + return obj; + } + + internal object Load() { + try { + CheckPreamble(); + return ReadAnObject(false); + } catch (IOException e) { + throw RubyExceptions.CreateArgumentError("marshal data too short", e); + } + } + } + + #endregion + + #region Public Instance Methods + + // TODO: Use DefaultValue attribute when it works with the binder + [RubyMethod("dump", RubyMethodAttributes.PublicSingleton)] + public static MutableString Dump(RubyModule/*!*/ self, object obj) { + return Dump(self, obj, -1); + } + + // TODO: Use DefaultValue attribute when it works with the binder + [RubyMethod("dump", RubyMethodAttributes.PublicSingleton)] + public static MutableString Dump(RubyModule/*!*/ self, object obj, int limit) { + MemoryStream buffer = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(buffer); + MarshalWriter dumper = new MarshalWriter(writer, self.Context, limit); + dumper.Dump(obj); + return MutableString.CreateBinary(buffer.ToArray()); + } + + // TODO: Use DefaultValue attribute when it works with the binder + [RubyMethod("dump", RubyMethodAttributes.PublicSingleton)] + public static object Dump(RubyModule/*!*/ self, object obj, [NotNull]RubyIO/*!*/ io, [Optional]int? limit) { + BinaryWriter writer = io.GetBinaryWriter(); + MarshalWriter dumper = new MarshalWriter(writer, self.Context, limit); + dumper.Dump(obj); + return io; + } + + // TODO: Use DefaultValue attribute when it works with the binder + [RubyMethod("dump", RubyMethodAttributes.PublicSingleton)] + public static object Dump(RubyModule/*!*/ self, object obj, object io, [Optional]int? limit) { + Stream stream = null; + if (io != null) { + stream = new IOWrapper(self.Context, io, FileAccess.Write); + } + if (stream == null || !stream.CanWrite) { + throw RubyExceptions.CreateTypeError("instance of IO needed"); + } + + BinaryWriter writer = new BinaryWriter(stream); + MarshalWriter dumper = new MarshalWriter(writer, self.Context, limit); + dumper.Dump(obj); + return io; + } + + [RubyMethod("load", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("restore", RubyMethodAttributes.PublicSingleton)] + public static object Load(RubyScope/*!*/ scope, RubyModule/*!*/ self, [NotNull]MutableString/*!*/ source, [Optional]Proc proc) { + BinaryReader reader = new BinaryReader(new MemoryStream(source.ConvertToBytes())); + MarshalReader loader = new MarshalReader(reader, scope.RubyContext, scope.GlobalScope, proc); + return loader.Load(); + } + + [RubyMethod("load", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("restore", RubyMethodAttributes.PublicSingleton)] + public static object Load(RubyScope/*!*/ scope, RubyModule/*!*/ self, [NotNull]RubyIO/*!*/ source, [Optional]Proc proc) { + BinaryReader reader = source.GetBinaryReader(); + MarshalReader loader = new MarshalReader(reader, scope.RubyContext, scope.GlobalScope, proc); + return loader.Load(); + } + + [RubyMethod("load", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("restore", RubyMethodAttributes.PublicSingleton)] + public static object Load(RubyScope/*!*/ scope, RubyModule/*!*/ self, object source, [Optional]Proc proc) { + Stream stream = null; + if (source != null) { + stream = new IOWrapper(self.Context, source, FileAccess.Read); + } + if (stream == null || !stream.CanRead) { + throw RubyExceptions.CreateTypeError("instance of IO needed"); + } + BinaryReader reader = new BinaryReader(stream); + MarshalReader loader = new MarshalReader(reader, scope.RubyContext, scope.GlobalScope, proc); + return loader.Load(); + } + + #endregion + + #region Declared Constants + + [RubyConstant] + public const int MAJOR_VERSION = 4; + + [RubyConstant] + public const int MINOR_VERSION = 8; + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/MatchDataOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/MatchDataOps.cs new file mode 100644 index 0000000000..fe3740e9fa --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/MatchDataOps.cs @@ -0,0 +1,177 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using IronRuby.Runtime; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.Builtins { + [RubyClass("MatchData", Extends = typeof(MatchData), Inherits = typeof(Object))] + [UndefineMethod("new", IsStatic = true)] + public static class MatchDataOps { + + #region Private Instance Methods + + [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)] + public static MatchData/*!*/ InitializeCopy(MatchData/*!*/ self, [NotNull]MatchData/*!*/ other) { + self.InitializeFrom(other); + return self; + } + + #endregion + + #region Public Instance Methods + + [RubyMethod("[]")] + public static MutableString GetGroup(RubyContext/*!*/ context, MatchData/*!*/ self, [DefaultProtocol]int index) { + index = IListOps.NormalizeIndex(self.Groups.Count, index); + return self.GetGroupValue(context, index); + } + + [RubyMethod("[]")] + public static RubyArray GetGroup(RubyContext/*!*/ context, MatchData/*!*/ self, [DefaultProtocol]int start, [DefaultProtocol]int length) { + if (!IListOps.NormalizeRange(self.Groups.Count, ref start, ref length)) { + return null; + } + + RubyArray result = new RubyArray(); + for (int i = 0; i < length; i++) { + result.Add(self.GetGroupValue(context, start + i)); + } + + return result; + } + + [RubyMethod("[]")] + public static RubyArray GetGroup(RubyContext/*!*/ context, MatchData/*!*/ self, [NotNull]Range/*!*/ range) { + int begin, count; + if (!IListOps.NormalizeRange(context, self.Groups.Count, range, out begin, out count)) { + return null; + } + return GetGroup(context, self, begin, count); + } + + [RubyMethod("begin")] + public static object Begin(MatchData/*!*/ self, [DefaultProtocol]int groupIndex) { + var group = self.GetExistingGroup(groupIndex); + return group.Success ? ScriptingRuntimeHelpers.Int32ToObject(group.Index) : null; + } + + [RubyMethod("end")] + public static object End(MatchData/*!*/ self, [DefaultProtocol]int groupIndex) { + var group = self.GetExistingGroup(groupIndex); + return group.Success ? ScriptingRuntimeHelpers.Int32ToObject(group.Index + group.Length) : null; + } + + [RubyMethod("length")] + [RubyMethod("size")] + public static int Length(MatchData/*!*/ self) { + return self.Groups.Count; + } + + [RubyMethod("offset")] + public static RubyArray/*!*/ Offset(MatchData/*!*/ self, [DefaultProtocol]int groupIndex) { + var group = self.GetExistingGroup(groupIndex); + RubyArray result = new RubyArray(2); + if (group.Success) { + result.Add(group.Index); + result.Add(group.Index + group.Length); + } else { + result.Add(null); + result.Add(null); + } + return result; + } + + [RubyMethod("pre_match")] + public static MutableString/*!*/ PreMatch(RubyContext/*!*/ context, MatchData/*!*/ self) { + return self.OriginalString.GetSlice(0, self.Index).TaintBy(self, context); + } + + [RubyMethod("post_match")] + public static MutableString/*!*/ PostMatch(RubyContext/*!*/ context, MatchData/*!*/ self) { + return self.OriginalString.GetSlice(self.Index + self.Length).TaintBy(self, context); + } + + private static RubyArray/*!*/ ReturnMatchingGroups(RubyContext/*!*/ context, MatchData/*!*/ self, int groupIndex) { + Debug.Assert(groupIndex >= 0); + + if (self.Groups.Count < groupIndex) { + return new RubyArray(); + } + + RubyArray result = new RubyArray(self.Groups.Count - groupIndex); + for (int i = groupIndex; i < self.Groups.Count; i++) { + result.Add(self.GetGroupValue(context, i)); + } + return result; + } + + [RubyMethod("captures")] + public static RubyArray/*!*/ Captures(RubyContext/*!*/ context, MatchData/*!*/ self) { + return ReturnMatchingGroups(context, self, 1); + } + + [RubyMethod("to_a")] + public static RubyArray/*!*/ ToArray(RubyContext/*!*/ context, MatchData/*!*/ self) { + return ReturnMatchingGroups(context, self, 0); + } + + [RubyMethod("string")] + public static MutableString/*!*/ ReturnFrozenString(RubyContext/*!*/ context, MatchData/*!*/ self) { + return MutableString.Create(self.OriginalString).TaintBy(self, context).Freeze(); + } + + [RubyMethod("select")] + public static object Select(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, MatchData/*!*/ self) { + RubyArray result = new RubyArray(); + for (int i = 0; i < self.Groups.Count; i++) { + MutableString value = self.GetGroupValue(context, i); + + object blockResult; + if (block.Yield(value, out blockResult)) { + return blockResult; + } + + if (RubyOps.IsTrue(blockResult)) { + result.Add(value); + } + } + return result; + } + + [RubyMethod("values_at")] + public static RubyArray/*!*/ ValuesAt(RubyContext/*!*/ context, MatchData/*!*/ self, [NotNull]params object[]/*!*/ indices) { + RubyArray result = new RubyArray(); + for (int i = 0; i < indices.Length; i++) { + result.Add(GetGroup(context, self, Protocols.CastToFixnum(context, indices[i]))); + } + return result; + } + + [RubyMethod("inspect")] + public static MutableString/*!*/ Inspect(RubyContext/*!*/ context, MatchData/*!*/ self) { + return RubyUtils.ObjectToMutableString(context, self); + } + + [RubyMethod("to_s")] + public static MutableString/*!*/ ToS(RubyContext/*!*/ context, MatchData/*!*/ self) { + return MutableString.Create(self.Value).TaintBy(self, context); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/MethodOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/MethodOps.cs new file mode 100644 index 0000000000..6ffb8077e0 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/MethodOps.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Builtins { + + [RubyClass("Method", Extends = typeof(RubyMethod))] + public static class MethodOps { + #region Public Instance Methods + + [RubyMethod("==")] + public static bool Equal(RubyMethod/*!*/ self, [NotNull]RubyMethod/*!*/ other) { + // TODO: method with changed visibility, define_methods, module_functions, aliases: + return ReferenceEquals(self.Target, other.Target) && ReferenceEquals(self.Info, other.Info); + } + + [RubyMethod("==")] + public static bool Equal(RubyMethod/*!*/ self, object other) { + return false; + } + + [RubyMethod("arity")] + public static int GetArity(RubyMethod/*!*/ self) { + return self.Info.Arity; + } + + [RubyMethod("clone")] + public static RubyMethod/*!*/ Clone(RubyMethod/*!*/ self) { + return new RubyMethod(self.Target, self.Info, self.Name); + } + + [RubyMethod("[]")] + [RubyMethod("call")] + public static RuleGenerator/*!*/ Call() { + return new RuleGenerator(RuleGenerators.MethodCall); + } + + [RubyMethod("to_s")] + public static MutableString/*!*/ ToS(RubyMethod/*!*/ self) { + return UnboundMethod.ToS(self.Name, self.Info.DeclaringModule, self.GetTargetClass(), "Method"); + } + + [RubyMethod("to_proc")] + public static Proc/*!*/ ToProc(RubyContext/*!*/ context, RubyMethod/*!*/ self) { + RubyMethodInfo mi = self.Info as RubyMethodInfo; + if (mi != null) { + return Proc.Create(context, mi.Method, self.Target, mi.Arity); + } + // TODO: figure out what the semantics should be for a set of CLR methods returned ... + throw new NotImplementedException(); + } + + [RubyMethod("unbind")] + public static UnboundMethod/*!*/ Unbind(RubyMethod/*!*/ self) { + return new UnboundMethod(self.GetTargetClass(), self.Name, self.Info); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ModuleOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ModuleOps.cs new file mode 100644 index 0000000000..15fa1a86ad --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ModuleOps.cs @@ -0,0 +1,827 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using IronRuby.Compiler; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Builtins { + + [RubyClass("Module", Extends = typeof(RubyModule), Inherits = typeof(Object))] + public static class ModuleOps { + + #region CLR extensions + + [RubyMethod("to_clr_type")] + public static Type ToClrType(RubyModule/*!*/ self) { + return self.Tracker != null ? self.Tracker.Type : null; + } + + #endregion + + #region Private Instance Methods + + #region class_variable_get, class_variable_set, remove_class_variable, remove_const + + [RubyMethod("class_variable_get", RubyMethodAttributes.PrivateInstance)] + public static object GetClassVariable(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ variableName) { + object value; + if (self.TryResolveClassVariable(variableName, out value) == null) { + RubyUtils.CheckClassVariableName(variableName); + throw RubyExceptions.CreateNameError(String.Format("uninitialized class variable `{0}' in `{1}'", variableName, self.Name)); + } + return value; + } + + [RubyMethod("class_variable_set", RubyMethodAttributes.PrivateInstance)] + public static object ClassVariableSet(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ variableName, object value) { + RubyUtils.CheckClassVariableName(variableName); + self.SetClassVariable(variableName, value); + return value; + } + + [RubyMethod("remove_class_variable", RubyMethodAttributes.PrivateInstance)] + public static object RemoveClassVariable(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ variableName) { + object value; + if (!self.TryGetClassVariable(variableName, out value)) { + RubyUtils.CheckClassVariableName(variableName); + throw RubyExceptions.CreateNameError(String.Format("class variable `{0}' not defined for `{1}'", variableName, self.Name)); + } + self.RemoveClassVariable(variableName); + return value; + } + + [RubyMethod("remove_const", RubyMethodAttributes.PrivateInstance)] + public static object RemoveConstant(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ constantName) { + object value; + if (!self.TryGetConstantNoAutoload(constantName, out value)) { + RubyUtils.CheckConstantName(constantName); + throw RubyExceptions.CreateNameError(String.Format("constant {0}::{1} not defined", self.Name, constantName)); + } + self.RemoveConstant(constantName); + return value; + } + + #endregion + + #region extend_object, extended, include, included + + [RubyMethod("extend_object", RubyMethodAttributes.PrivateInstance)] + public static RubyModule/*!*/ ExtendObject(RubyModule/*!*/ self, [NotNull]RubyModule/*!*/ extendedModule) { + // include self into extendedModule's singleton class + extendedModule.SingletonClass.IncludeModules(self); + return self; + } + + [RubyMethod("extend_object", RubyMethodAttributes.PrivateInstance)] + public static object ExtendObject(RubyModule/*!*/ self, object extendedObject) { + // include self into extendedObject's singleton + self.Context.CreateSingletonClass(extendedObject).IncludeModules(self); + return extendedObject; + } + + [RubyMethod("extended", RubyMethodAttributes.PrivateInstance)] + public static void ObjectExtended(RubyModule/*!*/ self, object extendedObject) { + // extendedObject has been extended by self, i.e. self has been included into extendedObject's singleton class + } + + [RubyMethod("include", RubyMethodAttributes.PrivateInstance)] + public static RubyModule/*!*/ Include(RubyModule/*!*/ self, [NotNull]params RubyModule[]/*!*/ modules) { + Assert.NotNull(self, modules); + RubyUtils.RequireNonClasses(modules); + + // Kernel#append_features inserts the module at the beginning of ancestors list; + // ancestors after include: [modules[0], modules[1], ..., modules[N-1], self, ...] + for (int i = modules.Length - 1; i >= 0; i--) { + _ModuleAppendFeaturesSite.Target(_ModuleAppendFeaturesSite, self.Context, modules[i], self); + _ModuleIncludedSite.Target(_ModuleIncludedSite, self.Context, modules[i], self); + } + + return self; + } + + private static readonly CallSite> _ModuleAppendFeaturesSite = + CallSite>.Create( + RubyCallAction.Make("append_features", RubyCallSignature.WithImplicitSelf(1)) + ); + + private static readonly CallSite> _ModuleIncludedSite = + CallSite>.Create( + RubyCallAction.Make("included", RubyCallSignature.WithImplicitSelf(1)) + ); + + [RubyMethod("included", RubyMethodAttributes.PrivateInstance)] + public static void Included(RubyModule/*!*/ self, RubyModule/*!*/ owner) { + // self has been included into owner + } + + [RubyMethod("append_features", RubyMethodAttributes.PrivateInstance)] + public static RubyModule/*!*/ AppendFeatures(RubyModule/*!*/ self, [NotNull]RubyModule/*!*/ owner) { + owner.IncludeModules(self); + return self; + } + + #endregion + + #region initialize, initialize_copy + + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static object Reinitialize(BlockParam block, RubyModule/*!*/ self) { + // no class can be reinitialized: + if (self.IsClass) { + throw RubyExceptions.CreateTypeError("already initialized class"); + } + + return (block != null) ? RubyUtils.EvaluateInModule(self, block, null) : null; + } + + [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)] + public static RubyModule/*!*/ InitializeCopy(RubyModule/*!*/ self, object other) { + // no class can be reinitialized: + if (self.IsClass) { + throw RubyExceptions.CreateTypeError("already initialized class"); + } + + // self could be a meta-module: + RubyClass selfClass = self.Context.GetClassOf(self); + RubyClass otherClass = self.Context.GetClassOf(other); + if (otherClass != selfClass) { + throw RubyExceptions.CreateTypeError("initialize_copy should take same class object"); + } + + self.InitializeModuleCopy((RubyModule)other); + return self; + } + + #endregion + + #region private, protected, public, module_function + + [RubyMethod("private", RubyMethodAttributes.PrivateInstance)] + public static RubyModule/*!*/ SetPrivateVisibility(RubyScope/*!*/ scope, RubyModule/*!*/ self, [NotNull]params object[]/*!*/ methodNames) { + // overwrites methods to instance: + SetMethodAttributes(scope, self, methodNames, RubyMethodAttributes.PrivateInstance); + return self; + } + + [RubyMethod("protected", RubyMethodAttributes.PrivateInstance)] + public static RubyModule/*!*/ SetProtectedVisibility(RubyScope/*!*/ scope, RubyModule/*!*/ self, [NotNull]params object[]/*!*/ methodNames) { + // overwrites methods to instance: + SetMethodAttributes(scope, self, methodNames, RubyMethodAttributes.ProtectedInstance); + return self; + } + + [RubyMethod("public", RubyMethodAttributes.PrivateInstance)] + public static RubyModule/*!*/ SetPublicVisibility(RubyScope/*!*/ scope, RubyModule/*!*/ self, [NotNull]params object[]/*!*/ methodNames) { + // overwrites methods to instance: + SetMethodAttributes(scope, self, methodNames, RubyMethodAttributes.PublicInstance); + return self; + } + + [RubyMethod("module_function", RubyMethodAttributes.PrivateInstance)] + public static RubyModule/*!*/ CopyMethodsToModuleSingleton(RubyScope/*!*/ scope, RubyModule/*!*/ self, [NotNull]params object[]/*!*/ methodNames) { + // This is an important restriction for correct super calls in module functions (see RubyOps.DefineMethod). + // MRI has it wrong. It checks just here and not in method definition. + if (self.IsClass) { + throw RubyExceptions.CreateTypeError("module_function must be called for modules"); + } + + // overwrites visibility to public: + SetMethodAttributes(scope, self, methodNames, RubyMethodAttributes.ModuleFunction); + return self; + } + + internal static void SetMethodAttributes(RubyScope/*!*/ scope, RubyModule/*!*/ module, object[]/*!*/ methodNames, RubyMethodAttributes attributes) { + ContractUtils.RequiresNotNull(scope, "scope"); + ContractUtils.RequiresNotNull(methodNames, "methodNames"); + + if (methodNames.Length == 0) { + scope.GetMethodAttributesDefinitionScope().MethodAttributes = attributes; + } else { + foreach (string methodName in Protocols.CastToSymbols(scope.RubyContext, methodNames)) { + RubyMemberInfo method = module.ResolveMethodFallbackToObject(methodName, true); + if (method == null) { + throw RubyExceptions.CreateNameError(RubyExceptions.FormatMethodMissingMessage(scope.RubyContext, module, methodName)); + } + + if ((attributes & RubyMethodAttributes.ModuleFunction) == RubyMethodAttributes.ModuleFunction) { + module.AddModuleFunction(methodName, method); + } else { + module.SetMethodVisibility(methodName, method, (RubyMethodVisibility)(attributes & RubyMethodAttributes.VisibilityMask)); + } + } + } + } + + #endregion + + #region define_method + + [RubyMethod("define_method", RubyMethodAttributes.PrivateInstance)] + public static RubyMethod/*!*/ DefineMethod(RubyModule/*!*/ self, + [DefaultProtocol]string/*!*/ methodName, [NotNull]RubyMethod/*!*/ method) { + + // MRI 1.8 does the check when the method is called, 1.9 checks it upfront as we do: + var targetClass = method.GetTargetClass(); + if (!self.HasAncestor(targetClass)) { + throw RubyExceptions.CreateTypeError( + String.Format("bind argument must be a subclass of {0}", targetClass.Name) + ); + } + + self.AddDefinedMethod(methodName, method.Info); + return method; + } + + [RubyMethod("define_method", RubyMethodAttributes.PrivateInstance)] + public static UnboundMethod/*!*/ DefineMethod(RubyModule/*!*/ self, + [DefaultProtocol]string/*!*/ methodName, [NotNull]UnboundMethod/*!*/ method) { + + // MRI 1.8 does the check when the method is called, 1.9 checks it upfront as we do: + if (!self.HasAncestor(method.TargetConstraint)) { + throw RubyExceptions.CreateTypeError( + String.Format("bind argument must be a subclass of {0}", method.TargetConstraint.Name) + ); + } + + self.AddDefinedMethod(methodName, method.Info); + return method; + } + + [RubyMethod("define_method", RubyMethodAttributes.PrivateInstance)] + public static Proc/*!*/ DefineMethod(RubyScope/*!*/ scope, [NotNull]BlockParam/*!*/ block, + RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ methodName) { + + return DefineMethod(scope, self, methodName, block.Proc); + } + + [RubyMethod("define_method", RubyMethodAttributes.PrivateInstance)] + public static Proc/*!*/ DefineMethod(RubyScope/*!*/ scope, RubyModule/*!*/ self, + [DefaultProtocol]string/*!*/ methodName, [NotNull]Proc/*!*/ method) { + + // MRI: ignores ModuleFunction scope flag (doesn't create singleton method). + // MRI 1.8: uses private visibility if module_function is applied (bug). + // MFI 1.9: uses public visibility as we do, unless the name is special. + var visibility = RubyUtils.GetSpecialMethodVisibility(scope.Visibility, methodName); + + self.AddMethod(methodName, Proc.ToLambdaMethodInfo(method.ToLambda(), methodName, visibility, self)); + return method; + } + + #endregion + + #region method_(added|removed|undefined) + + [RubyMethod("method_added", RubyMethodAttributes.PrivateInstance)] + public static void MethodAdded(RubyModule/*!*/ self, object methodName) { + // nop + } + + [RubyMethod("method_removed", RubyMethodAttributes.PrivateInstance)] + public static void MethodRemoved(RubyModule/*!*/ self, object methodName) { + // nop + } + + [RubyMethod("method_undefined", RubyMethodAttributes.PrivateInstance)] + public static void MethodUndefined(RubyModule/*!*/ self, object methodName) { + // nop + } + + #endregion + + #region attr, attr_(reader,writer,accessor) + + private static void DefineAccessor(RubyScope/*!*/ scope, RubyModule/*!*/ self, string/*!*/ name, bool readable, bool writable) { + // MRI: ignores ModuleFunction scope flag (doesn't create singleton methods): + + var varName = "@" + name; + + if (readable) { + var flags = (RubyMemberFlags)RubyUtils.GetSpecialMethodVisibility(scope.Visibility, name); + self.SetLibraryMethod(name, new RubyAttributeReaderInfo(flags, self, varName), false); + } + + if (writable) { + self.SetLibraryMethod(name + "=", new RubyAttributeWriterInfo((RubyMemberFlags)scope.Visibility, self, varName), false); + } + } + + [RubyMethod("attr", RubyMethodAttributes.PrivateInstance)] + public static void Attr(RubyScope/*!*/ scope, RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ name, [Optional]bool writable) { + DefineAccessor(scope, self, name, true, writable); + } + + [RubyMethod("attr_accessor", RubyMethodAttributes.PrivateInstance)] + public static void AttrAccessor(RubyScope/*!*/ scope, RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ name) { + DefineAccessor(scope, self, name, true, true); + } + + [RubyMethod("attr_accessor", RubyMethodAttributes.PrivateInstance)] + public static void AttrAccessor(RubyScope/*!*/ scope, RubyModule/*!*/ self, [NotNull]params object[] names) { + foreach (string name in Protocols.CastToSymbols(scope.RubyContext, names)) { + DefineAccessor(scope, self, name, true, true); + } + } + + [RubyMethod("attr_reader", RubyMethodAttributes.PrivateInstance)] + public static void AttrReader(RubyScope/*!*/ scope, RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ name) { + DefineAccessor(scope, self, name, true, false); + } + + [RubyMethod("attr_reader", RubyMethodAttributes.PrivateInstance)] + public static void AttrReader(RubyScope/*!*/ scope, RubyModule/*!*/ self, [NotNull]params object[] names) { + foreach (string name in Protocols.CastToSymbols(scope.RubyContext, names)) { + DefineAccessor(scope, self, name, true, false); + } + } + + [RubyMethod("attr_writer", RubyMethodAttributes.PrivateInstance)] + public static void AttrWriter(RubyScope/*!*/ scope, RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ name) { + DefineAccessor(scope, self, name, false, true); + } + + [RubyMethod("attr_writer", RubyMethodAttributes.PrivateInstance)] + public static void AttrWriter(RubyScope/*!*/ scope, RubyModule/*!*/ self, [NotNull]params object[] names) { + foreach (string name in Protocols.CastToSymbols(scope.RubyContext, names)) { + DefineAccessor(scope, self, name, false, true); + } + } + + #endregion + + #region alias_method, remove_method, undef_method + + [RubyMethod("alias_method", RubyMethodAttributes.PrivateInstance)] + public static RubyModule/*!*/ AliasMethod(RubyModule/*!*/ self, + [DefaultProtocol]string/*!*/ newName, [DefaultProtocol]string/*!*/ oldName) { + + RubyMemberInfo method = self.ResolveMethodFallbackToObject(oldName, true); + if (method == null) { + throw RubyExceptions.CreateUndefinedMethodError(self, oldName); + } + + self.AddMethodAlias(newName, method); + return self; + } + + [RubyMethod("remove_method", RubyMethodAttributes.PrivateInstance)] + public static RubyModule/*!*/ RemoveMethod(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ methodName) { + if (!self.RemoveMethod(methodName)) { + throw RubyExceptions.CreateUndefinedMethodError(self, methodName); + } + return self; + } + + [RubyMethod("undef_method", RubyMethodAttributes.PrivateInstance)] + public static RubyModule/*!*/ UndefineMethod(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ methodName) { + RubyMemberInfo method = self.ResolveMethod(methodName, true); + if (method == null) { + throw RubyExceptions.CreateUndefinedMethodError(self, methodName); + } + self.UndefineMethod(methodName); + return self; + } + + #endregion + + #endregion + + #region Public Instance Methods + + #region <, >, <=, >=, <=>, ==, ===, ancestors, included_modules, include? + + [RubyMethod("==")] + public static bool Equals(RubyModule/*!*/ self, object other) { + return ReferenceEquals(self, other); + } + + [RubyMethod("===")] + public static bool CaseEquals(RubyModule/*!*/ self, object other) { + return self.Context.GetClassOf(other).HasAncestor(self); + } + + [RubyMethod("<")] + public static object IsSubclassOrIncluded(RubyModule/*!*/ self, [NotNull]RubyModule/*!*/ module) { + if (ReferenceEquals(self, module)) { + return false; + } + return self.HasAncestor(module) ? ScriptingRuntimeHelpers.True : null; + } + + [RubyMethod("<=")] + public static object IsSubclassSameOrIncluded(RubyModule/*!*/ self, [NotNull]RubyModule/*!*/ module) { + if (self.HasAncestor(module)) { + return true; + } + return module.HasAncestor(self) ? ScriptingRuntimeHelpers.False : null; + } + + [RubyMethod(">")] + public static object IsNotSubclassOrIncluded(RubyModule/*!*/ self, [NotNull]RubyModule/*!*/ module) { + if (ReferenceEquals(self, module)) { + return false; + } + return module.HasAncestor(self) ? ScriptingRuntimeHelpers.True : null; + } + + [RubyMethod(">=")] + public static object IsNotSubclassSameOrIncluded(RubyModule/*!*/ self, [NotNull]RubyModule/*!*/ module) { + if (module.HasAncestor(self)) { + return true; + } + return self.HasAncestor(module) ? ScriptingRuntimeHelpers.False : null; + } + + [RubyMethod("<=>")] + public static object Comparison(RubyModule/*!*/ self, [NotNull]RubyModule/*!*/ module) { + if (ReferenceEquals(self, module)) { + return ScriptingRuntimeHelpers.Int32ToObject(0); + } + + if (self.HasAncestor(module)) { + return ScriptingRuntimeHelpers.Int32ToObject(-1); + } + + if (module.HasAncestor(self)) { + return ScriptingRuntimeHelpers.Int32ToObject(1); + } + + return null; + } + + [RubyMethod("<=>")] + public static object Comparison(RubyModule/*!*/ self, object module) { + return null; + } + + [RubyMethod("<")] + [RubyMethod(">")] + [RubyMethod("<=")] + [RubyMethod(">=")] + public static object InvalidComparison(RubyModule/*!*/ self, object module) { + throw RubyExceptions.CreateTypeError("compared with non class/module"); + } + + [RubyMethod("ancestors")] + public static RubyArray/*!*/ Ancestors(RubyModule/*!*/ self) { + RubyArray ancestors = new RubyArray(); + self.ForEachAncestor(true, delegate(RubyModule/*!*/ module) { + if (!module.IsSingletonClass) { + ancestors.Add(module); + } + return false; + }); + return ancestors; + } + + [RubyMethod("included_modules")] + public static RubyArray/*!*/ GetIncludedModules(RubyModule/*!*/ self) { + RubyArray ancestorModules = new RubyArray(); + self.ForEachAncestor(true, delegate(RubyModule/*!*/ module) { + if (module != self && !module.IsClass && !ancestorModules.Contains(module)) { + ancestorModules.Add(module); + } + return false; + }); + return ancestorModules; + } + + [RubyMethod("include?")] + public static bool IncludesModule(RubyModule/*!*/ self, [NotNull]RubyModule/*!*/ other) { + RubyUtils.RequireNonClasses(other); + + return other != self && self.HasAncestor(other); + } + + #endregion + + #region module_eval, class_eval + + [RubyMethod("module_eval")] + [RubyMethod("class_eval")] + public static object Evaluate(RubyScope/*!*/ scope, BlockParam block, RubyModule/*!*/ self, [NotNull]MutableString/*!*/ code, + [Optional, NotNull]MutableString file, [DefaultParameterValue(1)]int line) { + + if (block != null) { + throw RubyExceptions.CreateArgumentError("wrong number of arguments"); + } + + return RubyUtils.Evaluate(code, scope, self, self, file, line); + } + + [RubyMethod("module_eval")] + [RubyMethod("class_eval")] + public static object Evaluate([NotNull]BlockParam/*!*/ block, RubyModule/*!*/ self) { + return RubyUtils.EvaluateInModule(self, block); + } + + #endregion + + #region class_variables, class_variable_defined? + + [RubyMethod("class_variables", RubyMethodAttributes.PublicInstance)] + public static RubyArray/*!*/ ClassVariables(RubyModule/*!*/ self) { + var visited = new Dictionary(); + var result = new RubyArray(); + + self.ForEachClassVariable(true, delegate(RubyModule/*!*/ module, string name, object value) { + if (name != null && !visited.ContainsKey(name)) { + result.Add(MutableString.Create(name)); + visited.Add(name, true); + } + return false; + }); + return result; + } + + [RubyMethod("class_variable_defined?", RubyMethodAttributes.PublicInstance)] + public static bool ClassVariableDefined(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ variableName) { + RubyUtils.CheckClassVariableName(variableName); + object value; + return self.TryResolveClassVariable(variableName, out value) != null; + } + + #endregion + + #region const_defined?, const_set, const_get, constants, const_missing + + [RubyMethod("const_defined?")] + public static bool IsConstantDefined(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ constantName) { + RubyUtils.CheckConstantName(constantName); + + object constant; + return self.TryResolveConstantNoAutoload(constantName, out constant); + } + + [RubyMethod("const_get")] + public static object GetConstantValue(RubyScope/*!*/ scope, RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ constantName) { + return RubyUtils.GetConstant(scope, self, constantName, true); + } + + [RubyMethod("const_set")] + public static object SetConstantValue(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ constantName, object value) { + RubyUtils.CheckConstantName(constantName); + RubyUtils.SetConstant(self, constantName, value); + return value; + } + + [RubyMethod("constants")] + public static RubyArray/*!*/ GetDefinedConstants(RubyModule/*!*/ self) { + var visited = new Dictionary(); + var result = new RubyArray(); + + bool hideGlobalConstants = !ReferenceEquals(self, self.Context.ObjectClass); + self.ForEachConstant(true, delegate(RubyModule/*!*/ module, string name, object value) { + if (name == null) { + // terminate enumeration when Object is reached + return hideGlobalConstants && ReferenceEquals(module, module.Context.ObjectClass); + } + + if (!visited.ContainsKey(name)) { + visited.Add(name, true); + result.Add(MutableString.Create(name)); + } + return false; + }); + + return result; + } + + [RubyMethod("const_missing")] + public static void ConstantMissing(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ name) { + throw RubyExceptions.CreateNameError(String.Format("uninitialized constant {0}::{1}", self.Name, name)); + } + + #endregion + + #region {private_|protected_|public_|}instance_methods + + [RubyMethod("instance_methods")] + public static RubyArray/*!*/ GetInstanceMethods(RubyModule/*!*/ self) { + return GetInstanceMethods(self, true); + } + + [RubyMethod("instance_methods")] + public static RubyArray/*!*/ GetInstanceMethods(RubyModule/*!*/ self, bool inherited) { + return GetMethods(self, inherited, RubyMethodAttributes.PublicInstance | RubyMethodAttributes.ProtectedInstance); + } + + [RubyMethod("private_instance_methods")] + public static RubyArray/*!*/ GetPrivateInstanceMethods(RubyModule/*!*/ self) { + return GetPrivateInstanceMethods(self, true); + } + + [RubyMethod("private_instance_methods")] + public static RubyArray/*!*/ GetPrivateInstanceMethods(RubyModule/*!*/ self, bool inherited) { + return GetMethods(self, inherited, RubyMethodAttributes.PrivateInstance); + } + + [RubyMethod("protected_instance_methods")] + public static RubyArray/*!*/ GetProtectedInstanceMethods(RubyModule/*!*/ self) { + return GetProtectedInstanceMethods(self, true); + } + + [RubyMethod("protected_instance_methods")] + public static RubyArray/*!*/ GetProtectedInstanceMethods(RubyModule/*!*/ self, bool inherited) { + return GetMethods(self, inherited, RubyMethodAttributes.ProtectedInstance); + } + + [RubyMethod("public_instance_methods")] + public static RubyArray/*!*/ GetPublicInstanceMethods(RubyModule/*!*/ self) { + return GetPublicInstanceMethods(self, true); + } + + [RubyMethod("public_instance_methods")] + public static RubyArray/*!*/ GetPublicInstanceMethods(RubyModule/*!*/ self, bool inherited) { + return GetMethods(self, inherited, RubyMethodAttributes.PublicInstance); + } + + internal static RubyArray/*!*/ GetMethods(RubyModule/*!*/ self, bool inherited, RubyMethodAttributes attributes) { + var result = new RubyArray(); + self.ForEachMember(inherited, attributes, delegate(string/*!*/ name, RubyMemberInfo/*!*/ member) { + result.Add(MutableString.Create(name)); + }); + return result; + } + + #endregion + + #region {private_|protected_|public_|}method_defined? + + [RubyMethod("method_defined?")] + public static bool MethodDefined(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ methodName) { + RubyMemberInfo method = self.ResolveMethod(methodName, true); + return method != null && method.Visibility != RubyMethodVisibility.Private; + } + + [RubyMethod("private_method_defined?")] + public static bool PrivateMethodDefined(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ methodName) { + RubyMemberInfo method = self.ResolveMethod(methodName, true); + return method != null && method.Visibility == RubyMethodVisibility.Private; + } + + [RubyMethod("protected_method_defined?")] + public static bool ProtectedMethodDefined(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ methodName) { + RubyMemberInfo method = self.ResolveMethod(methodName, true); + return method != null && method.Visibility == RubyMethodVisibility.Protected; + } + + [RubyMethod("public_method_defined?")] + public static bool PublicMethodDefined(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ methodName) { + RubyMemberInfo method = self.ResolveMethod(methodName, true); + return method != null && method.Visibility == RubyMethodVisibility.Public; + } + + #endregion + + #region instance_method, private_class_method, public_class_method + + [RubyMethod("instance_method")] + public static UnboundMethod/*!*/ GetInstanceMethod(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ methodName) { + RubyMemberInfo method = self.ResolveMethod(methodName, true); + if (method == null) { + throw RubyExceptions.CreateUndefinedMethodError(self, methodName); + } + + // unbound method binable to any class with "self" mixin: + return new UnboundMethod(self, methodName, method); + } + + private static void SetClassMethodsVisibility(RubyModule/*!*/ module, string[]/*!*/ methodNames, RubyMethodVisibility visibility) { + var methods = new RubyMemberInfo[methodNames.Length]; + for (int i = 0; i < methods.Length; i++) { + RubyMemberInfo method = module.SingletonClass.ResolveMethod(methodNames[i], true); + if (method == null) { + throw RubyExceptions.CreateUndefinedMethodError(module, methodNames[i]); + } + methods[i] = method; + } + + for (int i = 0; i < methods.Length; i++) { + module.SingletonClass.SetMethodVisibility(methodNames[i], methods[i], visibility); + } + } + + [RubyMethodAttribute("private_class_method")] + public static RubyModule/*!*/ MakeClassMethodsPrivate(RubyModule/*!*/ self, [NotNull]params object[]/*!*/ methodNames) { + SetClassMethodsVisibility(self, Protocols.CastToSymbols(self.Context, methodNames), RubyMethodVisibility.Private); + return self; + } + + [RubyMethodAttribute("public_class_method")] + public static RubyModule/*!*/ MakeClassMethodsPublic(RubyModule/*!*/ self, [NotNull]params object[]/*!*/ methodNames) { + SetClassMethodsVisibility(self, Protocols.CastToSymbols(self.Context, methodNames), RubyMethodVisibility.Public); + return self; + } + + #endregion + + [RubyMethod("freeze")] + public static RubyModule/*!*/ Freeze(RubyContext/*!*/ context, RubyModule/*!*/ self) { + // TODO: + context.FreezeObject(self); + return self; + } + + [RubyMethod("autoload")] + public static void SetAutoloadedConstant(RubyModule/*!*/ self, + [DefaultProtocol]string/*!*/ constantName, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + + RubyUtils.CheckConstantName(constantName); + if (path.IsEmpty) { + throw RubyExceptions.CreateArgumentError("empty file name"); + } + + self.SetAutoloadedConstant(constantName, path); + } + + [RubyMethod("autoload?")] + public static MutableString GetAutoloadedConstantPath(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ constantName) { + return self.GetAutoloadedConstantPath(constantName); + } + + [RubyMethod("to_s")] + public static MutableString/*!*/ ToS(RubyModule/*!*/ self) { + return self.GetDisplayName(false); + } + + [RubyMethod("name")] + public static MutableString/*!*/ GetName(RubyModule/*!*/ self) { + return self.GetDisplayName(true); + } + + [RubyMethod("of")] + [RubyMethod("[]")] + public static RubyModule/*!*/ Of(RubyModule/*!*/ self, [NotNull]params object[]/*!*/ typeArgs) { + if (self.Tracker == null) { + throw new NotImplementedException("TODO"); + } + + Type type = self.Tracker.Type; + int provided = typeArgs.Length; + + if (provided == 1 && type == typeof(Array)) { + return self.Context.GetModule(Protocols.ToType(self.Context, typeArgs[0]).MakeArrayType()); + } + + int required = type.GetGenericArguments().Length; + if (required == 0 && provided > 0) { + throw RubyExceptions.CreateArgumentError(String.Format("'{0}' is not a generic type", type.FullName)); + } + + if (required != provided) { + throw RubyExceptions.CreateArgumentError(String.Format("Type '{0}' requires {1} generic type arguments, {2} provided", type.FullName, required, provided)); + } + + if (typeArgs.Length > 0) { + Type concreteType = type.MakeGenericType(Protocols.ToTypes(self.Context, typeArgs)); + return self.Context.GetModule(concreteType); + } else { + return self; + } + } + + #endregion + + #region Singleton Methods + + [RubyMethod("constants", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ GetGlobalConstants(RubyModule/*!*/ self) { + return ModuleOps.GetDefinedConstants(self.Context.ObjectClass); + } + + [RubyMethod("nesting", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ GetLexicalModuleNesting(RubyScope/*!*/ scope, RubyModule/*!*/ self) { + RubyArray result = new RubyArray(); + while (scope != null) { + // Ruby 1.9: the anonymous module doesn't show up + if (scope.Module != null) { + result.Add(scope.Module); + } + scope = (RubyScope)scope.Parent; + } + return result; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/MutableStringOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/MutableStringOps.cs new file mode 100644 index 0000000000..0c79daef99 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/MutableStringOps.cs @@ -0,0 +1,2889 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using IronRuby.Compiler; +using IronRuby.Runtime; +using Microsoft.Scripting; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Builtins { + + [RubyClass("String", Extends = typeof(MutableString), Inherits = typeof(Object))] + [Includes(typeof(Enumerable), typeof(Comparable))] + [HideMethod("clone")] + [HideMethod("version")] + public class MutableStringOps { + + [RubyConstructor] + public static MutableString/*!*/ Create(RubyClass/*!*/ self) { + return MutableString.CreateMutable(); + } + + [RubyConstructor] + public static MutableString/*!*/ Create(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ value) { + return MutableString.Create(value); + } + + #region Helpers + + internal static int NormalizeIndex(MutableString/*!*/ str, int index) { + return IListOps.NormalizeIndex(str.Length, index); + } + + private static bool InExclusiveRangeNormalized(MutableString/*!*/ str, ref int index) { + if (index < 0) { + index = index + str.Length; + } + return index >= 0 && index < str.Length; + } + + private static bool InInclusiveRangeNormalized(MutableString/*!*/ str, ref int index) { + if (index < 0) { + index = index + str.Length; + } + return index >= 0 && index <= str.Length; + } + + // Parses interval strings that are of this form: + // + // abc # abc + // abc-efg-h # abcdefgh + // ^abc # all characters in range 0-255 except abc + // \x00-0xDD # all characters in hex range + public class IntervalParser { + private readonly MutableString/*!*/ _range; + private int _pos; + private bool _rangeStarted; + private int _startRange; + + public IntervalParser(MutableString/*!*/ range) { + _range = range; + _pos = 0; + _rangeStarted = false; + } + + public int PeekChar() { + return _pos >= _range.Length ? -1 : _range.GetChar(_pos); + } + + public int GetChar() { + return _pos >= _range.Length ? -1 : _range.GetChar(_pos++); + } + + public int NextToken() { + int current = GetChar(); + + if (current == '\\') { + int next = PeekChar(); + switch (next) { + case 'x': + _pos++; + int digit1 = Tokenizer.ToDigit(GetChar()); + int digit2 = Tokenizer.ToDigit(GetChar()); + + if (digit1 >= 16) { + throw RubyExceptions.CreateArgumentError("Invalid escape character syntax"); + } + + if (digit2 >= 16) { + current = digit1; + } else { + current = ((digit1 << 4) + digit2); + } + break; + + case 't': + current = '\t'; + break; + + case 'n': + current = '\n'; + break; + + case 'r': + current = '\r'; + break; + + case 'v': + current = '\v'; + break; + + case '\\': + current = '\\'; + break; + + default: + break; + } + } + + return current; + } + + // TODO: refactor this and Parse() + public MutableString/*!*/ ParseSequence() { + _pos = 0; + + MutableString result = MutableString.CreateBinary(); + if (_range.Length == 0) { + return result; + } + + bool negate = false; + if (_range.GetChar(0) == '^') { + // Special case of ^ + if (_range.Length == 1) { + result.Append('^'); + return result; + } + + negate = true; + _pos = 1; + } + + BitArray array = new BitArray(256); + array.Not(); + + int c; + while ((c = NextToken()) != -1) { + if (_rangeStarted) { + // _startRange - c. ignore ranges which are the reverse sequence + if (_startRange <= c) { + for (int i = _startRange; i <= c; ++i) { + if (negate) { + array.Set(i, false); + } else { + result.Append((byte)i); + } + } + } + _rangeStarted = false; + } else { + int p = PeekChar(); + if (p == '-') { + // z- is treated as a literal 'z', '-' + if (_pos == _range.Length - 1) { + if (negate) { + array.Set(c, false); + array.Set('-', false); + } else { + result.Append((byte)c); + result.Append('-'); + } + break; + } + + _startRange = c; + if (_rangeStarted) { + if (negate) { + array.Set('-', false); + } else { + result.Append('-'); + } + _rangeStarted = false; + } else { + _rangeStarted = true; + } + _pos++; // consume - + } else { + if (negate) { + array.Set(c, false); + } else { + result.Append((byte)c); + } + } + } + } + + if (negate) { + for (int i = 0; i < 256; i++) { + if (array.Get(i)) { + result.Append((byte)i); + } + } + } + return result; + } + + public BitArray/*!*/ Parse() { + _pos = 0; + + BitArray result = new BitArray(256); + if (_range.Length == 0) { + return result; + } + + bool negate = false; + if (_range.GetChar(0) == '^') { + // Special case of ^ + if (_range.Length == 1) { + result.Set('^', true); + return result; + } + + negate = true; + _pos = 1; + result.Not(); + } + + int c; + while ((c = NextToken()) != -1) { + if (_rangeStarted) { + // _startRange - c. ignore ranges which are the reverse sequence + if (_startRange <= c) { + for (int i = _startRange; i <= c; ++i) + result.Set(i, !negate); + } + _rangeStarted = false; + } else { + int p = PeekChar(); + if (p == '-') { + // z- is treated as a literal 'z', '-' + if (_pos == _range.Length - 1) { + result.Set(c, !negate); + result.Set('-', !negate); + break; + } + + _startRange = c; + if (_rangeStarted) { + result.Set('-', !negate); + _rangeStarted = false; + } else { + _rangeStarted = true; + } + _pos++; // consume - + } else { + result.Set(c, !negate); + } + } + } + + return result; + } + } + + public class RangeParser { + + private readonly MutableString[]/*!*/ _ranges; + + public RangeParser(params MutableString[]/*!*/ ranges) { + ContractUtils.RequiresNotNull(ranges, "ranges"); + _ranges = ranges; + } + + public BitArray Parse() { + BitArray result = new IntervalParser(_ranges[0]).Parse(); + for (int i = 1; i < _ranges.Length; i++) { + result.And(new IntervalParser(_ranges[i]).Parse()); + } + return result; + } + } + + #endregion + + + #region initialize, initialize_copy + + // "initialize" not called when a factory/non-default ctor is called. + // "initialize_copy" called from "dup" and "clone" + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)] + public static MutableString/*!*/ Reinitialize(MutableString/*!*/ self, [DefaultProtocol, NotNull]MutableString other) { + return Replace(self, other); + } + + // Reinitialization. Not called when a factory/non-default ctor is called. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static MutableString/*!*/ Reinitialize(MutableString/*!*/ self) { + return self; + } + + #endregion + + + #region % + + [RubyMethod("%")] + public static MutableString/*!*/ Format(RubyContext/*!*/ context, MutableString/*!*/ self, object arg) { + IList args = arg as IList; + if (args == null) { + args = new object[] { arg }; + } + StringFormatter formatter = new StringFormatter(context, self.ConvertToString(), args); + return formatter.Format().TaintBy(self); + } + + #endregion + + #region * + + [RubyMethod("*")] + public static MutableString/*!*/ Repeat(MutableString/*!*/ self, [DefaultProtocol]int times) { + if (times < 0) { + throw RubyExceptions.CreateArgumentError("negative argument"); + } + + MutableString result = self.CreateInstance().TaintBy(self); + for (int i = 0; i < times; i++) { + result.Append(self); + } + + return result; + } + + #endregion + + #region +, <<, concat + + [RubyMethod("+")] + public static MutableString/*!*/ Concatenate(MutableString/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ other) { + // doesn't create a subclass: + return MutableString.Create(self).Append(other).TaintBy(self).TaintBy(other); + } + + [RubyMethod("<<")] + [RubyMethod("concat")] + public static MutableString/*!*/ Append(MutableString/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ other) { + return self.Append(other).TaintBy(other); + } + + [RubyMethod("<<")] + [RubyMethod("concat")] + public static MutableString/*!*/ Append(MutableString/*!*/ self, int c) { + if (c < 0 || c > 255) { + throw RubyExceptions.CreateTypeConversionError("Fixnum", "String"); + } + + return self.Append((byte)c); + } + + #endregion + + #region <=>, ==, === + + [RubyMethod("<=>")] + public static int Compare(MutableString/*!*/ self, [NotNull]MutableString/*!*/ other) { + return Math.Sign(self.CompareTo(other)); + } + + [RubyMethod("<=>")] + public static object Compare(RubyContext/*!*/ context, MutableString/*!*/ self, object other) { + + // We test to see if other responds to to_str AND <=> + // Ruby never attempts to convert other to a string via to_str and call Compare ... which is strange -- feels like a BUG in Ruby + + if (RubySites.RespondTo(context, other, "to_str") && RubySites.RespondTo(context, other, "<=>")) { + object result = Integer.TryUnaryMinus(RubySites.Compare(context, other, self)); + if (result == null) { + throw RubyExceptions.CreateTypeError(String.Format("{0} can't be coerced into Fixnum", + RubyUtils.GetClassName(context, result))); + } + + return result; + } + + return null; + } + + [RubyMethod("==")] + [RubyMethod("===")] + public static bool StringEquals(MutableString/*!*/ lhs, [NotNull]MutableString/*!*/ rhs) { + return lhs.Equals(rhs); + } + + [RubyMethod("==")] + [RubyMethod("===")] + public static bool Equals(RubyContext/*!*/ context, MutableString/*!*/ self, object other) { + Assert.NotNull(context, self); + return RubySites.RespondTo(context, other, "to_str") ? RubySites.Equal(context, other, self) : false; + } + + #endregion + + + #region slice! + + private static Group MatchRegexp(RubyScope/*!*/ scope, MutableString/*!*/ self, RubyRegex/*!*/ regex, int occurrance) { + MatchData match = RegexpOps.Match(scope, regex, self); + if (match == null || !match.Success) + return null; + + // Normalize index against # Groups in Match + if (occurrance < 0) { + occurrance += match.Groups.Count; + // Cannot refer to zero using negative indices + if (occurrance == 0) { + return null; + } + } + + if (occurrance < 0 || occurrance > match.Groups.Count) { + return null; + } + + return match.Groups[occurrance].Success ? match.Groups[occurrance] : null; + } + + [RubyMethod("slice!")] + public static object RemoveCharInPlace(RubyContext/*!*/ context, MutableString/*!*/ self, + [DefaultProtocol]int index) { + + if (!InExclusiveRangeNormalized(self, ref index)) { + return null; + } + + // TODO: optimize if the value is not read: + int result = self.PeekByte(index); + self.Remove(index, 1); + return result; + } + + [RubyMethod("slice!")] + public static MutableString RemoveSubstringInPlace(MutableString/*!*/ self, + [DefaultProtocol]int start, [DefaultProtocol]int length) { + + if (length < 0) { + return null; + } + + if (!InInclusiveRangeNormalized(self, ref start)) { + return null; + } + + if (start + length > self.Length) { + length = self.Length - start; + } + + MutableString result = self.CreateInstance().Append(self, start, length).TaintBy(self); + self.Remove(start, length); + return result; + } + + [RubyMethod("slice!")] + public static MutableString RemoveSubstringInPlace(RubyContext/*!*/ context, MutableString/*!*/ self, [NotNull]Range/*!*/ range) { + bool excludeEnd; + int begin, end; + Protocols.ConvertToIntegerRange(context, range, out begin, out end, out excludeEnd); + + if (!InInclusiveRangeNormalized(self, ref begin)) { + return null; + } + + end = NormalizeIndex(self, end); + + int count = excludeEnd ? end - begin : end - begin + 1; + return count < 0 ? self.CreateInstance() : RemoveSubstringInPlace(self, begin, count); + } + + [RubyMethod("slice!")] + public static MutableString RemoveSubstringInPlace(RubyScope/*!*/ scope, MutableString/*!*/ self, [NotNull]RubyRegex/*!*/ regex) { + if (regex.IsEmpty) { + return self.Clone().TaintBy(regex, scope); + } + + MatchData match = RegexpOps.Match(scope, regex, self); + if (match == null || !match.Success) { + return null; + } + + return RemoveSubstringInPlace(self, match.Index, match.Length).TaintBy(regex, scope); + } + + [RubyMethod("slice!")] + public static MutableString RemoveSubstringInPlace(RubyScope/*!*/ scope, MutableString/*!*/ self, + [NotNull]RubyRegex/*!*/ regex, [DefaultProtocol]int occurrance) { + + if (regex.IsEmpty) { + return self.Clone().TaintBy(regex, scope); + } + + Group group = MatchRegexp(scope, self, regex, occurrance); + return group == null ? null : RemoveSubstringInPlace(self, group.Index, group.Length).TaintBy(regex, scope); + } + + [RubyMethod("slice!")] + public static MutableString RemoveSubstringInPlace(MutableString/*!*/ self, [NotNull]MutableString/*!*/ searchStr) { + if (searchStr.IsEmpty) { + return searchStr.Clone(); + } + + int index = self.IndexOf(searchStr); + if (index < 0) { + return null; + } + + RemoveSubstringInPlace(self, index, searchStr.Length); + return searchStr.Clone(); + } + + #endregion + + #region [], slice + + [RubyMethod("[]")] + [RubyMethod("slice")] + public static object GetChar(MutableString/*!*/ self, [DefaultProtocol]int index) { + return InExclusiveRangeNormalized(self, ref index) ? (object)(int)self.GetByte(index) : null; + } + + [RubyMethod("[]")] + [RubyMethod("slice")] + public static MutableString GetSubstring(MutableString/*!*/ self, + [DefaultProtocol]int start, [DefaultProtocol]int length) { + + start = NormalizeIndex(self, start); + + if (start == self.Length) { + return self.CreateInstance().TaintBy(self); + } + + if (start < 0 || start > self.Length || length < 0) { + return null; + } + + if (start + length > self.Length) { + length = self.Length - start; + } + + return self.CreateInstance().Append(self, start, length).TaintBy(self); + } + + [RubyMethod("[]")] + [RubyMethod("slice")] + public static MutableString GetSubstring(RubyContext/*!*/ context, MutableString/*!*/ self, [NotNull]Range/*!*/ range) { + bool excludeEnd; + int begin, end; + Protocols.ConvertToIntegerRange(context, range, out begin, out end, out excludeEnd); + + begin = NormalizeIndex(self, begin); + if (begin < 0 || begin > self.Length) { + return null; + } + + end = NormalizeIndex(self, end); + + int count = excludeEnd ? end - begin : end - begin + 1; + return (count < 0) ? self.CreateInstance().TaintBy(self) : GetSubstring(self, begin, count); + } + + [RubyMethod("[]")] + [RubyMethod("slice")] + public static MutableString GetSubstring(MutableString/*!*/ self, [NotNull]MutableString/*!*/ searchStr) { + if (self.IndexOf(searchStr) != -1) { + return searchStr.Clone(); + } else { + return null; + } + } + + [RubyMethod("[]")] + [RubyMethod("slice")] + public static MutableString GetSubstring(RubyScope/*!*/ scope, MutableString/*!*/ self, [NotNull]RubyRegex/*!*/ regex) { + if (regex.IsEmpty) { + return self.CreateInstance().TaintBy(self).TaintBy(regex, scope); + } + + MatchData match = RegexpOps.Match(scope, regex, self); + if (match == null) { + return null; + } + + string result = match.Value; + return (result.Length == 0) ? null : self.CreateInstance().TaintBy(self).Append(result).TaintBy(regex, scope); + } + + [RubyMethod("[]")] + [RubyMethod("slice")] + public static MutableString GetSubstring(RubyScope/*!*/ scope, MutableString/*!*/ self, + [NotNull]RubyRegex/*!*/ regex, [DefaultProtocol]int occurrance) { + if (regex.IsEmpty) { + return self.CreateInstance().TaintBy(self).TaintBy(regex, scope); + } + + Group group = MatchRegexp(scope, self, regex, occurrance); + if (group == null || !group.Success) { + return null; + } + + return self.CreateInstance().Append(group.Value).TaintBy(self).TaintBy(regex, scope); + } + + #endregion + + #region []= + + [RubyMethod("[]=")] + public static MutableString/*!*/ ReplaceCharacter(MutableString/*!*/ self, + [DefaultProtocol]int index, [DefaultProtocol, NotNull]MutableString/*!*/ value) { + + index = index < 0 ? index + self.Length : index; + if (index < 0 || index >= self.Length) { + throw RubyExceptions.CreateIndexError(String.Format("index {0} out of string", index)); + } + + if (value.IsEmpty) { + self.Remove(index, 1).TaintBy(value); + return MutableString.CreateMutable(); + } + + self.Replace(index, 1, value).TaintBy(value); + return value; + } + + [RubyMethod("[]=")] + public static int SetCharacter(MutableString/*!*/ self, + [DefaultProtocol]int index, int value) { + + index = index < 0 ? index + self.Length : index; + if (index < 0 || index >= self.Length) { + throw RubyExceptions.CreateIndexError(String.Format("index {0} out of string", index)); + } + + self.SetByte(index, unchecked((byte)value)); + return value; + } + + [RubyMethod("[]=")] + public static MutableString/*!*/ ReplaceSubstring(MutableString/*!*/ self, + [DefaultProtocol]int start, [DefaultProtocol]int charsToOverwrite, [DefaultProtocol, NotNull]MutableString/*!*/ value) { + + if (charsToOverwrite < 0) { + throw RubyExceptions.CreateIndexError(String.Format("negative length {0}", charsToOverwrite)); + } + + if (System.Math.Abs(start) > self.Length) { + throw RubyExceptions.CreateIndexError(String.Format("index {0} out of string", start)); + } + + start = start < 0 ? start + self.Length : start; + + if (charsToOverwrite <= value.Length) { + int insertIndex = start + charsToOverwrite; + int limit = charsToOverwrite; + if (insertIndex > self.Length) { + limit -= insertIndex - self.Length; + insertIndex = self.Length; + } + + self.Replace(start, limit, value); + } else { + self.Replace(start, value.Length, value); + + int pos = start + value.Length; + int charsToRemove = charsToOverwrite - value.Length; + int charsLeftInString = self.Length - pos; + + self.Remove(pos, System.Math.Min(charsToRemove, charsLeftInString)); + } + + self.TaintBy(value); + return value; + } + + [RubyMethod("[]=")] + public static MutableString/*!*/ ReplaceSubstring(RubyContext/*!*/ context, MutableString/*!*/ self, + [NotNull]Range/*!*/ range, [DefaultProtocol, NotNull]MutableString/*!*/ value) { + + bool excludeEnd; + int begin, end; + Protocols.ConvertToIntegerRange(context, range, out begin, out end, out excludeEnd); + + begin = begin < 0 ? begin + self.Length : begin; + + if (begin < 0 || begin > self.Length) { + throw RubyExceptions.CreateRangeError(String.Format("{0}..{1} out of range", begin, end)); + } + + end = end < 0 ? end + self.Length : end; + + int count = excludeEnd ? end - begin : end - begin + 1; + return ReplaceSubstring(self, begin, count, value); + } + + [RubyMethod("[]=")] + public static MutableString ReplaceSubstring(MutableString/*!*/ self, + [NotNull]MutableString/*!*/ substring, [DefaultProtocol, NotNull]MutableString/*!*/ value) { + + int index = self.IndexOf(substring); + if (index == -1) { + throw RubyExceptions.CreateIndexError("string not matched"); + } + + return ReplaceSubstring(self, index, substring.Length, value); + } + + [RubyMethod("[]=")] + public static MutableString ReplaceSubstring(MutableString/*!*/ self, + [NotNull]RubyRegex/*!*/ regex, [DefaultProtocol, NotNull]MutableString/*!*/ value) { + + Match match = regex.Match(self); + if (!match.Success) { + throw RubyExceptions.CreateIndexError("regexp not matched"); + } + + return ReplaceSubstring(self, match.Index, match.Length, value); + } + + #endregion + + #region casecmp, capitalize, capitalize!, downcase, downcase!, swapcase, swapcase!, upcase, upcase! + + public static bool UpCaseChar(MutableString/*!*/ self, int index) { + char current = self.GetChar(index); + if (current >= 'a' && current <= 'z') { + self.SetChar(index, Char.ToUpper(current)); + return true; + } + return false; + } + + public static bool DownCaseChar(MutableString/*!*/ self, int index) { + char current = self.GetChar(index); + if (current >= 'A' && current <= 'Z') { + self.SetChar(index, Char.ToLower(current)); + return true; + } + return false; + } + + public static bool SwapCaseChar(MutableString/*!*/ self, int index) { + char current = self.GetChar(index); + if (current >= 'A' && current <= 'Z') { + self.SetChar(index, Char.ToLower(current)); + return true; + } else if (current >= 'a' && current <= 'z') { + self.SetChar(index, Char.ToUpper(current)); + return true; + } + return false; + } + + public static bool CapitalizeMutableString(MutableString/*!*/ str) { + bool changed = false; + if (str.Length > 0) { + if (UpCaseChar(str, 0)) { + changed = true; + } + for (int i = 1; i < str.Length; ++i) { + if (DownCaseChar(str, i)) { + changed = true; + } + } + } + return changed; + } + + public static bool UpCaseMutableString(MutableString/*!*/ str) { + bool changed = false; + for (int i = 0; i < str.Length; ++i) { + if (UpCaseChar(str, i)) { + changed = true; + } + } + return changed; + } + + public static bool DownCaseMutableString(MutableString/*!*/ str) { + bool changed = false; + for (int i = 0; i < str.Length; ++i) { + if (DownCaseChar(str, i)) { + changed = true; + } + } + return changed; + } + + public static bool SwapCaseMutableString(MutableString/*!*/ str) { + bool changed = false; + for (int i = 0; i < str.Length; ++i) { + if (SwapCaseChar(str, i)) { + changed = true; + } + } + return changed; + } + + [RubyMethod("casecmp")] + public static int Casecmp(MutableString/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ other) { + return Compare(DownCase(self), DownCase(other)); + } + + [RubyMethod("capitalize")] + public static MutableString/*!*/ Capitalize(MutableString/*!*/ self) { + MutableString result = self.Clone(); + CapitalizeMutableString(result); + return result; + } + + [RubyMethod("capitalize!")] + public static MutableString CapitalizeInPlace(MutableString/*!*/ self) { + return CapitalizeMutableString(self) ? self : null; + } + + [RubyMethod("downcase")] + public static MutableString/*!*/ DownCase(MutableString/*!*/ self) { + MutableString result = self.Clone(); + DownCaseMutableString(result); + return result; + } + + [RubyMethod("downcase!")] + public static MutableString DownCaseInPlace(MutableString/*!*/ self) { + return DownCaseMutableString(self) ? self : null; + } + + [RubyMethod("swapcase")] + public static MutableString/*!*/ SwapCase(MutableString/*!*/ self) { + MutableString result = self.Clone(); + SwapCaseMutableString(result); + return result; + } + + [RubyMethod("swapcase!")] + public static MutableString SwapCaseInPlace(MutableString/*!*/ self) { + return SwapCaseMutableString(self) ? self : null; + } + + [RubyMethod("upcase")] + public static MutableString/*!*/ UpCase(MutableString/*!*/ self) { + MutableString result = self.Clone(); + UpCaseMutableString(result); + return result; + } + + [RubyMethod("upcase!")] + public static MutableString UpCaseInPlace(MutableString/*!*/ self) { + return UpCaseMutableString(self) ? self : null; + } + + #endregion + + + #region center + + private static readonly MutableString _DefaultPadding = MutableString.Create(" ").Freeze(); + + [RubyMethod("center")] + public static MutableString/*!*/ Center(MutableString/*!*/ self, + [DefaultProtocol]int length, + [Optional, DefaultProtocol]MutableString padding) { + + if (padding != null && padding.Length == 0) { + throw RubyExceptions.CreateArgumentError("zero width padding"); + } + + if (self.Length >= length) { + return self; + } + + if (padding == null) { + padding = _DefaultPadding; + } + + char[] charArray = new char[length]; + int n = (length - self.Length) / 2; + + for (int i = 0; i < n; i++) { + charArray[i] = padding.GetChar(i % padding.Length); + } + + for (int i = 0; i < self.Length; i++) { + charArray[n + i] = self.GetChar(i); + } + + int m = length - self.Length - n; + for (int i = 0; i < m; i++) { + charArray[n + self.Length + i] = padding.GetChar(i % padding.Length); + } + + return self.CreateInstance().Append(new String(charArray)).TaintBy(self).TaintBy(padding); + } + + #endregion + + + #region chomp, chomp!, chop, chop! + + private static bool EndsWith(MutableString/*!*/ str, MutableString/*!*/ terminator) { + int offset = str.Length - terminator.Length; + if (offset < 0) { + return false; + } + + if (str.IsBinary) { + for (int i = 0; i < terminator.Length; i++) { + if (str.GetChar(offset + i) != terminator.GetChar(i)) { + return false; + } + } + } else { + for (int i = 0; i < terminator.Length; i++) { + if (str.GetByte(offset + i) != terminator.GetByte(i)) { + return false; + } + } + } + + return true; + } + + private static MutableString/*!*/ ChompTrailingCarriageReturns(MutableString/*!*/ str, bool removeCarriageReturnsToo) { + int end = str.Length; + while (true) { + if (end > 1) { + if (str.GetChar(end - 1) == '\n') { + end -= str.GetChar(end - 2) == '\r' ? 2 : 1; + } else if (removeCarriageReturnsToo && str.GetChar(end - 1) == '\r') { + end -= 1; + } + else { + break; + } + } else if (end > 0) { + if (str.GetChar(end - 1) == '\n' || str.GetChar(end - 1) == '\r') { + end -= 1; + } + break; + } else { + break; + } + } + return str.GetSlice(0, end); + } + + private static MutableString InternalChomp(MutableString/*!*/ self, MutableString separator) { + if (separator == null) { + return self.Clone(); + } + + // Remove multiple trailing CR/LFs + if (separator.Length == 0) { + return ChompTrailingCarriageReturns(self, false).TaintBy(self); + } + + // Remove single trailing CR/LFs + MutableString result = self.Clone(); + int length = result.Length; + if (separator.Length == 1 && separator.GetChar(0) == '\n') { + if (length > 1 && result.GetChar(length - 2) == '\r' && result.GetChar(length - 1) == '\n') { + result.Remove(length - 2, 2); + } else if (length > 0 && (self.GetChar(length - 1) == '\n' || result.GetChar(length - 1) == '\r')) { + result.Remove(length - 1, 1); + } + } else if (EndsWith(result, separator)) { + result.Remove(length - separator.Length, separator.Length); + } + + return result; + } + + [RubyMethod("chomp")] + public static MutableString/*!*/ Chomp(RubyContext/*!*/ context, MutableString/*!*/ self) { + return InternalChomp(self, context.InputSeparator); + } + + [RubyMethod("chomp")] + public static MutableString/*!*/ Chomp(MutableString/*!*/ self, [DefaultProtocol]MutableString separator) { + return InternalChomp(self, separator); + } + + [RubyMethod("chomp!")] + public static MutableString/*!*/ ChompInPlace(RubyContext/*!*/ context, MutableString/*!*/ self) { + return ChompInPlace(self, context.InputSeparator); + } + + [RubyMethod("chomp!")] + public static MutableString ChompInPlace(MutableString/*!*/ self, [DefaultProtocol]MutableString separator) { + MutableString result = InternalChomp(self, separator); + + if (result.Equals(self) || result == null) { + return null; + } + + self.Clear(); + self.Append(result); + return self; + } + + private static MutableString/*!*/ ChopInteral(MutableString/*!*/ self) { + if (self.Length == 1 || self.GetChar(self.Length - 2) != '\r' || self.GetChar(self.Length - 1) != '\n') { + self.Remove(self.Length - 1, 1); + } else { + self.Remove(self.Length - 2, 2); + } + return self; + } + + [RubyMethod("chop!")] + public static MutableString ChopInPlace(MutableString/*!*/ self) { + if (self.Length == 0) return null; + return ChopInteral(self); + } + + [RubyMethod("chop")] + public static MutableString/*!*/ Chop(MutableString/*!*/ self) { + return (self.Length == 0) ? self.CreateInstance().TaintBy(self) : ChopInteral(self.Clone()); + } + + #endregion + + + #region dump, inspect + + public static void AppendStringRepresentationOfChar(StringBuilder/*!*/ result, int c, int nextc, bool octals) { + switch (c) { + case '\a': result.Append("\\a"); break; + case '\b': result.Append("\\b"); break; + case '\t': result.Append("\\t"); break; + case '\n': result.Append("\\n"); break; + case '\v': result.Append("\\v"); break; + case '\f': result.Append("\\f"); break; + case '\r': result.Append("\\r"); break; + case 27: result.Append("\\e"); break; + case '"': result.Append("\\\""); break; + case '\\': result.Append("\\\\"); break; + + case '#': + switch (nextc) { + case '{': + case '$': + case '@': + result.Append('\\'); + break; + } + result.Append('#'); + break; + + default: + if (octals && c < 32 || c > 126) { + result.Append('\\'); + result.Append(System.Convert.ToString(c, 8).PadLeft(3, '0')); + } else { + result.Append((char)c); + } + break; + } + } + + [RubyMethod("dump")] + [RubyMethod("inspect")] + public static MutableString/*!*/ Dump(RubyContext/*!*/ context, MutableString/*!*/ self) { + StringBuilder result = new StringBuilder(); + result.Append('"'); + + byte[] bytes = self.ToByteArray(); + for (int i = 0; i < bytes.Length; i++) { + AppendStringRepresentationOfChar(result, bytes[i], i + 1 < bytes.Length ? bytes[i + 1] : -1, true); + } + + result.Append('"'); + return self.CreateInstance().Append(result).TaintBy(self); + } + + #endregion + + #region each, each_byte, each_line + + [RubyMethod("each_byte")] + public static object EachByte(BlockParam block, MutableString/*!*/ self) { + if (block == null && self.Length > 0) { + throw RubyExceptions.NoBlockGiven(); + } + + int i = 0; + while (i < self.Length) { + object result; + if (block.Yield((int)self.GetByte(i), out result)) { + return result; + } + i++; + } + return self; + } + + [RubyMethod("each")] + [RubyMethod("each_line")] + public static object EachLine(RubyContext/*!*/ context, BlockParam block, MutableString/*!*/ self) { + return EachLine(block, self, context.InputSeparator); + } + + private static readonly MutableString _DefaultLineSeparator = MutableString.Create("\n").Freeze(); + private static readonly MutableString _DefaultDoubleLineSeparator = MutableString.Create("\n\n").Freeze(); + + [RubyMethod("each")] + [RubyMethod("each_line")] + public static object EachLine(BlockParam block, MutableString/*!*/ self, [DefaultProtocol]MutableString/*!*/ separator) { + if (separator == null) { + separator = MutableString.Empty; + } + + uint version = self.Version; + + MutableString paragraphSeparator; + if (separator.IsEmpty) { + separator = _DefaultLineSeparator; + paragraphSeparator = _DefaultDoubleLineSeparator; + } else { + paragraphSeparator = null; + } + + // TODO: this is slow, refactor when we redo MutableString + MutableString str = self; + int start = 0; + + // In "normal" mode just split the string at the end of each seperator occurrance. + // In "paragraph" mode, split the string at the end of each occurrance of two or more + // successive seperators. + while (start < self.Length) { + int end; + if (paragraphSeparator == null) { + end = str.IndexOf(separator, start); + if (end >= 0) { + end += separator.Length; + } else { + end = str.Length; + } + } else { + end = str.IndexOf(paragraphSeparator, start); + if (end >= 0) { + end += (2 * separator.Length); + while (str.IndexOf(separator, end) == end) { + end += separator.Length; + } + } else { + end = str.Length; + } + } + + // Yield the current line + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + object result; + MutableString line = self.CreateInstance().TaintBy(self).Append(str, start, end - start); + if (block.Yield(line, out result)) { + return result; + } + + start = end; + } + + // Ensure that the underlying string has not been mutated during the iteration + RequireNoVersionChange(version, self); + return self; + } + + #endregion + + #region empty?, size, length, encoding + + [RubyMethod("empty?")] + public static bool Empty(MutableString/*!*/ self) { + return self.Length == 0; + } + + [RubyMethod("size")] + [RubyMethod("length")] + public static int MutableStringLength(MutableString/*!*/ self) { + return self.Length; + } + + [RubyMethod("encoding")] + public static RubyEncoding GetEncoding(MutableString/*!*/ self) { + return (self.Encoding != null) ? RubyEncoding.GetRubyEncoding(self.Encoding) : null; + } + + #endregion + + + #region sub, gsub + + // returns true if block jumped + // "result" will be null if there is no successful match + private static bool BlockReplaceFirst(RubyScope/*!*/ scope, MutableString/*!*/ input, BlockParam/*!*/ block, + RubyRegex/*!*/ pattern, out object blockResult, out MutableString result) { + + var matchScope = scope.GetInnerMostClosureScope(); + MatchData match = RegexpOps.Match(scope, pattern, input); + if (match == null || !match.Success) { + result = null; + blockResult = null; + matchScope.CurrentMatch = null; + return false; + } + + // copy upfront so that no modifications to the input string are included in the result: + result = input.Clone(); + matchScope.CurrentMatch = match; + + if (block.Yield(MutableString.Create(match.Value), out blockResult)) { + return true; + } + + // resets the $~ scope variable to the last match (skipd if block jumped): + matchScope.CurrentMatch = match; + + MutableString replacement = Protocols.ConvertToString(scope.RubyContext, blockResult); + result.TaintBy(replacement); + + // Note - we don't interpolate special sequences like \1 in block return value + result.Replace(match.Index, match.Length, replacement); + + blockResult = null; + return false; + } + + // returns true if block jumped + // "result" will be null if there is no successful match + private static bool BlockReplaceAll(RubyScope/*!*/ scope, MutableString/*!*/ input, BlockParam/*!*/ block, + RubyRegex/*!*/ regex, out object blockResult, out MutableString result) { + + var matchScope = scope.GetInnerMostClosureScope(); + + MatchCollection matches = regex.Matches(input); + if (matches.Count == 0) { + result = null; + blockResult = null; + matchScope.CurrentMatch = null; + return false; + } + + // create an empty result: + result = input.CreateInstance().TaintBy(input); + + int offset = 0; + foreach (Match match in matches) { + MatchData currentMatch = new MatchData(match, input); + matchScope.CurrentMatch = currentMatch; + + uint version = input.Version; + if (block.Yield(MutableString.Create(match.Value), out blockResult)) { + return true; + } + if (input.Version != version) { + return false; + } + + // resets the $~ scope variable to the last match (skipd if block jumped): + matchScope.CurrentMatch = currentMatch; + + MutableString replacement = Protocols.ConvertToString(scope.RubyContext, blockResult); + result.TaintBy(replacement); + + // prematch: + result.Append(input, offset, match.Index - offset); + + // replacement (unlike ReplaceAll, don't interpolate special sequences like \1 in block return value): + result.Append(replacement); + + offset = match.Index + match.Length; + } + + // post-last-match: + result.Append(input, offset, input.Length - offset); + + blockResult = null; + return false; + } + + private static void AppendBackslashes(int backslashCount, MutableString/*!*/ result, int minBackslashes) { + for (int j = 0; j < ((backslashCount - 1) >> 1) + minBackslashes; j++) { + result.Append('\\'); + } + } + + private static void AppendReplacementExpression(MutableString input, GroupCollection/*!*/ groups, MutableString/*!*/ result, MutableString/*!*/ replacement) { + int backslashCount = 0; + for (int i = 0; i < replacement.Length; i++) { + char c = replacement.GetChar(i); + if (c == '\\') + backslashCount++; + else if (backslashCount == 0) + result.Append(c); + else { + AppendBackslashes(backslashCount, result, 0); + // Odd number of \'s + digit means insert replacement expression + if ((backslashCount & 1) == 1) { + if (Char.IsDigit(c)) { + AppendGroupByIndex(groups, c - '0', backslashCount, result); + } else if (c == '&') { + AppendGroupByIndex(groups, groups.Count - 1, backslashCount, result); + } else if (c == '`') { + // Replace with everything in the input string BEFORE the match + result.Append(input, 0, groups[0].Index); + } else if (c == '\'') { + // Replace with everything in the input string AFTER the match + int start = groups[0].Index + groups[0].Length; + result.Append(input, start, input.Length - start); + } else if (c == '+') { + // Replace last character in last successful match group + AppendLastCharOfLastMatchGroup(groups, result); + } else { + // unknown escaped replacement char, go ahead and replace untouched + result.Append('\\'); + result.Append(c); + } + } else { + // Any other # of \'s or a non-digit character means insert literal \'s and character + AppendBackslashes(backslashCount, result, 1); + result.Append(c); + } + backslashCount = 0; + } + } + AppendBackslashes(backslashCount, result, 1); + } + + private static void AppendLastCharOfLastMatchGroup(GroupCollection groups, MutableString result) { + int i = groups.Count - 1; + // move to last successful match group + while (i > 0 && !groups[i].Success) { + i--; + } + + if (i > 0 && groups[i].Value.Length > 0) { + result.Append(groups[i].Value[groups[i].Length - 1]); + } + } + + private static void AppendGroupByIndex(GroupCollection/*!*/ groups, int index, int backslashCount, MutableString/*!*/ result) { + if (groups[index].Success) { + result.Append(groups[index].Value); + } + } + + private static void AppendReplacementExpression(MutableString/*!*/ input, MatchData/*!*/ match, MutableString/*!*/ result, MutableString/*!*/ replacement) { + AppendReplacementExpression(input, match.Groups, result, replacement); + } + + // TODO: we have two overloads right now because we haven't unified Matches to return a List yet ... + private static void AppendReplacementExpression(MutableString/*!*/ input, Match/*!*/ match, MutableString/*!*/ result, MutableString/*!*/ replacement) { + AppendReplacementExpression(input, match.Groups, result, replacement); + } + + private static MutableString ReplaceFirst(RubyScope/*!*/ scope, MutableString/*!*/ input, MutableString/*!*/ replacement, RubyRegex/*!*/ pattern) { + MatchData match = RegexpOps.Match(scope, pattern, input); + if (match == null || !match.Success) { + return null; + } + + MutableString result = input.CreateInstance().TaintBy(input).TaintBy(replacement); + + // prematch: + result.Append(input, 0, match.Index); + + AppendReplacementExpression(input, match, result, replacement); + + // postmatch: + int offset = match.Index + match.Length; + result.Append(input, offset, input.Length - offset); + + return result; + } + + private static MutableString ReplaceAll(RubyScope/*!*/ scope, MutableString/*!*/ input, MutableString/*!*/ replacement, + RubyRegex/*!*/ regex) { + var matchScope = scope.GetInnerMostClosureScope(); + + // case of all + MatchCollection matches = regex.Matches(input); + if (matches.Count == 0) { + matchScope.CurrentMatch = null; + return null; + } + + MutableString result = input.CreateInstance().TaintBy(input).TaintBy(replacement); + + int offset = 0; + foreach (Match match in matches) { + result.Append(input, offset, match.Index - offset); + AppendReplacementExpression(input, match, result, replacement); + offset = match.Index + match.Length; + } + + result.Append(input, offset, input.Length - offset); + + matchScope.CurrentMatch = new MatchData(matches[matches.Count - 1], input); + return result; + } + + [RubyMethod("sub")] + public static object BlockReplaceFirst(RubyScope/*!*/ scope, [NotNull]BlockParam/*!*/ block, MutableString/*!*/ self, + [NotNull]RubyRegex/*!*/ pattern) { + + object blockResult; + MutableString result; + return BlockReplaceFirst(scope, self, block, pattern, out blockResult, out result) ? blockResult : (result ?? self.Clone()); + } + + [RubyMethod("gsub")] + public static object BlockReplaceAll(RubyScope/*!*/ scope, [NotNull]BlockParam/*!*/ block, MutableString/*!*/ self, + [NotNull]RubyRegex pattern) { + + object blockResult; + MutableString result; + uint version = self.Version; + object r = BlockReplaceAll(scope, self, block, pattern, out blockResult, out result) ? blockResult : (result ?? self.Clone()); + + RequireNoVersionChange(version, self); + return r; + } + + [RubyMethod("sub")] + public static object BlockReplaceFirst(RubyScope/*!*/ scope, [NotNull]BlockParam/*!*/ block, MutableString/*!*/ self, + [NotNull]MutableString matchString) { + + object blockResult; + MutableString result; + var regex = new RubyRegex(Regex.Escape(matchString.ToString()), RubyRegexOptions.NONE); + + return BlockReplaceFirst(scope, self, block, regex, out blockResult, out result) ? blockResult : (result ?? self.Clone()); + } + + [RubyMethod("gsub")] + public static object BlockReplaceAll(RubyScope/*!*/ scope, [NotNull]BlockParam/*!*/ block, MutableString/*!*/ self, + [NotNull]MutableString matchString) { + + object blockResult; + MutableString result; + var regex = new RubyRegex(Regex.Escape(matchString.ToString()), RubyRegexOptions.NONE); + + uint version = self.Version; + object r = BlockReplaceAll(scope, self, block, regex, out blockResult, out result) ? blockResult : (result ?? self.Clone()); + RequireNoVersionChange(version, self); + return r; + } + + [RubyMethod("sub")] + public static MutableString ReplaceFirst(RubyScope/*!*/ scope, MutableString/*!*/ self, + [DefaultProtocol, NotNull]RubyRegex/*!*/ pattern, [DefaultProtocol, NotNull]MutableString/*!*/ replacement) { + + return ReplaceFirst(scope, self, replacement, pattern) ?? self.Clone(); + } + + [RubyMethod("gsub")] + public static MutableString ReplaceAll(RubyScope/*!*/ scope, MutableString/*!*/ self, + [DefaultProtocol, NotNull]RubyRegex/*!*/ pattern, [DefaultProtocol, NotNull]MutableString/*!*/ replacement) { + + return ReplaceAll(scope, self, replacement, pattern) ?? self.Clone(); + } + + #endregion + + + #region sub!, gsub! + + private static object BlockReplaceInPlace(RubyScope/*!*/ scope, BlockParam/*!*/ block, MutableString/*!*/ self, + RubyRegex/*!*/ pattern, bool replaceAll) { + + object blockResult; + + uint version = self.Version; + + // prepare replacement in a builder: + MutableString builder; + if (replaceAll ? + BlockReplaceAll(scope, self, block, pattern, out blockResult, out builder) : + BlockReplaceFirst(scope, self, block, pattern, out blockResult, out builder)) { + + // block jumped: + return blockResult; + } + + // unsuccessful match: + if (builder == null) { + return null; + } + + RequireNoVersionChange(version, self); + + if (self.IsFrozen) { + throw new RuntimeError("string frozen"); + } + + // replace content of self with content of the builder: + self.Replace(0, self.Length, builder); + return self.TaintBy(builder); + } + + private static MutableString ReplaceInPlace(RubyScope/*!*/ scope, MutableString/*!*/ self, RubyRegex/*!*/ pattern, + MutableString/*!*/ replacement, bool replaceAll) { + + MutableString builder = replaceAll ? + ReplaceAll(scope, self, replacement, pattern) : + ReplaceFirst(scope, self, replacement, pattern); + + // unsuccessful match: + if (builder == null) { + return null; + } + + self.Replace(0, self.Length, builder); + return self.TaintBy(builder); + } + + [RubyMethod("sub!")] + public static object BlockReplaceFirstInPlace(RubyScope/*!*/ scope, [NotNull]BlockParam/*!*/ block, MutableString/*!*/ self, + [DefaultProtocol, NotNull]RubyRegex/*!*/ pattern) { + + return BlockReplaceInPlace(scope, block, self, pattern, false); + } + + [RubyMethod("gsub!")] + public static object BlockReplaceAllInPlace(RubyScope/*!*/ scope, [NotNull]BlockParam/*!*/ block, MutableString/*!*/ self, + [DefaultProtocol, NotNull]RubyRegex/*!*/ pattern) { + + return BlockReplaceInPlace(scope, block, self, pattern, true); + } + + [RubyMethod("sub!")] + public static MutableString ReplaceFirstInPlace(RubyScope/*!*/ scope, MutableString/*!*/ self, + [DefaultProtocol, NotNull]RubyRegex/*!*/ pattern, [DefaultProtocol, NotNull]MutableString/*!*/ replacement) { + + return ReplaceInPlace(scope, self, pattern, replacement, false); + } + + [RubyMethod("gsub!")] + public static MutableString ReplaceAllInPlace(RubyScope/*!*/ scope, MutableString/*!*/ self, + [DefaultProtocol, NotNull]RubyRegex/*!*/ pattern, [DefaultProtocol, NotNull]MutableString/*!*/ replacement) { + + return ReplaceInPlace(scope, self, pattern, replacement, true); + } + + #endregion + + + #region index, rindex + + [RubyMethod("index")] + public static object Index(MutableString/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ substring, [DefaultProtocol, Optional]int start) { + + if (!NormalizeStart(self, ref start)) { + return null; + } + int result = self.IndexOf(substring, start); + return (result != -1) ? ScriptingRuntimeHelpers.Int32ToObject(result) : null; + } + + [RubyMethod("index")] + public static object Index(MutableString/*!*/ self, + int character, [DefaultProtocol, Optional]int start) { + + if (!NormalizeStart(self, ref start) || character < 0 || character > 255) { + return null; + } + int result = self.IndexOf((byte)character, start); + return (result != -1) ? ScriptingRuntimeHelpers.Int32ToObject(result) : null; + } + + [RubyMethod("index")] + public static object Index(RubyScope/*!*/ scope, MutableString/*!*/ self, + [NotNull]RubyRegex/*!*/ regex, [DefaultProtocol, Optional]int start) { + + if (!NormalizeStart(self, ref start)) { + return null; + } + + var matchScope = scope.GetInnerMostClosureScope(); + + Match match = regex.Match(self, start); + if (match.Success) { + matchScope.CurrentMatch = new MatchData(match, self); + return ScriptingRuntimeHelpers.Int32ToObject(match.Index); + } else { + matchScope.CurrentMatch = null; + return null; + } + } + + [RubyMethod("rindex")] + public static object ReverseIndex(MutableString/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ substring) { + return ReverseIndex(self, substring, self.Length); + } + + [RubyMethod("rindex")] + public static object ReverseIndex(MutableString/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ substring, [DefaultProtocol]int start) { + start = NormalizeIndex(self, start); + + if (start < 0) { + return null; + } + + if (substring.IsEmpty) { + return ScriptingRuntimeHelpers.Int32ToObject((start >= self.Length) ? self.Length : start); + } + + start += substring.Length - 1; + + if (start >= self.Length) { + start = self.Length - 1; + } + + int result = self.LastIndexOf(substring, start); + return (result != -1) ? ScriptingRuntimeHelpers.Int32ToObject(result) : null; + } + + [RubyMethod("rindex")] + public static object ReverseIndex(MutableString/*!*/ self, + int character, [DefaultProtocol, DefaultParameterValue(-1)]int start) { + + start = NormalizeIndex(self, start); + if (start < 0 || character < 0 || character > 255) { + return null; + } + + if (start >= self.Length) { + start = self.Length - 1; + } + + int result = self.LastIndexOf((byte)character, start); + return (result != -1) ? ScriptingRuntimeHelpers.Int32ToObject(result) : null; + } + + // TODO: note that .NET regex semantics don't line up well in these cases - so some specs do fail. There are 4 failures in rindex that are due to regex differences. + + [RubyMethod("rindex")] + public static object ReverseIndex(RubyScope/*!*/ scope, MutableString/*!*/ self, + [NotNull]RubyRegex/*!*/ regex) { + return ReverseIndex(scope, self, regex, self.Length); + } + + [RubyMethod("rindex")] + public static object ReverseIndex(RubyScope/*!*/ scope, MutableString/*!*/ self, + [NotNull]RubyRegex/*!*/ regex, [DefaultProtocol]int start) { + + start = NormalizeIndex(self, start); + if (start < 0) { + return null; + } + start = (start > self.Length) ? self.Length : start; + + var matchScope = scope.GetInnerMostClosureScope(); + + Match match = regex.ReverseMatch(self, start); + if (match.Success) { + matchScope.CurrentMatch = new MatchData(match, self); + return ScriptingRuntimeHelpers.Int32ToObject(match.Index); + } else { + matchScope.CurrentMatch = null; + return null; + } + } + + // Start in range ==> search range from the first character towards the end. + // + // [-length, 0) ==> [0, length + start] + // start < -length ==> false + // [0, length) ==> [start, length) + // start > length ==> false + // + private static bool NormalizeStart(MutableString/*!*/ self, ref int start) { + start = NormalizeIndex(self, start); + if (start < 0 || start > self.Length) { + return false; + } + return true; + } + + #endregion + + + #region delete + + private static MutableString/*!*/ InternalDelete(MutableString/*!*/ self, MutableString[]/*!*/ ranges) { + BitArray map = new RangeParser(ranges).Parse(); + MutableString result = self.CreateInstance().TaintBy(self); + for (int i = 0; i < self.Length; i++) { + if (!map.Get(self.GetChar(i))) { + result.Append(self.GetChar(i)); + } + } + return result; + } + + private static MutableString/*!*/ InternalDeleteInPlace(MutableString/*!*/ self, MutableString[]/*!*/ ranges) { + MutableString result = InternalDelete(self, ranges); + if (self.Equals(result)) { + return null; + } + + self.Clear(); + self.Append(result); + return self; + } + + [RubyMethod("delete")] + public static MutableString/*!*/ Delete(RubyContext/*!*/ context, MutableString/*!*/ self, [NotNull]params object[] str) { + if (str.Length == 0) { + throw RubyExceptions.CreateArgumentError("wrong number of arguments"); + } + return InternalDelete(self, Protocols.CastToStrings(context, str)); + } + + [RubyMethod("delete!")] + public static MutableString/*!*/ DeleteInPlace(RubyContext/*!*/ context, MutableString/*!*/ self, [NotNull]params object[] str) { + if (str.Length == 0) { + throw RubyExceptions.CreateArgumentError("wrong number of arguments"); + } + return InternalDeleteInPlace(self, Protocols.CastToStrings(context, str)); + } + + #endregion + + #region count + + private static object InternalCount(MutableString/*!*/ self, MutableString[]/*!*/ ranges) { + BitArray map = new RangeParser(ranges).Parse(); + int count = 0; + for (int i = 0; i < self.Length; i++) { + if (map.Get(self.GetChar(i))) + count++; + } + return ScriptingRuntimeHelpers.Int32ToObject(count); + } + + [RubyMethod("count")] + public static object Count(RubyContext/*!*/ context, MutableString/*!*/ self, [NotNull]params object[] str) { + if (str.Length == 0) { + throw RubyExceptions.CreateArgumentError("wrong number of arguments"); + } + return InternalCount(self, Protocols.CastToStrings(context, str)); + } + + #endregion + + #region include? + + [RubyMethod("include?")] + public static bool Include(MutableString/*!*/ str, [DefaultProtocol, NotNull]MutableString/*!*/ subString) { + return str.IndexOf(subString) != -1; + } + + [RubyMethod("include?")] + public static bool Include(MutableString/*!*/ str, int c) { + return str.IndexOf((byte)(c % 256)) != -1; + } + + #endregion + + + #region insert + + [RubyMethod("insert")] + public static MutableString Insert(MutableString/*!*/ self, [DefaultProtocol]int start, [DefaultProtocol, NotNull]MutableString/*!*/ value) { + int offset = start < 0 ? start + self.Length + 1 : start; + if (offset > self.Length || offset < 0) { + throw RubyExceptions.CreateIndexError(String.Format("index {0} out of string", start)); + } + + return self.Insert(offset, value).TaintBy(value); + } + + #endregion + + + #region match + + private static readonly CallSite>/*!*/ MatchDispatchSharedSite = + CallSite>.Create(RubySites.InstanceCallAction("=~", 1)); + + [RubyMethod("=~")] + public static object Match(RubyScope/*!*/ scope, MutableString/*!*/ self, [NotNull]RubyRegex/*!*/ regex) { + return RegexpOps.MatchIndex(scope, regex, self); + } + + [RubyMethod("=~")] + public static object Match(MutableString/*!*/ self, [NotNull]MutableString/*!*/ str) { + throw RubyExceptions.CreateTypeError("type mismatch: String given"); + } + + [RubyMethod("=~")] + public static object Match(RubyScope/*!*/ scope, MutableString/*!*/ self, object obj) { + return MatchDispatchSharedSite.Target(MatchDispatchSharedSite, scope.RubyContext, obj, self); + } + + [RubyMethod("match")] + public static object MatchRegexp(RubyScope/*!*/ scope, MutableString/*!*/ self, [NotNull]RubyRegex/*!*/ regex) { + return _MatchSite.Target(_MatchSite, scope, regex, self); + } + + [RubyMethod("match")] + public static object MatchObject(RubyScope/*!*/ scope, MutableString/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ pattern) { + return _MatchSite.Target(_MatchSite, scope, new RubyRegex(pattern, RubyRegexOptions.NONE), self); + } + + private static CallSite> _MatchSite = CallSite>.Create( + RubyCallAction.Make("match", RubyCallSignature.WithScope(1))); + + #endregion + + + #region scan + + [RubyMethod("scan")] + public static RubyArray/*!*/ Scan(RubyScope/*!*/ scope, MutableString/*!*/ self, [DefaultProtocol, NotNull]RubyRegex/*!*/ regex) { + MatchCollection matches = regex.Matches(self); + + var matchScope = scope.GetInnerMostClosureScope(); + + RubyArray result = new RubyArray(matches.Count); + if (matches.Count == 0) { + matchScope.CurrentMatch = null; + return result; + } + + foreach (Match match in matches) { + result.Add(MatchToScanResult(scope, self, regex, match)); + } + + matchScope.CurrentMatch = new MatchData(matches[matches.Count - 1], self); + return result; + } + + [RubyMethod("scan")] + public static object/*!*/ Scan(RubyScope/*!*/ scope, [NotNull]BlockParam/*!*/ block, MutableString/*!*/ self, [DefaultProtocol, NotNull]RubyRegex regex) { + var matchScope = scope.GetInnerMostClosureScope(); + + MatchCollection matches = regex.Matches(self); + if (matches.Count == 0) { + matchScope.CurrentMatch = null; + return self; + } + + foreach (Match match in matches) { + var currentMatch = new MatchData(match, self); + + matchScope.CurrentMatch = currentMatch; + + object blockResult; + if (block.Yield(MatchToScanResult(scope, self, regex, match), out blockResult)) { + return blockResult; + } + + // resets the $~ scope variable to the last match (skipd if block jumped): + matchScope.CurrentMatch = currentMatch; + } + return self; + } + + private static object MatchToScanResult(RubyScope/*!*/ scope, MutableString/*!*/ self, RubyRegex/*!*/ regex, Match/*!*/ match) { + if (match.Groups.Count == 1) { + return MutableString.Create(match.Value).TaintBy(self).TaintBy(regex, scope); + } else { + var result = new RubyArray(match.Groups.Count - 1); + for (int i = 1; i < match.Groups.Count; i++) { + if (match.Groups[i].Success) { + result.Add(MutableString.Create(match.Groups[i].Value).TaintBy(self).TaintBy(regex, scope)); + } else { + result.Add(null); + } + } + return result; + } + } + + #endregion + + + #region succ, succ! + + public static int GetIndexOfRightmostAlphaNumericCharacter(MutableString/*!*/ str, int index) { + for (int i = index; i >= 0; --i) + if (Char.IsLetterOrDigit(str.GetChar(i))) + return i; + + return -1; + } + + // TODO: remove recursion + public static void IncrementAlphaNumericChar(MutableString/*!*/ str, int index) { + char c = str.GetChar(index); + if (c == 'z' || c == 'Z' || c == '9') { + int nextIndex = GetIndexOfRightmostAlphaNumericCharacter(str, index - 1); + if (c == 'z') { + str.SetChar(index, 'a'); + if (nextIndex == -1) + str.Insert(index, "a"); + else + IncrementAlphaNumericChar(str, nextIndex); + } else if (c == 'Z') { + str.SetChar(index, 'A'); + if (nextIndex == -1) + str.Insert(index, "A"); + else + IncrementAlphaNumericChar(str, nextIndex); + } else { + str.SetChar(index, '0'); + if (nextIndex == -1) + str.Insert(index, "1"); + else + IncrementAlphaNumericChar(str, nextIndex); + } + } else { + IncrementChar(str, index); + } + } + + public static void IncrementChar(MutableString/*!*/ str, int index) { + byte c = str.GetByte(index); + if (c == 255) { + str.SetByte(index, 0); + if (index > 0) { + IncrementChar(str, index - 1); + } else { + str.Insert(0, 1); + } + } else { + str.SetByte(index, unchecked((byte)(c + 1))); + } + } + + [RubyMethod("succ!")] + [RubyMethod("next!")] + public static MutableString/*!*/ SuccInPlace(MutableString/*!*/ self) { + if (self.IsEmpty) { + return self; + } + + int index = GetIndexOfRightmostAlphaNumericCharacter(self, self.Length - 1); + if (index == -1) { + IncrementChar(self, self.Length - 1); + } else { + IncrementAlphaNumericChar(self, index); + } + + return self; + } + + [RubyMethod("succ")] + [RubyMethod("next")] + public static MutableString/*!*/ Succ(MutableString/*!*/ self) { + return SuccInPlace(self.Clone()); + } + + #endregion + + + #region split + + private static RubyArray/*!*/ MakeRubyArray(MutableString/*!*/ self, MutableString[]/*!*/ elements) { + return MakeRubyArray(self, elements, 0, elements.Length); + } + + private static RubyArray/*!*/ MakeRubyArray(MutableString/*!*/ self, MutableString[]/*!*/ elements, int start, int count) { + RubyArray result = new RubyArray(elements.Length); + for (int i = 0; i < count; i++) { + result.Add(self.CreateInstance().Append(elements[start + i]).TaintBy(self)); + } + return result; + } + + // The IndexOf and InternalSplit helper methods are necessary because Ruby semantics of these methods + // differ from .NET semantics. IndexOf("") returns the next character, which is reflected in the + // InternalSplit method which also flows taint + + private static int IndexOf(MutableString/*!*/ str, MutableString/*!*/ separator, int index) { + if (separator.Length > 0) + return str.IndexOf(separator, index); + else + return index + 1; + } + + private static RubyArray/*!*/ WhitespaceSplit(MutableString/*!*/ self, int maxComponents) { + char[] separators = new char[] { ' ', '\n', '\r', '\t', '\v' }; + MutableString[] elements = self.Split(separators, (maxComponents < 0) ? Int32.MaxValue : maxComponents, StringSplitOptions.RemoveEmptyEntries); + + RubyArray result = new RubyArray(); + foreach (MutableString element in elements) { + result.Add(self.CreateInstance().Append(element).TaintBy(self)); + } + + // Strange behavior to match Ruby semantics + if (maxComponents < 0) { + result.Add(self.CreateInstance().TaintBy(self)); + } + + return result; + } + + private static RubyArray/*!*/ InternalSplit(MutableString/*!*/ self, MutableString separator, StringSplitOptions options, int maxComponents) { + if (separator == null || separator.Length == 1 && separator.GetChar(0) == ' ') { + return WhitespaceSplit(self, maxComponents); + } + + if (maxComponents <= 0) { + maxComponents = Int32.MaxValue; + } + + RubyArray result = new RubyArray(maxComponents == Int32.MaxValue ? 1 : maxComponents + 1); + bool keepEmpty = (options & StringSplitOptions.RemoveEmptyEntries) != StringSplitOptions.RemoveEmptyEntries; + + int selfLength = self.Length; + int i = 0; + int next; + while (maxComponents > 1 && i < selfLength && (next = IndexOf(self, separator, i)) != -1) { + + if (next > i || keepEmpty) { + result.Add(self.CreateInstance().Append(self, i, next - i).TaintBy(self)); + maxComponents--; + } + + i = next + separator.Length; + } + + if (i < selfLength || keepEmpty) { + result.Add(self.CreateInstance().Append(self, i, selfLength - i).TaintBy(self)); + } + + return result; + } + + [RubyMethod("split")] + public static RubyArray/*!*/ Split(RubyScope/*!*/ scope, MutableString/*!*/ self) { + return Split(scope, self, (MutableString)null, 0); + } + + [RubyMethod("split")] + public static RubyArray/*!*/ Split(RubyScope/*!*/ scope, MutableString/*!*/ self, + [DefaultProtocol]MutableString separator, [DefaultProtocol, Optional]int limit) { + + if (separator == null) { + object defaultSeparator = scope.RubyContext.StringSeparator; + RubyRegex regexSeparator = defaultSeparator as RubyRegex; + if (regexSeparator != null) { + return Split(scope, self, regexSeparator, limit); + } + + if (defaultSeparator != null) { + separator = Protocols.CastToString(scope.RubyContext, defaultSeparator); + } + } + + if (limit == 0) { + // suppress trailing empty fields + RubyArray array = InternalSplit(self, separator, StringSplitOptions.None, Int32.MaxValue); + while (array.Count != 0 && ((MutableString)array[array.Count - 1]).Length == 0) { + array.RemoveAt(array.Count - 1); + } + return array; + } else if (limit == 1) { + // returns an array with original string + RubyArray result = new RubyArray(1); + result.Add(self); + return result; + } else { + return InternalSplit(self, separator, StringSplitOptions.None, limit); + } + } + + [RubyMethod("split")] + public static RubyArray/*!*/ Split(RubyScope/*!*/ scope, MutableString/*!*/ self, + [NotNull]RubyRegex/*!*/ regexp, [DefaultProtocol, Optional]int limit) { + + if (regexp.IsEmpty) { + return Split(scope, self, MutableString.Empty, limit); + } + + if (limit == 0) { + // suppress trailing empty fields + RubyArray array = MakeRubyArray(self, regexp.Split(self)); + while (array.Count != 0 && ((MutableString)array[array.Count - 1]).Length == 0) { + array.RemoveAt(array.Count - 1); + } + return array; + } else if (limit == 1) { + // returns an array with original string + RubyArray result = new RubyArray(1); + result.Add(self); + return result; + } else if (limit < 0) { + // does not suppress trailing fields when negative + return MakeRubyArray(self, regexp.Split(self)); + } else { + // limit > 1 limits to N fields + return MakeRubyArray(self, regexp.Split(self, limit)); + } + } + + #endregion + + + #region strip, lstrip, rstrip + + [RubyMethod("strip")] + public static MutableString/*!*/ Strip(RubyContext/*!*/ context, MutableString/*!*/ self) { + return Strip(self, true, true); + } + + [RubyMethod("lstrip")] + public static MutableString/*!*/ StripLeft(RubyContext/*!*/ context, MutableString/*!*/ self) { + return Strip(self, true, false); + } + + [RubyMethod("rstrip")] + public static MutableString/*!*/ StripRight(RubyContext/*!*/ context, MutableString/*!*/ self) { + return Strip(self, false, true); + } + + [RubyMethod("strip!")] + public static MutableString StripInPlace(RubyContext/*!*/ context, MutableString/*!*/ self) { + return StripInPlace(self, true, true); + } + + [RubyMethod("lstrip!")] + public static MutableString StripLeftInPlace(RubyContext/*!*/ context, MutableString/*!*/ self) { + return StripInPlace(self, true, false); + } + + [RubyMethod("rstrip!")] + public static MutableString StripRightInPlace(RubyContext/*!*/ context, MutableString/*!*/ self) { + return StripInPlace(self, false, true); + } + + private static MutableString/*!*/ Strip(MutableString/*!*/ str, bool trimLeft, bool trimRight) { + int left, right; + GetTrimRange(str, trimLeft, trimRight, out left, out right); + return str.GetSlice(left, right - left).TaintBy(str); + } + + public static MutableString StripInPlace(MutableString/*!*/ self, bool trimLeft, bool trimRight) { + int left, right; + GetTrimRange(self, trimLeft, trimRight, out left, out right); + int remaining = right - left; + + // nothing to trim: + if (remaining == self.Length) { + return null; + } + + if (remaining == 0) { + // all whitespace + self.Clear(); + } else { + self.Trim(left, remaining); + } + return self; + } + + private static void GetTrimRange(MutableString/*!*/ str, bool left, bool right, out int leftIndex, out int rightIndex) { + GetTrimRange( + str.Length, + !left ? (Func)null : (i) => Char.IsWhiteSpace(str.GetChar(i)), + !right ? (Func)null : (i) => { + char c = str.GetChar(i); + return Char.IsWhiteSpace(c) || c == '\0'; + }, + out leftIndex, + out rightIndex + ); + } + + // Returns indices of the first non-whitespace character (from left and from right). + // ensures (leftIndex == rightIndex) ==> all characters are whitespace + // leftIndex == 0, rightIndex == length if there is no whitespace to be trimmed. + internal static void GetTrimRange(int length, Func trimLeft, Func trimRight, out int leftIndex, out int rightIndex) { + int i; + if (trimLeft != null) { + i = 0; + while (i < length) { + if (!trimLeft(i)) { + break; + } + i++; + } + } else { + i = 0; + } + + int j; + if (trimRight != null) { + j = length - 1; + // we need to compare i-th character again as it could be treated as right whitespace but not as left whitespace: + while (j >= i) { + if (!trimRight(j)) { + break; + } + j--; + } + + // should point right after the non-whitespace character: + j++; + } else { + j = length; + } + + leftIndex = i; + rightIndex = j; + + Debug.Assert(leftIndex >= 0 && leftIndex <= length); + Debug.Assert(rightIndex >= 0 && rightIndex <= length); + } + + #endregion + + + #region squeeze + + [RubyMethod("squeeze")] + public static MutableString/*!*/ Squeeze(RubyContext/*!*/ context, MutableString/*!*/ self, [NotNull]params object[] args) { + MutableString result = self.Clone(); + SqueezeMutableString(result, Protocols.CastToStrings(context, args)); + return result; + } + + [RubyMethod("squeeze!")] + public static MutableString/*!*/ SqueezeInPlace(RubyContext/*!*/ context, MutableString/*!*/ self, [NotNull]params object[] args) { + return SqueezeMutableString(self, Protocols.CastToStrings(context, args)); + } + + private static MutableString SqueezeMutableString(MutableString/*!*/ str, MutableString[]/*!*/ ranges) { + // if squeezeAll is true then there should be no ranges, and vice versa + Assert.NotNull(str, ranges); + + // convert the args into a map of characters to be squeezed (same algorithm as count) + BitArray map = null; + if (ranges.Length > 0) { + map = new RangeParser(ranges).Parse(); + } + + // Do the squeeze in place + int j = 1, k = 1; + while (j < str.Length) { + if (str.GetChar(j) == str.GetChar(j-1) && (ranges.Length == 0 || map.Get(str.GetChar(j)))) { + j++; + } else { + str.SetChar(k, str.GetChar(j)); + j++; k++; + } + } + if (j > k) { + str.Remove(k, j - k); + } + + // if not modified return null + return j == k ? null : str; + } + + #endregion + + + #region to_i, hex, oct + + private static string/*!*/ StripWhitespace(MutableString/*!*/ str) { + int i = 0; + while (i < str.Length) { + char c = str.GetChar(i); + if (c == ' ' || c == '_' || c == '\t' || c == '\n' || c == '\r') { + i += 1; + } else { + return str.GetSlice(i).ConvertToString().Replace("_", ""); + } + } + return str.ConvertToString().Replace("_", ""); + } + + private static string/*!*/ ParseSign(string/*!*/ number, ref bool isNegative) { + if (number[0] == '-') { + isNegative = true; + number = number.Remove(0, 1); + } + return number; + } + + // This method only gets called if we are explicitly specifying base via argument to to_i + private static object ParseInteger(RubyContext/*!*/ context, string/*!*/ str, int @base) { + if (@base == 0) { + return ParseInteger(context, str); + } + + if (str.Length == 0) { + return ScriptingRuntimeHelpers.Int32ToObject(0); + } + + bool isNegative = false; + str = ParseSign(str, ref isNegative); + + Tokenizer.BignumParser parser = new Tokenizer.BignumParser(); + parser.Position = 0; + parser.Buffer = str.ToCharArray(); + BigInteger result = parser.Parse(str.Length, @base); + int intValue; + if (result.AsInt32(out intValue)) + return ScriptingRuntimeHelpers.Int32ToObject(isNegative ? -intValue : intValue); + else + return isNegative ? BigInteger.Negate(result) : result; + } + + // This method uses the tokenizer to auto-detect the base type -- happens when agument to to_i is 0 + private static object ParseInteger(RubyContext/*!*/ context, string/*!*/ str) { + bool isNegative = false; + if (str.Length == 0) { + return ScriptingRuntimeHelpers.Int32ToObject(0); + } + + str = ParseSign(str, ref isNegative); + + Tokenizer tokenizer = new Tokenizer(false, ErrorSink.Null); + tokenizer.Initialize(context.CreateSnippet(str, SourceCodeKind.File)); + tokenizer.GetNextToken(); + + TokenValue value = tokenizer.TokenValue; + TokenValueType type = value.Type; + if (type == TokenValueType.Integer) + return ScriptingRuntimeHelpers.Int32ToObject(isNegative ? -value.Integer: value.Integer); + else if (type == TokenValueType.BigInteger) + return isNegative ? BigInteger.Negate(value.BigInteger) : value.BigInteger; + else + return ScriptingRuntimeHelpers.Int32ToObject(0); + } + + [RubyMethod("to_i")] + public static object ToInteger(RubyContext/*!*/ context, MutableString/*!*/ self) { + return ParseInteger(context, StripWhitespace(self)); + } + + [RubyMethod("to_i")] + public static object ToInteger(RubyContext/*!*/ context, MutableString/*!*/ self, [DefaultProtocol]int @base) { + if (@base == 1 || @base < 0 || @base > 36) { + throw RubyExceptions.CreateArgumentError(String.Format("illegal radix {0}", @base)); + } + return ParseInteger(context, StripWhitespace(self), @base); + } + + [RubyMethod("hex")] + public static object ToIntegerHex(RubyContext/*!*/ context, MutableString/*!*/ self) { + return ParseInteger(context, StripWhitespace(self), 16); + } + + [RubyMethod("oct")] + public static object ToIntegerOctal(RubyContext/*!*/ context, MutableString/*!*/ self) { + return ParseInteger(context, StripWhitespace(self), 8); + } + + #endregion + + #region to_f, to_s, to_str, to_clr_string, to_sym, intern + + [RubyMethod("to_f")] + public static double ToDouble(MutableString/*!*/ self) { + return Tokenizer.ParseDouble(self.ConvertToString().ToCharArray()); + } + + [RubyMethod("to_s")] + [RubyMethod("to_str")] + public static MutableString/*!*/ ToS(MutableString/*!*/ self) { + return self.GetType() == typeof(MutableString) ? self : MutableString.Create(self).TaintBy(self); + } + + [RubyMethod("to_clr_string")] + public static string/*!*/ ToClrString(MutableString/*!*/ str) { + return str.ConvertToString(); + } + + + [RubyMethod("to_sym")] + [RubyMethod("intern")] + public static SymbolId ToSym(MutableString/*!*/ self) { + if (self.IsEmpty) { + throw RubyExceptions.CreateArgumentError("interning empty string"); + } + + string str = self.ConvertToString(); + + // Cannot convert a string that contains null to a symbol + if (str.IndexOf('\0') >= 0) { + throw RubyExceptions.CreateArgumentError("symbol string may not contain '\0'"); + } + + return SymbolTable.StringToId(str); + } + + #endregion + + + #region upto + + [RubyMethod("upto")] + public static object UpTo(RubyContext/*!*/ context, BlockParam block, MutableString/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ endString) { + RangeOps.Each(context, block, new Range(context, self, endString, false)); + return self; + } + + #endregion + + + #region replace, reverse + + [RubyMethod("replace")] + public static MutableString/*!*/ Replace(MutableString/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ other) { + // Handle case where objects are the same identity + if (ReferenceEquals(self, other)) { + return self; + } + + self.Clear(); + self.Append(other); + return self.TaintBy(other); + } + + [RubyMethod("reverse")] + public static MutableString/*!*/ GetReversed(MutableString/*!*/ self) { + return self.Clone().Reverse(); + } + + [RubyMethod("reverse!")] + public static MutableString/*!*/ Reverse(MutableString/*!*/ self) { + return self.Reverse(); + } + + #endregion + + + #region tr, tr_s + + private static MutableString/*!*/ TrInternal(MutableString/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ from, + [DefaultProtocol, NotNull]MutableString/*!*/ to, bool squeeze) { + + MutableString result = self.CreateInstance().TaintBy(self); + IntervalParser parser = new IntervalParser(from); + + // TODO: a single pass to generate both? + MutableString source = parser.ParseSequence(); + BitArray bitmap = parser.Parse(); + + MutableString dest = new IntervalParser(to).ParseSequence(); + + int lastChar = dest.GetLastChar(); + char? lastTranslated = null; + for (int i = 0; i < self.Length; i++) { + char c = self.GetChar(i); + if (bitmap.Get(c)) { + char? thisTranslated = null; + int index = source.IndexOf(c); + if (index >= dest.Length) { + if (lastChar != -1) { + thisTranslated = (char)lastChar; + } + } else { + thisTranslated = dest.GetChar(index); + } + if (thisTranslated != null && (!squeeze || lastTranslated == null || lastTranslated.Value != thisTranslated)) { + result.Append(thisTranslated.Value); + } + lastTranslated = thisTranslated; + } else { + result.Append(c); + lastTranslated = null; + } + } + + return result; + } + + [RubyMethod("tr")] + public static MutableString/*!*/ Tr(MutableString/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ from, [DefaultProtocol, NotNull]MutableString/*!*/ to) { + + return TrInternal(self, from, to, false); + } + + [RubyMethod("tr!")] + public static MutableString/*!*/ TrInPlace(MutableString/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ from, [DefaultProtocol, NotNull]MutableString/*!*/ to) { + + MutableString result = TrInternal(self, from, to, false); + if (self.Equals(result)) { + return null; + } + + self.Clear(); + self.Append(result); + return self; + } + + [RubyMethod("tr_s")] + public static MutableString/*!*/ TrSqueeze(MutableString/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ from, [DefaultProtocol, NotNull]MutableString/*!*/ to) { + + return TrInternal(self, from, to, true); + } + + [RubyMethod("tr_s!")] + public static MutableString/*!*/ TrSqueezeInPlace(MutableString/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ from, [DefaultProtocol, NotNull]MutableString/*!*/ to) { + + MutableString result = TrInternal(self, from, to, true); + if (self.Equals(result)) { + return null; + } + + self.Clear(); + self.Append(result); + return self; + } + + #endregion + + + #region ljust + + [RubyMethod("ljust")] + public static MutableString/*!*/ LeftJustify(MutableString/*!*/ self, [DefaultProtocol]int width) { + // TODO: is this correct? Is it just a space or is this some configurable whitespace thing? + return LeftJustify(self, width, _DefaultPadding); + } + + [RubyMethod("ljust")] + public static MutableString/*!*/ LeftJustify(MutableString/*!*/ self, + [DefaultProtocol]int width, [DefaultProtocol, NotNull]MutableString/*!*/ padding) { + + if (padding.Length == 0) { + throw RubyExceptions.CreateArgumentError("zero width padding"); + } + + int count = width - self.Length; + if (count <= 0) { + return self; + } + + int iterations = count / padding.Length; + int remainder = count % padding.Length; + MutableString result = self.Clone().TaintBy(padding); + + for (int i = 0; i < iterations; i++) { + result.Append(padding); + } + + result.Append(padding, 0, remainder); + + return result; + } + + #endregion + + #region rjust + + [RubyMethod("rjust")] + public static MutableString/*!*/ RightJustify(MutableString/*!*/ self, [DefaultProtocol]int width) { + // TODO: is this correct? Is it just a space or is this some configurable whitespace thing? + return RightJustify(self, width, _DefaultPadding); + } + + [RubyMethod("rjust")] + public static MutableString/*!*/ RightJustify(MutableString/*!*/ self, + [DefaultProtocol]int width, [DefaultProtocol, NotNull]MutableString/*!*/ padding) { + + if (padding.Length == 0) { + throw RubyExceptions.CreateArgumentError("zero width padding"); + } + + int count = width - self.Length; + if (count <= 0) { + return self; + } + + int iterations = count / padding.Length; + int remainder = count % padding.Length; + MutableString result = self.CreateInstance().TaintBy(self).TaintBy(padding); + + for (int i = 0; i < iterations; i++) { + result.Append(padding); + } + + result.Append(padding.GetSlice(0, remainder)); + result.Append(self); + + return result; + } + + #endregion + + + #region unpack + + private static bool HasCapacity(Stream/*!*/ s, int? n) { + if (s.Length < (s.Position + n)) { + s.Position = s.Length; + return false; + } else { + return true; + } + } + + private static int CalculateCounts(Stream/*!*/ s, int? count, int size, out int leftover) { + int remaining = (int)(s.Length - s.Position); + int maxCount = remaining / size; + if (!count.HasValue) { + leftover = 0; + return maxCount; + } else if (count.Value <= maxCount) { + leftover = 0; + return count.Value; + } else { + leftover = count.Value - maxCount; + return maxCount; + } + } + + [RubyMethod("unpack")] + public static RubyArray/*!*/ Unpack(RubyContext/*!*/ context, MutableString/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ format) { + RubyArray result = new RubyArray(1 + self.Length / 2); + using (MutableStringStream stream = new MutableStringStream(self)) { + BinaryReader reader = new BinaryReader(stream); + foreach (ArrayOps.FormatDirective directive in ArrayOps.FormatDirective.Enumerate(format.ToString())) { + int count, maxCount; + byte[] buffer; + MutableString str; + int nilCount = 0; + switch (directive.Directive) { + case '@': + stream.Position = directive.Count.HasValue ? directive.Count.Value : stream.Position; + break; + + case 'A': + case 'a': + maxCount = (int)(stream.Length - stream.Position); + count = directive.Count.HasValue ? directive.Count.Value : maxCount; + if (count > maxCount) { + count = maxCount; + } + buffer = reader.ReadBytes(count); + str = MutableString.CreateBinary(buffer); + if (directive.Directive == 'A') { + // TODO: efficiency? + for (int pos = count - 1; pos >= 0; pos--) { + if (buffer[pos] != 0 && buffer[pos] != 0x20) { + break; + } + str.Remove(pos, 1); + } + } + result.Add(str); + break; + + case 'Z': + maxCount = (int)(stream.Length - stream.Position); + count = directive.Count.HasValue ? directive.Count.Value : maxCount; + if (count > maxCount) { + count = maxCount; + } + buffer = reader.ReadBytes(count); + str = MutableString.CreateBinary(buffer); + for (int pos = 0; pos < count; pos++) { + if (buffer[pos] == 0) { + str.Remove(pos, count - pos); + break; + } + } + result.Add(str); + break; + + case 'c': + count = CalculateCounts(stream, directive.Count, sizeof(sbyte), out nilCount); + for (int j = 0; j < count; j++) { + result.Add((int)reader.ReadSByte()); + } + break; + + case 'C': + count = CalculateCounts(stream, directive.Count, sizeof(byte), out nilCount); + for (int j = 0; j < count; j++) { + result.Add((int)reader.ReadByte()); + } + break; + + case 'i': + case 'l': + count = CalculateCounts(stream, directive.Count, sizeof(int), out nilCount); + for (int j = 0; j < count; j++) { + result.Add((int)reader.ReadInt32()); + } + break; + + case 'I': + case 'L': + count = CalculateCounts(stream, directive.Count, sizeof(uint), out nilCount); + for (int j = 0; j < count; j++) { + unchecked { + result.Add((int)reader.ReadUInt32()); + } + } + break; + + case 'm': + // TODO: Recognize "==" as end of base 64 encoding + int len = (int)(stream.Length - stream.Position); + char[] base64 = reader.ReadChars(len); + byte[] data = Convert.FromBase64CharArray(base64, 0, len); + result.Add(MutableString.CreateBinary(data)); + break; + + case 's': + count = CalculateCounts(stream, directive.Count, sizeof(short), out nilCount); + for (int j = 0; j < count; j++) { + result.Add((int)reader.ReadInt16()); + } + break; + + case 'S': + count = CalculateCounts(stream, directive.Count, sizeof(ushort), out nilCount); + for (int j = 0; j < count; j++) { + result.Add((int)reader.ReadUInt16()); + } + break; + + case 'U': + maxCount = (int)(stream.Length - stream.Position); + count = directive.Count.HasValue ? directive.Count.Value : maxCount; + int readCount = directive.Count.HasValue ? Encoding.UTF8.GetMaxByteCount(count) : count; + if (readCount > maxCount) { + readCount = maxCount; + } + long startPosition = stream.Position; + char[] charData = Encoding.UTF8.GetChars(reader.ReadBytes(readCount)); + if (charData.Length > count) { + int actualCount = Encoding.UTF8.GetByteCount(charData, 0, count); + stream.Position = startPosition + actualCount; + } else if (charData.Length < count) { + count = charData.Length; + } + for (int j = 0; j < count; j++) { + result.Add((int)charData[j]); + } + break; + + case 'X': + int len3 = directive.Count.HasValue ? directive.Count.Value : 0; + if (len3 > stream.Position) { + throw RubyExceptions.CreateArgumentError("X outside of string"); + } + stream.Position -= len3; + break; + + case 'x': + int len4 = directive.Count.HasValue ? directive.Count.Value : 0; + stream.Position += len4; + break; + + case 'h': + case 'H': + maxCount = (int)(stream.Length - stream.Position) * 2; + result.Add(ToHex(reader, Math.Min(directive.Count ?? maxCount, maxCount), directive.Directive == 'h')); + break; + + default: + throw RubyExceptions.CreateArgumentError( + String.Format("Unknown format directive '{0}'", directive.Directive)); + } + for (int i = 0; i < nilCount; i++) { + result.Add(null); + } + } + } + return result; + } + + private static MutableString/*!*/ ToHex(BinaryReader/*!*/ reader, int nibbleCount, bool swap) { + int wholeChars = nibbleCount / 2; + MutableString hex = MutableString.CreateMutable(nibbleCount); + + for (int i = 0; i < wholeChars; i++) { + byte b = reader.ReadByte(); + char loNibble = ToLowerHexDigit(b & 0x0F); + char hiNibble = ToLowerHexDigit((b & 0xF0) >> 4); + + if (swap) { + hex.Append(loNibble); + hex.Append(hiNibble); + } else { + hex.Append(hiNibble); + hex.Append(loNibble); + } + } + + // the last nibble: + if ((nibbleCount & 1) != 0) { + int b = reader.ReadByte(); + if (swap) { + hex.Append(ToLowerHexDigit(b & 0x0F)); + } else { + hex.Append(ToLowerHexDigit((b & 0xF0) >> 4)); + } + } + + return hex; + } + + private static char ToLowerHexDigit(int digit) { + return (char)((digit < 10) ? '0' + digit : 'a' + digit - 10); + } + + #endregion + + #region sum + + [RubyMethod("sum")] + public static object GetChecksum(MutableString/*!*/ self, [DefaultProtocol, DefaultParameterValue(16)]int bitCount) { + int length = self.GetByteCount(); + uint mask = (bitCount > 31) ? 0xffffffff : (1U << bitCount) - 1; + uint sum = 0; + for (int i = 0; i < length; i++) { + byte b = self.GetByte(i); + try { + checked { sum = (sum + b) & mask; } + } catch (OverflowException) { + return GetBigChecksum(self, i, sum, bitCount); + } + } + + return (sum > Int32.MaxValue) ? (BigInteger)sum : (object)(int)sum; + } + + private static BigInteger GetBigChecksum(MutableString/*!*/ self, int start, BigInteger/*!*/ sum, int bitCount) { + BigInteger mask = (((BigInteger)1) << bitCount) - 1; + + int length = self.GetByteCount(); + for (int i = start; i < length; i++) { + sum = (sum + self.GetByte(i)) & mask; + } + return sum; + } + + #endregion + + private static void RequireNoVersionChange(uint previousVersion, MutableString self) { + if (previousVersion != self.Version) { + throw new RuntimeError("string modified"); + } + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/NilClassOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/NilClassOps.cs new file mode 100644 index 0000000000..f18c38f9c6 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/NilClassOps.cs @@ -0,0 +1,101 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Dynamic; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + [RubyClass("NilClass", Extends = typeof(Null))] + public static class NilClassOps { + #region Public Instance Methods + + [RubyMethodAttribute("&")] + public static bool And(object self, object obj) { + Debug.Assert(self == null); + return false; + } + + [RubyMethodAttribute("^")] + public static bool Xor(object self, object obj) { + Debug.Assert(self == null); + return obj != null; + } + + [RubyMethodAttribute("^")] + public static bool Xor(object self, bool obj) { + Debug.Assert(self == null); + return obj; + } + + [RubyMethodAttribute("|")] + public static bool Or(object self, object obj) { + Debug.Assert(self == null); + return obj != null; + } + + [RubyMethodAttribute("|")] + public static bool Or(object self, bool obj) { + Debug.Assert(self == null); + return obj; + } + + [RubyMethodAttribute("nil?")] + public static bool IsNil(object self) { + Debug.Assert(self == null); + return true; + } + + [RubyMethodAttribute("to_a")] + public static RubyArray/*!*/ ToArray(object self) { + Debug.Assert(self == null); + return new RubyArray(); + } + + [RubyMethodAttribute("to_f")] + public static double ToDouble(object self) { + Debug.Assert(self == null); + return 0.0; + } + + [RubyMethodAttribute("to_i")] + public static int ToInteger(object self) { + Debug.Assert(self == null); + return 0; + } + + [RubyMethodAttribute("inspect")] + public static MutableString Inspect(object self) { + return MutableString.Create("nil"); + } + + [RubyMethodAttribute("to_s")] + public static MutableString/*!*/ ToString(object self) { + Debug.Assert(self == null); + return MutableString.Create(String.Empty); + } + + [SpecialName] + public static bool op_Implicit(Null self) { + Debug.Assert(self == null); + return false; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Numeric.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Numeric.cs new file mode 100644 index 0000000000..a93f066304 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Numeric.cs @@ -0,0 +1,452 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Actions; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + [RubyClass("Numeric"), Includes(typeof(Comparable))] + public abstract class Numeric : Object { + + public Numeric() { } + + #region +@, -@ + + /// + /// Unary plus - returns the receivers value + /// + [RubyMethod("+@")] + public static object UnaryPlus(object self) { + return self; + } + + /// + /// Unary minus - returns the receivers value, negated. + /// + /// + /// Equivalent to: + /// + /// c = 0.coerce(self); + /// c[0] - c[1] + /// + /// + [RubyMethod("-@")] + public static object UnaryMinus(RubyContext/*!*/ context, object self) { + return Protocols.CoerceAndCall(context, 0, self, LibrarySites.Minus); + } + + #endregion + + #region <=> + /// + /// Returns zero if self equals other (and is same type), nil otherwise. + [RubyMethod("<=>")] + public static object Compare(object self, object other) { + if (self == other) { + return 0; + } + return null; + } + #endregion + + #region abs + + /// + /// Returns the absolute value of self + /// + /// + /// Dynamically invokes < operator on self and 0 + /// If this is true then invokes @- on self. + /// Otherwise just returns self + /// + [RubyMethod("abs")] + public static object Abs(RubyContext/*!*/ context, object self) { + if (RubyOps.IsTrue(LibrarySites.LessThan(context, self, 0))) { + return LibrarySites.UnaryMinus(context, self); + } + return self; + } + + #endregion + + #region ceil + + /// + /// Returns the smallest Integer greater than or equal to num. + /// + /// + /// This is achieved by converting self to a Float then directly calling Float#ceil. + /// + [RubyMethod("ceil")] + public static object Ceil(RubyContext/*!*/ context, object self) { + double floatSelf = Protocols.ConvertToFloat(context, self); + return FloatOps.Ceil(floatSelf); + } + + #endregion + + #region coerce + + /// + /// If other is the same type as self, returns an array [other, self]. + /// Otherwise, returns an array [floatOther, floatSelf], whose elements are other and self represented as Float objects. + /// + /// + /// This coercion mechanism is used by Ruby to handle mixed-type numeric operations: + /// It is intended to find a compatible common type between the two operands of the operator. + /// + [RubyMethod("coerce")] + public static RubyArray Coerce(RubyContext/*!*/ context, object self, object other) { + if (context.GetClassOf(self) == context.GetClassOf(other)) { + return RubyOps.MakeArray2(other, self); + } + return RubyOps.MakeArray2(Protocols.ConvertToFloat(context, other), Protocols.ConvertToFloat(context, self)); + } + + #endregion + + #region div + /// + /// Dynamically invokes / operator to perform division, then converts the result to an integer. + /// + /// + /// Numeric does not define the / operator; this is left to subclasses. + /// + [RubyMethod("div")] + public static object Div(RubyContext/*!*/ context, object self, object other) { + return Floor(context, LibrarySites.Divide(context, self, other)); + } + #endregion + + #region divmod + /// + /// Returns an array [quotient, modulus] obtained by dividing self by other. + /// The quotient is rounded toward -infinity. + /// If q, r = x.divmod(y), then + /// q = floor(float(x)/float(y)) + /// x = q*y + r + /// + /// + /// The quotient is found by directly calling Numeric#div + /// The modulus is found by dynamically invoking modulo method on self passing other. + /// + [RubyMethod("divmod")] + public static RubyArray DivMod(RubyContext/*!*/ context, object self, object other) { + object div = Div(context, self, other); + object mod = LibrarySites.Modulo(context, self, other); + return RubyOps.MakeArray2(div, mod); + } + #endregion + + #region eql? + + /// + /// Returns true if num and numeric are the same type and have equal values. + /// + /// + /// + /// + /// + [RubyMethod("eql?")] + public static bool Eql(RubyContext/*!*/ context, object self, object other) { + if (context.GetClassOf(self) != context.GetClassOf(other)) { + return false; + } + return Protocols.IsEqual(context, self, other); + } + + #endregion + + #region floor + /// + /// Returns the largest integer less than or equal to self. + /// + /// + /// This is achieved by converting self to a Float and directly calling Float#floor. + /// + [RubyMethod("floor")] + public static object Floor(RubyContext/*!*/ context, object self) { + return FloatOps.Floor(Protocols.ConvertToFloat(context, self)); + } + #endregion + + #region integer? + /// + /// Returns true if self is an Integer (i.e. Fixnum or Bignum). + /// + [RubyMethod("integer?")] + public static bool IsInteger(object self) { + return false; + } + #endregion + + #region modulo + /// + /// Equivalent to self.divmod(other)[1] + /// + /// + /// This is achieved by dynamically invoking % operator on self and other. + /// + [RubyMethod("modulo")] + public static object Modulo(RubyContext/*!*/ context, object self, object other) { + return LibrarySites.ModuloOp(context, self, other); + } + #endregion + + #region nonzero? + /// + /// Returns num if num is not zero, nil otherwise. + /// + /// + /// This behavior is useful when chaining comparisons: + /// a = %w( z Bb bB bb BB a aA Aa AA A ) + /// b = a.sort {|a,b| (a.downcase <=> b.downcase).nonzero? || a <=> b } + /// b #=> ["A", "a", "AA", "Aa", "aA", "BB", "Bb", "bB", "bb", "z"] + /// + /// + /// This is achieved by dynamically invoking IsZero on self; + /// returning nil if it is or self otherwise. + /// + [RubyMethod("nonzero?")] + public static object IsNonZero(RubyContext/*!*/ context, object self) { + if (LibrarySites.IsZero(context, self)) { + return null; + } else { + return self; + } + } + #endregion + + #region quo + /// + /// Equivalent to invoking Numeric#/; overridden in subclasses + /// + [RubyMethod("quo")] + public static object Quo(RubyContext/*!*/ context, object self, object other) { + return LibrarySites.Divide(context, self, other); + } + #endregion + + #region remainder + /// + /// If self and other have different signs, returns (self.modulo(other)-other; + /// otherwise, returns self.modulo(other). + /// + /// + /// This is achieved by dynamically invoking modulo on self; + /// then invoking < operator on the self and other against 0. + /// + [RubyMethod("remainder")] + public static object Remainder(RubyContext/*!*/ context, object self, object other) { + object modulo = LibrarySites.Modulo(context, self, other); + if (!Protocols.IsEqual(context, modulo, 0)) { + // modulo is not zero + if (RubyOps.IsTrue(LibrarySites.LessThan(context, self, 0)) && RubyOps.IsTrue(LibrarySites.GreaterThan(context, other, 0)) || + RubyOps.IsTrue(LibrarySites.GreaterThan(context, self, 0)) && RubyOps.IsTrue(LibrarySites.LessThan(context, other, 0))) { + // (self is negative and other is positive) OR (self is positive and other is negative) + return LibrarySites.Minus(context, modulo, other); + } + } + // Either modulo is zero or self and other are not of the same sign + return modulo; + } + #endregion + + #region round + /// + /// Rounds self to the nearest integer. + /// + /// + /// This is achieved by converting self to a Float and directly calling Float#round. + /// + [RubyMethod("round")] + public static object Round(RubyContext/*!*/ context, object self) { + double floatSelf = Protocols.ConvertToFloat(context, self); + return FloatOps.Round(floatSelf); + } + #endregion + + #region step + + /// + /// Invokes block with the sequence of numbers starting at self, incremented by step on each call. + /// The loop finishes when the value to be passed to the block is greater than limit (if step is positive) or less than limit (if step is negative). + /// + [RubyMethod("step")] + public static object Step(RubyContext/*!*/ context, BlockParam block, int self, int limit) { + return Step(context, block, self, limit, 1); + } + + /// + /// Invokes block with the sequence of numbers starting at self, incremented by step on each call. + /// The loop finishes when the value to be passed to the block is greater than limit (if step is positive) or less than limit (if step is negative). + /// + [RubyMethod("step")] + public static object Step(RubyContext/*!*/ context, BlockParam block, int self, int limit, int step) { + if (step == 0) { + throw RubyExceptions.CreateArgumentError("step can't be 0"); + } + if (step > 0) { + int current = self; + while (current <= limit) { + object result; + if (YieldStep(block, current, out result)) { + return result; + } + current += step; + } + } else { + int current = self; + while (current >= limit) { + object result; + if (YieldStep(block, current, out result)) { + return result; + } + current += step; + } + } + return self; + } + + /// + /// Invokes block with the sequence of numbers starting at self, incremented by step on each call. + /// The loop finishes when the value to be passed to the block is greater than limit (if step is positive) or less than limit (if step is negative). + /// + [RubyMethod("step")] + public static object Step(RubyContext/*!*/ context, BlockParam block, double self, double limit, double step) { + if (step == 0) { + throw RubyExceptions.CreateArgumentError("step can't be 0"); + } + double n = (limit - self) / step; + int count = ((int)System.Math.Floor(n + n * Double.Epsilon)) + 1; + double current = self; + while (count > 0) { + object result; + if (YieldStep(block, current, out result)) { + return result; + } + current += step; + count--; + } + return self; + } + + /// + /// Invokes block with the sequence of numbers starting at self, incremented by step on each call. + /// The loop finishes when the value to be passed to the block is greater than limit (if step is positive) or less than limit (if step is negative). + /// + [RubyMethod("step")] + public static object Step(RubyContext/*!*/ context, BlockParam block, object self, object limit) { + return Step(context, block, self, limit, 1); + } + + /// + /// Invokes block with the sequence of numbers starting at self, incremented by step on each call. + /// The loop finishes when the value to be passed to the block is greater than limit (if step is positive) or less than limit (if step is negative). + /// + [RubyMethod("step")] + public static object Step(RubyContext/*!*/ context, BlockParam block, object self, object limit, object step) { + if (self is double || limit is double || step is double) { + // At least one of the arguments is double so convert all to double and run the Float version of Step + double floatSelf = Protocols.ConvertToFloat(context, self); + double floatLimit = Protocols.ConvertToFloat(context, limit); + double floatStep = Protocols.ConvertToFloat(context, step); + return Step(context, block, floatSelf, floatLimit, floatSelf); + } else { + #region The generic step algorithm: + // current = self + // if step is postive then + // while current < limit do + // yield(current) + // current = current + step + // end + // else + // while current > limit do + // yield(current) + // current = current + step + // end + // return self + #endregion + bool isStepZero = Protocols.IsEqual(context, step, 0); + if (isStepZero) { + throw RubyExceptions.CreateArgumentError("step can't be 0"); + } + bool isStepPositive = RubyOps.IsTrue(LibrarySites.GreaterThan(context, step, 0)); + Protocols.DynamicInvocation compare; + if (isStepPositive) { + compare = LibrarySites.GreaterThan; + } else { + compare = LibrarySites.LessThan; + } + object current = self; + while (!RubyOps.IsTrue(compare.Invoke(context, current, limit))) { + object result; + if (YieldStep(block, current, out result)) { + return result; + } + current = LibrarySites.Add(context, current, step); + } + return self; + } + } + + private static bool YieldStep(BlockParam block, object current, out object result) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + return block.Yield(current, out result); + } + + #endregion + + #region to_int + /// + /// Invokes the self.to_i method to convert self to an integer. + /// + [RubyMethod("to_int")] + public static object ToInt(RubyContext/*!*/ context, object self) { + return RubySites.ToI(context, self); + } + #endregion + + #region truncate + /// + /// Returns self truncated to an integer. + /// + /// + /// This is achieved by converting self to a float and directly calling Float#truncate. + /// + [RubyMethod("truncate")] + public static object Truncate(RubyContext/*!*/ context, object self) { + double floatSelf = Protocols.ConvertToFloat(context, self); + return FloatOps.ToInt(floatSelf); + } + #endregion + + #region zero? + /// + /// Returns true if self has a zero value. + /// + [RubyMethod("zero?")] + public static bool IsZero(RubyContext/*!*/ context, object self) { + return Protocols.IsEqual(context, self, 0); + } + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ObjectOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ObjectOps.cs new file mode 100644 index 0000000000..e988fbc36c --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ObjectOps.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + [RubyClass("Object", Extends = typeof(object)), Includes(typeof(Kernel))] + public static class ObjectOps { + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance | RubyMethodAttributes.Empty)] + public static void Reinitialize(object self) { + // nop + } + + [RubyConstant] + public readonly static bool TRUE = true; + + [RubyConstant] + public readonly static bool FALSE = false; + + [RubyConstant] + public readonly static object NIL = null; + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ObjectSpace.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ObjectSpace.cs new file mode 100644 index 0000000000..e053f9e613 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ObjectSpace.cs @@ -0,0 +1,80 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Actions; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + [RubyModule("ObjectSpace")] + public static class ObjectSpace { + [RubyMethod("define_finalizer", RubyMethodAttributes.PublicSingleton)] + public static object DefineFinalizer(RubyModule/*!*/ self, object obj, Proc proc) { + RubyArray result = new RubyArray(2); + result.Add(0); + result.Add(proc); + return result; + } + + [RubyMethod("each_object", RubyMethodAttributes.PublicSingleton)] + public static object EachObject(BlockParam block, RubyModule/*!*/ self, [NotNull]RubyClass/*!*/ theClass) { + Type classType = theClass.GetType(); + bool isClass = (classType == typeof(RubyClass)); + if (!isClass && classType != typeof(RubyModule)) { + throw new NotSupportedException("each_object only supported for objects of type Class or Module"); + } + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + Dictionary visited = new Dictionary(); + Stack modules = new Stack(); + modules.Push(theClass.Context.ObjectClass); + while (modules.Count > 0) { + RubyModule next = modules.Pop(); + RubyClass asClass = next as RubyClass; + + if (!isClass || asClass != null) { + object result; + if (block.Yield(next, out result)) { + return result; + } + } + next.EnumerateConstants(delegate(RubyModule module, string name, object value) { + RubyModule constAsModule = (value as RubyModule); + if (constAsModule != null && !visited.ContainsKey(constAsModule)) { + modules.Push(constAsModule); + visited[module] = null; + } + return false; + }); + } + return visited.Count; + } + + [RubyMethod("garbage_collect", RubyMethodAttributes.PublicSingleton)] + public static void GarbageCollect(RubyModule/*!*/ self) { + GC.Collect(); + } + + [RubyMethod("undefine_finalizer", RubyMethodAttributes.PublicSingleton)] + public static object DefineFinalizer(RubyModule/*!*/ self, object obj) { + return obj; + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Precision.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Precision.cs new file mode 100644 index 0000000000..efe3b7b8ff --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Precision.cs @@ -0,0 +1,82 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + [RubyModule("Precision")] + public class Precision { + #region prec, prec_i, prec_f + + /// + /// Converts self into an instance of klass. + /// + /// + /// By default, prec invokes klass.induced_from(self) and returns its value. + /// So, if klass.induced_from doesn't return an instance of klass, it will be necessary to reimplement prec. + /// + [RubyMethod("prec")] + public static object Prec(object self, [NotNull]RubyClass/*!*/ klass) { + return LibrarySites.InvokeInducedFrom(klass.Context, klass, self); + } + + /// + /// Returns an Integer converted from self. It is equivalent to prec(Integer). + /// + [RubyMethod("prec_i")] + public static object PrecInteger(RubyContext/*!*/ context, object self) { + return LibrarySites.InvokePrec(context, context.GetClass(typeof(Integer)), self); + } + + /// + /// Returns a Float converted from self. It is equivalent to prec(Float). + /// + [RubyMethod("prec_f")] + public static object PrecFloat(RubyContext/*!*/ context, object self) { + return LibrarySites.InvokePrec(context, context.GetClass(typeof(double)), self); + } + + #endregion + + #region included + + /// + /// When the Precision module is mixed-in to a class, via the Module#include method, this included method is called. + /// Here it is used to add our default induced_from implementation to the host class. + /// + /// + /// The module being mixed in. + /// The host class including the module + [RubyMethod("included", RubyMethodAttributes.PublicSingleton)] + public static object Included(RubyContext/*!*/ context, RubyModule/*!*/ self, RubyModule/*!*/ includedIn) { + includedIn.SingletonClass.DefineLibraryMethod("induced_from", (int)RubyMethodAttributes.PublicSingleton, new Func(InducedFrom)); + return self; + } + + private static readonly SymbolId inducedFromSymbol = SymbolTable.StringToId("induced_from"); + + private static object InducedFrom(RubyModule/*!*/ rubyClass, object other) { + throw RubyExceptions.CreateTypeError(String.Format("undefined conversion from {0} into {1}", + rubyClass.Context.GetClassOf(other).Name, rubyClass.Name)); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ProcOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ProcOps.cs new file mode 100644 index 0000000000..30b422d872 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ProcOps.cs @@ -0,0 +1,183 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Builtins { + + [RubyClass("Proc", Extends = typeof(Proc), Inherits = typeof(Object))] + public static class ProcOps { + + [RubyConstructor] + public static void Error(RubyClass/*!*/ self, params object[] args) { + throw RubyExceptions.CreateTypeError(String.Format("allocator undefined for {0}", self.Name)); + } + + #region Public Instance Methods + + [RubyMethod("==", RubyMethodAttributes.PublicInstance)] + public static bool Equal(Proc/*!*/ self, [NotNull]Proc/*!*/ other) { + return self.Dispatcher == other.Dispatcher; + } + + [RubyMethod("==", RubyMethodAttributes.PublicInstance)] + public static bool Equal(Proc/*!*/ self, object other) { + return false; + } + + [RubyMethod("arity", RubyMethodAttributes.PublicInstance)] + public static int GetArity(Proc/*!*/ self) { + return self.Dispatcher.Arity; + } + + [RubyMethod("binding", RubyMethodAttributes.PublicInstance)] + public static Binding/*!*/ GetLocalScope(Proc/*!*/ self) { + return new Binding(self.LocalScope); + } + + [RubyMethod("dup", RubyMethodAttributes.PublicInstance)] + [RubyMethod("clone", RubyMethodAttributes.PublicInstance)] + public static Proc/*!*/ Clone(Proc/*!*/ self) { + return self.Copy(); + } + + [RubyMethod("to_proc", RubyMethodAttributes.PublicInstance)] + public static Proc/*!*/ ToProc(Proc/*!*/ self) { + return self; + } + + // to_s + + #endregion + + #region call, [] + + [RubyMethod("[]"), RubyMethod("call")] + public static object Call(Proc/*!*/ self) { + RequireParameterCount(self, 0); + return self.Call(); + } + + [RubyMethod("[]"), RubyMethod("call")] + public static object Call(Proc/*!*/ self, object arg1) { + RequireParameterCount(self, 1); + return self.Call(arg1); + } + + [RubyMethod("[]"), RubyMethod("call")] + public static object Call(Proc/*!*/ self, object arg1, object arg2) { + RequireParameterCount(self, 2); + return self.Call(arg1, arg2); + } + + [RubyMethod("[]"), RubyMethod("call")] + public static object Call(Proc/*!*/ self, object arg1, object arg2, object arg3) { + RequireParameterCount(self, 3); + return self.Call(arg1, arg2, arg3); + } + + [RubyMethod("[]"), RubyMethod("call")] + public static object Call(Proc/*!*/ self, object arg1, object arg2, object arg3, object arg4) { + RequireParameterCount(self, 4); + return self.Call(arg1, arg2, arg3, arg4); + } + + [RubyMethod("[]"), RubyMethod("call")] + public static object Call(Proc/*!*/ self, [NotNull]params object[]/*!*/ args) { + RequireParameterCount(self, args.Length); + return self.CallN(args); + } + + private static void RequireParameterCount(Proc/*!*/ proc, int argCount) { + int arity; + if (proc.Kind == ProcKind.Lambda && argCount != (arity = proc.Dispatcher.Arity)) { + if (arity >= 0) { + // arity 1 -> warning reported by block dispatcher + if (arity != 1) { + throw RubyOps.MakeWrongNumberOfArgumentsError(argCount, arity); + } + } else if (argCount < -arity - 1) { + throw RubyOps.MakeWrongNumberOfArgumentsError(argCount, -arity - 1); + } + } + } + + #endregion + + #region Singleton Methods + + private static readonly CallSite>/*!*/ _InitializeSite = + CallSite>.Create( + RubySites.InstanceCallAction("initialize", new RubyCallSignature(0, RubyCallFlags.HasImplicitSelf | RubyCallFlags.HasBlock)) + ); + + [RubyMethod("new", RubyMethodAttributes.PublicSingleton)] + public static Proc/*!*/ CreateNew(RubyScope/*!*/ scope, RubyClass/*!*/ self) { + RubyMethodScope methodScope = scope.GetInnerMostMethodScope(); + if (methodScope == null || methodScope.BlockParameter == null) { + throw RubyExceptions.CreateArgumentError("tried to create Proc object without a block"); + } + + return CreateNew(self, methodScope.BlockParameter); + } + + [RubyMethod("new", RubyMethodAttributes.PublicSingleton)] + public static Proc/*!*/ CreateNew(BlockParam/*!*/ block, RubyClass/*!*/ self) { + if (block == null) { + throw RubyExceptions.CreateArgumentError("tried to create Proc object without a block"); + } + + return CreateNew(self, block.Proc); + } + + public static Proc/*!*/ CreateNew(RubyClass/*!*/ self, Proc/*!*/ proc) { + Assert.NotNull(self, proc); + + // an instance of Proc class, the identity is preserved: + if (self.GetUnderlyingSystemType() == typeof(Proc)) { + return proc; + } + + // an instance of a Proc subclass: + var result = new Proc.Subclass(self, proc); + + // a call to the initializer with a block: + object initResult = null; + do { + // a new proc is created each iteration (even if a subclass is passed in, the Proc class is created): + var argProc = proc.Create(proc); + + try { + initResult = _InitializeSite.Target(_InitializeSite, self.Context, proc, argProc); + } catch (EvalUnwinder u) { + initResult = u.ReturnValue; + } + + Debug.Assert(proc != argProc, "retry doesn't propagate to the caller"); + } while (RubyOps.IsRetrySingleton(initResult)); + + return result; + } + + #endregion + } +} + diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RangeOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RangeOps.cs new file mode 100644 index 0000000000..df6d6a40fa --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RangeOps.cs @@ -0,0 +1,456 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Actions; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + /// + /// A Range represents an intervala set of values with a start and an end. + /// Ranges may be constructed using the s..e and se literals, or with Range::new. + /// Ranges constructed using .. run from the start to the end inclusively. + /// Those created using exclude the end value. + /// When used as an iterator, ranges return each value in the sequence. + /// + /// + /// (-1..-5).to_a #=> [] + /// (-5..-1).to_a #=> [-5, -4, -3, -2, -1] + /// ('a'..'e').to_a #=> ["a", "b", "c", "d", "e"] + /// ('a'...'e').to_a #=> ["a", "b", "c", "d"] + /// + /// + /// Ranges can be constructed using objects of any type, as long as the objects can be compared using their <=> operator + /// and they support the succ method to return the next object in sequence. + /// + [RubyClass("Range", Extends = typeof(Range), Inherits = typeof(Object)), Includes(typeof(Enumerable))] + public static class RangeOps { + + #region Construction and Initialization + + /// + /// Construct a new Range object. + /// + /// + /// An empty Range object + /// + /// + /// This constructor only creates an empty range object, + /// which will be initialized subsequently by a separate call through into one of the two initialize methods. + /// Literal Ranges (e.g. 1..5, 'a'...'b' are created by calls through to RubyOps.CreateInclusiveRange and + /// RubyOps.CreateExclusiveRange which bypass this constructor/initializer run about and initialize the object directly. + /// + [RubyConstructor] + public static Range/*!*/ CreateRange(RubyClass/*!*/ self, object begin, object end, [Optional]bool excludeEnd) { + return new Range(self.Context, begin, end, excludeEnd); + } + + // Reinitialization. Not called when a factory/non-default ctor is called. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static Range/*!*/ Reinitialize(RubyContext/*!*/ context, Range/*!*/ self, object begin, object end, [Optional]bool excludeEnd) { + self.Initialize(context, begin, end, excludeEnd); + return self; + } + + #endregion + + #region begin, first + /// + /// Returns the first object in self + /// + [RubyMethod("begin"), RubyMethod("first")] + public static object Begin([NotNull]Range/*!*/ self) { + return self.Begin; + } + #endregion + + #region end, last + /// + /// Returns the object that defines the end of self + /// + [RubyMethod("end"), RubyMethod("last")] + public static object End([NotNull]Range/*!*/ self) { + return self.End; + } + #endregion + + #region exclude_end? + /// + /// Returns true if self excludes its end value. + /// + [RubyMethod("exclude_end?")] + public static bool ExcludeEnd([NotNull]Range/*!*/ self) { + return self.ExcludeEnd; + } + #endregion + + #region inspect + + /// + /// Convert this range object to a printable form (using inspect to convert the start and end objects). + /// + [RubyMethod("inspect")] + public static MutableString/*!*/ Inspect(RubyContext/*!*/ context, Range/*!*/ self) { + MutableString str = RubySites.Inspect(context, self.Begin); + str.Append(self.ExcludeEnd ? "..." : ".."); + str.Append(RubySites.Inspect(context, self.End)); + return str; + } + + #endregion + + #region to_s + /// + /// Convert this range object to a printable form (using to_s to convert the start and end objects). + /// + [RubyMethod("to_s")] + public static MutableString/*!*/ ToS(RubyContext/*!*/ context, Range/*!*/ self) { + MutableString str = RubySites.ToS(context, self.Begin); + str.Append(self.ExcludeEnd ? "..." : ".."); + str.Append(RubySites.ToS(context, self.End)); + return str; + } + #endregion + + #region ==, eql? + /// + /// Is other equal to self? Here other is not a Range so returns false. + /// + [RubyMethod("=="), RubyMethod("eql?")] + public static bool Equals(Range/*!*/ self, object other) { + return false; + } + + /// + /// Returns true only if self is a Range, has equivalent beginning and end items (by comparing them with ==), + /// and has the same exclude_end? setting as other. + /// + /// + /// (0..2) == (0..2) #=> true + /// (0..2) == Range.new(0,2) #=> true + /// (0..2) == (0...2) #=> false + /// (0..2).eql?(0.0..2.0) #=> true + /// + [RubyMethod("==")] + public static bool Equals(RubyContext/*!*/ context, Range/*!*/ self, [NotNull]Range/*!*/ other) { + if (self == other) { + return true; + } + return (Protocols.IsEqual(context, self.Begin, other.Begin) + && Protocols.IsEqual(context, self.End, other.End) + && self.ExcludeEnd == other.ExcludeEnd); + } + /// + /// Returns true only if self is a Range, has equivalent beginning and end items (by comparing them with eql?), + /// and has the same exclude_end? setting as other. + /// + /// + /// (0..2).eql?(0..2) #=> true + /// (0..2).eql?(Range.new(0,2)) #=> true + /// (0..2).eql?(0...2) #=> false + /// (0..2).eql?(0.0..2.0) #=> false + /// + [RubyMethod("eql?")] + public static bool Eql(RubyContext/*!*/ context, Range/*!*/ self, [NotNull]Range/*!*/ other) { + if (self == other) { + return true; + } + return (LibrarySites.Eql(context, self.Begin, other.Begin) + && LibrarySites.Eql(context, self.End, other.End) + && self.ExcludeEnd == other.ExcludeEnd); + } + #endregion + + #region ===, member?, include? + /// + /// Returns true if other is an element of self, false otherwise. + /// Conveniently, === is the comparison operator used by case statements. + /// + /// + /// case 79 + /// when 1..50 then print "low\n" + /// when 51..75 then print "medium\n" + /// when 76..100 then print "high\n" + /// end + /// => "high" + /// + [RubyMethod("==="), RubyMethod("member?"), RubyMethod("include?")] + public static bool CaseEquals(RubyContext/*!*/ context, [NotNull]Range/*!*/ self, object other) { + // If compare returns nil then we just return false. + if (Protocols.TryCompare(context, self.Begin, other).GetValueOrDefault(-1) > 0 ) { + return false; + } + // Since we passed the first TryCompare without getting null then this Compare should not throw comparison error + int compareWithEnd = Protocols.Compare(context, other, self.End); + return compareWithEnd < 0 || (!self.ExcludeEnd && compareWithEnd == 0); + } + #endregion + + #region hash + /// + /// Generate a hash value such that two ranges with the same start and end points, + /// and the same value for the "exclude end" flag, generate the same hash value. + /// + [RubyMethod("hash")] + public static int GetHashCode(Range/*!*/ self) { + int hash = RubyUtils.GetHashCode(self.Begin); + hash ^= RubyUtils.GetHashCode(self.End); + hash ^= RubyUtils.GetHashCode(self.ExcludeEnd); + return hash; + } + #endregion + + #region each + + /// + /// Iterates over the elements of self, passing each in turn to the block. + /// You can only iterate if the start object of the range supports the succ method + /// (which means that you cant iterate over ranges of Float objects). + /// + [RubyMethod("each")] + public static object Each(RubyContext/*!*/ context, BlockParam block, Range/*!*/ self) { + // We check that self.begin responds to "succ" even though some of the implementations don't use it. + CheckBegin(context, self.Begin); + + if (self.Begin is int && self.End is int) { + return StepFixnum(context, block, self, (int)self.Begin, (int)self.End, 1); + } else if (self.Begin is MutableString) { + return StepString(context, block, self, (MutableString)self.Begin, (MutableString)self.End, 1); + } else { + return StepObject(context, block, self, self.Begin, self.End, 1); + } + } + + #endregion + + #region step + + /// + /// Iterates over self, passing each stepth (here defaulting to 1) element to the block. + /// If the range contains numbers or strings, natural ordering is used. + /// Otherwise step invokes succ to iterate through range elements. + /// + [RubyMethod("step")] + public static object Step(RubyContext/*!*/ context, BlockParam block, Range/*!*/ self) { + return Step(context, block, self, 1); + } + + /// + /// Iterates over self, passing each stepth element to the block. + /// If the range contains numbers or strings, natural ordering is used. + /// Otherwise step invokes succ to iterate through range elements. + /// + [RubyMethod("step")] + public static object Step(RubyContext/*!*/ context, BlockParam block, Range/*!*/ self, object step) { + // We attempt to cast step to Fixnum here even though if we were iterating over Floats, for instance, we use step as is. + // This prevents cases such as (1.0..2.0).step(0x800000000000000) {|x| x } from working but that is what MRI does. + int intStep = Protocols.CastToFixnum(context, step); + if (self.Begin is int && self.End is int) { + // self.begin is Fixnum; directly call item = item + 1 instead of succ + return StepFixnum(context, block, self, (int)self.Begin, (int)self.End, intStep); + } else if ( self.Begin is MutableString ) { + // self.begin is String; use item.succ and item <=> self.end but make sure you check the length of the strings + return StepString(context, block, self, (MutableString)self.Begin, (MutableString)self.End, intStep); + } else if (context.IsInstanceOf(self.Begin, context.GetClass(typeof(Numeric)))) { + // self.begin is Numeric; invoke item = item + 1 instead of succ and invoke < or <= for compare + return StepNumeric(context, block, self, self.Begin, self.End, step); + } else { + // self.begin is not Numeric or String; just invoke item.succ and item <=> self.end + CheckBegin(context, self.Begin); + return StepObject(context, block, self, self.Begin, self.End, intStep); + } + } + + #endregion + + #region Private Helper Stuff + + /// + /// Step through a Range of Fixnums. + /// + /// + /// This method is optimized for direct integer operations using < and + directly. + /// It is not used if either begin or end are outside Fixnum bounds. + /// + private static object StepFixnum(RubyContext/*!*/ context, BlockParam block, Range/*!*/ self, int begin, int end, int step) { + CheckStep(step); + + // throw only if there is at least one item that will be enumerated: + if (block == null && begin != end && !self.ExcludeEnd) { + throw RubyExceptions.NoBlockGiven(); + } + + object result; + int item = begin; + while (item < end) { + if (block.Yield(item, out result)) { + return result; + } + item += step; + } + + if (item == end && !self.ExcludeEnd) { + if (block.Yield(item, out result)) { + return result; + } + } + return self; + } + + /// + /// Step through a Range of Strings. + /// + /// + /// This method requires step to be a Fixnum. + /// It uses a hybrid string comparison to prevent infinite loops and calls String#succ to get each item in the range. + /// + private static object StepString(RubyContext/*!*/ context, BlockParam block, Range/*!*/ self, MutableString begin, MutableString end, int step) { + CheckStep(step); + object result; + MutableString item = begin; + int comp; + while ((comp = Protocols.Compare(context, item, end)) < 0) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + if (block.Yield(item, out result)) { + return result; + } + + for (int i = 0; i < step; ++i) { + item = (MutableString)RubySites.Successor(context, item); + } + + if (item.Length > end.Length) { + return self; + } + } + + if (comp == 0 && !self.ExcludeEnd) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + if (block.Yield(item, out result)) { + return result; + } + } + return self; + } + + /// + /// Step through a Range of Numerics. + /// + /// + /// + private static object StepNumeric(RubyContext/*!*/ context, BlockParam block, Range/*!*/ self, object begin, object end, object step) { + CheckStep(context, step); + + object item = begin; + Protocols.DynamicInvocation compareOp; + if (self.ExcludeEnd) { + compareOp = LibrarySites.LessThan; + } else { + compareOp = LibrarySites.LessThanOrEqual; + } + + object result; + while (RubyOps.IsTrue(compareOp(context, item, end))) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + if (block.Yield(item, out result)) { + return result; + } + + item = LibrarySites.Add(context, item, step); + } + + return self; + } + + /// + /// Step through a Range of objects that are not Numeric or String. + /// + private static object StepObject(RubyContext/*!*/ context, BlockParam block, Range/*!*/ self, object begin, object end, int step) { + CheckStep(context, step); + object item = begin, result; + int comp; + + while ((comp = Protocols.Compare(context, item, end)) < 0) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + if (block.Yield(item, out result)) { + return result; + } + + for (int i = 0; i < step; ++i) { + item = RubySites.Successor(context, item); + } + } + if (comp == 0 && !self.ExcludeEnd) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + if (block.Yield(item, out result)) { + return result; + } + } + return self; + } + + /// + /// Check that the int is not less than or equal to zero. + /// + private static void CheckStep(int step) { + if (step == 0) { + throw RubyExceptions.CreateArgumentError("step can't be 0"); + } + if (step < 0) { + throw RubyExceptions.CreateArgumentError("step can't be negative"); + } + } + + /// + /// Check that the object, when converted to an integer, is not less than or equal to zero. + /// + private static void CheckStep(RubyContext/*!*/ context, object step) { + if (RubySites.Equal(context, step, 0) ) { + throw RubyExceptions.CreateArgumentError("step can't be 0"); + } + if (RubyOps.IsTrue(LibrarySites.LessThan(context, step, 0)) ) { + throw RubyExceptions.CreateArgumentError("step can't be negative"); + } + } + + /// + /// Check that the object responds to "succ". + /// + private static void CheckBegin(RubyContext/*!*/ context, object begin) { + if (!RubySites.RespondTo(context, begin, "succ")) { + throw RubyExceptions.CreateTypeError(String.Format("can't iterate from {0}", RubyUtils.GetClassName(context, begin))); + } + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyEncodingOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyEncodingOps.cs new file mode 100644 index 0000000000..5b068d40d1 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyEncodingOps.cs @@ -0,0 +1,113 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ +#if !SILVERLIGHT + +using System; +using System.Collections.Generic; +using System.Text; +using IronRuby.Builtins; +using IronRuby.Runtime; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting; + +namespace IronRuby.Builtins { + [RubyClass("Encoding", Extends = typeof(RubyEncoding), Inherits = typeof(Object), BuildConfig = "!SILVERLIGHT")] + public static class RubyEncodingOps { + [RubyConstructor] + public static void Error(RubyClass/*!*/ self) { + throw RubyExceptions.CreateTypeError(String.Format("allocator undefined for {0}", self.Name)); + } + + #region Public Instance Methods + + [RubyMethod("_dump")] + [RubyMethod("name")] + [RubyMethod("to_s")] + public static MutableString/*!*/ ToS(RubyEncoding/*!*/ self) { + return MutableString.Create(self.Name); + } + + [RubyMethod("inspect")] + public static MutableString/*!*/ Inspect(RubyContext/*!*/ context, RubyEncoding/*!*/ self) { + // TODO: to_s overridden + MutableString result = MutableString.CreateMutable(); + result.Append("#<"); + result.Append(RubyUtils.GetClassName(context, self)); + result.Append(':'); + result.Append(self.Name); + result.Append(">"); + return result; + } + + [RubyMethod("based_encoding")] + public static RubyEncoding BasedEncoding(RubyEncoding/*!*/ self) { + return null; + } + + [RubyMethod("dummy?")] + public static bool IsDummy(RubyEncoding/*!*/ self) { + return false; + } + + #endregion + + #region Singleton Methods + + [RubyMethod("list", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ GetAvailableEncodings(RubyClass/*!*/ self) { + // TODO: loads all encodings, we should be lazy with encoding creation + + var infos = Encoding.GetEncodings(); + var result = new RubyArray(1 + infos.Length); + + // Ruby specific: + result.Add(RubyEncoding.Binary); + + foreach (var info in infos) { + result.Add(RubyEncoding.GetRubyEncoding(info.GetEncoding())); + } + return result; + } + + [RubyMethod("find", RubyMethodAttributes.PublicSingleton)] + public static RubyEncoding/*!*/ GetEncoding(RubyClass/*!*/ self, [NotNull]MutableString/*!*/ name) { + return RubyEncoding.GetRubyEncoding(name.ToString()); + } + + [RubyMethod("compatible?", RubyMethodAttributes.PublicSingleton)] + public static bool IsCompatible(RubyClass/*!*/ self, [NotNull]RubyEncoding/*!*/ encoding1, [NotNull]RubyEncoding/*!*/ encoding2) { + throw new NotImplementedException(); + } + + [RubyMethod("_load?", RubyMethodAttributes.PublicSingleton)] + public static bool Load(RubyClass/*!*/ self) { + throw new NotImplementedException(); + } + + [RubyMethod("default_external", RubyMethodAttributes.PublicSingleton)] + public static RubyEncoding/*!*/ GetDefaultEncoding(RubyClass/*!*/ self) { + return RubyEncoding.Default; + } + + [RubyMethod("locale_charmap", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ GetDefaultCharmap(RubyClass/*!*/ self) { + return MutableString.Create("CP" + StringUtils.DefaultEncoding.CodePage.ToString()); + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyGC.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyGC.cs new file mode 100644 index 0000000000..961e7539de --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyGC.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Actions; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + [RubyModule("GC")] + public static class RubyGC { + [RubyMethod("enable", RubyMethodAttributes.PublicSingleton)] + public static bool Enable(object self) { + return false; + } + + [RubyMethod("disable", RubyMethodAttributes.PublicSingleton)] + public static bool Disable(object self) { + return false; + } + + [RubyMethod("start", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("garbage_collect", RubyMethodAttributes.PublicInstance)] + public static void GarbageCollect(object self) { + GC.Collect(); + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyMath.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyMath.cs new file mode 100644 index 0000000000..83778ab944 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyMath.cs @@ -0,0 +1,203 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Runtime; +using SM = System.Math; + +namespace IronRuby.Builtins { + + [RubyModule("Math")] + public static class RubyMath { + + [RubyConstant] + public const double E = System.Math.E; + + [RubyConstant] + public const double PI = System.Math.PI; + + #region Private Implementation Details + + private static double DomainCheck(double result, string/*!*/ functionName) { + if (double.IsNaN(result)) { + throw new Errno.DomainError("Domain error - " + functionName); + } + return result; + } + + private static ushort Exponent(byte[] v) { + return (ushort)((((ushort)(v[7] & 0x7F)) << (ushort)4) | (((ushort)(v[6] & 0xF0)) >> 4)); + } + + private static ulong Mantissa(byte[] v) { + uint i1 = ((uint)v[0] | ((uint)v[1] << 8) | ((uint)v[2] << 16) | ((uint)v[3] << 24)); + uint i2 = ((uint)v[4] | ((uint)v[5] << 8) | ((uint)(v[6] & 0xF) << 16)); + + return (ulong)((ulong)i1 | ((ulong)i2 << 32)); + } + + #endregion + + #region Private Instance & Singleton Methods + + [RubyMethodAttribute("acos", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("acos", RubyMethodAttributes.PublicSingleton)] + public static double Acos(object self, double x) { + return DomainCheck(SM.Acos(x), "acos"); + } + + [RubyMethodAttribute("acosh", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("acosh", RubyMethodAttributes.PublicSingleton)] + public static double Acosh(object self, double x) { + //ln(x + sqrt(x*x - 1)) for x >= 1 + return DomainCheck(SM.Log(x + SM.Sqrt(x*x - 1)), "acosh"); + } + + [RubyMethodAttribute("asin", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("asin", RubyMethodAttributes.PublicSingleton)] + public static double Asin(object self, double x) { + return DomainCheck(SM.Asin(x), "asin"); + } + + [RubyMethodAttribute("asinh", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("asinh", RubyMethodAttributes.PublicSingleton)] + public static double Asinh(object self, double x) { + //ln(x + sqrt(x*x + 1)) + return SM.Log(x + SM.Sqrt(x * x + 1)); + } + + [RubyMethodAttribute("atan", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("atan", RubyMethodAttributes.PublicSingleton)] + public static double Atan(object self, double x) { + return SM.Atan(x); + } + + [RubyMethodAttribute("atan2", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("atan2", RubyMethodAttributes.PublicSingleton)] + public static double Atan2(object self, double y, double x) { + return SM.Atan2(y, x); + } + + [RubyMethodAttribute("atanh", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("atanh", RubyMethodAttributes.PublicSingleton)] + public static double Atanh(object self, double x) { + //(1/2) * ln((1+x)/(1-x)) + return DomainCheck((1 / 2) * SM.Log((1 + x) / (1 - x)), "atanh"); + } + + [RubyMethodAttribute("cos", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("cos", RubyMethodAttributes.PublicSingleton)] + public static double Cos(object self, double x) { + return SM.Cos(x); + } + + [RubyMethodAttribute("cosh", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("cosh", RubyMethodAttributes.PublicSingleton)] + public static double Cosh(object self, double x) { + return SM.Cosh(x); + } + + [RubyMethodAttribute("erf", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("erf", RubyMethodAttributes.PublicSingleton)] + public static double Erf(object self, double x) { + throw new NotImplementedError("erf"); + } + + [RubyMethodAttribute("erfc", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("erfc", RubyMethodAttributes.PublicSingleton)] + public static double Erfc(object self, double x) { + throw new NotImplementedError("erfc"); + } + + [RubyMethodAttribute("exp", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("exp", RubyMethodAttributes.PublicSingleton)] + public static double Exp(object self, double x) { + return SM.Exp(x); + } + + + [RubyMethodAttribute("hypot", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("hypot", RubyMethodAttributes.PublicSingleton)] + public static double Hypot(object self, double x, double y) { + return DomainCheck(SM.Sqrt(x*x+y*y), "hypot"); + } + + [RubyMethodAttribute("frexp", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("frexp", RubyMethodAttributes.PublicSingleton)] + public static RubyArray Frexp(object self, double x) { + byte[] bytes; + double mantissa; + int exponent; + RubyArray result = new RubyArray(2); + + bytes = System.BitConverter.GetBytes(x); + mantissa = (Mantissa(bytes) * SM.Pow(2, -52) + 1.0) / 2; + exponent = Exponent(bytes) - 1022; + + result.Add(mantissa); + result.Add(exponent); + return result; + } + + [RubyMethodAttribute("ldexp", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("ldexp", RubyMethodAttributes.PublicSingleton)] + public static double Ldexp(object self, double x, double y) { + return x * SM.Pow(2, SM.Floor(y)); + } + + [RubyMethodAttribute("log", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("log", RubyMethodAttributes.PublicSingleton)] + public static double Log(object self, double x) { + return DomainCheck(SM.Log(x), "log"); + } + + [RubyMethodAttribute("log10", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("log10", RubyMethodAttributes.PublicSingleton)] + public static double Log10(object self, double x) { + return DomainCheck(SM.Log10(x), "log10"); + } + + [RubyMethodAttribute("sin", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("sin", RubyMethodAttributes.PublicSingleton)] + public static double Sin(object self, double x) { + return SM.Sin(x); + } + + [RubyMethodAttribute("sinh", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("sinh", RubyMethodAttributes.PublicSingleton)] + public static double Sinh(object self, double x) { + return SM.Sinh(x); + } + + [RubyMethodAttribute("sqrt", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("sqrt", RubyMethodAttributes.PublicSingleton)] + public static double Sqrt(object self, double x) { + return DomainCheck(SM.Sqrt(x), "sqrt"); + } + + [RubyMethodAttribute("tan", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("tan", RubyMethodAttributes.PublicSingleton)] + public static double Tan(object self, double x) { + return SM.Tan(x); + } + + [RubyMethodAttribute("tanh", RubyMethodAttributes.PrivateInstance)] + [RubyMethodAttribute("tanh", RubyMethodAttributes.PublicSingleton)] + public static double Tanh(object self, double x) { + return SM.Tanh(x); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyProcess.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyProcess.cs new file mode 100644 index 0000000000..c13af5898d --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyProcess.cs @@ -0,0 +1,223 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ +#if !SILVERLIGHT + +using System; +using System.Diagnostics; +using System.Security; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + /// + /// Process builtin module + /// + [RubyModule("Process", BuildConfig = "!SILVERLIGHT")] + public static class RubyProcess { + + [RubyClass("Status", BuildConfig = "!SILVERLIGHT")] + public class Status { + private readonly Process/*!*/ _process; + + public Status(Process/*!*/ process) { + _process = process; + } + + [RubyMethod("coredump?")] + public static bool CoreDump(Status/*!*/ self) { + // Always false on Windows + return false; + } + + [RubyMethod("exitstatus")] + public static int ExitStatus(Status/*!*/ self) { + return self._process.ExitCode; + } + + [RubyMethod("exited?")] + public static bool Exited(Status/*!*/ self) { + return self._process.HasExited; + } + + [RubyMethod("pid")] + public static int Pid(Status/*!*/ self) { + return self._process.Id; + } + + [RubyMethod("stopped?")] + public static bool Stopped(Status/*!*/ self) { + // Always false on Windows + return false; + } + + [RubyMethod("stopsig")] + public static object StopSig(Status/*!*/ self) { + // Always nil on Windows + return null; + } + + [RubyMethod("success?")] + public static bool Success(Status/*!*/ self) { + return self._process.ExitCode == 0; + } + + [RubyMethod("termsig")] + public static object TermSig(Status/*!*/ self) { + // Always nil on Windows + return null; + } + } + + // abort + // detach + // egid + // egid= + + [RubyMethod("euid", RubyMethodAttributes.PublicSingleton)] + public static int EffectiveUserId(RubyModule/*!*/ self) { + return 0; // always 0 on Windows? + } + + // euid= + // exit + // exit! + // fork + // getpgid + // getpriority + // getrlimit + // gid + // gid= + // groups + // groups= + // initgroups + + [RubyMethod("kill", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("kill", RubyMethodAttributes.PublicSingleton)] + public static object Kill(RubyModule/*!*/ self, object signalId, object pid) { + throw RubyExceptions.CreateNotImplementedError("Signals are not currently implemented. Signal.trap just pretends to work"); + } + + // maxgroups + // maxgroups= + + [RubyMethod("pid", RubyMethodAttributes.PublicSingleton)] + public static int GetPid(RubyModule/*!*/ self) { + return Process.GetCurrentProcess().Id; + } + + [RubyMethod("ppid", RubyMethodAttributes.PublicSingleton)] + public static int GetParentPid(RubyModule/*!*/ self) { + return 0; + } + + // setpgid + // setpgrp + // setpriority + // setrlimit + // setsid + + [RubyMethod("times", RubyMethodAttributes.PublicSingleton)] + public static RubyStruct/*!*/ GetTimes(RubyModule/*!*/ self) { + var result = RubyStruct.Create(RubyStructOps.GetTmsClass(self.Context)); + try { + FillTimes(result); + } catch (SecurityException) { + RubyStructOps.TmsSetUserTime(result, 0.0); + RubyStructOps.TmsSetSystemTime(result, 0.0); + RubyStructOps.TmsSetChildUserTime(result, 0.0); + RubyStructOps.TmsSetChildSystemTime(result, 0.0); + } + + return result; + } + + private static void FillTimes(RubyStruct/*!*/ result) { + var process = Process.GetCurrentProcess(); + RubyStructOps.TmsSetUserTime(result, process.UserProcessorTime.TotalSeconds); + RubyStructOps.TmsSetSystemTime(result, process.PrivilegedProcessorTime.TotalSeconds); + RubyStructOps.TmsSetChildUserTime(result, 0.0); + RubyStructOps.TmsSetChildSystemTime(result, 0.0); + } + + [RubyMethod("uid", RubyMethodAttributes.PublicSingleton)] + public static int UserId(RubyModule/*!*/ self) { + return 0; // always 0 on Windows? + } + + // uid= + // wait + // wait2 + // waitall + // waitpid + // waitpid2 + } + + public static partial class RubyStructOps { + #region Tms + + internal static readonly object TmsStructClassKey = new object(); + + [RubyConstant("Tms", BuildConfig = "!SILVERLIGHT")] + internal static RubyClass/*!*/ CreateTmsClass(RubyModule/*!*/ module) { + // class is available for the library even if the constant is removed => store it on the context: + return (RubyClass)module.Context.GetOrCreateLibraryData(TmsStructClassKey, () => RubyStruct.DefineStruct( + (RubyClass)module, + "Tms", + new[] { "utime", "stime", "cutime", "cstime" } + )); + } + + public static RubyClass/*!*/ GetTmsClass(RubyContext/*!*/ context) { + ContractUtils.RequiresNotNull(context, "context"); + + object value; + if (context.TryGetLibraryData(TmsStructClassKey, out value)) { + return (RubyClass)value; + } + + // trigger constant initialization of Struct class: + context.GetClass(typeof(RubyStruct)).TryGetConstantNoAutoload(String.Empty, out value); + + // try again: + if (context.TryGetLibraryData(TmsStructClassKey, out value)) { + return (RubyClass)value; + } + + throw Assert.Unreachable; + } + + public static void TmsSetUserTime(RubyStruct/*!*/ tms, double value) { + tms[0] = value; + } + + public static void TmsSetSystemTime(RubyStruct/*!*/ tms, double value) { + tms[1] = value; + } + + public static void TmsSetChildUserTime(RubyStruct/*!*/ tms, double value) { + tms[2] = value; + } + + public static void TmsSetChildSystemTime(RubyStruct/*!*/ tms, double value) { + tms[3] = value; + } + + #endregion + } +} +#endif diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyRegexOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyRegexOps.cs new file mode 100644 index 0000000000..56c5375b62 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/RubyRegexOps.cs @@ -0,0 +1,379 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Builtins { + [RubyClass("Regexp", Extends = typeof(RubyRegex), Inherits = typeof(Object)), Includes(typeof(Enumerable))] + public static class RegexpOps { + #region constructors, compile + + [RubyConstructor] + public static RubyRegex/*!*/ Create(RubyClass/*!*/ self, + [NotNull]RubyRegex/*!*/ other) { + + return new RubyRegex(other); + } + + [RubyConstructor] + public static RubyRegex/*!*/ Create(RubyClass/*!*/ self, + [NotNull]RubyRegex/*!*/ other, int options, [Optional]object encoding) { + return Create(self, other, (object)options, encoding); + } + + [RubyConstructor] + public static RubyRegex/*!*/ Create(RubyClass/*!*/ self, + [NotNull]RubyRegex/*!*/ other, [DefaultParameterValue(null)]object options, [Optional]object encoding) { + + ReportParametersIgnoredWarning(self.Context, encoding); + return new RubyRegex(other); + } + + [RubyConstructor] + public static RubyRegex/*!*/ Create(RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ pattern, int options, [DefaultProtocol, Optional]MutableString encoding) { + + return new RubyRegex(pattern, MakeOptions(options, encoding)); + } + + [RubyConstructor] + public static RubyRegex/*!*/ Create(RubyScope/*!*/ scope, RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ pattern, [DefaultParameterValue(null)]object ignoreCase, [DefaultProtocol, Optional]MutableString encoding) { + + return new RubyRegex(pattern, MakeOptions(ignoreCase, encoding)); + } + + [RubyMethod("compile", RubyMethodAttributes.PublicSingleton)] + public static RuleGenerator/*!*/ Compile() { + return new RuleGenerator(RuleGenerators.InstanceConstructor); + } + + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static RubyRegex/*!*/ Reinitialize(RubyRegex/*!*/ self, [NotNull]RubyRegex/*!*/ other) { + self.Set(other.GetPattern(), other.Options); + return self; + } + + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static RubyRegex/*!*/ Reinitialize(RubyContext/*!*/ context, RubyRegex/*!*/ self, + [NotNull]RubyRegex/*!*/ regex, int options, [Optional]object encoding) { + + ReportParametersIgnoredWarning(context, encoding); + return Reinitialize(self, regex); + } + + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static RubyRegex/*!*/ Reinitialize(RubyContext/*!*/ context, RubyRegex/*!*/ self, + [NotNull]RubyRegex/*!*/ regex, [DefaultParameterValue(null)]object ignoreCase, [Optional]object encoding) { + + ReportParametersIgnoredWarning(context, encoding); + return Reinitialize(self, regex); + } + + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static RubyRegex/*!*/ Reinitialize(RubyRegex/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ pattern, int options, [DefaultProtocol, Optional]MutableString encoding) { + + self.Set(pattern, MakeOptions(options, encoding)); + return self; + } + + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static RubyRegex/*!*/ Reinitialize(RubyRegex/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ pattern, [DefaultParameterValue(null)]object ignoreCase, [DefaultProtocol, Optional]MutableString encoding) { + + self.Set(pattern, MakeOptions(ignoreCase, encoding)); + return self; + } + + private static void ReportParametersIgnoredWarning(RubyContext/*!*/ context, object encoding) { + context.ReportWarning((encoding != Missing.Value) ? "flags and encoding ignored" : "flags ignored"); + } + + internal static RubyRegexOptions MakeOptions(object ignoreCase, MutableString encoding) { + return ObjectToIgnoreCaseOption(ignoreCase) | StringToRegexEncoding(encoding); + } + + internal static RubyRegexOptions MakeOptions(int options, MutableString encoding) { + return (RubyRegexOptions)options | StringToRegexEncoding(encoding); + } + + internal static RubyRegexOptions ObjectToIgnoreCaseOption(object obj) { + return Protocols.IsTrue(obj) ? RubyRegexOptions.IgnoreCase : RubyRegexOptions.NONE; + } + + internal static RubyRegexOptions StringToRegexEncoding(MutableString encoding) { + if (MutableString.IsNullOrEmpty(encoding)) { + return RubyRegexOptions.NONE; + } + + switch (encoding.GetChar(0)) { + case 'N': + case 'n': return RubyRegexOptions.FIXED; + case 'E': + case 'e': return RubyRegexOptions.EUC; + case 'S': + case 's': return RubyRegexOptions.SJIS; + case 'U': + case 'u': return RubyRegexOptions.UTF8; + } + + return RubyRegexOptions.NONE; + } + + #endregion + + [RubyConstant] + public const int IGNORECASE = (int)RubyRegexOptions.IgnoreCase; + + [RubyConstant] + public const int EXTENDED = (int)RubyRegexOptions.Extended; + + [RubyConstant] + public const int MULTILINE = (int)RubyRegexOptions.Multiline; + + /// + /// Returns "(?{enabled-options}-{disabled-options}:{pattern-with-forward-slash-escaped})". + /// Doesn't escape forward slashes that are already escaped. + /// + [RubyMethod("to_s")] + public static MutableString/*!*/ ToS(RubyRegex/*!*/ self) { + // Ruby: doesn't wrap if there is a single embedded expression that evaluates to non-nil: + // puts(/#{nil}#{/a/}#{nil}/) + // We don't do that. + + return Append(self, MutableString.CreateMutable()); + } + + private static MutableString/*!*/ Append(RubyRegex/*!*/ self, MutableString/*!*/ result) { + Assert.NotNull(self, result); + + result.Append("(?"); + if (AppendOptionString(result, self.Options, true, false) < 3) { + result.Append('-'); + } + AppendOptionString(result, self.Options, false, false); + result.Append(':'); + AppendEscapeForwardSlash(result, self.GetPattern()); + result.Append(')'); + return result; + } + + private static int SkipToUnescapedForwardSlash(MutableString/*!*/ pattern, int i) { + while (i < pattern.Length) { + i = pattern.IndexOf('/', i); + if (i <= 0) { + return i; + } + + if (pattern.GetChar(i - 1) != '\\') { + return i; + } + + i++; + } + return -1; + } + + internal static MutableString/*!*/ AppendEscapeForwardSlash(MutableString/*!*/ result, MutableString/*!*/ pattern) { + int first = 0; + int i = SkipToUnescapedForwardSlash(pattern, 0); + while (i >= 0) { + Debug.Assert(i < pattern.Length); + Debug.Assert(pattern.GetChar(i) == '/' && (i == 0 || pattern.GetChar(i - 1) != '\\')); + + result.Append(pattern, first, i - first); + result.Append('\\'); + first = i; // include forward slash in the next append + i = SkipToUnescapedForwardSlash(pattern, i + 1); + } + + result.Append(pattern, first, pattern.Length - first); + return result; + } + + /// + /// Returns "/{pattern-with-forward-slash-escaped}/" + /// Doesn't escape forward slashes that are already escaped. + /// + [RubyMethod("inspect")] + public static MutableString/*!*/ Inspect(RubyRegex/*!*/ self) { + MutableString result = MutableString.CreateMutable(); + result.Append('/'); + AppendEscapeForwardSlash(result, self.GetPattern()); + result.Append('/'); + AppendOptionString(result, self.Options, true, true); + return result; + } + + [RubyMethod("options")] + public static int GetOptions(RubyRegex/*!*/ self) { + return (int)self.Options; + } + + [RubyMethod("kcode")] + public static MutableString GetEncoding(RubyRegex/*!*/ self) { + switch (self.Options & RubyRegexOptions.EncodingMask) { + case RubyRegexOptions.NONE: return null; + case RubyRegexOptions.EUC: return MutableString.Create("euc"); + case RubyRegexOptions.FIXED: return MutableString.Create("none"); + case RubyRegexOptions.UTF8: return MutableString.Create("utf8"); + case RubyRegexOptions.SJIS: return MutableString.Create("sjis"); + default: throw Assert.Unreachable; + } + } + + [RubyMethod("casefold?")] + public static bool IsCaseInsensitive(RubyRegex/*!*/ self) { + return (self.Options & RubyRegexOptions.IgnoreCase) != 0; + } + + private static int AppendOptionString(MutableString/*!*/ result, RubyRegexOptions options, bool enabled, bool includeEncoding) { + int count = 0; + + if (((options & RubyRegexOptions.Multiline) != 0) == enabled) { + result.Append('m'); + count++; + } + + if (((options & RubyRegexOptions.IgnoreCase) != 0) == enabled) { + result.Append('i'); + count++; + } + + if (((options & RubyRegexOptions.Extended) != 0) == enabled) { + result.Append('x'); + count++; + } + + if (includeEncoding) { + switch (options & RubyRegexOptions.EncodingMask) { + case RubyRegexOptions.NONE: break; + case RubyRegexOptions.EUC: result.Append('e'); break; + case RubyRegexOptions.FIXED: result.Append('n'); break; + case RubyRegexOptions.UTF8: result.Append('u'); break; + case RubyRegexOptions.SJIS: result.Append('s'); break; + default: throw Assert.Unreachable; + } + } + return count; + } + + [RubyMethod("match")] + public static MatchData Match(RubyScope/*!*/ scope, RubyRegex/*!*/ self, [DefaultProtocol]MutableString str) { + return RubyRegex.SetCurrentMatchData(scope, self, str); + } + + [RubyMethod("hash")] + public static int GetHash(RubyRegex/*!*/ self) { + return self.GetHashCode(); + } + + [RubyMethod("=="), RubyMethod("eql?")] + public static bool Equals(RubyRegex/*!*/ self, object other) { + return false; + } + + [RubyMethod("=="), RubyMethod("eql?")] + public static bool Equals(RubyContext/*!*/ context, RubyRegex/*!*/ self, [NotNull]RubyRegex/*!*/ other) { + return self.Equals(other); + } + + [RubyMethod("=~")] + public static object MatchIndex(RubyScope/*!*/ scope, RubyRegex/*!*/ self, [DefaultProtocol]MutableString/*!*/ str) { + MatchData match = RubyRegex.SetCurrentMatchData(scope, self, str); + return (match != null) ? ScriptingRuntimeHelpers.Int32ToObject(match.Index) : null; + } + + [RubyMethod("===")] + public static bool CaseCompare(RubyScope/*!*/ scope, RubyRegex/*!*/ self, [NotNull]MutableString/*!*/ str) { + MatchData match = Match(scope, self, str); + return (match != null && match.Success) ? true : false; + } + + [RubyMethod("===")] + public static bool CaseCompare(RubyContext/*!*/ context, RubyRegex/*!*/ self, object str) { + MutableString asString = Protocols.AsString(context, str); + if (asString == null) { + return false; + } + return CaseCompare(context, self, asString); + } + + [RubyMethod("~")] + public static object ImplicitMatch(RubyScope/*!*/ scope, RubyRegex/*!*/ self) { + return MatchIndex(scope, self, Protocols.CastToString(scope.RubyContext, scope.GetInnerMostClosureScope().LastInputLine)); + } + + [RubyMethod("source")] + public static MutableString/*!*/ Source(RubyRegex/*!*/ self) { + return self.GetPattern(); + } + + [RubyMethod("escape", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("quote", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ Escape(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ str) { + return RubyRegex.Escape(str).TaintBy(str); + } + + [RubyMethod("last_match", RubyMethodAttributes.PublicSingleton)] + public static MatchData LastMatch(RubyScope/*!*/ scope, RubyClass/*!*/ self) { + return scope.GetInnerMostClosureScope().CurrentMatch; + } + + [RubyMethod("last_match", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ LastMatch(RubyScope/*!*/ scope, RubyClass/*!*/ self, [DefaultProtocol]int groupIndex) { + return scope.GetInnerMostClosureScope().CurrentMatch.GetGroupValue(scope.RubyContext, groupIndex); + } + + [RubyMethod("union", RubyMethodAttributes.PublicSingleton)] + public static RubyRegex/*!*/ Union(RubyClass/*!*/ self, [NotNull]params object[]/*!*/ strings) { + + if (strings.Length == 0) { + return new RubyRegex("(?!)", RubyRegexOptions.NONE); + } + + MutableString result = MutableString.CreateMutable(); + for (int i = 0; i < strings.Length; i++) { + if (i > 0) { + result.Append('|'); + } + + RubyRegex regex = strings[i] as RubyRegex; + if (regex != null) { + if (strings.Length == 1) { + return regex; + } + + Append(regex, result); + } else { + result.Append(RubyRegex.Escape(Protocols.CastToString(self.Context, strings[i]))); + } + } + + // TODO: + //RubyClass regexClass = RubyUtils.GetExecutionContext(context).GetClass(typeof(RubyRegex)); + //return NewCallSite3.Invoke(context, regexClass, result, null, null); + return new RubyRegex(result, RubyRegexOptions.NONE); + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Signal.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Signal.cs new file mode 100644 index 0000000000..b607fc4359 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/Signal.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ +#if !SILVERLIGHT // Signals dont make much sense in Silverlight as cross-process communication is not allowed + +using IronRuby.Runtime; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.Builtins { + + [RubyModule("Signal", BuildConfig="!SILVERLIGHT")] + public static class Signal { + #region Private Instance & Singleton Methods + + [RubyMethod("list", RubyMethodAttributes.PublicSingleton)] + public static Hash/*!*/ List(RubyContext/*!*/ context, RubyModule/*!*/ self) { + Hash result = new Hash(context); + result.Add(MutableString.Create("TERM"), ScriptingRuntimeHelpers.Int32ToObject(15)); + result.Add(MutableString.Create("SEGV"), ScriptingRuntimeHelpers.Int32ToObject(11)); + result.Add(MutableString.Create("KILL"), ScriptingRuntimeHelpers.Int32ToObject(9)); + result.Add(MutableString.Create("EXIT"), ScriptingRuntimeHelpers.Int32ToObject(0)); + result.Add(MutableString.Create("INT"), ScriptingRuntimeHelpers.Int32ToObject(2)); + result.Add(MutableString.Create("FPE"), ScriptingRuntimeHelpers.Int32ToObject(8)); + result.Add(MutableString.Create("ABRT"), ScriptingRuntimeHelpers.Int32ToObject(22)); + result.Add(MutableString.Create("ILL"), ScriptingRuntimeHelpers.Int32ToObject(4)); + return result; + } + + [RubyMethod("trap", RubyMethodAttributes.PublicSingleton)] + public static object Trap(RubyContext/*!*/ context, object self, object signalId, Proc proc) { + // TODO: For now, just ignore the signal handler. The full implementation will need to build on + // the signal and raise functions in msvcrt. + return null; + } + + [RubyMethod("trap", RubyMethodAttributes.PublicSingleton)] + public static object Trap(RubyContext/*!*/ context, BlockParam block, object self, object signalId) { + // TODO: For now, just ignore the signal handler. The full implementation will need to build on + // the signal and raise functions in msvcrt. + return null; + } + + + #endregion + } +} +#endif diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/SingletonOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/SingletonOps.cs new file mode 100644 index 0000000000..aa4ea07e8c --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/SingletonOps.cs @@ -0,0 +1,149 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + /// + /// Methods on Singleton(main). + /// + [RubyModule(RubyClass.MainSingletonName)] + public static class MainSingletonOps { + #region Private Instance Methods + + // Reinitialization. Not called when a factory/non-default ctor is called. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static object/*!*/ Initialize(object/*!*/ self) { + // TODO: + throw new NotImplementedException("TODO"); + } + + #endregion + + #region Public Instance Methods + + [RubyMethod("to_s", RubyMethodAttributes.PublicInstance)] + public static MutableString/*!*/ ToS(object/*!*/ self) { + return MutableString.Create("main"); + } + + [RubyMethod("public", RubyMethodAttributes.PublicInstance)] + public static RubyModule/*!*/ SetPublicVisibility(RubyScope/*!*/ scope, object/*!*/ self, [NotNull]params object[]/*!*/ methodNames) { + return SetVisibility(scope, self, methodNames, RubyMethodAttributes.PublicInstance); + } + + [RubyMethod("private", RubyMethodAttributes.PublicInstance)] + public static RubyModule/*!*/ SetPrivateVisibility(RubyScope/*!*/ scope, object/*!*/ self, [NotNull]params object[]/*!*/ methodNames) { + return SetVisibility(scope, self, methodNames, RubyMethodAttributes.PrivateInstance); + } + + private static RubyModule/*!*/ SetVisibility(RubyScope/*!*/ scope, object/*!*/ self, object[]/*!*/ methodNames, RubyMethodAttributes attributes) { + Assert.NotNull(scope, self, methodNames); + RubyClass cls = scope.RubyContext.GetClassOf(self); + ModuleOps.SetMethodAttributes(scope, cls, methodNames, attributes); + return cls; + } + + [RubyMethod("include", RubyMethodAttributes.PublicInstance)] + public static RubyClass/*!*/ Include(RubyContext/*!*/ context, object/*!*/ self, params RubyModule[]/*!*/ modules) { + ContractUtils.RequiresNotNullItems(modules, "modules"); + RubyUtils.RequireNonClasses(modules); + + RubyClass result = context.GetClassOf(self); + result.IncludeModules(modules); + return result; + } + + #endregion + } + + /// + /// Methods on Singleton(class), Singleton(Singleton(object)). + /// + [RubyModule(RubyClass.ClassSingletonName)] + public static class ClassSingletonOps { + #region Private Instance Methods + + // Reinitialization. Not called when a factory/non-default ctor is called. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static object/*!*/ Initialize(object/*!*/ self) { + // TODO: + throw new NotImplementedException("TODO"); + } + + [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)] + public static object InitializeCopy(object/*!*/ self, object other) { + // TODO: + throw new NotImplementedException("TODO"); + } + + [RubyMethod("inherited", RubyMethodAttributes.PrivateInstance)] + public static void Inherited(object/*!*/ self, [NotNull]RubyClass/*!*/ subclass) { + // TODO: + throw new NotImplementedException("TODO"); + } + + #endregion + + #region Public Instance Methods + + [RubyMethod("allocate", RubyMethodAttributes.PublicInstance)] + public static void Allocate(RubyClass/*!*/ self) { + throw RubyExceptions.CreateTypeError("can't create instance of virtual class"); + } + + [RubyMethod("superclass", RubyMethodAttributes.PublicInstance)] + public static RubyClass/*!*/ GetSuperClass(RubyClass/*!*/ self) { + RubyClass result = self.SingletonClass; + Debug.Assert(result != null && result.IsSingletonClass); + + // do not return dummy singletons, also do not create a new singleton (MRI does): + return result.IsDummySingletonClass ? self : result; + } + + [RubyMethod("new", RubyMethodAttributes.PublicInstance)] + public static void New(RubyClass/*!*/ self) { + Allocate(self); + } + + #endregion + } + + /// + /// Methods on Singleton(Singleton(class)), Singleton(Singleton(Singleton(object))). + /// + [RubyModule(RubyClass.ClassSingletonSingletonName), Includes(typeof(ClassSingletonOps), Copy = true)] + public static class ClassSingletonSingletonOps { + + #region Public Instance Methods + + [RubyMethod("constants", RubyMethodAttributes.PublicInstance)] + public static RubyArray/*!*/ GetConstants(object/*!*/ self) { + throw new NotImplementedException("TODO"); + } + + [RubyMethod("nesting", RubyMethodAttributes.PublicInstance)] + public static RubyModule/*!*/ GetNesting(object/*!*/ self) { + throw new NotImplementedException("TODO"); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/StringFormatter.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/StringFormatter.cs new file mode 100644 index 0000000000..61f2c4200f --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/StringFormatter.cs @@ -0,0 +1,1016 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Diagnostics; +using System.Globalization; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Text; +using Microsoft.Scripting.Math; +using IronRuby.Runtime; +using SM = System.Math; + +namespace IronRuby.Builtins { + + /// + /// StringFormatter provides Ruby's sprintf style string formatting services. + /// + /// TODO: Many dynamic languages have similar printf style functionality. + /// Combine this with IronPython's StringFormatter and move the common code into the DLR + /// + /// TODO: Use MutableString instead of StringBuilder for building the string + /// TODO: Support negative numbers for %u and %o and %x + /// + internal class StringFormatter { + + // This is a ThreadStatic since so that formatting operations on one thread do not interfere with other threads + [ThreadStatic] + private static NumberFormatInfo NumberFormatInfoForThread; + + private static NumberFormatInfo nfi { + get { + if (NumberFormatInfoForThread == null) { + NumberFormatInfo numberFormatInfo = new CultureInfo("en-US").NumberFormat; + // The CLI formats as "Infinity", but Ruby formats differently: + // sprintf("%f", 1.0/0) => "Inf" + // sprintf("%f", -1.0/0) => "-Inf" + // sprintf("%f", 0.0/0) => "Nan" + numberFormatInfo.PositiveInfinitySymbol = "Infinity"; + numberFormatInfo.NegativeInfinitySymbol = "-Infinity"; + numberFormatInfo.NaNSymbol = "NaN"; + NumberFormatInfoForThread = numberFormatInfo; + } + return NumberFormatInfoForThread; + } + } + + public bool TrailingZeroAfterWholeFloat { + get { return _TrailingZeroAfterWholeFloat; } + set { _TrailingZeroAfterWholeFloat = value; } + } + + const int UnspecifiedPrecision = -1; // Use the default precision + + private readonly IList/*!*/ _data; + private readonly string/*!*/ _format; + private readonly RubyContext/*!*/ _context; + + private bool? _useAbsolute; + private int _relativeIndex; + private bool _tainted; + + private int _index; + private char _curCh; + + // The options for formatting the current formatting specifier in the format string + private FormatSettings _opts; + // Should ddd.0 be displayed as "ddd" or "ddd.0". "'%g' % ddd.0" needs "ddd", but str(ddd.0) needs "ddd.0" + private bool _TrailingZeroAfterWholeFloat; + + private StringBuilder _buf; + + + #region Constructors + + internal StringFormatter(RubyContext/*!*/ context, string/*!*/ format, IList/*!*/ data) { + Assert.NotNull(context, format, data); + + _format = format; + _data = data; + _context = context; + } + + #endregion + + #region Public API Surface + + public MutableString/*!*/ Format() { + _index = 0; + _buf = new StringBuilder(); + _tainted = false; + int modIndex; + + while ((modIndex = _format.IndexOf('%', _index)) != -1) { + _buf.Append(_format, _index, modIndex - _index); + _index = modIndex + 1; + DoFormatCode(); + } + _buf.Append(_format, _index, _format.Length - _index); + + MutableString result = MutableString.Create(_buf.ToString()); + + if (_tainted) { + KernelOps.Taint(_context, result); + } + + return result; + } + + #endregion + + #region Private APIs + + private void DoFormatCode() { + // we already pulled the first % + + if (_index == _format.Length) { + // '%' at the end of the string. Just print it and we are done. + _buf.Append('%'); + return; + } + + _curCh = _format[_index++]; + + if (_curCh == '%') { + // Escaped '%' character using "%%". Just print it and we are done + _buf.Append('%'); + return; + } + + _opts = new FormatSettings(); + + ReadConversionFlags(); + + ReadMinimumFieldWidth(); + + ReadPrecision(); + + _opts.Value = GetData(_opts.ArgIndex); + + WriteConversion(); + } + + private void ReadConversionFlags() { + bool fFoundConversion; + do { + fFoundConversion = true; + switch (_curCh) { + case '#': _opts.AltForm = true; break; + case '-': _opts.LeftAdj = true; _opts.ZeroPad = false; break; + case '0': if (!_opts.LeftAdj) _opts.ZeroPad = true; break; + case '+': _opts.SignChar = true; _opts.Space = false; break; + case ' ': if (!_opts.SignChar) _opts.Space = true; break; + default: + // Try to read an argument index + int? argIndex = TryReadArgumentIndex(); + if (fFoundConversion = argIndex.HasValue) + _opts.ArgIndex = argIndex; + break; + } + + // TODO: we need to rewrite how we parse these things to line up our error messages with MRI + if (fFoundConversion) { + if (_index >= _format.Length) + throw RubyExceptions.CreateArgumentError("illegal format character - %"); + + _curCh = _format[_index++]; + } + + } while (fFoundConversion); + } + + private int? TryReadArgumentIndex() { + if (char.IsDigit(_curCh)) { + int end = _index; + while (end < _format.Length && char.IsDigit(_format[end])) { + end++; + } + if (end < _format.Length && _format[end] == '$') { + int argIndex = int.Parse(_format.Substring(_index - 1, end - _index + 1)); + _index = end + 1; + return argIndex; + } + } + return null; + } + + private int ReadNumberOrStar() { + int res = 0; // default value + if (_curCh == '*') { + int? argindex = TryReadArgumentIndex(); + _curCh = _format[_index++]; + + res = Protocols.CastToFixnum(_context, GetData(argindex)); + } else { + if (Char.IsDigit(_curCh)) { + res = 0; + while (Char.IsDigit(_curCh) && _index < this._format.Length) { + res = res * 10 + ((int)(_curCh - '0')); + _curCh = _format[_index++]; + } + } + } + return res; + } + + private void ReadMinimumFieldWidth() { + _opts.FieldWidth = ReadNumberOrStar(); + if (_opts.FieldWidth == Int32.MaxValue) { + // TODO: this should be thrown by the converter + throw RubyExceptions.CreateRangeError("bignum too big to convert into `long'"); + } + } + + private void ReadPrecision() { + if (_curCh == '.') { + _curCh = _format[_index++]; + // possibility: "8.f", "8.0f", or "8.2f" + _opts.Precision = ReadNumberOrStar(); + } else { + _opts.Precision = UnspecifiedPrecision; + } + } + + private void WriteConversion() { + // conversion type (required) + switch (_curCh) { + // binary number + case 'b': + case 'B': AppendBinary(_curCh); return; + // single character (int or single char str) + case 'c': AppendChar(); return; + // signed integer decimal + case 'd': + case 'i': AppendInt('D'); return; + // floating point exponential format + case 'e': + case 'E': + // floating point decimal + case 'f': + // Same as "e" if exponent is less than -4 or more than precision, "f" otherwise. + case 'G': + case 'g': AppendFloat(_curCh); return; + // unsigned octal + case 'o': AppendOctal(); return; + // call inspect on argument + case 'p': AppendInspect(); return; + // string + case 's': AppendString(); return; + // unsigned decimal + case 'u': AppendInt(_curCh); return; + // unsigned hexadecimal + case 'x': + case 'X': AppendHex(_curCh); return; + default: throw RubyExceptions.CreateArgumentError("malformed format string - %" + _curCh); + } + } + + private object GetData(int? absoluteIndex) { + if (_useAbsolute.HasValue) { + // All arguments must use absolute or relative index. They can't be mixed + if (_useAbsolute.Value && !absoluteIndex.HasValue) { + throw RubyExceptions.CreateArgumentError(string.Format("unnumbered({0}) mixed with numbered", _relativeIndex + 1)); + } else if (!_useAbsolute.Value && absoluteIndex.HasValue) { + throw RubyExceptions.CreateArgumentError(string.Format("numbered({0}) after unnumbered({1})", absoluteIndex.Value, _relativeIndex + 1)); + } + } else { + // First time through, set _useAbsolute based on our current value + _useAbsolute = absoluteIndex.HasValue; + } + + int index = _useAbsolute.Value ? (absoluteIndex.Value - 1) : _relativeIndex++; + if (index < _data.Count) { + return _data[index]; + } + + throw RubyExceptions.CreateArgumentError("too few arguments"); + } + + private void AppendChar() { + int fixChar = Protocols.CastToFixnum(_context, _opts.Value); + fixChar = fixChar > 0 ? fixChar % 256 : ((-fixChar / 256) + 1) * 256 + fixChar; + char val = (char)fixChar; + if (_opts.FieldWidth > 1) { + if (!_opts.LeftAdj) { + _buf.Append(' ', _opts.FieldWidth - 1); + } + _buf.Append(val); + if (_opts.LeftAdj) { + _buf.Append(' ', _opts.FieldWidth - 1); + } + } else { + _buf.Append(val); + } + } + + private void AppendInt(char format) { + object val = Protocols.ConvertToInteger(_context, _opts.Value); + + bool isPositive = Comparable.GreaterOrEqual(_context, val, 0); + + if (_opts.LeftAdj) { + AppendLeftAdj(val, isPositive, 'D'); + } else if (_opts.ZeroPad) { + AppendZeroPad(val, isPositive, 'D'); + } else { + AppendNumeric(val, isPositive, 'D', format == 'u'); + } + } + + private static readonly char[] zero = new char[] { '0' }; + + // Return the new type char to use + // opts.Precision will be set to the nubmer of digits to display after the decimal point + private char AdjustForG(char type, double v) { + if (type != 'G' && type != 'g') + return type; + if (Double.IsNaN(v) || Double.IsInfinity(v)) + return type; + + double absV = SM.Abs(v); + + if ((v != 0.0) && // 0.0 should not be displayed as scientific notation + absV < 1e-4 || // Values less than 0.0001 will need scientific notation + absV >= SM.Pow(10, _opts.Precision)) { // Values bigger than 1e will need scientific notation + + // One digit is displayed before the decimal point. Hence, we need one fewer than the precision after the decimal point + int fractionDigitsRequired = (_opts.Precision - 1); + string expForm = absV.ToString("E" + fractionDigitsRequired); + string mantissa = expForm.Substring(0, expForm.IndexOf('E')).TrimEnd(zero); + + // We do -2 to ignore the digit before the decimal point and the decimal point itself + Debug.Assert(mantissa[1] == '.'); + _opts.Precision = mantissa.Length - 2; + + type = (type == 'G') ? 'E' : 'e'; + } else { + // "0.000ddddd" is allowed when the precision is 5. The 3 leading zeros are not counted + int numberDecimalDigits = _opts.Precision; + if (absV < 1e-3) numberDecimalDigits += 3; + else if (absV < 1e-2) numberDecimalDigits += 2; + else if (absV < 1e-1) numberDecimalDigits += 1; + + string fixedPointForm = absV.ToString("F" + numberDecimalDigits, CultureInfo.InvariantCulture).TrimEnd(zero); + string fraction = fixedPointForm.Substring(fixedPointForm.IndexOf('.') + 1); + if (absV < 1.0) { + _opts.Precision = fraction.Length; + } else { + int digitsBeforeDecimalPoint = 1 + (int)SM.Log10(absV); + _opts.Precision = SM.Min(_opts.Precision - digitsBeforeDecimalPoint, fraction.Length); + } + + type = 'f'; + } + + return type; + } + + private void AppendFloat(char type) { + double v = Protocols.ConvertToFloat(_context, _opts.Value); + + // scientific exponential format + Debug.Assert(type == 'E' || type == 'e' || + // floating point decimal + type == 'f' || + // Same as "e" if exponent is less than -4 or more than precision, "f" otherwise. + type == 'G' || type == 'g'); + + bool forceDot = false; + // update our precision first... + if (_opts.Precision != UnspecifiedPrecision) { + if (_opts.Precision == 0 && _opts.AltForm) forceDot = true; + if (_opts.Precision > 50) + _opts.Precision = 50; + } else { + // alternate form (#) specified, set precision to zero... + if (_opts.AltForm) { + _opts.Precision = 0; + forceDot = true; + } else _opts.Precision = 6; + } + + type = AdjustForG(type, v); + nfi.NumberDecimalDigits = _opts.Precision; + + // then append + if (_opts.LeftAdj) { + AppendLeftAdj(v, v >= 0, type); + } else if (_opts.ZeroPad) { + AppendZeroPadFloat(v, type); + } else { + AppendNumeric(v, v >= 0, type, false); + } + if (v <= 0 && v > -1 && _buf[0] != '-') { + FixupFloatMinus(v); + } + + if (forceDot) { + FixupAltFormDot(); + } + } + + private void FixupAltFormDot() { + _buf.Append('.'); + if (_opts.FieldWidth != 0) { + // try and remove the extra character we're adding. + for (int i = 0; i < _buf.Length; i++) { + char c = _buf[i]; + if (c == ' ' || c == '0') { + _buf.Remove(i, 1); + break; + } else if (c != '-' && c != '+') { + break; + } + } + } + } + + private void FixupFloatMinus(double value) { + // Ruby always appends a minus sign even if precision is 0 and the value would appear to be zero. + // sprintf("%.0f", -0.1) => "-0" + // Ruby also displays a "-0.0" for a negative zero whereas the CLR displays just "0.0" + bool fNeedMinus; + if (value == 0.0) { + fNeedMinus = FloatOps.IsNegativeZero(value); + } else { + fNeedMinus = true; + for (int i = 0; i < _buf.Length; i++) { + char c = _buf[i]; + if (c != '.' && c != '0' && c != ' ') { + fNeedMinus = false; + break; + } + } + } + + if (fNeedMinus) { + if (_opts.FieldWidth != 0) { + // trim us back down to the correct field width... + if (_buf[_buf.Length - 1] == ' ') { + _buf.Insert(0, "-"); + _buf.Remove(_buf.Length - 1, 1); + } else { + int index = 0; + while (_buf[index] == ' ') index++; + if (index > 0) index--; + _buf[index] = '-'; + } + } else { + _buf.Insert(0, "-"); + } + } + } + + private void AppendZeroPad(object val, bool fPos, char format) { + if (fPos && (_opts.SignChar || _opts.Space)) { + // produce [' '|'+']0000digits + // first get 0 padded number to field width + string res = String.Format(nfi, "{0:" + format + _opts.FieldWidth.ToString() + "}", val); + + char signOrSpace = _opts.SignChar ? '+' : ' '; + // then if we ended up with a leading zero replace it, otherwise + // append the space / + to the front. + if (res[0] == '0' && res.Length > 1) { + res = signOrSpace + res.Substring(1); + } else { + res = signOrSpace + res; + } + _buf.Append(res); + } else { + string res = String.Format(nfi, "{0:" + format + _opts.FieldWidth.ToString() + "}", val); + + // Difference: + // System.String.Format("{0:D3}", -1) '-001' + // "%03d" % -1 '-01' + + if (res[0] == '-') { + // negative + _buf.Append("-"); + if (res[1] != '0') { + _buf.Append(res.Substring(1)); + } else { + _buf.Append(res.Substring(2)); + } + } else { + // positive + _buf.Append(res); + } + } + } + + private void AppendZeroPadFloat(double val, char format) { + if (val >= 0) { + StringBuilder res = new StringBuilder(val.ToString(format.ToString(), nfi)); + if (res.Length < _opts.FieldWidth) { + res.Insert(0, new string('0', _opts.FieldWidth - res.Length)); + } + if (_opts.SignChar || _opts.Space) { + char signOrSpace = _opts.SignChar ? '+' : ' '; + // then if we ended up with a leading zero replace it, otherwise + // append the space / + to the front. + if (res[0] == '0' && res[1] != '.') { + res[0] = signOrSpace; + } else { + res.Insert(0, signOrSpace.ToString()); + } + } + _buf.Append(res); + } else { + StringBuilder res = new StringBuilder(val.ToString(format.ToString(), nfi)); + if (res.Length < _opts.FieldWidth) { + res.Insert(1, new string('0', _opts.FieldWidth - res.Length)); + } + _buf.Append(res); + } + } + + private void AppendNumeric(object val, bool fPos, char format, bool unsigned) { + bool isNegative = false; + + if (val is BigInteger && ((BigInteger)val).Sign == -1) + isNegative = true; + else if (val is int && (int)val < 0) + isNegative = true; + else if (val is float && (float)val < 0) + isNegative = true; + + if (isNegative && unsigned) { + val = val is BigInteger ? CastToUnsignedBigInteger(val as BigInteger) : (object)(uint)(int)val; + } + + if (fPos && (_opts.SignChar || _opts.Space)) { + string strval = (_opts.SignChar ? "+" : " ") + String.Format(nfi, "{0:" + format.ToString() + "}", val); + if (strval.Length < _opts.FieldWidth) { + _buf.Append(' ', _opts.FieldWidth - strval.Length); + } + _buf.Append(strval); + } else if (_opts.Precision == UnspecifiedPrecision) { + _buf.AppendFormat(nfi, "{0," + _opts.FieldWidth.ToString() + ":" + format + "}", val); + if (unsigned && isNegative) + _buf.Insert(0, ".."); + } else if (_opts.Precision < 100) { + //CLR formatting has a maximum precision of 100. + _buf.AppendFormat(nfi, "{0," + _opts.FieldWidth.ToString() + ":" + format + _opts.Precision.ToString() + "}", val); + } else { + StringBuilder res = new StringBuilder(); + res.AppendFormat("{0:" + format + "}", val); + if (res.Length < _opts.Precision) { + char padding = unsigned ? '.' : '0'; + res.Insert(0, new String(padding, _opts.Precision - res.Length)); + } + if (res.Length < _opts.FieldWidth) { + res.Insert(0, new String(' ', _opts.FieldWidth - res.Length)); + } + _buf.Append(res.ToString()); + } + + // If AdjustForG() sets opts.Precision == 0, it means that no significant digits should be displayed after + // the decimal point. ie. 123.4 should be displayed as "123", not "123.4". However, we might still need a + // decorative ".0". ie. to display "123.0" + if (_TrailingZeroAfterWholeFloat && (format == 'f') && _opts.Precision == 0) + _buf.Append(".0"); + } + + private void AppendLeftAdj(object val, bool fPos, char type) { + string str = String.Format(nfi, "{0:" + type.ToString() + "}", val); + if (fPos) { + if (_opts.SignChar) str = '+' + str; + else if (_opts.Space) str = ' ' + str; + } + _buf.Append(str); + if (str.Length < _opts.FieldWidth) _buf.Append(' ', _opts.FieldWidth - str.Length); + } + + private static bool NeedsAltForm(char format, char last) { + if (format == 'X' || format == 'x') return true; + + if (last == '0') return false; + return true; + } + + // Note backwards formats + private static string GetAltFormPrefixForRadix(char format, int radix) { + switch (radix) { + case 2: + return format == 'b' ? "b0" : "B0"; + case 8: return "0"; + case 16: return format + "0"; + } + return ""; + } + + private static uint[] _Mask = new uint[] { 0x0, 0x1, 0x0, 0x7, 0xF }; + private static char[] _UpperDigits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + private static char[] _LowerDigits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + private StringBuilder/*!*/ AppendBase(object/*!*/ value, int bitsToShift, bool lowerCase) { + if (value is BigInteger) + return AppendBaseBigInteger(value as BigInteger, bitsToShift, lowerCase); + + StringBuilder/*!*/ result = new StringBuilder(); + bool isNegative = IsNegative(value); + uint val = (uint)(int)value; + uint limit = isNegative ? 0xFFFFFFFF : 0; + uint mask = _Mask[bitsToShift]; + char[] digits = lowerCase ? _LowerDigits : _UpperDigits; + + while (val != limit) { + result.Append(digits[val & mask]); + val = val >> bitsToShift; + limit = limit >> bitsToShift; + } + + if (isNegative) + result.Append(digits[mask]); + + return result; + } + + private StringBuilder/*!*/ AppendBaseInt(int value, int radix) { + StringBuilder/*!*/ str = new StringBuilder(); + + if (value == 0) str.Append('0'); + while (value != 0) { + int digit = value % radix; + str.Append(_LowerDigits[digit]); + value /= radix; + } + return str; + } + + private StringBuilder/*!*/ AppendBaseUnsignedInt(uint value, uint radix) { + StringBuilder/*!*/ str = new StringBuilder(); + + if (value == 0) str.Append('0'); + while (value != 0) { + uint digit = value % radix; + str.Append(_LowerDigits[digit]); + value /= radix; + } + return str; + } + + private StringBuilder/*!*/ AppendBase2(object/*!*/ value, int radix, bool unsigned) { + if (value is BigInteger) + return AppendBaseBigInteger(value as BigInteger, radix); + + if (unsigned) + return AppendBaseInt((int)value, radix); + else + return AppendBaseUnsignedInt((uint)value, (uint)radix); + } + + private StringBuilder/*!*/ AppendBaseBigInteger(BigInteger/*!*/ value, int radix) { + StringBuilder/*!*/ str = new StringBuilder(); + if (value == 0) str.Append('0'); + while (value != 0) { + int digit = (value % radix).ToInt32(); + str.Append(_LowerDigits[digit]); + value /= radix; + } + return str; + } + + private BigInteger/*!*/ MakeBigIntegerFromByteArray(byte[] bytes) { + uint[] data = new uint[(bytes.Length / 4) + 1]; + + int j = 0; + for (int i = 0; i < bytes.Length; i += 4) { + uint word = 0; + int diff = bytes.Length - i; + if (diff > 3) { + word = (uint)bytes[i] | (uint)(bytes[i + 1] << 8) | (uint)(bytes[i + 2] << 16) | (uint)((uint)bytes[i + 3] << 24); + } else if (diff == 3) { + word = (uint)bytes[i] | (uint)(bytes[i + 1] << 8) | (uint)(bytes[i + 2] << 16); + } else if (diff == 2) { + word = (uint)bytes[i] | (uint)(bytes[i + 1] << 8); + } else if (diff == 1) { + word = (uint)bytes[i]; + } + data[j++] = word; + } + + return new BigInteger(1, data); + } + + private BigInteger/*!*/ CastToUnsignedBigInteger(BigInteger/*!*/ value) { + return MakeBigIntegerFromByteArray(value.ToByteArray()); + } + + private BigInteger/*!*/ GenerateMask(BigInteger/*!*/ value) { + byte[] bytes = new byte[value.ToByteArray().Length]; + for (int i = 0; i < bytes.Length; i++) { + bytes[i] = 0xFF; + } + return MakeBigIntegerFromByteArray(bytes); + } + + private StringBuilder/*!*/ AppendBaseBigInteger(BigInteger value, int bitsToShift, bool lowerCase) { + StringBuilder/*!*/ result = new StringBuilder(); + bool isNegative = value.Sign == -1; + BigInteger/*!*/ val = CastToUnsignedBigInteger(value); + BigInteger/*!*/ limit = isNegative ? GenerateMask(value) : new BigInteger(0); + uint mask = _Mask[bitsToShift]; + char[] digits = lowerCase ? _LowerDigits : _UpperDigits; + + while (val != limit) { + int t = (val & mask).ToInt32(); + result.Append(digits[(val & mask).ToInt32()]); + val >>= bitsToShift; + limit >>= bitsToShift; + } + + if (isNegative) + result.Append(digits[mask]); + + return result; + } + + private object/*!*/ Negate(object/*!*/ value) { + if (value is BigInteger) + return ((BigInteger)value).OnesComplement(); + else + return -((int)value); + } + + private bool IsNegative(object/*!*/ value) { + if (value is BigInteger) + return ((BigInteger)value).Sign == -1; + else + return (int)value < 0; + } + + /// + /// AppendBase appends an integer at the specified radix doing all the + /// special forms for Ruby. We have a copy and paste version of this + /// for BigInteger below that should be kept in sync. + /// + private void AppendBase(char format, int radix) { + object value = Protocols.ConvertToInteger(_context, _opts.Value); + + bool isNegative = IsNegative(value); + if (isNegative) { + // These options mean we're not looking at the one's complement + if (_opts.Space || _opts.SignChar) + value = Negate(value); + + // if negative number, the leading space has no impact + if (radix != 2 && radix != 8 && radix != 16) + _opts.Space = false; + } + + // we build up the number backwards inside a string builder, + // and after we've finished building this up we append the + // string to our output buffer backwards. + + StringBuilder str; + switch (radix) { + case 2: + str = AppendBase(value, 1, true); + break; + case 8: + str = AppendBase(value, 3, true); + break; + case 16: + str = AppendBase(value, 4, format == 'x'); + break; + default: + str = AppendBase2(value, 10, format == 'u'); + break; + } + + // pad out for additional precision + if (str.Length < _opts.Precision) { + int len = _opts.Precision - str.Length; + char padding = '0'; + if (radix == 2 && isNegative) + padding = '1'; + else if (radix == 8 && isNegative) + padding = '7'; + else if (radix == 16 && isNegative) + padding = format == 'x' ? 'f' : 'F'; + + str.Append(padding, len); + } + + // pad result to minimum field width + if (_opts.FieldWidth != 0) { + int signLen = (isNegative || _opts.SignChar) ? 1 : 0; + int spaceLen = _opts.Space ? 1 : 0; + int len = _opts.FieldWidth - (str.Length + signLen + spaceLen); + + if (len > 0) { + // we account for the size of the alternate form, if we'll end up adding it. + if (_opts.AltForm && NeedsAltForm(format, (!_opts.LeftAdj && _opts.ZeroPad) ? '0' : str[str.Length - 1])) { + len -= GetAltFormPrefixForRadix(format, radix).Length; + } + + if (len > 0) { + // and finally append the right form + if (_opts.LeftAdj) { + str.Insert(0, " ", len); + } else { + if (_opts.ZeroPad) { + str.Append('0', len); + } else { + _buf.Append(' ', len); + } + } + } + } + } + + // append the alternate form + if (_opts.AltForm && NeedsAltForm(format, str[str.Length - 1])) + str.Append(GetAltFormPrefixForRadix(format, radix)); + + // add any sign if necessary + if (isNegative) { + if (radix == 2 || radix == 8 || radix == 16) { + if (_opts.SignChar || _opts.Space) + _buf.Append('-'); + else if (!_opts.ZeroPad && _opts.Precision == -1) + _buf.Append(".."); + } else { + _buf.Append("-"); + } + } else if (_opts.SignChar) { + _buf.Append('+'); + } else if (_opts.Space) { + _buf.Append(' '); + } + + // append the final value + for (int i = str.Length - 1; i >= 0; i--) { + _buf.Append(str[i]); + } + } + + private void AppendBinary(char format) { + AppendBase(format, 2); + } + + private void AppendHex(char format) { + AppendBase(format, 16); + } + + private void AppendOctal() { + AppendBase('o', 8); + } + + private void AppendInspect() { + MutableString inspect = RubySites.Inspect(_context, _opts.Value); + if (KernelOps.Tainted(_context, inspect)) { + _tainted = true; + } + + AppendString(inspect); + } + + private void AppendString() { + MutableString/*!*/ str = Protocols.ConvertToString(_context, _opts.Value); + if (KernelOps.Tainted(_context, str)) { + _tainted = true; + } + + AppendString(str); + } + + private void AppendString(MutableString/*!*/ mutable) { + string str = mutable.ConvertToString(); + + if (_opts.Precision != UnspecifiedPrecision && str.Length > _opts.Precision) { + str = str.Substring(0, _opts.Precision); + } + + if (!_opts.LeftAdj && _opts.FieldWidth > str.Length) { + _buf.Append(' ', _opts.FieldWidth - str.Length); + } + + _buf.Append(str); + + if (_opts.LeftAdj && _opts.FieldWidth > str.Length) { + _buf.Append(' ', _opts.FieldWidth - str.Length); + } + } + + #endregion + + #region Private data structures + + // The conversion specifier format is as follows: + // % conversionFlags fieldWidth . precision conversionType + // where: + // mappingKey - value to be formatted + // conversionFlags - # 0 - + * + // conversionType - b c d E e f G g i o p s u X x % + // Note: + // conversionFlags can also contain: number$ + // where number is the index of the argument to get data from (uses 1-based indexing, so >= 1) + // This is called "abolute indexing", and if it's used anywhere it must be used everywhere. + // If absolute indexing is used, the "*" conversionFlag must be followed by number$ to indicate + // the (1-based) index of the argument containing the field width. + // Ex: + // %#4o - Display argument as octal and prepend with leading 0 if necessary, + // for a total of at least 4 characters + + [Flags] + internal enum FormatOptions { + ZeroPad = 0x01, // Use zero-padding to fit FieldWidth + LeftAdj = 0x02, // Use left-adjustment to fit FieldWidth. Overrides ZeroPad + AltForm = 0x04, // Add a leading 0 if necessary for octal, or add a leading 0x or 0X for hex + Space = 0x08, // Leave a white-space + SignChar = 0x10 // Force usage of a sign char even if the value is positive + } + + internal struct FormatSettings { + + #region FormatOptions property accessors + + public bool ZeroPad { + get { + return ((Options & FormatOptions.ZeroPad) != 0); + } + set { + if (value) { + Options |= FormatOptions.ZeroPad; + } else { + Options &= (~FormatOptions.ZeroPad); + } + } + } + public bool LeftAdj { + get { + return ((Options & FormatOptions.LeftAdj) != 0); + } + set { + if (value) { + Options |= FormatOptions.LeftAdj; + } else { + Options &= (~FormatOptions.LeftAdj); + } + } + } + public bool AltForm { + get { + return ((Options & FormatOptions.AltForm) != 0); + } + set { + if (value) { + Options |= FormatOptions.AltForm; + } else { + Options &= (~FormatOptions.AltForm); + } + } + } + public bool Space { + get { + return ((Options & FormatOptions.Space) != 0); + } + set { + if (value) { + Options |= FormatOptions.Space; + } else { + Options &= (~FormatOptions.Space); + } + } + } + public bool SignChar { + get { + return ((Options & FormatOptions.SignChar) != 0); + } + set { + if (value) { + Options |= FormatOptions.SignChar; + } else { + Options &= (~FormatOptions.SignChar); + } + } + } + #endregion + + internal FormatOptions Options; + + // Minimum number of characters that the entire formatted string should occupy. + // Smaller results will be left-padded with white-space or zeros depending on Options + internal int FieldWidth; + + // Number of significant digits to display, before and after the decimal point. + // For floats, it gets adjusted to the number of digits to display after the decimal point since + // that is the value required by StringBuilder.AppendFormat. + // For clarity, we should break this up into the two values - the precision specified by the + // format string, and the value to be passed in to StringBuilder.AppendFormat + internal int Precision; + + internal object Value; + + // If using absolute indexing, the index of the argument that has the data value + internal int? ArgIndex; + } + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/StringOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/StringOps.cs new file mode 100644 index 0000000000..ebac1e83b6 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/StringOps.cs @@ -0,0 +1,53 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Text; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + // Extension methods for System.String + [RubyClass("ClrString", Extends = typeof(string), MixinInterfaces = true)] + public static class StringOps { + + [RubyMethod("to_str", RubyMethodAttributes.PublicInstance)] + [RubyMethod("to_s", RubyMethodAttributes.PublicInstance)] + public static MutableString/*!*/ ToStr(string/*!*/ self) { + return MutableString.Create(self); + } + + [RubyMethod("to_clr_string", RubyMethodAttributes.PublicInstance)] + public static string/*!*/ ToClrString(string/*!*/ self) { + return self; + } + + [RubyMethod("inspect", RubyMethodAttributes.PublicInstance)] + public static MutableString/*!*/ Inspect(string/*!*/ self) { + StringBuilder result = new StringBuilder(); + result.Append('"'); + for (int i = 0; i < self.Length; i++) { + MutableStringOps.AppendStringRepresentationOfChar(result, self[i], i + 1 < self.Length ? self[i + 1] : -1, false); + } + + result.Append('"'); + return MutableString.Create(result.ToString()); + } + + [RubyMethod("===", RubyMethodAttributes.PublicInstance)] + [RubyMethod("==", RubyMethodAttributes.PublicInstance)] + public static bool Equals(string str, object other) { + return str.Equals(other); + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/StructOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/StructOps.cs new file mode 100644 index 0000000000..f0aee06e9f --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/StructOps.cs @@ -0,0 +1,314 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Scripting; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Actions; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Ast = System.Linq.Expressions.Expression; +using System.Runtime.InteropServices; + +namespace IronRuby.Builtins { + + [RubyClass("Struct", Extends = typeof(RubyStruct)), Includes(typeof(Enumerable))] + public static partial class RubyStructOps { + [RubyConstructor] + public static void AllocatorUndefined(RubyClass/*!*/ self, params object[] args) { + throw RubyExceptions.CreateAllocatorUndefinedError(self); + } + + [RubyMethod("new", RubyMethodAttributes.PublicSingleton)] + public static object NewAnonymousStruct(BlockParam block, RubyClass/*!*/ self, int className, + [NotNull]params object[]/*!*/ attributeNames) { + + return CreateAnonymousWithFirstAttribute(block, self, RubyOps.ConvertFixnumToSymbol(self.Context, className), attributeNames); + } + + [RubyMethod("new", RubyMethodAttributes.PublicSingleton)] + public static object NewAnonymousStruct(BlockParam block, RubyClass/*!*/ self, SymbolId className, + [NotNull]params object[]/*!*/ attributeNames) { + + return CreateAnonymousWithFirstAttribute(block, self, RubyOps.ConvertSymbolIdToSymbol(className), attributeNames); + } + + [RubyMethod("new", RubyMethodAttributes.PublicSingleton)] + public static object NewAnonymousStruct(BlockParam block, RubyClass/*!*/ self, [NotNull]string/*!*/ className, + [NotNull]params object[]/*!*/ attributeNames) { + + return CreateAnonymousWithFirstAttribute(block, self, className, attributeNames); + } + + [RubyMethod("new", RubyMethodAttributes.PublicSingleton)] + public static object NewStruct(BlockParam block, RubyClass/*!*/ self, [DefaultProtocol, Optional]MutableString className, + [NotNull]params object[]/*!*/ attributeNames) { + + string[] symbols = Protocols.CastToSymbols(self.Context, attributeNames); + + if (className == null) { + return Create(block, self, null, symbols); + } + + string strName = className.ConvertToString(); + RubyUtils.CheckConstantName(strName); + return Create(block, self, strName, symbols); + } + + public static object CreateAnonymousWithFirstAttribute(BlockParam block, RubyClass/*!*/ self, + string/*!*/ firstAttribute, object[]/*!*/ attributeNames) { + + return Create(block, self, null, ArrayUtils.Insert(firstAttribute, Protocols.CastToSymbols(self.Context, attributeNames))); + } + + /// + /// Struct#new + /// Creates Struct classes with the specified name and members + /// + private static object Create(BlockParam block, RubyClass/*!*/ self, string className, string/*!*/[]/*!*/ attributeNames) { + var result = RubyStruct.DefineStruct(self, className, attributeNames); + + if (block != null) { + return RubyUtils.EvaluateInModule(result, block, result); + } + + return result; + } + + // Reinitialization. Called only from derived struct's initializer. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static void Reinitialize(RubyStruct/*!*/ self, [NotNull]params object[]/*!*/ items) { + self.SetValues(items); + } + + // Copies data from one Struct instance into another: + [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)] + public static RubyStruct/*!*/ InitializeCopy(RubyStruct/*!*/ self, [NotNull]RubyStruct/*!*/ source) { + if (self.Class != source.Class) { + throw RubyExceptions.CreateTypeError("wrong argument class"); + } + + self.SetValues(source.Values); + return self; + } + + [RubyMethod("members")] + public static RubyArray/*!*/ GetMembers(RubyStruct/*!*/ self) { + return RubyStruct.GetMembers(self); + } + + [RubyMethod("length")] + [RubyMethod("size")] + public static int GetSize(RubyStruct/*!*/ self) { + return self.ItemCount; + } + + [RubyMethod("[]")] + public static object GetValue(RubyStruct/*!*/ self, int index) { + return self[NormalizeIndex(self.ItemCount, index)]; + } + + [RubyMethod("[]")] + public static object GetValue(RubyStruct/*!*/ self, SymbolId name) { + return self[SymbolTable.IdToString(name)]; + } + + [RubyMethod("[]")] + public static object GetValue(RubyStruct/*!*/ self, MutableString/*!*/ name) { + return self[name.ConvertToString()]; + } + + [RubyMethod("[]")] + public static object GetValue(RubyStruct/*!*/ self, object index) { + return self[NormalizeIndex(self.ItemCount, Protocols.CastToFixnum(self.Class.Context, index))]; + } + + [RubyMethod("[]=")] + public static object SetValue(RubyStruct/*!*/ self, int index, object value) { + return self[NormalizeIndex(self.ItemCount, index)] = value; + } + + [RubyMethod("[]=")] + public static object SetValue(RubyStruct/*!*/ self, SymbolId name, object value) { + return self[SymbolTable.IdToString(name)] = value; + } + + [RubyMethod("[]=")] + public static object SetValue(RubyStruct/*!*/ self, MutableString/*!*/ name, object value) { + return self[name.ConvertToString()] = value; + } + + [RubyMethod("[]=")] + public static object SetValue(RubyStruct/*!*/ self, object index, object value) { + return self[NormalizeIndex(self.ItemCount, Protocols.CastToFixnum(self.Class.Context, index))] = value; + } + + [RubyMethod("each")] + public static object Each(BlockParam block, RubyStruct/*!*/ self) { + if (block == null && self.ItemCount > 0) { + throw RubyExceptions.NoBlockGiven(); + } + + foreach (var value in self.Values) { + object result; + if (block.Yield(value, out result)) { + return result; + } + } + + return self; + } + + [RubyMethod("each_pair")] + public static object EachPair(BlockParam block, RubyStruct/*!*/ self) { + if (block == null && self.ItemCount > 0) { + throw RubyExceptions.NoBlockGiven(); + } + + foreach (KeyValuePair entry in self.GetItems()) { + object result; + if (block.Yield(SymbolTable.StringToId(entry.Key), entry.Value, out result)) { + return result; + } + } + + return self; + } + + [RubyMethod("to_a")] + [RubyMethod("values")] + public static RubyArray/*!*/ Values(RubyStruct/*!*/ self) { + return new RubyArray(self.Values); + } + + [RubyMethod("hash")] + public static int Hash(RubyStruct/*!*/ self) { + return self.GetHashCode(); + } + + [RubyMethod("eql?")] + public static bool Equal(RubyStruct/*!*/ self, object other) { + return self.Equals(other); + } + + // same pattern as RubyStruct.Equals, but we need to call == instead of eql? + [RubyMethod("==")] + public static bool Equals(RubyStruct/*!*/ self, object obj) { + var other = obj as RubyStruct; + if (!self.StructReferenceEquals(other)) { + return false; + } + + Debug.Assert(self.ItemCount == other.ItemCount); + for (int i = 0; i < self.Values.Length; i++) { + if (!RubySites.Equal(self.Class.Context, self.Values[i], other.Values[i])) { + return false; + } + } + + return true; + } + + [RubyMethod("to_s")] + [RubyMethod("inspect")] + public static MutableString/*!*/ Inspect(RubyStruct/*!*/ self) { + RubyContext context = self.Class.Context; + + using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(self)) { + // # + MutableString str = MutableString.Create("#"); + } + str.Append(' '); + + object[] data = self.Values; + var members = self.GetNames(); + for (int i = 0; i < data.Length; i++) { + if (i != 0) { + str.Append(", "); + } + str.Append(members[i]); + str.Append("="); + str.Append(RubySites.Inspect(context, data[i])); + } + str.Append('>'); + return str; + } + } + + // For some unknown reason Struct defines the method even though it is mixed in from Enumerable + // Until we discover the difference, delegate to Enumerable#select + [RubyMethod("select")] + public static RubyArray/*!*/ Select(BlockParam predicate, RubyStruct/*!*/ self) { + return Enumerable.Select(self.Class.Context, predicate, self); + } + + // equivalent to Array#values_at over the data array + [RubyMethod("values_at")] + public static RubyArray/*!*/ ValuesAt(RubyStruct/*!*/ self, [NotNull]params object[] values) { + RubyArray result = new RubyArray(); + object[] data = self.Values; + + for (int i = 0; i < values.Length; ++i) { + Range range = values[i] as Range; + if (range != null) { + bool excludeEnd; + int begin, end; + Protocols.ConvertToIntegerRange(self.Class.Context, range, out begin, out end, out excludeEnd); + + if (excludeEnd) { + end -= 1; + } + + begin = NormalizeIndex(data.Length, begin); + end = NormalizeIndex(data.Length, end); + Debug.Assert(end - begin <= data.Length); // because we normalized the indicies + + if (end - begin > 0) { + result.Capacity += (end - begin); + for (int j = begin; j <= end; j++) { + result.Add(data[j]); + } + } + } else { + int index = NormalizeIndex(data.Length, Protocols.CastToFixnum(self.Class.Context, values[i])); + result.Add(data[index]); + } + } + + return result; + } + + private static int NormalizeIndex(int itemCount, int index) { + int normalized = index; + if (normalized < 0) { + normalized += itemCount; + } + if (normalized >= 0 && normalized < itemCount) { + return normalized; + } + // MRI reports the normalized index, but we'll report the original one + throw RubyExceptions.CreateIndexError(String.Format("offset {0} too small for struct (size:{1})", index, itemCount)); + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/SymbolOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/SymbolOps.cs new file mode 100644 index 0000000000..164cddd7e7 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/SymbolOps.cs @@ -0,0 +1,137 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting; +using IronRuby.Runtime; +using IronRuby.Compiler; + +namespace IronRuby.Builtins { + + [RubyClass("Symbol", Extends = typeof(SymbolId), Inherits = typeof(Object))] + public static class SymbolOps { + + #region Public Instance Methods + + [RubyMethod("id2name")] + [RubyMethod("to_s")] + public static MutableString/*!*/ ToString(SymbolId self) { + return MutableString.Create(SymbolTable.IdToString(self)); + } + + [RubyMethod("inspect")] + public static MutableString/*!*/ Inspect(SymbolId self) { + var str = SymbolTable.IdToString(self); + + // simple cases: + if ( + Tokenizer.IsMethodName(str) || + Tokenizer.IsConstantName(str) || + Tokenizer.IsInstanceVariableName(str) || + Tokenizer.IsClassVariableName(str) || + Tokenizer.IsGlobalVariableName(str) + ) { + return MutableString.CreateMutable(":", 1 + str.Length).Append(str); + } + + // TODO: this is neither efficient nor complete. + // Any string that parses as 'sym' should not be quoted. + switch (str) { + case null: + // Ruby doesn't allow empty symbols, we can get one from outside though: + return MutableString.Create(":\"\""); + + case "|": + case "^": + case "&": + case "<=>": + case "==": + case "===": + case "=~": + case ">": + case ">=": + case "<": + case "<=": + case "<<": + case ">>": + case "+": + case "-": + case "*": + case "/": + case "%": + case "**": + case "~": + case "+@": + case "-@": + case "[]": + case "[]=": + case "`": + + case "$!": + case "$@": + case "$,": + case "$;": + case "$/": + case "$\\": + case "$*": + case "$$": + case "$?": + case "$=": + case "$:": + case "$\"": + case "$<": + case "$>": + case "$.": + case "$~": + case "$&": + case "$`": + case "$'": + case "$+": + return MutableString.CreateMutable(":", 1 + str.Length).Append(str); + } + + return MutableString.CreateMutable(":\"", 3 + str.Length).Append(str).Append('"'); + } + + [RubyMethod("to_i")] + [RubyMethod("to_int")] + public static int ToInteger(SymbolId self) { + return self.Id; + } + + [RubyMethod("to_sym")] + public static SymbolId ToSymbol(SymbolId self) { + return self; + } + + [RubyMethod("to_clr_string")] + public static string ToClrString(SymbolId self) { + return SymbolTable.IdToString(self); + } + + #endregion + + #region Public Singleton Methods + + [RubyMethod("all_symbols", RubyMethodAttributes.PublicSingleton)] + public static List/*!*/ GetAllSymbols(object self) { + // TODO: + throw new NotImplementedError(); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ThreadGroup.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ThreadGroup.cs new file mode 100644 index 0000000000..7e091c861c --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ThreadGroup.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Threading; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + [RubyClass("ThreadGroup", Inherits = typeof(object))] + public class ThreadGroup { + [RubyMethod("add")] + public static ThreadGroup/*!*/ Add([NotNull]ThreadGroup/*!*/ self, [NotNull]Thread/*!*/ thread) { + ThreadOps.RubyThreadInfo.FromThread(thread).Group = self; + return self; + } + + // enclose + // enclosed? + + [RubyMethod("list")] + public static RubyArray/*!*/ List([NotNull]ThreadGroup/*!*/ self) { + ThreadOps.RubyThreadInfo[] threads = ThreadOps.RubyThreadInfo.Threads; + RubyArray result = new RubyArray(threads.Length); + foreach (ThreadOps.RubyThreadInfo threadInfo in threads) { + Thread thread = threadInfo.Thread; + if (thread != null && threadInfo.Group == self) { + result.Add(thread); + } + } + + return result; + } + + [RubyConstant] + public readonly static ThreadGroup Default = new ThreadGroup(); + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ThreadOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ThreadOps.cs new file mode 100644 index 0000000000..9914384b34 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/ThreadOps.cs @@ -0,0 +1,458 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.Scripting; +using System.Dynamic.Binders; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Threading; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + [RubyClass("Thread", Extends = typeof(Thread), Inherits = typeof(object))] + public static class ThreadOps { + static bool _globalAbortOnException; + + internal class RubyThreadInfo { + private static readonly Dictionary _mapping = new Dictionary(); + private readonly Dictionary _threadLocalStorage; + private readonly int _id; + private ThreadGroup _group; + private readonly Thread _thread; + private bool _abortOnException; + + private RubyThreadInfo(Thread thread) { + _threadLocalStorage = new Dictionary(); + _group = ThreadGroup.Default; + _thread = thread; + _id = thread.ManagedThreadId; + } + + internal static RubyThreadInfo FromThread(Thread t) { + RubyThreadInfo result; + lock (_mapping) { + int key = t.ManagedThreadId; + if (!_mapping.TryGetValue(key, out result)) { + result = new RubyThreadInfo(t); + _mapping[key] = result; + } + } + return result; + } + + internal static void RegisterThread(Thread t) { + FromThread(t); + } + + internal object this[SymbolId key] { + get { + lock (_threadLocalStorage) { + object result; + if (!_threadLocalStorage.TryGetValue(key, out result)) { + result = null; + } + return result; + } + } + set { + lock (_threadLocalStorage) { + if (value == null) { + _threadLocalStorage.Remove(key); + } else { + _threadLocalStorage[key] = value; + } + } + } + } + + internal bool HasKey(SymbolId key) { + lock (_threadLocalStorage) { + return _threadLocalStorage.ContainsKey(key); + } + } + + internal RubyArray GetKeys() { + lock (_threadLocalStorage) { + RubyArray result = new RubyArray(_threadLocalStorage.Count); + foreach (SymbolId key in _threadLocalStorage.Keys) { + result.Add(key); + } + return result; + } + } + + internal ThreadGroup Group { + get { + return _group; + } + set { + Interlocked.Exchange(ref _group, value); + } + } + + internal Thread Thread { + get { + return _thread; + } + } + + internal Exception Exception { get; set; } + internal object Result { get; set; } + + internal bool AbortOnException { + get { + return _abortOnException; + } + set { + _abortOnException = value; + } + } + + internal static RubyThreadInfo[] Threads { + get { + lock (_mapping) { + List result = new List(_mapping.Count); + foreach (KeyValuePair entry in _mapping) { + if (entry.Value.Thread.IsAlive) { + result.Add(entry.Value); + } + } + return result.ToArray(); + } + } + } + } + + // declared private instance methods: + // initialize + // declared protected instance methods: + // declared public instance methods: + + private static Exception MakeKeyTypeException(RubyContext/*!*/ context, object key) { + if (key == null) { + return RubyExceptions.CreateTypeError("nil is not a symbol"); + } else { + MutableString repr = Protocols.ConvertToString(context, key); + string message = String.Format("{0} is not a symbol", repr.ToString()); + return RubyExceptions.CreateArgumentError(message); + } + } + + [RubyMethod("[]")] + public static object GetElement(Thread/*!*/ self, SymbolId key) { + RubyThreadInfo info = RubyThreadInfo.FromThread(self); + return info[key]; + } + + [RubyMethod("[]")] + public static object GetElement(Thread/*!*/ self, [NotNull]MutableString/*!*/ key) { + return GetElement(self, SymbolTable.StringToId(key.ConvertToString())); + } + + [RubyMethod("[]")] + public static object GetElement(RubyContext/*!*/ context, Thread/*!*/ self, object key) { + throw MakeKeyTypeException(context, key); + } + + [RubyMethod("[]=")] + public static object SetElement(Thread/*!*/ self, SymbolId key, object value) { + RubyThreadInfo info = RubyThreadInfo.FromThread(self); + info[key] = value; + return value; + } + + [RubyMethod("[]=")] + public static object SetElement(Thread/*!*/ self, [NotNull]MutableString/*!*/ key, object value) { + return SetElement(self, SymbolTable.StringToId(key.ConvertToString()), value); + } + + [RubyMethod("[]=")] + public static object SetElement(RubyContext/*!*/ context, Thread/*!*/ self, object key, object value) { + throw MakeKeyTypeException(context, key); + } + + [RubyMethod("abort_on_exception")] + public static object AbortOnException(Thread/*!*/ self) { + RubyThreadInfo info = RubyThreadInfo.FromThread(self); + return info.AbortOnException; + } + + [RubyMethod("abort_on_exception=")] + public static object AbortOnException(Thread/*!*/ self, bool value) { + RubyThreadInfo info = RubyThreadInfo.FromThread(self); + info.AbortOnException = value; + return value; + } + + [RubyMethod("alive?")] + public static bool IsAlive(Thread/*!*/ self) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + return self.IsAlive; + } + + [RubyMethod("group")] + public static ThreadGroup Group(Thread/*!*/ self) { + RubyThreadInfo info = RubyThreadInfo.FromThread(self); + return info.Group; + } + + [RubyMethod("inspect")] + public static MutableString/*!*/ Inspect(RubyContext/*!*/ context, Thread/*!*/ self) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + + MutableString result = MutableString.CreateMutable(); + result.Append("#<"); + result.Append(RubyUtils.GetClassName(context, self)); + result.Append(':'); + RubyUtils.AppendFormatHexObjectId(result, RubyUtils.GetObjectId(context, self)); + result.Append(' '); + + if ((self.ThreadState & ThreadState.WaitSleepJoin) != 0) { + result.Append("sleep"); + } else if ((self.ThreadState & (ThreadState.Stopped | ThreadState.Aborted | ThreadState.AbortRequested)) != 0) { + result.Append("dead"); + } else { + result.Append("run"); + } + + result.Append('>'); + return result; + } + + [RubyMethod("join")] + public static Thread/*!*/ Join(Thread/*!*/ self) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + + if (!(self.ThreadState == ThreadState.AbortRequested || self.ThreadState == ThreadState.Aborted)) { + self.Join(); + } + + Exception threadException = RubyThreadInfo.FromThread(self).Exception; + if (threadException != null) { + throw threadException; + } + + return self; + } + + [RubyMethod("join")] + public static Thread/*!*/ Join(Thread/*!*/ self, double seconds) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + + if (!(self.ThreadState == ThreadState.AbortRequested || self.ThreadState == ThreadState.Aborted)) { + double ms = seconds * 1000; + int timeout = (ms < Int32.MinValue || ms > Int32.MaxValue) ? Timeout.Infinite : (int)ms; + if (!self.Join(timeout)) { + return null; + } + } + + Exception threadException = RubyThreadInfo.FromThread(self).Exception; + if (threadException != null) { + throw threadException; + } + + return self; + } + + [RubyMethod("kill")] + [RubyMethod("exit")] + [RubyMethod("terminate")] + public static Thread Kill(Thread/*!*/ self) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + self.Abort(); + return self; + } + + [RubyMethod("key?")] + public static object HasKey(Thread/*!*/ self, SymbolId key) { + RubyThreadInfo info = RubyThreadInfo.FromThread(self); + return info.HasKey(key); + } + + [RubyMethod("key?")] + public static object HasKey(Thread/*!*/ self, [NotNull]MutableString/*!*/ key) { + return HasKey(self, SymbolTable.StringToId(key.ConvertToString())); + } + + [RubyMethod("key?")] + public static object HasKey(RubyContext/*!*/ context, Thread/*!*/ self, object key) { + throw MakeKeyTypeException(context, key); + } + + [RubyMethod("keys")] + public static object Keys(RubyContext/*!*/ context, Thread/*!*/ self) { + RubyThreadInfo info = RubyThreadInfo.FromThread(self); + return info.GetKeys(); + } + + // priority + // priority= + // raise + // safe_level + + // TODO: these two methods interrupt a sleeping thread via the Thread.Interrupt API. + // Unfortunately, this API interrupts the sleeping thread by throwing a ThreadInterruptedException. + // In many Ruby programs (eg the specs) this causes the thread to terminate, which is NOT the + // expected behavior. This is tracked by Rubyforge bug # 21157 + +#if !SILVERLIGHT + [RubyMethod("run", BuildConfig = "!SILVERLIGHT")] + [RubyMethod("wakeup", BuildConfig = "!SILVERLIGHT")] + public static void Run(Thread/*!*/ self) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + self.Interrupt(); + } +#endif + + [RubyMethod("status")] + public static object Status(Thread/*!*/ self) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + switch (self.ThreadState) { + case ThreadState.WaitSleepJoin: + return MutableString.Create("sleep"); + case ThreadState.Running: + return MutableString.Create("run"); + case ThreadState.Aborted: + case ThreadState.AbortRequested: + return null; + case ThreadState.Stopped: + case ThreadState.StopRequested: + return false; + default: + throw new ArgumentException("unknown thread status: " + self.ThreadState.ToString()); + } + } + + [RubyMethod("value")] + public static object Value(Thread/*!*/ self) { + Join(self); + return RubyThreadInfo.FromThread(self).Result; + } + + // stop? + + // declared singleton methods + + [RubyMethod("abort_on_exception", RubyMethodAttributes.PublicSingleton)] + public static object GlobalAbortOnException(object self) { + return _globalAbortOnException; + } + + [RubyMethod("abort_on_exception=", RubyMethodAttributes.PublicSingleton)] + public static object GlobalAbortOnException(object self, bool value) { + _globalAbortOnException = value; + return value; + } + + // critical + // critical= + + [RubyMethod("critical", RubyMethodAttributes.PublicSingleton)] + public static bool Critical(object self) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + return false; + } + + [RubyMethod("critical=", RubyMethodAttributes.PublicSingleton)] + public static bool Critical(object self, bool value) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + return false; + } + + [RubyMethod("current", RubyMethodAttributes.PublicSingleton)] + public static Thread/*!*/ Current(object self) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + return Thread.CurrentThread; + } + + // exclusive + // fork + [RubyMethod("list", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ List(object self) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + + RubyThreadInfo[] threads = RubyThreadInfo.Threads; + RubyArray result = new RubyArray(threads.Length); + foreach (RubyThreadInfo threadInfo in threads) { + Thread thread = threadInfo.Thread; + if (thread != null) { + result.Add(thread); + } + } + + return result; + } + + // main + + [RubyMethod("new", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("start", RubyMethodAttributes.PublicSingleton)] + public static Thread/*!*/ CreateThread(RubyContext/*!*/ context, BlockParam startRoutine, object self, [NotNull]params object[]/*!*/ args) { + if (startRoutine == null) { + throw new ThreadError("must be called with a block"); + } + ThreadGroup group = Group(Thread.CurrentThread); + Thread result = new Thread(new ThreadStart(delegate() { + RubyThreadInfo info = RubyThreadInfo.FromThread(Thread.CurrentThread); + info.Group = group; + + try { + object threadResult; + // TODO: break? + startRoutine.Yield(args, out threadResult); + info.Result = threadResult; +#if !SILVERLIGHT + } catch (ThreadInterruptedException) { + // Do nothing with this for now +#endif + } catch (Exception e) { + info.Exception = e; + + Utils.Log( + e.Message + "\r\n\r\n" + + e.StackTrace + "\r\n\r\n" + + IListOps.Join(context, RubyExceptionData.GetInstance(e).Backtrace).ToString(), + "THREAD" + ); + + if (_globalAbortOnException || info.AbortOnException) { + throw; + } + } + })); + + result.Start(); + return result; + } + + [RubyMethod("pass", RubyMethodAttributes.PublicSingleton)] + public static void Yield(object self) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + Thread.Sleep(0); + } + + [RubyMethod("stop", RubyMethodAttributes.PublicSingleton)] + public static void Stop(object self) { + RubyThreadInfo.RegisterThread(Thread.CurrentThread); + // TODO: MRI throws an exception if you try to stop the main thread + Thread.Sleep(Timeout.Infinite); + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/TimeOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/TimeOps.cs new file mode 100644 index 0000000000..aba63cf2a9 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/TimeOps.cs @@ -0,0 +1,564 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + [RubyClass("Time", Extends = typeof(DateTime), Inherits = typeof(Object)), Includes(typeof(Comparable))] + public static class TimeOps { + readonly static DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); //January 1, 1970 00:00 UTC + + [RubyConstructor] + public static DateTime Create(RubyClass/*!*/ self) { + return DateTime.Now; + } + + // TODO: I removed all of the constructor overloads since Ruby doesn't define any non-default constructors for Time. + // In the future, however, we need to fix this problem per RubyForge bug #20035 + + #region "Singleton Methods" + + #region at + + private static long microsecondsToTicks(long microseconds) { + return microseconds * 10; + } + + private static long secondsToTicks(long seconds) { + return seconds * 10000000; + } + + [RubyMethod("at", RubyMethodAttributes.PublicSingleton)] + public static DateTime Create(object/*!*/ self, DateTime other) { + return new DateTime(other.Ticks, other.Kind); + } + + [RubyMethod("at", RubyMethodAttributes.PublicSingleton)] + public static DateTime Create(object/*!*/ self, double seconds) { + return epoch.ToLocalTime().AddSeconds(seconds); + } + + + [RubyMethod("at", RubyMethodAttributes.PublicSingleton)] + public static DateTime Create(object/*!*/ self, long seconds, long microseconds) { + long ticks = epoch.ToLocalTime().Ticks + secondsToTicks(seconds) + microsecondsToTicks(microseconds); + return new DateTime(ticks); + } + + #endregion + + [RubyMethod("now", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateTime(object/*!*/ self) { + return DateTime.Now; + } + + [RubyMethod("today", RubyMethodAttributes.PublicSingleton)] + public static DateTime Today(object self) { + return DateTime.Today; + } + + #region local, mktime + + [RubyMethod("local", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("mktime", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateLocalTime(object/*!*/ self, int year) { + return new DateTime(year, 1, 1); + } + + [RubyMethod("local", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("mktime", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateLocalTime(object/*!*/ self, int year, int month) { + return new DateTime(year, month, 1); + } + + [RubyMethod("local", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("mktime", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateLocalTime(object/*!*/ self, int year, int month, int day) { + return new DateTime(year, month, day); + } + + [RubyMethod("local", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("mktime", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateLocalTime(object/*!*/ self, int year, int month, int day, int hour) { + return new DateTime(year, month, day, hour, 0, 0); + } + + [RubyMethod("local", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("mktime", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateLocalTime(object/*!*/ self, int year, int month, int day, int hour, int minute) { + return new DateTime(year, month, day, hour, minute, 0); + } + + [RubyMethod("local", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("mktime", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateLocalTime(object/*!*/ self, int year, int month, int day, int hour, int minute, int second) { + return new DateTime(year, month, day, hour, minute, second); + } + + [RubyMethod("local", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("mktime", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateLocalTime(object/*!*/ self, int year, int month, int day, int hour, int minute, int second, int microsecond) { + return new DateTime(year, month, day, hour, minute, second).AddTicks(microsecond*10); + } + + private static int GetComponent(RubyContext/*!*/ context, object[] components, int index, int defValue) { + if (index >= components.Length || components[index] == null) { + return defValue; + } + return Protocols.CastToFixnum(context, components[index]); + } + + [RubyMethod("local", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("mktime", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateLocalTime(RubyContext/*!*/ context, object/*!*/ self, [NotNull]params object[]/*!*/ components) { + if (components.Length > 7 || components.Length == 0) { + throw RubyExceptions.CreateArgumentError(String.Format("wrong number of arguments ({0} for 7)", components.Length)); + } + return new DateTime(Protocols.CastToFixnum(context, components[0]), GetComponent(context, components, 1, 1), + GetComponent(context, components, 2, 1), GetComponent(context, components, 3, 0), GetComponent(context, components, 4, 0), + GetComponent(context, components, 5, 0), GetComponent(context, components, 6, 0)); + } + + #endregion + + #region utc, gm + + [RubyMethod("utc", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("gm", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateGmtTime(object/*!*/ self, int year) { + return new DateTime(year, 1, 1, 0, 0, 0, DateTimeKind.Utc); + } + + [RubyMethod("utc", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("gm", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateGmtTime(object/*!*/ self, int year, int month) { + return new DateTime(year, month, 1, 0, 0, 0, DateTimeKind.Utc); + } + + [RubyMethod("utc", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("gm", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateGmtTime(object/*!*/ self, int year, int month, int day) { + return new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc); + } + + [RubyMethod("utc", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("gm", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateGmtTime(object/*!*/ self, int year, int month, int day, int hour) { + return new DateTime(year, month, day, hour, 0, 0, DateTimeKind.Utc); + } + + [RubyMethod("utc", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("gm", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateGmtTime(object/*!*/ self, int year, int month, int day, int hour, int minute) { + return new DateTime(year, month, day, hour, minute, 0, DateTimeKind.Utc); + } + + [RubyMethod("utc", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("gm", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateGmtTime(object/*!*/ self, int year, int month, int day, int hour, int minute, int second) { + return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc); + } + + [RubyMethod("utc", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("gm", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateGmtTime(object/*!*/ self, int year, int month, int day, int hour, int minute, int second, int microsecond) { + return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc).AddTicks(microsecond * 10); + } + + [RubyMethod("utc", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("gm", RubyMethodAttributes.PublicSingleton)] + public static DateTime CreateGmtTime(RubyContext/*!*/ context, object/*!*/ self, [NotNull]params object[]/*!*/ components) { + if (components.Length > 7 || components.Length == 0) { + throw RubyExceptions.CreateArgumentError(String.Format("wrong number of arguments ({0} for 7)", components.Length)); + } + return new DateTime(Protocols.CastToFixnum(context, components[0]), GetComponent(context, components, 1, 1), + GetComponent(context, components, 2, 1), GetComponent(context, components, 3, 0), GetComponent(context, components, 4, 0), + GetComponent(context, components, 5, 0), DateTimeKind.Utc).AddTicks(GetComponent(context, components, 6, 0)*10); + } + + #endregion + + #endregion "Singleton Methods" + + #region _dump, _load + + [RubyMethod("_dump")] + public static MutableString/*!*/ Dump(RubyContext/*!*/ context, DateTime self, [Optional]int depth) { + if (self.Year < 1900) { + throw RubyExceptions.CreateTypeError("unable to marshal time"); + } + + uint dword1 = 0x80000000; + // Uncomment for Ruby 1.9 compat? + // if (self.Kind == DateTimeKind.Utc) { + // dword1 |= 0x40000000; + // } else { + self = self.ToUniversalTime(); + dword1 |= (unchecked((uint)(self.Year - 1900)) << 14); + dword1 |= (unchecked((uint)(self.Month - 1)) << 10); + dword1 |= ((uint)self.Day << 5); + dword1 |= ((uint)self.Hour); + + uint dword2 = 0; + dword2 |= ((uint)self.Minute << 26); + dword2 |= ((uint)self.Second << 20); + dword2 |= ((uint)((self.Ticks % 10000000) / 10)); + + MemoryStream buf = new MemoryStream(8); + BinaryWriter writer = new BinaryWriter(buf); + writer.Write(dword1); + writer.Write(dword2); + + return MutableString.CreateBinary(buf.ToArray()); + } + + private static uint GetUint(byte[] data, int start) { + Assert.NotNull(data); + return (((((((uint)data[start + 3] << 8) + (uint)data[start + 2]) << 8) + (uint)data[start + 1]) << 8) + (uint)data[start + 0]); + } + + [RubyMethod("_load", RubyMethodAttributes.PublicSingleton)] + public static DateTime Load(RubyContext/*!*/ context, object/*!*/ self, [NotNull]MutableString time) { + byte[] data = time.ConvertToBytes(); + if (data.Length != 8 || (data[3] & 0x80) != 0x80) { + throw RubyExceptions.CreateTypeError("marshaled time format differ"); + } + bool isUtc = (data[3] & 0x40) != 0; + uint dword1 = GetUint(data, 0); + int year = 1900 + (int)((dword1 >> 14) & 0xffff); + int month = 1 + (int)((dword1 >> 10) & 0x0f); + int day = (int)((dword1 >> 5) & 0x01f); + int hour = (int)(dword1 & 0x01f); + + uint dword2 = GetUint(data, 4); + int minute = (int)((dword2 >> 26) & 0x2f); + int second = (int)((dword2 >> 20) & 0x2f); + int usec = (int)(dword2 & 0xfffff); + + try { + DateTime result = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc); + result = result.AddTicks(usec * 10L); + if (isUtc) { + result = result.ToLocalTime(); + } + return result; + } catch (ArgumentOutOfRangeException) { + throw RubyExceptions.CreateTypeError("marshaled time format differ"); + } + } + + #endregion _dump, _load + + [RubyMethod("+")] + public static DateTime AddSeconds(DateTime self, double seconds) { + return self.AddSeconds(seconds); + } + + [RubyMethod("+")] + public static DateTime AddTime(DateTime self, DateTime other) { + return new DateTime(self.Ticks + (other.Ticks - epoch.Ticks)); + } + + [RubyMethod("-")] + public static DateTime SubtractSeconds(DateTime self, double seconds) { + return self.AddSeconds(-1 * seconds); + } + + [RubyMethod("-")] + public static double SubtractTime(DateTime self, DateTime other) { + return (self - other).TotalSeconds; + } + + [RubyMethod("<=>")] + public static int CompareSeconds(DateTime self, double seconds) { + DateTime other = epoch.AddSeconds(seconds); + return self.CompareTo(other); + } + + [RubyMethod("<=>")] + public static int CompareTo(DateTime self, DateTime other) { + return self.CompareTo(other); + } + + // TODO: dup is not defined in MRI + [RubyMethod("dup")] + public static DateTime Clone(DateTime self) { + return new DateTime(self.Ticks, self.Kind); + } + + + [RubyMethod("gmtime")] + [RubyMethod("utc")] + public static DateTime ToUTC(DateTime self) { + return self.ToUniversalTime(); + } + + [RubyMethod("gmt?")] + [RubyMethod("utc?")] + public static bool IsUTC(DateTime self) { + return self.Equals(self.ToUniversalTime()); + } + + [RubyMethod("dst?")] + [RubyMethod("isdst")] + public static bool IsDST(DateTime self) { + return self.IsDaylightSavingTime(); + } + + [RubyMethod("localtime")] + public static DateTime ToLocalTime(DateTime self) { + return self.ToLocalTime(); + } + + [RubyMethod("hour")] + public static int Hour(DateTime self) { + return self.Hour; + } + + [RubyMethod("min")] + public static int Minute(DateTime self) { + return self.Minute; + } + + [RubyMethod("sec")] + public static int Second(DateTime self) { + return self.Second; + } + + [RubyMethod("year")] + public static int Year(DateTime self) { + return self.Year; + } + + [RubyMethod("mon")] + [RubyMethod("month")] + public static int Month(DateTime self) { + return self.Month; + } + + [RubyMethod("mday")] + [RubyMethod("day")] + public static int Day(DateTime self) { + return self.Day; + } + + [RubyMethod("yday")] + public static int DayOfYear(DateTime self) { + return self.DayOfYear; + } + + [RubyMethod("strftime")] + public static MutableString/*!*/ FormatTime(DateTime self, [DefaultProtocol, NotNull]MutableString/*!*/ format) { + bool inEscape = false; + StringBuilder builder = new StringBuilder(); + foreach (char c in format.ToString()) { + if (c == '%' && !inEscape) { + inEscape = true; + continue; + } + if (inEscape) { + string thisFormat = null; + DateTime firstDay; + int week; + switch (c) { + case 'a': + thisFormat = "ddd"; + break; + case 'A': + thisFormat = "dddd"; + break; + case 'b': + thisFormat = "MMM"; + break; + case 'B': + thisFormat = "MMMM"; + break; + case 'c': + thisFormat = "g"; + break; + case 'd': + thisFormat = "dd"; + break; + case 'H': + thisFormat = "HH"; + break; + case 'I': + thisFormat = "hh"; + break; + case 'j': + builder.AppendFormat("{0:000}", self.DayOfYear); + break; + case 'm': + thisFormat = "MM"; + break; + case 'M': + thisFormat = "mm"; + break; + case 'p': + thisFormat = "tt"; + break; + case 'S': + thisFormat = "ss"; + break; + case 'U': + firstDay = self.AddDays(1 - self.DayOfYear); + DateTime firstSunday = firstDay.AddDays((7 - (int)firstDay.DayOfWeek) % 7); + week = 1 + (int)Math.Floor((self - firstSunday).Days / 7.0); + builder.AppendFormat("{0:00}", week); + break; + case 'W': + firstDay = self.AddDays(1 - self.DayOfYear); + DateTime firstMonday = firstDay.AddDays((8 - (int)firstDay.DayOfWeek) % 7); + week = 1 + (int)Math.Floor((self - firstMonday).Days / 7.0); + builder.AppendFormat("{0:00}", week); + break; + case 'w': + builder.Append((int)self.DayOfWeek); + break; + case 'x': + thisFormat = "d"; + break; + case 'X': + thisFormat = "t"; + break; + case 'y': + thisFormat = "yy"; + break; + case 'Y': + thisFormat = "yyyy"; + break; + case 'Z': + thisFormat = "%K"; + break; + default: + builder.Append(c); + break; + } + if (thisFormat != null) { + builder.Append(self.ToString(thisFormat)); + } + inEscape = false; + } else { + builder.Append(c); + } + } + + return MutableString.Create(builder.ToString()); + } + + [RubyMethod("succ")] + public static DateTime SuccessiveSecond(DateTime self) { + return self.AddSeconds(1); + } + + private static long GetSeconds(DateTime self) { + return (self.ToUniversalTime().Ticks - epoch.Ticks) / 10000000; + } + + [RubyMethod("to_f")] + public static double ToFloatSeconds(DateTime self) { + double seconds = (self.ToUniversalTime().Ticks - epoch.Ticks) / 10000000.0; + return seconds; + } + + [RubyMethod("tv_sec")] + [RubyMethod("to_i")] + public static object/*!*/ ToSeconds(DateTime self) { + return Protocols.Normalize(GetSeconds(self)); + } + + [RubyMethod("tv_usec")] + [RubyMethod("usec")] + public static object/*!*/ GetMicroSeconds(DateTime self) { + return Protocols.Normalize((self.Ticks % 10000000) / 10); + } + + [RubyMethod("asctime")] + [RubyMethod("ctime")] + [RubyMethod("inspect")] + [RubyMethod("to_s")] + public static MutableString/*!*/ ToString(DateTime self) { + return MutableString.Create(self.ToString("ddd MMM dd HH:mm:ss K yyyy")); + } + + [RubyMethod("gmtoff")] + [RubyMethod("utc_offset")] + [RubyMethod("gmt_offset")] + public static object Offset(DateTime self) { + return Protocols.Normalize((self.Ticks - self.ToUniversalTime().Ticks) / 10000000); + } + + [RubyMethod("eql?")] + public static bool Eql(DateTime/*!*/ self, DateTime other) { + return self == other; + } + + [RubyMethod("eql?")] + public static bool Eql(DateTime/*!*/ self, object other) { + return false; + } + + [RubyMethod("getgm")] + [RubyMethod("getutc")] + public static DateTime GetUTC(DateTime/*!*/ self) { + return self.ToUniversalTime(); + } + + [RubyMethod("getlocal")] + public static DateTime GetLocal(DateTime/*!*/ self) { + return self.ToLocalTime(); + } + + [RubyMethod("to_a")] + public static RubyArray ToArray(DateTime/*!*/ self) { + RubyArray result = new RubyArray(); + result.Add(self.Second); + result.Add(self.Minute); + result.Add(self.Hour); + result.Add(self.Day); + result.Add(self.Month); + result.Add(self.Year); + result.Add((int)self.DayOfWeek); + result.Add(self.DayOfYear); + result.Add(self.IsDaylightSavingTime()); + result.Add(GetZone(self)); + return result; + } + + [RubyMethod("wday")] + public static int DayOfWeek(DateTime/*!*/ self) { + return (int)self.DayOfWeek; + } + + [RubyMethod("hash")] + public static int GetHash(DateTime/*!*/ self) { + return self.GetHashCode(); + } + + [RubyMethod("zone")] + public static MutableString/*!*/ GetZone(DateTime/*!*/ self) { + // TODO: + return MutableString.Create("UTC"); + } + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/TrueClass.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/TrueClass.cs new file mode 100644 index 0000000000..41181aa7a9 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/TrueClass.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + [RubyClass("TrueClass")] + public static class TrueClass : Object { + #region Public Instance Methods + + [RubyMethodAttribute("to_s")] + public static MutableString/*!*/ ToString(bool self) { + Debug.Assert(self == true); + return MutableString.Create("true"); + } + + [RubyMethodAttribute("&")] + public static bool And(bool self, object obj) { + Debug.Assert(self == true); + return obj != null; + } + + [RubyMethodAttribute("&")] + public static bool And(bool self, bool obj) { + Debug.Assert(self == true); + return obj; + } + + [RubyMethodAttribute("^")] + public static bool Xor(bool self, object obj) { + Debug.Assert(self == true); + return obj == null; + } + + [RubyMethodAttribute("^")] + public static bool Xor(bool self, bool obj) { + Debug.Assert(self == true); + return !obj; + } + + [RubyMethodAttribute("|")] + public static bool Or(bool self, object obj) { + Debug.Assert(self == true); + return true; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/UnboundMethod.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/UnboundMethod.cs new file mode 100644 index 0000000000..93de3eeeea --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Builtins/UnboundMethod.cs @@ -0,0 +1,116 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Builtins { + + [RubyClass("UnboundMethod")] + public class UnboundMethod : Object { + private readonly string/*!*/ _name; + private readonly RubyMemberInfo/*!*/ _info; + private readonly RubyModule/*!*/ _targetConstraint; + + internal RubyMemberInfo/*!*/ Info { + get { return _info; } + } + + internal string/*!*/ Name { + get { return _name; } + } + + internal RubyModule/*!*/ TargetConstraint { + get { return _targetConstraint; } + } + + internal UnboundMethod(RubyModule/*!*/ targetConstraint, string/*!*/ name, RubyMemberInfo/*!*/ info) { + Assert.NotNull(targetConstraint, name, info); + + _name = name; + _info = info; + _targetConstraint = targetConstraint; + } + + #region Public Instance Methods + + [RubyMethod("==")] + public static bool Equal(UnboundMethod/*!*/ self, [NotNull]UnboundMethod/*!*/ other) { + return ReferenceEquals(self.Info, other.Info); + } + + [RubyMethod("==")] + public static bool Equal(UnboundMethod/*!*/ self, object other) { + return false; + } + + [RubyMethod("arity")] + public static int GetArity(UnboundMethod/*!*/ self) { + return self.Info.Arity; + } + + [RubyMethod("bind")] + public static RubyMethod/*!*/ Bind(UnboundMethod/*!*/ self, object target) { + RubyContext ec = self._targetConstraint.Context; + + if (!ec.GetClassOf(target).HasAncestor(self._targetConstraint)) { + throw RubyExceptions.CreateTypeError( + String.Format("bind argument must be an instance of {0}", self._targetConstraint.Name) + ); + } + + return new RubyMethod(target, self._info, self._name); + } + + [RubyMethod("clone")] + public static UnboundMethod/*!*/ Clone(UnboundMethod/*!*/ self) { + return new UnboundMethod(self._targetConstraint, self._name, self._info); + } + + [RubyMethod("to_s")] + public static MutableString/*!*/ ToS(UnboundMethod/*!*/ self) { + return ToS(self.Name, self._info.DeclaringModule, self._targetConstraint, "UnboundMethod"); + } + + internal static MutableString/*!*/ ToS(string/*!*/ methodName, RubyModule/*!*/ declaringModule, RubyModule/*!*/ targetModule, + string/*!*/ classDisplayName) { + + MutableString result = MutableString.CreateMutable(); + result.Append("#<"); + result.Append(classDisplayName); + result.Append(": "); + + if (ReferenceEquals(targetModule, declaringModule)) { + result.Append(declaringModule.Name); + } else { + result.Append(targetModule.Name); + result.Append('('); + result.Append(declaringModule.Name); + result.Append(')'); + } + + result.Append('#'); + result.Append(methodName); + result.Append('>'); + return result; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Clr/ClrOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Clr/ClrOps.cs new file mode 100644 index 0000000000..f38375d750 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Clr/ClrOps.cs @@ -0,0 +1,63 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Builtins; +using IronRuby.Compiler.Generation; +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.Clr { + [RubyModule("Clr")] + public static class ClrOps { + + [RubyMethod("profile", RubyMethodAttributes.PublicSingleton)] + public static Hash/*!*/ GetProfile(RubyContext/*!*/ context, object self) { + if (!((RubyOptions)context.Options).Profile) { + throw RubyExceptions.CreateSystemCallError("You must enable profiling to use Clr.profile"); + } + + Hash result = new Hash(context); + foreach (var entry in Profiler.Instance.GetProfile()) { + result[entry.Key] = Protocols.Normalize(Utils.DateTimeTicksFromStopwatch(entry.Value)); + } + return result; + } + + [RubyMethod("profile", RubyMethodAttributes.PublicSingleton)] + public static object GetProfile(RubyContext/*!*/ context, BlockParam/*!*/ block, object self) { + if (!((RubyOptions)context.Options).Profile) { + throw RubyExceptions.CreateSystemCallError("You must enable profiling to use Clr.profile"); + } + + var start = Profiler.Instance.GetProfile(); + object blockResult; + if (block.Yield(out blockResult)) { + return blockResult; + } + + Hash result = new Hash(context); + foreach (var entry in Profiler.Instance.GetProfile()) { + long startTime; + if (!start.TryGetValue(entry.Key, out startTime)) { + startTime = 0; + } + long elapsed = entry.Value - startTime; + if (elapsed > 0) { + result[entry.Key] = Protocols.Normalize(Utils.DateTimeTicksFromStopwatch(elapsed)); + } + } + return result; + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Digest/Digest.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Digest/Digest.cs new file mode 100644 index 0000000000..746c170878 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Digest/Digest.cs @@ -0,0 +1,225 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using IronRuby.Builtins; +using IronRuby.Runtime; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.StandardLibrary.Digest { + + [RubyModule("Digest")] + public static class Digest { + + #region Module Methods + + [RubyMethod("const_missing", RubyMethodAttributes.PublicSingleton)] + public static object ConstantMissing(RubyModule/*!*/ self, [DefaultProtocol]string/*!*/ name) { + // TODO: + throw new NotImplementedException(); + } + + [RubyMethod("hexencode", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ HexEncode(RubyModule/*!*/ self, [NotNull]MutableString/*!*/ str) { + // TODO: + throw new NotImplementedException(); + } + + #endregion + + // TODO: MRI doesn't define MD5 constant here, it implements const_missing +#if !SILVERLIGHT + [RubyClass("MD5", BuildConfig = "!SILVERLIGHT")] + public class MD5 : Base { + public MD5() + : base(System.Security.Cryptography.MD5.Create()) { + } + } + + [RubyClass("SHA1", BuildConfig = "!SILVERLIGHT")] + public class SHA1 : Base { + public SHA1() + : base(System.Security.Cryptography.SHA1.Create()) { + } + } + + [RubyClass("SHA256", BuildConfig = "!SILVERLIGHT")] + public class SHA256 : Base { + public SHA256() + : base(System.Security.Cryptography.SHA256.Create()) { + } + } + + [RubyClass("SHA384", BuildConfig = "!SILVERLIGHT")] + public class SHA384 : Base { + public SHA384() + : base(System.Security.Cryptography.SHA384.Create()) { + } + } + + [RubyClass("SHA512", BuildConfig = "!SILVERLIGHT")] + public class SHA512 : Base { + public SHA512() + : base(System.Security.Cryptography.SHA512.Create()) { + } + } +#endif + + [RubyClass("Base")] + public class Base : Class { + private readonly HashAlgorithm/*!*/ _algorithm; + private MutableString/*!*/ _buffer; + + protected Base(HashAlgorithm/*!*/ algorithm) { + Assert.NotNull(algorithm); + _algorithm = algorithm; + _buffer = MutableString.CreateBinary(); + } + + [RubyMethod("<<")] + [RubyMethod("update")] + public static Base/*!*/ Update(RubyContext/*!*/ context, Base/*!*/ self, MutableString str) { + self._buffer.Append(str); + return self; + } + + [RubyMethod("finish", RubyMethodAttributes.PrivateInstance)] + public static MutableString/*!*/ Finish(RubyContext/*!*/ context, Base/*!*/ self) { + byte[] input = self._buffer.ConvertToBytes(); + byte[] hash = self._algorithm.ComputeHash(input); + return MutableString.CreateBinary(hash); + } + + [RubyMethod("reset")] + public static Base/*!*/ Reset(RubyContext/*!*/ context, Base/*!*/ self) { + self._buffer = MutableString.CreateBinary(); + self._algorithm.Initialize(); + return self; + } + } + + [RubyClass("Class"), Includes(typeof(Instance))] + public class Class { + + [RubyMethod("digest", RubyMethodAttributes.PublicSingleton)] + public static MutableString Digest(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ str) { + // TODO: new? + object obj = RubySites.Allocate(self); + // TODO: check obj + return DigestSite.Target(DigestSite, self.Context, obj, str); + } + + [RubyMethod("digest", RubyMethodAttributes.PublicSingleton)] + public static MutableString Digest(RubyClass/*!*/ self) { + throw RubyExceptions.CreateArgumentError("no data given"); + } + + [RubyMethod("hexdigest", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ HexDigest(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ str) { + MutableString result = DigestSite.Target(DigestSite, self.Context, self, str); + // TODO: check result != null + return HexEncode(result); + } + + [RubyMethod("hexdigest", RubyMethodAttributes.PublicSingleton)] + public static MutableString HexDigest(RubyClass/*!*/ self) { + throw RubyExceptions.CreateArgumentError("no data given"); + } + + #region Helpers + + internal static MutableString/*!*/ Bytes2Hex(byte[]/*!*/ bytes) { + return MutableString.Create(System.BitConverter.ToString(bytes).Replace("-", "").ToLower()); + } + + internal static MutableString/*!*/ HexEncode(MutableString/*!*/ str) { + return Bytes2Hex(str.ConvertToBytes()); + } + + #endregion + + #region DynamicSites + + private static CallSite> DigestSite = CallSite>.Create( + RubySites.InstanceCallAction("digest", 1) + ); + + #endregion + } + + [RubyModule("Instance")] + public class Instance { + + [RubyMethod("digest")] + public static MutableString Digest(RubyContext/*!*/ context, object self) { + object clone; + if (!RubyUtils.TryDuplicateObject(context, self, true, out clone)) { + throw RubyExceptions.CreateArgumentError("unable to copy object"); + } + return Finish.Target(Finish, context, clone); + } + + [RubyMethod("digest")] + public static MutableString Digest(RubyContext/*!*/ context, object self, [DefaultProtocol, NotNull]MutableString/*!*/ str) { + Update.Target(Update, context, self, str); + MutableString value = Finish.Target(Finish, context, self); + Reset.Target(Reset, context, self); + + return value; + } + + [RubyMethod("digest!")] + public static MutableString DigestNew(RubyContext/*!*/ context, object self) { + MutableString value = Finish.Target(Finish, context, self); + Reset.Target(Reset, context, self); + return value; + } + + [RubyMethod("hexdigest")] + public static MutableString HexDigest(RubyContext/*!*/ context, object self) { + return Class.HexEncode(Digest(context, self)); + } + + [RubyMethod("hexdigest")] + public static MutableString HexDigest(RubyContext/*!*/ context, object self, [DefaultProtocol, NotNull]MutableString/*!*/ str) { + return Class.HexEncode(Digest(context, self, str)); + } + + [RubyMethod("hexdigest!")] + public static MutableString HexDigestNew(RubyContext/*!*/ context, object self) { + return Class.HexEncode(DigestNew(context, self)); + } + + #region DynamicSites + + private static CallSite> Reset = CallSite>.Create( + RubySites.InstanceCallAction("reset") + ); + + private static CallSite> Update = CallSite>.Create( + RubySites.InstanceCallAction("update", 1) + ); + + private static CallSite> Finish = CallSite>.Create( + RubySites.InstanceCallAction("finish") + ); + + #endregion + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Enumerator/Enumerator.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Enumerator/Enumerator.cs new file mode 100644 index 0000000000..2264795cfa --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Enumerator/Enumerator.cs @@ -0,0 +1,103 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using EnumerableModule = IronRuby.Builtins.Enumerable; + +namespace IronRuby.StandardLibrary.Enumerator { + + [RubyModule(Extends = typeof(EnumerableModule))] + public static class Enumerable { + + // TODO: shouldn't be abstract class + [RubyClass("Enumerator"), Includes(typeof(EnumerableModule))] + public abstract class Enumerator { + + private static readonly Dictionary>/*!*/> _siteCache = + new Dictionary>/*!*/>(); + + private readonly object/*!*/ _obj; + + protected Enumerator(object/*!*/ obj) { + _obj = obj; + } + + protected CallSite>/*!*/ GetSite(string/*!*/ name) { + CallSite> result; + lock (_siteCache) { + if (!_siteCache.TryGetValue(name, out result)) { + result = CallSite>.Create(RubySites.InstanceCallAction(name, RubyCallSignature.WithBlock(0))); + _siteCache[name] = result; + } + } + return result; + } + + internal abstract CallSite> GetSite(); + + internal object Each(RubyContext/*!*/ context, Proc/*!*/ block) { + var site = GetSite(); + return site.Target(site, context, _obj, block); + } + + [RubyConstructor] + public static Enumerator CreateForEach(RubyClass/*!*/ self, object/*!*/ obj) { + return new EnumeratorWithSymbolName(obj, SymbolTable.StringToId("each")); + } + + [RubyConstructor] + public static Enumerator Create(RubyClass/*!*/ self, object/*!*/ obj, SymbolId enumerator) { + return new EnumeratorWithSymbolName(obj, enumerator); + } + + [RubyConstructor] + public static Enumerator Create(RubyClass/*!*/ self, object/*!*/ obj, [DefaultProtocol, NotNull]MutableString/*!*/ enumerator) { + return new EnumeratorWithStringName(obj, enumerator); + } + } + + internal class EnumeratorWithStringName : Enumerator { + private readonly MutableString/*!*/ _name; + + internal override CallSite>/*!*/ GetSite() { + return GetSite(_name.ConvertToString()); + } + + internal EnumeratorWithStringName(object/*!*/ obj, MutableString/*!*/ name) + : base(obj) { + _name = name; + } + } + internal class EnumeratorWithSymbolName : Enumerator { + private readonly SymbolId _name; + + internal override CallSite>/*!*/ GetSite() { + return GetSite(SymbolTable.IdToString(_name)); + } + + internal EnumeratorWithSymbolName(object/*!*/ obj, SymbolId name) + : base(obj) { + _name = name; + } + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/IComparableOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/IComparableOps.cs new file mode 100644 index 0000000000..0c48d9efca --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/IComparableOps.cs @@ -0,0 +1,27 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + [RubyModule(Extends = typeof(IComparable)), Includes(typeof(Comparable))] + public static class IComparableOps { + [RubyMethod("<=>", RubyMethodAttributes.PublicInstance)] + public static int Compare(IComparable/*!*/ self, object other) { + return self.CompareTo(other); + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/IDictionaryOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/IDictionaryOps.cs new file mode 100644 index 0000000000..a69d0c8433 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/IDictionaryOps.cs @@ -0,0 +1,539 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Actions; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + // TODO: IDictionary instead of IDictionary? + // (need support for extension methods on generic interfaces first) + // (IDictionary isn't a good solution because it doesn't have TryGetValue) + [RubyModule(Extends = typeof(IDictionary)), Includes(typeof(Enumerable))] + public static class IDictionaryOps { + + #region Helper methods + + // Make a 2 element array + internal static RubyArray/*!*/ MakeArray(KeyValuePair pair) { + RubyArray list = new RubyArray(2); + list.Add(BaseSymbolDictionary.ObjToNull(pair.Key)); + list.Add(pair.Value); + return list; + } + + internal static RubyArray/*!*/ MakeArray(object key, object value) { + RubyArray list = new RubyArray(2); + list.Add(BaseSymbolDictionary.ObjToNull(key)); + list.Add(value); + return list; + } + + // Replaces the data in dest with the data from src + internal static T ReplaceData(T dest, IEnumerable> src) where T : IDictionary { + dest.Clear(); + foreach (KeyValuePair pair in src) { + dest[pair.Key] = pair.Value; + } + return dest; + } + + // Copies key,value pairs into an array. Needed for iteration over the hash while mutating it. + private static IEnumerable> CopyKeyValuePairs(IDictionary/*!*/ dict) { + KeyValuePair[] pairs = new KeyValuePair[dict.Count]; + dict.CopyTo(pairs, 0); + return pairs; + } + + // Calls to_hash on the object, if it's not already a dictionary + // (only certain Hash functions use this in Ruby) + internal static IDictionary ConvertToHash(RubyContext/*!*/ context, object hash) { + try { + IDictionary dict = hash as IDictionary; + return dict != null ? dict : RubySites.ToHash(context, hash); + } catch (MissingMethodException e) { + throw new InvalidOperationException(String.Format("can't convert {0} into Hash", RubyUtils.GetClassName(context, hash)), e); + } + } + + #endregion + + #region Instance Methods + + [RubyMethod("==")] + public static bool Equals(RubyContext/*!*/ context, IDictionary/*!*/ self, object otherHash) { + IDictionary other = otherHash as IDictionary; + if (other == null || self.Count != other.Count) { + return false; + } + + // Each key value pair must be the same + foreach (KeyValuePair pair in self) { + object value; + if (!other.TryGetValue(pair.Key, out value) || !Protocols.IsEqual(context, pair.Value, value)) { + return false; + } + } + return true; + } + + [RubyMethod("[]")] + public static object GetElement(RubyContext/*!*/ context, IDictionary/*!*/ self, object key) { + object result; + if (!self.TryGetValue(BaseSymbolDictionary.NullToObj(key), out result)) { + return null; + } + return result; + } + + [RubyMethod("[]=")] + [RubyMethod("store")] + public static object SetElement(RubyContext/*!*/ context, IDictionary/*!*/ self, object key, object value) { + RubyUtils.RequiresNotFrozen(context, self); + return RubyUtils.SetHashElement(context, self, key, value); + } + + [RubyMethod("clear")] + public static IDictionary Clear(RubyContext/*!*/ context, IDictionary/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + self.Clear(); + return self; + } + + // We don't define "dup" here because "dup" shouldn't show up on builtin types like Hash + // (Kernel#dup just works for these types) + private static IDictionary/*!*/ Duplicate(RubyContext/*!*/ context, IDictionary/*!*/ self) { + // Call Kernel#dup, then copy items + var copy = (IDictionary)KernelOps.Duplicate(context, self); + return ReplaceData(copy, self); + } + + [RubyMethod("default")] + public static object GetDefaultValue(RubyContext/*!*/ context, IDictionary/*!*/ self, [Optional]object key) { + return null; + } + + [RubyMethod("default_proc")] + public static Proc GetDefaultProc(IDictionary/*!*/ self) { + return null; + } + + [RubyMethod("delete")] + public static object Delete(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self, object key) { + RubyUtils.RequiresNotFrozen(context, self); + + object value; + if (!self.TryGetValue(BaseSymbolDictionary.NullToObj(key), out value)) { + // key not found, call the block if it was passed in + if (block != null) { + object result; + block.Yield(key, out result); + return result; + } + return null; + } + self.Remove(BaseSymbolDictionary.NullToObj(key)); + return value; + } + + [RubyMethod("delete_if")] + public static object DeleteIf(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + + if (self.Count > 0 && block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + // Make a copy of the keys to delete, so we don't modify the collection + // while iterating over it + RubyArray keysToDelete = new RubyArray(); + + foreach (var pair in self) { + object result; + if (block.Yield(BaseSymbolDictionary.ObjToNull(pair.Key), pair.Value, out result)) { + return result; + } + + // Delete the key, unless 'false' or 'nil' is returned + if (RubyOps.IsTrue(result)) { + keysToDelete.Add(pair.Key); + } + } + + foreach (object key in keysToDelete) { + self.Remove(key); + } + + return self; + } + + [RubyMethod("each")] + public static object Each(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self) { + if (self.Count > 0) { + // Must make a copy of the Keys array so that we can iterate over a static set of keys. Remember + // that the block can modify the hash, hence the need for a copy of the keys + object[] keys = new object[self.Count]; + self.Keys.CopyTo(keys, 0); + + // TODO: what are all the scenarios where the block can mutate the hash? can it remove keys? if so, what happens? + for (int i = 0; i < keys.Length; i++) { + object result; + if (block.Yield(MakeArray(keys[i], self[keys[i]]), out result)) { + return result; + } + } + } + + return self; + } + + [RubyMethod("each_pair")] + public static object EachPair(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self) { + if (self.Count > 0) { + // Must make a copy of the Keys array so that we can iterate over a static set of keys. Remember + // that the block can modify the hash, hence the need for a copy of the keys + object[] keys = new object[self.Count]; + self.Keys.CopyTo(keys, 0); + + // TODO: what are all the scenarios where the block can mutate the hash? can it remove keys? if so, what happens? + for (int i = 0; i < keys.Length; i++) { + object result; + if (block.Yield(BaseSymbolDictionary.ObjToNull(keys[i]), self[keys[i]], out result)) { + return result; + } + } + } + + return self; + } + + [RubyMethod("each_key")] + public static object EachKey(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self) { + if (self.Count > 0) { + // Must make a copy of the Keys array so that we can iterate over a static set of keys. Remember + // that the block can modify the hash, hence the need for a copy of the keys + object[] keys = new object[self.Count]; + self.Keys.CopyTo(keys, 0); + + // TODO: what are all the scenarios where the block can mutate the hash? can it remove keys? if so, what happens? + for (int i = 0; i < keys.Length; i++) { + object result; + if (block.Yield(BaseSymbolDictionary.ObjToNull(keys[i]), out result)) { + return result; + } + } + } + + return self; + } + + [RubyMethod("each_value")] + public static object EachValue(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self) { + if (self.Count > 0) { + // Ruby allows modifications while iterating thru the dictionary: + object[] values = new object[self.Count]; + self.Values.CopyTo(values, 0); + + for (int i = 0; i < values.Length; i++) { + object result; + if (block.Yield(values[i], out result)) { + return result; + } + } + } + + return self; + } + + [RubyMethod("empty?")] + public static bool Empty(IDictionary/*!*/ self) { + return self.Count == 0; + } + + [RubyMethod("fetch")] + public static object Fetch(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self, object key, [Optional]object defaultValue) { + object result; + if (self.TryGetValue(BaseSymbolDictionary.NullToObj(key), out result)) { + return result; + } + + if (block != null) { + if (defaultValue != Missing.Value) { + context.ReportWarning("block supersedes default value argument"); + } + + block.Yield(key, out result); + return result; + } + + if (defaultValue == Missing.Value) { + throw RubyExceptions.CreateIndexError("key not found"); + } + + return defaultValue; + } + + [RubyMethod("has_key?")] + [RubyMethod("include?")] + [RubyMethod("key?")] + [RubyMethod("member?")] + public static bool HasKey(IDictionary/*!*/ self, object key) { + return self.ContainsKey(BaseSymbolDictionary.NullToObj(key)); + } + + [RubyMethod("has_value?")] + [RubyMethod("value?")] + public static bool HasValue(RubyContext/*!*/ context, IDictionary/*!*/ self, object value) { + foreach (KeyValuePair pair in self) { + if (Protocols.IsEqual(context, pair.Value, value)) { + return true; + } + } + return false; + } + + [RubyMethod("index")] + public static object Index(RubyContext/*!*/ context, IDictionary/*!*/ self, object value) { + foreach (KeyValuePair pair in self) { + if (Protocols.IsEqual(context, pair.Value, value)) { + return BaseSymbolDictionary.ObjToNull(pair.Key); + } + } + return null; + } + + [RubyMethod("indexes")] + [RubyMethod("indices")] + public static RubyArray/*!*/ Indexes(RubyContext/*!*/ context, IDictionary/*!*/ self, [NotNull]params object[] keys) { + context.ReportWarning("Hash#indices is deprecated; use Hash#values_at"); + return ValuesAt(context, self, keys); + } + + [RubyMethod("inspect")] + public static MutableString Inspect(RubyContext/*!*/ context, IDictionary/*!*/ self) { + using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(self)) { + if (handle == null) { + return MutableString.Create("{...}"); + } + MutableString str = MutableString.CreateMutable(); + str.Append('{'); + foreach (KeyValuePair pair in self) { + if (str.Length != 1) { + str.Append(", "); + } + str.Append(RubySites.Inspect(context, BaseSymbolDictionary.ObjToNull(pair.Key))); + str.Append("=>"); + str.Append(RubySites.Inspect(context, pair.Value)); + } + str.Append('}'); + return str; + } + } + + [RubyMethod("invert")] + public static Hash/*!*/ Invert(RubyContext/*!*/ context, IDictionary/*!*/ self) { + // invert returns a Hash, even from subclasses + Hash hash = new Hash(context.EqualityComparer, self.Count); + foreach (KeyValuePair pair in self) { + hash[BaseSymbolDictionary.NullToObj(pair.Value)] = BaseSymbolDictionary.ObjToNull(pair.Key); + } + return hash; + } + + [RubyMethod("keys")] + public static RubyArray/*!*/ GetKeys(IDictionary/*!*/ self) { + RubyArray keys = new RubyArray(self.Count); + foreach (object key in self.Keys) { + keys.Add(BaseSymbolDictionary.ObjToNull(key)); + } + return keys; + } + + [RubyMethod("length")] + [RubyMethod("size")] + public static int Length(IDictionary/*!*/ self) { + return self.Count; + } + + [RubyMethod("merge")] + public static object Merge(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self, object otherHash) { + return Update(context, block, Duplicate(context, self), otherHash); + } + + [RubyMethod("merge!")] + [RubyMethod("update")] + public static object Update(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self, object otherHash) { + IDictionary hash = ConvertToHash(context, otherHash); + if (hash != null && hash.Count > 0) { + RubyUtils.RequiresNotFrozen(context, self); + } + + if (block == null) { + foreach (KeyValuePair pair in CopyKeyValuePairs(hash)) { + self[BaseSymbolDictionary.NullToObj(pair.Key)] = pair.Value; + } + } else { + foreach (KeyValuePair pair in CopyKeyValuePairs(hash)) { + object key = pair.Key, newValue = pair.Value, oldValue; + if (self.TryGetValue(key, out oldValue)) { + if (block.Yield(BaseSymbolDictionary.ObjToNull(key), oldValue, pair.Value, out newValue)) { + return newValue; + } + } + self[key] = newValue; + } + } + return self; + } + + [RubyMethod("rehash")] + public static IDictionary Rehash(RubyContext/*!*/ context, IDictionary/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + return ReplaceData(self, CopyKeyValuePairs(self)); + } + + // This works like delete_if, not reject! + // (because it needs to return the new collection) + [RubyMethod("reject")] + public static object Reject(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self) { + return DeleteIf(context, block, Duplicate(context, self)); + } + + // This works like delete_if, but returns nil if no elements were removed + [RubyMethod("reject!")] + public static object RejectMutate(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + + // Make a copy of the keys to delete, so we don't modify the collection + // while iterating over it + RubyArray keysToDelete = new RubyArray(); + + foreach (KeyValuePair pair in self) { + object result; + if (block.Yield(BaseSymbolDictionary.ObjToNull(pair.Key), pair.Value, out result)) { + return result; + } + + // Delete the key, unless 'false' or 'nil' is returned + if (RubyOps.IsTrue(result)) { + keysToDelete.Add(pair.Key); + } + } + + foreach (object key in keysToDelete) { + self.Remove(key); + } + + return keysToDelete.Count == 0 ? null : self; + } + + [RubyMethod("replace")] + public static IDictionary Replace(RubyContext/*!*/ context, IDictionary/*!*/ self, [NotNull]object other) { + // Do nothing if identities are the same + if (Object.ReferenceEquals(self, other)) return self; + + RubyUtils.RequiresNotFrozen(context, self); + return ReplaceData(self, ConvertToHash(context, other)); + } + + [RubyMethod("select")] + public static object Select(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self) { + RubyArray list = new RubyArray(); + + foreach (var pair in CopyKeyValuePairs(self)) { + object result; + if (block.Yield(BaseSymbolDictionary.ObjToNull(pair.Key), pair.Value, out result)) { + return result; + } + + // Select the key, unless 'false' or 'nil' is returned + if (RubyOps.IsTrue(result)) { + list.Add(MakeArray(pair)); + } + } + + return list; + } + + [RubyMethod("shift")] + public static object Shift(RubyContext/*!*/ context, IDictionary/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + + if (self.Count == 0) { + return null; + } + + IEnumerator> e = self.GetEnumerator(); + e.MoveNext(); + KeyValuePair pair = e.Current; + self.Remove(pair.Key); + + return MakeArray(pair); + } + + [RubyMethod("sort")] + public static object Sort(RubyContext/*!*/ context, BlockParam block, IDictionary/*!*/ self) { + return ArrayOps.SortInPlace(context, block, ToArray(self)); + } + + [RubyMethod("to_a")] + public static RubyArray/*!*/ ToArray(IDictionary/*!*/ self) { + RubyArray result = new RubyArray(self.Count); + foreach (KeyValuePair pair in self) { + result.Add(MakeArray(pair)); + } + return result; + } + + [RubyMethod("to_hash")] + public static IDictionary ToHash(IDictionary/*!*/ self) { + return self; + } + + [RubyMethod("to_s")] + public static MutableString ToString(RubyContext/*!*/ context, IDictionary/*!*/ self) { + using (IDisposable handle = RubyUtils.InfiniteToSTracker.TrackObject(self)) { + if (handle == null) { + return MutableString.Create("{...}"); + } else { + return IListOps.Join(context, ToArray(self)); + } + } + } + + [RubyMethod("values")] + public static RubyArray/*!*/ GetValues(IDictionary/*!*/ self) { + return new RubyArray(self.Values); + } + + [RubyMethod("values_at")] + public static RubyArray/*!*/ ValuesAt(RubyContext/*!*/ context, IDictionary/*!*/ self, [NotNull]params object[] keys) { + RubyArray values = new RubyArray(); + foreach (object key in keys) { + values.Add(GetElement(context, self, key)); + } + return values; + } + + #endregion + } + +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/IEnumerableOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/IEnumerableOps.cs new file mode 100644 index 0000000000..a206f6d53c --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/IEnumerableOps.cs @@ -0,0 +1,39 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Actions; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + // For every .NET type that implements IEnumerable, extend it to include the Ruby Enumerable + // module, and inject an "each" method. + [RubyModule(Extends = typeof(IEnumerable)), Includes(typeof(Enumerable))] + public static class IEnumerableOps { + + [RubyMethod("each", RubyMethodAttributes.PublicInstance)] + public static object Each(BlockParam block, IEnumerable/*!*/ self) { + foreach (object obj in self) { + object result; + if (block.Yield(obj, out result)) { + return result; + } + } + return self; + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/IListOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/IListOps.cs new file mode 100644 index 0000000000..7037d17cbe --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/IListOps.cs @@ -0,0 +1,1374 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using IronRuby.Runtime; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Builtins { + using Math = System.Math; + + // TODO: Should this be IList instead of IList? + [RubyModule(Extends = typeof(IList)), Includes(typeof(Enumerable))] + public static class IListOps { + + #region Helpers + + internal static int NormalizeIndex(IList/*!*/ list, int index) { + return NormalizeIndex(list.Count, index); + } + + internal static int NormalizeIndex(int count, int index) { + return index < 0 ? index + count : index; + } + + internal static bool NormalizeRange(int listCount, ref int start, ref int count) { + start = NormalizeIndex(listCount, start); + if (start < 0 || start > listCount || count < 0) { + return false; + } + + if (count != 0) { + count = start + count > listCount ? listCount - start : count; + } + + return true; + } + + internal static bool NormalizeRange(RubyContext/*!*/ context, int listCount, Range/*!*/ range, out int begin, out int count) { + bool excludeEnd; + int end; + Protocols.ConvertToIntegerRange(context, range, out begin, out end, out excludeEnd); + + begin = NormalizeIndex(listCount, begin); + + if (begin < 0 || begin > listCount) { + count = 0; + return false; + } + + end = NormalizeIndex(listCount, end); + + count = excludeEnd ? end - begin : end - begin + 1; + return true; + } + + private static bool InRangeNormalized(IList/*!*/ list, ref int index) { + if (index < 0) { + index = index + list.Count; + } + return index >= 0 && index < list.Count; + } + + private static IEnumerable/*!*/ EnumerateRange(IList/*!*/ list, int index, int count) { + int stop = index + count; + for (int i = index; i < stop; i++) { + yield return list[i]; + } + } + + private static IList/*!*/ GetResultRange(RubyContext/*!*/ context, IList/*!*/ list, int index, int count) { + IList result = CreateResultArray(context, list); + int stop = index + count; + for (int i = index; i < stop; i++) { + result.Add(list[i]); + } + return result; + } + + private static void InsertRange(IList/*!*/ collection, int index, IEnumerable/*!*/ items) { + List list = collection as List; + if (list != null) { + list.InsertRange(index, items); + } else { + int i = index; + foreach (object obj in items) { + collection.Insert(i++, obj); + } + } + } + + private static void RemoveRange(IList/*!*/ collection, int index, int count) { + List list = collection as List; + if (list != null) { + list.RemoveRange(index, count); + } else { + for (int i = index + count - 1; i >= index; i--) { + collection.RemoveAt(i); + } + } + } + + internal static void AddRange(IList/*!*/ collection, IList/*!*/ items) { + List list = collection as List; + if (list != null) { + list.Capacity += items.Count; + } + // note: "collection" could be the same as "items" so we can't use an enumerator + int count = items.Count; + for (int i = 0; i < count; i++) { + collection.Add(items[i]); + } + } + + private static readonly CallSite>/*!*/ + _CreateArraySite = CallSite>.Create(RubySites.InstanceCallAction("new")); + + private static IList/*!*/ CreateResultArray(RubyContext/*!*/ context, IList/*!*/ self) { + return _CreateArraySite.Target(_CreateArraySite, context, context.GetClassOf(self)); + } + + #endregion + + #region *, +, <<, <=> + + [RubyMethod("*")] + public static RubyArray/*!*/ Repetition(IList/*!*/ self, int repeat) { + if (repeat < 0) { + throw RubyExceptions.CreateArgumentError("negative argument"); + } + RubyArray result = new RubyArray(self.Count * repeat); + for (int i = 0; i < repeat; ++i) { + AddRange(result, self); + } + return result; + } + + [RubyMethod("*")] + public static MutableString Repetition(RubyContext/*!*/ context, IList/*!*/ self, string separator) { + return Join(context, self, separator); + } + + [RubyMethod("*")] + public static MutableString Repetition(RubyContext/*!*/ context, IList/*!*/ self, MutableString separator) { + return Join(context, self, separator); + } + + [RubyMethod("*")] + public static object Repetition(RubyContext/*!*/ context, IList/*!*/ self, object repeat) { + MutableString str = Protocols.AsString(context, repeat); + if (str != null) + return Repetition(context, self, str); + else + return Repetition(self, Protocols.CastToFixnum(context, repeat)); + } + + [RubyMethod("+")] + public static RubyArray/*!*/ Concatenate(IList/*!*/ self, IList/*!*/ other) { + RubyArray result = new RubyArray(self.Count + other.Count); + AddRange(result, self); + AddRange(result, other); + return result; + } + + [RubyMethod("+")] + public static IList Concatenate(RubyContext/*!*/ context, IList/*!*/ self, object other) { + return Concatenate(self, Protocols.CastToArray(context, other)); + } + + [RubyMethod("-")] + public static RubyArray/*!*/ Difference(RubyContext/*!*/ context, IList/*!*/ self, IList/*!*/ other) { + RubyArray result = new RubyArray(); + + // TODO: optimize this + foreach (object element in self) { + if (!Include(context, other, element)) { + result.Add(element); + } + } + + return result; + } + + [RubyMethod("-")] + public static RubyArray/*!*/ Difference(RubyContext/*!*/ context, IList/*!*/ self, object other) { + return Difference(context, self, Protocols.CastToArray(context, other)); + } + + [RubyMethod("<<")] + public static IList/*!*/ Append(RubyContext/*!*/ context, IList/*!*/ self, object value) { + RubyUtils.RequiresNotFrozen(context, self); + self.Add(value); + return self; + } + + [RubyMethod("<=>")] + public static int Compare(RubyContext/*!*/ context, IList/*!*/ self, IList other) { + int limit = System.Math.Min(self.Count, other.Count); + + for (int i = 0; i < limit; ++i) { + int result = Protocols.Compare(context, self[i], other[i]); + if (result != 0) + return result; + } + + if (self.Count < other.Count) + return -1; + else if (self.Count == other.Count) + return 0; + else + return 1; + } + + [RubyMethod("<=>")] + public static int Compare(RubyContext/*!*/ context, IList/*!*/ self, object other) { + return Compare(context, self, Protocols.CastToArray(context, other)); + } + #endregion + + #region ==, eql?, hash + + [RubyMethod("==")] + public static bool Equals(IList/*!*/ self, object other) { + return false; + } + + [RubyMethod("==")] + public static bool Equals(RubyContext/*!*/ context, IList/*!*/ self, [NotNull]IList/*!*/ other) { + Assert.NotNull(self, other); + + if (object.ReferenceEquals(self, other)) { + return true; + } + + if (self.Count != other.Count) { + return false; + } + + for (int i = 0; i < self.Count; ++i) { + bool result = Protocols.IsEqual(context, self[i], other[i]); + if (!result) { + return false; + } + } + return true; + } + + // eql?, hash + [RubyMethod("eql?")] + public static bool HashEquals(IList/*!*/ self, object other) { + return RubyArray.Equals(self, other); + } + + [RubyMethod("hash")] + public static int GetHashCode(IList/*!*/ self) { + return RubyArray.GetHashCode(self); + } + #endregion + + #region slice, [] + + [RubyMethod("[]")] + [RubyMethod("slice")] + public static object GetElement(IList list, [DefaultProtocol]int index) { + return InRangeNormalized(list, ref index) ? list[index] : null; + } + + [RubyMethod("[]")] + [RubyMethod("slice")] + public static IList GetElements(RubyContext/*!*/ context, IList/*!*/ list, [DefaultProtocol]int index, [DefaultProtocol]int count) { + if (!NormalizeRange(list.Count, ref index, ref count)) { + return null; + } + + return GetResultRange(context, list, index, count); + } + + [RubyMethod("[]")] + [RubyMethod("slice")] + public static IList GetElement(RubyContext/*!*/ context, IList array, [NotNull]Range/*!*/ range) { + int start, count; + if (!NormalizeRange(context, array.Count, range, out start, out count)) { + return null; + } + + return count < 0 ? CreateResultArray(context, array) : GetElements(context, array, start, count); + } + + #endregion + + #region []= + + public static void ExpandList(IList list, int index) { + int diff = index - list.Count; + for (int i = 0; i < diff; ++i) + list.Add(null); + } + + public static void OverwriteOrAdd(IList list, int index, object value) { + if (index < list.Count) + list[index] = value; + else + list.Add(value); + } + + public static void DeleteItems(IList list, int index, int length) { + if (index >= list.Count) { + ExpandList(list, index); + } else { + // normalize for max length + if (index + length > list.Count) { + length = list.Count - index; + } + RemoveRange(list, index, length); + } + } + + [RubyMethod("[]=")] + public static object SetElement(RubyContext/*!*/ context, IList list, int index, object value) { + RubyUtils.RequiresNotFrozen(context, list); + + index = NormalizeIndex(list, index); + + if (index < 0) + throw RubyExceptions.CreateIndexError(String.Format("index {0} out of array", index)); + + if (index < list.Count) + list[index] = value; + else { + ExpandList(list, index); + list.Add(value); + } + return value; + } + + [RubyMethod("[]=")] + public static object SetElement(RubyContext/*!*/ context, IList list, object index, object value) { + return SetElement(context, list, Protocols.CastToFixnum(context, index), value); + } + + [RubyMethod("[]=")] + public static object SetElement(RubyContext/*!*/ context, IList/*!*/ list, int index, int length, object value) { + RubyUtils.RequiresNotFrozen(context, list); + + if (length < 0) { + throw RubyExceptions.CreateIndexError(String.Format("negative length ({0})", length)); + } + + index = NormalizeIndex(list, index); + if (index < 0) { + throw RubyExceptions.CreateIndexError(String.Format("index {0} out of array", index)); + } + + IList valueAsList = value as IList; + + if (value == null || (valueAsList != null && valueAsList.Count == 0)) { + DeleteItems(list, index, length); + } else { + if (valueAsList == null) { + SetElement(context, list, index, value); + } else { + ExpandList(list, index); + + int limit = length > valueAsList.Count ? valueAsList.Count : length; + + for (int i = 0; i < limit; ++i) { + OverwriteOrAdd(list, index + i, valueAsList[i]); + } + + if (length < valueAsList.Count) { + InsertRange(list, index + limit, EnumerateRange(valueAsList, limit, valueAsList.Count - limit)); + } else { + RemoveRange(list, index + limit, Math.Min(length - valueAsList.Count, list.Count - (index + limit))); + } + } + } + + return value; + } + + [RubyMethod("[]=")] + public static object SetElement(RubyContext/*!*/ context, IList list, int index, object length, object value) { + return SetElement(context, list, index, Protocols.CastToFixnum(context, length), value); + } + + [RubyMethod("[]=")] + public static object SetElement(RubyContext/*!*/ context, IList list, object index, int length, object value) { + return SetElement(context, list, Protocols.CastToFixnum(context, index), length, value); + } + + [RubyMethod("[]=")] + public static object SetElement(RubyContext/*!*/ context, IList list, object index, object length, object value) { + return SetElement(context, list, Protocols.CastToFixnum(context, index), Protocols.CastToFixnum(context, length), value); + } + + [RubyMethod("[]=")] + public static object SetElement(RubyContext/*!*/ context, IList list, Range range, object value) { + RubyUtils.RequiresNotFrozen(context, list); + bool excludeEnd; + int begin, end; + Protocols.ConvertToIntegerRange(context, range, out begin, out end, out excludeEnd); + + begin = begin < 0 ? begin + list.Count : begin; + if (begin < 0) + throw RubyExceptions.CreateRangeError(String.Format("{0}..{1} out of range", begin, end)); + + end = end < 0 ? end + list.Count : end; + + int count = excludeEnd ? end - begin : end - begin + 1; + return SetElement(context, list, begin, Math.Max(count, 0), value); + } + + #endregion + + #region &, | + + [RubyMethod("&")] + public static RubyArray/*!*/ Intersection(RubyContext/*!*/ context, IList/*!*/ self, [DefaultProtocol]IList/*!*/ other) { + Dictionary items = new Dictionary(context.EqualityComparer); + RubyArray result = new RubyArray(); + + // first get the items in the RHS + foreach (object item in other) { + items[item] = true; + } + + // now, go through the items in the LHS, adding ones that were also in the RHS + // this ensures that we return the items in the correct order + foreach (object item in self) { + if (items.Remove(item)) { + result.Add(item); + if (items.Count == 0) { + break; // all done + } + } + } + + return result; + } + + [RubyMethod("|")] + public static RubyArray/*!*/ Union(RubyContext/*!*/ context, IList/*!*/ self, [DefaultProtocol]IList other) { + var seen = new Dictionary(context.EqualityComparer); + var result = new RubyArray(); + + // Union merges the two arrays, removing duplicates + foreach (object item in self) { + if (!seen.ContainsKey(item)) { + seen.Add(item, true); + result.Add(item); + } + } + + foreach (object item in other) { + if (!seen.ContainsKey(item)) { + seen.Add(item, true); + result.Add(item); + } + } + + return result; + } + + #endregion + + public static IList GetContainerOf(RubyContext/*!*/ context, IList list, int index, object item) { + foreach (object current in list) { + IList subArray = current as IList; + if (subArray != null && subArray.Count > index) { + if (Protocols.IsEqual(context, subArray[index], item)) + return subArray; + } + } + return null; + } + + [RubyMethod("assoc")] + public static IList GetContainerOfFirstItem(RubyContext/*!*/ context, IList/*!*/ self, object item) { + return GetContainerOf(context, self, 0, item); + } + + [RubyMethod("at")] + public static object At(IList/*!*/ self, int index) { + return GetElement(self, index); + } + + [RubyMethod("at")] + public static object At(RubyContext/*!*/ context, IList/*!*/ self, object index) { + return GetElement(self, Protocols.CastToFixnum(context, index)); + } + + [RubyMethod("clear")] + public static IList Clear(RubyContext/*!*/ context, IList/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + self.Clear(); + return self; + } + + [RubyMethod("collect!")] + [RubyMethod("map!")] + public static object CollectInPlace(RubyContext/*!*/ context, BlockParam block, IList/*!*/ self) { + Assert.NotNull(context, self); + + if (self.Count > 0 && block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + RubyUtils.RequiresNotFrozen(context, self); + + int i = 0; + while (i < self.Count) { + object result; + if (block.Yield(self[i], out result)) { + return result; + } + self[i] = result; + i++; + } + + return self; + } + + [RubyMethod("compact")] + public static IList/*!*/ Compact(RubyContext/*!*/ context, IList/*!*/ self) { + IList result = CreateResultArray(context, self); + + foreach (object item in self) { + if (item != null) { + result.Add(item); + } + } + + return result; + } + + [RubyMethod("compact!")] + public static IList CompactInPlace(RubyContext/*!*/ context, IList/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + + bool changed = false; + int i = 0; + while (i < self.Count) { + if (self[i] == null) { + changed = true; + self.RemoveAt(i); + } else + ++i; + } + return changed ? self : null; + } + + [RubyMethod("concat")] + public static IList/*!*/ Concat(RubyContext/*!*/ context, IList/*!*/ self, [NotNull]IList/*!*/ other) { + if (other.Count > 0) + RubyUtils.RequiresNotFrozen(context, self); + AddRange(self, other); + return self; + } + + [RubyMethod("concat")] + public static IList Concat(RubyContext/*!*/ context, IList/*!*/ self, object other) { + if (other == null) { + throw RubyExceptions.CreateTypeError("cannot convert nil into Array"); + } + + return Concat(context, self, Protocols.CastToArray(context, other)); + } + + #region delete, delete_at + + public static bool Remove(RubyContext/*!*/ context, IList/*!*/ self, object item) { + int i = 0; + bool removed = false; + while (i < self.Count) { + if (Protocols.IsEqual(context, self[i], item)) { + RubyUtils.RequiresNotFrozen(context, self); + self.RemoveAt(i); + removed = true; + } else { + ++i; + } + } + return removed; + } + + [RubyMethod("delete")] + public static object Delete(RubyContext/*!*/ context, IList/*!*/ self, object item) { + return Remove(context, self, item) ? item : null; + } + + [RubyMethod("delete")] + public static object Delete(RubyContext/*!*/ context, BlockParam block, IList/*!*/ self, object item) { + bool removed = Remove(context, self, item); + + if (block != null) { + object result; + block.Yield(out result); + return result; + } + return removed ? item : null; + } + + [RubyMethod("delete_at")] + public static object DeleteAt(RubyContext/*!*/ context, IList/*!*/ self, object indexValue) { + RubyUtils.RequiresNotFrozen(context, self); + + int index = Protocols.CastToFixnum(context, indexValue); + index = index < 0 ? index + self.Count : index; + if (index < 0 || index > self.Count) + return null; + + object result = GetElement(self, index); + self.RemoveAt(index); + return result; + } + + [RubyMethod("delete_if")] + public static object DeleteIf(RubyContext/*!*/ context, BlockParam block, IList/*!*/ self) { + bool changed, jumped; + DeleteIf(context, block, self, out changed, out jumped); + return self; + } + + private static object DeleteIf(RubyContext/*!*/ context, BlockParam block, IList/*!*/ self, out bool changed, out bool jumped) { + RubyUtils.RequiresNotFrozen(context, self); + changed = false; + jumped = false; + + if (block == null && self.Count > 0) { + throw RubyExceptions.NoBlockGiven(); + } + + // TODO: if block jumpes the array is not modified: + int i = 0; + while (i < self.Count) { + object result; + if (block.Yield(self[i], out result)) { + jumped = true; + return result; + } + + if (RubyOps.IsTrue(result)) { + changed = true; + self.RemoveAt(i); + } else { + i++; + } + } + return null; + } + + [RubyMethod("reject!")] + public static object RejectInPlace(RubyContext/*!*/ context, BlockParam block, IList/*!*/ self) { + bool changed, jumped; + object result = DeleteIf(context, block, self, out changed, out jumped); + if (jumped) return result; + if (changed) return self; + return null; + } + + #endregion + + #region each, each_index + + [RubyMethod("each")] + public static object Each(RubyContext/*!*/ context, BlockParam block, IList/*!*/ self) { + Assert.NotNull(context, self); + + if (self.Count > 0 && block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + for (int i = 0; i < self.Count; i++) { + object result; + if (block.Yield(self[i], out result)) { + return result; + } + } + return self; + } + + [RubyMethod("each_index")] + public static object EachIndex(RubyContext/*!*/ context, BlockParam block, IList/*!*/ self) { + Assert.NotNull(context, self); + + if (self.Count > 0 && block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + int i = 0; + while (i < self.Count) { + object result; + if (block.Yield(i, out result)) { + return result; + } + i++; + } + + return self; + } + + #endregion + + [RubyMethod("empty?")] + public static bool Empty(IList/*!*/ self) { + return self.Count == 0; + } + + #region fetch + + [RubyMethod("fetch")] + public static object Fetch(RubyContext/*!*/ context, BlockParam outOfRangeValueProvider, IList/*!*/ list, object index, [Optional]object defaultValue) { + return Fetch(context, outOfRangeValueProvider, list, Protocols.CastToFixnum(context, index), defaultValue); + } + + [RubyMethod("fetch")] + public static object Fetch(RubyContext/*!*/ context, BlockParam outOfRangeValueProvider, IList/*!*/ list, int fixnumIndex, [Optional]object defaultValue) { + int oldIndex = fixnumIndex; + if (InRangeNormalized(list, ref fixnumIndex)) { + return list[fixnumIndex]; + } + + if (outOfRangeValueProvider != null) { + if (defaultValue != Missing.Value) { + context.ReportWarning("block supersedes default value argument"); + } + + object result; + outOfRangeValueProvider.Yield(oldIndex, out result); + return result; + } + + if (defaultValue == Missing.Value) { + throw RubyExceptions.CreateIndexError("index " + fixnumIndex + " out of array"); + } + return defaultValue; + } + + #endregion + + #region fill + + [RubyMethod("fill")] + public static IList/*!*/ Fill(RubyContext/*!*/ context, IList/*!*/ self, object obj, [DefaultParameterValue(0)]int start) { + RubyUtils.RequiresNotFrozen(context, self); + + // Note: Array#fill(obj, start) is not equivalent to Array#fill(obj, start, 0) + // (as per MRI behavior, the latter can expand the array if start > length, but the former doesn't) + start = Math.Max(0, NormalizeIndex(self, start)); + + for (int i = start; i < self.Count; i++) { + self[i] = obj; + } + return self; + } + + [RubyMethod("fill")] + public static IList/*!*/ Fill(RubyContext/*!*/ context, IList/*!*/ self, object obj, int start, int length) { + RubyUtils.RequiresNotFrozen(context, self); + + // Note: Array#fill(obj, start) is not equivalent to Array#fill(obj, start, 0) + // (as per MRI behavior, the latter can expand the array if start > length, but the former doesn't) + start = Math.Max(0, NormalizeIndex(self, start)); + + ExpandList(self, Math.Min(start, start + length)); + + for (int i = 0; i < length; i++) { + OverwriteOrAdd(self, start + i, obj); + } + + return self; + } + + [RubyMethod("fill")] + public static IList/*!*/ Fill(RubyContext/*!*/ context, IList/*!*/ self, object obj, object start, [DefaultParameterValue(null)]object length) { + int startFixnum = (start == null) ? 0 : Protocols.CastToFixnum(context, start); + if (length == null) { + return Fill(context, self, obj, startFixnum); + } else { + return Fill(context, self, obj, startFixnum, Protocols.CastToFixnum(context, length)); + } + } + + [RubyMethod("fill")] + public static IList/*!*/ Fill(RubyContext/*!*/ context, IList/*!*/ self, object obj, Range/*!*/ range) { + int begin = NormalizeIndex(self, Protocols.CastToFixnum(context, range.Begin)); + int end = NormalizeIndex(self, Protocols.CastToFixnum(context, range.End)); + int length = Math.Max(0, end - begin + (range.ExcludeEnd ? 0 : 1)); + + return Fill(context, self, obj, begin, length); + } + + [RubyMethod("fill")] + public static object Fill(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, IList/*!*/ self, [DefaultParameterValue(0)]int start) { + RubyUtils.RequiresNotFrozen(context, self); + + start = Math.Max(0, NormalizeIndex(self, start)); + + for (int i = start; i < self.Count; i++) { + object result; + if (block.Yield(i, out result)) { + return result; + } + self[i] = result; + } + return self; + } + + [RubyMethod("fill")] + public static object Fill(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, IList/*!*/ self, int start, int length) { + RubyUtils.RequiresNotFrozen(context, self); + + start = Math.Max(0, NormalizeIndex(self, start)); + + ExpandList(self, Math.Min(start, start + length)); + + for (int i = start; i < start + length; i++) { + object result; + if (block.Yield(i, out result)) { + return result; + } + OverwriteOrAdd(self, i, result); + } + + return self; + } + + [RubyMethod("fill")] + public static object Fill(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, IList/*!*/ self, object start, [DefaultParameterValue(null)]object length) { + int startFixnum = (start == null) ? 0 : Protocols.CastToFixnum(context, start); + if (length == null) { + return Fill(context, block, self, startFixnum); + } else { + return Fill(context, block, self, startFixnum, Protocols.CastToFixnum(context, length)); + } + } + + [RubyMethod("fill")] + public static object Fill(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, IList/*!*/ self, Range/*!*/ range) { + int begin = NormalizeIndex(self, Protocols.CastToFixnum(context, range.Begin)); + int end = NormalizeIndex(self, Protocols.CastToFixnum(context, range.End)); + int length = Math.Max(0, end - begin + (range.ExcludeEnd ? 0 : 1)); + + return Fill(context, block, self, begin, length); + } + + #endregion + + #region first + + [RubyMethod("first")] + public static object First(IList/*!*/ self) { + return self.Count == 0 ? null : self[0]; + } + + [RubyMethod("first")] + public static IList/*!*/ First(RubyContext/*!*/ context, IList/*!*/ self, int count) { + if (count < 0) { + throw RubyExceptions.CreateArgumentError("negative array size (or size too big)"); + } + + count = count > self.Count ? self.Count : count; + return GetResultRange(context, self, 0, count); + } + + [RubyMethod("first")] + public static object First(RubyContext/*!*/ context, IList/*!*/ self, object count) { + return First(context, self, Protocols.CastToFixnum(context, count)); + } + + #endregion + + #region flatten, flatten! + + [MultiRuntimeAware] + private static RubyUtils.RecursionTracker _infiniteFlattenTracker = new RubyUtils.RecursionTracker(); + + public static bool TryFlattenArray(RubyContext/*!*/ context, IList list, out IList/*!*/ result) { + // TODO: create correct subclass of RubyArray rather than RubyArray directly + result = new RubyArray(); + + using (IDisposable handle = _infiniteFlattenTracker.TrackObject(list)) { + if (handle == null) { + throw RubyExceptions.CreateArgumentError("tried to flatten recursive array"); + } + bool flattened = false; + for (int i = 0; i < list.Count; ++i) { + IList item = list[i] as IList; + if (item == null) { + item = Protocols.AsArray(context, list[i]); + } + + if (item == null) { + result.Add(list[i]); + } else { + flattened = true; + IList flattenedItem = LibrarySites.InvokeFlatten(context, item); + if (flattenedItem != null) { + AddRange(result, flattenedItem); + } + } + } + return flattened; + } + } + + [RubyMethod("flatten")] + public static IList/*!*/ Flatten(RubyContext/*!*/ context, IList/*!*/ self) { + IList result; + TryFlattenArray(context, self, out result); + return result; + } + + [RubyMethod("flatten!")] + public static IList FlattenInPlace(RubyContext/*!*/ context, IList/*!*/ self) { + IList result; + if (!TryFlattenArray(context, self, out result)) { + return null; + } + + RubyUtils.RequiresNotFrozen(context, self); + self.Clear(); + AddRange(self, result); + return self; + } + + #endregion + + #region include?, index, indexes, indices + + [RubyMethod("include?")] + public static bool Include(RubyContext/*!*/ context, IList/*!*/ self, object item) { + return Index(context, self, item) != null; + } + + [RubyMethod("index")] + public static object Index(RubyContext/*!*/ context, IList/*!*/ self, object item) { + for (int i = 0; i < self.Count; ++i) { + if (Protocols.IsEqual(context, self[i], item)) + return i; + } + return null; + } + + public static RubyArray/*!*/ GetIndicesAsNestedArrays(RubyContext/*!*/ context, IList list, object[] parameters) { + RubyArray result = new RubyArray(); + + for (int i = 0; i < parameters.Length; ++i) { + Range range = parameters[i] as Range; + if (range != null) { + IList fragment = GetElement(context, list, range); + if (fragment != null) { + result.Add(fragment); + } + } else { + result.Add(GetElement(list, Protocols.CastToFixnum(context, parameters[i]))); + } + } + + return result; + } + + // BUG? parameters are not splatted into object[], but instead are being passed in as IList + [RubyMethod("indexes")] + [RubyMethod("indices")] + public static object Indexes(RubyContext/*!*/ context, IList/*!*/ self, [NotNull]params object[] args) { + context.ReportWarning("Array#indexes and Array#indices are deprecated; use Array#values_at"); + return GetIndicesAsNestedArrays(context, self, args); + } + + #endregion + + [RubyMethod("insert")] + public static IList/*!*/ Insert(RubyContext/*!*/ context, IList/*!*/ self, int index, [NotNull]params object[] args) { + if (args.Length > 0) + RubyUtils.RequiresNotFrozen(context, self); + + if (args.Length == 0) { + return self; + } + + if (index == -1) { + AddRange(self, args); + return self; + } + + index = index < 0 ? index + self.Count + 1 : index; + if (index < 0) { + throw RubyExceptions.CreateIndexError(String.Format("index {0} out of array", index)); + } + + if (index >= self.Count) { + ExpandList(self, index); + AddRange(self, args); + return self; + } + + InsertRange(self, index, args); + return self; + + } + + [RubyMethod("inspect")] + public static MutableString Inspect(RubyContext/*!*/ context, IList/*!*/ self) { + using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(self)) { + if (handle == null) { + return MutableString.Create("[...]"); + } + MutableString str = MutableString.CreateMutable(); + str.Append('['); + bool first = true; + foreach (object obj in self) { + if (first) { + first = false; + } else { + str.Append(", "); + } + str.Append(RubySites.Inspect(context, obj)); + } + str.Append(']'); + return str; + } + } + + #region join, to_s + + public static void RecursiveJoin(RubyContext/*!*/ context, IList/*!*/ list, string separator, MutableString/*!*/ result, Dictionary/*!*/ seen) { + Assert.NotNull(list, result, seen); + // TODO: can we get by only tracking List<> ? + // (inspect needs to track everything) + bool found; + if (seen.TryGetValue(list, out found)) { + result.Append("[...]"); + return; + } + + seen.Add(list, true); // push + + for (int i = 0; i < list.Count; ++i) { + object item = list[i]; + + if (item is ValueType) { + result.Append(RubySites.ToS(context, item)); + } else if (item == null) { + // append nothing + } else { + IList listItem = item as IList; + if (listItem != null) { + RecursiveJoin(context, listItem, separator, result, seen); + } else { + result.Append(RubySites.ToS(context, item)); + } + } + + if (i < list.Count - 1) { + result.Append(separator); + } + } + + seen.Remove(list); + } + + [RubyMethod("join")] + [RubyMethod("to_s")] + public static MutableString/*!*/ Join(RubyContext/*!*/ context, [NotNull]IList/*!*/ self) { + MutableString separator = context.ItemSeparator; + return Join(context, self, (separator != null ? separator.ConvertToString() : String.Empty)); + } + + [RubyMethod("join")] + public static MutableString Join(RubyContext/*!*/ context, IList/*!*/ self, string separator) { + Assert.NotNull(context, self, separator); + + MutableString result = MutableString.CreateMutable(); + RecursiveJoin(context, self, separator, result, new Dictionary(ReferenceEqualityComparer.Instance)); + return result; + } + + [RubyMethod("join")] + public static MutableString Join(RubyContext/*!*/ context, IList/*!*/ self, [NotNull]MutableString/*!*/ separator) { + return Join(context, self, separator.ConvertToString()); + } + + #endregion + + [RubyMethod("last")] + public static object Last(IList/*!*/ self) { + return self.Count == 0 ? null : self[self.Count - 1]; + } + + [RubyMethod("last")] + public static IList/*!*/ Last(RubyContext/*!*/ context, IList/*!*/ self, int count) { + if (count < 0) { + throw RubyExceptions.CreateArgumentError("negative array size (or size too big)"); + } + + count = count > self.Count ? self.Count : count; + return GetResultRange(context, self, self.Count - count, count); + } + + [RubyMethod("last")] + public static object Last(RubyContext/*!*/ context, IList/*!*/ self, object count) { + return Last(context, self, Protocols.CastToFixnum(context, count)); + } + + [RubyMethod("length")] + [RubyMethod("size")] + public static int Length(IList/*!*/ self) { + return self.Count; + } + + [RubyMethod("nitems")] + public static int NumberOfNonNilItems(IList/*!*/ self) { + int count = 0; + foreach (object obj in self) + if (obj != null) + count++; + return count; + } + + [RubyMethod("pop")] + public static object Pop(RubyContext/*!*/ context, IList/*!*/ self) { + if (self.Count == 0) + return null; + + RubyUtils.RequiresNotFrozen(context, self); + object result = self[self.Count - 1]; + self.RemoveAt(self.Count - 1); + return result; + } + + [RubyMethod("push")] + public static IList/*!*/ Push(RubyContext/*!*/ context, IList/*!*/ self, [NotNull] params object[] values) { + if (values.Length > 0) + RubyUtils.RequiresNotFrozen(context, self); + AddRange(self, values); + return self; + } + + [RubyMethod("reverse")] + public static RubyArray/*!*/ Reverse(IList/*!*/ self) { + RubyArray reversedList = new RubyArray(self.Count); + for (int i = 0; i < self.Count; ++i) { + reversedList.Add(self[self.Count - i - 1]); + } + return reversedList; + } + + [RubyMethod("reverse!")] + public static IList InPlaceReverse(RubyContext/*!*/ context, IList/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + + int stop = self.Count / 2; + int last = self.Count - 1; + for (int i = 0; i < stop; i++) { + int swap = last - i; + object t = self[i]; + self[i] = self[swap]; + self[swap] = t; + } + return self; + } + + [RubyMethod("rassoc")] + public static IList GetContainerOfSecondItem(RubyContext/*!*/ context, IList/*!*/ self, object item) { + return GetContainerOf(context, self, 1, item); + } + + [RubyMethod("replace")] + [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)] + public static IList Replace(RubyContext/*!*/ context, IList/*!*/ self, [NotNull, DefaultProtocol]IList/*!*/ other) { + RubyUtils.RequiresNotFrozen(context, self); + + self.Clear(); + AddRange(self, other); + return self; + } + + [RubyMethod("rindex")] + public static object ReverseIndex(RubyContext/*!*/ context, IList/*!*/ self, object item) { + for (int i = self.Count - 1; i >= 0; --i) { + if (Protocols.IsEqual(context, self[i], item)) + return i; + } + return null; + } + + [RubyMethod("shift")] + public static object Shift(RubyContext/*!*/ context, IList/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + + if (self.Count == 0) { + return null; + } + + object result = self[0]; + self.RemoveAt(0); + return result; + } + + [RubyMethod("slice!")] + public static object SliceInPlace(RubyContext/*!*/ context, IList/*!*/ self, [DefaultProtocol]int index) { + RubyUtils.RequiresNotFrozen(context, self); + index = index < 0 ? index + self.Count : index; + if (index >= 0 && index < self.Count) { + object result = self[index]; + SetElement(context, self, index, 1, null); + return result; + } else { + return null; + } + } + + [RubyMethod("slice!")] + public static object SliceInPlace(RubyContext/*!*/ context, IList/*!*/ self, [NotNull]Range/*!*/ range) { + RubyUtils.RequiresNotFrozen(context, self); + object result = GetElement(context, self, range); + SetElement(context, self, range, null); + return result; + } + + [RubyMethod("slice!")] + public static IList/*!*/ SliceInPlace(RubyContext/*!*/ context, IList/*!*/ self, [DefaultProtocol]int start, [DefaultProtocol]int length) { + RubyUtils.RequiresNotFrozen(context, self); + IList result = GetElements(context, self, start, length); + SetElement(context, self, start, length, null); + return result; + } + + #region sort, sort! + + [RubyMethod("sort")] + public static IList/*!*/ Sort(RubyContext/*!*/ context, BlockParam block, IList/*!*/ self) { + // TODO: this is not optimal because it makes an extra array copy + // (only affects sorting of .NET types, do we need our own quicksort?) + IList result = CreateResultArray(context, self); + Replace(context, result, ArrayOps.SortInPlace(context, block, ToArray(self))); + return result; + } + + [RubyMethod("sort!")] + public static IList/*!*/ SortInPlace(RubyContext/*!*/ context, BlockParam block, IList/*!*/ self) { + RubyUtils.RequiresNotFrozen(context, self); + // this should always call ArrayOps.SortInPlace instead + Debug.Assert(!(self is RubyArray)); + + // TODO: this is not optimal because it makes an extra array copy + // (only affects sorting of .NET types, do we need our own quicksort?) + Replace(context, self, ArrayOps.SortInPlace(context, block, ToArray(self))); + return self; + } + + #endregion + + [RubyMethod("transpose")] + public static RubyArray/*!*/ Transpose(RubyContext/*!*/ context, IList/*!*/ self) { + // Get the arrays. Note we need to check length as we go, so we call to_ary on all the + // arrays we encounter before the error (if any). + RubyArray result = new RubyArray(); + for (int i = 0; i < self.Count; i++) { + IList list = Protocols.CastToArray(context, self[i]); + + if (i == 0) { + // initialize the result + result.Capacity = list.Count; + for (int j = 0; j < list.Count; j++) { + result.Add(new RubyArray()); + } + } else if (list.Count != result.Count) { + throw RubyExceptions.CreateIndexError(string.Format("element size differs ({0} should be {1})", list.Count, result.Count)); + } + + // add items + Debug.Assert(list.Count == result.Count); + for (int j = 0; j < result.Count; j++) { + ((RubyArray)result[j]).Add(list[j]); + } + } + + return result; + } + + [RubyMethod("to_a")] + [RubyMethod("to_ary")] + public static RubyArray/*!*/ ToArray(IList/*!*/ self) { + RubyArray list = new RubyArray(self.Count); + foreach (object item in self) { + list.Add(item); + } + return list; + } + + [RubyMethod("uniq")] + public static IList/*!*/ Unique(RubyContext/*!*/ context, IList/*!*/ self) { + IList result = CreateResultArray(context, self); + + Dictionary seen = new Dictionary(context.EqualityComparer); + foreach (object item in self) { + if (!seen.ContainsKey(item)) { + result.Add(item); + seen.Add(item, true); + } + } + + return result; + } + + [RubyMethod("uniq!")] + public static IList UniqueSelf(RubyContext/*!*/ context, IList/*!*/ self) { + Dictionary seen = new Dictionary(context.EqualityComparer); + bool modified = false; + int i = 0; + while (i < self.Count) { + object key = self[i]; + if (!seen.ContainsKey(key)) { + seen.Add(key, true); + i++; + } else { + self.RemoveAt(i); + modified = true; + } + } + + return modified ? self : null; + } + + [RubyMethod("unshift")] + public static IList Unshift(RubyContext/*!*/ context, IList/*!*/ self, [NotNull] params object[] args) { + if (args.Length > 0) { + RubyUtils.RequiresNotFrozen(context, self); + } + + InsertRange(self, 0, args); + return self; + } + + [RubyMethod("values_at")] + public static RubyArray/*!*/ ValuesAt(RubyContext/*!*/ context, IList/*!*/ self, [NotNull] params object[] values) { + RubyArray result = new RubyArray(); + + for (int i = 0; i < values.Length; ++i) { + Range range = values[i] as Range; + if (range != null) { + IList fragment = GetElement(context, self, range); + if (fragment != null) { + AddRange(result, fragment); + } + } else + result.Add(GetElement(self, Protocols.CastToFixnum(context, values[i]))); + } + + return result; + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/TypeGroupOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/TypeGroupOps.cs new file mode 100644 index 0000000000..3af72b05ec --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/TypeGroupOps.cs @@ -0,0 +1,74 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic.Binders; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Actions; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Builtins { + [RubyClass(Extends = typeof(TypeGroup)), Includes(typeof(Enumerable))] + public class TypeGroupOps { + + [RubyMethod("new")] + public static RuleGenerator/*!*/ GetInstanceConstructor() { + return new RuleGenerator(RuleGenerators.InstanceConstructorForGroup); + } + + [RubyMethod("of")] + [RubyMethod("[]")] + public static RubyModule/*!*/ Of(RubyContext/*!*/ context, TypeGroup/*!*/ self, [NotNull]params object[]/*!*/ typeArgs) { + TypeTracker tracker = self.GetTypeForArity(typeArgs.Length); + + if (tracker == null) { + throw RubyExceptions.CreateArgumentError(String.Format("Invalid number of type arguments for `{0}'", self.NormalizedName)); + } + + Type concreteType; + if (typeArgs.Length > 0) { + concreteType = tracker.Type.MakeGenericType(Protocols.ToTypes(context, typeArgs)); + } else { + concreteType = tracker.Type; + } + + return context.GetModule(concreteType); + } + + [RubyMethod("each")] + public static object EachType(RubyContext/*!*/ context, BlockParam/*!*/ block, TypeGroup/*!*/ self) { + if (block == null) { + throw RubyExceptions.NoBlockGiven(); + } + + foreach (Type type in self.Types) { + RubyModule module = context.GetModule(type); + object result; + if (block.Yield(module, out result)) { + return result; + } + } + + return self; + } + + [RubyMethod("name")] + [RubyMethod("to_s")] + public static MutableString/*!*/ GetName(TypeGroup/*!*/ self) { + return MutableString.CreateMutable().Append("TypeGroup of ").Append(self.Name); + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/TypeOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/TypeOps.cs new file mode 100644 index 0000000000..8ea6bb3880 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/TypeOps.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + [RubyClass(Extends = typeof(Type))] + public static class TypeOps { + [RubyMethod("to_module")] + public static RubyModule/*!*/ ToModule(RubyContext/*!*/ context, Type/*!*/ self) { + return context.GetModule(self); + } + + [RubyMethod("to_class")] + public static RubyClass/*!*/ ToClass(RubyContext/*!*/ context, Type/*!*/ self) { + if (self.IsInterface) { + RubyExceptions.CreateTypeError("Cannot convert a CLR interface to a Ruby class"); + } + return context.GetClass(self); + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/TypeTrackerOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/TypeTrackerOps.cs new file mode 100644 index 0000000000..26f48c1818 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Extensions/TypeTrackerOps.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + [RubyClass(Extends = typeof(TypeTracker))] + public static class TypeTrackerOps { + [RubyMethod("to_module")] + public static RubyModule/*!*/ ToModule(RubyContext/*!*/ context, TypeTracker/*!*/ self) { + return context.GetModule(self.Type); + } + + [RubyMethod("to_class")] + public static RubyClass/*!*/ ToClass(RubyContext/*!*/ context, TypeTracker/*!*/ self) { + if (self.Type.IsInterface) { + RubyExceptions.CreateTypeError("Cannot convert a CLR interface to a Ruby class"); + } + return context.GetClass(self.Type); + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/FileControl/Fcntl.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/FileControl/Fcntl.cs new file mode 100644 index 0000000000..6173e5e155 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/FileControl/Fcntl.cs @@ -0,0 +1,53 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.FileControl { + + [RubyModule("Fcntl")] + public class Fcntl { + + [RubyConstant] + public const int F_SETFL = 0x01; + + [RubyConstant] + public const int O_CREAT = 0x0100; + + [RubyConstant] + public const int O_EXCL = 0x0400; + + [RubyConstant] + public const int O_TRUNC = 0x0200; + + [RubyConstant] + public const int O_APPEND = 0x0008; + + [RubyConstant] + public const int O_NONBLOCK = 0x01; + + [RubyConstant] + public const int O_RDONLY = 0x0000; + + [RubyConstant] + public const int O_RDWR = 0x0002; + + [RubyConstant] + public const int O_WRONLY = 0x0001; + + [RubyConstant] + public const int O_ACCMODE = O_RDONLY | O_WRONLY | O_RDWR; + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/GenerateInitializers.cmd b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/GenerateInitializers.cmd new file mode 100644 index 0000000000..0549fc72bd --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/GenerateInitializers.cmd @@ -0,0 +1 @@ +"%MERLIN_ROOT%\Bin\Debug\ClassInitGenerator" "%MERLIN_ROOT%\Bin\Debug\IronRuby.Libraries.dll" /libraries:IronRuby.Builtins;IronRuby.StandardLibrary.Threading;IronRuby.StandardLibrary.Sockets;IronRuby.StandardLibrary.OpenSsl;IronRuby.StandardLibrary.Digest;IronRuby.StandardLibrary.Zlib;IronRuby.StandardLibrary.StringIO;IronRuby.StandardLibrary.StringScanner;IronRuby.StandardLibrary.Enumerator;IronRuby.StandardLibrary.FunctionControl;IronRuby.StandardLibrary.FileControl;IronRuby.StandardLibrary.BigDecimal;IronRuby.StandardLibrary.Iconv;IronRuby.StandardLibrary.Clr;IronRuby.StandardLibrary.ParseTree /out:%~dp0\Initializers.Generated.cs diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Iconv/Iconv.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Iconv/Iconv.cs new file mode 100644 index 0000000000..0b746bcb99 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Iconv/Iconv.cs @@ -0,0 +1,115 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Runtime.CompilerServices; +using IronRuby.Builtins; +using IronRuby.Runtime; +using Microsoft.Scripting.Runtime; +using System.Runtime.InteropServices; +using System.Diagnostics; + +namespace IronRuby.StandardLibrary.Iconv { + // TODO: inherits from class Data + [RubyClass("Iconv", Inherits = typeof(Object))] + public class Iconv { + private Decoder _fromEncoding; + private Encoder _toEncoding; + + [RubyConstructor] + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static Iconv/*!*/ Create(RubyClass/*!*/ self, + [DefaultProtocol]MutableString/*!*/ toEncoding, [DefaultProtocol]MutableString/*!*/ fromEncoding) { + + return Initialize(new Iconv(), toEncoding, fromEncoding); + } + + // Reinitialization. Not called when a factory/non-default ctor is called. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static Iconv/*!*/ Initialize(Iconv/*!*/ self, + [DefaultProtocol]MutableString/*!*/ toEncoding, [DefaultProtocol]MutableString/*!*/ fromEncoding) { + + self._toEncoding = RubyEncoding.GetEncodingByRubyName(toEncoding.ConvertToString()).GetEncoder(); + self._fromEncoding = RubyEncoding.GetEncodingByRubyName(fromEncoding.ConvertToString()).GetDecoder(); + + return self; + } + + [RubyMethod("iconv")] + public static MutableString/*!*/ iconv(Iconv/*!*/ self, [DefaultProtocol]MutableString/*!*/ str, + [DefaultProtocol, DefaultParameterValue(0)]int startIndex, [DefaultProtocol, DefaultParameterValue(-1)]int endIndex) { + + // TODO: + int bytesUsed, charsUsed; + bool completed; + + byte[] source = str.ConvertToBytes(); + char[] buffer = new char[self._fromEncoding.GetCharCount(source, 0, source.Length)]; + self._fromEncoding.Convert(source, 0, source.Length, buffer, 0, buffer.Length, false, out bytesUsed, out charsUsed, out completed); + Debug.Assert(charsUsed == buffer.Length && bytesUsed == source.Length); + + byte[] result = new byte[self._toEncoding.GetByteCount(buffer, 0, buffer.Length, false)]; + int bytesEncoded = self._toEncoding.GetBytes(buffer, 0, buffer.Length, result, 0, false); + Debug.Assert(bytesEncoded == result.Length); + + return MutableString.CreateBinary(result); + } + + [RubyMethod("close")] + public static MutableString/*!*/ Close(Iconv/*!*/ self) { + return null; + } + + [RubyMethod("conv", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ Convert(RubyClass/*!*/ self, + [DefaultProtocol]MutableString/*!*/ toEncoding, [DefaultProtocol]MutableString/*!*/ fromEncoding, + [DefaultProtocol]MutableString/*!*/ str) { + + //return iconv(to, from, str).join; + return null; + } + + [RubyMethod("charset_map", RubyMethodAttributes.PublicSingleton)] + public static Hash/*!*/ CharsetMap(RubyClass/*!*/ self) { + // ??? + return new Hash(self.Context); + } + + [RubyMethod("iconv", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ iconv(RubyClass/*!*/ self, + [DefaultProtocol]MutableString/*!*/ toEncoding, [DefaultProtocol]MutableString/*!*/ fromEncoding, + [NotNull]params MutableString[]/*!*/ strings) { + + //Iconv.open(to, from) { |cd| + // (strs + [nil]).collect { |s| cd.iconv(s) } + // } + + return null; + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ Open([NotNull]BlockParam/*!*/ block, RubyClass/*!*/ self, + [DefaultProtocol]MutableString/*!*/ toEncoding, [DefaultProtocol]MutableString/*!*/ fromEncoding) { + // Equivalent to Iconv.new except that when it is called with a block, + // it yields with the new instance and closes it, and returns the result which returned from the block. + + // using Iconv.new(to, from) { yield block; } ensure close + return null; + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs new file mode 100644 index 0000000000..0e5dcc3ab7 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs @@ -0,0 +1,7439 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.Builtins.BuiltinsLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.Threading.ThreadingLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.Sockets.SocketsLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.OpenSsl.OpenSslLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.Digest.DigestLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.Zlib.ZlibLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.StringIO.StringIOLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.StringScanner.StringScannerLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.Enumerator.EnumeratorLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.FunctionControl.FunctionControlLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.FileControl.FileControlLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.BigDecimal.BigDecimalLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.Iconv.IconvLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.Clr.ClrLibraryInitializer))] +[assembly: IronRuby.Runtime.RubyLibraryAttribute(typeof(IronRuby.StandardLibrary.ParseTree.ParseTreeLibraryInitializer))] + +namespace IronRuby.Builtins { + public sealed class BuiltinsLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + Context.RegisterPrimitives( + new System.Action(Load__ClassSingleton_Instance), + new System.Action(Load__ClassSingletonSingleton_Instance), + new System.Action(Load__MainSingleton_Instance), + new System.Action(LoadKernel_Instance), + new System.Action(LoadObject_Instance), + new System.Action(LoadModule_Instance), + new System.Action(LoadClass_Instance), + new System.Action(LoadKernel_Class), + new System.Action(LoadObject_Class), + new System.Action(LoadModule_Class), + new System.Action(LoadClass_Class) + ); + IronRuby.Builtins.RubyClass classRef0 = GetClass(typeof(IronRuby.Builtins.RubyObject)); + + + // Skipped primitive: __ClassSingleton + // Skipped primitive: __MainSingleton + IronRuby.Builtins.RubyModule def30 = DefineGlobalModule("Comparable", typeof(IronRuby.Builtins.Comparable), new System.Action(LoadComparable_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + IronRuby.Builtins.RubyModule def21 = DefineGlobalModule("Enumerable", typeof(IronRuby.Builtins.Enumerable), new System.Action(LoadEnumerable_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + IronRuby.Builtins.RubyModule def3 = DefineGlobalModule("Errno", typeof(IronRuby.Builtins.Errno), null, null, IronRuby.Builtins.RubyModule.EmptyArray); + IronRuby.Builtins.RubyModule def17 = DefineModule("File::Constants", typeof(IronRuby.Builtins.RubyFileOps.Constants), new System.Action(LoadFile__Constants_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + DefineGlobalModule("GC", typeof(IronRuby.Builtins.RubyGC), new System.Action(LoadGC_Instance), new System.Action(LoadGC_Class), IronRuby.Builtins.RubyModule.EmptyArray); + // Skipped primitive: Kernel + DefineGlobalModule("Marshal", typeof(IronRuby.Builtins.RubyMarshal), new System.Action(LoadMarshal_Instance), new System.Action(LoadMarshal_Class), IronRuby.Builtins.RubyModule.EmptyArray); + DefineGlobalModule("Math", typeof(IronRuby.Builtins.RubyMath), new System.Action(LoadMath_Instance), new System.Action(LoadMath_Class), IronRuby.Builtins.RubyModule.EmptyArray); + ExtendClass(typeof(Microsoft.Scripting.Actions.TypeTracker), new System.Action(LoadMicrosoft__Scripting__Actions__TypeTracker_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalModule("ObjectSpace", typeof(IronRuby.Builtins.ObjectSpace), null, new System.Action(LoadObjectSpace_Class), IronRuby.Builtins.RubyModule.EmptyArray); + IronRuby.Builtins.RubyModule def26 = DefineGlobalModule("Precision", typeof(IronRuby.Builtins.Precision), new System.Action(LoadPrecision_Instance), new System.Action(LoadPrecision_Class), IronRuby.Builtins.RubyModule.EmptyArray); + #if !SILVERLIGHT + IronRuby.Builtins.RubyModule def18 = DefineGlobalModule("Process", typeof(IronRuby.Builtins.RubyProcess), new System.Action(LoadProcess_Instance), new System.Action(LoadProcess_Class), IronRuby.Builtins.RubyModule.EmptyArray); + #endif + #if !SILVERLIGHT + DefineGlobalModule("Signal", typeof(IronRuby.Builtins.Signal), null, new System.Action(LoadSignal_Class), IronRuby.Builtins.RubyModule.EmptyArray); + #endif + ExtendClass(typeof(System.Type), new System.Action(LoadSystem__Type_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + // Skipped primitive: __ClassSingletonSingleton + #if !SILVERLIGHT + object def1 = DefineSingleton(new System.Action(Load__Singleton_ArgFilesSingletonOps_Instance), null, new IronRuby.Builtins.RubyModule[] {def21, }); + #endif + object def2 = DefineSingleton(new System.Action(Load__Singleton_EnvironmentSingletonOps_Instance), null, new IronRuby.Builtins.RubyModule[] {def21, }); + ExtendClass(typeof(Microsoft.Scripting.Actions.TypeGroup), new System.Action(LoadMicrosoft__Scripting__Actions__TypeGroup_Instance), null, new IronRuby.Builtins.RubyModule[] {def21, }, null); + // Skipped primitive: Object + DefineGlobalClass("Struct", typeof(IronRuby.Builtins.RubyStruct), classRef0, new System.Action(LoadStruct_Instance), new System.Action(LoadStruct_Class), new IronRuby.Builtins.RubyModule[] {def21, }, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyStructOps.AllocatorUndefined), + }); + ExtendModule(typeof(System.Collections.Generic.IDictionary), new System.Action(LoadSystem__Collections__Generic__IDictionary_Instance), null, new IronRuby.Builtins.RubyModule[] {def21, }); + ExtendModule(typeof(System.Collections.IEnumerable), new System.Action(LoadSystem__Collections__IEnumerable_Instance), null, new IronRuby.Builtins.RubyModule[] {def21, }); + ExtendModule(typeof(System.Collections.IList), new System.Action(LoadSystem__Collections__IList_Instance), null, new IronRuby.Builtins.RubyModule[] {def21, }); + ExtendModule(typeof(System.IComparable), new System.Action(LoadSystem__IComparable_Instance), null, new IronRuby.Builtins.RubyModule[] {def30, }); + DefineGlobalClass("Array", typeof(IronRuby.Builtins.RubyArray), Context.ObjectClass, new System.Action(LoadArray_Instance), new System.Action(LoadArray_Class), new IronRuby.Builtins.RubyModule[] {def21, }, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ArrayOps.CreateArray), + new System.Func(IronRuby.Builtins.ArrayOps.CreateArray), + new System.Func(IronRuby.Builtins.ArrayOps.CreateArray), + }); + DefineGlobalClass("Binding", typeof(IronRuby.Builtins.Binding), Context.ObjectClass, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("ClrString", typeof(System.String), Context.ObjectClass, new System.Action(LoadClrString_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("Dir", typeof(IronRuby.Builtins.RubyDir), Context.ObjectClass, new System.Action(LoadDir_Instance), new System.Action(LoadDir_Class), new IronRuby.Builtins.RubyModule[] {def21, }, null); + #if !SILVERLIGHT + DefineGlobalClass("Encoding", typeof(IronRuby.Builtins.RubyEncoding), Context.ObjectClass, new System.Action(LoadEncoding_Instance), new System.Action(LoadEncoding_Class), IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyEncodingOps.Error), + }); + #endif + IronRuby.Builtins.RubyClass def31 = Context.ExceptionClass = DefineGlobalClass("Exception", typeof(System.Exception), Context.ObjectClass, new System.Action(LoadException_Instance), new System.Action(LoadException_Class), IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__Exception) }); + Context.FalseClass = DefineGlobalClass("FalseClass", typeof(IronRuby.Builtins.FalseClass), Context.ObjectClass, new System.Action(LoadFalseClass_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + #if !SILVERLIGHT + IronRuby.Builtins.RubyClass def20 = DefineClass("File::Stat", typeof(System.IO.FileSystemInfo), Context.ObjectClass, new System.Action(LoadFile__Stat_Instance), null, new IronRuby.Builtins.RubyModule[] {def30, }, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.Create), + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.Create), + }); + #endif + DefineGlobalClass("FileTest", typeof(IronRuby.Builtins.FileTestOps), Context.ObjectClass, null, new System.Action(LoadFileTest_Class), IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("Hash", typeof(IronRuby.Builtins.Hash), Context.ObjectClass, new System.Action(LoadHash_Instance), new System.Action(LoadHash_Class), new IronRuby.Builtins.RubyModule[] {def21, }, new System.Delegate[] { + new System.Func(IronRuby.Builtins.HashOps.Hash), + new System.Func(IronRuby.Builtins.HashOps.Hash), + new System.Func(IronRuby.Builtins.HashOps.Hash), + }); + IronRuby.Builtins.RubyClass def32 = DefineGlobalClass("IO", typeof(IronRuby.Builtins.RubyIO), Context.ObjectClass, new System.Action(LoadIO_Instance), new System.Action(LoadIO_Class), new IronRuby.Builtins.RubyModule[] {def17, }, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.CreateIO), + new System.Func(IronRuby.Builtins.RubyIOOps.CreateIO), + }); + DefineGlobalClass("MatchData", typeof(IronRuby.Builtins.MatchData), Context.ObjectClass, new System.Action(LoadMatchData_Instance), new System.Action(LoadMatchData_Class), IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("Method", typeof(IronRuby.Builtins.RubyMethod), Context.ObjectClass, new System.Action(LoadMethod_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + // Skipped primitive: Module + Context.NilClass = DefineGlobalClass("NilClass", typeof(System.Dynamic.Null), Context.ObjectClass, new System.Action(LoadNilClass_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def25 = DefineGlobalClass("Numeric", typeof(IronRuby.Builtins.Numeric), Context.ObjectClass, new System.Action(LoadNumeric_Instance), null, new IronRuby.Builtins.RubyModule[] {def30, }, null); + DefineGlobalClass("Proc", typeof(IronRuby.Builtins.Proc), Context.ObjectClass, new System.Action(LoadProc_Instance), new System.Action(LoadProc_Class), IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ProcOps.Error), + }); + #if !SILVERLIGHT && !SILVERLIGHT + IronRuby.Builtins.RubyClass def19 = DefineClass("Process::Status", typeof(IronRuby.Builtins.RubyProcess.Status), Context.ObjectClass, new System.Action(LoadProcess__Status_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + #endif + DefineGlobalClass("Range", typeof(IronRuby.Builtins.Range), Context.ObjectClass, new System.Action(LoadRange_Instance), null, new IronRuby.Builtins.RubyModule[] {def21, }, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.CreateRange), + }); + DefineGlobalClass("Regexp", typeof(IronRuby.Builtins.RubyRegex), Context.ObjectClass, new System.Action(LoadRegexp_Instance), new System.Action(LoadRegexp_Class), new IronRuby.Builtins.RubyModule[] {def21, }, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.Create), + new System.Func(IronRuby.Builtins.RegexpOps.Create), + new System.Func(IronRuby.Builtins.RegexpOps.Create), + new System.Func(IronRuby.Builtins.RegexpOps.Create), + new System.Func(IronRuby.Builtins.RegexpOps.Create), + }); + DefineGlobalClass("String", typeof(IronRuby.Builtins.MutableString), Context.ObjectClass, new System.Action(LoadString_Instance), null, new IronRuby.Builtins.RubyModule[] {def21, def30, }, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Create), + new System.Func(IronRuby.Builtins.MutableStringOps.Create), + }); + DefineGlobalClass("Symbol", typeof(Microsoft.Scripting.SymbolId), Context.ObjectClass, new System.Action(LoadSymbol_Instance), new System.Action(LoadSymbol_Class), IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("Thread", typeof(System.Threading.Thread), Context.ObjectClass, new System.Action(LoadThread_Instance), new System.Action(LoadThread_Class), IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("ThreadGroup", typeof(IronRuby.Builtins.ThreadGroup), Context.ObjectClass, new System.Action(LoadThreadGroup_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("Time", typeof(System.DateTime), Context.ObjectClass, new System.Action(LoadTime_Instance), new System.Action(LoadTime_Class), new IronRuby.Builtins.RubyModule[] {def30, }, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Create), + }); + Context.TrueClass = DefineGlobalClass("TrueClass", typeof(IronRuby.Builtins.TrueClass), Context.ObjectClass, new System.Action(LoadTrueClass_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("UnboundMethod", typeof(IronRuby.Builtins.UnboundMethod), Context.ObjectClass, new System.Action(LoadUnboundMethod_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + // Skipped primitive: Class + IronRuby.Builtins.RubyClass def16 = DefineGlobalClass("File", typeof(IronRuby.Builtins.RubyFile), def32, new System.Action(LoadFile_Instance), new System.Action(LoadFile_Class), IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.CreateIO), + new System.Func(IronRuby.Builtins.RubyFileOps.CreateIO), + new System.Func(IronRuby.Builtins.RubyFileOps.CreateIO), + new System.Func(IronRuby.Builtins.RubyFileOps.CreateIO), + new System.Func(IronRuby.Builtins.RubyFileOps.CreateIO), + }); + DefineGlobalClass("Float", typeof(System.Double), def25, new System.Action(LoadFloat_Instance), new System.Action(LoadFloat_Class), new IronRuby.Builtins.RubyModule[] {def26, }, null); + IronRuby.Builtins.RubyClass def33 = DefineGlobalClass("Integer", typeof(IronRuby.Builtins.Integer), def25, new System.Action(LoadInteger_Instance), new System.Action(LoadInteger_Class), new IronRuby.Builtins.RubyModule[] {def26, }, null); + DefineGlobalClass("NoMemoryError", typeof(IronRuby.Builtins.NoMemoryError), def31, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__NoMemoryError) }); + IronRuby.Builtins.RubyClass def28 = DefineGlobalClass("ScriptError", typeof(IronRuby.Builtins.ScriptError), def31, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__ScriptError) }); + IronRuby.Builtins.RubyClass def27 = DefineGlobalClass("SignalException", typeof(IronRuby.Builtins.SignalException), def31, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__SignalException) }); + IronRuby.Builtins.RubyClass def29 = Context.StandardErrorClass = DefineGlobalClass("StandardError", typeof(System.SystemException), def31, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__StandardError) }); + DefineGlobalClass("SystemExit", typeof(IronRuby.Builtins.SystemExit), def31, new System.Action(LoadSystemExit_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.Builtins.SystemExitOps.Factory), + new System.Func(IronRuby.Builtins.SystemExitOps.Factory), + }); + DefineGlobalClass("ArgumentError", typeof(System.ArgumentException), def29, new System.Action(LoadArgumentError_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__ArgumentError) }); + DefineGlobalClass("Bignum", typeof(Microsoft.Scripting.Math.BigInteger), def33, new System.Action(LoadBignum_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("Fixnum", typeof(System.Int32), def33, new System.Action(LoadFixnum_Instance), new System.Action(LoadFixnum_Class), IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("IndexError", typeof(System.IndexOutOfRangeException), def29, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__IndexError) }); + DefineGlobalClass("Interrupt", typeof(IronRuby.Builtins.Interrupt), def27, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__Interrupt) }); + IronRuby.Builtins.RubyClass def22 = DefineGlobalClass("IOError", typeof(System.IO.IOException), def29, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__IOError) }); + DefineGlobalClass("LoadError", typeof(IronRuby.Builtins.LoadError), def28, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__LoadError) }); + DefineGlobalClass("LocalJumpError", typeof(IronRuby.Builtins.LocalJumpError), def29, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__LocalJumpError) }); + IronRuby.Builtins.RubyClass def34 = DefineGlobalClass("NameError", typeof(System.MemberAccessException), def29, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__NameError) }); + DefineGlobalClass("NotImplementedError", typeof(IronRuby.Builtins.NotImplementedError), def28, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__NotImplementedError) }); + IronRuby.Builtins.RubyClass def24 = DefineGlobalClass("RangeError", typeof(System.ArgumentOutOfRangeException), def29, new System.Action(LoadRangeError_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__RangeError) }); + DefineGlobalClass("RegexpError", typeof(IronRuby.Builtins.RegexpError), def29, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__RegexpError) }); + DefineGlobalClass("RuntimeError", typeof(IronRuby.Builtins.RuntimeError), def29, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__RuntimeError) }); + DefineGlobalClass("SecurityError", typeof(System.Security.SecurityException), def29, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__SecurityError) }); + DefineGlobalClass("SyntaxError", typeof(IronRuby.Builtins.SyntaxError), def28, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__SyntaxError) }); + IronRuby.Builtins.RubyClass def23 = DefineGlobalClass("SystemCallError", typeof(System.Runtime.InteropServices.ExternalException), def29, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.Builtins.SystemCallErrorOps.Factory), + new System.Func(IronRuby.Builtins.SystemCallErrorOps.Factory), + }); + DefineGlobalClass("SystemStackError", typeof(IronRuby.Builtins.SystemStackError), def29, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__SystemStackError) }); + DefineGlobalClass("ThreadError", typeof(IronRuby.Builtins.ThreadError), def29, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__ThreadError) }); + DefineGlobalClass("TypeError", typeof(System.InvalidOperationException), def29, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__TypeError) }); + DefineGlobalClass("ZeroDivisionError", typeof(System.DivideByZeroException), def29, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__ZeroDivisionError) }); + DefineGlobalClass("EOFError", typeof(IronRuby.Builtins.EOFError), def22, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__EOFError) }); + IronRuby.Builtins.RubyClass def4 = DefineClass("Errno::EACCES", typeof(IronRuby.Builtins.Errno.AccessError), def23, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def5 = DefineClass("Errno::EADDRINUSE", typeof(IronRuby.Builtins.Errno.AddressInUseError), def23, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def6 = DefineClass("Errno::EBADF", typeof(IronRuby.Builtins.Errno.BadFileDescriptorError), def23, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def7 = DefineClass("Errno::ECONNABORTED", typeof(IronRuby.Builtins.Errno.ConnectionAbortError), def23, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def8 = DefineClass("Errno::ECONNRESET", typeof(IronRuby.Builtins.Errno.ConnectionResetError), def23, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def9 = DefineClass("Errno::EDOM", typeof(IronRuby.Builtins.Errno.DomainError), def23, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def10 = DefineClass("Errno::EEXIST", typeof(IronRuby.Builtins.Errno.ExistError), def23, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def11 = DefineClass("Errno::EINVAL", typeof(IronRuby.Builtins.Errno.InvalidError), def23, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def12 = DefineClass("Errno::ENOENT", typeof(IronRuby.Builtins.Errno.NoEntryError), def23, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def13 = DefineClass("Errno::ENOTCONN", typeof(IronRuby.Builtins.Errno.NotConnectedError), def23, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def14 = DefineClass("Errno::ENOTDIR", typeof(IronRuby.Builtins.Errno.NotDirectoryError), def23, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def15 = DefineClass("Errno::EPIPE", typeof(IronRuby.Builtins.Errno.PipeError), def23, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("FloatDomainError", typeof(IronRuby.Builtins.FloatDomainError), def24, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__FloatDomainError) }); + DefineGlobalClass("NoMethodError", typeof(System.MissingMethodException), def34, new System.Action(LoadNoMethodError_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(BuiltinsLibraryInitializer.ExceptionFactory__NoMethodError) }); + def16.SetConstant("Constants", def17); + #if !SILVERLIGHT + Context.ObjectClass.SetConstant("ARGF", def1); + #endif + Context.ObjectClass.SetConstant("ENV", def2); + #if !SILVERLIGHT + def16.SetConstant("Stat", def20); + #endif + #if !SILVERLIGHT && !SILVERLIGHT + def18.SetConstant("Status", def19); + #endif + def3.SetConstant("EACCES", def4); + def3.SetConstant("EADDRINUSE", def5); + def3.SetConstant("EBADF", def6); + def3.SetConstant("ECONNABORTED", def7); + def3.SetConstant("ECONNRESET", def8); + def3.SetConstant("EDOM", def9); + def3.SetConstant("EEXIST", def10); + def3.SetConstant("EINVAL", def11); + def3.SetConstant("ENOENT", def12); + def3.SetConstant("ENOTCONN", def13); + def3.SetConstant("ENOTDIR", def14); + def3.SetConstant("EPIPE", def15); + } + + private void Load__ClassSingleton_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("allocate", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ClassSingletonOps.Allocate), + }); + + module.DefineLibraryMethod("inherited", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ClassSingletonOps.Inherited), + }); + + module.DefineLibraryMethod("initialize", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ClassSingletonOps.Initialize), + }); + + module.DefineLibraryMethod("initialize_copy", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ClassSingletonOps.InitializeCopy), + }); + + module.DefineLibraryMethod("new", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ClassSingletonOps.New), + }); + + module.DefineLibraryMethod("superclass", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ClassSingletonOps.GetSuperClass), + }); + + } + + private void Load__ClassSingleton_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + } + + private void Load__ClassSingletonSingleton_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + Load__ClassSingleton_Instance(module); + module.DefineLibraryMethod("constants", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ClassSingletonSingletonOps.GetConstants), + }); + + module.DefineLibraryMethod("nesting", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ClassSingletonSingletonOps.GetNesting), + }); + + } + + private void Load__ClassSingletonSingleton_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + Load__ClassSingleton_Class(module); + } + + private void Load__MainSingleton_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("include", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MainSingletonOps.Include), + }); + + module.DefineLibraryMethod("initialize", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MainSingletonOps.Initialize), + }); + + module.DefineLibraryMethod("private", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MainSingletonOps.SetPrivateVisibility), + }); + + module.DefineLibraryMethod("public", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MainSingletonOps.SetPublicVisibility), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MainSingletonOps.ToS), + }); + + } + + private void Load__MainSingleton_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + } + + #if !SILVERLIGHT + private void Load__Singleton_ArgFilesSingletonOps_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("filename", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ArgFilesSingletonOps.GetCurrentFileName), + }); + + } + #endif + + private void LoadArgumentError_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.HideMethod("message"); + } + + private void LoadArray_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + LoadSystem__Collections__IList_Instance(module); + module.DefineLibraryMethod("initialize", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ArrayOps.Reinitialize), + new System.Func(IronRuby.Builtins.ArrayOps.Reinitialize), + new System.Func(IronRuby.Builtins.ArrayOps.ReinitializeByRepeatedValue), + }); + + module.DefineLibraryMethod("pack", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ArrayOps.Pack), + }); + + module.DefineLibraryMethod("reverse!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ArrayOps.InPlaceReverse), + }); + + module.DefineLibraryMethod("reverse_each", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ArrayOps.ReverseEach), + }); + + module.DefineLibraryMethod("sort", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ArrayOps.Sort), + }); + + module.DefineLibraryMethod("sort!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ArrayOps.SortInPlace), + }); + + module.DefineLibraryMethod("to_a", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ArrayOps.ToArray), + }); + + module.DefineLibraryMethod("to_ary", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ArrayOps.ToArray), + }); + + } + + private void LoadArray_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("[]", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ArrayOps.MakeArray), + }); + + } + + private void LoadBignum_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("-", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Subtract), + new System.Func(IronRuby.Builtins.BignumOps.Subtract), + new System.Func(IronRuby.Builtins.BignumOps.Subtract), + }); + + module.DefineLibraryMethod("%", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Modulo), + new System.Func(IronRuby.Builtins.BignumOps.ModuloOp), + }); + + module.DefineLibraryMethod("&", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.And), + new System.Func(IronRuby.Builtins.BignumOps.And), + new System.Func(IronRuby.Builtins.BignumOps.And), + }); + + module.DefineLibraryMethod("*", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Multiply), + new System.Func(IronRuby.Builtins.BignumOps.Multiply), + new System.Func(IronRuby.Builtins.BignumOps.Multiply), + }); + + module.DefineLibraryMethod("**", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Power), + new System.Func(IronRuby.Builtins.BignumOps.Power), + new System.Func(IronRuby.Builtins.BignumOps.Power), + new System.Func(IronRuby.Builtins.BignumOps.Power), + }); + + module.DefineLibraryMethod("/", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Divide), + new System.Func(IronRuby.Builtins.BignumOps.Divide), + new System.Func(IronRuby.Builtins.BignumOps.Divide), + }); + + module.DefineLibraryMethod("-@", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Negate), + }); + + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Bit), + new System.Func(IronRuby.Builtins.BignumOps.Bit), + new System.Func(IronRuby.Builtins.BignumOps.Bit), + }); + + module.DefineLibraryMethod("^", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Xor), + new System.Func(IronRuby.Builtins.BignumOps.Xor), + new System.Func(IronRuby.Builtins.BignumOps.Xor), + }); + + module.DefineLibraryMethod("|", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.BitwiseOr), + new System.Func(IronRuby.Builtins.BignumOps.BitwiseOr), + new System.Func(IronRuby.Builtins.BignumOps.BitwiseOr), + }); + + module.DefineLibraryMethod("~", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Invert), + }); + + module.DefineLibraryMethod("+", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Add), + new System.Func(IronRuby.Builtins.BignumOps.Add), + new System.Func(IronRuby.Builtins.BignumOps.Add), + }); + + module.DefineLibraryMethod("<<", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.LeftShift), + new System.Func(IronRuby.Builtins.BignumOps.LeftShift), + new System.Func(IronRuby.Builtins.BignumOps.LeftShift), + }); + + module.DefineLibraryMethod("<=>", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Compare), + new System.Func(IronRuby.Builtins.BignumOps.Compare), + new System.Func(IronRuby.Builtins.BignumOps.Compare), + }); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Equal), + new System.Func(IronRuby.Builtins.BignumOps.Equal), + new System.Func(IronRuby.Builtins.BignumOps.Equal), + }); + + module.DefineLibraryMethod(">>", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.RightShift), + new System.Func(IronRuby.Builtins.BignumOps.RightShift), + new System.Func(IronRuby.Builtins.BignumOps.RightShift), + }); + + module.DefineLibraryMethod("abs", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Abs), + }); + + module.DefineLibraryMethod("coerce", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Coerce), + new System.Func(IronRuby.Builtins.BignumOps.Coerce), + }); + + module.DefineLibraryMethod("div", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Divide), + new System.Func(IronRuby.Builtins.BignumOps.Divide), + new System.Func(IronRuby.Builtins.BignumOps.Div), + }); + + module.DefineLibraryMethod("divmod", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.DivMod), + new System.Func(IronRuby.Builtins.BignumOps.DivMod), + }); + + module.DefineLibraryMethod("eql?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Eql), + new System.Func(IronRuby.Builtins.BignumOps.Eql), + new System.Func(IronRuby.Builtins.BignumOps.Eql), + }); + + module.DefineLibraryMethod("hash", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Hash), + }); + + module.DefineLibraryMethod("modulo", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Modulo), + new System.Func(IronRuby.Builtins.BignumOps.Modulo), + }); + + module.DefineLibraryMethod("quo", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Quotient), + new System.Func(IronRuby.Builtins.BignumOps.Quotient), + new System.Func(IronRuby.Builtins.BignumOps.Quotient), + }); + + module.DefineLibraryMethod("remainder", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Remainder), + new System.Func(IronRuby.Builtins.BignumOps.Remainder), + }); + + module.DefineLibraryMethod("size", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.Size), + }); + + module.DefineLibraryMethod("to_f", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.ToFloat), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.BignumOps.ToString), + new System.Func(IronRuby.Builtins.BignumOps.ToString), + }); + + } + + private void LoadClass_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.UndefineLibraryMethod("append_features"); + module.UndefineLibraryMethod("extend_object"); + module.UndefineLibraryMethod("module_function"); + module.DefineRuleGenerator("allocate", 0x51, IronRuby.Builtins.ClassOps.GetInstanceAllocator()); + + module.DefineLibraryMethod("inherited", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ClassOps.Inherited), + }); + + module.DefineLibraryMethod("initialize", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ClassOps.Reinitialize), + }); + + module.DefineLibraryMethod("initialize_copy", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ClassOps.InitializeCopy), + }); + + module.DefineRuleGenerator("new", 0x51, IronRuby.Builtins.ClassOps.GetInstanceConstructor()); + + module.DefineLibraryMethod("superclass", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ClassOps.GetSuperclass), + }); + + } + + private void LoadClass_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + } + + private void LoadClrString_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.StringOps.Equals), + }); + + module.DefineLibraryMethod("===", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.StringOps.Equals), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.StringOps.Inspect), + }); + + module.DefineLibraryMethod("to_clr_string", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.StringOps.ToClrString), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.StringOps.ToStr), + }); + + module.DefineLibraryMethod("to_str", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.StringOps.ToStr), + }); + + } + + private void LoadComparable_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("<", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Comparable.Less), + }); + + module.DefineLibraryMethod("<=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Comparable.LessOrEqual), + }); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Comparable.Equal), + }); + + module.DefineLibraryMethod(">", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Comparable.Greater), + }); + + module.DefineLibraryMethod(">=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Comparable.GreaterOrEqual), + }); + + module.DefineLibraryMethod("between?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Comparable.Between), + }); + + } + + private void LoadDir_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("close", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyDir.Close), + }); + + module.DefineLibraryMethod("each", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.Each), + }); + + module.DefineLibraryMethod("path", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.GetPath), + }); + + module.DefineLibraryMethod("pos", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.GetCurrentPosition), + }); + + module.DefineLibraryMethod("pos=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.SetPosition), + }); + + module.DefineLibraryMethod("read", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.Read), + }); + + module.DefineLibraryMethod("rewind", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.Rewind), + }); + + module.DefineLibraryMethod("seek", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.Seek), + }); + + module.DefineLibraryMethod("tell", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.GetCurrentPosition), + }); + + } + + private void LoadDir_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("[]", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.Glob), + }); + + module.DefineLibraryMethod("chdir", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.ChangeDirectory), + new System.Func(IronRuby.Builtins.RubyDir.ChangeDirectory), + new System.Func(IronRuby.Builtins.RubyDir.ChangeDirectory), + }); + + module.DefineLibraryMethod("chroot", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.ChangeRoot), + }); + + module.DefineLibraryMethod("delete", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.RemoveDirectory), + }); + + module.DefineLibraryMethod("entries", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.GetEntries), + }); + + module.DefineLibraryMethod("foreach", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.ForEach), + }); + + module.DefineLibraryMethod("getwd", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.GetCurrentDirectory), + }); + + module.DefineLibraryMethod("glob", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.Glob), + new System.Func(IronRuby.Builtins.RubyDir.Glob), + }); + + module.DefineLibraryMethod("mkdir", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.MakeDirectory), + }); + + module.DefineLibraryMethod("open", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.Open), + new System.Func(IronRuby.Builtins.RubyDir.Open), + }); + + module.DefineLibraryMethod("pwd", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.GetCurrentDirectory), + }); + + module.DefineLibraryMethod("rmdir", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.RemoveDirectory), + }); + + module.DefineLibraryMethod("unlink", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyDir.RemoveDirectory), + }); + + } + + #if !SILVERLIGHT + private void LoadEncoding_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("_dump", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyEncodingOps.ToS), + }); + + module.DefineLibraryMethod("based_encoding", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyEncodingOps.BasedEncoding), + }); + + module.DefineLibraryMethod("dummy?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyEncodingOps.IsDummy), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyEncodingOps.Inspect), + }); + + module.DefineLibraryMethod("name", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyEncodingOps.ToS), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyEncodingOps.ToS), + }); + + } + #endif + + #if !SILVERLIGHT + private void LoadEncoding_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("_load?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyEncodingOps.Load), + }); + + module.DefineLibraryMethod("compatible?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyEncodingOps.IsCompatible), + }); + + module.DefineLibraryMethod("default_external", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyEncodingOps.GetDefaultEncoding), + }); + + module.DefineLibraryMethod("find", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyEncodingOps.GetEncoding), + }); + + module.DefineLibraryMethod("list", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyEncodingOps.GetAvailableEncodings), + }); + + module.DefineLibraryMethod("locale_charmap", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyEncodingOps.GetDefaultCharmap), + }); + + } + #endif + + private void LoadEnumerable_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("all?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.TrueForAll), + }); + + module.DefineLibraryMethod("any?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.TrueForAny), + }); + + module.DefineLibraryMethod("collect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Map), + }); + + module.DefineLibraryMethod("detect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Find), + }); + + module.DefineLibraryMethod("each_with_index", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.EachWithIndex), + }); + + module.DefineLibraryMethod("entries", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.ToArray), + }); + + module.DefineLibraryMethod("find", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Find), + }); + + module.DefineLibraryMethod("find_all", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Select), + }); + + module.DefineLibraryMethod("grep", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Grep), + }); + + module.DefineLibraryMethod("include?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Contains), + }); + + module.DefineLibraryMethod("inject", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Inject), + }); + + module.DefineLibraryMethod("map", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Map), + }); + + module.DefineLibraryMethod("max", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.GetMaximum), + }); + + module.DefineLibraryMethod("member?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Contains), + }); + + module.DefineLibraryMethod("min", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.GetMinimum), + }); + + module.DefineLibraryMethod("partition", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Partition), + }); + + module.DefineLibraryMethod("reject", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Reject), + }); + + module.DefineLibraryMethod("select", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Select), + }); + + module.DefineLibraryMethod("sort", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Sort), + }); + + module.DefineLibraryMethod("sort_by", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.SortBy), + }); + + module.DefineLibraryMethod("to_a", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.ToArray), + }); + + module.DefineLibraryMethod("zip", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Enumerable.Zip), + }); + + } + + private void Load__Singleton_EnvironmentSingletonOps_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.GetVariable), + }); + + module.DefineLibraryMethod("[]=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.SetVariable), + }); + + module.DefineLibraryMethod("clear", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Clear), + }); + + module.DefineLibraryMethod("delete", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Delete), + }); + + module.DefineLibraryMethod("delete_if", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.DeleteIf), + }); + + module.DefineLibraryMethod("each", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Each), + }); + + module.DefineLibraryMethod("each_key", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.EachKey), + }); + + module.DefineLibraryMethod("each_pair", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Each), + }); + + module.DefineLibraryMethod("each_value", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.EachValue), + }); + + module.DefineLibraryMethod("empty?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.IsEmpty), + }); + + module.DefineLibraryMethod("fetch", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.GetVariable), + }); + + module.DefineLibraryMethod("has_key?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.HasKey), + }); + + module.DefineLibraryMethod("has_value?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.HasValue), + }); + + module.DefineLibraryMethod("include?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.HasKey), + }); + + module.DefineLibraryMethod("index", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Index), + }); + + module.DefineLibraryMethod("indexes", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Index), + }); + + module.DefineLibraryMethod("indices", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Indices), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Inspect), + }); + + module.DefineLibraryMethod("invert", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Invert), + }); + + module.DefineLibraryMethod("key?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.HasKey), + }); + + module.DefineLibraryMethod("keys", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Keys), + }); + + module.DefineLibraryMethod("length", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Length), + }); + + module.DefineLibraryMethod("rehash", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Rehash), + }); + + module.DefineLibraryMethod("reject!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.DeleteIf), + }); + + module.DefineLibraryMethod("replace", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Replace), + }); + + module.DefineLibraryMethod("shift", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Shift), + }); + + module.DefineLibraryMethod("size", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Length), + }); + + module.DefineLibraryMethod("store", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.SetVariable), + }); + + module.DefineLibraryMethod("to_hash", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.ToHash), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.ToString), + }); + + module.DefineLibraryMethod("update", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Update), + }); + + module.DefineLibraryMethod("value?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.HasValue), + }); + + module.DefineLibraryMethod("values", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.Values), + }); + + module.DefineLibraryMethod("values_at", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.EnvironmentSingletonOps.ValuesAt), + }); + + } + + private void LoadException_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("backtrace", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ExceptionOps.GetBacktrace), + }); + + module.DefineRuleGenerator("exception", 0x51, IronRuby.Builtins.ExceptionOps.GetException()); + + module.DefineLibraryMethod("initialize", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ExceptionOps.ReinitializeException), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ExceptionOps.Inspect), + }); + + module.DefineLibraryMethod("message", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ExceptionOps.GetMessage), + }); + + module.DefineLibraryMethod("set_backtrace", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ExceptionOps.SetBacktrace), + new System.Func(IronRuby.Builtins.ExceptionOps.SetBacktrace), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ExceptionOps.GetMessage), + }); + + module.DefineLibraryMethod("to_str", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ExceptionOps.GetMessage), + }); + + } + + private void LoadException_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineRuleGenerator("exception", 0x61, IronRuby.Builtins.ExceptionOps.CreateException()); + + } + + private void LoadFalseClass_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("&", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FalseClass.And), + }); + + module.DefineLibraryMethod("^", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FalseClass.Xor), + new System.Func(IronRuby.Builtins.FalseClass.Xor), + }); + + module.DefineLibraryMethod("|", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FalseClass.Or), + new System.Func(IronRuby.Builtins.FalseClass.Or), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FalseClass.ToString), + }); + + } + + private void LoadFile_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("ALT_SEPARATOR", IronRuby.Builtins.RubyFileOps.ALT_SEPARATOR); + module.SetConstant("PATH_SEPARATOR", IronRuby.Builtins.RubyFileOps.PATH_SEPARATOR); + module.SetConstant("Separator", IronRuby.Builtins.RubyFileOps.Separator); + module.SetConstant("SEPARATOR", IronRuby.Builtins.RubyFileOps.SEPARATOR); + + module.DefineLibraryMethod("atime", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.AccessTime), + }); + + module.DefineLibraryMethod("ctime", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.CreateTime), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Inspect), + }); + + module.DefineLibraryMethod("lstat", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Stat), + }); + + module.DefineLibraryMethod("mtime", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.ModifiedTime), + }); + + module.DefineLibraryMethod("path", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.GetPath), + }); + + } + + private void LoadFile_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("atime", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.AccessTime), + }); + + module.DefineLibraryMethod("basename", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Basename), + }); + + module.DefineLibraryMethod("blockdev?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsBlockDevice), + }); + + module.DefineLibraryMethod("chardev?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsCharDevice), + }); + + module.DefineLibraryMethod("chmod", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Chmod), + }); + + module.DefineLibraryMethod("ctime", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.CreateTime), + }); + + module.DefineLibraryMethod("delete", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Delete), + new System.Func(IronRuby.Builtins.RubyFileOps.Delete), + }); + + module.DefineLibraryMethod("directory?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsDirectory), + }); + + module.DefineLibraryMethod("dirname", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.DirName), + }); + + module.DefineLibraryMethod("executable?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsExecutable), + }); + + module.DefineLibraryMethod("executable_real?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsExecutable), + }); + + module.DefineLibraryMethod("exist?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Exists), + }); + + module.DefineLibraryMethod("exists?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Exists), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("expand_path", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.ExpandPath), + }); + + #endif + module.DefineLibraryMethod("extname", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.GetExtension), + }); + + module.DefineLibraryMethod("file?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsAFile), + }); + + module.DefineLibraryMethod("fnmatch", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.FnMatch), + }); + + module.DefineLibraryMethod("fnmatch?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.FnMatch), + }); + + module.DefineLibraryMethod("ftype", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.FileType), + }); + + module.DefineLibraryMethod("grpowned?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsGroupOwned), + }); + + module.DefineLibraryMethod("join", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Join), + }); + + module.DefineLibraryMethod("lstat", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Stat), + }); + + module.DefineLibraryMethod("mtime", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.ModifiedTime), + }); + + module.DefineLibraryMethod("open", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Open), + new System.Func(IronRuby.Builtins.RubyFileOps.Open), + new System.Func(IronRuby.Builtins.RubyFileOps.Open), + new System.Func(IronRuby.Builtins.RubyFileOps.Open), + new System.Func(IronRuby.Builtins.RubyFileOps.Open), + new System.Func(IronRuby.Builtins.RubyFileOps.Open), + new System.Func(IronRuby.Builtins.RubyFileOps.Open), + new System.Func(IronRuby.Builtins.RubyFileOps.Open), + }); + + module.DefineLibraryMethod("owned?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsUserOwned), + }); + + module.DefineLibraryMethod("pipe?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsPipe), + }); + + module.DefineLibraryMethod("readable?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsReadable), + }); + + module.DefineLibraryMethod("readable_real?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsReadable), + }); + + module.DefineLibraryMethod("readlink", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Readlink), + }); + + module.DefineLibraryMethod("rename", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Rename), + }); + + module.DefineLibraryMethod("setgid?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsSetGid), + }); + + module.DefineLibraryMethod("setuid?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsSetUid), + }); + + module.DefineLibraryMethod("size", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Size), + }); + + module.DefineLibraryMethod("size?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.NullableSize), + }); + + module.DefineLibraryMethod("socket?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsSocket), + }); + + module.DefineLibraryMethod("split", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Split), + }); + + module.DefineLibraryMethod("stat", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Stat), + }); + + module.DefineLibraryMethod("sticky?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsSticky), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("symlink", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.SymLink), + }); + + #endif + #if !SILVERLIGHT + module.DefineLibraryMethod("symlink?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsSymLink), + }); + + #endif + module.DefineLibraryMethod("unlink", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.Delete), + new System.Func(IronRuby.Builtins.RubyFileOps.Delete), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("utime", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.UpdateTimes), + new System.Func(IronRuby.Builtins.RubyFileOps.UpdateTimes), + }); + + #endif + module.DefineLibraryMethod("writable?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsWritable), + }); + + module.DefineLibraryMethod("writable_real?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsWritable), + }); + + module.DefineLibraryMethod("zero?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.IsZeroLength), + }); + + } + + private void LoadFile__Constants_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("APPEND", IronRuby.Builtins.RubyFileOps.Constants.APPEND); + module.SetConstant("BINARY", IronRuby.Builtins.RubyFileOps.Constants.BINARY); + module.SetConstant("CREAT", IronRuby.Builtins.RubyFileOps.Constants.CREAT); + module.SetConstant("EXCL", IronRuby.Builtins.RubyFileOps.Constants.EXCL); + module.SetConstant("FNM_CASEFOLD", IronRuby.Builtins.RubyFileOps.Constants.FNM_CASEFOLD); + module.SetConstant("FNM_DOTMATCH", IronRuby.Builtins.RubyFileOps.Constants.FNM_DOTMATCH); + module.SetConstant("FNM_NOESCAPE", IronRuby.Builtins.RubyFileOps.Constants.FNM_NOESCAPE); + module.SetConstant("FNM_PATHNAME", IronRuby.Builtins.RubyFileOps.Constants.FNM_PATHNAME); + module.SetConstant("FNM_SYSCASE", IronRuby.Builtins.RubyFileOps.Constants.FNM_SYSCASE); + module.SetConstant("LOCK_EX", IronRuby.Builtins.RubyFileOps.Constants.LOCK_EX); + module.SetConstant("LOCK_NB", IronRuby.Builtins.RubyFileOps.Constants.LOCK_NB); + module.SetConstant("LOCK_SH", IronRuby.Builtins.RubyFileOps.Constants.LOCK_SH); + module.SetConstant("LOCK_UN", IronRuby.Builtins.RubyFileOps.Constants.LOCK_UN); + module.SetConstant("NONBLOCK", IronRuby.Builtins.RubyFileOps.Constants.NONBLOCK); + module.SetConstant("RDONLY", IronRuby.Builtins.RubyFileOps.Constants.RDONLY); + module.SetConstant("RDWR", IronRuby.Builtins.RubyFileOps.Constants.RDWR); + module.SetConstant("SEEK_CUR", IronRuby.Builtins.RubyFileOps.Constants.SEEK_CUR); + module.SetConstant("SEEK_END", IronRuby.Builtins.RubyFileOps.Constants.SEEK_END); + module.SetConstant("SEEK_SET", IronRuby.Builtins.RubyFileOps.Constants.SEEK_SET); + module.SetConstant("TRUNC", IronRuby.Builtins.RubyFileOps.Constants.TRUNC); + module.SetConstant("WRONLY", IronRuby.Builtins.RubyFileOps.Constants.WRONLY); + + } + + #if !SILVERLIGHT + private void LoadFile__Stat_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("<=>", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.Compare), + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.Compare), + }); + + module.DefineLibraryMethod("atime", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.AccessTime), + }); + + module.DefineLibraryMethod("blksize", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.BlockSize), + }); + + module.DefineLibraryMethod("blockdev?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsBlockDevice), + }); + + module.DefineLibraryMethod("blocks", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.Blocks), + }); + + module.DefineLibraryMethod("chardev?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsCharDevice), + }); + + module.DefineLibraryMethod("ctime", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.CreateTime), + }); + + module.DefineLibraryMethod("dev", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.DeviceId), + }); + + module.DefineLibraryMethod("dev_major", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.DeviceIdMajor), + }); + + module.DefineLibraryMethod("dev_minor", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.DeviceIdMinor), + }); + + module.DefineLibraryMethod("directory?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsDirectory), + }); + + module.DefineLibraryMethod("executable?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsExecutable), + }); + + module.DefineLibraryMethod("executable_real?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsExecutable), + }); + + module.DefineLibraryMethod("file?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsFile), + }); + + module.DefineLibraryMethod("ftype", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.FileType), + }); + + module.DefineLibraryMethod("gid", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.GroupId), + }); + + module.DefineLibraryMethod("grpowned?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsGroupOwned), + }); + + module.DefineLibraryMethod("ino", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.Inode), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.Inspect), + }); + + module.DefineLibraryMethod("mode", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.Mode), + }); + + module.DefineLibraryMethod("mtime", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.ModifiedTime), + }); + + module.DefineLibraryMethod("nlink", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.NumberOfLinks), + }); + + module.DefineLibraryMethod("owned?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsUserOwned), + }); + + module.DefineLibraryMethod("pipe?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsPipe), + }); + + module.DefineLibraryMethod("rdev", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.DeviceId), + }); + + module.DefineLibraryMethod("rdev_major", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.DeviceIdMajor), + }); + + module.DefineLibraryMethod("rdev_minor", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.DeviceIdMinor), + }); + + module.DefineLibraryMethod("readable?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsReadable), + }); + + module.DefineLibraryMethod("readable_real?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsReadable), + }); + + module.DefineLibraryMethod("setgid?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsSetGid), + }); + + module.DefineLibraryMethod("setuid?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsSetUid), + }); + + module.DefineLibraryMethod("size", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.Size), + }); + + module.DefineLibraryMethod("size?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.NullableSize), + }); + + module.DefineLibraryMethod("socket?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsSocket), + }); + + module.DefineLibraryMethod("sticky?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsSticky), + }); + + module.DefineLibraryMethod("symlink?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsSymLink), + }); + + module.DefineLibraryMethod("uid", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.UserId), + }); + + module.DefineLibraryMethod("writable?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsWritable), + }); + + module.DefineLibraryMethod("writable_real?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsWritable), + }); + + module.DefineLibraryMethod("zero?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyFileOps.RubyStatOps.IsZeroLength), + }); + + } + #endif + + private void LoadFileTest_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("exist?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FileTestOps.Exists), + }); + + module.DefineLibraryMethod("exists?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FileTestOps.Exists), + }); + + } + + private void LoadFixnum_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("-", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.Subtract), + new System.Func(IronRuby.Builtins.FixnumOps.Subtract), + new System.Func(IronRuby.Builtins.FixnumOps.Subtract), + }); + + module.DefineLibraryMethod("%", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.ModuloOp), + new System.Func(IronRuby.Builtins.FixnumOps.ModuloOp), + }); + + module.DefineLibraryMethod("&", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.BitwiseAnd), + new System.Func(IronRuby.Builtins.FixnumOps.BitwiseAnd), + new System.Func(IronRuby.Builtins.FixnumOps.BitwiseAnd), + }); + + module.DefineLibraryMethod("*", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.Multiply), + new System.Func(IronRuby.Builtins.FixnumOps.Multiply), + new System.Func(IronRuby.Builtins.FixnumOps.Multiply), + new System.Func(IronRuby.Builtins.FixnumOps.Multiply), + }); + + module.DefineLibraryMethod("**", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.Power), + new System.Func(IronRuby.Builtins.FixnumOps.Power), + new System.Func(IronRuby.Builtins.FixnumOps.Power), + }); + + module.DefineLibraryMethod("/", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.DivideOp), + new System.Func(IronRuby.Builtins.FixnumOps.DivideOp), + }); + + module.DefineLibraryMethod("-@", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.UnaryMinus), + }); + + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.Bit), + new System.Func(IronRuby.Builtins.FixnumOps.Bit), + new System.Func(IronRuby.Builtins.FixnumOps.Bit), + }); + + module.DefineLibraryMethod("^", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.BitwiseXor), + new System.Func(IronRuby.Builtins.FixnumOps.BitwiseXor), + new System.Func(IronRuby.Builtins.FixnumOps.BitwiseXor), + }); + + module.DefineLibraryMethod("|", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.BitwiseOr), + new System.Func(IronRuby.Builtins.FixnumOps.BitwiseOr), + new System.Func(IronRuby.Builtins.FixnumOps.BitwiseOr), + }); + + module.DefineLibraryMethod("~", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.OnesComplement), + }); + + module.DefineLibraryMethod("+", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.Add), + new System.Func(IronRuby.Builtins.FixnumOps.Add), + new System.Func(IronRuby.Builtins.FixnumOps.Add), + }); + + module.DefineLibraryMethod("<", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.LessThan), + new System.Func(IronRuby.Builtins.FixnumOps.LessThan), + new System.Func(IronRuby.Builtins.FixnumOps.LessThan), + }); + + module.DefineLibraryMethod("<<", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.LeftShift), + new System.Func(IronRuby.Builtins.FixnumOps.LeftShift), + }); + + module.DefineLibraryMethod("<=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.LessThanOrEqual), + new System.Func(IronRuby.Builtins.FixnumOps.LessThanOrEqual), + }); + + module.DefineLibraryMethod("<=>", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.Compare), + new System.Func(IronRuby.Builtins.FixnumOps.Compare), + }); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.Equal), + new System.Func(IronRuby.Builtins.FixnumOps.Equal), + }); + + module.DefineLibraryMethod(">", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.GreaterThan), + new System.Func(IronRuby.Builtins.FixnumOps.GreaterThan), + }); + + module.DefineLibraryMethod(">=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.GreaterThanOrEqual), + new System.Func(IronRuby.Builtins.FixnumOps.GreaterThanOrEqual), + }); + + module.DefineLibraryMethod(">>", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.RightShift), + new System.Func(IronRuby.Builtins.FixnumOps.RightShift), + }); + + module.DefineLibraryMethod("abs", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.Abs), + }); + + module.DefineLibraryMethod("div", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.DivideOp), + new System.Func(IronRuby.Builtins.FixnumOps.Div), + }); + + module.DefineLibraryMethod("divmod", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.DivMod), + new System.Func(IronRuby.Builtins.FixnumOps.DivMod), + }); + + module.DefineLibraryMethod("id2name", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.Id2Name), + }); + + module.DefineLibraryMethod("modulo", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.ModuloOp), + new System.Func(IronRuby.Builtins.FixnumOps.Modulo), + }); + + module.DefineLibraryMethod("quo", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.Quotient), + new System.Func(IronRuby.Builtins.FixnumOps.Quotient), + }); + + module.DefineLibraryMethod("size", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.Size), + }); + + module.DefineLibraryMethod("to_f", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.ToFloat), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.ToString), + new System.Func(IronRuby.Builtins.FixnumOps.ToString), + }); + + module.DefineLibraryMethod("to_sym", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.ToSymbol), + }); + + module.DefineLibraryMethod("zero?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.IsZero), + }); + + } + + private void LoadFixnum_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("induced_from", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FixnumOps.InducedFrom), + new System.Func(IronRuby.Builtins.FixnumOps.InducedFrom), + new System.Func(IronRuby.Builtins.FixnumOps.InducedFrom), + }); + + } + + private void LoadFloat_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("DIG", IronRuby.Builtins.FloatOps.DIG); + module.SetConstant("EPSILON", IronRuby.Builtins.FloatOps.EPSILON); + module.SetConstant("MANT_DIG", IronRuby.Builtins.FloatOps.MANT_DIG); + module.SetConstant("MAX", IronRuby.Builtins.FloatOps.MAX); + module.SetConstant("MAX_10_EXP", IronRuby.Builtins.FloatOps.MAX_10_EXP); + module.SetConstant("MAX_EXP", IronRuby.Builtins.FloatOps.MAX_EXP); + module.SetConstant("MIN", IronRuby.Builtins.FloatOps.MIN); + module.SetConstant("MIN_10_EXP", IronRuby.Builtins.FloatOps.MIN_10_EXP); + module.SetConstant("MIN_EXP", IronRuby.Builtins.FloatOps.MIN_EXP); + module.SetConstant("RADIX", IronRuby.Builtins.FloatOps.RADIX); + module.SetConstant("ROUNDS", IronRuby.Builtins.FloatOps.ROUNDS); + + module.DefineLibraryMethod("-", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Subtract), + new System.Func(IronRuby.Builtins.FloatOps.Subtract), + new System.Func(IronRuby.Builtins.FloatOps.Subtract), + new System.Func(IronRuby.Builtins.FloatOps.Subtract), + }); + + module.DefineLibraryMethod("%", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Modulo), + new System.Func(IronRuby.Builtins.FloatOps.Modulo), + new System.Func(IronRuby.Builtins.FloatOps.Modulo), + new System.Func(IronRuby.Builtins.FloatOps.ModuloOp), + }); + + module.DefineLibraryMethod("*", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Multiply), + new System.Func(IronRuby.Builtins.FloatOps.Multiply), + new System.Func(IronRuby.Builtins.FloatOps.Multiply), + new System.Func(IronRuby.Builtins.FloatOps.Multiply), + }); + + module.DefineLibraryMethod("**", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Power), + new System.Func(IronRuby.Builtins.FloatOps.Power), + new System.Func(IronRuby.Builtins.FloatOps.Power), + new System.Func(IronRuby.Builtins.FloatOps.Power), + }); + + module.DefineLibraryMethod("/", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Divide), + new System.Func(IronRuby.Builtins.FloatOps.Divide), + new System.Func(IronRuby.Builtins.FloatOps.Divide), + new System.Func(IronRuby.Builtins.FloatOps.Divide), + }); + + module.DefineLibraryMethod("+", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Add), + new System.Func(IronRuby.Builtins.FloatOps.Add), + new System.Func(IronRuby.Builtins.FloatOps.Add), + new System.Func(IronRuby.Builtins.FloatOps.Add), + }); + + module.DefineLibraryMethod("<", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.LessThan), + new System.Func(IronRuby.Builtins.FloatOps.LessThan), + new System.Func(IronRuby.Builtins.FloatOps.LessThan), + new System.Func(IronRuby.Builtins.FloatOps.LessThan), + }); + + module.DefineLibraryMethod("<=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.LessThanOrEqual), + new System.Func(IronRuby.Builtins.FloatOps.LessThanOrEqual), + new System.Func(IronRuby.Builtins.FloatOps.LessThanOrEqual), + new System.Func(IronRuby.Builtins.FloatOps.LessThanOrEqual), + }); + + module.DefineLibraryMethod("<=>", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Compare), + new System.Func(IronRuby.Builtins.FloatOps.Compare), + new System.Func(IronRuby.Builtins.FloatOps.Compare), + new System.Func(IronRuby.Builtins.FloatOps.Compare), + }); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Equal), + new System.Func(IronRuby.Builtins.FloatOps.Equal), + }); + + module.DefineLibraryMethod(">", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.GreaterThan), + new System.Func(IronRuby.Builtins.FloatOps.GreaterThan), + new System.Func(IronRuby.Builtins.FloatOps.GreaterThan), + new System.Func(IronRuby.Builtins.FloatOps.GreaterThan), + }); + + module.DefineLibraryMethod(">=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.GreaterThanOrEqual), + new System.Func(IronRuby.Builtins.FloatOps.GreaterThanOrEqual), + new System.Func(IronRuby.Builtins.FloatOps.GreaterThanOrEqual), + new System.Func(IronRuby.Builtins.FloatOps.GreaterThanOrEqual), + }); + + module.DefineLibraryMethod("abs", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Abs), + }); + + module.DefineLibraryMethod("ceil", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Ceil), + }); + + module.DefineLibraryMethod("coerce", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Coerce), + }); + + module.DefineLibraryMethod("divmod", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.DivMod), + new System.Func(IronRuby.Builtins.FloatOps.DivMod), + new System.Func(IronRuby.Builtins.FloatOps.DivMod), + new System.Func(IronRuby.Builtins.FloatOps.DivMod), + }); + + module.DefineLibraryMethod("finite?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.IsFinite), + }); + + module.DefineLibraryMethod("floor", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Floor), + }); + + module.DefineLibraryMethod("hash", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Hash), + }); + + module.DefineLibraryMethod("infinite?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.IsInfinite), + }); + + module.DefineLibraryMethod("modulo", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Modulo), + new System.Func(IronRuby.Builtins.FloatOps.Modulo), + new System.Func(IronRuby.Builtins.FloatOps.Modulo), + new System.Func(IronRuby.Builtins.FloatOps.Modulo), + }); + + module.DefineLibraryMethod("nan?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.IsNan), + }); + + module.DefineLibraryMethod("round", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.Round), + }); + + module.DefineLibraryMethod("to_f", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.ToFloat), + }); + + module.DefineLibraryMethod("to_i", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.ToInt), + }); + + module.DefineLibraryMethod("to_int", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.ToInt), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.ToS), + }); + + module.DefineLibraryMethod("truncate", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.ToInt), + }); + + module.DefineLibraryMethod("zero?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.IsZero), + }); + + } + + private void LoadFloat_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("induced_from", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.FloatOps.InducedFrom), + new System.Func(IronRuby.Builtins.FloatOps.InducedFrom), + new System.Func(IronRuby.Builtins.FloatOps.InducedFrom), + new System.Func(IronRuby.Builtins.FloatOps.InducedFrom), + }); + + } + + private void LoadGC_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("garbage_collect", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyGC.GarbageCollect), + }); + + } + + private void LoadGC_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("disable", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyGC.Disable), + }); + + module.DefineLibraryMethod("enable", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyGC.Enable), + }); + + module.DefineLibraryMethod("start", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyGC.GarbageCollect), + }); + + } + + private void LoadHash_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + LoadSystem__Collections__Generic__IDictionary_Instance(module); + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.HashOps.GetElement), + }); + + module.DefineLibraryMethod("default", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.HashOps.GetDefaultValue), + new System.Func(IronRuby.Builtins.HashOps.GetDefaultValue), + }); + + module.DefineLibraryMethod("default_proc", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.HashOps.GetDefaultProc), + }); + + module.DefineLibraryMethod("default=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.HashOps.SetDefaultValue), + }); + + module.DefineLibraryMethod("initialize", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.HashOps.Initialize), + new System.Func(IronRuby.Builtins.HashOps.Initialize), + new System.Func(IronRuby.Builtins.HashOps.Initialize), + }); + + module.DefineLibraryMethod("initialize_copy", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.HashOps.InitializeCopy), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.HashOps.Inspect), + }); + + module.DefineLibraryMethod("replace", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.HashOps.Replace), + }); + + module.DefineLibraryMethod("shift", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.HashOps.Shift), + }); + + } + + private void LoadHash_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("[]", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.HashOps.CreateHash), + }); + + } + + private void LoadInteger_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("ceil", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.ToInteger), + }); + + module.DefineLibraryMethod("chr", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.ToChr), + }); + + module.DefineLibraryMethod("downto", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.DownTo), + new System.Func(IronRuby.Builtins.Integer.DownTo), + }); + + module.DefineLibraryMethod("floor", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.ToInteger), + }); + + module.DefineLibraryMethod("integer?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.IsInteger), + }); + + module.DefineLibraryMethod("next", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.Next), + new System.Func(IronRuby.Builtins.Integer.Next), + }); + + module.DefineLibraryMethod("round", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.ToInteger), + }); + + module.DefineLibraryMethod("succ", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.Next), + new System.Func(IronRuby.Builtins.Integer.Next), + }); + + module.DefineLibraryMethod("times", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.Times), + new System.Func(IronRuby.Builtins.Integer.Times), + }); + + module.DefineLibraryMethod("to_i", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.ToInteger), + }); + + module.DefineLibraryMethod("to_int", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.ToInteger), + }); + + module.DefineLibraryMethod("truncate", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.ToInteger), + }); + + module.DefineLibraryMethod("upto", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.UpTo), + new System.Func(IronRuby.Builtins.Integer.UpTo), + }); + + } + + private void LoadInteger_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("induced_from", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Integer.InducedFrom), + new System.Func(IronRuby.Builtins.Integer.InducedFrom), + new System.Func(IronRuby.Builtins.Integer.InducedFrom), + new System.Func(IronRuby.Builtins.Integer.InducedFrom), + }); + + } + + private void LoadIO_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("SEEK_CUR", IronRuby.Builtins.RubyIOOps.SEEK_CUR); + module.SetConstant("SEEK_END", IronRuby.Builtins.RubyIOOps.SEEK_END); + module.SetConstant("SEEK_SET", IronRuby.Builtins.RubyIOOps.SEEK_SET); + + module.DefineLibraryMethod("<<", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Output), + }); + + module.DefineLibraryMethod("binmode", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Binmode), + }); + + module.DefineLibraryMethod("close", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.Close), + }); + + module.DefineLibraryMethod("close_read", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.CloseReader), + }); + + module.DefineLibraryMethod("close_write", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.CloseWriter), + }); + + module.DefineLibraryMethod("closed?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Closed), + }); + + module.DefineLibraryMethod("each", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.Each), + new System.Func(IronRuby.Builtins.RubyIOOps.Each), + }); + + module.DefineLibraryMethod("each_byte", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.EachByte), + }); + + module.DefineLibraryMethod("each_line", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.Each), + new System.Func(IronRuby.Builtins.RubyIOOps.Each), + }); + + module.DefineLibraryMethod("eof", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Eof), + }); + + module.DefineLibraryMethod("eof?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Eof), + }); + + module.DefineLibraryMethod("external_encoding", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.GetExternalEncoding), + }); + + module.DefineLibraryMethod("fcntl", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.FileControl), + new System.Func(IronRuby.Builtins.RubyIOOps.FileControl), + }); + + module.DefineLibraryMethod("fileno", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.FileNo), + }); + + module.DefineLibraryMethod("flush", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.Flush), + }); + + module.DefineLibraryMethod("fsync", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.Flush), + }); + + module.DefineLibraryMethod("getc", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Getc), + }); + + module.DefineLibraryMethod("gets", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Gets), + new System.Func(IronRuby.Builtins.RubyIOOps.Gets), + }); + + module.DefineLibraryMethod("internal_encoding", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.GetInternalEncoding), + }); + + module.DefineLibraryMethod("isatty", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.IsAtty), + }); + + module.DefineLibraryMethod("lineno", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.GetLineNo), + }); + + module.DefineLibraryMethod("lineno=", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.SetLineNo), + }); + + module.DefineLibraryMethod("pid", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Pid), + }); + + module.DefineLibraryMethod("pos", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Pos), + }); + + module.DefineLibraryMethod("pos=", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.Pos), + }); + + module.DefineLibraryMethod("print", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.Print), + new System.Action(IronRuby.Builtins.RubyIOOps.Print), + new System.Action(IronRuby.Builtins.RubyIOOps.Print), + }); + + module.DefineLibraryMethod("printf", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.PrintFormatted), + }); + + module.DefineLibraryMethod("putc", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Putc), + new System.Func(IronRuby.Builtins.RubyIOOps.Putc), + }); + + module.DefineLibraryMethod("puts", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.PutsEmptyLine), + new System.Action(IronRuby.Builtins.RubyIOOps.Puts), + new System.Action(IronRuby.Builtins.RubyIOOps.Puts), + new System.Action(IronRuby.Builtins.RubyIOOps.Puts), + }); + + module.DefineLibraryMethod("read", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Read), + new System.Func(IronRuby.Builtins.RubyIOOps.Read), + }); + + module.DefineLibraryMethod("readchar", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.ReadChar), + }); + + module.DefineLibraryMethod("readline", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.ReadLine), + new System.Func(IronRuby.Builtins.RubyIOOps.ReadLine), + }); + + module.DefineLibraryMethod("readlines", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.ReadLines), + new System.Func(IronRuby.Builtins.RubyIOOps.ReadLines), + }); + + module.DefineLibraryMethod("rewind", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.Rewind), + }); + + module.DefineLibraryMethod("seek", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Seek), + new System.Func(IronRuby.Builtins.RubyIOOps.Seek), + }); + + module.DefineLibraryMethod("sync", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Sync), + }); + + module.DefineLibraryMethod("sync=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Sync), + }); + + module.DefineLibraryMethod("sysread", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.SystemRead), + }); + + module.DefineLibraryMethod("tell", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Pos), + }); + + module.DefineLibraryMethod("to_i", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.FileNo), + }); + + module.DefineLibraryMethod("to_io", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.ToIO), + }); + + module.DefineLibraryMethod("tty?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.IsAtty), + }); + + module.DefineLibraryMethod("write", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Write), + new System.Func(IronRuby.Builtins.RubyIOOps.Write), + }); + + } + + private void LoadIO_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("for_fd", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.ForFd), + }); + + module.DefineLibraryMethod("foreach", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyIOOps.ForEach), + new System.Action(IronRuby.Builtins.RubyIOOps.ForEach), + }); + + module.DefineLibraryMethod("open", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Open), + new System.Func(IronRuby.Builtins.RubyIOOps.Open), + new System.Func(IronRuby.Builtins.RubyIOOps.Open), + new System.Func(IronRuby.Builtins.RubyIOOps.Open), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("popen", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.OpenPipe), + new System.Func(IronRuby.Builtins.RubyIOOps.OpenPipe), + }); + + #endif + module.DefineLibraryMethod("read", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.ReadFile), + new System.Func(IronRuby.Builtins.RubyIOOps.Read), + }); + + module.DefineLibraryMethod("readlines", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.ReadLines), + new System.Func(IronRuby.Builtins.RubyIOOps.ReadLines), + }); + + module.DefineLibraryMethod("select", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyIOOps.Select), + new System.Func(IronRuby.Builtins.RubyIOOps.Select), + new System.Func(IronRuby.Builtins.RubyIOOps.Select), + }); + + } + + private void LoadKernel_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("__id__", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetObjectId), + }); + + module.DefineLibraryMethod("__send__", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("`", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ExecuteCommand), + }); + + #endif + module.DefineLibraryMethod("=~", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Match), + }); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ValueEquals), + }); + + module.DefineLibraryMethod("===", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.HashEquals), + }); + + module.DefineLibraryMethod("Array", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ToArray), + }); + + module.DefineLibraryMethod("at_exit", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.AtExit), + }); + + module.DefineLibraryMethod("autoload", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.SetAutoloadedConstant), + }); + + module.DefineLibraryMethod("autoload?", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetAutoloadedConstantPath), + }); + + module.DefineLibraryMethod("binding", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetLocalScope), + }); + + module.DefineLibraryMethod("block_given?", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.HasBlock), + }); + + module.DefineLibraryMethod("caller", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetStackTrace), + }); + + module.DefineLibraryMethod("catch", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Catch), + }); + + module.DefineLibraryMethod("class", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetClass), + }); + + module.DefineLibraryMethod("clone", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Clone), + }); + + module.DefineLibraryMethod("display", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.Display), + }); + + module.DefineLibraryMethod("dup", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Duplicate), + }); + + module.DefineLibraryMethod("eql?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ValueEquals), + }); + + module.DefineLibraryMethod("equal?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Equal), + }); + + module.DefineLibraryMethod("eval", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Evaluate), + new System.Func(IronRuby.Builtins.KernelOps.Evaluate), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("exec", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.Execute), + new System.Action(IronRuby.Builtins.KernelOps.Execute), + }); + + #endif + module.DefineLibraryMethod("exit", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.Exit), + new System.Action(IronRuby.Builtins.KernelOps.Exit), + new System.Action(IronRuby.Builtins.KernelOps.Exit), + }); + + module.DefineLibraryMethod("exit!", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.TerminateExecution), + new System.Action(IronRuby.Builtins.KernelOps.TerminateExecution), + new System.Action(IronRuby.Builtins.KernelOps.TerminateExecution), + }); + + module.DefineLibraryMethod("extend", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Extend), + }); + + module.DefineLibraryMethod("fail", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.RaiseException), + new System.Action(IronRuby.Builtins.KernelOps.RaiseException), + new System.Action(IronRuby.Builtins.KernelOps.RaiseException), + }); + + module.DefineLibraryMethod("Float", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ToFloat), + }); + + module.DefineLibraryMethod("format", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Sprintf), + }); + + module.DefineLibraryMethod("freeze", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Freeze), + }); + + module.DefineLibraryMethod("frozen?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Frozen), + new System.Func(IronRuby.Builtins.KernelOps.Frozen), + }); + + module.DefineLibraryMethod("getc", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ReadInputCharacter), + }); + + module.DefineLibraryMethod("gets", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ReadInputLine), + new System.Func(IronRuby.Builtins.KernelOps.ReadInputLine), + }); + + module.DefineLibraryMethod("global_variables", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetGlobalVariableNames), + }); + + module.DefineLibraryMethod("hash", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Hash), + }); + + module.DefineLibraryMethod("id", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetId), + }); + + module.DefineLibraryMethod("initialize_copy", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.InitializeCopy), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Inspect), + }); + + module.DefineLibraryMethod("instance_eval", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Evaluate), + new System.Func(IronRuby.Builtins.KernelOps.InstanceEval), + }); + + module.DefineLibraryMethod("instance_of?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.IsOfClass), + }); + + module.DefineLibraryMethod("instance_variable_defined?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.InstanceVariableDefined), + }); + + module.DefineLibraryMethod("instance_variable_get", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.InstanceVariableGet), + }); + + module.DefineLibraryMethod("instance_variable_set", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.InstanceVariableSet), + }); + + module.DefineLibraryMethod("instance_variables", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.InstanceVariables), + }); + + module.DefineLibraryMethod("Integer", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ToInteger), + }); + + module.DefineLibraryMethod("is_a?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.IsKindOf), + }); + + module.DefineLibraryMethod("iterator?", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.HasBlock), + }); + + module.DefineLibraryMethod("kind_of?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.IsKindOf), + }); + + module.DefineLibraryMethod("lambda", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.CreateLambda), + }); + + module.DefineLibraryMethod("load", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Load), + }); + + module.DefineLibraryMethod("load_assembly", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.LoadAssembly), + }); + + module.DefineLibraryMethod("local_variables", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetLocalVariableNames), + }); + + module.DefineLibraryMethod("loop", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Loop), + }); + + module.DefineLibraryMethod("method", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetMethod), + }); + + module.DefineLibraryMethod("method_missing", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.MethodMissing), + }); + + module.DefineLibraryMethod("methods", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetMethods), + }); + + module.DefineLibraryMethod("nil?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.IsNil), + }); + + module.DefineLibraryMethod("object_id", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetObjectId), + }); + + module.DefineLibraryMethod("open", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + }); + + module.DefineLibraryMethod("p", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.PrintInspect), + }); + + module.DefineLibraryMethod("print", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.Print), + new System.Action(IronRuby.Builtins.KernelOps.Print), + new System.Action(IronRuby.Builtins.KernelOps.Print), + }); + + module.DefineLibraryMethod("printf", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.PrintFormatted), + new System.Action(IronRuby.Builtins.KernelOps.PrintFormatted), + }); + + module.DefineLibraryMethod("private_methods", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetPrivateMethods), + }); + + module.DefineLibraryMethod("proc", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.CreateLambda), + }); + + module.DefineLibraryMethod("protected_methods", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetProtectedMethods), + }); + + module.DefineLibraryMethod("public_methods", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetPublicMethods), + }); + + module.DefineLibraryMethod("putc", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Putc), + new System.Func(IronRuby.Builtins.KernelOps.Putc), + }); + + module.DefineLibraryMethod("puts", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.PutsEmptyLine), + new System.Action(IronRuby.Builtins.KernelOps.PutString), + new System.Action(IronRuby.Builtins.KernelOps.PutString), + new System.Action(IronRuby.Builtins.KernelOps.PutString), + }); + + module.DefineLibraryMethod("raise", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.RaiseException), + new System.Action(IronRuby.Builtins.KernelOps.RaiseException), + new System.Action(IronRuby.Builtins.KernelOps.RaiseException), + }); + + module.DefineLibraryMethod("rand", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Rand), + new System.Func(IronRuby.Builtins.KernelOps.Rand), + new System.Func(IronRuby.Builtins.KernelOps.Rand), + new System.Func(IronRuby.Builtins.KernelOps.Rand), + new System.Func(IronRuby.Builtins.KernelOps.Rand), + }); + + module.DefineLibraryMethod("remove_instance_variable", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.RemoveInstanceVariable), + }); + + module.DefineLibraryMethod("require", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Require), + }); + + module.DefineLibraryMethod("respond_to?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.RespondTo), + }); + + module.DefineLibraryMethod("select", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Select), + new System.Func(IronRuby.Builtins.KernelOps.Select), + new System.Func(IronRuby.Builtins.KernelOps.Select), + }); + + module.DefineLibraryMethod("send", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + new System.Func(IronRuby.Builtins.KernelOps.SendMessage), + }); + + module.DefineLibraryMethod("set_trace_func", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.SetTraceListener), + }); + + module.DefineLibraryMethod("singleton_method_added", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.MethodAdded), + }); + + module.DefineLibraryMethod("singleton_method_removed", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.MethodRemoved), + }); + + module.DefineLibraryMethod("singleton_method_undefined", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.MethodUndefined), + }); + + module.DefineLibraryMethod("singleton_methods", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetSingletonMethods), + }); + + module.DefineLibraryMethod("sleep", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.Sleep), + new System.Func(IronRuby.Builtins.KernelOps.Sleep), + new System.Func(IronRuby.Builtins.KernelOps.Sleep), + }); + + module.DefineLibraryMethod("sprintf", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Sprintf), + }); + + module.DefineLibraryMethod("String", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ToString), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("system", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.System), + new System.Func(IronRuby.Builtins.KernelOps.System), + }); + + #endif + module.DefineLibraryMethod("taint", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Taint), + }); + + module.DefineLibraryMethod("tainted?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Tainted), + }); + + module.DefineLibraryMethod("throw", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.Throw), + }); + + module.DefineLibraryMethod("to_a", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ToA), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ToS), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("trap", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Trap), + new System.Func(IronRuby.Builtins.KernelOps.Trap), + }); + + #endif + module.DefineLibraryMethod("type", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetClassObsolete), + }); + + module.DefineLibraryMethod("untaint", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Untaint), + }); + + module.DefineLibraryMethod("warn", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.ReportWarning), + }); + + } + + private void LoadKernel_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + #if !SILVERLIGHT + module.DefineLibraryMethod("`", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ExecuteCommand), + }); + + #endif + module.DefineLibraryMethod("Array", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ToArray), + }); + + module.DefineLibraryMethod("at_exit", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.AtExit), + }); + + module.DefineLibraryMethod("autoload", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.SetAutoloadedConstant), + }); + + module.DefineLibraryMethod("autoload?", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetAutoloadedConstantPath), + }); + + module.DefineLibraryMethod("caller", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetStackTrace), + }); + + module.DefineLibraryMethod("catch", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Catch), + }); + + module.DefineLibraryMethod("eval", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Evaluate), + new System.Func(IronRuby.Builtins.KernelOps.Evaluate), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("exec", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.Execute), + new System.Action(IronRuby.Builtins.KernelOps.Execute), + }); + + #endif + module.DefineLibraryMethod("exit", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.Exit), + new System.Action(IronRuby.Builtins.KernelOps.Exit), + new System.Action(IronRuby.Builtins.KernelOps.Exit), + }); + + module.DefineLibraryMethod("exit!", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.TerminateExecution), + new System.Action(IronRuby.Builtins.KernelOps.TerminateExecution), + new System.Action(IronRuby.Builtins.KernelOps.TerminateExecution), + }); + + module.DefineLibraryMethod("fail", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.RaiseException), + new System.Action(IronRuby.Builtins.KernelOps.RaiseException), + new System.Action(IronRuby.Builtins.KernelOps.RaiseException), + }); + + module.DefineLibraryMethod("Float", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ToFloat), + }); + + module.DefineLibraryMethod("format", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Sprintf), + }); + + module.DefineLibraryMethod("getc", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ReadInputCharacter), + }); + + module.DefineLibraryMethod("gets", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ReadInputLine), + new System.Func(IronRuby.Builtins.KernelOps.ReadInputLine), + }); + + module.DefineLibraryMethod("global_variables", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetGlobalVariableNames), + }); + + module.DefineLibraryMethod("Integer", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ToInteger), + }); + + module.DefineLibraryMethod("lambda", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.CreateLambda), + }); + + module.DefineLibraryMethod("load", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Load), + }); + + module.DefineLibraryMethod("load_assembly", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.LoadAssembly), + }); + + module.DefineLibraryMethod("local_variables", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.GetLocalVariableNames), + }); + + module.DefineLibraryMethod("loop", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Loop), + }); + + module.DefineLibraryMethod("method_missing", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.MethodMissing), + }); + + module.DefineLibraryMethod("open", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + new System.Func(IronRuby.Builtins.KernelOps.Open), + }); + + module.DefineLibraryMethod("p", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.PrintInspect), + }); + + module.DefineLibraryMethod("print", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.Print), + new System.Action(IronRuby.Builtins.KernelOps.Print), + new System.Action(IronRuby.Builtins.KernelOps.Print), + }); + + module.DefineLibraryMethod("printf", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.PrintFormatted), + new System.Action(IronRuby.Builtins.KernelOps.PrintFormatted), + }); + + module.DefineLibraryMethod("proc", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.CreateLambda), + }); + + module.DefineLibraryMethod("putc", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Putc), + new System.Func(IronRuby.Builtins.KernelOps.Putc), + }); + + module.DefineLibraryMethod("puts", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.PutsEmptyLine), + new System.Action(IronRuby.Builtins.KernelOps.PutString), + new System.Action(IronRuby.Builtins.KernelOps.PutString), + new System.Action(IronRuby.Builtins.KernelOps.PutString), + }); + + module.DefineLibraryMethod("raise", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.RaiseException), + new System.Action(IronRuby.Builtins.KernelOps.RaiseException), + new System.Action(IronRuby.Builtins.KernelOps.RaiseException), + }); + + module.DefineLibraryMethod("rand", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Rand), + new System.Func(IronRuby.Builtins.KernelOps.Rand), + new System.Func(IronRuby.Builtins.KernelOps.Rand), + new System.Func(IronRuby.Builtins.KernelOps.Rand), + new System.Func(IronRuby.Builtins.KernelOps.Rand), + }); + + module.DefineLibraryMethod("require", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Require), + }); + + module.DefineLibraryMethod("select", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Select), + new System.Func(IronRuby.Builtins.KernelOps.Select), + new System.Func(IronRuby.Builtins.KernelOps.Select), + }); + + module.DefineLibraryMethod("set_trace_func", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.SetTraceListener), + }); + + module.DefineLibraryMethod("sleep", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.Sleep), + new System.Func(IronRuby.Builtins.KernelOps.Sleep), + new System.Func(IronRuby.Builtins.KernelOps.Sleep), + }); + + module.DefineLibraryMethod("sprintf", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Sprintf), + }); + + module.DefineLibraryMethod("String", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.ToString), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("system", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.System), + new System.Func(IronRuby.Builtins.KernelOps.System), + }); + + #endif + module.DefineLibraryMethod("throw", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.Throw), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("trap", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.KernelOps.Trap), + new System.Func(IronRuby.Builtins.KernelOps.Trap), + }); + + #endif + module.DefineLibraryMethod("warn", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.KernelOps.ReportWarning), + }); + + } + + private void LoadMarshal_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("MAJOR_VERSION", IronRuby.Builtins.RubyMarshal.MAJOR_VERSION); + module.SetConstant("MINOR_VERSION", IronRuby.Builtins.RubyMarshal.MINOR_VERSION); + + } + + private void LoadMarshal_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("dump", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMarshal.Dump), + new System.Func(IronRuby.Builtins.RubyMarshal.Dump), + new System.Func, System.Object>(IronRuby.Builtins.RubyMarshal.Dump), + new System.Func, System.Object>(IronRuby.Builtins.RubyMarshal.Dump), + }); + + module.DefineLibraryMethod("load", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMarshal.Load), + new System.Func(IronRuby.Builtins.RubyMarshal.Load), + new System.Func(IronRuby.Builtins.RubyMarshal.Load), + }); + + module.DefineLibraryMethod("restore", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMarshal.Load), + new System.Func(IronRuby.Builtins.RubyMarshal.Load), + new System.Func(IronRuby.Builtins.RubyMarshal.Load), + }); + + } + + private void LoadMatchData_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.GetGroup), + new System.Func(IronRuby.Builtins.MatchDataOps.GetGroup), + new System.Func(IronRuby.Builtins.MatchDataOps.GetGroup), + }); + + module.DefineLibraryMethod("begin", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.Begin), + }); + + module.DefineLibraryMethod("captures", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.Captures), + }); + + module.DefineLibraryMethod("end", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.End), + }); + + module.DefineLibraryMethod("initialize_copy", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.InitializeCopy), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.Inspect), + }); + + module.DefineLibraryMethod("length", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.Length), + }); + + module.DefineLibraryMethod("offset", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.Offset), + }); + + module.DefineLibraryMethod("post_match", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.PostMatch), + }); + + module.DefineLibraryMethod("pre_match", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.PreMatch), + }); + + module.DefineLibraryMethod("select", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.Select), + }); + + module.DefineLibraryMethod("size", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.Length), + }); + + module.DefineLibraryMethod("string", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.ReturnFrozenString), + }); + + module.DefineLibraryMethod("to_a", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.ToArray), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.ToS), + }); + + module.DefineLibraryMethod("values_at", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MatchDataOps.ValuesAt), + }); + + } + + private void LoadMatchData_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.UndefineLibraryMethod("new"); + } + + private void LoadMath_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("E", IronRuby.Builtins.RubyMath.E); + module.SetConstant("PI", IronRuby.Builtins.RubyMath.PI); + + module.DefineLibraryMethod("acos", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Acos), + }); + + module.DefineLibraryMethod("acosh", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Acosh), + }); + + module.DefineLibraryMethod("asin", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Asin), + }); + + module.DefineLibraryMethod("asinh", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Asinh), + }); + + module.DefineLibraryMethod("atan", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Atan), + }); + + module.DefineLibraryMethod("atan2", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Atan2), + }); + + module.DefineLibraryMethod("atanh", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Atanh), + }); + + module.DefineLibraryMethod("cos", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Cos), + }); + + module.DefineLibraryMethod("cosh", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Cosh), + }); + + module.DefineLibraryMethod("erf", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Erf), + }); + + module.DefineLibraryMethod("erfc", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Erfc), + }); + + module.DefineLibraryMethod("exp", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Exp), + }); + + module.DefineLibraryMethod("frexp", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Frexp), + }); + + module.DefineLibraryMethod("hypot", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Hypot), + }); + + module.DefineLibraryMethod("ldexp", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Ldexp), + }); + + module.DefineLibraryMethod("log", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Log), + }); + + module.DefineLibraryMethod("log10", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Log10), + }); + + module.DefineLibraryMethod("sin", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Sin), + }); + + module.DefineLibraryMethod("sinh", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Sinh), + }); + + module.DefineLibraryMethod("sqrt", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Sqrt), + }); + + module.DefineLibraryMethod("tan", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Tan), + }); + + module.DefineLibraryMethod("tanh", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Tanh), + }); + + } + + private void LoadMath_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("acos", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Acos), + }); + + module.DefineLibraryMethod("acosh", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Acosh), + }); + + module.DefineLibraryMethod("asin", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Asin), + }); + + module.DefineLibraryMethod("asinh", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Asinh), + }); + + module.DefineLibraryMethod("atan", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Atan), + }); + + module.DefineLibraryMethod("atan2", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Atan2), + }); + + module.DefineLibraryMethod("atanh", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Atanh), + }); + + module.DefineLibraryMethod("cos", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Cos), + }); + + module.DefineLibraryMethod("cosh", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Cosh), + }); + + module.DefineLibraryMethod("erf", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Erf), + }); + + module.DefineLibraryMethod("erfc", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Erfc), + }); + + module.DefineLibraryMethod("exp", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Exp), + }); + + module.DefineLibraryMethod("frexp", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Frexp), + }); + + module.DefineLibraryMethod("hypot", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Hypot), + }); + + module.DefineLibraryMethod("ldexp", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Ldexp), + }); + + module.DefineLibraryMethod("log", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Log), + }); + + module.DefineLibraryMethod("log10", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Log10), + }); + + module.DefineLibraryMethod("sin", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Sin), + }); + + module.DefineLibraryMethod("sinh", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Sinh), + }); + + module.DefineLibraryMethod("sqrt", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Sqrt), + }); + + module.DefineLibraryMethod("tan", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Tan), + }); + + module.DefineLibraryMethod("tanh", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyMath.Tanh), + }); + + } + + private void LoadMethod_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineRuleGenerator("[]", 0x51, IronRuby.Builtins.MethodOps.Call()); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MethodOps.Equal), + new System.Func(IronRuby.Builtins.MethodOps.Equal), + }); + + module.DefineLibraryMethod("arity", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MethodOps.GetArity), + }); + + module.DefineRuleGenerator("call", 0x51, IronRuby.Builtins.MethodOps.Call()); + + module.DefineLibraryMethod("clone", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MethodOps.Clone), + }); + + module.DefineLibraryMethod("to_proc", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MethodOps.ToProc), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MethodOps.ToS), + }); + + module.DefineLibraryMethod("unbind", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MethodOps.Unbind), + }); + + } + + private void LoadMicrosoft__Scripting__Actions__TypeGroup_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TypeGroupOps.Of), + }); + + module.DefineLibraryMethod("each", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TypeGroupOps.EachType), + }); + + module.DefineLibraryMethod("name", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TypeGroupOps.GetName), + }); + + module.DefineRuleGenerator("new", 0x51, IronRuby.Builtins.TypeGroupOps.GetInstanceConstructor()); + + module.DefineLibraryMethod("of", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TypeGroupOps.Of), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TypeGroupOps.GetName), + }); + + } + + private void LoadMicrosoft__Scripting__Actions__TypeTracker_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("to_class", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TypeTrackerOps.ToClass), + }); + + module.DefineLibraryMethod("to_module", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TypeTrackerOps.ToModule), + }); + + } + + private void LoadModule_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.Of), + }); + + module.DefineLibraryMethod("<", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.IsSubclassOrIncluded), + new System.Func(IronRuby.Builtins.ModuleOps.InvalidComparison), + }); + + module.DefineLibraryMethod("<=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.IsSubclassSameOrIncluded), + new System.Func(IronRuby.Builtins.ModuleOps.InvalidComparison), + }); + + module.DefineLibraryMethod("<=>", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.Comparison), + new System.Func(IronRuby.Builtins.ModuleOps.Comparison), + }); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.Equals), + }); + + module.DefineLibraryMethod("===", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.CaseEquals), + }); + + module.DefineLibraryMethod(">", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.IsNotSubclassOrIncluded), + new System.Func(IronRuby.Builtins.ModuleOps.InvalidComparison), + }); + + module.DefineLibraryMethod(">=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.IsNotSubclassSameOrIncluded), + new System.Func(IronRuby.Builtins.ModuleOps.InvalidComparison), + }); + + module.DefineLibraryMethod("alias_method", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.AliasMethod), + }); + + module.DefineLibraryMethod("ancestors", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.Ancestors), + }); + + module.DefineLibraryMethod("append_features", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.AppendFeatures), + }); + + module.DefineLibraryMethod("attr", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ModuleOps.Attr), + }); + + module.DefineLibraryMethod("attr_accessor", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ModuleOps.AttrAccessor), + new System.Action(IronRuby.Builtins.ModuleOps.AttrAccessor), + }); + + module.DefineLibraryMethod("attr_reader", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ModuleOps.AttrReader), + new System.Action(IronRuby.Builtins.ModuleOps.AttrReader), + }); + + module.DefineLibraryMethod("attr_writer", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ModuleOps.AttrWriter), + new System.Action(IronRuby.Builtins.ModuleOps.AttrWriter), + }); + + module.DefineLibraryMethod("autoload", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ModuleOps.SetAutoloadedConstant), + }); + + module.DefineLibraryMethod("autoload?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetAutoloadedConstantPath), + }); + + module.DefineLibraryMethod("class_eval", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.Evaluate), + new System.Func(IronRuby.Builtins.ModuleOps.Evaluate), + }); + + module.DefineLibraryMethod("class_variable_defined?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.ClassVariableDefined), + }); + + module.DefineLibraryMethod("class_variable_get", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetClassVariable), + }); + + module.DefineLibraryMethod("class_variable_set", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.ClassVariableSet), + }); + + module.DefineLibraryMethod("class_variables", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.ClassVariables), + }); + + module.DefineLibraryMethod("const_defined?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.IsConstantDefined), + }); + + module.DefineLibraryMethod("const_get", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetConstantValue), + }); + + module.DefineLibraryMethod("const_missing", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ModuleOps.ConstantMissing), + }); + + module.DefineLibraryMethod("const_set", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.SetConstantValue), + }); + + module.DefineLibraryMethod("constants", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetDefinedConstants), + }); + + module.DefineLibraryMethod("define_method", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.DefineMethod), + new System.Func(IronRuby.Builtins.ModuleOps.DefineMethod), + new System.Func(IronRuby.Builtins.ModuleOps.DefineMethod), + new System.Func(IronRuby.Builtins.ModuleOps.DefineMethod), + }); + + module.DefineLibraryMethod("extend_object", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.ExtendObject), + new System.Func(IronRuby.Builtins.ModuleOps.ExtendObject), + }); + + module.DefineLibraryMethod("extended", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ModuleOps.ObjectExtended), + }); + + module.DefineLibraryMethod("freeze", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.Freeze), + }); + + module.DefineLibraryMethod("include", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.Include), + }); + + module.DefineLibraryMethod("include?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.IncludesModule), + }); + + module.DefineLibraryMethod("included", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ModuleOps.Included), + }); + + module.DefineLibraryMethod("included_modules", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetIncludedModules), + }); + + module.DefineLibraryMethod("initialize", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.Reinitialize), + }); + + module.DefineLibraryMethod("initialize_copy", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.InitializeCopy), + }); + + module.DefineLibraryMethod("instance_method", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetInstanceMethod), + }); + + module.DefineLibraryMethod("instance_methods", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetInstanceMethods), + new System.Func(IronRuby.Builtins.ModuleOps.GetInstanceMethods), + }); + + module.DefineLibraryMethod("method_added", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ModuleOps.MethodAdded), + }); + + module.DefineLibraryMethod("method_defined?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.MethodDefined), + }); + + module.DefineLibraryMethod("method_removed", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ModuleOps.MethodRemoved), + }); + + module.DefineLibraryMethod("method_undefined", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ModuleOps.MethodUndefined), + }); + + module.DefineLibraryMethod("module_eval", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.Evaluate), + new System.Func(IronRuby.Builtins.ModuleOps.Evaluate), + }); + + module.DefineLibraryMethod("module_function", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.CopyMethodsToModuleSingleton), + }); + + module.DefineLibraryMethod("name", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetName), + }); + + module.DefineLibraryMethod("of", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.Of), + }); + + module.DefineLibraryMethod("private", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.SetPrivateVisibility), + }); + + module.DefineLibraryMethod("private_class_method", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.MakeClassMethodsPrivate), + }); + + module.DefineLibraryMethod("private_instance_methods", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetPrivateInstanceMethods), + new System.Func(IronRuby.Builtins.ModuleOps.GetPrivateInstanceMethods), + }); + + module.DefineLibraryMethod("private_method_defined?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.PrivateMethodDefined), + }); + + module.DefineLibraryMethod("protected", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.SetProtectedVisibility), + }); + + module.DefineLibraryMethod("protected_instance_methods", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetProtectedInstanceMethods), + new System.Func(IronRuby.Builtins.ModuleOps.GetProtectedInstanceMethods), + }); + + module.DefineLibraryMethod("protected_method_defined?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.ProtectedMethodDefined), + }); + + module.DefineLibraryMethod("public", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.SetPublicVisibility), + }); + + module.DefineLibraryMethod("public_class_method", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.MakeClassMethodsPublic), + }); + + module.DefineLibraryMethod("public_instance_methods", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetPublicInstanceMethods), + new System.Func(IronRuby.Builtins.ModuleOps.GetPublicInstanceMethods), + }); + + module.DefineLibraryMethod("public_method_defined?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.PublicMethodDefined), + }); + + module.DefineLibraryMethod("remove_class_variable", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.RemoveClassVariable), + }); + + module.DefineLibraryMethod("remove_const", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.RemoveConstant), + }); + + module.DefineLibraryMethod("remove_method", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.RemoveMethod), + }); + + module.DefineLibraryMethod("to_clr_type", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.ToClrType), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.ToS), + }); + + module.DefineLibraryMethod("undef_method", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.UndefineMethod), + }); + + } + + private void LoadModule_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("constants", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetGlobalConstants), + }); + + module.DefineLibraryMethod("nesting", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ModuleOps.GetLexicalModuleNesting), + }); + + } + + private void LoadNilClass_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("&", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.NilClassOps.And), + }); + + module.DefineLibraryMethod("^", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.NilClassOps.Xor), + new System.Func(IronRuby.Builtins.NilClassOps.Xor), + }); + + module.DefineLibraryMethod("|", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.NilClassOps.Or), + new System.Func(IronRuby.Builtins.NilClassOps.Or), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.NilClassOps.Inspect), + }); + + module.DefineLibraryMethod("nil?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.NilClassOps.IsNil), + }); + + module.DefineLibraryMethod("to_a", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.NilClassOps.ToArray), + }); + + module.DefineLibraryMethod("to_f", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.NilClassOps.ToDouble), + }); + + module.DefineLibraryMethod("to_i", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.NilClassOps.ToInteger), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.NilClassOps.ToString), + }); + + } + + private void LoadNoMethodError_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.HideMethod("message"); + } + + private void LoadNumeric_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("-@", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.UnaryMinus), + }); + + module.DefineLibraryMethod("+@", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.UnaryPlus), + }); + + module.DefineLibraryMethod("<=>", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Compare), + }); + + module.DefineLibraryMethod("abs", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Abs), + }); + + module.DefineLibraryMethod("ceil", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Ceil), + }); + + module.DefineLibraryMethod("coerce", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Coerce), + }); + + module.DefineLibraryMethod("div", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Div), + }); + + module.DefineLibraryMethod("divmod", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.DivMod), + }); + + module.DefineLibraryMethod("eql?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Eql), + }); + + module.DefineLibraryMethod("floor", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Floor), + }); + + module.DefineLibraryMethod("integer?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.IsInteger), + }); + + module.DefineLibraryMethod("modulo", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Modulo), + }); + + module.DefineLibraryMethod("nonzero?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.IsNonZero), + }); + + module.DefineLibraryMethod("quo", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Quo), + }); + + module.DefineLibraryMethod("remainder", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Remainder), + }); + + module.DefineLibraryMethod("round", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Round), + }); + + module.DefineLibraryMethod("step", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Step), + new System.Func(IronRuby.Builtins.Numeric.Step), + new System.Func(IronRuby.Builtins.Numeric.Step), + new System.Func(IronRuby.Builtins.Numeric.Step), + new System.Func(IronRuby.Builtins.Numeric.Step), + }); + + module.DefineLibraryMethod("to_int", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.ToInt), + }); + + module.DefineLibraryMethod("truncate", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.Truncate), + }); + + module.DefineLibraryMethod("zero?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Numeric.IsZero), + }); + + } + + private void LoadObject_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("FALSE", IronRuby.Builtins.ObjectOps.FALSE); + module.SetConstant("NIL", IronRuby.Builtins.ObjectOps.NIL); + module.SetConstant("TRUE", IronRuby.Builtins.ObjectOps.TRUE); + + module.DefineLibraryMethod("initialize", 0x5a, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ObjectOps.Reinitialize), + }); + + } + + private void LoadObject_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + } + + private void LoadObjectSpace_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("define_finalizer", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ObjectSpace.DefineFinalizer), + }); + + module.DefineLibraryMethod("each_object", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ObjectSpace.EachObject), + }); + + module.DefineLibraryMethod("garbage_collect", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ObjectSpace.GarbageCollect), + }); + + module.DefineLibraryMethod("undefine_finalizer", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ObjectSpace.DefineFinalizer), + }); + + } + + private void LoadPrecision_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("prec", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Precision.Prec), + }); + + module.DefineLibraryMethod("prec_f", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Precision.PrecFloat), + }); + + module.DefineLibraryMethod("prec_i", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Precision.PrecInteger), + }); + + } + + private void LoadPrecision_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("included", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Precision.Included), + }); + + } + + private void LoadProc_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ProcOps.Call), + new System.Func(IronRuby.Builtins.ProcOps.Call), + new System.Func(IronRuby.Builtins.ProcOps.Call), + new System.Func(IronRuby.Builtins.ProcOps.Call), + new System.Func(IronRuby.Builtins.ProcOps.Call), + new System.Func(IronRuby.Builtins.ProcOps.Call), + }); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ProcOps.Equal), + new System.Func(IronRuby.Builtins.ProcOps.Equal), + }); + + module.DefineLibraryMethod("arity", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ProcOps.GetArity), + }); + + module.DefineLibraryMethod("binding", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ProcOps.GetLocalScope), + }); + + module.DefineLibraryMethod("call", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ProcOps.Call), + new System.Func(IronRuby.Builtins.ProcOps.Call), + new System.Func(IronRuby.Builtins.ProcOps.Call), + new System.Func(IronRuby.Builtins.ProcOps.Call), + new System.Func(IronRuby.Builtins.ProcOps.Call), + new System.Func(IronRuby.Builtins.ProcOps.Call), + }); + + module.DefineLibraryMethod("clone", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ProcOps.Clone), + }); + + module.DefineLibraryMethod("dup", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ProcOps.Clone), + }); + + module.DefineLibraryMethod("to_proc", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ProcOps.ToProc), + }); + + } + + private void LoadProc_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("new", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ProcOps.CreateNew), + new System.Func(IronRuby.Builtins.ProcOps.CreateNew), + }); + + } + + #if !SILVERLIGHT + private void LoadProcess_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("kill", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.Kill), + }); + + } + #endif + + #if !SILVERLIGHT + private void LoadProcess_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("euid", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.EffectiveUserId), + }); + + module.DefineLibraryMethod("kill", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.Kill), + }); + + module.DefineLibraryMethod("pid", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.GetPid), + }); + + module.DefineLibraryMethod("ppid", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.GetParentPid), + }); + + module.DefineLibraryMethod("times", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.GetTimes), + }); + + module.DefineLibraryMethod("uid", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.UserId), + }); + + } + #endif + + #if !SILVERLIGHT && !SILVERLIGHT + private void LoadProcess__Status_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("coredump?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.Status.CoreDump), + }); + + module.DefineLibraryMethod("exited?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.Status.Exited), + }); + + module.DefineLibraryMethod("exitstatus", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.Status.ExitStatus), + }); + + module.DefineLibraryMethod("pid", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.Status.Pid), + }); + + module.DefineLibraryMethod("stopped?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.Status.Stopped), + }); + + module.DefineLibraryMethod("stopsig", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.Status.StopSig), + }); + + module.DefineLibraryMethod("success?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.Status.Success), + }); + + module.DefineLibraryMethod("termsig", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyProcess.Status.TermSig), + }); + + } + #endif + + private void LoadRange_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.Equals), + new System.Func(IronRuby.Builtins.RangeOps.Equals), + }); + + module.DefineLibraryMethod("===", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.CaseEquals), + }); + + module.DefineLibraryMethod("begin", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.Begin), + }); + + module.DefineLibraryMethod("each", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.Each), + }); + + module.DefineLibraryMethod("end", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.End), + }); + + module.DefineLibraryMethod("eql?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.Equals), + new System.Func(IronRuby.Builtins.RangeOps.Eql), + }); + + module.DefineLibraryMethod("exclude_end?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.ExcludeEnd), + }); + + module.DefineLibraryMethod("first", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.Begin), + }); + + module.DefineLibraryMethod("hash", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.GetHashCode), + }); + + module.DefineLibraryMethod("include?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.CaseEquals), + }); + + module.DefineLibraryMethod("initialize", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.Reinitialize), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.Inspect), + }); + + module.DefineLibraryMethod("last", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.End), + }); + + module.DefineLibraryMethod("member?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.CaseEquals), + }); + + module.DefineLibraryMethod("step", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.Step), + new System.Func(IronRuby.Builtins.RangeOps.Step), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RangeOps.ToS), + }); + + } + + private void LoadRangeError_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.HideMethod("message"); + } + + private void LoadRegexp_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("EXTENDED", IronRuby.Builtins.RegexpOps.EXTENDED); + module.SetConstant("IGNORECASE", IronRuby.Builtins.RegexpOps.IGNORECASE); + module.SetConstant("MULTILINE", IronRuby.Builtins.RegexpOps.MULTILINE); + + module.DefineLibraryMethod("~", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.ImplicitMatch), + }); + + module.DefineLibraryMethod("=~", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.MatchIndex), + }); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.Equals), + new System.Func(IronRuby.Builtins.RegexpOps.Equals), + }); + + module.DefineLibraryMethod("===", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.CaseCompare), + new System.Func(IronRuby.Builtins.RegexpOps.CaseCompare), + }); + + module.DefineLibraryMethod("casefold?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.IsCaseInsensitive), + }); + + module.DefineLibraryMethod("eql?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.Equals), + new System.Func(IronRuby.Builtins.RegexpOps.Equals), + }); + + module.DefineLibraryMethod("hash", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.GetHash), + }); + + module.DefineLibraryMethod("initialize", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.Reinitialize), + new System.Func(IronRuby.Builtins.RegexpOps.Reinitialize), + new System.Func(IronRuby.Builtins.RegexpOps.Reinitialize), + new System.Func(IronRuby.Builtins.RegexpOps.Reinitialize), + new System.Func(IronRuby.Builtins.RegexpOps.Reinitialize), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.Inspect), + }); + + module.DefineLibraryMethod("kcode", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.GetEncoding), + }); + + module.DefineLibraryMethod("match", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.Match), + }); + + module.DefineLibraryMethod("options", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.GetOptions), + }); + + module.DefineLibraryMethod("source", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.Source), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.ToS), + }); + + } + + private void LoadRegexp_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineRuleGenerator("compile", 0x61, IronRuby.Builtins.RegexpOps.Compile()); + + module.DefineLibraryMethod("escape", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.Escape), + }); + + module.DefineLibraryMethod("last_match", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.LastMatch), + new System.Func(IronRuby.Builtins.RegexpOps.LastMatch), + }); + + module.DefineLibraryMethod("quote", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.Escape), + }); + + module.DefineLibraryMethod("union", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RegexpOps.Union), + }); + + } + + #if !SILVERLIGHT + private void LoadSignal_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("list", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Signal.List), + }); + + module.DefineLibraryMethod("trap", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.Signal.Trap), + new System.Func(IronRuby.Builtins.Signal.Trap), + }); + + } + #endif + + private void LoadString_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.HideMethod("clone"); + module.HideMethod("version"); + module.DefineLibraryMethod("%", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Format), + }); + + module.DefineLibraryMethod("*", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Repeat), + }); + + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.GetChar), + new System.Func(IronRuby.Builtins.MutableStringOps.GetSubstring), + new System.Func(IronRuby.Builtins.MutableStringOps.GetSubstring), + new System.Func(IronRuby.Builtins.MutableStringOps.GetSubstring), + new System.Func(IronRuby.Builtins.MutableStringOps.GetSubstring), + new System.Func(IronRuby.Builtins.MutableStringOps.GetSubstring), + }); + + module.DefineLibraryMethod("[]=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ReplaceCharacter), + new System.Func(IronRuby.Builtins.MutableStringOps.SetCharacter), + new System.Func(IronRuby.Builtins.MutableStringOps.ReplaceSubstring), + new System.Func(IronRuby.Builtins.MutableStringOps.ReplaceSubstring), + new System.Func(IronRuby.Builtins.MutableStringOps.ReplaceSubstring), + new System.Func(IronRuby.Builtins.MutableStringOps.ReplaceSubstring), + }); + + module.DefineLibraryMethod("+", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Concatenate), + }); + + module.DefineLibraryMethod("<<", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Append), + new System.Func(IronRuby.Builtins.MutableStringOps.Append), + }); + + module.DefineLibraryMethod("<=>", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Compare), + new System.Func(IronRuby.Builtins.MutableStringOps.Compare), + }); + + module.DefineLibraryMethod("=~", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Match), + new System.Func(IronRuby.Builtins.MutableStringOps.Match), + new System.Func(IronRuby.Builtins.MutableStringOps.Match), + }); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.StringEquals), + new System.Func(IronRuby.Builtins.MutableStringOps.Equals), + }); + + module.DefineLibraryMethod("===", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.StringEquals), + new System.Func(IronRuby.Builtins.MutableStringOps.Equals), + }); + + module.DefineLibraryMethod("capitalize", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Capitalize), + }); + + module.DefineLibraryMethod("capitalize!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.CapitalizeInPlace), + }); + + module.DefineLibraryMethod("casecmp", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Casecmp), + }); + + module.DefineLibraryMethod("center", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Center), + }); + + module.DefineLibraryMethod("chomp", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Chomp), + new System.Func(IronRuby.Builtins.MutableStringOps.Chomp), + }); + + module.DefineLibraryMethod("chomp!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ChompInPlace), + new System.Func(IronRuby.Builtins.MutableStringOps.ChompInPlace), + }); + + module.DefineLibraryMethod("chop", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Chop), + }); + + module.DefineLibraryMethod("chop!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ChopInPlace), + }); + + module.DefineLibraryMethod("concat", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Append), + new System.Func(IronRuby.Builtins.MutableStringOps.Append), + }); + + module.DefineLibraryMethod("count", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Count), + }); + + module.DefineLibraryMethod("delete", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Delete), + }); + + module.DefineLibraryMethod("delete!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.DeleteInPlace), + }); + + module.DefineLibraryMethod("downcase", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.DownCase), + }); + + module.DefineLibraryMethod("downcase!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.DownCaseInPlace), + }); + + module.DefineLibraryMethod("dump", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Dump), + }); + + module.DefineLibraryMethod("each", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.EachLine), + new System.Func(IronRuby.Builtins.MutableStringOps.EachLine), + }); + + module.DefineLibraryMethod("each_byte", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.EachByte), + }); + + module.DefineLibraryMethod("each_line", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.EachLine), + new System.Func(IronRuby.Builtins.MutableStringOps.EachLine), + }); + + module.DefineLibraryMethod("empty?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Empty), + }); + + module.DefineLibraryMethod("encoding", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.GetEncoding), + }); + + module.DefineLibraryMethod("gsub", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ReplaceAll), + new System.Func(IronRuby.Builtins.MutableStringOps.BlockReplaceAll), + new System.Func(IronRuby.Builtins.MutableStringOps.BlockReplaceAll), + }); + + module.DefineLibraryMethod("gsub!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.BlockReplaceAllInPlace), + new System.Func(IronRuby.Builtins.MutableStringOps.ReplaceAllInPlace), + }); + + module.DefineLibraryMethod("hex", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ToIntegerHex), + }); + + module.DefineLibraryMethod("include?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Include), + new System.Func(IronRuby.Builtins.MutableStringOps.Include), + }); + + module.DefineLibraryMethod("index", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Index), + new System.Func(IronRuby.Builtins.MutableStringOps.Index), + new System.Func(IronRuby.Builtins.MutableStringOps.Index), + }); + + module.DefineLibraryMethod("initialize", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Reinitialize), + new System.Func(IronRuby.Builtins.MutableStringOps.Reinitialize), + }); + + module.DefineLibraryMethod("initialize_copy", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Reinitialize), + }); + + module.DefineLibraryMethod("insert", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Insert), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Dump), + }); + + module.DefineLibraryMethod("intern", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ToSym), + }); + + module.DefineLibraryMethod("length", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.MutableStringLength), + }); + + module.DefineLibraryMethod("ljust", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.LeftJustify), + new System.Func(IronRuby.Builtins.MutableStringOps.LeftJustify), + }); + + module.DefineLibraryMethod("lstrip", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.StripLeft), + }); + + module.DefineLibraryMethod("lstrip!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.StripLeftInPlace), + }); + + module.DefineLibraryMethod("match", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.MatchRegexp), + new System.Func(IronRuby.Builtins.MutableStringOps.MatchObject), + }); + + module.DefineLibraryMethod("next", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Succ), + }); + + module.DefineLibraryMethod("next!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.SuccInPlace), + }); + + module.DefineLibraryMethod("oct", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ToIntegerOctal), + }); + + module.DefineLibraryMethod("replace", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Replace), + }); + + module.DefineLibraryMethod("reverse", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.GetReversed), + }); + + module.DefineLibraryMethod("reverse!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Reverse), + }); + + module.DefineLibraryMethod("rindex", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ReverseIndex), + new System.Func(IronRuby.Builtins.MutableStringOps.ReverseIndex), + new System.Func(IronRuby.Builtins.MutableStringOps.ReverseIndex), + new System.Func(IronRuby.Builtins.MutableStringOps.ReverseIndex), + new System.Func(IronRuby.Builtins.MutableStringOps.ReverseIndex), + }); + + module.DefineLibraryMethod("rjust", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.RightJustify), + new System.Func(IronRuby.Builtins.MutableStringOps.RightJustify), + }); + + module.DefineLibraryMethod("rstrip", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.StripRight), + }); + + module.DefineLibraryMethod("rstrip!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.StripRightInPlace), + }); + + module.DefineLibraryMethod("scan", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Scan), + new System.Func(IronRuby.Builtins.MutableStringOps.Scan), + }); + + module.DefineLibraryMethod("size", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.MutableStringLength), + }); + + module.DefineLibraryMethod("slice", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.GetChar), + new System.Func(IronRuby.Builtins.MutableStringOps.GetSubstring), + new System.Func(IronRuby.Builtins.MutableStringOps.GetSubstring), + new System.Func(IronRuby.Builtins.MutableStringOps.GetSubstring), + new System.Func(IronRuby.Builtins.MutableStringOps.GetSubstring), + new System.Func(IronRuby.Builtins.MutableStringOps.GetSubstring), + }); + + module.DefineLibraryMethod("slice!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.RemoveCharInPlace), + new System.Func(IronRuby.Builtins.MutableStringOps.RemoveSubstringInPlace), + new System.Func(IronRuby.Builtins.MutableStringOps.RemoveSubstringInPlace), + new System.Func(IronRuby.Builtins.MutableStringOps.RemoveSubstringInPlace), + new System.Func(IronRuby.Builtins.MutableStringOps.RemoveSubstringInPlace), + new System.Func(IronRuby.Builtins.MutableStringOps.RemoveSubstringInPlace), + }); + + module.DefineLibraryMethod("split", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Split), + new System.Func(IronRuby.Builtins.MutableStringOps.Split), + new System.Func(IronRuby.Builtins.MutableStringOps.Split), + }); + + module.DefineLibraryMethod("squeeze", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Squeeze), + }); + + module.DefineLibraryMethod("squeeze!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.SqueezeInPlace), + }); + + module.DefineLibraryMethod("strip", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Strip), + }); + + module.DefineLibraryMethod("strip!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.StripInPlace), + }); + + module.DefineLibraryMethod("sub", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.BlockReplaceFirst), + new System.Func(IronRuby.Builtins.MutableStringOps.BlockReplaceFirst), + new System.Func(IronRuby.Builtins.MutableStringOps.ReplaceFirst), + }); + + module.DefineLibraryMethod("sub!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.BlockReplaceFirstInPlace), + new System.Func(IronRuby.Builtins.MutableStringOps.ReplaceFirstInPlace), + }); + + module.DefineLibraryMethod("succ", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Succ), + }); + + module.DefineLibraryMethod("succ!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.SuccInPlace), + }); + + module.DefineLibraryMethod("sum", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.GetChecksum), + }); + + module.DefineLibraryMethod("swapcase", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.SwapCase), + }); + + module.DefineLibraryMethod("swapcase!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.SwapCaseInPlace), + }); + + module.DefineLibraryMethod("to_clr_string", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ToClrString), + }); + + module.DefineLibraryMethod("to_f", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ToDouble), + }); + + module.DefineLibraryMethod("to_i", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ToInteger), + new System.Func(IronRuby.Builtins.MutableStringOps.ToInteger), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ToS), + }); + + module.DefineLibraryMethod("to_str", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ToS), + }); + + module.DefineLibraryMethod("to_sym", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.ToSym), + }); + + module.DefineLibraryMethod("tr", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Tr), + }); + + module.DefineLibraryMethod("tr!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.TrInPlace), + }); + + module.DefineLibraryMethod("tr_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.TrSqueeze), + }); + + module.DefineLibraryMethod("tr_s!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.TrSqueezeInPlace), + }); + + module.DefineLibraryMethod("unpack", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.Unpack), + }); + + module.DefineLibraryMethod("upcase", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.UpCase), + }); + + module.DefineLibraryMethod("upcase!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.UpCaseInPlace), + }); + + module.DefineLibraryMethod("upto", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.MutableStringOps.UpTo), + }); + + } + + private void LoadStruct_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + #if !SILVERLIGHT + module.SetConstant("Tms", IronRuby.Builtins.RubyStructOps.CreateTmsClass(module)); + #endif + + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.GetValue), + new System.Func(IronRuby.Builtins.RubyStructOps.GetValue), + new System.Func(IronRuby.Builtins.RubyStructOps.GetValue), + new System.Func(IronRuby.Builtins.RubyStructOps.GetValue), + }); + + module.DefineLibraryMethod("[]=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.SetValue), + new System.Func(IronRuby.Builtins.RubyStructOps.SetValue), + new System.Func(IronRuby.Builtins.RubyStructOps.SetValue), + new System.Func(IronRuby.Builtins.RubyStructOps.SetValue), + }); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.Equals), + }); + + module.DefineLibraryMethod("each", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.Each), + }); + + module.DefineLibraryMethod("each_pair", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.EachPair), + }); + + module.DefineLibraryMethod("eql?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.Equal), + }); + + module.DefineLibraryMethod("hash", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.Hash), + }); + + module.DefineLibraryMethod("initialize", 0x52, new System.Delegate[] { + new System.Action(IronRuby.Builtins.RubyStructOps.Reinitialize), + }); + + module.DefineLibraryMethod("initialize_copy", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.InitializeCopy), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.Inspect), + }); + + module.DefineLibraryMethod("length", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.GetSize), + }); + + module.DefineLibraryMethod("members", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.GetMembers), + }); + + module.DefineLibraryMethod("select", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.Select), + }); + + module.DefineLibraryMethod("size", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.GetSize), + }); + + module.DefineLibraryMethod("to_a", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.Values), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.Inspect), + }); + + module.DefineLibraryMethod("values", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.Values), + }); + + module.DefineLibraryMethod("values_at", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.ValuesAt), + }); + + } + + private void LoadStruct_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("new", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.RubyStructOps.NewAnonymousStruct), + new System.Func(IronRuby.Builtins.RubyStructOps.NewAnonymousStruct), + new System.Func(IronRuby.Builtins.RubyStructOps.NewAnonymousStruct), + new System.Func(IronRuby.Builtins.RubyStructOps.NewStruct), + }); + + } + + private void LoadSymbol_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("id2name", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.SymbolOps.ToString), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.SymbolOps.Inspect), + }); + + module.DefineLibraryMethod("to_clr_string", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.SymbolOps.ToClrString), + }); + + module.DefineLibraryMethod("to_i", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.SymbolOps.ToInteger), + }); + + module.DefineLibraryMethod("to_int", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.SymbolOps.ToInteger), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.SymbolOps.ToString), + }); + + module.DefineLibraryMethod("to_sym", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.SymbolOps.ToSymbol), + }); + + } + + private void LoadSymbol_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("all_symbols", 0x61, new System.Delegate[] { + new System.Func>(IronRuby.Builtins.SymbolOps.GetAllSymbols), + }); + + } + + private void LoadSystem__Collections__Generic__IDictionary_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Object>(IronRuby.Builtins.IDictionaryOps.GetElement), + }); + + module.DefineLibraryMethod("[]=", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Object, System.Object>(IronRuby.Builtins.IDictionaryOps.SetElement), + }); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Boolean>(IronRuby.Builtins.IDictionaryOps.Equals), + }); + + module.DefineLibraryMethod("clear", 0x51, new System.Delegate[] { + new System.Func, System.Collections.Generic.IDictionary>(IronRuby.Builtins.IDictionaryOps.Clear), + }); + + module.DefineLibraryMethod("default", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Object>(IronRuby.Builtins.IDictionaryOps.GetDefaultValue), + }); + + module.DefineLibraryMethod("default_proc", 0x51, new System.Delegate[] { + new System.Func, IronRuby.Builtins.Proc>(IronRuby.Builtins.IDictionaryOps.GetDefaultProc), + }); + + module.DefineLibraryMethod("delete", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Object>(IronRuby.Builtins.IDictionaryOps.Delete), + }); + + module.DefineLibraryMethod("delete_if", 0x51, new System.Delegate[] { + new System.Func, System.Object>(IronRuby.Builtins.IDictionaryOps.DeleteIf), + }); + + module.DefineLibraryMethod("each", 0x51, new System.Delegate[] { + new System.Func, System.Object>(IronRuby.Builtins.IDictionaryOps.Each), + }); + + module.DefineLibraryMethod("each_key", 0x51, new System.Delegate[] { + new System.Func, System.Object>(IronRuby.Builtins.IDictionaryOps.EachKey), + }); + + module.DefineLibraryMethod("each_pair", 0x51, new System.Delegate[] { + new System.Func, System.Object>(IronRuby.Builtins.IDictionaryOps.EachPair), + }); + + module.DefineLibraryMethod("each_value", 0x51, new System.Delegate[] { + new System.Func, System.Object>(IronRuby.Builtins.IDictionaryOps.EachValue), + }); + + module.DefineLibraryMethod("empty?", 0x51, new System.Delegate[] { + new System.Func, System.Boolean>(IronRuby.Builtins.IDictionaryOps.Empty), + }); + + module.DefineLibraryMethod("fetch", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Object, System.Object>(IronRuby.Builtins.IDictionaryOps.Fetch), + }); + + module.DefineLibraryMethod("has_key?", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Boolean>(IronRuby.Builtins.IDictionaryOps.HasKey), + }); + + module.DefineLibraryMethod("has_value?", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Boolean>(IronRuby.Builtins.IDictionaryOps.HasValue), + }); + + module.DefineLibraryMethod("include?", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Boolean>(IronRuby.Builtins.IDictionaryOps.HasKey), + }); + + module.DefineLibraryMethod("index", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Object>(IronRuby.Builtins.IDictionaryOps.Index), + }); + + module.DefineLibraryMethod("indexes", 0x51, new System.Delegate[] { + new System.Func, System.Object[], IronRuby.Builtins.RubyArray>(IronRuby.Builtins.IDictionaryOps.Indexes), + }); + + module.DefineLibraryMethod("indices", 0x51, new System.Delegate[] { + new System.Func, System.Object[], IronRuby.Builtins.RubyArray>(IronRuby.Builtins.IDictionaryOps.Indexes), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func, IronRuby.Builtins.MutableString>(IronRuby.Builtins.IDictionaryOps.Inspect), + }); + + module.DefineLibraryMethod("invert", 0x51, new System.Delegate[] { + new System.Func, IronRuby.Builtins.Hash>(IronRuby.Builtins.IDictionaryOps.Invert), + }); + + module.DefineLibraryMethod("key?", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Boolean>(IronRuby.Builtins.IDictionaryOps.HasKey), + }); + + module.DefineLibraryMethod("keys", 0x51, new System.Delegate[] { + new System.Func, IronRuby.Builtins.RubyArray>(IronRuby.Builtins.IDictionaryOps.GetKeys), + }); + + module.DefineLibraryMethod("length", 0x51, new System.Delegate[] { + new System.Func, System.Int32>(IronRuby.Builtins.IDictionaryOps.Length), + }); + + module.DefineLibraryMethod("member?", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Boolean>(IronRuby.Builtins.IDictionaryOps.HasKey), + }); + + module.DefineLibraryMethod("merge", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Object>(IronRuby.Builtins.IDictionaryOps.Merge), + }); + + module.DefineLibraryMethod("merge!", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Object>(IronRuby.Builtins.IDictionaryOps.Update), + }); + + module.DefineLibraryMethod("rehash", 0x51, new System.Delegate[] { + new System.Func, System.Collections.Generic.IDictionary>(IronRuby.Builtins.IDictionaryOps.Rehash), + }); + + module.DefineLibraryMethod("reject", 0x51, new System.Delegate[] { + new System.Func, System.Object>(IronRuby.Builtins.IDictionaryOps.Reject), + }); + + module.DefineLibraryMethod("reject!", 0x51, new System.Delegate[] { + new System.Func, System.Object>(IronRuby.Builtins.IDictionaryOps.RejectMutate), + }); + + module.DefineLibraryMethod("replace", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Collections.Generic.IDictionary>(IronRuby.Builtins.IDictionaryOps.Replace), + }); + + module.DefineLibraryMethod("select", 0x51, new System.Delegate[] { + new System.Func, System.Object>(IronRuby.Builtins.IDictionaryOps.Select), + }); + + module.DefineLibraryMethod("shift", 0x51, new System.Delegate[] { + new System.Func, System.Object>(IronRuby.Builtins.IDictionaryOps.Shift), + }); + + module.DefineLibraryMethod("size", 0x51, new System.Delegate[] { + new System.Func, System.Int32>(IronRuby.Builtins.IDictionaryOps.Length), + }); + + module.DefineLibraryMethod("sort", 0x51, new System.Delegate[] { + new System.Func, System.Object>(IronRuby.Builtins.IDictionaryOps.Sort), + }); + + module.DefineLibraryMethod("store", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Object, System.Object>(IronRuby.Builtins.IDictionaryOps.SetElement), + }); + + module.DefineLibraryMethod("to_a", 0x51, new System.Delegate[] { + new System.Func, IronRuby.Builtins.RubyArray>(IronRuby.Builtins.IDictionaryOps.ToArray), + }); + + module.DefineLibraryMethod("to_hash", 0x51, new System.Delegate[] { + new System.Func, System.Collections.Generic.IDictionary>(IronRuby.Builtins.IDictionaryOps.ToHash), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func, IronRuby.Builtins.MutableString>(IronRuby.Builtins.IDictionaryOps.ToString), + }); + + module.DefineLibraryMethod("update", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Object>(IronRuby.Builtins.IDictionaryOps.Update), + }); + + module.DefineLibraryMethod("value?", 0x51, new System.Delegate[] { + new System.Func, System.Object, System.Boolean>(IronRuby.Builtins.IDictionaryOps.HasValue), + }); + + module.DefineLibraryMethod("values", 0x51, new System.Delegate[] { + new System.Func, IronRuby.Builtins.RubyArray>(IronRuby.Builtins.IDictionaryOps.GetValues), + }); + + module.DefineLibraryMethod("values_at", 0x51, new System.Delegate[] { + new System.Func, System.Object[], IronRuby.Builtins.RubyArray>(IronRuby.Builtins.IDictionaryOps.ValuesAt), + }); + + } + + private void LoadSystem__Collections__IEnumerable_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("each", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IEnumerableOps.Each), + }); + + } + + private void LoadSystem__Collections__IList_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("-", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Difference), + new System.Func(IronRuby.Builtins.IListOps.Difference), + }); + + module.DefineLibraryMethod("&", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Intersection), + }); + + module.DefineLibraryMethod("*", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Repetition), + new System.Func(IronRuby.Builtins.IListOps.Repetition), + new System.Func(IronRuby.Builtins.IListOps.Repetition), + new System.Func(IronRuby.Builtins.IListOps.Repetition), + }); + + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.GetElement), + new System.Func(IronRuby.Builtins.IListOps.GetElements), + new System.Func(IronRuby.Builtins.IListOps.GetElement), + }); + + module.DefineLibraryMethod("[]=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.SetElement), + new System.Func(IronRuby.Builtins.IListOps.SetElement), + new System.Func(IronRuby.Builtins.IListOps.SetElement), + new System.Func(IronRuby.Builtins.IListOps.SetElement), + new System.Func(IronRuby.Builtins.IListOps.SetElement), + new System.Func(IronRuby.Builtins.IListOps.SetElement), + new System.Func(IronRuby.Builtins.IListOps.SetElement), + }); + + module.DefineLibraryMethod("|", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Union), + }); + + module.DefineLibraryMethod("+", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Concatenate), + new System.Func(IronRuby.Builtins.IListOps.Concatenate), + }); + + module.DefineLibraryMethod("<<", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Append), + }); + + module.DefineLibraryMethod("<=>", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Compare), + new System.Func(IronRuby.Builtins.IListOps.Compare), + }); + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Equals), + new System.Func(IronRuby.Builtins.IListOps.Equals), + }); + + module.DefineLibraryMethod("assoc", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.GetContainerOfFirstItem), + }); + + module.DefineLibraryMethod("at", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.At), + new System.Func(IronRuby.Builtins.IListOps.At), + }); + + module.DefineLibraryMethod("clear", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Clear), + }); + + module.DefineLibraryMethod("collect!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.CollectInPlace), + }); + + module.DefineLibraryMethod("compact", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Compact), + }); + + module.DefineLibraryMethod("compact!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.CompactInPlace), + }); + + module.DefineLibraryMethod("concat", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Concat), + new System.Func(IronRuby.Builtins.IListOps.Concat), + }); + + module.DefineLibraryMethod("delete", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Delete), + new System.Func(IronRuby.Builtins.IListOps.Delete), + }); + + module.DefineLibraryMethod("delete_at", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.DeleteAt), + }); + + module.DefineLibraryMethod("delete_if", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.DeleteIf), + }); + + module.DefineLibraryMethod("each", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Each), + }); + + module.DefineLibraryMethod("each_index", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.EachIndex), + }); + + module.DefineLibraryMethod("empty?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Empty), + }); + + module.DefineLibraryMethod("eql?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.HashEquals), + }); + + module.DefineLibraryMethod("fetch", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Fetch), + new System.Func(IronRuby.Builtins.IListOps.Fetch), + }); + + module.DefineLibraryMethod("fill", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Fill), + new System.Func(IronRuby.Builtins.IListOps.Fill), + new System.Func(IronRuby.Builtins.IListOps.Fill), + new System.Func(IronRuby.Builtins.IListOps.Fill), + new System.Func(IronRuby.Builtins.IListOps.Fill), + new System.Func(IronRuby.Builtins.IListOps.Fill), + new System.Func(IronRuby.Builtins.IListOps.Fill), + new System.Func(IronRuby.Builtins.IListOps.Fill), + }); + + module.DefineLibraryMethod("first", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.First), + new System.Func(IronRuby.Builtins.IListOps.First), + new System.Func(IronRuby.Builtins.IListOps.First), + }); + + module.DefineLibraryMethod("flatten", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Flatten), + }); + + module.DefineLibraryMethod("flatten!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.FlattenInPlace), + }); + + module.DefineLibraryMethod("hash", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.GetHashCode), + }); + + module.DefineLibraryMethod("include?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Include), + }); + + module.DefineLibraryMethod("index", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Index), + }); + + module.DefineLibraryMethod("indexes", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Indexes), + }); + + module.DefineLibraryMethod("indices", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Indexes), + }); + + module.DefineLibraryMethod("initialize_copy", 0x52, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Replace), + }); + + module.DefineLibraryMethod("insert", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Insert), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Inspect), + }); + + module.DefineLibraryMethod("join", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Join), + new System.Func(IronRuby.Builtins.IListOps.Join), + new System.Func(IronRuby.Builtins.IListOps.Join), + }); + + module.DefineLibraryMethod("last", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Last), + new System.Func(IronRuby.Builtins.IListOps.Last), + new System.Func(IronRuby.Builtins.IListOps.Last), + }); + + module.DefineLibraryMethod("length", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Length), + }); + + module.DefineLibraryMethod("map!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.CollectInPlace), + }); + + module.DefineLibraryMethod("nitems", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.NumberOfNonNilItems), + }); + + module.DefineLibraryMethod("pop", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Pop), + }); + + module.DefineLibraryMethod("push", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Push), + }); + + module.DefineLibraryMethod("rassoc", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.GetContainerOfSecondItem), + }); + + module.DefineLibraryMethod("reject!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.RejectInPlace), + }); + + module.DefineLibraryMethod("replace", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Replace), + }); + + module.DefineLibraryMethod("reverse", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Reverse), + }); + + module.DefineLibraryMethod("reverse!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.InPlaceReverse), + }); + + module.DefineLibraryMethod("rindex", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.ReverseIndex), + }); + + module.DefineLibraryMethod("shift", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Shift), + }); + + module.DefineLibraryMethod("size", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Length), + }); + + module.DefineLibraryMethod("slice", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.GetElement), + new System.Func(IronRuby.Builtins.IListOps.GetElements), + new System.Func(IronRuby.Builtins.IListOps.GetElement), + }); + + module.DefineLibraryMethod("slice!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.SliceInPlace), + new System.Func(IronRuby.Builtins.IListOps.SliceInPlace), + new System.Func(IronRuby.Builtins.IListOps.SliceInPlace), + }); + + module.DefineLibraryMethod("sort", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Sort), + }); + + module.DefineLibraryMethod("sort!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.SortInPlace), + }); + + module.DefineLibraryMethod("to_a", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.ToArray), + }); + + module.DefineLibraryMethod("to_ary", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.ToArray), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Join), + }); + + module.DefineLibraryMethod("transpose", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Transpose), + }); + + module.DefineLibraryMethod("uniq", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Unique), + }); + + module.DefineLibraryMethod("uniq!", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.UniqueSelf), + }); + + module.DefineLibraryMethod("unshift", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.Unshift), + }); + + module.DefineLibraryMethod("values_at", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IListOps.ValuesAt), + }); + + } + + private void LoadSystem__IComparable_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("<=>", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.IComparableOps.Compare), + }); + + } + + private void LoadSystem__Type_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("to_class", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TypeOps.ToClass), + }); + + module.DefineLibraryMethod("to_module", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TypeOps.ToModule), + }); + + } + + private void LoadSystemExit_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("status", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.SystemExitOps.GetStatus), + }); + + module.DefineLibraryMethod("success?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.SystemExitOps.IsSuccessful), + }); + + } + + private void LoadThread_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("[]", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.GetElement), + new System.Func(IronRuby.Builtins.ThreadOps.GetElement), + new System.Func(IronRuby.Builtins.ThreadOps.GetElement), + }); + + module.DefineLibraryMethod("[]=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.SetElement), + new System.Func(IronRuby.Builtins.ThreadOps.SetElement), + new System.Func(IronRuby.Builtins.ThreadOps.SetElement), + }); + + module.DefineLibraryMethod("abort_on_exception", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.AbortOnException), + }); + + module.DefineLibraryMethod("abort_on_exception=", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.AbortOnException), + }); + + module.DefineLibraryMethod("alive?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.IsAlive), + }); + + module.DefineLibraryMethod("exit", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.Kill), + }); + + module.DefineLibraryMethod("group", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.Group), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.Inspect), + }); + + module.DefineLibraryMethod("join", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.Join), + new System.Func(IronRuby.Builtins.ThreadOps.Join), + }); + + module.DefineLibraryMethod("key?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.HasKey), + new System.Func(IronRuby.Builtins.ThreadOps.HasKey), + new System.Func(IronRuby.Builtins.ThreadOps.HasKey), + }); + + module.DefineLibraryMethod("keys", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.Keys), + }); + + module.DefineLibraryMethod("kill", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.Kill), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("run", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ThreadOps.Run), + }); + + #endif + module.DefineLibraryMethod("status", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.Status), + }); + + module.DefineLibraryMethod("terminate", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.Kill), + }); + + module.DefineLibraryMethod("value", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.Value), + }); + + #if !SILVERLIGHT + module.DefineLibraryMethod("wakeup", 0x51, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ThreadOps.Run), + }); + + #endif + } + + private void LoadThread_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("abort_on_exception", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.GlobalAbortOnException), + }); + + module.DefineLibraryMethod("abort_on_exception=", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.GlobalAbortOnException), + }); + + module.DefineLibraryMethod("critical", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.Critical), + }); + + module.DefineLibraryMethod("critical=", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.Critical), + }); + + module.DefineLibraryMethod("current", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.Current), + }); + + module.DefineLibraryMethod("list", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.List), + }); + + module.DefineLibraryMethod("new", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.CreateThread), + }); + + module.DefineLibraryMethod("pass", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ThreadOps.Yield), + }); + + module.DefineLibraryMethod("start", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadOps.CreateThread), + }); + + module.DefineLibraryMethod("stop", 0x61, new System.Delegate[] { + new System.Action(IronRuby.Builtins.ThreadOps.Stop), + }); + + } + + private void LoadThreadGroup_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("Default", IronRuby.Builtins.ThreadGroup.Default); + + module.DefineLibraryMethod("add", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadGroup.Add), + }); + + module.DefineLibraryMethod("list", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.ThreadGroup.List), + }); + + } + + private void LoadTime_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("-", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.SubtractSeconds), + new System.Func(IronRuby.Builtins.TimeOps.SubtractTime), + }); + + module.DefineLibraryMethod("_dump", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Dump), + }); + + module.DefineLibraryMethod("+", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.AddSeconds), + new System.Func(IronRuby.Builtins.TimeOps.AddTime), + }); + + module.DefineLibraryMethod("<=>", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.CompareSeconds), + new System.Func(IronRuby.Builtins.TimeOps.CompareTo), + }); + + module.DefineLibraryMethod("asctime", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.ToString), + }); + + module.DefineLibraryMethod("ctime", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.ToString), + }); + + module.DefineLibraryMethod("day", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Day), + }); + + module.DefineLibraryMethod("dst?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.IsDST), + }); + + module.DefineLibraryMethod("dup", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Clone), + }); + + module.DefineLibraryMethod("eql?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Eql), + new System.Func(IronRuby.Builtins.TimeOps.Eql), + }); + + module.DefineLibraryMethod("getgm", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.GetUTC), + }); + + module.DefineLibraryMethod("getlocal", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.GetLocal), + }); + + module.DefineLibraryMethod("getutc", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.GetUTC), + }); + + module.DefineLibraryMethod("gmt?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.IsUTC), + }); + + module.DefineLibraryMethod("gmt_offset", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Offset), + }); + + module.DefineLibraryMethod("gmtime", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.ToUTC), + }); + + module.DefineLibraryMethod("gmtoff", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Offset), + }); + + module.DefineLibraryMethod("hash", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.GetHash), + }); + + module.DefineLibraryMethod("hour", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Hour), + }); + + module.DefineLibraryMethod("inspect", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.ToString), + }); + + module.DefineLibraryMethod("isdst", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.IsDST), + }); + + module.DefineLibraryMethod("localtime", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.ToLocalTime), + }); + + module.DefineLibraryMethod("mday", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Day), + }); + + module.DefineLibraryMethod("min", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Minute), + }); + + module.DefineLibraryMethod("mon", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Month), + }); + + module.DefineLibraryMethod("month", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Month), + }); + + module.DefineLibraryMethod("sec", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Second), + }); + + module.DefineLibraryMethod("strftime", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.FormatTime), + }); + + module.DefineLibraryMethod("succ", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.SuccessiveSecond), + }); + + module.DefineLibraryMethod("to_a", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.ToArray), + }); + + module.DefineLibraryMethod("to_f", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.ToFloatSeconds), + }); + + module.DefineLibraryMethod("to_i", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.ToSeconds), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.ToString), + }); + + module.DefineLibraryMethod("tv_sec", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.ToSeconds), + }); + + module.DefineLibraryMethod("tv_usec", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.GetMicroSeconds), + }); + + module.DefineLibraryMethod("usec", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.GetMicroSeconds), + }); + + module.DefineLibraryMethod("utc", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.ToUTC), + }); + + module.DefineLibraryMethod("utc?", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.IsUTC), + }); + + module.DefineLibraryMethod("utc_offset", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Offset), + }); + + module.DefineLibraryMethod("wday", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.DayOfWeek), + }); + + module.DefineLibraryMethod("yday", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.DayOfYear), + }); + + module.DefineLibraryMethod("year", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Year), + }); + + module.DefineLibraryMethod("zone", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.GetZone), + }); + + } + + private void LoadTime_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("_load", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Load), + }); + + module.DefineLibraryMethod("at", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Create), + new System.Func(IronRuby.Builtins.TimeOps.Create), + new System.Func(IronRuby.Builtins.TimeOps.Create), + }); + + module.DefineLibraryMethod("gm", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + }); + + module.DefineLibraryMethod("local", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + }); + + module.DefineLibraryMethod("mktime", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateLocalTime), + }); + + module.DefineLibraryMethod("now", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.CreateTime), + }); + + module.DefineLibraryMethod("today", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.Today), + }); + + module.DefineLibraryMethod("utc", 0x61, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + new System.Func(IronRuby.Builtins.TimeOps.CreateGmtTime), + }); + + } + + private void LoadTrueClass_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("&", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TrueClass.And), + new System.Func(IronRuby.Builtins.TrueClass.And), + }); + + module.DefineLibraryMethod("^", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TrueClass.Xor), + new System.Func(IronRuby.Builtins.TrueClass.Xor), + }); + + module.DefineLibraryMethod("|", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TrueClass.Or), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.TrueClass.ToString), + }); + + } + + private void LoadUnboundMethod_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("==", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.UnboundMethod.Equal), + new System.Func(IronRuby.Builtins.UnboundMethod.Equal), + }); + + module.DefineLibraryMethod("arity", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.UnboundMethod.GetArity), + }); + + module.DefineLibraryMethod("bind", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.UnboundMethod.Bind), + }); + + module.DefineLibraryMethod("clone", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.UnboundMethod.Clone), + }); + + module.DefineLibraryMethod("to_s", 0x51, new System.Delegate[] { + new System.Func(IronRuby.Builtins.UnboundMethod.ToS), + }); + + } + + public static System.Exception/*!*/ ExceptionFactory__EOFError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.EOFError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__FloatDomainError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.FloatDomainError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__Interrupt(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.Interrupt(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__LoadError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.LoadError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__LocalJumpError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.LocalJumpError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__NoMemoryError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.NoMemoryError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__NotImplementedError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.NotImplementedError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__RegexpError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.RegexpError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__RuntimeError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.RuntimeError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__ScriptError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.ScriptError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__SignalException(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.SignalException(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__SyntaxError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.SyntaxError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__SystemExit(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.SystemExit(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__SystemStackError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.SystemStackError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__ThreadError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.Builtins.ThreadError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__ArgumentError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new System.ArgumentException(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__RangeError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new System.ArgumentOutOfRangeException(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__ZeroDivisionError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new System.DivideByZeroException(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__Exception(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new System.Exception(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__IndexError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new System.IndexOutOfRangeException(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__TypeError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new System.InvalidOperationException(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__IOError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new System.IO.IOException(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__NameError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new System.MemberAccessException(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__NoMethodError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new System.MissingMethodException(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__SystemCallError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new System.Runtime.InteropServices.ExternalException(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__SecurityError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new System.Security.SecurityException(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + public static System.Exception/*!*/ ExceptionFactory__StandardError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new System.SystemException(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + } +} + +namespace IronRuby.StandardLibrary.Threading { + public sealed class ThreadingLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + IronRuby.Builtins.RubyClass classRef0 = GetClass(typeof(System.Object)); + + + DefineGlobalClass("ConditionVariable", typeof(IronRuby.StandardLibrary.Threading.RubyConditionVariable), classRef0, new System.Action(LoadConditionVariable_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("Mutex", typeof(IronRuby.StandardLibrary.Threading.RubyMutex), classRef0, new System.Action(LoadMutex_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def1 = DefineGlobalClass("Queue", typeof(IronRuby.StandardLibrary.Threading.RubyQueue), classRef0, new System.Action(LoadQueue_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("SizedQueue", typeof(IronRuby.StandardLibrary.Threading.SizedQueue), def1, new System.Action(LoadSizedQueue_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + } + + private void LoadConditionVariable_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("broadcast", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyConditionVariable.Broadcast), + }); + + module.DefineLibraryMethod("signal", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyConditionVariable.Signal), + }); + + module.DefineLibraryMethod("wait", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyConditionVariable.Wait), + }); + + } + + private void LoadMutex_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("exclusive_unlock", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyMutex.ExclusiveUnlock), + }); + + module.DefineLibraryMethod("lock", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyMutex.Lock), + }); + + module.DefineLibraryMethod("locked?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyMutex.IsLocked), + }); + + module.DefineLibraryMethod("synchronize", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyMutex.Synchronize), + }); + + module.DefineLibraryMethod("try_lock", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyMutex.TryLock), + }); + + module.DefineLibraryMethod("unlock", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyMutex.Unlock), + }); + + } + + private void LoadQueue_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("<<", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyQueue.Enqueue), + }); + + module.DefineLibraryMethod("clear", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyQueue.Clear), + }); + + module.DefineLibraryMethod("deq", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyQueue.Dequeue), + }); + + module.DefineLibraryMethod("empty?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyQueue.IsEmpty), + }); + + module.DefineLibraryMethod("enq", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyQueue.Enqueue), + }); + + module.DefineLibraryMethod("length", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyQueue.GetCount), + }); + + module.DefineLibraryMethod("num_waiting", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyQueue.GetNumberOfWaitingThreads), + }); + + module.DefineLibraryMethod("pop", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyQueue.Dequeue), + }); + + module.DefineLibraryMethod("push", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyQueue.Enqueue), + }); + + module.DefineLibraryMethod("shift", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyQueue.Dequeue), + }); + + module.DefineLibraryMethod("size", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.RubyQueue.GetCount), + }); + + } + + private void LoadSizedQueue_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("<<", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.SizedQueue.Enqueue), + }); + + module.DefineLibraryMethod("deq", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.SizedQueue.Dequeue), + }); + + module.DefineLibraryMethod("enq", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.SizedQueue.Enqueue), + }); + + module.DefineLibraryMethod("initialize", 0x12, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.SizedQueue.Reinitialize), + }); + + module.DefineLibraryMethod("max", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.SizedQueue.GetLimit), + }); + + module.DefineLibraryMethod("max=", 0x11, new System.Delegate[] { + new System.Action(IronRuby.StandardLibrary.Threading.SizedQueue.SetLimit), + }); + + module.DefineLibraryMethod("pop", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.SizedQueue.Dequeue), + }); + + module.DefineLibraryMethod("push", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.SizedQueue.Enqueue), + }); + + module.DefineLibraryMethod("shift", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Threading.SizedQueue.Dequeue), + }); + + } + + } +} + +namespace IronRuby.StandardLibrary.Sockets { + public sealed class SocketsLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + IronRuby.Builtins.RubyClass classRef0 = GetClass(typeof(IronRuby.Builtins.RubyIO)); + IronRuby.Builtins.RubyClass classRef1 = GetClass(typeof(System.SystemException)); + + + #if !SILVERLIGHT + IronRuby.Builtins.RubyClass def3 = DefineGlobalClass("BasicSocket", typeof(IronRuby.StandardLibrary.Sockets.RubyBasicSocket), classRef0, new System.Action(LoadBasicSocket_Instance), new System.Action(LoadBasicSocket_Class), IronRuby.Builtins.RubyModule.EmptyArray, null); + #endif + #if !SILVERLIGHT && !SILVERLIGHT + IronRuby.Builtins.RubyModule def2 = DefineModule("Socket::Constants", typeof(IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants), new System.Action(LoadSocket__Constants_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + #endif + #if !SILVERLIGHT + DefineGlobalClass("SocketError", typeof(System.Net.Sockets.SocketException), classRef1, new System.Action(LoadSocketError_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.SocketErrorOps.Create), + }); + #endif + #if !SILVERLIGHT + IronRuby.Builtins.RubyClass def5 = DefineGlobalClass("IPSocket", typeof(IronRuby.StandardLibrary.Sockets.IPSocket), def3, new System.Action(LoadIPSocket_Instance), new System.Action(LoadIPSocket_Class), IronRuby.Builtins.RubyModule.EmptyArray, null); + #endif + #if !SILVERLIGHT + IronRuby.Builtins.RubyClass def1 = DefineGlobalClass("Socket", typeof(IronRuby.StandardLibrary.Sockets.RubySocket), def3, new System.Action(LoadSocket_Instance), new System.Action(LoadSocket_Class), IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.CreateSocket), + }); + #endif + #if !SILVERLIGHT + IronRuby.Builtins.RubyClass def4 = DefineGlobalClass("TCPSocket", typeof(IronRuby.StandardLibrary.Sockets.TCPSocket), def5, null, new System.Action(LoadTCPSocket_Class), IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.TCPSocket.CreateTCPSocket), + }); + #endif + #if !SILVERLIGHT + DefineGlobalClass("UDPSocket", typeof(IronRuby.StandardLibrary.Sockets.UDPSocket), def5, new System.Action(LoadUDPSocket_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.UDPSocket.CreateUDPSocket), + new System.Func(IronRuby.StandardLibrary.Sockets.UDPSocket.CreateUDPSocket), + }); + #endif + #if !SILVERLIGHT + DefineGlobalClass("TCPServer", typeof(IronRuby.StandardLibrary.Sockets.TCPServer), def4, new System.Action(LoadTCPServer_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.TCPServer.CreateTCPServer), + }); + #endif + #if !SILVERLIGHT && !SILVERLIGHT + def1.SetConstant("Constants", def2); + #endif + } + + #if !SILVERLIGHT + private void LoadBasicSocket_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("close_read", 0x11, new System.Delegate[] { + new System.Action(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.CloseRead), + }); + + module.DefineLibraryMethod("close_write", 0x11, new System.Delegate[] { + new System.Action(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.CloseWrite), + }); + + module.DefineLibraryMethod("getpeername", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.GetPeerName), + }); + + module.DefineLibraryMethod("getsockname", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.GetSocketName), + }); + + module.DefineLibraryMethod("getsockopt", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.GetSocketOption), + }); + + module.DefineLibraryMethod("recv", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.Receive), + }); + + module.DefineLibraryMethod("recv_nonblock", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.ReceiveNonBlocking), + }); + + module.DefineLibraryMethod("send", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.Send), + new System.Func(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.Send), + }); + + module.DefineLibraryMethod("setsockopt", 0x11, new System.Delegate[] { + new System.Action(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.SetSocketOption), + new System.Action(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.SetSocketOption), + new System.Action(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.SetSocketOption), + }); + + module.DefineLibraryMethod("shutdown", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.Shutdown), + new System.Func(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.Shutdown), + new System.Func(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.Shutdown), + }); + + } + #endif + + #if !SILVERLIGHT + private void LoadBasicSocket_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("do_not_reverse_lookup", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.GetDoNotReverseLookup), + }); + + module.DefineLibraryMethod("do_not_reverse_lookup=", 0x21, new System.Delegate[] { + new System.Action(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.SetDoNotReverseLookup), + }); + + module.DefineLibraryMethod("for_fd", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubyBasicSocket.ForFileDescriptor), + }); + + } + #endif + + #if !SILVERLIGHT + private void LoadIPSocket_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("addr", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.IPSocket.GetLocalAddress), + }); + + module.DefineLibraryMethod("peeraddr", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.IPSocket.GetPeerAddress), + }); + + module.DefineLibraryMethod("recvfrom", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.IPSocket.ReceiveFrom), + new System.Func(IronRuby.StandardLibrary.Sockets.IPSocket.ReceiveFrom), + }); + + } + #endif + + #if !SILVERLIGHT + private void LoadIPSocket_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("getaddress", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.IPSocket.GetAddress), + }); + + } + #endif + + #if !SILVERLIGHT + private void LoadSocket_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("AF_APPLETALK", IronRuby.StandardLibrary.Sockets.RubySocket.AF_APPLETALK); + module.SetConstant("AF_ATM", IronRuby.StandardLibrary.Sockets.RubySocket.AF_ATM); + module.SetConstant("AF_CCITT", IronRuby.StandardLibrary.Sockets.RubySocket.AF_CCITT); + module.SetConstant("AF_CHAOS", IronRuby.StandardLibrary.Sockets.RubySocket.AF_CHAOS); + module.SetConstant("AF_DATAKIT", IronRuby.StandardLibrary.Sockets.RubySocket.AF_DATAKIT); + module.SetConstant("AF_DLI", IronRuby.StandardLibrary.Sockets.RubySocket.AF_DLI); + module.SetConstant("AF_ECMA", IronRuby.StandardLibrary.Sockets.RubySocket.AF_ECMA); + module.SetConstant("AF_HYLINK", IronRuby.StandardLibrary.Sockets.RubySocket.AF_HYLINK); + module.SetConstant("AF_IMPLINK", IronRuby.StandardLibrary.Sockets.RubySocket.AF_IMPLINK); + module.SetConstant("AF_INET", IronRuby.StandardLibrary.Sockets.RubySocket.AF_INET); + module.SetConstant("AF_IPX", IronRuby.StandardLibrary.Sockets.RubySocket.AF_IPX); + module.SetConstant("AF_ISO", IronRuby.StandardLibrary.Sockets.RubySocket.AF_ISO); + module.SetConstant("AF_LAT", IronRuby.StandardLibrary.Sockets.RubySocket.AF_LAT); + module.SetConstant("AF_MAX", IronRuby.StandardLibrary.Sockets.RubySocket.AF_MAX); + module.SetConstant("AF_NETBIOS", IronRuby.StandardLibrary.Sockets.RubySocket.AF_NETBIOS); + module.SetConstant("AF_NS", IronRuby.StandardLibrary.Sockets.RubySocket.AF_NS); + module.SetConstant("AF_OSI", IronRuby.StandardLibrary.Sockets.RubySocket.AF_OSI); + module.SetConstant("AF_PUP", IronRuby.StandardLibrary.Sockets.RubySocket.AF_PUP); + module.SetConstant("AF_SNA", IronRuby.StandardLibrary.Sockets.RubySocket.AF_SNA); + module.SetConstant("AF_UNIX", IronRuby.StandardLibrary.Sockets.RubySocket.AF_UNIX); + module.SetConstant("AF_UNSPEC", IronRuby.StandardLibrary.Sockets.RubySocket.AF_UNSPEC); + module.SetConstant("AI_ADDRCONFIG", IronRuby.StandardLibrary.Sockets.RubySocket.AI_ADDRCONFIG); + module.SetConstant("AI_ALL", IronRuby.StandardLibrary.Sockets.RubySocket.AI_ALL); + module.SetConstant("AI_CANONNAME", IronRuby.StandardLibrary.Sockets.RubySocket.AI_CANONNAME); + module.SetConstant("AI_DEFAULT", IronRuby.StandardLibrary.Sockets.RubySocket.AI_DEFAULT); + module.SetConstant("AI_MASK", IronRuby.StandardLibrary.Sockets.RubySocket.AI_MASK); + module.SetConstant("AI_NUMERICHOST", IronRuby.StandardLibrary.Sockets.RubySocket.AI_NUMERICHOST); + module.SetConstant("AI_PASSIVE", IronRuby.StandardLibrary.Sockets.RubySocket.AI_PASSIVE); + module.SetConstant("AI_V4MAPPED", IronRuby.StandardLibrary.Sockets.RubySocket.AI_V4MAPPED); + module.SetConstant("AI_V4MAPPED_CFG", IronRuby.StandardLibrary.Sockets.RubySocket.AI_V4MAPPED_CFG); + module.SetConstant("EAI_ADDRFAMILY", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_ADDRFAMILY); + module.SetConstant("EAI_AGAIN", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_AGAIN); + module.SetConstant("EAI_BADFLAGS", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_BADFLAGS); + module.SetConstant("EAI_BADHINTS", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_BADHINTS); + module.SetConstant("EAI_FAIL", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_FAIL); + module.SetConstant("EAI_FAMILY", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_FAMILY); + module.SetConstant("EAI_MAX", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_MAX); + module.SetConstant("EAI_MEMORY", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_MEMORY); + module.SetConstant("EAI_NODATA", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_NODATA); + module.SetConstant("EAI_NONAME", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_NONAME); + module.SetConstant("EAI_PROTOCOL", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_PROTOCOL); + module.SetConstant("EAI_SERVICE", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_SERVICE); + module.SetConstant("EAI_SOCKTYPE", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_SOCKTYPE); + module.SetConstant("EAI_SYSTEM", IronRuby.StandardLibrary.Sockets.RubySocket.EAI_SYSTEM); + module.SetConstant("INADDR_ALLHOSTS_GROUP", IronRuby.StandardLibrary.Sockets.RubySocket.INADDR_ALLHOSTS_GROUP); + module.SetConstant("INADDR_ANY", IronRuby.StandardLibrary.Sockets.RubySocket.INADDR_ANY); + module.SetConstant("INADDR_BROADCAST", IronRuby.StandardLibrary.Sockets.RubySocket.INADDR_BROADCAST); + module.SetConstant("INADDR_LOOPBACK", IronRuby.StandardLibrary.Sockets.RubySocket.INADDR_LOOPBACK); + module.SetConstant("INADDR_MAX_LOCAL_GROUP", IronRuby.StandardLibrary.Sockets.RubySocket.INADDR_MAX_LOCAL_GROUP); + module.SetConstant("INADDR_NONE", IronRuby.StandardLibrary.Sockets.RubySocket.INADDR_NONE); + module.SetConstant("INADDR_UNSPEC_GROUP", IronRuby.StandardLibrary.Sockets.RubySocket.INADDR_UNSPEC_GROUP); + module.SetConstant("IPPORT_RESERVED", IronRuby.StandardLibrary.Sockets.RubySocket.IPPORT_RESERVED); + module.SetConstant("IPPORT_USERRESERVED", IronRuby.StandardLibrary.Sockets.RubySocket.IPPORT_USERRESERVED); + module.SetConstant("IPPROTO_GGP", IronRuby.StandardLibrary.Sockets.RubySocket.IPPROTO_GGP); + module.SetConstant("IPPROTO_ICMP", IronRuby.StandardLibrary.Sockets.RubySocket.IPPROTO_ICMP); + module.SetConstant("IPPROTO_IDP", IronRuby.StandardLibrary.Sockets.RubySocket.IPPROTO_IDP); + module.SetConstant("IPPROTO_IGMP", IronRuby.StandardLibrary.Sockets.RubySocket.IPPROTO_IGMP); + module.SetConstant("IPPROTO_IP", IronRuby.StandardLibrary.Sockets.RubySocket.IPPROTO_IP); + module.SetConstant("IPPROTO_MAX", IronRuby.StandardLibrary.Sockets.RubySocket.IPPROTO_MAX); + module.SetConstant("IPPROTO_ND", IronRuby.StandardLibrary.Sockets.RubySocket.IPPROTO_ND); + module.SetConstant("IPPROTO_PUP", IronRuby.StandardLibrary.Sockets.RubySocket.IPPROTO_PUP); + module.SetConstant("IPPROTO_RAW", IronRuby.StandardLibrary.Sockets.RubySocket.IPPROTO_RAW); + module.SetConstant("IPPROTO_TCP", IronRuby.StandardLibrary.Sockets.RubySocket.IPPROTO_TCP); + module.SetConstant("IPPROTO_UDP", IronRuby.StandardLibrary.Sockets.RubySocket.IPPROTO_UDP); + module.SetConstant("MSG_DONTROUTE", IronRuby.StandardLibrary.Sockets.RubySocket.MSG_DONTROUTE); + module.SetConstant("MSG_OOB", IronRuby.StandardLibrary.Sockets.RubySocket.MSG_OOB); + module.SetConstant("MSG_PEEK", IronRuby.StandardLibrary.Sockets.RubySocket.MSG_PEEK); + module.SetConstant("NI_DGRAM", IronRuby.StandardLibrary.Sockets.RubySocket.NI_DGRAM); + module.SetConstant("NI_MAXHOST", IronRuby.StandardLibrary.Sockets.RubySocket.NI_MAXHOST); + module.SetConstant("NI_MAXSERV", IronRuby.StandardLibrary.Sockets.RubySocket.NI_MAXSERV); + module.SetConstant("NI_NAMEREQD", IronRuby.StandardLibrary.Sockets.RubySocket.NI_NAMEREQD); + module.SetConstant("NI_NOFQDN", IronRuby.StandardLibrary.Sockets.RubySocket.NI_NOFQDN); + module.SetConstant("NI_NUMERICHOST", IronRuby.StandardLibrary.Sockets.RubySocket.NI_NUMERICHOST); + module.SetConstant("NI_NUMERICSERV", IronRuby.StandardLibrary.Sockets.RubySocket.NI_NUMERICSERV); + module.SetConstant("PF_APPLETALK", IronRuby.StandardLibrary.Sockets.RubySocket.PF_APPLETALK); + module.SetConstant("PF_ATM", IronRuby.StandardLibrary.Sockets.RubySocket.PF_ATM); + module.SetConstant("PF_CCITT", IronRuby.StandardLibrary.Sockets.RubySocket.PF_CCITT); + module.SetConstant("PF_CHAOS", IronRuby.StandardLibrary.Sockets.RubySocket.PF_CHAOS); + module.SetConstant("PF_DATAKIT", IronRuby.StandardLibrary.Sockets.RubySocket.PF_DATAKIT); + module.SetConstant("PF_DLI", IronRuby.StandardLibrary.Sockets.RubySocket.PF_DLI); + module.SetConstant("PF_ECMA", IronRuby.StandardLibrary.Sockets.RubySocket.PF_ECMA); + module.SetConstant("PF_HYLINK", IronRuby.StandardLibrary.Sockets.RubySocket.PF_HYLINK); + module.SetConstant("PF_IMPLINK", IronRuby.StandardLibrary.Sockets.RubySocket.PF_IMPLINK); + module.SetConstant("PF_INET", IronRuby.StandardLibrary.Sockets.RubySocket.PF_INET); + module.SetConstant("PF_IPX", IronRuby.StandardLibrary.Sockets.RubySocket.PF_IPX); + module.SetConstant("PF_ISO", IronRuby.StandardLibrary.Sockets.RubySocket.PF_ISO); + module.SetConstant("PF_LAT", IronRuby.StandardLibrary.Sockets.RubySocket.PF_LAT); + module.SetConstant("PF_MAX", IronRuby.StandardLibrary.Sockets.RubySocket.PF_MAX); + module.SetConstant("PF_NS", IronRuby.StandardLibrary.Sockets.RubySocket.PF_NS); + module.SetConstant("PF_OSI", IronRuby.StandardLibrary.Sockets.RubySocket.PF_OSI); + module.SetConstant("PF_PUP", IronRuby.StandardLibrary.Sockets.RubySocket.PF_PUP); + module.SetConstant("PF_SNA", IronRuby.StandardLibrary.Sockets.RubySocket.PF_SNA); + module.SetConstant("PF_UNIX", IronRuby.StandardLibrary.Sockets.RubySocket.PF_UNIX); + module.SetConstant("PF_UNSPEC", IronRuby.StandardLibrary.Sockets.RubySocket.PF_UNSPEC); + module.SetConstant("SHUT_RD", IronRuby.StandardLibrary.Sockets.RubySocket.SHUT_RD); + module.SetConstant("SHUT_RDWR", IronRuby.StandardLibrary.Sockets.RubySocket.SHUT_RDWR); + module.SetConstant("SHUT_WR", IronRuby.StandardLibrary.Sockets.RubySocket.SHUT_WR); + module.SetConstant("SO_ACCEPTCONN", IronRuby.StandardLibrary.Sockets.RubySocket.SO_ACCEPTCONN); + module.SetConstant("SO_BROADCAST", IronRuby.StandardLibrary.Sockets.RubySocket.SO_BROADCAST); + module.SetConstant("SO_DEBUG", IronRuby.StandardLibrary.Sockets.RubySocket.SO_DEBUG); + module.SetConstant("SO_DONTROUTE", IronRuby.StandardLibrary.Sockets.RubySocket.SO_DONTROUTE); + module.SetConstant("SO_ERROR", IronRuby.StandardLibrary.Sockets.RubySocket.SO_ERROR); + module.SetConstant("SO_KEEPALIVE", IronRuby.StandardLibrary.Sockets.RubySocket.SO_KEEPALIVE); + module.SetConstant("SO_LINGER", IronRuby.StandardLibrary.Sockets.RubySocket.SO_LINGER); + module.SetConstant("SO_OOBINLINE", IronRuby.StandardLibrary.Sockets.RubySocket.SO_OOBINLINE); + module.SetConstant("SO_RCVBUF", IronRuby.StandardLibrary.Sockets.RubySocket.SO_RCVBUF); + module.SetConstant("SO_RCVLOWAT", IronRuby.StandardLibrary.Sockets.RubySocket.SO_RCVLOWAT); + module.SetConstant("SO_RCVTIMEO", IronRuby.StandardLibrary.Sockets.RubySocket.SO_RCVTIMEO); + module.SetConstant("SO_REUSEADDR", IronRuby.StandardLibrary.Sockets.RubySocket.SO_REUSEADDR); + module.SetConstant("SO_SNDBUF", IronRuby.StandardLibrary.Sockets.RubySocket.SO_SNDBUF); + module.SetConstant("SO_SNDLOWAT", IronRuby.StandardLibrary.Sockets.RubySocket.SO_SNDLOWAT); + module.SetConstant("SO_SNDTIMEO", IronRuby.StandardLibrary.Sockets.RubySocket.SO_SNDTIMEO); + module.SetConstant("SO_TYPE", IronRuby.StandardLibrary.Sockets.RubySocket.SO_TYPE); + module.SetConstant("SO_USELOOPBACK", IronRuby.StandardLibrary.Sockets.RubySocket.SO_USELOOPBACK); + module.SetConstant("SOCK_DGRAM", IronRuby.StandardLibrary.Sockets.RubySocket.SOCK_DGRAM); + module.SetConstant("SOCK_RAW", IronRuby.StandardLibrary.Sockets.RubySocket.SOCK_RAW); + module.SetConstant("SOCK_RDM", IronRuby.StandardLibrary.Sockets.RubySocket.SOCK_RDM); + module.SetConstant("SOCK_SEQPACKET", IronRuby.StandardLibrary.Sockets.RubySocket.SOCK_SEQPACKET); + module.SetConstant("SOCK_STREAM", IronRuby.StandardLibrary.Sockets.RubySocket.SOCK_STREAM); + module.SetConstant("SOL_SOCKET", IronRuby.StandardLibrary.Sockets.RubySocket.SOL_SOCKET); + module.SetConstant("TCP_NODELAY", IronRuby.StandardLibrary.Sockets.RubySocket.TCP_NODELAY); + + module.DefineLibraryMethod("accept", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.Accept), + }); + + module.DefineLibraryMethod("accept_nonblock", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.AcceptNonBlocking), + }); + + module.DefineLibraryMethod("bind", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.Bind), + }); + + module.DefineLibraryMethod("connect", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.Connect), + }); + + module.DefineLibraryMethod("connect_nonblock", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.ConnectNonBlocking), + }); + + module.DefineLibraryMethod("listen", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.Listen), + }); + + module.DefineLibraryMethod("recvfrom", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.ReceiveFrom), + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.ReceiveFrom), + }); + + module.DefineLibraryMethod("sysaccept", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.SysAccept), + }); + + } + #endif + + #if !SILVERLIGHT + private void LoadSocket_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("getaddrinfo", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.GetAddressInfo), + }); + + module.DefineLibraryMethod("gethostbyaddr", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.GetHostByAddress), + }); + + module.DefineLibraryMethod("gethostbyname", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.GetHostByName), + }); + + module.DefineLibraryMethod("gethostname", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.GetHostname), + }); + + module.DefineLibraryMethod("getnameinfo", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.GetNameInfo), + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.GetNameInfo), + }); + + module.DefineLibraryMethod("getservbyname", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.GetServiceByName), + }); + + module.DefineLibraryMethod("pack_sockaddr_in", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.PackInetSockAddr), + }); + + module.DefineLibraryMethod("pair", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.CreateSocketPair), + }); + + module.DefineLibraryMethod("sockaddr_in", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.PackInetSockAddr), + }); + + module.DefineLibraryMethod("socketpair", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.CreateSocketPair), + }); + + module.DefineLibraryMethod("unpack_sockaddr_in", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.RubySocket.UnPackInetSockAddr), + }); + + } + #endif + + #if !SILVERLIGHT && !SILVERLIGHT + private void LoadSocket__Constants_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("AF_APPLETALK", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_APPLETALK); + module.SetConstant("AF_ATM", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_ATM); + module.SetConstant("AF_CCITT", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_CCITT); + module.SetConstant("AF_CHAOS", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_CHAOS); + module.SetConstant("AF_DATAKIT", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_DATAKIT); + module.SetConstant("AF_DLI", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_DLI); + module.SetConstant("AF_ECMA", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_ECMA); + module.SetConstant("AF_HYLINK", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_HYLINK); + module.SetConstant("AF_IMPLINK", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_IMPLINK); + module.SetConstant("AF_INET", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_INET); + module.SetConstant("AF_IPX", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_IPX); + module.SetConstant("AF_ISO", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_ISO); + module.SetConstant("AF_LAT", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_LAT); + module.SetConstant("AF_MAX", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_MAX); + module.SetConstant("AF_NETBIOS", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_NETBIOS); + module.SetConstant("AF_NS", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_NS); + module.SetConstant("AF_OSI", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_OSI); + module.SetConstant("AF_PUP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_PUP); + module.SetConstant("AF_SNA", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_SNA); + module.SetConstant("AF_UNIX", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_UNIX); + module.SetConstant("AF_UNSPEC", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AF_UNSPEC); + module.SetConstant("AI_ADDRCONFIG", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AI_ADDRCONFIG); + module.SetConstant("AI_ALL", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AI_ALL); + module.SetConstant("AI_CANONNAME", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AI_CANONNAME); + module.SetConstant("AI_DEFAULT", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AI_DEFAULT); + module.SetConstant("AI_MASK", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AI_MASK); + module.SetConstant("AI_NUMERICHOST", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AI_NUMERICHOST); + module.SetConstant("AI_PASSIVE", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AI_PASSIVE); + module.SetConstant("AI_V4MAPPED", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AI_V4MAPPED); + module.SetConstant("AI_V4MAPPED_CFG", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.AI_V4MAPPED_CFG); + module.SetConstant("EAI_ADDRFAMILY", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_ADDRFAMILY); + module.SetConstant("EAI_AGAIN", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_AGAIN); + module.SetConstant("EAI_BADFLAGS", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_BADFLAGS); + module.SetConstant("EAI_BADHINTS", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_BADHINTS); + module.SetConstant("EAI_FAIL", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_FAIL); + module.SetConstant("EAI_FAMILY", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_FAMILY); + module.SetConstant("EAI_MAX", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_MAX); + module.SetConstant("EAI_MEMORY", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_MEMORY); + module.SetConstant("EAI_NODATA", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_NODATA); + module.SetConstant("EAI_NONAME", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_NONAME); + module.SetConstant("EAI_PROTOCOL", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_PROTOCOL); + module.SetConstant("EAI_SERVICE", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_SERVICE); + module.SetConstant("EAI_SOCKTYPE", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_SOCKTYPE); + module.SetConstant("EAI_SYSTEM", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.EAI_SYSTEM); + module.SetConstant("INADDR_ALLHOSTS_GROUP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.INADDR_ALLHOSTS_GROUP); + module.SetConstant("INADDR_ANY", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.INADDR_ANY); + module.SetConstant("INADDR_BROADCAST", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.INADDR_BROADCAST); + module.SetConstant("INADDR_LOOPBACK", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.INADDR_LOOPBACK); + module.SetConstant("INADDR_MAX_LOCAL_GROUP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.INADDR_MAX_LOCAL_GROUP); + module.SetConstant("INADDR_NONE", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.INADDR_NONE); + module.SetConstant("INADDR_UNSPEC_GROUP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.INADDR_UNSPEC_GROUP); + module.SetConstant("IPPORT_RESERVED", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPORT_RESERVED); + module.SetConstant("IPPORT_USERRESERVED", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPORT_USERRESERVED); + module.SetConstant("IPPROTO_GGP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPROTO_GGP); + module.SetConstant("IPPROTO_ICMP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPROTO_ICMP); + module.SetConstant("IPPROTO_IDP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPROTO_IDP); + module.SetConstant("IPPROTO_IGMP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPROTO_IGMP); + module.SetConstant("IPPROTO_IP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPROTO_IP); + module.SetConstant("IPPROTO_MAX", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPROTO_MAX); + module.SetConstant("IPPROTO_ND", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPROTO_ND); + module.SetConstant("IPPROTO_PUP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPROTO_PUP); + module.SetConstant("IPPROTO_RAW", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPROTO_RAW); + module.SetConstant("IPPROTO_TCP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPROTO_TCP); + module.SetConstant("IPPROTO_UDP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.IPPROTO_UDP); + module.SetConstant("MSG_DONTROUTE", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.MSG_DONTROUTE); + module.SetConstant("MSG_OOB", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.MSG_OOB); + module.SetConstant("MSG_PEEK", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.MSG_PEEK); + module.SetConstant("NI_DGRAM", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.NI_DGRAM); + module.SetConstant("NI_MAXHOST", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.NI_MAXHOST); + module.SetConstant("NI_MAXSERV", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.NI_MAXSERV); + module.SetConstant("NI_NAMEREQD", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.NI_NAMEREQD); + module.SetConstant("NI_NOFQDN", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.NI_NOFQDN); + module.SetConstant("NI_NUMERICHOST", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.NI_NUMERICHOST); + module.SetConstant("NI_NUMERICSERV", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.NI_NUMERICSERV); + module.SetConstant("PF_APPLETALK", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_APPLETALK); + module.SetConstant("PF_ATM", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_ATM); + module.SetConstant("PF_CCITT", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_CCITT); + module.SetConstant("PF_CHAOS", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_CHAOS); + module.SetConstant("PF_DATAKIT", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_DATAKIT); + module.SetConstant("PF_DLI", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_DLI); + module.SetConstant("PF_ECMA", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_ECMA); + module.SetConstant("PF_HYLINK", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_HYLINK); + module.SetConstant("PF_IMPLINK", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_IMPLINK); + module.SetConstant("PF_INET", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_INET); + module.SetConstant("PF_IPX", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_IPX); + module.SetConstant("PF_ISO", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_ISO); + module.SetConstant("PF_LAT", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_LAT); + module.SetConstant("PF_MAX", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_MAX); + module.SetConstant("PF_NS", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_NS); + module.SetConstant("PF_OSI", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_OSI); + module.SetConstant("PF_PUP", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_PUP); + module.SetConstant("PF_SNA", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_SNA); + module.SetConstant("PF_UNIX", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_UNIX); + module.SetConstant("PF_UNSPEC", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.PF_UNSPEC); + module.SetConstant("SHUT_RD", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SHUT_RD); + module.SetConstant("SHUT_RDWR", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SHUT_RDWR); + module.SetConstant("SHUT_WR", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SHUT_WR); + module.SetConstant("SO_ACCEPTCONN", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_ACCEPTCONN); + module.SetConstant("SO_BROADCAST", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_BROADCAST); + module.SetConstant("SO_DEBUG", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_DEBUG); + module.SetConstant("SO_DONTROUTE", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_DONTROUTE); + module.SetConstant("SO_ERROR", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_ERROR); + module.SetConstant("SO_KEEPALIVE", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_KEEPALIVE); + module.SetConstant("SO_LINGER", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_LINGER); + module.SetConstant("SO_OOBINLINE", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_OOBINLINE); + module.SetConstant("SO_RCVBUF", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_RCVBUF); + module.SetConstant("SO_RCVLOWAT", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_RCVLOWAT); + module.SetConstant("SO_RCVTIMEO", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_RCVTIMEO); + module.SetConstant("SO_REUSEADDR", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_REUSEADDR); + module.SetConstant("SO_SNDBUF", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_SNDBUF); + module.SetConstant("SO_SNDLOWAT", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_SNDLOWAT); + module.SetConstant("SO_SNDTIMEO", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_SNDTIMEO); + module.SetConstant("SO_TYPE", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_TYPE); + module.SetConstant("SO_USELOOPBACK", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SO_USELOOPBACK); + module.SetConstant("SOCK_DGRAM", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SOCK_DGRAM); + module.SetConstant("SOCK_RAW", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SOCK_RAW); + module.SetConstant("SOCK_RDM", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SOCK_RDM); + module.SetConstant("SOCK_SEQPACKET", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SOCK_SEQPACKET); + module.SetConstant("SOCK_STREAM", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SOCK_STREAM); + module.SetConstant("SOL_SOCKET", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.SOL_SOCKET); + module.SetConstant("TCP_NODELAY", IronRuby.StandardLibrary.Sockets.RubySocket.SocketConstants.TCP_NODELAY); + + } + #endif + + #if !SILVERLIGHT + private void LoadSocketError_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.HideMethod("message"); + } + #endif + + #if !SILVERLIGHT + private void LoadTCPServer_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("accept", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.TCPServer.Accept), + }); + + module.DefineLibraryMethod("accept_nonblock", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.TCPServer.AcceptNonBlocking), + }); + + module.DefineLibraryMethod("listen", 0x11, new System.Delegate[] { + new System.Action(IronRuby.StandardLibrary.Sockets.TCPServer.Listen), + }); + + module.DefineLibraryMethod("sysaccept", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.TCPServer.SysAccept), + }); + + } + #endif + + #if !SILVERLIGHT + private void LoadTCPSocket_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("gethostbyname", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.TCPSocket.GetHostByName), + }); + + } + #endif + + #if !SILVERLIGHT + private void LoadUDPSocket_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("bind", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.UDPSocket.Bind), + }); + + module.DefineLibraryMethod("connect", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.UDPSocket.Connect), + }); + + module.DefineLibraryMethod("recvfrom_nonblock", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.UDPSocket.ReceiveFromNonBlocking), + new System.Func(IronRuby.StandardLibrary.Sockets.UDPSocket.ReceiveFromNonBlocking), + }); + + module.DefineLibraryMethod("send", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Sockets.UDPSocket.Send), + new System.Func(IronRuby.StandardLibrary.Sockets.UDPSocket.Send), + new System.Func(IronRuby.StandardLibrary.Sockets.UDPSocket.Send), + }); + + } + #endif + + } +} + +namespace IronRuby.StandardLibrary.OpenSsl { + public sealed class OpenSslLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + IronRuby.Builtins.RubyClass classRef0 = GetClass(typeof(System.Object)); + + + IronRuby.Builtins.RubyModule def1 = DefineGlobalModule("OpenSSL", typeof(IronRuby.StandardLibrary.OpenSsl.OpenSsl), new System.Action(LoadOpenSSL_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + IronRuby.Builtins.RubyClass def2 = DefineClass("OpenSSL::BN", typeof(IronRuby.StandardLibrary.OpenSsl.OpenSsl.BN), classRef0, null, new System.Action(LoadOpenSSL__BN_Class), IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyModule def3 = DefineModule("OpenSSL::Digest", typeof(IronRuby.StandardLibrary.OpenSsl.OpenSsl.DigestFactory), null, null, IronRuby.Builtins.RubyModule.EmptyArray); + IronRuby.Builtins.RubyClass def4 = DefineClass("OpenSSL::Digest::Digest", typeof(IronRuby.StandardLibrary.OpenSsl.OpenSsl.DigestFactory.Digest), classRef0, new System.Action(LoadOpenSSL__Digest__Digest_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.OpenSsl.OpenSsl.DigestFactory.Digest.CreateDigest), + }); + IronRuby.Builtins.RubyClass def5 = DefineClass("OpenSSL::HMAC", typeof(IronRuby.StandardLibrary.OpenSsl.OpenSsl.HMAC), classRef0, null, new System.Action(LoadOpenSSL__HMAC_Class), IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyModule def6 = DefineModule("OpenSSL::Random", typeof(IronRuby.StandardLibrary.OpenSsl.OpenSsl.RandomModule), null, new System.Action(LoadOpenSSL__Random_Class), IronRuby.Builtins.RubyModule.EmptyArray); + def1.SetConstant("BN", def2); + def1.SetConstant("Digest", def3); + def3.SetConstant("Digest", def4); + def1.SetConstant("HMAC", def5); + def1.SetConstant("Random", def6); + } + + private void LoadOpenSSL_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("OPENSSL_VERSION", IronRuby.StandardLibrary.OpenSsl.OpenSsl.OPENSSL_VERSION); + module.SetConstant("OPENSSL_VERSION_NUMBER", IronRuby.StandardLibrary.OpenSsl.OpenSsl.OPENSSL_VERSION_NUMBER); + module.SetConstant("VERSION", IronRuby.StandardLibrary.OpenSsl.OpenSsl.VERSION); + + } + + private void LoadOpenSSL__BN_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("rand", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.OpenSsl.OpenSsl.BN.Rand), + }); + + } + + private void LoadOpenSSL__Digest__Digest_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("initialize", 0x12, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.OpenSsl.OpenSsl.DigestFactory.Digest.Initialize), + }); + + } + + private void LoadOpenSSL__HMAC_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("hexdigest", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.OpenSsl.OpenSsl.HMAC.HexDigest), + }); + + } + + private void LoadOpenSSL__Random_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("seed", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.OpenSsl.OpenSsl.RandomModule.Seed), + }); + + } + + } +} + +namespace IronRuby.StandardLibrary.Digest { + public sealed class DigestLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + IronRuby.Builtins.RubyClass classRef0 = GetClass(typeof(System.Object)); + + + IronRuby.Builtins.RubyModule def1 = DefineGlobalModule("Digest", typeof(IronRuby.StandardLibrary.Digest.Digest), null, new System.Action(LoadDigest_Class), IronRuby.Builtins.RubyModule.EmptyArray); + IronRuby.Builtins.RubyModule def4 = DefineModule("Digest::Instance", typeof(IronRuby.StandardLibrary.Digest.Digest.Instance), new System.Action(LoadDigest__Instance_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + IronRuby.Builtins.RubyClass def3 = DefineClass("Digest::Class", typeof(IronRuby.StandardLibrary.Digest.Digest.Class), classRef0, null, new System.Action(LoadDigest__Class_Class), new IronRuby.Builtins.RubyModule[] {def4, }, null); + IronRuby.Builtins.RubyClass def2 = DefineClass("Digest::Base", typeof(IronRuby.StandardLibrary.Digest.Digest.Base), def3, new System.Action(LoadDigest__Base_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + #if !SILVERLIGHT + IronRuby.Builtins.RubyClass def5 = DefineClass("Digest::MD5", typeof(IronRuby.StandardLibrary.Digest.Digest.MD5), def2, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + #endif + #if !SILVERLIGHT + IronRuby.Builtins.RubyClass def6 = DefineClass("Digest::SHA1", typeof(IronRuby.StandardLibrary.Digest.Digest.SHA1), def2, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + #endif + #if !SILVERLIGHT + IronRuby.Builtins.RubyClass def7 = DefineClass("Digest::SHA256", typeof(IronRuby.StandardLibrary.Digest.Digest.SHA256), def2, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + #endif + #if !SILVERLIGHT + IronRuby.Builtins.RubyClass def8 = DefineClass("Digest::SHA384", typeof(IronRuby.StandardLibrary.Digest.Digest.SHA384), def2, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + #endif + #if !SILVERLIGHT + IronRuby.Builtins.RubyClass def9 = DefineClass("Digest::SHA512", typeof(IronRuby.StandardLibrary.Digest.Digest.SHA512), def2, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + #endif + def1.SetConstant("Instance", def4); + def1.SetConstant("Class", def3); + def1.SetConstant("Base", def2); + #if !SILVERLIGHT + def1.SetConstant("MD5", def5); + #endif + #if !SILVERLIGHT + def1.SetConstant("SHA1", def6); + #endif + #if !SILVERLIGHT + def1.SetConstant("SHA256", def7); + #endif + #if !SILVERLIGHT + def1.SetConstant("SHA384", def8); + #endif + #if !SILVERLIGHT + def1.SetConstant("SHA512", def9); + #endif + } + + private void LoadDigest_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("const_missing", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Digest.Digest.ConstantMissing), + }); + + module.DefineLibraryMethod("hexencode", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Digest.Digest.HexEncode), + }); + + } + + private void LoadDigest__Base_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("<<", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Base.Update), + }); + + module.DefineLibraryMethod("finish", 0x12, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Base.Finish), + }); + + module.DefineLibraryMethod("reset", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Base.Reset), + }); + + module.DefineLibraryMethod("update", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Base.Update), + }); + + } + + private void LoadDigest__Class_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("digest", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Class.Digest), + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Class.Digest), + }); + + module.DefineLibraryMethod("hexdigest", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Class.HexDigest), + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Class.HexDigest), + }); + + } + + private void LoadDigest__Instance_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("digest", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Instance.Digest), + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Instance.Digest), + }); + + module.DefineLibraryMethod("digest!", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Instance.DigestNew), + }); + + module.DefineLibraryMethod("hexdigest", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Instance.HexDigest), + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Instance.HexDigest), + }); + + module.DefineLibraryMethod("hexdigest!", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Digest.Digest.Instance.HexDigestNew), + }); + + } + + } +} + +namespace IronRuby.StandardLibrary.Zlib { + public sealed class ZlibLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + IronRuby.Builtins.RubyClass classRef0 = GetClass(typeof(IronRuby.Builtins.RuntimeError)); + IronRuby.Builtins.RubyClass classRef1 = GetClass(typeof(System.Object)); + + + IronRuby.Builtins.RubyModule def1 = DefineGlobalModule("Zlib", typeof(IronRuby.StandardLibrary.Zlib.Zlib), new System.Action(LoadZlib_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + IronRuby.Builtins.RubyClass def2 = DefineClass("Zlib::DataError", typeof(IronRuby.StandardLibrary.Zlib.Zlib.DataError), classRef0, null, null, IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new System.Func(ZlibLibraryInitializer.ExceptionFactory__Zlib__DataError) }); + IronRuby.Builtins.RubyClass def3 = DefineClass("Zlib::GzipFile", typeof(IronRuby.StandardLibrary.Zlib.Zlib.GZipFile), classRef1, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def4 = DefineClass("Zlib::GzipFile::Error", typeof(IronRuby.StandardLibrary.Zlib.Zlib.GZipFile.Error), classRef0, null, null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def7 = DefineClass("Zlib::ZStream", typeof(IronRuby.StandardLibrary.Zlib.Zlib.ZStream), classRef1, new System.Action(LoadZlib__ZStream_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray, null); + IronRuby.Builtins.RubyClass def5 = DefineClass("Zlib::GzipReader", typeof(IronRuby.StandardLibrary.Zlib.Zlib.GZipReader), def3, new System.Action(LoadZlib__GzipReader_Instance), new System.Action(LoadZlib__GzipReader_Class), IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.GZipReader.Create), + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.GZipReader.Create), + }); + IronRuby.Builtins.RubyClass def6 = DefineClass("Zlib::Inflate", typeof(IronRuby.StandardLibrary.Zlib.Zlib.Inflate), def7, new System.Action(LoadZlib__Inflate_Instance), new System.Action(LoadZlib__Inflate_Class), IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.Inflate.Create), + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.Inflate.Create), + }); + def1.SetConstant("DataError", def2); + def1.SetConstant("GzipFile", def3); + def3.SetConstant("Error", def4); + def1.SetConstant("ZStream", def7); + def1.SetConstant("GzipReader", def5); + def1.SetConstant("Inflate", def6); + } + + private void LoadZlib_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("FIXLCODES", IronRuby.StandardLibrary.Zlib.Zlib.FIXLCODES); + module.SetConstant("MAX_WBITS", IronRuby.StandardLibrary.Zlib.Zlib.MAX_WBITS); + module.SetConstant("MAXBITS", IronRuby.StandardLibrary.Zlib.Zlib.MAXBITS); + module.SetConstant("MAXCODES", IronRuby.StandardLibrary.Zlib.Zlib.MAXCODES); + module.SetConstant("MAXDCODES", IronRuby.StandardLibrary.Zlib.Zlib.MAXDCODES); + module.SetConstant("MAXLCODES", IronRuby.StandardLibrary.Zlib.Zlib.MAXLCODES); + module.SetConstant("VERSION", IronRuby.StandardLibrary.Zlib.Zlib.VERSION); + module.SetConstant("Z_DEFLATED", IronRuby.StandardLibrary.Zlib.Zlib.Z_DEFLATED); + module.SetConstant("ZLIB_VERSION", IronRuby.StandardLibrary.Zlib.Zlib.ZLIB_VERSION); + + } + + private void LoadZlib__GzipReader_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("OSES", IronRuby.StandardLibrary.Zlib.Zlib.GZipReader.OSES); + + module.DefineLibraryMethod("close", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.GZipReader.Close), + }); + + module.DefineLibraryMethod("comment", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.GZipReader.Comment), + }); + + module.DefineLibraryMethod("open", 0x12, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.GZipReader.Open), + }); + + module.DefineLibraryMethod("original_name", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.GZipReader.OriginalName), + }); + + module.DefineLibraryMethod("read", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.GZipReader.Read), + }); + + module.DefineLibraryMethod("xtra_field", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.GZipReader.ExtraField), + }); + + } + + private void LoadZlib__GzipReader_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("open", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.GZipReader.Open), + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.GZipReader.Open), + }); + + } + + private void LoadZlib__Inflate_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("close", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.Inflate.Close), + }); + + module.DefineLibraryMethod("inflate", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.Inflate.InflateStream), + }); + + } + + private void LoadZlib__Inflate_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("inflate", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.Inflate.InflateStream), + }); + + } + + private void LoadZlib__ZStream_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("adler", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.Adler), + }); + + module.DefineLibraryMethod("avail_in", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.AvailIn), + }); + + module.DefineLibraryMethod("avail_out", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.GetAvailOut), + }); + + module.DefineLibraryMethod("avail_out=", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.SetAvailOut), + }); + + module.DefineLibraryMethod("close", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.Close), + }); + + module.DefineLibraryMethod("closed?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.IsClosed), + }); + + module.DefineLibraryMethod("data_type", 0x11, new System.Delegate[] { + new System.Action(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.DataType), + }); + + module.DefineLibraryMethod("finish", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.Close), + }); + + module.DefineLibraryMethod("finished?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.IsClosed), + }); + + module.DefineLibraryMethod("flush_next_in", 0x11, new System.Delegate[] { + new System.Func>(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.FlushNextIn), + }); + + module.DefineLibraryMethod("flush_next_out", 0x11, new System.Delegate[] { + new System.Func>(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.FlushNextOut), + }); + + module.DefineLibraryMethod("reset", 0x11, new System.Delegate[] { + new System.Action(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.Reset), + }); + + module.DefineLibraryMethod("stream_end?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.IsClosed), + }); + + module.DefineLibraryMethod("total_in", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.TotalIn), + }); + + module.DefineLibraryMethod("total_out", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Zlib.Zlib.ZStream.TotalOut), + }); + + } + + public static System.Exception/*!*/ ExceptionFactory__Zlib__DataError(IronRuby.Builtins.RubyClass/*!*/ self, [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]object message) { + return IronRuby.Runtime.RubyExceptionData.InitializeException(new IronRuby.StandardLibrary.Zlib.Zlib.DataError(IronRuby.Runtime.RubyExceptionData.GetClrMessage(self, message), (System.Exception)null), message); + } + + } +} + +namespace IronRuby.StandardLibrary.StringIO { + public sealed class StringIOLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + IronRuby.Builtins.RubyClass classRef0 = GetClass(typeof(IronRuby.Builtins.RubyIO)); + + + DefineGlobalClass("StringIO", typeof(IronRuby.StandardLibrary.StringIO.StringIO), classRef0, new System.Action(LoadStringIO_Instance), new System.Action(LoadStringIO_Class), IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringIO.StringIO.CreateIO), + }); + } + + private void LoadStringIO_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("length", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringIO.StringIO.GetLength), + }); + + module.DefineLibraryMethod("path", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringIO.StringIO.GetPath), + }); + + module.DefineLibraryMethod("size", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringIO.StringIO.GetLength), + }); + + module.DefineLibraryMethod("string", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringIO.StringIO.GetString), + }); + + module.DefineLibraryMethod("string=", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringIO.StringIO.SetString), + }); + + module.DefineLibraryMethod("truncate", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringIO.StringIO.SetLength), + }); + + } + + private void LoadStringIO_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("open", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringIO.StringIO.OpenIO), + }); + + } + + } +} + +namespace IronRuby.StandardLibrary.StringScanner { + public sealed class StringScannerLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + IronRuby.Builtins.RubyClass classRef0 = GetClass(typeof(IronRuby.Builtins.RubyObject)); + + + DefineGlobalClass("StringScanner", typeof(IronRuby.StandardLibrary.StringScanner.StringScanner), classRef0, new System.Action(LoadStringScanner_Instance), new System.Action(LoadStringScanner_Class), IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Create), + }); + } + + private void LoadStringScanner_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("[]", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.GetMatchSubgroup), + }); + + module.DefineLibraryMethod("<<", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Concat), + }); + + module.DefineLibraryMethod("beginning_of_line?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.BeginningOfLine), + }); + + module.DefineLibraryMethod("bol?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.BeginningOfLine), + }); + + module.DefineLibraryMethod("check", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Check), + }); + + module.DefineLibraryMethod("check_until", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.CheckUntil), + }); + + module.DefineLibraryMethod("clear", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Clear), + }); + + module.DefineLibraryMethod("concat", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Concat), + }); + + module.DefineLibraryMethod("empty?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.EndOfLine), + }); + + module.DefineLibraryMethod("eos?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.EndOfLine), + }); + + module.DefineLibraryMethod("exist?", 0x11, new System.Delegate[] { + new System.Func>(IronRuby.StandardLibrary.StringScanner.StringScanner.Exist), + }); + + module.DefineLibraryMethod("get_byte", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.GetByte), + }); + + module.DefineLibraryMethod("getbyte", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.GetByte), + }); + + module.DefineLibraryMethod("getch", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.GetChar), + }); + + module.DefineLibraryMethod("initialize", 0x12, new System.Delegate[] { + new System.Action(IronRuby.StandardLibrary.StringScanner.StringScanner.Reinitialize), + }); + + module.DefineLibraryMethod("initialize_copy", 0x12, new System.Delegate[] { + new System.Action(IronRuby.StandardLibrary.StringScanner.StringScanner.InitializeFrom), + }); + + module.DefineLibraryMethod("inspect", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.ToString), + }); + + module.DefineLibraryMethod("match?", 0x11, new System.Delegate[] { + new System.Func>(IronRuby.StandardLibrary.StringScanner.StringScanner.Match), + }); + + module.DefineLibraryMethod("matched", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Matched), + }); + + module.DefineLibraryMethod("matched?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.WasMatched), + }); + + module.DefineLibraryMethod("matched_size", 0x11, new System.Delegate[] { + new System.Func>(IronRuby.StandardLibrary.StringScanner.StringScanner.MatchedSize), + }); + + module.DefineLibraryMethod("matchedsize", 0x11, new System.Delegate[] { + new System.Func>(IronRuby.StandardLibrary.StringScanner.StringScanner.MatchedSize), + }); + + module.DefineLibraryMethod("peek", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Peek), + }); + + module.DefineLibraryMethod("peep", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Peek), + }); + + module.DefineLibraryMethod("pointer", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.GetCurrentPosition), + }); + + module.DefineLibraryMethod("pointer=", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.SetCurrentPosition), + }); + + module.DefineLibraryMethod("pos", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.GetCurrentPosition), + }); + + module.DefineLibraryMethod("pos=", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.SetCurrentPosition), + }); + + module.DefineLibraryMethod("post_match", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.PostMatch), + }); + + module.DefineLibraryMethod("pre_match", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.PreMatch), + }); + + module.DefineLibraryMethod("reset", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Reset), + }); + + module.DefineLibraryMethod("rest", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Rest), + }); + + module.DefineLibraryMethod("rest?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.IsRestLeft), + }); + + module.DefineLibraryMethod("rest_size", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.RestSize), + }); + + module.DefineLibraryMethod("restsize", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.RestSize), + }); + + module.DefineLibraryMethod("scan", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Scan), + }); + + module.DefineLibraryMethod("scan_full", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.ScanFull), + }); + + module.DefineLibraryMethod("scan_until", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.ScanUntil), + }); + + module.DefineLibraryMethod("search_full", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.SearchFull), + }); + + module.DefineLibraryMethod("skip", 0x11, new System.Delegate[] { + new System.Func>(IronRuby.StandardLibrary.StringScanner.StringScanner.Skip), + }); + + module.DefineLibraryMethod("skip_until", 0x11, new System.Delegate[] { + new System.Func>(IronRuby.StandardLibrary.StringScanner.StringScanner.SkipUntil), + }); + + module.DefineLibraryMethod("string", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.GetString), + }); + + module.DefineLibraryMethod("string=", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.SetString), + }); + + module.DefineLibraryMethod("terminate", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Clear), + }); + + module.DefineLibraryMethod("to_s", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.ToString), + }); + + module.DefineLibraryMethod("unscan", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.Unscan), + }); + + } + + private void LoadStringScanner_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("must_C_version", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.StringScanner.StringScanner.MustCVersion), + }); + + } + + } +} + +namespace IronRuby.StandardLibrary.Enumerator { + public sealed class EnumeratorLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + IronRuby.Builtins.RubyClass classRef0 = GetClass(typeof(System.Object)); + + + IronRuby.Builtins.RubyModule def1 = ExtendModule(typeof(IronRuby.Builtins.Enumerable), null, null, IronRuby.Builtins.RubyModule.EmptyArray); + IronRuby.Builtins.RubyClass def2 = DefineClass("IronRuby::Builtins::Enumerable::Enumerator", typeof(IronRuby.StandardLibrary.Enumerator.Enumerable.Enumerator), classRef0, null, null, new IronRuby.Builtins.RubyModule[] {def1, }, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Enumerator.Enumerable.Enumerator.CreateForEach), + new System.Func(IronRuby.StandardLibrary.Enumerator.Enumerable.Enumerator.Create), + new System.Func(IronRuby.StandardLibrary.Enumerator.Enumerable.Enumerator.Create), + }); + def1.SetConstant("Enumerator", def2); + } + + } +} + +namespace IronRuby.StandardLibrary.FunctionControl { + public sealed class FunctionControlLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + + + } + + } +} + +namespace IronRuby.StandardLibrary.FileControl { + public sealed class FileControlLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + + + DefineGlobalModule("Fcntl", typeof(IronRuby.StandardLibrary.FileControl.Fcntl), new System.Action(LoadFcntl_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + } + + private void LoadFcntl_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("F_SETFL", IronRuby.StandardLibrary.FileControl.Fcntl.F_SETFL); + module.SetConstant("O_ACCMODE", IronRuby.StandardLibrary.FileControl.Fcntl.O_ACCMODE); + module.SetConstant("O_APPEND", IronRuby.StandardLibrary.FileControl.Fcntl.O_APPEND); + module.SetConstant("O_CREAT", IronRuby.StandardLibrary.FileControl.Fcntl.O_CREAT); + module.SetConstant("O_EXCL", IronRuby.StandardLibrary.FileControl.Fcntl.O_EXCL); + module.SetConstant("O_NONBLOCK", IronRuby.StandardLibrary.FileControl.Fcntl.O_NONBLOCK); + module.SetConstant("O_RDONLY", IronRuby.StandardLibrary.FileControl.Fcntl.O_RDONLY); + module.SetConstant("O_RDWR", IronRuby.StandardLibrary.FileControl.Fcntl.O_RDWR); + module.SetConstant("O_TRUNC", IronRuby.StandardLibrary.FileControl.Fcntl.O_TRUNC); + module.SetConstant("O_WRONLY", IronRuby.StandardLibrary.FileControl.Fcntl.O_WRONLY); + + } + + } +} + +namespace IronRuby.StandardLibrary.BigDecimal { + public sealed class BigDecimalLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + IronRuby.Builtins.RubyClass classRef0 = GetClass(typeof(IronRuby.Builtins.Numeric)); + + + DefineGlobalClass("BigDecimal", typeof(IronRuby.StandardLibrary.BigDecimal.BigDecimal), classRef0, new System.Action(LoadBigDecimal_Instance), new System.Action(LoadBigDecimal_Class), IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.CreateBigDecimal), + }); + ExtendModule(typeof(IronRuby.Builtins.Kernel), new System.Action(LoadIronRuby__Builtins__Kernel_Instance), new System.Action(LoadIronRuby__Builtins__Kernel_Class), IronRuby.Builtins.RubyModule.EmptyArray); + } + + private void LoadBigDecimal_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + module.SetConstant("BASE", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.BASE); + module.SetConstant("EXCEPTION_ALL", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.EXCEPTION_ALL); + module.SetConstant("EXCEPTION_INFINITY", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.EXCEPTION_INFINITY); + module.SetConstant("EXCEPTION_NaN", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.EXCEPTION_NaN); + module.SetConstant("EXCEPTION_OVERFLOW", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.EXCEPTION_OVERFLOW); + module.SetConstant("EXCEPTION_UNDERFLOW", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.EXCEPTION_UNDERFLOW); + module.SetConstant("EXCEPTION_ZERODIVIDE", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.EXCEPTION_ZERODIVIDE); + module.SetConstant("ROUND_CEILING", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ROUND_CEILING); + module.SetConstant("ROUND_DOWN", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ROUND_DOWN); + module.SetConstant("ROUND_FLOOR", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ROUND_FLOOR); + module.SetConstant("ROUND_HALF_DOWN", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ROUND_HALF_DOWN); + module.SetConstant("ROUND_HALF_EVEN", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ROUND_HALF_EVEN); + module.SetConstant("ROUND_HALF_UP", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ROUND_HALF_UP); + module.SetConstant("ROUND_MODE", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ROUND_MODE); + module.SetConstant("ROUND_UP", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ROUND_UP); + module.SetConstant("SIGN_NaN", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.SIGN_NaN); + module.SetConstant("SIGN_NEGATIVE_FINITE", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.SIGN_NEGATIVE_FINITE); + module.SetConstant("SIGN_NEGATIVE_INFINITE", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.SIGN_NEGATIVE_INFINITE); + module.SetConstant("SIGN_NEGATIVE_ZERO", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.SIGN_NEGATIVE_ZERO); + module.SetConstant("SIGN_POSITIVE_FINITE", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.SIGN_POSITIVE_FINITE); + module.SetConstant("SIGN_POSITIVE_INFINITE", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.SIGN_POSITIVE_INFINITE); + module.SetConstant("SIGN_POSITIVE_ZERO", IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.SIGN_POSITIVE_ZERO); + + module.DefineLibraryMethod("-", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + }); + + module.DefineLibraryMethod("%", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Modulo), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Modulo), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Modulo), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ModuloOp), + }); + + module.DefineLibraryMethod("*", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + }); + + module.DefineLibraryMethod("**", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Power), + }); + + module.DefineLibraryMethod("/", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Divide), + }); + + module.DefineLibraryMethod("-@", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Negate), + }); + + module.DefineLibraryMethod("_dump", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Dump), + }); + + module.DefineLibraryMethod("+", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + }); + + module.DefineLibraryMethod("+@", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Identity), + }); + + module.DefineLibraryMethod("<", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.LessThan), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.LessThan), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.LessThan), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.LessThan), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.LessThan), + }); + + module.DefineLibraryMethod("<=", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.LessThanOrEqual), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.LessThanOrEqual), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.LessThanOrEqual), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.LessThanOrEqual), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.LessThanOrEqual), + }); + + module.DefineLibraryMethod("<=>", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Compare), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Compare), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Compare), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Compare), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Compare), + }); + + module.DefineLibraryMethod("==", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + }); + + module.DefineLibraryMethod("===", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + }); + + module.DefineLibraryMethod(">", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.GreaterThan), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.GreaterThan), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.GreaterThan), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.GreaterThan), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.GreaterThan), + }); + + module.DefineLibraryMethod(">=", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.GreaterThanOrEqual), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.GreaterThanOrEqual), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.GreaterThanOrEqual), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.GreaterThanOrEqual), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.GreaterThanOrEqual), + }); + + module.DefineLibraryMethod("abs", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Abs), + }); + + module.DefineLibraryMethod("add", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Add), + }); + + module.DefineLibraryMethod("ceil", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Ceil), + }); + + module.DefineLibraryMethod("coerce", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Coerce), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Coerce), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Coerce), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Coerce), + }); + + module.DefineLibraryMethod("div", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Div), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Div), + }); + + module.DefineLibraryMethod("divmod", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.DivMod), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.DivMod), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.DivMod), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.DivMod), + }); + + module.DefineLibraryMethod("eql?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Equal), + }); + + module.DefineLibraryMethod("exponent", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Exponent), + }); + + module.DefineLibraryMethod("finite?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.IsFinite), + }); + + module.DefineLibraryMethod("fix", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Fix), + }); + + module.DefineLibraryMethod("floor", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Floor), + }); + + module.DefineLibraryMethod("frac", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Fraction), + }); + + module.DefineLibraryMethod("hash", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Hash), + }); + + module.DefineLibraryMethod("infinite?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.IsInfinite), + }); + + module.DefineLibraryMethod("inspect", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Inspect), + }); + + module.DefineLibraryMethod("modulo", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Modulo), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Modulo), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Modulo), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Modulo), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Modulo), + }); + + module.DefineLibraryMethod("mult", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Multiply), + }); + + module.DefineLibraryMethod("nan?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.IsNaN), + }); + + module.DefineLibraryMethod("nonzero?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.IsNonZero), + }); + + module.DefineLibraryMethod("power", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Power), + }); + + module.DefineLibraryMethod("precs", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Precision), + }); + + module.DefineLibraryMethod("quo", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Divide), + }); + + module.DefineLibraryMethod("remainder", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Remainder), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Remainder), + }); + + module.DefineLibraryMethod("round", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Round), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Round), + }); + + module.DefineLibraryMethod("sign", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Sign), + }); + + module.DefineLibraryMethod("split", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Split), + }); + + module.DefineLibraryMethod("sqrt", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.SquareRoot), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.SquareRoot), + }); + + module.DefineLibraryMethod("sub", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Subtract), + }); + + module.DefineLibraryMethod("to_f", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ToFloat), + }); + + module.DefineLibraryMethod("to_i", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ToI), + }); + + module.DefineLibraryMethod("to_int", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ToI), + }); + + module.DefineLibraryMethod("to_s", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ToString), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ToString), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.ToString), + }); + + module.DefineLibraryMethod("truncate", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Truncate), + }); + + module.DefineLibraryMethod("zero?", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.IsZero), + }); + + } + + private void LoadBigDecimal_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("_load", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Load), + }); + + module.DefineLibraryMethod("double_fig", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.DoubleFig), + }); + + module.DefineLibraryMethod("induced_from", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.InducedFrom), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.InducedFrom), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.InducedFrom), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.InducedFrom), + }); + + module.DefineLibraryMethod("limit", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Limit), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Limit), + }); + + module.DefineLibraryMethod("mode", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Mode), + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Mode), + }); + + module.DefineLibraryMethod("ver", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.BigDecimalOps.Version), + }); + + } + + private void LoadIronRuby__Builtins__Kernel_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("BigDecimal", 0x12, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.KernelOps.CreateBigDecimal), + }); + + } + + private void LoadIronRuby__Builtins__Kernel_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("BigDecimal", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.BigDecimal.KernelOps.CreateBigDecimal), + }); + + } + + } +} + +namespace IronRuby.StandardLibrary.Iconv { + public sealed class IconvLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + IronRuby.Builtins.RubyClass classRef0 = GetClass(typeof(System.Object)); + + + DefineGlobalClass("Iconv", typeof(IronRuby.StandardLibrary.Iconv.Iconv), classRef0, new System.Action(LoadIconv_Instance), new System.Action(LoadIconv_Class), IronRuby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Iconv.Iconv.Create), + }); + } + + private void LoadIconv_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("close", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Iconv.Iconv.Close), + }); + + module.DefineLibraryMethod("iconv", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Iconv.Iconv.iconv), + }); + + module.DefineLibraryMethod("initialize", 0x12, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Iconv.Iconv.Initialize), + }); + + } + + private void LoadIconv_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("charset_map", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Iconv.Iconv.CharsetMap), + }); + + module.DefineLibraryMethod("conv", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Iconv.Iconv.Convert), + }); + + module.DefineLibraryMethod("iconv", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Iconv.Iconv.iconv), + }); + + module.DefineLibraryMethod("open", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Iconv.Iconv.Create), + new System.Func(IronRuby.StandardLibrary.Iconv.Iconv.Open), + }); + + } + + } +} + +namespace IronRuby.StandardLibrary.Clr { + public sealed class ClrLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + + + DefineGlobalModule("Clr", typeof(IronRuby.StandardLibrary.Clr.ClrOps), null, new System.Action(LoadClr_Class), IronRuby.Builtins.RubyModule.EmptyArray); + } + + private void LoadClr_Class(IronRuby.Builtins.RubyModule/*!*/ module) { + module.DefineLibraryMethod("profile", 0x21, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.Clr.ClrOps.GetProfile), + new System.Func(IronRuby.StandardLibrary.Clr.ClrOps.GetProfile), + }); + + } + + } +} + +namespace IronRuby.StandardLibrary.ParseTree { + public sealed class ParseTreeLibraryInitializer : IronRuby.Builtins.LibraryInitializer { + protected override void LoadModules() { + + + IronRuby.Builtins.RubyModule def1 = DefineGlobalModule("IronRuby", typeof(IronRuby.StandardLibrary.ParseTree.IronRubyOps), null, null, IronRuby.Builtins.RubyModule.EmptyArray); + IronRuby.Builtins.RubyModule def2 = DefineModule("IronRuby::ParseTree", typeof(IronRuby.StandardLibrary.ParseTree.IronRubyOps.ParseTreeOps), new System.Action(LoadIronRuby__ParseTree_Instance), null, IronRuby.Builtins.RubyModule.EmptyArray); + def1.SetConstant("ParseTree", def2); + } + + private void LoadIronRuby__ParseTree_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { + + module.DefineLibraryMethod("parse_tree_for_meth", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.ParseTree.IronRubyOps.ParseTreeOps.CreateParseTreeForMethod), + }); + + module.DefineLibraryMethod("parse_tree_for_str", 0x11, new System.Delegate[] { + new System.Func(IronRuby.StandardLibrary.ParseTree.IronRubyOps.ParseTreeOps.CreateParseTreeForString), + }); + + } + + } +} + diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/IronRuby.Libraries.csproj b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/IronRuby.Libraries.csproj new file mode 100644 index 0000000000..c40808f8b6 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/IronRuby.Libraries.csproj @@ -0,0 +1,199 @@ + + + + Debug + AnyCPU + 9.0.30703 + 2.0 + {77323B06-15A2-4CF4-8A7A-86EAA2B66498} + Library + Properties + IronRuby.Libraries + IronRuby.Libraries + SAK + SAK + SAK + SAK + 2.0 + 618,1685 + + + true + full + false + ..\..\..\Bin\Debug\ + DEBUG;TRACE;SIGNED + prompt + 4 + true + ..\..\..\MSSharedLibKey.snk + true + + + pdbonly + true + ..\..\..\Bin\Release\ + TRACE;SIGNED + prompt + 4 + true + ..\..\..\MSSharedLibKey.snk + true + + + true + ..\..\..\Bin\Silverlight Debug\ + TRACE;DEBUG;SILVERLIGHT + full + AnyCPU + prompt + true + true + ..\..\..\SilverlightKey.snk + true + true + ..\..\..\Utilities\Silverlight\x86ret\ + + + ..\..\..\Bin\Silverlight Release + TRACE;SILVERLIGHT + true + pdbonly + AnyCPU + prompt + true + ..\..\..\SilverlightKey.snk + true + true + ..\..\..\Utilities\Silverlight\x86ret\ + + + + Properties\SilverlightVersion.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + $(SilverlightSdkPath)\mscorlib.dll + + + False + $(SilverlightSdkPath)\System.dll + + + False + $(SilverlightSdkPath)\System.Net.dll + + + + + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4} + Microsoft.Scripting.Core + False + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Scripting + + + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD} + Ruby + False + + + + + + + + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/IronRuby.Libraries.csproj.vspscc b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/IronRuby.Libraries.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/IronRuby.Libraries.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/OpenSSL/OpenSSL.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/OpenSSL/OpenSSL.cs new file mode 100644 index 0000000000..ee1ff7bb83 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/OpenSSL/OpenSSL.cs @@ -0,0 +1,134 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using IronRuby.Builtins; +using IronRuby.Runtime; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using Crypto = System.Security.Cryptography; + +namespace IronRuby.StandardLibrary.OpenSsl { + + [RubyModule("OpenSSL")] + public static class OpenSsl { + // TODO: constants + // Config,HMACError,PKCS12,Random,OPENSSL_VERSION,PKCS7,BN,ConfigError,PKey,Engine,BNError,Netscape,OCSP + // OpenSSLError,CipherError,SSL,VERSION,X509,ASN1,OPENSSL_VERSION_NUMBER,Cipher + + [RubyConstant] + public const string OPENSSL_VERSION = "OpenSSL 0.9.8d 28 Sep 2006"; + + [RubyConstant] + public const double OPENSSL_VERSION_NUMBER = 9470031; + + [RubyConstant] + public const string VERSION = "1.0.0"; + + [RubyModule("Digest")] + public static class DigestFactory { + + // TODO: constants: + // SHA224,MDC2,DSS1,SHA512,SHA1,MD5,DSS,SHA384,SHA,MD4,SHA256,DigestError,RIPEMD160,MD2 + + [RubyClass("Digest")] + public class Digest { + private Crypto.HMAC _algorithm; + + public Crypto.HMAC Algorithm { + get { return _algorithm; } + } + + protected Digest() { + } + + [RubyConstructor] + public static Digest/*!*/ CreateDigest(RubyClass/*!*/ self, [NotNull]MutableString/*!*/ algorithmName) { + return Initialize(new Digest(), algorithmName); + } + + // Reinitialization. Not called when a factory/non-default ctor is called. + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static Digest/*!*/ Initialize(Digest/*!*/ self, [NotNull]MutableString/*!*/ algorithmName) { + Crypto.HMAC algorithm; + +#if SILVERLIGHT + switch (algorithmName.ToString()) { + case "SHA1": algorithm = new Crypto.HMACSHA1(); break; + case "SHA256": algorithm = new Crypto.HMACSHA256(); break; + default: algorithm = null; break; + } +#else + algorithm = Crypto.HMAC.Create("HMAC" + algorithmName.ConvertToString()); +#endif + + if (algorithm == null) { + throw new RuntimeError(String.Format("Unsupported digest algorithm ({0}).", algorithmName)); + } + + self._algorithm = algorithm; + return self; + } + } + } + + [RubyClass("HMAC")] + public class HMAC { + [RubyMethod("hexdigest", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ HexDigest(RubyClass/*!*/ self, [NotNull]DigestFactory.Digest/*!*/ digest, + [NotNull]MutableString/*!*/ key, [NotNull]MutableString/*!*/ data) { + + // TODO: does MRI really modify the digest object? + digest.Algorithm.Key = key.ConvertToBytes(); + byte[] hash = digest.Algorithm.ComputeHash(data.ConvertToBytes()); + + return MutableString.Create(BitConverter.ToString(hash).Replace("-", "").ToLower()); + } + } + + [RubyModule("Random")] + public static class RandomModule { + + // This is a no-op method since our random number generator uses the .NET crypto random number generator + // that gets its seed values from the OS + + [RubyMethod("seed", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ Seed(RubyModule/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ seed) { + return seed; + } + } + + [RubyClass("BN")] + public class BN { + + [RubyMethod("rand", RubyMethodAttributes.PublicSingleton)] + public static BigInteger/*!*/ Rand(RubyClass/*!*/ self, [DefaultProtocol]int bits, [DefaultProtocol, Optional]int someFlag, [Optional]bool otherFlag) { // TODO: figure out someFlag and otherFlag + byte[] data = new byte[bits >> 3]; + var generator = new Crypto.RNGCryptoServiceProvider(); + generator.GetBytes(data); + + uint[] transformed = new uint[data.Length >> 2]; + int j = 0; + for (int i = 0; i < transformed.Length; ++i) { + transformed[i] = data[j] + (uint)(data[j + 1] << 8) + (uint)(data[j + 2] << 16) + (uint)(data[j + 3] << 24); + j += 4; + } + + return new BigInteger(1, transformed); + } + } + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/ParseTree/IronRubyParseTreeOps.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/ParseTree/IronRubyParseTreeOps.cs new file mode 100644 index 0000000000..3fd10646d6 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/ParseTree/IronRubyParseTreeOps.cs @@ -0,0 +1,1501 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using IronRuby.Builtins; +using IronRuby.Compiler; +using IronRuby.Compiler.Ast; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.StandardLibrary.ParseTree { + [RubyModule("IronRuby")] + public static class IronRubyOps { + + [RubyModule("ParseTree")] + public static class ParseTreeOps { + + [RubyMethod("parse_tree_for_meth")] + public static RubyArray/*!*/ CreateParseTreeForMethod(object self, + [NotNull]RubyModule/*!*/ module, [DefaultProtocol]string/*!*/ methodName, bool isClassMethod) { + + bool includeNewLines = IncludeNewLines(module.Context, self); + + if (isClassMethod) { + module = module.SingletonClass; + } + + var member = module.GetMethod(methodName); + + // TODO: aliases, module_functions, define_methods, method witch changed visibility: + var method = member as RubyMethodInfo; + if (method == null) { + return RubyArray.Create(null); + } + + var visitor = new AstVisitor(GetNodeNames(module.Context, self), false); + visitor.Walk(method.GetSyntaxTree()); + + return visitor.Result; + } + + [RubyMethod("parse_tree_for_str")] + public static RubyArray/*!*/ CreateParseTreeForString(RubyScope/*!*/ scope, object self, + [NotNull]MutableString/*!*/ code, [Optional, NotNull]MutableString/*!*/ file, int line) { + + SourceUnit source = scope.RubyContext.CreateSnippet( + code.ConvertToString(), + file != null ? file.ConvertToString() : null, + SourceCodeKind.Statements + ); + + var options = RubyUtils.CreateCompilerOptionsForEval(scope, line); + + SourceUnitTree ast = new Parser().Parse(source, options, scope.RubyContext.RuntimeErrorSink); + bool includeNewLines = IncludeNewLines(scope.RubyContext, self); + var visitor = new AstVisitor(GetNodeNames(scope.RubyContext, self), false); + visitor.Walk(ast); + return visitor.Result; + } + + private static bool IncludeNewLines(RubyContext/*!*/ context, object self) { + object value; + if (context.TryGetInstanceVariable(self, "@include_newlines", out value)) { + return Protocols.IsTrue(value); + } + return false; + } + + private static RubyArray/*!*/ GetNodeNames(RubyContext/*!*/ context, object self) { + object value; + context.GetClassOf(self).TryGetConstantNoAutoload("NODE_NAMES", out value); + return value as RubyArray ?? new RubyArray(); + } + + private enum NodeKind { + // 00 + Method, fbody, cfunc, scope, block, + @if, @case, when, opt_n, @while, + // 10 + until, iter, @for, @break, next, + redo, retry, begin, rescue, resbody, + // 20 + ensure, and, or, not, masgn, + lasgn, dasgn, dasgn_curr, gasgn, iasgn, + // 30 + cdecl, cvasgn, cvdecl, op_asgn1, op_asgn2, + op_asgn_and, op_asgn_or, call, fcall, vcall, + // 40 + super, zsuper, array, zarray, hash, + @return, yield, lvar, dvar, gvar, + // 50 + ivar, @const, cvar, nth_ref, back_ref, + match, match2, match3, lit, str, + // 60 + dstr, xstr, dxstr, evstr, dregx, + dregx_once, args, argscat, argspush, splat, + // 70 + to_ary, svalue, block_arg, block_pass, defn, + defs, alias, valias, undef, @class, + // 80 + module, sclass, colon2, colon3, cref, + dot2, dot3, flip2, flip3, attrset, + // 90 + self, nil, @true, @false, defined, + // 95 + newline, postexe, alloca, dmethod, bmethod, + // 100 + memo, ifunc, dsym, attrasgn, + last + } + + private sealed class Rhs { + public object Value { get; set; } + public bool InBlockParameters { get; set; } + public bool InCompoundLhs { get; set; } + public bool InTopCompoundLhs { get; set; } + public bool IsRhsArg { get; set; } + } + + private sealed class AstVisitor : Walker { + private static readonly Rhs BlockRhs = new Rhs { InBlockParameters = true }; + private static readonly object Skip = new object(); + private object _result; + + // null -> no rhs + private Rhs _rhs; + + private readonly RubyArray/*!*/ _nodeNames; + private bool _isMethodAlias; + + public AstVisitor(RubyArray/*!*/ nodeNames, bool isMethodAlias) { + Assert.NotNull(nodeNames); + _nodeNames = nodeNames; + _isMethodAlias = isMethodAlias; + } + + #region Helpers + + private object GetNodeName(NodeKind nodeKind) { + int index = (int)nodeKind; + return (index < _nodeNames.Count) ? _nodeNames[index] : null; + } + + private RubyArray/*!*/ MakeNode(NodeKind nodeKind, int valueCount) { + var node = new RubyArray(1 + valueCount); + node.Add(GetNodeName(nodeKind)); + return node; + } + + private RubyArray/*!*/ MakeNode(NodeKind nodeKind) { + var node = MakeNode(nodeKind, 0); + return node; + } + + private RubyArray/*!*/ MakeNode(NodeKind nodeKind, object value1) { + var node = MakeNode(nodeKind, 1); + node.Add(value1); + return node; + } + + private RubyArray/*!*/ MakeNode(NodeKind nodeKind, object value1, object value2) { + var node = MakeNode(nodeKind, 2); + node.Add(value1); + node.Add(value2); + return node; + } + + private RubyArray/*!*/ MakeNode(NodeKind nodeKind, object value1, object value2, object value3) { + var node = MakeNode(nodeKind, 3); + node.Add(value1); + node.Add(value2); + node.Add(value3); + return node; + } + + public RubyArray/*!*/ Result { + get { + return (RubyArray)_result; + } + } + + private bool TryGetRhsValue(out object value) { + if (_rhs != null && !_rhs.InBlockParameters && !_rhs.InCompoundLhs) { + value = _rhs.Value; + return true; + } else { + value = null; + return false; + } + } + + private RubyArray/*!*/ AddRange(RubyArray/*!*/ list, IList nodes) where T : Node { + if (nodes != null) { + foreach (T node in nodes) { + Walk(node); + if (_result != Skip) { + list.Add(_result); + } + } + } + return list; + } + + private RubyArray/*!*/ AddSplat(RubyArray/*!*/ list, object value) { + var array = value as RubyArray; + if (array != null) { + list.AddRange(array); + } else { + list.Add(value); + } + return list; + } + + private void UsingRhs(Rhs rhs, Action/*!*/ region) { + var oldRhs = _rhs; + _rhs = rhs; + region(); + _rhs = oldRhs; + } + + #endregion + + #region SourceUnitTree + + public override bool Enter(SourceUnitTree/*!*/ node) { + if (node.Statements == null || node.Statements.Count == 0) { + _result = new RubyArray(); + } else if (node.Statements.Count == 1) { + Walk(node.Statements[0]); + _result = RubyArray.Create(_result); + } else { + _result = RubyArray.Create(AddRange(MakeNode(NodeKind.block, node.Statements.Count), node.Statements)); + } + + return false; + } + + #endregion + + #region Literals + + public override bool Enter(Literal/*!*/ node) { + if (node.Value == null) { + _result = MakeNode(NodeKind.nil); + } else if (node.Value is bool) { + _result = MakeNode((bool)node.Value ? NodeKind.@true : NodeKind.@false); + } else { + _result = MakeNode(NodeKind.lit, node.Value); + } + return false; + } + + public override bool Enter(RangeExpression/*!*/ node) { + Literal litBegin = node.Begin as Literal; + Literal litEnd = node.End as Literal; + + if (!node.IsCondition && litBegin != null && litEnd != null && litBegin.Value is int && litBegin.Value is int) { + _result = MakeNode(NodeKind.lit, new Range((int)litBegin.Value, (int)litEnd.Value, node.IsExclusive)); + } else { + var range = MakeNode(node.IsCondition ? + (node.IsExclusive ? NodeKind.flip3 : NodeKind.flip2) : + (node.IsExclusive ? NodeKind.dot3 : NodeKind.dot2), 2 + ); + + Walk(node.Begin); + range.Add(_result); + + Walk(node.End); + range.Add(_result); + _result = range; + } + return false; + } + + public override bool Enter(StringLiteral/*!*/ node) { + _result = MakeNode(NodeKind.str, node.Value); + return false; + } + + public override bool Enter(SymbolLiteral/*!*/ node) { + _result = MakeNode(NodeKind.lit, SymbolTable.StringToId(node.Value)); + return false; + } + + public override bool Enter(StringConstructor/*!*/ node) { + StringLiteral lit; + if (node.Parts.Count == 1 && (lit = node.Parts[0] as StringLiteral) != null) { + NodeKind kind; + object value; + switch (node.Kind) { + case StringKind.Immutable: kind = NodeKind.lit; value = SymbolTable.StringToId(lit.Value); break; + case StringKind.Command: kind = NodeKind.xstr; value = MutableString.Create(lit.Value); break; + case StringKind.Mutable: kind = NodeKind.str; value = MutableString.Create(lit.Value); break; + default: throw Assert.Unreachable; + } + + _result = MakeNode(kind, value); + } else { + NodeKind kind; + switch (node.Kind) { + case StringKind.Command: kind = NodeKind.dxstr; break; + case StringKind.Immutable: kind = NodeKind.dsym; break; + case StringKind.Mutable: kind = NodeKind.dstr; break; + default: throw Assert.Unreachable; + } + + _result = VisitStringConstructor(node.Parts, kind); + } + + return false; + } + + private RubyArray/*!*/ VisitStringConstructor(List/*!*/ parts, NodeKind kind) { + StringLiteral lit; + var str = MakeNode(kind, parts.Count); + + if (parts.Count == 1) { + str.Add(MutableString.Empty); + } + + for (int i = 0; i < parts.Count; i++) { + var part = parts[i]; + lit = part as StringLiteral; + if (lit != null) { + object value = MutableString.Create(lit.Value); + if (i > 0) { + value = MakeNode(NodeKind.str, value); + } + str.Add(value); + } else { + Walk(part); + str.Add(MakeNode(NodeKind.evstr, _result)); + } + } + + return str; + } + + public override bool Enter(RegularExpression/*!*/ node) { + StringLiteral lit; + if (node.Pattern.Count == 0) { + _result = MakeNode(NodeKind.lit, new RubyRegex(String.Empty, node.Options)); + } else if (node.Pattern.Count == 1 && (lit = node.Pattern[0] as StringLiteral) != null) { + _result = MakeNode(NodeKind.lit, new RubyRegex(lit.Value, node.Options)); + } else { + var regex = VisitStringConstructor(node.Pattern, NodeKind.dregx); + if (node.Options != RubyRegexOptions.NONE) { + regex.Add((int)node.Options); + } + _result = regex; + } + + if (node.IsCondition) { + _result = MakeNode(NodeKind.match, _result); + } + + return false; + } + + public override bool Enter(MatchExpression/*!*/ node) { + var match = MakeNode(NodeKind.match2, 2); + + Walk(node.Regex); + match.Add(_result); + + Walk(node.Expression); + match.Add(_result); + + _result = match; + return false; + } + + public override bool Enter(HashConstructor/*!*/ node) { + if (node.Expressions != null) { + _result = AddRange(MakeNode(NodeKind.hash, node.Expressions.Count), node.Expressions); + } else if (node.Maplets != null) { + _result = MakeHash(node.Maplets); + } else { + _result = MakeNode(NodeKind.hash); + } + return false; + } + + private RubyArray/*!*/ MakeHash(List/*!*/ maplets) { + var hash = MakeNode(NodeKind.hash, maplets.Count * 2); + foreach (var maplet in maplets) { + Walk(maplet.Key); + hash.Add(_result); + + Walk(maplet.Value); + hash.Add(_result); + } + return hash; + } + + public override bool Enter(ArrayConstructor/*!*/ node) { + if (node.Arguments == null || node.Arguments.IsEmpty) { + _result = MakeNode(NodeKind.zarray); + } else { + Walk(node.Arguments); + if (_result == Skip) { + _result = MakeNode(NodeKind.zarray); + } + } + return false; + } + + #endregion + + #region CallExpressions + + public override bool Enter(MethodCall/*!*/ node) { + RubyArray call; + if (node.Target != null) { + call = MakeNode(NodeKind.call, 2 + SizeOf(node.Arguments)); + } else if (node.Arguments != null) { + call = MakeNode(NodeKind.fcall, 1 + SizeOf(node.Arguments)); + } else { + call = MakeNode(NodeKind.vcall, 1); + } + + // add target: + if (node.Target != null) { + Walk(node.Target); + call.Add(_result); + } + + // add name: + call.Add(SymbolTable.StringToId(node.MethodName)); + + // add arguments: + AddArguments(call, node.Arguments); + + _result = MakeCallWithBlock(node.Block, call); + return false; + } + + public override bool Enter(SuperCall/*!*/ node) { + RubyArray call; + + if (node.Arguments != null) { + call = MakeNode(NodeKind.super, SizeOf(node.Arguments)); + + // add arguments: + AddArguments(call, node.Arguments); + } else { + call = MakeNode(NodeKind.zsuper); + } + + _result = MakeCallWithBlock(node.Block, call); + return false; + } + + public override bool Enter(YieldCall/*!*/ node) { + var call = MakeNode(NodeKind.yield, SizeOf(node.Arguments)); + + // add arguments: + AddArguments(call, node.Arguments); // TODO: splat [:array, value] + + _result = call; + return false; + } + + private static int SizeOf(Arguments args) { + return args != null && !args.IsEmpty ? 1 : 0; + } + + private void AddArguments(RubyArray/*!*/ list, Arguments args) { + if (args != null && !args.IsEmpty) { + Walk(args); + if (_result != Skip) { + list.Add(_result); + } + } + } + + public override bool Enter(Arguments/*!*/ node) { + RubyArray exprs = VisitExpressionsAndMaplets(node); + if (node.Array != null) { + RubyArray args = MakeSplatArguments(exprs, node.Array); + + object rhsValue; + if (TryGetRhsValue(out rhsValue)) { + _result = MakeNode(NodeKind.argspush, args, rhsValue); + } else { + _result = args; + } + } else if (exprs != null) { + _result = exprs; + } else { + _result = Skip; + } + + return false; + } + + private RubyArray/*!*/ MakeSplatArguments(RubyArray/*!*/ exprs, Expression/*!*/ splattedValue) { + RubyArray args; + if (exprs != null) { + args = MakeNode(NodeKind.argscat, 2); + args.Add(exprs); + } else { + args = MakeNode(NodeKind.splat, 1); + } + + Walk(splattedValue); + args.Add(_result); + + return args; + } + + private RubyArray VisitExpressionsAndMaplets(Arguments/*!*/ node) { + if (node.Expressions != null || node.Maplets != null) { + var array = MakeNode(NodeKind.array, + (node.Expressions != null ? node.Expressions.Count : 0) + + (node.Maplets != null ? 1 : 0) + ); + + AddRange(array, node.Expressions); + + if (node.Maplets != null) { + array.Add(MakeHash(node.Maplets)); + } + + // append RHS unless splat is present: + object rhsValue; + if (node.Array == null && TryGetRhsValue(out rhsValue)) { + array.Add(rhsValue); + } + + return array; + } + + return null; + } + + public override bool Enter(Maplet/*!*/ node) { + throw Assert.Unreachable; + } + + #endregion + + #region Blocks + + private RubyArray/*!*/ MakeCallWithBlock(Block block, RubyArray/*!*/ call) { + if (block != null) { + var blockRef = block as BlockReference; + if (blockRef != null) { + var result = MakeNode(NodeKind.block_pass, 2); // 0 .. block, 1 .. call + + // block: + Walk(blockRef.Expression); + result.Add(_result); + + // call: + result.Add(call); + + return result; + } else { + var blockDef = (BlockDefinition)block; + var result = MakeNode(NodeKind.iter, 3); // 0 .. call, 1 .. args(opt), 2 .. body(opt) + + // call: + result.Add(call); + + // block args: + if (blockDef.HasSignature) { + UsingRhs(BlockRhs, () => { + + Walk(blockDef.Parameters); + result.Add(_result); + + }); + } else { + result.Add(null); + } + + // block body: + AddRange(result, blockDef.Body); + return result; + } + } else { + return call; + } + } + + public override bool Enter(BlockReference/*!*/ node) { + throw Assert.Unreachable; + } + + public override bool Enter(BlockDefinition/*!*/ node) { + throw Assert.Unreachable; + } + + #endregion + + #region Variables + + public bool EnterVariable(string/*!*/ name, NodeKind read, NodeKind write) { + SymbolId symbol = SymbolTable.StringToId(name); + + RubyArray variable; + if (_rhs == null || _rhs.IsRhsArg) { + variable = MakeNode(read, symbol); + } else if (_rhs.InBlockParameters) { + variable = MakeNode((write == NodeKind.lasgn) ? NodeKind.dasgn_curr : write, symbol); + } else if (_rhs.InCompoundLhs) { + variable = MakeNode(write, symbol); + } else { + variable = MakeNode(write, symbol, _rhs.Value); + } + + _result = variable; + return false; + } + + public override bool Enter(ClassVariable/*!*/ node) { + return EnterVariable(node.Name, NodeKind.cvar, NodeKind.cvdecl); + } + + public override bool Enter(ConstantVariable/*!*/ node) { + if (node.IsGlobal) { + return EnterVariable(node.Name, NodeKind.colon3, NodeKind.cdecl); + } else if (node.IsBound) { + var qualified = MakeNode(NodeKind.colon2, 2); + + Walk(node.Qualifier); + qualified.Add(_result); + + qualified.Add(SymbolTable.StringToId(node.Name)); + + _result = qualified; + return false; + } else { + return EnterVariable(node.Name, NodeKind.@const, NodeKind.cdecl); + } + } + + public override bool Enter(IronRuby.Compiler.Ast.GlobalVariable/*!*/ node) { + return EnterVariable(node.FullName, NodeKind.gvar, NodeKind.gasgn); + } + + public override bool Enter(InstanceVariable/*!*/ node) { + return EnterVariable(node.Name, NodeKind.ivar, NodeKind.iasgn); + } + + public override bool Enter(LocalVariable/*!*/ node) { + return EnterVariable(node.Name, NodeKind.lvar, NodeKind.lasgn); + } + + public override bool Enter(RegexMatchReference/*!*/ node) { + if (node.FullName == "$~") { + return EnterVariable(node.FullName, NodeKind.gvar, NodeKind.gasgn); + } else if (node.Index > 0) { + Debug.Assert(_rhs == null); + _result = MakeNode(NodeKind.nth_ref, ScriptingRuntimeHelpers.Int32ToObject(node.Index)); + } else { + Debug.Assert(_rhs == null); + _result = MakeNode(NodeKind.back_ref, SymbolTable.StringToId(node.VariableName)); + } + return false; + } + + public override bool Enter(Placeholder/*!*/ node) { + // nop + _result = Skip; + return false; + } + + #endregion + + #region Assignment + + public override bool Enter(SimpleAssignmentExpression/*!*/ node) { + bool isAnd = node.Operation == "&&"; + bool isOr = node.Operation == "||"; + + var oldRhs = _rhs; + + _rhs = null; + Walk(node.Right); + var rvalue = _result; + + if (node.Operation != null && !isAnd && !isOr) { + Walk(node.Left); + rvalue = MakeNode(NodeKind.call, _result, SymbolTable.StringToId(node.Operation), MakeNode(NodeKind.array, rvalue)); + } + + _rhs = new Rhs { Value = rvalue }; + Walk(node.Left); + + if (isAnd || isOr) { + var lvalue = _result; + _rhs = null; + Walk(node.Left); + _result = MakeNode(isAnd ? NodeKind.op_asgn_and : NodeKind.op_asgn_or, _result, lvalue); + } + + _rhs = oldRhs; + return false; + } + + public override bool Enter(MemberAssignmentExpression/*!*/ node) { + // TODO: + throw new NotImplementedException(node.NodeType.ToString()); + } + + public override bool Enter(ParallelAssignmentExpression/*!*/ node) { + var oldRhs = _rhs; + _rhs = null; + + if (node.Right.SplattedValue == null && node.Right.RightValues.Count == 1 && node.Left.LeftValues.Count > 0) { + Walk(node.Right.RightValues[0]); + _rhs = new Rhs { InCompoundLhs = true, InTopCompoundLhs = true, Value = MakeNode(NodeKind.to_ary, _result) }; + } else if (node.Right.SplattedValue != null && node.Right.RightValues.Count == 0) { + Walk(node.Right.SplattedValue); + + var rvalue = MakeNode(NodeKind.splat, _result); + if (node.Left.UnsplattedValue == null && node.Left.LeftValues.Count == 1) { + _rhs = new Rhs { Value = MakeNode(NodeKind.svalue, rvalue) }; + } else { + _rhs = new Rhs { InCompoundLhs = true, InTopCompoundLhs = true, Value = rvalue }; + } + + } else { + var exprs = AddRange(MakeNode(NodeKind.array, node.Right.RightValues.Count), node.Right.RightValues); + + if (node.Right.SplattedValue != null) { + exprs = MakeSplatArguments(exprs, node.Right.SplattedValue); + } + + if (node.Left.UnsplattedValue == null && node.Left.LeftValues.Count == 1) { + _rhs = new Rhs { Value = MakeNode(NodeKind.svalue, exprs) }; + } else { + _rhs = new Rhs { InCompoundLhs = true, InTopCompoundLhs = true, Value = exprs }; + } + } + + Walk(node.Left); + _rhs = oldRhs; + return false; + } + + // RHS: [:call, ARRAY, :[], ARGUMENTS] + // LHS: [:attrasgn, ARRAY, :[]=, ARGUMENTS + RHS] + public override bool Enter(ArrayItemAccess/*!*/ node) { + if (_rhs == null) { + RubyArray call = MakeNode(NodeKind.call, 2 + SizeOf(node.Arguments)); + + // add target: + Walk(node.Array); + call.Add(_result); + + // add name: + call.Add(SymbolTable.StringToId("[]")); + + // add arguments: + AddArguments(call, node.Arguments); + + _result = call; + } else { + var isRhsArg = _rhs.IsRhsArg; + _rhs.IsRhsArg = true; + + var assignment = MakeNode(NodeKind.attrasgn, 2 + SizeOf(node.Arguments)); + + UsingRhs(null, () => { + Walk(node.Array); + assignment.Add(_result); + }); + + assignment.Add(SymbolTable.StringToId("[]=")); + + AddArguments(assignment, node.Arguments); + + _rhs.IsRhsArg = isRhsArg; + _result = assignment; + } + return false; + } + + // [:attrasgn, QUALIFIER, :NAME=, [:array, RHS]] + public override bool Enter(AttributeAccess/*!*/ node) { + Debug.Assert(_rhs != null); + + var assignment = MakeNode(NodeKind.attrasgn, 3); + + // qualifier: + UsingRhs(null, () => { + Walk(node.Qualifier); + assignment.Add(_result); + }); + + // name: + assignment.Add(SymbolTable.StringToId(node.Name)); + + // rhs array: + object rhsValue; + if (TryGetRhsValue(out rhsValue)) { + assignment.Add(MakeNode(NodeKind.array, rhsValue)); + } + + _result = assignment; + return false; + } + + public override bool Enter(AssignmentExpression/*!*/ node) { + throw Assert.Unreachable; + } + + #endregion + + #region CompoundLeftValue + + public override bool Enter(CompoundLeftValue/*!*/ node) { + Debug.Assert(_rhs != null); + + if (node.UnsplattedValue == null) { + if (node.LeftValues.Count == 0) { + Debug.Assert(_rhs == BlockRhs); + _result = ScriptingRuntimeHelpers.Int32ToObject(0); + return false; + } + + if (node.LeftValues.Count == 1) { + Walk(node.LeftValues[0]); + return false; + } + } + + bool isTop = _rhs.InTopCompoundLhs; + _rhs.InTopCompoundLhs = false; + + var assignment = MakeNode(NodeKind.masgn, + (node.LeftValues.Count > 0 ? 1 : 0) + + (node.UnsplattedValue != null ? 1 : 0) + + (_rhs != null ? 1 : 0) + + (isTop ? 1 : 0) // outer most gets RHS + ); + + if (node.LeftValues.Count > 0) { + assignment.Add( + AddRange(MakeNode(NodeKind.array, node.LeftValues.Count), node.LeftValues) + ); + } + + if (node.UnsplattedValue != null) { + if (node.UnsplattedValue is Placeholder) { + assignment.Add(MakeNode(NodeKind.splat)); + } else { + Walk(node.UnsplattedValue); + assignment.Add(_result); + } + } + + if (_rhs.InCompoundLhs && isTop) { + assignment.Add(_rhs.Value); + } + + _rhs.InTopCompoundLhs = isTop; + _result = assignment; + return false; + } + + //MemberAssignmentExpression + //SimpleAssignmentExpression + // + + #endregion + + #region Alias, Undefine, Defined?, Self, Initializer, Finalizer + + public override bool Enter(AliasStatement/*!*/ node) { + if (node.IsGlobalVariableAlias) { + _result = MakeNode(NodeKind.valias, + SymbolTable.StringToId("$" + node.NewName), + SymbolTable.StringToId("$" + node.OldName) + ); + } else { + _result = MakeNode(NodeKind.alias, + MakeNode(NodeKind.lit, SymbolTable.StringToId(node.NewName)), + MakeNode(NodeKind.lit, SymbolTable.StringToId(node.OldName)) + ); + } + return false; + } + + public override bool Enter(UndefineStatement/*!*/ node) { + if (node.Items.Count == 1) { + _result = MakeNode(NodeKind.undef, MakeNode(NodeKind.lit, SymbolTable.StringToId(node.Items[0].Name))); + } else { + var block = MakeNode(NodeKind.block, node.Items.Count); + foreach (var item in node.Items) { + block.Add(MakeNode(NodeKind.undef, MakeNode(NodeKind.lit, SymbolTable.StringToId(item.Name)))); + } + _result = block; + } + return false; + } + + public override bool Enter(IsDefinedExpression/*!*/ node) { + var def = MakeNode(NodeKind.defined, 1); + + Walk(node.Expression); + def.Add(_result); + + _result = def; + return false; + } + + public override bool Enter(SelfReference/*!*/ node) { + _result = MakeNode(NodeKind.self); + return false; + } + + public override bool Enter(Finalizer/*!*/ node) { + // TODO: + throw new NotImplementedException(); + } + + public override bool Enter(Initializer/*!*/ node) { + // TODO: + throw new NotImplementedException(); + } + + #endregion + + #region Boolean Expressions + + private bool EnterBooleanExpression(Expression/*!*/ left, Expression/*!*/ right, NodeKind kind) { + var b = MakeNode(kind, 2); + Walk(left); + b.Add(_result); + + Walk(right); + b.Add(_result); + + _result = b; + return false; + } + + public override bool Enter(AndExpression/*!*/ node) { + return EnterBooleanExpression(node.Left, node.Right, NodeKind.and); + } + + public override bool Enter(OrExpression/*!*/ node) { + return EnterBooleanExpression(node.Left, node.Right, NodeKind.or); + } + + public override bool Enter(NotExpression/*!*/ node) { + var b = MakeNode(NodeKind.not, 1); + Walk(node.Expression); + b.Add(_result); + + _result = b; + return false; + } + + #endregion + + #region JumpStatements + + private bool EnterJumpStatement(JumpStatement/*!*/ node, NodeKind kind) { + var jmp = MakeNode(kind, SizeOf(node.Arguments)); + + // add arguments: + AddArguments(jmp, node.Arguments); // TODO: splat [:array, value] + + _result = jmp; + return false; + } + + public override bool Enter(BreakStatement/*!*/ node) { + return EnterJumpStatement(node, NodeKind.@break); + } + + public override bool Enter(NextStatement/*!*/ node) { + return EnterJumpStatement(node, NodeKind.next); + } + + public override bool Enter(ReturnStatement/*!*/ node) { + return EnterJumpStatement(node, NodeKind.@return); + } + + public override bool Enter(RetryStatement/*!*/ node) { + return EnterJumpStatement(node, NodeKind.retry); + } + + public override bool Enter(RedoStatement/*!*/ node) { + return EnterJumpStatement(node, NodeKind.redo); + } + + #endregion + + #region Body (EH) + + public override bool Enter(BlockExpression/*!*/ node) { + _result = MakeBlock(node.Statements); + return false; + } + + public override bool Enter(Body/*!*/ node) { + var begin = MakeNode(NodeKind.begin, 5); + AddBody(begin, node); + + _result = begin; + return false; + } + + private RubyArray/*!*/ AddBody(RubyArray/*!*/ list, Body/*!*/ node) { + RubyArray current; + if (node.EnsureStatements != null) { + list.Add(current = MakeNode(NodeKind.ensure, 2)); + } else { + current = list; + } + + if (node.RescueClauses != null) { + var rescue = MakeNode(NodeKind.rescue); + rescue.Add(MakeBlock(node.Statements)); + AddRescueBody(rescue, node); + AddRange(rescue, node.ElseStatements); + current.Add(rescue); + } else { + current.Add(MakeBlock(node.Statements)); + } + + AddRange(current, node.EnsureStatements); + + return list; + } + + /// + /// rescue + /// 2 + /// rescue A => e + /// 3 + /// rescue A,B => e + /// 4 + /// rescue A + /// 5 + /// rescue A,B + /// 6 + /// end + /// + /// [:resbody, + /// nil, + /// [:lit, 2], + /// [:resbody, + /// [:array, [:const, :A]], + /// [:block, + /// [:lasgn, :e, [:gvar, :$!]], + /// [:lit, 3] + /// ], + /// [:resbody, + /// [:array, [:const, :A], [:const, :B]], + /// [:block, + /// [:lasgn, :e, [:gvar, :$!]], + /// [:lit, 4] + /// ], + /// [:resbody, + /// [:array, [:const, :A]], + /// [:lit, 5], + /// [:resbody, + /// [:array, [:const, :A], [:const, :B]], + /// [:lit, 6] + /// ] + /// ] + /// ] + /// ] + /// ] + /// + private void AddRescueBody(RubyArray/*!*/ current, Body/*!*/ node) { + foreach (var clause in node.RescueClauses) { + var resbody = MakeNode(NodeKind.resbody, 3); + + if (clause.Types != null) { + resbody.Add(AddRange(MakeNode(NodeKind.array, clause.Types.Count), clause.Types)); + } else { + resbody.Add(null); + } + + if (clause.Target != null) { + UsingRhs(new Rhs { Value = MakeNode(NodeKind.gvar, SymbolTable.StringToId("$!")) }, () => { + Walk(clause.Target); + }); + var assignment = _result; + + var block = MakeNode(NodeKind.block, 1 + (clause.Statements != null ? clause.Statements.Count : 0)); + block.Add(assignment); + AddRange(block, clause.Statements); + + resbody.Add(block); + } else { + AddRange(resbody, clause.Statements); + } + + current.Add(resbody); + current = resbody; + } + } + + public override bool Enter(RescueClause/*!*/ node) { + throw Assert.Unreachable; + } + + public override bool Enter(RescueExpression/*!*/ node) { + var rescue = MakeNode(NodeKind.rescue, 2); + + Walk(node.GuardedExpression); + rescue.Add(_result); + + var resbody = MakeNode(NodeKind.resbody, 2); + + resbody.Add(null); + + Walk(node.RescueClauseStatement); + resbody.Add(_result); + + rescue.Add(resbody); + + _result = rescue; + return false; + } + + #endregion + + #region Flow + + private object MakeBlock(List statements) { + var block = MakeBlockOpt(statements); + return (block != Skip) ? block : MakeNode(NodeKind.nil); + } + + private RubyArray/*!*/ AddBlock(RubyArray/*!*/ list, List statements) { + var block = MakeBlockOpt(statements); + if (block != Skip) { + list.Add(block); + } + return list; + } + + private object MakeBlockOpt(List statements) { + if (statements == null || statements.Count == 0) { + return Skip; + } else if (statements.Count == 1) { + Walk(statements[0]); + return _result; + } else { + return AddRange(MakeNode(NodeKind.block, statements.Count), statements); + } + } + + public override bool Enter(IfExpression/*!*/ node) { + RubyArray @if; + RubyArray current = @if = MakeNode(NodeKind.@if, 3); + + Walk(node.Condition); + current.Add(_result); + + current.Add(MakeBlock(node.Body)); + + if (node.ElseIfClauses != null && node.ElseIfClauses.Count != 0) { + foreach (var clause in node.ElseIfClauses) { + if (clause.Condition != null) { + // elsif cond + current.Add(current = MakeNode(NodeKind.@if, 3)); + + Walk(clause.Condition); + current.Add(_result); + } + + current.Add(MakeBlock(clause.Statements)); + } + + if (node.ElseIfClauses[node.ElseIfClauses.Count - 1].Condition != null) { + current.Add(null); + } + } else { + current.Add(null); + } + + _result = @if; + return false; + } + + public override bool Enter(UnlessExpression/*!*/ node) { + var @if = MakeNode(NodeKind.@if, 3); + + Walk(node.Condition); + @if.Add(_result); + + if (node.ElseClause != null) { + Debug.Assert(node.ElseClause.Condition == null); + @if.Add(MakeBlock(node.ElseClause.Statements)); + } else { + @if.Add(null); + } + + @if.Add(MakeBlock(node.Statements)); + + _result = @if; + return false; + } + + public override bool Enter(ElseIfClause/*!*/ node) { + throw Assert.Unreachable; + } + + private bool EnterTernary(NodeKind kind, Expression/*!*/ expr1, Expression expr2, Expression expr3) { + var result = MakeNode(NodeKind.@if, 3); + + Walk(expr1); + result.Add(_result); + + if (expr2 != null) { + Walk(expr2); + result.Add(_result); + } else { + result.Add(null); + } + + if (expr3 != null) { + Walk(expr3); + result.Add(_result); + } else { + result.Add(null); + } + + _result = result; + return false; + } + + public override bool Enter(ConditionalExpression/*!*/ node) { + return EnterTernary(NodeKind.@if, node.Condition, node.TrueExpression, node.FalseExpression); + } + + public override bool Enter(ConditionalJumpExpression/*!*/ node) { + if (node.IsBooleanExpression) { + if (node.NegateCondition) { + return EnterBooleanExpression(node.Condition, node.JumpStatement, NodeKind.or); + } else { + return EnterBooleanExpression(node.Condition, node.JumpStatement, NodeKind.and); + } + } + + if (node.NegateCondition) { + return EnterTernary(NodeKind.@if, node.Condition, node.Value, node.JumpStatement); + } else { + return EnterTernary(NodeKind.@if, node.Condition, node.JumpStatement, node.Value); + } + } + + public override bool Enter(ConditionalStatement/*!*/ node) { + if (node.IsUnless) { + return EnterTernary(NodeKind.@if, node.Condition, node.ElseStatement, node.Body); + } else { + return EnterTernary(NodeKind.@if, node.Condition, node.Body, node.ElseStatement); + } + } + + public override bool Enter(WhileLoopExpression/*!*/ node) { + var loop = MakeNode(node.IsWhileLoop ? NodeKind.@while : NodeKind.until); + + Walk(node.Condition); + loop.Add(_result); + + loop.Add(MakeBlock(node.Statements)); + + loop.Add(!node.IsPostTest); + + _result = loop; + return false; + } + + public override bool Enter(ForLoopExpression/*!*/ node) { + var loop = MakeNode(NodeKind.@for, 3); + + Walk(node.List); + loop.Add(_result); + + UsingRhs(new Rhs { InCompoundLhs = true, InTopCompoundLhs = false}, () => { + Walk(node.Block.Parameters); + loop.Add(_result); + + // block body: + AddBlock(loop, node.Block.Body); + }); + + _result = loop; + return false; + } + + // case v + // when ca1,ca2: a + // when cb: b + // when cc: + // when cd1, cd2, cd3*: a + // else + // e + // end + // + // [:case, + // , + // [:when, [:array, , ], ], + // [:when, [:array, ], ], + // [:when, [:array, ], nil], + // [:when, [:array, , , [:when, , nil]], ] + // + // ] + public override bool Enter(CaseExpression/*!*/ node) { + var c = MakeNode(NodeKind.@case, 1 + node.WhenClauses.Count + 1); + + if (node.Value != null) { + Walk(node.Value); + c.Add(_result); + } else { + c.Add(null); + } + + foreach (var whenClause in node.WhenClauses) { + var when = MakeNode(NodeKind.when, 2); + + var array = MakeNode(NodeKind.array, + (whenClause.Comparisons != null ? whenClause.Comparisons.Count : 0) + + (whenClause.ComparisonArray != null ? 1 :0) + ); + + AddRange(array, whenClause.Comparisons); + + if (whenClause.ComparisonArray != null) { + Walk(whenClause.ComparisonArray); + array.Add(MakeNode(NodeKind.when, _result, null)); + } + + when.Add(array); + when.Add(MakeBlock(whenClause.Statements)); + + c.Add(when); + } + + c.Add(MakeBlock(node.ElseStatements)); + + _result = c; + return false; + } + + public override bool Enter(WhenClause/*!*/ node) { + throw Assert.Unreachable; + } + + #endregion + + #region Declarations + + private void AddScope(RubyArray/*!*/ list, DeclarationExpression/*!*/ node) { + list.Add(AddBody(MakeNode(NodeKind.scope), node.Body)); + } + + public override bool Enter(ModuleDeclaration/*!*/ node) { + var module = MakeNode(NodeKind.module, 2); + + Walk(node.QualifiedName); + module.Add(_result); + + AddScope(module, node); + + _result = module; + return false; + } + + public override bool Enter(ClassDeclaration/*!*/ node) { + var module = MakeNode(NodeKind.@class, 3); + + Walk(node.QualifiedName); + module.Add(_result); + + if (node.SuperClass != null) { + Walk(node.SuperClass); + module.Add(_result); + } else { + module.Add(null); + } + + AddScope(module, node); + + _result = module; + return false; + } + + public override bool Enter(SingletonDeclaration/*!*/ node) { + var module = MakeNode(NodeKind.sclass, 2); + + Walk(node.Singleton); + module.Add(_result); + + AddScope(module, node); + + _result = module; + return false; + } + + public override bool Enter(MethodDeclaration/*!*/ node) { + bool isMethodAlias = _isMethodAlias; + _isMethodAlias = false; + + RubyArray method; + if (node.Target != null) { + method = MakeNode(NodeKind.defs, 3); + + Walk(node.Target); + method.Add(_result); + } else { + method = MakeNode(NodeKind.defn, 2); + } + + method.Add(SymbolTable.StringToId(node.Name)); + + var scope = MakeNode(NodeKind.scope, 1); + var block = MakeNode(NodeKind.block, 5); + + var parameters = MakeNode(NodeKind.args, + node.Parameters.MandatoryCount + + node.Parameters.OptionalCount + + (node.Parameters.Array != null ? 1 : 0) + ); + + if (node.Parameters.Mandatory != null) { + foreach (var p in node.Parameters.Mandatory) { + parameters.Add(SymbolTable.StringToId(p.Name)); + } + } + + if (node.Parameters.Optional != null) { + foreach (var assignment in node.Parameters.Optional) { + parameters.Add(SymbolTable.StringToId(((LocalVariable)assignment.Left).Name)); + } + } + + if (node.Parameters.Array != null) { + parameters.Add(SymbolTable.StringToId("*" + node.Parameters.Array.Name)); + } + + if (node.Parameters.Optional != null) { + var paramInit = MakeNode(NodeKind.block); + foreach (var assignment in node.Parameters.Optional) { + Walk(assignment); + paramInit.Add(_result); + } + parameters.Add(paramInit); + } + + block.Add(parameters); + + if (node.Parameters.Block != null) { + block.Add(MakeNode(NodeKind.block_arg, SymbolTable.StringToId(node.Parameters.Block.Name))); + } + + AddBody(block, node.Body); + + scope.Add(block); + + if (isMethodAlias) { + method.Add(MakeNode(NodeKind.fbody, scope)); + } else { + method.Add(scope); + } + + _isMethodAlias = isMethodAlias; + _result = method; + return false; + } + + public override bool Enter(Parameters/*!*/ node) { + throw Assert.Unreachable; + } + + #endregion + + } + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Properties/AssemblyInfo.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..ac6a5fd45d --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using IronRuby.Hosting; +using IronRuby.Runtime; +using System.Security; + +[assembly: AssemblyTitle("Ruby Libraries")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Ruby")] +[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] +[assembly: Guid("ca75230d-3011-485d-b1db-dfe924b6c434")] + +#if !SILVERLIGHT +[assembly: AssemblyVersion(RubyContext.IronRubyVersionString)] +[assembly: AssemblyFileVersion(RubyContext.IronRubyVersionString)] +[assembly: AllowPartiallyTrustedCallers] +[assembly: SecurityTransparent] +#endif diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Protocols.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Protocols.cs new file mode 100644 index 0000000000..e0bb220886 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Protocols.cs @@ -0,0 +1,688 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using IronRuby.Builtins; +using Microsoft.Scripting; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Actions; + +namespace IronRuby.Runtime { + /// + /// Class for implementing standard Ruby conversion logic + /// + /// Ruby conversion rules aren't always consistent, but we should try to capture all + /// common conversion patterns here. They're more likely to be correct than something + /// created by hand. + /// + public static class Protocols { + + #region dynamic sites + + private static readonly CallSite>/*!*/ + _ToF = CallSite>.Create(RubySites.InstanceCallAction("to_f")), + _ToI = CallSite>.Create(RubySites.InstanceCallAction("to_i")), + _ToInt = CallSite>.Create(RubySites.InstanceCallAction("to_int")), + _ToS = CallSite>.Create(RubySites.InstanceCallAction("to_s")), + _ToStr = CallSite>.Create(RubySites.InstanceCallAction("to_str")), + _ToA = CallSite>.Create(RubySites.InstanceCallAction("to_a")), + _ToAry = CallSite>.Create(RubySites.InstanceCallAction("to_ary")); + + #endregion + + #region Bignum/Fixnum Normalization + + /// + /// Converts a BigInteger to int if it is small enough + /// + /// The value to convert + /// An int if x is small enough, otherwise x. + /// + /// Use this helper to downgrade BigIntegers as necessary. + /// + public static object Normalize(BigInteger x) { + int result; + if (x.AsInt32(out result)) { + return ScriptingRuntimeHelpers.Int32ToObject(result); + } + return x; + } + + public static object Normalize(long x) { + if (x >= Int32.MinValue && x <= Int32.MaxValue) { + return ScriptingRuntimeHelpers.Int32ToObject((int)x); + } else { + return BigInteger.Create(x); + } + } + + public static object Normalize(object x) { + int result; + if (x is BigInteger) { + if (((BigInteger)x).AsInt32(out result)) { + return ScriptingRuntimeHelpers.Int32ToObject(result); + } + } + return x; + } + + #endregion + + #region CastToString, AsString, TryConvertToString + + /// + /// Standard way to convert to a Ruby String, using to_str + /// + /// Checks if it's already a string, and if so returns it. + /// Then calls to_str if it exists, otherwise throw a TypeError + /// + public static MutableString/*!*/ CastToString(RubyContext/*!*/ context, object obj) { + MutableString str = AsString(context, obj); + if (str != null) { + return str; + } + throw RubyExceptions.CannotConvertTypeToTargetType(context, obj, "String"); + } + + public static MutableString[]/*!*/ CastToStrings(RubyContext/*!*/ context, object[]/*!*/ objs) { + var result = new MutableString[objs.Length]; + for (int i = 0; i < objs.Length; i++) { + result[i] = Protocols.CastToString(context, objs[i]); + } + return result; + } + + /// + /// Standard way to convert to a Ruby String, using to_str + /// + /// Checks if it's already a string, and if so returns it. + /// Then calls to_str if it exists, otherwise returns null + /// + public static MutableString AsString(RubyContext/*!*/ context, object obj) { + MutableString str = obj as MutableString; + if (str != null) { + return str; + } + if (RubySites.RespondTo(context, obj, "to_str")) { + str = _ToStr.Target(_ToStr, context, obj) as MutableString; + if (str != null) { + return str; + } + + throw RubyExceptions.MethodShouldReturnType(context, obj, "to_str", "String"); + } + + return null; + } + + /// + /// Convert to string using to_s + /// + /// The behavior is different from the typical conversion protocol: + /// * it assumes that to to_s is defined, and just calls it + /// * if to_s returns a non-string value, we fall back to Kernel.ToString + /// + public static MutableString/*!*/ ConvertToString(RubyContext/*!*/ context, object obj) { + MutableString str = obj as MutableString; + if (str != null) { + return str; + } + + str = _ToS.Target(_ToS, context, obj) as MutableString; + if (str != null) { + return str; + } + + // fallback to Kernel#to_s if to_s returned a non-string + return KernelOps.ToS(context, obj); + } + + #endregion + + #region ConvertToFloat + + /// + /// Convert to a Float, using to_f + /// Throws if conversion fails + /// + public static double ConvertToFloat(RubyContext/*!*/ context, object value) { + if (value == null) { + throw RubyExceptions.CreateTypeError("can't convert nil into Float"); + } + if (value is int || value is double) { + return Converter.ConvertToDouble(value); + } + if (value is BigInteger) { + return ((BigInteger)value).ToFloat64(); + } + if (value is MutableString) { + return ConvertStringToFloat(context, (MutableString)value); + } + + if (RubySites.RespondTo(context, value, "to_f")) { + object obj = _ToF.Target(_ToF, context, value); + if (!(obj is double)) { + throw RubyExceptions.MethodShouldReturnType(context, value, "to_f", "Float"); + } + return (double)obj; + } + + throw RubyExceptions.CannotConvertTypeToTargetType(context, value, "Float"); + } + public static double ConvertStringToFloat(RubyContext context, MutableString value) { + try { + return double.Parse(value.ConvertToString(), System.Globalization.CultureInfo.InvariantCulture.NumberFormat); + } catch (FormatException x) { + MutableString valueString = RubySites.Inspect(context, value); + throw RubyExceptions.CreateArgumentError("invalid value for Float(): " + valueString.ConvertToString(), x); + } + } + #endregion + + #region TryConvertToInteger, ConvertToInteger, AsInteger, CastToInteger, CastToFixnum, IsInteger, IntegerAsFixnum + + private static bool AsPrimitiveInteger(object obj, out int intValue, out BigInteger bigValue) { + // TODO: All CLR primitive numeric types? + + if (obj is int) { + intValue = (int)obj; + bigValue = null; + return true; + } + + var big = obj as BigInteger; + if ((object)big != null) { + intValue = 0; + bigValue = big; + return true; + } + + intValue = 0; + bigValue = null; + return false; + } + + public static object/*!*/ ConvertToInteger(RubyContext/*!*/ context, object obj) { + int fixnum; + BigInteger bignum; + ConvertToInteger(context, obj, out fixnum, out bignum); + return (object)bignum ?? ScriptingRuntimeHelpers.Int32ToObject(fixnum); + } + + /// + /// Standard way to convert to a Ruby Integer, using to_int and to_i + /// Trys to call to_int, followed by to_i (if implemented). + /// If neither is callable, throws a type error. + /// + public static void ConvertToInteger(RubyContext/*!*/ context, object obj, out int fixnum, out BigInteger bignum) { + // Don't call to_int, to_i on primitive types: + if (AsPrimitiveInteger(obj, out fixnum, out bignum)) { + return; + } + + if (RubySites.RespondTo(context, obj, "to_int")) { + object result = _ToInt.Target(_ToInt, context, obj); + if (AsPrimitiveInteger(result, out fixnum, out bignum)) { + return; + } + + throw RubyExceptions.MethodShouldReturnType(context, obj, "to_int", "Integer"); + } + + if (RubySites.RespondTo(context, obj, "to_i")) { + object result = _ToI.Target(_ToI, context, obj); + if (AsPrimitiveInteger(result, out fixnum, out bignum)) { + return; + } + + throw RubyExceptions.MethodShouldReturnType(context, obj, "to_i", "Integer"); + } + + throw RubyExceptions.CannotConvertTypeToTargetType(context, obj, "Integer"); + } + + /// + /// Try to cast the object to an Integer using to_int + /// Returns null if the object doesn't implement to_int + /// Can return either Bignum or Fixnum + /// + public static bool AsInteger(RubyContext/*!*/ context, object obj, out int fixnum, out BigInteger bignum) { + // Don't call to_int on types derived from Integer + if (AsPrimitiveInteger(obj, out fixnum, out bignum)) { + return true; + } + + if (RubySites.RespondTo(context, obj, "to_int")) { + object result = _ToInt.Target(_ToInt, context, obj); + if (AsPrimitiveInteger(result, out fixnum, out bignum)) { + return true; + } + + throw RubyExceptions.InvalidValueForType(context, result, "Integer"); + } + + return false; + } + + /// + /// Converts an Integer to a Fixnum. + /// Don't call any conversion methods--just handles Fixnum & Bignum + /// + /// + /// true if value is an Integer, false otherwise + /// Throws a RangeError if value is a + /// BigInteger but can't be converted to a Fixnum + public static bool IntegerAsFixnum(object value, out int result) { + if (value is int) { + result = (int)value; + return true; + } + + var bignum = value as BigInteger; + if ((object)bignum != null) { + if (!bignum.AsInt32(out result)) { + throw RubyExceptions.CreateRangeError("bignum too big to convert into `long'"); + } + return true; + } + + result = 0; + return false; + } + + /// + /// Try to cast the object to an Integer using to_int + /// Throws if the cast fails + /// Can return either Bignum or Fixnum + /// + public static void CastToInteger(RubyContext/*!*/ context, object obj, out int fixnum, out BigInteger bignum) { + if (AsInteger(context, obj, out fixnum, out bignum)) { + return; + } + + throw RubyExceptions.CannotConvertTypeToTargetType(context, obj, "Integer"); + } + + /// + /// Like CastToInteger, but converts the result to a Fixnum + /// + public static int CastToFixnum(RubyContext/*!*/ context, object obj) { + if (obj == null) { + throw RubyExceptions.CreateTypeError("no implicit conversion from nil to integer"); + } + + int fixnum; + BigInteger bignum; + CastToInteger(context, obj, out fixnum, out bignum); + if ((object)bignum != null && !bignum.AsInt32(out fixnum)) { + throw RubyExceptions.CreateRangeError("bignum too big to convert into `long'"); + } + + return fixnum; + } + + /// + /// Like CastToInteger, but converts the result to an unsigned int. + /// + public static uint CastToUInt32Unchecked(RubyContext/*!*/ context, object obj) { + if (obj == null) { + throw RubyExceptions.CreateTypeError("no implicit conversion from nil to integer"); + } + + int fixnum; + BigInteger bignum; + CastToInteger(context, obj, out fixnum, out bignum); + if ((object)bignum != null) { + uint u; + if (bignum.AsUInt32(out u)) { + return u; + } + throw RubyExceptions.CreateRangeError("bignum too big to convert into `unsigned long'"); + } + + return unchecked((uint)fixnum); + } + + /// + /// Like CastToInteger, but converts the result to an unsigned int. + /// + public static ulong CastToUInt64Unchecked(RubyContext/*!*/ context, object obj) { + if (obj == null) { + throw RubyExceptions.CreateTypeError("no implicit conversion from nil to integer"); + } + + int fixnum; + BigInteger bignum; + CastToInteger(context, obj, out fixnum, out bignum); + if ((object)bignum != null) { + ulong u; + if (bignum.AsUInt64(out u)) { + return u; + } + throw RubyExceptions.CreateRangeError("bignum too big to convert into `quad long'"); + } + + return unchecked((ulong)fixnum); + } + + #endregion + + #region TryConvertToArray, ConvertToArray, AsArray, CastToArray + + /// + /// Try to convert obj to an Array using #to_a + /// 1. If obj is an Array (or a subtype), returns it + /// 2. Calls to_a if it exists, possibly throwing if to_a doesn't return an Array + /// 3. else returns null + /// + public static IList TryConvertToArray(RubyContext/*!*/ context, object obj) { + // Don't call to_a on types derived from Array + IList ary = obj as IList; + if (ary != null) { + return ary; + } + + if (RubySites.RespondTo(context, obj, "to_a")) { + object result = _ToA.Target(_ToA, context, obj); + ary = result as List; + if (ary != null) { + return ary; + } + + throw RubyExceptions.MethodShouldReturnType(context, obj, "to_a", "Array"); + } + + return null; + } + + /// + /// Works like TryConvertToArray, but throws a type error if the conversion fails + /// + public static IList/*!*/ ConvertToArray(RubyContext/*!*/ context, object obj) { + IList ary = TryConvertToArray(context, obj); + if (ary != null) { + return ary; + } + + throw RubyExceptions.CannotConvertTypeToTargetType(context, obj, "Array"); + } + + /// + /// Try to convert obj to an Array using #to_ary + /// 1. If obj is an Array (or a subtype), returns it + /// 2. Calls to_ary if it exists, possibly throwing if to_ary doesn't return an Array + /// 3. else returns null + /// + public static IList AsArray(RubyContext/*!*/ context, object obj) { + // Don't call to_a on types derived from Array + IList ary = obj as IList; + if (ary != null) { + return ary; + } + + if (RubySites.RespondTo(context, obj, "to_ary")) { + object result = _ToAry.Target(_ToAry, context, obj); + ary = result as IList; + if (ary != null) { + return ary; + } + + throw RubyExceptions.MethodShouldReturnType(context, obj, "to_ary", "Array"); + } + + return null; + } + + /// + /// Works like AsArray, but throws a type error if the conversion fails + /// + public static IList/*!*/ CastToArray(RubyContext/*!*/ context, object obj) { + IList ary = AsArray(context, obj); + if (ary != null) { + return ary; + } + + throw RubyExceptions.CannotConvertTypeToTargetType(context, obj, "Array"); + } + #endregion + + #region CastToSymbol + + /// + /// Casts to symbol. Note that this doesn't actually use to_sym -- it uses to_str. + /// That's just how Ruby does it. + /// + /// Another fun detail: you can pass Fixnums as Symbols. If you pass a Fixnum that + /// doesn't map to a Symbol (i.e. Fixnum#to_sym returns nil), you get an ArgumentError + /// instead of a TypeError. At least it produces a warning about using Fixnums as Symbols + /// + public static string/*!*/ CastToSymbol(RubyContext/*!*/ context, object obj) { + if (obj is SymbolId) { + return SymbolTable.IdToString((SymbolId)obj); + } + + if (obj is int) { + return RubyOps.ConvertFixnumToSymbol(context, (int)obj); + } else { + MutableString str = AsString(context, obj); + if (str != null) { + return RubyOps.ConvertMutableStringToSymbol(str); + } + } + + throw RubyExceptions.CreateTypeError(String.Format("{0} is not a symbol", RubySites.Inspect(context, obj))); + } + + public static string[]/*!*/ CastToSymbols(RubyContext/*!*/ context, object[]/*!*/ objects) { + string[] result = new string[objects.Length]; + for (int i = 0; i < objects.Length; i++) { + result[i] = Protocols.CastToSymbol(context, objects[i]); + } + return result; + } + + #endregion + + #region Compare (<=>), ConvertCompareResult + + private static CallSite> _CompareSharedSite = + CallSite>.Create( + RubySites.InstanceCallAction("<=>", 1)); + + private static CallSite> _GreaterSharedSite = + CallSite>.Create( + RubySites.InstanceCallAction(">", 1)); + + private static CallSite> _LessSharedSite = + CallSite>.Create( + RubySites.InstanceCallAction("<", 1)); + + + // Standard Ruby way of calling "<=>" + // Call it, and then see if the result is > 0 by calling ">" + // If not, see if the result is < 0 by calling "<" + // Returns -1, 0, 1 with the usual interpretation + public static int Compare(RubyContext/*!*/ context, object lhs, object rhs) { + object result = _CompareSharedSite.Target(_CompareSharedSite, context, lhs, rhs); + if (result == null) { + throw RubyExceptions.MakeComparisonError(context, lhs, rhs); + } + return ConvertCompareResult(context, result); + } + + /// + /// Try to compare the lhs and rhs. If they can't be compared return nil + /// + public static int? TryCompare(RubyContext/*!*/ context, object lhs, object rhs) { + object result = _CompareSharedSite.Target(_CompareSharedSite, context, lhs, rhs); + if (result == null) { + return null; + } + return ConvertCompareResult(context, result); + } + + public static int ConvertCompareResult(RubyContext/*!*/ context, object result) { + // Should throw if a call to <=> returns null + Debug.Assert(result != null); + + if (RubyOps.IsTrue(_GreaterSharedSite.Target(_GreaterSharedSite, context, result, 0))) { + return 1; + } else if (RubyOps.IsTrue(_LessSharedSite.Target(_LessSharedSite, context, result, 0))) { + return -1; + } + return 0; + } + + /// + /// Protocol for determining truth in Ruby (not null and not false) + /// + public static bool IsTrue(object obj) { + return (obj is bool) ? (bool)obj == true : obj != null; + } + + /// + /// Protocol for determining value equality in Ruby (uses IsTrue protocol on result of == call) + /// + public static bool IsEqual(RubyContext/*!*/ context, object lhs, object rhs) { + return IsTrue(RubySites.Equal(context, lhs, rhs)); + } + + public delegate object DynamicInvocation(RubyContext/*!*/ context, object self, object other); + + /// + /// Coerce the values of self and other (using other as the object) then call the passed in method. + /// + public static object CoerceAndCall(RubyContext/*!*/ context, object self, object other, DynamicInvocation invoke) { + RubyArray coercedValues; + try { + // Swap self and other around to do the coercion. + coercedValues = RubySites.Coerce(context, other, self); + } catch (MemberAccessException x) { + throw RubyExceptions.MakeCoercionError(context, self, other, x); + } catch (ArgumentException x) { + throw RubyExceptions.MakeCoercionError(context, self, other, x); + } + // But then swap them back when invoking the operation + return invoke(context, coercedValues[0], coercedValues[1]); + } + + /// + /// Try to coerce the values of self and other (using other as the object) then dynamically invoke "<=>". + /// + /// + /// 1 if self is greater than other + /// 0 if self is equal to other + /// -1 if self is less than other + /// null otherwise (like if self and other cannot be coerced) + /// + /// + /// This method is used when comparing Numerics. + /// If we can't coerce then they are not the same, so return null. + /// But if the <=> doesn't exist on the first item in the array returned from coerce then throw missing method error. + /// + public static object CoerceAndCallCompare(RubyContext/*!*/ context, object self, object other) { + RubyArray coercedValues; + try { + // Swap self and other around to do the coercion. + coercedValues = RubySites.Coerce(context, other, self); + } catch (Exception) { + // The coercion failed so return null. + return null; + } + // This call is outside the try block as we do want problems with this call being raised up. + return LibrarySites.Compare(context, coercedValues[0], coercedValues[1]); + } + + /// + /// Compare the values of self and other (coerce using other as the object then call the passed in comparison operator). + /// + /// + /// This method is used for <, <=, > and >= operators. If we can't coerce then throw a specific comparison exception. + /// + /// If self and other cannot be coerced to the same type. + public static bool CoerceAndCallRelationOperator(RubyContext/*!*/ context, object self, object other, DynamicInvocation invoke) { + try { + // Swap self and other around to do the coercion. + RubyArray coercedValues = RubySites.Coerce(context, other, self); + return RubyOps.IsTrue(invoke(context, coercedValues[0], coercedValues[1])); + } catch (MemberAccessException x) { + throw RubyExceptions.MakeComparisonError(context, self, other, x); + } catch (ArgumentException x) { + throw RubyExceptions.MakeComparisonError(context, self, other, x); + } catch (NullReferenceException x) { + throw RubyExceptions.MakeComparisonError(context, self, other, x); + } + } + + #endregion + + #region Range + + public static void ConvertToIntegerRange(RubyContext/*!*/ context, Range/*!*/ range, out int begin, out int end, out bool excludeEnd) { + begin = Protocols.CastToFixnum(context, RubySites.RangeBegin(context, range)); + end = Protocols.CastToFixnum(context, RubySites.RangeEnd(context, range)); + excludeEnd = RubySites.RangeExcludeEnd(context, range); + } + + #endregion + + #region CLR Types + + public static Type[]/*!*/ ToTypes(RubyContext/*!*/ context, object[]/*!*/ values) { + Type[] args = new Type[values.Length]; + for (int i = 0; i < args.Length; i++) { + args[i] = ToType(context, values[i]); + } + + return args; + } + + public static Type/*!*/ ToType(RubyContext/*!*/ context, object value) { + TypeTracker tt = value as TypeTracker; + if (tt != null) { + return tt.Type; + } + + RubyClass rc = value as RubyClass; + if (rc != null) { + return rc.GetUnderlyingSystemType(); + } + + throw RubyExceptions.InvalidValueForType(context, value, "Class"); + } + + #endregion + + #region Security + + public static void CheckSafeLevel(RubyContext/*!*/ context, int level) { + if (level <= context.CurrentSafeLevel) { + throw RubyExceptions.CreateSecurityError("Insecure operation at level " + context.CurrentSafeLevel); + } + } + public static void CheckSafeLevel(RubyContext/*!*/ context, int level, string/*!*/ method) { + if (level <= context.CurrentSafeLevel) { + throw RubyExceptions.CreateSecurityError(String.Format("Insecure operation {0} at level {1}", method, context.CurrentSafeLevel)); + } + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/StringIO/StringIO.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/StringIO/StringIO.cs new file mode 100644 index 0000000000..bedc0b3a6b --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/StringIO/StringIO.cs @@ -0,0 +1,101 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Runtime.InteropServices; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Actions; +using IronRuby.Builtins; +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.StringIO { + + [RubyClass("StringIO")] + public class StringIO : RubyIO { + + protected StringIO(RubyContext/*!*/ context, MutableStringStream/*!*/ stream, string/*!*/ mode) + : base(context, stream, mode) { + } + + protected MutableStringStream Data { + get { + MutableStringStream stream = (this.Stream as MutableStringStream); + if (stream == null) { + throw RubyExceptions.CreateArgumentError("stream is not a StringIO"); + } + return stream; + } + } + + // for derivation + protected StringIO() { } + + #region Public Singleton Methods + + [RubyConstructor] + public static RubyIO CreateIO(RubyClass/*!*/ self, [Optional]MutableString initialString, [Optional]MutableString mode) { + MutableStringStream stream = new MutableStringStream(initialString ?? MutableString.CreateBinary()); + string ioMode = (mode != null) ? mode.ConvertToString() : "rb+"; + return new StringIO(self.Context, stream, ioMode); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object OpenIO([NotNull]BlockParam/*!*/ block, RubyClass/*!*/ self, [Optional]MutableString initialString, [Optional]MutableString mode) { + MutableStringStream stream = new MutableStringStream(initialString ?? MutableString.CreateBinary()); + string ioMode = (mode != null) ? mode.ConvertToString() : "rb+"; + RubyIO io = new StringIO(self.Context, stream, ioMode); + + object result; + block.Yield(io, out result); + if (!io.Closed) { + io.Close(); + } + return result; + } + + #endregion + + #region Public Instance Methods + + [RubyMethod("length")] + [RubyMethod("size")] + public static int GetLength(StringIO/*!*/ self) { + return (int)self.Data.Length; + } + + [RubyMethod("path")] + public static object GetPath(StringIO/*!*/ self) { + return null; + } + + [RubyMethod("string")] + public static MutableString/*!*/ GetString(StringIO/*!*/ self) { + return self.Data.String; + } + + [RubyMethod("string=")] + public static MutableString/*!*/ SetString(StringIO/*!*/ self, [NotNull]MutableString/*!*/ str) { + self.Data.String = str; + return str; + } + + [RubyMethod("truncate")] + public static int SetLength(StringIO/*!*/ self, int length) { + self.Data.SetLength(length); + return length; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/StringScanner/StringScanner.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/StringScanner/StringScanner.cs new file mode 100644 index 0000000000..2bec036581 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/StringScanner/StringScanner.cs @@ -0,0 +1,487 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Runtime.InteropServices; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Text; +using System.Text.RegularExpressions; +using IronRuby.Builtins; +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.StringScanner { + + [RubyClass("StringScanner")] + public sealed class StringScanner : RubyObject { + private MutableString/*!*/ _scanString; + private int _previousPosition; + private int _currentPosition; + private int _foundPosition; + private MutableString _lastMatch; + private GroupCollection _lastMatchingGroups; + + #region Construction + + public StringScanner(RubyClass/*!*/ rubyClass) + : base(rubyClass) { + _scanString = MutableString.Empty; + } + + protected override RubyObject/*!*/ CreateInstance() { + return new StringScanner(Class); + } + + private void InitializeFrom(StringScanner/*!*/ other) { + _currentPosition = other._currentPosition; + _foundPosition = other._foundPosition; + _lastMatch = other._lastMatch; + _lastMatchingGroups = other._lastMatchingGroups; + _previousPosition = other._previousPosition; + _scanString = other.ScanString; + } + + [RubyConstructor] + public static StringScanner/*!*/ Create(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ scan) { + var result = new StringScanner(self); + result.ScanString = scan; + result.Reset(); + return result; + } + + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static void Reinitialize(StringScanner/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ scan) { + self.ScanString = scan; + self.Reset(); + } + + [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)] + public static void InitializeFrom(StringScanner/*!*/ self, [DefaultProtocol, NotNull]StringScanner/*!*/ other) { + self.InitializeFrom(other); + } + + #endregion + + #region Singleton Methods + + /// + /// This method is defined for backwards compatibility + /// + [RubyMethod("must_C_version", RubyMethodAttributes.PublicSingleton)] + public static object MustCVersion(object self) { + return self; + } + + #endregion + + #region Public Instance Methods + + [RubyMethod("<<")] + [RubyMethod("concat")] + public static StringScanner Concat(StringScanner/*!*/ self, MutableString str) { + self.ScanString.Append(str); + return self; + } + + [RubyMethod("[]")] + public static MutableString GetMatchSubgroup(StringScanner/*!*/ self, int subgroup) { + if (subgroup == 0 && self.LastMatch != null) { + return MutableString.Create(self.LastMatch); + } + if (self.LastMatchingGroups == null) { + return null; + } + if (subgroup < 0) { + subgroup = self.LastMatchingGroups.Count - subgroup; + } + if (subgroup >= self.LastMatchingGroups.Count) { + return null; + } + return MutableString.Create(self.LastMatchingGroups[subgroup].ToString()); + } + + [RubyMethod("beginning_of_line?")] + [RubyMethod("bol?")] + public static bool BeginningOfLine(StringScanner/*!*/ self) { + return (self.CurrentPosition == 0) || (self.ScanString.GetChar(self.CurrentPosition - 1) == '\n'); + } + + [RubyMethod("check")] + public static MutableString Check(StringScanner/*!*/ self, [NotNull]RubyRegex/*!*/ pattern) { + return (ScanFull(self, pattern, false, true) as MutableString); + } + + [RubyMethod("check_until")] + public static MutableString CheckUntil(StringScanner/*!*/ self, [NotNull]RubyRegex/*!*/ pattern) { + return (SearchFull(self, pattern, false, true) as MutableString); + } + + [RubyMethod("empty?")] + [RubyMethod("eos?")] + public static bool EndOfLine(StringScanner/*!*/ self) { + return self.CurrentPosition >= self.Length; + } + + [RubyMethod("exist?")] + public static int? Exist(StringScanner/*!*/ self, [NotNull]RubyRegex/*!*/ pattern) { + if (!self.Match(pattern, false, false)) { + return null; + } + return self.FoundPosition + self.LastMatch.Length; + } + + [RubyMethod("get_byte")] + [RubyMethod("getbyte")] + public static MutableString GetByte(StringScanner/*!*/ self) { + if (self.CurrentPosition >= self.Length) { + return null; + } + self.PreviousPosition = self.CurrentPosition; + self.FoundPosition = self.CurrentPosition; + self.LastMatch = self.ScanString.GetSlice(self.CurrentPosition++, 1); + return MutableString.Create(self.LastMatch); + } + + [RubyMethod("getch")] + public static MutableString GetChar(StringScanner/*!*/ self) { + if (self.CurrentPosition >= self.Length) { + return null; + } + self.PreviousPosition = self.CurrentPosition; + self.FoundPosition = self.CurrentPosition; + self.LastMatch = self.ScanString.GetSlice(self.CurrentPosition++, 1); + return MutableString.Create(self.LastMatch); + } + + [RubyMethod("inspect")] + [RubyMethod("to_s")] + public static MutableString ToString(StringScanner/*!*/ self) { + return MutableString.Create(self.ToString()); + } + + [RubyMethod("match?")] + public static int? Match(StringScanner/*!*/ self, [NotNull]RubyRegex/*!*/ pattern) { + if (!self.Match(pattern, true, false)) { + return null; + } + return self.LastMatch.GetLength(); + } + + [RubyMethod("matched")] + public static MutableString Matched(StringScanner/*!*/ self) { + if (self.LastMatch == null) { + return null; + } + return MutableString.Create(self.LastMatch); + } + + [RubyMethod("matched?")] + public static bool WasMatched(StringScanner/*!*/ self) { + return (self.LastMatch != null); + } + + [RubyMethod("matched_size")] + [RubyMethod("matchedsize")] + public static int? MatchedSize(StringScanner/*!*/ self) { + if (self.LastMatch == null) { + return null; + } + return self.LastMatch.Length; + } + + [RubyMethod("peek")] + [RubyMethod("peep")] + public static MutableString Peek(StringScanner/*!*/ self, int len) { + if (len < 0) { + throw RubyExceptions.CreateArgumentError("negative string size (or size too big)"); + } + int maxlen = self.Length - self.CurrentPosition; + if (len > maxlen) { + len = maxlen; + } + if (self.CurrentPosition >= self.Length || len == 0) { + return MutableString.CreateMutable(); + } + return self.ScanString.GetSlice(self.CurrentPosition, len); + } + + [RubyMethod("pos")] + [RubyMethod("pointer")] + public static int GetCurrentPosition(StringScanner/*!*/ self) { + return self.CurrentPosition; + } + + [RubyMethod("pos=")] + [RubyMethod("pointer=")] + public static int SetCurrentPosition(StringScanner/*!*/ self, int newPosition) { + int newPos = newPosition; + if (newPos < 0) { + newPos = self.Length - self.CurrentPosition; + } + if (newPos > self.Length) { + throw RubyExceptions.CreateRangeError("index out of range"); + } + self.CurrentPosition = newPos; + return newPosition; + } + + [RubyMethod("post_match")] + public static MutableString PostMatch(StringScanner/*!*/ self) { + if (self.LastMatch == null) { + return null; + } + int position = self.FoundPosition + self.LastMatch.Length; + int len = self.Length - position; + if (len <= 0) { + return MutableString.CreateMutable(); + } + return self.ScanString.GetSlice(position, len); + } + + [RubyMethod("pre_match")] + public static MutableString PreMatch(StringScanner/*!*/ self) { + if (self.LastMatch == null) { + return null; + } + return self.ScanString.GetSlice(0, self.FoundPosition); + } + + [RubyMethod("reset")] + public static StringScanner Reset(StringScanner/*!*/ self) { + self.Reset(); + return self; + } + + [RubyMethod("rest")] + public static MutableString Rest(StringScanner/*!*/ self) { + int len = self.Length - self.CurrentPosition; + if (len <= 0) { + return MutableString.CreateMutable(); + } + return self.ScanString.GetSlice(self.CurrentPosition, len); + } + + [RubyMethod("rest?")] + public static bool IsRestLeft(StringScanner/*!*/ self) { + return self.CurrentPosition < self.Length; + } + + [RubyMethod("rest_size")] + [RubyMethod("restsize")] + public static int RestSize(StringScanner/*!*/ self) { + return (self.CurrentPosition < self.Length) ? (self.Length - self.CurrentPosition) : 0; + } + + [RubyMethod("scan")] + public static MutableString Scan(StringScanner/*!*/ self, [NotNull]RubyRegex/*!*/ pattern) { + return (ScanFull(self, pattern, true, true) as MutableString); + } + + [RubyMethod("scan_full")] + public static object ScanFull(StringScanner/*!*/ self, [NotNull]RubyRegex/*!*/ pattern, bool advance_pointer_p, bool return_string_p) { + bool match = self.Match(pattern, true, advance_pointer_p); + object result = null; + if (match) { + if (return_string_p) { + result = MutableString.Create(self.LastMatch); + } else { + result = self.LastMatch.Length; + } + } + return result; + } + + [RubyMethod("scan_until")] + public static MutableString ScanUntil(StringScanner/*!*/ self, [NotNull]RubyRegex/*!*/ pattern) { + return (SearchFull(self, pattern, true, true) as MutableString); + } + + [RubyMethod("search_full")] + public static object SearchFull(StringScanner/*!*/ self, [NotNull]RubyRegex/*!*/ pattern, bool advance_pointer_p, bool return_string_p) { + bool match = self.Match(pattern, false, advance_pointer_p); + object result = null; + if (match) { + int length = self.LastMatch.Length + (self.FoundPosition - self.PreviousPosition); + if (return_string_p) { + result = self.ScanString.GetSlice(self.PreviousPosition, length); + } else { + result = length; + } + } + return result; + } + + [RubyMethod("skip")] + public static int? Skip(StringScanner/*!*/ self, [NotNull]RubyRegex/*!*/ pattern) { + bool match = self.Match(pattern, true, true); + if (!match) { + return null; + } + return (self.CurrentPosition - self.PreviousPosition); + } + + [RubyMethod("skip_until")] + public static int? SkipUntil(StringScanner/*!*/ self, [NotNull]RubyRegex/*!*/ pattern) { + bool match = self.Match(pattern, false, true); + if (!match) { + return null; + } + return (self.CurrentPosition - self.PreviousPosition); + } + + [RubyMethod("string")] + public static MutableString GetString(StringScanner/*!*/ self) { + return self.ScanString; + } + + [RubyMethod("string=")] + public static MutableString SetString(RubyContext/*!*/ context, StringScanner/*!*/ self, [NotNull]MutableString/*!*/ str) { + self.ScanString = (MutableString)KernelOps.Freeze(context, MutableString.Create(str)); + self.Reset(); + return str; + } + + [RubyMethod("clear")] + [RubyMethod("terminate")] + public static StringScanner Clear(StringScanner/*!*/ self) { + self.Reset(); + self.CurrentPosition = self.Length; + return self; + } + + [RubyMethod("unscan")] + public static StringScanner Unscan(StringScanner/*!*/ self) { + if (self.LastMatch == null) { + // throw Exception StringScanner::Error + throw RubyExceptions.CreateRangeError("unscan failed: previous match had failed"); + } + int position = self.PreviousPosition; + self.Reset(); + self.CurrentPosition = position; + return self; + } + #endregion + + #region Helpers + + private bool Match(RubyRegex/*!*/ pattern, bool currentPositionOnly, bool advancePosition) { + Match match = pattern.Match(_scanString, _currentPosition); + _lastMatch = null; + _lastMatchingGroups = null; + _foundPosition = 0; + if (!match.Success) { + return false; + } + if (currentPositionOnly && match.Index != _currentPosition) { + return false; + } + int length = (match.Index - _currentPosition) + match.Length; + _foundPosition = match.Index; + _previousPosition = _currentPosition; + _lastMatch = _scanString.GetSlice(_foundPosition, match.Length); + _lastMatchingGroups = match.Groups; + if (advancePosition) { + _currentPosition += length; + } + return true; + } + + #endregion + + + private int PreviousPosition { + get { return _previousPosition; } + set { _previousPosition = value; } + } + + private int CurrentPosition { + get { return _currentPosition; } + set { _currentPosition = value; } + } + + private int Length { + get { return _scanString.Length; } + } + + private MutableString ScanString { + get { return _scanString; } + set { _scanString = value; } + } + + private int FoundPosition { + get { return _foundPosition; } + set { _foundPosition = value; } + } + + private MutableString LastMatch { + set { _lastMatch = value; } + get { return _lastMatch; } + } + + private GroupCollection LastMatchingGroups { + get { return _lastMatchingGroups; } + } + + private void Reset() { + _previousPosition = 0; + _currentPosition = 0; + _foundPosition = 0; + _lastMatch = null; + _lastMatchingGroups = null; + } + + public override string ToString() { + // # + byte[] scanstr = ScanString.ToByteArray(); + StringBuilder sb = new StringBuilder("#= Length || CurrentPosition < 0) { + sb.Append("fin >"); + return sb.ToString(); + } + + sb.AppendFormat("{0}/{1}", CurrentPosition, scanstr.Length); + if (CurrentPosition > 0) { + sb.Append(" \""); + int len = CurrentPosition; + if (len > 5) { + len = 5; + sb.Append("..."); + } + for (int i = CurrentPosition - len; i < CurrentPosition; i++) { + MutableStringOps.AppendStringRepresentationOfChar(sb, scanstr[i], -1, true); + } + sb.Append('"'); + } + sb.Append(" @ "); + if (CurrentPosition < scanstr.Length) { + int len = scanstr.Length - CurrentPosition; + bool ellipsis = false; + if (len > 5) { + len = 5; + ellipsis = true; + } + sb.Append('"'); + for (int i = CurrentPosition; i < CurrentPosition + len; i++) { + MutableStringOps.AppendStringRepresentationOfChar(sb, scanstr[i], -1, true); + } + if (ellipsis) { + sb.Append("..."); + } + sb.Append('"'); + } + sb.Append('>'); + return sb.ToString(); + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Thread/RubyConditionVariable.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Thread/RubyConditionVariable.cs new file mode 100644 index 0000000000..6b0b3ec0a4 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Thread/RubyConditionVariable.cs @@ -0,0 +1,57 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Threading; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.Threading { + [RubyClass("ConditionVariable")] + public class RubyConditionVariable { + private RubyMutex _mutex; + + public RubyConditionVariable() { + } + + [RubyMethod("signal")] + public static RubyConditionVariable/*!*/ Signal(RubyConditionVariable/*!*/ self) { + RubyMutex m = self._mutex; + if (m != null) { + Monitor.Pulse(m); + } + return self; + } + + [RubyMethod("broadcast")] + public static RubyConditionVariable/*!*/ Broadcast(RubyConditionVariable/*!*/ self) { + RubyMutex m = self._mutex; + if (m != null) { + Monitor.PulseAll(m); + } + return self; + } + + [RubyMethod("wait")] + public static RubyConditionVariable/*!*/ Wait(RubyConditionVariable/*!*/ self, [NotNull]RubyMutex/*!*/ mutex) { + self._mutex = mutex; + Monitor.Wait(mutex.Mutex); + return self; + } + + // TODO: + // "marshal_load" + // "marshal_dump" + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Thread/RubyMutex.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Thread/RubyMutex.cs new file mode 100644 index 0000000000..92a9a0d206 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Thread/RubyMutex.cs @@ -0,0 +1,88 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Runtime; +using System.Threading; +using Microsoft.Scripting.Actions; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +namespace IronRuby.StandardLibrary.Threading { + // TODO: + // Ruby mutex is not recursive. + // It can be unlocked from non-owning thread. + [RubyClass("Mutex")] + public class RubyMutex { + private readonly object/*!*/ _mutex; + + // TODO: this is not precise. + private bool _isLocked; + + internal object Mutex { get { return _mutex; } } + + public RubyMutex() { + _mutex = new object(); + } + + [RubyMethod("locked?")] + public static bool IsLocked(RubyMutex/*!*/ self) { + return self._isLocked; + } + + [RubyMethod("try_lock")] + public static bool TryLock(RubyMutex/*!*/ self) { + return self._isLocked = Monitor.TryEnter(self._mutex); + } + + [RubyMethod("lock")] + public static RubyMutex/*!*/ Lock(RubyMutex/*!*/ self) { + Monitor.Enter(self._mutex); + self._isLocked = true; + return self; + } + + [RubyMethod("unlock")] + public static RubyMutex/*!*/ Unlock(RubyMutex/*!*/ self) { + self._isLocked = false; + Monitor.Exit(self._mutex); + return self; + } + + [RubyMethod("synchronize")] + public static object Synchronize(BlockParam criticalSection, RubyMutex/*!*/ self) { + lock (self._mutex) { + self._isLocked = true; + try { + object result; + criticalSection.Yield(out result); + return result; + } finally { + self._isLocked = false; + } + } + } + + [RubyMethod("exclusive_unlock")] + public static bool ExclusiveUnlock(BlockParam criticalSection, RubyMutex/*!*/ self) { + // TODO: + throw new NotImplementedException(); + } + + // TODO: + // "marshal_load" + // "marshal_dump" + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Thread/RubyQueue.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Thread/RubyQueue.cs new file mode 100644 index 0000000000..3edc4576ec --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Thread/RubyQueue.cs @@ -0,0 +1,115 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; +using IronRuby.Runtime; +using IronRuby.Builtins; + +namespace IronRuby.StandardLibrary.Threading { + // Synchronized queue. + [RubyClass("Queue")] + public class RubyQueue { + protected readonly Queue/*!*/ _queue; + protected int _waiting; + + public RubyQueue() { + _queue = new Queue(); + } + + protected RubyQueue(int capacity) { + _queue = new Queue(capacity); + } + + private void Enqueue(object value) { + lock (_queue) { + _queue.Enqueue(value); + Monitor.PulseAll(_queue); + } + } + + protected object Dequeue() { + object value; + lock (_queue) { + _waiting++; + + try { + while (_queue.Count == 0) { + Monitor.Wait(_queue); + } + } finally { + _waiting--; + } + value = _queue.Dequeue(); + Monitor.PulseAll(_queue); + } + return value; + } + + [RubyMethod("enq")] + [RubyMethod("push")] + [RubyMethod("<<")] + public static RubyQueue/*!*/ Enqueue(RubyQueue/*!*/ self, object value) { + self.Enqueue(value); + return self; + } + + [RubyMethod("deq")] + [RubyMethod("pop")] + [RubyMethod("shift")] + public static object Dequeue(RubyQueue/*!*/ self, [Optional]bool nonBlocking) { + if (nonBlocking) { + lock (self._queue) { + if (self._queue.Count == 0) { + throw new ThreadError("queue empty"); + } + return self._queue.Dequeue(); + } + } + return self.Dequeue(); + } + + [RubyMethod("size")] + [RubyMethod("length")] + public static int GetCount(RubyQueue/*!*/ self) { + lock (self._queue) { + return self._queue.Count; + } + } + + [RubyMethod("clear")] + public static RubyQueue/*!*/ Clear(RubyQueue/*!*/ self) { + lock (self._queue) { + self._queue.Clear(); + } + return self; + } + + [RubyMethod("empty?")] + public static bool IsEmpty(RubyQueue/*!*/ self) { + return GetCount(self) == 0; + } + + [RubyMethod("num_waiting")] + public static int GetNumberOfWaitingThreads(RubyQueue/*!*/ self) { + return self._waiting; + } + + // TODO: + // "marshal_load" + // "marshal_dump" + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Thread/SizedQueue.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Thread/SizedQueue.cs new file mode 100644 index 0000000000..6cfb65d321 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Thread/SizedQueue.cs @@ -0,0 +1,88 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.Scripting.Runtime; +using System.Threading; +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.Threading { + // Synchronized queue. + [RubyClass] + public class SizedQueue : RubyQueue { + private int _limit; + + public SizedQueue([DefaultProtocol]int limit) { + _limit = limit; + } + + public SizedQueue() { + } + + private void Enqueue(object value) { + lock (_queue) { + _waiting++; + try { + while (_queue.Count == _limit) { + Monitor.Wait(_queue); + } + } finally { + _waiting--; + } + _queue.Enqueue(value); + Debug.Assert(_queue.Count <= _limit); + + Monitor.PulseAll(_queue); + } + } + + + [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] + public static SizedQueue/*!*/ Reinitialize(SizedQueue/*!*/ self, [DefaultProtocol]int limit) { + SetLimit(self, limit); + return self; + } + + [RubyMethod("max")] + public static int GetLimit(SizedQueue/*!*/ self) { + return self._limit; + } + + [RubyMethod("max=")] + public static void SetLimit(SizedQueue/*!*/ self, [DefaultProtocol]int limit) { + self._limit = limit; + } + + [RubyMethod("enq")] + [RubyMethod("push")] + [RubyMethod("<<")] + public static SizedQueue/*!*/ Enqueue(SizedQueue/*!*/ self, object value) { + self.Enqueue(value); + return self; + } + + [RubyMethod("deq")] + [RubyMethod("pop")] + [RubyMethod("shift")] + public static object Dequeue(SizedQueue/*!*/ self, [NotNull]params object[]/*!*/ values) { + // TODO: + if (values.Length != 0) { + throw new NotImplementedException(); + } + return self.Dequeue(); + } + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Zlib/zlib.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Zlib/zlib.cs new file mode 100644 index 0000000000..cb6cd2144a --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/Zlib/zlib.cs @@ -0,0 +1,768 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using IronRuby.Builtins; +using IronRuby.Runtime; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.StandardLibrary.Zlib { + + [RubyModule("Zlib")] + public static class Zlib { + + #region Constants + + [RubyConstant("ZLIB_VERSION")] + public static string ZLIB_VERSION = "1.2.3"; + + [RubyConstant("VERSION")] + public static string VERSION = "0.6.0"; + + [RubyConstant("MAXBITS")] + public const int MAXBITS = 15; + + [RubyConstant("MAXLCODES")] + public const int MAXLCODES = 286; + + [RubyConstant("MAXDCODES")] + public const int MAXDCODES = 30; + + [RubyConstant("MAXCODES")] + public const int MAXCODES = (MAXLCODES + MAXDCODES); + + [RubyConstant("FIXLCODES")] + public const int FIXLCODES = 288; + + [RubyConstant("MAX_WBITS")] + public const int MAX_WBITS = 15; + + [RubyConstant("Z_DEFLATED")] + public const int Z_DEFLATED = 8; + + #endregion + + #region ZStream class + + [RubyClass("ZStream")] + public class ZStream { + protected readonly List/*!*/ _inputBuffer; + protected readonly List/*!*/ _outputBuffer; + protected int _outPos = -1; + protected int _inPos = -1; + protected byte _bitBucket = 0; + protected byte _bitCount = 0; + protected bool _closed = false; + + public ZStream() { + _outPos = -1; + _inPos = -1; + _bitBucket = 0; + _bitCount = 0; + _inputBuffer = new List(); + _outputBuffer = new List(); + } + + #region instance methods + public bool Close() { + _closed = true; + return _closed; + } + #endregion + + [RubyMethod("adler")] + public static int Adler(ZStream/*!*/ self) { + throw new NotImplementedError(); + } + + [RubyMethod("avail_in")] + public static int AvailIn(ZStream/*!*/ self) { + return self._inputBuffer.Count - self._inPos; + } + + [RubyMethod("avail_out")] + public static int GetAvailOut(ZStream/*!*/ self) { + return self._outputBuffer.Count - self._outPos; + } + + [RubyMethod("avail_out=")] + public static int SetAvailOut(ZStream/*!*/ self, int size) { + self._outputBuffer.Capacity = size; + return self._outputBuffer.Count; + } + + [RubyMethod("finish")] + [RubyMethod("close")] + public static bool Close(ZStream/*!*/ self) { + return self.Close(); + } + + [RubyMethod("stream_end?")] + [RubyMethod("finished?")] + [RubyMethod("closed?")] + public static bool IsClosed(ZStream/*!*/ self) { + return self._closed; + } + + [RubyMethod("data_type")] + public static void DataType(ZStream/*!*/ self) { + throw new NotImplementedException(); + } + + [RubyMethod("flush_next_in")] + public static List FlushNextIn(ZStream/*!*/ self) { + self._inPos = self._inputBuffer.Count; + return self._inputBuffer; + } + + [RubyMethod("flush_next_out")] + public static List FlushNextOut(ZStream/*!*/ self) { + self._outPos = self._outputBuffer.Count; + return self._outputBuffer; + } + + [RubyMethod("reset")] + public static void Reset(ZStream/*!*/ self) { + self._outPos = -1; + self._inPos = -1; + self._inputBuffer.Clear(); + self._outputBuffer.Clear(); + } + + [RubyMethod("total_in")] + public static int TotalIn(ZStream/*!*/ self) { + return self._inputBuffer.Count; + } + + [RubyMethod("total_out")] + public static int TotalOut(ZStream/*!*/ self) { + return self._outputBuffer.Count; + } + + protected int GetBits(int need) { + int val = _bitBucket; + while (_bitCount < need) { + val |= (int)(_inputBuffer[++_inPos] << _bitCount); + _bitCount += 8; + } + + _bitBucket = (byte)(val >> need); + _bitCount -= (byte)need; + return (val & ((1 << need) - 1)); + } + } + + #endregion + + #region Inflate class + + [RubyClass("Inflate")] + public class Inflate : ZStream { + private int _wBits; + private bool _rawDeflate; + private HuffmanTree _fixedLengthCodes; + private HuffmanTree _fixedDistanceCodes; + private HuffmanTree _dynamicLengthCodes; + private HuffmanTree _dynamicDistanceCodes; + + [RubyConstructor] + public static Inflate/*!*/ Create(RubyClass/*!*/ self) { + return new Inflate(MAX_WBITS); + } + + [RubyConstructor] + public static Inflate/*!*/ Create(RubyClass/*!*/ self, int windowBits) { + return new Inflate(windowBits); + } + + public Inflate(int windowBits) { + _wBits = windowBits; + if (_wBits < 0) { + _rawDeflate = true; + _wBits *= -1; + } + } + + [RubyMethod("inflate")] + public static MutableString/*!*/ InflateStream(Inflate/*!*/ self, MutableString/*!*/ zstring) { + self._inputBuffer.AddRange(zstring.ConvertToBytes()); + + if (self._rawDeflate == false) { + byte compression_method_and_flags = self._inputBuffer[++(self._inPos)]; + byte flags = self._inputBuffer[++(self._inPos)]; + if (((compression_method_and_flags << (byte)0x08) + flags) % (byte)31 != 0) { + throw new DataError("incorrect header check"); + } + + byte compression_method = (byte)(compression_method_and_flags & (byte)0x0F); + if (compression_method != Z_DEFLATED) { + throw new DataError("unknown compression method"); + } + + byte compression_info = (byte)(compression_method_and_flags >> (byte)0x04); + if ((compression_info + 8) > self._wBits) { + throw new DataError("invalid window size"); + } + + bool preset_dictionary_flag = ((flags & 0x20) >> 0x05 == 1); + byte compression_level = (byte)((flags & 0xC0) >> (byte)0x06); + + //TODO: Add Preset Dictionary Support + if (preset_dictionary_flag) { + self._inPos += 4; + } + } + + bool last_block = false; + + while (!last_block) { + last_block = (self.GetBits(1) == 1); + byte block_type = (byte)self.GetBits(2); + switch (block_type) { + case 0: + self.NoCompression(); + break; + case 1: + self.FixedCodes(); + break; + case 2: + self.DynamicCodes(); + break; + case 3: + throw new DataError("invalid block type"); + } + } + + return Inflate.Close(self); + } + + private void DynamicCodes() { + byte[] order = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + int nlen = (int)GetBits(5) + 257; + int ndist = (int)GetBits(5) + 1; + int ncode = (int)GetBits(4) + 4; + + List lengths = new List(); + + _dynamicLengthCodes = new HuffmanTree(); + _dynamicDistanceCodes = new HuffmanTree(); + + if (nlen > MAXLCODES || ndist > MAXDCODES) { + throw new DataError("too many length or distance codes"); + } + + int idx = 0; + + while (idx < ncode) { + SetOrExpand(lengths, order[idx], GetBits(3)); + idx++; + } + + while (idx < 19) { + SetOrExpand(lengths, order[idx], 0); + idx++; + } + + int err = ConstructTree(_dynamicLengthCodes, lengths, 18); + if (err != 0) { + throw new DataError("code lengths codes incomplete"); + } + + idx = 0; + + while (idx < (nlen + ndist)) { + int symbol = Decode(_dynamicLengthCodes); + if (symbol < 16) { + SetOrExpand(lengths, idx, symbol); + idx++; + } else { + int len = 0; + if (symbol == 16) { + if (idx == 0) { + throw new DataError("repeat lengths with no first length"); + } + len = lengths[idx - 1]; + symbol = 3 + (int)GetBits(2); + } else if (symbol == 17) { + symbol = 3 + (int)GetBits(3); + } else if (symbol == 18) { + symbol = 11 + (int)GetBits(7); + } else { + throw new DataError("invalid repeat length code"); + } + + if ((idx + symbol) > (nlen + ndist)) { + throw new DataError("repeat more than specified lengths"); + } + + while (symbol != 0) { + SetOrExpand(lengths, idx, len); + idx++; + symbol--; + } + } + } + + err = ConstructTree(_dynamicLengthCodes, lengths, nlen - 1); + if (err < 0 || (err > 0 && (nlen - _dynamicLengthCodes.Count[0] != 1))) { + throw new DataError("invalid literal/length code lengths"); + } + + lengths.RemoveRange(0, nlen); + + err = ConstructTree(_dynamicDistanceCodes, lengths, ndist - 1); + if (err < 0 || (err > 0 && (ndist - _dynamicDistanceCodes.Count[0] != 1))) { + throw new DataError("invalid distance code lengths"); + } + + Codes(_dynamicLengthCodes, _dynamicDistanceCodes); + } + + [RubyMethod("close")] + public static MutableString/*!*/ Close(Inflate/*!*/ self) { + return MutableString.CreateBinary(self._outputBuffer); + } + + private void NoCompression() { + _bitBucket = 0; + _bitCount = 0; + + if (_inPos + 4 > _inputBuffer.Count) { + throw new DataError("not enough input to read length code"); + } + + int length = (int)(_inputBuffer[++_inPos] | (_inputBuffer[++_inPos] << 8)); + int lengthComplement = (int)(_inputBuffer[++_inPos] | (_inputBuffer[++_inPos] << 8)); + if (unchecked((ushort)length) != unchecked((ushort)(~lengthComplement))) { + throw new DataError("invalid stored block lengths"); + } + + if (_inPos + length > _inputBuffer.Count) { + throw new DataError("ran out of input"); + } + + _outputBuffer.AddRange(_inputBuffer.GetRange(_inPos + 1, length)); + _inPos += length; + _outPos += length; + } + + private void FixedCodes() { + if (_fixedLengthCodes == null && _fixedDistanceCodes == null) { + GenerateHuffmans(); + } + Codes(_fixedLengthCodes, _fixedDistanceCodes); + } + + private void GenerateHuffmans() { + List lengths = new List(300); + int x = 0; + for (; x < 144; x++) { + lengths.Add(8); + } + for (; x < 256; x++) { + lengths.Add(9); + } + for (; x < 280; x++) { + lengths.Add(7); + } + for (; x < 288; x++) { + lengths.Add(8); + } + _fixedLengthCodes = new HuffmanTree(); + ConstructTree(_fixedLengthCodes, lengths, 287); + + lengths.Clear(); + + for (int y = 0; y < 30; y++) { + lengths.Add(5); + } + + _fixedDistanceCodes = new HuffmanTree(); + ConstructTree(_fixedDistanceCodes, lengths, 29); + } + + private int ConstructTree(HuffmanTree/*!*/ tree, List/*!*/ lengths, int symbols) { + List offs = new List(); + + for (int x = 0; x <= MAXBITS; x++) { + SetOrExpand(tree.Count, x, 0); + } + + for (int y = 0; y <= symbols; y++) { + (tree.Count[lengths[y]])++; + } + + if (tree.Count[0] == symbols) { + return 0; + } + + int left = 1; + for (int y = 1; y <= MAXBITS; y++) { + left <<= 1; + left -= tree.Count[y]; + if (left < 0) { + return left; + } + } + + offs.Add(0); + offs.Add(0); + + for (int len = 1; len <= MAXBITS - 1; len++) { + offs.Add(0); + offs[len + 1] = offs[len] + tree.Count[len]; + } + + for (int symbol = 0; symbol <= symbols; symbol++) { + if (lengths[symbol] != 0) { + SetOrExpand(tree.Symbol, offs[lengths[symbol]], symbol); + offs[lengths[symbol]]++; + } + } + + return left; + } + + private void SetOrExpand(List/*!*/ list, int index, T item) { + int minCount = index + 1; + int expand = minCount - list.Count; + while (expand > 0) { + list.Add(default(T)); + expand--; + } + list[index] = item; + } + + private int Codes(HuffmanTree/*!*/ lengthCodes, HuffmanTree/*!*/ distanceCodes) { + int[] lens = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 }; + int[] lext = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + int[] dists = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 }; + int[] dext = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + + int symbol = 0; + while (symbol != 256) { + symbol = Decode(lengthCodes); + if (symbol < 0) { + return symbol; + } + if (symbol < 256) { + SetOrExpand(_outputBuffer, ++_outPos, (byte)symbol); + } + if (symbol > 256) { + symbol -= 257; + if (symbol >= 29) { + throw new DataError("invalid literal/length or distance code in fixed or dynamic block"); + } + int len = lens[symbol] + GetBits((byte)lext[symbol]); + symbol = Decode(distanceCodes); + if (symbol < 0) { + return symbol; + } + int dist = dists[symbol] + GetBits((byte)dext[symbol]); + if (dist > _outputBuffer.Count) { + throw new DataError("distance is too far back in fixed or dynamic block"); + } + while (len > 0) { + SetOrExpand(_outputBuffer, ++_outPos, _outputBuffer[_outPos - dist]); + len--; + } + } + } + + return 0; + } + + private int Decode(HuffmanTree/*!*/ tree) { + int code = 0; + int first = 0; + int index = 0; + for (int len = 1; len <= 15; len++) { + code |= GetBits(1); + int count = tree.Count[len]; + if (code < (first + count)) { + return tree.Symbol[index + (code - first)]; + } + index += count; + first += count; + first <<= 1; + code <<= 1; + } + + return -9; + } + + #region Dynamic Site + + private static CallSite> InflateSite = CallSite>.Create( + RubySites.InstanceCallAction("inflate", 1) + ); + + #endregion + + [RubyMethod("inflate", RubyMethodAttributes.PublicSingleton)] + public static MutableString InflateStream(RubyClass/*!*/ self, MutableString zstring) { + object obj = RubySites.Allocate(self); + return InflateSite.Target(InflateSite, self.Context, obj, zstring); + } + + internal class HuffmanTree { + internal List/*!*/ Count; + internal List/*!*/ Symbol; + + internal HuffmanTree() { + Count = new List(); + Symbol = new List(); + } + } + } + + #endregion + + #region GzipFile class + + [RubyClass("GzipFile")] + public class GZipFile { + protected List/*!*/ _inputBuffer; + protected List/*!*/ _outputBuffer; + protected int _outPos; + protected int _inPos; + + public GZipFile() { + _inputBuffer = new List(); + _outputBuffer = new List(); + _outPos = -1; + _inPos = -1; + } + + [RubyClass("Error")] + public class Error : RuntimeError { + public Error(string message) + : base(message) { + } + } + + // TODO: missing NoFooter, LengthError, CRCError constants + } + + #endregion + + #region GzipReader class + + /* [RubyClass("GzipReader"), Includes(typeof(Enumerable))] */ + [RubyClass("GzipReader")] + public class GZipReader : GZipFile { + + protected MutableString _xtraField; + protected MutableString/*!*/ _contents; + protected MutableString/*!*/ _originalName; + protected MutableString/*!*/ _comment; + protected ushort _headerCrc; + + [RubyMethod("xtra_field")] + public static MutableString ExtraField(GZipReader/*!*/ self) { + return self._xtraField; + } + + [RubyMethod("original_name")] + public static MutableString/*!*/ OriginalName(GZipReader/*!*/ self) { + return self._originalName; + } + + [RubyMethod("comment")] + public static MutableString/*!*/ Comment(GZipReader/*!*/ self) { + return self._comment; + } + + [RubyConstant("OSES")] + public static string[] OSES = { + "FAT filesystem", + "Amiga", + "VMS (or OpenVMS)", + "Unix", + "VM/CMS", + "Atari TOS", + "HPFS fileystem (OS/2, NT)", + "Macintosh", + "Z-System", + "CP/M", + "TOPS-20", + "NTFS filesystem (NT)", + "QDOS", + "Acorn RISCOS", + "unknown" + }; + + private bool IsBitSet(byte b, byte bit) { + return ((b & (1 << bit)) == (1 << bit)); + } + + [RubyConstructor] + public static GZipReader/*!*/ Create(RubyClass/*!*/ self, [NotNull]RubyIO/*!*/ io) { + using (BinaryReader reader = io.GetBinaryReader()) { + return new GZipReader(reader); + } + } + + [RubyConstructor] + public static GZipReader/*!*/ Create(RubyClass/*!*/ self, object io) { + Stream stream = null; + if (io != null) { + stream = new IOWrapper(self.Context, io, FileAccess.Read); + } + if (stream == null || !stream.CanRead) { + throw RubyExceptions.CreateTypeError("instance of IO needed"); + } + + using (BinaryReader reader = new BinaryReader(stream)) { + return new GZipReader(reader); + } + } + + private static ushort ReadUInt16LE(BinaryReader/*!*/ reader) { + return (ushort)( + (ushort)(reader.ReadByte()) | + (((ushort)(reader.ReadByte())) << 8) + ); + } + + private static uint ReadUInt32LE(BinaryReader/*!*/ reader) { + return (uint)( + (uint)(reader.ReadByte()) | + (((uint)(reader.ReadByte())) << 8) | + (((uint)(reader.ReadByte())) << 16) | + (((uint)(reader.ReadByte())) << 24) + ); + } + + private static MutableString/*!*/ ReadStringZ(BinaryReader/*!*/ reader) { + List result = new List(); + byte c; + while ((c = reader.ReadByte()) != 0) { + result.Add(c); + } + return MutableString.CreateBinary(result); + } + + private static MutableString/*!*/ ReadToEnd(BinaryReader/*!*/ reader) { + List result = new List(); + try { + while (true) { + result.Add(reader.ReadByte()); + } + } catch (EndOfStreamException) { + } + return MutableString.CreateBinary(result); + } + + public GZipReader(BinaryReader/*!*/ reader) + : base() { + + // TODO: should all of this code be moved to open()? + if (ReadUInt16LE(reader) != 0x8b1f) { + throw new Error("not in gzip format"); + } + if (reader.ReadByte() != 0x08) { + throw new Error("unknown compression method"); + } + + byte flg = reader.ReadByte(); + bool ftext = IsBitSet(flg, 0); + bool fhcrc = IsBitSet(flg, 1); + bool fextra = IsBitSet(flg, 2); + bool fname = IsBitSet(flg, 3); + bool fcomment = IsBitSet(flg, 4); + + uint secondsSince1970 = ReadUInt32LE(reader); + DateTime mtime = TimeOps.Create(null, (double)secondsSince1970); + byte xfl = reader.ReadByte(); + string os = GZipReader.OSES[reader.ReadByte()]; + if (fextra) { + int xlen = ReadUInt16LE(reader); + _xtraField = MutableString.CreateBinary(reader.ReadBytes(xlen)); + } + + if (fname) { + _originalName = ReadStringZ(reader); + } else { + _originalName = MutableString.CreateBinary(); + } + + if (fcomment) { + _comment = ReadStringZ(reader); + } else { + _comment = MutableString.CreateBinary(); + } + + if (fhcrc) { + _headerCrc = ReadUInt16LE(reader); + } + + _contents = ReadToEnd(reader); + } + + [RubyMethod("read")] + public static MutableString/*!*/ Read(GZipReader/*!*/ self) { + Inflate z = new Inflate(-MAX_WBITS); + return Inflate.InflateStream(z, self._contents); + } + + [RubyMethod("open", RubyMethodAttributes.PrivateInstance)] + public static GZipReader/*!*/ Open(GZipReader/*!*/ self) { + // TODO: Open as an private instance method probably doesn't create a new GzipReader, right? + // it probably returns nothing and is used internally to do all initialization + return self; + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static GZipReader/*!*/ Open(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + return Create(self, new RubyFile(self.Context, path.ConvertToString(), RubyFileMode.RDONLY)); + } + + [RubyMethod("open", RubyMethodAttributes.PublicSingleton)] + public static object Open([NotNull]BlockParam/*!*/ block, RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ path) { + GZipReader reader = Open(self, path); + object blockResult; + block.Yield(reader, out blockResult); + return blockResult; + } + + [RubyMethod("close")] + public static GZipReader/*!*/ Close(GZipReader/*!*/ self) { + return self; + } + } + + #endregion + + #region DataError class + + [RubyException("DataError"), Serializable] + public class DataError : RuntimeError { + public DataError() : this(null, null) { } + public DataError(string message) : this(message, null) { } + public DataError(string message, Exception inner) : base(message ?? "SignalException", inner) { } + +#if !SILVERLIGHT + protected DataError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/BasicSocket.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/BasicSocket.cs new file mode 100644 index 0000000000..00ed955bd8 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/BasicSocket.cs @@ -0,0 +1,776 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.StandardLibrary.FileControl; + +namespace IronRuby.StandardLibrary.Sockets { + [RubyClass("BasicSocket", BuildConfig = "!SILVERLIGHT")] + public abstract class RubyBasicSocket : RubyIO { + private static readonly MutableString BROADCAST_STRING = MutableString.Create(""); + private static readonly MutableString BROADCAST_IP_STRING = MutableString.Create("255.255.255.255"); + private static readonly MutableString ANY_IP_STRING = MutableString.Create("0.0.0.0"); + + private readonly Socket/*!*/ _socket; + + [MultiRuntimeAware] + private static readonly object BasicSocketClassKey = new object(); + + internal static StrongBox DoNotReverseLookup(RubyContext/*!*/ context) { + Assert.NotNull(context); + + return (StrongBox)context.GetOrCreateLibraryData(BasicSocketClassKey, () => new StrongBox(false)); + } + + /// + /// Create a new RubyBasicSocket from a specified stream and mode + /// + protected RubyBasicSocket(RubyContext/*!*/ context, Socket/*!*/ socket) + : base(context, new SocketStream(socket), "rb+") { + _socket = socket; + } + + protected internal Socket/*!*/ Socket { + get { return _socket; } + } + + public override WaitHandle/*!*/ CreateReadWaitHandle() { + return _socket.BeginReceive(Utils.Array.EmptyBytes, 0, 0, SocketFlags.Peek, null, null).AsyncWaitHandle; + } + + public override WaitHandle/*!*/ CreateWriteWaitHandle() { + return _socket.BeginSend(Utils.Array.EmptyBytes, 0, 0, SocketFlags.Peek, null, null).AsyncWaitHandle; + } + + public override WaitHandle/*!*/ CreateErrorWaitHandle() { + // TODO: + throw new NotSupportedException(); + } + + // returns 0 on success, -1 on failure + private int SetFileControlFlags(int flags) { + // TODO: + _socket.Blocking = (flags & RubyFileOps.Constants.NONBLOCK) != 0; + return 0; + } + + public override int FileControl(int commandId, int arg) { + // TODO: + switch (commandId) { + case Fcntl.F_SETFL: + return SetFileControlFlags(arg); + } + throw new NotSupportedException(); + } + + public override int FileControl(int commandId, byte[] arg) { + // TODO: + throw new NotSupportedException(); + } + + #region Public Singleton Methods + + /// + /// Returns the value of the global reverse lookup flag. + /// + [RubyMethod("do_not_reverse_lookup", RubyMethodAttributes.PublicSingleton)] + public static bool GetDoNotReverseLookup(RubyContext/*!*/ context, RubyClass/*!*/ self) { + return DoNotReverseLookup(context).Value; + } + + /// + /// Sets the value of the global reverse lookup flag. + /// If set to true, queries on remote addresses will return the numeric address but not the host name. + /// Defaults to false. + /// + [RubyMethod("do_not_reverse_lookup=", RubyMethodAttributes.PublicSingleton)] + public static void SetDoNotReverseLookup(RubyContext/*!*/ context, RubyClass/*!*/ self, bool value) { + Protocols.CheckSafeLevel(context, 4); + DoNotReverseLookup(context).Value = value; + } + + /// + /// Wraps an already open file descriptor into a socket object. + /// + /// The corresponding socket + [RubyMethod("for_fd", RubyMethodAttributes.PublicSingleton)] + public static RubyBasicSocket/*!*/ ForFileDescriptor(RubyClass/*!*/ self, [DefaultProtocol]int fileDescriptor) { + return (RubyBasicSocket)self.Context.GetDescriptor(fileDescriptor); + } + + #endregion + + #region Public Instance Methods + + [RubyMethod("close_read")] + public static void CloseRead(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self) { + CheckSecurity(context, self, "can't close socket"); + // TODO: It would be nice to alter the SocketStream to be WriteOnly here + self.Socket.Shutdown(SocketShutdown.Receive); + } + + [RubyMethod("close_write")] + public static void CloseWrite(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self) { + CheckSecurity(context, self, "can't close socket"); + // TODO: It would be nice to alter the SocketStream to be ReadOnly here + self.Socket.Shutdown(SocketShutdown.Send); + } + + [RubyMethod("shutdown")] + public static int Shutdown(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self) { + return Shutdown(context, self, 2); + } + + [RubyMethod("shutdown")] + public static int Shutdown(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, object/*Numeric*/ how) { + int iHow = 2; + if (how != null) { + iHow = Protocols.CastToFixnum(context, how); + } + return Shutdown(context, self, iHow); + } + + [RubyMethod("shutdown")] + public static int Shutdown(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, int how) { + CheckSecurity(context, self, "can't shutdown socket"); + if (how < 0 || 2 < how) { + throw RubyExceptions.CreateArgumentError("`how' should be either 0, 1, 2"); + } + self.Socket.Shutdown((SocketShutdown)how); + return 0; + } + + /// + /// Sets a socket option. These are protocol and system specific, see your local sytem documentation for details. + /// + /// level is an integer, usually one of the SOL_ constants such as Socket::SOL_SOCKET, or a protocol level. + /// optname is an integer, usually one of the SO_ constants, such as Socket::SO_REUSEADDR. + /// value is the value of the option, it is passed to the underlying setsockopt() as a pointer to a certain number of bytes. How this is done depends on the type. + [RubyMethod("setsockopt")] + public static void SetSocketOption(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, object/*Numeric*/ level, object/*Numeric*/ optname, int value) { + Protocols.CheckSafeLevel(context, 2, "setsockopt"); + int iLevel = Protocols.CastToFixnum(context, level); + int iOptname = Protocols.CastToFixnum(context, optname); + self.Socket.SetSocketOption((SocketOptionLevel)iLevel, (SocketOptionName)iOptname, value); + } + + /// + /// Sets a socket option. These are protocol and system specific, see your local sytem documentation for details. + /// + /// level is an integer, usually one of the SOL_ constants such as Socket::SOL_SOCKET, or a protocol level. + /// optname is an integer, usually one of the SO_ constants, such as Socket::SO_REUSEADDR. + /// value is the value of the option, it is passed to the underlying setsockopt() as a pointer to a certain number of bytes. How this is done depends on the type. + [RubyMethod("setsockopt")] + public static void SetSocketOption(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, object/*Numeric*/ level, object/*Numeric*/ optname, bool value) { + Protocols.CheckSafeLevel(context, 2, "setsockopt"); + int iLevel = Protocols.CastToFixnum(context, level); + int iOptname = Protocols.CastToFixnum(context, optname); + self.Socket.SetSocketOption((SocketOptionLevel)iLevel, (SocketOptionName)iOptname, value); + } + + /// + /// Sets a socket option. These are protocol and system specific, see your local sytem documentation for details. + /// + /// level is an integer, usually one of the SOL_ constants such as Socket::SOL_SOCKET, or a protocol level. + /// optname is an integer, usually one of the SO_ constants, such as Socket::SO_REUSEADDR. + /// value is the value of the option, it is passed to the underlying setsockopt() as a pointer to a certain number of bytes. How this is done depends on the type. + /// + /// Some socket options are integers with boolean values, in this case setsockopt could be called like this: + /// + /// sock.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true) + /// Some socket options are integers with numeric values, in this case setsockopt could be called like this: + /// + /// sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, 255) + /// Option values may be structs. Passing them can be complex as it involves examining your system headers to determine the correct definition. An example is an ip_mreq, which may be defined in your system headers as: + /// + /// struct ip_mreq { + /// struct in_addr imr_multiaddr; + /// struct in_addr imr_interface; + /// }; + /// In this case setsockopt could be called like this: + /// + /// optval = IPAddr.new("224.0.0.251") + Socket::INADDR_ANY + /// sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, optval) + /// + [RubyMethod("setsockopt")] + public static void SetSocketOption(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, object/*Numeric*/ level, object/*Numeric*/ optname, object value) { + Protocols.CheckSafeLevel(context, 2, "setsockopt"); + MutableString strValue = Protocols.CastToString(context, value); + int iLevel = Protocols.CastToFixnum(context, Protocols.ConvertToInteger(context, level)); + int iOptname = Protocols.CastToFixnum(context, Protocols.ConvertToInteger(context, optname)); + self.Socket.SetSocketOption((SocketOptionLevel)iLevel, (SocketOptionName)iOptname, strValue.ConvertToBytes()); + } + + /// + /// Gets a socket option. These are protocol and system specific, see your local sytem documentation for details. + /// + /// level is an integer, usually one of the SOL_ constants such as Socket::SOL_SOCKET, or a protocol level. + /// optname is an integer, usually one of the SO_ constants, such as Socket::SO_REUSEADDR. + /// The option is returned as a String with the data being the binary value of the socket option. + /// + /// Some socket options are integers with boolean values, in this case getsockopt could be called like this: + /// optval = sock.getsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR) + /// optval = optval.unpack "i" + /// reuseaddr = optval[0] == 0 ? false : true + /// Some socket options are integers with numeric values, in this case getsockopt could be called like this: + /// optval = sock.getsockopt(Socket::IPPROTO_IP, Socket::IP_TTL) + /// ipttl = optval.unpack("i")[0] + /// Option values may be structs. Decoding them can be complex as it involves examining your system headers to determine the correct definition. An example is a +struct linger+, which may be defined in your system headers as: + /// struct linger { + /// int l_onoff; + /// int l_linger; + /// }; + /// In this case getsockopt could be called like this: + /// optval = sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER) + /// onoff, linger = optval.unpack "ii" + /// + [RubyMethod("getsockopt")] + public static MutableString GetSocketOption(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, object/*Numeric*/ level, object/*Numeric*/ optname) { + Protocols.CheckSafeLevel(context, 2, "getsockopt"); + int iLevel = Protocols.CastToFixnum(context, Protocols.ConvertToInteger(context, level)); + int iOptname = Protocols.CastToFixnum(context, Protocols.ConvertToInteger(context, optname)); + byte[] value = self.Socket.GetSocketOption((SocketOptionLevel)iLevel, (SocketOptionName)iOptname, 4); + return MutableString.CreateBinary(value); + } + + [RubyMethod("getsockname")] + public static MutableString GetSocketName(RubyBasicSocket/*!*/ self) { + SocketAddress addr = self.Socket.LocalEndPoint.Serialize(); + byte[] bytes = new byte[addr.Size]; + for (int i = 0; i < addr.Size; ++i) { + bytes[i] = addr[i]; + } + return MutableString.CreateBinary(bytes); + } + + [RubyMethod("getpeername")] + public static MutableString GetPeerName(RubyBasicSocket/*!*/ self) { + SocketAddress addr = self.Socket.RemoteEndPoint.Serialize(); + byte[] bytes = new byte[addr.Size]; + for (int i = 0; i < addr.Size; ++i) { + bytes[i] = addr[i]; + } + return MutableString.CreateBinary(bytes); + } + + [RubyMethod("send")] + public static int Send(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, object message, object flags) { + Protocols.CheckSafeLevel(context, 4, "send"); + SocketFlags socketFlags = ConvertToSocketFlag(context, flags); + MutableString strMessage = Protocols.CastToString(context, message); + return self.Socket.Send(strMessage.ConvertToBytes(), socketFlags); + } + + [RubyMethod("send")] + public static int Send(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, object message, object flags, object to) { + Protocols.CheckSafeLevel(context, 4, "send"); + // Convert the parameters + SocketFlags socketFlags = ConvertToSocketFlag(context, flags); + MutableString strMessage = Protocols.CastToString(context, message); + MutableString strToAddress = Protocols.CastToString(context, to); + // Unpack the socket address information from the to parameter + SocketAddress address = new SocketAddress(AddressFamily.InterNetwork); + for (int i = 0; i < strToAddress.GetByteCount(); i++) { + address[i] = strToAddress.GetByte(i); + } + EndPoint toEndPoint = self.Socket.LocalEndPoint.Create(address); + return self.Socket.SendTo(strMessage.ConvertToBytes(), socketFlags, toEndPoint); + } + + [RubyMethod("recv")] + public static MutableString Receive(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, + [DefaultProtocol]int length, [DefaultParameterValue(null)]object flags) { + + SocketFlags sFlags = ConvertToSocketFlag(context, flags); + + byte[] buffer = new byte[length]; + int received = self.Socket.Receive(buffer, 0, length, sFlags); + + MutableString str = MutableString.CreateBinary(received); + str.Append(buffer, 0, received); + context.SetObjectTaint(str, true); + return str; + } + + /// + /// Receives up to length bytes from socket using recvfrom after O_NONBLOCK is set for the underlying file descriptor. + /// + /// Maximum number of bytes to receive + /// flags is zero or more of the MSG_ options. + /// The data received. When recvfrom(2) returns 0, Socket#recv_nonblock returns an empty string as data. The meaning depends on the socket: EOF on TCP, empty packet on UDP, etc. + /// + /// serv = TCPServer.new("127.0.0.1", 0) + /// af, port, host, addr = serv.addr + /// c = TCPSocket.new(addr, port) + /// s = serv.accept + /// c.send "aaa", 0 + /// IO.select([s]) + /// p s.recv_nonblock(10) #=> "aaa" + /// + [RubyMethod("recv_nonblock")] + public static MutableString/*!*/ ReceiveNonBlocking(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, + [DefaultProtocol]int length, [DefaultParameterValue(null)]object flags) { + + bool blocking = self.Socket.Blocking; + try { + self.Socket.Blocking = false; + return Receive(context, self, length, flags); + } finally { + // Reset the blocking + self.Socket.Blocking = blocking; + } + } + + #endregion + + #region Internal Helpers + + internal static void CheckSecurity(RubyContext/*!*/ context, object self, string message) { + if (context.CurrentSafeLevel >= 4 && context.IsObjectTainted(self)) { + throw RubyExceptions.CreateSecurityError("Insecure: " + message); + } + } + + internal static MutableString/*!*/ GetAddressInternal(RubyContext/*!*/ context, object hostname) { + // Ruby raises a SocketError rather than the NullReferenceException thrown by .NET + if (hostname == null) { + throw new SocketException((int)SocketError.HostNotFound); + } + MutableString strHostname = ConvertToHostString(context, hostname); + // If it is an IP address already then just pass it back + IPAddress address; + if (IPAddress.TryParse(strHostname.ConvertToString(), out address)) { + return strHostname; + } + // Lookup the host IP address via DNS + return MutableString.Create(Dns.GetHostAddresses(strHostname.ConvertToString())[0].ToString()); + } + + internal static RubyArray/*!*/ GetAddressArray(RubyContext/*!*/ context, EndPoint endPoint) { + RubyArray result = new RubyArray(4); + + IPEndPoint ep = (IPEndPoint)endPoint; + + result.Add(MutableString.Create(AddressFamilyToString(ep.AddressFamily))); + result.Add(ep.Port); + if (DoNotReverseLookup(context).Value) { + result.Add(MutableString.Create(ep.Address.ToString())); + } else { + // TODO: MRI returns localhost rather than the local machine name here + result.Add(MutableString.Create(Dns.GetHostEntry(ep.Address).HostName)); + } + result.Add(MutableString.Create(ep.Address.ToString())); + return result; + } + + private static string AddressFamilyToString(AddressFamily af) { + // for the most part we can just use the upper-cased AddressFamily name + // of the enum value, but for some types we need to explicitly map the + // correct names + switch (af) { + case AddressFamily.InterNetwork: return "AF_INET"; + case AddressFamily.DataLink: return "AF_DLI"; + case AddressFamily.HyperChannel: return "AF_HYLINK"; + case AddressFamily.Banyan: return "AF_BAN"; + case AddressFamily.InterNetworkV6: return "AF_INET6"; + case AddressFamily.Ieee12844: return "AF_12844"; + case AddressFamily.NetworkDesigners: return "AF_NETDES"; + default: + string name = Enum.GetName(typeof(AddressFamily), af); + return (name != null) ? + "AF_" + name.ToUpper() : + "unknown:" + ((int)af).ToString(); + } + } + + internal static SocketFlags ConvertToSocketFlag(RubyContext/*!*/ context, object flags) { + if (flags == null) { + return SocketFlags.None; + } + return (SocketFlags)Protocols.CastToFixnum(context, flags); + } + + internal static AddressFamily ConvertToAddressFamily(RubyContext/*!*/ context, object family) { + // Default is AF_INET + if (family == null) { + return AddressFamily.InterNetwork; + } + // If it is a Fixnum then assume it is just the constant value + if (family is int) { + return (AddressFamily)(int)family; + } + // Convert to a string (using to_str) and then look up the value + MutableString strFamily = Protocols.CastToString(context, family); + foreach (AddressFamilyName name in FamilyNames) { + if (name.Name.Equals(strFamily)) { + return name.Family; + } + } + // Convert to a Fixnum (using to_i) and hope it is a valid AddressFamily constant + return (AddressFamily)Protocols.CastToFixnum(context, strFamily); + } + + internal static MutableString ToAddressFamilyString(AddressFamily family) { + foreach (AddressFamilyName name in FamilyNames) { + if (name.Family == family) { + return name.Name; + } + } + throw new SocketException((int)SocketError.AddressFamilyNotSupported); + } + + + internal static MutableString ConvertToHostString(RubyContext/*!*/ context, object hostname) { + if (hostname == null) { + return null; + } + if (hostname is MutableString) { + MutableString strHostname = (MutableString)hostname; + // Special cases + if (strHostname.IsEmpty) { + strHostname = ANY_IP_STRING; + } else if (strHostname.Equals(BROADCAST_STRING)) { + strHostname = BROADCAST_IP_STRING; + } + return strHostname; + } + int iHostname; + if (Protocols.IntegerAsFixnum(hostname, out iHostname)) { + // Ruby uses Little Endian whereas .NET uses Big Endian IP values + byte[] bytes = new byte[4]; + for (int i = 3; i >= 0; --i) { + bytes[i] = (byte)(iHostname & 0xff); + iHostname >>= 8; + } + return MutableString.Create(new System.Net.IPAddress(bytes).ToString()); + } + return Protocols.CastToString(context, hostname); + } + + internal static int ConvertToPortNum(RubyContext/*!*/ context, object port) { + // conversion protocol: if it's a Fixnum, return it + // otherwise, convert to string & then convert the result to a Fixnum + if (port is int) { + return (int)port; + } + + MutableString serviceName = Protocols.CastToString(context, port); + ServiceName service = SearchForService(serviceName); + if (service != null) { + return service.Port; + } + + int result; + Protocols.IntegerAsFixnum(Protocols.ConvertToInteger(context, serviceName), out result); + return result; + } + + internal static ServiceName SearchForService(int port) { + foreach (ServiceName name in ServiceNames) { + if (name.Port == port) { + return name; + } + } + return null; + } + + internal static ServiceName SearchForService(MutableString/*!*/ serviceName) { + foreach (ServiceName name in ServiceNames) { + if (name.Name.Equals(serviceName)) { + return name; + } + } + return null; + } + + internal static ServiceName SearchForService(MutableString/*!*/ serviceName, MutableString/*!*/ protocol) { + foreach (ServiceName name in ServiceNames) { + if (name.Name.Equals(serviceName) && name.Protocol.Equals(protocol)) { + return name; + } + } + return null; + } + + internal static RubyArray/*!*/ InternalGetHostByName(RubyClass/*!*/ klass, object hostName, bool packIpAddresses) { + MutableString strHostName = ConvertToHostString(klass.Context, hostName); + + if (strHostName == null) { + throw new SocketException(); + } + + IPHostEntry hostEntry = Dns.GetHostEntry(hostName.ToString()); + return CreateHostEntryArray(hostEntry, packIpAddresses); + } + + internal static RubyArray/*!*/ CreateHostEntryArray(IPHostEntry hostEntry, bool packIpAddresses) { + RubyArray result = new RubyArray(4); + // Canonical Hostname + result.Add(MutableString.Create(hostEntry.HostName)); + + // Aliases + RubyArray aliases = new RubyArray(hostEntry.Aliases.Length); + foreach (string alias in hostEntry.Aliases) { + aliases.Add(MutableString.Create(alias)); + } + result.Add(aliases); + + // Address Type + result.Add((int)hostEntry.AddressList[0].AddressFamily); + + // IP Address + foreach (IPAddress address in hostEntry.AddressList) { + if (packIpAddresses) { + byte[] bytes = address.GetAddressBytes(); + MutableString str = MutableString.CreateBinary(); + str.Append(bytes, 0, bytes.Length); + result.Add(str); + } else { + result.Add(MutableString.Create(address.ToString())); + } + } + return result; + } + + + class AddressFamilyName { + MutableString _name; + AddressFamily _family; + public MutableString Name { get { return _name; } } + public AddressFamily Family { get { return _family; } } + public AddressFamilyName(string name, AddressFamily family) { + _name = MutableString.Create(name); + _family = family; + } + } + + static List FamilyNames = new List(new AddressFamilyName[] { + new AddressFamilyName("AF_INET", AddressFamily.InterNetwork), + new AddressFamilyName("AF_UNIX", AddressFamily.Unix), + //new AddressFamilyName("AF_AX25", AddressFamily.Ax), + new AddressFamilyName("AF_IPX", AddressFamily.Ipx), + new AddressFamilyName("AF_APPLETALK", AddressFamily.AppleTalk), + new AddressFamilyName("AF_UNSPEC", AddressFamily.Unspecified), + new AddressFamilyName("AF_INET6", AddressFamily.InterNetworkV6), + //new AddressFamilyName("AF_LOCAL", AddressFamily.Local), + new AddressFamilyName("AF_IMPLINK", AddressFamily.ImpLink), + new AddressFamilyName("AF_PUP", AddressFamily.Pup), + new AddressFamilyName("AF_CHAOS", AddressFamily.Chaos), + new AddressFamilyName("AF_NS", AddressFamily.NS), + new AddressFamilyName("AF_ISO", AddressFamily.Iso), + new AddressFamilyName("AF_OSI", AddressFamily.Osi), + new AddressFamilyName("AF_ECMA", AddressFamily.Ecma), + new AddressFamilyName("AF_DATAKIT", AddressFamily.DataKit), + new AddressFamilyName("AF_CCITT", AddressFamily.Ccitt), + new AddressFamilyName("AF_SNA", AddressFamily.Sna), + new AddressFamilyName("AF_DEC", AddressFamily.DecNet), + new AddressFamilyName("AF_DLI", AddressFamily.DataLink), + new AddressFamilyName("AF_LAT", AddressFamily.Lat), + new AddressFamilyName("AF_HYLINK", AddressFamily.HyperChannel), + //new AddressFamilyName("AF_ROUTE", AddressFamily.Route), + //new AddressFamilyName("AF_LINK", AddressFamily.Link), + //new AddressFamilyName("AF_COIP", AddressFamily.Coip), + //new AddressFamilyName("AF_CNT", AddressFamily.Cnt), + //new AddressFamilyName("AF_SIP", AddressFamily.Sip), + //new AddressFamilyName("AF_NDRV", AddressFamily.Nrdv), + //new AddressFamilyName("AF_ISDN", AddressFamily.Isdn), + //new AddressFamilyName("AF_NATM", AddressFamily.NATM), + //new AddressFamilyName("AF_SYSTEM", AddressFamily.System), + new AddressFamilyName("AF_NETBIOS", AddressFamily.NetBios), + //new AddressFamilyName("AF_PPP", AddressFamily.Ppp), + new AddressFamilyName("AF_ATM", AddressFamily.Atm), + //new AddressFamilyName("AF_NETGRAPH", AddressFamily.Netgraph), + new AddressFamilyName("AF_MAX", AddressFamily.Max), + //new AddressFamilyName("AF_E164", AddressFamily.E164), + }); + + internal class ServiceName { + int _port; + MutableString _protocol; + MutableString _name; + + public int Port { get { return _port; } } + public MutableString Protocol { get { return _protocol; } } + public MutableString Name { get { return _name; } } + + public ServiceName(int port, string protocol, string name) { + _port = port; + _protocol = MutableString.Create(protocol); + _name = MutableString.Create(name); + } + } + + static List ServiceNames = new List(new ServiceName[] { + new ServiceName(7, "tcp", "echo"), + new ServiceName(7, "udp", "echo"), + new ServiceName(9, "tcp", "discard"), + new ServiceName(9, "udp", "discard"), + new ServiceName(11, "tcp", "systat"), + new ServiceName(11, "udp", "systat"), + new ServiceName(13, "tcp", "daytime"), + new ServiceName(13, "udp", "daytime"), + new ServiceName(15, "tcp", "netstat"), + new ServiceName(17, "tcp", "qotd"), + new ServiceName(17, "udp", "qotd"), + new ServiceName(19, "tcp", "chargen"), + new ServiceName(19, "udp", "chargen"), + new ServiceName(20, "tcp", "ftp-data"), + new ServiceName(21, "tcp", "ftp"), + new ServiceName(23, "tcp", "telnet"), + new ServiceName(25, "tcp", "smtp"), + new ServiceName(37, "tcp", "time"), + new ServiceName(37, "udp", "time"), + new ServiceName(39, "udp", "rlp"), + new ServiceName(42, "tcp", "name"), + new ServiceName(42, "udp", "name"), + new ServiceName(43, "tcp", "whois"), + new ServiceName(53, "tcp", "domain"), + new ServiceName(53, "udp", "domain"), + new ServiceName(53, "tcp", "nameserver"), + new ServiceName(53, "udp", "nameserver"), + new ServiceName(57, "tcp", "mtp"), + new ServiceName(67, "udp", "bootp"), + new ServiceName(69, "udp", "tftp"), + new ServiceName(77, "tcp", "rje"), + new ServiceName(79, "tcp", "finger"), + new ServiceName(80, "tcp", "http"), + new ServiceName(87, "tcp", "link"), + new ServiceName(95, "tcp", "supdup"), + new ServiceName(101, "tcp", "hostnames"), + new ServiceName(102, "tcp", "iso-tsap"), + new ServiceName(103, "tcp", "dictionary"), + new ServiceName(103, "tcp", "x400"), + new ServiceName(104, "tcp", "x400-snd"), + new ServiceName(105, "tcp", "csnet-ns"), + new ServiceName(109, "tcp", "pop"), + new ServiceName(109, "tcp", "pop2"), + new ServiceName(110, "tcp", "pop3"), + new ServiceName(111, "tcp", "portmap"), + new ServiceName(111, "udp", "portmap"), + new ServiceName(111, "tcp", "sunrpc"), + new ServiceName(111, "udp", "sunrpc"), + new ServiceName(113, "tcp", "auth"), + new ServiceName(115, "tcp", "sftp"), + new ServiceName(117, "tcp", "path"), + new ServiceName(117, "tcp", "uucp-path"), + new ServiceName(119, "tcp", "nntp"), + new ServiceName(123, "udp", "ntp"), + new ServiceName(137, "udp", "nbname"), + new ServiceName(138, "udp", "nbdatagram"), + new ServiceName(139, "tcp", "nbsession"), + new ServiceName(144, "tcp", "NeWS"), + new ServiceName(153, "tcp", "sgmp"), + new ServiceName(158, "tcp", "tcprepo"), + new ServiceName(161, "tcp", "snmp"), + new ServiceName(162, "tcp", "snmp-trap"), + new ServiceName(170, "tcp", "print-srv"), + new ServiceName(175, "tcp", "vmnet"), + new ServiceName(315, "udp", "load"), + new ServiceName(400, "tcp", "vmnet0"), + new ServiceName(500, "udp", "sytek"), + new ServiceName(512, "udp", "biff"), + new ServiceName(512, "tcp", "exec"), + new ServiceName(513, "tcp", "login"), + new ServiceName(513, "udp", "who"), + new ServiceName(514, "tcp", "shell"), + new ServiceName(514, "udp", "syslog"), + new ServiceName(515, "tcp", "printer"), + new ServiceName(517, "udp", "talk"), + new ServiceName(518, "udp", "ntalk"), + new ServiceName(520, "tcp", "efs"), + new ServiceName(520, "udp", "route"), + new ServiceName(525, "udp", "timed"), + new ServiceName(526, "tcp", "tempo"), + new ServiceName(530, "tcp", "courier"), + new ServiceName(531, "tcp", "conference"), + new ServiceName(531, "udp", "rvd-control"), + new ServiceName(532, "tcp", "netnews"), + new ServiceName(533, "udp", "netwall"), + new ServiceName(540, "tcp", "uucp"), + new ServiceName(543, "tcp", "klogin"), + new ServiceName(544, "tcp", "kshell"), + new ServiceName(550, "udp", "new-rwho"), + new ServiceName(556, "tcp", "remotefs"), + new ServiceName(560, "udp", "rmonitor"), + new ServiceName(561, "udp", "monitor"), + new ServiceName(600, "tcp", "garcon"), + new ServiceName(601, "tcp", "maitrd"), + new ServiceName(602, "tcp", "busboy"), + new ServiceName(700, "udp", "acctmaster"), + new ServiceName(701, "udp", "acctslave"), + new ServiceName(702, "udp", "acct"), + new ServiceName(703, "udp", "acctlogin"), + new ServiceName(704, "udp", "acctprinter"), + new ServiceName(704, "udp", "elcsd"), + new ServiceName(705, "udp", "acctinfo"), + new ServiceName(706, "udp", "acctslave2"), + new ServiceName(707, "udp", "acctdisk"), + new ServiceName(750, "tcp", "kerberos"), + new ServiceName(750, "udp", "kerberos"), + new ServiceName(751, "tcp", "kerberos_master"), + new ServiceName(751, "udp", "kerberos_master"), + new ServiceName(752, "udp", "passwd_server"), + new ServiceName(753, "udp", "userreg_server"), + new ServiceName(754, "tcp", "krb_prop"), + new ServiceName(888, "tcp", "erlogin"), + new ServiceName(1109, "tcp", "kpop"), + new ServiceName(1167, "udp", "phone"), + new ServiceName(1524, "tcp", "ingreslock"), + new ServiceName(1666, "udp", "maze"), + new ServiceName(2049, "udp", "nfs"), + new ServiceName(2053, "tcp", "knetd"), + new ServiceName(2105, "tcp", "eklogin"), + new ServiceName(5555, "tcp", "rmt"), + new ServiceName(5556, "tcp", "mtb"), + new ServiceName(9535, "tcp", "man"), + new ServiceName(9536, "tcp", "w"), + new ServiceName(9537, "tcp", "mantst"), + new ServiceName(10000, "tcp", "bnews"), + new ServiceName(10000, "udp", "rscs0"), + new ServiceName(10001, "tcp", "queue"), + new ServiceName(10001, "udp", "rscs1"), + new ServiceName(10002, "tcp", "poker"), + new ServiceName(10002, "udp", "rscs2"), + new ServiceName(10003, "tcp", "gateway"), + new ServiceName(10003, "udp", "rscs3"), + new ServiceName(10004, "tcp", "remp"), + new ServiceName(10004, "udp", "rscs4"), + new ServiceName(10005, "udp", "rscs5"), + new ServiceName(10006, "udp", "rscs6"), + new ServiceName(10007, "udp", "rscs7"), + new ServiceName(10008, "udp", "rscs8"), + new ServiceName(10009, "udp", "rscs9"), + new ServiceName(10010, "udp", "rscsa"), + new ServiceName(10011, "udp", "rscsb"), + new ServiceName(10012, "tcp", "qmaster"), + new ServiceName(10012, "udp", "qmaster") + }); + + #endregion + } +} +#endif diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/IPSocket.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/IPSocket.cs new file mode 100644 index 0000000000..49957808b8 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/IPSocket.cs @@ -0,0 +1,66 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Net; +using System.Net.Sockets; +using Microsoft.Scripting.Runtime; +using IronRuby.Builtins; +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.Sockets { + [RubyClass("IPSocket", BuildConfig = "!SILVERLIGHT")] + public abstract class IPSocket : RubyBasicSocket { + + public IPSocket(RubyContext/*!*/ context, Socket/*!*/ socket) + : base(context, socket) { + } + + [RubyMethod("getaddress", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ GetAddress(RubyClass/*!*/ self, object hostname) { + return GetAddressInternal(self.Context, hostname); + } + + #region Public Instance Methods + [RubyMethod("addr")] + public static RubyArray/*!*/ GetLocalAddress(RubyContext/*!*/ context, IPSocket/*!*/ self) { + return GetAddressArray(context, self.Socket.LocalEndPoint); + } + + [RubyMethod("peeraddr")] + public static object/*!*/ GetPeerAddress(RubyContext/*!*/ context, IPSocket/*!*/ self) { + return GetAddressArray(context, self.Socket.RemoteEndPoint); + } + + [RubyMethod("recvfrom")] + public static RubyArray/*!*/ ReceiveFrom(RubyContext/*!*/ context, IPSocket/*!*/ self, int length) { + return ReceiveFrom(context, self, length, null); + } + [RubyMethod("recvfrom")] + public static RubyArray/*!*/ ReceiveFrom(RubyContext/*!*/ context, IPSocket/*!*/ self, int length, object/*Numeric*/ flags) { + SocketFlags sFlags = ConvertToSocketFlag(context, flags); + byte[] buffer = new byte[length]; + EndPoint fromEP = new IPEndPoint(IPAddress.Any, 0); + int received = self.Socket.ReceiveFrom(buffer, sFlags, ref fromEP); + MutableString str = MutableString.CreateBinary(); + str.Append(buffer, 0, received); + context.SetObjectTaint(str, true); + return RubyOps.MakeArray2(str, GetAddressArray(context, fromEP)); + } + #endregion + } +} +#endif diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/Socket.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/Socket.cs new file mode 100644 index 0000000000..76c30d2ed8 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/Socket.cs @@ -0,0 +1,956 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using IronRuby.Builtins; +using IronRuby.Compiler; +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.Sockets { + [RubyClass("Socket", BuildConfig = "!SILVERLIGHT")] + public class RubySocket : RubyBasicSocket { + + #region Construction + + public RubySocket(RubyContext/*!*/ context, Socket/*!*/ socket) + : base(context, socket) { + } + + [RubyConstructor] + public static RubySocket/*!*/ CreateSocket(RubyClass/*!*/ self, [NotNull]object/*!*/ domain, [DefaultProtocol]int/*!*/ type, [DefaultProtocol]int protocol) { + AddressFamily addressFamily = ConvertToAddressFamily(self.Context, domain); + return new RubySocket(self.Context, new Socket(addressFamily, (SocketType)type, (ProtocolType)protocol)); + } + + #endregion + + #region Public Singleton Methods + + [RubyMethod("getaddrinfo", RubyMethodAttributes.PublicSingleton)] + public static RubyArray GetAddressInfo(RubyContext/*!*/ context, RubyClass/*!*/ self, object hostname, object port, + [DefaultParameterValue(null)]object family, + [DefaultParameterValue(0)]object socktype, + [DefaultParameterValue(0)]object protocol, + [DefaultParameterValue(null)]object flags) { + + MutableString strHostname = ConvertToHostString(context, hostname); + int iPort = ConvertToPortNum(context, port); + + // Sadly the family, socktype, protocol and flags get passed through at best and ignored at worst, + // since System.Net does not provide for them + AddressFamily addrFamily = ConvertToAddressFamily(context, family); + int socketType = Protocols.CastToFixnum(context, socktype); + int protocolType = Protocols.CastToFixnum(context, protocol); + + RubyArray results = new RubyArray(); + + IPHostEntry entry = (strHostname != null) ? GetHostEntry(strHostname.ConvertToString()) : MakeEntry(IPAddress.Any); + for (int i = 0; i < entry.AddressList.Length; ++i) { + IPAddress address = entry.AddressList[i]; + RubyArray result = new RubyArray(); + result.Add(ToAddressFamilyString(address.AddressFamily)); + result.Add(iPort); + if (DoNotReverseLookup(context).Value) { + result.Add(MutableString.Create(address.ToString())); + } else { + IPHostEntry alias = GetHostEntry(address); + result.Add(MutableString.Create(alias.HostName)); + } + result.Add(MutableString.Create(address.ToString())); + result.Add((int)address.AddressFamily); + result.Add(socketType); + + // TODO: protocol type: + result.Add(protocolType); + + results.Add(result); + } + return results; + } + + // TODO: handle other invalid addresses + private static IPHostEntry/*!*/ GetHostEntry(IPAddress/*!*/ address) { + if (address.Equals(IPAddress.Any) || address.Equals(IPAddress.Loopback)) { + return MakeEntry(address); + } else { + return Dns.GetHostEntry(address); + } + } + + private static IPHostEntry/*!*/ GetHostEntry(string/*!*/ hostNameOrAddress) { + if (hostNameOrAddress == IPAddress.Any.ToString()) { + return MakeEntry(IPAddress.Any); + } else if (hostNameOrAddress == IPAddress.Loopback.ToString()) { + return MakeEntry(IPAddress.Loopback); + } else { + return Dns.GetHostEntry(hostNameOrAddress); + } + } + + private static IPHostEntry/*!*/ MakeEntry(IPAddress/*!*/ address) { + IPHostEntry entry = new IPHostEntry(); + entry.AddressList = new IPAddress[] { address }; + entry.Aliases = new string[] { address.ToString() }; + entry.HostName = address.ToString(); + return entry; + } + + [RubyMethod("gethostbyaddr", RubyMethodAttributes.PublicSingleton)] + public static RubyArray GetHostByAddress(RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ address, [DefaultParameterValue(null)]object type) { + + AddressFamily addressFamily = ConvertToAddressFamily(self.Context, type); + IPHostEntry entry = GetHostEntry(new IPAddress(address.ConvertToBytes())); + + return CreateHostEntryArray(entry, true); + } + + [RubyMethod("gethostbyname", RubyMethodAttributes.PublicSingleton)] + public static RubyArray GetHostByName(RubyClass/*!*/ self, object hostname) { + return InternalGetHostByName(self, hostname, true); + } + + [RubyMethod("gethostname", RubyMethodAttributes.PublicSingleton)] + public static MutableString GetHostname(RubyClass/*!*/ self) { + return MutableString.Create(Dns.GetHostName()); + } + + private static readonly MutableString/*!*/ _DefaultProtocol = MutableString.Create("tcp").Freeze(); + + [RubyMethod("getservbyname", RubyMethodAttributes.PublicSingleton)] + public static int GetServiceByName(RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ name, + [DefaultProtocol, Optional]MutableString protocol) { + + if (protocol == null) { + protocol = _DefaultProtocol; + } + + ServiceName service = SearchForService(name, protocol); + if (service != null) { + return service.Port; + } + + // Cannot use: object port = Protocols.TryConvertToInteger(context, name); + // Since the conversion process returns 0 if the string is not a valid number + try { + return ParseInteger(self.Context, name.ConvertToString()); + } catch (InvalidOperationException) { + throw SocketErrorOps.Create( + String.Format("no such service {0} {1}", name.ConvertToString(), protocol.ConvertToString()) + ); + } + } + + /// + /// Returns a pair of connected sockets + /// [Not Implemented] + /// + [RubyMethod("socketpair", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("pair", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ CreateSocketPair(RubyClass/*!*/ self, object domain, object type, object protocol) { + throw new NotImplementedError(); + } + + [RubyMethod("getnameinfo", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ GetNameInfo(RubyClass/*!*/ self, [NotNull]RubyArray/*!*/ hostInfo, [Optional]object flags) { + if (hostInfo.Count < 3 || hostInfo.Count > 4) { + throw RubyExceptions.CreateArgumentError("First parameter must be a 3 or 4 element array"); + } + + // We only support AF_INET (IP V4) family + AddressFamily addressFamily = ConvertToAddressFamily(self.Context, hostInfo[0]); + if (addressFamily != AddressFamily.InterNetwork) { + throw new SocketException((int)SocketError.AddressFamilyNotSupported); + } + + // Lookup the service name for the given port. + int port = ConvertToPortNum(self.Context, hostInfo[1]); + ServiceName service = SearchForService(port); + + // hostInfo[2] should have a host name + // if it exists and is not null hostInfo[3] should have an IP address + // in that case we use that rather than the host name. + MutableString address = ConvertToHostString(self.Context, hostInfo[2]); + if (hostInfo.Count > 3 && hostInfo[3] != null) { + address = ConvertToHostString(self.Context, hostInfo[3]); + } + + IPHostEntry entry = GetHostEntry(address.ConvertToString()); + + RubyArray result = new RubyArray(2); + result.Add(MutableString.Create(entry.HostName)); + if (service != null) { + result.Add(MutableString.Create(service.Name)); + } else { + result.Add(port); + } + return result; + } + + [RubyMethod("getnameinfo", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ GetNameInfo(RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ address, [Optional]object flags) { + + IPEndPoint ep = UnpackSockAddr(address); + IPHostEntry entry = GetHostEntry(ep.Address); + ServiceName service = SearchForService(ep.Port); + + RubyArray result = new RubyArray(2); + result.Add(MutableString.Create(entry.HostName)); + if (service != null) { + result.Add(MutableString.Create(service.Name)); + } else { + result.Add(ep.Port); + } + return result; + } + + /// + /// Returns the system dependent sockaddr structure packed into a string + /// + [RubyMethod("sockaddr_in", RubyMethodAttributes.PublicSingleton)] + [RubyMethod("pack_sockaddr_in", RubyMethodAttributes.PublicSingleton)] + public static MutableString/*!*/ PackInetSockAddr(RubyClass/*!*/ self, object port, object host) { + int iPort = ConvertToPortNum(self.Context, port); + MutableString strHost = ConvertToHostString(self.Context, host); + IPEndPoint ep; + IPAddress[] addresses = Dns.GetHostAddresses(strHost.ConvertToString()); + if (addresses.Length > 0) { + ep = new IPEndPoint(addresses[0], iPort); + } else { + throw RubyExceptions.CreateArgumentError("invalid host"); + } + return MutableString.Create(ep.Serialize().ToString()); + } + + /// + /// Returns the system dependent sockaddr structure packed into a string + /// + [RubyMethod("unpack_sockaddr_in", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ UnPackInetSockAddr(RubyClass/*!*/ self, + [DefaultProtocol, NotNull]MutableString/*!*/ address) { + + IPEndPoint ep = UnpackSockAddr(address); + RubyArray result = new RubyArray(2); + result.Add(ep.Port); + result.Add(ep.Address.ToString()); + return result; + } + + internal static IPEndPoint/*!*/ UnpackSockAddr(MutableString/*!*/ stringAddress) { + byte[] bytes = stringAddress.ConvertToBytes(); + SocketAddress addr = new SocketAddress(AddressFamily.InterNetwork, bytes.Length); + for (int i = 0; i < bytes.Length; ++i) { + addr[i] = bytes[i]; + } + IPEndPoint ep = new IPEndPoint(0, 0); + return (IPEndPoint)ep.Create(addr); + } + + #endregion + + #region Public Instance Methods + + [RubyMethod("accept")] + public static RubyArray/*!*/ Accept(RubyContext/*!*/ context, RubySocket/*!*/ self) { + RubyArray result = new RubyArray(2); + RubySocket s = new RubySocket(context, self.Socket.Accept()); + result.Add(s); + SocketAddress addr = s.Socket.RemoteEndPoint.Serialize(); + result.Add(MutableString.Create(addr.ToString())); + return result; + } + + [RubyMethod("accept_nonblock")] + public static RubyArray/*!*/ AcceptNonBlocking(RubyContext/*!*/ context, RubySocket/*!*/ self) { + bool blocking = self.Socket.Blocking; + try { + self.Socket.Blocking = false; + return Accept(context, self); + } finally { + // Reset the blocking + self.Socket.Blocking = blocking; + } + } + + [RubyMethod("bind")] + public static int Bind(RubyContext/*!*/ context, RubySocket/*!*/ self, MutableString sockaddr) { + IPEndPoint ep = UnpackSockAddr(sockaddr); + self.Socket.Bind(ep); + return 0; + } + + [RubyMethod("connect")] + public static int Connect(RubyContext/*!*/ context, RubySocket/*!*/ self, MutableString sockaddr) { + IPEndPoint ep = UnpackSockAddr(sockaddr); + self.Socket.Connect(ep); + return 0; + } + + [RubyMethod("connect_nonblock")] + public static int ConnectNonBlocking(RubyContext/*!*/ context, RubySocket/*!*/ self, MutableString sockaddr) { + bool blocking = self.Socket.Blocking; + try { + self.Socket.Blocking = false; + return Connect(context, self, sockaddr); + } finally { + // Reset the blocking + self.Socket.Blocking = blocking; + } + } + + [RubyMethod("listen")] + public static int Listen(RubyContext/*!*/ context, RubySocket/*!*/ self, int backlog) { + self.Socket.Listen(backlog); + return 0; + } + + [RubyMethod("recvfrom")] + public static RubyArray/*!*/ ReceiveFrom(RubyContext/*!*/ context, RubySocket/*!*/ self, int length) { + return ReceiveFrom(context, self, length, null); + } + + [RubyMethod("recvfrom")] + public static RubyArray/*!*/ ReceiveFrom(RubyContext/*!*/ context, RubySocket/*!*/ self, int length, object/*Numeric*/ flags) { + SocketFlags sFlags = ConvertToSocketFlag(context, flags); + byte[] buffer = new byte[length]; + EndPoint fromEP = new IPEndPoint(IPAddress.Any, 0); + int received = self.Socket.ReceiveFrom(buffer, sFlags, ref fromEP); + MutableString str = MutableString.CreateBinary(); + str.Append(buffer, 0, received); + str.IsTainted = true; + return RubyOps.MakeArray2(str, GetAddressArray(context, fromEP)); + } + + + [RubyMethod("sysaccept")] + public static RubyArray/*!*/ SysAccept(RubyContext/*!*/ context, RubySocket/*!*/ self) { + RubyArray result = new RubyArray(2); + // TODO: Do we need some kind of strong reference to the socket + // here to stop the RubySocket from being garbage collected? + RubySocket s = new RubySocket(context, self.Socket.Accept()); + result.Add(s.FileDescriptor); + SocketAddress addr = s.Socket.RemoteEndPoint.Serialize(); + result.Add(MutableString.Create(addr.ToString())); + return result; + } + + #endregion + + #region Constants + + #region Address Family + [RubyConstant] + public const int AF_APPLETALK = (int)AddressFamily.AppleTalk; + [RubyConstant] + public const int AF_ATM = (int)AddressFamily.Atm; + [RubyConstant] + public const int AF_CCITT = (int)AddressFamily.Ccitt; + [RubyConstant] + public const int AF_CHAOS = (int)AddressFamily.Chaos; + [RubyConstant] + public const int AF_DATAKIT = (int)AddressFamily.DataKit; + [RubyConstant] + public const int AF_DLI = (int)AddressFamily.DataLink; + [RubyConstant] + public const int AF_ECMA = (int)AddressFamily.Ecma; + [RubyConstant] + public const int AF_HYLINK = (int)AddressFamily.HyperChannel; + [RubyConstant] + public const int AF_IMPLINK = (int)AddressFamily.ImpLink; + [RubyConstant] + public const int AF_INET = (int)AddressFamily.InterNetwork; + [RubyConstant] + public const int AF_IPX = (int)AddressFamily.Ipx; + [RubyConstant] + public const int AF_ISO = (int)AddressFamily.Iso; + [RubyConstant] + public const int AF_LAT = (int)AddressFamily.Lat; + [RubyConstant] + public const int AF_MAX = (int)AddressFamily.Max; + [RubyConstant] + public const int AF_NETBIOS = (int)AddressFamily.NetBios; + [RubyConstant] + public const int AF_NS = (int)AddressFamily.NS; + [RubyConstant] + public const int AF_OSI = (int)AddressFamily.Osi; + [RubyConstant] + public const int AF_PUP = (int)AddressFamily.Pup; + [RubyConstant] + public const int AF_SNA = (int)AddressFamily.Sna; + [RubyConstant] + public const int AF_UNIX = (int)AddressFamily.Unix; + [RubyConstant] + public const int AF_UNSPEC = (int)AddressFamily.Unspecified; + #endregion + + #region Flag Options for GetAddressInfo + [RubyConstant] + public const int AI_PASSIVE = 1; + [RubyConstant] + public const int AI_CANONNAME = 2; + [RubyConstant] + public const int AI_NUMERICHOST = 4; + [RubyConstant] + public const int AI_MASK = (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST); + + [RubyConstant] + public const int AI_ALL = 0x00000100; + [RubyConstant] + public const int AI_V4MAPPED_CFG = 0x00000200; + [RubyConstant] + public const int AI_ADDRCONFIG = 0x00000400; + [RubyConstant] + public const int AI_V4MAPPED = 0x00000800; + [RubyConstant] + public const int AI_DEFAULT = (AI_V4MAPPED_CFG | AI_ADDRCONFIG); + #endregion + + #region Error Return Codes from GetAddressInfo + [RubyConstant] + public const int EAI_ADDRFAMILY = 1; + [RubyConstant] + public const int EAI_AGAIN = 2; + [RubyConstant] + public const int EAI_BADFLAGS = 3; + [RubyConstant] + public const int EAI_BADHINTS = 12; + [RubyConstant] + public const int EAI_FAIL = 4; + [RubyConstant] + public const int EAI_FAMILY = 5; + [RubyConstant] + public const int EAI_MAX = 14; + [RubyConstant] + public const int EAI_MEMORY = 6; + [RubyConstant] + public const int EAI_NODATA = 7; + [RubyConstant] + public const int EAI_NONAME = 8; + [RubyConstant] + public const int EAI_PROTOCOL = 13; + [RubyConstant] + public const int EAI_SERVICE = 9; + [RubyConstant] + public const int EAI_SOCKTYPE = 10; + [RubyConstant] + public const int EAI_SYSTEM = 11; + #endregion + + #region + [RubyConstant] + public const uint INADDR_ALLHOSTS_GROUP = 0xe0000001; + [RubyConstant] + public const int INADDR_ANY = 0; + [RubyConstant] + public const uint INADDR_BROADCAST = 0xffffffff; + [RubyConstant] + public const int INADDR_LOOPBACK = 0x7F000001; + [RubyConstant] + public const uint INADDR_MAX_LOCAL_GROUP = 0xe00000ff; + [RubyConstant] + public const uint INADDR_NONE = 0xffffffff; + [RubyConstant] + public const uint INADDR_UNSPEC_GROUP = 0xe0000000; + #endregion + + #region IP Protocol Constants + [RubyConstant] + public const int IPPORT_RESERVED = 1024; + [RubyConstant] + public const int IPPORT_USERRESERVED = 5000; + [RubyConstant] + public const int IPPROTO_GGP = 3; + [RubyConstant] + public const int IPPROTO_ICMP = 1; + [RubyConstant] + public const int IPPROTO_IDP = 22; + [RubyConstant] + public const int IPPROTO_IGMP = 2; + [RubyConstant] + public const int IPPROTO_IP = 0; + [RubyConstant] + public const int IPPROTO_MAX = 256; + [RubyConstant] + public const int IPPROTO_ND = 77; + [RubyConstant] + public const int IPPROTO_PUP = 12; + [RubyConstant] + public const int IPPROTO_RAW = 255; + [RubyConstant] + public const int IPPROTO_TCP = 6; + [RubyConstant] + public const int IPPROTO_UDP = 17; + #endregion + + #region Message Options + [RubyConstant] + public const int MSG_DONTROUTE = 4; + [RubyConstant] + public const int MSG_OOB = 1; + [RubyConstant] + public const int MSG_PEEK = 2; + [RubyConstant] + #endregion + + #region Name Info + public const int NI_DGRAM = 16; + [RubyConstant] + public const int NI_MAXHOST = 1025; + [RubyConstant] + public const int NI_MAXSERV = 32; + [RubyConstant] + public const int NI_NAMEREQD = 4; + [RubyConstant] + public const int NI_NOFQDN = 1; + [RubyConstant] + public const int NI_NUMERICHOST = 2; + [RubyConstant] + public const int NI_NUMERICSERV = 8; + #endregion + + #region Protocol Family + [RubyConstant] + public const int PF_APPLETALK = (int)ProtocolFamily.AppleTalk; + [RubyConstant] + public const int PF_ATM = (int)ProtocolFamily.Atm; + [RubyConstant] + public const int PF_CCITT = (int)ProtocolFamily.Ccitt; + [RubyConstant] + public const int PF_CHAOS = (int)ProtocolFamily.Chaos; + [RubyConstant] + public const int PF_DATAKIT = (int)ProtocolFamily.DataKit; + [RubyConstant] + public const int PF_DLI = (int)ProtocolFamily.DataLink; + [RubyConstant] + public const int PF_ECMA = (int)ProtocolFamily.Ecma; + [RubyConstant] + public const int PF_HYLINK = (int)ProtocolFamily.HyperChannel; + [RubyConstant] + public const int PF_IMPLINK = (int)ProtocolFamily.ImpLink; + [RubyConstant] + public const int PF_INET = (int)ProtocolFamily.InterNetwork; + [RubyConstant] + public const int PF_IPX = (int)ProtocolFamily.Ipx; + [RubyConstant] + public const int PF_ISO = (int)ProtocolFamily.Iso; + [RubyConstant] + public const int PF_LAT = (int)ProtocolFamily.Lat; + [RubyConstant] + public const int PF_MAX = (int)ProtocolFamily.Max; + [RubyConstant] + public const int PF_NS = (int)ProtocolFamily.NS; + [RubyConstant] + public const int PF_OSI = (int)ProtocolFamily.Osi; + [RubyConstant] + public const int PF_PUP = (int)ProtocolFamily.Pup; + [RubyConstant] + public const int PF_SNA = (int)ProtocolFamily.Sna; + [RubyConstant] + public const int PF_UNIX = (int)ProtocolFamily.Unix; + [RubyConstant] + public const int PF_UNSPEC = (int)ProtocolFamily.Unspecified; + #endregion + + #region Socket Shutdown + [RubyConstant] + public const int SHUT_RD = (int)SocketShutdown.Receive; + [RubyConstant] + public const int SHUT_RDWR = (int)SocketShutdown.Both; + [RubyConstant] + public const int SHUT_WR = (int)SocketShutdown.Send; + #endregion + + #region Socket Type + [RubyConstant] + public const int SOCK_DGRAM = (int)SocketType.Dgram; + [RubyConstant] + public const int SOCK_RAW = (int)SocketType.Raw; + [RubyConstant] + public const int SOCK_RDM = (int)SocketType.Rdm; + [RubyConstant] + public const int SOCK_SEQPACKET = (int)SocketType.Seqpacket; + [RubyConstant] + public const int SOCK_STREAM = (int)SocketType.Stream; + #endregion + + [RubyConstant] + public const int SOL_SOCKET = 65535; + + #region Socket Option + [RubyConstant] + public const int SO_ACCEPTCONN = (int)SocketOptionName.AcceptConnection; + [RubyConstant] + public const int SO_BROADCAST = (int)SocketOptionName.Broadcast; + [RubyConstant] + public const int SO_DEBUG = (int)SocketOptionName.Debug; + [RubyConstant] + public const int SO_DONTROUTE = (int)SocketOptionName.DontRoute; + [RubyConstant] + public const int SO_ERROR = (int)SocketOptionName.Error; + [RubyConstant] + public const int SO_KEEPALIVE = (int)SocketOptionName.KeepAlive; + [RubyConstant] + public const int SO_LINGER = (int)SocketOptionName.Linger; + [RubyConstant] + public const int SO_OOBINLINE = (int)SocketOptionName.OutOfBandInline; + [RubyConstant] + public const int SO_RCVBUF = (int)SocketOptionName.ReceiveBuffer; + [RubyConstant] + public const int SO_RCVLOWAT = (int)SocketOptionName.ReceiveLowWater; + [RubyConstant] + public const int SO_RCVTIMEO = (int)SocketOptionName.ReceiveTimeout; + [RubyConstant] + public const int SO_REUSEADDR = (int)SocketOptionName.ReuseAddress; + [RubyConstant] + public const int SO_SNDBUF = (int)SocketOptionName.SendBuffer; + [RubyConstant] + public const int SO_SNDLOWAT = (int)SocketOptionName.SendLowWater; + [RubyConstant] + public const int SO_SNDTIMEO = (int)SocketOptionName.SendTimeout; + [RubyConstant] + public const int SO_TYPE = (int)SocketOptionName.Type; + [RubyConstant] + public const int SO_USELOOPBACK = (int)SocketOptionName.UseLoopback; + #endregion + + [RubyConstant] + public const int TCP_NODELAY = 1; + #endregion + + [RubyModule("Constants", BuildConfig ="!SILVERLIGHT")] + public class SocketConstants { + + #region Constants + + #region Address Family + [RubyConstant] + public const int AF_APPLETALK = (int)AddressFamily.AppleTalk; + [RubyConstant] + public const int AF_ATM = (int)AddressFamily.Atm; + [RubyConstant] + public const int AF_CCITT = (int)AddressFamily.Ccitt; + [RubyConstant] + public const int AF_CHAOS = (int)AddressFamily.Chaos; + [RubyConstant] + public const int AF_DATAKIT = (int)AddressFamily.DataKit; + [RubyConstant] + public const int AF_DLI = (int)AddressFamily.DataLink; + [RubyConstant] + public const int AF_ECMA = (int)AddressFamily.Ecma; + [RubyConstant] + public const int AF_HYLINK = (int)AddressFamily.HyperChannel; + [RubyConstant] + public const int AF_IMPLINK = (int)AddressFamily.ImpLink; + [RubyConstant] + public const int AF_INET = (int)AddressFamily.InterNetwork; + [RubyConstant] + public const int AF_IPX = (int)AddressFamily.Ipx; + [RubyConstant] + public const int AF_ISO = (int)AddressFamily.Iso; + [RubyConstant] + public const int AF_LAT = (int)AddressFamily.Lat; + [RubyConstant] + public const int AF_MAX = (int)AddressFamily.Max; + [RubyConstant] + public const int AF_NETBIOS = (int)AddressFamily.NetBios; + [RubyConstant] + public const int AF_NS = (int)AddressFamily.NS; + [RubyConstant] + public const int AF_OSI = (int)AddressFamily.Osi; + [RubyConstant] + public const int AF_PUP = (int)AddressFamily.Pup; + [RubyConstant] + public const int AF_SNA = (int)AddressFamily.Sna; + [RubyConstant] + public const int AF_UNIX = (int)AddressFamily.Unix; + [RubyConstant] + public const int AF_UNSPEC = (int)AddressFamily.Unspecified; + #endregion + + #region Flag Options for GetAddressInfo + [RubyConstant] + public const int AI_PASSIVE = 1; + [RubyConstant] + public const int AI_CANONNAME = 2; + [RubyConstant] + public const int AI_NUMERICHOST = 4; + [RubyConstant] + public const int AI_MASK = (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST); + + [RubyConstant] + public const int AI_ALL = 0x00000100; + [RubyConstant] + public const int AI_V4MAPPED_CFG = 0x00000200; + [RubyConstant] + public const int AI_ADDRCONFIG = 0x00000400; + [RubyConstant] + public const int AI_V4MAPPED = 0x00000800; + [RubyConstant] + public const int AI_DEFAULT = (AI_V4MAPPED_CFG | AI_ADDRCONFIG); + #endregion + + #region Error Return Codes from GetAddressInfo + [RubyConstant] + public const int EAI_ADDRFAMILY = 1; + [RubyConstant] + public const int EAI_AGAIN = 2; + [RubyConstant] + public const int EAI_BADFLAGS = 3; + [RubyConstant] + public const int EAI_BADHINTS = 12; + [RubyConstant] + public const int EAI_FAIL = 4; + [RubyConstant] + public const int EAI_FAMILY = 5; + [RubyConstant] + public const int EAI_MAX = 14; + [RubyConstant] + public const int EAI_MEMORY = 6; + [RubyConstant] + public const int EAI_NODATA = 7; + [RubyConstant] + public const int EAI_NONAME = 8; + [RubyConstant] + public const int EAI_PROTOCOL = 13; + [RubyConstant] + public const int EAI_SERVICE = 9; + [RubyConstant] + public const int EAI_SOCKTYPE = 10; + [RubyConstant] + public const int EAI_SYSTEM = 11; + #endregion + + #region + [RubyConstant] + public const uint INADDR_ALLHOSTS_GROUP = 0xe0000001; + [RubyConstant] + public const int INADDR_ANY = 0; + [RubyConstant] + public const uint INADDR_BROADCAST = 0xffffffff; + [RubyConstant] + public const int INADDR_LOOPBACK = 0x7F000001; + [RubyConstant] + public const uint INADDR_MAX_LOCAL_GROUP = 0xe00000ff; + [RubyConstant] + public const uint INADDR_NONE = 0xffffffff; + [RubyConstant] + public const uint INADDR_UNSPEC_GROUP = 0xe0000000; + #endregion + + #region IP Protocol Constants + [RubyConstant] + public const int IPPORT_RESERVED = 1024; + [RubyConstant] + public const int IPPORT_USERRESERVED = 5000; + [RubyConstant] + public const int IPPROTO_GGP = 3; + [RubyConstant] + public const int IPPROTO_ICMP = 1; + [RubyConstant] + public const int IPPROTO_IDP = 22; + [RubyConstant] + public const int IPPROTO_IGMP = 2; + [RubyConstant] + public const int IPPROTO_IP = 0; + [RubyConstant] + public const int IPPROTO_MAX = 256; + [RubyConstant] + public const int IPPROTO_ND = 77; + [RubyConstant] + public const int IPPROTO_PUP = 12; + [RubyConstant] + public const int IPPROTO_RAW = 255; + [RubyConstant] + public const int IPPROTO_TCP = 6; + [RubyConstant] + public const int IPPROTO_UDP = 17; + #endregion + + #region Message Options + [RubyConstant] + public const int MSG_DONTROUTE = 4; + [RubyConstant] + public const int MSG_OOB = 1; + [RubyConstant] + public const int MSG_PEEK = 2; + [RubyConstant] + #endregion + + #region Name Info + public const int NI_DGRAM = 16; + [RubyConstant] + public const int NI_MAXHOST = 1025; + [RubyConstant] + public const int NI_MAXSERV = 32; + [RubyConstant] + public const int NI_NAMEREQD = 4; + [RubyConstant] + public const int NI_NOFQDN = 1; + [RubyConstant] + public const int NI_NUMERICHOST = 2; + [RubyConstant] + public const int NI_NUMERICSERV = 8; + #endregion + + #region Protocol Family + [RubyConstant] + public const int PF_APPLETALK = (int)ProtocolFamily.AppleTalk; + [RubyConstant] + public const int PF_ATM = (int)ProtocolFamily.Atm; + [RubyConstant] + public const int PF_CCITT = (int)ProtocolFamily.Ccitt; + [RubyConstant] + public const int PF_CHAOS = (int)ProtocolFamily.Chaos; + [RubyConstant] + public const int PF_DATAKIT = (int)ProtocolFamily.DataKit; + [RubyConstant] + public const int PF_DLI = (int)ProtocolFamily.DataLink; + [RubyConstant] + public const int PF_ECMA = (int)ProtocolFamily.Ecma; + [RubyConstant] + public const int PF_HYLINK = (int)ProtocolFamily.HyperChannel; + [RubyConstant] + public const int PF_IMPLINK = (int)ProtocolFamily.ImpLink; + [RubyConstant] + public const int PF_INET = (int)ProtocolFamily.InterNetwork; + [RubyConstant] + public const int PF_IPX = (int)ProtocolFamily.Ipx; + [RubyConstant] + public const int PF_ISO = (int)ProtocolFamily.Iso; + [RubyConstant] + public const int PF_LAT = (int)ProtocolFamily.Lat; + [RubyConstant] + public const int PF_MAX = (int)ProtocolFamily.Max; + [RubyConstant] + public const int PF_NS = (int)ProtocolFamily.NS; + [RubyConstant] + public const int PF_OSI = (int)ProtocolFamily.Osi; + [RubyConstant] + public const int PF_PUP = (int)ProtocolFamily.Pup; + [RubyConstant] + public const int PF_SNA = (int)ProtocolFamily.Sna; + [RubyConstant] + public const int PF_UNIX = (int)ProtocolFamily.Unix; + [RubyConstant] + public const int PF_UNSPEC = (int)ProtocolFamily.Unspecified; + #endregion + + #region Socket Shutdown + [RubyConstant] + public const int SHUT_RD = (int)SocketShutdown.Receive; + [RubyConstant] + public const int SHUT_RDWR = (int)SocketShutdown.Both; + [RubyConstant] + public const int SHUT_WR = (int)SocketShutdown.Send; + #endregion + + #region Socket Type + [RubyConstant] + public const int SOCK_DGRAM = (int)SocketType.Dgram; + [RubyConstant] + public const int SOCK_RAW = (int)SocketType.Raw; + [RubyConstant] + public const int SOCK_RDM = (int)SocketType.Rdm; + [RubyConstant] + public const int SOCK_SEQPACKET = (int)SocketType.Seqpacket; + [RubyConstant] + public const int SOCK_STREAM = (int)SocketType.Stream; + #endregion + + [RubyConstant] + public const int SOL_SOCKET = 65535; + + #region Socket Option + [RubyConstant] + public const int SO_ACCEPTCONN = (int)SocketOptionName.AcceptConnection; + [RubyConstant] + public const int SO_BROADCAST = (int)SocketOptionName.Broadcast; + [RubyConstant] + public const int SO_DEBUG = (int)SocketOptionName.Debug; + [RubyConstant] + public const int SO_DONTROUTE = (int)SocketOptionName.DontRoute; + [RubyConstant] + public const int SO_ERROR = (int)SocketOptionName.Error; + [RubyConstant] + public const int SO_KEEPALIVE = (int)SocketOptionName.KeepAlive; + [RubyConstant] + public const int SO_LINGER = (int)SocketOptionName.Linger; + [RubyConstant] + public const int SO_OOBINLINE = (int)SocketOptionName.OutOfBandInline; + [RubyConstant] + public const int SO_RCVBUF = (int)SocketOptionName.ReceiveBuffer; + [RubyConstant] + public const int SO_RCVLOWAT = (int)SocketOptionName.ReceiveLowWater; + [RubyConstant] + public const int SO_RCVTIMEO = (int)SocketOptionName.ReceiveTimeout; + [RubyConstant] + public const int SO_REUSEADDR = (int)SocketOptionName.ReuseAddress; + [RubyConstant] + public const int SO_SNDBUF = (int)SocketOptionName.SendBuffer; + [RubyConstant] + public const int SO_SNDLOWAT = (int)SocketOptionName.SendLowWater; + [RubyConstant] + public const int SO_SNDTIMEO = (int)SocketOptionName.SendTimeout; + [RubyConstant] + public const int SO_TYPE = (int)SocketOptionName.Type; + [RubyConstant] + public const int SO_USELOOPBACK = (int)SocketOptionName.UseLoopback; + #endregion + + [RubyConstant] + public const int TCP_NODELAY = 1; + #endregion + + } + + #region Private Helpers + + private static int ParseInteger(RubyContext/*!*/ context, string/*!*/ str) { + bool isNegative = false; + if (str[0] == '-') { + isNegative = true; + str = str.Remove(0, 1); + } + + Tokenizer tokenizer = new Tokenizer(false, ErrorSink.Null); + tokenizer.Initialize(context.CreateSnippet(str, SourceCodeKind.File)); + tokenizer.GetNextToken(); + TokenValue value = tokenizer.TokenValue; + TokenValueType type = value.Type; + tokenizer.GetNextToken(); + TokenValueType nextType = tokenizer.TokenValue.Type; + + // We are only interested in the whole string being a valid Integer + if (type == TokenValueType.Integer && nextType == TokenValueType.None) { + return isNegative ? -value.Integer : value.Integer; + } else { + throw RubyExceptions.CreateTypeConversionError("String", "Integer"); + } + } + + #endregion + } +} +#endif diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/SocketError.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/SocketError.cs new file mode 100644 index 0000000000..c1f59705d8 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/SocketError.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using IronRuby.Runtime; +using IronRuby.Builtins; + +namespace IronRuby.StandardLibrary.Sockets { + [RubyClass("SocketError", BuildConfig = "!SILVERLIGHT", Extends = typeof(SocketException), Inherits = typeof(SystemException))] + [HideMethod("message")] // SocketException overrides Message so we have to hide it here + public static class SocketErrorOps { + [RubyConstructor] + public static Exception/*!*/ Create(RubyClass/*!*/ self, [DefaultParameterValue(null)]object message) { + return RubyExceptionData.InitializeException(new SocketException(0), message ?? MutableString.Create("SocketError")); + + } + + public static Exception/*!*/ Create(string/*!*/ message) { + return RubyExceptionData.InitializeException(new SocketException(0), MutableString.Create(message)); + } + } +} + +#endif diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/SocketStream.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/SocketStream.cs new file mode 100644 index 0000000000..737296b9e4 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/SocketStream.cs @@ -0,0 +1,125 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System; +using System.Collections.Generic; +using System.Net.Sockets; + +namespace IronRuby.StandardLibrary.Sockets { + internal class SocketStream : System.IO.Stream { + public SocketStream(/*!*/Socket s) { + _socket = s; + } + + private long _pos = 0; + private byte _lastByteRead; + private bool _peeked = false; + private List _internalWriteBuffer = new List(); + internal readonly Socket/*!*/ _socket; + + public override bool CanRead { + get { return true; } + } + + public override bool CanSeek { + get { return false; } + } + + public override bool CanWrite { + get { return true; } + } + + public override void Close() { + base.Close(); + _socket.Close(); + } + + public override void Flush() { + byte[] bufferedData = _internalWriteBuffer.ToArray(); + int bytesSent = _socket.Send(bufferedData); + if (bytesSent < bufferedData.Length) { + // TODO: Resend the rest + } + _internalWriteBuffer.Clear(); + } + + public override long Length { + get { throw new Exception("The method or operation is not implemented."); } + } + + public override long Position { + get { + return _pos; + } + set { + long diff = _pos - value; + if (diff == 1) { + _peeked = true; + } + + _pos = value; + } + } + + public override int Read(byte[] buffer, int offset, int count) { + int bytesToRead = _peeked ? count - 1 : count; + byte[] readBuffer = new byte[bytesToRead]; + long oldPos = _pos; + + if (bytesToRead > 0) { + int bytesRead = _socket.Receive(readBuffer, bytesToRead, SocketFlags.None); + _pos += bytesRead; + } + + if (_peeked) { + // Put the byte we've already peeked at the beginning of the buffer + buffer[offset] = _lastByteRead; + // Put the rest of the data afterwards + Array.Copy(readBuffer, 0, buffer, offset + 1, count - 1); + _pos += 1; + _peeked = false; + } else { + Array.Copy(readBuffer, 0, buffer, offset, count); + } + + int totalBytesRead = (int)(_pos - oldPos); + if (totalBytesRead > 0) { + _lastByteRead = buffer[totalBytesRead - 1]; + } + + return totalBytesRead; + } + + public override long Seek(long offset, System.IO.SeekOrigin origin) { + throw new NotSupportedException("The method or operation is not implemented."); + } + + public override void SetLength(long value) { + throw new NotSupportedException("The method or operation is not implemented."); + } + + public override void Write(byte[] buffer, int offset, int count) { + for (int i = offset; i < offset + count; i++) { + _internalWriteBuffer.Add(buffer[i]); + if (buffer[i] == '\n') { + Flush(); + } + } + } + } +} +#endif diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/TCPServer.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/TCPServer.cs new file mode 100644 index 0000000000..78e0edc8ce --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/TCPServer.cs @@ -0,0 +1,133 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Threading; +using IronRuby.Builtins; +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.Sockets { + [RubyClass("TCPServer", BuildConfig = "!SILVERLIGHT")] + public class TCPServer : TCPSocket { + private object _mutex = new object(); + private IAsyncResult _acceptResult; + + public TCPServer(RubyContext/*!*/ context, Socket/*!*/ socket) + : base(context, socket) { + } + + public override WaitHandle/*!*/ CreateReadWaitHandle() { + return GetAcceptResult().AsyncWaitHandle; + } + + private IAsyncResult/*!*/ GetAcceptResult() { + if (_acceptResult == null) { + lock (_mutex) { + if (_acceptResult == null) { + _acceptResult = Socket.BeginAccept(null, null); + } + } + } + return _acceptResult; + } + + private Socket/*!*/ Accept() { + // acquire the result and replace it by null, so that no other thread can acquire it: + IAsyncResult result = Interlocked.Exchange(ref _acceptResult, null); + + if (result == null) { + return Socket.Accept(); + } + + // wait until accept finishes: + return Socket.EndAccept(result); + } + + [RubyConstructor] + public static TCPServer/*!*/ CreateTCPServer(RubyClass/*!*/ self, + [DefaultProtocol]MutableString hostname, [DefaultParameterValue(null)]object port) { + + IPAddress listeningInterface = null; + if (hostname == null) { + listeningInterface = new IPAddress(0); + } else { + string hostnameStr = hostname.ConvertToString(); + if (hostnameStr == IPAddress.Any.ToString()) { + listeningInterface = IPAddress.Any; + } else if (hostnameStr == IPAddress.Loopback.ToString()) { + listeningInterface = IPAddress.Loopback; + } else if (!IPAddress.TryParse(hostnameStr, out listeningInterface)) { + + // look up the host IP from DNS + IPHostEntry hostEntry = Dns.GetHostEntry(hostnameStr); + foreach (IPAddress address in hostEntry.AddressList) { + if (address.AddressFamily == AddressFamily.InterNetwork) { + listeningInterface = address; + break; + } + } + if (listeningInterface == null) { + // TODO: do we need to support any other address family types? + // (presumably should support at least IPv6) + throw new NotImplementedException("TODO: non-inet addresses"); + } + } + Assert.NotNull(listeningInterface); + } + + Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + socket.Bind(new IPEndPoint(listeningInterface, ConvertToPortNum(self.Context, port))); + socket.Listen(10); + + return new TCPServer(self.Context, socket); + } + + [RubyMethod("accept")] + public static TCPSocket/*!*/ Accept(RubyContext/*!*/ context, TCPServer/*!*/ self) { + return new TCPSocket(context, self.Accept()); + } + + [RubyMethod("accept_nonblock")] + public static TCPSocket/*!*/ AcceptNonBlocking(RubyContext/*!*/ context, TCPServer/*!*/ self) { + bool blocking = self.Socket.Blocking; + try { + self.Socket.Blocking = false; + return Accept(context, self); + } finally { + // Reset the blocking + self.Socket.Blocking = blocking; + } + } + + [RubyMethod("sysaccept")] + public static int SysAccept(RubyContext/*!*/ context, TCPServer/*!*/ self) { + return Accept(context, self).FileDescriptor; + } + + [RubyMethod("listen")] + public static void Listen(TCPServer/*!*/ self, int backlog) { + self.Socket.Listen(backlog); + } + } +} +#endif diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/TCPSocket.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/TCPSocket.cs new file mode 100644 index 0000000000..3e651b26f8 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/TCPSocket.cs @@ -0,0 +1,46 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Net.Sockets; +using Microsoft.Scripting.Runtime; +using IronRuby.Builtins; +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.Sockets { + [RubyClass("TCPSocket", BuildConfig = "!SILVERLIGHT")] + public class TCPSocket : IPSocket { + public TCPSocket(RubyContext/*!*/ context, Socket/*!*/ socket) + : base(context, socket) { + } + + [RubyMethod("gethostbyname", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ GetHostByName(RubyClass/*!*/ self, object hostName) { + return InternalGetHostByName(self, hostName, false); + } + + [RubyConstructor] + public static TCPSocket/*!*/ CreateTCPSocket(RubyClass/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ remoteHost, object remotePort) { + int port = ConvertToPortNum(self.Context, remotePort); + + Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + socket.Connect(remoteHost.ConvertToString(), port); + + return new TCPSocket(self.Context, socket); + } + } +} +#endif diff --git a/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/UDPSocket.cs b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/UDPSocket.cs new file mode 100644 index 0000000000..aaaea68af1 --- /dev/null +++ b/merlin/main/languages/ruby/Libraries.LCA_RESTRICTED/socket/UDPSocket.cs @@ -0,0 +1,116 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Net; +using System.Net.Sockets; +using Microsoft.Scripting.Runtime; +using IronRuby.Builtins; +using IronRuby.Runtime; + +namespace IronRuby.StandardLibrary.Sockets { + [RubyClass("UDPSocket", BuildConfig = "!SILVERLIGHT")] + public class UDPSocket : IPSocket { + public UDPSocket(RubyContext/*!*/ context, Socket/*!*/ socket) + : base(context, socket) { + } + + [RubyConstructor] + public static UDPSocket/*!*/ CreateUDPSocket(RubyClass/*!*/ self) { + Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + return new UDPSocket(self.Context, socket); + } + + [RubyConstructor] + public static UDPSocket/*!*/ CreateUDPSocket(RubyClass/*!*/ self, object family) { + AddressFamily addressFamily = ConvertToAddressFamily(self.Context, family); + Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + return new UDPSocket(self.Context, socket); + } + + #region Public Instance Methods + + [RubyMethod("bind")] + public static int Bind(RubyContext/*!*/ context, UDPSocket/*!*/ self, object hostname, object port) { + int iPort = ConvertToPortNum(context, port); + if (hostname == null) { + hostname = MutableString.Create("localhost"); + } + MutableString address = GetAddressInternal(context, hostname); + IPEndPoint ep = new IPEndPoint(IPAddress.Parse(address.ConvertToString()), iPort); + self.Socket.Bind(ep); + return 0; + } + + [RubyMethod("connect")] + public static int Connect(RubyContext/*!*/ context, UDPSocket/*!*/ self, object hostname, object port) { + MutableString strHostname = ConvertToHostString(context, hostname); + int iPort = ConvertToPortNum(context, port); + self.Socket.Connect(strHostname.ConvertToString(), iPort); + return 0; + } + + [RubyMethod("recvfrom_nonblock")] + public static RubyArray/*!*/ ReceiveFromNonBlocking(RubyContext/*!*/ context, IPSocket/*!*/ self, int length) { + bool blocking = self.Socket.Blocking; + try { + self.Socket.Blocking = false; + return ReceiveFrom(context, self, length, null); + } finally { + // Reset the blocking + self.Socket.Blocking = blocking; + } + } + + [RubyMethod("recvfrom_nonblock")] + public static RubyArray/*!*/ ReceiveFromNonBlocking(RubyContext/*!*/ context, IPSocket/*!*/ self, int length, object/*Numeric*/ flags) { + bool blocking = self.Socket.Blocking; + try { + self.Socket.Blocking = false; + return ReceiveFrom(context, self, length, flags); + } finally { + // Reset the blocking + self.Socket.Blocking = blocking; + } + } + + [RubyMethod("send")] + public static int Send(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, object message, object flags, object hostname, object port) { + Protocols.CheckSafeLevel(context, 4, "send"); + // Convert the parameters + SocketFlags sFlags = ConvertToSocketFlag(context, flags); + MutableString strMessage = Protocols.CastToString(context, message); + MutableString address = GetAddressInternal(context, hostname); + int iPort = ConvertToPortNum(context, port); + EndPoint toEndPoint = new IPEndPoint(IPAddress.Parse(address.ConvertToString()), iPort); + return self.Socket.SendTo(strMessage.ConvertToBytes(), sFlags, toEndPoint); + } + + // These overwritten methods have to be here because we kill the ones in RubyBasicSocket by creating the one above + [RubyMethod("send")] + public static new int Send(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, object message, object flags) { + return RubyBasicSocket.Send(context, self, message, flags); + } + + [RubyMethod("send")] + public static new int Send(RubyContext/*!*/ context, RubyBasicSocket/*!*/ self, object message, object flags, object to) { + return RubyBasicSocket.Send(context, self, message, flags, to); + } + + #endregion + } +} +#endif diff --git a/merlin/main/languages/ruby/Libs/PresentationCore.rb b/merlin/main/languages/ruby/Libs/PresentationCore.rb new file mode 100644 index 0000000000..3aecb9cf19 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/PresentationCore.rb @@ -0,0 +1 @@ +require 'PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' diff --git a/merlin/main/languages/ruby/Libs/PresentationFramework.rb b/merlin/main/languages/ruby/Libs/PresentationFramework.rb new file mode 100644 index 0000000000..ceb450f8fd --- /dev/null +++ b/merlin/main/languages/ruby/Libs/PresentationFramework.rb @@ -0,0 +1 @@ +require 'PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' diff --git a/merlin/main/languages/ruby/Libs/System.Data.rb b/merlin/main/languages/ruby/Libs/System.Data.rb new file mode 100644 index 0000000000..f855218385 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/System.Data.rb @@ -0,0 +1 @@ +require 'System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' diff --git a/merlin/main/languages/ruby/Libs/System.Drawing.rb b/merlin/main/languages/ruby/Libs/System.Drawing.rb new file mode 100644 index 0000000000..a42f2648ec --- /dev/null +++ b/merlin/main/languages/ruby/Libs/System.Drawing.rb @@ -0,0 +1 @@ +require 'System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' diff --git a/merlin/main/languages/ruby/Libs/bigdecimal.rb b/merlin/main/languages/ruby/Libs/bigdecimal.rb new file mode 100644 index 0000000000..b2822f0fd0 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/bigdecimal.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.BigDecimal' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libs/clr.rb b/merlin/main/languages/ruby/Libs/clr.rb new file mode 100644 index 0000000000..670458749d --- /dev/null +++ b/merlin/main/languages/ruby/Libs/clr.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.Clr' diff --git a/merlin/main/languages/ruby/Libs/digest.rb b/merlin/main/languages/ruby/Libs/digest.rb new file mode 100644 index 0000000000..ac5e43cff5 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/digest.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.Digest' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libs/digest/md5.rb b/merlin/main/languages/ruby/Libs/digest/md5.rb new file mode 100644 index 0000000000..5d30ed0f36 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/digest/md5.rb @@ -0,0 +1,17 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require 'digest' +# placeholder no-op file diff --git a/merlin/main/languages/ruby/Libs/digest/sha1.rb b/merlin/main/languages/ruby/Libs/digest/sha1.rb new file mode 100644 index 0000000000..a7364da5e3 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/digest/sha1.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require 'digest' diff --git a/merlin/main/languages/ruby/Libs/enumerator.rb b/merlin/main/languages/ruby/Libs/enumerator.rb new file mode 100644 index 0000000000..5759561006 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/enumerator.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.Enumerator' diff --git a/merlin/main/languages/ruby/Libs/etc.rb b/merlin/main/languages/ruby/Libs/etc.rb new file mode 100644 index 0000000000..a43febf466 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/etc.rb @@ -0,0 +1 @@ +# No-op file since we won't support this under Windows diff --git a/merlin/main/languages/ruby/Libs/fcntl.rb b/merlin/main/languages/ruby/Libs/fcntl.rb new file mode 100644 index 0000000000..eabc4379c6 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/fcntl.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.FileControl' diff --git a/merlin/main/languages/ruby/Libs/hacks.rb b/merlin/main/languages/ruby/Libs/hacks.rb new file mode 100644 index 0000000000..f43443c03f --- /dev/null +++ b/merlin/main/languages/ruby/Libs/hacks.rb @@ -0,0 +1,50 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# Hacked up implementation of Method + +class Method + def to_proc + Proc.new { |*args| self.call(*args) } + end +end + +# Subclass Tracking + +module SubclassTracking + class Holder < Array + def each_object(klass, &b) + self.each { |c| b[c] } + end + end + def self.extended(klass) + (class << klass; self; end).send :attr_accessor, :subclasses + (class << klass; self; end).send :define_method, :inherited do |klazz| + klass.subclasses << klazz + super + end + klass.subclasses = Holder.new + end +end + +class IO + def flush; end # nop +end + +class String + def hex + self.to_i(16) + end +end diff --git a/merlin/main/languages/ruby/Libs/iconv.rb b/merlin/main/languages/ruby/Libs/iconv.rb new file mode 100644 index 0000000000..ad2201f7c2 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/iconv.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.Iconv' diff --git a/merlin/main/languages/ruby/Libs/nkf.rb b/merlin/main/languages/ruby/Libs/nkf.rb new file mode 100644 index 0000000000..dc85bf94fd --- /dev/null +++ b/merlin/main/languages/ruby/Libs/nkf.rb @@ -0,0 +1,33 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# HACKHACKHACK: Stub implementation of NKF + +module NKF + AUTO = 0 + JIS = 1 + EUC = 2 + SJIS = 3 + NOCONV = 4 + ASCII = 5 + BINARY = 4 + NKF_RELEASE_DATE = "2007-01-28" + NKF_VERSION = "2.0.8" + UNKNOWN = 0 + UTF16 = 8 + UTF32 = 12 + UTF8 = 6 + VERSION = "2.0.8 (2007-01-28)" +end diff --git a/merlin/main/languages/ruby/Libs/openssl.rb b/merlin/main/languages/ruby/Libs/openssl.rb new file mode 100644 index 0000000000..9608003ae0 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/openssl.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.OpenSsl' \ No newline at end of file diff --git a/merlin/main/languages/ruby/Libs/parse_tree.rb b/merlin/main/languages/ruby/Libs/parse_tree.rb new file mode 100644 index 0000000000..3e4312ad41 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/parse_tree.rb @@ -0,0 +1,253 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +abort "*** Sorry, ParseTree doesn't work with ruby #{RUBY_VERSION}" if + RUBY_VERSION >= "1.9" + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.ParseTree' + +class Module + def modules + ancestors[1..-1] + end +end + +class Class + def modules + a = self.ancestors + a[1..a.index(superclass)-1] + end +end + +## +# ParseTree is a RubyInline-style extension that accesses and +# traverses the internal parse tree created by ruby. +# +# class Example +# def blah +# return 1 + 1 +# end +# end +# +# ParseTree.new.parse_tree(Example) +# => [[:class, :Example, :Object, +# [:defn, +# "blah", +# [:scope, +# [:block, +# [:args], +# [:return, [:call, [:lit, 1], "+", [:array, [:lit, 1]]]]]]]]] + +class ParseTree + + VERSION = '2.1.1' + + ## + # Front end translation method. + + def self.translate(klass_or_str, method=nil) + pt = self.new(false) + case klass_or_str + when String then + sexp = pt.parse_tree_for_string(klass_or_str).first + if method then + # class, scope, block, *methods + sexp.last.last[1..-1].find do |defn| + defn[1] == method + end + else + sexp + end + else + unless method.nil? then + if method.to_s =~ /^self\./ then + method = method.to_s[5..-1].intern + pt.parse_tree_for_method(klass_or_str, method, true) + else + pt.parse_tree_for_method(klass_or_str, method) + end + else + pt.parse_tree(klass_or_str).first + end + end + end + + ## + # Initializes a ParseTree instance. Includes newline nodes if + # +include_newlines+ which defaults to +$DEBUG+. + + def initialize(include_newlines=$DEBUG) + @include_newlines = include_newlines + end + + ## + # Main driver for ParseTree. Returns an array of arrays containing + # the parse tree for +klasses+. + # + # Structure: + # + # [[:class, classname, superclassname, [:defn :method1, ...], ...], ...] + # + # NOTE: v1.0 - v1.1 had the signature (klass, meth=nil). This wasn't + # used much at all and since parse_tree_for_method already existed, + # it was deemed more useful to expand this method to do multiple + # classes. + + def parse_tree(*klasses) + result = [] + klasses.each do |klass| + klassname = klass.name rescue '' # HACK klass.name should never be nil + # Tempfile's DelegateClass(File) seems to + # cause this + klassname = "UnnamedClass_#{klass.object_id}" if klassname.empty? + klassname = klassname.to_sym + + code = if Class === klass then + sc = klass.superclass + sc_name = ((sc.nil? or sc.name.empty?) ? "nil" : sc.name).intern + [:class, klassname, [:const, sc_name]] + else + [:module, klassname] + end + + method_names = [] + method_names += klass.instance_methods false + method_names += klass.private_instance_methods false + # protected methods are included in instance_methods, go figure! + + method_names.sort.each do |m| + r = parse_tree_for_method(klass, m.to_sym) + code << r + end + + klass.modules.each do |mod| # TODO: add a test for this + mod.instance_methods.each do |m| + r = parse_tree_for_method(mod, m.to_sym) + code << r + end + end + + klass.singleton_methods(false).sort.each do |m| + code << parse_tree_for_method(klass, m.to_sym, true) + end + + result << code + end + return result + end + + ## + # Returns the parse tree for just one +method+ of a class +klass+. + # + # Format: + # + # [:defn, :name, :body] + + def parse_tree_for_method(klass, method, is_cls_meth=false) + $stderr.puts "** parse_tree_for_method(#{klass}, #{method}):" if $DEBUG + r = parse_tree_for_meth(klass, method.to_sym, is_cls_meth) + r + end + + ## + # Returns the parse tree for a string +source+. + # + # Format: + # + # [[sexps] ... ] + + def parse_tree_for_string(source, filename = '(string)', line = 1) + old_verbose, $VERBOSE = $VERBOSE, true + return parse_tree_for_str0(source, filename, line) + ensure + $VERBOSE = old_verbose + end + + def parse_tree_for_str0(*__1args2__) # :nodoc: + parse_tree_for_str(*__1args2__) # just helps clean up the binding + end + + if RUBY_VERSION < "1.8.4" then + inline do |builder| + builder.add_type_converter("bool", '', '') + builder.c_singleton " + bool has_alloca() { + (void)self; + #ifdef C_ALLOCA + return Qtrue; + #else + return Qfalse; + #endif + }" + end + else + def self.has_alloca + true + end + end + + + NODE_NAMES = [ + # 00 + :method, :fbody, :cfunc, :scope, :block, + :if, :case, :when, :opt_n, :while, + # 10 + :until, :iter, :for, :break, :next, + :redo, :retry, :begin, :rescue, :resbody, + # 20 + :ensure, :and, :or, :not, :masgn, + :lasgn, :dasgn, :dasgn_curr, :gasgn, :iasgn, + # 30 + :cdecl, :cvasgn, :cvdecl, :op_asgn1, :op_asgn2, + :op_asgn_and, :op_asgn_or, :call, :fcall, :vcall, + # 40 + :super, :zsuper, :array, :zarray, :hash, + :return, :yield, :lvar, :dvar, :gvar, + # 50 + :ivar, :const, :cvar, :nth_ref, :back_ref, + :match, :match2, :match3, :lit, :str, + # 60 + :dstr, :xstr, :dxstr, :evstr, :dregx, + :dregx_once, :args, :argscat, :argspush, :splat, + # 70 + :to_ary, :svalue, :block_arg, :block_pass, :defn, + :defs, :alias, :valias, :undef, :class, + # 80 + :module, :sclass, :colon2, :colon3, :cref, + :dot2, :dot3, :flip2, :flip3, :attrset, + # 90 + :self, :nil, :true, :false, :defined, + # 95 + :newline, :postexe, :alloca, :dmethod, :bmethod, + # 100 + :memo, :ifunc, :dsym, :attrasgn, + :last + ] + + if RUBY_VERSION > "1.9" then + NODE_NAMES.insert NODE_NAMES.index(:hash), :values + NODE_NAMES.insert NODE_NAMES.index(:defined), :errinfo + NODE_NAMES.insert NODE_NAMES.index(:last), :prelude, :lambda + NODE_NAMES.delete :dmethod + NODE_NAMES[128] = NODE_NAMES.delete :newline + end + + ############################################################ + # END of rdoc methods + ############################################################ + + include ::IronRuby::ParseTree + +end # ParseTree class diff --git a/merlin/main/languages/ruby/Libs/rbconfig.rb b/merlin/main/languages/ruby/Libs/rbconfig.rb new file mode 100644 index 0000000000..161d1a193d --- /dev/null +++ b/merlin/main/languages/ruby/Libs/rbconfig.rb @@ -0,0 +1,175 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +module Config + RUBY_VERSION == "1.8.6" or + raise "ruby lib version (1.8.6) doesn't match executable version (#{RUBY_VERSION})" + + # TODO: Temporary hack to locate where we are based on relative MERLIN + # layout paths. We will replace this with just the path to this file when + # we build out the Ruby/libs directory to contain our own private copy of + # the Ruby libraries + # Note that this symbol should be redefined by the packaging script for binary + # layouts + TOPDIR = File.dirname(__FILE__) + '/../../../../External/languages/ruby/ruby-1.8.6/' + + DESTDIR = TOPDIR && TOPDIR[/\A[a-z]:/i] || '' unless defined? DESTDIR + CONFIG = {} + CONFIG["DESTDIR"] = DESTDIR + CONFIG["MAJOR"] = "1" + CONFIG["MINOR"] = "8" + CONFIG["TEENY"] = "6" + CONFIG["PATCHLEVEL"] = "0" + CONFIG["prefix"] = (TOPDIR || DESTDIR + "") + CONFIG["EXEEXT"] = ".exe" + # TODO: change back to ironruby + CONFIG["ruby_install_name"] = "ruby" + CONFIG["RUBY_INSTALL_NAME"] = "ruby" + # END TODO: + CONFIG["RUBY_SO_NAME"] = "msvcrt-ruby18" + CONFIG["SHELL"] = "$(COMSPEC)" + CONFIG["BUILD_FILE_SEPARATOR"] = "\\" + CONFIG["PATH_SEPARATOR"] = ";" + CONFIG["CFLAGS"] = "-MD -Zi -O2b2xg- -G6" + CONFIG["CPPFLAGS"] = "" + CONFIG["CXXFLAGS"] = "" + CONFIG["FFLAGS"] = "" + CONFIG["LDFLAGS"] = "" + CONFIG["LIBS"] = "oldnames.lib user32.lib advapi32.lib ws2_32.lib " + CONFIG["exec_prefix"] = "$(prefix)" + CONFIG["bindir"] = "$(exec_prefix)/bin" + CONFIG["sbindir"] = "$(exec_prefix)/sbin" + CONFIG["libexecdir"] = "$(exec_prefix)/libexec" + CONFIG["datadir"] = "$(prefix)/share" + CONFIG["sysconfdir"] = "$(prefix)/etc" + CONFIG["sharedstatedir"] = "$(DESTDIR)/etc" + CONFIG["localstatedir"] = "$(DESTDIR)/var" + CONFIG["libdir"] = "$(exec_prefix)/lib" + CONFIG["includedir"] = "$(prefix)/include" + CONFIG["oldincludedir"] = "/usr/include" + CONFIG["infodir"] = "$(prefix)/info" + CONFIG["mandir"] = "$(prefix)/man" + CONFIG["build"] = "i686-pc-mswin32" + CONFIG["build_alias"] = "i686-mswin32" + CONFIG["build_cpu"] = "i686" + CONFIG["build_vendor"] = "pc" + CONFIG["build_os"] = "mswin32" + CONFIG["host"] = "i686-pc-mswin32" + CONFIG["host_alias"] = "i686-mswin32" + CONFIG["host_cpu"] = "i686" + CONFIG["host_vendor"] = "pc" + CONFIG["host_os"] = "mswin32" + CONFIG["target"] = "i386-pc-mswin32" + CONFIG["target_alias"] = "i386-mswin32" + CONFIG["target_cpu"] = "i386" + CONFIG["target_vendor"] = "pc" + CONFIG["target_os"] = "mswin32" + CONFIG["CC"] = "cl -nologo" + CONFIG["CPP"] = "cl -nologo -E" + CONFIG["YACC"] = "byacc" + CONFIG["RANLIB"] = "" + CONFIG["AR"] = "lib -nologo" + CONFIG["ARFLAGS"] = "-machine:x86 -out:" + CONFIG["LN_S"] = "" + CONFIG["SET_MAKE"] = "" + CONFIG["CP"] = "copy > nul" + CONFIG["ALLOCA"] = "" + CONFIG["DEFAULT_KCODE"] = "" + CONFIG["OBJEXT"] = "obj" + CONFIG["XCFLAGS"] = "-DRUBY_EXPORT -I. -IC:/develop/win/ruby/ruby-1.8.6 -IC:/develop/win/ruby/ruby-1.8.6/missing" + CONFIG["XLDFLAGS"] = "-stack:0x2000000" + CONFIG["DLDFLAGS"] = "-link -incremental:no -debug -opt:ref -opt:icf -dll $(LIBPATH) -def:$(DEFFILE) -implib:$(*F:.so=)-$(arch).lib -pdb:$(*F:.so=)-$(arch).pdb" + CONFIG["ARCH_FLAG"] = "" + CONFIG["STATIC"] = "" + CONFIG["CCDLFLAGS"] = "" + CONFIG["LDSHARED"] = "cl -nologo -LD" + CONFIG["DLEXT"] = "so" + CONFIG["DLEXT2"] = "dll" + CONFIG["LIBEXT"] = "lib" + CONFIG["STRIP"] = "" + CONFIG["EXTSTATIC"] = "" + CONFIG["setup"] = "Setup" + CONFIG["MINIRUBY"] = ".\\miniruby.exe " + CONFIG["PREP"] = "miniruby.exe" + CONFIG["RUNRUBY"] = ".\\ruby.exe \"C:/develop/win/ruby/ruby-1.8.6/runruby.rb\" --extout=\".ext\" --" + CONFIG["EXTOUT"] = ".ext" + CONFIG["ARCHFILE"] = "" + CONFIG["RDOCTARGET"] = "install-nodoc" + CONFIG["LIBRUBY_LDSHARED"] = "cl -nologo -LD" + CONFIG["LIBRUBY_DLDFLAGS"] = " -def:msvcrt-ruby18.def" + CONFIG["rubyw_install_name"] = "rubyw" + CONFIG["RUBYW_INSTALL_NAME"] = "rubyw" + CONFIG["LIBRUBY_A"] = "$(RUBY_SO_NAME)-static.lib" + CONFIG["LIBRUBY_SO"] = "$(RUBY_SO_NAME).dll" + CONFIG["LIBRUBY_ALIASES"] = "" + CONFIG["LIBRUBY"] = "$(RUBY_SO_NAME).lib" + CONFIG["LIBRUBYARG"] = "$(LIBRUBYARG_SHARED)" + CONFIG["LIBRUBYARG_STATIC"] = "$(LIBRUBY_A)" + CONFIG["LIBRUBYARG_SHARED"] = "$(LIBRUBY)" + CONFIG["SOLIBS"] = "" + CONFIG["DLDLIBS"] = "" + CONFIG["ENABLE_SHARED"] = "yes" + CONFIG["OUTFLAG"] = "-Fe" + CONFIG["CPPOUTFILE"] = "-P" + CONFIG["LIBPATHFLAG"] = " -libpath:\"%s\"" + CONFIG["RPATHFLAG"] = "" + CONFIG["LIBARG"] = "%s.lib" + CONFIG["LINK_SO"] = "$(LDSHARED) -Fe$(@) $(OBJS) $(LIBS) $(LOCAL_LIBS) $(DLDFLAGS)" + CONFIG["COMPILE_C"] = "$(CC) $(INCFLAGS) $(CFLAGS) $(CPPFLAGS) -c -Tc$(<:\\=/)" + CONFIG["COMPILE_CXX"] = "$(CXX) $(INCFLAGS) $(CXXFLAGS) $(CPPFLAGS) -c -Tp$(<:\\=/)" + CONFIG["COMPILE_RULES"] = "{$(srcdir)}.%s{}.%s: {$(topdir)}.%s{}.%s: {$(hdrdir)}.%s{}.%s: .%s.%s:" + CONFIG["RULE_SUBST"] = "{.;$(srcdir);$(topdir);$(hdrdir)}%s" + CONFIG["TRY_LINK"] = "$(CC) -Feconftest $(INCFLAGS) -I$(hdrdir) $(CPPFLAGS) $(CFLAGS) $(src) $(LOCAL_LIBS) $(LIBS) -link $(LDFLAGS) $(LIBPATH) $(XLDFLAGS)" + CONFIG["COMMON_LIBS"] = "m" + CONFIG["COMMON_MACROS"] = "WIN32_LEAN_AND_MEAN" + CONFIG["COMMON_HEADERS"] = "winsock2.h windows.h" + CONFIG["DISTCLEANFILES"] = "vc*.pdb" + CONFIG["EXPORT_PREFIX"] = " " + CONFIG["arch"] = "i386-mswin32" + CONFIG["sitearch"] = "i386-msvcrt" + CONFIG["sitedir"] = "$(prefix)/lib/ruby/site_ruby" + CONFIG["configure_args"] = "--with-make-prog=nmake --enable-shared --with-winsock2" + CONFIG["ruby_version"] = "$(MAJOR).$(MINOR)" + CONFIG["rubylibdir"] = "$(libdir)/ruby/$(ruby_version)" + CONFIG["archdir"] = "$(rubylibdir)/$(arch)" + CONFIG["sitelibdir"] = "$(sitedir)/$(ruby_version)" + CONFIG["sitearchdir"] = "$(sitelibdir)/$(sitearch)" + CONFIG["topdir"] = File.dirname(__FILE__) + MAKEFILE_CONFIG = {} + CONFIG.each{|k,v| MAKEFILE_CONFIG[k] = v.dup} + def Config::expand(val, config = CONFIG) + val.gsub!(/\$\$|\$\(([^()]+)\)|\$\{([^{}]+)\}/) do |var| + if !(v = $1 || $2) + '$' + elsif key = config[v = v[/\A[^:]+(?=(?::(.*?)=(.*))?\z)/]] + pat, sub = $1, $2 + config[v] = false + Config::expand(key, config) + config[v] = key + key = key.gsub(/#{Regexp.quote(pat)}(?=\s|\z)/n) {sub} if pat + key + else + var + end + end + val + end + CONFIG.each_value do |val| + Config::expand(val) + end +end +RbConfig = Config # compatibility for ruby-1.9 +CROSS_COMPILING = nil unless defined? CROSS_COMPILING diff --git a/merlin/main/languages/ruby/Libs/socket.rb b/merlin/main/languages/ruby/Libs/socket.rb new file mode 100644 index 0000000000..225a189ae6 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/socket.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.Sockets' diff --git a/merlin/main/languages/ruby/Libs/stringio.rb b/merlin/main/languages/ruby/Libs/stringio.rb new file mode 100644 index 0000000000..3ec05c0d38 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/stringio.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.StringIO' diff --git a/merlin/main/languages/ruby/Libs/strscan.rb b/merlin/main/languages/ruby/Libs/strscan.rb new file mode 100644 index 0000000000..fbed87e0b0 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/strscan.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.StringScanner' diff --git a/merlin/main/languages/ruby/Libs/system.rb b/merlin/main/languages/ruby/Libs/system.rb new file mode 100644 index 0000000000..a34eca5eb7 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/system.rb @@ -0,0 +1 @@ +require 'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' diff --git a/merlin/main/languages/ruby/Libs/test/unit.rb b/merlin/main/languages/ruby/Libs/test/unit.rb new file mode 100644 index 0000000000..e1b43ea3b3 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/test/unit.rb @@ -0,0 +1,281 @@ +require 'hacks' +require 'test/unit/testcase' +require 'test/unit/autorunner' + +module Test # :nodoc: + # + # = Test::Unit - Ruby Unit Testing Framework + # + # == Introduction + # + # Unit testing is making waves all over the place, largely due to the + # fact that it is a core practice of XP. While XP is great, unit testing + # has been around for a long time and has always been a good idea. One + # of the keys to good unit testing, though, is not just writing tests, + # but having tests. What's the difference? Well, if you just _write_ a + # test and throw it away, you have no guarantee that something won't + # change later which breaks your code. If, on the other hand, you _have_ + # tests (obviously you have to write them first), and run them as often + # as possible, you slowly build up a wall of things that cannot break + # without you immediately knowing about it. This is when unit testing + # hits its peak usefulness. + # + # Enter Test::Unit, a framework for unit testing in Ruby, helping you to + # design, debug and evaluate your code by making it easy to write and + # have tests for it. + # + # + # == Notes + # + # Test::Unit has grown out of and superceded Lapidary. + # + # + # == Feedback + # + # I like (and do my best to practice) XP, so I value early releases, + # user feedback, and clean, simple, expressive code. There is always + # room for improvement in everything I do, and Test::Unit is no + # exception. Please, let me know what you think of Test::Unit as it + # stands, and what you'd like to see expanded/changed/improved/etc. If + # you find a bug, let me know ASAP; one good way to let me know what the + # bug is is to submit a new test that catches it :-) Also, I'd love to + # hear about any successes you have with Test::Unit, and any + # documentation you might add will be greatly appreciated. My contact + # info is below. + # + # + # == Contact Information + # + # A lot of discussion happens about Ruby in general on the ruby-talk + # mailing list (http://www.ruby-lang.org/en/ml.html), and you can ask + # any questions you might have there. I monitor the list, as do many + # other helpful Rubyists, and you're sure to get a quick answer. Of + # course, you're also welcome to email me (Nathaniel Talbott) directly + # at mailto:testunit@talbott.ws, and I'll do my best to help you out. + # + # + # == Credits + # + # I'd like to thank... + # + # Matz, for a great language! + # + # Masaki Suketa, for his work on RubyUnit, which filled a vital need in + # the Ruby world for a very long time. I'm also grateful for his help in + # polishing Test::Unit and getting the RubyUnit compatibility layer + # right. His graciousness in allowing Test::Unit to supercede RubyUnit + # continues to be a challenge to me to be more willing to defer my own + # rights. + # + # Ken McKinlay, for his interest and work on unit testing, and for his + # willingness to dialog about it. He was also a great help in pointing + # out some of the holes in the RubyUnit compatibility layer. + # + # Dave Thomas, for the original idea that led to the extremely simple + # "require 'test/unit'", plus his code to improve it even more by + # allowing the selection of tests from the command-line. Also, without + # RDoc, the documentation for Test::Unit would stink a lot more than it + # does now. + # + # Everyone who's helped out with bug reports, feature ideas, + # encouragement to continue, etc. It's a real privilege to be a part of + # the Ruby community. + # + # The guys at RoleModel Software, for putting up with me repeating, "But + # this would be so much easier in Ruby!" whenever we're coding in Java. + # + # My Creator, for giving me life, and giving it more abundantly. + # + # + # == License + # + # Test::Unit is copyright (c) 2000-2003 Nathaniel Talbott. It is free + # software, and is distributed under the Ruby license. See the COPYING + # file in the standard Ruby distribution for details. + # + # + # == Warranty + # + # This software is provided "as is" and without any express or + # implied warranties, including, without limitation, the implied + # warranties of merchantibility and fitness for a particular + # purpose. + # + # + # == Author + # + # Nathaniel Talbott. + # Copyright (c) 2000-2003, Nathaniel Talbott + # + # ---- + # + # = Usage + # + # The general idea behind unit testing is that you write a _test_ + # _method_ that makes certain _assertions_ about your code, working + # against a _test_ _fixture_. A bunch of these _test_ _methods_ are + # bundled up into a _test_ _suite_ and can be run any time the + # developer wants. The results of a run are gathered in a _test_ + # _result_ and displayed to the user through some UI. So, lets break + # this down and see how Test::Unit provides each of these necessary + # pieces. + # + # + # == Assertions + # + # These are the heart of the framework. Think of an assertion as a + # statement of expected outcome, i.e. "I assert that x should be equal + # to y". If, when the assertion is executed, it turns out to be + # correct, nothing happens, and life is good. If, on the other hand, + # your assertion turns out to be false, an error is propagated with + # pertinent information so that you can go back and make your + # assertion succeed, and, once again, life is good. For an explanation + # of the current assertions, see Test::Unit::Assertions. + # + # + # == Test Method & Test Fixture + # + # Obviously, these assertions have to be called within a context that + # knows about them and can do something meaningful with their + # pass/fail value. Also, it's handy to collect a bunch of related + # tests, each test represented by a method, into a common test class + # that knows how to run them. The tests will be in a separate class + # from the code they're testing for a couple of reasons. First of all, + # it allows your code to stay uncluttered with test code, making it + # easier to maintain. Second, it allows the tests to be stripped out + # for deployment, since they're really there for you, the developer, + # and your users don't need them. Third, and most importantly, it + # allows you to set up a common test fixture for your tests to run + # against. + # + # What's a test fixture? Well, tests do not live in a vacuum; rather, + # they're run against the code they are testing. Often, a collection + # of tests will run against a common set of data, also called a + # fixture. If they're all bundled into the same test class, they can + # all share the setting up and tearing down of that data, eliminating + # unnecessary duplication and making it much easier to add related + # tests. + # + # Test::Unit::TestCase wraps up a collection of test methods together + # and allows you to easily set up and tear down the same test fixture + # for each test. This is done by overriding #setup and/or #teardown, + # which will be called before and after each test method that is + # run. The TestCase also knows how to collect the results of your + # assertions into a Test::Unit::TestResult, which can then be reported + # back to you... but I'm getting ahead of myself. To write a test, + # follow these steps: + # + # * Make sure Test::Unit is in your library path. + # * require 'test/unit' in your test script. + # * Create a class that subclasses Test::Unit::TestCase. + # * Add a method that begins with "test" to your class. + # * Make assertions in your test method. + # * Optionally define #setup and/or #teardown to set up and/or tear + # down your common test fixture. + # * You can now run your test as you would any other Ruby + # script... try it and see! + # + # A really simple test might look like this (#setup and #teardown are + # commented out to indicate that they are completely optional): + # + # require 'test/unit' + # + # class TC_MyTest < Test::Unit::TestCase + # # def setup + # # end + # + # # def teardown + # # end + # + # def test_fail + # assert(false, 'Assertion was false.') + # end + # end + # + # + # == Test Runners + # + # So, now you have this great test class, but you still need a way to + # run it and view any failures that occur during the run. This is + # where Test::Unit::UI::Console::TestRunner (and others, such as + # Test::Unit::UI::GTK::TestRunner) comes into play. The console test + # runner is automatically invoked for you if you require 'test/unit' + # and simply run the file. To use another runner, or to manually + # invoke a runner, simply call its run class method and pass in an + # object that responds to the suite message with a + # Test::Unit::TestSuite. This can be as simple as passing in your + # TestCase class (which has a class suite method). It might look + # something like this: + # + # require 'test/unit/ui/console/testrunner' + # Test::Unit::UI::Console::TestRunner.run(TC_MyTest) + # + # + # == Test Suite + # + # As more and more unit tests accumulate for a given project, it + # becomes a real drag running them one at a time, and it also + # introduces the potential to overlook a failing test because you + # forget to run it. Suddenly it becomes very handy that the + # TestRunners can take any object that returns a Test::Unit::TestSuite + # in response to a suite method. The TestSuite can, in turn, contain + # other TestSuites or individual tests (typically created by a + # TestCase). In other words, you can easily wrap up a group of + # TestCases and TestSuites like this: + # + # require 'test/unit/testsuite' + # require 'tc_myfirsttests' + # require 'tc_moretestsbyme' + # require 'ts_anothersetoftests' + # + # class TS_MyTests + # def self.suite + # suite = Test::Unit::TestSuite.new + # suite << TC_MyFirstTests.suite + # suite << TC_MoreTestsByMe.suite + # suite << TS_AnotherSetOfTests.suite + # return suite + # end + # end + # Test::Unit::UI::Console::TestRunner.run(TS_MyTests) + # + # Now, this is a bit cumbersome, so Test::Unit does a little bit more + # for you, by wrapping these up automatically when you require + # 'test/unit'. What does this mean? It means you could write the above + # test case like this instead: + # + # require 'test/unit' + # require 'tc_myfirsttests' + # require 'tc_moretestsbyme' + # require 'ts_anothersetoftests' + # + # Test::Unit is smart enough to find all the test cases existing in + # the ObjectSpace and wrap them up into a suite for you. It then runs + # the dynamic suite using the console TestRunner. + # + # + # == Questions? + # + # I'd really like to get feedback from all levels of Ruby + # practitioners about typos, grammatical errors, unclear statements, + # missing points, etc., in this document (or any other). + # + + module Unit + # If set to false Test::Unit will not automatically run at exit. + def self.run=(flag) + @run = flag + end + + # Automatically run tests at exit? + def self.run? + @run ||= false + end + end +end + +at_exit do + unless $! || Test::Unit.run? + exit Test::Unit::AutoRunner.run + end +end diff --git a/merlin/main/languages/ruby/Libs/test/unit/autorunner.rb b/merlin/main/languages/ruby/Libs/test/unit/autorunner.rb new file mode 100644 index 0000000000..18023d5518 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/test/unit/autorunner.rb @@ -0,0 +1,217 @@ +require 'test/unit' +require 'test/unit/ui/testrunnerutilities' +require 'optparse' + +module Test + module Unit + class AutoRunner + def self.run(force_standalone=false, default_dir=nil, argv=ARGV, &block) + r = new(force_standalone || standalone?, &block) + r.base = default_dir + r.process_args(argv) + r.run + end + + def self.standalone? + return false unless("-e" == $0) + return TestCase.subclasses.length == 0 + end + + RUNNERS = { + :console => proc do |r| + require 'test/unit/ui/console/testrunner' + Test::Unit::UI::Console::TestRunner + end, + :gtk => proc do |r| + require 'test/unit/ui/gtk/testrunner' + Test::Unit::UI::GTK::TestRunner + end, + :gtk2 => proc do |r| + require 'test/unit/ui/gtk2/testrunner' + Test::Unit::UI::GTK2::TestRunner + end, + :fox => proc do |r| + require 'test/unit/ui/fox/testrunner' + Test::Unit::UI::Fox::TestRunner + end, + :tk => proc do |r| + require 'test/unit/ui/tk/testrunner' + Test::Unit::UI::Tk::TestRunner + end, + } + + OUTPUT_LEVELS = [ + [:silent, UI::SILENT], + [:progress, UI::PROGRESS_ONLY], + [:normal, UI::NORMAL], + [:verbose, UI::VERBOSE], + ] + + COLLECTORS = { + :objectspace => proc do |r| + require 'test/unit/collector/objectspace' + c = Collector::ObjectSpace.new(TestCase.subclasses) + c.filter = r.filters + c.collect($0.sub(/\.rb\Z/, '')) + end, + :dir => proc do |r| + require 'test/unit/collector/dir' + c = Collector::Dir.new + c.filter = r.filters + c.pattern.concat(r.pattern) if(r.pattern) + c.exclude.concat(r.exclude) if(r.exclude) + c.base = r.base + $:.push(r.base) if r.base + c.collect(*(r.to_run.empty? ? ['.'] : r.to_run)) + end, + } + + attr_reader :suite + attr_accessor :output_level, :filters, :to_run, :pattern, :exclude, :base, :workdir + attr_writer :runner, :collector + + def initialize(standalone) + Unit.run = true + @standalone = standalone + @runner = RUNNERS[:console] + @collector = COLLECTORS[(standalone ? :dir : :objectspace)] + @filters = [] + @to_run = [] + @output_level = UI::NORMAL + @workdir = nil + yield(self) if(block_given?) + end + + def process_args(args = ARGV) + begin + options.order!(args) {|arg| @to_run << arg} + rescue OptionParser::ParseError => e + puts e + puts options + $! = nil + abort + else + @filters << proc{false} unless(@filters.empty?) + end + not @to_run.empty? + end + + def options + @options ||= OptionParser.new do |o| + o.banner = "Test::Unit automatic runner." + o.banner << "\nUsage: #{$0} [options] [-- untouched arguments]" + + o.on + o.on('-r', '--runner=RUNNER', RUNNERS, + "Use the given RUNNER.", + "(" + keyword_display(RUNNERS) + ")") do |r| + @runner = r + end + + if(@standalone) + o.on('-b', '--basedir=DIR', "Base directory of test suites.") do |b| + @base = b + end + + o.on('-w', '--workdir=DIR', "Working directory to run tests.") do |w| + @workdir = w + end + + o.on('-a', '--add=TORUN', Array, + "Add TORUN to the list of things to run;", + "can be a file or a directory.") do |a| + @to_run.concat(a) + end + + @pattern = [] + o.on('-p', '--pattern=PATTERN', Regexp, + "Match files to collect against PATTERN.") do |e| + @pattern << e + end + + @exclude = [] + o.on('-x', '--exclude=PATTERN', Regexp, + "Ignore files to collect against PATTERN.") do |e| + @exclude << e + end + end + + o.on('-n', '--name=NAME', String, + "Runs tests matching NAME.", + "(patterns may be used).") do |n| + n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n) + case n + when Regexp + @filters << proc{|t| n =~ t.method_name ? true : nil} + else + @filters << proc{|t| n == t.method_name ? true : nil} + end + end + + o.on('-t', '--testcase=TESTCASE', String, + "Runs tests in TestCases matching TESTCASE.", + "(patterns may be used).") do |n| + n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n) + case n + when Regexp + @filters << proc{|t| n =~ t.class.name ? true : nil} + else + @filters << proc{|t| n == t.class.name ? true : nil} + end + end + + o.on('-I', "--load-path=DIR[#{File::PATH_SEPARATOR}DIR...]", + "Appends directory list to $LOAD_PATH.") do |dirs| + $LOAD_PATH.concat(dirs.split(File::PATH_SEPARATOR)) + end + + o.on('-v', '--verbose=[LEVEL]', OUTPUT_LEVELS, + "Set the output level (default is verbose).", + "(" + keyword_display(OUTPUT_LEVELS) + ")") do |l| + @output_level = l || UI::VERBOSE + end + + o.on('--', + "Stop processing options so that the", + "remaining options will be passed to the", + "test."){o.terminate} + + o.on('-h', '--help', 'Display this help.'){puts o; exit} + + o.on_tail + o.on_tail('Deprecated options:') + + o.on_tail('--console', 'Console runner (use --runner).') do + warn("Deprecated option (--console).") + @runner = RUNNERS[:console] + end + + o.on_tail('--gtk', 'GTK runner (use --runner).') do + warn("Deprecated option (--gtk).") + @runner = RUNNERS[:gtk] + end + + o.on_tail('--fox', 'Fox runner (use --runner).') do + warn("Deprecated option (--fox).") + @runner = RUNNERS[:fox] + end + + o.on_tail + end + end + + def keyword_display(array) + list = array.collect {|e, *| e.to_s} + Array === array or list.sort! + list.collect {|e| e.sub(/^(.)([A-Za-z]+)(?=\w*$)/, '\\1[\\2]')}.join(", ") + end + + def run + @suite = @collector[self] + result = @runner[self] or return false + Dir.chdir(@workdir) if @workdir + result.run(@suite, @output_level).passed? + end + end + end +end diff --git a/merlin/main/languages/ruby/Libs/test/unit/testcase.rb b/merlin/main/languages/ruby/Libs/test/unit/testcase.rb new file mode 100644 index 0000000000..7d737bca0f --- /dev/null +++ b/merlin/main/languages/ruby/Libs/test/unit/testcase.rb @@ -0,0 +1,161 @@ +#-- +# +# Author:: Nathaniel Talbott. +# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved. +# License:: Ruby license. + +require 'test/unit/assertions' +require 'test/unit/failure' +require 'test/unit/error' +require 'test/unit/testsuite' +require 'test/unit/assertionfailederror' +require 'test/unit/util/backtracefilter' + +module Test + module Unit + + # Ties everything together. If you subclass and add your own + # test methods, it takes care of making them into tests and + # wrapping those tests into a suite. It also does the + # nitty-gritty of actually running an individual test and + # collecting its results into a Test::Unit::TestResult object. + class TestCase + include Assertions + include Util::BacktraceFilter + + attr_reader :method_name + + STARTED = name + "::STARTED" + FINISHED = name + "::FINISHED" + + ## + # These exceptions are not caught by #run. + + PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, Interrupt, + SystemExit] + + # Creates a new instance of the fixture for running the + # test represented by test_method_name. + def initialize(test_method_name) + unless(respond_to?(test_method_name) and + (method(test_method_name).arity == 0 || + method(test_method_name).arity == -1)) + throw :invalid_test + end + @method_name = test_method_name + @test_passed = true + end + + # Rolls up all of the test* methods in the fixture into + # one suite, creating a new instance of the fixture for + # each method. + def self.suite + method_names = public_instance_methods(true) + tests = method_names.delete_if {|method_name| method_name !~ /^test./} + suite = TestSuite.new(name) + tests.sort.each do + |test| + catch(:invalid_test) do + suite << new(test) + end + end + if (suite.empty?) + catch(:invalid_test) do + suite << new("default_test") + end + end + return suite + end + + # Runs the individual test method represented by this + # instance of the fixture, collecting statistics, failures + # and errors in result. + def run(result) + yield(STARTED, name) + @_result = result + begin + setup + __send__(@method_name) + rescue AssertionFailedError => e + add_failure(e.message, e.backtrace) + rescue Exception + raise if PASSTHROUGH_EXCEPTIONS.include? $!.class + add_error($!) + ensure + begin + teardown + rescue AssertionFailedError => e + add_failure(e.message, e.backtrace) + rescue Exception + raise if PASSTHROUGH_EXCEPTIONS.include? $!.class + add_error($!) + end + end + result.add_run + yield(FINISHED, name) + end + + # Called before every test method runs. Can be used + # to set up fixture information. + def setup + end + + # Called after every test method runs. Can be used to tear + # down fixture information. + def teardown + end + + def default_test + flunk("No tests were specified") + end + + # Returns whether this individual test passed or + # not. Primarily for use in teardown so that artifacts + # can be left behind if the test fails. + def passed? + return @test_passed + end + private :passed? + + def size + 1 + end + + def add_assertion + @_result.add_assertion + end + private :add_assertion + + def add_failure(message, all_locations=caller()) + @test_passed = false + @_result.add_failure(Failure.new(name, filter_backtrace(all_locations), message)) + end + private :add_failure + + def add_error(exception) + @test_passed = false + @_result.add_error(Error.new(name, exception)) + end + private :add_error + + # Returns a human-readable name for the specific test that + # this instance of TestCase represents. + def name + "#{@method_name}(#{self.class.name})" + end + + # Overridden to return #name. + def to_s + name + end + + # It's handy to be able to compare TestCase instances. + def ==(other) + return false unless(other.kind_of?(self.class)) + return false unless(@method_name == other.method_name) + self.class == other.class + end + end + TestCase.extend ::SubclassTracking + end +end diff --git a/merlin/main/languages/ruby/Libs/thread.rb b/merlin/main/languages/ruby/Libs/thread.rb new file mode 100644 index 0000000000..2d2cf25bd8 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/thread.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.Threading' diff --git a/merlin/main/languages/ruby/Libs/tmpdir.rb b/merlin/main/languages/ruby/Libs/tmpdir.rb new file mode 100644 index 0000000000..43ce53ecf7 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/tmpdir.rb @@ -0,0 +1,38 @@ +# +# tmpdir - retrieve temporary directory path +# +# $Id: tmpdir.rb 11708 2007-02-12 23:01:19Z shyouhei $ +# + +require 'mscorlib' + +class Dir + + begin + @@systmpdir = System::IO::Path.get_temp_path + rescue + @@systmpdir = nil + end + + private + + ## + # Returns the operating system's temporary file path. + + def Dir::tmpdir + tmp = '.' + if $SAFE > 0 and @@systmpdir + tmp = @@systmpdir + else + for dir in [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], + ENV['USERPROFILE'], @@systmpdir, '/tmp'] + if dir and File.directory?(dir) and File.writable?(dir) + tmp = dir + break + end + end + end + File.expand_path(tmp) + end +end + diff --git a/merlin/main/languages/ruby/Libs/yaml.rb b/merlin/main/languages/ruby/Libs/yaml.rb new file mode 100644 index 0000000000..7d92ce16e2 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/yaml.rb @@ -0,0 +1,17 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require 'date' +load_assembly 'IronRuby.Libraries.Yaml', 'IronRuby.StandardLibrary.Yaml' diff --git a/merlin/main/languages/ruby/Libs/zlib.rb b/merlin/main/languages/ruby/Libs/zlib.rb new file mode 100644 index 0000000000..e4c84bc290 --- /dev/null +++ b/merlin/main/languages/ruby/Libs/zlib.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +load_assembly 'IronRuby.Libraries', 'IronRuby.StandardLibrary.Zlib' diff --git a/merlin/main/languages/ruby/License.html b/merlin/main/languages/ruby/License.html new file mode 100644 index 0000000000..60af8a9f0f --- /dev/null +++ b/merlin/main/languages/ruby/License.html @@ -0,0 +1,39 @@ + + + +Microsoft Public License (Ms-PL) + + + + +

    This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.

    + +

    1. Definitions

    +

    The terms “reproduce,” “reproduction,” “derivative works,” and “distribution” have the same meaning here as under U.S. copyright law.

    +

    A “contribution” is the original software, or any additions or changes to the software.

    +

    A “contributor” is any person that distributes its contribution under this license.

    +

    “Licensed patents” are a contributor’s patent claims that read directly on its contribution.

    +

    2. Grant of Rights

    +

    (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright +license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.

    + +

    (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, +royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative +works of the contribution in the software.

    + +

    3. Conditions and Limitations

    (A) No Trademark License- This license does not grant you rights to use any contributors’ name, logo, or trademarks.

    +

    (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.

    +

    (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.

    +

    (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. +If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.

    + +

    (E) The software is licensed “as-is.” You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional +consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, +fitness for a particular purpose and non-infringement.

    + +

    See FAQ.html for answers to frequently asked questions +about this license.

    + + + + diff --git a/merlin/main/languages/ruby/Licenses/CPL.txt b/merlin/main/languages/ruby/Licenses/CPL.txt new file mode 100644 index 0000000000..a8e6c050cc --- /dev/null +++ b/merlin/main/languages/ruby/Licenses/CPL.txt @@ -0,0 +1,86 @@ +Common Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and + b) in the case of each subsequent Contributor: + + i) changes to the Program, and + + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. + + b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. + + c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. + + d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + + b) its license agreement: + + i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; + + ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; + + iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and + + iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + + b) a copy of this Agreement must be included with each copy of the Program. + +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. diff --git a/merlin/main/languages/ruby/Licenses/MSPL.html b/merlin/main/languages/ruby/Licenses/MSPL.html new file mode 100644 index 0000000000..60af8a9f0f --- /dev/null +++ b/merlin/main/languages/ruby/Licenses/MSPL.html @@ -0,0 +1,39 @@ + + + +Microsoft Public License (Ms-PL) + + + + +

    This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.

    + +

    1. Definitions

    +

    The terms “reproduce,” “reproduction,” “derivative works,” and “distribution” have the same meaning here as under U.S. copyright law.

    +

    A “contribution” is the original software, or any additions or changes to the software.

    +

    A “contributor” is any person that distributes its contribution under this license.

    +

    “Licensed patents” are a contributor’s patent claims that read directly on its contribution.

    +

    2. Grant of Rights

    +

    (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright +license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.

    + +

    (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, +royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative +works of the contribution in the software.

    + +

    3. Conditions and Limitations

    (A) No Trademark License- This license does not grant you rights to use any contributors’ name, logo, or trademarks.

    +

    (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.

    +

    (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.

    +

    (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. +If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.

    + +

    (E) The software is licensed “as-is.” You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional +consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, +fitness for a particular purpose and non-infringement.

    + +

    See FAQ.html for answers to frequently asked questions +about this license.

    + + + + diff --git a/merlin/main/languages/ruby/Rakefile b/merlin/main/languages/ruby/Rakefile new file mode 100644 index 0000000000..4666392707 --- /dev/null +++ b/merlin/main/languages/ruby/Rakefile @@ -0,0 +1,782 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +$rakefile_path = File.dirname(__FILE__) +# Alias HOME to USERPROFILE under Windows if HOME is not defined +ENV['HOME'] ||= ENV['USERPROFILE'] + +# IronRuby rakefile for build, test, and repository integration v2 + +require 'rubygems' +gem 'pathname2' + +require 'pathname2' +require 'tmpdir' +require 'rexml/document' +include REXML + +verbose(false) # default to non-verbose output + +# Paths to configure + +IRONRUBY_COMPILER = 'ir.exe' +CS_COMPILER = ENV['mono'].nil? ? 'csc' : 'gmcs' +EXCLUDED_EXTENSIONS = ['.old', '.suo', '.vspscc', '.vssscc', '.user', '.log', '.pdb', '.cache', '.swp'] +EXCLUDED_DIRECTORIES = ['.svn', 'obj', '.', '..'] +PACKAGE_DIR = 'c:\ironruby' # directory that binary package is created in + +require 'context' + +# Add some utility methods to Pathname to support filtered lists + +class Pathname + def filtered_subdirs(extras = []) + return [] unless exist? + filtered_subdirs = subdirs.find_all { |dir| (dir.to_a & (EXCLUDED_DIRECTORIES + extras)) == [] } + result = filtered_subdirs.map { |dir| self + dir } + result.insert(0, self) + end + + def subdirs + glob("**/*", File::FNM_DOTMATCH).find_all { |path| (self + path).directory? } + end + + def files + raise "Cannot call files on a filename path: #{self}" if !directory? + entries.find_all { |e| !(self + e).directory? }.sort + end + + def filtered_files + raise "Cannot call filtered_files on a filename path: #{self}" if !directory? + files.find_all { |f| !EXCLUDED_EXTENSIONS.include?((self + f).extname) }.map { |f| f.downcase } + end +end + +def transform_dlr_project(csproj_filename) + transform_project(:dlr_core, csproj_filename) do |contents| + if IronRuby.is_merlin? + replace_output_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Debug\\', '..\..\build\debug\\' + replace_output_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Release\\', '..\..\build\release\\' + replace_output_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\\', '..\..\build\silverlight debug\\' + replace_output_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\\', '..\..\build\silverlight release\\' + replace_import_project contents, '..\..\..\..\..\..\Merlin\Main\SpecSharp.targets', '..\..\SpecSharp.targets' + replace_doc_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Debug\Microsoft.Scripting.Core.xml', '..\..\build\debug\Microsoft.Scripting.Core.xml' + replace_doc_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Release\Microsoft.Scripting.Core.xml', '..\..\build\release\Microsoft.Scripting.Core.xml' + replace_doc_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\Microsoft.Scripting.Core.xml', '..\..\build\silverlight debug\Microsoft.Scripting.Core.xml' + replace_doc_path contents, '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\Microsoft.Scripting.Core.xml', '..\..\build\silverlight release\Microsoft.Scripting.Core.xml' + + contents.change_configuration! 'FxCop|AnyCPU', 'TRACE;MICROSOFT_SCRIPTING_CORE' + contents.change_configuration! 'SpecSharp|AnyCPU', 'TRACE;DEBUG;MICROSOFT_SCRIPTING_CORE' + contents.change_configuration! 'Debug|AnyCPU', 'TRACE;DEBUG;MICROSOFT_SCRIPTING_CORE' + contents.change_configuration! 'Release|AnyCPU', 'TRACE;MICROSOFT_SCRIPTING_CORE' + contents.change_configuration! 'Silverlight Debug|AnyCPU', 'TRACE;DEBUG;SILVERLIGHT;MICROSOFT_SCRIPTING_CORE' + contents.change_configuration! 'Silverlight Release|AnyCPU', 'TRACE;SILVERLIGHT;MICROSOFT_SCRIPTING_CORE' + else + replace_output_path contents, '..\..\build\debug\\', '..\..\..\..\..\..\Merlin\Main\Bin\Debug\\' + replace_output_path contents, '..\..\build\release\\', '..\..\..\..\..\..\Merlin\Main\Bin\Release\\' + replace_output_path contents, '..\..\build\silverlight debug\\', '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\\' + replace_output_path contents, '..\..\build\silverlight release\\', '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\\' + replace_import_project contents, '..\..\SpecSharp.targets', '..\..\..\..\..\..\Merlin\Main\SpecSharp.targets' + replace_doc_path contents, '..\..\build\debug\microsoft.scripting.core.xml', '..\..\..\..\..\..\Merlin\Main\Bin\Debug\Microsoft.Scripting.Core.xml' + replace_doc_path contents, '..\..\build\release\microsoft.scripting.core.xml', '..\..\..\..\..\..\Merlin\Main\Bin\Release\Microsoft.Scripting.Core.xml' + replace_doc_path contents, '..\..\build\silverlight debug\microsoft.scripting.core.xml', '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\Microsoft.Scripting.Core.xml' + replace_doc_path contents, '..\..\build\silverlight release\microsoft.scripting.core.xml', '..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\Microsoft.Scripting.Core.xml' + + contents.change_configuration! 'FxCop|AnyCPU', 'TRACE;MICROSOFT_SCRIPTING_CORE;SIGNED' + contents.change_configuration! 'SpecSharp|AnyCPU', 'TRACE;DEBUG;MICROSOFT_SCRIPTING_CORE;SIGNED' + contents.change_configuration! 'Debug|AnyCPU', 'TRACE;DEBUG;MICROSOFT_SCRIPTING_CORE;SIGNED' + contents.change_configuration! 'Release|AnyCPU', 'TRACE;MICROSOFT_SCRIPTING_CORE;SIGNED' + contents.change_configuration! 'Silverlight Debug|AnyCPU', 'TRACE;DEBUG;SILVERLIGHT;MICROSOFT_SCRIPTING_CORE;SIGNED' + contents.change_configuration! 'Silverlight Release|AnyCPU', 'TRACE;SILVERLIGHT;MICROSOFT_SCRIPTING_CORE;SIGNED' + end + end +end + +# Source repository synchronization tasks + +def push + IronRuby.source_context do + rake_output_message "#{'=' * 78}\nTransforming source to target layout\n\n" + + # Copy to temporary directory and transform layout to match target layout + # This lets us diff the temp target layout with the actual layout + + temp_dir = generate_temp_dir + nodes = [:root, :gppg, :dlr_core, :dlr_libs, :dlr_com, :ironruby, :libraries, :tests, :console, :generator, :test_runner, :scanner, :yaml, :stdlibs, :ironlibs] + nodes.each do |node| + # special case tests directory to avoid filtering sub-directories + if node == :tests + copy_to_temp_dir node, temp_dir + else + copy_to_temp_dir node, temp_dir, ['bin'] # always filter out bin subdirectories except in tests + end + end + + # Do some post-transform filtering of files + + if IronRuby.is_merlin? + del temp_dir, 'Ruby.sln' + else + puts "copying #{IronRuby.source + 'IronRuby.sln'} to #{temp_dir + 'Merlin/Main/languages/ruby/IronRuby.sln'}" + copy IronRuby.source + 'IronRuby.sln', temp_dir + 'Merlin/Main/languages/ruby/Ruby.sln' + end + + # Special-cased one-way copy of app.config to external layout + + if IronRuby.is_merlin? + transform_config_file 'Svn', get_source_dir(:lang_root) + 'app.config', temp_dir + 'app.config' + end + + # Diff and push temp directory files to the target + + push_to_target temp_dir + + # Transform the project files + + transform_project :ironruby, 'ruby.csproj' + transform_project :libraries, 'ironruby.libraries.csproj' + transform_dlr_project 'microsoft.scripting.core.csproj' + transform_dlr_project 'microsoft.scripting.extensionattribute.csproj' + transform_project(:dlr_libs, 'microsoft.scripting.csproj') do |contents| + if IronRuby.is_merlin? + replace_output_path contents, '..\..\Bin\Debug\\', '..\..\build\debug\\' + replace_output_path contents, '..\..\Bin\Release\\', '..\..\build\release\\' + replace_output_path contents, '..\..\Bin\Silverlight Debug\\', '..\..\build\silverlight debug\\' + replace_output_path contents, '..\..\Bin\Silverlight Release\\', '..\..\build\silverlight release\\' + replace_doc_path contents, '..\..\Bin\Debug\Microsoft.Scripting.xml', '..\..\build\debug\Microsoft.Scripting.xml' + replace_doc_path contents, '..\..\Bin\Release\Microsoft.Scripting.xml', '..\..\build\release\Microsoft.Scripting.xml' + replace_doc_path contents, '..\..\Bin\Silverlight Debug\Microsoft.Scripting.xml', '..\..\build\silverlight debug\Microsoft.Scripting.xml' + replace_doc_path contents, '..\..\Bin\Silverlight Release\Microsoft.Scripting.xml', '..\..\build\silverlight release\Microsoft.Scripting.xml' + + contents.change_configuration! 'FxCop|AnyCPU', 'TRACE' + contents.change_configuration! 'SpecSharp|AnyCPU', 'TRACE;DEBUG' + contents.change_configuration! 'Debug|AnyCPU', 'TRACE;DEBUG' + contents.change_configuration! 'Release|AnyCPU', 'TRACE' + contents.change_configuration! 'Silverlight Debug|AnyCPU', 'TRACE;DEBUG;SILVERLIGHT' + contents.change_configuration! 'Silverlight Release|AnyCPU', 'TRACE;SILVERLIGHT' + else + replace_output_path contents, '..\..\build\debug\\', '..\..\Bin\Debug\\' + replace_output_path contents, '..\..\build\release\\', '..\..\Bin\Release\\' + replace_output_path contents, '..\..\build\silverlight debug\\', '..\..\Bin\Silverlight Debug\\' + replace_output_path contents, '..\..\build\silverlight release\\', '..\..\Bin\Silverlight Release\\' + replace_doc_path contents, '..\..\build\debug\Microsoft.Scripting.xml', '..\..\Bin\Debug\Microsoft.Scripting.xml' + replace_doc_path contents, '..\..\build\release\Microsoft.Scripting.xml', '..\..\Bin\Release\Microsoft.Scripting.xml' + replace_doc_path contents, '..\..\build\silverlight debug\Microsoft.Scripting.xml', '..\..\Bin\Silverlight Debug\Microsoft.Scripting.xml' + replace_doc_path contents, '..\..\build\silverlight release\Microsoft.Scripting.xml', '..\..\Bin\Silverlight Release\Microsoft.Scripting.xml' + + contents.change_configuration! 'FxCop|AnyCPU', 'TRACE;SIGNED' + contents.change_configuration! 'SpecSharp|AnyCPU', 'TRACE;DEBUG;SIGNED' + contents.change_configuration! 'Debug|AnyCPU', 'TRACE;DEBUG;SIGNED' + contents.change_configuration! 'Release|AnyCPU', 'TRACE;SIGNED' + contents.change_configuration! 'Silverlight Debug|AnyCPU', 'TRACE;DEBUG;SILVERLIGHT' + contents.change_configuration! 'Silverlight Release|AnyCPU', 'TRACE;SILVERLIGHT' + end + end + transform_project(:dlr_com, 'system.dynamic.cominterop.csproj') do |contents| + if IronRuby.is_merlin? + replace_output_path contents, '..\..\..\..\Merlin\Main\Bin\Debug\\', '..\..\build\debug\\' + replace_output_path contents, '..\..\..\..\Merlin\Main\Bin\Release\\', '..\..\build\release\\' + replace_output_path contents, '..\..\..\..\Merlin\Main\Bin\Silverlight Debug\\', '..\..\build\silverlight debug\\' + replace_output_path contents, '..\..\..\..\Merlin\Main\Bin\Silverlight Release\\', '..\..\build\silverlight release\\' + replace_import_project contents, '..\..\..\..\Merlin\Main\SpecSharp.targets', '..\..\SpecSharp.targets' + replace_doc_path contents, '..\..\..\..\Merlin\Main\Bin\Debug\Microsoft.Scripting.Core.xml', '..\..\build\debug\Microsoft.Scripting.Core.xml' + replace_doc_path contents, '..\..\..\..\Merlin\Main\Bin\Release\Microsoft.Scripting.Core.xml', '..\..\build\release\Microsoft.Scripting.Core.xml' + replace_doc_path contents, '..\..\..\..\Merlin\Main\Bin\Silverlight Debug\Microsoft.Scripting.Core.xml', '..\..\build\silverlight debug\Microsoft.Scripting.Core.xml' + replace_doc_path contents, '..\..\..\..\Merlin\Main\Bin\Silverlight Release\Microsoft.Scripting.Core.xml', '..\..\build\silverlight release\Microsoft.Scripting.Core.xml' + + contents.change_configuration! 'FxCop|AnyCPU', 'TRACE;MICROSOFT_SCRIPTING_CORE' + contents.change_configuration! 'Debug|AnyCPU', 'TRACE;DEBUG;MICROSOFT_SCRIPTING_CORE' + contents.change_configuration! 'Release|AnyCPU', 'TRACE;MICROSOFT_SCRIPTING_CORE' + else + replace_output_path contents, '..\..\build\debug\\', '..\..\..\..\Merlin\Main\Bin\Debug\\' + replace_output_path contents, '..\..\build\release\\', '..\..\..\..\Merlin\Main\Bin\Release\\' + replace_output_path contents, '..\..\build\silverlight debug\\', '..\..\..\..\Merlin\Main\Bin\Silverlight Debug\\' + replace_output_path contents, '..\..\build\silverlight release\\', '..\..\..\..\Merlin\Main\Bin\Silverlight Release\\' + replace_import_project contents, '..\..\SpecSharp.targets', '..\..\..\..\Merlin\Main\SpecSharp.targets' + replace_doc_path contents, '..\..\build\debug\microsoft.scripting.core.xml', '..\..\..\..\Merlin\Main\Bin\Debug\Microsoft.Scripting.Core.xml' + replace_doc_path contents, '..\..\build\release\microsoft.scripting.core.xml', '..\..\..\..\Merlin\Main\Bin\Release\Microsoft.Scripting.Core.xml' + replace_doc_path contents, '..\..\build\silverlight debug\microsoft.scripting.core.xml', '..\..\..\..\Merlin\Main\Bin\Silverlight Debug\Microsoft.Scripting.Core.xml' + replace_doc_path contents, '..\..\build\silverlight release\microsoft.scripting.core.xml', '..\..\..\..\Merlin\Main\Bin\Silverlight Release\Microsoft.Scripting.Core.xml' + + contents.change_configuration! 'FxCop|AnyCPU', 'TRACE;MICROSOFT_SCRIPTING_CORE;SIGNED' + contents.change_configuration! 'Debug|AnyCPU', 'TRACE;DEBUG;MICROSOFT_SCRIPTING_CORE;SIGNED' + contents.change_configuration! 'Release|AnyCPU', 'TRACE;MICROSOFT_SCRIPTING_CORE;SIGNED' + end + end + # TODO: add signing to this project + transform_project(:yaml, 'IronRuby.Libraries.Yaml.csproj') do |contents| + if IronRuby.is_merlin? + replace_output_path contents, '..\..\..\..\..\Main\Bin\Debug\\', '..\..\build\debug\\' + replace_output_path contents, '..\..\..\..\..\Main\Bin\Release\\', '..\..\build\release\\' + replace_output_path contents, '..\..\..\..\..\Main\Bin\Silverlight Debug\\', '..\..\build\silverlight debug\\' + replace_output_path contents, '..\..\..\..\..\Main\Bin\Silverlight Release\\', '..\..\build\silverlight release\\' + replace_import_project contents, '..\..\..\..\Main\SpecSharp.targets', '..\..\SpecSharp.targets' + else + replace_output_path contents, '..\..\build\debug\\', '..\..\..\..\..\Main\Bin\Debug\\' + replace_output_path contents, '..\..\build\release\\', '..\..\..\..\..\Main\Bin\Release\\' + replace_output_path contents, '..\..\build\silverlight debug\\', '..\..\..\..\..\Main\Bin\Silverlight Debug\\' + replace_output_path contents, '..\..\build\silverlight release\\', '..\..\..\..\..\Main\Bin\Silverlight Release\\' + replace_import_project contents, '..\..\SpecSharp.targets', '..\..\..\..\..\Main\SpecSharp.targets' + end + end + transform_project :generator, 'classinitgenerator.csproj' + transform_project(:console, 'ruby.console.csproj') do |contents| + if IronRuby.is_merlin? + replace_output_path contents, '..\..\..\Bin\Debug\\', '..\..\build\debug\\' + replace_output_path contents, '..\..\..\Bin\Release\\', '..\..\build\release\\' + replace_output_path contents, '..\..\..\Bin\Silverlight Debug\\', '..\..\build\silverlight debug\\' + replace_output_path contents, '..\..\..\Bin\Silverlight Release\\', '..\..\build\silverlight release\\' + + replace_post_build_event contents, 'copy $(ProjectDir)..\merlin_ir.exe.config $(TargetDir)ir.exe.config', + 'copy $(ProjectDir)..\..\external_ir.exe.config $(TargetDir)ir.exe.config' + + replace_app_config_path contents, '..\..\..\App.config', '..\..\App.config' + else + replace_output_path contents, '..\..\build\debug\\', '..\..\..\Bin\Debug\\' + replace_output_path contents, '..\..\build\release\\', '..\..\..\Bin\Release\\' + replace_output_path contents, '..\..\build\silverlight debug', '..\..\..\Bin\Silverlight Debug\\' + replace_output_path contents, '..\..\build\silverlight release', '..\..\..\Bin\Silverlight Release\\' + + replace_post_build_event contents, 'copy $(ProjectDir)..\..\external_ir.exe.config $(TargetDir)ir.exe.config', + 'copy $(ProjectDir)..\merlin_ir.exe.config $(TargetDir)ir.exe.config' + + replace_app_config_path contents, '..\..\App.config', '..\..\..\App.config' + end + end + transform_project :test_runner, 'ironruby.tests.csproj' do |contents| + if IronRuby.is_merlin? + replace_output_path contents, '..\..\..\Bin\Debug\\', '..\..\build\debug\\' + replace_output_path contents, '..\..\..\Bin\Release\\', '..\..\build\release\\' + replace_output_path contents, '..\..\..\Bin\Silverlight Debug\\', '..\..\build\silverlight debug\\' + replace_output_path contents, '..\..\..\Bin\Silverlight Release\\', '..\..\build\silverlight release\\' + + replace_app_config_path contents, '..\..\..\App.config', '..\..\App.config' + else + replace_output_path contents, '..\..\build\debug\\', '..\..\..\Bin\Debug\\' + replace_output_path contents, '..\..\build\release\\', '..\..\..\Bin\Release\\' + replace_output_path contents, '..\..\build\silverlight debug', '..\..\..\Bin\Silverlight Debug\\' + replace_output_path contents, '..\..\build\silverlight release', '..\..\..\Bin\Silverlight Release\\' + replace_key_path contents, '..\..\RubyTestKey.snk', '..\..\..\MSSharedLibKey.snk' + + replace_app_config_path contents, '..\..\App.config', '..\..\..\App.config' + end + end + + transform_project :scanner, 'ironruby.libraries.scanner.csproj' + end +end + +desc "push TFS source tree to Subversion" +task :to_svn => [:happy, :clean_tests] do + raise "must be in MERLIN enlistment to run to_svn" unless IronRuby.is_merlin? + push +end + +desc "push Subversion source tree to TFS" +task :to_merlin => [:happy, :clean_tests] do + raise "must be in SVN enlistment to run to_merlin" if IronRuby.is_merlin? + push +end + +# Compilation tasks + +desc "clean build directory" +task :clean_build => [:happy] do + IronRuby.source_context do + rd build_path + mkdir build_path + end +end + +desc "compile extension attribute assembly" +task :compile_extension_attributes => [:clean_build] do + IronRuby.source_context do + compile :dlr_core, :references => ['!System.dll'], :switches => ['target:library'], :output => 'Microsoft.Scripting.ExtensionAttribute.dll', :csproj => 'microsoft.scripting.extensionattribute.csproj' + end +end + +desc "compile DLR (Microsoft.Scripting.dll and Microsoft.Scripting.Core.dll)" +task :compile_dlr => [:compile_extension_attributes] do + IronRuby.source_context do + compile :dlr_core, :references => ['!System.dll', '!System.Configuration.dll', 'Microsoft.Scripting.ExtensionAttribute.dll'], :switches => ['target:library', 'define:MICROSOFT_SCRIPTING_CORE'], :output => 'Microsoft.Scripting.Core.dll', :csproj => 'Microsoft.Scripting.Core.csproj' + resources = { Pathname.new('math') + 'MathResources.resx' => Pathname.new('Microsoft.Scripting.Math.MathResources.resources') } + compile :dlr_libs, :references => ['Microsoft.Scripting.Core.dll', '!System.Xml.dll', '!System.dll', '!System.Configuration.dll', 'Microsoft.Scripting.ExtensionAttribute.dll','!System.Runtime.Remoting.dll'], :switches => ['target:library'], :resources => resources, :output => 'Microsoft.Scripting.dll' + compile :dlr_com, :references => ['Microsoft.Scripting.Core.dll', '!System.Xml.dll', '!System.dll', 'Microsoft.Scripting.ExtensionAttribute.dll'], :switches => ['target:library', 'unsafe'], :output => 'Microsoft.Dynamic.ComInterop.dll' + end +end + +desc "compile ClassInitGenerator.exe" +task :compile_generator => [:compile_ruby] do + IronRuby.source_context do + compile :generator, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'Microsoft.Scripting.ExtensionAttribute.dll', 'IronRuby.dll', '!System.dll'], :output => 'ClassInitGenerator.exe' + end +end + +desc "compile IronRuby.dll" +task :compile_ruby => [:compile_dlr] do + IronRuby.source_context do + compile :ironruby, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'Microsoft.Scripting.ExtensionAttribute.dll', '!System.dll', '!System.Configuration.dll'], :switches => ['target:library'], :output => 'IronRuby.dll' + end +end + +desc "compile IronRuby.Libraries.dll" +task :compile_libraries => [:compile_ruby] do + IronRuby.source_context do + compile :libraries, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'Microsoft.Scripting.ExtensionAttribute.dll', 'IronRuby.dll', '!System.dll'], :switches => ['target:library'], :output => 'IronRuby.Libraries.dll' + end +end + +def transform_config(source_path, target_path, signed, paths) + file = File.new source_path + doc = Document.new file + + # disable signing + unless signed + configSections = XPath.each(doc, '/configuration/configSections/section') do |node| + node.attributes['type'].gsub!(/31bf3856ad364e35/, 'null') + end + + # disable signing in IronRuby and replace the paths + languages = XPath.each(doc, '/configuration/microsoft.scripting/languages/language') do |node| + if node.attributes['names'] == 'IronRuby;Ruby;rb' + node.attributes['type'].gsub!(/31bf3856ad364e35/, 'null') + end + end + end + + # replace LibraryPaths + options = XPath.each(doc, '/configuration/microsoft.scripting/options/set') do |node| + if node.attributes['language'] == 'Ruby' && node.attributes['option'] == 'LibraryPaths' + node.attributes['value'] = paths + end + end + + File.open(target_path, 'w+') do |f| + f.write doc.to_s + end +end + +def transform_config_file(configuration, source_path, target_build_path) + # signing is on for IronRuby in Merlin, off for SVN and Binary + layout = {'Merlin' => { :signing => false, :LibraryPaths => '..\..\Languages\Ruby\libs;..\..\..\External\Languages\Ruby\Ruby-1.8.6\lib\ruby\site_ruby\1.8;..\..\..\External\Languages\Ruby\Ruby-1.8.6\lib\ruby\site_ruby;..\..\..\External\Languages\Ruby\Ruby-1.8.6\lib\ruby\1.8' }, + 'Svn' => { :signing => false, :LibraryPaths => '..\..\lib\IronRuby;..\..\lib\ruby\site_ruby\1.8;..\..\lib\ruby\site_ruby;..\..\lib\ruby\1.8' }, + 'Binary' => { :signing => true, :LibraryPaths => '..\lib\IronRuby;..\lib\ruby\site_ruby\1.8;..\lib\ruby\site_ruby;..\lib\ruby\1.8' } } + + transform_config source_path, target_build_path, layout[configuration][:signing], layout[configuration][:LibraryPaths] +end + +desc "compile IronRuby console" +task :compile_console => [:compile_libraries] do + IronRuby.source_context do + compile :console, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'IronRuby.dll'], :output => IRONRUBY_COMPILER + transform_config_file IronRuby.is_merlin? ? 'Merlin' : 'Svn', get_source_dir(:lang_root) + 'app.config', "#{build_path}\\ir.exe.config" + end +end + +desc "compile IronRuby.Tests" +task :compile_testhost => [:compile_libraries] do + IronRuby.source_context do + compile :test_runner, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'IronRuby.dll', 'IronRuby.Libraries.dll', '!System.dll', '!System.Windows.Forms.dll'], :output => 'IronRuby.Tests.exe' + end +end + +desc "compile IronRuby.Libraries.Scanner" +task :compile_scanner => [:compile_libraries] do + IronRuby.source_context do + compile :scanner, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'IronRuby.dll', 'IronRuby.Libraries.dll', '!System.Core.dll'], :output => 'IronRuby.Libraries.Scanner.exe' + end +end + +desc "compile Yaml" +task :compile_yaml => [:compile_libraries] do + IronRuby.source_context do + compile :yaml, :references => ['Microsoft.Scripting.Core.dll', 'Microsoft.Scripting.dll', 'IronRuby.dll', 'IronRuby.Libraries.dll', '!System.dll'], :switches => ['target:library'], :output => 'IronRuby.Libraries.Yaml.dll' + end +end + +desc "compile everything" +task :compile => [:happy, :clean_build, :compile_dlr, :compile_ruby, :compile_libraries, :compile_console, :compile_testhost, :compile_generator, :compile_yaml] do +end + +# Unit test/spec tasks + +desc "[deprecated] run old school spec tests" +task :test_libs do + IronRuby.source_context do + get_source_dir(:tests).filtered_subdirs.each do |dir| + chdir(dir) { + dir.glob('test_*.rb').each { |file| exec_net "\"#{build_path + IRONRUBY_COMPILER}\" \"#{file}\"" } + } + end + end +end + +desc "run compiler only tests using IronRuby.Tests.exe C# driver" +task :test_compiler do + IronRuby.source_context do + exec_net "#{build_path + 'ironruby.tests.exe'}" + # TODO: make run.rb do the right thing in external svn-only install + chdir(:tests) { exec "ruby run.rb" } + end +end + +desc "Alias for mspec:core" +task :mspec => "mspec:core" + +namespace :mspec do + desc "Run RubySpec core suite" + task :core => ["ruby_imp", :testhappy] do + IronRuby.source_context { invoke_mspec($ruby_imp) } + exit + end + + desc "Run core suite with both CRuby and Ironruby" + task :dual => [:testhappy] do + IronRuby.source_context do + rake_output_message "Ruby\n" + invoke_mspec(UserEnvironment.mri_binary) + rake_output_message "IronRuby\n" + invoke_mspec(path_to_ir) + exit + end + end + + desc "Run RubySpec language suite" + task :lang => ["ruby_imp", :testhappy] do + IronRuby.source_context { invoke_mspec($ruby_imp, 'language')} + exit + end + + desc "Run RubySpec library suite" + task :lib => ["ruby_imp", :testhappy] do + IronRuby.source_context { invoke_mspec($ruby_imp, 'library')} + exit + end +end + +def split_args + klass = ARGV.length == 1 ? '-' : ARGV[1] + name = ARGV.length <= 2 ? '-' : ARGV[2] + reporter = ARGV.length == 4 ? ARGV[3] : "summary" + [klass, name, reporter] +end + +def path_to_ir + (build_path + IRONRUBY_COMPILER).gsub '\\', '/' +end + +def extract_reporter(reporter) + case reporter + when 'dox' + '-f specdoc' + when 'fail' + '-f fail' + when 'cov' + ['-f coverage','run -G critical'] + when 'tag' + ['-f dotted','tag -G critical'] + when 'run' + ['-f dotted','run -G critical'] + else + '-f dotted' + end +end + +def invoke_mspec(path_to_ruby, root_path = "core") + unless root_path == "language" + klass, name, reporter = split_args + else + name, reporter, _ = split_args + end + IronRuby.source_context do + root = UserEnvironment.rubyspec + spec_file = name == '-' ? '' : "#{name}_spec.rb" + spec_dir = klass == '-' ? '' : "#{klass}/" + spec_suite = spec_dir + spec_file + run_spec = root + "/1.8/#{root_path}/#{spec_suite}" + reporter,tag = extract_reporter(reporter) + + chdir(get_source_dir(:tests) +'util'){ + cmd = "\"#{UserEnvironment.mri_binary}\" \"#{UserEnvironment.mspec}/bin/mspec\" #{tag || 'ci'} -t #{path_to_ruby} -B \"#{UserEnvironment.config}\" \"#{run_spec}\" #{reporter}" + exec_net cmd + } + end +end + +desc "remove output files and generated debugging info from tests directory" +task :clean_tests do + IronRuby.source_context do + chdir(:tests) do + exec "del /s *.log" + exec "del /s *.pdb" + exec "del /s *.exe" + exec "del /s *.dll" + end + end +end + +desc "generate initializers.generated.cs file for IronRuby.Libraries" +task :gen do + IronRuby.source_context do + cmd = "#{build_path + 'ClassInitGenerator.exe'} #{build_path + 'IronRuby.Libraries.dll'} /libraries:IronRuby.Builtins;IronRuby.StandardLibrary.Threading;IronRuby.StandardLibrary.Sockets;IronRuby.StandardLibrary.OpenSsl;IronRuby.StandardLibrary.Digest;IronRuby.StandardLibrary.Zlib;IronRuby.StandardLibrary.StringIO;IronRuby.StandardLibrary.StringScanner;IronRuby.StandardLibrary.Enumerator;IronRuby.StandardLibrary.FunctionControl;IronRuby.StandardLibrary.FileControl;IronRuby.StandardLibrary.BigDecimal /out:#{get_source_dir(:libraries) + 'Initializers.Generated.cs'}" + exec_net cmd + end +end + +desc "generate initializers.generated.cs file for IronRuby.Libraries.Yaml" +task :gen_yaml do + IronRuby.source_context do + cmd = "#{build_path + 'ClassInitGenerator'} #{build_path + 'IronRuby.Libraries.Yaml.dll'} /libraries:IronRuby.StandardLibrary.Yaml /out:#{get_source_dir(:yaml) + 'Initializer.Generated.cs'}" + exec_net cmd + end +end + +def path_exists?(paths, command) + paths.each do |path| + return true if (path + command).exists? + end + false +end + +STDLIB_CLASSES = {} + +def walk_classes(klass) + klass.constants.each do |constant| + current_klass = klass.const_get(constant) + if current_klass.is_a?(Module) + return if STDLIB_CLASSES.has_key?(current_klass.name) + STDLIB_CLASSES[current_klass.name] = true + walk_classes(current_klass) + end + end +end + +desc "perform gap analysis on app's library method usage and IronRuby" +task :gap => [:compile_scanner] do + IronRuby.source_context do + libraries_file = Pathname.new(Dir.tmpdir) + "libraries.txt" + exec "#{build_path + 'IronRuby.Libraries.Scanner.exe'} > \"#{libraries_file}\"" + + library_methods = {} + IO.foreach(libraries_file) { |method| library_methods[method.strip] = true } + + # Generate list of methods used by program + trace_output_stream = File.open(Pathname.new(Dir.tmpdir) + 'trace.txt', 'w') + function_table = {} + + ARGV.delete_at 0 + if ARGV.length < 1 + rake_output_message "usage: rake gap program [args]" + exit(-1) + end + + app_name = ARGV.first + ARGV.delete_at 0 + + walk_classes(self.class) + + set_trace_func proc { |event, file, line, id, binding, klass| + if event == "c-call" || event == "call" + method_name = klass.to_s + "#" + id.to_s + function_table[method_name] = true + end + } + + at_exit do + rake_output_message 'shutdown ...' + function_table.keys.sort.each do |method_name| + class_name = method_name.split("#").first + if STDLIB_CLASSES.has_key?(class_name) && !library_methods.has_key?(method_name) + # output methods that aren't in standard library + trace_output_stream.puts method_name + end + end + trace_output_stream.close + end + + load app_name + rake_output_message 'about to exit ...' + exit + end +end + +desc "is the environment setup for an IronRuby dev?" +task :happy do + IronRuby.source_context do + commands = ENV['mono'].nil? ? ['resgen.exe', 'csc.exe'] : ['resgen', 'gmcs'] + commands += ['tf.exe', 'svn.exe'] if IronRuby.is_merlin? + + paths = ENV['PATH'].split(File::PATH_SEPARATOR).collect { |path| Pathname.new path } + + failure = false + commands.each do |command| + if !path_exists? paths, command + rake_output_message "Cannot find #{command} on system path." + failure = true + end + end + + if failure + rake_output_message "\n" + rake_output_message "***** Missing commands! You must have the .NET redist and the SDK" + rake_output_message "***** (for resgen.exe) installed. If you are synchronizing source" + rake_output_message "***** trees *inside* Microsoft, you must have both tfs.exe and" + rake_output_message "***** svn.exe on your path." + abort + end + end +end + +# New mspec tasks for developers - these set a new regression baseline, run the +# regression tests, reports why a regression test failed, and test a specific class +# while ignoring exclusions. + +def run_specs(method_name) + ARGV.delete_at 0 + runner = MSpecRunner.new + + IronRuby.source_context do + iruby = $ruby_imp + + if ARGV.length == 0 + runner.all_core(method_name, iruby) + elsif ARGV.length == 1 + runner.send(:"#{method_name}", iruby, ARGV.first) + elsif ARGV.length == 2 + runner.send(:"#{method_name}", iruby, ARGV[0], ARGV[1]) + else + rake_output_message "usage: rake #{method_name} [class] [method]" + exit(-1) + end + end + runner +end + +desc "yell if the rubyspec test environment is not setup" +task :testhappy do + spec_env_ready = UserEnvironment.rubyspec? && UserEnvironment.mspec? && UserEnvironment.tags? && UserEnvironment.config? + + if not spec_env_ready + rake_output_message "\n" + rake_output_message "***** Missing rubyspec test environment! You must have rubyspec, mspec, ironruby-tags and default.mspec" + rake_output_message "***** 1. get GIT from http://code.google.com/p/msysgit/" + rake_output_message "***** 2. run: git clone git://github.com/ironruby/ironruby-tags.git" + rake_output_message "***** 3. run: git clone git://github.com/ironruby/mspec.git" + rake_output_message "***** 4. run: git clone git://github.com/ironruby/rubyspec.git" + rake_output_message "***** 5. run: runfirst.cmd" + rake_output_message "***** 6. edit: #{ENV['USERPROFILE']}\\.irconfig.rb" + rake_output_message "***** 7. edit: #{ENV['USERPROFILE']}\\default.mspec" + exit(-1) + end +end + +desc "generate new baseline against a class" +task :baseline => [:testhappy, :ruby_imp] do + run_specs(:baseline) + exit +end + +desc "run specs against a class, ignoring all exclusions" +task :test => [:testhappy, :ruby_imp] do + run_specs :test + exit +end + +desc "show report of why a regression test failed" +task :why_regression => [:testhappy, :ruby_imp] do + run_specs :why_regression + exit +end + +desc "run regression tests using mspec" +task :regression => [:testhappy, :ruby_imp] do + run_specs(:regression).report + exit +end + +desc "regenerate critical tags" +task :regen_tags => [:testhappy] do + IronRuby.source_context { MSpecRunner.new.generate_critical_tags } +end + +desc "Set ruby runner to CRuby" +task :ruby do + begin + old_verbose,$VERBOSE = $VERBOSE,nil + $ruby_imp = [UserEnvironment.mri_binary] + ARGV = [ARGV[0],*ARGV[2..-1]] + ensure + $VERBOSE = old_verbose + end +end + +task :ruby_imp do + IronRuby.source_context do + $ruby_imp ||= %Q{#{path_to_ir} -T "-X:Interpret"} + end +end + +desc "Run PEVerify on the generated IL" +task :peverify do + begin + old_verbose, $VERBOSE = $VERBOSE, nil + IronRuby.source_context {$ruby_imp ||= %Q{#{path_to_ir} -T "-X:SaveAssemblies"} } + ARGV = [ARGV[0], *ARGV[2..-1]] + ensure + $VERBOSE = old_verbose + end +end + +desc "Generate an IronRuby binary redist package from the layout" +task :package do + IronRuby.source_context do + # Directory layouts + system %Q{rmdir /S /Q #{PACKAGE_DIR}} + mkdir_p PACKAGE_DIR + mkdir_p "#{PACKAGE_DIR}\\bin" + + # Copy Licenses + system %Q{copy "#{ENV['MERLIN_ROOT']}\\Languages\\Ruby\\Licenses\\*" #{PACKAGE_DIR}} + + # Copy binaries + system %Q{copy "#{ENV['MERLIN_ROOT']}\\Languages\\Ruby\\IronRuby.BinaryLayout.config" "#{PACKAGE_DIR}\\bin\\ir.exe.config"} + system %Q{copy "#{ENV['MERLIN_ROOT']}\\bin\\release\\ir.exe" #{PACKAGE_DIR}\\bin\\} + system %Q{copy "#{ENV['MERLIN_ROOT']}\\bin\\release\\IronRuby*.dll" #{PACKAGE_DIR}\\bin\\} + system %Q{copy "#{ENV['MERLIN_ROOT']}\\bin\\release\\Microsoft.Scripting.Core.dll" #{PACKAGE_DIR}\\bin\\} + system %Q{copy "#{ENV['MERLIN_ROOT']}\\bin\\release\\Microsoft.Scripting.dll" #{PACKAGE_DIR}\\bin\\} + system %Q{copy "#{ENV['MERLIN_ROOT']}\\Languages\\Ruby\\Scripts\\bin\\*" #{PACKAGE_DIR}\\bin\\} + + # Generate ir.exe.config + transform_config_file 'Binary', get_source_dir(:lang_root) + 'app.config', "#{PACKAGE_DIR}\\bin\\ir.exe.config" + + # Copy standard library + system %Q{xcopy /E /I "#{ENV['MERLIN_ROOT']}\\..\\External\\Languages\\Ruby\\redist-libs\\ruby" #{PACKAGE_DIR}\\lib\\ruby} + system %Q{xcopy /E /I "#{ENV['MERLIN_ROOT']}\\Languages\\Ruby\\Libs" #{PACKAGE_DIR}\\lib\\IronRuby} + + # Generate compressed package + if ENV['ZIP'] + system %Q{del "#{ENV['TEMP']}\\ironruby.7z"} + system %Q{"#{ENV['PROGRAM_FILES_32']}/7-Zip/7z.exe" a -bd -t7z -mx9 "#{ENV['TEMP']}\\ironruby.7z" "#{PACKAGE_DIR}\\"} + system %Q{"#{ENV['PROGRAM_FILES_32']}/7-Zip/7z.exe" a -bd -tzip -mx9 "c:\\ironruby.zip" "#{PACKAGE_DIR}\\"} + system %Q{copy /b /Y "#{ENV['PROGRAM_FILES_32']}\\7-Zip\\7zSD.sfx" + "#{ENV['MERLIN_ROOT']}\\Languages\\Ruby\\sfx_config.txt" + "#{ENV['TEMP']}\\ironruby.7z" "c:\\ironruby.exe"} + end + end +end + +task :default => [:happy] do + Rake.application.options.show_tasks = true + Rake.application.options.show_task_pattern = Regexp.new('.') + Rake.application.display_tasks_and_comments +end diff --git a/merlin/main/languages/ruby/Ruby.sln b/merlin/main/languages/ruby/Ruby.sln new file mode 100644 index 0000000000..c0e3642aad --- /dev/null +++ b/merlin/main/languages/ruby/Ruby.sln @@ -0,0 +1,163 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronRuby.Tests", "IronRuby.Tests\IronRuby.Tests.csproj", "{8103D91B-89D8-4A18-9A40-426992602EA2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ruby", "Ruby\Ruby.csproj", "{7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ruby.Console", "Console\Ruby.Console.csproj", "{D6AB587D-A888-4B98-85AC-F15E36F53838}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassInitGenerator", "ClassInitGenerator\ClassInitGenerator.csproj", "{166940A1-2C91-4BD0-B72B-A517FBD8BF0B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Scripting.Core", "..\..\..\..\ndp\fx\src\Core\Microsoft\Scripting\Microsoft.Scripting.Core.csproj", "{2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronRuby.Libraries", "Libraries.LCA_RESTRICTED\IronRuby.Libraries.csproj", "{77323B06-15A2-4CF4-8A7A-86EAA2B66498}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronRuby.Libraries.Scanner", "Utils\IronRuby.Libraries.Scanner\IronRuby.Libraries.Scanner.csproj", "{5F692D9C-968B-48BF-AC2F-6B4F54D9C1BB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Scripting", "..\..\Runtime\Microsoft.Scripting\Microsoft.Scripting.csproj", "{EB66B766-6354-4208-A3D4-AACBDCB5C3B3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronRuby.Libraries.Yaml", "..\..\..\External\Languages\IronRuby\yaml\IronRuby.Libraries.Yaml\IronRuby.Libraries.Yaml.csproj", "{AA18A245-E342-4368-A474-83178311A742}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Scripting.ExtensionAttribute", "..\..\..\..\ndp\fx\src\Core\Microsoft\Scripting\Microsoft.Scripting.ExtensionAttribute.csproj", "{8B0F1074-750E-4D64-BF23-A1E0F54261E5}" +EndProject +Global + GlobalSection(TeamFoundationVersionControl) = preSolution + SccNumberOfProjects = 11 + SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} + SccTeamFoundationServer = http://vstfdevdiv:8080/ + SccLocalPath0 = . + SccProjectUniqueName1 = IronRuby.Tests\\IronRuby.Tests.csproj + SccProjectName1 = IronRuby.Tests + SccLocalPath1 = IronRuby.Tests + SccProjectUniqueName2 = Ruby\\Ruby.csproj + SccProjectName2 = Ruby + SccLocalPath2 = Ruby + SccProjectUniqueName3 = Console\\Ruby.Console.csproj + SccProjectName3 = Console + SccLocalPath3 = Console + SccProjectUniqueName4 = ClassInitGenerator\\ClassInitGenerator.csproj + SccProjectName4 = ClassInitGenerator + SccLocalPath4 = ClassInitGenerator + SccProjectUniqueName5 = Utils\\IronRuby.Libraries.Scanner\\IronRuby.Libraries.Scanner.csproj + SccProjectName5 = Utils/IronRuby.Libraries.Scanner + SccLocalPath5 = Utils\\IronRuby.Libraries.Scanner + SccProjectUniqueName6 = Libraries.LCA_RESTRICTED\\IronRuby.Libraries.csproj + SccProjectName6 = Libraries.LCA_RESTRICTED + SccLocalPath6 = Libraries.LCA_RESTRICTED + SccProjectUniqueName7 = ..\\..\\Runtime\\Microsoft.Scripting\\Microsoft.Scripting.csproj + SccProjectName7 = ../../Runtime/Microsoft.Scripting + SccLocalPath7 = ..\\..\\Runtime\\Microsoft.Scripting + SccProjectUniqueName8 = ..\\..\\..\\..\\ndp\\fx\\src\\Core\\Microsoft\\Scripting\\Microsoft.Scripting.Core.csproj + SccProjectName8 = ../../../../ndp/fx/src/Core/Microsoft/Scripting + SccLocalPath8 = ..\\..\\..\\..\\ndp\\fx\\src\\Core\\Microsoft\\Scripting + SccProjectUniqueName9 = ..\\..\\..\\External\\Languages\\IronRuby\\yaml\\IronRuby.Libraries.Yaml\\IronRuby.Libraries.Yaml.csproj + SccProjectName9 = ../../../../../../../Merlin_External/Languages/IronRuby/yaml/IronRuby.Libraries.Yaml + SccLocalPath9 = ..\\..\\..\\External\\Languages\\IronRuby\\yaml\\IronRuby.Libraries.Yaml + SccProjectUniqueName10 = ..\\..\\..\\..\\ndp\\fx\\src\\Core\\Microsoft\\Scripting\\Microsoft.Scripting.ExtensionAttribute.csproj + SccProjectName10 = ../../../../ndp/fx/src/Core/Microsoft/Scripting + SccLocalPath10 = ..\\..\\..\\..\\ndp\\fx\\src\\Core\\Microsoft\\Scripting + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + FxCop|Any CPU = FxCop|Any CPU + Release|Any CPU = Release|Any CPU + Silverlight Debug|Any CPU = Silverlight Debug|Any CPU + Silverlight Release|Any CPU = Silverlight Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8103D91B-89D8-4A18-9A40-426992602EA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.FxCop|Any CPU.Build.0 = Release|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Release|Any CPU.Build.0 = Release|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8103D91B-89D8-4A18-9A40-426992602EA2}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.FxCop|Any CPU.Build.0 = FxCop|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Release|Any CPU.Build.0 = Release|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.FxCop|Any CPU.Build.0 = Release|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Release|Any CPU.Build.0 = Release|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6AB587D-A888-4B98-85AC-F15E36F53838}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.FxCop|Any CPU.Build.0 = Release|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Release|Any CPU.Build.0 = Release|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU + {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.FxCop|Any CPU.Build.0 = FxCop|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Release|Any CPU.Build.0 = Release|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.FxCop|Any CPU.Build.0 = Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Release|Any CPU.Build.0 = Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU + {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU + {5F692D9C-968B-48BF-AC2F-6B4F54D9C1BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F692D9C-968B-48BF-AC2F-6B4F54D9C1BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F692D9C-968B-48BF-AC2F-6B4F54D9C1BB}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {5F692D9C-968B-48BF-AC2F-6B4F54D9C1BB}.FxCop|Any CPU.Build.0 = Release|Any CPU + {5F692D9C-968B-48BF-AC2F-6B4F54D9C1BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F692D9C-968B-48BF-AC2F-6B4F54D9C1BB}.Release|Any CPU.Build.0 = Release|Any CPU + {5F692D9C-968B-48BF-AC2F-6B4F54D9C1BB}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F692D9C-968B-48BF-AC2F-6B4F54D9C1BB}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.FxCop|Any CPU.Build.0 = FxCop|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Release|Any CPU.Build.0 = Release|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.FxCop|Any CPU.Build.0 = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Release|Any CPU.Build.0 = Release|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA18A245-E342-4368-A474-83178311A742}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.FxCop|Any CPU.Build.0 = FxCop|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Release|Any CPU.Build.0 = Release|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU + {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/merlin/main/languages/ruby/Ruby.vssscc b/merlin/main/languages/ruby/Ruby.vssscc new file mode 100644 index 0000000000..6cb031bcf5 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby.vssscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT" +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/Binding.cs b/merlin/main/languages/ruby/Ruby/Builtins/Binding.cs new file mode 100644 index 0000000000..cfad93c158 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/Binding.cs @@ -0,0 +1,32 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + public sealed class Binding { + private readonly RubyScope/*!*/ _localScope; + + public RubyScope/*!*/ LocalScope { + get { return _localScope; } + } + + public Binding(RubyScope/*!*/ localScope) { + Assert.NotNull(localScope); + _localScope = localScope; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/ConsoleStream.cs b/merlin/main/languages/ruby/Ruby/Builtins/ConsoleStream.cs new file mode 100644 index 0000000000..b21060bfb0 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/ConsoleStream.cs @@ -0,0 +1,94 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Threading; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using System.Text; + +namespace IronRuby.Builtins { + /// + /// A custom Stream class that forwards calls to Console In/Out/Error based on ConsoleType + /// + internal class ConsoleStream : Stream { + private ConsoleStreamType _consoleType; + private readonly SharedIO _io; + + public ConsoleStream(SharedIO/*!*/ io, ConsoleStreamType consoleType) { + Assert.NotNull(io); + _consoleType = consoleType; + _io = io; + } + + public override bool CanRead { + get { return _consoleType == ConsoleStreamType.Input ? true : false; } + } + + public override bool CanSeek { + get { return false; } + } + + public override bool CanWrite { + get { return _consoleType == ConsoleStreamType.Input ? false : true; } + } + + public override void Flush() { + switch (_consoleType) { + case ConsoleStreamType.ErrorOutput: + _io.ErrorWriter.Flush(); + break; + + case ConsoleStreamType.Output: + _io.OutputWriter.Flush(); + break; + + case ConsoleStreamType.Input: + throw new NotSupportedException(); + } + } + + public override long Length { + get { throw new NotSupportedException(); } + } + + public override long Position { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[]/*!*/ buffer, int offset, int count) { + return _io.InputStream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) { + throw new NotSupportedException(); + } + + public override void SetLength(long value) { + throw new NotSupportedException(); + } + + public override void Write(byte[]/*!*/ buffer, int offset, int count) { + if (_consoleType == ConsoleStreamType.Output) { + _io.OutputStream.Write(buffer, offset, count); + } else { + _io.ErrorStream.Write(buffer, offset, count); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/DuplexStream.cs b/merlin/main/languages/ruby/Ruby/Builtins/DuplexStream.cs new file mode 100644 index 0000000000..5a2c8e8801 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/DuplexStream.cs @@ -0,0 +1,99 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Threading; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using System.Text; +using System.Diagnostics; + +namespace IronRuby.Builtins { + // TODO: remove (RubyIO class should handle this functionality) + internal sealed class DuplexStream : Stream { + private readonly StreamReader _reader; + private readonly StreamWriter _writer; + + public DuplexStream(StreamReader reader, StreamWriter writer) { + Debug.Assert(reader != null || writer != null); + _reader = reader; + _writer = writer; + } + + public override void Close() { + _reader.Close(); + _writer.Close(); + } + + public StreamReader Reader { + get { return _reader; } + } + + public StreamWriter Writer { + get { return _writer; } + } + + public override bool CanRead { + get { return _reader != null; } + } + + public override bool CanSeek { + get { return false; } + } + + public override bool CanWrite { + get { return _writer != null; } + } + + public override void Flush() { + _reader.BaseStream.Flush(); + _writer.Flush(); + } + + public override long Length { + get { throw new NotImplementedException(); } + } + + public override long Position { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override int ReadByte() { + return _reader.Read(); + } + + public override int Read(byte[]/*!*/ buffer, int offset, int count) { + // TODO: + return _reader.BaseStream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) { + throw new NotImplementedException(); + } + + public override void SetLength(long value) { + throw new NotImplementedException(); + } + + public override void Write(byte[] buffer, int offset, int count) { + // TODO: + Debug.Assert(_writer != null); + _writer.Write(_writer.Encoding.GetString(buffer, offset, count)); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/Exceptions.cs b/merlin/main/languages/ruby/Ruby/Builtins/Exceptions.cs new file mode 100644 index 0000000000..49e1499397 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/Exceptions.cs @@ -0,0 +1,187 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + [Serializable] + public class LocalJumpError : SystemException { + + private readonly RuntimeFlowControl _skipFrame; + + /// + /// The exception cannot be rescued in this frame if set. + /// + internal RuntimeFlowControl SkipFrame { + get { return _skipFrame; } + } + + internal LocalJumpError(string/*!*/ message, RuntimeFlowControl/*!*/ skipFrame) + : this(message, (Exception)null) { + Assert.NotNull(message, skipFrame); + _skipFrame = skipFrame; + } + + public LocalJumpError() : this(null, (Exception)null) { } + public LocalJumpError(string message) : this(message, (Exception)null) { } + public LocalJumpError(string message, Exception inner) : base(message, inner) { } + +#if !SILVERLIGHT + protected LocalJumpError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [Serializable] + public class SystemExit : Exception { + private readonly int _status; + + public int Status { + get { return _status; } + } + + public SystemExit(int status, string message) + : this(message) { + _status = status; + } + + public SystemExit(int status) + : this() { + _status = status; + } + + public SystemExit() : this(null, null) { } + public SystemExit(string message) : this(message, null) { } + public SystemExit(string message, Exception inner) : base(message, inner) { } + +#if !SILVERLIGHT + protected SystemExit(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [Serializable] + public class ScriptError : Exception { + public ScriptError() : this(null, null) { } + public ScriptError(string message) : this(message, null) { } + public ScriptError(string message, Exception inner) : base(message, inner) { } + +#if !SILVERLIGHT + protected ScriptError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [Serializable] + public class NotImplementedError : ScriptError { + public NotImplementedError() : this(null, null) { } + public NotImplementedError(string message) : this(message, null) { } + public NotImplementedError(string message, Exception inner) : base(message, inner) { } + +#if !SILVERLIGHT + protected NotImplementedError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [Serializable] + public class LoadError : ScriptError { + public LoadError() : this(null, null) { } + public LoadError(string message) : this(message, null) { } + public LoadError(string message, Exception inner) : base(message, inner) { } + +#if !SILVERLIGHT + protected LoadError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [Serializable] + public class SystemStackError : SystemException { + public SystemStackError() : this(null, null) { } + public SystemStackError(string message) : this(message, null) { } + public SystemStackError(string message, Exception inner) : base(message, inner) { } + +#if !SILVERLIGHT + protected SystemStackError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [Serializable] + public class RegexpError : SystemException { + public RegexpError() : this(null, null) { } + public RegexpError(string message) : this(message, null) { } + public RegexpError(string message, Exception inner) : base(message, inner) { } + +#if !SILVERLIGHT + protected RegexpError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + [Serializable] + public class SyntaxError : ScriptError { + private readonly string _file; + private readonly string _lineSourceCode; + private readonly int _line; + private readonly int _column; + private readonly bool _hasLineInfo; + + public SyntaxError() : this(null, null) { } + public SyntaxError(string message) : this(message, null) { } + public SyntaxError(string message, Exception inner) : base(message, inner) { } + + internal string File { + get { return _file; } + } + + internal int Line { + get { return _line; } + } + + internal int Column { + get { return _column; } + } + + internal string LineSourceCode { + get { return _lineSourceCode; } + } + + internal bool HasLineInfo { + get { return _hasLineInfo; } + } + + internal SyntaxError(string/*!*/ message, string file, int line, int column, string lineSourceCode) + : base(message) { + _file = file; + _line = line; + _column = column; + _lineSourceCode = lineSourceCode; + _hasLineInfo = true; + } + +#if !SILVERLIGHT + protected SyntaxError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } +#endif + } + + +} + diff --git a/merlin/main/languages/ruby/Ruby/Builtins/File.cs b/merlin/main/languages/ruby/Ruby/Builtins/File.cs new file mode 100644 index 0000000000..b19c47b826 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/File.cs @@ -0,0 +1,124 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + [Flags] + public enum RubyFileMode : int { + APPEND = 0x08, + BINARY = 0x8000, + CREAT = 0x100, + EXCL = 0x400, + NONBLOCK = 0x01, + RDONLY = 0x00, + RDWR = 0x02, + TRUNC = 0x200, + WRONLY = 0x01, + ReadWriteMask = 0x03, + } + + public class RubyFile : RubyIO { + private readonly string/*!*/ _path; + + private static Stream/*!*/ OpenFileStream(RubyContext/*!*/ context, string/*!*/ path, RubyFileMode mode) { + FileMode fileMode; + FileAccess access = FileAccess.Read; + FileShare share = FileShare.ReadWrite; + + RubyFileMode readWriteFlags = mode & RubyFileMode.ReadWriteMask; + + if (readWriteFlags == RubyFileMode.WRONLY) { + access = FileAccess.Write; + } else if (readWriteFlags == RubyFileMode.RDONLY) { + access = FileAccess.Read; + } else if (readWriteFlags == RubyFileMode.RDWR) { + access = FileAccess.ReadWrite; + } else { + throw new ArgumentException("file open mode must be one of RDONLY WRONLY or RDWR"); + } + + if ((mode & RubyFileMode.APPEND) != 0) { + fileMode = FileMode.Append; + } else if ((mode & RubyFileMode.CREAT) != 0) { + fileMode = FileMode.Create; + } else if ((mode & RubyFileMode.TRUNC) != 0) { + fileMode = FileMode.Truncate; + } else { + fileMode = FileMode.Open; + } + + if ((mode & RubyFileMode.EXCL) != 0) { + share = FileShare.None; + } + + return context.DomainManager.Platform.OpenInputFileStream(path, fileMode, access, share); + } + + private static Stream/*!*/ OpenFileStream(RubyContext/*!*/ context, string/*!*/ path, string/*!*/ modeString) { + FileMode mode; + FileAccess access; + + // ignore "b": + bool preserveEndOfLines; + + switch (RubyIO.ParseIOMode(modeString, out preserveEndOfLines)) { + case IOMode.ReadOnlyFromStart: + mode = FileMode.Open; access = FileAccess.Read; + break; + + case IOMode.ReadWriteFromStart: + mode = FileMode.Open; access = FileAccess.ReadWrite; + break; + + case IOMode.WriteOnlyTruncate: + mode = FileMode.Create; access = FileAccess.Write; + break; + + case IOMode.ReadWriteTruncate: + mode = FileMode.Create; access = FileAccess.ReadWrite; + break; + + case IOMode.WriteOnlyAppend: + mode = FileMode.Append; access = FileAccess.Write; + break; + + case IOMode.ReadWriteAppend: + mode = FileMode.Append; access = FileAccess.ReadWrite; + break; + + default: + throw RubyIO.IllegalMode(modeString); + } + return context.DomainManager.Platform.OpenInputFileStream(path, mode, access, FileShare.ReadWrite); + } + + public RubyFile(RubyContext/*!*/ context, string/*!*/ path, string/*!*/ modeString) + : base(context, OpenFileStream(context, path, modeString), modeString) { + _path = path; + } + + public RubyFile(RubyContext/*!*/ context, string/*!*/ path, RubyFileMode mode) + : base(context, OpenFileStream(context, path, mode), mode) { + _path = path; + } + + public string/*!*/ Path { + get { return _path; } + } + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Ruby/Builtins/GenericRegex.cs b/merlin/main/languages/ruby/Ruby/Builtins/GenericRegex.cs new file mode 100644 index 0000000000..83b027ac1a --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/GenericRegex.cs @@ -0,0 +1,376 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Text; +using System.Text.RegularExpressions; +using IronRuby.Compiler; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + public abstract class GenericRegex { + private readonly RubyRegexOptions _options; + + public abstract bool IsEmpty { get; } + public RubyRegexOptions Options { get { return _options; } } + public abstract MutableString/*!*/ GetPattern(); + + public abstract Match/*!*/ Match(MutableString/*!*/ input, int start, int count); + public abstract Match/*!*/ ReverseMatch(MutableString/*!*/ input, int start); + public abstract Match/*!*/ ReverseMatch(MutableString/*!*/ input, int start, int count); + public abstract MatchCollection/*!*/ Matches(MutableString/*!*/ input, int start); + public abstract MutableString[]/*!*/ Split(MutableString/*!*/ input, int count, int start); + + public bool Equals(GenericRegex/*!*/ other) { + // TODO: + return Options == other.Options && GetPattern().Equals(other.GetPattern()); + } + + public override int GetHashCode() { + // TODO: + return (int)Options ^ GetPattern().GetHashCode(); + } + + protected GenericRegex(RubyRegexOptions options) { + _options = options; + } + } + + public class BinaryRegex : GenericRegex { + private readonly byte[]/*!*/ _pattern; + + internal protected BinaryRegex(byte[]/*!*/ pattern, RubyRegexOptions options) + : base(options) { + Assert.NotNull(pattern); + _pattern = ArrayUtils.Copy(pattern); + } + + public override bool IsEmpty { + get { return _pattern.Length == 0; } + } + + public override Match/*!*/ Match(MutableString/*!*/ input, int start, int count) { + throw new NotImplementedException(); + } + + public override Match/*!*/ ReverseMatch(MutableString/*!*/ input, int start) { + throw new NotImplementedException(); + } + + public override Match/*!*/ ReverseMatch(MutableString/*!*/ input, int start, int count) { + throw new NotImplementedException(); + } + + public override MatchCollection/*!*/ Matches(MutableString/*!*/ input, int start) { + throw new NotImplementedException(); + } + + public override MutableString/*!*/ GetPattern() { + return MutableString.CreateBinary(_pattern); + } + + public override MutableString[]/*!*/ Split(MutableString/*!*/ input, int count, int start) { + throw new NotImplementedException(); + } + + internal static byte[]/*!*/ Escape(byte[]/*!*/ pattern) { + // TODO: encoding + return BinaryEncoding.Obsolete.GetBytes(StringRegex.Escape(BinaryEncoding.Obsolete.GetString(pattern, 0, pattern.Length))); + } + } + + public class StringRegex : GenericRegex { + internal static readonly StringRegex Empty = new StringRegex(new Regex(String.Empty, RubyRegex.ToClrOptions(RubyRegexOptions.NONE))); + + private readonly Regex/*!*/ _regex; + private readonly string/*!*/ _pattern; + + internal protected StringRegex(string/*!*/ pattern, RubyRegexOptions options) + : base(options) { + Assert.NotNull(pattern); + _pattern = pattern; + + string transformed = TransformPattern(pattern, options); + try { + _regex = new Regex(transformed, RubyRegex.ToClrOptions(options)); + } catch (ArgumentException e) { + Utils.Log("-- original ---" + new String('-', 50), "REGEX_ERROR"); + Utils.Log(pattern, "REGEX_ERROR"); + Utils.Log("-- transformed " + new String('-', 50), "REGEX_ERROR"); + Utils.Log(transformed, "REGEX_ERROR"); + Utils.Log("---------------" + new String('-', 50), "REGEX_ERROR"); + throw new RegexpError(e.Message, e); + } + } + + internal protected StringRegex(Regex/*!*/ regex) + : base(RubyRegexOptions.NONE) { + Assert.NotNull(regex); + _regex = regex; + _pattern = regex.ToString(); + } + + public override bool IsEmpty { + get { return _pattern.Length == 0; } + } + + public override Match/*!*/ Match(MutableString/*!*/ input, int start, int count) { + return _regex.Match(input.ConvertToString(), start, count); + } + + public override Match/*!*/ ReverseMatch(MutableString/*!*/ input, int start) { + return new Regex(_pattern, _regex.Options | RegexOptions.RightToLeft).Match(input.ConvertToString(), start); + } + + public override Match/*!*/ ReverseMatch(MutableString/*!*/ input, int start, int count) { + return new Regex(_pattern, _regex.Options | RegexOptions.RightToLeft).Match(input.ConvertToString(), start, count); + } + + public override MatchCollection Matches(MutableString/*!*/ input, int start) { + return _regex.Matches(input.ConvertToString(), start); + } + + public override MutableString/*!*/ GetPattern() { + return MutableString.Create(_pattern); + } + + public override MutableString[]/*!*/ Split(MutableString/*!*/ input, int count, int start) { + return MutableString.MakeArray(_regex.Split(input.ConvertToString(), count, start)); + } + + private static int SkipNonSpecial(string/*!*/ pattern, int i, out char escaped) { + while (i < pattern.Length) { + char c = pattern[i]; + switch (c) { + case '$': + case '^': + case '|': + case '[': + case ']': + case '(': + case ')': + case '\\': + case '.': + case '#': + case '-': + + case '{': + case '}': + case '*': + case '+': + case '?': + case ' ': + escaped = c; + return i; + + case '\t': + escaped = 't'; + return i; + + case '\n': + escaped = 'n'; + return i; + + case '\r': + escaped = 'r'; + return i; + + case '\f': + escaped = 'f'; + return i; + } + i++; + } + + escaped = '\0'; + return -1; + } + + internal static string/*!*/ Escape(string/*!*/ pattern) { + StringBuilder sb = EscapeToStringBuilder(pattern); + return (sb != null) ? sb.ToString() : pattern; + } + + internal static StringBuilder EscapeToStringBuilder(string/*!*/ pattern) { + int first = 0; + char escaped; + int i = SkipNonSpecial(pattern, 0, out escaped); + + if (i == -1) { + return null; + } + + StringBuilder result = new StringBuilder(pattern.Length + 1); + + do { + Debug.Assert(i < pattern.Length); + // pattern[i] needs escape + + result.Append(pattern, first, i - first); + result.Append('\\'); + result.Append(escaped); + i++; + + Debug.Assert(i <= pattern.Length); + + first = i; + i = SkipNonSpecial(pattern, i, out escaped); + } while (i >= 0); + + result.Append(pattern, first, pattern.Length - first); + return result; + } + + private static int SkipWellEscaped(string/*!*/ pattern, int i) { + while (i < pattern.Length - 1) { + if (pattern[i] == '\\') { + switch (pattern[i + 1]) { + // metacharacters: + case '$': + case '^': + case '|': + case '[': + case ']': + case '(': + case ')': + case '\\': + case '.': + case '#': + case '-': + + case '{': + case '}': + case '*': + case '+': + case '?': + + case '0': // octal + case 't': + case 'v': + case 'n': + case 'r': + case 'f': + case 'a': + case 'e': // characters + + case 'b': // word boundary or backslash in character group + case 'B': // not word boundary + case 'A': // beginning of string + case 'Z': // end of string, or before newline at the end + case 'z': // end of string + case 'G': + + case 'd': + case 'D': + case 's': + case 'S': + case 'w': // word character + case 'W': // non word char + // TODO: may need non-Unicode adjustment + + case 'C': // control characters + case 'M': // meta characters + // TODO: replace + + // Oniguruma + .NET - character classes, they don't match so some fixups would be needed + // MRI: doesn't support, but is also an error since it is followed by {name}, which is illegal + case 'p': + case 'P': + // keep + break; + + default: + return i; + } + i += 2; + } else { + i += 1; + } + } + return -1; + } + + // fixes escapes + // - unescapes non-special characters + // - fixes \xF -> \x0F + internal static string/*!*/ TransformPattern(string/*!*/ pattern, RubyRegexOptions options) { + + int first = 0; + int i = SkipWellEscaped(pattern, 0); + + // trailing backslash is an error in both MRI, .NET + if (i == -1) { + return pattern; + } + + StringBuilder result = new StringBuilder(pattern.Length); + + do { + Debug.Assert(i + 1 < pattern.Length); + Debug.Assert(pattern[i] == '\\'); + + result.Append(pattern, first, i - first); + i++; + + char c = pattern[i++]; + switch (c) { + case 'x': + result.Append('\\'); + result.Append('x'); + + // error: + if (i == pattern.Length) { + break; + } + + // fix single digit: + c = pattern[i++]; + if (i == pattern.Length || !Tokenizer.IsHexadecimalDigit(pattern[i])) { + result.Append('0'); + } + result.Append(c); + break; + + case 'h': // Oniguruma only: [0-9A-Fa-f] + case 'H': // Oniguruma only: [^0-9A-Fa-f] + case 'g': // Oniguruma only + case 'k': // Oniguruma, .NET: named backreference, MRI not supported + // remove backslash + + default: + if (Tokenizer.IsDecimalDigit(c)) { + // TODO: + // \([1-9][0-9]*) where there is no group of such number (replace by an empty string) + result.Append('\\'); + } + + // .NET throws invalid escape exception, remove backslash: + result.Append(c); + break; + } + Debug.Assert(i <= pattern.Length); + + first = i; + i = SkipWellEscaped(pattern, i); + } while (i >= 0); + + result.Append(pattern, first, pattern.Length - first); + return result.ToString(); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/Glob.cs b/merlin/main/languages/ruby/Ruby/Builtins/Glob.cs new file mode 100644 index 0000000000..2aa3a0fa83 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/Glob.cs @@ -0,0 +1,551 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Text; +using System.Text.RegularExpressions; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + public class Glob { + // Duplicated constants from File.Constants + private static class Constants { + public readonly static int FNM_CASEFOLD = 0x08; + public readonly static int FNM_DOTMATCH = 0x04; + public readonly static int FNM_NOESCAPE = 0x01; + public readonly static int FNM_PATHNAME = 0x02; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] // TODO + public readonly static int FNM_SYSCASE = 0x08; + } + + private class CharClass { + private readonly StringBuilder/*!*/ _chars = new StringBuilder(); + + internal void Add(char c) { + if (c == ']' || c == '\\') { + _chars.Append('\\'); + } + _chars.Append(c); + } + + internal string MakeString() { + if (_chars.Length == 0) { + return null; + } + if (_chars.Length == 1 && _chars[0] == '^') { + _chars.Insert(0, "\\"); + } + _chars.Insert(0, "["); + _chars.Append(']'); + return _chars.ToString(); + } + } + + private static void AppendExplicitRegexChar(StringBuilder/*!*/ builder, char c) { + builder.Append('['); + if (c == '^' || c == '\\') { + builder.Append('\\'); + } + builder.Append(c); + builder.Append(']'); + } + + internal static string/*!*/ PatternToRegex(string/*!*/ pattern, bool pathName, bool noEscape) { + StringBuilder result = new StringBuilder(pattern.Length); + result.Append("\\G"); + + bool inEscape = false; + CharClass charClass = null; + + foreach (char c in pattern) { + if (c == '\\' && !inEscape && !noEscape) { + inEscape = true; + continue; + } + if (inEscape) { + if (charClass != null) { + charClass.Add(c); + } else { + AppendExplicitRegexChar(result, c); + } + inEscape = false; + continue; + } + if (charClass != null) { + if (c == ']') { + string set = charClass.MakeString(); + if (set == null) { + // Ruby regex "[]" matches nothing + // CLR regex "[]" throws exception + return String.Empty; + } + result.Append(set); + charClass = null; + } else { + charClass.Add(c); + } + continue; + } + switch (c) { + case '*': + result.Append(pathName ? "[^/]*" : ".*"); + break; + + case '?': + result.Append('.'); + break; + + case '[': + charClass = new CharClass(); + break; + + default: + AppendExplicitRegexChar(result, c); + break; + } + } + + return (charClass == null) ? result.ToString() : String.Empty; + } + + public static bool FnMatch(MutableString/*!*/ pattern, MutableString/*!*/ path, int flags) { + if (pattern.IsEmpty) { + return path.IsEmpty; + } + + bool pathName = ((flags & Constants.FNM_PATHNAME) != 0); + bool noEscape = ((flags & Constants.FNM_NOESCAPE) != 0); + string sPath = path.ConvertToString(); + string regexPattern = PatternToRegex(pattern.ConvertToString(), pathName, noEscape); + if (regexPattern.Length == 0) { + return false; + } + + if (((flags & Constants.FNM_DOTMATCH) == 0) && sPath.Length > 0 && sPath[0] == '.') { + // Starting dot requires an explicit dot in the pattern + if (regexPattern.Length < 4 || regexPattern[2] != '[' || regexPattern[3] != '.') { + return false; + } + } + + RegexOptions options = RegexOptions.None; + if ((flags & Constants.FNM_CASEFOLD) != 0) { + options |= RegexOptions.IgnoreCase; + } + Match match = Regex.Match(sPath, regexPattern, options); + return match != null && match.Success && (match.Length == path.Length); + } + + private class GlobUngrouper { + internal abstract class GlobNode { + internal readonly GlobNode/*!*/ _parent; + protected GlobNode(GlobNode parentNode) { + _parent = parentNode ?? this; + } + abstract internal GlobNode/*!*/ AddChar(char c); + abstract internal GlobNode/*!*/ StartLevel(); + abstract internal GlobNode/*!*/ AddGroup(); + abstract internal GlobNode/*!*/ FinishLevel(); + abstract internal List/*!*/ Flatten(); + } + + internal class TextNode : GlobNode { + private readonly StringBuilder/*!*/ _builder; + + internal TextNode(GlobNode/*!*/ parentNode) + : base(parentNode) { + _builder = new StringBuilder(); + } + internal override GlobNode/*!*/ AddChar(char c) { + if (c != 0) { + _builder.Append(c); + } + return this; + } + internal override GlobNode/*!*/ StartLevel() { + return _parent.StartLevel(); + } + internal override GlobNode/*!*/ AddGroup() { + return _parent.AddGroup(); + } + internal override GlobNode/*!*/ FinishLevel() { + return _parent.FinishLevel(); + } + internal override List/*!*/ Flatten() { + List result = new List(1); + result.Add(_builder); + return result; + } + } + + internal class ChoiceNode : GlobNode { + private readonly List/*!*/ _nodes; + + internal ChoiceNode(GlobNode/*!*/ parentNode) + : base(parentNode) { + _nodes = new List(); + } + internal override GlobNode/*!*/ AddChar(char c) { + SequenceNode node = new SequenceNode(this); + _nodes.Add(node); + return node.AddChar(c); + } + internal override GlobNode/*!*/ StartLevel() { + SequenceNode node = new SequenceNode(this); + _nodes.Add(node); + return node.StartLevel(); + } + internal override GlobNode/*!*/ AddGroup() { + AddChar('\0'); + return this; + } + internal override GlobNode/*!*/ FinishLevel() { + AddChar('\0'); + return _parent; + } + internal override List/*!*/ Flatten() { + List result = new List(); + foreach (GlobNode node in _nodes) { + foreach (StringBuilder builder in node.Flatten()) { + result.Add(builder); + } + } + return result; + } + } + + internal class SequenceNode : GlobNode { + private readonly List/*!*/ _nodes; + + internal SequenceNode(GlobNode parentNode) + : base(parentNode) { + _nodes = new List(); + } + + internal override GlobNode/*!*/ AddChar(char c) { + TextNode node = new TextNode(this); + _nodes.Add(node); + return node.AddChar(c); + } + + internal override GlobNode/*!*/ StartLevel() { + ChoiceNode node = new ChoiceNode(this); + _nodes.Add(node); + return node; + } + + internal override GlobNode/*!*/ AddGroup() { + return _parent; + } + + internal override GlobNode/*!*/ FinishLevel() { + return _parent._parent; + } + + internal override List/*!*/ Flatten() { + List result = new List(); + result.Add(new StringBuilder()); + foreach (GlobNode node in _nodes) { + List tmp = new List(); + foreach (StringBuilder builder in node.Flatten()) { + foreach (StringBuilder sb in result) { + StringBuilder newsb = new StringBuilder(sb.ToString()); + newsb.Append(builder.ToString()); + tmp.Add(newsb); + } + } + result = tmp; + } + return result; + } + } + + private readonly SequenceNode/*!*/ _rootNode; + private GlobNode/*!*/ _currentNode; + private int _level; + + internal GlobUngrouper(int patternLength) { + _rootNode = new SequenceNode(null); + _currentNode = _rootNode; + _level = 0; + } + + internal void AddChar(char c) { + _currentNode = _currentNode.AddChar(c); + } + + internal void StartLevel() { + _currentNode = _currentNode.StartLevel(); + _level++; + } + + internal void AddGroup() { + _currentNode = _currentNode.AddGroup(); + } + + internal void FinishLevel() { + _currentNode = _currentNode.FinishLevel(); + _level--; + } + internal int Level { + get { return _level; } + } + internal string[]/*!*/ Flatten() { + if (_level != 0) { + return ArrayUtils.EmptyStrings; + } + List list = _rootNode.Flatten(); + string[] result = new string[list.Count]; + for (int i = 0; i < list.Count; i++) { + result[i] = list[i].ToString(); + } + return result; + } + } + + private static string[] UngroupGlobs(string/*!*/ pattern, bool noEscape) { + if (pattern.IndexOf('{') < 0) { + if (pattern.IndexOf('}') < 0) { + return new string[1] { pattern }; + } else { + return ArrayUtils.EmptyStrings; + } + } + + GlobUngrouper ungrouper = new GlobUngrouper(pattern.Length); + + bool inEscape = false; + foreach (char c in pattern) { + if (c == '\\' && !inEscape && !noEscape) { + inEscape = true; + continue; + } + if (inEscape) { + if (c != ',' && c != '{' && c != '{') { + ungrouper.AddChar('\\'); + } + ungrouper.AddChar(c); + inEscape = false; + continue; + } + switch (c) { + case '{': + ungrouper.StartLevel(); + break; + + case ',': + if (ungrouper.Level < 1) { + ungrouper.AddChar(c); + } else { + ungrouper.AddGroup(); + } + break; + + case '}': + if (ungrouper.Level < 1) { + // Unbalanced closing bracket matches nothing + return ArrayUtils.EmptyStrings; + } + ungrouper.FinishLevel(); + break; + + default: + ungrouper.AddChar(c); + break; + } + } + return ungrouper.Flatten(); + } + + private sealed class GlobMatcher { + readonly PlatformAdaptationLayer/*!*/ _pal; + readonly string/*!*/ _pattern; + readonly int _flags; + readonly bool _dirOnly; + readonly List/*!*/ _result; + bool _stripTwo; + + internal GlobMatcher(PlatformAdaptationLayer/*!*/ pal, string/*!*/ pattern, int flags) { + _pal = pal; + _pattern = (pattern == "**") ? "*" : pattern; + _flags = flags | Constants.FNM_CASEFOLD; + _result = new List(); + _dirOnly = (_pattern.Length > 0) && (_pattern[_pattern.Length - 1] == '/'); + _stripTwo = false; + } + + internal int FindNextSeparator(int position, bool allowWildcard, out bool containsWildcard) { + int lastSlash = -1; + bool inEscape = false; + containsWildcard = false; + for (int i = position; i < _pattern.Length; i++) { + if (inEscape) { + inEscape = false; + continue; + } + char c = _pattern[i]; + if (c == '\\') { + inEscape = true; + continue; + } else if (c == '*' || c == '?' || c == '[') { + if (!allowWildcard) { + return lastSlash + 1; + } else if (lastSlash >= 0) { + return lastSlash; + } + containsWildcard = true; + } else if (c == '/' || c == ':') { + if (containsWildcard) { + return i; + } + lastSlash = i; + } + } + return _pattern.Length; + } + + private void TestPath(string path, int patternEnd, bool isLastPathSegment) { + if (!isLastPathSegment) { + DoGlob(path, patternEnd, false); + return; + } + string pathName = path.Replace('\\', '/'); + if (_stripTwo) { + pathName = pathName.Substring(2); + } + if (_pal.DirectoryExists(pathName)) { + _result.Add(pathName); + } else if (!_dirOnly && _pal.FileExists(pathName)) { + _result.Add(pathName); + } + } + + internal IList/*!*/ DoGlob() { + if (_pattern.Length == 0) { + return ArrayUtils.EmptyStrings; + } + + int pos = 0; + string baseDirectory = "."; + if (_pattern[0] == '/' || _pattern.IndexOf(':') >= 0) { + bool containsWildcard; + pos = FindNextSeparator(0, false, out containsWildcard); + if (pos == _pattern.Length) { + TestPath(_pattern, pos, true); + return _result; + } + if (pos > 0 || _pattern[0] == '/') { + baseDirectory = _pattern.Substring(0, pos); + } + } + + _stripTwo = (baseDirectory == "."); + + DoGlob(baseDirectory, pos, false); + return _result; + } + + internal void DoGlob(string/*!*/ baseDirectory, int position, bool isPreviousDoubleStar) { + if (!_pal.DirectoryExists(baseDirectory)) { + return; + } + + bool containsWildcard; + int patternEnd = FindNextSeparator(position, true, out containsWildcard); + bool isLastPathSegment = (patternEnd == _pattern.Length); + string dirSegment = _pattern.Substring(position, patternEnd - position); + + if (!isLastPathSegment) { + patternEnd++; + } + + if (!containsWildcard) { + string path = baseDirectory + "/" + dirSegment; + TestPath(path, patternEnd, isLastPathSegment); + return; + } + + MutableString mPattern = MutableString.Create(dirSegment); + bool doubleStar = dirSegment.Equals("**"); + if (doubleStar && !isPreviousDoubleStar) { + DoGlob(baseDirectory, patternEnd, true); + } + + string[] files = Directory.GetFileSystemEntries(baseDirectory); + foreach (string file in files) { + string objectName = Path.GetFileName(file); + if (FnMatch(mPattern, MutableString.Create(objectName), _flags)) { + TestPath(file, patternEnd, isLastPathSegment); + if (doubleStar) { + DoGlob(file, position, true); + } + } + } + if (isLastPathSegment && (_flags & Constants.FNM_DOTMATCH) != 0 || mPattern.GetChar(0) == '.') { + if (FnMatch(mPattern, MutableString.Create("."), _flags)) { + string directory = baseDirectory + "/."; + if (_dirOnly) { + directory += '/'; + } + TestPath(directory, patternEnd, true); + } + if (FnMatch(mPattern, MutableString.Create(".."), _flags)) { + string directory = baseDirectory + "/.."; + if (_dirOnly) { + directory += '/'; + } + TestPath(directory, patternEnd, true); + } + } + } + } + + private static IList/*!*/ DoGlob(PlatformAdaptationLayer/*!*/ pal, string/*!*/ pattern, int flags) { + GlobMatcher matcher = new GlobMatcher(pal, pattern, flags); + return matcher.DoGlob(); + } + + public static MutableString/*!*/ CanonicalizePath(MutableString/*!*/ path) { + for (int i = 0; i < path.Length; i++) { + if (path.GetChar(i) == '\\') + path.SetChar(i, '/'); + } + return path; + } + + public static IEnumerable/*!*/ GlobResults(RubyContext/*!*/ context, string/*!*/ pattern, int flags) { + if (pattern.Length == 0) { + yield break; + } + bool noEscape = ((flags & Constants.FNM_NOESCAPE) != 0); + string sPattern = pattern; + string[] groups = UngroupGlobs(sPattern, noEscape); + if (groups.Length == 0) { + yield break; + } + + foreach (string group in groups) { + foreach (string filename in DoGlob(context.DomainManager.Platform, group, flags)) { + yield return filename; + } + } + } + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Ruby/Builtins/Hash.Subclass.cs b/merlin/main/languages/ruby/Ruby/Builtins/Hash.Subclass.cs new file mode 100644 index 0000000000..7ffe89419b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/Hash.Subclass.cs @@ -0,0 +1,56 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Generation; +using System.Collections.Generic; + +namespace IronRuby.Builtins { + public partial class Hash { + public sealed class Subclass : Hash, IRubyObject { + private readonly RubyClass/*!*/ _class; + private RubyInstanceData _instanceData; + + // called by Class#new rule when creating a Ruby subclass of String: + public Subclass(RubyClass/*!*/ rubyClass) + : base(rubyClass.Context) { + Assert.NotNull(rubyClass); + _class = rubyClass; + } + + protected override Hash/*!*/ CreateInstance() { + return new Subclass(_class); + } + + #region IRubyObject Members + + [Emitted] + public RubyClass/*!*/ Class { + get { return _class; } + } + + public RubyInstanceData/*!*/ GetInstanceData() { + return RubyOps.GetInstanceData(ref _instanceData); + } + + public RubyInstanceData TryGetInstanceData() { + return _instanceData; + } + + #endregion + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/Hash.cs b/merlin/main/languages/ruby/Ruby/Builtins/Hash.cs new file mode 100644 index 0000000000..a74d73dca4 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/Hash.cs @@ -0,0 +1,88 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + /// + /// Dictionary inherits from Object, mixes in Enumerable. + /// Ruby hash is a Dictionary{object, object}, but it adds default value/proc + /// + public partial class Hash : Dictionary, IDuplicable { + + // The default value can be a Proc that we should *return*, and that is different + // from the default value being a Proc that we should *call*, hence two variables + private Proc _defaultProc; + private object _defaultValue; + + public Proc DefaultProc { get { return _defaultProc; } set { _defaultProc = value; } } + public object DefaultValue { get { return _defaultValue; } set { _defaultValue = value; } } + + #region Construction + + public Hash(RubyContext/*!*/ context) + : base(context.EqualityComparer) { + } + + public Hash(IEqualityComparer/*!*/ comparer) + : base(comparer) { + } + + public Hash(EqualityComparer/*!*/ comparer, Proc defaultProc, object defaultValue) + : base(comparer) { + _defaultValue = defaultValue; + _defaultProc = defaultProc; + } + + public Hash(EqualityComparer/*!*/ comparer, int capacity) + : base(capacity, comparer) { + } + + public Hash(IDictionary/*!*/ dictionary) + : base(dictionary) { + } + + public Hash(IDictionary/*!*/ dictionary, EqualityComparer/*!*/ comparer) + : base(dictionary, comparer) { + } + + public Hash(Hash/*!*/ hash) + : base(hash, hash.Comparer) { + _defaultProc = hash._defaultProc; + _defaultValue = hash.DefaultValue; + } + + /// + /// Creates an empty instance. + /// Doesn't copy instance data. + /// Preserves the class of the Array. + /// + protected virtual Hash/*!*/ CreateInstance() { + return new Hash(Comparer); + } + + object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) { + var result = CreateInstance(); + context.CopyInstanceData(this, result, copySingletonMembers); + return result; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/IO.cs b/merlin/main/languages/ruby/Ruby/Builtins/IO.cs new file mode 100644 index 0000000000..0ec9eff2fc --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/IO.cs @@ -0,0 +1,534 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Threading; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using System.Text; + +namespace IronRuby.Builtins { + public enum IOMode { + ReadOnlyFromStart, + ReadWriteFromStart, + WriteOnlyTruncate, + ReadWriteTruncate, + WriteOnlyAppend, + ReadWriteAppend, + Closed, + } + + /// + /// IO builtin class. Wraps a BCL Stream object. Implementation of Ruby methods is in IoOps.cs in IronRuby.Libraries assembly. + /// + public class RubyIO : IDisposable { + private Encoding/*!*/ _externalEncoding; + private Encoding _internalEncoding; + + private Stream _stream; + private bool _preserveEndOfLines; + + private IOMode _mode; + private int _fileDescriptor; + private bool _disposed; + private bool _closed; + private bool _autoFlush; + private int _peekAhead; + + public const int SEEK_SET = 0; + public const int SEEK_CUR = 1; + public const int SEEK_END = 2; + + #region Construction + + protected RubyIO() { + _disposed = false; + _closed = false; + _peekAhead = -1; + _stream = Stream.Null; + + // TODO: enable setting + _externalEncoding = BinaryEncoding.Instance; + _internalEncoding = null; + } + + public RubyIO(RubyContext/*!*/ context) + : this() { + ContractUtils.RequiresNotNull(context, "context"); + _fileDescriptor = context.AddDescriptor(this); + } + + public RubyIO(RubyContext/*!*/ context, Stream/*!*/ stream, string/*!*/ modeString) + : this(context) { + ContractUtils.RequiresNotNull(stream, "stream"); + ContractUtils.RequiresNotNull(modeString, "modeString"); + + _mode = ParseIOMode(modeString, out _preserveEndOfLines); + _stream = stream; + + ResetLineNumbersForReadOnlyFiles(context); + } + + // TODO: hack + public RubyIO(RubyContext/*!*/ context, StreamReader reader, StreamWriter writer, string/*!*/ modeString) + : this(context) { + _mode = ParseIOMode(modeString, out _preserveEndOfLines); + _stream = new DuplexStream(reader, writer); + + ResetLineNumbersForReadOnlyFiles(context); + } + + + public RubyIO(RubyContext/*!*/ context, Stream/*!*/ stream, RubyFileMode mode) + : this(context) { + ContractUtils.RequiresNotNull(stream, "stream"); + + _mode = ParseIOMode(mode, out _preserveEndOfLines); + _stream = stream; + + ResetLineNumbersForReadOnlyFiles(context); + } + + private void ResetLineNumbersForReadOnlyFiles(RubyContext/*!*/ context) { + if (_mode == IOMode.ReadOnlyFromStart || _mode == IOMode.ReadWriteAppend || _mode == IOMode.ReadWriteFromStart || _mode == IOMode.ReadWriteTruncate) { + context.InputProvider.LastInputLineNumber = 0; + } + } + + #endregion + + public Encoding ExternalEncoding { + get { return _externalEncoding; } + } + + public Encoding InternalEncoding { + get { return _internalEncoding; } + } + + public int FileDescriptor { + get { return _fileDescriptor; } + } + + public IOMode Mode { + get { return _mode; } + } + + protected Stream/*!*/ Stream { + get { + if (_stream == null) { + throw RubyExceptions.CreateIOError("uninitialized stream"); + } + + return _stream; + } + } + + public bool IsConsole { + get { return _stream is ConsoleStream; } + } + + internal static bool IsConsoleDescriptor(int fileDescriptor) { + return (fileDescriptor < 3); + } + + public bool IsConsoleDescriptor() { + return IsConsoleDescriptor(_fileDescriptor); + } + + public bool Closed { + get { return _closed; } + set { _closed = value; } + } + + public bool PreserveEndOfLines { + get { return _preserveEndOfLines; } + set { _preserveEndOfLines = value; } + } + + #region Mode + + public static IOMode ParseIOMode(RubyFileMode mode, out bool preserveEndOfLines) { + preserveEndOfLines = ((mode & RubyFileMode.BINARY) != 0); + + IOMode io; + RubyFileMode readWriteMode = mode & RubyFileMode.ReadWriteMask; + + if (readWriteMode == RubyFileMode.WRONLY) { + io = ((mode & RubyFileMode.APPEND) != 0) ? IOMode.WriteOnlyAppend : IOMode.WriteOnlyTruncate; + } else if (readWriteMode == RubyFileMode.RDONLY) { + io = ((mode & RubyFileMode.APPEND) != 0) ? IOMode.ReadWriteFromStart : IOMode.ReadOnlyFromStart; + } else if (readWriteMode == RubyFileMode.RDWR) { + io = ((mode & RubyFileMode.APPEND) != 0) ? IOMode.ReadWriteAppend : IOMode.ReadWriteFromStart; + } else { + throw new ArgumentException("file mode must be one of WRONLY, RDONLY, RDWR"); + } + + return io; + } + + public static IOMode ParseIOMode(string/*!*/ mode, out bool preserveEndOfLines) { + int i = mode.Length - 1; + if (i < 0) { + // empty: + preserveEndOfLines = false; + return IOMode.ReadOnlyFromStart; + } + + bool plus = (mode[i] == '+'); + if (plus) { + i--; + } + + if (i < 0) { + throw IllegalMode(mode); + } + + preserveEndOfLines = (mode[i] == 'b'); + if (preserveEndOfLines) { + i--; + } + + if (i != 0) { + throw IllegalMode(mode); + } + + switch (mode[0]) { + case 'r': + return plus ? IOMode.ReadWriteFromStart : IOMode.ReadOnlyFromStart; + + case 'w': + return plus ? IOMode.ReadWriteTruncate : IOMode.WriteOnlyTruncate; + + case 'a': + return plus ? IOMode.ReadWriteAppend : IOMode.WriteOnlyAppend; + + default: + throw IllegalMode(mode); + } + } + + internal static ArgumentException/*!*/ IllegalMode(string modeString) { + return new ArgumentException(String.Format("illegal access mode {0}", modeString)); + } + + #endregion + + #region IDisposable Members + + protected virtual void Dispose(bool disposing) { + if (!_disposed) { + if (disposing) { + if (_stream != null) { + _stream.Dispose(); + _stream = null; + } + } + _disposed = true; + } + } + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~RubyIO() { + Dispose(false); + } + + #endregion + + #region Instance methods + + public virtual WaitHandle/*!*/ CreateReadWaitHandle() { + // TODO: + throw new NotSupportedException(); + } + + public virtual WaitHandle/*!*/ CreateWriteWaitHandle() { + // TODO: + throw new NotSupportedException(); + } + + public virtual WaitHandle/*!*/ CreateErrorWaitHandle() { + // TODO: + throw new NotSupportedException(); + } + + public virtual int FileControl(int commandId, int arg) { + // TODO: + throw new NotSupportedException(); + } + + public virtual int FileControl(int commandId, byte[] arg) { + // TODO: + throw new NotSupportedException(); + } + + public void ResetIOMode(string/*!*/ modeString) { + _mode = ParseIOMode(modeString, out _preserveEndOfLines); + } + + public long Position { + get { + Assert.NotNull(_stream); + return _stream.Position; + } + } + + public bool AutoFlush { + get { return _autoFlush; } + set { _autoFlush = value; } + } + + public void AssertNotClosed() { + if (_closed) { + throw RubyExceptions.CreateIOError("closed stream"); + } + } + + public void AssertOpenedForWriting() { + AssertNotClosed(); + if (_mode == IOMode.ReadOnlyFromStart) { + throw RubyExceptions.CreateIOError("not opened for writing"); + } + } + + public void AssertOpenedForReading() { + AssertNotClosed(); + if (_mode == IOMode.WriteOnlyAppend || _mode == IOMode.WriteOnlyTruncate) { + throw RubyExceptions.CreateIOError("not opened for reading"); + } + } + + public BinaryReader/*!*/ GetBinaryReader() { + AssertOpenedForReading(); + return new BinaryReader(_stream); + } + + public BinaryWriter/*!*/ GetBinaryWriter() { + AssertOpenedForWriting(); + return new BinaryWriter(_stream); + } + + public bool IsEndOfStream() { + return PeekByte() == -1; + } + + public void Close() { + if (_stream != null) { + _stream.Close(); + } + _closed = true; + } + + // TODO: + public void CloseWriter() { + var duplex = _stream as DuplexStream; + if (duplex == null) { + throw RubyExceptions.CreateIOError("closing non-duplex IO for writing"); + } + duplex.Writer.Close(); + } + + // TODO: + public void CloseReader() { + var duplex = _stream as DuplexStream; + if (duplex == null) { + throw RubyExceptions.CreateIOError("closing non-duplex IO for reading"); + } + duplex.Reader.Close(); + } + + public long Seek(long offset, SeekOrigin origin) { + return _stream.Seek(offset, origin); + } + + public void Flush() { + _stream.Flush(); + } + + public long Length { + get { return _stream.Length; } + } + + // returns the number of bytes written to the stream: + public int Write(char[]/*!*/ buffer, int index, int count) { + byte[] bytes = _externalEncoding.GetBytes(buffer, index, count); + Write(bytes, 0, bytes.Length); + return bytes.Length; + } + + // returns the number of bytes written to the stream: + public int Write(string/*!*/ value) { + byte[] bytes = _externalEncoding.GetBytes(value); + Write(bytes, 0, bytes.Length); + return bytes.Length; + } + + // returns the number of bytes written to the stream: + public int Write(MutableString/*!*/ value) { + byte[] bytes = value.ToByteArray(); + return Write(bytes, 0, bytes.Length); + } + + public int Write(byte[]/*!*/ buffer, int index, int count) { + if (_preserveEndOfLines) { + _stream.Write(buffer, index, count); + return buffer.Length; + } else { + int bytesWritten = 0; + int i = index; + while (i < count) { + int j = i; + while (j < buffer.Length && buffer[j] != (byte)'\n') { + j++; + } + _stream.Write(buffer, i, j - i); + bytesWritten += j - i; + + if (j < buffer.Length) { + _stream.WriteByte((byte)'\r'); + _stream.WriteByte((byte)'\n'); + bytesWritten += 2; + } + + i = j + 1; + } + + return bytesWritten; + } + } + + public int PeekByte() { + int result; + if (_peekAhead != -1) { + result = _peekAhead; + } else if (!_stream.CanSeek) { + result = _stream.ReadByte(); + _peekAhead = result; + } else { + long pos = _stream.Position; + result = _stream.ReadByte(); + _stream.Position = pos; + } + return result; + } + + public int ReadByte() { + if (_peekAhead != -1) { + int result = _peekAhead; + _peekAhead = -1; + return result; + } + return _stream.ReadByte(); + } + + public int ReadBytes(byte[] buffer, int offset, int count) { + return _stream.Read(buffer, offset, count); + } + + public int ReadByteNormalizeEoln() { + // TODO: encoding + if (IsEndOfStream()) { + return -1; + } + int first = ReadByte(); + if (first == '\r' && !_preserveEndOfLines) { + int second = PeekByte(); + if (second != -1 && second == '\n') { + return ReadByte(); + } + } + + return first; + } + + public int PeekByteNormalizeEoln() { + // TODO: encoding + long position = _stream.Position; + + int first = PeekByte(); + if (first == -1) { + return -1; + } + + if (first == '\r' && !_preserveEndOfLines) { + first = ReadByte(); + int second = PeekByte(); + if (second == '\n') { + return second; + } + + _stream.Position = position; + } + + return first; + } + + public MutableString ReadLineOrParagraph(MutableString separator) { + if (separator != null && separator.Length == 0) { + return ReadParagraph(); + } else { + return ReadLine(separator); + } + } + + public MutableString ReadLine(MutableString separator) { + AssertOpenedForReading(); + + int c = ReadByteNormalizeEoln(); + if (c == -1) { + return null; + } + + int separatorOffset = 0; + MutableString result = MutableString.CreateMutable(); + + do { + result.Append((char)c); + + if (separator != null && c == separator.GetChar(separatorOffset)) { + if (separatorOffset == separator.Length - 1) { + break; + } + separatorOffset++; + } else if (separatorOffset > 0) { + separatorOffset = 0; + } + + c = ReadByteNormalizeEoln(); + } while (c != -1); + + return result; + } + + public MutableString ReadParagraph() { + var result = ReadLine(MutableString.Create("\n\n")); + + int c; + while ((c = PeekByteNormalizeEoln()) != -1) { + if (c != '\n') break; + ReadByteNormalizeEoln(); + } + + return result; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/Initializer.cs b/merlin/main/languages/ruby/Ruby/Builtins/Initializer.cs new file mode 100644 index 0000000000..1623c1fc4c --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/Initializer.cs @@ -0,0 +1,126 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + public abstract class LibraryInitializer { + private RubyContext _context; + private bool _builtin; + + protected RubyContext/*!*/ Context { + get { return _context; } + } + + protected LibraryInitializer() { + } + + internal void LoadModules(RubyContext/*!*/ context, bool builtin) { + Assert.NotNull(context); + _context = context; + _builtin = builtin; + LoadModules(); + } + + protected RubyClass/*!*/ DefineGlobalClass(string/*!*/ name, Type/*!*/ type, RubyClass/*!*/ super, + Action instanceTrait, Action classTrait, RubyModule[]/*!*/ mixins, Delegate[] factories) { + + RubyClass result = _context.DefineLibraryClass(name, type, instanceTrait, classTrait, super, mixins, factories, _builtin); + _context.ObjectClass.SetConstant(result.Name, result); + return result; + } + + protected RubyClass/*!*/ DefineClass(string/*!*/ name, Type/*!*/ type, RubyClass/*!*/ super, + Action instanceTrait, Action classTrait, RubyModule[]/*!*/ mixins, Delegate[] factories) { + return _context.DefineLibraryClass(name, type, instanceTrait, classTrait, super, mixins, factories, _builtin); + } + + protected RubyClass/*!*/ ExtendClass(Type/*!*/ type, Action instanceTrait, Action classTrait, + RubyModule[]/*!*/ mixins, Delegate[] factories) { + return _context.DefineLibraryClass(null, type, instanceTrait, classTrait, null, mixins, factories, _builtin); + } + + protected RubyModule/*!*/ DefineGlobalModule(string/*!*/ name, Type/*!*/ type, Action instanceTrait, + Action classTrait, RubyModule[]/*!*/ mixins) { + RubyModule module = _context.DefineLibraryModule(name, type, instanceTrait, classTrait, mixins); + _context.ObjectClass.SetConstant(module.Name, module); + return module; + } + + // + // - Ruby module definitions: + // "type" parameter specifies the primary type that identifies the module. + // If a library extends existing Ruby module it uses its primary type in Extends attribute parameter. + // E.g. Ruby.Builtins.Kernel is primary type for Kernel module, libraries maight add more methods by implementing + // types marked by [RubyModule(Extends = typeof(Ruby.Builtins.Kernel)] + // - Interface modules: + // "type" parameter specifies the CLR interface being extended. + // + protected RubyModule/*!*/ DefineModule(string/*!*/ name, Type/*!*/ type, Action instanceTrait, + Action classTrait, RubyModule[]/*!*/ mixins) { + return _context.DefineLibraryModule(name, type, instanceTrait, classTrait, mixins); + } + + protected RubyModule/*!*/ ExtendModule(Type/*!*/ type, Action instanceTrait, Action classTrait, + RubyModule[]/*!*/ mixins) { + return _context.DefineLibraryModule(null, type, instanceTrait, classTrait, mixins); + } + + protected object/*!*/ DefineSingleton(Action instanceTrait, Action classTrait, RubyModule[]/*!*/ mixins) { + Assert.NotNullItems(mixins); + Debug.Assert(_context.ObjectClass != null); + + object result = new object(); + RubyClass singleton = _context.CreateInstanceSingleton(result, instanceTrait, classTrait); + singleton.SetMixins(mixins); + + return result; + } + + // TODO: dynamic library loading? + protected RubyClass/*!*/ GetClass(Type/*!*/ type) { + Debug.Assert(type != null && !type.IsInterface); + return _context.GetOrCreateClass(type); + } + + // TODO: dynamic library loading? + protected RubyModule/*!*/ GetModule(Type/*!*/ type) { + Debug.Assert(type != null && type.IsInterface); + throw new NotImplementedException("TODO"); + // return _context.GetOrCreateInterfaceModule(type); + } + + protected virtual void LoadModules() { + throw new NotImplementedException(); + } + + public static string/*!*/ GetTypeName(string/*!*/ libraryNamespace) { + ContractUtils.RequiresNotNull(libraryNamespace, "libraryNamespace"); + return libraryNamespace.Substring(libraryNamespace.LastIndexOf(Type.Delimiter) + 1) + "LibraryInitializer"; + } + + public static string/*!*/ GetFullTypeName(string/*!*/ libraryNamespace) { + return libraryNamespace + Type.Delimiter + GetTypeName(libraryNamespace); + } + + public static string/*!*/ GetBuiltinsFullTypeName() { + return GetFullTypeName(typeof(RubyClass).Namespace); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/Kernel.cs b/merlin/main/languages/ruby/Ruby/Builtins/Kernel.cs new file mode 100644 index 0000000000..70fc3f403e --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/Kernel.cs @@ -0,0 +1,20 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Builtins { + public static class Kernel { + // stub + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/MatchData.Subclass.cs b/merlin/main/languages/ruby/Ruby/Builtins/MatchData.Subclass.cs new file mode 100644 index 0000000000..31a04120a9 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/MatchData.Subclass.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Builtins { + public partial class MatchData { + public sealed class Subclass : MatchData, IRubyObject { + private readonly RubyClass/*!*/ _class; + private RubyInstanceData _instanceData; + + // called by Class#new rule when creating a Ruby subclass of MatchData: + public Subclass(RubyClass/*!*/ rubyClass) { + Assert.NotNull(rubyClass); + _class = rubyClass; + } + + protected override MatchData/*!*/ CreateInstance() { + return new Subclass(_class); + } + + #region IRubyObject Members + + [Emitted] + public RubyClass/*!*/ Class { + get { return _class; } + } + + public RubyInstanceData/*!*/ GetInstanceData() { + return RubyOps.GetInstanceData(ref _instanceData); + } + + public RubyInstanceData TryGetInstanceData() { + return _instanceData; + } + + #endregion + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/MatchData.cs b/merlin/main/languages/ruby/Ruby/Builtins/MatchData.cs new file mode 100644 index 0000000000..4f1ad420be --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/MatchData.cs @@ -0,0 +1,82 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting.Utils; +using System.Text.RegularExpressions; +using IronRuby.Runtime; +using System; + +namespace IronRuby.Builtins { + // TODO: taint from OriginalString, copy original string? + public partial class MatchData : IDuplicable { + private Match/*!*/ _match; + private MutableString/*!*/ _originalString; + + public Match/*!*/ Match { get { return _match; } } + public MutableString/*!*/ OriginalString { get { return _originalString; } } + public bool Success { get { return _match.Success; } } + public GroupCollection Groups { get { return _match.Groups; } } + public int Index { get { return _match.Index; } } + public int Length { get { return _match.Length; } } + public string Value { get { return _match.Value; } } + + #region Construction + + public MatchData(Match/*!*/ match, MutableString/*!*/ originalString) { + ContractUtils.RequiresNotNull(match, "regex"); + ContractUtils.RequiresNotNull(originalString, "originalString"); + _match = match; + _originalString = originalString; + } + + public MatchData() { + _originalString = MutableString.Empty; + _match = Match.Empty; + } + + protected MatchData(MatchData/*!*/ data) + : this(data._match, data._originalString) { + } + + object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) { + var result = CreateInstance(); + context.CopyInstanceData(this, result, copySingletonMembers); + return result; + } + + protected virtual MatchData/*!*/ CreateInstance() { + return new MatchData(); + } + + public void InitializeFrom(MatchData/*!*/ other) { + _match = other._match; + _originalString = other._originalString; + } + + #endregion + + public MutableString GetGroupValue(RubyContext/*!*/ context, int index) { + var group = Groups[index]; + return group.Success ? MutableString.Create(group.Value).TaintBy(this, context) : null; + } + + public Group/*!*/ GetExistingGroup(int index) { + if (index >= Groups.Count || index < 0) { + throw RubyExceptions.CreateIndexError(String.Format("index {0} out of matches", index)); + } + return Groups[index]; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/MutableString.BinaryContent.cs b/merlin/main/languages/ruby/Ruby/Builtins/MutableString.BinaryContent.cs new file mode 100644 index 0000000000..2141f43907 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/MutableString.BinaryContent.cs @@ -0,0 +1,416 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting.Utils; +using System.Text; +using IronRuby.Compiler; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + public partial class MutableString { + [Serializable] + private class BinaryContent : Content { + // TODO: replace by resizable byte[] + // List is not efficient + private readonly List _data; + + internal BinaryContent(MutableString/*!*/ owner, List/*!*/ data) + : base(owner) { + Assert.NotNull(data); + _data = data; + } + + //private Content/*!*/ SwitchToText() { + // return SwitchToStringBuilder(0); + //} + + private StringBuilderContent/*!*/ SwitchToStringBuilder() { + return SwitchToStringBuilder(0); + } + + private StringBuilderContent/*!*/ SwitchToStringBuilder(int additionalCapacity) { + string data = DataToString(); + return WrapContent(new StringBuilder(data, data.Length + additionalCapacity)); + } + + #region Data Operations + + private string/*!*/ DataToString() { + byte[] bytes = _data.ToArray(); + return _owner._encoding.GetString(bytes, 0, bytes.Length); + } + + public int DataCompareTo(byte[]/*!*/ other) { + int min = _data.Count, defaultResult; + if (min < other.Length) { + defaultResult = -1; + } else if (min > other.Length) { + min = other.Length; + defaultResult = +1; + } else { + defaultResult = 0; + } + + for (int i = 0; i < min; i++) { + if (_data[i] != other[i]) { + return (int)_data[i] - other[i]; + } + } + + return defaultResult; + } + + public byte DataGetByte(int index) { + return _data[index]; + } + + public BinaryContent/*!*/ DataSetByte(int index, byte b) { + _data[index] = b; + return this; + } + + public byte[]/*!*/ DataGetSlice(int start, int count) { + byte[] range = new byte[count]; + Buffer.BlockCopy(_data.ToArray(), start, range, 0, count); + return range; + } + + public BinaryContent/*!*/ DataAppend(byte b, int repeatCount) { + _data.Add(b); + return this; + } + + public BinaryContent/*!*/ DataAppend(byte[]/*!*/ bytes, int start, int count) { + for (int i = 0; i < count; i++) { + _data.Add(bytes[start + i]); + } + return this; + } + + public BinaryContent/*!*/ DataInsert(int index, byte b) { + _data.Insert(index, b); + return this; + } + + public BinaryContent/*!*/ DataInsert(int index, byte[]/*!*/ bytes, int start, int count) { + _data.InsertRange(index, bytes); + return this; + } + + // TODO: range checking and throw appropriate exceptions + + public int DataIndexOf(byte b, int start, int count) { + for (int i = start; i < start + count; i++) { + if (_data[i] == b) { + return i; + } + } + return -1; + } + + public int DataIndexOf(byte[]/*!*/ bytes, int start, int count) { + // TODO: + for (int i = start; i < start + count - bytes.Length + 1; i++) { + bool match = true; + for (int j = 0; j < bytes.Length; j++) { + if (bytes[j] != _data[i + j]) { + match = false; + break; + } + } + + if (match) { + return i; + } + } + return -1; + } + + public int DataLastIndexOf(byte b, int start, int count) { + int finish = start - count < 0 ? 0 : start - count; + for (int i = start; i >= finish; --i) { + if (_data[i] == b) { + return i; + } + } + return -1; + } + + public int DataLastIndexOf(byte[]/*!*/ bytes, int start, int count) { + // TODO: + int finish = start - count < 0 ? bytes.Length - 1 : start - count + bytes.Length; + //for (int i = start; i < start + count - bytes.Length + 1; i++) { + for (int i = start; i >= finish; --i) { + bool match = true; + for (int j = 0; j < bytes.Length; j++) { + if (bytes[j] != _data[i + j]) { + match = false; + break; + } + } + + if (match) { + return i; + } + } + return -1; + } + + #endregion + + #region GetHashCode, Length, Clone (read-only) + + public override bool IsBinary { + get { return true; } + } + + public override int GetHashCode() { + // TODO: we need the same hash as if this was string. could be optimized. + return SwitchToStringBuilder().GetHashCode(); + } + + public int DataLength { + get { return _data.Count; } + } + + public override int Length { + get { return _data.Count; } + } + + public override bool IsEmpty { + get { return _data.Count == 0; } + } + + public override int GetCharCount() { + return (_data.Count > 0) ? SwitchToStringBuilder().DataLength : 0; + } + + public override int GetByteCount() { + return _data.Count; + } + + public override Content/*!*/ Clone(MutableString/*!*/ newOwner) { + return new BinaryContent(newOwner, new List(_data)); + } + + public override void GetDebugView(out string/*!*/ value, out string/*!*/ type) { + value = DataToString(); + type = "String (binary)"; + } + + #endregion + + #region Conversions (read-only) + + public override string/*!*/ ConvertToString() { + var builder = SwitchToStringBuilder(); + return builder.DataGetSlice(0, builder.DataLength); + } + + public override byte[]/*!*/ ConvertToBytes() { + return DataGetSlice(0, _data.Count); + } + + public override string/*!*/ ToString() { + return DataToString(); + } + + public override byte[]/*!*/ ToByteArray() { + return _data.ToArray(); + } + + public override GenericRegex/*!*/ ToRegularExpression(RubyRegexOptions options) { + // TODO: Fix BinaryRegex and use instead + return new StringRegex(ToString(), options); + } + + public override Content/*!*/ EscapeRegularExpression() { + return new BinaryContent(_owner, new List(BinaryRegex.Escape(_data.ToArray()))); + } + + #endregion + + #region CompareTo (read-only) + + public override int CompareTo(string/*!*/ str) { + return SwitchToStringBuilder().DataCompareTo(str); + } + + public override int CompareTo(byte[]/*!*/ bytes) { + return DataCompareTo(bytes); + } + + public override int ReverseCompareTo(Content/*!*/ str) { + return str.CompareTo(_data.ToArray()); + } + + #endregion + + #region Slices (read-only) + + public override char GetChar(int index) { + return SwitchToStringBuilder().DataGetChar(index); + } + + public override byte GetByte(int index) { + return DataGetByte(index); + } + + public override char PeekChar(int index) { + int bc = _owner._encoding.GetMaxByteCount(1); + if (_data.Count < bc) bc = _data.Count; + return _owner._encoding.GetChars(_data.ToArray(), index, bc)[0]; + } + + public override byte PeekByte(int index) { + return _data[index]; + } + + public override string/*!*/ GetStringSlice(int start, int count) { + return SwitchToStringBuilder().DataGetSlice(start, count); + } + + public override byte[]/*!*/ GetBinarySlice(int start, int count) { + return DataGetSlice(start, count); + } + + public override Content/*!*/ GetSlice(int start, int count) { + return new BinaryContent(_owner, new List(DataGetSlice(start, count))); + } + + #endregion + + #region IndexOf (read-only) + + public override int IndexOf(char c, int start, int count) { + return SwitchToStringBuilder().DataIndexOf(c, start, count); + } + + public override int IndexOf(byte b, int start, int count) { + return DataIndexOf(b, start, count); + } + + public override int IndexOf(string/*!*/ str, int start, int count) { + return SwitchToStringBuilder().DataIndexOf(str, start, count); + } + + public override int IndexOf(byte[]/*!*/ bytes, int start, int count) { + return DataIndexOf(bytes, start, count); + } + + public override int IndexIn(Content/*!*/ str, int start, int count) { + return str.IndexOf(_data.ToArray(), start, count); + } + + #endregion + + #region LastIndexOf (read-only) + + public override int LastIndexOf(char c, int start, int count) { + return SwitchToStringBuilder().DataLastIndexOf(c, start, count); + } + + public override int LastIndexOf(byte b, int start, int count) { + return DataLastIndexOf(b, start, count); + } + + public override int LastIndexOf(string/*!*/ str, int start, int count) { + return SwitchToStringBuilder().DataLastIndexOf(str, start, count); + } + + public override int LastIndexOf(byte[]/*!*/ bytes, int start, int count) { + return DataLastIndexOf(bytes, start, count); + } + + public override int LastIndexIn(Content/*!*/ str, int start, int count) { + return str.LastIndexOf(_data.ToArray(), start, count); + } + + #endregion + + + #region Append + + public override Content/*!*/ Append(char c, int repeatCount) { + return SwitchToStringBuilder(repeatCount).DataAppend(c, repeatCount); + } + + public override Content/*!*/ Append(byte b, int repeatCount) { + return DataAppend(b, repeatCount); + } + + public override Content/*!*/ Append(string/*!*/ str, int start, int count) { + return SwitchToStringBuilder(count).DataAppend(str, start, count); + } + + public override Content/*!*/ Append(byte[]/*!*/ bytes, int start, int count) { + return DataAppend(bytes, start, count); + } + + public override Content/*!*/ AppendFormat(IFormatProvider provider, string/*!*/ format, object[]/*!*/ args) { + return SwitchToStringBuilder().DataAppendFormat(provider, format, args); + } + + public override Content/*!*/ AppendTo(Content/*!*/ str, int start, int count) { + return str.Append(_data.ToArray(), start, count); + } + + #endregion + + #region Insert + + public override Content/*!*/ Insert(int index, char c) { + return SwitchToStringBuilder(1).DataInsert(index, c); + } + + public override Content/*!*/ Insert(int index, byte b) { + return DataInsert(index, b); + } + + public override Content/*!*/ Insert(int index, string/*!*/ str, int start, int count) { + return SwitchToStringBuilder(count).DataInsert(index, str, start, count); + } + + public override Content/*!*/ Insert(int index, byte[]/*!*/ bytes, int start, int count) { + return DataInsert(index, bytes, start, count); + } + + public override Content/*!*/ InsertTo(Content/*!*/ str, int index, int start, int count) { + return str.Insert(index, _data.ToArray(), start, count); + } + + public override Content/*!*/ SetItem(int index, byte b) { + return DataSetByte(index, b); + } + + public override Content/*!*/ SetItem(int index, char c) { + return SwitchToStringBuilder().DataSetChar(index, c); + } + + #endregion + + #region Remove + + public override Content/*!*/ Remove(int start, int count) { + _data.RemoveRange(start, count); + return this; + } + + #endregion + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/MutableString.Content.cs b/merlin/main/languages/ruby/Ruby/Builtins/MutableString.Content.cs new file mode 100644 index 0000000000..a2a45f5d3f --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/MutableString.Content.cs @@ -0,0 +1,119 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting.Utils; +using System.Text; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + public partial class MutableString { + [Serializable] + private abstract class Content { + protected MutableString/*!*/ _owner; + + #region Utils + + internal void SetOwner(MutableString/*!*/ owner) { + Assert.NotNull(owner); + _owner = owner; + } + + protected Content(MutableString/*!*/ owner) { + Assert.NotNull(owner); + _owner = owner; + } + + protected BinaryContent/*!*/ WrapContent(byte[]/*!*/ bytes) { + var result = new BinaryContent(_owner, new List(bytes)); // TODO: do not copy + _owner.SetContent(result); + return result; + } + + protected StringBuilderContent/*!*/ WrapContent(StringBuilder/*!*/ sb) { + var result = new StringBuilderContent(_owner, sb); + _owner.SetContent(result); + return result; + } + + #endregion + + public abstract string/*!*/ ConvertToString(); + public abstract byte[]/*!*/ ConvertToBytes(); + + public abstract byte[]/*!*/ ToByteArray(); + public abstract GenericRegex/*!*/ ToRegularExpression(RubyRegexOptions options); + + // returns self if there are no characters to be escaped: + public abstract Content/*!*/ EscapeRegularExpression(); + + // read: + public abstract bool IsBinary { get; } + public abstract bool IsEmpty { get; } + public abstract int Length { get; } + public abstract int GetCharCount(); + public abstract int GetByteCount(); + public abstract void GetDebugView(out string/*!*/ value, out string/*!*/ type); + public abstract Content/*!*/ Clone(MutableString/*!*/ newOwner); + + public abstract char GetChar(int index); + public abstract byte GetByte(int index); + public abstract byte PeekByte(int index); + public abstract char PeekChar(int index); + public abstract string/*!*/ GetStringSlice(int start, int count); + public abstract byte[]/*!*/ GetBinarySlice(int start, int count); + + public abstract int CompareTo(string/*!*/ str); + public abstract int CompareTo(byte[]/*!*/ bytes); + public abstract int ReverseCompareTo(Content/*!*/ str); + + // the owner of the result is the current owner: + public abstract Content/*!*/ GetSlice(int start, int count); + + public abstract int IndexOf(char c, int start, int count); + public abstract int IndexOf(byte b, int start, int count); + public abstract int IndexOf(string/*!*/ str, int start, int count); + public abstract int IndexOf(byte[]/*!*/ bytes, int start, int count); + public abstract int IndexIn(Content/*!*/ str, int start, int count); + + public abstract int LastIndexOf(char c, int start, int count); + public abstract int LastIndexOf(byte b, int start, int count); + public abstract int LastIndexOf(string/*!*/ str, int start, int count); + public abstract int LastIndexOf(byte[]/*!*/ bytes, int start, int count); + public abstract int LastIndexIn(Content/*!*/ str, int start, int count); + + // write: + public abstract Content/*!*/ Append(char c, int repeatCount); + public abstract Content/*!*/ Append(byte b, int repeatCount); + public abstract Content/*!*/ Append(string/*!*/ str, int start, int count); + public abstract Content/*!*/ Append(byte[]/*!*/ bytes, int start, int count); + public abstract Content/*!*/ AppendTo(Content/*!*/ str, int start, int count); + + public abstract Content/*!*/ AppendFormat(IFormatProvider provider, string/*!*/ format, object[]/*!*/ args); + + public abstract Content/*!*/ Insert(int index, char c); + public abstract Content/*!*/ Insert(int index, byte b); + public abstract Content/*!*/ Insert(int index, string/*!*/ str, int start, int count); + public abstract Content/*!*/ Insert(int index, byte[]/*!*/ bytes, int start, int count); + public abstract Content/*!*/ InsertTo(Content/*!*/ str, int index, int start, int count); + + public abstract Content/*!*/ SetItem(int index, byte b); + public abstract Content/*!*/ SetItem(int index, char c); + + public abstract Content/*!*/ Remove(int start, int count); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/MutableString.StringBuilderContent.cs b/merlin/main/languages/ruby/Ruby/Builtins/MutableString.StringBuilderContent.cs new file mode 100644 index 0000000000..5cdfe914cd --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/MutableString.StringBuilderContent.cs @@ -0,0 +1,385 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting.Utils; +using System.Text; + +namespace IronRuby.Builtins { + public partial class MutableString { + [Serializable] + private sealed class StringBuilderContent : Content { + private readonly StringBuilder/*!*/ _data; + + public StringBuilderContent(MutableString/*!*/ owner, StringBuilder/*!*/ data) + : base(owner) { + Assert.NotNull(data); + _data = data; + } + + private BinaryContent/*!*/ SwitchToBinary() { + return WrapContent(DataToBytes()); + } + + #region Data Operations + + public byte[] DataToBytes() { + return _data.Length > 0 ? _owner._encoding.GetBytes(_data.ToString()) : IronRuby.Runtime.Utils.Array.EmptyBytes; + } + + public char DataGetChar(int index) { + return _data[index]; + } + + public StringBuilderContent/*!*/ DataSetChar(int index, char c) { + _data[index] = c; + return this; + } + + public string/*!*/ DataGetSlice(int start, int count) { + return _data.ToString(start, count); + } + + // TODO: + public int DataCompareTo(string/*!*/ other) { + int min = _data.Length, defaultResult; + if (min < other.Length) { + defaultResult = -1; + } else if (min > other.Length) { + min = other.Length; + defaultResult = +1; + } else { + defaultResult = 0; + } + + for (int i = 0; i < min; i++) { + if (_data[i] != other[i]) { + return (int)_data[i] - other[i]; + } + } + + return defaultResult; + } + + public int DataIndexOf(char c, int start, int count) { + for (int i = start; i < start + count; i++) { + if (_data[i] == c) { + return i; + } + } + return -1; + } + + public int DataLastIndexOf(char c, int start, int count) { + for (int i = start; i > start - count; i--) { + if (_data[i] == c) { + return i; + } + } + return -1; + } + + public int DataIndexOf(string str, int start, int count) { + // TODO: is there a better way? + return _data.ToString().IndexOf(str, start, count); + } + + public int DataLastIndexOf(string str, int start, int count) { + // TODO: is there a better way? + return _data.ToString().LastIndexOf(str, start, count); + } + + public StringBuilderContent/*!*/ DataAppend(char c, int repeatCount) { + _data.Append(c, repeatCount); + return this; + } + + public StringBuilderContent/*!*/ DataAppend(string/*!*/ str, int start, int count) { + _data.Append(str, start, count); + return this; + } + + public StringBuilderContent/*!*/ DataAppendFormat(IFormatProvider provider, string/*!*/ format, object[]/*!*/ args) { + _data.AppendFormat(provider, format, args); + return this; + } + + public StringBuilderContent/*!*/ DataInsert(int index, char c) { + _data.Insert(index, new String(c, 1)); + return this; + } + + public StringBuilderContent/*!*/ DataInsert(int index, string/*!*/ str, int start, int count) { + _data.Insert(index, str.ToCharArray(), start, count); + return this; + } + + public StringBuilderContent/*!*/ DataInsert(int index, char c, int repeatCount) { + // TODO: + _data.Insert(index, c.ToString(), repeatCount); + return this; + } + + public StringBuilderContent/*!*/ DataRemove(int start, int count) { + _data.Remove(start, count); + return this; + } + + #endregion + + #region GetHashCode, Length, Clone (read-only) + + public override int GetHashCode() { + int result = 5381; + for (int i = 0; i < _data.Length; i++) { + result = unchecked(((result << 5) + result) ^ _data[i]); + } + return result; + } + + public override bool IsBinary { + get { return false; } + } + + public int DataLength { + get { return _data.Length; } + } + + public override int Length { + get { return _data.Length; } + } + + public override bool IsEmpty { + get { return _data.Length == 0; } + } + + public override int GetCharCount() { + return _data.Length; + } + + public override int GetByteCount() { + return (_data.Length > 0) ? SwitchToBinary().DataLength : 0; + } + + public override Content/*!*/ Clone(MutableString/*!*/ newOwner) { + return new StringBuilderContent(newOwner, new StringBuilder(_data.ToString())); + } + + public override void GetDebugView(out string/*!*/ value, out string/*!*/ type) { + value = _data.ToString(); + type = "String (mutable)"; + } + + #endregion + + #region Conversions (read-only) + + public override string/*!*/ ConvertToString() { + return _data.ToString(); + } + + public override byte[]/*!*/ ConvertToBytes() { + var binary = SwitchToBinary(); + return binary.DataGetSlice(0, binary.DataLength); + } + + public override string/*!*/ ToString() { + return _data.ToString(); + } + + public override byte[]/*!*/ ToByteArray() { + return DataToBytes(); + } + + public override GenericRegex/*!*/ ToRegularExpression(RubyRegexOptions options) { + return new StringRegex(_data.ToString(), options); + } + + public override Content/*!*/ EscapeRegularExpression() { + StringBuilder sb = StringRegex.EscapeToStringBuilder(_data.ToString()); + return (sb != null) ? new StringBuilderContent(_owner, sb) : this; + } + + #endregion + + #region CompareTo (read-only) + + public override int CompareTo(string/*!*/ str) { + return DataCompareTo(str); + } + + public override int CompareTo(byte[]/*!*/ bytes) { + return SwitchToBinary().CompareTo(bytes); + } + + public override int ReverseCompareTo(Content/*!*/ str) { + return str.CompareTo(_data.ToString()); + } + + #endregion + + #region Slices (read-only) + + public override char GetChar(int index) { + return _data[index]; + } + + public override byte GetByte(int index) { + return SwitchToBinary().DataGetByte(index); + } + + public override char PeekChar(int index) { + return _data[index]; + } + + public override byte PeekByte(int index) { + byte[] bytes = new byte[_owner._encoding.GetMaxByteCount(1)]; + _owner._encoding.GetBytes(_data.ToString(), index, 1, bytes, 0); + return bytes[0]; + } + + public override string/*!*/ GetStringSlice(int start, int count) { + return _data.ToString(start, count); + } + + public override byte[]/*!*/ GetBinarySlice(int start, int count) { + return SwitchToBinary().DataGetSlice(start, count); + } + + public override Content/*!*/ GetSlice(int start, int count) { + return new StringBuilderContent(_owner, new StringBuilder(_data.ToString(start, count))); + } + + #endregion + + #region IndexOf (read-only) + + public override int IndexOf(char c, int start, int count) { + return DataIndexOf(c, start, count); + } + + public override int IndexOf(byte b, int start, int count) { + return SwitchToBinary().DataIndexOf(b, start, count); + } + + public override int IndexOf(string/*!*/ str, int start, int count) { + return DataIndexOf(str, start, count); + } + + public override int IndexOf(byte[]/*!*/ bytes, int start, int count) { + return SwitchToBinary().DataIndexOf(bytes, start, count); + } + + public override int IndexIn(Content/*!*/ str, int start, int count) { + return str.IndexOf(_data.ToString(), start, count); + } + + #endregion + + #region LastIndexOf (read-only) + + public override int LastIndexOf(char c, int start, int count) { + return DataLastIndexOf(c, start, count); + } + + public override int LastIndexOf(byte b, int start, int count) { + return SwitchToBinary().DataLastIndexOf(b, start, count); + } + + public override int LastIndexOf(string/*!*/ str, int start, int count) { + return DataLastIndexOf(str, start, count); + } + + public override int LastIndexOf(byte[]/*!*/ bytes, int start, int count) { + return SwitchToBinary().DataLastIndexOf(bytes, start, count); + } + + public override int LastIndexIn(Content/*!*/ str, int start, int count) { + return str.LastIndexOf(_data.ToString(), start, count); + } + + #endregion + + #region Append + + public override Content/*!*/ Append(char c, int repeatCount) { + return DataAppend(c, repeatCount); + } + + public override Content/*!*/ Append(byte b, int repeatCount) { + return SwitchToBinary().DataAppend(b, repeatCount); + } + + public override Content/*!*/ Append(string/*!*/ str, int start, int count) { + return DataAppend(str, start, count); + } + + public override Content/*!*/ Append(byte[]/*!*/ bytes, int start, int count) { + return SwitchToBinary().DataAppend(bytes, start, count); + } + + public override Content/*!*/ AppendFormat(IFormatProvider provider, string/*!*/ format, object[]/*!*/ args) { + return DataAppendFormat(provider, format, args); + } + + public override Content/*!*/ AppendTo(Content/*!*/ str, int start, int count) { + return str.Append(_data.ToString(), start, count); + } + + #endregion + + #region Insert + + public override Content/*!*/ Insert(int index, char c) { + return DataInsert(index, c); + } + + public override Content/*!*/ Insert(int index, byte b) { + return SwitchToBinary().DataInsert(index, b); + } + + public override Content/*!*/ Insert(int index, string/*!*/ str, int start, int count) { + return DataInsert(index, str, start, count); + } + + public override Content/*!*/ Insert(int index, byte[]/*!*/ bytes, int start, int count) { + return SwitchToBinary().DataInsert(index, bytes, start, count); + } + + public override Content/*!*/ InsertTo(Content/*!*/ str, int index, int start, int count) { + return str.Insert(index, _data.ToString(), start, count); + } + + public override Content/*!*/ SetItem(int index, byte b) { + return SwitchToBinary().DataSetByte(index, b); + } + + public override Content/*!*/ SetItem(int index, char c) { + return DataSetChar(index, c); + } + + #endregion + + #region Remove + + public override Content/*!*/ Remove(int start, int count) { + return DataRemove(start, count); + } + + #endregion + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/MutableString.StringContent.cs b/merlin/main/languages/ruby/Ruby/Builtins/MutableString.StringContent.cs new file mode 100644 index 0000000000..8dde429558 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/MutableString.StringContent.cs @@ -0,0 +1,311 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Builtins { + public partial class MutableString { + [Serializable] + private class StringContent : Content { + private readonly string/*!*/ _data; + + public StringContent(MutableString/*!*/ owner, string/*!*/ data) + : base(owner) { + Assert.NotNull(data); + _data = data; + } + + protected virtual BinaryContent/*!*/ SwitchToBinary() { + return WrapContent(DataToBytes()); + } + + private StringBuilderContent/*!*/ SwitchToMutable() { + return WrapContent(new StringBuilder(_data)); + } + + #region Data Operations + + protected byte[] DataToBytes() { + return _data.Length > 0 ? _owner._encoding.GetBytes(_data) : IronRuby.Runtime.Utils.Array.EmptyBytes; + } + + // TODO: + public int DataCompareTo(string/*!*/ other) { + int min = _data.Length, defaultResult; + if (min < other.Length) { + defaultResult = -1; + } else if (min > other.Length) { + min = other.Length; + defaultResult = +1; + } else { + defaultResult = 0; + } + + for (int i = 0; i < min; i++) { + if (_data[i] != other[i]) { + return (int)_data[i] - other[i]; + } + } + + return defaultResult; + } + + #endregion + + #region GetHashCode, Length, Clone (read-only) + + public override int GetHashCode() { + // TODO: Duping our hash function from the StringBuilder case to ensure that we return the same hash value for "get".hash vs. "GET".downcase.hash + int result = 5381; + for (int i = 0; i < _data.Length; i++) { + result = unchecked(((result << 5) + result) ^ _data[i]); + } + return result; + } + + public override bool IsBinary { + get { return false; } + } + + public override int Length { + get { return _data.Length; } + } + + public override bool IsEmpty { + get { return _data.Length == 0; } + } + + public override int GetCharCount() { + return _data.Length; + } + + public override int GetByteCount() { + return (_data.Length > 0) ? SwitchToBinary().DataLength : 0; + } + + public override void GetDebugView(out string/*!*/ value, out string/*!*/ type) { + value = _data; + type = "String (immutable)"; + } + + public override Content/*!*/ Clone(MutableString/*!*/ newOwner) { + return new StringContent(newOwner, _data); + } + + #endregion + + #region Conversions (read-only) + + // internal representation is immutable so we can pass it outside: + public override string/*!*/ ConvertToString() { + return _data; + } + + public override byte[]/*!*/ ConvertToBytes() { + var binary = SwitchToBinary(); + return binary.DataGetSlice(0, binary.DataLength); + } + + public override string/*!*/ ToString() { + return _data; + } + + public override byte[]/*!*/ ToByteArray() { + return DataToBytes(); + } + + public override GenericRegex/*!*/ ToRegularExpression(RubyRegexOptions options) { + return new StringRegex(_data, options); + } + + public override Content/*!*/ EscapeRegularExpression() { + StringBuilder sb = StringRegex.EscapeToStringBuilder(_data); + return (sb != null) ? new StringContent(_owner, sb.ToString()) : this; + } + + #endregion + + #region CompareTo (read-only) + + public override int CompareTo(string/*!*/ str) { + return DataCompareTo(str); + } + + public override int CompareTo(byte[]/*!*/ bytes) { + return SwitchToBinary().CompareTo(bytes); + } + + public override int ReverseCompareTo(Content/*!*/ str) { + return str.CompareTo(_data); + } + + #endregion + + #region Slices (read-only) + + public override char GetChar(int index) { + return _data[index]; + } + + public override byte GetByte(int index) { + return SwitchToBinary().DataGetByte(index); + } + + public override char PeekChar(int index) { + return _data[index]; + } + + public override byte PeekByte(int index) { + byte[] bytes = new byte[_owner._encoding.GetMaxByteCount(1)]; + _owner._encoding.GetBytes(_data, index, 1, bytes, 0); + return bytes[0]; + } + + public override string/*!*/ GetStringSlice(int start, int count) { + return _data.Substring(start, count); + } + + public override byte[]/*!*/ GetBinarySlice(int start, int count) { + return SwitchToBinary().DataGetSlice(start, count); + } + + public override Content/*!*/ GetSlice(int start, int count) { + return new StringContent(_owner, _data.Substring(start, count)); + } + + #endregion + + #region IndexOf (read-only) + + public override int IndexOf(char c, int start, int count) { + return _data.IndexOf(c, start, count); + } + + public override int IndexOf(byte b, int start, int count) { + return SwitchToBinary().DataIndexOf(b, start, count); + } + + public override int IndexOf(string/*!*/ str, int start, int count) { + return _data.IndexOf(str, start, count); + } + + public override int IndexOf(byte[]/*!*/ bytes, int start, int count) { + return SwitchToBinary().DataIndexOf(bytes, start, count); + } + + public override int IndexIn(Content/*!*/ str, int start, int count) { + return str.IndexOf(_data, start, count); + } + + #endregion + + #region LastIndexOf (read-only) + + public override int LastIndexOf(char c, int start, int count) { + return _data.LastIndexOf(c, start, count); + } + + public override int LastIndexOf(byte b, int start, int count) { + return SwitchToBinary().DataLastIndexOf(b, start, count); + } + + public override int LastIndexOf(string/*!*/ str, int start, int count) { + return _data.LastIndexOf(str, start, count); + } + + public override int LastIndexOf(byte[]/*!*/ bytes, int start, int count) { + return SwitchToBinary().DataLastIndexOf(bytes, start, count); + } + + public override int LastIndexIn(Content/*!*/ str, int start, int count) { + return str.LastIndexOf(_data, start, count); + } + + #endregion + + #region Append + + public override Content/*!*/ Append(char c, int repeatCount) { + return SwitchToMutable().DataAppend(c, repeatCount); + } + + public override Content/*!*/ Append(byte b, int repeatCount) { + return SwitchToBinary().DataAppend(b, repeatCount); + } + + public override Content/*!*/ Append(string/*!*/ str, int start, int count) { + return SwitchToMutable().DataAppend(str, start, count); + } + + public override Content/*!*/ Append(byte[]/*!*/ bytes, int start, int count) { + return SwitchToBinary().DataAppend(bytes, start, count); + } + + public override Content/*!*/ AppendFormat(IFormatProvider provider, string/*!*/ format, object[]/*!*/ args) { + return SwitchToMutable().DataAppendFormat(provider, format, args); + } + + public override Content/*!*/ AppendTo(Content/*!*/ str, int start, int count) { + return str.Append(_data, start, count); + } + + #endregion + + #region Insert + + public override Content/*!*/ Insert(int index, char c) { + return SwitchToMutable().DataInsert(index, c); + } + + public override Content/*!*/ Insert(int index, byte b) { + return SwitchToBinary().DataInsert(index, b); + } + + public override Content/*!*/ Insert(int index, string/*!*/ str, int start, int count) { + return SwitchToMutable().DataInsert(index, str, start, count); + } + + public override Content/*!*/ Insert(int index, byte[]/*!*/ bytes, int start, int count) { + return SwitchToBinary().DataInsert(index, bytes, start, count); + } + + public override Content/*!*/ InsertTo(Content/*!*/ str, int index, int start, int count) { + return str.Insert(index, _data, start, count); + } + + public override Content/*!*/ SetItem(int index, byte b) { + return SwitchToBinary().DataSetByte(index, b); + } + + public override Content/*!*/ SetItem(int index, char c) { + return SwitchToMutable().DataSetChar(index, c); + } + + #endregion + + #region Remove + + public override Content/*!*/ Remove(int start, int count) { + return SwitchToMutable().DataRemove(start, count); + } + + #endregion + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/MutableString.Subclass.cs b/merlin/main/languages/ruby/Ruby/Builtins/MutableString.Subclass.cs new file mode 100644 index 0000000000..aa2f576573 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/MutableString.Subclass.cs @@ -0,0 +1,77 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Builtins { + // Represents any Ruby subclass of String. The actual class object is remembered. + // We don't allow non-Ruby code to extend Ruby String. + public partial class MutableString { + public sealed class Subclass : MutableString, IRubyObject { + private readonly RubyClass/*!*/ _class; + private RubyInstanceData _instanceData; + + // Called by Class#new rule when creating a Ruby subclass of String. + // The encoding is set to BINARY. + public Subclass(RubyClass/*!*/ rubyClass) + : this(rubyClass, BinaryEncoding.Instance) { + } + + public Subclass(RubyClass/*!*/ rubyClass, Encoding encoding) + : base(encoding) { + Assert.NotNull(rubyClass); + _class = rubyClass; + } + + private Subclass(Subclass/*!*/ str) + : base(str) { + _class = str._class; + } + + // creates a blank instance of self type: + public override MutableString/*!*/ CreateInstance() { + return new Subclass(_class, _encoding); + } + + // creates a copy including the version and flags: + public override MutableString/*!*/ Clone() { + return new Subclass(this); + } + + #region IRubyObject Members + + [Emitted] + public RubyClass/*!*/ Class { + get { return _class; } + } + + public RubyInstanceData/*!*/ GetInstanceData() { + return RubyOps.GetInstanceData(ref _instanceData); + } + + public RubyInstanceData TryGetInstanceData() { + return _instanceData; + } + + #endregion + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/MutableString.cs b/merlin/main/languages/ruby/Ruby/Builtins/MutableString.cs new file mode 100644 index 0000000000..fa4fe0ae54 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/MutableString.cs @@ -0,0 +1,990 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using System.Text; +using IronRuby.Runtime; +using Microsoft.Scripting.Runtime; +using IronRuby.Compiler; + +namespace IronRuby.Builtins { + + // Doesn't implement IRubyObject since that would require to hold on a RubyClass object and flow it into each factory. + // We don't want to do so since it would make libraries complex and frozen per-appdomain singletons impossible. + // It would also consume more memory while the string subclassing is not a common scenario. + // To allow inheriting from String in Ruby, we need a subclass that implements IRubyObject. + // We could genrate one the first time a String is subclassed. Having it defined explicitly (MutableStringSubclass) however + // saves that code gen and also makes it simpler to detect whether or not we need to create a subclass of a string fast. + // That's a common operation String methods do. + [Serializable] + [DebuggerDisplay("{GetDebugValue()}", Type = "{GetDebugType()}")] + public partial class MutableString : IEquatable, IComparable, IRubyObjectState, IDuplicable { + private Content/*!*/ _content; + private Encoding/*!*/ _encoding; + + // The lowest bit is tainted flag. + // The version is set to FrozenVersion when the string is frozen. FrozenVersion is the maximum version, so any update to the version + // triggers an OverflowException, which we convert to InvalidOperationException. + private uint _versionAndFlags; + + private const uint IsTaintedFlag = 1; + private const int FlagsCount = 1; + private const uint FlagsMask = (1U << FlagsCount) - 1; + private const uint VersionMask = ~FlagsMask; + private const uint FrozenVersion = VersionMask; + + public static readonly MutableString/*!*/ Empty = MutableString.Create(String.Empty).Freeze(); + + #region Construction + + private MutableString(Content/*!*/ content, Encoding/*!*/ encoding) { + Assert.NotNull(content, encoding); + content.SetOwner(this); + _content = content; + _encoding = encoding; + } + + private void SetContent(Content/*!*/ content) { + Assert.NotNull(content); + _content = content; + } + + // creates a copy including the taint flag, not including the version: + protected MutableString(MutableString/*!*/ str) { + Assert.NotNull(str); + _content = str._content.Clone(this); + _encoding = str._encoding; + IsTainted = str.IsTainted; + } + + // mutable + private MutableString(StringBuilder/*!*/ sb, Encoding/*!*/ encoding) { + _content = new StringBuilderContent(this, sb); + _encoding = encoding; + } + + // binary + private MutableString(List/*!*/ bytes, Encoding/*!*/ encoding) { + _content = new BinaryContent(this, bytes); + _encoding = encoding; + } + + // immutable + private MutableString(string/*!*/ str, Encoding/*!*/ encoding) { + _content = new StringContent(this, str); + _encoding = encoding; + } + + // Ruby subclasses (MutableStringSubclass): + protected MutableString(Encoding encoding) + : this(new StringBuilder(), encoding) { + } + + // Ruby allocator + public MutableString() + : this(String.Empty, BinaryEncoding.Instance) { + } + + /// + /// Creates a blank instance of self type with no flags set. + /// Copies encoding from the current class. + /// + public virtual MutableString/*!*/ CreateInstance() { + return new MutableString(new StringBuilder(), _encoding); + } + + /// + /// Creates a copy of this instance, including content and taint. + /// Doesn't copy frozen state and instance variables. + /// Preserves the class of the String. + /// + public virtual MutableString/*!*/ Clone() { + return new MutableString(this); + } + + /// + /// Creates an empty copy of this instance, taint and instance variables. + /// + public MutableString/*!*/ Duplicate(RubyContext/*!*/ context, bool copySingletonMembers, MutableString/*!*/ result) { + context.CopyInstanceData(this, result, false, false, copySingletonMembers); + return result; + } + + object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) { + return Duplicate(context, copySingletonMembers, CreateInstance()); + } + + /// + /// Creates an empty textual MutableString. + /// + public static MutableString/*!*/ CreateMutable() { + // TODO: encoding + return new MutableString(new StringBuilder(), BinaryEncoding.Obsolete); + } + + // TODO: encoding + public static MutableString/*!*/ CreateMutable(int capacity) { + return new MutableString(new StringBuilder(capacity), BinaryEncoding.Obsolete); + } + + // TODO: encoding + public static MutableString/*!*/ CreateMutable(string/*!*/ str, int capacity) { + return new MutableString(new StringBuilder(str, capacity), BinaryEncoding.Obsolete); + } + + // TODO: encoding + public static MutableString/*!*/ CreateMutable(string/*!*/ str) { + return new MutableString(new StringBuilder(str), BinaryEncoding.Obsolete); + } + + public static MutableString/*!*/ CreateMutable(string/*!*/ str, Encoding encoding) { + return new MutableString(new StringBuilder(str), encoding); + } + + // TODO: encoding + public static MutableString/*!*/ Create(string/*!*/ str) { + return Create(str, BinaryEncoding.Obsolete); + } + + public static MutableString/*!*/ Create(string/*!*/ str, Encoding/*!*/ encoding) { + ContractUtils.RequiresNotNull(str, "str"); + ContractUtils.RequiresNotNull(encoding, "encoding"); + return new MutableString(str, encoding); + } + + /// + /// Creates an instance of MutableString with content and taint copied from a given string. + /// + public static MutableString/*!*/ Create(MutableString/*!*/ str) { + ContractUtils.RequiresNotNull(str, "str"); + return new MutableString(str); + } + + // used by RubyOps: + internal static MutableString/*!*/ CreateInternal(MutableString str) { + if (str != null) { + // "...#{str}..." + return new MutableString(str); + } else { + // empty literal: "...#{nil}..." + return CreateMutable(String.Empty, BinaryEncoding.Instance); + } + } + + public static MutableString/*!*/ CreateBinary() { + return new MutableString(new List(), BinaryEncoding.Instance); + } + + public static MutableString/*!*/ CreateBinary(int capacity) { + return new MutableString(new List(capacity), BinaryEncoding.Instance); + } + + public static MutableString/*!*/ CreateBinary(byte[]/*!*/ bytes) { + ContractUtils.RequiresNotNull(bytes, "bytes"); + return CreateBinary((IList)bytes, bytes.Length); + } + + public static MutableString/*!*/ CreateBinary(IList/*!*/ bytes) { + ContractUtils.RequiresNotNull(bytes, "bytes"); + return CreateBinary(bytes, bytes.Count); + } + + public static MutableString/*!*/ CreateBinary(byte[]/*!*/ bytes, int capacity) { + return CreateBinary((IList)bytes, capacity); + } + + public static MutableString/*!*/ CreateBinary(IList/*!*/ bytes, int capacity) { + ContractUtils.RequiresNotNull(bytes, "bytes"); + List list = new List(capacity); + list.AddRange(bytes); + return new MutableString(list, BinaryEncoding.Instance); + } + + public static MutableString[]/*!*/ MakeArray(ICollection/*!*/ stringCollection) { + ContractUtils.RequiresNotNull(stringCollection, "stringCollection"); + MutableString[] result = new MutableString[stringCollection.Count]; + int i = 0; + foreach (var str in stringCollection) { + result[i++] = MutableString.Create(str); + } + return result; + } + + #endregion + + #region Versioning and Flags + + [CLSCompliant(false)] + public uint Version { + get { + return _versionAndFlags >> FlagsCount; + } + } + + private void Mutate() { + try { + checked { _versionAndFlags += (1 << FlagsCount); } + } catch (OverflowException) { + throw RubyExceptions.CreateTypeError("can't modify frozen object"); + } + } + + public bool IsTainted { + get { + return (_versionAndFlags & IsTaintedFlag) != 0; + } + set { + Mutate(); + _versionAndFlags = (_versionAndFlags & ~IsTaintedFlag) | (value ? IsTaintedFlag : 0); + } + } + + public bool IsFrozen { + get { + return (_versionAndFlags & VersionMask) == FrozenVersion; + } + } + + void IRubyObjectState.Freeze() { + Freeze(); + } + + public MutableString/*!*/ Freeze() { + _versionAndFlags |= FrozenVersion; + return this; + } + + /// + /// Makes this string tainted if the specified string is tainted. + /// + public MutableString/*!*/ TaintBy(MutableString/*!*/ str) { + IsTainted |= str.IsTainted; + return this; + } + + /// + /// Makes this string tainted if the specified string is tainted. + /// + public MutableString/*!*/ TaintBy(object/*!*/ obj, RubyContext/*!*/ context) { + IsTainted |= context.IsObjectTainted(obj); + return this; + } + + /// + /// Makes this string tainted if the specified string is tainted. + /// + public MutableString/*!*/ TaintBy(object/*!*/ obj, RubyScope/*!*/ scope) { + IsTainted |= scope.RubyContext.IsObjectTainted(obj); + return this; + } + + #endregion + + #region Misc (read-only) + + public bool IsBinary { + get { + return _content.IsBinary; + } + } + + public Encoding/*!*/ Encoding { + get { return _encoding; } + set { + ContractUtils.RequiresNotNull(value, "value"); + _encoding = value; + } + } + + public override int GetHashCode() { + return _content.GetHashCode(); + } + + internal string/*!*/ GetDebugValue() { + string value, type; + _content.GetDebugView(out value, out type); + return value; + } + + internal string/*!*/ GetDebugType() { + string value, type; + _content.GetDebugView(out value, out type); + return type; + } + + internal MutableString/*!*/ EscapeRegularExpression() { + return new MutableString(_content.EscapeRegularExpression(), _encoding); + } + + #endregion + + #region Conversions (read-only) + + /// + /// Returns a copy of the content in a form of an read-only string. + /// The internal representation of the MutableString is preserved. + /// + public override string/*!*/ ToString() { + return _content.ToString(); + } + + /// + /// Returns a copy of the content in a form of an byte array. + /// The internal representation of the MutableString is preserved. + /// + public byte[]/*!*/ ToByteArray() { + return _content.ToByteArray(); + } + + public GenericRegex/*!*/ ToRegularExpression(RubyRegexOptions options) { + return _content.ToRegularExpression(options); + } + + /// + /// Switches internal representation to textual. + /// + /// A copy of the internal representation unless it is read-only (string). + public string/*!*/ ConvertToString() { + return _content.ConvertToString(); + } + + /// + /// Switches internal representation to binary. + /// + /// A copy of the internal representation. + public byte[]/*!*/ ConvertToBytes() { + return _content.ConvertToBytes(); + } + + // used by auto-conversions + [Obsolete("Do not use in code")] + public static implicit operator string(MutableString/*!*/ self) { + return self._content.ConvertToString(); + } + + // used by auto-conversions + [Obsolete("Do not use in code")] + public static implicit operator byte[](MutableString/*!*/ self) { + return self._content.ConvertToBytes(); + } + + #endregion + + #region Comparisons (read-only) + + public static bool operator ==(MutableString self, char other) { + return Equals(self, other); + } + + public static bool operator !=(MutableString self, char other) { + return !Equals(self, other); + } + + public static bool operator ==(MutableString self, MutableString other) { + return Equals(self, other); + } + + public static bool operator !=(MutableString self, MutableString other) { + return !Equals(self, other); + } + + private static bool Equals(MutableString self, MutableString other) { + if (ReferenceEquals(self, other)) return true; + if (ReferenceEquals(self, null)) return false; + if (ReferenceEquals(other, null)) return false; + return other._content.ReverseCompareTo(self._content) == 0; + } + + private static bool Equals(MutableString self, char other) { + if (ReferenceEquals(self, null)) return false; + return self.GetCharCount() == 1 && self.GetChar(0) == other; + } + + public override bool Equals(object other) { + return Equals(other as MutableString); + } + + public bool Equals(MutableString other) { + return CompareTo(other) == 0; + } + + public int CompareTo(MutableString other) { + if (ReferenceEquals(this, other)) return 0; + if (ReferenceEquals(other, null)) return 1; + return other._content.ReverseCompareTo(_content); + } + + #endregion + + #region Length (read-only) + + public static bool IsNullOrEmpty(MutableString/*!*/ str) { + return ReferenceEquals(str, null) || str.IsEmpty; + } + + public bool IsEmpty { + get { return _content.IsEmpty; } + } + + // TODO: replace by CharCount, ByteCount + //[Obsolete("Use GetCharCount(), GetByteCount()")] + public int Length { + get { return _content.Length; } + } + + public int GetLength() { + return _content.Length; + } + + public int GetCharCount() { + return _content.GetCharCount(); + } + + public int GetByteCount() { + return _content.GetByteCount(); + } + + #endregion + + #region StartsWith, EndsWith (read-only) + + public bool EndsWith(char value) { + return GetLastChar() == value; + } + + public bool EndsWith(string/*!*/ value) { + // TODO: + return _content.ConvertToString().EndsWith(value); + } + + #endregion + + #region Slices (read-only) + + // converts the result, not the string + public char PeekChar(int index) { + return _content.PeekChar(index); + } + + // converts the result, not the string + public byte PeekByte(int index) { + return _content.PeekByte(index); + } + + // converts the string representation to text if not already + public char GetChar(int index) { + return _content.GetChar(index); + } + + // converts the string representation to binary if not already + public byte GetByte(int index) { + return _content.GetByte(index); + } + + // returns -1 if the string is empty + public int GetLastChar() { + return (_content.IsEmpty) ? -1 : _content.GetChar(_content.Length - 1); + } + + // returns -1 if the string is empty + public int GetFirstChar() { + return (_content.IsEmpty) ? -1 : _content.GetChar(0); + } + + /// + /// Returns a new mutable string containing a substring of the current one. + /// + public MutableString/*!*/ GetSlice(int start) { + return GetSlice(start, _content.Length - start); + } + + public MutableString/*!*/ GetSlice(int start, int count) { + //RequiresArrayRange(start, count); + return new MutableString(_content.GetSlice(start, count), _encoding); + } + + public string/*!*/ GetStringSlice(int start) { + return GetStringSlice(start, _content.GetCharCount() - start); + } + + public string/*!*/ GetStringSlice(int start, int count) { + //RequiresArrayRange(start, count); + return _content.GetStringSlice(start, count); + } + + public byte[]/*!*/ GetBinarySlice(int start) { + return GetBinarySlice(start, _content.GetByteCount() - start); + } + + public byte[]/*!*/ GetBinarySlice(int start, int count) { + //RequiresArrayRange(start, count); + return _content.GetBinarySlice(start, count); + } + + #endregion + + #region Split (read-only) + + // TODO: binary ops, ... + public MutableString[]/*!*/ Split(char[]/*!*/ separators, int maxComponents, StringSplitOptions options) { + // TODO: + return MakeArray(StringUtils.Split(_content.ConvertToString(), separators, maxComponents, options)); + } + + #endregion + + #region IndexOf (read-only) + + public int IndexOf(char value) { + return IndexOf(value, 0); + } + + public int IndexOf(char value, int start) { + return IndexOf(value, start, _content.GetCharCount() - start); + } + + public int IndexOf(char value, int start, int count) { + //RequiresArrayRange(start, count); + return _content.IndexOf(value, start, count); + } + + public int IndexOf(byte value) { + return IndexOf(value, 0); + } + + public int IndexOf(byte value, int start) { + return IndexOf(value, start, _content.GetByteCount() - start); + } + + public int IndexOf(byte value, int start, int count) { + //RequiresArrayRange(start, count); + return _content.IndexOf(value, start, count); + } + + public int IndexOf(string/*!*/ value) { + return IndexOf(value, 0); + } + + public int IndexOf(string/*!*/ value, int start) { + return IndexOf(value, start, _content.GetCharCount() - start); + } + + public int IndexOf(string/*!*/ value, int start, int count) { + ContractUtils.RequiresNotNull(value, "value"); + //RequiresArrayRange(start, count); + + return _content.IndexOf(value, start, count); + } + + public int IndexOf(byte[]/*!*/ value) { + return IndexOf(value, 0); + } + + public int IndexOf(byte[]/*!*/ value, int start) { + return IndexOf(value, start, _content.GetByteCount() - start); + } + + public int IndexOf(byte[]/*!*/ value, int start, int count) { + ContractUtils.RequiresNotNull(value, "value"); + //RequiresArrayRange(start, count); + + return _content.IndexOf(value, start, count); + } + + public int IndexOf(MutableString/*!*/ value) { + return IndexOf(value, 0); + } + + public int IndexOf(MutableString/*!*/ value, int start) { + return IndexOf(value, start, _content.Length - start); + } + + public int IndexOf(MutableString/*!*/ value, int start, int count) { + ContractUtils.RequiresNotNull(value, "value"); + //RequiresArrayRange(start, count); + + return value._content.IndexIn(_content, start, count); + } + + #endregion + + #region LastIndexOf (read-only) + + public int LastIndexOf(char value) { + int length = _content.GetCharCount(); + return LastIndexOf(value, length - 1, length); + } + + public int LastIndexOf(char value, int start) { + return LastIndexOf(value, start, start + 1); + } + + public int LastIndexOf(char value, int start, int count) { + //RequiresReverseArrayRange(start, count); + return _content.LastIndexOf(value, start, count); + } + + public int LastIndexOf(byte value) { + int length = _content.GetByteCount(); + return LastIndexOf(value, length - 1, length); + } + + public int LastIndexOf(byte value, int start) { + return LastIndexOf(value, start, start + 1); + } + + public int LastIndexOf(byte value, int start, int count) { + //RequiresReverseArrayRange(start, count); + return _content.LastIndexOf(value, start, count); + } + + public int LastIndexOf(string/*!*/ value) { + int length = _content.GetCharCount(); + return LastIndexOf(value, length - 1, length); + } + + public int LastIndexOf(string/*!*/ value, int start) { + return LastIndexOf(value, start, start + 1); + } + + public int LastIndexOf(string/*!*/ value, int start, int count) { + ContractUtils.RequiresNotNull(value, "value"); + //RequiresReverseArrayRange(start, count); + + return _content.LastIndexOf(value, start, count); + } + + public int LastIndexOf(byte[]/*!*/ value) { + int length = _content.GetByteCount(); + return LastIndexOf(value, length - 1, length); + } + + public int LastIndexOf(byte[]/*!*/ value, int start) { + return LastIndexOf(value, start, start + 1); + } + + public int LastIndexOf(byte[]/*!*/ value, int start, int count) { + ContractUtils.RequiresNotNull(value, "value"); + //RequiresReverseArrayRange(start, count); + + return _content.LastIndexOf(value, start, count); + } + + public int LastIndexOf(MutableString/*!*/ value) { + int length = _content.Length; + return LastIndexOf(value, length - 1, length); + } + + public int LastIndexOf(MutableString/*!*/ value, int start) { + return LastIndexOf(value, start, start + 1); + } + + public int LastIndexOf(MutableString/*!*/ value, int start, int count) { + ContractUtils.RequiresNotNull(value, "value"); + //RequiresReverseArrayRange(start, count); + + return value._content.LastIndexIn(_content, start, count); + } + + #endregion + + #region Append + + public MutableString/*!*/ Append(char value) { + Mutate(); + _content.Append(value, 1); + return this; + } + + public MutableString/*!*/ Append(char value, int repeatCount) { + Mutate(); + _content.Append(value, repeatCount); + return this; + } + + public MutableString/*!*/ Append(byte value) { + Mutate(); + _content.Append(value, 1); + return this; + } + + public MutableString/*!*/ Append(byte value, int repeatCount) { + Mutate(); + _content.Append(value, repeatCount); + return this; + } + + public MutableString/*!*/ Append(StringBuilder value) { + if (value != null) { + Mutate(); + _content.Append(value.ToString(), 0, value.Length); + } + return this; + } + + public MutableString/*!*/ Append(string value) { + if (value != null) { + Mutate(); + _content.Append(value, 0, value.Length); + } + return this; + } + + public MutableString/*!*/ Append(string/*!*/ value, int startIndex, int charCount) { + ContractUtils.RequiresNotNull(value, "value"); + ContractUtils.RequiresArrayRange(value, startIndex, charCount, "startIndex", "charCount"); + Mutate(); + + _content.Append(value, startIndex, charCount); + return this; + } + + public MutableString/*!*/ Append(byte[] value) { + if (value != null) { + Mutate(); + _content.Append(value, 0, value.Length); + } + return this; + } + + public MutableString/*!*/ Append(byte[]/*!*/ value, int start, int count) { + ContractUtils.RequiresNotNull(value, "value"); + ContractUtils.RequiresArrayRange(value, start, count, "startIndex", "count"); + + Mutate(); + _content.Append(value, start, count); + return this; + } + + public MutableString/*!*/ Append(MutableString/*!*/ value) { + if (value != null) { + Mutate(); + value._content.AppendTo(_content, 0, value._content.Length); + } + return this; + } + + public MutableString/*!*/ Append(MutableString/*!*/ str, int start, int count) { + ContractUtils.RequiresNotNull(str, "str"); + //RequiresArrayRange(start, count); + + Mutate(); + str._content.AppendTo(_content, start, count); + return this; + } + + public MutableString/*!*/ AppendFormat(string/*!*/ format, params object[] args) { + Mutate(); + return AppendFormat(null, format, args); + } + + public MutableString/*!*/ AppendFormat(IFormatProvider provider, string/*!*/ format, params object[] args) { + ContractUtils.RequiresNotNull(format, "format"); + Mutate(); + + _content.AppendFormat(provider, format, args); + return this; + } + + #endregion + + #region Insert + + public MutableString/*!*/ SetChar(int index, char c) { + Mutate(); + _content.SetItem(index, c); + return this; + } + + public MutableString/*!*/ SetByte(int index, byte b) { + Mutate(); + _content.SetItem(index, b); + return this; + } + + public MutableString/*!*/ Insert(int index, char c) { + //RequiresArrayInsertIndex(index); + Mutate(); + _content.Insert(index, c); + return this; + } + + public MutableString/*!*/ Insert(int index, byte b) { + //RequiresArrayInsertIndex(index); + Mutate(); + _content.Insert(index, b); + return this; + } + + public MutableString/*!*/ Insert(int index, string value) { + //RequiresArrayInsertIndex(index); + if (value != null) { + Mutate(); + _content.Insert(index, value, 0, value.Length); + } + return this; + } + + public MutableString/*!*/ Insert(int index, string/*!*/ value, int start, int count) { + //RequiresArrayInsertIndex(index); + ContractUtils.RequiresNotNull(value, "value"); + ContractUtils.RequiresArrayRange(value, start, count, "start", "count"); + + Mutate(); + _content.Insert(index, value, start, count); + return this; + } + + public MutableString/*!*/ Insert(int index, byte[] value) { + //RequiresArrayInsertIndex(index); + if (value != null) { + Mutate(); + _content.Insert(index, value, 0, value.Length); + } + return this; + } + + public MutableString/*!*/ Insert(int index, byte[]/*!*/ value, int start, int count) { + //RequiresArrayInsertIndex(index); + ContractUtils.RequiresNotNull(value, "value"); + ContractUtils.RequiresArrayRange(value, start, count, "start", "count"); + + Mutate(); + _content.Insert(index, value, start, count); + return this; + } + + public MutableString/*!*/ Insert(int index, MutableString value) { + //RequiresArrayInsertIndex(index); + if (value != null) { + Mutate(); + value._content.InsertTo(_content, index, 0, value._content.Length); + } + return this; + } + + public MutableString/*!*/ Insert(int index, MutableString/*!*/ value, int start, int count) { + //RequiresArrayInsertIndex(index); + ContractUtils.RequiresNotNull(value, "value"); + //value.RequiresArrayRange(start, count); + + Mutate(); + value._content.InsertTo(_content, index, start, count); + return this; + } + + #endregion + + #region Reverse + + public MutableString/*!*/ Reverse() { + Mutate(); + + // TODO: + if (IsBinary) { + int length = _content.Length; + for (int i = 0; i < length / 2; i++) { + byte a = GetByte(i); + byte b = GetByte(length - i - 1); + SetByte(i, b); + SetByte(length - i - 1, a); + } + } else { + int length = _content.Length; + for (int i = 0; i < length / 2; i++) { + char a = GetChar(i); + char b = GetChar(length - i - 1); + SetChar(i, b); + SetChar(length - i - 1, a); + } + } + + return this; + } + + #endregion + + #region Replace, Remove, Trim, Clear + + public MutableString/*!*/ Replace(int start, int count, MutableString value) { + //RequiresArrayRange(start, count); + + // TODO: + Mutate(); + return Remove(start, count).Insert(start, value); + } + + public MutableString/*!*/ Remove(int start, int count) { + //RequiresArrayRange(start, count); + Mutate(); + _content.Remove(start, count); + return this; + } + + public MutableString/*!*/ Trim(int start, int count) { + Mutate(); + _content = _content.GetSlice(start, count); + return this; + } + + public MutableString/*!*/ Clear() { + Mutate(); + _content = _content.GetSlice(0, 0); + return this; + } + + + #endregion + + +#if OBSOLETE + #region Utils + + /// + /// Requires the range [offset, offset + count] to be a subset of [0, dataLength]. + /// + /// String is null. + /// Offset or count are out of range. + private void RequiresArrayRange(int start, int count, int dataLength) { + if (count < 0) throw new ArgumentOutOfRangeException("count"); + if (start < 0 || dataLength - start < count) throw new ArgumentOutOfRangeException("start"); + } + + /// + /// Requires the range [offset - count, offset] to be a subset of [0, dataLength]. + /// + /// String is null. + /// Offset or count are out of range. + private void //RequiresReverseArrayRange(int start, int count, int dataLength) { + if (count < 0) throw new ArgumentOutOfRangeException("count"); + if (start < count - 1 || start >= dataLength) throw new ArgumentOutOfRangeException("start"); + } + + /// + /// Requires the specified index to point inside the array or at the end. + /// + /// Index is outside the array. + private void RequiresArrayInsertIndex(int index, int dataLength) { + if (index < 0 || index > dataLength) throw new ArgumentOutOfRangeException("index"); + } + + #endregion +#endif + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/MutableStringStream.cs b/merlin/main/languages/ruby/Ruby/Builtins/MutableStringStream.cs new file mode 100644 index 0000000000..200bf88178 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/MutableStringStream.cs @@ -0,0 +1,131 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.IO; + +using IronRuby.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Builtins { + public class MutableStringStream : Stream { + private MutableString/*!*/ _string; + private int _position; + + public MutableStringStream() + : this(MutableString.CreateMutable()) { + } + + public MutableStringStream(MutableString/*!*/ basis) { + _string = basis; + _position = 0; + } + + public MutableString/*!*/ String { + get { return _string; } + set { + ContractUtils.RequiresNotNull(value, "value"); + _string = value; + _position = 0; + } + } + + public override bool CanRead { + get { return true; } + } + + public override bool CanSeek { + get { return true; } + } + + public override bool CanWrite { + get { return true; } + } + + public override void Flush() { + } + + public override long Length { + get { return _string.ConvertToBytes().Length; } + } + + public override long Position { + get { + return _position; + } + set { + if (value < 0) { + throw RubyExceptions.CreateIOError("Invalid argument"); + } + _position = (int)value; + } + } + + public override int Read(byte[]/*!*/ buffer, int offset, int count) { + int maxReadLen = _string.GetByteCount() - _position; + if (count > maxReadLen) { + count = maxReadLen; + } + for (int i = 0; i < count; i++) { + buffer[offset + i] = _string.GetByte(_position++); + } + return count; + } + + public override int ReadByte() { + if (_position >= _string.GetByteCount()) { + return -1; + } + return _string.GetByte(_position++); + } + + public override long Seek(long offset, SeekOrigin origin) { + int byteCount = _string.GetByteCount(); + + int position = _position; + switch (origin) { + case SeekOrigin.Begin: + position = (int)offset; + break; + case SeekOrigin.End: + position = byteCount + (int)offset; + break; + case SeekOrigin.Current: + position += (int)offset; + break; + } + Position = position; + return Position; + } + + public override void SetLength(long value) { + int byteCount = _string.GetByteCount(); + if (byteCount < value) { + _string.Append(0, (int)(value - byteCount)); + } else if (byteCount > value) { + _string.Remove((int)value, byteCount - (int)value); + } + } + + public override void Write(byte[]/*!*/ buffer, int offset, int count) { + _string.Insert(_position, buffer, offset, count); + _position += count; + } + + public override void WriteByte(byte value) { + _string.Insert(_position, value); + _position++; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/Proc.Meta.cs b/merlin/main/languages/ruby/Ruby/Builtins/Proc.Meta.cs new file mode 100644 index 0000000000..c39e062285 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/Proc.Meta.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Dynamic.Binders; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +using Ast = System.Linq.Expressions.Expression; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using IronRuby.Compiler; + +namespace IronRuby.Builtins { + + public partial class Proc : IDynamicObject { + public MetaObject/*!*/ GetMetaObject(Expression/*!*/ parameter) { + return new Meta(parameter, Restrictions.Empty, this); + } + + internal sealed class Meta : MetaObject { + public Meta(Expression/*!*/ expression, Restrictions/*!*/ restrictions, Proc/*!*/ value) + : base(expression, restrictions, value) { + ContractUtils.RequiresNotNull(value, "value"); + } + + public override MetaObject/*!*/ BindInvoke(InvokeBinder/*!*/ action, MetaObject/*!*/[]/*!*/ args) { + RubyCallSignature callSignature; + if (RubyCallSignature.TryCreate(action.Arguments, out callSignature)) { + return action.FallbackInvoke(this, args); + } + + var context = new MetaObject( + Methods.GetContextFromProc.OpCall(AstUtils.Convert(Expression, typeof(Proc))), + Restrictions.Empty, + RubyOps.GetContextFromProc((Proc)Value) + ); + + var metaBuilder = new MetaObjectBuilder(); + Proc.SetCallActionRule(metaBuilder, new CallArguments(context, this, args, callSignature), true); + return metaBuilder.CreateMetaObject(action, args); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/Proc.Subclass.cs b/merlin/main/languages/ruby/Ruby/Builtins/Proc.Subclass.cs new file mode 100644 index 0000000000..8f968aa900 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/Proc.Subclass.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Dynamic.Binders; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime.Calls; +using IronRuby.Runtime; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Builtins { + public partial class Proc { + public sealed class Subclass : Proc, IRubyObject { + private readonly RubyClass/*!*/ _class; + private RubyInstanceData _instanceData; + + // called by Proc#new rule when creating a Ruby subclass of Proc: + public Subclass(RubyClass/*!*/ rubyClass, Proc/*!*/ proc) + : base(proc) { + Assert.NotNull(rubyClass); + _class = rubyClass; + } + + public override Proc/*!*/ Copy() { + return new Subclass(_class, this); + } + + #region IRubyObject Members + + [Emitted] + public RubyClass/*!*/ Class { + get { return _class; } + } + + public RubyInstanceData/*!*/ GetInstanceData() { + return RubyOps.GetInstanceData(ref _instanceData); + } + + public RubyInstanceData TryGetInstanceData() { + return _instanceData; + } + + #endregion + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/Proc.cs b/merlin/main/languages/ruby/Ruby/Builtins/Proc.cs new file mode 100644 index 0000000000..906f821c7c --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/Proc.cs @@ -0,0 +1,299 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +using Ast = System.Linq.Expressions.Expression; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Builtins { + + public enum ProcKind { + Block, + Proc, + Lambda + } + + public partial class Proc : IDuplicable { + // Self object captured by the block definition, if any. + // Although we could load self from scope in Ruby defined blocks, we cannot do so when we don't have a scope. + private readonly object _self; + + // Local scope inside the proc captured by the block definition: + private readonly RubyScope/*!*/ _scope; + + private readonly BlockDispatcher/*!*/ _dispatcher; + private ProcKind _kind; + + // we need to remember the block's owner and proc-converter frames: + private RuntimeFlowControl _owner; + private RuntimeFlowControl _converter; + + internal RuntimeFlowControl Owner { get { return _owner; } set { _owner = value; } } + internal RuntimeFlowControl Converter { get { return _converter; } set { _converter = value; } } + + public ProcKind Kind { + get { return _kind; } + // friend: RuntimeFlowControl + internal set { _kind = value; } + } + + public BlockDispatcher/*!*/ Dispatcher { + get { return _dispatcher; } + } + + public object Self { + get { return _self; } + } + + public RubyScope/*!*/ LocalScope { + get { return _scope; } + } + + internal static PropertyInfo/*!*/ SelfProperty { get { return typeof(Proc).GetProperty("Self"); } } + + #region Construction, Conversion + + internal Proc(ProcKind kind, object self, RubyScope/*!*/ scope, BlockDispatcher/*!*/ dispatcher) { + Assert.NotNull(scope, dispatcher); + _kind = kind; + _self = self; + _scope = scope; + _dispatcher = dispatcher; + } + + protected Proc(Proc/*!*/ proc) + : this(proc.Kind, proc.Self, proc.LocalScope, proc.Dispatcher) { + _owner = proc._owner; + _converter = proc._converter; + } + + /// + /// Creates a copy of the proc that has the same target, context and self object as this block. + /// + public Proc/*!*/ Create(Proc/*!*/ proc) { + return new Proc(proc); + } + + /// + /// Creates a lambda Proc that has the same target, context and self object as this block. + /// Doesn't preserve the class of the Proc. + /// + public Proc/*!*/ ToLambda() { + Proc result = new Proc(this); + result.Kind = ProcKind.Lambda; + return result; + } + + /// + /// Creates a copy of the proc that has the same target, context, self object as this instance. + /// Doesn't copy instance data. + /// Preserves the class of the Proc. + /// + public virtual Proc/*!*/ Copy() { + return new Proc(this); + } + + object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) { + var result = Copy(); + context.CopyInstanceData(this, result, copySingletonMembers); + return result; + } + + public static RubyMemberInfo/*!*/ ToLambdaMethodInfo(Proc/*!*/ lambda, string/*!*/ definitionName, RubyMethodVisibility visibility, + RubyModule/*!*/ owner) { + return new RubyLambdaMethodInfo(lambda, definitionName, (RubyMemberFlags)visibility, owner); + } + + #endregion + + #region Dynamic Operations + + internal static void SetCallActionRule(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, bool testTarget) { + Assert.NotNull(metaBuilder, args); + + var convertedTarget = AstUtils.Convert(args.TargetExpression, typeof(Proc)); + + // test for target type: + if (testTarget) { + metaBuilder.AddTypeRestriction(args.Target.GetType(), args.TargetExpression); + } + + SetProcCallRule( + metaBuilder, + convertedTarget, // proc object + Ast.Property(convertedTarget, SelfProperty), // self captured by the block closure + null, + args + ); + } + + /// + /// From control flow perspective it "calls" the proc. + /// + internal static void SetProcCallRule( + MetaObjectBuilder/*!*/ metaBuilder, + Expression/*!*/ procExpression, // proc object + Expression/*!*/ selfExpression, // self passed to the proc + Expression callingMethodExpression, // RubyLambdaMethodInfo passed to the proc via BlockParam + CallArguments/*!*/ args // user arguments passed to the proc + ) { + var bfcVariable = metaBuilder.GetTemporary(typeof(BlockParam), "#bfc"); + var resultVariable = metaBuilder.GetTemporary(typeof(object), "#result"); + + metaBuilder.Result = AstFactory.Block( + Ast.Assign(bfcVariable, + (callingMethodExpression != null) ? + Methods.CreateBfcForMethodProcCall.OpCall( + AstUtils.Convert(procExpression, typeof(Proc)), + callingMethodExpression + ) : + Methods.CreateBfcForProcCall.OpCall( + AstUtils.Convert(procExpression, typeof(Proc)) + ) + ), + Ast.Assign(resultVariable, AstFactory.YieldExpression( + args.GetSimpleArgumentExpressions(), + args.GetSplattedArgumentExpression(), + args.GetRhsArgumentExpression(), + bfcVariable, + selfExpression + )), + Methods.MethodProcCall.OpCall(bfcVariable, resultVariable), + resultVariable + ); + } + + #region Call // TODO: generate + + // Call overloads don't check parameter count, this is done by Proc#call. + + public object Call() { + var blockParam = RubyOps.CreateBfcForProcCall(this); + var result = RubyOps.Yield0(_self, blockParam); + RubyOps.MethodProcCall(blockParam, result); + return result; + } + + public object Call(object arg1) { + var blockParam = RubyOps.CreateBfcForProcCall(this); + + // lambda calls are weird: + var result = (_kind == ProcKind.Lambda) ? + RubyOps.YieldNoAutoSplat1(arg1, _self, blockParam) : + RubyOps.Yield1(arg1, _self, blockParam); + + RubyOps.MethodProcCall(blockParam, result); + return result; + } + + public object Call(object arg1, object arg2) { + var blockParam = RubyOps.CreateBfcForProcCall(this); + var result = RubyOps.Yield2(arg1, arg2, _self, blockParam); + RubyOps.MethodProcCall(blockParam, result); + return result; + } + + public object Call(object arg1, object arg2, object arg3) { + var blockParam = RubyOps.CreateBfcForProcCall(this); + var result = RubyOps.Yield3(arg1, arg2, arg3, _self, blockParam); + RubyOps.MethodProcCall(blockParam, result); + return result; + } + + public object Call(object arg1, object arg2, object arg3, object arg4) { + var blockParam = RubyOps.CreateBfcForProcCall(this); + var result = RubyOps.Yield4(arg1, arg2, arg3, arg4, _self, blockParam); + RubyOps.MethodProcCall(blockParam, result); + return result; + } + + public object Call(params object[]/*!*/ args) { + switch (args.Length) { + case 0: return Call(); + case 1: return Call(args[0]); + case 2: return Call(args[0], args[1]); + case 3: return Call(args[0], args[1], args[2]); + case 4: return Call(args[0], args[1], args[2], args[3]); + } + + var blockParam = RubyOps.CreateBfcForProcCall(this); + var result = RubyOps.YieldN(args, _self, blockParam); + RubyOps.MethodProcCall(blockParam, result); + return result; + } + + public object CallN(object[]/*!*/ args) { + Debug.Assert(args.Length > 4); + var blockParam = RubyOps.CreateBfcForProcCall(this); + var result = RubyOps.YieldN(args, _self, blockParam); + RubyOps.MethodProcCall(blockParam, result); + return result; + } + + #endregion + + #endregion + + #region Block helper methods + + // TODO: remove context, rename to CreateBlock + public static Proc/*!*/ Create(RubyContext/*!*/ context, Func/*!*/ clrMethod) { + return Create(context, (BlockCallTarget1)BlockCallback1, clrMethod, 1); + } + + public static Proc/*!*/ Create(RubyContext/*!*/ context, Func/*!*/ clrMethod) { + return Create(context, (BlockCallTarget2)BlockCallback2, clrMethod, 2); + } + + public static Proc/*!*/ Create(RubyContext/*!*/ context, Func/*!*/ clrMethod) { + return Create(context, (BlockCallTarget3)BlockCallback3, clrMethod, 3); + } + + public static Proc/*!*/ Create(RubyContext/*!*/ context, Delegate/*!*/ clrMethod, object self, int parameterCount) { + // scope is used to get to the execution context: + return new Proc(ProcKind.Block, self, context.EmptyScope, BlockDispatcher.Create(clrMethod, parameterCount, BlockSignatureAttributes.None)); + } + + // The following methods are block implementations, therefore they need to have signature like a block. + // We need to get to the real delegate to call. We capture it into the self object. + public static object BlockCallback1(BlockParam/*!*/ block, object self, object arg0) { + var clrMethod = (Func)self; + return clrMethod(block, arg0); + } + + public static object BlockCallback2(BlockParam/*!*/ block, object self, object arg0, object arg1) { + var clrMethod = (Func)self; + return clrMethod(block, arg0, arg1); + } + + public static object BlockCallback3(BlockParam/*!*/ block, object self, object arg0, object arg1, object arg2) { + var clrMethod = (Func)self; + return clrMethod(block, arg0, arg1, arg2); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/Range.Subclass.cs b/merlin/main/languages/ruby/Ruby/Builtins/Range.Subclass.cs new file mode 100644 index 0000000000..3918d88f2b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/Range.Subclass.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Builtins { + public partial class Range { + public sealed class Subclass : Range, IRubyObject { + private readonly RubyClass/*!*/ _class; + private RubyInstanceData _instanceData; + + // called by Class#new rule when creating a Ruby subclass of String: + public Subclass(RubyClass/*!*/ rubyClass) { + Assert.NotNull(rubyClass); + _class = rubyClass; + } + + private Subclass(Range.Subclass/*!*/ range) + : base(range) { + _class = range._class; + } + + protected override Range/*!*/ Copy() { + return new Subclass(this); + } + + #region IRubyObject Members + + [Emitted] + public RubyClass/*!*/ Class { + get { return _class; } + } + + public RubyInstanceData/*!*/ GetInstanceData() { + return RubyOps.GetInstanceData(ref _instanceData); + } + + public RubyInstanceData TryGetInstanceData() { + return _instanceData; + } + + #endregion + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/Range.cs b/merlin/main/languages/ruby/Ruby/Builtins/Range.cs new file mode 100644 index 0000000000..4131e30ee8 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/Range.cs @@ -0,0 +1,108 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.Serialization; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; +using System.Security.Permissions; + +namespace IronRuby.Builtins { + + public partial class Range : IDuplicable +#if !SILVERLIGHT + , ISerializable +#endif + { + private object _begin; + private object _end; + private bool _excludeEnd; + private bool _initialized; + + public object Begin { get { return _begin; } } + public object End { get { return _end; } } + public bool ExcludeEnd { get { return _excludeEnd; } } + +#if !SILVERLIGHT // SerializationInfo + protected Range(SerializationInfo info, StreamingContext context) { + _begin = info.GetValue("begin", typeof(object)); + _end = info.GetValue("end", typeof(object)); + _excludeEnd = info.GetBoolean("excl"); + _initialized = true; + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] + public void GetObjectData(SerializationInfo info, StreamingContext context) { + info.AddValue("begin", _begin); + info.AddValue("end", _end); + info.AddValue("excl", _excludeEnd); + } +#endif + + protected Range(Range/*!*/ range) { + _begin = range._begin; + _end = range._end; + _excludeEnd = range._excludeEnd; + } + + public Range() { + } + + public Range(int begin, int end, bool excludeEnd) { + _begin = begin; + _end = end; + _excludeEnd = excludeEnd; + _initialized = true; + } + + // Convience function for constructing from C#, calls initialize + public Range(RubyContext/*!*/ context, object begin, object end, bool excludeEnd) { + Initialize(context, begin, end, excludeEnd); + } + + public void Initialize(RubyContext/*!*/ context, object begin, object end, bool excludeEnd) { + if (_initialized) { + throw RubyExceptions.CreateNameError("`initialize' called twice"); + } + + // Range tests whether the items can be compared, and uses that to determine if the range is valid + // Only a non-existent <=> method or a result of nil seems to trigger the exception. + object compareResult = null; + try { + compareResult = RubySites.Compare(context, begin, end); + } catch (Exception) { + } + if (compareResult == null) { + throw RubyExceptions.CreateArgumentError("bad value for range"); + } + + _begin = begin; + _end = end; + _excludeEnd = excludeEnd; + _initialized = true; + } + + protected virtual Range/*!*/ Copy() { + return new Range(this); + } + + // Range doesn't have "initialize_copy", it's entirely initialized in dup: + object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) { + var result = Copy(); + context.CopyInstanceData(this, result, copySingletonMembers); + return result; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyArray.Subclass.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyArray.Subclass.cs new file mode 100644 index 0000000000..0624c5aae7 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyArray.Subclass.cs @@ -0,0 +1,63 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Builtins { + public partial class RubyArray { + public sealed class Subclass : RubyArray, IRubyObject { + private readonly RubyClass/*!*/ _class; + private RubyInstanceData _instanceData; + + // called by Class#new rule when creating a Ruby subclass of String: + public Subclass(RubyClass/*!*/ rubyClass) { + Assert.NotNull(rubyClass); + _class = rubyClass; + } + + private Subclass(RubyArray.Subclass/*!*/ array) + : base(array) { + _class = array._class; + } + + protected override RubyArray/*!*/ Copy() { + return new Subclass(this); + } + + public override RubyArray/*!*/ CreateInstance() { + return new Subclass(_class); + } + + #region IRubyObject Members + + [Emitted] + public RubyClass/*!*/ Class { + get { return _class; } + } + + public RubyInstanceData/*!*/ GetInstanceData() { + return RubyOps.GetInstanceData(ref _instanceData); + } + + public RubyInstanceData TryGetInstanceData() { + return _instanceData; + } + + #endregion + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyArray.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyArray.cs new file mode 100644 index 0000000000..aa18f3dcce --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyArray.cs @@ -0,0 +1,148 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using System.Diagnostics; +using System.Text; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.Builtins { + + /// + /// Array inherits from Object, mixes in Enumerable. + /// Ruby's Array is a List{object}, but it overrides Equals and GetHashCode so + /// .NET dictionaries will hash it correctly. + /// + [DebuggerDisplay("{GetDebugView()}")] + public partial class RubyArray : List, IDuplicable { + #region Construction + + public RubyArray() { + } + + public RubyArray(int capacity) + : base(capacity) { + } + + public RubyArray(IEnumerable/*!*/ collection) + : base(collection) { + } + + public RubyArray(IEnumerable/*!*/ collection) + : base(CollectionUtils.ToEnumerable(collection)) { + } + + public static RubyArray/*!*/ Create(IList/*!*/ list, int start, int count) { + ContractUtils.RequiresNotNull(list, "list"); + ContractUtils.RequiresArrayRange(list, start, count, "start", "count"); + + RubyArray result = new RubyArray(); + for (int i = 0; i < count; i++) { + result.Add(list[start + i]); + } + return result; + } + + public static RubyArray/*!*/ Create(object item) { + var result = new RubyArray(); + result.Add(item); + return result; + } + + /// + /// Creates a blank instance of a RubyArray or its subclass given the Ruby class object. + /// + public static RubyArray/*!*/ CreateInstance(RubyClass/*!*/ rubyClass) { + return (rubyClass.GetUnderlyingSystemType() == typeof(RubyArray)) ? new RubyArray() : new RubyArray.Subclass(rubyClass); + } + + /// + /// Creates a blank instance of self type with no flags set. + /// + public virtual RubyArray/*!*/ CreateInstance() { + return new RubyArray(); + } + + /// + /// Creates a copy of the array that has the same items. + /// Doesn't copy instance data. + /// Preserves the class of the Array. + /// + protected virtual RubyArray/*!*/ Copy() { + return new RubyArray(this); + } + + object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) { + var result = Copy(); + context.CopyInstanceData(this, result, copySingletonMembers); + return result; + } + + #endregion + + public override bool Equals(object obj) { + return Equals(this, obj); + } + + public override int GetHashCode() { + return GetHashCode(this); + } + + public static int GetHashCode(IList/*!*/ self) { + int hash = self.Count; + foreach (object obj in self) { + hash <<= 1; + hash ^= RubyUtils.GetHashCode(obj); + } + return hash; + } + + public static bool Equals(IList/*!*/ self, object obj) { + if (self == obj) { + return true; + } + + IList other = obj as IList; + if (other == null || self.Count != other.Count) { + return false; + } + + for (int i = 0; i < self.Count; ++i) { + if (!RubyUtils.ValueEquals(self[i], other[i])) { + return false; + } + } + return true; + } + + public RubyArray/*!*/ AddMultiple(int count, object value) { + for (int i = 0; i < count; i++) { + Add(value); + } + return this; + } + + #region DebugView + + internal string/*!*/ GetDebugView() { + return RubySites.Inspect(RubyContext._Default, this).ToString(); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyClass.Meta.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyClass.Meta.cs new file mode 100644 index 0000000000..985364c5b8 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyClass.Meta.cs @@ -0,0 +1,56 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Builtins { + + public partial class RubyClass { + public override MetaObject/*!*/ GetMetaObject(Expression/*!*/ parameter) { + return new Meta(parameter, Restrictions.Empty, this); + } + + internal new sealed class Meta : RubyModule.Meta { + public Meta(Expression/*!*/ expression, Restrictions/*!*/ restrictions, RubyClass/*!*/ value) + : base(expression, restrictions, value) { + ContractUtils.RequiresNotNull(value, "value"); + } + + public override MetaObject/*!*/ BindInvokeMember(InvokeMemberBinder/*!*/ binder, params MetaObject/*!*/[]/*!*/ args) { + var self = (RubyClass)Value; + return RubyInvokeMemberBinder.TryBind(self.Context, binder, this, args) ?? binder.FallbackInvokeMember(this, args); + } + + public override MetaObject/*!*/ BindGetMember(GetMemberBinder/*!*/ binder) { + var self = (RubyClass)Value; + return RubyGetMemberBinder.TryBind(self.Context, binder, this) ?? binder.FallbackGetMember(this); + } + + public override IEnumerable GetDynamicMemberNames() { + var self = (RubyClass)Value; + var names = new List(); + self.SingletonClass.ForEachMember(true, RubyMethodAttributes.DefaultVisibility, delegate(string/*!*/ name, RubyMemberInfo/*!*/ member) { + names.Add(name); + }); + return names; + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyClass.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyClass.cs new file mode 100644 index 0000000000..68425e5ea6 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyClass.cs @@ -0,0 +1,595 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Dynamic; +using System.Threading; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler; +using IronRuby.Compiler.Generation; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Ast = System.Linq.Expressions.Expression; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using System.Dynamic.Binders; + +namespace IronRuby.Builtins { + + public sealed partial class RubyClass : RubyModule, IDuplicable { + public const string/*!*/ ClassSingletonName = "__ClassSingleton"; + public const string/*!*/ ClassSingletonSingletonName = "__ClassSingletonSingleton"; + public const string/*!*/ MainSingletonName = "__MainSingleton"; + + private readonly RubyClass _superClass; + + // is this class a singleton class? + private readonly bool _isSingletonClass; + + // an object that the class is a singleton of (reverse _singletonClass pointer): + private readonly object _singletonClassOf; + + // null for singletons: + private Type _underlyingSystemType; + private readonly bool _isRubyClass; + private GlobalScopeExtension _globalScope; + + // if this class is a struct represents its layout: + private RubyStruct.Info _structInfo; + + // whether initialize_copy can be called (the class has just been duplicated): + private bool _isUninitializedCopy; + + private Delegate[] _factories; + + public RubyClass SuperClass { + get { return _superClass; } + } + + public override bool IsClass { + get { return true; } + } + + public override bool IsSingletonClass { + get { return _isSingletonClass; } + } + + public object SingletonClassOf { + get { return _singletonClassOf; } + } + + // A class defined in Ruby code (not libraries, CLR types) + public bool IsRubyClass { + get { return _isRubyClass; } + } + + internal RubyStruct.Info StructInfo { + get { return _structInfo; } + set { _structInfo = value; } + } + + internal override GlobalScopeExtension GlobalScope { + get { return _globalScope; } + } + + internal void SetGlobalScope(GlobalScopeExtension/*!*/ value) { + Assert.NotNull(value); + _globalScope = value; + } + + protected override bool CanIncludeClrInterface { + get { return !IsSingletonClass && (_underlyingSystemType == null); } + } + +#if FALSE + private Type _extensionType; + + /// + /// Holds on to the type with extension members, if any + /// Used by the DLR action binder & binder helpers. + /// See RubyActionBinder.GetExtensionTypes + /// + public Type ExtensionType { + get { return _extensionType; } + internal set { _extensionType = value; } + } +#endif + + public Type/*!*/ GetUnderlyingSystemType() { + if (_isSingletonClass) { + throw new InvalidOperationException("Singleton class doesn't have underlying system type."); + } + + if (_underlyingSystemType == null) { + Interlocked.Exchange(ref _underlyingSystemType, RubyTypeDispenser.GetOrCreateType(_superClass.GetUnderlyingSystemType(), GetClrInterfaces())); + } + + Debug.Assert(_underlyingSystemType != null); + return _underlyingSystemType; + } + + private PropertyInfo/*!*/ UnderlyingSystemTypeProperty { + get { return typeof(RubyClass).GetProperty("UnderlyingSystemType"); } + } + + public Delegate[] Factories { + get { return _factories; } + set { _factories = value; } + } + + // default allocator: + public RubyClass(RubyClass/*!*/ rubyClass) + : this(rubyClass.Context, null, null, null, null, rubyClass.Context.ObjectClass, null, true, false) { + + // all modules need a singleton (see RubyContext.CreateModule): + rubyClass.Context.CreateDummySingletonClassFor(this, rubyClass, null); + } + + // friend: RubyContext + // tracker: non-null => show members declared on the tracker + internal RubyClass(RubyContext/*!*/ context, string name, Type type, object singletonClassOf, Action initializer, + RubyClass superClass, TypeTracker tracker, bool isRubyClass, bool isSingletonClass) + : base(context, name, initializer, null, tracker) { + + Debug.Assert((superClass == null) == (type == typeof(object)), "All classes have a superclass, except for Object"); + Debug.Assert(!isRubyClass || tracker == null, "Ruby class cannot have a tracker"); + Debug.Assert(singletonClassOf != null || !isSingletonClass, "Singleton classes don't have a type"); + Debug.Assert(superClass != this); + + _underlyingSystemType = type; + _superClass = superClass; + _isSingletonClass = isSingletonClass; + _isRubyClass = isRubyClass; + _singletonClassOf = singletonClassOf; + + if (superClass != null) { + superClass.AddDependentModule(this); + _structInfo = superClass._structInfo; + } + } + + internal override void EnsureInitialized() { + base.EnsureInitialized(); + if (_superClass != null) { + _superClass.EnsureInitialized(); + } + } + + object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) { + if (IsSingletonClass) { + throw RubyExceptions.CreateTypeError("can't copy singleton class"); + } + + RubyClass result = Duplicate(null); + + result._isUninitializedCopy = true; + return result; + } + + public void InitializeClassCopy(RubyClass/*!*/ rubyClass) { + if (!_isUninitializedCopy) { + throw RubyExceptions.CreateTypeError("already initialized class"); + } + _isUninitializedCopy = false; + + InitializeModuleCopy(rubyClass); + + // MRI clears the name in initialize_copy: + Name = null; + } + + /// + /// Duplicates this class object. When duplicating singleton class a new "singletonClassOf" reference needs to be provided. + /// + internal RubyClass/*!*/ Duplicate(object singletonClassOf) { + Type type; + bool isRubyClass; + + // Notes: + // - MRI duplicates Object class so that the result doesn't inherit from Object, + // which we don't do (we want our class hierarchy to be rooted). + if (this != Context.ObjectClass) { + isRubyClass = IsRubyClass; + type = _underlyingSystemType; + } else { + isRubyClass = true; + type = null; + } + + RubyClass result = Context.CreateClass(Name, type, singletonClassOf, null, null, + _superClass ?? Context.ObjectClass, null, isRubyClass, IsSingletonClass + ); + + result._structInfo = _structInfo; + + // copy factories so that the class can be instantiated: + result._factories = (_factories != null) ? ArrayUtils.Copy(_factories) : _factories; + + if (!IsSingletonClass) { + // singleton members are copied here, not in InitializeCopy: + result.SingletonClass.InitializeMembersFrom(SingletonClass); + + // copy instance variables, and frozen, taint flags: + Context.CopyInstanceData(this, result, false, true, false); + } + + // members initialized in InitializeClassFrom (invoked by "initialize_copy") + return result; + } + + // TODO: public due to partial trust + // implements Class#new + public static object CreateAnonymousClass(RubyScope/*!*/ scope, BlockParam body, RubyClass/*!*/ self, [Optional]RubyClass superClass) { + RubyContext ec = scope.RubyContext; + RubyModule owner = scope.GetInnerMostModule(); + + // MRI is inconsistent here, it triggers "inherited" event after the body of the method is evaluated. + // In all other cases the order is event first, body next. + RubyClass newClass = ec.DefineClass(owner, null, superClass ?? ec.ObjectClass); + return (body != null) ? RubyUtils.EvaluateInModule(newClass, body, newClass) : newClass; + } + + public override string/*!*/ ToString() { + return Name; + } + + protected override bool ForEachAncestor(Func/*!*/ action) { + // walk up the class hierarchy: + for (RubyClass c = this; c != null; c = c._superClass) { + if (c.ForEachDeclaredAncestor(action)) return true; + } + return false; + } + + internal override bool IsSuperClassAncestor(RubyModule/*!*/ module) { + return _superClass != null && _superClass.HasAncestor(module); + } + + public bool IsSubclassOf(RubyClass/*!*/ super) { + Assert.NotNull(super); + + RubyClass c = this; + do { + if (c == super) return true; + c = c.SuperClass; + } while (c != null); + + return false; + } + + public bool IsException() { + return IsSubclassOf(Context.ExceptionClass); + } + + public RubyClass/*!*/ GetNonSingletonClass() { + RubyClass result = this; + while (result != null && result.IsSingletonClass) { + result = result._superClass; + } + return result; + } + + public override RubyMemberInfo ResolveMethodFallbackToObject(string/*!*/ name, bool includeMethod) { + // Note: all classes include Object in ancestors, so we don't need to search there. + return ResolveMethod(name, includeMethod); + } + + #region CLR Member Lookup + + /// + /// Stores unsucessfull look-ups of CLR type members. + /// Reflection is not efficient at caching look-ups and we do multiple of them (due to mangling) each time a method is being resolved. + /// + private static Dictionary _clrFailedMemberLookupCache + = new Dictionary(); + + private struct MemberLookupCacheEntry : IEquatable { + private readonly Type/*!*/ _type; + private readonly string/*!*/ _methodName; + + public MemberLookupCacheEntry(Type/*!*/ type, string/*!*/ methodName) { + _type = type; + _methodName = methodName; + } + + public override int GetHashCode() { + return _type.GetHashCode() ^ _methodName.GetHashCode(); + } + + public bool Equals(MemberLookupCacheEntry other) { + return _type == other._type && _methodName == other._methodName; + } + } + + private static bool IsFailureCached(Type/*!*/ type, string/*!*/ methodName) { + // check for cached lookup failure (if the cache is available): + bool result = false; + var cache = Interlocked.Exchange(ref _clrFailedMemberLookupCache, null); + if (cache != null) { + result = cache.ContainsKey(new MemberLookupCacheEntry(type, methodName)); + Interlocked.Exchange(ref _clrFailedMemberLookupCache, cache); + } + +#if DEBUG + PerfTrack.NoteEvent(PerfTrack.Categories.Count, "CLR member lookup failure cache " + (result ? "hit" : "miss")); +#endif + return result; + } + + private static void CacheFailure(Type/*!*/ type, string/*!*/ methodName) { + // store failure to the cache if the cache is not owned by another thread: + var cache = Interlocked.Exchange(ref _clrFailedMemberLookupCache, null); + if (cache != null) { + cache[new MemberLookupCacheEntry(type, methodName)] = true; + Interlocked.Exchange(ref _clrFailedMemberLookupCache, cache); + } + } + + protected override bool TryGetClrMember(Type/*!*/ type, string/*!*/ name, out RubyMemberInfo method) { + string unmangled; + + if (IsFailureCached(type, name)) { + method = null; + return false; + } + + BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; + bindingFlags |= (_isSingletonClass) ? BindingFlags.Static : BindingFlags.Instance; + + if (name.EndsWith("=")) { + string propertyName = name.Substring(0, name.Length - 1); + + // property setter: + if (TryGetClrMethod(type, bindingFlags, "set_" + propertyName, out method)) return true; + unmangled = RubyUtils.TryUnmangleName(propertyName); + if (unmangled != null && TryGetClrMethod(type, bindingFlags, "set_" + unmangled, out method)) return true; + + // writeable field: + if (TryGetClrField(type, bindingFlags, propertyName, true, out method)) return true; + if (unmangled != null && TryGetClrField(type, bindingFlags, unmangled, true, out method)) return true; + } else { + // method: + if (TryGetClrMethod(type, bindingFlags, name, out method)) return true; + unmangled = RubyUtils.TryUnmangleName(name); + if (unmangled != null && TryGetClrMethod(type, bindingFlags, unmangled, out method)) return true; + + // getter: + if (TryGetClrMethod(type, bindingFlags, "get_" + name, out method)) return true; + if (unmangled != null && TryGetClrMethod(type, bindingFlags, "get_" + unmangled, out method)) return true; + + // event: + if (TryGetClrEvent(type, bindingFlags, name, out method)) return true; + if (unmangled != null && TryGetClrEvent(type, bindingFlags, unmangled, out method)) return true; + + // field: + if (TryGetClrField(type, bindingFlags, name, false, out method)) return true; + if (unmangled != null && TryGetClrField(type, bindingFlags, unmangled, false, out method)) return true; + } + + CacheFailure(type, name); + + method = null; + return false; + } + + private bool TryGetClrMethod(Type/*!*/ type, BindingFlags bindingFlags, string/*!*/ name, out RubyMemberInfo method) { + Assert.NotNull(type, name); + + MemberInfo[] members = type.GetMember(name, MemberTypes.Method, bindingFlags | BindingFlags.InvokeMethod); + if (members.Length > 0) { + method = new RubyMethodGroupInfo(SelectNonPrivateMethods(members), this, _isSingletonClass); + return true; + } else { + method = null; + return false; + } + } + + private IList/*!*/ SelectNonPrivateMethods(MemberInfo[]/*!*/ members) { + var result = new List(members.Length); + for (int i = 0; i < members.Length; i++) { + var method = (MethodBase)members[i]; + if (!method.IsPrivate) { + result.Add(method); + } + } + return result; + } + + private bool TryGetClrField(Type/*!*/ type, BindingFlags bindingFlags, string/*!*/ name, bool isWrite, out RubyMemberInfo method) { + Assert.NotNull(type, name); + + FieldInfo fieldInfo = type.GetField(name, bindingFlags); + if (fieldInfo != null && !fieldInfo.IsPrivate && (!isWrite || !fieldInfo.IsInitOnly && !fieldInfo.IsLiteral)) { + method = new RubyFieldInfo(fieldInfo, RubyMemberFlags.Public, this, isWrite); + return true; + } + + method = null; + return false; + } + + private bool TryGetClrEvent(Type/*!*/ type, BindingFlags bindingFlags, string/*!*/ name, out RubyMemberInfo method) { + Assert.NotNull(type, name); + + EventInfo eventInfo = type.GetEvent(name, bindingFlags); + if (eventInfo != null) { + method = new RubyEventInfo(eventInfo, RubyMemberFlags.Public, this); + return true; + } + + method = null; + return false; + } + + #endregion + + #region Dynamic operations + + /// + /// Implements Class#allocate feature. + /// + public void BuildObjectAllocation(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName) { + // check for empty arguments (handles splat correctly): + var argsBuilder = new ArgsBuilder(0, 0, 0, false); + argsBuilder.AddCallArguments(metaBuilder, args); + + if (!metaBuilder.Error) { + metaBuilder.Result = MakeAllocatorCall(args, () => Ast.Constant(Name)); + } + } + + /// + /// Implements Class#new feature. + /// + public void BuildObjectConstruction(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName) { + Debug.Assert(!IsSingletonClass, "Cannot instantiate singletons"); + + Type type = GetUnderlyingSystemType(); + + RubyMemberInfo initializer = ResolveMethod(Symbols.Initialize, true).InvalidateSitesOnOverride(); + RubyMethodInfo overriddenInitializer = initializer as RubyMethodInfo; + + // check the version of this class to ensure the initializer won't be changed without rebuilding the site: + metaBuilder.AddCondition( + Ast.Equal(Ast.Property(Ast.Constant(this), RubyModule.VersionProperty), Ast.Constant(this.Version)) + ); + + // Initializer is overridden => initializer is invoked on an uninitialized instance. + // Is user class (defined in Ruby code) => construct it as if it had initializer that calls super immediately + // (we need to "inherit" factories/constructors from the base class (e.g. class S < String; self; end.new('foo')). + if (overriddenInitializer != null || (_isRubyClass && _structInfo == null)) { + metaBuilder.Result = MakeAllocatorCall(args, () => Ast.Constant(Name)); + + if (overriddenInitializer != null || (_isRubyClass && initializer != null && !initializer.IsEmpty)) { + BuildOverriddenInitializerCall(metaBuilder, args, initializer); + } + } else if (type.IsSubclassOf(typeof(Delegate))) { + metaBuilder.Result = MakeDelegateConstructorCall(type, args); + } else { + MethodBase[] constructionOverloads; + + bool includeSelf; + if (_structInfo != null) { + constructionOverloads = new MethodBase[] { Methods.CreateStructInstance }; + includeSelf = true; + } else if (_factories != null) { + constructionOverloads = (MethodBase[])ReflectionUtils.GetMethodInfos(_factories); + includeSelf = true; + } else { + constructionOverloads = type.GetConstructors(); + if (constructionOverloads.Length == 0) { + throw RubyExceptions.CreateTypeError(String.Format("allocator undefined for {0}", Name)); + } + includeSelf = false; + } + + RubyMethodGroupInfo.BuildCallNoFlow(metaBuilder, args, methodName, constructionOverloads, includeSelf, false); + RubyMethodGroupInfo.ApplyBlockFlowHandlingInternal(metaBuilder, args); + } + } + + private static void BuildOverriddenInitializerCall(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, RubyMemberInfo/*!*/ initializer) { + var instanceExpr = metaBuilder.Result; + metaBuilder.Result = null; + + var instanceVariable = metaBuilder.GetTemporary(instanceExpr.Type, "#instance"); + + // We know an exact type of the new instance and that there is no singleton for that instance. + // We also have the exact method we need to call ("initialize" is a RubyMethodInfo). + // => no tests are necessary: + args.SetTarget(instanceVariable, null); + + if (initializer is RubyMethodInfo) { + initializer.BuildCall(metaBuilder, args, Symbols.Initialize); + } else { + // TODO: we need more refactoring of RubyMethodGroupInfo.BuildCall to be able to inline this: + metaBuilder.Result = Ast.Dynamic( + RubyCallAction.Make("initialize", + new RubyCallSignature(args.Signature.ArgumentCount, args.Signature.Flags | RubyCallFlags.HasImplicitSelf) + ), + typeof(object), + args.GetCallSiteArguments(instanceVariable) + ); + } + + if (!metaBuilder.Error) { + // PropagateRetrySingleton(instance = new (), instance.initialize()) + metaBuilder.Result = Methods.PropagateRetrySingleton.OpCall( + Ast.Assign(instanceVariable, instanceExpr), + metaBuilder.Result + ); + RubyMethodInfo.ApplyBlockFlowHandlingInternal(metaBuilder, args); + } + } + + public Expression/*!*/ MakeAllocatorCall(CallArguments/*!*/ args, Func/*!*/ defaultExceptionMessage) { + Type type = GetUnderlyingSystemType(); + + if (_structInfo != null) { + return Methods.AllocateStructInstance.OpCall(AstUtils.Convert(args.TargetExpression, typeof(RubyClass))); + } + + if (type.IsSubclassOf(typeof(Delegate))) { + return MakeDelegateConstructorCall(type, args); + } + + ConstructorInfo ctor; + if (IsException()) { + if ((ctor = type.GetConstructor(new[] { typeof(string) })) != null) { + return Ast.New(ctor, defaultExceptionMessage()); + } else if ((ctor = type.GetConstructor(new[] { typeof(string), typeof(Exception) })) != null) { + return Ast.New(ctor, defaultExceptionMessage(), Ast.Constant(null)); + } + } + + if ((ctor = type.GetConstructor(new[] { typeof(RubyClass) })) != null) { + return Ast.New(ctor, AstUtils.Convert(args.TargetExpression, typeof(RubyClass))); + } + + if ((ctor = type.GetConstructor(new[] { typeof(RubyContext) })) != null) { + return Ast.New(ctor, args.ContextExpression); + } + + if ((ctor = type.GetConstructor(Type.EmptyTypes)) != null) { + return Ast.New(ctor); + } + + throw RubyExceptions.CreateTypeError(String.Format("allocator undefined for {0}", Name)); + } + + private Expression/*!*/ MakeDelegateConstructorCall(Type/*!*/ type, CallArguments/*!*/ args) { + if (args.Signature.HasBlock) { + return Methods.CreateDelegateFromProc.OpCall( + Ast.Constant(type), + AstUtils.Convert(args.GetBlockExpression(), typeof(Proc)) + ); + } else { + // TODO: + throw new NotImplementedError("no block given"); + } + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyEncoding.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyEncoding.cs new file mode 100644 index 0000000000..6a4e56a4f0 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyEncoding.cs @@ -0,0 +1,292 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; +using System.Threading; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + + /// + /// All encodings in Ruby are represented by instances of a single class. Therefore we need to wrap .NET Encoding class in RubyEncoding. + /// Instances of this class are app-domain singletons. That's all right as far as the class is readonly and doesn't implement IRubyObject. + /// Taint, frozen flags and instance variables need to be stored in per-runtime lookaside table. + /// + public class RubyEncoding { + public static readonly RubyEncoding/*!*/ Binary = new RubyEncoding(BinaryEncoding.Instance); + public static readonly RubyEncoding/*!*/ UTF8 = new RubyEncoding(Encoding.UTF8); + public static readonly RubyEncoding/*!*/ Default = new RubyEncoding(StringUtils.DefaultEncoding); + + private static Dictionary _Encodings; + + private readonly Encoding/*!*/ _encoding; + + private RubyEncoding(Encoding/*!*/ encoding) { + ContractUtils.RequiresNotNull(encoding, "encoding"); + _encoding = encoding; + } + + public Encoding/*!*/ Encoding { + get { return _encoding; } + } + + public string/*!*/ Name { + get { return _encoding.WebName; } + } + + /// Unknown encoding. + public static Encoding/*!*/ GetEncodingByRubyName(string/*!*/ name) { + ContractUtils.RequiresNotNull(name, "name"); + + switch (name.ToLower()) { + case "binary": + case "ascii": + case "ascii-8bit": return BinaryEncoding.Instance; +#if SILVERLIGHT + case "utf-8": return Encoding.UTF8; + default: throw new ArgumentException(String.Format("Unknown encoding: '{0}'", name)); +#else + default: return Encoding.GetEncoding(name); +#endif + } + } + + public static RubyEncoding/*!*/ GetRubyEncoding(string/*!*/ name) { + return GetRubyEncoding(GetEncodingByRubyName(name)); + } + +#if SILVERLIGHT + public static RubyEncoding/*!*/ GetRubyEncoding(Encoding/*!*/ encoding) { + ContractUtils.RequiresNotNull(encoding, "encoding"); + if (encoding == BinaryEncoding.Instance) { + return Binary; + } else if (encoding == Encoding.UTF8) { + return UTF8; + } else { + throw new ArgumentException(String.Format("Unknown encoding: '{0}'", encoding)); + } + } + + internal static RubyEncoding/*!*/ GetRubyEncoding(int codepage) { + switch (codepage) { + case 0: return Binary; + case 65001: return UTF8; + default: throw new ArgumentException(String.Format("Unknown encoding codepage: {0}", codepage)); + } + } +#else + internal static RubyEncoding/*!*/ GetRubyEncoding(int codepage) { + return GetRubyEncoding(null, codepage); + } + + public static RubyEncoding/*!*/ GetRubyEncoding(Encoding/*!*/ encoding) { + ContractUtils.RequiresNotNull(encoding, "encoding"); + return GetRubyEncoding(encoding, encoding.CodePage); + } + + private static RubyEncoding/*!*/ GetRubyEncoding(Encoding encoding, int codepage) { + switch (codepage) { + case 0: return Binary; + case 65001: return UTF8; + } + + if (_Encodings == null) { + Interlocked.CompareExchange(ref _Encodings, new Dictionary(), null); + } + + RubyEncoding result; + lock (_Encodings) { + if (!_Encodings.TryGetValue(codepage, out result)) { + _Encodings.Add(codepage, result = new RubyEncoding(encoding ?? Encoding.GetEncoding(codepage))); + } + } + + return result; + } +#endif + + internal static Encoding/*!*/ GetEncoding(int codepage) { + switch (codepage) { + case 0: return BinaryEncoding.Instance; +#if SILVERLIGHT + default: throw Assert.Unreachable; +#else + default: return Encoding.GetEncoding(codepage); +#endif + } + } + + internal static int GetCodePage(Encoding/*!*/ encoding) { + Debug.Assert(encoding != null); + + if (encoding == BinaryEncoding.Instance) { + return 0; + } + +#if SILVERLIGHT + if (encoding == BinaryEncoding.UTF8) { + return 65001; + } + + throw Assert.Unreachable; +#else + return encoding.CodePage; +#endif + } + + internal static Encoding/*!*/ GetDefaultHostEncoding(RubyCompatibility compatibility) { + return (compatibility >= RubyCompatibility.Ruby19) ? Encoding.UTF8 : BinaryEncoding.Instance; + } + +#if !SILVERLIGHT + public static bool IsAsciiIdentity(Encoding/*!*/ encoding) { + if (encoding == BinaryEncoding.Instance) { + return true; + } + + switch (encoding.CodePage) { + case 437: // OEM United States + case 708: // Arabic (ASMO 708) + case 720: // Arabic (DOS) + case 737: // Greek (DOS) + case 775: // Baltic (DOS) + case 850: // Western European (DOS) + case 852: // Central European (DOS) + case 855: // OEM Cyrillic + case 857: // Turkish (DOS) + case 858: // OEM Multilingual Latin I + case 860: // Portuguese (DOS) + case 861: // Icelandic (DOS) + case 862: // Hebrew (DOS) + case 863: // French Canadian (DOS) + case 864: // Arabic (864) + case 865: // Nordic (DOS) + case 866: // Cyrillic (DOS) + case 869: // Greek, Modern (DOS) + case 874: // Thai (Windows) + case 932: // Japanese (Shift-JIS) + case 936: // Chinese Simplified (GB2312) + case 949: // Korean + case 950: // Chinese Traditional (Big5) + case 1250: // Central European (Windows) + case 1251: // Cyrillic (Windows) + case 1252: // Western European (Windows) + case 1253: // Greek (Windows) + case 1254: // Turkish (Windows) + case 1255: // Hebrew (Windows) + case 1256: // Arabic (Windows) + case 1257: // Baltic (Windows) + case 1258: // Vietnamese (Windows) + case 1361: // Korean (Johab) + case 10000: // Western European (Mac) + case 10001: // Japanese (Mac) + case 10002: // Chinese Traditional (Mac) + case 10003: // Korean (Mac) + case 10004: // Arabic (Mac) + case 10005: // Hebrew (Mac) + case 10006: // Greek (Mac) + case 10007: // Cyrillic (Mac) + case 10008: // Chinese Simplified (Mac) + case 10010: // Romanian (Mac) + case 10017: // Ukrainian (Mac) + case 10029: // Central European (Mac) + case 10079: // Icelandic (Mac) + case 10081: // Turkish (Mac) + case 10082: // Croatian (Mac) + case 20000: // Chinese Traditional (CNS) + case 20001: // TCA Taiwan + case 20002: // Chinese Traditional (Eten) + case 20003: // IBM5550 Taiwan + case 20004: // TeleText Taiwan + case 20005: // Wang Taiwan + case 20127: // US-ASCII + case 20866: // Cyrillic (KOI8-R) + case 20932: // Japanese (JIS 0208-1990 and 0212-1990) + case 20936: // Chinese Simplified (GB2312-80) + case 20949: // Korean Wansung + case 21866: // Cyrillic (KOI8-U) + case 28591: // Western European (ISO) + case 28592: // Central European (ISO) + case 28593: // Latin 3 (ISO) + case 28594: // Baltic (ISO) + case 28595: // Cyrillic (ISO) + case 28596: // Arabic (ISO) + case 28597: // Greek (ISO) + case 28598: // Hebrew (ISO-Visual) + case 28599: // Turkish (ISO) + case 28603: // Estonian (ISO) + case 28605: // Latin 9 (ISO) + case 38598: // Hebrew (ISO-Logical) + case 50220: // Japanese (JIS) + case 50221: // Japanese (JIS-Allow 1 byte Kana) + case 50222: // Japanese (JIS-Allow 1 byte Kana - SO/SI) + case 50225: // Korean (ISO) + case 50227: // Chinese Simplified (ISO-2022) + case 51932: // Japanese (EUC) + case 51936: // Chinese Simplified (EUC) + case 51949: // Korean (EUC) + case 54936: // Chinese Simplified (GB18030) + case 57002: // ISCII Devanagari + case 57003: // ISCII Bengali + case 57004: // ISCII Tamil + case 57005: // ISCII Telugu + case 57006: // ISCII Assamese + case 57007: // ISCII Oriya + case 57008: // ISCII Kannada + case 57009: // ISCII Malayalam + case 57010: // ISCII Gujarati + case 57011: // ISCII Punjabi + case 65001: // Unicode (UTF-8) + Debug.Assert(IsAsciiIdentityFallback(encoding)); + return true; + + default: + return IsAsciiIdentityFallback(encoding); + } + } + + private static string _AllAscii; + + private static bool IsAsciiIdentityFallback(Encoding/*!*/ encoding) { + if (_AllAscii == null) { + // all ASCII characters: + var sb = new StringBuilder(0x80); + for (int i = 0; i < 0x80; i++) { + sb.Append((char)i); + } + _AllAscii = sb.ToString(); + } + + var bytes = encoding.GetBytes(_AllAscii); + if (bytes.Length != _AllAscii.Length) { + return false; + } + + for (int i = 0; i < _AllAscii.Length; i++) { + if ((int)_AllAscii[i] != (int)bytes[i]) { + return false; + } + } + + return true; + } +#endif + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyInputProvider.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyInputProvider.cs new file mode 100644 index 0000000000..ace262277c --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyInputProvider.cs @@ -0,0 +1,98 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Threading; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + public sealed class RubyInputProvider { + private readonly RubyContext/*!*/ _context; + + // $<. ARGF + private object/*!*/ _singleton; + + // $*, ARGV + private readonly RubyArray/*!*/ _commandLineArguments; + + // $. + private int _lastInputLineNumber; + + internal RubyInputProvider(RubyContext/*!*/ context, ICollection/*!*/ arguments) { + Assert.NotNull(context); + Assert.NotNullItems(arguments); + _context = context; + + var args = new RubyArray(); + foreach (var arg in arguments) { + ExpandArgument(args, arg); + } + + _commandLineArguments = args; + _lastInputLineNumber = 1; + _singleton = new object(); + } + + public RubyContext/*!*/ Context { + get { return _context; } + } + + public object/*!*/ Singleton { + get { return _singleton; } + // set by environment initializer: + internal set { + Assert.NotNull(value); + _singleton = value; + } + } + + public RubyArray/*!*/ CommandLineArguments { + get { return _commandLineArguments; } + } + + public int LastInputLineNumber { + get { return _lastInputLineNumber; } + set { _lastInputLineNumber = value; } + } + + public MutableString/*!*/ CurrentFileName { + get { + // TODO: + return MutableString.Create("-"); + } + } + + public void IncrementLastInputLineNumber() { + Interlocked.Increment(ref _lastInputLineNumber); + } + + private void ExpandArgument(RubyArray/*!*/ args, string/*!*/ arg) { + if (arg.IndexOf('*') != -1 || arg.IndexOf('?') != -1) { + bool added = false; + foreach (string path in Glob.GlobResults(_context, arg, 0)) { + args.Add(MutableString.Create(path)); + added = true; + } + + if (!added) { + args.Add(MutableString.Create(arg)); + } + } else { + args.Add(MutableString.Create(arg)); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyMethod.Meta.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyMethod.Meta.cs new file mode 100644 index 0000000000..f1ef0d9e6b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyMethod.Meta.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Dynamic; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Builtins { + using Ast = System.Linq.Expressions.Expression; + using IronRuby.Compiler; + + public partial class RubyMethod : IDynamicObject { + public MetaObject/*!*/ GetMetaObject(Expression/*!*/ parameter) { + return new Meta(parameter, Restrictions.Empty, this); + } + + internal sealed class Meta : MetaObject { + public RubyMethod/*!*/ Method { + get { return (RubyMethod)Value; } + } + + public Meta(Expression/*!*/ expression, Restrictions/*!*/ restrictions, RubyMethod/*!*/ value) + : base(expression, restrictions, value) { + ContractUtils.RequiresNotNull(value, "value"); + } + + public override MetaObject/*!*/ BindInvoke(InvokeBinder/*!*/ action, MetaObject/*!*/[]/*!*/ args) { + RubyCallSignature callSignature; + if (RubyCallSignature.TryCreate(action.Arguments, out callSignature)) { + return action.FallbackInvoke(this, args); + } + + var self = (RubyMethod)Value; + + var context = new MetaObject( + Methods.GetContextFromMethod.OpCall(AstUtils.Convert(Expression, typeof(RubyMethod))), + Restrictions.Empty, + RubyOps.GetContextFromMethod(self) + ); + + var metaBuilder = new MetaObjectBuilder(); + Method.SetRuleForCall(metaBuilder, new CallArguments(context, this, args, callSignature)); + return metaBuilder.CreateMetaObject(action, args); + } + + public override MetaObject/*!*/ BindConvert(ConvertBinder/*!*/ action) { + var result = RubyBinder.TryBindCovertToDelegate(action, this); + if (result != null) { + return result; + } + + return base.BindConvert(action); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyMethod.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyMethod.cs new file mode 100644 index 0000000000..119606a0ea --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyMethod.cs @@ -0,0 +1,77 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Reflection; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime.Calls; +using Ast = System.Linq.Expressions.Expression; +using System.Dynamic.Binders; + +namespace IronRuby.Builtins { + + public partial class RubyMethod { + private readonly object _target; + private readonly string/*!*/ _name; + private readonly RubyMemberInfo/*!*/ _info; + + public object Target { + get { return _target; } + } + + public RubyMemberInfo/*!*/ Info { + get { return _info; } + } + + public string/*!*/ Name { + get { return _name; } + } + + public RubyMethod(object target, RubyMemberInfo/*!*/ info, string/*!*/ name) { + ContractUtils.RequiresNotNull(info, "info"); + ContractUtils.RequiresNotNull(name, "name"); + + _target = target; + _info = info; + _name = name; + } + + public RubyClass/*!*/ GetTargetClass() { + return _info.Context.GetClassOf(_target); + } + + #region Dynamic Operations + + internal void SetRuleForCall(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + Assert.NotNull(metaBuilder, args); + Debug.Assert(args.Target == this); + + // TODO: we could compare infos here: + // first argument must be this method: + metaBuilder.AddRestriction(Ast.Equal(args.TargetExpression, Ast.Constant(this))); + + // set the target (becomes self in the called method): + args.SetTarget(Ast.Constant(_target), _target); + + _info.BuildCall(metaBuilder, args, _name); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyModule.Meta.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyModule.Meta.cs new file mode 100644 index 0000000000..b3f3367f85 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyModule.Meta.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Dynamic.Binders; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Builtins { + + public partial class RubyModule : IDynamicObject { + public virtual MetaObject/*!*/ GetMetaObject(Expression/*!*/ parameter) { + return new Meta(parameter, Restrictions.Empty, this); + } + + internal class Meta : MetaObject { + public Meta(Expression/*!*/ expression, Restrictions/*!*/ restrictions, RubyModule/*!*/ value) + : base(expression, restrictions, value) { + ContractUtils.RequiresNotNull(value, "value"); + } + + // TODO: GetMember, SetMember, Call + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyModule.Subclass.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyModule.Subclass.cs new file mode 100644 index 0000000000..56cfda4e40 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyModule.Subclass.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Builtins { + public partial class RubyModule { + public sealed class Subclass : RubyModule, IRubyObject { + private readonly RubyClass/*!*/ _class; + private RubyInstanceData _instanceData; + + // called by Class#new rule when creating a Ruby subclass: + public Subclass(RubyClass/*!*/ rubyClass) + : this(rubyClass, null) { + } + + // called by Class#new rule when creating a Ruby subclass: + internal Subclass(RubyClass/*!*/ rubyClass, string name) + : base(rubyClass, name) { + Assert.NotNull(rubyClass); + _class = rubyClass; + } + + protected override RubyModule/*!*/ CreateInstance(string name) { + return new Subclass(_class, name); + } + + #region IRubyObject Members + + [Emitted] + public RubyClass/*!*/ Class { + get { return _class; } + } + + public RubyInstanceData/*!*/ GetInstanceData() { + return RubyOps.GetInstanceData(ref _instanceData); + } + + public RubyInstanceData TryGetInstanceData() { + return _instanceData; + } + + #endregion + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyModule.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyModule.cs new file mode 100644 index 0000000000..b60c9c660a --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyModule.cs @@ -0,0 +1,1257 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Threading; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler; +using IronRuby.Compiler.Generation; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using MSA = System.Linq.Expressions; +using Ast = System.Linq.Expressions.Expression; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; + +namespace IronRuby.Builtins { + + // TODO: freezing +#if DEBUG + [DebuggerDisplay("{DebugName}")] +#endif + public partial class RubyModule : IDuplicable { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")] + public static readonly RubyModule[]/*!*/ EmptyArray = new RubyModule[0]; + + private enum State { + Uninitialized, + Initializing, + Initialized + } + + private static int _globalVersion = 0; + + // TODO: more versions: constants, methods, etc. + private int _version; + + // TODO: debug only + private int _updateCounter; // number of updates of this module + + private readonly RubyContext/*!*/ _context; + + // non-null after primitive classes initialized: + private RubyClass _singletonClass; + + // null means lookup the execution context's global namespace + private Dictionary _constants; + + private Dictionary _methods; + private Dictionary _classVariables; + + /// + /// The entire list of modules included in this one. Newly-added mixins are at the front of the array. + /// When adding a module that itself contains other modules, Ruby tries to maintain the ordering of the + /// contained modules so that method resolution is reasonably consistent. + /// + private RubyModule[]/*!*/ _mixins; + + private string _name; + private State _state; + private Action _initializer; + + // namespace constants: + private IAttributesCollection _clrConstants; + + // need to know if this is an interface module + private readonly TypeTracker _tracker; + + /// + /// A list of dependent modules. Forms a DAG. + /// TODO: Use weak references. + /// + private List/*!*/ _dependentModules = new List(); + +#if DEBUG + private string _debugName; + public string DebugName { get { return _debugName ?? _name ?? ""; } set { _debugName = value; } } +#endif + public TypeTracker Tracker { + get { return _tracker; } + } + + public RubyClass/*!*/ SingletonClass { + get { + Debug.Assert(_singletonClass != null); + return _singletonClass; + } + // friend: RubyContext:InitializePrimitives + internal set { + Assert.NotNull(value); + _singletonClass = value; + } + } + + public bool IsDummySingletonClass { + get { return ReferenceEquals(_singletonClass, this); } + } + + [Emitted] + public int Version { + get { return _version; } + } + + internal static PropertyInfo/*!*/ VersionProperty { + get { return typeof(RubyModule).GetProperty("Version"); } + } + + public string Name { + get { return _name; } + internal set { _name = value; } + } + + public virtual bool IsSingletonClass { + get { return false; } + } + + public RubyContext/*!*/ Context { + [Emitted] + get { return _context; } + } + + public virtual bool IsClass { + get { return false; } + } + + public bool IsInterface { + get { return _tracker != null && _tracker.Type.IsInterface; } + } + + private bool DeclaresGlobalConstants { + get { return ReferenceEquals(this, _context.ObjectClass); } + } + + internal virtual GlobalScopeExtension GlobalScope { + get { return null; } + } + + public RubyModule[]/*!*/ Mixins { + get { return _mixins; } + } + + // default allocator: + public RubyModule(RubyClass/*!*/ rubyClass) + : this(rubyClass, null) { + } + + // creates an empty module: + protected RubyModule(RubyClass/*!*/ rubyClass, string name) + : this(rubyClass.Context, name, null, null, null) { + + // all modules need a singleton (see RubyContext.CreateModule): + rubyClass.Context.CreateDummySingletonClassFor(this, rubyClass, null); + } + + internal RubyModule(RubyContext/*!*/ context, string name, Action initializer, + IAttributesCollection clrConstants, TypeTracker tracker) { + Assert.NotNull(context); + + _context = context; + _name = name; + _state = State.Uninitialized; + _mixins = EmptyArray; + _initializer = initializer; + _version = Interlocked.Increment(ref _globalVersion); + _clrConstants = clrConstants; + _tracker = tracker; + } + + #region Initialization, Versioning + + private void InitializeMembers() { + Debug.Assert(_state == State.Uninitialized); + Debug.Assert(_constants == null && _methods == null, "Tables are null until initialized"); + + Debug.Assert(_context.ObjectClass != null, "ObjectClass should already be initialized"); + + if (DeclaresGlobalConstants) { + _constants = null; + } else { + _constants = new Dictionary(); + } + + _methods = new Dictionary(); + + Utils.Log(_name ?? "", "INITED"); + + _state = State.Initializing; + try { + if (_initializer != null) { + _initializer(this); + } + } finally { + _initializer = null; + _state = State.Initialized; + } + } + + internal virtual void EnsureInitialized() { + if (_state == State.Uninitialized) { + for (int i = 0; i < _mixins.Length; i++) { + _mixins[i].EnsureInitialized(); + } + + if (_state == State.Uninitialized) { + InitializeMembers(); + } + } + } + + private void EnsureInitializedClassVariables() { + if (_classVariables == null) { + _classVariables = new Dictionary(); + } + } + + internal void AddDependentModule(RubyModule/*!*/ dependentModule) { + Assert.NotNull(dependentModule); + if (!_dependentModules.Contains(dependentModule)) { + _dependentModules.Add(dependentModule); + } + } + + internal void SetDependency(IList/*!*/ modules) { + for (int i = 0; i < modules.Count; i++) { + modules[i].AddDependentModule(this); + } + } + + internal void Updated(string/*!*/ reason) { + int affectedModules = 0; + int counter = Updated(ref affectedModules); + + if (affectedModules > 1) { + Utils.Log(String.Format("{0,-50} {1,-30} affected={2,-5} total={3,-5}", Name, reason, affectedModules, counter), "UPDATED"); + } + } + + // TODO: optimize + private int Updated(ref int affectedModules) { + _version = Interlocked.Increment(ref _globalVersion); + + for (int i = 0; i < _dependentModules.Count; i++) { + _dependentModules[i].Updated(ref affectedModules); + } + + // debug: + affectedModules++; + return Interlocked.Increment(ref _updateCounter); + } + + internal void InitializeMembersFrom(RubyModule/*!*/ module) { + ContractUtils.RequiresNotNull(module, "module"); + +#if !SILVERLIGHT // missing Clone on Delegate + if (module.DeclaresGlobalConstants || module._clrConstants != null && _constants == null) { +#endif + EnsureInitialized(); + module.EnsureInitialized(); +#if !SILVERLIGHT + } else { + _state = module._state; + _initializer = (module._initializer != null) ? (Action)module._initializer.Clone() : null; + } +#endif + + if (module.DeclaresGlobalConstants) { + Debug.Assert(module._constants == null && module._clrConstants == null); + Debug.Assert(_constants != null); + _constants.Clear(); + foreach (KeyValuePair constant in _context.TopGlobalScope.Items) { + _constants.Add(SymbolTable.IdToString(constant.Key), constant.Value); + } + } else { + _constants = (module._constants != null) ? new Dictionary(module._constants) : null; + + // copy namespace members: + if (module._clrConstants != null) { + Debug.Assert(_constants != null); + foreach (KeyValuePair constant in module._clrConstants.SymbolAttributes) { + _constants.Add(SymbolTable.IdToString(constant.Key), constant.Value); + } + } + } + + _methods = (module._methods != null) ? new Dictionary(module._methods) : null; + _classVariables = (module._classVariables != null) ? new Dictionary(module._classVariables) : null; + _mixins = ArrayUtils.Copy(module._mixins); + + // dependentModules - skip + // tracker - skip, .NET members not copied + + Updated("InitializeFrom"); + } + + public void InitializeModuleCopy(RubyModule/*!*/ module) { + if (_context.IsObjectFrozen(this)) { + throw RubyExceptions.CreateTypeError("can't modify frozen Module"); + } + + InitializeMembersFrom(module); + } + + object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) { + RubyModule result = CreateInstance(null); + + // singleton members are copied here, not in InitializeCopy: + if (copySingletonMembers && !IsSingletonClass) { + result.SingletonClass.InitializeMembersFrom(SingletonClass); + } + + // copy instance variables, and frozen, taint flags: + _context.CopyInstanceData(this, result, false, true, false); + return result; + } + + // creates an empty Module or its subclass: + protected virtual RubyModule/*!*/ CreateInstance(string name) { + return _context.CreateModule(name, null, null, null, null); + } + + public static RubyModule/*!*/ CreateInstance(RubyClass/*!*/ rubyClass, string name) { + return rubyClass == rubyClass.Context.ModuleClass ? + new RubyModule(rubyClass, name) : + new RubyModule.Subclass(rubyClass, name); + } + + #endregion + + #region Factories + + // Ruby constructor: + public static object CreateAnonymousModule(RubyScope/*!*/ scope, BlockParam body, RubyClass/*!*/ self) { + RubyModule newModule = CreateInstance(self, null); + return (body != null) ? RubyUtils.EvaluateInModule(newModule, body, newModule) : newModule; + } + + public RubyClass/*!*/ CreateSingletonClass() { + //^ ensures result.IsSingletonClass && !result.IsDummySingletonClass + + Debug.Assert(!IsDummySingletonClass); + + if (_singletonClass.IsDummySingletonClass) { + _context.AppendDummySingleton(_singletonClass); + } + + return _singletonClass; + } + + #endregion + + #region Ancestors + + // Return true from action to terminate enumeration. + public bool ForEachAncestor(bool inherited, Func/*!*/ action) { + if (inherited) { + return ForEachAncestor(action); + } else { + return ForEachDeclaredAncestor(action); + } + } + + protected virtual bool ForEachAncestor(Func/*!*/ action) { + return ForEachDeclaredAncestor(action); + } + + protected bool ForEachDeclaredAncestor(Func/*!*/ action) { + // this module: + if (action(this)) return true; + + // mixins: + // (need to be walked in reverse order--last one wins) + foreach (RubyModule m in _mixins) { + if (action(m)) return true; + } + + return false; + } + + #endregion + + #region Constants + + // Value of constant that is to be auto-loaded on first use. + private sealed class AutoloadedConstant { + private readonly MutableString/*!*/ _path; + private bool _loaded; + + // File already loaded? An auto-loaded constant can be referenced by mutliple classes (via duplication). + // After the constant is accessed in one of them and the file is loaded access to the other duplicates doesn't trigger file load. + public bool Loaded { get { return _loaded; } } + public MutableString/*!*/ Path { get { return _path; } } + + public AutoloadedConstant(MutableString/*!*/ path) { + Assert.NotNull(path); + Debug.Assert(path.IsFrozen); + _path = path; + } + + public bool Load(RubyContext/*!*/ context, Scope/*!*/ autoloadScope) { + if (_loaded) { + return false; + } + + _loaded = true; + return context.Loader.LoadFile(autoloadScope, null, _path, LoadFlags.LoadOnce | LoadFlags.AppendExtensions); + } + } + + public string/*!*/ MakeNestedModuleName(string nestedModuleSimpleName) { + return (ReferenceEquals(this, _context.ObjectClass) || nestedModuleSimpleName == null) ? + nestedModuleSimpleName : + _name + "::" + nestedModuleSimpleName; + } + + public void ForEachConstant(bool inherited, Func/*!*/ action) { + ForEachAncestor(inherited, delegate(RubyModule/*!*/ module) { + // notification that we entered the module (it could have no constant): + if (action(module, null, Missing.Value)) return true; + + return module.EnumerateConstants(action); + }); + } + + public void SetConstant(string/*!*/ name, object value) { + if (DeclaresGlobalConstants) { + _context.TopGlobalScope.SetName(SymbolTable.StringToId(name), value); + } else { + EnsureInitialized(); + _constants[name] = value; + } + + // TODO: we don't do dynamic constant lookup, so there is no need to update the class + // Updated("SetConstant"); + } + + /// + /// Sets constant of this module. + /// Returns true if the constant is already defined in the module and it is not an autoloaded constant. + /// + public bool SetConstantChecked(string/*!*/ name, object value) { + object existing; + var result = TryLookupConstant(false, false, null, name, out existing); + SetConstant(name, value); + return result == ConstantLookupResult.Found; + } + + public void SetAutoloadedConstant(string/*!*/ name, MutableString/*!*/ path) { + SetConstant(name, new AutoloadedConstant(MutableString.Create(path).Freeze())); + } + + public MutableString GetAutoloadedConstantPath(string/*!*/ name) { + object value; + AutoloadedConstant autoloaded; + return (TryGetConstantNoAutoloadCheck(name, out value) + && (autoloaded = value as AutoloadedConstant) != null + && !autoloaded.Loaded) ? + autoloaded.Path : null; + } + + /// + /// Get constant defined in this module. Do not autoload. Value is null for autoloaded constant. + /// + public bool TryGetConstantNoAutoload(string/*!*/ name, out object value) { + return TryGetConstant(null, name, out value); + } + + /// + /// Get constant defined in this module. + /// + public bool TryGetConstant(Scope autoloadScope, string/*!*/ name, out object value) { + return TryLookupConstant(false, false, autoloadScope, name, out value) != ConstantLookupResult.NotFound; + } + + /// + /// Get constant defined in this module or any of its ancestors. Do not autoload. Value is null for autoloaded constant. + /// + public bool TryResolveConstantNoAutoload(string/*!*/ name, out object value) { + return TryResolveConstant(null, name, out value); + } + + /// + /// Get constant defined in this module or any of its ancestors. + /// + public bool TryResolveConstant(Scope autoloadScope, string/*!*/ name, out object value) { + return TryLookupConstant(true, true, autoloadScope, name, out value) != ConstantLookupResult.NotFound; + } + + private enum ConstantLookupResult { + NotFound = 0, + Found = 1, + FoundAutoload = 2, + } + + private ConstantLookupResult TryLookupConstant(bool included, bool inherited, Scope autoloadScope, string/*!*/ name, out object value) { + Debug.Assert(included || !inherited); + + value = null; + while (true) { + object result; + + bool found = included ? + TryResolveConstantNoAutoloadCheck(inherited, name, out result) : + TryGetConstantNoAutoloadCheck(name, out result); + + if (!found) { + return ConstantLookupResult.NotFound; + } + + var autoloaded = result as AutoloadedConstant; + if (autoloaded == null) { + value = result; + return ConstantLookupResult.Found; + } + + if (autoloadScope == null) { + return ConstantLookupResult.FoundAutoload; + } + + // autoloaded constants are removed before the associated file is loaded: + RemoveConstant(name); + + // load file and try lookup again: + if (!autoloaded.Load(_context, autoloadScope)) { + return ConstantLookupResult.NotFound; + } + } + } + + private bool TryResolveConstantNoAutoloadCheck(bool inherited, string/*!*/ name, out object value) { + object result = null; // C# closures can't capture "out" parameters + + bool found = ForEachAncestor(inherited, delegate(RubyModule module) { + return module.TryGetConstantNoAutoloadCheck(name, out result); + }); + + value = result; + return found; + } + + // TODO: DLR interop + private bool TryGetConstantNoAutoloadCheck(string/*!*/ name, out object value) { + if (DeclaresGlobalConstants) { + Debug.Assert(_constants == null && _clrConstants == null); + + if (_context.TopGlobalScope.TryLookupName(SymbolTable.StringToId(name), out value)) { + value = TrackerToModule(value); + return true; + } + + return false; + } + + EnsureInitialized(); + + if (_constants != null && _constants.TryGetValue(name, out value)) { + return true; + } + + if (_clrConstants != null && _clrConstants.TryGetValue(SymbolTable.StringToId(name), out value)) { + value = TrackerToModule(value); + return true; + } + + value = null; + return false; + } + + // TODO: DLR interop + public bool RemoveConstant(string/*!*/ name) { + if (DeclaresGlobalConstants) { + Debug.Assert(_constants == null && _clrConstants == null); + return _context.TopGlobalScope.TryRemoveName(SymbolTable.StringToId(name)); + } + + EnsureInitialized(); + + if (_constants != null && _constants.Remove(name)) { + return true; + } + + if (_clrConstants != null && _clrConstants.Remove(SymbolTable.StringToId(name))) { + return true; + } + + return false; + } + + private object TrackerToModule(object value) { + TypeGroup typeGroup = value as TypeGroup; + if (typeGroup != null) { + return value; + } + + // TypeTracker retrieved from namespace tracker should behave like a RubyClass/RubyModule: + TypeTracker typeTracker = value as TypeTracker; + if (typeTracker != null) { + return _context.GetModule(typeTracker.Type); + } + + // NamespaceTracker retrieved from namespace tracker should behave like a RubyModule: + NamespaceTracker namespaceTracker = value as NamespaceTracker; + if (namespaceTracker != null) { + return _context.GetModule(namespaceTracker); + } + + return value; + } + + // TODO: DLR interop + public bool EnumerateConstants(Func/*!*/ action) { + if (DeclaresGlobalConstants) { + Debug.Assert(_constants == null && _clrConstants == null); + foreach (KeyValuePair constant in _context.TopGlobalScope.Items) { + if (action(this, SymbolTable.IdToString(constant.Key), constant.Value)) return true; + } + + return false; + } + + EnsureInitialized(); + + foreach (KeyValuePair constant in _constants) { + if (action(this, constant.Key, constant.Value)) return true; + } + + if (_clrConstants != null) { + foreach (KeyValuePair constant in _clrConstants.SymbolAttributes) { + if (action(this, SymbolTable.IdToString(constant.Key), constant.Value)) return true; + } + } + + return false; + } + + #endregion + + #region Methods + + public void ForEachInstanceMethod(bool inherited, Func/*!*/ action) { + ForEachAncestor(inherited, delegate(RubyModule/*!*/ module) { + + // skip interfaces (methods declared on interfaces have already been looked for in the class); + // if 'this' is an interface, we want to visit all interface methods: + if (module.IsInterface && !this.IsInterface) return false; + + // notification that we entered the module (it could have no method): + if (action(module, null, null)) return true; + + return module.EnumerateMethods(action); + }); + } + + public void AddMethodAlias(string/*!*/ name, RubyMemberInfo/*!*/ method) { + // Alias preserves visibility and declaring module even though the alias is declared in a different module (e.g. subclass) => + // we can share method info (in fact, sharing is sound with Method#== semantics - it returns true on aliased methods). + AddMethod(name, method); + } + + // adds method alias via define_method: + public void AddDefinedMethod(string/*!*/ name, RubyMemberInfo/*!*/ method) { + // copy method, Method#== returns false on defined methods: + AddMethod(name, method.Copy(method.Flags, this)); + } + + // adds instance and singleton methods of a module function: + public void AddModuleFunction(string/*!*/ name, RubyMemberInfo/*!*/ method) { + AddMethod(name, method.Copy(RubyMemberFlags.Private, this)); + SingletonClass.AddMethod(name, method.Copy(RubyMemberFlags.Public, SingletonClass)); + } + + public void SetMethodVisibility(string/*!*/ name, RubyMemberInfo/*!*/ method, RubyMethodVisibility visibility) { + if (method.Visibility != visibility) { + AddMethod(name, method.Copy((RubyMemberFlags)visibility, this)); + } + } + + public void AddMethod(string/*!*/ name, RubyMemberInfo/*!*/ method) { + Assert.NotNull(name, method); + + SetMethodNoEvent(name, method); + _context.MethodAdded(this, name); + } + + internal void SetMethodNoEvent(string/*!*/ name, RubyMemberInfo/*!*/ method) { + Assert.NotNull(name, method); + + // Update only if the current method overrides/redefines another one in the inheritance hierarchy. + // Method lookup failures are not cached in dynamic sites. + // Therefore a future invocation of the method will trigger resolution in binder that will find just added method. + // If, on the other hand, a method is overridden there might be a dynamic site that caches a method call to that method. + // In that case we need to update version to invalidate the site. + + RubyMemberInfo overriddenMethod = ResolveMethodUsedInDynamicSite(name); + + // TODO: cache this? (definition of method_missing could have updated all subclasses): + if (overriddenMethod == null) { + overriddenMethod = ResolveMethodUsedInDynamicSite(Symbols.MethodMissing); + // TODO: better check for builtin method + if (overriddenMethod != null && overriddenMethod.DeclaringModule == _context.KernelModule && overriddenMethod is RubyMethodGroupInfo) { + overriddenMethod = null; + } + } + + EnsureInitialized(); + _methods[name] = method; + + if (overriddenMethod != null) { + // TODO: stop updating if a module that defines a method of the same name is reached: + Updated("SetMethod: " + name); + } + } + + public bool RemoveMethod(string/*!*/ name) { + if (RemoveMethodNoEvent(name)) { + _context.MethodRemoved(this, name); + return true; + } + return false; + } + + internal bool RemoveMethodNoEvent(string/*!*/ name) { + EnsureInitialized(); + bool result = _methods.Remove(name); + Updated("RemoveMethodNoEvent"); + return result; + } + + internal void UndefineMethodNoEvent(string/*!*/ name) { + EnsureInitialized(); + _methods[name] = RubyMethodInfo.UndefinedMethod; + Updated("UndefineMethodNoEvent"); + } + + public void UndefineMethod(string/*!*/ name) { + UndefineMethodNoEvent(name); + _context.MethodUndefined(this, name); + } + + public void HideMethod(string/*!*/ name) { + EnsureInitialized(); + _methods[name] = RubyMethodInfo.HiddenMethod; + Updated("HideMethod"); + } + + public void UndefineLibraryMethod(string/*!*/ name) { + UndefineMethod(name); + } + + public void DefineLibraryMethod(string/*!*/ name, int attributes, params Delegate[]/*!*/ overloads) { + bool skipEvent = ((RubyMethodAttributes)attributes & RubyMethodAttributes.NoEvent) != 0; + SetLibraryMethod(name, MakeMethodGroupInfo(attributes, overloads), skipEvent); + } + + public void DefineRuleGenerator(string/*!*/ name, int attributes, RuleGenerator/*!*/ generator) { + Assert.NotNull(generator); + var flags = (RubyMemberFlags)(attributes & (int)RubyMethodAttributes.VisibilityMask); + bool skipEvent = ((RubyMethodAttributes)attributes & RubyMethodAttributes.NoEvent) != 0; + SetLibraryMethod(name, new RubyCustomMethodInfo(generator, flags, this), skipEvent); + } + + public void SetLibraryMethod(string/*!*/ name, RubyMemberInfo/*!*/ method, bool noEvent) { + // trigger event only for non-builtins: + if (noEvent) { + SetMethodNoEvent(name, method); + } else { + AddMethod(name, method); + } + } + + private RubyMethodGroupInfo/*!*/ MakeMethodGroupInfo(int attributes, params Delegate[]/*!*/ overloads) { + Assert.NotNullItems(overloads); + + var flags = (RubyMemberFlags)(attributes & (int)RubyMethodAttributes.MemberFlagsMask); + return new RubyMethodGroupInfo(overloads, flags, this); + } + + // Looks only for those methods that were used. Doesn't need to initialize method tables (a used method is always stored in a table). + internal RubyMemberInfo ResolveMethodUsedInDynamicSite(string/*!*/ name) { + RubyMemberInfo result = null; + + if (ForEachAncestor(delegate(RubyModule module) { + return module._methods != null && module.TryGetMethod(name, out result); + })) { + // includes private methods: + return result != null && result != RubyMethodInfo.UndefinedMethod && result.InvalidateSitesOnOverride ? result : null; + } + + return null; + } + + public RubyMemberInfo ResolveMethod(string/*!*/ name, bool includePrivate) { + Assert.NotNull(name); + EnsureInitialized(); + + RubyMemberInfo result = null; + + if (ForEachAncestor(delegate(RubyModule module) { + return module.TryGetMethod(name, out result); + })) { + return result != null && result != RubyMethodInfo.UndefinedMethod && IsMethodVisible(result, includePrivate) ? result : null; + } + + return null; + } + + internal static bool IsMethodVisible(RubyMemberInfo/*!*/ method, bool includePrivate) { + return ( + method.Visibility == RubyMethodVisibility.Public || + method.Visibility == RubyMethodVisibility.Protected || + method.Visibility == RubyMethodVisibility.Private && includePrivate + ); + // TODO: protected + } + + /// + /// Resolve method and if it is not found in this module's ancestors, resolve in Object. + /// + public virtual RubyMemberInfo ResolveMethodFallbackToObject(string/*!*/ name, bool includePrivate) { + return ResolveMethod(name, includePrivate) ?? _context.ObjectClass.ResolveMethod(name, includePrivate); + } + + public RubyMemberInfo GetMethod(string/*!*/ name) { + ContractUtils.RequiresNotNull(name, "name"); + EnsureInitialized(); + + RubyMemberInfo method; + TryGetMethod(name, out method); + return method; + } + + // skip one method in the method resolution order (MRO) + public RubyMemberInfo ResolveSuperMethod(string/*!*/ name, RubyModule/*!*/ declaringModule) { + Assert.NotNull(name, declaringModule); + + EnsureInitialized(); + + RubyMemberInfo result = null; + bool foundModule = false; + + // start searching for the method in the MRO parent of the declaringModule: + if (ForEachAncestor(delegate(RubyModule module) { + if (module == declaringModule) { + foundModule = true; + return false; + } + return foundModule && module.TryGetMethod(name, out result); + })) { + return (result != RubyMethodInfo.UndefinedMethod) ? result : null; + } + + return null; + } + + protected bool TryGetMethod(string/*!*/ name, out RubyMemberInfo method) { + Assert.NotNull(name); + Debug.Assert(_methods != null); + + // lookup Ruby method first: + if (_methods.TryGetValue(name, out method)) { + + // method is hidden, continue resolution, but skip CLR method lookup: + if (ReferenceEquals(method, RubyMethodInfo.HiddenMethod)) { + method = null; + return false; + } + + return true; + } + + // skip lookup on types that are not visible or interfaces: + if (_tracker != null && _tracker.Type.IsVisible && !_tracker.Type.IsInterface) { + if (TryGetClrMember(_tracker.Type, name, out method)) { + // We can add the resolved method to the method table since CLR types are immutable. + // Doing so makes next lookup faster (prevents allocating new method groups each time). + _methods.Add(name, method); + return true; + } + } + + return false; + } + + protected virtual bool TryGetClrMember(Type/*!*/ type, string/*!*/ name, out RubyMemberInfo method) { + method = null; + return false; + } + + public bool EnumerateMethods(Func/*!*/ action) { + EnsureInitialized(); + + foreach (KeyValuePair method in _methods) { + if (action(this, method.Key, method.Value)) return true; + } + + if (_tracker != null) { + // TODO: CLR methods but removed... + } + + return false; + } + + /// + /// inherited == false, attributes & attr == Instance: + /// - get methods in the "self" module + /// - also include methods on singleton ancestor classes until a non-singleton class is reached + /// inherited == false, attributes & attr == Singleton: + /// - get methods only in the "self" module if it's a singleton class + /// - do not visit mixins nor super classes + /// inherited == true, attributes & attr == Singleton: + /// - walk all ancestors until a non-singleton class is reached (do not include non-singleton's methods) + /// inherited == true, attributes & attr == None: + /// - walk all ancestors until an Object is reached + /// + /// Methods are filtered by visibility specified in attributes (mutliple visibilities could be specified). + /// A name undefined in a module is not visible in that module and its ancestors. + /// Method names are not duplicated in the result. + /// + public void ForEachMember(bool inherited, RubyMethodAttributes attributes, Action/*!*/ action) { + var visited = new Dictionary(); + + // We can look for instance methods, singleton methods or all methods. + // The difference is when we stop searching. + bool instanceMethods = (attributes & RubyMethodAttributes.Instance) != 0; + bool singletonMethods = (attributes & RubyMethodAttributes.Singleton) != 0; + + bool stop = false; + ForEachInstanceMethod(true, delegate(RubyModule/*!*/ module, string name, RubyMemberInfo member) { + + if (member == null) { + // notification received before any method of the module + + if (stop) { + return true; + } + + if (instanceMethods) { + stop = !inherited && (!IsClass || module.IsClass && !module.IsSingletonClass); + } else if (singletonMethods) { + if (!inherited && module != this || module.IsClass && !module.IsSingletonClass) { + return true; + } + } else { + stop = !inherited; + } + + } else if (member.IsUndefined) { + visited.Add(name, true); + } else if (((RubyMethodAttributes)member.Visibility & attributes) != 0 && !visited.ContainsKey(name)) { + action(name, member); + visited.Add(name, true); + } + + return false; + }); + } + + #endregion + + #region Class variables + + public void ForEachClassVariable(bool inherited, Func/*!*/ action) { + ForEachAncestor(inherited, delegate(RubyModule/*!*/ module) { + // notification that we entered the module (it could have no class variable): + if (action(module, null, Missing.Value)) return true; + + return module.EnumerateClassVariables(action); + }); + } + + public void SetClassVariable(string/*!*/ name, object value) { + EnsureInitializedClassVariables(); + _classVariables[name] = value; + } + + public bool TryGetClassVariable(string/*!*/ name, out object value) { + value = null; + return _classVariables != null && _classVariables.TryGetValue(name, out value); + } + + public bool RemoveClassVariable(string/*!*/ name) { + return _classVariables != null && _classVariables.Remove(name); + } + + public RubyModule TryResolveClassVariable(string/*!*/ name, out object value) { + RubyModule result = null; + object constValue = null; + if (ForEachAncestor(delegate(RubyModule/*!*/ module) { + if (module._classVariables != null && module._classVariables.TryGetValue(name, out constValue)) { + result = module; + return true; + } + + return false; + })) { + value = constValue; + return result; + } + + value = null; + return null; + } + + private bool EnumerateClassVariables(Func/*!*/ action) { + if (_classVariables != null) { + foreach (KeyValuePair variable in _classVariables) { + if (action(this, variable.Key, variable.Value)) return true; + } + } + + return false; + } + + #endregion + + #region Mixins + + public bool HasAncestor(RubyModule/*!*/ module) { + return ForEachAncestor(true, delegate(RubyModule m) { + return m == module; + }); + } + + internal void SetMixins(IList/*!*/ modules) { + Debug.Assert(_mixins.Length == 0); + Debug.Assert(modules != null && CollectionUtils.TrueForAll(modules, delegate(RubyModule m) { return m != null && !m.IsClass; })); + + // do not initialize modules: + _mixins = MakeNewMixins(EmptyArray, modules); + SetDependency(_mixins); + Updated("SetMixins"); + } + + public void IncludeModules(params RubyModule[]/*!*/ modules) { + Debug.Assert(CollectionUtils.TrueForAll(modules, delegate(RubyModule m) { return m != null && !m.IsClass; })); + + RubyModule[] tmp = MakeNewMixins(_mixins, modules); + + foreach (RubyModule module in tmp) { + if (module.IsInterface) { + // Can't include generic interfaces + if (module.Tracker.Type.ContainsGenericParameters) { + throw RubyExceptions.CreateTypeError(String.Format( + "{0}: cannot extend with open generic instantiation {1}. Only closed instantiations are supported.", + Name, module.Name + )); + } + + if (!CanIncludeClrInterface) { + bool alreadyIncluded = false; + foreach (RubyModule includedModule in _mixins) { + if (includedModule == module) { + alreadyIncluded = true; + break; + } + } + if (!alreadyIncluded) { + throw new InvalidOperationException(String.Format( + "Interface {0} cannot be included in class {1} because its underlying type has already been created.", + module.Name, Name + )); + } + } + } + } + + SetDependency(tmp); + + // initialize added modules: + if (_state == State.Initialized) { + foreach (RubyModule module in tmp) { + module.EnsureInitialized(); + } + } + + _mixins = tmp; + + Updated("IncludeModules"); + } + + /// + /// Build a new list of mixins based on an original list and a list of added modules + /// + private RubyModule[]/*!*/ MakeNewMixins(RubyModule[]/*!*/ original, IList/*!*/ updates) { + Assert.NotNull(original); + Assert.NotNull(updates); + + List tmp = new List(original); + AddMixins(tmp, 0, updates, true); + return tmp.ToArray(); + } + + private int AddMixins(List/*!*/ list, int index, IList/*!*/ updates, bool recursive) { + foreach (RubyModule module in updates) { + Assert.NotNull(module); + + if (module == this) { + throw RubyExceptions.CreateArgumentError("cyclic include detected"); + } + int newIndex = list.IndexOf(module); + if (newIndex >= 0) { + // Module is already present in _mixins + // Update the insertion point so that we retain ordering of dependencies + // If we're still in the initial level of recursion, repeat for module's mixins + index = newIndex + 1; + if (recursive) { + index = AddMixins(list, index, module._mixins, false); + } + } else { + // Module is not yet present in _mixins + // Recursively insert module dependencies at the insertion point, then insert module itself + newIndex = AddMixins(list, index, module._mixins, false); + + // insert module only if it is not an ancestor of the superclass: + if (!IsSuperClassAncestor(module)) { + list.Insert(index, module); + index = newIndex + 1; + } else { + index = newIndex; + } + } + } + return index; + } + + /// + /// Returns true if given module is an ancestor of the superclass of this class (provided that this is a class). + /// + internal virtual bool IsSuperClassAncestor(RubyModule/*!*/ module) { + return false; + } + + protected virtual bool CanIncludeClrInterface { + get { return true; } + } + + protected List GetClrInterfaces() { + List interfaces = new List(); + foreach (RubyModule m in _mixins) { + if (m.IsInterface && !interfaces.Contains(m.Tracker.Type)) { + interfaces.Add(m.Tracker.Type); + } + } + return interfaces; + } + + internal void IncludeTrait(Action/*!*/ trait) { + Assert.NotNull(trait); + + if (_state == State.Uninitialized) { + if (_initializer != null) { + _initializer += trait; + } else { + _initializer = trait; + } + } else { + // TODO: postpone + trait(this); + } + } + + internal void IncludeLibraryModule(Action instanceTrait, Action classTrait, RubyModule[]/*!*/ mixins) { + Assert.NotNull(mixins); + + IncludeModules(mixins); + + if (instanceTrait != null) { + IncludeTrait(instanceTrait); + } + + if (classTrait != null) { + SingletonClass.IncludeTrait(classTrait); + } + } + + #endregion + + internal void AddFullVersionTest(MetaObjectBuilder/*!*/ metaBuilder, MSA.Expression/*!*/ contextExpression) { + Assert.NotNull(metaBuilder); + EnsureInitialized(); // Initialization changes the version number, so ensure that the module is initialized + + // check for runtime: + metaBuilder.AddRestriction(Ast.Equal(contextExpression, Ast.Constant(_context))); + + // check for version: + metaBuilder.AddCondition(Ast.Equal(Ast.Property(Ast.Constant(this), VersionProperty), Ast.Constant(_version))); + } + + #region Utils + + public MutableString/*!*/ GetDisplayName(bool showEmptyName) { + if (IsSingletonClass) { + RubyClass c = (RubyClass)this; + object singletonOf; + MutableString result = MutableString.CreateMutable(); + + int nestings = 0; + while (true) { + nestings++; + result.Append("#', nestings); + } else if (_name == null) { + if (showEmptyName) { + return MutableString.Empty; + } else { + MutableString result = MutableString.CreateMutable(); + result.Append("#<"); + result.Append(_context.GetClassOf(this).Name); + result.Append(':'); + RubyUtils.AppendFormatHexObjectId(result, RubyUtils.GetObjectId(_context, this)); + result.Append('>'); + return result; + } + } else { + return MutableString.Create(_name); + } + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyObject.Meta.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyObject.Meta.cs new file mode 100644 index 0000000000..83fe99698f --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyObject.Meta.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Builtins { + + public partial class RubyObject : IDynamicObject { + public virtual MetaObject/*!*/ GetMetaObject(Expression/*!*/ parameter) { + return new Meta(parameter, Restrictions.Empty, this); + } + + internal class Meta : MetaObject { + public Meta(Expression/*!*/ expression, Restrictions/*!*/ restrictions, IRubyObject/*!*/ value) + : base(expression, restrictions, value) { + ContractUtils.RequiresNotNull(value, "value"); + } + + public override MetaObject/*!*/ BindGetMember(GetMemberBinder/*!*/ binder) { + var self = (IRubyObject)Value; + return RubyGetMemberBinder.TryBind(self.Class.Context, binder, this) ?? binder.FallbackGetMember(this); + } + + public override MetaObject/*!*/ BindInvokeMember(InvokeMemberBinder/*!*/ binder, params MetaObject/*!*/[]/*!*/ args) { + var self = (IRubyObject)Value; + return RubyInvokeMemberBinder.TryBind(self.Class.Context, binder, this, args) ?? binder.FallbackInvokeMember(this, args); + } + + public override IEnumerable GetDynamicMemberNames() { + var self = (IRubyObject)Value; + RubyInstanceData instanceData = self.GetInstanceData(); + RubyModule theClass = (instanceData != null ? instanceData.ImmediateClass : null); + theClass = theClass ?? self.Class; + var names = new List(); + theClass.ForEachMember(true, RubyMethodAttributes.DefaultVisibility, delegate(string/*!*/ name, RubyMemberInfo/*!*/ member) { + names.Add(name); + }); + return names; + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyObject.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyObject.cs new file mode 100644 index 0000000000..2c9c149d24 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyObject.cs @@ -0,0 +1,121 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Runtime.Serialization; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using System.Security.Permissions; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Builtins { + public partial class RubyObject : IRubyObject, IRubyObjectState, IDuplicable, ISerializable { + internal const string ClassPropertyName = "Class"; + + private RubyInstanceData _instanceData; + private readonly RubyClass/*!*/ _class; + + public RubyObject(RubyClass/*!*/ cls) { + Assert.NotNull(cls); + _class = cls; + } + +#if !SILVERLIGHT + protected RubyObject(SerializationInfo/*!*/ info, StreamingContext context) { + RubyOps.DeserializeObject(out _instanceData, out _class, info); + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] + public void GetObjectData(SerializationInfo/*!*/ info, StreamingContext context) { + RubyOps.SerializeObject(_instanceData, _class, info); + } +#endif + + [Emitted] + public RubyClass/*!*/ Class { + get { return _class; } + } + + public RubyInstanceData/*!*/ GetInstanceData() { + return RubyOps.GetInstanceData(ref _instanceData); + } + + public RubyInstanceData TryGetInstanceData() { + return _instanceData; + } + + private void CopyInstanceDataFrom(IRubyObject/*!*/ source, bool copyFrozenState) { + // copy instance data, but not the state: + var sourceData = source.TryGetInstanceData(); + if (sourceData != null) { + _instanceData = new RubyInstanceData(); + sourceData.CopyInstanceVariablesTo(_instanceData); + } + + // copy flags: + SetTaint(this, IsTainted(source)); + if (copyFrozenState && IsFrozen(source)) { + Freeze(this); + } + } + + protected virtual RubyObject/*!*/ CreateInstance() { + return new RubyObject(_class); + } + + object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) { + var result = CreateInstance(); + result.CopyInstanceDataFrom(this, copySingletonMembers); + return result; + } + + #region IRubyObjectState Members + + bool IRubyObjectState.IsFrozen { + get { return _instanceData != null && _instanceData.Frozen; } + } + + bool IRubyObjectState.IsTainted { + get { return _instanceData != null && _instanceData.Tainted; } + set { GetInstanceData().Tainted = value; } + } + + void IRubyObjectState.Freeze() { + GetInstanceData().Freeze(); + } + + public static bool IsFrozen(IRubyObject/*!*/ obj) { + var instanceData = obj.TryGetInstanceData(); + return instanceData != null && instanceData.Frozen; + } + + public static bool IsTainted(IRubyObject/*!*/ obj) { + var instanceData = obj.TryGetInstanceData(); + return instanceData != null && instanceData.Tainted; + } + + public static void Freeze(IRubyObject/*!*/ obj) { + obj.GetInstanceData().Freeze(); + } + + public static void SetTaint(IRubyObject/*!*/ obj, bool value) { + obj.GetInstanceData().Tainted = value; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyRegex.Subclass.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyRegex.Subclass.cs new file mode 100644 index 0000000000..63068f3fa5 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyRegex.Subclass.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Builtins { + public partial class RubyRegex { + public sealed class Subclass : RubyRegex, IRubyObject { + private readonly RubyClass/*!*/ _class; + private RubyInstanceData _instanceData; + + // called by Class#new rule when creating a Ruby subclass of String: + public Subclass(RubyClass/*!*/ rubyClass) { + Assert.NotNull(rubyClass); + _class = rubyClass; + } + + private Subclass(RubyRegex.Subclass/*!*/ regex) + : base(regex) { + _class = regex._class; + } + + protected override RubyRegex/*!*/ Copy() { + return new Subclass(this); + } + + #region IRubyObject Members + + [Emitted] + public RubyClass/*!*/ Class { + get { return _class; } + } + + public RubyInstanceData/*!*/ GetInstanceData() { + return RubyOps.GetInstanceData(ref _instanceData); + } + + public RubyInstanceData TryGetInstanceData() { + return _instanceData; + } + + #endregion + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyRegex.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyRegex.cs new file mode 100644 index 0000000000..704f929502 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyRegex.cs @@ -0,0 +1,196 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Text; +using System.Text.RegularExpressions; +using IronRuby.Compiler; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + public partial class RubyRegex : IEquatable, IDuplicable { + private GenericRegex/*!*/ _regex; + + #region Construction + + public RubyRegex() { + _regex = StringRegex.Empty; + } + + public RubyRegex(MutableString/*!*/ pattern, RubyRegexOptions options) { + ContractUtils.RequiresNotNull(pattern, "pattern"); + _regex = pattern.ToRegularExpression(options); + } + + public RubyRegex(string/*!*/ pattern, RubyRegexOptions options) { + ContractUtils.RequiresNotNull(pattern, "pattern"); + _regex = new StringRegex(pattern, options); + } + + public RubyRegex(byte[]/*!*/ pattern, RubyRegexOptions options) { + ContractUtils.RequiresNotNull(pattern, "pattern"); + // TODO: _regex = new BinaryRegex(pattern, options); + _regex = new StringRegex(BinaryEncoding.Obsolete.GetString(pattern, 0, pattern.Length), options); + } + + public RubyRegex(Regex/*!*/ regex) { + ContractUtils.RequiresNotNull(regex, "regex"); + _regex = new StringRegex(regex); + } + + public RubyRegex(RubyRegex/*!*/ regex) { + ContractUtils.RequiresNotNull(regex, "regex"); + _regex = regex._regex; + } + + /// + /// Creates a copy of the proc that has the same target, context, self object as this instance. + /// Doesn't copy instance data. + /// Preserves the class of the Proc. + /// + protected virtual RubyRegex/*!*/ Copy() { + return new RubyRegex(this); + } + + object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) { + var result = Copy(); + context.CopyInstanceData(this, result, copySingletonMembers); + return result; + } + + #endregion + + public bool IsEmpty { + get { return _regex.IsEmpty; } + } + + public RubyRegexOptions Options { + get { return _regex.Options; } + } + + public MutableString/*!*/ GetPattern() { + return _regex.GetPattern(); + } + + public bool Equals(RubyRegex other) { + return ReferenceEquals(this, other) || other != null && _regex.Equals(other._regex); + } + + public override bool Equals(object other) { + return _regex.Equals(other as RubyRegex); + } + + public override int GetHashCode() { + return _regex.GetHashCode(); + } + + #region Match, ReverseMatch, Matches, Split + + public Match/*!*/ Match(MutableString/*!*/ input) { + return Match(input, 0); + } + + public Match/*!*/ Match(MutableString/*!*/ input, int start) { + ContractUtils.RequiresNotNull(input, "input"); + return Match(input, start, input.Length - start); + } + + public Match/*!*/ Match(MutableString/*!*/ input, int start, int count) { + ContractUtils.RequiresNotNull(input, "input"); + return _regex.Match(input, start, count); + } + + public Match/*!*/ ReverseMatch(MutableString/*!*/ str, int start) { + ContractUtils.RequiresNotNull(str, "str"); + return _regex.ReverseMatch(str, start); + } + + public Match/*!*/ ReverseMatch(MutableString/*!*/ str, int start, int count) { + ContractUtils.RequiresNotNull(str, "str"); + return _regex.ReverseMatch(str, start, count); + } + + public MatchCollection/*!*/ Matches(MutableString/*!*/ input) { + return Matches(input, 0); + } + + public MatchCollection/*!*/ Matches(MutableString/*!*/ input, int start) { + ContractUtils.RequiresNotNull(input, "input"); + return _regex.Matches(input, start); + } + + public MutableString[]/*!*/ Split(MutableString/*!*/ input) { + return _regex.Split(input, 0, 0); + } + + public MutableString[]/*!*/ Split(MutableString/*!*/ input, int count) { + return _regex.Split(input, count, 0); + } + + public MutableString[]/*!*/ Split(MutableString/*!*/ input, int count, int start) { + return _regex.Split(input, count, start); + } + + public static MatchData SetCurrentMatchData(RubyScope/*!*/ scope, RubyRegex/*!*/ regex, MutableString/*!*/ str) { + var targetScope = scope.GetInnerMostClosureScope(); + + if (str == null) { + return targetScope.CurrentMatch = null; + } + + Match match = regex.Match(str, 0); + if (match.Success) { + return targetScope.CurrentMatch = scope.RubyContext.TaintObjectBy(new MatchData(match, str), str); + } else { + return targetScope.CurrentMatch = null; + } + } + + #endregion + + /// + /// Returns a new instance of MutableString that contains escaped content of the given string. + /// + public static MutableString/*!*/ Escape(MutableString/*!*/ str) { + // TODO: + return str.EscapeRegularExpression(); + } + + public void Set(MutableString/*!*/ pattern, RubyRegexOptions options) { + _regex = pattern.ToRegularExpression(options); + } + + public static RegexOptions ToClrOptions(RubyRegexOptions options) { + RegexOptions result = RegexOptions.Multiline; + + if ((options & RubyRegexOptions.IgnoreCase) != 0) { + result |= RegexOptions.IgnoreCase; + } + + if ((options & RubyRegexOptions.Extended) != 0) { + result |= RegexOptions.IgnorePatternWhitespace; + } + + if ((options & RubyRegexOptions.Multiline) != 0) { + result |= RegexOptions.Singleline; + } + + return result; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyRegexOptions.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyRegexOptions.cs new file mode 100644 index 0000000000..3d0b597f45 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyRegexOptions.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace IronRuby.Builtins { + [Flags] + public enum RubyRegexOptions { + NONE = 0, + IgnoreCase = 1, + Extended = 2, + Multiline = 4, + + // encoding: + FIXED = 16, + EUC = 32, + SJIS = 48, + UTF8 = 64, + + EncodingMask = FIXED | EUC | SJIS | UTF8 + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubySites.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubySites.cs new file mode 100644 index 0000000000..4f2ea5feef --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubySites.cs @@ -0,0 +1,213 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.Builtins { + + // TODO: Many of these methods have strongly typed results (int, bool, etc). + // While this is nice for using them in C#, it is incorrect, because the + // method could return an arbitrary object. Currently the binder will + // generate calls into Converter, which will not have the right behavior. + public static class RubySites { + private static readonly CallSite> InspectSharedSite = + CallSite>.Create(InstanceCallAction("inspect")); + + public static MutableString Inspect(RubyContext/*!*/ context, object obj) { + return InspectSharedSite.Target(InspectSharedSite, context, obj); + } + + private static readonly CallSite> AllocateSharedSite = + CallSite>.Create(InstanceCallAction("allocate")); + + public static object Allocate(RubyClass/*!*/ classObj) { + return AllocateSharedSite.Target(AllocateSharedSite, classObj.Context, classObj); + } + + private static readonly CallSite> CaseEqualSharedSite = + CallSite>.Create(InstanceCallAction("===", 1)); + + public static bool CaseEqual(RubyContext/*!*/ context, object lhs, object rhs) { + return RubyOps.IsTrue(CaseEqualSharedSite.Target(CaseEqualSharedSite, context, lhs, rhs)); + } + + private static readonly CallSite> EqualSharedSite = + CallSite>.Create(InstanceCallAction("==", 1)); + + public static bool Equal(RubyContext/*!*/ context, object lhs, object rhs) { + return RubyOps.IsTrue(EqualSharedSite.Target(EqualSharedSite, context, lhs, rhs)); + } + + private static readonly CallSite> RespondToSharedSite = + CallSite>.Create(InstanceCallAction("respond_to?", 1)); + + public static bool RespondTo(RubyContext/*!*/ context, object obj, SymbolId name) { + return RubyOps.IsTrue(RespondToSharedSite.Target(RespondToSharedSite, context, obj, name)); + } + + public static bool RespondTo(RubyContext/*!*/ context, object obj, string name) { + return RubyOps.IsTrue(RespondToSharedSite.Target(RespondToSharedSite, context, obj, SymbolTable.StringToId(name))); + } + + private static readonly CallSite> _ToISharedSite = + CallSite>.Create(InstanceCallAction("to_i")); + + public static object ToI(RubyContext/*!*/ context, object value) { + return _ToISharedSite.Target(_ToISharedSite, context, value); + } + + private static readonly CallSite> _ToIntSharedSite = CallSite>.Create( + InstanceCallAction("to_int")); + + public static object ToInt(RubyContext/*!*/ context, object value) { + return _ToIntSharedSite.Target(_ToIntSharedSite, context, value); + } + + private static readonly CallSite> _ToFSharedSite = CallSite>.Create( + InstanceCallAction("to_f")); + + public static double ToF(RubyContext/*!*/ context, object value) { + return _ToFSharedSite.Target(_ToFSharedSite, context, value); + } + + private static readonly CallSite> ToStrSharedSite = CallSite>.Create( + InstanceCallAction("to_str")); + + public static MutableString ToStr(RubyContext/*!*/ context, object value) { + return ToStrSharedSite.Target(ToStrSharedSite, context, value); + } + + private static readonly CallSite> ToRangeSharedSite = CallSite>.Create( + InstanceCallAction("to_range")); + + public static Range ToRange(RubyContext/*!*/ context, object value) { + return ToRangeSharedSite.Target(ToRangeSharedSite, context, value); + } + + private static readonly CallSite> ToSSharedSite = CallSite>.Create( + InstanceCallAction("to_s")); + + public static MutableString ToS(RubyContext/*!*/ context, object value) { + return ToSSharedSite.Target(ToSSharedSite, context, value); + } + + private static readonly CallSite> RangeBeginSharedSite = CallSite>.Create( + InstanceCallAction("begin")); + + public static object RangeBegin(RubyContext/*!*/ context, Range range) { + return RangeBeginSharedSite.Target(RangeBeginSharedSite, context, range); + } + + private static readonly CallSite> RangeEndSharedSite = CallSite>.Create( + InstanceCallAction("end")); + + public static object RangeEnd(RubyContext/*!*/ context, Range range) { + return RangeEndSharedSite.Target(RangeEndSharedSite, context, range); + } + + private static readonly CallSite> RangeExcludeEndSharedSite = CallSite>.Create( + InstanceCallAction("exclude_end?")); + + public static bool RangeExcludeEnd(RubyContext/*!*/ context, Range range) { + return RangeExcludeEndSharedSite.Target(RangeExcludeEndSharedSite, context, range); + } + + private static readonly CallSite> ModuleConstMissingSharedSite = CallSite>.Create( + InstanceCallAction("const_missing", 1)); + + public static object ModuleConstMissing(RubyContext/*!*/ context, RubyModule self, string/*!*/ name) { + return ModuleConstMissingSharedSite.Target(ModuleConstMissingSharedSite, context, self, SymbolTable.StringToId(name)); + } + + private static readonly CallSite>> _ToHashSharedSite = CallSite>>.Create( + InstanceCallAction("to_hash")); + + public static IDictionary ToHash(RubyContext/*!*/ context, object obj) { + return _ToHashSharedSite.Target(_ToHashSharedSite, context, obj); + } + + private static readonly CallSite> _EachSharedSite = CallSite>.Create( + InstanceCallAction("each", RubyCallSignature.WithBlock(0))); + + public static object Each(RubyContext/*!*/ context, object self, Proc block) { + return _EachSharedSite.Target(_EachSharedSite, context, self, block); + } + + private static readonly CallSite> _SuccSharedSite = CallSite>.Create(InstanceCallAction("succ")); + + public static object Successor(RubyContext/*!*/ context, object self) { + return _SuccSharedSite.Target(_SuccSharedSite, context, self); + } + + private static readonly CallSite> _CallSharedSite = CallSite>.Create(InstanceCallAction("call")); + + public static object Call(RubyContext/*!*/ context, object self) { + return _CallSharedSite.Target(_CallSharedSite, context, self); + } + + private static readonly CallSite> _CompareSharedSite = CallSite>.Create( + InstanceCallAction("<=>", 1)); + + /// + /// Calls <=>, returning the actual result. You should probably use Protocols.Compare, unless you + /// have some reason for wanting to call <=>directly (e.g. Range.Initialize does this). + /// + public static object Compare(RubyContext/*!*/ context, object lhs, object rhs) { + return _CompareSharedSite.Target(_CompareSharedSite, context, lhs, rhs); + } + + #region Helpers + + public static RubyCallAction InstanceCallAction(string/*!*/ name) { + return RubyCallAction.Make(name, 0); + } + + public static RubyCallAction InstanceCallAction(string/*!*/ name, int argumentCount) { + return RubyCallAction.Make(name, argumentCount); + } + + public static RubyCallAction InstanceCallAction(string/*!*/ name, RubyCallSignature callSignature) { + return RubyCallAction.Make(name, callSignature); + } + + #endregion + + /// + /// Dynamically invoke the coerce method on the object specified by self. + /// + /// The current context + /// The object on which to invoke the method + /// The object that we are coercing + /// A two element array containing [other, self] coerced by self + /// Raises TypeError if coerce does not return a two element array - [x,y]. + public static RubyArray Coerce(RubyContext/*!*/ context, object self, object other) { + RubyArray array; + array = _CoerceSite.Target(_CoerceSite, context, self, other); + if (array == null || array.Count != 2) { + throw RubyExceptions.CreateTypeError("coerce must return [x, y]"); + } + return array; + } + + private static readonly CallSite> _CoerceSite = CallSite>.Create( + InstanceCallAction("coerce", 1)); + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/RubyStruct.cs b/merlin/main/languages/ruby/Ruby/Builtins/RubyStruct.cs new file mode 100644 index 0000000000..4256905098 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/RubyStruct.cs @@ -0,0 +1,251 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Scripting; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Actions; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Ast = System.Linq.Expressions.Expression; +using IronRuby.Compiler; +using System.Collections.ObjectModel; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Builtins { + + // TODO: freezing + public sealed class RubyStruct : RubyObject { + /// + /// This class represents the type information of a Struct. There is one + /// instance of this per Struct RubyClass; all instances of that class shares + /// one instance of StructInfo. + /// + internal sealed class Info { + internal readonly string[] Members; + internal int Length { get { return Members.Length; } } + + // This dictionary is an immutable hash used for fast lookup on the SymbolId indexer + internal readonly Dictionary IndexMap; + + internal RubyArray/*!*/ GetMembers() { + RubyArray list = new RubyArray(Members.Length); + foreach (string id in Members) { + list.Add(MutableString.Create(id)); + } + return list; + } + + internal Info(string[]/*!*/ members) { + Members = members; + IndexMap = new Dictionary(members.Length); + for (int i = 0; i < members.Length; i++) { + // overwrites duplicates: + IndexMap[members[i]] = i; + } + } + } + + private readonly object[]/*!*/ _data; + + #region Construction + + // This class should not have a default constructor (or one that takes RubyClass or RubyContext)! + // Struct class provides its own allocator (Struct#new), + // The default allocator should never be called unless Struct#new is removed and a derived class is constructed. + private RubyStruct(RubyClass/*!*/ rubyClass, bool dummy) + : base(rubyClass) { + Debug.Assert(rubyClass.StructInfo != null); + _data = new object[rubyClass.StructInfo.Length]; + } + + // copy ctor: + private RubyStruct(RubyClass/*!*/ rubyClass, object[]/*!*/ data) + : base(rubyClass) { + Debug.Assert(rubyClass.StructInfo != null); + _data = ArrayUtils.Copy(data); + } + + protected override RubyObject/*!*/ CreateInstance() { + return new RubyStruct(Class, _data); + } + + public static RubyStruct/*!*/ Create(RubyClass/*!*/ rubyClass) { + return new RubyStruct(rubyClass, true); + } + + // triggers "inherited" event, adds constant to the owner + public static RubyClass/*!*/ DefineStruct(RubyClass/*!*/ owner, string className, string[]/*!*/ attributeNames) { + // MRI: "inherited" event is triggered by DefineClass before the members are defined and the body is evaluated. + // Any exception thrown by the event will cancel struct initialization. + RubyClass result = owner.Context.DefineClass(owner, className, owner); + result.StructInfo = new Info(attributeNames); + + AddClassMembers(result, attributeNames); + + return result; + } + + // add methods to the generated class + private static void AddClassMembers(RubyClass/*!*/ cls, string[]/*!*/ structMembers) { + var newInstance = new RuleGenerator(RuleGenerators.InstanceConstructor); + + cls.SingletonClass.DefineRuleGenerator("[]", (int)RubyMethodAttributes.PublicSingleton, newInstance); + cls.SingletonClass.DefineRuleGenerator("new", (int)RubyMethodAttributes.PublicSingleton, newInstance); + + cls.SingletonClass.DefineLibraryMethod("members", (int)RubyMethodAttributes.PublicSingleton, + new Func>(GetMembers) + ); + + for (int i = 0; i < structMembers.Length; i++) { + string getter = structMembers[i]; + cls.DefineRuleGenerator(getter, (int)RubyMethodAttributes.PublicInstance, CreateGetter(i)); + cls.DefineRuleGenerator(getter + '=', (int)RubyMethodAttributes.PublicInstance, CreateSetter(i)); + } + } + + // Derived struct: [RubyMethod("members", RubyMethodAttributes.PublicSingleton)] + public static RubyArray/*!*/ GetMembers(RubyClass/*!*/ self) { + Debug.Assert(self.StructInfo != null); + return self.StructInfo.GetMembers(); + } + + public static RubyArray/*!*/ GetMembers(RubyStruct/*!*/ self) { + Debug.Assert(self.Class.StructInfo != null); + return self.Class.StructInfo.GetMembers(); + } + + private static RuleGenerator/*!*/ CreateGetter(int index) { + return delegate(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + metaBuilder.Result = Ast.Call( + Ast.Convert(args.TargetExpression, typeof(RubyStruct)), + typeof(RubyStruct).GetMethod("GetValue"), + Ast.Constant(index) + ); + }; + } + + private static RuleGenerator/*!*/ CreateSetter(int index) { + return delegate(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + + var actualArgs = RubyMethodGroupInfo.MakeActualArgs(metaBuilder, args, true, false, false, false); + + metaBuilder.Result = Ast.Call( + Ast.Convert(actualArgs[0], typeof(RubyStruct)), + typeof(RubyStruct).GetMethod("SetValue"), + Ast.Constant(index), + Ast.Convert(actualArgs[1], typeof(object)) + ); + }; + } + + #endregion + + public int GetIndex(string/*!*/ name) { + int result; + if (Class.StructInfo.IndexMap.TryGetValue(name, out result)) { + return result; + } + throw RubyExceptions.CreateNameError(String.Format("no member `{0}' in struct", name)); + } + + public override int GetHashCode() { + // hash is: struct's hash, plus data hashes + int hash = Class.StructInfo.GetHashCode(); + foreach (object obj in _data) { + hash ^= RubyUtils.GetHashCode(obj); + } + return hash; + } + + public override bool Equals(object obj) { + var other = obj as RubyStruct; + if (!StructReferenceEquals(other)) { + return false; + } + + Debug.Assert(_data.Length == other._data.Length); + for (int i = 0; i < _data.Length; i++) { + if (!RubyUtils.ValueEquals(_data[i], other._data[i])) { + return false; + } + } + + return true; + } + + public bool StructReferenceEquals(RubyStruct other) { + return ReferenceEquals(this, other) || (other != null && Class == other.Class); + } + + #region Emitted Helpers + + [Emitted] + public object GetValue(int index) { + return _data[index]; + } + + [Emitted] + public object SetValue(int index, object value) { + return _data[index] = value; + } + + #endregion + + + public IEnumerable>/*!*/ GetItems() { + for (int i = 0; i < _data.Length; i++) { + yield return new KeyValuePair(Class.StructInfo.Members[i], _data[i]); + } + } + + public object[]/*!*/ Values { + get { return _data; } + } + + public ReadOnlyCollection/*!*/ GetNames() { + return new ReadOnlyCollection(Class.StructInfo.Members); + } + + public void SetValues(object[]/*!*/ items) { + ContractUtils.RequiresNotNull(items, "items"); + + if (items.Length > _data.Length) { + throw RubyExceptions.CreateArgumentError("struct size differs"); + } + + Array.Copy(items, _data, items.Length); + } + + public object this[string name] { + get { return _data[GetIndex(name)]; } + set { _data[GetIndex(name)] = value; } + } + + public object this[int index] { + get { return _data[index]; } + set { _data[index] = value; } + } + + public int ItemCount { + get { return _data.Length; } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Builtins/TypeDescriptor.Generated.cs b/merlin/main/languages/ruby/Ruby/Builtins/TypeDescriptor.Generated.cs new file mode 100644 index 0000000000..6e66a59661 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Builtins/TypeDescriptor.Generated.cs @@ -0,0 +1,81 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ICustomTypeDescriptor + +using System; +using System.ComponentModel; +using IronRuby.Runtime; + +namespace IronRuby.Builtins { + // TODO: Generate automatically from a script + // TODO: Generate for other "base" classes + public partial class RubyObject : ICustomTypeDescriptor { + + #region ICustomTypeDescriptor Members + + AttributeCollection ICustomTypeDescriptor.GetAttributes() { + return CustomTypeDescHelpers.GetAttributes(this); + } + + string ICustomTypeDescriptor.GetClassName() { + return CustomTypeDescHelpers.GetClassName(this); + } + + string ICustomTypeDescriptor.GetComponentName() { + return CustomTypeDescHelpers.GetComponentName(this); + } + + TypeConverter ICustomTypeDescriptor.GetConverter() { + return CustomTypeDescHelpers.GetConverter(this); + } + + EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { + return CustomTypeDescHelpers.GetDefaultEvent(this); + } + + PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { + return CustomTypeDescHelpers.GetDefaultProperty(this); + } + + object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { + return CustomTypeDescHelpers.GetEditor(this, editorBaseType); + } + + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) { + return CustomTypeDescHelpers.GetEvents(this, attributes); + } + + EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { + return CustomTypeDescHelpers.GetEvents(this); + } + + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { + return CustomTypeDescHelpers.GetProperties(this, attributes); + } + + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { + return CustomTypeDescHelpers.GetProperties(this); + } + + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { + return CustomTypeDescHelpers.GetPropertyOwner(this, pd); + } + + #endregion + } +} + +#endif // !SILVERLIGHT diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Arguments.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Arguments.cs new file mode 100644 index 0000000000..7b3ccc76d4 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Arguments.cs @@ -0,0 +1,197 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using MSA = System.Linq.Expressions; + + public partial class Arguments : Node { + internal static readonly Arguments Empty = new Arguments(SourceSpan.None); + + private readonly List _expressions; + private readonly List _maplets; + private readonly Expression _array; + + public bool IsEmpty { + get { return _expressions == null && _maplets == null && _array == null; } + } + + public List Expressions { get { return _expressions; } } + public List Maplets { get { return _maplets; } } + public Expression Array { get { return _array; } } + + public Arguments(SourceSpan location) + : base(location) { + _expressions = null; + _maplets = null; + _array = null; + } + + public Arguments(Expression/*!*/ arg) + : base(arg.Location) { + ContractUtils.RequiresNotNull(arg, "arg"); + + _expressions = new List(); + _expressions.Add(arg); + _maplets = null; + _array = null; + } + + public Arguments(List arguments, List maplets, Expression array, SourceSpan location) + : base(location) { + if (arguments != null) { + ContractUtils.RequiresNotNullItems(arguments, "arguments"); + } + if (maplets != null) { + ContractUtils.RequiresNotNullItems(maplets, "maplets"); + } + + _expressions = arguments; + _maplets = maplets; + _array = array; + } + + #region Transform to Call Expression + + private List/*!*/ MakeSimpleArgumentExpressionList(AstGenerator/*!*/ gen) { + var args = new List(); + + if (_expressions != null) { + gen.TranformExpressions(_expressions, args); + } + + if (_maplets != null) { + args.Add(gen.TransformToHashConstructor(_maplets)); + } + + return args; + } + + internal void TransformToCall(AstGenerator/*!*/ gen, CallBuilder callBuilder) { + if (_expressions != null) { + foreach (var arg in _expressions) { + callBuilder.Add(arg.TransformRead(gen)); + } + } + + if (_maplets != null) { + callBuilder.Add(gen.TransformToHashConstructor(_maplets)); + } + + if (_array != null) { + callBuilder.SplattedArgument = _array.TransformRead(gen); + } + } + + #endregion + + #region Transform To Yield + + internal MSA.Expression/*!*/ TransformToYield(AstGenerator/*!*/ gen, MSA.Expression/*!*/ bfcVariable, MSA.Expression/*!*/ selfExpression) { + var args = (_expressions != null) ? gen.TranformExpressions(_expressions) : new List(); + + if (_maplets != null) { + args.Add(gen.TransformToHashConstructor(_maplets)); + } + + return AstFactory.YieldExpression( + args, + (_array != null) ? _array.TransformRead(gen) : null, // splatted argument + null, // rhs argument + bfcVariable, + selfExpression + ); + } + + #endregion + + #region Transform Read + + internal MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen, bool doSplat) { + Assert.NotNull(gen); + return TransformRead(gen, + MakeSimpleArgumentExpressionList(gen), + (_array != null) ? _array.TransformRead(gen) : null, + doSplat + ); + } + + internal static MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen, List/*!*/ rightValues, + MSA.Expression splattedValue, bool doSplat) { + + Assert.NotNull(gen, rightValues); + + MSA.Expression result; + + // We need to distinguish various special cases here. + // For parallel assignment specification, see "Ruby Language.docx/Runtime/Parallel Assignment". + + // R(0,*)? + bool rightNoneSplat = rightValues.Count == 0 && splattedValue != null; + + // R(1,-)? + bool rightOneNone = rightValues.Count == 1 && splattedValue == null; + + if (rightNoneSplat) { + result = (doSplat ? Methods.Splat : Methods.Unsplat).OpCall(AstFactory.Box(splattedValue)); + } else if (rightOneNone && doSplat) { + result = rightValues[0]; + } else { + result = Methods.MakeArrayOpCall(rightValues); + + if (splattedValue != null) { + result = Methods.SplatAppend.OpCall(result, AstFactory.Box(splattedValue)); + } + } + return result; + } + + #endregion + + #region Tranform to Return Value + + private MSA.Expression/*!*/ TransformToReturnValue(AstGenerator/*!*/ gen) { + Assert.NotNull(gen); + + if (_maplets != null && _expressions == null && _array == null) { + return gen.TransformToHashConstructor(_maplets); + } + + return TransformRead(gen, true /* Splat */); + } + + internal static MSA.Expression/*!*/ TransformToReturnValue(AstGenerator/*!*/ gen, Arguments arguments) { + return (arguments != null) ? arguments.TransformToReturnValue(gen) : Ast.Constant(null); + } + + #endregion + + #region Transform To Array + + internal static MSA.Expression/*!*/ TransformToArray(AstGenerator/*!*/ gen, Arguments arguments) { + Assert.NotNull(gen); + return (arguments != null) ? arguments.TransformRead(gen, false /* Unsplat */) : Methods.MakeArray0.OpCall(); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Assignments/CompoundRightValue.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Assignments/CompoundRightValue.cs new file mode 100644 index 0000000000..f689842846 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Assignments/CompoundRightValue.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + + public class CompoundRightValue { + private readonly List/*!*/ _rightValues; + private readonly Expression _splattedValue; + + public List/*!*/ RightValues { + get { return _rightValues; } + } + + public Expression SplattedValue { + get { return _splattedValue; } + } + + public CompoundRightValue(List/*!*/ rightValues, Expression splattedValue) { + Assert.NotNull(rightValues); + + _rightValues = rightValues; + _splattedValue = splattedValue; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Assignments/MemberAssignmentExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Assignments/MemberAssignmentExpression.cs new file mode 100644 index 0000000000..1f6854f215 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Assignments/MemberAssignmentExpression.cs @@ -0,0 +1,86 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + // left.id op= rigth + public partial class MemberAssignmentExpression : AssignmentExpression { + private readonly Expression/*!*/ _leftTarget; + private readonly Expression/*!*/ _right; + private readonly string/*!*/ _memberName; + + public Expression/*!*/ LeftTarget { + get { return _leftTarget; } + } + + public Expression/*!*/ Right { + get { return _right; } + } + + public string/*!*/ MemberName { + get { return _memberName; } + } + + public MemberAssignmentExpression(Expression/*!*/ leftTarget, string/*!*/ memberName, string/*!*/ operation, Expression/*!*/ right, SourceSpan location) + : base(operation, location) { + ContractUtils.RequiresNotNull(leftTarget, "leftTarget"); + ContractUtils.RequiresNotNull(operation, "operation"); + ContractUtils.RequiresNotNull(right, "right"); + ContractUtils.RequiresNotNull(memberName, "memberName"); + ContractUtils.RequiresNotNull(operation, "operation"); + + _memberName = memberName; + _leftTarget = leftTarget; + _right = right; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + string setterName = _memberName + "="; + + MSA.Expression transformedLeftTarget = _leftTarget.TransformRead(gen); + MSA.Expression transformedRight = _right.TransformRead(gen); + + MSA.Expression leftTemp = gen.CurrentScope.DefineHiddenVariable(String.Empty, transformedLeftTarget.Type); + + bool leftIsSelf = _leftTarget.NodeType == NodeTypes.SelfReference; + + // lhs &&= rhs --> left.member && (left.member = rhs) + // lhs ||= rhs --> left.member || (left.member = rhs) + if (Operation == Symbols.And || Operation == Symbols.Or) { + MSA.Expression leftMemberRead = MethodCall.TransformRead(this, gen, false, _memberName, Ast.Assign(leftTemp, transformedLeftTarget), null, null, null, null); + MSA.Expression transformedWrite = MethodCall.TransformRead(this, gen, leftIsSelf, setterName, leftTemp, null, null, null, transformedRight); + + if (Operation == Symbols.And) { + return AndExpression.TransformRead(gen, leftMemberRead, transformedWrite); + } else { + return OrExpression.TransformRead(gen, leftMemberRead, transformedWrite); + } + } else { + // left.member= left.member().op(right) + MSA.Expression leftMemberRead = MethodCall.TransformRead(this, gen, false, _memberName, leftTemp, null, null, null, null); + MSA.Expression operationCall = MethodCall.TransformRead(this, gen, false, Operation, leftMemberRead, null, null, transformedRight, null); + MSA.Expression transformedWrite = MethodCall.TransformRead(this, gen, leftIsSelf, setterName, Ast.Assign(leftTemp, transformedLeftTarget), null, null, null, operationCall); + return transformedWrite; + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Assignments/ParallelAssignmentExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Assignments/ParallelAssignmentExpression.cs new file mode 100644 index 0000000000..18b98d9dad --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Assignments/ParallelAssignmentExpression.cs @@ -0,0 +1,51 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + + /// + /// compound-lhs = compound-rhs + /// + public partial class ParallelAssignmentExpression : AssignmentExpression { + private readonly CompoundLeftValue/*!*/ _lhs; + private readonly CompoundRightValue/*!*/ _rhs; + + public CompoundLeftValue/*!*/ Left { + get { return _lhs; } + } + + public CompoundRightValue/*!*/ Right { + get { return _rhs; } + } + + public ParallelAssignmentExpression(CompoundLeftValue/*!*/ lhs, CompoundRightValue/*!*/ rhs, SourceSpan location) + : base(null, location) { + Assert.NotNull(lhs, rhs); + + _lhs = lhs; + _rhs = rhs; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + Assert.NotNull(gen); + return _lhs.TransformWrite(gen, _rhs); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Assignments/SimpleAssignmentExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Assignments/SimpleAssignmentExpression.cs new file mode 100644 index 0000000000..9f6c4cd9c6 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Assignments/SimpleAssignmentExpression.cs @@ -0,0 +1,101 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + /// + /// lhs = rhs + /// lhs op= rhs + /// + public partial class SimpleAssignmentExpression : AssignmentExpression { + private readonly LeftValue/*!*/ _left; + private readonly Expression/*!*/ _right; + + public LeftValue/*!*/ Left { + get { return _left; } + } + + public Expression/*!*/ Right { + get { return _right; } + } + + public SimpleAssignmentExpression(LeftValue/*!*/ left, Expression/*!*/ right, string operation, SourceSpan location) + : base(operation, location) { + Assert.NotNull(left, right); + + _left = left; + _right = right; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + + // first, read target into a temp: + MSA.Expression transformedLeftTarget = _left.TransformTargetRead(gen); + MSA.Expression leftTargetTemp; + + if (transformedLeftTarget != null) { + leftTargetTemp = gen.CurrentScope.DefineHiddenVariable(String.Empty, transformedLeftTarget.Type); + } else { + leftTargetTemp = null; + } + + MSA.Expression transformedRight = _right.TransformRead(gen); + + // lhs &&= rhs --> lhs && (lhs = rhs) + // lhs ||= rhs --> lhs || (lhs = rhs) + if (Operation == Symbols.And || Operation == Symbols.Or) { + MSA.Expression transformedLeftRead = _left.TransformRead(gen, + (transformedLeftTarget != null) ? Ast.Assign(leftTargetTemp, transformedLeftTarget) : null, + true // tryRead + ); + + MSA.Expression transformedWrite = _left.TransformWrite(gen, leftTargetTemp, transformedRight); + + if (Operation == Symbols.And) { + return AndExpression.TransformRead(gen, transformedLeftRead, transformedWrite); + } else { + return OrExpression.TransformRead(gen, transformedLeftRead, transformedWrite); + } + } else { + // lhs op= rhs --> lhs = lhs op rhs + if (Operation != null) { + MSA.Expression transformedLeftRead = _left.TransformRead(gen, leftTargetTemp, false); + transformedRight = MethodCall.TransformRead(this, gen, false, Operation, transformedLeftRead, null, null, transformedRight, null); + } + + // transform lhs write assigning lhs-target temp: + return _left.TransformWrite(gen, + (transformedLeftTarget != null) ? Ast.Assign(leftTargetTemp, transformedLeftTarget) : null, + transformedRight + ); + } + } + + internal override string GetNodeName(AstGenerator gen) { + if (Operation == Symbols.And || Operation == Symbols.Or) { + return "expression"; + } else { + return base.GetNodeName(gen); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/AstFactory.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/AstFactory.cs new file mode 100644 index 0000000000..e0d653d92d --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/AstFactory.cs @@ -0,0 +1,237 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Reflection; +using System.Dynamic; +using System.Threading; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +using AstUtils = Microsoft.Scripting.Ast.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public static class AstFactory { + + public static readonly MSA.Expression[] EmptyExpressions = new MSA.Expression[0]; + public static readonly MSA.ParameterExpression[] EmptyParameters = new MSA.ParameterExpression[0]; + public static readonly MSA.Expression NullOfMutableString = Ast.Constant(null, typeof(MutableString)); + public static readonly MSA.Expression NullOfProc = Ast.Constant(null, typeof(Proc)); + internal static readonly MSA.Expression BlockReturnReasonBreak = Ast.Constant(BlockReturnReason.Break); + + #region Control Flow + + internal static MSA.Expression/*!*/ MakeUserMethodBody(AstGenerator gen, int lastLine, + MSA.Expression/*!*/ blockParameter, MSA.Expression/*!*/ rfcVariable, + MSA.ParameterExpression/*!*/ methodUnwinder, MSA.Expression/*!*/ bodyStatement, ResultOperation resultOperation, + int profileTickIndex, MSA.ParameterExpression stampVariable, MSA.LabelTarget returnLabel) { + + Assert.NotNull(blockParameter, rfcVariable, bodyStatement, methodUnwinder); + Debug.Assert(!resultOperation.IsIgnore, "return value should not be ignored"); + Debug.Assert(returnLabel != null || resultOperation.Variable != null, "return label needed"); + + MSA.Expression resultExpression = Ast.Field(methodUnwinder, MethodUnwinder.ReturnValueField); + if (resultOperation.Variable != null) { + resultExpression = Ast.Assign(resultOperation.Variable, resultExpression); + } else { + resultExpression = Ast.Return(returnLabel, resultExpression); + } + + // TODO: move this to the caller: + MSA.Expression profileStart, profileEnd; + if (stampVariable != null) { + profileStart = Ast.Assign(stampVariable, Methods.Stopwatch_GetTimestamp.OpCall()); + profileEnd = Methods.UpdateProfileTicks.OpCall(Ast.Constant(profileTickIndex), stampVariable); + } else { + profileStart = profileEnd = Ast.Empty(); + } + + return AstUtils.Try( + // initialize frame (RFC): + profileStart, + Ast.Assign(rfcVariable, Methods.CreateRfcForMethod.OpCall(AstUtils.Convert(blockParameter, typeof(Proc)))), + bodyStatement + ).Filter(methodUnwinder, Ast.Equal(Ast.Field(methodUnwinder, MethodUnwinder.TargetFrameField), rfcVariable), + + // return unwinder.ReturnValue; + resultExpression + + ).Finally( + Ast.Assign(Ast.Field(rfcVariable, RuntimeFlowControl.IsActiveMethodField), Ast.Constant(false)), + profileEnd, + gen != null && gen.TraceEnabled ? Methods.TraceMethodReturn.OpCall( + gen.CurrentScopeVariable, + Ast.Convert(Ast.Constant(gen.SourceUnit.Path), typeof(string)), + Ast.Constant(lastLine) + ) : Ast.Empty() + ); + } + + #endregion + + public static MSA.Expression/*!*/ Infinite(MSA.LabelTarget @break, MSA.LabelTarget @continue, params MSA.Expression[]/*!*/ body) { + return AstUtils.Infinite(Ast.Void(Ast.Block(body)), @break, @continue); + } + + public static MSA.Expression[]/*!*/ CreateExpressionArray(int count) { + return (count > 0) ? new MSA.Expression[count] : EmptyExpressions; + } + + public static MSA.Expression/*!*/ Block(params MSA.Expression/*!*/[]/*!*/ expressions) { + switch (expressions.Length) { + case 0: return Ast.Empty(); + case 1: return expressions[0]; + default: return Ast.Block(new ReadOnlyCollection(expressions)); + } + } + + public static MSA.Expression/*!*/ Block(List/*!*/ expressions) { + switch (expressions.Count) { + case 0: return Ast.Empty(); + case 1: return expressions[0]; + default: return Ast.Block(new ReadOnlyCollection(expressions.ToArray())); + } + } + + public static MSA.Expression/*!*/ Box(MSA.Expression/*!*/ expression) { + return AstUtils.Convert(expression, typeof(object)); + } + + public static MSA.Expression/*!*/ IsTrue(MSA.Expression/*!*/ expression) { + if (expression.Type == typeof(bool)) { + return expression; + } else { + return Methods.IsTrue.OpCall(Box(expression)); + } + } + + public static MSA.Expression/*!*/ IsFalse(MSA.Expression/*!*/ expression) { + if (expression.Type == typeof(bool)) { + return Ast.Not(expression); + } else { + return Methods.IsFalse.OpCall(Box(expression)); + } + } + + internal static TryStatementBuilder/*!*/ FinallyIf(this TryStatementBuilder/*!*/ builder, bool ifdef, params MSA.Expression[]/*!*/ body) { + return ifdef ? builder.Finally(body) : builder; + } + + internal static TryStatementBuilder/*!*/ FilterIf(this TryStatementBuilder/*!*/ builder, bool ifdef, + MSA.ParameterExpression/*!*/ holder, MSA.Expression/*!*/ condition, params MSA.Expression[]/*!*/ body) { + return ifdef ? builder.Filter(holder, condition, body) : builder; + } + + public static MSA.Expression/*!*/ Condition(MSA.Expression/*!*/ test, MSA.Expression/*!*/ ifTrue, MSA.Expression/*!*/ ifFalse) { + Assert.NotNull(test, ifTrue, ifFalse); + Debug.Assert(test.Type == typeof(bool)); + + if (ifTrue.Type != ifFalse.Type) { + if (ifTrue.Type.IsAssignableFrom(ifFalse.Type)) { + ifFalse = Ast.Convert(ifFalse, ifTrue.Type); + } else if (ifFalse.Type.IsAssignableFrom(ifTrue.Type)) { + ifTrue = Ast.Convert(ifTrue, ifFalse.Type); + } else { + ifTrue = Box(ifTrue); + ifFalse = Box(ifFalse); + } + } + + return Ast.Condition(test, ifTrue, ifFalse); + } + + internal static MSA.Expression/*!*/ CallDelegate(Delegate/*!*/ method, MSA.Expression[]/*!*/ arguments) { + // We prefer to peek inside the delegate and call the target method directly. However, we need to + // exclude DynamicMethods since Delegate.Method returns a dummy MethodInfo, and we cannot emit a call to it. + if (method.Method.DeclaringType == null || !method.Method.DeclaringType.IsPublic || !method.Method.IsPublic) { + // do not inline: + return Ast.Call(Ast.Constant(method), method.GetType().GetMethod("Invoke"), arguments); + } else if (method.Target != null) { + if (method.Method.IsStatic) { + // inline a closed static delegate: + return Ast.Call(null, method.Method, ArrayUtils.Insert(Ast.Constant(method.Target), arguments)); + } else { + // inline a closed instance delegate: + return Ast.Call(Ast.Constant(method.Target), method.Method, arguments); + } + } else if (method.Method.IsStatic) { + // inline an open static delegate: + return Ast.Call(null, method.Method, arguments); + } else { + // inline an open instance delegate: + return Ast.Call(arguments[0], method.Method, ArrayUtils.RemoveFirst(arguments)); + } + } + + internal static Type/*!*/[]/*!*/ GetSignature(MSA.ParameterExpression/*!*/[]/*!*/ parameters, Type/*!*/ returnType) { + Type[] result = new Type[parameters.Length + 1]; + for (int i = 0; i < parameters.Length; i++) { + result[i] = parameters[i].Type; + } + + // return type: + result[result.Length - 1] = returnType; + return result; + } + + internal static MSA.Expression/*!*/ YieldExpression( + IList/*!*/ arguments, + MSA.Expression splattedArgument, + MSA.Expression rhsArgument, + MSA.Expression/*!*/ bfcVariable, + MSA.Expression/*!*/ selfArgument) { + + Assert.NotNull(arguments, bfcVariable, selfArgument); + + bool hasArgumentArray; + var opMethod = Methods.Yield(arguments.Count, splattedArgument != null, rhsArgument != null, out hasArgumentArray); + + var args = new List(); + + foreach (var arg in arguments) { + args.Add(AstFactory.Box(arg)); + } + + if (hasArgumentArray) { + args = CollectionUtils.MakeList(Ast.NewArrayInit(typeof(object), args)); + } + + if (splattedArgument != null) { + args.Add(AstFactory.Box(splattedArgument)); + } + + if (rhsArgument != null) { + args.Add(AstFactory.Box(rhsArgument)); + } + + args.Add(AstFactory.Box(selfArgument)); + args.Add(bfcVariable); + + return Ast.Call(opMethod, args.ToArray()); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Block.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Block.cs new file mode 100644 index 0000000000..9b96ac5fb6 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Block.cs @@ -0,0 +1,29 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + public abstract class Block : Node { + protected Block(SourceSpan location) + : base(location) { + } + + public abstract bool IsDefinition { get; } + + internal abstract MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen); + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/BlockDefinition.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/BlockDefinition.cs new file mode 100644 index 0000000000..ad65776706 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/BlockDefinition.cs @@ -0,0 +1,219 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class BlockDefinition : Block { + // { |args| body } + + // self, block flow control: + internal const int HiddenParameterCount = BlockDispatcher.HiddenParameterCount; + + // if the block has more parameters object[] is used: + internal const int MaxBlockArity = BlockDispatcher.MaxBlockArity; + + private readonly CompoundLeftValue/*!*/ _parameters; + private readonly List/*!*/ _body; + private readonly LexicalScope _definedScope; + + public CompoundLeftValue/*!*/ Parameters { + get { return _parameters; } + } + + public List/*!*/ Body { + get { return _body; } + } + + public sealed override bool IsDefinition { get { return true; } } + + private bool HasFormalParametersInArray { + get { return _parameters.LeftValues.Count > MaxBlockArity || HasUnsplatParameter; } + } + + private bool HasUnsplatParameter { + get { return _parameters.UnsplattedValue != null; } + } + + public bool HasSignature { + get { return _parameters != CompoundLeftValue.UnspecifiedBlockSignature; } + } + + public BlockDefinition(LexicalScope definedScope, CompoundLeftValue/*!*/ parameters, List/*!*/ body, SourceSpan location) + : base(location) { + Assert.NotNull(parameters, body); + + _definedScope = definedScope; + _body = body; + _parameters = parameters; + } + + private MSA.ParameterExpression[]/*!*/ DefineParameters(out MSA.Expression/*!*/ selfVariable, out MSA.Expression/*!*/ blockParamVariable) { + var parameters = new MSA.ParameterExpression[ + HiddenParameterCount + + (HasFormalParametersInArray ? 1 : _parameters.LeftValues.Count) + + (HasUnsplatParameter ? 1 : 0) + ]; + + // hidden parameters: + // #proc must be the first one - it is used as instance target for method invocation: + blockParamVariable = parameters[0] = Ast.Parameter(typeof(BlockParam), "#proc"); + selfVariable = parameters[1] = Ast.Parameter(typeof(object), "#self"); + + int i = HiddenParameterCount; + if (HasFormalParametersInArray) { + parameters[i++] = Ast.Parameter(typeof(object[]), "#parameters"); + } else { + for (; i < parameters.Length; i++) { + parameters[i] = Ast.Parameter(typeof(object), "#" + (i - HiddenParameterCount)); + } + } + + if (HasUnsplatParameter) { + parameters[i] = Ast.Parameter(typeof(RubyArray), "#array"); + } + + return parameters; + } + + internal override MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + MSA.Expression parentScope = gen.CurrentScopeVariable; + ScopeBuilder scope = new ScopeBuilder(); + + // define hidden parameters and RHS-placeholders (#1..#n will be used as RHS of a parallel assignment): + MSA.Expression blockParameter, selfParameter; + MSA.ParameterExpression[] parameters = DefineParameters(out selfParameter, out blockParameter); + + MSA.Expression scopeVariable = scope.DefineHiddenVariable("#scope", typeof(RubyBlockScope)); + MSA.LabelTarget redoLabel = Ast.Label(); + + gen.EnterBlockDefinition( + scope, + blockParameter, + selfParameter, + scopeVariable, + redoLabel + ); + + if (_definedScope != null) { + _definedScope.TransformLocals(scope); + } + + MSA.Expression paramInit = MakeParametersInitialization(gen, parameters); + MSA.ParameterExpression blockUnwinder = scope.DefineHiddenVariable("#unwinder", typeof(BlockUnwinder)); + + MSA.Expression loop = AstFactory.Infinite(null, redoLabel, + AstUtils.Try( + gen.TransformStatements(_body, ResultOperation.Return) + ).Catch(blockUnwinder, + // redo: + AstUtils.IfThen(Ast.Field(blockUnwinder, BlockUnwinder.IsRedoField), Ast.Continue(redoLabel)), + + // next: + gen.Return(Ast.Field(blockUnwinder, BlockUnwinder.ReturnValueField)) + ) + ); + + if (gen.TraceEnabled) { + int firstStatementLine = _body.Count > 0 ? _body[0].Location.Start.Line : Location.End.Line; + int lastStatementLine = _body.Count > 0 ? _body[_body.Count - 1].Location.End.Line : Location.End.Line; + + loop = Ast.TryFinally( + Ast.Block( + Methods.TraceBlockCall.OpCall(scopeVariable, blockParameter, Ast.Convert(Ast.Constant(gen.SourceUnit.Path), typeof(string)), Ast.Constant(firstStatementLine)), + loop + ), + Methods.TraceBlockReturn.OpCall(scopeVariable, blockParameter, Ast.Convert(Ast.Constant(gen.SourceUnit.Path), typeof(string)), Ast.Constant(lastStatementLine)) + ); + } + + MSA.Expression body = Ast.Block( + Ast.Assign(scopeVariable, + Methods.CreateBlockScope.OpCall(scope.VisibleVariables(), parentScope, blockParameter, selfParameter) + ), + + paramInit, + + loop, + + Ast.Empty() + ); + + body = gen.AddReturnTarget(scope.CreateScope(body)); + gen.LeaveBlockDefinition(); + + int parameterCount = _parameters.LeftValues.Count; + + var attributes = _parameters.GetBlockSignatureAttributes(); + + return Methods.DefineBlock.OpCall( + gen.CurrentScopeVariable, + gen.CurrentRfcVariable, + gen.CurrentSelfVariable, + Ast.Lambda( + BlockDispatcher.GetDelegateType(parameterCount, attributes), + body, + gen.EncodeMethodName(gen.CurrentMethod.MethodName, Location), + new ReadOnlyCollection(parameters) + ), + Ast.Constant(parameterCount), + Ast.Constant(attributes) + ); + } + + private MSA.Expression/*!*/ MakeParametersInitialization(AstGenerator/*!*/ gen, MSA.Expression[]/*!*/ parameters) { + Assert.NotNull(gen); + Assert.NotNullItems(parameters); + + var result = AstFactory.CreateExpressionArray( + _parameters.LeftValues.Count + + (_parameters.UnsplattedValue != null ? 1 : 0) + + 1 + ); + + int resultIndex = 0; + + bool paramsInArray = HasFormalParametersInArray; + for (int i = 0; i < _parameters.LeftValues.Count; i++) { + var parameter = paramsInArray ? + Ast.ArrayAccess(parameters[HiddenParameterCount], Ast.Constant(i)) : + parameters[HiddenParameterCount + i]; + + result[resultIndex++] = _parameters.LeftValues[i].TransformWrite(gen, parameter); + } + + if (_parameters.UnsplattedValue != null) { + // the last parameter is unsplat: + var parameter = parameters[parameters.Length - 1]; + result[resultIndex++] = _parameters.UnsplattedValue.TransformWrite(gen, parameter); + } + + result[resultIndex++] = Ast.Empty(); + Debug.Assert(resultIndex == result.Length); + return AstFactory.Block(result); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/BlockReference.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/BlockReference.cs new file mode 100644 index 0000000000..f7d9922c90 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/BlockReference.cs @@ -0,0 +1,50 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Runtime.Calls; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class BlockReference : Block { + private readonly Expression/*!*/ _expression; + + public sealed override bool IsDefinition { get { return false; } } + + public Expression/*!*/ Expression { + get { return _expression; } + } + + public BlockReference(Expression/*!*/ expression, SourceSpan location) + : base(location) { + Assert.NotNull(expression); + + _expression = expression; + } + + internal override MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + Assert.NotNull(gen); + return Ast.Dynamic( + ConvertToProcAction.Instance, + typeof(Proc), + gen.CurrentScopeVariable, _expression.TransformRead(gen) + ); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Body.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Body.cs new file mode 100644 index 0000000000..339f42a949 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Body.cs @@ -0,0 +1,245 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + // begin/class/def/module/class << {expression} + // statements + // rescue-clauses + // else + // statements + // ensure + // statements + // end + public partial class Body : Expression { + private readonly List/*!*/ _statements; + private readonly List _rescueClauses; // optional + private readonly List _elseStatements; // optional + private readonly List _ensureStatements; // optional + + // TODO: readonly + public List/*!*/ Statements { get { return _statements; } } + public List RescueClauses { get { return _rescueClauses; } } + public List ElseStatements { get { return _elseStatements; } } + public List EnsureStatements { get { return _ensureStatements; } } + + private bool HasExceptionHandling { + get { + return (_statements.Count > 0 && _rescueClauses != null) || _elseStatements != null || _ensureStatements != null; + } + } + + public Body(List/*!*/ statements, List rescueClauses, List elseStatements, + List ensureStatements, SourceSpan location) + : base(location) { + + Assert.NotNull(statements); + + _statements = statements; + _rescueClauses = rescueClauses; + _elseStatements = elseStatements; + _ensureStatements = ensureStatements; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + Assert.NotNull(gen); + + if (HasExceptionHandling) { + MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#block-result", typeof(object)); + + return AstFactory.Block( + TransformExceptionHandling(gen, ResultOperation.Store(resultVariable)), + resultVariable + ); + + } else { + return gen.TransformStatementsToExpression(_statements); + } + } + + internal override MSA.Expression/*!*/ TransformResult(AstGenerator/*!*/ gen, ResultOperation resultOperation) { + Assert.NotNull(gen); + + if (HasExceptionHandling) { + return TransformExceptionHandling(gen, resultOperation); + } else { + return gen.TransformStatements(_statements, resultOperation); + } + } + + private MSA.Expression/*!*/ TransformExceptionHandling(AstGenerator/*!*/ gen, ResultOperation resultOperation) { + Assert.NotNull(gen); + + MSA.Expression exceptionThrownVariable = gen.CurrentScope.DefineHiddenVariable("#exception-thrown", typeof(bool)); + MSA.ParameterExpression exceptionVariable = gen.CurrentScope.DefineHiddenVariable("#exception", typeof(Exception)); + MSA.Expression exceptionRethrowVariable = gen.CurrentScope.DefineHiddenVariable("#exception-rethrow", typeof(bool)); + MSA.Expression retryingVariable = gen.CurrentScope.DefineHiddenVariable("#retrying", typeof(bool)); + MSA.ParameterExpression evalUnwinder = gen.CurrentScope.DefineHiddenVariable("#unwinder", typeof(EvalUnwinder)); + MSA.Expression oldExceptionVariable = gen.CurrentScope.DefineHiddenVariable("#old-exception", typeof(Exception)); + + MSA.Expression transformedBody; + MSA.Expression transformedEnsure; + MSA.Expression transformedElse; + + if (_ensureStatements != null) { + transformedEnsure = Ast.Block( + // ensure: + Ast.Assign(oldExceptionVariable, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)), + gen.TransformStatements(_ensureStatements, ResultOperation.Ignore), + Methods.SetCurrentException.OpCall(gen.CurrentScopeVariable, oldExceptionVariable), + + // rethrow: + AstUtils.IfThen( + Ast.AndAlso( + exceptionRethrowVariable, + Ast.NotEqual(oldExceptionVariable, Ast.Constant(null)) + ), + Ast.Throw(oldExceptionVariable) + ), + Ast.Empty() + ); + } else { + // rethrow: + transformedEnsure = AstUtils.IfThen( + Ast.AndAlso( + exceptionRethrowVariable, + Ast.NotEqual( + Ast.Assign(oldExceptionVariable, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)), + Ast.Constant(null, typeof(Exception))) + ), + Ast.Throw(oldExceptionVariable) + ); + } + + if (_elseStatements != null) { + transformedElse = gen.TransformStatements(_elseStatements, resultOperation); + } else { + transformedElse = Ast.Empty(); + } + + // body should do return, but else-clause is present => we cannot do return from the guarded statements: + // (the value of the last expression in the body cannot be the last executed expression statement => we can ignore it): + transformedBody = gen.TransformStatements(_statements, (_elseStatements != null) ? ResultOperation.Ignore : resultOperation); + + MSA.Expression setInRescueFlag = null, clearInRescueFlag = null; + var breakLabel = Ast.Label(); + var continueLabel = Ast.Label(); + + // make rescue clause: + MSA.Expression transformedRescue; + if (_rescueClauses != null) { + // outer-most EH blocks sets and clears runtime flag RuntimeFlowControl.InTryRescue: + if (gen.CurrentRescue == null) { + setInRescueFlag = Ast.Assign(Ast.Field(gen.CurrentRfcVariable, RuntimeFlowControl.InRescueField), Ast.Constant(true)); + clearInRescueFlag = Ast.Assign(Ast.Field(gen.CurrentRfcVariable, RuntimeFlowControl.InRescueField), Ast.Constant(false)); + } else { + setInRescueFlag = clearInRescueFlag = Ast.Empty(); + } + + gen.EnterRescueClause(retryingVariable, breakLabel, continueLabel); + + var handlers = new IfStatementTest[_rescueClauses.Count]; + for (int i = 0; i < handlers.Length; i++) { + handlers[i] = _rescueClauses[i].Transform(gen, resultOperation); + } + + transformedRescue = Ast.Block( + setInRescueFlag, + AstUtils.Try( + AstUtils.If(handlers, Ast.Assign(exceptionRethrowVariable, Ast.Constant(true))) + ).Filter(evalUnwinder, Ast.Equal(Ast.Field(evalUnwinder, EvalUnwinder.ReasonField), Ast.Constant(BlockReturnReason.Retry)), + Ast.Block( + Ast.Assign(retryingVariable, Ast.Constant(true)), + Ast.Continue(continueLabel), + Ast.Empty() + ) + ) + ); + + gen.LeaveRescueClause(); + + } else { + transformedRescue = Ast.Assign(exceptionRethrowVariable, Ast.Constant(true)); + } + + if (_elseStatements != null) { + transformedElse = AstUtils.Unless(exceptionThrownVariable, transformedElse); + } + + var result = AstFactory.Infinite(breakLabel, continueLabel, + Ast.Assign(exceptionThrownVariable, Ast.Constant(false)), + Ast.Assign(exceptionRethrowVariable, Ast.Constant(false)), + Ast.Assign(retryingVariable, Ast.Constant(false)), + + AstUtils.Try( + // save exception (old_$! is not used unless there is a rescue clause): + Ast.Block( + (_rescueClauses == null) ? (MSA.Expression)Ast.Empty() : + Ast.Assign(oldExceptionVariable, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)), + + AstUtils.Try( + transformedBody + ).Filter(exceptionVariable, Methods.CanRescue.OpCall(gen.CurrentRfcVariable, exceptionVariable), + Ast.Assign(exceptionThrownVariable, Ast.Constant(true)), + Methods.SetCurrentExceptionAndStackTrace.OpCall(gen.CurrentScopeVariable, exceptionVariable), + transformedRescue + ).FinallyIf((_rescueClauses != null), + // restore previous exception if the current one has been handled: + AstUtils.Unless(exceptionRethrowVariable, + Methods.SetCurrentException.OpCall(gen.CurrentScopeVariable, oldExceptionVariable) + ), + clearInRescueFlag + ), + + // unless (exception_thrown) do end + transformedElse, + Ast.Empty() + ) + ).FilterIf((_rescueClauses != null || _elseStatements != null), + exceptionVariable, Methods.CanRescue.OpCall(gen.CurrentRfcVariable, exceptionVariable), + Ast.Block( + Methods.SetCurrentExceptionAndStackTrace.OpCall(gen.CurrentScopeVariable, exceptionVariable), + Ast.Assign(exceptionRethrowVariable, Ast.Constant(true)), + Ast.Empty() + ) + ).Finally( + AstUtils.Unless(retryingVariable, transformedEnsure) + ), + + Ast.Break(breakLabel) + ); + + return result; + } + + internal override Expression/*!*/ ToCondition() { + // propagates 'in condition' property if we have a single element: + if (_statements != null && _statements.Count == 1 && !HasExceptionHandling) { + _statements[0].ToCondition(); + } + return this; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/CallBuilder.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/CallBuilder.cs new file mode 100644 index 0000000000..c0cfa87ccc --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/CallBuilder.cs @@ -0,0 +1,110 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using System.Diagnostics; +using Microsoft.Scripting.Actions; +using IronRuby.Runtime.Calls; +using IronRuby.Builtins; + +namespace IronRuby.Compiler.Ast { + using MSA = System.Linq.Expressions; + using Ast = System.Linq.Expressions.Expression; + using AstUtils = Microsoft.Scripting.Ast.Utils; + + /// + /// Simple helper for building up method call actions. + /// + internal class CallBuilder { + private readonly AstGenerator _gen; + + private readonly List/*!*/ _args = new List(); + + // TODO: + public MSA.Expression Instance; + public MSA.Expression SplattedArgument; + public MSA.Expression Block; + public MSA.Expression RhsArgument; + + internal CallBuilder(AstGenerator gen) { + _gen = gen; + } + + public void Add(MSA.Expression/*!*/ expression) { + _args.Add(expression); + } + + private RubyCallSignature MakeCallSignature(bool hasImplicitSelf) { + return new RubyCallSignature(true, hasImplicitSelf, _args.Count, SplattedArgument != null, Block != null, RhsArgument != null); + } + + public MSA.DynamicExpression/*!*/ MakeCallAction(string/*!*/ name, bool hasImplicitSelf) { + return MakeCallAction(name, _gen.Binder, MakeCallSignature(hasImplicitSelf), GetExpressions()); + } + + public static MSA.DynamicExpression/*!*/ MakeCallAction(string/*!*/ name, ActionBinder/*!*/ binder, RubyCallSignature signature, + params MSA.Expression[]/*!*/ args) { + RubyCallAction call = RubyCallAction.Make(name, signature); + switch (args.Length) { + case 0: return Ast.Dynamic(call, typeof(object), AstFactory.EmptyExpressions); + case 1: return Ast.Dynamic(call, typeof(object), args[0]); + case 2: return Ast.Dynamic(call, typeof(object), args[0], args[1]); + case 3: return Ast.Dynamic(call, typeof(object), args[0], args[1], args[2]); + case 4: return Ast.Dynamic(call, typeof(object), args[0], args[1], args[2], args[3]); + default: + return Ast.Dynamic( + call, + typeof(object), + new ReadOnlyCollection(args) + ); + } + } + + public MSA.Expression/*!*/ MakeSuperCallAction(int lexicalScopeId) { + return Ast.Dynamic( + SuperCallAction.Make(MakeCallSignature(true), lexicalScopeId), + typeof(object), + GetExpressions() + ); + } + + private MSA.Expression/*!*/[]/*!*/ GetExpressions() { + var result = new List(); + result.Add(_gen.CurrentScopeVariable); + result.Add(Instance); + + if (Block != null) { + result.Add(Block); + } + + for (int i = 0; i < _args.Count; i++) { + result.Add(_args[i]); + } + + if (SplattedArgument != null) { + result.Add(SplattedArgument); + } + + if (RhsArgument != null) { + result.Add(RhsArgument); + } + + return result.ToArray(); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Clauses/ElseIfClause.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Clauses/ElseIfClause.cs new file mode 100644 index 0000000000..180c7cfa7e --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Clauses/ElseIfClause.cs @@ -0,0 +1,46 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + + public partial class ElseIfClause : Node { + private readonly List/*!*/ _statements; + + /// + /// Null means a simple else. + /// + private readonly Expression _condition; + + public List/*!*/ Statements { + get { return _statements; } + } + + public Expression Condition { + get { return _condition; } + } + + public ElseIfClause(Expression condition, List/*!*/ statements, SourceSpan location) + : base(location) { + Assert.NotNullItems(statements); + + _statements = statements; + _condition = condition; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Clauses/RescueClause.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Clauses/RescueClause.cs new file mode 100644 index 0000000000..962c35d9c3 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Clauses/RescueClause.cs @@ -0,0 +1,169 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using System.Diagnostics; + using IronRuby.Runtime.Calls; + using System.Collections; + + // rescue type + // statements + // rescue type => target + // statements + // rescue types,*type-array + // statements + // rescue types,*type-array => target + // statements + public partial class RescueClause : Node { + private readonly List/*!*/ _types; // might be empty + private readonly Expression _splatType; // optional + private readonly LeftValue _target; // optional + private readonly List _statements; // optional + + public List Types { + get { return _types; } + } + + public LeftValue Target { + get { return _target; } + } + + public List Statements { + get { return _statements; } + } + + public RescueClause(LeftValue target, List statements, SourceSpan location) + : base(location) { + _target = target; + _types = Expression.EmptyList; + _statements = statements; + } + + public RescueClause(CompoundRightValue type, LeftValue target, List statements, SourceSpan location) + : base(location) { + _types = type.RightValues; + _splatType = type.SplattedValue; + _target = target; + _statements = statements; + } + + public RescueClause(Expression/*!*/ type, LeftValue target, List statements, SourceSpan location) + : base(location) { + Assert.NotNull(type); + _types = CollectionUtils.MakeList(type); + _target = target; + _statements = statements; + } + + // + // rescue stmts ... if (StandardError === $!) { stmts; } + // rescue stmts ... temp1 = type1; ...; if ( === $! || ...) { stmts; } + // rescue => stmts ... temp1 = type1; ...; if ( === $! || ...) { = $!; stmts; } + // + internal IfStatementTest/*!*/ Transform(AstGenerator/*!*/ gen, ResultOperation resultOperation) { + Assert.NotNull(gen); + + MSA.Expression condition; + if (_types.Count != 0 || _splatType != null) { + if (_types.Count == 0) { + // splat only: + condition = MakeCompareSplattedExceptions(gen, TransformSplatType(gen)); + } else if (_types.Count == 1 && _splatType == null) { + condition = MakeCompareException(gen, _types[0].TransformRead(gen)); + } else { + + // forall{i}: = evaluate type[i] + var temps = new MSA.Expression[_types.Count + (_splatType != null ? 1 : 0)]; + var exprs = new MSA.Expression[temps.Length + 1]; + + int i = 0; + while (i < _types.Count) { + var tmp = gen.CurrentScope.DefineHiddenVariable("#type_" + i, typeof(object)); + temps[i] = tmp; + exprs[i] = Ast.Assign(tmp, _types[i].TransformRead(gen)); + i++; + } + + if (_splatType != null) { + var tmp = gen.CurrentScope.DefineHiddenVariable("#type_" + i, typeof(object)); + temps[i] = tmp; + exprs[i] = Ast.Assign(tmp, TransformSplatType(gen)); + + i++; + } + + Debug.Assert(i == temps.Length); + + // CompareException() || ... CompareException() || CompareSplattedExceptions() + i = 0; + condition = MakeCompareException(gen, temps[i++]); + while (i < _types.Count) { + condition = Ast.OrElse(condition, MakeCompareException(gen, temps[i++])); + } + + if (_splatType != null) { + condition = Ast.OrElse(condition, MakeCompareSplattedExceptions(gen, temps[i++])); + } + + Debug.Assert(i == temps.Length); + + // (temps[0] = type[0], ..., temps[n] == type[n], condition) + exprs[exprs.Length - 1] = condition; + condition = AstFactory.Block(exprs); + } + + } else { + condition = Methods.CompareDefaultException.OpCall(gen.CurrentScopeVariable, gen.CurrentSelfVariable); + } + + return AstUtils.IfCondition(condition, + gen.TransformStatements( + // = e; + (_target != null) ? _target.TransformWrite(gen, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)) : null, + + // body: + _statements, + + resultOperation + ) + ); + } + + private MSA.Expression/*!*/ TransformSplatType(AstGenerator/*!*/ gen) { + return Ast.Dynamic( + TryConvertToArrayAction.Instance, + typeof(object), + gen.CurrentScopeVariable, + _splatType.TransformRead(gen) + ); + } + + private MSA.Expression/*!*/ MakeCompareException(AstGenerator/*!*/ gen, MSA.Expression/*!*/ expression) { + return Methods.CompareException.OpCall(gen.CurrentScopeVariable, AstFactory.Box(expression)); + } + + private MSA.Expression/*!*/ MakeCompareSplattedExceptions(AstGenerator/*!*/ gen, MSA.Expression/*!*/ expression) { + return Methods.CompareSplattedExceptions.OpCall(gen.CurrentScopeVariable, expression); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Clauses/WhenClause.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Clauses/WhenClause.cs new file mode 100644 index 0000000000..04a3132e6d --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Clauses/WhenClause.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + + // when , *: + public partial class WhenClause : Node { + + private readonly List _comparisons; // optional + private readonly Expression _comparisonArray; + private readonly List _statements; // optional + + public List Statements { + get { return _statements; } + } + + public List Comparisons { + get { return _comparisons; } + } + + public Expression ComparisonArray { + get { return _comparisonArray; } + } + + public WhenClause(List comparisons, Expression comparisonArray, List statements, SourceSpan location) + : base(location) { + _comparisons = comparisons; + _comparisonArray = comparisonArray; + _statements = statements; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Declarations/ClassDeclaration.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Declarations/ClassDeclaration.cs new file mode 100644 index 0000000000..ba442f9720 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Declarations/ClassDeclaration.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using MSA = System.Linq.Expressions; + + // class Name + // + // end + public partial class ClassDeclaration : ModuleDeclaration { + private readonly Expression _superClass; + + public Expression SuperClass { + get { return _superClass; } + } + + public ClassDeclaration(LexicalScope/*!*/ definedScope, ConstantVariable/*!*/ name, Expression superClass, Body/*!*/ body, SourceSpan location) + : base(definedScope, name, body, location) { + ContractUtils.RequiresNotNull(name, "name"); + + _superClass = superClass; + } + + internal override MSA.Expression/*!*/ MakeDefinitionExpression(AstGenerator/*!*/ gen) { + MSA.Expression transformedQualifier; + MSA.Expression name = QualifiedName.TransformName(gen); + MSA.Expression transformedSuper = (_superClass != null) ? AstFactory.Box(_superClass.TransformRead(gen)) : Ast.Constant(null); + + switch (QualifiedName.TransformQualifier(gen, out transformedQualifier)) { + case StaticScopeKind.Global: + return Methods.DefineGlobalClass.OpCall(gen.CurrentScopeVariable, name, transformedSuper); + + case StaticScopeKind.EnclosingModule: + return Methods.DefineNestedClass.OpCall(gen.CurrentScopeVariable, name, transformedSuper); + + case StaticScopeKind.Explicit: + return Methods.DefineClass.OpCall(gen.CurrentScopeVariable, transformedQualifier, name, transformedSuper); + } + + throw Assert.Unreachable; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Declarations/MethodDeclaration.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Declarations/MethodDeclaration.cs new file mode 100644 index 0000000000..272c73efd4 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Declarations/MethodDeclaration.cs @@ -0,0 +1,217 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using MSA = System.Linq.Expressions; +using System; +using Microsoft.Scripting.Actions; +using System.Collections.ObjectModel; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class MethodDeclaration : DeclarationExpression { + + // self, block + internal const int HiddenParameterCount = 2; + + /// + /// Non-null for singleton/class methods. + /// + private readonly Expression _target; + + private readonly string/*!*/ _name; + private readonly Parameters/*!*/ _parameters; + + public Expression Target { + get { return _target; } + } + + public string/*!*/ Name { + get { return _name; } + } + + public Parameters/*!*/ Parameters { + get { return _parameters; } + } + + public MethodDeclaration(LexicalScope/*!*/ definedScope, Expression target, string/*!*/ name, Parameters parameters, Body/*!*/ body, + SourceSpan location) + : base(definedScope, body, location) { + Assert.NotNull(name); + + _target = target; + _name = name; + _parameters = parameters ?? Parameters.Empty; + } + + private MSA.ParameterExpression[]/*!*/ DefineParameters(AstGenerator/*!*/ gen, ScopeBuilder/*!*/ scope) { + + // user defined locals/args: + MSA.ParameterExpression[] parameters = DefinedScope.TransformParameters(_parameters, HiddenParameterCount); + scope.AddVisibleParameters(parameters, HiddenParameterCount); + + parameters[0] = Ast.Parameter(typeof(object), "#self"); + + if (_parameters.Block != null) { + // map user defined proc parameter to the special param #1: + parameters[1] = _parameters.Block.TransformBlockParameterDefinition(); + } else { + parameters[1] = Ast.Parameter(typeof(Proc), "#block"); + } + + return parameters; + } + + private MSA.Expression/*!*/ TransformBody(AstGenerator/*!*/ gen, MSA.Expression/*!*/ methodDefinitionVariable) { + string encodedName = gen.EncodeMethodName(_name, Location); + + ScopeBuilder scope = new ScopeBuilder(); + MSA.Expression parentScope = gen.CurrentScopeVariable; + + MSA.ParameterExpression[] parameters = DefineParameters(gen, scope); + MSA.Expression currentMethodVariable = scope.DefineHiddenVariable("#method", typeof(RubyMethodInfo)); + MSA.Expression rfcVariable = scope.DefineHiddenVariable("#rfc", typeof(RuntimeFlowControl)); + MSA.Expression scopeVariable = scope.DefineHiddenVariable("#scope", typeof(RubyMethodScope)); + MSA.Expression selfParameter = parameters[0]; + MSA.Expression blockParameter = parameters[1]; + + gen.EnterMethodDefinition( + scope, + selfParameter, + scopeVariable, + blockParameter, + rfcVariable, + currentMethodVariable, + _name, + _parameters + ); + + DefinedScope.TransformLocals(scope); + + MSA.ParameterExpression unwinder = scope.DefineHiddenVariable("#unwinder", typeof(MethodUnwinder)); + + MSA.Expression body = AstFactory.MakeUserMethodBody( + gen, Location.End.Line, + blockParameter, + rfcVariable, + unwinder, + Ast.Block( + Ast.Assign(currentMethodVariable, methodDefinitionVariable), + + Ast.Assign(scopeVariable, Methods.CreateMethodScope.OpCall( + scope.VisibleVariables(), parentScope, currentMethodVariable, rfcVariable, selfParameter, blockParameter) + ), + + _parameters.TransformOptionalsInitialization(gen), + gen.TraceEnabled ? Methods.TraceMethodCall.OpCall(scopeVariable, Ast.Convert(Ast.Constant(gen.SourceUnit.Path), typeof(string)), Ast.Constant(Location.Start.Line)) : Ast.Empty(), + Body.TransformResult(gen, ResultOperation.Return), + Ast.Empty() + ), + ResultOperation.Return, + (gen.Profiler != null) ? gen.Profiler.GetTickIndex(encodedName) : -1, + (gen.Profiler != null) ? scope.DefineHiddenVariable("#stamp", typeof(long)) : null, + gen.ReturnLabel + ); + + body = gen.AddReturnTarget(scope.CreateScope(body)); + gen.LeaveMethodDefinition(); + + return CreateLambda( + encodedName, + parameters, + body + ); + } + + private static MSA.LambdaExpression/*!*/ CreateLambda(string/*!*/ name, MSA.ParameterExpression/*!*/[]/*!*/ parameters, MSA.Expression/*!*/ body) { + Type lambdaType = DynamicSiteHelpers.GetStandardDelegateType(AstFactory.GetSignature(parameters, typeof(object))); + if (lambdaType == null) { + // to many parameters for Func<> delegate -> use object[]: + MSA.ParameterExpression array = Ast.Parameter(typeof(object[]), "#params"); + var actualParameters = new MSA.ParameterExpression[] { parameters[0], parameters[1], array }; + parameters = ArrayUtils.ShiftLeft(parameters, 2); + + var bodyWithParamInit = new MSA.Expression[parameters.Length + 1]; + for (int i = 0; i < parameters.Length; i++) { + bodyWithParamInit[i] = Ast.Assign(parameters[i], Ast.ArrayIndex(array, Ast.Constant(i))); + } + bodyWithParamInit[parameters.Length] = body; + + return Ast.Lambda( + RubyMethodInfo.ParamsArrayDelegateType, + Ast.Block( + new ReadOnlyCollection(parameters), + new ReadOnlyCollection(bodyWithParamInit) + ), + name, + new ReadOnlyCollection(actualParameters) + ); + } else { + return Ast.Lambda( + lambdaType, + body, + name, + new ReadOnlyCollection(parameters) + ); + } + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + MSA.Expression methodDefinitionVariable = gen.CurrentScope.DefineHiddenVariable( + "#method_" + _name, typeof(RubyMethodInfo) + ); + + return Methods.MethodDefined.OpCall( + Ast.Assign(methodDefinitionVariable, + Methods.DefineMethod.OpCall( + (_target != null) ? _target.TransformRead(gen) : gen.CurrentSelfVariable, // target + Ast.Constant((gen.SavingToDisk) ? (object)new Serializable(this) : this), + gen.CurrentScopeVariable, + Ast.Constant(_target != null), // isSingleton? + AstUtils.Constant(_name), + TransformBody(gen, methodDefinitionVariable), + Ast.Constant(_parameters.MandatoryCount), + Ast.Constant(_parameters.OptionalCount), + Ast.Constant(_parameters.Array != null) // hasUnsplatParameter + ) + ) + ); + } + + internal sealed class Serializable : IExpressionSerializable { + private readonly MethodDeclaration/*!*/ _method; + + public MethodDeclaration/*!*/ Method { + get { return _method; } + } + + public Serializable(MethodDeclaration/*!*/ method) { + _method = method; + } + + public MSA.Expression/*!*/ CreateExpression() { + // TODO: serialize source code: + return Ast.Constant(null); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Declarations/ModuleDeclaration.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Declarations/ModuleDeclaration.cs new file mode 100644 index 0000000000..d35bf4d345 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Declarations/ModuleDeclaration.cs @@ -0,0 +1,124 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Runtime; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using MSA = System.Linq.Expressions; + + public partial class ModuleDeclaration : DeclarationExpression { + /// + /// Singleton classes don't have a name. + /// + private readonly ConstantVariable _qualifiedName; + + public ConstantVariable QualifiedName { + get { return _qualifiedName; } + } + + protected virtual bool IsSingletonDeclaration { + get { return false; } + } + + public ModuleDeclaration(LexicalScope/*!*/ definedScope, ConstantVariable/*!*/ qualifiedName, Body/*!*/ body, SourceSpan location) + : base(definedScope, body, location) { + ContractUtils.RequiresNotNull(qualifiedName, "qualifiedName"); + + _qualifiedName = qualifiedName; + } + + protected ModuleDeclaration(LexicalScope/*!*/ definedScope, Body/*!*/ body, SourceSpan location) + : base(definedScope, body, location) { + _qualifiedName = null; + } + + internal virtual MSA.Expression/*!*/ MakeDefinitionExpression(AstGenerator/*!*/ gen) { + MSA.Expression transformedQualifier; + MSA.Expression name = QualifiedName.TransformName(gen); + + switch (QualifiedName.TransformQualifier(gen, out transformedQualifier)) { + case StaticScopeKind.Global: + return Methods.DefineGlobalModule.OpCall(gen.CurrentScopeVariable, name); + + case StaticScopeKind.EnclosingModule: + return Methods.DefineNestedModule.OpCall(gen.CurrentScopeVariable, name); + + case StaticScopeKind.Explicit: + return Methods.DefineModule.OpCall(gen.CurrentScopeVariable, AstFactory.Box(transformedQualifier), name); + } + + throw Assert.Unreachable; + } + + internal sealed override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + string debugString = (IsSingletonDeclaration) ? "SINGLETON" : ((this is ClassDeclaration) ? "CLASS" : "MODULE") + " " + QualifiedName.Name; + + ScopeBuilder outerLocals = gen.CurrentScope; + + // definition needs to take place outside the defined lexical scope: + MSA.Expression definition = MakeDefinitionExpression(gen); + MSA.Expression selfVariable = outerLocals.DefineHiddenVariable("#module", typeof(RubyModule)); + MSA.Expression rfcVariable = gen.CurrentRfcVariable; + MSA.Expression parentScope = gen.CurrentScopeVariable; + + // inner locals: + ScopeBuilder scope = new ScopeBuilder(); + MSA.Expression scopeVariable = scope.DefineHiddenVariable("#scope", typeof(RubyScope)); + + gen.EnterModuleDefinition( + scope, + selfVariable, + scopeVariable, + IsSingletonDeclaration + ); + + // first, transform locals defined within the module body: + DefinedScope.TransformLocals(scope); + + // second, transform body: + MSA.Expression transformedBody = Body.TransformRead(gen); + + // outer local: + MSA.Expression resultVariable = outerLocals.DefineHiddenVariable("#result", transformedBody.Type); + + // begin with new scope + // self = DefineModule/Class(... parent scope here ...) + // + // end + MSA.Expression result = AstFactory.Block( + gen.DebugMarker(debugString), + Ast.Assign(selfVariable, definition), + scope.CreateScope( + Ast.Block( + Ast.Assign(scopeVariable, + Methods.CreateModuleScope.OpCall(scope.VisibleVariables(), parentScope, rfcVariable, selfVariable)), + Ast.Assign(resultVariable, transformedBody), + Ast.Empty() + ) + ), + gen.DebugMarker("END OF " + debugString), + resultVariable + ); + + gen.LeaveModuleDefinition(); + + return result; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Declarations/SingletonDeclaration.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Declarations/SingletonDeclaration.cs new file mode 100644 index 0000000000..308d4bc802 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Declarations/SingletonDeclaration.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + using MSA = System.Linq.Expressions; + + public partial class SingletonDeclaration : ModuleDeclaration { + private readonly Expression/*!*/ _singleton; + + public Expression/*!*/ Singleton { + get { return _singleton; } + } + + protected override bool IsSingletonDeclaration { get { return true; } } + + public SingletonDeclaration(LexicalScope/*!*/ definedScope, Expression/*!*/ singleton, Body/*!*/ body, SourceSpan location) + : base(definedScope, body, location) { + ContractUtils.RequiresNotNull(singleton, "singleton"); + + _singleton = singleton; + } + + internal override MSA.Expression/*!*/ MakeDefinitionExpression(AstGenerator/*!*/ gen) { + return Methods.DefineSingletonClass.OpCall(gen.CurrentScopeVariable, AstFactory.Box(_singleton.TransformRead(gen))); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/AndExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/AndExpression.cs new file mode 100644 index 0000000000..808498c401 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/AndExpression.cs @@ -0,0 +1,71 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + /// + /// TODO: unify with OrExpression (?) + /// + public partial class AndExpression : Expression { + private readonly Expression/*!*/ _left; + private readonly Expression/*!*/ _right; + + public Expression/*!*/ Left { + get { return _left; } + } + + public Expression/*!*/ Right { + get { return _right; } + } + + public AndExpression(Expression/*!*/ left, Expression/*!*/ right, SourceSpan location) + : base(location) { + Assert.NotNull(left, right); + + _left = left; + _right = right; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return TransformRead(gen, _left.TransformRead(gen), _right.TransformRead(gen)); + } + + internal static MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen, MSA.Expression/*!*/ left, MSA.Expression/*!*/ right) { + MSA.ParameterExpression temp; + MSA.Expression result = AstUtils.CoalesceTrue( + AstFactory.Box(left), + AstFactory.Box(right), + Methods.IsTrue, + out temp + ); + + gen.CurrentScope.AddHidden(temp); + return result; + } + + internal override Expression/*!*/ ToCondition() { + _left.ToCondition(); + _right.ToCondition(); + return this; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ArrayConstructor.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ArrayConstructor.cs new file mode 100644 index 0000000000..ef54fef009 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ArrayConstructor.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + + /// + /// [ args ] + /// + public partial class ArrayConstructor : Expression { + private readonly Arguments _arguments; + + public Arguments Arguments { + get { return _arguments; } + } + + public ArrayConstructor(Arguments arguments, SourceSpan location) + : base(location) { + _arguments = arguments; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return Arguments.TransformToArray(gen, _arguments); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/AssignmentExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/AssignmentExpression.cs new file mode 100644 index 0000000000..50957cbd60 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/AssignmentExpression.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic; +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + + /// + /// lhs = rhs + /// lhs op= rhs + /// + public abstract class AssignmentExpression : Expression { + // "&" "|", null, etc + private string _operation; + + public string Operation { + get { return _operation; } + internal set { _operation = value; } + } + + public AssignmentExpression(string operation, SourceSpan location) + : base(location) { + + _operation = operation; + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + return null; + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + return "assignment"; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/BlockExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/BlockExpression.cs new file mode 100644 index 0000000000..8a946cbaa6 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/BlockExpression.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; +using System.Collections.Generic; +using System.Diagnostics; + +namespace IronRuby.Compiler.Ast { + + // #{; ... ;} + // (; ... ;) + public partial class BlockExpression : Expression { + internal static readonly BlockExpression Empty = new BlockExpression(); + + private readonly List/*!*/ _statements; + + public List/*!*/ Statements { + get { return _statements; } + } + + private BlockExpression() + : base(SourceSpan.None) { + _statements = EmptyList; + } + + internal BlockExpression(List/*!*/ statements, SourceSpan location) + : base(location) { + Assert.NotNull(statements); + Debug.Assert(statements.Count > 1); + + _statements = statements; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return gen.TransformStatementsToExpression(_statements); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/CallExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/CallExpression.cs new file mode 100644 index 0000000000..e2c25ce1c4 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/CallExpression.cs @@ -0,0 +1,45 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using System.Diagnostics; + +namespace IronRuby.Compiler.Ast { + + public abstract class CallExpression : Expression { + // null means no parameters, not even empty parenthesis + private readonly Arguments _args; + private Block _block; + + public Arguments Arguments { + get { return _args; } + } + + public Block Block { + get { return _block; } + internal set { + Debug.Assert(_block == null); + _block = value; + } + } + + protected CallExpression(Arguments args, Block block, SourceSpan location) + : base(location) { + _args = args; + _block = block; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/CaseExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/CaseExpression.cs new file mode 100644 index 0000000000..e79665acfb --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/CaseExpression.cs @@ -0,0 +1,199 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using MSA = System.Linq.Expressions; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class CaseExpression : Expression { + + // case value + // when args: statements + // ... + // when args: statements + // else + // statements + // end + + // equivalent to + // value == null: + // if then elseif then else end + // value != null: + // if === then elseif === then else end + + // the only tricky part is that the when clause can contain a splatted array: + // case ... + // when arg0, ..., *argn: statements + // ... + // end + + private readonly Expression _value; + private readonly List/*!*/ _whenClauses; + private readonly List _elseStatements; + + public Expression Value { + get { return _value; } + } + + public List/*!*/ WhenClauses { + get { return _whenClauses; } + } + + public List ElseStatements { + get { return _elseStatements; } + } + + internal CaseExpression(Expression value, List/*!*/ whenClauses, ElseIfClause elseClause, SourceSpan location) + :this (value, whenClauses, (elseClause != null) ? elseClause.Statements : null, location) { + } + + public CaseExpression(Expression value, List/*!*/ whenClauses, List elseStatements, SourceSpan location) + : base(location) { + ContractUtils.RequiresNotNull(whenClauses, "whenClauses"); + + _value = value; + _whenClauses = whenClauses; + _elseStatements = elseStatements; + } + + // when + // generates into: + // RubyOps.IsTrue() if the case has no value, otherise: + // RubyOps.IsTrue(InvokeMember("===", , )) + private static MSA.Expression/*!*/ MakeTest(AstGenerator/*!*/ gen, MSA.Expression/*!*/ expr, MSA.Expression/*!*/ value) { + if (value != null) { + // InvokeMember("===", , ) + expr = Ast.Dynamic(RubyCallAction.Make("===", RubyCallSignature.WithScope(1)), typeof(object), + gen.CurrentScopeVariable, + expr, + value + ); + } + return AstFactory.IsTrue(expr); + } + + // when [, ...] * + // + // generates this code: + // + // IEnumerator/*!*/ enumVar = RubyOps.Unsplat().GetEnumerator(); + // bool result = false; + // while (enumVar.MoveNext()) { + // if ((enumVar.Current)) { + // result = true; + // break; + // } + // } + private static MSA.Expression/*!*/ MakeArrayTest(AstGenerator/*!*/ gen, MSA.Expression/*!*/ array, MSA.Expression value) { + MSA.Expression enumVariable = gen.CurrentScope.DefineHiddenVariable("#case-enumerator", typeof(IEnumerator)); + MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#case-compare-result", typeof(bool)); + + MSA.LabelTarget label = Ast.Label(); + return AstFactory.Block( + Ast.Assign(enumVariable, Ast.Call( + Methods.Unsplat.OpCall(AstFactory.Box(array)), + Methods.IEnumerable_Of_Object_GetEnumerator + )), + + Ast.Assign(resultVariable, Ast.Constant(false)), + + AstUtils.While( + Ast.Call(enumVariable, Methods.IEnumerator_MoveNext), + AstUtils.If( + MakeTest(gen, Ast.Call(enumVariable, Methods.IEnumerator_get_Current), value), + Ast.Block( + Ast.Assign(resultVariable, Ast.Constant(true)), + Ast.Break(label), + Ast.Empty() + ) + ), + null, + label, + null + ), + resultVariable + ); + } + + // when , ... [*] + // generates: + // () || () || ... [ || () ] + internal static MSA.Expression/*!*/ TransformWhenCondition(AstGenerator/*!*/ gen, List comparisons, + Expression comparisonArray, MSA.Expression value) { + + MSA.Expression result; + if (comparisonArray != null) { + result = MakeArrayTest(gen, comparisonArray.TransformRead(gen), value); + } else { + result = Ast.Constant(false); + } + + if (comparisons != null) { + for (int i = comparisons.Count - 1; i >= 0; i--) { + result = Ast.OrElse( + MakeTest(gen, comparisons[i].TransformRead(gen), value), + result + ); + } + } + + return result; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + MSA.Expression result; + if (_elseStatements != null) { + // ... else body end + result = gen.TransformStatementsToExpression(_elseStatements); + } else { + // no else clause => the result of the if-expression is nil: + result = Ast.Constant(null); + } + + MSA.Expression value; + if (_value != null) { + value = gen.CurrentScope.DefineHiddenVariable("#case-compare-value", typeof(object)); + } else { + value = null; + } + + for (int i = _whenClauses.Count - 1; i >= 0; i-- ) { + // emit: else (if (condition) body else result) + result = AstFactory.Condition( + TransformWhenCondition(gen, _whenClauses[i].Comparisons, _whenClauses[i].ComparisonArray, value), + gen.TransformStatementsToExpression(_whenClauses[i].Statements), + result + ); + } + + if (_value != null) { + result = AstFactory.Block( + Ast.Assign(value, Ast.Convert(_value.TransformRead(gen), typeof(object))), + result + ); + } + + return result; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ConditionalExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ConditionalExpression.cs new file mode 100644 index 0000000000..93f4165f25 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ConditionalExpression.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + + /// + /// Lightweight form of if-then-else-end expression. + /// Used for ternary ?: operator. + /// + public partial class ConditionalExpression : Expression { + private readonly Expression/*!*/ _condition; + private readonly Expression/*!*/ _trueExpression; + private readonly Expression/*!*/ _falseExpression; + + public Expression/*!*/ Condition { + get { return _condition; } + } + + public Expression/*!*/ TrueExpression { + get { return _trueExpression; } + } + + public Expression/*!*/ FalseExpression { + get { return _falseExpression; } + } + + public ConditionalExpression(Expression/*!*/ condition, Expression/*!*/ trueExpression, Expression/*!*/ falseExpression, SourceSpan location) + : base(location) { + Assert.NotNull(condition, trueExpression, falseExpression); + _condition = condition; + _trueExpression = trueExpression; + _falseExpression = falseExpression; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return AstFactory.Condition( + AstFactory.IsTrue(_condition.TransformRead(gen)), + _trueExpression.TransformRead(gen), + _falseExpression.TransformRead(gen) + ); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ConditionalJumpExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ConditionalJumpExpression.cs new file mode 100644 index 0000000000..04cc803d79 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ConditionalJumpExpression.cs @@ -0,0 +1,79 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + /// + /// Represents {condition} {and/or/&&/||} {jump-statement}, + /// or {condition} ? {jump-statement} : {value}. + /// + public partial class ConditionalJumpExpression : Expression { + private readonly bool _negateCondition; + private readonly Expression/*!*/ _condition; + private readonly Expression _value; + private readonly JumpStatement/*!*/ _jumpStatement; + + public bool NegateCondition { + get { return _negateCondition; } + } + + public bool IsBooleanExpression { + get { return _value == null; } + } + + public Expression/*!*/ Condition { + get { return _condition; } + } + + public Expression Value { + get { return _value; } + } + + public JumpStatement/*!*/ JumpStatement { + get { return _jumpStatement; } + } + + public ConditionalJumpExpression(Expression/*!*/ condition, JumpStatement/*!*/ jumpStatement, bool negateCondition, Expression value, SourceSpan location) + : base(location) { + ContractUtils.RequiresNotNull(condition, "condition"); + ContractUtils.RequiresNotNull(jumpStatement, "jumpStatement"); + + _condition = condition; + _jumpStatement = jumpStatement; + _negateCondition = negateCondition; + _value = value; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + MSA.Expression transformedCondition = AstFactory.Box(_condition.TransformRead(gen)); + MSA.Expression tmpVariable = gen.CurrentScope.DefineHiddenVariable("#tmp_cond", transformedCondition.Type); + + return AstFactory.Block( + Ast.Assign(tmpVariable, transformedCondition), + AstUtils.IfThen( + (_negateCondition ? AstFactory.IsFalse(tmpVariable) : AstFactory.IsTrue(tmpVariable)), + _jumpStatement.Transform(gen) + ), + (_value != null) ? _value.TransformRead(gen) : tmpVariable + ); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/DeclarationExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/DeclarationExpression.cs new file mode 100644 index 0000000000..44a44ac0bf --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/DeclarationExpression.cs @@ -0,0 +1,46 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + + public abstract class DeclarationExpression : Expression { + private readonly LexicalScope/*!*/ _definedScope; + private readonly Body/*!*/ _body; + + public LexicalScope/*!*/ DefinedScope { + get { + return _definedScope; + } + } + + public Body/*!*/ Body { + get { + return _body; + } + } + + protected DeclarationExpression(LexicalScope/*!*/ definedScope, Body/*!*/ body, SourceSpan location) + : base(location) { + ContractUtils.RequiresNotNull(definedScope, "definedScope"); + ContractUtils.RequiresNotNull(body, "body"); + + _definedScope = definedScope; + _body = body; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/EncodingExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/EncodingExpression.cs new file mode 100644 index 0000000000..3d6c6ee374 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/EncodingExpression.cs @@ -0,0 +1,53 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using System.Diagnostics; +using System.Text; + +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Math; +using Microsoft.Scripting; + +using IronRuby.Builtins; + +namespace IronRuby.Compiler.Ast { + using MSA = System.Linq.Expressions; + using Ast = System.Linq.Expressions.Expression; + using AstUtils = Microsoft.Scripting.Ast.Utils; + + public partial class EncodingExpression : Expression { + public EncodingExpression(SourceSpan location) + : base(location) { + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { +#if SILVERLIGHT + return Ast.Constant(null, typeof(Encoding)); +#else + if (gen.Encoding == null) { + return Ast.Constant(null, typeof(Encoding)); + } + + return Methods.CreateEncoding.OpCall(Ast.Constant(RubyEncoding.GetCodePage(gen.Encoding))); +#endif + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + return null; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ErrorExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ErrorExpression.cs new file mode 100644 index 0000000000..3c4d5d5724 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ErrorExpression.cs @@ -0,0 +1,32 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + + public partial class ErrorExpression : Expression { + public ErrorExpression(SourceSpan location) + : base(location) { + + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + throw Assert.Unreachable; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/Expression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/Expression.cs new file mode 100644 index 0000000000..3b04b01ab8 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/Expression.cs @@ -0,0 +1,97 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + /// + /// Represents expressions. Statements are considered special cases of expressions in AST class hierarchy. + /// Unlike syntactic expression a syntactic statement cannot be assigned to a left value. + /// However certain Ruby constructs (e.g. block-expression) allow to read the value of a statement. + /// Usually such value is null (e.g. undef, alias, while/until statements), + /// although some syntactic statements evaluate to a non-null value (e.g. if/unless-statements). + /// + public abstract class Expression : Node { + + internal static readonly List/*!*/ _EmptyList = new List(); + + internal static List/*!*/ EmptyList { + get { + Debug.Assert(_EmptyList.Count == 0); + return _EmptyList; + } + } + + protected Expression(SourceSpan location) + : base(location) { + } + + /// + /// Transform as expression (value is read); + /// + internal abstract MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen); + + /// + /// Transform as statement (value is not read). + /// + internal virtual MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + return gen.AddDebugInfo(TransformRead(gen), Location); + } + + /// + /// Transform and handle the result according to the specified result operation. + /// + internal virtual MSA.Expression/*!*/ TransformResult(AstGenerator/*!*/ gen, ResultOperation resultOperation) { + MSA.Expression resultExpression = TransformRead(gen); + MSA.Expression statement; + + if (resultOperation.Variable != null) { + statement = Ast.Assign(resultOperation.Variable, Ast.Convert(resultExpression, resultOperation.Variable.Type)); + } else { + statement = gen.Return(resultExpression); + } + + return gen.AddDebugInfo(statement, Location); + } + + // Condition under which the expression is considered "defined?". + // Returns null if there is no condition. + internal virtual MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + return null; + } + + // the name that is returned when the expression is defined: + internal virtual string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + return "expression"; + } + + internal MSA.Expression/*!*/ TransformIsDefined(AstGenerator/*!*/ gen) { + MSA.Expression condition = TransformDefinedCondition(gen); + MSA.Expression result = Methods.CreateMutableStringB.OpCall(Ast.Constant(GetNodeName(gen))); + return (condition != null) ? Ast.Condition(condition, result, AstFactory.NullOfMutableString) : result; + } + + // Called on an expression that is used as a condition. + // Range expression with a non-literal-integer bound is converted to flip expression. + internal virtual Expression/*!*/ ToCondition() { + return this; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ForLoopExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ForLoopExpression.cs new file mode 100644 index 0000000000..d9a0f20f28 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/ForLoopExpression.cs @@ -0,0 +1,70 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Dynamic.Binders; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime.Calls; +using IronRuby.Builtins; + +using MSA = System.Linq.Expressions; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class ForLoopExpression : Expression { + // for variables in list + // body + // end + + private readonly BlockDefinition/*!*/ _block; + private readonly Expression/*!*/ _list; + + public BlockDefinition/*!*/ Block { + get { return _block; } + } + + public Expression/*!*/ List { + get { return _list; } + } + + public ForLoopExpression(CompoundLeftValue/*!*/ variables, Expression/*!*/ list, List body, SourceSpan location) + : base(location) { + Assert.NotNull(variables, list); + + _block = new BlockDefinition(null, variables, body, location); + _list = list; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + Assert.NotNull(gen); + + MSA.Expression transformedBlock = _block.Transform(gen); + + MSA.Expression blockArgVariable = gen.CurrentScope.DefineHiddenVariable("#forloop-block", typeof(Proc)); + + MSA.Expression result = Ast.Dynamic(RubyCallAction.Make("each", RubyCallSignature.WithScopeAndBlock(0)), typeof(object), + gen.CurrentScopeVariable, + _list.TransformRead(gen), + blockArgVariable + ); + + return gen.DebugMark(MethodCall.MakeCallWithBlockRetryable(gen, result, blockArgVariable, transformedBlock, true), + "#RB: method call with a block ('for-loop')"); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/HashConstructor.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/HashConstructor.cs new file mode 100644 index 0000000000..b00f3cde16 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/HashConstructor.cs @@ -0,0 +1,61 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class HashConstructor : Expression { + // { key1 => value1, key2 => value2, ... } + // or + // { key1, value1, key2, value2, ... } + + private readonly List _maplets; + private readonly List _expressions; + + public List Maplets { + get { return _maplets; } + } + + public List Expressions { + get { return _expressions; } + } + + public HashConstructor(List maplets, List expressions, SourceSpan location) + : base(location) { + ContractUtils.Requires(maplets == null || expressions == null); + ContractUtils.Requires(expressions == null || expressions.Count % 2 == 0, "expressions"); + + _maplets = maplets; + _expressions = expressions; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + Assert.NotNull(gen); + + if (_maplets != null) { + return gen.MakeHashOpCall(gen.TransformMapletsToExpressions(_maplets)); + } else if (_expressions != null) { + return gen.MakeHashOpCall(gen.TranformExpressions(_expressions)); + } else { + return Methods.MakeHash0.OpCall(gen.CurrentScopeVariable); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/IfExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/IfExpression.cs new file mode 100644 index 0000000000..3891189c2e --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/IfExpression.cs @@ -0,0 +1,92 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class IfExpression : Expression { + private Expression/*!*/ _condition; + private List _body; + private List _elseIfClauses; + + public Expression/*!*/ Condition { + get { return _condition; } + } + + public List Body { + get { return _body; } + } + + public List ElseIfClauses { + get { return _elseIfClauses; } + } + + public IfExpression(Expression/*!*/ condition, List/*!*/ body, List/*!*/ elseIfClauses, SourceSpan location) + : base(location) { + ContractUtils.RequiresNotNull(body, "body"); + ContractUtils.RequiresNotNull(condition, "condition"); + ContractUtils.RequiresNotNull(elseIfClauses, "elseIfClauses"); + + // all but the last clause should have non-null conditions: + for (int i = 0; i < elseIfClauses.Count - 1; i++) { + if (elseIfClauses[i].Condition == null) { + throw ExceptionUtils.MakeArgumentItemNullException(i, "elseIfClauses"); + } + } + + _condition = condition; + _body = body; + _elseIfClauses = elseIfClauses; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + + MSA.Expression result; + + int i = _elseIfClauses.Count - 1; + + if (i >= 0 && _elseIfClauses[i].Condition == null) { + // ... else body end + result = gen.TransformStatementsToExpression(_elseIfClauses[i].Statements); + i--; + } else { + // no else clause => the result of the if-expression is nil: + result = Ast.Constant(null); + } + + while (i >= 0) { + // emit: else (if (condition) body else result) + result = AstFactory.Condition( + AstFactory.IsTrue(_elseIfClauses[i].Condition.TransformRead(gen)), + gen.TransformStatementsToExpression(_elseIfClauses[i].Statements), + result + ); + i--; + } + + // if (condition) body else result + return AstFactory.Condition( + AstFactory.IsTrue(_condition.TransformRead(gen)), + gen.TransformStatementsToExpression(_body), + result + ); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/IsDefinedExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/IsDefinedExpression.cs new file mode 100644 index 0000000000..e011b926c7 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/IsDefinedExpression.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + using MSA = System.Linq.Expressions; + + public partial class IsDefinedExpression : Expression { + private readonly Expression/*!*/ _expression; + + public Expression/*!*/ Expression { + get { return _expression; } + } + + public IsDefinedExpression(Expression/*!*/ expression, SourceSpan location) + : base(location) { + Assert.NotNull(expression); + + _expression = expression; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return _expression.TransformIsDefined(gen); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/Literal.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/Literal.cs new file mode 100644 index 0000000000..5e0d1d75ae --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/Literal.cs @@ -0,0 +1,78 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + + public partial class Literal : Expression { + private readonly object _value; + + public object Value { + get { return _value; } + } + + private Literal(object value, SourceSpan location) + : base(location) { + _value = value; + } + + public static Literal/*!*/ Integer(int value, SourceSpan location) { + return new Literal(ScriptingRuntimeHelpers.Int32ToObject(value), location); + } + + public static Literal/*!*/ Double(double value, SourceSpan location) { + return new Literal(value, location); + } + + public static Literal/*!*/ BigInteger(BigInteger/*!*/ value, SourceSpan location) { + return new Literal(value, location); + } + + public static Literal/*!*/ Nil(SourceSpan location) { + return new Literal(null, location); + } + + public static Literal/*!*/ True(SourceSpan location) { + return new Literal(ScriptingRuntimeHelpers.True, location); + } + + public static Literal/*!*/ False(SourceSpan location) { + return new Literal(ScriptingRuntimeHelpers.False, location); + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return AstUtils.Constant(_value); + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + if (_value == null) { + return "nil"; + } else if (_value is bool) { + return (bool)_value ? "true" : "false"; + } else { + return base.GetNodeName(gen); + } + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + return null; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/MatchExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/MatchExpression.cs new file mode 100644 index 0000000000..60220353f4 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/MatchExpression.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using IronRuby.Runtime.Calls; + + public partial class MatchExpression : Expression { + private readonly RegularExpression/*!*/ _regex; + private readonly Expression/*!*/ _expression; + + public RegularExpression/*!*/ Regex { + get { return _regex; } + } + + public Expression/*!*/ Expression { + get { return _expression; } + } + + public MatchExpression(RegularExpression/*!*/ regex, Expression/*!*/ expression, SourceSpan location) + : base(location) { + _regex = regex; + _expression = expression; + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + return "method"; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return Methods.MatchString.OpCall( + Ast.Dynamic(ConvertToStrAction.Instance, typeof(MutableString), gen.CurrentScopeVariable, _expression.Transform(gen)), + _regex.Transform(gen), + gen.CurrentScopeVariable + ); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/MethodCall.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/MethodCall.cs new file mode 100644 index 0000000000..69b1f7ae96 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/MethodCall.cs @@ -0,0 +1,184 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using MSA = System.Linq.Expressions; + + /// + /// target.method_id(args) + /// + public partial class MethodCall : CallExpression { + private string/*!*/ _methodName; + private readonly Expression _target; + + public string/*!*/ MethodName { + get { return _methodName; } + } + + public Expression Target { + get { return _target; } + } + + public MethodCall(Expression target, string/*!*/ methodName, Arguments args, SourceSpan location) + : this(target, methodName, args, null, location) { + } + + public MethodCall(Expression target, string/*!*/ methodName, Arguments args, Block block, SourceSpan location) + : base(args, block, location) { + Assert.NotEmpty(methodName); + + _methodName = methodName; + _target = target; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + MSA.Expression transformedTarget; + bool hasImplicitSelf; + if (_target != null) { + transformedTarget = _target.TransformRead(gen); + hasImplicitSelf = false; + } else { + transformedTarget = gen.CurrentSelfVariable; + hasImplicitSelf = true; + } + return TransformRead(this, gen, hasImplicitSelf, _methodName, transformedTarget, Arguments, Block, null, null); + } + + // arguments: complex arguments (expressions, maplets, splat, block) + // singleArgument: siple argument (complex are not used) + // assignmentRhsArgument: rhs of the assignment: target.method=(rhs) + internal static MSA.Expression/*!*/ TransformRead(Expression/*!*/ node, AstGenerator/*!*/ gen, bool hasImplicitSelf, + string/*!*/ methodName, MSA.Expression/*!*/ transformedTarget, + Arguments arguments, Block block, MSA.Expression singleArgument, MSA.Expression assignmentRhsArgument) { + + Debug.Assert(assignmentRhsArgument == null || block == null, "Block not allowed in assignment"); + Debug.Assert(singleArgument == null || arguments == null && assignmentRhsArgument == null); + Assert.NotNull(gen, transformedTarget); + Assert.NotEmpty(methodName); + + // Pass args in this order: + // 1. instance + // 2. block (if present) + // 3. passed args: normal args, maplets, array + // 4. RHS of assignment (if present) + + CallBuilder callBuilder = new CallBuilder(gen); + callBuilder.Instance = transformedTarget; + + MSA.Expression blockArgVariable = null; + MSA.Expression transformedBlock = null; + + if (block != null) { + blockArgVariable = gen.CurrentScope.DefineHiddenVariable("#block-def", typeof(Proc)); + transformedBlock = block.Transform(gen); + callBuilder.Block = blockArgVariable; + } + + if (arguments != null) { + arguments.TransformToCall(gen, callBuilder); + } else if (singleArgument != null) { + callBuilder.Add(singleArgument); + } + + MSA.Expression rhsVariable = null; + if (assignmentRhsArgument != null) { + rhsVariable = gen.CurrentScope.DefineHiddenVariable("#rhs", assignmentRhsArgument.Type); + callBuilder.RhsArgument = Ast.Assign(rhsVariable, assignmentRhsArgument); + } + + var dynamicSite = callBuilder.MakeCallAction(methodName, hasImplicitSelf); + gen.TraceCallSite(node, dynamicSite); + + MSA.Expression result = gen.DebugMark(dynamicSite, methodName); + + if (block != null) { + result = gen.DebugMark(MakeCallWithBlockRetryable(gen, result, blockArgVariable, transformedBlock, block.IsDefinition), + "#RB: method call with a block ('" + methodName + "')"); + } + + if (assignmentRhsArgument != null) { + result = AstFactory.Block(result, rhsVariable); + } + + return result; + } + + internal static MSA.Expression/*!*/ MakeCallWithBlockRetryable(AstGenerator/*!*/ gen, MSA.Expression/*!*/ invoke, + MSA.Expression blockArgVariable, MSA.Expression transformedBlock, bool isBlockDefinition) { + Assert.NotNull(invoke); + Debug.Assert((blockArgVariable == null) == (transformedBlock == null)); + + // see Ruby Language.doc/Control Flow Implementation/Method Call With a Block + MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#method-result", typeof(object)); + MSA.ParameterExpression evalUnwinder = gen.CurrentScope.DefineHiddenVariable("#unwinder", typeof(EvalUnwinder)); + + MSA.LabelTarget label = Ast.Label(); + + return AstFactory.Block( + Ast.Assign(blockArgVariable, Ast.Convert(transformedBlock, blockArgVariable.Type)), + AstFactory.Infinite(label, null, + (!isBlockDefinition) ? + (MSA.Expression)Ast.Empty() : + (MSA.Expression)Methods.InitializeBlock.OpCall(blockArgVariable), + + AstUtils.Try( + Ast.Assign(resultVariable, invoke) + ).Catch(evalUnwinder, + Ast.Assign( + resultVariable, + Ast.Field(evalUnwinder, EvalUnwinder.ReturnValueField) + ) + ), + + // if result != RetrySingleton then break end + AstUtils.Unless(Methods.IsRetrySingleton.OpCall(AstFactory.Box(resultVariable)), Ast.Break(label)), + + // if blockParam == #block then retry end + (gen.CurrentMethod.IsTopLevelCode) ? Ast.Empty() : + AstUtils.IfThen(Ast.Equal(gen.MakeMethodBlockParameterRead(), blockArgVariable), RetryStatement.TransformRetry(gen)) + + ), + resultVariable + ); + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + if (_target != null) { + return gen.TryCatchAny( + Methods.IsDefinedMethod.OpCall( + AstFactory.Box(_target.TransformRead(gen)), gen.CurrentScopeVariable, AstUtils.Constant(_methodName) + ), + Ast.Constant(false) + ); + } else { + return Methods.IsDefinedMethod.OpCall(gen.CurrentSelfVariable, gen.CurrentScopeVariable, AstUtils.Constant(_methodName)); + } + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + return "method"; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/NotExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/NotExpression.cs new file mode 100644 index 0000000000..fa220d1284 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/NotExpression.cs @@ -0,0 +1,46 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + using MSA = System.Linq.Expressions; + + public partial class NotExpression : Expression { + private readonly Expression/*!*/ _expression; + + public Expression/*!*/ Expression { + get { return _expression; } + } + + public NotExpression(Expression/*!*/ expression, SourceSpan location) + : base(location) { + Assert.NotNull(expression); + + _expression = expression; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return Methods.IsFalse.OpCall(AstFactory.Box(_expression.TransformRead(gen))); + } + + internal override Expression/*!*/ ToCondition() { + _expression.ToCondition(); + return this; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/OrExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/OrExpression.cs new file mode 100644 index 0000000000..3fb5680630 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/OrExpression.cs @@ -0,0 +1,70 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using AstUtils = Microsoft.Scripting.Ast.Utils; + using MSA = System.Linq.Expressions; + + public partial class OrExpression : Expression { + private readonly Expression/*!*/ _left; + private readonly Expression/*!*/ _right; + + public Expression/*!*/ Left { + get { return _left; } + } + + public Expression/*!*/ Right { + get { return _right; } + } + + public OrExpression(Expression/*!*/ left, Expression/*!*/ right, SourceSpan location) + : base(location) { + Assert.NotNull(left, right); + + _left = left; + _right = right; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return TransformRead(gen, _left.TransformRead(gen), _right.TransformRead(gen)); + } + + internal static MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen, MSA.Expression/*!*/ left, MSA.Expression/*!*/ right) { + MSA.ParameterExpression temp; + + MSA.Expression result = AstUtils.CoalesceFalse( + AstFactory.Box(left), + AstFactory.Box(right), + Methods.IsTrue, + out temp + ); + + gen.CurrentScope.AddHidden(temp); + return result; + } + + internal override Expression/*!*/ ToCondition() { + _left.ToCondition(); + _right.ToCondition(); + return this; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/RangeExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/RangeExpression.cs new file mode 100644 index 0000000000..0b7571c4fc --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/RangeExpression.cs @@ -0,0 +1,120 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + using MSA = System.Linq.Expressions; + using Ast = System.Linq.Expressions.Expression; + + public partial class RangeExpression : Expression { + private readonly Expression/*!*/ _begin; + private readonly Expression/*!*/ _end; + private readonly bool _isExclusive; + private bool _isCondition; + + public Expression/*!*/ Begin { + get { return _begin; } + } + + public Expression/*!*/ End { + get { return _end; } + } + + public bool IsExclusive { + get { return _isExclusive; } + } + + public bool IsCondition { + get { return _isCondition; } + } + + public RangeExpression(Expression/*!*/ begin, Expression/*!*/ end, bool isExclusive, bool isCondition, SourceSpan location) + : base(location) { + Assert.NotNull(begin, end); + _begin = begin; + _end = end; + _isExclusive = isExclusive; + _isCondition = isCondition; + } + + public RangeExpression(Expression/*!*/ begin, Expression/*!*/ end, bool isExclusive, SourceSpan location) + : this(begin, end, isExclusive, false, location) { + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + if (_isCondition) { + return TransformReadCondition(gen); + } else { + return (_isExclusive ? Methods.CreateExclusiveRange : Methods.CreateInclusiveRange). + OpCall(gen.CurrentScopeVariable, AstFactory.Box(_begin.TransformRead(gen)), AstFactory.Box(_end.TransformRead(gen))); + } + } + + /// + /// End-exclusive range: + /// if state + /// state = IsFalse({end}) + /// true + /// else + /// state = IsTrue({begin}) + /// end + /// + /// End-inclusive range: + /// if state || IsTrue({begin}) + /// state = IsFalse({end}) + /// true + /// else + /// false + /// end + /// + private MSA.Expression/*!*/ TransformReadCondition(AstGenerator/*!*/ gen) { + // Define state variable in the inner most method scope. + var stateVariable = gen.CurrentMethod.Builder.DefineHiddenVariable("#in_range", typeof(bool)); + + var begin = AstFactory.Box(_begin.TransformRead(gen)); + var end = AstFactory.Box(_end.TransformRead(gen)); + + if (_isExclusive) { + return Ast.Condition( + stateVariable, + Ast.Block(Ast.Assign(stateVariable, Methods.IsFalse.OpCall(end)), Ast.Constant(true)), + Ast.Assign(stateVariable, Methods.IsTrue.OpCall(begin)) + ); + } else { + return Ast.Condition( + Ast.OrElse(stateVariable, Methods.IsTrue.OpCall(begin)), + Ast.Block(Ast.Assign(stateVariable, Methods.IsFalse.OpCall(end)), Ast.Constant(true)), + Ast.Constant(false) + ); + + } + } + + internal override Expression/*!*/ ToCondition() { + Literal literal; + + _isCondition = !( + (literal = _begin as Literal) != null && + (literal.Value is int) && + (literal = _end as Literal) != null && + (literal.Value is int) + ); + + return this; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/RegexMatchReference.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/RegexMatchReference.cs new file mode 100644 index 0000000000..b37a7e6e88 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/RegexMatchReference.cs @@ -0,0 +1,140 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class RegexMatchReference : Expression { + // $& + internal const int EntireMatch = 0; + internal const string EntireMatchName = "&"; + + // $~ + internal const int MatchData = -1; + internal const string MatchDataName = "~"; + + // $+ + internal const int MatchLastGroup = -2; + internal const string MatchLastGroupName = "+"; + + // $` + internal const int MatchPrefix = -3; + internal const string MatchPrefixName = "`"; + + // $' + internal const int MatchSuffix = -4; + internal const string MatchSuffixName = "'"; + + private readonly int _index; + + public int Index { + get { return (_index > 0) ? _index : 0; } + } + + internal RegexMatchReference(int index, SourceSpan location) + : base(location) { + Debug.Assert(index >= MatchSuffix, "index"); + _index = index; + } + + public RegexMatchReference/*!*/ CreateGroupReference(int index, SourceSpan location) { + ContractUtils.Requires(index >= 0); + return new RegexMatchReference(index, location); + } + + public RegexMatchReference/*!*/ CreateLastGroupReference(SourceSpan location) { + return new RegexMatchReference(MatchLastGroup, location); + } + + public RegexMatchReference/*!*/ CreatePrefixReference(SourceSpan location) { + return new RegexMatchReference(MatchPrefix, location); + } + + public RegexMatchReference/*!*/ CreateSuffixReference(SourceSpan location) { + return new RegexMatchReference(MatchSuffix, location); + } + + public RegexMatchReference/*!*/ CreateMatchReference(SourceSpan location) { + return new RegexMatchReference(MatchData, location); + } + + // TODO: keep only full names? + public string/*!*/ VariableName { + get { + switch (_index) { + case EntireMatch: return EntireMatchName; + case MatchData: return MatchDataName; + case MatchLastGroup: return MatchLastGroupName; + case MatchPrefix: return MatchPrefixName; + case MatchSuffix: return MatchSuffixName; + default: return _index.ToString(); + } + } + } + + public string/*!*/ FullName { + get { + return "$" + VariableName; + } + } + + // Numeric references cannot be aliased (alias _ $n doesn't work for some reason): + internal bool CanAlias { + get { + return _index <= 0; + } + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + switch (_index) { + case MatchData: + return Methods.GetCurrentMatchData.OpCall(gen.CurrentScopeVariable); + + case MatchLastGroup: + return Methods.GetCurrentMatchLastGroup.OpCall(gen.CurrentScopeVariable); + + case MatchPrefix: + return Methods.GetCurrentMatchPrefix.OpCall(gen.CurrentScopeVariable); + + case MatchSuffix: + return Methods.GetCurrentMatchSuffix.OpCall(gen.CurrentScopeVariable); + + default: + return Methods.GetCurrentMatchGroup.OpCall(gen.CurrentScopeVariable, Ast.Constant(_index)); + } + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + return Ast.NotEqual(TransformRead(gen), Ast.Constant(null)); + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + // TODO: Ruby 1.9: all return "global-variable" + switch (_index) { + case MatchData: return "$" + MatchDataName; + case MatchLastGroup: return "$" + MatchLastGroupName; + case MatchPrefix: return "$" + MatchPrefixName; + case MatchSuffix: return "$" + MatchSuffixName; + default: return "$" + _index.ToString(); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/RegularExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/RegularExpression.cs new file mode 100644 index 0000000000..0951ed88c4 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/RegularExpression.cs @@ -0,0 +1,70 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using MSA = System.Linq.Expressions; + + // /pattern/options + public partial class RegularExpression : Expression { + private readonly RubyRegexOptions _options; + private readonly List/*!*/ _pattern; + private bool _isCondition; + + public RubyRegexOptions Options { + get { return _options; } + } + + public List/*!*/ Pattern { + get { return _pattern; } + } + + public bool IsCondition { + get { return _isCondition; } + } + + public RegularExpression(List/*!*/ pattern, RubyRegexOptions options, SourceSpan location) + : this(pattern, options, false, location) { + } + + public RegularExpression(List/*!*/ pattern, RubyRegexOptions options, bool isCondition, SourceSpan location) + : base(location) { + Assert.NotNull(pattern); + + _pattern = pattern; + _options = options; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + var result = StringConstructor.TransformConcatentation(gen, _pattern, Methods.CreateRegex, Ast.Constant(_options)); + + if (_isCondition) { + result = Methods.MatchLastInputLine.OpCall(result, gen.CurrentScopeVariable); + } + + return result; + } + + internal override Expression/*!*/ ToCondition() { + _isCondition = true; + return this; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/RescueExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/RescueExpression.cs new file mode 100644 index 0000000000..9345bed2a8 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/RescueExpression.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; + +namespace IronRuby.Compiler.Ast { + using Microsoft.Scripting.Utils; + using MSA = System.Linq.Expressions; + + // x = expression rescue jump_statement + // x = expression rescue expression + public partial class RescueExpression : Expression { + private readonly SourceSpan _rescueSpan; + private readonly Expression/*!*/ _guardedExpression; + private readonly Expression/*!*/ _rescueClauseStatement; + + public Expression/*!*/ GuardedExpression { + get { return _guardedExpression; } + } + + public Expression/*!*/ RescueClauseStatement { + get { return _rescueClauseStatement; } + } + + public RescueExpression(Expression/*!*/ guardedExpression, Expression/*!*/ rescueClauseStatement, SourceSpan rescueSpan, SourceSpan location) + : base(location) { + ContractUtils.RequiresNotNull(guardedExpression, "guardedExpression"); + ContractUtils.RequiresNotNull(rescueClauseStatement, "rescueClauseStatement"); + + _guardedExpression = guardedExpression; + _rescueClauseStatement = rescueClauseStatement; + _rescueSpan = rescueSpan; + } + + private Body/*!*/ ToBody(AstGenerator/*!*/ gen) { + return new Body( + CollectionUtils.MakeList(_guardedExpression), + CollectionUtils.MakeList(new RescueClause(null, CollectionUtils.MakeList(_rescueClauseStatement), _rescueSpan)), + null, null, Location); + } + + internal override MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + return ToBody(gen).TransformResult(gen, ResultOperation.Ignore); + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator gen) { + return ToBody(gen).TransformRead(gen); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/SelfReference.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/SelfReference.cs new file mode 100644 index 0000000000..3c87939383 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/SelfReference.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; + +namespace IronRuby.Compiler.Ast { + using MSA = System.Linq.Expressions; + + /// + /// self + /// + public partial class SelfReference : Expression { + + public SelfReference(SourceSpan location) + : base(location) { + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return gen.CurrentSelfVariable; + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + return "self"; + } + + internal override MSA.Expression/*!*/ TransformDefinedCondition(AstGenerator/*!*/ gen) { + return null; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/StringConstructor.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/StringConstructor.cs new file mode 100644 index 0000000000..179615091b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/StringConstructor.cs @@ -0,0 +1,208 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using MSA = System.Linq.Expressions; + + public enum StringKind { + Mutable, + Immutable, + Command + } + + /// + /// Sequence of string literals and/or string embedded expressions. + /// + public partial class StringConstructor : Expression { + private readonly StringKind _kind; + private readonly List/*!*/ _parts; + + public StringKind Kind { + get { return _kind; } + } + + public List/*!*/ Parts { + get { return _parts; } + } + + public StringConstructor(List/*!*/ parts, StringKind kind, SourceSpan location) + : base(location) { + ContractUtils.RequiresNotNullItems(parts, "parts"); + + _parts = parts; + _kind = kind; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + switch (_kind) { + case StringKind.Mutable: + return TransformConcatentation(gen, _parts, Methods.CreateMutableString, null); + + case StringKind.Immutable: + return TransformConcatentation(gen, _parts, Methods.CreateSymbol, null); + + case StringKind.Command: + return Ast.Dynamic(RubyCallAction.Make("`", new RubyCallSignature(1, RubyCallFlags.HasScope | RubyCallFlags.HasImplicitSelf)), typeof(object), + gen.CurrentScopeVariable, + gen.CurrentSelfVariable, + TransformConcatentation(gen, _parts, Methods.CreateMutableString, null) + ); + } + + throw Assert.Unreachable; + } + + internal static MSA.Expression/*!*/ TransformConcatentation(AstGenerator/*!*/ gen, List/*!*/ parts, + Func/*!*/ opFactory, MSA.Expression additionalArg) { + + var opSuffix = new StringBuilder(Math.Min(parts.Count, 4)); + + List merged = ConcatLiteralsAndTransform(gen, parts, opSuffix); + + if (merged.Count <= RubyOps.MakeStringParamCount) { + if (merged.Count == 0) { + merged.Add(Ast.Constant(String.Empty)); + opSuffix.Append(RubyOps.SuffixBinary); + } + + if (opSuffix.IndexOf(RubyOps.SuffixEncoded) != -1) { + merged.Add(Ast.Constant(RubyEncoding.GetCodePage(gen.Encoding))); + } + + if (additionalArg != null) { + merged.Add(additionalArg); + } + + return opFactory(opSuffix.ToString()).OpCall(merged); + } else { + var paramArray = Ast.NewArrayInit(typeof(object), merged); + var codePage = Ast.Constant(RubyEncoding.GetCodePage(gen.Encoding)); + + return (additionalArg != null) ? + opFactory("N").OpCall(paramArray, codePage, additionalArg) : + opFactory("N").OpCall(paramArray, codePage); + } + } + + private static List/*!*/ ConcatLiteralsAndTransform(AstGenerator/*!*/ gen, List/*!*/ parts, StringBuilder/*!*/ opName) { + var result = new List(); + var literals = new List(); + int concatLength = 0; + var concatEncoding = StringLiteralEncoding.Ascii; + ConcatLiteralsAndTransformRecursive(gen, parts, literals, ref concatLength, ref concatEncoding, result, opName); + + // finish trailing literals: + if (literals.Count > 0) { + result.Add(Ast.Constant(Concat(literals, concatLength))); + opName.Append(OpSuffix(gen, concatEncoding)); + } + + return result; + } + + // + // Traverses expressions in "parts" and concats all contiguous literal strings. + // Notes: + // - Instead of usign StringBuilder we place the string values that can be concatenated so far in to "literals" list and keep track + // of their total length in "concatLength" and encoding in "concatEncoding". + // If we reach a non-literal expression and we have some literals ready in "literals" array we do the concat and clear the list + // and "concatLength" and "concatEncoding". + // - "result" list contains argument expressions to the CreateMutableString* overloads. + // - "opName" contains the name of the operation. This method appends suffix based on the argument types (see RubyOps.Suffix*). + // + private static void ConcatLiteralsAndTransformRecursive(AstGenerator/*!*/ gen, List/*!*/ parts, + List/*!*/ literals, ref int concatLength, ref StringLiteralEncoding concatEncoding, List/*!*/ result, + StringBuilder/*!*/ opName) { + + for (int i = 0; i < parts.Count; i++) { + Expression part = parts[i]; + StringLiteral literal; + StringConstructor ctor; + + if ((literal = part as StringLiteral) != null) { + literals.Add(literal.Value); + concatEncoding = CombineEncoding(concatEncoding, literal); + concatLength += literal.Value.Length; + } else if ((ctor = part as StringConstructor) != null) { + ConcatLiteralsAndTransformRecursive(gen, ctor.Parts, literals, ref concatLength, ref concatEncoding, result, opName); + } else { + if (literals.Count > 0) { + result.Add(Ast.Constant(Concat(literals, concatLength))); + opName.Append(OpSuffix(gen, concatEncoding)); + concatLength = 0; + concatEncoding = StringLiteralEncoding.Ascii; + literals.Clear(); + } + + result.Add( + Ast.Dynamic( + ConvertToSAction.Instance, + typeof(MutableString), + gen.CurrentScopeVariable, part.TransformRead(gen) + ) + ); + opName.Append(RubyOps.SuffixMutable); + } + } + } + + private static StringLiteralEncoding CombineEncoding(StringLiteralEncoding encoding, StringLiteral literal) { + if (encoding == StringLiteralEncoding.UTF8 || literal.IsUTF8) { + return StringLiteralEncoding.UTF8; + } + + if (encoding == StringLiteralEncoding.Ascii && literal.IsAscii) { + return StringLiteralEncoding.Ascii; + } + + return StringLiteralEncoding.Default; + } + + private static char OpSuffix(AstGenerator/*!*/ gen, StringLiteralEncoding encoding) { + if (encoding == StringLiteralEncoding.Ascii) { + return RubyOps.SuffixBinary; + } else if (encoding == StringLiteralEncoding.UTF8 || gen.Encoding == Encoding.UTF8) { + return RubyOps.SuffixUTF8; + } else if (gen.Encoding == BinaryEncoding.Instance) { + return RubyOps.SuffixBinary; + } else { + return RubyOps.SuffixEncoded; + } + } + + private static string/*!*/ Concat(List/*!*/ literals, int capacity) { + if (literals.Count == 1) { + return literals[0]; + } + + StringBuilder sb = new StringBuilder(capacity); + for (int j = 0; j < literals.Count; j++) { + sb.Append(literals[j]); + } + return sb.ToString(); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/StringLiteral.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/StringLiteral.cs new file mode 100644 index 0000000000..ec08ff23e0 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/StringLiteral.cs @@ -0,0 +1,82 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using System.Diagnostics; +using System.Text; + +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Math; +using Microsoft.Scripting; + +using IronRuby.Builtins; +using IronRuby.Runtime; + +namespace IronRuby.Compiler.Ast { + using MSA = System.Linq.Expressions; + using Ast = System.Linq.Expressions.Expression; + using AstUtils = Microsoft.Scripting.Ast.Utils; + + internal enum StringLiteralEncoding { + // encoding not specified in the literal + Default = 0, + + // literal doesn't contain non-ASCII characters (> 0x7f) + Ascii = 1, + + // literal contains \u escape + UTF8 = 2, + } + + public partial class StringLiteral : Expression { + private readonly string/*!*/ _value; + private readonly StringLiteralEncoding _encoding; + + public string/*!*/ Value { + get { return _value; } + } + + public bool IsAscii { + get { return _encoding == StringLiteralEncoding.Ascii; } + } + + public bool IsUTF8 { + get { return _encoding == StringLiteralEncoding.UTF8; } + } + + internal StringLiteral(string/*!*/ value, StringLiteralEncoding encoding, SourceSpan location) + : base(location) { + _value = value; + _encoding = encoding; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + if (IsAscii || gen.Encoding == BinaryEncoding.Instance) { + return Methods.CreateMutableStringB.OpCall(Ast.Constant(_value)); + } else if (IsUTF8 || gen.Encoding == BinaryEncoding.UTF8) { + return Methods.CreateMutableStringU.OpCall(Ast.Constant(_value)); + } else { + return Methods.CreateMutableStringE.OpCall( + Ast.Constant(_value), Ast.Constant(RubyEncoding.GetCodePage(gen.Encoding)) + ); + } + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + return null; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/SuperCall.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/SuperCall.cs new file mode 100644 index 0000000000..e754da01ad --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/SuperCall.cs @@ -0,0 +1,95 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + /// + /// super(args) + /// super + /// + /// The former case passes the arguments explicitly + /// The latter passes all of the arguments that were passed to the current method + /// (including the block, if any) + /// + /// Also works from a method defined using define_method (not supported yet!) + /// + public partial class SuperCall : CallExpression { + public SuperCall(Arguments args, Block block, SourceSpan location) + : base(args, block, location) { + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + if (gen.CurrentMethod.IsTopLevelCode && gen.CurrentBlock == null && !gen.CompilerOptions.IsEval) { + return Ast.Throw(Methods.MakeTopLevelSuperException.OpCall()); + } + + // invoke super member action: + CallBuilder callBuilder = new CallBuilder(gen); + + // self: + callBuilder.Instance = gen.CurrentSelfVariable; + + // arguments: + if (Arguments != null) { + Arguments.TransformToCall(gen, callBuilder); + } else { + // copy parameters from the method: + // TODO: parameters in top-level eval + LexicalScope.TransformParametersToSuperCall(gen, callBuilder, gen.CurrentMethod.Parameters); + } + + // block: + MSA.Expression transformedBlock; + if (Block != null) { + transformedBlock = Block.Transform(gen); + } else { + transformedBlock = gen.MakeMethodBlockParameterRead(); + } + + // variable assigned to the transformed block in MakeCallWithBlockRetryable: + MSA.Expression blockArgVariable = gen.CurrentScope.DefineHiddenVariable("#super-call-block", typeof(Proc)); + callBuilder.Block = blockArgVariable; + + // TODO: this could be improved, currently the right method name and declaring module is always searched for at run-time (at the site): + + return gen.DebugMark( + MethodCall.MakeCallWithBlockRetryable(gen, + callBuilder.MakeSuperCallAction(gen.CurrentFrame.UniqueId), + blockArgVariable, + transformedBlock, + Block != null && Block.IsDefinition + ), + "#RB: super call ('" + gen.CurrentMethod.MethodName + "')" + ); + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + // TODO: + throw new NotImplementedError(); + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + return "super"; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/SymbolLiteral.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/SymbolLiteral.cs new file mode 100644 index 0000000000..f0465d51a7 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/SymbolLiteral.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Dynamic; +using Microsoft.Scripting; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using MSA = System.Linq.Expressions; + + public partial class SymbolLiteral : Expression { + private readonly string/*!*/ _value; + + public string/*!*/ Value { + get { return _value; } + } + + internal SymbolLiteral(string/*!*/ value, SourceSpan location) + : base(location) { + _value = value; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return Methods.CreateSymbolB.OpCall(Ast.Constant(_value)); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/UnlessExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/UnlessExpression.cs new file mode 100644 index 0000000000..20575ae96e --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/UnlessExpression.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + using MSA = System.Linq.Expressions; + + public partial class UnlessExpression : Expression { + + private readonly Expression/*!*/ _condition; + private readonly List _statements; + private readonly ElseIfClause _elseClause; + + public Expression/*!*/ Condition { + get { return _condition; } + } + + public List Statements { + get { return _statements; } + } + + public ElseIfClause ElseClause { + get { return _elseClause; } + } + + public UnlessExpression(Expression/*!*/ condition, List/*!*/ statements, ElseIfClause elseClause, SourceSpan location) + : base(location) { + ContractUtils.RequiresNotNull(condition, "condition"); + ContractUtils.RequiresNotNull(statements, "statements"); + ContractUtils.Requires(elseClause == null || elseClause.Condition == null, "elseClause", "No condition allowed."); + + _statements = statements; + _condition = condition; + _elseClause = elseClause; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return AstFactory.Condition( + Methods.IsFalse.OpCall(AstFactory.Box(_condition.TransformRead(gen))), + gen.TransformStatementsToExpression(_statements), + gen.TransformStatementsToExpression(_elseClause != null ? _elseClause.Statements : null) + ); + } + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/WhileLoopExpression.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/WhileLoopExpression.cs new file mode 100644 index 0000000000..b8021949b0 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/WhileLoopExpression.cs @@ -0,0 +1,137 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + // pre-test: + // while do end + // until do end + // post-test: + // while + // until + // while + // until + public partial class WhileLoopExpression : Expression { + private readonly Expression _condition; + private readonly List _statements; // optional + private readonly bool _isWhileLoop; // while or until + private readonly bool _isPostTest; // do-while or while-do + + public Expression Condition { + get { return _condition; } + } + + public List Statements { + get { return _statements; } + } + + public bool IsWhileLoop { + get { return _isWhileLoop; } + } + + public bool IsPostTest { + get { return _isPostTest; } + } + + public WhileLoopExpression(Expression/*!*/ condition, bool isWhileLoop, bool isPostTest, List/*!*/ statements, SourceSpan location) + : base(location) { + + ContractUtils.RequiresNotNull(condition, "condition"); + ContractUtils.RequiresNotNullItems(statements, "statements"); + + _condition = condition; + _isWhileLoop = isWhileLoop; + _isPostTest = isPostTest; + _statements = statements; + } + + // see Ruby Language.doc/Runtime/Control Flow Implementation/While-Until + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#loop-result", typeof(object)); + MSA.Expression redoVariable = gen.CurrentScope.DefineHiddenVariable("#skip-condition", typeof(bool)); + MSA.ParameterExpression blockUnwinder = gen.CurrentScope.DefineHiddenVariable("#unwinder", typeof(BlockUnwinder)); + MSA.ParameterExpression evalUnwinder = gen.CurrentScope.DefineHiddenVariable("#unwinder", typeof(EvalUnwinder)); + + bool isInnerLoop = gen.CurrentLoop != null; + + MSA.LabelTarget breakLabel = Ast.Label(); + MSA.LabelTarget continueLabel = Ast.Label(); + + gen.EnterLoop(redoVariable, resultVariable, breakLabel, continueLabel); + MSA.Expression transformedBody = gen.TransformStatements(_statements, ResultOperation.Ignore); + MSA.Expression transformedCondition = AstFactory.IsTrue(_condition.TransformRead(gen)); + gen.LeaveLoop(); + + MSA.Expression conditionPositiveStmt, conditionNegativeStmt; + if (_isWhileLoop) { + conditionPositiveStmt = Ast.Empty(); + conditionNegativeStmt = Ast.Break(breakLabel); + } else { + conditionPositiveStmt = Ast.Break(breakLabel); + conditionNegativeStmt = Ast.Empty(); + } + + // make the loop first: + MSA.Expression loop = Ast.Block( + Ast.Assign(redoVariable, Ast.Constant(_isPostTest)), + + AstFactory.Infinite(breakLabel, continueLabel, + AstUtils.Try( + + AstUtils.If(redoVariable, + Ast.Assign(redoVariable, Ast.Constant(false)) + ).ElseIf(transformedCondition, + conditionPositiveStmt + ).Else( + conditionNegativeStmt + ), + + transformedBody + + ).Catch(blockUnwinder, + // redo = u.IsRedo + Ast.Assign(redoVariable, Ast.Field(blockUnwinder, BlockUnwinder.IsRedoField)) + + ).Filter(evalUnwinder, Ast.Equal(Ast.Field(evalUnwinder, EvalUnwinder.ReasonField), AstFactory.BlockReturnReasonBreak), + // result = unwinder.ReturnValue + Ast.Assign(resultVariable, Ast.Field(evalUnwinder, EvalUnwinder.ReturnValueField)), + Ast.Break(breakLabel) + ) + ), + Ast.Empty() + ); + + // wrap it to try finally that updates RFC state: + if (!isInnerLoop) { + loop = AstUtils.Try( + Ast.Assign(Ast.Field(gen.CurrentRfcVariable, RuntimeFlowControl.InLoopField), Ast.Constant(true)), + loop + ).Finally( + Ast.Assign(Ast.Field(gen.CurrentRfcVariable, RuntimeFlowControl.InLoopField), Ast.Constant(false)) + ); + } + + return AstFactory.Block(loop, resultVariable); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/YieldCall.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/YieldCall.cs new file mode 100644 index 0000000000..8afedc8e42 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Expressions/YieldCall.cs @@ -0,0 +1,81 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using Microsoft.Scripting; +using IronRuby.Builtins; +using IronRuby.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + /// + /// yield(args) + /// + public partial class YieldCall : CallExpression { + + public YieldCall(Arguments args, SourceSpan location) + : base(args, null, location) { + } + + // see Ruby Language.doc/Runtime/Control Flow Implementation/Yield + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + MSA.Expression bfcVariable = gen.CurrentScope.DefineHiddenVariable("#yielded-bfc", typeof(BlockParam)); + MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#result", typeof(object)); + MSA.Expression evalUnwinder = gen.CurrentScope.DefineHiddenVariable("#unwinder", typeof(EvalUnwinder)); + + MSA.Expression postYield; + + if (gen.CompilerOptions.IsEval) { + // eval: + postYield = Methods.EvalYield.OpCall(gen.CurrentRfcVariable, bfcVariable, resultVariable); + } else if (gen.CurrentBlock != null) { + // block: + postYield = Methods.BlockYield.OpCall(gen.CurrentRfcVariable, gen.CurrentBlock.BfcVariable, bfcVariable, resultVariable); + } else { + // method: + postYield = Methods.MethodYield.OpCall(gen.CurrentRfcVariable, bfcVariable, resultVariable); + } + + return AstFactory.Block( + gen.DebugMarker("#RB: yield begin"), + + Ast.Assign(bfcVariable, Methods.CreateBfcForYield.OpCall(gen.MakeMethodBlockParameterRead())), + + Ast.Assign(resultVariable, (Arguments ?? Arguments.Empty).TransformToYield(gen, bfcVariable, + Ast.Property(AstUtils.Convert(gen.MakeMethodBlockParameterRead(), typeof(Proc)), Proc.SelfProperty) + )), + + AstUtils.IfThen(postYield, gen.Return(resultVariable)), + + gen.DebugMarker("#RB: yield end"), + + resultVariable + ); + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + return "yield"; + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + // block_given semantics: + return Ast.NotEqual(gen.MakeMethodBlockParameterRead(), Ast.Constant(null)); + } + + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Identifier.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Identifier.cs new file mode 100644 index 0000000000..5683e9c015 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Identifier.cs @@ -0,0 +1,33 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic; +using Microsoft.Scripting; + +namespace IronRuby.Compiler.Ast { + public struct Identifier { + private readonly string/*!*/ _name; + private readonly SourceSpan _location; + + public string/*!*/ Name { get { return _name; } } + public SourceSpan Location { get { return _location; } } + + public Identifier(string/*!*/ name, SourceSpan location) { + _name = name; + _location = location; + } + } + +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/BreakStatement.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/BreakStatement.cs new file mode 100644 index 0000000000..6c2806b7a6 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/BreakStatement.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class BreakStatement : JumpStatement { + public BreakStatement(Arguments arguments, SourceSpan location) + : base(arguments, location) { + } + + // see Ruby Language.doc/Runtime/Control Flow Implementation/Break + internal override MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + + MSA.Expression transformedReturnValue = TransformReturnValue(gen); + + // eval: + if (gen.CompilerOptions.IsEval) { + return Methods.EvalBreak.OpCall(gen.CurrentRfcVariable, AstFactory.Box(transformedReturnValue)); + } + + // loop: + if (gen.CurrentLoop != null) { + return Ast.Block( + Ast.Assign( + gen.CurrentLoop.ResultVariable, + Ast.Convert(transformedReturnValue, gen.CurrentLoop.ResultVariable.Type) + ), + Ast.Break(gen.CurrentLoop.BreakLabel), + Ast.Empty() + ); + } + + // block: + if (gen.CurrentBlock != null) { + return gen.Return(Methods.BlockBreak.OpCall(gen.CurrentBlock.BfcVariable, AstFactory.Box(transformedReturnValue))); + } + + // primary frame: + return Methods.MethodBreak.OpCall(AstFactory.Box(transformedReturnValue)); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/JumpStatement.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/JumpStatement.cs new file mode 100644 index 0000000000..ac824d8a2e --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/JumpStatement.cs @@ -0,0 +1,48 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public abstract class JumpStatement : Expression { + private readonly Arguments _arguments; + + public Arguments Arguments { + get { return _arguments; } + } + + public JumpStatement(Arguments arguments, SourceSpan location) + : base(location) { + _arguments = arguments; + } + + internal MSA.Expression/*!*/ TransformReturnValue(AstGenerator/*!*/ gen) { + return Arguments.TransformToReturnValue(gen, _arguments); + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return Ast.Convert(Transform(gen), typeof(object)); + } + + internal override MSA.Expression TransformResult(AstGenerator/*!*/ gen, ResultOperation resultOperation) { + // the code will jump, ignore the result variable: + return Transform(gen); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/NextStatement.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/NextStatement.cs new file mode 100644 index 0000000000..5764e70000 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/NextStatement.cs @@ -0,0 +1,55 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class NextStatement : JumpStatement { + public NextStatement(Arguments arguments, SourceSpan location) + : base(arguments, location) { + } + + // see Ruby Language.doc/Runtime/Control Flow Implementation/Next + internal override MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + + MSA.Expression transformedReturnValue = TransformReturnValue(gen); + + // eval: + if (gen.CompilerOptions.IsEval) { + return Methods.EvalNext.OpCall(gen.CurrentRfcVariable, AstFactory.Box(transformedReturnValue)); + } + + // loop: + if (gen.CurrentLoop != null) { + return Ast.Block( + transformedReturnValue, // evaluate for side-effects + Ast.Continue(gen.CurrentLoop.ContinueLabel), + Ast.Empty() + ); + } + + // block: + if (gen.CurrentBlock != null) { + return gen.Return(transformedReturnValue); + } + + // method: + return Methods.MethodNext.OpCall(gen.CurrentRfcVariable, AstFactory.Box(transformedReturnValue)); + } + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/RedoStatement.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/RedoStatement.cs new file mode 100644 index 0000000000..fc90f25554 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/RedoStatement.cs @@ -0,0 +1,53 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class RedoStatement : JumpStatement { + public RedoStatement(SourceSpan location) + : base(null, location) { + } + + // see Ruby Language.doc/Runtime/Control Flow Implementation/Redo + internal override MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + + // eval: + if (gen.CompilerOptions.IsEval) { + return Methods.EvalRedo.OpCall(gen.CurrentRfcVariable); + } + + // loop: + if (gen.CurrentLoop != null) { + return Ast.Block( + Ast.Assign(gen.CurrentLoop.RedoVariable, Ast.Constant(true)), + Ast.Continue(gen.CurrentLoop.ContinueLabel), + Ast.Empty() + ); + } + + // block: + if (gen.CurrentBlock != null) { + return Ast.Continue(gen.CurrentBlock.RedoLabel); + } + + // method: + return Methods.MethodRedo.OpCall(gen.CurrentRfcVariable); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/RetryStatement.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/RetryStatement.cs new file mode 100644 index 0000000000..41521fe09b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/RetryStatement.cs @@ -0,0 +1,57 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + + public partial class RetryStatement : JumpStatement { + public RetryStatement(SourceSpan location) + : base(null, location) { + } + + // see Ruby Language.doc/Runtime/Control Flow Implementation/Retry + internal override MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + return TransformRetry(gen); + } + + internal static MSA.Expression/*!*/ TransformRetry(AstGenerator/*!*/ gen) { + // eval: + if (gen.CompilerOptions.IsEval) { + return Methods.EvalRetry.OpCall(gen.CurrentRfcVariable); + } + + // rescue clause: + if (gen.CurrentRescue != null) { + return Ast.Block( + Ast.Assign(gen.CurrentRescue.RetryingVariable, Ast.Constant(true)), + Ast.Continue(gen.CurrentRescue.ContinueLabel), + Ast.Empty() + ); + } + + // block: + if (gen.CurrentBlock != null) { + return gen.Return(Methods.BlockRetry.OpCall(gen.CurrentBlock.BfcVariable)); + } + + // primary frame: + return gen.Return(Methods.MethodRetry.OpCall(gen.CurrentRfcVariable, gen.MakeMethodBlockParameterRead())); + } + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/ReturnStatement.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/ReturnStatement.cs new file mode 100644 index 0000000000..df2544254d --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/JumpStatements/ReturnStatement.cs @@ -0,0 +1,46 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class ReturnStatement : JumpStatement { + public ReturnStatement(Arguments arguments, SourceSpan location) + : base(arguments, location) { + } + + // see Ruby Language.doc/Runtime/Control Flow Implementation/Return + internal override MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + + MSA.Expression transformedReturnValue = TransformReturnValue(gen); + + // eval: + if (gen.CompilerOptions.IsEval) { + return gen.Return(Methods.EvalReturn.OpCall(gen.CurrentRfcVariable, AstFactory.Box(transformedReturnValue))); + } + + // block: + if (gen.CurrentBlock != null) { + return gen.Return(Methods.BlockReturn.OpCall(gen.CurrentBlock.BfcVariable, AstFactory.Box(transformedReturnValue))); + } + + // method: + return gen.Return(transformedReturnValue); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/ArrayItemAccess.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/ArrayItemAccess.cs new file mode 100644 index 0000000000..4f9c2ce4fb --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/ArrayItemAccess.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + + // {array}[{arguments}] + // {array}[{arguments}] = rhs + public partial class ArrayItemAccess : LeftValue { + private readonly Expression/*!*/ _array; + private readonly Arguments/*!*/ _arguments; + + public Expression/*!*/ Array { + get { return _array; } + } + + public Arguments/*!*/ Arguments { + get { return _arguments; } + } + + public ArrayItemAccess(Expression/*!*/ array, Arguments/*!*/ arguments, SourceSpan location) + : base(location) { + ContractUtils.RequiresNotNull(array, "array"); + ContractUtils.RequiresNotNull(arguments, "arguments"); + + _array = array; + _arguments = arguments; + } + + internal override MSA.Expression TransformTargetRead(AstGenerator/*!*/ gen) { + return _array.TransformRead(gen); + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen, MSA.Expression targetValue, bool tryRead) { + Assert.NotNull(gen, targetValue); + return MethodCall.TransformRead(this, gen, false, "[]", targetValue, _arguments, null, null, null); + } + + internal override MSA.Expression/*!*/ TransformWrite(AstGenerator/*!*/ gen, MSA.Expression target, MSA.Expression/*!*/ rightValue) { + Assert.NotNull(target); + return MethodCall.TransformRead(this, gen, _array.NodeType == NodeTypes.SelfReference, "[]=", target, _arguments, null, null, rightValue); + } + } + +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/AttributeAccess.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/AttributeAccess.cs new file mode 100644 index 0000000000..da50a0bbe0 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/AttributeAccess.cs @@ -0,0 +1,66 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + + public partial class AttributeAccess : LeftValue { + // qualifier::name = + + private Expression/*!*/ _qualifier; + private string/*!*/ _name; + + public Expression/*!*/ Qualifier { + get { return _qualifier; } + } + + public string/*!*/ Name { + get { return _name; } + } + + public AttributeAccess(Expression/*!*/ qualifier, string/*!*/ name, SourceSpan location) + : base(location) { + Assert.NotNull(qualifier, name); + + _name = name + "="; + _qualifier = qualifier; + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + return "assignment"; + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + return null; + } + + internal override MSA.Expression TransformTargetRead(AstGenerator/*!*/ gen) { + return _qualifier.TransformRead(gen); + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen, MSA.Expression targetValue, bool tryRead) { + throw Assert.Unreachable; + } + + internal override MSA.Expression/*!*/ TransformWrite(AstGenerator/*!*/ gen, MSA.Expression/*!*/ targetValue, MSA.Expression/*!*/ rightValue) { + Assert.NotNull(gen, targetValue, rightValue); + return MethodCall.TransformRead(this, gen, _qualifier.NodeType == NodeTypes.SelfReference, _name, targetValue, null, null, null, rightValue); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/ClassVariable.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/ClassVariable.cs new file mode 100644 index 0000000000..6a38c0f3be --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/ClassVariable.cs @@ -0,0 +1,77 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using System.Reflection; + using Microsoft.Scripting.Utils; + + public partial class ClassVariable : Variable { + public ClassVariable(string/*!*/ name, SourceSpan location) + : base(name, location) { + Debug.Assert(name.StartsWith("@@")); + } + + private const int OpTryGet = 0; + private const int OpGet = 1; + private const int OpIsDefined = 2; + + private MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen, int/*!*/ opKind) { + // eval or in a non-singleton module/class declaration + // -> we find the right scope at runtime by walking the hierarchy + // otherwise + // -> variable is on Object + + return GetOp(gen, opKind).OpCall(gen.CurrentScopeVariable, AstUtils.Constant(Name)); + } + + private static MethodInfo/*!*/ GetOp(AstGenerator/*!*/ gen, int/*!*/ opKind) { + bool unknownOwner = gen.CompilerOptions.IsEval || gen.GetCurrentNonSingletonModule() != null; + + switch (opKind) { + case OpTryGet: return unknownOwner ? Methods.TryGetClassVariable : Methods.TryGetObjectClassVariable; + case OpGet: return unknownOwner ? Methods.GetClassVariable : Methods.GetObjectClassVariable; + case OpIsDefined: return unknownOwner ? Methods.IsDefinedClassVariable : Methods.IsDefinedObjectClassVariable; + default: throw Assert.Unreachable; + } + } + + internal override MSA.Expression/*!*/ TransformReadVariable(AstGenerator/*!*/ gen, bool tryRead) { + return TransformRead(gen, tryRead ? OpTryGet : OpGet); + } + + internal override MSA.Expression/*!*/ TransformWriteVariable(AstGenerator/*!*/ gen, MSA.Expression/*!*/ rightValue) { + if (gen.CompilerOptions.IsEval || gen.GetCurrentNonSingletonModule() != null) { + return Methods.SetClassVariable.OpCall(AstFactory.Box(rightValue), gen.CurrentScopeVariable, AstUtils.Constant(Name)); + } else { + return Methods.SetObjectClassVariable.OpCall(AstFactory.Box(rightValue), gen.CurrentScopeVariable, AstUtils.Constant(Name)); + } + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + return TransformRead(gen, OpIsDefined); + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + return "class variable"; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/CompoundLeftValue.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/CompoundLeftValue.cs new file mode 100644 index 0000000000..681da15f50 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/CompoundLeftValue.cs @@ -0,0 +1,266 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Text; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class CompoundLeftValue : LeftValue { + /// + /// Empty LHS - used by blocks. + /// + internal static readonly CompoundLeftValue/*!*/ EmptyBlockSignature = new CompoundLeftValue(LeftValue.EmptyList, null, SourceSpan.None); + + /// + /// Unspecified LHS - used by blocks. + /// + internal static readonly CompoundLeftValue/*!*/ UnspecifiedBlockSignature = new CompoundLeftValue(LeftValue.EmptyList, null, SourceSpan.None); + + /// + /// List of l-values, possibly compound. + /// + private readonly List/*!*/ _leftValues; + + /// + /// Residual l-value (l-value following the * sentinel). + /// + private readonly LeftValue _unsplattedValue; + + public List/*!*/ LeftValues { + get { return _leftValues; } + } + + public LeftValue UnsplattedValue { + get { return _unsplattedValue; } + } + + public CompoundLeftValue(List/*!*/ leftValues, LeftValue unsplattedValue, SourceSpan location) + : base(location) { + Assert.NotNull(leftValues); + + _leftValues = leftValues; + _unsplattedValue = unsplattedValue; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen, MSA.Expression targetValue, bool tryRead) { + throw Assert.Unreachable; + } + + internal override MSA.Expression TransformTargetRead(AstGenerator/*!*/ gen) { + return null; + } + + internal override MSA.Expression/*!*/ TransformWrite(AstGenerator/*!*/ gen, MSA.Expression targetValue, MSA.Expression/*!*/ rightValue) { + Debug.Assert(targetValue == null); + return TransformWrite(gen, CollectionUtils.MakeList(rightValue), null); + } + + internal MSA.Expression/*!*/ TransformWrite(AstGenerator/*!*/ gen, CompoundRightValue/*!*/ rhs) { + return TransformWrite(gen, gen.TranformExpressions(rhs.RightValues), (rhs.SplattedValue != null) ? rhs.SplattedValue.TransformRead(gen) : null); + } + + private MSA.Expression/*!*/ TransformWrite(AstGenerator/*!*/ gen, List/*!*/ rightValues, MSA.Expression splattedValue) { + + // We need to distinguish various special cases here. + // Each of the bool variables defined below is true iff the corresponding special form of LHS/RHS occurs. + // These flags drive the DLR AST being produced by this method. + // For parallel assignment specification, see "Ruby Language.docx/Runtime/Parallel Assignment". + + // L(0,-) not applicable + Debug.Assert(!(_leftValues.Count == 0 && _unsplattedValue == null)); + + // L(1,-)? + bool leftOneNone = _leftValues.Count == 1 && _unsplattedValue == null; + + // L(0,*)? + bool leftNoneSplat = _leftValues.Count == 0 && _unsplattedValue != null; + + // R(0,*)? + bool rightNoneSplat = rightValues.Count == 0 && splattedValue != null; + + // R(1,-)? + bool rightOneNone = rightValues.Count == 1 && splattedValue == null; + + // R(1,*)? + bool rightOneSplat = rightValues.Count == 1 && splattedValue != null; + + // R(0,-) not applicable + Debug.Assert(!(rightValues.Count == 0 && splattedValue == null)); + + MSA.Expression resultExpression; + + if (leftOneNone) { + // L(1,-): + + // recurse right away (X) = RHS is equivalent to X = RHS: + CompoundLeftValue compound = _leftValues[0] as CompoundLeftValue; + if (compound != null) { + return compound.TransformWrite(gen, rightValues, splattedValue); + } + + if (rightOneSplat) { + // R(1,*) + resultExpression = Methods.SplatPair.OpCall(AstFactory.Box(rightValues[0]), AstFactory.Box(splattedValue)); + } else { + // case 1: R(1,-) + // case 2: R(0,*) + // case 3: otherwise + resultExpression = Arguments.TransformRead(gen, rightValues, splattedValue, true /* Splat */); + } + + return _leftValues[0].TransformWrite(gen, resultExpression); + } + + bool optimizeReads = true; + + if (rightOneNone && !leftNoneSplat) { + // R(1,-) && !L(0,*) + resultExpression = Methods.Unsplat.OpCall(AstFactory.Box(rightValues[0])); + optimizeReads = false; + } else { + // case 1: R(0,*) = L + // case 2: otherwise + resultExpression = Arguments.TransformRead(gen, rightValues, splattedValue, false /* Unsplat */); + optimizeReads = !rightNoneSplat; + } + + int writesCount = _leftValues.Count + (_unsplattedValue != null ? 1 : 0); + if (writesCount == 0) { + return resultExpression; + } + + var writes = new MSA.Expression[ + 1 + // store result to a temp + writesCount + + 1 // load from the temp + ]; + + int writeIndex = 0; + MSA.Expression result = gen.CurrentScope.DefineHiddenVariable("#rhs", typeof(List)); + writes[writeIndex++] = Ast.Assign(result, resultExpression); + + MethodInfo itemGetter = typeof(List).GetMethod("get_Item"); + for (int i = 0; i < _leftValues.Count; i++) { + MSA.Expression rvalue; + + if (optimizeReads) { + if (i < rightValues.Count) { + // unchecked get item: + rvalue = Ast.Call(result, itemGetter, Ast.Constant(i)); + } else if (splattedValue != null) { + // checked get item: + rvalue = Methods.GetArrayItem.OpCall(result, Ast.Constant(i)); + } else { + // missing item: + rvalue = Ast.Constant(null); + } + } else { + rvalue = Methods.GetArrayItem.OpCall(result, Ast.Constant(i)); + } + + writes[writeIndex++] = _leftValues[i].TransformWrite(gen, rvalue); + } + + // unsplatting the rest of rhs values into an array: + if (_unsplattedValue != null) { + // copies the rest of resulting array to the *LHS; + // the resulting array contains splatted *RHS - no need for additional appending: + MSA.Expression array = Methods.GetArraySuffix.OpCall(result, Ast.Constant(_leftValues.Count)); + + // assign the array (possibly empty) to *LHS: + writes[writeIndex++] = _unsplattedValue.TransformWrite(gen, array); + } + + writes[writeIndex++] = result; + + Debug.Assert(writes.Length == writeIndex); + return AstFactory.Block(writes); + } + + internal BlockSignatureAttributes GetBlockSignatureAttributes() { + var result = BlockSignatureAttributes.None; + + CompoundLeftValue compound; + + if (_unsplattedValue != null) { + result |= BlockSignatureAttributes.HasUnsplatParameter; + compound = this; + } else if (_leftValues.Count == 1 && (compound = _leftValues[0] as CompoundLeftValue) != null) { + result |= BlockSignatureAttributes.HasSingleCompoundParameter; + } else { + compound = this; + } + + int arity; + + if (this == UnspecifiedBlockSignature) { + arity = -1; + } else { + arity = compound._leftValues.Count; + if (compound._unsplattedValue != null) { + arity = -arity - 1; + } else if (compound._leftValues.Count > 0 && compound._leftValues[compound._leftValues.Count - 1] is Placeholder) { + arity--; + } + } + + return result | (BlockSignatureAttributes)(arity << 2); + } + + public override string/*!*/ ToString() { + var result = new StringBuilder(); + bool first = true; + + for (int i = 0; i < _leftValues.Count; i++) { + if (!first) { + result.Append(','); + } else { + first = false; + } + + var compound = _leftValues[i] as CompoundLeftValue; + if (compound != null) { + result.Append('('); + } + + result.Append(_leftValues[i].ToString()); + + if (compound != null) { + result.Append(')'); + } + } + + if (_unsplattedValue != null) { + if (!first) { + result.Append(','); + } else { + first = false; + } + result.Append('*'); + result.Append(_unsplattedValue.ToString()); + } + + return result.ToString(); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/ConstantVariable.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/ConstantVariable.cs new file mode 100644 index 0000000000..a3bc140b03 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/ConstantVariable.cs @@ -0,0 +1,154 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + using MSA = System.Linq.Expressions; + using Ast = System.Linq.Expressions.Expression; + using System; + + internal enum StaticScopeKind { + Global, + EnclosingModule, + Explicit + } + + public partial class ConstantVariable : Variable { + private readonly bool _explicitlyBound; + private readonly Expression _qualifier; + + public Expression Qualifier { + get { return _qualifier; } + } + + public bool IsGlobal { + get { + return _explicitlyBound && _qualifier == null; + } + } + + public bool IsBound { + get { + return _explicitlyBound; + } + } + + /// + /// Unbound constant (Foo). + /// + public ConstantVariable(string/*!*/ name, SourceSpan location) + : base(name, location) { + + _qualifier = null; + _explicitlyBound = false; + } + + /// + /// Bound constant (::Foo - bound to Object, qualifier.Foo - bound to qualifier object). + /// + public ConstantVariable(Expression qualifier, string/*!*/ name, SourceSpan location) + : base(name, location) { + + _qualifier = qualifier; + _explicitlyBound = true; + } + + internal StaticScopeKind TransformQualifier(AstGenerator/*!*/ gen, out MSA.Expression transformedQualifier) { + if (_qualifier != null) { + Debug.Assert(_explicitlyBound); + + // qualifier.Foo + transformedQualifier = _qualifier.TransformRead(gen); + return StaticScopeKind.Explicit; + } else if (_explicitlyBound) { + // ::Foo + transformedQualifier = null; + return StaticScopeKind.Global; + } else if (gen.CurrentModule != null) { + // statically (lexically) implicitly bound to the enclosing module: + transformedQualifier = gen.CurrentModule.SelfVariable; // TODO: remove, should be retrieved from code context/scope + return StaticScopeKind.EnclosingModule; + } else { + // statically (lexically) implicitly bound to top declaring module: + transformedQualifier = null; + return StaticScopeKind.EnclosingModule; + } + } + + internal override MSA.Expression/*!*/ TransformReadVariable(AstGenerator/*!*/ gen, bool tryRead) { + return TransformRead(gen, OpGet); + } + + private const int OpGet = 0; + private const int OpIsDefined = 1; + + private MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen, int/*!*/ opKind) { + MSA.Expression transformedName = TransformName(gen); + MSA.Expression transformedQualifier; + + switch (TransformQualifier(gen, out transformedQualifier)) { + case StaticScopeKind.Global: + return (opKind == OpGet ? Methods.GetGlobalConstant : Methods.IsDefinedGlobalConstant). + OpCall(gen.CurrentScopeVariable, transformedName); + + case StaticScopeKind.EnclosingModule: + return (opKind == OpGet ? Methods.GetUnqualifiedConstant : Methods.IsDefinedUnqualifiedConstant). + OpCall(gen.CurrentScopeVariable, transformedName); + + case StaticScopeKind.Explicit: + if (opKind == OpGet) { + return Methods.GetQualifiedConstant.OpCall(AstFactory.Box(transformedQualifier), gen.CurrentScopeVariable, transformedName); + } else { + return gen.TryCatchAny( + Methods.IsDefinedQualifiedConstant.OpCall(AstFactory.Box(transformedQualifier), gen.CurrentScopeVariable, transformedName), + Ast.Constant(false) + ); + } + } + + throw Assert.Unreachable; + } + + internal override MSA.Expression/*!*/ TransformWriteVariable(AstGenerator/*!*/ gen, MSA.Expression/*!*/ rightValue) { + MSA.Expression transformedName = TransformName(gen); + MSA.Expression transformedQualifier; + + switch (TransformQualifier(gen, out transformedQualifier)) { + case StaticScopeKind.Global: + return Methods.SetGlobalConstant.OpCall(AstFactory.Box(rightValue), gen.CurrentScopeVariable, transformedName); + + case StaticScopeKind.EnclosingModule: + return Methods.SetUnqualifiedConstant.OpCall(AstFactory.Box(rightValue), gen.CurrentScopeVariable, transformedName); + + case StaticScopeKind.Explicit: + return Methods.SetQualifiedConstant.OpCall(AstFactory.Box(rightValue), transformedQualifier, gen.CurrentScopeVariable, transformedName); + } + + throw Assert.Unreachable; + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + return TransformRead(gen, OpIsDefined); + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + return "constant"; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/GlobalVariable.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/GlobalVariable.cs new file mode 100644 index 0000000000..cf46e13434 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/GlobalVariable.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + public partial class GlobalVariable : Variable { + + public string/*!*/ FullName { + get { return "$" + Name; } + } + + public GlobalVariable(string/*!*/ name, SourceSpan location) + : base(name, location) { + Debug.Assert(name.ToString() == "$" || !name.ToString().StartsWith("$")); + } + + internal override MSA.Expression/*!*/ TransformReadVariable(AstGenerator/*!*/ gen, bool tryRead) { + return Methods.GetGlobalVariable.OpCall(gen.CurrentScopeVariable, TransformName(gen)); + } + + internal override MSA.Expression/*!*/ TransformWriteVariable(AstGenerator/*!*/ gen, MSA.Expression/*!*/ rightValue) { + return Methods.SetGlobalVariable.OpCall(AstFactory.Box(rightValue), gen.CurrentScopeVariable, TransformName(gen)); + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + return Methods.IsDefinedGlobalVariable.OpCall(gen.CurrentScopeVariable, TransformName(gen)); + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + return "global-variable"; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/InstanceVariable.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/InstanceVariable.cs new file mode 100644 index 0000000000..3e602ae828 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/InstanceVariable.cs @@ -0,0 +1,47 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class InstanceVariable : Variable { + public InstanceVariable(string/*!*/ name, SourceSpan location) + : base(name, location) { + Debug.Assert(name.StartsWith("@")); + } + + internal override MSA.Expression/*!*/ TransformReadVariable(AstGenerator/*!*/ gen, bool tryRead) { + return Methods.GetInstanceVariable.OpCall(gen.CurrentScopeVariable, gen.CurrentSelfVariable, AstUtils.Constant(Name)); + } + + internal override MSA.Expression/*!*/ TransformWriteVariable(AstGenerator/*!*/ gen, MSA.Expression/*!*/ rightValue) { + return Methods.SetInstanceVariable.OpCall(gen.CurrentSelfVariable, AstFactory.Box(rightValue), gen.CurrentScopeVariable, AstUtils.Constant(Name)); + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + return Methods.IsDefinedInstanceVariable.OpCall(gen.CurrentScopeVariable, gen.CurrentSelfVariable, AstUtils.Constant(Name)); + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + return "instance-variable"; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/LeftValue.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/LeftValue.cs new file mode 100644 index 0000000000..1a117e5e3b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/LeftValue.cs @@ -0,0 +1,53 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + + public abstract class LeftValue : Expression { + internal static new readonly List/*!*/ EmptyList = new List(); + + public LeftValue(SourceSpan location) + : base(location) { + } + + // Gets an expression that evaluates to the part of the left value that represents a holder (target) of the left value; + // For example target.bar, target[key], ... + // This target is passed to the TransformWrite by assignment expressions. + // This is necessary to prevent redundant evaluation of the target expression in in-place assignment left op= right. + // Returns null if the left value doesn't have target expression. + internal abstract MSA.Expression TransformTargetRead(AstGenerator/*!*/ gen); + + internal sealed override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return TransformRead(gen, TransformTargetRead(gen), false); + } + + internal MSA.Expression/*!*/ TransformWrite(AstGenerator/*!*/ gen, MSA.Expression/*!*/ rightValue) { + return TransformWrite(gen, TransformTargetRead(gen), rightValue); + } + + internal abstract MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen, MSA.Expression targetValue, bool tryRead); + internal abstract MSA.Expression/*!*/ TransformWrite(AstGenerator/*!*/ gen, MSA.Expression targetValue, MSA.Expression/*!*/ rightValue); + + internal virtual List/*!*/ ToList() { + List result = new List(); + result.Add(this); + return result; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/LocalVariable.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/LocalVariable.cs new file mode 100644 index 0000000000..37e3fdea8c --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/LocalVariable.cs @@ -0,0 +1,78 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using IronRuby.Builtins; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class LocalVariable : Variable { + private MSA.ParameterExpression _transformed; + + public LocalVariable(string/*!*/ name, SourceSpan location) + : base(name, location) { + } + + internal void TransformDefinition(ScopeBuilder/*!*/ locals) { + if (_transformed == null) { + _transformed = locals.DefineVariable(Name); + } + } + + internal MSA.ParameterExpression/*!*/ TransformParameterDefinition() { + Debug.Assert(_transformed == null); + return _transformed = Ast.Parameter(typeof(object), Name); + } + + internal MSA.ParameterExpression/*!*/ TransformBlockParameterDefinition() { + Debug.Assert(_transformed == null); + return _transformed = Ast.Parameter(typeof(Proc), Name); + } + + internal override MSA.Expression/*!*/ TransformReadVariable(AstGenerator/*!*/ gen, bool tryRead) { + if (_transformed != null) { + // static lookup: + return _transformed; + } else { + // dynamic lookup: + return Methods.GetLocalVariable.OpCall(gen.CurrentScopeVariable, AstUtils.Constant(Name)); + } + } + + internal override MSA.Expression/*!*/ TransformWriteVariable(AstGenerator/*!*/ gen, MSA.Expression/*!*/ rightValue) { + if (_transformed != null) { + // static lookup: + return Ast.Assign(_transformed, AstUtils.Convert(rightValue, _transformed.Type)); + } else { + // dynamic lookup: + return Methods.SetLocalVariable.OpCall(AstFactory.Box(rightValue), gen.CurrentScopeVariable, AstUtils.Constant(Name)); + } + } + + internal override MSA.Expression TransformDefinedCondition(AstGenerator/*!*/ gen) { + return null; + } + + internal override string/*!*/ GetNodeName(AstGenerator/*!*/ gen) { + // TODO: 1.8/1.9 variables in a block + return "local-variable"; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/Placeholder.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/Placeholder.cs new file mode 100644 index 0000000000..c92da49867 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/Placeholder.cs @@ -0,0 +1,50 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; +using System; + +namespace IronRuby.Compiler.Ast { + + /// + /// Used as a null LHS without backing storage. + /// E.g. 'a, = 1' is represented as 'ParallelAssignment(CoumpoundLeftValue(Variable, Placeholder), CompoundRightValue(Constant)) + /// + public partial class Placeholder : Variable { + public static readonly Placeholder/*!*/ Singleton = new Placeholder(); + + private Placeholder() + : base(String.Empty, SourceSpan.None) { + } + + internal override MSA.Expression/*!*/ TransformReadVariable(AstGenerator/*!*/ gen, bool tryRead) { + throw Assert.Unreachable; + } + + internal override MSA.Expression/*!*/ TransformWriteVariable(AstGenerator/*!*/ gen, MSA.Expression/*!*/ rightValue) { + Assert.NotNull(gen, rightValue); + + // no-op + return rightValue; + } + + public override string/*!*/ ToString() { + return " "; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/Variable.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/Variable.cs new file mode 100644 index 0000000000..5c47e5b7dc --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/LeftValues/Variable.cs @@ -0,0 +1,65 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using Microsoft.Scripting.Utils; + + public abstract class Variable : LeftValue { + private readonly string/*!*/ _name; + + public string/*!*/ Name { + get { return _name; } + } + + public Variable(string/*!*/ name, SourceSpan location) + : base(location) { + ContractUtils.RequiresNotNull(name, "name"); + + _name = name; + } + + internal MSA.Expression/*!*/ TransformName(AstGenerator/*!*/ gen) { + return Ast.Constant(_name); + } + + internal sealed override MSA.Expression TransformTargetRead(AstGenerator/*!*/ gen) { + return null; + } + + internal abstract MSA.Expression/*!*/ TransformReadVariable(AstGenerator/*!*/ gen, bool tryRead); + internal abstract MSA.Expression/*!*/ TransformWriteVariable(AstGenerator/*!*/ gen, MSA.Expression/*!*/ rightValue); + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen, MSA.Expression targetValue, bool tryRead) { + Debug.Assert(targetValue == null); + return TransformReadVariable(gen, tryRead); + } + + internal override MSA.Expression/*!*/ TransformWrite(AstGenerator/*!*/ gen, MSA.Expression targetValue, MSA.Expression/*!*/ rightValue) { + Debug.Assert(targetValue == null); + return TransformWriteVariable(gen, rightValue); + } + + public override string/*!*/ ToString() { + return _name.ToString(); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Maplet.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Maplet.cs new file mode 100644 index 0000000000..8622ab0bc5 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Maplet.cs @@ -0,0 +1,41 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Ast { + + public partial class Maplet : Node { + private readonly Expression/*!*/ _key; + private readonly Expression/*!*/ _value; + + public Expression/*!*/ Key { + get { return _key; } + } + + public Expression/*!*/ Value { + get { return _value; } + } + + public Maplet(Expression/*!*/ key, Expression/*!*/ value, SourceSpan location) + : base(location) { + Assert.NotNull(key, value); + + _key = key; + _value = value; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Node.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Node.cs new file mode 100644 index 0000000000..c7338a3809 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Node.cs @@ -0,0 +1,43 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic; +using Microsoft.Scripting; + +namespace IronRuby.Compiler.Ast { + + public abstract class Node { + private readonly SourceSpan _location; + + public abstract NodeTypes NodeType { get; } + internal protected abstract void Walk(Walker/*!*/ walker); + + public SourceSpan Location { + get { return _location; } + } + + protected Node(SourceSpan location) { + _location = location; + +#if DEBUG + PerfTrack.NoteEvent(PerfTrack.Categories.Count, "RubyAST: " + GetType().Name); +#endif + + } + } +} + + + diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Parameters.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Parameters.cs new file mode 100644 index 0000000000..9eac6e8c34 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Parameters.cs @@ -0,0 +1,94 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class Parameters : Node { + internal static readonly Parameters/*!*/ Empty = new Parameters(null, null, null, null, SourceSpan.None); + + private readonly List _mandatory; + private readonly List _optional; + private readonly LocalVariable _array; + private readonly LocalVariable _block; + + public List Mandatory { + get { return _mandatory; } + } + + public List Optional { + get { return _optional; } + } + + public LocalVariable Array { + get { return _array; } + } + + public LocalVariable Block { + get { return _block; } + } + + public int MandatoryCount { + get { + return (_mandatory != null) ? _mandatory.Count : 0; + } + } + + public int OptionalCount { + get { + return (_optional != null) ? _optional.Count : 0; + } + } + + public Parameters(List mandatory, List optional, + LocalVariable array, LocalVariable block, SourceSpan location) + : base(location) { + + _mandatory = mandatory; + _optional = optional; + _array = array; + _block = block; + } + + internal MSA.Expression/*!*/ TransformOptionalsInitialization(AstGenerator/*!*/ gen) { + Assert.NotNull(gen); + + if (_optional == null) return Ast.Empty(); + + MSA.Expression singleton = gen.CurrentScope.DefineHiddenVariable("#default", typeof(object)); + + MSA.Expression result = Ast.Empty(); + for (int i = 0; i < _optional.Count; i++) { + result = AstUtils.IfThen(Ast.Equal(_optional[i].Left.TransformRead(gen), singleton), + result, + _optional[i].TransformRead(gen) + ); + } + + return Ast.Block( + Ast.Assign(singleton, Ast.Field(null, Fields.RubyOps_DefaultArgumentField)), + result, + Ast.Empty() + ); + } + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/ScopeBuilder.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/ScopeBuilder.cs new file mode 100644 index 0000000000..7b2cb1b09d --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/ScopeBuilder.cs @@ -0,0 +1,124 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using Microsoft.Scripting.Utils; +using System.Threading; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using Microsoft.Scripting.Ast; + + internal sealed class ScopeBuilder { + private static readonly List _FinishedVariables = new List(0); + + // local variables and parameters exposed at runtime via dictionary: + private List _visibleLocals; + + // all local variables except for parameters that are used in the scope: + private List _allLocals; + +#if DEBUG + private static int _Id; + private int _id; + + public ScopeBuilder() { + _id = Interlocked.Increment(ref _Id); + } +#endif + + private bool VisibleFinished { + get { return _visibleLocals == _FinishedVariables; } + } + + private bool AllFinished { + get { return _allLocals == _FinishedVariables; } + } + + public MSA.ParameterExpression/*!*/ DefineHiddenVariable(string/*!*/ name, Type/*!*/ type) { +#if DEBUG + int hiddenCount = ((_allLocals != null) ? _allLocals.Count : 0) - ((_visibleLocals != null) ? _visibleLocals.Count : 0); + name += "_" + _id + "_" + hiddenCount; +#endif + return AddHidden(Ast.Variable(type, name)); + } + + // Defines visible user variable (of object type). + public MSA.ParameterExpression/*!*/ DefineVariable(string/*!*/ name) { + return AddVisible(Ast.Variable(typeof(object), name)); + } + + public MSA.ParameterExpression/*!*/ AddVisible(MSA.ParameterExpression/*!*/ variable) { + ContractUtils.Requires(!VisibleFinished); + Debug.Assert(!AllFinished); + + if (_visibleLocals == null) { + _visibleLocals = new List(); + } + + if (_allLocals == null) { + _allLocals = new List(); + } + + _visibleLocals.Add(variable); + _allLocals.Add(variable); + return variable; + } + + public void AddVisibleParameters(MSA.ParameterExpression[]/*!*/ parameters, int hiddenCount) { + ContractUtils.Requires(_visibleLocals == null, "Parameters should be added prior to other visible variables"); + + _visibleLocals = new List(parameters.Length - hiddenCount); + for (int i = hiddenCount; i < parameters.Length; i++) { + _visibleLocals.Add(parameters[i]); + } + } + + public MSA.ParameterExpression/*!*/ AddHidden(MSA.ParameterExpression/*!*/ variable) { + ContractUtils.Requires(!AllFinished); + if (_allLocals == null) { + _allLocals = new List(); + } + _allLocals.Add(variable); + return variable; + } + + public MSA.Expression/*!*/ VisibleVariables() { + MSA.Expression result; + if (_visibleLocals != null) { + result = Utils.VariableDictionary(new ReadOnlyCollection(_visibleLocals.ToArray())); + } else { + result = Utils.VariableDictionary(); + } + _visibleLocals = _FinishedVariables; + return result; + } + + public MSA.Expression/*!*/ CreateScope(MSA.Expression/*!*/ body) { + MSA.Expression result; + if (_allLocals != null) { + result = Ast.Block(new ReadOnlyCollection(_allLocals.ToArray()), body); + } else { + result = body; + } + _allLocals = _FinishedVariables; + return result; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/SourceUnitTree.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/SourceUnitTree.cs new file mode 100644 index 0000000000..d7994d7f1b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/SourceUnitTree.cs @@ -0,0 +1,183 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Dynamic; +using System.Text; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using MSA = System.Linq.Expressions; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class SourceUnitTree : Node { + + private readonly LexicalScope/*!*/ _definedScope; + private readonly List _initializers; + private readonly List _statements; + private readonly Encoding/*!*/ _encoding; + + // An offset of the first byte after __END__ that can be read via DATA constant or -1 if __END__ is not present. + private readonly int _dataOffset; + + public List Initializers { + get { return _initializers; } + } + + public List Statements { + get { return _statements; } + } + + public Encoding/*!*/ Encoding { + get { return _encoding; } + } + + public SourceUnitTree(LexicalScope/*!*/ definedScope, List statements, List initializers, + Encoding/*!*/ encoding, int dataOffset) + : base(SourceSpan.None) { + Assert.NotNull(definedScope, encoding); + + _definedScope = definedScope; + _statements = statements; + _initializers = initializers; + _encoding = encoding; + _dataOffset = dataOffset; + } + + internal MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + Debug.Assert(gen != null); + + ScopeBuilder scope = new ScopeBuilder(); + + MSA.ParameterExpression[] parameters; + MSA.Expression selfVariable; + MSA.Expression rfcVariable; + MSA.Expression parentScope; + MSA.Expression language; + MSA.Expression runtimeScopeVariable; + MSA.Expression moduleVariable; + MSA.Expression blockParameter; + MSA.Expression currentMethodVariable; + + if (gen.CompilerOptions.IsEval) { + parameters = new MSA.ParameterExpression[6]; + + parameters[0] = Ast.Parameter(typeof(RubyScope), "#scope"); + selfVariable = parameters[1] = Ast.Parameter(typeof(object), "#self"); + parameters[2] = Ast.Parameter(typeof(RubyModule), "#module"); + blockParameter = parameters[3] = Ast.Parameter(typeof(Proc), "#block"); + currentMethodVariable = parameters[4] = Ast.Parameter(typeof(RubyMethodInfo), "#method"); + rfcVariable = parameters[5] = Ast.Parameter(typeof(RuntimeFlowControl), "#rfc"); + + if (gen.CompilerOptions.IsModuleEval) { + runtimeScopeVariable = scope.DefineHiddenVariable("#scope", typeof(RubyScope)); + parentScope = parameters[0]; + moduleVariable = parameters[2]; + } else { + runtimeScopeVariable = parameters[0]; + moduleVariable = null; + parentScope = null; + } + + language = null; + } else { + parameters = new MSA.ParameterExpression[2]; + parentScope = parameters[0] = Ast.Parameter(typeof(Scope), "#globalScope"); + language = parameters[1] = Ast.Parameter(typeof(LanguageContext), "#language"); + + selfVariable = scope.DefineHiddenVariable("#self", typeof(object)); + rfcVariable = scope.DefineHiddenVariable("#rfc", typeof(RuntimeFlowControl)); + runtimeScopeVariable = scope.DefineHiddenVariable("#scope", typeof(RubyScope)); + blockParameter = null; + currentMethodVariable = null; + moduleVariable = null; + } + + gen.EnterSourceUnit( + scope, + selfVariable, + runtimeScopeVariable, + blockParameter, + rfcVariable, + currentMethodVariable, + gen.CompilerOptions.TopLevelMethodName, // method name + null // parameters + ); + + _definedScope.TransformLocals(scope); + + MSA.Expression scopeFactoryCall; + + if (gen.CompilerOptions.IsEval) { + if (gen.CompilerOptions.IsModuleEval) { + scopeFactoryCall = Methods.CreateModuleEvalScope.OpCall( + scope.VisibleVariables(), parentScope, selfVariable, moduleVariable + ); + } else { + scopeFactoryCall = null; + } + } else if (!gen.CompilerOptions.IsIncluded) { + scopeFactoryCall = Methods.CreateMainTopLevelScope.OpCall(scope.VisibleVariables(), parentScope, language, selfVariable, rfcVariable, + Ast.Constant(gen.SourceUnit.Path, typeof(string)), Ast.Constant(_dataOffset)); + } else if (gen.CompilerOptions.IsWrapped) { + scopeFactoryCall = Methods.CreateWrappedTopLevelScope.OpCall(scope.VisibleVariables(), parentScope, language, selfVariable, rfcVariable); + } else { + scopeFactoryCall = Methods.CreateTopLevelScope.OpCall(scope.VisibleVariables(), parentScope, language, selfVariable, rfcVariable); + } + + MSA.Expression prologue, body; + + if (scopeFactoryCall != null) { + prologue = Ast.Assign(runtimeScopeVariable, scopeFactoryCall); + } else { + prologue = null; + } + + if (gen.SourceUnit.Kind == SourceCodeKind.InteractiveCode) { + var resultVariable = scope.DefineHiddenVariable("#result", typeof(object)); + + var epilogue = Methods.PrintInteractiveResult.OpCall(runtimeScopeVariable, + Ast.Dynamic(ConvertToSAction.Instance, typeof(MutableString), gen.CurrentScopeVariable, + Ast.Dynamic(RubyCallAction.Make("inspect", RubyCallSignature.WithScope(0)), typeof(object), + gen.CurrentScopeVariable, resultVariable + ) + ) + ); + + body = gen.TransformStatements(prologue, _statements, epilogue, ResultOperation.Store(resultVariable)); + } else { + body = gen.TransformStatements(prologue, _statements, ResultOperation.Return); + } + + body = gen.AddReturnTarget(scope.CreateScope(body)); + gen.LeaveSourceUnit(); + + return Ast.Lambda( + body, + RubyExceptionData.TopLevelMethodName, + parameters + ); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/AliasStatement.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/AliasStatement.cs new file mode 100644 index 0000000000..f75b175d5d --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/AliasStatement.cs @@ -0,0 +1,63 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic; +using Microsoft.Scripting; +using MSA = System.Linq.Expressions; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using Microsoft.Scripting.Utils; + + public partial class AliasStatement : Expression { + private readonly string/*!*/ _newName; + private readonly string/*!*/ _oldName; + private readonly bool _isMethodAlias; + + public string/*!*/ NewName { + get { return _newName; } + } + + public string/*!*/ OldName { + get { return _oldName; } + } + + public bool IsMethodAlias { + get { return _isMethodAlias; } + } + + public bool IsGlobalVariableAlias { + get { return !_isMethodAlias; } + } + + public AliasStatement(bool isMethodAlias, string/*!*/ newName, string/*!*/ oldName, SourceSpan location) + : base(location) { + Assert.NotNull(newName, oldName); + _newName = newName; + _oldName = oldName; + _isMethodAlias = isMethodAlias; + } + + internal override MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + return (_isMethodAlias ? Methods.AliasMethod : Methods.AliasGlobalVariable). + OpCall(gen.CurrentScopeVariable, AstUtils.Constant(_newName), AstUtils.Constant(_oldName)); + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return AstFactory.Block(Transform(gen), Ast.Constant(null)); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/ConditionalStatement.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/ConditionalStatement.cs new file mode 100644 index 0000000000..e573ef7382 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/ConditionalStatement.cs @@ -0,0 +1,80 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + /// + /// Represents if- and unless- statements. + /// Also used for (condition) ? jump-statement : jump-statement. + /// + public partial class ConditionalStatement : Expression { + private readonly Expression/*!*/ _condition; + private readonly Expression/*!*/ _body; + private readonly Expression _elseStatement; + private readonly bool _negateCondition; + + public Expression/*!*/ Condition { + get { return _condition; } + } + + public Expression/*!*/ Body { + get { return _body; } + } + + public Expression ElseStatement { + get { return _elseStatement; } + } + + public bool IsUnless { + get { return _negateCondition; } + } + + public ConditionalStatement(Expression/*!*/ condition, bool negateCondition, Expression/*!*/ body, Expression elseStatement, SourceSpan location) + : base(location) { + ContractUtils.RequiresNotNull(condition, "condition"); + ContractUtils.RequiresNotNull(body, "body"); + + _condition = condition; + _body = body; + _negateCondition = negateCondition; + _elseStatement = elseStatement; + } + + private MSA.Expression/*!*/ TransformCondition(AstGenerator/*!*/ gen) { + var transformedCondition = _condition.TransformRead(gen); + return (_negateCondition) ? AstFactory.IsFalse(transformedCondition) : AstFactory.IsTrue(transformedCondition); + } + + internal override MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + return AstUtils.IfThenElse(TransformCondition(gen), _body.Transform(gen), + _elseStatement != null ? _elseStatement.Transform(gen) : Ast.Empty() + ); + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return Ast.Condition( + TransformCondition(gen), + AstFactory.Box(_body.TransformRead(gen)), + (_elseStatement != null) ? AstFactory.Box(_elseStatement.TransformRead(gen)) : (MSA.Expression)Ast.Constant(null) + ); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/Finalizer.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/Finalizer.cs new file mode 100644 index 0000000000..03c252000b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/Finalizer.cs @@ -0,0 +1,50 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + + public partial class Finalizer : Expression { + // END { body } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] // TODO + private readonly LexicalScope/*!*/ _definedScope; + + // TODO: + private readonly List _statements; + + public List Statements { + get { return _statements; } + } + + public Finalizer(LexicalScope/*!*/ definedScope, List statements, SourceSpan location) + : base(location) { + Assert.NotNull(definedScope); + + _definedScope = definedScope; + _statements = statements; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + throw new NotImplementedException(); + } + } + +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/Initializer.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/Initializer.cs new file mode 100644 index 0000000000..0ad42ddb73 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/Initializer.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler.Ast { + + public partial class Initializer : Expression { + // BEGIN { body } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] // TODO + private readonly LexicalScope/*!*/ _definedScope; + + // TODO: + private readonly List _statements; + + public List Statements { + get { return _statements; } + } + + public Initializer(LexicalScope/*!*/ definedScope, List statements, SourceSpan location) + : base(location) { + Assert.NotNull(definedScope); + + _definedScope = definedScope; + _statements = statements; + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + throw new NotImplementedException(); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/UndefineStatement.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/UndefineStatement.cs new file mode 100644 index 0000000000..ae48c2cc7e --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Statements/UndefineStatement.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + + public partial class UndefineStatement : Expression { + private readonly List/*!*/ _items; + + public List/*!*/ Items { + get { return _items; } + } + + public UndefineStatement(List/*!*/ items, SourceSpan location) + : base(location) { + Assert.NotNull(items); + + _items = items; + } + + internal override MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen) { + MSA.Expression[] result = new MSA.Expression[_items.Count + 1]; + for (int i = 0; i < _items.Count; i++) { + result[i] = Methods.UndefineMethod.OpCall(gen.CurrentScopeVariable, AstUtils.Constant(_items[i].Name)); + } + result[_items.Count] = Ast.Empty(); + return Ast.Block(result); + } + + internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { + return AstFactory.Block(Transform(gen), Ast.Constant(null)); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Walkers/NodeTypes.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Walkers/NodeTypes.cs new file mode 100644 index 0000000000..5c6e7852e8 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Walkers/NodeTypes.cs @@ -0,0 +1,181 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace IronRuby.Compiler.Ast { + public enum NodeTypes { + // root: + SourceUnitTree, + + // misc: + BlockDefinition, + BlockReference, + Body, + Maplet, + Parameters, + Arguments, + + // declarations: + ClassDeclaration, + MethodDeclaration, + ModuleDeclaration, + SingletonDeclaration, + + // expressions: + AndExpression, + ArrayConstructor, + AssignmentExpression, + AstNodeDescriptionExpression, + BlockExpression, + CaseExpression, + ConditionalExpression, + ConditionalJumpExpression, + ErrorExpression, + ForLoopExpression, + HashConstructor, + IfExpression, + Literal, + StringLiteral, + SymbolLiteral, + EncodingExpression, + MethodCall, + MatchExpression, + NotExpression, + OrExpression, + RangeExpression, + RegularExpression, + RegexMatchReference, + RescueExpression, + SelfReference, + StringConstructor, + SuperCall, + UnlessExpression, + WhileLoopExpression, + YieldCall, + + // l-values: + ArrayItemAccess, + AttributeAccess, + ClassVariable, + CompoundLeftValue, + ConstantVariable, + GlobalVariable, + InstanceVariable, + LocalVariable, + Placeholder, + + // assignments: + MemberAssignmentExpression, + ParallelAssignmentExpression, + SimpleAssignmentExpression, + + // statements: + AliasStatement, + ConditionalStatement, + ExpressionStatement, + Finalizer, + Initializer, + UndefineStatement, + + // jump statements: + BreakStatement, + NextStatement, + RedoStatement, + RetryStatement, + ReturnStatement, + + // clauses: + RescueClause, + WhenClause, + ElseIfClause, + } + + // TODO: generate: + public partial class SourceUnitTree { public override NodeTypes NodeType { get { return NodeTypes.SourceUnitTree; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class BlockDefinition { public override NodeTypes NodeType { get { return NodeTypes.BlockDefinition; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class BlockReference { public override NodeTypes NodeType { get { return NodeTypes.BlockReference; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class Body { public override NodeTypes NodeType { get { return NodeTypes.Body; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class Maplet { public override NodeTypes NodeType { get { return NodeTypes.Maplet; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class Parameters { public override NodeTypes NodeType { get { return NodeTypes.Parameters; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class Arguments { public override NodeTypes NodeType { get { return NodeTypes.Arguments; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + + public partial class ClassDeclaration { public override NodeTypes NodeType { get { return NodeTypes.ClassDeclaration; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class MethodDeclaration { public override NodeTypes NodeType { get { return NodeTypes.MethodDeclaration; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class ModuleDeclaration { public override NodeTypes NodeType { get { return NodeTypes.ModuleDeclaration; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class SingletonDeclaration { public override NodeTypes NodeType { get { return NodeTypes.SingletonDeclaration; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + + public partial class AndExpression { public override NodeTypes NodeType { get { return NodeTypes.AndExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class ArrayConstructor { public override NodeTypes NodeType { get { return NodeTypes.ArrayConstructor; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class IsDefinedExpression { public override NodeTypes NodeType { get { return NodeTypes.AstNodeDescriptionExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class BlockExpression { public override NodeTypes NodeType { get { return NodeTypes.BlockExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class CaseExpression { public override NodeTypes NodeType { get { return NodeTypes.CaseExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class ConditionalExpression { public override NodeTypes NodeType { get { return NodeTypes.ConditionalExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class ConditionalJumpExpression { public override NodeTypes NodeType { get { return NodeTypes.ConditionalJumpExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class ErrorExpression { public override NodeTypes NodeType { get { return NodeTypes.ErrorExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class ForLoopExpression { public override NodeTypes NodeType { get { return NodeTypes.ForLoopExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class HashConstructor { public override NodeTypes NodeType { get { return NodeTypes.HashConstructor; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class IfExpression { public override NodeTypes NodeType { get { return NodeTypes.IfExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class Literal { public override NodeTypes NodeType { get { return NodeTypes.Literal; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class StringLiteral { public override NodeTypes NodeType { get { return NodeTypes.StringLiteral; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class SymbolLiteral { public override NodeTypes NodeType { get { return NodeTypes.SymbolLiteral; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class EncodingExpression { public override NodeTypes NodeType { get { return NodeTypes.EncodingExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class MethodCall { public override NodeTypes NodeType { get { return NodeTypes.MethodCall; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class MatchExpression { public override NodeTypes NodeType { get { return NodeTypes.MatchExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class NotExpression { public override NodeTypes NodeType { get { return NodeTypes.NotExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class OrExpression { public override NodeTypes NodeType { get { return NodeTypes.OrExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class RangeExpression { public override NodeTypes NodeType { get { return NodeTypes.RangeExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class RegularExpression { public override NodeTypes NodeType { get { return NodeTypes.RegularExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class RegexMatchReference { public override NodeTypes NodeType { get { return NodeTypes.RegexMatchReference; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class RescueExpression { public override NodeTypes NodeType { get { return NodeTypes.RescueExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class SelfReference { public override NodeTypes NodeType { get { return NodeTypes.SelfReference; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class StringConstructor { public override NodeTypes NodeType { get { return NodeTypes.StringConstructor; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class SuperCall { public override NodeTypes NodeType { get { return NodeTypes.SuperCall; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class UnlessExpression { public override NodeTypes NodeType { get { return NodeTypes.UnlessExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class WhileLoopExpression { public override NodeTypes NodeType { get { return NodeTypes.WhileLoopExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class YieldCall { public override NodeTypes NodeType { get { return NodeTypes.YieldCall; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + + public partial class ArrayItemAccess { public override NodeTypes NodeType { get { return NodeTypes.ArrayItemAccess; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class AttributeAccess { public override NodeTypes NodeType { get { return NodeTypes.AttributeAccess; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class ClassVariable { public override NodeTypes NodeType { get { return NodeTypes.ClassVariable; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class CompoundLeftValue { public override NodeTypes NodeType { get { return NodeTypes.CompoundLeftValue; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class ConstantVariable { public override NodeTypes NodeType { get { return NodeTypes.ConstantVariable; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class GlobalVariable { public override NodeTypes NodeType { get { return NodeTypes.GlobalVariable; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class InstanceVariable { public override NodeTypes NodeType { get { return NodeTypes.InstanceVariable; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class LocalVariable { public override NodeTypes NodeType { get { return NodeTypes.LocalVariable; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class Placeholder { public override NodeTypes NodeType { get { return NodeTypes.Placeholder; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + + public partial class MemberAssignmentExpression { public override NodeTypes NodeType { get { return NodeTypes.MemberAssignmentExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class ParallelAssignmentExpression { public override NodeTypes NodeType { get { return NodeTypes.ParallelAssignmentExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class SimpleAssignmentExpression { public override NodeTypes NodeType { get { return NodeTypes.SimpleAssignmentExpression; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + + public partial class AliasStatement { public override NodeTypes NodeType { get { return NodeTypes.AliasStatement; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class ConditionalStatement { public override NodeTypes NodeType { get { return NodeTypes.ConditionalStatement; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class Finalizer { public override NodeTypes NodeType { get { return NodeTypes.Finalizer; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class Initializer { public override NodeTypes NodeType { get { return NodeTypes.Initializer; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class UndefineStatement { public override NodeTypes NodeType { get { return NodeTypes.UndefineStatement; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + + public partial class BreakStatement { public override NodeTypes NodeType { get { return NodeTypes.BreakStatement; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class NextStatement { public override NodeTypes NodeType { get { return NodeTypes.NextStatement; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class RedoStatement { public override NodeTypes NodeType { get { return NodeTypes.RedoStatement; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class RetryStatement { public override NodeTypes NodeType { get { return NodeTypes.RetryStatement; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class ReturnStatement { public override NodeTypes NodeType { get { return NodeTypes.ReturnStatement; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + + public partial class RescueClause { public override NodeTypes NodeType { get { return NodeTypes.RescueClause; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class WhenClause { public override NodeTypes NodeType { get { return NodeTypes.WhenClause; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } + public partial class ElseIfClause { public override NodeTypes NodeType { get { return NodeTypes.ElseIfClause; } } internal protected override void Walk(Walker/*!*/ walker) { walker.Walk(this); } } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Walkers/Walker.Generated.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Walkers/Walker.Generated.cs new file mode 100644 index 0000000000..f6ba2cf77a --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Walkers/Walker.Generated.cs @@ -0,0 +1,173 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace IronRuby.Compiler.Ast { + public partial class Walker { + // TODO: generate + // root: + public virtual bool Enter(SourceUnitTree/*!*/ node) { return true; } + public virtual void Exit(SourceUnitTree/*!*/ node) { } + + // misc: + public virtual bool Enter(BlockDefinition/*!*/ node) { return true; } + public virtual void Exit(BlockDefinition/*!*/ node) { } + public virtual bool Enter(BlockReference/*!*/ node) { return true; } + public virtual void Exit(BlockReference/*!*/ node) { } + public virtual bool Enter(Body/*!*/ node) { return true; } + public virtual void Exit(Body/*!*/ node) { } + public virtual bool Enter(Maplet/*!*/ node) { return true; } + public virtual void Exit(Maplet/*!*/ node) { } + public virtual bool Enter(Parameters/*!*/ node) { return true; } + public virtual void Exit(Parameters/*!*/ node) { } + public virtual bool Enter(Arguments/*!*/ node) { return true; } + public virtual void Exit(Arguments/*!*/ node) { } + + // declarations: + public virtual bool Enter(ClassDeclaration/*!*/ node) { return true; } + public virtual void Exit(ClassDeclaration/*!*/ node) { } + public virtual bool Enter(MethodDeclaration/*!*/ node) { return true; } + public virtual void Exit(MethodDeclaration/*!*/ node) { } + public virtual bool Enter(ModuleDeclaration/*!*/ node) { return true; } + public virtual void Exit(ModuleDeclaration/*!*/ node) { } + public virtual bool Enter(SingletonDeclaration/*!*/ node) { return true; } + public virtual void Exit(SingletonDeclaration/*!*/ node) { } + + // expressions: + public virtual bool Enter(AndExpression/*!*/ node) { return true; } + public virtual void Exit(AndExpression/*!*/ node) { } + public virtual bool Enter(ArrayConstructor/*!*/ node) { return true; } + public virtual void Exit(ArrayConstructor/*!*/ node) { } + public virtual bool Enter(AssignmentExpression/*!*/ node) { return true; } + public virtual void Exit(AssignmentExpression/*!*/ node) { } + public virtual bool Enter(IsDefinedExpression/*!*/ node) { return true; } + public virtual void Exit(IsDefinedExpression/*!*/ node) { } + public virtual bool Enter(BlockExpression/*!*/ node) { return true; } + public virtual void Exit(BlockExpression/*!*/ node) { } + public virtual bool Enter(CaseExpression/*!*/ node) { return true; } + public virtual void Exit(CaseExpression/*!*/ node) { } + public virtual bool Enter(ConditionalExpression/*!*/ node) { return true; } + public virtual void Exit(ConditionalExpression/*!*/ node) { } + public virtual bool Enter(ConditionalJumpExpression/*!*/ node) { return true; } + public virtual void Exit(ConditionalJumpExpression/*!*/ node) { } + public virtual bool Enter(ErrorExpression/*!*/ node) { return true; } + public virtual void Exit(ErrorExpression/*!*/ node) { } + public virtual bool Enter(ForLoopExpression/*!*/ node) { return true; } + public virtual void Exit(ForLoopExpression/*!*/ node) { } + public virtual bool Enter(HashConstructor/*!*/ node) { return true; } + public virtual void Exit(HashConstructor/*!*/ node) { } + public virtual bool Enter(IfExpression/*!*/ node) { return true; } + public virtual void Exit(IfExpression/*!*/ node) { } + public virtual bool Enter(Literal/*!*/ node) { return true; } + public virtual void Exit(Literal/*!*/ node) { } + public virtual bool Enter(StringLiteral/*!*/ node) { return true; } + public virtual void Exit(StringLiteral/*!*/ node) { } + public virtual bool Enter(SymbolLiteral/*!*/ node) { return true; } + public virtual void Exit(SymbolLiteral/*!*/ node) { } + public virtual bool Enter(EncodingExpression/*!*/ node) { return true; } + public virtual void Exit(EncodingExpression/*!*/ node) { } + public virtual bool Enter(MethodCall/*!*/ node) { return true; } + public virtual void Exit(MethodCall/*!*/ node) { } + public virtual bool Enter(MatchExpression/*!*/ node) { return true; } + public virtual void Exit(MatchExpression/*!*/ node) { } + public virtual bool Enter(NotExpression/*!*/ node) { return true; } + public virtual void Exit(NotExpression/*!*/ node) { } + public virtual bool Enter(OrExpression/*!*/ node) { return true; } + public virtual void Exit(OrExpression/*!*/ node) { } + public virtual bool Enter(RangeExpression/*!*/ node) { return true; } + public virtual void Exit(RangeExpression/*!*/ node) { } + public virtual bool Enter(RegularExpression/*!*/ node) { return true; } + public virtual void Exit(RegularExpression/*!*/ node) { } + public virtual bool Enter(RescueExpression/*!*/ node) { return true; } + public virtual void Exit(RescueExpression/*!*/ node) { } + public virtual bool Enter(SelfReference/*!*/ node) { return true; } + public virtual void Exit(SelfReference/*!*/ node) { } + public virtual bool Enter(StringConstructor/*!*/ node) { return true; } + public virtual void Exit(StringConstructor/*!*/ node) { } + public virtual bool Enter(SuperCall/*!*/ node) { return true; } + public virtual void Exit(SuperCall/*!*/ node) { } + public virtual bool Enter(UnlessExpression/*!*/ node) { return true; } + public virtual void Exit(UnlessExpression/*!*/ node) { } + public virtual bool Enter(WhileLoopExpression/*!*/ node) { return true; } + public virtual void Exit(WhileLoopExpression/*!*/ node) { } + public virtual bool Enter(YieldCall/*!*/ node) { return true; } + public virtual void Exit(YieldCall/*!*/ node) { } + + // l-values: + public virtual bool Enter(ArrayItemAccess/*!*/ node) { return true; } + public virtual void Exit(ArrayItemAccess/*!*/ node) { } + public virtual bool Enter(AttributeAccess/*!*/ node) { return true; } + public virtual void Exit(AttributeAccess/*!*/ node) { } + public virtual bool Enter(ClassVariable/*!*/ node) { return true; } + public virtual void Exit(ClassVariable/*!*/ node) { } + public virtual bool Enter(CompoundLeftValue/*!*/ node) { return true; } + public virtual void Exit(CompoundLeftValue/*!*/ node) { } + public virtual bool Enter(ConstantVariable/*!*/ node) { return true; } + public virtual void Exit(ConstantVariable/*!*/ node) { } + public virtual bool Enter(GlobalVariable/*!*/ node) { return true; } + public virtual void Exit(GlobalVariable/*!*/ node) { } + public virtual bool Enter(InstanceVariable/*!*/ node) { return true; } + public virtual void Exit(InstanceVariable/*!*/ node) { } + public virtual bool Enter(LocalVariable/*!*/ node) { return true; } + public virtual void Exit(LocalVariable/*!*/ node) { } + public virtual bool Enter(Placeholder/*!*/ node) { return true; } + public virtual void Exit(Placeholder/*!*/ node) { } + public virtual bool Enter(RegexMatchReference/*!*/ node) { return true; } + public virtual void Exit(RegexMatchReference/*!*/ node) { } + + // assignments: + public virtual bool Enter(MemberAssignmentExpression/*!*/ node) { return true; } + public virtual void Exit(MemberAssignmentExpression/*!*/ node) { } + public virtual bool Enter(ParallelAssignmentExpression/*!*/ node) { return true; } + public virtual void Exit(ParallelAssignmentExpression/*!*/ node) { } + public virtual bool Enter(SimpleAssignmentExpression/*!*/ node) { return true; } + public virtual void Exit(SimpleAssignmentExpression/*!*/ node) { } + + // statements: + public virtual bool Enter(AliasStatement/*!*/ node) { return true; } + public virtual void Exit(AliasStatement/*!*/ node) { } + public virtual bool Enter(ConditionalStatement/*!*/ node) { return true; } + public virtual void Exit(ConditionalStatement/*!*/ node) { } + public virtual bool Enter(Finalizer/*!*/ node) { return true; } + public virtual void Exit(Finalizer/*!*/ node) { } + public virtual bool Enter(Initializer/*!*/ node) { return true; } + public virtual void Exit(Initializer/*!*/ node) { } + public virtual bool Enter(UndefineStatement/*!*/ node) { return true; } + public virtual void Exit(UndefineStatement/*!*/ node) { } + + // jump statements: + public virtual bool Enter(BreakStatement/*!*/ node) { return true; } + public virtual void Exit(BreakStatement/*!*/ node) { } + public virtual bool Enter(NextStatement/*!*/ node) { return true; } + public virtual void Exit(NextStatement/*!*/ node) { } + public virtual bool Enter(RedoStatement/*!*/ node) { return true; } + public virtual void Exit(RedoStatement/*!*/ node) { } + public virtual bool Enter(RetryStatement/*!*/ node) { return true; } + public virtual void Exit(RetryStatement/*!*/ node) { } + public virtual bool Enter(ReturnStatement/*!*/ node) { return true; } + public virtual void Exit(ReturnStatement/*!*/ node) { } + + // clauses: + public virtual bool Enter(RescueClause/*!*/ node) { return true; } + public virtual void Exit(RescueClause/*!*/ node) { } + public virtual bool Enter(WhenClause/*!*/ node) { return true; } + public virtual void Exit(WhenClause/*!*/ node) { } + public virtual bool Enter(ElseIfClause/*!*/ node) { return true; } + public virtual void Exit(ElseIfClause/*!*/ node) { } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Ast/Walkers/Walker.cs b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Walkers/Walker.cs new file mode 100644 index 0000000000..e2732b3a27 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Ast/Walkers/Walker.cs @@ -0,0 +1,616 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; + +namespace IronRuby.Compiler.Ast { + public partial class Walker { + public void VisitOptionalList(IList list) where T : Node { + if (list != null) { + for (int i = 0; i < list.Count; i++) { + list[i].Walk(this); + } + } + } + + public void VisitList(List list) where T : Node { + for (int i = 0; i < list.Count; i++) { + list[i].Walk(this); + } + } + + public void Walk(Node/*!*/ node) { + node.Walk(this); + } + + #region Default Walk + + internal protected virtual void Walk(Arguments/*!*/ node) { + if (Enter(node)) { + VisitOptionalList(node.Expressions); + VisitOptionalList(node.Maplets); + + if (node.Array != null) { + node.Array.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(BlockReference/*!*/ node) { + if (Enter(node)) { + node.Expression.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(BlockDefinition/*!*/ node) { + if (Enter(node)) { + node.Parameters.Walk(this); + VisitList(node.Body); + } + Exit(node); + } + + internal protected virtual void Walk(Body/*!*/ node) { + if (Enter(node)) { + VisitList(node.Statements); + VisitOptionalList(node.RescueClauses); + VisitOptionalList(node.ElseStatements); + VisitOptionalList(node.EnsureStatements); + } + Exit(node); + } + + internal protected virtual void Walk(Maplet/*!*/ node) { + if (Enter(node)) { + node.Key.Walk(this); + node.Value.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(Parameters/*!*/ node) { + if (Enter(node)) { + VisitOptionalList(node.Mandatory); + VisitOptionalList(node.Optional); + + if (node.Array != null) { + node.Array.Walk(this); + } + + if (node.Block != null) { + node.Block.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(RegexMatchReference/*!*/ node) { + Enter(node); + Exit(node); + } + + // TODO: + + internal protected virtual void Walk(SourceUnitTree/*!*/ node) { + if (Enter(node)) { + VisitOptionalList(node.Initializers); + VisitOptionalList(node.Statements); + } + + Exit(node); + } + + internal void Visit(CompoundRightValue/*!*/ node) { + VisitList(node.RightValues); + + if (node.SplattedValue != null) { + node.SplattedValue.Walk(this); + } + } + + internal protected virtual void Walk(ParallelAssignmentExpression/*!*/ node) { + if (Enter(node)) { + node.Left.Walk(this); + } + Exit(node); + } + + + internal protected virtual void Walk(MemberAssignmentExpression/*!*/ node) { + if (Enter(node)) { + node.LeftTarget.Walk(this); + node.Right.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(SimpleAssignmentExpression/*!*/ node) { + if (Enter(node)) { + node.Left.Walk(this); + node.Right.Walk(this); + } + Exit(node); + } + + + internal protected virtual void Walk(ElseIfClause/*!*/ node) { + if (Enter(node)) { + if (node.Condition != null) { + node.Condition.Walk(this); + } + + VisitOptionalList(node.Statements); + } + Exit(node); + } + + internal protected virtual void Walk(WhenClause/*!*/ node) { + if (Enter(node)) { + VisitOptionalList(node.Comparisons); + + if (node.ComparisonArray != null) { + node.ComparisonArray.Walk(this); + } + + VisitOptionalList(node.Statements); + } + Exit(node); + } + + internal protected virtual void Walk(RescueClause/*!*/ node) { + if (Enter(node)) { + if (node.Types != null) { + VisitOptionalList(node.Types); + } + + if (node.Target != null) { + node.Target.Walk(this); + } + + if (node.Statements != null) { + VisitOptionalList(node.Statements); + } + } + Exit(node); + } + + internal protected virtual void Walk(ClassDeclaration/*!*/ node) { + if (Enter(node)) { + if (node.QualifiedName != null) { + node.QualifiedName.Walk(this); + } + + if (node.SuperClass != null) { + node.SuperClass.Walk(this); + } + + node.Body.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(MethodDeclaration/*!*/ node) { + if (Enter(node)) { + if (node.Target != null) { + node.Target.Walk(this); + } + + node.Parameters.Walk(this); + + node.Body.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(ModuleDeclaration/*!*/ node) { + if (Enter(node)) { + if (node.QualifiedName != null) { + node.QualifiedName.Walk(this); + } + + node.Body.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(SingletonDeclaration/*!*/ node) { + if (Enter(node)) { + if (node.QualifiedName != null) { + node.QualifiedName.Walk(this); + } + + node.Singleton.Walk(this); + + node.Body.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(AndExpression/*!*/ node) { + if (Enter(node)) { + node.Left.Walk(this); + node.Right.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(IsDefinedExpression/*!*/ node) { + if (Enter(node)) { + node.Expression.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(BlockExpression/*!*/ node) { + if (Enter(node)) { + VisitList(node.Statements); + } + Exit(node); + } + + internal protected virtual void Walk(ArrayConstructor/*!*/ node) { + if (Enter(node)) { + if (node.Arguments != null) { + node.Arguments.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(CaseExpression/*!*/ node) { + if (Enter(node)) { + if (node.Value != null) { + node.Value.Walk(this); + } + + VisitList(node.WhenClauses); + VisitOptionalList(node.ElseStatements); + } + Exit(node); + } + + internal protected virtual void Walk(ConditionalExpression/*!*/ node) { + if (Enter(node)) { + node.Condition.Walk(this); + node.TrueExpression.Walk(this); + node.FalseExpression.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(ConditionalJumpExpression/*!*/ node) { + if (Enter(node)) { + node.Condition.Walk(this); + node.JumpStatement.Walk(this); + if (node.Value != null) { + node.Value.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(ErrorExpression/*!*/ node) { + Enter(node); + Exit(node); + } + + internal protected virtual void Walk(ForLoopExpression/*!*/ node) { + if (Enter(node)) { + if (node.Block != null) { + node.Block.Walk(this); + } + + if (node.List != null) { + node.List.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(HashConstructor/*!*/ node) { + if (Enter(node)) { + VisitOptionalList(node.Maplets); + VisitOptionalList(node.Expressions); + } + Exit(node); + } + + internal protected virtual void Walk(IfExpression/*!*/ node) { + if (Enter(node)) { + node.Condition.Walk(this); + VisitOptionalList(node.Body); + VisitOptionalList(node.ElseIfClauses); + } + Exit(node); + } + + internal protected virtual void Walk(Literal/*!*/ node) { + Enter(node); + Exit(node); + } + + internal protected virtual void Walk(StringLiteral/*!*/ node) { + Enter(node); + Exit(node); + } + + internal protected virtual void Walk(SymbolLiteral/*!*/ node) { + Enter(node); + Exit(node); + } + + internal protected virtual void Walk(EncodingExpression/*!*/ node) { + Enter(node); + Exit(node); + } + + internal protected virtual void Walk(MethodCall/*!*/ node) { + if (Enter(node)) { + if (node.Target != null) { + node.Target.Walk(this); + } + + if (node.Arguments != null) { + node.Arguments.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(MatchExpression/*!*/ node) { + if (Enter(node)) { + node.Regex.Walk(this); + node.Expression.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(NotExpression/*!*/ node) { + if (Enter(node)) { + node.Expression.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(OrExpression/*!*/ node) { + if (Enter(node)) { + node.Left.Walk(this); + node.Right.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(RangeExpression/*!*/ node) { + if (Enter(node)) { + node.Begin.Walk(this); + node.End.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(RegularExpression/*!*/ node) { + if (Enter(node)) { + VisitList(node.Pattern); + } + Exit(node); + } + + internal protected virtual void Walk(RescueExpression/*!*/ node) { + if (Enter(node)) { + node.GuardedExpression.Walk(this); + node.RescueClauseStatement.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(StringConstructor/*!*/ node) { + if (Enter(node)) { + VisitList(node.Parts); + } + Exit(node); + } + + internal protected virtual void Walk(SelfReference/*!*/ node) { + Enter(node); + Exit(node); + } + + internal protected virtual void Walk(UnlessExpression/*!*/ node) { + if (Enter(node)) { + node.Condition.Walk(this); + + VisitOptionalList(node.Statements); + + if (node.ElseClause != null) { + node.ElseClause.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(SuperCall/*!*/ node) { + if (Enter(node)) { + if (node.Arguments != null) { + node.Arguments.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(WhileLoopExpression/*!*/ node) { + if (Enter(node)) { + if (node.Condition != null) { + node.Condition.Walk(this); + } + + VisitOptionalList(node.Statements); + } + Exit(node); + } + + internal protected virtual void Walk(YieldCall/*!*/ node) { + if (Enter(node)) { + if (node.Arguments != null) { + node.Arguments.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(BreakStatement/*!*/ node) { + if (Enter(node)) { + if (node.Arguments != null) { + node.Arguments.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(NextStatement/*!*/ node) { + if (Enter(node)) { + if (node.Arguments != null) { + node.Arguments.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(RedoStatement/*!*/ node) { + if (Enter(node)) { + if (node.Arguments != null) { + node.Arguments.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(RetryStatement/*!*/ node) { + if (Enter(node)) { + if (node.Arguments != null) { + node.Arguments.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(ReturnStatement/*!*/ node) { + if (Enter(node)) { + if (node.Arguments != null) { + node.Arguments.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(ArrayItemAccess/*!*/ node) { + if (Enter(node)) { + node.Array.Walk(this); + node.Arguments.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(AttributeAccess/*!*/ node) { + if (Enter(node)) { + node.Qualifier.Walk(this); + } + Exit(node); + } + + internal protected virtual void Walk(ConstantVariable/*!*/ node) { + if (Enter(node)) { + if (node.Qualifier != null) { + node.Qualifier.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(ClassVariable/*!*/ node) { + Enter(node); + Exit(node); + } + + internal protected virtual void Walk(CompoundLeftValue/*!*/ node) { + if (Enter(node)) { + VisitList(node.LeftValues); + + if (node.UnsplattedValue != null) { + node.UnsplattedValue.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(GlobalVariable/*!*/ node) { + Enter(node); + Exit(node); + } + + internal protected virtual void Walk(InstanceVariable/*!*/ node) { + Enter(node); + Exit(node); + } + + internal protected virtual void Walk(LocalVariable/*!*/ node) { + Enter(node); + Exit(node); + } + + internal protected virtual void Walk(Placeholder/*!*/ node) { + Enter(node); + Exit(node); + } + + internal protected virtual void Walk(ConditionalStatement/*!*/ node) { + if (Enter(node)) { + node.Condition.Walk(this); + node.Body.Walk(this); + if (node.ElseStatement != null) { + node.ElseStatement.Walk(this); + } + } + Exit(node); + } + + internal protected virtual void Walk(AliasStatement/*!*/ node) { + Enter(node); + Exit(node); + } + + internal protected virtual void Walk(Initializer/*!*/ node) { + if (Enter(node)) { + VisitOptionalList(node.Statements); + } + Exit(node); + } + + internal protected virtual void Walk(Finalizer/*!*/ node) { + if (Enter(node)) { + VisitOptionalList(node.Statements); + } + Exit(node); + } + + internal protected virtual void Walk(UndefineStatement/*!*/ node) { + Enter(node); + Exit(node); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/AstGenerator.cs b/merlin/main/languages/ruby/Ruby/Compiler/AstGenerator.cs new file mode 100644 index 0000000000..6dc1e3324d --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/AstGenerator.cs @@ -0,0 +1,831 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.IO; +using System.Dynamic; +using System.Text; +using System.Threading; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Compiler.Ast { + using Ast = System.Linq.Expressions.Expression; + using AstUtils = Microsoft.Scripting.Ast.Utils; + using MSA = System.Linq.Expressions; + + internal class AstGenerator { + private static int _UniqueId; + + private readonly RubyBinder/*!*/ _binder; + private readonly RubyCompilerOptions/*!*/ _compilerOptions; + private readonly SourceUnit/*!*/ _sourceUnit; + private readonly MSA.SymbolDocumentInfo _document; + private readonly Encoding/*!*/ _encoding; + private readonly Profiler _profiler; + private readonly bool _debugCompiler; + private readonly bool _debugMode; + private readonly bool _traceEnabled; + private readonly bool _savingToDisk; + + internal AstGenerator(RubyBinder/*!*/ binder, RubyCompilerOptions/*!*/ options, SourceUnit/*!*/ sourceUnit, Encoding/*!*/ encoding, + bool debugCompiler, bool debugMode, bool traceEnabled, bool profilerEnabled, bool savingToDisk) { + + Assert.NotNull(binder, options, encoding); + _binder = binder; + _compilerOptions = options; + _debugCompiler = debugCompiler; + _debugMode = debugMode; + _traceEnabled = traceEnabled; + _sourceUnit = sourceUnit; + _document = sourceUnit.Document; + _encoding = encoding; + _profiler = profilerEnabled ? Profiler.Instance : null; + _savingToDisk = savingToDisk; + } + + public RubyCompilerOptions/*!*/ CompilerOptions { + get { return _compilerOptions; } + } + + public bool DebugMode { + get { return _debugMode; } + } + + public Profiler Profiler { + get { return _profiler; } + } + + public bool DebugCompiler { + get { return _debugCompiler; } + } + + public bool TraceEnabled { + get { return _traceEnabled; } + } + + public bool SavingToDisk { + get { return _savingToDisk; } + } + + public SourceUnit/*!*/ SourceUnit { + get { return _sourceUnit; } + } + + public Encoding Encoding { + get { return _encoding; } + } + + internal ActionBinder Binder { + get { + return _binder; + } + } + + #region Lexical Scopes + + public abstract class LexicalScope { + public LexicalScope Parent; + } + + public sealed class LoopScope : LexicalScope { + private readonly MSA.Expression/*!*/ _redoVariable; + private readonly MSA.Expression/*!*/ _resultVariable; + private readonly MSA.LabelTarget/*!*/ _breakLabel; + private readonly MSA.LabelTarget/*!*/ _continueLabel; + public LoopScope _parentLoop; + + public MSA.Expression/*!*/ RedoVariable { + get { return _redoVariable; } + } + + public MSA.Expression/*!*/ ResultVariable { + get { return _resultVariable; } + } + + public MSA.LabelTarget/*!*/ BreakLabel { + get { return _breakLabel; } + } + + public MSA.LabelTarget/*!*/ ContinueLabel { + get { return _continueLabel; } + } + + public LoopScope ParentLoop { + get { return _parentLoop; } + set { _parentLoop = value; } + } + + public LoopScope(MSA.Expression/*!*/ redoVariable, MSA.Expression/*!*/ resultVariable, MSA.LabelTarget/*!*/ breakLabel, MSA.LabelTarget/*!*/ continueLabel) { + Assert.NotNull(redoVariable, resultVariable, breakLabel, continueLabel); + _redoVariable = redoVariable; + _resultVariable = resultVariable; + _breakLabel = breakLabel; + _continueLabel = continueLabel; + } + } + + public sealed class RescueScope : LexicalScope { + private readonly MSA.Expression/*!*/ _retryingVariable; + private readonly MSA.LabelTarget/*!*/ _breakLabel; + private readonly MSA.LabelTarget/*!*/ _continueLabel; + + public RescueScope _parentRescue; + + public MSA.Expression/*!*/ RetryingVariable { + get { return _retryingVariable; } + } + + public RescueScope ParentRescue { + get { return _parentRescue; } + set { _parentRescue = value; } + } + + public MSA.LabelTarget/*!*/ BreakLabel { + get { return _breakLabel; } + } + + public MSA.LabelTarget/*!*/ ContinueLabel { + get { return _continueLabel; } + } + + public RescueScope(MSA.Expression/*!*/ retryingVariable, MSA.LabelTarget/*!*/ breakLabel, MSA.LabelTarget/*!*/ continueLabel) { + Assert.NotNull(retryingVariable, breakLabel, continueLabel); + _retryingVariable = retryingVariable; + _breakLabel = breakLabel; + _continueLabel = continueLabel; + } + } + + public class VariableScope : LexicalScope { + private readonly ScopeBuilder/*!*/ _builder; + private readonly MSA.Expression/*!*/ _selfVariable; + private readonly MSA.Expression/*!*/ _runtimeScopeVariable; + private VariableScope _parentVariableScope; + + public ScopeBuilder/*!*/ Builder { + get { return _builder; } + } + + public MSA.Expression/*!*/ SelfVariable { + get { return _selfVariable; } + } + + public MSA.Expression/*!*/ RuntimeScopeVariable { + get { return _runtimeScopeVariable; } + } + + public VariableScope ParentVariableScope { + get { return _parentVariableScope; } + set { _parentVariableScope = value; } + } + + public VariableScope(ScopeBuilder/*!*/ locals, MSA.Expression/*!*/ selfVariable, MSA.Expression/*!*/ runtimeScopeVariable) { + Assert.NotNull(selfVariable, runtimeScopeVariable); + _builder = locals; + _runtimeScopeVariable = runtimeScopeVariable; + _selfVariable = selfVariable; + } + } + + public abstract class FrameScope : VariableScope { + // A unique id of the scope per app-domain. Used for caching calls in dynamic sites. + private readonly int _uniqueId; + + private BlockScope _parentBlock; + private RescueScope _parentRescue; + private LoopScope _parentLoop; + private MSA.LabelTarget _returnLabel; + + public int UniqueId { + get { return _uniqueId; } + } + + public BlockScope ParentBlock { get { return _parentBlock; } set { _parentBlock = value; } } + public RescueScope ParentRescue { get { return _parentRescue; } set { _parentRescue = value; } } + public LoopScope ParentLoop { get { return _parentLoop; } set { _parentLoop = value; } } + + public FrameScope(ScopeBuilder/*!*/ builder, MSA.Expression/*!*/ selfVariable, MSA.Expression/*!*/ runtimeScopeVariable) + : base(builder, selfVariable, runtimeScopeVariable) { + _uniqueId = Interlocked.Increment(ref _UniqueId); + } + + internal MSA.LabelTarget/*!*/ ReturnLabel { + get { + if (_returnLabel == null) { + _returnLabel = MSA.Expression.Label(typeof(object)); + } + return _returnLabel; + } + } + + internal MSA.Expression/*!*/ AddReturnTarget(MSA.Expression/*!*/ expression) { + if (expression.Type != typeof(object)) { + expression = AstFactory.Box(expression); + } + if (_returnLabel != null) { + expression = Ast.Label(_returnLabel, expression); + _returnLabel = null; + } + return expression; + } + } + + public sealed class BlockScope : FrameScope { + private readonly MSA.Expression/*!*/ _bfcVariable; + private readonly MSA.LabelTarget/*!*/ _redoLabel; + + public MSA.Expression/*!*/ BfcVariable { + get { return _bfcVariable; } + } + + public MSA.LabelTarget/*!*/ RedoLabel { + get { return _redoLabel; } + } + + public BlockScope(ScopeBuilder/*!*/ builder, MSA.Expression/*!*/ selfVariable, MSA.Expression/*!*/ runtimeScopeVariable, + MSA.Expression/*!*/ bfcVariable, MSA.LabelTarget/*!*/ redoLabel) + : base(builder, selfVariable, runtimeScopeVariable) { + Assert.NotNull(bfcVariable, redoLabel); + _bfcVariable = bfcVariable; + _redoLabel = redoLabel; + } + } + + public sealed class MethodScope : FrameScope { + private readonly MSA.Expression _blockVariable; + private readonly MSA.Expression/*!*/ _rfcVariable; + private readonly MSA.Expression _currentMethodVariable; + private readonly string _methodName; + private readonly Parameters _parameters; + private MethodScope _parentMethod; + + public MSA.Expression BlockVariable { + get { return _blockVariable; } + } + + public MSA.Expression/*!*/ RfcVariable { + get { return _rfcVariable; } + } + + public MethodScope ParentMethod { + get { return _parentMethod; } + set { _parentMethod = value; } + } + + // TODO: super call + // non-null if !IsTopLevelCode + public string MethodName { + get { return _methodName; } + } + + // TODO: super call + // non-null if !IsTopLevelCode + public Parameters Parameters { + get { return _parameters; } + } + + // non-null if !IsTopLevelCode + public MSA.Expression CurrentMethodVariable { + get { return _currentMethodVariable; } + } + + public bool IsTopLevelCode { + get { return _parentMethod == null; } + } + + public MethodScope( + ScopeBuilder/*!*/ builder, + MSA.Expression/*!*/ selfVariable, + MSA.Expression/*!*/ runtimeScopeVariable, + MSA.Expression blockVariable, + MSA.Expression/*!*/ rfcVariable, + MSA.Expression currentMethodVariable, + string methodName, + Parameters parameters) + : base(builder, selfVariable, runtimeScopeVariable) { + + Assert.NotNull(rfcVariable); + _blockVariable = blockVariable; + _rfcVariable = rfcVariable; + _methodName = methodName; + _parameters = parameters; + _currentMethodVariable = currentMethodVariable; + } + } + + public sealed class ModuleScope : VariableScope { + private readonly bool _isSingleton; + private ModuleScope _parentModule; + + public ModuleScope ParentModule { + get { return _parentModule; } + set { _parentModule = value; } + } + + public bool IsSingleton { + get { return _isSingleton; } + } + + public ModuleScope(ScopeBuilder/*!*/ builder, MSA.Expression/*!*/ selfVariable, MSA.Expression/*!*/ runtimeScopeVariable, bool isSingleton) + : base(builder, selfVariable, runtimeScopeVariable) { + _isSingleton = isSingleton; + } + } + + private LexicalScope/*!*/ _currentElement; + private MethodScope/*!*/ _currentMethod; + private BlockScope _currentBlock; + private LoopScope _currentLoop; + private RescueScope _currentRescue; + private VariableScope _currentVariableScope; + private ModuleScope _currentModule; + + // inner-most module (not reset by method definition): + public ModuleScope CurrentModule { + get { return _currentModule; } + } + + // inner-most method frame: + public MethodScope/*!*/ CurrentMethod { + get { + Debug.Assert(_currentMethod != null); + return _currentMethod; + } + } + + // inner-most frame scope (block or method): + public FrameScope/*!*/ CurrentFrame { + get { return (FrameScope)CurrentBlock ?? CurrentMethod; } + } + + // inner-most block scope within the current method frame: + public BlockScope CurrentBlock { + get { return _currentBlock; } + } + + // inner-most loop within the current frame (block or method): + public LoopScope CurrentLoop { + get { return _currentLoop; } + } + + // inner-most rescue within the current frame (block or method): + public RescueScope CurrentRescue { + get { return _currentRescue; } + } + + // RFC variable of the current method frame: + public MSA.Expression/*!*/ CurrentRfcVariable { + get { return CurrentMethod.RfcVariable; } + } + + // "self" variable of the current variable scope: + public MSA.Expression/*!*/ CurrentSelfVariable { + get { return _currentVariableScope.SelfVariable; } + } + + // runtime scope variable of the current variable scope: + public MSA.Expression/*!*/ CurrentScopeVariable { + get { return _currentVariableScope.RuntimeScopeVariable; } + } + + // inner-most scope builder: + public ScopeBuilder/*!*/ CurrentScope { + get { return _currentVariableScope.Builder; } + } + + public ModuleScope GetCurrentNonSingletonModule() { + ModuleScope scope = CurrentModule; + while (scope != null && scope.IsSingleton) { + scope = scope.ParentModule; + } + return scope; + } + + #endregion + + #region Entering and Leaving Lexical Scopes + + public void EnterLoop(MSA.Expression/*!*/ redoVariable, MSA.Expression/*!*/ resultVariable, MSA.LabelTarget/*!*/ breakLabel, MSA.LabelTarget/*!*/ continueLabel) { + Assert.NotNull(redoVariable, resultVariable, breakLabel, continueLabel); + + LoopScope loop = new LoopScope(redoVariable, resultVariable, breakLabel, continueLabel); + + loop.Parent = _currentElement; + loop.ParentLoop = _currentLoop; + + _currentElement = _currentLoop = loop; + } + + public void LeaveLoop() { + Debug.Assert(_currentElement == _currentLoop); + _currentElement = _currentLoop.Parent; + _currentLoop = _currentLoop.ParentLoop; + } + + public void EnterRescueClause(MSA.Expression/*!*/ retryingVariable, MSA.LabelTarget/*!*/ breakLabel, MSA.LabelTarget/*!*/ continueLabel) { + Assert.NotNull(retryingVariable, breakLabel, continueLabel); + + RescueScope body = new RescueScope(retryingVariable, breakLabel, continueLabel); + + body.Parent = _currentElement; + body.ParentRescue = _currentRescue; + + _currentElement = _currentRescue = body; + } + + public void LeaveRescueClause() { + Debug.Assert(_currentElement == _currentRescue); + _currentElement = _currentRescue.Parent; + _currentRescue = _currentRescue.ParentRescue; + } + + public void EnterBlockDefinition( + ScopeBuilder/*!*/ locals, + MSA.Expression/*!*/ bfcVariable, + MSA.Expression/*!*/ selfVariable, + MSA.Expression/*!*/ runtimeScopeVariable, + MSA.LabelTarget/*!*/ redoLabel) { + Assert.NotNull(locals, bfcVariable, selfVariable); + Assert.NotNull(redoLabel); + + BlockScope block = new BlockScope(locals, selfVariable, runtimeScopeVariable, bfcVariable, redoLabel); + block.Parent = _currentElement; + block.ParentRescue = _currentRescue; + block.ParentLoop = _currentLoop; + block.ParentBlock = _currentBlock; + block.ParentVariableScope = _currentVariableScope; + + _currentElement = block; + _currentRescue = null; + _currentLoop = null; + _currentBlock = block; + _currentVariableScope = block; + } + + public void LeaveBlockDefinition() { + Debug.Assert(_currentElement == _currentBlock); + BlockScope oldBlock = _currentBlock; + + _currentElement = oldBlock.Parent; + _currentRescue = oldBlock.ParentRescue; + _currentLoop = oldBlock.ParentLoop; + _currentVariableScope = oldBlock.ParentVariableScope; + _currentBlock = oldBlock.ParentBlock; + } + + public void EnterMethodDefinition( + ScopeBuilder/*!*/ locals, + MSA.Expression/*!*/ selfParameter, + MSA.Expression/*!*/ runtimeScopeVariable, + MSA.Expression blockParameter, + MSA.Expression/*!*/ rfcVariable, + MSA.Expression currentMethodVariable, + string/*!*/ methodName, + Parameters parameters) { + Assert.NotNull(locals, selfParameter, runtimeScopeVariable, rfcVariable); + + MethodScope method = new MethodScope( + locals, + selfParameter, + runtimeScopeVariable, + blockParameter, + rfcVariable, + currentMethodVariable, + methodName, + parameters + ); + + method.Parent = _currentElement; + method.ParentRescue = _currentRescue; + method.ParentLoop = _currentLoop; + method.ParentBlock = _currentBlock; + method.ParentVariableScope = _currentVariableScope; + method.ParentMethod = _currentMethod; + + _currentElement = method; + _currentRescue = null; + _currentLoop = null; + _currentBlock = null; + _currentVariableScope = method; + _currentMethod = method; + } + + public void LeaveMethodDefinition() { + Debug.Assert(_currentElement == _currentMethod); + MethodScope oldMethod = _currentMethod; + + _currentElement = oldMethod.Parent; + _currentRescue = oldMethod.ParentRescue; + _currentLoop = oldMethod.ParentLoop; + _currentBlock = oldMethod.ParentBlock; + _currentVariableScope = oldMethod.ParentVariableScope; + _currentMethod = oldMethod.ParentMethod; + } + + public void EnterModuleDefinition( + ScopeBuilder/*!*/ locals, + MSA.Expression/*!*/ selfVariable, + MSA.Expression/*!*/ runtimeScopeVariable, + bool isSingleton) { + Assert.NotNull(locals, selfVariable, runtimeScopeVariable); + + ModuleScope module = new ModuleScope(locals, selfVariable, runtimeScopeVariable, isSingleton); + + module.Parent = _currentElement; + module.ParentVariableScope = _currentVariableScope; + module.ParentModule = _currentModule; + + _currentElement = module; + _currentVariableScope = module; + _currentModule = module; + } + + public void LeaveModuleDefinition() { + Debug.Assert(_currentElement == _currentModule); + ModuleScope oldModule = _currentModule; + + _currentElement = oldModule.Parent; + _currentVariableScope = oldModule.ParentVariableScope; + _currentModule = oldModule.ParentModule; + } + + public void EnterSourceUnit( + ScopeBuilder/*!*/ locals, + MSA.Expression/*!*/ selfParameter, + MSA.Expression/*!*/ runtimeScopeVariable, + MSA.Expression blockParameter, + MSA.Expression/*!*/ rfcVariable, + MSA.Expression currentMethodVariable, + string methodName, + Parameters parameters) { + Assert.NotNull(locals, selfParameter, runtimeScopeVariable, rfcVariable); + + Debug.Assert(_currentElement == null && _currentLoop == null && _currentRescue == null && + _currentVariableScope == null && _currentModule == null && _currentBlock == null && _currentMethod == null); + + EnterMethodDefinition( + locals, + selfParameter, + runtimeScopeVariable, + blockParameter, + rfcVariable, + currentMethodVariable, + methodName, + parameters); + } + + public void LeaveSourceUnit() { + Debug.Assert(_currentElement == _currentMethod && _currentVariableScope == _currentMethod); + Debug.Assert(_currentLoop == null && _currentRescue == null); + Debug.Assert(_currentBlock == null); + + _currentElement = null; + _currentMethod = null; + _currentVariableScope = null; + } + + #endregion + + /// + /// Makes a read of the current method's block parameter. + /// Returns Null constant in top-level code. + /// + internal MSA.Expression/*!*/ MakeMethodBlockParameterRead() { + Debug.Assert(CurrentMethod != null); + + if (CurrentMethod.BlockVariable != null) { + return CurrentMethod.BlockVariable; + } else { + return Ast.Constant(null, typeof(Proc)); + } + } + + internal List/*!*/ TranformExpressions(List/*!*/ arguments) { + Assert.NotNull(arguments); + return TranformExpressions(arguments, new List(arguments.Count)); + } + + internal List/*!*/ TranformExpressions(List/*!*/ arguments, List/*!*/ result) { + Assert.NotNullItems(arguments); + Assert.NotNull(result); + + foreach (Expression arg in arguments) { + result.Add(arg.TransformRead(this)); + } + + return result; + } + + internal MSA.Expression/*!*/ TransformStatements(List/*!*/ statements, ResultOperation resultOperation) { + return TransformStatements(null, statements, null, resultOperation); + } + + internal MSA.Expression/*!*/ TransformStatements(MSA.Expression prologue, List/*!*/ statements, ResultOperation resultOperation) { + return TransformStatements(prologue, statements, null, resultOperation); + } + + internal MSA.Expression/*!*/ TransformStatements(MSA.Expression prologue, List/*!*/ statements, MSA.Expression epilogue, + ResultOperation resultOperation) { + + Assert.NotNullItems(statements); + + int count = statements.Count + (prologue != null ? 1 : 0) + (epilogue != null ? 1 : 0); + + if (count == 0) { + + if (resultOperation.IsIgnore) { + return Ast.Empty(); + } else if (resultOperation.Variable != null) { + return Ast.Assign(resultOperation.Variable, Ast.Constant(null, resultOperation.Variable.Type)); + } else { + return Ast.Return(CurrentFrame.ReturnLabel, Ast.Constant(null)); + } + + } else if (count == 1) { + if (prologue != null) { + return prologue; + } + + if (epilogue != null) { + return epilogue; + } + + if (resultOperation.IsIgnore) { + return statements[0].Transform(this); + } else { + return statements[0].TransformResult(this, resultOperation); + } + + } else { + var result = new MSA.Expression[count + 1]; + int resultIndex = 0; + + if (prologue != null) { + result[resultIndex++] = prologue; + } + + // transform all but the last statement if it is an expression stmt: + for (int i = 0; i < statements.Count - 1; i++) { + result[resultIndex++] = statements[i].Transform(this); + } + + if (statements.Count > 0) { + if (resultOperation.IsIgnore) { + result[resultIndex++] = statements[statements.Count - 1].Transform(this); + } else { + result[resultIndex++] = statements[statements.Count - 1].TransformResult(this, resultOperation); + } + } + + if (epilogue != null) { + result[resultIndex++] = epilogue; + } + + result[resultIndex++] = MSA.Expression.Empty(); + Debug.Assert(resultIndex == result.Length); + + return Ast.Block(new ReadOnlyCollection(result)); + } + } + + internal MSA.Expression/*!*/ TransformStatementsToExpression(List statements) { + if (statements == null || statements.Count == 0) { + return Ast.Constant(null); + } + + if (statements.Count == 1) { + return statements[0].TransformRead(this); + } + + var result = new MSA.Expression[statements.Count]; + for (int i = 0; i < result.Length - 1; i++) { + result[i] = statements[i].Transform(this); + } + result[result.Length - 1] = statements[statements.Count - 1].TransformRead(this); + + return Ast.Block(new ReadOnlyCollection(result)); + } + + internal List/*!*/ TransformMapletsToExpressions(IList/*!*/ maplets) { + Assert.NotNullItems(maplets); + return TransformMapletsToExpressions(maplets, new List(maplets.Count * 2)); + } + + internal List/*!*/ TransformMapletsToExpressions(IList/*!*/ maplets, List/*!*/ result) { + Assert.NotNullItems(maplets); + Assert.NotNull(result); + + foreach (Maplet entry in maplets) { + result.Add(entry.Key.TransformRead(this)); + result.Add(entry.Value.TransformRead(this)); + } + + return result; + } + + public MSA.Expression/*!*/ TransformToHashConstructor(IList/*!*/ maplets) { + return MakeHashOpCall(TransformMapletsToExpressions(maplets)); + } + + internal MSA.Expression/*!*/ MakeHashOpCall(List/*!*/ expressions) { + return Methods.MakeHash.OpCall(CurrentScopeVariable, AstUtils.NewArrayHelper(typeof(object), expressions)); + } + + internal static bool CanAssign(Type/*!*/ to, Type/*!*/ from) { + return to.IsAssignableFrom(from) && (to.IsValueType == from.IsValueType); + } + + internal MSA.Expression/*!*/ AddReturnTarget(MSA.Expression/*!*/ expression) { + return CurrentFrame.AddReturnTarget(expression); + } + + internal MSA.LabelTarget/*!*/ ReturnLabel { + get { return CurrentFrame.ReturnLabel; } + } + + internal MSA.Expression/*!*/ Return(MSA.Expression/*!*/ expression) { + MSA.LabelTarget returnLabel = ReturnLabel; + if (returnLabel.Type != typeof(void) && expression.Type == typeof(void)) { + expression = Ast.Block(expression, Ast.Constant(null, typeof(object))); + } else if (returnLabel.Type != expression.Type) { + if (!CanAssign(returnLabel.Type, expression.Type)) { + // Add conversion step to the AST + expression = Ast.Convert(expression, returnLabel.Type); + } + } + return Ast.Return(returnLabel, expression); + } + + internal MSA.Expression/*!*/ AddDebugInfo(MSA.Expression/*!*/ expression, SourceSpan location) { + if (_document == null || !location.IsValid) { + return expression; + } + return MSA.Expression.DebugInfo(expression, _document, + location.Start.Line, location.Start.Column, location.End.Line, location.End.Column); + } + + internal MSA.Expression/*!*/ DebugMarker(string/*!*/ marker) { + return _debugCompiler ? Methods.X.OpCall(Ast.Constant(marker)) : (MSA.Expression)Ast.Empty(); + } + + internal MSA.Expression/*!*/ DebugMark(MSA.Expression/*!*/ expression, string/*!*/ marker) { + return _debugCompiler ? AstFactory.Block(Methods.X.OpCall(Ast.Constant(marker)), expression) : expression; + } + + internal virtual void TraceCallSite(Expression/*!*/ expression, MSA.DynamicExpression/*!*/ callSite) { + } + + internal string/*!*/ EncodeMethodName(string/*!*/ name, SourceSpan location) { + string encoded = name; + + // TODO: hack + // encodes line number, file name into the method name +#if !SILVERLIGHT && !DEBUG + if (!_debugMode) +#endif + { + string fileName = _sourceUnit.HasPath ? Path.GetFileName(_sourceUnit.Path) : String.Empty; + encoded = String.Format("{0};{1};{2}", encoded, fileName, location.Start.Line); + } + + return String.IsNullOrEmpty(encoded) ? RubyExceptionData.TopLevelMethodName : encoded; + } + + internal MSA.Expression/*!*/ TryCatchAny(MSA.Expression/*!*/ tryBody, MSA.Expression/*!*/ catchBody) { + var variable = CurrentScope.DefineHiddenVariable("#value", tryBody.Type); + + return + Ast.Block( + Ast.TryCatch( + Ast.Assign(variable, tryBody), + Ast.Catch(typeof(Exception), + Ast.Assign(variable, catchBody) + ) + ), + variable + ); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Errors.cs b/merlin/main/languages/ruby/Ruby/Compiler/Errors.cs new file mode 100644 index 0000000000..5f30bb9930 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Errors.cs @@ -0,0 +1,108 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Utils; +using System.Diagnostics; + +namespace IronRuby.Compiler { + // TODO: tests + public struct ErrorInfo { + public readonly int Code; + public readonly string/*!*/ ResourceId; + + public ErrorInfo(int code, string/*!*/ id) { + Assert.NotNull(id); + Code = code; + ResourceId = id; + } + + // TODO: load from resource + public string/*!*/ GetMessage() { + return ResourceId; + } + + public string/*!*/ GetMessage(params object[] args) { + return String.Format(GetMessage(), args); + } + } + + public static class Errors { + private const int Tokenizer = 0x1000; + + public static readonly ErrorInfo IllegalOctalDigit = new ErrorInfo(Tokenizer + 0, "Illegal octal digit"); + public static readonly ErrorInfo NumericLiteralWithoutDigits = new ErrorInfo(Tokenizer + 1, "Numeric literal without digits"); + public static readonly ErrorInfo TrailingUnderscoreInNumber = new ErrorInfo(Tokenizer + 2, "Trailing `_' in number"); + public static readonly ErrorInfo TrailingEInNumber = new ErrorInfo(Tokenizer + 3, "Trailing `e' in number"); + public static readonly ErrorInfo TrailingPlusInNumber = new ErrorInfo(Tokenizer + 4, "Trailing `+' in number"); + public static readonly ErrorInfo TrailingMinusInNumber = new ErrorInfo(Tokenizer + 5, "Trailing `-' in number"); + public static readonly ErrorInfo FloatOutOfRange = new ErrorInfo(Tokenizer + 6, "Float {0} out of range"); + public static readonly ErrorInfo NoFloatingLiteral = new ErrorInfo(Tokenizer + 7, "No . floating literal anymore; put 0 before dot"); + public static readonly ErrorInfo InvalidGlobalVariableName = new ErrorInfo(Tokenizer + 8, "Identifier {0} is not valid"); + public static readonly ErrorInfo CannotAssignTo = new ErrorInfo(Tokenizer + 9, "Can't assign to {0}"); + public static readonly ErrorInfo FormalArgumentIsConstantVariable = new ErrorInfo(Tokenizer + 10, "Formal argument cannot be a constant"); + public static readonly ErrorInfo FormalArgumentIsInstanceVariable = new ErrorInfo(Tokenizer + 11, "Formal argument cannot be an instance variable"); + public static readonly ErrorInfo FormalArgumentIsGlobalVariable = new ErrorInfo(Tokenizer + 12, "Formal argument cannot be a global variable"); + public static readonly ErrorInfo FormalArgumentIsClassVariable = new ErrorInfo(Tokenizer + 13, "Formal argument cannot be a class variable"); + public static readonly ErrorInfo InvalidInstanceVariableName = new ErrorInfo(Tokenizer + 14, "`@{0}' is not allowed as an instance variable name"); + public static readonly ErrorInfo InvalidClassVariableName = new ErrorInfo(Tokenizer + 15, "`@@{0}' is not allowed as a class variable name"); + public static readonly ErrorInfo InvalidCharacterInExpression = new ErrorInfo(Tokenizer + 16, "Invalid character '{0}' in expression"); + + public static readonly ErrorInfo MatchGroupReferenceOverflow = new ErrorInfo(Tokenizer + 30, "Match group reference ${0} doesn't fit into Fixnum"); + public static readonly ErrorInfo MatchGroupReferenceReadOnly = new ErrorInfo(Tokenizer + 31, "Can't set variable ${0}"); + public static readonly ErrorInfo CannotAliasGroupMatchVariable = new ErrorInfo(Tokenizer + 32, "Can't make alias for number variable"); + public static readonly ErrorInfo DuplicateParameterName = new ErrorInfo(Tokenizer + 33, "duplicate parameter name"); + + public static readonly ErrorInfo IncompleteCharacter = new ErrorInfo(Tokenizer + 37, "Incomplete character syntax"); + public static readonly ErrorInfo UnterminatedEmbeddedDocument = new ErrorInfo(Tokenizer + 38, "Embedded document meets end of file"); + public static readonly ErrorInfo UnterminatedString = new ErrorInfo(Tokenizer + 39, "Unterminated quoted string meets end of file"); + public static readonly ErrorInfo UnterminatedQuotedString = new ErrorInfo(Tokenizer + 40, "Unterminated quoted string meets end of file"); + public static readonly ErrorInfo UnterminatedHereDocIdentifier = new ErrorInfo(Tokenizer + 41, "Unterminated here document identifier"); + public static readonly ErrorInfo UnterminatedHereDoc = new ErrorInfo(Tokenizer + 42, "can't find string \"{0}\" anywhere before end-of-file"); + + public static readonly ErrorInfo UnknownQuotedStringType = new ErrorInfo(Tokenizer + 50, "Unknown type of quoted string"); + public static readonly ErrorInfo UnknownRegexOption = new ErrorInfo(Tokenizer + 51, "Unknown RegEx option '{0}'"); + public static readonly ErrorInfo TooLargeUnicodeCodePoint = new ErrorInfo(Tokenizer + 52, "Invalid Unicode codepoint (too large)"); + public static readonly ErrorInfo InvalidEscapeCharacter = new ErrorInfo(Tokenizer + 53, "Invalid escape character syntax"); + public static readonly ErrorInfo NullCharacterInSymbol = new ErrorInfo(Tokenizer + 54, "Symbol cannot contain '\\0'"); + public static readonly ErrorInfo EmptySymbolLiteral = new ErrorInfo(Tokenizer + 55, "empty symbol literal"); + + public static readonly ErrorInfo FileInitializerInMethod = new ErrorInfo(Tokenizer + 60, "BEGIN in method"); + public static readonly ErrorInfo FileFinalizerInMethod = new ErrorInfo(Tokenizer + 61, "END in method; use at_exit"); + public static readonly ErrorInfo ModuleNameNotConstant = new ErrorInfo(Tokenizer + 66, "Class/module name must be a constant"); + public static readonly ErrorInfo ConstantReassigned = new ErrorInfo(Tokenizer + 67, "Constant re-assignment"); + + // level 1 warnings: + private const int WarningLevel1 = 0x2000; + internal const int RuntimeWarning = WarningLevel1; + public static readonly ErrorInfo ParenthesizeArguments = new ErrorInfo(WarningLevel1 + 1, "parenthesize argument(s) for future version"); + public static readonly ErrorInfo WhitespaceBeforeArgumentParentheses = new ErrorInfo(WarningLevel1 + 2, "don't put space before argument parentheses"); + public static readonly ErrorInfo InvalidCharacterSyntax = new ErrorInfo(WarningLevel1 + 3, "invalid character syntax; use ?\\{0}"); + + // level 2 warnings: + private const int WarningLevel2 = 0x3000; + internal const int RuntimeVerboseWarning = WarningLevel2; + public static readonly ErrorInfo InterpretedAsGroupedExpression = new ErrorInfo(WarningLevel2 + 1, "(...) interpreted as grouped expression"); + public static readonly ErrorInfo AmbiguousFirstArgument = new ErrorInfo(WarningLevel2 + 2, "Ambiguous first argument; put parentheses or even spaces"); + + // TODO: level 1? + public static readonly ErrorInfo AmpersandInterpretedAsProcArgument = new ErrorInfo(WarningLevel2 + 3, "`&' interpreted as argument prefix"); + public static readonly ErrorInfo AmpersandInVoidContext = new ErrorInfo(WarningLevel2 + 4, "Useless use of & in void context"); + + internal static bool IsVerboseWarning(int errorCode) { + return errorCode >= WarningLevel2; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/GenerateReflectionCache.cmd b/merlin/main/languages/ruby/Ruby/Compiler/GenerateReflectionCache.cmd new file mode 100644 index 0000000000..8caf62aceb --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/GenerateReflectionCache.cmd @@ -0,0 +1 @@ +"%MERLIN_ROOT%\Bin\Debug\ClassInitGenerator" /refcache /out:%~dp0\ReflectionCache.Generated.cs \ No newline at end of file diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Generation/ClsTypeEmitter.cs b/merlin/main/languages/ruby/Ruby/Compiler/Generation/ClsTypeEmitter.cs new file mode 100644 index 0000000000..0dacfbda07 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Generation/ClsTypeEmitter.cs @@ -0,0 +1,829 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using System.Dynamic.Binders; +using Microsoft.Scripting; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Runtime.CompilerServices; + +namespace IronRuby.Compiler.Generation { + + public abstract partial class ClsTypeEmitter { + class SpecialNames { + private readonly Dictionary/*!*/>/*!*/ _specialNames; + + internal SpecialNames() { + _specialNames = new Dictionary>(); + } + + internal void SetSpecialName(string/*!*/ specialName, List/*!*/ names) { + _specialNames[specialName] = names; + } + + internal void SetSpecialName(string/*!*/ name) { + List names = new List(1); + names.Add(name); + _specialNames[name] = names; + } + + internal IEnumerable GetBaseName(MethodInfo/*!*/ mi) { + string newName; + if (mi.Name.StartsWith(BaseMethodPrefix)) { + newName = mi.Name.Substring(BaseMethodPrefix.Length); + } else if (mi.Name.StartsWith(FieldGetterPrefix)) { + newName = mi.Name.Substring(FieldGetterPrefix.Length); + } else if (mi.Name.StartsWith(FieldSetterPrefix)) { + newName = mi.Name.Substring(FieldSetterPrefix.Length); + } else { + throw new InvalidOperationException(); + } + + Debug.Assert(_specialNames.ContainsKey(newName)); + + return _specialNames[newName]; + } + } + + public const string VtableNamesField = "#VTableNames#"; + public const string BaseMethodPrefix = "#base#"; + public const string FieldGetterPrefix = "#field_get#", FieldSetterPrefix = "#field_set#"; + + private static bool ShouldOverrideVirtual(MethodInfo/*!*/ mi) { + return true; + } + + private static bool CanOverrideMethod(MethodInfo/*!*/ mi) { +#if !SILVERLIGHT + return true; +#else + // can only override the method if it is not SecurityCritical + return mi.GetCustomAttributes(typeof(System.Security.SecurityCriticalAttribute), false).Length == 0; +#endif + } + + protected abstract MethodInfo NonInheritedValueHelper(); + protected abstract MethodInfo NonInheritedMethodHelper(); + protected abstract MethodInfo EventHelper(); + protected abstract MethodInfo GetFastConvertMethod(Type toType); + protected abstract MethodInfo GetGenericConvertMethod(Type toType); + protected abstract MethodInfo MissingInvokeMethodException(); + protected abstract MethodInfo ConvertToDelegate(); + + protected abstract void EmitImplicitContext(ILGen il); + protected abstract void EmitMakeCallAction(string name, int nargs, bool isList); + protected abstract void EmitClassObjectFromInstance(ILGen il); + protected abstract void EmitPropertyGet(ILGen il, MethodInfo mi, string name, LocalBuilder callTarget); + protected abstract void EmitPropertySet(ILGen il, MethodInfo mi, string name, LocalBuilder callTarget); + + protected abstract bool TryGetName(Type clrType, MethodInfo mi, out string name); + protected abstract bool TryGetName(Type clrType, EventInfo ei, MethodInfo mi, out string name); + protected abstract bool TryGetName(Type clrType, PropertyInfo pi, MethodInfo mi, out string name); + protected abstract Type/*!*/[]/*!*/ MakeSiteSignature(int nargs); + protected abstract Type/*!*/ ContextType { get; } + + /// + /// Gets the position for the parameter which we are overriding. + /// + /// + /// + /// + /// + private int GetOriginalIndex(ParameterInfo/*!*/[]/*!*/ pis, ParameterInfo/*!*/[]/*!*/ overrideParams, int i) { + if (pis.Length == 0 || pis[0].ParameterType != ContextType) { + return i - (overrideParams.Length - pis.Length); + } + + // context & cls are swapped, context comes first. + if (i == 1) return -1; + if (i == 0) return 0; + + return i - (overrideParams.Length - pis.Length); + } + + private void CallBaseConstructor(ConstructorInfo parentConstructor, ParameterInfo[] pis, ParameterInfo[] overrideParams, ILGen il) { + il.EmitLoadArg(0); +#if DEBUG + int lastIndex = -1; +#endif + for (int i = 0; i < overrideParams.Length; i++) { + int index = GetOriginalIndex(pis, overrideParams, i); + +#if DEBUG + // we insert a new parameter (the class) but the parameters should + // still remain in the same order after the extra parameter is removed. + if (index >= 0) { + Debug.Assert(index > lastIndex); + lastIndex = index; + } +#endif + if (index >= 0) { + il.EmitLoadArg(i + 1); + } + } + il.Emit(OpCodes.Call, parentConstructor); + il.Emit(OpCodes.Ret); + } + + private ILGen _cctor; + private TypeBuilder _tb; + private Type _baseType; + private int _site; + private SpecialNames _specialNames; + + protected ClsTypeEmitter(TypeBuilder tb) { + _tb = tb; + _baseType = tb.BaseType; + _specialNames = new SpecialNames(); + } + + protected ILGen GetCCtor() { + if (_cctor == null) { + ConstructorBuilder cctor = _tb.DefineTypeInitializer(); + _cctor = CreateILGen(cctor.GetILGenerator()); + } + return _cctor; + } + + protected Type BaseType { + get { return _baseType; } + } + + private void ImplementProtectedFieldAccessors() { + // For protected fields to be accessible from the derived type in Silverlight, + // we need to create public helper methods that expose them. These methods are + // used by the IOldDynamicObject implementation (in UserTypeOps.GetRuleHelper) + + FieldInfo[] fields = _baseType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy); + foreach (FieldInfo fi in fields) { + if (!fi.IsFamily && !fi.IsFamilyOrAssembly) { + continue; + } + + List fieldAccessorNames = new List(); + + PropertyBuilder pb = _tb.DefineProperty(fi.Name, PropertyAttributes.None, fi.FieldType, Type.EmptyTypes); + MethodAttributes methodAttrs = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName; + if (fi.IsStatic) { + methodAttrs |= MethodAttributes.Static; + } + + MethodBuilder method; + method = _tb.DefineMethod(FieldGetterPrefix + fi.Name, methodAttrs, + fi.FieldType, Type.EmptyTypes); + ILGen il = CreateILGen(method.GetILGenerator()); + if (!fi.IsStatic) { + il.EmitLoadArg(0); + } + + if (fi.IsLiteral) { + // literal fields need to be inlined directly in here... We use GetRawConstant + // which will work even in partial trust if the constant is protected. + object value = fi.GetRawConstantValue(); + switch (Type.GetTypeCode(fi.FieldType)) { + case TypeCode.Boolean: + if ((bool)value) { + il.Emit(OpCodes.Ldc_I4_1); + } else { + il.Emit(OpCodes.Ldc_I4_0); + } + break; + case TypeCode.Byte: il.Emit(OpCodes.Ldc_I4, (byte)value); break; + case TypeCode.Char: il.Emit(OpCodes.Ldc_I4, (char)value); break; + case TypeCode.Double: il.Emit(OpCodes.Ldc_R8, (double)value); break; + case TypeCode.Int16: il.Emit(OpCodes.Ldc_I4, (short)value); break; + case TypeCode.Int32: il.Emit(OpCodes.Ldc_I4, (int)value); break; + case TypeCode.Int64: il.Emit(OpCodes.Ldc_I8, (long)value); break; + case TypeCode.SByte: il.Emit(OpCodes.Ldc_I4, (sbyte)value); break; + case TypeCode.Single: il.Emit(OpCodes.Ldc_R4, (float)value); break; + case TypeCode.String: il.Emit(OpCodes.Ldstr, (string)value); break; + case TypeCode.UInt16: il.Emit(OpCodes.Ldc_I4, (ushort)value); break; + case TypeCode.UInt32: il.Emit(OpCodes.Ldc_I4, (uint)value); break; + case TypeCode.UInt64: il.Emit(OpCodes.Ldc_I8, (ulong)value); break; + } + } else { + il.EmitFieldGet(fi); + } + il.Emit(OpCodes.Ret); + + pb.SetGetMethod(method); + fieldAccessorNames.Add(method.Name); + + if (!fi.IsLiteral && !fi.IsInitOnly) { + method = _tb.DefineMethod(FieldSetterPrefix + fi.Name, methodAttrs, + null, new Type[] { fi.FieldType }); + method.DefineParameter(1, ParameterAttributes.None, "value"); + il = CreateILGen(method.GetILGenerator()); + il.EmitLoadArg(0); + if (!fi.IsStatic) { + il.EmitLoadArg(1); + } + il.EmitFieldSet(fi); + il.Emit(OpCodes.Ret); + pb.SetSetMethod(method); + + fieldAccessorNames.Add(method.Name); + } + + _specialNames.SetSpecialName(fi.Name, fieldAccessorNames); + } + } + + /// + /// Overrides methods - this includes all accessible virtual methods as well as protected non-virtual members + /// including statics and non-statics. + /// + internal void OverrideMethods(Type type) { + // if we have conflicting virtual's due to new slots only override the methods on the + // most derived class. + Dictionary, MethodInfo> added = new Dictionary, MethodInfo>(); + + MethodInfo overridden; + MethodInfo[] methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy); + + foreach (MethodInfo mi in methods) { + KeyValuePair key = new KeyValuePair(mi.Name, new MethodSignatureInfo(mi.IsStatic, mi.GetParameters())); + + if (!added.TryGetValue(key, out overridden)) { + added[key] = mi; + continue; + } + + if (overridden.DeclaringType.IsAssignableFrom(mi.DeclaringType)) { + added[key] = mi; + } + } + + Dictionary overriddenProperties = new Dictionary(); + foreach (MethodInfo mi in added.Values) { + if (!ShouldOverrideVirtual(mi) || !CanOverrideMethod(mi)) continue; + + if (mi.IsPublic || mi.IsFamily || mi.IsFamilyOrAssembly) { + if (mi.IsGenericMethodDefinition) continue; + + if (mi.IsSpecialName) { + OverrideSpecialName(mi, overriddenProperties); + } else { + OverrideBaseMethod(mi); + } + } + } + } + + private void OverrideSpecialName(MethodInfo mi, Dictionary overridden) { + if (!mi.IsVirtual || mi.IsFinal) { + if ((mi.IsFamily || mi.IsSpecialName) && (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_"))) { + // need to be able to call into protected getter/setter methods from derived types, + // even if these methods aren't virtual and we are in partial trust. + _specialNames.SetSpecialName(mi.Name); + MethodBuilder mb = CreateSuperCallHelper(mi); + + foreach (PropertyInfo pi in mi.DeclaringType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { + if (pi.GetGetMethod(true) == mi || pi.GetSetMethod(true) == mi) { + AddPublicProperty(mi, overridden, mb, pi); + break; + } + } + } + } else if (!TryOverrideProperty(mi, overridden)) { + string name; + EventInfo[] eis = mi.DeclaringType.GetEvents(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + foreach (EventInfo ei in eis) { + if (ei.GetAddMethod() == mi) { + if (!TryGetName(mi.DeclaringType, ei, mi, out name)) return; + CreateVTableEventOverride(mi, mi.Name); + return; + } else if (ei.GetRemoveMethod() == mi) { + if (!TryGetName(mi.DeclaringType, ei, mi, out name)) return; + CreateVTableEventOverride(mi, mi.Name); + return; + } + } + + OverrideBaseMethod(mi); + } + } + + private bool TryOverrideProperty(MethodInfo mi, Dictionary overridden) { + string name; + PropertyInfo[] pis = mi.DeclaringType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + _specialNames.SetSpecialName(mi.Name); + MethodBuilder mb = null; + PropertyInfo foundProperty = null; + foreach (PropertyInfo pi in pis) { + if (pi.GetIndexParameters().Length > 0) { + if (mi == pi.GetGetMethod(true)) { + mb = CreateVTableMethodOverride(mi, "__getitem__"); + if (!mi.IsAbstract) { + CreateSuperCallHelper(mi); + } + foundProperty = pi; + break; + } else if (mi == pi.GetSetMethod(true)) { + mb = CreateVTableMethodOverride(mi, "__setitem__"); + if (!mi.IsAbstract) { + CreateSuperCallHelper(mi); + } + foundProperty = pi; + break; + } + } else if (mi == pi.GetGetMethod(true)) { + if (mi.Name != "get_PythonType") { + if (!TryGetName(mi.DeclaringType, pi, mi, out name)) { + return true; + } + mb = CreateVTableGetterOverride(mi, name); + if (!mi.IsAbstract) { + CreateSuperCallHelper(mi); + } + } + foundProperty = pi; + break; + } else if (mi == pi.GetSetMethod(true)) { + if (!TryGetName(mi.DeclaringType, pi, mi, out name)) { + return true; + } + mb = CreateVTableSetterOverride(mi, name); + if (!mi.IsAbstract) { + CreateSuperCallHelper(mi); + } + foundProperty = pi; + break; + } + } + + if (foundProperty != null) { + AddPublicProperty(mi, overridden, mb, foundProperty); + return true; + } + return false; + } + + private void AddPublicProperty(MethodInfo mi, Dictionary overridden, MethodBuilder mb, PropertyInfo foundProperty) { + MethodInfo getter = foundProperty.GetGetMethod(true); + MethodInfo setter = foundProperty.GetSetMethod(true); + if (IsProtected(getter) || IsProtected(setter)) { + PropertyBuilder builder; + if (!overridden.TryGetValue(foundProperty, out builder)) { + ParameterInfo[] indexArgs = foundProperty.GetIndexParameters(); + Type[] paramTypes = new Type[indexArgs.Length]; + for (int i = 0; i < paramTypes.Length; i++) { + paramTypes[i] = indexArgs[i].ParameterType; + } + + overridden[foundProperty] = builder = _tb.DefineProperty(foundProperty.Name, foundProperty.Attributes, foundProperty.PropertyType, paramTypes); + } + + if (foundProperty.GetGetMethod(true) == mi) { + builder.SetGetMethod(mb); + } else if (foundProperty.GetSetMethod(true) == mi) { + builder.SetSetMethod(mb); + } + } + } + + private static bool IsProtected(MethodInfo mi) { + if (mi != null) { + return mi.IsFamilyOrAssembly || mi.IsFamily; + } + return false; + } + + /// + /// Loads all the incoming arguments and forwards them to mi which + /// has the same signature and then returns the result + /// + private void EmitBaseMethodDispatch(MethodInfo mi, ILGen il) { + if (!mi.IsAbstract) { + int offset = 0; + if (!mi.IsStatic) { + il.EmitLoadArg(0); + offset = 1; + } + ParameterInfo[] parameters = mi.GetParameters(); + for (int i = 0; i < parameters.Length; i++) { + il.EmitLoadArg(i + offset); + } + il.EmitCall(OpCodes.Call, mi, null); // base call must be non-virtual + il.Emit(OpCodes.Ret); + } else { + il.EmitLoadArg(0); + il.EmitString(mi.Name); + il.EmitCall(MissingInvokeMethodException()); + il.Emit(OpCodes.Throw); + } + } + + private void OverrideBaseMethod(MethodInfo mi) { + if ((!mi.IsVirtual || mi.IsFinal) && !mi.IsFamily) { + return; + } + + Type baseType; + if (_baseType == mi.DeclaringType || _baseType.IsSubclassOf(mi.DeclaringType)) { + baseType = _baseType; + } else { + // We must be inherting from an interface + Debug.Assert(mi.DeclaringType.IsInterface); + baseType = mi.DeclaringType; + } + + string name = null; + if (!TryGetName(baseType, mi, out name)) return; + + if (mi.DeclaringType == typeof(object) && mi.Name == "Finalize") return; + + _specialNames.SetSpecialName(mi.Name); + + if (!mi.IsStatic) { + CreateVTableMethodOverride(mi, name); + } + if (!mi.IsAbstract) { + CreateSuperCallHelper(mi); + } + } + + /// + /// Emits code to check if the class has overridden this specific + /// function. For example: + /// + /// MyDerivedType.SomeVirtualFunction = ... + /// or + /// + /// class MyDerivedType(MyBaseType): + /// def SomeVirtualFunction(self, ...): + /// + /// + internal LocalBuilder EmitBaseClassCallCheckForProperties(ILGen il, MethodInfo baseMethod, string name) { + Label instanceCall = il.DefineLabel(); + LocalBuilder callTarget = il.DeclareLocal(typeof(object)); + + il.EmitLoadArg(0); + EmitClassObjectFromInstance(il); + il.EmitLoadArg(0); + il.Emit(OpCodes.Ldstr, name); + il.Emit(OpCodes.Ldloca, callTarget); + il.EmitCall(NonInheritedValueHelper()); + + il.Emit(OpCodes.Brtrue, instanceCall); + + EmitBaseMethodDispatch(baseMethod, il); + + il.MarkLabel(instanceCall); + + return callTarget; + } + + private MethodBuilder CreateVTableGetterOverride(MethodInfo mi, string name) { + MethodBuilder impl; + ILGen il = DefineMethodOverride(MethodAttributes.Public, mi, out impl); + LocalBuilder callTarget = EmitBaseClassCallCheckForProperties(il, mi, name); + + EmitPropertyGet(il, mi, name, callTarget); + + if (!il.TryEmitImplicitCast(typeof(object), mi.ReturnType)) { + EmitConvertFromObject(il, mi.ReturnType); + } + il.Emit(OpCodes.Ret); + _tb.DefineMethodOverride(impl, mi); + return impl; + } + + /// + /// Emit code to convert object to a given type. This code is semantically equivalent + /// to PythonBinder.EmitConvertFromObject, except this version accepts ILGen whereas + /// PythonBinder accepts Compiler. The Binder will chagne soon and the two will merge. + /// + public void EmitConvertFromObject(ILGen il, Type toType) { + if (toType == typeof(object)) return; + + MethodInfo fastConvertMethod = GetFastConvertMethod(toType); + if (fastConvertMethod != null) { + il.EmitCall(fastConvertMethod); + } else if (toType == typeof(void)) { + il.Emit(OpCodes.Pop); + } else if (typeof(Delegate).IsAssignableFrom(toType)) { + il.EmitType(toType); + il.EmitCall(ConvertToDelegate()); + il.Emit(OpCodes.Castclass, toType); + } else { + Label end = il.DefineLabel(); + il.Emit(OpCodes.Dup); + il.Emit(OpCodes.Isinst, toType); + + il.Emit(OpCodes.Brtrue_S, end); + il.Emit(OpCodes.Ldtoken, toType); + il.EmitCall(GetGenericConvertMethod(toType)); + il.MarkLabel(end); + + il.Emit(OpCodes.Unbox_Any, toType); //??? this check may be redundant + } + } + + private MethodBuilder CreateVTableSetterOverride(MethodInfo mi, string name) { + MethodBuilder impl; + ILGen il = DefineMethodOverride(MethodAttributes.Public, mi, out impl); + LocalBuilder callTarget = EmitBaseClassCallCheckForProperties(il, mi, name); + + EmitPropertySet(il, mi, name, callTarget); + + il.Emit(OpCodes.Ret); + _tb.DefineMethodOverride(impl, mi); + return impl; + } + + private void CreateVTableEventOverride(MethodInfo mi, string name) { + // override the add/remove method + MethodBuilder impl; + ILGen il = DefineMethodOverride(mi, out impl); + + LocalBuilder callTarget = EmitBaseClassCallCheckForProperties(il, mi, name); + + il.Emit(OpCodes.Ldloc, callTarget); + il.EmitLoadArg(0); + il.EmitLoadArg(0); + EmitClassObjectFromInstance(il); + il.EmitLoadArg(1); + il.EmitBoxing(mi.GetParameters()[0].ParameterType); + il.Emit(OpCodes.Ldstr, name); + il.EmitCall(EventHelper()); + il.Emit(OpCodes.Ret); + _tb.DefineMethodOverride(impl, mi); + } + + private MethodBuilder CreateVTableMethodOverride(MethodInfo mi, string name) { + ParameterInfo[] parameters = mi.GetParameters(); + MethodBuilder impl; + ILGen il; + if (mi.IsVirtual && !mi.IsFinal) { + il = DefineMethodOverride(MethodAttributes.Public, mi, out impl); + } else { + impl = _tb.DefineMethod( + mi.Name, + mi.IsVirtual ? + (mi.Attributes | MethodAttributes.NewSlot) : + ((mi.Attributes & ~MethodAttributes.MemberAccessMask) | MethodAttributes.Public), + mi.ReturnType, + ReflectionUtils.GetParameterTypes(parameters)); + il = CreateILGen(impl.GetILGenerator()); + } + //CompilerHelpers.GetArgumentNames(parameters)); TODO: Set names + + LocalBuilder callTarget = EmitNonInheritedMethodLookup(name, il); + Label instanceCall = il.DefineLabel(); + il.Emit(OpCodes.Brtrue, instanceCall); + + // lookup failed, call the base class method (this returns or throws) + EmitBaseMethodDispatch(mi, il); + + // lookup succeeded, call the user defined method & return + il.MarkLabel(instanceCall); + EmitClrCallStub(il, mi, callTarget, name); + EmitConvertFromObject(il, mi.ReturnType); + il.Emit(OpCodes.Ret); + + if (mi.IsVirtual && !mi.IsFinal) { + _tb.DefineMethodOverride(impl, mi); + } + return impl; + } + + /// + /// Emits the call to lookup a member defined in the user's type. Returns + /// the local which stores the resulting value and leaves a value on the + /// stack indicating the success of the lookup. + /// + private LocalBuilder EmitNonInheritedMethodLookup(string name, ILGen il) { + LocalBuilder callTarget = il.DeclareLocal(typeof(object)); + + // emit call to helper to do lookup + il.EmitLoadArg(0); + EmitClassObjectFromInstance(il); + il.EmitLoadArg(0); + il.Emit(OpCodes.Ldstr, name); + il.Emit(OpCodes.Ldloca, callTarget); + il.EmitCall(NonInheritedMethodHelper()); + return callTarget; + } + + /// + /// Creates a method for doing a base method dispatch. This is used to support + /// super(type, obj) calls. + /// + private MethodBuilder CreateSuperCallHelper(MethodInfo mi) { + ParameterInfo[] parms = mi.GetParameters(); + Type[] types = ReflectionUtils.GetParameterTypes(parms); + Type miType = mi.DeclaringType; + for (int i = 0; i < types.Length; i++) { + if (types[i] == miType) { + types[i] = _tb; + } + } + + MethodAttributes attrs = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName; + if (mi.IsStatic) { + attrs |= MethodAttributes.Static; + } + + MethodBuilder method = _tb.DefineMethod( + BaseMethodPrefix + mi.Name, + attrs, + mi.ReturnType, types + ); + + for (int i = 0; i < types.Length; i++) { + method.DefineParameter(i + 1, ParameterAttributes.None, parms[i].Name); + } + + EmitBaseMethodDispatch(mi, CreateILGen(method.GetILGenerator())); + return method; + } + + public Type FinishType() { + if (_cctor != null) { + _cctor.Emit(OpCodes.Ret); + } + Type result = _tb.CreateType(); + new OverrideBuilder(_baseType).AddBaseMethods(result, _specialNames); + return result; + } + + internal protected ILGen CreateILGen(ILGenerator il) { + // TODO: Debugging support + return new ILGen(il); + } + + private ILGen DefineExplicitInterfaceImplementation(MethodInfo baseMethod, out MethodBuilder builder) { + MethodAttributes attrs = baseMethod.Attributes & ~(MethodAttributes.Abstract | MethodAttributes.Public); + attrs |= MethodAttributes.NewSlot | MethodAttributes.Final; + + Type[] baseSignature = ReflectionUtils.GetParameterTypes(baseMethod.GetParameters()); + builder = _tb.DefineMethod( + baseMethod.DeclaringType.Name + "." + baseMethod.Name, + attrs, + baseMethod.ReturnType, + baseSignature + ); + return CreateILGen(builder.GetILGenerator()); + } + + protected const MethodAttributes MethodAttributesToEraseInOveride = MethodAttributes.Abstract | MethodAttributes.ReservedMask; + + protected ILGen DefineMethodOverride(Type type, string name, out MethodInfo decl, out MethodBuilder impl) { + return DefineMethodOverride(MethodAttributes.PrivateScope, type, name, out decl, out impl); + } + + protected ILGen DefineMethodOverride(MethodAttributes extra, Type type, string name, out MethodInfo decl, out MethodBuilder impl) { + decl = type.GetMethod(name); + return DefineMethodOverride(extra, decl, out impl); + } + + protected ILGen DefineMethodOverride(MethodInfo decl, out MethodBuilder impl) { + return DefineMethodOverride(MethodAttributes.PrivateScope, decl, out impl); + } + + protected ILGen DefineMethodOverride(MethodAttributes extra, MethodInfo decl, out MethodBuilder impl) { + MethodAttributes finalAttrs = (decl.Attributes & ~MethodAttributesToEraseInOveride) | extra; + if ((extra & MethodAttributes.MemberAccessMask) != 0) { + // remove existing member access, add new member access + finalAttrs &= ~MethodAttributes.MemberAccessMask; + finalAttrs |= extra; + } + Type[] signature = ReflectionUtils.GetParameterTypes(decl.GetParameters()); + impl = _tb.DefineMethod(decl.Name, finalAttrs, decl.ReturnType, signature); + return CreateILGen(impl.GetILGenerator()); + } + + /// + /// Generates stub to receive the CLR call and then call the dynamic language code. + /// This code is similar to that in DelegateSignatureInfo.cs in the Microsoft.Scripting. + /// + internal void EmitClrCallStub(ILGen/*!*/ il, MethodInfo/*!*/ mi, LocalBuilder/*!*/ callTarget, string/*!*/ name) { + int firstArg = 0; + bool list = false; // The list calling convention + bool context = false; // Context is an argument + + ParameterInfo[] pis = mi.GetParameters(); + if (pis.Length > 0) { + if (pis[0].ParameterType == ContextType) { + firstArg = 1; + context = true; + } + if (pis[pis.Length - 1].IsDefined(typeof(ParamArrayAttribute), false)) { + list = true; + } + } + + ParameterInfo[] args = pis; + int nargs = args.Length - firstArg; + + // Create the action + ILGen cctor = GetCCtor(); + EmitMakeCallAction(name, nargs, list); + + // Create the dynamic site + Type siteType = CompilerHelpers.MakeCallSiteType(MakeSiteSignature(nargs)); + FieldBuilder site = _tb.DefineField("site$" + _site++, siteType, FieldAttributes.Private | FieldAttributes.Static); + cctor.EmitCall(siteType.GetMethod("Create")); + cctor.EmitFieldSet(site); + + List fixers = new List(0); + + // + // Emit the site invoke + // + il.EmitFieldGet(site); + FieldInfo target = siteType.GetField("Target"); + il.EmitFieldGet(target); + il.EmitFieldGet(site); + + // Emit the code context + EmitContext(il, context); + + il.Emit(OpCodes.Ldloc, callTarget); + + for (int i = firstArg; i < args.Length; i++) { + ReturnFixer rf = ReturnFixer.EmitArgument(il, args[i], i + 1); + if (rf != null) { + fixers.Add(rf); + } + } + + il.EmitCall(target.FieldType, "Invoke"); + + foreach (ReturnFixer rf in fixers) { + rf.FixReturn(il); + } + } + + private void EmitContext(ILGen/*!*/ il, bool context) { + if (context) { + il.EmitLoadArg(1); + } else { + EmitImplicitContext(il); + } + } + } + + /// + /// Same as the DLR ReturnFixer, but accepts lower level constructs, + /// such as LocalBuilder, ParameterInfos and ILGen. + /// + sealed class ReturnFixer { + private readonly ParameterInfo/*!*/ _parameter; + private readonly LocalBuilder/*!*/ _reference; + private readonly int _index; + + private ReturnFixer(LocalBuilder/*!*/ reference, ParameterInfo/*!*/ parameter, int index) { + Debug.Assert(reference.LocalType.IsGenericType && reference.LocalType.GetGenericTypeDefinition() == typeof(StrongBox<>)); + Debug.Assert(parameter.ParameterType.IsByRef); + + _parameter = parameter; + _reference = reference; + _index = index; + } + + public void FixReturn(ILGen/*!*/ il) { + il.EmitLoadArg(_index); + il.Emit(OpCodes.Ldloc, _reference); + il.EmitFieldGet(_reference.LocalType.GetField("Value")); + il.EmitStoreValueIndirect(_parameter.ParameterType.GetElementType()); + } + + public static ReturnFixer/*!*/ EmitArgument(ILGen/*!*/ il, ParameterInfo/*!*/ parameter, int index) { + il.EmitLoadArg(index); + if (parameter.ParameterType.IsByRef) { + Type elementType = parameter.ParameterType.GetElementType(); + Type concreteType = typeof(StrongBox<>).MakeGenericType(elementType); + LocalBuilder refSlot = il.DeclareLocal(concreteType); + il.EmitLoadValueIndirect(elementType); + il.EmitNew(concreteType, new Type[] { elementType }); + il.Emit(OpCodes.Stloc, refSlot); + il.Emit(OpCodes.Ldloc, refSlot); + return new ReturnFixer(refSlot, parameter, index); + } else { + il.EmitBoxing(parameter.ParameterType); + return null; + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Generation/EmittedAttribute.cs b/merlin/main/languages/ruby/Ruby/Compiler/Generation/EmittedAttribute.cs new file mode 100644 index 0000000000..38606ef31f --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Generation/EmittedAttribute.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; + +namespace IronRuby.Compiler.Generation { + + /// + /// Marks methods and properties that are emitted into the generated code. + /// + [Conditional("DEBUG")] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] + public sealed class EmittedAttribute : Attribute { + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Generation/ITypeFeature.cs b/merlin/main/languages/ruby/Ruby/Compiler/Generation/ITypeFeature.cs new file mode 100644 index 0000000000..e9eeb7f756 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Generation/ITypeFeature.cs @@ -0,0 +1,29 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection.Emit; + +namespace IronRuby.Compiler.Generation { + internal interface ITypeFeature { + bool CanInherit { get; } + bool IsImplementedBy(Type type); + IFeatureBuilder MakeBuilder(TypeBuilder tb); + } + + internal interface IFeatureBuilder { + void Implement(ClsTypeEmitter emitter); + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Generation/InterfacesBuilder.cs b/merlin/main/languages/ruby/Ruby/Compiler/Generation/InterfacesBuilder.cs new file mode 100644 index 0000000000..243a3b08ae --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Generation/InterfacesBuilder.cs @@ -0,0 +1,137 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Reflection.Emit; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Generation { + public class InterfacesBuilder : IFeatureBuilder { + + #region ITypeFeature + + sealed class TypeFeature : ITypeFeature { + internal static readonly TypeFeature/*!*/ _empty = new TypeFeature(Type.EmptyTypes); + + private readonly Type/*!*/[]/*!*/ _interfaces; + private readonly int _hash; + + internal TypeFeature(IList/*!*/ interfaceTypes) { + Assert.NotNull(interfaceTypes); + + List types = new List(interfaceTypes.Count); + foreach (Type t in interfaceTypes) { + AddInterface(types, t); + } + types.Sort(delegate(Type a, Type b) { return a.FullName.CompareTo(b.FullName); }); + _interfaces = types.ToArray(); + + _hash = typeof(TypeFeature).GetHashCode(); + foreach (Type t in _interfaces) { + _hash ^= t.GetHashCode(); // + } + } + + private static void AddInterface(List/*!*/ types, Type/*!*/ type) { + Assert.NotNull(type); + Assert.Equals(true, type.IsInterface && !type.ContainsGenericParameters); + + for (int i = 0; i < types.Count; i++) { + Type t = types[i]; + if (t == type || type.IsAssignableFrom(t)) { + // This interface is already included in the list + return; + } + if (t.IsAssignableFrom(type)) { + // We are going to supercede this interface + types.RemoveAt(i--); + } + } + types.Add(type); + } + + public bool CanInherit { + get { return true; } + } + + public bool IsImplementedBy(Type/*!*/ type) { + // TODO: If the type's implementation of each interface is a delegating implementation, return true + return (_interfaces.Length == 0); + } + + public IFeatureBuilder/*!*/ MakeBuilder(TypeBuilder/*!*/ tb) { + return new InterfacesBuilder(tb, _interfaces); + } + + public override int GetHashCode() { + return typeof(TypeFeature).GetHashCode(); + } + + public override bool Equals(object obj) { + TypeFeature other = obj as TypeFeature; + if (other == null) return false; + if (_interfaces.Length != other._interfaces.Length) return false; + + for (int i = 0; i < _interfaces.Length; i++) { + if (!_interfaces[i].Equals(other._interfaces[i])) { + return false; + } + } + + return true; + } + } + + internal static ITypeFeature/*!*/ MakeFeature(IList/*!*/ interfaceTypes) { + if (interfaceTypes.Count == 0) { + return TypeFeature._empty; + } + return new TypeFeature(interfaceTypes); + } + + #endregion + + private readonly TypeBuilder/*!*/ _tb; + private readonly Type/*!*/[]/*!*/ _interfaces; + + internal InterfacesBuilder(TypeBuilder/*!*/ tb, Type/*!*/[]/*!*/ interfaces) { + _tb = tb; + _interfaces = interfaces; + } + + public void Implement(ClsTypeEmitter/*!*/ emitter) { + // TODO: Exclude interfaces already implemented in base class feature sets + // TODO: Exclude IDynamicObject, IRubyObject, etc. or handle specially + Dictionary doneTypes = new Dictionary(); + foreach (Type interfaceType in _interfaces) { + _tb.AddInterfaceImplementation(interfaceType); + ImplementInterface(emitter, interfaceType, doneTypes); + } + } + + private void ImplementInterface(ClsTypeEmitter/*!*/ emitter, Type/*!*/ interfaceType, Dictionary/*!*/ doneTypes) { + if (doneTypes.ContainsKey(interfaceType)) { + return; + } + doneTypes.Add(interfaceType, true); + emitter.OverrideMethods(interfaceType); + + foreach (Type t in interfaceType.GetInterfaces()) { + ImplementInterface(emitter, t, doneTypes); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Generation/OverriddenMembers.cs b/merlin/main/languages/ruby/Ruby/Compiler/Generation/OverriddenMembers.cs new file mode 100644 index 0000000000..81b67633fd --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Generation/OverriddenMembers.cs @@ -0,0 +1,191 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using System.Dynamic; +using System.Dynamic.Binders; +using System.Text; + +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Generation { + public abstract partial class ClsTypeEmitter { + + class OverriddenMembers { + private static readonly Dictionary _overrideData = new Dictionary(); + + private readonly Dictionary/*!*/>/*!*/ _methods = new Dictionary>(); + private readonly Dictionary/*!*/>/*!*/ _properties = new Dictionary>(); + + internal static OverriddenMembers GetForType(Type/*!*/ type) { + OverriddenMembers result; + lock (_overrideData) { + if (!_overrideData.TryGetValue(type, out result)) { + _overrideData[type] = result = new OverriddenMembers(); + } + } + return result; + } + + internal void AddMethod(MethodInfo/*!*/ mi, string/*!*/ newName) { + List methods; + if (!_methods.TryGetValue(newName, out methods)) { + _methods[newName] = methods = new List(); + } + + methods.Add(mi); + } + + internal ExtensionPropertyTracker AddPropertyInfo(string/*!*/ propName, MethodInfo get, MethodInfo set) { + MethodInfo mi = get ?? set; + + List trackers; + if (!_properties.TryGetValue(propName, out trackers)) { + _properties[propName] = trackers = new List(); + } + + ExtensionPropertyTracker res; + for (int i = 0; i < trackers.Count; i++) { + if (trackers[i].DeclaringType == mi.DeclaringType) { + trackers[i] = res = new ExtensionPropertyTracker( + propName, + get ?? trackers[i].GetGetMethod(), + set ?? trackers[i].GetSetMethod(), + null, + mi.DeclaringType + ); + return res; + } + } + + trackers.Add( + res = new ExtensionPropertyTracker( + propName, + get, + set, + null, + mi.DeclaringType + ) + ); + + return res; + } + + internal List GetMembers(string/*!*/ name) { + List members = null; + + List methodList; + if (_methods.TryGetValue(name, out methodList) && methodList.Count > 0) { + foreach (MethodInfo mi in methodList) { + if (members == null) members = new List(); + members.Add(MemberTracker.FromMemberInfo(mi)); + } + } + + List propertyList; + if (_properties.TryGetValue(name, out propertyList) && propertyList.Count > 0) { + foreach (ExtensionPropertyTracker tracker in propertyList) { + if (members == null) members = new List(); + members.Add(tracker); + } + } + + return members; + } + } + + class OverrideBuilder { + private readonly Type/*!*/ _baseType; + + internal OverrideBuilder(Type baseType) { + _baseType = baseType; + } + + // TODO: if it's an indexer then we want to override get_Item/set_Item methods + // which map to [] and []= + + internal void AddBaseMethods(Type finishedType, SpecialNames specialNames) { + // "Adds" base methods to super type - this makes super(...).xyz to work - otherwise + // we'd return a function that did a virtual call resulting in a stack overflow. + OverriddenMembers overrides = OverriddenMembers.GetForType(finishedType); + + foreach (MethodInfo mi in finishedType.GetMethods()) { + if (!ShouldOverrideVirtual(mi)) continue; + + string methodName = mi.Name; + if (methodName.StartsWith(BaseMethodPrefix) || methodName.StartsWith(FieldGetterPrefix) || methodName.StartsWith(FieldSetterPrefix)) { + foreach (string newName in specialNames.GetBaseName(mi)) { + if (mi.IsSpecialName && (newName.StartsWith("get_") || newName.StartsWith("set_"))) { + StoreOverriddenProperty(overrides, mi, newName); + } else if (mi.IsSpecialName && (newName.StartsWith(FieldGetterPrefix) || newName.StartsWith(FieldSetterPrefix))) { + StoreOverriddenField(overrides, mi, newName); + } else { + StoreOverriddenMethod(overrides, mi, newName); + } + } + } + } + } + + private void StoreOverriddenProperty(OverriddenMembers overrides, MethodInfo mi, string newName) { + string propName = newName.Substring(4); // get_ or set_ + foreach (PropertyInfo pi in _baseType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy)) { + if (pi.Name == propName) { + if (newName.StartsWith("get_")) { + overrides.AddMethod(mi, propName); + } else if (newName.StartsWith("set_")) { + overrides.AddMethod(mi, propName + "="); + } + } + } + } + + private void StoreOverriddenField(OverriddenMembers overrides, MethodInfo mi, string newName) { + string fieldName = newName.Substring(FieldGetterPrefix.Length); // get_ or set_ + foreach (FieldInfo pi in _baseType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy)) { + if (pi.Name == fieldName) { + if (newName.StartsWith(FieldGetterPrefix)) { + overrides.AddMethod(mi, fieldName); + } else if (newName.StartsWith(FieldSetterPrefix)) { + overrides.AddMethod(mi, fieldName + "="); + } + } + } + } + + private void StoreOverriddenMethod(OverriddenMembers overrides, MethodInfo mi, string newName) { + MemberInfo[] members = _baseType.GetMember(newName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy); + Debug.Assert(members.Length > 0, String.Format("{0} from {1}", newName, _baseType.Name)); + Type declType = members[0].DeclaringType; + + overrides.AddMethod(mi, newName); + } + } + + public static List GetOverriddenMembersForType(Type/*!*/ type, string/*!*/ name) { + return OverriddenMembers.GetForType(type).GetMembers(name); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Generation/Profiler.cs b/merlin/main/languages/ruby/Ruby/Compiler/Generation/Profiler.cs new file mode 100644 index 0000000000..50c4b77178 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Generation/Profiler.cs @@ -0,0 +1,88 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Scripting.Math; +using IronRuby.Builtins; +using IronRuby.Compiler.Generation; +using IronRuby.Runtime; + +namespace IronRuby.Compiler.Generation { + + public sealed class Profiler { + public static readonly Profiler/*!*/ Instance = new Profiler(); + internal static long[] _ProfileTicks = new long[100]; + + private readonly Dictionary/*!*/ _counters; + private readonly List/*!*/ _profiles; + private static int _Index; + + private Profiler() { + _counters = new Dictionary(); + _profiles = new List(); + } + + public int GetTickIndex(string/*!*/ name) { + int index; + lock (_counters) { + if (!_counters.TryGetValue(name, out index)) { + index = _Index++; + _counters.Add(name, index); + } + if (index >= _ProfileTicks.Length) { + long[] newProfile = new long[index * 2]; + _profiles.Add(Interlocked.Exchange(ref _ProfileTicks, newProfile)); + } + } + return index; + } + + public Dictionary/*!*/ GetProfile() { + var result = new Dictionary(); + lock (_counters) { + // capture the current profile: + long[] newProfile = new long[_ProfileTicks.Length]; + long[] total = Interlocked.Exchange(ref _ProfileTicks, newProfile); + + for (int i = 0; i < _profiles.Count; i++) { + for (int j = 0; j < total.Length; j++) { + if (j < _profiles[i].Length) { + total[j] += _profiles[i][j]; + } + } + } + + foreach (var counter in _counters) { + result.Add(counter.Key, total[counter.Value]); + } + } + + return result; + } + } +} + +namespace IronRuby.Runtime { + public static partial class RubyOps { + [Emitted] + public static void UpdateProfileTicks(int index, long entryStamp) { + Interlocked.Add(ref Profiler._ProfileTicks[index], Stopwatch.GetTimestamp() - entryStamp); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Generation/RubyTypeBuilder.cs b/merlin/main/languages/ruby/Ruby/Compiler/Generation/RubyTypeBuilder.cs new file mode 100644 index 0000000000..2deaeb579b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Generation/RubyTypeBuilder.cs @@ -0,0 +1,437 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Dynamic.Binders; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Runtime; + +namespace IronRuby.Compiler.Generation { + internal class RubyTypeBuilder : IFeatureBuilder { + + #region ITypeFeature + + sealed class TypeFeature : ITypeFeature { + public bool CanInherit { + get { return true; } + } + + public bool IsImplementedBy(Type/*!*/ type) { + return typeof(IRubyObject).IsAssignableFrom(type); + } + + public IFeatureBuilder/*!*/ MakeBuilder(TypeBuilder/*!*/ tb) { + return new RubyTypeBuilder(tb); + } + + public override int GetHashCode() { + return typeof(TypeFeature).GetHashCode(); + } + + public override bool Equals(object obj) { + return Object.ReferenceEquals(obj, _feature); + } + } + + static readonly TypeFeature/*!*/ _feature = new TypeFeature(); + + public static ITypeFeature/*!*/ Feature { + get { return _feature; } + } + + #endregion + + protected readonly TypeBuilder/*!*/ _tb; + protected readonly FieldBuilder/*!*/ _classField; + protected readonly FieldBuilder/*!*/ _instanceDataField; + + internal RubyTypeBuilder(TypeBuilder/*!*/ tb) { + _tb = tb; + _classField = _tb.DefineField("_class", typeof(RubyClass), FieldAttributes.Private | FieldAttributes.InitOnly); + _instanceDataField = _tb.DefineField("_instanceData", typeof(RubyInstanceData), FieldAttributes.Private); + } + + public void Implement(ClsTypeEmitter/*!*/ emitter) { + DefineConstructors(); + DefineRubyObjectImplementation(); + DefineSerializer(); + + RubyTypeEmitter re = (emitter as RubyTypeEmitter); + Assert.NotNull(re); + re.ClassField = _classField; + + DefineDynamicObjectImplementation(); + +#if !SILVERLIGHT // ICustomTypeDescriptor + DefineCustomTypeDescriptor(); +#endif + + // we need to get the right execution context +#if OBSOLETE + // TODO: remove the need for these methods to be special cased + EmitOverrideEquals(typeGen); + EmitOverrideGetHashCode(typeGen); +#endif + } + +#if !SILVERLIGHT + private static readonly Type/*!*/[]/*!*/ _deserializerSignature = new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }; +#endif + private static readonly Type/*!*/[]/*!*/ _classArgSignature = new Type[] { typeof(RubyClass) }; + + private static bool IsAvailable(MethodBase method) { + return method != null && !method.IsPrivate && !method.IsFamilyAndAssembly; + } + + private void DefineConstructors() { + BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; + ConstructorInfo emptyCtor = _tb.BaseType.GetConstructor(bindingFlags, null, Type.EmptyTypes, null); + ConstructorInfo classArgCtor = null; + + bool deserializerFound = false; + foreach (var baseCtor in _tb.BaseType.GetConstructors(bindingFlags)) { + if (!baseCtor.IsPublic && !baseCtor.IsFamily) { + continue; + } + + int oldLength = baseCtor.GetParameters().Length; + List newParams = new List(oldLength + 1); + foreach (var param in baseCtor.GetParameters()) { + newParams.Add(param.ParameterType); + } + + int offset = 1; + bool isDeserializer = false; + if (oldLength > 0 && newParams[0] == typeof(RubyClass)) { + // Build a simple pass-through constructor + offset = 0; +#if !SILVERLIGHT + } else if (oldLength == 2 && newParams[0] == typeof(SerializationInfo) && newParams[1] == typeof(StreamingContext)) { + // Build a deserializer + deserializerFound = true; + isDeserializer = true; + offset = 0; +#endif + } else { + // Special-case for Exception + if (_tb.IsSubclassOf(typeof(Exception)) && IsAvailable(emptyCtor)) { + if (oldLength == 0) { + // Skip this constructor; it would conflict with the one we're going to build next + continue; + } else if (oldLength == 1 && newParams[0] == typeof(string)) { + // Special case exceptions to improve interop. Ruby's default message for an exception is the name of the exception class. + BuildExceptionConstructor(baseCtor); + } + } + + // Add RubyClass to the head of the parameter list + newParams.Insert(0, typeof(RubyClass)); + } + + // Build a new constructor based on this base class ctor + ConstructorBuilder cb = _tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, newParams.ToArray()); + ILGen il = new ILGen(cb.GetILGenerator()); + il.EmitLoadArg(0); + for (int i = 1; i < newParams.Count; i++) { + il.EmitLoadArg(i + offset); + } + il.Emit(OpCodes.Call, baseCtor); + if (!isDeserializer) { + // ctor(RubyClass! class, ...) : ... { this._class = class; } + il.EmitLoadArg(0); + il.EmitLoadArg(1); + il.EmitFieldSet(_classField); + } else { + // ctor(SerializationInfo! info, StreamingContext! context) : base { + // RubyOps.DeserializeObject(out this._instanceData, out this._class, info); + // } + il.EmitLoadArg(0); + il.EmitFieldAddress(_instanceDataField); + il.EmitLoadArg(0); + il.EmitFieldAddress(_classField); + il.EmitLoadArg(1); + il.EmitCall(typeof(RubyOps).GetMethod("DeserializeObject")); + } + il.Emit(OpCodes.Ret); + + if (oldLength == 0) { + classArgCtor = cb; + } + } +#if !SILVERLIGHT + if (classArgCtor != null && !deserializerFound) { + // We didn't previously find a deserialization constructor. If we can, build one now. + BuildDeserializationConstructor(classArgCtor); + } +#endif + } + + private void BuildExceptionConstructor(ConstructorInfo baseCtor) { + // ctor(RubyClass! class) : base(RubyOps.GetDefaultExceptionMessage(class)) { + // this._class = class; + // } + ConstructorBuilder ctor = _tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, _classArgSignature); + ILGen il = new ILGen(ctor.GetILGenerator()); + il.EmitLoadArg(0); + il.EmitLoadArg(1); + il.Emit(OpCodes.Call, Methods.GetDefaultExceptionMessage); + il.Emit(OpCodes.Call, baseCtor); + il.EmitLoadArg(0); + il.EmitLoadArg(1); + il.EmitFieldSet(_classField); + il.Emit(OpCodes.Ret); + } + +#if !SILVERLIGHT + private void BuildDeserializationConstructor(ConstructorInfo thisCtor) { + // ctor(SerializationInfo! info, StreamingContext! context) : this((RubyClass)context.Context) { + // RubyOps.DeserializeObject(out this._instanceData, out this._class, info); + // } + ConstructorBuilder ctor = _tb.DefineConstructor(MethodAttributes.Family, CallingConventions.Standard, _deserializerSignature); + ILGen il = new ILGen(ctor.GetILGenerator()); + il.EmitLoadArg(0); + il.EmitLoadArgAddress(2); + il.EmitCall(typeof(StreamingContext).GetProperty("Context").GetGetMethod()); + il.Emit(OpCodes.Castclass, typeof(RubyClass)); + il.Emit(OpCodes.Call, thisCtor); + + il.EmitLoadArg(0); + il.EmitFieldAddress(_instanceDataField); + il.EmitLoadArg(0); + il.EmitFieldAddress(_classField); + il.EmitLoadArg(1); + il.EmitCall(typeof(RubyOps).GetMethod("DeserializeObject")); + il.Emit(OpCodes.Ret); + } +#endif + + private void DefineRubyObjectImplementation() { + _tb.AddInterfaceImplementation(typeof(IRubyObject)); + + ILGen il; + + // RubyClass! IRubyObject.RubyClass { get { return this._class; } } + il = DefineMethodOverride(_tb, typeof(IRubyObject).GetProperty(RubyObject.ClassPropertyName).GetGetMethod()); + il.EmitLoadArg(0); + il.EmitFieldGet(_classField); + il.Emit(OpCodes.Ret); + + // RubyInstanceData IRubyObject.TryGetInstanceData() { return this._instanceData; } + il = DefineMethodOverride(_tb, typeof(IRubyObject).GetMethod("TryGetInstanceData")); + il.EmitLoadArg(0); + il.EmitFieldGet(_instanceDataField); + il.Emit(OpCodes.Ret); + + // RubyInstanceData! IRubyObject.GetInstanceData() { return RubyOps.GetInstanceData(ref _instanceData); } + il = DefineMethodOverride(_tb, typeof(IRubyObject).GetMethod("GetInstanceData")); + il.EmitLoadArg(0); + il.EmitFieldAddress(_instanceDataField); + il.EmitCall(typeof(RubyOps).GetMethod("GetInstanceData")); + il.Emit(OpCodes.Ret); + } + + private void DefineSerializer() { +#if !SILVERLIGHT + ILGen il; + _tb.AddInterfaceImplementation(typeof(ISerializable)); + + // void ISerializable.GetObjectData(SerializationInfo! info, StreamingContext! context) { + // base.GetObjectData(info, context); + // RubyOps.SerializeObject(_instanceData, _class, info); + // } + + MethodInfo baseSerializer; + if (typeof(ISerializable).IsAssignableFrom(_tb.BaseType)) { + InterfaceMapping map = _tb.BaseType.GetInterfaceMap(typeof(ISerializable)); + baseSerializer = map.TargetMethods[0]; + } else { + baseSerializer = null; + } + + il = DefinePrivateInterfaceMethodOverride(_tb, typeof(ISerializable).GetMethod("GetObjectData")); + if (baseSerializer != null) { + il.EmitLoadArg(0); + il.EmitLoadArg(1); + il.EmitLoadArg(2); + il.Emit(OpCodes.Call, baseSerializer); // Nonvirtual call + } + + il.EmitLoadArg(0); + il.EmitFieldGet(_instanceDataField); + il.EmitLoadArg(0); + il.EmitFieldGet(_classField); + il.EmitLoadArg(1); + il.EmitCall(typeof(RubyOps).GetMethod("SerializeObject")); + il.Emit(OpCodes.Ret); +#endif + } + + // we need to get the right execution context +#if OBSOLETE + private static void EmitOverrideEquals(TypeGen typeGen) { + Type baseType = typeGen.TypeBuilder.BaseType; + MethodInfo baseMethod = baseType.GetMethod("Equals", new Type[] { typeof(object) }); + Compiler cg = typeGen.DefineMethodOverride(baseMethod); + + // Check if an "eql?" method exists on this class + cg.EmitType(typeGen.TypeBuilder); + cg.EmitString("eql?"); + cg.EmitCall(typeof(RubyOps).GetMethod("ResolveDeclaredInstanceMethod")); + Label callBase = cg.DefineLabel(); + cg.Emit(OpCodes.Brfalse_S, callBase); + + // If so, call it + cg.EmitThis(); + cg.EmitArgGet(0); + cg.EmitCall(typeof(RubyOps).GetMethod("CallEql")); + cg.EmitReturn(); + + // Otherwise, call base class + cg.MarkLabel(callBase); + cg.EmitThis(); + cg.EmitArgGet(0); + cg.Emit(OpCodes.Call, baseMethod); // base call must be non-virtual + cg.EmitReturn(); + + cg.Finish(); + } + + private static void EmitOverrideGetHashCode(TypeGen typeGen) { + Type baseType = typeGen.TypeBuilder.BaseType; + MethodInfo baseMethod = baseType.GetMethod("GetHashCode", Type.EmptyTypes); + Compiler cg = typeGen.DefineMethodOverride(baseMethod); + + // Check if a "hash" method exists on this class + cg.EmitType(typeGen.TypeBuilder); + cg.EmitString("hash"); + cg.EmitCall(typeof(RubyOps).GetMethod("ResolveDeclaredInstanceMethod")); + Label callBase = cg.DefineLabel(); + cg.Emit(OpCodes.Brfalse_S, callBase); + + // If so, call it + cg.EmitThis(); + cg.EmitCall(typeof(RubyOps).GetMethod("CallHash")); + cg.EmitReturn(); + + // Otherwise, call base class + cg.MarkLabel(callBase); + cg.EmitThis(); + cg.Emit(OpCodes.Call, baseMethod); // base call must be non-virtual + cg.EmitReturn(); + + cg.Finish(); + } +#endif + + private void DefineDynamicObjectImplementation() { + _tb.AddInterfaceImplementation(typeof(IDynamicObject)); + + // MetaObject! IDynamicObject.GetMetaObject(Expression! parameter) { + // return RubyOps.GetMetaObject(this, parameter); + // } + + MethodInfo decl = typeof(IDynamicObject).GetMethod("GetMetaObject"); + MethodBuilder impl = _tb.DefineMethod( + decl.Name, + decl.Attributes & ~(MethodAttributes.Abstract | MethodAttributes.ReservedMask), + decl.ReturnType, + ReflectionUtils.GetParameterTypes(decl.GetParameters()) + ); + + ILGen il = new ILGen(impl.GetILGenerator()); + il.EmitLoadArg(0); + il.EmitLoadArg(1); + il.EmitCall(Methods.GetMetaObject); + il.Emit(OpCodes.Ret); + + _tb.DefineMethodOverride(impl, decl); + } + + private static ILGen/*!*/ DefineMethodOverride(TypeBuilder/*!*/ tb, MethodInfo/*!*/ decl) { + MethodBuilder impl; + return DefineMethodOverride(tb, decl, out impl); + } + + private static ILGen/*!*/ DefineMethodOverride(TypeBuilder/*!*/ tb, MethodInfo/*!*/ decl, out MethodBuilder/*!*/ impl) { + impl = tb.DefineMethod( + decl.Name, + decl.Attributes & ~(MethodAttributes.Abstract | MethodAttributes.ReservedMask), + decl.ReturnType, + ReflectionUtils.GetParameterTypes(decl.GetParameters()) + ); + + tb.DefineMethodOverride(impl, decl); + return new ILGen(impl.GetILGenerator()); + } + + private static ILGen/*!*/ DefinePrivateInterfaceMethodOverride(TypeBuilder/*!*/ tb, MethodInfo/*!*/ decl) { + MethodBuilder impl; + return DefinePrivateInterfaceMethodOverride(tb, decl, out impl); + } + + private static ILGen/*!*/ DefinePrivateInterfaceMethodOverride(TypeBuilder/*!*/ tb, MethodInfo/*!*/ decl, out MethodBuilder/*!*/ impl) { + string name = decl.DeclaringType.Name + "." + decl.Name; + //MethodAttributes attributes = decl.Attributes & ~(MethodAttributes.Abstract | MethodAttributes.ReservedMask | MethodAttributes.MemberAccessMask) + // | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Private; + MethodAttributes attributes = decl.Attributes & ~(MethodAttributes.Abstract | MethodAttributes.Public); + attributes |= MethodAttributes.NewSlot | MethodAttributes.Final; + impl = tb.DefineMethod( + name, + attributes, + decl.ReturnType, + ReflectionUtils.GetParameterTypes(decl.GetParameters()) + ); + + tb.DefineMethodOverride(impl, decl); + return new ILGen(impl.GetILGenerator()); + } + +#if !SILVERLIGHT // ICustomTypeDescriptor + private void DefineCustomTypeDescriptor() { + _tb.AddInterfaceImplementation(typeof(ICustomTypeDescriptor)); + + foreach (MethodInfo m in typeof(ICustomTypeDescriptor).GetMethods()) { + ImplementCTDOverride(m); + } + } + + private void ImplementCTDOverride(MethodInfo m) { + MethodBuilder builder; + ILGen il = DefinePrivateInterfaceMethodOverride(_tb, m, out builder); + il.EmitLoadArg(0); + + ParameterInfo[] pis = m.GetParameters(); + Type[] paramTypes = new Type[pis.Length + 1]; + paramTypes[0] = typeof(object); + for (int i = 0; i < pis.Length; i++) { + il.EmitLoadArg(i + 1); + paramTypes[i + 1] = pis[i].ParameterType; + } + + il.EmitCall(typeof(CustomTypeDescHelpers), m.Name, paramTypes); + il.EmitBoxing(m.ReturnType); + il.Emit(OpCodes.Ret); + } +#endif + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Generation/RubyTypeDispenser.cs b/merlin/main/languages/ruby/Ruby/Compiler/Generation/RubyTypeDispenser.cs new file mode 100644 index 0000000000..097a3b771b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Generation/RubyTypeDispenser.cs @@ -0,0 +1,141 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection.Emit; +using System.Text; +using System.Threading; +using Microsoft.Scripting; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Runtime; + +namespace IronRuby.Compiler.Generation { + internal static class RubyTypeDispenser { + private static readonly Publisher/*!*/ _newTypes; + private static readonly Dictionary/*!*/>/*!*/ _typeFeatures; + private static readonly ITypeFeature/*!*/[]/*!*/ _defaultFeatures = new ITypeFeature[2] { + RubyTypeBuilder.Feature, + InterfacesBuilder.MakeFeature(Type.EmptyTypes) + }; + + static RubyTypeDispenser() { + _newTypes = new Publisher(); + _typeFeatures = new Dictionary>(); + + AddSystemType(typeof(object), typeof(RubyObject)); + AddSystemType(typeof(RubyModule), typeof(RubyModule.Subclass)); + AddSystemType(typeof(MutableString), typeof(MutableString.Subclass)); + AddSystemType(typeof(Proc), typeof(Proc.Subclass)); + AddSystemType(typeof(RubyRegex), typeof(RubyRegex.Subclass)); + AddSystemType(typeof(Hash), typeof(Hash.Subclass)); + AddSystemType(typeof(RubyArray), typeof(RubyArray.Subclass)); + AddSystemType(typeof(MatchData), typeof(MatchData.Subclass)); + } + + internal static Type/*!*/ GetOrCreateType(Type/*!*/ baseType, IList/*!*/ interfaces) { + Assert.NotNull(baseType); + Assert.NotNull(interfaces); + + ITypeFeature[] features; + if (interfaces.Count == 0) { + features = _defaultFeatures; + } else { + features = new ITypeFeature[2] { + RubyTypeBuilder.Feature, + InterfacesBuilder.MakeFeature(interfaces) + }; + } + + TypeDescription typeInfo = new TypeDescription(baseType, features); + Type type = _newTypes.GetOrCreateValue(typeInfo, + delegate() { + if (TypeImplementsFeatures(baseType, features)) { + return baseType; + } + return CreateType(typeInfo); + }); + + Debug.Assert(typeof(IRubyObject).IsAssignableFrom(type)); + return type; + } + + internal static bool TryGetFeatures(Type/*!*/ type, out IList features) { + lock (_typeFeatures) { + return _typeFeatures.TryGetValue(type, out features); + } + } + + private static bool TypeImplementsFeatures(Type/*!*/ type, IList/*!*/ features) { + IList featuresFound; + if (TryGetFeatures(type, out featuresFound)) { + return TypeDescription.FeatureSetsMatch(features, featuresFound); + } + + foreach (ITypeFeature feature in features) { + if (!feature.IsImplementedBy(type)) { + return false; + } + } + return true; + } + + private static Type CreateType(TypeDescription/*!*/ typeInfo) { + Type baseType = typeInfo.BaseType; + if (baseType.IsSealed) { + throw new NotSupportedException("Can't inherit from a sealed type."); + } + + string typeName = GetName(baseType); + TypeBuilder tb = Snippets.Shared.DefinePublicType(typeName, baseType); + Utils.Log(typeName, "TYPE_BUILDER"); + + IFeatureBuilder[] _features = new IFeatureBuilder[typeInfo.Features.Count]; + ClsTypeEmitter emitter = new RubyTypeEmitter(tb); + for (int i = 0; i < typeInfo.Features.Count; i++) { + _features[i] = typeInfo.Features[i].MakeBuilder(tb); + } + + foreach (IFeatureBuilder feature in _features) { + feature.Implement(emitter); + } + emitter.OverrideMethods(baseType); + Type result = emitter.FinishType(); + lock (_typeFeatures) { + _typeFeatures.Add(result, typeInfo.Features); + } + return result; + } + + private static string GetName(Type/*!*/ baseType) { + // DLR appends a counter, so we don't need to + // TODO: Reflect feature set in the name? + StringBuilder name = new StringBuilder("IronRuby.Classes."); + name.Append(baseType.Name); + return name.ToString(); + } + + private static void AddSystemType(Type/*!*/ clsBaseType, Type/*!*/ rubyType) { + AddSystemType(clsBaseType, rubyType, _defaultFeatures); + } + private static void AddSystemType(Type/*!*/ clsBaseType, Type/*!*/ rubyType, ITypeFeature/*!*/[]/*!*/ features) { + _newTypes.GetOrCreateValue(new TypeDescription(clsBaseType, features), delegate() { return rubyType; }); + _typeFeatures[rubyType] = features; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Generation/RubyTypeEmitter.cs b/merlin/main/languages/ruby/Ruby/Compiler/Generation/RubyTypeEmitter.cs new file mode 100644 index 0000000000..7e44539da6 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Generation/RubyTypeEmitter.cs @@ -0,0 +1,160 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using Microsoft.Scripting; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Compiler.Generation { + public class RubyTypeEmitter : ClsTypeEmitter { + private FieldBuilder _classField; + + public RubyTypeEmitter(TypeBuilder tb) + : base(tb) { + } + + internal FieldBuilder ClassField { + get { return _classField; } + set { _classField = value; } + } + + public static bool TryGetNonInheritedMethodHelper(object clsObject, object instance, string/*!*/ name, out object callTarget) { + // In Ruby, this simply returns the instance object + // It's the callable site that's bound to the name through a RubyCallAction + // Properties are equivalent to Ruby getter and setter methods + RubyClass cls = (clsObject as RubyClass); + RubyMemberInfo method; + // TODO: visibility + if (cls == null || (method = cls.ResolveMethod(name, true)) == null || (method is RubyMethodGroupInfo)) { + callTarget = null; + return false; + } + callTarget = instance; + return true; + } + + protected override MethodInfo NonInheritedMethodHelper() { + return typeof(RubyTypeEmitter).GetMethod("TryGetNonInheritedMethodHelper"); + } + + protected override MethodInfo NonInheritedValueHelper() { + return typeof(RubyTypeEmitter).GetMethod("TryGetNonInheritedMethodHelper"); + } + + public static void AddRemoveEventHelper(object method, object instance, object dt, object eventValue, string name) { + throw new NotImplementedException(); + } + + protected override MethodInfo EventHelper() { + return typeof(RubyTypeEmitter).GetMethod("AddRemoveEventHelper"); + } + + protected override MethodInfo GetFastConvertMethod(Type toType) { + return RubyBinder.GetFastConvertMethod(toType); + } + + protected override MethodInfo GetGenericConvertMethod(Type toType) { + return RubyBinder.GetGenericConvertMethod(toType); + } + + public static Exception InvokeMethodMissing(object o, string/*!*/ name) { + return RubyExceptions.CreateMethodMissing(RubyContext._Default, o, name); + } + + protected override MethodInfo MissingInvokeMethodException() { + return typeof(RubyTypeEmitter).GetMethod("InvokeMethodMissing"); + } + + protected override MethodInfo ConvertToDelegate() { + return typeof(Converter).GetMethod("ConvertToDelegate"); + } + + protected override void EmitMakeCallAction(string name, int nargs, bool isList) { + ILGen cctor = GetCCtor(); + cctor.Emit(OpCodes.Ldstr, name); + cctor.EmitInt(nargs); + cctor.EmitCall(typeof(RubyCallAction), "Make", new Type[] { typeof(string), typeof(int) }); + } + + protected override void EmitPropertyGet(ILGen il, MethodInfo mi, string name, LocalBuilder callTarget) { + EmitClrCallStub(il, mi, callTarget, name); + } + + protected override void EmitPropertySet(ILGen il, MethodInfo mi, string name, LocalBuilder callTarget) { + EmitClrCallStub(il, mi, callTarget, name); + il.Emit(OpCodes.Pop); + } + + protected override void EmitImplicitContext(ILGen il) { + il.EmitLoadArg(0); + EmitClassObjectFromInstance(il); + il.EmitPropertyGet(typeof(RubyModule), "Context"); + } + + protected override void EmitClassObjectFromInstance(ILGen il) { + if (typeof(IRubyObject).IsAssignableFrom(BaseType)) { + il.EmitPropertyGet(typeof(IRubyObject), "Class"); + } else { + il.EmitFieldGet(_classField); + } + } + + protected override bool TryGetName(Type clrType, MethodInfo mi, out string name) { + name = RubyUtils.MangleName(mi.Name); + return true; + } + + protected override bool TryGetName(Type clrType, EventInfo ei, MethodInfo mi, out string name) { + // TODO: Determine naming convention? + name = RubyUtils.MangleName(ei.Name); + return true; + } + + protected override bool TryGetName(Type clrType, PropertyInfo pi, MethodInfo mi, out string name) { + if (mi.Name.StartsWith("get_")) { + name = RubyUtils.MangleName(pi.Name); + } else if (mi.Name.StartsWith("set_")) { + name = RubyUtils.MangleName(pi.Name) + "="; + } else { + name = null; + return false; + } + return true; + } + + protected override Type/*!*/[]/*!*/ MakeSiteSignature(int nargs) { + Type[] sig = new Type[nargs + 4]; + sig[0] = typeof(CallSite); + sig[1] = typeof(RubyContext); + for (int i = 2; i < sig.Length; i++) { + sig[i] = typeof(object); + } + return sig; + } + + protected override Type/*!*/ ContextType { + get { + return typeof(RubyContext); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Generation/TypeDescription.cs b/merlin/main/languages/ruby/Ruby/Compiler/Generation/TypeDescription.cs new file mode 100644 index 0000000000..ace3d6ea1b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Generation/TypeDescription.cs @@ -0,0 +1,85 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler.Generation { + + /// + /// TypeDescription captures the minimal information required by TypeDispenser to define a distinct CLS type + /// + internal sealed class TypeDescription { + // The CLI base-type. + private readonly Type/*!*/ _baseType; + + private readonly IList/*!*/ _features; + private int _hash; + + public TypeDescription(Type/*!*/ baseType, IList/*!*/ features) { + Assert.NotNull(baseType); + Assert.NotNull(features); + + _baseType = baseType; + _features = features; + + _hash = _baseType.GetHashCode(); + for (int i = 0; i < features.Count; i++) { + _hash ^= features[i].GetHashCode(); + } + } + + public Type/*!*/ BaseType { + get { return _baseType; } + } + + public IList/*!*/ Features { + get { return _features; } + } + + public override int GetHashCode() { + return _hash; + } + + public static bool FeatureSetsMatch(IList/*!*/ f1, IList/*!*/ f2) { + if (f1.Count != f2.Count || f1.GetHashCode() != f2.GetHashCode()) { + return false; + } + + // The size of a feature set is expected to be small enough that this should be + // reasonably fast. If in the future, sets grows much larger -- perhaps + // because we expose a large number of CLS methods -- then the representation of + // a feature set may need to be modified to allow for faster indexing + foreach (ITypeFeature feature in f2) { + if (!f1.Contains(feature)) { + return false; + } + } + + return true; + } + + public override bool Equals(object obj) { + TypeDescription other = obj as TypeDescription; + if (other == null) return false; + + if (_baseType.Equals(other._baseType) && FeatureSetsMatch(Features, other.Features)) { + return true; + } + return false; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Parser/GPPG.cs b/merlin/main/languages/ruby/Ruby/Compiler/Parser/GPPG.cs new file mode 100644 index 0000000000..c4663bb2cf --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Parser/GPPG.cs @@ -0,0 +1,760 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using Microsoft.Scripting.Utils; +using System.Text; +using System.Threading; + +namespace IronRuby.Compiler { + + #region State + + [Serializable] + public sealed class State { +#if DEBUG + private int _id; + public int Id { get { return _id; } set { _id = value; } } +#endif + + // State x Terminal -> ERROR + SHIFT(State) + REDUCE(State) + ACCEPT + // + // SHIFT > 0 + // ERROR == 0 + // REDUCE < 0 + // ACCEPT == -1 + private readonly Dictionary _actions; + + // State x NonTerminal -> State + private readonly Dictionary _gotos; + + // ParseAction - default action if terminal not in _actions dict + private readonly int _defaultAction; + + public int DefaultAction { + get { return _defaultAction; } + } + + public Dictionary GotoStates { + get { return _gotos; } + } + + public Dictionary Actions { + get { return _actions; } + } + + public State(Dictionary actions, Dictionary gotos, int defaultAction) { + _actions = actions; + _gotos = gotos; + _defaultAction = defaultAction; + } + } + + #endregion + + #region Stack + + public class ParserStack { + private T[]/*!*/ _array = new T[1]; + private int _top = 0; + + public void Push(T value) { + if (_top >= _array.Length) { + T[] newarray = new T[_array.Length * 2]; + System.Array.Copy(_array, newarray, _top); + _array = newarray; + } + _array[_top++] = value; + } + + public T Pop() { + return _array[--_top]; + } + + public void Pop(int depth) { + _top -= depth; + } + + public T Peek(int depth) { + return _array[_top - depth]; + } + + public bool IsEmpty() { + return _top == 0; + } + + public IEnumerable/*!*/ GetEnumerator() { + return _array; + } + } + + #endregion + + #region ParserTables + + public sealed class ParserTables { + public State[] States; + public byte[] RuleLhsNonTerminals; + public byte[] RuleRhsLengths; + public int ErrorToken; + public int EofToken; + +#if DEBUG // Metadata + internal string[] NonTerminalNames; + + // concatenated symbols of rule RHSs; + // symbol < 0 represents a non-terminal + // symbol >= 0 represents a terminal + internal short[] RuleRhsSymbols; + + // rule index -> index in RuleRhsSymbols array (calculated): + internal ushort[] RuleRhsSymbolIndexes; +#endif + } + + #endregion + + #region IParserLogger + + internal interface IParserLogger { + void BeforeReduction(int ruleId); + void BeforeShift(int stateId, int tokenId, bool isErrorShift); + void BeforeGoto(int stateId, int ruleId); + void StateEntered(); + void NextToken(int tokenId); + } + + #endregion + + #region ShiftReduceParser + + public abstract class ShiftReduceParser + where TValue : struct { + + private static ParserTables _tables; + private static readonly object _tablesLock = new object(); + + protected TValue yyval; + protected TLocation yyloc; + + // Experimental : last yylloc prior to call of yylex() + private TLocation _lastTokenSpan; + + private int _nextToken; + private State _currentState; + + private bool _recovering; + private int _tokensSinceLastError; + + private ParserStack/*!*/ _stateStack; + private ParserStack/*!*/ _valueStack; + private ParserStack/*!*/ _locationStack; + private int _errorToken; + private int _eofToken; + + private State[] _states; + private byte[] _ruleLhsNonTerminals; + private byte[] _ruleRhsLengths; + +#if DEBUG + // test hooks: + internal State CurrentState { get { return _currentState; } } + internal ParserStack/*!*/ StateStack { get { return _stateStack; } } + internal ParserStack/*!*/ ValueStack { get { return _valueStack; } } + internal ParserStack/*!*/ LocationStack { get { return _locationStack; } } + internal State[] States { get { return _states; } } + internal byte[] RuleLhsNonTerminals { get { return _ruleLhsNonTerminals; } } + internal byte[] RuleRhsLengths { get { return _ruleRhsLengths; } } + internal ParserTables Tables { get { return _tables; } } +#endif + + public ShiftReduceParser() { + _stateStack = new ParserStack(); + _valueStack = new ParserStack(); + _locationStack = new ParserStack(); + + if (_tables == null) { + lock (_tablesLock) { + if (_tables == null) { + ParserTables tables = new ParserTables(); + Initialize(tables); +#if DEBUG + InitializeMetadata(tables); + InitializeRulesMetadata(tables); +#endif + Thread.MemoryBarrier(); + _tables = tables; + } + } + } + + _states = _tables.States; + _ruleLhsNonTerminals = _tables.RuleLhsNonTerminals; + _ruleRhsLengths = _tables.RuleRhsLengths; + _errorToken = _tables.ErrorToken; + _eofToken = _tables.EofToken; + } + + protected abstract void Initialize(ParserTables/*!*/ tables); + protected abstract TLocation MergeLocations(TLocation start, TLocation end); + + protected abstract TValue TokenValue { get; } // lexical value: set by scanner + protected abstract TLocation TokenSpan { get; } // location value: set by scanner + protected abstract TLocation DefaultTokenSpan { get; } + + protected abstract int GetNextToken(); + protected abstract void ReportSyntaxError(string message); + + protected State[]/*!*/ BuildStates(short[]/*!*/ data) { + Debug.Assert(data != null && data.Length > 0); + + // + // serialized structure: + // + // length, + // ( + // (action_count: positive short, goto_count: positive short) | (action_count: negative short), + // (key: short, value: short){action_count} | (defaultAction: short), + // (key: short, value: short){goto_count} + // ){length} + // + // where action_count is + // > 0 ... a number of items in actions hashtable + // == 0 ... there is no action hashtable, but there is a single integer default action id + // < 0 ... there is no action hashtable and no goto table, the value is default action id + // goto_count is a number of items in gotos hashtable, + // zero means there is no goto hashtable + // + + int offset = 0; + State[] states = new State[data[offset++]]; + + for (int i = 0; i < states.Length; i++) { + int actionCount = data[offset++]; + + Dictionary actions = null; + Dictionary gotos = null; + int defaultAction = 0; + + if (actionCount >= 0) { + int gotoCount = data[offset++]; + Debug.Assert(gotoCount >= 0); + + if (actionCount > 0) { + actions = new Dictionary(actionCount); + for (int j = 0; j < actionCount; j++) { + actions.Add(data[offset++], data[offset++]); + } + } else { + defaultAction = data[offset++]; + } + + if (gotoCount > 0) { + gotos = new Dictionary(gotoCount); + for (int j = 0; j < gotoCount; j++) { + Debug.Assert(data[offset] < 0); + gotos.Add(-data[offset++], data[offset++]); + } + } + } else { + defaultAction = actionCount; + } + + states[i] = new State(actions, gotos, defaultAction); +#if DEBUG + states[i].Id = i; +#endif + } + + return states; + } + + public bool Parse() { + + _nextToken = 0; + _currentState = _states[0]; + _lastTokenSpan = TokenSpan; + + _stateStack.Push(_currentState); + _valueStack.Push(yyval); + _locationStack.Push(yyloc); + + while (true) { + + LogStateEntered(); + + int action = _currentState.DefaultAction; + + if (_currentState.Actions != null) { + if (_nextToken == 0) { + + // We save the last token span, so that the location span + // of production right hand sides that begin or end with a + // nullable production will be correct. + _lastTokenSpan = TokenSpan; + _nextToken = GetNextToken(); + } + + LogNextToken(_nextToken); + + _currentState.Actions.TryGetValue(_nextToken, out action); + } + + if (action > 0) { + LogBeforeShift(action, _nextToken, false); + Shift(action); + } else if (action < 0) { + Reduce(-action - 1); + + if (action == -1) // accept + return true; + } else if (action == 0) { + // error + if (!ErrorRecovery()) + return false; + } + } + } + + protected void Shift(int stateId) { + _currentState = _states[stateId]; + + _valueStack.Push(TokenValue); + _stateStack.Push(_currentState); + _locationStack.Push(TokenSpan); + + if (_recovering) { + if (_nextToken != _errorToken) { + _tokensSinceLastError++; + } + + if (_tokensSinceLastError > 5) { + _recovering = false; + } + } + + if (_nextToken != _eofToken) { + _nextToken = 0; + } + } + + + protected void Reduce(int ruleId) { + LogBeforeReduction(ruleId); + + int rhsLength = _ruleRhsLengths[ruleId]; + + // + // Default action "$$ = $1" for unit productions. + // Default action "@$ = @1.Merge(@N)" for location info. + // + if (rhsLength == 1) { + yyval = _valueStack.Peek(1); // default action: $$ = $1; + yyloc = _locationStack.Peek(1); + } else { + yyval = new TValue(); + if (rhsLength == 0) { + // The location span for an empty production will start with the + // beginning of the next lexeme, and end with the finish of the + // previous lexeme. This gives the correct behaviour when this + // nonsense value is used in later Merge operations. + yyloc = MergeLocations(_lastTokenSpan, TokenSpan); + } else { + TLocation at1 = GetLocation(rhsLength); + TLocation atN = GetLocation(1); + if (at1 != null && atN != null) { + yyloc = MergeLocations(at1, atN); + } + } + } + + DoAction(ruleId); + + _stateStack.Pop(rhsLength); + _valueStack.Pop(rhsLength); + _locationStack.Pop(rhsLength); + + _currentState = _stateStack.Peek(1); + + int gotoState; + if (_currentState.GotoStates.TryGetValue(_ruleLhsNonTerminals[ruleId], out gotoState)) { + LogBeforeGoto(gotoState, ruleId); + _currentState = _states[gotoState]; + } + + _stateStack.Push(_currentState); + _valueStack.Push(yyval); + _locationStack.Push(yyloc); + } + + + protected abstract void DoAction(int action_nr); + + public bool ErrorRecovery() { + bool discard; + + if (!_recovering) { // if not recovering from previous error + ReportSyntaxError(GetSyntaxErrorMessage()); + } + + if (!FindErrorRecoveryState()) + return false; + + // + // The interim fix for the "looping in error recovery" + // artifact involved moving the setting of the recovering + // bool until after invalid tokens have been discarded. + // + ShiftErrorToken(); + discard = DiscardInvalidTokens(); + _recovering = true; + _tokensSinceLastError = 0; + return discard; + } + + private string GetSyntaxErrorMessage() { + StringBuilder errorMsg = new StringBuilder(); + errorMsg.AppendFormat("syntax error, unexpected {0}", Parser.TerminalToString(_nextToken)); + + if (_currentState.Actions.Count < 7) { + bool first = true; + foreach (int terminal in _currentState.Actions.Keys) { + if (first) { + errorMsg.Append(", expecting "); + } else { + errorMsg.Append(", or "); + } + + errorMsg.Append(Parser.TerminalToString(terminal)); + first = false; + } + } + return errorMsg.ToString(); + } + + public void ShiftErrorToken() { + int oldNext = _nextToken; + _nextToken = _errorToken; + + int state = _currentState.Actions[_nextToken]; + LogBeforeShift(state, _nextToken, true); + Shift(state); + + _nextToken = oldNext; + } + + + public bool FindErrorRecoveryState() { + // pop states until one found that accepts error token + while (true) { + + // shift + int action; + if (_currentState.Actions != null && _currentState.Actions.TryGetValue(_errorToken, out action) && action > 0) { + return true; + } + + // LogState("Error, popping state", _stateStack.Peek(1)); + + _stateStack.Pop(); + _valueStack.Pop(); + _locationStack.Pop(); + + if (_stateStack.IsEmpty()) { + // Log("Aborting: didn't find a state that accepts error token"); + return false; + } else { + _currentState = _stateStack.Peek(1); + } + } + } + + + public bool DiscardInvalidTokens() { + + int action = _currentState.DefaultAction; + + if (_currentState.Actions != null) { + + // Discard tokens until find one that works ... + while (true) { + if (_nextToken == 0) { + _nextToken = GetNextToken(); + } + + LogNextToken(_nextToken); + + if (_nextToken == _eofToken) + return false; + + _currentState.Actions.TryGetValue(_nextToken, out action); + + if (action != 0) { + return true; + } + + // LogToken("Error, discarding token", _nextToken); + _nextToken = 0; + } + + } else if (_recovering && _tokensSinceLastError == 0) { + // + // Boolean recovering is not set until after the first + // error token has been shifted. Thus if we get back + // here with recovering set and no tokens read we are + // looping on the same error recovery action. This + // happens if current_state.parser_table is null because + // the state has an LR(0) reduction, but not all + // lookahead tokens are valid. This only occurs for + // error productions that *end* on "error". + // + // This action discards tokens one at a time until + // the looping stops. Another attack would be to always + // use the LALR(1) table if a production ends on "error" + // + // LogToken("Error, panic discard of {0}", _nextToken); + _nextToken = 0; + return true; + } else { + return true; + } + } + + protected TValue GetValue(int depth) { + return _valueStack.Peek(depth); + } + + protected TLocation GetLocation(int depth) { + return _locationStack.Peek(depth); + } + + protected void ClearInput() { + // experimental in this version. + _nextToken = 0; + } + + protected void StopErrorRecovery() { + _recovering = false; + } + + #region Debug Logging + +#if DEBUG + private IParserLogger _logger; +#endif + + [Conditional("DEBUG")] + internal void EnableLogging(IParserLogger/*!*/ logger) { +#if DEBUG + Assert.NotNull(logger); + _logger = logger; +#endif + } + + [Conditional("DEBUG")] + internal void DisableLogging() { +#if DEBUG + _logger = null; +#endif + } + + [Conditional("DEBUG")] + private void LogStateEntered() { +#if DEBUG + if (_logger != null) _logger.StateEntered(); +#endif + } + + [Conditional("DEBUG")] + private void LogNextToken(int tokenId) { +#if DEBUG + if (_logger != null) _logger.NextToken(tokenId); +#endif + } + + [Conditional("DEBUG")] + private void LogBeforeReduction(int ruleId) { +#if DEBUG + if (_logger != null) _logger.BeforeReduction(ruleId); +#endif + } + + [Conditional("DEBUG")] + private void LogBeforeShift(int stateId, int tokenId, bool isErrorShift) { +#if DEBUG + if (_logger != null) _logger.BeforeShift(stateId, tokenId, isErrorShift); +#endif + } + + [Conditional("DEBUG")] + private void LogBeforeGoto(int stateId, int ruleId) { +#if DEBUG + if (_logger != null) _logger.BeforeGoto(stateId, ruleId); +#endif + } + + #endregion + + #region Parser Reflection + +#if DEBUG + protected abstract void InitializeMetadata(ParserTables/*!*/ tables); + + private static void InitializeRulesMetadata(ParserTables/*!*/ tables) { + ushort[] indexes = new ushort[tables.RuleRhsLengths.Length]; + ushort index = 0; + for (int i = 0; i < indexes.Length; i++) { + indexes[i] = index; + index += tables.RuleRhsLengths[i]; + } + tables.RuleRhsSymbolIndexes = indexes; + } + + // SHIFT > 0 + // ERROR == 0 + // REDUCE < 0 + // ACCEPT == -1 + internal string ActionToString(int action) { + if (action > 0) return "S(" + action + ")"; + if (action == 0) return ""; + if (action == -1) return "ACCEPT"; + return "R(" + (-action) + ")"; + } + + internal string NonTerminalToString(int nonTerminal) { + Debug.Assert(nonTerminal > 0); + return _tables.NonTerminalNames[nonTerminal]; + } + + // < 0 -> non-terminal + // > 0 -> terminal + internal string SymbolToString(int symbol) { + return (symbol < 0) ? NonTerminalToString(-symbol) : Parser.TerminalToString(symbol); + } + + internal string RuleToString(int ruleIndex) { + Debug.Assert(ruleIndex >= 0); + StringBuilder sb = new StringBuilder(); + sb.Append(NonTerminalToString(_tables.RuleLhsNonTerminals[ruleIndex])); + sb.Append(" -> "); + + // index of the first RHS symbol: + int rhsLength = _tables.RuleRhsLengths[ruleIndex]; + if (rhsLength > 0) { + int first = _tables.RuleRhsSymbolIndexes[ruleIndex]; + for (int i = 0; i < rhsLength; i++) { + sb.Append(SymbolToString(_tables.RuleRhsSymbols[first + i])); + sb.Append(" "); + } + } else { + sb.Append(""); + } + + return sb.ToString(); + } +#endif + + [Conditional("DEBUG")] + public void DumpTables(TextWriter/*!*/ output) { +#if DEBUG + Dictionary terminals = new Dictionary(); + Dictionary nonterminals = new Dictionary(); + + int termCount = -1; + int ntermCount = -1; + for (int q = 0; q < _states.Length; q++) { + State s = _states[q]; + if (s.Actions != null) { + foreach (int t in s.Actions.Keys) { + if (t > termCount) { + termCount = t; + } + + terminals[t] = true; + } + } + + if (s.GotoStates != null) { + foreach (int t in s.GotoStates.Keys) { + if (t > ntermCount) { + ntermCount = t; + } + nonterminals[t] = true; + } + } + } + + output.WriteLine("States x (Terms + NonTerms) = {0} x ({1} + {2})", _states.Length, termCount, ntermCount); + + output.Write("State,"); + output.Write("Default,"); + for (int t = 0; t < termCount; t++) { + if (terminals.ContainsKey(t)) { + output.Write(Parser.TerminalToString(t)); + output.Write(","); + } + } + + for (int t = 0; t < ntermCount; t++) { + if (nonterminals.ContainsKey(t)) { + output.Write(t); // TODO + output.Write(","); + } + } + + for (int q = 0; q < _states.Length; q++) { + State s = _states[q]; + output.Write(q); + output.Write(","); + if (s.Actions == null) { + output.Write(ActionToString(s.DefaultAction)); + } + output.Write(","); + + for (int t = 0; t < termCount; t++) { + if (terminals.ContainsKey(t)) { + int action; + if (s.Actions != null) { + s.Actions.TryGetValue(t, out action); + output.Write(ActionToString(action)); + } + output.Write(","); + } + } + + for (int t = 0; t < ntermCount; t++) { + if (nonterminals.ContainsKey(t)) { + if (s.GotoStates != null) { + int state; + if (s.GotoStates.TryGetValue(t, out state)) { + output.Write(state); + } + } + output.Write(","); + } + } + output.WriteLine(); + } +#endif + } + + #endregion + } + + #endregion +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Parser/Generate.cmd b/merlin/main/languages/ruby/Ruby/Compiler/Parser/Generate.cmd new file mode 100644 index 0000000000..ccd9d290f5 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Parser/Generate.cmd @@ -0,0 +1,2 @@ +@rem tf checkout Parser.Generated.cs +..\..\..\..\..\Utilities\GPPG\gppg.exe Parser.y > Parser.Generated.cs diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Parser/LexicalScope.cs b/merlin/main/languages/ruby/Ruby/Compiler/Parser/LexicalScope.cs new file mode 100644 index 0000000000..f40197951d --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Parser/LexicalScope.cs @@ -0,0 +1,254 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Ast; +using MSA = System.Linq.Expressions; +using IronRuby.Runtime; + +namespace IronRuby.Compiler { + + // Scope contains variables defined outside of the current compilation unit. Used for assertion checks only. + // (e.g. created for variables in the runtime scope of eval). + internal sealed class RuntimeLexicalScope : LexicalScope { + public RuntimeLexicalScope(List/*!*/ names) + : base(null) { + + for (int i = 0; i < names.Count; i++) { + AddVariable(names[i], SourceSpan.None); + } + } + + protected override bool IsRuntimeScope { + get { return true; } + } + } + + public class LexicalScope : HybridStringDictionary { + private readonly LexicalScope _outerScope; + + internal LexicalScope(LexicalScope outerScope) { + _outerScope = outerScope; + } + + public LexicalScope OuterScope { + get { return _outerScope; } + } + + protected virtual bool IsRuntimeScope { + get { return false; } + } + + public LocalVariable/*!*/ AddVariable(string/*!*/ name, SourceSpan location) { + var var = new LocalVariable(name, location); + Add(name, var); + return var; + } + + public LocalVariable/*!*/ ResolveOrAddVariable(string/*!*/ name, SourceSpan location) { + var result = ResolveVariable(name); + + if (result != null) { + return result; + } + + return AddVariable(name, location); + } + + public LocalVariable ResolveVariable(string/*!*/ name) { + LexicalScope scope = this; + do { + LocalVariable result; + if (scope.TryGetValue(name, out result)) { + return result; + } + scope = scope.OuterScope; + } while (scope != null); + return null; + } + + #region Transformation + + internal void TransformLocals(ScopeBuilder/*!*/ locals) { + Assert.NotNull(locals); + Debug.Assert(!IsRuntimeScope); + + // Do not statically define variables defined in top-level eval'd code: + // + // eval('x = 1') <-- this variable needs to be defined in containing runtime scope, not in top-level eval scope + // eval('puts x') + // + // eval('1.times { x = 1 }') <-- x could be statically defined in the block since it is not visible outside the block + // + if (_outerScope == null || !_outerScope.IsRuntimeScope) { + foreach (var entry in this) { + entry.Value.TransformDefinition(locals); + } + } + } + + /// + /// Updates local variable table on this scope with transformed parameters. + /// + internal MSA.ParameterExpression[]/*!*/ TransformParameters(Parameters parameters, int hiddenParameterCount) { + + int paramCount = hiddenParameterCount; + + if (parameters == null) { + return new MSA.ParameterExpression[0]; + } + + if (parameters.Mandatory != null) { + paramCount += parameters.Mandatory.Count; + } + + if (parameters.Optional != null) { + paramCount += parameters.Optional.Count; + } + + if (parameters.Array != null) { + paramCount += 1; + } + + var result = new MSA.ParameterExpression[paramCount]; + + int dlrParamIndex = hiddenParameterCount; + + if (parameters.Mandatory != null) { + for (int i = 0; i < parameters.Mandatory.Count; i++) { + result[dlrParamIndex++] = parameters.Mandatory[i].TransformParameterDefinition(); + } + } + + if (parameters.Optional != null) { + for (int i = 0; i < parameters.Optional.Count; i++) { + result[dlrParamIndex++] = ((LocalVariable)parameters.Optional[i].Left).TransformParameterDefinition(); + } + } + + if (parameters.Array != null) { + result[dlrParamIndex++] = parameters.Array.TransformParameterDefinition(); + } + + Debug.Assert(dlrParamIndex == result.Length); + + return result; + } + + internal static void TransformParametersToSuperCall(AstGenerator/*!*/ gen, CallBuilder/*!*/ callBuilder, Parameters parameters) { + if (parameters == null) { + return; + } + + if (parameters.Mandatory != null) { + foreach (Variable v in parameters.Mandatory) { + callBuilder.Add(v.TransformRead(gen)); + } + } + + if (parameters.Optional != null) { + foreach (SimpleAssignmentExpression s in parameters.Optional) { + callBuilder.Add(s.Left.TransformRead(gen)); + } + } + + if (parameters.Array != null) { + callBuilder.SplattedArgument = parameters.Array.TransformRead(gen); + } + } + + #endregion + } + + #region HybridStringDictionary + + public class HybridStringDictionary : IEnumerable> { + // Number of variables in scopes during Rails startup: + // #variables 0 1 2 3 4 5 6+ + // #scopes 4587 3814 1994 794 608 220 295 + private const int ListLength = 4; + + private Dictionary _dict; + private KeyValuePair[] _list; + private int _size; + + public bool TryGetValue(string key, out TValue value) { + for (int i = 0; i < _size; i++) { + var entry = _list[i]; + if (entry.Key == key) { + value = entry.Value; + return true; + } + } + + if (_dict != null) { + return _dict.TryGetValue(key, out value); + } + + value = default(TValue); + return false; + } + + public void Add(string key, TValue value) { + if (_size > 0) { + if (_size < _list.Length) { + _list[_size++] = new KeyValuePair(key, value); + } else { + _dict = new Dictionary(); + for (int i = 0; i < _list.Length; i++) { + var entry = _list[i]; + _dict.Add(entry.Key, entry.Value); + } + _dict.Add(key, value); + _list = null; + _size = -1; + } + } else if (_size == 0) { + Debug.Assert(_list == null); + _list = new KeyValuePair[ListLength]; + _list[0] = new KeyValuePair(key, value); + _size = 1; + } else { + Debug.Assert(_size == -1 && _dict != null); + _dict.Add(key, value); + } + } + + IEnumerator>/*!*/ IEnumerable>.GetEnumerator() { + for (int i = 0; i < _size; i++) { + yield return _list[i]; + } + + if (_dict != null) { + foreach (var entry in _dict) { + yield return entry; + } + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return ((IEnumerable>)this).GetEnumerator(); + } + } + + #endregion +} + diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Parser/Parser.Generated.cs b/merlin/main/languages/ruby/Ruby/Compiler/Parser/Parser.Generated.cs new file mode 100644 index 0000000000..fefc61b59c --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Parser/Parser.Generated.cs @@ -0,0 +1,5611 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Ast; + +namespace IronRuby.Compiler +{ +public enum Tokens { +None = 127, Error = 128, + EndOfFile = 129, SingleLineComment = 130, MultiLineComment = 131, Whitespace = 132, InvalidCharacter = 133, EndOfLine = 134, WordSeparator = 135, Class = 136, + Module = 137, Def = 138, Undef = 139, Begin = 140, Rescue = 141, Ensure = 142, End = 143, If = 144, + Unless = 145, Then = 146, Elsif = 147, Else = 148, Case = 149, When = 150, While = 151, Until = 152, + For = 153, Break = 154, Next = 155, Redo = 156, Retry = 157, In = 158, Do = 159, LoopDo = 160, + BlockDo = 161, Return = 162, Yield = 163, Super = 164, Self = 165, Nil = 166, True = 167, False = 168, + And = 169, Or = 170, Not = 171, IfMod = 172, UnlessMod = 173, WhileMod = 174, UntilMod = 175, RescueMod = 176, + Alias = 177, Defined = 178, UppercaseBegin = 179, UppercaseEnd = 180, Line = 181, File = 182, Encoding = 183, Uplus = 184, + Uminus = 185, Pow = 186, Cmp = 187, Eq = 188, Eqq = 189, Neq = 190, Geq = 191, Leq = 192, + BitwiseAnd = 193, BitwiseOr = 194, Match = 195, Nmatch = 196, Dot2 = 197, Dot3 = 198, Aref = 199, Aset = 200, + Lshft = 201, Rshft = 202, SeparatingDoubleColon = 203, LeadingDoubleColon = 204, Assoc = 205, LeftParen = 206, StringEnd = 207, LparenArg = 208, + Lbrack = 209, Lbrace = 210, LbraceArg = 211, Star = 212, Ampersand = 213, Identifier = 214, FunctionIdentifier = 215, GlobalVariable = 216, + InstanceVariable = 217, ConstantIdentifier = 218, ClassVariable = 219, Assignment = 220, Integer = 221, BigInteger = 222, Float = 223, StringContent = 224, + MatchReference = 225, RegexpEnd = 226, StringEmbeddedVariableBegin = 227, StringEmbeddedCodeBegin = 228, StringBeg = 229, RegexpBeg = 230, ShellStringBegin = 231, WordsBeg = 232, + VerbatimWordsBegin = 233, Symbeg = 234, Lowest = 235, UminusNum = 236, LastToken = 237}; + +public partial struct TokenValue +#line 27 "Parser.y" + { } +public partial class Parser: ShiftReduceParser +{ + protected sealed override void Initialize(ParserTables tables) + { + tables.ErrorToken = (int)Tokens.Error; + tables.EofToken = (int)Tokens.EndOfFile; + + tables.States = BuildStates(new short[] { + 901, + 0,2, /* default action: */ -2, /* gotos: */ -1,1,-98,3, + 1,0, /* actions: */ 129,2, + /* default action: */ -1, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,129,-5, /* gotos: */ -4,4,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + /* default action: */ -3, + 11,3, /* actions: */ 59,695,10,696,129,-494,143,-494,125,-494,148,-494,147,-494,142,-494,150,-494,41,-494,141,-494, /* gotos: */ -99,6,-100,7,-123,804, + /* default action: */ -4, + 67,38, /* actions: */ 59,803,177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,129,-495,143,-495,125,-495,148,-495,147,-495,142,-495,150,-495,41,-495,141,-495, /* gotos: */ -2,8,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 16,0, /* actions: */ 172,9,173,25,174,604,175,606,176,608,59,-7,10,-7,129,-7,143,-7,125,-7,148,-7,147,-7,142,-7,150,-7,41,-7,141,-7, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,10,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 18,0, /* actions: */ 169,11,170,13,172,-46,173,-46,174,-46,175,-46,176,-46,59,-46,10,-46,129,-46,143,-46,125,-46,148,-46,147,-46,142,-46,150,-46,41,-46,141,-46, + 52,29, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,162,250,154,342,155,353,156,362,157,363, /* gotos: */ -12,12,-6,900,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509,-7,249,-8,361, + /* default action: */ -56, + 52,29, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,162,250,154,342,155,353,156,362,157,363, /* gotos: */ -12,14,-6,15,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509,-7,249,-8,361, + /* default action: */ -57, + /* default action: */ -59, + /* default action: */ -55, + 22,1, /* actions: */ 161,19,169,-63,170,-63,172,-63,173,-63,174,-63,175,-63,176,-63,59,-63,10,-63,129,-63,143,-63,125,-63,148,-63,147,-63,142,-63,150,-63,41,-63,141,-63,58,-63,146,-63,160,-63, /* gotos: */ -35,18, + /* default action: */ -360, + 0,1, /* default action: */ -358, /* gotos: */ -130,20, + 63,1, /* actions: */ 124,610,194,630,177,-354,139,-354,179,-354,180,-354,225,-354,162,-354,154,-354,155,-354,156,-354,157,-354,214,-354,218,-354,215,-354,221,-354,222,-354,223,-354,236,-354,234,-354,229,-354,231,-354,230,-354,232,-354,233,-354,217,-354,216,-354,219,-354,166,-354,165,-354,167,-354,168,-354,182,-354,181,-354,183,-354,204,-354,209,-354,210,-354,163,-354,178,-354,144,-354,145,-354,151,-354,152,-354,149,-354,153,-354,208,-354,206,-354,140,-354,136,-354,137,-354,138,-354,164,-354,171,-354,33,-354,184,-354,185,-354,126,-354,212,-354,128,-354,143,-354,59,-354,10,-354, /* gotos: */ -95,21, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,143,-5, /* gotos: */ -4,22,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 143,23, + /* default action: */ -359, + 16,0, /* actions: */ 172,9,173,25,174,604,175,606,176,608,59,-6,10,-6,129,-6,143,-6,125,-6,148,-6,147,-6,142,-6,150,-6,41,-6,141,-6, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,26,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 18,0, /* actions: */ 169,11,170,13,172,-47,173,-47,174,-47,175,-47,176,-47,59,-47,10,-47,129,-47,143,-47,125,-47,148,-47,147,-47,142,-47,150,-47,41,-47,141,-47, + 51,4, /* actions: */ 123,337,159,491,40,457,214,-269,217,-269,216,-269,218,-269,219,-269,166,-269,165,-269,167,-269,168,-269,182,-269,181,-269,183,-269,221,-269,222,-269,223,-269,236,-269,234,-269,229,-269,231,-269,230,-269,232,-269,233,-269,225,-269,215,-269,204,-269,209,-269,210,-269,163,-269,178,-269,144,-269,145,-269,151,-269,152,-269,149,-269,153,-269,208,-269,206,-269,140,-269,136,-269,137,-269,138,-269,164,-269,184,-269,185,-269,33,-269,126,-269,212,-269,213,-269, /* gotos: */ -41,28,-34,489,-38,490,-109,311, + 24,1, /* actions: */ 211,30,161,-70,169,-70,170,-70,172,-70,173,-70,174,-70,175,-70,176,-70,59,-70,10,-70,129,-70,143,-70,125,-70,148,-70,147,-70,142,-70,150,-70,41,-70,141,-70,58,-70,146,-70,160,-70,93,-70, /* gotos: */ -33,29, + /* default action: */ -71, + 0,1, /* default action: */ -68, /* gotos: */ -104,31, + 63,1, /* actions: */ 124,610,194,630,177,-354,139,-354,179,-354,180,-354,225,-354,162,-354,154,-354,155,-354,156,-354,157,-354,214,-354,218,-354,215,-354,221,-354,222,-354,223,-354,236,-354,234,-354,229,-354,231,-354,230,-354,232,-354,233,-354,217,-354,216,-354,219,-354,166,-354,165,-354,167,-354,168,-354,182,-354,181,-354,183,-354,204,-354,209,-354,210,-354,163,-354,178,-354,144,-354,145,-354,151,-354,152,-354,149,-354,153,-354,208,-354,206,-354,140,-354,136,-354,137,-354,138,-354,164,-354,171,-354,33,-354,184,-354,185,-354,126,-354,212,-354,128,-354,125,-354,59,-354,10,-354, /* gotos: */ -95,32, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,125,-5, /* gotos: */ -4,33,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 125,34, + /* default action: */ -69, + /* default action: */ -9, + 73,5, /* actions: */ 216,123,214,41,218,42,215,43,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70,181,72,182,73,183,74,179,75,180,76,177,77,169,78,140,79,154,80,149,81,136,82,138,83,178,84,159,85,148,86,147,87,143,88,142,89,168,90,153,91,158,92,137,93,155,94,166,95,171,96,170,97,156,98,141,99,157,100,162,101,165,102,164,103,146,104,167,105,139,106,150,107,163,108,172,109,173,110,174,111,175,112,176,113,234,115, /* gotos: */ -86,37,-84,40,-83,44,-105,71,-85,114, + 0,1, /* default action: */ -19, /* gotos: */ -103,38, + 72,5, /* actions: */ 214,41,218,42,215,43,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70,181,72,182,73,183,74,179,75,180,76,177,77,169,78,140,79,154,80,149,81,136,82,138,83,178,84,159,85,148,86,147,87,143,88,142,89,168,90,153,91,158,92,137,93,155,94,166,95,171,96,170,97,156,98,141,99,157,100,162,101,165,102,164,103,146,104,167,105,139,106,150,107,163,108,172,109,173,110,174,111,175,112,176,113,234,115, /* gotos: */ -86,39,-84,40,-83,44,-105,71,-85,114, + /* default action: */ -20, + /* default action: */ -115, + /* default action: */ -110, + /* default action: */ -111, + /* default action: */ -112, + /* default action: */ -113, + /* default action: */ -120, + /* default action: */ -121, + /* default action: */ -122, + /* default action: */ -123, + /* default action: */ -124, + /* default action: */ -125, + /* default action: */ -126, + /* default action: */ -127, + /* default action: */ -128, + /* default action: */ -129, + /* default action: */ -130, + /* default action: */ -131, + /* default action: */ -132, + /* default action: */ -133, + /* default action: */ -134, + /* default action: */ -135, + /* default action: */ -136, + /* default action: */ -137, + /* default action: */ -138, + /* default action: */ -139, + /* default action: */ -140, + /* default action: */ -141, + /* default action: */ -142, + /* default action: */ -143, + /* default action: */ -144, + /* default action: */ -145, + /* default action: */ -114, + /* default action: */ -146, + /* default action: */ -147, + /* default action: */ -148, + /* default action: */ -149, + /* default action: */ -150, + /* default action: */ -151, + /* default action: */ -152, + /* default action: */ -153, + /* default action: */ -154, + /* default action: */ -155, + /* default action: */ -156, + /* default action: */ -157, + /* default action: */ -158, + /* default action: */ -159, + /* default action: */ -160, + /* default action: */ -161, + /* default action: */ -162, + /* default action: */ -163, + /* default action: */ -164, + /* default action: */ -165, + /* default action: */ -166, + /* default action: */ -167, + /* default action: */ -168, + /* default action: */ -169, + /* default action: */ -170, + /* default action: */ -171, + /* default action: */ -172, + /* default action: */ -173, + /* default action: */ -174, + /* default action: */ -175, + /* default action: */ -176, + /* default action: */ -177, + /* default action: */ -178, + /* default action: */ -179, + /* default action: */ -180, + /* default action: */ -181, + /* default action: */ -182, + /* default action: */ -183, + /* default action: */ -184, + /* default action: */ -185, + /* default action: */ -186, + /* default action: */ -187, + /* default action: */ -116, + 75,5, /* actions: */ 214,41,218,42,215,43,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70,181,72,182,73,183,74,179,75,180,76,177,77,169,78,140,79,154,80,149,81,136,82,138,83,178,84,159,85,148,86,147,87,143,88,142,89,168,90,153,91,158,92,137,93,155,94,166,95,171,96,170,97,156,98,141,99,157,100,162,101,165,102,164,103,146,104,167,105,139,106,150,107,163,108,172,109,173,110,174,111,175,112,176,113,217,118,216,119,219,120,225,122, /* gotos: */ -79,116,-84,117,-83,44,-105,71,-76,121, + /* default action: */ -415, + /* default action: */ -416, + /* default action: */ -417, + /* default action: */ -418, + /* default action: */ -419, + /* default action: */ -420, + /* default action: */ -441, + 2,1, /* actions: */ 216,124,225,122, /* gotos: */ -76,125, + /* default action: */ -21, + /* default action: */ -22, + 72,6, /* actions: */ 214,41,218,42,215,43,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70,181,72,182,73,183,74,179,75,180,76,177,77,169,78,140,79,154,80,149,81,136,82,138,83,178,84,159,85,148,86,147,87,143,88,142,89,168,90,153,91,158,92,137,93,155,94,166,95,171,96,170,97,156,98,141,99,157,100,162,101,165,102,164,103,146,104,167,105,139,106,150,107,163,108,172,109,173,110,174,111,175,112,176,113,234,115, /* gotos: */ -30,127,-86,131,-84,40,-83,44,-105,71,-85,114, + 17,0, /* actions: */ 44,128,172,-10,173,-10,174,-10,175,-10,176,-10,59,-10,10,-10,129,-10,143,-10,125,-10,148,-10,147,-10,142,-10,150,-10,41,-10,141,-10, + 0,1, /* default action: */ -118, /* gotos: */ -106,129, + 72,5, /* actions: */ 214,41,218,42,215,43,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70,181,72,182,73,183,74,179,75,180,76,177,77,169,78,140,79,154,80,149,81,136,82,138,83,178,84,159,85,148,86,147,87,143,88,142,89,168,90,153,91,158,92,137,93,155,94,166,95,171,96,170,97,156,98,141,99,157,100,162,101,165,102,164,103,146,104,167,105,139,106,150,107,163,108,172,109,173,110,174,111,175,112,176,113,234,115, /* gotos: */ -86,130,-84,40,-83,44,-105,71,-85,114, + /* default action: */ -119, + /* default action: */ -117, + 0,1, /* default action: */ -11, /* gotos: */ -101,133, + 1,0, /* actions: */ 123,134, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,125,-5, /* gotos: */ -4,135,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 125,136, + /* default action: */ -12, + 0,1, /* default action: */ -13, /* gotos: */ -102,138, + 1,0, /* actions: */ 123,139, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,125,-5, /* gotos: */ -4,140,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 125,141, + /* default action: */ -14, + 50,0, /* actions: */ 220,143,46,-290,203,-290,91,-290,43,-290,45,-290,42,-290,47,-290,37,-290,186,-290,124,-290,94,-290,38,-290,187,-290,62,-290,191,-290,60,-290,192,-290,188,-290,189,-290,190,-290,195,-290,196,-290,201,-290,202,-290,193,-290,194,-290,197,-290,198,-290,63,-290,169,-290,170,-290,172,-290,173,-290,174,-290,175,-290,176,-290,59,-290,10,-290,129,-290,143,-290,125,-290,148,-290,147,-290,142,-290,150,-290,41,-290,141,-290,61,-104,44,-96, + 46,25, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518, /* gotos: */ -25,144,-18,145,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-93,200,-92,509, + /* default action: */ -15, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-198,170,-198,172,-198,173,-198,174,-198,175,-198,176,-198,59,-198,10,-198,129,-198,143,-198,125,-198,148,-198,147,-198,142,-198,150,-198,41,-198,141,-198,58,-198,146,-198,160,-198,205,-198,44,-198,93,-198,211,-198,161,-198, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,147,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,-199,45,-199,42,150,47,152,37,154,186,156,124,-199,94,-199,38,-199,187,-199,62,-199,191,-199,60,-199,192,-199,188,-199,189,-199,190,-199,195,-199,196,-199,201,-199,202,-199,193,-199,194,-199,197,-199,198,-199,63,-199,169,-199,170,-199,172,-199,173,-199,174,-199,175,-199,176,-199,59,-199,10,-199,129,-199,143,-199,125,-199,148,-199,147,-199,142,-199,150,-199,41,-199,141,-199,58,-199,146,-199,160,-199,205,-199,44,-199,93,-199,211,-199,161,-199, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,149,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,-200,45,-200,42,150,47,152,37,154,186,156,124,-200,94,-200,38,-200,187,-200,62,-200,191,-200,60,-200,192,-200,188,-200,189,-200,190,-200,195,-200,196,-200,201,-200,202,-200,193,-200,194,-200,197,-200,198,-200,63,-200,169,-200,170,-200,172,-200,173,-200,174,-200,175,-200,176,-200,59,-200,10,-200,129,-200,143,-200,125,-200,148,-200,147,-200,142,-200,150,-200,41,-200,141,-200,58,-200,146,-200,160,-200,205,-200,44,-200,93,-200,211,-200,161,-200, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,151,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,-201,45,-201,42,-201,47,-201,37,-201,186,156,124,-201,94,-201,38,-201,187,-201,62,-201,191,-201,60,-201,192,-201,188,-201,189,-201,190,-201,195,-201,196,-201,201,-201,202,-201,193,-201,194,-201,197,-201,198,-201,63,-201,169,-201,170,-201,172,-201,173,-201,174,-201,175,-201,176,-201,59,-201,10,-201,129,-201,143,-201,125,-201,148,-201,147,-201,142,-201,150,-201,41,-201,141,-201,58,-201,146,-201,160,-201,205,-201,44,-201,93,-201,211,-201,161,-201, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,153,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,-202,45,-202,42,-202,47,-202,37,-202,186,156,124,-202,94,-202,38,-202,187,-202,62,-202,191,-202,60,-202,192,-202,188,-202,189,-202,190,-202,195,-202,196,-202,201,-202,202,-202,193,-202,194,-202,197,-202,198,-202,63,-202,169,-202,170,-202,172,-202,173,-202,174,-202,175,-202,176,-202,59,-202,10,-202,129,-202,143,-202,125,-202,148,-202,147,-202,142,-202,150,-202,41,-202,141,-202,58,-202,146,-202,160,-202,205,-202,44,-202,93,-202,211,-202,161,-202, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,155,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,-203,45,-203,42,-203,47,-203,37,-203,186,156,124,-203,94,-203,38,-203,187,-203,62,-203,191,-203,60,-203,192,-203,188,-203,189,-203,190,-203,195,-203,196,-203,201,-203,202,-203,193,-203,194,-203,197,-203,198,-203,63,-203,169,-203,170,-203,172,-203,173,-203,174,-203,175,-203,176,-203,59,-203,10,-203,129,-203,143,-203,125,-203,148,-203,147,-203,142,-203,150,-203,41,-203,141,-203,58,-203,146,-203,160,-203,205,-203,44,-203,93,-203,211,-203,161,-203, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,157,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,-204,45,-204,42,-204,47,-204,37,-204,186,156,124,-204,94,-204,38,-204,187,-204,62,-204,191,-204,60,-204,192,-204,188,-204,189,-204,190,-204,195,-204,196,-204,201,-204,202,-204,193,-204,194,-204,197,-204,198,-204,63,-204,169,-204,170,-204,172,-204,173,-204,174,-204,175,-204,176,-204,59,-204,10,-204,129,-204,143,-204,125,-204,148,-204,147,-204,142,-204,150,-204,41,-204,141,-204,58,-204,146,-204,160,-204,205,-204,44,-204,93,-204,211,-204,161,-204, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,159,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,-210,94,-210,38,162,187,-210,62,-210,191,-210,60,-210,192,-210,188,-210,189,-210,190,-210,195,-210,196,-210,201,184,202,186,193,-210,194,-210,197,-210,198,-210,63,-210,169,-210,170,-210,172,-210,173,-210,174,-210,175,-210,176,-210,59,-210,10,-210,129,-210,143,-210,125,-210,148,-210,147,-210,142,-210,150,-210,41,-210,141,-210,58,-210,146,-210,160,-210,205,-210,44,-210,93,-210,211,-210,161,-210, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,161,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,-211,94,-211,38,162,187,-211,62,-211,191,-211,60,-211,192,-211,188,-211,189,-211,190,-211,195,-211,196,-211,201,184,202,186,193,-211,194,-211,197,-211,198,-211,63,-211,169,-211,170,-211,172,-211,173,-211,174,-211,175,-211,176,-211,59,-211,10,-211,129,-211,143,-211,125,-211,148,-211,147,-211,142,-211,150,-211,41,-211,141,-211,58,-211,146,-211,160,-211,205,-211,44,-211,93,-211,211,-211,161,-211, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,163,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,-212,94,-212,38,-212,187,-212,62,-212,191,-212,60,-212,192,-212,188,-212,189,-212,190,-212,195,-212,196,-212,201,184,202,186,193,-212,194,-212,197,-212,198,-212,63,-212,169,-212,170,-212,172,-212,173,-212,174,-212,175,-212,176,-212,59,-212,10,-212,129,-212,143,-212,125,-212,148,-212,147,-212,142,-212,150,-212,41,-212,141,-212,58,-212,146,-212,160,-212,205,-212,44,-212,93,-212,211,-212,161,-212, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,165,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,-213,194,-213,197,-213,198,-213,63,-213,169,-213,170,-213,172,-213,173,-213,174,-213,175,-213,176,-213,59,-213,10,-213,129,-213,143,-213,125,-213,148,-213,147,-213,142,-213,150,-213,41,-213,141,-213,58,-213,146,-213,160,-213,205,-213,44,-213,93,-213,211,-213,161,-213, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,167,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,-214,62,-214,191,-214,60,-214,192,-214,188,-214,189,-214,190,-214,195,-214,196,-214,201,184,202,186,193,-214,194,-214,197,-214,198,-214,63,-214,169,-214,170,-214,172,-214,173,-214,174,-214,175,-214,176,-214,59,-214,10,-214,129,-214,143,-214,125,-214,148,-214,147,-214,142,-214,150,-214,41,-214,141,-214,58,-214,146,-214,160,-214,205,-214,44,-214,93,-214,211,-214,161,-214, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,169,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,-215,62,-215,191,-215,60,-215,192,-215,188,-215,189,-215,190,-215,195,-215,196,-215,201,184,202,186,193,-215,194,-215,197,-215,198,-215,63,-215,169,-215,170,-215,172,-215,173,-215,174,-215,175,-215,176,-215,59,-215,10,-215,129,-215,143,-215,125,-215,148,-215,147,-215,142,-215,150,-215,41,-215,141,-215,58,-215,146,-215,160,-215,205,-215,44,-215,93,-215,211,-215,161,-215, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,171,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,-216,62,-216,191,-216,60,-216,192,-216,188,-216,189,-216,190,-216,195,-216,196,-216,201,184,202,186,193,-216,194,-216,197,-216,198,-216,63,-216,169,-216,170,-216,172,-216,173,-216,174,-216,175,-216,176,-216,59,-216,10,-216,129,-216,143,-216,125,-216,148,-216,147,-216,142,-216,150,-216,41,-216,141,-216,58,-216,146,-216,160,-216,205,-216,44,-216,93,-216,211,-216,161,-216, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,173,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,-217,62,-217,191,-217,60,-217,192,-217,188,-217,189,-217,190,-217,195,-217,196,-217,201,184,202,186,193,-217,194,-217,197,-217,198,-217,63,-217,169,-217,170,-217,172,-217,173,-217,174,-217,175,-217,176,-217,59,-217,10,-217,129,-217,143,-217,125,-217,148,-217,147,-217,142,-217,150,-217,41,-217,141,-217,58,-217,146,-217,160,-217,205,-217,44,-217,93,-217,211,-217,161,-217, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,175,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,-218,194,-218,197,-218,198,-218,63,-218,169,-218,170,-218,172,-218,173,-218,174,-218,175,-218,176,-218,59,-218,10,-218,129,-218,143,-218,125,-218,148,-218,147,-218,142,-218,150,-218,41,-218,141,-218,58,-218,146,-218,160,-218,205,-218,44,-218,93,-218,211,-218,161,-218, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,177,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,-219,194,-219,197,-219,198,-219,63,-219,169,-219,170,-219,172,-219,173,-219,174,-219,175,-219,176,-219,59,-219,10,-219,129,-219,143,-219,125,-219,148,-219,147,-219,142,-219,150,-219,41,-219,141,-219,58,-219,146,-219,160,-219,205,-219,44,-219,93,-219,211,-219,161,-219, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,179,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,-220,194,-220,197,-220,198,-220,63,-220,169,-220,170,-220,172,-220,173,-220,174,-220,175,-220,176,-220,59,-220,10,-220,129,-220,143,-220,125,-220,148,-220,147,-220,142,-220,150,-220,41,-220,141,-220,58,-220,146,-220,160,-220,205,-220,44,-220,93,-220,211,-220,161,-220, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,181,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,-221,194,-221,197,-221,198,-221,63,-221,169,-221,170,-221,172,-221,173,-221,174,-221,175,-221,176,-221,59,-221,10,-221,129,-221,143,-221,125,-221,148,-221,147,-221,142,-221,150,-221,41,-221,141,-221,58,-221,146,-221,160,-221,205,-221,44,-221,93,-221,211,-221,161,-221, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,183,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,-222,194,-222,197,-222,198,-222,63,-222,169,-222,170,-222,172,-222,173,-222,174,-222,175,-222,176,-222,59,-222,10,-222,129,-222,143,-222,125,-222,148,-222,147,-222,142,-222,150,-222,41,-222,141,-222,58,-222,146,-222,160,-222,205,-222,44,-222,93,-222,211,-222,161,-222, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,185,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,-225,94,-225,38,-225,187,-225,62,-225,191,-225,60,-225,192,-225,188,-225,189,-225,190,-225,195,-225,196,-225,201,-225,202,-225,193,-225,194,-225,197,-225,198,-225,63,-225,169,-225,170,-225,172,-225,173,-225,174,-225,175,-225,176,-225,59,-225,10,-225,129,-225,143,-225,125,-225,148,-225,147,-225,142,-225,150,-225,41,-225,141,-225,58,-225,146,-225,160,-225,205,-225,44,-225,93,-225,211,-225,161,-225, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,187,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,-226,94,-226,38,-226,187,-226,62,-226,191,-226,60,-226,192,-226,188,-226,189,-226,190,-226,195,-226,196,-226,201,-226,202,-226,193,-226,194,-226,197,-226,198,-226,63,-226,169,-226,170,-226,172,-226,173,-226,174,-226,175,-226,176,-226,59,-226,10,-226,129,-226,143,-226,125,-226,148,-226,147,-226,142,-226,150,-226,41,-226,141,-226,58,-226,146,-226,160,-226,205,-226,44,-226,93,-226,211,-226,161,-226, + 51,22, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,162,526,154,527,155,528,156,362,157,363, /* gotos: */ -18,189,-8,899,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,-227,194,-227,197,-227,198,-227,63,-227,169,-227,170,-227,172,-227,173,-227,174,-227,175,-227,176,-227,59,-227,10,-227,129,-227,143,-227,125,-227,148,-227,147,-227,142,-227,150,-227,41,-227,141,-227,58,-227,146,-227,160,-227,205,-227,44,-227,93,-227,211,-227,161,-227, + 51,22, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,162,526,154,527,155,528,156,362,157,363, /* gotos: */ -18,191,-8,898,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,-228,197,-228,198,-228,63,-228,169,-228,170,-228,172,-228,173,-228,174,-228,175,-228,176,-228,59,-228,10,-228,129,-228,143,-228,125,-228,148,-228,147,-228,142,-228,150,-228,41,-228,141,-228,58,-228,146,-228,160,-228,205,-228,44,-228,93,-228,211,-228,161,-228, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,193,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,-231,169,-231,170,-231,172,-231,173,-231,174,-231,175,-231,176,-231,59,-231,10,-231,129,-231,143,-231,125,-231,148,-231,147,-231,142,-231,150,-231,41,-231,141,-231,58,-231,146,-231,160,-231,205,-231,44,-231,93,-231,211,-231,161,-231, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,195,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,-232,169,-232,170,-232,172,-232,173,-232,174,-232,175,-232,176,-232,59,-232,10,-232,129,-232,143,-232,125,-232,148,-232,147,-232,142,-232,150,-232,41,-232,141,-232,58,-232,146,-232,160,-232,205,-232,44,-232,93,-232,211,-232,161,-232, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,197,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 27,0, /* actions: */ 58,198,43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,199,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-234,170,-234,172,-234,173,-234,174,-234,175,-234,176,-234,59,-234,10,-234,129,-234,143,-234,125,-234,148,-234,147,-234,142,-234,150,-234,41,-234,141,-234,58,-234,146,-234,160,-234,205,-234,44,-234,93,-234,211,-234,161,-234, + 1,0, /* actions: */ 61,201, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,202,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 176,203,43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-188,170,-188,172,-188,173,-188,174,-188,175,-188,59,-188,10,-188,129,-188,143,-188,125,-188,148,-188,147,-188,142,-188,150,-188,41,-188,141,-188,58,-188,146,-188,160,-188,205,-188,44,-188,93,-188,211,-188,161,-188, + 51,22, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,162,526,154,527,155,528,156,362,157,363, /* gotos: */ -18,204,-8,205,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-189,170,-189,172,-189,173,-189,174,-189,175,-189,176,-189,59,-189,10,-189,129,-189,143,-189,125,-189,148,-189,147,-189,142,-189,150,-189,41,-189,141,-189,58,-189,146,-189,160,-189,205,-189,44,-189,93,-189,211,-189,161,-189, + /* default action: */ -190, + 57,0, /* actions: */ 61,-97,46,-439,203,-439,91,-439,43,-439,45,-439,42,-439,47,-439,37,-439,186,-439,124,-439,94,-439,38,-439,187,-439,62,-439,191,-439,60,-439,192,-439,188,-439,189,-439,190,-439,195,-439,196,-439,201,-439,202,-439,193,-439,194,-439,197,-439,198,-439,63,-439,169,-439,170,-439,172,-439,173,-439,174,-439,175,-439,176,-439,59,-439,10,-439,129,-439,143,-439,125,-439,148,-439,147,-439,142,-439,150,-439,41,-439,141,-439,58,-439,146,-439,160,-439,205,-439,44,-439,93,-439,211,-439,161,-439,220,-440, + 109,0, /* actions: */ 46,-427,203,-427,91,-427,43,-427,45,-427,42,-427,47,-427,37,-427,186,-427,124,-427,94,-427,38,-427,187,-427,62,-427,191,-427,60,-427,192,-427,188,-427,189,-427,190,-427,195,-427,196,-427,201,-427,202,-427,193,-427,194,-427,197,-427,198,-427,63,-427,169,-427,170,-427,172,-427,173,-427,174,-427,175,-427,176,-427,59,-427,10,-427,129,-427,61,-427,220,-427,44,-427,143,-427,125,-427,148,-427,147,-427,142,-427,150,-427,41,-427,141,-427,58,-427,146,-427,160,-427,205,-427,93,-427,211,-427,161,-427,158,-427,123,-482,159,-482,40,-482,214,-482,217,-482,216,-482,218,-482,219,-482,166,-482,165,-482,167,-482,168,-482,182,-482,181,-482,183,-482,221,-482,222,-482,223,-482,236,-482,234,-482,229,-482,231,-482,230,-482,232,-482,233,-482,225,-482,215,-482,204,-482,209,-482,210,-482,163,-482,178,-482,144,-482,145,-482,151,-482,152,-482,149,-482,153,-482,208,-482,206,-482,140,-482,136,-482,137,-482,138,-482,164,-482,184,-482,185,-482,33,-482,126,-482,212,-482,213,-482, + /* default action: */ -428, + /* default action: */ -429, + 109,0, /* actions: */ 46,-430,203,-430,91,-430,43,-430,45,-430,42,-430,47,-430,37,-430,186,-430,124,-430,94,-430,38,-430,187,-430,62,-430,191,-430,60,-430,192,-430,188,-430,189,-430,190,-430,195,-430,196,-430,201,-430,202,-430,193,-430,194,-430,197,-430,198,-430,63,-430,169,-430,170,-430,172,-430,173,-430,174,-430,175,-430,176,-430,59,-430,10,-430,129,-430,61,-430,220,-430,44,-430,143,-430,125,-430,148,-430,147,-430,142,-430,150,-430,41,-430,141,-430,58,-430,146,-430,160,-430,205,-430,93,-430,211,-430,161,-430,158,-430,123,-483,159,-483,40,-483,214,-483,217,-483,216,-483,218,-483,219,-483,166,-483,165,-483,167,-483,168,-483,182,-483,181,-483,183,-483,221,-483,222,-483,223,-483,236,-483,234,-483,229,-483,231,-483,230,-483,232,-483,233,-483,225,-483,215,-483,204,-483,209,-483,210,-483,163,-483,178,-483,144,-483,145,-483,151,-483,152,-483,149,-483,153,-483,208,-483,206,-483,140,-483,136,-483,137,-483,138,-483,164,-483,184,-483,185,-483,33,-483,126,-483,212,-483,213,-483, + /* default action: */ -431, + /* default action: */ -432, + /* default action: */ -433, + /* default action: */ -434, + /* default action: */ -435, + /* default action: */ -436, + /* default action: */ -437, + /* default action: */ -438, + 55,0, /* actions: */ 91,220,203,896,46,897,43,-235,45,-235,42,-235,47,-235,37,-235,186,-235,124,-235,94,-235,38,-235,187,-235,62,-235,191,-235,60,-235,192,-235,188,-235,189,-235,190,-235,195,-235,196,-235,201,-235,202,-235,193,-235,194,-235,197,-235,198,-235,63,-235,169,-235,170,-235,172,-235,173,-235,174,-235,175,-235,176,-235,59,-235,10,-235,129,-235,143,-235,125,-235,148,-235,147,-235,142,-235,150,-235,41,-235,141,-235,58,-235,146,-235,160,-235,205,-235,44,-235,93,-235,211,-235,161,-235, + 48,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,570,93,-236, /* gotos: */ -36,221,-27,302,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-19,559,-18,314,-93,200,-92,509,-51,567,-52,569, + 1,0, /* actions: */ 93,222, + 57,0, /* actions: */ 220,223,61,-98,46,-294,203,-294,91,-294,43,-294,45,-294,42,-294,47,-294,37,-294,186,-294,124,-294,94,-294,38,-294,187,-294,62,-294,191,-294,60,-294,192,-294,188,-294,189,-294,190,-294,195,-294,196,-294,201,-294,202,-294,193,-294,194,-294,197,-294,198,-294,63,-294,169,-294,170,-294,172,-294,173,-294,174,-294,175,-294,176,-294,59,-294,10,-294,129,-294,143,-294,125,-294,148,-294,147,-294,142,-294,150,-294,41,-294,141,-294,58,-294,146,-294,160,-294,205,-294,44,-294,93,-294,211,-294,161,-294, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,224,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-192,170,-192,172,-192,173,-192,174,-192,175,-192,176,-192,59,-192,10,-192,129,-192,143,-192,125,-192,148,-192,147,-192,142,-192,150,-192,41,-192,141,-192,58,-192,146,-192,160,-192,205,-192,44,-192,93,-192,211,-192,161,-192, + /* default action: */ -281, + /* default action: */ -421, + /* default action: */ -422, + /* default action: */ -423, + 3,0, /* actions: */ 221,230,222,890,223,893, + 55,0, /* actions: */ 186,231,46,-424,203,-424,91,-424,43,-424,45,-424,42,-424,47,-424,37,-424,124,-424,94,-424,38,-424,187,-424,62,-424,191,-424,60,-424,192,-424,188,-424,189,-424,190,-424,195,-424,196,-424,201,-424,202,-424,193,-424,194,-424,197,-424,198,-424,63,-424,169,-424,170,-424,172,-424,173,-424,174,-424,175,-424,176,-424,59,-424,10,-424,129,-424,143,-424,125,-424,148,-424,147,-424,142,-424,150,-424,41,-424,141,-424,58,-424,146,-424,160,-424,205,-424,44,-424,93,-424,211,-424,161,-424, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,232,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,-205,45,-205,42,-205,47,-205,37,-205,186,156,124,-205,94,-205,38,-205,187,-205,62,-205,191,-205,60,-205,192,-205,188,-205,189,-205,190,-205,195,-205,196,-205,201,-205,202,-205,193,-205,194,-205,197,-205,198,-205,63,-205,169,-205,170,-205,172,-205,173,-205,174,-205,175,-205,176,-205,59,-205,10,-205,129,-205,143,-205,125,-205,148,-205,147,-205,142,-205,150,-205,41,-205,141,-205,58,-205,146,-205,160,-205,205,-205,44,-205,93,-205,211,-205,161,-205, + /* default action: */ -282, + 79,6, /* actions: */ 214,41,218,42,215,43,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70,181,72,182,73,183,74,179,75,180,76,177,77,169,78,140,79,154,80,149,81,136,82,138,83,178,84,159,85,148,86,147,87,143,88,142,89,168,90,153,91,158,92,137,93,155,94,166,95,171,96,170,97,156,98,141,99,157,100,162,101,165,102,164,103,146,104,167,105,139,106,150,107,163,108,172,109,173,110,174,111,175,112,176,113,217,118,216,119,219,120,225,122,207,-406,224,-406,227,-406,228,-406, /* gotos: */ -79,116,-64,235,-84,117,-83,44,-105,71,-76,121, + 4,1, /* actions: */ 207,236,224,238,227,239,228,245, /* gotos: */ -63,237, + /* default action: */ -394, + /* default action: */ -407, + /* default action: */ -408, + 4,2, /* actions: */ 216,241,225,122,217,243,219,244, /* gotos: */ -62,240,-76,242, + /* default action: */ -409, + /* default action: */ -411, + /* default action: */ -412, + /* default action: */ -413, + /* default action: */ -414, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,125,-5, /* gotos: */ -4,246,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 125,247, + /* default action: */ -410, + /* default action: */ -16, + /* default action: */ -23, + 69,27, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,644,213,486,172,-28,173,-28,174,-28,175,-28,176,-28,59,-28,10,-28,129,-28,143,-28,125,-28,148,-28,147,-28,142,-28,150,-28,41,-28,141,-28,169,-28,170,-28,58,-28,146,-28,160,-28, /* gotos: */ -39,251,-19,252,-18,314,-93,200,-78,206,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,27,-23,335,-17,390,-20,411,-21,436,-92,509,-51,638,-52,569,-31,647,-27,831, + /* default action: */ -25, + 25,1, /* actions: */ 44,254,172,-278,173,-278,174,-278,175,-278,176,-278,59,-278,10,-278,129,-278,143,-278,125,-278,148,-278,147,-278,142,-278,150,-278,41,-278,141,-278,169,-278,170,-278,58,-278,146,-278,160,-278,211,-278,161,-278,93,-278, /* gotos: */ -32,253, + /* default action: */ -248, + 48,24, /* actions: */ 212,481,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,213,486, /* gotos: */ -18,255,-51,631,-31,485,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509,-52,569, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,205,256,44,-280,172,-280,173,-280,174,-280,175,-280,176,-280,59,-280,10,-280,129,-280,143,-280,125,-280,148,-280,147,-280,142,-280,150,-280,41,-280,141,-280,169,-280,170,-280,58,-280,146,-280,160,-280,211,-280,161,-280,93,-280, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,257,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 51,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,-481,10,-481,93,-481,172,-481,173,-481,174,-481,175,-481,176,-481,59,-481,129,-481,143,-481,125,-481,148,-481,147,-481,142,-481,150,-481,41,-481,141,-481,169,-481,170,-481,58,-481,146,-481,160,-481,211,-481,161,-481, + /* default action: */ -283, + 56,1, /* actions: */ 229,261,46,-284,203,-284,91,-284,43,-284,45,-284,42,-284,47,-284,37,-284,186,-284,124,-284,94,-284,38,-284,187,-284,62,-284,191,-284,60,-284,192,-284,188,-284,189,-284,190,-284,195,-284,196,-284,201,-284,202,-284,193,-284,194,-284,197,-284,198,-284,63,-284,169,-284,170,-284,172,-284,173,-284,174,-284,175,-284,176,-284,59,-284,10,-284,129,-284,143,-284,125,-284,148,-284,147,-284,142,-284,150,-284,41,-284,141,-284,58,-284,146,-284,160,-284,205,-284,44,-284,93,-284,211,-284,161,-284, /* gotos: */ -65,260, + /* default action: */ -391, + 0,1, /* default action: */ -406, /* gotos: */ -64,262, + 4,1, /* actions: */ 207,263,224,238,227,239,228,245, /* gotos: */ -63,237, + /* default action: */ -392, + /* default action: */ -390, + /* default action: */ -285, + 0,1, /* default action: */ -406, /* gotos: */ -64,267, + 4,1, /* actions: */ 207,268,224,238,227,239,228,245, /* gotos: */ -63,237, + /* default action: */ -393, + /* default action: */ -286, + 0,1, /* default action: */ -406, /* gotos: */ -64,271, + 4,1, /* actions: */ 226,272,224,238,227,239,228,245, /* gotos: */ -63,237, + /* default action: */ -395, + /* default action: */ -287, + 5,1, /* actions: */ 135,275,207,-398,224,-398,227,-398,228,-398, /* gotos: */ -69,277, + 1,0, /* actions: */ 207,276, + /* default action: */ -396, + 4,2, /* actions: */ 207,278,224,238,227,239,228,245, /* gotos: */ -68,279,-63,282, + /* default action: */ -397, + 4,1, /* actions: */ 135,280,224,238,227,239,228,245, /* gotos: */ -63,281, + /* default action: */ -399, + /* default action: */ -401, + /* default action: */ -400, + /* default action: */ -288, + 3,1, /* actions: */ 135,285,207,-404,224,-404, /* gotos: */ -70,287, + 1,0, /* actions: */ 207,286, + /* default action: */ -402, + 2,0, /* actions: */ 207,288,224,289, + /* default action: */ -403, + 1,0, /* actions: */ 135,290, + /* default action: */ -405, + /* default action: */ -289, + 57,0, /* actions: */ 220,293,46,-290,203,-290,91,-290,43,-290,45,-290,42,-290,47,-290,37,-290,186,-290,124,-290,94,-290,38,-290,187,-290,62,-290,191,-290,60,-290,192,-290,188,-290,189,-290,190,-290,195,-290,196,-290,201,-290,202,-290,193,-290,194,-290,197,-290,198,-290,63,-290,169,-290,170,-290,172,-290,173,-290,174,-290,175,-290,176,-290,59,-290,10,-290,129,-290,143,-290,125,-290,148,-290,147,-290,142,-290,150,-290,41,-290,141,-290,58,-290,146,-290,160,-290,205,-290,44,-290,93,-290,211,-290,161,-290,61,-104, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,145,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 106,0, /* actions: */ 46,-291,203,-291,91,-291,43,-291,45,-291,42,-291,47,-291,37,-291,186,-291,124,-291,94,-291,38,-291,187,-291,62,-291,191,-291,60,-291,192,-291,188,-291,189,-291,190,-291,195,-291,196,-291,201,-291,202,-291,193,-291,194,-291,197,-291,198,-291,63,-291,169,-291,170,-291,172,-291,173,-291,174,-291,175,-291,176,-291,59,-291,10,-291,129,-291,143,-291,125,-291,148,-291,147,-291,142,-291,150,-291,41,-291,141,-291,58,-291,146,-291,160,-291,205,-291,44,-291,93,-291,211,-291,161,-291,123,-484,159,-484,40,-484,214,-484,217,-484,216,-484,218,-484,219,-484,166,-484,165,-484,167,-484,168,-484,182,-484,181,-484,183,-484,221,-484,222,-484,223,-484,236,-484,234,-484,229,-484,231,-484,230,-484,232,-484,233,-484,225,-484,215,-484,204,-484,209,-484,210,-484,163,-484,178,-484,144,-484,145,-484,151,-484,152,-484,149,-484,153,-484,208,-484,206,-484,140,-484,136,-484,137,-484,138,-484,164,-484,184,-484,185,-484,33,-484,126,-484,212,-484,213,-484, + 1,0, /* actions: */ 218,296, + 57,0, /* actions: */ 220,297,46,-293,203,-293,91,-293,43,-293,45,-293,42,-293,47,-293,37,-293,186,-293,124,-293,94,-293,38,-293,187,-293,62,-293,191,-293,60,-293,192,-293,188,-293,189,-293,190,-293,195,-293,196,-293,201,-293,202,-293,193,-293,194,-293,197,-293,198,-293,63,-293,169,-293,170,-293,172,-293,173,-293,174,-293,175,-293,176,-293,59,-293,10,-293,129,-293,143,-293,125,-293,148,-293,147,-293,142,-293,150,-293,41,-293,141,-293,58,-293,146,-293,160,-293,205,-293,44,-293,93,-293,211,-293,161,-293,61,-103, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,298,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-197,170,-197,172,-197,173,-197,174,-197,175,-197,176,-197,59,-197,10,-197,129,-197,143,-197,125,-197,148,-197,147,-197,142,-197,150,-197,41,-197,141,-197,58,-197,146,-197,160,-197,205,-197,44,-197,93,-197,211,-197,161,-197, + 48,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,570,93,-236, /* gotos: */ -36,300,-27,302,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-19,559,-18,314,-93,200,-92,509,-51,567,-52,569, + 1,0, /* actions: */ 93,301, + /* default action: */ -295, + 2,1, /* actions: */ 10,304,93,-496, /* gotos: */ -107,303, + /* default action: */ -237, + /* default action: */ -497, + 55,0, /* actions: */ 46,306,203,886,91,220,43,-235,45,-235,42,-235,47,-235,37,-235,186,-235,124,-235,94,-235,38,-235,187,-235,62,-235,191,-235,60,-235,192,-235,188,-235,189,-235,190,-235,195,-235,196,-235,201,-235,202,-235,193,-235,194,-235,197,-235,198,-235,63,-235,169,-235,170,-235,172,-235,173,-235,174,-235,175,-235,176,-235,59,-235,10,-235,129,-235,143,-235,125,-235,148,-235,147,-235,142,-235,150,-235,41,-235,141,-235,58,-235,146,-235,160,-235,205,-235,44,-235,93,-235,211,-235,161,-235, + 29,2, /* actions: */ 214,882,218,884,215,471,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,307,-83,472, + 106,4, /* actions: */ 40,457,214,-269,217,-269,216,-269,218,-269,219,-269,166,-269,165,-269,167,-269,168,-269,182,-269,181,-269,183,-269,221,-269,222,-269,223,-269,236,-269,234,-269,229,-269,231,-269,230,-269,232,-269,233,-269,225,-269,215,-269,204,-269,209,-269,210,-269,163,-269,178,-269,144,-269,145,-269,151,-269,152,-269,149,-269,153,-269,208,-269,206,-269,140,-269,136,-269,137,-269,138,-269,164,-269,184,-269,185,-269,33,-269,126,-269,212,-269,213,-269,123,-246,159,-246,46,-246,203,-246,91,-246,43,-246,45,-246,42,-246,47,-246,37,-246,186,-246,124,-246,94,-246,38,-246,187,-246,62,-246,191,-246,60,-246,192,-246,188,-246,189,-246,190,-246,195,-246,196,-246,201,-246,202,-246,193,-246,194,-246,197,-246,198,-246,63,-246,169,-246,170,-246,172,-246,173,-246,174,-246,175,-246,176,-246,59,-246,10,-246,129,-246,143,-246,125,-246,148,-246,147,-246,142,-246,150,-246,41,-246,141,-246,58,-246,146,-246,160,-246,205,-246,44,-246,93,-246,211,-246,161,-246, /* gotos: */ -41,308,-43,310,-109,311,-38,468, + 24,1, /* actions: */ 211,30,161,-72,169,-72,170,-72,172,-72,173,-72,174,-72,175,-72,176,-72,59,-72,10,-72,129,-72,143,-72,125,-72,148,-72,147,-72,142,-72,150,-72,41,-72,141,-72,58,-72,146,-72,160,-72,93,-72, /* gotos: */ -33,309, + /* default action: */ -73, + /* default action: */ -364, + 48,28, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,842,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,644,213,486, /* gotos: */ -42,312,-39,313,-19,252,-18,314,-93,200,-78,206,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,27,-23,335,-17,390,-20,411,-21,436,-92,509,-51,638,-52,569,-31,647,-27,831, + /* default action: */ -270, + /* default action: */ -271, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,205,256,44,-279,10,-279,93,-279,172,-279,173,-279,174,-279,175,-279,176,-279,59,-279,129,-279,143,-279,125,-279,148,-279,147,-279,142,-279,150,-279,41,-279,141,-279,169,-279,170,-279,58,-279,146,-279,160,-279,211,-279,161,-279, + 47,24, /* actions: */ 125,316,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -51,317,-19,838,-52,569,-18,314,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + /* default action: */ -296, + 3,1, /* actions: */ 44,320,10,566,125,-498, /* gotos: */ -108,318, + 1,0, /* actions: */ 125,319, + /* default action: */ -297, + 48,22, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,125,-500,93,-500, /* gotos: */ -52,321,-18,322,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + /* default action: */ -480, + 27,0, /* actions: */ 205,256,43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196, + 56,0, /* actions: */ 40,324,91,-301,203,-301,46,-301,43,-301,45,-301,42,-301,47,-301,37,-301,186,-301,124,-301,94,-301,38,-301,187,-301,62,-301,191,-301,60,-301,192,-301,188,-301,189,-301,190,-301,195,-301,196,-301,201,-301,202,-301,193,-301,194,-301,197,-301,198,-301,63,-301,169,-301,170,-301,172,-301,173,-301,174,-301,175,-301,176,-301,59,-301,10,-301,129,-301,143,-301,125,-301,148,-301,147,-301,142,-301,150,-301,41,-301,141,-301,58,-301,146,-301,160,-301,205,-301,44,-301,93,-301,211,-301,161,-301, + 49,27, /* actions: */ 41,327,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,644,213,486, /* gotos: */ -39,325,-19,252,-18,314,-93,200,-78,206,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,27,-23,335,-17,390,-20,411,-21,436,-92,509,-51,638,-52,569,-31,647,-27,831, + 1,0, /* actions: */ 41,326, + /* default action: */ -299, + /* default action: */ -300, + 104,2, /* actions: */ 40,324,46,-301,203,-301,91,-301,43,-301,45,-301,42,-301,47,-301,37,-301,186,-301,124,-301,94,-301,38,-301,187,-301,62,-301,191,-301,60,-301,192,-301,188,-301,189,-301,190,-301,195,-301,196,-301,201,-301,202,-301,193,-301,194,-301,197,-301,198,-301,63,-301,169,-301,170,-301,172,-301,173,-301,174,-301,175,-301,176,-301,59,-301,10,-301,129,-301,143,-301,125,-301,148,-301,147,-301,142,-301,150,-301,41,-301,141,-301,58,-301,146,-301,160,-301,205,-301,44,-301,93,-301,211,-301,161,-301,214,-269,217,-269,216,-269,218,-269,219,-269,166,-269,165,-269,167,-269,168,-269,182,-269,181,-269,183,-269,221,-269,222,-269,223,-269,236,-269,234,-269,229,-269,231,-269,230,-269,232,-269,233,-269,225,-269,215,-269,204,-269,209,-269,210,-269,163,-269,178,-269,144,-269,145,-269,151,-269,152,-269,149,-269,153,-269,208,-269,206,-269,140,-269,136,-269,137,-269,138,-269,164,-269,184,-269,185,-269,33,-269,126,-269,212,-269,213,-269, /* gotos: */ -41,329,-109,311, + /* default action: */ -77, + 48,1, /* actions: */ 10,304,40,-496,214,-496,217,-496,216,-496,218,-496,219,-496,166,-496,165,-496,167,-496,168,-496,182,-496,181,-496,183,-496,221,-496,222,-496,223,-496,236,-496,234,-496,229,-496,231,-496,230,-496,232,-496,233,-496,225,-496,215,-496,204,-496,209,-496,210,-496,163,-496,178,-496,144,-496,145,-496,151,-496,152,-496,149,-496,153,-496,208,-496,206,-496,140,-496,136,-496,137,-496,138,-496,164,-496,184,-496,185,-496,33,-496,126,-496, /* gotos: */ -107,331, + 47,21, /* actions: */ 40,332,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,837,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,333,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 3,0, /* actions: */ 41,334,169,11,170,13, + /* default action: */ -302, + 57,1, /* actions: */ 123,337,159,491,46,-304,203,-304,91,-304,43,-304,45,-304,42,-304,47,-304,37,-304,186,-304,124,-304,94,-304,38,-304,187,-304,62,-304,191,-304,60,-304,192,-304,188,-304,189,-304,190,-304,195,-304,196,-304,201,-304,202,-304,193,-304,194,-304,197,-304,198,-304,63,-304,169,-304,170,-304,172,-304,173,-304,174,-304,175,-304,176,-304,59,-304,10,-304,129,-304,143,-304,125,-304,148,-304,147,-304,142,-304,150,-304,41,-304,141,-304,58,-304,146,-304,160,-304,205,-304,44,-304,93,-304,211,-304,161,-304, /* gotos: */ -34,336, + /* default action: */ -305, + 0,1, /* default action: */ -369, /* gotos: */ -131,338, + 63,1, /* actions: */ 124,610,194,630,177,-354,139,-354,179,-354,180,-354,225,-354,162,-354,154,-354,155,-354,156,-354,157,-354,214,-354,218,-354,215,-354,221,-354,222,-354,223,-354,236,-354,234,-354,229,-354,231,-354,230,-354,232,-354,233,-354,217,-354,216,-354,219,-354,166,-354,165,-354,167,-354,168,-354,182,-354,181,-354,183,-354,204,-354,209,-354,210,-354,163,-354,178,-354,144,-354,145,-354,151,-354,152,-354,149,-354,153,-354,208,-354,206,-354,140,-354,136,-354,137,-354,138,-354,164,-354,171,-354,33,-354,184,-354,185,-354,126,-354,212,-354,128,-354,125,-354,59,-354,10,-354, /* gotos: */ -95,339, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,125,-5, /* gotos: */ -4,340,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 125,341, + /* default action: */ -370, + 69,27, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,644,213,486,172,-29,173,-29,174,-29,175,-29,176,-29,59,-29,10,-29,129,-29,143,-29,125,-29,148,-29,147,-29,142,-29,150,-29,41,-29,141,-29,169,-29,170,-29,58,-29,146,-29,160,-29, /* gotos: */ -39,343,-19,252,-18,314,-93,200,-78,206,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,27,-23,335,-17,390,-20,411,-21,436,-92,509,-51,638,-52,569,-31,647,-27,831, + /* default action: */ -26, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,345,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 6,2, /* actions: */ 169,11,170,13,59,695,10,696,58,762,146,763, /* gotos: */ -112,346,-123,760, + 63,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,148,-5,147,-5,143,-5, /* gotos: */ -4,347,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 3,2, /* actions: */ 148,351,147,832,143,-350, /* gotos: */ -29,348,-28,350, + 1,0, /* actions: */ 143,349, + /* default action: */ -306, + /* default action: */ -348, + 62,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,143,-5,142,-5, /* gotos: */ -4,352,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + /* default action: */ -351, + 69,27, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,644,213,486,172,-30,173,-30,174,-30,175,-30,176,-30,59,-30,10,-30,129,-30,143,-30,125,-30,148,-30,147,-30,142,-30,150,-30,41,-30,141,-30,169,-30,170,-30,58,-30,146,-30,160,-30, /* gotos: */ -39,354,-19,252,-18,314,-93,200,-78,206,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,27,-23,335,-17,390,-20,411,-21,436,-92,509,-51,638,-52,569,-31,647,-27,831, + /* default action: */ -27, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,356,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 6,2, /* actions: */ 169,11,170,13,59,695,10,696,58,762,146,763, /* gotos: */ -112,357,-123,760, + 62,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,148,-5,143,-5, /* gotos: */ -4,358,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 2,1, /* actions: */ 148,351,143,-350, /* gotos: */ -28,359, + 1,0, /* actions: */ 143,360, + /* default action: */ -307, + /* default action: */ -24, + /* default action: */ -31, + /* default action: */ -32, + /* default action: */ -17, + /* default action: */ -18, + 18,0, /* actions: */ 169,11,170,13,172,-33,173,-33,174,-33,175,-33,176,-33,59,-33,10,-33,129,-33,143,-33,125,-33,148,-33,147,-33,142,-33,150,-33,41,-33,141,-33, + 47,0, /* actions: */ 46,368,203,815,91,826,43,-235,45,-235,42,-235,47,-235,37,-235,186,-235,124,-235,94,-235,38,-235,187,-235,62,-235,191,-235,60,-235,192,-235,188,-235,189,-235,190,-235,195,-235,196,-235,201,-235,202,-235,193,-235,194,-235,197,-235,198,-235,63,-235,169,-235,170,-235,172,-235,173,-235,174,-235,175,-235,176,-235,59,-235,10,-235,129,-235,143,-235,125,-235,148,-235,147,-235,142,-235,150,-235,41,-235,141,-235, + 29,2, /* actions: */ 214,369,218,811,215,471,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,307,-83,472, + 101,0, /* actions: */ 220,370,61,-99,44,-91,40,-485,214,-485,217,-485,216,-485,218,-485,219,-485,166,-485,165,-485,167,-485,168,-485,182,-485,181,-485,183,-485,221,-485,222,-485,223,-485,236,-485,234,-485,229,-485,231,-485,230,-485,232,-485,233,-485,225,-485,215,-485,204,-485,209,-485,210,-485,163,-485,178,-485,144,-485,145,-485,151,-485,152,-485,149,-485,153,-485,208,-485,206,-485,140,-485,136,-485,137,-485,138,-485,164,-485,184,-485,185,-485,33,-485,126,-485,212,-485,213,-485,123,-485,159,-485,46,-485,203,-485,91,-485,43,-485,45,-485,42,-485,47,-485,37,-485,186,-485,124,-485,94,-485,38,-485,187,-485,62,-485,191,-485,60,-485,192,-485,188,-485,189,-485,190,-485,195,-485,196,-485,201,-485,202,-485,193,-485,194,-485,197,-485,198,-485,63,-485,169,-485,170,-485,172,-485,173,-485,174,-485,175,-485,176,-485,59,-485,10,-485,129,-485,143,-485,125,-485,148,-485,147,-485,142,-485,150,-485,41,-485,141,-485, + 46,25, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518, /* gotos: */ -18,371,-25,372,-93,200,-78,206,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,27,-23,335,-17,390,-20,411,-21,436,-92,509,-27,17,-26,496,-24,497, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-193,170,-193,172,-193,173,-193,174,-193,175,-193,176,-193,59,-193,10,-193,129,-193,143,-193,125,-193,148,-193,147,-193,142,-193,150,-193,41,-193,141,-193,58,-193,146,-193,160,-193,205,-193,44,-193,93,-193,211,-193,161,-193, + /* default action: */ -38, + 0,1, /* default action: */ -308, /* gotos: */ -113,374, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,375,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 6,2, /* actions: */ 169,11,170,13,59,695,10,696,58,790,160,791, /* gotos: */ -114,376,-123,789, + 0,1, /* default action: */ -309, /* gotos: */ -115,377, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,143,-5, /* gotos: */ -4,378,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 143,379, + /* default action: */ -310, + 50,0, /* actions: */ 46,-439,203,-439,91,-439,43,-439,45,-439,42,-439,47,-439,37,-439,186,-439,124,-439,94,-439,38,-439,187,-439,62,-439,191,-439,60,-439,192,-439,188,-439,189,-439,190,-439,195,-439,196,-439,201,-439,202,-439,193,-439,194,-439,197,-439,198,-439,63,-439,169,-439,170,-439,172,-439,173,-439,174,-439,175,-439,176,-439,59,-439,10,-439,129,-439,143,-439,125,-439,148,-439,147,-439,142,-439,150,-439,41,-439,141,-439,61,-97,220,-440,44,-89, + 1,0, /* actions: */ 218,382, + 50,0, /* actions: */ 220,297,46,-293,203,-293,91,-293,43,-293,45,-293,42,-293,47,-293,37,-293,186,-293,124,-293,94,-293,38,-293,187,-293,62,-293,191,-293,60,-293,192,-293,188,-293,189,-293,190,-293,195,-293,196,-293,201,-293,202,-293,193,-293,194,-293,197,-293,198,-293,63,-293,169,-293,170,-293,172,-293,173,-293,174,-293,175,-293,176,-293,59,-293,10,-293,129,-293,143,-293,125,-293,148,-293,147,-293,142,-293,150,-293,41,-293,141,-293,61,-103,44,-95, + 0,1, /* default action: */ -311, /* gotos: */ -116,384, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,385,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 6,2, /* actions: */ 169,11,170,13,59,695,10,696,58,790,160,791, /* gotos: */ -114,386,-123,789, + 0,1, /* default action: */ -312, /* gotos: */ -117,387, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,143,-5, /* gotos: */ -4,388,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 143,389, + /* default action: */ -313, + /* default action: */ -314, + 52,29, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,59,695,10,696,150,-494,148,-494,143,-494, /* gotos: */ -12,392,-99,805,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509,-100,802,-123,804, + 7,3, /* actions: */ 169,11,170,13,59,695,10,696,150,-494,148,-494,143,-494, /* gotos: */ -99,393,-100,802,-123,804, + 3,3, /* actions: */ 150,398,148,351,143,-350, /* gotos: */ -49,394,-28,799,-50,801, + 3,2, /* actions: */ 148,351,150,398,143,-350, /* gotos: */ -28,395,-50,397, + 1,0, /* actions: */ 143,396, + /* default action: */ -337, + /* default action: */ -374, + 47,23, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,212,797, /* gotos: */ -37,399,-19,792,-18,796,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 4,2, /* actions: */ 59,695,10,696,58,762,146,763, /* gotos: */ -112,400,-123,760, + 63,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,148,-5,150,-5,143,-5, /* gotos: */ -4,401,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + /* default action: */ -375, + 43,25, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,585,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,627,209,299,210,315,163,323,178,592,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,512,212,600, /* gotos: */ -94,403,-93,614,-78,615,-11,616,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,626,-77,488,-23,335,-17,390,-20,411,-21,436,-87,629,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 158,404, + 0,1, /* default action: */ -315, /* gotos: */ -118,405, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,406,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 6,2, /* actions: */ 169,11,170,13,59,695,10,696,58,790,160,791, /* gotos: */ -114,407,-123,789, + 0,1, /* default action: */ -316, /* gotos: */ -119,408, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,143,-5, /* gotos: */ -4,409,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 143,410, + /* default action: */ -317, + /* default action: */ -318, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,413,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 4,1, /* actions: */ 169,11,170,13,10,-320,41,-320, /* gotos: */ -120,414, + 2,1, /* actions: */ 10,304,41,-496, /* gotos: */ -107,415, + 1,0, /* actions: */ 41,416, + /* default action: */ -321, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,41,-5, /* gotos: */ -4,418,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 41,419, + /* default action: */ -322, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,41,-5, /* gotos: */ -4,418,-87,421,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-88,549,-89,597,-91,553,-90,599, + 2,0, /* actions: */ 41,422,61,423, + 5,0, /* actions: */ 44,-88,61,-80,158,-80,41,-80,124,-80, + 47,27, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,543, /* gotos: */ -25,424,-18,425,-44,426,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-93,200,-92,509,-19,538, + /* default action: */ -35, + 43,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,172,-42,173,-42,174,-42,175,-42,176,-42,59,-42,10,-42,129,-42,143,-42,125,-42,148,-42,147,-42,142,-42,150,-42,41,-42,141,-42,44,-279, + /* default action: */ -43, + 64,41, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,141,-5,148,-5,142,-5,143,-5, /* gotos: */ -22,428,-4,430,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 143,429, + /* default action: */ -323, + 4,3, /* actions: */ 141,756,148,-379,142,-379,143,-379, /* gotos: */ -47,431,-46,754,-48,788, + 3,1, /* actions: */ 148,351,142,-350,143,-350, /* gotos: */ -28,432, + 2,1, /* actions: */ 142,434,143,-388, /* gotos: */ -5,433, + /* default action: */ -336, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,143,-5, /* gotos: */ -4,435,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + /* default action: */ -389, + /* default action: */ -319, + 43,20, /* actions: */ 201,747,204,723,218,728,214,729,221,226,222,227,223,228,236,585,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,215,294,209,299,210,315,163,323,178,592,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512, /* gotos: */ -45,438,-82,727,-11,730,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,717,-76,739,-77,488,-23,335,-17,390,-20,411,-21,436, + 4,2, /* actions: */ 59,695,10,696,60,741,128,745, /* gotos: */ -14,439,-123,740, + 0,1, /* default action: */ -324, /* gotos: */ -121,440, + 64,41, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,141,-5,148,-5,142,-5,143,-5, /* gotos: */ -22,441,-4,430,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 143,442, + /* default action: */ -325, + 42,20, /* actions: */ 204,723,218,728,214,729,221,226,222,227,223,228,236,585,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,215,294,209,299,210,315,163,323,178,592,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512, /* gotos: */ -45,444,-82,727,-11,730,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,717,-76,739,-77,488,-23,335,-17,390,-20,411,-21,436, + 0,1, /* default action: */ -329, /* gotos: */ -125,445, + 64,41, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,141,-5,148,-5,142,-5,143,-5, /* gotos: */ -22,446,-4,430,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 143,447, + /* default action: */ -330, + 75,6, /* actions: */ 214,707,218,708,215,43,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70,181,709,182,710,183,711,179,75,180,76,177,77,169,78,140,79,154,80,149,81,136,82,138,83,178,84,159,85,148,86,147,87,143,88,142,89,168,712,153,91,158,92,137,93,155,94,166,713,171,96,170,97,156,98,141,99,157,100,162,101,165,714,164,103,146,104,167,715,139,106,150,107,163,108,172,109,173,110,174,111,175,112,176,113,217,208,216,209,219,211,40,718, /* gotos: */ -84,449,-16,697,-83,44,-105,71,-15,716,-78,717, + 0,1, /* default action: */ -331, /* gotos: */ -126,450, + 12,10, /* actions: */ 40,649,218,663,217,664,216,665,219,666,214,667,42,673,212,674,38,668,213,669,59,-456,10,-456, /* gotos: */ -53,451,-54,693,-55,653,-56,684,-60,685,-61,682,-57,690,-134,671,-58,692,-135,661, + 64,41, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,141,-5,148,-5,142,-5,143,-5, /* gotos: */ -22,452,-4,430,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 143,453, + /* default action: */ -332, + 106,3, /* actions: */ 40,457,123,-368,159,-368,46,-368,203,-368,91,-368,43,-368,45,-368,42,-368,47,-368,37,-368,186,-368,124,-368,94,-368,38,-368,187,-368,62,-368,191,-368,60,-368,192,-368,188,-368,189,-368,190,-368,195,-368,196,-368,201,-368,202,-368,193,-368,194,-368,197,-368,198,-368,63,-368,169,-368,170,-368,172,-368,173,-368,174,-368,175,-368,176,-368,59,-368,10,-368,129,-368,143,-368,125,-368,148,-368,147,-368,142,-368,150,-368,41,-368,141,-368,58,-368,146,-368,160,-368,205,-368,44,-368,93,-368,211,-368,161,-368,214,-269,217,-269,216,-269,218,-269,219,-269,166,-269,165,-269,167,-269,168,-269,182,-269,181,-269,183,-269,221,-269,222,-269,223,-269,236,-269,234,-269,229,-269,231,-269,230,-269,232,-269,233,-269,225,-269,215,-269,204,-269,209,-269,210,-269,163,-269,178,-269,144,-269,145,-269,151,-269,152,-269,149,-269,153,-269,208,-269,206,-269,140,-269,136,-269,137,-269,138,-269,164,-269,184,-269,185,-269,33,-269,126,-269,212,-269,213,-269, /* gotos: */ -38,455,-41,456,-109,311, + /* default action: */ -367, + /* default action: */ -76, + 49,28, /* actions: */ 41,458,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,644,213,486, /* gotos: */ -39,459,-24,462,-19,476,-18,314,-93,200,-78,206,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,27,-23,335,-17,390,-20,411,-21,436,-92,509,-51,638,-52,569,-31,647,-27,648, + /* default action: */ -242, + 2,1, /* actions: */ 10,304,41,-496, /* gotos: */ -107,460, + 1,0, /* actions: */ 41,461, + /* default action: */ -243, + 4,1, /* actions: */ 46,465,203,473,10,304,41,-496, /* gotos: */ -107,463, + 1,0, /* actions: */ 41,464, + /* default action: */ -244, + 29,2, /* actions: */ 214,469,218,470,215,471,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,466,-83,472, + 5,2, /* actions: */ 40,457,46,-246,203,-246,10,-246,41,-246, /* gotos: */ -43,467,-38,468, + /* default action: */ -361, + /* default action: */ -247, + /* default action: */ -485, + /* default action: */ -486, + /* default action: */ -487, + /* default action: */ -488, + 29,2, /* actions: */ 214,469,218,470,215,471,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,474,-83,472, + 5,2, /* actions: */ 40,457,46,-246,203,-246,10,-246,41,-246, /* gotos: */ -43,475,-38,468, + /* default action: */ -362, + 3,1, /* actions: */ 44,477,10,-278,41,-278, /* gotos: */ -32,253, + 48,26, /* actions: */ 212,481,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,213,486, /* gotos: */ -24,478,-18,255,-51,631,-31,485,-27,637,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-93,200,-92,509,-52,569, + 4,1, /* actions: */ 46,465,203,473,10,304,41,-496, /* gotos: */ -107,479, + 1,0, /* actions: */ 41,480, + /* default action: */ -245, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,482,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 51,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,484,172,-278,173,-278,174,-278,175,-278,176,-278,59,-278,10,-278,129,-278,143,-278,125,-278,148,-278,147,-278,142,-278,150,-278,41,-278,141,-278,169,-278,170,-278,58,-278,146,-278,160,-278,211,-278,161,-278,93,-278, /* gotos: */ -32,483, + /* default action: */ -249, + 1,1, /* actions: */ 213,486, /* gotos: */ -31,485, + /* default action: */ -277, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,487,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 50,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,172,-276,173,-276,174,-276,175,-276,176,-276,59,-276,10,-276,129,-276,143,-276,125,-276,148,-276,147,-276,142,-276,150,-276,41,-276,141,-276,169,-276,170,-276,58,-276,146,-276,160,-276,211,-276,161,-276,93,-276, + 3,2, /* actions: */ 123,337,159,491,40,457, /* gotos: */ -34,489,-38,490, + /* default action: */ -303, + /* default action: */ -363, + 0,1, /* default action: */ -371, /* gotos: */ -132,492, + 63,1, /* actions: */ 124,610,194,630,177,-354,139,-354,179,-354,180,-354,225,-354,162,-354,154,-354,155,-354,156,-354,157,-354,214,-354,218,-354,215,-354,221,-354,222,-354,223,-354,236,-354,234,-354,229,-354,231,-354,230,-354,232,-354,233,-354,217,-354,216,-354,219,-354,166,-354,165,-354,167,-354,168,-354,182,-354,181,-354,183,-354,204,-354,209,-354,210,-354,163,-354,178,-354,144,-354,145,-354,151,-354,152,-354,149,-354,153,-354,208,-354,206,-354,140,-354,136,-354,137,-354,138,-354,164,-354,171,-354,33,-354,184,-354,185,-354,126,-354,212,-354,128,-354,143,-354,59,-354,10,-354, /* gotos: */ -95,493, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,143,-5, /* gotos: */ -4,494,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 143,495, + /* default action: */ -372, + /* default action: */ -64, + 23,0, /* actions: */ 46,498,203,501,169,-65,170,-65,172,-65,173,-65,174,-65,175,-65,176,-65,59,-65,10,-65,129,-65,143,-65,125,-65,148,-65,147,-65,142,-65,150,-65,41,-65,141,-65,58,-65,146,-65,160,-65, + 29,2, /* actions: */ 214,469,218,470,215,471,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,499,-83,472, + 72,4, /* actions: */ 40,457,46,-246,203,-246,169,-246,170,-246,172,-246,173,-246,174,-246,175,-246,176,-246,59,-246,10,-246,129,-246,143,-246,125,-246,148,-246,147,-246,142,-246,150,-246,41,-246,141,-246,58,-246,146,-246,160,-246,214,-269,217,-269,216,-269,218,-269,219,-269,166,-269,165,-269,167,-269,168,-269,182,-269,181,-269,183,-269,221,-269,222,-269,223,-269,236,-269,234,-269,229,-269,231,-269,230,-269,232,-269,233,-269,225,-269,215,-269,204,-269,209,-269,210,-269,163,-269,178,-269,144,-269,145,-269,151,-269,152,-269,149,-269,153,-269,208,-269,206,-269,140,-269,136,-269,137,-269,138,-269,164,-269,184,-269,185,-269,33,-269,126,-269,212,-269,213,-269, /* gotos: */ -43,467,-41,500,-38,468,-109,311, + /* default action: */ -66, + 29,2, /* actions: */ 214,469,218,470,215,471,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,502,-83,472, + 72,4, /* actions: */ 40,457,46,-246,203,-246,169,-246,170,-246,172,-246,173,-246,174,-246,175,-246,176,-246,59,-246,10,-246,129,-246,143,-246,125,-246,148,-246,147,-246,142,-246,150,-246,41,-246,141,-246,58,-246,146,-246,160,-246,214,-269,217,-269,216,-269,218,-269,219,-269,166,-269,165,-269,167,-269,168,-269,182,-269,181,-269,183,-269,221,-269,222,-269,223,-269,236,-269,234,-269,229,-269,231,-269,230,-269,232,-269,233,-269,225,-269,215,-269,204,-269,209,-269,210,-269,163,-269,178,-269,144,-269,145,-269,151,-269,152,-269,149,-269,153,-269,208,-269,206,-269,140,-269,136,-269,137,-269,138,-269,164,-269,184,-269,185,-269,33,-269,126,-269,212,-269,213,-269, /* gotos: */ -43,475,-41,503,-38,468,-109,311, + /* default action: */ -67, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,505,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + /* default action: */ -60, + 46,25, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518, /* gotos: */ -25,507,-18,508,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-93,200,-92,509, + /* default action: */ -61, + /* default action: */ -223, + 1,0, /* actions: */ 220,510, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,511,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-191,170,-191,172,-191,173,-191,174,-191,175,-191,176,-191,59,-191,10,-191,129,-191,143,-191,125,-191,148,-191,147,-191,142,-191,150,-191,41,-191,141,-191,58,-191,146,-191,160,-191,205,-191,44,-191,93,-191,211,-191,161,-191, + 58,1, /* actions: */ 40,457,123,-368,159,-368,91,-368,203,-368,46,-368,43,-368,45,-368,42,-368,47,-368,37,-368,186,-368,124,-368,94,-368,38,-368,187,-368,62,-368,191,-368,60,-368,192,-368,188,-368,189,-368,190,-368,195,-368,196,-368,201,-368,202,-368,193,-368,194,-368,197,-368,198,-368,63,-368,169,-368,170,-368,172,-368,173,-368,174,-368,175,-368,176,-368,59,-368,10,-368,129,-368,143,-368,125,-368,148,-368,147,-368,142,-368,150,-368,41,-368,141,-368,58,-368,146,-368,160,-368,205,-368,44,-368,93,-368,211,-368,161,-368, /* gotos: */ -38,455, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,514,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + /* default action: */ -208, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,516,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,-209,45,-209,42,-209,47,-209,37,-209,186,156,124,-209,94,-209,38,-209,187,-209,62,-209,191,-209,60,-209,192,-209,188,-209,189,-209,190,-209,195,-209,196,-209,201,-209,202,-209,193,-209,194,-209,197,-209,198,-209,63,-209,169,-209,170,-209,172,-209,173,-209,174,-209,175,-209,176,-209,59,-209,10,-209,129,-209,143,-209,125,-209,148,-209,147,-209,142,-209,150,-209,41,-209,141,-209,58,-209,146,-209,160,-209,205,-209,44,-209,93,-209,211,-209,161,-209, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,508,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,519,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + /* default action: */ -224, + 47,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-62,170,-62,172,-62,173,-62,174,-62,175,-62,176,-62,59,-62,10,-62,129,-62,143,-62,125,-62,148,-62,147,-62,142,-62,150,-62,41,-62,141,-62,58,-62,146,-62,160,-62, + 44,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,522,169,-62,170,-62,172,-62,173,-62,174,-62,175,-62,176,-62,59,-62,10,-62,129,-62,143,-62,125,-62,148,-62,147,-62,142,-62,150,-62,41,-62,141,-62, + 51,22, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,162,526,154,527,155,528,156,362,157,363, /* gotos: */ -18,523,-8,529,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 27,0, /* actions: */ 58,524,43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196, + 51,22, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,162,526,154,527,155,528,156,362,157,363, /* gotos: */ -18,199,-8,525,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + /* default action: */ -45, + /* default action: */ -28, + /* default action: */ -29, + /* default action: */ -30, + 1,0, /* actions: */ 58,530, + 51,22, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,162,526,154,527,155,528,156,362,157,363, /* gotos: */ -18,531,-8,532,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 42,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,172,-44,173,-44,174,-44,175,-44,176,-44,59,-44,10,-44,129,-44,143,-44,125,-44,148,-44,147,-44,142,-44,150,-44,41,-44,141,-44, + /* default action: */ -51, + 1,0, /* actions: */ 61,534, + 47,27, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,543, /* gotos: */ -18,535,-25,536,-44,537,-93,200,-78,206,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,27,-23,335,-17,390,-20,411,-21,436,-92,509,-27,17,-26,496,-24,497,-19,538, + 45,0, /* actions: */ 176,203,43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-188,170,-188,172,-188,173,-188,174,-188,175,-188,59,-188,10,-188,129,-188,143,-188,125,-188,148,-188,147,-188,142,-188,150,-188,41,-188,141,-188,44,-279, + /* default action: */ -34, + /* default action: */ -41, + 1,0, /* actions: */ 44,539, + 47,21, /* actions: */ 212,541,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,540,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 46,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,172,-52,173,-52,174,-52,175,-52,176,-52,59,-52,10,-52,129,-52,143,-52,125,-52,148,-52,147,-52,142,-52,150,-52,41,-52,141,-52,205,-52,58,-52,146,-52,44,-280, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,542,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 45,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,172,-53,173,-53,174,-53,175,-53,176,-53,59,-53,10,-53,129,-53,143,-53,125,-53,148,-53,147,-53,142,-53,150,-53,41,-53,141,-53,205,-53,58,-53,146,-53, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,544,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 45,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,172,-54,173,-54,174,-54,175,-54,176,-54,59,-54,10,-54,129,-54,143,-54,125,-54,148,-54,147,-54,142,-54,150,-54,41,-54,141,-54,205,-54,58,-54,146,-54, + 1,0, /* actions: */ 220,546, + 46,25, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518, /* gotos: */ -18,511,-25,547,-93,200,-78,206,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,27,-23,335,-17,390,-20,411,-21,436,-92,509,-27,17,-26,496,-24,497, + /* default action: */ -36, + 1,0, /* actions: */ 61,423, + 47,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,585,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,590,209,299,210,315,163,323,178,592,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,594,140,427,136,437,137,443,138,448,164,512,212,600,61,-79,158,-79,41,-79,124,-79, /* gotos: */ -89,550,-90,552,-91,553,-78,554,-11,555,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,589,-77,488,-23,335,-17,390,-20,411,-21,436, + 5,0, /* actions: */ 44,551,61,-78,158,-78,41,-78,124,-78, + /* default action: */ -85, + /* default action: */ -81, + /* default action: */ -87, + 8,0, /* actions: */ 44,-89,61,-89,158,-89,41,-89,124,-89,91,-439,203,-439,46,-439, + 3,0, /* actions: */ 91,556,203,573,46,581, + 48,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,570,93,-236, /* gotos: */ -36,557,-27,302,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-19,559,-18,314,-93,200,-92,509,-51,567,-52,569, + 1,0, /* actions: */ 93,558, + 8,0, /* actions: */ 44,-90,61,-90,158,-90,41,-90,124,-90,91,-294,203,-294,46,-294, + 3,1, /* actions: */ 44,561,10,566,93,-498, /* gotos: */ -108,560, + /* default action: */ -238, + 48,21, /* actions: */ 212,563,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,93,-500, /* gotos: */ -18,562,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 33,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,-280,10,-280,93,-280,125,-280,59,-280,58,-280,146,-280, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,564,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 28,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,10,304,93,-496, /* gotos: */ -107,565, + /* default action: */ -239, + /* default action: */ -499, + 3,1, /* actions: */ 44,320,10,566,93,-498, /* gotos: */ -108,568, + /* default action: */ -240, + /* default action: */ -479, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,571,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 28,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,10,304,93,-496, /* gotos: */ -107,572, + /* default action: */ -241, + 29,3, /* actions: */ 218,574,214,578,215,579,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,575,-81,577,-83,580, + 9,0, /* actions: */ 91,-292,203,-292,46,-292,44,-94,61,-94,158,-94,41,-94,124,-94,40,-486, + 1,1, /* actions: */ 40,457, /* gotos: */ -38,576, + /* default action: */ -365, + /* default action: */ -366, + 11,0, /* actions: */ 44,-92,61,-92,158,-92,41,-92,124,-92,40,-485,123,-489,159,-489,91,-489,203,-489,46,-489, + 106,0, /* actions: */ 40,-487,214,-487,217,-487,216,-487,218,-487,219,-487,166,-487,165,-487,167,-487,168,-487,182,-487,181,-487,183,-487,221,-487,222,-487,223,-487,236,-487,234,-487,229,-487,231,-487,230,-487,232,-487,233,-487,225,-487,215,-487,204,-487,209,-487,210,-487,163,-487,178,-487,144,-487,145,-487,151,-487,152,-487,149,-487,153,-487,208,-487,206,-487,140,-487,136,-487,137,-487,138,-487,164,-487,184,-487,185,-487,33,-487,126,-487,212,-487,213,-487,123,-490,159,-490,91,-490,203,-490,46,-490,43,-490,45,-490,42,-490,47,-490,37,-490,186,-490,124,-490,94,-490,38,-490,187,-490,62,-490,191,-490,60,-490,192,-490,188,-490,189,-490,190,-490,195,-490,196,-490,201,-490,202,-490,193,-490,194,-490,197,-490,198,-490,63,-490,169,-490,170,-490,172,-490,173,-490,174,-490,175,-490,176,-490,59,-490,10,-490,129,-490,143,-490,125,-490,148,-490,147,-490,142,-490,150,-490,41,-490,141,-490,58,-490,146,-490,160,-490,205,-490,44,-490,93,-490,211,-490,161,-490, + 106,0, /* actions: */ 40,-488,214,-488,217,-488,216,-488,218,-488,219,-488,166,-488,165,-488,167,-488,168,-488,182,-488,181,-488,183,-488,221,-488,222,-488,223,-488,236,-488,234,-488,229,-488,231,-488,230,-488,232,-488,233,-488,225,-488,215,-488,204,-488,209,-488,210,-488,163,-488,178,-488,144,-488,145,-488,151,-488,152,-488,149,-488,153,-488,208,-488,206,-488,140,-488,136,-488,137,-488,138,-488,164,-488,184,-488,185,-488,33,-488,126,-488,212,-488,213,-488,123,-491,159,-491,91,-491,203,-491,46,-491,43,-491,45,-491,42,-491,47,-491,37,-491,186,-491,124,-491,94,-491,38,-491,187,-491,62,-491,191,-491,60,-491,192,-491,188,-491,189,-491,190,-491,195,-491,196,-491,201,-491,202,-491,193,-491,194,-491,197,-491,198,-491,63,-491,169,-491,170,-491,172,-491,173,-491,174,-491,175,-491,176,-491,59,-491,10,-491,129,-491,143,-491,125,-491,148,-491,147,-491,142,-491,150,-491,41,-491,141,-491,58,-491,146,-491,160,-491,205,-491,44,-491,93,-491,211,-491,161,-491, + 29,2, /* actions: */ 214,583,218,584,215,471,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,582,-83,472, + 58,2, /* actions: */ 40,457,123,-246,159,-246,91,-246,203,-246,46,-246,43,-246,45,-246,42,-246,47,-246,37,-246,186,-246,124,-246,94,-246,38,-246,187,-246,62,-246,191,-246,60,-246,192,-246,188,-246,189,-246,190,-246,195,-246,196,-246,201,-246,202,-246,193,-246,194,-246,197,-246,198,-246,63,-246,169,-246,170,-246,172,-246,173,-246,174,-246,175,-246,176,-246,59,-246,10,-246,129,-246,143,-246,125,-246,148,-246,147,-246,142,-246,150,-246,41,-246,141,-246,58,-246,146,-246,160,-246,205,-246,44,-246,93,-246,211,-246,161,-246, /* gotos: */ -43,310,-38,468, + 11,0, /* actions: */ 44,-91,61,-91,158,-91,41,-91,124,-91,40,-485,123,-485,159,-485,91,-485,203,-485,46,-485, + 11,0, /* actions: */ 44,-93,61,-93,158,-93,41,-93,124,-93,40,-486,123,-486,159,-486,91,-486,203,-486,46,-486, + 3,0, /* actions: */ 221,586,222,587,223,588, + /* default action: */ -424, + /* default action: */ -425, + /* default action: */ -426, + 8,0, /* actions: */ 91,-290,203,-290,46,-290,44,-96,61,-96,158,-96,41,-96,124,-96, + 1,0, /* actions: */ 218,591, + 8,0, /* actions: */ 91,-293,203,-293,46,-293,44,-95,61,-95,158,-95,41,-95,124,-95, + 2,1, /* actions: */ 10,304,40,-496, /* gotos: */ -107,593, + 1,0, /* actions: */ 40,332, + 61,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,41,-5, /* gotos: */ -4,418,-87,595,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-88,549,-89,597,-91,553,-90,599, + 2,0, /* actions: */ 41,596,61,423, + /* default action: */ -88, + 1,0, /* actions: */ 44,598, + /* default action: */ -86, + /* default action: */ -82, + 46,19, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,585,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,590,209,299,210,315,163,323,178,592,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,61,-84,158,-84,41,-84,124,-84, /* gotos: */ -91,601,-78,554,-11,555,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,589,-77,488,-23,335,-17,390,-20,411,-21,436, + /* default action: */ -83, + 57,38, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600, /* gotos: */ -2,603,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 16,0, /* actions: */ 172,9,173,25,174,604,175,606,176,608,59,-8,10,-8,129,-8,143,-8,125,-8,148,-8,147,-8,142,-8,150,-8,41,-8,141,-8, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,605,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 18,0, /* actions: */ 169,11,170,13,172,-48,173,-48,174,-48,175,-48,176,-48,59,-48,10,-48,129,-48,143,-48,125,-48,148,-48,147,-48,142,-48,150,-48,41,-48,141,-48, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,607,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 18,0, /* actions: */ 169,11,170,13,172,-49,173,-49,174,-49,175,-49,176,-49,59,-49,10,-49,129,-49,143,-49,125,-49,148,-49,147,-49,142,-49,150,-49,41,-49,141,-49, + 57,38, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600, /* gotos: */ -2,609,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + /* default action: */ -50, + 44,25, /* actions: */ 124,611,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,585,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,627,209,299,210,315,163,323,178,592,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,512,212,600, /* gotos: */ -94,612,-93,614,-78,615,-11,616,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,626,-77,488,-23,335,-17,390,-20,411,-21,436,-87,629,-88,549,-89,597,-91,553,-90,599, + /* default action: */ -355, + 1,0, /* actions: */ 124,613, + /* default action: */ -357, + /* default action: */ -352, + 6,0, /* actions: */ 158,-97,124,-97,91,-439,203,-439,46,-439,44,-89, + 3,0, /* actions: */ 91,617,203,620,46,623, + 48,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,570,93,-236, /* gotos: */ -36,618,-27,302,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-19,559,-18,314,-93,200,-92,509,-51,567,-52,569, + 1,0, /* actions: */ 93,619, + 6,0, /* actions: */ 158,-98,124,-98,91,-294,203,-294,46,-294,44,-90, + 29,3, /* actions: */ 218,621,214,622,215,579,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,575,-81,577,-83,580, + 7,0, /* actions: */ 91,-292,203,-292,46,-292,158,-102,124,-102,44,-94,40,-486, + 9,0, /* actions: */ 158,-100,124,-100,44,-92,40,-485,123,-489,159,-489,91,-489,203,-489,46,-489, + 29,2, /* actions: */ 214,624,218,625,215,471,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,582,-83,472, + 9,0, /* actions: */ 158,-99,124,-99,44,-91,40,-485,123,-485,159,-485,91,-485,203,-485,46,-485, + 9,0, /* actions: */ 158,-101,124,-101,44,-93,40,-486,123,-486,159,-486,91,-486,203,-486,46,-486, + 6,0, /* actions: */ 91,-290,203,-290,46,-290,158,-104,124,-104,44,-96, + 1,0, /* actions: */ 218,628, + 6,0, /* actions: */ 91,-293,203,-293,46,-293,158,-103,124,-103,44,-95, + /* default action: */ -353, + /* default action: */ -356, + 25,1, /* actions: */ 44,633,172,-278,173,-278,174,-278,175,-278,176,-278,59,-278,10,-278,129,-278,143,-278,125,-278,148,-278,147,-278,142,-278,150,-278,41,-278,141,-278,169,-278,170,-278,58,-278,146,-278,160,-278,211,-278,161,-278,93,-278, /* gotos: */ -32,632, + /* default action: */ -252, + 48,23, /* actions: */ 212,634,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,213,486, /* gotos: */ -52,321,-31,485,-18,322,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,635,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 51,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,484,172,-278,173,-278,174,-278,175,-278,176,-278,59,-278,10,-278,129,-278,143,-278,125,-278,148,-278,147,-278,142,-278,150,-278,41,-278,141,-278,169,-278,170,-278,58,-278,146,-278,160,-278,211,-278,161,-278,93,-278, /* gotos: */ -32,636, + /* default action: */ -253, + 1,1, /* actions: */ 161,19, /* gotos: */ -35,18, + 25,1, /* actions: */ 44,640,172,-278,173,-278,174,-278,175,-278,176,-278,59,-278,10,-278,129,-278,143,-278,125,-278,148,-278,147,-278,142,-278,150,-278,41,-278,141,-278,169,-278,170,-278,58,-278,146,-278,160,-278,211,-278,161,-278,93,-278, /* gotos: */ -32,639, + /* default action: */ -250, + 48,23, /* actions: */ 212,641,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,213,486, /* gotos: */ -52,321,-31,485,-18,322,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,642,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 51,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,484,172,-278,173,-278,174,-278,175,-278,176,-278,59,-278,10,-278,129,-278,143,-278,125,-278,148,-278,147,-278,142,-278,150,-278,41,-278,141,-278,169,-278,170,-278,58,-278,146,-278,160,-278,211,-278,161,-278,93,-278, /* gotos: */ -32,643, + /* default action: */ -251, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,645,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 51,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,484,172,-278,173,-278,174,-278,175,-278,176,-278,59,-278,10,-278,129,-278,143,-278,125,-278,148,-278,147,-278,142,-278,150,-278,41,-278,141,-278,169,-278,170,-278,58,-278,146,-278,160,-278,211,-278,161,-278,93,-278, /* gotos: */ -32,646, + /* default action: */ -254, + /* default action: */ -255, + 3,1, /* actions: */ 161,19,10,-256,41,-256, /* gotos: */ -35,18, + 11,9, /* actions: */ 218,663,217,664,216,665,219,666,214,667,42,673,212,674,38,668,213,669,10,-456,41,-456, /* gotos: */ -54,650,-55,653,-56,684,-60,685,-61,682,-57,690,-134,671,-58,692,-135,661, + 2,1, /* actions: */ 10,304,41,-496, /* gotos: */ -107,651, + 1,0, /* actions: */ 41,652, + /* default action: */ -446, + 4,1, /* actions: */ 44,654,59,-474,10,-474,41,-474, /* gotos: */ -59,683, + 9,7, /* actions: */ 218,663,217,664,216,665,219,666,214,667,42,673,212,674,38,668,213,669, /* gotos: */ -60,655,-56,679,-57,680,-58,660,-61,682,-134,671,-135,661, + 4,1, /* actions: */ 44,656,59,-474,10,-474,41,-474, /* gotos: */ -59,678, + 9,6, /* actions: */ 42,673,212,674,218,663,217,664,216,665,219,666,214,667,38,668,213,669, /* gotos: */ -57,657,-61,670,-58,660,-134,671,-56,675,-135,661, + 4,1, /* actions: */ 44,659,59,-474,10,-474,41,-474, /* gotos: */ -59,658, + /* default action: */ -448, + 2,2, /* actions: */ 38,668,213,669, /* gotos: */ -58,660,-135,661, + /* default action: */ -475, + 5,1, /* actions: */ 218,663,217,664,216,665,219,666,214,667, /* gotos: */ -56,662, + /* default action: */ -473, + /* default action: */ -457, + /* default action: */ -458, + /* default action: */ -459, + /* default action: */ -460, + /* default action: */ -461, + /* default action: */ -471, + /* default action: */ -472, + /* default action: */ -466, + 9,1, /* actions: */ 218,663,217,664,216,665,219,666,214,667,44,-470,59,-470,10,-470,41,-470, /* gotos: */ -56,672, + /* default action: */ -469, + /* default action: */ -467, + /* default action: */ -468, + 1,0, /* actions: */ 61,676, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,677,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 30,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,-464,59,-464,10,-464,41,-464, + /* default action: */ -449, + 5,0, /* actions: */ 61,676,44,-463,59,-463,10,-463,41,-463, + 4,1, /* actions: */ 44,659,59,-474,10,-474,41,-474, /* gotos: */ -59,681, + /* default action: */ -450, + /* default action: */ -465, + /* default action: */ -451, + 5,0, /* actions: */ 61,676,44,-462,59,-462,10,-462,41,-462, + 4,1, /* actions: */ 44,686,59,-474,10,-474,41,-474, /* gotos: */ -59,689, + 9,6, /* actions: */ 42,673,212,674,218,663,217,664,216,665,219,666,214,667,38,668,213,669, /* gotos: */ -57,687,-61,670,-58,660,-134,671,-56,675,-135,661, + 4,1, /* actions: */ 44,659,59,-474,10,-474,41,-474, /* gotos: */ -59,688, + /* default action: */ -452, + /* default action: */ -453, + 4,1, /* actions: */ 44,659,59,-474,10,-474,41,-474, /* gotos: */ -59,691, + /* default action: */ -454, + /* default action: */ -455, + 2,1, /* actions: */ 59,695,10,696, /* gotos: */ -123,694, + /* default action: */ -447, + /* default action: */ -501, + /* default action: */ -502, + 2,1, /* actions: */ 46,705,203,706, /* gotos: */ -127,698, + 0,1, /* default action: */ -333, /* gotos: */ -128,699, + 71,3, /* actions: */ 214,41,218,42,215,43,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70,181,72,182,73,183,74,179,75,180,76,177,77,169,78,140,79,154,80,149,81,136,82,138,83,178,84,159,85,148,86,147,87,143,88,142,89,168,90,153,91,158,92,137,93,155,94,166,95,171,96,170,97,156,98,141,99,157,100,162,101,165,102,164,103,146,104,167,105,139,106,150,107,163,108,172,109,173,110,174,111,175,112,176,113, /* gotos: */ -84,700,-83,44,-105,71, + 0,1, /* default action: */ -334, /* gotos: */ -129,701, + 12,10, /* actions: */ 40,649,218,663,217,664,216,665,219,666,214,667,42,673,212,674,38,668,213,669,59,-456,10,-456, /* gotos: */ -53,702,-54,693,-55,653,-56,684,-60,685,-61,682,-57,690,-134,671,-58,692,-135,661, + 64,41, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,141,-5,148,-5,142,-5,143,-5, /* gotos: */ -22,703,-4,430,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 143,704, + /* default action: */ -335, + /* default action: */ -492, + /* default action: */ -493, + 14,0, /* actions: */ 40,-110,218,-110,217,-110,216,-110,219,-110,214,-110,42,-110,212,-110,38,-110,213,-110,59,-110,10,-110,46,-427,203,-427, + 14,0, /* actions: */ 40,-111,218,-111,217,-111,216,-111,219,-111,214,-111,42,-111,212,-111,38,-111,213,-111,59,-111,10,-111,46,-430,203,-430, + 14,0, /* actions: */ 40,-146,218,-146,217,-146,216,-146,219,-146,214,-146,42,-146,212,-146,38,-146,213,-146,59,-146,10,-146,46,-437,203,-437, + 14,0, /* actions: */ 40,-147,218,-147,217,-147,216,-147,219,-147,214,-147,42,-147,212,-147,38,-147,213,-147,59,-147,10,-147,46,-436,203,-436, + 14,0, /* actions: */ 40,-148,218,-148,217,-148,216,-148,219,-148,214,-148,42,-148,212,-148,38,-148,213,-148,59,-148,10,-148,46,-438,203,-438, + 14,0, /* actions: */ 40,-164,218,-164,217,-164,216,-164,219,-164,214,-164,42,-164,212,-164,38,-164,213,-164,59,-164,10,-164,46,-435,203,-435, + 14,0, /* actions: */ 40,-169,218,-169,217,-169,216,-169,219,-169,214,-169,42,-169,212,-169,38,-169,213,-169,59,-169,10,-169,46,-432,203,-432, + 14,0, /* actions: */ 40,-176,218,-176,217,-176,216,-176,219,-176,214,-176,42,-176,212,-176,38,-176,213,-176,59,-176,10,-176,46,-433,203,-433, + 14,0, /* actions: */ 40,-179,218,-179,217,-179,216,-179,219,-179,214,-179,42,-179,212,-179,38,-179,213,-179,59,-179,10,-179,46,-434,203,-434, + /* default action: */ -476, + /* default action: */ -439, + 0,1, /* default action: */ -477, /* gotos: */ -136,719, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,720,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 4,1, /* actions: */ 169,11,170,13,10,304,41,-496, /* gotos: */ -107,721, + 1,0, /* actions: */ 41,722, + /* default action: */ -478, + 2,1, /* actions: */ 218,725,214,726, /* gotos: */ -82,724, + /* default action: */ -107, + 68,0, /* actions: */ 203,-293,91,-293,46,-293,59,-105,10,-105,60,-105,128,-105,177,-105,139,-105,179,-105,180,-105,225,-105,162,-105,154,-105,155,-105,156,-105,157,-105,214,-105,218,-105,215,-105,221,-105,222,-105,223,-105,236,-105,234,-105,229,-105,231,-105,230,-105,232,-105,233,-105,217,-105,216,-105,219,-105,166,-105,165,-105,167,-105,168,-105,182,-105,181,-105,183,-105,204,-105,209,-105,210,-105,163,-105,178,-105,144,-105,145,-105,151,-105,152,-105,149,-105,153,-105,208,-105,206,-105,140,-105,136,-105,137,-105,138,-105,164,-105,171,-105,33,-105,184,-105,185,-105,126,-105,212,-105,143,-105,141,-105,148,-105,142,-105, + /* default action: */ -106, + /* default action: */ -108, + 71,0, /* actions: */ 59,-105,10,-105,60,-105,128,-105,177,-105,139,-105,179,-105,180,-105,225,-105,162,-105,154,-105,155,-105,156,-105,157,-105,214,-105,218,-105,215,-105,221,-105,222,-105,223,-105,236,-105,234,-105,229,-105,231,-105,230,-105,232,-105,233,-105,217,-105,216,-105,219,-105,166,-105,165,-105,167,-105,168,-105,182,-105,181,-105,183,-105,204,-105,209,-105,210,-105,163,-105,178,-105,144,-105,145,-105,151,-105,152,-105,149,-105,153,-105,208,-105,206,-105,140,-105,136,-105,137,-105,138,-105,164,-105,171,-105,33,-105,184,-105,185,-105,126,-105,212,-105,143,-105,141,-105,148,-105,142,-105,203,-430,91,-430,46,-430,123,-483,159,-483,40,-483, + 71,0, /* actions: */ 59,-106,10,-106,60,-106,128,-106,177,-106,139,-106,179,-106,180,-106,225,-106,162,-106,154,-106,155,-106,156,-106,157,-106,214,-106,218,-106,215,-106,221,-106,222,-106,223,-106,236,-106,234,-106,229,-106,231,-106,230,-106,232,-106,233,-106,217,-106,216,-106,219,-106,166,-106,165,-106,167,-106,168,-106,182,-106,181,-106,183,-106,204,-106,209,-106,210,-106,163,-106,178,-106,144,-106,145,-106,151,-106,152,-106,149,-106,153,-106,208,-106,206,-106,140,-106,136,-106,137,-106,138,-106,164,-106,171,-106,33,-106,184,-106,185,-106,126,-106,212,-106,143,-106,141,-106,148,-106,142,-106,203,-427,91,-427,46,-427,123,-482,159,-482,40,-482, + 3,0, /* actions: */ 203,731,91,735,46,738, + 29,4, /* actions: */ 218,733,214,734,215,579,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -82,732,-80,575,-81,577,-83,580, + /* default action: */ -109, + 69,0, /* actions: */ 203,-292,91,-292,46,-292,59,-105,10,-105,60,-105,128,-105,177,-105,139,-105,179,-105,180,-105,225,-105,162,-105,154,-105,155,-105,156,-105,157,-105,214,-105,218,-105,215,-105,221,-105,222,-105,223,-105,236,-105,234,-105,229,-105,231,-105,230,-105,232,-105,233,-105,217,-105,216,-105,219,-105,166,-105,165,-105,167,-105,168,-105,182,-105,181,-105,183,-105,204,-105,209,-105,210,-105,163,-105,178,-105,144,-105,145,-105,151,-105,152,-105,149,-105,153,-105,208,-105,206,-105,140,-105,136,-105,137,-105,138,-105,164,-105,171,-105,33,-105,184,-105,185,-105,126,-105,212,-105,143,-105,141,-105,148,-105,142,-105,40,-486, + 71,0, /* actions: */ 59,-106,10,-106,60,-106,128,-106,177,-106,139,-106,179,-106,180,-106,225,-106,162,-106,154,-106,155,-106,156,-106,157,-106,214,-106,218,-106,215,-106,221,-106,222,-106,223,-106,236,-106,234,-106,229,-106,231,-106,230,-106,232,-106,233,-106,217,-106,216,-106,219,-106,166,-106,165,-106,167,-106,168,-106,182,-106,181,-106,183,-106,204,-106,209,-106,210,-106,163,-106,178,-106,144,-106,145,-106,151,-106,152,-106,149,-106,153,-106,208,-106,206,-106,140,-106,136,-106,137,-106,138,-106,164,-106,171,-106,33,-106,184,-106,185,-106,126,-106,212,-106,143,-106,141,-106,148,-106,142,-106,40,-485,123,-489,159,-489,203,-489,91,-489,46,-489, + 48,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,570,93,-236, /* gotos: */ -36,736,-27,302,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-19,559,-18,314,-93,200,-92,509,-51,567,-52,569, + 1,0, /* actions: */ 93,737, + /* default action: */ -294, + 29,2, /* actions: */ 214,469,218,470,215,471,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,582,-83,472, + /* default action: */ -290, + /* default action: */ -442, + 0,1, /* default action: */ -443, /* gotos: */ -133,742, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,743,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 4,1, /* actions: */ 169,11,170,13,59,695,10,696, /* gotos: */ -123,744, + /* default action: */ -444, + 2,1, /* actions: */ 59,695,10,696, /* gotos: */ -123,746, + /* default action: */ -445, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,748,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 4,1, /* actions: */ 169,11,170,13,59,-326,10,-326, /* gotos: */ -122,749, + 2,1, /* actions: */ 59,695,10,696, /* gotos: */ -123,750, + 0,1, /* default action: */ -327, /* gotos: */ -124,751, + 64,41, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,141,-5,148,-5,142,-5,143,-5, /* gotos: */ -22,752,-4,430,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 1,0, /* actions: */ 143,753, + /* default action: */ -328, + 4,1, /* actions: */ 141,756,148,-380,142,-380,143,-380, /* gotos: */ -48,755, + /* default action: */ -382, + 52,24, /* actions: */ 205,768,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,212,543,59,-386,10,-386,58,-386,146,-386, /* gotos: */ -96,757,-18,764,-44,784,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509,-19,538, + 4,2, /* actions: */ 59,695,10,696,58,762,146,763, /* gotos: */ -112,758,-123,760, + 64,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,141,-5,148,-5,142,-5,143,-5, /* gotos: */ -4,759,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + /* default action: */ -383, + 67,0, /* actions: */ 146,761,177,-341,139,-341,179,-341,180,-341,225,-341,162,-341,154,-341,155,-341,156,-341,157,-341,214,-341,218,-341,215,-341,221,-341,222,-341,223,-341,236,-341,234,-341,229,-341,231,-341,230,-341,232,-341,233,-341,217,-341,216,-341,219,-341,166,-341,165,-341,167,-341,168,-341,182,-341,181,-341,183,-341,204,-341,209,-341,210,-341,163,-341,178,-341,144,-341,145,-341,151,-341,152,-341,149,-341,153,-341,208,-341,206,-341,140,-341,136,-341,137,-341,138,-341,164,-341,171,-341,33,-341,184,-341,185,-341,126,-341,212,-341,128,-341,148,-341,147,-341,143,-341,59,-341,10,-341,150,-341,141,-341,142,-341, + /* default action: */ -344, + /* default action: */ -342, + /* default action: */ -343, + 32,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,205,768,44,-279,59,-386,10,-386,58,-386,146,-386, /* gotos: */ -96,765, + 4,2, /* actions: */ 59,695,10,696,58,762,146,763, /* gotos: */ -112,766,-123,760, + 64,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,141,-5,148,-5,142,-5,143,-5, /* gotos: */ -4,767,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + /* default action: */ -384, + 42,19, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,585,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,782,209,299,210,315,163,323,178,592,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512, /* gotos: */ -93,769,-78,770,-11,771,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,781,-77,488,-23,335,-17,390,-20,411,-21,436, + /* default action: */ -387, + 7,0, /* actions: */ 59,-97,10,-97,58,-97,146,-97,91,-439,203,-439,46,-439, + 3,0, /* actions: */ 91,772,203,775,46,778, + 48,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,570,93,-236, /* gotos: */ -36,773,-27,302,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-19,559,-18,314,-93,200,-92,509,-51,567,-52,569, + 1,0, /* actions: */ 93,774, + 7,0, /* actions: */ 59,-98,10,-98,58,-98,146,-98,91,-294,203,-294,46,-294, + 29,3, /* actions: */ 218,776,214,777,215,579,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,575,-81,577,-83,580, + 8,0, /* actions: */ 91,-292,203,-292,46,-292,59,-102,10,-102,58,-102,146,-102,40,-486, + 10,0, /* actions: */ 59,-100,10,-100,58,-100,146,-100,40,-485,123,-489,159,-489,91,-489,203,-489,46,-489, + 29,2, /* actions: */ 214,779,218,780,215,471,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,582,-83,472, + 10,0, /* actions: */ 59,-99,10,-99,58,-99,146,-99,40,-485,123,-485,159,-485,91,-485,203,-485,46,-485, + 10,0, /* actions: */ 59,-101,10,-101,58,-101,146,-101,40,-486,123,-486,159,-486,91,-486,203,-486,46,-486, + 7,0, /* actions: */ 91,-290,203,-290,46,-290,59,-104,10,-104,58,-104,146,-104, + 1,0, /* actions: */ 218,783, + 7,0, /* actions: */ 91,-293,203,-293,46,-293,59,-103,10,-103,58,-103,146,-103, + 5,1, /* actions: */ 205,768,59,-386,10,-386,58,-386,146,-386, /* gotos: */ -96,785, + 4,2, /* actions: */ 59,695,10,696,58,762,146,763, /* gotos: */ -112,786,-123,760, + 64,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,141,-5,148,-5,142,-5,143,-5, /* gotos: */ -4,787,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + /* default action: */ -385, + /* default action: */ -381, + /* default action: */ -345, + /* default action: */ -346, + /* default action: */ -347, + 5,0, /* actions: */ 44,793,59,-376,10,-376,58,-376,146,-376, + 47,21, /* actions: */ 212,794,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,562,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,795,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 30,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,59,-377,10,-377,58,-377,146,-377, + 31,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,-279,59,-279,10,-279,58,-279,146,-279, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,798,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 30,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,59,-378,10,-378,58,-378,146,-378, + 1,0, /* actions: */ 143,800, + /* default action: */ -339, + /* default action: */ -373, + 4,0, /* actions: */ 59,803,150,-495,148,-495,143,-495, + /* default action: */ -504, + /* default action: */ -503, + 3,3, /* actions: */ 150,398,148,351,143,-350, /* gotos: */ -49,806,-28,809,-50,801, + 3,2, /* actions: */ 148,351,150,398,143,-350, /* gotos: */ -28,807,-50,397, + 1,0, /* actions: */ 143,808, + /* default action: */ -338, + 1,0, /* actions: */ 143,810, + /* default action: */ -340, + 101,0, /* actions: */ 220,812,61,-101,44,-93,40,-486,214,-486,217,-486,216,-486,218,-486,219,-486,166,-486,165,-486,167,-486,168,-486,182,-486,181,-486,183,-486,221,-486,222,-486,223,-486,236,-486,234,-486,229,-486,231,-486,230,-486,232,-486,233,-486,225,-486,215,-486,204,-486,209,-486,210,-486,163,-486,178,-486,144,-486,145,-486,151,-486,152,-486,149,-486,153,-486,208,-486,206,-486,140,-486,136,-486,137,-486,138,-486,164,-486,184,-486,185,-486,33,-486,126,-486,212,-486,213,-486,123,-486,159,-486,46,-486,203,-486,91,-486,43,-486,45,-486,42,-486,47,-486,37,-486,186,-486,124,-486,94,-486,38,-486,187,-486,62,-486,191,-486,60,-486,192,-486,188,-486,189,-486,190,-486,195,-486,196,-486,201,-486,202,-486,193,-486,194,-486,197,-486,198,-486,63,-486,169,-486,170,-486,172,-486,173,-486,174,-486,175,-486,176,-486,59,-486,10,-486,129,-486,143,-486,125,-486,148,-486,147,-486,142,-486,150,-486,41,-486,141,-486, + 46,25, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518, /* gotos: */ -18,813,-25,814,-93,200,-78,206,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,27,-23,335,-17,390,-20,411,-21,436,-92,509,-27,17,-26,496,-24,497, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-194,170,-194,172,-194,173,-194,174,-194,175,-194,176,-194,59,-194,10,-194,129,-194,143,-194,125,-194,148,-194,147,-194,142,-194,150,-194,41,-194,141,-194,58,-194,146,-194,160,-194,205,-194,44,-194,93,-194,211,-194,161,-194, + /* default action: */ -39, + 29,3, /* actions: */ 218,816,214,822,215,579,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,819,-81,577,-83,580, + 99,0, /* actions: */ 220,817,46,-292,203,-292,91,-292,43,-292,45,-292,42,-292,47,-292,37,-292,186,-292,124,-292,94,-292,38,-292,187,-292,62,-292,191,-292,60,-292,192,-292,188,-292,189,-292,190,-292,195,-292,196,-292,201,-292,202,-292,193,-292,194,-292,197,-292,198,-292,63,-292,169,-292,170,-292,172,-292,173,-292,174,-292,175,-292,176,-292,59,-292,10,-292,129,-292,143,-292,125,-292,148,-292,147,-292,142,-292,150,-292,41,-292,141,-292,61,-102,44,-94,40,-486,214,-486,217,-486,216,-486,218,-486,219,-486,166,-486,165,-486,167,-486,168,-486,182,-486,181,-486,183,-486,221,-486,222,-486,223,-486,236,-486,234,-486,229,-486,231,-486,230,-486,232,-486,233,-486,225,-486,215,-486,204,-486,209,-486,210,-486,163,-486,178,-486,144,-486,145,-486,151,-486,152,-486,149,-486,153,-486,208,-486,206,-486,140,-486,136,-486,137,-486,138,-486,164,-486,184,-486,185,-486,33,-486,126,-486,212,-486,213,-486, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,818,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-196,170,-196,172,-196,173,-196,174,-196,175,-196,176,-196,59,-196,10,-196,129,-196,143,-196,125,-196,148,-196,147,-196,142,-196,150,-196,41,-196,141,-196,58,-196,146,-196,160,-196,205,-196,44,-196,93,-196,211,-196,161,-196, + 49,3, /* actions: */ 40,457,214,-269,217,-269,216,-269,218,-269,219,-269,166,-269,165,-269,167,-269,168,-269,182,-269,181,-269,183,-269,221,-269,222,-269,223,-269,236,-269,234,-269,229,-269,231,-269,230,-269,232,-269,233,-269,225,-269,215,-269,204,-269,209,-269,210,-269,163,-269,178,-269,144,-269,145,-269,151,-269,152,-269,149,-269,153,-269,208,-269,206,-269,140,-269,136,-269,137,-269,138,-269,164,-269,184,-269,185,-269,33,-269,126,-269,212,-269,213,-269, /* gotos: */ -38,576,-41,820,-109,311, + 24,1, /* actions: */ 211,30,161,-74,169,-74,170,-74,172,-74,173,-74,174,-74,175,-74,176,-74,59,-74,10,-74,129,-74,143,-74,125,-74,148,-74,147,-74,142,-74,150,-74,41,-74,141,-74,58,-74,146,-74,160,-74,93,-74, /* gotos: */ -33,821, + /* default action: */ -75, + 101,0, /* actions: */ 220,823,61,-100,44,-92,40,-485,214,-485,217,-485,216,-485,218,-485,219,-485,166,-485,165,-485,167,-485,168,-485,182,-485,181,-485,183,-485,221,-485,222,-485,223,-485,236,-485,234,-485,229,-485,231,-485,230,-485,232,-485,233,-485,225,-485,215,-485,204,-485,209,-485,210,-485,163,-485,178,-485,144,-485,145,-485,151,-485,152,-485,149,-485,153,-485,208,-485,206,-485,140,-485,136,-485,137,-485,138,-485,164,-485,184,-485,185,-485,33,-485,126,-485,212,-485,213,-485,123,-489,159,-489,46,-489,203,-489,91,-489,43,-489,45,-489,42,-489,47,-489,37,-489,186,-489,124,-489,94,-489,38,-489,187,-489,62,-489,191,-489,60,-489,192,-489,188,-489,189,-489,190,-489,195,-489,196,-489,201,-489,202,-489,193,-489,194,-489,197,-489,198,-489,63,-489,169,-489,170,-489,172,-489,173,-489,174,-489,175,-489,176,-489,59,-489,10,-489,129,-489,143,-489,125,-489,148,-489,147,-489,142,-489,150,-489,41,-489,141,-489, + 46,25, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518, /* gotos: */ -18,824,-25,825,-93,200,-78,206,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,27,-23,335,-17,390,-20,411,-21,436,-92,509,-27,17,-26,496,-24,497, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-195,170,-195,172,-195,173,-195,174,-195,175,-195,176,-195,59,-195,10,-195,129,-195,143,-195,125,-195,148,-195,147,-195,142,-195,150,-195,41,-195,141,-195,58,-195,146,-195,160,-195,205,-195,44,-195,93,-195,211,-195,161,-195, + /* default action: */ -40, + 48,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518,212,570,93,-236, /* gotos: */ -36,827,-27,302,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-19,559,-18,314,-93,200,-92,509,-51,567,-52,569, + 1,0, /* actions: */ 93,828, + 50,0, /* actions: */ 220,829,46,-294,203,-294,91,-294,43,-294,45,-294,42,-294,47,-294,37,-294,186,-294,124,-294,94,-294,38,-294,187,-294,62,-294,191,-294,60,-294,192,-294,188,-294,189,-294,190,-294,195,-294,196,-294,201,-294,202,-294,193,-294,194,-294,197,-294,198,-294,63,-294,169,-294,170,-294,172,-294,173,-294,174,-294,175,-294,176,-294,59,-294,10,-294,129,-294,143,-294,125,-294,148,-294,147,-294,142,-294,150,-294,41,-294,141,-294,61,-98,44,-90, + 46,25, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,184,513,185,515,33,517,126,518, /* gotos: */ -18,224,-25,830,-93,200,-78,206,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,27,-23,335,-17,390,-20,411,-21,436,-92,509,-27,17,-26,496,-24,497, + /* default action: */ -37, + /* default action: */ -256, + 47,26, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518, /* gotos: */ -12,833,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,520,-93,200,-92,509, + 6,2, /* actions: */ 169,11,170,13,59,695,10,696,58,762,146,763, /* gotos: */ -112,834,-123,760, + 63,40, /* actions: */ 177,36,139,126,179,132,180,137,225,122,162,250,154,342,155,353,156,362,157,363,214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,204,381,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,420,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,600,128,602,59,-5,10,-5,148,-5,147,-5,143,-5, /* gotos: */ -4,835,-3,5,-2,24,-9,35,-76,142,-6,248,-7,249,-8,361,-10,364,-13,365,-12,366,-25,16,-27,17,-77,27,-11,367,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,380,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,521,-93,533,-92,545,-87,548,-88,549,-89,597,-91,553,-90,599, + 3,2, /* actions: */ 148,351,147,832,143,-350, /* gotos: */ -29,836,-28,350, + /* default action: */ -349, + 52,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,169,-233,170,-233,172,-233,173,-233,174,-233,175,-233,176,-233,59,-233,10,-233,129,-233,143,-233,125,-233,148,-233,147,-233,142,-233,150,-233,41,-233,141,-233,58,-233,146,-233,160,-233,205,-233,44,-233,93,-233,211,-233,161,-233, + 3,1, /* actions: */ 44,841,10,566,125,-498, /* gotos: */ -108,839, + 1,0, /* actions: */ 125,840, + /* default action: */ -298, + 47,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,125,-500, /* gotos: */ -18,562,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 50,31, /* actions: */ 214,207,218,210,215,294,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,217,208,216,209,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,225,122,204,295,209,299,210,315,163,328,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,454,171,504,33,506,184,513,185,515,126,518,212,878,213,486,41,-272, /* gotos: */ -12,413,-110,843,-40,845,-25,16,-27,17,-77,27,-11,305,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-78,206,-76,292,-23,335,-17,390,-20,411,-21,436,-26,496,-24,497,-18,848,-93,200,-92,509,-51,872,-52,569,-31,881, + 1,0, /* actions: */ 41,844, + /* default action: */ -273, + 0,1, /* default action: */ -274, /* gotos: */ -111,846, + 1,0, /* actions: */ 41,847, + /* default action: */ -275, + 32,0, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,849,205,256,169,-62,170,-62,10,-62,41,-62, + 48,25, /* actions: */ 212,863,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,213,486, /* gotos: */ -19,850,-31,862,-51,866,-18,314,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509,-52,569, + 2,1, /* actions: */ 44,852,41,-278, /* gotos: */ -32,851, + /* default action: */ -257, + 48,24, /* actions: */ 212,853,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,213,486, /* gotos: */ -51,856,-18,255,-31,485,-52,569,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,854,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 28,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,484,41,-278, /* gotos: */ -32,855, + /* default action: */ -260, + 2,1, /* actions: */ 44,858,41,-278, /* gotos: */ -32,857, + /* default action: */ -264, + 48,23, /* actions: */ 212,859,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,213,486, /* gotos: */ -52,321,-31,485,-18,322,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,860,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 28,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,484,41,-278, /* gotos: */ -32,861, + /* default action: */ -266, + /* default action: */ -258, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,864,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 28,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,484,41,-278, /* gotos: */ -32,865, + /* default action: */ -259, + 2,1, /* actions: */ 44,868,41,-278, /* gotos: */ -32,867, + /* default action: */ -263, + 48,23, /* actions: */ 212,869,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,213,486, /* gotos: */ -52,321,-31,485,-18,322,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,870,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 28,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,484,41,-278, /* gotos: */ -32,871, + /* default action: */ -265, + 2,1, /* actions: */ 44,874,41,-278, /* gotos: */ -32,873, + /* default action: */ -261, + 48,23, /* actions: */ 212,875,214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518,213,486, /* gotos: */ -52,321,-31,485,-18,322,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,876,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 28,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,484,41,-278, /* gotos: */ -32,877, + /* default action: */ -262, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,879,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 28,1, /* actions: */ 43,146,45,148,42,150,47,152,37,154,186,156,124,158,94,160,38,162,187,164,62,166,191,168,60,170,192,172,188,174,189,176,190,178,195,180,196,182,201,184,202,186,193,188,194,190,197,192,198,194,63,196,44,484,41,-278, /* gotos: */ -32,880, + /* default action: */ -267, + /* default action: */ -268, + 108,0, /* actions: */ 220,883,61,-99,40,-485,214,-485,217,-485,216,-485,218,-485,219,-485,166,-485,165,-485,167,-485,168,-485,182,-485,181,-485,183,-485,221,-485,222,-485,223,-485,236,-485,234,-485,229,-485,231,-485,230,-485,232,-485,233,-485,225,-485,215,-485,204,-485,209,-485,210,-485,163,-485,178,-485,144,-485,145,-485,151,-485,152,-485,149,-485,153,-485,208,-485,206,-485,140,-485,136,-485,137,-485,138,-485,164,-485,184,-485,185,-485,33,-485,126,-485,212,-485,213,-485,123,-485,159,-485,46,-485,203,-485,91,-485,43,-485,45,-485,42,-485,47,-485,37,-485,186,-485,124,-485,94,-485,38,-485,187,-485,62,-485,191,-485,60,-485,192,-485,188,-485,189,-485,190,-485,195,-485,196,-485,201,-485,202,-485,193,-485,194,-485,197,-485,198,-485,63,-485,169,-485,170,-485,172,-485,173,-485,174,-485,175,-485,176,-485,59,-485,10,-485,129,-485,143,-485,125,-485,148,-485,147,-485,142,-485,150,-485,41,-485,141,-485,58,-485,146,-485,160,-485,205,-485,44,-485,93,-485,211,-485,161,-485, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,371,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 108,0, /* actions: */ 220,885,61,-101,40,-486,214,-486,217,-486,216,-486,218,-486,219,-486,166,-486,165,-486,167,-486,168,-486,182,-486,181,-486,183,-486,221,-486,222,-486,223,-486,236,-486,234,-486,229,-486,231,-486,230,-486,232,-486,233,-486,225,-486,215,-486,204,-486,209,-486,210,-486,163,-486,178,-486,144,-486,145,-486,151,-486,152,-486,149,-486,153,-486,208,-486,206,-486,140,-486,136,-486,137,-486,138,-486,164,-486,184,-486,185,-486,33,-486,126,-486,212,-486,213,-486,123,-486,159,-486,46,-486,203,-486,91,-486,43,-486,45,-486,42,-486,47,-486,37,-486,186,-486,124,-486,94,-486,38,-486,187,-486,62,-486,191,-486,60,-486,192,-486,188,-486,189,-486,190,-486,195,-486,196,-486,201,-486,202,-486,193,-486,194,-486,197,-486,198,-486,63,-486,169,-486,170,-486,172,-486,173,-486,174,-486,175,-486,176,-486,59,-486,10,-486,129,-486,143,-486,125,-486,148,-486,147,-486,142,-486,150,-486,41,-486,141,-486,58,-486,146,-486,160,-486,205,-486,44,-486,93,-486,211,-486,161,-486, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,813,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 29,3, /* actions: */ 218,887,214,888,215,579,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,819,-81,577,-83,580, + 106,0, /* actions: */ 220,817,46,-292,203,-292,91,-292,43,-292,45,-292,42,-292,47,-292,37,-292,186,-292,124,-292,94,-292,38,-292,187,-292,62,-292,191,-292,60,-292,192,-292,188,-292,189,-292,190,-292,195,-292,196,-292,201,-292,202,-292,193,-292,194,-292,197,-292,198,-292,63,-292,169,-292,170,-292,172,-292,173,-292,174,-292,175,-292,176,-292,59,-292,10,-292,129,-292,143,-292,125,-292,148,-292,147,-292,142,-292,150,-292,41,-292,141,-292,58,-292,146,-292,160,-292,205,-292,44,-292,93,-292,211,-292,161,-292,61,-102,40,-486,214,-486,217,-486,216,-486,218,-486,219,-486,166,-486,165,-486,167,-486,168,-486,182,-486,181,-486,183,-486,221,-486,222,-486,223,-486,236,-486,234,-486,229,-486,231,-486,230,-486,232,-486,233,-486,225,-486,215,-486,204,-486,209,-486,210,-486,163,-486,178,-486,144,-486,145,-486,151,-486,152,-486,149,-486,153,-486,208,-486,206,-486,140,-486,136,-486,137,-486,138,-486,164,-486,184,-486,185,-486,33,-486,126,-486,212,-486,213,-486, + 108,0, /* actions: */ 220,889,61,-100,40,-485,214,-485,217,-485,216,-485,218,-485,219,-485,166,-485,165,-485,167,-485,168,-485,182,-485,181,-485,183,-485,221,-485,222,-485,223,-485,236,-485,234,-485,229,-485,231,-485,230,-485,232,-485,233,-485,225,-485,215,-485,204,-485,209,-485,210,-485,163,-485,178,-485,144,-485,145,-485,151,-485,152,-485,149,-485,153,-485,208,-485,206,-485,140,-485,136,-485,137,-485,138,-485,164,-485,184,-485,185,-485,33,-485,126,-485,212,-485,213,-485,123,-489,159,-489,46,-489,203,-489,91,-489,43,-489,45,-489,42,-489,47,-489,37,-489,186,-489,124,-489,94,-489,38,-489,187,-489,62,-489,191,-489,60,-489,192,-489,188,-489,189,-489,190,-489,195,-489,196,-489,201,-489,202,-489,193,-489,194,-489,197,-489,198,-489,63,-489,169,-489,170,-489,172,-489,173,-489,174,-489,175,-489,176,-489,59,-489,10,-489,129,-489,143,-489,125,-489,148,-489,147,-489,142,-489,150,-489,41,-489,141,-489,58,-489,146,-489,160,-489,205,-489,44,-489,93,-489,211,-489,161,-489, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,824,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 55,0, /* actions: */ 186,891,46,-425,203,-425,91,-425,43,-425,45,-425,42,-425,47,-425,37,-425,124,-425,94,-425,38,-425,187,-425,62,-425,191,-425,60,-425,192,-425,188,-425,189,-425,190,-425,195,-425,196,-425,201,-425,202,-425,193,-425,194,-425,197,-425,198,-425,63,-425,169,-425,170,-425,172,-425,173,-425,174,-425,175,-425,176,-425,59,-425,10,-425,129,-425,143,-425,125,-425,148,-425,147,-425,142,-425,150,-425,41,-425,141,-425,58,-425,146,-425,160,-425,205,-425,44,-425,93,-425,211,-425,161,-425, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,892,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,-206,45,-206,42,-206,47,-206,37,-206,186,156,124,-206,94,-206,38,-206,187,-206,62,-206,191,-206,60,-206,192,-206,188,-206,189,-206,190,-206,195,-206,196,-206,201,-206,202,-206,193,-206,194,-206,197,-206,198,-206,63,-206,169,-206,170,-206,172,-206,173,-206,174,-206,175,-206,176,-206,59,-206,10,-206,129,-206,143,-206,125,-206,148,-206,147,-206,142,-206,150,-206,41,-206,141,-206,58,-206,146,-206,160,-206,205,-206,44,-206,93,-206,211,-206,161,-206, + 55,0, /* actions: */ 186,894,46,-426,203,-426,91,-426,43,-426,45,-426,42,-426,47,-426,37,-426,124,-426,94,-426,38,-426,187,-426,62,-426,191,-426,60,-426,192,-426,188,-426,189,-426,190,-426,195,-426,196,-426,201,-426,202,-426,193,-426,194,-426,197,-426,198,-426,63,-426,169,-426,170,-426,172,-426,173,-426,174,-426,175,-426,176,-426,59,-426,10,-426,129,-426,143,-426,125,-426,148,-426,147,-426,142,-426,150,-426,41,-426,141,-426,58,-426,146,-426,160,-426,205,-426,44,-426,93,-426,211,-426,161,-426, + 46,21, /* actions: */ 214,207,217,208,216,209,218,210,219,211,166,212,165,213,167,214,168,215,182,216,181,217,183,218,221,226,222,227,223,228,236,229,234,234,229,261,231,266,230,270,232,274,233,284,225,122,215,294,204,295,209,299,210,315,163,323,178,330,144,344,145,355,151,373,152,383,149,391,153,402,208,412,206,417,140,427,136,437,137,443,138,448,164,512,184,513,185,515,33,517,126,518, /* gotos: */ -18,895,-93,200,-78,206,-11,219,-74,225,-85,233,-75,258,-66,259,-65,264,-67,265,-73,269,-71,273,-72,283,-15,291,-76,292,-77,488,-23,335,-17,390,-20,411,-21,436,-92,509, + 52,0, /* actions: */ 43,-207,45,-207,42,-207,47,-207,37,-207,186,156,124,-207,94,-207,38,-207,187,-207,62,-207,191,-207,60,-207,192,-207,188,-207,189,-207,190,-207,195,-207,196,-207,201,-207,202,-207,193,-207,194,-207,197,-207,198,-207,63,-207,169,-207,170,-207,172,-207,173,-207,174,-207,175,-207,176,-207,59,-207,10,-207,129,-207,143,-207,125,-207,148,-207,147,-207,142,-207,150,-207,41,-207,141,-207,58,-207,146,-207,160,-207,205,-207,44,-207,93,-207,211,-207,161,-207, + 29,3, /* actions: */ 218,887,214,888,215,579,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,575,-81,577,-83,580, + 29,2, /* actions: */ 214,882,218,884,215,471,124,45,94,46,38,47,187,48,188,49,189,50,195,51,62,52,191,53,60,54,192,55,201,56,202,57,43,58,45,59,42,60,212,61,47,62,37,63,186,64,126,65,184,66,185,67,199,68,200,69,96,70, /* gotos: */ -80,582,-83,472, + /* default action: */ -230, + /* default action: */ -229, + /* default action: */ -58, + }); + + tables.RuleLhsNonTerminals = new byte[] {97, 98, 1, 4, 3, 3, 3, 3, 2, 2, 101, 2, 102, 2, 2, 2, 2, 2, 103, 9, 9, 9, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 10, 10, 10, 10, 10, 44, 44, 44, 12, 12, 12, 12, 12, 12, 12, 12, 25, 25, 26, 26, 26, 104, 33, 27, 27, 27, 27, 27, 27, 27, 27, 87, 87, 87, 87, 87, 90, 90, 88, 88, 89, 89, 91, 91, 91, 91, 91, 91, 91, 91, 93, 93, 93, 93, 93, 93, 93, 93, 82, 82, 45, 45, 45, 84, 84, 84, 84, 84, 86, 86, 30, 106, 30, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 36, 36, 36, 36, 36, 36, 38, 38, 38, 38, 43, 43, 39, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 109, 41, 42, 110, 42, 111, 42, 31, 32, 32, 19, 19, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 113, 115, 11, 116, 117, 11, 11, 118, 119, 11, 11, 11, 120, 20, 20, 20, 121, 21, 122, 124, 21, 125, 21, 126, 21, 128, 129, 21, 22, 17, 17, 17, 17, 112, 112, 112, 112, 114, 114, 114, 29, 29, 28, 28, 94, 94, 95, 95, 95, 95, 130, 35, 24, 24, 24, 23, 23, 23, 23, 23, 23, 131, 34, 132, 34, 49, 49, 50, 37, 37, 37, 47, 47, 46, 46, 48, 48, 48, 96, 96, 5, 5, 66, 66, 65, 67, 75, 73, 71, 71, 69, 69, 68, 68, 72, 72, 70, 70, 64, 64, 63, 63, 63, 62, 62, 62, 62, 85, 79, 79, 79, 79, 79, 74, 74, 74, 74, 74, 74, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 15, 92, 76, 14, 133, 14, 14, 53, 53, 54, 54, 54, 54, 54, 54, 54, 54, 54, 56, 56, 56, 56, 56, 55, 55, 61, 60, 60, 134, 134, 57, 57, 135, 135, 58, 59, 59, 16, 136, 16, 51, 51, 52, 77, 77, 77, 80, 80, 80, 80, 81, 81, 81, 127, 127, 99, 99, 107, 107, 108, 108, 108, 123, 123, 100, 100, }; + tables.RuleRhsLengths = new byte[] {2, 0, 2, 2, 0, 1, 3, 2, 1, 2, 0, 5, 0, 5, 3, 1, 1, 1, 0, 4, 3, 3, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 3, 3, 3, 6, 5, 5, 5, 3, 3, 3, 5, 5, 3, 3, 3, 3, 3, 5, 3, 4, 2, 1, 3, 3, 3, 3, 2, 2, 1, 1, 1, 1, 4, 4, 0, 5, 2, 3, 4, 5, 4, 5, 2, 2, 2, 1, 3, 2, 1, 2, 1, 3, 2, 1, 3, 1, 4, 3, 3, 3, 3, 2, 1, 1, 4, 3, 3, 3, 3, 2, 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 5, 3, 6, 5, 5, 5, 5, 4, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 1, 0, 2, 2, 5, 2, 3, 2, 4, 4, 6, 0, 1, 2, 5, 2, 5, 4, 7, 3, 1, 1, 4, 3, 5, 7, 2, 5, 4, 6, 7, 9, 3, 1, 0, 2, 1, 0, 3, 0, 4, 2, 2, 0, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 4, 3, 2, 4, 4, 4, 3, 1, 5, 2, 1, 2, 6, 6, 0, 0, 7, 0, 0, 7, 1, 0, 0, 9, 1, 1, 0, 5, 3, 3, 0, 6, 0, 0, 8, 0, 5, 0, 6, 0, 0, 9, 4, 6, 5, 5, 4, 1, 1, 1, 2, 1, 1, 1, 1, 5, 0, 2, 1, 1, 0, 2, 1, 3, 0, 5, 2, 4, 4, 2, 4, 4, 3, 2, 1, 0, 5, 0, 5, 1, 2, 4, 1, 4, 2, 0, 1, 1, 2, 4, 5, 5, 0, 2, 0, 2, 1, 2, 3, 3, 3, 3, 3, 3, 0, 3, 1, 2, 3, 3, 0, 3, 0, 2, 1, 2, 3, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 2, 4, 2, 6, 4, 4, 2, 4, 2, 2, 1, 0, 1, 1, 1, 1, 1, 1, 3, 3, 1, 3, 1, 1, 2, 1, 1, 1, 2, 0, 2, 1, 0, 5, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, }; + } + +#if DEBUG + protected override void InitializeMetadata(ParserTables tables) { + tables.NonTerminalNames = new string[] {"", +"program", "stmt", "stmts", "compstmt", + "ensure_opt", "jump_statement", "jump_statement_with_parameters", "jump_statement_parameterless", + "alias_statement", "conditional_statement", "primary", "expr", "expression_statement", + "superclass", "var_ref", "singleton", "case_expression", "arg", "args", + "block_expression", "declaration_expression", "body", "method_call", "block_call", + "command_call", "block_command", "command", "else_opt", "if_tail", "undef_list", + "block_reference", "opt_block_reference", "cmd_brace_block", "brace_block", + "do_block", "array_key", "when_args", "paren_args", "open_args", "closed_args", + "command_args", "command_args_content", "opt_paren_args", "compound_rhs", + "qualified_module_name", "rescue_clauses", "rescue_clauses_opt", "rescue_clause", + "when_clauses", "when_clause", "maplets", "maplet", "parameters_declaration", + "parameters", "parameter_list", "parameter", "array_parameter", "block_parameter", + "block_parameter_opt", "default_parameter_list", "default_parameter", "string_embedded_variable", + "string_content", "string_contents", "string", "string_concatenation", + "shell_string", "word", "word_list", "verbatim_word_list", "words", "verbatim_words", + "regexp", "numeric_literal", "immutable_string", "match_reference", "operation", + "variable", "sym", "operation2", "operation3", "module_name", "op", "method_name", + "symbol", "method_name_or_symbol", "compound_lhs", "compound_lhs_head", + "compound_lhs_item", "compound_lhs_tail", "compound_lhs_node", "var_lhs", + "lhs", "block_parameters", "block_parameters_opt", "exc_var", "$accept", + "@1", "opt_terms", "terms", "@2", "@3", "@4", "@5", "reswords", "@6", "opt_nl", + "trailer", "@7", "@8", "@9", "then", "@10", "do", "@11", "@12", "@13", + "@14", "@15", "@16", "@17", "@18", "term", "@19", "@20", "@21", "dot_or_colon", + "@22", "@23", "@24", "@25", "@26", "@27", "array_parameter_mark", "block_parameter_mark", + "@28", }; + tables.RuleRhsSymbols = new short[] { + -1, 129, // 1 + // 2 + -98, -4, // 3 + -3, -99, // 4 + // 5 + -2, // 6 + -3, -100, -2, // 7 + 128, -2, // 8 + -9, // 9 + 139, -30, // 10 + // 11 + 179, -101, 123, -4, 125, // 12 + // 13 + 180, -102, 123, -4, 125, // 14 + -76, 220, -25, // 15 + -6, // 16 + -10, // 17 + -13, // 18 + // 19 + 177, -86, -103, -86, // 20 + 177, 216, 216, // 21 + 177, 216, -76, // 22 + -7, // 23 + -8, // 24 + 162, -39, // 25 + 154, -39, // 26 + 155, -39, // 27 + 162, // 28 + 154, // 29 + 155, // 30 + 156, // 31 + 157, // 32 + -12, // 33 + -93, 61, -25, // 34 + -87, 61, -25, // 35 + -92, 220, -25, // 36 + -11, 91, -36, 93, 220, -25, // 37 + -11, 46, 214, 220, -25, // 38 + -11, 46, 218, 220, -25, // 39 + -11, 203, 214, 220, -25, // 40 + -93, 61, -44, // 41 + -87, 61, -18, // 42 + -87, 61, -44, // 43 + -18, 63, -8, 58, -18, // 44 + -18, 63, -18, 58, -8, // 45 + -2, 172, -12, // 46 + -2, 173, -12, // 47 + -2, 174, -12, // 48 + -2, 175, -12, // 49 + -2, 176, -2, // 50 + -18, 63, -8, 58, -8, // 51 + -19, 44, -18, // 52 + -19, 44, 212, -18, // 53 + 212, -18, // 54 + -25, // 55 + -12, 169, -12, // 56 + -12, 170, -12, // 57 + -12, 169, -6, // 58 + -12, 170, -6, // 59 + 171, -12, // 60 + 33, -25, // 61 + -18, // 62 + -27, // 63 + -26, // 64 + -24, // 65 + -24, 46, -80, -41, // 66 + -24, 203, -80, -41, // 67 + // 68 + 211, -104, -95, -4, 125, // 69 + -77, -41, // 70 + -77, -41, -33, // 71 + -11, 46, -80, -41, // 72 + -11, 46, -80, -41, -33, // 73 + -11, 203, -80, -41, // 74 + -11, 203, -80, -41, -33, // 75 + 164, -41, // 76 + 163, -41, // 77 + -88, -89, // 78 + -88, // 79 + 206, -87, 41, // 80 + -88, -90, // 81 + -90, // 82 + 212, -91, // 83 + 212, // 84 + -88, -89, 44, // 85 + -89, 44, // 86 + -91, // 87 + 206, -87, 41, // 88 + -78, // 89 + -11, 91, -36, 93, // 90 + -11, 46, 214, // 91 + -11, 203, 214, // 92 + -11, 46, 218, // 93 + -11, 203, 218, // 94 + 204, 218, // 95 + -76, // 96 + -78, // 97 + -11, 91, -36, 93, // 98 + -11, 46, 214, // 99 + -11, 203, 214, // 100 + -11, 46, 218, // 101 + -11, 203, 218, // 102 + 204, 218, // 103 + -76, // 104 + 218, // 105 + 214, // 106 + 204, -82, // 107 + -82, // 108 + -11, 203, -82, // 109 + 214, // 110 + 218, // 111 + 215, // 112 + -83, // 113 + -105, // 114 + -84, // 115 + -85, // 116 + -86, // 117 + // 118 + -30, 44, -106, -86, // 119 + 124, // 120 + 94, // 121 + 38, // 122 + 187, // 123 + 188, // 124 + 189, // 125 + 195, // 126 + 62, // 127 + 191, // 128 + 60, // 129 + 192, // 130 + 201, // 131 + 202, // 132 + 43, // 133 + 45, // 134 + 42, // 135 + 212, // 136 + 47, // 137 + 37, // 138 + 186, // 139 + 126, // 140 + 184, // 141 + 185, // 142 + 199, // 143 + 200, // 144 + 96, // 145 + 181, // 146 + 182, // 147 + 183, // 148 + 179, // 149 + 180, // 150 + 177, // 151 + 169, // 152 + 140, // 153 + 154, // 154 + 149, // 155 + 136, // 156 + 138, // 157 + 178, // 158 + 159, // 159 + 148, // 160 + 147, // 161 + 143, // 162 + 142, // 163 + 168, // 164 + 153, // 165 + 158, // 166 + 137, // 167 + 155, // 168 + 166, // 169 + 171, // 170 + 170, // 171 + 156, // 172 + 141, // 173 + 157, // 174 + 162, // 175 + 165, // 176 + 164, // 177 + 146, // 178 + 167, // 179 + 139, // 180 + 150, // 181 + 163, // 182 + 172, // 183 + 173, // 184 + 174, // 185 + 175, // 186 + 176, // 187 + -93, 61, -18, // 188 + -93, 61, -18, 176, -18, // 189 + -93, 61, -18, 176, -8, // 190 + -92, 220, -18, // 191 + -11, 91, -36, 93, 220, -18, // 192 + -11, 46, 214, 220, -18, // 193 + -11, 46, 218, 220, -18, // 194 + -11, 203, 214, 220, -18, // 195 + -11, 203, 218, 220, -18, // 196 + 204, 218, 220, -18, // 197 + -76, 220, -18, // 198 + -18, 43, -18, // 199 + -18, 45, -18, // 200 + -18, 42, -18, // 201 + -18, 47, -18, // 202 + -18, 37, -18, // 203 + -18, 186, -18, // 204 + 236, 221, 186, -18, // 205 + 236, 222, 186, -18, // 206 + 236, 223, 186, -18, // 207 + 184, -18, // 208 + 185, -18, // 209 + -18, 124, -18, // 210 + -18, 94, -18, // 211 + -18, 38, -18, // 212 + -18, 187, -18, // 213 + -18, 62, -18, // 214 + -18, 191, -18, // 215 + -18, 60, -18, // 216 + -18, 192, -18, // 217 + -18, 188, -18, // 218 + -18, 189, -18, // 219 + -18, 190, -18, // 220 + -18, 195, -18, // 221 + -18, 196, -18, // 222 + 33, -18, // 223 + 126, -18, // 224 + -18, 201, -18, // 225 + -18, 202, -18, // 226 + -18, 193, -18, // 227 + -18, 194, -18, // 228 + -18, 193, -8, // 229 + -18, 194, -8, // 230 + -18, 197, -18, // 231 + -18, 198, -18, // 232 + 178, -107, -18, // 233 + -18, 63, -18, 58, -18, // 234 + -11, // 235 + // 236 + -27, -107, // 237 + -19, -108, // 238 + -19, 44, 212, -18, -107, // 239 + -51, -108, // 240 + 212, -18, -107, // 241 + 40, 41, // 242 + 40, -39, -107, 41, // 243 + 40, -24, -107, 41, // 244 + 40, -19, 44, -24, -107, 41, // 245 + // 246 + -38, // 247 + -19, -32, // 248 + -19, 44, 212, -18, -32, // 249 + -51, -32, // 250 + -51, 44, 212, -18, -32, // 251 + -19, 44, -51, -32, // 252 + -19, 44, -51, 44, 212, -18, -32, // 253 + 212, -18, -32, // 254 + -31, // 255 + -27, // 256 + -18, 44, -19, -32, // 257 + -18, 44, -31, // 258 + -18, 44, 212, -18, -32, // 259 + -18, 44, -19, 44, 212, -18, -32, // 260 + -51, -32, // 261 + -51, 44, 212, -18, -32, // 262 + -18, 44, -51, -32, // 263 + -18, 44, -19, 44, -51, -32, // 264 + -18, 44, -51, 44, 212, -18, -32, // 265 + -18, 44, -19, 44, -51, 44, 212, -18, -32, // 266 + 212, -18, -32, // 267 + -31, // 268 + // 269 + -109, -42, // 270 + -39, // 271 + // 272 + 208, -110, 41, // 273 + // 274 + 208, -40, -111, 41, // 275 + 213, -18, // 276 + 44, -31, // 277 + // 278 + -18, // 279 + -19, 44, -18, // 280 + -74, // 281 + -85, // 282 + -75, // 283 + -66, // 284 + -67, // 285 + -73, // 286 + -71, // 287 + -72, // 288 + -15, // 289 + -76, // 290 + 215, // 291 + -11, 203, 218, // 292 + 204, 218, // 293 + -11, 91, -36, 93, // 294 + 209, -36, 93, // 295 + 210, 125, // 296 + 210, -51, -108, 125, // 297 + 210, -19, -108, 125, // 298 + 163, 40, -39, 41, // 299 + 163, 40, 41, // 300 + 163, // 301 + 178, -107, 40, -12, 41, // 302 + -77, -34, // 303 + -23, // 304 + -23, -34, // 305 + 144, -12, -112, -4, -29, 143, // 306 + 145, -12, -112, -4, -28, 143, // 307 + // 308 + // 309 + 151, -113, -12, -114, -115, -4, 143, // 310 + // 311 + // 312 + 152, -116, -12, -114, -117, -4, 143, // 313 + -17, // 314 + // 315 + // 316 + 153, -94, 158, -118, -12, -114, -119, -4, 143, // 317 + -20, // 318 + -21, // 319 + // 320 + 208, -12, -120, -107, 41, // 321 + 206, -4, 41, // 322 + 140, -22, 143, // 323 + // 324 + 136, -45, -14, -121, -22, 143, // 325 + // 326 + // 327 + 136, 201, -12, -122, -123, -124, -22, 143, // 328 + // 329 + 137, -45, -125, -22, 143, // 330 + // 331 + 138, -84, -126, -53, -22, 143, // 332 + // 333 + // 334 + 138, -16, -127, -128, -84, -129, -53, -22, 143, // 335 + -4, -47, -28, -5, // 336 + 149, -12, -99, -49, -28, 143, // 337 + 149, -99, -49, -28, 143, // 338 + 149, -12, -99, -28, 143, // 339 + 149, -99, -28, 143, // 340 + -123, // 341 + 58, // 342 + 146, // 343 + -123, 146, // 344 + -123, // 345 + 58, // 346 + 160, // 347 + -28, // 348 + 147, -12, -112, -4, -29, // 349 + // 350 + 148, -4, // 351 + -93, // 352 + -87, // 353 + // 354 + 124, 124, // 355 + 194, // 356 + 124, -94, 124, // 357 + // 358 + 161, -130, -95, -4, 143, // 359 + -27, -35, // 360 + -24, 46, -80, -43, // 361 + -24, 203, -80, -43, // 362 + -77, -38, // 363 + -11, 46, -80, -43, // 364 + -11, 203, -80, -38, // 365 + -11, 203, -81, // 366 + 164, -38, // 367 + 164, // 368 + // 369 + 123, -131, -95, -4, 125, // 370 + // 371 + 159, -132, -95, -4, 143, // 372 + -50, // 373 + -49, -50, // 374 + 150, -37, -112, -4, // 375 + -19, // 376 + -19, 44, 212, -18, // 377 + 212, -18, // 378 + // 379 + -46, // 380 + -48, // 381 + -46, -48, // 382 + 141, -96, -112, -4, // 383 + 141, -18, -96, -112, -4, // 384 + 141, -44, -96, -112, -4, // 385 + // 386 + 205, -93, // 387 + // 388 + 142, -4, // 389 + -65, // 390 + -66, -65, // 391 + 229, -64, 207, // 392 + 231, -64, 207, // 393 + 234, -64, 207, // 394 + 230, -64, 226, // 395 + 232, 135, 207, // 396 + 232, -69, 207, // 397 + // 398 + -69, -68, 135, // 399 + -63, // 400 + -68, -63, // 401 + 233, 135, 207, // 402 + 233, -70, 207, // 403 + // 404 + -70, 224, 135, // 405 + // 406 + -64, -63, // 407 + 224, // 408 + 227, -62, // 409 + 228, -4, 125, // 410 + 216, // 411 + -76, // 412 + 217, // 413 + 219, // 414 + 234, -79, // 415 + -84, // 416 + 217, // 417 + 216, // 418 + 219, // 419 + -76, // 420 + 221, // 421 + 222, // 422 + 223, // 423 + 236, 221, // 424 + 236, 222, // 425 + 236, 223, // 426 + 214, // 427 + 217, // 428 + 216, // 429 + 218, // 430 + 219, // 431 + 166, // 432 + 165, // 433 + 167, // 434 + 168, // 435 + 182, // 436 + 181, // 437 + 183, // 438 + -78, // 439 + -78, // 440 + 225, // 441 + -123, // 442 + // 443 + 60, -133, -12, -123, // 444 + 128, -123, // 445 + 40, -54, -107, 41, // 446 + -54, -123, // 447 + -55, 44, -60, 44, -57, -59, // 448 + -55, 44, -60, -59, // 449 + -55, 44, -57, -59, // 450 + -55, -59, // 451 + -60, 44, -57, -59, // 452 + -60, -59, // 453 + -57, -59, // 454 + -58, // 455 + // 456 + 218, // 457 + 217, // 458 + 216, // 459 + 219, // 460 + 214, // 461 + -56, // 462 + -55, 44, -56, // 463 + -56, 61, -18, // 464 + -61, // 465 + -60, 44, -61, // 466 + 42, // 467 + 212, // 468 + -134, -56, // 469 + -134, // 470 + 38, // 471 + 213, // 472 + -135, -56, // 473 + // 474 + 44, -58, // 475 + -15, // 476 + // 477 + 40, -136, -12, -107, 41, // 478 + -52, // 479 + -51, 44, -52, // 480 + -18, 205, -18, // 481 + 214, // 482 + 218, // 483 + 215, // 484 + 214, // 485 + 218, // 486 + 215, // 487 + -83, // 488 + 214, // 489 + 215, // 490 + -83, // 491 + 46, // 492 + 203, // 493 + // 494 + -100, // 495 + // 496 + 10, // 497 + // 498 + 10, // 499 + 44, // 500 + 59, // 501 + 10, // 502 + -123, // 503 + -100, 59, // 504 + }; + } +#endif + + protected override void DoAction(int action) + { + switch (action) + { + case 1: _1(); return; + case 2: _2(); return; + case 3: _3(); return; + case 4: _4(); return; + case 5: _5(); return; + case 6: _6(); return; + case 7: _7(); return; + case 9: _9(); return; + case 10: _10(); return; + case 11: _11(); return; + case 12: _12(); return; + case 13: _13(); return; + case 14: _14(); return; + case 15: _15(); return; + case 16: _16(); return; + case 17: _17(); return; + case 18: _18(); return; + case 19: _19(); return; + case 20: _20(); return; + case 21: _21(); return; + case 22: _22(); return; + case 23: _23(); return; + case 24: _24(); return; + case 25: _25(); return; + case 26: _26(); return; + case 27: _27(); return; + case 28: _28(); return; + case 29: _29(); return; + case 30: _30(); return; + case 31: _31(); return; + case 32: _32(); return; + case 33: _33(); return; + case 34: _34(); return; + case 35: _35(); return; + case 36: _36(); return; + case 37: _37(); return; + case 38: _38(); return; + case 39: _39(); return; + case 40: _40(); return; + case 41: _41(); return; + case 42: _42(); return; + case 43: _43(); return; + case 44: _44(); return; + case 45: _45(); return; + case 46: _46(); return; + case 47: _47(); return; + case 48: _48(); return; + case 49: _49(); return; + case 50: _50(); return; + case 51: _51(); return; + case 52: _52(); return; + case 53: _53(); return; + case 55: _55(); return; + case 56: _56(); return; + case 57: _57(); return; + case 58: _58(); return; + case 59: _59(); return; + case 60: _60(); return; + case 62: _62(); return; + case 63: _63(); return; + case 64: _64(); return; + case 65: _65(); return; + case 66: _66(); return; + case 67: _67(); return; + case 68: _68(); return; + case 69: _69(); return; + case 70: _70(); return; + case 71: _71(); return; + case 72: _72(); return; + case 73: _73(); return; + case 74: _74(); return; + case 75: _75(); return; + case 76: _76(); return; + case 77: _77(); return; + case 78: _78(); return; + case 79: _79(); return; + case 80: _80(); return; + case 81: _81(); return; + case 82: _82(); return; + case 83: _83(); return; + case 84: _84(); return; + case 85: _85(); return; + case 86: _86(); return; + case 87: _87(); return; + case 88: _88(); return; + case 89: _89(); return; + case 90: _90(); return; + case 91: _91(); return; + case 92: _92(); return; + case 93: _93(); return; + case 94: _94(); return; + case 95: _95(); return; + case 96: _96(); return; + case 97: _97(); return; + case 98: _98(); return; + case 99: _99(); return; + case 100: _100(); return; + case 101: _101(); return; + case 102: _102(); return; + case 103: _103(); return; + case 104: _104(); return; + case 105: _105(); return; + case 106: _106(); return; + case 107: _107(); return; + case 108: _108(); return; + case 109: _109(); return; + case 110: _110(); return; + case 111: _111(); return; + case 112: _112(); return; + case 113: _113(); return; + case 114: _114(); return; + case 115: _115(); return; + case 116: _116(); return; + case 117: _117(); return; + case 118: _118(); return; + case 119: _119(); return; + case 120: _120(); return; + case 121: _121(); return; + case 122: _122(); return; + case 123: _123(); return; + case 124: _124(); return; + case 125: _125(); return; + case 126: _126(); return; + case 127: _127(); return; + case 128: _128(); return; + case 129: _129(); return; + case 130: _130(); return; + case 131: _131(); return; + case 132: _132(); return; + case 133: _133(); return; + case 134: _134(); return; + case 135: _135(); return; + case 136: _136(); return; + case 137: _137(); return; + case 138: _138(); return; + case 139: _139(); return; + case 140: _140(); return; + case 141: _141(); return; + case 142: _142(); return; + case 143: _143(); return; + case 144: _144(); return; + case 187: _187(); return; + case 188: _188(); return; + case 189: _189(); return; + case 190: _190(); return; + case 191: _191(); return; + case 192: _192(); return; + case 193: _193(); return; + case 194: _194(); return; + case 195: _195(); return; + case 196: _196(); return; + case 197: _197(); return; + case 198: _198(); return; + case 199: _199(); return; + case 200: _200(); return; + case 201: _201(); return; + case 202: _202(); return; + case 203: _203(); return; + case 204: _204(); return; + case 205: _205(); return; + case 206: _206(); return; + case 207: _207(); return; + case 208: _208(); return; + case 209: _209(); return; + case 210: _210(); return; + case 211: _211(); return; + case 212: _212(); return; + case 213: _213(); return; + case 214: _214(); return; + case 215: _215(); return; + case 216: _216(); return; + case 217: _217(); return; + case 218: _218(); return; + case 219: _219(); return; + case 220: _220(); return; + case 221: _221(); return; + case 222: _222(); return; + case 223: _223(); return; + case 224: _224(); return; + case 225: _225(); return; + case 226: _226(); return; + case 227: _227(); return; + case 228: _228(); return; + case 229: _229(); return; + case 230: _230(); return; + case 231: _231(); return; + case 232: _232(); return; + case 233: _233(); return; + case 234: _234(); return; + case 235: _235(); return; + case 236: _236(); return; + case 237: _237(); return; + case 238: _238(); return; + case 239: _239(); return; + case 240: _240(); return; + case 241: _241(); return; + case 242: _242(); return; + case 243: _243(); return; + case 244: _244(); return; + case 245: _245(); return; + case 246: _246(); return; + case 247: _247(); return; + case 248: _248(); return; + case 249: _249(); return; + case 250: _250(); return; + case 251: _251(); return; + case 252: _252(); return; + case 253: _253(); return; + case 254: _254(); return; + case 255: _255(); return; + case 256: _256(); return; + case 257: _257(); return; + case 258: _258(); return; + case 259: _259(); return; + case 260: _260(); return; + case 261: _261(); return; + case 262: _262(); return; + case 263: _263(); return; + case 264: _264(); return; + case 265: _265(); return; + case 266: _266(); return; + case 267: _267(); return; + case 268: _268(); return; + case 269: _269(); return; + case 270: _270(); return; + case 271: _271(); return; + case 272: _272(); return; + case 273: _273(); return; + case 274: _274(); return; + case 275: _275(); return; + case 276: _276(); return; + case 277: _277(); return; + case 278: _278(); return; + case 279: _279(); return; + case 281: _281(); return; + case 283: _283(); return; + case 289: _289(); return; + case 290: _290(); return; + case 291: _291(); return; + case 292: _292(); return; + case 293: _293(); return; + case 294: _294(); return; + case 295: _295(); return; + case 296: _296(); return; + case 297: _297(); return; + case 298: _298(); return; + case 299: _299(); return; + case 300: _300(); return; + case 301: _301(); return; + case 302: _302(); return; + case 304: _304(); return; + case 305: _305(); return; + case 306: _306(); return; + case 307: _307(); return; + case 308: _308(); return; + case 309: _309(); return; + case 310: _310(); return; + case 311: _311(); return; + case 312: _312(); return; + case 314: _314(); return; + case 315: _315(); return; + case 316: _316(); return; + case 317: _317(); return; + case 318: _318(); return; + case 319: _319(); return; + case 320: _320(); return; + case 321: _321(); return; + case 322: _322(); return; + case 323: _323(); return; + case 324: _324(); return; + case 325: _325(); return; + case 326: _326(); return; + case 327: _327(); return; + case 328: _328(); return; + case 329: _329(); return; + case 330: _330(); return; + case 331: _331(); return; + case 332: _332(); return; + case 333: _333(); return; + case 334: _334(); return; + case 335: _335(); return; + case 336: _336(); return; + case 337: _337(); return; + case 338: _338(); return; + case 339: _339(); return; + case 347: _347(); return; + case 348: _348(); return; + case 349: _349(); return; + case 350: _350(); return; + case 351: _351(); return; + case 352: _352(); return; + case 353: _353(); return; + case 354: _354(); return; + case 355: _355(); return; + case 356: _356(); return; + case 357: _357(); return; + case 358: _358(); return; + case 359: _359(); return; + case 360: _360(); return; + case 361: _361(); return; + case 362: _362(); return; + case 363: _363(); return; + case 364: _364(); return; + case 365: _365(); return; + case 366: _366(); return; + case 367: _367(); return; + case 368: _368(); return; + case 369: _369(); return; + case 370: _370(); return; + case 371: _371(); return; + case 372: _372(); return; + case 373: _373(); return; + case 374: _374(); return; + case 375: _375(); return; + case 376: _376(); return; + case 377: _377(); return; + case 380: _380(); return; + case 381: _381(); return; + case 382: _382(); return; + case 383: _383(); return; + case 384: _384(); return; + case 386: _386(); return; + case 388: _388(); return; + case 389: _389(); return; + case 390: _390(); return; + case 391: _391(); return; + case 392: _392(); return; + case 393: _393(); return; + case 394: _394(); return; + case 395: _395(); return; + case 396: _396(); return; + case 397: _397(); return; + case 398: _398(); return; + case 399: _399(); return; + case 400: _400(); return; + case 401: _401(); return; + case 402: _402(); return; + case 403: _403(); return; + case 404: _404(); return; + case 405: _405(); return; + case 406: _406(); return; + case 407: _407(); return; + case 408: _408(); return; + case 409: _409(); return; + case 410: _410(); return; + case 411: _411(); return; + case 412: _412(); return; + case 413: _413(); return; + case 414: _414(); return; + case 417: _417(); return; + case 419: _419(); return; + case 420: _420(); return; + case 421: _421(); return; + case 422: _422(); return; + case 423: _423(); return; + case 424: _424(); return; + case 425: _425(); return; + case 426: _426(); return; + case 427: _427(); return; + case 428: _428(); return; + case 429: _429(); return; + case 430: _430(); return; + case 431: _431(); return; + case 432: _432(); return; + case 433: _433(); return; + case 434: _434(); return; + case 435: _435(); return; + case 436: _436(); return; + case 437: _437(); return; + case 438: _438(); return; + case 439: _439(); return; + case 440: _440(); return; + case 441: _441(); return; + case 442: _442(); return; + case 443: _443(); return; + case 444: _444(); return; + case 445: _445(); return; + case 446: _446(); return; + case 447: _447(); return; + case 448: _448(); return; + case 449: _449(); return; + case 450: _450(); return; + case 451: _451(); return; + case 452: _452(); return; + case 453: _453(); return; + case 454: _454(); return; + case 455: _455(); return; + case 456: _456(); return; + case 457: _457(); return; + case 458: _458(); return; + case 459: _459(); return; + case 460: _460(); return; + case 461: _461(); return; + case 462: _462(); return; + case 463: _463(); return; + case 464: _464(); return; + case 465: _465(); return; + case 468: _468(); return; + case 469: _469(); return; + case 472: _472(); return; + case 474: _474(); return; + case 476: _476(); return; + case 477: _477(); return; + case 478: _478(); return; + case 479: _479(); return; + case 480: _480(); return; + case 500: _500(); return; + case 503: _503(); return; + } + } + + private void _1() + { + // @1 -> +#line 160 "Parser.y" + { + _tokenizer.SetState(LexicalState.EXPR_BEG); + } + } + + private void _2() + { + // program -> @1 compstmt +#line 164 "Parser.y" + { + _ast = new SourceUnitTree(CurrentScope, GetValue(1).Expressions, _initializers, _encoding, _tokenizer.DataOffset); + } + } + + private void _3() + { + // compstmt -> stmts opt_terms +#line 170 "Parser.y" + { + yyval.Expressions = GetValue(2).Expressions; + } + } + + private void _4() + { + // stmts -> +#line 176 "Parser.y" + { + yyval.Expressions = new List(); + } + } + + private void _5() + { + // stmts -> stmt +#line 180 "Parser.y" + { + yyval.Expressions = CollectionUtils.MakeList(GetValue(1).Expression); + } + } + + private void _6() + { + // stmts -> stmts terms stmt +#line 184 "Parser.y" + { + GetValue(3).Expressions.Add(GetValue(1).Expression); + yyval.Expressions = GetValue(3).Expressions; + } + } + + private void _7() + { + // stmts -> Error stmt +#line 189 "Parser.y" + { + yyval.Expressions = CollectionUtils.MakeList(GetValue(1).Expression); + } + } + + private void _9() + { + // stmt -> Undef undef_list +#line 196 "Parser.y" + { + yyval.Expression = new UndefineStatement(GetValue(1).Identifiers, yyloc); + } + } + + private void _10() + { + // @2 -> +#line 200 "Parser.y" + { + if (InMethod) { + _tokenizer.ReportError(Errors.FileInitializerInMethod); + } + + EnterTopScope(); + } + } + + private void _11() + { + // stmt -> UppercaseBegin @2 '{' compstmt '}' +#line 208 "Parser.y" + { + yyval.Expression = AddInitializer(new Initializer(CurrentScope, GetValue(2).Expressions, yyloc)); + LeaveScope(); + } + } + + private void _12() + { + // @3 -> +#line 213 "Parser.y" + { + if (InMethod) { + _tokenizer.ReportError(Errors.FileFinalizerInMethod); + } + + EnterTopScope(); + } + } + + private void _13() + { + // stmt -> UppercaseEnd @3 '{' compstmt '}' +#line 221 "Parser.y" + { + yyval.Expression = new Finalizer(CurrentScope, GetValue(2).Expressions, yyloc); + LeaveScope(); + } + } + + private void _14() + { + // stmt -> match_reference Assignment command_call +#line 226 "Parser.y" + { + MatchReferenceReadOnlyError(GetValue(3).RegexMatchReference); + yyval.Expression = new ErrorExpression(yyloc); + } + } + + private void _15() + { + // stmt -> jump_statement +#line 231 "Parser.y" + { + yyval.Expression = GetValue(1).JumpStatement; + } + } + + private void _16() + { + // stmt -> conditional_statement +#line 235 "Parser.y" + { + yyval.Expression = GetValue(1).Expression; + } + } + + private void _17() + { + // stmt -> expression_statement +#line 239 "Parser.y" + { + yyval.Expression = GetValue(1).Expression; + } + } + + private void _18() + { + // @4 -> +#line 246 "Parser.y" + { + _tokenizer.SetState(LexicalState.EXPR_FNAME); + } + } + + private void _19() + { + // alias_statement -> Alias method_name_or_symbol @4 method_name_or_symbol +#line 250 "Parser.y" + { + yyval.Expression = new AliasStatement(true, GetValue(3).String, GetValue(1).String, yyloc); + } + } + + private void _20() + { + // alias_statement -> Alias GlobalVariable GlobalVariable +#line 254 "Parser.y" + { + yyval.Expression = MakeGlobalAlias(GetValue(2).String, GetValue(1).String, yyloc); + } + } + + private void _21() + { + // alias_statement -> Alias GlobalVariable match_reference +#line 258 "Parser.y" + { + yyval.Expression = MakeGlobalAlias(GetValue(2).String, GetValue(1).RegexMatchReference, yyloc); + } + } + + private void _22() + { + // jump_statement -> jump_statement_with_parameters +#line 265 "Parser.y" + { + yyval.JumpStatement = GetValue(1).JumpStatement; + } + } + + private void _23() + { + // jump_statement -> jump_statement_parameterless +#line 269 "Parser.y" + { + yyval.JumpStatement = GetValue(1).JumpStatement; + } + } + + private void _24() + { + // jump_statement_with_parameters -> Return open_args +#line 276 "Parser.y" + { + yyval.JumpStatement = new ReturnStatement(RequireNoBlockArg(GetValue(1)), yyloc); + } + } + + private void _25() + { + // jump_statement_with_parameters -> Break open_args +#line 280 "Parser.y" + { + yyval.JumpStatement = new BreakStatement(RequireNoBlockArg(GetValue(1)), yyloc); + } + } + + private void _26() + { + // jump_statement_with_parameters -> Next open_args +#line 284 "Parser.y" + { + yyval.JumpStatement = new NextStatement(RequireNoBlockArg(GetValue(1)), yyloc); + } + } + + private void _27() + { + // jump_statement_parameterless -> Return +#line 291 "Parser.y" + { + yyval.JumpStatement = new ReturnStatement(null, yyloc); + } + } + + private void _28() + { + // jump_statement_parameterless -> Break +#line 295 "Parser.y" + { + yyval.JumpStatement = new BreakStatement(null, yyloc); + } + } + + private void _29() + { + // jump_statement_parameterless -> Next +#line 299 "Parser.y" + { + yyval.JumpStatement = new NextStatement(null, yyloc); + } + } + + private void _30() + { + // jump_statement_parameterless -> Redo +#line 303 "Parser.y" + { + yyval.JumpStatement = new RedoStatement(yyloc); + } + } + + private void _31() + { + // jump_statement_parameterless -> Retry +#line 307 "Parser.y" + { + yyval.JumpStatement = new RetryStatement(yyloc); + } + } + + private void _32() + { + // expression_statement -> expr +#line 314 "Parser.y" + { + yyval.Expression = GetValue(1).Expression; + } + } + + private void _33() + { + // expression_statement -> lhs '=' command_call +#line 318 "Parser.y" + { + yyval.Expression = new SimpleAssignmentExpression(GetValue(3).LeftValue, GetValue(1).CallExpression, null, yyloc); + } + } + + private void _34() + { + // expression_statement -> compound_lhs '=' command_call +#line 322 "Parser.y" + { + yyval.Expression = new ParallelAssignmentExpression(GetValue(3).CompoundLeftValue, new CompoundRightValue(CollectionUtils.MakeList(GetValue(1).CallExpression), null), yyloc); + } + } + + private void _35() + { + // expression_statement -> var_lhs Assignment command_call +#line 326 "Parser.y" + { + yyval.Expression = new SimpleAssignmentExpression(GetValue(3).LeftValue, GetValue(1).CallExpression, GetValue(2).String, yyloc); + } + } + + private void _36() + { + // expression_statement -> primary '[' array_key ']' Assignment command_call +#line 330 "Parser.y" + { + yyval.Expression = new SimpleAssignmentExpression(new ArrayItemAccess(GetValue(6).Expression, GetValue(4).Arguments, GetLocation(5)), GetValue(1).CallExpression, GetValue(2).String, yyloc); + } + } + + private void _37() + { + // expression_statement -> primary '.' Identifier Assignment command_call +#line 334 "Parser.y" + { + yyval.Expression = new MemberAssignmentExpression(GetValue(5).Expression, GetValue(3).String, GetValue(2).String, GetValue(1).CallExpression, yyloc); + } + } + + private void _38() + { + // expression_statement -> primary '.' ConstantIdentifier Assignment command_call +#line 338 "Parser.y" + { + yyval.Expression = new MemberAssignmentExpression(GetValue(5).Expression, GetValue(3).String, GetValue(2).String, GetValue(1).CallExpression, yyloc); + } + } + + private void _39() + { + // expression_statement -> primary SeparatingDoubleColon Identifier Assignment command_call +#line 342 "Parser.y" + { + yyval.Expression = new MemberAssignmentExpression(GetValue(5).Expression, GetValue(3).String, GetValue(2).String, GetValue(1).CallExpression, yyloc); + } + } + + private void _40() + { + // expression_statement -> lhs '=' compound_rhs +#line 346 "Parser.y" + { + yyval.Expression = new ParallelAssignmentExpression(new CompoundLeftValue(CollectionUtils.MakeList(GetValue(3).LeftValue), null, GetLocation(3)), GetValue(1).CompoundRightValue, yyloc); + } + } + + private void _41() + { + // expression_statement -> compound_lhs '=' arg +#line 350 "Parser.y" + { + yyval.Expression = new ParallelAssignmentExpression(GetValue(3).CompoundLeftValue, new CompoundRightValue(CollectionUtils.MakeList(GetValue(1).Expression), null), yyloc); + } + } + + private void _42() + { + // expression_statement -> compound_lhs '=' compound_rhs +#line 354 "Parser.y" + { + yyval.Expression = new ParallelAssignmentExpression(GetValue(3).CompoundLeftValue, GetValue(1).CompoundRightValue, yyloc); + } + } + + private void _43() + { + // expression_statement -> arg '?' jump_statement_parameterless ':' arg +#line 358 "Parser.y" + { + yyval.Expression = new ConditionalJumpExpression(GetValue(5).Expression.ToCondition(), GetValue(3).JumpStatement, false, GetValue(1).Expression, yyloc); + } + } + + private void _44() + { + // expression_statement -> arg '?' arg ':' jump_statement_parameterless +#line 362 "Parser.y" + { + yyval.Expression = new ConditionalJumpExpression(GetValue(5).Expression.ToCondition(), GetValue(1).JumpStatement, true, GetValue(3).Expression, yyloc); + } + } + + private void _45() + { + // conditional_statement -> stmt IfMod expr +#line 369 "Parser.y" + { + yyval.Expression = new ConditionalStatement(GetValue(1).Expression.ToCondition(), false, GetValue(3).Expression, null, yyloc); + } + } + + private void _46() + { + // conditional_statement -> stmt UnlessMod expr +#line 373 "Parser.y" + { + yyval.Expression = new ConditionalStatement(GetValue(1).Expression.ToCondition(), true, GetValue(3).Expression, null, yyloc); + } + } + + private void _47() + { + // conditional_statement -> stmt WhileMod expr +#line 377 "Parser.y" + { + yyval.Expression = MakeLoopStatement(GetValue(3).Expression, GetValue(1).Expression.ToCondition(), true, yyloc); + } + } + + private void _48() + { + // conditional_statement -> stmt UntilMod expr +#line 381 "Parser.y" + { + yyval.Expression = MakeLoopStatement(GetValue(3).Expression, GetValue(1).Expression.ToCondition(), false, yyloc); + } + } + + private void _49() + { + // conditional_statement -> stmt RescueMod stmt +#line 385 "Parser.y" + { + yyval.Expression = new RescueExpression(GetValue(3).Expression, GetValue(1).Expression, MergeLocations(GetLocation(2), GetLocation(1)), yyloc); + } + } + + private void _50() + { + // conditional_statement -> arg '?' jump_statement_parameterless ':' jump_statement_parameterless +#line 389 "Parser.y" + { + yyval.Expression = new ConditionalStatement(GetValue(5).Expression.ToCondition(), false, GetValue(3).JumpStatement, GetValue(1).JumpStatement, yyloc); + } + } + + private void _51() + { + // compound_rhs -> args ',' arg +#line 396 "Parser.y" + { + GetValue(3).Expressions.Add(GetValue(1).Expression); + yyval.CompoundRightValue = new CompoundRightValue(GetValue(3).Expressions, null); + } + } + + private void _52() + { + // compound_rhs -> args ',' Star arg +#line 401 "Parser.y" + { + yyval.CompoundRightValue = new CompoundRightValue(GetValue(4).Expressions, GetValue(1).Expression); + } + } + + private void _53() + { + // compound_rhs -> Star arg +#line 405 "Parser.y" + { + yyval.CompoundRightValue = new CompoundRightValue(Expression.EmptyList, GetValue(1).Expression); + } + } + + private void _55() + { + // expr -> expr And expr +#line 413 "Parser.y" + { + yyval.Expression = new AndExpression(GetValue(3).Expression, GetValue(1).Expression, yyloc); + } + } + + private void _56() + { + // expr -> expr Or expr +#line 417 "Parser.y" + { + yyval.Expression = new OrExpression(GetValue(3).Expression, GetValue(1).Expression, yyloc); + } + } + + private void _57() + { + // expr -> expr And jump_statement +#line 421 "Parser.y" + { + yyval.Expression = new ConditionalJumpExpression(GetValue(3).Expression, GetValue(1).JumpStatement, false, null, yyloc); + } + } + + private void _58() + { + // expr -> expr Or jump_statement +#line 425 "Parser.y" + { + yyval.Expression = new ConditionalJumpExpression(GetValue(3).Expression, GetValue(1).JumpStatement, true, null, yyloc); + } + } + + private void _59() + { + // expr -> Not expr +#line 429 "Parser.y" + { + // TODO: warning: string literal in condition + yyval.Expression = new NotExpression(GetValue(1).Expression, yyloc); + } + } + + private void _60() + { + // expr -> '!' command_call +#line 434 "Parser.y" + { + // TODO: warning: string literal in condition + yyval.Expression = new NotExpression(GetValue(1).CallExpression, yyloc); + } + } + + private void _62() + { + // command_call -> command +#line 443 "Parser.y" + { + yyval.CallExpression = GetValue(1).CallExpression; + } + } + + private void _63() + { + // command_call -> block_command +#line 447 "Parser.y" + { + yyval.CallExpression = GetValue(1).CallExpression; + } + } + + private void _64() + { + // block_command -> block_call +#line 454 "Parser.y" + { + yyval.CallExpression = GetValue(1).CallExpression; + } + } + + private void _65() + { + // block_command -> block_call '.' operation2 command_args +#line 458 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(GetValue(4).CallExpression, GetValue(2).String, GetValue(1), yyloc); + } + } + + private void _66() + { + // block_command -> block_call SeparatingDoubleColon operation2 command_args +#line 462 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(GetValue(4).CallExpression, GetValue(2).String, GetValue(1), yyloc); + } + } + + private void _67() + { + // @5 -> +#line 469 "Parser.y" + { + EnterNestedScope(); + } + } + + private void _68() + { + // cmd_brace_block -> LbraceArg @5 block_parameters_opt compstmt '}' +#line 473 "Parser.y" + { + yyval.BlockDefinition = new BlockDefinition(CurrentScope, GetValue(3).CompoundLeftValue, GetValue(2).Expressions, yyloc); + LeaveScope(); + } + } + + private void _69() + { + // command -> operation command_args +#line 480 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(null, GetValue(2).String, GetValue(1), yyloc); + } + } + + private void _70() + { + // command -> operation command_args cmd_brace_block +#line 484 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(null, GetValue(3).String, GetValue(2), GetValue(1).BlockDefinition, yyloc); + } + } + + private void _71() + { + // command -> primary '.' operation2 command_args +#line 488 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(GetValue(4).Expression, GetValue(2).String, GetValue(1), yyloc); + } + } + + private void _72() + { + // command -> primary '.' operation2 command_args cmd_brace_block +#line 492 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(GetValue(5).Expression, GetValue(3).String, GetValue(2), GetValue(1).BlockDefinition, yyloc); + } + } + + private void _73() + { + // command -> primary SeparatingDoubleColon operation2 command_args +#line 496 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(GetValue(4).Expression, GetValue(2).String, GetValue(1), yyloc); + } + } + + private void _74() + { + // command -> primary SeparatingDoubleColon operation2 command_args cmd_brace_block +#line 500 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(GetValue(5).Expression, GetValue(3).String, GetValue(2), GetValue(1).BlockDefinition, yyloc); + } + } + + private void _75() + { + // command -> Super command_args +#line 504 "Parser.y" + { + yyval.CallExpression = MakeSuperCall(GetValue(1), yyloc); + } + } + + private void _76() + { + // command -> Yield command_args +#line 508 "Parser.y" + { + yyval.CallExpression = new YieldCall(RequireNoBlockArg(GetValue(1)), yyloc); + } + } + + private void _77() + { + // compound_lhs -> compound_lhs_head compound_lhs_item +#line 515 "Parser.y" + { + GetValue(2).LeftValues.Add(GetValue(1).LeftValue); + yyval.CompoundLeftValue = new CompoundLeftValue(GetValue(2).LeftValues, null, yyloc); + } + } + + private void _78() + { + // compound_lhs -> compound_lhs_head +#line 520 "Parser.y" + { + GetValue(1).LeftValues.Add(Placeholder.Singleton); + yyval.CompoundLeftValue = new CompoundLeftValue(GetValue(1).LeftValues, null, yyloc); + } + } + + private void _79() + { + // compound_lhs -> LeftParen compound_lhs ')' +#line 525 "Parser.y" + { + yyval.CompoundLeftValue = new CompoundLeftValue(CollectionUtils.MakeList(GetValue(2).CompoundLeftValue), null, yyloc); + } + } + + private void _80() + { + // compound_lhs -> compound_lhs_head compound_lhs_tail +#line 529 "Parser.y" + { + yyval.CompoundLeftValue = new CompoundLeftValue(GetValue(2).LeftValues, GetValue(1).LeftValue, yyloc); + } + } + + private void _81() + { + // compound_lhs -> compound_lhs_tail +#line 533 "Parser.y" + { + yyval.CompoundLeftValue = new CompoundLeftValue(LeftValue.EmptyList, GetValue(1).LeftValue, yyloc); + } + } + + private void _82() + { + // compound_lhs_tail -> Star compound_lhs_node +#line 540 "Parser.y" + { + yyval.LeftValue = GetValue(1).LeftValue; + } + } + + private void _83() + { + // compound_lhs_tail -> Star +#line 544 "Parser.y" + { + yyval.LeftValue = Placeholder.Singleton; + } + } + + private void _84() + { + // compound_lhs_head -> compound_lhs_head compound_lhs_item ',' +#line 551 "Parser.y" + { + GetValue(3).LeftValues.Add(GetValue(2).LeftValue); + yyval.LeftValues = GetValue(3).LeftValues; + } + } + + private void _85() + { + // compound_lhs_head -> compound_lhs_item ',' +#line 556 "Parser.y" + { + yyval.LeftValues = CollectionUtils.MakeList(GetValue(2).LeftValue); + } + } + + private void _86() + { + // compound_lhs_item -> compound_lhs_node +#line 563 "Parser.y" + { + yyval.LeftValue = GetValue(1).LeftValue; + } + } + + private void _87() + { + // compound_lhs_item -> LeftParen compound_lhs ')' +#line 567 "Parser.y" + { + yyval.LeftValue = GetValue(2).CompoundLeftValue; + } + } + + private void _88() + { + // compound_lhs_node -> variable +#line 574 "Parser.y" + { + yyval.LeftValue = VariableFactory.MakeLeftValue(GetValue(1).VariableFactory, this, GetValue(1).String, yyloc); + } + } + + private void _89() + { + // compound_lhs_node -> primary '[' array_key ']' +#line 578 "Parser.y" + { + yyval.LeftValue = new ArrayItemAccess(GetValue(4).Expression, GetValue(2).Arguments, yyloc); + } + } + + private void _90() + { + // compound_lhs_node -> primary '.' Identifier +#line 582 "Parser.y" + { + yyval.LeftValue = new AttributeAccess(GetValue(3).Expression, GetValue(1).String, yyloc); + } + } + + private void _91() + { + // compound_lhs_node -> primary SeparatingDoubleColon Identifier +#line 586 "Parser.y" + { + yyval.LeftValue = new AttributeAccess(GetValue(3).Expression, GetValue(1).String, yyloc); + } + } + + private void _92() + { + // compound_lhs_node -> primary '.' ConstantIdentifier +#line 590 "Parser.y" + { + yyval.LeftValue = new AttributeAccess(GetValue(3).Expression, GetValue(1).String, yyloc); + } + } + + private void _93() + { + // compound_lhs_node -> primary SeparatingDoubleColon ConstantIdentifier +#line 594 "Parser.y" + { + yyval.LeftValue = new ConstantVariable(GetValue(3).Expression, GetValue(1).String, yyloc); + } + } + + private void _94() + { + // compound_lhs_node -> LeadingDoubleColon ConstantIdentifier +#line 598 "Parser.y" + { + yyval.LeftValue = new ConstantVariable(null, GetValue(1).String, yyloc); + } + } + + private void _95() + { + // compound_lhs_node -> match_reference +#line 602 "Parser.y" + { + MatchReferenceReadOnlyError(GetValue(1).RegexMatchReference); + yyval.LeftValue = new GlobalVariable(Symbols.Error, yyloc); + } + } + + private void _96() + { + // lhs -> variable +#line 610 "Parser.y" + { + yyval.LeftValue = VariableFactory.MakeLeftValue(GetValue(1).VariableFactory, this, GetValue(1).String, yyloc); + } + } + + private void _97() + { + // lhs -> primary '[' array_key ']' +#line 614 "Parser.y" + { + yyval.LeftValue = new ArrayItemAccess(GetValue(4).Expression, GetValue(2).Arguments, yyloc); + } + } + + private void _98() + { + // lhs -> primary '.' Identifier +#line 618 "Parser.y" + { + yyval.LeftValue = new AttributeAccess(GetValue(3).Expression, GetValue(1).String, yyloc); + } + } + + private void _99() + { + // lhs -> primary SeparatingDoubleColon Identifier +#line 622 "Parser.y" + { + yyval.LeftValue = new AttributeAccess(GetValue(3).Expression, GetValue(1).String, yyloc); + } + } + + private void _100() + { + // lhs -> primary '.' ConstantIdentifier +#line 626 "Parser.y" + { + yyval.LeftValue = new AttributeAccess(GetValue(3).Expression, GetValue(1).String, yyloc); + } + } + + private void _101() + { + // lhs -> primary SeparatingDoubleColon ConstantIdentifier +#line 630 "Parser.y" + { + yyval.LeftValue = new ConstantVariable(GetValue(3).Expression, GetValue(1).String, yyloc); + } + } + + private void _102() + { + // lhs -> LeadingDoubleColon ConstantIdentifier +#line 634 "Parser.y" + { + yyval.LeftValue = new ConstantVariable(null, GetValue(1).String, yyloc); + } + } + + private void _103() + { + // lhs -> match_reference +#line 638 "Parser.y" + { + MatchReferenceReadOnlyError(GetValue(1).RegexMatchReference); + yyval.LeftValue = new GlobalVariable(Symbols.Error, yyloc); + } + } + + private void _104() + { + // module_name -> ConstantIdentifier +#line 646 "Parser.y" + { + yyval.String = GetValue(1).String; + } + } + + private void _105() + { + // module_name -> Identifier +#line 650 "Parser.y" + { + _tokenizer.ReportError(Errors.ModuleNameNotConstant); + yyval.String = GetValue(1).String; + } + } + + private void _106() + { + // qualified_module_name -> LeadingDoubleColon module_name +#line 658 "Parser.y" + { + yyval.ConstantVariable = new ConstantVariable(null, GetValue(1).String, yyloc); + } + } + + private void _107() + { + // qualified_module_name -> module_name +#line 662 "Parser.y" + { + yyval.ConstantVariable = new ConstantVariable(GetValue(1).String, yyloc); + } + } + + private void _108() + { + // qualified_module_name -> primary SeparatingDoubleColon module_name +#line 666 "Parser.y" + { + yyval.ConstantVariable = new ConstantVariable(GetValue(3).Expression, GetValue(1).String, yyloc); + } + } + + private void _109() + { + // method_name -> Identifier +#line 673 "Parser.y" + { + yyval.String = GetValue(1).String; + } + } + + private void _110() + { + // method_name -> ConstantIdentifier +#line 677 "Parser.y" + { + yyval.String = GetValue(1).String; + } + } + + private void _111() + { + // method_name -> FunctionIdentifier +#line 681 "Parser.y" + { + yyval.String = GetValue(1).String; + } + } + + private void _112() + { + // method_name -> op +#line 685 "Parser.y" + { + _tokenizer.SetState(LexicalState.EXPR_END); + yyval.String = GetValue(1).String; + } + } + + private void _113() + { + // method_name -> reswords +#line 690 "Parser.y" + { + _tokenizer.SetState(LexicalState.EXPR_END); + yyval.String = GetValue(1).String; + } + } + + private void _114() + { + // method_name_or_symbol -> method_name +#line 698 "Parser.y" + { + yyval.String = GetValue(1).String; + } + } + + private void _115() + { + // method_name_or_symbol -> symbol +#line 702 "Parser.y" + { + yyval.String = GetValue(1).String; + } + } + + private void _116() + { + // undef_list -> method_name_or_symbol +#line 709 "Parser.y" + { + yyval.Identifiers = CollectionUtils.MakeList(new Identifier(GetValue(1).String, GetLocation(1))); + } + } + + private void _117() + { + // @6 -> +#line 713 "Parser.y" + { + _tokenizer.SetState(LexicalState.EXPR_FNAME); + } + } + + private void _118() + { + // undef_list -> undef_list ',' @6 method_name_or_symbol +#line 717 "Parser.y" + { + GetValue(4).Identifiers.Add(new Identifier(GetValue(1).String, GetLocation(1))); + yyval.Identifiers = GetValue(4).Identifiers; + } + } + + private void _119() + { + // op -> '|' +#line 724 "Parser.y" + { yyval.String = Symbols.BitwiseOr; } + } + + private void _120() + { + // op -> '^' +#line 725 "Parser.y" + { yyval.String = Symbols.Xor; } + } + + private void _121() + { + // op -> '&' +#line 726 "Parser.y" + { yyval.String = Symbols.BitwiseAnd; } + } + + private void _122() + { + // op -> Cmp +#line 727 "Parser.y" + { yyval.String = Symbols.Comparison; } + } + + private void _123() + { + // op -> Eq +#line 728 "Parser.y" + { yyval.String = Symbols.Equal; } + } + + private void _124() + { + // op -> Eqq +#line 729 "Parser.y" + { yyval.String = Symbols.StrictEqual; } + } + + private void _125() + { + // op -> Match +#line 730 "Parser.y" + { yyval.String = Symbols.Match; } + } + + private void _126() + { + // op -> '>' +#line 731 "Parser.y" + { yyval.String = Symbols.GreaterThan; } + } + + private void _127() + { + // op -> Geq +#line 732 "Parser.y" + { yyval.String = Symbols.GreaterEqual; } + } + + private void _128() + { + // op -> '<' +#line 733 "Parser.y" + { yyval.String = Symbols.LessThan; } + } + + private void _129() + { + // op -> Leq +#line 734 "Parser.y" + { yyval.String = Symbols.LessEqual; } + } + + private void _130() + { + // op -> Lshft +#line 735 "Parser.y" + { yyval.String = Symbols.LeftShift; } + } + + private void _131() + { + // op -> Rshft +#line 736 "Parser.y" + { yyval.String = Symbols.RightShift; } + } + + private void _132() + { + // op -> '+' +#line 737 "Parser.y" + { yyval.String = Symbols.Plus; } + } + + private void _133() + { + // op -> '-' +#line 738 "Parser.y" + { yyval.String = Symbols.Minus; } + } + + private void _134() + { + // op -> '*' +#line 739 "Parser.y" + { yyval.String = Symbols.Multiply; } + } + + private void _135() + { + // op -> Star +#line 740 "Parser.y" + { yyval.String = Symbols.Multiply; } + } + + private void _136() + { + // op -> '/' +#line 741 "Parser.y" + { yyval.String = Symbols.Divide; } + } + + private void _137() + { + // op -> '%' +#line 742 "Parser.y" + { yyval.String = Symbols.Mod; } + } + + private void _138() + { + // op -> Pow +#line 743 "Parser.y" + { yyval.String = Symbols.Power; } + } + + private void _139() + { + // op -> '~' +#line 744 "Parser.y" + { yyval.String = Symbols.BitwiseNot; } + } + + private void _140() + { + // op -> Uplus +#line 745 "Parser.y" + { yyval.String = Symbols.UnaryPlus; } + } + + private void _141() + { + // op -> Uminus +#line 746 "Parser.y" + { yyval.String = Symbols.UnaryMinus; } + } + + private void _142() + { + // op -> Aref +#line 747 "Parser.y" + { yyval.String = Symbols.ArrayItemRead; } + } + + private void _143() + { + // op -> Aset +#line 748 "Parser.y" + { yyval.String = Symbols.ArrayItemWrite; } + } + + private void _144() + { + // op -> '`' +#line 749 "Parser.y" + { yyval.String = Symbols.Backtick; } + } + + private void _187() + { + // arg -> lhs '=' arg +#line 763 "Parser.y" + { + yyval.Expression = new SimpleAssignmentExpression(GetValue(3).LeftValue, GetValue(1).Expression, null, yyloc); + } + } + + private void _188() + { + // arg -> lhs '=' arg RescueMod arg +#line 767 "Parser.y" + { + yyval.Expression = new SimpleAssignmentExpression(GetValue(5).LeftValue, new RescueExpression(GetValue(3).Expression, GetValue(1).Expression, MergeLocations(GetLocation(2), GetLocation(1)), MergeLocations(GetLocation(3), GetLocation(1))), null, yyloc); + } + } + + private void _189() + { + // arg -> lhs '=' arg RescueMod jump_statement_parameterless +#line 771 "Parser.y" + { + yyval.Expression = new SimpleAssignmentExpression(GetValue(5).LeftValue, new RescueExpression(GetValue(3).Expression, GetValue(1).JumpStatement, MergeLocations(GetLocation(2), GetLocation(1)), MergeLocations(GetLocation(3), GetLocation(1))), null, yyloc); + } + } + + private void _190() + { + // arg -> var_lhs Assignment arg +#line 775 "Parser.y" + { + yyval.Expression = new SimpleAssignmentExpression(GetValue(3).LeftValue, GetValue(1).Expression, GetValue(2).String, yyloc); + } + } + + private void _191() + { + // arg -> primary '[' array_key ']' Assignment arg +#line 779 "Parser.y" + { + yyval.Expression = new SimpleAssignmentExpression(new ArrayItemAccess(GetValue(6).Expression, GetValue(4).Arguments, GetLocation(5)), GetValue(1).Expression, GetValue(2).String, yyloc); + } + } + + private void _192() + { + // arg -> primary '.' Identifier Assignment arg +#line 783 "Parser.y" + { + yyval.Expression = new MemberAssignmentExpression(GetValue(5).Expression, GetValue(3).String, GetValue(2).String, GetValue(1).Expression, yyloc); + } + } + + private void _193() + { + // arg -> primary '.' ConstantIdentifier Assignment arg +#line 787 "Parser.y" + { + yyval.Expression = new MemberAssignmentExpression(GetValue(5).Expression, GetValue(3).String, GetValue(2).String, GetValue(1).Expression, yyloc); + } + } + + private void _194() + { + // arg -> primary SeparatingDoubleColon Identifier Assignment arg +#line 791 "Parser.y" + { + yyval.Expression = new MemberAssignmentExpression(GetValue(5).Expression, GetValue(3).String, GetValue(2).String, GetValue(1).Expression, yyloc); + } + } + + private void _195() + { + // arg -> primary SeparatingDoubleColon ConstantIdentifier Assignment arg +#line 795 "Parser.y" + { + _tokenizer.ReportError(Errors.ConstantReassigned); + yyval.Expression = new ErrorExpression(yyloc); + } + } + + private void _196() + { + // arg -> LeadingDoubleColon ConstantIdentifier Assignment arg +#line 800 "Parser.y" + { + _tokenizer.ReportError(Errors.ConstantReassigned); + yyval.Expression = new ErrorExpression(yyloc); + } + } + + private void _197() + { + // arg -> match_reference Assignment arg +#line 805 "Parser.y" + { + MatchReferenceReadOnlyError(GetValue(3).RegexMatchReference); + yyval.Expression = new ErrorExpression(yyloc); + } + } + + private void _198() + { + // arg -> arg '+' arg +#line 810 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.Plus, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _199() + { + // arg -> arg '-' arg +#line 814 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.Minus, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _200() + { + // arg -> arg '*' arg +#line 818 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.Multiply, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _201() + { + // arg -> arg '/' arg +#line 822 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.Divide, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _202() + { + // arg -> arg '%' arg +#line 826 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.Mod, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _203() + { + // arg -> arg Pow arg +#line 830 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.Power, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _204() + { + // arg -> UminusNum Integer Pow arg +#line 834 "Parser.y" + { + // ** has precedence over unary minus, hence -number**arg is equivalent to -(number**arg) + yyval.Expression = new MethodCall(new MethodCall(Literal.Integer(GetValue(3).Integer, GetLocation(3)), Symbols.Power, new Arguments(GetValue(1).Expression), GetLocation(2)), Symbols.UnaryMinus, Arguments.Empty, GetLocation(4)); + } + } + + private void _205() + { + // arg -> UminusNum BigInteger Pow arg +#line 839 "Parser.y" + { + yyval.Expression = new MethodCall(new MethodCall(Literal.BigInteger(GetValue(3).BigInteger, GetLocation(3)), Symbols.Power, new Arguments(GetValue(1).Expression), GetLocation(2)), Symbols.UnaryMinus, Arguments.Empty, GetLocation(4)); + } + } + + private void _206() + { + // arg -> UminusNum Float Pow arg +#line 843 "Parser.y" + { + yyval.Expression = new MethodCall(new MethodCall(Literal.Double(GetValue(3).Double, GetLocation(3)), Symbols.Power, new Arguments(GetValue(1).Expression), GetLocation(2)), Symbols.UnaryMinus, Arguments.Empty, GetLocation(4)); + } + } + + private void _207() + { + // arg -> Uplus arg +#line 847 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(1).Expression, Symbols.UnaryPlus, null, GetLocation(2)); + } + } + + private void _208() + { + // arg -> Uminus arg +#line 851 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(1).Expression, Symbols.UnaryMinus, null, GetLocation(2)); + } + } + + private void _209() + { + // arg -> arg '|' arg +#line 855 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.BitwiseOr, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _210() + { + // arg -> arg '^' arg +#line 859 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.Xor, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _211() + { + // arg -> arg '&' arg +#line 863 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.BitwiseAnd, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _212() + { + // arg -> arg Cmp arg +#line 867 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.Comparison, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _213() + { + // arg -> arg '>' arg +#line 871 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.GreaterThan, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _214() + { + // arg -> arg Geq arg +#line 875 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.GreaterEqual, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _215() + { + // arg -> arg '<' arg +#line 879 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.LessThan, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _216() + { + // arg -> arg Leq arg +#line 883 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.LessEqual, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _217() + { + // arg -> arg Eq arg +#line 887 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.Equal, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _218() + { + // arg -> arg Eqq arg +#line 891 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.StrictEqual, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _219() + { + // arg -> arg Neq arg +#line 895 "Parser.y" + { + yyval.Expression = new NotExpression(new MethodCall(GetValue(3).Expression, Symbols.Equal, new Arguments(GetValue(1).Expression), yyloc), GetLocation(2)); + } + } + + private void _220() + { + // arg -> arg Match arg +#line 899 "Parser.y" + { + yyval.Expression = MakeMatch(GetValue(3).Expression, GetValue(1).Expression, GetLocation(2)); + } + } + + private void _221() + { + // arg -> arg Nmatch arg +#line 903 "Parser.y" + { + yyval.Expression = new NotExpression(MakeMatch(GetValue(3).Expression, GetValue(1).Expression, GetLocation(2)), yyloc); + } + } + + private void _222() + { + // arg -> '!' arg +#line 907 "Parser.y" + { + // TODO: warning: string literal in condition + yyval.Expression = new NotExpression(GetValue(1).Expression, yyloc); + } + } + + private void _223() + { + // arg -> '~' arg +#line 912 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(1).Expression, Symbols.BitwiseNot, Arguments.Empty, GetLocation(2)); + } + } + + private void _224() + { + // arg -> arg Lshft arg +#line 916 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.LeftShift, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _225() + { + // arg -> arg Rshft arg +#line 920 "Parser.y" + { + yyval.Expression = new MethodCall(GetValue(3).Expression, Symbols.RightShift, new Arguments(GetValue(1).Expression), GetLocation(2)); + } + } + + private void _226() + { + // arg -> arg BitwiseAnd arg +#line 924 "Parser.y" + { + yyval.Expression = new AndExpression(GetValue(3).Expression, GetValue(1).Expression, GetLocation(2)); + } + } + + private void _227() + { + // arg -> arg BitwiseOr arg +#line 928 "Parser.y" + { + yyval.Expression = new OrExpression(GetValue(3).Expression, GetValue(1).Expression, GetLocation(2)); + } + } + + private void _228() + { + // arg -> arg BitwiseAnd jump_statement_parameterless +#line 932 "Parser.y" + { + yyval.Expression = new ConditionalJumpExpression(GetValue(3).Expression, GetValue(1).JumpStatement, false, null, GetLocation(2)); + } + } + + private void _229() + { + // arg -> arg BitwiseOr jump_statement_parameterless +#line 936 "Parser.y" + { + yyval.Expression = new ConditionalJumpExpression(GetValue(3).Expression, GetValue(1).JumpStatement, true, null, GetLocation(2)); + } + } + + private void _230() + { + // arg -> arg Dot2 arg +#line 940 "Parser.y" + { + yyval.Expression = new RangeExpression(GetValue(3).Expression, GetValue(1).Expression, false, GetLocation(2)); + } + } + + private void _231() + { + // arg -> arg Dot3 arg +#line 944 "Parser.y" + { + yyval.Expression = new RangeExpression(GetValue(3).Expression, GetValue(1).Expression, true, GetLocation(2)); + } + } + + private void _232() + { + // arg -> Defined opt_nl arg +#line 948 "Parser.y" + { + yyval.Expression = new IsDefinedExpression(GetValue(1).Expression, yyloc); + } + } + + private void _233() + { + // arg -> arg '?' arg ':' arg +#line 952 "Parser.y" + { + yyval.Expression = new ConditionalExpression(GetValue(5).Expression.ToCondition(), GetValue(3).Expression, GetValue(1).Expression, yyloc); + } + } + + private void _234() + { + // arg -> primary +#line 956 "Parser.y" + { + yyval.Expression = GetValue(1).Expression; + } + } + + private void _235() + { + // array_key -> +#line 962 "Parser.y" + { + yyval.Arguments = Arguments.Empty; + } + } + + private void _236() + { + // array_key -> command opt_nl +#line 966 "Parser.y" + { + _tokenizer.ReportWarning(Errors.ParenthesizeArguments); + yyval.Arguments = new Arguments(GetValue(2).CallExpression); + } + } + + private void _237() + { + // array_key -> args trailer +#line 971 "Parser.y" + { + yyval.Arguments = new Arguments(GetValue(2).Expressions, null, null, GetLocation(2)); + } + } + + private void _238() + { + // array_key -> args ',' Star arg opt_nl +#line 975 "Parser.y" + { + yyval.Arguments = new Arguments(GetValue(5).Expressions, null, GetValue(2).Expression, MergeLocations(GetLocation(5), GetLocation(2))); + } + } + + private void _239() + { + // array_key -> maplets trailer +#line 979 "Parser.y" + { + yyval.Arguments = new Arguments(null, GetValue(2).Maplets, null, GetLocation(2)); + } + } + + private void _240() + { + // array_key -> Star arg opt_nl +#line 983 "Parser.y" + { + yyval.Arguments = new Arguments(null, null, GetValue(2).Expression, MergeLocations(GetLocation(3), GetLocation(2))); + } + } + + private void _241() + { + // paren_args -> '(' ')' +#line 990 "Parser.y" + { + yyval = MakeArguments(); + } + } + + private void _242() + { + // paren_args -> '(' open_args opt_nl ')' +#line 994 "Parser.y" + { + Debug.Assert(GetValue(3).Arguments != null); + yyval = GetValue(3); + } + } + + private void _243() + { + // paren_args -> '(' block_call opt_nl ')' +#line 999 "Parser.y" + { + _tokenizer.ReportWarning(Errors.ParenthesizeArguments); + yyval = MakeArguments(GetValue(3).CallExpression); + } + } + + private void _244() + { + // paren_args -> '(' args ',' block_call opt_nl ')' +#line 1004 "Parser.y" + { + _tokenizer.ReportWarning(Errors.ParenthesizeArguments); + GetValue(5).Expressions.Add(GetValue(3).CallExpression); + yyval = MakeArguments(GetValue(5).Expressions, null, null, null, yyloc); + } + } + + private void _245() + { + // opt_paren_args -> +#line 1013 "Parser.y" + { + yyval = NoArguments(null); + } + } + + private void _246() + { + // opt_paren_args -> paren_args +#line 1017 "Parser.y" + { + yyval = GetValue(1); + } + } + + private void _247() + { + // open_args -> args opt_block_reference +#line 1024 "Parser.y" + { + yyval = MakeArguments(GetValue(2).Expressions, null, null, GetValue(1).BlockReference, yyloc); + } + } + + private void _248() + { + // open_args -> args ',' Star arg opt_block_reference +#line 1028 "Parser.y" + { + yyval = MakeArguments(GetValue(5).Expressions, null, GetValue(2).Expression, GetValue(1).BlockReference, yyloc); + } + } + + private void _249() + { + // open_args -> maplets opt_block_reference +#line 1032 "Parser.y" + { + yyval = MakeArguments(null, GetValue(2).Maplets, null, GetValue(1).BlockReference, yyloc); + } + } + + private void _250() + { + // open_args -> maplets ',' Star arg opt_block_reference +#line 1036 "Parser.y" + { + yyval = MakeArguments(null, GetValue(5).Maplets, GetValue(2).Expression, GetValue(1).BlockReference, yyloc); + } + } + + private void _251() + { + // open_args -> args ',' maplets opt_block_reference +#line 1040 "Parser.y" + { + yyval = MakeArguments(GetValue(4).Expressions, GetValue(2).Maplets, null, GetValue(1).BlockReference, yyloc); + } + } + + private void _252() + { + // open_args -> args ',' maplets ',' Star arg opt_block_reference +#line 1044 "Parser.y" + { + yyval = MakeArguments(GetValue(7).Expressions, GetValue(5).Maplets, GetValue(2).Expression, GetValue(1).BlockReference, yyloc); + } + } + + private void _253() + { + // open_args -> Star arg opt_block_reference +#line 1048 "Parser.y" + { + yyval = MakeArguments(null, null, GetValue(2).Expression, GetValue(1).BlockReference, yyloc); + } + } + + private void _254() + { + // open_args -> block_reference +#line 1052 "Parser.y" + { + yyval = MakeArguments(GetValue(1).BlockReference); + } + } + + private void _255() + { + // open_args -> command +#line 1056 "Parser.y" + { + _tokenizer.ReportWarning(Errors.ParenthesizeArguments); + yyval = MakeArguments(GetValue(1).CallExpression); + } + } + + private void _256() + { + // closed_args -> arg ',' args opt_block_reference +#line 1064 "Parser.y" + { + GetValue(2).Expressions.Insert(0, GetValue(4).Expression); + yyval = MakeArguments(GetValue(2).Expressions, null, null, GetValue(1).BlockReference, yyloc); + } + } + + private void _257() + { + // closed_args -> arg ',' block_reference +#line 1069 "Parser.y" + { + yyval = MakeArguments(GetValue(3).Expression, GetValue(1).BlockReference); + } + } + + private void _258() + { + // closed_args -> arg ',' Star arg opt_block_reference +#line 1073 "Parser.y" + { + yyval = MakeArguments(CollectionUtils.MakeList(GetValue(5).Expression), null, GetValue(2).Expression, GetValue(1).BlockReference, yyloc); + } + } + + private void _259() + { + // closed_args -> arg ',' args ',' Star arg opt_block_reference +#line 1077 "Parser.y" + { + GetValue(5).Expressions.Insert(0, GetValue(7).Expression); + yyval = MakeArguments(GetValue(5).Expressions, null, GetValue(2).Expression, GetValue(1).BlockReference, yyloc); + } + } + + private void _260() + { + // closed_args -> maplets opt_block_reference +#line 1082 "Parser.y" + { + yyval = MakeArguments(null, GetValue(2).Maplets, null, GetValue(1).BlockReference, yyloc); + } + } + + private void _261() + { + // closed_args -> maplets ',' Star arg opt_block_reference +#line 1086 "Parser.y" + { + yyval = MakeArguments(null, GetValue(5).Maplets, GetValue(2).Expression, GetValue(1).BlockReference, yyloc); + } + } + + private void _262() + { + // closed_args -> arg ',' maplets opt_block_reference +#line 1090 "Parser.y" + { + yyval = MakeArguments(CollectionUtils.MakeList(GetValue(4).Expression), GetValue(2).Maplets, null, GetValue(1).BlockReference, yyloc); + } + } + + private void _263() + { + // closed_args -> arg ',' args ',' maplets opt_block_reference +#line 1094 "Parser.y" + { + GetValue(4).Expressions.Insert(0, GetValue(6).Expression); + yyval = MakeArguments(GetValue(4).Expressions, GetValue(2).Maplets, null, GetValue(1).BlockReference, yyloc); + } + } + + private void _264() + { + // closed_args -> arg ',' maplets ',' Star arg opt_block_reference +#line 1099 "Parser.y" + { + yyval = MakeArguments(CollectionUtils.MakeList(GetValue(7).Expression), GetValue(5).Maplets, GetValue(2).Expression, GetValue(1).BlockReference, yyloc); + } + } + + private void _265() + { + // closed_args -> arg ',' args ',' maplets ',' Star arg opt_block_reference +#line 1103 "Parser.y" + { + GetValue(7).Expressions.Insert(0, GetValue(9).Expression); + yyval = MakeArguments(GetValue(7).Expressions, GetValue(5).Maplets, GetValue(2).Expression, GetValue(1).BlockReference, yyloc); + } + } + + private void _266() + { + // closed_args -> Star arg opt_block_reference +#line 1108 "Parser.y" + { + yyval = MakeArguments(null, null, GetValue(2).Expression, GetValue(1).BlockReference, yyloc); + } + } + + private void _267() + { + // closed_args -> block_reference +#line 1112 "Parser.y" + { + yyval = MakeArguments(GetValue(1).BlockReference); + } + } + + private void _268() + { + // @7 -> +#line 1118 "Parser.y" + { + yyval.Integer = _tokenizer.CMDARG; + _tokenizer.CMDARG_PUSH(1); + } + } + + private void _269() + { + // command_args -> @7 command_args_content +#line 1123 "Parser.y" + { + _tokenizer.CMDARG = GetValue(2).Integer; + yyval = GetValue(1); + } + } + + private void _270() + { + // command_args_content -> open_args +#line 1131 "Parser.y" + { + Debug.Assert(GetValue(1).Arguments != null); + yyval = GetValue(1); + } + } + + private void _271() + { + // @8 -> +#line 1136 "Parser.y" + { + _tokenizer.SetState(LexicalState.EXPR_ENDARG); + } + } + + private void _272() + { + // command_args_content -> LparenArg @8 ')' +#line 1140 "Parser.y" + { + _tokenizer.ReportWarning(Errors.WhitespaceBeforeArgumentParentheses); + yyval = MakeArguments(); + } + } + + private void _273() + { + // @9 -> +#line 1145 "Parser.y" + { + _tokenizer.SetState(LexicalState.EXPR_ENDARG); + } + } + + private void _274() + { + // command_args_content -> LparenArg closed_args @9 ')' +#line 1149 "Parser.y" + { + _tokenizer.ReportWarning(Errors.WhitespaceBeforeArgumentParentheses); + yyval = GetValue(3); + } + } + + private void _275() + { + // block_reference -> Ampersand arg +#line 1157 "Parser.y" + { + yyval.BlockReference = new BlockReference(GetValue(1).Expression, yyloc); + } + } + + private void _276() + { + // opt_block_reference -> ',' block_reference +#line 1164 "Parser.y" + { + yyval.BlockReference = GetValue(1).BlockReference; + } + } + + private void _277() + { + // opt_block_reference -> +#line 1168 "Parser.y" + { + yyval.BlockReference = null; + } + } + + private void _278() + { + // args -> arg +#line 1175 "Parser.y" + { + yyval.Expressions = CollectionUtils.MakeList(GetValue(1).Expression); + } + } + + private void _279() + { + // args -> args ',' arg +#line 1179 "Parser.y" + { + GetValue(3).Expressions.Add(GetValue(1).Expression); + yyval.Expressions = GetValue(3).Expressions; + } + } + + private void _281() + { + // primary -> symbol +#line 1187 "Parser.y" + { + yyval.Expression = new SymbolLiteral(GetValue(1).String, yyloc); + } + } + + private void _283() + { + // primary -> string_concatenation +#line 1192 "Parser.y" + { + yyval.Expression = new StringConstructor(GetValue(1).Expressions, StringKind.Mutable, GetLocation(1)); + } + } + + private void _289() + { + // primary -> match_reference +#line 1201 "Parser.y" + { + yyval.Expression = GetValue(1).RegexMatchReference; + } + } + + private void _290() + { + // primary -> FunctionIdentifier +#line 1205 "Parser.y" + { + yyval.Expression = new MethodCall(null, GetValue(1).String, null, GetLocation(1)); + } + } + + private void _291() + { + // primary -> primary SeparatingDoubleColon ConstantIdentifier +#line 1209 "Parser.y" + { + yyval.Expression = new ConstantVariable(GetValue(3).Expression, GetValue(1).String, yyloc); + } + } + + private void _292() + { + // primary -> LeadingDoubleColon ConstantIdentifier +#line 1213 "Parser.y" + { + yyval.Expression = new ConstantVariable(null, GetValue(1).String, yyloc); + } + } + + private void _293() + { + // primary -> primary '[' array_key ']' +#line 1217 "Parser.y" + { + yyval.Expression = new ArrayItemAccess(GetValue(4).Expression, GetValue(2).Arguments, yyloc); + } + } + + private void _294() + { + // primary -> Lbrack array_key ']' +#line 1221 "Parser.y" + { + yyval.Expression = new ArrayConstructor(GetValue(2).Arguments, yyloc); + } + } + + private void _295() + { + // primary -> Lbrace '}' +#line 1225 "Parser.y" + { + yyval.Expression = new HashConstructor(null, null, yyloc); + } + } + + private void _296() + { + // primary -> Lbrace maplets trailer '}' +#line 1229 "Parser.y" + { + yyval.Expression = new HashConstructor(GetValue(3).Maplets, null, yyloc); + } + } + + private void _297() + { + // primary -> Lbrace args trailer '}' +#line 1233 "Parser.y" + { + yyval.Expression = new HashConstructor(null, CheckHashExpressions(GetValue(3).Expressions, GetLocation(2)), yyloc); + } + } + + private void _298() + { + // primary -> Yield '(' open_args ')' +#line 1237 "Parser.y" + { + yyval.Expression = new YieldCall(RequireNoBlockArg(GetValue(2)), yyloc); + } + } + + private void _299() + { + // primary -> Yield '(' ')' +#line 1241 "Parser.y" + { + yyval.Expression = new YieldCall(Arguments.Empty, yyloc); + } + } + + private void _300() + { + // primary -> Yield +#line 1245 "Parser.y" + { + yyval.Expression = new YieldCall(null, GetLocation(1)); + } + } + + private void _301() + { + // primary -> Defined opt_nl '(' expr ')' +#line 1249 "Parser.y" + { + yyval.Expression = new IsDefinedExpression(GetValue(2).Expression, yyloc); + } + } + + private void _302() + { + // primary -> operation brace_block +#line 1253 "Parser.y" + { + yyval.Expression = new MethodCall(null, GetValue(2).String, null, GetValue(1).BlockDefinition, GetLocation(2)); + } + } + + private void _304() + { + // primary -> method_call brace_block +#line 1258 "Parser.y" + { + GetValue(2).CallExpression.Block = GetValue(1).BlockDefinition; + yyval.Expression = GetValue(2).CallExpression; + } + } + + private void _305() + { + // primary -> If expr then compstmt if_tail End +#line 1263 "Parser.y" + { + yyval.Expression = MakeIfExpression(GetValue(5).Expression.ToCondition(), GetValue(3).Expressions, GetValue(2).ElseIfClauses, yyloc); + } + } + + private void _306() + { + // primary -> Unless expr then compstmt else_opt End +#line 1267 "Parser.y" + { + yyval.Expression = new UnlessExpression(GetValue(5).Expression.ToCondition(), GetValue(3).Expressions, GetValue(2).ElseIfClause, yyloc); + } + } + + private void _307() + { + // @10 -> +#line 1271 "Parser.y" + { + _tokenizer.COND_PUSH(1); + } + } + + private void _308() + { + // @11 -> +#line 1275 "Parser.y" + { + _tokenizer.COND_POP(); + } + } + + private void _309() + { + // primary -> While @10 expr do @11 compstmt End +#line 1279 "Parser.y" + { + yyval.Expression = new WhileLoopExpression(GetValue(5).Expression.ToCondition(), true, false, GetValue(2).Expressions, yyloc); + } + } + + private void _310() + { + // @12 -> +#line 1283 "Parser.y" + { + _tokenizer.COND_PUSH(1); + } + } + + private void _311() + { + // @13 -> +#line 1287 "Parser.y" + { + _tokenizer.COND_POP(); + } + } + + private void _312() + { + // primary -> Until @12 expr do @13 compstmt End +#line 1291 "Parser.y" + { + yyval.Expression = new WhileLoopExpression(GetValue(5).Expression.ToCondition(), false, false, GetValue(2).Expressions, yyloc); + } + } + + private void _314() + { + // @14 -> +#line 1296 "Parser.y" + { + _tokenizer.COND_PUSH(1); + } + } + + private void _315() + { + // @15 -> +#line 1300 "Parser.y" + { + _tokenizer.COND_POP(); + } + } + + private void _316() + { + // primary -> For block_parameters In @14 expr do @15 compstmt End +#line 1304 "Parser.y" + { + yyval.Expression = new ForLoopExpression(GetValue(8).CompoundLeftValue, GetValue(5).Expression, GetValue(2).Expressions, yyloc); + } + } + + private void _317() + { + // primary -> block_expression +#line 1308 "Parser.y" + { + yyval.Expression = GetValue(1).Expression; + } + } + + private void _318() + { + // primary -> declaration_expression +#line 1312 "Parser.y" + { + yyval.Expression = GetValue(1).Expression; + } + } + + private void _319() + { + // @16 -> +#line 1319 "Parser.y" + { + _tokenizer.SetState(LexicalState.EXPR_ENDARG); + } + } + + private void _320() + { + // block_expression -> LparenArg expr @16 opt_nl ')' +#line 1323 "Parser.y" + { + _tokenizer.ReportWarning(Errors.InterpretedAsGroupedExpression); + // BlockExpression behaves like an expression, so we don't need to create one here: + yyval.Expression = GetValue(4).Expression; + } + } + + private void _321() + { + // block_expression -> LeftParen compstmt ')' +#line 1329 "Parser.y" + { + yyval.Expression = MakeBlockExpression(GetValue(2).Expressions, yyloc); + } + } + + private void _322() + { + // block_expression -> Begin body End +#line 1333 "Parser.y" + { + yyval.Expression = GetValue(2).Body; + } + } + + private void _323() + { + // @17 -> +#line 1340 "Parser.y" + { + EnterTopScope(); + } + } + + private void _324() + { + // declaration_expression -> Class qualified_module_name superclass @17 body End +#line 1344 "Parser.y" + { + if (InMethod) { + ErrorSink.Add(_sourceUnit, "class definition in method body", GetLocation(6), -1, Severity.Error); + } + yyval.Expression = new ClassDeclaration(CurrentScope, GetValue(5).ConstantVariable, GetValue(4).Expression, GetValue(2).Body, yyloc); + LeaveScope(); + } + } + + private void _325() + { + // @18 -> +#line 1352 "Parser.y" + { + yyval.Integer = _inInstanceMethodDefinition; + _inInstanceMethodDefinition = 0; + } + } + + private void _326() + { + // @19 -> +#line 1357 "Parser.y" + { + yyval.Integer = _inSingletonMethodDefinition; + _inSingletonMethodDefinition = 0; + EnterTopScope(); + } + } + + private void _327() + { + // declaration_expression -> Class Lshft expr @18 term @19 body End +#line 1363 "Parser.y" + { + _inInstanceMethodDefinition = GetValue(5).Integer; + _inSingletonMethodDefinition = GetValue(3).Integer; + yyval.Expression = new SingletonDeclaration(LeaveScope(), GetValue(6).Expression, GetValue(2).Body, yyloc); + } + } + + private void _328() + { + // @20 -> +#line 1369 "Parser.y" + { + EnterTopScope(); + } + } + + private void _329() + { + // declaration_expression -> Module qualified_module_name @20 body End +#line 1373 "Parser.y" + { + if (InMethod) { + ErrorSink.Add(_sourceUnit, "module definition in method body", GetLocation(5), -1, Severity.Error); + } + yyval.Expression = new ModuleDeclaration(CurrentScope, GetValue(4).ConstantVariable, GetValue(2).Body, yyloc); + LeaveScope(); + } + } + + private void _330() + { + // @21 -> +#line 1381 "Parser.y" + { + _inInstanceMethodDefinition++; + EnterTopScope(); + } + } + + private void _331() + { + // declaration_expression -> Def method_name @21 parameters_declaration body End +#line 1386 "Parser.y" + { + _inInstanceMethodDefinition--; + yyval.Expression = new MethodDeclaration(CurrentScope, null, GetValue(5).String, GetValue(3).Parameters, GetValue(2).Body, yyloc); + LeaveScope(); + } + } + + private void _332() + { + // @22 -> +#line 1392 "Parser.y" + { + _tokenizer.SetState(LexicalState.EXPR_FNAME); + } + } + + private void _333() + { + // @23 -> +#line 1396 "Parser.y" + { + _inSingletonMethodDefinition++; + _tokenizer.SetState(LexicalState.EXPR_END); + EnterTopScope(); + } + } + + private void _334() + { + // declaration_expression -> Def singleton dot_or_colon @22 method_name @23 parameters_declaration body End +#line 1402 "Parser.y" + { + _inSingletonMethodDefinition--; + yyval.Expression = new MethodDeclaration(CurrentScope, GetValue(8).Expression, GetValue(5).String, GetValue(3).Parameters, GetValue(2).Body, yyloc); + LeaveScope(); + } + } + + private void _335() + { + // body -> compstmt rescue_clauses_opt else_opt ensure_opt +#line 1411 "Parser.y" + { + ElseIfClause elseIf = GetValue(2).ElseIfClause; + Debug.Assert(elseIf == null || elseIf.Condition == null); + + if (elseIf != null && GetValue(3).RescueClauses == null) { + ErrorSink.Add(_sourceUnit, "else without rescue is useless", GetLocation(2), -1, Severity.Warning); + } + + yyval.Body = new Body(GetValue(4).Expressions, GetValue(3).RescueClauses, (elseIf != null) ? elseIf.Statements : null, GetValue(1).Expressions, yyloc); + } + } + + private void _336() + { + // case_expression -> Case expr opt_terms when_clauses else_opt End +#line 1425 "Parser.y" + { + yyval.Expression = new CaseExpression(GetValue(5).Expression, GetValue(3).WhenClauses, GetValue(2).ElseIfClause, yyloc); + } + } + + private void _337() + { + // case_expression -> Case opt_terms when_clauses else_opt End +#line 1429 "Parser.y" + { + yyval.Expression = new CaseExpression(null, GetValue(3).WhenClauses, GetValue(2).ElseIfClause, yyloc); + } + } + + private void _338() + { + // case_expression -> Case expr opt_terms else_opt End +#line 1433 "Parser.y" + { + yyval.Expression = new CaseExpression(GetValue(4).Expression, null, GetValue(2).ElseIfClause, yyloc); + } + } + + private void _339() + { + // case_expression -> Case opt_terms else_opt End +#line 1437 "Parser.y" + { + yyval.Expression = new CaseExpression(null, null, GetValue(2).ElseIfClause, yyloc); + } + } + + private void _347() + { + // if_tail -> else_opt +#line 1457 "Parser.y" + { + yyval.ElseIfClauses = MakeListAddOpt(GetValue(1).ElseIfClause); + } + } + + private void _348() + { + // if_tail -> Elsif expr then compstmt if_tail +#line 1461 "Parser.y" + { + GetValue(1).ElseIfClauses.Add(new ElseIfClause(GetValue(4).Expression, GetValue(2).Expressions, yyloc)); + yyval.ElseIfClauses = GetValue(1).ElseIfClauses; + } + } + + private void _349() + { + // else_opt -> +#line 1468 "Parser.y" + { + yyval.ElseIfClause = null; + } + } + + private void _350() + { + // else_opt -> Else compstmt +#line 1472 "Parser.y" + { + yyval.ElseIfClause = new ElseIfClause(null, GetValue(1).Expressions, yyloc); + } + } + + private void _351() + { + // block_parameters -> lhs +#line 1479 "Parser.y" + { + yyval.CompoundLeftValue = new CompoundLeftValue(CollectionUtils.MakeList(GetValue(1).LeftValue), null, GetLocation(1)); + } + } + + private void _352() + { + // block_parameters -> compound_lhs +#line 1483 "Parser.y" + { + yyval.CompoundLeftValue = GetValue(1).CompoundLeftValue; + } + } + + private void _353() + { + // block_parameters_opt -> +#line 1490 "Parser.y" + { + yyval.CompoundLeftValue = CompoundLeftValue.UnspecifiedBlockSignature; + } + } + + private void _354() + { + // block_parameters_opt -> '|' '|' +#line 1494 "Parser.y" + { + yyval.CompoundLeftValue = CompoundLeftValue.EmptyBlockSignature; + } + } + + private void _355() + { + // block_parameters_opt -> BitwiseOr +#line 1498 "Parser.y" + { + yyval.CompoundLeftValue = CompoundLeftValue.EmptyBlockSignature; + } + } + + private void _356() + { + // block_parameters_opt -> '|' block_parameters '|' +#line 1502 "Parser.y" + { + yyval.CompoundLeftValue = GetValue(2).CompoundLeftValue; + } + } + + private void _357() + { + // @24 -> +#line 1509 "Parser.y" + { + EnterNestedScope(); + } + } + + private void _358() + { + // do_block -> BlockDo @24 block_parameters_opt compstmt End +#line 1513 "Parser.y" + { + yyval.BlockDefinition = new BlockDefinition(CurrentScope, GetValue(3).CompoundLeftValue, GetValue(2).Expressions, yyloc); + LeaveScope(); + } + } + + private void _359() + { + // block_call -> command do_block +#line 1521 "Parser.y" + { + GetValue(2).CallExpression.Block = GetValue(1).BlockDefinition; + yyval.CallExpression = GetValue(2).CallExpression; + } + } + + private void _360() + { + // block_call -> block_call '.' operation2 opt_paren_args +#line 1526 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(GetValue(4).CallExpression, GetValue(2).String, GetValue(1), yyloc); + } + } + + private void _361() + { + // block_call -> block_call SeparatingDoubleColon operation2 opt_paren_args +#line 1530 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(GetValue(4).CallExpression, GetValue(2).String, GetValue(1), yyloc); + } + } + + private void _362() + { + // method_call -> operation paren_args +#line 1537 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(null, GetValue(2).String, GetValue(1), yyloc); + } + } + + private void _363() + { + // method_call -> primary '.' operation2 opt_paren_args +#line 1541 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(GetValue(4).Expression, GetValue(2).String, GetValue(1), yyloc); + } + } + + private void _364() + { + // method_call -> primary SeparatingDoubleColon operation2 paren_args +#line 1545 "Parser.y" + { + yyval.CallExpression = MakeMethodCall(GetValue(4).Expression, GetValue(2).String, GetValue(1), yyloc); + } + } + + private void _365() + { + // method_call -> primary SeparatingDoubleColon operation3 +#line 1549 "Parser.y" + { + yyval.CallExpression = new MethodCall(GetValue(3).Expression, GetValue(1).String, null, GetLocation(1)); + } + } + + private void _366() + { + // method_call -> Super paren_args +#line 1553 "Parser.y" + { + yyval.CallExpression = MakeSuperCall(GetValue(1), GetLocation(2)); + } + } + + private void _367() + { + // method_call -> Super +#line 1557 "Parser.y" + { + yyval.CallExpression = new SuperCall(null, null, GetLocation(1)); + } + } + + private void _368() + { + // @25 -> +#line 1564 "Parser.y" + { + EnterNestedScope(); + } + } + + private void _369() + { + // brace_block -> '{' @25 block_parameters_opt compstmt '}' +#line 1568 "Parser.y" + { + yyval.BlockDefinition = new BlockDefinition(CurrentScope, GetValue(3).CompoundLeftValue, GetValue(2).Expressions, yyloc); + LeaveScope(); + } + } + + private void _370() + { + // @26 -> +#line 1573 "Parser.y" + { + EnterNestedScope(); + } + } + + private void _371() + { + // brace_block -> Do @26 block_parameters_opt compstmt End +#line 1577 "Parser.y" + { + yyval.BlockDefinition = new BlockDefinition(CurrentScope, GetValue(3).CompoundLeftValue, GetValue(2).Expressions, yyloc); + LeaveScope(); + } + } + + private void _372() + { + // when_clauses -> when_clause +#line 1584 "Parser.y" + { + yyval.WhenClauses = CollectionUtils.MakeList(GetValue(1).WhenClause); + } + } + + private void _373() + { + // when_clauses -> when_clauses when_clause +#line 1588 "Parser.y" + { + GetValue(2).WhenClauses.Add(GetValue(1).WhenClause); + yyval.WhenClauses = GetValue(2).WhenClauses; + } + } + + private void _374() + { + // when_clause -> When when_args then compstmt +#line 1596 "Parser.y" + { + yyval.WhenClause = new WhenClause(GetValue(3).Expressions, GetValue(3).Expression, GetValue(1).Expressions, yyloc); + } + } + + private void _375() + { + // when_args -> args +#line 1603 "Parser.y" + { + yyval = new TokenValue(GetValue(1).Expressions, null); + } + } + + private void _376() + { + // when_args -> args ',' Star arg +#line 1607 "Parser.y" + { + yyval = new TokenValue(GetValue(4).Expressions, GetValue(1).Expression); + } + } + + private void _377() + { + // when_args -> Star arg +#line 1611 "Parser.y" + { + yyval = new TokenValue(null, GetValue(1).Expression); + } + } + + private void _380() + { + // rescue_clauses -> rescue_clause +#line 1623 "Parser.y" + { + yyval.RescueClauses = CollectionUtils.MakeList(GetValue(1).RescueClause); + } + } + + private void _381() + { + // rescue_clauses -> rescue_clauses rescue_clause +#line 1627 "Parser.y" + { + GetValue(2).RescueClauses.Add(GetValue(1).RescueClause); + yyval.RescueClauses = GetValue(2).RescueClauses; + } + } + + private void _382() + { + // rescue_clause -> Rescue exc_var then compstmt +#line 1635 "Parser.y" + { + yyval.RescueClause = new RescueClause(GetValue(3).LeftValue, GetValue(1).Expressions, yyloc); + } + } + + private void _383() + { + // rescue_clause -> Rescue arg exc_var then compstmt +#line 1639 "Parser.y" + { + yyval.RescueClause = new RescueClause(GetValue(4).Expression, GetValue(3).LeftValue, GetValue(1).Expressions, yyloc); + } + } + + private void _384() + { + // rescue_clause -> Rescue compound_rhs exc_var then compstmt +#line 1643 "Parser.y" + { + yyval.RescueClause = new RescueClause(GetValue(4).CompoundRightValue, GetValue(3).LeftValue, GetValue(1).Expressions, yyloc); + } + } + + private void _386() + { + // exc_var -> Assoc lhs +#line 1651 "Parser.y" + { + yyval.LeftValue = GetValue(1).LeftValue; + } + } + + private void _388() + { + // ensure_opt -> Ensure compstmt +#line 1659 "Parser.y" + { + yyval.Expressions = GetValue(1).Expressions; + } + } + + private void _389() + { + // string_concatenation -> string +#line 1666 "Parser.y" + { + yyval.Expressions = GetValue(1).Expressions; + } + } + + private void _390() + { + // string_concatenation -> string_concatenation string +#line 1670 "Parser.y" + { + GetValue(2).Expressions.AddRange(GetValue(1).Expressions); + yyval.Expressions = GetValue(2).Expressions; + } + } + + private void _391() + { + // string -> StringBeg string_contents StringEnd +#line 1678 "Parser.y" + { + yyval.Expressions = GetValue(2).Expressions; + } + } + + private void _392() + { + // shell_string -> ShellStringBegin string_contents StringEnd +#line 1685 "Parser.y" + { + yyval.Expression = new StringConstructor(GetValue(2).Expressions, StringKind.Command, yyloc); + } + } + + private void _393() + { + // immutable_string -> Symbeg string_contents StringEnd +#line 1692 "Parser.y" + { + yyval.Expression = MakeSymbolConstructor(GetValue(2).Expressions, yyloc); + } + } + + private void _394() + { + // regexp -> RegexpBeg string_contents RegexpEnd +#line 1699 "Parser.y" + { + yyval.Expression = new RegularExpression(GetValue(2).Expressions, GetValue(1).RegExOptions, yyloc); + } + } + + private void _395() + { + // words -> WordsBeg WordSeparator StringEnd +#line 1705 "Parser.y" + { + yyval.Expression = new ArrayConstructor(null, yyloc); + } + } + + private void _396() + { + // words -> WordsBeg word_list StringEnd +#line 1709 "Parser.y" + { + yyval.Expression = new ArrayConstructor(new Arguments(GetValue(2).Expressions, null, null, GetLocation(2)), yyloc); + } + } + + private void _397() + { + // word_list -> +#line 1715 "Parser.y" + { + yyval.Expressions = new List(); + } + } + + private void _398() + { + // word_list -> word_list word WordSeparator +#line 1719 "Parser.y" + { + GetValue(3).Expressions.Add(new StringConstructor(GetValue(2).Expressions, StringKind.Mutable, GetLocation(2))); + yyval.Expressions = GetValue(3).Expressions; + } + } + + private void _399() + { + // word -> string_content +#line 1726 "Parser.y" + { + yyval.Expressions = CollectionUtils.MakeList(GetValue(1).Expression); + } + } + + private void _400() + { + // word -> word string_content +#line 1730 "Parser.y" + { + GetValue(2).Expressions.Add(GetValue(1).Expression); + yyval.Expressions = GetValue(2).Expressions; + } + } + + private void _401() + { + // verbatim_words -> VerbatimWordsBegin WordSeparator StringEnd +#line 1737 "Parser.y" + { + yyval.Expression = new ArrayConstructor(null, yyloc); + } + } + + private void _402() + { + // verbatim_words -> VerbatimWordsBegin verbatim_word_list StringEnd +#line 1741 "Parser.y" + { + yyval.Expression = MakeVerbatimWords(GetValue(2).Expressions, GetLocation(2), yyloc); + } + } + + private void _403() + { + // verbatim_word_list -> +#line 1747 "Parser.y" + { + yyval.Expressions = new List(); + } + } + + private void _404() + { + // verbatim_word_list -> verbatim_word_list StringContent WordSeparator +#line 1751 "Parser.y" + { + GetValue(3).Expressions.Add(MakeStringLiteral(GetValue(2), GetLocation(2))); + yyval.Expressions = GetValue(3).Expressions; + } + } + + private void _405() + { + // string_contents -> +#line 1759 "Parser.y" + { + yyval.Expressions = new List(); + } + } + + private void _406() + { + // string_contents -> string_contents string_content +#line 1763 "Parser.y" + { + GetValue(2).Expressions.Add(GetValue(1).Expression); + yyval.Expressions = GetValue(2).Expressions; + } + } + + private void _407() + { + // string_content -> StringContent +#line 1771 "Parser.y" + { + yyval.Expression = MakeStringLiteral(GetValue(1), yyloc); + } + } + + private void _408() + { + // string_content -> StringEmbeddedVariableBegin string_embedded_variable +#line 1775 "Parser.y" + { + _tokenizer.StringEmbeddedVariableEnd(GetValue(2).StringTokenizer); + yyval.Expression = GetValue(1).Expression; + } + } + + private void _409() + { + // string_content -> StringEmbeddedCodeBegin compstmt '}' +#line 1780 "Parser.y" + { + _tokenizer.StringEmbeddedCodeEnd(GetValue(3).StringTokenizer); + yyval.Expression = MakeBlockExpression(GetValue(2).Expressions, GetLocation(2)); + } + } + + private void _410() + { + // string_embedded_variable -> GlobalVariable +#line 1788 "Parser.y" + { + yyval.Expression = new GlobalVariable(GetValue(1).String, yyloc); + } + } + + private void _411() + { + // string_embedded_variable -> match_reference +#line 1792 "Parser.y" + { + yyval.Expression = GetValue(1).RegexMatchReference; + } + } + + private void _412() + { + // string_embedded_variable -> InstanceVariable +#line 1796 "Parser.y" + { + yyval.Expression = new InstanceVariable(GetValue(1).String, yyloc); + } + } + + private void _413() + { + // string_embedded_variable -> ClassVariable +#line 1800 "Parser.y" + { + yyval.Expression = new ClassVariable(GetValue(1).String, yyloc); + } + } + + private void _414() + { + // symbol -> Symbeg sym +#line 1807 "Parser.y" + { + _tokenizer.SetState(LexicalState.EXPR_END); + yyval.String = GetValue(1).String; + } + } + + private void _417() + { + // sym -> GlobalVariable +#line 1817 "Parser.y" + { + yyval.String = "$" + GetValue(1).String; + } + } + + private void _419() + { + // sym -> match_reference +#line 1822 "Parser.y" + { + yyval.String = GetValue(1).RegexMatchReference.FullName; + } + } + + private void _420() + { + // numeric_literal -> Integer +#line 1829 "Parser.y" + { + // unsigned integer: + yyval.Expression = Literal.Integer(GetValue(1).Integer, yyloc); + } + } + + private void _421() + { + // numeric_literal -> BigInteger +#line 1834 "Parser.y" + { + yyval.Expression = Literal.BigInteger(GetValue(1).BigInteger, yyloc); + } + } + + private void _422() + { + // numeric_literal -> Float +#line 1838 "Parser.y" + { + yyval.Expression = Literal.Double(GetValue(1).Double, yyloc); + } + } + + private void _423() + { + // numeric_literal -> UminusNum Integer +#line 1842 "Parser.y" + { + // cannot overflow INTEGER is unsigned and Int32.MaxValue < |Int32.MinValue| + yyval.Expression = Literal.Integer(-GetValue(1).Integer, yyloc); + } + } + + private void _424() + { + // numeric_literal -> UminusNum BigInteger +#line 1847 "Parser.y" + { + // TODO: -|Int32.MinValue| actually ends up here (converted to bigint) instead of being Int32. We should fix that. + yyval.Expression = Literal.BigInteger(-GetValue(1).BigInteger, yyloc); + } + } + + private void _425() + { + // numeric_literal -> UminusNum Float +#line 1852 "Parser.y" + { + yyval.Expression = Literal.Double(-GetValue(1).Double, yyloc); + } + } + + private void _426() + { + // variable -> Identifier +#line 1858 "Parser.y" + { yyval.VariableFactory = VariableFactory.Identifier; yyval.String = GetValue(1).String; } + } + + private void _427() + { + // variable -> InstanceVariable +#line 1859 "Parser.y" + { yyval.VariableFactory = VariableFactory.Instance; yyval.String = GetValue(1).String; } + } + + private void _428() + { + // variable -> GlobalVariable +#line 1860 "Parser.y" + { yyval.VariableFactory = VariableFactory.Global; yyval.String = GetValue(1).String; } + } + + private void _429() + { + // variable -> ConstantIdentifier +#line 1861 "Parser.y" + { yyval.VariableFactory = VariableFactory.Constant; yyval.String = GetValue(1).String; } + } + + private void _430() + { + // variable -> ClassVariable +#line 1862 "Parser.y" + { yyval.VariableFactory = VariableFactory.Class; yyval.String = GetValue(1).String; } + } + + private void _431() + { + // variable -> Nil +#line 1863 "Parser.y" + { yyval.VariableFactory = VariableFactory.Nil; yyval.String = null; } + } + + private void _432() + { + // variable -> Self +#line 1864 "Parser.y" + { yyval.VariableFactory = VariableFactory.Self; yyval.String = null; } + } + + private void _433() + { + // variable -> True +#line 1865 "Parser.y" + { yyval.VariableFactory = VariableFactory.True; yyval.String = null; } + } + + private void _434() + { + // variable -> False +#line 1866 "Parser.y" + { yyval.VariableFactory = VariableFactory.False; yyval.String = null; } + } + + private void _435() + { + // variable -> File +#line 1867 "Parser.y" + { yyval.VariableFactory = VariableFactory.File; yyval.String = null; } + } + + private void _436() + { + // variable -> Line +#line 1868 "Parser.y" + { yyval.VariableFactory = VariableFactory.Line; yyval.String = null; } + } + + private void _437() + { + // variable -> Encoding +#line 1869 "Parser.y" + { yyval.VariableFactory = VariableFactory.Encoding; yyval.String = null; } + } + + private void _438() + { + // var_ref -> variable +#line 1874 "Parser.y" + { + yyval.Expression = VariableFactory.MakeRead(GetValue(1).VariableFactory, this, GetValue(1).String, yyloc); + } + } + + private void _439() + { + // var_lhs -> variable +#line 1881 "Parser.y" + { + yyval.LeftValue = VariableFactory.MakeLeftValue(GetValue(1).VariableFactory, this, GetValue(1).String, yyloc); + } + } + + private void _440() + { + // match_reference -> MatchReference +#line 1888 "Parser.y" + { + yyval.RegexMatchReference = new RegexMatchReference(GetValue(1).Integer, GetLocation(1)); + } + } + + private void _441() + { + // superclass -> term +#line 1894 "Parser.y" + { + yyval.Expression = null; + } + } + + private void _442() + { + // @27 -> +#line 1898 "Parser.y" + { + _tokenizer.SetState(LexicalState.EXPR_BEG); + } + } + + private void _443() + { + // superclass -> '<' @27 expr term +#line 1902 "Parser.y" + { + yyval.Expression = GetValue(2).Expression; + } + } + + private void _444() + { + // superclass -> Error term +#line 1906 "Parser.y" + { + StopErrorRecovery(); + yyval.Expression = null; + } + } + + private void _445() + { + // parameters_declaration -> '(' parameters opt_nl ')' +#line 1913 "Parser.y" + { + yyval.Parameters = GetValue(3).Parameters; + _tokenizer.SetState(LexicalState.EXPR_BEG); + } + } + + private void _446() + { + // parameters_declaration -> parameters term +#line 1918 "Parser.y" + { + yyval.Parameters = GetValue(2).Parameters; + } + } + + private void _447() + { + // parameters -> parameter_list ',' default_parameter_list ',' array_parameter block_parameter_opt +#line 1924 "Parser.y" + { + yyval.Parameters = new Parameters(GetValue(6).LocalVariables, GetValue(4).SimpleAssignmentExpressions, GetValue(2).LocalVariable, GetValue(1).LocalVariable, yyloc); + } + } + + private void _448() + { + // parameters -> parameter_list ',' default_parameter_list block_parameter_opt +#line 1928 "Parser.y" + { + yyval.Parameters = new Parameters(GetValue(4).LocalVariables, GetValue(2).SimpleAssignmentExpressions, null, GetValue(1).LocalVariable, yyloc); + } + } + + private void _449() + { + // parameters -> parameter_list ',' array_parameter block_parameter_opt +#line 1932 "Parser.y" + { + yyval.Parameters = new Parameters(GetValue(4).LocalVariables, null, GetValue(2).LocalVariable, GetValue(1).LocalVariable, yyloc); + } + } + + private void _450() + { + // parameters -> parameter_list block_parameter_opt +#line 1936 "Parser.y" + { + yyval.Parameters = new Parameters(GetValue(2).LocalVariables, null, null, GetValue(1).LocalVariable, yyloc); + } + } + + private void _451() + { + // parameters -> default_parameter_list ',' array_parameter block_parameter_opt +#line 1940 "Parser.y" + { + yyval.Parameters = new Parameters(null, GetValue(4).SimpleAssignmentExpressions, GetValue(2).LocalVariable, GetValue(1).LocalVariable, yyloc); + } + } + + private void _452() + { + // parameters -> default_parameter_list block_parameter_opt +#line 1944 "Parser.y" + { + yyval.Parameters = new Parameters(null, GetValue(2).SimpleAssignmentExpressions, null, GetValue(1).LocalVariable, yyloc); + } + } + + private void _453() + { + // parameters -> array_parameter block_parameter_opt +#line 1948 "Parser.y" + { + yyval.Parameters = new Parameters(null, null, GetValue(2).LocalVariable, GetValue(1).LocalVariable, yyloc); + } + } + + private void _454() + { + // parameters -> block_parameter +#line 1952 "Parser.y" + { + yyval.Parameters = new Parameters(null, null, null, GetValue(1).LocalVariable, yyloc); + } + } + + private void _455() + { + // parameters -> +#line 1956 "Parser.y" + { + yyval.Parameters = new Parameters(null, null, null, null, yyloc); + } + } + + private void _456() + { + // parameter -> ConstantIdentifier +#line 1962 "Parser.y" + { + _tokenizer.ReportError(Errors.FormalArgumentIsConstantVariable); + yyval.LocalVariable = DefineParameter(GenerateErrorConstantName(), yyloc); + } + } + + private void _457() + { + // parameter -> InstanceVariable +#line 1967 "Parser.y" + { + _tokenizer.ReportError(Errors.FormalArgumentIsInstanceVariable); + yyval.LocalVariable = DefineParameter(GenerateErrorConstantName(), yyloc); + } + } + + private void _458() + { + // parameter -> GlobalVariable +#line 1972 "Parser.y" + { + _tokenizer.ReportError(Errors.FormalArgumentIsGlobalVariable); + yyval.LocalVariable = DefineParameter(GenerateErrorConstantName(), yyloc); + } + } + + private void _459() + { + // parameter -> ClassVariable +#line 1977 "Parser.y" + { + _tokenizer.ReportError(Errors.FormalArgumentIsClassVariable); + yyval.LocalVariable = DefineParameter(GenerateErrorConstantName(), yyloc); + } + } + + private void _460() + { + // parameter -> Identifier +#line 1982 "Parser.y" + { + yyval.LocalVariable = DefineParameter(GetValue(1).String, yyloc); + } + } + + private void _461() + { + // parameter_list -> parameter +#line 1988 "Parser.y" + { + yyval.LocalVariables = CollectionUtils.MakeList(GetValue(1).LocalVariable); + } + } + + private void _462() + { + // parameter_list -> parameter_list ',' parameter +#line 1992 "Parser.y" + { + GetValue(3).LocalVariables.Add(GetValue(1).LocalVariable); + yyval.LocalVariables = GetValue(3).LocalVariables; + } + } + + private void _463() + { + // default_parameter -> parameter '=' arg +#line 1999 "Parser.y" + { + yyval.SimpleAssignmentExpression = new SimpleAssignmentExpression(GetValue(3).LocalVariable, GetValue(1).Expression, null, yyloc); + } + } + + private void _464() + { + // default_parameter_list -> default_parameter +#line 2005 "Parser.y" + { + yyval.SimpleAssignmentExpressions = CollectionUtils.MakeList(GetValue(1).SimpleAssignmentExpression); + } + } + + private void _465() + { + // default_parameter_list -> default_parameter_list ',' default_parameter +#line 2009 "Parser.y" + { + GetValue(3).SimpleAssignmentExpressions.Add(GetValue(1).SimpleAssignmentExpression); + yyval.SimpleAssignmentExpressions = GetValue(3).SimpleAssignmentExpressions; + } + } + + private void _468() + { + // array_parameter -> array_parameter_mark parameter +#line 2020 "Parser.y" + { + yyval.LocalVariable = GetValue(1).LocalVariable; + } + } + + private void _469() + { + // array_parameter -> array_parameter_mark +#line 2024 "Parser.y" + { + yyval.LocalVariable = DefineParameter(Symbols.RestArgsLocal, GetLocation(1)); + } + } + + private void _472() + { + // block_parameter -> block_parameter_mark parameter +#line 2034 "Parser.y" + { + yyval.LocalVariable = GetValue(1).LocalVariable; + } + } + + private void _474() + { + // block_parameter_opt -> ',' block_parameter +#line 2041 "Parser.y" + { + yyval.LocalVariable = GetValue(1).LocalVariable; + } + } + + private void _476() + { + // @28 -> +#line 2048 "Parser.y" + { + _tokenizer.SetState(LexicalState.EXPR_BEG); + } + } + + private void _477() + { + // singleton -> '(' @28 expr opt_nl ')' +#line 2052 "Parser.y" + { + yyval.Expression = GetValue(3).Expression; + } + } + + private void _478() + { + // maplets -> maplet +#line 2058 "Parser.y" + { + yyval.Maplets = CollectionUtils.MakeList(GetValue(1).Maplet); + } + } + + private void _479() + { + // maplets -> maplets ',' maplet +#line 2062 "Parser.y" + { + GetValue(3).Maplets.Add(GetValue(1).Maplet); + yyval.Maplets = GetValue(3).Maplets; + } + } + + private void _480() + { + // maplet -> arg Assoc arg +#line 2069 "Parser.y" + { + yyval.Maplet = new Maplet(GetValue(3).Expression, GetValue(1).Expression, yyloc); + } + } + + private void _500() + { + // term -> ';' +#line 2107 "Parser.y" + { StopErrorRecovery(); } + } + + private void _503() + { + // terms -> terms ';' +#line 2112 "Parser.y" + { StopErrorRecovery(); } + } + +#line 2115 "Parser.y" + +} +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Parser/Parser.cs b/merlin/main/languages/ruby/Ruby/Compiler/Parser/Parser.cs new file mode 100644 index 0000000000..98af6ea867 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Parser/Parser.cs @@ -0,0 +1,460 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic; +using System.Text; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Ast; +using IronRuby.Runtime; +using IronRuby.Builtins; + +namespace IronRuby.Compiler { + + // The non-autogenerated part of the Parser class. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + public partial class Parser { + internal sealed class InternalSyntaxError : Exception { + } + + private int _inSingletonMethodDefinition = 0; + private int _inInstanceMethodDefinition = 0; + + private SourceUnitTree _ast; + private List _initializers; // lazy + + private Stack/*!*/ _lexicalScopes = new Stack(); + private SourceUnit _sourceUnit; + private readonly Tokenizer/*!*/ _tokenizer; + private Action _tokenSink; + private int _generatedNameId; + + // current encoding (used for __ENCODING__ pseudo-constant, literal string, symbol, regex encodings): + private Encoding/*!*/ _encoding; + + internal Encoding/*!*/ Encoding { + get { return _encoding; } + } + + private bool InMethod { + get { return _inSingletonMethodDefinition > 0 || _inInstanceMethodDefinition > 0; } + } + + public Tokenizer/*!*/ Tokenizer { + get { return _tokenizer; } + } + + public ErrorSink/*!*/ ErrorSink { + get { return _tokenizer.ErrorSink; } + } + + public Action TokenSink { + get { return _tokenSink; } + set { _tokenSink = value; } + } + + protected override SourceSpan TokenSpan { + get { + return _tokenizer.TokenSpan; + } + } + + protected override TokenValue TokenValue { + get { + return _tokenizer.TokenValue; + } + } + + protected override SourceSpan DefaultTokenSpan { + get { return SourceSpan.None; } + } + + protected override int GetNextToken() { + Tokens token = _tokenizer.GetNextToken(); + if (_tokenSink != null) { + _tokenSink(token, _tokenizer.TokenSpan); + } + return (int)token; + } + + protected override void ReportSyntaxError(string message) { + ErrorSink.Add(_sourceUnit, message, TokenSpan, -1, Severity.FatalError); + throw new InternalSyntaxError(); + } + + internal void ReportSyntaxError(ErrorInfo error) { + ErrorSink.Add(_sourceUnit, error.GetMessage(), TokenSpan, error.Code, Severity.FatalError); + throw new InternalSyntaxError(); + } + + private string/*!*/ GenerateErrorLocalName() { + return "error#" + _generatedNameId++; + } + + private string/*!*/ GenerateErrorConstantName() { + return "Error#" + _generatedNameId++; + } + + public Parser() + : this(ErrorSink.Default) { + } + + // TODO: + public Parser(ErrorSink/*!*/ errorSink) { + _tokenizer = new Tokenizer(this); + } + + public SourceUnitTree Parse(SourceUnit/*!*/ sourceUnit, RubyCompilerOptions/*!*/ options, ErrorSink/*!*/ errorSink) { + Assert.NotNull(sourceUnit, options, errorSink); + + ErrorCounter counter = new ErrorCounter(errorSink); + _tokenizer.ErrorSink = counter; + _tokenizer.Compatibility = options.Compatibility; + + _lexicalScopes.Clear(); + + EnterScope(CreateTopScope(options.LocalNames)); + + using (SourceCodeReader reader = sourceUnit.GetReader()) { + _sourceUnit = sourceUnit; + _tokenizer.Initialize(null, reader, sourceUnit, options.InitialLocation); + + // default encoding when hosted: + _encoding = reader.Encoding ?? RubyEncoding.GetDefaultHostEncoding(options.Compatibility); + + try { + Parse(); + LeaveScope(); + } catch (InternalSyntaxError) { + _ast = null; + _lexicalScopes.Clear(); + } finally { + ScriptCodeParseResult props; + if (counter.AnyError) { + _ast = null; + + if (_tokenizer.UnterminatedToken) { + props = ScriptCodeParseResult.IncompleteToken; + } else if (_tokenizer.IsEndOfFile) { + props = ScriptCodeParseResult.IncompleteStatement; + } else { + props = ScriptCodeParseResult.Invalid; + } + + } else { + props = ScriptCodeParseResult.Complete; + } + + sourceUnit.CodeProperties = props; + } + + return _ast; + } + } + + // Top level scope is created for top level code. + // Variables defined outside of compilation unit (we are compiling eval) are stored in "outer scope", + // to which the top level scope is nested in such case. + private static LexicalScope/*!*/ CreateTopScope(List localVariableNames) { + LexicalScope outer; + if (localVariableNames != null) { + outer = new RuntimeLexicalScope(localVariableNames); + } else { + outer = null; + } + + return new LexicalScope(outer); + } + + private LocalVariable/*!*/ DefineParameter(string/*!*/ name, SourceSpan location) { + // we are in a method: + Debug.Assert(CurrentScope.OuterScope == null); + + LocalVariable variable; + if (CurrentScope.TryGetValue(name, out variable)) { + _tokenizer.ReportError(Errors.DuplicateParameterName); + return variable; + } + + return CurrentScope.AddVariable(name, location); + } + + private Initializer/*!*/ AddInitializer(Initializer/*!*/ initializer) { + if (_initializers == null) { + _initializers = new List(); + } + + _initializers.Add(initializer); + + return initializer; + } + + protected override SourceSpan MergeLocations(SourceSpan start, SourceSpan end) { + Debug.Assert(start.IsValid && end.IsValid); + + return new SourceSpan(start.Start, end.End); + } + + private LexicalScope/*!*/ EnterScope(LexicalScope/*!*/ scope) { + Assert.NotNull(scope); + _lexicalScopes.Push(scope); + return scope; + } + + private LexicalScope EnterNestedScope() { + LexicalScope result = new LexicalScope(CurrentScope); + _lexicalScopes.Push(result); + return result; + } + + private LexicalScope EnterTopScope() { + LexicalScope result = new LexicalScope(null); + _lexicalScopes.Push(result); + return result; + } + + private LexicalScope LeaveScope() { + return _lexicalScopes.Pop(); + } + + public LexicalScope CurrentScope { + get { + Debug.Assert(_lexicalScopes.Count > 0); + return _lexicalScopes.Peek(); + } + } + + // __FILE__ + internal Expression/*!*/ GetCurrentFileExpression(SourceSpan location) { + if (_sourceUnit.Path == null) { + return new StringLiteral("(eval)", StringLiteralEncoding.Ascii, location); + } else { + var encoding = _sourceUnit.Path.IsAscii() ? StringLiteralEncoding.Ascii : StringLiteralEncoding.Default; + return new StringLiteral(_sourceUnit.Path, encoding, location); + } + } + + internal LeftValue/*!*/ CannotAssignError(string/*!*/ constantName, SourceSpan location) { + Tokenizer.ReportError(Errors.CannotAssignTo, constantName); + return CurrentScope.ResolveOrAddVariable(GenerateErrorLocalName(), location); + } + + private void MatchReferenceReadOnlyError(RegexMatchReference/*!*/ matchRef) { + Tokenizer.ReportError(Errors.MatchGroupReferenceReadOnly, matchRef.VariableName); + } + + private AliasStatement/*!*/ MakeGlobalAlias(string/*!*/ newVar, string/*!*/ existingVar, SourceSpan location) { + return new AliasStatement(false, newVar, existingVar, location); + } + + private Expression/*!*/ MakeGlobalAlias(string/*!*/ newVar, RegexMatchReference/*!*/ existingVar, SourceSpan location) { + if (existingVar.CanAlias) { + return new AliasStatement(false, newVar, existingVar.VariableName, location); + } else { + _tokenizer.ReportError(Errors.CannotAliasGroupMatchVariable); + return new ErrorExpression(location); + } + } + + private AliasStatement/*!*/ MakeGlobalAlias(RegexMatchReference/*!*/ newVar, string/*!*/ existingVar, SourceSpan location) { + return new AliasStatement(false, newVar.VariableName, existingVar, location); + } + + private Expression/*!*/ MakeGlobalAlias(RegexMatchReference/*!*/ newVar, RegexMatchReference/*!*/ existingVar, SourceSpan location) { + if (existingVar.CanAlias) { + return new AliasStatement(false, newVar.VariableName, existingVar.VariableName, location); + } else { + _tokenizer.ReportError(Errors.CannotAliasGroupMatchVariable); + return new ErrorExpression(location); + } + } + + private List/*!*/ MakeListAddOpt(T item) { + List result = new List(); + if (item != null) { + result.Add(item); + } + return result; + } + + // BlockExpression behaves like an expression, so we don't need to create one that comprises of a single expression: + private Expression/*!*/ MakeBlockExpression(List/*!*/ statements, SourceSpan location) { + if (statements.Count == 0) { + return BlockExpression.Empty; + } else if (statements.Count == 1) { + return statements[0]; + } else { + return new BlockExpression(statements, location); + } + } + + private IfExpression/*!*/ MakeIfExpression(Expression/*!*/ condition, List/*!*/ body, List/*!*/ elseIfClauses, SourceSpan location) { + // last else-if/else clause is the first one in the list: + elseIfClauses.Reverse(); + return new IfExpression(condition, body, elseIfClauses, location); + } + + private ArrayConstructor/*!*/ MakeVerbatimWords(List/*!*/ words, SourceSpan wordsLocation, SourceSpan location) { + Debug.Assert(CollectionUtils.TrueForAll(words, (word) => word is StringLiteral), "all words are string literals"); + + return new ArrayConstructor(new Arguments(words, null, null, wordsLocation), location); + } + + private List/*!*/ CheckHashExpressions(List/*!*/ expressions, SourceSpan location) { + Assert.NotNull(expressions); + + if (expressions.Count % 2 != 0) { + ErrorSink.Add(_sourceUnit, "odd number list for Hash", location, -1, Severity.Error); + expressions.Add(Literal.Nil(SourceSpan.None)); + } + + return expressions; + } + + private Arguments/*!*/ RequireNoBlockArg(TokenValue arguments) { + if (arguments.Block != null) { + ErrorSink.Add(_sourceUnit, "block argument should not be given", arguments.Block.Location, -1, Severity.Error); + arguments.Block = null; + } + + return arguments.Arguments; + } + + private static MethodCall/*!*/ MakeMethodCall(Expression target, string/*!*/ methodName, TokenValue args, SourceSpan location) { + return new MethodCall(target, methodName, args.Arguments, args.Block, location); + } + + private static MethodCall/*!*/ MakeMethodCall(Expression target, string/*!*/ methodName, TokenValue args, Block block, SourceSpan location) { + Debug.Assert(args.Block == null); + return new MethodCall(target, methodName, args.Arguments, block, location); + } + + private static Expression/*!*/ MakeMatch(Expression/*!*/ left, Expression/*!*/ right, SourceSpan location) { + var regex = left as RegularExpression; + if (regex != null) { + return new MatchExpression(regex, right, location); + } else { + return new MethodCall(left, Symbols.Match, new Arguments(right), location); + } + } + + private static SuperCall/*!*/ MakeSuperCall(TokenValue args, SourceSpan location) { + return new SuperCall(args.Arguments, args.Block, location); + } + + + // foo + // foo {} + private static TokenValue NoArguments(Block block) { + return new TokenValue(null, block); + } + + // foo() + private static TokenValue MakeArguments() { + return new TokenValue(Arguments.Empty, null); + } + + // foo() + // foo() {} + // foo(&p) + // foo &p + private static TokenValue MakeArguments(Block block) { + return new TokenValue(Arguments.Empty, block); + } + + // foo(expr) + private static TokenValue MakeArguments(Expression/*!*/ expression) { + return new TokenValue(new Arguments(expression), null); + } + + // foo(expr) + // foo(expr) {} + // foo(expr, &p) + private static TokenValue MakeArguments(Expression/*!*/ expression, Block block) { + return new TokenValue(new Arguments(expression), block); + } + + // foo(expr1, ..., exprN, k1 => v1, ..., kN => vN, *a) + // foo(expr1, ..., exprN, k1 => v1, ..., kN => vN, *a) {} + // foo(expr1, ..., exprN, k1 => v1, ..., kN => vN, *a, &p) + private static TokenValue MakeArguments(List arguments, List maplets, Expression array, Block block, SourceSpan location) { + return new TokenValue( + new Arguments(arguments, maplets, array, location), + block + ); + } + + private static Expression/*!*/ MakeLoopStatement(Expression/*!*/ statement, Expression/*!*/ condition, bool isWhileLoop, SourceSpan location) { + return new WhileLoopExpression(condition, isWhileLoop, statement is Body, CollectionUtils.MakeList(statement), location); + } + + public static string/*!*/ TerminalToString(int terminal) { + Debug.Assert(terminal >= 0); + if (((Tokens)terminal).ToString() != terminal.ToString()) { + return IronRuby.Runtime.RubyUtils.MangleName(((Tokens)terminal).ToString()).ToUpper(); + } else { + return CharToString((char)terminal); + } + } + + public static StringLiteral/*!*/ MakeStringLiteral(TokenValue token, SourceSpan location) { + return new StringLiteral(token.String, token.StringLiteralEncoding, location); + } + + private StringConstructor/*!*/ MakeSymbolConstructor(List/*!*/ content, SourceSpan location) { + if (content.Count == 0) { + _tokenizer.ReportError(Errors.EmptySymbolLiteral); + } + return new StringConstructor(content, StringKind.Immutable, location); + } + + // TODO: utils + + public static string/*!*/ CharToString(char ch) { + switch (ch) { + case '\a': return @"'\a'"; + case '\b': return @"'\b'"; + case '\f': return @"'\f'"; + case '\n': return @"'\n'"; + case '\r': return @"'\r'"; + case '\t': return @"'\t'"; + case '\v': return @"'\v'"; + case '\0': return @"'\0'"; + default: return String.Concat("'", ch.ToString(), "'"); + } + } + + public static string/*!*/ EscapeString(string str) { + return (str == null) ? String.Empty : + new StringBuilder(str). + Replace(@"\", @"\\"). + Replace(@"""", @"\"""). + Replace("\a", @"\a"). + Replace("\b", @"\b"). + Replace("\f", @"\f"). + Replace("\n", @"\n"). + Replace("\r", @"\r"). + Replace("\t", @"\t"). + Replace("\v", @"\v"). + Replace("\0", @"\0").ToString(); + } + } +} + + diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Parser/Parser.y b/merlin/main/languages/ruby/Ruby/Compiler/Parser/Parser.y new file mode 100644 index 0000000000..c31184de24 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Parser/Parser.y @@ -0,0 +1,2115 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Ast; + +%% + +%namespace IronRuby.Compiler + +%union { } + +%SymbolLocationType SourceSpan +%SymbolValueType TokenValue + +%partial +%visibility public + +%token SINGLE_LINE_COMMENT MULTI_LINE_COMMENT WHITESPACE INVALID_CHARACTER END_OF_LINE +%token WORD_SEPARATOR +%token CLASS MODULE DEF UNDEF BEGIN RESCUE ENSURE END IF UNLESS THEN ELSIF ELSE +%token CASE WHEN WHILE UNTIL FOR BREAK NEXT REDO RETRY IN DO LOOP_DO BLOCK_DO +%token RETURN YIELD SUPER SELF NIL TRUE FALSE AND OR NOT IF_MOD UNLESS_MOD +%token WHILE_MOD UNTIL_MOD RESCUE_MOD ALIAS DEFINED UPPERCASE_BEGIN UPPERCASE_END LINE FILE ENCODING +%token UPLUS UMINUS POW CMP EQ EQQ NEQ GEQ LEQ BITWISE_AND BITWISE_OR MATCH NMATCH +%token DOT2 DOT3 AREF ASET LSHFT RSHFT SEPARATING_DOUBLE_COLON LEADING_DOUBLE_COLON ASSOC LEFT_PAREN STRING_END +%token LPAREN_ARG LBRACK LBRACE LBRACE_ARG STAR AMPERSAND + +%token IDENTIFIER FUNCTION_IDENTIFIER GLOBAL_VARIABLE INSTANCE_VARIABLE CONSTANT_IDENTIFIER CLASS_VARIABLE ASSIGNMENT +%token INTEGER +%token BIG_INTEGER +%token FLOAT +%token STRING_CONTENT +%token MATCH_REFERENCE +%token REGEXP_END +%token STRING_EMBEDDED_VARIABLE_BEGIN STRING_EMBEDDED_CODE_BEGIN +%token STRING_BEG REGEXP_BEG SHELL_STRING_BEGIN WORDS_BEG VERBATIM_WORDS_BEGIN SYMBEG + +%type program + +%type stmt +%type stmts compstmt ensure_opt +%type jump_statement jump_statement_with_parameters jump_statement_parameterless +%type alias_statement +%type conditional_statement + +%type primary expr expression_statement superclass var_ref singleton case_expression +%type arg +%type args +%type block_expression // Expression, BlockExpression or Body +%type declaration_expression // DeclarationExpression +%type body +%type + +%type method_call block_call command_call block_command command + +%type else_opt +%type if_tail +%type undef_list +%type block_reference opt_block_reference +%type cmd_brace_block brace_block do_block + +%type array_key +%type when_args // optional Expression; optional Expressions +%type paren_args open_args closed_args command_args command_args_content // non-null Arguments; optional Block +%type opt_paren_args // optional Arguments; optional Block + +%type compound_rhs +%type qualified_module_name + +%type rescue_clauses rescue_clauses_opt +%type rescue_clause + +%type when_clauses +%type when_clause + +%type maplets +%type maplet + +%type parameters_declaration parameters +%type parameter_list +%type parameter array_parameter block_parameter block_parameter_opt +%type default_parameter_list +%type default_parameter + +%type string_embedded_variable // string embedded variable +%type string_content // string piece - literal, string embedded code/variable +%type string_contents // list of string pieces +%type string // quoted string constructor taking list of string pieces "#{foo}bar#{baz}" +%type string_concatenation // string constructor taking a list of quoted strings "foo" 'bar' "baz" + +%type shell_string // shell string constructor taking list of string pieces `#{foo}bar#{baz}` + +%type word // concatenation of string pieces +%type word_list verbatim_word_list // list of words separated by space +%type words verbatim_words // array constructor taking a list of words + +%type regexp +%type numeric_literal +%type immutable_string +%type match_reference + +%type operation variable sym operation2 operation3 module_name op method_name symbol method_name_or_symbol + +%type compound_lhs +%type compound_lhs_head +%type compound_lhs_item compound_lhs_tail +%type compound_lhs_node + +%type var_lhs +%type lhs +%type block_parameters block_parameters_opt +%type exc_var + +%nonassoc LOWEST +%nonassoc LBRACE_ARG +%nonassoc IF_MOD UNLESS_MOD WHILE_MOD UNTIL_MOD +%left OR AND +%right NOT +%nonassoc DEFINED +%right '=' ASSIGNMENT +%left RESCUE_MOD +%right '?' ':' +%nonassoc DOT2 DOT3 +%left BITWISE_OR +%left BITWISE_AND +%nonassoc CMP EQ EQQ NEQ MATCH NMATCH +%left '>' GEQ '<' LEQ +%left '|' '^' +%left '&' +%left LSHFT RSHFT +%left '+' '-' +%left '*' '/' '%' +%right UMINUS_NUM UMINUS +%right POW +%right '!' '~' UPLUS + +%token LAST_TOKEN + + +%% + + +program : { + _tokenizer.SetState(LexicalState.EXPR_BEG); + } + compstmt + { + _ast = new SourceUnitTree(CurrentScope, $2, _initializers, _encoding, _tokenizer.DataOffset); + } + ; + +compstmt: stmts opt_terms + { + $$ = $1; + } + ; + +stmts : /* empty */ + { + $$ = new List(); + } + | stmt + { + $$ = CollectionUtils.MakeList($1); + } + | stmts terms stmt + { + $1.Add($3); + $$ = $1; + } + | ERROR stmt + { + $$ = CollectionUtils.MakeList($2); + } + ; + +stmt: alias_statement + | UNDEF undef_list + { + $$ = new UndefineStatement($2, @$); + } + | UPPERCASE_BEGIN + { + if (InMethod) { + _tokenizer.ReportError(Errors.FileInitializerInMethod); + } + + EnterTopScope(); + } + '{' compstmt '}' + { + $$ = AddInitializer(new Initializer(CurrentScope, $4, @$)); + LeaveScope(); + } + | UPPERCASE_END + { + if (InMethod) { + _tokenizer.ReportError(Errors.FileFinalizerInMethod); + } + + EnterTopScope(); + } + '{' compstmt '}' + { + $$ = new Finalizer(CurrentScope, $4, @$); + LeaveScope(); + } + | match_reference ASSIGNMENT command_call + { + MatchReferenceReadOnlyError($1); + $$ = new ErrorExpression(@$); + } + | jump_statement + { + $$ = $1; + } + | conditional_statement + { + $$ = $1; + } + | expression_statement + { + $$ = $1; + } +; + +alias_statement: + ALIAS method_name_or_symbol + { + _tokenizer.SetState(LexicalState.EXPR_FNAME); + } + method_name_or_symbol + { + $$ = new AliasStatement(true, $2, $4, @$); + } + | ALIAS GLOBAL_VARIABLE GLOBAL_VARIABLE + { + $$ = MakeGlobalAlias($2, $3, @$); + } + | ALIAS GLOBAL_VARIABLE match_reference + { + $$ = MakeGlobalAlias($2, $3, @$); + } +; + +jump_statement: + jump_statement_with_parameters + { + $$ = $1; + } + | jump_statement_parameterless + { + $$ = $1; + } +; + +jump_statement_with_parameters: + RETURN open_args + { + $$ = new ReturnStatement(RequireNoBlockArg($2), @$); + } + | BREAK open_args + { + $$ = new BreakStatement(RequireNoBlockArg($2), @$); + } + | NEXT open_args + { + $$ = new NextStatement(RequireNoBlockArg($2), @$); + } +; + +jump_statement_parameterless: + RETURN + { + $$ = new ReturnStatement(null, @$); + } + | BREAK + { + $$ = new BreakStatement(null, @$); + } + | NEXT + { + $$ = new NextStatement(null, @$); + } + | REDO + { + $$ = new RedoStatement(@$); + } + | RETRY + { + $$ = new RetryStatement(@$); + } +; + +expression_statement: + expr + { + $$ = $1; + } + | lhs '=' command_call + { + $$ = new SimpleAssignmentExpression($1, $3, null, @$); + } + | compound_lhs '=' command_call + { + $$ = new ParallelAssignmentExpression($1, new CompoundRightValue(CollectionUtils.MakeList($3), null), @$); + } + | var_lhs ASSIGNMENT command_call + { + $$ = new SimpleAssignmentExpression($1, $3, $2, @$); + } + | primary '[' array_key ']' ASSIGNMENT command_call + { + $$ = new SimpleAssignmentExpression(new ArrayItemAccess($1, $3, @2), $6, $5, @$); + } + | primary '.' IDENTIFIER ASSIGNMENT command_call + { + $$ = new MemberAssignmentExpression($1, $3, $4, $5, @$); + } + | primary '.' CONSTANT_IDENTIFIER ASSIGNMENT command_call + { + $$ = new MemberAssignmentExpression($1, $3, $4, $5, @$); + } + | primary SEPARATING_DOUBLE_COLON IDENTIFIER ASSIGNMENT command_call + { + $$ = new MemberAssignmentExpression($1, $3, $4, $5, @$); + } + | lhs '=' compound_rhs + { + $$ = new ParallelAssignmentExpression(new CompoundLeftValue(CollectionUtils.MakeList($1), null, @1), $3, @$); + } + | compound_lhs '=' arg + { + $$ = new ParallelAssignmentExpression($1, new CompoundRightValue(CollectionUtils.MakeList($3), null), @$); + } + | compound_lhs '=' compound_rhs + { + $$ = new ParallelAssignmentExpression($1, $3, @$); + } + | arg '?' jump_statement_parameterless ':' arg + { + $$ = new ConditionalJumpExpression($1.ToCondition(), $3, false, $5, @$); + } + | arg '?' arg ':' jump_statement_parameterless + { + $$ = new ConditionalJumpExpression($1.ToCondition(), $5, true, $3, @$); + } +; + +conditional_statement: + stmt IF_MOD expr + { + $$ = new ConditionalStatement($3.ToCondition(), false, $1, null, @$); + } + | stmt UNLESS_MOD expr + { + $$ = new ConditionalStatement($3.ToCondition(), true, $1, null, @$); + } + | stmt WHILE_MOD expr + { + $$ = MakeLoopStatement($1, $3.ToCondition(), true, @$); + } + | stmt UNTIL_MOD expr + { + $$ = MakeLoopStatement($1, $3.ToCondition(), false, @$); + } + | stmt RESCUE_MOD stmt + { + $$ = new RescueExpression($1, $3, MergeLocations(@2, @3), @$); + } + | arg '?' jump_statement_parameterless ':' jump_statement_parameterless + { + $$ = new ConditionalStatement($1.ToCondition(), false, $3, $5, @$); + } +; + +compound_rhs: + args ',' arg + { + $1.Add($3); + $$ = new CompoundRightValue($1, null); + } + | args ',' STAR arg + { + $$ = new CompoundRightValue($1, $4); + } + | STAR arg + { + $$ = new CompoundRightValue(Expression.EmptyList, $2); + } +; + +expr: + command_call + | expr AND expr + { + $$ = new AndExpression($1, $3, @$); + } + | expr OR expr + { + $$ = new OrExpression($1, $3, @$); + } + | expr AND jump_statement + { + $$ = new ConditionalJumpExpression($1, $3, false, null, @$); + } + | expr OR jump_statement + { + $$ = new ConditionalJumpExpression($1, $3, true, null, @$); + } + | NOT expr + { + // TODO: warning: string literal in condition + $$ = new NotExpression($2, @$); + } + | '!' command_call + { + // TODO: warning: string literal in condition + $$ = new NotExpression($2, @$); + } + | arg +; + +command_call: + command + { + $$ = $1; + } + | block_command + { + $$ = $1; + } +; + +block_command: + block_call + { + $$ = $1; + } + | block_call '.' operation2 command_args + { + $$ = MakeMethodCall($1, $3, $4, @$); + } + | block_call SEPARATING_DOUBLE_COLON operation2 command_args + { + $$ = MakeMethodCall($1, $3, $4, @$); + } +; + +cmd_brace_block: + LBRACE_ARG + { + EnterNestedScope(); + } + block_parameters_opt compstmt '}' + { + $$ = new BlockDefinition(CurrentScope, $3, $4, @$); + LeaveScope(); + } +; + +command: operation command_args %prec LOWEST + { + $$ = MakeMethodCall(null, $1, $2, @$); + } + | operation command_args cmd_brace_block + { + $$ = MakeMethodCall(null, $1, $2, $3, @$); + } + | primary '.' operation2 command_args %prec LOWEST + { + $$ = MakeMethodCall($1, $3, $4, @$); + } + | primary '.' operation2 command_args cmd_brace_block + { + $$ = MakeMethodCall($1, $3, $4, $5, @$); + } + | primary SEPARATING_DOUBLE_COLON operation2 command_args %prec LOWEST + { + $$ = MakeMethodCall($1, $3, $4, @$); + } + | primary SEPARATING_DOUBLE_COLON operation2 command_args cmd_brace_block + { + $$ = MakeMethodCall($1, $3, $4, $5, @$); + } + | SUPER command_args + { + $$ = MakeSuperCall($2, @$); + } + | YIELD command_args + { + $$ = new YieldCall(RequireNoBlockArg($2), @$); + } + ; + +compound_lhs: + compound_lhs_head compound_lhs_item + { + $1.Add($2); + $$ = new CompoundLeftValue($1, null, @$); + } + | compound_lhs_head + { + $1.Add(Placeholder.Singleton); + $$ = new CompoundLeftValue($1, null, @$); + } + | LEFT_PAREN compound_lhs ')' + { + $$ = new CompoundLeftValue(CollectionUtils.MakeList($2), null, @$); + } + | compound_lhs_head compound_lhs_tail + { + $$ = new CompoundLeftValue($1, $2, @$); + } + | compound_lhs_tail + { + $$ = new CompoundLeftValue(LeftValue.EmptyList, $1, @$); + } +; + +compound_lhs_tail: + STAR compound_lhs_node + { + $$ = $2; + } + | STAR + { + $$ = Placeholder.Singleton; + } +; + +compound_lhs_head: + compound_lhs_head compound_lhs_item ',' + { + $1.Add($2); + $$ = $1; + } + | compound_lhs_item ',' + { + $$ = CollectionUtils.MakeList($1); + } +; + +compound_lhs_item: + compound_lhs_node + { + $$ = $1; + } + | LEFT_PAREN compound_lhs ')' + { + $$ = $2; + } +; + +compound_lhs_node: + variable + { + $$ = VariableFactory.MakeLeftValue($1, this, $1, @$); + } + | primary '[' array_key ']' + { + $$ = new ArrayItemAccess($1, $3, @$); + } + | primary '.' IDENTIFIER + { + $$ = new AttributeAccess($1, $3, @$); + } + | primary SEPARATING_DOUBLE_COLON IDENTIFIER + { + $$ = new AttributeAccess($1, $3, @$); + } + | primary '.' CONSTANT_IDENTIFIER + { + $$ = new AttributeAccess($1, $3, @$); + } + | primary SEPARATING_DOUBLE_COLON CONSTANT_IDENTIFIER + { + $$ = new ConstantVariable($1, $3, @$); + } + | LEADING_DOUBLE_COLON CONSTANT_IDENTIFIER + { + $$ = new ConstantVariable(null, $2, @$); + } + | match_reference + { + MatchReferenceReadOnlyError($1); + $$ = new GlobalVariable(Symbols.Error, @$); + } +; + + +lhs: variable + { + $$ = VariableFactory.MakeLeftValue($1, this, $1, @$); + } + | primary '[' array_key ']' + { + $$ = new ArrayItemAccess($1, $3, @$); + } + | primary '.' IDENTIFIER + { + $$ = new AttributeAccess($1, $3, @$); + } + | primary SEPARATING_DOUBLE_COLON IDENTIFIER + { + $$ = new AttributeAccess($1, $3, @$); + } + | primary '.' CONSTANT_IDENTIFIER + { + $$ = new AttributeAccess($1, $3, @$); + } + | primary SEPARATING_DOUBLE_COLON CONSTANT_IDENTIFIER + { + $$ = new ConstantVariable($1, $3, @$); + } + | LEADING_DOUBLE_COLON CONSTANT_IDENTIFIER + { + $$ = new ConstantVariable(null, $2, @$); + } + | match_reference + { + MatchReferenceReadOnlyError($1); + $$ = new GlobalVariable(Symbols.Error, @$); + } + ; + +module_name: + CONSTANT_IDENTIFIER + { + $$ = $1; + } + | IDENTIFIER + { + _tokenizer.ReportError(Errors.ModuleNameNotConstant); + $$ = $1; + } +; + +qualified_module_name: + LEADING_DOUBLE_COLON module_name + { + $$ = new ConstantVariable(null, $2, @$); + } + | module_name + { + $$ = new ConstantVariable($1, @$); + } + | primary SEPARATING_DOUBLE_COLON module_name + { + $$ = new ConstantVariable($1, $3, @$); + } +; + +method_name: + IDENTIFIER + { + $$ = $1; + } + | CONSTANT_IDENTIFIER + { + $$ = $1; + } + | FUNCTION_IDENTIFIER + { + $$ = $1; + } + | op + { + _tokenizer.SetState(LexicalState.EXPR_END); + $$ = $1; + } + | reswords + { + _tokenizer.SetState(LexicalState.EXPR_END); + $$ = $1; + } +; + +method_name_or_symbol: + method_name + { + $$ = $1; + } + | symbol + { + $$ = $1; + } +; + +undef_list: + method_name_or_symbol + { + $$ = CollectionUtils.MakeList(new Identifier($1, @1)); + } + | undef_list ',' + { + _tokenizer.SetState(LexicalState.EXPR_FNAME); + } + method_name_or_symbol + { + $1.Add(new Identifier($4, @4)); + $$ = $1; + } +; + +op: + '|' { $$ = Symbols.BitwiseOr; } + | '^' { $$ = Symbols.Xor; } + | '&' { $$ = Symbols.BitwiseAnd; } + | CMP { $$ = Symbols.Comparison; } + | EQ { $$ = Symbols.Equal; } + | EQQ { $$ = Symbols.StrictEqual; } + | MATCH { $$ = Symbols.Match; } + | '>' { $$ = Symbols.GreaterThan; } + | GEQ { $$ = Symbols.GreaterEqual; } + | '<' { $$ = Symbols.LessThan; } + | LEQ { $$ = Symbols.LessEqual; } + | LSHFT { $$ = Symbols.LeftShift; } + | RSHFT { $$ = Symbols.RightShift; } + | '+' { $$ = Symbols.Plus; } + | '-' { $$ = Symbols.Minus; } + | '*' { $$ = Symbols.Multiply; } + | STAR { $$ = Symbols.Multiply; } + | '/' { $$ = Symbols.Divide; } + | '%' { $$ = Symbols.Mod; } + | POW { $$ = Symbols.Power; } + | '~' { $$ = Symbols.BitwiseNot; } + | UPLUS { $$ = Symbols.UnaryPlus; } + | UMINUS { $$ = Symbols.UnaryMinus; } + | AREF { $$ = Symbols.ArrayItemRead; } + | ASET { $$ = Symbols.ArrayItemWrite; } + | '`' { $$ = Symbols.Backtick; } +; + +reswords: LINE | FILE | ENCODING | UPPERCASE_BEGIN | UPPERCASE_END + | ALIAS | AND | BEGIN | BREAK | CASE | CLASS | DEF + | DEFINED | DO | ELSE | ELSIF | END | ENSURE | FALSE + | FOR | IN | MODULE | NEXT | NIL | NOT + | OR | REDO | RESCUE | RETRY | RETURN | SELF | SUPER + | THEN | TRUE | UNDEF | WHEN | YIELD + | IF_MOD | UNLESS_MOD | WHILE_MOD | UNTIL_MOD | RESCUE_MOD + ; + +arg: + lhs '=' arg + { + $$ = new SimpleAssignmentExpression($1, $3, null, @$); + } + | lhs '=' arg RESCUE_MOD arg + { + $$ = new SimpleAssignmentExpression($1, new RescueExpression($3, $5, MergeLocations(@4, @5), MergeLocations(@3, @5)), null, @$); + } + | lhs '=' arg RESCUE_MOD jump_statement_parameterless + { + $$ = new SimpleAssignmentExpression($1, new RescueExpression($3, $5, MergeLocations(@4, @5), MergeLocations(@3, @5)), null, @$); + } + | var_lhs ASSIGNMENT arg + { + $$ = new SimpleAssignmentExpression($1, $3, $2, @$); + } + | primary '[' array_key ']' ASSIGNMENT arg + { + $$ = new SimpleAssignmentExpression(new ArrayItemAccess($1, $3, @2), $6, $5, @$); + } + | primary '.' IDENTIFIER ASSIGNMENT arg + { + $$ = new MemberAssignmentExpression($1, $3, $4, $5, @$); + } + | primary '.' CONSTANT_IDENTIFIER ASSIGNMENT arg + { + $$ = new MemberAssignmentExpression($1, $3, $4, $5, @$); + } + | primary SEPARATING_DOUBLE_COLON IDENTIFIER ASSIGNMENT arg + { + $$ = new MemberAssignmentExpression($1, $3, $4, $5, @$); + } + | primary SEPARATING_DOUBLE_COLON CONSTANT_IDENTIFIER ASSIGNMENT arg + { + _tokenizer.ReportError(Errors.ConstantReassigned); + $$ = new ErrorExpression(@$); + } + | LEADING_DOUBLE_COLON CONSTANT_IDENTIFIER ASSIGNMENT arg + { + _tokenizer.ReportError(Errors.ConstantReassigned); + $$ = new ErrorExpression(@$); + } + | match_reference ASSIGNMENT arg + { + MatchReferenceReadOnlyError($1); + $$ = new ErrorExpression(@$); + } + | arg '+' arg + { + $$ = new MethodCall($1, Symbols.Plus, new Arguments($3), @2); + } + | arg '-' arg + { + $$ = new MethodCall($1, Symbols.Minus, new Arguments($3), @2); + } + | arg '*' arg + { + $$ = new MethodCall($1, Symbols.Multiply, new Arguments($3), @2); + } + | arg '/' arg + { + $$ = new MethodCall($1, Symbols.Divide, new Arguments($3), @2); + } + | arg '%' arg + { + $$ = new MethodCall($1, Symbols.Mod, new Arguments($3), @2); + } + | arg POW arg + { + $$ = new MethodCall($1, Symbols.Power, new Arguments($3), @2); + } + | UMINUS_NUM INTEGER POW arg + { + // ** has precedence over unary minus, hence -number**arg is equivalent to -(number**arg) + $$ = new MethodCall(new MethodCall(Literal.Integer($2, @2), Symbols.Power, new Arguments($4), @3), Symbols.UnaryMinus, Arguments.Empty, @1); + } + | UMINUS_NUM BIG_INTEGER POW arg + { + $$ = new MethodCall(new MethodCall(Literal.BigInteger($2, @2), Symbols.Power, new Arguments($4), @3), Symbols.UnaryMinus, Arguments.Empty, @1); + } + | UMINUS_NUM FLOAT POW arg + { + $$ = new MethodCall(new MethodCall(Literal.Double($2, @2), Symbols.Power, new Arguments($4), @3), Symbols.UnaryMinus, Arguments.Empty, @1); + } + | UPLUS arg + { + $$ = new MethodCall($2, Symbols.UnaryPlus, null, @1); + } + | UMINUS arg + { + $$ = new MethodCall($2, Symbols.UnaryMinus, null, @1); + } + | arg '|' arg + { + $$ = new MethodCall($1, Symbols.BitwiseOr, new Arguments($3), @2); + } + | arg '^' arg + { + $$ = new MethodCall($1, Symbols.Xor, new Arguments($3), @2); + } + | arg '&' arg + { + $$ = new MethodCall($1, Symbols.BitwiseAnd, new Arguments($3), @2); + } + | arg CMP arg + { + $$ = new MethodCall($1, Symbols.Comparison, new Arguments($3), @2); + } + | arg '>' arg + { + $$ = new MethodCall($1, Symbols.GreaterThan, new Arguments($3), @2); + } + | arg GEQ arg + { + $$ = new MethodCall($1, Symbols.GreaterEqual, new Arguments($3), @2); + } + | arg '<' arg + { + $$ = new MethodCall($1, Symbols.LessThan, new Arguments($3), @2); + } + | arg LEQ arg + { + $$ = new MethodCall($1, Symbols.LessEqual, new Arguments($3), @2); + } + | arg EQ arg + { + $$ = new MethodCall($1, Symbols.Equal, new Arguments($3), @2); + } + | arg EQQ arg + { + $$ = new MethodCall($1, Symbols.StrictEqual, new Arguments($3), @2); + } + | arg NEQ arg + { + $$ = new NotExpression(new MethodCall($1, Symbols.Equal, new Arguments($3), @$), @2); + } + | arg MATCH arg + { + $$ = MakeMatch($1, $3, @2); + } + | arg NMATCH arg + { + $$ = new NotExpression(MakeMatch($1, $3, @2), @$); + } + | '!' arg + { + // TODO: warning: string literal in condition + $$ = new NotExpression($2, @$); + } + | '~' arg + { + $$ = new MethodCall($2, Symbols.BitwiseNot, Arguments.Empty, @1); + } + | arg LSHFT arg + { + $$ = new MethodCall($1, Symbols.LeftShift, new Arguments($3), @2); + } + | arg RSHFT arg + { + $$ = new MethodCall($1, Symbols.RightShift, new Arguments($3), @2); + } + | arg BITWISE_AND arg + { + $$ = new AndExpression($1, $3, @2); + } + | arg BITWISE_OR arg + { + $$ = new OrExpression($1, $3, @2); + } + | arg BITWISE_AND jump_statement_parameterless + { + $$ = new ConditionalJumpExpression($1, $3, false, null, @2); + } + | arg BITWISE_OR jump_statement_parameterless + { + $$ = new ConditionalJumpExpression($1, $3, true, null, @2); + } + | arg DOT2 arg + { + $$ = new RangeExpression($1, $3, false, @2); + } + | arg DOT3 arg + { + $$ = new RangeExpression($1, $3, true, @2); + } + | DEFINED opt_nl arg + { + $$ = new IsDefinedExpression($3, @$); + } + | arg '?' arg ':' arg + { + $$ = new ConditionalExpression($1.ToCondition(), $3, $5, @$); + } + | primary + { + $$ = $1; + } + ; + +array_key : /* empty */ + { + $$ = Arguments.Empty; + } + | command opt_nl + { + _tokenizer.ReportWarning(Errors.ParenthesizeArguments); + $$ = new Arguments($1); + } + | args trailer + { + $$ = new Arguments($1, null, null, @1); + } + | args ',' STAR arg opt_nl + { + $$ = new Arguments($1, null, $4, MergeLocations(@1, @4)); + } + | maplets trailer + { + $$ = new Arguments(null, $1, null, @1); + } + | STAR arg opt_nl + { + $$ = new Arguments(null, null, $2, MergeLocations(@1, @2)); + } + ; + +paren_args: + '(' /* empty */ ')' + { + $$ = MakeArguments(); + } + | '(' open_args opt_nl ')' + { + Debug.Assert($2.Arguments != null); + $$ = $2; + } + | '(' block_call opt_nl ')' + { + _tokenizer.ReportWarning(Errors.ParenthesizeArguments); + $$ = MakeArguments($2); + } + | '(' args ',' block_call opt_nl ')' + { + _tokenizer.ReportWarning(Errors.ParenthesizeArguments); + $2.Add($4); + $$ = MakeArguments($2, null, null, null, @$); + } +; + +opt_paren_args: + /* empty */ + { + $$ = NoArguments(null); + } + | paren_args + { + $$ = $1; + } +; + +open_args: + args opt_block_reference + { + $$ = MakeArguments($1, null, null, $2, @$); + } + | args ',' STAR arg opt_block_reference + { + $$ = MakeArguments($1, null, $4, $5, @$); + } + | maplets opt_block_reference + { + $$ = MakeArguments(null, $1, null, $2, @$); + } + | maplets ',' STAR arg opt_block_reference + { + $$ = MakeArguments(null, $1, $4, $5, @$); + } + | args ',' maplets opt_block_reference + { + $$ = MakeArguments($1, $3, null, $4, @$); + } + | args ',' maplets ',' STAR arg opt_block_reference + { + $$ = MakeArguments($1, $3, $6, $7, @$); + } + | STAR arg opt_block_reference + { + $$ = MakeArguments(null, null, $2, $3, @$); + } + | block_reference + { + $$ = MakeArguments($1); + } + | command + { + _tokenizer.ReportWarning(Errors.ParenthesizeArguments); + $$ = MakeArguments($1); + } +; + +closed_args: + arg ',' args opt_block_reference + { + $3.Insert(0, $1); + $$ = MakeArguments($3, null, null, $4, @$); + } + | arg ',' block_reference + { + $$ = MakeArguments($1, $3); + } + | arg ',' STAR arg opt_block_reference + { + $$ = MakeArguments(CollectionUtils.MakeList($1), null, $4, $5, @$); + } + | arg ',' args ',' STAR arg opt_block_reference + { + $3.Insert(0, $1); + $$ = MakeArguments($3, null, $6, $7, @$); + } + | maplets opt_block_reference + { + $$ = MakeArguments(null, $1, null, $2, @$); + } + | maplets ',' STAR arg opt_block_reference + { + $$ = MakeArguments(null, $1, $4, $5, @$); + } + | arg ',' maplets opt_block_reference + { + $$ = MakeArguments(CollectionUtils.MakeList($1), $3, null, $4, @$); + } + | arg ',' args ',' maplets opt_block_reference + { + $3.Insert(0, $1); + $$ = MakeArguments($3, $5, null, $6, @$); + } + | arg ',' maplets ',' STAR arg opt_block_reference + { + $$ = MakeArguments(CollectionUtils.MakeList($1), $3, $6, $7, @$); + } + | arg ',' args ',' maplets ',' STAR arg opt_block_reference + { + $3.Insert(0, $1); + $$ = MakeArguments($3, $5, $8, $9, @$); + } + | STAR arg opt_block_reference + { + $$ = MakeArguments(null, null, $2, $3, @$); + } + | block_reference + { + $$ = MakeArguments($1); + } +; + +command_args: + { + $$ = _tokenizer.CMDARG; + _tokenizer.CMDARG_PUSH(1); + } + command_args_content + { + _tokenizer.CMDARG = $1; + $$ = $2; + } +; + +command_args_content: + open_args + { + Debug.Assert($1.Arguments != null); + $$ = $1; + } + | LPAREN_ARG + { + _tokenizer.SetState(LexicalState.EXPR_ENDARG); + } + ')' + { + _tokenizer.ReportWarning(Errors.WhitespaceBeforeArgumentParentheses); + $$ = MakeArguments(); + } + | LPAREN_ARG closed_args + { + _tokenizer.SetState(LexicalState.EXPR_ENDARG); + } + ')' + { + _tokenizer.ReportWarning(Errors.WhitespaceBeforeArgumentParentheses); + $$ = $2; + } +; + +block_reference: + AMPERSAND arg + { + $$ = new BlockReference($2, @$); + } +; + +opt_block_reference: + ',' block_reference + { + $$ = $2; + } + | /* empty */ + { + $$ = null; + } +; + +args: + arg + { + $$ = CollectionUtils.MakeList($1); + } + | args ',' arg + { + $1.Add($3); + $$ = $1; + } +; + +primary: numeric_literal + | symbol + { + $$ = new SymbolLiteral($1, @$); + } + | immutable_string + | string_concatenation + { + $$ = new StringConstructor($1, StringKind.Mutable, @1); + } + | shell_string + | regexp + | words + | verbatim_words + | var_ref + | match_reference + { + $$ = $1; + } + | FUNCTION_IDENTIFIER + { + $$ = new MethodCall(null, $1, null, @1); + } + | primary SEPARATING_DOUBLE_COLON CONSTANT_IDENTIFIER + { + $$ = new ConstantVariable($1, $3, @$); + } + | LEADING_DOUBLE_COLON CONSTANT_IDENTIFIER + { + $$ = new ConstantVariable(null, $2, @$); + } + | primary '[' array_key ']' + { + $$ = new ArrayItemAccess($1, $3, @$); + } + | LBRACK array_key ']' + { + $$ = new ArrayConstructor($2, @$); + } + | LBRACE '}' + { + $$ = new HashConstructor(null, null, @$); + } + | LBRACE maplets trailer '}' + { + $$ = new HashConstructor($2, null, @$); + } + | LBRACE args trailer '}' + { + $$ = new HashConstructor(null, CheckHashExpressions($2, @3), @$); + } + | YIELD '(' open_args ')' + { + $$ = new YieldCall(RequireNoBlockArg($3), @$); + } + | YIELD '(' ')' + { + $$ = new YieldCall(Arguments.Empty, @$); + } + | YIELD + { + $$ = new YieldCall(null, @1); + } + | DEFINED opt_nl '(' expr ')' + { + $$ = new IsDefinedExpression($4, @$); + } + | operation brace_block + { + $$ = new MethodCall(null, $1, null, $2, @1); + } + | method_call + | method_call brace_block + { + $1.Block = $2; + $$ = $1; + } + | IF expr then compstmt if_tail END + { + $$ = MakeIfExpression($2.ToCondition(), $4, $5, @$); + } + | UNLESS expr then compstmt else_opt END + { + $$ = new UnlessExpression($2.ToCondition(), $4, $5, @$); + } + | WHILE + { + _tokenizer.COND_PUSH(1); + } + expr do + { + _tokenizer.COND_POP(); + } + compstmt END + { + $$ = new WhileLoopExpression($3.ToCondition(), true, false, $6, @$); + } + | UNTIL + { + _tokenizer.COND_PUSH(1); + } + expr do + { + _tokenizer.COND_POP(); + } + compstmt END + { + $$ = new WhileLoopExpression($3.ToCondition(), false, false, $6, @$); + } + | case_expression + | FOR block_parameters IN + { + _tokenizer.COND_PUSH(1); + } + expr do + { + _tokenizer.COND_POP(); + } + compstmt END + { + $$ = new ForLoopExpression($2, $5, $8, @$); + } + | block_expression + { + $$ = $1; + } + | declaration_expression + { + $$ = $1; + } +; + +block_expression: + LPAREN_ARG expr + { + _tokenizer.SetState(LexicalState.EXPR_ENDARG); + } + opt_nl ')' + { + _tokenizer.ReportWarning(Errors.InterpretedAsGroupedExpression); + // BlockExpression behaves like an expression, so we don't need to create one here: + $$ = $2; + } + | LEFT_PAREN compstmt ')' + { + $$ = MakeBlockExpression($2, @$); + } + | BEGIN body END + { + $$ = $2; + } +; + +declaration_expression: + CLASS qualified_module_name superclass + { + EnterTopScope(); + } + body END + { + if (InMethod) { + ErrorSink.Add(_sourceUnit, "class definition in method body", @1, -1, Severity.Error); + } + $$ = new ClassDeclaration(CurrentScope, $2, $3, $5, @$); + LeaveScope(); + } + | CLASS LSHFT expr + { + $$ = _inInstanceMethodDefinition; + _inInstanceMethodDefinition = 0; + } + term + { + $$ = _inSingletonMethodDefinition; + _inSingletonMethodDefinition = 0; + EnterTopScope(); + } + body END + { + _inInstanceMethodDefinition = $4; + _inSingletonMethodDefinition = $6; + $$ = new SingletonDeclaration(LeaveScope(), $3, $7, @$); + } + | MODULE qualified_module_name + { + EnterTopScope(); + } + body END + { + if (InMethod) { + ErrorSink.Add(_sourceUnit, "module definition in method body", @1, -1, Severity.Error); + } + $$ = new ModuleDeclaration(CurrentScope, $2, $4, @$); + LeaveScope(); + } + | DEF method_name + { + _inInstanceMethodDefinition++; + EnterTopScope(); + } + parameters_declaration body END + { + _inInstanceMethodDefinition--; + $$ = new MethodDeclaration(CurrentScope, null, $2, $4, $5, @$); + LeaveScope(); + } + | DEF singleton dot_or_colon + { + _tokenizer.SetState(LexicalState.EXPR_FNAME); + } + method_name + { + _inSingletonMethodDefinition++; + _tokenizer.SetState(LexicalState.EXPR_END); + EnterTopScope(); + } + parameters_declaration body END + { + _inSingletonMethodDefinition--; + $$ = new MethodDeclaration(CurrentScope, $2, $5, $7, $8, @$); + LeaveScope(); + } +; + +body: + compstmt rescue_clauses_opt else_opt ensure_opt + { + ElseIfClause elseIf = $3; + Debug.Assert(elseIf == null || elseIf.Condition == null); + + if (elseIf != null && $2 == null) { + ErrorSink.Add(_sourceUnit, "else without rescue is useless", @3, -1, Severity.Warning); + } + + $$ = new Body($1, $2, (elseIf != null) ? elseIf.Statements : null, $4, @$); + } +; + +case_expression: + CASE expr opt_terms when_clauses else_opt END + { + $$ = new CaseExpression($2, $4, $5, @$); + } + | CASE opt_terms when_clauses else_opt END + { + $$ = new CaseExpression(null, $3, $4, @$); + } + | CASE expr opt_terms else_opt END + { + $$ = new CaseExpression($2, null, $4, @$); + } + | CASE opt_terms else_opt END + { + $$ = new CaseExpression(null, null, $3, @$); + } +; + +then: + term + | ':' + | THEN + | term THEN +; + +do: + term + | ':' + | LOOP_DO +; + +if_tail: + else_opt + { + $$ = MakeListAddOpt($1); + } + | ELSIF expr then compstmt if_tail + { + $5.Add(new ElseIfClause($2, $4, @$)); + $$ = $5; + } +; + +else_opt: /* empty */ + { + $$ = null; + } + | ELSE compstmt + { + $$ = new ElseIfClause(null, $2, @$); + } + ; + +block_parameters: + lhs + { + $$ = new CompoundLeftValue(CollectionUtils.MakeList($1), null, @1); + } + | compound_lhs + { + $$ = $1; + } +; + +block_parameters_opt: + /* empty */ + { + $$ = CompoundLeftValue.UnspecifiedBlockSignature; + } + | '|' /* empty */ '|' + { + $$ = CompoundLeftValue.EmptyBlockSignature; + } + | BITWISE_OR + { + $$ = CompoundLeftValue.EmptyBlockSignature; + } + | '|' block_parameters '|' + { + $$ = $2; + } +; + +do_block: + BLOCK_DO + { + EnterNestedScope(); + } + block_parameters_opt compstmt END + { + $$ = new BlockDefinition(CurrentScope, $3, $4, @$); + LeaveScope(); + } +; + +block_call: + command do_block + { + $1.Block = $2; + $$ = $1; + } + | block_call '.' operation2 opt_paren_args + { + $$ = MakeMethodCall($1, $3, $4, @$); + } + | block_call SEPARATING_DOUBLE_COLON operation2 opt_paren_args + { + $$ = MakeMethodCall($1, $3, $4, @$); + } +; + +method_call: + operation paren_args + { + $$ = MakeMethodCall(null, $1, $2, @$); + } + | primary '.' operation2 opt_paren_args + { + $$ = MakeMethodCall($1, $3, $4, @$); + } + | primary SEPARATING_DOUBLE_COLON operation2 paren_args + { + $$ = MakeMethodCall($1, $3, $4, @$); + } + | primary SEPARATING_DOUBLE_COLON operation3 + { + $$ = new MethodCall($1, $3, null, @3); + } + | SUPER paren_args + { + $$ = MakeSuperCall($2, @1); + } + | SUPER + { + $$ = new SuperCall(null, null, @1); + } +; + +brace_block: + '{' + { + EnterNestedScope(); + } + block_parameters_opt compstmt '}' + { + $$ = new BlockDefinition(CurrentScope, $3, $4, @$); + LeaveScope(); + } + | DO + { + EnterNestedScope(); + } + block_parameters_opt compstmt END + { + $$ = new BlockDefinition(CurrentScope, $3, $4, @$); + LeaveScope(); + } +; + +when_clauses : when_clause + { + $$ = CollectionUtils.MakeList($1); + } + | when_clauses when_clause + { + $1.Add($2); + $$ = $1; + } + ; + +when_clause: + WHEN when_args then compstmt + { + $$ = new WhenClause($2, $2, $4, @$); + } +; + +when_args: + args + { + $$ = new TokenValue($1, null); + } + | args ',' STAR arg + { + $$ = new TokenValue($1, $4); + } + | STAR arg + { + $$ = new TokenValue(null, $2); + } +; + +rescue_clauses_opt: + /* empty */ + | rescue_clauses +; + +rescue_clauses: + rescue_clause + { + $$ = CollectionUtils.MakeList($1); + } + | rescue_clauses rescue_clause + { + $1.Add($2); + $$ = $1; + } +; + +rescue_clause: + RESCUE exc_var then compstmt + { + $$ = new RescueClause($2, $4, @$); + } + | RESCUE arg exc_var then compstmt + { + $$ = new RescueClause($2, $3, $5, @$); + } + | RESCUE compound_rhs exc_var then compstmt + { + $$ = new RescueClause($2, $3, $5, @$); + } +; + +exc_var: + /* empty */ + | ASSOC lhs + { + $$ = $2; + } +; + +ensure_opt: + /* empty */ + | ENSURE compstmt + { + $$ = $2; + } +; + +string_concatenation: + string + { + $$ = $1; + } + | string_concatenation string + { + $1.AddRange($2); + $$ = $1; + } +; + +string: + STRING_BEG string_contents STRING_END + { + $$ = $2; + } +; + +shell_string: + SHELL_STRING_BEGIN string_contents STRING_END + { + $$ = new StringConstructor($2, StringKind.Command, @$); + } +; + +immutable_string: + SYMBEG string_contents STRING_END + { + $$ = MakeSymbolConstructor($2, @$); + } +; + +regexp: + REGEXP_BEG string_contents REGEXP_END + { + $$ = new RegularExpression($2, $3, @$); + } +; + +words : WORDS_BEG WORD_SEPARATOR STRING_END + { + $$ = new ArrayConstructor(null, @$); + } + | WORDS_BEG word_list STRING_END + { + $$ = new ArrayConstructor(new Arguments($2, null, null, @2), @$); + } + ; + +word_list : /* empty */ + { + $$ = new List(); + } + | word_list word WORD_SEPARATOR + { + $1.Add(new StringConstructor($2, StringKind.Mutable, @2)); + $$ = $1; + } + ; + +word : string_content + { + $$ = CollectionUtils.MakeList($1); + } + | word string_content + { + $1.Add($2); + $$ = $1; + } + ; + +verbatim_words : VERBATIM_WORDS_BEGIN WORD_SEPARATOR STRING_END + { + $$ = new ArrayConstructor(null, @$); + } + | VERBATIM_WORDS_BEGIN verbatim_word_list STRING_END + { + $$ = MakeVerbatimWords($2, @2, @$); + } + ; + +verbatim_word_list : /* empty */ + { + $$ = new List(); + } + | verbatim_word_list STRING_CONTENT WORD_SEPARATOR + { + $1.Add(MakeStringLiteral($2, @2)); + $$ = $1; + } + ; + +string_contents: + /* empty */ + { + $$ = new List(); + } + | string_contents string_content + { + $1.Add($2); + $$ = $1; + } +; + + +string_content: STRING_CONTENT + { + $$ = MakeStringLiteral($1, @$); + } + | STRING_EMBEDDED_VARIABLE_BEGIN string_embedded_variable + { + _tokenizer.StringEmbeddedVariableEnd($1); + $$ = $2; + } + | STRING_EMBEDDED_CODE_BEGIN compstmt '}' + { + _tokenizer.StringEmbeddedCodeEnd($1); + $$ = MakeBlockExpression($2, @2); + } + ; + +string_embedded_variable: + GLOBAL_VARIABLE + { + $$ = new GlobalVariable($1, @$); + } + | match_reference + { + $$ = $1; + } + | INSTANCE_VARIABLE + { + $$ = new InstanceVariable($1, @$); + } + | CLASS_VARIABLE + { + $$ = new ClassVariable($1, @$); + } +; + +symbol: + SYMBEG sym + { + _tokenizer.SetState(LexicalState.EXPR_END); + $$ = $2; + } +; + +sym: + method_name + | INSTANCE_VARIABLE + | GLOBAL_VARIABLE + { + $$ = "$" + $1; + } + | CLASS_VARIABLE + | match_reference + { + $$ = $1.FullName; + } +; + +numeric_literal: + INTEGER + { + // unsigned integer: + $$ = Literal.Integer($1, @$); + } + | BIG_INTEGER + { + $$ = Literal.BigInteger($1, @$); + } + | FLOAT + { + $$ = Literal.Double($1, @$); + } + | UMINUS_NUM INTEGER %prec LOWEST + { + // cannot overflow INTEGER is unsigned and Int32.MaxValue < |Int32.MinValue| + $$ = Literal.Integer(-$2, @$); + } + | UMINUS_NUM BIG_INTEGER %prec LOWEST + { + // TODO: -|Int32.MinValue| actually ends up here (converted to bigint) instead of being Int32. We should fix that. + $$ = Literal.BigInteger(-$2, @$); + } + | UMINUS_NUM FLOAT %prec LOWEST + { + $$ = Literal.Double(-$2, @$); + } +; + +variable: + IDENTIFIER { $$ = VariableFactory.Identifier; $$ = $1; } + | INSTANCE_VARIABLE { $$ = VariableFactory.Instance; $$ = $1; } + | GLOBAL_VARIABLE { $$ = VariableFactory.Global; $$ = $1; } + | CONSTANT_IDENTIFIER { $$ = VariableFactory.Constant; $$ = $1; } + | CLASS_VARIABLE { $$ = VariableFactory.Class; $$ = $1; } + | NIL { $$ = VariableFactory.Nil; $$ = null; } + | SELF { $$ = VariableFactory.Self; $$ = null; } + | TRUE { $$ = VariableFactory.True; $$ = null; } + | FALSE { $$ = VariableFactory.False; $$ = null; } + | FILE { $$ = VariableFactory.File; $$ = null; } + | LINE { $$ = VariableFactory.Line; $$ = null; } + | ENCODING { $$ = VariableFactory.Encoding; $$ = null; } +; + +var_ref: + variable + { + $$ = VariableFactory.MakeRead($1, this, $1, @$); + } +; + +var_lhs: + variable + { + $$ = VariableFactory.MakeLeftValue($1, this, $1, @$); + } +; + +match_reference: + MATCH_REFERENCE + { + $$ = new RegexMatchReference($1, @1); + } +; + +superclass : term + { + $$ = null; + } + | '<' + { + _tokenizer.SetState(LexicalState.EXPR_BEG); + } + expr term + { + $$ = $3; + } + | ERROR term + { + StopErrorRecovery(); + $$ = null; + } + ; + +parameters_declaration : '(' parameters opt_nl ')' + { + $$ = $2; + _tokenizer.SetState(LexicalState.EXPR_BEG); + } + | parameters term + { + $$ = $1; + } + ; + +parameters : parameter_list ',' default_parameter_list ',' array_parameter block_parameter_opt + { + $$ = new Parameters($1, $3, $5, $6, @$); + } + | parameter_list ',' default_parameter_list block_parameter_opt + { + $$ = new Parameters($1, $3, null, $4, @$); + } + | parameter_list ',' array_parameter block_parameter_opt + { + $$ = new Parameters($1, null, $3, $4, @$); + } + | parameter_list block_parameter_opt + { + $$ = new Parameters($1, null, null, $2, @$); + } + | default_parameter_list ',' array_parameter block_parameter_opt + { + $$ = new Parameters(null, $1, $3, $4, @$); + } + | default_parameter_list block_parameter_opt + { + $$ = new Parameters(null, $1, null, $2, @$); + } + | array_parameter block_parameter_opt + { + $$ = new Parameters(null, null, $1, $2, @$); + } + | block_parameter + { + $$ = new Parameters(null, null, null, $1, @$); + } + | /* empty */ + { + $$ = new Parameters(null, null, null, null, @$); + } + ; + +parameter : CONSTANT_IDENTIFIER + { + _tokenizer.ReportError(Errors.FormalArgumentIsConstantVariable); + $$ = DefineParameter(GenerateErrorConstantName(), @$); + } + | INSTANCE_VARIABLE + { + _tokenizer.ReportError(Errors.FormalArgumentIsInstanceVariable); + $$ = DefineParameter(GenerateErrorConstantName(), @$); + } + | GLOBAL_VARIABLE + { + _tokenizer.ReportError(Errors.FormalArgumentIsGlobalVariable); + $$ = DefineParameter(GenerateErrorConstantName(), @$); + } + | CLASS_VARIABLE + { + _tokenizer.ReportError(Errors.FormalArgumentIsClassVariable); + $$ = DefineParameter(GenerateErrorConstantName(), @$); + } + | IDENTIFIER + { + $$ = DefineParameter($1, @$); + } + ; + +parameter_list : parameter + { + $$ = CollectionUtils.MakeList($1); + } + | parameter_list ',' parameter + { + $1.Add($3); + $$ = $1; + } + ; + +default_parameter : parameter '=' arg + { + $$ = new SimpleAssignmentExpression($1, $3, null, @$); + } + ; + +default_parameter_list : default_parameter + { + $$ = CollectionUtils.MakeList($1); + } + | default_parameter_list ',' default_parameter + { + $1.Add($3); + $$ = $1; + } + ; + +array_parameter_mark : '*' + | STAR + ; + +array_parameter : array_parameter_mark parameter + { + $$ = $2; + } + | array_parameter_mark + { + $$ = DefineParameter(Symbols.RestArgsLocal, @1); + } + ; + +block_parameter_mark : '&' + | AMPERSAND + ; + +block_parameter : block_parameter_mark parameter + { + $$ = $2; + } + ; + +block_parameter_opt : /* empty */ + | ',' block_parameter + { + $$ = $2; + } + ; + +singleton : var_ref + | '(' + { + _tokenizer.SetState(LexicalState.EXPR_BEG); + } + expr opt_nl ')' + { + $$ = $3; + } + ; + +maplets : maplet + { + $$ = CollectionUtils.MakeList($1); + } + | maplets ',' maplet + { + $1.Add($3); + $$ = $1; + } + ; + +maplet : arg ASSOC arg + { + $$ = new Maplet($1, $3, @$); + } + ; + +operation : IDENTIFIER + | CONSTANT_IDENTIFIER + | FUNCTION_IDENTIFIER + ; + +operation2 : IDENTIFIER + | CONSTANT_IDENTIFIER + | FUNCTION_IDENTIFIER + | op + ; + +operation3 : IDENTIFIER + | FUNCTION_IDENTIFIER + | op + ; + +dot_or_colon: '.' + | SEPARATING_DOUBLE_COLON + ; + +opt_terms : /* empty */ + | terms + ; + +opt_nl : /* empty */ + | '\n' + ; + +trailer : /* empty */ + | '\n' + | ',' + ; + +term : ';' { StopErrorRecovery(); } + | '\n' + ; + +terms : term + | terms ';' { StopErrorRecovery(); } + ; + +%% diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Parser/StringTokenizer.cs b/merlin/main/languages/ruby/Ruby/Compiler/Parser/StringTokenizer.cs new file mode 100644 index 0000000000..b23768e059 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Parser/StringTokenizer.cs @@ -0,0 +1,145 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; + +namespace IronRuby.Compiler { + + internal abstract class StringTokenizer { + internal abstract Tokens Tokenize(Tokenizer/*!*/ tokenizer); + } + + [Flags] + public enum StringType { + Default = 0, + ExpandsEmbedded = 1, + RegularExpression = 2, + Words = 4, + Symbol = 8, + IndentedHeredoc = 16, + FinalWordSeparator = 32, + } + + internal sealed class StringContentTokenizer : StringTokenizer { + private StringType _properties; + + // The number of opening parentheses that need to be closed before the string terminates. + // The level is tracked for all currently opened strings. + // For example, %< .. < .. < #{...} .. > .. > .. > comprises of 3 parts: + // %< .. < .. < + // #{...} + // .. > .. > .. > + // After reading the first part the nested level for the string is set to 2. + private int _nestingLevel; + + // The character terminating the string: + private readonly char _terminator; + + // Parenthesis opening the string; if non-zero parenthesis of this kind is balanced before the string is closed: + private readonly char _openingParenthesis; + + public StringContentTokenizer(StringType properties, char terminator) + : this(properties, terminator, '\0') { + } + + public StringContentTokenizer(StringType properties, char terminator, char openingParenthesis) { + Debug.Assert(!Tokenizer.IsLetterOrDigit(terminator)); + + _properties = properties; + _terminator = terminator; + _openingParenthesis = openingParenthesis; + _nestingLevel = 0; + } + + public StringType Properties { + get { return _properties; } + set { _properties = value; } + } + + public int NestingLevel { + get { return _nestingLevel; } + set { _nestingLevel = value; } + } + + public char TerminatingCharacter { + get { return _terminator; } + } + + public char OpeningParenthesis { + get { return _openingParenthesis; } + } + + public override string ToString() { + return String.Format("StringTerminator({0},{1},{2},{3},{4})", _properties, (int)_terminator, (int)_openingParenthesis, 0, _nestingLevel); + } + + internal override Tokens Tokenize(Tokenizer/*!*/ tokenizer) { + return tokenizer.TokenizeString(this); + } + } + + internal sealed class HeredocTokenizer : StringTokenizer { + + private readonly StringType _properties; + private readonly string _label; + private readonly int _resumePosition; + private readonly char[] _resumeLine; + private readonly int _firstLine; + private readonly int _firstLineIndex; + + public HeredocTokenizer(StringType properties, string label, int resumePosition, char[] resumeLine, int firstLine, int firstLineIndex) { + _properties = properties; + _label = label; + _resumePosition = resumePosition; + _resumeLine = resumeLine; + _firstLine = firstLine; + _firstLineIndex = firstLineIndex; + } + + public StringType Properties { + get { return _properties; } + } + + public string Label { + get { return _label; } + } + + public int ResumePosition { + get { return _resumePosition; } + } + + + public char[] ResumeLine { + get { return _resumeLine; } + } + + public int FirstLine { + get { return _firstLine; } + } + + public int FirstLineIndex { + get { return _firstLineIndex; } + } + + public override string ToString() { + return String.Format("Heredoc({0},'{1}',{2},'{2}')", _properties, _label, _resumePosition, new String(_resumeLine)); + } + + internal override Tokens Tokenize(Tokenizer/*!*/ tokenizer) { + return tokenizer.TokenizeHeredoc(this); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Parser/Symbols.cs b/merlin/main/languages/ruby/Ruby/Compiler/Parser/Symbols.cs new file mode 100644 index 0000000000..1807314c1a --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Parser/Symbols.cs @@ -0,0 +1,110 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using IronRuby.Compiler.Ast; +using System; + +namespace IronRuby.Compiler { + public static class Symbols { + // TODO: + public static readonly string None = String.Empty; + public static readonly string Error = "#error"; + public static readonly string ErrorVariable = "error__"; + public static readonly string RestArgsLocal = "?rest?"; + + public static readonly string MethodMissing = "method_missing"; + public static readonly string MethodAdded = "method_added"; + public static readonly string MethodRemoved = "method_removed"; + public static readonly string MethodUndefined = "method_undefined"; + public static readonly string SingletonMethodAdded = "singleton_method_added"; + public static readonly string SingletonMethodRemoved = "singleton_method_removed"; + public static readonly string SingletonMethodUndefined = "singleton_method_undefined"; + public static readonly string Inherited = "inherited"; + public static readonly string RespondTo = "respond_to?"; + public static readonly string ToProc = "to_proc"; + public static readonly string ToS = "to_s"; + public static readonly string ToStr = "to_str"; + public static readonly string ToAry = "to_ary"; + public static readonly string ToInt = "to_int"; + public static readonly string Initialize = "initialize"; + public static readonly string InitializeCopy = "initialize_copy"; + public static readonly string Object = "Object"; + public static readonly string Kernel = "Kernel"; + public static readonly string Module = "Module"; + public static readonly string Class = "Class"; + + public static readonly string Each = "each"; + + public static readonly string DoubleDot = ".."; + public static readonly string TripleDot = "..."; + public static readonly string Power = "**"; + public static readonly string UnaryPlus = "+@"; + public static readonly string Plus = "+"; + public static readonly string UnaryMinus = "-@"; + public static readonly string Minus = "-"; + public static readonly string Comparison = "<=>"; + public static readonly string GreaterEqual = ">="; + public static readonly string GreaterThan = ">"; + public static readonly string LessEqual = "<="; + public static readonly string LessThan = "<"; + public static readonly string Equal = "=="; + public static readonly string StrictEqual = "==="; + public static readonly string NotEqual = "!="; + public static readonly string Match = "=~"; + public static readonly string NotMatch = "!~"; + public static readonly string BitwiseNot = "~"; + public static readonly string ArrayItemRead = "[]"; + public static readonly string ArrayItemWrite = "[]="; + public static readonly string LeftShift = "<<"; + public static readonly string RightShift = ">>"; + public static readonly string DoubleColon = "::"; + public static readonly string Or = "||"; + public static readonly string And = "&&"; + public static readonly string BitwiseOr = "|"; + public static readonly string BitwiseAnd = "&"; + public static readonly string Mod = "%"; + public static readonly string Xor = "^"; + public static readonly string Multiply = "*"; + public static readonly string Backtick = "`"; + public static readonly string Divide = "/"; + + // variables: + public static readonly string CurrentException = "!"; + public static readonly string CurrentExceptionBacktrace = "@"; + public static readonly string ItemSeparator = ","; + public static readonly string StringSeparator = ";"; + public static readonly string InputSeparator = Divide; + public static readonly string OutputSeparator = "\\"; + public static readonly string CommandLineArguments = Multiply; + public static readonly string CommandLineProgramPath = "0"; + public static readonly string CurrentProcessId = "$"; + public static readonly string ChildProcessExitStatus = "?"; + public static readonly string IgnoreCaseComparator = "="; + public static readonly string LoadPath = ":"; + public static readonly string LoadedFiles = "\""; + public static readonly string InputContent = LessThan; + public static readonly string OutputStream = GreaterThan; + public static readonly string LastInputLine = "_"; + public static readonly string LastInputLineNumber = "."; + + // match references: + public static readonly string MatchData = RegexMatchReference.MatchDataName; + public static readonly string EntireMatch = RegexMatchReference.EntireMatchName; + public static readonly string MatchLastGroup = RegexMatchReference.MatchLastGroupName; + public static readonly string MatchPrefix = RegexMatchReference.MatchPrefixName; + public static readonly string MatchSuffix = RegexMatchReference.MatchSuffixName; + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Parser/TokenValue.cs b/merlin/main/languages/ruby/Ruby/Compiler/Parser/TokenValue.cs new file mode 100644 index 0000000000..982193b1aa --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Parser/TokenValue.cs @@ -0,0 +1,203 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Math; +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Compiler.Ast; +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler { + + public enum TokenValueType { + None = 0, + String, + Integer, + BigInteger, + Double, + RegexOptions, + StringTokenizer, + } + + // TODO: + // [StructLayout(LayoutKind.Explicit)] + public partial struct TokenValue { + // TODO: debug only + public TokenValueType Type { get { return _type; } } + private TokenValueType _type; + + // [FieldOffset(0)] + private int _integer1; + + // [FieldOffset(0)] + private double _double; + + // [FieldOffset(8)] + public object obj; + + // [FieldOffset(12)] + public Node node; + + public TokenValue(Arguments arguments, Block block) { + _integer1 = 0; + _double = 0.0; + node = arguments; + obj = block; + _type = TokenValueType.None; + } + + public TokenValue(List comparisons, Expression comparisonArray) { + _integer1 = 0; + _double = 0.0; + node = comparisonArray; + obj = comparisons; + _type = TokenValueType.None; + } + + public int Integer { get { return _integer1; } set { _integer1 = value; } } + public double Double { get { return _double; } set { _double = value; } } + + // Tokens: StringContent + internal StringLiteralEncoding/*!*/ StringLiteralEncoding { get { return (StringLiteralEncoding)_integer1; } } + + // Tokens: StringBegin, SymbolBegin, RegexBegin, ShellStringBegin + internal StringTokenizer/*!*/ StringTokenizer { get { return (StringTokenizer)obj; } set { obj = value; } } + + internal int VariableFactory { get { return _integer1; } set { _integer1 = value; } } + + public RubyRegexOptions RegExOptions { get { return (RubyRegexOptions)Integer; } set { Integer = (int)value; } } + public CallExpression CallExpression { get { return (CallExpression)node; } set { node = value; } } + public ElseIfClause ElseIfClause { get { return (ElseIfClause)node; } set { node = value; } } + public RescueClause RescueClause { get { return (RescueClause)node; } set { node = value; } } + public WhenClause WhenClause { get { return (WhenClause)node; } set { node = value; } } + + public Arguments Arguments { get { return (Arguments)node; } set { node = value; } } + public Block Block { get { return (Block)obj; } set { obj = value; } } + + public Expression Expression { get { return (Expression)node; } set { node = value; } } + public List/*!*/ Expressions { get { return (List)obj; } set { obj = value; } } + + public BlockReference BlockReference { get { return (BlockReference)node; } set { node = value; } } + public BlockDefinition BlockDefinition { get { return (BlockDefinition)node; } set { node = value; } } + public ConstantVariable ConstantVariable { get { return (ConstantVariable)node; } set { node = value; } } + public Maplet Maplet { get { return (Maplet)node; } set { node = value; } } + public BigInteger/*!*/ BigInteger { get { return (BigInteger)obj; } set { obj = value; } } + public String/*!*/ String { get { return (String)obj; } set { obj = value; } } + public Parameters Parameters { get { return (Parameters)node; } set { node = value; } } + public LocalVariable LocalVariable { get { return (LocalVariable)node; } set { node = value; } } + public SimpleAssignmentExpression SimpleAssignmentExpression { get { return (SimpleAssignmentExpression)node; } set { node = value; } } + public LeftValue LeftValue { get { return (LeftValue)node; } set { node = value; } } + public CompoundLeftValue CompoundLeftValue { get { return (CompoundLeftValue)node; } set { node = value; } } + public Body Body { get { return (Body)node; } set { node = value; } } + public CompoundRightValue CompoundRightValue { get { return (CompoundRightValue)obj; } set { obj = value; } } + public JumpStatement JumpStatement { get { return (JumpStatement)obj; } set { obj = value; } } + public RegexMatchReference RegexMatchReference { get { return (RegexMatchReference)obj; } set { obj = value; } } + + public List LeftValues { get { return (List)obj; } set { obj = value; } } + public List/*!*/ Identifiers { get { return (List)obj; } set { obj = value; } } + public List/*!*/ ElseIfClauses { get { return (List)obj; } set { obj = value; } } + public List/*!*/ WhenClauses { get { return (List)obj; } set { obj = value; } } + public List/*!*/ LeftValueList { get { return (List)obj; } set { obj = value; } } + public List/*!*/ Statements { get { return (List)obj; } set { obj = value; } } + public List RescueClauses { get { return (List)obj; } set { obj = value; } } + public List Maplets { get { return (List)obj; } set { obj = value; } } + public List/*!*/ LocalVariables { get { return (List)obj; } set { obj = value; } } + public List/*!*/ SimpleAssignmentExpressions { get { return (List)obj; } set { obj = value; } } + + internal void SetInteger(int value) { + Integer = value; + _type = TokenValueType.Integer; + } + + internal void SetBigInteger(BigInteger value) { + BigInteger = value; + _type = TokenValueType.BigInteger; + } + + internal void SetDouble(double value) { + Double = value; + _type = TokenValueType.Double; + } + + internal void SetString(string/*!*/ value, StringLiteralEncoding encoding) { + Assert.NotNull(value); + String = value; + _integer1 = (int)encoding; + _type = TokenValueType.String; + } + + internal void SetString(string/*!*/ value, bool hasUnicodeEscape) { + SetString(value, + hasUnicodeEscape ? StringLiteralEncoding.UTF8 : + value.IsAscii() ? StringLiteralEncoding.Ascii : // TODO: tokenizer already knows IsAscii + StringLiteralEncoding.Default + ); + } + + internal void SetSymbol(string/*!*/ value) { + SetString(value, StringLiteralEncoding.Ascii); + } + + internal void SetStringTokenizer(StringTokenizer value) { + StringTokenizer = value; + _type = TokenValueType.StringTokenizer; + } + + internal void SetRegexOptions(RubyRegexOptions value) { + Integer = (int)value; + _type = TokenValueType.RegexOptions; + } + +#if DEBUG + public override string ToString() { + string str; + switch (_type) { + case TokenValueType.Double: + str = Double.ToString(); + break; + + case TokenValueType.Integer: + str = Integer.ToString(); + break; + + case TokenValueType.BigInteger: + str = BigInteger.ToString(); + break; + + case TokenValueType.RegexOptions: + str = ((RubyRegexOptions)Integer).ToString(); + break; + + case TokenValueType.String: + str = "\"" + Parser.EscapeString(String) + "\""; + break; + + case TokenValueType.StringTokenizer: + str = (StringTokenizer != null) ? StringTokenizer.ToString() : ""; + break; + + default: + str = ""; + break; + } + return String.Format("{0}: {1}", _type, str); + } +#endif + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Parser/Tokenizer.cs b/merlin/main/languages/ruby/Ruby/Compiler/Parser/Tokenizer.cs new file mode 100644 index 0000000000..256cb80017 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Parser/Tokenizer.cs @@ -0,0 +1,3754 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Dynamic; +using System.Text; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Compiler.Ast; +using System.Text.RegularExpressions; + +namespace IronRuby.Compiler { + public enum LexicalState { + EXPR_BEG, /* ignore newline, +/- is a sign. */ + EXPR_END, /* newline significant, +/- is a operator. */ + EXPR_ARG, /* newline significant, +/- is a operator. */ + EXPR_CMDARG, /* newline significant, +/- is a operator. */ + EXPR_ENDARG, /* newline significant, +/- is a operator. */ + EXPR_MID, /* newline significant, +/- is a operator. */ + EXPR_FNAME, /* ignore newline, no reserved words. */ + EXPR_DOT, /* right after `.' or `::', no reserved words. */ + EXPR_CLASS, /* immediate after `class', no here document. */ + }; + + public class Tokenizer : TokenizerService { + public sealed class BignumParser : UnsignedBigIntegerParser { + private char[] _buffer; // TODO: TokenizerBuffer + private int _position; + + public int Position { get { return _position; } set { _position = value; } } + public char[] Buffer { get { return _buffer; } set { _buffer = value; } } // TODO: remove + + public BignumParser() { + } + + protected override int ReadDigit() { + Debug.Assert('0' < 'A' && 'A' < '_' && '_' < 'a'); + + while (true) { + char c = _buffer[_position++]; + + if (c <= '9') { + Debug.Assert(c >= '0'); + return c - '0'; + } else if (c >= 'a') { + Debug.Assert(c <= 'z'); + return c - 'a' + 10; + } else if (c != '_') { + Debug.Assert(c >= 'A' && c <= 'Z'); + return c - 'A' + 10; + } + } + } + } + + #region DLR + + private const int DefaultBufferCapacity = 1024; + + // tokenizer properties: + private SourceUnit _sourceUnit; + + //private TokenizerBuffer _buffer; + private ErrorSink/*!*/ _errorSink; + private TokenValue _tokenValue; + private bool _verbatim; + private bool _eofReached; + + // __END__ + private int _dataOffset; + + private RubyCompatibility _compatibility; + + /// + /// Can be null - unbound tokenizer. + /// + private Parser _parser; + + public override object CurrentState { + get { return null; } + } + + public SourceUnit SourceUnit { + get { return _sourceUnit; } + } + + public int DataOffset { + get { return _dataOffset; } + } + + public override ErrorSink/*!*/ ErrorSink { + get { return _errorSink; } + set { + ContractUtils.RequiresNotNull(value, "value"); + _errorSink = value; + } + } + + public RubyCompatibility Compatibility { + get { return _compatibility; } + set { _compatibility = value; } + } + + public override bool IsRestartable { + get { return false; } + } + + internal bool IsEndOfFile { + get { + return _eofReached; // TODO: _buffer.Peek() == TokenizerBuffer.EOF; + } + } + + public override SourceLocation CurrentPosition { + get { return _tokenSpan.End; } // TODO: ??? + } + + public SourceSpan TokenSpan { + get { + return _tokenSpan; + } + } + + // TODO: internal (tests) + public TokenValue TokenValue { + get { + return _tokenValue; + } + } + + // TODO: internal (tests) + public LexicalState LexicalState { + get { + return _lexicalState; + } + } + + public Tokenizer() + : this(true) { + } + + public Tokenizer(bool verbatim) + : this(verbatim, ErrorSink.Null) { + } + + public Tokenizer(bool verbatim, ErrorSink/*!*/ errorSink) { + ContractUtils.RequiresNotNull(errorSink, "errorSink"); + + _bigIntParser = new BignumParser(); + _errorSink = errorSink; + _sourceUnit = null; + _parser = null; + _verbatim = verbatim; + _compatibility = RubyCompatibility.Default; +// _buffer = null; + _initialLocation = SourceLocation.Invalid; + _tokenSpan = SourceSpan.Invalid; + _tokenValue = new TokenValue(); + _bufferPos = 0; + + // TODO: + _input = null; + } + + internal Tokenizer(Parser/*!*/ parser) + : this(false) { + _parser = parser; + } + + public void Initialize(SourceUnit sourceUnit) { + ContractUtils.RequiresNotNull(sourceUnit, "sourceUnit"); + + Initialize(null, sourceUnit.GetReader(), sourceUnit, SourceLocation.MinValue, DefaultBufferCapacity); + } + + public override void Initialize(object state, TextReader/*!*/ reader, SourceUnit/*!*/ sourceUnit, SourceLocation initialLocation) { + Initialize(state, reader, sourceUnit, initialLocation, DefaultBufferCapacity); + } + + public void Initialize(object state, TextReader/*!*/ reader, SourceUnit sourceUnit, SourceLocation initialLocation, int bufferCapacity) { + ContractUtils.RequiresNotNull(reader, "reader"); + + //if (state != null) { + // if (!(state is State)) throw new ArgumentException(); + // _state = new State((State)state); + //} else { + // _state = new State(null); + //} + + _sourceUnit = sourceUnit; + + //_buffer = new TokenizerBuffer(sourceReader, initialLocation, bufferCapacity, true); + _input = reader; + _initialLocation = initialLocation; + _tokenSpan = new SourceSpan(initialLocation, initialLocation); + _tokenValue = new TokenValue(); + + _eofReached = false; + + UnterminatedToken = false; + + DumpBeginningOfUnit(); + } + + /// + /// Asks parser whether a given identifier is a local variable. + /// Unbound tokenizer considers any identifier a local variable. + /// + private bool IsLocalVariable(string/*!*/ identifier) { + return _parser == null || _parser.CurrentScope.ResolveVariable(identifier) != null; + } + + private string GetTokenString() { + return _tokenString != null ? _tokenString.ToString() : ""; + } + + #endregion + + private readonly BignumParser/*!*/ _bigIntParser; + private LexicalState _lexicalState; + + private bool _commaStart = true; + + + private StringTokenizer _currentString = null; + + private TextReader _input; + + private StringBuilder yytext; + + // Entire line that is currently being tokenized. + // Ends with \r, \n, or \r\n. + private char[] _lineBuffer; + + // characters belonging to the token currently being read: + private StringBuilder _tokenString = null; + + // index in the current buffer/line: + private int _bufferPos = 0; + + // current line no: + private int _currentLine; + private int _currentLineIndex; + + // non-zero => end line of the current heredoc + private int _heredocEndLine; + private int _heredocEndLineIndex = -1; + private SourceLocation _initialLocation; + + private int _cmdArgStack = 0; + private int _condStack = 0; + + internal bool UnterminatedToken; + + // token positions set during tokenization (TODO: to be replaced by tokenizer buffer): + private SourceLocation _currentTokenStart; + private SourceLocation _currentTokenEnd; + + // last token span: + private SourceSpan _tokenSpan; + + #region Debug Logging + +#if DEBUG + private int _logVerbosity; // 0 means logging disabled + private TextWriter _log; +#endif + [Conditional("DEBUG")] + public void EnableLogging(int verbosity, TextWriter/*!*/ output) { + Debug.Assert(verbosity > 0 && verbosity <= 2); + Assert.NotNull(output); +#if DEBUG + _logVerbosity = verbosity; + _log = output; +#endif + } + + [Conditional("DEBUG")] + public void DisableLogging() { +#if DEBUG + _logVerbosity = 0; + _log = null; +#endif + } + + [Conditional("DEBUG")] + private void Log(string/*!*/ format, params object[] args) { +#if DEBUG + if (_logVerbosity > 0) { + _log.WriteLine(format, args); + } +#endif + } + + [Conditional("DEBUG")] + private void DumpBeginningOfUnit() { + Log("--- Source unit: '{0}' ---", _sourceUnit != null ? _sourceUnit.Path : "N/A"); + } + + [Conditional("DEBUG")] + private void DumpToken(Tokens token) { +#if DEBUG + Log("{0,-25} {1,-25} {2,-25} {3,-20} {4}", + Parser.TerminalToString((int)token), + "\"" + Parser.EscapeString(GetTokenString()) + "\"", + _tokenValue.ToString(), + "", // TODO: FIX RUBY + _lexicalState); +#endif + } + + #endregion + + #region Parser Callbacks, State Operations + + private bool IS_ARG() { + return _lexicalState == LexicalState.EXPR_ARG || _lexicalState == LexicalState.EXPR_CMDARG; + } + + public void SetState(LexicalState state) { + _lexicalState = state; + } + + internal int CMDARG + { + get { return _cmdArgStack; } + set { _cmdArgStack = value; } + } + + internal void CMDARG_PUSH(int n) { + BITSTACK_PUSH(ref _cmdArgStack, n); + } + + internal int CMDARG_POP() { + return BITSTACK_POP(ref _cmdArgStack); + } + + internal void CMDARG_LEXPOP() { + BITSTACK_LEXPOP(ref _cmdArgStack); + } + + internal bool CMDARG_P() { + return BITSTACK_SET_P(_cmdArgStack); + } + + // Push(n) + private void BITSTACK_PUSH(ref int stack, int n) { + stack = (stack << 1) | ((n) & 1); + } + + // Pop() + private int BITSTACK_POP(ref int stack) { + return (stack >>= 1); + } + + // x = Pop(), Top |= x + private void BITSTACK_LEXPOP(ref int stack) { + stack = (stack >> 1) | (stack & 1); + } + + // Peek() != 0 + private bool BITSTACK_SET_P(int stack) { + return (stack & 1) != 0; + } + + internal void COND_PUSH(int n) { + BITSTACK_PUSH(ref _condStack, n); + } + + internal int COND_POP() { + return BITSTACK_POP(ref _condStack); + } + + internal void COND_LEXPOP() { + // this case cannot happen, since it would require closing parenthesis not matching opening + // ( ... while [[ ... ) ... do ]] + + // was: BITSTACK_LEXPOP(ref cond_stack); + COND_POP(); + } + + internal bool COND_P() { + return BITSTACK_SET_P(_condStack); + } + + private Tokens StringEmbeddedVariableBegin() { + _tokenValue.SetStringTokenizer(_currentString); + _currentString = null; + SetState(LexicalState.EXPR_BEG); + return Tokens.StringEmbeddedVariableBegin; + } + + private Tokens StringEmbeddedCodeBegin() { + _tokenValue.SetStringTokenizer(_currentString); + _currentString = null; + SetState(LexicalState.EXPR_BEG); + COND_PUSH(0); + CMDARG_PUSH(0); + return Tokens.StringEmbeddedCodeBegin; + } + + // called from parser at the end of the embedded variable + internal void StringEmbeddedVariableEnd(StringTokenizer stringTokenizer) { + _currentString = stringTokenizer; + } + + // called from parser at the end of the embedded code + internal void StringEmbeddedCodeEnd(StringTokenizer terminator) { + _currentString = terminator; + COND_LEXPOP(); + CMDARG_LEXPOP(); + } + + #endregion + + #region Error Reporting + + private void Report(string/*!*/ message, int errorCode, SourceSpan location, Severity severity) { + Debug.Assert(severity != Severity.FatalError); + _errorSink.Add(_sourceUnit, message, location, errorCode, severity); + } + + internal void ReportError(ErrorInfo info) { + Report(info.GetMessage(), info.Code, GetCurrentSpan(), Severity.Error); + } + + internal void ReportError(ErrorInfo info, params object[] args) { + Report(info.GetMessage(args), info.Code, GetCurrentSpan(), Severity.Error); + } + + internal void ReportWarning(ErrorInfo info) { + Report(info.GetMessage(), info.Code, GetCurrentSpan(), Severity.Warning); + } + + internal void ReportWarning(ErrorInfo info, params object[] args) { + Report(info.GetMessage(args), info.Code, GetCurrentSpan(), Severity.Warning); + } + + #endregion + + #region Buffer Operations + + // reads a line including eoln (any of \r, \n, \r\n) + // returns null if no characters read (end of sream) + // doesn't return an empty string + private char[] lex_getline() { + var result = new char[80]; + int size = 0; + + int c; + for (; ; ) { + c = _input.Read(); + if (c == -1) { + if (size > 0) { + // append eoln at the end of each file + //sb.Append('\n'); + //_currentLineLength++; + break; + } + return null; + } + + if (size == result.Length) { + Array.Resize(ref result, result.Length * 2); + } + result[size++] = (char)c; + + if (c == '\n') break; + if (c == '\r' && _input.Peek() != '\n') break; + } + + Debug.Assert(size > 0); + Array.Resize(ref result, size); + return result; + } + + // reads next character from the current line, if no characters available, reads a new line to the buffer + // skips \r if followed by \n + // appends read character to the token value + private int nextc() { + if (!RefillBuffer()) { + return -1; + } + + Debug.Assert(0 <= _bufferPos && _bufferPos < _lineBuffer.Length); + + int c = _lineBuffer[_bufferPos]; + _bufferPos++; + + // skip \r if followed by \n + if (c == '\r' && _bufferPos < _lineBuffer.Length && _lineBuffer[_bufferPos] == '\n') { + _bufferPos++; + yytext.Append((char)c); // remembers skipped \r in yytext + c = '\n'; + } + + yytext.Append((char)c); + + return c; + } + + private int peekc() { + if (!RefillBuffer()) { + return -1; + } + + Debug.Assert(0 <= _bufferPos && _bufferPos < _lineBuffer.Length); + + int c = _lineBuffer[_bufferPos]; + + // skip \r if followed by \n + if (c == '\r' && _bufferPos < _lineBuffer.Length && _lineBuffer[_bufferPos] == '\n') { + _bufferPos++; + c = '\n'; + } + + return c; + } + + private int lookahead(int i) { + if (_lineBuffer == null) { + if (!RefillBuffer()) { + return -1; + } + } + + if (_bufferPos + i < _lineBuffer.Length) { + return _lineBuffer[_bufferPos + i]; + } + + return '\n'; + } + + private bool RefillBuffer() { + Debug.Assert(_lineBuffer == null || 0 <= _bufferPos && _bufferPos <= _lineBuffer.Length); + + if (_lineBuffer == null || _bufferPos == _lineBuffer.Length) { + var lineContent = lex_getline(); + + // end of stream: + if (lineContent == null) { + return false; + } + + // skips lines of heredoc content (only number, real bits has already been read): + if (_heredocEndLine > 0) { + _currentLine = _heredocEndLine + 1; + _currentLineIndex = _heredocEndLineIndex; + _heredocEndLine = 0; + _heredocEndLineIndex = -1; + } else { + + // TODO: initial column + if (_lineBuffer == null) { + _currentLine = _initialLocation.Line; + _currentLineIndex = _initialLocation.Index; + } else { + _currentLine++; + _currentLineIndex += _lineBuffer.Length; + } + } + _lineBuffer = lineContent; + _bufferPos = 0; + } + + return true; + } + + private void pushback(int c) { + int remove = 0; + if (yytext.Length > 0) { + if (yytext.Length > 1 && yytext[yytext.Length - 1] == '\n' && yytext[yytext.Length - 2] == '\r') { + remove = 2; + } else { + remove = 1; + } + yytext.Remove(yytext.Length - remove, remove); + } + + if (c == -1) { + return; + } + + if (_bufferPos > 0) { + _bufferPos -= remove; + } + } + + private bool is_bol() { + return _bufferPos == 0; + } + + private bool was_bol() { + return _bufferPos == 1; + } + + private bool peek(char c) { + return _bufferPos < _lineBuffer.Length && c == _lineBuffer[_bufferPos]; + } + + private string tok() { + return _tokenString.ToString(); + } + + private int toklen() { + return _tokenString.Length; + } + + private int toklast() { + return (_tokenString.Length > 0 ? _tokenString[_tokenString.Length - 1] : 0); + } + + private void newtok() { + if (_tokenString == null) { + _tokenString = new StringBuilder(); + } else { + _tokenString.Length = 0; + } + } + + private void tokadd(char c) { + _tokenString.Append(c); + } + + private bool LineContentEquals(string str, bool skipWhitespace) { + int p = 0; + int n; + + if (skipWhitespace) { + while (p < _lineBuffer.Length && IsWhiteSpace(_lineBuffer[p])) { + p++; + } + } + + n = _lineBuffer.Length - (p + str.Length); + if (n < 0 || (n > 0 && _lineBuffer[p + str.Length] != '\n' && _lineBuffer[p + str.Length] != '\r')) { + return false; + } + + if (str == new String(_lineBuffer, p, str.Length)) { + return true; + } + return false; + } + + #endregion + + private void MarkSingleLineTokenEnd() { + _currentTokenEnd = GetCurrentLocation(); + } + + private void MarkMultiLineTokenEnd() { + _currentTokenEnd = GetCurrentLocation(); + } + + private Tokens MarkSingleLineTokenEnd(Tokens token) { + MarkSingleLineTokenEnd(); + return token; + } + + private void MarkTokenStart() { + _currentTokenStart = GetCurrentLocation(); + } + + private SourceLocation GetCurrentLocation() { + if (_lineBuffer != null) { + return new SourceLocation(_currentLineIndex + _bufferPos, _currentLine, _bufferPos + 1); + } else { + return _initialLocation; + } + } + + private SourceSpan GetCurrentSpan() { + SourceLocation loc = GetCurrentLocation(); + return new SourceSpan(loc, loc); + } + + public Tokens GetNextToken() { + //if (_buffer == null) throw new InvalidOperationException("Uninitialized"); + if (_input == null) throw new InvalidOperationException("Uninitialized"); + +#if DEBUG + _tokenValue = new TokenValue(); +#endif + + Tokens result = Tokenize(); + + if (result == Tokens.EndOfFile) { + _eofReached = true; + } + + return result; + } + + private Tokens Tokenize() { + yytext = new StringBuilder(); + bool whitespaceSeen = false; + + if (_currentString != null) { + Tokens token = _currentString.Tokenize(this); + if (token == Tokens.StringEnd || token == Tokens.RegexpEnd) { + _currentString = null; + _lexicalState = LexicalState.EXPR_END; + } + _tokenSpan = new SourceSpan(_currentTokenStart, _currentTokenEnd); + DumpToken(token); + return token; + } + + bool cmdState = _commaStart; + _commaStart = false; + + while (true) { + Tokens token = Tokenize(whitespaceSeen, cmdState); + + _tokenSpan = new SourceSpan(_currentTokenStart, _currentTokenEnd); + DumpToken(token); + + // ignored tokens: + switch (token) { + case Tokens.MultiLineComment: + case Tokens.SingleLineComment: + if (_verbatim) { + return token; + } + continue; + + case Tokens.Whitespace: + whitespaceSeen = true; + continue; + + case Tokens.EndOfLine: // not considered whitespace + case Tokens.InvalidCharacter: + continue; + } + + return token; + } + } + + private Tokens Tokenize(bool whitespaceSeen, bool cmdState) { + peekc(); + MarkTokenStart(); + int c = nextc(); + + switch (c) { + case '\0': // null terminates the input + // if tokenizer is asked for the next token it returns EOF again: + pushback(c); + MarkSingleLineTokenEnd(); + return Tokens.EndOfFile; + + //case '\004': /* ^D */ fixme + //case '\032': /* ^Z */ fixme + case -1: // end of stream + MarkSingleLineTokenEnd(); + return Tokens.EndOfFile; + + //case '\13': /* '\v' */ fixme + + // whitespace + case ' ': + case '\t': + case '\f': + case '\r': + do { + c = nextc(); + } while (c == ' ' || c == '\t' || c == '\f' || c == '\r'); + + if (c != -1) { + pushback(c); + } + + MarkSingleLineTokenEnd(); + return Tokens.Whitespace; + + case '\\': + c = nextc(); + + // escaped eoln is considered whitespace: + if (c == '\n') { + MarkMultiLineTokenEnd(); + return Tokens.Whitespace; + } + + pushback(c); + MarkSingleLineTokenEnd(); + return (Tokens)'\\'; + + // single-line comment + case '#': + while (true) { + c = nextc(); + + if (c == -1 || c == '\n') { + if (c != -1) { + pushback(c); + } + + MarkSingleLineTokenEnd(); + return Tokens.SingleLineComment; + } + } + + case '\n': + if (_lexicalState == LexicalState.EXPR_BEG || + _lexicalState == LexicalState.EXPR_FNAME || + _lexicalState == LexicalState.EXPR_DOT || + _lexicalState == LexicalState.EXPR_CLASS) { + + MarkMultiLineTokenEnd(); + return Tokens.EndOfLine; + } + + _commaStart = true; + _lexicalState = LexicalState.EXPR_BEG; + MarkSingleLineTokenEnd(); + return (Tokens)'\n'; + + case '*': + return MarkSingleLineTokenEnd(ReadStar(whitespaceSeen)); + + case '!': + return MarkSingleLineTokenEnd(ReadBang()); + + case '=': + if (ReadMultiLineComment()) { + MarkMultiLineTokenEnd(); + return Tokens.MultiLineComment; + } + + return MarkSingleLineTokenEnd(ReadEquals()); + + case '<': + return TokenizeLessThan(whitespaceSeen); + + case '>': + return MarkSingleLineTokenEnd(ReadGreaterThan()); + + case '"': + return MarkSingleLineTokenEnd(ReadDoubleQuote()); + + case '\'': + return MarkSingleLineTokenEnd(ReadSingleQuote()); + + case '`': + return MarkSingleLineTokenEnd(ReadBacktick(cmdState)); + + case '?': + return MarkSingleLineTokenEnd(ReadQuestionmark()); + + case '&': + return MarkSingleLineTokenEnd(ReadAmpersand(whitespaceSeen)); + + case '|': + return MarkSingleLineTokenEnd(ReadPipe()); + + case '+': + return MarkSingleLineTokenEnd(ReadPlus(whitespaceSeen)); + + case '-': + return MarkSingleLineTokenEnd(ReadMinus(whitespaceSeen)); + + case '.': + return MarkSingleLineTokenEnd(ReadDot()); + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return MarkSingleLineTokenEnd(ReadUnsignedNumber(c)); + + case ':': + return MarkSingleLineTokenEnd(ReadColon(whitespaceSeen)); + + case '/': + return MarkSingleLineTokenEnd(ReadSlash(whitespaceSeen)); + + case '^': + return MarkSingleLineTokenEnd(ReadCaret()); + + case ';': + _commaStart = true; + _lexicalState = LexicalState.EXPR_BEG; + MarkSingleLineTokenEnd(); + return (Tokens)';'; + + case ',': + _lexicalState = LexicalState.EXPR_BEG; + MarkSingleLineTokenEnd(); + return (Tokens)','; + + case '~': + return MarkSingleLineTokenEnd(ReadTilde()); + + case '(': + _commaStart = true; + return MarkSingleLineTokenEnd(ReadLeftParenthesis(whitespaceSeen)); + + case '[': + return MarkSingleLineTokenEnd(ReadLeftBracket(whitespaceSeen)); + + case '{': + return MarkSingleLineTokenEnd(ReadLeftBrace()); + + case ')': + case ']': + case '}': + COND_LEXPOP(); + CMDARG_LEXPOP(); + _lexicalState = LexicalState.EXPR_END; + MarkSingleLineTokenEnd(); + return (Tokens)c; + + case '%': + return MarkSingleLineTokenEnd(ReadPercent(whitespaceSeen)); + + case '$': + return MarkSingleLineTokenEnd(ReadGlobalVariable()); + + case '@': + return MarkSingleLineTokenEnd(ReadInstanceOrClassVariable()); + + case '_': + if (was_bol() && LineContentEquals("__END__", false)) { + // if tokenizer is asked for the next token it returns EOF again: + pushback(c); + MarkSingleLineTokenEnd(); + _dataOffset = _currentLineIndex + _lineBuffer.Length; + return Tokens.EndOfFile; + } + return MarkSingleLineTokenEnd(ReadIdentifier(c, cmdState)); + + default: + if (!IsIdentifierInitial(c)) { + ReportError(Errors.InvalidCharacterInExpression, (char)c); + MarkSingleLineTokenEnd(); + return Tokens.InvalidCharacter; + } + + return MarkSingleLineTokenEnd(ReadIdentifier(c, cmdState)); + } + } + + #region Identifiers and Keywords + + // Identifiers: + // [:alpha:_][:identifier:]+ + // Method names: + // [:alpha:_][:identifier:]+[?][^=] + // [:alpha:_][:identifier:]+[!][^=] + // [:alpha:_][:identifier:]+[=][^=~>] + // [:alpha:_][:identifier:]+[=] immediately followed by => + // Keywords + private Tokens ReadIdentifier(int firstCharacter, bool cmdState) { + // the first character already read: + int start = _bufferPos - 1; + SkipVariableName(); + + // reads token suffix (!, ?, =) and returns the the token kind based upon the suffix: + Tokens result = ReadIdentifierSuffix(firstCharacter); + + string identifier = new String(_lineBuffer, start, _bufferPos - start); + + if (_lexicalState != LexicalState.EXPR_DOT) { + if (_lexicalState == LexicalState.EXPR_FNAME) { + _tokenValue.SetSymbol(identifier); + } + + Tokens keyword = StringToKeyword(identifier); + if (keyword != Tokens.None) { + return keyword; + } + } + + if (_lexicalState == LexicalState.EXPR_BEG || + _lexicalState == LexicalState.EXPR_MID || + _lexicalState == LexicalState.EXPR_DOT || + _lexicalState == LexicalState.EXPR_ARG || + _lexicalState == LexicalState.EXPR_CMDARG) { + + if (IsLocalVariable(identifier)) { + _lexicalState = LexicalState.EXPR_END; + } else if (cmdState) { + _lexicalState = LexicalState.EXPR_CMDARG; + } else { + _lexicalState = LexicalState.EXPR_ARG; + } + } else { + _lexicalState = LexicalState.EXPR_END; + } + + _tokenValue.SetSymbol(identifier); + return result; + } + + private Tokens ReadIdentifierSuffix(int firstCharacter) { + int suffix = lookahead(+0); + int c = lookahead(+1); + if ((suffix == '!' || suffix == '?') && c != '=') { + nextc(); + return Tokens.FunctionIdentifier; + } + + if (_lexicalState == LexicalState.EXPR_FNAME && + suffix == '=' && c != '~' && c != '>' && (c != '=' || lookahead(+2) == '>')) { + // include '=' into the token: + nextc(); + // TODO: FunctionIdentifier might be better, seems to not matter because the rules that use it accept FtnIdf as well + return Tokens.Identifier; + } + + // no suffix: + return IsUpperLetter(firstCharacter) ? Tokens.ConstantIdentifier : Tokens.Identifier; + } + + private Tokens StringToKeyword(string/*!*/ identifier) { + switch (identifier) { + case "if": return ReturnKeyword(Tokens.If, Tokens.IfMod, LexicalState.EXPR_BEG); + case "in": return ReturnKeyword(Tokens.In, LexicalState.EXPR_BEG); + case "do": return ReturnDoKeyword(); + case "or": return ReturnKeyword(Tokens.Or, LexicalState.EXPR_BEG); + + case "and": return ReturnKeyword(Tokens.And, LexicalState.EXPR_BEG); + case "end": return ReturnKeyword(Tokens.End, LexicalState.EXPR_END); + case "def": return ReturnKeyword(Tokens.Def, LexicalState.EXPR_FNAME); + case "for": return ReturnKeyword(Tokens.For, LexicalState.EXPR_BEG); + case "not": return ReturnKeyword(Tokens.Not, LexicalState.EXPR_BEG); + case "nil": return ReturnKeyword(Tokens.Nil, LexicalState.EXPR_END); + case "END": return ReturnKeyword(Tokens.UppercaseEnd, LexicalState.EXPR_END); + + case "else": return ReturnKeyword(Tokens.Else, LexicalState.EXPR_BEG); + case "then": return ReturnKeyword(Tokens.Then, LexicalState.EXPR_BEG); + case "case": return ReturnKeyword(Tokens.Case, LexicalState.EXPR_BEG); + case "self": return ReturnKeyword(Tokens.Self, LexicalState.EXPR_END); + case "true": return ReturnKeyword(Tokens.True, LexicalState.EXPR_END); + case "next": return ReturnKeyword(Tokens.Next, LexicalState.EXPR_MID); + case "when": return ReturnKeyword(Tokens.When, LexicalState.EXPR_BEG); + case "redo": return ReturnKeyword(Tokens.Redo, LexicalState.EXPR_END); + + case "alias": return ReturnKeyword(Tokens.Alias, LexicalState.EXPR_FNAME); + case "begin": return ReturnKeyword(Tokens.Begin, LexicalState.EXPR_BEG); + case "break": return ReturnKeyword(Tokens.Break, LexicalState.EXPR_MID); + case "BEGIN": return ReturnKeyword(Tokens.UppercaseBegin, LexicalState.EXPR_END); + case "class": return ReturnKeyword(Tokens.Class, LexicalState.EXPR_CLASS); + case "elsif": return ReturnKeyword(Tokens.Elsif, LexicalState.EXPR_BEG); + case "false": return ReturnKeyword(Tokens.False, LexicalState.EXPR_END); + case "retry": return ReturnKeyword(Tokens.Retry, LexicalState.EXPR_END); + case "super": return ReturnKeyword(Tokens.Super, LexicalState.EXPR_ARG); + case "until": return ReturnKeyword(Tokens.Until, Tokens.UntilMod, LexicalState.EXPR_BEG); + case "undef": return ReturnKeyword(Tokens.Undef, LexicalState.EXPR_FNAME); + case "while": return ReturnKeyword(Tokens.While, Tokens.WhileMod, LexicalState.EXPR_BEG); + case "yield": return ReturnKeyword(Tokens.Yield, LexicalState.EXPR_ARG); + + case "ensure": return ReturnKeyword(Tokens.Ensure, LexicalState.EXPR_BEG); + case "module": return ReturnKeyword(Tokens.Module, LexicalState.EXPR_BEG); + case "rescue": return ReturnKeyword(Tokens.Rescue, Tokens.RescueMod, LexicalState.EXPR_MID); + case "return": return ReturnKeyword(Tokens.Return, LexicalState.EXPR_MID); + case "unless": return ReturnKeyword(Tokens.Unless, Tokens.UnlessMod, LexicalState.EXPR_BEG); + + case "defined?": return ReturnKeyword(Tokens.Defined, LexicalState.EXPR_ARG); + case "__LINE__": return ReturnKeyword(Tokens.Line, LexicalState.EXPR_END); + case "__FILE__": return ReturnKeyword(Tokens.File, LexicalState.EXPR_END); + case "__ENCODING__": + if (_compatibility >= RubyCompatibility.Ruby19) { + return ReturnKeyword(Tokens.Encoding, LexicalState.EXPR_END); + } else { + return Tokens.None; + } + + default: return Tokens.None; + } + } + + private Tokens ReturnKeyword(Tokens keyword, LexicalState state) { + _lexicalState = state; + return keyword; + } + + private Tokens ReturnKeyword(Tokens keywordInExpression, Tokens keywordModifier, LexicalState state) { + Debug.Assert(keywordInExpression != keywordModifier); + + if (_lexicalState == LexicalState.EXPR_BEG) { + _lexicalState = state; + return keywordInExpression; + } else { + _lexicalState = LexicalState.EXPR_BEG; + return keywordModifier; + } + } + + private Tokens ReturnDoKeyword() { + LexicalState oldState = _lexicalState; + _lexicalState = LexicalState.EXPR_BEG; + + // if last conditional opening is a parenthesis: + if (COND_P()) { + return Tokens.LoopDo; + } + + if (CMDARG_P() && oldState != LexicalState.EXPR_CMDARG) { + return Tokens.BlockDo; + } + + if (oldState == LexicalState.EXPR_ENDARG) { + return Tokens.BlockDo; + } + + return Tokens.Do; + } + + #endregion + + #region Comments + + // =begin + // ... + // =end + private bool ReadMultiLineComment() { + if (was_bol() && PeekMultiLineCommentBegin()) { + + while (true) { + _bufferPos = _lineBuffer.Length; + + int c = nextc(); + if (c == -1) { + UnterminatedToken = true; + ReportError(Errors.UnterminatedEmbeddedDocument); + return true; + } + + if (c != '=') { + continue; + } + + if (PeekMultiLineCommentEnd()) { + break; + } + } + + _bufferPos = _lineBuffer.Length; + return true; + } + + return false; + } + + private bool PeekMultiLineCommentBegin() { + int minLength = _bufferPos + 5; + return minLength <= _lineBuffer.Length && + _lineBuffer[_bufferPos + 0] == 'b' && + _lineBuffer[_bufferPos + 1] == 'e' && + _lineBuffer[_bufferPos + 2] == 'g' && + _lineBuffer[_bufferPos + 3] == 'i' && + _lineBuffer[_bufferPos + 4] == 'n' && + (minLength == _lineBuffer.Length || IsWhiteSpace(_lineBuffer[minLength])); + } + + private bool PeekMultiLineCommentEnd() { + int minLength = _bufferPos + 3; + return minLength <= _lineBuffer.Length && + _lineBuffer[_bufferPos + 0] == 'e' && + _lineBuffer[_bufferPos + 1] == 'n' && + _lineBuffer[_bufferPos + 2] == 'd' && + (minLength == _lineBuffer.Length || IsWhiteSpace(_lineBuffer[minLength])); + } + + #endregion + + // Assignment: = + // Operators: == === =~ => + private Tokens ReadEquals() { + switch (_lexicalState) { + case LexicalState.EXPR_FNAME: + case LexicalState.EXPR_DOT: + _lexicalState = LexicalState.EXPR_ARG; + break; + + default: + _lexicalState = LexicalState.EXPR_BEG; + break; + } + + int c = nextc(); + if (c == '=') { + + c = nextc(); + if (c == '=') { + return Tokens.Eqq; + } + + pushback(c); + return Tokens.Eq; + } + + if (c == '~') { + return Tokens.Match; + } else if (c == '>') { + return Tokens.Assoc; + } + + pushback(c); + return (Tokens)'='; + } + + // Operators: + +@ + // Assignments: += + // Literals: +[:number:] + private Tokens ReadPlus(bool whitespaceSeen) { + int c = nextc(); + if (_lexicalState == LexicalState.EXPR_FNAME || _lexicalState == LexicalState.EXPR_DOT) { + + _lexicalState = LexicalState.EXPR_ARG; + if (c == '@') { + return Tokens.Uplus; + } + + pushback(c); + return (Tokens)'+'; + } + + if (c == '=') { + _tokenValue.SetSymbol(Symbols.Plus); + _lexicalState = LexicalState.EXPR_BEG; + return Tokens.Assignment; + } + + if (_lexicalState == LexicalState.EXPR_BEG || _lexicalState == LexicalState.EXPR_MID || + (IS_ARG() && whitespaceSeen && !IsWhiteSpace(c))) { + + if (IS_ARG()) { + ReportWarning(Errors.AmbiguousFirstArgument); + } + + _lexicalState = LexicalState.EXPR_BEG; + if (IsDecimalDigit(c)) { + return ReadUnsignedNumber(c); + } else { + pushback(c); + } + + return Tokens.Uplus; + } + + _lexicalState = LexicalState.EXPR_BEG; + pushback(c); + return (Tokens)'+'; + } + + // Brackets: ( + private Tokens ReadLeftParenthesis(bool whitespaceSeen) { + Tokens result = (Tokens)'('; + + if (_lexicalState == LexicalState.EXPR_BEG || _lexicalState == LexicalState.EXPR_MID) { + result = Tokens.LeftParen; + } else if (whitespaceSeen) { + if (_lexicalState == LexicalState.EXPR_CMDARG) { + result = Tokens.LparenArg; + } else if (_lexicalState == LexicalState.EXPR_ARG) { + ReportWarning(Errors.WhitespaceBeforeArgumentParentheses); + } + } + + COND_PUSH(0); + CMDARG_PUSH(0); + _lexicalState = LexicalState.EXPR_BEG; + return result; + } + + // Instance variables: + // @[:alpha:_][:identifier:]* + // Class variables: + // @@[:alpha:_][:identifier:]* + // At: + // @ + private Tokens ReadInstanceOrClassVariable() { + Tokens result; + + // start right before @/@@, the resulting symbol starts with @/@@ + int start = _bufferPos - 1; + + int c = nextc(); + if (c == '@') { + c = nextc(); + result = Tokens.ClassVariable; + } else { + result = Tokens.InstanceVariable; + } + + if (IsDecimalDigit(c)) { + ReportError(result == Tokens.InstanceVariable ? Errors.InvalidInstanceVariableName : Errors.InvalidClassVariableName, (char)c); + } else if (IsIdentifierInitial(c)) { + SkipVariableName(); + _tokenValue.SetSymbol(GetSymbol(start, _bufferPos - start)); + _lexicalState = LexicalState.EXPR_END; + return result; + } + + pushback(c); + if (result == Tokens.ClassVariable) { + pushback('@'); + } + + return (Tokens)'@'; + } + + // Global variables: + // $[_~*$?!@/\;,.=:<>"] + // $-[:identifier:] + // $[:identifier:] + // Match references: + // $[&`'+] + // $[1-9][0-9]+ + // Dollar: + // $ + private Tokens ReadGlobalVariable() { + _lexicalState = LexicalState.EXPR_END; + + // start right after $, the resulting symbol doesn't contain $ + int start = _bufferPos; + + int c = nextc(); + switch (c) { + case '_': + c = nextc(); + if (IsIdentifier(c)) { + SkipVariableName(); + _tokenValue.SetSymbol(GetSymbol(start, _bufferPos - start)); + return Tokens.GlobalVariable; + } + pushback(c); + return GlobalVariableToken(Symbols.LastInputLine); + + // exceptions: + case '!': return GlobalVariableToken(Symbols.CurrentException); + case '@': return GlobalVariableToken(Symbols.CurrentExceptionBacktrace); + + // options: + case '-': + int length = IsIdentifier(nextc()) ? 2 : 1; + _tokenValue.SetSymbol(GetSymbol(start, length)); + return Tokens.GlobalVariable; + + // others: + case ',': return GlobalVariableToken(Symbols.ItemSeparator); + case ';': return GlobalVariableToken(Symbols.StringSeparator); + case '/': return GlobalVariableToken(Symbols.InputSeparator); + case '\\': return GlobalVariableToken(Symbols.OutputSeparator); + case '*': return GlobalVariableToken(Symbols.CommandLineArguments); + case '$': return GlobalVariableToken(Symbols.CurrentProcessId); + case '?': return GlobalVariableToken(Symbols.ChildProcessExitStatus); + case '=': return GlobalVariableToken(Symbols.IgnoreCaseComparator); + case ':': return GlobalVariableToken(Symbols.LoadPath); + case '"': return GlobalVariableToken(Symbols.LoadedFiles); + case '<': return GlobalVariableToken(Symbols.InputContent); + case '>': return GlobalVariableToken(Symbols.OutputStream); + case '.': return GlobalVariableToken(Symbols.LastInputLineNumber); + + // regex: + case '~': + return GlobalVariableToken(Symbols.MatchData); + + case '&': + _tokenValue.SetInteger(RegexMatchReference.EntireMatch); + return Tokens.MatchReference; + + case '`': + _tokenValue.SetInteger(RegexMatchReference.MatchPrefix); + return Tokens.MatchReference; + + case '\'': + _tokenValue.SetInteger(RegexMatchReference.MatchSuffix); + return Tokens.MatchReference; + + case '+': + _tokenValue.SetInteger(RegexMatchReference.MatchLastGroup); + return Tokens.MatchReference; + + case '0': + c = nextc(); + if (IsIdentifier(c)) { + // $0[A-Za-z0-9_] are invalid: + SkipVariableName(); + ReportError(Errors.InvalidGlobalVariableName, new String(_lineBuffer, start - 1, _bufferPos - start)); + _tokenValue.SetSymbol(Symbols.ErrorVariable); + return Tokens.GlobalVariable; + } + pushback(c); + + return GlobalVariableToken(Symbols.CommandLineProgramPath); + + default: + if (IsDecimalDigit(c)) { + return ReadMatchGroupReferenceVariable(c); + } + + if (IsLetter(c)) { + SkipVariableName(); + _tokenValue.SetSymbol(GetSymbol(start, _bufferPos - start)); + return Tokens.GlobalVariable; + } + + pushback(c); + return (Tokens)'$'; + } + } + + private string/*!*/ GetSymbol(int start, int length) { + return new String(_lineBuffer, start, length); + } + + private Tokens ReadMatchGroupReferenceVariable(int c) { + int start = _bufferPos - 1; + int value = c - '0'; + bool overflow = false; + + while (true) { + c = nextc(); + + if (!IsDecimalDigit(c)) { + pushback(c); + break; + } + + value = unchecked(value * 10 + (c - '0')); + overflow |= (value < 0); + } + + if (overflow) { + ReportError(Errors.MatchGroupReferenceOverflow, new String(_lineBuffer, start, _bufferPos - start)); + } + + _tokenValue.SetInteger(value); + return Tokens.MatchReference; + } + + private Tokens GlobalVariableToken(string/*!*/ symbol) { + _tokenValue.SetSymbol(symbol); + return Tokens.GlobalVariable; + } + + // Assignments: %= + // Operators: % + // Literals: %{... (quotation start) + private Tokens ReadPercent(bool whitespaceSeen) { + int c = nextc(); + + if (_lexicalState == LexicalState.EXPR_BEG || _lexicalState == LexicalState.EXPR_MID) { + return ReadQuotationStart(c); + } + + if (c == '=') { + _tokenValue.SetSymbol(Symbols.Mod); + _lexicalState = LexicalState.EXPR_BEG; + return Tokens.Assignment; + } + + if (IS_ARG() && whitespaceSeen && !IsWhiteSpace(c)) { + return ReadQuotationStart(c); + } + + switch (_lexicalState) { + case LexicalState.EXPR_FNAME: + case LexicalState.EXPR_DOT: + _lexicalState = LexicalState.EXPR_ARG; + break; + + default: + _lexicalState = LexicalState.EXPR_BEG; + break; + } + + pushback(c); + return (Tokens)'%'; + } + + // Brackets: { + private Tokens ReadLeftBrace() { + Tokens result; + + if (IS_ARG() || _lexicalState == LexicalState.EXPR_END) { + result = (Tokens)'{'; // block (primary) + } else if (_lexicalState == LexicalState.EXPR_ENDARG) { + result = Tokens.LbraceArg; // block (expr) + } else { + result = Tokens.Lbrace; // hash + } + + COND_PUSH(0); + CMDARG_PUSH(0); + _lexicalState = LexicalState.EXPR_BEG; + return result; + } + + // Brackets: [ + // Operators: [] []= + private Tokens ReadLeftBracket(bool whitespaceSeen) { + if (_lexicalState == LexicalState.EXPR_FNAME || _lexicalState == LexicalState.EXPR_DOT) { + _lexicalState = LexicalState.EXPR_ARG; + + int c = nextc(); + if (c == ']') { + c = nextc(); + if (c == '=') { + return Tokens.Aset; + } + pushback(c); + return Tokens.Aref; + } + + pushback(c); + return (Tokens)'['; + } + + Tokens result; + if (_lexicalState == LexicalState.EXPR_BEG || _lexicalState == LexicalState.EXPR_MID) { + result = Tokens.Lbrack; + } else if (IS_ARG() && whitespaceSeen) { + result = Tokens.Lbrack; + } else { + result = (Tokens)'['; + } + + _lexicalState = LexicalState.EXPR_BEG; + COND_PUSH(0); + CMDARG_PUSH(0); + return result; + } + + // Operators: ~ ~@ + private Tokens ReadTilde() { + if (_lexicalState == LexicalState.EXPR_FNAME || _lexicalState == LexicalState.EXPR_DOT) { + int c = nextc(); + + // @~ is treated as ~ + if (c != '@') { + pushback(c); + } + } + + switch (_lexicalState) { + case LexicalState.EXPR_FNAME: + case LexicalState.EXPR_DOT: + _lexicalState = LexicalState.EXPR_ARG; + break; + + default: + _lexicalState = LexicalState.EXPR_BEG; + break; + } + + return (Tokens)'~'; + } + + // Assignments: ^= + // Operators: ^ + private Tokens ReadCaret() { + int c = nextc(); + if (c == '=') { + _tokenValue.SetSymbol(Symbols.Xor); + _lexicalState = LexicalState.EXPR_BEG; + return Tokens.Assignment; + } + + switch (_lexicalState) { + case LexicalState.EXPR_FNAME: + case LexicalState.EXPR_DOT: + _lexicalState = LexicalState.EXPR_ARG; + break; + + default: + _lexicalState = LexicalState.EXPR_BEG; + break; + } + + pushback(c); + return (Tokens)'^'; + } + + // Operators: / + // Assignments: /= + // Literals: /... (regex start) + private Tokens ReadSlash(bool whitespaceSeen) { + if (_lexicalState == LexicalState.EXPR_BEG || _lexicalState == LexicalState.EXPR_MID) { + _currentString = new StringContentTokenizer(StringType.RegularExpression | StringType.ExpandsEmbedded, '/'); + _tokenValue.SetStringTokenizer(_currentString); + return Tokens.RegexpBeg; + } + + int c = nextc(); + if (c == '=') { + _tokenValue.SetSymbol(Symbols.Divide); + _lexicalState = LexicalState.EXPR_BEG; + return Tokens.Assignment; + } + + pushback(c); + if (IS_ARG() && whitespaceSeen) { + if (!IsWhiteSpace(c)) { + ReportWarning(Errors.AmbiguousFirstArgument); + _currentString = new StringContentTokenizer(StringType.RegularExpression | StringType.ExpandsEmbedded, '/'); + _tokenValue.SetStringTokenizer(_currentString); + return Tokens.RegexpBeg; + } + } + + switch (_lexicalState) { + case LexicalState.EXPR_FNAME: + case LexicalState.EXPR_DOT: + _lexicalState = LexicalState.EXPR_ARG; + break; + + default: + _lexicalState = LexicalState.EXPR_BEG; + break; + } + + return (Tokens)'/'; + } + + // Operators: :: : + // Literals: :... (symbol start) + private Tokens ReadColon(bool whitespaceSeen) { + int c = nextc(); + if (c == ':') { + if (_lexicalState == LexicalState.EXPR_BEG || _lexicalState == LexicalState.EXPR_MID || + _lexicalState == LexicalState.EXPR_CLASS || (IS_ARG() && whitespaceSeen)) { + + _lexicalState = LexicalState.EXPR_BEG; + return Tokens.LeadingDoubleColon; + } + + _lexicalState = LexicalState.EXPR_DOT; + return Tokens.SeparatingDoubleColon; + } + + if (_lexicalState == LexicalState.EXPR_END || _lexicalState == LexicalState.EXPR_ENDARG || IsWhiteSpace(c)) { + pushback(c); + _lexicalState = LexicalState.EXPR_BEG; + return (Tokens)':'; + } + + switch (c) { + case '\'': + _currentString = new StringContentTokenizer(StringType.Symbol, '\''); + break; + + case '"': + _currentString = new StringContentTokenizer(StringType.Symbol | StringType.ExpandsEmbedded, '"'); + break; + + default: + Debug.Assert(_currentString == null); + pushback(c); + break; + } + + _lexicalState = LexicalState.EXPR_FNAME; + _tokenValue.SetStringTokenizer(_currentString); + return Tokens.Symbeg; + } + + // Assignments: **= *= + // Operators: ** * splat + private Tokens ReadStar(bool whitespaceSeen) { + Tokens result; + int c = nextc(); + + if (c == '*') { + c = nextc(); + if (c == '=') { + _tokenValue.SetSymbol(Symbols.Power); + _lexicalState = LexicalState.EXPR_BEG; + + return Tokens.Assignment; + } + + pushback(c); + result = Tokens.Pow; + } else { + if (c == '=') { + _tokenValue.SetSymbol(Symbols.Multiply); + _lexicalState = LexicalState.EXPR_BEG; + + return Tokens.Assignment; + } + + pushback(c); + + if (IS_ARG() && whitespaceSeen && !IsWhiteSpace(c)) { + //yywarn("`*' interpreted as argument prefix"); + result = Tokens.Star; + } else if (_lexicalState == LexicalState.EXPR_BEG || _lexicalState == LexicalState.EXPR_MID) { + result = Tokens.Star; + } else { + result = (Tokens)'*'; + } + } + + switch (_lexicalState) { + case LexicalState.EXPR_FNAME: + case LexicalState.EXPR_DOT: + _lexicalState = LexicalState.EXPR_ARG; + break; + + default: + _lexicalState = LexicalState.EXPR_BEG; + break; + } + + return result; + } + + // Operators: ! != !~ + private Tokens ReadBang() { + _lexicalState = LexicalState.EXPR_BEG; + + int c = nextc(); + if (c == '=') { + return Tokens.Neq; + } else if (c == '~') { + return Tokens.Nmatch; + } + + pushback(c); + return (Tokens)'!'; + } + + // String: < < + private Tokens TokenizeLessThan(bool whitespaceSeen) { + int c = nextc(); + + if (c == '<' && + _lexicalState != LexicalState.EXPR_END && + _lexicalState != LexicalState.EXPR_DOT && + _lexicalState != LexicalState.EXPR_ENDARG && + _lexicalState != LexicalState.EXPR_CLASS && + (!IS_ARG() || whitespaceSeen)) { + + Tokens token = TokenizeHeredocLabel(); + if (token != Tokens.None) { + return token; + } + } + + switch (_lexicalState) { + case LexicalState.EXPR_FNAME: + case LexicalState.EXPR_DOT: + _lexicalState = LexicalState.EXPR_ARG; + break; + + default: + _lexicalState = LexicalState.EXPR_BEG; + break; + } + + if (c == '=') { + c = nextc(); + if (c == '>') { + MarkSingleLineTokenEnd(); + return Tokens.Cmp; + } + pushback(c); + MarkSingleLineTokenEnd(); + return Tokens.Leq; + } + + if (c == '<') { + c = nextc(); + if (c == '=') { + _tokenValue.SetSymbol(Symbols.LeftShift); + _lexicalState = LexicalState.EXPR_BEG; + MarkSingleLineTokenEnd(); + return Tokens.Assignment; + } + pushback(c); + MarkSingleLineTokenEnd(); + return Tokens.Lshft; + } + + pushback(c); + MarkSingleLineTokenEnd(); + return (Tokens)'<'; + } + + // Assignment: >>= + // Operators: > >= >> + private Tokens ReadGreaterThan() { + switch (_lexicalState) { + case LexicalState.EXPR_FNAME: + case LexicalState.EXPR_DOT: + _lexicalState = LexicalState.EXPR_ARG; + break; + + default: + _lexicalState = LexicalState.EXPR_BEG; + break; + } + + int c = nextc(); + if (c == '=') { + return Tokens.Geq; + } + + if (c == '>') { + c = nextc(); + if (c == '=') { + _tokenValue.SetSymbol(Symbols.RightShift); + _lexicalState = LexicalState.EXPR_BEG; + return Tokens.Assignment; + } + pushback(c); + return Tokens.Rshft; + } + + pushback(c); + return (Tokens)'>'; + } + + // String: `... + // Operator: ` + private Tokens ReadBacktick(bool cmdState) { + if (_lexicalState == LexicalState.EXPR_FNAME) { + _lexicalState = LexicalState.EXPR_END; + return (Tokens)'`'; + } + + if (_lexicalState == LexicalState.EXPR_DOT) { + _lexicalState = (cmdState) ? LexicalState.EXPR_CMDARG : LexicalState.EXPR_ARG; + return (Tokens)'`'; + } + + _currentString = new StringContentTokenizer(StringType.ExpandsEmbedded, '`'); + _tokenValue.SetStringTokenizer(_currentString); + return Tokens.ShellStringBegin; + } + + // Operators: ? (conditional) + // Literals: ?[:char:] ?\[:escape:] + // Errors: ?[:EOF:] + private Tokens ReadQuestionmark() { + if (_lexicalState == LexicalState.EXPR_END || _lexicalState == LexicalState.EXPR_ENDARG) { + _lexicalState = LexicalState.EXPR_BEG; + return (Tokens)'?'; + } + + // ?[:EOF:] + int c = nextc(); + if (c == -1) { + UnterminatedToken = true; + ReportError(Errors.IncompleteCharacter); + return Tokens.EndOfFile; + } + + // TODO: ?x, ?\u1234, ?\u{123456} -> string in 1.9 + // ?[:whitespace:] + if (IsWhiteSpace(c)) { + if (!IS_ARG()) { + int c2 = 0; + switch (c) { + case ' ': c2 = 's'; break; + case '\n': c2 = 'n'; break; + case '\t': c2 = 't'; break; + case '\v': c2 = 'v'; break; + case '\r': c2 = 'r'; break; + case '\f': c2 = 'f'; break; + } + + if (c2 != 0) { + ReportWarning(Errors.InvalidCharacterSyntax, (char)c2); + } + } + pushback(c); + _lexicalState = LexicalState.EXPR_BEG; + return (Tokens)'?'; + } + + // ?[:identifier:] + if ((IsLetterOrDigit(c) || c == '_') && _bufferPos < _lineBuffer.Length && IsIdentifier(_lineBuffer[_bufferPos])) { + pushback(c); + _lexicalState = LexicalState.EXPR_BEG; + return (Tokens)'?'; + } + + // ?\[:escape:] + if (c == '\\') { + // TODO: ?x, ?\u1234, ?\u{123456} -> string in 1.9 + c = ReadEscape(); + } + + // TODO: ?x, ?\u1234, ?\u{123456} -> string in 1.9 + c &= 0xff; + _lexicalState = LexicalState.EXPR_END; + _tokenValue.SetInteger(c); + + return Tokens.Integer; + } + + // Operators: & && + // Assignments: &= + private Tokens ReadAmpersand(bool whitespaceSeen) { + int c = nextc(); + + if (c == '&') { + _lexicalState = LexicalState.EXPR_BEG; + + c = nextc(); + if (c == '=') { + _tokenValue.SetSymbol(Symbols.And); + return Tokens.Assignment; + } + + pushback(c); + return Tokens.BitwiseAnd; + } + + if (c == '=') { + _lexicalState = LexicalState.EXPR_BEG; + _tokenValue.SetSymbol(Symbols.BitwiseAnd); + return Tokens.Assignment; + } + + pushback(c); + + Tokens result; + if (IS_ARG() && whitespaceSeen && !IsWhiteSpace(c)) { + // we are in command argument and there is a whitespace between ampersand: "foo &bar" + ReportWarning(Errors.AmpersandInterpretedAsProcArgument); + result = Tokens.Ampersand; + } else if (_lexicalState == LexicalState.EXPR_BEG || _lexicalState == LexicalState.EXPR_MID) { + result = Tokens.Ampersand; + } else { + result = (Tokens)'&'; + } + + switch (_lexicalState) { + case LexicalState.EXPR_FNAME: + case LexicalState.EXPR_DOT: + _lexicalState = LexicalState.EXPR_ARG; + break; + + default: + _lexicalState = LexicalState.EXPR_BEG; + break; + } + + return result; + } + + // Operators: | || + // Assignments: |= ||= + private Tokens ReadPipe() { + int c = nextc(); + + if (c == '|') { + _lexicalState = LexicalState.EXPR_BEG; + + c = nextc(); + if (c == '=') { + _tokenValue.SetSymbol(Symbols.Or); + _lexicalState = LexicalState.EXPR_BEG; + return Tokens.Assignment; + } + + pushback(c); + return Tokens.BitwiseOr; + } + + if (c == '=') { + _tokenValue.SetSymbol(Symbols.BitwiseOr); + _lexicalState = LexicalState.EXPR_BEG; + return Tokens.Assignment; + } + + if (_lexicalState == LexicalState.EXPR_FNAME || _lexicalState == LexicalState.EXPR_DOT) { + _lexicalState = LexicalState.EXPR_ARG; + } else { + _lexicalState = LexicalState.EXPR_BEG; + } + + pushback(c); + return (Tokens)'|'; + } + + // Operators: . .. ... + // Errors: .[:digit:] + private Tokens ReadDot() { + _lexicalState = LexicalState.EXPR_BEG; + + int c = nextc(); + if (c == '.') { + c = nextc(); + if (c == '.') { + return Tokens.Dot3; + } + pushback(c); + return Tokens.Dot2; + } + + pushback(c); + if (IsDecimalDigit(c)) { + ReportError(Errors.NoFloatingLiteral); + } + + _lexicalState = LexicalState.EXPR_DOT; + return (Tokens)'.'; + } + + // Operators: - @- + // Assignments: -= + // Literals: -... (negative number sign) + private Tokens ReadMinus(bool whitespaceSeen) { + int c = nextc(); + if (_lexicalState == LexicalState.EXPR_FNAME || _lexicalState == LexicalState.EXPR_DOT) { + + _lexicalState = LexicalState.EXPR_ARG; + if (c == '@') { + return Tokens.Uminus; + } + + pushback(c); + return (Tokens)'-'; + } + + if (c == '=') { + _tokenValue.SetSymbol(Symbols.Minus); + _lexicalState = LexicalState.EXPR_BEG; + return Tokens.Assignment; + } + + if (_lexicalState == LexicalState.EXPR_BEG || _lexicalState == LexicalState.EXPR_MID || + (IS_ARG() && whitespaceSeen && !IsWhiteSpace(c))) { + + if (IS_ARG()) { + ReportWarning(Errors.AmbiguousFirstArgument); + } + + _lexicalState = LexicalState.EXPR_BEG; + pushback(c); + if (IsDecimalDigit(c)) { + return Tokens.UminusNum; + } + + return Tokens.Uminus; + } + + _lexicalState = LexicalState.EXPR_BEG; + pushback(c); + return (Tokens)'-'; + } + + // Reads + // [:letter:]* + // and converts it to RegEx options. + private RubyRegexOptions ReadRegexOptions() { + RubyRegexOptions encoding = 0; + RubyRegexOptions options = 0; + + int c; + while (IsLetter(c = nextc())) { + switch (c) { + case 'i': options |= RubyRegexOptions.IgnoreCase; break; + case 'x': options |= RubyRegexOptions.Extended; break; + case 'm': options |= RubyRegexOptions.Multiline; break; + case 'o': + // TODO: Once option not implemented. + break; + + case 'n': encoding |= RubyRegexOptions.FIXED; break; + case 'e': encoding = RubyRegexOptions.EUC; break; + case 's': encoding = RubyRegexOptions.SJIS; break; + case 'u': encoding = RubyRegexOptions.UTF8; break; + + default: + ReportError(Errors.UnknownRegexOption, (char)c); + break; + } + } + pushback(c); + + return options | encoding; + } + + #region Character Escapes + + // \\ \n \t \r \f \v \a \b \s + // \[:octal:] \x[:hexa:] \M-\[:escape:] \M-[:char:] \C-[:escape:] \C-[:char:] \c[:escape:] \c[:char:] \[:char:] + private int ReadEscape() { + int c = nextc(); + switch (c) { + case '\\': return '\\'; + case 'n': return '\n'; + case 't': return '\t'; + case 'r': return '\r'; + case 'f': return '\f'; + case 'v': return '\v'; + case 'a': return '\a'; + case 'e': return 27; + case 'b': return '\b'; + case 's': return ' '; + + case 'x': return ReadHexEscape(); + + case 'M': + c = nextc(); + if (c != '-') { + pushback(c); + return InvalidEscapeCharacter(); + } + + c = nextc(); + if (c == '\\') { + return ReadEscape() | 0x80; + } + + if (c == -1) { + return InvalidEscapeCharacter(); + } + + return (c & 0xff) | 0x80; + + case 'C': + c = nextc(); + if (c != '-') { + pushback(c); + return InvalidEscapeCharacter(); + } + goto case 'c'; + + case 'c': + c = nextc(); + + if (c == '?') { + return 0177; + } + + if (c == -1) { + return InvalidEscapeCharacter(); + } + + if (c == '\\') { + c = ReadEscape(); + } + + return c & 0x9f; + + case -1: + return InvalidEscapeCharacter(); + + default: + if (IsOctalDigit(c)) { + return ReadOctalEscape(c - '0'); + } + return c; + } + } + + private int InvalidEscapeCharacter() { + ReportError(Errors.InvalidEscapeCharacter); + // return != 0 so that additional errors (\0 in a symbol) are not invoked + return '?'; + } + + // Appends escaped regex escape sequence. + private void AppendEscapedRegexEscape(int term) { + int c = nextc(); + + switch (c) { + case '\n': + break; + + case 'x': + tokadd('\\'); + AppendEscapedHexEscape(); + break; + + case 'M': + c = nextc(); + if (c != '-') { + pushback(c); + InvalidEscapeCharacter(); + break; + } + + tokadd('\\'); + tokadd('M'); + tokadd('-'); + + // escaped: + AppendRegularExpressionCompositeEscape(term); + break; + + case 'C': + c = nextc(); + if (c != '-') { + pushback(c); + InvalidEscapeCharacter(); + break; + } + + tokadd('\\'); + tokadd('C'); + tokadd('-'); + + AppendRegularExpressionCompositeEscape(term); + break; + + case 'c': + tokadd('\\'); + tokadd('c'); + AppendRegularExpressionCompositeEscape(term); + break; + + case -1: + InvalidEscapeCharacter(); + break; + + default: + if (IsOctalDigit(c)) { + tokadd('\\'); + AppendEscapedOctalEscape(); + break; + } + + if (c != '\\' || c != term) { + tokadd('\\'); + } + tokadd((char)c); + break; + } + } + + private void AppendRegularExpressionCompositeEscape(int term) { + int c = nextc(); + if (c == '\\') { + AppendEscapedRegexEscape(term); + } else if (c == -1) { + InvalidEscapeCharacter(); + } else { + tokadd((char)c); + } + } + + private void AppendEscapedOctalEscape() { + int start = _bufferPos - 1; + ReadOctalEscape(0); + + Debug.Assert(IsOctalDigit(_lineBuffer[start])); // first digit + _tokenString.Append(_lineBuffer, start, _bufferPos - start); + } + + private void AppendEscapedHexEscape() { + int start = _bufferPos - 1; + ReadHexEscape(); + + Debug.Assert(_lineBuffer[start] == 'x'); + _tokenString.Append(_lineBuffer, start, _bufferPos - start); + } + + private void AppendEscapedUnicode() { + int start = _bufferPos - 1; + + if (peekc() == '{') { + ReadUnicodeCodePoint(); + } else { + ReadUnicodeEscape(); + } + + Debug.Assert(_lineBuffer[start] == 'u'); + _tokenString.Append(_lineBuffer, start, _bufferPos - start); + } + + // Reads octal number of at most 3 digits. + // Reads at most 2 octal digits as the value of the first digit is in "value". + private int ReadOctalEscape(int value) { + if (IsOctalDigit(peekc())) { + value = (value << 3) | (nextc() - '0'); + + if (IsOctalDigit(peekc())) { + value = (value << 3) | (nextc() - '0'); + } + } + return value; + } + + // Reads hexadecimal number of at most 2 digits. + private int ReadHexEscape() { + int value = ToDigit(peekc()); + if (value < 16) { + nextc(); + int digit = ToDigit(peekc()); + if (digit < 16) { + value = (value << 4) | digit; + nextc(); + } + + return value; + } else { + return InvalidEscapeCharacter(); + } + } + + // Peeks exactly 4 hexadecimal characters (\uFFFF). + private int ReadUnicodeEscape() { + int d4 = ToDigit(lookahead(0)); + int d3 = ToDigit(lookahead(1)); + int d2 = ToDigit(lookahead(2)); + int d1 = ToDigit(lookahead(3)); + + if (d1 >= 16 || d2 >= 16 || d3 >= 16 || d4 >= 16) { + return InvalidEscapeCharacter(); + } + + nextc(); + nextc(); + nextc(); + nextc(); + return (d4 << 12) | (d3 << 8) | (d2 << 4) | d1; + } + + // Reads {at-most-six-hexa-digits} + private int ReadUnicodeCodePoint() { + int c = nextc(); + Debug.Assert(c == '{'); + + int codepoint = 0; + int i = 0; + while (true) { + c = nextc(); + if (i == 6) { + break; + } + + int digit = ToDigit(c); + if (digit >= 16) { + break; + } + + codepoint = (codepoint << 4) | digit; + i++; + } + + if (c != '}') { + pushback(c); + InvalidEscapeCharacter(); + } + + if (codepoint > 0x10ffff) { + ReportError(Errors.TooLargeUnicodeCodePoint); + } + + return codepoint; + } + + // Reads up to 6 hex characters, treats them as a exadecimal code-point value and appends the result to the buffer. + private void AppendUnicodeCodePoint(StringType stringType) { + int codepoint = ReadUnicodeCodePoint(); + + if (codepoint < 0x10000) { + // code-points [0xd800 .. 0xdffff] are not treated as invalid + AppendCharacter(codepoint, stringType); + } else { + codepoint -= 0x10000; + _tokenString.Append((char)((codepoint / 0x400) + 0xd800)); + _tokenString.Append((char)((codepoint % 0x400) + 0xdc00)); + } + } + + #endregion + + #region Strings + + // String: "... + private Tokens ReadDoubleQuote() { + _currentString = new StringContentTokenizer(StringType.ExpandsEmbedded, '"'); + _tokenValue.SetStringTokenizer(_currentString); + return Tokens.StringBeg; + } + + // String: '... + private Tokens ReadSingleQuote() { + _currentString = new StringContentTokenizer(StringType.Default, '\''); + _tokenValue.SetStringTokenizer(_currentString); + return Tokens.StringBeg; + } + + // returns last character read + private int ReadStringContent(StringType stringType, int terminator, int openingParenthesis, + ref int nestingLevel, ref bool hasUnicodeEscape) { + + while (true) { + int c = nextc(); + if (c == -1) { + return -1; + } + + if (openingParenthesis != 0 && c == openingParenthesis) { + nestingLevel++; + } else if (c == terminator) { + if (nestingLevel == 0) { + pushback(c); + return c; + } + nestingLevel--; + } else if (((stringType & StringType.ExpandsEmbedded) != 0) && c == '#' && _bufferPos < _lineBuffer.Length) { + int c2 = _lineBuffer[_bufferPos]; + if (c2 == '$' || c2 == '@' || c2 == '{') { + pushback(c); + return c; + } + } else if ((stringType & StringType.Words) != 0 && IsWhiteSpace(c)) { + pushback(c); + return c; + } else if (c == '\\') { + c = nextc(); + + if (c == '\n') { + if ((stringType & StringType.Words) == 0) { + if ((stringType & StringType.ExpandsEmbedded) != 0) { + continue; + } + tokadd('\\'); + } + } else if (c == '\\') { + if ((stringType & StringType.RegularExpression) != 0) { + tokadd('\\'); + } + } else if ((stringType & StringType.RegularExpression) != 0) { + // \uFFFF, \u{codepoint} + if (c == 'u' && _compatibility >= RubyCompatibility.Ruby19) { + hasUnicodeEscape = true; + tokadd('\\'); + AppendEscapedUnicode(); + } else { + pushback(c); + AppendEscapedRegexEscape(terminator); + } + continue; + } else if ((stringType & StringType.ExpandsEmbedded) != 0) { + // \uFFFF, \u{codepoint} + if (c == 'u' && _compatibility >= RubyCompatibility.Ruby19) { + hasUnicodeEscape = true; + if (peekc() == '{') { + AppendUnicodeCodePoint(stringType); + continue; + } else { + c = ReadUnicodeEscape(); + } + } else { + pushback(c); + c = ReadEscape(); + } + } else if ((stringType & StringType.Words) != 0 && IsWhiteSpace(c)) { + /* ignore backslashed spaces in %w */ + } else if (c != terminator && !(openingParenthesis != 0 && c == openingParenthesis)) { + tokadd('\\'); + } + } + + AppendCharacter(c, stringType); + } + } + + private void AppendCharacter(int c, StringType stringType) { + if (c == 0 && (stringType & StringType.Symbol) != 0) { + ReportError(Errors.NullCharacterInSymbol); + } else { + _tokenString.Append((char)c); + } + } + + // + // returns tokens: + // - StringEnd/RegexEnd ... string/regex closed + // - (Tokens)' ' ... space in word list + // - StringEmbeddedVariableBegin ... #$, #@ (start of an embedded global/instance variable) + // - StringEmbeddedCodeBegin ... #{ (start of an embedded expression) + // - StringContent ... string data + // + internal Tokens TokenizeString(StringContentTokenizer/*!*/ info) { + StringType stringKind = info.Properties; + bool whitespaceSeen = false; + + // final separator in the list of words (see grammar): + if (stringKind == StringType.FinalWordSeparator) { + MarkTokenStart(); + MarkSingleLineTokenEnd(); + return Tokens.StringEnd; + } + + int c = peekc(); + MarkTokenStart(); + + // unterminated string (error recovery is slightly different from MRI): + if (c == -1) { + ReportError(Errors.UnterminatedString); + UnterminatedToken = true; + MarkSingleLineTokenEnd(); + return Tokens.StringEnd; + } + + c = nextc(); + + // skip whitespace in word list: + if ((stringKind & StringType.Words) != 0 && IsWhiteSpace(c)) { + do { + c = nextc(); + } while (IsWhiteSpace(c)); + + whitespaceSeen = true; + } + + // end of the top-level string: + if (c == info.TerminatingCharacter && info.NestingLevel == 0) { + + // end of words: + if ((stringKind & StringType.Words) != 0) { + // final separator in the list of words (see grammar): + info.Properties = StringType.FinalWordSeparator; + MarkMultiLineTokenEnd(); + return Tokens.WordSeparator; + } + + // end of regex: + if ((stringKind & StringType.RegularExpression) != 0) { + _tokenValue.SetRegexOptions(ReadRegexOptions()); + MarkSingleLineTokenEnd(); + return Tokens.RegexpEnd; + } + + // end of string/symbol: + MarkSingleLineTokenEnd(); + return Tokens.StringEnd; + } + + // word separator: + if (whitespaceSeen) { + pushback(c); + MarkMultiLineTokenEnd(); + return Tokens.WordSeparator; + } + + newtok(); + + // start of #$variable, #@variable, #{expression} in a string: + if ((stringKind & StringType.ExpandsEmbedded) != 0 && c == '#') { + c = nextc(); + switch (c) { + case '$': + case '@': + pushback(c); + MarkSingleLineTokenEnd(); + return StringEmbeddedVariableBegin(); + + case '{': + MarkSingleLineTokenEnd(); + return StringEmbeddedCodeBegin(); + } + tokadd('#'); + } + + pushback(c); + bool hasUnicodeEscape = false; + + int nestingLevel = info.NestingLevel; + ReadStringContent(stringKind, info.TerminatingCharacter, info.OpeningParenthesis, ref nestingLevel, ref hasUnicodeEscape); + info.NestingLevel = nestingLevel; + + _tokenValue.SetString(tok(), hasUnicodeEscape); + MarkMultiLineTokenEnd(); + return Tokens.StringContent; + } + + #endregion + + #region Heredoc + + private Tokens TokenizeHeredocLabel() { + int term; + StringType stringType = StringType.Default; + + int c = nextc(); + if (c == '-') { + c = nextc(); + stringType = StringType.IndentedHeredoc; + } + + string label; + if (c == '\'' || c == '"' || c == '`') { + if (c != '\'') { + stringType |= StringType.ExpandsEmbedded; + } + + // do not include quotes: + int start = _bufferPos; + term = c; + + while (true) { + c = nextc(); + if (c == -1) { + UnterminatedToken = true; + ReportError(Errors.UnterminatedHereDocIdentifier); + c = term; + break; + } + + if (c == term) { + break; + } + + // MRI doesn't do this, it continues reading the label and includes \n into it. + // The label cannot be matched with the end label (only single-line comparison is done), so it's better to report error here + // Allowing \n in label requires the token to be multi-line. + if (c == '\n') { + pushback(c); + ReportError(Errors.UnterminatedHereDocIdentifier); + c = term; + break; + } + } + + label = new String(_lineBuffer, start, _bufferPos - start - 1); + } else if (IsIdentifier(c)) { + term = '"'; + stringType |= StringType.ExpandsEmbedded; + + int start = _bufferPos - 1; + SkipVariableName(); + label = new String(_lineBuffer, start, _bufferPos - start); + } else { + pushback(c); + if ((stringType & StringType.IndentedHeredoc) != 0) { + pushback('-'); + } + return Tokens.None; + } + + // note that if we allow \n in the label we must change this to multi-line token! + MarkSingleLineTokenEnd(); + + // skip the rest of the line (the content is stored in heredoc string terminal and tokenized upon restore) + int resume = _bufferPos; + _bufferPos = _lineBuffer.Length; + _currentString = new HeredocTokenizer(stringType, label, resume, _lineBuffer, _currentLine, _currentLineIndex); + _tokenValue.SetStringTokenizer(_currentString); + + return term == '`' ? Tokens.ShellStringBegin : Tokens.StringBeg; + } + + private void HeredocRestore(HeredocTokenizer/*!*/ here) { + _lineBuffer = here.ResumeLine; + _bufferPos = here.ResumePosition; + _heredocEndLine = _currentLine; + _heredocEndLineIndex = _currentLineIndex; + _currentLine = here.FirstLine; + _currentLineIndex = here.FirstLineIndex; + } + + internal Tokens TokenizeHeredoc(HeredocTokenizer/*!*/ heredoc) { + StringType stringKind = heredoc.Properties; + bool isIndented = (stringKind & StringType.IndentedHeredoc) != 0; + + int c = peekc(); + MarkTokenStart(); + + if (c == -1) { + ReportError(Errors.UnterminatedHereDoc, heredoc.Label); + MarkSingleLineTokenEnd(); + HeredocRestore(heredoc); + UnterminatedToken = true; + return Tokens.StringEnd; + } + + // label reached - it becomes a string-end token: + // (note that label is single line, MRI allows multiline, but such label is never matched) + if (is_bol() && LineContentEquals(heredoc.Label, isIndented)) { + + // TODO: reads the entire label: + do { + c = nextc(); + } while (c != '\n' && c != -1); + pushback(c); + + MarkSingleLineTokenEnd(); + HeredocRestore(heredoc); + + // Zero-width token end immediately follows the heredoc opening label. + // Prevents parser confusion when merging locations. + // + // [< 0) { + switch (_lineBuffer[end - 1]) { + case '\n': + if (--end == 0 || _lineBuffer[end - 1] != '\r') { + end++; + break; + } + --end; + break; + + case '\r': + --end; + break; + } + } + + result.Append(_lineBuffer, 0, end); + + if (end < _lineBuffer.Length) { + result.Append('\n'); + } + + _bufferPos = _lineBuffer.Length; + + // peekc forces a new line load: + if (peekc() == -1) { + // eof reached before end of heredoc: + return result; + } + + } while (!LineContentEquals(heredoc.Label, isIndented)); + + // return to the end of line, next token will be StringEnd spanning over the end-of-heredoc label: + _bufferPos = 0; + return result; + } + + private Tokens TokenizeExpandingHeredocContent(HeredocTokenizer/*!*/ heredoc) { + newtok(); + int c = nextc(); + + if (c == '#') { + c = nextc(); + + switch (c) { + case '$': + case '@': + pushback(c); + MarkSingleLineTokenEnd(); + return StringEmbeddedVariableBegin(); + + case '{': + MarkSingleLineTokenEnd(); + return StringEmbeddedCodeBegin(); + } + + tokadd('#'); + } + + bool isIndented = (heredoc.Properties & StringType.IndentedHeredoc) != 0; + + pushback(c); + bool hasUnicodeEscape = false; + + do { + // read string content upto the end of the line: + int tmp = 0; + c = ReadStringContent(heredoc.Properties, '\n', 0, ref tmp, ref hasUnicodeEscape); + + // stop reading on end-of-file or just before an embedded expression: #$, #$, #{ + if (c != '\n') { + break; + } + + // adds \n + tokadd((char)nextc()); + + // first char on the next line: + if (peekc() == -1) { + break; + } + + } while (!LineContentEquals(heredoc.Label, isIndented)); + + _tokenValue.SetString(tok(), hasUnicodeEscape); + MarkMultiLineTokenEnd(); + return Tokens.StringContent; + } + + #endregion + + // Quotation start: + // %[QqWwxrs]?[^:alpha-numeric:] + private Tokens ReadQuotationStart(int c) { + StringType type; + Tokens token; + int terminator; + + // c is the character following % + switch (c) { + case 'Q': + type = StringType.ExpandsEmbedded; + token = Tokens.StringBeg; + terminator = nextc(); + break; + + case 'q': + type = StringType.Default; + token = Tokens.StringBeg; + terminator = nextc(); + break; + + case 'W': + type = StringType.Words | StringType.ExpandsEmbedded; + token = Tokens.WordsBeg; + terminator = nextc(); + SkipWhitespace(); + break; + + case 'w': + type = StringType.Words; + token = Tokens.VerbatimWordsBegin; + terminator = nextc(); + SkipWhitespace(); + break; + + case 'x': + type = StringType.ExpandsEmbedded; + token = Tokens.ShellStringBegin; + terminator = nextc(); + break; + + case 'r': + type = StringType.RegularExpression | StringType.ExpandsEmbedded; + token = Tokens.RegexpBeg; + terminator = nextc(); + break; + + case 's': + type = StringType.Symbol; + token = Tokens.Symbeg; + terminator = nextc(); + _lexicalState = LexicalState.EXPR_FNAME; + break; + + default: + type = StringType.ExpandsEmbedded; + token = Tokens.StringBeg; + terminator = c; + break; + } + + int parenthesis = terminator; + switch (terminator) { + case -1: + UnterminatedToken = true; + ReportError(Errors.UnterminatedQuotedString); + return Tokens.EndOfFile; + + case '(': terminator = ')'; break; + case '{': terminator = '}'; break; + case '[': terminator = ']'; break; + case '<': terminator = '>'; break; + + default: + if (IsLetterOrDigit(terminator)) { + pushback(c); + ReportError(Errors.UnknownQuotedStringType); + return (Tokens)'%'; + } + parenthesis = 0; + break; + } + + _currentString = new StringContentTokenizer(type, (char)terminator, (char)parenthesis); + _tokenValue.SetStringTokenizer(_currentString); + return token; + } + + #region Numbers + + private enum NumericCharKind { + None, + Digit, + Underscore + } + + // INTEGER: + // [1-9]([0-9_]*[1-9])? + // 0([0-7_]*[0-7])? + // 0[xX][0-9a-fA-F]([0-9a-fA-F_]*[0-9a-fA-F])? + // 0[dD][0-9]([0-9_]*[0-9])? + // 0[bB][01]([01_]*[01])? + // 0[oO][0-7]([0-7_]*[0-7])? + // + // FLOAT: + // (0|[1-9]([0-9_]*[0-9])?)[.][0-9_]*[0-9]([eE][+-]?[0-9]([0-9_]*[0-9])?) + // + // Takes the first decimal digit of the number. + // + private Tokens ReadUnsignedNumber(int c) { + _lexicalState = LexicalState.EXPR_END; + + if (c == '0') { + switch (peekc()) { + case 'x': + case 'X': + nextc(); + return ReadInteger(16, NumericCharKind.None); + + case 'b': + case 'B': + nextc(); + return ReadInteger(2, NumericCharKind.None); + + case 'o': + case 'O': + nextc(); + return ReadInteger(8, NumericCharKind.None); + + case 'd': + case 'D': + nextc(); + return ReadInteger(10, NumericCharKind.None); + + case 'e': + case 'E': { + // 0e[+-]... + int sign; + int start = _bufferPos - 1; + + nextc(); + if (TryReadExponentSign(out sign)) { + return ReadDoubleExponent(start, sign); + } + + pushback('e'); + _tokenValue.SetInteger(0); + return Tokens.Integer; + } + + case '.': + // 0. + nextc(); + if (IsDecimalDigit(peekc())) { + return ReadDouble(_bufferPos - 2); + } + pushback('.'); + + _tokenValue.SetInteger(0); + return Tokens.Integer; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '_': + // the previous character is '0' digit: + return ReadInteger(8, NumericCharKind.Digit); + + case '8': + case '9': + ReportError(Errors.IllegalOctalDigit); + // treat the number as decimal + return ReadInteger(10, NumericCharKind.Digit); + + default: + _tokenValue.SetInteger(0); + return Tokens.Integer; + } + } + + return ReadDecimalNumber(c); + } + + // OCTAL: [0-7]([0-7_]*[0-7])? + // HEXA: [0-9a-fA-F]([0-9a-fA-F_]*[0-9a-fA-F])? + // BINARY: [01]([01_]*[01])? + // DECIMAL: [0-9]([0-9_]*[0-9])? + // + // prev ... previous character: either '0' for octals or -1 for 0[xbdo] + private Tokens ReadInteger(int @base, NumericCharKind prev) { + Debug.Assert(prev == NumericCharKind.None || prev == NumericCharKind.Digit); + Debug.Assert(@base <= 16); + long integer = 0; + int numberStartIndex = _bufferPos; + int underscoreCount = 0; + + while (true) { + int c = nextc(); + int digit = ToDigit(c); + + if (digit < @base) { + integer = integer * @base + digit; + prev = NumericCharKind.Digit; + + if (integer > Int32.MaxValue) { + return ReadBigNumber(integer, @base, numberStartIndex, underscoreCount, false); + } + + } else { + if (prev != NumericCharKind.Digit) { + if (prev == NumericCharKind.Underscore) { + ReportError(Errors.TrailingUnderscoreInNumber); + } else { + ReportError(Errors.NumericLiteralWithoutDigits); + } + } else if (c == '_') { + prev = NumericCharKind.Underscore; + underscoreCount++; + continue; + } + + if (c == '.' && IsDecimalDigit(peekc())) { + ReportWarning(Errors.NoFloatingLiteral); + } + + pushback(c); + _tokenValue.SetInteger((int)integer); + return Tokens.Integer; + } + } + } + + // INTEGER: + // [1-9]([0-9_]*[1-9])? + // + // FLOAT: + // [1-9]([0-9_]*[0-9])?[.][0-9_]*[0-9]([eE][+-]?[0-9]([0-9_]*[0-9])?) + private Tokens ReadDecimalNumber(int c) { + Debug.Assert(IsDecimalDigit(c) && c != '0'); + + // the first character of the number already read: + int numberStartIndex = _bufferPos - 1; + + int underscoreCount = 0; + long integer = 0; + NumericCharKind prev = NumericCharKind.None; + + while (true) { + int sign; + + if (IsDecimalDigit(c)) { + prev = NumericCharKind.Digit; + integer = integer * 10 + (c - '0'); + if (integer > Int32.MaxValue) { + return ReadBigNumber(integer, 10, numberStartIndex, underscoreCount, true); + } + + } else if (prev == NumericCharKind.Underscore) { + + ReportError(Errors.TrailingUnderscoreInNumber); + pushback(c); + _tokenValue.SetInteger((int)integer); + return Tokens.Integer; + + } else if ((c == 'e' || c == 'E') && TryReadExponentSign(out sign)) { + + return ReadDoubleExponent(numberStartIndex, sign); + + } else if (c == '_') { + + underscoreCount++; + prev = NumericCharKind.Underscore; + + } else { + + if (c == '.' && IsDecimalDigit(peekc())) { + return ReadDouble(numberStartIndex); + } + + pushback(c); + _tokenValue.SetInteger((int)integer); + return Tokens.Integer; + } + + c = nextc(); + } + } + private bool TryReadExponentSign(out int sign) { + int s = peekc(); + if (s == '-') { + nextc(); + sign = -1; + } else if (s == '+') { + nextc(); + sign = +1; + } else { + sign = +1; + } + + if (IsDecimalDigit(peekc())) { + return true; + } + + if (s == '-') { + ReportError(Errors.TrailingMinusInNumber); + pushback('-'); + } else if (s == '+') { + ReportError(Errors.TrailingPlusInNumber); + pushback('+'); + } else { + ReportError(Errors.TrailingEInNumber); + } + + return false; + } + + // OCTAL: [0-7]([0-7_]*[0-7])? + // HEXA: [0-9a-fA-F]([0-9a-fA-F_]*[0-9a-fA-F])? + // BINARY: [01]([01_]*[01])? + // DECIMAL: [0-9]([0-9_]*[0-9])? + // FLOAT: [1-9]([0-9_]*[0-9])?[.][0-9_]*[0-9]([eE][+-]?[0-9]([0-9_]*[0-9])?) + // + // Previous digit caused an integer overflow. + // numberStartIndex ... index of the first (most significant) digit + // underscoreCount ... number of underscores already read + private Tokens ReadBigNumber(long value, int @base, int numberStartIndex, int underscoreCount, bool allowDouble) { + Debug.Assert(!allowDouble || @base == 10, "Only decimal based doubles supported"); + Debug.Assert(@base <= 16); + + // the previous char is a digit: + NumericCharKind prev = NumericCharKind.Digit; + + while (true) { + int c = nextc(); + int digit = ToDigit(c); + + if (digit < @base) { + prev = NumericCharKind.Digit; + } else { + + if (prev == NumericCharKind.Underscore) { + ReportError(Errors.TrailingUnderscoreInNumber); + } else if (c == '_') { + prev = NumericCharKind.Underscore; + underscoreCount++; + continue; + } else if (allowDouble) { + int sign; + if ((c == 'e' || c == 'E') && TryReadExponentSign(out sign)) { + return ReadDoubleExponent(numberStartIndex, sign); + } else if (c == '.') { + if (IsDecimalDigit(peekc())) { + return ReadDouble(numberStartIndex); + } + } + } + + pushback(c); + + // TODO: store only the digit count, the actual value will be parsed later: + // TODO: skip initial zeros + _bigIntParser.Position = numberStartIndex; + _bigIntParser.Buffer = _lineBuffer; + + BigInteger result = _bigIntParser.Parse(_bufferPos - numberStartIndex - underscoreCount, @base); + + Debug.Assert(value > 0, "Cannot be zero since we are parsing a number greater than Int32.MaxValue"); + + _tokenValue.SetBigInteger(result); + return Tokens.BigInteger; + } + } + } + + // FLOAT - decimal and exponent + // {value.}[0-9_]*[0-9])([eE][+-]?[0-9]([0-9_]*[0-9])?) + private Tokens ReadDouble(int numberStartIndex) { + Debug.Assert(IsDecimalDigit(peekc())); + + NumericCharKind prev = NumericCharKind.None; + while (true) { + int sign; + int c = nextc(); + + if (IsDecimalDigit(c)) { + prev = NumericCharKind.Digit; + } else if ((c == 'e' || c == 'E') && TryReadExponentSign(out sign)) { + return ReadDoubleExponent(numberStartIndex, sign); + } else { + if (prev == NumericCharKind.Underscore) { + ReportError(Errors.TrailingUnderscoreInNumber); + } else if (c == '_') { + prev = NumericCharKind.Underscore; + continue; + } + + pushback(c); + return DecodeDouble(numberStartIndex, _bufferPos); + } + } + } + + // FLOAT - exponent + // [+-]?[0-9]([0-9_]*[0-9])? + private Tokens ReadDoubleExponent(int numberStartIndex, int sign) { + int exponent = 0; + NumericCharKind prev = NumericCharKind.None; + while (true) { + int c = nextc(); + + if (IsDecimalDigit(c)) { + prev = NumericCharKind.Digit; + + // greater exponents evaluate to infinity/zero, we need to keep parsing though: + if (exponent < 10000) { + exponent = exponent * 10 + (c - '0'); + } + } else { + if (prev != NumericCharKind.Digit) { + Debug.Assert(prev == NumericCharKind.Underscore); + ReportError(Errors.TrailingUnderscoreInNumber); + } else if (c == '_') { + prev = NumericCharKind.Underscore; + continue; + } + + pushback(c); + + exponent *= sign; + + // some MRI arbitrary restrictions on the exponent: + if (exponent <= -1021 || exponent >= 1025) { + // TODO: + int start = _currentTokenStart.Column - 1; + ReportWarning(Errors.FloatOutOfRange, new String(_lineBuffer, start, _bufferPos - start).Replace("_", "")); + } + + return DecodeDouble(numberStartIndex, _bufferPos); + } + } + } + + private static bool TryDecodeDouble(char[]/*!*/ str, int first, int end, out double result) { + StringBuilder sb = new StringBuilder(end - first); + sb.Length = end - first; + + int j = 0; + for (int i = first; i < end; i++) { + if (str[i] != '_') { + sb[j++] = str[i]; + } + } + + sb.Length = j; + return Double.TryParse(sb.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out result); + } + + private Tokens DecodeDouble(int first, int end) { + double result; + if (!TryDecodeDouble(_lineBuffer, first, end, out result)) { + result = Double.PositiveInfinity; + } + + _tokenValue.SetDouble(result); + return Tokens.Float; + } + + #endregion + + #region Characters + + public static bool IsDecimalDigit(int c) { + return unchecked((uint)c - '0' <= (uint)'9' - '0'); + } + + public static bool IsOctalDigit(int c) { + return unchecked((uint)c - '0' <= (uint)'7' - '0'); + } + + public static bool IsHexadecimalDigit(int c) { + unchecked { + return IsDecimalDigit(c) || + (uint)c - 'a' <= (uint)'f' - 'a' || + (uint)c - 'A' <= (uint)'F' - 'A'; + } + } + + public static int ToDigit(int c) { + if (IsDecimalDigit(c)) { + return c - '0'; + } + + if (IsLowerLetter(c)) { + return c - 'a' + 10; + } + + if (IsUpperLetter(c)) { + return c - 'A' + 10; + } + + return Int32.MaxValue; + } + + public static bool IsIdentifier(int c) { + return IsIdentifierInitial(c) || IsDecimalDigit(c); + } + + public static bool IsIdentifierInitial(int c) { + return IsLetter(c) || c == '_'; + } + + public static bool IsLetter(int c) { + return IsUpperLetter(c) || IsLowerLetter(c); + } + + public static bool IsLetterOrDigit(int c) { + return IsLetter(c) || IsDecimalDigit(c); + } + + public static bool IsUpperLetter(int c) { + return unchecked((uint)c - 'A' <= (uint)'Z' - 'A'); + } + + public static bool IsLowerLetter(int c) { + return unchecked((uint)c - 'a' <= (uint)'z' - 'a'); + } + + public static bool IsWhiteSpace(int c) { + return IsAsciiWhiteSpace(c); + } + + public static bool IsAsciiWhiteSpace(int c) { + return unchecked(((uint)c - 9 <= (uint)13 - 9) || c == 32); + } + + private static bool IsMethodNameSuffix(int c) { + return IsIdentifier(c) || c == '!' || c == '?' || c == '='; + } + + // Reads [A-Za-z0-9_]* + private void SkipVariableName() { + int c; + do { c = nextc(); } while (IsIdentifier(c)); + pushback(c); + } + + private void SkipWhitespace() { + int c; + do { c = nextc(); } while (IsWhiteSpace(c)); + pushback(c); + } + + #endregion + + #region Public API + + private static int nextc(char[]/*!*/ str, ref int i) { + i++; + return (i < str.Length) ? str[i] : -1; + } + + // subsequent _ are not considered error + public static double ParseDouble(char[]/*!*/ str) { + double sign; + int i = -1; + + int c; + do { c = nextc(str, ref i); } while (IsWhiteSpace(c)); + + if (c == '-') { + c = nextc(str, ref i); + if (c == '_') return 0.0; + sign = -1; + } else if (c == '+') { + c = nextc(str, ref i); + if (c == '_') return 0.0; + sign = +1; + } else { + sign = +1; + } + + int start = i; + + while (c == '_' || IsDecimalDigit(c)) { + c = nextc(str, ref i); + } + + if (c == '.') { + c = nextc(str, ref i); + while (c == '_' || IsDecimalDigit(c)) { + c = nextc(str, ref i); + } + } + + // just before the current character: + int end = i; + + if (c == 'e' || c == 'E') { + c = nextc(str, ref i); + if (c == '+' || c == '-') { + c = nextc(str, ref i); + } + + int expEnd = end; + + while (true) { + if (IsDecimalDigit(c)) { + expEnd = i + 1; + } else if (c != '_') { + break; + } + c = nextc(str, ref i); + } + + end = expEnd; + } + + double result; + return TryDecodeDouble(str, start, end, out result) ? result * sign : 0.0; + } + + private const string EncodingHeaderPattern = @"^[#].*?coding\s*[:=]\s*(?[a-z0-9_-]+)"; + + // reads case insensitively, doesn't rewind the reader if the content doesn't match, doesn't read a line unless it starts with '#': + // ([#][!].*(\r|\n|\r\n))? + // [#].*?coding\s*[:=]\s*([a-z0-9_-]+).*(\r|\n|\r\n) + internal static bool TryParseEncodingHeader(TextReader/*!*/ reader, out string encodingName) { + Assert.NotNull(reader); + + encodingName = null; + + if (reader.Peek() != '#') { + return false; + } + + string line = reader.ReadLine(); + + // skip shebang: + if (line.Length > 1 && line[1] == '!') { + if (reader.Peek() != '#') { + return false; + } + line = reader.ReadLine(); + } + + var regex = new Regex(EncodingHeaderPattern, RegexOptions.IgnoreCase); + var match = regex.Match(line); + if (match.Success) { + encodingName = match.Groups["encoding"].Value; + return encodingName.Length > 0; + } + + return false; + } + + public static bool IsConstantName(string name) { + return !String.IsNullOrEmpty(name) + && IsUpperLetter(name[0]) + && IsVariableName(name, 1, 1) + && IsIdentifier(name[name.Length - 1]); + } + + public static bool IsMethodName(string name) { + return !String.IsNullOrEmpty(name) + && IsIdentifierInitial(name[0]) + && IsVariableName(name, 1, 1) + && IsMethodNameSuffix(name[name.Length - 1]); + } + + public static bool IsInstanceVariableName(string name) { + return name != null && name.Length >= 2 + && name[0] == '@' + && IsVariableName(name, 1, 0); + } + + public static bool IsClassVariableName(string name) { + return name != null && name.Length >= 3 + && name[0] == '@' + && name[1] == '@' + && IsVariableName(name, 2, 0); + } + + public static bool IsGlobalVariableName(string name) { + return name != null && name.Length >= 2 + && name[0] == '$' + && IsVariableName(name, 1, 0); + } + + private static bool IsVariableName(string name, int trimStart, int trimEnd) { + for (int i = trimStart; i < name.Length - trimEnd; i++) { + if (!IsIdentifier(name[i])) { + return false; + } + } + + return true; + } + + #endregion + + #region Tokenizer Service + + public override bool SkipToken() { + return GetNextToken() != Tokens.EndOfFile; + } + + public override TokenInfo ReadToken() { + + TokenInfo result = new TokenInfo(); + + Tokens token = GetNextToken(); + result.SourceSpan = TokenSpan; + + switch (token) { + case Tokens.Undef: + case Tokens.Rescue: + case Tokens.Ensure: + case Tokens.If: + case Tokens.Unless: + case Tokens.Then: + case Tokens.Elsif: + case Tokens.Else: + case Tokens.Case: + case Tokens.When: + case Tokens.While: + case Tokens.Until: + case Tokens.For: + case Tokens.Break: + case Tokens.Next: + case Tokens.Redo: + case Tokens.Retry: + case Tokens.In: + case Tokens.Return: + case Tokens.Yield: + case Tokens.Super: + case Tokens.Self: + case Tokens.Nil: + case Tokens.True: + case Tokens.False: + case Tokens.And: + case Tokens.Or: + case Tokens.Not: + case Tokens.IfMod: + case Tokens.UnlessMod: + case Tokens.WhileMod: + case Tokens.UntilMod: + case Tokens.RescueMod: + case Tokens.Alias: + case Tokens.Defined: + case Tokens.Line: + case Tokens.File: + result.Category = TokenCategory.Keyword; + break; + + case Tokens.Def: + case Tokens.Class: + case Tokens.Module: + case Tokens.End: + case Tokens.Begin: + case Tokens.UppercaseBegin: + case Tokens.UppercaseEnd: + case Tokens.Do: + case Tokens.LoopDo: + case Tokens.BlockDo: + result.Category = TokenCategory.Keyword; + result.Trigger = TokenTriggers.MatchBraces; + break; + + case Tokens.Uplus: + case Tokens.Uminus: + case Tokens.UminusNum: + case Tokens.Pow: + case Tokens.Cmp: + case Tokens.Eq: + case Tokens.Eqq: + case Tokens.Neq: + case Tokens.Geq: + case Tokens.Leq: + case Tokens.BitwiseAnd: + case Tokens.BitwiseOr: + case Tokens.Match: // =~ + case Tokens.Nmatch: // !~ + case Tokens.Dot2: // .. + case Tokens.Dot3: // ... + case Tokens.Aref: // [] + case Tokens.Aset: // []= + case Tokens.Lshft: + case Tokens.Rshft: + case Tokens.Assoc: + case Tokens.Star: + case Tokens.Ampersand: + case Tokens.Assignment: + result.Category = TokenCategory.Operator; + break; + + case Tokens.SeparatingDoubleColon: + case Tokens.LeadingDoubleColon: + result.Category = TokenCategory.Delimiter; + result.Trigger = TokenTriggers.MemberSelect; + break; + + case Tokens.Lbrack: // [ + case Tokens.Lbrace: // { + case (Tokens)'{': + case Tokens.LbraceArg: // { + case (Tokens)'}': + case (Tokens)']': + case (Tokens)'|': + result.Category = TokenCategory.Grouping; + result.Trigger = TokenTriggers.MatchBraces; + break; + + case Tokens.LeftParen: // ( + case Tokens.LparenArg: // ( + result.Category = TokenCategory.Grouping; + result.Trigger = TokenTriggers.MatchBraces | TokenTriggers.ParameterStart; + break; + + case (Tokens)')': + result.Category = TokenCategory.Grouping; + result.Trigger = TokenTriggers.MatchBraces | TokenTriggers.ParameterEnd; + break; + + case (Tokens)',': + result.Category = TokenCategory.Delimiter; + result.Trigger = TokenTriggers.ParameterNext; + break; + + case (Tokens)'.': + result.Category = TokenCategory.Delimiter; + result.Trigger = TokenTriggers.MemberSelect; + break; + + case Tokens.StringEnd: + result.Category = TokenCategory.StringLiteral; + break; + + case Tokens.StringEmbeddedVariableBegin: // # in string followed by @ or $ + case Tokens.StringEmbeddedCodeBegin: // # in string followed by { + result.Category = TokenCategory.Delimiter; + break; + + case Tokens.Identifier: + case Tokens.FunctionIdentifier: + case Tokens.GlobalVariable: + case Tokens.InstanceVariable: + case Tokens.ConstantIdentifier: + case Tokens.ClassVariable: + case Tokens.MatchReference: + result.Category = TokenCategory.Identifier; + break; + + case Tokens.Integer: + case Tokens.Float: + result.Category = TokenCategory.NumericLiteral; + break; + + case Tokens.StringContent: + case Tokens.StringBeg: + case Tokens.ShellStringBegin: + case Tokens.Symbeg: + case Tokens.WordsBeg: + case Tokens.VerbatimWordsBegin: + case Tokens.RegexpBeg: + case Tokens.RegexpEnd: + // TODO: distingush various kinds of string content (regex, string, heredoc) + result.Category = TokenCategory.StringLiteral; + break; + + case (Tokens)'#': + result.Category = TokenCategory.LineComment; + break; + + case Tokens.EndOfFile: + result.Category = TokenCategory.EndOfStream; + break; + + case (Tokens)'\n': + case Tokens.EndOfLine: + case Tokens.Whitespace: + result.Category = TokenCategory.WhiteSpace; + break; + + case Tokens.SingleLineComment: + result.Category = TokenCategory.LineComment; + break; + + case Tokens.MultiLineComment: + result.Category = TokenCategory.Comment; + break; + + case Tokens.Error: + case Tokens.InvalidCharacter: + result.Category = TokenCategory.Error; + break; + + default: + result.Category = TokenCategory.None; + break; + } + + return result; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Parser/VariableFactory.cs b/merlin/main/languages/ruby/Ruby/Compiler/Parser/VariableFactory.cs new file mode 100644 index 0000000000..d2fd47343d --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Parser/VariableFactory.cs @@ -0,0 +1,120 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler.Ast; + +namespace IronRuby.Compiler { + internal static class VariableFactory { + public const int Identifier = 0; + public const int Instance = 1; + public const int Global = 2; + public const int Constant = 3; + public const int Class = 4; + public const int Nil = 5; + public const int Self = 6; + public const int True = 7; + public const int False = 8; + public const int File = 9; + public const int Line = 10; + public const int Encoding = 11; + + internal static Expression/*!*/ MakeRead(int kind, Parser/*!*/ parser, string name, SourceSpan location) { + switch (kind) { + case Identifier: + return (Expression)parser.CurrentScope.ResolveVariable(name) ?? new MethodCall(null, name, null, null, location); + + case Instance: + return new InstanceVariable(name, location); + + case Global: + return new GlobalVariable(name, location); + + case Constant: + return new ConstantVariable(name, location); + + case Class: + return new ClassVariable(name, location); + + case Nil: + return Literal.Nil(location); + + case Self: + return new SelfReference(location); + + case True: + return Literal.True(location); + + case False: + return Literal.False(location); + + case File: + return parser.GetCurrentFileExpression(location); + + case Line: + return Literal.Integer(parser.Tokenizer.TokenSpan.Start.Line, location); + + case Encoding: + return new EncodingExpression(location); + } + + throw Assert.Unreachable; + } + + internal static LeftValue/*!*/ MakeLeftValue(int kind, Parser/*!*/ parser, string name, SourceSpan location) { + switch (kind) { + case Identifier: + return parser.CurrentScope.ResolveOrAddVariable(name, location); + + case Instance: + return new InstanceVariable(name, location); + + case Global: + return new GlobalVariable(name, location); + + case Constant: + return new ConstantVariable(name, location); + + case Class: + return new ClassVariable(name, location); + + case Nil: + return parser.CannotAssignError("nil", location); + + case Self: + return parser.CannotAssignError("self", location); + + case True: + return parser.CannotAssignError("true", location); + + case False: + return parser.CannotAssignError("false", location); + + case File: + return parser.CannotAssignError("__FILE__", location); + + case Line: + return parser.CannotAssignError("__LINE__", location); + + case Encoding: + return parser.CannotAssignError("__ENCODING__", location); + } + + return null; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/ReflectionCache.Generated.cs b/merlin/main/languages/ruby/Ruby/Compiler/ReflectionCache.Generated.cs new file mode 100644 index 0000000000..62b172241f --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/ReflectionCache.Generated.cs @@ -0,0 +1,309 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using IronRuby.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Compiler { + internal static partial class Methods { + private static MethodInfo _IsDefinedClassVariable, _SetObjectClassVariable, _SetClassVariable, _GetInstanceData, _DeserializeObject, _SerializeObject, _HookupEvent, _CreateDelegateFromProc, _CreateDelegateFromMethod, _X, _UpdateProfileTicks, _CreateRfcForMethod, _BlockRetry, _MethodRetry, _EvalRetry, _BlockBreak, _MethodBreak, _EvalBreak, _MethodNext, + _EvalNext, _MethodRedo, _EvalRedo, _BlockReturn, _EvalReturn, _BlockYield, _MethodYield, _EvalYield, _MethodProcCall, _CanRescue, _IsRetrySingleton, _PropagateRetrySingleton, _GetRetrySingleton, _GetCurrentMatchData, _GetCurrentMatchLastGroup, _GetCurrentMatchPrefix, _GetCurrentMatchSuffix, _MatchLastInputLine, _MatchString, _CreateRegexB, + _CreateRegexU, _CreateRegexE, _CreateRegexM, _CreateRegexBM, _CreateRegexUM, _CreateRegexEM, _CreateRegexMB, _CreateRegexMU, _CreateRegexME, _CreateRegexMM, _CreateRegexN, _CreateMutableStringB, _CreateMutableStringU, _CreateMutableStringE, _CreateMutableStringM, _CreateMutableStringBM, _CreateMutableStringUM, _CreateMutableStringEM, _CreateMutableStringMB, _CreateMutableStringMU, + _CreateMutableStringME, _CreateMutableStringMM, _CreateMutableStringN, _CreateSymbolB, _CreateSymbolU, _CreateSymbolE, _CreateSymbolM, _CreateSymbolBM, _CreateSymbolUM, _CreateSymbolEM, _CreateSymbolMB, _CreateSymbolMU, _CreateSymbolME, _CreateSymbolMM, _CreateSymbolN, _CreateEncoding, _IsTrue, _IsFalse, _GetCurrentException, _SetCurrentExceptionAndStackTrace, + _SetCurrentException, _CompareException, _CompareSplattedExceptions, _CompareDefaultException, _GetDefaultExceptionMessage, _MakeWrongNumberOfArgumentsError, _MakeTopLevelSuperException, _MakeMissingSuperException, _MakeInvalidArgumentTypesError, _IsSuperCallTarget, _CreateInclusiveRange, _CreateExclusiveRange, _AllocateStructInstance, _CreateStructInstance, _GetMetaObject, _ToProcValidator, _ToStringValidator, _ToSValidator, _ToSymbolValidator, _ConvertSymbolIdToSymbol, + _ConvertFixnumToSymbol, _ConvertMutableStringToSymbol, _ToRegexValidator, _ToArrayValidator, _ToFixnumValidator, _CreateTypeConversionError, _ConvertBignumToFixnum, _GetInstanceVariable, _IsDefinedInstanceVariable, _SetInstanceVariable, _GetObjectClassVariable, _GetClassVariable, _TryGetObjectClassVariable, _TryGetClassVariable, _IsDefinedObjectClassVariable, _IsProcConverterTarget, _CreateBfcForYield, _CreateBfcForMethodProcCall, _CreateBfcForProcCall, _CreateBfcForLibraryMethod, + _LeaveProcConverter, _CreateMainTopLevelScope, _CreateTopLevelScope, _CreateWrappedTopLevelScope, _CreateModuleEvalScope, _CreateModuleScope, _CreateMethodScope, _CreateBlockScope, _TraceMethodCall, _TraceMethodReturn, _TraceBlockCall, _TraceBlockReturn, _PrintInteractiveResult, _GetLocalVariable, _SetLocalVariable, _GetContextFromScope, _GetContextFromMethod, _GetContextFromBlockParam, _GetContextFromProc, _GetEmptyScope, + _DefineBlock, _InitializeBlock, _Yield0, _Yield1, _Yield2, _Yield3, _Yield4, _YieldN, _YieldSplat0, _YieldSplat1, _YieldSplat2, _YieldSplat3, _YieldSplat4, _YieldSplatN, _YieldSplatNRhs, _DefineMethod, _MethodDefined, _AliasMethod, _UndefineMethod, _IsDefinedMethod, + _DefineGlobalModule, _DefineNestedModule, _DefineModule, _ConvertNamespaceToModule, _DefineSingletonClass, _DefineGlobalClass, _DefineNestedClass, _DefineClass, _GetGlobalConstant, _GetUnqualifiedConstant, _GetQualifiedConstant, _IsDefinedGlobalConstant, _IsDefinedUnqualifiedConstant, _IsDefinedQualifiedConstant, _SetGlobalConstant, _SetUnqualifiedConstant, _SetQualifiedConstant, _MakeArray0, _MakeArray1, _MakeArray2, + _MakeArray3, _MakeArray4, _MakeArray5, _MakeArrayN, _MakeHash0, _MakeHash, _SplatAppend, _Splat, _SplatPair, _Unsplat, _GetArrayItem, _GetArraySuffix, _GetGlobalVariable, _IsDefinedGlobalVariable, _SetGlobalVariable, _AliasGlobalVariable, _GetCurrentMatchGroup; + + public static MethodInfo/*!*/ IsDefinedClassVariable { get { return _IsDefinedClassVariable ?? (_IsDefinedClassVariable = GetMethod(typeof(RubyOps), "IsDefinedClassVariable")); } } + public static MethodInfo/*!*/ SetObjectClassVariable { get { return _SetObjectClassVariable ?? (_SetObjectClassVariable = GetMethod(typeof(RubyOps), "SetObjectClassVariable")); } } + public static MethodInfo/*!*/ SetClassVariable { get { return _SetClassVariable ?? (_SetClassVariable = GetMethod(typeof(RubyOps), "SetClassVariable")); } } + public static MethodInfo/*!*/ GetInstanceData { get { return _GetInstanceData ?? (_GetInstanceData = GetMethod(typeof(RubyOps), "GetInstanceData")); } } + public static MethodInfo/*!*/ DeserializeObject { get { return _DeserializeObject ?? (_DeserializeObject = GetMethod(typeof(RubyOps), "DeserializeObject")); } } + public static MethodInfo/*!*/ SerializeObject { get { return _SerializeObject ?? (_SerializeObject = GetMethod(typeof(RubyOps), "SerializeObject")); } } + public static MethodInfo/*!*/ HookupEvent { get { return _HookupEvent ?? (_HookupEvent = GetMethod(typeof(RubyOps), "HookupEvent")); } } + public static MethodInfo/*!*/ CreateDelegateFromProc { get { return _CreateDelegateFromProc ?? (_CreateDelegateFromProc = GetMethod(typeof(RubyOps), "CreateDelegateFromProc")); } } + public static MethodInfo/*!*/ CreateDelegateFromMethod { get { return _CreateDelegateFromMethod ?? (_CreateDelegateFromMethod = GetMethod(typeof(RubyOps), "CreateDelegateFromMethod")); } } + public static MethodInfo/*!*/ X { get { return _X ?? (_X = GetMethod(typeof(RubyOps), "X")); } } + public static MethodInfo/*!*/ UpdateProfileTicks { get { return _UpdateProfileTicks ?? (_UpdateProfileTicks = GetMethod(typeof(RubyOps), "UpdateProfileTicks")); } } + public static MethodInfo/*!*/ CreateRfcForMethod { get { return _CreateRfcForMethod ?? (_CreateRfcForMethod = GetMethod(typeof(RubyOps), "CreateRfcForMethod")); } } + public static MethodInfo/*!*/ BlockRetry { get { return _BlockRetry ?? (_BlockRetry = GetMethod(typeof(RubyOps), "BlockRetry")); } } + public static MethodInfo/*!*/ MethodRetry { get { return _MethodRetry ?? (_MethodRetry = GetMethod(typeof(RubyOps), "MethodRetry")); } } + public static MethodInfo/*!*/ EvalRetry { get { return _EvalRetry ?? (_EvalRetry = GetMethod(typeof(RubyOps), "EvalRetry")); } } + public static MethodInfo/*!*/ BlockBreak { get { return _BlockBreak ?? (_BlockBreak = GetMethod(typeof(RubyOps), "BlockBreak")); } } + public static MethodInfo/*!*/ MethodBreak { get { return _MethodBreak ?? (_MethodBreak = GetMethod(typeof(RubyOps), "MethodBreak")); } } + public static MethodInfo/*!*/ EvalBreak { get { return _EvalBreak ?? (_EvalBreak = GetMethod(typeof(RubyOps), "EvalBreak")); } } + public static MethodInfo/*!*/ MethodNext { get { return _MethodNext ?? (_MethodNext = GetMethod(typeof(RubyOps), "MethodNext")); } } + public static MethodInfo/*!*/ EvalNext { get { return _EvalNext ?? (_EvalNext = GetMethod(typeof(RubyOps), "EvalNext")); } } + public static MethodInfo/*!*/ MethodRedo { get { return _MethodRedo ?? (_MethodRedo = GetMethod(typeof(RubyOps), "MethodRedo")); } } + public static MethodInfo/*!*/ EvalRedo { get { return _EvalRedo ?? (_EvalRedo = GetMethod(typeof(RubyOps), "EvalRedo")); } } + public static MethodInfo/*!*/ BlockReturn { get { return _BlockReturn ?? (_BlockReturn = GetMethod(typeof(RubyOps), "BlockReturn")); } } + public static MethodInfo/*!*/ EvalReturn { get { return _EvalReturn ?? (_EvalReturn = GetMethod(typeof(RubyOps), "EvalReturn")); } } + public static MethodInfo/*!*/ BlockYield { get { return _BlockYield ?? (_BlockYield = GetMethod(typeof(RubyOps), "BlockYield")); } } + public static MethodInfo/*!*/ MethodYield { get { return _MethodYield ?? (_MethodYield = GetMethod(typeof(RubyOps), "MethodYield")); } } + public static MethodInfo/*!*/ EvalYield { get { return _EvalYield ?? (_EvalYield = GetMethod(typeof(RubyOps), "EvalYield")); } } + public static MethodInfo/*!*/ MethodProcCall { get { return _MethodProcCall ?? (_MethodProcCall = GetMethod(typeof(RubyOps), "MethodProcCall")); } } + public static MethodInfo/*!*/ CanRescue { get { return _CanRescue ?? (_CanRescue = GetMethod(typeof(RubyOps), "CanRescue")); } } + public static MethodInfo/*!*/ IsRetrySingleton { get { return _IsRetrySingleton ?? (_IsRetrySingleton = GetMethod(typeof(RubyOps), "IsRetrySingleton")); } } + public static MethodInfo/*!*/ PropagateRetrySingleton { get { return _PropagateRetrySingleton ?? (_PropagateRetrySingleton = GetMethod(typeof(RubyOps), "PropagateRetrySingleton")); } } + public static MethodInfo/*!*/ GetRetrySingleton { get { return _GetRetrySingleton ?? (_GetRetrySingleton = GetMethod(typeof(RubyOps), "GetRetrySingleton")); } } + public static MethodInfo/*!*/ GetCurrentMatchData { get { return _GetCurrentMatchData ?? (_GetCurrentMatchData = GetMethod(typeof(RubyOps), "GetCurrentMatchData")); } } + public static MethodInfo/*!*/ GetCurrentMatchLastGroup { get { return _GetCurrentMatchLastGroup ?? (_GetCurrentMatchLastGroup = GetMethod(typeof(RubyOps), "GetCurrentMatchLastGroup")); } } + public static MethodInfo/*!*/ GetCurrentMatchPrefix { get { return _GetCurrentMatchPrefix ?? (_GetCurrentMatchPrefix = GetMethod(typeof(RubyOps), "GetCurrentMatchPrefix")); } } + public static MethodInfo/*!*/ GetCurrentMatchSuffix { get { return _GetCurrentMatchSuffix ?? (_GetCurrentMatchSuffix = GetMethod(typeof(RubyOps), "GetCurrentMatchSuffix")); } } + public static MethodInfo/*!*/ MatchLastInputLine { get { return _MatchLastInputLine ?? (_MatchLastInputLine = GetMethod(typeof(RubyOps), "MatchLastInputLine")); } } + public static MethodInfo/*!*/ MatchString { get { return _MatchString ?? (_MatchString = GetMethod(typeof(RubyOps), "MatchString")); } } + public static MethodInfo/*!*/ CreateRegexB { get { return _CreateRegexB ?? (_CreateRegexB = GetMethod(typeof(RubyOps), "CreateRegexB")); } } + public static MethodInfo/*!*/ CreateRegexU { get { return _CreateRegexU ?? (_CreateRegexU = GetMethod(typeof(RubyOps), "CreateRegexU")); } } + public static MethodInfo/*!*/ CreateRegexE { get { return _CreateRegexE ?? (_CreateRegexE = GetMethod(typeof(RubyOps), "CreateRegexE")); } } + public static MethodInfo/*!*/ CreateRegexM { get { return _CreateRegexM ?? (_CreateRegexM = GetMethod(typeof(RubyOps), "CreateRegexM")); } } + public static MethodInfo/*!*/ CreateRegexBM { get { return _CreateRegexBM ?? (_CreateRegexBM = GetMethod(typeof(RubyOps), "CreateRegexBM")); } } + public static MethodInfo/*!*/ CreateRegexUM { get { return _CreateRegexUM ?? (_CreateRegexUM = GetMethod(typeof(RubyOps), "CreateRegexUM")); } } + public static MethodInfo/*!*/ CreateRegexEM { get { return _CreateRegexEM ?? (_CreateRegexEM = GetMethod(typeof(RubyOps), "CreateRegexEM")); } } + public static MethodInfo/*!*/ CreateRegexMB { get { return _CreateRegexMB ?? (_CreateRegexMB = GetMethod(typeof(RubyOps), "CreateRegexMB")); } } + public static MethodInfo/*!*/ CreateRegexMU { get { return _CreateRegexMU ?? (_CreateRegexMU = GetMethod(typeof(RubyOps), "CreateRegexMU")); } } + public static MethodInfo/*!*/ CreateRegexME { get { return _CreateRegexME ?? (_CreateRegexME = GetMethod(typeof(RubyOps), "CreateRegexME")); } } + public static MethodInfo/*!*/ CreateRegexMM { get { return _CreateRegexMM ?? (_CreateRegexMM = GetMethod(typeof(RubyOps), "CreateRegexMM")); } } + public static MethodInfo/*!*/ CreateRegexN { get { return _CreateRegexN ?? (_CreateRegexN = GetMethod(typeof(RubyOps), "CreateRegexN")); } } + public static MethodInfo/*!*/ CreateMutableStringB { get { return _CreateMutableStringB ?? (_CreateMutableStringB = GetMethod(typeof(RubyOps), "CreateMutableStringB")); } } + public static MethodInfo/*!*/ CreateMutableStringU { get { return _CreateMutableStringU ?? (_CreateMutableStringU = GetMethod(typeof(RubyOps), "CreateMutableStringU")); } } + public static MethodInfo/*!*/ CreateMutableStringE { get { return _CreateMutableStringE ?? (_CreateMutableStringE = GetMethod(typeof(RubyOps), "CreateMutableStringE")); } } + public static MethodInfo/*!*/ CreateMutableStringM { get { return _CreateMutableStringM ?? (_CreateMutableStringM = GetMethod(typeof(RubyOps), "CreateMutableStringM")); } } + public static MethodInfo/*!*/ CreateMutableStringBM { get { return _CreateMutableStringBM ?? (_CreateMutableStringBM = GetMethod(typeof(RubyOps), "CreateMutableStringBM")); } } + public static MethodInfo/*!*/ CreateMutableStringUM { get { return _CreateMutableStringUM ?? (_CreateMutableStringUM = GetMethod(typeof(RubyOps), "CreateMutableStringUM")); } } + public static MethodInfo/*!*/ CreateMutableStringEM { get { return _CreateMutableStringEM ?? (_CreateMutableStringEM = GetMethod(typeof(RubyOps), "CreateMutableStringEM")); } } + public static MethodInfo/*!*/ CreateMutableStringMB { get { return _CreateMutableStringMB ?? (_CreateMutableStringMB = GetMethod(typeof(RubyOps), "CreateMutableStringMB")); } } + public static MethodInfo/*!*/ CreateMutableStringMU { get { return _CreateMutableStringMU ?? (_CreateMutableStringMU = GetMethod(typeof(RubyOps), "CreateMutableStringMU")); } } + public static MethodInfo/*!*/ CreateMutableStringME { get { return _CreateMutableStringME ?? (_CreateMutableStringME = GetMethod(typeof(RubyOps), "CreateMutableStringME")); } } + public static MethodInfo/*!*/ CreateMutableStringMM { get { return _CreateMutableStringMM ?? (_CreateMutableStringMM = GetMethod(typeof(RubyOps), "CreateMutableStringMM")); } } + public static MethodInfo/*!*/ CreateMutableStringN { get { return _CreateMutableStringN ?? (_CreateMutableStringN = GetMethod(typeof(RubyOps), "CreateMutableStringN")); } } + public static MethodInfo/*!*/ CreateSymbolB { get { return _CreateSymbolB ?? (_CreateSymbolB = GetMethod(typeof(RubyOps), "CreateSymbolB")); } } + public static MethodInfo/*!*/ CreateSymbolU { get { return _CreateSymbolU ?? (_CreateSymbolU = GetMethod(typeof(RubyOps), "CreateSymbolU")); } } + public static MethodInfo/*!*/ CreateSymbolE { get { return _CreateSymbolE ?? (_CreateSymbolE = GetMethod(typeof(RubyOps), "CreateSymbolE")); } } + public static MethodInfo/*!*/ CreateSymbolM { get { return _CreateSymbolM ?? (_CreateSymbolM = GetMethod(typeof(RubyOps), "CreateSymbolM")); } } + public static MethodInfo/*!*/ CreateSymbolBM { get { return _CreateSymbolBM ?? (_CreateSymbolBM = GetMethod(typeof(RubyOps), "CreateSymbolBM")); } } + public static MethodInfo/*!*/ CreateSymbolUM { get { return _CreateSymbolUM ?? (_CreateSymbolUM = GetMethod(typeof(RubyOps), "CreateSymbolUM")); } } + public static MethodInfo/*!*/ CreateSymbolEM { get { return _CreateSymbolEM ?? (_CreateSymbolEM = GetMethod(typeof(RubyOps), "CreateSymbolEM")); } } + public static MethodInfo/*!*/ CreateSymbolMB { get { return _CreateSymbolMB ?? (_CreateSymbolMB = GetMethod(typeof(RubyOps), "CreateSymbolMB")); } } + public static MethodInfo/*!*/ CreateSymbolMU { get { return _CreateSymbolMU ?? (_CreateSymbolMU = GetMethod(typeof(RubyOps), "CreateSymbolMU")); } } + public static MethodInfo/*!*/ CreateSymbolME { get { return _CreateSymbolME ?? (_CreateSymbolME = GetMethod(typeof(RubyOps), "CreateSymbolME")); } } + public static MethodInfo/*!*/ CreateSymbolMM { get { return _CreateSymbolMM ?? (_CreateSymbolMM = GetMethod(typeof(RubyOps), "CreateSymbolMM")); } } + public static MethodInfo/*!*/ CreateSymbolN { get { return _CreateSymbolN ?? (_CreateSymbolN = GetMethod(typeof(RubyOps), "CreateSymbolN")); } } + public static MethodInfo/*!*/ CreateEncoding { get { return _CreateEncoding ?? (_CreateEncoding = GetMethod(typeof(RubyOps), "CreateEncoding")); } } + public static MethodInfo/*!*/ IsTrue { get { return _IsTrue ?? (_IsTrue = GetMethod(typeof(RubyOps), "IsTrue")); } } + public static MethodInfo/*!*/ IsFalse { get { return _IsFalse ?? (_IsFalse = GetMethod(typeof(RubyOps), "IsFalse")); } } + public static MethodInfo/*!*/ GetCurrentException { get { return _GetCurrentException ?? (_GetCurrentException = GetMethod(typeof(RubyOps), "GetCurrentException")); } } + public static MethodInfo/*!*/ SetCurrentExceptionAndStackTrace { get { return _SetCurrentExceptionAndStackTrace ?? (_SetCurrentExceptionAndStackTrace = GetMethod(typeof(RubyOps), "SetCurrentExceptionAndStackTrace")); } } + public static MethodInfo/*!*/ SetCurrentException { get { return _SetCurrentException ?? (_SetCurrentException = GetMethod(typeof(RubyOps), "SetCurrentException")); } } + public static MethodInfo/*!*/ CompareException { get { return _CompareException ?? (_CompareException = GetMethod(typeof(RubyOps), "CompareException")); } } + public static MethodInfo/*!*/ CompareSplattedExceptions { get { return _CompareSplattedExceptions ?? (_CompareSplattedExceptions = GetMethod(typeof(RubyOps), "CompareSplattedExceptions")); } } + public static MethodInfo/*!*/ CompareDefaultException { get { return _CompareDefaultException ?? (_CompareDefaultException = GetMethod(typeof(RubyOps), "CompareDefaultException")); } } + public static MethodInfo/*!*/ GetDefaultExceptionMessage { get { return _GetDefaultExceptionMessage ?? (_GetDefaultExceptionMessage = GetMethod(typeof(RubyOps), "GetDefaultExceptionMessage")); } } + public static MethodInfo/*!*/ MakeWrongNumberOfArgumentsError { get { return _MakeWrongNumberOfArgumentsError ?? (_MakeWrongNumberOfArgumentsError = GetMethod(typeof(RubyOps), "MakeWrongNumberOfArgumentsError")); } } + public static MethodInfo/*!*/ MakeTopLevelSuperException { get { return _MakeTopLevelSuperException ?? (_MakeTopLevelSuperException = GetMethod(typeof(RubyOps), "MakeTopLevelSuperException")); } } + public static MethodInfo/*!*/ MakeMissingSuperException { get { return _MakeMissingSuperException ?? (_MakeMissingSuperException = GetMethod(typeof(RubyOps), "MakeMissingSuperException")); } } + public static MethodInfo/*!*/ MakeInvalidArgumentTypesError { get { return _MakeInvalidArgumentTypesError ?? (_MakeInvalidArgumentTypesError = GetMethod(typeof(RubyOps), "MakeInvalidArgumentTypesError")); } } + public static MethodInfo/*!*/ IsSuperCallTarget { get { return _IsSuperCallTarget ?? (_IsSuperCallTarget = GetMethod(typeof(RubyOps), "IsSuperCallTarget")); } } + public static MethodInfo/*!*/ CreateInclusiveRange { get { return _CreateInclusiveRange ?? (_CreateInclusiveRange = GetMethod(typeof(RubyOps), "CreateInclusiveRange")); } } + public static MethodInfo/*!*/ CreateExclusiveRange { get { return _CreateExclusiveRange ?? (_CreateExclusiveRange = GetMethod(typeof(RubyOps), "CreateExclusiveRange")); } } + public static MethodInfo/*!*/ AllocateStructInstance { get { return _AllocateStructInstance ?? (_AllocateStructInstance = GetMethod(typeof(RubyOps), "AllocateStructInstance")); } } + public static MethodInfo/*!*/ CreateStructInstance { get { return _CreateStructInstance ?? (_CreateStructInstance = GetMethod(typeof(RubyOps), "CreateStructInstance")); } } + public static MethodInfo/*!*/ GetClrExceptionMessage { get { return _CreateStructInstance ?? (_CreateStructInstance = GetMethod(typeof(RubyOps), "CreateStructInstance")); } } + public static MethodInfo/*!*/ InitializeException { get { return _CreateStructInstance ?? (_CreateStructInstance = GetMethod(typeof(RubyOps), "CreateStructInstance")); } } + public static MethodInfo/*!*/ GetMetaObject { get { return _GetMetaObject ?? (_GetMetaObject = GetMethod(typeof(RubyOps), "GetMetaObject")); } } + public static MethodInfo/*!*/ ToProcValidator { get { return _ToProcValidator ?? (_ToProcValidator = GetMethod(typeof(RubyOps), "ToProcValidator")); } } + public static MethodInfo/*!*/ ToStringValidator { get { return _ToStringValidator ?? (_ToStringValidator = GetMethod(typeof(RubyOps), "ToStringValidator")); } } + public static MethodInfo/*!*/ ToSValidator { get { return _ToSValidator ?? (_ToSValidator = GetMethod(typeof(RubyOps), "ToSValidator")); } } + public static MethodInfo/*!*/ ToSymbolValidator { get { return _ToSymbolValidator ?? (_ToSymbolValidator = GetMethod(typeof(RubyOps), "ToSymbolValidator")); } } + public static MethodInfo/*!*/ ConvertSymbolIdToSymbol { get { return _ConvertSymbolIdToSymbol ?? (_ConvertSymbolIdToSymbol = GetMethod(typeof(RubyOps), "ConvertSymbolIdToSymbol")); } } + public static MethodInfo/*!*/ ConvertFixnumToSymbol { get { return _ConvertFixnumToSymbol ?? (_ConvertFixnumToSymbol = GetMethod(typeof(RubyOps), "ConvertFixnumToSymbol")); } } + public static MethodInfo/*!*/ ConvertMutableStringToSymbol { get { return _ConvertMutableStringToSymbol ?? (_ConvertMutableStringToSymbol = GetMethod(typeof(RubyOps), "ConvertMutableStringToSymbol")); } } + public static MethodInfo/*!*/ ToRegexValidator { get { return _ToRegexValidator ?? (_ToRegexValidator = GetMethod(typeof(RubyOps), "ToRegexValidator")); } } + public static MethodInfo/*!*/ ToArrayValidator { get { return _ToArrayValidator ?? (_ToArrayValidator = GetMethod(typeof(RubyOps), "ToArrayValidator")); } } + public static MethodInfo/*!*/ ToFixnumValidator { get { return _ToFixnumValidator ?? (_ToFixnumValidator = GetMethod(typeof(RubyOps), "ToFixnumValidator")); } } + public static MethodInfo/*!*/ CreateTypeConversionError { get { return _CreateTypeConversionError ?? (_CreateTypeConversionError = GetMethod(typeof(RubyOps), "CreateTypeConversionError")); } } + public static MethodInfo/*!*/ ConvertBignumToFixnum { get { return _ConvertBignumToFixnum ?? (_ConvertBignumToFixnum = GetMethod(typeof(RubyOps), "ConvertBignumToFixnum")); } } + public static MethodInfo/*!*/ GetInstanceVariable { get { return _GetInstanceVariable ?? (_GetInstanceVariable = GetMethod(typeof(RubyOps), "GetInstanceVariable")); } } + public static MethodInfo/*!*/ IsDefinedInstanceVariable { get { return _IsDefinedInstanceVariable ?? (_IsDefinedInstanceVariable = GetMethod(typeof(RubyOps), "IsDefinedInstanceVariable")); } } + public static MethodInfo/*!*/ SetInstanceVariable { get { return _SetInstanceVariable ?? (_SetInstanceVariable = GetMethod(typeof(RubyOps), "SetInstanceVariable")); } } + public static MethodInfo/*!*/ GetObjectClassVariable { get { return _GetObjectClassVariable ?? (_GetObjectClassVariable = GetMethod(typeof(RubyOps), "GetObjectClassVariable")); } } + public static MethodInfo/*!*/ GetClassVariable { get { return _GetClassVariable ?? (_GetClassVariable = GetMethod(typeof(RubyOps), "GetClassVariable")); } } + public static MethodInfo/*!*/ TryGetObjectClassVariable { get { return _TryGetObjectClassVariable ?? (_TryGetObjectClassVariable = GetMethod(typeof(RubyOps), "TryGetObjectClassVariable")); } } + public static MethodInfo/*!*/ TryGetClassVariable { get { return _TryGetClassVariable ?? (_TryGetClassVariable = GetMethod(typeof(RubyOps), "TryGetClassVariable")); } } + public static MethodInfo/*!*/ IsDefinedObjectClassVariable { get { return _IsDefinedObjectClassVariable ?? (_IsDefinedObjectClassVariable = GetMethod(typeof(RubyOps), "IsDefinedObjectClassVariable")); } } + public static MethodInfo/*!*/ IsProcConverterTarget { get { return _IsProcConverterTarget ?? (_IsProcConverterTarget = GetMethod(typeof(RubyOps), "IsProcConverterTarget")); } } + public static MethodInfo/*!*/ CreateBfcForYield { get { return _CreateBfcForYield ?? (_CreateBfcForYield = GetMethod(typeof(RubyOps), "CreateBfcForYield")); } } + public static MethodInfo/*!*/ CreateBfcForMethodProcCall { get { return _CreateBfcForMethodProcCall ?? (_CreateBfcForMethodProcCall = GetMethod(typeof(RubyOps), "CreateBfcForMethodProcCall")); } } + public static MethodInfo/*!*/ CreateBfcForProcCall { get { return _CreateBfcForProcCall ?? (_CreateBfcForProcCall = GetMethod(typeof(RubyOps), "CreateBfcForProcCall")); } } + public static MethodInfo/*!*/ CreateBfcForLibraryMethod { get { return _CreateBfcForLibraryMethod ?? (_CreateBfcForLibraryMethod = GetMethod(typeof(RubyOps), "CreateBfcForLibraryMethod")); } } + public static MethodInfo/*!*/ LeaveProcConverter { get { return _LeaveProcConverter ?? (_LeaveProcConverter = GetMethod(typeof(RubyOps), "LeaveProcConverter")); } } + public static MethodInfo/*!*/ CreateMainTopLevelScope { get { return _CreateMainTopLevelScope ?? (_CreateMainTopLevelScope = GetMethod(typeof(RubyOps), "CreateMainTopLevelScope")); } } + public static MethodInfo/*!*/ CreateTopLevelScope { get { return _CreateTopLevelScope ?? (_CreateTopLevelScope = GetMethod(typeof(RubyOps), "CreateTopLevelScope")); } } + public static MethodInfo/*!*/ CreateWrappedTopLevelScope { get { return _CreateWrappedTopLevelScope ?? (_CreateWrappedTopLevelScope = GetMethod(typeof(RubyOps), "CreateWrappedTopLevelScope")); } } + public static MethodInfo/*!*/ CreateModuleEvalScope { get { return _CreateModuleEvalScope ?? (_CreateModuleEvalScope = GetMethod(typeof(RubyOps), "CreateModuleEvalScope")); } } + public static MethodInfo/*!*/ CreateModuleScope { get { return _CreateModuleScope ?? (_CreateModuleScope = GetMethod(typeof(RubyOps), "CreateModuleScope")); } } + public static MethodInfo/*!*/ CreateMethodScope { get { return _CreateMethodScope ?? (_CreateMethodScope = GetMethod(typeof(RubyOps), "CreateMethodScope")); } } + public static MethodInfo/*!*/ CreateBlockScope { get { return _CreateBlockScope ?? (_CreateBlockScope = GetMethod(typeof(RubyOps), "CreateBlockScope")); } } + public static MethodInfo/*!*/ TraceMethodCall { get { return _TraceMethodCall ?? (_TraceMethodCall = GetMethod(typeof(RubyOps), "TraceMethodCall")); } } + public static MethodInfo/*!*/ TraceMethodReturn { get { return _TraceMethodReturn ?? (_TraceMethodReturn = GetMethod(typeof(RubyOps), "TraceMethodReturn")); } } + public static MethodInfo/*!*/ TraceBlockCall { get { return _TraceBlockCall ?? (_TraceBlockCall = GetMethod(typeof(RubyOps), "TraceBlockCall")); } } + public static MethodInfo/*!*/ TraceBlockReturn { get { return _TraceBlockReturn ?? (_TraceBlockReturn = GetMethod(typeof(RubyOps), "TraceBlockReturn")); } } + public static MethodInfo/*!*/ PrintInteractiveResult { get { return _PrintInteractiveResult ?? (_PrintInteractiveResult = GetMethod(typeof(RubyOps), "PrintInteractiveResult")); } } + public static MethodInfo/*!*/ GetLocalVariable { get { return _GetLocalVariable ?? (_GetLocalVariable = GetMethod(typeof(RubyOps), "GetLocalVariable")); } } + public static MethodInfo/*!*/ SetLocalVariable { get { return _SetLocalVariable ?? (_SetLocalVariable = GetMethod(typeof(RubyOps), "SetLocalVariable")); } } + public static MethodInfo/*!*/ GetContextFromScope { get { return _GetContextFromScope ?? (_GetContextFromScope = GetMethod(typeof(RubyOps), "GetContextFromScope")); } } + public static MethodInfo/*!*/ GetContextFromMethod { get { return _GetContextFromMethod ?? (_GetContextFromMethod = GetMethod(typeof(RubyOps), "GetContextFromMethod")); } } + public static MethodInfo/*!*/ GetContextFromBlockParam { get { return _GetContextFromBlockParam ?? (_GetContextFromBlockParam = GetMethod(typeof(RubyOps), "GetContextFromBlockParam")); } } + public static MethodInfo/*!*/ GetContextFromProc { get { return _GetContextFromProc ?? (_GetContextFromProc = GetMethod(typeof(RubyOps), "GetContextFromProc")); } } + public static MethodInfo/*!*/ GetEmptyScope { get { return _GetEmptyScope ?? (_GetEmptyScope = GetMethod(typeof(RubyOps), "GetEmptyScope")); } } + public static MethodInfo/*!*/ DefineBlock { get { return _DefineBlock ?? (_DefineBlock = GetMethod(typeof(RubyOps), "DefineBlock")); } } + public static MethodInfo/*!*/ InitializeBlock { get { return _InitializeBlock ?? (_InitializeBlock = GetMethod(typeof(RubyOps), "InitializeBlock")); } } + public static MethodInfo/*!*/ Yield0 { get { return _Yield0 ?? (_Yield0 = GetMethod(typeof(RubyOps), "Yield0")); } } + public static MethodInfo/*!*/ Yield1 { get { return _Yield1 ?? (_Yield1 = GetMethod(typeof(RubyOps), "Yield1")); } } + public static MethodInfo/*!*/ Yield2 { get { return _Yield2 ?? (_Yield2 = GetMethod(typeof(RubyOps), "Yield2")); } } + public static MethodInfo/*!*/ Yield3 { get { return _Yield3 ?? (_Yield3 = GetMethod(typeof(RubyOps), "Yield3")); } } + public static MethodInfo/*!*/ Yield4 { get { return _Yield4 ?? (_Yield4 = GetMethod(typeof(RubyOps), "Yield4")); } } + public static MethodInfo/*!*/ YieldN { get { return _YieldN ?? (_YieldN = GetMethod(typeof(RubyOps), "YieldN")); } } + public static MethodInfo/*!*/ YieldSplat0 { get { return _YieldSplat0 ?? (_YieldSplat0 = GetMethod(typeof(RubyOps), "YieldSplat0")); } } + public static MethodInfo/*!*/ YieldSplat1 { get { return _YieldSplat1 ?? (_YieldSplat1 = GetMethod(typeof(RubyOps), "YieldSplat1")); } } + public static MethodInfo/*!*/ YieldSplat2 { get { return _YieldSplat2 ?? (_YieldSplat2 = GetMethod(typeof(RubyOps), "YieldSplat2")); } } + public static MethodInfo/*!*/ YieldSplat3 { get { return _YieldSplat3 ?? (_YieldSplat3 = GetMethod(typeof(RubyOps), "YieldSplat3")); } } + public static MethodInfo/*!*/ YieldSplat4 { get { return _YieldSplat4 ?? (_YieldSplat4 = GetMethod(typeof(RubyOps), "YieldSplat4")); } } + public static MethodInfo/*!*/ YieldSplatN { get { return _YieldSplatN ?? (_YieldSplatN = GetMethod(typeof(RubyOps), "YieldSplatN")); } } + public static MethodInfo/*!*/ YieldSplatNRhs { get { return _YieldSplatNRhs ?? (_YieldSplatNRhs = GetMethod(typeof(RubyOps), "YieldSplatNRhs")); } } + public static MethodInfo/*!*/ DefineMethod { get { return _DefineMethod ?? (_DefineMethod = GetMethod(typeof(RubyOps), "DefineMethod")); } } + public static MethodInfo/*!*/ MethodDefined { get { return _MethodDefined ?? (_MethodDefined = GetMethod(typeof(RubyOps), "MethodDefined")); } } + public static MethodInfo/*!*/ AliasMethod { get { return _AliasMethod ?? (_AliasMethod = GetMethod(typeof(RubyOps), "AliasMethod")); } } + public static MethodInfo/*!*/ UndefineMethod { get { return _UndefineMethod ?? (_UndefineMethod = GetMethod(typeof(RubyOps), "UndefineMethod")); } } + public static MethodInfo/*!*/ IsDefinedMethod { get { return _IsDefinedMethod ?? (_IsDefinedMethod = GetMethod(typeof(RubyOps), "IsDefinedMethod")); } } + public static MethodInfo/*!*/ DefineGlobalModule { get { return _DefineGlobalModule ?? (_DefineGlobalModule = GetMethod(typeof(RubyOps), "DefineGlobalModule")); } } + public static MethodInfo/*!*/ DefineNestedModule { get { return _DefineNestedModule ?? (_DefineNestedModule = GetMethod(typeof(RubyOps), "DefineNestedModule")); } } + public static MethodInfo/*!*/ DefineModule { get { return _DefineModule ?? (_DefineModule = GetMethod(typeof(RubyOps), "DefineModule")); } } + public static MethodInfo/*!*/ ConvertNamespaceToModule { get { return _ConvertNamespaceToModule ?? (_ConvertNamespaceToModule = GetMethod(typeof(RubyOps), "ConvertNamespaceToModule")); } } + public static MethodInfo/*!*/ DefineSingletonClass { get { return _DefineSingletonClass ?? (_DefineSingletonClass = GetMethod(typeof(RubyOps), "DefineSingletonClass")); } } + public static MethodInfo/*!*/ DefineGlobalClass { get { return _DefineGlobalClass ?? (_DefineGlobalClass = GetMethod(typeof(RubyOps), "DefineGlobalClass")); } } + public static MethodInfo/*!*/ DefineNestedClass { get { return _DefineNestedClass ?? (_DefineNestedClass = GetMethod(typeof(RubyOps), "DefineNestedClass")); } } + public static MethodInfo/*!*/ DefineClass { get { return _DefineClass ?? (_DefineClass = GetMethod(typeof(RubyOps), "DefineClass")); } } + public static MethodInfo/*!*/ GetGlobalConstant { get { return _GetGlobalConstant ?? (_GetGlobalConstant = GetMethod(typeof(RubyOps), "GetGlobalConstant")); } } + public static MethodInfo/*!*/ GetUnqualifiedConstant { get { return _GetUnqualifiedConstant ?? (_GetUnqualifiedConstant = GetMethod(typeof(RubyOps), "GetUnqualifiedConstant")); } } + public static MethodInfo/*!*/ GetQualifiedConstant { get { return _GetQualifiedConstant ?? (_GetQualifiedConstant = GetMethod(typeof(RubyOps), "GetQualifiedConstant")); } } + public static MethodInfo/*!*/ IsDefinedGlobalConstant { get { return _IsDefinedGlobalConstant ?? (_IsDefinedGlobalConstant = GetMethod(typeof(RubyOps), "IsDefinedGlobalConstant")); } } + public static MethodInfo/*!*/ IsDefinedUnqualifiedConstant { get { return _IsDefinedUnqualifiedConstant ?? (_IsDefinedUnqualifiedConstant = GetMethod(typeof(RubyOps), "IsDefinedUnqualifiedConstant")); } } + public static MethodInfo/*!*/ IsDefinedQualifiedConstant { get { return _IsDefinedQualifiedConstant ?? (_IsDefinedQualifiedConstant = GetMethod(typeof(RubyOps), "IsDefinedQualifiedConstant")); } } + public static MethodInfo/*!*/ SetGlobalConstant { get { return _SetGlobalConstant ?? (_SetGlobalConstant = GetMethod(typeof(RubyOps), "SetGlobalConstant")); } } + public static MethodInfo/*!*/ SetUnqualifiedConstant { get { return _SetUnqualifiedConstant ?? (_SetUnqualifiedConstant = GetMethod(typeof(RubyOps), "SetUnqualifiedConstant")); } } + public static MethodInfo/*!*/ SetQualifiedConstant { get { return _SetQualifiedConstant ?? (_SetQualifiedConstant = GetMethod(typeof(RubyOps), "SetQualifiedConstant")); } } + public static MethodInfo/*!*/ MakeArray0 { get { return _MakeArray0 ?? (_MakeArray0 = GetMethod(typeof(RubyOps), "MakeArray0")); } } + public static MethodInfo/*!*/ MakeArray1 { get { return _MakeArray1 ?? (_MakeArray1 = GetMethod(typeof(RubyOps), "MakeArray1")); } } + public static MethodInfo/*!*/ MakeArray2 { get { return _MakeArray2 ?? (_MakeArray2 = GetMethod(typeof(RubyOps), "MakeArray2")); } } + public static MethodInfo/*!*/ MakeArray3 { get { return _MakeArray3 ?? (_MakeArray3 = GetMethod(typeof(RubyOps), "MakeArray3")); } } + public static MethodInfo/*!*/ MakeArray4 { get { return _MakeArray4 ?? (_MakeArray4 = GetMethod(typeof(RubyOps), "MakeArray4")); } } + public static MethodInfo/*!*/ MakeArray5 { get { return _MakeArray5 ?? (_MakeArray5 = GetMethod(typeof(RubyOps), "MakeArray5")); } } + public static MethodInfo/*!*/ MakeArrayN { get { return _MakeArrayN ?? (_MakeArrayN = GetMethod(typeof(RubyOps), "MakeArrayN")); } } + public static MethodInfo/*!*/ MakeHash0 { get { return _MakeHash0 ?? (_MakeHash0 = GetMethod(typeof(RubyOps), "MakeHash0")); } } + public static MethodInfo/*!*/ MakeHash { get { return _MakeHash ?? (_MakeHash = GetMethod(typeof(RubyOps), "MakeHash")); } } + public static MethodInfo/*!*/ SplatAppend { get { return _SplatAppend ?? (_SplatAppend = GetMethod(typeof(RubyOps), "SplatAppend")); } } + public static MethodInfo/*!*/ Splat { get { return _Splat ?? (_Splat = GetMethod(typeof(RubyOps), "Splat")); } } + public static MethodInfo/*!*/ SplatPair { get { return _SplatPair ?? (_SplatPair = GetMethod(typeof(RubyOps), "SplatPair")); } } + public static MethodInfo/*!*/ Unsplat { get { return _Unsplat ?? (_Unsplat = GetMethod(typeof(RubyOps), "Unsplat")); } } + public static MethodInfo/*!*/ GetArrayItem { get { return _GetArrayItem ?? (_GetArrayItem = GetMethod(typeof(RubyOps), "GetArrayItem")); } } + public static MethodInfo/*!*/ GetArraySuffix { get { return _GetArraySuffix ?? (_GetArraySuffix = GetMethod(typeof(RubyOps), "GetArraySuffix")); } } + public static MethodInfo/*!*/ GetGlobalVariable { get { return _GetGlobalVariable ?? (_GetGlobalVariable = GetMethod(typeof(RubyOps), "GetGlobalVariable")); } } + public static MethodInfo/*!*/ IsDefinedGlobalVariable { get { return _IsDefinedGlobalVariable ?? (_IsDefinedGlobalVariable = GetMethod(typeof(RubyOps), "IsDefinedGlobalVariable")); } } + public static MethodInfo/*!*/ SetGlobalVariable { get { return _SetGlobalVariable ?? (_SetGlobalVariable = GetMethod(typeof(RubyOps), "SetGlobalVariable")); } } + public static MethodInfo/*!*/ AliasGlobalVariable { get { return _AliasGlobalVariable ?? (_AliasGlobalVariable = GetMethod(typeof(RubyOps), "AliasGlobalVariable")); } } + public static MethodInfo/*!*/ GetCurrentMatchGroup { get { return _GetCurrentMatchGroup ?? (_GetCurrentMatchGroup = GetMethod(typeof(RubyOps), "GetCurrentMatchGroup")); } } + + public static MethodInfo/*!*/ CreateRegex(string/*!*/ suffix) { + switch (suffix) { + case "N": return CreateRegexN; + case "B": return CreateRegexB; + case "E": return CreateRegexE; + case "U": return CreateRegexU; + case "M": return CreateRegexM; + case "BM": return CreateRegexBM; + case "UM": return CreateRegexUM; + case "EM": return CreateRegexEM; + case "MB": return CreateRegexMB; + case "MU": return CreateRegexMU; + case "ME": return CreateRegexME; + case "MM": return CreateRegexMM; + } + throw Assert.Unreachable; + } + + public static MethodInfo/*!*/ CreateMutableString(string/*!*/ suffix) { + switch (suffix) { + case "N": return CreateMutableStringN; + case "B": return CreateMutableStringB; + case "E": return CreateMutableStringE; + case "U": return CreateMutableStringU; + case "M": return CreateMutableStringM; + case "BM": return CreateMutableStringBM; + case "UM": return CreateMutableStringUM; + case "EM": return CreateMutableStringEM; + case "MB": return CreateMutableStringMB; + case "MU": return CreateMutableStringMU; + case "ME": return CreateMutableStringME; + case "MM": return CreateMutableStringMM; + } + throw Assert.Unreachable; + } + + public static MethodInfo/*!*/ CreateSymbol(string/*!*/ suffix) { + switch (suffix) { + case "N": return CreateSymbolN; + case "B": return CreateSymbolB; + case "E": return CreateSymbolE; + case "U": return CreateSymbolU; + case "M": return CreateSymbolM; + case "BM": return CreateSymbolBM; + case "UM": return CreateSymbolUM; + case "EM": return CreateSymbolEM; + case "MB": return CreateSymbolMB; + case "MU": return CreateSymbolMU; + case "ME": return CreateSymbolME; + case "MM": return CreateSymbolMM; + } + throw Assert.Unreachable; + } + + public static MethodInfo/*!*/ Yield(int parameterCount) { + switch (parameterCount) { + case 0: return Yield0; + case 1: return Yield1; + case 2: return Yield2; + case 3: return Yield3; + case 4: return Yield4; + } + return YieldN; + } + + public static MethodInfo/*!*/ YieldSplat(int parameterCount) { + switch (parameterCount) { + case 0: return YieldSplat0; + case 1: return YieldSplat1; + case 2: return YieldSplat2; + case 3: return YieldSplat3; + case 4: return YieldSplat4; + } + return YieldSplatN; + } + + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/ReflectionCache.cs b/merlin/main/languages/ruby/Ruby/Compiler/ReflectionCache.cs new file mode 100644 index 0000000000..3a85ac868a --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/ReflectionCache.cs @@ -0,0 +1,129 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Threading; +using IronRuby.Compiler.Generation; +using IronRuby.Runtime; +using IronRuby.Builtins; +using Microsoft.Scripting.Utils; + +using AstUtils = Microsoft.Scripting.Ast.Utils; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using MSA = System.Linq.Expressions; +using IronRuby.Runtime.Calls; +using System.Collections.ObjectModel; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.Compiler { + internal static class Fields { + private static FieldInfo _RubyOps_DefaultArgumentField; + public static FieldInfo RubyOps_DefaultArgumentField { get { return _RubyOps_DefaultArgumentField ?? (_RubyOps_DefaultArgumentField = typeof(RubyOps).GetField("DefaultArgument")); } } + } + + internal static partial class Methods { + private static ConstructorInfo _RubyCallSignatureCtor; + private static MethodInfo _Stopwatch_GetTimestamp, _IEnumerable_Of_Object_GetEnumerator, _IEnumerator_MoveNext, + _IEnumerator_get_Current; + + public static ConstructorInfo RubyCallSignatureCtor { get { return _RubyCallSignatureCtor ?? (_RubyCallSignatureCtor = typeof(RubyCallSignature).GetConstructor(new[] { typeof(int) })); } } + + public static MethodInfo Stopwatch_GetTimestamp { get { return _Stopwatch_GetTimestamp ?? (_Stopwatch_GetTimestamp = GetMethod(typeof(Stopwatch), "GetTimestamp")); } } + public static MethodInfo IEnumerable_Of_Object_GetEnumerator { get { return _IEnumerable_Of_Object_GetEnumerator ?? (_IEnumerable_Of_Object_GetEnumerator = GetMethod(typeof(IEnumerable), "GetEnumerator", BindingFlags.Instance, Type.EmptyTypes)); } } + public static MethodInfo IEnumerator_get_Current { get { return _IEnumerator_get_Current ?? (_IEnumerator_get_Current = GetMethod(typeof(IEnumerator), "get_Current", BindingFlags.Instance, Type.EmptyTypes)); } } + public static MethodInfo IEnumerator_MoveNext { get { return _IEnumerator_MoveNext ?? (_IEnumerator_MoveNext = GetMethod(typeof(IEnumerator), "MoveNext", BindingFlags.Instance, Type.EmptyTypes)); } } + + private static MethodInfo/*!*/ GetMethod(Type/*!*/ type, string/*!*/ name) { + var method = type.GetMethod(name, BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); + Debug.Assert(method != null, type.Name + "::" + name); + return method; + } + + private static MethodInfo/*!*/ GetMethod(Type/*!*/ type, string/*!*/ name, BindingFlags flags, Type/*!*/[]/*!*/ signature) { + var method = type.GetMethod(name, flags | BindingFlags.Public | BindingFlags.DeclaredOnly, null, signature, null); + + Debug.Assert(method != null, type.Name + "::" + name); + return method; + } + + public static MSA.Expression/*!*/ MakeArrayOpCall(List/*!*/ args) { + Assert.NotNull(args); + + switch (args.Count) { + case 0: return Methods.MakeArray0.OpCall(); + case 1: return Methods.MakeArray1.OpCall(AstFactory.Box(args[0])); + case 2: return Methods.MakeArray2.OpCall(AstFactory.Box(args[0]), AstFactory.Box(args[1])); + case 3: return Methods.MakeArray3.OpCall(AstFactory.Box(args[0]), AstFactory.Box(args[1]), AstFactory.Box(args[2])); + case 4: return Methods.MakeArray4.OpCall(AstFactory.Box(args[0]), AstFactory.Box(args[1]), AstFactory.Box(args[2]), AstFactory.Box(args[3])); + case 5: return Methods.MakeArray5.OpCall(AstFactory.Box(args[0]), AstFactory.Box(args[1]), AstFactory.Box(args[2]), AstFactory.Box(args[3]), AstFactory.Box(args[4])); + + default: + Debug.Assert(args.Count > Runtime.RubyOps.OptimizedOpCallParamCount); + return Methods.MakeArrayN.OpCall(AstUtils.NewArrayHelper(typeof(object), args)); + } + } + + public static MethodInfo/*!*/ Yield(int argumentCount, bool hasSplattedArgument, bool hasRhsArgument, out bool hasArgumentArray) { + if (hasRhsArgument) { + if (hasSplattedArgument) { + hasArgumentArray = true; + return Methods.YieldSplatNRhs; + } else { + argumentCount++; + } + } + + hasArgumentArray = argumentCount > BlockDispatcher.MaxBlockArity; + return hasSplattedArgument ? Methods.YieldSplat(argumentCount) : Methods.Yield(argumentCount); + } + } + + internal static class MethodInfoExtensions { + public static MSA.Expression/*!*/ OpCall(this MethodInfo/*!*/ method) { + Assert.NotNull(method); + return MSA.Expression.Call(null, method); + } + + public static MSA.Expression/*!*/ OpCall(this MethodInfo/*!*/ method, MSA.Expression/*!*/ arg1) { + Assert.NotNull(method); + return MSA.Expression.Call(method, arg1); + } + + public static MSA.Expression/*!*/ OpCall(this MethodInfo/*!*/ method, MSA.Expression/*!*/ arg1, MSA.Expression/*!*/ arg2) { + Assert.NotNull(method); + return MSA.Expression.Call(null, method, arg1, arg2); + } + + public static MSA.Expression/*!*/ OpCall(this MethodInfo/*!*/ method, MSA.Expression/*!*/ arg1, MSA.Expression/*!*/ arg2, MSA.Expression/*!*/ arg3) { + Assert.NotNull(method); + return MSA.Expression.Call(null, method, arg1, arg2, arg3); + } + + public static MSA.Expression/*!*/ OpCall(this MethodInfo/*!*/ method, params MSA.Expression[]/*!*/ args) { + Assert.NotNull(method, args); + return MSA.Expression.Call(null, method, new ReadOnlyCollection(args)); + } + + public static MSA.Expression/*!*/ OpCall(this MethodInfo/*!*/ method, List/*!*/ args) { + Assert.NotNull(method, args); + return MSA.Expression.Call(null, method, new ReadOnlyCollection(args)); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/ResultOperation.cs b/merlin/main/languages/ruby/Ruby/Compiler/ResultOperation.cs new file mode 100644 index 0000000000..0aba3c9413 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/ResultOperation.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting.Utils; +using MSA = System.Linq.Expressions; + +namespace IronRuby.Compiler { + + internal struct ResultOperation { + public static readonly ResultOperation Return = new ResultOperation(null, true); + public static readonly ResultOperation Ignore = new ResultOperation(null, false); + + private MSA.Expression _variable; + private bool _doReturn; + + public MSA.Expression Variable { get { return _variable; } } + public bool DoReturn { get { return _doReturn; } } + public bool IsIgnore { get { return _variable == null && !_doReturn; } } + + public ResultOperation(MSA.Expression variable, bool doReturn) { + _variable = variable; + _doReturn = doReturn; + } + + public static ResultOperation Store(MSA.Expression/*!*/ variable) { + Assert.NotNull(variable); + return new ResultOperation(variable, false); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/RubyCompilerOptions.cs b/merlin/main/languages/ruby/Ruby/Compiler/RubyCompilerOptions.cs new file mode 100644 index 0000000000..315fa28d08 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/RubyCompilerOptions.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting; +using System.Dynamic; +using IronRuby.Runtime; + +namespace IronRuby.Compiler { + [Serializable] + public sealed class RubyCompilerOptions : CompilerOptions { + // TODO: replace bool's by flags/enum + internal bool IsEval { get; set; } + internal bool IsModuleEval { get; set; } + internal bool IsIncluded { get; set; } + internal bool IsWrapped { get; set; } + + private SourceLocation _initialLocation = SourceLocation.MinValue; + + internal SourceLocation InitialLocation { + get { return _initialLocation; } + set { _initialLocation = value; } + } + + /// + /// Method name used by super in eval. + /// + internal string TopLevelMethodName { get; set; } + + internal List LocalNames { get; set; } + internal RubyCompatibility Compatibility { get; set; } + + public RubyCompilerOptions() { + } + + // copies relevant options from runtime options: + public RubyCompilerOptions(RubyOptions/*!*/ runtimeOptions) { + Compatibility = runtimeOptions.Compatibility; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Compiler/Tokens.cs b/merlin/main/languages/ruby/Ruby/Compiler/Tokens.cs new file mode 100644 index 0000000000..5b3d65d7cf --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/Tokens.cs @@ -0,0 +1,148 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if NEW_PARSER +using System; +using System.Collections.Generic; +using System.Text; + +// true operators: .. ... ! not && and || or != !~ += *= /= %= ^= etc. + +namespace Ruby.Compiler { + public enum Tokens { + None, + Error, + WhiteSpace, + EndOfFile, + UnexpectedCharacter, + + SingleLineComment, + MultiLineComment, + + // keywords: + CLASS,MODULE,DEF,UNDEF,BEGIN,RESCUE,ENSURE,END,IF,UNLESS,THEN,ELSIF,ELSE, + CASE,WHEN,WHILE,UNTIL,FOR,BREAK,NEXT,REDO,RETRY,IN,DO,LOOP_DO,BLOCK_DO, + RETURN,YIELD,SUPER,SELF,NIL,TRUE,FALSE,AND,OR,NOT,IF_MOD,UNLESS_MOD, + WHILE_MOD,UNTIL_MOD,RESCUE_MOD,ALIAS,DEFINED,UPPERCASE_BEGIN,UPPERCASE_END,__LINE__,__FILE__, + + // % token + CONSTANT_IDENTIFIER, // CONSTANT_IDENTIFIER: [A-Z][_a-zA-Z0-9]* + IDENTIFIER, // IDENTIFIER: [_a-z][_a-zA-Z0-9]* + FUNCTION_IDENTIFIER, // FUNCTION-IDENTIFIER: [_a-zA-Z][_a-zA-Z0-9]*[!?=]? + + GVAR, + // GLOBAL-VARIABLE: + // [$][_a-zA-Z][_a-zA-Z0-9]* + // [$][~`_\\>=<;:/.+,-*'&$"!] + // [$]-[0-9_a-zA-Z] + + IVAR, // INSTANCE-VARIABLE: @[_a-zA-Z][_a-zA-Z0-9]* + CVAR, // CLASS-VARIABLE: @@[_a-zA-Z][_a-zA-Z0-9]* + + // % token + OP_ASGN, // =, !=, %=, &=, *=, +=, -=, /=, ^=, |=, ||=, &&=, <<=, >>=, **= + + INTEGER, + // [1-9]([0-9_]*[1-9])? + // 0([0-7_]*[0-7])? + // 0[xX][0-9a-fA-F]([0-9a-fA-F_]*[0-9a-fA-F])? + // 0[dD][0-9]([0-9_]*[0-9])? + // 0[bB][01]([01_]*[01])? + // 0[oO][0-7]([0-7_]*[0-7])? + // [?]{char} + + // {char} is + // (\\(c|C-|M-))*([^\\]|\\[abefnrstv]|\\[0-7]{1,3}|\\x[0-9a-fA-F]{1-2}) + // + // ... \c, \C- mean & 0x9f, \M- means | 0x80 + // ... [abefnrstv] are escaped special characters + // ... \000 octal + // ... \xFF hex + // + // [-2**30, 2*30-1] -> Fixnum -- define CLR as 32bit platform? + // otherwise -> Bignum + + FLOAT, + // (0|[1-9]([0-9_]*[0-9])?)[.][0-9_]*[0-9]([eE][+-]?[0-9]([0-9_]*[0-9])?) + + NTH_REF, // [$][0-9]+ + BACK_REF, // [$][`'+&] + + STRING_BEG, // ["']|%[qQ]?[:open:] + STRING_END, // ["']|[:close:] + + STRING_EMBEDDED_CODE_BEGIN, // # in string + STRING_EMBEDDED_VARIABLE_BEGIN, // #[{] in string + + STRING_CONTENT, // string content w/o # + + SHELL_STRING_BEGIN, // `|%x[:open:] + + REGEXP_BEG, // (%r[:open:]|[/]) + REGEXP_END, // ([:close:]|[/])[eimnosux]* + + SYMBEG, // [:]["]? + + WORDS_BEG, // %W[:open:] + VERBATIM_WORDS_BEGIN, // %w[:open:] + + AREF, // [] (?) + ASET, // []= (?) + ASSOC, // => + + AMPER, // & + CMP, // <=> + EQ, // == (?) + EQQ, // === (?) + MATCH, // ~= + GreaterThen, // > + LessThen, // < + LEQ, // <= + LSHFT, // << + RSHFT, // >> + + UPLUS, // +@ (unary plus method on Numeric class) + + UMINUS, // -@ (unary minus method on Numeric class) + UMINUS_NUM, // - (unary minus ???) + + POW, // ** + NEQ, // != + GEQ, // >= + ANDOP, // && + OROP, // || + NMATCH, // !~ + Mul, // * + STAR, // * in arguments + Div, // / + Mod, // % + Twiddle, // ~ + BackQuote, // ` + Comma, // , + DOT2, // .. + DOT3, // ... + SEPARATING_DOUBLE_COLON, // :: + LEADING_DOUBLE_COLON, // ::: + + LBRACK, // [ + + LBRACE, // { + LBRACE_ARG, // ' {' + + LPAREN, // ( + LPAREN_ARG, // ' (' + } +} +#endif \ No newline at end of file diff --git a/merlin/main/languages/ruby/Ruby/Compiler/UnsignedBigIntegerParser.cs b/merlin/main/languages/ruby/Ruby/Compiler/UnsignedBigIntegerParser.cs new file mode 100644 index 0000000000..8035a13d13 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Compiler/UnsignedBigIntegerParser.cs @@ -0,0 +1,201 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Math; + +namespace IronRuby.Compiler { + + /// + /// TODO: move to DLR. + /// + public abstract class UnsignedBigIntegerParser { + protected abstract int ReadDigit(); + + protected UnsignedBigIntegerParser() { + } + + public BigInteger/*!*/ ParseBinary(int digitCount) { + return ParseBinarySuperPowers(digitCount, 1); + } + + public BigInteger/*!*/ ParseHexadecimal(int digitCount) { + return ParseBinarySuperPowers(digitCount, 4); + } + + public BigInteger/*!*/ ParseDecimal(int digitCount) { + const int DigitsPerWord = 9; + const uint WordBase = 1000000000; + return ParseAnyBase(digitCount, 10, WordBase, DigitsPerWord); + } + + public BigInteger ParseOctal(int digitCount) { + ContractUtils.Requires(digitCount > 0, "digitCount"); + + const int BitsPerWord = 32; + const int BitsPerDigit = 3; + const int DigitsPerWord = BitsPerWord / BitsPerDigit; + + int remainingBits = (digitCount * BitsPerDigit) % (BitsPerWord * BitsPerDigit); + int triwordCount = (digitCount * BitsPerDigit) / (BitsPerWord * BitsPerDigit); + + uint[] result = new uint[triwordCount * BitsPerDigit + (remainingBits + BitsPerWord - 1) / BitsPerWord]; + + if (remainingBits <= BitsPerWord) { + uint c = ReadBinaryWord(remainingBits / BitsPerDigit, BitsPerDigit); + result[result.Length - 1] = c; + } else if (remainingBits <= BitsPerWord * 2) { + uint b = ReadBinaryWord((remainingBits - BitsPerWord) / BitsPerDigit, BitsPerDigit); + uint bc = (uint)ReadDigit(); + uint c = ReadBinaryWord(DigitsPerWord, BitsPerDigit); + result[result.Length - 1] = (b << 1) | (bc >> 2); + result[result.Length - 2] = c | ((bc & 3) << 30); + } else { + ReadOctalTriword(result, result.Length - 1, (remainingBits - 2 * BitsPerWord) / BitsPerDigit); + } + + for (int i = triwordCount * 3 - 1; i > 0; i -= 3) { + ReadOctalTriword(result, i, DigitsPerWord); + } + + return new BigInteger(+1, result); + } + + private void ReadOctalTriword(uint[]/*!*/ result, int i, int digits) { + const int BitsPerWord = 32; + const int BitsPerDigit = 3; + const int DigitsPerWord = BitsPerWord / BitsPerDigit; + + // [10 digits + 2 bits][1 bit + 10 digits + 1 bit][2 bits + 10 digits] + uint a = ReadBinaryWord(digits, BitsPerDigit); + uint ab = (uint)ReadDigit(); + uint b = ReadBinaryWord(DigitsPerWord, BitsPerDigit); + uint bc = (uint)ReadDigit(); + uint c = ReadBinaryWord(DigitsPerWord, BitsPerDigit); + + result[i - 0] = (a << 2) | (ab >> 1); + result[i - 1] = (b << 1) | ((ab & 1) << 31) | (bc >> 2); + result[i - 2] = c | ((bc & 3) << 30); + } + + public BigInteger/*!*/ Parse(int digitCount, int @base) { + ContractUtils.Requires(@base > 1, "base"); + switch (@base) { + case 2: return ParseBinary(digitCount); + case 4: return ParseBinarySuperPowers(digitCount, 2); + case 8: return ParseOctal(digitCount); + case 16: return ParseHexadecimal(digitCount); + case 10: return ParseDecimal(digitCount); + default: + return ParseDefault(digitCount, (uint)@base); + } + } + + internal BigInteger/*!*/ ParseDefault(int digitCount, uint @base) { + uint wordBase = 1; + int digitsPerWord = 0; + while (true) { + ulong newBase = (ulong)wordBase * @base; + if (newBase > UInt32.MaxValue) break; + wordBase = (uint)newBase; + digitsPerWord++; + } + + return ParseAnyBase(digitCount, @base, wordBase, digitsPerWord); + } + + private BigInteger/*!*/ ParseAnyBase(int digitCount, uint @base, uint wordBase, int digitsPerWord) { + ContractUtils.Requires(digitCount > 0, "digitCount"); + + int resultSize = GetResultSize(digitCount, @base); + int remainingDigits = digitCount % digitsPerWord; + int wordCount = digitCount / digitsPerWord; + + uint[] result = new uint[resultSize]; + result[0] = ReadWord(remainingDigits, @base); + int count = 1; + for (int i = 0; i < wordCount; i++) { + count = MultiplyAdd(result, count, wordBase, ReadWord(digitsPerWord, @base)); + } + + return new BigInteger(+1, result); + } + + private int GetResultSize(int digitCount, uint @base) { + int resultSize; + + try { + resultSize = (int)System.Math.Ceiling(System.Math.Log(@base) * digitCount); + } catch (OverflowException) { + throw new ArgumentOutOfRangeException("Too many digits", "digitCount"); + } + + return resultSize; + } + + private BigInteger/*!*/ ParseBinarySuperPowers(int digitCount, int bitsPerDigit) { + ContractUtils.Requires(digitCount > 0, "digitCount"); + + const int BitsPerWord = 32; + + Debug.Assert(BitsPerWord % bitsPerDigit == 0); + int digitsPerWord = BitsPerWord / bitsPerDigit; + + int remainingDigits = digitCount % digitsPerWord; + int wordCount = digitCount / digitsPerWord; + uint[] result = new uint[wordCount + (remainingDigits + digitsPerWord - 1) / digitsPerWord]; + + result[result.Length - 1] = ReadBinaryWord(remainingDigits, bitsPerDigit); + for (int i = wordCount - 1; i >= 0; i--) { + result[i] = ReadBinaryWord(digitsPerWord, bitsPerDigit); + } + + return new BigInteger(+1, result); + } + + // data = data * x + carry + private int MultiplyAdd(uint[]/*!*/ data, int count, uint x, uint carry) { + ulong m = 0; + for (int i = 0; i < count + 1; i++) { + m = (ulong)data[i] * x + carry; + data[i] = (uint)(m & 0xffffffffL); + carry = (uint)(m >> 32); + } + + Debug.Assert(carry == 0); + return (m > 0) ? count + 1 : count; + } + + private uint ReadBinaryWord(int digitCount, int bitsPerDigit) { + uint word = 0; + while (digitCount > 0) { + word = (word << bitsPerDigit) | (uint)ReadDigit(); + digitCount--; + } + return word; + } + + private uint ReadWord(int digitCount, uint @base) { + uint word = 0; + while (digitCount > 0) { + word = word * @base + (uint)ReadDigit(); + digitCount--; + } + return word; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/GlobalSuppressions.cs b/merlin/main/languages/ruby/Ruby/GlobalSuppressions.cs new file mode 100644 index 0000000000..02c32c9bfe --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/GlobalSuppressions.cs @@ -0,0 +1,18 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "IronRuby")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "IronRuby.Hosting")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "IronRuby.Compiler.Parser.#DoAction(System.Int32)")] diff --git a/merlin/main/languages/ruby/Ruby/Hosting/RubyCommandLine.cs b/merlin/main/languages/ruby/Ruby/Hosting/RubyCommandLine.cs new file mode 100644 index 0000000000..4cce70dcdc --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Hosting/RubyCommandLine.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using IronRuby.Builtins; +using IronRuby.Runtime; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting.Shell; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.Hosting { + + /// + /// A simple Ruby command-line should mimic the standard irb.exe + /// + public class RubyCommandLine : CommandLine { + public RubyCommandLine() { + } + + protected override string Logo { + get { + return String.Format("IronRuby {1} on .NET {2}{0}Copyright (c) Microsoft Corporation. All rights reserved.{0}{0}Note that local variables do not work today in the console.{0}As a workaround, use globals instead (eg $x = 42 instead of x = 42).{0}{0}", + Environment.NewLine, RubyContext.IronRubyVersion, Environment.Version); + } + } + + protected override int? TryInteractiveAction() { + try { + return base.TryInteractiveAction(); + } catch (SystemExit e) { + return e.Status; + } + } + + // overridden to set the default encoding to BINARY + protected override int RunFile(string fileName) { + return RunFile(Engine.CreateScriptSourceFromFile(fileName, BinaryEncoding.Instance)); + } + + protected override Scope/*!*/ CreateScope() { + Scope scope = base.CreateScope(); + scope.SetName(SymbolTable.StringToId("iron_ruby"), Engine); + return scope; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Hosting/RubyOptionsParser.cs b/merlin/main/languages/ruby/Ruby/Hosting/RubyOptionsParser.cs new file mode 100644 index 0000000000..d6fd9b4bcb --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Hosting/RubyOptionsParser.cs @@ -0,0 +1,196 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Shell; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Hosting { + + public sealed class RubyOptionsParser : OptionsParser { + private readonly List/*!*/ _loadPaths = new List(); + +#if DEBUG && !SILVERLIGHT + private ConsoleTraceListener _debugListener; + + private sealed class CustomTraceFilter : TraceFilter { + public readonly Dictionary/*!*/ Categories = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public bool EnableAll { get; set; } + + public override bool ShouldTrace(TraceEventCache cache, string source, TraceEventType eventType, int id, string category, object[] args, object data1, object[] data) { + string message = data1 as string; + if (message == null) return true; + + bool enabled; + if (Categories.TryGetValue(category, out enabled)) { + return enabled; + } else { + return EnableAll; + } + } + } + + private void SetTraceFilter(string/*!*/ arg, bool enable) { + string[] categories = arg.Split(new[] { ';', ','}, StringSplitOptions.RemoveEmptyEntries); + + if (categories.Length == 0 && !enable) { + Debug.Listeners.Clear(); + return; + } + + if (_debugListener == null) { + _debugListener = new ConsoleTraceListener { IndentSize = 4, Filter = new CustomTraceFilter { EnableAll = categories.Length == 0 } }; + Debug.Listeners.Add(_debugListener); + } + + foreach (var category in categories) { + ((CustomTraceFilter)_debugListener.Filter).Categories[category] = enable; + } + } +#endif + + /// On error. + protected override void ParseArgument(string arg) { + ContractUtils.RequiresNotNull(arg, "arg"); + + int colon = arg.IndexOf(':'); + string optionName, optionValue; + if (colon >= 0) { + optionName = arg.Substring(0, colon); + optionValue = arg.Substring(colon + 1); + } else { + optionName = arg; + optionValue = null; + } + + switch (optionName) { + case "-v": + // TODO: print version + goto case "-W2"; + + case "-W0": + LanguageSetup.Options["Verbosity"] = 0; // $VERBOSE = nil + break; + + case "-W1": + LanguageSetup.Options["Verbosity"] = 1; // $VERBOSE = false + break; + + case "-w": + case "-W2": + LanguageSetup.Options["Verbosity"] = 2; // $VERBOSE = true + break; + + case "-d": + RuntimeSetup.DebugMode = true; // $DEBUG = true + break; + + case "-r": + string libPath = PopNextArg(); + LanguageSetup.Options["RequiredLibraries"] = libPath; + break; + + +#if DEBUG && !SILVERLIGHT + case "-DT*": + SetTraceFilter(String.Empty, false); + break; + + case "-DT": + SetTraceFilter(PopNextArg(), false); + break; + + case "-ET*": + SetTraceFilter(String.Empty, true); + break; + + case "-ET": + SetTraceFilter(PopNextArg(), true); + break; + + case "-save": + LanguageSetup.Options["SavePath"] = optionValue ?? AppDomain.CurrentDomain.BaseDirectory; + break; + + case "-load": + LanguageSetup.Options["LoadFromDisk"] = ScriptingRuntimeHelpers.True; + break; +#endif + case "-I": + _loadPaths.AddRange(PopNextArg().Split(Path.PathSeparator)); + break; + + case "-trace": + LanguageSetup.Options["EnableTracing"] = ScriptingRuntimeHelpers.True; + break; + + case "-profile": + LanguageSetup.Options["Profile"] = ScriptingRuntimeHelpers.True; + break; + + case "-18": + LanguageSetup.Options["Compatibility"] = RubyCompatibility.Ruby18; + break; + + case "-19": + LanguageSetup.Options["Compatibility"] = RubyCompatibility.Ruby19; + break; + + case "-20": + LanguageSetup.Options["Compatibility"] = RubyCompatibility.Ruby20; + break; + + default: + base.ParseArgument(arg); + if (ConsoleOptions.FileName != null) { + LanguageSetup.Options["MainFile"] = ConsoleOptions.FileName; + LanguageSetup.Options["Arguments"] = PopRemainingArgs(); + } + break; + } + } + + protected override void AfterParse() { + var existingSearchPaths = + LanguageOptions.GetSearchPathsOption(LanguageSetup.Options) ?? + LanguageOptions.GetSearchPathsOption(RuntimeSetup.Options); + + if (existingSearchPaths != null) { + _loadPaths.InsertRange(0, existingSearchPaths); + } + + LanguageSetup.Options["SearchPaths"] = _loadPaths; + } + + public override void GetHelp(out string commandLine, out string[,] options, out string[,] environmentVariables, out string comments) { + string[,] standardOptions; + base.GetHelp(out commandLine, out standardOptions, out environmentVariables, out comments); + + string [,] rubyOptions = new string[,] { + { "-opt", "dummy" }, + }; + + // Append the Ruby-specific options and the standard options + options = ArrayUtils.Concatenate(rubyOptions, standardOptions); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Properties/AssemblyInfo.cs b/merlin/main/languages/ruby/Ruby/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..4c0b898164 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; +using IronRuby.Hosting; +using IronRuby.Runtime; + +[assembly: AssemblyTitle("Ruby")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Ruby")] +[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] +[assembly: CLSCompliant(true)] +[assembly: Guid("ca75230d-3011-485d-b1db-dfe924b6c434")] + +#if !SILVERLIGHT +[assembly: AssemblyVersion(RubyContext.IronRubyVersionString)] +[assembly: AssemblyFileVersion(RubyContext.IronRubyVersionString)] +#endif + +#if SILVERLIGHT +[assembly: InternalsVisibleTo("IronRuby.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] + +#elif SIGNED +[assembly: InternalsVisibleTo("IronRuby.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("ClassInitGenerator, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: AllowPartiallyTrustedCallers] + +#else +[assembly: InternalsVisibleTo("IronRuby.Tests")] +[assembly: InternalsVisibleTo("ClassInitGenerator")] +#endif + + diff --git a/merlin/main/languages/ruby/Ruby/Ruby.cs b/merlin/main/languages/ruby/Ruby/Ruby.cs new file mode 100644 index 0000000000..2cba3becae --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Ruby.cs @@ -0,0 +1,172 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Providers; +using Microsoft.Scripting.Runtime; +using IronRuby.Runtime; +using IronRuby.Builtins; +using System.ComponentModel; +using System; +using System.Collections; +using System.Collections.Generic; + +#if SILVERLIGHT +[assembly: DynamicLanguageProvider(typeof(RubyContext), RubyContext.IronRubyDisplayName, RubyContext.IronRubyNames, RubyContext.IronRubyFileExtensions)] +#endif + +namespace IronRuby { + + public static class Ruby { + /// + /// Creates a new script runtime configured using .NET configuration files. + /// If the .NET configuration doesn't include IronRuby a default IronRuby configuration is added. + /// + public static ScriptRuntime/*!*/ CreateRuntime() { + var setup = ScriptRuntimeSetup.ReadConfiguration(); + setup.AddRubySetup(); + return new ScriptRuntime(setup); + } + + public static ScriptRuntime/*!*/ CreateRuntime(ScriptRuntimeSetup/*!*/ setup) { + return new ScriptRuntime(setup); + } + + /// + /// Creates a new script runtime and returns its IronRuby engine. + /// + public static ScriptEngine/*!*/ CreateEngine() { + return GetEngine(CreateRuntime()); + } + + /// + /// Creates a new script runtime and returns its IronRuby engine configured using . + /// + /// + /// If the configuration loaded from .config files already contains + /// + public static ScriptEngine/*!*/ CreateEngine(Action/*!*/ setupInitializer) { + ContractUtils.RequiresNotNull(setupInitializer, "setupInitializer"); + + var runtimeSetup = ScriptRuntimeSetup.ReadConfiguration(); + int index = IndexOfRubySetup(runtimeSetup); + if (index != -1) { + setupInitializer(runtimeSetup.LanguageSetups[index]); + } else { + runtimeSetup.LanguageSetups.Add(CreateRubySetup(setupInitializer)); + } + + return GetEngine(CreateRuntime(runtimeSetup)); + } + + /// + /// Creates IronRuby setup with default language names and file extensions. + /// + public static LanguageSetup/*!*/ CreateRubySetup() { + return new LanguageSetup( + typeof(RubyContext).AssemblyQualifiedName, + RubyContext.IronRubyDisplayName, + RubyContext.IronRubyNames.Split(';'), + RubyContext.IronRubyFileExtensions.Split(';') + ); + } + + public static LanguageSetup/*!*/ CreateRubySetup(Action/*!*/ initializer) { + ContractUtils.RequiresNotNull(initializer, "initializer"); + + var setup = CreateRubySetup(); + initializer(setup); + return setup; + } + + public static ScriptEngine/*!*/ GetEngine(ScriptRuntime/*!*/ runtime) { + ContractUtils.RequiresNotNull(runtime, "runtime"); + return runtime.GetEngineByTypeName(typeof(RubyContext).AssemblyQualifiedName); + } + + public static bool RequireFile(ScriptEngine/*!*/ engine, string/*!*/ path) { + ContractUtils.RequiresNotNull(engine, "engine"); + ContractUtils.RequiresNotNull(path, "path"); + + return HostingHelpers.CallEngine(engine, RequireFile, path); + } + + internal static bool RequireFile(LanguageContext/*!*/ context, string/*!*/ path) { + var rc = (RubyContext)context; + return rc.Loader.LoadFile(rc.DefaultGlobalScope, null, MutableString.Create(path), + LoadFlags.LoadOnce | LoadFlags.AppendExtensions); + } + + // TODO: + public static RubyContext/*!*/ GetExecutionContext(ScriptEngine/*!*/ engine) { + ContractUtils.RequiresNotNull(engine, "engine"); + return (RubyContext)HostingHelpers.GetLanguageContext(engine); + } + + // TODO: + public static RubyContext/*!*/ GetExecutionContext(ScriptRuntime/*!*/ runtime) { + ContractUtils.RequiresNotNull(runtime, "runtime"); + return GetExecutionContext(GetEngine(runtime)); + } + + internal static int IndexOfRubySetup(ScriptRuntimeSetup/*!*/ runtimeSetup) { + for (int i = 0; i < runtimeSetup.LanguageSetups.Count; i++) { + var langSetup = runtimeSetup.LanguageSetups[i]; + if (langSetup.TypeName == typeof(RubyContext).AssemblyQualifiedName) { + return i; + } + } + return -1; + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public static class RubyHostingExtensions { + public static bool RequireRubyFile(this ScriptEngine/*!*/ engine, string/*!*/ path) { + return Ruby.RequireFile(engine, path); + } + + public static ScriptEngine/*!*/ GetRubyEngine(this ScriptRuntime/*!*/ runtime) { + return Ruby.GetEngine(runtime); + } + + public static LanguageSetup/*!*/ AddRubySetup(this ScriptRuntimeSetup/*!*/ runtimeSetup) { + return AddRubySetup(runtimeSetup, null); + } + + /// + /// Adds a new Ruby setup into the given runtime setup unless Ruby setup is already there. + /// Returns the newly created setup or the existing one. + /// If non-null is given and a new setup is created runs the initializer on the new setup instance. + /// + public static LanguageSetup/*!*/ AddRubySetup(this ScriptRuntimeSetup/*!*/ runtimeSetup, Action newSetupInitializer) { + ContractUtils.RequiresNotNull(runtimeSetup, "runtimeSetup"); + + LanguageSetup langSetup; + int index = Ruby.IndexOfRubySetup(runtimeSetup); + if (index == -1) { + langSetup = Ruby.CreateRubySetup(); + if (newSetupInitializer != null) { + newSetupInitializer(langSetup); + } + runtimeSetup.LanguageSetups.Add(langSetup); + } else { + langSetup = runtimeSetup.LanguageSetups[index]; + } + return langSetup; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Ruby.csproj b/merlin/main/languages/ruby/Ruby/Ruby.csproj new file mode 100644 index 0000000000..75bc68a238 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Ruby.csproj @@ -0,0 +1,365 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD} + Library + Properties + IronRuby + IronRuby + SAK + SAK + SAK + SAK + 2.0 + OnOutputUpdated + 618,1685 + + + pdbonly + true + ..\..\..\Bin\FxCop\ + TRACE;SIGNED + prompt + 4 + true + true + ..\..\..\MSSharedLibKey.snk + true + true + -Microsoft.Usage#CA2209;-!Microsoft.Design#CA1012;-!Microsoft.Design#CA2210;-!Microsoft.Design#CA1040;-!Microsoft.Design#CA1005;-!Microsoft.Design#CA1020;-!Microsoft.Design#CA1021;-!Microsoft.Design#CA1010;-!Microsoft.Design#CA1011;-!Microsoft.Design#CA1009;-!Microsoft.Design#CA1050;-!Microsoft.Design#CA1026;-!Microsoft.Design#CA1019;-!Microsoft.Design#CA1031;-!Microsoft.Design#CA1047;-!Microsoft.Design#CA1000;-!Microsoft.Design#CA1048;-!Microsoft.Design#CA1051;-!Microsoft.Design#CA1002;-!Microsoft.Design#CA1061;-!Microsoft.Design#CA1006;-!Microsoft.Design#CA1046;-!Microsoft.Design#CA1045;-!Microsoft.Design#CA1065;-!Microsoft.Design#CA1038;-!Microsoft.Design#CA1008;-!Microsoft.Design#CA1028;-!Microsoft.Design#CA1064;-!Microsoft.Design#CA1004;-!Microsoft.Design#CA1035;-!Microsoft.Design#CA1063;-!Microsoft.Design#CA1032;-!Microsoft.Design#CA1023;-!Microsoft.Design#CA1033;-!Microsoft.Design#CA1039;-!Microsoft.Design#CA1016;-!Microsoft.Design#CA1014;-!Microsoft.Design#CA1017;-!Microsoft.Design#CA1018;-!Microsoft.Design#CA1027;-!Microsoft.Design#CA1059;-!Microsoft.Design#CA1060;-!Microsoft.Design#CA1034;-!Microsoft.Design#CA1013;-!Microsoft.Design#CA1036;-!Microsoft.Design#CA1044;-!Microsoft.Design#CA1041;-!Microsoft.Design#CA1025;-!Microsoft.Design#CA1052;-!Microsoft.Design#CA1053;-!Microsoft.Design#CA1057;-!Microsoft.Design#CA1058;-!Microsoft.Design#CA1001;-!Microsoft.Design#CA1049;-!Microsoft.Design#CA1054;-!Microsoft.Design#CA1056;-!Microsoft.Design#CA1055;-!Microsoft.Design#CA1030;-!Microsoft.Design#CA1003;-!Microsoft.Design#CA1007;-!Microsoft.Design#CA1043;-!Microsoft.Design#CA1024;-!Microsoft.Globalization#CA1301;-!Microsoft.Globalization#CA1302;-!Microsoft.Globalization#CA1308;-!Microsoft.Globalization#CA1306;-!Microsoft.Globalization#CA1304;-!Microsoft.Globalization#CA1305;-!Microsoft.Globalization#CA2101;-!Microsoft.Globalization#CA1300;-!Microsoft.Globalization#CA1307;-!Microsoft.Globalization#CA1309;-!Microsoft.Interoperability#CA1403;-!Microsoft.Interoperability#CA1406;-!Microsoft.Interoperability#CA1413;-!Microsoft.Interoperability#CA1402;-!Microsoft.Interoperability#CA1407;-!Microsoft.Interoperability#CA1404;-!Microsoft.Interoperability#CA1410;-!Microsoft.Interoperability#CA1411;-!Microsoft.Interoperability#CA1405;-!Microsoft.Interoperability#CA1409;-!Microsoft.Interoperability#CA1415;-!Microsoft.Interoperability#CA1408;-!Microsoft.Interoperability#CA1414;-!Microsoft.Interoperability#CA1412;-!Microsoft.Interoperability#CA1400;-!Microsoft.Interoperability#CA1401;-!Microsoft.Maintainability#CA1506;-!Microsoft.Maintainability#CA1502;-!Microsoft.Maintainability#CA1501;-!Microsoft.Maintainability#CA1505;-!Microsoft.Maintainability#CA1504;-!Microsoft.Maintainability#CA1500;-!Microsoft.Mobility#CA1600;-!Microsoft.Mobility#CA1601;-!Microsoft.Naming#CA1702;-!Microsoft.Naming#CA1700;-!Microsoft.Naming#CA1712;-!Microsoft.Naming#CA1713;-!Microsoft.Naming#CA1714;-!Microsoft.Naming#CA1709;-!Microsoft.Naming#CA1704;-!Microsoft.Naming#CA1708;-!Microsoft.Naming#CA1715;-!Microsoft.Naming#CA1710;-!Microsoft.Naming#CA1720;-!Microsoft.Naming#CA1707;-!Microsoft.Naming#CA1722;-!Microsoft.Naming#CA1711;-!Microsoft.Naming#CA1716;-!Microsoft.Naming#CA1717;-!Microsoft.Naming#CA1725;-!Microsoft.Naming#CA1719;-!Microsoft.Naming#CA1721;-!Microsoft.Naming#CA1701;-!Microsoft.Naming#CA1703;-!Microsoft.Naming#CA1724;-!Microsoft.Naming#CA1726;-!Microsoft.Performance#CA1809;-!Microsoft.Performance#CA1811;-!Microsoft.Performance#CA1812;-!Microsoft.Performance#CA1813;+!Microsoft.Performance#CA1823;-!Microsoft.Performance#CA1800;-!Microsoft.Performance#CA1805;-!Microsoft.Performance#CA1810;+!Microsoft.Performance#CA1824;-!Microsoft.Performance#CA1822;-!Microsoft.Performance#CA1815;-!Microsoft.Performance#CA1814;-!Microsoft.Performance#CA1819;-!Microsoft.Performance#CA1821;-!Microsoft.Performance#CA1804;-!Microsoft.Performance#CA1820;-!Microsoft.Performance#CA1802;+!Microsoft.Portability#CA1903;+!Microsoft.Portability#CA1901;+!Microsoft.Portability#CA1900;+!Microsoft.Reliability#CA2001;+!Microsoft.Reliability#CA2002;+!Microsoft.Reliability#CA2003;+!Microsoft.Reliability#CA2004;+!Microsoft.Reliability#CA2006;+!Microsoft.Security#CA2116;+!Microsoft.Security#CA2117;+!Microsoft.Security#CA2105;+!Microsoft.Security#CA2115;+!Microsoft.Security#CA2102;-!Microsoft.Security#CA2104;+!Microsoft.Security#CA2122;+!Microsoft.Security#CA2114;+!Microsoft.Security#CA2123;+!Microsoft.Security#CA2111;+!Microsoft.Security#CA2108;+!Microsoft.Security#CA2107;+!Microsoft.Security#CA2103;+!Microsoft.Security#CA2118;+!Microsoft.Security#CA2109;+!Microsoft.Security#CA2119;+!Microsoft.Security#CA2106;+!Microsoft.Security#CA2112;+!Microsoft.Security#CA2120;+!Microsoft.Security#CA2121;+!Microsoft.Security#CA2126;+!Microsoft.Security#CA2124;+!Microsoft.Security#CA2127;+!Microsoft.Security#CA2128;+!Microsoft.Security#CA2129;-!Microsoft.Usage#CA2243;-!Microsoft.Usage#CA2236;-!Microsoft.Usage#CA1816;-!Microsoft.Usage#CA2227;-!Microsoft.Usage#CA2213;-!Microsoft.Usage#CA2216;-!Microsoft.Usage#CA2214;-!Microsoft.Usage#CA2222;-!Microsoft.Usage#CA1806;-!Microsoft.Usage#CA2217;-!Microsoft.Usage#CA2212;-!Microsoft.Usage#CA2219;-!Microsoft.Usage#CA2201;-!Microsoft.Usage#CA2228;-!Microsoft.Usage#CA2221;-!Microsoft.Usage#CA2220;-!Microsoft.Usage#CA2240;-!Microsoft.Usage#CA2229;-!Microsoft.Usage#CA2238;-!Microsoft.Usage#CA2207;-!Microsoft.Usage#CA2208;-!Microsoft.Usage#CA2235;-!Microsoft.Usage#CA2237;-!Microsoft.Usage#CA2232;-!Microsoft.Usage#CA2223;-!Microsoft.Usage#CA2211;-!Microsoft.Usage#CA2233;-!Microsoft.Usage#CA2225;-!Microsoft.Usage#CA2226;-!Microsoft.Usage#CA2231;-!Microsoft.Usage#CA2224;-!Microsoft.Usage#CA2218;-!Microsoft.Usage#CA2234;-!Microsoft.Usage#CA2239;-!Microsoft.Usage#CA2200;-!Microsoft.Usage#CA1801;-!Microsoft.Usage#CA2242;-!Microsoft.Usage#CA2205;-!Microsoft.Usage#CA2230 + + + true + full + false + ..\..\..\Bin\Debug\ + TRACE;DEBUG;SIGNED + prompt + 4 + true + ..\..\..\MSSharedLibKey.snk + true + true + + + pdbonly + true + ..\..\..\Bin\Release\ + TRACE;SIGNED + prompt + 4 + true + ..\..\..\MSSharedLibKey.snk + true + + + true + ..\..\..\Bin\Silverlight Debug\ + TRACE;DEBUG;SILVERLIGHT + full + AnyCPU + prompt + true + true + ..\..\..\SilverlightKey.snk + true + true + ..\..\..\Utilities\Silverlight\x86ret\ + + + ..\..\..\Bin\Silverlight Release\ + TRACE;SILVERLIGHT + true + pdbonly + AnyCPU + prompt + true + ..\..\..\SilverlightKey.snk + true + true + ..\..\..\Utilities\Silverlight\x86ret\ + + + + + False + $(SilverlightSdkPath)\mscorlib.dll + + + + False + $(SilverlightSdkPath)\System.Net.dll + + + False + $(SilverlightSdkPath)\System.dll + + + + + + + Properties\SilverlightVersion.cs + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Properties\SilverlightKey.snk + + + + + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4} + Microsoft.Scripting.Core + False + + + {8B0F1074-750E-4D64-BF23-A1E0F54261E5} + Microsoft.Scripting.ExtensionAttribute + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Scripting + + + + + + + + + + mkdir "$(SolutionDir)..\..\Tools\Nessie\Nessie\bin\Debug" +copy /y "$(TargetPath)" "$(SolutionDir)..\..\Tools\Nessie\Nessie\bin\Debug" + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Ruby/Ruby.csproj.vspscc b/merlin/main/languages/ruby/Ruby/Ruby.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Ruby.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/merlin/main/languages/ruby/Ruby/RubyCompatibility.cs b/merlin/main/languages/ruby/Ruby/RubyCompatibility.cs new file mode 100644 index 0000000000..50d78f6fc5 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/RubyCompatibility.cs @@ -0,0 +1,23 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby { + public enum RubyCompatibility { + Default = Ruby18, + Ruby18 = 0, + Ruby19 = 190, + Ruby20 = 200, + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Attributes.cs b/merlin/main/languages/ruby/Ruby/Runtime/Attributes.cs new file mode 100644 index 0000000000..9dd53a5625 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Attributes.cs @@ -0,0 +1,311 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; + +namespace IronRuby.Runtime { + + public abstract class RubyAttribute : Attribute { + private string _buildConfig; + + /// + /// If set, indicates what build configurations this module should be available under + /// Can be any string that is valid after a #if + /// Default is to be available under all configurations + /// + /// typical usage: BuildConfig = "!SILVERLIGHT" + /// + /// TODO: is there a better way to do this? + /// + public string BuildConfig { + get { return _buildConfig; } + set { _buildConfig = value; } + } + } + + /// + /// Applied to assemblies containing Ruby library methods. + /// Specifies an initializer for the library, which is a type that publishes RubyModules and RubyClasses defined in the assembly. + /// + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RubyLibraryAttribute : Attribute { + private Type/*!*/ _initializer; + + public Type/*!*/ Initializer { + get { return _initializer; } + } + + public RubyLibraryAttribute(Type/*!*/ initializer) { + ContractUtils.RequiresNotNull(initializer, "initializer"); + _initializer = initializer; + } + } + + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method, AllowMultiple = true)] + public sealed class RubyConstantAttribute : RubyAttribute { + private readonly string _name; + + public string Name { get { return _name; } } + + public RubyConstantAttribute() { + } + + public RubyConstantAttribute(string/*!*/ name) { + ContractUtils.RequiresNotNull(name, "name"); + _name = name; + } + } + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public sealed class RubyMethodAttribute : RubyAttribute { + private readonly string/*!*/ _name; + private readonly RubyMethodAttributes _methodAttributes; + + public string/*!*/ Name { + get { return _name; } + } + + public RubyMethodAttributes MethodAttributes { + get { return _methodAttributes; } + } + + public RubyMethodAttribute(string/*!*/ name) + : this(name, RubyMethodAttributes.Default) { + } + + public RubyMethodAttribute(string/*!*/ name, RubyMethodAttributes methodAttributes) + : base() { + ContractUtils.RequiresNotNull(name, "name"); + + _name = name; + _methodAttributes = methodAttributes; + } + } + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public sealed class RubyConstructorAttribute : RubyAttribute { + public RubyConstructorAttribute() { + } + } + +#if TODO + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] + public class RubyExtensionAttribute : RubyAttribute { + private readonly Type/*!*/ _extends; + + public Type/*!*/ Extends { + get { return _extends; } + } + + public RubyExtensionAttribute(Type/*!*/ extends) { + ContractUtils.RequiresNotNull(extends, "extends"); + _extends = extends; + } + } +#endif + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] + public class RubyModuleAttribute : RubyAttribute { + private readonly string _name; + + private bool _mixinInterfaces; + private bool _hideClrMembers; + private Type _extends; + + /// + /// If an extension type doesn't specify Ruby name its name is inferred from the CLR + /// non-generic qualified name of the type being extended. No constant is added to the Object class. + /// + /// Otherwise, the qualified name is the qualified name of the outer Ruby type within which the type is nested + /// or the simple name of the type only if it is not a nested type (non-Ruby types and namespaces are ignored). + /// The constant defined for such module/class is set on the declaring Ruby type or on Object if it is not nested in a Ruby type. + /// + /// Examples: + /// 1) + /// namespace Ruby.Extensions { + /// [RubyClass(Extends = System.Collections.Generic.IDictionary{K,V}] + /// public static class IDictionaryOps {} + /// } + /// + /// Ruby name is "System::Collections::Generic::IDictionary" + /// + /// 2) + /// namespace Ruby.Builtins { + /// [RubyClass("Time", Extends = typeof(DateTime))] + /// public static class TimeOps {} + /// } + /// + /// Ruby name is "Time". Constant "Time" is defined on Object. + /// + /// 3) + /// namespace Ruby.MyLibrary { + /// public class MyType { + /// [RubyModule] + /// public class MyClass { + /// [RubyClass("Foo")] + /// public class Bar { + /// [RubyClass] + /// public class Baz { + /// } + /// + /// [RubyClass("Goo", Extends = typeof(int))] + /// public class Gaz { + /// } + /// + /// [RubyClass(Extends = typeof(int))] + /// public class IntOps { + /// + /// [RubyClass] + /// public class C { + /// } + /// } + /// } + /// } + /// } + /// } + /// + /// names are (full CLR name -> Ruby name): + /// Ruby.MyLibrary.MyType.MyClass -> "MyClass" + /// Ruby.MyLibrary.MyType.MyClass.Bar -> "MyClass::Foo" + /// Ruby.MyLibrary.MyType.MyClass.Bar.Baz -> "MyClass::Foo::Baz" + /// Ruby.MyLibrary.MyType.MyClass.Bar.Gaz -> "MyClass::Foo::Goo" + /// Ruby.MyLibrary.MyType.MyClass.Bar.IntOps -> "System::Int32" + /// Ruby.MyLibrary.MyType.MyClass.Bar.IntOps -> "System::Int32::C" + /// + public string Name { + get { return _name; } + } + + /// + /// Indicates that CLR interface modules should be mixed in to the module. + /// + public bool MixinInterfaces { + get { return _mixinInterfaces; } + set { _mixinInterfaces = value; } + } + + public bool HideClrMembers { + get { return _hideClrMembers; } + set { _hideClrMembers = value; } + } + + public Type Extends { + get { return _extends; } + set { _extends = value; } + } + + public RubyModuleAttribute() { + _name = null; + } + + public RubyModuleAttribute(string/*!*/ name) { + ContractUtils.RequiresNotEmpty(name, "name"); + _name = name; + } + } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public sealed class RubySingletonAttribute : RubyModuleAttribute { + public RubySingletonAttribute() : base() { + } + } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class RubyClassAttribute : RubyModuleAttribute { + private Type/*!*/ _inherits; + + public Type/*!*/ Inherits { + get { return _inherits; } + set { ContractUtils.RequiresNotNull(value, "value"); _inherits = value; } + } + + public RubyClassAttribute() { + } + + public RubyClassAttribute(string/*!*/ name) + : base(name) { + } + } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public sealed class RubyExceptionAttribute : RubyClassAttribute { + public RubyExceptionAttribute(string/*!*/ name) + : base(name) { + } + } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class IncludesAttribute : Attribute { + private readonly Type/*!*/[]/*!*/ _types; + private bool _copy; + + public Type/*!*/[]/*!*/ Types { + get { return _types; } + } + + public bool Copy { + get { return _copy; } + set { _copy = value; } + } + + public IncludesAttribute() { + _types = Type.EmptyTypes; + } + + public IncludesAttribute(params Type[]/*!*/ types) { + ContractUtils.RequiresNotNullItems(types, "types"); + _types = types; + } + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)] + public sealed class HideMethodAttribute : Attribute { + private readonly string/*!*/ _name; + private bool _isStatic; + + public string/*!*/ Name { get { return _name; } } + public bool IsStatic { get { return _isStatic; } set { _isStatic = value; } } + + public HideMethodAttribute(string/*!*/ name) { + ContractUtils.RequiresNotNull(name, "name"); + _name = name; + } + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)] + public sealed class UndefineMethodAttribute : Attribute { + private readonly string/*!*/ _name; + private bool _isStatic; + + public string/*!*/ Name { get { return _name; } } + public bool IsStatic { get { return _isStatic; } set { _isStatic = value; } } + + public UndefineMethodAttribute(string/*!*/ name) { + ContractUtils.RequiresNotNull(name, "name"); + _name = name; + } + } + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public sealed class RubyStackTraceHiddenAttribute : Attribute { + } + + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] + public class DefaultProtocolAttribute : Attribute { + } +} + diff --git a/merlin/main/languages/ruby/Ruby/Runtime/BinaryEncoding.cs b/merlin/main/languages/ruby/Ruby/Runtime/BinaryEncoding.cs new file mode 100644 index 0000000000..3ced7176ea --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/BinaryEncoding.cs @@ -0,0 +1,93 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Text; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Runtime { + + /// + /// ASCII-8BIT encoding: + /// 0xmn -> \u00mn + /// \u00mn -> 0xmn, error otherwise + /// + public sealed class BinaryEncoding : Encoding { + public static readonly Encoding/*!*/ Instance = new BinaryEncoding(); + + // TODO: remove + public static readonly Encoding/*!*/ Obsolete = Instance; + + private BinaryEncoding() { + } + + public override int GetByteCount(char[]/*!*/ chars, int index, int count) { + return count; + } + + public override int GetCharCount(byte[]/*!*/ bytes, int index, int count) { + return count; + } + + public override int GetMaxByteCount(int charCount) { + return charCount; + } + + public override int GetMaxCharCount(int byteCount) { + return byteCount; + } + + public override int GetBytes(char[]/*!*/ chars, int charIndex, int charCount, byte[]/*!*/ bytes, int byteIndex) { + ContractUtils.RequiresArrayRange(chars, charIndex, charCount, "charIndex", "charCount"); + ContractUtils.RequiresArrayRange(bytes, byteIndex, charCount, "byteIndex", "charCount"); + + try { + for (int i = 0; i < charCount; i++) { + bytes[byteIndex + i] = checked((byte)chars[charIndex + i]); + } + } catch (OverflowException) { + // TODO: we don't support fallbacks + throw new EncoderFallbackException(); + } + + return charCount; + } + + public override int GetChars(byte[]/*!*/ bytes, int byteIndex, int byteCount, char[]/*!*/ chars, int charIndex) { + ContractUtils.RequiresArrayRange(bytes, byteIndex, byteCount, "byteIndex", "byteCount"); + ContractUtils.RequiresArrayRange(chars, charIndex, byteCount, "charIndex", "byteCount"); + + for (int i = 0; i < byteCount; i++) { + chars[byteIndex + i] = (char)bytes[charIndex + i]; + } + + return byteCount; + } + +#if !SILVERLIGHT + public override string/*!*/ EncodingName { + get { return "ASCII-8BIT"; } + } + + public override bool IsSingleByte { + get { return true; } + } +#endif + + public override string/*!*/ WebName { + get { return "ASCII-8BIT"; } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/BlockParam.Meta.cs b/merlin/main/languages/ruby/Ruby/Runtime/BlockParam.Meta.cs new file mode 100644 index 0000000000..d84a5ecfae --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/BlockParam.Meta.cs @@ -0,0 +1,65 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime.Calls; + +using Ast = System.Linq.Expressions.Expression; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using IronRuby.Compiler; + +namespace IronRuby.Runtime { + public sealed partial class BlockParam : IDynamicObject { + public MetaObject/*!*/ GetMetaObject(Expression/*!*/ parameter) { + return new Meta(parameter, Restrictions.Empty, this); + } + + internal sealed class Meta : MetaObject { + private BlockParam BlockParam { + get { return (BlockParam)Value; } + } + + public Meta(Expression/*!*/ expression, Restrictions/*!*/ restrictions, BlockParam/*!*/ value) + : base(expression, restrictions, value) { + ContractUtils.RequiresNotNull(value, "value"); + } + + public override MetaObject/*!*/ BindInvoke(InvokeBinder/*!*/ action, MetaObject/*!*/[]/*!*/ args) { + RubyCallSignature callSignature; + if (RubyCallSignature.TryCreate(action.Arguments, out callSignature)) { + return action.FallbackInvoke(this, args); + } + + var metaBuilder = new MetaObjectBuilder(); + + var context = new MetaObject( + Methods.GetContextFromBlockParam.OpCall(AstUtils.Convert(Expression, typeof(BlockParam))), + Restrictions.Empty, + RubyOps.GetContextFromBlockParam((BlockParam)Value) + ); + + BlockParam.SetCallActionRule(metaBuilder, new CallArguments(context, this, args, callSignature)); + return metaBuilder.CreateMetaObject(action, args); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/BlockParam.cs b/merlin/main/languages/ruby/Ruby/Runtime/BlockParam.cs new file mode 100644 index 0000000000..75355d045d --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/BlockParam.cs @@ -0,0 +1,256 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Reflection; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Runtime.Calls; +using MSA = System.Linq.Expressions; +using Ast = System.Linq.Expressions.Expression; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Runtime { + + public enum BlockReturnReason { + Undefined = 0, + Retry, + Break + } + + public enum BlockCallerKind { + Yield, + Call + } + + internal sealed class MissingBlockParam { + } + + public sealed partial class BlockParam { + // -- in -- + private readonly Proc/*!*/ _proc; + private readonly BlockCallerKind _callerKind; + + // filled by define_method, module_eval, load: if not null than method definition and method alias uses the module + private RubyModule _moduleDeclaration; + + // filled by define_method: if not null then injects a scope in super call method lookup: + private readonly string _superMethodName; + + // Is the library method call taking this BlockParam a proc converter? + // Used only for BlockParams that are passed to library method calls. + // Friend: RubyOps + internal readonly bool _isLibProcConverter; + + // -- out -- + private BlockReturnReason _returnReason; + private RuntimeFlowControl _targetFrame; + private ProcKind _sourceProcKind; + + internal BlockCallerKind CallerKind { get { return _callerKind; } } + internal ProcKind SourceProcKind { get { return _sourceProcKind; } } + internal BlockReturnReason ReturnReason { get { return _returnReason; } set { _returnReason = value; } } + internal RuntimeFlowControl TargetFrame { get { return _targetFrame; } } + internal RubyModule ModuleDeclaration { get { return _moduleDeclaration; } set { _moduleDeclaration = value; } } + internal string SuperMethodName { get { return _superMethodName; } } + internal bool IsLibProcConverter { get { return _isLibProcConverter; } } + + public Proc/*!*/ Proc { get { return _proc; } } + + [Emitted] + public object Self { get { return _proc.Self; } } + + public RubyContext RubyContext { + get { return _proc.LocalScope.RubyContext; } + } + + internal static PropertyInfo/*!*/ SelfProperty { get { return typeof(BlockParam).GetProperty("Self"); } } + + // friend: RubyOps + internal BlockParam(Proc/*!*/ proc, BlockCallerKind callerKind, bool isLibProcConverter, RubyModule moduleDeclaration, string superName) { + _callerKind = callerKind; + _proc = proc; + _isLibProcConverter = isLibProcConverter; + _moduleDeclaration = moduleDeclaration; + _superMethodName = superName; + } + + internal void SetFlowControl(BlockReturnReason reason, RuntimeFlowControl targetFrame, ProcKind sourceProcKind) { + Debug.Assert((reason == BlockReturnReason.Break) == (targetFrame != null)); + + _returnReason = reason; + _targetFrame = targetFrame; + _sourceProcKind = sourceProcKind; + } + + internal object GetUnwinderResult(EvalUnwinder/*!*/ unwinder) { + Debug.Assert(unwinder != null); + SetFlowControl(unwinder.Reason, unwinder.TargetFrame, unwinder.SourceProcKind); + return unwinder.ReturnValue; + } + + internal void MultipleValuesForBlockParameterWarning(int actualCount) { + Debug.Assert(actualCount == 0 || actualCount > 1); + _proc.LocalScope.RubyContext.ReportWarning(String.Format("multiple values for a block parameter ({0} for 1)", actualCount)); + } + + #region Library Block Yield Helpers + + /// + /// Used by library methods that take a block. The binder creates an instance of BlockParam holding on RFC if necessary. + /// A library method that creates a block yet doesn't take one needs to manage RFC on its own. + /// + public bool BlockJumped(object returnValue) { + // if this method is a proc converter then the current frame is Proc.Converter, otherwise it is not available: + return RubyOps.MethodYield(_isLibProcConverter ? _proc.Converter : null, this, returnValue); + } + + public object Break(object returnValue) { + Debug.Assert(_proc.Converter != null); + + // unwind to proc converter: + SetFlowControl(BlockReturnReason.Break, _proc.Converter, _proc.Kind); + return returnValue; + } + + public bool Yield(out object blockResult) { + return BlockJumped(blockResult = RubyOps.Yield0(Self, this)); + } + + public bool Yield(object arg1, out object blockResult) { + return BlockJumped(blockResult = RubyOps.Yield1(arg1, Self, this)); + } + + public bool Yield(object arg1, object arg2, out object blockResult) { + return BlockJumped(blockResult = RubyOps.Yield2(arg1, arg2, Self, this)); + } + + public bool Yield(object arg1, object arg2, object arg3, out object blockResult) { + return BlockJumped(blockResult = RubyOps.Yield3(arg1, arg2, arg3, Self, this)); + } + + public bool Yield(object arg1, object arg2, object arg3, object arg4, out object blockResult) { + return BlockJumped(blockResult = RubyOps.Yield4(arg1, arg2, arg3, arg4, Self, this)); + } + + public bool Yield(object[]/*!*/ args, out object blockResult) { + ContractUtils.RequiresNotNull(args, "args"); + switch (args.Length) { + case 0: blockResult = RubyOps.Yield0(Self, this); break; + case 1: blockResult = RubyOps.Yield1(args[0], Self, this); break; + case 2: blockResult = RubyOps.Yield2(args[0], args[1], Self, this); break; + case 3: blockResult = RubyOps.Yield3(args[0], args[1], args[2], Self, this); break; + case 4: blockResult = RubyOps.Yield4(args[0], args[1], args[2], args[3], Self, this); break; + default: blockResult = RubyOps.YieldN(args, Self, this); break; + } + return BlockJumped(blockResult); + } + + #endregion + + #region Dynamic Operations + + /// + /// OldCallAction on Proc target. + /// From control flow perspective it "yields" to the proc. + /// + internal void SetCallActionRule(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + Assert.NotNull(metaBuilder, args); + Debug.Assert(!args.Signature.HasBlock); + + var convertedTarget = AstUtils.Convert(args.TargetExpression, typeof(BlockParam)); + + // test for target type: + metaBuilder.AddTypeRestriction(args.Target.GetType(), args.TargetExpression); + + metaBuilder.Result = AstFactory.YieldExpression( + args.GetSimpleArgumentExpressions(), + args.GetSplattedArgumentExpression(), + args.GetRhsArgumentExpression(), + convertedTarget, // block param + Ast.Property(convertedTarget, SelfProperty) // self + ); + } + + #endregion + + } + + #region RubyOps + + public static partial class RubyOps { + + [Emitted] + public static bool IsProcConverterTarget(BlockParam/*!*/ bfc, MethodUnwinder/*!*/ unwinder) { + Debug.Assert(unwinder != null); + return bfc.IsLibProcConverter && unwinder.TargetFrame == bfc.Proc.Converter; + } + + [Emitted] + public static BlockParam/*!*/ CreateBfcForYield(Proc proc) { + if (proc != null) { + return new BlockParam(proc, BlockCallerKind.Yield, false, null, null); + } else { + throw RubyExceptions.NoBlockGiven(); + } + } + + [Emitted] + public static BlockParam/*!*/ CreateBfcForMethodProcCall(Proc/*!*/ proc, RubyLambdaMethodInfo/*!*/ method) { + Assert.NotNull(proc, method); + return new BlockParam(proc, BlockCallerKind.Call, false, method.DeclaringModule, method.DefinitionName); + } + + [Emitted] + public static BlockParam/*!*/ CreateBfcForProcCall(Proc/*!*/ proc) { + Assert.NotNull(proc); + return new BlockParam(proc, BlockCallerKind.Call, false, null, null); + } + + [Emitted] + public static BlockParam/*!*/ CreateBfcForLibraryMethod(Proc/*!*/ proc) { + Assert.NotNull(proc); + bool isProcConverter; + + if (proc.Kind == ProcKind.Block) { + proc.Converter = new RuntimeFlowControl(); + proc.Converter.IsActiveMethod = true; + proc.Kind = ProcKind.Proc; + isProcConverter = true; + } else { + isProcConverter = false; + } + + return new BlockParam(proc, BlockCallerKind.Yield, isProcConverter, null, null); + } + + [Emitted] + public static void LeaveProcConverter(BlockParam/*!*/ bfc) { + Debug.Assert(bfc.Proc != null); + if (bfc._isLibProcConverter) { + Debug.Assert(bfc.Proc.Converter != null); + bfc.Proc.Converter.IsActiveMethod = false; + } + } + } + + #endregion +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/ArgsBuilder.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/ArgsBuilder.cs new file mode 100644 index 0000000000..651a423bb4 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/ArgsBuilder.cs @@ -0,0 +1,198 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using IronRuby.Compiler; + +namespace IronRuby.Runtime.Calls { + using Ast = System.Linq.Expressions.Expression; + + public sealed class ArgsBuilder { + private readonly Expression[]/*!*/ _arguments; + private readonly int _mandatoryParamCount; + private readonly int _optionalParamCount; + private readonly bool _hasUnsplatParameter; + + // Actual arguments that overflow the signature of the callee and thus will be unsplatted. + private List _argsToUnsplat; + + private int _nextArgIndex; + + // Total number of arguments explicitly passed to the call site + // (whether they map to a mandatory, optional or unsplat parameter). + private int _explicitArgCount; + + public int ExplicitArgumentCount { + get { return _explicitArgCount; } + } + + public int MandatoryParamCount { + get { return _mandatoryParamCount; } + } + + public int OptionalParamCount { + get { return _optionalParamCount; } + } + + public bool HasUnsplatParameter { + get { return _hasUnsplatParameter; } + } + + public bool HasTooFewArguments { + get { return _explicitArgCount < _mandatoryParamCount; } + } + + public bool HasTooManyArguments { + get { return !_hasUnsplatParameter && _explicitArgCount > _mandatoryParamCount + _optionalParamCount; } + } + + /// Parameters for which arguments are provided implicitly, i.e. not specified by user. + /// Number of parameters for which an actual argument must be specified. + /// Number of optional parameters. + /// Method has * parameter (accepts any number of additional parameters). + public ArgsBuilder(int implicitParamCount, int mandatoryParamCount, int optionalParamCount, bool hasUnsplatParameter) { + _arguments = new Expression[implicitParamCount + mandatoryParamCount + optionalParamCount + (hasUnsplatParameter ? 1 : 0)]; + _mandatoryParamCount = mandatoryParamCount; + _optionalParamCount = optionalParamCount; + _nextArgIndex = implicitParamCount; + _explicitArgCount = 0; + _argsToUnsplat = null; + _hasUnsplatParameter = hasUnsplatParameter; + } + + /// + /// Adds explicit arguments and maps themp to parameters. + /// + public void AddRange(IList/*!*/ values) { + foreach (Expression value in values) { + Add(value); + } + } + + /// + /// Adds an explicit argument and maps it to a parameter. + /// + public void Add(Expression/*!*/ value) { + Assert.NotNull(value); + + if (_explicitArgCount < _mandatoryParamCount + _optionalParamCount) { + Debug.Assert(_nextArgIndex < _arguments.Length); + _arguments[_nextArgIndex++] = value; + } else { + if (_argsToUnsplat == null) { + _argsToUnsplat = new List(); + } + _argsToUnsplat.Add(value); + } + + _explicitArgCount++; + } + + public void SetImplicit(int index, Expression/*!*/ arg) { + _arguments[index] = arg; + } + + public Expression this[int index] { + get { return _arguments[index]; } + } + + internal Expression[]/*!*/ GetArguments() { + Debug.Assert(_nextArgIndex == _arguments.Length); + return _arguments; + } + + // Adds an argument expression that wraps the remaining arguments into an array. + public void AddUnsplat() { + Debug.Assert(_hasUnsplatParameter); + Debug.Assert(_nextArgIndex == _arguments.Length - 1); + _arguments[_nextArgIndex++] = (_argsToUnsplat != null) ? Methods.MakeArrayOpCall(_argsToUnsplat) : Methods.MakeArray0.OpCall(); + } + + /// + /// Fills missing arguments with the missing argument placeholder (RubyOps.DefaultArgument singleton). + /// + public void FillMissingArguments() { + for (int i = _explicitArgCount; i < _mandatoryParamCount + _optionalParamCount; i++) { + // TODO: optimize field read? + _arguments[_nextArgIndex++] = Ast.Field(null, Fields.RubyOps_DefaultArgumentField); + } + } + + public void AddSplatted(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + var arg = args.GetSplattedArgument(); + var parameter = args.GetSplattedArgumentExpression(); + + int listLength; + ParameterExpression listVariable; + if (metaBuilder.AddSplattedArgumentTest(arg, parameter, out listLength, out listVariable)) { + if (listLength > 0) { + for (int i = 0; i < listLength; i++) { + Add( + Ast.Call( + listVariable, + typeof(List).GetMethod("get_Item"), + Ast.Constant(i) + ) + ); + } + } + } else { + // argument is not an array => add the argument itself: + Add(parameter); + } + } + + public void AddCallArguments(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + // simple args: + for (int i = 0; i < args.SimpleArgumentCount; i++) { + Add(args.GetSimpleArgumentExpression(i)); + } + + // splat arg: + if (args.Signature.HasSplattedArgument) { + AddSplatted(metaBuilder, args); + } + + // rhs arg: + if (args.Signature.HasRhsArgument) { + Add(args.GetRhsArgumentExpression()); + } + + if (HasTooFewArguments) { + metaBuilder.SetWrongNumberOfArgumentsError(_explicitArgCount, _mandatoryParamCount); + return; + } + + if (HasTooManyArguments) { + metaBuilder.SetWrongNumberOfArgumentsError(_explicitArgCount, _mandatoryParamCount); + return; + } + + // add optional placeholders: + FillMissingArguments(); + + if (_hasUnsplatParameter) { + AddUnsplat(); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/BlockDispatcher.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/BlockDispatcher.cs new file mode 100644 index 0000000000..c64d230b3c --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/BlockDispatcher.cs @@ -0,0 +1,349 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Linq.Expressions; +using System.Reflection; +using IronRuby.Builtins; + +namespace IronRuby.Runtime.Calls { + using Ast = System.Linq.Expressions.Expression; + using AstFactory = IronRuby.Compiler.Ast.AstFactory; + + [Flags] + public enum BlockSignatureAttributes { + None = 0, + + // {|(...)|} + HasSingleCompoundParameter = 1, + + // {|*|} + // {|...,*|} + HasUnsplatParameter = 2, + + // bits 31..3 store arity + } + + public delegate object BlockCallTarget0(BlockParam param, object self); + public delegate object BlockCallTarget1(BlockParam param, object self, object arg1); + public delegate object BlockCallTarget2(BlockParam param, object self, object arg1, object arg2); + public delegate object BlockCallTarget3(BlockParam param, object self, object arg1, object arg2, object arg3); + public delegate object BlockCallTarget4(BlockParam param, object self, object arg1, object arg2, object arg3, object arg4); + public delegate object BlockCallTargetN(BlockParam param, object self, object[] args); + public delegate object BlockCallTargetUnsplatN(BlockParam param, object self, object[] args, RubyArray/*!*/ array); + + public abstract class BlockDispatcher { + private readonly BlockSignatureAttributes _attributesAndArity; + + public bool HasSingleCompoundParameter { + get { return (_attributesAndArity & BlockSignatureAttributes.HasSingleCompoundParameter) != 0; } + } + + public bool HasUnsplatParameter { + get { return (_attributesAndArity & BlockSignatureAttributes.HasUnsplatParameter) != 0; } + } + + public int Arity { + get { return ((int)_attributesAndArity >> 2); } + } + + // Doesn't include unsplat parameter. + // Includes anonymous parameter. + public abstract int ParameterCount { get; } + public abstract Delegate/*!*/ Method { get; } + + public abstract object Invoke(BlockParam/*!*/ param, object self); + public abstract object InvokeNoAutoSplat(BlockParam/*!*/ param, object self, object arg1); + public abstract object Invoke(BlockParam/*!*/ param, object self, object arg1); + public abstract object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2); + public abstract object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3); + public abstract object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4); + public abstract object Invoke(BlockParam/*!*/ param, object self, object[]/*!*/ args); + + public abstract object InvokeSplat(BlockParam/*!*/ param, object self, object splattee); + public abstract object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object splattee); + public abstract object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object splattee); + public abstract object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object splattee); + public abstract object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4, object splattee); + public abstract object InvokeSplat(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee); + + public abstract object InvokeSplatRhs(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee, object rhs); + + internal const int MaxBlockArity = 4; + internal const int HiddenParameterCount = 2; + + internal BlockDispatcher(BlockSignatureAttributes attributes) { + _attributesAndArity = attributes; + } + + internal static BlockDispatcher/*!*/ Create(Delegate/*!*/ method, int parameterCount, BlockSignatureAttributes attributes) { + if ((attributes & BlockSignatureAttributes.HasUnsplatParameter) == 0) { + switch (parameterCount) { + case 0: return new BlockDispatcher0((BlockCallTarget0)method, attributes); + case 1: return new BlockDispatcher1((BlockCallTarget1)method, attributes); + case 2: return new BlockDispatcher2((BlockCallTarget2)method, attributes); + case 3: return new BlockDispatcher3((BlockCallTarget3)method, attributes); + case 4: return new BlockDispatcher4((BlockCallTarget4)method, attributes); + default: return new BlockDispatcherN((BlockCallTargetN)method, parameterCount, attributes); + } + } + + return new BlockDispatcherUnsplatN((BlockCallTargetUnsplatN)method, parameterCount, attributes); + } + + internal static Type/*!*/ GetDelegateType(int parameterCount, BlockSignatureAttributes attributes) { + if ((attributes & BlockSignatureAttributes.HasUnsplatParameter) == 0) { + switch (parameterCount) { + case 0: return typeof(BlockCallTarget0); + case 1: return typeof(BlockCallTarget1); + case 2: return typeof(BlockCallTarget2); + case 3: return typeof(BlockCallTarget3); + case 4: return typeof(BlockCallTarget4); + default: return typeof(BlockCallTargetN); + } + } + return typeof(BlockCallTargetUnsplatN); + } + + private static void CopyArgumentsFromSplattee(object[]/*!*/ args, int initializedArgCount, int parameterCount, + out int nextArg, out int nextItem, object splattee) { + + int i = Math.Min(initializedArgCount, parameterCount); + int j = 0; + var list = splattee as List; + if (list != null) { + while (i < parameterCount && j < list.Count) { + args[i++] = list[j++]; + } + } else if (i < parameterCount) { + args[i++] = splattee; + j++; + } + + nextArg = i; + nextItem = j; + } + + // Expects first "initializeArgCount" slots of "args" array initialized with actual argument values + // and fills the rest by splatting "splattee". The size of the array "args" is the number of formal parameters the block takes. + internal static object[]/*!*/ CopyArgumentsFromSplattee(object[]/*!*/ args, int initializedArgCount, object splattee) { + int nextArg, nextItem; + CopyArgumentsFromSplattee(args, initializedArgCount, args.Length, out nextArg, out nextItem, splattee); + return args; + } + + internal static void CreateArgumentsFromSplattee(int parameterCount, out int nextArg, out int nextItem, ref object[]/*!*/ args, object splattee) { + // the args array is passed to the block, we need at least space for all explicit parameters: + int originalLength = args.Length; + if (args.Length < parameterCount) { + Array.Resize(ref args, parameterCount); + } + + CopyArgumentsFromSplattee(args, originalLength, parameterCount, out nextArg, out nextItem, splattee); + } + + internal static object[]/*!*/ CreateArgumentsFromSplatteeAndRhs(int parameterCount, object[]/*!*/ args, object splattee, object rhs) { + int nextArg, nextItem; + + // the args array is passed to the block, we need at least space for all explicit parameters: + CreateArgumentsFromSplattee(parameterCount, out nextArg, out nextItem, ref args, splattee); + + if (nextArg < args.Length) { + args[nextArg++] = rhs; + } + + return args; + } + +#if OBSOLETE + private Expression/*!*/ AddWarning(Expression/*!*/ codeContextExpression, Expression/*!*/ expression) { + Assert.NotNull(codeContextExpression, expression); + + // do not report warning if the only parameter is a nested left value: + if (FirstArgumentIsNestedLValue) { + return expression; + } + + return Methods.MultipleValuesForBlockParameterWarning", codeContextExpression, expression); + } + + private void SetCallRuleArguments( + Expression/*!*/ blockParameterExpression, // special arg #0 + Expression/*!*/ selfParameterExpression, // special arg #1 + CallArguments/*!*/ args, // user args + Expression/*!*/ codeContextExpression, + MetaObjectBuilder/*!*/ rule, + ArgsBuilder/*!*/ actualArgs) { + + // mandatory args: + actualArgs.Add(blockParameterExpression); + actualArgs.Add(selfParameterExpression); + + int parameterIndex = 0; + + // mimics CompoundLeftValue.TransformWrite // + + // L(1,-)? + bool leftOneNone = OptionalParamCount == 1 && !HasParamsArray; + + // L(0,*)? + bool leftNoneSplat = OptionalParamCount == 0 && HasParamsArray; + + // R(0,*)? + bool rightNoneSplat = !args.Signature.IsSimple && args.Length == 1 && args.GetArgumentKind(0) == ArgumentKind.List; + + // R(1,-)? + bool rightOneNone = !args.Signature.IsSimple && args.Length == 1 && args.GetArgumentKind(0) == ArgumentKind.Simple + || args.Signature.IsSimple && args.Length == 1; + + // R(1,*)? + bool rightOneSplat = !args.Signature.IsSimple && args.Length == 2 && + args.GetArgumentKind(0) == ArgumentKind.Simple && + args.GetArgumentKind(1) == ArgumentKind.List; + + // R(0,-)? + bool rightNoneNone = args.Length == 0; + + if (leftOneNone) { + Expression rvalue; + + if (rightOneNone) { + // simple assignment + rvalue = args.Expressions[parameterIndex]; + } else if (rightOneSplat && TestEmptyList(rule, args.Values[parameterIndex + 1], args.Expressions[parameterIndex + 1])) { + // simple assignment if the splatted value is an empty array: + rvalue = args.Expressions[parameterIndex]; + } else if (rightNoneNone) { + // nil assignment + rvalue = AddWarning(codeContextExpression, Ast.Constant(null)); + } else if (rightNoneSplat) { + // Splat(RHS[*]): + rvalue = MakeArgumentSplatWithWarning(rule, args.Values[parameterIndex], args.Expressions[parameterIndex], codeContextExpression); + } else { + // more than one argument -> pack to an array + warning + + // MakeArray(RHS) + SplatAppend(RHS*): + List arguments = new List(); + AddBlockArguments(rule, arguments, args, parameterIndex); + rvalue = AddWarning(codeContextExpression, ArgsBuilder.MakeArgsArray(arguments)); + } + + actualArgs.Add(rvalue); + + } else { + + // R(0,*) || R(1,-) && !L(0,*) ==> CompoundLeftValue.TransformWrite does Unsplat, MakeArray otherwise. + // + // However, we are not constructing a materalized resulting array (contrary to CompoundLeftValue.TransformWrite). + // The resulting array is comprised of slots on the stack (loaded to the formal parameters of the block #1, ..., #n). + // Therefore, we effectively need to take items of imaginary Unsplat's result and put them into the actualArgs as arguments. + // + // Unsplat of x makes an array containing x if x is not an array, otherwise it returns x. + // So, we just need to take elements of x and push them onto the stack. + // + + List arguments = new List(); + + if (rightNoneSplat) { + ArgsBuilder.SplatListToArguments(rule, arguments, args.Values[parameterIndex], args.Expressions[parameterIndex], false); + } else if (rightOneNone && !leftNoneSplat) { + ArgsBuilder.SplatListToArguments(rule, arguments, args.Values[parameterIndex], args.Expressions[parameterIndex], true); + } else { + AddBlockArguments(rule, arguments, args, parameterIndex); + } + + actualArgs.AddRange(arguments); + } + + actualArgs.AddForEachMissingArgument(delegate() { return Ast.Constant(null); }); + + if (HasParamsArray) { + actualArgs.AddParamsArray(); + } + } + + private bool TestEmptyList(MetaObjectBuilder/*!*/ rule, object arg, Expression/*!*/ parameter) { + int listLength; + ParameterExpression listVariable; + return ArgsBuilder.AddTestForListArg(rule, arg, parameter, out listLength, out listVariable) && listLength == 0; + } + + private Expression/*!*/ MakeArgumentSplatWithWarning(MetaObjectBuilder/*!*/ rule, object arg, Expression/*!*/ parameter, + Expression/*!*/ codeContextExpression) { + + int listLength; + ParameterExpression listVariable; + if (ArgsBuilder.AddTestForListArg(rule, arg, parameter, out listLength, out listVariable)) { + if (listLength == 0) { + // return nil argument + Warning + return AddWarning(codeContextExpression, Ast.Constant(null)); + } else if (listLength == 1) { + // return the only item of the array: + return Ast.Call( + listVariable, + typeof(List).GetMethod("get_Item"), + Ast.Constant(0) + ); + } else { + // return the array itself + Warning: + return AddWarning(codeContextExpression, parameter); + } + } else { + // not an array, return the value: + return parameter; + } + } + + private Expression/*!*/ MakeArgumentUnsplat(MetaObjectBuilder/*!*/ rule, object arg, Expression/*!*/ parameter) { + int listLength; + ParameterExpression listVariable; + if (ArgsBuilder.AddTestForListArg(rule, arg, parameter, out listLength, out listVariable)) { + // an array, return: + return parameter; + } else { + // not an array, wrap: + return AstFactory.OptimizedOpCall("MakeArray", parameter); + } + } + + private void AddBlockArguments(MetaObjectBuilder/*!*/ rule, List/*!*/ actualArgs, CallArguments/*!*/ args, int parameterIndex) { + + while (parameterIndex < args.Length) { + switch (args.GetArgumentKind(parameterIndex)) { + case ArgumentKind.Simple: + actualArgs.Add(args.Expressions[parameterIndex]); + break; + + case ArgumentKind.List: + ArgsBuilder.SplatListToArguments(rule, actualArgs, args.Values[parameterIndex], args.Expressions[parameterIndex], false); + break; + + case ArgumentKind.Instance: + case ArgumentKind.Block: + default: + throw new NotImplementedException(); + } + + parameterIndex++; + } + } +#endif + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/BlockDispatcherN.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/BlockDispatcherN.cs new file mode 100644 index 0000000000..517b895d4d --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/BlockDispatcherN.cs @@ -0,0 +1,152 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Scripting.Utils; +using System.Diagnostics; + +namespace IronRuby.Runtime.Calls { + // L(n > 4, -) + internal sealed class BlockDispatcherN : BlockDispatcher { + private readonly BlockCallTargetN/*!*/ _block; + private readonly int _parameterCount; + + public override Delegate/*!*/ Method { get { return _block; } } + public override int ParameterCount { get { return _parameterCount; } } + + internal BlockDispatcherN(BlockCallTargetN/*!*/ block, int parameterCount, BlockSignatureAttributes attributes) + : base(attributes) { + Assert.NotNull(block); + Debug.Assert(parameterCount > BlockDispatcher.MaxBlockArity); + Debug.Assert(!HasUnsplatParameter); + + _parameterCount = parameterCount; + _block = block; + } + + private object[]/*!*/ MakeArray(object arg1) { + var array = new object[_parameterCount]; + array[0] = arg1; + return array; + } + + private object[]/*!*/ MakeArray(object arg1, object arg2) { + var array = new object[_parameterCount]; + array[0] = arg1; + array[1] = arg2; + return array; + } + + private object[]/*!*/ MakeArray(object arg1, object arg2, object arg3) { + var array = new object[_parameterCount]; + array[0] = arg1; + array[1] = arg2; + array[2] = arg3; + return array; + } + + private object[]/*!*/ MakeArray(object arg1, object arg2, object arg3, object arg4) { + var array = new object[_parameterCount]; + array[0] = arg1; + array[1] = arg2; + array[2] = arg3; + array[3] = arg4; + return array; + } + + // R(0, -) + public override object Invoke(BlockParam/*!*/ param, object self) { + // TODO: warning except for L == 1 nested l-value + return _block(param, self, new object[_parameterCount]); + } + + // R(1, -) + public override object InvokeNoAutoSplat(BlockParam/*!*/ param, object self, object arg1) { + return _block(param, self, MakeArray(arg1)); + } + + // R(1, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1) { + return _block(param, self, CopyArgumentsFromSplattee(new object[_parameterCount], 0, arg1)); + } + + // R(2, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2) { + return _block(param, self, MakeArray(arg1, arg2)); + } + + // R(3, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3) { + return _block(param, self, MakeArray(arg1, arg2, arg3)); + } + + // R(4, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4) { + return _block(param, self, MakeArray(arg1, arg2, arg3, arg4)); + } + + // R(N, -) + public override object Invoke(BlockParam/*!*/ param, object self, object[]/*!*/ args) { + Debug.Assert(args.Length > 4); + + // we need at least _parameterCount items in the parameter array: + if (args.Length < _parameterCount) { + Array.Resize(ref args, _parameterCount); + } + + return _block(param, self, args); + } + + // R(0, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object splattee) { + return _block(param, self, CopyArgumentsFromSplattee(new object[_parameterCount], 0, splattee)); + } + + // R(1, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object splattee) { + return _block(param, self, CopyArgumentsFromSplattee(MakeArray(arg1), 1, splattee)); + } + + // R(2, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object splattee) { + return _block(param, self, CopyArgumentsFromSplattee(MakeArray(arg1, arg2), 2, splattee)); + } + + // R(3, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object splattee) { + return _block(param, self, CopyArgumentsFromSplattee(MakeArray(arg1, arg2, arg3), 3, splattee)); + } + + // R(4, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4, object splattee) { + return _block(param, self, CopyArgumentsFromSplattee(MakeArray(arg1, arg2, arg3, arg4), 4, splattee)); + } + + // R(N, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee) { + Debug.Assert(args.Length > MaxBlockArity); + int i, j; + CreateArgumentsFromSplattee(_parameterCount, out i, out j, ref args, splattee); + return _block(param, self, args); + } + + // R(N, *, =) + public override object InvokeSplatRhs(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee, object rhs) { + return _block(param, self, CreateArgumentsFromSplatteeAndRhs(_parameterCount, args, splattee, rhs)); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/BlockDispatcherUnsplatN.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/BlockDispatcherUnsplatN.cs new file mode 100644 index 0000000000..7fc0b39414 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/BlockDispatcherUnsplatN.cs @@ -0,0 +1,178 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Scripting.Utils; +using System.Diagnostics; +using IronRuby.Builtins; + +namespace IronRuby.Runtime.Calls { + // L(n, *) + internal sealed class BlockDispatcherUnsplatN : BlockDispatcher { + private readonly BlockCallTargetUnsplatN/*!*/ _block; + private readonly int _parameterCount; // doesn't include the * parameter + + public override Delegate/*!*/ Method { get { return _block; } } + public override int ParameterCount { get { return _parameterCount; } } + + internal BlockDispatcherUnsplatN(BlockCallTargetUnsplatN/*!*/ block, int parameterCount, BlockSignatureAttributes attributes) + : base(attributes) { + Assert.NotNull(block); + Debug.Assert(HasUnsplatParameter); + + _parameterCount = parameterCount; + _block = block; + } + + // R(0, -) + public override object Invoke(BlockParam/*!*/ param, object self) { + return InvokeInternal(param, self, ArrayUtils.EmptyObjects); + } + + // R(1, -) + public override object InvokeNoAutoSplat(BlockParam/*!*/ param, object self, object arg1) { + return InvokeInternal(param, self, new object[] { arg1 }); // TODO: optimize + } + + // R(1, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1) { + if (_parameterCount > 0) { + return InvokeSplatInternal(param, self, ArrayUtils.EmptyObjects, arg1); // TODO: optimize + } else { + return InvokeInternal(param, self, new object[] { arg1 }); // TODO: optimize + } + } + + // R(2, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2) { + return InvokeInternal(param, self, new object[] { arg1, arg2 });// TODO: optimize + } + + // R(3, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3) { + return InvokeInternal(param, self, new object[] { arg1, arg2, arg3 });// TODO: optimize + } + + // R(4, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4) { + return InvokeInternal(param, self, new object[] { arg1, arg2, arg3, arg4 });// TODO: optimize + } + + // R(N, -) + public override object Invoke(BlockParam/*!*/ param, object self, object[]/*!*/ args) { + return InvokeInternal(param, self, args); + } + + private object InvokeInternal(BlockParam/*!*/ param, object self, object[]/*!*/ args) { + // TODO + if (args.Length < _parameterCount) { + Array.Resize(ref args, _parameterCount); + return _block(param, self, args, RubyOps.MakeArray0()); + } else if (args.Length == _parameterCount) { + return _block(param, self, args, RubyOps.MakeArray0()); + } else { + var actualArgs = new object[_parameterCount]; + + for (int i = 0; i < actualArgs.Length; i++) { + actualArgs[i] = args[i]; + } + + var array = new RubyArray(args.Length - _parameterCount); + for (int i = _parameterCount; i < args.Length; i++) { + array.Add(args[i]); + } + + return _block(param, self, actualArgs, array); + } + } + + // R(0, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object splattee) { + return InvokeSplatInternal(param, self, ArrayUtils.EmptyObjects, splattee); + } + + // R(1, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object splattee) { + return InvokeSplatInternal(param, self, new object[] { arg1 }, splattee); + } + + // R(2, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object splattee) { + return InvokeSplatInternal(param, self, new object[] { arg1, arg2 }, splattee); + } + + // R(3, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object splattee) { + return InvokeSplatInternal(param, self, new object[] { arg1, arg2, arg3 }, splattee); + } + + // R(4, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4, object splattee) { + return InvokeSplatInternal(param, self, new object[] { arg1, arg2, arg3, arg4 }, splattee); + } + + // R(N, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee) { + return InvokeSplatInternal(param, self, args, splattee); + } + + // R(N, *, =) + public override object InvokeSplatRhs(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee, object rhs) { + var list = splattee as List; + if (list != null) { + var l = new RubyArray(list.Count + 1); + l.AddRange(list); + l.Add(rhs); + list = l; + } else { + list = RubyOps.MakeArray2(splattee, rhs); + } + + return InvokeSplatInternal(param, self, args, list, list); + } + + private object InvokeSplatInternal(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee) { + return InvokeSplatInternal(param, self, args, splattee, splattee as List); + } + + private object InvokeSplatInternal(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee, List list) { + int argsLength = args.Length; + + int nextArg, nextItem; + CreateArgumentsFromSplattee(_parameterCount, out nextArg, out nextItem, ref args, splattee); + + var array = new RubyArray(); + + // remaining args: + while (nextArg < argsLength) { + array.Add(args[nextArg++]); + } + + // remaining items: + if (list != null) { + while (nextItem < list.Count) { + array.Add(list[nextItem++]); + } + } else if (nextItem < 1) { + // splattee hasn't been added yet: + array.Add(splattee); + } + + return _block(param, self, args, array); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/BlockDispatchers.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/BlockDispatchers.cs new file mode 100644 index 0000000000..fe9709c8a2 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/BlockDispatchers.cs @@ -0,0 +1,641 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Utils; +using System.Collections.Generic; +using System.Diagnostics; +using IronRuby.Builtins; + +namespace IronRuby.Runtime.Calls { + // L(0, -) + internal sealed class BlockDispatcher0 : BlockDispatcher { + private readonly BlockCallTarget0/*!*/ _block; + + public override Delegate/*!*/ Method { get { return _block; } } + public override int ParameterCount { get { return 0; } } + + public BlockDispatcher0(BlockCallTarget0/*!*/ block, BlockSignatureAttributes attributes) + : base(attributes) { + Assert.NotNull(block); + Debug.Assert(!HasUnsplatParameter); + + _block = block; + } + + // R(0, -) + public override object Invoke(BlockParam/*!*/ param, object self) { + return _block(param, self); + } + + // R(1, -) + public override object InvokeNoAutoSplat(BlockParam/*!*/ param, object self, object arg1) { + return _block(param, self); + } + + // R(1, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1) { + return _block(param, self); + } + + // R(2, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2) { + return _block(param, self); + } + + // R(3, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3) { + return _block(param, self); + } + + // R(4, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4) { + return _block(param, self); + } + + // R(N, -) + public override object Invoke(BlockParam/*!*/ param, object self, object[]/*!*/ args) { + Debug.Assert(args.Length > MaxBlockArity); + return _block(param, self); + } + + // R(0, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object splattee) { + return _block(param, self); + } + + // R(1, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object splattee) { + return _block(param, self); + } + + // R(2, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object splattee) { + return _block(param, self); + } + + // R(3, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object splattee) { + return _block(param, self); + } + + // R(4, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4, object splattee) { + return _block(param, self); + } + + // R(N, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee) { + return _block(param, self); + } + + // R(N, *, =) + public override object InvokeSplatRhs(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee, object rhs) { + return _block(param, self); + } + } + + // L(1, -) + internal sealed class BlockDispatcher1 : BlockDispatcher { + private readonly BlockCallTarget1/*!*/ _block; + + public override Delegate/*!*/ Method { get { return _block; } } + public override int ParameterCount { get { return 1; } } + + public BlockDispatcher1(BlockCallTarget1/*!*/ block, BlockSignatureAttributes attributes) + : base(attributes) { + Assert.NotNull(block); + Debug.Assert(!HasUnsplatParameter); + _block = block; + } + + // R(0, -) + public override object Invoke(BlockParam/*!*/ param, object self) { + if (!HasSingleCompoundParameter) { + param.MultipleValuesForBlockParameterWarning(0); + } + + return _block(param, self, null); + } + + // R(1, -) + public override object InvokeNoAutoSplat(BlockParam/*!*/ param, object self, object arg1) { + return _block(param, self, arg1); + } + + // R(1, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1) { + return _block(param, self, arg1); + } + + // R(2, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2) { + if (!HasSingleCompoundParameter) { + param.MultipleValuesForBlockParameterWarning(2); + } + + return _block(param, self, RubyOps.MakeArray2(arg1, arg2)); + } + + // R(3, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3) { + if (!HasSingleCompoundParameter) { + param.MultipleValuesForBlockParameterWarning(3); + } + + return _block(param, self, RubyOps.MakeArray3(arg1, arg2, arg3)); + } + + // R(4, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4) { + if (!HasSingleCompoundParameter) { + param.MultipleValuesForBlockParameterWarning(4); + } + + return _block(param, self, RubyOps.MakeArray4(arg1, arg2, arg3, arg4)); + } + + // R(N, -) + public override object Invoke(BlockParam/*!*/ param, object self, object[]/*!*/ args) { + Debug.Assert(args.Length > MaxBlockArity); + + if (!HasSingleCompoundParameter) { + param.MultipleValuesForBlockParameterWarning(args.Length); + } + + return _block(param, self, RubyOps.MakeArrayN(args)); + } + + // R(0, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object splattee) { + var list = splattee as List; + if (list != null) { + switch (list.Count) { + case 0: + if (!HasSingleCompoundParameter) { + param.MultipleValuesForBlockParameterWarning(list.Count); + } + splattee = null; + break; + + case 1: + splattee = list[0]; + break; + + default: + if (!HasSingleCompoundParameter) { + param.MultipleValuesForBlockParameterWarning(list.Count); + } + splattee = new RubyArray(list); + break; + } + } + + return _block(param, self, splattee); + } + + // R(1, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object splattee) { + var list = splattee as List; + if (list == null) { + if (!HasSingleCompoundParameter) { + param.MultipleValuesForBlockParameterWarning(2); + } + + arg1 = RubyOps.MakeArray2(arg1, splattee); + } else if (list.Count > 0) { + var array = RubyOps.MakeArray1(arg1); + array.AddRange(list); + arg1 = array; + + if (!HasSingleCompoundParameter) { + param.MultipleValuesForBlockParameterWarning(array.Count); + } + } + + return _block(param, self, arg1); + } + + private object InvokeSplatInternal(BlockParam/*!*/ param, object self, RubyArray/*!*/ array, object splattee) { + Debug.Assert(array.Count >= 2); + + RubyOps.SplatAppend(array, splattee); + + if (!HasSingleCompoundParameter) { + param.MultipleValuesForBlockParameterWarning(array.Count); + } + + return _block(param, self, array); + } + + // R(2, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object splattee) { + return InvokeSplatInternal(param, self, RubyOps.MakeArray2(arg1, arg2), splattee); + } + + // R(3, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object splattee) { + return InvokeSplatInternal(param, self, RubyOps.MakeArray3(arg1, arg2, arg3), splattee); + } + + // R(4, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4, object splattee) { + return InvokeSplatInternal(param, self, RubyOps.MakeArray4(arg1, arg2, arg3, arg4), splattee); + } + + // R(N, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee) { + Debug.Assert(args.Length > MaxBlockArity); + return InvokeSplatInternal(param, self, RubyOps.MakeArrayN(args), splattee); + } + + // R(N, *, =) + public override object InvokeSplatRhs(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee, object rhs) { + var array = new RubyArray(args); + RubyOps.SplatAppend(array, splattee); + array.Add(rhs); + + if (array.Count == 1) { + return _block(param, self, rhs); + } + + Debug.Assert(array.Count >= 2); + + if (!HasSingleCompoundParameter) { + param.MultipleValuesForBlockParameterWarning(array.Count); + } + + return _block(param, self, array); + } + } + + // L(2, -) + internal sealed class BlockDispatcher2 : BlockDispatcher { + private readonly BlockCallTarget2/*!*/ _block; + + public override Delegate/*!*/ Method { get { return _block; } } + public override int ParameterCount { get { return 2; } } + + public BlockDispatcher2(BlockCallTarget2/*!*/ block, BlockSignatureAttributes attributes) + : base(attributes) { + Assert.NotNull(block); + Debug.Assert(!HasUnsplatParameter); + _block = block; + } + + // R(0, -) + public override object Invoke(BlockParam/*!*/ param, object self) { + return _block(param, self, null, null); + } + + // R(1, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1) { + return InvokeSplatInternal(param, self, arg1); + } + + // R(1, -) + public override object InvokeNoAutoSplat(BlockParam/*!*/ param, object self, object arg1) { + return _block(param, self, arg1, null); + } + + // R(2, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2) { + return _block(param, self, arg1, arg2); + } + + // R(3, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3) { + return _block(param, self, arg1, arg2); + } + + // R(4, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4) { + return _block(param, self, arg1, arg2); + } + + // R(N, -) + public override object Invoke(BlockParam/*!*/ param, object self, object[]/*!*/ args) { + Debug.Assert(args.Length > MaxBlockArity); + return _block(param, self, args[0], args[1]); + } + + // R(0, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object splattee) { + return InvokeSplatInternal(param, self, splattee); + } + + private object InvokeSplatInternal(BlockParam/*!*/ param, object self, object splattee) { + var list = splattee as List; + if (list != null) { + switch (list.Count) { + case 0: return _block(param, self, null, null); + case 1: return _block(param, self, list[0], null); + default: return _block(param, self, list[0], list[1]); + } + } + + return _block(param, self, splattee, null); + } + + // R(1, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object splattee) { + var list = splattee as List; + if (list != null) { + switch (list.Count) { + case 0: return _block(param, self, arg1, null); + default: return _block(param, self, arg1, list[0]); + } + } + + return _block(param, self, arg1, splattee); + } + + // R(2, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object splattee) { + return _block(param, self, arg1, arg2); + } + + // R(3, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object splattee) { + return _block(param, self, arg1, arg2); + } + + // R(4, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4, object splattee) { + return _block(param, self, arg1, arg2); + } + + // R(N, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee) { + Debug.Assert(args.Length > MaxBlockArity); + return _block(param, self, args[0], args[1]); + } + + // R(N, *, =) + public override object InvokeSplatRhs(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee, object rhs) { + args = CreateArgumentsFromSplatteeAndRhs(2, args, splattee, rhs); + return _block(param, self, args[0], args[1]); + } + } + + // L(3, -) + internal sealed class BlockDispatcher3 : BlockDispatcher { + private readonly BlockCallTarget3/*!*/ _block; + + public override Delegate/*!*/ Method { get { return _block; } } + public override int ParameterCount { get { return 3; } } + + public BlockDispatcher3(BlockCallTarget3/*!*/ block, BlockSignatureAttributes attributes) + : base(attributes) { + Assert.NotNull(block); + Debug.Assert(!HasUnsplatParameter); + _block = block; + } + + // R(0, -) + public override object Invoke(BlockParam/*!*/ param, object self) { + return _block(param, self, null, null, null); + } + + // R(1, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1) { + return InvokeSplatInternal(param, self, arg1); + } + + // R(1, -) + public override object InvokeNoAutoSplat(BlockParam/*!*/ param, object self, object arg1) { + return _block(param, self, arg1, null, null); + } + + // R(2, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2) { + return _block(param, self, arg1, arg2, null); + } + + // R(3, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3) { + return _block(param, self, arg1, arg2, arg3); + } + + // R(4, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4) { + return _block(param, self, arg1, arg2, arg3); + } + + // R(N, -) + public override object Invoke(BlockParam/*!*/ param, object self, object[]/*!*/ args) { + Debug.Assert(args.Length > MaxBlockArity); + return _block(param, self, args[1], args[2], args[3]); + } + + // R(0, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object splattee) { + return InvokeSplatInternal(param, self, splattee); + } + + private object InvokeSplatInternal(BlockParam/*!*/ param, object self, object splattee) { + var list = splattee as List; + if (list != null) { + switch (list.Count) { + case 0: return _block(param, self, null, null, null); + case 1: return _block(param, self, list[0], null, null); + case 2: return _block(param, self, list[0], list[1], null); + default: return _block(param, self, list[0], list[1], list[2]); + } + } + + return _block(param, self, splattee, null, null); + } + + // R(1, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object splattee) { + var list = splattee as List; + if (list != null) { + switch (list.Count) { + case 0: return _block(param, self, arg1, null, null); + case 1: return _block(param, self, arg1, list[0], null); + default: return _block(param, self, arg1, list[0], list[1]); + } + } + + return _block(param, self, arg1, splattee, null); + } + + // R(2, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object splattee) { + var list = splattee as List; + if (list != null) { + switch (list.Count) { + case 0: return _block(param, self, arg1, arg2, null); + default: return _block(param, self, arg1, arg2, list[0]); + } + } + + return _block(param, self, arg1, arg2, splattee); + } + + // R(3, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object splattee) { + return _block(param, self, arg1, arg2, arg3); + } + + // R(4, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4, object splattee) { + return _block(param, self, arg1, arg2, arg3); + } + + // R(N, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee) { + Debug.Assert(args.Length > MaxBlockArity); + return _block(param, self, args[0], args[1], args[2]); + } + + // R(N, *, =) + public override object InvokeSplatRhs(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee, object rhs) { + args = CreateArgumentsFromSplatteeAndRhs(3, args, splattee, rhs); + return _block(param, self, args[0], args[1], args[2]); + } + } + + // L(4, -) + internal sealed class BlockDispatcher4 : BlockDispatcher { + private readonly BlockCallTarget4/*!*/ _block; + + public override Delegate/*!*/ Method { get { return _block; } } + public override int ParameterCount { get { return 4; } } + + public BlockDispatcher4(BlockCallTarget4/*!*/ block, BlockSignatureAttributes attributes) + : base(attributes) { + Assert.NotNull(block); + Debug.Assert(!HasUnsplatParameter); + _block = block; + } + + // R(0, -) + public override object Invoke(BlockParam/*!*/ param, object self) { + return _block(param, self, null, null, null, null); + } + + // R(1, -) + public override object InvokeNoAutoSplat(BlockParam/*!*/ param, object self, object arg1) { + return _block(param, self, arg1, null, null, null); + } + + // R(1, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1) { + return InvokeSplatInternal(param, self, arg1); + } + + // R(2, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2) { + return _block(param, self, arg1, arg2, null, null); + } + + // R(3, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3) { + return _block(param, self, arg1, arg2, arg3, null); + } + + // R(4, -) + public override object Invoke(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4) { + return _block(param, self, arg1, arg2, arg3, arg4); + } + + // R(N, -) + public override object Invoke(BlockParam/*!*/ param, object self, object[]/*!*/ args) { + Debug.Assert(args.Length > MaxBlockArity); + return _block(param, self, args[1], args[2], args[3], args[4]); + } + + // R(0, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object splattee) { + return InvokeSplatInternal(param, self, splattee); + } + + private object InvokeSplatInternal(BlockParam/*!*/ param, object self, object splattee) { + var list = splattee as List; + if (list != null) { + switch (list.Count) { + case 0: return _block(param, self, null, null, null, null); + case 1: return _block(param, self, list[0], null, null, null); + case 2: return _block(param, self, list[0], list[1], null, null); + case 3: return _block(param, self, list[0], list[1], list[2], null); + default: return _block(param, self, list[0], list[1], list[2], list[3]); + } + } + + return _block(param, self, splattee, null, null, null); + } + + // R(1, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object splattee) { + var list = splattee as List; + if (list != null) { + switch (list.Count) { + case 0: return _block(param, self, arg1, null, null, null); + case 1: return _block(param, self, arg1, list[0], null, null); + case 2: return _block(param, self, arg1, list[0], list[1], null); + default: return _block(param, self, arg1, list[0], list[1], list[2]); + } + } + + return _block(param, self, arg1, splattee, null, null); + } + + // R(2, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object splattee) { + var list = splattee as List; + if (list != null) { + switch (list.Count) { + case 0: return _block(param, self, arg1, arg2, null, null); + case 1: return _block(param, self, arg1, arg2, list[0], null); + default: return _block(param, self, arg1, arg2, list[0], list[1]); + } + } + + return _block(param, self, arg1, arg2, splattee, null); + } + + // R(3, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object splattee) { + var list = splattee as List; + if (list != null) { + switch (list.Count) { + case 0: return _block(param, self, arg1, arg2, arg3, null); + default: return _block(param, self, arg1, arg2, arg3, list[0]); + } + } + + return _block(param, self, arg1, arg2, arg3, splattee); + } + + // R(4, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object arg1, object arg2, object arg3, object arg4, object splattee) { + return _block(param, self, arg1, arg2, arg3, arg4); + } + + // R(N, *) + public override object InvokeSplat(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee) { + Debug.Assert(args.Length > MaxBlockArity); + return _block(param, self, args[0], args[1], args[2], args[3]); + } + + // R(N, *, =) + public override object InvokeSplatRhs(BlockParam/*!*/ param, object self, object[]/*!*/ args, object splattee, object rhs) { + args = CreateArgumentsFromSplatteeAndRhs(4, args, splattee, rhs); + return _block(param, self, args[0], args[1], args[2], args[3]); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/CallArguments.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/CallArguments.cs new file mode 100644 index 0000000000..1ac3a571a4 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/CallArguments.cs @@ -0,0 +1,276 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Actions; +using Ast = System.Linq.Expressions.Expression; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using IronRuby.Compiler; + +namespace IronRuby.Runtime.Calls { + + /// + /// Wraps the arguments of a dynamic call site + /// Includes the actual arguments, the expressions that produced those arguments, + /// and the call signature. + /// + /// These three things are grouped together to ensure that they are all in sync + /// when we want to shift the arguments around during the method binding process. + /// + public sealed class CallArguments { + private readonly MetaObject/*!*/ _context; + + // _args[0] might be target, if so _target is null: + private MetaObject _target; + + // Arguments must be readonly if _copyOnWrite is true. + private MetaObject[]/*!*/ _args; + private bool _copyArgsOnWrite; + + private Expression _scopeExpression; + private Expression _contextExpression; + + private RubyCallSignature _signature; + + public RubyCallSignature/*!*/ Signature { + get { return _signature; } + } + + public int CallSiteArgumentCount { + get { + // context, target, arguments: + return 1 + ExplicitArgumentCount; + } + } + + public int ExplicitArgumentCount { + get { + // target, arguments: + return (_target != null ? 1 : 0) + _args.Length; + } + } + + // Index of the first argument in _args array. + private int FirstArgumentIndex { + get { return (_target == null) ? 1 : 0; } + } + + public int SimpleArgumentCount { + get { + return _args.Length - FirstArgumentIndex - (_signature.HasBlock ? 1 : 0) - + (_signature.HasSplattedArgument ? 1 : 0) - (_signature.HasRhsArgument ? 1 : 0); + } + } + + public Expression/*!*/ ScopeExpression { + get { + if (_scopeExpression == null) { + if (_signature.HasScope) { + _scopeExpression = AstUtils.Convert(MetaContext.Expression, typeof(RubyScope)); + } else { + _scopeExpression = Methods.GetEmptyScope.OpCall(AstUtils.Convert(MetaContext.Expression, typeof(RubyContext))); + } + } + return _scopeExpression; + } + } + + public Expression/*!*/ ContextExpression { + get { + if (_contextExpression == null) { + if (_signature.HasScope) { + _contextExpression = Methods.GetContextFromScope.OpCall(AstUtils.Convert(MetaContext.Expression, typeof(RubyScope))); + } else { + _contextExpression = AstUtils.Convert(MetaContext.Expression, typeof(RubyContext)); + } + } + return _contextExpression; + } + } + + public RubyScope/*!*/ Scope { + get { return (RubyScope)MetaContext.Value; } + } + + public RubyContext/*!*/ RubyContext { + get { return _signature.HasScope ? Scope.RubyContext : (RubyContext)MetaContext.Value; } + } + + // RubyScope or RubyContext + public MetaObject/*!*/ MetaContext { + get { return _context; } + } + + public MetaObject/*!*/ MetaTarget { + get { return _target ?? _args[0]; } + } + + public Expression/*!*/ TargetExpression { + get { return MetaTarget.Expression; } + } + + public object Target { + get { return MetaTarget.Value; } + } + + public Proc GetBlock() { + return (Proc)_args[GetBlockIndex()].Value; + } + + public object GetSplattedArgument() { + return _args[GetSplattedArgumentIndex()].Value; + } + + public object GetRhsArgument() { + return _args[GetRhsArgumentIndex()].Value; + } + + public Expression GetBlockExpression() { + return _signature.HasBlock ? _args[GetBlockIndex()].Expression : null; + } + + public Expression GetSplattedArgumentExpression() { + return _signature.HasSplattedArgument ? _args[GetSplattedArgumentIndex()].Expression : null; + } + + public Expression GetRhsArgumentExpression() { + return _signature.HasRhsArgument ? _args[GetRhsArgumentIndex()].Expression : null; + } + + public Expression[]/*!*/ GetSimpleArgumentExpressions() { + var result = new Expression[SimpleArgumentCount]; + for (int i = 0, j = GetSimpleArgumentsIndex(0); i < result.Length; j++, i++) { + result[i] = _args[j].Expression; + } + return result; + } + + internal Expression[]/*!*/ GetCallSiteArguments(Expression/*!*/ targetExpression) { + // context, target, arguments: + var result = new Expression[CallSiteArgumentCount]; + result[0] = MetaContext.Expression; + result[1] = targetExpression; + + int i = 2, j = FirstArgumentIndex; + for (; j < _args.Length; i++, j++) { + result[i] = _args[j].Expression; + } + + Debug.Assert(i == result.Length && j == _args.Length); + + return result; + } + + private int GetSimpleArgumentsIndex(int i) { + return FirstArgumentIndex + (_signature.HasBlock ? 1 : 0) + i; + } + + internal object GetSimpleArgument(int i) { + return _args[GetSimpleArgumentsIndex(i)].Value; + } + + internal Expression/*!*/ GetSimpleArgumentExpression(int i) { + return _args[GetSimpleArgumentsIndex(i)].Expression; + } + + internal int GetBlockIndex() { + Debug.Assert(_signature.HasBlock); + return FirstArgumentIndex; + } + + internal int GetSplattedArgumentIndex() { + Debug.Assert(_signature.HasSplattedArgument); + return _args.Length - (_signature.HasRhsArgument ? 2 : 1); + } + + internal int GetRhsArgumentIndex() { + Debug.Assert(_signature.HasRhsArgument); + return _args.Length - 1; + } + + internal CallArguments(MetaObject/*!*/ context, MetaObject/*!*/ target, MetaObject/*!*/[]/*!*/ args, RubyCallSignature signature) { + Assert.NotNull(target, context); + Assert.NotNullItems(args); + + Debug.Assert(signature.HasScope == context.Value is RubyScope); + Debug.Assert(!signature.HasScope == context.Value is RubyContext); + + _target = target; + _context = context; + _args = args; + _copyArgsOnWrite = true; + _signature = signature; + } + + internal CallArguments(MetaObject/*!*/ context, MetaObject/*!*/[]/*!*/ args, RubyCallSignature signature) { + Assert.NotNull(context); + Assert.NotNullItems(args); + Assert.NotEmpty(args); + + Debug.Assert(signature.HasScope == context.Value is RubyScope); + Debug.Assert(!signature.HasScope == context.Value is RubyContext); + + _target = null; + _context = context; + _args = args; + _copyArgsOnWrite = true; + _signature = signature; + } + + public void InsertSimple(int index, MetaObject/*!*/ arg) { + index = GetSimpleArgumentsIndex(index); + + _args = ArrayUtils.InsertAt(_args, index, arg); + _signature = new RubyCallSignature(_signature.ArgumentCount + 1, _signature.Flags); + } + + public void SetSimpleArgument(int index, MetaObject/*!*/ arg) { + SetArgument(GetSimpleArgumentsIndex(index), arg); + } + + private void SetArgument(int index, MetaObject/*!*/ arg) { + if (_copyArgsOnWrite) { + _args = ArrayUtils.Copy(_args); + _copyArgsOnWrite = false; + } + + _args[index] = arg; + } + + public void SetTarget(Expression/*!*/ expression, object value) { + Assert.NotNull(expression); + + var metaTarget = new MetaObject(expression, Restrictions.Empty, value); + + if (_target == null) { + if (_copyArgsOnWrite) { + _args = ArrayUtils.RemoveFirst(_args); + _copyArgsOnWrite = false; + _target = metaTarget; + } else { + _args[0] = metaTarget; + } + } else { + _target = metaTarget; + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/CallSiteTracer.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/CallSiteTracer.cs new file mode 100644 index 0000000000..75f3556d8f --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/CallSiteTracer.cs @@ -0,0 +1,156 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Dynamic.Binders; +using System.Reflection; +using System.Dynamic; +using System.Diagnostics; + +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +using IronRuby.Compiler; +using IronRuby.Compiler.Ast; +using IronRuby.Compiler.Generation; +using IronRuby.Runtime.Calls; + +using MSA = System.Linq.Expressions; +using Ast = System.Linq.Expressions.Expression; +using Microsoft.Scripting; + +namespace IronRuby.Runtime.Calls { + public sealed class CallSiteTracer { + /// + /// Registers a call site tracer associated with the current thread. + /// Traces is called for each rule created for sites with arguments: + /// - context meta-object (RubyScope or RubyContext) + /// - argument meta-objects + /// - resulting meta-object + /// - sourceId passed to Transform method + /// - site offset within the source code + /// + public static void Register(Action/*!*/ tracer) { + ContractUtils.RequiresNotNull(tracer, "tracer"); + TracingRubyCallAction.Tracer = tracer; + } + + public static MSA.Expression/*!*/ Transform(SourceUnitTree/*!*/ ast, SourceUnit/*!*/ sourceUnit, + RubyCompilerOptions/*!*/ options, int sourceId) { + + var context = (RubyContext)sourceUnit.LanguageContext; + var siteNodes = new Dictionary(); + var generator = new TraceAstGenerator(siteNodes, context, options, sourceUnit, ast.Encoding); + var lambda = ast.Transform(generator); + + return (MSA.Expression)new CallSiteTraceInjector(siteNodes, sourceId).Visit(lambda); + } + + public sealed class TracingRubyCallAction : RubyCallAction, IExpressionSerializable { + [ThreadStatic] + private static int _Id; + + [ThreadStatic] + private static int _Location; + + [ThreadStatic] + internal static Action Tracer; + + [Emitted] + public static T EnterCallSite(T result, int id, int location) { + _Id = id; + _Location = location; + return result; + } + + internal TracingRubyCallAction(string/*!*/ methodName, RubyCallSignature signature) + : base(methodName, signature) { + } + + public override string/*!*/ ToString() { + return base.ToString() + "!"; + } + + public override MetaObject/*!*/ Bind(MetaObject/*!*/ context, MetaObject/*!*/[]/*!*/ args) { + var result = base.Bind(context, args); + var tracer = Tracer; + if (tracer != null) { + tracer(context, args, result, _Id, _Location); + } + return result; + } + + MSA.Expression/*!*/ IExpressionSerializable.CreateExpression() { + throw new NotSupportedException(); + } + } + + private sealed class TraceAstGenerator : AstGenerator { + private readonly Dictionary/*!*/ _sites; + + public TraceAstGenerator(Dictionary/*!*/ sites, + RubyContext/*!*/ context, RubyCompilerOptions/*!*/ options, SourceUnit/*!*/ sourceUnit, Encoding/*!*/ encoding) + : base((RubyBinder)context.Binder, options, sourceUnit, encoding, false, + context.DomainManager.Configuration.DebugMode, false, false, false) { + _sites = sites; + } + + internal override void TraceCallSite(Expression/*!*/ expression, MSA.DynamicExpression/*!*/ callSite) { + _sites.Add(callSite, expression.Location); + } + } + + private sealed class CallSiteTraceInjector : MSA.ExpressionVisitor { + private readonly Dictionary/*!*/ _sites; + private readonly int _sourceId; + + public CallSiteTraceInjector(Dictionary/*!*/ sites, int sourceId) { + _sites = sites; + _sourceId = sourceId; + } + + protected override MSA.Expression/*!*/ VisitDynamic(MSA.DynamicExpression/*!*/ node) { + var callAction = node.Binder as RubyCallAction; + if (callAction != null) { + var args = new MSA.Expression[node.Arguments.Count]; + + for (int i = 0; i < args.Length; i++) { + args[i] = node.Arguments[i]; + } + + Debug.Assert(args.Length > 0); + int last = args.Length - 1; + + args[last] = typeof(TracingRubyCallAction).GetMethod("EnterCallSite").MakeGenericMethod(args[last].Type).OpCall( + args[last], + Ast.Constant(_sourceId), + Ast.Constant(_sites[node].Start.Index) + ); + + return Ast.Dynamic( + new TracingRubyCallAction(callAction.MethodName, callAction.Signature), + node.Type, + args + ); + } else { + return base.VisitDynamic(node); + } + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/MetaObjectBuilder.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/MetaObjectBuilder.cs new file mode 100644 index 0000000000..331fedee3d --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/MetaObjectBuilder.cs @@ -0,0 +1,245 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Collections.Generic; +using System.Dynamic.Binders; + +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Utils; + +using Ast = System.Linq.Expressions.Expression; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using IronRuby.Builtins; +using IronRuby.Compiler; + +namespace IronRuby.Runtime.Calls { + public sealed class MetaObjectBuilder { + private Expression _condition; + private Expression _restriction; + private Expression _result; + private List _temps; + private bool _error; + private bool _treatRestrictionsAsConditions; + + internal MetaObjectBuilder() { + } + + public bool Error { + get { return _error; } + } + + public Expression Result { + get { return _result; } + set { _result = value; } + } + + public ParameterExpression BfcVariable { get; set; } + + public bool TreatRestrictionsAsConditions { + get { return _treatRestrictionsAsConditions; } + set { _treatRestrictionsAsConditions = value; } + } + + internal MetaObject/*!*/ CreateMetaObject(MetaObjectBinder/*!*/ action, MetaObject/*!*/ context, MetaObject/*!*/[]/*!*/ args) { + return CreateMetaObject(action, ArrayUtils.Insert(context, args)); + } + + internal MetaObject/*!*/ CreateMetaObject(MetaObjectBinder/*!*/ action, MetaObject/*!*/[]/*!*/ siteArgs) { + var expr = _error ? Ast.Throw(_result) : _result; + + Restrictions restrictions; + if (_condition != null) { + var deferral = action.Defer(siteArgs); + expr = Ast.Condition(_condition, AstUtils.Convert(expr, typeof(object)), deferral.Expression); + restrictions = deferral.Restrictions; + } else { + restrictions = Restrictions.Empty; + } + + if (_temps != null) { + expr = Ast.Block(_temps, expr); + } + + if (_restriction != null) { + restrictions = restrictions.Merge(Restrictions.GetExpressionRestriction(_restriction)); + } + + return new MetaObject(expr, restrictions); + } + + public void SetError(Expression/*!*/ expression) { + Assert.NotNull(expression); + Debug.Assert(!_error, "Error already set"); + + _result = expression; + _error = true; + } + + public void SetWrongNumberOfArgumentsError(int actual, int expected) { + SetError(Methods.MakeWrongNumberOfArgumentsError.OpCall(Ast.Constant(actual), Ast.Constant(expected))); + } + + public void AddCondition(Expression/*!*/ condition) { + Assert.NotNull(condition); + _condition = (_condition != null) ? Ast.AndAlso(_condition, condition) : condition; + } + + public void AddRestriction(Expression/*!*/ restriction) { + Assert.NotNull(restriction); + if (_treatRestrictionsAsConditions) { + AddCondition(restriction); + } else { + _restriction = (_restriction != null) ? Ast.AndAlso(_restriction, restriction) : restriction; + } + } + + public static Expression/*!*/ GetObjectTypeTestExpression(object value, Expression/*!*/ expression) { + if (value == null) { + return Ast.Equal(expression, Ast.Constant(null)); + } else { + return RuleBuilder.MakeTypeTestExpression(value.GetType(), expression); + } + } + + public void AddTypeRestriction(Type/*!*/ type, Expression/*!*/ expression) { + AddRestriction(RuleBuilder.MakeTypeTestExpression(type, expression)); + } + + public void AddObjectTypeRestriction(object value, Expression/*!*/ expression) { + if (value == null) { + AddRestriction(Ast.Equal(expression, Ast.Constant(null))); + } else { + AddTypeRestriction(value.GetType(), expression); + } + } + + public void AddObjectTypeCondition(object value, Expression/*!*/ expression) { + AddCondition(GetObjectTypeTestExpression(value, expression)); + } + + public void AddTargetTypeTest(CallArguments/*!*/ args) { + AddTargetTypeTest(args.Target, args.TargetExpression, args.RubyContext, args.ContextExpression); + } + + // TODO: do not test runtime for runtime bound sites + // TODO: ResolveMethod invalidates modules that were not initialized yet -> snapshot version after method resolution + // TODO: thread safety: synchronize version snapshot and method resolution + public void AddTargetTypeTest(object target, Expression/*!*/ targetParameter, RubyContext/*!*/ context, Expression/*!*/ contextExpression) { + + // singleton nil: + if (target == null) { + AddRestriction(Ast.Equal(targetParameter, Ast.Constant(null))); + context.NilClass.AddFullVersionTest(this, contextExpression); + return; + } + + // singletons true, false: + if (target is bool) { + AddRestriction(Ast.AndAlso( + Ast.TypeIs(targetParameter, typeof(bool)), + Ast.Equal(Ast.Convert(targetParameter, typeof(bool)), Ast.Constant(target)) + )); + + if ((bool)target) { + context.TrueClass.AddFullVersionTest(this, contextExpression); + } else { + context.FalseClass.AddFullVersionTest(this, contextExpression); + } + return; + + } + + RubyClass immediateClass = context.GetImmediateClassOf(target); + + // user defined instance singletons, modules, classes: + if (immediateClass.IsSingletonClass) { + AddRestriction( + Ast.Equal( + Ast.Convert(targetParameter, typeof(object)), + Ast.Convert(Ast.Constant(target), typeof(object)) + ) + ); + + // we need to check for a runtime (e.g. "foo" .NET string instance could be shared accross runtimes): + immediateClass.AddFullVersionTest(this, contextExpression); + return; + } + + Type type = target.GetType(); + AddTypeRestriction(type, targetParameter); + + if (typeof(IRubyObject).IsAssignableFrom(type)) { + // Ruby objects (get the method directly to prevent interface dispatch): + MethodInfo classGetter = type.GetMethod("get_" + RubyObject.ClassPropertyName, BindingFlags.Public | BindingFlags.Instance); + if (classGetter != null && classGetter.ReturnType == typeof(RubyClass)) { + AddCondition( + // (#{type})target.Class.Version == #{immediateClass.Version} + Ast.Equal( + Ast.Call(Ast.Call(Ast.Convert(targetParameter, type), classGetter), RubyModule.VersionProperty.GetGetMethod()), + Ast.Constant(immediateClass.Version) + ) + ); + return; + } + + // TODO: explicit iface-implementation + throw new NotSupportedException("Type implementing IRubyObject should have RubyClass getter"); + } else { + // CLR objects: + immediateClass.AddFullVersionTest(this, contextExpression); + } + } + + internal bool AddSplattedArgumentTest(object value, Expression/*!*/ expression, out int listLength, out ParameterExpression/*!*/ listVariable) { + if (value == null) { + AddRestriction(Ast.Equal(expression, Ast.Constant(null))); + } else { + // test exact type: + AddTypeRestriction(value.GetType(), expression); + + List list = value as List; + if (list != null) { + Type type = typeof(List); + listLength = list.Count; + listVariable = GetTemporary(type, "#list"); + AddCondition(Ast.Equal( + Ast.Property(Ast.Assign(listVariable, Ast.Convert(expression, type)), type.GetProperty("Count")), + Ast.Constant(list.Count)) + ); + return true; + } + } + + listLength = -1; + listVariable = null; + return false; + } + + public ParameterExpression/*!*/ GetTemporary(Type/*!*/ type, string/*!*/ name) { + if (_temps == null) { + _temps = new List(); + } + + var variable = Ast.Variable(type, name); + _temps.Add(variable); + return variable; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/ProtocolConversionAction.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/ProtocolConversionAction.cs new file mode 100644 index 0000000000..4918cc599b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/ProtocolConversionAction.cs @@ -0,0 +1,459 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Dynamic.Binders; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Compiler; +using Ast = System.Linq.Expressions.Expression; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using Microsoft.Scripting.Math; +using IronRuby.Compiler.Generation; +using System.Collections; +using System.Reflection; + +namespace IronRuby.Runtime.Calls { + + public abstract class ProtocolConversionAction : MetaObjectBinder, IExpressionSerializable { + internal static readonly RubyCallSignature Signature = RubyCallSignature.WithScope(0); + + protected ProtocolConversionAction() { + } + + public static ProtocolConversionAction TryGetConversionAction(Type/*!*/ parameterType) { + if (parameterType == typeof(MutableString)) { + return ConvertToStrAction.Instance; + } + + if (parameterType == typeof(int)) { + return ConvertToFixnumAction.Instance; + } + + if (parameterType == typeof(string)) { + return ConvertToSymbolAction.Instance; + } + + if (parameterType == typeof(RubyRegex)) { + return ConvertToRegexAction.Instance; + } + + if (parameterType == typeof(bool)) { + return ConvertToBooleanAction.Instance; + } + + if (parameterType == typeof(IList)) { + return ConvertToArrayAction.Instance; + } + + return null; + } + + public override object/*!*/ CacheIdentity { + get { return this; } + } + + public override MetaObject/*!*/ Bind(MetaObject/*!*/ context, MetaObject/*!*/[]/*!*/ args) { + var mo = new MetaObjectBuilder(); + SetRule(mo, new CallArguments(context, args, Signature)); + return mo.CreateMetaObject(this, context, args); + } + + Expression/*!*/ IExpressionSerializable.CreateExpression() { + return Ast.Call(GetType().GetMethod("Make")); + } + + protected abstract string/*!*/ ToMethodName { get; } + protected abstract MethodInfo ConversionResultValidator { get; } + protected abstract string/*!*/ TargetTypeName { get; } + + protected abstract bool TryImplicitConversion(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args); + + internal void SetRule(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + Assert.NotNull(metaBuilder, args); + Debug.Assert(args.SimpleArgumentCount == 0 && !args.Signature.HasBlock && !args.Signature.HasSplattedArgument && !args.Signature.HasRhsArgument); + Debug.Assert(args.Signature.HasScope); + + var ec = args.RubyContext; + + // implicit conversions should only depend on a static type: + if (TryImplicitConversion(metaBuilder, args)) { + if (args.Target == null) { + metaBuilder.AddRestriction(Ast.Equal(args.TargetExpression, Ast.Constant(null, args.TargetExpression.Type))); + } else { + metaBuilder.AddTypeRestriction(args.Target.GetType(), args.TargetExpression); + } + return; + } + + // check for type version: + metaBuilder.AddTargetTypeTest(args); + + string toMethodName = ToMethodName; + Expression targetClassNameConstant = Ast.Constant(ec.GetClassOf(args.Target).Name); + + // Kernel#respond_to? method is not overridden => we can optimize + RubyMemberInfo respondToMethod = ec.ResolveMethod(args.Target, Symbols.RespondTo, true).InvalidateSitesOnOverride(); + if (respondToMethod == null || + // the method is defined in library, hasn't been replaced by user defined method (TODO: maybe we should make this check better) + (respondToMethod.DeclaringModule == ec.KernelModule && respondToMethod is RubyMethodGroupInfo)) { + + RubyMemberInfo conversionMethod = ec.ResolveMethod(args.Target, toMethodName, false).InvalidateSitesOnOverride(); + if (conversionMethod == null) { + // error: + SetError(metaBuilder, targetClassNameConstant, args); + return; + } else { + // invoke target.to_xxx() and validate it; returns an instance of TTargetType: + conversionMethod.BuildCall(metaBuilder, args, toMethodName); + + if (!metaBuilder.Error && ConversionResultValidator != null) { + metaBuilder.Result = ConversionResultValidator.OpCall(targetClassNameConstant, AstFactory.Box(metaBuilder.Result)); + } + return; + } + } else if (!RubyModule.IsMethodVisible(respondToMethod, false)) { + // respond_to? is private: + SetError(metaBuilder, targetClassNameConstant, args); + return; + } + + // slow path: invoke respond_to?, to_xxx and result validation: + + var conversionCallSite = Ast.Dynamic( + RubyCallAction.Make(toMethodName, RubyCallSignature.WithScope(0)), + typeof(object), + args.ScopeExpression, args.TargetExpression + ); + + Expression opCall; + metaBuilder.Result = Ast.Condition( + // If + + // respond_to?() + Methods.IsTrue.OpCall( + Ast.Dynamic( + RubyCallAction.Make(Symbols.RespondTo, RubyCallSignature.WithScope(1)), + typeof(object), + args.ScopeExpression, args.TargetExpression, Ast.Constant(SymbolTable.StringToId(toMethodName)) + ) + ), + + // Then + + // to_xxx(): + opCall = (ConversionResultValidator == null) ? conversionCallSite : + ConversionResultValidator.OpCall(targetClassNameConstant, conversionCallSite), + + // Else + + AstUtils.Convert( + (ConversionResultValidator == null) ? args.TargetExpression : + Ast.Convert( + Ast.Throw(Methods.CreateTypeConversionError.OpCall(targetClassNameConstant, Ast.Constant(TargetTypeName))), + typeof(object) + ), + opCall.Type + ) + ); + } + + private void SetError(MetaObjectBuilder/*!*/ metaBuilder, Expression/*!*/ targetClassNameConstant, CallArguments/*!*/ args) { + if (ConversionResultValidator != null) { + metaBuilder.SetError(Methods.CreateTypeConversionError.OpCall(targetClassNameConstant, Ast.Constant(TargetTypeName))); + } else { + metaBuilder.Result = args.TargetExpression; + } + } + } + + public abstract class ConvertToReferenceTypeAction : ProtocolConversionAction where TTargetType : class { + protected override bool TryImplicitConversion(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + if (args.Target == null) { + metaBuilder.Result = Ast.Constant(null); + return true; + } + + var convertedTarget = args.Target as TTargetType; + if (convertedTarget != null) { + metaBuilder.Result = AstUtils.Convert(args.TargetExpression, typeof(TTargetType)); + return true; + } + return false; + } + } + + public sealed class ConvertToProcAction : ConvertToReferenceTypeAction, IEquatable { + public static readonly ConvertToProcAction Instance = new ConvertToProcAction(); + + protected override string/*!*/ ToMethodName { get { return Symbols.ToProc; } } + protected override string/*!*/ TargetTypeName { get { return "Proc"; } } + protected override MethodInfo ConversionResultValidator { get { return Methods.ToProcValidator; } } + + private ConvertToProcAction() { + } + + [Emitted] + public static ConvertToProcAction/*!*/ Make() { + return Instance; + } + + public bool Equals(ConvertToProcAction other) { + return other != null; + } + } + + public sealed class ConvertToStrAction : ConvertToReferenceTypeAction, IEquatable { + public static readonly ConvertToStrAction Instance = new ConvertToStrAction(); + + protected override string/*!*/ ToMethodName { get { return Symbols.ToStr; } } + protected override string/*!*/ TargetTypeName { get { return "String"; } } + protected override MethodInfo ConversionResultValidator { get { return Methods.ToStringValidator; } } + + private ConvertToStrAction() { + } + + [Emitted] + public static ConvertToStrAction/*!*/ Make() { + return Instance; + } + + public bool Equals(ConvertToStrAction other) { + return other != null; + } + } + + // TODO: escaping vs. non-escaping? + // This conversion escapes the regex. + public sealed class ConvertToRegexAction : ConvertToReferenceTypeAction, IEquatable { + public static readonly ConvertToRegexAction Instance = new ConvertToRegexAction(); + + protected override string/*!*/ ToMethodName { get { return Symbols.ToStr; } } + protected override string/*!*/ TargetTypeName { get { return "Regexp"; } } + protected override MethodInfo ConversionResultValidator { get { return Methods.ToRegexValidator; } } + + private ConvertToRegexAction() { + } + + [Emitted] + public static ConvertToRegexAction/*!*/ Make() { + return Instance; + } + + public bool Equals(ConvertToRegexAction other) { + return other != null; + } + } + + public sealed class ConvertToArrayAction : ConvertToReferenceTypeAction, IEquatable { + public static readonly ConvertToArrayAction Instance = new ConvertToArrayAction(); + + protected override string/*!*/ ToMethodName { get { return Symbols.ToAry; } } + protected override string/*!*/ TargetTypeName { get { return "Array"; } } + protected override MethodInfo ConversionResultValidator { get { return Methods.ToArrayValidator; } } + + private ConvertToArrayAction() { + } + + [Emitted] + public static ConvertToArrayAction/*!*/ Make() { + return Instance; + } + + public bool Equals(ConvertToArrayAction other) { + return other != null; + } + } + + public sealed class TryConvertToArrayAction : ConvertToReferenceTypeAction, IEquatable { + public static readonly TryConvertToArrayAction Instance = new TryConvertToArrayAction(); + + protected override string/*!*/ ToMethodName { get { return Symbols.ToAry; } } + protected override string/*!*/ TargetTypeName { get { return "Array"; } } + protected override MethodInfo ConversionResultValidator { get { return null; } } + + private TryConvertToArrayAction() { + } + + [Emitted] + public static TryConvertToArrayAction/*!*/ Make() { + return Instance; + } + + public bool Equals(TryConvertToArrayAction other) { + return other != null; + } + } + + public sealed class ConvertToSAction : ConvertToReferenceTypeAction, IEquatable { + public static readonly ConvertToSAction Instance = new ConvertToSAction(); + + protected override string/*!*/ ToMethodName { get { return Symbols.ToS; } } + protected override string/*!*/ TargetTypeName { get { return "String"; } } + protected override MethodInfo ConversionResultValidator { get { return Methods.ToSValidator; } } + + private ConvertToSAction() { + } + + [Emitted] + public static ConvertToSAction/*!*/ Make() { + return Instance; + } + + public bool Equals(ConvertToSAction other) { + return other != null; + } + } + + public sealed class ConvertToFixnumAction : ProtocolConversionAction, IEquatable { + public static readonly ConvertToFixnumAction Instance = new ConvertToFixnumAction(); + + protected override string/*!*/ ToMethodName { get { return Symbols.ToInt; } } + protected override string/*!*/ TargetTypeName { get { return "Fixnum"; } } + protected override MethodInfo ConversionResultValidator { get { return Methods.ToFixnumValidator; } } + + private ConvertToFixnumAction() { + } + + [Emitted] + public static ConvertToFixnumAction/*!*/ Make() { + return Instance; + } + + public bool Equals(ConvertToFixnumAction other) { + return other != null; + } + + protected override bool TryImplicitConversion(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + object target = args.Target; + + if (args.Target == null) { + metaBuilder.SetError(Methods.CreateTypeConversionError.OpCall(Ast.Constant("nil"), Ast.Constant(TargetTypeName))); + return true; + } + + // TODO: other .NET primitive integer types + if (target is int) { + metaBuilder.Result = AstUtils.Convert(args.TargetExpression, typeof(int)); + return true; + } + + var bignum = target as BigInteger; + if ((object)bignum != null) { + metaBuilder.Result = Methods.ConvertBignumToFixnum.OpCall(AstUtils.Convert(args.TargetExpression, typeof(BigInteger))); + return true; + } + + return false; + } + } + + public sealed class ConvertToBooleanAction : ProtocolConversionAction, IEquatable { + public static readonly ConvertToBooleanAction Instance = new ConvertToBooleanAction(); + + protected override string/*!*/ ToMethodName { get { return Symbols.ToInt; } } + protected override string/*!*/ TargetTypeName { get { return "Fixnum"; } } + protected override MethodInfo ConversionResultValidator { get { return Methods.ToFixnumValidator; } } + + private ConvertToBooleanAction() { + } + + [Emitted] + public static ConvertToBooleanAction/*!*/ Make() { + return Instance; + } + + public bool Equals(ConvertToBooleanAction other) { + return other != null; + } + + protected override bool TryImplicitConversion(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + object target = args.Target; + + if (args.Target == null) { + metaBuilder.Result = Ast.Constant(false); + return true; + } + + if (target is bool) { + metaBuilder.Result = AstUtils.Convert(args.TargetExpression, typeof(bool)); + return true; + } + + return true; + } + } + + + public sealed class ConvertToSymbolAction : ProtocolConversionAction, IEquatable { + public static readonly ConvertToSymbolAction Instance = new ConvertToSymbolAction(); + + protected override string/*!*/ ToMethodName { get { return Symbols.ToStr; } } + protected override string/*!*/ TargetTypeName { get { return "Symbol"; } } + protected override MethodInfo ConversionResultValidator { get { return Methods.ToSymbolValidator; } } + + private ConvertToSymbolAction() { + } + + [Emitted] + public static ConvertToSymbolAction/*!*/ Make() { + return Instance; + } + + public bool Equals(ConvertToSymbolAction other) { + return other != null; + } + + protected override bool TryImplicitConversion(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + object target = args.Target; + var targetExpression = args.TargetExpression; + + if (args.Target == null) { + metaBuilder.SetError(Methods.CreateTypeConversionError.OpCall(Ast.Constant("nil"), Ast.Constant(TargetTypeName))); + return true; + } + + var str = target as MutableString; + if (str != null) { + metaBuilder.Result = Methods.ConvertMutableStringToSymbol.OpCall(AstUtils.Convert(targetExpression, typeof(MutableString))); + return true; + } + + var sym = target as string; + if (sym != null) { + metaBuilder.Result = AstUtils.Convert(targetExpression, typeof(string)); + return true; + } + + if (target is SymbolId) { + metaBuilder.Result = Methods.ConvertSymbolIdToSymbol.OpCall(AstUtils.Convert(targetExpression, typeof(SymbolId))); + return true; + } + + if (target is int) { + metaBuilder.Result = Methods.ConvertFixnumToSymbol.OpCall(args.ContextExpression, AstUtils.Convert(targetExpression, typeof(int))); + return true; + } + + return false; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyAccessorInfo.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyAccessorInfo.cs new file mode 100644 index 0000000000..ebd4110ec0 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyAccessorInfo.cs @@ -0,0 +1,82 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using System.Linq.Expressions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace IronRuby.Runtime.Calls { + using Ast = System.Linq.Expressions.Expression; + using AstFactory = IronRuby.Compiler.Ast.AstFactory; + using IronRuby.Compiler; + using System.Diagnostics; + + public abstract class RubyAttributeAccessorInfo : RubyMemberInfo { + private readonly string/*!*/ _instanceVariableName; + + protected string/*!*/ InstanceVariableName { get { return _instanceVariableName; } } + + protected RubyAttributeAccessorInfo(RubyMemberFlags flags, RubyModule/*!*/ declaringModule, string/*!*/ variableName) + : base(flags, declaringModule) { + Assert.NotEmpty(variableName); + Debug.Assert(variableName.StartsWith("@")); + _instanceVariableName = variableName; + } + } + + public sealed class RubyAttributeReaderInfo : RubyAttributeAccessorInfo { + public RubyAttributeReaderInfo(RubyMemberFlags flags, RubyModule/*!*/ declaringModule, string/*!*/ variableName) + : base(flags, declaringModule, variableName) { + } + + internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + metaBuilder.Result = Methods.GetInstanceVariable.OpCall( + args.ScopeExpression, + AstFactory.Box(args.TargetExpression), + AstUtils.Constant(InstanceVariableName) + ); + } + + protected internal override RubyMemberInfo/*!*/ Copy(RubyMemberFlags flags, RubyModule/*!*/ module) { + return new RubyAttributeReaderInfo(flags, module, InstanceVariableName); + } + } + + public sealed class RubyAttributeWriterInfo : RubyAttributeAccessorInfo { + public RubyAttributeWriterInfo(RubyMemberFlags flags, RubyModule/*!*/ declaringModule, string/*!*/ name) + : base(flags, declaringModule, name) { + } + + internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + + var actualArgs = RubyMethodGroupInfo.MakeActualArgs(metaBuilder, args, true, false, false, false); + + metaBuilder.Result = Methods.SetInstanceVariable.OpCall( + AstFactory.Box(actualArgs[0]), + AstFactory.Box(actualArgs[1]), + args.ScopeExpression, + AstUtils.Constant(InstanceVariableName) + ); + } + + protected internal override RubyMemberInfo/*!*/ Copy(RubyMemberFlags flags, RubyModule/*!*/ module) { + return new RubyAttributeWriterInfo(flags, module, InstanceVariableName); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyBinder.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyBinder.cs new file mode 100644 index 0000000000..0ad80e1447 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyBinder.cs @@ -0,0 +1,409 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic; +using System.Collections.Generic; +using System.Dynamic.Binders; + +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +using Ast = System.Linq.Expressions.Expression; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using IronRuby.Compiler; +using IronRuby.Builtins; + +namespace IronRuby.Runtime.Calls { + public sealed class RubyBinder : DefaultBinder { + internal RubyBinder(ScriptDomainManager/*!*/ manager) + : base(manager) { + } + + protected override int PrepareParametersBinding(ParameterInfo/*!*/[]/*!*/ parameterInfos, List/*!*/ arguments, + List/*!*/ parameters, ref int index) { + + var i = 0; + + if (i < parameterInfos.Length) { + var parameterInfo = parameterInfos[i]; + + if (parameterInfo.ParameterType == typeof(RubyScope)) { + arguments.Add(new RubyScopeArgBuilder(parameterInfo)); + i++; + } else if (parameterInfo.ParameterType == typeof(RubyContext)) { + arguments.Add(new RubyContextArgBuilder(parameterInfo)); + i++; + } + } + + // If the method overload doesn't have a BlockParam parameter, we inject MissingBlockParam parameter and arg builder. + // The parameter is treated as a regular explicit mandatory parameter. + // + // The argument builder provides no value for the actual argument expression, which makes the default binder to skip it + // when emitting a tree for the actual method call (this is necessary since the method doesn't in fact have the parameter). + // + // By injecting the missing block parameter we achieve that all overloads have either BlockParam, [NotNull]BlockParam or + // MissingBlockParam parameter. MissingBlockParam and BlockParam are convertible to each other. Default binder prefers + // those overloads where no conversion needs to happen, which ensures the desired semantics: + // + // conversions with desired priority (the less number the higher priority) + // Parameters: call w/o block call with non-null block call with null block + // (implicit, MBP, ... ) MBP -> MBP (1) BP -> MBP (3) BP -> MBP (2) + // (implicit, BP, ... ) MBP -> BP (2) BP -> BP (2) BP -> BP (1) + // (implicit, BP!, ... ) N/A BP -> BP! (1) N/A + // + if (i >= parameterInfos.Length || parameterInfos[i].ParameterType != typeof(BlockParam)) { + arguments.Add(new MissingBlockArgBuilder(index++)); + parameters.Add(new ParameterWrapper(this, typeof(MissingBlockParam), SymbolId.Empty, false)); + } + + return i; + } + + internal static void GetParameterCount(ParameterInfo/*!*/[]/*!*/ parameterInfos, out int mandatory, out int optional, out bool acceptsBlock) { + acceptsBlock = false; + mandatory = 0; + optional = 0; + foreach (ParameterInfo parameterInfo in parameterInfos) { + if (parameterInfo.ParameterType == typeof(RubyScope)) { + continue; + } else if (parameterInfo.ParameterType == typeof(RubyContext)) { + continue; + } else if (parameterInfo.ParameterType == typeof(BlockParam)) { + acceptsBlock = true; + } else if (CompilerHelpers.IsParamArray(parameterInfo)) { + // TODO: indicate splat args separately? + optional++; + } else if (CompilerHelpers.IsOutParameter(parameterInfo)) { + // Python allows passing of optional "clr.Reference" to capture out parameters + // Ruby should allow similar + optional++; + } else if (CompilerHelpers.IsMandatoryParameter(parameterInfo)) { + mandatory++; + } else { + optional++; + } + } + } + + internal sealed class RubyContextArgBuilder : ArgBuilder { + public RubyContextArgBuilder(ParameterInfo/*!*/ info) + : base(info) { + } + + public override int Priority { + get { return -1; } + } + + protected override Expression ToExpression(ParameterBinder/*!*/ parameterBinder, IList/*!*/ parameters, bool[]/*!*/ hasBeenUsed) { + return ((RubyParameterBinder)parameterBinder).ContextExpression; + } + } + + internal sealed class RubyScopeArgBuilder : ArgBuilder { + public RubyScopeArgBuilder(ParameterInfo/*!*/ info) + : base(info) { + } + + public override int Priority { + get { return -1; } + } + + protected override Expression ToExpression(ParameterBinder/*!*/ parameterBinder, IList/*!*/ parameters, bool[]/*!*/ hasBeenUsed) { + return ((RubyParameterBinder)parameterBinder).ScopeExpression; + } + } + + internal sealed class MissingBlockArgBuilder : SimpleArgBuilder { + public MissingBlockArgBuilder(int index) + : base(typeof(MissingBlockParam), index, false, false) { + } + + public override int Priority { + get { return -1; } + } + + protected override SimpleArgBuilder/*!*/ Copy(int newIndex) { + return new MissingBlockArgBuilder(newIndex); + } + + protected override Expression ToExpression(ParameterBinder/*!*/ parameterBinder, IList/*!*/ parameters, bool[]/*!*/ hasBeenUsed) { + Debug.Assert(Index < parameters.Count); + Debug.Assert(Index < hasBeenUsed.Length); + Debug.Assert(parameters[Index] != null); + hasBeenUsed[Index] = true; + return null; + } + } + + + +#if TODO + protected override IList/*!*/ GetExtensionTypes(Type/*!*/ t) { + Type extentionType = _rubyContext.RubyContext.GetClass(t).ExtensionType; + + if (extentionType != null) { + List result = new List(); + result.Add(extentionType); + result.AddRange(base.GetExtensionTypes(t)); + return result; + } + + return base.GetExtensionTypes(t); + } +#endif + + #region Conversions + + public override object Convert(object obj, Type/*!*/ toType) { + Assert.NotNull(toType); + return Converter.Convert(obj, toType); + } + + public override Expression ConvertExpression(Expression/*!*/ expr, Type/*!*/ toType, ConversionResultKind kind, Expression context) { + Type fromType = expr.Type; + + if (toType == typeof(object)) { + if (fromType.IsValueType) { + return Ast.Convert(expr, toType); + } else { + return expr; + } + } + + if (toType.IsAssignableFrom(fromType)) { + return expr; + } + + // We used to have a special case for int -> double... + if (fromType != typeof(object) && fromType.IsValueType) { + expr = Ast.Convert(expr, typeof(object)); + } + + MethodInfo fastConvertMethod = GetFastConvertMethod(toType); + if (fastConvertMethod != null) { + return Ast.Call(fastConvertMethod, AstUtils.Convert(expr, typeof(object))); + } + + if (typeof(Delegate).IsAssignableFrom(toType)) { + return Ast.Convert( + Ast.Call( + typeof(Converter).GetMethod("ConvertToDelegate"), + AstUtils.Convert(expr, typeof(object)), + Ast.Constant(toType) + ), + toType + ); + } + + Expression typeIs; + Type visType = CompilerHelpers.GetVisibleType(toType); + if (toType.IsVisible) { + typeIs = Ast.TypeIs(expr, toType); + } else { + typeIs = Ast.Call( + AstUtils.Convert(Ast.Constant(toType), typeof(Type)), + typeof(Type).GetMethod("IsInstanceOfType"), + AstUtils.Convert(expr, typeof(object)) + ); + } + + return Ast.Condition( + typeIs, + Ast.Convert( + expr, + visType), + Ast.Convert( + Ast.Call( + GetGenericConvertMethod(visType), + AstUtils.Convert(expr, typeof(object)), + Ast.Constant(visType.TypeHandle) + ), + visType + ) + ); + } + + public override bool CanConvertFrom(Type/*!*/ fromType, Type/*!*/ toType, bool toNotNullable, NarrowingLevel level) { + return Converter.CanConvertFrom(fromType, toType, level); + } + + public override bool CanConvertFrom(Type/*!*/ fromType, ParameterWrapper/*!*/ toParameter, NarrowingLevel level) { + Type toType = toParameter.Type; + + if (base.CanConvertFrom(fromType, toParameter, level)) { + return true; + } + + // blocks: + if (fromType == typeof(MissingBlockParam)) { + return toType == typeof(BlockParam) && !toParameter.ProhibitNull; + } + + if (fromType == typeof(BlockParam) && toType == typeof(MissingBlockParam)) { + return true; + } + + // protocol conversions: + if (toParameter.ParameterInfo != null && toParameter.ParameterInfo.IsDefined(typeof(DefaultProtocolAttribute), false)) { + // any type is potentially convertible, except for nil if [NotNull] is used: + return fromType != typeof(Null) || !toParameter.ProhibitNull; + } + + return false; + } + + public override Candidate SelectBestConversionFor(Type/*!*/ actualType, ParameterWrapper/*!*/ candidateOne, ParameterWrapper/*!*/ candidateTwo, NarrowingLevel level) { + Type typeOne = candidateOne.Type; + Type typeTwo = candidateTwo.Type; + + if (actualType == typeof(Null)) { + // if nil is passed as a block argument prefer BlockParam over missing block; + if (typeOne == typeof(BlockParam) && typeTwo == typeof(MissingBlockParam)) { + return Candidate.One; + } + + if (typeOne == typeof(MissingBlockParam) && typeTwo == typeof(BlockParam)) { + return Candidate.Two; + } + } else { + if (actualType == typeOne && candidateOne.ProhibitNull) { + return Candidate.One; + } + + if (actualType == typeTwo && candidateTwo.ProhibitNull) { + return Candidate.Two; + } + } + + if (actualType == typeOne) { + return Candidate.One; + } + + if (actualType == typeTwo) { + return Candidate.Two; + } + + + return Candidate.Equivalent; + } + + public override Candidate PreferConvert(Type t1, Type t2) { + return Converter.PreferConvert(t1, t2); + // return t1 == t2; + } + + + + internal static MethodInfo/*!*/ GetGenericConvertMethod(Type/*!*/ toType) { + if (toType.IsValueType) { + if (toType.IsGenericType && toType.GetGenericTypeDefinition() == typeof(Nullable<>)) { + return typeof(Converter).GetMethod("ConvertToNullableType"); + } else { + return typeof(Converter).GetMethod("ConvertToValueType"); + } + } else { + return typeof(Converter).GetMethod("ConvertToReferenceType"); + } + } + + internal static MethodInfo GetFastConvertMethod(Type/*!*/ toType) { + if (toType == typeof(char)) { + return typeof(Converter).GetMethod("ConvertToChar"); + } else if (toType == typeof(int)) { + return typeof(Converter).GetMethod("ConvertToInt32"); + } else if (toType == typeof(string)) { + return typeof(Converter).GetMethod("ConvertToString"); + } else if (toType == typeof(long)) { + return typeof(Converter).GetMethod("ConvertToInt64"); + } else if (toType == typeof(double)) { + return typeof(Converter).GetMethod("ConvertToDouble"); + } else if (toType == typeof(bool)) { + return typeof(Converter).GetMethod("ConvertToBoolean"); + } else if (toType == typeof(BigInteger)) { + return typeof(Converter).GetMethod("ConvertToBigInteger"); + } else if (toType == typeof(Complex64)) { + return typeof(Converter).GetMethod("ConvertToComplex64"); + } else if (toType == typeof(IEnumerable)) { + return typeof(Converter).GetMethod("ConvertToIEnumerable"); + } else if (toType == typeof(float)) { + return typeof(Converter).GetMethod("ConvertToSingle"); + } else if (toType == typeof(byte)) { + return typeof(Converter).GetMethod("ConvertToByte"); + } else if (toType == typeof(sbyte)) { + return typeof(Converter).GetMethod("ConvertToSByte"); + } else if (toType == typeof(short)) { + return typeof(Converter).GetMethod("ConvertToInt16"); + } else if (toType == typeof(uint)) { + return typeof(Converter).GetMethod("ConvertToUInt32"); + } else if (toType == typeof(ulong)) { + return typeof(Converter).GetMethod("ConvertToUInt64"); + } else if (toType == typeof(ushort)) { + return typeof(Converter).GetMethod("ConvertToUInt16"); + } else if (toType == typeof(Type)) { + return typeof(Converter).GetMethod("ConvertToType"); + } else { + return null; + } + } + + #endregion + + #region MetaObjects + + internal static Expression/*!*/[]/*!*/ ToExpressions(MetaObject/*!*/[]/*!*/ args, int start) { + var result = new Expression[args.Length - start]; + for (int i = 0; i < result.Length; i++) { + result[i] = args[start + i].Expression; + } + return result; + } + + internal static object/*!*/[]/*!*/ ToValues(MetaObject/*!*/[]/*!*/ args, int start) { + var result = new object[args.Length - start]; + for (int i = 0; i < result.Length; i++) { + result[i] = args[start + i].Value; + } + return result; + } + + internal static MetaObject TryBindCovertToDelegate(ConvertBinder/*!*/ action, MetaObject/*!*/ target) { + if (typeof(Delegate).IsAssignableFrom(action.Type)) { + return new MetaObject( + Methods.CreateDelegateFromMethod.OpCall( + Ast.Constant(action.Type), + AstUtils.Convert(target.Expression, typeof(RubyMethod)) + ), + target.Restrictions.Merge(Restrictions.GetTypeRestriction(target.Expression, target.Value.GetType())) + ); + } + return null; + } + + #endregion + + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyCallAction.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyCallAction.cs new file mode 100644 index 0000000000..852bde03ec --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyCallAction.cs @@ -0,0 +1,141 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic.Binders; + +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Actions; + +using IronRuby.Builtins; +using IronRuby.Compiler; + +using Ast = System.Linq.Expressions.Expression; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Runtime.Calls { + + public class RubyCallAction : MetaObjectBinder, IEquatable, IExpressionSerializable { + private readonly RubyCallSignature _signature; + private readonly string/*!*/ _methodName; + + public RubyCallSignature Signature { + get { return _signature; } + } + + public string/*!*/ MethodName { + get { return _methodName; } + } + + protected RubyCallAction(string/*!*/ methodName, RubyCallSignature signature) { + Assert.NotNull(methodName); + _methodName = methodName; + _signature = signature; + } + + public static RubyCallAction/*!*/ Make(string/*!*/ methodName, int argumentCount) { + return Make(methodName, RubyCallSignature.Simple(argumentCount)); + } + + [Emitted] + public static RubyCallAction/*!*/ Make(string/*!*/ methodName, RubyCallSignature signature) { + return new RubyCallAction(methodName, signature); + } + + public override object/*!*/ CacheIdentity { + get { return this; } + } + + #region Object Overrides, IEquatable + + public override int GetHashCode() { + return _methodName.GetHashCode() ^ _signature.GetHashCode() ^ GetType().GetHashCode(); + } + + public override bool Equals(object obj) { + return Equals(obj as RubyCallAction); + } + + public override string/*!*/ ToString() { + return _methodName + _signature.ToString(); + } + + public bool Equals(RubyCallAction other) { + return other != null && _methodName == other._methodName && _signature.Equals(other._signature) && other.GetType() == GetType(); + } + + #endregion + + #region IExpressionSerializable Members + + Expression/*!*/ IExpressionSerializable.CreateExpression() { + return Expression.Call( + typeof(RubyCallAction).GetMethod("Make", new Type[] { typeof(string), typeof(RubyCallSignature) }), + Expression.Constant(_methodName), + _signature.CreateExpression() + ); + } + + #endregion + + public override MetaObject/*!*/ Bind(MetaObject/*!*/ context, MetaObject/*!*/[]/*!*/ args) { + var mo = new MetaObjectBuilder(); + Bind(mo, _methodName, new CallArguments(context, args, _signature)); + return mo.CreateMetaObject(this, context, args); + } + + /// The resolved method is Kernel#method_missing. + internal static void Bind(MetaObjectBuilder/*!*/ metaBuilder, string/*!*/ methodName, CallArguments/*!*/ args) { + metaBuilder.AddTargetTypeTest(args); + + RubyMemberInfo method = args.RubyContext.ResolveMethod(args.Target, methodName, true).InvalidateSitesOnOverride(); + if (method != null && RubyModule.IsMethodVisible(method, args.Signature.HasImplicitSelf)) { + method.BuildCall(metaBuilder, args, methodName); + } else { + // insert the method name argument into the args + object symbol = SymbolTable.StringToId(methodName); + args.InsertSimple(0, new MetaObject(Ast.Constant(symbol), Restrictions.Empty, symbol)); + + BindToMethodMissing(metaBuilder, methodName, args, method != null); + } + } + + internal static void BindToMethodMissing(MetaObjectBuilder/*!*/ metaBuilder, string/*!*/ methodName, CallArguments/*!*/ args, bool privateMethod) { + // args already contain method name: + var method = args.RubyContext.ResolveMethod(args.Target, Symbols.MethodMissing, true).InvalidateSitesOnOverride(); + + // TODO: better check for builtin method + if (method == null || + method.DeclaringModule == args.RubyContext.KernelModule && method is RubyMethodGroupInfo) { + + // throw an exception immediately, do not cache the rule: + if (privateMethod) { + throw RubyExceptions.CreatePrivateMethodCalled(args.RubyContext, args.Target, methodName); + } else { + throw RubyExceptions.CreateMethodMissing(args.RubyContext, args.Target, methodName); + } + } + + method.BuildCall(metaBuilder, args, methodName); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyCallSignature.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyCallSignature.cs new file mode 100644 index 0000000000..639e27e62a --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyCallSignature.cs @@ -0,0 +1,165 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Dynamic.Binders; +using System.Collections.Generic; + +using Microsoft.Scripting.Utils; +using Ast = System.Linq.Expressions.Expression; +using System.Diagnostics; +using IronRuby.Compiler.Generation; +using IronRuby.Compiler; + +namespace IronRuby.Runtime.Calls { + public enum RubyCallFlags { + None = 0, + HasScope = 1, + HasSplattedArgument = 2, + + // an additional argument following splat arguments (e.g. target[args, *splat]=rhs) + HasRhsArgument = 4, + HasBlock = 8, + + // Used for private visibility check. By default method call sites have explicit self, so private methods are not visible. + HasImplicitSelf = 16, + } + + /// + /// RubyScope/RubyContext, (self), (argument){ArgumentCount}, (splatted-argument)?, (block)? + /// + public struct RubyCallSignature : IEquatable { + private const int FlagsCount = 5; + private const int MaxArgumentCount = (int)(UInt32.MaxValue >> FlagsCount); + private const RubyCallFlags FlagsMask = (RubyCallFlags)(1 << FlagsCount) - 1; + + private readonly uint _countAndFlags; + + public bool HasImplicitSelf { get { return ((RubyCallFlags)_countAndFlags & RubyCallFlags.HasImplicitSelf) != 0; } } + public bool HasScope { get { return ((RubyCallFlags)_countAndFlags & RubyCallFlags.HasScope) != 0; } } + public bool HasBlock { get { return ((RubyCallFlags)_countAndFlags & RubyCallFlags.HasBlock) != 0; } } + public bool HasSplattedArgument { get { return ((RubyCallFlags)_countAndFlags & RubyCallFlags.HasSplattedArgument) != 0; } } + public bool HasRhsArgument { get { return ((RubyCallFlags)_countAndFlags & RubyCallFlags.HasRhsArgument) != 0; } } + public int ArgumentCount { get { return (int)_countAndFlags >> FlagsCount; } } + internal RubyCallFlags Flags { get { return (RubyCallFlags)_countAndFlags & FlagsMask; } } + + // total call-site arguments w/o RubyContext/RubyScope + public int TotalArgumentCount { + get { + return 1 + // instance (self) + ArgumentCount + + (HasSplattedArgument ? 1 : 0) + + (HasBlock ? 1 : 0) + + (HasRhsArgument ? 1 : 0); + } + } + + public RubyCallSignature(int argumentCount, RubyCallFlags flags) { + Debug.Assert(argumentCount >= 0 && argumentCount < MaxArgumentCount); + Debug.Assert(((int)flags >> FlagsCount) == 0); + + _countAndFlags = ((uint)argumentCount << FlagsCount) | (uint)flags; + } + + public RubyCallSignature(bool hasScope, bool hasImplicitSelf, int argumentCount, bool hasSplattedArgument, bool hasBlock, bool hasRhsArgument) { + Debug.Assert(argumentCount >= 0 && argumentCount < MaxArgumentCount); + + var flags = RubyCallFlags.None; + if (hasImplicitSelf) flags |= RubyCallFlags.HasImplicitSelf; + if (hasScope) flags |= RubyCallFlags.HasScope; + if (hasSplattedArgument) flags |= RubyCallFlags.HasSplattedArgument; + if (hasBlock) flags |= RubyCallFlags.HasBlock; + if (hasRhsArgument) flags |= RubyCallFlags.HasRhsArgument; + + _countAndFlags = ((uint)argumentCount << FlagsCount) | (uint)flags; + } + + [Emitted, Obsolete("Do not use from code"), CLSCompliant(false)] + public RubyCallSignature(uint countAndFlags) { + _countAndFlags = countAndFlags; + } + + internal static bool TryCreate(IList/*!*/ action, out RubyCallSignature callSignature) { + callSignature = Simple(action.Count); + return HasNamedArgument(action); + } + + internal static bool HasNamedArgument(IList/*!*/ arguments) { + for (int i = 0; i < arguments.Count; i++) { + if (arguments[i].ArgumentType == ArgumentKind.Named) { + return true; + } + } + + return false; + } + + public static RubyCallSignature WithImplicitSelf(int argumentCount) { + return new RubyCallSignature(argumentCount, RubyCallFlags.HasImplicitSelf); + } + + public static RubyCallSignature Simple(int argumentCount) { + return new RubyCallSignature(argumentCount, RubyCallFlags.None); + } + + public static RubyCallSignature WithBlock(int argumentCount) { + return new RubyCallSignature(argumentCount, RubyCallFlags.HasBlock); + } + + public static RubyCallSignature WithSplat(int argumentCount) { + return new RubyCallSignature(argumentCount, RubyCallFlags.HasSplattedArgument); + } + + public static RubyCallSignature WithSplatAndBlock(int argumentCount) { + return new RubyCallSignature(argumentCount, RubyCallFlags.HasBlock | RubyCallFlags.HasSplattedArgument); + } + + public static RubyCallSignature WithScope(int argumentCount) { + return new RubyCallSignature(argumentCount, RubyCallFlags.HasScope); + } + + public static RubyCallSignature WithScopeAndBlock(int argumentCount) { + return new RubyCallSignature(argumentCount, RubyCallFlags.HasScope | RubyCallFlags.HasBlock); + } + + public static RubyCallSignature WithScopeAndSplat(int argumentCount) { + return new RubyCallSignature(argumentCount, RubyCallFlags.HasScope | RubyCallFlags.HasSplattedArgument); + } + + public static RubyCallSignature WithScopeAndSplatAndBlock(int argumentCount) { + return new RubyCallSignature(argumentCount, RubyCallFlags.HasScope | RubyCallFlags.HasBlock | RubyCallFlags.HasSplattedArgument); + } + + internal Expression/*!*/ CreateExpression() { + return Ast.New(Methods.RubyCallSignatureCtor, Ast.Constant(_countAndFlags)); + } + + public bool Equals(RubyCallSignature other) { + return _countAndFlags == other._countAndFlags; + } + + public override string/*!*/ ToString() { + return "(" + + (HasImplicitSelf ? "." : "") + + (HasScope ? "S," : "C,") + + ArgumentCount.ToString() + + (HasSplattedArgument ? "*" : "") + + (HasBlock ? "&" : "") + + (HasRhsArgument ? "=" : "") + + ")"; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyCreateInstanceBinder.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyCreateInstanceBinder.cs new file mode 100644 index 0000000000..4f68b6d685 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyCreateInstanceBinder.cs @@ -0,0 +1,70 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Dynamic.Binders; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Runtime.Calls { + using IronRuby.Builtins; + using Ast = System.Linq.Expressions.Expression; + using System; + using IronRuby.Compiler; + + internal sealed class RubyCreateInstanceBinder : CreateInstanceBinder { + private readonly RubyContext/*!*/ _context; + + public RubyCreateInstanceBinder(RubyContext/*!*/ context, ArgumentInfo[]/*!*/ arguments) + : base(arguments) { + _context = context; + } + + public override object CacheIdentity { + get { return this; } + } + + public override MetaObject/*!*/ FallbackCreateInstance(MetaObject/*!*/ target, MetaObject/*!*/[]/*!*/ args, MetaObject errorSuggestion) { + var result = TryBind(_context, this, target, args); + if (result != null) { + return result; + } + + throw new NotImplementedException(); + // TODO: + //return ((DefaultBinder)_context.Binder).Create(.GetMember(Name, self, Ast.Null(typeof(CodeContext)), true); + } + + public static MetaObject TryBind(RubyContext/*!*/ context, CreateInstanceBinder/*!*/ binder, MetaObject/*!*/ target, MetaObject/*!*/[]/*!*/ args) { + Assert.NotNull(context, binder, target, args); + + var metaBuilder = new MetaObjectBuilder(); + + RubyCallAction.Bind(metaBuilder, "new", + new CallArguments( + new MetaObject(Ast.Constant(context), Restrictions.Empty, context), + target, + args, + RubyCallSignature.Simple(args.Length) + ) + ); + + // TODO: we should return null if we fail, we need to throw exception due to version update optimization: + return metaBuilder.CreateMetaObject(binder, MetaObject.EmptyMetaObjects); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyCustomMethodInfo.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyCustomMethodInfo.cs new file mode 100644 index 0000000000..e0ef099390 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyCustomMethodInfo.cs @@ -0,0 +1,44 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; + +namespace IronRuby.Runtime.Calls { + internal sealed class RubyCustomMethodInfo : RubyMemberInfo { + private readonly RuleGenerator/*!*/ _ruleGenerator; + + public RubyCustomMethodInfo(RuleGenerator/*!*/ ruleGenerator, RubyMemberFlags flags, RubyModule/*!*/ declaringModule) + : base(flags, declaringModule) { + + Assert.NotNull(ruleGenerator, declaringModule); + _ruleGenerator = ruleGenerator; + } + + internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + _ruleGenerator(metaBuilder, args, name); + } + + internal override void ApplyBlockFlowHandling(MetaObjectBuilder metaBuilder, CallArguments args) { + // nop + } + + protected internal override RubyMemberInfo/*!*/ Copy(RubyMemberFlags flags, RubyModule/*!*/ module) { + return new RubyCustomMethodInfo(_ruleGenerator, flags, module); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyEventInfo.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyEventInfo.cs new file mode 100644 index 0000000000..1432f3a656 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyEventInfo.cs @@ -0,0 +1,61 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Reflection; +using Microsoft.Scripting.Actions; +using System.Linq.Expressions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +using IronRuby.Builtins; + +namespace IronRuby.Runtime.Calls { + using Ast = System.Linq.Expressions.Expression; + using IronRuby.Compiler; + + internal class RubyEventInfo : RubyMemberInfo { + private readonly EventInfo/*!*/ _eventInfo; + + public RubyEventInfo(EventInfo/*!*/ eventInfo, RubyMemberFlags flags, RubyModule/*!*/ declaringModule) + : base(flags, declaringModule) { + Assert.NotNull(eventInfo, declaringModule); + _eventInfo = eventInfo; + } + + protected internal override RubyMemberInfo/*!*/ Copy(RubyMemberFlags flags, RubyModule/*!*/ module) { + return new RubyEventInfo(_eventInfo, flags, module); + } + + internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + if (args.Signature.HasBlock) { + metaBuilder.Result = Methods.HookupEvent.OpCall( + Ast.Convert(Ast.Constant(_eventInfo), typeof(EventInfo)), + args.TargetExpression, + Ast.Convert(args.GetBlockExpression(), typeof(Proc)) + ); + } else { + // TODO: make error + throw new NotImplementedError("no block given"); + } + } + + private static void ReadAll(IList variables, IList expressions, int start) { + for (int i = 0, j = start; i < variables.Count; i++, j++) { + expressions[j] = variables[i]; + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyFieldInfo.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyFieldInfo.cs new file mode 100644 index 0000000000..a20ac0ee88 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyFieldInfo.cs @@ -0,0 +1,83 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using Microsoft.Scripting.Actions; +using System.Linq.Expressions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Compiler; + +namespace IronRuby.Runtime.Calls { + using Ast = System.Linq.Expressions.Expression; + + internal class RubyFieldInfo : RubyMemberInfo { + private readonly FieldInfo/*!*/ _fieldInfo; + private readonly bool _isSetter; + + public RubyFieldInfo(FieldInfo/*!*/ fieldInfo, RubyMemberFlags flags, RubyModule/*!*/ declaringModule, bool isSetter) + : base(flags, declaringModule) { + Assert.NotNull(fieldInfo, declaringModule); + _fieldInfo = fieldInfo; + _isSetter = isSetter; + } + + protected internal override RubyMemberInfo/*!*/ Copy(RubyMemberFlags flags, RubyModule/*!*/ module) { + return new RubyFieldInfo(_fieldInfo, flags, module, _isSetter); + } + + internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + Expression expr = null; + Expression instance = _fieldInfo.IsStatic ? null : Ast.Convert(args.TargetExpression, _fieldInfo.DeclaringType); + + if (_isSetter) { + // parameters should be: instance/type, value + if (args.SimpleArgumentCount == 0 && args.Signature.HasRhsArgument) { + expr = Ast.Assign( + Ast.Field(instance, _fieldInfo), + // TODO: remove + args.RubyContext.Binder.ConvertExpression( + args.GetRhsArgumentExpression(), + _fieldInfo.FieldType, + ConversionResultKind.ExplicitCast, + args.ScopeExpression + ) + ); + } + } else { + // parameter should be: instance/type + if (args.SimpleArgumentCount == 0) { + if (_fieldInfo.IsLiteral) { + // TODO: seems like Compiler should correctly handle the literal field case + // (if you emit a read to a literal field, you get a NotSupportedExpception from + // FieldHandle when we try to emit) + expr = Ast.Constant(_fieldInfo.GetValue(null)); + } else { + expr = Ast.Field(instance, _fieldInfo); + } + } + } + + if (expr != null) { + metaBuilder.Result = expr; + } else { + metaBuilder.SetError( + Methods.MakeInvalidArgumentTypesError.OpCall(Ast.Constant(_isSetter ? name + "=" : name)) + ); + } + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyGetMemberBinder.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyGetMemberBinder.cs new file mode 100644 index 0000000000..19776ca29f --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyGetMemberBinder.cs @@ -0,0 +1,96 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Dynamic.Binders; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Runtime.Calls { + using IronRuby.Builtins; + using Ast = System.Linq.Expressions.Expression; + + internal sealed class RubyGetMemberBinder : GetMemberBinder { + private readonly RubyContext/*!*/ _context; + + public RubyGetMemberBinder(RubyContext/*!*/ context, string/*!*/ name) + : base(name, false) { + _context = context; + } + + public override object CacheIdentity { + get { return this; } + } + + public override MetaObject/*!*/ FallbackGetMember(MetaObject/*!*/ self, MetaObject/*!*/ onBindingError) { + var result = TryBind(_context, this, self); + if (result != null) { + return result; + } + + // TODO: remove CodeContext + return ((DefaultBinder)_context.Binder).GetMember(Name, self, Ast.Constant(null, typeof(CodeContext)), true); + } + + public static MetaObject TryBind(RubyContext/*!*/ context, GetMemberBinder/*!*/ binder, MetaObject/*!*/ target) { + Assert.NotNull(context, target); + var metaBuilder = new MetaObjectBuilder(); + var contextExpression = Ast.Constant(context); + + metaBuilder.AddTargetTypeTest(target.Value, target.Expression, context, contextExpression); + + RubyMemberInfo method = context.ResolveMethod(target.Value, binder.Name, true).InvalidateSitesOnOverride(); + if (method != null && RubyModule.IsMethodVisible(method, false)) { + // we need to create a bound member: + metaBuilder.Result = Ast.Constant(new RubyMethod(target.Value, method, binder.Name)); + } else { + // TODO: + // We need to throw an exception if we don't find method_missing so that our version update optimization works: + // This limits interop with other languages. + // + // class B CLR type with method 'foo' + // class C < B Ruby class + // x = C.new + // + // 1. x.GET("foo") from Ruby + // No method found or CLR method found -> fallback to Python + // Python might see its method foo or might just fallback to .NET, + // in any case it will add rule [1] with restriction on type of C w/o Ruby version check. + // 2. B.define_method("foo") + // This doesn't update C due to the optimization (there is no overridden method foo in C). + // 3. x.GET("foo") from Ruby + // This will not invoke the binder since the rule [1] is still valid. + // + object symbol = SymbolTable.StringToId(binder.Name); + RubyCallAction.BindToMethodMissing(metaBuilder, binder.Name, + new CallArguments( + new MetaObject(contextExpression, Restrictions.Empty, context), + new[] { + target, + new MetaObject(Ast.Constant(symbol), Restrictions.Empty, symbol) + }, + RubyCallSignature.Simple(1) + ), + method != null + ); + } + + // TODO: we should return null if we fail, we need to throw exception for now: + return metaBuilder.CreateMetaObject(binder, MetaObject.EmptyMetaObjects); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyInvokeMemberBinder.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyInvokeMemberBinder.cs new file mode 100644 index 0000000000..8b46d0005b --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyInvokeMemberBinder.cs @@ -0,0 +1,79 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Dynamic.Binders; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Compiler; +using IronRuby.Builtins; + +namespace IronRuby.Runtime.Calls { + using Ast = System.Linq.Expressions.Expression; + + internal sealed class RubyInvokeMemberBinder : InvokeMemberBinder { + private readonly RubyContext/*!*/ _context; + + public RubyInvokeMemberBinder(RubyContext/*!*/ context, string/*!*/ name, ArgumentInfo[]/*!*/ arguments) + : base(name, false, arguments) { + _context = context; + } + + public override object CacheIdentity { + get { return this; } + } + + public override MetaObject/*!*/ FallbackInvoke(MetaObject/*!*/ self, MetaObject/*!*/[]/*!*/ args, MetaObject/*!*/ onBindingError) { + var result = TryBind(_context, this, self, args); + if (result != null) { + return result; + } + + // TODO: return ((DefaultBinder)_context.Binder).GetMember(Name, self, Ast.Null(typeof(CodeContext)), true); + throw new NotImplementedException(); + } + + public override MetaObject/*!*/ FallbackInvokeMember(MetaObject/*!*/ self, MetaObject/*!*/[]/*!*/ args, MetaObject/*!*/ onBindingError) { + var result = TryBind(_context, this, self, args); + if (result != null) { + return result; + } + + // TODO: return ((DefaultBinder)_context.Binder).GetMember(Name, self, Ast.Null(typeof(CodeContext)), true); + throw new NotImplementedException(); + } + + public static MetaObject TryBind(RubyContext/*!*/ context, InvokeMemberBinder/*!*/ binder, MetaObject/*!*/ target, MetaObject/*!*/[]/*!*/ args) { + Assert.NotNull(context, target); + + var metaBuilder = new MetaObjectBuilder(); + + RubyCallAction.Bind(metaBuilder, binder.Name, + new CallArguments( + new MetaObject(Ast.Constant(context), Restrictions.Empty, context), + target, + args, + RubyCallSignature.Simple(binder.Arguments.Count) + ) + ); + + // TODO: we should return null if we fail, we need to throw exception for now: + return metaBuilder.CreateMetaObject(binder, MetaObject.EmptyMetaObjects); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyLambdaMethodInfo.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyLambdaMethodInfo.cs new file mode 100644 index 0000000000..f5fa4419af --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyLambdaMethodInfo.cs @@ -0,0 +1,61 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; + +namespace IronRuby.Runtime.Calls { + using Ast = System.Linq.Expressions.Expression; + using System.Diagnostics; + + public class RubyLambdaMethodInfo : RubyMemberInfo { + private readonly Proc/*!*/ _lambda; + private readonly string/*!*/ _definitionName; + + public Proc/*!*/ Lambda { + get { return _lambda; } + } + + public string/*!*/ DefinitionName { + get { return _definitionName; } + } + + internal RubyLambdaMethodInfo(Proc/*!*/ lambda, string/*!*/ definitionName, RubyMemberFlags flags, RubyModule/*!*/ declaringModule) + : base(flags, declaringModule) { + Assert.NotNull(lambda, definitionName, declaringModule); + _lambda = lambda; + _definitionName = definitionName; + } + + protected internal override RubyMemberInfo/*!*/ Copy(RubyMemberFlags flags, RubyModule/*!*/ module) { + return new RubyLambdaMethodInfo(_lambda, _definitionName, flags, module); + } + + internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + Debug.Assert(!args.Signature.HasBlock); + + Proc.SetProcCallRule( + metaBuilder, + Ast.Constant(_lambda), // proc object + args.TargetExpression, // self + Ast.Constant(this), // this method for super and class_eval + args + ); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyMemberInfo.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyMemberInfo.cs new file mode 100644 index 0000000000..7e9e9ba3e0 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyMemberInfo.cs @@ -0,0 +1,127 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using System.Diagnostics; + +namespace IronRuby.Runtime.Calls { + + public class RubyMemberInfo { + // Singleton used to undefine methods: stops method resolution + internal static readonly RubyMemberInfo/*!*/ UndefinedMethod = new RubyMemberInfo(); + // Singleton used to hide CLR methods: doesn't stop method resolution, skips CLR method lookup + internal static readonly RubyMemberInfo/*!*/ HiddenMethod = new RubyMemberInfo(); + + private readonly RubyMemberFlags _flags; + private bool _invalidateSitesOnOverride; + + // Method, UnboundMethod, super, trace: Aliased methods preserve the declaring module. + // Null for dummy methods. + private readonly RubyModule _declaringModule; + + public RubyMethodVisibility Visibility { + get { return (RubyMethodVisibility)(_flags & RubyMemberFlags.VisibilityMask); } + } + + internal bool IsModuleFunction { + get { return (_flags & RubyMemberFlags.ModuleFunction) != 0; } + } + + internal bool IsEmpty { + get { return (_flags & RubyMemberFlags.Empty) != 0; } + } + + internal RubyMemberFlags Flags { + get { return _flags; } + } + + /// + /// Method definition that replaces/overrides this method will cause version update of all dependent subclasses/modules, which + /// triggers invalidation of sites that are bound to those classes. + /// + internal bool InvalidateSitesOnOverride { + get { return _invalidateSitesOnOverride; } + set { _invalidateSitesOnOverride = value; } + } + + public RubyModule/*!*/ DeclaringModule { + get { + Debug.Assert(_declaringModule != null); + return _declaringModule; + } + } + + public RubyContext/*!*/ Context { + get { + Debug.Assert(_declaringModule != null); + return _declaringModule.Context; + } + } + + // TODO: + public virtual int Arity { + get { return 0; } + } + + public bool IsUndefined { + get { return ReferenceEquals(this, UndefinedMethod); } + } + + public bool IsHidden { + get { return ReferenceEquals(this, HiddenMethod); } + } + + // undefined, hidden method: + private RubyMemberInfo() { + } + + internal RubyMemberInfo(RubyMemberFlags flags, RubyModule/*!*/ declaringModule) { + Assert.NotNull(declaringModule); + Debug.Assert(flags != RubyMemberFlags.Invalid); + + _flags = flags; + _declaringModule = declaringModule; + } + + internal protected virtual RubyMemberInfo Copy(RubyMemberFlags flags, RubyModule/*!*/ module) { + throw Assert.Unreachable; + } + + internal virtual void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + throw Assert.Unreachable; + } + + internal virtual void ApplyBlockFlowHandling(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + // nop + } + + internal void BuildCall(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + BuildCallNoFlow(metaBuilder, args, name); + ApplyBlockFlowHandling(metaBuilder, args); + } + + internal virtual void BuildSuperCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, RubyModule/*!*/ declaringModule) { + BuildCallNoFlow(metaBuilder, args, name); + } + + internal virtual void BuildSuperCall(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, RubyModule/*!*/ declaringModule) { + BuildSuperCallNoFlow(metaBuilder, args, name, declaringModule); + ApplyBlockFlowHandling(metaBuilder, args); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyMethodGroupInfo.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyMethodGroupInfo.cs new file mode 100644 index 0000000000..a36940054e --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyMethodGroupInfo.cs @@ -0,0 +1,505 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Compiler.Generation; + +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using Ast = System.Linq.Expressions.Expression; +using IronRuby.Compiler; + +namespace IronRuby.Runtime.Calls { + + /// + /// Performs method binding for calling CLR methods. + /// Currently this is used for all builtin libary methods and interop calls to CLR methods + /// + public sealed class RubyMethodGroupInfo : RubyMemberInfo { + private readonly Delegate/*!*/[]/*!*/ _overloads; + private IList _methodBases; + private IList _staticDispatchMethods; + private bool? _hasVirtuals; + + // remove call site type object (CLR static methods don't accept self type): + private readonly bool _isClrStatic; + private readonly bool _isRubyMethod; + + public bool IsRubyMethod { + get { return _isRubyMethod; } + } + + private IList/*!*/ MethodBases { + get { + if (_methodBases == null) { + var result = new MethodBase[_overloads.Length]; + for (int i = 0; i < _overloads.Length; i++) { + result[i] = _overloads[i].Method; + } + _methodBases = result; + } + + // either all methods in the group are static or instance, a mixture is not allowed: + Debug.Assert( + CollectionUtils.TrueForAll(_methodBases, (method) => method.IsStatic) || + CollectionUtils.TrueForAll(_methodBases, (method) => !method.IsStatic) + ); + + return _methodBases; + } + } + + /// + /// Creates a Ruby method implemented by a method group of CLR methods. + /// + internal RubyMethodGroupInfo(Delegate/*!*/[]/*!*/ overloads, RubyMemberFlags flags, + RubyModule/*!*/ declaringModule) + : base(flags, declaringModule) { + Assert.NotNullItems(overloads); + Assert.NotNull(declaringModule); + _overloads = overloads; + + _isClrStatic = false; + _isRubyMethod = true; + } + + /// + /// Creates a CLR method group. + /// + internal RubyMethodGroupInfo(IList/*!*/ methods, RubyModule/*!*/ declaringModule, bool isStatic) + : base(RubyMemberFlags.Public, declaringModule) { + Assert.NotNull(methods, declaringModule); + _methodBases = methods; + _isClrStatic = isStatic; + _isRubyMethod = false; + } + + // copy ctor + private RubyMethodGroupInfo(RubyMethodGroupInfo/*!*/ info, RubyMemberFlags flags, RubyModule/*!*/ module) + : base(flags, module) { + _methodBases = info._methodBases; + _overloads = info._overloads; + _isRubyMethod = info._isRubyMethod; + _isClrStatic = info._isClrStatic; + } + + protected internal override RubyMemberInfo/*!*/ Copy(RubyMemberFlags flags, RubyModule/*!*/ module) { + return new RubyMethodGroupInfo(this, flags, module); + } + + public override int Arity { + get { + int minParameters = Int32.MaxValue; + int maxParameters = 0; + bool hasOptional = false; + foreach (MethodBase method in MethodBases) { + int mandatory, optional; + bool acceptsBlock; + RubyBinder.GetParameterCount(method.GetParameters(), out mandatory, out optional, out acceptsBlock); + if (mandatory > 0) { + mandatory--; // account for "self" + } + if (mandatory < minParameters) { + minParameters = mandatory; + } + if (mandatory > maxParameters) { + maxParameters = mandatory; + } + if (!hasOptional && optional > 0) { + hasOptional = true; + } + } + if (hasOptional || maxParameters > minParameters) { + return -minParameters - 1; + } else { + return minParameters; + } + } + } + + #region Static dispatch to virtual methods + + private bool HasVirtuals { + get { + if (!_hasVirtuals.HasValue) { + if (_isClrStatic) { + _hasVirtuals = false; + } else { + bool hasVirtuals = false; + foreach (MethodBase method in MethodBases) { + if (method.IsVirtual) { + hasVirtuals = true; + break; + } + } + _hasVirtuals = hasVirtuals; + } + } + return _hasVirtuals.Value; + } + } + + private IList/*!*/ GetStaticDispatchMethods(Type/*!*/ baseType, string/*!*/ name) { + if (!HasVirtuals) { + return MethodBases; + } + if (_staticDispatchMethods == null) { + _staticDispatchMethods = new MethodBase[MethodBases.Count]; + for (int i = 0; i < MethodBases.Count; i++) { + MethodBase method = MethodBases[i]; + _staticDispatchMethods[i] = method; + + MethodInfo methodInfo = (method as MethodInfo); + if (methodInfo != null && methodInfo.IsVirtual) { + _staticDispatchMethods[i] = WrapMethod(methodInfo, baseType); + } + } + } + return _staticDispatchMethods; + } + + public static DynamicMethod/*!*/ WrapMethod(MethodInfo/*!*/ info, Type/*!*/ associatedType) { + var originalParams = info.GetParameters(); + var newParams = new Type[originalParams.Length + 1]; + string name = ""; + newParams[0] = info.DeclaringType; + for (int i = 0; i < originalParams.Length; i++) { + newParams[i + 1] = originalParams[i].ParameterType; + } + DynamicMethod result = new DynamicMethod(name, info.ReturnType, newParams, associatedType); + ILGenerator ilg = result.GetILGenerator(); + for (int i = 0; i < newParams.Length; i++) { + ilg.Emit(OpCodes.Ldarg, i); + } + ilg.EmitCall(OpCodes.Call, info, null); + ilg.Emit(OpCodes.Ret); + return result; + } + + #endregion + + #region Dynamic Sites + + private static Type GetAssociatedSystemType(RubyModule/*!*/ module) { + if (module.IsClass) { + RubyClass cls = (module as RubyClass); + Type type = cls.GetUnderlyingSystemType(); + if (type != null) { + return type; + } + } + return typeof(SuperCallAction); + } + + internal override void BuildSuperCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, RubyModule/*!*/ declaringModule) { + Assert.NotNull(declaringModule, metaBuilder, args); + + IList methods; + if (!declaringModule.IsSingletonClass) { + Type associatedType = GetAssociatedSystemType(declaringModule); + methods = GetStaticDispatchMethods(associatedType, name); + } else { + methods = MethodBases; + } + + BuildCallNoFlow(metaBuilder, args, name, methods, !_isClrStatic, !_isRubyMethod); + } + + internal static BindingTarget/*!*/ ResolveOverload(string/*!*/ name, IList/*!*/ overloads, CallArguments/*!*/ args, + bool includeSelf, bool selfIsInstance) { + + var methodBinder = MethodBinder.MakeBinder(args.RubyContext.Binder, name, overloads, SymbolId.EmptySymbols, NarrowingLevel.None, NarrowingLevel.All); + var argTypes = GetSignatureToMatch(args, includeSelf, selfIsInstance); + return methodBinder.MakeBindingTarget(CallTypes.None, argTypes); + } + + internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + Assert.NotNull(name, metaBuilder, args); + + BuildCallNoFlow(metaBuilder, args, name, MethodBases, !_isClrStatic, !_isRubyMethod); + } + + /// + /// Resolves an library method overload and builds call expression. + /// The resulting expression on meta-builder doesn't handle block control flow yet. + /// + internal static void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, + IList/*!*/ overloads, bool includeSelf, bool selfIsInstance) { + + var bindingTarget = ResolveOverload(name, overloads, args, includeSelf, selfIsInstance); + if (bindingTarget.Success) { + bool calleeHasBlockParam = HasBlockParameter(bindingTarget.Method); + + // Allocates a variable holding BlockParam. At runtime the BlockParam is created with a new RFC instance that + // identifies the library method frame as a proc-converter target of a method unwinder triggered by break from a block. + // + // NOTE: We check for null block here -> test fore that fact is added in MakeActualArgs + if (metaBuilder.BfcVariable == null && args.Signature.HasBlock && args.GetBlock() != null && calleeHasBlockParam) { + metaBuilder.BfcVariable = metaBuilder.GetTemporary(typeof(BlockParam), "#bfc"); + } + + var actualArgs = MakeActualArgs(metaBuilder, args, includeSelf, selfIsInstance, calleeHasBlockParam, true); + var parameterBinder = new RubyParameterBinder(args.RubyContext.Binder, args.MetaContext.Expression, args.Signature.HasScope); + var targetExpression = bindingTarget.MakeExpression(parameterBinder, actualArgs); + + metaBuilder.Result = targetExpression; + } else { + metaBuilder.SetError( + Methods.MakeInvalidArgumentTypesError.OpCall(Ast.Constant(name)) + ); + } + } + + internal override void ApplyBlockFlowHandling(MetaObjectBuilder metaBuilder, CallArguments args) { + ApplyBlockFlowHandlingInternal(metaBuilder, args); + } + + /// + /// Takes current result and wraps it into try-filter(MethodUnwinder)-finally block that ensures correct "break" behavior for + /// library method calls with block given in bfcVariable (BlockParam). + /// + internal static void ApplyBlockFlowHandlingInternal(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + if (metaBuilder.Error) { + return; + } + + Expression expression = metaBuilder.Result; + Expression bfcVariable = metaBuilder.BfcVariable; + + // Method call with proc can invoke control flow that returns an arbitrary value from the call, so we need to type result to Object. + // Otherwise, the result could only be result of targetExpression unless its return type is void. + Type resultType = (bfcVariable != null) ? typeof(object) : expression.Type; + + Expression resultVariable; + if (resultType != typeof(void)) { + resultVariable = metaBuilder.GetTemporary(resultType, "#result"); + } else { + resultVariable = Expression.Empty(); + } + + if (expression.Type != typeof(void)) { + expression = Ast.Assign(resultVariable, AstUtils.Convert(expression, resultType)); + } + + // a non-null proc is being passed to the callee: + if (bfcVariable != null) { + ParameterExpression methodUnwinder = metaBuilder.GetTemporary(typeof(MethodUnwinder), "#unwinder"); + + expression = AstFactory.Block( + Ast.Assign(bfcVariable, Methods.CreateBfcForLibraryMethod.OpCall(AstUtils.Convert(args.GetBlockExpression(), typeof(Proc)))), + AstUtils.Try( + expression + ).Filter(methodUnwinder, Methods.IsProcConverterTarget.OpCall(bfcVariable, methodUnwinder), + Ast.Assign(resultVariable, Ast.Field(methodUnwinder, MethodUnwinder.ReturnValueField)) + ).Finally( + Methods.LeaveProcConverter.OpCall(bfcVariable) + ), + resultVariable + ); + } + + metaBuilder.Result = expression; + } + + private static bool HasBlockParameter(MethodBase/*!*/ method) { + foreach (ParameterInfo param in method.GetParameters()) { + if (param.ParameterType == typeof(BlockParam)) { + return true; + } + } + return false; + } + + public static Expression[]/*!*/ MakeActualArgs(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, + bool includeSelf, bool selfIsInstance, bool calleeHasBlockParam, bool injectMissingBlockParam) { + + var actualArgs = new List(args.ExplicitArgumentCount); + + // self (instance): + if (includeSelf && selfIsInstance) { + // test already added by method resolver + Debug.Assert(args.TargetExpression != null); + AddArgument(actualArgs, args.Target, args.TargetExpression); + } + + Proc block = null; + Expression blockExpression = null; + + // block test - we need to test for a block regardless of whether it is actually passed to the method or not + // since the information that the block is not null is used for overload resolution. + if (args.Signature.HasBlock) { + block = args.GetBlock(); + blockExpression = args.GetBlockExpression(); + + if (block == null) { + metaBuilder.AddRestriction(Ast.Equal(blockExpression, Ast.Constant(null))); + } else { + // don't need to test the exact type of the Proc since the code is subclass agnostic: + metaBuilder.AddRestriction(Ast.NotEqual(blockExpression, Ast.Constant(null))); + } + } + + // block: + if (calleeHasBlockParam) { + if (args.Signature.HasBlock) { + if (block == null) { + // the user explicitly passed nil as a block arg: + actualArgs.Add(Ast.Constant(null)); + } else { + // pass BlockParam: + Debug.Assert(metaBuilder.BfcVariable != null); + actualArgs.Add(metaBuilder.BfcVariable); + } + } else { + // no block passed into a method with a BlockParam: + actualArgs.Add(Ast.Constant(null)); + } + } else if (injectMissingBlockParam) { + // no block passed into a method w/o a BlockParam (we still need to fill the missing block argument): + actualArgs.Add(Ast.Constant(null)); + } + + // self (non-instance): + if (includeSelf && !selfIsInstance) { + // test already added by method resolver + AddArgument(actualArgs, args.Target, args.TargetExpression); + } + + // simple arguments: + for (int i = 0; i < args.SimpleArgumentCount; i++) { + var value = args.GetSimpleArgument(i); + var expr = args.GetSimpleArgumentExpression(i); + + metaBuilder.AddObjectTypeRestriction(value, expr); + AddArgument(actualArgs, value, expr); + } + + // splat argument: + int listLength; + ParameterExpression listVariable; + if (args.Signature.HasSplattedArgument) { + object splattedArg = args.GetSplattedArgument(); + Expression splattedArgExpression = args.GetSplattedArgumentExpression(); + + if (metaBuilder.AddSplattedArgumentTest(splattedArg, splattedArgExpression, out listLength, out listVariable)) { + + // AddTestForListArg only returns 'true' if the argument is a List + var list = (List)splattedArg; + + // get arguments, add tests + for (int j = 0; j < listLength; j++) { + var value = list[j]; + var expr = Ast.Call(listVariable, typeof(List).GetMethod("get_Item"), Ast.Constant(j)); + + metaBuilder.AddObjectTypeCondition(value, expr); + AddArgument(actualArgs, value, expr); + } + + } else { + // argument is not an array => add the argument itself: + AddArgument(actualArgs, splattedArg, splattedArgExpression); + } + } + + // rhs argument: + if (args.Signature.HasRhsArgument) { + var value = args.GetRhsArgument(); + var expr = args.GetRhsArgumentExpression(); + + metaBuilder.AddObjectTypeRestriction(value, expr); + AddArgument(actualArgs, value, expr); + } + + return actualArgs.ToArray(); + } + + private static void AddArgument(List/*!*/ actualArgs, object arg, Expression/*!*/ expr) { + if (arg == null) { + actualArgs.Add(Ast.Constant(null)); + } else { + var type = CompilerHelpers.GetVisibleType(arg); + if (type.IsValueType) { + actualArgs.Add(expr); + } else { + actualArgs.Add(AstUtils.Convert(expr, type)); + } + } + } + + private static Type[]/*!*/ GetSignatureToMatch(CallArguments/*!*/ args, bool includeSelf, bool selfIsInstance) { + var result = new List(args.ExplicitArgumentCount); + + // self (instance): + if (includeSelf && selfIsInstance) { + result.Add(CompilerHelpers.GetType(args.Target)); + } + + // block: + if (args.Signature.HasBlock) { + // use None to let binder know that [NotNull]BlockParam is not applicable + result.Add(args.GetBlock() != null ? typeof(BlockParam) : typeof(Null)); + } else { + result.Add(typeof(MissingBlockParam)); + } + + // self (non-instance): + if (includeSelf && !selfIsInstance) { + result.Add(CompilerHelpers.GetType(args.Target)); + } + + // simple args: + for (int i = 0; i < args.SimpleArgumentCount; i++) { + result.Add(CompilerHelpers.GetType(args.GetSimpleArgument(i))); + } + + // splat arg: + if (args.Signature.HasSplattedArgument) { + object splattedArg = args.GetSplattedArgument(); + + var list = splattedArg as List; + if (list != null) { + foreach (object obj in list) { + result.Add(CompilerHelpers.GetType(obj)); + } + } else { + result.Add(CompilerHelpers.GetType(splattedArg)); + } + } + + // rhs arg: + if (args.Signature.HasRhsArgument) { + result.Add(CompilerHelpers.GetType(args.GetRhsArgument())); + } + + return result.ToArray(); + } + + #endregion + } +} + diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyMethodInfo.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyMethodInfo.cs new file mode 100644 index 0000000000..372b0a6844 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyMethodInfo.cs @@ -0,0 +1,179 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using System.Linq.Expressions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Compiler; +using MethodDeclaration = IronRuby.Compiler.Ast.MethodDeclaration; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using Ast = System.Linq.Expressions.Expression; + +namespace IronRuby.Runtime.Calls { + public sealed class RubyMethodInfo : RubyMemberInfo { + // Delegate type for methods with many parameters. + internal static readonly Type ParamsArrayDelegateType = typeof(Func); + + // Method AST: + // - MethodDeclaration in normal execution mode (no -save flag), or after queried. + // - MethodDeclaration.Serializable in -save mode. + // - char[] in -load mode when the method's source code is deserialized from a compiled assembly. + private object/*!*/ _ast; + + private readonly Delegate/*!*/ _method; + private readonly string/*!*/ _definitionName; + + private readonly int _mandatoryParamCount; + private readonly int _optionalParamCount; + private readonly bool _hasUnsplatParameter; + + public Delegate/*!*/ Method { get { return _method; } } + public string/*!*/ DefinitionName { get { return _definitionName; } } + public int MandatoryParamCount { get { return _mandatoryParamCount; } } + public int OptionalParamCount { get { return _optionalParamCount; } } + public bool HasUnsplatParameter { get { return _hasUnsplatParameter; } } + + // method: + internal RubyMethodInfo(object/*!*/ ast, Delegate/*!*/ method, RubyModule/*!*/ declaringModule, + string/*!*/ definitionName, int mandatory, int optional, bool hasUnsplatParameter, RubyMemberFlags flags) + : base(flags, declaringModule) { + Assert.NotNull(ast, method, declaringModule, definitionName); + + _ast = ast; + _method = method; + _mandatoryParamCount = mandatory; + _optionalParamCount = optional; + _hasUnsplatParameter = hasUnsplatParameter; + _definitionName = definitionName; + } + + protected internal override RubyMemberInfo/*!*/ Copy(RubyMemberFlags flags, RubyModule/*!*/ module) { + return new RubyMethodInfo(_ast, _method, module, _definitionName, _mandatoryParamCount, _optionalParamCount, + _hasUnsplatParameter, flags + ); + } + + public override int Arity { + get { + if (_optionalParamCount > 0) { + return -_mandatoryParamCount - 1; + } else { + return _mandatoryParamCount; + } + } + } + + public MethodDeclaration/*!*/ GetSyntaxTree() { + // live tree: + var tree = _ast as MethodDeclaration; + if (tree != null) { + return tree; + } + + // live tree wrapped into IExpressionSerializable class: + var serializableTree = _ast as MethodDeclaration.Serializable; + if (serializableTree != null) { + _ast = serializableTree.Method; + return serializableTree.Method; + } + + // TODO: serialized tree: + throw new NotImplementedException(); + } + + #region Dynamic Sites + + internal override void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + Assert.NotNull(metaBuilder, args, name); + + // 2 implicit args: self, block + var argsBuilder = new ArgsBuilder(2, _mandatoryParamCount, _optionalParamCount, _hasUnsplatParameter); + argsBuilder.SetImplicit(0, AstFactory.Box(args.TargetExpression)); + argsBuilder.SetImplicit(1, args.Signature.HasBlock ? AstUtils.Convert(args.GetBlockExpression(), typeof(Proc)) : AstFactory.NullOfProc); + argsBuilder.AddCallArguments(metaBuilder, args); + + if (metaBuilder.Error) { + return; + } + + // box explicit arguments: + var boxedArguments = argsBuilder.GetArguments(); + for (int i = 2; i < boxedArguments.Length; i++) { + boxedArguments[i] = AstFactory.Box(boxedArguments[i]); + } + + if (_method.GetType() == ParamsArrayDelegateType) { + // Func + metaBuilder.Result = AstFactory.CallDelegate(_method, new[] { + boxedArguments[0], + boxedArguments[1], + Ast.NewArrayInit(typeof(object), ArrayUtils.ShiftLeft(boxedArguments, 2)) + }); + } else { + metaBuilder.Result = AstFactory.CallDelegate(_method, boxedArguments); + } + } + + internal override void ApplyBlockFlowHandling(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + ApplyBlockFlowHandlingInternal(metaBuilder, args); + } + + /// + /// Takes current result and wraps it into try-filter(MethodUnwinder)-finally block that ensures correct "break" behavior for + /// Ruby method calls with a block given in arguments. + /// + /// Sets up a RFC frame similarly to MethodDeclaration. + /// + internal static void ApplyBlockFlowHandlingInternal(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + // TODO (improvement): + // We don't special case null block here, although we could (we would need a test for that then). + // We could also statically know (via call-site flag) that the current method is not a proc-converter (passed by ref), + // which would make such calls faster. + if (metaBuilder.Error || !args.Signature.HasBlock) { + return; + } + + Expression rfcVariable = metaBuilder.GetTemporary(typeof(RuntimeFlowControl), "#rfc"); + ParameterExpression methodUnwinder = metaBuilder.GetTemporary(typeof(MethodUnwinder), "#unwinder"); + Expression resultVariable = metaBuilder.GetTemporary(typeof(object), "#result"); + + metaBuilder.Result = Ast.Block( + // initialize frame (RFC): + Ast.Assign(rfcVariable, Methods.CreateRfcForMethod.OpCall(AstUtils.Convert(args.GetBlockExpression(), typeof(Proc)))), + AstUtils.Try( + Ast.Assign(resultVariable, metaBuilder.Result) + ).Filter(methodUnwinder, Ast.Equal(Ast.Field(methodUnwinder, MethodUnwinder.TargetFrameField), rfcVariable), + + // return unwinder.ReturnValue; + Ast.Assign(resultVariable, Ast.Field(methodUnwinder, MethodUnwinder.ReturnValueField)) + + ).Finally( + // we need to mark the RFC dead snce the block might escape and break later: + Ast.Assign(Ast.Field(rfcVariable, RuntimeFlowControl.IsActiveMethodField), Ast.Constant(false)) + ), + resultVariable + ); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyParameterBinder.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyParameterBinder.cs new file mode 100644 index 0000000000..82fb5f5f86 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RubyParameterBinder.cs @@ -0,0 +1,87 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using IronRuby.Builtins; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Utils; +using Ast = System.Linq.Expressions.Expression; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using IronRuby.Compiler; + +namespace IronRuby.Runtime.Calls { + internal sealed class RubyParameterBinder : ParameterBinder { + private Expression _scopeExpression; + private Expression _contextExpression; + + public Expression/*!*/ ScopeExpression { + get { return _scopeExpression ?? (_scopeExpression = Methods.GetEmptyScope.OpCall(_contextExpression)); } + } + + public Expression/*!*/ ContextExpression { + get { return _contextExpression ?? (_contextExpression = Methods.GetContextFromScope.OpCall(_scopeExpression)); } + } + + public RubyParameterBinder(ActionBinder/*!*/ binder, Expression/*!*/ scopeOrContextExpression, bool isScope) + : base(binder) { + Assert.NotNull(binder, scopeOrContextExpression); + + if (isScope) { + _scopeExpression = AstUtils.Convert(scopeOrContextExpression, typeof(RubyScope)); + } else { + _contextExpression = AstUtils.Convert(scopeOrContextExpression, typeof(RubyContext)); + } + } + + public override Expression/*!*/ ConvertExpression(Expression/*!*/ expr, ParameterInfo info, Type/*!*/ toType) { + Type fromType = expr.Type; + + // block: + if (fromType == typeof(MissingBlockParam)) { + Debug.Assert(toType == typeof(BlockParam) || toType == typeof(MissingBlockParam)); + return Ast.Constant(null); + } + + if (fromType == typeof(BlockParam) && toType == typeof(MissingBlockParam)) { + return Ast.Constant(null); + } + + // protocol conversions: + if (info != null && info.IsDefined(typeof(DefaultProtocolAttribute), false)) { + var action = ProtocolConversionAction.TryGetConversionAction(toType); + if (action != null) { + // TODO: once we work with MetaObjects, we could inline these dynamic sites: + return Ast.Dynamic(action, toType, ScopeExpression, expr); + } + + throw new InvalidOperationException(String.Format("No default protocol conversion for type {0}.", toType)); + } + + return Binder.ConvertExpression(expr, toType, ConversionResultKind.ExplicitCast, ScopeExpression); + } + + public override Expression/*!*/ GetDynamicConversion(Expression/*!*/ value, Type/*!*/ type) { + return Expression.Dynamic(OldConvertToAction.Make(Binder, type), type, ScopeExpression, value); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/RuleGenerators.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RuleGenerators.cs new file mode 100644 index 0000000000..0319e38938 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/RuleGenerators.cs @@ -0,0 +1,46 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using IronRuby.Builtins; +using IronRuby.Compiler; +using System.Diagnostics; +using System; + +namespace IronRuby.Runtime.Calls { + + public delegate void RuleGenerator(MetaObjectBuilder/*!*/ result, CallArguments/*!*/ args, string/*!*/ name); + + public static class RuleGenerators { + public static void InstanceConstructor(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + ((RubyClass)args.Target).BuildObjectConstruction(metaBuilder, args, name); + } + + public static void InstanceConstructorForGroup(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + RubyClass cls = args.RubyContext.GetClass(((TypeGroup)args.Target).Type); + + cls.BuildObjectConstruction(metaBuilder, args, name); + } + + public static void InstanceAllocator(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + ((RubyClass)args.Target).BuildObjectAllocation(metaBuilder, args, name); + } + + public static void MethodCall(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name) { + ((RubyMethod)args.Target).SetRuleForCall(metaBuilder, args); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Calls/SuperCallAction.cs b/merlin/main/languages/ruby/Ruby/Runtime/Calls/SuperCallAction.cs new file mode 100644 index 0000000000..d911f105c6 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Calls/SuperCallAction.cs @@ -0,0 +1,142 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Dynamic.Binders; + +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Runtime; + +using IronRuby.Builtins; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using Ast = System.Linq.Expressions.Expression; +using AstFactory = IronRuby.Compiler.Ast.AstFactory; +using IronRuby.Compiler; + +namespace IronRuby.Runtime.Calls { + + public sealed class SuperCallAction : MetaObjectBinder, IExpressionSerializable, IEquatable { + private readonly RubyCallSignature _signature; + private readonly int _lexicalScopeId; + + internal RubyCallSignature Signature { + get { return _signature; } + } + + private SuperCallAction(RubyCallSignature signature, int lexicalScopeId) { + _signature = signature; + _lexicalScopeId = lexicalScopeId; + } + + public static SuperCallAction/*!*/ Make(RubyCallSignature signature, int lexicalScopeId) { + return new SuperCallAction(signature, lexicalScopeId); + } + + public static SuperCallAction/*!*/ Make(int argumentCount, int lexicalScopeId) { + ContractUtils.Requires(argumentCount >= 0, "argumentCount"); + return new SuperCallAction(RubyCallSignature.WithScope(argumentCount), lexicalScopeId); + } + + public override object/*!*/ CacheIdentity { + get { return this; } + } + + #region Object Overrides, IEquatable + + public override bool Equals(object obj) { + return Equals(obj as SuperCallAction); + } + + public override int GetHashCode() { + return _signature.GetHashCode() ^ _lexicalScopeId; + } + + public override string/*!*/ ToString() { + return "super" + _signature.ToString() + ":" + _lexicalScopeId; + } + + public bool Equals(SuperCallAction other) { + return other != null && _signature.Equals(other._signature) && _lexicalScopeId == other._lexicalScopeId; + } + + #endregion + + #region Rule Generation + + public override MetaObject/*!*/ Bind(MetaObject/*!*/ context, MetaObject/*!*/[]/*!*/ args) { + var mo = new MetaObjectBuilder(); + SetRule(mo, new CallArguments(context, args, _signature)); + return mo.CreateMetaObject(this, context, args); + } + + internal void SetRule(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) { + RubyModule currentDeclaringModule; + string currentMethodName; + + var scope = args.Scope; + + object target; + scope.GetSuperCallTarget(out currentDeclaringModule, out currentMethodName, out target); + + var targetExpression = metaBuilder.GetTemporary(typeof(object), "#super-self"); + + metaBuilder.AddCondition( + Methods.IsSuperCallTarget.OpCall( + AstUtils.Convert(args.ScopeExpression, typeof(RubyScope)), + Ast.Constant(currentDeclaringModule), + AstUtils.Constant(currentMethodName), + targetExpression + ) + ); + + args.SetTarget(targetExpression, target); + + Debug.Assert(currentDeclaringModule != null); + + // target is stored in a local, therefore it cannot be part of the restrictions: + metaBuilder.TreatRestrictionsAsConditions = true; + metaBuilder.AddTargetTypeTest(target, targetExpression, scope.RubyContext, args.ContextExpression); + metaBuilder.TreatRestrictionsAsConditions = false; + + RubyMemberInfo method = scope.RubyContext.ResolveSuperMethod(target, currentMethodName, currentDeclaringModule); + + // super calls don't go to method_missing + if (method == null) { + metaBuilder.SetError(Methods.MakeMissingSuperException.OpCall(Ast.Constant(currentMethodName))); + } else { + method.InvalidateSitesOnOverride = true; + method.BuildSuperCall(metaBuilder, args, currentMethodName, currentDeclaringModule); + } + } + + #endregion + + #region IExpressionSerializable Members + + Expression/*!*/ IExpressionSerializable.CreateExpression() { + return Expression.Call( + typeof(SuperCallAction).GetMethod("Make", new Type[] { typeof(RubyCallSignature), typeof(int) }), + _signature.CreateExpression(), + Expression.Constant(_lexicalScopeId) + ); + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Converter.Generated.cs b/merlin/main/languages/ruby/Ruby/Runtime/Converter.Generated.cs new file mode 100644 index 0000000000..b544533c98 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Converter.Generated.cs @@ -0,0 +1,976 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Math; + +namespace IronRuby.Runtime { + public static partial class Converter { + #region Generated conversion helpers + + // *** BEGIN GENERATED CODE *** + + + /// + /// Conversion routine TryConvertToByte - converts object to Byte + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToByte(object value, out Byte result) { + try { + result = ConvertToByte(value); + return true; + } catch { + result = default(Byte); + return false; + } + } + + /// + /// Conversion routine TryConvertToSByte - converts object to SByte + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToSByte(object value, out SByte result) { + try { + result = ConvertToSByte(value); + return true; + } catch { + result = default(SByte); + return false; + } + } + + /// + /// Conversion routine TryConvertToInt16 - converts object to Int16 + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToInt16(object value, out Int16 result) { + try { + result = ConvertToInt16(value); + return true; + } catch { + result = default(Int16); + return false; + } + } + + /// + /// Conversion routine TryConvertToInt32 - converts object to Int32 + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToInt32(object value, out Int32 result) { + try { + result = ConvertToInt32(value); + return true; + } catch { + result = default(Int32); + return false; + } + } + + /// + /// Conversion routine TryConvertToInt64 - converts object to Int64 + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToInt64(object value, out Int64 result) { + try { + result = ConvertToInt64(value); + return true; + } catch { + result = default(Int64); + return false; + } + } + + /// + /// Conversion routine TryConvertToUInt16 - converts object to UInt16 + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToUInt16(object value, out UInt16 result) { + try { + result = ConvertToUInt16(value); + return true; + } catch { + result = default(UInt16); + return false; + } + } + + /// + /// Conversion routine TryConvertToUInt32 - converts object to UInt32 + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToUInt32(object value, out UInt32 result) { + try { + result = ConvertToUInt32(value); + return true; + } catch { + result = default(UInt32); + return false; + } + } + + /// + /// Conversion routine TryConvertToUInt64 - converts object to UInt64 + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToUInt64(object value, out UInt64 result) { + try { + result = ConvertToUInt64(value); + return true; + } catch { + result = default(UInt64); + return false; + } + } + + /// + /// Conversion routine TryConvertToDouble - converts object to Double + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToDouble(object value, out Double result) { + try { + result = ConvertToDouble(value); + return true; + } catch { + result = default(Double); + return false; + } + } + + /// + /// Conversion routine TryConvertToBigInteger - converts object to BigInteger + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToBigInteger(object value, out BigInteger result) { + try { + result = ConvertToBigInteger(value); + return true; + } catch { + result = default(BigInteger); + return false; + } + } + + /// + /// Conversion routine TryConvertToComplex64 - converts object to Complex64 + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToComplex64(object value, out Complex64 result) { + try { + result = ConvertToComplex64(value); + return true; + } catch { + result = default(Complex64); + return false; + } + } + + /// + /// Conversion routine TryConvertToString - converts object to String + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToString(object value, out String result) { + try { + result = ConvertToString(value); + return true; + } catch { + result = default(String); + return false; + } + } + + /// + /// Conversion routine TryConvertToChar - converts object to Char + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvertToChar(object value, out Char result) { + try { + result = ConvertToChar(value); + return true; + } catch { + result = default(Char); + return false; + } + } + + // *** END GENERATED CODE *** + + #endregion + + #region Generated explicit enum conversion + + // *** BEGIN GENERATED CODE *** + + /// + /// Explicit conversion of Enum to Int32 + /// + internal static Int32 CastEnumToInt32(object value) { + Debug.Assert(value is Enum); + switch (((Enum)value).GetTypeCode()) { + case TypeCode.Int32: + return (Int32)value; + case TypeCode.Byte: + return (Int32)(Byte)value; + case TypeCode.SByte: + return (Int32)(SByte)value; + case TypeCode.Int16: + return (Int32)(Int16)value; + case TypeCode.Int64: + return (Int32)(Int64)value; + case TypeCode.UInt16: + return (Int32)(UInt16)value; + case TypeCode.UInt32: + return (Int32)(UInt32)value; + case TypeCode.UInt64: + return (Int32)(UInt64)value; + } + // Should never get here + Debug.Assert(false, "Invalid enum detected"); + return default(Int32); + } + /// + /// Explicit conversion of Enum to Byte + /// + internal static Byte CastEnumToByte(object value) { + Debug.Assert(value is Enum); + switch (((Enum)value).GetTypeCode()) { + case TypeCode.Int32: + return (Byte)(Int32)value; + case TypeCode.Byte: + return (Byte)value; + case TypeCode.SByte: + return (Byte)(SByte)value; + case TypeCode.Int16: + return (Byte)(Int16)value; + case TypeCode.Int64: + return (Byte)(Int64)value; + case TypeCode.UInt16: + return (Byte)(UInt16)value; + case TypeCode.UInt32: + return (Byte)(UInt32)value; + case TypeCode.UInt64: + return (Byte)(UInt64)value; + } + // Should never get here + Debug.Assert(false, "Invalid enum detected"); + return default(Byte); + } + /// + /// Explicit conversion of Enum to SByte + /// + internal static SByte CastEnumToSByte(object value) { + Debug.Assert(value is Enum); + switch (((Enum)value).GetTypeCode()) { + case TypeCode.Int32: + return (SByte)(Int32)value; + case TypeCode.Byte: + return (SByte)(Byte)value; + case TypeCode.SByte: + return (SByte)value; + case TypeCode.Int16: + return (SByte)(Int16)value; + case TypeCode.Int64: + return (SByte)(Int64)value; + case TypeCode.UInt16: + return (SByte)(UInt16)value; + case TypeCode.UInt32: + return (SByte)(UInt32)value; + case TypeCode.UInt64: + return (SByte)(UInt64)value; + } + // Should never get here + Debug.Assert(false, "Invalid enum detected"); + return default(SByte); + } + /// + /// Explicit conversion of Enum to Int16 + /// + internal static Int16 CastEnumToInt16(object value) { + Debug.Assert(value is Enum); + switch (((Enum)value).GetTypeCode()) { + case TypeCode.Int32: + return (Int16)(Int32)value; + case TypeCode.Byte: + return (Int16)(Byte)value; + case TypeCode.SByte: + return (Int16)(SByte)value; + case TypeCode.Int16: + return (Int16)value; + case TypeCode.Int64: + return (Int16)(Int64)value; + case TypeCode.UInt16: + return (Int16)(UInt16)value; + case TypeCode.UInt32: + return (Int16)(UInt32)value; + case TypeCode.UInt64: + return (Int16)(UInt64)value; + } + // Should never get here + Debug.Assert(false, "Invalid enum detected"); + return default(Int16); + } + /// + /// Explicit conversion of Enum to Int64 + /// + internal static Int64 CastEnumToInt64(object value) { + Debug.Assert(value is Enum); + switch (((Enum)value).GetTypeCode()) { + case TypeCode.Int32: + return (Int64)(Int32)value; + case TypeCode.Byte: + return (Int64)(Byte)value; + case TypeCode.SByte: + return (Int64)(SByte)value; + case TypeCode.Int16: + return (Int64)(Int16)value; + case TypeCode.Int64: + return (Int64)value; + case TypeCode.UInt16: + return (Int64)(UInt16)value; + case TypeCode.UInt32: + return (Int64)(UInt32)value; + case TypeCode.UInt64: + return (Int64)(UInt64)value; + } + // Should never get here + Debug.Assert(false, "Invalid enum detected"); + return default(Int64); + } + /// + /// Explicit conversion of Enum to UInt16 + /// + internal static UInt16 CastEnumToUInt16(object value) { + Debug.Assert(value is Enum); + switch (((Enum)value).GetTypeCode()) { + case TypeCode.Int32: + return (UInt16)(Int32)value; + case TypeCode.Byte: + return (UInt16)(Byte)value; + case TypeCode.SByte: + return (UInt16)(SByte)value; + case TypeCode.Int16: + return (UInt16)(Int16)value; + case TypeCode.Int64: + return (UInt16)(Int64)value; + case TypeCode.UInt16: + return (UInt16)value; + case TypeCode.UInt32: + return (UInt16)(UInt32)value; + case TypeCode.UInt64: + return (UInt16)(UInt64)value; + } + // Should never get here + Debug.Assert(false, "Invalid enum detected"); + return default(UInt16); + } + /// + /// Explicit conversion of Enum to UInt32 + /// + internal static UInt32 CastEnumToUInt32(object value) { + Debug.Assert(value is Enum); + switch (((Enum)value).GetTypeCode()) { + case TypeCode.Int32: + return (UInt32)(Int32)value; + case TypeCode.Byte: + return (UInt32)(Byte)value; + case TypeCode.SByte: + return (UInt32)(SByte)value; + case TypeCode.Int16: + return (UInt32)(Int16)value; + case TypeCode.Int64: + return (UInt32)(Int64)value; + case TypeCode.UInt16: + return (UInt32)(UInt16)value; + case TypeCode.UInt32: + return (UInt32)value; + case TypeCode.UInt64: + return (UInt32)(UInt64)value; + } + // Should never get here + Debug.Assert(false, "Invalid enum detected"); + return default(UInt32); + } + /// + /// Explicit conversion of Enum to UInt64 + /// + internal static UInt64 CastEnumToUInt64(object value) { + Debug.Assert(value is Enum); + switch (((Enum)value).GetTypeCode()) { + case TypeCode.Int32: + return (UInt64)(Int32)value; + case TypeCode.Byte: + return (UInt64)(Byte)value; + case TypeCode.SByte: + return (UInt64)(SByte)value; + case TypeCode.Int16: + return (UInt64)(Int16)value; + case TypeCode.Int64: + return (UInt64)(Int64)value; + case TypeCode.UInt16: + return (UInt64)(UInt16)value; + case TypeCode.UInt32: + return (UInt64)(UInt32)value; + case TypeCode.UInt64: + return (UInt64)value; + } + // Should never get here + Debug.Assert(false, "Invalid enum detected"); + return default(UInt64); + } + internal static Boolean CastEnumToBoolean(object value) { + Debug.Assert(value is Enum); + switch (((Enum)value).GetTypeCode()) { + case TypeCode.Int32: + return (Int32)value != 0; + case TypeCode.Byte: + return (Byte)value != 0; + case TypeCode.SByte: + return (SByte)value != 0; + case TypeCode.Int16: + return (Int16)value != 0; + case TypeCode.Int64: + return (Int64)value != 0; + case TypeCode.UInt16: + return (UInt16)value != 0; + case TypeCode.UInt32: + return (UInt32)value != 0; + case TypeCode.UInt64: + return (UInt64)value != 0; + } + // Should never get here + Debug.Assert(false, "Invalid enum detected"); + return default(Boolean); + } + + // *** END GENERATED CODE *** + + #endregion + + #region Generated conversion implementations + + // *** BEGIN GENERATED CODE *** + + /// + /// ConvertToByte Conversion Routine. If no conversion exists, throws TypeError false. Can throw OverflowException. + /// + public static Byte ConvertToByte(object value) { + if (value is Byte) { + return (Byte)value; + } else if (value is Int32) { + return checked((Byte)(Int32)value); + } else if (value is Boolean) { + return (Boolean)value ? (Byte)1 : (Byte)0; + } else if (value is BigInteger) { + UInt32 UInt32Value = ((BigInteger)value).ToUInt32(); + return checked((Byte)UInt32Value); + } else if (value is Extensible) { + return checked((Byte)(Int32)((Extensible)value).Value); + } else if (value is Extensible) { + UInt32 UInt32Value = ((BigInteger)((Extensible)value).Value).ToUInt32(); + return checked((Byte)UInt32Value); + } else if (value is Int64) { + return checked((Byte)(Int64)value); + } else if (value is SByte) { + return checked((Byte)(SByte)value); + } else if (value is Int16) { + return checked((Byte)(Int16)value); + } else if (value is UInt16) { + return checked((Byte)(UInt16)value); + } else if (value is UInt32) { + return checked((Byte)(UInt32)value); + } else if (value is UInt64) { + return checked((Byte)(UInt64)value); + } else if (value is Decimal) { + return checked((Byte)(Decimal)value); + } + + Object result; + if(TryConvertObject(value, typeof(Byte), out result) && result is Byte) { + return (Byte)result; + } + + throw CannotConvertTo("Byte", value); + } + + /// + /// ConvertToSByte Conversion Routine. If no conversion exists, throws TypeError false. Can throw OverflowException. + /// + [CLSCompliant(false)] + public static SByte ConvertToSByte(object value) { + if (value is SByte) { + return (SByte)value; + } else if (value is Int32) { + return checked((SByte)(Int32)value); + } else if (value is Boolean) { + return (Boolean)value ? (SByte)1 : (SByte)0; + } else if (value is BigInteger) { + Int32 Int32Value = ((BigInteger)value).ToInt32(); + return checked((SByte)Int32Value); + } else if (value is Extensible) { + return checked((SByte)(Int32)((Extensible)value).Value); + } else if (value is Extensible) { + Int32 Int32Value = ((BigInteger)((Extensible)value).Value).ToInt32(); + return checked((SByte)Int32Value); + } else if (value is Int64) { + return checked((SByte)(Int64)value); + } else if (value is Byte) { + return checked((SByte)(Byte)value); + } else if (value is Int16) { + return checked((SByte)(Int16)value); + } else if (value is UInt16) { + return checked((SByte)(UInt16)value); + } else if (value is UInt32) { + return checked((SByte)(UInt32)value); + } else if (value is UInt64) { + return checked((SByte)(UInt64)value); + } else if (value is Decimal) { + return checked((SByte)(Decimal)value); + } + + Object result; + if(TryConvertObject(value, typeof(SByte), out result) && result is SByte) { + return (SByte)result; + } + + throw CannotConvertTo("SByte", value); + } + /// + /// ConvertToInt16 Conversion Routine. If no conversion exists, throws TypeError false. Can throw OverflowException. + /// + public static Int16 ConvertToInt16(object value) { + if (value is Int16) { + return (Int16)value; + } else if (value is Int32) { + return checked((Int16)(Int32)value); + } else if (value is Boolean) { + return (Boolean)value ? (Int16)1 : (Int16)0; + } else if (value is BigInteger) { + Int32 Int32Value = ((BigInteger)value).ToInt32(); + return checked((Int16)Int32Value); + } else if (value is Extensible) { + return checked((Int16)(Int32)((Extensible)value).Value); + } else if (value is Extensible) { + Int32 Int32Value = ((BigInteger)((Extensible)value).Value).ToInt32(); + return checked((Int16)Int32Value); + } else if (value is Int64) { + return checked((Int16)(Int64)value); + } else if (value is Byte) { + return (Int16)(Byte)value; + } else if (value is SByte) { + return (Int16)(SByte)value; + } else if (value is UInt16) { + return checked((Int16)(UInt16)value); + } else if (value is UInt32) { + return checked((Int16)(UInt32)value); + } else if (value is UInt64) { + return checked((Int16)(UInt64)value); + } else if (value is Decimal) { + return checked((Int16)(Decimal)value); + } + + Object result; + if(TryConvertObject(value, typeof(Int16), out result) && result is Int16) { + return (Int16)result; + } + + throw CannotConvertTo("Int16", value); + } + + /// + /// ConvertToUInt16 Conversion Routine. If no conversion exists, throws TypeError false. Can throw OverflowException. + /// + [CLSCompliant(false)] + public static UInt16 ConvertToUInt16(object value) { + if (value is UInt16) { + return (UInt16)value; + } else if (value is Int32) { + return checked((UInt16)(Int32)value); + } else if (value is Boolean) { + return (Boolean)value ? (UInt16)1 : (UInt16)0; + } else if (value is BigInteger) { + UInt32 UInt32Value = ((BigInteger)value).ToUInt32(); + return checked((UInt16)UInt32Value); + } else if (value is Extensible) { + return checked((UInt16)(Int32)((Extensible)value).Value); + } else if (value is Extensible) { + UInt32 UInt32Value = ((BigInteger)((Extensible)value).Value).ToUInt32(); + return checked((UInt16)UInt32Value); + } else if (value is Int64) { + return checked((UInt16)(Int64)value); + } else if (value is Byte) { + return (UInt16)(Byte)value; + } else if (value is SByte) { + return checked((UInt16)(SByte)value); + } else if (value is Int16) { + return checked((UInt16)(Int16)value); + } else if (value is UInt32) { + return checked((UInt16)(UInt32)value); + } else if (value is UInt64) { + return checked((UInt16)(UInt64)value); + } else if (value is Decimal) { + return checked((UInt16)(Decimal)value); + } + + Object result; + if(TryConvertObject(value, typeof(UInt16), out result) && result is UInt16) { + return (UInt16)result; + } + + throw CannotConvertTo("UInt16", value); + } + /// + /// ConvertToInt32Impl Conversion Routine. If no conversion exists, returns false. Can throw OverflowException. + /// + private static bool ConvertToInt32Impl(object value, out Int32 result) { + if (value is Int32) { + result = (Int32)value; return true; + } else if (value is Boolean) { + result = (Boolean)value ? (Int32)1 : (Int32)0; return true; + } else if (value is BigInteger) { + result = ((BigInteger)value).ToInt32(); return true; + } else if (value is Extensible) { + result = (Int32)(Int32)((Extensible)value).Value; return true; + } else if (value is Extensible) { + result = ((BigInteger)((Extensible)value).Value).ToInt32(); return true; + } else if (value is Int64) { + result = checked((Int32)(Int64)value); return true; + } else if (value is Byte) { + result = (Int32)(Byte)value; return true; + } else if (value is SByte) { + result = (Int32)(SByte)value; return true; + } else if (value is Int16) { + result = (Int32)(Int16)value; return true; + } else if (value is UInt16) { + result = (Int32)(UInt16)value; return true; + } else if (value is UInt32) { + result = checked((Int32)(UInt32)value); return true; + } else if (value is UInt64) { + result = checked((Int32)(UInt64)value); return true; + } else if (value is Decimal) { + result = checked((Int32)(Decimal)value); return true; + } + result = default(Int32); + return false; + } + + /// + /// ConvertToUInt32 Conversion Routine. If no conversion exists, throws TypeError false. Can throw OverflowException. + /// + [CLSCompliant(false)] + public static UInt32 ConvertToUInt32(object value) { + if (value is UInt32) { + return (UInt32)value; + } else if (value is Int32) { + return checked((UInt32)(Int32)value); + } else if (value is Boolean) { + return (Boolean)value ? (UInt32)1 : (UInt32)0; + } else if (value is BigInteger) { + return ((BigInteger)value).ToUInt32(); + } else if (value is Extensible) { + return checked((UInt32)(Int32)((Extensible)value).Value); + } else if (value is Extensible) { + return ((BigInteger)((Extensible)value).Value).ToUInt32(); + } else if (value is Int64) { + return checked((UInt32)(Int64)value); + } else if (value is Byte) { + return (UInt32)(Byte)value; + } else if (value is SByte) { + return checked((UInt32)(SByte)value); + } else if (value is Int16) { + return checked((UInt32)(Int16)value); + } else if (value is UInt16) { + return (UInt32)(UInt16)value; + } else if (value is UInt64) { + return checked((UInt32)(UInt64)value); + } else if (value is Decimal) { + return checked((UInt32)(Decimal)value); + } + + Object result; + if(TryConvertObject(value, typeof(UInt32), out result) && result is UInt32) { + return (UInt32)result; + } + + throw CannotConvertTo("UInt32", value); + } + /// + /// ConvertToInt64 Conversion Routine. If no conversion exists, throws TypeError false. Can throw OverflowException. + /// + public static Int64 ConvertToInt64(object value) { + if (value is Int64) { + return (Int64)value; + } else if (value is Int32) { + return (Int64)(Int32)value; + } else if (value is Boolean) { + return (Boolean)value ? (Int64)1 : (Int64)0; + } else if (value is BigInteger) { + return ((BigInteger)value).ToInt64(); + } else if (value is Extensible) { + return (Int64)(Int32)((Extensible)value).Value; + } else if (value is Extensible) { + return ((BigInteger)((Extensible)value).Value).ToInt64(); + } else if (value is Byte) { + return (Int64)(Byte)value; + } else if (value is SByte) { + return (Int64)(SByte)value; + } else if (value is Int16) { + return (Int64)(Int16)value; + } else if (value is UInt16) { + return (Int64)(UInt16)value; + } else if (value is UInt32) { + return (Int64)(UInt32)value; + } else if (value is UInt64) { + return checked((Int64)(UInt64)value); + } else if (value is Decimal) { + return checked((Int64)(Decimal)value); + } + + Object result; + if(TryConvertObject(value, typeof(Int64), out result) && result is Int64) { + return (Int64)result; + } + + throw CannotConvertTo("Int64", value); + } + /// + /// ConvertToUInt64 Conversion Routine. If no conversion exists, throws TypeError false. Can throw OverflowException. + /// + [CLSCompliant(false)] + public static UInt64 ConvertToUInt64(object value) { + if (value is UInt64) { + return (UInt64)value; + } else if (value is Int32) { + return checked((UInt64)(Int32)value); + } else if (value is Boolean) { + return (Boolean)value ? (UInt64)1 : (UInt64)0; + } else if (value is BigInteger) { + return ((BigInteger)value).ToUInt64(); + } else if (value is Extensible) { + return checked((UInt64)(Int32)((Extensible)value).Value); + } else if (value is Extensible) { + return ((BigInteger)((Extensible)value).Value).ToUInt64(); + } else if (value is Int64) { + return checked((UInt64)(Int64)value); + } else if (value is Byte) { + return (UInt64)(Byte)value; + } else if (value is SByte) { + return checked((UInt64)(SByte)value); + } else if (value is Int16) { + return checked((UInt64)(Int16)value); + } else if (value is UInt16) { + return (UInt64)(UInt16)value; + } else if (value is UInt32) { + return (UInt64)(UInt32)value; + } else if (value is Decimal) { + return checked((UInt64)(Decimal)value); + } + + Object result; + if(TryConvertObject(value, typeof(UInt64), out result) && result is UInt64) { + return (UInt64)result; + } + + throw CannotConvertTo("UInt64", value); + } + /// + /// ConvertToSingle Conversion Routine. If no conversion exists, throws TypeError false. Can throw OverflowException. + /// + public static Single ConvertToSingle(object value) { + if (value is Single) { + return (Single)value; + } else if (value is Int32) { + return (Single)(Int32)value; + } else if (value is Boolean) { + return (Boolean)value ? (Single)1 : (Single)0; + } else if (value is BigInteger) { + Single SingleValue = checked((Single)((BigInteger)value).ToFloat64()); + if (Single.IsInfinity(SingleValue)) { + //throw PythonOps.OverflowError("{0} won't fit into Single", value); + } + return SingleValue; + } else if (value is Double) { + Single SingleValue = checked((Single)(Double)value); + if (Single.IsInfinity(SingleValue)) { + //throw PythonOps.OverflowError("{0} won't fit into Single", value); + } + return SingleValue; + } else if (value is Extensible) { + return (Single)(Int32)((Extensible)value).Value; + } else if (value is Extensible) { + Single SingleValue = checked((Single)((BigInteger)((Extensible)value).Value).ToFloat64()); + if (Single.IsInfinity(SingleValue)) { + //throw PythonOps.OverflowError("{0} won't fit into Single", ((Extensible)value).Value); + } + return SingleValue; + } else if (value is Extensible) { + Single SingleValue = checked((Single)(Double)((Extensible)value).Value); + if (Single.IsInfinity(SingleValue)) { + //throw PythonOps.OverflowError("{0} won't fit into Single", ((Extensible)value).Value); + } + return SingleValue; + } else if (value is Int64) { + return (Single)(Int64)value; + } else if (value is Byte) { + return (Single)(Byte)value; + } else if (value is SByte) { + return (Single)(SByte)value; + } else if (value is Int16) { + return (Single)(Int16)value; + } else if (value is UInt16) { + return (Single)(UInt16)value; + } else if (value is UInt32) { + return (Single)(UInt32)value; + } else if (value is UInt64) { + return (Single)(UInt64)value; + } else if (value is Decimal) { + return (Single)(Decimal)value; + } + + Object result; + if(TryConvertObject(value, typeof(Single), out result) && result is Single) { + return (Single)result; + } + + throw CannotConvertTo("Single", value); + } + /// + /// ConvertToDoubleImpl Conversion Routine. If no conversion exists, returns false. Can throw OverflowException. + /// + private static bool ConvertToDoubleImpl(object value, out Double result) { + if (value is Double) { + result = (Double)value; return true; + } else if (value is Int32) { + result = (Double)(Int32)value; return true; + } else if (value is Boolean) { + result = (Boolean)value ? (Double)1 : (Double)0; return true; + } else if (value is BigInteger) { + result = ((BigInteger)value).ToFloat64(); return true; + } else if (value is Extensible) { + result = (Double)(Int32)((Extensible)value).Value; return true; + } else if (value is Extensible) { + result = ((BigInteger)((Extensible)value).Value).ToFloat64(); return true; + } else if (value is Extensible) { + result = (Double)(Double)((Extensible)value).Value; return true; + } else if (value is Int64) { + result = (Double)(Int64)value; return true; + } else if (value is Byte) { + result = (Double)(Byte)value; return true; + } else if (value is SByte) { + result = (Double)(SByte)value; return true; + } else if (value is Int16) { + result = (Double)(Int16)value; return true; + } else if (value is UInt16) { + result = (Double)(UInt16)value; return true; + } else if (value is UInt32) { + result = (Double)(UInt32)value; return true; + } else if (value is UInt64) { + result = (Double)(UInt64)value; return true; + } else if (value is Single) { + result = (Double)(Single)value; return true; + } else if (value is Decimal) { + result = (Double)(Decimal)value; return true; + } + result = default(Double); + return false; + } + /// + /// ConvertToDecimal Conversion Routine. If no conversion exists, throws TypeError false. Can throw OverflowException. + /// + public static Decimal ConvertToDecimal(object value) { + if (value is Decimal) { + return (Decimal)value; + } else if (value is Int32) { + return (Decimal)(Int32)value; + } else if (value is Boolean) { + return (Boolean)value ? (Decimal)1 : (Decimal)0; + } else if (value is BigInteger) { + return ((BigInteger)value).ToDecimal(); + } else if (value is Double) { + return checked((Decimal)(Double)value); + } else if (value is Extensible) { + return (Decimal)(Int32)((Extensible)value).Value; + } else if (value is Extensible) { + return ((BigInteger)((Extensible)value).Value).ToDecimal(); + } else if (value is Extensible) { + return checked((Decimal)(Double)((Extensible)value).Value); + } else if (value is Int64) { + return (Decimal)(Int64)value; + } else if (value is Byte) { + return (Decimal)(Byte)value; + } else if (value is SByte) { + return (Decimal)(SByte)value; + } else if (value is Int16) { + return (Decimal)(Int16)value; + } else if (value is UInt16) { + return (Decimal)(UInt16)value; + } else if (value is UInt32) { + return (Decimal)(UInt32)value; + } else if (value is UInt64) { + return (Decimal)(UInt64)value; + } else if (value is Single) { + return checked((Decimal)(Single)value); + } + + Object result; + if(TryConvertObject(value, typeof(Decimal), out result) && result is Decimal) { + return (Decimal)result; + } + + throw CannotConvertTo("Decimal", value); + } + /// + /// ConvertToBigIntegerImpl Conversion Routine. If no conversion exists, returns false. Can throw OverflowException. + /// + private static bool ConvertToBigIntegerImpl(object value, out BigInteger result) { + if (value is BigInteger) { + result = (BigInteger)value; return true; + } else if (value is Int32) { + result = (BigInteger)(Int32)value; return true; + } else if (value is Boolean) { + result = (Boolean)value ? BigInteger.One : BigInteger.Zero; return true; + } else if (value is Extensible) { + result = (BigInteger)(Int32)((Extensible)value).Value; return true; + } else if (value is Extensible) { + result = (BigInteger)((Extensible)value).Value; return true; + } else if (value is Int64) { + result = (BigInteger)(Int64)value; return true; + } else if (value is Byte) { + result = (BigInteger)(Byte)value; return true; + } else if (value is SByte) { + result = (BigInteger)(SByte)value; return true; + } else if (value is Int16) { + result = (BigInteger)(Int16)value; return true; + } else if (value is UInt16) { + result = (BigInteger)(UInt16)value; return true; + } else if (value is UInt32) { + result = (BigInteger)(UInt32)value; return true; + } else if (value is UInt64) { + result = (BigInteger)(UInt64)value; return true; + } else if (value is Decimal) { + result = (BigInteger)(Decimal)value; return true; + } else if (value == null) { + result = null; return true; + } + result = default(BigInteger); + return false; + } + + // *** END GENERATED CODE *** + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Converter.cs b/merlin/main/languages/ruby/Ruby/Runtime/Converter.cs new file mode 100644 index 0000000000..377428ec79 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Converter.cs @@ -0,0 +1,1113 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.ComponentModel; +using System.Reflection; +using System.Runtime.CompilerServices; +using IronRuby.Builtins; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using RuntimeHelpers = Microsoft.Scripting.Runtime.ScriptingRuntimeHelpers; + +namespace IronRuby.Runtime { + + public static partial class Converter { + #region Conversion entry points + + // + // ConvertToInt32 - fast paths and custom logic + // + public static Int32 ConvertToInt32(object value) { + // Fast Paths + Extensible ei; + BigInteger bi; + if (value is Int32) return (Int32)value; + if ((ei = value as Extensible) != null) return ei.Value; + if (value is Boolean) return ((Boolean)value) ? 1 : 0; + if ((Object)(bi = value as BigInteger) != null) return bi.ToInt32(); + + // Fall back to comprehensive conversion + Int32 result; + if (ConvertToInt32Impl(value, out result)) return result; + + if (value is double) return checked((int)((double)value)); + + // Fall back to __xxx__ method call + object newValue; + //if(PythonOps.TryInvokeOperator(DefaultContext.Default, + // Operators.ConvertToInt32, + // value, + // out newValue)) { + // // Convert resulting object to the desired type + // if (ConvertToInt32Impl(newValue, out result)) return result; + //} + + if (TryConvertObject(value, typeof(Int32), out newValue) && newValue is Int32) return (Int32)newValue; + + throw CannotConvertTo("Int32", value); + } + + // + // ConvertToDouble - fast paths and custom logic + // + public static Double ConvertToDouble(object value) { + // Fast Paths + Extensible ei; + Extensible ef; + if (value is Double) return (Double)value; + if (value is Int32) return (Double)(Int32)value; + if ((ef = value as Extensible) != null) return ef.Value; + if ((ei = value as Extensible) != null) return ei.Value; + + // Fall back to comprehensive conversion + Double result; + if (ConvertToDoubleImpl(value, out result)) return result; + + // Fall back to __xxx__ method call + object newValue; + //if(PythonOps.TryInvokeOperator(DefaultContext.Default, + // Operators.ConvertToDouble, + // value, + // out newValue)) { + + // // Convert resulting object to the desired type + // if (ConvertToDoubleImpl(newValue, out result)) return result; + //} + + if (TryConvertObject(value, typeof(Double), out newValue) && newValue is Double) return (Double)newValue; + + throw CannotConvertTo("Double", value); + } + + // + // ConvertToBigInteger - fast paths and custom logic + // + public static BigInteger ConvertToBigInteger(object value) { + // Fast Paths + BigInteger bi; + Extensible el; + + if ((Object)(bi = value as BigInteger) != null) return bi; + if (value is Int32) return BigInteger.Create((Int32)value); + if ((el = value as Extensible) != null) return el.Value; + if (value == null) return null; + + // Fall back to comprehensive conversion + BigInteger result; + if (ConvertToBigIntegerImpl(value, out result)) return result; + + // Fall back to __xxx__ method call + object newValue; + //if(PythonOps.TryInvokeOperator(DefaultContext.Default, + // Operators.ConvertToBigInteger, + // value, + // out newValue)) { + // // Convert resulting object to the desired type + // if (ConvertToBigIntegerImpl(newValue, out result)) return result; + //} + + if (TryConvertObject(value, typeof(BigInteger), out newValue) && newValue is BigInteger) return (BigInteger)newValue; + + throw CannotConvertTo("BigInteger", value); + } + + // + // ConvertToComplex64 - fast paths and custom logic + // + public static Complex64 ConvertToComplex64(object value) { + // Fast Paths + if (value is Complex64) return (Complex64)value; + if (value is Double) return Complex64.MakeReal((Double)value); + + // Fall back to comprehensive conversion + Complex64 result; + if (ConvertToComplex64Impl(value, out result)) return result; + + // Fall back to __xxx__ method call + object newValue; + //if (PythonOps.TryInvokeOperator(DefaultContext.Default, + // Operators.ConvertToComplex, + // value, + // out newValue)) { // Convert resulting object to the desired type + // if (ConvertToComplex64Impl(newValue, out result)) return result; + //} + + // Try converting to double and use it as a real part of the complex number + Double dresult; + if (ConvertToDoubleImpl(value, out dresult)) return new Complex64(dresult); + + // Fall back to __xxx__ method call + //if (PythonOps.TryInvokeOperator(DefaultContext.Default, + // Operators.ConvertToDouble, + // value, + // out newValue)) { + + // if (newValue is double) { + // dresult = (double)newValue; + // } else if (newValue is Extensible) { + // dresult = ((Extensible)newValue).Value; + // } else { + // throw RubyExceptions.CreateTypeError("__float__ returned non-float"); + // } + // return new Complex64(dresult); + //} + + if (TryConvertObject(value, typeof(Complex64), out newValue) && newValue is Complex64) return (Complex64)newValue; + + throw CannotConvertTo("Complex64", value); + } + + // + // ConvertToString - fast paths and custom logic + // + public static String ConvertToString(object value) { + // Fast Paths + Object res; + String result; + + if ((result = value as String) != null) return result; + if (value == null) return null; + if (value is Char) return RuntimeHelpers.CharToString((Char)value); + if (TryConvertObject(value, typeof(String), out res) && res is String) return (String)res; + + throw CannotConvertTo("String", value); + } + + // + // ConvertToChar - fast paths and custom logic + // + public static Char ConvertToChar(object value) { + // Fast Paths + Object res; + string str; + + if (value is Char) return (Char)value; + if ((object)(str = value as string) != null && str.Length == 1) return str[0]; + if (TryConvertObject(value, typeof(Char), out res) && res is Char) return (Char)res; + + throw CannotConvertTo("Char", value); + } + + // + // ConvertToBoolean - fast paths and custom logic + // + public static Boolean ConvertToBoolean(object value) { + // Fast Paths + if (value is Int32) return (Int32)value != 0; + if (value is Boolean) return (Boolean)value; + if (value == null) return false; + + return SlowConvertToBoolean(value); + } + + private static bool SlowConvertToBoolean(object value) { + Boolean result; + + // Fall back to comprehensive conversion + if (ConvertToBooleanImpl(value, out result)) return result; + + // Additional logic to convert to bool + if (value == null) return false; + if (value is ICollection) return ((ICollection)value).Count != 0; + + // Explictly block conversion of References to bool + if (value is IStrongBox) { + throw RuntimeHelpers.SimpleTypeError("Can't convert a Reference<> instance to a bool"); + } + + // Fall back to __xxx__ method call + object newValue; + + // First, try __nonzero__ + //if(PythonOps.TryInvokeOperator(DefaultContext.Default, + // Operators.ConvertToBoolean, + // value, + // out newValue)) { + // // Convert resulting object to the desired type + // if (newValue is bool || newValue is Int32) { + // if (ConvertToBooleanImpl(newValue, out result)) return result; + // } + // throw RubyExceptions.CreateTypeError("__nonzero__ should return bool or int, returned {0}", PythonOps.GetClassName(newValue)); + //} + + // Then, try __len__ + //try { + // if(PythonOps.TryInvokeOperator(DefaultContext.Default, + // Operators.Length, + // value, + // out newValue)) { + // // Convert resulting object to the desired type + // if (newValue is Int32 || newValue is BigInteger) { + // if (ConvertToBooleanImpl(newValue, out result)) return result; + // } + // throw RubyExceptions.CreateTypeError("an integer is required"); + // } + //} catch (MissingMemberException) { + // // old-style __len__ throws if we don't have __len__ defined on the instance + //} + + // Try Extensible types as last due to possible __nonzero__ overload + if (value is Extensible) return (Int32)((Extensible)value).Value != (Int32)0; + if (value is Extensible) return ((Extensible)value).Value != BigInteger.Zero; + if (value is Extensible) return ((Extensible)value).Value != (Double)0; + + if (TryConvertObject(value, typeof(bool), out newValue) && newValue is Boolean) return (bool)newValue; + + // Non-null value is true + result = true; + return true; + } + + #endregion + + public static Char ExplicitConvertToChar(object value) { + string str; + if (value is Char) return (Char)value; + if (value is Int32) return checked((Char)(Int32)value); + if ((Object)(str = value as string) != null && str.Length == 1) return str[0]; + if (value is SByte) return checked((Char)(SByte)value); + if (value is Int16) return checked((Char)(Int16)value); + if (value is UInt32) return checked((Char)(UInt32)value); + if (value is UInt64) return checked((Char)(UInt64)value); + if (value is Decimal) return checked((Char)(Decimal)value); + if (value is Int64) return checked((Char)(Int64)value); + if (value is Byte) return (Char)(Byte)value; + if (value is UInt16) return checked((Char)(UInt16)value); + + throw CannotConvertTo("char", value); + } + + public static T Convert(object value) { + return (T)Convert(value, typeof(T)); + } + + /// + /// General conversion routine TryConvert - tries to convert the object to the desired type. + /// Try to avoid using this method, the goal is to ultimately remove it! + /// + internal static bool TryConvert(object value, Type to, out object result) { + try { + result = Convert(value, to); + return true; + } catch { + result = default(object); + return false; + } + } + + internal static object Convert(object value, Type to) { + if (value == null) { + if (to == typeof(bool)) return RuntimeHelpers.False; + + if (to.IsValueType && + (!to.IsGenericType || to.GetGenericTypeDefinition() != typeof(Nullable<>))) { + + throw MakeTypeError(to, value); + } + return null; + } + + Type from = value.GetType(); + if (from == to || to == ObjectType) return value; + if (to.IsInstanceOfType(value)) return value; + + if (to == TypeType) return ConvertToType(value); + if (to == Int32Type) return ConvertToInt32(value); + if (to == DoubleType) return ConvertToDouble(value); + if (to == BooleanType) return ConvertToBoolean(value); + + if (to == CharType) return ConvertToChar(value); + if (to == StringType) return ConvertToString(value); + + if (to == BigIntegerType) return ConvertToBigInteger(value); + if (to == Complex64Type) return ConvertToComplex64(value); + + if (to == ByteType) return ConvertToByte(value); + if (to == SByteType) return ConvertToSByte(value); + if (to == Int16Type) return ConvertToInt16(value); + if (to == UInt32Type) return ConvertToUInt32(value); + if (to == UInt64Type) return ConvertToUInt64(value); + if (to == UInt16Type) return ConvertToUInt16(value); + if (to == SingleType) return ConvertToSingle(value); + if (to == Int64Type) return ConvertToInt64(value); + if (to == DecimalType) return ConvertToDecimal(value); + + if (to == IEnumerableType) return ConvertToIEnumerable(value); + + if (DelegateType.IsAssignableFrom(to)) return ConvertToDelegate(value, to); + + if (to.IsArray) return ConvertToArray(value, to); + + Object result; + if (TrySlowConvert(value, to, out result)) return result; + + throw MakeTypeError(to, value); + } + + internal static bool TrySlowConvert(object value, Type to, out object result) { + // check for implicit conversions + if(CompilerHelpers.TryImplicitConversion(value, to, out result)) { + return true; + } + + if (to.IsGenericType) { + Type genTo = to.GetGenericTypeDefinition(); + if (genTo == NullableOfTType) { + result = ConvertToNullableT(value, to.GetGenericArguments()); + return true; + } + + if (genTo == IListOfTType) { + result = ConvertToIListT(value, to.GetGenericArguments()); + return true; + } + + if (genTo == IDictOfTType) { + result = ConvertToIDictT(value, to.GetGenericArguments()); + return true; + } + + if (genTo == IEnumerableOfTType) { + result = ConvertToIEnumerableT(value, to.GetGenericArguments()); + return true; + } + } + + if (value.GetType().IsValueType) { + if (to == ValueTypeType) { + result = (System.ValueType)value; + return true; + } + } + +#if !SILVERLIGHT + if (value != null) { + // try available type conversions... + object[] tcas = to.GetCustomAttributes(typeof(TypeConverterAttribute), true); + foreach (TypeConverterAttribute tca in tcas) { + TypeConverter tc = GetTypeConverter(tca); + + if (tc == null) continue; + + if (tc.CanConvertFrom(value.GetType())) { + result = tc.ConvertFrom(value); + return true; + } + } + } +#endif + + result = null; + return false; + } + + internal static bool TryConvertObject(object value, Type to, out object result) { + //This is the fallback call for every fast path converter. If we land here, + //then 'value' has to be a reference type which might have a custom converter + //defined on its dynamic type. (Value Type conversions if any would have + //already taken place during the fast conversions and should not occur through + //the dynamic types). + + if (value == null || value.GetType().IsValueType) { + result = null; + return false; + } + + return TrySlowConvert(value, to, out result); + } + + // TODO: Make internal once JS has its own converter + /// + /// This function tries to convert an object to IEnumerator, or wraps it into an adapter + /// Do not use this function directly. It is only meant to be used by Ops.GetEnumerator. + /// + public static bool TryConvertToIEnumerator(object o, out IEnumerator e) { + //if (o is string) { + // e = StringOps.GetEnumerator((string)o); + // return true; + //} else + if (o is IEnumerable) { + e = ((IEnumerable)o).GetEnumerator(); + return true; + } else if (o is IEnumerator) { + e = (IEnumerator)o; + return true; + } + + //if (PythonEnumerator.TryCreate(o, out e)) { + // return true; + //} + //if (ItemEnumerator.TryCreate(o, out e)) { + // return true; + //} + e = null; + return false; + } + + public static IEnumerable ConvertToIEnumerable(object o) { + if (o == null) return null; + + IEnumerable e = o as IEnumerable; + if (e != null) return e; + + //PythonEnumerable pe; + //if (PythonEnumerable.TryCreate(o, out pe)) { + // return pe; + //} + + //ItemEnumerable ie; + //if (ItemEnumerable.TryCreate(o, out ie)) { + // return ie; + //} + + throw MakeTypeError("IEnumerable", o); + } + + public static object ConvertToIEnumerableT(object value, Type[] enumOf) { + //Type type = IEnumerableOfTType.MakeGenericType(enumOf); + //if (type.IsInstanceOfType(value)) { + // return value; + //} + + //IEnumerable ie = value as IEnumerable; + //if (ie == null) { + // ie = ConvertToIEnumerable(value); + //} + + ////type = IEnumerableOfTWrapperType.MakeGenericType(enumOf); + //object res = Activator.CreateInstance(type, ie); + //return res; + throw new NotSupportedException(); + } + + private static object ConvertToArray(object value, Type to) { + int rank = to.GetArrayRank(); + + //if (rank == 1) { + // Tuple tupleVal = value as Tuple; + // if (tupleVal != null) { + // Type elemType = to.GetElementType(); + // Array ret = Array.CreateInstance(elemType, tupleVal.Count); + // try { + // tupleVal.CopyTo(ret, 0); + // return ret; + // } catch (InvalidCastException) { + // // invalid conversion + // for (int i = 0; i < tupleVal.Count; i++) { + // ret.SetValue(Convert(tupleVal[i], elemType), i); + // } + // return ret; + // } + // } + //} + + throw MakeTypeError("Array", value); + } + + internal static int ConvertToSliceIndex(object value) { + int val; + if (TryConvertToInt32(value, out val)) + return val; + + BigInteger bigval; + if (TryConvertToBigInteger(value, out bigval)) { + return bigval > 0 ? Int32.MaxValue : Int32.MinValue; + } + + throw RubyExceptions.CreateTypeError("slice indices must be integers"); + } + + internal static Exception CannotConvertTo(string name, object value) { + return RubyExceptions.CreateTypeError(String.Format("Cannot convert {0}({1}) to {2}", CompilerHelpers.GetType(value).Name, value, name)); + } + + private static Exception MakeTypeError(Type expectedType, object o) { + return MakeTypeError(expectedType.Name.ToString(), o); + } + + private static Exception MakeTypeError(string expectedType, object o) { + return RubyExceptions.CreateTypeError(String.Format("Object '{1}' of type '{2}' cannot be converted to type '{0}'", expectedType, + o ?? "nil", o != null ? o.GetType().Name : "NilClass")); + } + + #region Cached Type instances + + private static readonly Type Int16Type = typeof(System.Int16); + private static readonly Type SByteType = typeof(System.SByte); + private static readonly Type StringType = typeof(System.String); + private static readonly Type UInt64Type = typeof(System.UInt64); + private static readonly Type Int32Type = typeof(System.Int32); + private static readonly Type DoubleType = typeof(System.Double); + private static readonly Type DecimalType = typeof(System.Decimal); + private static readonly Type ObjectType = typeof(System.Object); + private static readonly Type Int64Type = typeof(System.Int64); + private static readonly Type CharType = typeof(System.Char); + private static readonly Type SingleType = typeof(System.Single); + private static readonly Type BooleanType = typeof(System.Boolean); + private static readonly Type UInt16Type = typeof(System.UInt16); + private static readonly Type UInt32Type = typeof(System.UInt32); + private static readonly Type ByteType = typeof(System.Byte); + private static readonly Type BigIntegerType = typeof(BigInteger); + private static readonly Type Complex64Type = typeof(Complex64); + private static readonly Type DelegateType = typeof(Delegate); + private static readonly Type IEnumerableType = typeof(IEnumerable); + private static readonly Type ValueTypeType = typeof(ValueType); + private static readonly Type TypeType = typeof(Type); + private static readonly Type NullableOfTType = typeof(Nullable<>); + private static readonly Type IListOfTType = typeof(System.Collections.Generic.IList<>); + private static readonly Type IDictOfTType = typeof(System.Collections.Generic.IDictionary<,>); + private static readonly Type IEnumerableOfTType = typeof(System.Collections.Generic.IEnumerable<>); + private static readonly Type IListOfObjectType = typeof(System.Collections.Generic.IList); + private static readonly Type IDictionaryOfObjectType = typeof(System.Collections.Generic.IDictionary); + + #endregion + + #region Implementation routines + // + // ConvertToBooleanImpl Conversion Routine + // + private static bool ConvertToBooleanImpl(object value, out Boolean result) { + if (value is Boolean) { + result = (Boolean)value; + return true; + } else if (value is Int32) { + result = (Int32)value != (Int32)0; + return true; + } else if (value is Double) { + result = (Double)value != (Double)0; + return true; + } else if (value is BigInteger) { + result = ((BigInteger)value) != BigInteger.Zero; + return true; + } else if (value is String) { + result = ((String)value).Length != 0; + return true; + } else if (value is Complex64) { + result = !((Complex64)value).IsZero; + return true; + } else if (value is Int64) { + result = (Int64)value != (Int64)0; + return true; + } else if (value is Byte) { + result = (Byte)value != (Byte)0; + return true; + } else if (value is SByte) { + result = (SByte)value != (SByte)0; + return true; + } else if (value is Int16) { + result = (Int16)value != (Int16)0; + return true; + } else if (value is UInt16) { + result = (UInt16)value != (UInt16)0; + return true; + } else if (value is UInt32) { + result = (UInt32)value != (UInt32)0; + return true; + } else if (value is UInt64) { + result = (UInt64)value != (UInt64)0; + return true; + } else if (value is Single) { + result = (Single)value != (Single)0; + return true; + } else if (value is Decimal) { + result = (Decimal)value != (Decimal)0; + return true; + } else if (value is Enum) { + return TryConvertEnumToBoolean(value, out result); + } + + result = default(Boolean); + return false; + } + + private static bool TryConvertEnumToBoolean(object value, out bool result) { + switch (((Enum)value).GetTypeCode()) { + case TypeCode.Int32: + result = (int)value != 0; return true; + case TypeCode.Int64: + result = (long)value != 0; return true; + case TypeCode.Int16: + result = (short)value != 0; return true; + case TypeCode.UInt32: + result = (uint)value != 0; return true; + case TypeCode.UInt64: + result = (ulong)value != 0; return true; + case TypeCode.SByte: + result = (sbyte)value != 0; return true; + case TypeCode.UInt16: + result = (ushort)value != 0; return true; + case TypeCode.Byte: + result = (byte)value != 0; return true; + default: + result = default(Boolean); return false; + } + } + + // + // ConvertToComplex64Impl Conversion Routine + // + private static bool ConvertToComplex64Impl(object value, out Complex64 result) { + if (value is Complex64) { + result = (Complex64)value; + return true; + } else if (value is Double) { + result = Complex64.MakeReal((Double)value); + return true; + } else if (value is Extensible) { + result = ((Extensible)value).Value; + return true; + } else { + Double DoubleValue; + if (ConvertToDoubleImpl(value, out DoubleValue)) { + result = Complex64.MakeReal(DoubleValue); + return true; + } + } + result = default(Complex64); + return false; + } + + #endregion + + private static object ConvertToNullableT(object value, Type[] typeOf) { + if (value == null) return null; + else return Convert(value, typeOf[0]); + } + + #region Entry points called from the generated code + + public static object ConvertToReferenceType(object fromObject, RuntimeTypeHandle typeHandle) { + if (fromObject == null) return null; + return Convert(fromObject, Type.GetTypeFromHandle(typeHandle)); + } + + public static object ConvertToNullableType(object fromObject, RuntimeTypeHandle typeHandle) { + if (fromObject == null) return null; + return Convert(fromObject, Type.GetTypeFromHandle(typeHandle)); + } + + public static object ConvertToValueType(object fromObject, RuntimeTypeHandle typeHandle) { + ContractUtils.RequiresNotNull(fromObject, "fromObject"); + //if (fromObject == null) throw PythonOps.InvalidType(fromObject, typeHandle); + return Convert(fromObject, Type.GetTypeFromHandle(typeHandle)); + } + + public static Type ConvertToType(object value) { + if (value == null) return null; + + Type TypeVal = value as Type; + if (TypeVal != null) return TypeVal; + + TypeTracker typeTracker = value as TypeTracker; + if (typeTracker != null) return typeTracker.Type; + + throw MakeTypeError("Type", value); + } + + public static object ConvertToDelegate(object value, Type to) { + if (value == null) return null; + return BinderOps.GetDelegate(RubyContext._Default, value, to); + } + + + #endregion + + private static object ConvertToIListT(object value, Type[] listOf) { + System.Collections.Generic.IList lst = value as System.Collections.Generic.IList; + if (lst != null) { + //Type t = ListGenericWrapperType.MakeGenericType(listOf); + //return Activator.CreateInstance(t, lst); + } + throw MakeTypeError("IList", value); + } + + private static object ConvertToIDictT(object value, Type[] dictOf) { + System.Collections.Generic.IDictionary dict = value as System.Collections.Generic.IDictionary; + if (dict != null) { + //Type t = DictionaryGenericWrapperType.MakeGenericType(dictOf); + //return Activator.CreateInstance(t, dict); + } + throw MakeTypeError("IDictionary", value); + } + + public static bool CanConvertFrom(Type fromType, Type toType, NarrowingLevel allowNarrowing) { + ContractUtils.RequiresNotNull(fromType, "fromType"); + ContractUtils.RequiresNotNull(toType, "toType"); + + if (toType == fromType) return true; + if (toType.IsAssignableFrom(fromType)) return true; + if (fromType.IsCOMObject && toType.IsInterface) return true; // A COM object could be cast to any interface + + if (HasImplicitNumericConversion(fromType, toType)) return true; + + // Handling the hole that Type is the only object that we 'box' + if (toType == TypeType && typeof(TypeTracker).IsAssignableFrom(fromType)) return true; + + // Support extensible types with simple implicit conversions to their base types + if (typeof(Extensible).IsAssignableFrom(fromType) && CanConvertFrom(Int32Type, toType, allowNarrowing)) { + return true; + } + if (typeof(Extensible).IsAssignableFrom(fromType) && CanConvertFrom(BigIntegerType, toType, allowNarrowing)) { + return true; + } + //if (typeof(ExtensibleString).IsAssignableFrom(fromType) && CanConvertFrom(StringType, toType, allowNarrowing)) { + // return true; + //} + if (typeof(Extensible).IsAssignableFrom(fromType) && CanConvertFrom(DoubleType, toType, allowNarrowing)) { + return true; + } + if (typeof(Extensible).IsAssignableFrom(fromType) && CanConvertFrom(Complex64Type, toType, allowNarrowing)) { + return true; + } + + if (typeof(MutableString).IsAssignableFrom(fromType) && toType == typeof(string)) { + return true; + } + + +#if !SILVERLIGHT + // try available type conversions... + object[] tcas = toType.GetCustomAttributes(typeof(TypeConverterAttribute), true); + foreach (TypeConverterAttribute tca in tcas) { + TypeConverter tc = GetTypeConverter(tca); + + if (tc == null) continue; + + if (tc.CanConvertFrom(fromType)) { + return true; + } + } +#endif + + //!!!do user-defined implicit conversions here + + if (allowNarrowing == NarrowingLevel.None) return false; + + return HasNarrowingConversion(fromType, toType, allowNarrowing); + } + +#if !SILVERLIGHT + private static TypeConverter GetTypeConverter(TypeConverterAttribute tca) { + try { + ConstructorInfo ci = Type.GetType(tca.ConverterTypeName).GetConstructor(Type.EmptyTypes); + if (ci != null) return ci.Invoke(ArrayUtils.EmptyObjects) as TypeConverter; + } catch (TargetInvocationException) { + } + return null; + } +#endif + + private static bool HasImplicitNumericConversion(Type fromType, Type toType) { + if (fromType.IsEnum) return false; + + if (fromType == typeof(BigInteger)) { + if (toType == typeof(double)) return true; + if (toType == typeof(float)) return true; + if (toType == typeof(Complex64)) return true; + return false; + } + + if (fromType == typeof(bool)) { + return false; + } + + switch (Type.GetTypeCode(fromType)) { + case TypeCode.SByte: + switch (Type.GetTypeCode(toType)) { + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + default: + if (toType == BigIntegerType) return true; + if (toType == Complex64Type) return true; + return false; + } + case TypeCode.Byte: + switch (Type.GetTypeCode(toType)) { + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + default: + if (toType == BigIntegerType) return true; + if (toType == Complex64Type) return true; + return false; + } + case TypeCode.Int16: + switch (Type.GetTypeCode(toType)) { + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + default: + if (toType == BigIntegerType) return true; + if (toType == Complex64Type) return true; + return false; + } + case TypeCode.UInt16: + switch (Type.GetTypeCode(toType)) { + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + default: + if (toType == BigIntegerType) return true; + if (toType == Complex64Type) return true; + return false; + } + case TypeCode.Int32: + switch (Type.GetTypeCode(toType)) { + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + default: + if (toType == BigIntegerType) return true; + if (toType == Complex64Type) return true; + return false; + } + case TypeCode.UInt32: + switch (Type.GetTypeCode(toType)) { + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + default: + if (toType == BigIntegerType) return true; + if (toType == Complex64Type) return true; + return false; + } + case TypeCode.Int64: + switch (Type.GetTypeCode(toType)) { + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + default: + if (toType == BigIntegerType) return true; + if (toType == Complex64Type) return true; + return false; + } + case TypeCode.UInt64: + switch (Type.GetTypeCode(toType)) { + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + default: + if (toType == BigIntegerType) return true; + if (toType == Complex64Type) return true; + return false; + } + case TypeCode.Char: + switch (Type.GetTypeCode(toType)) { + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + default: + if (toType == BigIntegerType) return true; + if (toType == Complex64Type) return true; + return false; + } + case TypeCode.Single: + switch (Type.GetTypeCode(toType)) { + case TypeCode.Double: + return true; + default: + if (toType == Complex64Type) return true; + return false; + } + case TypeCode.Double: + switch (Type.GetTypeCode(toType)) { + default: + if (toType == Complex64Type) return true; + return false; + } + default: + return false; + } + } + + public static Candidate PreferConvert(Type t1, Type t2) { + if (t1 == typeof(bool) && t2 == typeof(int)) return Candidate.Two; + if (t1 == typeof(Decimal) && t2 == typeof(BigInteger)) return Candidate.Two; + //if (t1 == typeof(int) && t2 == typeof(BigInteger)) return Candidate.Two; + + switch (Type.GetTypeCode(t1)) { + case TypeCode.SByte: + switch (Type.GetTypeCode(t2)) { + case TypeCode.Byte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + return Candidate.Two; + default: + return Candidate.Equivalent; + } + case TypeCode.Int16: + switch (Type.GetTypeCode(t2)) { + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + return Candidate.Two; + default: + return Candidate.Equivalent; + } + case TypeCode.Int32: + switch (Type.GetTypeCode(t2)) { + case TypeCode.UInt32: + case TypeCode.UInt64: + return Candidate.Two; + default: + return Candidate.Equivalent; + } + case TypeCode.Int64: + switch (Type.GetTypeCode(t2)) { + case TypeCode.UInt64: + return Candidate.Two; + default: + return Candidate.Equivalent; + } + } + return Candidate.Equivalent; + } + + private static bool HasNarrowingConversion(Type fromType, Type toType, NarrowingLevel allowNarrowing) { + if (allowNarrowing == NarrowingLevel.All) { + if (toType == CharType && fromType == StringType) return true; + //if (toType == Int32Type && fromType == BigIntegerType) return true; + //if (IsIntegral(fromType) && IsIntegral(toType)) return true; + + //Check if there is an implicit convertor defined on fromType to toType + if (HasImplicitConversion(fromType, toType)) { + return true; + } + + if (IsNumeric(fromType) && IsNumeric(toType)) return true; + + //if (toType.IsArray) { + // return typeof(Tuple).IsAssignableFrom(fromType); + //} + + if (toType == CharType && fromType == StringType) return true; + if (toType == Int32Type && fromType == BooleanType) return true; + + // Everything can convert to Boolean in Python + if (toType == BooleanType) return true; + + //if (DelegateType.IsAssignableFrom(toType) && IsPythonType(fromType)) return true; + //if (IEnumerableType == toType && IsPythonType(fromType)) return true; + + //__int__, __float__, __long__ + //if (toType == Int32Type && HasPythonProtocol(fromType, Symbols.ConvertToInt)) return true; + //if (toType == DoubleType && HasPythonProtocol(fromType, Symbols.ConvertToFloat)) return true; + //if (toType == BigIntegerType && HasPythonProtocol(fromType, Symbols.ConvertToLong)) return true; + } + + if (toType.IsGenericType) { + Type genTo = toType.GetGenericTypeDefinition(); + if (genTo == IListOfTType) { + return IListOfObjectType.IsAssignableFrom(fromType); + } else if (genTo == typeof(System.Collections.Generic.IEnumerator<>)) { + //if (IsPythonType(fromType)) return true; + } else if (genTo == IDictOfTType) { + return IDictionaryOfObjectType.IsAssignableFrom(fromType); + } + } + + if (fromType == BigIntegerType && toType == Int64Type) return true; + + return false; + } + + private static bool HasImplicitConversion(Type fromType, Type toType) { + foreach (MethodInfo method in fromType.GetMethods()) { + if (method.Name == "op_Implicit" && + method.GetParameters()[0].ParameterType == fromType && + method.ReturnType == toType) { + return true; + } + } + return false; + } + + private static bool IsIntegral(Type t) { + switch (Type.GetTypeCode(t)) { + case TypeCode.DateTime: + case TypeCode.DBNull: + case TypeCode.Char: + case TypeCode.Empty: + case TypeCode.String: + case TypeCode.Single: + case TypeCode.Double: + return false; + case TypeCode.Object: + return t == BigIntegerType; + default: + return true; + } + } + + private static bool IsNumeric(Type t) { + if (t.IsEnum) return false; + + switch (Type.GetTypeCode(t)) { + case TypeCode.DateTime: + case TypeCode.DBNull: + case TypeCode.Char: + case TypeCode.Empty: + case TypeCode.String: + case TypeCode.Boolean: + return false; + case TypeCode.Object: + return t == BigIntegerType || t == Complex64Type; + default: + return true; + } + } + + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/CustomTypeDescHelpers.cs b/merlin/main/languages/ruby/Ruby/Runtime/CustomTypeDescHelpers.cs new file mode 100644 index 0000000000..611a830f19 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/CustomTypeDescHelpers.cs @@ -0,0 +1,341 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ICustomTypeDescriptor + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Compiler.Generation; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Runtime { + /// + /// Helper class that all custom type descriptor implementations call for + /// the bulk of their implementation. + /// + public static class CustomTypeDescHelpers { + + #region ICustomTypeDescriptor helper functions + + private static RubyClass/*!*/ GetClass(object self) { + IRubyObject rubyObj = self as IRubyObject; + ContractUtils.RequiresNotNull(rubyObj, "self"); + return rubyObj.Class; + } + + [Emitted] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "self")] + public static AttributeCollection GetAttributes(object self) { + return AttributeCollection.Empty; + } + + [Emitted] + public static string GetClassName(object self) { + return GetClass(self).Name; + } + + [Emitted] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "self")] + public static string GetComponentName(object self) { + return null; + } + + [Emitted] + public static TypeConverter GetConverter(object self) { + return new TypeConv(self); + } + + [Emitted] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "self")] + public static EventDescriptor GetDefaultEvent(object self) { + return null; + } + + [Emitted] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "self")] + public static PropertyDescriptor GetDefaultProperty(object self) { + return null; + } + + [Emitted] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "self"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "editorBaseType")] + public static object GetEditor(object self, Type editorBaseType) { + return null; + } + + [Emitted] + public static EventDescriptorCollection GetEvents(object self, Attribute[] attributes) { + // TODO: update when we support attributes on Ruby types + if (attributes == null || attributes.Length == 0) { + return GetEvents(self); + } + + // you want things w/ attributes? we don't have attributes! + return EventDescriptorCollection.Empty; + } + + [Emitted] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "self")] + public static EventDescriptorCollection GetEvents(object self) { + return EventDescriptorCollection.Empty; + } + + [Emitted] + public static PropertyDescriptorCollection GetProperties(object self) { + return new PropertyDescriptorCollection(GetPropertiesImpl(self, new Attribute[0])); + } + + [Emitted] + public static PropertyDescriptorCollection GetProperties(object self, Attribute[] attributes) { + return new PropertyDescriptorCollection(GetPropertiesImpl(self, attributes)); + } + + static PropertyDescriptor[] GetPropertiesImpl(object self, Attribute[] attributes) { + bool ok = true; + foreach (var attr in attributes) { + if (attr.GetType() != typeof(BrowsableAttribute)) { + ok = false; + break; + } + } + if (!ok) { + return new PropertyDescriptor[0]; + } + + RubyContext context = GetClass(self).Context; + RubyClass immediateClass = context.GetImmediateClassOf(self); + + const int readable = 0x01; + const int writable = 0x02; + + var properties = new Dictionary(); + immediateClass.ForEachMember(true, RubyMethodAttributes.DefaultVisibility, delegate(string/*!*/ name, RubyMemberInfo/*!*/ member) { + int flag = 0; + if (member is RubyAttributeReaderInfo) { + flag = readable; + } else if (member is RubyAttributeWriterInfo) { + flag = writable; + } else if (name == "initialize") { + // Special case; never a property + } else if (member.Arity == 0) { + flag = readable; + } else if (member.Arity == 1 && name.EndsWith("=")) { + flag = writable; + } + if (flag != 0) { + if (flag == writable) { + name = name.Substring(0, name.Length - 1); + } + int oldFlag; + properties.TryGetValue(name, out oldFlag); + properties[name] = oldFlag | flag; + } + }); + + var result = new List(properties.Count); + foreach (var pair in properties) { + if (pair.Value == (readable | writable)) { + result.Add(new RubyPropertyDescriptor(pair.Key, self, immediateClass.GetUnderlyingSystemType())); + } + } + return result.ToArray(); + } + + private static bool ShouldIncludeInstanceMember(string memberName, Attribute[] attributes) { + bool include = true; + foreach (Attribute attr in attributes) { + if (attr.GetType() == typeof(BrowsableAttribute)) { + if (memberName.StartsWith("__") && memberName.EndsWith("__")) { + include = false; + } + } else { + // unknown attribute, Python doesn't support attributes, so we + // say this doesn't have that attribute. + include = false; + } + } + return include; + } + + [Emitted] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "pd")] + public static object GetPropertyOwner(object self, PropertyDescriptor pd) { + return self; + } + + #endregion + +#if EXPOSE_INSTANCE_VARS + class RubyInstanceVariableDescriptor : PropertyDescriptor { + private readonly string _name; + private readonly Type _propertyType; + private readonly Type _componentType; + + internal RubyInstanceVariableDescriptor(string name, Type propertyType, Type componentType) + : base(name, null) { + _name = name; + _propertyType = propertyType; + _componentType = componentType; + } + + public override object GetValue(object component) { + object value; + GetClass(component).Context.TryGetInstanceVariable(component, _name, out value); + return value; + } + public override void SetValue(object component, object value) { + GetClass(component).Context.SetInstanceVariable(component, _name, value); + } + + public override bool CanResetValue(object component) { + return false; + } + + public override Type ComponentType { + get { return _componentType; } + } + + public override bool IsReadOnly { + get { return false; } + } + + public override Type PropertyType { + get { return _propertyType; } + } + + public override void ResetValue(object component) { + } + + public override bool ShouldSerializeValue(object component) { + return false; + } + } +#endif // EXPOSE_INSTANCE_VARS + + class RubyPropertyDescriptor : PropertyDescriptor { + private readonly string/*!*/ _name; + private readonly Type _propertyType; + private readonly Type _componentType; + private readonly CallSite> _getterSite; + private readonly CallSite> _setterSite; + + internal RubyPropertyDescriptor(string name, object testObject, Type componentType) + : base(name, null) { + _name = name; + _componentType = componentType; + + _getterSite = CallSite>.Create(RubySites.InstanceCallAction(_name)); + _setterSite = CallSite>.Create(RubySites.InstanceCallAction(_name + "=")); + + try { + _propertyType = GetValue(testObject).GetType(); + } catch (Exception) { + _propertyType = typeof(object); + } + } + + private static RubyContext/*!*/ GetContext(object obj) { + return GetClass(obj).Context; + } + + public override object GetValue(object obj) { + return _getterSite.Target.Invoke(_getterSite, GetContext(obj), obj); + } + + public override void SetValue(object obj, object value) { + if (_setterSite != null) { + _setterSite.Target.Invoke(_setterSite, GetContext(obj), obj, value); + } + } + + public override bool CanResetValue(object component) { + return false; + } + + public override Type ComponentType { + get { return _componentType; } + } + + public override bool IsReadOnly { + get { return (_setterSite == null); } + } + + public override Type PropertyType { + get { return _propertyType; } + } + + public override void ResetValue(object component) { + } + + public override bool ShouldSerializeValue(object component) { + return false; + } + } + + private class TypeConv : TypeConverter { + object convObj; + + public TypeConv(object self) { + convObj = self; + } + + #region TypeConverter overrides + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + object result; + return Converter.TryConvert(convObj, destinationType, out result); + } + + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + return Converter.CanConvertFrom(sourceType, convObj.GetType(), NarrowingLevel.All); + } + + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { + return Converter.Convert(value, convObj.GetType()); + } + + public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { + return Converter.Convert(convObj, destinationType); + } + + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { + return false; + } + + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + return false; + } + + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { + return false; + } + + public override bool IsValid(ITypeDescriptorContext context, object value) { + object result; + return Converter.TryConvert(value, convObj.GetType(), out result); + } + + #endregion + } + } +} + +#endif diff --git a/merlin/main/languages/ruby/Ruby/Runtime/EqualityComparer.cs b/merlin/main/languages/ruby/Ruby/Runtime/EqualityComparer.cs new file mode 100644 index 0000000000..98e1fc1b22 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/EqualityComparer.cs @@ -0,0 +1,55 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using IronRuby.Builtins; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Runtime { + // Even though Ruby types overload Equals & GetHashCode, we can't use them + // because monkeypatching allows for implementing "hash" and "eql?" on any type + // (including instances of arbitrary .NET types via singleton methods) + // TODO: optimize this by caching hash values? + public class EqualityComparer : IEqualityComparer { + private readonly RubyContext/*!*/ _context; + + private static readonly CallSite>/*!*/ _HashSharedSite = CallSite>.Create( + RubySites.InstanceCallAction("hash")); + + private static readonly CallSite>/*!*/ _EqlSharedSite = CallSite>.Create( + RubySites.InstanceCallAction("eql?", 1)); + + // friend: RubyContext + internal EqualityComparer(RubyContext/*!*/ context) { + Assert.NotNull(context); + _context = context; + } + + bool IEqualityComparer.Equals(object x, object y) { + return x == y || _EqlSharedSite.Target(_EqlSharedSite, _context, x, y); + } + + int IEqualityComparer.GetHashCode(object obj) { + object result = _HashSharedSite.Target(_HashSharedSite, _context, obj); + if (result is int) { + return (int)result; + } + return result.GetHashCode(); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/EvalEntryPointDelegate.cs b/merlin/main/languages/ruby/Ruby/Runtime/EvalEntryPointDelegate.cs new file mode 100644 index 0000000000..7047d57c6c --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/EvalEntryPointDelegate.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using System.Diagnostics; +using Microsoft.Scripting.Ast; +using IronRuby.Runtime.Calls; + +namespace IronRuby.Runtime { + public delegate object EvalEntryPointDelegate( + RubyScope scope, + object self, + RubyModule module, + Proc blockParameter, + RubyMethodInfo method, + RuntimeFlowControl rfc + ); +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/GlobalScopeExtension.cs b/merlin/main/languages/ruby/Ruby/Runtime/GlobalScopeExtension.cs new file mode 100644 index 0000000000..844821a6dd --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/GlobalScopeExtension.cs @@ -0,0 +1,46 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Runtime { + public class GlobalScopeExtension : ScopeExtension { + private RubyContext/*!*/ _context; + private object/*!*/ _mainObject; + private bool _isHosted; + + public RubyContext/*!*/ Context { + get { return _context; } + set { _context = value; } + } + + public object/*!*/ MainObject { + get { return _mainObject; } + } + + public bool IsHosted { + get { return _isHosted; } + } + + public GlobalScopeExtension(RubyContext/*!*/ context, Scope/*!*/ globalScope, object/*!*/ mainObject, bool isHosted) + : base(globalScope) { + Assert.NotNull(context, globalScope, mainObject); + _context = context; + _mainObject = mainObject; + _isHosted = isHosted; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Globals/GlobalVariable.cs b/merlin/main/languages/ruby/Ruby/Runtime/Globals/GlobalVariable.cs new file mode 100644 index 0000000000..2e6e98d4cb --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Globals/GlobalVariable.cs @@ -0,0 +1,50 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting; + +namespace IronRuby.Runtime { + public abstract class GlobalVariable { + protected GlobalVariable() { + } + + /// + /// Whether the variable is listed in the globals_variable result. + /// For hosts and libraries global variables are good way how to associate arbitrary data with execution context. + /// They can use this property to hide such data from user. + /// + public virtual bool IsEnumerated { get { return true; } } + + /// + /// Implements semantics if the defined? keyword. + /// + public virtual bool IsDefined { get { return true; } } + + public abstract object GetValue(RubyContext/*!*/ context, RubyScope scope); + public abstract void SetValue(RubyContext/*!*/ context, RubyScope scope, string/*!*/ name, object value); + + internal Exception/*!*/ ReadOnlyError(string/*!*/ name) { + return RubyExceptions.CreateNameError(String.Format("${0} is a read-only variable", name)); + } + + internal T RequireType(object value, string/*!*/ variableName, string/*!*/ typeName) { + if (!(value is T)) { + throw RubyExceptions.CreateTypeError(String.Format("Value of ${0} must be {1}", variableName, typeName)); + } + return (T)value; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Globals/GlobalVariableInfo.cs b/merlin/main/languages/ruby/Ruby/Runtime/Globals/GlobalVariableInfo.cs new file mode 100644 index 0000000000..1a96b90e10 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Globals/GlobalVariableInfo.cs @@ -0,0 +1,47 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; + +namespace IronRuby.Runtime { + internal sealed class GlobalVariableInfo : GlobalVariable { + private object _value; + + // whether the variable is considered defined: + private bool _isDefined; + + internal GlobalVariableInfo(object value) + : this(value, true) { + } + + internal GlobalVariableInfo(object value, bool isDefined) { + _value = value; + _isDefined = isDefined; + } + + public override bool IsDefined { + get { return _isDefined; } + } + + public override object GetValue(RubyContext/*!*/ context, RubyScope scope) { + return _value; + } + + public override void SetValue(RubyContext/*!*/ context, RubyScope scope, string/*!*/ name, object value) { + _value = value; + _isDefined = true; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Globals/GlobalVariables.cs b/merlin/main/languages/ruby/Ruby/Runtime/Globals/GlobalVariables.cs new file mode 100644 index 0000000000..ff6b29d25f --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Globals/GlobalVariables.cs @@ -0,0 +1,117 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Compiler; + +namespace IronRuby.Runtime { + internal enum GlobalVariableId { + MatchData, + EntireMatch, + MatchLastGroup, + MatchPrefix, + MatchSuffix, + + CurrentException, + CurrentExceptionBacktrace, + ItemSeparator, + StringSeparator, + InputSeparator, + OutputSeparator, + LastInputLine, + InputFileName, + CommandLineProgramPath, + CommandLineArguments, + LoadPath, + LoadedFiles, + InputContent, + OutputStream, + LastInputLineNumber, + + InputStream, + ErrorOutputStream, + SafeLevel, + Verbose, + KCode, + ChildProcessExitStatus + } + + public static class GlobalVariables { + // regex: + public static readonly GlobalVariable MatchData = new SpecialGlobalVariableInfo(GlobalVariableId.MatchData); + public static readonly GlobalVariable EntireMatch = new SpecialGlobalVariableInfo(GlobalVariableId.EntireMatch); + public static readonly GlobalVariable MatchLastGroup = new SpecialGlobalVariableInfo(GlobalVariableId.MatchLastGroup); + public static readonly GlobalVariable MatchPrefix = new SpecialGlobalVariableInfo(GlobalVariableId.MatchPrefix); + public static readonly GlobalVariable MatchSuffix = new SpecialGlobalVariableInfo(GlobalVariableId.MatchSuffix); + + public static readonly GlobalVariable CurrentException = new SpecialGlobalVariableInfo(GlobalVariableId.CurrentException); + public static readonly GlobalVariable CurrentExceptionBacktrace = new SpecialGlobalVariableInfo(GlobalVariableId.CurrentExceptionBacktrace); + + public static readonly GlobalVariable CommandLineArguments = new SpecialGlobalVariableInfo(GlobalVariableId.CommandLineArguments); + public static readonly GlobalVariable LastInputLine = new SpecialGlobalVariableInfo(GlobalVariableId.LastInputLine); + public static readonly GlobalVariable InputFileName = new SpecialGlobalVariableInfo(GlobalVariableId.InputFileName); + public static readonly GlobalVariable InputContent = new SpecialGlobalVariableInfo(GlobalVariableId.InputContent); + public static readonly GlobalVariable LastInputLineNumber = new SpecialGlobalVariableInfo(GlobalVariableId.LastInputLineNumber); + + public static readonly GlobalVariable InputSeparator = new SpecialGlobalVariableInfo(GlobalVariableId.InputSeparator); + public static readonly GlobalVariable ItemSeparator = new SpecialGlobalVariableInfo(GlobalVariableId.ItemSeparator); + public static readonly GlobalVariable StringSeparator = new SpecialGlobalVariableInfo(GlobalVariableId.StringSeparator); + public static readonly GlobalVariable OutputSeparator = new SpecialGlobalVariableInfo(GlobalVariableId.OutputSeparator); + + public static readonly GlobalVariable LoadPath = new SpecialGlobalVariableInfo(GlobalVariableId.LoadPath); + public static readonly GlobalVariable LoadedFiles = new SpecialGlobalVariableInfo(GlobalVariableId.LoadedFiles); + + public static readonly GlobalVariable OutputStream = new SpecialGlobalVariableInfo(GlobalVariableId.OutputStream); + public static readonly GlobalVariable InputStream = new SpecialGlobalVariableInfo(GlobalVariableId.InputStream); + public static readonly GlobalVariable ErrorOutputStream = new SpecialGlobalVariableInfo(GlobalVariableId.ErrorOutputStream); + public static readonly GlobalVariable CommandLineProgramPath = new SpecialGlobalVariableInfo(GlobalVariableId.CommandLineProgramPath); + + public static readonly GlobalVariable SafeLevel = new SpecialGlobalVariableInfo(GlobalVariableId.SafeLevel); + public static readonly GlobalVariable Verbose = new SpecialGlobalVariableInfo(GlobalVariableId.Verbose); + public static readonly GlobalVariable KCode = new SpecialGlobalVariableInfo(GlobalVariableId.KCode); + public static readonly GlobalVariable ChildProcessExitStatus = new SpecialGlobalVariableInfo(GlobalVariableId.ChildProcessExitStatus); + + // + // Defines variables backed by a field on RubyContext or Scope and variables that derived from them. + // Also variables that need type check on assignment are defined here. + // + // Other variables are simply looked up in the dictionary on RubyContext. All uses of such variables in libraries + // go thru alias table so they don't need a direct reference from RubyContext. + // + internal static void DefineVariablesNoLock(RubyContext/*!*/ context) { + // scope based variables (regex and input line): + context.DefineGlobalVariableNoLock(Symbols.MatchData, MatchData); + context.DefineGlobalVariableNoLock(Symbols.EntireMatch, EntireMatch); + context.DefineGlobalVariableNoLock(Symbols.MatchLastGroup, MatchLastGroup); + context.DefineGlobalVariableNoLock(Symbols.MatchPrefix, MatchPrefix); + context.DefineGlobalVariableNoLock(Symbols.MatchSuffix, MatchSuffix); + context.DefineGlobalVariableNoLock(Symbols.LastInputLine, LastInputLine); + + // directly accessed variables provided by execution context: + context.DefineGlobalVariableNoLock(Symbols.CurrentException, CurrentException); + context.DefineGlobalVariableNoLock(Symbols.CurrentExceptionBacktrace, CurrentExceptionBacktrace); + context.DefineGlobalVariableNoLock(Symbols.CommandLineArguments, CommandLineArguments); + context.DefineGlobalVariableNoLock(Symbols.InputSeparator, InputSeparator); + context.DefineGlobalVariableNoLock(Symbols.ItemSeparator, ItemSeparator); + context.DefineGlobalVariableNoLock(Symbols.StringSeparator, StringSeparator); + context.DefineGlobalVariableNoLock(Symbols.OutputSeparator, OutputSeparator); + context.DefineGlobalVariableNoLock(Symbols.InputContent, InputContent); + context.DefineGlobalVariableNoLock(Symbols.OutputStream, OutputStream); + context.DefineGlobalVariableNoLock(Symbols.LoadedFiles, LoadedFiles); + context.DefineGlobalVariableNoLock(Symbols.LoadPath, LoadPath); + context.DefineGlobalVariableNoLock(Symbols.LastInputLineNumber, LastInputLineNumber); + context.DefineGlobalVariableNoLock(Symbols.ChildProcessExitStatus, ChildProcessExitStatus); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Globals/KCode.cs b/merlin/main/languages/ruby/Ruby/Runtime/Globals/KCode.cs new file mode 100644 index 0000000000..d2b2af3630 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Globals/KCode.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace IronRuby.Runtime { + public enum KCode { + Default = 0, + Binary = 1, + Utf8 = 2, + Euc = 3, + Sjis = 4, + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Globals/ReadOnlyGlobalVariableInfo.cs b/merlin/main/languages/ruby/Ruby/Runtime/Globals/ReadOnlyGlobalVariableInfo.cs new file mode 100644 index 0000000000..4d15ed5387 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Globals/ReadOnlyGlobalVariableInfo.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting; + +namespace IronRuby.Runtime { + internal sealed class ReadOnlyGlobalVariableInfo : GlobalVariable { + private object _value; + + public ReadOnlyGlobalVariableInfo(object value) { + _value = value; + } + + public override object GetValue(RubyContext/*!*/ context, RubyScope scope) { + return _value; + } + + public override void SetValue(RubyContext/*!*/ context, RubyScope scope, string/*!*/ name, object value) { + throw ReadOnlyError(name); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Globals/SpecialGlobalVariableInfo.cs b/merlin/main/languages/ruby/Ruby/Runtime/Globals/SpecialGlobalVariableInfo.cs new file mode 100644 index 0000000000..1b6da95261 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Globals/SpecialGlobalVariableInfo.cs @@ -0,0 +1,245 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; + +namespace IronRuby.Runtime { + internal sealed class SpecialGlobalVariableInfo : GlobalVariable { + private readonly GlobalVariableId _id; + + internal SpecialGlobalVariableInfo(GlobalVariableId id) { + _id = id; + } + + public override object GetValue(RubyContext/*!*/ context, RubyScope scope) { + switch (_id) { + + // regular expressions: + case GlobalVariableId.MatchData: + return (scope != null) ? scope.GetInnerMostClosureScope().CurrentMatch : null; + + case GlobalVariableId.MatchLastGroup: + return (scope != null) ? scope.GetInnerMostClosureScope().GetCurrentMatchLastGroup() : null; + + case GlobalVariableId.MatchPrefix: + // TODO: + throw new NotImplementedException(); + + case GlobalVariableId.MatchSuffix: + // TODO: + throw new NotImplementedException(); + + case GlobalVariableId.EntireMatch: + return (scope != null) ? scope.GetInnerMostClosureScope().GetCurrentMatchGroup(0) : null; + + + // exceptions: + case GlobalVariableId.CurrentException: + return context.CurrentException; + + case GlobalVariableId.CurrentExceptionBacktrace: + return context.GetCurrentExceptionBacktrace(); + + + // input: + case GlobalVariableId.InputContent: + return context.InputProvider.Singleton; + + case GlobalVariableId.InputFileName: + return context.InputProvider.CurrentFileName; + + case GlobalVariableId.LastInputLine: + return (scope != null) ? scope.GetInnerMostClosureScope().LastInputLine : null; + + case GlobalVariableId.LastInputLineNumber: + return context.InputProvider.LastInputLineNumber; + + case GlobalVariableId.CommandLineArguments: + return context.InputProvider.CommandLineArguments; + + + // output: + case GlobalVariableId.OutputStream: + return context.StandardOutput; + + case GlobalVariableId.ErrorOutputStream: + return context.StandardErrorOutput; + + case GlobalVariableId.InputStream: + return context.StandardInput; + + + // separators: + case GlobalVariableId.InputSeparator: + return context.InputSeparator; + + case GlobalVariableId.OutputSeparator: + return context.OutputSeparator; + + case GlobalVariableId.StringSeparator: + return context.StringSeparator; + + case GlobalVariableId.ItemSeparator: + return context.ItemSeparator; + + + // loader: + case GlobalVariableId.LoadPath: + return context.Loader.LoadPaths; + + case GlobalVariableId.LoadedFiles: + return context.Loader.LoadedFiles; + + + // misc: + case GlobalVariableId.SafeLevel: + return context.CurrentSafeLevel; + + case GlobalVariableId.Verbose: + return context.Verbose; + + case GlobalVariableId.KCode: + // TODO: Ruby 1.9 reports a warning: + string name = context.GetKCodeName(); + return (name != null) ? MutableString.Create(name) : null; + + case GlobalVariableId.ChildProcessExitStatus: + return context.ChildProcessExitStatus; + + default: + throw Assert.Unreachable; + } + } + + public override void SetValue(RubyContext/*!*/ context, RubyScope scope, string/*!*/ name, object value) { + switch (_id) { + // regex: + case GlobalVariableId.MatchData: + if (scope == null) { + throw ReadOnlyError(name); + } + + scope.GetInnerMostClosureScope().CurrentMatch = (value != null) ? RequireType(value, name, "MatchData") : null; + return; + + case GlobalVariableId.MatchLastGroup: + case GlobalVariableId.MatchPrefix: + case GlobalVariableId.MatchSuffix: + case GlobalVariableId.EntireMatch: + throw ReadOnlyError(name); + + + // exceptions: + case GlobalVariableId.CurrentException: + context.SetCurrentException(value); + return; + + case GlobalVariableId.CurrentExceptionBacktrace: + context.SetCurrentExceptionBacktrace(value); + return; + + + // input: + case GlobalVariableId.LastInputLine: + if (scope == null) { + throw ReadOnlyError(name); + } + scope.GetInnerMostClosureScope().LastInputLine = value; + return; + + case GlobalVariableId.LastInputLineNumber: + context.InputProvider.LastInputLineNumber = RequireType(value, name, "Fixnum"); + return; + + case GlobalVariableId.CommandLineArguments: + case GlobalVariableId.InputFileName: + throw ReadOnlyError(name); + + // output: + case GlobalVariableId.OutputStream: + context.StandardOutput = RequireWriteProtocol(context, value, name); + return; + + case GlobalVariableId.ErrorOutputStream: + context.StandardErrorOutput = RequireWriteProtocol(context, value, name); + break; + + case GlobalVariableId.InputStream: + context.StandardInput = value; + return; + + // separators: + case GlobalVariableId.InputContent: + throw ReadOnlyError(name); + + case GlobalVariableId.InputSeparator: + context.InputSeparator = (value != null) ? RequireType(value, name, "String") : null; + return; + + case GlobalVariableId.OutputSeparator: + context.OutputSeparator = (value != null) ? RequireType(value, name, "String") : null; + return; + + case GlobalVariableId.StringSeparator: + // type not enforced: + context.StringSeparator = value; + return; + + case GlobalVariableId.ItemSeparator: + context.ItemSeparator = (value != null) ? RequireType(value, name, "String") : null; + return; + + + // loader: + case GlobalVariableId.LoadedFiles: + case GlobalVariableId.LoadPath: + throw ReadOnlyError(name); + + + // misc: + case GlobalVariableId.SafeLevel: + context.SetSafeLevel(RequireType(value, name, "Fixnum")); + return; + + case GlobalVariableId.Verbose: + context.Verbose = value; + return; + + case GlobalVariableId.KCode: + // MRI calls to_str; we don't do that, it's inconsistent with other globals. + // If some app depends on this behavior, it will fail gracefully: + context.SetKCode((value != null) ? RequireType(value, name, "String") : null); + return; + + case GlobalVariableId.ChildProcessExitStatus: + throw ReadOnlyError(name); + + default: + throw Assert.Unreachable; + } + } + + private object RequireWriteProtocol(RubyContext/*!*/ context, object value, string/*!*/ variableName) { + if (!RubySites.RespondTo(context, value, "write")) { + throw RubyExceptions.CreateTypeError(String.Format("${0} must have write method, {1} given", variableName, RubyUtils.GetClassName(context, value))); + } + + return value; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/IDuplicable.cs b/merlin/main/languages/ruby/Ruby/Runtime/IDuplicable.cs new file mode 100644 index 0000000000..0c34edc48c --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/IDuplicable.cs @@ -0,0 +1,35 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace IronRuby.Runtime { + + // Naming conventions: + // method | tainted | Ruby instance variables | frozen | singleton members | internal state of the object + // Copy | NO | NO | NO | NO | YES + // Clone | YES | NO | NO | NO | YES + // Duplicate(false) | YES | YES | NO | NO | PARTIAL + // Duplicate(true) | YES | YES | NO | YES | PARTIAL + // + // PARTIAL - part of the state is initialized by "initialize_copy" + // + // Hash: initialize_copy + + /// + /// Implemented by classes that are capable of creating their subclass clones. + /// + public interface IDuplicable { + object Duplicate(RubyContext/*!*/ context, bool copySingletonMembers); + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/IOWrapper.cs b/merlin/main/languages/ruby/Ruby/Runtime/IOWrapper.cs new file mode 100644 index 0000000000..240cbedc75 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/IOWrapper.cs @@ -0,0 +1,277 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Runtime.CompilerServices; +using IronRuby.Builtins; +using Microsoft.Scripting.Runtime; + +namespace IronRuby.Runtime { + + public class IOWrapper : Stream { + private static readonly CallSite> _writeSite = + CallSite>.Create(RubySites.InstanceCallAction("write", 1)); + + private static readonly CallSite> _readSite = + CallSite>.Create(RubySites.InstanceCallAction("read", 1)); + + private static readonly CallSite> _seekSite = + CallSite>.Create(RubySites.InstanceCallAction("seek", 2)); + + private static readonly CallSite> _tellSite = + CallSite>.Create(RubySites.InstanceCallAction("tell")); + + private readonly RubyContext/*!*/ _context; + private readonly object/*!*/ _obj; + private readonly bool _canRead; + private readonly bool _canWrite; + private readonly bool _canSeek; + private readonly byte[]/*!*/ _buffer; + private int _writePos; + private int _readPos; + private int _readLen; + + private const int _bufferSize = 0x1000; + + public IOWrapper(RubyContext/*!*/ context, object/*!*/ obj, FileAccess access) { + _context = context; + _obj = obj; + + if (access == FileAccess.Read || access == FileAccess.ReadWrite) { + _canRead = RubySites.RespondTo(context, obj, "read"); + } else { + _canRead = false; + } + + if (access == FileAccess.Write || access == FileAccess.ReadWrite) { + _canWrite = RubySites.RespondTo(context, obj, "write"); + } else { + _canWrite = false; + } + + _canSeek = (RubySites.RespondTo(context, obj, "seek") && RubySites.RespondTo(context, obj, "tell")); + + _buffer = new byte[_bufferSize]; + _writePos = 0; + _readPos = 0; + _readLen = 0; + } + + public override bool CanRead { + get { return _canRead; } + } + + public override bool CanSeek { + get { return _canSeek; } + } + + public override bool CanWrite { + get { return _canWrite; } + } + + public override long Length { + get { + long currentPos = Position; + Seek(0, SeekOrigin.End); + long result = Position; + Position = currentPos; + return result; + } + } + + public override long Position { + get { + if (!_canSeek) { + throw new NotSupportedException(); + } + return _tellSite.Target(_tellSite, _context, _obj); + } + set { + if (!_canSeek) { + throw new NotSupportedException(); + } + Seek(value, SeekOrigin.Begin); + } + } + + public override void Flush() { + FlushWrite(); + FlushRead(); + } + + private void FlushWrite() { + if (_writePos > 0) { + WriteToObject(); + } + } + + private void FlushRead() { + if (_canSeek && (_readPos < _readLen)) { + Seek(this._readPos - this._readLen, SeekOrigin.Current); + } + _readPos = 0; + _readLen = 0; + } + + public override int Read(byte[]/*!*/ buffer, int offset, int count) { + if (!_canRead) { + throw new NotSupportedException(); + } + int size = _readLen - _readPos; + if (size == 0) { + FlushWrite(); + if (count > _bufferSize) { + size = ReadFromObject(buffer, offset, count); + _readPos = 0; + _readLen = 0; + return size; + } + size = ReadFromObject(_buffer, 0, _bufferSize); + if (size == 0) { + return 0; + } + _readPos = 0; + _readLen = size; + } + if (size > count) { + size = count; + } + Buffer.BlockCopy(_buffer, _readPos, buffer, offset, size); + _readPos += size; + if (size < count) { + int additionalSize = ReadFromObject(buffer, offset + size, count - size); + size += additionalSize; + _readPos = 0; + _readLen = 0; + } + return size; + } + + public override int ReadByte() { + if (!_canRead) { + throw new NotSupportedException(); + } + if (_readPos == _readLen) { + FlushWrite(); + + _readLen = ReadFromObject(_buffer, 0, _bufferSize); + _readPos = 0; + if (_readLen == 0) { + return -1; + } + + } + return _buffer[_readPos++]; + } + + private int ReadFromObject(byte[]/*!*/ buffer, int offset, int count) { + MutableString result = _readSite.Target(_readSite, _context, _obj, count); + if (result == null) { + return 0; + } else { + byte[] readdata = result.ConvertToBytes(); + Buffer.BlockCopy(readdata, 0, buffer, offset, readdata.Length); + return readdata.Length; + } + } + + public override long Seek(long offset, SeekOrigin origin) { + if (!_canSeek) { + throw new NotSupportedException(); + } + + int rubyOrigin = 0; + switch (origin) { + case SeekOrigin.Begin: + rubyOrigin = RubyIO.SEEK_SET; + break; + case SeekOrigin.Current: + rubyOrigin = RubyIO.SEEK_CUR; + break; + case SeekOrigin.End: + rubyOrigin = RubyIO.SEEK_END; + break; + } + + _seekSite.Target(_seekSite, _context, _obj, offset, rubyOrigin); + return Position; + } + + public override void SetLength(long value) { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) { + if (!_canWrite) { + throw new NotSupportedException(); + } + + if (_writePos == 0) { + FlushRead(); + } else { + int size = _bufferSize - _writePos; + if (size > 0) { + if (size > count) { + size = count; + } + Buffer.BlockCopy(buffer, offset, _buffer, _writePos, size); + _writePos += size; + if (size == count) { + return; + } + offset += size; + count -= size; + } + WriteToObject(); + } + if (count >= _bufferSize) { + WriteToObject(buffer, offset, count); + } else if (count > 0) { + Buffer.BlockCopy(buffer, offset, _buffer, 0, count); + _writePos = count; + } + } + + public override void WriteByte(byte value) { + if (!_canWrite) { + throw new NotSupportedException(); + } + + if (_writePos == 0) { + FlushRead(); + } + if (_writePos == _bufferSize) { + WriteToObject(); + } + _buffer[_writePos++] = value; + } + + private void WriteToObject() { + WriteToObject(_buffer, 0, _writePos); + _writePos = 0; + } + + private void WriteToObject(byte[] buffer, int offset, int count) { + if (offset != 0 || count != buffer.Length) { + byte[] newBuffer = new byte[count]; + Buffer.BlockCopy(buffer, offset, newBuffer, 0, count); + buffer = newBuffer; + } + MutableString argument = MutableString.CreateBinary(buffer, count); + _writeSite.Target(_writeSite, _context, _obj, argument); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/IRubyObject.cs b/merlin/main/languages/ruby/Ruby/Runtime/IRubyObject.cs new file mode 100644 index 0000000000..643c1caa36 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/IRubyObject.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Builtins; +using IronRuby.Runtime; +using IronRuby.Compiler.Generation; + +namespace IronRuby { + public interface IRubyObject { + // Gets the ruby class associated with this object + [Emitted] // RubyTypeBuilder + RubyClass/*!*/ Class { get; } + + // Returns the instance object data. May return null. + [Emitted] // RubyTypeBuilder + RubyInstanceData TryGetInstanceData(); + + // Returns the instance object data. + [Emitted] // RubyTypeBuilder + RubyInstanceData/*!*/ GetInstanceData(); + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/IRubyObjectState.cs b/merlin/main/languages/ruby/Ruby/Runtime/IRubyObjectState.cs new file mode 100644 index 0000000000..89c0520563 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/IRubyObjectState.cs @@ -0,0 +1,26 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using IronRuby.Builtins; +using IronRuby.Runtime; + +namespace IronRuby { + public interface IRubyObjectState { + bool IsFrozen { get; } + bool IsTainted { get; set; } + + void Freeze(); + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/InstanceData.cs b/merlin/main/languages/ruby/Ruby/Runtime/InstanceData.cs new file mode 100644 index 0000000000..13a1ef6a98 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/InstanceData.cs @@ -0,0 +1,186 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting; +using System.Threading; +using IronRuby.Builtins; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Runtime { + /// + /// Stores the per-instance data that all Ruby objects need + /// (frozen?, tainted?, instance_variables, etc) + /// + /// Stored in a lookaside weak hashtable for types that don't implement IRubyObject (i.e. .NET types). + /// + public class RubyInstanceData { + private static int _CurrentObjectId = 42; // Last unique Id we gave out. + + // These need to be seperate fields so we get atomic access to them + // (which means no synchronization is needed for get/set operations) + private int _objectId; + private bool _frozen, _tainted; + private Dictionary _instanceVars; + private RubyClass _immediateClass; + + internal bool Tainted { + get { return _tainted; } + set { _tainted = value; } + } + + internal bool Frozen { + get { return _frozen; } + } + + internal void Freeze() { + _frozen = true; + } + + /// + /// Null - uninitialized (lazy init'd) => object doesn't have an instance singleton. + /// Class - object doesn't have an instance singleton + /// Singleton - object has an instance singleton + /// + internal RubyClass ImmediateClass { + get { return _immediateClass; } + set { _immediateClass = value; } + } + + internal RubyClass InstanceSingleton { + get { return (_immediateClass != null && _immediateClass.IsSingletonClass) ? _immediateClass : null; } + } + + /// + /// WARNING: not all objects store their ID here. + /// Use ObjectOps.GetObjectId instead. + /// + internal int ObjectId { + get { return _objectId; } + } + + internal RubyInstanceData(int id) { + _objectId = id; + } + + internal RubyInstanceData() { + _objectId = Interlocked.Increment(ref _CurrentObjectId); + } + + /// + /// Gets the instance variables dictionary, initializing it if it was null. + /// Only use this if you want to set something into the dictionary, otherwise + /// just use the _instanceVars field + /// + private Dictionary/*!*/ GetInstanceVariables() { + if (_instanceVars == null) { + var newValue = new Dictionary(); + if (Interlocked.CompareExchange(ref _instanceVars, newValue, null) == null) { + return newValue; + } + } + return _instanceVars; + } + + #region instance variable support + + internal bool HasInstanceVariables { + get { + return _instanceVars != null; + } + } + + internal void CopyInstanceVariablesTo(RubyInstanceData/*!*/ dup) { + if (_instanceVars == null) { + return; + } + lock (_instanceVars) { + Dictionary dupVars = dup.GetInstanceVariables(); + foreach (var var in _instanceVars) { + dupVars.Add(var.Key, var.Value); + } + } + } + + internal bool IsInstanceVariableDefined(string/*!*/ name) { + if (_instanceVars == null) { + return false; + } + lock (_instanceVars) { + return _instanceVars.ContainsKey(name); + } + } + + internal string/*!*/[]/*!*/ GetInstanceVariableNames() { + if (_instanceVars == null) { + return ArrayUtils.EmptyStrings; + } + lock (_instanceVars) { + string[] result = new string[_instanceVars.Count]; + _instanceVars.Keys.CopyTo(result, 0); + return result; + } + } + + // Returns a copy of the current instance variable key-value pairs for this object + internal List>/*!*/ GetInstanceVariablePairs() { + if (_instanceVars == null) { + return new List>(); + } + lock (_instanceVars) { + return new List>(_instanceVars); + } + } + + internal bool TryGetInstanceVariable(string/*!*/ name, out object value) { + if (_instanceVars == null) { + value = null; + return false; + } + lock (_instanceVars) { + return _instanceVars.TryGetValue(name, out value); + } + } + + internal bool TryRemoveInstanceVariable(string/*!*/ name, out object value) { + if (_instanceVars == null) { + value = null; + return false; + } + lock (_instanceVars) { + if (!_instanceVars.TryGetValue(name, out value)) { + return false; + } + _instanceVars.Remove(name); + return true; + } + } + + internal object GetInstanceVariable(string/*!*/ name) { + object value; + TryGetInstanceVariable(name, out value); + return value; + } + + internal void SetInstanceVariable(string/*!*/ name, object value) { + Dictionary vars = GetInstanceVariables(); + lock (vars) { + vars[name] = value; + } + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/InstanceDataWeakTable.cs b/merlin/main/languages/ruby/Ruby/Runtime/InstanceDataWeakTable.cs new file mode 100644 index 0000000000..3caa801636 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/InstanceDataWeakTable.cs @@ -0,0 +1,217 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using SRC = System.Runtime.CompilerServices; +using System.Reflection; +using System.Diagnostics; + +namespace IronRuby.Runtime { + + // thread-unsafe + internal class WeakTable { + private Dictionary/*!*/ _dict; + private static readonly IEqualityComparer _Comparer = new Comparer(); + + #region Comparer + + // WeakComparer treats WeakReference as transparent envelope + // also, uses reference equality + sealed class Comparer : IEqualityComparer { + bool IEqualityComparer.Equals(object x, object y) { + WeakReference wx = x as WeakReference; + WeakReference wy = y as WeakReference; + + // Pull the value(s) out of the WeakReference for comparison + // Empty WeakReference slots are only equal to themselves + if (wx != null) { + x = wx.Target; + if (x == null) { + return wx == wy; + } + } + + if (wy != null) { + y = wy.Target; + if (y == null) { + return wx == wy; + } + } + + return x == y; + } + + int IEqualityComparer.GetHashCode(object obj) { + WeakReference wobj = obj as WeakReference; + if (wobj != null) { + obj = wobj.Target; + if (obj == null) { + // empty WeakReference slots are only equal to themselves + return wobj.GetHashCode(); + } + } + + return SRC.RuntimeHelpers.GetHashCode(obj); + } + } + #endregion + + #region weak hashtable cleanup + + int _version, _cleanupVersion; + +#if SILVERLIGHT // GC + WeakReference _cleanupGC = new WeakReference(new object()); +#else + int _cleanupGC = 0; +#endif + + bool GarbageCollected() { + // Determine if a GC has happened + + // WeakReferences can become zero only during the GC. + bool garbage_collected; +#if SILVERLIGHT // GC.CollectionCount + garbage_collected = !_cleanupGC.IsAlive; + if (garbage_collected) _cleanupGC = new WeakReference(new object()); +#else + int currentGC = GC.CollectionCount(0); + garbage_collected = currentGC != _cleanupGC; + if (garbage_collected) _cleanupGC = currentGC; +#endif + return garbage_collected; + } + + void CheckCleanup() { + _version++; + + long change = _version - _cleanupVersion; + + // Cleanup the table if it is a while since we have done it last time. + // Take the size of the table into account. + if (change > 1234 + _dict.Count / 2) { + // It makes sense to do the cleanup only if a GC has happened in the meantime. + if (GarbageCollected()) { + Cleanup(); + _cleanupVersion = _version; + } else { + _cleanupVersion += 1234; + } + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")] // TODO + void Cleanup() { + int liveCount = 0; + int emptyCount = 0; + + foreach (WeakReference w in _dict.Keys) { + if (w.Target != null) { + liveCount++; + } else { + emptyCount++; + } + } + + // Rehash the table if there is a significant number of empty slots + if (emptyCount > liveCount / 4) { + Dictionary newtable = new Dictionary(liveCount + liveCount / 4, _Comparer); + + foreach (WeakReference w in _dict.Keys) { + object target = w.Target; + + if (target != null) { + newtable[w] = _dict[w]; + GC.KeepAlive(target); + } + } + + _dict = newtable; + } + } + #endregion + + public WeakTable() { + _dict = new Dictionary(_Comparer); + } + + public bool TryGetValue(TKey key, out TValue value) { + return _dict.TryGetValue(key, out value); + } + + public void Add(TKey key, TValue value) { + Cleanup(); + // _dict might be a new Dictionary after Cleanup(), + // so use the field directly + _dict.Add(new WeakReference(key, true), value); + } + } + + // thread-safe + internal class InstanceDataWeakTable { + + private delegate bool TryGetValueDelegate(object key, out RubyInstanceData value); + private delegate void AddDelegate(object key, RubyInstanceData value); + + private static readonly MethodInfo _AddMethod, _TryGetValueMethod; + private static readonly Type _TableType; + + private readonly object/*!*/ _dict; + private readonly TryGetValueDelegate/*!*/ _tryGetValue; + private readonly AddDelegate/*!*/ _add; + + static InstanceDataWeakTable() { +#if !SILVERLIGHT + _TableType = typeof(object).Assembly.GetType("System.Runtime.CompilerServices.ConditionalWeakTable`2", false, false); + if (_TableType != null) { + _TableType = _TableType.MakeGenericType(typeof(object), typeof(RubyInstanceData)); + } else +#endif + _TableType = typeof(WeakTable); + + Utils.Log(_TableType.FullName, "WEAK_TABLE"); + + _TryGetValueMethod = _TableType.GetMethod("TryGetValue", new Type[] { typeof(object), typeof(RubyInstanceData).MakeByRefType() }); + _AddMethod = _TableType.GetMethod("Add", new Type[] { typeof(object), typeof(RubyInstanceData) }); + } + + public InstanceDataWeakTable() { + _dict = Activator.CreateInstance(_TableType); + _tryGetValue = (TryGetValueDelegate)Delegate.CreateDelegate(typeof(TryGetValueDelegate), _dict, _TryGetValueMethod); + _add = (AddDelegate)Delegate.CreateDelegate(typeof(AddDelegate), _dict, _AddMethod); + } + + public bool TryGetValue(object key, out RubyInstanceData value) { + lock (_dict) { + return _tryGetValue(key, out value); + } + } + + public RubyInstanceData/*!*/ GetValue(object key) { + lock (_dict) { + RubyInstanceData value; + if (_tryGetValue(key, out value)) { + return value; + } + + value = new RubyInstanceData(); + _add(key, value); + return value; + } + } + } +} \ No newline at end of file diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Loader.cs b/merlin/main/languages/ruby/Ruby/Runtime/Loader.cs new file mode 100644 index 0000000000..636feb74e2 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Loader.cs @@ -0,0 +1,711 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Security; +using System.Text.RegularExpressions; +using System.Threading; +using IronRuby.Builtins; +using IronRuby.Compiler; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Runtime { + [Flags] + public enum LoadFlags { + None = 0, + LoadOnce = 1, + LoadIsolated = 2, + AppendExtensions = 4, + } + + // TODO: thread safety + public sealed class Loader { + + internal enum FileKind { + RubySourceFile, + NonRubySourceFile, + Assembly, + Type, + Unknown, + } + + private RubyContext/*!*/ _context; + + // $: + private readonly RubyArray/*!*/ _loadPaths; + + // $" + private readonly RubyArray/*!*/ _loadedFiles; + + // files that were required but their execution haven't completed yet: + private readonly Stack/*!*/ _unfinishedFiles; + + // TODO: static + // maps full normalized path to compiled code: + private Dictionary _compiledFiles; + private readonly object/*!*/ _compiledFileMutex = new object(); + + private struct CompiledFile { + public readonly ScriptCode/*!*/ CompiledCode; + + public CompiledFile(ScriptCode/*!*/ compiledCode) { + Assert.NotNull(compiledCode); + + CompiledCode = compiledCode; + } + } + + // counters: + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + private int _cacheHitCount; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + private int _compiledFileCount; + + internal static long _ILGenerationTimeTicks; + internal static long _ScriptCodeGenerationTimeTicks; + + /// + /// TODO: Thread safety: the user of this object is responsible for locking it. + /// + public RubyArray/*!*/ LoadPaths { + get { return _loadPaths; } + } + + /// + /// TODO: Thread safety: the user of this object is responsible for locking it. + /// + public RubyArray/*!*/ LoadedFiles { + get { return _loadedFiles; } + } + + private PlatformAdaptationLayer/*!*/ Platform { + get { return DomainManager.Platform; } + } + + private ScriptDomainManager/*!*/ DomainManager { + get { return _context.DomainManager; } + } + + internal Loader(RubyContext/*!*/ context) { + Assert.NotNull(context); + _context = context; + + _loadPaths = MakeLoadPaths(context.RubyOptions); + _loadedFiles = new RubyArray(); + _unfinishedFiles = new Stack(); + } + + private RubyArray/*!*/ MakeLoadPaths(RubyOptions/*!*/ options) { + var loadPaths = new RubyArray(); + + if (options.HasSearchPaths) { + foreach (string path in options.SearchPaths) { + loadPaths.Add(MutableString.Create(path.Replace('\\', '/'))); + } + } + +#if !SILVERLIGHT // no library paths on Silverlight + string applicationBaseDir; + try { + applicationBaseDir = AppDomain.CurrentDomain.BaseDirectory; + } catch (SecurityException) { + applicationBaseDir = null; + } + + AddAbsoluteLibraryPaths(loadPaths, applicationBaseDir, options.LibraryPaths); +#endif + loadPaths.Add(MutableString.Create(".")); + return loadPaths; + } + + private void AddAbsoluteLibraryPaths(RubyArray/*!*/ result, string applicationBaseDir, ICollection/*!*/ paths) { + foreach (var path in paths) { + string fullPath; + if (applicationBaseDir != null) { + try { + fullPath = Platform.IsAbsolutePath(path) ? path : Platform.GetFullPath(Path.Combine(applicationBaseDir, path)); + } catch (Exception) { + // error will be reported on first require: + fullPath = path; + } + } else { + fullPath = path; + } + result.Add(MutableString.Create(fullPath.Replace('\\', '/'))); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] // TODO + private Dictionary/*!*/ LoadCompiledCode() { + Debug.Assert(_context.RubyOptions.LoadFromDisk); + + Dictionary result = new Dictionary(); + Utils.Log("LOADING", "LOADER"); + + ScriptCode[] codes = ScriptCode.LoadFromAssembly(_context.DomainManager, + Assembly.Load(Path.GetFileName(_context.RubyOptions.MainFile)) + ); + + for (int i = 0; i < codes.Length; i++) { + string path = codes[i].SourceUnit.Path; + string fullPath = Platform.GetFullPath(path); + result[fullPath] = new CompiledFile(codes[i]); + } + + return result; + } + + internal void SaveCompiledCode() { + string savePath = _context.RubyOptions.SavePath; + if (savePath != null) { + lock (_compiledFileMutex) { + var assemblyPath = Path.Combine(savePath, Path.GetFileName(_context.RubyOptions.MainFile) + ".dll"); + + Utils.Log(String.Format("SAVING to {0}", Path.GetFullPath(assemblyPath)), "LOADER"); + + // TODO: allocate eagerly (as soon as config gets fixed) + if (_compiledFiles == null) { + _compiledFiles = new Dictionary(); + } + + ScriptCode[] codes = new ScriptCode[_compiledFiles.Count]; + int i = 0; + foreach (CompiledFile file in _compiledFiles.Values) { + codes[i++] = file.CompiledCode; + } + + ScriptCode.SaveToAssembly(assemblyPath, codes); + } + } + } + + private bool TryGetCompiledFile(string/*!*/ fullPath, out CompiledFile compiledFile) { + if (!_context.RubyOptions.LoadFromDisk) { + compiledFile = default(CompiledFile); + return false; + } + + lock (_compiledFileMutex) { + if (_compiledFiles == null) { + _compiledFiles = LoadCompiledCode(); + } + + return _compiledFiles.TryGetValue(fullPath, out compiledFile); + } + } + + private void AddCompiledFile(string/*!*/ fullPath, ScriptCode/*!*/ compiledCode) { + if (_context.RubyOptions.SavePath != null) { + lock (_compiledFileMutex) { + // TODO: allocate eagerly (as soon as config gets fixed) + if (_compiledFiles == null) { + _compiledFiles = new Dictionary(); + } + _compiledFiles[fullPath] = new CompiledFile(compiledCode); + } + } + } + + /// + /// Returns true if a Ruby file is successfully loaded, false if it is already loaded. + /// + public bool LoadFile(Scope/*!*/ globalScope, object self, MutableString/*!*/ path, LoadFlags flags) { + Assert.NotNull(globalScope, path); + + string assemblyName, typeName; + + string strPath = path.ConvertToString(); + if (TryParseAssemblyName(strPath, out typeName, out assemblyName)) { + + if (AlreadyLoaded(path, flags)) { + return false; + } + + if (LoadAssembly(assemblyName, typeName, false)) { + FileLoaded(path, flags); + return true; + } + } + + return LoadFromPath(globalScope, self, strPath, flags); + } + + public bool LoadAssembly(string/*!*/ assemblyName, string typeName, bool throwOnError) { + Utils.Log(String.Format("Loading assembly '{0}' and type '{1}'", assemblyName, typeName), "LOADER"); + + Assembly assembly; + try { + assembly = Platform.LoadAssembly(assemblyName); + } catch (Exception e) { + if (throwOnError) throw new LoadError(e.Message, e); + return false; + } + + Type initializerType; + if (typeName != null) { + // load Ruby library: + try { + initializerType = assembly.GetType(typeName, true); + } catch (Exception e) { + if (throwOnError) throw new LoadError(e.Message, e); + return false; + } + + LoadLibrary(initializerType, false); + } else { + // load namespaces: + try { + DomainManager.LoadAssembly(assembly); + } catch (Exception e) { + if (throwOnError) throw new LoadError(e.Message, e); + return false; + } + } + + return true; + } + + private static Regex _AssemblyNameRegex = new Regex(@" + \s*((?[\w.+]+)\s*,)?\s* # type name + (? + [^,=]+\s* # assembly name + (,\s*[\w]+\s*=\s*[^,]+\s*)+ # properties + )", + RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline + ); + + internal static bool TryParseAssemblyName(string/*!*/ path, out string typeName, out string assemblyName) { + Match match = _AssemblyNameRegex.Match(path); + if (match.Success) { + Group typeGroup = match.Groups["type"]; + Group assemblyGroup = match.Groups["assembly"]; + Debug.Assert(assemblyGroup.Success); + + typeName = typeGroup.Success ? typeGroup.Value : null; + assemblyName = assemblyGroup.Value; + return true; + } + + if (path.Trim() == "mscorlib") { + typeName = null; + assemblyName = path; + return true; + } + + typeName = null; + assemblyName = null; + return false; + } + + private class ResolvedFile { + public readonly SourceUnit SourceUnit; + public readonly string/*!*/ Path; + public readonly string AppendedExtension; + + public ResolvedFile(SourceUnit/*!*/ sourceUnit, string appendedExtension) { + SourceUnit = sourceUnit; + Path = sourceUnit.Path; + AppendedExtension = appendedExtension; + } + + public ResolvedFile(string/*!*/ libraryPath, string appendedExtension) { + Assert.NotNull(libraryPath); + Path = libraryPath; + AppendedExtension = appendedExtension; + } + } + + private bool LoadFromPath(Scope/*!*/ globalScope, object self, string/*!*/ path, LoadFlags flags) { + Assert.NotNull(globalScope, path); + + ResolvedFile file = FindFile(globalScope, path, (flags & LoadFlags.AppendExtensions) != 0); + if (file == null) { + throw new LoadError(String.Format("no such file to load -- {0}", path)); + } + + MutableString pathWithExtension = MutableString.Create(path); + if (file.AppendedExtension != null) { + pathWithExtension.Append(file.AppendedExtension); + } + + if (AlreadyLoaded(pathWithExtension, flags) || _unfinishedFiles.Contains(pathWithExtension.ToString())) { + return false; + } + + try { + // save path as is, no canonicalization nor combination with an extension or directory: + _unfinishedFiles.Push(pathWithExtension.ToString()); + + if (file.SourceUnit != null) { + + RubyContext rubySource = file.SourceUnit.LanguageContext as RubyContext; + if (rubySource != null) { + ExecuteRubySourceUnit(file.SourceUnit, globalScope, flags); + } else { + file.SourceUnit.Execute(); + } + } else { + Debug.Assert(file.Path != null); + try { + Assembly asm = Platform.LoadAssemblyFromPath(Platform.GetFullPath(file.Path)); + DomainManager.LoadAssembly(asm); + } catch (Exception e) { + throw new LoadError(e.Message, e); + } + } + + FileLoaded(pathWithExtension, flags); + } finally { + _unfinishedFiles.Pop(); + } + + return true; + } + + private void ExecuteRubySourceUnit(SourceUnit/*!*/ sourceUnit, Scope/*!*/ globalScope, LoadFlags flags) { + Assert.NotNull(sourceUnit, globalScope); + + // TODO: check file timestamp + string fullPath = Platform.GetFullPath(sourceUnit.Path); + CompiledFile compiledFile; + if (TryGetCompiledFile(fullPath, out compiledFile)) { + Utils.Log(String.Format("{0}: {1}", ++_cacheHitCount, sourceUnit.Path), "LOAD_CACHED"); + compiledFile.CompiledCode.Run(globalScope); + } else { + Utils.Log(String.Format("{0}: {1}", ++_compiledFileCount, sourceUnit.Path), "LOAD_COMPILED"); + + RubyCompilerOptions options = new RubyCompilerOptions(_context.RubyOptions) { + IsIncluded = true, + IsWrapped = (flags & LoadFlags.LoadIsolated) != 0, + }; + + long ts1 = Stopwatch.GetTimestamp(); + ScriptCode compiledCode = sourceUnit.Compile(options, _context.RuntimeErrorSink); + long ts2 = Stopwatch.GetTimestamp(); + Interlocked.Add(ref _ScriptCodeGenerationTimeTicks, ts2 - ts1); + + AddCompiledFile(fullPath, compiledCode); + + CompileAndRun(globalScope, compiledCode, _context.Options.InterpretedMode); + } + } + + internal object CompileAndRun(Scope/*!*/ globalScope, ScriptCode/*!*/ code, bool tryEvaluate) { + long ts1 = Stopwatch.GetTimestamp(); + code.EnsureCompiled(); + long ts2 = Stopwatch.GetTimestamp(); + Interlocked.Add(ref _ILGenerationTimeTicks, ts2 - ts1); + + return code.Run(globalScope); + } + + private ResolvedFile FindFile(Scope/*!*/ globalScope, string/*!*/ path, bool appendExtensions) { + Assert.NotNull(path); + bool isAbsolutePath; + string extension; + string home = null; + +#if !SILVERLIGHT + if (path.StartsWith("~/") || path.StartsWith("~\\")) { + try { + home = Environment.GetEnvironmentVariable("HOME"); + } catch (SecurityException) { + home = null; + } + + if (home == null) { + throw RubyExceptions.CreateArgumentError(String.Format("couldn't find HOME environment -- expanding `{0}'", path)); + } + } +#endif + + try { + if (home != null) { + path = Path.Combine(home, path.Substring(2)); + } + + isAbsolutePath = Platform.IsAbsolutePath(path); + extension = Path.GetExtension(path); + } catch (ArgumentException e) { + throw new LoadError(e.Message, e); + } + + string[] knownExtensions = DomainManager.Configuration.GetFileExtensions(); + Array.Sort(knownExtensions, DlrConfiguration.FileExtensionComparer); + + // Absolute path -> load paths not consulted. + if (isAbsolutePath) { + return ResolveFile(path, extension, appendExtensions, knownExtensions); + } + + string[] loadPaths = GetLoadPathStrings(); + + if (loadPaths.Length == 0) { + return null; + } + + // If load paths are non-empty and the path starts with .\ or ..\ then MRI also ignores the load paths. + if (path.StartsWith("./") || path.StartsWith("../") || path.StartsWith(".\\") || path.StartsWith("..\\")) { + return ResolveFile(path, extension, appendExtensions, knownExtensions); + } + + foreach (var dir in loadPaths) { + try { + ResolvedFile result = ResolveFile(Path.Combine(dir, path), extension, appendExtensions, knownExtensions); + if (result != null) { + return result; + } + } catch (ArgumentException) { + // invalid characters in path + } + } + + return null; + } + + internal string[]/*!*/ GetLoadPathStrings() { + var loadPaths = GetLoadPaths(); + var result = new string[loadPaths.Length]; + + for (int i = 0; i < loadPaths.Length; i++) { + if (loadPaths[i] == null) { + throw RubyExceptions.CreateTypeConversionError("nil", "String"); + } + + result[i] = _toStrSite.Target(_toStrSite, _context.EmptyScope, loadPaths[i]).ConvertToString(); + } + + return result; + } + + private ResolvedFile ResolveFile(string/*!*/ path, string/*!*/ extension, bool appendExtensions, string[]/*!*/ knownExtensions) { + Debug.Assert(Path.GetExtension(path) == extension); + + // MRI doesn't load file w/o .rb extension: + if (IsKnownExtension(extension, knownExtensions)) { + return GetSourceUnit(path, extension, false); + } else if (Utils.Array.IndexOf(_LibraryExtensions, extension, DlrConfiguration.FileExtensionComparer) != -1) { + if (Platform.FileExists(path)) { + return new ResolvedFile(path, null); + } + } else if (!appendExtensions) { + return GetSourceUnit(path, extension, false); + } + + if (appendExtensions) { + List matchingExtensions = GetExtensionsOfExistingFiles(path, knownExtensions); + + if (matchingExtensions.Count == 1) { + return GetSourceUnit(path + matchingExtensions[0], matchingExtensions[0], true); + } else if (matchingExtensions.Count > 1) { + Exception e = new AmbiguousFileNameException(path + matchingExtensions[0], path + matchingExtensions[1]); + throw new LoadError(e.Message, e); + } + + foreach (string libExtension in _LibraryExtensions) { + if (Platform.FileExists(path + libExtension)) { + return new ResolvedFile(path + libExtension, libExtension); + } + } + } + + return null; + } + + private static readonly string[] _LibraryExtensions = new string[] { ".dll", ".so", ".exe" }; + + private static bool IsKnownExtension(string/*!*/ extension, string[]/*!*/ knownExtensions) { + return extension.Length > 0 && Array.BinarySearch(knownExtensions, extension, DlrConfiguration.FileExtensionComparer) >= 0; + } + + private ResolvedFile GetSourceUnit(string/*!*/ path, string/*!*/ extension, bool extensionAppended) { + Assert.NotNull(path, extension); + + LanguageContext language; + if (extension.Length == 0 || !DomainManager.TryGetLanguageByFileExtension(extension, out language)) { + // Ruby by default: + language = _context; + } + + if (!DomainManager.Platform.FileExists(path)) { + return null; + } + + // TODO: default encoding: + var sourceUnit = _context.CreateFileUnit(path, BinaryEncoding.Instance, SourceCodeKind.File); + return new ResolvedFile(sourceUnit, extensionAppended ? extension : null); + } + + private List/*!*/ GetExtensionsOfExistingFiles(string/*!*/ path, IEnumerable/*!*/ extensions) { + // all extensions that could be appended to the path to get an sexisting file: + List result = new List(); + foreach (string extension in extensions) { + Debug.Assert(extension != null && extension.StartsWith(".")); + string fullPath = path + extension; + if (Platform.FileExists(fullPath)) { + result.Add(extension); + } + } + return result; + } + + #region Global Variables + + private readonly CallSite> _toStrSite = CallSite>.Create(ConvertToStrAction.Instance); + + internal object[]/*!*/ GetLoadPaths() { + lock (_loadedFiles) { + object[] result = new object[_loadPaths.Count]; + _loadPaths.CopyTo(result); + return result; + } + } + + public void SetLoadPaths(IEnumerable/*!*/ paths) { + ContractUtils.RequiresNotNullItems(paths, "paths"); + + lock (_loadPaths) { + _loadPaths.Clear(); + foreach (string path in paths) { + _loadPaths.Add(MutableString.Create(path)); + } + } + } + + internal void AddLoadPaths(IEnumerable/*!*/ paths) { + Assert.NotNullItems(paths); + + lock (_loadPaths) { + foreach (string path in paths) { + _loadPaths.Add(MutableString.Create(path)); + } + } + } + + internal void InsertLoadPaths(IEnumerable/*!*/ paths, int index) { + Assert.NotNullItems(paths); + + lock (_loadPaths) { + foreach (string path in paths) { + _loadPaths.Insert(0, MutableString.Create(path)); + } + } + } + + internal void InsertLoadPaths(IEnumerable/*!*/ paths) { + InsertLoadPaths(paths, 0); + } + + private void AddLoadedFile(MutableString/*!*/ path) { + lock (_loadedFiles) { + _loadedFiles.Add(path); + } + } + + internal object[]/*!*/ GetLoadedFiles() { + lock (_loadedFiles) { + object[] result = new object[_loadedFiles.Count]; + _loadedFiles.CopyTo(result); + return result; + } + } + + private bool AlreadyLoaded(MutableString/*!*/ path, LoadFlags flags) { + return (flags & LoadFlags.LoadOnce) != 0 && IsFileLoaded(path); + } + + private void FileLoaded(MutableString/*!*/ path, LoadFlags flags) { + if ((flags & LoadFlags.LoadOnce) != 0) { + AddLoadedFile(path); + } + } + + private bool IsFileLoaded(MutableString/*!*/ path) { + foreach (object file in GetLoadedFiles()) { + if (file == null) { + throw RubyExceptions.CreateTypeConversionError("nil", "String"); + } + + // case sensitive comparison: + if (path.Equals(_toStrSite.Target(_toStrSite, _context.EmptyScope, file))) { + return true; + } + } + + return false; + } + + #endregion + + #region IronRuby Libraries + + /// + internal void LoadBuiltins() { + Type initializerType; + try { + Assembly assembly = _context.DomainManager.Platform.LoadAssembly(GetIronRubyAssemblyLongName("IronRuby.Libraries")); + initializerType = assembly.GetType(LibraryInitializer.GetBuiltinsFullTypeName()); + } catch (Exception e) { + throw new LoadError(e.Message, e); + } + + LoadLibrary(initializerType, true); + } + + public static string/*!*/ GetIronRubyAssemblyLongName(string/*!*/ simpleName) { + ContractUtils.RequiresNotNull(simpleName, "simpleName"); +#if SIGNED + return simpleName + ", Version=" + RubyContext.IronRubyVersionString + ", Culture=neutral, PublicKeyToken=31bf3856ad364e35"; +#else + return simpleName; +#endif + } + + /// + private void LoadLibrary(Type/*!*/ initializerType, bool builtin) { + LibraryInitializer initializer; + try { + initializer = Activator.CreateInstance(initializerType) as LibraryInitializer; + } catch (TargetInvocationException e) { + throw new LoadError(e.Message, e); + } catch (Exception e) { + throw new LoadError(e.Message, e); + } + + if (initializer == null) { + throw new LoadError(String.Format("Specified type {0} is not a subclass of {1}", + initializerType.FullName, + typeof(LibraryInitializer).FullName) + ); + } + + try { + initializer.LoadModules(_context, builtin); + } catch (Exception e) { + throw new LoadError(e.Message, e); + } + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/MethodVisibility.cs b/merlin/main/languages/ruby/Ruby/Runtime/MethodVisibility.cs new file mode 100644 index 0000000000..0a6be39088 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/MethodVisibility.cs @@ -0,0 +1,92 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace IronRuby.Runtime { + [Flags] + public enum RubyMethodAttributes { + None = 0, + + Public = 1, + Private = 2, + Protected = 4, + DefaultVisibility = Public, + VisibilityMask = Public | Private | Protected, + + /// + /// Method does nothing. + /// + Empty = 8, + + MemberFlagsMask = VisibilityMask | Empty, + + /// + /// Method is defined in the type's instance method table. + /// + Instance = 16, + + /// + /// Method is defined in the type's static method table. + /// + Singleton = 32, + + /// + /// Do not trigger method_added when the method is defined. + /// + NoEvent = 64, + + PublicInstance = Public | Instance, + PrivateInstance = Private | Instance, + ProtectedInstance = Protected | Instance, + + PublicSingleton = Public | Singleton, + PrivateSingleton = Private | Singleton, + ProtectedSingleton = Protected | Singleton, + + /// + /// Set by module_function. Subsequently defined methods are private instance and public singleton. + /// + ModuleFunction = Public | Instance | Singleton, + + Default = PublicInstance, + } + + [Flags] + public enum RubyMemberFlags { + Invalid = 0, + + // visibility: + Public = RubyMethodAttributes.Public, + Private = RubyMethodAttributes.Private, + Protected = RubyMethodAttributes.Protected, + VisibilityMask = Public | Private | Protected, + + // method is empty: + Empty = RubyMethodAttributes.Empty, + + // used internally in RubyOps.DefineMethod + ModuleFunction = 16, + } + + public enum RubyMethodVisibility { + None = 0, + Public = RubyMethodAttributes.Public, + Private = RubyMethodAttributes.Private, + Protected = RubyMethodAttributes.Protected + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/ReferenceEqualityComparer.cs b/merlin/main/languages/ruby/Ruby/Runtime/ReferenceEqualityComparer.cs new file mode 100644 index 0000000000..0434777169 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/ReferenceEqualityComparer.cs @@ -0,0 +1,35 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; + +namespace IronRuby.Runtime { + public class ReferenceEqualityComparer : IEqualityComparer where T : class { + private ReferenceEqualityComparer() {} + + public static readonly ReferenceEqualityComparer Instance = new ReferenceEqualityComparer(); + + public bool Equals(T x, T y) { + return object.ReferenceEquals(x, y); + } + + public int GetHashCode(T obj) { + return RuntimeHelpers.GetHashCode(obj); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/RubyCallTargets.cs b/merlin/main/languages/ruby/Ruby/Runtime/RubyCallTargets.cs new file mode 100644 index 0000000000..d6427881ee --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/RubyCallTargets.cs @@ -0,0 +1,83 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Runtime { + // + // Ruby call targets + // + + /// + /// The delegate representing the Ruby code entry point + /// + public delegate object RubyMainDelegate(); + + /// + /// Generic delegate type for block with >RubyCallTargets.MaxSignatureSize parameters + /// + public delegate object RubyCallTargetN(params object[] args); + + static class RubyCallTargets { + + internal const int MaxSignatureSize = 10; + + internal static Type GetDelegateType(Type[] arguments, Type returnType) { + Assert.NotNull(arguments, returnType); + Type result; + + if (returnType == typeof(void)) { + switch (arguments.Length) { + case 0: return typeof(Action); + case 1: result = typeof(Action<>); break; + case 2: result = typeof(Action<,>); break; + case 3: result = typeof(Action<,,>); break; + case 4: result = typeof(Action<,,,>); break; + case 5: result = typeof(Action<,,,,>); break; + case 6: result = typeof(Action<,,,,,>); break; + case 7: result = typeof(Action<,,,,,,>); break; + case 8: result = typeof(Action<,,,,,,,>); break; + case 9: result = typeof(Action<,,,,,,,,>); break; + case 10: result = typeof(Action<,,,,,,,,,>); break; + default: + throw new NotImplementedException("Action delegate not implemented for " + arguments.Length + " arguments."); + } + } else { + arguments = ArrayUtils.Append(arguments, returnType); + switch (arguments.Length) { + case 0: throw Assert.Unreachable; + case 1: result = typeof(Func<>); break; + case 2: result = typeof(Func<,>); break; + case 3: result = typeof(Func<,,>); break; + case 4: result = typeof(Func<,,,>); break; + case 5: result = typeof(Func<,,,,>); break; + case 6: result = typeof(Func<,,,,,>); break; + case 7: result = typeof(Func<,,,,,,>); break; + case 8: result = typeof(Func<,,,,,,,>); break; + case 9: result = typeof(Func<,,,,,,,,>); break; + case 10: result = typeof(Func<,,,,,,,,,>); break; + case 11: result = typeof(Func<,,,,,,,,,,>); break; + default: + throw new NotImplementedException("Function delegate not implemented for " + arguments.Length + " arguments."); + } + } + + return result.MakeGenericType(arguments); + } + + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/RubyContext.cs b/merlin/main/languages/ruby/Ruby/Runtime/RubyContext.cs new file mode 100644 index 0000000000..c0d6e9572a --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/RubyContext.cs @@ -0,0 +1,2054 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Dynamic; +using System.Dynamic.Binders; +using System.Security; +using System.Text; +using System.Threading; +using IronRuby.Builtins; +using IronRuby.Compiler; +using IronRuby.Compiler.Ast; +using IronRuby.Compiler.Generation; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Interpretation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace IronRuby.Runtime { + /// + /// An isolation context: could be per thread/request or shared accross certain threads. + /// + public sealed class RubyContext : LanguageContext { + internal static readonly Guid RubyLanguageGuid = new Guid("F03C4640-DABA-473f-96F1-391400714DAB"); + + // MRI compliance: + public static readonly string/*!*/ MriVersion = "1.8.6"; + public static readonly string/*!*/ MriReleaseDate = "2008-05-28"; + + // IronRuby: + public const string/*!*/ IronRubyVersionString = "1.0.0.0"; + public static readonly Version IronRubyVersion = new Version(1, 0, 0, 0); + internal const string/*!*/ IronRubyDisplayName = "IronRuby 1.0 Alpha"; + internal const string/*!*/ IronRubyNames = "IronRuby;Ruby;rb"; + internal const string/*!*/ IronRubyFileExtensions = ".rb"; + + // TODO: remove + internal static RubyContext _Default; + + private readonly RubyScope/*!*/ _emptyScope; + + private RubyOptions/*!*/ _options; + private Dictionary _libraryData; + + private readonly Stopwatch _upTime; + + /// + /// $! + /// + [ThreadStatic] + private static Exception _currentException; + + /// + /// $? of type Process::Status + /// + [ThreadStatic] + private static object _childProcessExitStatus; + + /// + /// $SAFE + /// + [ThreadStatic] + private static int _currentSafeLevel; + + /// + /// $KCODE + /// + private static KCode _kcode; + + /// + /// $/, $-O + /// + private MutableString _inputSeparator; + + /// + /// $\ + /// + private MutableString _outputSeparator; + + /// + /// $;, $-F + /// + private object _stringSeparator; + + /// + /// $, + /// + private MutableString _itemSeparator; + + private readonly RuntimeErrorSink/*!*/ _runtimeErrorSink; + private readonly RubyInputProvider/*!*/ _inputProvider; + private readonly Dictionary/*!*/ _globalVariables; + private Proc _traceListener; + + [ThreadStatic] + private bool _traceListenerSuspended; + + private EqualityComparer _equalityComparer; + + /// + /// Maps CLR types to Ruby classes/modules. + /// Doesn't contain classes defined in Ruby. + /// + private readonly Dictionary/*!*/ _moduleCache; + private object ModuleCacheSyncRoot { get { return _moduleCache; } } + + /// + /// Maps CLR namespace trackers to Ruby modules. + /// + private readonly Dictionary/*!*/ _namespaceCache; + private object NamespaceCacheSyncRoot { get { return _namespaceCache; } } + + private readonly Loader/*!*/ _loader; + private Scope/*!*/ _globalScope; + private readonly List/*!*/ _fileDescriptors = new List(10); + + // classes used by runtime (we need to update initialization generator if any of these are added): + private RubyModule/*!*/ _kernelModule; + private RubyClass/*!*/ _objectClass; + private RubyClass/*!*/ _classClass; + private RubyClass/*!*/ _moduleClass; + private RubyClass/*!*/ _nilClass; + private RubyClass/*!*/ _trueClass; + private RubyClass/*!*/ _falseClass; + private RubyClass/*!*/ _exceptionClass; + private RubyClass _standardErrorClass; + + private Action/*!*/ _classSingletonTrait; + private Action/*!*/ _singletonSingletonTrait; + private Action/*!*/ _mainSingletonTrait; + + // internally set by Initializer: + public RubyModule/*!*/ KernelModule { get { return _kernelModule; } } + public RubyClass/*!*/ ObjectClass { get { return _objectClass; } } + public RubyClass/*!*/ ClassClass { get { return _classClass; } set { _classClass = value; } } + public RubyClass/*!*/ ModuleClass { get { return _moduleClass; } set { _moduleClass = value; } } + public RubyClass/*!*/ NilClass { get { return _nilClass; } set { _nilClass = value; } } + public RubyClass/*!*/ TrueClass { get { return _trueClass; } set { _trueClass = value; } } + public RubyClass/*!*/ FalseClass { get { return _falseClass; } set { _falseClass = value; } } + public RubyClass ExceptionClass { get { return _exceptionClass; } set { _exceptionClass = value; } } + public RubyClass StandardErrorClass { get { return _standardErrorClass; } set { _standardErrorClass = value; } } + + internal Action/*!*/ ClassSingletonTrait { get { return _classSingletonTrait; } } + + // Maps objects to InstanceData. The keys store weak references to the objects. + // Objects are compared by reference (identity). + // An entry can be removed as soon as the key object becomes unreachable. + private readonly InstanceDataWeakTable/*!*/ _referenceTypeInstanceData; + + // Maps values to InstanceData. The keys store value representatives. + // All objects that has the same value (value-equality) map to the same InstanceData. + // Entries cannot be ever freed since anytime in future one may create a new object whose value has already been mapped to InstanceData. + private readonly Dictionary/*!*/ _valueTypeInstanceData; + private object/*!*/ ValueTypeInstanceDataSyncRoot { get { return _valueTypeInstanceData; } } + + private RubyInstanceData/*!*/ _nilInstanceData = new RubyInstanceData(RubyUtils.NilObjectId); + + #region Dynamic Sites + + private CallSite> _methodAddedCallbackSite; + private CallSite> _methodRemovedCallbackSite; + private CallSite> _methodUndefinedCallbackSite; + private CallSite> _singletonMethodAddedCallbackSite; + private CallSite> _singletonMethodRemovedCallbackSite; + private CallSite> _singletonMethodUndefinedCallbackSite; + private CallSite> _classInheritedCallbackSite; + + private void MethodEvent(ref CallSite> site, string/*!*/ eventName, + RubyContext/*!*/ context, RubyModule/*!*/ module, string/*!*/ methodName) { + + if (site == null) { + Interlocked.CompareExchange( + ref site, + CallSite>.Create(RubyCallAction.Make(eventName, RubyCallSignature.WithImplicitSelf(1))), + null + ); + } + + site.Target(site, context, module, SymbolTable.StringToId(methodName)); + } + + private void SingletonMethodEvent(ref CallSite> site, string/*!*/ eventName, + RubyContext/*!*/ context, object obj, string/*!*/ methodName) { + + if (site == null) { + Interlocked.CompareExchange( + ref site, + CallSite>.Create(RubyCallAction.Make(eventName, RubyCallSignature.WithImplicitSelf(1))), + null + ); + } + + site.Target(site, context, obj, SymbolTable.StringToId(methodName)); + } + + private void ClassInheritedEvent(RubyClass/*!*/ superClass, RubyClass/*!*/ subClass) { + if (_classInheritedCallbackSite == null) { + Interlocked.CompareExchange( + ref _classInheritedCallbackSite, + CallSite>.Create(RubyCallAction.Make(Symbols.Inherited, RubyCallSignature.WithImplicitSelf(1) + )), + null + ); + } + + _classInheritedCallbackSite.Target(_classInheritedCallbackSite, this, superClass, subClass); + } + + #endregion + + public override LanguageOptions Options { + get { return _options; } + } + + public RubyOptions RubyOptions { + get { return _options; } + } + + internal RubyScope/*!*/ EmptyScope { + get { return _emptyScope; } + } + + // TODO: + internal Scope/*!*/ DefaultGlobalScope { + get { return DomainManager.Globals; } + } + + public Exception CurrentException { + get { return _currentException; } + set { _currentException = value; } + } + + public int CurrentSafeLevel { + get { return _currentSafeLevel; } + } + + public KCode KCode { + get { return _kcode; } + } + + public MutableString InputSeparator { + get { return _inputSeparator; } + set { _inputSeparator = value; } + } + + public MutableString OutputSeparator { + get { return _outputSeparator; } + set { _outputSeparator = value; } + } + + public object StringSeparator { + get { return _stringSeparator; } + set { _stringSeparator = value; } + } + + public MutableString ItemSeparator { + get { return _itemSeparator; } + set { _itemSeparator = value; } + } + + public Proc TraceListener { + get { return _traceListener; } + set { _traceListener = value; } + } + + public IEnumerable>/*!*/ GlobalVariables { + get { return _globalVariables; } + } + + public object/*!*/ GlobalVariablesSyncRoot { + get { return _globalVariables; } + } + + public RubyInputProvider/*!*/ InputProvider { + get { return _inputProvider; } + } + + public RuntimeErrorSink/*!*/ RuntimeErrorSink { + get { return _runtimeErrorSink; } + } + + public object ChildProcessExitStatus { + get { return _childProcessExitStatus; } + set { _childProcessExitStatus = value; } + } + + public Scope/*!*/ TopGlobalScope { + get { return _globalScope; } + } + + public Loader/*!*/ Loader { + get { return _loader; } + } + + public bool ShowCls { + get { return false; } + } + + public EqualityComparer EqualityComparer { + get { + if (_equalityComparer == null) { + Interlocked.CompareExchange(ref _equalityComparer, new EqualityComparer(this), null); + } + return _equalityComparer; + } + } + + public const int StandardInputDescriptor = 0; + public const int StandardOutputDescriptor = 1; + public const int StandardErrorOutputDescriptor = 2; + + public object StandardInput { get; set; } + public object StandardOutput { get; set; } + public object StandardErrorOutput { get; set; } + + public object Verbose { get; set; } + + public override Version LanguageVersion { + get { return IronRubyVersion; } + } + + public override Guid LanguageGuid { + get { return RubyLanguageGuid; } + } + + #region Initialization + + public RubyContext(ScriptDomainManager/*!*/ manager, IDictionary options) + : base(manager) { + ContractUtils.RequiresNotNull(manager, "manager"); + _options = new RubyOptions(options); + + _upTime = new Stopwatch(); + _upTime.Start(); + + Binder = new RubyBinder(manager); + + _runtimeErrorSink = new RuntimeErrorSink(this); + _globalVariables = new Dictionary(); + _moduleCache = new Dictionary(); + _namespaceCache = new Dictionary(); + _referenceTypeInstanceData = new InstanceDataWeakTable(); + _valueTypeInstanceData = new Dictionary(); + _inputProvider = new RubyInputProvider(this, _options.Arguments); + _globalScope = DomainManager.Globals; + _loader = new Loader(this); + _emptyScope = new RubyTopLevelScope(this); + + _currentException = null; + _currentSafeLevel = 0; + _childProcessExitStatus = null; + _inputSeparator = MutableString.Create("\n"); + _outputSeparator = null; + _stringSeparator = null; + _itemSeparator = null; + _kcode = KCode.Default; + + if (_options.Verbosity <= 0) { + Verbose = null; + } else if (_options.Verbosity == 1) { + Verbose = ScriptingRuntimeHelpers.False; + } else { + Verbose = ScriptingRuntimeHelpers.True; + } + + // TODO: + Interlocked.CompareExchange(ref _Default, this, null); + + _loader.LoadBuiltins(); + Debug.Assert(_exceptionClass != null && _standardErrorClass != null && _nilClass != null); + + Debug.Assert(_classClass != null && _moduleClass != null); + + // needs to run before globals and constants are initialized: + InitializeFileDescriptors(DomainManager.SharedIO); + + InitializeGlobalConstants(); + InitializeGlobalVariables(); + } + + /// + /// Clears thread static variables. + /// + internal static void ClearThreadStatics() { + _currentException = null; + } + + private void InitializeGlobalVariables() { + // special variables: + Runtime.GlobalVariables.DefineVariablesNoLock(this); + + // TODO: + // $-a + // $F + // $-i + // $-l + // $-p + + // $? + + + // $0 + if (_options.MainFile != null) { + DefineGlobalVariableNoLock(Symbols.CommandLineProgramPath, new GlobalVariableInfo(MutableString.Create(_options.MainFile))); + } + + DefineGlobalVariableNoLock("stdin", Runtime.GlobalVariables.InputStream); + DefineGlobalVariableNoLock("stdout", Runtime.GlobalVariables.OutputStream); + DefineGlobalVariableNoLock("defout", Runtime.GlobalVariables.OutputStream); + DefineGlobalVariableNoLock("stderr", Runtime.GlobalVariables.ErrorOutputStream); + + DefineGlobalVariableNoLock("LOADED_FEATURES", Runtime.GlobalVariables.LoadedFiles); + DefineGlobalVariableNoLock("LOAD_PATH", Runtime.GlobalVariables.LoadPath); + DefineGlobalVariableNoLock("-I", Runtime.GlobalVariables.LoadPath); + DefineGlobalVariableNoLock("-O", Runtime.GlobalVariables.InputSeparator); + DefineGlobalVariableNoLock("-F", Runtime.GlobalVariables.StringSeparator); + DefineGlobalVariableNoLock("FILENAME", Runtime.GlobalVariables.InputFileName); + + // TODO: + GlobalVariableInfo debug = new GlobalVariableInfo(DomainManager.Configuration.DebugMode); + + DefineGlobalVariableNoLock("VERBOSE", Runtime.GlobalVariables.Verbose); + DefineGlobalVariableNoLock("-v", Runtime.GlobalVariables.Verbose); + DefineGlobalVariableNoLock("-w", Runtime.GlobalVariables.Verbose); + DefineGlobalVariableNoLock("DEBUG", debug); + DefineGlobalVariableNoLock("-d", debug); + +#if !SILVERLIGHT + DefineGlobalVariableNoLock("KCODE", Runtime.GlobalVariables.KCode); + DefineGlobalVariableNoLock("-K", Runtime.GlobalVariables.KCode); + DefineGlobalVariableNoLock("SAFE", Runtime.GlobalVariables.SafeLevel); + + try { + TrySetCurrentProcessVariables(); + } catch (SecurityException) { + // nop + } +#endif + } + +#if !SILVERLIGHT + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + private void TrySetCurrentProcessVariables() { + Process process = Process.GetCurrentProcess(); + DefineGlobalVariableNoLock(Symbols.CurrentProcessId, new ReadOnlyGlobalVariableInfo(process.Id)); + } +#endif + + private void InitializeGlobalConstants() { + Debug.Assert(_objectClass != null); + + MutableString version = MutableString.Create(RubyContext.MriVersion); + MutableString platform = MutableString.Create("i386-mswin32"); // TODO: make this the correct string for MAC OS X in Silverlight + MutableString releaseDate = MutableString.Create(RubyContext.MriReleaseDate); + MutableString rubyEngine = MutableString.Create("ironruby"); + + _objectClass.SetConstant("RUBY_ENGINE", rubyEngine); + _objectClass.SetConstant("RUBY_VERSION", version); + _objectClass.SetConstant("RUBY_PATCHLEVEL", 0); + _objectClass.SetConstant("RUBY_PLATFORM", platform); + _objectClass.SetConstant("RUBY_RELEASE_DATE", releaseDate); + + _objectClass.SetConstant("VERSION", version); + _objectClass.SetConstant("PLATFORM", platform); + _objectClass.SetConstant("RELEASE_DATE", releaseDate); + + _objectClass.SetConstant("IRONRUBY_VERSION", MutableString.Create(RubyContext.IronRubyVersionString)); + + _objectClass.SetConstant("STDIN", StandardInput); + _objectClass.SetConstant("STDOUT", StandardOutput); + _objectClass.SetConstant("STDERR", StandardErrorOutput); + + object ARGF; + if (_objectClass.TryGetConstantNoAutoload("ARGF", out ARGF)) { + _inputProvider.Singleton = ARGF; + } + + _objectClass.SetConstant("ARGV", _inputProvider.CommandLineArguments); + + // File object + //_objectClass.SetConstant("DATA", null); + + // Binding + // TOPLEVEL_BINDING + + // Hash + // SCRIPT_LINES__ + } + + private void InitializeFileDescriptors(SharedIO/*!*/ io) { + Debug.Assert(_fileDescriptors.Count == 0); + StandardInput = new RubyIO(this, new ConsoleStream(io, ConsoleStreamType.Input), "r"); + StandardOutput = new RubyIO(this, new ConsoleStream(io, ConsoleStreamType.Output), "a"); + StandardErrorOutput = new RubyIO(this, new ConsoleStream(io, ConsoleStreamType.ErrorOutput), "a"); + } + + // TODO: internal + public void RegisterPrimitives( + Action/*!*/ classSingletonTrait, + Action/*!*/ singletonSingletonTrait, + Action/*!*/ mainSingletonTrait, + + Action/*!*/ kernelInstanceTrait, + Action/*!*/ objectInstanceTrait, + Action/*!*/ moduleInstanceTrait, + Action/*!*/ classInstanceTrait, + + Action/*!*/ kernelClassTrait, + Action/*!*/ objectClassTrait, + Action/*!*/ moduleClassTrait, + Action/*!*/ classClassTrait) { + + Assert.NotNull(classSingletonTrait, singletonSingletonTrait, mainSingletonTrait); + Assert.NotNull(objectInstanceTrait, kernelInstanceTrait, moduleInstanceTrait, classInstanceTrait); + Assert.NotNull(objectClassTrait, kernelClassTrait, moduleClassTrait, classClassTrait); + + _classSingletonTrait = classSingletonTrait; + _singletonSingletonTrait = singletonSingletonTrait; + _mainSingletonTrait = mainSingletonTrait; + + // inheritance hierarchy: + // + // Class + // ^ + // Object -> Object' + // ^ ^ + // Module -> Module' + // ^ ^ + // Class -> Class' + // ^ + // Object' + // + + // only Object should expose CLR methods: + TypeTracker objectTracker = ReflectionCache.GetTypeTracker(typeof(object)); + + // we need to create object class before any other since constants are added in its dictionary: + _kernelModule = new RubyModule(this, Symbols.Kernel, kernelInstanceTrait, null, null); + _objectClass = new RubyClass(this, Symbols.Object, objectTracker.Type, null, objectInstanceTrait, null, objectTracker, false, false); + _moduleClass = new RubyClass(this, Symbols.Module, typeof(RubyModule), null, moduleInstanceTrait, _objectClass, null, false, false); + _classClass = new RubyClass(this, Symbols.Class, typeof(RubyClass), null, classInstanceTrait, _moduleClass, null, false, false); + + CreateDummySingletonClassFor(_kernelModule, _moduleClass, kernelClassTrait); + CreateDummySingletonClassFor(_objectClass, _classClass, objectClassTrait); + CreateDummySingletonClassFor(_moduleClass, _objectClass.SingletonClass, moduleClassTrait); + CreateDummySingletonClassFor(_classClass, _moduleClass.SingletonClass, classClassTrait); + + _objectClass.SetMixins(new RubyModule[] { _kernelModule }); + + AddModuleToCacheNoLock(typeof(Kernel), _kernelModule); + AddModuleToCacheNoLock(objectTracker.Type, _objectClass); + AddModuleToCacheNoLock(_moduleClass.GetUnderlyingSystemType(), _moduleClass); + AddModuleToCacheNoLock(_classClass.GetUnderlyingSystemType(), _classClass); + + _objectClass.SetConstant(_moduleClass.Name, _moduleClass); + _objectClass.SetConstant(_classClass.Name, _classClass); + _objectClass.SetConstant(_objectClass.Name, _objectClass); + _objectClass.SetConstant(_kernelModule.Name, _kernelModule); + + _moduleClass.Factories = new Delegate[] { + new Func(RubyModule.CreateAnonymousModule), + }; + + _classClass.Factories = new Delegate[] { + new Func(RubyClass.CreateAnonymousClass), + }; + } + + #endregion + + #region CLR Type and Namespaces caching + + internal void AddModuleToCacheNoLock(Type/*!*/ type, RubyModule/*!*/ module) { + Assert.NotNull(type, module); + _moduleCache.Add(type, module); + } + + internal void AddNamespaceToCacheNoLock(NamespaceTracker/*!*/ namespaceTracker, RubyModule/*!*/ module) { + Assert.NotNull(namespaceTracker, module); + + _namespaceCache.Add(namespaceTracker, module); + } + + internal RubyModule/*!*/ GetOrCreateModule(NamespaceTracker/*!*/ tracker) { + Assert.NotNull(tracker); + + lock (ModuleCacheSyncRoot) { + return GetOrCreateModuleNoLock(tracker); + } + } + + internal bool TryGetModule(NamespaceTracker/*!*/ namespaceTracker, out RubyModule result) { + lock (NamespaceCacheSyncRoot) { + return _namespaceCache.TryGetValue(namespaceTracker, out result); + } + } + + internal RubyModule/*!*/ GetOrCreateModule(Type/*!*/ interfaceType) { + Debug.Assert(interfaceType != null && interfaceType.IsInterface); + + lock (ModuleCacheSyncRoot) { + return GetOrCreateModuleNoLock(interfaceType); + } + } + + internal bool TryGetModuleNoLock(Type/*!*/ type, out RubyModule result) { + return _moduleCache.TryGetValue(type, out result); + } + + internal bool TryGetClassNoLock(Type/*!*/ type, out RubyClass result) { + RubyModule module; + if (_moduleCache.TryGetValue(type, out module)) { + result = module as RubyClass; + if (result == null) { + throw new InvalidOperationException("Specified type doesn't represent a class"); + } + return true; + } else { + result = null; + return false; + } + } + + internal RubyClass/*!*/ GetOrCreateClass(Type/*!*/ type) { + Debug.Assert(type != null && !type.IsInterface); + + lock (ModuleCacheSyncRoot) { + return GetOrCreateClassNoLock(type); + } + } + + private RubyModule/*!*/ GetOrCreateModuleNoLock(NamespaceTracker/*!*/ tracker) { + Assert.NotNull(tracker); + + RubyModule result; + if (_namespaceCache.TryGetValue(tracker, out result)) { + return result; + } + + result = CreateModule(RubyUtils.GetQualifiedName(tracker), null, null, tracker, null); + _namespaceCache[tracker] = result; + return result; + } + + private RubyModule/*!*/ GetOrCreateModuleNoLock(Type/*!*/ interfaceType) { + Debug.Assert(interfaceType != null && interfaceType.IsInterface); + + RubyModule result; + if (_moduleCache.TryGetValue(interfaceType, out result)) { + return result; + } + + TypeTracker tracker = (TypeTracker)TypeTracker.FromMemberInfo(interfaceType); + result = CreateModule(RubyUtils.GetQualifiedName(interfaceType), null, null, null, tracker); + _moduleCache[interfaceType] = result; + return result; + } + + private RubyClass/*!*/ GetOrCreateClassNoLock(Type/*!*/ type) { + Debug.Assert(type != null && !type.IsInterface); + + RubyClass result; + if (TryGetClassNoLock(type, out result)) { + return result; + } + + RubyClass baseClass = GetOrCreateClassNoLock(type.BaseType); + TypeTracker tracker = (TypeTracker)TypeTracker.FromMemberInfo(type); + + result = CreateClass(RubyUtils.GetQualifiedName(type), type, null, null, null, baseClass, tracker, false, false); + + List interfaceMixins = GetDeclaredInterfaceModules(type); + if (interfaceMixins != null) { + result.SetMixins(interfaceMixins); + } + + _moduleCache[type] = result; + return result; + } + + private List GetDeclaredInterfaceModules(Type/*!*/ type) { + // TODO: + if (type.IsGenericTypeDefinition) { + return null; + } + + List interfaces = new List(); + foreach (Type iface in ReflectionUtils.GetDeclaredInterfaces(type)) { + interfaces.Add(GetOrCreateModuleNoLock(iface)); + } + + return interfaces.Count > 0 ? interfaces : null; + } + + #endregion + + #region Class and Module Factories + + internal RubyClass/*!*/ CreateClass(string name, Type type, object classSingletonOf, Action instanceTrait, Action classTrait, RubyClass/*!*/ superClass, TypeTracker tracker, bool isRubyClass, bool isSingletonClass) { + Debug.Assert(superClass != null); + + RubyClass result = new RubyClass(this, name, type, classSingletonOf, instanceTrait, superClass, tracker, isRubyClass, isSingletonClass); + CreateDummySingletonClassFor(result, superClass.SingletonClass, classTrait); + return result; + } + + internal RubyModule/*!*/ CreateModule(string name, Action instanceTrait, Action classTrait, + NamespaceTracker namespaceTracker, TypeTracker typeTracker) { + RubyModule result = new RubyModule(this, name, instanceTrait, namespaceTracker, typeTracker); + CreateDummySingletonClassFor(result, _moduleClass, classTrait); + return result; + } + + internal RubyClass/*!*/ CreateDummySingletonClassFor(RubyModule/*!*/ module, RubyClass/*!*/ superClass, Action/*!*/ trait) { + // Note that in MRI, member tables of dummy singleton are shared with the class the dummy is singleton for + // This is obviously an implementation detail leaking to the language and we don't support that for code clarity. + + // real class object and it's singleton share the tracker: + TypeTracker tracker = (module.IsSingletonClass) ? null : module.Tracker; + + RubyClass result = new RubyClass(this, null, null, module, trait, superClass, tracker, false, true); + result.SingletonClass = result; + module.SingletonClass = result; +#if DEBUG + result.DebugName = "S(" + module.DebugName + ")"; +#endif + return result; + } + + internal RubyClass/*!*/ AppendDummySingleton(RubyClass/*!*/ singleton) { + Debug.Assert(singleton.IsDummySingletonClass); + RubyClass super = ((RubyModule)singleton.SingletonClassOf).IsClass ? _classClass.SingletonClass : _moduleClass.SingletonClass; + return CreateDummySingletonClassFor(singleton, super, _singletonSingletonTrait); + } + + /// + /// Creates a singleton class for specified object unless it already exists. + /// + public RubyClass/*!*/ CreateSingletonClass(object obj) { + // TODO: maybe more general interface like IRubyObject: + RubyModule module = obj as RubyModule; + if (module != null) { + return module.CreateSingletonClass(); + } + + return CreateInstanceSingleton(obj, null, null); + } + + internal RubyClass/*!*/ CreateMainSingleton(object obj) { + return CreateInstanceSingleton(obj, _mainSingletonTrait, null); + } + + internal RubyClass/*!*/ CreateInstanceSingleton(object obj, Action instanceTrait, Action classTrait) + //^ ensures result.IsSingletonClass && !result.IsDummySingletonClass; + { + Debug.Assert(!(obj is RubyModule)); + Debug.Assert(RubyUtils.CanCreateSingleton(obj)); + + if (obj == null) { + return _nilClass; + } + + if (obj is bool) { + return (bool)obj ? _trueClass : _falseClass; + } + + RubyInstanceData data; + RubyClass result = TryGetInstanceSingletonOf(obj, out data); + if (result != null) { + Debug.Assert(!result.IsDummySingletonClass); + return result; + } + + RubyClass c = GetClassOf(obj, data); + + result = CreateClass(null, null, obj, instanceTrait, classTrait ?? _classSingletonTrait, c, null, true, true); + c.Updated("CreateInstanceSingleton"); + SetInstanceSingletonOf(obj, ref data, result); +#if DEBUG + result.DebugName = (instanceTrait != null) ? instanceTrait.Method.DeclaringType.Name : "S(" + data.ObjectId + ")"; + result.SingletonClass.DebugName = "S(" + result.DebugName + ")"; +#endif + return result; + } + + public RubyModule/*!*/ DefineModule(RubyModule/*!*/ owner, string name) { + ContractUtils.RequiresNotNull(owner, "owner"); + + RubyModule result = CreateModule(owner.MakeNestedModuleName(name), null, null, null, null); + if (name != null) { + owner.SetConstant(name, result); + } + return result; + } + + // triggers "inherited" event: + internal RubyClass/*!*/ DefineClass(RubyModule/*!*/ owner, string name, RubyClass/*!*/ superClass) { + ContractUtils.RequiresNotNull(owner, "owner"); + ContractUtils.RequiresNotNull(superClass, "superClass"); + + if (superClass.Tracker != null && superClass.Tracker.Type.ContainsGenericParameters) { + throw RubyExceptions.CreateTypeError(String.Format( + "{0}: cannot inherit from open generic instantiation {1}. Only closed instantiations are supported.", + name, superClass.Name + )); + } + + string qualifiedName = owner.MakeNestedModuleName(name); + RubyClass result = CreateClass(qualifiedName, null, null, null, null, superClass, null, true, false); + + if (name != null) { + owner.SetConstant(name, result); + } + + ClassInheritedEvent(superClass, result); + + return result; + } + + #endregion + + #region Libraries + + internal RubyModule/*!*/ DefineLibraryModule(string name, Type/*!*/ type, Action instanceTrait, + Action classTrait, RubyModule[]/*!*/ mixins) { + Assert.NotNull(type); + Assert.NotNullItems(mixins); + + lock (ModuleCacheSyncRoot) { + RubyModule module; + + if (TryGetModuleNoLock(type, out module)) { + module.IncludeLibraryModule(instanceTrait, classTrait, mixins); + return module; + } + + if (name == null) { + name = RubyUtils.GetQualifiedName(type); + } + + // setting tracker for interfaces: + TypeTracker tracker = type.IsInterface ? ReflectionCache.GetTypeTracker(type) : null; + + module = CreateModule(name, instanceTrait, classTrait, null, tracker); + module.SetMixins(mixins); + + AddModuleToCacheNoLock(type, module); + return module; + } + } + + internal RubyClass/*!*/ DefineLibraryClass(string name, Type/*!*/ type, Action instanceTrait, Action classTrait, + RubyClass super, RubyModule[]/*!*/ mixins, Delegate[] factories, bool builtin) { + + Assert.NotNull(type); + + RubyClass result; + lock (ModuleCacheSyncRoot) { + if (TryGetClassNoLock(type, out result)) { + if (super != null && super != result.SuperClass) { + // TODO: better message + throw new InvalidOperationException("Cannot change super class"); + } + + result.IncludeLibraryModule(instanceTrait, classTrait, mixins); + if (factories != null) { + result.Factories = ArrayUtils.AppendRange(result.Factories, factories); + } + + return result; + } + + if (name == null) { + name = RubyUtils.GetQualifiedName(type); + } + + if (super == null) { + super = GetOrCreateClassNoLock(type.BaseType); + } + + // setting tracker on the class makes CLR methods visible: + TypeTracker tracker = ReflectionCache.GetTypeTracker(type); + + result = CreateClass(name, type, null, instanceTrait, classTrait, super, tracker, false, false); + result.SetMixins(mixins); + result.Factories = factories; + + AddModuleToCacheNoLock(type, result); + } + + if (!builtin) { + ClassInheritedEvent(super, result); + } + + return result; + } + + #endregion + + #region Getting Modules and Classes from objects, CLR types and CLR namespaces. + + public RubyModule/*!*/ GetModule(Type/*!*/ type) { + if (type.IsInterface) { + return GetOrCreateModule(type); + } else { + return GetOrCreateClass(type); + } + } + + public RubyModule/*!*/ GetModule(NamespaceTracker/*!*/ namespaceTracker) { + return GetOrCreateModule(namespaceTracker); + } + + public RubyClass/*!*/ GetClass(Type/*!*/ type) { + ContractUtils.Requires(!type.IsInterface); + return GetOrCreateClass(type); + } + + /// + /// Gets a class of the specified object (skips any singletons). + /// + public RubyClass/*!*/ GetClassOf(object obj) + //^ ensures !result.IsSingletonClass; + { + if (obj == null) { + return _nilClass; + } + + if (obj is bool) { + return (bool)obj ? _trueClass : _falseClass; + } + + IRubyObject rubyObj = obj as IRubyObject; + if (rubyObj != null) { + return rubyObj.Class; + } + + return GetOrCreateClass(obj.GetType()); + } + + /// + /// Gets a singleton or class for obj. + /// + public RubyClass/*!*/ GetImmediateClassOf(object obj) { + RubyModule module = obj as RubyModule; + if (module != null) { + return module.SingletonClass; + } + + RubyInstanceData data; + RubyClass result = TryGetInstanceSingletonOf(obj, out data); + if (result != null) { + return result; + } + + return GetClassOf(obj, data); + } + + private RubyClass TryGetInstanceSingletonOf(object obj, out RubyInstanceData data) { + //^ ensures return != null ==> return.IsSingletonClass + Debug.Assert(!(obj is RubyModule)); + + data = TryGetInstanceData(obj); + if (data != null) { + return data.InstanceSingleton; + } + + return null; + } + + private void SetInstanceSingletonOf(object obj, ref RubyInstanceData data, RubyClass/*!*/ singleton) { + Debug.Assert(!(obj is RubyModule) && singleton != null); + + if (data == null) { + data = GetInstanceData(obj); + } + + data.ImmediateClass = singleton; + } + + private RubyClass/*!*/ GetClassOf(object obj, RubyInstanceData data) { + Debug.Assert(!(obj is RubyModule)); + RubyClass result; + + if (data != null) { + result = data.ImmediateClass; + if (result != null) { + return result.IsSingletonClass ? result.SuperClass : result; + } + + result = this.GetClassOf(obj); + data.ImmediateClass = result; + } else { + result = this.GetClassOf(obj); + } + return result; + } + + public bool IsInstanceOf(object value, object classObject) { + RubyClass c = classObject as RubyClass; + if (c != null) { + return GetClassOf(value).IsSubclassOf(c); + } + + return false; + } + + #endregion + + #region Member Resolution + + public RubyMemberInfo ResolveMethod(object target, string/*!*/ name, bool includePrivate) { + return GetImmediateClassOf(target).ResolveMethod(name, includePrivate); + } + + public RubyMemberInfo ResolveSuperMethod(object target, string/*!*/ name, RubyModule/*!*/ declaringModule) { + return GetImmediateClassOf(target).ResolveSuperMethod(name, declaringModule); + } + + public bool TryGetModule(Scope autoloadScope, string/*!*/ moduleName, out RubyModule result) { + result = _objectClass; + int pos = 0; + while (true) { + int pos2 = moduleName.IndexOf("::", pos); + string partialName; + if (pos2 < 0) { + partialName = moduleName.Substring(pos); + } else { + partialName = moduleName.Substring(pos, pos2 - pos); + pos = pos2 + 2; + } + object tmp; + if (!result.TryResolveConstant(autoloadScope, partialName, out tmp)) { + result = null; + return false; + } + result = (tmp as RubyModule); + if (result == null) { + return false; + } else if (pos2 < 0) { + return true; + } + } + } + + #endregion + + #region Object Operations + + internal RubyInstanceData TryGetInstanceData(object obj) { + IRubyObject rubyObject = obj as IRubyObject; + if (rubyObject != null) { + return rubyObject.TryGetInstanceData(); + } + + if (obj == null) { + return _nilInstanceData; + } + + RubyInstanceData result; + if (RubyUtils.IsRubyValueType(obj)) { + lock (ValueTypeInstanceDataSyncRoot) { + _valueTypeInstanceData.TryGetValue(obj, out result); + } + return result; + } + + _referenceTypeInstanceData.TryGetValue(obj, out result); + return result; + } + + internal RubyInstanceData/*!*/ GetInstanceData(object obj) { + IRubyObject rubyObject = obj as IRubyObject; + if (rubyObject != null) { + return rubyObject.GetInstanceData(); + } + + if (obj == null) { + return _nilInstanceData; + } + + RubyInstanceData result; + if (RubyUtils.IsRubyValueType(obj)) { + lock (ValueTypeInstanceDataSyncRoot) { + if (!_valueTypeInstanceData.TryGetValue(obj, out result)) { + _valueTypeInstanceData.Add(obj, result = new RubyInstanceData()); + } + } + return result; + } + + return _referenceTypeInstanceData.GetValue(obj); + } + + public string[]/*!*/ GetInstanceVariableNames(object obj) { + RubyInstanceData data = TryGetInstanceData(obj); + return (data != null) ? data.GetInstanceVariableNames() : ArrayUtils.EmptyStrings; + } + + public bool TryGetInstanceVariable(object obj, string/*!*/ name, out object value) { + RubyInstanceData data = TryGetInstanceData(obj); + if (data == null || !data.TryGetInstanceVariable(name, out value)) { + value = null; + return false; + } + return true; + } + + public void SetInstanceVariable(object obj, string/*!*/ name, object value) { + GetInstanceData(obj).SetInstanceVariable(name, value); + } + + public bool TryRemoveInstanceVariable(object obj, string/*!*/ name, out object value) { + RubyInstanceData data = TryGetInstanceData(obj); + if (data == null || !data.TryRemoveInstanceVariable(name, out value)) { + value = null; + return false; + } + return true; + } + + /// + /// Copies instance data from source to target object (i.e. instance variables, tainted, frozen flags). + /// If the source has a singleton class it's members are copied to the target as well. + /// Assumes a fresh instance of target, with no instance data. + /// + public void CopyInstanceData(object source, object target, bool copySingletonMembers) { + CopyInstanceData(source, target, false, true, copySingletonMembers); + } + + public void CopyInstanceData(object source, object target, bool copyFrozenState, bool copyTaint, bool copySingletonMembers) { + Debug.Assert(!copySingletonMembers || !(source is RubyModule)); + Debug.Assert(TryGetInstanceData(target) == null); + + var sourceData = TryGetInstanceData(source); + if (sourceData != null) { + RubyInstanceData targetData = null; + + if (copyTaint) { + if (targetData == null) targetData = GetInstanceData(target); + targetData.Tainted = sourceData.Tainted; + } + + if (copyFrozenState && sourceData.Frozen) { + if (targetData == null) targetData = GetInstanceData(target); + targetData.Freeze(); + } + + if (sourceData.HasInstanceVariables) { + if (targetData == null) targetData = GetInstanceData(target); + sourceData.CopyInstanceVariablesTo(targetData); + } + + RubyClass singleton; + if (copySingletonMembers && (singleton = sourceData.InstanceSingleton) != null) { + if (targetData == null) targetData = GetInstanceData(target); + + var dup = singleton.Duplicate(target); + dup.InitializeMembersFrom(singleton); + + SetInstanceSingletonOf(target, ref targetData, dup); + } + } + } + + public bool IsObjectFrozen(object obj) { + var state = obj as IRubyObjectState; + if (state != null) { + return state.IsFrozen; + } + + RubyInstanceData data = TryGetInstanceData(obj); + return data != null ? data.Frozen : false; + } + + public bool IsObjectTainted(object obj) { + var state = obj as IRubyObjectState; + if (state != null) { + return state.IsTainted; + } + + RubyInstanceData data = TryGetInstanceData(obj); + return data != null ? data.Tainted : false; + } + + public void FreezeObject(object obj) { + var state = obj as IRubyObjectState; + if (state != null) { + state.Freeze(); + } else { + GetInstanceData(obj).Freeze(); + } + } + + public void SetObjectTaint(object obj, bool taint) { + var state = obj as IRubyObjectState; + if (state != null) { + state.IsTainted = taint; + } else { + GetInstanceData(obj).Tainted = taint; + } + } + + public T TaintObjectBy(T obj, object taintSource) { + if (IsObjectTainted(taintSource)) { + SetObjectTaint(obj, true); + } + return obj; + } + + public T FreezeObjectBy(T obj, object frozenStateSource) { + if (IsObjectFrozen(frozenStateSource)) { + FreezeObject(obj); + } + return obj; + } + + #endregion + + #region Global Variables + + public object GetGlobalVariable(string/*!*/ name) { + object value; + TryGetGlobalVariable(null, name, out value); + return value; + } + + public void DefineGlobalVariable(string/*!*/ name, object value) { + lock (_globalVariables) { + _globalVariables[name] = new GlobalVariableInfo(value); + } + } + + public void DefineReadOnlyGlobalVariable(string/*!*/ name, object value) { + lock (_globalVariables) { + _globalVariables[name] = new ReadOnlyGlobalVariableInfo(value); + } + } + + public void DefineGlobalVariable(string/*!*/ name, GlobalVariable/*!*/ variable) { + ContractUtils.RequiresNotNull(variable, "variable"); + lock (_globalVariables) { + _globalVariables[name] = variable; + } + } + + internal void DefineGlobalVariableNoLock(string/*!*/ name, GlobalVariable/*!*/ variable) { + _globalVariables[name] = variable; + } + + public bool DeleteGlobalVariable(string/*!*/ name) { + lock (_globalVariables) { + return _globalVariables.Remove(name); + } + } + + public void AliasGlobalVariable(string/*!*/ newName, string/*!*/ oldName) { + lock (_globalVariables) { + GlobalVariable existing; + if (!_globalVariables.TryGetValue(oldName, out existing)) { + DefineGlobalVariableNoLock(oldName, existing = new GlobalVariableInfo(null, false)); + } + _globalVariables[newName] = existing; + } + } + + // null scope should be used only when accessed outside Ruby code; in that case no scoped variables are available + // we need scope here, bacause the variable might be an alias for scoped variable (regex matches): + public void SetGlobalVariable(RubyScope scope, string/*!*/ name, object value) { + lock (_globalVariables) { + GlobalVariable global; + if (_globalVariables.TryGetValue(name, out global)) { + global.SetValue(this, scope, name, value); + return; + } + + _globalVariables[name] = new GlobalVariableInfo(value); + } + } + + // null scope should be used only when accessed outside Ruby code; in that case no scoped variables are available + // we need scope here, bacause the variable might be an alias for scoped variable (regex matches): + public bool TryGetGlobalVariable(RubyScope scope, string/*!*/ name, out object value) { + lock (_globalVariables) { + GlobalVariable global; + if (_globalVariables.TryGetValue(name, out global)) { + value = global.GetValue(this, scope); + return true; + } + } + value = null; + return false; + } + + internal bool TryGetGlobalVariable(string/*!*/ name, out GlobalVariable variable) { + lock (_globalVariables) { + return _globalVariables.TryGetValue(name, out variable); + } + } + + // special global accessors: + + internal Exception SetCurrentException(object value) { + Exception e = value as Exception; + + // "$! = nil" is allowed + if (value != null && e == null) { + throw RubyExceptions.CreateTypeError("assigning non-exception to $!"); + } + + return _currentException = e; + } + + internal RubyArray GetCurrentExceptionBacktrace() { + // Under certain circumstances MRI invokes "backtrace" method, but it is quite unstable (crashes sometimes). + // Therefore we don't call the method and return the backtrace immediately. + Exception e = _currentException; + return (e != null) ? RubyExceptionData.GetInstance(e).Backtrace : null; + } + + internal RubyArray SetCurrentExceptionBacktrace(object value) { + // first check availability of the current exception: + Exception e = _currentException; + if (e == null) { + throw RubyExceptions.CreateArgumentError("$! not set"); + } + + // check assigned value: + RubyArray array = RubyUtils.AsArrayOfStrings(value); + if (value != null && array == null) { + throw RubyExceptions.CreateTypeError("backtrace must be Array of String"); + } + + RubyExceptionData.GetInstance(e).Backtrace = array; + return array; + } + + public void SetSafeLevel(int value) { + if (_currentSafeLevel <= value) { + _currentSafeLevel = value; + } else { + throw RubyExceptions.CreateSecurityError(String.Format("tried to downgrade safe level from {0} to {1}", + _currentSafeLevel, value)); + } + } + + public void SetKCode(MutableString encodingName) { + // TODO: Ruby 1.9 reports a warning: + + // we allow nil as Ruby 1.9 does (Ruby 1.8 doesn't): + if (encodingName == null) { + _kcode = KCode.Default; + } else if (encodingName.IsEmpty) { + _kcode = KCode.Binary; + } else { + switch (encodingName.GetChar(0)) { + case 'E': + case 'e': + _kcode = KCode.Euc; + break; + + case 'S': + case 's': + _kcode = KCode.Sjis; + break; + + case 'U': + case 'u': + _kcode = KCode.Utf8; + break; + + default: + _kcode = KCode.Binary; + break; + } + } + } + + public string GetKCodeName() { + switch (_kcode) { + case KCode.Default: + case KCode.Binary: return "NONE"; + case KCode.Euc: return "EUC"; + case KCode.Sjis: return "SJIS"; + case KCode.Utf8: return "UTF8"; + default: throw Assert.Unreachable; + } + } + + public Encoding/*!*/ GetKCodeEncoding() { + switch (_kcode) { + case KCode.Default: + case KCode.Binary: return BinaryEncoding.Instance; + case KCode.Euc: return Encoding.GetEncoding("EUC-JP"); + case KCode.Sjis: return Encoding.GetEncoding("SJIS"); + case KCode.Utf8: return Encoding.UTF8; + default: throw Assert.Unreachable; + } + } + + #endregion + + #region IO + + public void ReportWarning(string/*!*/ message) { + ReportWarning(message, false); + } + + public void ReportWarning(string/*!*/ message, bool isVerbose) { + _runtimeErrorSink.Add(null, message, SourceSpan.None, isVerbose ? Errors.RuntimeVerboseWarning : Errors.RuntimeWarning, Severity.Warning); + } + + public RubyIO GetDescriptor(int fileDescriptor) { + lock (_fileDescriptors) { + if (fileDescriptor < 0 || fileDescriptor >= _fileDescriptors.Count) { + return null; + } else { + return _fileDescriptors[fileDescriptor]; + } + } + } + + public int AddDescriptor(RubyIO/*!*/ descriptor) { + ContractUtils.RequiresNotNull(descriptor, "descriptor"); + + lock (_fileDescriptors) { + for (int i = 0; i < _fileDescriptors.Count; ++i) { + if (_fileDescriptors[i] == null) { + _fileDescriptors[i] = descriptor; + return i; + } + } + _fileDescriptors.Add(descriptor); + return _fileDescriptors.Count - 1; + } + } + + public void RemoveDescriptor(int descriptor) { + ContractUtils.Requires(!RubyIO.IsConsoleDescriptor(descriptor)); + + lock (_fileDescriptors) { + if (descriptor < _fileDescriptors.Count) { + throw new ArgumentException("Invalid file descriptor", "descriptor"); + } + + _fileDescriptors[descriptor] = null; + } + } + + #endregion + + #region Callbacks + + // Ruby 1.8: called after method is added, except for alias_method which calls it before + // Ruby 1.9: called before method is added + internal void MethodAdded(RubyModule/*!*/ module, string/*!*/ name) { + Assert.NotNull(module, name); + + // not called on singleton classes: + if (!module.IsSingletonClass) { + MethodEvent(ref _methodAddedCallbackSite, Symbols.MethodAdded, + this, module, name); + } else { + SingletonMethodEvent(ref _singletonMethodAddedCallbackSite, Symbols.SingletonMethodAdded, + this, ((RubyClass)module).SingletonClassOf, name); + } + } + + internal void MethodRemoved(RubyModule/*!*/ module, string/*!*/ name) { + Assert.NotNull(module, name); + + // not called on singleton classes: + if (!module.IsSingletonClass) { + MethodEvent(ref _methodRemovedCallbackSite, Symbols.MethodRemoved, + this, module, name); + } else { + SingletonMethodEvent(ref _singletonMethodRemovedCallbackSite, Symbols.SingletonMethodRemoved, + this, ((RubyClass)module).SingletonClassOf, name); + } + } + + internal void MethodUndefined(RubyModule/*!*/ module, string/*!*/ name) { + Assert.NotNull(module, name); + + // not called on singleton classes: + if (!module.IsSingletonClass) { + MethodEvent(ref _methodUndefinedCallbackSite, Symbols.MethodUndefined, + this, module, name); + } else { + SingletonMethodEvent(ref _singletonMethodUndefinedCallbackSite, Symbols.SingletonMethodUndefined, + this, ((RubyClass)module).SingletonClassOf, name); + } + } + + internal void ReportTraceEvent(string/*!*/ operation, RubyScope/*!*/ scope, RubyModule/*!*/ module, string/*!*/ name, string fileName, int lineNumber) { + if (_traceListener != null && !_traceListenerSuspended) { + + try { + _traceListenerSuspended = true; + + _traceListener.Call(new[] { + MutableString.Create(operation), // event + fileName != null ? MutableString.Create(fileName) : null, // file + ScriptingRuntimeHelpers.Int32ToObject(lineNumber), // line + SymbolTable.StringToId(name), // TODO: alias + new Binding(scope), // binding + module.IsSingletonClass ? ((RubyClass)module).SingletonClassOf : module // module + }); + } finally { + _traceListenerSuspended = false; + } + } + } + + #endregion + + #region Library Data + + private void EnsureLibraryData() { + if (_libraryData == null) { + Interlocked.CompareExchange(ref _libraryData, new Dictionary(), null); + } + } + + public bool TryGetLibraryData(object key, out object value) { + EnsureLibraryData(); + + lock (_libraryData) { + return _libraryData.TryGetValue(key, out value); + } + } + + public object GetOrCreateLibraryData(object key, Func valueFactory) { + object value; + if (TryGetLibraryData(key, out value)) { + return value; + } + + value = valueFactory(); + + object actualResult; + TryAddLibraryData(key, value, out actualResult); + return actualResult; + } + + public bool TryAddLibraryData(object key, object value, out object actualValue) { + EnsureLibraryData(); + + lock (_libraryData) { + if (_libraryData.TryGetValue(key, out actualValue)) { + return false; + } + + _libraryData.Add(key, actualValue = value); + return true; + } + } + + #endregion + + #region Parsing, Compilation + + protected override ScriptCode CompileSourceCode(SourceUnit/*!*/ sourceUnit, CompilerOptions/*!*/ options, ErrorSink/*!*/ errorSink) { + ContractUtils.RequiresNotNull(sourceUnit, "sourceUnit"); + ContractUtils.RequiresNotNull(options, "options"); + ContractUtils.RequiresNotNull(errorSink, "errorSink"); + ContractUtils.Requires(sourceUnit.LanguageContext == this, "Language mismatch."); + +#if DEBUG + if (RubyOptions.LoadFromDisk) { + string code; + Utils.Log(String.Format("{0} {1}", Options.InterpretedMode ? "interpreting" : "compiling", sourceUnit.Path ?? + ((code = sourceUnit.GetCode()).Length < 100 ? code : code.Substring(0, 100)) + .Replace('\r', ' ').Replace('\n', ' ') + ), "COMPILER"); + } +#endif + + Expression lambda = ParseSourceCode(sourceUnit, (RubyCompilerOptions)options, errorSink); + if (lambda == null) { + return null; + } + + if (Options.InterpretedMode) { + return new InterpretedScriptCode(lambda, sourceUnit); + } else { + return new ScriptCode(lambda, sourceUnit); + } + } + +#if MEASURE_AST + private static readonly object _TransformationLock = new object(); + private static readonly Dictionary _TransformationHistogram = new Dictionary(); +#endif + + private static long _ParseTimeTicks; + private static long _AstGenerationTimeTicks; + + internal Expression ParseSourceCode(SourceUnit/*!*/ sourceUnit, RubyCompilerOptions/*!*/ options, ErrorSink/*!*/ errorSink) { + long ts1, ts2; + + ts1 = Stopwatch.GetTimestamp(); + SourceUnitTree ast = new Parser().Parse(sourceUnit, options, errorSink); + ts2 = Stopwatch.GetTimestamp(); + + Interlocked.Add(ref _ParseTimeTicks, ts2 - ts1); + + if (ast == null) { + return null; + } + + Expression lambda; +#if MEASURE_AST + lock (_TransformationLock) { + var oldHistogram = System.Linq.Expressions.Expression.Histogram; + System.Linq.Expressions.Expression.Histogram = _TransformationHistogram; + try { +#endif + ts1 = Stopwatch.GetTimestamp(); + lambda = TransformTree(ast, sourceUnit, options); + ts2 = Stopwatch.GetTimestamp(); + Interlocked.Add(ref _AstGenerationTimeTicks, ts2 - ts1); + +#if MEASURE_AST + } finally { + System.Linq.Expressions.Expression.Histogram = oldHistogram; + } + } +#endif + + return lambda; + } + + internal Expression/*!*/ TransformTree(SourceUnitTree/*!*/ ast, SourceUnit/*!*/ sourceUnit, RubyCompilerOptions/*!*/ options) { + return ast.Transform( + new AstGenerator( + (RubyBinder)Binder, + options, + sourceUnit, + ast.Encoding, + Snippets.Shared.SaveSnippets, + DomainManager.Configuration.DebugMode, + RubyOptions.EnableTracing, + RubyOptions.Profile, + RubyOptions.SavePath != null + ) + ); + } + + public override CompilerOptions/*!*/ GetCompilerOptions() { + return new RubyCompilerOptions(_options); + } + + public override ErrorSink GetCompilerErrorSink() { + return _runtimeErrorSink; + } + + #endregion + + #region Global Scope + + /// + /// Creates a scope extension for DLR scopes that haven't been created by Ruby. + /// These scopes adds method_missing and const_missing methods to handle lookups to the DLR scope. + /// + public override ScopeExtension/*!*/ CreateScopeExtension(Scope/*!*/ globalScope) { + Assert.NotNull(globalScope); + + object mainObject = new Object(); + RubyClass singletonClass = CreateMainSingleton(mainObject); + + // method_missing: + singletonClass.SetMethodNoEvent(Symbols.MethodMissing, new RubyMethodGroupInfo(new Delegate[] { + new Func(RubyTopLevelScope.TopMethodMissing) + }, RubyMemberFlags.Private, singletonClass)); + + // TODO: + // TOPLEVEL_BINDING: + //singletonClass.SetMethod(Symbols.MethodMissing, new RubyMethodGroupInfo(new Delegate[] { + // new Func(RubyScope.GetTopLevelBinding) + //}, RubyMethodVisibility.Private, singletonClass, false)); + + GlobalScopeExtension result = new GlobalScopeExtension(this, globalScope, mainObject, true); + singletonClass.SetGlobalScope(result); + + return result; + } + + // + // Create a scope extension for Ruby program/file. + // + internal GlobalScopeExtension/*!*/ CreateScopeExtensionForProgram(Scope/*!*/ globalScope) { + Assert.NotNull(globalScope); + + object mainObject = new Object(); + CreateMainSingleton(mainObject); + + return new GlobalScopeExtension(this, globalScope, mainObject, false); + } + + public override int ExecuteProgram(SourceUnit/*!*/ program) { + try { + RubyCompilerOptions options = new RubyCompilerOptions(_options); + ScriptCode compiledCode = CompileSourceCode(program, options, _runtimeErrorSink); + + Scope scope = new Scope(); + scope.SetExtension(ContextId, CreateScopeExtensionForProgram(scope)); + + compiledCode.Run(scope); + } catch (SystemExit e) { + return e.Status; + } + + return 0; + } + + #endregion + + #region Shutdown + + private List _shutdownHandlers = new List(); + + public void RegisterShutdownHandler(BlockParam proc) { + lock (_shutdownHandlers) { + _shutdownHandlers.Add(proc); + } + } + + public override void Shutdown() { +#if !SILVERLIGHT + _upTime.Stop(); + + if (RubyOptions.Profile) { + var profile = Profiler.Instance.GetProfile(); + using (TextWriter writer = File.CreateText("profile.log")) { + int maxLength = 0; + long totalTicks = 0L; + var keys = new string[profile.Count]; + var values = new long[profile.Count]; + + int i = 0; + foreach (var counter in profile) { + if (counter.Key.Length > maxLength) { + maxLength = counter.Key.Length; + } + + totalTicks += counter.Value; + + keys[i] = counter.Key; + values[i] = counter.Value; + i++; + } + + Array.Sort(values, keys); + + for (int j = keys.Length - 1; j >= 0; j--) { + long ticks = values[j]; + + writer.WriteLine("{0,-" + (maxLength + 4) + "} {1,8:F0} ms {2,5:F1}%", keys[j], + new TimeSpan(Utils.DateTimeTicksFromStopwatch(ticks)).TotalMilliseconds, + (((double)ticks) / totalTicks * 100) + ); + } + + writer.WriteLine("{0,-" + (maxLength + 4) + "} {1,8:F0} ms", "total", + new TimeSpan(Utils.DateTimeTicksFromStopwatch(totalTicks)).TotalMilliseconds + ); + } + } + + if (Options.PerfStats) { + Console.WriteLine(String.Format(@" + total: {0} + parse: {1} + ast transform: {2} + script code: {3} + il: {4} + binding: {5} ({6} calls) +", + _upTime.Elapsed, + new TimeSpan(_ParseTimeTicks), + new TimeSpan(_AstGenerationTimeTicks), + new TimeSpan(Loader._ScriptCodeGenerationTimeTicks), + new TimeSpan(Loader._ILGenerationTimeTicks), +#if MEASURE + new TimeSpan(MetaAction.BindingTimeTicks), + MetaAction.BindCallCount +#else + "N/A", "N/A" +#endif +)); + +#if MEASURE_BINDING + Console.WriteLine(); + Console.WriteLine("---- MetaAction kinds ----"); + Console.WriteLine(); + + PerfTrack.DumpHistogram(MetaAction.HistogramOfKinds); + + Console.WriteLine(); + + Console.WriteLine(); + Console.WriteLine("---- MetaAction instances ----"); + Console.WriteLine(); + + PerfTrack.DumpHistogram(MetaAction.HistogramOfInstances); + + Console.WriteLine(); +#endif + +#if MEASURE_AST + Console.WriteLine(); + Console.WriteLine("---- Ruby Parser generated Expression Trees ----"); + Console.WriteLine(); + + PerfTrack.DumpHistogram(_TransformationHistogram); + + Console.WriteLine(); +#endif + PerfTrack.DumpStats(); + } +#endif + _loader.SaveCompiledCode(); + + List handlers = new List(); + + while (_shutdownHandlers.Count > 0) { + lock (_shutdownHandlers) { + handlers.AddRange(_shutdownHandlers); + _shutdownHandlers.Clear(); + } + + for (int i = handlers.Count - 1; i >= 0; --i) { + try { + object result; + handlers[i].Yield(out result); + } catch (SystemExit) { + // ignored + } + } + + handlers.Clear(); + } + } + + #endregion + + #region Exceptions + + // Formats exceptions like Ruby does, for example: + // + //repro.rb:2:in `fetch': wrong number of arguments (0 for 1) (ArgumentError) + // from repro.rb:2:in `test' + // from repro.rb:5 + public override string/*!*/ FormatException(Exception/*!*/ exception) { + var syntaxError = exception as SyntaxError; + if (syntaxError != null && syntaxError.HasLineInfo) { + return FormatErrorMessage(syntaxError.Message, null, syntaxError.File, syntaxError.Line, syntaxError.Column, syntaxError.LineSourceCode); + } + + var exceptionClass = GetClassOf(exception); + RubyExceptionData data = RubyExceptionData.GetInstance(exception); + var message = RubyExceptionData.GetClrMessage(data.Message, exceptionClass.Name); + + RubyArray backtrace = data.Backtrace; + + StringBuilder sb = new StringBuilder(); + if (backtrace != null && backtrace.Count > 0) { + sb.AppendFormat("{0}: {1} ({2})", backtrace[0], message, exceptionClass.Name); + sb.AppendLine(); + + for (int i = 1; i < backtrace.Count; i++) { + sb.Append("\tfrom ").Append(backtrace[i]).AppendLine(); + } + } else { + sb.AppendFormat("unknown: {0} ({1})", message, exceptionClass.Name).AppendLine(); + } + + // display the raw CLR exception & strack trace if requested + if (Options.ShowClrExceptions) { + sb.AppendLine().AppendLine(); + sb.AppendLine("CLR exception:"); + sb.Append(base.FormatException(exception)); + sb.AppendLine(); + } + + return sb.ToString(); + } + + internal static string/*!*/ FormatErrorMessage(string/*!*/ message, string prefix, string file, int line, int column, string lineSource) { + var sb = new StringBuilder(); + sb.Append(file ?? "unknown"); + sb.Append(':'); + sb.Append(file != null ? line : 0); + sb.Append(": "); + if (prefix != null) { + sb.Append(prefix); + sb.Append(": "); + } + sb.Append(message); + sb.AppendLine(); + if (lineSource != null) { + sb.Append(lineSource); + sb.AppendLine(); + sb.Append(' ', column); + sb.Append('^'); + sb.AppendLine(); + } + return sb.ToString(); + } + + #endregion + + #region Language Context Overrides + + public override void SetName(CodeContext context, SymbolId name, object value) { + RubyScope scope = (RubyScope)context; + RubyOps.SetLocalVariable(value, scope, SymbolTable.IdToString(name)); + } + + public override bool TryLookupName(CodeContext context, SymbolId name, out object value) { + value = RubyOps.GetLocalVariable((RubyScope)context, SymbolTable.IdToString(name)); + return true; + } + + public override TService GetService(params object[] args) { + if (typeof(TService) == typeof(TokenizerService)) { + return (TService)(object)new Tokenizer(); + } + + return base.GetService(args); + } + + public override void SetSearchPaths(ICollection/*!*/ paths) { + ContractUtils.RequiresNotNullItems(paths, "paths"); + _loader.SetLoadPaths(paths); + } + + // Might run an arbitrary user code. + public override ICollection/*!*/ GetSearchPaths() { + return _loader.GetLoadPathStrings(); + } + + public override SourceCodeReader/*!*/ GetSourceReader(Stream/*!*/ stream, Encoding/*!*/ defaultEncoding) { + ContractUtils.RequiresNotNull(stream, "stream"); + ContractUtils.RequiresNotNull(defaultEncoding, "defaultEncoding"); + ContractUtils.Requires(stream.CanRead && stream.CanSeek, "stream", "The stream must support seeking and reading"); + +#if SILVERLIGHT + return base.GetSourceReader(stream, defaultEncoding); +#else + if (_options.Compatibility <= RubyCompatibility.Ruby18) { + return new SourceCodeReader(new StreamReader(stream, BinaryEncoding.Instance, false), BinaryEncoding.Instance); + } + + long initialPosition = stream.Position; + var reader = new StreamReader(stream, BinaryEncoding.Instance, true); + + // reads preamble, if present: + reader.Peek(); + + Encoding preambleEncoding = (reader.CurrentEncoding != BinaryEncoding.Instance) ? reader.CurrentEncoding : null; + Encoding rubyPreambleEncoding = null; + + // header: + string encodingName; + if (Tokenizer.TryParseEncodingHeader(reader, out encodingName)) { + rubyPreambleEncoding = RubyEncoding.GetEncodingByRubyName(encodingName); + + // Check if the preamble encoding is an identity on preamble bytes. + // If not we shouldn't allow such encoding since the encoding of the preamble would be different from the encoding of the file. + if (!RubyEncoding.IsAsciiIdentity(rubyPreambleEncoding)) { + throw new IOException(String.Format("Encoding '{0}' is not allowed in preamble.", rubyPreambleEncoding.WebName)); + } + } + + // skip the encoding preamble, the resulting stream shouldn't detect the preamble again + // (this is necessary to override the preamble by Ruby specific one): + if (preambleEncoding != null) { + initialPosition += preambleEncoding.GetPreamble().Length; + } + + stream.Seek(initialPosition, SeekOrigin.Begin); + + var encoding = rubyPreambleEncoding ?? preambleEncoding ?? defaultEncoding; + return new SourceCodeReader(new StreamReader(stream, encoding, false), encoding); +#endif + } + + #endregion + + #region MetaObject binding + + public override GetMemberBinder/*!*/ CreateGetMemberBinder(string/*!*/ name, bool ignoreCase) { + // TODO: + if (ignoreCase) { + throw new NotSupportedException("Ignore-case lookup not supported"); + } + return new RubyGetMemberBinder(this, name); + } + + public override InvokeMemberBinder/*!*/ CreateCallBinder(string name, bool ignoreCase, params ArgumentInfo[] arguments) { + if (RubyCallSignature.HasNamedArgument(arguments)) { + return base.CreateCallBinder(name, ignoreCase, arguments); + } + // TODO: + if (ignoreCase) { + throw new NotSupportedException("Ignore-case lookup not supported"); + } + return new RubyInvokeMemberBinder(this, name, arguments); + } + + public override CreateInstanceBinder/*!*/ CreateCreateBinder(params ArgumentInfo[]/*!*/ arguments) { + if (RubyCallSignature.HasNamedArgument(arguments)) { + return base.CreateCreateBinder(arguments); + } + + return new RubyCreateInstanceBinder(this, arguments); + } + + #endregion + + #region Interpretation + + protected override void InterpretExceptionThrow(InterpreterState state, Exception exception, bool isInterpretedThrow) { + Assert.NotNull(state, exception); + if (RubyExceptionData.TryGetInstance(exception) == null) { + RubyExceptionData.AssociateInstance(exception).SetInterpretedTrace(state); + } + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/RubyExceptionData.cs b/merlin/main/languages/ruby/Ruby/Runtime/RubyExceptionData.cs new file mode 100644 index 0000000000..4a5714f1b9 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/RubyExceptionData.cs @@ -0,0 +1,402 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using Microsoft.Scripting.Utils; +using System.Security; +using System.Security.Permissions; +using IronRuby.Builtins; +using Microsoft.Scripting.Interpretation; +using System.Linq.Expressions; + +namespace IronRuby.Runtime { + /// + /// Stores extra instance data associated with Ruby exceptions + /// + [Serializable] + public class RubyExceptionData { + private static readonly object/*!*/ _DataKey = new object(); + internal const string TopLevelMethodName = "#top-level-method#"; + + // owner exception, needed for lazy initialization of message, backtrace + private Exception/*!*/ _exception; + + // if this is set to null we need to initialize it + private object _message; + + // can be set explicitly by the user (even to nil): + private RubyArray _backtrace; + + // false if _backtrace needs to be initialized. + private bool _backtraceInitialized; + + // Compiled trace: contains frames above and including the first Ruby filter/catch site that the exception was caught by: + private StackTrace _catchSiteTrace; + + // Compiled trace: contains frames starting with the throw site up to the first filter/catch that the exception was caught by: + private StackTrace _throwSiteTrace; + + private RubyExceptionData(Exception/*!*/ exception) { + _exception = exception; + } + + // Called lazily to create a Ruby backtrace. + private void CreateBacktrace() { + RubyArray result; + + int skipFrames = 0; + bool hasFileAccessPermissions = DetectFileAccessPermissions(); + +#if SILVERLIGHT // TODO: StackTrace.ctor(exception) security critical + // throw-site trace is built already: + result = _backtrace ?? new RubyArray(); +#else + result = new RubyArray(); + if (_throwSiteTrace == null) { + SetCompiledTrace(); + } + + AddBacktrace(result, _throwSiteTrace.GetFrames(), hasFileAccessPermissions, skipFrames); +#endif + if (_catchSiteTrace != null) { + // skip one frame - the catch-site frame is already included + AddBacktrace(result, _catchSiteTrace.GetFrames(), hasFileAccessPermissions, 1); + } + + _backtrace = result; + _backtraceInitialized = true; + } + + internal void SetCompiledTrace() { + Debug.Assert(!_backtraceInitialized); + +#if SILVERLIGHT // TODO: StackTrace.ctor(exception) security critical + _catchSiteTrace = new StackTrace(); + + var result = new RubyArray(); + foreach (string line in _exception.StackTrace.Split('\n')) { + string frame = line.Trim(); + if (frame.StartsWith("at ")) { + frame = frame.Substring("at ".Length); + } + + if (frame.StartsWith("_stub_") || + frame.StartsWith("Microsoft.Scripting") || + frame.StartsWith("System.Runtime") || + frame.StartsWith("IronRuby.Builtins.Kernel.RaiseException") || + frame.StartsWith("IronRuby.Builtins.Kernel.MethodMissing")) { + continue; + } + + int lineNumber = 0; + string fileName = null; + string methodName = frame; + TryParseRubyMethodName(ref methodName, ref fileName, ref lineNumber); + result.Add(FormatFrame(methodName, lineNumber, fileName)); + } + + // save partial trace: + _backtrace = result; +#else + _catchSiteTrace = new StackTrace(true); + _throwSiteTrace = new StackTrace(_exception, true); +#endif + } + + internal void SetInterpretedTrace(InterpreterState/*!*/ state) { + Debug.Assert(!_backtraceInitialized); + + // we need to copy the trace since the source locations in frames above catch site could be altered by further interpretation: + _backtrace = AddBacktrace(new RubyArray(), state, 0); + _backtraceInitialized = true; + } + + public static RubyArray/*!*/ CreateBacktrace(IEnumerable/*!*/ stackTrace, int skipFrames) { + return AddBacktrace(new RubyArray(), stackTrace, DetectFileAccessPermissions(), skipFrames); + } + + public static RubyArray/*!*/ CreateBacktrace(RubyContext/*!*/ context, int skipFrames) { + if (context.Options.InterpretedMode) { + var currentFrame = InterpreterState.Current.Value; + Debug.Assert(currentFrame != null); + return AddBacktrace(new RubyArray(), currentFrame, skipFrames); + } else { +#if SILVERLIGHT + StackTrace trace = new StackTrace(); +#else + StackTrace trace = new StackTrace(true); +#endif + return AddBacktrace(new RubyArray(), trace.GetFrames(), DetectFileAccessPermissions(), skipFrames); + } + } + + // TODO: partial trust + private static bool DetectFileAccessPermissions() { +#if SILVERLIGHT + return false; +#else + try { + new FileIOPermission(PermissionState.Unrestricted).Demand(); + return true; + } catch (SecurityException) { + return false; + } +#endif + } + + private static RubyArray/*!*/ AddBacktrace(RubyArray/*!*/ result, InterpreterState/*!*/ frame, int skipFrames) { + do { + if (skipFrames == 0) { + string methodName; + + // TODO: generalize for all languages + if (frame.ScriptCode.LanguageContext is RubyContext) { + methodName = ParseRubyMethodName(frame.Lambda.Name); + } else { + methodName = frame.Lambda.Name; + } + + result.Add(MutableString.Create(FormatFrame( + frame.ScriptCode.SourceUnit.Path, + frame.CurrentLocation.Line, + methodName + ))); + } else { + skipFrames--; + } + + frame = frame.Caller; + } while (frame != null); + + return result; + } + + private static RubyArray/*!*/ AddBacktrace(RubyArray/*!*/ result, IEnumerable stackTrace, bool hasFileAccessPermission, + int skipFrames) { + + if (stackTrace != null) { + foreach (StackFrame frame in stackTrace) { + if (IsVisibleFrame(frame.GetMethod())) { + if (skipFrames == 0) { + string methodName, file; + int line; + GetStackFrameInfo(frame, hasFileAccessPermission, out methodName, out file, out line); + result.Add(MutableString.Create(FormatFrame(file, line, methodName))); + } else { + skipFrames--; + } + } + } + } + + return result; + } + + private static string/*!*/ FormatFrame(string file, int line, string methodName) { + if (String.IsNullOrEmpty(methodName)) { + return String.Format("{0}:{1}", file, line); + } else { + return String.Format("{0}:{1}:in `{2}'", file, line, methodName); + } + } + + private static void GetStackFrameInfo(StackFrame/*!*/ frame, bool hasFileAccessPermission, + out string/*!*/ methodName, out string/*!*/ fileName, out int line) { + + MethodBase method = frame.GetMethod(); + methodName = method.Name; + +#if SILVERLIGHT + fileName = null; + line = 0; +#else + fileName = (hasFileAccessPermission) ? frame.GetFileName() : null; + line = frame.GetFileLineNumber(); +#endif + if (!TryParseRubyMethodName(ref methodName, ref fileName, ref line)) { + object[] attrs = method.GetCustomAttributes(typeof(RubyMethodAttribute), false); + if (attrs.Length > 0) { + // TODO: aliases + methodName = ((RubyMethodAttribute)attrs[0]).Name; + } + } + + if (methodName.StartsWith(TopLevelMethodName)) { + methodName = null; + } + +#if !SILVERLIGHT + if (String.IsNullOrEmpty(fileName)) { + if (method.DeclaringType != null) { + fileName = (hasFileAccessPermission) ? method.DeclaringType.Assembly.GetName().Name : null; + line = 0; + } + } +#endif + } + + // {method-name};{file-name};{line-number}${dlr-suffix} + private static bool TryParseRubyMethodName(ref string methodName, ref string fileName, ref int line) { + int dollar = methodName.IndexOf('$'); // TODO: no support in DLR + if (dollar != -1) { + string[] parts = methodName.Substring(0, dollar).Split(';'); + methodName = parts[0]; + if (parts.Length == 3) { + if (fileName == null) { + fileName = parts[1]; + } + if (line == 0) { + line = Int32.Parse(parts[2]); + } + } + return true; + } else { + return false; + } + } + + private static string ParseRubyMethodName(string/*!*/ lambdaName) { + if (lambdaName.StartsWith(TopLevelMethodName)) { + return null; + } + + int idx = lambdaName.IndexOf(';'); + if (idx < 0) { + return lambdaName; + } + + return lambdaName.Substring(0, idx); + } + + // TODO: better filtering in DLR + private static bool IsVisibleFrame(MethodBase/*!*/ method) { + // filter out the _stub_ methods + if (method.Name.StartsWith("_stub_")) { + return false; + } + + Type type = method.DeclaringType; + + if (type != null) { + string typeName = type.FullName; + if (typeName.StartsWith("System.Reflection.") || + typeName.StartsWith("System.Runtime") || + typeName.StartsWith("System.Dynamic") || + typeName.StartsWith("Microsoft.Scripting")) { + return false; + } + + // TODO: check loaded assemblies + if (type.Assembly == typeof(RubyOps).Assembly) { + return false; + } + + if (method.IsDefined(typeof(RubyStackTraceHiddenAttribute), false)) { + return false; + } + + if (type.Assembly.IsDefined(typeof(RubyLibraryAttribute), false)) { + return method.IsDefined(typeof(RubyMethodAttribute), false); + } + } + + return true; + } + + /// + /// Gets the instance data associated with the exception + /// + public static RubyExceptionData/*!*/ GetInstance(Exception/*!*/ e) { + RubyExceptionData result = TryGetInstance(e); + if (result == null) { + result = AssociateInstance(e); + } + return result; + } + + internal static RubyExceptionData/*!*/ AssociateInstance(Exception/*!*/ e) { + var result = new RubyExceptionData(e); + e.Data[_DataKey] = result; + return result; + } + + internal static RubyExceptionData TryGetInstance(Exception/*!*/ e) { + return e.Data[_DataKey] as RubyExceptionData; + } + + public object Message { + get { + if (_message == null) { + _message = MutableString.Create(_exception.Message); + } + return _message; + } + set { + ContractUtils.RequiresNotNull(value, "value"); + _message = value; + } + } + + public RubyArray Backtrace { + get { + if (!_backtraceInitialized) { + CreateBacktrace(); + } + return _backtrace; + } + set { + _backtraceInitialized = true; + _backtrace = value; + } + } + + /// + /// Called from Kernel#raise, when throwing a user created Exception objects + /// Clears out any backtrace set by the user + /// This causes the new one to be lazily created the next time it is accessed + /// + public static void ClearBacktrace(Exception e) { + IDictionary dict = e.Data; + RubyExceptionData result = e.Data[_DataKey] as RubyExceptionData; + if (result != null) { + result._backtraceInitialized = false; + result._backtrace = null; + result._catchSiteTrace = null; + } + } + + public static string/*!*/ GetClrMessage(object message, string/*!*/ className) { + // TODO: we can use to_s protocol conversion that doesn't throw an exception: + var str = message as MutableString; + return (str != null) ? str.ToString() : className; + } + + public static string/*!*/ GetClrMessage(RubyClass/*!*/ exceptionClass, object message) { + return GetClrMessage(message, exceptionClass.Name); + } + + public static Exception/*!*/ InitializeException(Exception/*!*/ exception, object message) { + // only set it if message is non-null. Otherwise, let lazy initialization create the default message from CLR exception message + if (message != null) { + RubyExceptionData.GetInstance(exception).Message = message; + } + return exception; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/RubyExceptions.cs b/merlin/main/languages/ruby/Ruby/Runtime/RubyExceptions.cs new file mode 100644 index 0000000000..291ea30bd1 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/RubyExceptions.cs @@ -0,0 +1,174 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Security; +using IronRuby.Builtins; +using IronRuby.Compiler; +using System.Runtime.InteropServices; +using System.Net.Sockets; + +namespace IronRuby.Runtime { + /// + /// Helper class for creating the corresponding .NET exceptions from the Ruby error names + /// + public static class RubyExceptions { + public static Exception/*!*/ CreateTypeError(string/*!*/ message) { + return new InvalidOperationException(message); + } + + public static Exception/*!*/ CreateTypeError(string/*!*/ message, Exception innerException) { + return new InvalidOperationException(message, innerException); + } + + public static Exception/*!*/ CreateTypeConversionError(string/*!*/ fromType, string/*!*/ toType) { + Assert.NotNull(fromType, toType); + return CreateTypeError(String.Format("can't convert {0} into {1}", fromType, toType)); + } + + public static Exception/*!*/ CreateUnexpectedTypeError(RubyContext/*!*/ context, object param, string/*!*/ type) { + return CreateTypeError(String.Format("wrong argument type {0} (expected {1})", RubyUtils.GetClassName(context, param), type)); + } + + public static Exception/*!*/ CannotConvertTypeToTargetType(RubyContext/*!*/ context, object param, string/*!*/ toType) { + Assert.NotNull(context, toType); + return CreateTypeConversionError(RubyUtils.GetClassName(context, param), toType); + } + + public static Exception/*!*/ MethodShouldReturnType(RubyContext/*!*/ context, object param, string/*!*/ method, string/*!*/ targetType) { + Assert.NotNull(context, method, targetType); + return new InvalidOperationException(String.Format("{0}#{1} should return {2}", + RubyUtils.GetClassName(context, param), method, targetType + )); + } + + public static Exception/*!*/ CreateAllocatorUndefinedError(RubyClass/*!*/ rubyClass) { + return CreateTypeError(String.Format("allocator undefined for {0}", rubyClass.Name)); + } + + public static Exception/*!*/ CreateArgumentError(string/*!*/ message) { + return new ArgumentException(message); + } + + public static Exception/*!*/ CreateArgumentError(string/*!*/ message, Exception innerException) { + return new ArgumentException(message, innerException); + } + + public static Exception/*!*/ CreateNotImplementedError(string/*!*/ message) { + return new NotImplementedError(message); + } + + public static Exception/*!*/ CreateNotImplementedError(string/*!*/ message, Exception innerException) { + return new NotImplementedError(message, innerException); + } + + public static Exception/*!*/ CreateIndexError(string/*!*/ message) { + return new IndexOutOfRangeException(message); + } + + public static Exception/*!*/ CreateIndexError(string/*!*/ message, Exception innerException) { + return new IndexOutOfRangeException(message, innerException); + } + + public static Exception/*!*/ CreateRangeError(string/*!*/ message) { + return new ArgumentOutOfRangeException(String.Empty, message); + } + + public static Exception/*!*/ CreateNameError(string/*!*/ message) { + return new MemberAccessException(message); + } + + public static Exception/*!*/ CreateLocalJumpError(string/*!*/ message) { + return new LocalJumpError(message); + } + + public static Exception/*!*/ NoBlockGiven() { + return new LocalJumpError("no block given"); + } + + public static Exception/*!*/ CreateIOError(string/*!*/ message) { + return new IOException(message); + } + + public static Exception/*!*/ CreateSystemCallError(string/*!*/ message) { + return new ExternalException(message); + } + + public static Exception/*!*/ InvalidValueForType(RubyContext/*!*/ context, object obj, string type) { + return CreateArgumentError(String.Format("invalid value for {0}: {1}", type, RubySites.Inspect(context, obj).ConvertToString())); + } + + public static Exception/*!*/ CreateUndefinedMethodError(RubyModule/*!*/ module, string/*!*/ methodName) { + return RubyExceptions.CreateNameError(String.Format("undefined method `{0}' for {2} `{1}'", + methodName, module.Name, module.IsClass ? "class" : "module")); + } + + public static Exception/*!*/ MakeCoercionError(RubyContext/*!*/ context, object self, object other) { + return MakeCoercionError(context, self, other, null); + } + + public static Exception/*!*/ MakeCoercionError(RubyContext/*!*/ context, object self, object other, Exception x) { + string selfClass = context.GetClassOf(self).Name; + string otherClass = context.GetClassOf(other).Name; + return RubyExceptions.CreateTypeError(String.Format("{0} can't be coerced into {1}", selfClass, otherClass), x); + } + + public static Exception/*!*/ MakeComparisonError(RubyContext/*!*/ context, object self, object other) { + return MakeComparisonError(context, self, other, null); + } + + public static Exception/*!*/ MakeComparisonError(RubyContext/*!*/ context, object self, object other, Exception x) { + string selfClass = context.GetClassOf(self).Name; + string otherClass = context.GetClassOf(other).Name; + return RubyExceptions.CreateArgumentError("comparison of " + selfClass+ " with " + otherClass + " failed", x); + } + + public static Exception/*!*/ CreateSecurityError(string/*!*/ message) { + throw new SecurityException(message); + } + + public static string/*!*/ FormatMethodMissingMessage(RubyContext/*!*/ context, object self, string/*!*/ name) { + return FormatMethodMissingMessage(context, self, name, "undefined method `{0}' for {1}"); + } + + private static string/*!*/ FormatMethodMissingMessage(RubyContext/*!*/ context, object self, string/*!*/ name, string/*!*/ message) { + Assert.NotNull(name); + string strObject; + + if (self == null) { + strObject = "nil:NilClass"; + } else { + strObject = RubySites.ToS(context, self).ConvertToString(); + if (!strObject.StartsWith("#")) { + strObject += ":" + RubyUtils.GetClassName(context, self); + } + } + + return String.Format(message, name, strObject); + } + + public static Exception/*!*/ CreateMethodMissing(RubyContext/*!*/ context, object self, string/*!*/ name) { + return new MissingMethodException(FormatMethodMissingMessage(context, self, name)); + } + + public static Exception/*!*/ CreatePrivateMethodCalled(RubyContext/*!*/ context, object self, string/*!*/ name) { + return new MissingMethodException(FormatMethodMissingMessage(context, self, name, "private method `{0}' called for {1}")); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/RubyOps.cs b/merlin/main/languages/ruby/Ruby/Runtime/RubyOps.cs new file mode 100644 index 0000000000..83be6015e0 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/RubyOps.cs @@ -0,0 +1,1658 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Text; +using System.Threading; +using IronRuby.Builtins; +using IronRuby.Compiler; +using IronRuby.Compiler.Generation; +using IronRuby.Runtime.Calls; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.IO; +using System.Runtime.InteropServices; + +namespace IronRuby.Runtime { + public static partial class RubyOps { + + [Emitted] + public static readonly object/*!*/ DefaultArgument = new object(); + + #region Scopes + + [Emitted] + public static RubyTopLevelScope/*!*/ CreateMainTopLevelScope(LocalsDictionary/*!*/ locals, Scope/*!*/ globalScope, LanguageContext/*!*/ language, + out object self, out RuntimeFlowControl/*!*/ rfc, string dataPath, int dataOffset) { + Assert.NotNull(locals, globalScope, language); + + GlobalScopeExtension rubyGlobalScope = (GlobalScopeExtension)language.EnsureScopeExtension(globalScope); + + RubyTopLevelScope scope = new RubyTopLevelScope(rubyGlobalScope, null, locals); + scope.Initialize(new RuntimeFlowControl(), RubyMethodAttributes.PrivateInstance, rubyGlobalScope.MainObject); + scope.SetDebugName(rubyGlobalScope.IsHosted ? "top-primary-hosted" : "top-primary"); + + // define TOPLEVEL_BINDING constant: + if (!rubyGlobalScope.IsHosted) { + var objectClass = rubyGlobalScope.Context.ObjectClass; + + objectClass.SetConstant("TOPLEVEL_BINDING", new Binding(scope)); + if (dataOffset >= 0) { + RubyFile dataFile; + if (File.Exists(dataPath)) { + dataFile = new RubyFile(rubyGlobalScope.Context, dataPath, RubyFileMode.RDONLY); + dataFile.Seek(dataOffset, SeekOrigin.Begin); + } else { + dataFile = null; + } + + objectClass.SetConstant("DATA", dataFile); + } + } + + self = scope.SelfObject; + rfc = scope.RuntimeFlowControl; + + return scope; + } + + [Emitted] + public static RubyTopLevelScope/*!*/ CreateTopLevelScope(LocalsDictionary/*!*/ locals, Scope/*!*/ globalScope, LanguageContext/*!*/ language, + out object self, out RuntimeFlowControl/*!*/ rfc) { + + RubyTopLevelScope scope = CreateTopLevelScopeInternal(locals, globalScope, language); + self = scope.SelfObject; + rfc = scope.RuntimeFlowControl; + return scope; + } + + [Emitted] + public static RubyTopLevelScope/*!*/ CreateWrappedTopLevelScope(LocalsDictionary/*!*/ locals, Scope/*!*/ globalScope, LanguageContext/*!*/ language, + out object self, out RuntimeFlowControl/*!*/ rfc) { + + RubyTopLevelScope scope = CreateTopLevelScopeInternal(locals, globalScope, language); + + RubyModule module = scope.RubyGlobalScope.Context.CreateModule(null, null, null, null, null); + + object mainObject = new Object(); + RubyClass mainSingleton = scope.RubyContext.CreateMainSingleton(mainObject); + mainSingleton.SetMixins(new RubyModule[] { module }); + + scope.SelfObject = mainObject; + scope.SetModule(module); + + self = scope.SelfObject; + rfc = scope.RuntimeFlowControl; + return scope; + } + + private static RubyTopLevelScope/*!*/ CreateTopLevelScopeInternal(LocalsDictionary/*!*/ locals, Scope/*!*/ globalScope, LanguageContext/*!*/ language) { + GlobalScopeExtension rubyGlobalScope = (GlobalScopeExtension)language.EnsureScopeExtension(globalScope); + RubyTopLevelScope result = new RubyTopLevelScope(rubyGlobalScope, null, locals); + result.Initialize(new RuntimeFlowControl(), RubyMethodAttributes.PrivateInstance, rubyGlobalScope.MainObject); + result.SetDebugName("top-level"); + + return result; + } + + [Emitted] + public static RubyModuleScope/*!*/ CreateModuleEvalScope(LocalsDictionary/*!*/ locals, RubyScope/*!*/ parent, object self, RubyModule module) { + RubyModuleScope scope = new RubyModuleScope(parent, locals, module, true); + // TODO: used to inherit parent.MethodAttributes + scope.Initialize(parent.RuntimeFlowControl, RubyMethodAttributes.PublicInstance, self); + scope.SetDebugName("top-module/instance-eval"); + return scope; + } + + [Emitted] + public static RubyModuleScope/*!*/ CreateModuleScope(LocalsDictionary/*!*/ locals, RubyScope/*!*/ parent, + RuntimeFlowControl/*!*/ rfc, RubyModule/*!*/ module) { + Assert.NotNull(locals, parent, rfc, module); + + // TODO: + RubyModuleScope scope = new RubyModuleScope(parent, locals, null, false); + scope.Initialize(rfc, RubyMethodAttributes.PublicInstance, module); + scope.SetModule(module); + scope.SetDebugName((module.IsClass ? "class" : "module") + " " + module.Name); + + return scope; + } + + [Emitted] + public static RubyMethodScope/*!*/ CreateMethodScope(LocalsDictionary/*!*/ locals, RubyScope/*!*/ parent, + RubyMethodInfo/*!*/ methodDefinition, RuntimeFlowControl/*!*/ rfc, object selfObject, Proc blockParameter) { + + Assert.NotNull(locals, parent, methodDefinition, rfc); + + RubyMethodScope scope = new RubyMethodScope(parent, locals, methodDefinition, blockParameter); + scope.Initialize(rfc, RubyMethodAttributes.PublicInstance, selfObject); + + scope.SetDebugName("method " + + methodDefinition.DefinitionName + + ((blockParameter != null) ? "&" : null) + ); + + return scope; + } + + [Emitted] + public static RubyBlockScope/*!*/ CreateBlockScope(LocalsDictionary/*!*/ locals, RubyScope/*!*/ parent, + BlockParam/*!*/ blockParam, object selfObject) { + Assert.NotNull(locals, parent, blockParam); + + RubyBlockScope scope = new RubyBlockScope(parent, locals); + // TODO: used to inherit parent.MethodAttributes + scope.Initialize(parent.RuntimeFlowControl, RubyMethodAttributes.PublicInstance, selfObject); + scope.BlockParameter = blockParam; + + return scope; + } + + [Emitted] + public static void TraceMethodCall(RubyMethodScope/*!*/ scope, string fileName, int lineNumber) { + // MRI: + // Reports DeclaringModule even though an aliased method in a sub-module is called. + // Also works for singleton module-function, which shares DeclaringModule with instance module-function. + RubyModule module = scope.Method.DeclaringModule; + scope.RubyContext.ReportTraceEvent("call", scope, module, scope.Method.DefinitionName, fileName, lineNumber); + } + + [Emitted] + public static void TraceMethodReturn(RubyMethodScope/*!*/ scope, string fileName, int lineNumber) { + RubyModule module = scope.Method.DeclaringModule; + scope.RubyContext.ReportTraceEvent("return", scope, module, scope.Method.DefinitionName, fileName, lineNumber); + } + + [Emitted] + public static void TraceBlockCall(RubyBlockScope/*!*/ scope, BlockParam/*!*/ block, string fileName, int lineNumber) { + if (block.ModuleDeclaration != null && block.SuperMethodName != null) { + scope.RubyContext.ReportTraceEvent("call", scope, block.ModuleDeclaration, block.SuperMethodName, fileName, lineNumber); + } + } + + [Emitted] + public static void TraceBlockReturn(RubyBlockScope/*!*/ scope, BlockParam/*!*/ block, string fileName, int lineNumber) { + if (block.ModuleDeclaration != null && block.SuperMethodName != null) { + scope.RubyContext.ReportTraceEvent("return", scope, block.ModuleDeclaration, block.SuperMethodName, fileName, lineNumber); + } + } + + // TODO: move to the host + [Emitted] + public static void PrintInteractiveResult(RubyScope/*!*/ scope, MutableString/*!*/ value) { + var writer = scope.RubyContext.DomainManager.SharedIO.OutputStream; + writer.WriteByte((byte)'='); + writer.WriteByte((byte)'>'); + writer.WriteByte((byte)' '); + var bytes = value.ToByteArray(); + writer.Write(bytes, 0, bytes.Length); + writer.WriteByte((byte)'\r'); + writer.WriteByte((byte)'\n'); + } + + [Emitted] + public static object GetLocalVariable(RubyScope/*!*/ scope, string/*!*/ name) { + return scope.ResolveLocalVariable(name); + } + + [Emitted] + public static object SetLocalVariable(object value, RubyScope/*!*/ scope, string/*!*/ name) { + return scope.Frame[SymbolTable.StringToId(name)] = value; + } + + #endregion + + #region Context + + [Emitted] + public static RubyContext/*!*/ GetContextFromScope(RubyScope/*!*/ scope) { + return scope.RubyContext; + } + + [Emitted] + public static RubyContext/*!*/ GetContextFromMethod(RubyMethod/*!*/ method) { + return method.Info.Context; + } + + [Emitted] + public static RubyContext/*!*/ GetContextFromBlockParam(BlockParam/*!*/ block) { + return block.RubyContext; + } + + [Emitted] + public static RubyContext/*!*/ GetContextFromProc(Proc/*!*/ proc) { + return proc.LocalScope.RubyContext; + } + + [Emitted] + public static RubyScope/*!*/ GetEmptyScope(RubyContext/*!*/ context) { + return context.EmptyScope; + } + + #endregion + + #region Blocks + + [Emitted] + public static Proc/*!*/ DefineBlock(RubyScope/*!*/ scope, RuntimeFlowControl/*!*/ runtimeFlowControl, object self, Delegate/*!*/ clrMethod, + int parameterCount, BlockSignatureAttributes attributes) { + Assert.NotNull(scope, clrMethod); + + // closes block over self and context + BlockDispatcher dispatcher = BlockDispatcher.Create(clrMethod, parameterCount, attributes); + Proc result = new Proc(ProcKind.Block, self, scope, dispatcher); + + result.Owner = runtimeFlowControl; + return result; + } + + [Emitted] + /// + /// Used in a method call with a block to reset proc-kind when the call is retried + /// + public static void InitializeBlock(Proc/*!*/ proc) { + Assert.NotNull(proc); + proc.Kind = ProcKind.Block; + } + + #endregion + + #region Yield: TODO: generate + + [Emitted] + public static object Yield0(object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.Invoke(blockParam, self); + } catch(EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + [Emitted] + public static object Yield1(object arg1, object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.Invoke(blockParam, self, arg1); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + // YieldNoAutoSplat1 uses InvokeNoAutoSplat instead of Invoke (used by Call1) + internal static object YieldNoAutoSplat1(object arg1, object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.InvokeNoAutoSplat(blockParam, self, arg1); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + [Emitted] + public static object Yield2(object arg1, object arg2, object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.Invoke(blockParam, self, arg1, arg2); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + [Emitted] + public static object Yield3(object arg1, object arg2, object arg3, object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.Invoke(blockParam, self, arg1, arg2, arg3); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + [Emitted] + public static object Yield4(object arg1, object arg2, object arg3, object arg4, object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.Invoke(blockParam, self, arg1, arg2, arg3, arg4); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + [Emitted] + public static object YieldN(object[]/*!*/ args, object self, BlockParam/*!*/ blockParam) { + Debug.Assert(args.Length > BlockDispatcher.MaxBlockArity); + + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.Invoke(blockParam, self, args); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + [Emitted] + public static object YieldSplat0(object splattee, object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.InvokeSplat(blockParam, self, splattee); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + [Emitted] + public static object YieldSplat1(object arg1, object splattee, object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.InvokeSplat(blockParam, self, arg1, splattee); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + [Emitted] + public static object YieldSplat2(object arg1, object arg2, object splattee, object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.InvokeSplat(blockParam, self, arg1, arg2, splattee); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + [Emitted] + public static object YieldSplat3(object arg1, object arg2, object arg3, object splattee, object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.InvokeSplat(blockParam, self, arg1, arg2, arg3, splattee); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + [Emitted] + public static object YieldSplat4(object arg1, object arg2, object arg3, object arg4, object splattee, object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.InvokeSplat(blockParam, self, arg1, arg2, arg3, arg4, splattee); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + [Emitted] + public static object YieldSplatN(object[]/*!*/ args, object splattee, object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.InvokeSplat(blockParam, self, args, splattee); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + + [Emitted] + public static object YieldSplatNRhs(object[]/*!*/ args, object splattee, object rhs, object self, BlockParam/*!*/ blockParam) { + object result; + var proc = blockParam.Proc; + try { + result = proc.Dispatcher.InvokeSplatRhs(blockParam, self, args, splattee, rhs); + } catch (EvalUnwinder evalUnwinder) { + result = blockParam.GetUnwinderResult(evalUnwinder); + } + + return result; + } + #endregion + + #region Methods + + [Emitted] // MethodDeclaration: + public static RubyMethodInfo/*!*/ DefineMethod(object targetOrSelf, object/*!*/ ast, RubyScope/*!*/ scope, + bool hasTarget, string/*!*/ name, Delegate/*!*/ clrMethod, int mandatory, int optional, bool hasUnsplatParameter) { + + Assert.NotNull(ast, scope, clrMethod, name); + + RubyModule instanceOwner, singletonOwner; + RubyMemberFlags instanceFlags, singletonFlags; + + if (hasTarget) { + if (!RubyUtils.CanCreateSingleton(targetOrSelf)) { + throw RubyExceptions.CreateTypeError("can't define singleton method for literals"); + } + + instanceOwner = null; + instanceFlags = RubyMemberFlags.Invalid; + singletonOwner = scope.RubyContext.CreateSingletonClass(targetOrSelf); + singletonFlags = RubyMemberFlags.Public; + } else { + // TODO: ??? + var attributesScope = scope.GetMethodAttributesDefinitionScope(); + //var attributesScope = scope; + if ((attributesScope.MethodAttributes & RubyMethodAttributes.ModuleFunction) == RubyMethodAttributes.ModuleFunction) { + // Singleton module-function's scope points to the instance method's RubyMemberInfo. + // This affects: + // 1) super call + // Super call is looking for Method.DeclaringModule while searching MRO, which would fail if the singleton module-function + // was in MRO. Since module-function can only be used on module the singleton method could only be on module's singleton. + // Module's singleton is never part of MRO so we are safe. + // 2) trace + // Method call trace reports non-singleton module. + + // MRI 1.8: instance method owner is self -> it is possible (via define_method) to define m.f. on a class (bug) + // MRI 1.9: instance method owner GetMethodDefinitionOwner + // MRI allows to define m.f. on classes but then doesn't work correctly with it. + instanceOwner = scope.GetMethodDefinitionOwner(); + if (instanceOwner.IsClass) { + throw RubyExceptions.CreateTypeError("A module function cannot be defined on a class."); + } + + instanceFlags = RubyMemberFlags.ModuleFunction | RubyMemberFlags.Private; + singletonOwner = instanceOwner.SingletonClass; + singletonFlags = RubyMemberFlags.ModuleFunction | RubyMemberFlags.Public; + } else { + instanceOwner = scope.GetMethodDefinitionOwner(); + instanceFlags = (RubyMemberFlags)RubyUtils.GetSpecialMethodVisibility(attributesScope.Visibility, name); + singletonOwner = null; + singletonFlags = RubyMemberFlags.Invalid; + } + } + + RubyMethodInfo instanceMethod = null, singletonMethod = null; + + if (instanceOwner != null) { + SetMethod(instanceMethod = + new RubyMethodInfo(ast, clrMethod, instanceOwner, name, mandatory, optional, hasUnsplatParameter, instanceFlags) + ); + } + + if (singletonOwner != null) { + SetMethod(singletonMethod = + new RubyMethodInfo(ast, clrMethod, singletonOwner, name, mandatory, optional, hasUnsplatParameter, singletonFlags) + ); + } + + // the method's scope saves the result => singleton module-function uses instance-method + return instanceMethod ?? singletonMethod; + } + + private static void SetMethod(RubyMethodInfo/*!*/ method) { + var owner = method.DeclaringModule; + + // Do not trigger the add-method event just yet, we need to assign the result into closure before executing any user code. + // If the method being defined is "method_added" itself, we would call that method before the info gets assigned to the closure. + owner.SetMethodNoEvent(method.DefinitionName, method); + + // expose RubyMethod in the scope (the method is bound to the main singleton instance): + if (owner.GlobalScope != null) { + owner.GlobalScope.Scope.SetName( + SymbolTable.StringToId(method.DefinitionName), + new RubyMethod(owner.GlobalScope.MainObject, method, method.DefinitionName) + ); + } + } + + [Emitted] + public static object MethodDefined(RubyMethodInfo/*!*/ method) { + method.Context.MethodAdded(method.DeclaringModule, method.DefinitionName); + + if (method.IsModuleFunction) { + Debug.Assert(!method.DeclaringModule.IsClass); + method.Context.MethodAdded(method.DeclaringModule.SingletonClass, method.DefinitionName); + } + + return null; + } + + [Emitted] // AliasStatement: + public static void AliasMethod(RubyScope/*!*/ scope, string/*!*/ newName, string/*!*/ oldName) { + // MRI 1.8: if (newName == oldName) return; + // MRI 1.9: no check + + // lexical lookup: + RubyModule owner = scope.GetMethodDefinitionOwner(); + RubyMemberInfo method = owner.ResolveMethodFallbackToObject(oldName, true); + if (method != null) { + owner.AddMethodAlias(newName, method); + return; + } + + throw RubyExceptions.CreateUndefinedMethodError(owner, oldName); + } + + [Emitted] // UndefineMethod: + public static void UndefineMethod(RubyScope/*!*/ scope, string/*!*/ name) { + RubyModule owner = scope.GetInnerMostModule(); + if (owner.ResolveMethod(name, true) == null) { + throw RubyExceptions.CreateUndefinedMethodError(owner, name); + } + + owner.UndefineMethod(name); + } + + [Emitted] // MethodCall: + public static bool IsDefinedMethod(object self, RubyScope/*!*/ scope, string/*!*/ name) { + // MRI: this is different from UndefineMethod, it behaves like Kernel#method (i.e. doesn't use lexical scope): + // TODO: visibility + return scope.RubyContext.ResolveMethod(self, name, true) != null; + } + + #endregion + + #region Modules + + [Emitted] + public static RubyModule/*!*/ DefineGlobalModule(RubyScope/*!*/ scope, string/*!*/ name) { + return RubyUtils.DefineModule(scope.GlobalScope, scope.Top.TopModuleOrObject, name); + } + + [Emitted] + public static RubyModule/*!*/ DefineNestedModule(RubyScope/*!*/ scope, string/*!*/ name) { + return RubyUtils.DefineModule(scope.GlobalScope, scope.GetInnerMostModule(), name); + } + + [Emitted] + public static RubyModule/*!*/ DefineModule(RubyScope/*!*/ scope, object target, string/*!*/ name) { + Assert.NotNull(scope); + return RubyUtils.DefineModule(scope.GlobalScope, RubyUtils.GetModuleFromObject(scope.RubyContext, target), name); + } + + [Emitted] + public static RubyModule/*!*/ ConvertNamespaceToModule(RubyScope/*!*/ scope, NamespaceTracker/*!*/ tracker) { + return scope.RubyContext.GetOrCreateModule(tracker); + } + + #endregion + + #region Classes + + [Emitted] + public static RubyClass/*!*/ DefineSingletonClass(RubyScope/*!*/ scope, object obj) { + if (obj != null && !(obj is bool) && RubyUtils.IsRubyValueType(obj)) { + throw RubyExceptions.CreateTypeError(String.Format("no virtual class for {0}", scope.RubyContext.GetClassOf(obj).Name)); + } + return scope.RubyContext.CreateSingletonClass(obj); + } + + [Emitted] + public static RubyModule/*!*/ DefineGlobalClass(RubyScope/*!*/ scope, string/*!*/ name, object superClassObject) { + return RubyUtils.DefineClass(scope.GlobalScope, scope.Top.TopModuleOrObject, name, superClassObject); + } + + [Emitted] + public static RubyModule/*!*/ DefineNestedClass(RubyScope/*!*/ scope, string/*!*/ name, object superClassObject) { + return RubyUtils.DefineClass(scope.GlobalScope, scope.GetInnerMostModule(), name, superClassObject); + } + + [Emitted] + public static RubyModule/*!*/ DefineClass(RubyScope/*!*/ scope, object target, string/*!*/ name, object superClassObject) { + return RubyUtils.DefineClass(scope.GlobalScope, RubyUtils.GetModuleFromObject(scope.RubyContext, target), name, superClassObject); + } + + #endregion + + #region Constants + + [Emitted] // ConstantVariable: + public static object GetGlobalConstant(RubyScope/*!*/ scope, string/*!*/ name) { + return RubyUtils.GetConstant(scope, scope.RubyContext.ObjectClass, name, false); + } + + [Emitted] // ConstantVariable: + public static object GetUnqualifiedConstant(RubyScope/*!*/ scope, string/*!*/ name) { + return scope.ResolveConstant(true, name); + } + + [Emitted] // ConstantVariable: + public static object GetQualifiedConstant(object target, RubyScope/*!*/ scope, string/*!*/ name) { + return RubyUtils.GetConstant(scope, RubyUtils.GetModuleFromObject(scope.RubyContext, target), name, false); + } + + + [Emitted] // ConstantVariable: + public static bool IsDefinedGlobalConstant(RubyScope/*!*/ scope, string/*!*/ name) { + object result; + return scope.RubyContext.ObjectClass.TryResolveConstantNoAutoload(name, out result); + } + + [Emitted] // ConstantVariable: + public static bool IsDefinedUnqualifiedConstant(RubyScope/*!*/ scope, string/*!*/ name) { + object result; + return scope.TryResolveConstant(false, name, out result); + } + + [Emitted] // ConstantVariable: + public static bool IsDefinedQualifiedConstant(object target, RubyScope/*!*/ scope, string/*!*/ name) { + object result; + RubyModule module = target as RubyModule; + if (module == null) { + return false; + } + return module.TryResolveConstantNoAutoload(name, out result); + } + + + [Emitted] // ConstantVariable: + public static object SetGlobalConstant(object value, RubyScope/*!*/ scope, string/*!*/ name) { + RubyUtils.SetConstant(scope.RubyContext.ObjectClass, name, value); + return value; + } + + [Emitted] // ConstantVariable: + public static object SetUnqualifiedConstant(object value, RubyScope/*!*/ scope, string/*!*/ name) { + RubyUtils.SetConstant(scope.GetInnerMostModule(), name, value); + return value; + } + + [Emitted] // ConstantVariable: + public static object SetQualifiedConstant(object value, object target, RubyScope/*!*/ scope, string/*!*/ name) { + RubyUtils.SetConstant(RubyUtils.GetModuleFromObject(scope.RubyContext, target), name, value); + return value; + } + + #endregion + + // MakeArray* + public const int OptimizedOpCallParamCount = 5; + + #region MakeArray + + [Emitted] + public static RubyArray/*!*/ MakeArray0() { + return new RubyArray(0); + } + + [Emitted] + public static RubyArray/*!*/ MakeArray1(object item1) { + RubyArray result = new RubyArray(1); + result.Add(item1); + return result; + } + + [Emitted] + public static RubyArray/*!*/ MakeArray2(object item1, object item2) { + RubyArray result = new RubyArray(2); + result.Add(item1); + result.Add(item2); + return result; + } + + [Emitted] + public static RubyArray/*!*/ MakeArray3(object item1, object item2, object item3) { + RubyArray result = new RubyArray(3); + result.Add(item1); + result.Add(item2); + result.Add(item3); + return result; + } + + [Emitted] + public static RubyArray/*!*/ MakeArray4(object item1, object item2, object item3, object item4) { + RubyArray result = new RubyArray(4); + result.Add(item1); + result.Add(item2); + result.Add(item3); + result.Add(item4); + return result; + } + + [Emitted] + public static RubyArray/*!*/ MakeArray5(object item1, object item2, object item3, object item4, object item5) { + RubyArray result = new RubyArray(5); + result.Add(item1); + result.Add(item2); + result.Add(item3); + result.Add(item4); + result.Add(item5); + return result; + } + + [Emitted] + public static RubyArray/*!*/ MakeArrayN(object[]/*!*/ items) { + Debug.Assert(items != null); + return new RubyArray(items); + } + + #endregion + + #region MakeHash + + [Emitted] + public static Hash/*!*/ MakeHash0(RubyScope/*!*/ scope) { + return new Hash(scope.RubyContext.EqualityComparer, 0); + } + + [Emitted] + public static Hash/*!*/ MakeHash(RubyScope/*!*/ scope, object[]/*!*/ items) { + return RubyUtils.MakeHash(scope.RubyContext, items); + } + + #endregion + + #region Array + + [Emitted] + public static List/*!*/ SplatAppend(List/*!*/ array, object splattee) { + List list = splattee as List; + if (list != null) { + array.AddRange(list); + } else { + array.Add(splattee); + } + return array; + } + + [Emitted] + public static object Splat(object/*!*/ value) { + List list = value as List; + if (list == null) { + return value; + } + + if (list.Count <= 1) { + return (list.Count > 0) ? list[0] : null; + } + + return list; + } + + [Emitted] + public static object SplatPair(object value, object array) { + List list = array as List; + if (list != null) { + if (list.Count == 0) { + return value; + } + + RubyArray result = new RubyArray(list.Count + 1); + result.Add(value); + result.AddRange(list); + return result; + } + + return MakeArray2(value, array); + } + + [Emitted] + public static RubyArray/*!*/ Unsplat(object/*!*/ value) { + RubyArray list = value as RubyArray; + if (list == null) { + list = new RubyArray(1); + list.Add(value); + } + return list; + } + + [Emitted] // parallel assignment: + public static object GetArrayItem(List/*!*/ array, int index) { + Debug.Assert(index >= 0); + return index < array.Count ? array[index] : null; + } + + [Emitted] // parallel assignment: + public static List/*!*/ GetArraySuffix(List/*!*/ array, int startIndex) { + int size = array.Count - startIndex; + if (size > 0) { + RubyArray result = new RubyArray(size); + for (int i = startIndex; i < array.Count; i++) { + result.Add(array[i]); + } + return result; + } else { + return new RubyArray(); + } + } + + #endregion + + #region Global Variables + + [Emitted] + public static object GetGlobalVariable(RubyScope/*!*/ scope, string/*!*/ name) { + object value; + // no error reported if the variable doesn't exist: + scope.RubyContext.TryGetGlobalVariable(scope, name, out value); + return value; + } + + [Emitted] + public static bool IsDefinedGlobalVariable(RubyScope/*!*/ scope, string/*!*/ name) { + GlobalVariable variable; + return scope.RubyContext.TryGetGlobalVariable(name, out variable) && variable.IsDefined; + } + + [Emitted] + public static object SetGlobalVariable(object value, RubyScope/*!*/ scope, string/*!*/ name) { + scope.RubyContext.SetGlobalVariable(scope, name, value); + return value; + } + + [Emitted] + public static void AliasGlobalVariable(RubyScope/*!*/ scope, string/*!*/ newName, string/*!*/ oldName) { + scope.RubyContext.AliasGlobalVariable(newName, oldName); + } + + #endregion + + #region Regex + + [Emitted] //RegexMatchReference: + public static MutableString GetCurrentMatchGroup(RubyScope/*!*/ scope, int index) { + Debug.Assert(index >= 0); + return scope.GetInnerMostClosureScope().GetCurrentMatchGroup(index); + } + + [Emitted] //RegexMatchReference: + public static MatchData GetCurrentMatchData(RubyScope/*!*/ scope) { + return scope.GetInnerMostClosureScope().CurrentMatch; + } + + [Emitted] //RegexMatchReference: + public static MutableString GetCurrentMatchLastGroup(RubyScope/*!*/ scope) { + return scope.GetInnerMostClosureScope().GetCurrentMatchLastGroup(); + } + + [Emitted] //RegexMatchReference: + public static MutableString GetCurrentMatchPrefix(RubyScope/*!*/ scope) { + MatchData match = scope.GetInnerMostClosureScope().CurrentMatch; + if (match == null) { + return null; + } + return match.OriginalString.GetSlice(0, match.Index).TaintBy(match.OriginalString); + } + + [Emitted] //RegexMatchReference: + public static MutableString GetCurrentMatchSuffix(RubyScope/*!*/ scope) { + MatchData match = scope.GetInnerMostClosureScope().CurrentMatch; + if (match == null) { + return null; + } + return match.OriginalString.GetSlice(match.Index + match.Length); + } + + [Emitted] //RegularExpression: + public static bool MatchLastInputLine(RubyRegex/*!*/ regex, RubyScope/*!*/ scope) { + var str = scope.GetInnerMostClosureScope().LastInputLine as MutableString; + return (str != null) ? RubyRegex.SetCurrentMatchData(scope, regex, str) != null : false; + } + + [Emitted] //MatchExpression: + public static object MatchString(MutableString str, RubyRegex/*!*/ regex, RubyScope/*!*/ scope) { + var match = RubyRegex.SetCurrentMatchData(scope, regex, str); + return (match != null) ? ScriptingRuntimeHelpers.Int32ToObject(match.Index) : null; + } + + #endregion + + public const int MakeStringParamCount = 2; + public const char SuffixEncoded = 'E'; + public const char SuffixBinary = 'B'; + public const char SuffixMutable = 'M'; + public const char SuffixUTF8 = 'U'; + + // TODO: encodings + #region CreateRegex + + [Emitted] + public static RubyRegex/*!*/ CreateRegexB(string/*!*/ str1, RubyRegexOptions options) { + return new RubyRegex(str1, options); + } + + [Emitted] + public static RubyRegex/*!*/ CreateRegexU(string/*!*/ str1, RubyRegexOptions options) { + return new RubyRegex(str1, options); + } + + [Emitted] + public static RubyRegex/*!*/ CreateRegexE(string/*!*/ str1, int codepage, RubyRegexOptions options) { + return new RubyRegex(str1, options); + } + + [Emitted] + public static RubyRegex/*!*/ CreateRegexM(MutableString str1, RubyRegexOptions options) { + return new RubyRegex(MutableString.CreateInternal(str1), options); + } + + [Emitted] + public static RubyRegex/*!*/ CreateRegexBM(string/*!*/ str1, MutableString str2, RubyRegexOptions options) { + return new RubyRegex(MutableString.Create(str1).Append(str2), options); + } + + [Emitted] + public static RubyRegex/*!*/ CreateRegexUM(string/*!*/ str1, MutableString str2, RubyRegexOptions options) { + return new RubyRegex(MutableString.Create(str1).Append(str2), options); + } + + [Emitted] + public static RubyRegex/*!*/ CreateRegexEM(string/*!*/ str1, MutableString str2, int codepage, RubyRegexOptions options) { + return new RubyRegex(MutableString.Create(str1).Append(str2), options); + } + + [Emitted] + public static RubyRegex/*!*/ CreateRegexMB(MutableString str1, string/*!*/ str2, RubyRegexOptions options) { + return new RubyRegex(MutableString.CreateInternal(str1).Append(str2), options); + } + + [Emitted] + public static RubyRegex/*!*/ CreateRegexMU(MutableString str1, string/*!*/ str2, RubyRegexOptions options) { + return new RubyRegex(MutableString.CreateInternal(str1).Append(str2), options); + } + + [Emitted] + public static RubyRegex/*!*/ CreateRegexME(MutableString str1, string/*!*/ str2, int codepage, RubyRegexOptions options) { + return new RubyRegex(MutableString.CreateInternal(str1).Append(str2), options); + } + + [Emitted] + public static RubyRegex/*!*/ CreateRegexMM(MutableString str1, MutableString str2, RubyRegexOptions options) { + return new RubyRegex(MutableString.CreateInternal(str1).Append(str2), options); + } + + [Emitted] + public static RubyRegex/*!*/ CreateRegexN(object[]/*!*/ strings, int codepage, RubyRegexOptions options) { + return new RubyRegex(ConcatenateStrings(strings), options); + } + + #endregion + + // TODO: encodings + #region CreateMutableString + + [Emitted] + public static MutableString/*!*/ CreateMutableStringB(string/*!*/ str) { + return MutableString.Create(str, BinaryEncoding.Instance); + } + + [Emitted] + public static MutableString/*!*/ CreateMutableStringU(string/*!*/ str) { + return MutableString.Create(str, Encoding.UTF8); + } + + [Emitted] + public static MutableString/*!*/ CreateMutableStringE(string/*!*/ str1, int codepage) { + return MutableString.Create(str1, RubyEncoding.GetEncoding(codepage)); + } + + [Emitted] + public static MutableString/*!*/ CreateMutableStringM(MutableString str1) { + return MutableString.CreateInternal(str1); + } + + [Emitted] + public static MutableString/*!*/ CreateMutableStringBM(string/*!*/ str1, MutableString str2) { + // TODO: encoding + return MutableString.CreateMutable(str1, BinaryEncoding.Instance).Append(str2); + } + + [Emitted] + public static MutableString/*!*/ CreateMutableStringUM(string/*!*/ str1, MutableString str2) { + // TODO: encoding + return MutableString.CreateMutable(str1, BinaryEncoding.UTF8).Append(str2); + } + + [Emitted] + public static MutableString/*!*/ CreateMutableStringEM(string/*!*/ str1, MutableString str2, int codepage) { + return MutableString.CreateMutable(str1, RubyEncoding.GetEncoding(codepage)).Append(str2); + } + + [Emitted] + public static MutableString/*!*/ CreateMutableStringMB(MutableString str1, string/*!*/ str2) { + // TODO: encoding + return MutableString.CreateInternal(str1).Append(str2); + } + + [Emitted] + public static MutableString/*!*/ CreateMutableStringMU(MutableString str1, string/*!*/ str2) { + // TODO: encoding + return MutableString.CreateInternal(str1).Append(str2); + } + + [Emitted] + public static MutableString/*!*/ CreateMutableStringME(MutableString str1, string/*!*/ str2, int codepage) { + // TODO: encoding + return MutableString.CreateInternal(str1).Append(str2); + } + + [Emitted] + public static MutableString/*!*/ CreateMutableStringMM(MutableString str1, MutableString str2) { + // TODO: encoding + return MutableString.CreateInternal(str1).Append(str2); + } + + [Emitted] + public static MutableString/*!*/ CreateMutableStringN(object[]/*!*/ strings, int codepage) { + // TODO: encodings of literals in the array needs to be handled + return ConcatenateStrings(strings); + } + + #endregion + + // TODO: encodings + #region CreateSymbol + + [Emitted] + public static SymbolId/*!*/ CreateSymbolB(string/*!*/ str1) { + Debug.Assert(str1.Length > 0); + return SymbolTable.StringToId(str1); + } + + [Emitted] + public static SymbolId/*!*/ CreateSymbolU(string/*!*/ str1) { + Debug.Assert(str1.Length > 0); + return SymbolTable.StringToId(str1); + } + + [Emitted] + public static SymbolId/*!*/ CreateSymbolE(string/*!*/ str1, int codepage) { + Debug.Assert(str1.Length > 0); + return SymbolTable.StringToId(str1); + } + + [Emitted] + public static SymbolId/*!*/ CreateSymbolM(MutableString str1) { + if (MutableString.IsNullOrEmpty(str1)) { + throw RubyExceptions.CreateArgumentError("interning empty string"); + } + return SymbolTable.StringToId(str1.ConvertToString()); + } + + [Emitted] + public static SymbolId/*!*/ CreateSymbolBM(string/*!*/ str1, MutableString str2) { + Debug.Assert(str1.Length > 0); + return (str2 != null) ? SymbolTable.StringToId(str1 + str2.ConvertToString()) : SymbolTable.StringToId(str1); + } + + [Emitted] + public static SymbolId/*!*/ CreateSymbolUM(string/*!*/ str1, MutableString str2) { + Debug.Assert(str1.Length > 0); + return (str2 != null) ? SymbolTable.StringToId(str1 + str2.ConvertToString()) : SymbolTable.StringToId(str1); + } + + [Emitted] + public static SymbolId/*!*/ CreateSymbolEM(string/*!*/ str1, MutableString str2, int codepage) { + Debug.Assert(str1.Length > 0); + return (str2 != null) ? SymbolTable.StringToId(str1 + str2.ConvertToString()) : SymbolTable.StringToId(str1); + } + + [Emitted] + public static SymbolId/*!*/ CreateSymbolMB(MutableString str1, string/*!*/ str2) { + Debug.Assert(str2.Length > 0); + return (str1 != null) ? SymbolTable.StringToId(str1.ConvertToString() + str2) : SymbolTable.StringToId(str2); + } + + [Emitted] + public static SymbolId/*!*/ CreateSymbolMU(MutableString str1, string/*!*/ str2) { + Debug.Assert(str2.Length > 0); + return (str1 != null) ? SymbolTable.StringToId(str1.ConvertToString() + str2) : SymbolTable.StringToId(str2); + } + + [Emitted] + public static SymbolId/*!*/ CreateSymbolME(MutableString str1, string/*!*/ str2, int codepage) { + Debug.Assert(str2.Length > 0); + return (str1 != null) ? SymbolTable.StringToId(str1.ConvertToString() + str2) : SymbolTable.StringToId(str2); + } + + [Emitted] + public static SymbolId/*!*/ CreateSymbolMM(MutableString str1, MutableString str2) { + MutableString str; + if (MutableString.IsNullOrEmpty(str1)) { + if (MutableString.IsNullOrEmpty(str2)) { + throw RubyExceptions.CreateArgumentError("interning empty string"); + } else { + str = str2; + } + } else if (MutableString.IsNullOrEmpty(str2)) { + str = str1; + } else { + str = MutableString.Create(str1).Append(str2); + } + + return SymbolTable.StringToId(str.ToString()); + } + + [Emitted] + public static SymbolId/*!*/ CreateSymbolN(object[]/*!*/ strings, int codepage) { + var str = ConcatenateStrings(strings); + if (str.IsEmpty) { + throw RubyExceptions.CreateArgumentError("interning empty string"); + } + return SymbolTable.StringToId(str.ToString()); + } + + #endregion + + // TODO: encodings + // TODO: could be optimized if we knew that we don't have any string literal: + private static MutableString/*!*/ ConcatenateStrings(object[]/*!*/ strings) { + Assert.NotNull(strings); + Type strType = typeof(string); + MutableString result = MutableString.CreateMutable(); + for (int i = 0; i < strings.Length; i++) { + object str = strings[i]; + if (str != null) { + if (str.GetType() == strType) { + result.Append((string)str); + } else { + result.Append((MutableString)str); + } + } + } + return result; + } + + [Emitted] + public static RubyEncoding/*!*/ CreateEncoding(int codepage) { + Debug.Assert(codepage >= 0, "Null encoding not allowed here"); + return RubyEncoding.GetRubyEncoding(codepage); + } + + [Emitted] + public static bool IsTrue(object obj) { + return (obj is bool) ? (bool)obj == true : obj != null; + } + + [Emitted] + public static bool IsFalse(object obj) { + return (obj is bool) ? (bool)obj == false : obj == null; + } + + #region Exceptions + + // + // NOTE: + // Exception Ops go directly to the current exception object. MRI ignores potential aliases. + // + + [Emitted] //Body, RescueClause: + public static Exception GetCurrentException(RubyScope/*!*/ scope) { + return scope.RubyContext.CurrentException; + } + + [Emitted] //Body: + public static void SetCurrentExceptionAndStackTrace(RubyScope/*!*/ scope, Exception/*!*/ exception) { + if (RubyExceptionData.TryGetInstance(exception) == null) { + RubyExceptionData.AssociateInstance(exception).SetCompiledTrace(); + } + scope.RubyContext.CurrentException = exception; + } + + [Emitted] //Body: + public static void SetCurrentException(RubyScope/*!*/ scope, Exception exception) { + scope.RubyContext.CurrentException = exception; + } + + private static readonly CallSite>/*!*/ _compareExceptionSite = CallSite>.Create( + RubySites.InstanceCallAction("===", 1)); + + [Emitted] //RescueClause: + public static bool CompareException(RubyScope/*!*/ scope, object classObject) { + // throw the same exception when classObject is nil + if (!(classObject is RubyModule)) { + throw RubyExceptions.CreateTypeError("class or module required for rescue clause"); + } + + return _compareExceptionSite.Target(_compareExceptionSite, scope.RubyContext, classObject, scope.RubyContext.CurrentException); + } + + [Emitted] //RescueClause: + public static bool CompareSplattedExceptions(RubyScope/*!*/ scope, object classObjects) { + var list = classObjects as IList; + if (list != null) { + for (int i = 0; i < list.Count; i++) { + if (CompareException(scope, list[i])) { + return true; + } + } + return false; + } else { + return CompareException(scope, classObjects); + } + } + + [Emitted] //RescueClause: + public static bool CompareDefaultException(RubyScope/*!*/ scope, object/*!*/ self) { + RubyContext ec = scope.RubyContext; + + // MRI doesn't call === here; + return ec.IsInstanceOf(ec.CurrentException, ec.StandardErrorClass); + } + + [Emitted] + public static string/*!*/ GetDefaultExceptionMessage(RubyClass/*!*/ exceptionClass) { + return exceptionClass.Name; + } + + [Emitted] + public static ArgumentException/*!*/ MakeWrongNumberOfArgumentsError(int actual, int expected) { + return new ArgumentException(String.Format("wrong number of arguments ({0} for {1})", actual, expected)); + } + + [Emitted] //SuperCall + public static Exception/*!*/ MakeTopLevelSuperException() { + return new MissingMethodException("super called outside of method"); + } + + [Emitted] //SuperCallAction + public static Exception/*!*/ MakeMissingSuperException(string/*!*/ name) { + return new MissingMethodException(String.Format("super: no superclass method `{0}'", name)); + } + + [Emitted] + public static Exception/*!*/ MakeInvalidArgumentTypesError(string/*!*/ methodName) { + // TODO: + return new ArgumentException(String.Format("wrong number or type of arguments for `{0}'", methodName)); + } + + #endregion + + [Emitted] //RubyBinder + public static bool IsSuperCallTarget(RubyScope/*!*/ scope, RubyModule/*!*/ module, string/*!*/ methodName, out object self) { + RubyModule _currentDeclaringModule; + string _currentMethodName; + scope.GetSuperCallTarget(out _currentDeclaringModule, out _currentMethodName, out self); + return module == _currentDeclaringModule && methodName == _currentMethodName; + } + + [Emitted] + public static Range/*!*/ CreateInclusiveRange(RubyScope/*!*/ scope, object begin, object end) { + return new Range(scope.RubyContext, begin, end, false); + } + + [Emitted] + public static Range/*!*/ CreateExclusiveRange(RubyScope/*!*/ scope, object begin, object end) { + return new Range(scope.RubyContext, begin, end, true); + } + + // allocator for struct instances: + [Emitted] + public static RubyStruct/*!*/ AllocateStructInstance(RubyClass/*!*/ self) { + return RubyStruct.Create(self); + } + + // factory for struct instances: + [Emitted] + public static RubyStruct/*!*/ CreateStructInstance(RubyClass/*!*/ self, [NotNull]params object[]/*!*/ items) { + var result = RubyStruct.Create(self); + result.SetValues(items); + return result; + } + + [Emitted] + public static MetaObject/*!*/ GetMetaObject(IRubyObject/*!*/ obj, Expression/*!*/ parameter) { + return new RubyObject.Meta(parameter, Restrictions.Empty, obj); + } + + #region Dynamic Actions + + [Emitted] //ProtocolConversionAction + public static Proc/*!*/ ToProcValidator(string/*!*/ className, object obj) { + Proc result = obj as Proc; + if (result == null) { + throw new InvalidOperationException(String.Format("{0}#to_proc should return Proc", className)); + } + return result; + } + + [Emitted] //ProtocolConversionAction + public static MutableString/*!*/ ToStringValidator(string/*!*/ className, object obj) { + MutableString result = obj as MutableString; + if (result == null) { + throw new InvalidOperationException(String.Format("{0}#to_str should return String", className)); + } + return result; + } + + [Emitted] //ProtocolConversionAction + public static MutableString/*!*/ ToSValidator(string/*!*/ className, object obj) { + MutableString result = obj as MutableString; + if (result == null) { + throw new InvalidOperationException(String.Format("{0}#to_s should return String", className)); + } + return result; + } + + [Emitted] //ProtocolConversionAction + public static string/*!*/ ToSymbolValidator(string/*!*/ className, object obj) { + var str = obj as MutableString; + if (str == null) { + throw new InvalidOperationException(String.Format("{0}#to_str should return String", className)); + } + return str.ConvertToString(); + } + + [Emitted] //ProtocolConversionAction + public static string/*!*/ ConvertSymbolIdToSymbol(SymbolId value) { + return SymbolTable.IdToString(value); + } + + [Emitted] //ProtocolConversionAction + public static string/*!*/ ConvertFixnumToSymbol(RubyContext/*!*/ context, int value) { + context.ReportWarning("do not use Fixnums as Symbols"); + + SymbolId result; + if (TryConvertFixnumToSymbol(value, out result)) { + return SymbolTable.IdToString(result); + } else { + throw RubyExceptions.CreateArgumentError(String.Format("{0} is not a symbol", value)); + } + } + + public static bool TryConvertFixnumToSymbol(int number, out SymbolId symbol) { + symbol = new SymbolId(number); + return !symbol.IsEmpty && SymbolTable.ContainsId(symbol); + } + + [Emitted] //ProtocolConversionAction + public static string/*!*/ ConvertMutableStringToSymbol(MutableString/*!*/ value) { + return value.ConvertToString(); + } + + [Emitted] //ProtocolConversionAction + public static RubyRegex/*!*/ ToRegexValidator(string/*!*/ className, object obj) { + return new RubyRegex(RubyRegex.Escape(ToStringValidator(className, obj)), RubyRegexOptions.NONE); + } + + [Emitted] //ProtocolConversionAction + public static IList/*!*/ ToArrayValidator(string/*!*/ className, object obj) { + IList result = obj as IList; + if (result == null) { + throw new InvalidOperationException(String.Format("{0}#to_ary should return Array", className)); + } + return result; + } + + [Emitted] //ProtocolConversionAction + public static int ToFixnumValidator(string/*!*/ className, object obj) { + if (obj is int) { + return (int)obj; + } + + var bignum = obj as BigInteger; + if ((object)bignum != null) { + int fixnum; + if (bignum.AsInt32(out fixnum)) { + return fixnum; + } + throw RubyExceptions.CreateRangeError("bignum too big to convert into `long'"); + } + + throw new InvalidOperationException(String.Format("{0}#to_int should return Integer", className)); + } + + [Emitted] //ProtocolConversionAction + public static Exception/*!*/ CreateTypeConversionError(string/*!*/ fromType, string/*!*/ toType) { + return RubyExceptions.CreateTypeConversionError(fromType, toType); + } + + [Emitted] //ConvertToFixnumAction + public static int ConvertBignumToFixnum(BigInteger/*!*/ bignum) { + int fixnum; + if (bignum.AsInt32(out fixnum)) { + return fixnum; + } + throw RubyExceptions.CreateRangeError("bignum too big to convert into `long'"); + } + + #endregion + + #region Called by GetHashCode/Equals methods in generated .NET classes + + // we need to get the right execution context here +#if OBSOLETE + [Emitted] + public static bool ResolveDeclaredInstanceMethod(Type myType, string name) { + RubyModule module = RubyUtils.GetExecutionContext(null).GetOrCreateClass(myType); + return module.ResolveDeclaredMethod(SymbolTable.StringToId(name)) != null; + } + + [Emitted] + public static int CallHash(object obj) { + // TODO: do not use default context: + return _HashSharedSite.Invoke(RubyContext._DefaultContext, obj); + } + + [Emitted] + public static bool CallEql(object lhs, object rhs) { + // TODO: do not use default context: + return _EqlSharedSite.Invoke(RubyContext._DefaultContext, lhs, rhs); + } +#endif + + #endregion + + #region Instance variable support + + [Emitted] + public static object GetInstanceVariable(RubyScope/*!*/ scope, object self, string/*!*/ name) { + RubyInstanceData data = scope.RubyContext.TryGetInstanceData(self); + return (data != null) ? data.GetInstanceVariable(name) : null; + } + + [Emitted] + public static bool IsDefinedInstanceVariable(RubyScope/*!*/ scope, object self, string/*!*/ name) { + RubyInstanceData data = scope.RubyContext.TryGetInstanceData(self); + if (data == null) return false; + object value; + return data.TryGetInstanceVariable(name, out value); + } + + [Emitted] + public static object SetInstanceVariable(object self, object value, RubyScope/*!*/ scope, string/*!*/ name) { + scope.RubyContext.SetInstanceVariable(self, name, value); + return value; + } + + #endregion + + #region Class Variables + + [Emitted] + public static object GetObjectClassVariable(RubyScope/*!*/ scope, string/*!*/ name) { + return GetClassVariableInternal(scope.RubyContext.ObjectClass, name); + } + + [Emitted] + public static object GetClassVariable(RubyScope/*!*/ scope, string/*!*/ name) { + // owner is the first module in scope: + RubyModule owner = scope.GetInnerMostModule(true); + return GetClassVariableInternal(owner, name); + } + + private static object GetClassVariableInternal(RubyModule/*!*/ module, string/*!*/ name) { + object value; + if (module.TryResolveClassVariable(name, out value) == null) { + throw RubyExceptions.CreateNameError(String.Format("uninitialized class variable {0} in {1}", name, module.Name)); + } + return value; + } + + [Emitted] + public static object TryGetObjectClassVariable(RubyScope/*!*/ scope, string/*!*/ name) { + object value; + scope.RubyContext.ObjectClass.TryGetClassVariable(name, out value); + return value; + } + + [Emitted] + public static object TryGetClassVariable(RubyScope/*!*/ scope, string/*!*/ name) { + object value; + // owner is the first module in scope: + scope.GetInnerMostModule(true).TryResolveClassVariable(name, out value); + return value; + } + + [Emitted] + public static bool IsDefinedObjectClassVariable(RubyScope/*!*/ scope, string/*!*/ name) { + object value; + return scope.RubyContext.ObjectClass.TryResolveClassVariable(name, out value) != null; + } + + [Emitted] + public static bool IsDefinedClassVariable(RubyScope/*!*/ scope, string/*!*/ name) { + // owner is the first module in scope: + RubyModule owner = scope.GetInnerMostModule(true); + object value; + return owner.TryResolveClassVariable(name, out value) != null; + } + + [Emitted] + public static object SetObjectClassVariable(object value, RubyScope/*!*/ scope, string/*!*/ name) { + return SetClassVariableInternal(scope.RubyContext.ObjectClass, name, value); + } + + [Emitted] + public static object SetClassVariable(object value, RubyScope/*!*/ scope, string/*!*/ name) { + return SetClassVariableInternal(scope.GetInnerMostModule(true), name, value); + } + + private static object SetClassVariableInternal(RubyModule/*!*/ lexicalOwner, string/*!*/ name, object value) { + object oldValue; + RubyModule owner = lexicalOwner.TryResolveClassVariable(name, out oldValue); + (owner ?? lexicalOwner).SetClassVariable(name, value); + return value; + } + + #endregion + + #region Ruby Types + + [Emitted] //RubyTypeBuilder + public static RubyInstanceData/*!*/ GetInstanceData(ref RubyInstanceData/*!*/ instanceData) { + if (instanceData == null) { + Interlocked.CompareExchange(ref instanceData, new RubyInstanceData(), null); + } + return instanceData; + } + +#if !SILVERLIGHT + [Emitted] //RubyTypeBuilder + public static void DeserializeObject(out RubyInstanceData/*!*/ instanceData, out RubyClass/*!*/ rubyClass, SerializationInfo/*!*/ info) { + rubyClass = (RubyClass)info.GetValue("#class", typeof(RubyClass)); + RubyInstanceData newInstanceData = null; + foreach (SerializationEntry entry in info) { + if (entry.Name.StartsWith("@")) { + if (newInstanceData == null) { + newInstanceData = new RubyInstanceData(); + } + newInstanceData.SetInstanceVariable(entry.Name, entry.Value); + } + } + instanceData = newInstanceData; + } + + [Emitted] //RubyTypeBuilder + public static void SerializeObject(RubyInstanceData instanceData, RubyClass/*!*/ rubyClass, SerializationInfo/*!*/ info) { + info.AddValue("#class", rubyClass, typeof(RubyClass)); + if (instanceData != null) { + string[] instanceNames = instanceData.GetInstanceVariableNames(); + foreach (string name in instanceNames) { + object value; + if (!instanceData.TryGetInstanceVariable(name, out value)) { + value = null; + } + info.AddValue(name, value, typeof(object)); + } + } + } +#endif + #endregion + + #region Delegates, Events + + /// + /// Hooks up an event to call a proc at hand. + /// EventInfo is passed in as object since it is an internal type. + /// + [Emitted] + public static object HookupEvent(EventInfo/*!*/ eventInfo, object target, Proc/*!*/ proc) { + Assert.NotNull(eventInfo, proc); + + BlockParam bp = CreateBfcForProcCall(proc); + Delegate eh = BinderOps.GetDelegate(proc.LocalScope.RubyContext, bp, eventInfo.EventHandlerType); + MethodInfo mi = eventInfo.GetAddMethod(); + + mi.Invoke(target, new object[] { eh }); + + return null; + } + + [Emitted] + public static Delegate/*!*/ CreateDelegateFromProc(Type/*!*/ type, Proc/*!*/ proc) { + BlockParam bp = CreateBfcForProcCall(proc); + return BinderOps.GetDelegate(proc.LocalScope.RubyContext, bp, type); + } + + [Emitted] + public static Delegate/*!*/ CreateDelegateFromMethod(Type/*!*/ type, RubyMethod/*!*/ method) { + return BinderOps.GetDelegate(method.Info.Context, method, type); + } + + #endregion + + [Emitted] + public static void X(string marker) { + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/RubyOptions.cs b/merlin/main/languages/ruby/Ruby/Runtime/RubyOptions.cs new file mode 100644 index 0000000000..0df18d0f24 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/RubyOptions.cs @@ -0,0 +1,113 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Microsoft.Scripting; +using System.Threading; + +namespace IronRuby { + + [Serializable] + public sealed class RubyOptions : LanguageOptions { + private readonly ReadOnlyCollection/*!*/ _arguments; + private readonly ReadOnlyCollection/*!*/ _libraryPaths; + private readonly string _mainFile; + private readonly bool _enableTracing; + private readonly int _verbosity; + private readonly string _savePath; + private readonly bool _loadFromDisk; + private readonly bool _profile; + private readonly bool _hasSearchPaths; + private readonly RubyCompatibility _compatibility; + + public ReadOnlyCollection/*!*/ Arguments { + get { return _arguments; } + } + + public string MainFile { + get { return _mainFile; } + } + + public int Verbosity { + get { return _verbosity; } + } + + public bool EnableTracing { + get { return _enableTracing; } + } + + public string SavePath { + get { return _savePath; } + } + + public bool LoadFromDisk { + get { return _loadFromDisk; } + } + + public bool Profile { + get { return _profile; } + } + + public ReadOnlyCollection/*!*/ LibraryPaths { + get { return _libraryPaths; } + } + + public bool HasSearchPaths { + get { return _hasSearchPaths; } + } + + public RubyCompatibility Compatibility { + get { return _compatibility; } + } + + public RubyOptions(IDictionary/*!*/ options) + : base(options) { + _arguments = GetStringCollectionOption(options, "Arguments") ?? EmptyStringCollection; + + _mainFile = GetOption(options, "MainFile", (string)null); + _verbosity = GetOption(options, "Verbosity", 1); + _enableTracing = GetOption(options, "EnableTracing", false); + _savePath = GetOption(options, "SavePath", (string)null); + _loadFromDisk = GetOption(options, "LoadFromDisk", false); + _profile = GetOption(options, "Profile", false); + _libraryPaths = GetStringCollectionOption(options, "LibraryPaths", ';', ',') ?? new ReadOnlyCollection(new[] { "." }); + _hasSearchPaths = GetOption(options, "SearchPaths", null) != null; + _compatibility = GetCompatibility(options, "Compatibility", RubyCompatibility.Default); + } + + private static RubyCompatibility GetCompatibility(IDictionary/*!*/ options, string/*!*/ name, RubyCompatibility defaultValue) { + object value; + if (options != null && options.TryGetValue(name, out value)) { + if (value is RubyCompatibility) { + return (RubyCompatibility)value; + } + + string str = value as string; + if (str != null) { + switch (str) { + case "1.8": return RubyCompatibility.Ruby18; + case "1.9": return RubyCompatibility.Ruby19; + case "2.0": return RubyCompatibility.Ruby20; + } + } + + return (RubyCompatibility)Convert.ChangeType(value, typeof(RubyCompatibility), Thread.CurrentThread.CurrentCulture); + } + return defaultValue; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/RubyScope.cs b/merlin/main/languages/ruby/Ruby/Runtime/RubyScope.cs new file mode 100644 index 0000000000..f8de9c9c74 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/RubyScope.cs @@ -0,0 +1,639 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Scripting; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Text.RegularExpressions; +using IronRuby.Builtins; +using IronRuby.Compiler; +using IronRuby.Runtime.Calls; +using System.Reflection; +using IronRuby.Compiler.Generation; +using System.Runtime.CompilerServices; +using System.Collections.ObjectModel; + +namespace IronRuby.Runtime { + + public enum ScopeKind { + TopLevel, + Method, + Module, + Block + } + +#if !SILVERLIGHT + [DebuggerTypeProxy(typeof(RubyScope.DebugView))] +#endif + public abstract class RubyScope : CodeContext { + internal static readonly LocalsDictionary _EmptyLocals = new LocalsDictionary(new IStrongBox[0], new SymbolId[0]); + + private readonly IAttributesCollection/*!*/ _frame; + + private RubyTopLevelScope/*!*/ _top; + + private object _selfObject; + private RuntimeFlowControl/*!*/ _runtimeFlowControl; // TODO: merge? + + // set by private/public/protected/module_function + private RubyMethodAttributes _methodAttributes; + + public abstract ScopeKind Kind { get; } + public abstract bool InheritsLocalVariables { get; } + + public virtual RubyModule Module { + get { return null; } + } + + public object SelfObject { + get { return _selfObject; } + internal set { _selfObject = value; } + } + + public RubyMethodVisibility Visibility { + get { return (RubyMethodVisibility)(_methodAttributes & RubyMethodAttributes.VisibilityMask); } + } + + public RubyMethodAttributes MethodAttributes { + get { return _methodAttributes; } + set { _methodAttributes = value; } + } + + public RuntimeFlowControl/*!*/ RuntimeFlowControl { + get { return _runtimeFlowControl; } + } + + public sealed override Scope GlobalScope { + get { return _top.RubyGlobalScope.Scope; } + } + + public RubyTopLevelScope/*!*/ Top { + get { return _top; } + } + + public RubyContext/*!*/ RubyContext { + get { return (RubyContext)LanguageContext; } + } + + public IAttributesCollection/*!*/ Frame { + get { return _frame; } + } + + // top scope: + protected RubyScope(LanguageContext/*!*/ language, IAttributesCollection/*!*/ frame) + : base(null, language, null) { + _frame = frame; + _top = (RubyTopLevelScope)this; + } + + // other scopes: + protected RubyScope(RubyScope/*!*/ parent, IAttributesCollection/*!*/ frame) + : base(null, parent.Top.LanguageContext, parent) { + _frame = frame; + _top = parent.Top; + } + + internal void Initialize(RuntimeFlowControl/*!*/ runtimeFlowControl, RubyMethodAttributes methodAttributes, object selfObject) { + Assert.NotNull(runtimeFlowControl); + + _selfObject = selfObject; + _runtimeFlowControl = runtimeFlowControl; + _methodAttributes = methodAttributes; + } + + public bool IsEmpty { + get { return RubyContext.EmptyScope == this; } + } + + protected virtual bool IsClosureScope { + get { return false; } + } + +#if DEBUG + private string _debugName; + + public override string ToString() { + return _debugName; + } +#endif + [Conditional("DEBUG")] + public void SetDebugName(string name) { +#if DEBUG + _debugName = name; +#endif + } + + // TODO: + public List/*!*/ GetVisibleLocalNames() { + var result = new List(); + RubyScope scope = this; + while (true) { + foreach (object name in scope.Frame.Keys) { + string strName = name as string; + if (strName != null && !strName.StartsWith("#")) { + result.Add(strName); + } + } + + if (!scope.InheritsLocalVariables) { + return result; + } + + scope = (RubyScope)scope.Parent; + } + } + + internal object ResolveLocalVariable(string/*!*/ name) { + RubyScope scope = this; + while (true) { + object result; + if (scope.Frame.TryGetValue(SymbolTable.StringToId(name), out result)) { + return result; + } + + if (!scope.InheritsLocalVariables) { + return null; + } + + scope = (RubyScope)scope.Parent; + } + } + + public RubyModule/*!*/ GetInnerMostModule() { + return GetInnerMostModule(false); + } + + public RubyModule/*!*/ GetInnerMostModule(bool skipSingletons) { + RubyScope scope = this; + do { + RubyModule result = scope.Module; + if (result != null && (!skipSingletons || !result.IsSingletonClass)) { + return result; + } + scope = (RubyScope)scope.Parent; + } while (scope != null); + return RubyContext.ObjectClass; + } + + public RubyMethodScope GetInnerMostMethodScope() { + RubyScope scope = this; + while (scope != null && scope.Kind != ScopeKind.Method) { + scope = (RubyScope)scope.Parent; + } + return (RubyMethodScope)scope; + } + + public RubyClosureScope/*!*/ GetInnerMostClosureScope() { + RubyScope scope = this; + while (scope != null && !scope.IsClosureScope) { + scope = (RubyScope)scope.Parent; + } + return (RubyClosureScope)scope; + } + + internal void GetSuperCallTarget(out RubyModule declaringModule, out string/*!*/ methodName, out object self) { + RubyScope scope = this; + while (true) { + Debug.Assert(scope != null); + + switch (scope.Kind) { + case ScopeKind.Method: + RubyMethodScope methodScope = (RubyMethodScope)scope; + // See RubyOps.DefineMethod for why we can use Method here. + declaringModule = methodScope.Method.DeclaringModule; + methodName = methodScope.Method.DefinitionName; + self = scope.SelfObject; + return; + + case ScopeKind.Block: + BlockParam blockParam = ((RubyBlockScope)scope).BlockParameter; + if (blockParam.SuperMethodName != null) { + declaringModule = blockParam.ModuleDeclaration; + methodName = blockParam.SuperMethodName; + self = scope.SelfObject; + return; + } + break; + + case ScopeKind.TopLevel: + throw RubyOps.MakeTopLevelSuperException(); + } + + scope = (RubyScope)scope.Parent; + } + } + + public RubyScope/*!*/ GetMethodAttributesDefinitionScope() { + RubyScope scope = this; + while (true) { + if (scope.Kind == ScopeKind.Block) { + BlockParam blockParam = ((RubyBlockScope)scope).BlockParameter; + if (blockParam.ModuleDeclaration != null && blockParam.SuperMethodName == null) { + return scope; + } + } else { + return scope; + } + + scope = (RubyScope)scope.Parent; + } + } + + internal RubyModule/*!*/ GetMethodDefinitionOwner() { + RubyScope scope = this; + bool skipBlocks = false; + while (true) { + Debug.Assert(scope != null); + + switch (scope.Kind) { + case ScopeKind.TopLevel: + return ((RubyTopLevelScope)scope).TopModuleOrObject; + + case ScopeKind.Module: + Debug.Assert(scope.Module != null); + return scope.Module; + + case ScopeKind.Method: + // MRI 1.8: skips module_evals above + // MRI 1.9: just continue search for a module or module_eval + skipBlocks = RubyContext.RubyOptions.Compatibility == RubyCompatibility.Ruby18; + break; + + case ScopeKind.Block: + if (!skipBlocks) { + BlockParam blockParam = ((RubyBlockScope)scope).BlockParameter; + if (blockParam.ModuleDeclaration != null) { + return blockParam.ModuleDeclaration; + } + } + break; + } + + scope = (RubyScope)scope.Parent; + } + } + + // dynamic dispatch to "const_missing" if not found + public object ResolveConstant(bool autoload, string/*!*/ name) { + object result; + + if (TryResolveConstant(autoload, name, out result)) { + return result; + } + + RubyUtils.CheckConstantName(name); + return RubySites.ModuleConstMissing(RubyContext, GetInnerMostModule(), name); + } + + public bool TryResolveConstant(bool autoload, string/*!*/ name, out object result) { + Scope autoloadScope = autoload ? GlobalScope : null; + RubyScope scope = this; + + // lexical lookup first: + RubyModule innerMostModule = null; + do { + RubyModule module = scope.Module; + + if (module != null) { + if (module.TryGetConstant(autoloadScope, name, out result)) { + return true; + } + + // remember the module: + if (innerMostModule == null) { + innerMostModule = module; + } + } + + scope = (RubyScope)scope.Parent; + } while (scope != null); + + // check the inner most module and it's base classes/mixins: + if (innerMostModule != null && innerMostModule.TryResolveConstant(autoloadScope, name, out result)) { + return true; + } + + return RubyContext.ObjectClass.TryResolveConstant(autoloadScope, name, out result); + } + + #region Debug View +#if !SILVERLIGHT + internal sealed class DebugView { + private readonly RubyScope/*!*/ _scope; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + private readonly string/*!*/ _selfClassName; + + public DebugView(RubyScope/*!*/ scope) { + Assert.NotNull(scope); + _scope = scope; + _selfClassName = _scope.RubyContext.GetImmediateClassOf(_scope._selfObject).GetDisplayName(true).ConvertToString(); + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public VariableView[]/*!*/ A0 { + get { + List result = new List(); + RubyScope scope = _scope; + while (true) { + foreach (KeyValuePair variable in scope._frame.SymbolAttributes) { + string name = SymbolTable.IdToString(variable.Key); + if (!name.StartsWith("#")) { + string className = _scope.RubyContext.GetImmediateClassOf(variable.Value).GetDisplayName(true).ConvertToString(); + if (scope != _scope) { + name += " (outer)"; + } + result.Add(new VariableView(name, variable.Value, className)); + } + } + + if (!scope.InheritsLocalVariables) { + break; + } + scope = (RubyScope)scope.Parent; + } + return result.ToArray(); + } + } + + [DebuggerDisplay("{A1}", Name = "self", Type = "{_selfClassName,nq}")] + public object A1 { + get { return _scope._selfObject; } + } + + [DebuggerDisplay("{B}", Name = "MethodAttributes", Type = "")] + public RubyMethodAttributes B { + get { return _scope._methodAttributes; } + } + + [DebuggerDisplay("{C}", Name = "ParentScope", Type = "")] + public RubyScope C { + get { return (RubyScope)_scope.Parent; } + } + + [DebuggerDisplay("", Name = "RawVariables", Type = "")] + public System.Collections.Hashtable/*!*/ D { + get { + System.Collections.Hashtable result = new System.Collections.Hashtable(); + foreach (KeyValuePair variable in _scope._frame.SymbolAttributes) { + result.Add(variable.Key, variable.Value); + } + return result; + } + } + + [DebuggerDisplay("{_value}", Name = "{_name,nq}", Type = "{_valueClassName,nq}")] + internal struct VariableView { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private readonly string/*!*/ _name; + [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] + private readonly object _value; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private readonly string/*!*/ _valueClassName; + + public VariableView(string/*!*/ name, object value, string/*!*/ valueClassName) { + _name = name; + _value = value; + _valueClassName = valueClassName; + } + } + } +#endif + #endregion + } + + public abstract class RubyClosureScope : RubyScope { + // $+ + private MatchData _currentMatch; // TODO: per method scope and top level scope, not block scope + + // $_ + private object _lastInputLine; // TODO: per method scope and top level scope, not block scope + + // top scope: + protected RubyClosureScope(LanguageContext/*!*/ language, IAttributesCollection/*!*/ frame) + : base(language, frame) { + } + + // other scopes: + protected RubyClosureScope(RubyScope/*!*/ parent, IAttributesCollection/*!*/ frame) + : base(parent, frame) { + } + + protected override bool IsClosureScope { + get { return true; } + } + + public MatchData CurrentMatch { + get { return _currentMatch; } + set { _currentMatch = value; } + } + + public object LastInputLine { + get { return _lastInputLine; } + set { _lastInputLine = value; } + } + + internal MutableString GetCurrentMatchGroup(int index) { + Debug.Assert(index >= 0); + + // we don't need to check index range, Groups indexer returns an unsuccessful group if out of range: + Group group; + if (_currentMatch != null && (group = _currentMatch.Groups[index]).Success) { + return MutableString.Create(group.Value).TaintBy(_currentMatch.OriginalString); + } + + return null; + } + + internal MutableString GetCurrentMatchLastGroup() { + if (_currentMatch != null) { + // TODO: cache the last successful group index? + for (int i = _currentMatch.Groups.Count - 1; i >= 0; i--) { + Group group = _currentMatch.Groups[i]; + if (group.Success) { + return MutableString.Create(group.Value).TaintBy(_currentMatch.OriginalString); + } + } + } + + return null; + } + +#if TODO_DebugView + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + private readonly string/*!*/ _matchClassName; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + private readonly string/*!*/ _lastInputLine; +var closureScope = scope as RubyClosureScope; + if (closureScope != null) { + _matchClassName = _scope.RubyContext.GetImmediateClassOf(closureScope.CurrentMatch).GetDisplayName(true).ConvertToString(); + _lastInputLine = _scope.RubyContext.GetImmediateClassOf(closureScope.LastInputLine).GetDisplayName(true).ConvertToString(); + } + + + [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] + [DebuggerDisplay("{A2 != null ? A2.ToString() : \"nil\",nq}", Name = "$~", Type = "{_matchClassName,nq}")] + public Match A2 { + get { return (_scope._currentMatch != null) ? _scope._currentMatch.Match : null; } + } + + [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] + [DebuggerDisplay("{A2 != null ? A2.ToString() : \"nil\",nq}", Name = "$~", Type = "{_matchClassName,nq}")] + public Match A2 { + get { return (_scope._currentMatch != null) ? _scope._currentMatch.Match : null; } + } +#endif + } + + public sealed class RubyMethodScope : RubyClosureScope { + private readonly RubyMethodInfo/*!*/ _method; + private readonly Proc _blockParameter; + + public override ScopeKind Kind { get { return ScopeKind.Method; } } + public override bool InheritsLocalVariables { get { return false; } } + + // Singleton module-function method shares this pointer with instance method. See RubyOps.DefineMethod for details. + internal RubyMethodInfo Method { + get { return _method; } + } + + public Proc BlockParameter { + get { return _blockParameter; } + } + + internal RubyMethodScope(RubyScope/*!*/ parent, IAttributesCollection/*!*/ frame, RubyMethodInfo/*!*/ method, Proc blockParameter) + : base(parent, frame) { + _method = method; + _blockParameter = blockParameter; + } + } + + public sealed class RubyModuleScope : RubyClosureScope { + // TODO: readonly + private RubyModule _module; + private readonly bool _isEval; + + public override ScopeKind Kind { get { return ScopeKind.Module; } } + public override bool InheritsLocalVariables { get { return _isEval; } } + + public override RubyModule Module { get { return _module; } } + + internal void SetModule(RubyModule/*!*/ module) { _module = module; } + + internal RubyModuleScope(RubyScope/*!*/ parent, IAttributesCollection/*!*/ frame, RubyModule module, bool isEval) + : base(parent, frame) { + _module = module; + _isEval = isEval; + } + } + + public sealed class RubyBlockScope : RubyScope { + public override ScopeKind Kind { get { return ScopeKind.Block; } } + public override bool InheritsLocalVariables { get { return true; } } + + // TODO: readonly + private BlockParam _blockParam; + + public BlockParam BlockParameter { + get { return _blockParam; } + internal set { _blockParam = value; } + } + + internal RubyBlockScope(RubyScope/*!*/ parent, IAttributesCollection/*!*/ frame) + : base(parent, frame) { + } + } + + public sealed class RubyTopLevelScope : RubyClosureScope { + public override ScopeKind Kind { get { return ScopeKind.TopLevel; } } + public override bool InheritsLocalVariables { get { return false; } } + + private readonly GlobalScopeExtension/*!*/ _globalScope; + private RubyModule _definitionsModule; + + public GlobalScopeExtension/*!*/ RubyGlobalScope { + get { + if (_globalScope == null) { + throw new InvalidOperationException("Empty scope has no global scope."); + } + return _globalScope; + } + } + + public override RubyModule Module { + get { return _definitionsModule; } + } + + internal void SetModule(RubyModule/*!*/ value) { + _definitionsModule = value; + } + + internal RubyModule/*!*/ TopModuleOrObject { + get { return _definitionsModule ?? _globalScope.Context.ObjectClass; } + } + + // empty scope: + internal RubyTopLevelScope(LanguageContext/*!*/ context) + : base(context, _EmptyLocals) { + } + + internal RubyTopLevelScope(GlobalScopeExtension/*!*/ globalScope, RubyModule definitionsModule, IAttributesCollection/*!*/ frame) + : base(globalScope.Context, frame) { + Assert.NotNull(globalScope); + _globalScope = globalScope; + _definitionsModule = definitionsModule; + } + + // TODO: might be called by MI.Invoke -> needs to be public in partial trust + // method_missing on top-level DLR created scope: + public static object TopMethodMissing(RubyScope/*!*/ scope, BlockParam block, object/*!*/ self, SymbolId name, [NotNull]params object[]/*!*/ args) { + Assert.NotNull(scope, self); + Debug.Assert(!scope.IsEmpty); + Scope globalScope = scope.GlobalScope; + Debug.Assert(globalScope != null); + + // TODO: error when arguments non-empty, block != null, ... + + object value; + if (globalScope.TryGetName(name, out value)) { + return value; + } + + // TODO: assignment + //if (args.Length == 1) { + // string str = SymbolTable.IdToString(name); + // if (str.Length > 0 && str[str.Length - 1] == '=') { + // SymbolId plainName = SymbolTable.StringToId(str.Substring(0, str.Length - 1)); + // if (globalScope.ContainsName(plainName)) { + // globalScope.SetName(plainName, args[0]); + // } + // return value; + // } + //} + + // TODO: call super + throw RubyExceptions.CreateMethodMissing(scope.RubyContext, self, SymbolTable.IdToString(name)); + } + + // TODO: + // TOPLEVEL_BINDING gets the Binding instance for DLR created scope: + //internal static Binding/*!*/ GetTopLevelBinding(CodeContext/*!*/ context) { + // return RubyUtils.GetScope(context).GlobalScope.Binding; + //} + + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/RubyUtils.cs b/merlin/main/languages/ruby/Ruby/Runtime/RubyUtils.cs new file mode 100644 index 0000000000..95759900ee --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/RubyUtils.cs @@ -0,0 +1,756 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text; +using System.Threading; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Interpretation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Compiler; +using IronRuby.Runtime.Calls; +using System.Dynamic; +using Microsoft.Scripting.Math; +using System.Runtime.CompilerServices; + +namespace IronRuby.Runtime { + + public static class RubyUtils { + #region Objects + + public static readonly int FalseObjectId = 0; + public static readonly int TrueObjectId = 2; + public static readonly int NilObjectId = 4; + + // TODO: this is not correct, because it won't call singleton "eql?" methods + public static bool ValueEquals(object self, object other) { + return object.Equals(self, other); + } + + // TODO: this is not correct, because it won't call singleton "hash" methods + public static int GetHashCode(object self) { + return self != null ? self.GetHashCode() : RubyUtils.NilObjectId; + } + + /// + /// Determines whether the given object is a value type in Ruby (i.e. they have value equality) + /// + /// In Ruby, immediate values are Fixnums, Symbols, true, false, and nil + /// All of those have value equality, whereas other types like Bignum & Float have reference equality + /// + /// TODO: currently we treat all .NET value types (except floating point) as if they were + /// immediate values. Is this correct? + /// + public static bool IsRubyValueType(object obj) { + return obj == null || obj is ValueType && !(obj is float || obj is double); + } + + public static bool CanCreateSingleton(object obj) { + return !(obj is int || obj is SymbolId || obj is float || obj is double || obj is BigInteger); + } + + public static void RequiresNotFrozen(RubyContext/*!*/ context, object/*!*/ obj) { + if (context.IsObjectFrozen(obj)) { + throw RubyExceptions.CreateTypeError("can't modify frozen object"); + } + } + + public static string/*!*/ GetClassName(RubyContext/*!*/ context, object self) { + return context.GetClassOf(self).Name; + } + + public static MutableString/*!*/ ObjectToMutableString(RubyContext/*!*/ context, object obj) { + using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(obj)) { + if (handle == null) { + return MutableString.Create("..."); + } + + MutableString str = MutableString.CreateMutable(); + str.Append("#<"); + str.Append(context.GetClassOf(obj).Name); + + // Ruby prints 2*object_id for objects + str.Append(':'); + AppendFormatHexObjectId(str, GetObjectId(context, obj)); + + // display instance variables + RubyInstanceData data = context.TryGetInstanceData(obj); + if (data != null) { + var vars = data.GetInstanceVariablePairs(); + bool first = true; + foreach (KeyValuePair var in vars) { + if (first) { + str.Append(" "); + first = false; + } else { + str.Append(", "); + } + str.Append(var.Key); + str.Append("="); + str.Append(RubySites.Inspect(context, var.Value)); + } + } + str.Append(">"); + + return str; + } + } + + public static MutableString/*!*/ AppendFormatHexObjectId(MutableString/*!*/ str, int objectId) { + return str.AppendFormat("0x{0:x7}", 2 * objectId); + } + + public static bool TryDuplicateObject(RubyContext/*!*/ context, object obj, bool cloneSemantics, out object copy) { + // Ruby value types can't be cloned + if (RubyUtils.IsRubyValueType(obj)) { + copy = null; + return false; + } + + IDuplicable clonable = obj as IDuplicable; + if (clonable != null) { + copy = clonable.Duplicate(context, cloneSemantics); + } else { + // .NET classes and library clases that doesn't implement IDuplicable: + copy = RubySites.Allocate(context.GetClassOf(obj)); + context.CopyInstanceData(obj, copy, cloneSemantics); + } + + // TODO: optimize + _InitializeCopySharedSite.Target(_InitializeCopySharedSite, context, copy, obj); + if (cloneSemantics) { + context.FreezeObjectBy(copy, obj); + } + + return true; + } + + private static readonly CallSite> _InitializeCopySharedSite = + CallSite>.Create( + RubyCallAction.Make("initialize_copy", RubyCallSignature.WithImplicitSelf(1)) + ); + + +#if FALSE + [MultiRuntimeAware] + private static RecursionTracker/*!*/ _infiniteCopyTracker = new RecursionTracker(); + + public static object DeepCopy(RubyContext/*!*/ context, object obj) { + using (IDisposable handle = _infiniteCopyTracker.TrackObject(obj)) { + if (handle == null) { + return RubyExceptions.CreateArgumentError("unable to deep copy recursive structure"); + } else { + RubyContext ec = RubyUtils.GetExecutionContext(context); + + if (RubyUtils.IsRubyValueType(obj)) { + return obj; + } + + object copy; + + // TODO: special case class objects: + RubyClass classObject = obj as RubyClass; + if (classObject != null) { + copy = classObject.Duplicate(); + } else { + copy = RubySites.Allocate(context, ec.GetClassOf(obj)); + } + + SymbolId[] names = ec.GetInstanceVariableNames(obj); + RubyInstanceData newVars = (names.Length > 0) ? ec.GetInstanceData(copy) : null; + foreach (SymbolId name in names) { + object value; + if (!ec.TryGetInstanceVariable(obj, name, out value)) { + value = null; + } else { + value = DeepCopy(context, value); + } + newVars.SetInstanceVariable(name, value); + } + + if (classObject == null) { + // do any special copying needed for library types + // TODO: we still need to implement copy semantics for .NET types in general + IDuplicable duplicable = copy as IDuplicable; + if (duplicable != null) { + duplicable.InitializeFrom(obj); + } + } + return copy; + } + } + } +#endif + public static int GetFixnumId(int number) { + return number * 2 + 1; + } + + public static int GetObjectId(RubyContext/*!*/ context, object obj) { + if (obj == null) return NilObjectId; + if (obj is bool) return (bool)obj ? TrueObjectId : FalseObjectId; + if (obj is int) return GetFixnumId((int)obj); + + return context.GetInstanceData(obj).ObjectId; + } + + #endregion + + #region Names + + // Unmangles a method name. Not all names can be unmangled. + // For a name to be unmangle-able, it must be lower_case_with_underscores. + // If a name can't be unmangled, this function returns null + public static string TryUnmangleName(string/*!*/ name) { + if (name.ToUpper().Equals("INITIALIZE")) { + // Special case for compatibility with CLR + return name; + } + + StringBuilder sb = new StringBuilder(name.Length); + bool upcase = true; + foreach (char c in name) { + if (char.IsUpper(c)) { + // can't unmangle a name with uppercase letters + return null; + } + + if (c == '_') { + if (upcase) { + // can't unmangle a name with consecutive or leading underscores + return null; + } + upcase = true; + } else { + if (upcase) { + sb.Append(char.ToUpper(c)); + upcase = false; + } else { + sb.Append(c); + } + } + } + if (upcase) { + // string was empty or ended with an underscore, can't unmangle + return null; + } + return sb.ToString(); + } + + internal static string/*!*/ MangleName(string/*!*/ name) { + Assert.NotNull(name); + + if (name.ToUpper().Equals("INITIALIZE")) { + // Special case for compatibility with CLR + return name; + } + + StringBuilder result = new StringBuilder(name.Length); + + for (int i = 0; i < name.Length; i++) { + if (Char.IsUpper(name[i])) { + if (!(i == 0 || i + 1 < name.Length && Char.IsUpper(name[i + 1]) || i + 1 == name.Length && Char.IsUpper(name[i - 1]))) { + result.Append('_'); + } + result.Append(Char.ToLower(name[i])); + } else { + result.Append(name[i]); + } + } + + return result.ToString(); + } + + public static string/*!*/ GetQualifiedName(Type/*!*/ type) { + ContractUtils.RequiresNotNull(type, "type"); + + StringBuilder sb = new StringBuilder(); + + Type t = type; + do { + if (sb.Length > 0) { + sb.Insert(0, "::"); + } + + int tick = t.Name.LastIndexOf('`'); + if (tick != -1) { + sb.Insert(0, t.Name.Substring(0, tick)); + } else { + sb.Insert(0, t.Name); + } + + t = t.DeclaringType; + } while (t != null); + + if (type.Namespace != null) { + sb.Insert(0, "::"); + sb.Insert(0, type.Namespace.Replace(Type.Delimiter.ToString(), "::")); + } + + return sb.ToString(); + } + + public static string/*!*/ GetQualifiedName(NamespaceTracker/*!*/ namespaceTracker) { + ContractUtils.RequiresNotNull(namespaceTracker, "namespaceTracker"); + if (namespaceTracker.Name == null) return String.Empty; + + return namespaceTracker.Name.Replace(Type.Delimiter.ToString(), "::"); + } + + public static void CheckConstantName(string name) { + if (!Tokenizer.IsConstantName(name)) { + throw RubyExceptions.CreateNameError(String.Format("`{0}' is not allowed as a constant name", name)); + } + } + + public static void CheckClassVariableName(string name) { + if (!Tokenizer.IsClassVariableName(name)) { + throw RubyExceptions.CreateNameError(String.Format("`{0}' is not allowed as a class variable name", name)); + } + } + + public static void CheckInstanceVariableName(string name) { + if (!Tokenizer.IsInstanceVariableName(name)) { + throw RubyExceptions.CreateNameError(String.Format("`{0}' is not allowed as an instance variable name", name)); + } + } + + #endregion + + #region Constants, Methods + + public static object GetConstant(RubyScope/*!*/ scope, RubyModule/*!*/ owner, string/*!*/ name, bool lookupObject) { + Assert.NotNull(scope, owner, name); + + object result; + if (owner.TryResolveConstant(scope.GlobalScope, name, out result)) { + return result; + } + + RubyClass objectClass = owner.Context.ObjectClass; + if (owner != objectClass && lookupObject && objectClass.TryResolveConstant(scope.GlobalScope, name, out result)) { + return result; + } + + CheckConstantName(name); + return RubySites.ModuleConstMissing(scope.RubyContext, owner, name); + } + + public static void SetConstant(RubyModule/*!*/ owner, string/*!*/ name, object value) { + Assert.NotNull(owner, name); + + if (owner.SetConstantChecked(name, value)) { + owner.Context.ReportWarning(String.Format("already initialized constant {0}", name)); + } + + // Initializes anonymous module's name: + RubyModule module = value as RubyModule; + if (module != null && module.Name == null) { + module.Name = owner.MakeNestedModuleName(name); + } + } + + public static RubyMethodVisibility GetSpecialMethodVisibility(RubyMethodVisibility/*!*/ visibility, string/*!*/ methodName) { + return (methodName == Symbols.Initialize || methodName == Symbols.InitializeCopy) ? RubyMethodVisibility.Private : visibility; + } + + public static RubyMemberInfo InvalidateSitesOnOverride(this RubyMemberInfo member) { + if (member != null) { + member.InvalidateSitesOnOverride = true; + } + return member; + } + + #endregion + + #region Modules, Classes + + internal static RubyModule/*!*/ DefineModule(Scope/*!*/ autoloadScope, RubyModule/*!*/ owner, string/*!*/ name) { + Assert.NotNull(autoloadScope, owner); + + object existing; + if (owner.TryGetConstant(autoloadScope, name, out existing)) { + RubyModule module = existing as RubyModule; + if (module == null || module.IsClass) { + throw RubyExceptions.CreateTypeError(String.Format("{0} is not a module", name)); + } + return module; + } else { + // create class/module object: + return owner.Context.DefineModule(owner, name); + } + } + + internal static RubyClass/*!*/ DefineClass(Scope/*!*/ autoloadScope, RubyModule/*!*/ owner, string/*!*/ name, object superClassObject) { + Assert.NotNull(owner); + RubyClass superClass = ToSuperClass(owner.Context, superClassObject); + + object existing; + if (ReferenceEquals(owner, owner.Context.ObjectClass) + ? owner.TryResolveConstant(autoloadScope, name, out existing) + : owner.TryGetConstant(autoloadScope, name, out existing)) { + + RubyClass cls = existing as RubyClass; + if (cls == null || !cls.IsClass) { + throw RubyExceptions.CreateTypeError(String.Format("{0} is not a class", name)); + } + + if (superClassObject != null && !ReferenceEquals(cls.SuperClass, superClass)) { + throw RubyExceptions.CreateTypeError(String.Format("superclass mismatch for class {0}", name)); + } + return cls; + } else { + return owner.Context.DefineClass(owner, name, superClass); + } + } + + private static RubyClass/*!*/ ToSuperClass(RubyContext/*!*/ ec, object superClassObject) { + if (superClassObject != null) { + RubyClass superClass = superClassObject as RubyClass; + if (superClass == null) { + throw RubyExceptions.CreateTypeError(String.Format("superclass must be a Class ({0} given)", ec.GetClassOf(superClassObject).Name)); + } + + if (superClass.IsSingletonClass) { + throw RubyExceptions.CreateTypeError("can't make subclass of virtual class"); + } + + return superClass; + } else { + return ec.ObjectClass; + } + } + + internal static RubyModule/*!*/ GetModuleFromObject(RubyContext/*!*/ context, object obj) { + Assert.NotNull(context); + RubyModule module = obj as RubyModule; + if (module == null) { + throw RubyExceptions.CreateTypeError( + RubyUtils.ObjectToMutableString(context, obj).Append(" is not a class/module").ConvertToString() + ); + } + return module; + } + + public static void RequireNonClasses(params RubyModule[]/*!*/ modules) { + foreach (RubyModule module in modules) { + if (module == null) { + throw RubyExceptions.CreateTypeError("wrong argument type nil (expected Module)"); + } + + if (module.IsClass) { + throw RubyExceptions.CreateTypeError("wrong argument type Class (expected Module)"); + } + } + } + + #endregion + + #region Tracking operations that have the potential for infinite recursion + + public class RecursionTracker { + [ThreadStatic] + private Dictionary _infiniteTracker; + + private Dictionary TryPushInfinite(object obj) { + if (_infiniteTracker == null) { + _infiniteTracker = new Dictionary(ReferenceEqualityComparer.Instance); + } + Dictionary infinite = _infiniteTracker; + if (infinite.ContainsKey(obj)) { + return null; + } + infinite.Add(obj, true); + return infinite; + } + + public IDisposable TrackObject(object obj) { + obj = BaseSymbolDictionary.NullToObj(obj); + Dictionary tracker = TryPushInfinite(obj); + return (tracker == null) ? null : new RecursionHandle(tracker, obj); + } + + private class RecursionHandle : IDisposable { + private readonly Dictionary/*!*/ _tracker; + private readonly object _obj; + + internal RecursionHandle(Dictionary/*!*/ tracker, object obj) { + _tracker = tracker; + _obj = obj; + } + + public void Dispose() { + _tracker.Remove(_obj); + } + } + } + + [MultiRuntimeAware] + private static readonly RecursionTracker _infiniteInspectTracker = new RecursionTracker(); + + public static RecursionTracker InfiniteInspectTracker { + get { return _infiniteInspectTracker; } + } + + [MultiRuntimeAware] + private static readonly RecursionTracker _infiniteToSTracker = new RecursionTracker(); + + public static RecursionTracker InfiniteToSTracker { + get { return _infiniteToSTracker; } + } + + #endregion + + #region Arrays, Hashes + + // MRI checks for a subtype of RubyArray of subtypes of MutableString. + internal static RubyArray AsArrayOfStrings(object value) { + RubyArray array = value as RubyArray; + if (array != null) { + foreach (object obj in array) { + MutableString str = obj as MutableString; + if (str == null) { + return null; + } + } + return array; + } + return null; + } + + public static object SetHashElement(RubyContext/*!*/ context, IDictionary/*!*/ obj, object key, object value) { + MutableString str = key as MutableString; + if (str != null) { + key = str.Duplicate(context, false, str.Clone()).Freeze(); + } else { + key = BaseSymbolDictionary.NullToObj(key); + } + return obj[key] = value; + } + + public static Hash/*!*/ MakeHash(RubyContext/*!*/ context, object[]/*!*/ items) { + Debug.Assert(items != null && items.Length % 2 == 0); + + Hash hash = new Hash(context.EqualityComparer, items.Length / 2); + for (int i = 0; i < items.Length; i += 2) { + Debug.Assert(i + 1 < items.Length); + SetHashElement(context, hash, items[i], items[i + 1]); + } + + return hash; + } + + #endregion + + #region Evals + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + private static int _stringEvalCounter; + + public static RubyCompilerOptions/*!*/ CreateCompilerOptionsForEval(RubyScope/*!*/ targetScope, int line) { + return CreateCompilerOptionsForEval(targetScope, targetScope.GetInnerMostMethodScope(), false, line); + } + + private static RubyCompilerOptions/*!*/ CreateCompilerOptionsForEval(RubyScope/*!*/ targetScope, RubyMethodScope methodScope, + bool isModuleEval, int line) { + + return new RubyCompilerOptions(targetScope.RubyContext.RubyOptions) { + IsEval = true, + IsModuleEval = isModuleEval, + LocalNames = targetScope.GetVisibleLocalNames(), + TopLevelMethodName = (methodScope != null) ? methodScope.Method.DefinitionName : null, + InitialLocation = new SourceLocation(0, line <= 0 ? 1 : line, 1), + }; + } + + public static object Evaluate(MutableString/*!*/ code, RubyScope/*!*/ targetScope, object self, RubyModule module, MutableString file, int line) { + Assert.NotNull(code, targetScope); + + RubyContext context = targetScope.RubyContext; + RubyMethodScope methodScope = targetScope.GetInnerMostMethodScope(); + + Utils.Log(Interlocked.Increment(ref _stringEvalCounter).ToString(), "EVAL"); + + // we want to create a new top-level local scope: + var options = CreateCompilerOptionsForEval(targetScope, methodScope, module != null, line); + + SourceUnit source = context.CreateSnippet(code.ConvertToString(), file != null ? file.ConvertToString() : "(eval)", SourceCodeKind.Statements); + Expression lambda; + try { + lambda = context.ParseSourceCode(source, options, context.RuntimeErrorSink); + } catch (SyntaxError e) { + Utils.Log(e.Message, "EVAL_ERROR"); + Utils.Log(new String('-', 50), "EVAL_ERROR"); + Utils.Log(source.GetCode(), "EVAL_ERROR"); + Utils.Log(new String('-', 50), "EVAL_ERROR"); + throw; + } + Debug.Assert(lambda != null); + + Proc blockParameter; + RubyMethodInfo methodDefinition; + if (methodScope != null) { + blockParameter = methodScope.BlockParameter; + methodDefinition = methodScope.Method; + } else { + blockParameter = null; + methodDefinition = null; + } + + if (context.Options.InterpretedMode) { + return Interpreter.TopLevelExecute(new InterpretedScriptCode(lambda, source), + targetScope, + self, + module, + blockParameter, + methodDefinition, + targetScope.RuntimeFlowControl + ); + } else { + return lambda.Compile(source.EmitDebugSymbols)( + targetScope, + self, + module, + blockParameter, + methodDefinition, + targetScope.RuntimeFlowControl + ); + } + } + + public static object EvaluateInModule(RubyModule/*!*/ self, BlockParam/*!*/ block) { + Assert.NotNull(self, block); + + object returnValue = EvaluateInModuleNoJumpCheck(self, block); + block.BlockJumped(returnValue); + return returnValue; + } + + public static object EvaluateInModule(RubyModule/*!*/ self, BlockParam/*!*/ block, object defaultReturnValue) { + Assert.NotNull(self, block); + + object returnValue = EvaluateInModuleNoJumpCheck(self, block); + + if (block.BlockJumped(returnValue)) { + return returnValue; + } + + return defaultReturnValue; + } + + private static object EvaluateInModuleNoJumpCheck(RubyModule/*!*/ self, BlockParam/*!*/ block) { + block.ModuleDeclaration = self; + return RubyOps.Yield1(self, self, block); + } + + public static object EvaluateInSingleton(object self, BlockParam/*!*/ block) { + // TODO: this is checked in method definition, if no method is defined it is ok. + // => singleton is create in method definition also. + if (!RubyUtils.CanCreateSingleton(self)) { + throw RubyExceptions.CreateTypeError("can't define singleton method for literals"); + } + + block.ModuleDeclaration = block.RubyContext.CreateSingletonClass(self); + + // TODO: flows Public visibility in the block + // Flow "Singleton" method attribute? If we change method attribute + object returnValue = RubyOps.Yield1(self, self, block); + block.BlockJumped(returnValue); + return returnValue; + } + + #endregion + + #region Object Construction + + private static readonly Type[] _ccTypes1 = new Type[] { typeof(RubyClass) }; + private static readonly Type[] _ccTypes2 = new Type[] { typeof(RubyContext) }; +#if !SILVERLIGHT + private static readonly Type[] _serializableTypeSignature = new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }; +#endif + + public static object/*!*/ CreateObject(RubyClass/*!*/ theclass, Hash/*!*/ attributes, bool decorate) { + Assert.NotNull(theclass, attributes); + + Type baseType = theclass.GetUnderlyingSystemType(); + object obj; + if (typeof(ISerializable).IsAssignableFrom(baseType)) { +#if !SILVERLIGHT + BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; + ConstructorInfo ci = baseType.GetConstructor(bindingFlags, null, _serializableTypeSignature, null); + if (ci == null) { +#endif + string message = String.Format("Class {0} does not have a valid deserializing constructor", baseType.FullName); + throw new NotSupportedException(message); +#if !SILVERLIGHT + } + SerializationInfo info = new SerializationInfo(baseType, new FormatterConverter()); + info.AddValue("#class", theclass); + foreach (KeyValuePair pair in attributes) { + string key = pair.Key.ToString(); + key = decorate ? "@" + key : key; + info.AddValue(key, pair.Value); + } + obj = ci.Invoke(new object[2] { info, new StreamingContext(StreamingContextStates.Other, theclass) }); +#endif + } else { + obj = CreateObject(theclass); + foreach (KeyValuePair pair in attributes) { + string key = pair.Key.ToString(); + key = decorate ? "@" + key : key; + theclass.Context.SetInstanceVariable(obj, key, pair.Value); + } + } + return obj; + } + + private static bool IsAvailable(MethodBase method) { + return method != null && !method.IsPrivate && !method.IsFamilyAndAssembly; + } + + public static object/*!*/ CreateObject(RubyClass/*!*/ theClass) { + Assert.NotNull(theClass); + + Type baseType = theClass.GetUnderlyingSystemType(); + if (baseType == typeof(RubyStruct)) { + return RubyStruct.Create(theClass); + } + + object result; + BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; + ConstructorInfo ci; + if (IsAvailable(ci = baseType.GetConstructor(bindingFlags, null, Type.EmptyTypes, null))) { + result = ci.Invoke(new object[0] { }); + } else if (IsAvailable(ci = baseType.GetConstructor(bindingFlags, null, _ccTypes1, null))) { + result = ci.Invoke(new object[1] { theClass }); + } else if (IsAvailable(ci = baseType.GetConstructor(bindingFlags, null, _ccTypes2, null))) { + result = ci.Invoke(new object[1] { theClass.Context }); + } else { + string message = String.Format("Class {0} does not have a valid constructor", theClass.Name); + throw new NotSupportedException(message); + } + return result; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/RuntimeErrorSink.cs b/merlin/main/languages/ruby/Ruby/Runtime/RuntimeErrorSink.cs new file mode 100644 index 0000000000..8f18296be0 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/RuntimeErrorSink.cs @@ -0,0 +1,77 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using System.Runtime.CompilerServices; +using System.Threading; +using IronRuby.Runtime.Calls; +using IronRuby.Compiler; + +namespace IronRuby.Runtime { + public class RuntimeErrorSink : ErrorCounter { + private readonly RubyContext/*!*/ _context; + + internal RuntimeErrorSink(RubyContext/*!*/ context) { + Assert.NotNull(context); + _context = context; + } + + private CallSite> _WriteSite; + + public override void Add(SourceUnit sourceUnit, string message, SourceSpan span, int errorCode, Severity severity) { + if (severity == Severity.Warning && !ReportWarning(_context.Verbose, errorCode)) { + return; + } + + CountError(severity); + + string path; + string codeLine; + int line = span.Start.Line; + if (sourceUnit != null) { + path = sourceUnit.Path; + codeLine = (line > 0) ? sourceUnit.GetCodeLine(line) : null; + } else { + path = null; + codeLine = null; + } + + if (severity == Severity.Error || severity == Severity.FatalError) { + throw new SyntaxError(message, path, line, span.Start.Column, codeLine); + } else { + + if (_WriteSite == null) { + Interlocked.CompareExchange( + ref _WriteSite, + CallSite>.Create(RubyCallAction.Make("write", 1)), + null + ); + } + + message = RubyContext.FormatErrorMessage(message, "warning", path, line, span.Start.Column, null); + + _WriteSite.Target(_WriteSite, _context, _context.StandardErrorOutput, MutableString.CreateMutable(message)); + } + } + + private static bool ReportWarning(object verbose, int errorCode) { + return verbose is bool && ((bool)verbose || !Errors.IsVerboseWarning(errorCode)); + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/RuntimeFlowControl.cs b/merlin/main/languages/ruby/Ruby/Runtime/RuntimeFlowControl.cs new file mode 100644 index 0000000000..44a284fe21 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/RuntimeFlowControl.cs @@ -0,0 +1,379 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Runtime { + + public sealed class RuntimeFlowControl { + [Emitted] + public bool InLoop; + [Emitted] + public bool InRescue; + [Emitted] + public bool InBlock; + [Emitted] + public bool IsActiveMethod; + + public RuntimeFlowControl() { + } + + internal static FieldInfo/*!*/ InRescueField { get { return typeof(RuntimeFlowControl).GetField("InRescue"); } } + internal static FieldInfo/*!*/ InLoopField { get { return typeof(RuntimeFlowControl).GetField("InLoop"); } } + internal static FieldInfo/*!*/ IsActiveMethodField { get { return typeof(RuntimeFlowControl).GetField("IsActiveMethod"); } } + internal static FieldInfo/*!*/ InBlockField { get { return typeof(RuntimeFlowControl).GetField("InBlock"); } } + } + + public static partial class RubyOps { + private static readonly object/*!*/ RetrySingleton = new object(); + + #region User Method Prologue Helpers + + [Emitted] + public static RuntimeFlowControl/*!*/ CreateRfcForMethod(Proc proc) { + RuntimeFlowControl result = new RuntimeFlowControl(); + result.IsActiveMethod = true; + + if (proc != null && proc.Kind == ProcKind.Block) { + proc.Kind = ProcKind.Proc; + proc.Converter = result; + } + + return result; + } + + #endregion + + #region Retry Helpers + + [Emitted] + public static object BlockRetry(BlockParam/*!*/ blockFlowControl) { + if (blockFlowControl.CallerKind == BlockCallerKind.Yield) { + blockFlowControl.SetFlowControl(BlockReturnReason.Retry, null, blockFlowControl.Proc.Kind); + return RetrySingleton; + } else { + throw new LocalJumpError("retry from proc-clause"); + } + } + + [Emitted] + public static object MethodRetry(RuntimeFlowControl/*!*/ rfc, Proc proc) { + if (proc != null) { + return RetrySingleton; + } else { + throw new LocalJumpError("retry used out of rescue", rfc); + } + } + + /// Optional: if called from block + /// Optional: value of the proc parameter of the enclosing method. + [Emitted] + public static void EvalRetry(RuntimeFlowControl/*!*/ rfc) { + + // TODO: get from scope: + BlockParam blockFlowControl = null; + Proc proc = null; + + if (rfc.InBlock && blockFlowControl.CallerKind != BlockCallerKind.Yield) { + throw new LocalJumpError("retry from proc-clause"); + } + + if (rfc.InRescue || rfc.InBlock || proc != null) { + throw new EvalUnwinder(BlockReturnReason.Retry, null, blockFlowControl.Proc.Kind, RetrySingleton); + } else { + throw new LocalJumpError("retry used out of rescue", rfc); + } + } + + #endregion + + #region Break Helpers + + [Emitted] + public static object BlockBreak(BlockParam/*!*/ blockFlowControl, object returnValue) { + return blockFlowControl.Break(returnValue); + } + + [Emitted] + public static void MethodBreak(object returnValue) { + throw new LocalJumpError("unexpected break"); + } + + [Emitted] + public static void EvalBreak(RuntimeFlowControl/*!*/ rfc, object returnValue) { + // TODO: get from scope: + BlockParam blockFlowControl = null; + + if (rfc.InLoop || rfc.InBlock) { + throw new EvalUnwinder(BlockReturnReason.Break, blockFlowControl.Proc.Converter, blockFlowControl.Proc.Kind, returnValue); + } else { + throw new LocalJumpError("unexpected break"); + } + } + + #endregion + + #region Next Helpers + + [Emitted] + public static void MethodNext(RuntimeFlowControl/*!*/ rfc, object returnValue) { + throw new LocalJumpError("unexpected next", rfc); + } + + [Emitted] + public static void EvalNext(RuntimeFlowControl/*!*/ rfc, object returnValue) { + if (rfc.InLoop || rfc.InBlock) { + throw new BlockUnwinder(returnValue, false); // next + } else { + throw new LocalJumpError("unexpected next"); + } + } + + #endregion + + #region Redo Helpers + + [Emitted] + public static void MethodRedo(RuntimeFlowControl/*!*/ rfc) { + throw new LocalJumpError("unexpected redo", rfc); + } + + [Emitted] + public static void EvalRedo(RuntimeFlowControl/*!*/ rfc) { + if (rfc.InLoop || rfc.InBlock) { + throw new BlockUnwinder(null, true); // redo + } else { + throw new LocalJumpError("unexpected redo"); + } + } + + #endregion + + #region Return Helpers + + [Emitted] + public static object BlockReturn(BlockParam/*!*/ blockFlowControl, object returnValue) { + Assert.NotNull(blockFlowControl); + Assert.NotNull(blockFlowControl.Proc); + + Proc proc = blockFlowControl.Proc; + if (blockFlowControl.CallerKind == BlockCallerKind.Call && proc.Kind == ProcKind.Lambda) { + return returnValue; + } + + if (proc.Owner.IsActiveMethod) { + throw new MethodUnwinder(proc.Owner, returnValue); + } + + throw new LocalJumpError("unexpected return"); + } + + [Emitted] + public static object EvalReturn(RuntimeFlowControl/*!*/ rfc, object returnValue) { + // TODO: get from scope: + Proc proc = null; + BlockParam blockFlowControl = null; + + if (rfc.InBlock) { + if (blockFlowControl.CallerKind == BlockCallerKind.Call && proc.Kind == ProcKind.Lambda) { + throw new BlockUnwinder(returnValue, false); + } + + if (proc.Owner.IsActiveMethod) { + throw new MethodUnwinder(proc.Owner, returnValue); + } + + throw new LocalJumpError("unexpected return"); + } else { + // return from the current method: + throw new MethodUnwinder(rfc, returnValue); + } + } + + #endregion + + #region Yield Helpers + + [Emitted] + public static bool BlockYield(RuntimeFlowControl/*!*/ rfc, BlockParam/*!*/ ownerBlockFlowControl, BlockParam/*!*/ yieldedBlockFlowControl, object returnValue) { + Assert.NotNull(rfc, ownerBlockFlowControl, yieldedBlockFlowControl); + + switch (yieldedBlockFlowControl.ReturnReason) { + case BlockReturnReason.Retry: + // the result that the caller returns should already be RetrySingleton: + BlockRetry(ownerBlockFlowControl); + return true; + + case BlockReturnReason.Break: + YieldBlockBreak(rfc, ownerBlockFlowControl, yieldedBlockFlowControl, returnValue); + return true; + } + return false; + } + + [Emitted] + public static bool MethodYield(RuntimeFlowControl rfc, BlockParam/*!*/ yieldedBlockFlowControl, object returnValue) { + Assert.NotNull(yieldedBlockFlowControl); + + switch (yieldedBlockFlowControl.ReturnReason) { + case BlockReturnReason.Retry: + // the result that the caller returns should already be RetrySingleton: + return true; + + case BlockReturnReason.Break: + YieldMethodBreak(rfc, yieldedBlockFlowControl, returnValue); + return true; + } + return false; + } + + [Emitted] + public static bool EvalYield(RuntimeFlowControl/*!*/ rfc, BlockParam/*!*/ yieldedBlockFlowControl, object returnValue) { + Assert.NotNull(rfc, yieldedBlockFlowControl); + + switch (yieldedBlockFlowControl.ReturnReason) { + case BlockReturnReason.Retry: + // the result that the caller returns should already be RetrySingleton: + EvalRetry(rfc); + return true; + + case BlockReturnReason.Break: + YieldEvalBreak(yieldedBlockFlowControl, returnValue); + return true; + } + return false; + } + + // post-yield break ops: + + private static void YieldMethodBreak(RuntimeFlowControl rfc, BlockParam/*!*/ yieldedBlockFlowControl, object returnValue) { + Assert.NotNull(yieldedBlockFlowControl); + + // target proc-converter: + RuntimeFlowControl targetFrame = yieldedBlockFlowControl.TargetFrame; + Debug.Assert(targetFrame != null); + + if (targetFrame.IsActiveMethod) { + // optimize break to the current frame: + if (targetFrame == rfc) { + return; + } else { + throw new MethodUnwinder(targetFrame, returnValue); + } + } else { + throw new LocalJumpError("break from proc-closure"); + } + } + + private static void YieldBlockBreak(RuntimeFlowControl rfc, BlockParam/*!*/ ownerBlockFlowControl, BlockParam/*!*/ yieldedBlockFlowControl, object returnValue) { + Assert.NotNull(ownerBlockFlowControl, yieldedBlockFlowControl); + + // target proc-converter: + RuntimeFlowControl targetFrame = yieldedBlockFlowControl.TargetFrame; + Debug.Assert(targetFrame != null); + + if (targetFrame.IsActiveMethod) { + if (targetFrame == rfc) { + // The current primary super-frame is the proc-converter, however we are still in the block frame that needs to be unwound. + // Sets the owner's BFC to exit the current block (recursively up to the primary frame). + ownerBlockFlowControl.SetFlowControl(BlockReturnReason.Break, targetFrame, yieldedBlockFlowControl.SourceProcKind); + return; + } else { + throw new MethodUnwinder(targetFrame, returnValue); + } + } else { + throw new LocalJumpError("break from proc-closure"); + } + } + + private static void YieldEvalBreak(BlockParam/*!*/ blockFlowControl, object returnValue) { + if (blockFlowControl.TargetFrame.IsActiveMethod) { + // do not "optimize" for current RFC, we need to unwind stack anyway + throw new MethodUnwinder(blockFlowControl.TargetFrame, returnValue); + } else { + throw new LocalJumpError("break from proc-closure"); + } + } + + #endregion + + #region Call Helpers + + /// + /// Proc#call helper emitted in the rule. Handles break from the called block. + /// + [Emitted] + public static bool MethodProcCall(BlockParam/*!*/ blockFlowControl, object returnValue) { + Assert.NotNull(blockFlowControl); + + Debug.Assert(blockFlowControl.ReturnReason != BlockReturnReason.Retry, "Cannot retry a block invoked via call"); + + if (blockFlowControl.ReturnReason == BlockReturnReason.Break) { + // breaking thru call - a kind of the break originator is checked: + if (blockFlowControl.SourceProcKind != ProcKind.Lambda) { + YieldMethodBreak(null, blockFlowControl, returnValue); + Debug.Assert(false, "YieldBreak should throw"); + } + return true; + } + return false; + } + + #endregion + + #region EH Helpers + + [Emitted] + public static bool CanRescue(RuntimeFlowControl/*!*/ rfc, Exception/*!*/ e) { + if (e is StackUnwinder) { + return false; + } + + LocalJumpError lje = e as LocalJumpError; + if (lje != null && ReferenceEquals(lje.SkipFrame, rfc)) { + return false; + } + + return true; + } + + #endregion + + #region Method Call with Block Helpers + + [Emitted] + public static bool IsRetrySingleton(object value) { + return value == RetrySingleton; + } + + [Emitted] + public static object PropagateRetrySingleton(object other, object possibleRetrySingleton) { + return IsRetrySingleton(possibleRetrySingleton) ? possibleRetrySingleton : other; + } + + [Emitted] + public static object GetRetrySingleton() { + return RetrySingleton; + } + + #endregion + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/StackUnwinder.cs b/merlin/main/languages/ruby/Ruby/Runtime/StackUnwinder.cs new file mode 100644 index 0000000000..36fc83410f --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/StackUnwinder.cs @@ -0,0 +1,90 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using Microsoft.Scripting.Utils; +using IronRuby.Builtins; +using IronRuby.Compiler.Generation; + +namespace IronRuby.Runtime { + + public abstract class StackUnwinder : Exception { + [Emitted] + public readonly object ReturnValue; + + internal static FieldInfo ReturnValueField { get { return typeof(StackUnwinder).GetField("ReturnValue"); } } + //internal static MethodInfo GetMethodCallWithBlockResultMethod { get { return typeof(StackUnwinder).GetMethod("GetMethodCallWithBlockResult"); } } + + public StackUnwinder(object returnValue) { + ReturnValue = returnValue; + } + } + + /// + /// Return. + /// + public sealed class MethodUnwinder : StackUnwinder { + [Emitted] + public readonly RuntimeFlowControl/*!*/ TargetFrame; + + internal static FieldInfo TargetFrameField { get { return typeof(MethodUnwinder).GetField("TargetFrame"); } } + + internal MethodUnwinder(RuntimeFlowControl/*!*/ targetFrame, object returnValue) + : base(returnValue) { + Assert.NotNull(targetFrame); + TargetFrame = targetFrame; + } + } + + /// + /// Retry/Break. + /// + public sealed class EvalUnwinder : StackUnwinder { + private readonly RuntimeFlowControl _targetFrame; + private readonly ProcKind _sourceProcKind; + + [Emitted] + public readonly BlockReturnReason Reason; + + internal static FieldInfo ReasonField { get { return typeof(EvalUnwinder).GetField("Reason"); } } + + internal RuntimeFlowControl TargetFrame { get { return _targetFrame; } } + internal ProcKind SourceProcKind { get { return _sourceProcKind; } } + + internal EvalUnwinder(BlockReturnReason reason, RuntimeFlowControl targetFrame, ProcKind sourceProcKind, object returnValue) + : base(returnValue) { + + Reason = reason; + _targetFrame = targetFrame; + _sourceProcKind = sourceProcKind; + } + } + + /// + /// Redo/Next. + /// + public sealed class BlockUnwinder : StackUnwinder { + [Emitted] + public readonly bool IsRedo; + + internal static FieldInfo IsRedoField { get { return typeof(BlockUnwinder).GetField("IsRedo"); } } + + internal BlockUnwinder(object returnValue, bool isRedo) + : base(returnValue) { + IsRedo = isRedo; + } + } +} diff --git a/merlin/main/languages/ruby/Ruby/Runtime/Utils.cs b/merlin/main/languages/ruby/Ruby/Runtime/Utils.cs new file mode 100644 index 0000000000..c729afc3e6 --- /dev/null +++ b/merlin/main/languages/ruby/Ruby/Runtime/Utils.cs @@ -0,0 +1,97 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Utils; +using System.Diagnostics; +using System.Text; + +namespace IronRuby.Runtime { + public static class Utils { + public static class Array { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")] + public static readonly byte[] EmptyBytes = new byte[0]; + + public static int IndexOf(/*this*/ string[]/*!*/ array, string/*!*/ value, StringComparer/*!*/ comparer) { + ContractUtils.RequiresNotNull(array, "array"); + ContractUtils.RequiresNotNull(value, "value"); + ContractUtils.RequiresNotNull(comparer, "comparer"); + + for (int i = 0; i < array.Length; i++) { + if (comparer.Equals(array[i], value)) { + return i; + } + } + + return -1; + } + } + + [Conditional("DEBUG")] + public static void Log(string/*!*/ message, string/*!*/ category) { +#if !SILVERLIGHT + Debug.WriteLine((object)message, category); +#endif + } + + internal static bool IsAscii(this string/*!*/ str) { + for (int i = 0; i < str.Length; i++) { + if (str[i] > 0x7f) { + return false; + } + } + return true; + } + + public static long DateTimeTicksFromStopwatch(long elapsedStopwatchTicks) { +#if !SILVERLIGHT + if (Stopwatch.IsHighResolution) { + return (long)(((double)elapsedStopwatchTicks) * 10000000.0 / (double)Stopwatch.Frequency); + } +#endif + return elapsedStopwatchTicks; + } + } + + public static class StringBuilderExtensions { + public static int IndexOf(this StringBuilder/*!*/ sb, char value) { + ContractUtils.RequiresNotNull(sb, "sb"); + + for (int i = 0; i < sb.Length; i++) { + if (sb[i] == value) { + return i; + } + } + + return -1; + } + } +} + +#if SILVERLIGHT +namespace System.Diagnostics { + internal struct Stopwatch { + public void Start() { + } + + public void Stop() { + } + + public static long GetTimestamp() { + return 0; + } + } +} +#endif diff --git a/merlin/main/languages/ruby/Scripts/bin/igem b/merlin/main/languages/ruby/Scripts/bin/igem new file mode 100644 index 0000000000..3bb0d083a1 --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/igem @@ -0,0 +1,28 @@ +#!c:/utils/ruby/bin/ruby.exe +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +require 'rubygems' +require 'rubygems/gem_runner' +require 'rubygems/exceptions' + +required_version = Gem::Requirement.new "> 1.8.3" + +unless required_version.satisfied_by? Gem.ruby_version then + abort "Expected Ruby Version #{required_version}, was #{Gem.ruby_version}" +end + +# We need to preserve the original ARGV to use for passing gem options +# to source gems. If there is a -- in the line, strip all options after +# it...its for the source building process. +args = !ARGV.include?("--") ? ARGV.clone : ARGV[0...ARGV.index("--")] + +begin + Gem::GemRunner.new.run args +rescue Gem::SystemExitException => e + exit e.exit_code +end + diff --git a/merlin/main/languages/ruby/Scripts/bin/igem.bat b/merlin/main/languages/ruby/Scripts/bin/igem.bat new file mode 100644 index 0000000000..dd2fc41fb8 --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/igem.bat @@ -0,0 +1,2 @@ +@ECHO OFF +@"ir.exe" "%~dpn0" %* diff --git a/merlin/main/languages/ruby/Scripts/bin/iirb b/merlin/main/languages/ruby/Scripts/bin/iirb new file mode 100644 index 0000000000..424f96f8fc --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/iirb @@ -0,0 +1,21 @@ +#!/usr/bin/env ruby +# +# irb.rb - intaractive ruby +# $Release Version: 0.9.5 $ +# $Revision: 11708 $ +# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# + +require "irb" + +if __FILE__ == $0 + IRB.start(__FILE__) +else + # check -e option + if /^-e$/ =~ $0 + IRB.start(__FILE__) + else + IRB.setup(__FILE__) + end +end diff --git a/merlin/main/languages/ruby/Scripts/bin/iirb.bat b/merlin/main/languages/ruby/Scripts/bin/iirb.bat new file mode 100644 index 0000000000..c3cd36eb0e --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/iirb.bat @@ -0,0 +1,2 @@ +@ECHO OFF +"%~d0%~p0ir.exe" "%~d0%~p0%~n0" %* diff --git a/merlin/main/languages/ruby/Scripts/bin/irails b/merlin/main/languages/ruby/Scripts/bin/irails new file mode 100644 index 0000000000..f16e9c8aeb --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/irails @@ -0,0 +1,19 @@ +#!c:/utils/ruby/bin/ruby.exe +# +# This file was generated by RubyGems. +# +# The application 'rails' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0" + +if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then + version = $1 + ARGV.shift +end + +gem 'rails', version +load 'rails' diff --git a/merlin/main/languages/ruby/Scripts/bin/irails.bat b/merlin/main/languages/ruby/Scripts/bin/irails.bat new file mode 100644 index 0000000000..dd2fc41fb8 --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/irails.bat @@ -0,0 +1,2 @@ +@ECHO OFF +@"ir.exe" "%~dpn0" %* diff --git a/merlin/main/languages/ruby/Scripts/bin/irake b/merlin/main/languages/ruby/Scripts/bin/irake new file mode 100644 index 0000000000..a76d282f76 --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/irake @@ -0,0 +1,19 @@ +#!c:/vsl/Merlin/External/Languages/Ruby/Ruby-1.8.6/bin/ruby.exe +# +# This file was generated by RubyGems. +# +# The application 'rake' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +version = ">= 0" + +if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then + version = $1 + ARGV.shift +end + +gem 'rake', version +load 'rake' diff --git a/merlin/main/languages/ruby/Scripts/bin/irake.bat b/merlin/main/languages/ruby/Scripts/bin/irake.bat new file mode 100644 index 0000000000..dd2fc41fb8 --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/irake.bat @@ -0,0 +1,2 @@ +@ECHO OFF +@"ir.exe" "%~dpn0" %* diff --git a/merlin/main/languages/ruby/Scripts/bin/irake.cmd b/merlin/main/languages/ruby/Scripts/bin/irake.cmd new file mode 100644 index 0000000000..0612c86279 --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/irake.cmd @@ -0,0 +1 @@ +@ir "%~dp0/rake" %* diff --git a/merlin/main/languages/ruby/Scripts/bin/irdoc b/merlin/main/languages/ruby/Scripts/bin/irdoc new file mode 100644 index 0000000000..b1538ac508 --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/irdoc @@ -0,0 +1,66 @@ +#!/usr/bin/env ruby +# RDoc: Documentation tool for source code +# (see lib/rdoc/rdoc.rb for more information) +# +# Copyright (c) 2003 Dave Thomas +# Released under the same terms as Ruby +# +# $Revision: 11708 $ + +## Transitional Hack #### +# +# RDoc was initially distributed independently, and installed +# itself into /lib/ruby/site_ruby//rdoc... +# +# Now that RDoc is part of the distribution, it's installed into +# /lib/ruby/, which unfortunately appears later in the +# search path. This means that if you have previously installed RDoc, +# and then install from ruby-lang, you'll pick up the old one by +# default. This hack checks for the condition, and readjusts the +# search path if necessary. + +def adjust_for_existing_rdoc(path) + + $stderr.puts %{ + It seems as if you have a previously-installed RDoc in + the directory #{path}. + + Because this is now out-of-date, you might want to consider + removing the directories: + + #{File.join(path, "rdoc")} + + and + + #{File.join(path, "markup")} + + } + + # Move all the site_ruby directories to the end + p $: + $:.replace($:.partition {|path| /site_ruby/ !~ path}.flatten) + p $: +end + +$:.each do |path| + if /site_ruby/ =~ path + rdoc_path = File.join(path, 'rdoc', 'rdoc.rb') + if File.exists?(rdoc_path) + adjust_for_existing_rdoc(path) + break + end + end +end + +## End of Transitional Hack ## + + +require 'rdoc/rdoc' + +begin + r = RDoc::RDoc.new + r.document(ARGV) +rescue RDoc::RDocError => e + $stderr.puts e.message + exit(1) +end diff --git a/merlin/main/languages/ruby/Scripts/bin/irdoc.bat b/merlin/main/languages/ruby/Scripts/bin/irdoc.bat new file mode 100644 index 0000000000..c3cd36eb0e --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/irdoc.bat @@ -0,0 +1,2 @@ +@ECHO OFF +"%~d0%~p0ir.exe" "%~d0%~p0%~n0" %* diff --git a/merlin/main/languages/ruby/Scripts/bin/iri b/merlin/main/languages/ruby/Scripts/bin/iri new file mode 100644 index 0000000000..77d612f2bd --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/iri @@ -0,0 +1,48 @@ +#!/usr/bin/env ruby +# usage: +# +# ri name... +# +# where name can be +# +# Class | Class::method | Class#method | Class.method | method +# +# All names may be abbreviated to their minimum unbiguous form. If a name +# _is_ ambiguous, all valid options will be listed. +# +# The form '.' method matches either class or instance methods, while +# #method matches only instance and ::method matches only class methods. +# +# +# == Installing Documentation +# +# 'ri' uses a database of documentation built by the RDoc utility. +# +# So, how do you install this documentation on your system? +# It depends on how you installed Ruby. +# +# If you installed Ruby from source files (that is, if it some point +# you typed 'make' during the process :), you can install the RDoc +# documentation yourself. Just go back to the place where you have +# your Ruby source and type +# +# make install-doc +# +# You'll probably need to do this as a superuser, as the documentation +# is installed in the Ruby target tree (normally somewhere under +# /usr/local. +# +# If you installed Ruby from a binary distribution (perhaps +# using a one-click installer, or using some other packaging system), +# then the team that produced the package probably forgot to package +# the documentation as well. Contact them, and see if they can add +# it to the next release. +# + + +require 'rdoc/ri/ri_driver' + +###################################################################### + +ri = RiDriver.new +ri.process_args diff --git a/merlin/main/languages/ruby/Scripts/bin/iri.bat b/merlin/main/languages/ruby/Scripts/bin/iri.bat new file mode 100644 index 0000000000..c3cd36eb0e --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bin/iri.bat @@ -0,0 +1,2 @@ +@ECHO OFF +"%~d0%~p0ir.exe" "%~d0%~p0%~n0" %* diff --git a/merlin/main/languages/ruby/Scripts/bm_filewrite.rb b/merlin/main/languages/ruby/Scripts/bm_filewrite.rb new file mode 100644 index 0000000000..f50933b7e1 --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/bm_filewrite.rb @@ -0,0 +1,39 @@ +class TestFile + + DATASIZE = 100*1024 + + def self.filename + root = Dir.pwd + file = "test" + $i ||= 0 + $i += 1 + f = File.join(root,file + $i.to_s) + File.delete(f) if File.exists?(f) + f + end + + def self.data + s = "" + DATASIZE.times { s << rand(255).to_i } + s + end +end +#setup +def setup + data = {} + 10.times { data[TestFile.filename] = TestFile.data } + data +end + +def test(name) + data = setup + p name + start = Time.now + data.each {|k,v| File.open(k,name) {|file| file.write v}} + ending = Time.now - start + p ending +end + +test("wb") +test("w") + diff --git a/merlin/main/languages/ruby/Scripts/merb.rb b/merlin/main/languages/ruby/Scripts/merb.rb new file mode 100644 index 0000000000..f94306f9da --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/merb.rb @@ -0,0 +1,161 @@ +MERLIN_ROOT = ENV['MERLIN_ROOT'] + +# this is used in Gem.default_dir in +# C:\M4\Merlin\External\Languages\Ruby\ruby-1.8.6\lib\ruby\gems\1.8\gems\rubygems-update-1.2.0\lib\rubygems\defaults.rb +RUBY_ENGINE = 'ruby' + +# load_gems_in loads gems: +# C:\M4\Merlin\External\Languages\Ruby\ruby-1.8.6\lib\ruby\site_ruby\1.8\rubygems\source_index.rb + +CURRENT_DIR = Dir.pwd +MERB_APP_ROOT = MERLIN_ROOT + "/../External/Languages/IronRuby/merb/test_app" +ENV['GEM_HOME'] = MERLIN_ROOT + "/../External/Languages/Ruby/ruby-1.8.6/lib/ruby/gems/1.8" + +puts "Booting merb ..." + +# `attrib -r %MERB_APP_ROOT%/log/merb.main.pid` + +Dir.chdir MERB_APP_ROOT + +def trace_requires + puts 'Tracing requires' + + $REQUIRE_DEPTH = 0 + Kernel.module_eval do + alias x_require require + alias x_load load + + def require path + $REQUIRE_DEPTH += 1 + puts "#{$REQUIRE_DEPTH}\t" + ('| ' * $REQUIRE_DEPTH) + "> #{path}" + x_require path + ensure + $REQUIRE_DEPTH -= 1 + end + + def load path, *wrap + $REQUIRE_DEPTH += 1 + puts "#{$REQUIRE_DEPTH}\t" + ('| ' * $REQUIRE_DEPTH) + "> #{path} (LOAD)" + x_load path, *wrap + ensure + $REQUIRE_DEPTH -= 1 + end + end +end + +if ARGV.include? "-tr" + ARGV.delete "-tr" + + trace_requires +end + +if ARGV.include? "-tc" + ARGV.delete "-tc" + + call_depth = 0 + + set_trace_func proc { |op, file, line, method, b, cls| + if op == "call" + puts "#{call_depth}\t" + ('| ' * call_depth) + "> #{cls}::#{method} (#{line} in #{file.nil? ? nil : file.gsub('\\','/')})" + call_depth += 1 + elsif op == "return" + call_depth -= 1 + end + } +end + +ARGV << '-a' +ARGV << 'webrick' + +require 'rbconfig' + +if ARGV.include? "-tcx" + ARGV.delete "-tcx" + + require 'thread' + + def trace_thread_calls id + call_depth = 0 + tag_open = false + trace_out = File.open("#{CURRENT_DIR}/trace_#{defined?(IRONRUBY_VERSION) ? 'ir' : 'mri'}_#{id}.xml", "a") + + Thread.current[:__tracekey__] = lambda { |op, file, line, method, cls| + if op == "call" + if tag_open + trace_out.puts(">") + end + + trace_out.print((' ' * call_depth) + %Q{ 0 + call_depth -= 1 + + if tag_open + trace_out.puts("/>") + tag_open = false + else + trace_out.puts((' ' * call_depth) + "") + end + end + trace_out.flush + } + + puts "tracing for #{id}: #{trace_out.path}" + end + + class Thread + $TRACE_THREAD_ID = 0 + TRACE_MUTEX = Mutex.new + + class << self + alias __new new + + def new *a, &p + __new *a do + id = nil + TRACE_MUTEX.synchronize { $TRACE_THREAD_ID += 1; id = $TRACE_THREAD_ID } + + trace_thread_calls id + p.call + end + end + end + end + + trace_thread_calls "main" + + set_trace_func proc { |op, file, line, method, _, cls| + if op == "call" or op == "return" + t = Thread.current[:__tracekey__] + t[op, file, line, method, cls] unless t.nil? + end + } +end + +# make Hash.each sorted so that traces match +class Hash + alias __eachXXX each + + def each &p + entries = [] + __eachXXX { |k,v| entries << [k, v]; } + + entries.sort! { |x,y| + x[0].inspect <=> y[0].inspect + } + + entries.each &p + self + end + + def each_value &p + each { |k,v| p[v] } + end + + def each_key &p + each { |k,v| p[k] } + end +end + +load MERLIN_ROOT + "/../External/Languages/Ruby/ruby-1.8.6/bin/merb" diff --git a/merlin/main/languages/ruby/Scripts/rewrite.rb b/merlin/main/languages/ruby/Scripts/rewrite.rb new file mode 100644 index 0000000000..1917493032 --- /dev/null +++ b/merlin/main/languages/ruby/Scripts/rewrite.rb @@ -0,0 +1,104 @@ +require 'fileutils' +require 'tmpdir' + +def usage + STDERR.puts 'Usage: rewrite {assembly-path} [/include:{included-il}] [/config:(DEBUG|RELEASE)] [/sign:{key}]' + exit(-1) +end + +ARGV.each_with_index do |option,index| + if option =~ /\/([a-z]+)(\:(.*))?/i + case $1.upcase + when "INCLUDE": INCLUDE = $3 + when "CONFIG": OPTIMIZE = $3.upcase.include?('RELEASE') + when "SIGN": SIGN_KEY = $3 + else + usage + end + elsif index == 0 + ASSEMBLY = File.expand_path(option) + end +end + +usage if not defined? ASSEMBLY + +#TODO: Mono? +ASSEMBLY_FILENAME = File.basename(ASSEMBLY) +ASSEMBLY_DIR = File.dirname(ASSEMBLY) +MERLIN_ROOT = File.expand_path(File.join(File.dirname(__FILE__), "../../..")) +ILDASM = File.join(MERLIN_ROOT, "Utilities/IL/ildasm.exe") +ILASM = File.join(ENV["FrameworkDir"], ENV["FrameworkVersion"], "ilasm.exe") + +[ASSEMBLY, ILDASM, ILASM].each do |path| + next if File.exist?(path) + STDERR.puts "File #{path} not found" + exit(-2) +end + +def run exe, *args + command = "#{exe} #{args.join(' ')}" + puts command if $DEBUG + output = `#{command}` + if $?.exitstatus != 0 + STDERR.puts command + STDERR.puts + STDERR.puts output + exit($?.exitstatus) + end +end + +def using_tempdir + temp = Dir.tmpdir + i = $$ + begin + begin + dir = File.join(temp, "IR.#{Time.now.strftime("%Y.%m.%d-%I.%M.%S")}.#{i}") + end while File.exists? dir + Dir.mkdir dir + rescue SystemCallError + retry if File.exists? dir + raise + end + begin + yield dir + ensure + FileUtils.rm_rf dir unless $DEBUG + end +end + +def q str + '"' + str + '"' +end + +def rewrite path + if defined? INCLUDE + File.open(path, "a") do |il| + il.puts + il.puts %Q{#include "#{INCLUDE}"} + end + end +end + +using_tempdir do |temp_dir| + puts temp_dir if $DEBUG + Dir.chdir temp_dir + + il_file = ASSEMBLY_FILENAME + '.il' + res_file = ASSEMBLY_FILENAME + '.res' + ext = File.extname(ASSEMBLY_FILENAME) + pdb_file = File.basename(ASSEMBLY_FILENAME, ext) + '.pdb'; + + run q(ILDASM), q(ASSEMBLY), q("/OUT=#{il_file}"), "/TEXT /LINENUM" + + rewrite il_file + + run q(ILASM), q(il_file), q("/RESOURCE=#{res_file}"), q("/OUTPUT=#{ASSEMBLY_FILENAME}"), "/QUIET /#{ext[1..-1]}", + OPTIMIZE ? "/OPTIMIZE /FOLD /PDB" : "/DEBUG", + defined?(SIGN_KEY) ? q("/KEY=#{SIGN_KEY}") : nil + + FileUtils.mv ASSEMBLY_FILENAME, ASSEMBLY_DIR + FileUtils.mv pdb_file, ASSEMBLY_DIR +end + + + diff --git a/merlin/main/languages/ruby/SpecSharp.targets b/merlin/main/languages/ruby/SpecSharp.targets new file mode 100644 index 0000000000..2659906362 --- /dev/null +++ b/merlin/main/languages/ruby/SpecSharp.targets @@ -0,0 +1,71 @@ + + + + + + + + diff --git a/merlin/main/languages/ruby/Tests/Builtin/Array/test_array.rb b/merlin/main/languages/ruby/Tests/Builtin/Array/test_array.rb new file mode 100644 index 0000000000..1dac66e89f --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Array/test_array.rb @@ -0,0 +1,1676 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "../../Util/simple_test.rb" + +# adds init method so that we don't need send to test private initialize +class Array + def init *a, &p + initialize *a, &p + end +end + +class MyArray < Array; end + +describe "Array#new" do + it "creates an empty array via language syntactical sugar" do + a = [] + a.length.should == 0 + end + + it "creates an empty array explicitly" do + a = Array.new + a.length.should == 0 + end + + it "creates an array of an explicit size" do + a = Array.new(5) + a.should == [nil, nil, nil, nil, nil] + end + + it "creates calls to_int on the size argument" do + a = Array.new(5.2) + a.should == [nil, nil, nil, nil, nil] + + class Bob1 + def to_int + 5 + end + end + b = Array.new(Bob1.new) + b.should == [nil, nil, nil, nil, nil] + end + + it "creates an instance of a subclassed array type" do + c = MyArray.new + c.length.should == 0 + end + + it "creates and initializes an array" do + a = [1,2,3] + a.length.should == 3 + + b = [1] + b.length.should == 1 + end + + it "creates and initializes an array using a block" do + a = Array.new(5) { |x| x + 1 } + a.should == [1,2,3,4,5] + end + + it "creates and initializes an array using a block, size is to_int convertible" do + obj = Object.new + class << obj + def to_int; 3; end + end + a = Array.new(obj) { |x| x + 1 } + a.should == [1,2,3] + end + + it "returns result of the block break" do + a = Array.new(5) { |x| if x < 2 then x else break 'foo' end } + a.should == 'foo' + end +end + +describe "Array#initialize" do + it "clears the array before adding items" do + a = [1,2,3] + r = a.init([3,4,5]) + r.object_id.should == a.object_id + a.should == [3,4,5] + end + + it "uses nil values when block is nil" do + a = [1] + r = a.init(3,&nil) + r.object_id.should == a.object_id + a.should == [nil, nil, nil] + end + + it "is not atomic wrt break from block" do + a = [1,2,3] + r = a.init(5) { |x| if x < 2 then x else break 'foo' end } + r.should == 'foo' + a.should == [0,1] + end +end + +describe "Array#<=>" do + skip "<=> should call <=> left to right and return first non-0 result" do + [-1, +1, nil, "foobar"].each do |result| + lhs = Array.new(3) { Object.new } + rhs = Array.new(3) { Object.new } + + lhs[0].should_receive(:<=>, :with => [rhs[0]], :returning => 0) + lhs[1].should_receive(:<=>, :with => [rhs[1]], :returning => result) + lhs[2].should_not_receive(:<=>) + + (lhs <=> rhs).should == result + end + end + + it "<=> should be 0 if the arrays are equal" do + ([] <=> []).should == 0 + ([1, 2, 3, 4, 5, 6] <=> [1, 2, 3, 4, 5, 6]).should == 0 + # TODO: once we have numerical comparisons working correctly we can + # uncomment this test + #([1, 2, 3, 4, 5, 6] <=> [1, 2, 3, 4, 5.0, 6.0]).should == 0 + end + + it "<=> should be -1 if the array is shorter than the other array" do + ([] <=> [1]).should == -1 + ([1, 1] <=> [1, 1, 1]).should == -1 + end + + it "<=> should be +1 if the array is longer than the other array" do + ([1] <=> []).should == +1 + ([1, 1, 1] <=> [1, 1]).should == +1 + end + + it "<=> should call to_ary on its argument" do + class Bob100 + def to_ary + [1, 2, 3] + end + end + obj = Bob100.new + ([4, 5] <=> obj).should == ([4, 5] <=> obj.to_ary) + end +end + +describe "Array#assoc" do + # BUGBUG: this reveals the nested dynamic site bug as well + skip "(assoc) should return the first contained array the first element of which is obj" do + s1 = [ "colors", "red", "blue", "green" ] + s2 = [ "letters", "a", "b", "c" ] + a = [ s1, s2, "foo", [], [4] ] + a.assoc("letters").should == %w{letters a b c} + a.assoc(4).should == [4] + a.assoc("foo").should == nil + end + + skip "(assoc) should call == on argument" do + key = Object.new + items = Array.new(3) { [Object.new, "foo"] } + items[0][0].should_receive(:==, :with => [key], :returning => false) + items[1][0].should_receive(:==, :with => [key], :returning => true) + items[2][0].should_not_receive(:==, :with => [key]) + items.assoc(key).should == items[1] + end +end + +describe "Array#fetch" do + it "(fetch) should return the element at index" do + [[1, 2, 3].fetch(1), [1, 2, 3, 4].fetch(-1)].should == [2, 4] + end + + it "(fetch) should raise if there is no element at index" do + should_raise(IndexError) { [1, 2, 3].fetch(3) } + should_raise(IndexError) { [1, 2, 3].fetch(-4) } + end + + it "(fetch) with default should return default if there is no element at index" do + [1, 2, 3].fetch(5, :not_found).should == :not_found + [1, 2, 3].fetch(5, nil).should == nil + [nil].fetch(0, :not_found).should == nil + end + + it "(fetch) with block should return the value of block if there is no element at index" do + [1, 2, 3].fetch(9) { |i| i * i }.should == 81 + end + + it "(fetch) default block takes precedence over its default argument" do + [1, 2, 3].fetch(9, :foo) { |i| i * i }.should == 81 + end + + it "(fetch) should call to_int on its argument" do + class Bob102 + def to_int + 0 + end + end + x = Bob102.new + [1, 2, 3].fetch(x).should == 1 + end +end + +describe "Array#include?" do + # TODO: same bugs related to heterogeneous calls to RubySites.Equal() + it "(include?) should return true if object is present, false otherwise" do + [1, 2, "a", "b"].include?("c").should == false + [1, 2, "a", "b"].include?("a").should == true + end + + skip "(include?) calls == on elements from left to right until success" do + key = "x" + ary = Array.new(3) { Object.new } + ary[0].should_receive(:==, :with => [key], :returning => false) + ary[1].should_receive(:==, :with => [key], :returning => true) + ary[2].should_not_receive(:==) + + ary.include?(key).should == true + end +end + +describe "Array#index" do + it "(index) returns the index of the first element == object" do + class Bob103 + def ==(obj) + 3 == obj + end + end + x = Bob103.new + + [2, x, 3, 1, 3, 1].index(3).should == 1 + end + + it "(index) returns 0 if first element == object" do + [2, 1, 3, 2, 5].index(2).should == 0 + end + + it "(index) returns size-1 if only last element == to object" do + [2, 1, 3, 1, 5].index(5).should == 4 + end + + it "(index) returns nil if no element == to object" do + [2, 1, 1, 1, 1].index(3).should == nil + end +end + +describe "Array#indexes and Array#indices are DEPRECATED synonyms for values_at" do + it "returns a new array containing the elements indexed by its parameters" do + a = [1,2,3,4,5] + a.indexes(0,1,2,3,4).should == [1,2,3,4,5] + a.indexes(0,0,0).should == [1,1,1] + end + + it "works with negative offset indices" do + a = [1,2,3,4,5] + a.indexes(-1,-2,-3,-4,-5).should == [5,4,3,2,1] + a.indexes(-5,0).should == [1, 1] + end + + it "returns nil for elements that are beyond the indices of the array" do + a = [1,2,3,4,5] + a.indexes(4,5,-5,-6).should == [5,nil,1,nil] + end + + skip "(indexes) and (indices) with integer indices are DEPRECATED synonyms for values_at" do + array = [1, 2, 3, 4, 5] + params = [1, 0, 5, -1, -8, 10] + array.indexes(*params).should == array.values_at(*params) + array.indices(*params).should == array.values_at(*params) + end + + skip '(indexes) and (indices) can be given ranges which are returned as nested arrays (DEPRECATED)' do + array = [1, 2, 3, 4, 5] + params = [0..2, 1...3, 4..4] + array.indexes(*params).should == [[1, 2, 3], [2, 3], [5]] + array.indices(*params).should == [[1, 2, 3], [2, 3], [5]] + end +end + +describe "Array#rassoc" do + it "(rassoc) should return the first contained array whose second element is == object" do + ary = [[1, "a", 0.5], [2, "b"], [3, "b"], [4, "c"], [], [5], [6, "d"]] + ary.rassoc("a").should == [1, "a", 0.5] + ary.rassoc("b").should == [2, "b"] + ary.rassoc("d").should == [6, "d"] + ary.rassoc("z").should == nil + end + + skip "(rassoc) should call == on argument" do + key = Object.new + items = Array.new(3) { ["foo", Object.new, "bar"] } + items[0][1].should_receive(:==, :with => [key], :returning => false) + items[1][1].should_receive(:==, :with => [key], :returning => true) + items[2][1].should_not_receive(:==, :with => [key]) + items.rassoc(key).should == items[1] + end +end + +describe "Array#rindex" do + skip "(rindex) returns the first index backwards from the end where element == to object" do + key = 3 + ary = Array.new(3) { Object.new } + ary[2].should_receive(:==, :with => [key], :returning => false) + ary[1].should_receive(:==, :with => [key], :returning => true) + ary[0].should_not_receive(:==) + + ary.rindex(key).should == 1 + end + + it "(rindex) returns size-1 if last element == object" do + [2, 1, 3, 2, 5].rindex(5).should == 4 + end + + it "(rindex) returns 0 if only first element == object" do + [2, 1, 3, 1, 5].rindex(2).should == 0 + end + + it "(rindex) returns nil if no element == object" do + [1, 1, 3, 2, 1, 3].rindex(4).should == nil + end +end + +describe "Array#empty?" do + it "tests for an empty array" do + a = [] + b = [1,2,3] + + a.empty?.should == true + b.empty?.should_not == true + end +end + +describe "Array#to_a and Array#to_ary" do + it "to_a returns self" do + a = [1, 2, 3] + a.to_a.should == [1, 2, 3] + a.equal?(a.to_a).should == true + end + + skip "to_a on array subclasses shouldn't return subclass instance" do + e = MyArray.new + e << 1 + e.to_a.class.should == Array + e.to_a.should == [1] + end + + it "to_ary returns self" do + a = [1, 2, 3] + a.equal?(a.to_ary).should == true + #a = MyArray[1, 2, 3] + #a.equal?(a.to_ary).should == true + end +end + +describe "Array#nitems" do + it "counts the number of non-nil items" do + a = [nil,1,2,nil,4,5,nil,7,8,nil] + a.length.should == 10 + a.nitems.should == 6 + + b = [] + b.nitems.should == 0 + + c = [nil] + c.nitems.should == 0 + c.length.should == 1 + end +end + +describe "Array#length and Array#size" do + it "retrieves the length/size of empty and non-empty arrays" do + a = [] + a.length.should == 0 + a.size.should == 0 + + b = [1,2,3] + b.length.should == 3 + b.size.should == 3 + end +end + +describe "Array#==(other)" do + it "compares two arrays" do + [].should == [] + [1,2].should == [1,2] + [1,2].should_not == [3,4] + [].should_not == [1] + end +end + +describe "Array#[idx]" do + it "reads elements in array using integer indices" do + a = [1,2,3] + a[0].should == 1 + a[1].should == 2 + a[2].should == 3 + a[-1].should == 3 + a[-2].should == 2 + a[-3].should == 1 + a[-4].should == nil + a[4].should == nil + end + + it "reads elements in array using offset, length parameters" do + a = [1,2,3,4] + a[0,2].should == [1,2] + end + + it "uses slice method to read elements in array (just an alias for [])" do + a = [1,2,3] + a.slice(0).should == 1 + a.slice(0,2).should == [1,2] + a.slice(-1).should == 3 + a.slice(-3).should == 1 + end + + it "returns element at index via slice" do + a = [1, 2, 3, 4] + + a.slice(0).should == 1 + a.slice(1).should == 2 + a.slice(2).should == 3 + a.slice(3).should == 4 + a.slice(4).should == nil + a.slice(10).should == nil + + a.slice(-1).should == 4 + a.slice(-2).should == 3 + a.slice(-3).should == 2 + a.slice(-4).should == 1 + a.slice(-5).should == nil + a.slice(-10).should == nil + + a == [1, 2, 3, 4] + end + + it "returns elements beginning at start when using slice with start, length" do + a = [1, 2, 3, 4] + + a.slice(0, 0).should == [] + a.slice(0, 1).should == [1] + a.slice(0, 2).should == [1, 2] + a.slice(0, 4).should == [1, 2, 3, 4] + a.slice(0, 6).should == [1, 2, 3, 4] + + a.slice(2, 0).should == [] + a.slice(2, 1).should == [3] + a.slice(2, 2).should == [3, 4] + a.slice(2, 4).should == [3, 4] + a.slice(2, -1).should == nil + + a.slice(4, 0).should == [] + a.slice(4, 2).should == [] + a.slice(4, -1).should == nil + + a.slice(5, 0).should == nil + a.slice(5, 2).should == nil + a.slice(5, -1).should == nil + + a.slice(6, 0).should == nil + a.slice(6, 2).should == nil + a.slice(6, -1).should == nil + + a.slice(-1, 0).should == [] + a.slice(-1, 1).should == [4] + a.slice(-1, 2).should == [4] + a.slice(-1, -1).should == nil + + a.slice(-2, 0).should == [] + a.slice(-2, 1).should == [3] + a.slice(-2, 2).should == [3, 4] + a.slice(-2, 4).should == [3, 4] + a.slice(-2, -1).should == nil + + a.slice(-4, 0).should == [] + a.slice(-4, 1).should == [1] + a.slice(-4, 2).should == [1, 2] + a.slice(-4, 4).should == [1, 2, 3, 4] + a.slice(-4, 6).should == [1, 2, 3, 4] + a.slice(-4, -1).should == nil + + a.slice(-5, 0).should == nil + a.slice(-5, 1).should == nil + a.slice(-5, 10).should == nil + a.slice(-5, -1).should == nil + + a.should == [1, 2, 3, 4] + end + + it "should return elements from array using slice and ranges" do + a = [1, 2, 3, 4] + + a.slice(0..-10).should == [] + a.slice(0...-10).should == [] + a.slice(0..0).should == [1] + a.slice(0...0).should == [] + a.slice(0..1).should == [1, 2] + a.slice(0...1).should == [1] + a.slice(0..2).should == [1, 2, 3] + a.slice(0...2).should == [1, 2] + a.slice(0..3).should == [1, 2, 3, 4] + a.slice(0...3).should == [1, 2, 3] + a.slice(0..4).should == [1, 2, 3, 4] + a.slice(0...4).should == [1, 2, 3, 4] + a.slice(0..10).should == [1, 2, 3, 4] + a.slice(0...10).should == [1, 2, 3, 4] + + a.slice(2..-10).should == [] + a.slice(2...-10).should == [] + a.slice(2..0).should == [] + a.slice(2...0).should == [] + a.slice(2..2).should == [3] + a.slice(2...2).should == [] + a.slice(2..3).should == [3, 4] + a.slice(2...3).should == [3] + a.slice(2..4).should == [3, 4] + a.slice(2...4).should == [3, 4] + + a.slice(3..0).should == [] + a.slice(3...0).should == [] + a.slice(3..3).should == [4] + a.slice(3...3).should == [] + a.slice(3..4).should == [4] + a.slice(3...4).should == [4] + + a.slice(4..0).should == [] + a.slice(4...0).should == [] + a.slice(4..4).should == [] + a.slice(4...4).should == [] + a.slice(4..5).should == [] + a.slice(4...5).should == [] + + a.slice(5..0).should == nil + a.slice(5...0).should == nil + a.slice(5..5).should == nil + a.slice(5...5).should == nil + a.slice(5..6).should == nil + a.slice(5...6).should == nil + + a.slice(-1..-1).should == [4] + a.slice(-1...-1).should == [] + a.slice(-1..3).should == [4] + a.slice(-1...3).should == [] + a.slice(-1..4).should == [4] + a.slice(-1...4).should == [4] + a.slice(-1..10).should == [4] + a.slice(-1...10).should == [4] + a.slice(-1..0).should == [] + a.slice(-1..-4).should == [] + a.slice(-1...-4).should == [] + a.slice(-1..-6).should == [] + a.slice(-1...-6).should == [] + + a.slice(-2..-2).should == [3] + a.slice(-2...-2).should == [] + a.slice(-2..-1).should == [3, 4] + a.slice(-2...-1).should == [3] + a.slice(-2..10).should == [3, 4] + a.slice(-2...10).should == [3, 4] + + a.slice(-4..-4).should == [1] + a.slice(-4..-2).should == [1, 2, 3] + a.slice(-4...-2).should == [1, 2] + a.slice(-4..-1).should == [1, 2, 3, 4] + a.slice(-4...-1).should == [1, 2, 3] + a.slice(-4..3).should == [1, 2, 3, 4] + a.slice(-4...3).should == [1, 2, 3] + a.slice(-4..4).should == [1, 2, 3, 4] + a.slice(-4...4).should == [1, 2, 3, 4] + a.slice(-4...4).should == [1, 2, 3, 4] + a.slice(-4..0).should == [1] + a.slice(-4...0).should == [] + a.slice(-4..1).should == [1, 2] + a.slice(-4...1).should == [1] + + a.slice(-5..-5).should == nil + a.slice(-5...-5).should == nil + a.slice(-5..-4).should == nil + a.slice(-5..-1).should == nil + a.slice(-5..10).should == nil + + a.should == [1,2,3,4] + end + + it "should not expand array when slice called with indices outside of array" do + a = [1, 2] + a.slice(4).should == nil + a.should == [1, 2] + a.slice(4, 0).should == nil + a.should == [1, 2] + a.slice(6, 1).should == nil + a.should == [1, 2] + a.slice(8...8).should == nil + a.should == [1, 2] + a.slice(10..10).should == nil + a.should == [1, 2] + end + + it "(at) should return the element at index" do + a = [1, 2, 3, 4, 5, 6] + a.at(0).should == 1 + a.at(-2).should == 5 + a.at(10).should == nil + end + + skip "(at) should call to_int on its argument" do + a = ["a", "b", "c"] + a.at(0.5).should == "a" + + obj = Object.new + obj.should_receive(:to_int, :returning => 2) + a.at(obj).should == "c" + end + + it "(first) should return the first element" do + ['a', 'b', 'c'].first.should == 'a' + [nil].first.should == nil + end + + it "(first) should return nil if self is empty" do + [].first.should == nil + end + + it "(first) with count should return the first count elements" do + [true, false, true, nil, false].first(2).should == [true, false] + end + + it "(first) with count == 0 should return an empty array" do + [1, 2, 3, 4, 5].first(0).should == [] + end + + it "(first) with count == 1 should return an array containing the first element" do + [1, 2, 3, 4, 5].first(1).should == [1] + end + + it "(first) should raise ArgumentError when count is negative" do + should_raise(ArgumentError) { [1, 2].first(-1) } + end + + it "(first) should return the entire array when count > length" do + [1, 2, 3, 4, 5, 9].first(10).should == [1, 2, 3, 4, 5, 9] + end + + skip "(first) should call to_int on count" do + obj = Object.new + def obj.to_int() 2 end + [1, 2, 3, 4, 5].first(obj).should == [1, 2] + end + + it "(last) should return last element" do + [1, 1, 1, 1, 2].last.should == 2 + end + + it "(last) returns nil if self is empty" do + [].last.should == nil + end + + it "(last) returns the last count elements" do + [1, 2, 3, 4, 5, 9].last(3).should == [4, 5, 9] + end + + it "(last) returns an empty array when count == 0" do + [1, 2, 3, 4, 5].last(0).should == [] + end + + it "(last) raises ArgumentError when count is negative" do + should_raise(ArgumentError) { [1, 2].last(-1) } + end + + it "(last) returns the entire array when count > length" do + [1, 2, 3, 4, 5, 9].last(10).should == [1, 2, 3, 4, 5, 9] + end + + it "(values_at) with indices should return an array of elements at the indexes" do + [1, 2, 3, 4, 5].values_at().should == [] + [1, 2, 3, 4, 5].values_at(1, 0, 5, -1, -8, 10).should == [2, 1, nil, 5, nil, nil] + end + + skip "values_at should call to_int on its indices" do + obj = Object.new + def obj.to_int() 1 end + [1, 2].values_at(obj, obj, obj).should == [2, 2, 2] + end + + skip "(values_at) with ranges should return an array of elements in the ranges" do + # MRI (i think this is a bug) + #[1, 2, 3, 4, 5].values_at(0..2, 1...3, 4..6).should == [1, 2, 3, 2, 3, 5, nil] + # IronRuby + [1, 2, 3, 4, 5].values_at(0..2, 1...3, 4..6).should == [1, 2, 3, 2, 3, 5] + [1, 2, 3, 4, 5].values_at(6..4).should == [] + end + + skip "values_at with ranges should call to_int on arguments of ranges" do + from = Object.new + to = Object.new + + # So we can construct a range out of them... + def from.<=>(o) 0 end + def to.<=>(o) 0 end + + def from.to_int() 1 end + def to.to_int() -2 end + + ary = [1, 2, 3, 4, 5] + ary.values_at(from .. to, from ... to, to .. from).should == [2, 3, 4, 2, 3] + end + + skip "values_at on array subclasses shouldn't return subclass instance" do + MyArray[1, 2, 3].values_at(0, 1..2, 1).class.should == Array + end +end + +describe "generating arrays" do + skip "& should create an array with elements common to both arrays (intersection)" do + ([] & []).should == [] + ([1, 2] & []).should == [] + ([] & [1, 2]).should == [] + ([ 1, 1, 3, 5 ] & [ 1, 2, 3 ]).should == [1, 3] + end + + skip "& should create an array with no duplicates" do + ([] | []).should == [] + ([1, 2] | []).should == [1, 2] + ([] | [1, 2]).should == [1, 2] + ([ 1, 1, 3, 5 ] & [ 1, 2, 3 ]).uniq!.should == nil + end + + skip "& should call to_ary on its argument" do + obj = Object.new + def obj.to_ary() [1, 2, 3] end + ([1, 2] & obj).should == ([1, 2] & obj.to_ary) + end + + # MRI doesn't actually call eql?() however. So you can't reimplement it. + skip "& should act as if using eql?" do + ([5.0, 4.0] & [5, 4]).should == [] + str = "x" + ([str] & [str.dup]).should == [str] + end + + skip "& with array subclasses shouldn't return subclass instance" do + (MyArray[1, 2, 3] & []).class.should == Array + (MyArray[1, 2, 3] & MyArray[1, 2, 3]).class.should == Array + ([] & MyArray[1, 2, 3]).class.should == Array + end + + it "| should return an array of elements that appear in either array (union) without duplicates" do + ([1, 2, 3] | [1, 2, 3, 4, 5]).should == [1, 2, 3, 4, 5] + end + + skip "| should call to_ary on its argument" do + obj = Object.new + def obj.to_ary() [1, 2, 3] end + ([0] | obj).should == ([0] | obj.to_ary) + end + + # MRI doesn't actually call eql?() however. So you can't reimplement it. + skip "| should act as if using eql?" do + ([5.0, 4.0] | [5, 4]).should == [5.0, 4.0, 5, 4] + str = "x" + ([str] | [str.dup]).should == [str] + end + + skip "| with array subclasses shouldn't return subclass instance" do + (MyArray[1, 2, 3] | []).class.should == Array + (MyArray[1, 2, 3] | MyArray[1, 2, 3]).class.should == Array + ([] | MyArray[1, 2, 3]).class.should == Array + end + + it "appends elements to an array using << method" do + a = [] + (a << 1).should == [1] + (a << 2).should == [1,2] + (a << 3).should == [1,2,3] + a.length.should == 3 + a << 4 << 5 << 6 + a.length.should == 6 + a.should == [1,2,3,4,5,6] + end + + it "<< should push the object onto the end of the array" do + ([ 1, 2 ] << "c" << "d" << [ 3, 4 ]).should == [1, 2, "c", "d", [3, 4]] + end +end + +describe "Array#*(count)" do + it "generates count copies of an array" do + a = [1,2,3] * 3 + a.length.should == 9 + a.should == [1,2,3,1,2,3,1,2,3] + end + + it "(*) should concatenate n copies of the array" do + ([ 1, 2, 3 ] * 0).should == [] + ([ 1, 2, 3 ] * 3).should == [1, 2, 3, 1, 2, 3, 1, 2, 3] + ([] * 10).should == [] + end + + it "* with a negative int should raise an ArgumentError" do + should_raise(ArgumentError) { [ 1, 2, 3 ] * -1 } + should_raise(ArgumentError) { [] * -1 } + end + + it "* should call to_int on its argument" do + class Bob301 + def to_int + 2 + end + end + obj = Bob301.new + #def obj.to_int() 2 end + ([1, 2, 3] * obj).should == [1, 2, 3] * obj.to_int + end + + skip "* on array subclass should return subclass instance" do + (MyArray[1, 2, 3] * 0).class.should == MyArray + (MyArray[1, 2, 3] * 1).class.should == MyArray + (MyArray[1, 2, 3] * 2).class.should == MyArray + end +end + +describe "Array#*(string)" do + it "should be equivalent to self.join(str)" do + ([ 1, 2, 3 ] * ",").should == [1, 2, 3].join(",") + end + + it "should call to_str on its argument" do + class Bob300 + def to_str + "x" + end + end + obj = Bob300.new + ([ 1, 2, 3 ] * obj).should == "1x2x3" + end + + it "should call to_str on its argument before to_int" do + class Bob301 + def to_int + 2 + end + def to_str + "x" + end + end + obj = Bob301.new + ([1, 2, 3] * obj).should == [1, 2, 3] * obj.to_str + end +end + +describe "Array#+(array)" do + it "(+) should concatenate arrays" do + a = [1,2,3] + b = [4,5,6] + c = a + b + c.length.should == 6 + end + + it "(+) should concatenate two arrays" do + ([ 1, 2, 3 ] + [ 3, 4, 5 ]).should == [1, 2, 3, 3, 4, 5] + ([ 1, 2, 3 ] + []).should == [1, 2, 3] + ([] + [ 1, 2, 3 ]).should == [1, 2, 3] + ([] + []).should == [] + end + + skip "(+) should call to_ary on its argument" do + obj = Object.new + def obj.to_ary() ["x", "y"] end + ([1, 2, 3] + obj).should == [1, 2, 3] + obj.to_ary + end +end + +describe "Array#compact" do + it "(compact) should return a copy of array with all nil elements removed" do + a = [1, nil, 2, nil, 4, nil] + a.compact.should == [1, 2, 4] + end + + skip "(compact) on array subclasses should return subclass instance" do + MyArray[1, 2, 3, nil].compact.class.should == MyArray + end +end + +describe "Array#flatten" do + it "flatten should return a one-dimensional flattening recursively" do + [[[1, [2, 3]],[2, 3, [4, [4, [5, 5]], [1, 2, 3]]], [4]], []].flatten.should == [1, 2, 3, 2, 3, 4, 4, 5, 5, 1, 2, 3, 4] + end + + skip "flatten shouldn't call flatten on elements" do + obj = Object.new + def obj.flatten() [1, 2] end + [obj, obj].flatten.should == [obj, obj] + + obj = [5, 4] + def obj.flatten() [1, 2] end + [obj, obj].flatten.should == [5, 4, 5, 4] + end + + it "flatten should complain about recursive arrays" do + x = [] + x << x + should_raise(ArgumentError) { x.flatten } + + x = [] + y = [] + x << y + y << x + should_raise(ArgumentError) { x.flatten } + end + + skip "flatten on array subclasses should return subclass instance" do + MyArray[].flatten.class.should == MyArray + MyArray[1, 2, 3].flatten.class.should == MyArray + MyArray[1, [2], 3].flatten.class.should == MyArray + end +end + +describe "Array#reverse" do + it "(reverse) creates a reversed array" do + a = [1,2,3] + b = a.reverse + b.should == [3,2,1] + a.object_id.should_not == b.object_id + end +end + +describe "Array#sort" do + skip "(sort) should return a new array from sorting elements using <=> on the pivot" do + # TODO: entering try with non-empty stack bug + #[1, 1, 5, -5, 2, -10, 14, 6].sort.should == [-10, -5, 1, 1, 2, 5, 6, 14] + #%w{z y x a e b d}.sort.should == ['a', 'b', 'd', 'e', 'x', 'y', 'z'] + end + + skip "(sort) raises an ArgumentError if the comparison cannot be completed" do + # TODO: entering try with non-empty stack bug + # d = D.new + + # Fails essentially because of d.<=>(e) whereas d.<=>(1) would work + # should_raise(ArgumentError) { [1, d].sort.should == [1, d] } + end + + skip "sort may take a block which is used to determine the order of objects a and b described as -1, 0 or +1" do + # TODO: entering try with non-empty stack bug + #a = [5, 1, 4, 3, 2] + #a.sort.should == [1, 2, 3, 4, 5] + #a.sort {|x, y| y <=> x}.should == [5, 4, 3, 2, 1] + end + + skip "sort on array subclasses should return subclass instance" do + # TODO: entering try with non-empty stack bug + #ary = MyArray[1, 2, 3] + #ary.sort.class.should == MyArray + end +end + +describe "Array#uniq" do + it "uniq should return an array with no duplicates" do + ["a", "a", "b", "b", "c"].uniq.should == ["a", "b", "c"] + [1.0, 1].uniq.should == [1.0, 1] + end + + skip "uniq on array subclasses should return subclass instance" do + MyArray[1, 2, 3].uniq.class.should == MyArray + end +end + +describe "Array#uniq!" do + it "uniq! modifies the array in place" do + a = [ "a", "a", "b", "b", "c" ] + a.uniq! + a.should == ["a", "b", "c"] + end + + it "uniq! should return self" do + a = [ "a", "a", "b", "b", "c" ] + a.equal?(a.uniq!).should == true + end + + it "uniq! should return nil if no changes are made to the array" do + [ "a", "b", "c" ].uniq!.should == nil + end +end + +describe "converting arrays" do + it "converts an array to string" do + [1,2,3].to_s.should == "123" + ['hello','world'].to_s.should == 'helloworld' + end + + it "converts an array containing nil elements to a string" do + #[1,2,nil,4,5].to_s.should == "12nil45" + [1,2,nil,4,5].inspect.should == "[1, 2, nil, 4, 5]" + end + + it "converts an array to a string using a separator between elements" do + ([1,2,3] * ',').should == '1,2,3' + ([1,2,3] * '--').should == '1--2--3' + ([1,2,3] * '').should == '123' + end + + skip "(join) should return a string formed by concatenating each element.to_s separated by separator without trailing separator" do + obj = Object.new + def obj.to_s() 'foo' end + + [1, 2, 3, 4, obj].join(' | ').should == '1 | 2 | 3 | 4 | foo' + end + + it "join's separator defaults to $, (which defaults to empty)" do + [1, 2, 3].join.should == '123' + old, $, = $,, '-' + [1, 2, 3].join.should == '1-2-3' + $, = old + end + + skip "join should call to_str on its separator argument" do + obj = Object.new + def obj.to_str() '::' end + [1, 2, 3, 4].join(obj).should == '1::2::3::4' + end + + it "(to_s) is equivalent to #joining without a separator string" do + a = [1, 2, 3, 4] + a.to_s.should == a.join + $, = '-' + a.to_s.should == a.join + $, = '' + end +end + +describe "modifying arrays" do + it "modifies elements in array using []=" do + a = [1,2,3] + a[0] = 42 + a[-1] = 1000 + a.first.should == 42 + a.last.should == 1000 + a[-3] = 10 + a.first.should == 10 + end + + it "should modify single elements / optionally expand array when []= called with index" do + a = [] + a[4] = "e" + a.should == [nil, nil, nil, nil, "e"] + a[3] = "d" + a.should == [nil, nil, nil, "d", "e"] + a[0] = "a" + a.should == ["a", nil, nil, "d", "e"] + a[-3] = "C" + a.should == ["a", nil, "C", "d", "e"] + a[-1] = "E" + a.should == ["a", nil, "C", "d", "E"] + a[-5] = "A" + a.should == ["A", nil, "C", "d", "E"] + a[5] = "f" + a.should == ["A", nil, "C", "d", "E", "f"] + a[1] = [] + a.should == ["A", [], "C", "d", "E", "f"] + a[-1] = nil + a.should == ["A", [], "C", "d", "E", nil] + end + + it "should raise if []= called with start and negative length" do + a = [1, 2, 3, 4] + should_raise(IndexError) { a[-2, -1] = "" } + should_raise(IndexError) { a[0, -1] = "" } + should_raise(IndexError) { a[2, -1] = "" } + should_raise(IndexError) { a[4, -1] = "" } + should_raise(IndexError) { a[10, -1] = "" } + end + + # TODO: bust this baby up into multiple it + it "[]= with start, length should set elements" do + a = []; a[0, 0] = nil; a.should == [] + a = []; a[2, 0] = nil; a.should == [nil, nil] + a = []; a[0, 2] = nil; a.should == [] + a = []; a[2, 2] = nil; a.should == [nil, nil] + + a = []; a[0, 0] = []; a.should == [] + a = []; a[2, 0] = []; a.should == [nil, nil] + a = []; a[0, 2] = []; a.should == [] + a = []; a[2, 2] = []; a.should == [nil, nil] + + a = []; a[0, 0] = ["a"]; a.should == ["a"] + a = []; a[2, 0] = ["a"]; a.should == [nil, nil, "a"] + a = []; a[0, 2] = ["a","b"]; a.should == ["a", "b"] + a = []; a[2, 2] = ["a","b"]; a.should == [nil, nil, "a", "b"] + + a = []; a[0, 0] = ["a","b","c"]; a.should == ["a", "b", "c"] + a = []; a[2, 0] = ["a","b","c"]; a.should == [nil, nil, "a", "b", "c"] + a = []; a[0, 2] = ["a","b","c"]; a.should == ["a", "b", "c"] + a = []; a[2, 2] = ["a","b","c"]; a.should == [nil, nil, "a", "b", "c"] + + a = [1, 2, 3, 4] + a[0, 0] = []; a.should == [1, 2, 3, 4] + a[1, 0] = []; a.should == [1, 2, 3, 4] + a[-1,0] = []; a.should == [1, 2, 3, 4] + + a = [1, 2, 3, 4] + a[0, 0] = [8, 9, 9]; a.should == [8, 9, 9, 1, 2, 3, 4] + a = [1, 2, 3, 4] + a[1, 0] = [8, 9, 9]; a.should == [1, 8, 9, 9, 2, 3, 4] + a = [1, 2, 3, 4] + a[-1,0] = [8, 9, 9]; a.should == [1, 2, 3, 8, 9, 9, 4] + a = [1, 2, 3, 4] + a[4, 0] = [8, 9, 9]; a.should == [1, 2, 3, 4, 8, 9, 9] + + a = [1, 2, 3, 4] + a[0, 1] = [9]; a.should == [9, 2, 3, 4] + a[1, 1] = [8]; a.should == [9, 8, 3, 4] + a[-1,1] = [7]; a.should == [9, 8, 3, 7] + a[4, 1] = [9]; a.should == [9, 8, 3, 7, 9] + + a = [1, 2, 3, 4] + a[0, 1] = [8, 9]; a.should == [8, 9, 2, 3, 4] + a = [1, 2, 3, 4] + a[1, 1] = [8, 9]; a.should == [1, 8, 9, 3, 4] + a = [1, 2, 3, 4] + a[-1,1] = [8, 9]; a.should == [1, 2, 3, 8, 9] + a = [1, 2, 3, 4] + a[4, 1] = [8, 9]; a.should == [1, 2, 3, 4, 8, 9] + + a = [1, 2, 3, 4] + a[0, 2] = [8, 9]; a.should == [8, 9, 3, 4] + a = [1, 2, 3, 4] + a[1, 2] = [8, 9]; a.should == [1, 8, 9, 4] + a = [1, 2, 3, 4] + a[-2,2] = [8, 9]; a.should == [1, 2, 8, 9] + a = [1, 2, 3, 4] + a[-1,2] = [8, 9]; a.should == [1, 2, 3, 8, 9] + a = [1, 2, 3, 4] + a[4, 2] = [8, 9]; a.should == [1, 2, 3, 4, 8, 9] + + a = [1, 2, 3, 4] + a[0, 2] = [7, 8, 9]; a.should == [7, 8, 9, 3, 4] + a = [1, 2, 3, 4] + a[1, 2] = [7, 8, 9]; a.should == [1, 7, 8, 9, 4] + a = [1, 2, 3, 4] + a[-2,2] = [7, 8, 9]; a.should == [1, 2, 7, 8, 9] + a = [1, 2, 3, 4] + a[-1,2] = [7, 8, 9]; a.should == [1, 2, 3, 7, 8, 9] + a = [1, 2, 3, 4] + a[4, 2] = [7, 8, 9]; a.should == [1, 2, 3, 4, 7, 8, 9] + end + + it "should assign multiple array elements with the [start, length]= syntax" do + a = [1, 2, 3, 4] + a[0, 2] = [1, 1.25, 1.5, 1.75, 2] + a.should == [1, 1.25, 1.5, 1.75, 2, 3, 4] + a[1, 1] = a[3, 1] = [] + a.should == [1, 1.5, 2, 3, 4] + a[0, 2] = [1] + a.should == [1, 2, 3, 4] + a[5, 0] = [4, 3, 2, 1] + a.should == [1, 2, 3, 4, nil, 4, 3, 2, 1] + a[-2, 5] = nil + a.should == [1, 2, 3, 4, nil, 4, 3] + a[-2, 5] = [] + a.should == [1, 2, 3, 4, nil] + a[0, 2] = nil + a.should == [3, 4, nil] + a[0, 100] = [1, 2, 3] + a.should == [1, 2, 3] + a[0, 2] *= 2 + a.should == [1, 2, 1, 2, 3] + end + + it "[]= with negative index beyond array should raise" do + a = [1, 2, 3, 4] + should_raise(IndexError) { a[-5] = "" } + should_raise(IndexError) { a[-5, -1] = "" } + should_raise(IndexError) { a[-5, 0] = "" } + should_raise(IndexError) { a[-5, 1] = "" } + should_raise(IndexError) { a[-5, 2] = "" } + should_raise(IndexError) { a[-5, 10] = "" } + + should_raise(RangeError) { a[-5..-5] = "" } + should_raise(RangeError) { a[-5...-5] = "" } + should_raise(RangeError) { a[-5..-4] = "" } + should_raise(RangeError) { a[-5...-4] = "" } + should_raise(RangeError) { a[-5..10] = "" } + should_raise(RangeError) { a[-5...10] = "" } + + # ok + a[0..-9] = [1] + a.should == [1, 1, 2, 3, 4] + end + + it "(compact!) should remove all nil elements" do + a = ['a', nil, 'b', nil, nil, 'c'] + a.compact!.equal?(a).should == true + a.should == ["a", "b", "c"] + end + + it "(compact!) should return nil if there are no nil elements to remove" do + [1, 2, 3].compact!.should == nil + end + + it "concat should append the elements in the other array" do + ary = [1, 2, 3] + ary.concat([9, 10, 11]).equal?(ary).should == true + ary.should == [1, 2, 3, 9, 10, 11] + ary.concat([]) + ary.should == [1, 2, 3, 9, 10, 11] + end + + it "concat shouldn't loop endlessly when argument is self" do + ary = ["x", "y"] + ary.concat(ary).should == ["x", "y", "x", "y"] + end + + it "concat should call to_ary on its argument" do + class Bob104 + def to_ary + ["x", "y"] + end + end + obj = Bob104.new + #def obj.to_ary() ["x", "y"] end + [4, 5, 6].concat(obj).should == [4, 5, 6, "x", "y"] + end + + skip "(delete) removes elements that are #== to object" do + class Bob201 + def ==(other) + 3 == other + end + end + x = Bob201.new + #def x.==(other) 3 == other end + + a = [1, 2, 3, x, 4, 3, 5, x] + a.delete Object.new + a.should == [1, 2, 3, x, 4, 3, 5, x] + + a.delete 3 + a.should == [1, 2, 4, 5] + end + + it "(delete) should return object or nil if no elements match object" do + [1, 2, 4, 5].delete(1).should == 1 + [1, 2, 4, 5].delete(3).should == nil + a = %w{a b b b c d} + a.delete('b').should == 'b' + a.should == ['a', 'c', 'd'] + end + + # TODO: entering try with non-empty stack bug + skip '(delete) may be given a block that is executed if no element matches object' do + # [].delete('a') {:not_found}.should == :not_found + end + + it '(delete) may be given a block that is executed if no element matches object' do + r = [].delete('a') { :not_found } + r.should == :not_found + end +end + +describe "Array#delete_at" do + it "(delete_at) should remove the element at the specified index" do + a = [1, 2, 3, 4] + a.delete_at(2) + a.should == [1, 2, 4] + a.delete_at(-1) + a.should == [1, 2] + end + + it "(delete_at) should return the removed element at the specified index" do + a = [1, 2, 3, 4] + a.delete_at(2).should == 3 + a.delete_at(-1).should == 4 + end + + it "(delete_at) should return nil if the index is out of range" do + a = [1, 2] + a.delete_at(3).should == nil + end + + it "(delete_at) should call to_int on its argument" do + class Bob200 + def to_int + -1 + end + end + obj = Bob200.new + [1, 2].delete_at(obj).should == 2 + end +end + +describe "Array#flatten!" do + it "(flatten!) should modify array to produce a one-dimensional flattening recursively" do + a = [[[1, [2, 3]],[2, 3, [4, [4, [5, 5]], [1, 2, 3]]], [4]], []] + a.flatten!.equal?(a).should == true + a.should == [1, 2, 3, 2, 3, 4, 4, 5, 5, 1, 2, 3, 4] + end + + it "(flatten!) should return nil if no modifications took place" do + a = [1, 2, 3] + a.flatten!.should == nil + end + + it "(flatten!) should complain about recursive arrays" do + x = [] + x << x + should_raise(ArgumentError) { x.flatten! } + + x = [] + y = [] + x << y + y << x + should_raise(ArgumentError) { x.flatten! } + end +end + +describe "Array#insert" do + it "(insert) with non-negative index should insert objects before the element at index" do + ary = [] + ary.insert(0, 3).equal?(ary).should == true + ary.should == [3] + + ary.insert(0, 1, 2).equal?(ary).should == true + ary.should == [1, 2, 3] + ary.insert(0) + ary.should == [1, 2, 3] + + # Let's just assume insert() always modifies the array from now on. + ary.insert(1, 'a').should == [1, 'a', 2, 3] + ary.insert(0, 'b').should == ['b', 1, 'a', 2, 3] + ary.insert(5, 'c').should == ['b', 1, 'a', 2, 3, 'c'] + ary.insert(7, 'd').should == ['b', 1, 'a', 2, 3, 'c', nil, 'd'] + ary.insert(10, 5, 4).should == ['b', 1, 'a', 2, 3, 'c', nil, 'd', nil, nil, 5, 4] + end + + it "(insert) with index -1 should append objects to the end" do + [1, 3, 3].insert(-1, 2, 'x', 0.5).should == [1, 3, 3, 2, 'x', 0.5] + end + + it "(insert) with negative index should insert objects after the element at index" do + ary = [] + ary.insert(-1, 3).should == [3] + ary.insert(-2, 2).should == [2, 3] + ary.insert(-3, 1).should == [1, 2, 3] + ary.insert(-2, -3).should == [1, 2, -3, 3] + ary.insert(-1, []).should == [1, 2, -3, 3, []] + ary.insert(-2, 'x', 'y').should == [1, 2, -3, 3, 'x', 'y', []] + ary = [1, 2, 3] + end + + it "(insert) with negative index beyond array should raise" do + should_raise(IndexError) { [].insert(-2, 1) } + should_raise(IndexError) { [1].insert(-3, 2) } + end + + it "(insert) without objects should do nothing" do + [].insert(0).should == [] + [].insert(-1).should == [] + [].insert(10).should == [] + [].insert(-2).should == [] + end + + skip "(insert) should call to_int on position argument" do + obj = Object.new + def obj.to_int() 2 end + [].insert(obj, 'x').should == [nil, nil, 'x'] + end +end + +describe "Array#clear" do + it "(clear) removes all elements from an array" do + a = [1,2,3] + a.clear.equal?(a).should == true + a.length.should == 0 + end +end + +describe "Array#collect!" do + it "modifies elements in an array using collect!" do + a = [1,2,3] + a.collect! { |x| x + 1 } + a.should == [2,3,4] + end +end + +describe "Array#replace" do + it "(replace) should replace the elements with elements from other array" do + a = [1, 2, 3, 4, 5] + b = ['a', 'b', 'c'] + a.replace(b).equal?(a).should == true + a.should == b + a.equal?(b).should == false + + a.replace([4] * 10) + a.should == [4] * 10 + + a.replace([]) + a.should == [] + end + + # TODO: skip until site caching bug fixed + skip "(replace) should call to_ary on its argument" do + class Bob105 + def to_ary + [1, 2, 3] + end + end + obj = Bob105.new + + ary = [] + ary.replace(obj) + ary.should == [1, 2, 3] + end +end + +describe "Array#replace!" do + it "(reverse!) will reverse an array in-place" do + a = [1,2,3] + b = a.reverse! + b.should == [3,2,1] + a.object_id.should == b.object_id + end +end + +describe "Array#sort!" do + it "(sort!) should sort array in place using <=>" do + a = [1, 9, 7, 11, -1, -4] + a.sort! + a.should == [-4, -1, 1, 7, 9, 11] + end + + skip "sort! should sort array in place using block value" do + a = [1, 3, 2, 5, 4] + a.sort! { |x, y| y <=> x } + a.should == [5, 4, 3, 2, 1] + end +end + +describe "Array#pop" do + it "(pop) should remove and return the last element of the array" do + a = ["a", 1, nil, true] + + a.pop.should == true + a.should == ["a", 1, nil] + + a.pop.should == nil + a.should == ["a", 1] + + a.pop.should == 1 + a.should == ["a"] + + a.pop.should == "a" + a.should == [] + end + + it "(pop) should return nil if there are no more elements" do + [].pop.should == nil + end +end + +describe "Array#push" do + it "(push) should append the arguments to the array" do + a = [ "a", "b", "c" ] + a.push("d", "e", "f").equal?(a).should == true + a.push().should == ["a", "b", "c", "d", "e", "f"] + a.push(5) + a.should == ["a", "b", "c", "d", "e", "f", 5] + end + + it "(push) should append elements onto an array" do + a = [1,2,3] + a.push 4 + a.should == [1,2,3,4] + a.push 5,6 + a.should == [1,2,3,4,5,6] + a.push + a.should == [1,2,3,4,5,6] + end +end + +describe "Array#shift" do + it "(shift) should remove and return the first element" do + a = [5, 1, 1, 5, 4] + a.shift.should == 5 + a.should == [1, 1, 5, 4] + a.shift.should == 1 + a.should == [1, 5, 4] + a.shift.should == 1 + a.should == [5, 4] + a.shift.should == 5 + a.should == [4] + a.shift.should == 4 + a.should == [] + end + + it "(shift) should return nil when the array is empty" do + [].shift.should == nil + end +end + +describe "Array#slice!" do + it "(slice!) with index should remove and return the element at index" do + a = [1, 2, 3, 4] + a.slice!(10).should == nil + a.should == [1, 2, 3, 4] + a.slice!(-10).should == nil + a.should == [1, 2, 3, 4] + a.slice!(2).should == 3 + a.should == [1, 2, 4] + a.slice!(-1).should == 4 + a.should == [1, 2] + a.slice!(1).should == 2 + a.should == [1] + a.slice!(-1).should == 1 + a.should == [] + a.slice!(-1).should == nil + a.should == [] + a.slice!(0).should == nil + a.should == [] + end + + it "(slice!) with start, length should remove and return length elements beginning at start" do + a = [1, 2, 3, 4, 5, 6] + a.slice!(2, 3).should == [3, 4, 5] + a.should == [1, 2, 6] + a.slice!(1, 1).should == [2] + a.should == [1, 6] + a.slice!(1, 0).should == [] + a.should == [1, 6] + a.slice!(2, 0).should == [] + a.should == [1, 6] + a.slice!(0, 4).should == [1, 6] + a.should == [] + a.slice!(0, 4).should == [] + a.should == [] + end + + skip "(slice!) should call to_int on start and length arguments" do + obj = Object.new + def obj.to_int() 2 end + + a = [1, 2, 3, 4, 5] + a.slice!(obj).should == 3 + a.should == [1, 2, 4, 5] + a.slice!(obj, obj).should == [4, 5] + a.should == [1, 2] + a.slice!(0, obj).should == [1, 2] + a.should == [] + end + + it "(slice!) with range should remove and return elements in range" do + a = [1, 2, 3, 4, 5, 6, 7, 8] + a.slice!(1..4).should == [2, 3, 4, 5] + a.should == [1, 6, 7, 8] + a.slice!(1...3).should == [6, 7] + a.should == [1, 8] + a.slice!(-1..-1).should == [8] + a.should == [1] + a.slice!(0...0).should == [] + a.should == [1] + a.slice!(0..0).should == [1] + a.should == [] + end + + skip "(slice!) with range should call to_int on range arguments" do + from = Object.new + to = Object.new + + # So we can construct a range out of them... + def from.<=>(o) 0 end + def to.<=>(o) 0 end + + def from.to_int() 1 end + def to.to_int() -2 end + + a = [1, 2, 3, 4, 5] + + a.slice!(from .. to).should == [2, 3, 4] + a.should == [1, 5] + + a.slice!(1..0).should == [] + a.should == [1, 5] + + should_raise(TypeError) { a.slice!("a" .. "b") } + should_raise(TypeError) { a.slice!(from .. "b") } + end + + # TODO: MRI behaves inconsistently here. I'm trying to find out what it should + # do at ruby-core right now. -- flgr + # See http://groups.google.com/group/ruby-core-google/t/af70e3d0e9b82f39 + skip "(slice!) with indices outside of array should (not?) expand array" do + # This is the way MRI behaves -- subject to change + a = [1, 2] + a.slice!(4).should == nil + a.should == [1, 2] + a.slice!(4, 0).should == nil + a.should == [1, 2, nil, nil] + a.slice!(6, 1).should == nil + a.should == [1, 2, nil, nil, nil, nil] + a.slice!(8...8).should == nil + a.should == [1, 2, nil, nil, nil, nil, nil, nil] + a.slice!(10..10).should == nil + a.should == [1, 2, nil, nil, nil, nil, nil, nil, nil, nil] + end +end + +describe "Array#unshift" do + it "should prepend object to the original array" do + a = [1, 2, 3] + a.unshift("a").equal?(a).should == true + a.should == ['a', 1, 2, 3] + a.unshift().equal?(a).should == true + a.should == ['a', 1, 2, 3] + a.unshift(5, 4, 3) + a.should == [5, 4, 3, 'a', 1, 2, 3] + end +end + +it "Array#each" do + it "reads elements using each" do + a = [1,2,3] + b = [] + a.each { |x| b << x } + b.should == [1,2,3] + end + + it "reads no elements in an empty array" do + a, b = [], [] + a.each { |x| b << x } + b.should == [] + end + + it "each should yield each element to the block" do + a = [] + x = [1, 2, 3] + # BUG: This is the guilty line - chaining against the result of the call to + # each() causes the generation of an invalid program - assigning to a temp + # works fine. + #x.each { |item| a << item }.equal?(x).should == true + r = x.each { |item| a << item } + # BUG: this line generates a system error as well ... + #r.equal?(x).should == true + a.should == [1, 2, 3] + end +end + +it "Array#each_index" do + it "reads elements by index using each_index" do + a = [1,2,3] + b = [] + a.each_index { |index| b << index } + b.should == [0,1,2] + end + + it "reads no elements by index using each_index over an empty array" do + a, b = [], [] + c = a.each_index { |x| b << x } + b.should == [] + end + + it "each_index should pass the index of each element to the block" do + a = [] + x = ['a', 'b', 'c', 'd'] + #x.each_index { |i| a << i }.equal?(x).should == true + r = x.each_index { |i| a << i } + r.equal?(x).should == true + a.should == [0, 1, 2, 3] + end +end + +finished diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/abs_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/abs_spec.rb new file mode 100644 index 0000000000..2eef7e7372 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/abs_spec.rb @@ -0,0 +1,10 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#abs" do + it "returns the absolute value" do + (0x80000000).abs.should == 0x80000000 + (-0x80000000).abs.should == 0x80000000 + (0x987654321).abs.should == 0x987654321 + (-0x987654321).abs.should == 0x987654321 + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/add_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/add_spec.rb new file mode 100644 index 0000000000..c341cd9260 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/add_spec.rb @@ -0,0 +1,65 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +class X + def coerce(other) + [other, 2] + end +end + +describe "Bignum#+" do + it "returns self plus other, where other is Bignum" do + (0x80000000 + 0x80000000).should == 0x100000000 + (0x80000000 + (-0x80000000)).should == 0 + ((-0x80000000) + 0x80000000).should == 0 + ((-0x80000000) + (-0x80000000)).should == -0x100000000 + end + it "returns self plus other, where other is Fixnum" do + (0x80000000 + 5).should == 0x80000005 + (0x80000000 + (-5)).should == 0x7ffffffb + ((-0x80000000) + 5).should == -0x7ffffffb + ((-0x80000000) + (-5)).should == -0x80000005 + (0x80000000 + 1).should == 0x80000001 + (0x80000000 + 0).should == 0x80000000 + end + it "normalizes result to a Fixnum as necessary" do + (0x80000000 + 0).class.to_s.should == 'Bignum' + (0x80000000 + (-0x80000000)).class.to_s.should == 'Fixnum' + end + it "returns self plus other, where other is Float" do + (0x80000000 + 7.4).should_be_close(2147483655.4, TOLERANCE) + (0x80000000 + (-7.4)).should_be_close(2147483640.6, TOLERANCE) + ((-0x80000000) + 7.4).should_be_close(-2147483640.6, TOLERANCE) + ((-0x80000000) + (-7.4)).should_be_close(-2147483655.4, TOLERANCE) + (0x80000000 + 0.001).should_be_close(2147483648.001, TOLERANCE) + (0x80000000 + 0.0).should_be_close(2147483648.0, TOLERANCE) + (0x80000000 + 0.0).class.to_s.should == 'Float' + end + it "returns NaN if other is NaN" do + (0x80000000 + (0.0/0.0)).to_s.should == 'NaN' + end + it "returns -Infinity if other is -Infinity" do + (0x80000000 + (-1.0/0.0)).to_s.should == '-Infinity' + end + it "returns Infinity if other is Infinity" do + (0x80000000 + (1.0/0.0)).to_s.should == 'Infinity' + end + it "coerces on other if other is not Fixnum, Bignum or Float" do + (0x987654321 + X.new).should == 0x987654323 + (0x80000000 + X.new).should == 0x80000002 + end + it "dynamically invokes the + method if other is not a Fixnum, Bignum or Float" do + class Bignum + alias :old_add :+ + def +(other) + 33 + end + end + (0x987654321.old_add(X.new)).should == 33 + (0x80000000.old_add(X.new)).should == 33 + (0x80000000.old_add(0)).should == 0x80000000 + class Bignum + remove_method :+ + alias :+ :old_add + end + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/bit_and_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/bit_and_spec.rb new file mode 100644 index 0000000000..9a825649f7 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/bit_and_spec.rb @@ -0,0 +1,40 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#&" do + it "returns self bitwise-and other, where other is Fixnum" do + (0x80000010 & 3).should == 0 + (0x80000005 & 52).should == 4 + end + it "return self bitwise-and other, where other is Bignum" do + (0x80000005 & 0x80000005).should == 0x80000005 + end + it "returns self bitwise-and other, where other is float (downcast to int using Float.floor)" do + (0x80000005 & 3.4).should == 1 + end + it "returns self bitwise-and other, where self or other are negative" do + (0x80000001 & -1).should == 0x80000001 + (-0x80000001 & 1).should == 1 + (-0x80000001 & -1).should == -0x80000001 + end + it "normalizes values to Fixnum as necessary" do + (0x80000005 & 0x80000001).class.to_s.should == 'Bignum' + (0x80000005 & 5).class.to_s.should == 'Fixnum' + end + it "returns self bitwise-and other, calling Float.to_int dynamically" do + # Redefine Float#to_int + class Float + alias :old_to_int :to_int + def to_int + 55 + end + end + + (0x80000005 & 3.4).should == 5 + + # Clear the redefined to_int + class Float + alias :dead_to_int :to_int + alias :to_int :old_to_int + end + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/bit_or_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/bit_or_spec.rb new file mode 100644 index 0000000000..ea8230a15c --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/bit_or_spec.rb @@ -0,0 +1,40 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#|" do + it "returns self bitwise-or other, where other is Fixnum" do + (0x80000010 | 3).should == 0x80000013 + (0x80000005 | 52).should == 0x80000035 + end + it "return self bitwise-or other, where other is Bignum" do + (0x80000005 | 0x80000034).should == 0x80000035 + end + it "returns self bitwise-or other, where other is float" do + (0x80000005 | 3.4).should == 0x80000007 + end + it "returns self bitwise-or other, where self or other are negative" do + (0x80000001 | -1).should == -1 + (-0x80000001 | 1).should == -0x80000001 + (-0x80000001 | -1).should == -1 + end + it "returns self bitwise-or other, normalizing values to Fixnum as necessary" do + (0x80000005 | 0x80000001).class.to_s.should == 'Bignum' + (0x80000001 | -1).class.to_s.should == 'Fixnum' + end + it "returns self bitwise-or other, calling Float.to_int dynamically" do + # Redefine Float#to_int + class Float + alias :old_to_int :to_int + def to_int + 55 + end + end + + (0x80000005 | 3.4).should == 0x80000037 + + # Clear the redefined to_int + class Float + alias :dead_to_int :to_int + alias :to_int :old_to_int + end + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/bit_xor_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/bit_xor_spec.rb new file mode 100644 index 0000000000..0d30415935 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/bit_xor_spec.rb @@ -0,0 +1,40 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#^" do + it "returns self bitwise-xor other, where other is Fixnum" do + (0x80000010 ^ 3).should == 0x80000013 + (0x80000005 ^ 52).should == 0x80000031 + end + it "return self bitwise-xor other, where other is Bignum" do + (0x80000005 ^ 0x80000034).should == 0x31 + end + it "returns self bitwise-xor other, where other is float (downcast to int using Float.floor)" do + (0x80000005 ^ 3.4).should == 0x80000006 + end + it "returns self bitwise-xor other, where self or other are negative" do + (0x80000001 ^ -1).should == -0x80000002 + (-0x80000001 ^ 1).should == -0x80000002 + (-0x80000001 ^ -1).should == 0x80000000 + end + it "returns self bitwise-xor other, normalizing values to Fixnum as necessary" do + (0x80000005 ^ 52).class.to_s.should == 'Bignum' + (0x80000005 ^ 0x80000034).class.to_s.should == 'Fixnum' + end + it "returns self bitwise-xor other, calling Float.to_int dynamically" do + # Redefine Float#to_int + class Float + alias :old_to_int :to_int + def to_int + 55 + end + end + + (0x80000005 ^ 3.4).should == 0x80000032 + + # Clear the redefined to_int + class Float + alias :dead_to_int :to_int + alias :to_int :old_to_int + end + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/coerce_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/coerce_spec.rb new file mode 100644 index 0000000000..33eb03b08c --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/coerce_spec.rb @@ -0,0 +1,21 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#coerce" do + it "returns [other, self] both as Bignum if other is an Integer" do + (0x80000001).coerce(2).class.to_s.should == 'Array' + (0x80000001).coerce(2).length.should == 2 + (0x80000001).coerce(2)[0].should == 2 + (0x80000001).coerce(2)[1].should == 0x80000001 + (0x80000001).coerce(2)[0].class.to_s.should == 'Bignum' + (0x80000001).coerce(2)[1].class.to_s.should == 'Bignum' + end + + it "returns [Bignum, Bignum] if other is a Bignum" do + (0x80000001).coerce(0x80000002).class.to_s.should == 'Array' + (0x80000001).coerce(0x80000002).length.should == 2 + (0x80000001).coerce(0x80000002)[0].should == 0x80000002 + (0x80000001).coerce(0x80000002)[1].should == 0x80000001 + (0x80000001).coerce(0x80000002)[0].class.to_s.should == 'Bignum' + (0x80000001).coerce(0x80000002)[1].class.to_s.should == 'Bignum' + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/comparison_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/comparison_spec.rb new file mode 100644 index 0000000000..c7f80c76ab --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/comparison_spec.rb @@ -0,0 +1,28 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#<=>" do + it "returns -1, 0, 1 when self is less than, equal, or greater than other, where other is Bignum" do + (0x80000001 <=> 0x80000002).should == -1 + (0x80000002 <=> 0x80000002).should == 0 + (0x80000002 <=> 0x80000001).should == 1 + (-0x80000001 <=> -0x80000002).should == 1 + (-0x80000002 <=> -0x80000002).should == 0 + (-0x80000002 <=> -0x80000001).should == -1 + end + + it "returns -1, 0, 1 when self is less than, equal, or greater than other, where other is Fixnum" do + (0x80000001 <=> 55).should == 1 + (-0x80000002 <=> 55).should == -1 + (0x80000002 <=> -55).should == 1 + (-0x80000001 <=> -55).should == -1 + end + + it "returns -1, 0, 1 when self is less than, equal, or greater than other, where other is Float" do + (0x80000001 <=> 2147483650.4).should == -1 + (0x80000002 <=> 2147483650.0).should == 0 + (0x80000002 <=> 2147483649.0).should == 1 + (-0x80000001 <=> -2147483649.5).should == 1 + (-0x80000002 <=> -2147483650.0).should == 0 + (-0x80000002 <=> -2147483649.5).should == -1 + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/complement_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/complement_spec.rb new file mode 100644 index 0000000000..b0704be6e6 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/complement_spec.rb @@ -0,0 +1,14 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#~" do + it "returns self bitwise inverted" do + (~0x80000000).should == -0x80000001 + (~(-0x80000000)).should == 0x7fffffff + end + + it "normalizes to a fixnum if necessary" do + (~0x80000001).class.to_s.should == 'Bignum' + #This test is dependent upon FIXNUM_MAX + (~(-0x80000000)).class.to_s.should == 'Fixnum' if 0x7fffffff.class.to_s == 'Fixnum' + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/divide_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/divide_spec.rb new file mode 100644 index 0000000000..0273e511b5 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/divide_spec.rb @@ -0,0 +1,206 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +class X + def coerce(other) + [other, 2] + end +end + +@bignum_divide = shared "Bignum divide" do |cmd| + it "returns self divided by other, where other is Fixnum" do + (0x80000000).send(cmd, 4).should == 0x20000000 + (0x80000001).send(cmd, 4).should == 0x20000000 + (0x8000000D).send(cmd, 4).should == 0x20000003 + end + it "rounds toward minus infinity" do + (0x8000000D).send(cmd, 4).should == 0x20000003 + (-0x8000000D).send(cmd, 4).should == -0x20000004 + (0x8000000D).send(cmd, -4).should == -0x20000004 + (-0x8000000D).send(cmd, -4).should == 0x20000003 + end + it "returns self divided by other (rounded toward minus infinity)" do + (0x80000000).send(cmd, 16.2).should_be_close(132560719.012346, TOLERANCE) + (0x80000000).send(cmd, 0x80000000).should == 1 + end + it "invokes divmod directly, when other is Bignum" do + class Bignum + alias :old_divmod :divmod + def divmod(other) + [55,56] + end + end + (0x80000000).send(cmd, 0x80000000).should_not == 55 + (0x80000000).old_divmod(0x80000000).should == [1,0] + class Bignum + remove_method :divmod + alias :divmod :old_divmod + end + end + it "normalizes values to Fixnum as necessary" do + ((0x180000000).send(cmd, 2)).class.to_s.should == 'Bignum' + (0x80000000).send(cmd, 0x80000000).class.to_s.should == 'Fixnum' + end + it "raises ZeroDivisionError if other is zero and not a Float" do + should_raise(ZeroDivisionError) { (0x80000000).send cmd, 0 } + end + it "does NOT raise ZeroDivisionError if other is zero and is a Float" do + (0x80000000).send(cmd, 0.0).to_s.should == 'Infinity' + (0x80000000).send(cmd, -0.0).to_s.should == '-Infinity' + end + it "coerces on other if other is not Fixnum, Bignum or Float" do + 0x987654321.send(cmd, X.new).should == 0x4c3b2a190 + 0x80000000.send(cmd, X.new).should == 0x40000000 + end +end + +describe "Bignum#/" do + it_behaves_like(@bignum_divide, :/) +end +describe "Bignum#div" do + it_behaves_like(@bignum_divide, :div) +end + +describe "Bignum#/ (dynamic behaviour)" do + it "dynamically invokes the / method if other is not a Fixnum, Bignum or Float" do + class Bignum + alias :old_div :/ + def /(other) + 33 + end + end + (0x987654321.old_div(X.new)).should == 33 + (0x987654321.div(X.new)).should_not == 33 + (0x80000000.old_div(X.new)).should == 33 + (0x80000000.div(X.new)).should_not == 33 + (0x80000000.old_div(0x80000000)).should == 1 + (0x80000000.div(0x80000000)).should == 1 + class Bignum + remove_method :/ + alias :/ :old_div + end + end +end + +describe "Bignum#div (dynamic behaviour)" do + it "dynamically invokes the div method if other is not a Fixnum, Bignum or Float" do + class Bignum + alias :old_div :div + def div(other) + 33 + end + end + (0x987654321.old_div(X.new)).should == 33 + (0x987654321 / X.new).should_not == 33 + (0x80000000.old_div(X.new)).should == 33 + (0x80000000 / X.new).should_not == 33 + (0x80000000.old_div(0x80000000)).should == 1 + (0x80000000 / 0x80000000).should == 1 + class Bignum + remove_method :div + alias :div :old_div + end + end +end +require File.dirname(__FILE__) + '/../../spec_helper' + +class X + def coerce(other) + [other, 2] + end +end + +@bignum_divide = shared "Bignum divide" do |cmd| + it "returns self divided by other, where other is Fixnum" do + (0x80000000).send(cmd, 4).should == 0x20000000 + (0x80000001).send(cmd, 4).should == 0x20000000 + (0x8000000D).send(cmd, 4).should == 0x20000003 + end + it "rounds toward minus infinity" do + (0x8000000D).send(cmd, 4).should == 0x20000003 + (-0x8000000D).send(cmd, 4).should == -0x20000004 + (0x8000000D).send(cmd, -4).should == -0x20000004 + (-0x8000000D).send(cmd, -4).should == 0x20000003 + end + it "returns self divided by other (rounded toward minus infinity)" do + (0x80000000).send(cmd, 16.2).should_be_close(132560719.012346, TOLERANCE) + (0x80000000).send(cmd, 0x80000000).should == 1 + end + it "invokes divmod directly, when other is Bignum" do + class Bignum + alias :old_divmod :divmod + def divmod(other) + [55,56] + end + end + (0x80000000).send(cmd, 0x80000000).should_not == 55 + (0x80000000).old_divmod(0x80000000).should == [1,0] + class Bignum + remove_method :divmod + alias :divmod :old_divmod + end + end + it "normalizes values to Fixnum as necessary" do + ((0x180000000).send(cmd, 2)).class.to_s.should == 'Bignum' + (0x80000000).send(cmd, 0x80000000).class.to_s.should == 'Fixnum' + end + it "raises ZeroDivisionError if other is zero and not a Float" do + should_raise(ZeroDivisionError) { (0x80000000).send cmd, 0 } + end + it "does NOT raise ZeroDivisionError if other is zero and is a Float" do + (0x80000000).send(cmd, 0.0).to_s.should == 'Infinity' + (0x80000000).send(cmd, -0.0).to_s.should == '-Infinity' + end + it "coerces on other if other is not Fixnum, Bignum or Float" do + 0x987654321.send(cmd, X.new).should == 0x4c3b2a190 + 0x80000000.send(cmd, X.new).should == 0x40000000 + end +end + +describe "Bignum#/" do + it_behaves_like(@bignum_divide, :/) +end +describe "Bignum#div" do + it_behaves_like(@bignum_divide, :div) +end + +describe "Bignum#/ (dynamic behaviour)" do + it "dynamically invokes the / method if other is not a Fixnum, Bignum or Float" do + class Bignum + alias :old_div :/ + def /(other) + 33 + end + end + (0x987654321.old_div(X.new)).should == 33 + (0x987654321.div(X.new)).should_not == 33 + (0x80000000.old_div(X.new)).should == 33 + (0x80000000.div(X.new)).should_not == 33 + (0x80000000.old_div(0x80000000)).should == 1 + (0x80000000.div(0x80000000)).should == 1 + class Bignum + remove_method :/ + alias :/ :old_div + end + end +end + +describe "Bignum#div (dynamic behaviour)" do + it "dynamically invokes the div method if other is not a Fixnum, Bignum or Float" do + class Bignum + alias :old_div :div + def div(other) + 33 + end + end + (0x987654321.old_div(X.new)).should == 33 + (0x987654321 / X.new).should_not == 33 + (0x80000000.old_div(X.new)).should == 33 + (0x80000000 / X.new).should_not == 33 + (0x80000000.old_div(0x80000000)).should == 1 + (0x80000000 / 0x80000000).should == 1 + class Bignum + remove_method :div + alias :div :old_div + end + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/divmod_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/divmod_spec.rb new file mode 100644 index 0000000000..afd8fbdc69 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/divmod_spec.rb @@ -0,0 +1,112 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +class X + def coerce(other) + [other, 2] + end +end + +describe "Bignum#divmod" do + it "returns self divided by other, where other is Fixnum" do + (0x80000000).divmod(4)[0].should == 0x20000000 + (0x80000000).divmod(4)[1].should == 0 + (0x80000001).divmod(4)[0].should == 0x20000000 + (0x80000001).divmod(4)[1].should == 1 + (0x8000000D).divmod(4)[0].should == 0x20000003 + (0x8000000D).divmod(4)[1].should == 1 + end + it "rounds toward minus infinity" do + (0x8000000D).divmod(4)[0].should == 0x20000003 + (0x8000000D).divmod(4)[1].should == 1 + (-0x8000000D).divmod(4)[0].should == -0x20000004 + (-0x8000000D).divmod(4)[1].should == 3 + (0x8000000D).divmod(-4)[0].should == -0x20000004 + (0x8000000D).divmod(-4)[1].should == -3 + (-0x8000000D).divmod(-4)[0].should == 0x20000003 + (-0x8000000D).divmod(-4)[1].should == -1 + end + it "returns self divided by other (rounded toward minus infinity), where other is Bignum" do + (0x80000000).divmod(16.2)[0].should == 132560719 + (0x80000000).divmod(16.2)[1].should_be_close(0.2, TOLERANCE) + (0x80000000).divmod(0x80000000)[0].should == 1 + (0x80000000).divmod(0x80000000)[1].should_be_close(0, TOLERANCE) + end + it "always produces an integer as the div part" do + (0x80000000).divmod(16.2)[0].class.to_s.should_not == 'Float' + end + it "normalizes values to Fixnum as necessary" do + ((0x180000000).divmod(2))[0].class.to_s.should == 'Bignum' + ((0x180000000).divmod(2))[1].class.to_s.should == 'Fixnum' + ((0x8ffffffff).divmod(0x800000000))[0].class.to_s.should == 'Fixnum' + ((0x8ffffffff).divmod(0x800000000))[1].class.to_s.should == 'Bignum' + end + it "raises ZeroDivisionError if other is zero and not a Float" do + should_raise(ZeroDivisionError) { (0x80000000).divmod(0) } + end + it "raises FloatDomainError if other is zero and is a Float" do + should_raise(FloatDomainError) { (0x80000000).divmod(0.0) } + should_raise(FloatDomainError) { (0x80000000).divmod(-0.0) } + end + it "coerces on other if other is not Fixnum, Bignum or Float" do + 0x987654321.divmod(X.new)[0].should == 0x4c3b2a190 + 0x987654321.divmod(X.new)[1].should == 1 + 0x80000000.divmod(X.new)[0].should == 0x40000000 + 0x80000000.divmod(X.new)[1].should == 0 + end +end +require File.dirname(__FILE__) + '/../../spec_helper' + +class X + def coerce(other) + [other, 2] + end +end + +describe "Bignum#divmod" do + it "returns self divided by other, where other is Fixnum" do + (0x80000000).divmod(4)[0].should == 0x20000000 + (0x80000000).divmod(4)[1].should == 0 + (0x80000001).divmod(4)[0].should == 0x20000000 + (0x80000001).divmod(4)[1].should == 1 + (0x8000000D).divmod(4)[0].should == 0x20000003 + (0x8000000D).divmod(4)[1].should == 1 + end + it "rounds toward minus infinity" do + (0x8000000D).divmod(4)[0].should == 0x20000003 + (0x8000000D).divmod(4)[1].should == 1 + (-0x8000000D).divmod(4)[0].should == -0x20000004 + (-0x8000000D).divmod(4)[1].should == 3 + (0x8000000D).divmod(-4)[0].should == -0x20000004 + (0x8000000D).divmod(-4)[1].should == -3 + (-0x8000000D).divmod(-4)[0].should == 0x20000003 + (-0x8000000D).divmod(-4)[1].should == -1 + end + it "returns self divided by other (rounded toward minus infinity), where other is Bignum" do + (0x80000000).divmod(16.2)[0].should == 132560719 + (0x80000000).divmod(16.2)[1].should_be_close(0.2, TOLERANCE) + (0x80000000).divmod(0x80000000)[0].should == 1 + (0x80000000).divmod(0x80000000)[1].should_be_close(0, TOLERANCE) + end + it "always produces an integer as the div part" do + (0x80000000).divmod(16.2)[0].class.to_s.should_not == 'Float' + end + it "normalizes values to Fixnum as necessary" do + ((0x180000000).divmod(2))[0].class.to_s.should == 'Bignum' + ((0x180000000).divmod(2))[1].class.to_s.should == 'Fixnum' + ((0x8ffffffff).divmod(0x800000000))[0].class.to_s.should == 'Fixnum' + ((0x8ffffffff).divmod(0x800000000))[1].class.to_s.should == 'Bignum' + end + it "raises ZeroDivisionError if other is zero and not a Float" do + should_raise(ZeroDivisionError) { (0x80000000).divmod(0) } + end + it "raises FloatDomainError if other is zero and is a Float" do + should_raise(FloatDomainError) { (0x80000000).divmod(0.0) } + should_raise(FloatDomainError) { (0x80000000).divmod(-0.0) } + end + it "coerces on other if other is not Fixnum, Bignum or Float" do + 0x987654321.divmod(X.new)[0].should == 0x4c3b2a190 + 0x987654321.divmod(X.new)[1].should == 1 + 0x80000000.divmod(X.new)[0].should == 0x40000000 + 0x80000000.divmod(X.new)[1].should == 0 + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/element_reference_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/element_reference_spec.rb new file mode 100644 index 0000000000..94e7a7bc71 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/element_reference_spec.rb @@ -0,0 +1,88 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#[]" do + it "returns the nth bit in the binary representation of self" do + (0x80001384)[2].should == 1 + (0x80001384)[9.2].should == 1 + (0x80001384)[21].should == 0 + end + it "returns 0 if index is negative" do + (0x800000000)[-10].should == 0 + (-0x800000000)[-10].should === 0 + end + it "returns 1 if self is negative and the index is greater than the most significant bit" do + (-0x80000000)[33].should == 1 + (-0x80000000)[0x80000000].should == 1 + end + it "returns 0 if self is positive and the index is greater than the most significant bit" do + (0x80000000)[33].should == 0 + (0x80000000)[0x80000000].should == 0 + end + class X + def to_int + 2 + end + end + it "converts index to integer using to_int" do + (0x800012834)[X.new].should == 1 + (0x800012831)[X.new].should == 0 + end + it "dynamically invokes [] after converting index to_int" do + class Bignum + alias :old_ref :[] + def [](other) + 2 + end + end + (0x800012834)[X.new].should == 2 + (0x800012831)[X.new].should == 2 + class Bignum + remove_method :[] + alias :[] :old_ref + end + end +end +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#[]" do + it "returns the nth bit in the binary representation of self" do + (0x80001384)[2].should == 1 + (0x80001384)[9.2].should == 1 + (0x80001384)[21].should == 0 + end + it "returns 0 if index is negative" do + (0x800000000)[-10].should == 0 + (-0x800000000)[-10].should === 0 + end + it "returns 1 if self is negative and the index is greater than the most significant bit" do + (-0x80000000)[33].should == 1 + (-0x80000000)[0x80000000].should == 1 + end + it "returns 0 if self is positive and the index is greater than the most significant bit" do + (0x80000000)[33].should == 0 + (0x80000000)[0x80000000].should == 0 + end + class X + def to_int + 2 + end + end + it "converts index to integer using to_int" do + (0x800012834)[X.new].should == 1 + (0x800012831)[X.new].should == 0 + end + it "dynamically invokes [] after converting index to_int" do + class Bignum + alias :old_ref :[] + def [](other) + 2 + end + end + (0x800012834)[X.new].should == 2 + (0x800012831)[X.new].should == 2 + class Bignum + remove_method :[] + alias :[] :old_ref + end + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/eql_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/eql_spec.rb new file mode 100644 index 0000000000..821295ef62 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/eql_spec.rb @@ -0,0 +1,25 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#eql?" do + it "returns true if other is a Bignum with the same value, where other is Bignum" do + (0x80000000).eql?(0x80000000).should == true + (0x987654321).eql?(0x987654321).should == true + (0x80000000).eql?(0x987654321).should_not == true + (0x80000000).eql?(-0x80000000).should_not == true + (-0x987654321).eql?(0x987654321).should_not == true + end + it "returns false if other is a Fixnum" do + (0x80000000).eql?(4).should_not == true + (0x80000000).eql?(-5).should_not == true + end + it "returns false if other is a Float" do + (0x80000000).eql?(3.14).should_not == true + (0x80000000).eql?(2147483648.0).should_not == true + end + it "returns false if other is a not a Fixnum, Bignum or Float" do + class X + end + (0x80000000).eql?(X.new).should_not == true + (0x80000000).eql?(X.new).should_not == true + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/equal_value_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/equal_value_spec.rb new file mode 100644 index 0000000000..164cadd1dc --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/equal_value_spec.rb @@ -0,0 +1,23 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#==" do + it "should true if self has the same value as other" do + (0x80000000 == 0x80000000).should == true + (0x987654321 == 0x987654321).should == true + (0x80000000 == 0x80000003).should_not == true + (0x80000000 == -0x80000000).should_not == true + (0x80000000 == 2147483648.0).should == true + (0x987654321 == 5.4).should_not == true + (0x800000000 == 121).should_not == true + end + + it "calls 'other == self' if coercion fails" do + class X + def ==(other) + 0x80000000 == other + end + end + (0x987654321 == X.new).should == false + (0x80000000 == X.new).should == true + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/exponent_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/exponent_spec.rb new file mode 100644 index 0000000000..7b5ec04dbe --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/exponent_spec.rb @@ -0,0 +1,13 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#**" do + it "returns self raised to other power, where other is Fixnum" do + (0x80000001 ** 4).class.to_s.should == 'Bignum' + (0x80000001 ** 4).should == 0x10000000800000018000000200000001 + end + it "returns self raised to other power, where other is Float" do + (0x80000001 ** 5.2).class.to_s.should == 'Float' + (10000000000 ** 0.5).should == 100000.0 + (0x80000001 ** 5.2).to_s.should == '3.35764906138532e+048' + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/gt_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/gt_spec.rb new file mode 100644 index 0000000000..572efaa2cc --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/gt_spec.rb @@ -0,0 +1,33 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#>" do + it "returns true if self is greater than other, where other is Bignum" do + (0x80000000 > 0x80000000).should_not == true + (0x80000002 > 0x80000001).should == true + (0x80000001 > 0x80000002).should == false + (-0x80000002 > 0x80000001).should == false + (-0x80000001 > 0x80000002).should == false + (0x80000002 > -0x80000001).should == true + (0x80000001 > -0x80000002).should == true + (-0x80000002 > -0x80000001).should == false + (-0x80000001 > -0x80000002).should == true + end + it "returns true if self is greater than other, where other is Fixnum" do + (0x80000000 > 10).should == true + (-0x80000000 > 10).should == false + (0x80000000 > -10).should == true + (-0x80000000 > -10).should == false + end + it "returns true if self is greater than other, where other is Float" do + (0x80000000 > 14.6).should == true + (-0x80000000 > 14.6).should == false + (0x80000000 > -14.6).should == true + (-0x80000000 > -14.6).should == false + + (0x80000000 > 2147483648.0).should_not == true + (0x80000000 > 2147483648.5).should == false + (0x80000000 > 2147483647.5).should == true + (-0x80000000 > -2147483648.5).should == true + (-0x80000000 > -2147483647.5).should == false + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/gte_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/gte_spec.rb new file mode 100644 index 0000000000..5f14569331 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/gte_spec.rb @@ -0,0 +1,33 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#>=" do + it "returns true if self is greater than or equal to other, where other is Bignum" do + (0x80000000 >= 0x80000000).should == true + (0x80000002 >= 0x80000001).should == true + (0x80000001 >= 0x80000002).should == false + (-0x80000002 >= 0x80000001).should == false + (-0x80000001 >= 0x80000002).should == false + (0x80000002 >= -0x80000001).should == true + (0x80000001 >= -0x80000002).should == true + (-0x80000002 >= -0x80000001).should == false + (-0x80000001 >= -0x80000002).should == true + end + it "returns true if self is greater than or equal to other, where other is Fixnum" do + (0x80000000 >= 10).should == true + (-0x80000000 >= 10).should == false + (0x80000000 >= -10).should == true + (-0x80000000 >= -10).should == false + end + it "returns true if self is greater than or equal to other, where other is Float" do + (0x80000000 >= 14.6).should == true + (-0x80000000 >= 14.6).should == false + (0x80000000 >= -14.6).should == true + (-0x80000000 >= -14.6).should == false + + (0x80000000 >= 2147483648.0).should == true + (0x80000000 >= 2147483648.5).should == false + (0x80000000 >= 2147483647.5).should == true + (-0x80000000 >= -2147483648.5).should == true + (-0x80000000 >= -2147483647.5).should == false + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/hash_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/hash_spec.rb new file mode 100644 index 0000000000..c04d053280 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/hash_spec.rb @@ -0,0 +1,9 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#hash" do + it "has the property that a.eql?(b) implies a.hash == b.hash" do + 0x80000005.hash.should == 0x80000005.hash + (0x80000000 + 5).hash.should == (0x8000000A - 5).hash + 0x80000000.hash.should_not == 0x80000005.hash + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/left_shift_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/left_shift_spec.rb new file mode 100644 index 0000000000..6b1272ba35 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/left_shift_spec.rb @@ -0,0 +1,18 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#<<" do + it "returns self shifted left other bits" do + (0x80000001 << 4).should == 0x800000010 + (0x80000002 << 1).should == 0x100000004 + (0x80000002 << 0).should == 0x80000002 + (0x80000001 << 1.5).should == 0x100000002 + (0x80000002 << -1).should == 0x40000001 + (0x987654321 << 9).should == 0x130eca864200 + end + it "return the right shift alignment" do + ((-0xffffffff) << -32).should == -1 + ((-0xffffffff) << -33).should == -1 + ((-0x7fffffffffffffff) << -63).should == -1 + ((-0xffffffffffffffff) << -64).should == -1 + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/lt_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/lt_spec.rb new file mode 100644 index 0000000000..5934706c6d --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/lt_spec.rb @@ -0,0 +1,33 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#<" do + it "returns true if self is less than other, where other is Bignum" do + (0x80000000 < 0x80000000).should_not == true + (0x80000002 < 0x80000001).should == false + (0x80000001 < 0x80000002).should == true + (-0x80000002 < 0x80000001).should == true + (-0x80000001 < 0x80000002).should == true + (0x80000002 < -0x80000001).should == false + (0x80000001 < -0x80000002).should == false + (-0x80000002 < -0x80000001).should == true + (-0x80000001 < -0x80000002).should == false + end + it "returns true if self is greater than other, where other is Fixnum" do + (0x80000000 < 10).should == false + (-0x80000000 < 10).should == true + (0x80000000 < -10).should == false + (-0x80000000 < -10).should == true + end + it "returns true if self is greater than other, where other is Float" do + (0x80000000 < 14.6).should == false + (-0x80000000 < 14.6).should == true + (0x80000000 < -14.6).should == false + (-0x80000000 < -14.6).should == true + + (0x80000000 < 2147483648.0).should_not == true + (0x80000000 < 2147483648.5).should == true + (0x80000000 < 2147483647.5).should == false + (-0x80000000 < -2147483648.5).should == false + (-0x80000000 < -2147483647.5).should == true + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/lte_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/lte_spec.rb new file mode 100644 index 0000000000..cc6c6071d1 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/lte_spec.rb @@ -0,0 +1,33 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#<=" do + it "returns false if self is less than or equal to other, where other is Bignum" do + (0x80000000 <= 0x80000000).should == true + (0x80000002 <= 0x80000001).should == false + (0x80000001 <= 0x80000002).should == true + (-0x80000002 <= 0x80000001).should == true + (-0x80000001 <= 0x80000002).should == true + (0x80000002 <= -0x80000001).should == false + (0x80000001 <= -0x80000002).should == false + (-0x80000002 <= -0x80000001).should == true + (-0x80000001 <= -0x80000002).should == false + end + it "returns false if self is less than or equal to other, where other is Fixnum" do + (0x80000000 <= 10).should == false + (-0x80000000 <= 10).should == true + (0x80000000 <= -10).should == false + (-0x80000000 <= -10).should == true + end + it "returns false if self is less than or equal to other, where other is Float" do + (0x80000000 <= 14.6).should == false + (-0x80000000 <= 14.6).should == true + (0x80000000 <= -14.6).should == false + (-0x80000000 <= -14.6).should == true + + (0x80000000 <= 2147483648.0).should == true + (0x80000000 <= 2147483648.5).should == true + (0x80000000 <= 2147483647.5).should == false + (-0x80000000 <= -2147483648.5).should == false + (-0x80000000 <= -2147483647.5).should == true + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/minus_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/minus_spec.rb new file mode 100644 index 0000000000..67409401a2 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/minus_spec.rb @@ -0,0 +1,24 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#-" do + it "returns self minus other, where other is Bignum" do + (0x90000009 - 0x90000009).should == 0 + (0x90000009 - 0x10000009).should == 0x80000000 + (-0x80000000 - 0x80000000).should == -0x100000000 + (0x80000000 - (-0x80000000)).should == 0x100000000 + end + it "normalizes to Fixnum if necessary" do + (0x80000000 - 0x80000000).class.to_s.should == 'Fixnum' + (0x80000005 - 0x40000006).class.to_s.should == 'Fixnum' + end + it "returns self minus other, where other is Fixnum" do + (0x80000030 - 0x30).should == 0x80000000 + (-0x80000000 - 0x30).should == -0x80000030 + (0x80000000 - (-0x30)).should == 0x80000030 + end + it "returns self minus other, where other is Float" do + (0x80000000 - 56.7).should_be_close(2147483591.3, TOLERANCE) + (-0x80000000 - 2147483648.0).should_be_close(-4294967296.0, TOLERANCE) + (0x80000000 - (-56.7)).should_be_close(2147483704.7, TOLERANCE) + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/modulo_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/modulo_spec.rb new file mode 100644 index 0000000000..5a4580c3ce --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/modulo_spec.rb @@ -0,0 +1,106 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +class X + def coerce(other) + [other, 2] + end +end + +@bignum_modulo = shared "Bignum modulo" do |cmd| + it "returns self modulo by other, where other is Fixnum" do + (0x80000000).send(cmd, 4).should == 0 + (0x80000001).send(cmd, 4).should == 1 + (0x8000000D).send(cmd, 4).should == 1 + end + it "rounds toward minus infinity" do + (0x8000000D).send(cmd, 4).should == 1 + (-0x8000000D).send(cmd, 4).should == 3 + (0x8000000D).send(cmd, -4).should == -3 + (-0x8000000D).send(cmd, -4).should == -1 + end + it "returns self modulo other, where other is Bignum" do + (0x80000000).send(cmd, 0x80000000).should == 0 + end + it "returns self modulo other, where other is Float" do + (0x80000000).send(cmd, 16.2).should_be_close(0.2, TOLERANCE) + end + it "normalizes values to Fixnum as necessary" do + ((0x180000000).send(cmd, 2)).class.to_s.should == 'Fixnum' + ((0x8ffffffff).send(cmd, 0x800000000)).class.to_s.should == 'Bignum' + end + it "invokes divmod directly, when other is Bignum" do + class Bignum + alias :old_divmod :divmod + def divmod(other) + [55,56] + end + end + (0x80000000).send(cmd, 0x80000000).should_not == 56 + (0x80000000).old_divmod(0x80000000).should == [1,0] + class Bignum + remove_method :divmod + alias :divmod :old_divmod + end + end + it "raises ZeroDivisionError if other is zero and not a Float" do + should_raise(ZeroDivisionError) { (0x80000000).send(cmd, 0) } + end + it "raises FloatDomainError if other is zero and is a Float" do + (0x80000000).send(cmd, 0.0).to_s.should == 'NaN' + (0x80000000).send(cmd, -0.0).to_s.should == 'NaN' + end + it "coerces on other if other is not Fixnum, Bignum or Float" do + 0x987654321.send(cmd, X.new).should == 1 + 0x80000000.send(cmd, X.new).should == 0 + end +end + +describe "Bignum#%" do + it_behaves_like(@bignum_modulo, :%) +end + +describe "Bignum#modulo" do + it_behaves_like(@bignum_modulo, :modulo) +end + +describe "Bignum#% (dynamic behaviour)" do + it "dynamically invokes the % method if other is not a Fixnum, Bignum or Float" do + class Bignum + alias :old_mod :% + def %(other) + 33 + end + end + (0x987654321.old_mod(X.new)).should == 33 + (0x987654321.modulo(X.new)).should_not == 33 + (0x80000000.old_mod(X.new)).should == 33 + (0x80000000.modulo(X.new)).should_not == 33 + (0x80000000.old_mod(0x80000000)).should == 0 + (0x80000000.modulo(0x80000000)).should == 0 + class Bignum + remove_method :% + alias :% :old_mod + end + end +end + +describe "Bignum#modulo (dynamic behaviour)" do + it "dynamically invokes the modulo method if other is not a Fixnum, Bignum or Float" do + class Bignum + alias :old_mod :modulo + def modulo(other) + 33 + end + end + (0x987654321.old_mod(X.new)).should == 33 + (0x987654321 % X.new).should_not == 33 + (0x80000000.old_mod(X.new)).should == 33 + (0x80000000 % X.new).should_not == 33 + (0x80000000.old_mod(0x80000000)).should == 0 + (0x80000000 % 0x80000000).should == 0 + class Bignum + remove_method :modulo + alias :modulo :old_mod + end + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/multiply_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/multiply_spec.rb new file mode 100644 index 0000000000..c4c7a4d20d --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/multiply_spec.rb @@ -0,0 +1,55 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +class X + def coerce(other) + [other, 2] + end +end + +describe "Bignum#*" do + it "returns self multiplied by other, where other is Bignum" do + (0x80000000 * 0x80000000).should == 0x4000000000000000 + (0x80000000 * -0x80000000).should == -0x4000000000000000 + (-0x80000000 * 0x80000000).should == -0x4000000000000000 + (-0x80000000 * -0x80000000).should == 0x4000000000000000 + end + it "returns self multiplied by other, where other is Fixnum" do + (0x80000000 * 5).should == 0x280000000 + (0x80000000 * -5).should == -0x280000000 + (-0x80000000 * 5).should == -0x280000000 + (-0x80000000 * -5).should == 0x280000000 + (0x80000000 * 1).should == 0x80000000 + (0x80000000 * 0).should == 0 + end + it "normalizes result to a Fixnum as necessary" do + (0x80000000 * 0).class.to_s.should == 'Fixnum' + end + it "returns self multiplied by other, where other is Float" do + (0x80000000 * 7.4).should_be_close(15891378995.2, TOLERANCE) + (0x80000000 * -7.4).should_be_close(-15891378995.2, TOLERANCE) + (-0x80000000 * 7.4).should_be_close(-15891378995.2, TOLERANCE) + (-0x80000000 * -7.4).should_be_close(15891378995.2, TOLERANCE) + (0x80000000 * 0.001).should_be_close(2147483.648, TOLERANCE) + (0x80000000 * 0.0).should_be_close(0.0, TOLERANCE) + (0x80000000 * 0.0).class.to_s.should == 'Float' + end + it "coerces on other if other is not Fixnum, Bignum or Float" do + (0x987654321 * X.new).should == 0x130eca8642 + (0x80000000 * X.new).should == 0x100000000 + end + it "dynamically invokes the * method if other is not a Fixnum, Bignum or Float" do + class Bignum + alias :old_mult :* + def *(other) + 33 + end + end + (0x987654321.old_mult(X.new)).should == 33 + (0x80000000.old_mult(X.new)).should == 33 + (0x80000000.old_mult(0)).should == 0 + class Bignum + remove_method :* + alias :* :old_mult + end + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/plus_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/plus_spec.rb new file mode 100644 index 0000000000..b65ac64e15 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/plus_spec.rb @@ -0,0 +1,30 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#+" do + it "returns self plus other, where other is Bignum" do + (0x80000000 + 0x80000000).should == 0x100000000 + (0x80000000 + -0x80000000).should == 0 + (-0x80000000 + 0x80000000).should == 0 + (-0x80000000 + -0x80000000).should == -0x100000000 + end + it "returns self plus other, where other is Fixnum" do + (0x80000000 + 5).should == 0x80000005 + (0x80000000 + -5).should == 0x7ffffffb + (-0x80000000 + 5).should == -0x7ffffffb + (-0x80000000 + -5).should == -0x80000005 + (0x80000000 + 1).should == 0x80000001 + (0x80000000 + 0).should == 0x80000000 + end + it "normalizes result to a Fixnum as necessary" do + (0x80000000 + (-0x70000000)).class.to_s.should == 'Fixnum' + (-0x80000000 + 0x70000000).class.to_s.should == 'Fixnum' + end + it "returns self plus other, where other is Float" do + (0x80000000 + 7.4).should_be_close(2147483655.4, TOLERANCE) + (0x80000000 + -7.4).should_be_close(2147483640.6, TOLERANCE) + (-0x80000000 + 7.4).should_be_close(-2147483640.6, TOLERANCE) + (-0x80000000 + -7.4).should_be_close(-2147483655.4, TOLERANCE) + (0x80000000 + 0.0).should_be_close(2147483648.0, TOLERANCE) + (0x80000000 + 0.0).class.to_s.should == 'Float' + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/power_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/power_spec.rb new file mode 100644 index 0000000000..bd7f68b461 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/power_spec.rb @@ -0,0 +1,59 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#**" do + it "returns self to the power of other, where other is Bignum" do + (0x80000000 ** 0x80000000).to_s.should == 'Infinity' + (0x80000000 ** -0x80000000).should == 0.0 + ((-0x80000000) ** 0x80000000).to_s.should == 'Infinity' + ((-0x80000000) ** (-0x80000000)).should == 0.0 + end + it "returns self to the power of other, where other is Fixnum" do + (0x80000000 ** 5).should == 0x800000000000000000000000000000000000000 + (0x80000000 ** (-5)).should_be_close(2.18952885050753e-047, TOLERANCE) + ((-0x80000000) ** 5).should == -0x800000000000000000000000000000000000000 + ((-0x80000000) ** (-5)).should_be_close(2.18952885050753e-047, TOLERANCE) + (0x80000000 ** 1).should == 0x80000000 + (0x80000000 ** 0).should == 1 + end + it "normalizes result to a Fixnum as necessary" do + (0x80000000 ** 1).class.to_s.should == 'Bignum' + (0x80000000 ** 0).class.to_s.should == 'Fixnum' + end + it "returns self to the power of other, where other is Float" do + (0x80000000 ** 7.4).to_s.should == '1.13836361284227e+069' + (0x80000000 ** -7.4).to_s.should == '8.78453939249866e-070' + ((-0x80000000) ** 7.4).to_s.should == 'NaN' + ((-0x80000000) ** (-7.4)).to_s.should == 'NaN' + (0x80000000 ** 0.001).should_be_close(1.0217200827143, TOLERANCE) + (0x80000000 ** 0.0).should_be_close(1.0, TOLERANCE) + (0x80000000 ** 0.0).class.to_s.should == 'Float' + end + it "returns 1 if other is 0" do + (0x80000000 ** 0).should == 1 + (0x80000000 ** 0).class.to_s.should == 'Fixnum' + (0x80000000 ** 0.0).should == 1.0 + (0x80000000 ** 0.0).class.to_s.should == 'Float' + end + it "returns NaN if other is NaN" do + (0x80000000 ** (0.0/0.0)).to_s.should == 'NaN' + end + it "returns NaN if self < 0 and other is not an Integer or Infinity" do + ((-0x80000000) ** -1.2).to_s.should == 'NaN' + end + it "returns 0 if other is -Infinity" do + (0x80000000 ** (-1.0/0.0)).should == 0 + end + it "returns Infinity if other is Infinity" do + (0x80000000 ** (1.0/0.0)).to_s.should == 'Infinity' + end + it "coerces on other if other is not Fixnum, Bignum or Float" do + class X + def coerce(other) + [other, 2] + end + end + (0x987654321 ** X.new).should == 0x5accbaad2cd7a44a41 + (0x80000000 ** X.new).should == 0x4000000000000000 + end +end + diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/quo_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/quo_spec.rb new file mode 100644 index 0000000000..6e554c0535 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/quo_spec.rb @@ -0,0 +1,29 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#quo" do + it "returns the floating-point result of self divided by other, where other is BigInteger" do + (0x80000005.quo(0x80000000)).should_be_close(1.00000000232831, TOLERANCE) + (0x80000000.quo(0x80000000)).should_be_close(1.0, TOLERANCE) + end + it "returns the floating-point result of self divided by other, where other is Fixnum" do + (0x80000000.quo(13)).should_be_close(165191049.846154, TOLERANCE) + end + it "returns the floating-point result of self divided by other, where other is Float" do + (0x80000000.quo(2.5)).should_be_close(858993459.2, TOLERANCE) + end + it "does NOT raise an exception when other is zero" do + (0x80000000.quo(0.0)).to_s.should == "Infinity" + (0x80000000.quo(-0.0)).to_s.should == "-Infinity" + (0x80000000.quo(0)).to_s.should == "Infinity" + (0x80000000.quo(-0)).to_s.should == "Infinity" + end + it "coerces on other if other is not Fixnum, Bignum or Float" do + class X + def coerce(other) + [other, 2] + end + end + (0x987654321.quo(X.new)).should == 20463133072.5 + (0x80000000.quo(X.new)).should == 1073741824.0 + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/remainder_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/remainder_spec.rb new file mode 100644 index 0000000000..768b3b30d4 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/remainder_spec.rb @@ -0,0 +1,64 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +class X + def coerce(other) + [other, 2] + end +end + +describe "Bignum#remainder" do + it "returns remainder from self divided by other, where other is Fixnum" do + (0x80000000).remainder(4).should == 0 + (0x80000001).remainder(4).should == 1 + (0x8000000D).remainder(4).should == 1 + end + it "does not round toward minus infinity" do + (0x8000000D).remainder(4).should == 1 + (-0x8000000D).remainder(4).should == -1 + (0x8000000D).remainder(-4).should == 1 + (-0x8000000D).remainder(-4).should == -1 + (0x8000000D).remainder(4.0).should == 1 + (-0x8000000D).remainder(4.0).should == -1 + (0x8000000D).remainder(-4.0).should == 1 + (-0x8000000D).remainder(-4.0).should == -1 + end + it "returns remainder from self divided by other, where other is Bignum" do + (0x80000000).remainder(0x80000000).should == 0 + end + it "returns remainder from self divided by other, where other is Float" do + (0x80000000).remainder(16.2).should_be_close(0.2, TOLERANCE) + end + it "normalizes values to Fixnum as necessary" do + ((0x180000000).remainder(2)).class.to_s.should == 'Fixnum' + ((0x8ffffffff).remainder(0x800000000)).class.to_s.should == 'Bignum' + end + + it "raises ZeroDivisionError if other is zero and not a Float" do + should_raise(ZeroDivisionError) { 0x80000000.remainder(0) } + end + + it "does NOT raise ZeroDivisionError if other is zero and is a Float" do + (0x80000000.remainder(0.0)).to_s.should == 'NaN' + (0x80000000.remainder(-0.0)).to_s.should == 'NaN' + end + + it "coerces on other if other is not Fixnum, Bignum or Float" do + 0x987654321.remainder(X.new).should == 1 + 0x80000000.remainder(X.new).should == 0 + end + it "dynamically invokes the remainder method if other is not a Fixnum, Bignum or Float" do + class Bignum + alias :old_rem :remainder + def remainder(other) + 33 + end + end + (0x987654321.old_rem(X.new)).should == 33 + (0x80000000.old_rem(X.new)).should == 33 + (0x80000000.old_rem(0x80000000)).should == 0 + class Bignum + remove_method :remainder + alias :remainder :old_rem + end + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/right_shift_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/right_shift_spec.rb new file mode 100644 index 0000000000..0bf9d93f92 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/right_shift_spec.rb @@ -0,0 +1,18 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#>>" do + it "returns self shifted right other bits" do + (0x80000001 >> 4).should == 0x8000000 + (0x80000002 >> 1).should == 0x40000001 + (0x80000002 >> 0).should == 0x80000002 + (0x80000001 >> 1.5).should == 0x40000000 + (0x80000002 >> -1).should == 0x100000004 + (0x987654321 >> 9).should == 0x4c3b2a1 + end + it "return the right shift alignment" do + ((-0xffffffff) >> 32).should == -1 + ((-0xffffffff) >> 33).should == -1 + ((-0x7fffffffffffffff) >> 63).should == -1 + ((-0xffffffffffffffff) >> 64).should == -1 + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/size_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/size_spec.rb new file mode 100644 index 0000000000..e48fd6967b --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/size_spec.rb @@ -0,0 +1,17 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#size" do + it "returns number of bytes in self" do + (0x100000000000000).size.should == 8 #(256**7) + (0x10000000000000000).size.should == 12 #(256**8) + (0x1000000000000000000).size.should == 12 #(256**9) + (0x100000000000000000000).size.should == 12 #(256**10) + (0xffffffffffffffffffff).size.should == 12 #(256**10-1) + (0x10000000000000000000000).size.should == 12 #(256**11) + (0x1000000000000000000000000).size.should == 16 #(256**12) + (0x10000000000000000000000000000000000000000).size.should == 24 #(256**20) + (0xffffffffffffffffffffffffffffffffffffffff).size.should == 20 #(256**20-1) + (0x100000000000000000000000000000000000000000000000000000000000000000000000000000000).size.should == 44 #(256**40) + (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff).size.should == 40 #(256**40-1) + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/subtract_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/subtract_spec.rb new file mode 100644 index 0000000000..18519677b5 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/subtract_spec.rb @@ -0,0 +1,65 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +class X + def coerce(other) + [other, 2] + end +end + +describe "Bignum#-" do + it "returns self plus other, where other is Bignum" do + (0x80000000 - 0x80000000).should == 0 + (0x80000000 - (-0x80000000)).should == 0x100000000 + ((-0x80000000) - 0x80000000).should == -0x100000000 + ((-0x80000000) - (-0x80000000)).should == 0 + end + it "returns self plus other, where other is Fixnum" do + (0x80000000 - 5).should == 0x7ffffffb + (0x80000000 - (-5)).should == 0x80000005 + ((-0x80000000) - 5).should == -0x80000005 + ((-0x80000000) - (-5)).should == -0x7ffffffb + (0x80000000 - 1).should == 0x7fffffff + (0x80000000 - 0).should == 0x80000000 + end + it "normalizes result to a Fixnum as necessary" do + (0x80000000 - 0).class.to_s.should == 'Bignum' + (0x80000000 - 0x80000000).class.to_s.should == 'Fixnum' + end + it "returns self plus other, where other is Float" do + (0x80000000 - 7.4).should_be_close(2147483640.6, TOLERANCE) + (0x80000000 - (-7.4)).should_be_close(2147483655.4, TOLERANCE) + ((-0x80000000) - 7.4).should_be_close(-2147483655.4, TOLERANCE) + ((-0x80000000) - (-7.4)).should_be_close(-2147483640.6, TOLERANCE) + (0x80000000 - 0.001).should_be_close(2147483647.999, TOLERANCE) + (0x80000000 - 0.0).should_be_close(2147483648.0, TOLERANCE) + (0x80000000 - 0.0).class.to_s.should == 'Float' + end + it "returns NaN if other is NaN" do + (0x80000000 - (0.0/0.0)).to_s.should == 'NaN' + end + it "returns Infinity if other is -Infinity" do + (0x80000000 - (-1.0/0.0)).to_s.should == 'Infinity' + end + it "returns -Infinity if other is Infinity" do + (0x80000000 - (1.0/0.0)).to_s.should == '-Infinity' + end + it "coerces on other if other is not Fixnum, Bignum or Float" do + (0x987654321 - X.new).should == 0x98765431f + (0x80000000 - X.new).should == 0x7ffffffe + end + it "dynamically invokes the - method if other is not a Fixnum, Bignum or Float" do + class Bignum + alias :old_sub :- + def -(other) + 33 + end + end + (0x987654321.old_sub(X.new)).should == 33 + (0x80000000.old_sub(X.new)).should == 33 + (0x80000000.old_sub(0)).should == 0x80000000 + class Bignum + remove_method :- + alias :- :old_sub + end + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/to_f_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/to_f_spec.rb new file mode 100644 index 0000000000..ee9e042a5a --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/to_f_spec.rb @@ -0,0 +1,10 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#to_f" do + it "returns self converted to Float" do + 0x80000000.to_f.should == 2147483648.0 + -0x80000000.to_f.should == -2147483648.0 + (0x987654321**100).to_f.to_s.should == 'Infinity' + (-0x987654321**100).to_f.to_s.should == '-Infinity' + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/to_s_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/to_s_spec.rb new file mode 100644 index 0000000000..5d16aa1232 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/to_s_spec.rb @@ -0,0 +1,25 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#to_s" do + it "returns a string representation of self, base 10" do + 0x80000000.to_s.should == "2147483648" + (-0x80000000).to_s.should == "-2147483648" + 0x987654321.to_s.should == "40926266145" + end + + it "returns a string with the representation of self in base x" do + # 18446744073709551616 == 2**64 + 18446744073709551616.to_s(2).should == "10000000000000000000000000000000000000000000000000000000000000000" + 18446744073709551616.to_s(8).should == "2000000000000000000000" + 18446744073709551616.to_s(16).should == "10000000000000000" + 18446744073709551616.to_s(32).should == "g000000000000" + end + + it "raises an ArgumentError exception if argument is 0" do + should_raise(ArgumentError){ 18446744073709551616.to_s(0) } + end + + it "raises an ArgumentError exception if argument is bigger than 36" do + should_raise(ArgumentError){ 18446744073709551616.to_s(37) } # Max is 36 + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Bignum/uminus_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/Bignum/uminus_spec.rb new file mode 100644 index 0000000000..ecdee129d5 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Bignum/uminus_spec.rb @@ -0,0 +1,10 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe "Bignum#-@" do + it "negates self" do + (0x80000000).send(:-@).should == (-0x80000000) + (0x987654321).send(:-@).should == (-0x987654321) + (-0x80000000).send(:-@).should == (0x80000000) + (-0x987654321).send(:-@).should == (0x987654321) + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/Class/test_class.rb b/merlin/main/languages/ruby/Tests/Builtin/Class/test_class.rb new file mode 100644 index 0000000000..0fa5989727 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Class/test_class.rb @@ -0,0 +1,28 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "../../Util/simple_test.rb" + +describe "inspecting class attributes" do + it "reads the name of a class" do + "bob".class.name.should == "String" + "bob".class.to_s.should == "String" + "bob".class.class.name.should == "Class" + String.name.should == "String" + String.class.name.should == "Class" + end +end + +finished diff --git a/merlin/main/languages/ruby/Tests/Builtin/Enumerable/test_enumerable.rb b/merlin/main/languages/ruby/Tests/Builtin/Enumerable/test_enumerable.rb new file mode 100644 index 0000000000..af279e1ceb --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Enumerable/test_enumerable.rb @@ -0,0 +1,800 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "../../Util/simple_test.rb" + +# all?, any?, collect, detect, +# each_with_index, entries, +# find, find_all, grep, include?, inject, map, max, member?, min, +# partition, reject, select, sort, sort_by, to_a, zip + +# Note about "nil" tests: +# All of the methods on Enumerable should support self == nil. +# If, for some bizarre reason, NilClass is extended to implement +# "each" and mixin "Enumerable", all of the methods should work +# (basically this is testing that the C# methods on Enumerable accept null) + +class EnumTest + include Enumerable + + def initialize(*list) + @list = list.empty? ? [2, 5, 3, 6, 1, 4] : list + end + + def each + @list.each { |i| yield i } + end +end + +describe "Enumerable" do + it "Some builtins should include Enumerable" do + String.include?(Enumerable).should == true + Array.include?(Enumerable).should == true + Hash.include?(Enumerable).should == true + Range.include?(Enumerable).should == true + NilClass.include?(Enumerable).should == false + end + + it "user types can mixin Enumerable" do + # Verify that all enumerable methods work with a "nil" self + class NilClass + include Enumerable + + def each + yield nil + end + end + + class EnumTestNoEach + include Enumerable + end + + EnumTest.include?(Enumerable).should == true + EnumTestNoEach.include?(Enumerable).should == true + NilClass.include?(Enumerable).should == true + end + + it "Enumerable calls each" do + $each_called = false + class EnumTest2 + include Enumerable + def each + $each_called = true + yield 123 + end + end + + EnumTest2.new.to_a.should == [123] + $each_called.should == true + + should_raise(NoMethodError) { EnumTestNoEach.new.to_a } + end +end + +describe "Enumerable#all?" do + it "basic functionaltiy" do + tests = [ [nil], [false], [true], [1,2,nil,true], [1,true,false], [1, 2, 3] ] + expected = [ false, false, true, false, false, true ] + + tests.length.times do |i| + tests[i].all?.should == expected[i] + EnumTest.new(*tests[i]).all?.should == expected[i] + end + + nil.all?.should == false + end + + it "with block" do + r0 = %w{ foo bar bazz }.all? { |w| w.length >= 3 } + r1 = %w{ foo bar bazz }.all? { |w| w.length >= 4 } + r2 = EnumTest.new(*%w{ foo bar bazz }).all? { |w| w.length >= 3 } + r3 = [1, 2, 3, 4].all? { |e| e % 2 == 0 } + r4 = EnumTest.new(2, 8, 6).all? { |e| e % 2 == 0 } + r5 = nil.all? {|e| !e} + + [r0, r1, r2, r3, r4, r5].should == [true, false, true, false, true, true] + + end +end + +describe "Enumerable#any?" do + it "basic functionaltiy" do + tests = [ [nil], [false], [true], [1,2,nil,true], [1,true,false], [1, 2, 3] ] + expected = [ false, false, true, true, true, true ] + + tests.length.times do |i| + tests[i].any?.should == expected[i] + EnumTest.new(*tests[i]).any?.should == expected[i] + end + + nil.any?.should == false + end + + it "with block" do + r0 = %w{ ant bear cat }.any? { |w| w.length >= 4 } + r1 = %w{ ant bear cat }.any? { |w| w.length >= 5 } + r2 = EnumTest.new(*%w{ ant bear cat }).any? { |w| w.length >= 4 } + r3 = [1, 2, 3, 4].any? { |e| e % 2 == 1 } + r4 = EnumTest.new(2, 8, 6).any? { |e| e % 2 == 1 } + r5 = nil.any? {|e| !e} + + [r0, r1, r2, r3, r4, r5].should == [true, false, true, true, false, true] + end +end + +describe "Enumerable#collect" do + it "without block" do + (5..9).collect.should == [5,6,7,8,9] + "abc\ndef".collect.should == ["abc\n","def"] + [1,2,3].collect.should == [1,2,3] + nil.collect.should == [nil] + end + + it "with block" do + r = (1..4).collect { |i| i*i } + r.should == [1,4,9,16] + + r = EnumTest.new(5,3,2).collect { |i| i %2 } + r.should == [1,1,0] + + r = EnumTest.new(*%w{ ant bear cat }).collect { |w| w.length } + r.should == [3,4,3] + + r = [[:a,:b],[:c,:d],[:e,:f]].collect { |a| a.collect { |i| i.to_s } } + r.should == [['a','b'],['c','d'],['e','f']] + + r = EnumTest.new(123, "567", :abc).collect { |e| e } + r.should == [123, "567", :abc] + + r = EnumTest.new(123, "567", :abc).collect { |e| "foo" } + r.should == ["foo","foo","foo"] + + r = nil.collect {|e| "bar" } + r.should == ["bar"] + end +end + +describe "Enumerable#detect" do + it "basic functionality" do + def foo x + x.detect {|i| i%5 + i%7 == 0 } + end + + foo(1..34).should == nil + foo(1..100).should == 35 + foo(35..100).should == 35 + foo(50..100).should == 70 + + r = nil.detect {|i| i == nil} + r.should == nil + end + + it "detect calls its argument if the element is not found" do + # TODO: test with Procs, lambda expressions + + class TestDetect; def call; "not found"; end; end + t = TestDetect.new + + r = (1...35).detect(t) {|i| i%5 + i%7 == 0 } + r.should == "not found" + + r = (1...35).detect(nil) {|i| i%5 + i%7 == 0 } + r.should == nil + + r = (1..35).detect(t) {|i| i%5 + i%7 == 0 } + r.should == 35 + + r = EnumTest.new("all", "any", "detect").detect(t) {|w| w.length == 6} + r.should == "detect" + + r = EnumTest.new("all", "any", "detect").detect(t) {|w| w.length == 5} + r.should == "not found" + + r = nil.detect(t) { |i| i } + r.should == "not found" + end + + it "negative tests" do + [].detect # no error because yield is not called + should_raise(LocalJumpError, "no block given") { ["all", "any", "detect"].detect } + should_raise(LocalJumpError, "no block given") { EnumTest.new("all", "any", "detect").detect(nil) } + should_raise(LocalJumpError, "no block given") { ["all", "any", "detect"].detect(TestDetect.new) } + end +end + +describe "Enumerable#each_with_index" do + it "basic functionality" do + hash = Hash.new + r = %w(foo bar baz).each_with_index { |i,x| hash[i] = x } + hash.should == {"foo"=>0, "bar"=>1, "baz"=>2} + r.should == %w(foo bar baz) + + array = [] + r = nil.each_with_index { |i,x| array << i << x } + r.should == nil + array.should == [nil, 0] + + # test order + items = [] + r = [:a, :b, :c, :d].each_with_index { |i,x| items << [i,x]} + items.should == [[:a,0], [:b,1], [:c,2], [:d,3]] + r.should == [:a, :b, :c, :d] + end + + it "each_with_index calls each" do + array = [] + enum = EnumTest.new("abc", 123, ["test"], :x123) + r = enum.each_with_index do |i,x| + if x % 2 == 0 + array << i + end + end + array.should == ["abc", ["test"]] + r.should == enum + end + + it "negative tests" do + should_raise(LocalJumpError, "no block given") { [].each_with_index } + should_raise(LocalJumpError, "no block given") { [1].each_with_index } + end + +end + +describe "Enumerable#entries" do + it "entries returns all items in an array" do + (7..10).entries.should == [7,8,9,10] + EnumTest.new("foo", 1, "bar", :baz).entries.should == ["foo", 1, "bar", :baz] + {}.entries.should == [] + {1,2,3,4}.entries.should == {1,2,3,4}.to_a + {1,2,3,4}.entries.should == [[1,2], [3,4]] + "hello\nworld".entries.should == ["hello\n", "world"] + nil.entries.should == [nil] + + r = [1,2,3].entries {|p| raise "dummy block" } # block is ignored + r.should == [1,2,3] + end +end + +describe "Enumerable#find" do + it "find is the same as detect" do + def foo x + x.find {|i| i%5 + i%7 == 0 } + end + + foo(1..34).should == nil + foo(1..100).should == 35 + foo(35..100).should == 35 + foo(50..100).should == 70 + + r = nil.find {|i| i == nil} + r.should == nil + end + + it "find calls its argument if the element is not found" do + t = TestDetect.new + + r = (1...35).find(t) {|i| i%5 + i%7 == 0 } + r.should == "not found" + + r = (1...35).find(nil) {|i| i%5 + i%7 == 0 } + r.should == nil + + r = (1..35).find(t) {|i| i%5 + i%7 == 0 } + r.should == 35 + + r = EnumTest.new("all", "any", "detect").find(t) {|w| w.length == 6} + r.should == "detect" + + r = EnumTest.new("all", "any", "detect").find(t) {|w| w.length == 5} + r.should == "not found" + + r = nil.find(t) { |i| i } + r.should == "not found" + end + + it "negative tests" do + [].find # no error because yield is not called + should_raise(LocalJumpError, "no block given") { ["all", "any", "detect"].find } + should_raise(LocalJumpError, "no block given") { EnumTest.new("all", "any", "detect").find(nil) } + should_raise(LocalJumpError, "no block given") { ["all", "any", "detect"].find(TestDetect.new) } + end + +end + +describe "Enumerable#find_all" do + it "basic functionality" do + r = (1..10).find_all { |i| i % 3 == 0 } + r.should == [3, 6, 9] + + test = [{1,"a",3,4,0,"b"},{0,5,1,"a"},["b","a"]] + r = test.find_all { |e| e[1] == "a" } + r.should == test + r = test.find_all { |e| e[0] == "b" } + r.should == [test[0], test[2]] + + r = nil.find_all { |e| e == nil } + r.should == [nil] + r = nil.find_all { |e| e } + r.should == [] + + r = EnumTest.new("a", :b, "c"[0]).find_all { |e| e == :b } + r.should == [:b] + + [].find_all + should_raise(LocalJumpError, "no block given") { [1].find_all } + end +end + +describe "Enumerable#grep" do + it "basic functionality" do + (1..100).grep(42..45).should == [42, 43, 44, 45] + + EnumTest.new("abc", "cab", "aaa", "def").grep("aaa").should == ["aaa"] + + should_raise(ArgumentError) { [].grep } + end + + it "grep calls === on the argument" do + $case_equal_called = 0 + class TestGrep + def initialize data + @data = data + end + + def === other + $case_equal_called += 1 + @data[0] == other[0] + end + end + + ["abc", "cab", "aaa", "def"].grep(TestGrep.new("abc")).should == ["abc", "aaa"] + $case_equal_called.should == 4 + + EnumTest.new("abc", "cab", "aaa", "def").grep(TestGrep.new("abc")).should == ["abc", "aaa"] + $case_equal_called.should == 8 + end +end + +describe "Enumerable#include?" do + it "basic functionality" do + a = ["abc", "cab", "aaa", "def"] + ["abc", "cab", "aaa", "def"].each do |w| + a.include?(w).should == true + a.include?(w.dup + "a").should == false + end + + (1...10).include?(5).should == true + (1...10).include?(10).should == false + + nil.include?(nil).should == true + + should_raise(ArgumentError) { [].include? } + end + + it "include? calls == on the argument" do + $equal_called = 0 + class TestInclude + def initialize data + @data = data + end + + def == other + $equal_called += 1 + @data == other + end + end + + t = [TestInclude.new("foo"), TestInclude.new("bar")] + t.include?("foo").should == true + $equal_called.should == 1 + t.include?("bar").should == true + $equal_called.should == 3 + t.include?("baz").should == false + $equal_called.should == 5 + end +end + +describe "Enumerable#inject" do + it "basic functionality" do + r = (1..10).inject { |sum, n| sum + n } + r.should == 55 + r = (1..5).inject(2) { |prod, n| prod * n} + r.should == 240 + + r = (42..46).inject([]) { |a,i| a << i } + r.should == [42, 43, 44, 45, 46] + + r = nil.inject(123) {|a,i| a} + r.should == 123 + end + + it "inject with no block" do + [].inject.should == nil + [].inject(nil).should == nil + [].inject([]).should == [] + [].inject("foo").should == "foo" + + [1].inject.should == 1 + [:bar].inject.should == :bar + + should_raise(LocalJumpError, 'no block given') { [1].inject(2) } + should_raise(LocalJumpError, 'no block given') { [1,2].inject } + end + +end + +describe "Enumerable#map" do + it "map is the same as collect" do + (5..9).map.should == [5,6,7,8,9] + "abc\ndef".map.should == ["abc\n","def"] + [1,2,3].map.should == [1,2,3] + nil.map.should == [nil] + + r = (1..4).map { |i| i*i } + r.should == [1,4,9,16] + + r = nil.map {|e| "bar" } + r.map == ["bar"] + end + + it "map works on a user type with each" do + r = EnumTest.new(5,3,2).map { |i| i %2 } + r.should == [1,1,0] + + r = EnumTest.new(*%w{ ant bear cat }).map { |w| w.length } + r.should == [3,4,3] + + r = [[:a,:b],[:c,:d],[:e,:f]].map { |a| a.map { |i| i.to_s } } + r.should == [['a','b'],['c','d'],['e','f']] + + r = EnumTest.new(123, "567", :abc).map { |e| e } + r.should == [123, "567", :abc] + + r = EnumTest.new(123, "567", :abc).map { |e| "foo" } + r.should == ["foo","foo","foo"] + end +end + +describe "Enumerable#max" do + it "basic functionality" do + %w(foo bar baz).max.should == "foo" + (1..100).max.should == 100 + + # newer entries don't override older ones + x = "abc" + [x, "abc", "abc"].max.equal?(x).should == true + + nil.max.should == nil + end + + it "block overrides <=>" do + r = %w(foo bar baz).max { |x,y| x[2] <=> y[2] } + r.should == "baz" + + r = (1..100).max { |x,y| (x % 60) <=> (y % 60) } + r.should == 59 + + r = (1..100).max { |x,y| (x % 16) <=> (y % 16) } + r.should == 15 + end +end + +describe "Enumerable#member?" do + it "member? is the same as member?" do + a = ["abc", "cab", "aaa", "def"] + ["abc", "cab", "aaa", "def"].each do |w| + a.member?(w).should == true + a.member?(w.dup + "a").should == false + end + + (1...10).member?(5).should == true + (1...10).member?(10).should == false + + nil.member?(nil).should == true + + should_raise(ArgumentError) { [].member? } + end + + it "member? calls == on the argument" do + $equal_called = 0 + t = [TestInclude.new("foo"), TestInclude.new("bar")] + t.member?("foo").should == true + $equal_called.should == 1 + t.member?("bar").should == true + $equal_called.should == 3 + t.member?("baz").should == false + $equal_called.should == 5 + end +end + +describe "Enumerable#min" do + it "basic functionality" do + %w(foo bar baz).min.should == "bar" + (1..100).min.should == 1 + + # newer entries don't override older ones + x = "abc" + [x, "abc", "abc"].min.equal?(x).should == true + + nil.min.should == nil + end + + it "block overrides <=>" do + r = %w(foo bar baz).min { |x,y| x[2] <=> y[2] } + r.should == "foo" + + r = (1..100).min { |x,y| (x % 60) <=> (y % 60) } + r.should == 60 + + r = (42..80).min { |x,y| (x % 42) <=> (y % 42) } + r.should == 42 + end +end + +describe "Enumerable#partition" do + it "basic functionality" do + r = (5..10).partition { |i| i%2 == 0 } + r.should == [[6,8,10], [5,7,9]] + + # partition is non-destructive + a = [7, 4, 9, 3, 4, 8, 1, 2, 6, 9] + r = a.partition { |i| i < 5 } + a.should == [7, 4, 9, 3, 4, 8, 1, 2, 6, 9] + r.should == [[4, 3, 4, 1, 2], [7, 9, 8, 6, 9]] + + r = a.partition { |i| i < 10 } + r.should == [a, []] + + r = a.partition { |i| i >= 10 } + r.should == [[], a] + + r = nil.partition { |i| i } + r.should == [[], [nil]] + end + + it "use partition to implement quicksort" do + def qsort a + if a.length <= 1 + return a + end + a = a.dup + k = a.pop + part = a.partition { |i| i < k } + qsort(part[0]).push(k).concat(qsort(part[1])) + end + + qsort([7, 4, 9, 3, 4, 8, 1, 2, 6, 9]).should == [1, 2, 3, 4, 4, 6, 7, 8, 9, 9] + qsort(["foo", "bar", "baz"]).should == ["bar", "baz", "foo"] + end + + it "negative tests" do + [].partition.should == [[],[]] + should_raise(LocalJumpError, 'no block given') { [1].partition } + should_raise(ArgumentError) { [1].partition(1) } + end +end + +describe "Enumerable#reject" do + it "reject is the opposite of find_all/select" do + r = (1..10).reject { |i| i % 3 != 0 } + r.should == [3, 6, 9] + + test = [{1,"a",3,4,0,"b"},{0,5,1,"a"},["b","a"]] + r = test.reject { |e| e[1] != "a" } + r.should == test + r = test.reject { |e| e[0] != "b" } + r.should == [test[0], test[2]] + + r = nil.reject { |e| e != nil } + r.should == [nil] + r = nil.reject { |e| !e } + r.should == [] + + r = EnumTest.new("a", :b, "c"[0]).reject { |e| e != :b } + r.should == [:b] + + [].reject + should_raise(LocalJumpError, "no block given") { [1].reject } + end +end + +describe "Enumerable#select" do + it "select is the same as find_all" do + r = (1..10).select { |i| i % 3 == 0 } + r.should == [3, 6, 9] + + test = [{1,"a",3,4,0,"b"},{0,5,1,"a"},["b","a"]] + r = test.select { |e| e[1] == "a" } + r.should == test + r = test.select { |e| e[0] == "b" } + r.should == [test[0], test[2]] + + r = nil.select { |e| e == nil } + r.should == [nil] + r = nil.select { |e| e } + r.should == [] + + r = EnumTest.new("a", :b, "c"[0]).select { |e| e == :b } + r.should == [:b] + + [].select + should_raise(LocalJumpError, "no block given") { [1].select } + end +end + +describe "Enumerable#sort" do + it "basic functionality" do + unsorted = [7, 4, 9, 3, 4, 8, 1, 2, 6, 9] + sorted = [1, 2, 3, 4, 4, 6, 7, 8, 9, 9] + unsorted.sort.should == sorted + unsorted.should == [7, 4, 9, 3, 4, 8, 1, 2, 6, 9] + + r = unsorted.sort { |x,y| y <=> x } + r.should == sorted.reverse + + ["foo", "bar", "baz"].sort.should == ["bar", "baz", "foo"] + + nil.sort.should == [nil] + end + + it "block is used for comparison" do + $words = %w( + all? any? collect detect each_with_index entries find + find_all grep include? inject map max member? min + partition reject select sort sort_by to_a zip) + + backup = $words.dup + $words.reverse.sort.should == $words + $words.should == backup + + # sort by length, then by word order + # (we want the resulting ordering to be total, because + # sort isn't stable so the exact order wouldn't be guaranteed) + r = $words.reverse.sort do |x,y| + z = x.length <=> y.length + if z == 0 + z = x <=> y + end + z + end + + $words_sorted_by_length = %w( + map max min zip + all? any? find grep sort to_a + detect inject reject select collect entries member? sort_by + find_all include? + partition + each_with_index) + + r.should == $words_sorted_by_length + $words.should == backup + end +end + +describe "Enumerable#sort_by" do + it "basic functionality" do + a = [7, 4, 9, 3, 4, 8, 1, 2, 6, 9] + r = a.sort_by { |i| -i } + r.should == a.sort.reverse + + backup = $words.dup + r = $words.reverse.sort_by { |w| w } + r.should == $words + + r = $words.reverse.sort_by { |w| [w.length, w] } + r.should == $words_sorted_by_length + $words.should == backup + + r = nil.sort_by { |e| e } + r.should == [nil] + end + + it "negative tests" do + [].sort_by.should == [] + should_raise(LocalJumpError, "no block given") { [1].sort_by } + end +end + +describe "Enumerable#to_a" do + it "to_a is the same as entries (returns all enumerated items in an array)" do + (7..10).to_a.should == [7,8,9,10] + EnumTest.new("foo", 1, "bar", :baz).to_a.should == ["foo", 1, "bar", :baz] + {}.to_a.should == [] + {1,2,3,4}.to_a.should == {1,2,3,4}.to_a + {1,2,3,4}.to_a.should == [[1,2], [3,4]] + "hello\nworld".to_a.should == ["hello\n", "world"] + nil.to_a.should == [] # nil defines its own to_a method which takes precedence + + r = [1,2,3].to_a {|p| raise "dummy block" } # block is ignored + r.should == [1,2,3] + end +end + +describe "Enumerable#zip" do + it "basic functionality" do + (1..3).zip.should == [[1],[2],[3]] + (1..3).zip(4..6).should == [[1,4],[2,5],[3,6]] + (1..3).zip(4..5).should == [[1,4],[2,5],[3,nil]] + (1..3).zip(4..6, 7..9).should == [[1,4,7],[2,5,8],[3,6,9]] + (1..3).zip(4..6, 7..9, 10..12).should == [[1,4,7,10],[2,5,8,11],[3,6,9,12]] + (1..3).zip(4..7, 7..8, 10..10).should == [[1, 4, 7, 10], [2, 5, 8, nil], [3, 6, nil, nil]] + end + + it "zip works with a block" do + def ziptest *args + a = [] + r = (1..3).zip(*args) { |i| a << i } + r.should == nil + a + end + + ziptest.should == [[1],[2],[3]] + ziptest(4..6).should == [[1,4],[2,5],[3,6]] + ziptest(4..6, 7..9).should == [[1,4,7],[2,5,8],[3,6,9]] + ziptest(4..7, 7..8, 10..10).should == [[1, 4, 7, 10], [2, 5, 8, nil], [3, 6, nil, nil]] + + a = [] + r = (1..3).zip { |i| a << i } + a.should == [[1],[2],[3]] + r.should == nil + + a = [] + r = (1..3).zip(4..6) { |i| a << i } + a.should == [[1,4],[2,5],[3,6]] + r.should == nil + + a = [] + r = (1..3).zip(4..6, 7..9) { |i| a << i } + a.should == [[1,4,7],[2,5,8],[3,6,9]] + r.should == nil + + a = [] + r = (1..3).zip(4..7, 7..8, 10..10) { |i| a << i } + a.should == [[1, 4, 7, 10], [2, 5, 8, nil], [3, 6, nil, nil]] + r.should == nil + + end + + it "zip works with a type implementing each" do + EnumTest.new(1, 2, 3).zip.should == [[1],[2],[3]] + EnumTest.new(1, 2, 3).zip(4..6).should == [[1,4],[2,5],[3,6]] + EnumTest.new(1, 2, 3).zip(4..6, EnumTest.new(7, 8, 9)).should == [[1,4,7],[2,5,8],[3,6,9]] + EnumTest.new(1, 2, 3).zip(EnumTest.new(4, 5, 6), 7..9, 10..12).should == [[1,4,7,10],[2,5,8,11],[3,6,9,12]] + EnumTest.new(1, 2, 3).zip(EnumTest.new(4, 5, 6, 7), 7..8, 10..10).should == [[1, 4, 7, 10], [2, 5, 8, nil], [3, 6, nil, nil]] + end + + it "zip calls to_a on arguments" do + $to_a_called = 0 + class TestZip + def initialize data + @data = data + end + + def to_a + $to_a_called += 1 + @data + end + end + + (1..3).zip(TestZip.new([4,5,6])).should == [[1,4],[2,5],[3,6]] + $to_a_called.should == 1 + (1..3).zip(4..6, TestZip.new([7,8,9])).should == [[1,4,7],[2,5,8],[3,6,9]] + $to_a_called.should == 2 + (1..3).zip(TestZip.new([4,5,6]), 7..9, TestZip.new([10,11,12])).should == [[1,4,7,10],[2,5,8,11],[3,6,9,12]] + $to_a_called.should == 4 + end +end + +finished diff --git a/merlin/main/languages/ruby/Tests/Builtin/Hash/test_hash.rb b/merlin/main/languages/ruby/Tests/Builtin/Hash/test_hash.rb new file mode 100644 index 0000000000..3edaa0e0a9 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Hash/test_hash.rb @@ -0,0 +1,1541 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "../../Util/simple_test.rb" + +# ==, [], []=, clear, default, default=, default_proc, delete, +# delete_if, each, each_key, each_pair, each_value, empty?, fetch, +# has_key?, has_value?, include?, index, indexes, indices, +# initialize_copy, inspect, invert, key?, keys, length, member?, +# merge, merge!, rehash, reject, reject!, replace, select, shift, +# size, sort, store, to_a, to_hash, to_s, update, value?, values, +# values_at + +class MyHash < Hash; end + +class ToHashHash < Hash + def to_hash() { "to_hash" => "was", "called!" => "duh." } end +end + +# TODO: remove globals when we get instance variable support +class ToHash + def initialize(hash) $tohash_hash = hash end + def to_hash() $tohash_hash end +end + +class EqlTest + def initialize (result) + $eqltest_called_eql = false + $eqltest_called_eq = false + $eqltest_result = result + end + + def called_eql() $eqltest_called_eql end + def called_eq() $eqltest_called_eq end + + def == (x) + $eqltest_called_eq = true + $eqltest_result + end + + def eql?(x) + $eqltest_called_eql = true + $eqltest_result + end +end + +class IntHash0 < EqlTest + def initialize (result) + $eqltest_called_eql = false + $eqltest_called_eq = false + $eqltest_result = result + $inthash0_called_hash == false + end + + def called_hash() $inthash0_called_hash end + def called_hash=(x) $inthash0_called_hash = x end + + def hash + $inthash0_called_hash = true + 0 + end +end + +class IntHash1 < EqlTest + def initialize (result) + $eqltest_called_eql = false + $eqltest_called_eq = false + $eqltest_result = result + $inthash0_called_hash == false + end + + def called_hash() $inthash1_called_hash end + def called_hash=(x) $inthash1_called_hash = x end + + def hash + $inthash1_called_hash = true + 1 + end +end + +class IntHashX < EqlTest + def initialize (val, result) + $eqltest_called_eql = false + $eqltest_called_eq = false + $eqltest_result = result + $inthashX_called_hash == false + $inthashX_hash = val + end + + def called_hash() $inthashX_called_hash end + def called_hash=(x) $inthashX_called_hash = x end + def newhash=(x) $inthashX_hash = x end + + def hash + $inthashX_called_hash = true + $inthashX_hash + end +end + +class NoHash + def hash() raise("hash shouldn't be called here"); end +end + +class ToHashUsingMissing + def initialize(hash) $tohashmm_hash = hash end + def respond_to? (m) m == :to_hash end + def method_missing (m) + if m == :to_hash + $tohashmm_hash + end + end +end + +context "Hash" do + specify "includes Enumerable" do + Hash.include?(Enumerable).should == true + end +end + +context "Hash#[] (class method)" do + specify "[] creates a Hash; values can be provided as the argument list" do + Hash[:a, 1, :b, 2].should == {:a => 1, :b => 2} + Hash[].should == {} + end + + specify "[] creates a Hash; values can be provided as one single hash" do + Hash[:a => 1, :b => 2].should == {:a => 1, :b => 2} + Hash[{1 => 2, 3 => 4}].should == {1 => 2, 3 => 4} + Hash[{}].should == {} + end + + specify "[] raises on odd parameter list count" do + should_raise(ArgumentError) { Hash[1, 2, 3] } + end + + specify "[] raises when mixing argument styles" do + should_raise(ArgumentError) { Hash[1, 2, {3 => 4}] } + Hash[1, 2, 3, {3 => 4}].should == {1 => 2, 3 => {3 => 4}} + end + + specify "[] shouldn't call to_hash" do + should_raise(ArgumentError) { Hash[ToHash.new({1 => 2})] } + end + + specify "[] should always return an instance of the class it's called on" do + Hash[MyHash[1, 2]].class.should == Hash + MyHash[Hash[1, 2]].class.should == MyHash + MyHash[].class.should == MyHash + end +end + +context "Hash#new" do + specify "new with default argument creates a new Hash with default object" do + Hash.new(5).default.should == 5 + Hash.new({}).default.should == {} + end + + specify "new with default argument shouldn't create a copy of the default" do + str = "foo" + Hash.new(str).default.equal?(str).should == true + end + + specify "new with block creates a Hash with a default_proc" do + Hash.new.default_proc.should == nil + + hash = Hash.new { |x| "Answer to #{x}" } + hash[5].should == "Answer to 5" + hash["x"].should == "Answer to x" + hash.default_proc.call(5).should == "Answer to 5" + hash.default_proc.call("x").should == "Answer to x" + end + + specify "new with Proc sets the proc as the default value" do + p = Proc.new { |x| "Answer to #{x}" } + hash = Hash.new p + hash.default_proc.should == nil + hash.default.class.should == Proc + hash.default.equal?(p).should == true + hash[5].should == p + hash["x"].should == p + hash.default.call(5).should == "Answer to 5" + hash.default.call("x").should == "Answer to x" + end + + specify "new with default argument and default block should raise" do + should_raise(ArgumentError) { Hash.new(5) { 0 } } + should_raise(ArgumentError) { Hash.new(nil) { 0 } } + end +end + +context "Hash#==" do + specify "== is true if they have the same number of keys and each key-value pair matches" do + Hash.new(5).should == Hash.new(1) + h = Hash.new {|h, k| 1} + h2 = Hash.new {} + h.should == h2 + h = Hash.new {|h, k| 1} + h.should == Hash.new(2) + + a = {:a => 5} + b = {} + + a.should_not == b + + b[:a] = 5 + + a.should == b + + c = Hash.new {|h, k| 1} + d = Hash.new {} + c[1] = 2 + d[1] = 2 + c.should == d + end + + # Bad test? Ruby doesn't seem to call to_hash on its argument + skip "(Fails on Ruby 1.8.6) == should call to_hash on its argument" do + obj = ToHash.new({1 => 2, 3 => 4}) + obj.to_hash.should == {1 => 2, 3 => 4} + + {1 => 2, 3 => 4}.should == obj + end + + specify "== shouldn't call to_hash on hash subclasses" do + {5 => 6}.should == ToHashHash[5 => 6] + end + + specify "== should be instantly false when the numbers of keys differ" do + obj = EqlTest.new true + {}.should_not == { obj => obj } + { obj => obj }.should_not == {} + obj.called_eql.should == false + obj.called_eq.should == false + end + + specify "== should compare keys with eql? semantics" do + { 1.0 => "x" }.should == { 1.0 => "x" } + { 1 => "x" }.should_not == { 1.0 => "x" } + { 1.0 => "x" }.should_not == { 1 => "x" } + end + + specify "== should first compare keys via hash" do + x = IntHash0.new false + y = IntHash0.new false + { x => 1 } == { y => 1 } + x.called_hash.should == true + y.called_hash.should == true + x.called_eql.should == true + y.called_eql.should == true + x.called_eq.should == false + y.called_eq.should == false + end + + specify "== shouldn't compare keys with different hash codes via eql?" do + x = IntHash0.new true + y = IntHash1.new true + { x => 1 }.should_not == { y => 1 } + x.called_hash.should == true + y.called_hash.should == true + x.called_eql.should == false + y.called_eql.should == false + x.called_eq.should == false + y.called_eq.should == false + end + + specify "== should compare keys with matching hash codes via eql?" do + a = Array.new(2) { IntHash0.new(false) } + + { a[0] => 1 }.should_not == { a[1] => 1 } + a[0].called_eql.should == true + a[1].called_eql.should == true + a[0].called_eq.should == false + a[1].called_eq.should == false + + a = Array.new(2) { IntHash0.new(true) } + + { a[0] => 1 }.should == { a[1] => 1 } + a[0].called_eql.should == true + a[1].called_eql.should == true + a[0].called_eq.should == false + a[1].called_eq.should == false + end + + specify "== should compare values with == semantics" do + { "x" => 1.0 }.should == { "x" => 1 } + end + + specify "== shouldn't compare values when keys don't match" do + value = EqlTest.new true + { 1 => value }.should_not == { 2 => value } + value.called_eql.should == false + value.called_eq.should == false + end + + specify "== should compare values when keys match" do + x = EqlTest.new false + y = EqlTest.new false + { 1 => x }.should_not == { 1 => y } + x.called_eq.should == true + y.called_eq.should == true + x.called_eql.should == false + y.called_eql.should == false + + x = EqlTest.new true + y = EqlTest.new true + { 1 => x }.should == { 1 => y } + x.called_eq.should == true + y.called_eq.should == true + x.called_eql.should == false + y.called_eql.should == false + end + + specify "== should ignore hash class differences" do + h = { 1 => 2, 3 => 4 } + MyHash[h].should == h + MyHash[h].should == MyHash[h] + h.should == MyHash[h] + end +end + +context "Hash#[] (instance method)" do + specify "[] should return the value for key" do + obj = Object.new + h = { 1 => 2, 3 => 4, "foo" => "bar", obj => obj } + h[1].should == 2 + h[3].should == 4 + h["foo"].should == "bar" + h[obj].should == obj + end + + specify "[] should return the default (immediate) value for missing keys" do + h = Hash.new(5) + h[:a].should == 5 + h[:a] = 0 + h[:a].should == 0 + h[:b].should == 5 + + # The default default is nil + { 0 => 0 }[5].should == nil + end + + specify "[] shouldn't create copies of the immediate default value" do + str = "foo" + h = Hash.new(str) + a = h[:a] + b = h[:b] + a << "bar" + + a.equal?(b).should == true + a.should == "foobar" + b.should == "foobar" + end + + specify "[] should return the default (dynamic) value for missing keys" do + h = Hash.new { |hash, k| k.kind_of?(Numeric) ? hash[k] = k + 2 : hash[k] = k } + h[1].should == 3 + h['this'].should == 'this' + h.should == {1 => 3, 'this' => 'this'} + + i = 0 + h = Hash.new { |hash, key| i += 1 } + h[:foo].should == 1 + h[:foo].should == 2 + h[:bar].should == 3 + end + + specify "[] shouldn't return default values for keys with nil values" do + h = Hash.new(5) + h[:a] = nil + h[:a].should == nil + + h = Hash.new() { 5 } + h[:a] = nil + h[:a].should == nil + end + + specify "[] should compare keys with eql? semantics" do + { 1.0 => "x" }[1].should == nil + { 1.0 => "x" }[1.0].should == "x" + { 1 => "x" }[1.0].should == nil + { 1 => "x" }[1].should == "x" + end + + specify "[] should compare key via hash" do + x = IntHash0.new true + { }[x].should == nil + x.called_hash.should == true + end + + specify "[] shouldn't compare key with unknown hash codes via eql?" do + x = IntHash0.new true + y = IntHash1.new true + { y => 1 }[x].should == nil + x.called_hash.should == true + x.called_eql.should == false + y.called_eql.should == false + end + + specify "[] should compare key with found hash code via eql?" do + x = IntHash0.new false + y = IntHash0.new false + { y => 1 }[x].should == nil + x.called_eql.should == true + + x = IntHash0.new true + + { y => 1 }[x].should == 1 + x.called_eql.should == true + end +end + +context "Hash#[]=" do + specify "[]= should associate the key with the value and return the value" do + h = { :a => 1 } + (h[:b] = 2).should == 2 + h.should == {:b=>2, :a=>1} + end + + specify "[]= should duplicate and freeze string keys" do + key = "foo" + h = {} + h[key] = 0 + key << "bar" + + h.should == { "foo" => 0 } + skip "TODO: implement freeze" do + h.keys[0].frozen?.should == true + end + end + + skip "TODO: []= should duplicate string keys using dup semantics" do + # dup doesn't copy singleton methods + key = "foo" + def key.reverse() "bar" end + h = {} + h[key] = 0 + + h.keys[0].reverse.should == "oof" + end +end + +context "Hash#clear" do + specify "clear should remove all key, value pairs" do + h = { 1 => 2, 3 => 4 } + h.clear.equal?(h).should == true + h.should == {} + end + + specify "clear shouldn't remove default values and procs" do + h = Hash.new(5) + h.clear + h.default.should == 5 + + h = Hash.new { 5 } + h.clear + h.default_proc.should_not == nil + end +end + +context "Hash#default" do + specify "default should return the default value" do + h = Hash.new(5) + h.default.should == 5 + h.default(4).should == 5 + {}.default.should == nil + {}.default(4).should == nil + end + + specify "default should use the default proc to compute a default value, passing given key" do + h = Hash.new { |*args| args } + h.default(nil).should == [h, nil] + h.default(5).should == [h, 5] + end + + skip "(Fails on Ruby 1.8.6) default with default proc, but no arg should call default proc with nil arg" do + h = Hash.new { |*args| args } + h.default.should == [h, nil] + end +end + +context "Hash#default=" do + specify "default= should set the default value" do + h = Hash.new + h.default = 99 + h.default.should == 99 + end + + specify "default= should unset the default proc" do + p = Proc.new { 6 } + [99, nil, p].each do |default| + h = Hash.new { 5 } + h.default_proc.should_not == nil + h.default = default + h.default.should == default + h.default_proc.should == nil + end + end +end + +context "Hash#default_proc" do + specify "default_proc should return the block passed to Hash.new" do + h = Hash.new { |i| 'Paris' } + p = h.default_proc + p.call(1).should == 'Paris' + end + + specify "default_proc should return nil if no block was passed to proc" do + Hash.new.default_proc.should == nil + end +end + +context "Hash#delete" do + it "delete should delete the item with the specified key" do + h = {:a => 5, :b => 2} + h.delete(:b).should == 2 + h.should == {:a => 5} + end + + skip "(BUG: Ruby's Hash respects an ordering constraint that we don't) delete should first entry (#keys order) whose key is == key and return the deleted value" do + # So they end up in the same bucket + class ArrayWithHash < Array + def hash() 0 end + end + + k1 = ArrayWithHash.new + k1[0] = "x" + k2 = ArrayWithHash.new + k2[0] = "y" + + h = Hash.new + h[k1] = 1 + h[k2] = 2 + k1.replace(k2) + + first_value = h.values.first + first_key = h.keys.first + h.delete(first_key).should == first_value + h.size.should == 1 + end + + specify "delete should call supplied block if the key is not found" do + d = {:a => 1, :b => 10, :c => 100 }.delete(:d) { 5 } + d.should == 5 + d = Hash.new(:default).delete(:d) { 5 } + d.should == 5 + h = Hash.new() { :default } + d = h.delete(:d) { 5 } + d.should == 5 + end + + specify "delete should return nil if the key is not found when no block is given" do + {:a => 1, :b => 10, :c => 100 }.delete(:d).should == nil + Hash.new(:default).delete(:d).should == nil + h = Hash.new() { :defualt } + h.delete(:d).should == nil + end +end + +context "Hash#delete_if" do + specify "delete_if should yield two arguments: key and value" do + all_args = [] + {1 => 2, 3 => 4}.delete_if { |*args| all_args << args } + all_args.should == [[1, 2], [3, 4]] + end + + specify "delete_if should remove every entry for which block is true and returns self" do + h = {:a => 1, :b => 2, :c => 3, :d => 4} + h2 = h.delete_if { |k,v| v % 2 == 1 } + h2.equal?(h).should == true + h.should == {:b => 2, :d => 4} + end + + specify "delete_if should process entries with the same order as each()" do + h = {:a => 1, :b => 2, :c => 3, :d => 4} + + each_pairs = [] + delete_pairs = [] + h.each { |pair| each_pairs << pair } + h.delete_if { |*pair| delete_pairs << pair } + + each_pairs.should == delete_pairs + end +end + +context "Hash#each" do + specify "each should yield one argument: [key, value]" do + all_args = [] + {1 => 2, 3 => 4}.each { |*args| all_args << args } + all_args.should == [[[1, 2]], [[3, 4]]] + end + + specify "each should call block once for each entry, passing key, value" do + r = {} + h = {:a => 1, :b => 2, :c => 3, :d => 5} + h2 = h.each { |p| r[p[0].to_s] = p[1].to_s } + h2.equal?(h).should == true + r.should == {"a" => "1", "b" => "2", "c" => "3", "d" => "5" } + end + + specify "each should use the same order as keys() and values()" do + h = {:a => 1, :b => 2, :c => 3, :d => 5} + keys = [] + values = [] + + h.each do |p| + keys << p[0] + values << p[1] + end + + keys.should == h.keys + values.should == h.values + end +end + +context "Hash#each_key" do + specify "each_key should call block once for each key, passing key" do + r = {} + h = {1 => -1, 2 => -2, 3 => -3, 4 => -4 } + h2 = h.each_key { |k| r[k] = k } + h2.equal?(h).should == true + r.should == { 1 => 1, 2 => 2, 3 => 3, 4 => 4 } + end + + specify "each_key should process keys in the same order as keys()" do + keys = [] + h = {1 => -1, 2 => -2, 3 => -3, 4 => -4 } + h.each_key { |k| keys << k } + keys.should == h.keys + end +end + +context "Hash#each_pair" do + specify "each_pair should process all pairs, yielding two arguments: key and value" do + all_args = [] + + h = {1 => 2, 3 => 4} + h2 = h.each_pair { |*args| all_args << args } + h2.equal?(h).should == true + + all_args.should == [[1, 2], [3, 4]] + end +end + +context "Hash#each_value" do + specify "each_value should call block once for each key, passing value" do + r = [] + h = { :a => -5, :b => -3, :c => -2, :d => -1, :e => -1 } + h2 = h.each_value { |v| r << v } + h2.equal?(h).should == true + r.sort.should == [-5, -3, -2, -1, -1] + end + + specify "each_value should process values in the same order as values()" do + values = [] + h = { :a => -5, :b => -3, :c => -2, :d => -1, :e => -1 } + h.each_value { |v| values << v } + values.should == h.values + end +end + +context "Hash#empty?" do + specify "empty? should return true if the hash has no entries" do + {}.empty?.should == true + {1 => 1}.empty?.should == false + Hash.new(5).empty?.should == true + h = Hash.new { 5 } + h.empty?.should == true + end +end + +context "Hash#fetch" do + specify "fetch should return the value for key" do + { :a => 1, :b => -1 }.fetch(:b).should == -1 + end + + specify "fetch should raise IndexError if key is not found" do + should_raise(IndexError) { {}.fetch(:a) } + should_raise(IndexError) { Hash.new(5).fetch(:a) } + should_raise(IndexError) do + h = Hash.new { 5 } + h.fetch(:a) + end + end + + specify "fetch with default should return default if key is not found" do + {}.fetch(:a, nil).should == nil + {}.fetch(:a, 'not here!').should == "not here!" + { :a => nil }.fetch(:a, 'not here!').should == nil + end + + specify "fetch with block should return value of block if key is not found" do + r = {}.fetch('a') { |k| k + '!' } + r.should == "a!" + end + + specify "fetch's default block should take precedence over its default argument" do + r = {}.fetch(9, :foo) { |i| i * i } + r.should == 81 + end +end + +context "Hash#has_key?" do + specify "has_key? should be a synonym for key?" do + h = {:a => 1, :b => 2, :c => 3} + h.has_key?(:a).should == h.key?(:a) + h.has_key?(:b).should == h.key?(:b) + h.has_key?('b').should == h.key?('b') + h.has_key?(2).should == h.key?(2) + end +end + +context "Hash#has_value?" do + specify "has_value? should be a synonym for value?" do + {:a => :b}.has_value?(:a).should == {:a => :b}.value?(:a) + {1 => 2}.has_value?(2).should == {1 => 2}.value?(2) + h = Hash.new(5) + h.has_value?(5).should == h.value?(5) + h = Hash.new { 5 } + h.has_value?(5).should == h.value?(5) + end +end + +context "Hash#include?" do + specify "include? should be a synonym for key?" do + h = {:a => 1, :b => 2, :c => 3} + h.include?(:a).should == h.key?(:a) + h.include?(:b).should == h.key?(:b) + h.include?('b').should == h.key?('b') + h.include?(2).should == h.key?(2) + end +end + +context "Hash#index" do + specify "index should return the corresponding key for value" do + {2 => 'a', 1 => 'b'}.index('b').should == 1 + end + + specify "index should return nil if the value is not found" do + {:a => -1, :b => 3.14, :c => 2.718}.index(1).should == nil + Hash.new(5).index(5).should == nil + end + + specify "index should compare values using ==" do + {1 => 0}.index(0.0).should == 1 + {1 => 0.0}.index(0).should == 1 + + needle = EqlTest.new true + inhash = EqlTest.new true + + {1 => inhash}.index(needle).should == 1 + needle.called_eql.should == false + inhash.called_eql.should == false + needle.called_eq.should == true + inhash.called_eq.should == true + end + + specify "index should compare values with same order as keys() and values()" do + h = {1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0} + h.index(0).should == h.keys.first + + h = {1 => Object.new, 3 => Object.new, 4 => Object.new, 42 => Object.new } + needle = h.values[2] + h.index(needle).should == h.keys[2] + end +end + +context "Hash#indexes, Hash#indices" do + specify "indexes and indices should be DEPRECATED synonyms for values_at" do + h = {:a => 9, :b => 'a', :c => -10, :d => nil} + h.indexes(:a, :d, :b).should == h.values_at(:a, :d, :b) + h.indexes().should == h.values_at() + h.indices(:a, :d, :b).should == h.values_at(:a, :d, :b) + h.indices().should == h.values_at() + end +end + +skip "(TODO, testing private method) Hash#initialize" do + specify "initialize should be private" do + #{}.private_methods.map { |m| m.to_s }.include?("initialize").should == true + end + + specify "initialize can be used to reset default_proc" do + h = { "foo" => 1, "bar" => 2 } + h.default_proc.should == nil + h.instance_eval { initialize { |h, k| k * 2 } } + h.default_proc.should_not == nil + h["a"].should == "aa" + end +end + +skip "(TODO, testing private method) Hash#initialize_copy" do + specify "initialize_copy should be private" do + #{}.private_methods.map { |m| m.to_s }.include?("initialize_copy").should == true + end + + specify "initialize_copy should be a synonym for replace" do + # TODO: entering try with non-empty stack bug + # init_hash = Hash.new + # repl_hash = Hash.new + # arg = { :a => 1, :b => 2 } + + # init_hash.instance_eval { initialize_copy(arg) }.should == repl_hash.replace(arg) + # init_hash.should == repl_hash + end + + specify "initialize_copy should have the same to_hash behaviour as replace" do + init_hash = Hash.new + repl_hash = Hash.new + arg1 = Object.new + def arg1.to_hash() {1 => 2} end + arg2 = ToHashHash[1 => 2] + + # TODO: entering try with non-empty stack bug + # [arg1, arg2].each do |arg| + # init_hash.instance_eval { initialize_copy(arg) }.should == repl_hash.replace(arg) + # init_hash.should == repl_hash + # end + end +end + +context "Hash#inspect" do + specify "inspect should return a string representation with same order as each()" do + h = {:a => [1, 2], :b => -2, :d => -6, nil => nil, "abc" => {7 => 8} } + + pairs = [] + h.each do |pair| + pairs << pair[0].inspect + "=>" + pair[1].inspect + end + + str = '{' + pairs.join(', ') + '}' + h.inspect.should == str + end + + specify "inspect should call inspect on keys and values" do + class InspectKey + def inspect() 'key' end + end + class InspectVal + def inspect() 'val' end + end + + key = InspectKey.new + val = InspectVal.new + + { key => val }.inspect.should == '{key=>val}' + end + + specify "inspect should handle recursive hashes" do + x = {} + x[0] = x + x.inspect.should == '{0=>{...}}' + + x = {} + x[x] = 0 + x.inspect.should == '{{...}=>0}' + + x = {} + x[x] = x + x.inspect.should == '{{...}=>{...}}' + + x = {} + y = {} + x[0] = y + y[1] = x + x.inspect.should == "{0=>{1=>{...}}}" + y.inspect.should == "{1=>{0=>{...}}}" + + x = {} + y = {} + x[y] = 0 + y[x] = 1 + x.inspect.should == "{{{...}=>1}=>0}" + y.inspect.should == "{{{...}=>0}=>1}" + + x = {} + y = {} + x[y] = x + y[x] = y + x.inspect.should == "{{{...}=>{...}}=>{...}}" + y.inspect.should == "{{{...}=>{...}}=>{...}}" + end +end + +context "Hash#invert" do + specify "invert should return a new hash where keys are values and vice versa" do + { 1 => 'a', 2 => 'b', 3 => 'c' }.invert.should == { 'a' => 1, 'b' => 2, 'c' => 3 } + end + + specify "invert should handle collisions by overriding with the key coming later in keys()" do + h = { :a => 1, :b => 1 } + override_key = h.keys.last + h.invert[1].should == override_key + end + + specify "invert should compare new keys with eql? semantics" do + h = { :a => 1.0, :b => 1 } + i = h.invert + i[1.0].should == :a + i[1].should == :b + end + + specify "invert on hash subclasses shouldn't return subclass instances" do + MyHash[1 => 2, 3 => 4].invert.class.should == Hash + MyHash[].invert.class.should == Hash + end +end + +context "Hash#key?" do + specify "key? should return true if argument is a key" do + h = { :a => 1, :b => 2, :c => 3, 4 => 0 } + h.key?(:a).should == true + h.key?(:b).should == true + h.key?('b').should == false + h.key?(2).should == false + h.key?(4).should == true + h.key?(4.0).should == false + end + + specify "key? should return true if the key's matching value was nil" do + { :xyz => nil }.key?(:xyz).should == true + end + + specify "key? should return true if the key's matching value was false" do + { :xyz => false }.key?(:xyz).should == true + end +end + +context "Hash#keys" do + specify "keys should return an array populated with keys" do + {}.keys.should == [] + {}.keys.class.should == Array + Hash.new(5).keys.should == [] + h = Hash.new { 5 } + h.keys.should == [] + { 1 => 2, 2 => 4, 4 => 8 }.keys.should == [1, 2, 4] + { 1 => 2, 2 => 4, 4 => 8 }.keys.class.should == Array + { nil => nil }.keys.should == [nil] + end + + specify "keys and values should use the same order" do + h = { 1 => "1", 2 => "2", 3 => "3", 4 => "4" } + + h.size.times do |i| + h[h.keys[i]].should == h.values[i] + end + end +end + +context "Hash#length" do + specify "length should return the number of entries" do + {:a => 1, :b => 'c'}.length.should == 2 + {:a => 1, :b => 2, :a => 2}.length.should == 2 + {:a => 1, :b => 1, :c => 1}.length.should == 3 + {}.length.should == 0 + Hash.new(5).length.should == 0 + h = Hash.new { 5 } + h.length.should == 0 + end +end + +context "Hash#member?" do + specify "member? should be a synonym for key?" do + h = {:a => 1, :b => 2, :c => 3} + h.member?(:a).should == h.key?(:a) + h.member?(:b).should == h.key?(:b) + h.member?('b').should == h.key?('b') + h.member?(2).should == h.key?(2) + end +end + +context "Hash#merge" do + specify "merge should return a new hash by combining self with the contents of other" do + { 1 => :a, 2 => :b, 3 => :c }.merge(:a => 1, :c => 2).should == { :c => 2, 1 => :a, 2 => :b, :a => 1, 3 => :c } + end + + specify "merge with block sets any duplicate key to the value of block" do + h1 = { :a => 2, :b => 1, :d => 5} + h2 = { :a => -2, :b => 4, :c => -3 } + r = h1.merge(h2) { |k,x,y| nil } + r.should == { :a => nil, :b => nil, :c => -3, :d => 5 } + + r = h1.merge(h2) { |k,x,y| "#{k}:#{x+2*y}" } + r.should == { :a => "a:-2", :b => "b:9", :c => -3, :d => 5 } + + should_raise(IndexError) do + h1.merge(h2) { |k, x, y| raise(IndexError) } + end + + r = h1.merge(h1) { |k,x,y| :x } + r.should == { :a => :x, :b => :x, :d => :x } + end + + specify "merge should call to_hash on its argument" do + obj = ToHash.new({1 => 2}) + {3 => 4}.merge(obj).should == {1 => 2, 3 => 4} + + obj = ToHashUsingMissing.new({1 => 2}) + + {3 => 4}.merge(obj).should == {1 => 2, 3 => 4} + end + + specify "merge shouldn't call to_hash on hash subclasses" do + {3 => 4}.merge(ToHashHash[1 => 2]).should == {1 => 2, 3 => 4} + end + + specify "merge on hash subclasses should return subclass instance" do + MyHash[1 => 2, 3 => 4].merge({1 => 2}).class.should == MyHash + MyHash[].merge({1 => 2}).class.should == MyHash + + {1 => 2, 3 => 4}.merge(MyHash[1 => 2]).class.should == Hash + {}.merge(MyHash[1 => 2]).class.should == Hash + end + + specify "merge should process entries with same order as each()" do + h = {1 => 2, 3 => 4, 5 => 6, "x" => nil, nil => 5, [] => []} + merge_pairs = [] + each_pairs = [] + h.each { |pair| each_pairs << pair } + h.merge(h) { |k, v1, v2| merge_pairs << [k, v1] } + merge_pairs.should == each_pairs + end +end + +context "Hash#merge!" do + specify "merge! should add the entries from other, overwriting duplicate keys. Returns self" do + h = { :_1 => 'a', :_2 => '3' } + h2 = h.merge!(:_1 => '9', :_9 => 2).equal?(h).should == true + h.should == {:_1 => "9", :_2 => "3", :_9 => 2} + end + + specify "merge! with block sets any duplicate key to the value of block" do + h1 = { :a => 2, :b => -1 } + h2 = { :a => -2, :c => 1 } + h3 = h1.merge!(h2) { |k,x,y| 3.14 } + h3.equal?(h1).should == true + h1.should == {:c => 1, :b => -1, :a => 3.14} + + h1.merge!(h1) { nil } + h1.should == { :a => nil, :b => nil, :c => nil } + end + + specify "merge! should call to_hash on its argument" do + obj = ToHash.new({1 => 2}) + {3 => 4}.merge!(obj).should == {1 => 2, 3 => 4} + + obj = ToHashUsingMissing.new({ 1 => 2 }) + {3 => 4}.merge!(obj).should == {1 => 2, 3 => 4} + end + + specify "merge! shouldn't call to_hash on hash subclasses" do + {3 => 4}.merge!(ToHashHash[1 => 2]).should == {1 => 2, 3 => 4} + end + + specify "merge! should process entries with same order as merge()" do + h = {1 => 2, 3 => 4, 5 => 6, "x" => nil, nil => 5, [] => []} + merge_bang_pairs = [] + merge_pairs = [] + h.merge(h) { |*arg| merge_pairs << arg } + h.merge!(h) { |*arg| merge_bang_pairs << arg } + merge_bang_pairs.should == merge_pairs + end +end + +context "Hash#rehash" do + specify "rehash should reorganize the hash by recomputing all key hash codes" do + k1 = [1] + k2 = [2] + h = {} + h[k1] = 0 + h[k2] = 1 + + k1 << 2 + + h.key?(k1).should == false + h.keys.include?(k1).should == true + + h.rehash.equal?(h).should == true + h.key?(k1).should == true + h[k1].should == 0 + + k1 = IntHash0.new false + k2 = IntHashX.new 1, false + v1 = NoHash.new + v2 = NoHash.new + + h = { k1 => v1, k2 => v2 } + k1.called_hash.should == true + k2.called_hash.should == true + k1.called_hash = false + k2.called_hash = false + + k2.newhash = 0 + + h.rehash + k1.called_hash.should == true + k2.called_hash.should == true + end + + specify "rehash gives precedence to keys coming later in keys() on collisions" do + k1 = [1] + k2 = [2] + h = {} + h[k1] = 0 + h[k2] = 1 + + k1.replace(k2) + override_val = h[h.keys.last] + h.rehash + h[k1].should == override_val + end + +end + +context "Hash#reject" do + specify "reject should be equivalent to hsh.dup.delete_if" do + h = { :a => 'a', :b => 'b', :c => 'd' } + h2 = h.reject { |k,v| k == 'd' } + h3 = h.dup.delete_if { |k, v| k == 'd' } + h2.should == h3 + + all_args_reject = [] + all_args_delete_if = [] + h = {1 => 2, 3 => 4} + h.reject { |*args| all_args_reject << args } + h.delete_if { |*args| all_args_delete_if << args } + all_args_reject.should == all_args_delete_if + + skip "TODO: singleton method support" do + h = { 1 => 2 } + # dup doesn't copy singleton methods + def h.to_a() end + h2 = h.reject { false } + h2.to_a.should == [[1, 2]] + end + end + + specify "reject on hash subclasses should return subclass instance" do + h = MyHash[1 => 2, 3 => 4].reject { false } + h.class.should == MyHash + h = MyHash[1 => 2, 3 => 4].reject { true } + h.class.should == MyHash + end + + specify "reject should process entries with the same order as reject!" do + h = {:a => 1, :b => 2, :c => 3, :d => 4} + + reject_pairs = [] + reject_bang_pairs = [] + h.reject { |*pair| reject_pairs << pair } + h.reject! { |*pair| reject_bang_pairs << pair } + + reject_pairs.should == reject_bang_pairs + end +end + +context "Hash#reject!" do + specify "reject! is equivalent to delete_if if changes are made" do + h = {:a => 2}.reject! { |k,v| v > 1 } + h2 = {:a => 2}.delete_if { |k, v| v > 1 } + h.should == h2 + + h = {1 => 2, 3 => 4} + all_args_reject = [] + all_args_delete_if = [] + h.dup.reject! { |*args| all_args_reject << args } + h.dup.delete_if { |*args| all_args_delete_if << args } + all_args_reject.should == all_args_delete_if + end + + specify "reject! should return nil if no changes were made" do + r = { :a => 1 }.reject! { |k,v| v > 1 } + r.should == nil + end + + specify "reject! should process entries with the same order as delete_if" do + h = {:a => 1, :b => 2, :c => 3, :d => 4} + + reject_bang_pairs = [] + delete_if_pairs = [] + h.dup.reject! { |*pair| reject_bang_pairs << pair } + h.dup.delete_if { |*pair| delete_if_pairs << pair } + + reject_bang_pairs.should == delete_if_pairs + end +end + +context "Hash#replace" do + specify "replace should replace the contents of self with other" do + h = { :a => 1, :b => 2 } + h.replace(:c => -1, :d => -2).equal?(h).should == true + h.should == { :c => -1, :d => -2 } + end + + specify "replace should call to_hash on its argument" do + obj = ToHash.new({1 => 2, 3 => 4}) + + h = {} + h.replace(obj) + h.should == {1 => 2, 3 => 4} + + obj = ToHashUsingMissing.new({}) + + h.replace(obj) + h.should == {} + end + + specify "replace shouldn't call to_hash on hash subclasses" do + h = {} + h.replace(ToHashHash[1 => 2]) + h.should == {1 => 2} + end + + specify "replace should transfer default values" do + hash_a = {} + hash_b = Hash.new(5) + hash_a.replace(hash_b) + hash_a.default.should == 5 + + hash_a = {} + hash_b = Hash.new { |h, k| k * 2 } + hash_a.replace(hash_b) + hash_a.default(5).should == 10 + + skip "TODO: lambda not implemented" do + hash_a = Hash.new { |h, k| k * 5 } + p = lambda { raise "Should not invoke lambda" } + hash_b = Hash.new(p) + hash_a.replace(hash_b) + hash_a.default.should == hash_b.default + end + end +end + +context "Hash#select" do + specify "select should yield two arguments: key and value" do + all_args = [] + {1 => 2, 3 => 4}.select { |*args| all_args << args } + all_args.should == [[1, 2], [3, 4]] + end + + specify "select should return an array of entries for which block is true" do + { :a => 9, :c => 4, :b => 5, :d => 2 }.select { |k,v| v % 2 == 0 } + end + + specify "select should process entries with the same order as reject" do + h = { :a => 9, :c => 4, :b => 5, :d => 2 } + + select_pairs = [] + reject_pairs = [] + h.select { |*pair| select_pairs << pair } + h.reject { |*pair| reject_pairs << pair } + + select_pairs.should == reject_pairs + end +end + +context "Hash#shift" do + specify "shift should remove a pair from hash and return it (same order as to_a)" do + hash = { :a => 1, :b => 2, "c" => 3, nil => 4, [] => 5 } + pairs = hash.to_a + + hash.size.times do + r = hash.shift + r.class.should == Array + r.should == pairs.shift + hash.size.should == pairs.size + end + + hash.should == {} + hash.shift.should == nil + end + + specify "shift should return (computed) default for empty hashes" do + Hash.new(5).shift.should == 5 + h = Hash.new { |*args| args } + h.shift.should == [h, nil] + end +end + +context "Hash#size" do + specify "size should be a synonym for length" do + { :a => 1, :b => 'c' }.size.should == {:a => 1, :b => 'c'}.length + {}.size.should == {}.length + end +end + +context "Hash#sort" do + specify "sort should convert self to a nested array of [key, value] arrays and sort with Array#sort" do + { 'a' => 'b', '1' => '2', 'b' => 'a' }.sort.should == [["1", "2"], ["a", "b"], ["b", "a"]] + end + + specify "sort should work when some of the keys are themselves arrays" do + { [1,2] => 5, [1,1] => 5 }.sort.should == [[[1,1],5], [[1,2],5]] + end + + specify "sort with block should use block to sort array" do + a = { 1 => 2, 2 => 9, 3 => 4 }.sort { |a,b| b <=> a } + a.should == [[3, 4], [2, 9], [1, 2]] + end +end + +context "Hash#store" do + specify "store should be a synonym for []=" do + h1, h2 = {:a => 1}, {:a => 1} + h1.store(:b, 2).should == (h2[:b] = 2) + h1.should == h2 + end +end + +context "Hash#to_a" do + specify "to_a should return a list of [key, value] pairs with same order as each()" do + h = {:a => 1, 1 => :a, 3 => :b, :b => 5} + pairs = [] + + h.each do |pair| + pairs << pair + end + + h.to_a.class.should == Array + h.to_a.should == pairs + end +end + +context "Hash#to_hash" do + specify "to_hash should return self" do + h = {} + h.to_hash.equal?(h).should == true + end +end + +context "Hash#to_s" do + specify "to_s should return a string by calling Hash#to_a and using Array#join with default separator" do + h = { :fun => 'x', 1 => 3, nil => "ok", [] => :y } + h.to_a.to_s.should == h.to_s + $, = ':' + h.to_a.to_s.should == h.to_s + end +end + +context "Hash#update" do + specify "update should be a synonym for merge!" do + h1 = { :_1 => 'a', :_2 => '3' } + h2 = h1.dup + + h1.update(:_1 => '9', :_9 => 2).should == h2.merge!(:_1 => '9', :_9 => 2) + h1.should == h2 + end + + specify "update with block should be a synonym for merge!" do + h1 = { :a => 2, :b => -1 } + h2 = h1.dup + + h3 = h1.update(:a => -2, :c => 1) { |k,v| 3.14 } + h4 = h2.merge!(:a => -2, :c => 1) { |k,v| 3.14 } + h3.should == h4 + h1.should == h2 + end + + specify "update should have the same to_hash behaviour as merge!" do + update_hash = Hash.new + merge_hash = Hash.new + + arg1 = ToHash.new({1 => 2}) + arg2 = ToHashHash[1 => 2] + + [arg1, arg2].each do |arg| + update_hash.update(arg).should == merge_hash.merge!(arg) + update_hash.should == merge_hash + end + end +end + +context "Hash#value?" do + specify "value? returns true if the value exists in the hash" do + {:a => :b}.value?(:a).should == false + {1 => 2}.value?(2).should == true + h = Hash.new(5) + h.value?(5).should == false + h = Hash.new { 5 } + h.value?(5).should == false + end + + specify "value? uses == semantics for comparing values" do + { 5 => 2.0 }.value?(2).should == true + end +end + +context "Hash#values" do + specify "values should return an array of values" do + h = { 1 => :a, 'a' => :a, 'the' => 'lang'} + h.values.class.should == Array + h2 = h.values.sort {|a, b| a.to_s <=> b.to_s} + h2.should == [:a, :a, 'lang'] + end +end + +context "Hash#values_at" do + specify "values_at should return an array of values for the given keys" do + h = {:a => 9, :b => 'a', :c => -10, :d => nil} + h.values_at().class.should == Array + h.values_at().should == [] + h.values_at(:a, :d, :b).class.should == Array + h.values_at(:a, :d, :b).should == [9, nil, 'a'] + end +end + +skip "BUG: we convert InvalidOperationException into TypeError, but it should be RuntimeError" do + # These are the only ones that actually have the exceptions on MRI 1.8. + # sort and reject don't raise! + %w( + delete_if each each_key each_pair each_value merge merge! reject! + select update + ).each do |cmd| + hash = {1 => 2, 3 => 4, 5 => 6} + big_hash = {} + 100.times { |k| big_hash[k.to_s] = k } + + specify "#{cmd} should raise if rehash() is called from block" do + h = hash.dup + args = cmd[/merge|update/] ? [h] : [] + + should_raise(RuntimeError, "rehash occurred during iteration") do + h.send(cmd, *args) { h.rehash } + end + end + + specify "#{cmd} should raise if lots of new entries are added from block" do + h = hash.dup + args = cmd[/merge|update/] ? [h] : [] + + should_raise(RuntimeError, "hash modified during iteration") do + h.send(cmd, *args) { |*x| h.merge!(big_hash) } + end + end + + skip "(Test failure on Ruby 1.8.6) #{cmd}'s yielded items shouldn't be affected by removing current element" do + n = 3 + + h = Array.new(n) { hash.dup } + args = Array.new(n) { |i| cmd[/merge|update/] ? [h[i]] : [] } + r = Array.new(n) { [] } + + h[0].send(cmd, *args[0]) { |*x| r[0] << x; true } + h[1].send(cmd, *args[1]) { |*x| r[1] << x; h[1].shift; true } + h[2].send(cmd, *args[2]) { |*x| r[2] << x; h[2].delete(h[2].keys.first); true } + + r[1..-1].each do |yielded| + yielded.should == r[0] + end + end + end +end + +skip "(TODO) On a frozen hash" do + empty = {} + empty.freeze + + hash = {1 => 2, 3 => 4} + hash.freeze + + specify "[]= should raise" do + should_raise(TypeError) { hash[1] = 2 } + end + + specify "clear should raise" do + should_raise(TypeError) { hash.clear } + should_raise(TypeError) { empty.clear } + end + + specify "default= should raise" do + should_raise(TypeError) { hash.default = nil } + end + + specify "delete should raise" do + should_raise(TypeError) { hash.delete("foo") } + should_raise(TypeError) { empty.delete("foo") } + end + + specify "delete_if should raise" do + should_raise(TypeError) { hash.delete_if { false } } + should_raise(TypeError) { empty.delete_if { true } } + end + + specify "initialize should raise" do + hash.instance_eval do + should_raise(TypeError) { initialize() } + should_raise(TypeError) { initialize(nil) } + should_raise(TypeError) { initialize(5) } + should_raise(TypeError) { initialize { 5 } } + end + end + + specify "merge! should raise" do + hash.merge!(empty) # ok, empty + should_raise(TypeError) { hash.merge!(1 => 2) } + end + + specify "rehash should raise" do + should_raise(TypeError) { hash.rehash } + should_raise(TypeError) { empty.rehash } + end + + specify "reject! should raise" do + should_raise(TypeError) { hash.reject! { false } } + should_raise(TypeError) { empty.reject! { true } } + end + + specify "replace should raise" do + hash.replace(hash) # ok, nothing changed + should_raise(TypeError) { hash.replace(empty) } + end + + specify "shift should raise" do + should_raise(TypeError) { hash.shift } + should_raise(TypeError) { empty.shift } + end + + specify "store should raise" do + should_raise(TypeError) { hash.store(1, 2) } + should_raise(TypeError) { empty.shift } + end + + specify "update should raise" do + hash.update(empty) # ok, empty + should_raise(TypeError) { hash.update(1 => 2) } + end +end + +finished diff --git a/merlin/main/languages/ruby/Tests/Builtin/Module/test_module.rb b/merlin/main/languages/ruby/Tests/Builtin/Module/test_module.rb new file mode 100644 index 0000000000..c540700b84 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Module/test_module.rb @@ -0,0 +1,227 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "../../util/simple_test.rb" + +describe "Module constant lookup" do + it "modules can access constants on Object" do + class Object + def test_const_lookup + Object + end + end + + # this should successfully find Object + x = module TestModule + test_const_lookup.name + end + x.should == "Object" + TestModule.test_const_lookup.should.equal? Object + end +end + +describe "Module#ancestors" do + it "ancestors on builtin types" do + Kernel.ancestors.should == [Kernel] + Object.ancestors.should == [Object, Kernel] + Class.ancestors.should == [Class, Module, Object, Kernel] + Enumerable.ancestors.should == [Enumerable] + Comparable.ancestors.should == [Comparable] + Fixnum.ancestors.should == [Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel] + Array.ancestors.should == [Array, Enumerable, Object, Kernel] + Hash.ancestors.should == [Hash, Enumerable, Object, Kernel] + String.ancestors.should == [String, Enumerable, Comparable, Object, Kernel] + end + + it "ancestors on user types" do + # TODO: modules including other modules + # (doesn't work today) + module AncestorModule1; end + module AncestorModule2; end + AncestorModule1.ancestors.should == [AncestorModule1] + AncestorModule2.ancestors.should == [AncestorModule2] + + class AncestorsClass1; end + AncestorsClass1.ancestors.should == [AncestorsClass1, Object, Kernel] + + class AncestorsClass1 + include AncestorModule1 + end + AncestorsClass1.ancestors.should == [AncestorsClass1, AncestorModule1, Object, Kernel] + + class AncestorsClass2 < AncestorsClass1 + include AncestorModule2 + end + AncestorsClass2.ancestors.should == [AncestorsClass2, AncestorModule2, AncestorsClass1, AncestorModule1, Object, Kernel] + end +end + +describe "Module#module_eval" do + it "module_eval allows defining methods on a module" do + module TestModuleEval + def bar; "hello"; end + end + x = TestModuleEval.module_eval do + def foo; bar; end + self + end + x.name.should == "TestModuleEval" + should_raise(NoMethodError) { TestModuleEval.bar } + should_raise(NoMethodError) { TestModuleEval.foo } + + class TestModuleEval2 + include TestModuleEval + def baz; foo; end + end + t = TestModuleEval2.new + t.foo.should == "hello" + t.baz.should == "hello" + + # try module method, constant + module TestModuleEval + def self.m1; "m1"; end + CONST1 = "const" + end + x = TestModuleEval.module_eval do + def self.m2; self.m1.upcase; end + self::CONST2 = TestModuleEval::CONST1.upcase + end + x.should == "CONST" + TestModuleEval::CONST2.should == "CONST" + TestModuleEval.m2.should == "M1" + + # empty block + x = TestModuleEval.module_eval {} + x.should == nil + end + + it "module_eval with no block should raise an error" do + should_raise(ArgumentError, 'block not supplied') { Enumerable::module_eval } + end + + it "module_eval allows defining methods on a class" do + class TestModuleEvalClass + def bar; "hello"; end + def self.m1; 'm1'; end + end + x = TestModuleEvalClass.module_eval do + def foo; bar; end + def TestModuleEvalClass.m2; self.m1.upcase; end + self + end + x.should.equal? TestModuleEvalClass + should_raise(NoMethodError) { TestModuleEvalClass.bar } + should_raise(NoMethodError) { TestModuleEvalClass.foo } + t = TestModuleEvalClass.new + t.foo.should == "hello" + TestModuleEvalClass.m2.should == "M1" + end + + it "module_eval and break" do + x = TestModuleEval + y = x.module_eval { break 123 } + y.should == 123 + def foo(&blk) + TestModuleEval.module_eval(&blk) + end + y = foo { break 456 } + y.should == 456 + end + + it "module_eval and next" do + x = TestModuleEval + y = x.module_eval { next 123 } + y.should == 123 + y = foo { next 456 } + y.should == 456 + end + + it "module_eval and return" do + x = TestModuleEval + should_raise(LocalJumpError) { x.module_eval { return 'test0' } } + def bar + TestModuleEval.module_eval { return 'test1' } + end + bar.should == 'test1' + def bar + foo { return 'test2' } + end + bar.should == 'test2' + end + + it "module_eval and redo" do + x = TestModuleEval + again = true + y = x.module_eval do + if again + again = false + redo + else + 3.14 + end + end + y.should == 3.14 + z = foo do + if y < 9 + y *= 2 + redo + end + y + end + y.should == 12.56 + z.should == 12.56 + end + + it "module_eval and retry" do + x = TestModuleEval + total = 0 + y = x.module_eval do + total += 1 + if total < 8; retry; end + total + end + y.should == 8 + total.should == 8 + y = foo do + total *= 2 + if total < 1000; retry; end + total + end + y.should == 1024 + total.should == 1024 + end +end + +describe "Module#class_eval" do + it "class_eval is an alias for module_eval" do + class TestClassEval + def bar2; "hello"; end + end + x = TestClassEval.class_eval do + def foo2; bar2; end + self + end + x.should.equal? TestClassEval + should_raise(NoMethodError) { TestClassEval.bar2 } + should_raise(NoMethodError) { TestClassEval.foo2 } + t = TestClassEval.new + t.bar2.should == 'hello' + t.foo2.should == 'hello' + + should_raise(ArgumentError, 'block not supplied') { Enumerable::class_eval } + end +end + +finished diff --git a/merlin/main/languages/ruby/Tests/Builtin/Object/test_objects.rb b/merlin/main/languages/ruby/Tests/Builtin/Object/test_objects.rb new file mode 100644 index 0000000000..e62310d91a --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Object/test_objects.rb @@ -0,0 +1,526 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "../../Util/simple_test.rb" + +describe "inspect object attributes" do + it "asserts object identity for built-in types" do + 3.object_id.should == 3.object_id + 3.object_id.should_not == 4.object_id + + a, b = [], [] + a.object_id.should_not == b.object_id + end + + it "tests for well-known object_id values" do + nil.object_id.should == 4 + true.object_id.should == 2 + false.object_id.should == 0 + 0.object_id.should == 1 + 1.object_id.should == 3 + 2.object_id.should == 5 + -1.object_id.should == -1 + -2.object_id.should == -3 + end + + it "tests for nil objects" do + 3.nil?.should == false + nil.nil?.should == true + [].nil?.should == false + end + + it "converts well-known object to strings" do + true.to_s.should == "true" + false.to_s.should == "false" + nil.to_s.should == "" + end +end + +# TODO: must implement is_a? +#test "kind_of? and is_a? tests on well-known types" do +# assert 3.is_a? Fixnum +# assert 3.is_a? Integer +# assert 3.is_a? Numeric +# assert 3.is_a? Object +#end + +describe "Object#send" do + it "test basic functionality" do + # simple stuff + nil.send(:inspect).should == nil.inspect + nil.send("inspect").should == "nil" + 5.send("==", (2 + 3)).should == true + + # test block support + a, b = [1,2,3,4], [] + a.send(:each) { |e| b << e } + b.should == [1,2,3,4] + + # class method + String.send('new', 'abc').should == 'abc' + Array.send("[]", 1, 2, 3).should == [1, 2, 3] + end + + it "test error messages" do + should_raise(ArgumentError, "no method name given") { 123.send } + should_raise(ArgumentError, "no method name given") { 123.send {} } + should_raise(TypeError, "[] is not a symbol") { 123.send [] } + should_raise(TypeError, "[] is not a symbol") { 123.send([]) {} } + + #TODO: fix error message + should_raise(NoMethodError) { 123.send "Abc" } + skip "TODO: our error message for undefined methods isn't quite right" do + should_raise(NoMethodError, "undefined method `Abc' for 123:Fixnum") { 123.send "Abc" } + end + end + + it "deals with module method" do + 2.send(">", 4).should == false + 2.send("between?", 1, 3).should == true + + should_raise(RuntimeError, 'the error message') { Kernel.send('raise', 'the error message') } + end + + it "gives errors if incorrect arguments given" do + # should_raise(LocalJumpError, "no block given") { 'abc'.send 'each' } + + # TODO: expected message - "wrong number of arguments (1 for 0)" + should_raise(ArgumentError) { 'abc'.send :length, 3 } + end + + it "test argument splatting" do + # basic tests + [4,5,6].send(*[:to_s]).should == "456" + 123.send(*["+", 5]).should == 128 + Hash.send("[]",1,2,3,4).should == { 1=>2, 3=>4 } + Hash.send(*["[]",1,2,3,4]).should == { 1=>2, 3=>4 } + + Hash.send ("[]",*[1,2,3,4]).should == { 1=>2, 3=>4 } + "abc".send(:<<, *["def"]).should == "abcdef" + "abcdef".send("index", *["cd"]).should == 2 + + # negative tests + should_raise(TypeError, "[:inspect] is not a symbol") { 123.send([:inspect]) } + end + + it "test dynamic site polymorphism" do + # test the dynamic site to make sure it is truly polymorphic + # mix some errors in as well + def foo x, y, z; x.send y, z; end + foo(1, :+, 2).should == 3 + foo(1, "-", 2).should == -1 + should_raise(TypeError, "[:+] is not a symbol") { foo(1,[:+],2) } + foo("abc", :<<, "def").should == "abcdef" + foo([1,2], :+, [3,4]).should == [1,2,3,4] + should_raise(TypeError, "[:+] is not a symbol") { foo(1,[:+],2) } + + def foo2 x, *y; x.send *y; end + foo2(1, :+, 2).should == 3 + should_raise(ArgumentError, "no method name given") { foo2(1) } + foo2(1, *["-", 2]).should == -1 + should_raise(ArgumentError, "no method name given") { foo2(1, *[]) } + foo2("abc", *[:<<, "def"]).should == "abcdef" + should_raise(ArgumentError, "no method name given") { foo2(1) } + end + + it "test overriding send works" do + class TypeOverridingSend1 + def send x; x; end + def inspect; "#"; end + end + + a = TypeOverridingSend1.new + a.send("test2").should == "test2" + # TODO: error message is wrong; just test that the right exception is thrown + should_raise(NoMethodError) { a.__send__("test2") } + skip "TODO: our error message is wrong; it's not calling inspect" do + should_raise(NoMethodError, "undefined method `test2' for #") { a.__send__("test2") } + end + a.__send__(:send, "test2") == "test2" + # just because method bind failed we shouldn't try to fall back to Object#send + should_raise(ArgumentError, "wrong number of arguments (2 for 1)") { a.send("==", a) } + end + + it "test recursive send works" do + x = [1,2,3] + y = "[1, 2, 3]" + x.send(:send, :__send__, "send", "__send__", :inspect).should == y + # This is put at 20 instead of a bigger number because we it's a slow test + # (our unsplatting logic for the recusive send w/ splatting is O(N^2) with the + # number of arguments... however, this is a *highly* unlikely scenario in real code) + [:send, :__send__, "send", "__send__"].each do |s| + 20.times do |i| + args = Array.new(i, s) << :inspect + x.send(*args).should == y + end + end + end + + it "passing an object derived from String to send should work" do + # you can derive a type from String, and it works, based on the string though + # (none of these overloads will get called) + class MyString < String + def to_str; "hash"; end + def to_s; "hash"; end + def inspect; "hash"; end + def to_sym; :hash; end + end + + s = MyString.new + s << "inspect" + # Note: this should call inspect, not hash + [1,2,3].send(s).should == "[1, 2, 3]" + end + + # TODO: fix this once super works + skip "TODO: test that super works in overriden send" do + class TypeOverridingSend2 + # TODO: uncomment once super AST transformation works + #def send x, y; super(x, y); end + def inspect; "#"; end + end + + a = TypeOverridingSend2.new + a.send(:==, a).should == true + a.send(:==, "123").should == false + end + +end + +describe "Object#__send__" do + it "__send__ should work like send" do + # simple stuff + nil.__send__(:inspect).should == nil.inspect + nil.__send__("inspect").should == "nil" + 5.__send__("==", (2 + 3)).should == true + + def foo x, y, z; x.__send__ y, z; end + foo(1, :+, 2).should == 3 + foo(1, "-", 2).should == -1 + foo("abc", :<<, "def").should == "abcdef" + foo([1,2], :+, [3,4]).should == [1,2,3,4] + end + + it "__send__ can be overriden" do + class TypeSendUnderbar1 + def __send__ x; x; end + def inspect; "#"; end + end + + a = TypeSendUnderbar1.new + a.__send__("test2").should == "test2" + # TODO: error message is wrong; just test that the right exception is thrown + should_raise(NoMethodError) { a.send("test2") } + skip "TODO: our error message is wrong; it's not calling inspect" do + should_raise(NoMethodError, "undefined method `test2' for #") { a.send("test2") } + end + a.send(:__send__, "test2") == "test2" + # just because method bind failed we shouldn't try to fall back to Object#__send__ + should_raise(ArgumentError, "wrong number of arguments (2 for 1)") { a.__send__("==", a) } + end +end + +describe "Object#dup" do + it "dup should copy instance variables" do + class TestVars + def x= v; @x=v; end + def x; @x; end + def y= v; @y=v; end + def y; @y; end + def z= v; @z=v; end + def z; @z; end + end + a = TestVars.new + a.x = "a.x" + a.y = "a.y" + b = a.dup + [a.x, a.y, a.z].should == ["a.x", "a.y", nil] + [b.x, b.y, b.z].should == ["a.x", "a.y", nil] + a.x << "-test" + b.z = "b.z" + [a.x, a.y, a.z].should == ["a.x-test", "a.y", nil] + [b.x, b.y, b.z].should == ["a.x-test", "a.y", "b.z"] + end + + it "dup should copy tainted state" do + x = Object.new + x.tainted?.should == false + x.taint + x.tainted?.should == true + y = x.dup + y.tainted?.should == true + y.untaint + y.tainted?.should == false + x.tainted?.should == true + end + + it "dup should not copy frozen state" do + x = Object.new + x.frozen?.should == false + x.freeze + x.frozen?.should == true + x.dup.frozen?.should == false + x.frozen?.should == true + end + + it "dup on builtin class (reference type)" do + x = "test" + foo = "foo" + x.instance_variable_set(:@foo, foo) + x.instance_variable_set(:@bar, "bar") + y = x.dup + y.should == x + y.should == "test" + y.instance_variable_get(:@foo).should == "foo" + y.instance_variable_get(:@bar).should == "bar" + foo << "bar" + y.instance_variable_get(:@foo).should == "foobar" + x.instance_variable_get(:@foo).should == "foobar" + y.instance_variables.sort.should == ["@bar", "@foo"] + end + + it "dup on builtin value type raises an error" do + should_raise(TypeError, "can't dup NilClass") { nil.dup } + should_raise(TypeError, "can't dup Fixnum") { 123.dup } + should_raise(TypeError, "can't dup Symbol") { :abc.dup } + end +end + +describe "Object#clone" do + it "clone should copy instance variables" do + a = TestVars.new + a.x = "a.x" + a.y = "a.y" + b = a.clone + [a.x, a.y, a.z].should == ["a.x", "a.y", nil] + [b.x, b.y, b.z].should == ["a.x", "a.y", nil] + a.x << "-test" + b.z = "b.z" + [a.x, a.y, a.z].should == ["a.x-test", "a.y", nil] + [b.x, b.y, b.z].should == ["a.x-test", "a.y", "b.z"] + end + + it "clone should copy tainted state" do + x = Object.new + x.tainted?.should == false + x.taint + x.tainted?.should == true + y = x.clone + y.tainted?.should == true + y.untaint + y.tainted?.should == false + x.tainted?.should == true + end + + it "clone should copy frozen state" do + x = Object.new + x.frozen?.should == false + x.clone.frozen?.should == false + x.freeze + x.frozen?.should == true + x.clone.frozen?.should == true + end + + it "clone on builtin class (reference type)" do + x = "test" + foo = "foo" + x.instance_variable_set(:@foo, foo) + x.instance_variable_set(:@bar, "bar") + y = x.clone + y.should == x + y.should == "test" + y.instance_variable_get(:@foo).should == "foo" + y.instance_variable_get(:@bar).should == "bar" + foo << "bar" + y.instance_variable_get(:@foo).should == "foobar" + x.instance_variable_get(:@foo).should == "foobar" + y.instance_variables.sort.should == ["@bar", "@foo"] + end + + it "clone on builtin value type raises an error" do + should_raise(TypeError, "can't clone NilClass") { nil.clone } + should_raise(TypeError, "can't clone Fixnum") { 123.clone } + should_raise(TypeError, "can't clone Symbol") { :abc.clone } + end +end + +describe "Object#instance_eval" do + it "instance_eval should change the self" do + test_objects = [Object.new, {:key=>:value}, [1,2,3], "hello"] + test_objects.each do |e| + e.instance_eval { self.should.equal? e } + end + end + + it 'repeat the previous' do + class C; end + x = C.new + x.instance_eval { def f; 12; end } + x.f.should == 12 + + #y = C.new + #should_raise(NoMethodError) { y.f } + + # TODO: add C.instance_eval scenario later + end + + it "instance_eval allows accessing instance variables" do + class TestEval + @@class_var = "xyz" + def initialize + @abc = "def" + end + def bar + @abc = "zzz" + @@class_var = "yyy" + end + def baz + 123 + end + def TestEval.ClassVar + @@class_var + end + end + t = TestEval.new + x = t.instance_eval { @abc } + x.should == "def" + x = t.instance_eval { bar; baz } + x.should == 123 + + t.instance_variable_get(:@abc).should == "zzz" + TestEval.ClassVar.should == "yyy" + end + + it "return value" do + x = 'abc'.instance_eval { length } + x.should == 3 + + x = 'abc'.instance_eval { } + x.should == nil + end + +=begin + # IronRuby has a differnet error message + it "instance_eval with no block should raise an error" do + should_raise(ArgumentError, 'block not supplied') { instance_eval } + end +=end + + it "instance_eval and break" do + x = Object.new + y = x.instance_eval { break 123 } + y.should == 123 + def foo(&blk) + instance_eval(&blk) + end + y = foo { break 456 } + y.should == 456 + end + + it "instance_eval and next" do + x = Object.new + y = x.instance_eval { next 123 } + y.should == 123 + y = foo { next 456 } + y.should == 456 + end + + it "instance_eval and return" do + x = Object.new + should_raise(LocalJumpError) { x.instance_eval { return 'test0' } } + def bar + instance_eval { return 'test1' } + end + bar.should == 'test1' + def bar + foo { return 'test2' } + end + bar.should == 'test2' + end + + it "instance_eval and redo" do + x = Object.new + again = true + y = x.instance_eval do + if again + again = false + redo + else + 3.14 + end + end + y.should == 3.14 + z = foo do + if y < 9 + y *= 2 + redo + end + y + end + y.should == 12.56 + z.should == 12.56 + end + + it "instance_eval and retry" do + x = Object.new + total = 0 + y = x.instance_eval do + total += 1 + if total < 8; retry; end + total + end + y.should == 8 + total.should == 8 + y = foo do + total *= 2 + if total < 1000; retry; end + total + end + y.should == 1024 + total.should == 1024 + end + + it "instance_eval and exception" do + x = Object.new + begin + x.instance_eval { 1/0 } + rescue ZeroDivisionError + else + raise("ZeroDivisionError should be thrown and caught") + end + + x.instance_eval do + begin + 1/0 + rescue ZeroDivisionError + end + end + end + + skip "acts on module/class too" do + y = Kernel.instance_eval do + x = self.lambda { 2 } + x.call + end + y.should == 2 + + y = Hash.instance_eval { new(99) } + y.should == {} + y[0].should == 99 + end + +end + +finished diff --git a/merlin/main/languages/ruby/Tests/Builtin/Range/test_range.rb b/merlin/main/languages/ruby/Tests/Builtin/Range/test_range.rb new file mode 100644 index 0000000000..221a76f574 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/Range/test_range.rb @@ -0,0 +1,87 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "../../Util/simple_test.rb" + +describe "range creation" do + + it "creates an inclusive range object from integer literals" do + a = 0..1 + a.class.name.should == "Range" + a.begin.should == 0 + a.end.should == 1 + a.exclude_end?.should == false + end + + it "creates an inclusive range object from integer expressions" do + x = 0 + y = 1 + a = x..y + 1 + a.class.name.should == "Range" + a.begin.should == 0 + a.end.should == 2 + a.exclude_end?.should == false + end + + it "creates an exclusive range object from integer literals" do + a = 0...2 + a.class.name.should == "Range" + a.begin.should == 0 + a.end.should == 2 + a.exclude_end?.should == true + end + + it "creates an exclusive range object from integer expressions" do + x = 0 + y = 1 + a = x...y + 1 + a.class.name.should == "Range" + a.begin.should == 0 + a.end.should == 2 + a.exclude_end?.should == true + end + + it "explicit creation" do + should_raise(ArgumentError) { Range.new } + should_raise(ArgumentError) { Range.new 1,2,true,true } + Range.new(5, 10).should == (5..10) + Range.new('a', 'z', false).should == ('a'..'z') + Range.new('a', 'z', true).should == ('a'...'z') + Range.new('a', 'z', Object.new).should == ('a'...'z') + end + + it "creation of a derived type" do + class MyRange < Range; end + should_raise(ArgumentError) { MyRange.new } + should_raise(ArgumentError) { MyRange.new 1,2,true,true } + MyRange.new(5, 10).inspect.should == (5..10).inspect + MyRange.new('a', 'z', false).inspect.should == ('a'..'z').inspect + MyRange.new('a', 'z', true).inspect.should == ('a'...'z').inspect + MyRange.new('a', 'z', Object.new).inspect.should == ('a'...'z').inspect + end +end + +describe "inspecting range objects" do + it "tests for range equality in inclusive and exclusive ranges" do + (0..1).should == (0..1) + (0..1).should == (0..1) + (0...1).should == (0...1) + (0..1).should_not == (0...1) + (0..1).should_not == (0..2) + (-1..1).should_not == (0..1) + end +end + +finished diff --git a/merlin/main/languages/ruby/Tests/Builtin/String/test_string.rb b/merlin/main/languages/ruby/Tests/Builtin/String/test_string.rb new file mode 100644 index 0000000000..a209a9dab7 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/String/test_string.rb @@ -0,0 +1,1395 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "../../Util/simple_test.rb" + +describe "String behavior in string expressions" do + it "calls to_s on the result of an expression" do + class Bob701 + def to_s + "bob" + end + end + "#{Bob701.new}".should == "bob" + end +end + +describe "String#<=>(other_string)" do + skip "compares individual characters based on their ascii value" do + ascii_order = Array.new(256) { |x| x.chr } + sort_order = ascii_order.sort + sort_order.should == ascii_order + end + + it "returns -1 when self is less than other" do + ("this" <=> "those").should == -1 + end + + it "returns 0 when self is equal to other" do + ("yep" <=> "yep").should == 0 + end + + it "returns 1 when self is greater than other" do + ("yoddle" <=> "griddle").should == 1 + end + + it "considers string that comes lexicographically first to be less if strings have same size" do + ("aba" <=> "abc").should == -1 + ("abc" <=> "aba").should == 1 + end + + it "doesn't consider shorter string to be less if longer string starts with shorter one" do + ("abc" <=> "abcd").should == -1 + ("abcd" <=> "abc").should == 1 + end + + it "compares shorter string with corresponding number of first chars of longer string" do + ("abx" <=> "abcd").should == 1 + ("abcd" <=> "abx").should == -1 + end + + skip "ignores subclass differences" do + a = "hello" + b = MyString.new("hello") + + (a <=> b).should == 0 + (b <=> a).should == 0 + end +end + +describe "String#<=>(obj)" do + it "returns nil if its argument does not respond to to_str" do + ("abc" <=> 1).should == nil + ("abc" <=> :abc).should == nil + ("abc" <=> Object.new).should == nil + end + + # I believe that this behavior is a bug in Ruby + skip "returns nil if its argument does not respond to <=>" do + obj = Object.new + def obj.to_str() "" end + + ("abc" <=> obj).should == nil + end + + # Similarly, I believe that this behavior is a bug in Ruby -- the semantics of + # to_str should convert obj into a String + skip "compares its argument and self by calling <=> on obj and turning the result around" do + obj = Object.new + def obj.to_str() "" end + def obj.<=>(arg) 1 end + + ("abc" <=> obj).should == -1 + ("xyz" <=> obj).should == -1 + end +end + +describe "String#[idx]" do + it "returns the character code of the character at fixnum" do + "hello"[0].should == ?h + "hello"[-1].should == ?o + end + + it "returns nil if idx is outside of self" do + "hello"[20].should == nil + "hello"[-20].should == nil + + ""[0].should == nil + ""[-1].should == nil + end + + it "calls to_int on idx" do + "hello"[0.5].should == ?h + + # Too fancy for me right now + #obj = Object.new + #obj.should_receive(:to_int, :returning => 1) + + class Bob1 + def to_int + 1 + end + end + "hello"[Bob1.new].should == ?e + end +end + +describe "String#[idx, length]" do + it "returns the substring starting at idx and the given length" do + "hello there"[0,0].should == "" + "hello there"[0,1].should == "h" + "hello there"[0,3].should == "hel" + "hello there"[0,6].should == "hello " + "hello there"[0,9].should == "hello the" + "hello there"[0,12].should == "hello there" + + "hello there"[1,0].should == "" + "hello there"[1,1].should == "e" + "hello there"[1,3].should == "ell" + "hello there"[1,6].should == "ello t" + "hello there"[1,9].should == "ello ther" + "hello there"[1,12].should == "ello there" + + "hello there"[3,0].should == "" + "hello there"[3,1].should == "l" + "hello there"[3,3].should == "lo " + "hello there"[3,6].should == "lo the" + "hello there"[3,9].should == "lo there" + + "hello there"[4,0].should == "" + "hello there"[4,3].should == "o t" + "hello there"[4,6].should == "o ther" + "hello there"[4,9].should == "o there" + + "foo"[2,1].should == "o" + "foo"[3,0].should == "" + "foo"[3,1].should == "" + + ""[0,0].should == "" + ""[0,1].should == "" + + "x"[0,0].should == "" + "x"[0,1].should == "x" + "x"[1,0].should == "" + "x"[1,1].should == "" + + "x"[-1,0].should == "" + "x"[-1,1].should == "x" + + "hello there"[-3,2].should == "er" + end + + it "returns nil if the offset falls outside of self" do + "hello there"[20,3].should == nil + "hello there"[-20,3].should == nil + + ""[1,0].should == nil + ""[1,1].should == nil + + ""[-1,0].should == nil + ""[-1,1].should == nil + + "x"[2,0].should == nil + "x"[2,1].should == nil + + "x"[-2,0].should == nil + "x"[-2,1].should == nil + end + + it "returns nil if the length is negative" do + "hello there"[4,-3].should == nil + "hello there"[-4,-3].should == nil + end + + it "converts non-integer numbers to integers" do + "hello"[0.5, 1].should == "h" + "hello"[0.5, 2.5].should == "he" + "hello"[1, 2.5].should == "el" + end + + it "calls to_int on idx and length" do + # Too fancy for me right now + #obj = Object.new + #obj.should_receive(:to_int, :count => 4, :returning => 2) + + class Bob2 + def to_int + 2 + end + end + + obj = Bob2.new + + "hello"[obj, 1].should == "l" + "hello"[obj, obj].should == "ll" + "hello"[0, obj].should == "he" + end + + skip "returns subclass instances" do + s = MyString.new("hello") + s[0,0].class.should == MyString + s[0,4].class.should == MyString + s[1,4].class.should == MyString + end +end + +describe "String#[range]" do + it "returns the substring given by the offsets of the range" do + "hello there"[1..1].should == "e" + "hello there"[1..3].should == "ell" + "hello there"[1...3].should == "el" + "hello there"[-4..-2].should == "her" + "hello there"[-4...-2].should == "he" + "hello there"[5..-1].should == " there" + "hello there"[5...-1].should == " ther" + + ""[0..0].should == "" + + "x"[0..0].should == "x" + "x"[0..1].should == "x" + "x"[0...1].should == "x" + "x"[0..-1].should == "x" + + "x"[1..1].should == "" + "x"[1..-1].should == "" + end + + it "returns nil if the beginning of the range falls outside of self" do + "hello there"[12..-1].should == nil + "hello there"[20..25].should == nil + "hello there"[20..1].should == nil + "hello there"[-20..1].should == nil + "hello there"[-20..-1].should == nil + + ""[-1..-1].should == nil + ""[-1...-1].should == nil + ""[-1..0].should == nil + ""[-1...0].should == nil + end + + it "returns an empty string if the number of characters returned from the range (end - begin + ?1) is < 0" do + "hello there"[1...1].should == "" + "hello there"[4..2].should == "" + "hello"[4..-4].should == "" + "hello there"[-5..-6].should == "" + "hello there"[-2..-4].should == "" + "hello there"[-5..-6].should == "" + "hello there"[-5..2].should == "" + + ""[0...0].should == "" + ""[0..-1].should == "" + ""[0...-1].should == "" + + "x"[0...0].should == "" + "x"[0...-1].should == "" + "x"[1...1].should == "" + "x"[1...-1].should == "" + end + + skip "returns subclass instances" do + s = MyString.new("hello") + s[0...0].class.should == MyString + s[0..4].class.should == MyString + s[1..4].class.should == MyString + end + + skip "calls to_int on range arguments" do + from = Object.new + to = Object.new + + # So we can construct a range out of them... + def from.<=>(o) 0 end + def to.<=>(o) 0 end + + def from.to_int() 1 end + def to.to_int() -2 end + + "hello there"[from..to].should == "ello ther" + "hello there"[from...to].should == "ello the" + end +end + +# There's some serious bug here -- these tests pass just fine if run in +# isolation but when run at this point in this test case will raise an error: +# Cannot convert ZZZ to Integer -- as if we are calling to_int on the type +# rather than treating it as a regex - some kind of bug with dynamic sites? + +skip "String#[regexp]" do + it "returns the matching portion of self" do + "hello there"[/[aeiou](.)\1/].should == "ell" + ""[//].should == "" + end + + it "returns nil if there is no match" do + "hello there"[/xyz/].should == nil + end + + skip "returns subclass instances" do + s = MyString.new("hello") + s[//].class.should == MyString + s[/../].class.should == MyString + end +end + +skip "String#[regexp, idx]" do + it "returns the capture for idx" do + "hello there"[/[aeiou](.)/,0].should == "el" + "hello there"[/[aeiou](.)/,1].should == "o " + "hello there"[/[aeiou](.)/,2].should == "er" + + # TODO: .NET regex semantics do not work with any but the first of the + # expressions below! I need to rewrite this test case to focus on + # this method and not regular expressions themselves. + #"hello there"[/[aeiou](.)\1/, 0].should == "ell" + #"hello there"[/[aeiou](.)\1/, 1].should == "l" + #"hello there"[/[aeiou](.)\1/, -1].should == "l" + + #"har"[/(.)(.)(.)/, 0].should == "har" + #"har"[/(.)(.)(.)/, 1].should == "h" + #"har"[/(.)(.)(.)/, 2].should == "a" + #"har"[/(.)(.)(.)/, 3].should == "r" + #"har"[/(.)(.)(.)/, -1].should == "r" + #"har"[/(.)(.)(.)/, -2].should == "a" + #"har"[/(.)(.)(.)/, -3].should == "h" + end + + skip "returns nil if there is no match" do + "hello there"[/(what?)/, 1].should == nil + end + + skip "returns nil if there is no capture for idx" do + "hello there"[/[aeiou](.)\1/, 2].should == nil + # You can't refer to 0 using negative indices + "hello there"[/[aeiou](.)\1/, -2].should == nil + end + + skip "calls to_int on idx" do + obj = Object.new + obj.should_receive(:to_int, :returning => 2) + + "har"[/(.)(.)(.)/, 1.5].should == "h" + "har"[/(.)(.)(.)/, obj].should == "a" + end + + skip "returns subclass instances" do + s = MyString.new("hello") + s[/(.)(.)/, 0].class.should == MyString + s[/(.)(.)/, 1].class.should == MyString + end +end + +skip "String#[other_string]" do + it "returns the string if it occurs in self" do + s = "lo" + "hello there"[s].should == s + end + + it "returns nil if there is no match" do + "hello there"["bye"].should == nil + end + + skip "doesn't call to_str on its argument" do + o = Object.new + o.should_not_receive(:to_str) + + should_raise(TypeError) { "hello"[o] } + end + + skip "returns a subclass instance when given a subclass instance" do + s = MyString.new("el") + r = "hello"[s] + r.should == "el" + r.class.should == MyString + end +end + +describe "String#[idx] = char" do + it "sets the code of the character at idx to char modulo 256" do + a = "hello" + a[0] = ?b + a.should == "bello" + a[-1] = ?a + a.should == "bella" + a[-1] = 0 + a.should == "bell\x00" + a[-5] = 0 + a.should == "\x00ell\x00" + + a = "x" + a[0] = ?y + a.should == "y" + a[-1] = ?z + a.should == "z" + + a[0] = 255 + a[0].should == 255 + a[0] = 256 + a[0].should == 0 + a[0] = 256 * 3 + 42 + a[0].should == 42 + a[0] = -214 + a[0].should == 42 + end + + it "raises an IndexError without changing self if idx is outside of self" do + a = "hello" + + should_raise(IndexError) { a[20] = ?a } + a.should == "hello" + + should_raise(IndexError) { a[-20] = ?a } + a.should == "hello" + + should_raise(IndexError) { ""[0] = ?a } + should_raise(IndexError) { ""[-1] = ?a } + end +end + +describe "String#[idx] = other_str" do + it "replaces the char at idx with other_str" do + a = "hello" + a[0] = "bam" + a.should == "bamello" + a[-2] = "" + a.should == "bamelo" + end + + it "raises an IndexError without changing self if idx is outside of self" do + str = "hello" + + should_raise(IndexError) { str[20] = "bam" } + str.should == "hello" + + should_raise(IndexError) { str[-20] = "bam" } + str.should == "hello" + + should_raise(IndexError) { ""[0] = "bam" } + should_raise(IndexError) { ""[-1] = "bam" } + end + + skip "raises a TypeError when self is frozen" do + a = "hello" + a.freeze + + should_raise(TypeError) { a[0] = "bam" } + end + + # Broken in MRI 1.8.4 + it "calls to_int on idx" do + if defined? IRONRUBY_VERSION + str = "hello" + str[0.5] = "hi " + str.should == "hi ello" + + class Bob4 + def to_int + -1 + end + end + str[Bob4.new] = "!" + str.should == "hi ell!" + end + end + + # BUGBUG: This raises an error only when run after all the other tests here, but not + # in isolation. This smells like another variant of the dynamic site caching + # bug that we see elsewhere in this set of tests + skip "tries to convert other_str to a String using to_str" do + class Bob55 + def to_str + "-test-" + end + end + other_str = Bob55.new + + a = "abc" + a[1] = other_str + a.should == "a-test-c" + end + + skip "raises a TypeError if other_str can't be converted to a String" do + should_raise(TypeError) { "test"[1] = :test } + should_raise(TypeError) { "test"[1] = Object.new } + should_raise(TypeError) { "test"[1] = nil } + end +end + +# MSFT +describe "String#[idx, chars_to_overwrite] = other_str" do + it "starts at idx and overwrites chars_to_overwrite characters before inserting the rest of other_str" do + a = "hello" + a[0, 2] = "xx" + a.should == "xxllo" + a = "hello" + a[0, 2] = "jello" + a.should == "jellollo" + end + + it "counts negative idx values from end of the string" do + a = "hello" + a[-1, 0] = "bob" + a.should == "hellbobo" + a = "hello" + a[-5, 0] = "bob" + a.should == "bobhello" + end + + it "overwrites and deletes characters if chars_to_overwrite is less than the length of other_str" do + a = "hello" + a[0, 4] = "x" + a.should == "xo" + a = "hello" + a[0, 5] = "x" + a.should == "x" + end + + it "deletes characters if other_str is an empty string" do + a = "hello" + a[0, 2] = "" + a.should == "llo" + end + + it "deletes characters up to the maximum length of the existing string" do + a = "hello" + a[0, 6] = "x" + a.should == "x" + a = "hello" + a[0, 100] = "" + a.should == "" + end + + it "appends other_str to the end of the string if idx == the length of the string" do + a = "hello" + a[5, 0] = "bob" + a.should == "hellobob" + end + + it "ignores the length parameter if idx == the length of the string and just appends other_str to the end of the string" do + a = "hello" + a[5, 1] = "bob" + a.should == "hellobob" + end + + it "throws an IndexError if |idx| is greater than the length of the string" do + should_raise(IndexError) { "hello"[6, 0] = "bob" } + should_raise(IndexError) { "hello"[-6, 0] = "bob" } + end + + it "throws an IndexError if chars_to_overwrite < 0" do + should_raise(IndexError) { "hello"[0, -1] = "bob" } + should_raise(IndexError) { "hello"[1, -1] = "bob" } + end + + it "throws a TypeError if other_str is a type other than String" do + #should_raise(TypeError) { "hello"[0, 2] = nil } # this doesn't work for + #some reason???? + should_raise(TypeError) { "hello"[0, 2] = :bob } + should_raise(TypeError) { "hello"[0, 2] = 33 } + end +end + +# MSFT +describe "String#[range] = other_str" do + it "overwrites characters defined by range with other_str when range size and other_str size are equal" do + a = "hello" + a[0..2] = "abc" + a.should == "abclo" + a = "hello" + a[0..4] = "world" + a.should == "world" + end + + it "overwrites the first character defined by a single character range if other_str is a single character" do + a = "hello" + a[0..0] = "j" + a.should == "jello" + a = "hello" + a[4..4] = "p" + a.should == "hellp" + end +end + +describe "String#*count" do + it "returns a new string containing count copies of self" do + ("cool" * 0).should == "" + ("cool" * 1).should == "cool" + ("cool" * 3).should == "coolcoolcool" + end + + skip "tries to convert the given argument to an integer using to_int" do + ("cool" * 3.1).should == "coolcoolcool" + ("a" * 3.999).should == "aaa" + end +end + +describe "String#capitalize" do + it "returns a copy of self with the first character converted to uppercase and the remainder to lowercase" do + "hello".capitalize.should == "Hello" + "HELLO".capitalize.should == "Hello" + "123ABC".capitalize.should == "123abc" + end + + it "is locale insensitive (only upcases a-z and only downcases A-Z)" do + "ÄÖÜ".capitalize.should == "ÄÖÜ" + "ärger".capitalize.should == "ärger" + "BÄR".capitalize.should == "BÄr" + end +end + +describe "String#capitalize!" do + it "capitalizes self in place" do + a = "hello" + a.capitalize!.should == "Hello" + a.should == "Hello" + end + + it "returns nil when no changes are made" do + a = "Hello" + a.capitalize!.should == nil + a.should == "Hello" + + "".capitalize!.should == nil + end + + skip "raises a TypeError when self is frozen" do + ["", "Hello", "hello"].each do |a| + a.freeze + should_raise(TypeError) { a.capitalize! } + end + end +end + +describe "String#center" do + it "returns self when the centering length is less than self's length" do + "12345".center(4).should == "12345" + end + + it "returns self centered and padded with spaces when no padding string is provided" do + "12345".center(16).should == " 12345 " + end + + it "returns self centered and padded with spaces when no padding string is provided" do + "12345".center(16, "abc").should == "abcab12345abcabc" + end + + it "raises an ArgumentError when the padding string is empty" do + should_raise(ArgumentError) { "12345".center(16, "") } + end +end + +describe "String#chomp" do + it "returns string without record separator, if present" do + "12345".chomp("45").should == "123" + "12345".chomp("34").should == "12345" + "".chomp("34").should == "" + end + + it "for default record separator, returns string without terminating CR, LF and CRLF" do + "12345\n".chomp.should == "12345" + "12345\r\n".chomp.should == "12345" + "12345\r".chomp.should == "12345" + "12345".chomp.should == "12345" + "".chomp.should == "" + end +end + +describe "String#chomp!" do + it "removes record separator in-place from the end of the string if present" do + a = "12345" + a.chomp!("345").should == "12" + a.should == "12" + end + + it "for default record separator, removes CR, LF and CRLF from the end of the string" do + a = "12345\r\n" + a.chomp!.should == "12345" + a.should == "12345" + end + + it "returns nil if no modifications were made" do + a = "12345" + a.chomp!.should == nil + a.should == "12345" + end +end + +describe "String#chop" do + it "returns string without last character; last two for CRLF" do + "12345".chop.should == "1234" + "12345\n".chop.should == "12345" + "12345\r".chop.should == "12345" + "12345\r\n".chop.should == "12345" + "".chop.should == "" + end +end + +describe "String#chop!" do + it "removes last character in-place from the end of the string if" do + a = "12345\r\n" + a.chop!.should == "12345" + a.should == "12345" + end + + it "returns nil if no modifications were made" do + a = "" + a.chop!.should == nil + a.should == "" + end +end + +describe "String#downcase" do + it "returns a copy of self with all uppercase letters downcased" do + "hELLO".downcase.should == "hello" + "hello".downcase.should == "hello" + end + + it "is locale insensitive (only replacing A-Z)" do + "ÄÖÜ".downcase.should == "ÄÖÜ" + end +end + +describe "String#downcase!" do + it "modifies self in place" do + a = "HeLlO" + a.downcase!.should == "hello" + a.should == "hello" + end + + it "returns nil if no modifications were made" do + a = "hello" + a.downcase!.should == nil + a.should == "hello" + end + + skip "raises a TypeError when self is frozen" do + should_raise(TypeError) do + a = "HeLlO" + a.freeze + a.downcase! + end + end +end + +describe "String#dump" do + skip "produces a version of self with all nonprinting charaters replaced by \\nnn notation" do + ("\000".."A").to_a.to_s.dump.should == "\"\\000\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\e\\034\\035\\036\\037 !\\\"\\\#$%&'()*+,-./0123456789\"" + end + + skip "ignores the $KCODE setting" do + old_kcode = $KCODE + + begin + $KCODE = "NONE" + "äöü".dump.should == "\"\\303\\244\\303\\266\\303\\274\"" + + $KCODE = "UTF-8" + "äöü".dump.should == "\"\\303\\244\\303\\266\\303\\274\"" + ensure + $KCODE = old_kcode + end + end +end + +describe "String#empty?" do + it "returns true if the string has a length of zero" do + "hello".empty?.should == false + " ".empty?.should == false + "".empty?.should == true + end +end + +describe "String#include?(other)" do + it "returns true if self contains other" do + "hello".include?("lo").should == true + "hello".include?("ol").should == false + end + + it "tries to convert other to string using to_str" do + class Bob3 + def to_str + "lo" + end + end + + "hello".include?(Bob3.new).should == true + end + + it "raises a TypeError if other can't be converted to string" do + should_raise(TypeError) do + "hello".include?(:lo) + end + + should_raise(TypeError) do + "hello".include?(Object.new) + end + end +end + +describe "String#include?(fixnum)" do + it "returns true if self contains the given char" do + "hello".include?(?h).should == true + "hello".include?(?z).should == false + end +end + +# TODO: inadequate tests ... must write proper set for the next two describes +describe "String#index(fixnum [, offset])" do + it "returns the index of the first occurence of the given character" do + "hello".index(?e).should == 1 + end + + it "starts the search at the given offset" do + "hello".index(?o, -2).should == 4 + end + + it "returns nil if no occurence is found" do + "hello".index(?z).should == nil + "hello".index(?e, -2).should == nil + end +end + +describe "String#index(substring [, offset])" do + it "returns the index of the first occurence of the given substring" do + "hello".index('e').should == 1 + "hello".index('lo').should == 3 + end + + it "starts the search at the given offset" do + "hello".index('o', -3).should == 4 + end + + it "returns nil if no occurence is found" do + "hello".index('z').should == nil + "hello".index('e', -2).should == nil + end + + it "raises a TypeError if no string was given" do + should_raise(TypeError) do + "hello".index(:sym) + end + + #should_raise(TypeError) do + # "hello".index(Object.new) + #end + #"a b c ".split(/\s+/).should == ["a", "b", "c"] + end +end + +describe "String#insert(index, other)" do + it "inserts other before the character at the given index" do + "abcd".insert(0, 'X').should == "Xabcd" + "abcd".insert(3, 'X').should == "abcXd" + "abcd".insert(4, 'X').should == "abcdX" + end + + it "modifies self in place" do + a = "abcd" + a.insert(4, 'X').should == "abcdX" + a.should == "abcdX" + end + + it "inserts after the given character on an negative count" do + "abcd".insert(-3, 'X').should == "abXcd" + "abcd".insert(-1, 'X').should == "abcdX" + end + + it "raises an IndexError if the index is out of string" do + should_raise(IndexError) { "abcd".insert(5, 'X') } + should_raise(IndexError) { "abcd".insert(-6, 'X') } + end + + it "converts other to a string using to_str" do + class Bob6 + def to_str + "XYZ" + end + end + + "abcd".insert(-3, Bob6.new).should == "abXYZcd" + end + + it "raises a TypeError if other can't be converted to string" do + should_raise(TypeError) do + "abcd".insert(-6, :sym) + end + + should_raise(TypeError) do + "abcd".insert(-6, 12) + end + + should_raise(TypeError) do + "abcd".insert(-6, Object.new) + end + end + + skip "raises a TypeError if self is frozen" do + should_raise(TypeError) do + a = "abcd" + a.freeze + a.insert(4, 'X') + end + end +end + +describe "String#scan" do + it "scans for fixed strings when passed a string" do + a = "hello world" + a.scan("l").should == ["l", "l", "l"] + a.scan("ld").should == ["ld"] + a.scan("he").should == ["he"] + a.scan("abc").should == [] + + s = "" + result = a.scan("l") { |c| s += c } + result.should == a + s.should == "lll" + end + + it "returns n+1 empty strings for a string of n chars when passed a blank string to match" do + a = "abc" + a.scan("").should == ["", "", "", ""] + + s = "" + result = a.scan("") { |c| s += c } + result.should == a + s.should == "" + end + + it "returns matches for a regex when there's no grouping" do + a = "hello world" + a.scan(/\w+/).should == ["hello", "world"] + a.scan(/.../).should == ["hel", "lo ", "wor"] + + s = "" + result = a.scan(/\w+/) { |c| s = s + "|" + c } + result.should == a + s.should == "|hello|world" + end + + it "returns group information for a regex with groups" do + a = "hello world" + a.scan(/(...)/).should == [["hel"], ["lo "], ["wor"]] + a.scan(/(..)(..)/).should == [["he", "ll"], ["o ", "wo"]] + + s = "" + result = a.scan(/(..)(..)/) { |b, c| s = s + c + "." + b } + result.should == a + s.should == "ll.hewo.o " + end +end + +describe "String#succ" do + it "increments alphanumeric characters" do + "a".succ.should == "b" + "0".succ.should == "1" + "A".succ.should == "B" + end + + it "performs carry operations on alphanumeric characters" do + "az".succ.should == "ba" + "09".succ.should == "10" + "AZ".succ.should == "BA" + "Az".succ.should == "Ba" + "a9".succ.should == "b0" + "aZ".succ.should == "bA" + end + + it "inserts a new character if it runs out of alphanumeric characters to apply a carry to" do + "z".succ.should == "aa" + "9".succ.should == "10" + "Z".succ.should == "AA" + end + + it "skips over non alphanumeric characters when looking for next character to apply a carry to" do + "a!!!z".succ.should == "b!!!a" + "a!!!9".succ.should == "b!!!0" + end + + it "increments the byte-based value of the char by one for non alphanumeric characters" do + "?".succ.should == "@" + "~".succ.should == "\177" # 127 + "\000\377".succ.should == "\001\000" + "\000\377\377".succ.should == "\001\000\000" + end + + it "inserts a new character if it runs out of non alphanumeric characters to apply a carry to" do + "\377".succ.should == "\001\000" + "\377\377".succ.should == "\001\000\000" + end +end + +describe "String#succ!" do + it "behaves identically to String#succ except for the fact that it does things in-place" do + a = "a" + a.succ!.should == "b" + a.should == "b" + a = "ZZZ9999" + a.succ!.should == "AAAA0000" + a.should == "AAAA0000" + end +end + +describe "String#swapcase" do + it "returns a copy of self with all lowercase letters upcased and all uppercase letters downcased" do + "hELLO".swapcase.should == "Hello" + "hello".swapcase.should == "HELLO" + "HELLO".swapcase.should == "hello" + end + + it "is locale insensitive (only upcases a-z and only downcases A-Z)" do + "".swapcase.should == "" + end +end + +describe "String#swapcase!" do + it "modifies self in place" do + a = "HeLlO" + a.swapcase!.should == "hElLo" + a.should == "hElLo" + end + + it "returns nil if no modifications were made" do + a = "12345" + a.swapcase!.should == nil + a.should == "12345" + end + + skip "raises a TypeError when self is frozen" do + should_raise(TypeError) do + a = "HeLlO" + a.freeze + a.swapcase! + end + end +end + +describe "String#upcase" do + it "returns a copy of self with all lowercase letters upcased" do + "hELLO".upcase.should == "HELLO" + "hello".upcase.should == "HELLO" + "HELLO".upcase.should == "HELLO" + end + + it "is locale insensitive (only replacing a-z)" do + "-".upcase.should == "-" + end +end + +describe "String#upcase!" do + it "modifies self in place" do + a = "HeLlO" + a.upcase!.should == "HELLO" + a.should == "HELLO" + end + + it "returns nil if no modifications were made" do + a = "HELLO" + a.upcase!.should == nil + a.should == "HELLO" + end + + skip "raises a TypeError when self is frozen" do + should_raise(TypeError) do + a = "HeLlO" + a.freeze + a.upcase! + end + end +end + +describe "String#upto" do + it "enumerates the range between the strings" do + s = "" + n = 0 + result = "b8".upto("c2") { |c| + s += c + n += 1 + } + result.should == "b8" + s.should == "b8b9c0c1c2" + n.should == 5 + end +end + +# TODO: haven't implemented regex parse tree transformation yet +#describe "String#index(regexp [, offset])" do +# skip "returns the index of the first match with the given regexp" do +# "hello".index(/[aeiou]/).should == 1 +# end + +# skip "starts the search at the given offset" do +# "hello".index(/[aeiou]/, -3).should == 4 +# end + +# skip "returns nil if no occurence is found" do +# "hello".index(/z/).should == nil +# "hello".index(/e/, -2).should == nil +# end +#end + +describe "string creation" do + it "creates a string using language syntax" do + a = "test" + a.length.should == 4 + a.should == "test" + end + + it "creates a string using String.new" do + b = String.new("test") + b.length.should == 4 + b.should == "test" + end + + it "String.new doesn't accept nil" do + should_raise(defined?(IRONRUBY_VERSION) ? ArgumentError : TypeError) { String.new(nil) } + end + + it "String.new doesn't accept nil" do + obj = Object.new + class << obj + def to_s + "to_s" + end + + def to_str + "to_str" + end + end + String.new(obj).should == "to_str" + end + + it "(*) creates a repeated string" do + a = "bob" * 3 + a.length.should == 9 + a.should == "bobbobbob" + + b = "bob" * 0 + b.length.should == 0 + b.should == "" + + should_raise(ArgumentError) { c = "bob" * -3 } + + d = "bob" * 3.333 + d.length.should == 9 + d.should == "bobbobbob" + end + + it "(+) creates a concatenated string" do + a = "hello" + b = "world" + (a + b).should == "helloworld" + end + + it "(<<) appends strings" do + a = "hello" + oid = a.object_id + a << "world" + a.object_id.should == oid + a.should == "helloworld" + end +end + +describe "String#each, String#each_line" do + # Test case data + testcases = [ + "", + "\n", + "\n\n", + "\n\n\n", + "abc", + "abc\n", + "\nabc", + "\nabc\n", + "abc\n\n", + "\n\nabc", + "\n\nabc\n\n", + "abc\ndef", + "abc\ndef\n", + "\nabc\ndef", + "\nabc\ndef\n", + "abc\ndef\n\n", + "\n\nabc\ndef", + "\n\nabc\ndef\n\n", + "abc\n\ndef", + "abc\n\ndef\n", + "\nabc\n\ndef", + "\nabc\n\ndef\n", + "abc\n\ndef\n\n", + "\n\nabc\n\ndef", + "\n\nabc\n\ndef\n\n", + ] + + it "test normal mode" do + expected = [ + [], + ["\n"], + ["\n", "\n"], + ["\n", "\n", "\n"], + ["abc"], + ["abc\n"], + ["\n", "abc"], + ["\n", "abc\n"], + ["abc\n", "\n"], + ["\n", "\n", "abc"], + ["\n", "\n", "abc\n", "\n"], + ["abc\n", "def"], + ["abc\n", "def\n"], + ["\n", "abc\n", "def"], + ["\n", "abc\n", "def\n"], + ["abc\n", "def\n", "\n"], + ["\n", "\n", "abc\n", "def"], + ["\n", "\n", "abc\n", "def\n", "\n"], + ["abc\n", "\n", "def"], + ["abc\n", "\n", "def\n"], + ["\n", "abc\n", "\n", "def"], + ["\n", "abc\n", "\n", "def\n"], + ["abc\n", "\n", "def\n", "\n"], + ["\n", "\n", "abc\n", "\n", "def"], + ["\n", "\n", "abc\n", "\n", "def\n", "\n"], + ] + + testcases.length.times do |i| + a = [] + testcases[i].each { |e| a << e } + a.should == expected[i] + + a = [] + testcases[i].each_line { |e| a << e } + a.should == expected[i] + end + end + + it "test paragraph mode" do + expected = [ + [], + ["\n"], + ["\n\n"], + ["\n\n\n"], + ["abc"], + ["abc\n"], + ["\nabc"], + ["\nabc\n"], + ["abc\n\n"], + ["\n\n", "abc"], + ["\n\n", "abc\n\n"], + ["abc\ndef"], + ["abc\ndef\n"], + ["\nabc\ndef"], + ["\nabc\ndef\n"], + ["abc\ndef\n\n"], + ["\n\n", "abc\ndef"], + ["\n\n", "abc\ndef\n\n"], + ["abc\n\n", "def"], + ["abc\n\n", "def\n"], + ["\nabc\n\n", "def"], + ["\nabc\n\n", "def\n"], + ["abc\n\n", "def\n\n"], + ["\n\n", "abc\n\n", "def"], + ["\n\n", "abc\n\n", "def\n\n"], + ] + + testcases.length.times do |i| + a = [] + testcases[i].each('') { |e| a << e } + a.should == expected[i] + + a = [] + testcases[i].each_line('') { |e| a << e } + a.should == expected[i] + end + end + +end + +describe "String#each_byte" do + # TODO: we cannot enable this test until method dispatch correctly detects + # missing block parameters + skip "raises a LocalJumpError if no block given" do + should_raise(LocalJumpError) { "bob".each_byte } + end + + it "passes each byte in self to the given block" do + a = [] + "hello\x00".each_byte { |c| a << c } + a.should == [104, 101, 108, 108, 111, 0] + end + + # globals used since closures are broken + it "respects the fact that the string can change length during iteration" do + s = "hello" + t = "" + s.each_byte do |c| + t << c + if t.length < 3 + s << c + end + end + s.should == "hellohe" + t.should == "hellohe" + end +end + +describe "String#<<(string)" do + it "concatenates the given argument to self and returns self" do + str = 'hello ' + (str << 'world').equal?(str).should == true + str.should == "hello world" + end + + it "converts the given argument to a String using to_str" do + class Bob500 + def to_str + "world!" + end + end + obj = Bob500.new + + a = 'hello ' << obj + a.should == 'hello world!' + end + + it "raises a TypeError if the given argument can't be converted to a String" do + should_raise(TypeError) do + a = 'hello ' << :world + end + + should_raise(TypeError) do + a = 'hello ' << Object.new + end + end + + skip "raises a TypeError when self is frozen" do + a = "hello" + a.freeze + + should_raise(TypeError) { a << "" } + should_raise(TypeError) { a << "test" } + end + + skip "works when given a subclass instance" do + a = "hello" + a << MyString.new(" world") + a.should == "hello world" + end +end + +describe "String#<<(fixnum)" do + it "converts the given Fixnum to a char before concatenating" do + b = 'hello ' << 'world' << 33 + b.should == "hello world!" + b << 0 + b.should == "hello world!\x00" + end + + it "raises a TypeError when the given Fixnum is not between 0 and 255" do + should_raise(TypeError) do + "hello world" << 333 + end + end + + it "doesn't call to_int on its argument" do + should_raise(TypeError) { "" << Object.new } + end + + skip "raises a TypeError when self is frozen" do + a = "hello" + a.freeze + + should_raise(TypeError) { a << 0 } + should_raise(TypeError) { a << 33 } + end +end + +finished diff --git a/merlin/main/languages/ruby/Tests/Builtin/StringScanner/test_strscan.rb b/merlin/main/languages/ruby/Tests/Builtin/StringScanner/test_strscan.rb new file mode 100644 index 0000000000..bf8fc0a0be --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/StringScanner/test_strscan.rb @@ -0,0 +1,260 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require File.dirname(__FILE__) + '/../../Util/simple_test.rb' +require 'strscan' + +class RandomClass + def to_str + "bob" + end +end + +describe "StringScanner#new" do + it "can be constructed" do + str = 'test string' + s = StringScanner.new(str) + s.class.should == StringScanner + s.eos?.should == false + s.string.should == str + s.string.frozen? == false + end + + it "can be constructed from a non-string" do + s = StringScanner.new(RandomClass.new) + s.peek(3).should == 'bob' + end + + skip "(test bug? Ruby 1.8.6 also fails this test) ignores the legacy parameter to the constructor" do + a = "123" + s = StringScanner.new(a, true) + s.string.object_id.should == a.object_id + s = StringScanner.new(a, false) + s.string.object_id.should == a.object_id + end + + it "implements a legacy class function" do + StringScanner.must_C_version.should == StringScanner + end +end + +describe "StringScanner#class" do + it "can be constructed" do + str = 'test string' + s = StringScanner.new(str) + s.class.should == StringScanner + s.eos?.should == false + s.string.should == str + end + + it "implements a legacy class function" do + StringScanner.must_C_version.should == StringScanner + end +end + +describe "StringScanner#concat" do + it "appends to the internal buffer" do + s = StringScanner.new('abcde') + s.string.should == 'abcde' + s.concat('fgh').string.should == 'abcdefgh' + s << 'i' + s.string.should == 'abcdefghi' + s.bol?.should == true + end + + it "works when at the end of the string" do + s = StringScanner.new('') + s.eos?.should == true + s << '123' + s.eos?.should == false + s.pos.should == 0 + end +end + +# TODO: treat bytes and chars differently +describe "StringScanner#get_byte" do + it "gets the next character" do + s = StringScanner.new('abcde') + s.get_byte.should == 'a' + s.get_byte.should == 'b' + s.get_byte.should == 'c' + end + + it "returns nil with no chars left" do + s = StringScanner.new('') + s.get_byte.should == nil + end + + it "updates matched information" do + s = StringScanner.new('abcde') + s.get_byte.should == 'a' + s.get_byte.should == 'b' + s.matched?.should == true + s.matched.should == 'b' + s.pre_match.should == 'a' + s.post_match.should == 'cde' + s[0].should == 'b' + s.pos.should == 2 + s.matched_size.should == 1 + end +end + +describe "StringScanner#getch" do + it "gets the next character" do + s = StringScanner.new('abcde') + s.getch.should == 'a' + s.getch.should == 'b' + s.getch.should == 'c' + end + + it "returns nil with no chars left" do + s = StringScanner.new('') + s.getch.should == nil + end + + it "updates matched information" do + s = StringScanner.new('abcde') + s.getch.should == 'a' + s.getch.should == 'b' + s.matched?.should == true + s.matched.should == 'b' + s.pre_match.should == 'a' + s.post_match.should == 'cde' + s[0].should == 'b' + s.pos.should == 2 + s.matched_size.should == 1 + end +end + +describe "StringScanner#check" do + it "matches at the current position without advancing" do + s = StringScanner.new('Fri Dec 12 1975 14:39') + s.check(/Fri/).should == 'Fri' + s.pos.should == 0 + s.matched.should == 'Fri' + s[0].should == 'Fri' + end + + it "returns nil when match fails" do + s = StringScanner.new('Fri Dec 12 1975 14:39') + s.check(/12/).should == nil + s.matched.should == nil + end +end + +describe "StringScanner#check_until" do + it "matches further in the string without advancing" do + s = StringScanner.new('Fri Dec 12 1975 14:39') + s.check_until(/12/).should == 'Fri Dec 12' + s.pos.should == 0 + s.matched.should == '12' + s[0].should == '12' + end + + it "returns nil when match fails" do + s = StringScanner.new('Fri Dec 12 1975 14:39') + s.check_until(/abc/).should == nil + s.pos.should == 0 + s.matched.should == nil + end +end + +describe "StringScanner#skip_until" do + it "matches further in the string and advances position" do + s = StringScanner.new('Fri Dec 12 1975 14:39') + s.skip_until(/12/).should == 10 + s.pos.should == 10 + s.matched.should == '12' + s[0].should == '12' + end + + it "returns nil when match fails" do + s = StringScanner.new('Fri Dec 12 1975 14:39') + s.check(/abc/).should == nil + s.pos.should == 0 + s.matched.should == nil + end +end + +describe "StringScanner#exist?" do + it "matches further in the string without advancing and returns potentially resulting position" do + s = StringScanner.new('test string') + s.exist?(/s/).should == 3 + s.pos.should == 0 + s.matched.should == 's' + end + + it "returns nil when match fails" do + s = StringScanner.new('test string') + s.exist?(/abc/).should == nil + s.matched.should == nil + end +end + +describe "StringScanner#peek" do + it "returns the next n characters without advancing" do + s = StringScanner.new('test string') + s.peek(5).should == 'test ' + s.pos.should == 0 + end + + it "truncates at the end of the string" do + s = StringScanner.new('test') + s.peek(5).should == 'test' + s.pos.should == 0 + end + + it "returns an empty string when appropriate" do + s = StringScanner.new('a') + s.getch + s.peek(5).should == '' + end +end + +describe "StringScanner#match?" do + it "matches string and returns match length without advancing" do + s = StringScanner.new('test string') + s.match?(/\w+/).should == 4 + s.pos.should == 0 + s.matched_size.should == 4 + end + + it "returns nil on failure to match" do + s = StringScanner.new('test string') + s.match?(/\s+/).should == nil + end +end + +describe "StringScanner#string=" do + it "resets everything when you set a new string" do + s = StringScanner.new('test string') + s.scan(/\w+/).should == 'test' + s.pos.should == 4 + s.matched?.should == true + s.string = "123" + s.pos.should == 0 + s.matched?.should == false + end + + it "clones and then freezes the string when you set a new string" do + s = StringScanner.new('') + a = "123" + s.string = a + s.string.frozen?.should == true + s.string.object_id.should_not == a.object_id + end +end + +finished \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/chdir_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/chdir_excludes.txt new file mode 100644 index 0000000000..bcddbaf0ec --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/chdir_excludes.txt @@ -0,0 +1 @@ +Dir.chdir raises a SystemCallError if the directory does not exist diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/chroot_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/chroot_excludes.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/close_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/close_excludes.txt new file mode 100644 index 0000000000..6788a8700b --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/close_excludes.txt @@ -0,0 +1,2 @@ +Dir#close closes the stream and fd and returns nil +Dir#close raises an IOError when called on a closed Dir instance diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/delete_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/delete_excludes.txt new file mode 100644 index 0000000000..9e8baa414e --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/delete_excludes.txt @@ -0,0 +1 @@ +Dir.delete removes empty directories diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/each_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/each_excludes.txt new file mode 100644 index 0000000000..da60f05c10 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/each_excludes.txt @@ -0,0 +1,5 @@ +Dir#close closes the stream and fd and returns nil +Dir#close raises an IOError when called on a closed Dir instance +Dir#each yields each directory entry in succession +Dir#each returns the directory which remains open +Dir#each raises an IOError when called on a closed Dir instance diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/element_reference_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/element_reference_excludes.txt new file mode 100644 index 0000000000..3cc9573e31 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/element_reference_excludes.txt @@ -0,0 +1,37 @@ +Dir.glob matches non-dotfiles with '*' +Dir.glob matches dotfiles with '.*' +Dir.glob matches non-dotfiles with '*' +Dir.glob matches dotfiles with '.*' +Dir.glob matches files with any ending with '*' +Dir.glob matches files with any middle with '*' +Dir.glob matches files with multiple '*' special characters +Dir.glob matches non-dotfiles in the current directory with '**' +Dir.glob matches dotfiles in the current directory with '.**' +Dir.glob recursively matches any nondot subdirectories with '**/' +Dir.glob recursively matches any subdirectories including ./ and ../ with '.**/' +Dir.glob matches a single character except leading '.' with '?' +Dir.glob accepts multiple '?' characters in a pattern +Dir.glob matches any characters in a set with '[]' +Dir.glob matches any characters in a range with '[-]' +Dir.glob matches any characters except those in a set with '[^]' +Dir.glob matches any characters except those in a range with '[^-,,...}' +Dir.glob accepts string sets with empty strings with {,,} +Dir.glob matches dot or non-dotfiles with '{,.}*' +Dir.glob matches special characters by escaping with a backslash with '\' +Dir.glob recursively matches directories with '**/' +Dir.glob matches both dot and non-dotfiles with '*' and option File::FNM_DOTMATCH +Dir.glob matches files with any beginning with '*' and option File::FNM_DOTMATCH +Dir.glob matches any files in the current directory with '.**' and option File::FNM_DOTMATCH +Dir.glob recursively matches any subdirectories except './' or '../' with '**/' and option File::FNM_DOTMATCH +Dir.glob matches the literal character '\' with option File::FNM_NOESCAPE +Dir.[] matches non-dotfiles with '*' +Dir.[] matches non-dotfiles in the current directory with '**' +Dir.[] recursively matches any nondot subdirectories with '**/' +Dir.[] matches any characters except those in a set with '[^]' +Dir.[] matches any characters except those in a range with '[^-,,...}' +Dir.[] accepts string sets with empty strings with {,,} +Dir.[] matches dot or non-dotfiles with '{,.}*' +Dir.[] matches special characters by escaping with a backslash with '\' +Dir.[] recursively matches directories with '**/' diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/entries_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/entries_excludes.txt new file mode 100644 index 0000000000..b7ecd4235f --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/entries_excludes.txt @@ -0,0 +1,2 @@ +Dir.entries returns an Array of filenames in an existing directory including dotfiles +Dir.entries raises a SystemCallError if called with a nonexistent diretory diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/foreach_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/foreach_excludes.txt new file mode 100644 index 0000000000..4fc2b4145b --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/foreach_excludes.txt @@ -0,0 +1,3 @@ +Dir.foreach yields all names in an existing directory to the provided block +Dir.foreach returns nil when successful +Dir.foreach raises a SystemCallError if passed a nonexistent directory diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/getwd_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/getwd_excludes.txt new file mode 100644 index 0000000000..3ee0922184 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/getwd_excludes.txt @@ -0,0 +1,2 @@ +Dir.pwd returns the current working directory +Dir.pwd returns the current working directory diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/glob_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/glob_excludes.txt new file mode 100644 index 0000000000..8f3f08e3f2 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/glob_excludes.txt @@ -0,0 +1,27 @@ +Dir.glob matches non-dotfiles with '*' +Dir.glob matches dotfiles with '.*' +Dir.glob matches non-dotfiles with '*' +Dir.glob matches dotfiles with '.*' +Dir.glob matches files with any ending with '*' +Dir.glob matches files with any middle with '*' +Dir.glob matches files with multiple '*' special characters +Dir.glob matches non-dotfiles in the current directory with '**' +Dir.glob matches dotfiles in the current directory with '.**' +Dir.glob recursively matches any nondot subdirectories with '**/' +Dir.glob recursively matches any subdirectories including ./ and ../ with '.**/' +Dir.glob matches a single character except leading '.' with '?' +Dir.glob accepts multiple '?' characters in a pattern +Dir.glob matches any characters in a set with '[]' +Dir.glob matches any characters in a range with '[-]' +Dir.glob matches any characters except those in a set with '[^]' +Dir.glob matches any characters except those in a range with '[^-,,...}' +Dir.glob accepts string sets with empty strings with {,,} +Dir.glob matches dot or non-dotfiles with '{,.}*' +Dir.glob matches special characters by escaping with a backslash with '\' +Dir.glob recursively matches directories with '**/' +Dir.glob matches both dot and non-dotfiles with '*' and option File::FNM_DOTMATCH +Dir.glob matches files with any beginning with '*' and option File::FNM_DOTMATCH +Dir.glob matches any files in the current directory with '.**' and option File::FNM_DOTMATCH +Dir.glob recursively matches any subdirectories except './' or '../' with '**/' and option File::FNM_DOTMATCH +Dir.glob matches the literal character '\' with option File::FNM_NOESCAPE diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/initialize_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/initialize_excludes.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/mkdir_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/mkdir_excludes.txt new file mode 100644 index 0000000000..e26e038f19 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/mkdir_excludes.txt @@ -0,0 +1,2 @@ +Dir.mkdir creates the named directory with the given permissions +Dir.mkdir raises without adequate permissions in the parent dir diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/new_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/new_excludes.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/open_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/open_excludes.txt new file mode 100644 index 0000000000..ebcd815052 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/open_excludes.txt @@ -0,0 +1,2 @@ +Dir.open raises SystemCallError if the directory does not exist +Dir.open takes a block which yields the Dir instance and closes it after diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/path_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/path_excludes.txt new file mode 100644 index 0000000000..91e433950c --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/path_excludes.txt @@ -0,0 +1,2 @@ +Dir#path returns the path that was supplied to .new or .open +Dir#path raises an IOError when called on a closed Dir instance diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/pos_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/pos_excludes.txt new file mode 100644 index 0000000000..4eb5a1bcc8 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/pos_excludes.txt @@ -0,0 +1 @@ +Dir#pos raises an IOError when called on a closed Dir instance diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/pwd_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/pwd_excludes.txt new file mode 100644 index 0000000000..b7d7fde443 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/pwd_excludes.txt @@ -0,0 +1 @@ +Dir.pwd returns the current working directory diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/read_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/read_excludes.txt new file mode 100644 index 0000000000..212dc49ab6 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/read_excludes.txt @@ -0,0 +1 @@ +Dir#read raises an IOError when called on a closed Dir instance diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/rewind_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/rewind_excludes.txt new file mode 100644 index 0000000000..96ab4c95e9 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/rewind_excludes.txt @@ -0,0 +1,3 @@ +Dir#rewind resets the next read to start from the first entry +Dir#rewind returns the Dir instance +Dir#rewind raises an IOError when called on a closed Dir instance diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/rmdir_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/rmdir_excludes.txt new file mode 100644 index 0000000000..d7f72e9385 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/rmdir_excludes.txt @@ -0,0 +1,2 @@ +Dir.delete removes empty directories +Dir.rmdir removes empty directories diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/seek_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/seek_excludes.txt new file mode 100644 index 0000000000..e1f2d4c6bc --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/seek_excludes.txt @@ -0,0 +1,2 @@ +Dir#seek moves the current read position +Dir#seek returns the Dir instance diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/tell_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/tell_excludes.txt new file mode 100644 index 0000000000..ac55107f1c --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/tell_excludes.txt @@ -0,0 +1,4 @@ +Dir#pos raises an IOError when called on a closed Dir instance +Dir#tell Both Dir#pos and Dir#tell give the current dir position +Dir#tell Dir#pos= also seeks to a certain position but returns the position number instead +Dir#tell raises an IOError when called on a closed Dir instance diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/unlink_excludes.txt b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/unlink_excludes.txt new file mode 100644 index 0000000000..ce1fb54c7c --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/.spec/unlink_excludes.txt @@ -0,0 +1 @@ +Dir.unlink removes empty directories diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/chdir_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/chdir_spec.rb new file mode 100644 index 0000000000..1705600f20 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/chdir_spec.rb @@ -0,0 +1,39 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' + +describe "Dir.chdir" do + before(:each) do + @original = Dir.pwd + end + + after(:each) do + Dir.chdir(@original) + end + + it "defaults to $HOME with no arguments" do + Dir.chdir + Dir.pwd.should == ENV['HOME'] + end + + it "changes to the specified directory" do + Dir.chdir mock_dir + Dir.pwd.should == mock_dir + end + + it "returns 0 when successfully changing directory" do + Dir.chdir(@original).should == 0 + end + + it "returns the value of the block when a block is given" do + Dir.chdir(@original) { :block_value }.should == :block_value + end + + it "changes to the specified directory for the duration of the block" do + Dir.chdir(mock_dir) { |dir| [dir, Dir.pwd] }.should == [mock_dir, mock_dir] + Dir.pwd.should == @original + end + + it "raises a SystemCallError if the directory does not exist" do + should_raise(SystemCallError) { Dir.chdir nonexistent } + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/chroot_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/chroot_spec.rb new file mode 100644 index 0000000000..1df42cfba7 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/chroot_spec.rb @@ -0,0 +1,27 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' + +# Need special perms to run chroot +# describe "Dir.chroot" do +# specify 'Dir.chroot can be used to change the process\' root directory, see chroot(2)' do +# example do +# Kernel.fork { +# begin +# ret = Dir.chroot mock_dir +# File.open('/root_contents.txt', 'wb') {|f| f.puts ret; f.puts Dir.entries('/').sort} +# FileUtils.chmod 0777, '/root_contents.txt' +# rescue SystemCallError +# warn '**WARN: Insufficient permissions to test Dir.chroot! (Not a huge problem.)' +# end +# } +# +# Process.waitall +# +# contents = File.read "#{mock_dir}/root_contents.txt" +# FileUtils.rm "#{mock_dir}/root_contents.txt" +# +# # Should have the return value + the filenames +# contents.split("\n").sort +# end.should == %w|0 . .. .dotfile .dotsubdir subdir_one subdir_two deeply nondotfile file_one.ext file_two.ext root_contents.txt|.sort +# end +# end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/close_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/close_spec.rb new file mode 100644 index 0000000000..d42d40a235 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/close_spec.rb @@ -0,0 +1,34 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' + +describe "Dir#close" do + it "closes the stream and fd and returns nil" do + # This is a bit convoluted but we are trying to ensure the file gets closed. + # To do that, we peek to see what the next FD number is and then probe that + # to see whether it has been closed. + peek = IO.sysopen mock_dir + File.for_fd(peek).close + + dir = Dir.open mock_dir + File.for_fd(peek).close # Should be open here + + dir.close.should == nil + should_raise(SystemCallError) { File.for_fd(peek).close } # And closed here + end +end + +@dir_closed = shared "Dir closed" do |cmd| + describe "Dir##{cmd}" do + it "raises an IOError when called on a closed Dir instance" do + should_raise(IOError) do + dir = Dir.open mock_dir + dir.close + dir.send cmd + end + end + end +end + +describe "Dir#close" do + it_behaves_like @dir_closed, :close +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/delete_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/delete_spec.rb new file mode 100644 index 0000000000..b51dc2f80d --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/delete_spec.rb @@ -0,0 +1,36 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' + +@dir_delete = shared "Dir.delete" do |cmd| + describe "Dir.#{cmd}" do + specify "removes empty directories" do + %w|rmdir delete unlink|.each {|cmd| + Dir.mkdir 'empty_subdir' + Dir.send(cmd, 'empty_subdir').should == 0 + } + end + + it "raises SystemCallError when trying to remove a nonempty directory" do + %w|rmdir delete unlink|.each {|cmd| + should_raise(SystemCallError) { Dir.send cmd, 'subdir_one' } + } + end + + it "raises SystemCallError if lacking adequate permissions to remove the directory" do + %w|rmdir delete unlink|.each {|cmd| + system "mkdir -p noperm_#{cmd}/child" + system "chmod 0000 noperm_#{cmd}" + + should_raise(SystemCallError) { Dir.send cmd, "noperm_#{cmd}/child" } + + system "chmod 0777 noperm_#{cmd}" + Dir.rmdir "noperm_#{cmd}/child" + Dir.rmdir "noperm_#{cmd}" + } + end + end +end + +describe "Dir.delete" do + it_behaves_like @dir_delete, :delete +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/each_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/each_spec.rb new file mode 100644 index 0000000000..8feac8aae1 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/each_spec.rb @@ -0,0 +1,30 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' +require File.dirname(__FILE__) + '/close_spec' + +describe "Dir#each" do + before(:each) do + @dir = Dir.open mock_dir + end + + after(:each) do + @dir.close + end + + it "yields each directory entry in succession" do + a = [] + @dir.each {|dir| a << dir} + a.sort.should == %w|. .. .dotfile .dotsubdir nondotfile subdir_one subdir_two deeply file_one.ext file_two.ext|.sort + end + + it "returns the directory which remains open" do + @dir.each {}.should == @dir + @dir.read.should == nil + @dir.rewind + @dir.read.should == '.' + end +end + +describe "Dir#each" do + it_behaves_like @dir_closed, :each +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/element_reference_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/element_reference_spec.rb new file mode 100644 index 0000000000..7d9abfeee3 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/element_reference_spec.rb @@ -0,0 +1,7 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' +require File.dirname(__FILE__) + '/glob_spec' + +describe "Dir.[]" do + it_behaves_like @dir_glob, :[] +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/entries_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/entries_spec.rb new file mode 100644 index 0000000000..b7696bff39 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/entries_spec.rb @@ -0,0 +1,16 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' + +describe "Dir.entries" do + it "returns an Array of filenames in an existing directory including dotfiles" do + + Dir.entries(mock_dir).sort.should == %w|. .. subdir_one subdir_two .dotsubdir deeply + .dotfile nondotfile file_one.ext file_two.ext|.sort + + Dir.entries("#{mock_dir}/deeply/nested").sort.should == %w|. .. .dotfile.ext directory|.sort + end + + it "raises a SystemCallError if called with a nonexistent diretory" do + should_raise(SystemCallError) { Dir.entries nonexistent } + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/common.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/common.rb new file mode 100644 index 0000000000..661b3a2465 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/common.rb @@ -0,0 +1,12 @@ +def mock_dir + @mock_dir ||= Dir.chdir(File.dirname(__FILE__) + '/mock') { Dir.pwd } +end + +def nonexistent + name = mock_dir + "/nonexistent00" + while File.exist? name + name = name.next + end + #name = name.next while File.exist? name + name +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/.dotfile b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/.dotfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/.dotsubdir/.dotfile b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/.dotsubdir/.dotfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/.dotsubdir/nondotfile b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/.dotsubdir/nondotfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/.dotfile b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/.dotfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/.dotfile.ext b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/.dotfile.ext new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/directory/structure/.ext b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/directory/structure/.ext new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/directory/structure/bar b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/directory/structure/bar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/directory/structure/baz b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/directory/structure/baz new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/directory/structure/file_one b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/directory/structure/file_one new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/directory/structure/file_one.ext b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/directory/structure/file_one.ext new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/directory/structure/foo b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nested/directory/structure/foo new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nondotfile b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/deeply/nondotfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/file_one.ext b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/file_one.ext new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/file_two.ext b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/file_two.ext new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/nondotfile b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/nondotfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/subdir_one/.dotfile b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/subdir_one/.dotfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/subdir_one/nondotfile b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/subdir_one/nondotfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/subdir_two/nondotfile b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/subdir_two/nondotfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/subdir_two/nondotfile.ext b/merlin/main/languages/ruby/Tests/Builtin/dir/fixtures/mock/subdir_two/nondotfile.ext new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/foreach_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/foreach_spec.rb new file mode 100644 index 0000000000..0b34391051 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/foreach_spec.rb @@ -0,0 +1,22 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' + +describe "Dir.foreach" do + it "yields all names in an existing directory to the provided block" do + a, b = [], [] + + Dir.foreach(mock_dir) {|f| a << f} + Dir.foreach("#{mock_dir}/deeply/nested") {|f| b << f} + + a.sort.should == %w|. .. subdir_one subdir_two .dotsubdir deeply .dotfile nondotfile file_one.ext file_two.ext|.sort + b.sort.should == %w|. .. .dotfile.ext directory|.sort + end + + it "returns nil when successful" do + Dir.foreach(mock_dir) {|f| f}.should == nil + end + + it "raises a SystemCallError if passed a nonexistent directory" do + should_raise(SystemCallError) { Dir.foreach nonexistent } + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/getwd_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/getwd_spec.rb new file mode 100644 index 0000000000..a986cd2a6d --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/getwd_spec.rb @@ -0,0 +1,7 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' +require File.dirname(__FILE__) + '/pwd_spec' + +describe "Dir.pwd" do + it_behaves_like @dir_pwd, :getwd +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/glob_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/glob_spec.rb new file mode 100644 index 0000000000..bde04e09fe --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/glob_spec.rb @@ -0,0 +1,168 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' + +@dir_glob = shared "Dir.glob" do |cmd| + describe "Dir.#{cmd}" do + before(:all) do + @cwd = Dir.pwd + Dir.chdir mock_dir + end + + it "matches non-dotfiles with '*'" do + Dir.send(cmd,'*').sort.should == + %w|subdir_one subdir_two deeply nondotfile file_one.ext file_two.ext|.sort + end + + it "matches dotfiles with '.*'" do + Dir.send(cmd, '.*').sort.should == %w|. .. .dotfile .dotsubdir|.sort + end + + it "matches non-dotfiles with '*'" do + Dir.send(cmd, '*file').sort.should == %w|nondotfile|.sort + end + + it "matches dotfiles with '.*'" do + Dir.send(cmd, '.*file').sort.should == %w|.dotfile|.sort + end + + it "matches files with any ending with '*'" do + Dir.send(cmd, 'file*').sort.should == %w|file_one.ext file_two.ext|.sort + end + + it "matches files with any middle with '*'" do + Dir.send(cmd, 'sub*_one').sort.should == %w|subdir_one|.sort + end + + it "matches files with multiple '*' special characters" do + Dir.send(cmd, '*fi*e*').sort.should == %w|nondotfile file_one.ext file_two.ext|.sort + end + + it "matches non-dotfiles in the current directory with '**'" do + Dir.send(cmd, '**').sort.should == %w|subdir_one subdir_two deeply nondotfile file_one.ext file_two.ext|.sort + end + + it "matches dotfiles in the current directory with '.**'" do + Dir.send(cmd, '.**').sort.should == %w|. .. .dotsubdir .dotfile|.sort + end + + it "recursively matches any nondot subdirectories with '**/'" do + Dir.send(cmd, '**/').sort.should == %w|subdir_one/ subdir_two/ deeply/ deeply/nested/ + deeply/nested/directory/ deeply/nested/directory/structure/|.sort + end + + it "recursively matches any subdirectories including ./ and ../ with '.**/'" do + Dir.chdir("#{mock_dir}/subdir_one") do + Dir.send(cmd, '.**/').sort.should == %w|./ ../|.sort + end + end + + it "matches a single character except leading '.' with '?'" do + Dir.send(cmd, '?ubdir_one').sort.should == %w|subdir_one|.sort + end + + it "accepts multiple '?' characters in a pattern" do + Dir.send(cmd, 'subdir_???').sort.should == %w|subdir_one subdir_two|.sort + end + + it "matches any characters in a set with '[]'" do + Dir.send(cmd, '[some]ubdir_one').sort.should == %w|subdir_one|.sort + end + + it "matches any characters in a range with '[-]'" do + Dir.send(cmd, '[a-zA-Z]ubdir_one').sort.should == %w|subdir_one|.sort + end + + it "matches any characters except those in a set with '[^]'" do + Dir.send(cmd, '[^not]ubdir_one').sort.should == %w|subdir_one|.sort + end + + it "matches any characters except those in a range with '[^-,,...}'" do + Dir.send(cmd, 'subdir_{one,two,three}').sort.should == %w|subdir_one subdir_two|.sort + end + + it "accepts string sets with empty strings with {,,}" do + a = Dir.send(cmd, 'deeply/nested/directory/structure/file_one{.ext,}').sort + a.should == %w|deeply/nested/directory/structure/file_one.ext + deeply/nested/directory/structure/file_one|.sort + end + + it "matches dot or non-dotfiles with '{,.}*'" do + Dir.send(cmd, '{,.}*').sort.should == %w|. .. .dotsubdir subdir_one subdir_two deeply + .dotfile nondotfile file_one.ext file_two.ext|.sort + end + + it "matches special characters by escaping with a backslash with '\\'" do + Dir.mkdir 'foo*bar' + + Dir.glob('foo?bar').should == %w|foo*bar| + Dir.glob('foo\?bar').should == [] + Dir.glob('nond\otfile').should == %w|nondotfile| + + Dir.rmdir 'foo*bar' + end + + it "recursively matches directories with '**/'" do + %w|glob []|.each {|cmd| + Dir.send(cmd, '**/*fil?{,.}*').sort.should == %w|deeply/nested/directory/structure/file_one + deeply/nested/directory/structure/file_one.ext + deeply/nondotfile file_one.ext file_two.ext + nondotfile subdir_one/nondotfile + subdir_two/nondotfile subdir_two/nondotfile.ext + subdir_two/nondotfile.ext| + } + end + after(:all) do + Dir.chdir @cwd + end + end +end + +describe "Dir.glob" do + it_behaves_like(@dir_glob, :glob) + + before(:all) do + @cwd = Dir.pwd + Dir.chdir mock_dir + end + + it "matches both dot and non-dotfiles with '*' and option File::FNM_DOTMATCH" do + Dir.glob('*', File::FNM_DOTMATCH).sort.should == + %w|. .. .dotfile .dotsubdir subdir_one subdir_two deeply nondotfile file_one.ext file_two.ext|.sort + end + + it "matches files with any beginning with '*' and option File::FNM_DOTMATCH" do + Dir.glob('*file', File::FNM_DOTMATCH).sort.should == %w|.dotfile nondotfile|.sort + end + + it "matches any files in the current directory with '.**' and option File::FNM_DOTMATCH" do + Dir.glob('**', File::FNM_DOTMATCH).sort.should == %w|. .. .dotsubdir .dotfile subdir_one subdir_two + deeply nondotfile file_one.ext file_two.ext|.sort + end + + it "recursively matches any subdirectories except './' or '../' with '**/' and option File::FNM_DOTMATCH" do + Dir.glob('**/', File::FNM_DOTMATCH).sort.should == %w|.dotsubdir/ subdir_one/ subdir_two/ deeply/ deeply/nested/ + deeply/nested/directory/ deeply/nested/directory/structure/|.sort + end + + it "matches the literal character '\\' with option File::FNM_NOESCAPE" do + Dir.mkdir 'foo?bar' + + Dir.glob('foo?bar', File::FNM_NOESCAPE).should == %w|foo?bar| + Dir.glob('foo\?bar', File::FNM_NOESCAPE).should == [] + + Dir.mkdir 'foo\?bar' + + Dir.glob('foo\?bar', File::FNM_NOESCAPE).should == %w|foo\\?bar| + + Dir.rmdir 'foo?bar' + Dir.rmdir 'foo\?bar' + end + + after(:all) do + Dir.chdir @cwd + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/initialize_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/initialize_spec.rb new file mode 100644 index 0000000000..eec2922936 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/initialize_spec.rb @@ -0,0 +1,2 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/mkdir_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/mkdir_spec.rb new file mode 100644 index 0000000000..183e294c6d --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/mkdir_spec.rb @@ -0,0 +1,36 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' + +describe "Dir.mkdir" do + it "creates the named directory with the given permissions" do + File.exist?('nonexisting').should == false + Dir.mkdir 'nonexisting' + File.exist?('nonexisting').should == true + + Dir.mkdir 'default_perms' + a = File.stat('default_perms').mode + Dir.mkdir 'reduced', (a - 1) + File.stat('reduced').mode.should_not == a + + Dir.mkdir('always_returns_0').should == 0 + + system "chmod 0777 nonexisting default_perms reduced always_returns_0" + Dir.rmdir 'nonexisting' + Dir.rmdir 'default_perms' + Dir.rmdir 'reduced' + Dir.rmdir 'always_returns_0' + end + + it "raises without adequate permissions in the parent dir" do + Dir.mkdir 'noperms', 0000 + + should_raise(SystemCallError) { Dir.mkdir 'noperms/subdir' } + + system 'chmod 0777 noperms' + Dir.rmdir 'noperms' + end + + it "raises a SystemCallError any of the directories in the path before the last does not exist" do + should_raise(SystemCallError) { Dir.mkdir "#{nonexistent}/subdir" } + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/new_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/new_spec.rb new file mode 100644 index 0000000000..eec2922936 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/new_spec.rb @@ -0,0 +1,2 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/open_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/open_spec.rb new file mode 100644 index 0000000000..f40af06a89 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/open_spec.rb @@ -0,0 +1,33 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' + +@dir_open = shared "Dir.open" do |cmd| + it "returns a Dir instance representing the specified directory" do + dir = Dir.send(cmd, mock_dir) + dir.class.should == Dir + dir.close + end + + it "raises SystemCallError if the directory does not exist" do + should_raise(SystemCallError) { Dir.send cmd, nonexistent } + end + + it "takes a block which yields the Dir instance and closes it after" do + # This is a bit convoluted but we are trying to ensure the file gets closed. + # To do that, we peek to see what the next FD number is and then probe that + # to see whether it has been closed. + peek = IO.sysopen mock_dir + File.for_fd(peek).close + + Dir.send(cmd, mock_dir) { } + should_raise(SystemCallError) { File.for_fd peek } + end + + it "returns the value of the block if a block is passed" do + Dir.open(mock_dir) {|dir| dir.class}.should == Dir + end +end + +describe "Dir.open" do + it_behaves_like @dir_open, :open +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/path_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/path_spec.rb new file mode 100644 index 0000000000..b7f57816f1 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/path_spec.rb @@ -0,0 +1,15 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' +require File.dirname(__FILE__) + '/close_spec' + +describe "Dir#path" do + it "returns the path that was supplied to .new or .open" do + dir = Dir.open mock_dir + dir.path.should == mock_dir + dir.close rescue nil + end +end + +describe "Dir#path" do + it_behaves_like @dir_closed, :path +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/pos_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/pos_spec.rb new file mode 100644 index 0000000000..0028b2c45d --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/pos_spec.rb @@ -0,0 +1,40 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' +require File.dirname(__FILE__) + '/close_spec' + +@dir_pos = shared "Dir#pos" do |cmd| + describe "Dir##{cmd}" do + specify 'Both Dir#pos and Dir#tell give the current dir position' do + @dir = Dir.open mock_dir + + @dir.pos.should == 1 + @dir.tell.should == 2 + @dir.pos.should == 3 + @dir.tell.should == 4 + + @dir.close rescue nil + end + + specify 'Dir#pos= also seeks to a certain position but returns the position number instead' do + @dir = Dir.open mock_dir + + pos = @dir.pos + a = @dir.read + b = @dir.read + ret = @dir.pos = pos + c = @dir.read + + a.should_not == b + b.should_not == c + c.should == a + + ret.should == pos + + @dir.close rescue nil + end + end +end + +describe "Dir#pos" do + it_behaves_like @dir_closed, :pos +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/pwd_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/pwd_spec.rb new file mode 100644 index 0000000000..46cf369a0d --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/pwd_spec.rb @@ -0,0 +1,14 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' + +@dir_pwd = shared "Dir.pwd" do |cmd| + describe "Dir.pwd" do + it "returns the current working directory" do + Dir.send(cmd).should == `pwd`.chomp + end + end +end + +describe "Dir.pwd" do + it_behaves_like @dir_pwd, :pwd +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/read_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/read_spec.rb new file mode 100644 index 0000000000..2bef975107 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/read_spec.rb @@ -0,0 +1,15 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' +require File.dirname(__FILE__) + '/close_spec' + +describe "Dir#read" do + it "returns the file name in the current seek position" do + dir = Dir.open mock_dir + dir.read.should == '.' + dir.close + end +end + +describe "Dir#read" do + it_behaves_like @dir_closed, :read +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/rewind_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/rewind_spec.rb new file mode 100644 index 0000000000..92485643bb --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/rewind_spec.rb @@ -0,0 +1,38 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' +require File.dirname(__FILE__) + '/close_spec' + +describe "Dir#rewind" do + before(:each) do + @dir = Dir.open mock_dir + end + + after(:each) do + @dir.close + end + + it "resets the next read to start from the first entry" do + first = @dir.pos + a = @dir.read + b = @dir.read + prejmp = @dir.pos + ret = @dir.rewind + second = @dir.pos + c = @dir.read + + a.should_not == b + b.should_not == c + c.should == a + + second.should_not == first + second.should_not == prejmp + end + + it "returns the Dir instance" do + @dir.rewind.should == @dir + end +end + +describe "Dir#rewind" do + it_behaves_like @dir_closed, :rewind +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/rmdir_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/rmdir_spec.rb new file mode 100644 index 0000000000..47bdd8b6ec --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/rmdir_spec.rb @@ -0,0 +1,7 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' +require File.dirname(__FILE__) + '/delete_spec' + +describe "Dir.rmdir" do + it_behaves_like @dir_delete, :rmdir +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/seek_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/seek_spec.rb new file mode 100644 index 0000000000..63f61fcb96 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/seek_spec.rb @@ -0,0 +1,28 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' + +describe "Dir#seek" do + before(:each) do + @dir = Dir.open mock_dir + end + + after(:each) do + @dir.close + end + + it "moves the current read position" do + pos = @dir.pos + a = @dir.read + b = @dir.read + ret = @dir.seek pos + c = @dir.read + + a.should_not == b + b.should_not == c + c.should == a + end + + it "returns the Dir instance" do + @dir.seek(0).should == @dir + end +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/tell_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/tell_spec.rb new file mode 100644 index 0000000000..8978e34f5b --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/tell_spec.rb @@ -0,0 +1,12 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' +require File.dirname(__FILE__) + '/close_spec' +require File.dirname(__FILE__) + '/pos_spec' + +describe "Dir#tell" do + it_behaves_like @dir_pos, :tell +end + +describe "Dir#tell" do + it_behaves_like @dir_closed, :tell +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/dir/unlink_spec.rb b/merlin/main/languages/ruby/Tests/Builtin/dir/unlink_spec.rb new file mode 100644 index 0000000000..e3a165b57c --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/dir/unlink_spec.rb @@ -0,0 +1,7 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' +require File.dirname(__FILE__) + '/delete_spec' + +describe "Dir.unlink" do + it_behaves_like @dir_delete, :unlink +end diff --git a/merlin/main/languages/ruby/Tests/Builtin/test_dir.rb b/merlin/main/languages/ruby/Tests/Builtin/test_dir.rb new file mode 100644 index 0000000000..9bcefd8d73 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Builtin/test_dir.rb @@ -0,0 +1,201 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "../Util/simple_test.rb" + +$current = Dir.pwd + +describe "Dir#new" do + it "throws if the new is bad" do + should_raise(Errno::ENOENT) { Dir.new("") } + should_raise(Errno::ENOENT) { Dir.new("testdir/notexisting") } + #should_raise(Errno::ENOTDIR) { Dir.new("testdir/file1.txt") } + should_raise(Errno::ENOENT) { Dir.new("test?dir") } + end + + it "does not work for subdirectory" do + should_raise(Errno::ENOENT) { Dir.new("testdir/folder") } + end + + it "does allow / at the end" do + Dir.new("testdir/").close + end +end + +describe "Dir#getwd" do + it "returns the current directory" do + Dir.getwd.should == $current + end +end + +describe "Dir#chdir" do + skip "changes the current directory to HOME or LOGDIR if set" do + if ENV['HOME'] || ENV['LOGDIR'] + Dir.chdir + else + should_raise(ArgumentError) { Dir.chdir } + end + end + + it "changes to the root directory" do + Dir.chdir("C:/").should == 0 + Dir.getwd.should == "C:/" + + Dir.chdir($current).should == 0 + end + + it "throws if the directory does not exist, or invalid arg" do + saved = Dir.getwd + #should_raise(Errno::ENOENT) { Dir.chdir("C:/notexisting/directory") } + should_raise(Errno::EINVAL) { Dir.chdir("") } + should_raise(Errno::EINVAL) { Dir.chdir("test?dir") } + Dir.getwd.should == saved + end + + it "" do + Dir.chdir("testdir/") + Dir.chdir($current) + end + + skip "takes block" do + saved = Dir.getwd + # TODO: entering try with non-empty stack bug + #Dir.chdir("testdir") { |x| nil }.should == nil + #Dir.chdir("testdir") { |x| x }.should == "testdir" + #Dir.chdir(saved) + end + + Dir.chdir($current) +end + +describe "Dir#foreach" do + it "throws if dirname is wrong" do + should_raise(Errno::ENOENT) { Dir.foreach("C:/notexisting/directory") { |x| x } } + should_raise(Errno::ENOENT) { Dir.foreach("") { |x| x } } + should_raise(Errno::ENOENT) { Dir.foreach("test?dir") { |x| x } } + end + + # TODO: fix this test so that it accounts for the hidden .svn directory in + # the SVN layout tree + skip "calls the block once for each entry" do + l = [] + Dir.foreach("testdir") do |x| + l << x + end + l.should == ['.', '..', 'file1.txt', 'file2.txt', 'folder1'] + end +end + +describe "Dir#glob" do + Dir.chdir("testdir") + + it "returns set of files with different patterns" do + # TODO: similar SVN layout bug + #Dir.glob("*").sort.should == ['file1.txt', 'file2.txt', 'folder1'] + Dir.glob("*.txt").sort.should == ['file1.txt', 'file2.txt'] + Dir.glob("*.xyz").should == [] + end + + skip "takes blocks too" do + # TODO: entering try with non-empty stack bug + # l = [] + # Dir.glob("*1.*") { |x| l << x } .should == nil + # l.should == ['file1.txt'] + end + + Dir.chdir($current) +end + +describe "Dir#each" do + skip "calls the block once for each entry" do + # TODO: entering try with non-empty stack bug + #x = Dir.new("testdir") + + #l = [] + #x.each do |y| + # l << y + #end.should == x + + #l.should == ['.', '..', 'file1.txt', 'file2.txt', 'folder1'] + + #x.close + end +end + +describe "Dir#path" do + it "returns the path parameter" do + x = Dir.open("testdir") + x.path.should == "testdir" + x.close + end +end + +describe "Dir#close" do + it "set some flag; after that, all operations will throw" do + x = Dir.new("testdir") + x.close.should == nil + + should_raise(IOError, "closed directory") { x.close } + should_raise(IOError) { x.path } + should_raise(IOError) { x.each {|x| x } } + should_raise(IOError) { x.pos } + should_raise(IOError) { x.pos = 1 } + should_raise(IOError) { x.read } + should_raise(IOError) { x.seek(1) } + should_raise(IOError) { x.tell } + end +end + +describe "Dir#pos related" do + # TODO: fix test bug related to .svn hidden directory + skip "does the following sequence" do + x = Dir.new("testdir") + x.pos.should == 0 + x.tell.should == 0 + + x.read.should == "." + x.pos.should == 1 + x.read.should == ".." + x.tell.should == 2 + x.tell.should == 2 + + x.read.should == "file1.txt" + x.pos = x.tell + 1 + x.read.should == "folder1" + + # at the end of stream + x.read.should == nil + x.read.should == nil + x.pos.should == 5 + + # set pos + x.pos = 3 + x.read.should == "file2.txt" + + x.rewind.should == x + x.tell.should == 0 + x.seek(4).should == x + x.read.should == "folder1" + + # negative or large number to seek + x.seek(-3).pos.should == 0 + x.seek(-2).pos.should == 0 + x.seek(5).pos.should == 5 + x.seek(6).pos.should == 5 + x.seek(106).pos.should == 5 + end +end + +finished diff --git a/merlin/main/languages/ruby/Tests/Builtin/testdir/file1.txt b/merlin/main/languages/ruby/Tests/Builtin/testdir/file1.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Builtin/testdir/file2.txt b/merlin/main/languages/ruby/Tests/Builtin/testdir/file2.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Compat/compat_common.py b/merlin/main/languages/ruby/Tests/Compat/compat_common.py new file mode 100644 index 0000000000..d4aa99d05f --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/compat_common.py @@ -0,0 +1,122 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +def replace_A(line): + count = 0 + while True: + pos = line.find("A") + if pos == -1: break + line = line[:pos] + chr(ord('a') + count) + line[pos+1:] + count += 1 + return line, count + +def replace_B(line): + count = 0 + while True: + pos = line.find("B") + if pos == -1: break + line = line[:pos] + str(1 + count) + line[pos+1:] + count += 1 + return line + +def concat_char(i): + return ",".join(["abcdefghijklmnopqrstuvwxyz"[x] for x in range(i)]) + + +SPACE_LIMIT = 10 +def _build_space_hash(): + h = {} + for i in range(SPACE_LIMIT): h[i] = " " * i + return h + +_space_hash = _build_space_hash() +def space(d): + if d < SPACE_LIMIT: return _space_hash[d] + return " " * d + +class FileCreator: + def __init__(self, prefix, size, header=""): + from datetime import datetime + now = datetime.now() + import nt + if not nt.environ.has_key("ROWAN_BIN"): + self.directory = prefix + else: + self.directory = "%s_%02d%02d_%02d%02d_%d" % (prefix, now.month, now.day, now.hour, now.minute, now.microsecond) + self.size = size + self.header = header + self.count = 0 + self.func_count = 0 + self.file_list = [] + self.func_lines = [] + self.current_file = None + + try: + nt.mkdir(self.directory) + except: pass + + def save_block(self, *lines): + self.save(False, *lines) + + def save_any(self, *lines): + self.save(True, *lines) + + def save(self, do_not_counting, *lines): + if self.count % self.size == 0: + self.close() + + filename = "%s\\%s.rb" % (self.directory, self.count) + self.file_list.append(filename) + + self.current_file = open(filename, 'w') + self.current_file.writelines(self.header + "\n") + + if not do_not_counting: + self.count += 1 + + for x in lines: + self.current_file.writelines(x + "\n") + + def save_to_function(self, line): + self.func_lines.append(line) + + if self.count % (self.size * self.func_size) == 0: + self.close() + + filename = "%s\\%s.rb" % (self.directory, self.count) + self.file_list.append(filename) + + self.current_file = open(filename, 'w') + self.current_file.writelines(self.header + "\n") + + if len(self.func_lines) >= self.func_size: + self.current_file.writelines("def f_%s\n" % self.func_count) + for x in self.func_lines: + self.current_file.writelines(x + "\n") + self.current_file.writelines("end\n") + self.current_file.writelines("f_%s\n" % self.func_count) + self.func_count += 1 + self.func_lines = [] + + self.count += 1 + + def close(self): + if self.current_file and not self.current_file.closed: + self.current_file.close() + + def print_file_list(self): + print "#Total: %s files" % len(self.file_list) + for x in self.file_list: + print x diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_ancestor_graph.py b/merlin/main/languages/ruby/Tests/Compat/gen_ancestor_graph.py new file mode 100644 index 0000000000..c50da03680 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_ancestor_graph.py @@ -0,0 +1,267 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# Try to generate the test which represents the specified module/class layout +# We verify the ancestors, and try to call class/module methods and access the constants. + +from compat_common import * +count = 0 +class Entity(object): + def __init__(self, name): + self.name = name + self.parent = None + self.mixins = [] + self.identifiers = {} + self.dependency = [] + + def include(self, *mixins): + self.mixins = mixins + + def reset_mixin(self): + self.mixins = [] + + def get_body(self): + body = "" + if self.parent: + body += "%s %s < %s\n" % (self.keyword, self.name, self.parent.name) + else: + body += "%s %s\n" % (self.keyword, self.name) + if self.mixins: + body += " include %s\n" % ", ".join([x.name for x in self.mixins]) + for (k,v) in self.identifiers.iteritems(): + body += " def m_%s; %s; end\n" % (k, v) + for (k,v) in self.identifiers.iteritems(): + body += " C_%s = %s\n" % (k, v) + body += "end\n" + return body + + def __str__(self): + return self.name + + def generate_ids(self, available_entities): + global count + + self.identifiers["%s_only" % self.name] = count + count += 1 + + for x in available_entities: + if x == self: continue + + if x.name > self.name: + t = (x.name, self.name) + else: + t = (self.name, x.name) + + self.identifiers["%s_%s" % t] = count + count += 1 + +class Module(Entity): + def __init__(self, name): + super(Module, self).__init__(name) + self.keyword = "module" + +class Class(Entity): + def __init__(self, name): + super(Class, self).__init__(name) + self.keyword = "class" + def derive_from(self, parent): + self.parent = parent + +# globals +class_count = 7 +module_count = 10 + +# create modules +m_kernel = Module("Kernel") +for x in range(1, module_count): exec "m%s = Module('M%s')" % (x, x) + +# create classes with fixed parent-child relationships +c_object = Class("Object") +for x in range(1, class_count): exec "c%s = Class('C%s')" % (x, x) + +# c1 +# / \ +# c2 c6 +# / +# c3 +# / \ +# c4 c5 + +c2.derive_from(c1) +c3.derive_from(c2) +c4.derive_from(c3) +c5.derive_from(c3) +c6.derive_from(c1) + +user_defined_classes = [eval("c%s" % x) for x in range(1, class_count)] +user_defined_modules = [eval("m%s" % x) for x in range(1, module_count)] +all_entities = [ m_kernel, c_object ] +all_entities.extend(user_defined_classes) +all_entities.extend(user_defined_modules) +all_classes = [ x for x in all_entities if isinstance(x, Class) ] +all_modules = [ x for x in all_entities if isinstance(x, Module) ] + +def get_header(identifiers): + header = '''def check(l1, l2) + cnt = 0 + puts "************************#{l2.inspect}***********************" + l1.each do |c| + x = c.new + y = l2[cnt] + cnt += 1 + puts "===================#{y}=======================" + puts "-------------ancestors: #{c.ancestors.inspect}--------" +''' + + for x in identifiers: + header += " puts 'm_%s'; puts x.m_%s rescue puts 'error'\n" % (x, x) + for x in identifiers: + header += " puts 'C_%s'; puts c::C_%s rescue puts 'error'\n" % (x, x) + + header += ' end\nend\n' + return header + +fc = FileCreator("test_ancestor_graph", 1) + +def merge_dict(*dicts): + d = {} + for x in dicts: d.update(x) + return d + +module_layout1 = { + m2 : [m1], + m4 : [m1, m3], m5 : [m3, m1], + m6 : [m2, m1], m7 : [m1, m2], + m8 : [m6, m1, m4], +} + + +# m1 m3 +# /|\ / +# m2 | m4/m5 +# \ | \| +# m6/m7| +# \| +# m8 + +# c1 +# / \ +# c2 c6 +# / +# c3 +# / \ +# c4 c5 + +for (sequence, global_include) in [ + # hand-picked scenarios + ( + merge_dict(module_layout1, { c1 : [m2] }), + { c3 : [m3]} + ), + ( + merge_dict(module_layout1, { m_kernel : [m9], c3 : [m5] }), + { c3 : [m_kernel, m4]} + ), + ( + merge_dict(module_layout1, { c2 : [m4], c6 : [m6] }), + { m4 : [m2], c4 : [m4]} + ), + ( + merge_dict(module_layout1, { c1 : [m5], c3 : [m1], c_object : [m3] }), + { } + ), + ( + merge_dict(module_layout1, { c4 : [m6, m3], c2 : [m1]}), + { m5 : [m3]} + ), +] : + current_entities = all_entities[:] + + # reset the class mixins + for e in current_entities: + e.reset_mixin() + + # set new mixins + for (k, v) in sequence.iteritems(): + k.include(*v) + + # get nessary modules + included_modules = set() + for x in all_classes: + for y in x.mixins: included_modules.add(y) + + for y in m_kernel.mixins: included_modules.add(y) + + for (k, v) in global_include.iteritems(): + if isinstance(k, Module): + included_modules.add(k) + for y in v: + included_modules.add(y) + + # omg!! + while True: + copy = included_modules.copy() + l1 = len(copy) + + for x in included_modules: + for y in x.mixins: + copy.add(y) + + l2 = len(copy) + if l1 == l2: break + + included_modules = copy.copy() + + current_entities = list(included_modules) + current_entities.sort(lambda x, y: (x.name < y.name) and -1 or 1) + + # tweak Kernel location: Kernel will be first if currently present + if m_kernel.mixins: + current_entities.remove(m_kernel) + current_entities.append(m_kernel) + else: + current_entities.insert(0, m_kernel) + + current_entities.extend(all_classes) + + scenario = ",".join([x.name for x in current_entities]) + + current_identifiers = set() + for e in current_entities: + e.generate_ids(current_entities) + for y in e.identifiers.keys(): + current_identifiers.add(y) + + lines = "## %s\n\n" % scenario + lines += get_header(current_identifiers) + + cx = [] + for e in current_entities: + lines += e.get_body() + + if e.name.startswith("C"): + cx.append(e.name) + lines += "check [%s], %s\n" % (','.join(cx), cx) + + if e in global_include.keys(): + lines += "include " + ",".join([x.name for x in global_include[e]]) + "\n" + + if e.name.startswith("C"): + lines += "check [%s], %s\n" % (','.join(cx), cx) + + fc.save_block(lines) + +fc.close() +fc.print_file_list() \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_assignment.py b/merlin/main/languages/ruby/Tests/Compat/gen_assignment.py new file mode 100644 index 0000000000..f51a31ccc1 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_assignment.py @@ -0,0 +1,78 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +from compat_common import * + +left_side = ''' +A +A,A +*A +A,*A +(A,) +(A,A) +(A,*A) +(*A) +(A,A),A +'''.split() + +right_side = ''' +nil +[] +[nil] +B +B,B +B,B,B +[B] +[B,B] +[B,B,B] +*B +*nil +*[] +*[nil] +*[B] +*[B,B] +B,*[B,B] +*[[]] +B,*[] +[B,B],B +nil,B +'''.split() + +lines = [] +fc = 0 + +print ''' +def p *a + a.each { |x| print x.inspect, '; ' } + puts +end +''' + +for x in left_side: + left, vars_num = replace_A(x) + vars_text = concat_char(vars_num) + + print "def f%s()" % fc + for y in right_side: + right = replace_B(y) + + print " %s = 0; puts 'repro: %s = %s'; %s = %s; p %s" % (vars_text, left, right, left, right, vars_text) + + print "end" + fc += 1 + +for x in range(fc): + print "f%s()" % x + diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_assignment_long.py b/merlin/main/languages/ruby/Tests/Compat/gen_assignment_long.py new file mode 100644 index 0000000000..c5fb9055d9 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_assignment_long.py @@ -0,0 +1,79 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +from compat_common import * + +# left = A | (A, ) | (*A) | *(A) | (A, left) | A, left +# | *A | *(A, left) # has to be the last + +def left_generator(depth): + if depth > 4: return + + for x in ["A", "(A,)", "(*A)", "*A"]: + yield x + + #for x in left_generator(depth + 1): + # yield "*(A, %s)" % x + + for x in left_generator(depth + 1): + yield "A, %s" % x + + for x in left_generator(depth + 1): + yield "(A, %s)" % x + + +# right = B | [] | *[] | B, right | *[right] | [right] + +def right_generator(depth): + if depth > 5: return + + for x in ["[]", "*[]", "B"]: + yield x + + for x in right_generator(depth+1): + yield "B, %s" % x + + for x in right_generator(depth+1): + yield "[%s]" % x + + for x in right_generator(depth+1): + yield "*[%s]" % x + +fc = FileCreator("test_assignment", 40, ''' +def p *a + a.each { |x| print x.inspect, '; ' } + puts +end +''') + +fc.func_size = 40 + +for x in left_generator(0): + left, vars_num = replace_A(x) + vars_text = concat_char(vars_num) + + for y in right_generator(0): + right = replace_B(y) + fc.save_to_function(" %s = 0; puts 'repro: %s = %s'; %s = %s; p %s" % (vars_text, left, right, left, right, vars_text)) + #fc.save_to_function(" %s = 0; def f; yield %s; end; p(f {|%s| [%s] }); " % (vars_text, right, left, vars_text)) + +for y in right_generator(0): + right = replace_B(y) + fc.save_to_function(" p(while true; break %s; end)" % (right, )) + fc.save_to_function(" p([%s])" % (right, )) + +fc.close() +fc.print_file_list() + diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_block_ctrl_flow.bat b/merlin/main/languages/ruby/Tests/Compat/gen_block_ctrl_flow.bat new file mode 100644 index 0000000000..84066ac85f --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_block_ctrl_flow.bat @@ -0,0 +1,18 @@ +REM **************************************************************************** +REM +REM Copyright (c) Microsoft Corporation. +REM +REM This source code is subject to terms and conditions of the Microsoft Public License. A +REM copy of the license can be found in the License.html file at the root of this distribution. If +REM you cannot locate the Microsoft Public License, please send an email to +REM ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +REM by the terms of the Microsoft Public License. +REM +REM You must not remove this notice, or any other, from this software. +REM +REM +REM **************************************************************************** + +set PYTHON_EXE=%MERLIN_ROOT%\bin\debug\ipy.exe +%PYTHON_EXE% gen_block_ctrl_flow_first.py > template_block_ctrl_flow.rb +%PYTHON_EXE% gen_block_ctrl_flow_long.py %* \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_block_ctrl_flow_first.py b/merlin/main/languages/ruby/Tests/Compat/gen_block_ctrl_flow_first.py new file mode 100644 index 0000000000..6f2c7a0076 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_block_ctrl_flow_first.py @@ -0,0 +1,141 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# at +# top level code +# | method call +# | eval call (TODO) +# | block +# | proc +# | lambda +# | loop + +# use the +# block +# | lambda +# | Proc +# and check the return value + +# which is +# locally created +# | locally returned from a method call +# | passed as argument + +# return inside body and ensure + +print ''' +# helper +def myeval(line); puts B; eval(line); puts B; end +def call1(x); puts B; call2(x); puts B; end +def call2(x); puts B; call3(x); puts B; end +def call3(x); puts B; puts x.call; puts B; end + +# producer + +def get_block(&p); p; end +def get_lambda(&p); lambda(&p); end +def get_proc(&p); Proc.new(&p); end + +def get_local_block; get_block { puts B; ctrl_flow; puts B }; end +def get_local_lambda; lambda { puts B; ctrl_flow; puts B }; end +def get_local_proc; Proc.new { puts B; ctrl_flow; puts B }; end + +# consumer + +# taking arguments +def iterator_via_yield; puts B; x = yield; puts x; puts B; end +def iterator_via_call(&p); puts B; puts(p.call); puts B; end + +def method_call_iterator_via_yield(&p); puts B; iterator_via_yield(&p); puts B; end +def method_call_iterator_via_call(&p); puts B; iterator_via_call(&p); puts B; end + +def method_use_lambda_and_yield; puts B; x = lambda { puts B; yield; puts B }; puts x.call; puts B; end +def method_use_proc_and_yield; puts B; x = Proc.new { puts B; yield; puts B }; puts x.call; puts B; end +def method_use_lambda_and_call(&p); puts B; x = lambda { puts B; p.call; puts B }; puts x.call; puts B; end +def method_use_proc_and_call(&p); puts B; x = Proc.new { puts B; p.call; puts B }; puts x.call; puts B; end + +def method_use_lambda_and_yield_2; puts B; x = lambda { puts B; yield; puts B }; call1(x); puts B; end + +def method_yield_in_loop; puts B; for i in [1, 2]; puts B; yield; puts B; end; puts B; end +def method_call_in_loop(&p); puts B; for i in [3, 4]; puts B; p.call; puts B; end; puts B; end +''' + +methods_take_argument = ''' + iterator_via_yield iterator_via_call + method_call_iterator_via_yield method_call_iterator_via_call + method_use_lambda_and_yield method_use_proc_and_yield method_use_lambda_and_call method_use_proc_and_call + method_use_lambda_and_yield_2 + method_yield_in_loop method_call_in_loop'''.split() + +s = "{ puts B; ctrl_flow; puts B}" +i = 0 + +print "# created in-place" + +print "def test" +for x in methods_take_argument: + t1 = "$g = 0; begin; puts B; %s %s; puts B; rescue; puts B; puts $!.class; end" % (x, s) + i += 1 + t2 = "$g = 0; def m_%s; puts B; %s; puts B; end; m_%s " % (i, t1, i) + print t1 + print t2 + +print "end" +print "test" +print + +print "\n# created locally or from method" + +for x in [ 'lambda' + s, 'Proc.new'+s, 'get_block' + s, 'get_lambda' +s, 'get_proc' +s, 'get_local_block', 'get_local_lambda', 'get_local_proc']: + print "def test" + for y in methods_take_argument: + print "$g = 0; begin; p = %s; puts B; %s(&p); puts B; rescue; puts B; puts $!.class; end" % (x, y) + i += 1 + print "$g = 0; def m_%s; p = %s; puts B; %s(&p); puts B; end; \nbegin; puts B; m_%s; puts B; rescue; puts B; puts $!.class; end" % (i, x, y, i) + print "end" + print "test" + print + +print "def test" +for x in ['lambda' + s, 'Proc.new'+s, 'get_block' + s, 'get_lambda' +s, 'get_proc' +s, 'get_local_block', 'get_local_lambda', 'get_local_proc']: + print "$g = 0; begin; puts B; p = %s; puts(p.call); puts B; rescue; puts B; puts $!.class; end" % x + i += 1 + print "$g = 0; def m_%s; puts B; p = %s; puts(p.call); puts B; end; \nbegin; puts B; m_%s; puts B; rescue; puts B; puts $!.class; end" % (i, x, i) + print "$g = 0; begin; puts B; puts m_%s; puts B; rescue; puts B; puts $!.class; end" % i + i += 1 + print "$g = 0; def m_%s; puts B; puts m_%s; puts B; end; \nbegin; puts B; m_%s; puts B; rescue; puts B; puts $!.class; end" % (i, i-1, i) + +print "end" +print "test" +print + +for x in 'lambda Proc.new get_block get_lambda get_proc'.split(): + print "def test" + for y in ['lambda' + s, 'Proc.new'+s, 'get_block' + s, 'get_lambda' +s, 'get_proc' +s, 'get_local_block', 'get_local_lambda', 'get_local_proc']: + print "$g = 0; begin; puts B; x = %s { puts B; p = %s; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end" % (x, y) + i += 1 + print '$g = 0; def m_%s; puts B; x = %s { puts B; p = %s; puts p.call; puts B}; puts x.call; puts B; end; \nbegin; puts B; m_%s; puts B; rescue; puts B; puts $!.class; end' % (i, x, y, i) + print "end" + print "test" + print + +print "def test" +for x in ['lambda' + s, 'Proc.new'+s, 'get_block' + s, 'get_lambda' +s, 'get_proc' +s, 'get_local_block', 'get_local_lambda', 'get_local_proc']: + print '$g = 0; begin; puts B; for i in [1, 2]; puts B; p = %s; puts p.call; puts B; end; puts B; rescue; puts B; puts $!.class; end' % (x) + i += 1 + print '$g = 0; def m_%s; puts B; for i in [1, 2]; puts B; p = %s; puts p.call; puts B; end; puts B; end;\nbegin; puts B; m_%s; puts B; rescue; puts B; puts $!.class; end' % (i, x, i) + +print "end" +print "test" \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_block_ctrl_flow_long.py b/merlin/main/languages/ruby/Tests/Compat/gen_block_ctrl_flow_long.py new file mode 100644 index 0000000000..c0f1f601db --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_block_ctrl_flow_long.py @@ -0,0 +1,68 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +from compat_common import * + +f = open("template_block_ctrl_flow.rb") +line = "".join(f.readlines()) +f.close() + +import sys + +if len(sys.argv) > 1 and sys.argv[1] == "main": + mapping = { + 'puts "A"' : "normal", + 'raise IOError' : "raise", + 'return' : "return", + 'break' : "break", + 'next' : "next", + '$g += 1; retry if $g < 4;' : "retry", + '$g += 1; redo if $g < 4;' : "redo", + } + + for (x, y) in mapping.iteritems(): + f = file("test_block_ctrl_flow_%s.rb" % y, "w") + new_line = replace_B(line.replace("ctrl_flow", x)) + f.writelines(new_line) + f.close() +else: + extras = [ + 'return 41', + 'break 42', + + 'if 2 == 1; return "B"; end', + 'if 4 == 4; return "B"; end', + + 'true and return "B"', + 'false or return "B"', + + #'eval("next")', + #'eval("break")', + #'eval("return")', + #'eval("$g += 1; retry if $g < 4;")', + #'eval("$g += 1; redo if $g < 4;")', + #'eval("if 1==2; return; end")', + + #'myeval("break")', + ] + + fc = FileCreator("test_block_ctrl_flow", 1) + + for x in extras: + new_line = replace_B(line.replace("ctrl_flow", x)) + fc.save_block(new_line) + + fc.close() + fc.print_file_list() diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_ctrl_flow.py b/merlin/main/languages/ruby/Tests/Compat/gen_ctrl_flow.py new file mode 100644 index 0000000000..0e61b6b13c --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_ctrl_flow.py @@ -0,0 +1,54 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +from compat_common import * + +#IF_EXPR = if CONDITION then +# STMT +# ELSIF_EXPR +# ELSE_EXPR +# end + +#UNLESS_EXPR = unless CONDITION then +# STMT +# ELSE_EXPR +# end + +#ELSIF_EXPR = | elsif CONDITION then +# STMT +# ELSIF_EXPR +#ELSE_EXPR = | else +# STMT + +#IF_MODIFIER_EXPR = STMT if CONDITION +#UNLESS_MODIFIER_EXPR = STMT unless CONDITION + +#STMT = | print B | return | IF_EXPR | UNLESS_EXPR | IF_MODIFIER_EXPR | UNLESS_MODIFIER_EXPR | begin; print B; end + +#WHILE_EXPR = while CONDITION do +# STMT_FOR_LOOP +# end +#WHILE_MODIFIER_EXPR = STMT_FOR_LOOP while CONDITION + +#UNTIL_EXPR = until CONDITION do +# STMT_FOR_LOOP +# end +#UNTIL_MODIFIER_EXPR = STMT_FOR_LOOP until CONDITION + +#FOR_EXPR = for x in EXPR do +# STMT_FOR_LOOP +# end + +#STMT_FOR_LOOP = STMT | break | redo | next | retry diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_ctrl_flow.rb b/merlin/main/languages/ruby/Tests/Compat/gen_ctrl_flow.rb new file mode 100644 index 0000000000..eb5780aa92 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_ctrl_flow.rb @@ -0,0 +1,167 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "pp" +require "compat_common" + + +def one_level_loop + bool_seq = Bool_Sequence.new(64) + cb = Combination.new([1, 3, 5,], ['next', 'break', 'retry', 'redo']) + + ['while', 'until'].each do |keyword| + bool_seq.each do |bool_sequence| + cb.generate do |number, flow| + t = "bools =" + PP.pp(bool_sequence + [keyword == 'while' ? false : true] * 2, t) + + t += "i = 0 +$g = \"\" +#{keyword} bools[i] do + i += 1 + $g += \"a1\" + if i == #{number} + $g += \"#{flow[0,1]}1\" + #{flow} + $g += \"#{flow[0,1]}2\" + end + $g += \"a2\" +end +puts $g +" + yield t + end + + cb.generate2 do |number1, flow1, number2, flow2| + t = "bools =" + PP.pp(bool_sequence + [keyword == 'while' ? false : true] * 3, t) + + t += "i = 0 +$g = \"\" +#{keyword} bools[i] do + i += 1 + $g += \"a1\" + + if i == #{number1} + $g += \"#{flow1[0,1]}1\" + #{flow1} + $g += \"#{flow1[0,1]}2\" + end + + $g += \"a2\" + + if i == #{number2} + $g += \"#{flow2[0,1]}1\" + #{flow2} + $g += \"#{flow2[0,1]}2\" + end + + $g += \"a3\" +end +puts $g +" + yield t + end + end + end +end + + +def three_level_loop + bs2 = Bool_Sequence.new(8) + bs3 = Bool_Sequence.new(8) + cb1 = Combination.new([1, 2, 3,], ['next', 'break', 'retry', 'redo']) + cb2 = Combination.new([1, 2, 3,], ['next', 'break', 'retry', 'redo']) + + [[true, true,], ].each do |bool_sequence1| + bs2.each do |bool_sequence2| + bs3.each do |bool_sequence3| + cb1.generate do |number1, flow1| + cb2.generate do |number2, flow2| + t = "bools1 = " + PP.pp(bool_sequence1 + [false] * 2, t) + t += "bools2 = " + PP.pp(bool_sequence2 + [true] * 2, t) + t += "bools3 = " + PP.pp(bool_sequence3 + [false] * 2, t) + + t += " +$g = \"\" +i = 0 +while bools1[i] do + i += 1 + $g += \"a1\" + + j = 0 + until bools2[j] do + j += 1 + $g += \"b1\" + + if j == #{number1} + $g += \"#{flow1[0,1]}1\" + #{flow1} + $g += \"#{flow1[0,1]}2\" + end + + k = 0 + while bools3[k] + k += 1 + $g += \"c1\" + if k == #{number2} + $g += \"#{flow2[0,1]}1\" + #{flow2} + $g += \"#{flow2[0,1]}2\" + end + $g += \"c2\" + end + $g += \"b2\" + + end + $g += \"a2\" +end +puts $g + " + + yield t + end + end + end + end + end +end + + +total = 0 + +p = lambda do |t| + total += 1 + results = GenFile.create_temporaily(t).run() + if results[0] == 0 + GenFile.create_sequentially(t + "\nraise if $g != \"#{results[1]}\"\n") + else + tf = GenFile.create_sequentially(t + "\n#expected to fail\n") + GenFile.add_negative_test(tf) + end +end + +GenFile.prepare("ctlf_one_") +one_level_loop &p +GenFile.finish + +GenFile.prepare("ctlf_three_") +three_level_loop &p +GenFile.finish + +printf("total generated: %s", total) diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_exception1_long.py b/merlin/main/languages/ruby/Tests/Compat/gen_exception1_long.py new file mode 100644 index 0000000000..5910228c08 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_exception1_long.py @@ -0,0 +1,150 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +from compat_common import * + +# exception_stmt = +# "begin" +# statement +# rescue_block +# else_block +# ensure_block +# "end" + +# rescue_block = +# +# | "rescue" +# statement +# | "rescue (1+'1')" +# statement + +# else_block = +# +# | "else" +# statement + +# ensure = +# | "ensure" +# statement + +# statement = +# +# | "print B" +# | raise "B" +# | 1/0 +# | "retry" +# | "next" +# | "return" +# | puts $!.class +# | exception_stmt + +max_depth = 4 + +def exception_stmt_generator(depth): + if depth > max_depth: + return + + for rescue_b in rescue_block_generator(depth): + for else_b in else_block_generator(depth): + # warning: else without rescue is useless + #if rescue_b.lstrip().startswith("#empty") and not else_b.lstrip().startswith("#empty"): + # continue + + for ensure_b in ensure_block_generator(depth): + for body_b in statement_generator(depth+1): + s = space(depth) + "print B, $!.class\n" + s += space(depth) + "begin\n" + s += body_b + s += rescue_b + s += else_b + s += ensure_b + s += space(depth) + "end\n" + s += space(depth) + "print B, $!.class\n" + yield s + +def rescue_block_generator(depth): + if depth > max_depth: return + + yield space(depth) + "#empty_rescue\n" + yield space(depth) + "rescue (print B, $!.class; raise TypeError; IOError)\n" + space(depth+1) + "puts $!.class\n" + + for stmt_b in statement_generator(depth+1): + s = space(depth) + "rescue => ex\n" + s += space(depth+1) +"print B, ex.class, $!.class\n" + s += stmt_b + s += space(depth+1) +"print B, ex.class, $!.class\n" + yield s + + for stmt_b in statement_generator(depth+1): + s = space(depth) + "rescue IOError => ex\n" + s += space(depth+1) +"print B, ex.class, $!.class\n" + s += stmt_b + s += space(depth+1) +"print B, ex.class, $!.class\n" + yield s + +def else_block_generator(depth): + if depth > max_depth: return + + yield space(depth) + "#empty_else\n" + for stmt_b in statement_generator(depth+1): + s = space(depth) + "else\n" + s += space(depth+1) + "print B, $!.class\n" + s += stmt_b + s += space(depth+1) + "print B, $!.class\n" + yield s + +def ensure_block_generator(depth): + if depth > max_depth: return + + yield space(depth) + "#empty_ensure\n" + for stmt_b in statement_generator(depth+1): + s = space(depth) + "ensure\n" + s += space(depth+1) + "print B, $!.class\n" + s += stmt_b + s += space(depth+1) + "print B, $!.class\n" + yield s + +def statement_generator(depth): + if depth > max_depth: return + + for x in [ + "#empty_stmt", + "raise \"B\"", + "raise SyntaxError.new", + "1/0", + "return B", + "break B", + "while true; print B; raise \"B\";end", + "$g += 1; if $g < 4; print B; retry; print B; end;" + ]: + yield space(depth) + x + "\n" + + for x in exception_stmt_generator(depth+1): + yield x + + +fc = FileCreator("test_exception1", 400) +count = 0 + +for x in exception_stmt_generator(1): + line1 = "def f_%s\n" % count + replace_B(x) + "end\n" + line1 += "$g = 0; begin; print(f_%s); rescue Exception; print $!.class; end; puts \" : f_%s\"\n" % (count, count) + count += 1 + + fc.save_block(replace_B(line1)) + +fc.close() +fc.print_file_list() + diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_exception2_long.py b/merlin/main/languages/ruby/Tests/Compat/gen_exception2_long.py new file mode 100644 index 0000000000..6a81ada897 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_exception2_long.py @@ -0,0 +1,130 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# rescue_clause = empty | rescue_default | rescue_E | rescue_which_raise_E +# else_clause = empty | "else" +# ensure_clause = empty | "ensure" +# body = not_raise | raise_E + +class RescueInfo: + def __init__(self, present, except_type, raise_in_param): + self.present = present + self.except_type = except_type + self.raise_in_param = raise_in_param + + def get_code(self): + if self.present: + if self.raise_in_param: + s = "rescue (puts B, $!.class; raise IOError)" + else: + s = "rescue " + if self.except_type: + s += self.except_type + + for x in possible_bodies: + yield s + "; \n" + x + else: + yield "#empty_rescue" + +class ElseInfo: + def __init__(self, present): + self.present = present + def get_code(self): + if self.present: + for x in possible_bodies: + yield "else; \n" + x + else: + yield "#empty_else" + +class EnsureInfo: + def __init__(self, present): + self.present = present + def get_code(self): + if self.present: + for x in possible_bodies: + yield "ensure; \n" + x + else: + yield "#empty_ensure" + +class EH: + def __init__(self, rescue_info, else_info, ensure_info): + self.rescue_info = rescue_info + self.else_info = else_info + self.ensure_info = ensure_info + + def get_code(self): + for x in possible_bodies: + for ri in self.rescue_info.get_code(): + for eli in self.else_info.get_code(): + for eni in self.ensure_info.get_code(): + s = "begin\n" + s += x + "\n" + s += ri + "\n" + s += eli + "\n" + s += eni + "\n" + s += "end\n" + yield s + +def rescueinfo_generator(): + for x in [True, False]: + for y in [None, "ZeroDivisionError", "IOError"]: + for z in [True, False]: + yield RescueInfo(x, y, z) + +def elseinfo_generator(): + for x in [True, False]: + yield ElseInfo(x) + +def ensureinfo_generator(): + for x in [True, False]: + yield EnsureInfo(x) + +possible_bodies = [""] + map(lambda x : " puts B, $!.class;" + x, [ + "1/0", + "raise TypeError", + "raise SyntaxError.new", + "return 3", + "break", + "next", + "$g += 1; retry if $g < 4;", + "$g += 1; redo if $g < 4;" + + "begin; puts B, $!.class; raise ArgumentError; rescue TypeError; puts B; end", + "begin; puts B, $!.class; ensure; puts B, $!.class; raise TypeError; puts B; end", + "$! = nil", + "$! = IOError.new", + "$! = TypeError.new", + ]) + +# another body set +# possible_bodies = + +from compat_common import * + +fc = FileCreator("test_exception2", 200) +count = 0 + +for x in rescueinfo_generator(): + for y in elseinfo_generator(): + for z in ensureinfo_generator(): + for w in EH(x, y, z).get_code(): + line1 = "def f_%s\n" % count + w + "puts B, $!.class\nend\n" + line1 += "$g = 0; begin; print(f_%s); rescue Exception; puts B, $!.class; end; puts \" : f_%s\"\n" % (count, count) + count += 1 + + fc.save_block(replace_B(line1)) + +fc.close() +fc.print_file_list() diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_func_splat.py b/merlin/main/languages/ruby/Tests/Compat/gen_func_splat.py new file mode 100644 index 0000000000..93810cf68c --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_func_splat.py @@ -0,0 +1,89 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +from compat_common import * + +callee = """ +A +*A +A,A +A,*A +A,A,A +A,A,*A +""".split() + +caller = """ +nil +*nil +*[nil] +nil,nil +nil,nil,nil +nil,*[nil] +B,nil +[] +B +B,B +B,B,B +B,[] +B,[B] +B,[B,B] +[[]] +[B] +[B,B] +[B,B,B] +[B,B,*[B]] +*[] +*B +B,*B +B,B,*B +B,*[] +B,*[B] +B,*[B,B] +*[[]] +*[B] +*[B,B] +*[B,B,B] +*[B,[B,B]] +*[B,*[B,B]] +nil,*B +""".split() + +print ''' +def p *a + a.each { |x| print x.inspect, '; ' } + puts +end +''' + +count = 0 +for x in callee: + parameters, vars_num = replace_A(x) + vars_text = concat_char(vars_num) + + print "def test_%s" % count + count += 1 + + print " def f(%s)" % parameters + print " return %s" % vars_text + print " end" + + for y in caller: + arguments = replace_B(y) + print " begin; puts \"repro:%s\"; p f(%s); rescue ArgumentError; puts('except'); end;" % ("f(%s)" % arguments, arguments); + + print "end\n" + +for x in range(count): + print "test_%s" % x diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_if_unless.py b/merlin/main/languages/ruby/Tests/Compat/gen_if_unless.py new file mode 100644 index 0000000000..9418be5fe0 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_if_unless.py @@ -0,0 +1,128 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +from compat_common import * + +#IF_EXPR = if CONDITION then +# STMT +# ELSIF_EXPR +# ELSE_EXPR +# end + +#UNLESS_EXPR = unless CONDITION then +# STMT +# ELSE_EXPR +# end + +#ELSIF_EXPR = | elsif CONDITION then +# STMT +# ELSIF_EXPR +#ELSE_EXPR = | else +# STMT + +#IF_MODIFIER_EXPR = STMT if CONDITION +#UNLESS_MODIFIER_EXPR = STMT unless CONDITION + +#STMT = | print B | break | redo | next | retry | IF_EXPR | UNLESS_EXPR | IF_MODIFIER_EXPR | UNLESS_MODIFIER_EXPR | begin; print B; end + +max_depth = 4 +def space(d): return " " * d + +def if_expr_generator(depth): + if depth > max_depth: return + + for x in stmt_generator(depth+1): + for y in elsif_expr_generator(depth+1): + for z in else_expr_generator(depth+1): + s = space(depth) + "if A then\n" + s += x + s += y + s += z + s += space(depth) + "end" + yield s + "\n" + +def unless_expr_generator(depth): + if depth > max_depth: return + + for x in stmt_generator(depth+1): + for y in else_expr_generator(depth+1): + s = space(depth) + "unless A then\n" + s += x + s += y + s += space(depth) + "end" + yield s + "\n" + +def elsif_expr_generator(depth): + if depth > max_depth: return + + yield space(depth) + "#empty_elsif\n" + for x in stmt_generator(depth+1): + for y in elsif_expr_generator(depth+1): + s = space(depth) + "elsif A then\n" + s += x + s += y + yield s + "\n" + +def else_expr_generator(depth): + if depth > max_depth: return + + yield space(depth) + "#empty_else\n" + for x in stmt_generator(depth+1): + s = space(depth) + "else\n" + s += x + yield s + "\n" + +def stmt_generator(depth): + if depth > max_depth: return + + for x in [ "#empty_stmt", + "print B", + "return B", + "print B; break; print B", + "print B; redo; print B", + "print B; next; print B", + "print B; retry; print B", + "begin; print B; end;" + ]: + yield space(depth) + x + "\n" + + for x in if_expr_generator(depth+1): + yield x + "\n" + + for x in unless_expr_generator(depth+1): + yield x + "\n" + + yield space(depth) + "print B unless A\n" + yield space(depth) + "print B if A\n" + +fc = FileCreator("test_if_unless", 500, "require 'compat_common.rb'") + +count = 0 +for x in if_expr_generator(1): + string, number = replace_A(replace_B(x)) + cc = concat_char(number) + + fc.save_block("def f_%s(%s)" % (count, cc), + string, + "rescue", + " print 0", + "end", + "Bool_Sequence.new(2**%s).each { |%s| begin; print f_%s(%s); rescue; print -1; end; puts ' :f_%s' }\n" % (number, cc, count, cc, count) + ) + + count += 1 + +fc.close() +fc.print_file_list() \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_rescue_clause_long.py b/merlin/main/languages/ruby/Tests/Compat/gen_rescue_clause_long.py new file mode 100644 index 0000000000..e579e5c955 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_rescue_clause_long.py @@ -0,0 +1,176 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +from compat_common import * + +''' +Exception +|- ScriptError + |- LoadError + |- SyntaxError +|- StandardError + |- IOError + |- TyperError + |- ZeroDivisionError + |- RuntimeError + |- MyStdError +|- MyException +''' + +from _random import Random +rnd = Random() + +def randomly_choose(l): + if l: + return l[int(rnd.random() * len(l))] + else: + return None + +class Ex: + def __init__(self, name): + self.name = name + self.children = [] + self.parent = None + + def add_child(self, ex): + self.children.append(ex) + ex.parent = self + + def __str__(self): + return self.name + + @property + def any_sibling(self): + if self.parent: + return randomly_choose(self.parent.children) + else: + return None + + @property + def any_child(self): + return randomly_choose(self.children) + +all_exceptions = "Exception ScriptError LoadError SyntaxError StandardError IOError TyperError ZeroDivisionError RuntimeError MyException MyStdError".split() +for ex in all_exceptions: exec("%s = Ex('%s')" % (ex, ex)) + +# build the exception tree +Exception.add_child(ScriptError) +Exception.add_child(StandardError) +Exception.add_child(MyException) + +ScriptError.add_child(LoadError) +ScriptError.add_child(SyntaxError) + +StandardError.add_child(IOError) +StandardError.add_child(TyperError) +StandardError.add_child(ZeroDivisionError) +StandardError.add_child(RuntimeError) +StandardError.add_child(MyStdError) + +assign_to_bang = "($!=%s.new; %s)" + +def assign_to_bang_generator(ex): + yield assign_to_bang % (ex, ex) + if ex.parent: + yield assign_to_bang % (ex.parent, ex) + if ex.any_sibling: + yield assign_to_bang % (ex.any_sibling, ex) + if ex.any_child: + yield assign_to_bang % (ex.any_child, ex) + #yield "($!=nil; %s)" % ex + +def catch_generator(ex): + yield "(return B; IOError)" + yield "(raise MyStdError; IOError)" + + yield ex.name + + for x in assign_to_bang_generator(ex): + yield x + + if ex.parent: + yield ex.parent + for x in assign_to_bang_generator(ex.parent): + yield x + + y = ex.any_sibling + if y: + yield y + for x in assign_to_bang_generator(y): + yield x + + y = ex.any_child + if y: + yield y + for x in assign_to_bang_generator(y): + yield x + +concat_string = "%s, %s" + +def twice_catch_generator(ex): + for x in catch_generator(ex): + if ex.parent: + yield concat_string % (x, ex.parent) + for z in catch_generator(ex.parent): + yield concat_string % (x, z) + + y = ex.any_sibling + if y: + yield concat_string % (x, y) + for z in catch_generator(y): + yield concat_string % (x, z) + + y = ex.any_child + if y: + yield concat_string % (x, y) + for z in catch_generator(y): + yield concat_string % (x, z) + + +fc = FileCreator("test_rescue_clause", 300, ''' +class MyException < Exception; end +class MyStdError < StandardError; end +''') + +count = 0 + +def write(raise_x, catch_y): + global fc, count + s = "begin\n" + s += " raise %s.new\n" % raise_x + s += "rescue %s\n" % catch_y + s += " puts B, $!.class\n" + + line1 = "def f_%s\n" % count + s + "end\nputs B, $!.class\nend\n" + line2 = "$g = 0; begin; print(f_%s); rescue Exception; puts B, $!.class; end; puts \" : f_%s\"\n" % (count, count) + count += 1 + fc.save_block(replace_B(line1+line2)) + + #line1 = "def f_%s\n" % count + s + "ensure;\n puts B, $class\nend\nputs B, $!.class\nend\n" + #line2 = "$g = 0; begin; print(f_%s); rescue Exception; puts B, $!.class; end; puts \" : f_%s\"\n" % (count, count) + #count += 1 + #fc.save_block(replace_B(line1+line2)) + +interesting_exceptions = [eval(x) for x in "Exception ScriptError SyntaxError StandardError ZeroDivisionError MyException MyStdError".split()] + +for raise_x in interesting_exceptions: + for catch_y in catch_generator(raise_x): + write(raise_x, catch_y) + for catch_y in twice_catch_generator(raise_x): + write(raise_x, catch_y) + +fc.close() +fc.print_file_list() + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Compat/gen_singleton_graph.py b/merlin/main/languages/ruby/Tests/Compat/gen_singleton_graph.py new file mode 100644 index 0000000000..70428b2acd --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/gen_singleton_graph.py @@ -0,0 +1,250 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# Try to generate the test which represents the user-defined class, Class, Object, Module and +# their Singleton classes (and their Singleton classes)... We also re-open such classes with +# new members +# +# We verify by calling the methods through the class name and its' instance. We also call methods +# which access instance/class variables (this part is ugly, not sure how I can/should simplify it) + +from compat_common import * + +# globals +entities = [] +count = 0 + +class Entity: + def __init__(self, name, body, depends=None): + self.name = name + self.body = body + self.set_dependency(depends) + self.updated = False + + def __str__(self): + return self.name + + def set_dependency(self, d): + self.depend_on = d + + def get_id(self): + if "##" in self.body: + start = self.body.find("##") + end = self.body.find("\n", start) + return int(self.body[start + 2 : end]) + else: + return None + + def create_singleton_class(self): + global entities + + s_name = "S_" + self.name + if s_name.startswith("S_" * 4): return + + # exit if already created + for t in entities: + if t.name == s_name: return + + new_entity = Entity(s_name, "%s = class << %s\n%s\n self\nend\n" % (s_name, self.name, get_body())) + + # they are predefined classes, not necessary to be defined again + if self.name not in ["Object", "Module", "Class"]: + new_entity.set_dependency(self) + + entities.append(new_entity) + + def update_body(self, ids): + if self.name in 'xyz' or self.updated: + return + + eol1 = self.body.find('\n') + + methods = " def read_icheck();\n" + for y in ids: + methods += ' puts "iv%s"; puts @iv%s rescue puts "inner_error"\n' % (y, y) + methods += ' puts "sv%s"; puts @@sv%s rescue puts "inner_error"\n' % (y, y) + methods += " end\n def self.read_scheck();\n" + for y in ids: + methods += ' puts "iv%s"; puts @iv%s rescue puts "inner_error"\n' % (y, y) + methods += ' puts "sv%s"; puts @@sv%s rescue puts "inner_error"\n' % (y, y) + methods += " end\n" + + self.body = self.body[:eol1+1] + methods + self.body[eol1+1:] + self.updated = True +# +# create all entities +# +def get_body(): + global count + count += 1 + return ''' + ##%s + CONST%s = -%s + @iv%s = %s # class instance variable + @@sv%s = %s0 + + def initialize; @iv%s = -%s0; end # instance variable; intentionally they have the same name + def im%s; %s00; end + def self.sm%s; -%s00; end +''' % ( (count,) * 13 ) + +def get_definition(name, is_module=False): + return '%s %s\n%s\nend' % (is_module and "module" or "class", name, get_body()) + +module_m = Entity("M", get_definition("M", is_module=True)) +class_b = Entity("B", get_definition("B")) +class_c = Entity("C", get_definition("C < B"), class_b) +class_d = Entity("D", get_definition("D < B\n include M"), class_b) + +c_x = Entity("x", "x = C.new\n", class_c) +c_y = Entity("y", "y = C.new\n", class_c) +c_z = Entity("z", "z = D.new\n", class_d) + +class_object = Entity("Object", get_definition("Object")) +class_module = Entity("Module", get_definition("Module")) +class_class = Entity("Class", get_definition("Class")) + +entities = [class_b, class_c, module_m, class_d, c_y, c_z, class_object, class_module, class_class ] +for e in entities: e.create_singleton_class() +entities.append(c_x) + +# add explicitly re-opened classes +entities.extend([Entity(e.name + "_open", get_definition(e.name), e) + for e in entities + if e.name.startswith("S_")]) + +# Module, S_Module, B, +# | | +# S_S_Module C +# / \ +# y S_C +# / \ +# S_y S_S_C + +entities_dict = dict([(e.name, e) for e in entities]) +def names_to_entities(s): + return [entities_dict[x] for x in s.split()] + +def generate_entity_sequence(): + #yield (entities, str(entities_dict.keys())) # too slow + + # hand-picked sequences + for x in [ + # need update + 'B C S_C y S_B S_S_B S_S_C x S_Class S_Object Object Class S_S_Object S_S_Class', + 'S_Object B Object S_Class Class C x S_C S_S_C y S_Module Module S_y S_S_y', + 'B C x y S_y S_S_y S_S_S_y S_B S_S_B S_S_S_B S_C S_S_C S_S_S_C Object S_Object S_S_Object S_S_S_Object Class S_Class S_S_Class S_S_S_Class Module S_Module S_S_Module S_S_S_Module', + 'S_Object S_Class S_Module S_S_Object S_S_S_Object S_S_Class S_S_S_Class S_S_Module Class Object Module S_S_S_Module B C y S_y S_S_y S_S_S_y S_B S_S_B S_S_S_B S_C S_S_C S_S_S_C x', + ]: + yield (names_to_entities(x), x) + + import random + + for x in range(5): + sample = random.sample(entities, 4) + + #print [z.name for z in sample] + + # remove those be depended by other in sample + for y in sample: + current = y + while current.depend_on: + if current.depend_on in sample: + sample.remove(current.depend_on) + current = current.depend_on + + #print [z.name for z in sample] + + # made a seqence + sample_clone = [] + for y in sample: + current = y + sample_clone.insert(0, y) + while current.depend_on: + if current.depend_on in sample_clone: + sample_clone.remove(current.depend_on) + sample_clone.insert(0, current.depend_on) + current = current.depend_on + + if class_d in sample_clone: + if module_m in sample_clone: + sample_clone.remove(module_m) + sample_clone.insert(0, module_m) + + #print [z.name for z in sample_clone] + + yield (sample_clone, str([z.name for z in sample_clone])) + +# the function we use to check, added to each file + +def get_header(ids): + header = 'def check_each(x, l)\n' + for y in ids: + header += ' puts "#{l}.CONST%s"; puts x::CONST%s rescue puts "error"\n' % (y, y) + header += ' puts "#{l}.im%s"; puts x.im%s rescue puts "error"\n' % (y, y) + header += ' puts "#{l}.sm%s"; puts x.sm%s rescue puts "error"\n' % (y, y) + + header += ' puts "read_icheck"; x.read_icheck rescue puts "error"\n' + header += ' puts "read_scheck"; x.read_scheck rescue puts "error"\n' + header += "end\n" + + header += ''' +def check(vars, names) + puts '==========================================' + cnt = 0 + vars.each do |x| + puts "--------------#{cnt}, #{names[cnt]}---------------" + cnt += 1 +''' + header += " check_each(x, 'Class')\n" + header += " begin; y = x.new \n" + header += " rescue; puts 'fail to new'\n" + header += " else; check_each(y, 'Instance')\n" + header += " end\n end\nend\n" + return header + +fc = FileCreator("test_singleton_graph", 1) + +# give me a names_to_entities +for (seq, scenario) in generate_entity_sequence(): + vars = [] + lines = "# %s \n" % scenario + + all_ids = [] + for e in seq: + if hasattr(e, "get_id"): + idx = e.get_id() + if idx: + all_ids.append(idx) + + lines += get_header(all_ids) + + for e in seq: + if not e.name.endswith("_open"): + vars.append(e.name) + + temp = ', '.join(vars) + + e.update_body(all_ids) + lines += e.body + + lines += '\nputs "after adding %s"\n' % e.name + lines += 'puts "now vars: %s"\n' % temp + lines += 'check [%s], %s\n' % (temp, vars) + + fc.save_block(lines) + +fc.close() +fc.print_file_list() diff --git a/merlin/main/languages/ruby/Tests/Compat/run_compat.rb b/merlin/main/languages/ruby/Tests/Compat/run_compat.rb new file mode 100644 index 0000000000..8fb192befc --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/run_compat.rb @@ -0,0 +1,153 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# +# run each test in c-ruby and ironruby, check the output difference. +# if a baseline log file (whether it is correct or not) exists, +# c-ruby run will be skipped +# +# usage: +# * ruby.exe run_compat.rb +# -- run all test_*.rb under this directory +# * ruby.exe run_compat.rb test_a.rb test_b.rb +# -- run test_a.rb and test_b.rb only +# +# to do: +# * support "smart" compare + +require "../common" +begin + require 'fileutils' +rescue LoadError => e + p $: + raise e +end + +$failure = 0 +def log_error(comment) + printf " #{comment}" + $failure += 1 +end + +CRubyDriver = Test::CRubyDriver.new(false, false) + +$applicable_drivers = [ + Test::IronRubyDriver.new(2, "ironm2", false, false) +] + +def compare_succeed?(log1, log2, diff_log) + lines1 = open(log1) { |f1| f1.readlines } + lines2 = open(log2) { |f2| f2.readlines } + + return false unless lines1.length == lines2.length + + diff = [] + last_repro_line1 = last_repro_line2 = "" + + 0.upto(lines1.length - 1) do |i| + last_repro_line1 = lines1[i].strip if lines1[i].include? "repro:" + last_repro_line2 = lines2[i].strip if lines2[i].include? "repro:" + + next if lines1[i] == lines2[i] + + if last_repro_line1 == last_repro_line2 + temp = last_repro_line1 + else + temp = last_repro_line1 + " | " + last_repro_line2 + end + + diff << temp +" | " + lines1[i].strip + " | " + lines2[i].strip + "\n" + end + + if diff.length != 0 + File.open(diff_log, "w") do |f| + diff.each do |l| + f << l + end + end + end + return diff.length +end + +def run_one_test(f) + printf ">>> testing #{f} ... " + use_baseline = false + fbn = f[0..-4] + clog = File.expand_path "#{fbn}.c.log" + ilog = File.expand_path "#{fbn}.i.log" + baseline = File.expand_path"#{fbn}.bsl" + diff_log = File.expand_path "#{fbn}.diff.log" + + if File.exist?(baseline) + FileUtils.cp(baseline,clog) + printf " S" + elsif CRubyDriver.run(f, clog) != 0 + log_error("failed while running CRuby\n") + return + end + + $applicable_drivers.each do |d| + if d.run(f, ilog) != 0 + log_error("failed while running #{d}\n") + end + + diff_count = compare_succeed?(clog, ilog, diff_log) + if diff_count != 0 + log_error "failed while comparing (#{diff_count})\n" + printf " windiff #{clog} #{ilog} | diff log: #{diff_log}\n" + else + File.delete(clog, ilog) + printf " pass\n" + end + end +end + +test_files = [] + +ARGV.each do |arg| + if arg =~ /-snap/ + # filter -xxx switches out + else + if arg.index("@") == 0 + File.open(arg[1..-1]) { |f| f.readlines.each { |l| test_files << l.strip unless l.include?("#") } } + else + test_files << arg + end + end +end + +if test_files.empty? + test_files = [ + "test_parallel_assign1.rb", + "test_parallel_assign2.rb", + "test_parallel_assign3.rb", + "test_assignment.rb", + "test_func_splat.rb", + "test_assignment_regression.rb", + "test_exception_regression.rb", + "test_block_ctrl_flow_break.rb", + "test_block_ctrl_flow_next.rb", + "test_block_ctrl_flow_normal.rb", + "test_block_ctrl_flow_raise.rb", + "test_block_ctrl_flow_redo.rb", + "test_block_ctrl_flow_retry.rb", + "test_block_ctrl_flow_return.rb", + ] +end + +test_files.each { |f| run_one_test(f) } + +printf "\nSummary [failure: #{$failure}]\n" +exit($failure) diff --git a/merlin/main/languages/ruby/Tests/Compat/run_compat_long.bat b/merlin/main/languages/ruby/Tests/Compat/run_compat_long.bat new file mode 100644 index 0000000000..f8468aa64e --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/run_compat_long.bat @@ -0,0 +1,28 @@ +REM **************************************************************************** +REM +REM Copyright (c) Microsoft Corporation. +REM +REM This source code is subject to terms and conditions of the Microsoft Public License. A +REM copy of the license can be found in the License.html file at the root of this distribution. If +REM you cannot locate the Microsoft Public License, please send an email to +REM ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +REM by the terms of the Microsoft Public License. +REM +REM You must not remove this notice, or any other, from this software. +REM +REM +REM **************************************************************************** + +if "%1"=="" ( + set FILELIST=gen_assignment_long,gen_exception1_long,gen_exception2_long,gen_block_ctrl_flow_long,gen_rescue_clause_long +) ELSE ( + set FILELIST=%1 +) + +for %%f in (%FILELIST%) do ( + %MERLIN_ROOT%\..\external\languages\cpython\25\python.exe %%f.py > %%f.lst + %MERLIN_ROOT%\..\external\languages\ruby\ruby-1.8.6\bin\ruby.exe run_compat.rb @%%f.lst + if NOT "%ERRORLEVEL%" == "0" exit /b 1 +) + +exit /b 0 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Compat/template_block_ctrl_flow.rb b/merlin/main/languages/ruby/Tests/Compat/template_block_ctrl_flow.rb new file mode 100644 index 0000000000..542523fc57 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/template_block_ctrl_flow.rb @@ -0,0 +1,595 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +# helper +def myeval(line); puts B; eval(line); puts B; end +def call1(x); puts B; call2(x); puts B; end +def call2(x); puts B; call3(x); puts B; end +def call3(x); puts B; puts x.call; puts B; end + +# producer + +def get_block(&p); p; end +def get_lambda(&p); lambda(&p); end +def get_proc(&p); Proc.new(&p); end + +def get_local_block; get_block { puts B; ctrl_flow; puts B }; end +def get_local_lambda; lambda { puts B; ctrl_flow; puts B }; end +def get_local_proc; Proc.new { puts B; ctrl_flow; puts B }; end + +# consumer + +# taking arguments +def iterator_via_yield; puts B; x = yield; puts x; puts B; end +def iterator_via_call(&p); puts B; puts(p.call); puts B; end + +def method_call_iterator_via_yield(&p); puts B; iterator_via_yield(&p); puts B; end +def method_call_iterator_via_call(&p); puts B; iterator_via_call(&p); puts B; end + +def method_use_lambda_and_yield; puts B; x = lambda { puts B; yield; puts B }; puts x.call; puts B; end +def method_use_proc_and_yield; puts B; x = Proc.new { puts B; yield; puts B }; puts x.call; puts B; end +def method_use_lambda_and_call(&p); puts B; x = lambda { puts B; p.call; puts B }; puts x.call; puts B; end +def method_use_proc_and_call(&p); puts B; x = Proc.new { puts B; p.call; puts B }; puts x.call; puts B; end + +def method_use_lambda_and_yield_2; puts B; x = lambda { puts B; yield; puts B }; call1(x); puts B; end + +def method_yield_in_loop; puts B; for i in [1, 2]; puts B; yield; puts B; end; puts B; end +def method_call_in_loop(&p); puts B; for i in [3, 4]; puts B; p.call; puts B; end; puts B; end + +# created in-place +def test +$g = 0; begin; puts B; iterator_via_yield { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_1; puts B; $g = 0; begin; puts B; iterator_via_yield { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end; puts B; end; m_1 +$g = 0; begin; puts B; iterator_via_call { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_2; puts B; $g = 0; begin; puts B; iterator_via_call { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end; puts B; end; m_2 +$g = 0; begin; puts B; method_call_iterator_via_yield { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_3; puts B; $g = 0; begin; puts B; method_call_iterator_via_yield { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end; puts B; end; m_3 +$g = 0; begin; puts B; method_call_iterator_via_call { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_4; puts B; $g = 0; begin; puts B; method_call_iterator_via_call { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end; puts B; end; m_4 +$g = 0; begin; puts B; method_use_lambda_and_yield { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_5; puts B; $g = 0; begin; puts B; method_use_lambda_and_yield { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end; puts B; end; m_5 +$g = 0; begin; puts B; method_use_proc_and_yield { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_6; puts B; $g = 0; begin; puts B; method_use_proc_and_yield { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end; puts B; end; m_6 +$g = 0; begin; puts B; method_use_lambda_and_call { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_7; puts B; $g = 0; begin; puts B; method_use_lambda_and_call { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end; puts B; end; m_7 +$g = 0; begin; puts B; method_use_proc_and_call { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_8; puts B; $g = 0; begin; puts B; method_use_proc_and_call { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end; puts B; end; m_8 +$g = 0; begin; puts B; method_use_lambda_and_yield_2 { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_9; puts B; $g = 0; begin; puts B; method_use_lambda_and_yield_2 { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end; puts B; end; m_9 +$g = 0; begin; puts B; method_yield_in_loop { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_10; puts B; $g = 0; begin; puts B; method_yield_in_loop { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end; puts B; end; m_10 +$g = 0; begin; puts B; method_call_in_loop { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_11; puts B; $g = 0; begin; puts B; method_call_in_loop { puts B; ctrl_flow; puts B}; puts B; rescue; puts B; puts $!.class; end; puts B; end; m_11 +end +test + + +# created locally or from method +def test +$g = 0; begin; p = lambda{ puts B; ctrl_flow; puts B}; puts B; iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_12; p = lambda{ puts B; ctrl_flow; puts B}; puts B; iterator_via_yield(&p); puts B; end; +begin; puts B; m_12; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = lambda{ puts B; ctrl_flow; puts B}; puts B; iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_13; p = lambda{ puts B; ctrl_flow; puts B}; puts B; iterator_via_call(&p); puts B; end; +begin; puts B; m_13; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_14; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_yield(&p); puts B; end; +begin; puts B; m_14; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_15; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_call(&p); puts B; end; +begin; puts B; m_15; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_16; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield(&p); puts B; end; +begin; puts B; m_16; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_17; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_yield(&p); puts B; end; +begin; puts B; m_17; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_18; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_call(&p); puts B; end; +begin; puts B; m_18; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_19; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_call(&p); puts B; end; +begin; puts B; m_19; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield_2(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_20; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield_2(&p); puts B; end; +begin; puts B; m_20; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_yield_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_21; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_yield_in_loop(&p); puts B; end; +begin; puts B; m_21; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_call_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_22; p = lambda{ puts B; ctrl_flow; puts B}; puts B; method_call_in_loop(&p); puts B; end; +begin; puts B; m_22; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_23; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; iterator_via_yield(&p); puts B; end; +begin; puts B; m_23; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_24; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; iterator_via_call(&p); puts B; end; +begin; puts B; m_24; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_25; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_yield(&p); puts B; end; +begin; puts B; m_25; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_26; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_call(&p); puts B; end; +begin; puts B; m_26; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_27; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield(&p); puts B; end; +begin; puts B; m_27; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_28; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_yield(&p); puts B; end; +begin; puts B; m_28; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_29; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_call(&p); puts B; end; +begin; puts B; m_29; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_30; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_call(&p); puts B; end; +begin; puts B; m_30; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield_2(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_31; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield_2(&p); puts B; end; +begin; puts B; m_31; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_yield_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_32; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_yield_in_loop(&p); puts B; end; +begin; puts B; m_32; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_call_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_33; p = Proc.new{ puts B; ctrl_flow; puts B}; puts B; method_call_in_loop(&p); puts B; end; +begin; puts B; m_33; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_block{ puts B; ctrl_flow; puts B}; puts B; iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_34; p = get_block{ puts B; ctrl_flow; puts B}; puts B; iterator_via_yield(&p); puts B; end; +begin; puts B; m_34; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_block{ puts B; ctrl_flow; puts B}; puts B; iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_35; p = get_block{ puts B; ctrl_flow; puts B}; puts B; iterator_via_call(&p); puts B; end; +begin; puts B; m_35; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_36; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_yield(&p); puts B; end; +begin; puts B; m_36; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_37; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_call(&p); puts B; end; +begin; puts B; m_37; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_38; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield(&p); puts B; end; +begin; puts B; m_38; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_39; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_yield(&p); puts B; end; +begin; puts B; m_39; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_40; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_call(&p); puts B; end; +begin; puts B; m_40; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_41; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_call(&p); puts B; end; +begin; puts B; m_41; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield_2(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_42; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield_2(&p); puts B; end; +begin; puts B; m_42; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_yield_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_43; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_yield_in_loop(&p); puts B; end; +begin; puts B; m_43; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_call_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_44; p = get_block{ puts B; ctrl_flow; puts B}; puts B; method_call_in_loop(&p); puts B; end; +begin; puts B; m_44; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_45; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; iterator_via_yield(&p); puts B; end; +begin; puts B; m_45; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_46; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; iterator_via_call(&p); puts B; end; +begin; puts B; m_46; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_47; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_yield(&p); puts B; end; +begin; puts B; m_47; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_48; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_call(&p); puts B; end; +begin; puts B; m_48; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_49; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield(&p); puts B; end; +begin; puts B; m_49; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_50; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_yield(&p); puts B; end; +begin; puts B; m_50; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_51; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_call(&p); puts B; end; +begin; puts B; m_51; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_52; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_call(&p); puts B; end; +begin; puts B; m_52; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield_2(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_53; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield_2(&p); puts B; end; +begin; puts B; m_53; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_yield_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_54; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_yield_in_loop(&p); puts B; end; +begin; puts B; m_54; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_call_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_55; p = get_lambda{ puts B; ctrl_flow; puts B}; puts B; method_call_in_loop(&p); puts B; end; +begin; puts B; m_55; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_56; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; iterator_via_yield(&p); puts B; end; +begin; puts B; m_56; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_57; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; iterator_via_call(&p); puts B; end; +begin; puts B; m_57; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_58; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_yield(&p); puts B; end; +begin; puts B; m_58; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_59; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_call_iterator_via_call(&p); puts B; end; +begin; puts B; m_59; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_60; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield(&p); puts B; end; +begin; puts B; m_60; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_61; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_yield(&p); puts B; end; +begin; puts B; m_61; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_62; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_call(&p); puts B; end; +begin; puts B; m_62; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_63; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_use_proc_and_call(&p); puts B; end; +begin; puts B; m_63; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield_2(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_64; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_use_lambda_and_yield_2(&p); puts B; end; +begin; puts B; m_64; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_yield_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_65; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_yield_in_loop(&p); puts B; end; +begin; puts B; m_65; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_call_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_66; p = get_proc{ puts B; ctrl_flow; puts B}; puts B; method_call_in_loop(&p); puts B; end; +begin; puts B; m_66; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_block; puts B; iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_67; p = get_local_block; puts B; iterator_via_yield(&p); puts B; end; +begin; puts B; m_67; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_block; puts B; iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_68; p = get_local_block; puts B; iterator_via_call(&p); puts B; end; +begin; puts B; m_68; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_block; puts B; method_call_iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_69; p = get_local_block; puts B; method_call_iterator_via_yield(&p); puts B; end; +begin; puts B; m_69; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_block; puts B; method_call_iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_70; p = get_local_block; puts B; method_call_iterator_via_call(&p); puts B; end; +begin; puts B; m_70; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_block; puts B; method_use_lambda_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_71; p = get_local_block; puts B; method_use_lambda_and_yield(&p); puts B; end; +begin; puts B; m_71; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_block; puts B; method_use_proc_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_72; p = get_local_block; puts B; method_use_proc_and_yield(&p); puts B; end; +begin; puts B; m_72; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_block; puts B; method_use_lambda_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_73; p = get_local_block; puts B; method_use_lambda_and_call(&p); puts B; end; +begin; puts B; m_73; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_block; puts B; method_use_proc_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_74; p = get_local_block; puts B; method_use_proc_and_call(&p); puts B; end; +begin; puts B; m_74; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_block; puts B; method_use_lambda_and_yield_2(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_75; p = get_local_block; puts B; method_use_lambda_and_yield_2(&p); puts B; end; +begin; puts B; m_75; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_block; puts B; method_yield_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_76; p = get_local_block; puts B; method_yield_in_loop(&p); puts B; end; +begin; puts B; m_76; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_block; puts B; method_call_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_77; p = get_local_block; puts B; method_call_in_loop(&p); puts B; end; +begin; puts B; m_77; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_lambda; puts B; iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_78; p = get_local_lambda; puts B; iterator_via_yield(&p); puts B; end; +begin; puts B; m_78; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts B; iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_79; p = get_local_lambda; puts B; iterator_via_call(&p); puts B; end; +begin; puts B; m_79; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts B; method_call_iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_80; p = get_local_lambda; puts B; method_call_iterator_via_yield(&p); puts B; end; +begin; puts B; m_80; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts B; method_call_iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_81; p = get_local_lambda; puts B; method_call_iterator_via_call(&p); puts B; end; +begin; puts B; m_81; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts B; method_use_lambda_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_82; p = get_local_lambda; puts B; method_use_lambda_and_yield(&p); puts B; end; +begin; puts B; m_82; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts B; method_use_proc_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_83; p = get_local_lambda; puts B; method_use_proc_and_yield(&p); puts B; end; +begin; puts B; m_83; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts B; method_use_lambda_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_84; p = get_local_lambda; puts B; method_use_lambda_and_call(&p); puts B; end; +begin; puts B; m_84; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts B; method_use_proc_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_85; p = get_local_lambda; puts B; method_use_proc_and_call(&p); puts B; end; +begin; puts B; m_85; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts B; method_use_lambda_and_yield_2(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_86; p = get_local_lambda; puts B; method_use_lambda_and_yield_2(&p); puts B; end; +begin; puts B; m_86; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts B; method_yield_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_87; p = get_local_lambda; puts B; method_yield_in_loop(&p); puts B; end; +begin; puts B; m_87; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts B; method_call_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_88; p = get_local_lambda; puts B; method_call_in_loop(&p); puts B; end; +begin; puts B; m_88; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_proc; puts B; iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_89; p = get_local_proc; puts B; iterator_via_yield(&p); puts B; end; +begin; puts B; m_89; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts B; iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_90; p = get_local_proc; puts B; iterator_via_call(&p); puts B; end; +begin; puts B; m_90; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts B; method_call_iterator_via_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_91; p = get_local_proc; puts B; method_call_iterator_via_yield(&p); puts B; end; +begin; puts B; m_91; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts B; method_call_iterator_via_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_92; p = get_local_proc; puts B; method_call_iterator_via_call(&p); puts B; end; +begin; puts B; m_92; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts B; method_use_lambda_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_93; p = get_local_proc; puts B; method_use_lambda_and_yield(&p); puts B; end; +begin; puts B; m_93; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts B; method_use_proc_and_yield(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_94; p = get_local_proc; puts B; method_use_proc_and_yield(&p); puts B; end; +begin; puts B; m_94; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts B; method_use_lambda_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_95; p = get_local_proc; puts B; method_use_lambda_and_call(&p); puts B; end; +begin; puts B; m_95; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts B; method_use_proc_and_call(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_96; p = get_local_proc; puts B; method_use_proc_and_call(&p); puts B; end; +begin; puts B; m_96; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts B; method_use_lambda_and_yield_2(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_97; p = get_local_proc; puts B; method_use_lambda_and_yield_2(&p); puts B; end; +begin; puts B; m_97; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts B; method_yield_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_98; p = get_local_proc; puts B; method_yield_in_loop(&p); puts B; end; +begin; puts B; m_98; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts B; method_call_in_loop(&p); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_99; p = get_local_proc; puts B; method_call_in_loop(&p); puts B; end; +begin; puts B; m_99; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts(p.call); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_100; puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts(p.call); puts B; end; +begin; puts B; m_100; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; puts m_100; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_101; puts B; puts m_100; puts B; end; +begin; puts B; m_101; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts(p.call); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_102; puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts(p.call); puts B; end; +begin; puts B; m_102; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; puts m_102; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_103; puts B; puts m_102; puts B; end; +begin; puts B; m_103; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts(p.call); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_104; puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts(p.call); puts B; end; +begin; puts B; m_104; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; puts m_104; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_105; puts B; puts m_104; puts B; end; +begin; puts B; m_105; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts(p.call); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_106; puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts(p.call); puts B; end; +begin; puts B; m_106; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; puts m_106; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_107; puts B; puts m_106; puts B; end; +begin; puts B; m_107; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts(p.call); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_108; puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts(p.call); puts B; end; +begin; puts B; m_108; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; puts m_108; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_109; puts B; puts m_108; puts B; end; +begin; puts B; m_109; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; p = get_local_block; puts(p.call); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_110; puts B; p = get_local_block; puts(p.call); puts B; end; +begin; puts B; m_110; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; puts m_110; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_111; puts B; puts m_110; puts B; end; +begin; puts B; m_111; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; p = get_local_lambda; puts(p.call); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_112; puts B; p = get_local_lambda; puts(p.call); puts B; end; +begin; puts B; m_112; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; puts m_112; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_113; puts B; puts m_112; puts B; end; +begin; puts B; m_113; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; p = get_local_proc; puts(p.call); puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_114; puts B; p = get_local_proc; puts(p.call); puts B; end; +begin; puts B; m_114; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; puts m_114; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_115; puts B; puts m_114; puts B; end; +begin; puts B; m_115; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; puts B; x = lambda { puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_116; puts B; x = lambda { puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_116; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = lambda { puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_117; puts B; x = lambda { puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_117; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = lambda { puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_118; puts B; x = lambda { puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_118; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = lambda { puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_119; puts B; x = lambda { puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_119; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = lambda { puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_120; puts B; x = lambda { puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_120; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = lambda { puts B; p = get_local_block; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_121; puts B; x = lambda { puts B; p = get_local_block; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_121; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = lambda { puts B; p = get_local_lambda; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_122; puts B; x = lambda { puts B; p = get_local_lambda; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_122; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = lambda { puts B; p = get_local_proc; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_123; puts B; x = lambda { puts B; p = get_local_proc; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_123; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; puts B; x = Proc.new { puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_124; puts B; x = Proc.new { puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_124; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = Proc.new { puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_125; puts B; x = Proc.new { puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_125; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = Proc.new { puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_126; puts B; x = Proc.new { puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_126; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = Proc.new { puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_127; puts B; x = Proc.new { puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_127; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = Proc.new { puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_128; puts B; x = Proc.new { puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_128; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = Proc.new { puts B; p = get_local_block; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_129; puts B; x = Proc.new { puts B; p = get_local_block; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_129; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = Proc.new { puts B; p = get_local_lambda; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_130; puts B; x = Proc.new { puts B; p = get_local_lambda; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_130; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = Proc.new { puts B; p = get_local_proc; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_131; puts B; x = Proc.new { puts B; p = get_local_proc; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_131; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; puts B; x = get_block { puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_132; puts B; x = get_block { puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_132; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_block { puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_133; puts B; x = get_block { puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_133; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_block { puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_134; puts B; x = get_block { puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_134; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_block { puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_135; puts B; x = get_block { puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_135; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_block { puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_136; puts B; x = get_block { puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_136; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_block { puts B; p = get_local_block; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_137; puts B; x = get_block { puts B; p = get_local_block; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_137; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_block { puts B; p = get_local_lambda; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_138; puts B; x = get_block { puts B; p = get_local_lambda; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_138; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_block { puts B; p = get_local_proc; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_139; puts B; x = get_block { puts B; p = get_local_proc; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_139; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; puts B; x = get_lambda { puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_140; puts B; x = get_lambda { puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_140; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_lambda { puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_141; puts B; x = get_lambda { puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_141; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_lambda { puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_142; puts B; x = get_lambda { puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_142; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_lambda { puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_143; puts B; x = get_lambda { puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_143; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_lambda { puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_144; puts B; x = get_lambda { puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_144; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_lambda { puts B; p = get_local_block; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_145; puts B; x = get_lambda { puts B; p = get_local_block; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_145; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_lambda { puts B; p = get_local_lambda; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_146; puts B; x = get_lambda { puts B; p = get_local_lambda; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_146; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_lambda { puts B; p = get_local_proc; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_147; puts B; x = get_lambda { puts B; p = get_local_proc; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_147; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; puts B; x = get_proc { puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_148; puts B; x = get_proc { puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_148; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_proc { puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_149; puts B; x = get_proc { puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_149; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_proc { puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_150; puts B; x = get_proc { puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_150; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_proc { puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_151; puts B; x = get_proc { puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_151; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_proc { puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_152; puts B; x = get_proc { puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_152; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_proc { puts B; p = get_local_block; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_153; puts B; x = get_proc { puts B; p = get_local_block; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_153; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_proc { puts B; p = get_local_lambda; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_154; puts B; x = get_proc { puts B; p = get_local_lambda; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_154; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; x = get_proc { puts B; p = get_local_proc; puts p.call; puts B}; puts x.call; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_155; puts B; x = get_proc { puts B; p = get_local_proc; puts p.call; puts B}; puts x.call; puts B; end; +begin; puts B; m_155; puts B; rescue; puts B; puts $!.class; end +end +test + +def test +$g = 0; begin; puts B; for i in [1, 2]; puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B; end; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_156; puts B; for i in [1, 2]; puts B; p = lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B; end; puts B; end; +begin; puts B; m_156; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; for i in [1, 2]; puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts p.call; puts B; end; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_157; puts B; for i in [1, 2]; puts B; p = Proc.new{ puts B; ctrl_flow; puts B}; puts p.call; puts B; end; puts B; end; +begin; puts B; m_157; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; for i in [1, 2]; puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts p.call; puts B; end; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_158; puts B; for i in [1, 2]; puts B; p = get_block{ puts B; ctrl_flow; puts B}; puts p.call; puts B; end; puts B; end; +begin; puts B; m_158; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; for i in [1, 2]; puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B; end; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_159; puts B; for i in [1, 2]; puts B; p = get_lambda{ puts B; ctrl_flow; puts B}; puts p.call; puts B; end; puts B; end; +begin; puts B; m_159; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; for i in [1, 2]; puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts p.call; puts B; end; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_160; puts B; for i in [1, 2]; puts B; p = get_proc{ puts B; ctrl_flow; puts B}; puts p.call; puts B; end; puts B; end; +begin; puts B; m_160; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; for i in [1, 2]; puts B; p = get_local_block; puts p.call; puts B; end; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_161; puts B; for i in [1, 2]; puts B; p = get_local_block; puts p.call; puts B; end; puts B; end; +begin; puts B; m_161; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; for i in [1, 2]; puts B; p = get_local_lambda; puts p.call; puts B; end; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_162; puts B; for i in [1, 2]; puts B; p = get_local_lambda; puts p.call; puts B; end; puts B; end; +begin; puts B; m_162; puts B; rescue; puts B; puts $!.class; end +$g = 0; begin; puts B; for i in [1, 2]; puts B; p = get_local_proc; puts p.call; puts B; end; puts B; rescue; puts B; puts $!.class; end +$g = 0; def m_163; puts B; for i in [1, 2]; puts B; p = get_local_proc; puts p.call; puts B; end; puts B; end; +begin; puts B; m_163; puts B; rescue; puts B; puts $!.class; end +end +test diff --git a/merlin/main/languages/ruby/Tests/Compat/test_assignment.rb b/merlin/main/languages/ruby/Tests/Compat/test_assignment.rb new file mode 100644 index 0000000000..06f4d8be83 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_assignment.rb @@ -0,0 +1,228 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +def p *a + a.each { |x| print x.inspect, '; ' } + puts +end + +def f0() + a = 0; puts 'repro: a = nil'; a = nil; p a + a = 0; puts 'repro: a = []'; a = []; p a + a = 0; puts 'repro: a = [nil]'; a = [nil]; p a + a = 0; puts 'repro: a = 1'; a = 1; p a + a = 0; puts 'repro: a = 1,2'; a = 1,2; p a + a = 0; puts 'repro: a = 1,2,3'; a = 1,2,3; p a + a = 0; puts 'repro: a = [1]'; a = [1]; p a + a = 0; puts 'repro: a = [1,2]'; a = [1,2]; p a + a = 0; puts 'repro: a = [1,2,3]'; a = [1,2,3]; p a + a = 0; puts 'repro: a = *1'; a = *1; p a + a = 0; puts 'repro: a = *nil'; a = *nil; p a + a = 0; puts 'repro: a = *[]'; a = *[]; p a + a = 0; puts 'repro: a = *[nil]'; a = *[nil]; p a + a = 0; puts 'repro: a = *[1]'; a = *[1]; p a + a = 0; puts 'repro: a = *[1,2]'; a = *[1,2]; p a + a = 0; puts 'repro: a = 1,*[2,3]'; a = 1,*[2,3]; p a + a = 0; puts 'repro: a = *[[]]'; a = *[[]]; p a + a = 0; puts 'repro: a = 1,*[]'; a = 1,*[]; p a + a = 0; puts 'repro: a = [1,2],3'; a = [1,2],3; p a + a = 0; puts 'repro: a = nil,1'; a = nil,1; p a +end +def f1() + a,b = 0; puts 'repro: a,b = nil'; a,b = nil; p a,b + a,b = 0; puts 'repro: a,b = []'; a,b = []; p a,b + a,b = 0; puts 'repro: a,b = [nil]'; a,b = [nil]; p a,b + a,b = 0; puts 'repro: a,b = 1'; a,b = 1; p a,b + a,b = 0; puts 'repro: a,b = 1,2'; a,b = 1,2; p a,b + a,b = 0; puts 'repro: a,b = 1,2,3'; a,b = 1,2,3; p a,b + a,b = 0; puts 'repro: a,b = [1]'; a,b = [1]; p a,b + a,b = 0; puts 'repro: a,b = [1,2]'; a,b = [1,2]; p a,b + a,b = 0; puts 'repro: a,b = [1,2,3]'; a,b = [1,2,3]; p a,b + a,b = 0; puts 'repro: a,b = *1'; a,b = *1; p a,b + a,b = 0; puts 'repro: a,b = *nil'; a,b = *nil; p a,b + a,b = 0; puts 'repro: a,b = *[]'; a,b = *[]; p a,b + a,b = 0; puts 'repro: a,b = *[nil]'; a,b = *[nil]; p a,b + a,b = 0; puts 'repro: a,b = *[1]'; a,b = *[1]; p a,b + a,b = 0; puts 'repro: a,b = *[1,2]'; a,b = *[1,2]; p a,b + a,b = 0; puts 'repro: a,b = 1,*[2,3]'; a,b = 1,*[2,3]; p a,b + a,b = 0; puts 'repro: a,b = *[[]]'; a,b = *[[]]; p a,b + a,b = 0; puts 'repro: a,b = 1,*[]'; a,b = 1,*[]; p a,b + a,b = 0; puts 'repro: a,b = [1,2],3'; a,b = [1,2],3; p a,b + a,b = 0; puts 'repro: a,b = nil,1'; a,b = nil,1; p a,b +end +def f2() + a = 0; puts 'repro: *a = nil'; *a = nil; p a + a = 0; puts 'repro: *a = []'; *a = []; p a + a = 0; puts 'repro: *a = [nil]'; *a = [nil]; p a + a = 0; puts 'repro: *a = 1'; *a = 1; p a + a = 0; puts 'repro: *a = 1,2'; *a = 1,2; p a + a = 0; puts 'repro: *a = 1,2,3'; *a = 1,2,3; p a + a = 0; puts 'repro: *a = [1]'; *a = [1]; p a + a = 0; puts 'repro: *a = [1,2]'; *a = [1,2]; p a + a = 0; puts 'repro: *a = [1,2,3]'; *a = [1,2,3]; p a + a = 0; puts 'repro: *a = *1'; *a = *1; p a + a = 0; puts 'repro: *a = *nil'; *a = *nil; p a + a = 0; puts 'repro: *a = *[]'; *a = *[]; p a + a = 0; puts 'repro: *a = *[nil]'; *a = *[nil]; p a + a = 0; puts 'repro: *a = *[1]'; *a = *[1]; p a + a = 0; puts 'repro: *a = *[1,2]'; *a = *[1,2]; p a + a = 0; puts 'repro: *a = 1,*[2,3]'; *a = 1,*[2,3]; p a + a = 0; puts 'repro: *a = *[[]]'; *a = *[[]]; p a + a = 0; puts 'repro: *a = 1,*[]'; *a = 1,*[]; p a + a = 0; puts 'repro: *a = [1,2],3'; *a = [1,2],3; p a + a = 0; puts 'repro: *a = nil,1'; *a = nil,1; p a +end +def f3() + a,b = 0; puts 'repro: a,*b = nil'; a,*b = nil; p a,b + a,b = 0; puts 'repro: a,*b = []'; a,*b = []; p a,b + a,b = 0; puts 'repro: a,*b = [nil]'; a,*b = [nil]; p a,b + a,b = 0; puts 'repro: a,*b = 1'; a,*b = 1; p a,b + a,b = 0; puts 'repro: a,*b = 1,2'; a,*b = 1,2; p a,b + a,b = 0; puts 'repro: a,*b = 1,2,3'; a,*b = 1,2,3; p a,b + a,b = 0; puts 'repro: a,*b = [1]'; a,*b = [1]; p a,b + a,b = 0; puts 'repro: a,*b = [1,2]'; a,*b = [1,2]; p a,b + a,b = 0; puts 'repro: a,*b = [1,2,3]'; a,*b = [1,2,3]; p a,b + a,b = 0; puts 'repro: a,*b = *1'; a,*b = *1; p a,b + a,b = 0; puts 'repro: a,*b = *nil'; a,*b = *nil; p a,b + a,b = 0; puts 'repro: a,*b = *[]'; a,*b = *[]; p a,b + a,b = 0; puts 'repro: a,*b = *[nil]'; a,*b = *[nil]; p a,b + a,b = 0; puts 'repro: a,*b = *[1]'; a,*b = *[1]; p a,b + a,b = 0; puts 'repro: a,*b = *[1,2]'; a,*b = *[1,2]; p a,b + a,b = 0; puts 'repro: a,*b = 1,*[2,3]'; a,*b = 1,*[2,3]; p a,b + a,b = 0; puts 'repro: a,*b = *[[]]'; a,*b = *[[]]; p a,b + a,b = 0; puts 'repro: a,*b = 1,*[]'; a,*b = 1,*[]; p a,b + a,b = 0; puts 'repro: a,*b = [1,2],3'; a,*b = [1,2],3; p a,b + a,b = 0; puts 'repro: a,*b = nil,1'; a,*b = nil,1; p a,b +end +def f4() + a = 0; puts 'repro: (a,) = nil'; (a,) = nil; p a + a = 0; puts 'repro: (a,) = []'; (a,) = []; p a + a = 0; puts 'repro: (a,) = [nil]'; (a,) = [nil]; p a + a = 0; puts 'repro: (a,) = 1'; (a,) = 1; p a + a = 0; puts 'repro: (a,) = 1,2'; (a,) = 1,2; p a + a = 0; puts 'repro: (a,) = 1,2,3'; (a,) = 1,2,3; p a + a = 0; puts 'repro: (a,) = [1]'; (a,) = [1]; p a + a = 0; puts 'repro: (a,) = [1,2]'; (a,) = [1,2]; p a + a = 0; puts 'repro: (a,) = [1,2,3]'; (a,) = [1,2,3]; p a + a = 0; puts 'repro: (a,) = *1'; (a,) = *1; p a + a = 0; puts 'repro: (a,) = *nil'; (a,) = *nil; p a + a = 0; puts 'repro: (a,) = *[]'; (a,) = *[]; p a + a = 0; puts 'repro: (a,) = *[nil]'; (a,) = *[nil]; p a + a = 0; puts 'repro: (a,) = *[1]'; (a,) = *[1]; p a + a = 0; puts 'repro: (a,) = *[1,2]'; (a,) = *[1,2]; p a + a = 0; puts 'repro: (a,) = 1,*[2,3]'; (a,) = 1,*[2,3]; p a + a = 0; puts 'repro: (a,) = *[[]]'; (a,) = *[[]]; p a + a = 0; puts 'repro: (a,) = 1,*[]'; (a,) = 1,*[]; p a + a = 0; puts 'repro: (a,) = [1,2],3'; (a,) = [1,2],3; p a + a = 0; puts 'repro: (a,) = nil,1'; (a,) = nil,1; p a +end +def f5() + a,b = 0; puts 'repro: (a,b) = nil'; (a,b) = nil; p a,b + a,b = 0; puts 'repro: (a,b) = []'; (a,b) = []; p a,b + a,b = 0; puts 'repro: (a,b) = [nil]'; (a,b) = [nil]; p a,b + a,b = 0; puts 'repro: (a,b) = 1'; (a,b) = 1; p a,b + a,b = 0; puts 'repro: (a,b) = 1,2'; (a,b) = 1,2; p a,b + a,b = 0; puts 'repro: (a,b) = 1,2,3'; (a,b) = 1,2,3; p a,b + a,b = 0; puts 'repro: (a,b) = [1]'; (a,b) = [1]; p a,b + a,b = 0; puts 'repro: (a,b) = [1,2]'; (a,b) = [1,2]; p a,b + a,b = 0; puts 'repro: (a,b) = [1,2,3]'; (a,b) = [1,2,3]; p a,b + a,b = 0; puts 'repro: (a,b) = *1'; (a,b) = *1; p a,b + a,b = 0; puts 'repro: (a,b) = *nil'; (a,b) = *nil; p a,b + a,b = 0; puts 'repro: (a,b) = *[]'; (a,b) = *[]; p a,b + a,b = 0; puts 'repro: (a,b) = *[nil]'; (a,b) = *[nil]; p a,b + a,b = 0; puts 'repro: (a,b) = *[1]'; (a,b) = *[1]; p a,b + a,b = 0; puts 'repro: (a,b) = *[1,2]'; (a,b) = *[1,2]; p a,b + a,b = 0; puts 'repro: (a,b) = 1,*[2,3]'; (a,b) = 1,*[2,3]; p a,b + a,b = 0; puts 'repro: (a,b) = *[[]]'; (a,b) = *[[]]; p a,b + a,b = 0; puts 'repro: (a,b) = 1,*[]'; (a,b) = 1,*[]; p a,b + a,b = 0; puts 'repro: (a,b) = [1,2],3'; (a,b) = [1,2],3; p a,b + a,b = 0; puts 'repro: (a,b) = nil,1'; (a,b) = nil,1; p a,b +end +def f6() + a,b = 0; puts 'repro: (a,*b) = nil'; (a,*b) = nil; p a,b + a,b = 0; puts 'repro: (a,*b) = []'; (a,*b) = []; p a,b + a,b = 0; puts 'repro: (a,*b) = [nil]'; (a,*b) = [nil]; p a,b + a,b = 0; puts 'repro: (a,*b) = 1'; (a,*b) = 1; p a,b + a,b = 0; puts 'repro: (a,*b) = 1,2'; (a,*b) = 1,2; p a,b + a,b = 0; puts 'repro: (a,*b) = 1,2,3'; (a,*b) = 1,2,3; p a,b + a,b = 0; puts 'repro: (a,*b) = [1]'; (a,*b) = [1]; p a,b + a,b = 0; puts 'repro: (a,*b) = [1,2]'; (a,*b) = [1,2]; p a,b + a,b = 0; puts 'repro: (a,*b) = [1,2,3]'; (a,*b) = [1,2,3]; p a,b + a,b = 0; puts 'repro: (a,*b) = *1'; (a,*b) = *1; p a,b + a,b = 0; puts 'repro: (a,*b) = *nil'; (a,*b) = *nil; p a,b + a,b = 0; puts 'repro: (a,*b) = *[]'; (a,*b) = *[]; p a,b + a,b = 0; puts 'repro: (a,*b) = *[nil]'; (a,*b) = *[nil]; p a,b + a,b = 0; puts 'repro: (a,*b) = *[1]'; (a,*b) = *[1]; p a,b + a,b = 0; puts 'repro: (a,*b) = *[1,2]'; (a,*b) = *[1,2]; p a,b + a,b = 0; puts 'repro: (a,*b) = 1,*[2,3]'; (a,*b) = 1,*[2,3]; p a,b + a,b = 0; puts 'repro: (a,*b) = *[[]]'; (a,*b) = *[[]]; p a,b + a,b = 0; puts 'repro: (a,*b) = 1,*[]'; (a,*b) = 1,*[]; p a,b + a,b = 0; puts 'repro: (a,*b) = [1,2],3'; (a,*b) = [1,2],3; p a,b + a,b = 0; puts 'repro: (a,*b) = nil,1'; (a,*b) = nil,1; p a,b +end +def f7() + a = 0; puts 'repro: (*a) = nil'; (*a) = nil; p a + a = 0; puts 'repro: (*a) = []'; (*a) = []; p a + a = 0; puts 'repro: (*a) = [nil]'; (*a) = [nil]; p a + a = 0; puts 'repro: (*a) = 1'; (*a) = 1; p a + a = 0; puts 'repro: (*a) = 1,2'; (*a) = 1,2; p a + a = 0; puts 'repro: (*a) = 1,2,3'; (*a) = 1,2,3; p a + a = 0; puts 'repro: (*a) = [1]'; (*a) = [1]; p a + a = 0; puts 'repro: (*a) = [1,2]'; (*a) = [1,2]; p a + a = 0; puts 'repro: (*a) = [1,2,3]'; (*a) = [1,2,3]; p a + a = 0; puts 'repro: (*a) = *1'; (*a) = *1; p a + a = 0; puts 'repro: (*a) = *nil'; (*a) = *nil; p a + a = 0; puts 'repro: (*a) = *[]'; (*a) = *[]; p a + a = 0; puts 'repro: (*a) = *[nil]'; (*a) = *[nil]; p a + a = 0; puts 'repro: (*a) = *[1]'; (*a) = *[1]; p a + a = 0; puts 'repro: (*a) = *[1,2]'; (*a) = *[1,2]; p a + a = 0; puts 'repro: (*a) = 1,*[2,3]'; (*a) = 1,*[2,3]; p a + a = 0; puts 'repro: (*a) = *[[]]'; (*a) = *[[]]; p a + a = 0; puts 'repro: (*a) = 1,*[]'; (*a) = 1,*[]; p a + a = 0; puts 'repro: (*a) = [1,2],3'; (*a) = [1,2],3; p a + a = 0; puts 'repro: (*a) = nil,1'; (*a) = nil,1; p a +end +def f8() + a,b,c = 0; puts 'repro: (a,b),c = nil'; (a,b),c = nil; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = []'; (a,b),c = []; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = [nil]'; (a,b),c = [nil]; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = 1'; (a,b),c = 1; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = 1,2'; (a,b),c = 1,2; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = 1,2,3'; (a,b),c = 1,2,3; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = [1]'; (a,b),c = [1]; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = [1,2]'; (a,b),c = [1,2]; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = [1,2,3]'; (a,b),c = [1,2,3]; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = *1'; (a,b),c = *1; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = *nil'; (a,b),c = *nil; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = *[]'; (a,b),c = *[]; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = *[nil]'; (a,b),c = *[nil]; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = *[1]'; (a,b),c = *[1]; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = *[1,2]'; (a,b),c = *[1,2]; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = 1,*[2,3]'; (a,b),c = 1,*[2,3]; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = *[[]]'; (a,b),c = *[[]]; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = 1,*[]'; (a,b),c = 1,*[]; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = [1,2],3'; (a,b),c = [1,2],3; p a,b,c + a,b,c = 0; puts 'repro: (a,b),c = nil,1'; (a,b),c = nil,1; p a,b,c +end +f0() +f1() +f2() +f3() +f4() +f5() +f6() +f7() +f8() diff --git a/merlin/main/languages/ruby/Tests/Compat/test_assignment_regression.rb b/merlin/main/languages/ruby/Tests/Compat/test_assignment_regression.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_break.rb b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_break.rb new file mode 100644 index 0000000000..14a502a16d --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_break.rb @@ -0,0 +1,595 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +# helper +def myeval(line); puts 1; eval(line); puts 2; end +def call1(x); puts 3; call2(x); puts 4; end +def call2(x); puts 5; call3(x); puts 6; end +def call3(x); puts 7; puts x.call; puts 8; end + +# producer + +def get_block(&p); p; end +def get_lambda(&p); lambda(&p); end +def get_proc(&p); Proc.new(&p); end + +def get_local_block; get_block { puts 9; break; puts 10 }; end +def get_local_lambda; lambda { puts 11; break; puts 12 }; end +def get_local_proc; Proc.new { puts 13; break; puts 14 }; end + +# consumer + +# taking arguments +def iterator_via_yield; puts 15; x = yield; puts x; puts 16; end +def iterator_via_call(&p); puts 17; puts(p.call); puts 18; end + +def method_call_iterator_via_yield(&p); puts 19; iterator_via_yield(&p); puts 20; end +def method_call_iterator_via_call(&p); puts 21; iterator_via_call(&p); puts 22; end + +def method_use_lambda_and_yield; puts 23; x = lambda { puts 24; yield; puts 25 }; puts x.call; puts 26; end +def method_use_proc_and_yield; puts 27; x = Proc.new { puts 28; yield; puts 29 }; puts x.call; puts 30; end +def method_use_lambda_and_call(&p); puts 31; x = lambda { puts 32; p.call; puts 33 }; puts x.call; puts 34; end +def method_use_proc_and_call(&p); puts 35; x = Proc.new { puts 36; p.call; puts 37 }; puts x.call; puts 38; end + +def method_use_lambda_and_yield_2; puts 39; x = lambda { puts 40; yield; puts 41 }; call1(x); puts 42; end + +def method_yield_in_loop; puts 43; for i in [1, 2]; puts 44; yield; puts 45; end; puts 46; end +def method_call_in_loop(&p); puts 47; for i in [3, 4]; puts 48; p.call; puts 49; end; puts 50; end + +# created in-place +def test +$g = 0; begin; puts 51; iterator_via_yield { puts 52; break; puts 53}; puts 54; rescue; puts 55; puts $!.class; end +$g = 0; def m_1; puts 56; $g = 0; begin; puts 57; iterator_via_yield { puts 58; break; puts 59}; puts 60; rescue; puts 61; puts $!.class; end; puts 62; end; m_1 +$g = 0; begin; puts 63; iterator_via_call { puts 64; break; puts 65}; puts 66; rescue; puts 67; puts $!.class; end +$g = 0; def m_2; puts 68; $g = 0; begin; puts 69; iterator_via_call { puts 70; break; puts 71}; puts 72; rescue; puts 73; puts $!.class; end; puts 74; end; m_2 +$g = 0; begin; puts 75; method_call_iterator_via_yield { puts 76; break; puts 77}; puts 78; rescue; puts 79; puts $!.class; end +$g = 0; def m_3; puts 80; $g = 0; begin; puts 81; method_call_iterator_via_yield { puts 82; break; puts 83}; puts 84; rescue; puts 85; puts $!.class; end; puts 86; end; m_3 +$g = 0; begin; puts 87; method_call_iterator_via_call { puts 88; break; puts 89}; puts 90; rescue; puts 91; puts $!.class; end +$g = 0; def m_4; puts 92; $g = 0; begin; puts 93; method_call_iterator_via_call { puts 94; break; puts 95}; puts 96; rescue; puts 97; puts $!.class; end; puts 98; end; m_4 +$g = 0; begin; puts 99; method_use_lambda_and_yield { puts 100; break; puts 101}; puts 102; rescue; puts 103; puts $!.class; end +$g = 0; def m_5; puts 104; $g = 0; begin; puts 105; method_use_lambda_and_yield { puts 106; break; puts 107}; puts 108; rescue; puts 109; puts $!.class; end; puts 110; end; m_5 +$g = 0; begin; puts 111; method_use_proc_and_yield { puts 112; break; puts 113}; puts 114; rescue; puts 115; puts $!.class; end +$g = 0; def m_6; puts 116; $g = 0; begin; puts 117; method_use_proc_and_yield { puts 118; break; puts 119}; puts 120; rescue; puts 121; puts $!.class; end; puts 122; end; m_6 +$g = 0; begin; puts 123; method_use_lambda_and_call { puts 124; break; puts 125}; puts 126; rescue; puts 127; puts $!.class; end +$g = 0; def m_7; puts 128; $g = 0; begin; puts 129; method_use_lambda_and_call { puts 130; break; puts 131}; puts 132; rescue; puts 133; puts $!.class; end; puts 134; end; m_7 +$g = 0; begin; puts 135; method_use_proc_and_call { puts 136; break; puts 137}; puts 138; rescue; puts 139; puts $!.class; end +$g = 0; def m_8; puts 140; $g = 0; begin; puts 141; method_use_proc_and_call { puts 142; break; puts 143}; puts 144; rescue; puts 145; puts $!.class; end; puts 146; end; m_8 +$g = 0; begin; puts 147; method_use_lambda_and_yield_2 { puts 148; break; puts 149}; puts 150; rescue; puts 151; puts $!.class; end +$g = 0; def m_9; puts 152; $g = 0; begin; puts 153; method_use_lambda_and_yield_2 { puts 154; break; puts 155}; puts 156; rescue; puts 157; puts $!.class; end; puts 158; end; m_9 +$g = 0; begin; puts 159; method_yield_in_loop { puts 160; break; puts 161}; puts 162; rescue; puts 163; puts $!.class; end +$g = 0; def m_10; puts 164; $g = 0; begin; puts 165; method_yield_in_loop { puts 166; break; puts 167}; puts 168; rescue; puts 169; puts $!.class; end; puts 170; end; m_10 +$g = 0; begin; puts 171; method_call_in_loop { puts 172; break; puts 173}; puts 174; rescue; puts 175; puts $!.class; end +$g = 0; def m_11; puts 176; $g = 0; begin; puts 177; method_call_in_loop { puts 178; break; puts 179}; puts 180; rescue; puts 181; puts $!.class; end; puts 182; end; m_11 +end +test + + +# created locally or from method +def test +$g = 0; begin; p = lambda{ puts 183; break; puts 184}; puts 185; iterator_via_yield(&p); puts 186; rescue; puts 187; puts $!.class; end +$g = 0; def m_12; p = lambda{ puts 188; break; puts 189}; puts 190; iterator_via_yield(&p); puts 191; end; +begin; puts 192; m_12; puts 193; rescue; puts 194; puts $!.class; end +$g = 0; begin; p = lambda{ puts 195; break; puts 196}; puts 197; iterator_via_call(&p); puts 198; rescue; puts 199; puts $!.class; end +$g = 0; def m_13; p = lambda{ puts 200; break; puts 201}; puts 202; iterator_via_call(&p); puts 203; end; +begin; puts 204; m_13; puts 205; rescue; puts 206; puts $!.class; end +$g = 0; begin; p = lambda{ puts 207; break; puts 208}; puts 209; method_call_iterator_via_yield(&p); puts 210; rescue; puts 211; puts $!.class; end +$g = 0; def m_14; p = lambda{ puts 212; break; puts 213}; puts 214; method_call_iterator_via_yield(&p); puts 215; end; +begin; puts 216; m_14; puts 217; rescue; puts 218; puts $!.class; end +$g = 0; begin; p = lambda{ puts 219; break; puts 220}; puts 221; method_call_iterator_via_call(&p); puts 222; rescue; puts 223; puts $!.class; end +$g = 0; def m_15; p = lambda{ puts 224; break; puts 225}; puts 226; method_call_iterator_via_call(&p); puts 227; end; +begin; puts 228; m_15; puts 229; rescue; puts 230; puts $!.class; end +$g = 0; begin; p = lambda{ puts 231; break; puts 232}; puts 233; method_use_lambda_and_yield(&p); puts 234; rescue; puts 235; puts $!.class; end +$g = 0; def m_16; p = lambda{ puts 236; break; puts 237}; puts 238; method_use_lambda_and_yield(&p); puts 239; end; +begin; puts 240; m_16; puts 241; rescue; puts 242; puts $!.class; end +$g = 0; begin; p = lambda{ puts 243; break; puts 244}; puts 245; method_use_proc_and_yield(&p); puts 246; rescue; puts 247; puts $!.class; end +$g = 0; def m_17; p = lambda{ puts 248; break; puts 249}; puts 250; method_use_proc_and_yield(&p); puts 251; end; +begin; puts 252; m_17; puts 253; rescue; puts 254; puts $!.class; end +$g = 0; begin; p = lambda{ puts 255; break; puts 256}; puts 257; method_use_lambda_and_call(&p); puts 258; rescue; puts 259; puts $!.class; end +$g = 0; def m_18; p = lambda{ puts 260; break; puts 261}; puts 262; method_use_lambda_and_call(&p); puts 263; end; +begin; puts 264; m_18; puts 265; rescue; puts 266; puts $!.class; end +$g = 0; begin; p = lambda{ puts 267; break; puts 268}; puts 269; method_use_proc_and_call(&p); puts 270; rescue; puts 271; puts $!.class; end +$g = 0; def m_19; p = lambda{ puts 272; break; puts 273}; puts 274; method_use_proc_and_call(&p); puts 275; end; +begin; puts 276; m_19; puts 277; rescue; puts 278; puts $!.class; end +$g = 0; begin; p = lambda{ puts 279; break; puts 280}; puts 281; method_use_lambda_and_yield_2(&p); puts 282; rescue; puts 283; puts $!.class; end +$g = 0; def m_20; p = lambda{ puts 284; break; puts 285}; puts 286; method_use_lambda_and_yield_2(&p); puts 287; end; +begin; puts 288; m_20; puts 289; rescue; puts 290; puts $!.class; end +$g = 0; begin; p = lambda{ puts 291; break; puts 292}; puts 293; method_yield_in_loop(&p); puts 294; rescue; puts 295; puts $!.class; end +$g = 0; def m_21; p = lambda{ puts 296; break; puts 297}; puts 298; method_yield_in_loop(&p); puts 299; end; +begin; puts 300; m_21; puts 301; rescue; puts 302; puts $!.class; end +$g = 0; begin; p = lambda{ puts 303; break; puts 304}; puts 305; method_call_in_loop(&p); puts 306; rescue; puts 307; puts $!.class; end +$g = 0; def m_22; p = lambda{ puts 308; break; puts 309}; puts 310; method_call_in_loop(&p); puts 311; end; +begin; puts 312; m_22; puts 313; rescue; puts 314; puts $!.class; end +end +test + +def test +$g = 0; begin; p = Proc.new{ puts 315; break; puts 316}; puts 317; iterator_via_yield(&p); puts 318; rescue; puts 319; puts $!.class; end +$g = 0; def m_23; p = Proc.new{ puts 320; break; puts 321}; puts 322; iterator_via_yield(&p); puts 323; end; +begin; puts 324; m_23; puts 325; rescue; puts 326; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 327; break; puts 328}; puts 329; iterator_via_call(&p); puts 330; rescue; puts 331; puts $!.class; end +$g = 0; def m_24; p = Proc.new{ puts 332; break; puts 333}; puts 334; iterator_via_call(&p); puts 335; end; +begin; puts 336; m_24; puts 337; rescue; puts 338; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 339; break; puts 340}; puts 341; method_call_iterator_via_yield(&p); puts 342; rescue; puts 343; puts $!.class; end +$g = 0; def m_25; p = Proc.new{ puts 344; break; puts 345}; puts 346; method_call_iterator_via_yield(&p); puts 347; end; +begin; puts 348; m_25; puts 349; rescue; puts 350; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 351; break; puts 352}; puts 353; method_call_iterator_via_call(&p); puts 354; rescue; puts 355; puts $!.class; end +$g = 0; def m_26; p = Proc.new{ puts 356; break; puts 357}; puts 358; method_call_iterator_via_call(&p); puts 359; end; +begin; puts 360; m_26; puts 361; rescue; puts 362; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 363; break; puts 364}; puts 365; method_use_lambda_and_yield(&p); puts 366; rescue; puts 367; puts $!.class; end +$g = 0; def m_27; p = Proc.new{ puts 368; break; puts 369}; puts 370; method_use_lambda_and_yield(&p); puts 371; end; +begin; puts 372; m_27; puts 373; rescue; puts 374; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 375; break; puts 376}; puts 377; method_use_proc_and_yield(&p); puts 378; rescue; puts 379; puts $!.class; end +$g = 0; def m_28; p = Proc.new{ puts 380; break; puts 381}; puts 382; method_use_proc_and_yield(&p); puts 383; end; +begin; puts 384; m_28; puts 385; rescue; puts 386; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 387; break; puts 388}; puts 389; method_use_lambda_and_call(&p); puts 390; rescue; puts 391; puts $!.class; end +$g = 0; def m_29; p = Proc.new{ puts 392; break; puts 393}; puts 394; method_use_lambda_and_call(&p); puts 395; end; +begin; puts 396; m_29; puts 397; rescue; puts 398; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 399; break; puts 400}; puts 401; method_use_proc_and_call(&p); puts 402; rescue; puts 403; puts $!.class; end +$g = 0; def m_30; p = Proc.new{ puts 404; break; puts 405}; puts 406; method_use_proc_and_call(&p); puts 407; end; +begin; puts 408; m_30; puts 409; rescue; puts 410; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 411; break; puts 412}; puts 413; method_use_lambda_and_yield_2(&p); puts 414; rescue; puts 415; puts $!.class; end +$g = 0; def m_31; p = Proc.new{ puts 416; break; puts 417}; puts 418; method_use_lambda_and_yield_2(&p); puts 419; end; +begin; puts 420; m_31; puts 421; rescue; puts 422; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 423; break; puts 424}; puts 425; method_yield_in_loop(&p); puts 426; rescue; puts 427; puts $!.class; end +$g = 0; def m_32; p = Proc.new{ puts 428; break; puts 429}; puts 430; method_yield_in_loop(&p); puts 431; end; +begin; puts 432; m_32; puts 433; rescue; puts 434; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 435; break; puts 436}; puts 437; method_call_in_loop(&p); puts 438; rescue; puts 439; puts $!.class; end +$g = 0; def m_33; p = Proc.new{ puts 440; break; puts 441}; puts 442; method_call_in_loop(&p); puts 443; end; +begin; puts 444; m_33; puts 445; rescue; puts 446; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_block{ puts 447; break; puts 448}; puts 449; iterator_via_yield(&p); puts 450; rescue; puts 451; puts $!.class; end +$g = 0; def m_34; p = get_block{ puts 452; break; puts 453}; puts 454; iterator_via_yield(&p); puts 455; end; +begin; puts 456; m_34; puts 457; rescue; puts 458; puts $!.class; end +$g = 0; begin; p = get_block{ puts 459; break; puts 460}; puts 461; iterator_via_call(&p); puts 462; rescue; puts 463; puts $!.class; end +$g = 0; def m_35; p = get_block{ puts 464; break; puts 465}; puts 466; iterator_via_call(&p); puts 467; end; +begin; puts 468; m_35; puts 469; rescue; puts 470; puts $!.class; end +$g = 0; begin; p = get_block{ puts 471; break; puts 472}; puts 473; method_call_iterator_via_yield(&p); puts 474; rescue; puts 475; puts $!.class; end +$g = 0; def m_36; p = get_block{ puts 476; break; puts 477}; puts 478; method_call_iterator_via_yield(&p); puts 479; end; +begin; puts 480; m_36; puts 481; rescue; puts 482; puts $!.class; end +$g = 0; begin; p = get_block{ puts 483; break; puts 484}; puts 485; method_call_iterator_via_call(&p); puts 486; rescue; puts 487; puts $!.class; end +$g = 0; def m_37; p = get_block{ puts 488; break; puts 489}; puts 490; method_call_iterator_via_call(&p); puts 491; end; +begin; puts 492; m_37; puts 493; rescue; puts 494; puts $!.class; end +$g = 0; begin; p = get_block{ puts 495; break; puts 496}; puts 497; method_use_lambda_and_yield(&p); puts 498; rescue; puts 499; puts $!.class; end +$g = 0; def m_38; p = get_block{ puts 500; break; puts 501}; puts 502; method_use_lambda_and_yield(&p); puts 503; end; +begin; puts 504; m_38; puts 505; rescue; puts 506; puts $!.class; end +$g = 0; begin; p = get_block{ puts 507; break; puts 508}; puts 509; method_use_proc_and_yield(&p); puts 510; rescue; puts 511; puts $!.class; end +$g = 0; def m_39; p = get_block{ puts 512; break; puts 513}; puts 514; method_use_proc_and_yield(&p); puts 515; end; +begin; puts 516; m_39; puts 517; rescue; puts 518; puts $!.class; end +$g = 0; begin; p = get_block{ puts 519; break; puts 520}; puts 521; method_use_lambda_and_call(&p); puts 522; rescue; puts 523; puts $!.class; end +$g = 0; def m_40; p = get_block{ puts 524; break; puts 525}; puts 526; method_use_lambda_and_call(&p); puts 527; end; +begin; puts 528; m_40; puts 529; rescue; puts 530; puts $!.class; end +$g = 0; begin; p = get_block{ puts 531; break; puts 532}; puts 533; method_use_proc_and_call(&p); puts 534; rescue; puts 535; puts $!.class; end +$g = 0; def m_41; p = get_block{ puts 536; break; puts 537}; puts 538; method_use_proc_and_call(&p); puts 539; end; +begin; puts 540; m_41; puts 541; rescue; puts 542; puts $!.class; end +$g = 0; begin; p = get_block{ puts 543; break; puts 544}; puts 545; method_use_lambda_and_yield_2(&p); puts 546; rescue; puts 547; puts $!.class; end +$g = 0; def m_42; p = get_block{ puts 548; break; puts 549}; puts 550; method_use_lambda_and_yield_2(&p); puts 551; end; +begin; puts 552; m_42; puts 553; rescue; puts 554; puts $!.class; end +$g = 0; begin; p = get_block{ puts 555; break; puts 556}; puts 557; method_yield_in_loop(&p); puts 558; rescue; puts 559; puts $!.class; end +$g = 0; def m_43; p = get_block{ puts 560; break; puts 561}; puts 562; method_yield_in_loop(&p); puts 563; end; +begin; puts 564; m_43; puts 565; rescue; puts 566; puts $!.class; end +$g = 0; begin; p = get_block{ puts 567; break; puts 568}; puts 569; method_call_in_loop(&p); puts 570; rescue; puts 571; puts $!.class; end +$g = 0; def m_44; p = get_block{ puts 572; break; puts 573}; puts 574; method_call_in_loop(&p); puts 575; end; +begin; puts 576; m_44; puts 577; rescue; puts 578; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_lambda{ puts 579; break; puts 580}; puts 581; iterator_via_yield(&p); puts 582; rescue; puts 583; puts $!.class; end +$g = 0; def m_45; p = get_lambda{ puts 584; break; puts 585}; puts 586; iterator_via_yield(&p); puts 587; end; +begin; puts 588; m_45; puts 589; rescue; puts 590; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 591; break; puts 592}; puts 593; iterator_via_call(&p); puts 594; rescue; puts 595; puts $!.class; end +$g = 0; def m_46; p = get_lambda{ puts 596; break; puts 597}; puts 598; iterator_via_call(&p); puts 599; end; +begin; puts 600; m_46; puts 601; rescue; puts 602; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 603; break; puts 604}; puts 605; method_call_iterator_via_yield(&p); puts 606; rescue; puts 607; puts $!.class; end +$g = 0; def m_47; p = get_lambda{ puts 608; break; puts 609}; puts 610; method_call_iterator_via_yield(&p); puts 611; end; +begin; puts 612; m_47; puts 613; rescue; puts 614; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 615; break; puts 616}; puts 617; method_call_iterator_via_call(&p); puts 618; rescue; puts 619; puts $!.class; end +$g = 0; def m_48; p = get_lambda{ puts 620; break; puts 621}; puts 622; method_call_iterator_via_call(&p); puts 623; end; +begin; puts 624; m_48; puts 625; rescue; puts 626; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 627; break; puts 628}; puts 629; method_use_lambda_and_yield(&p); puts 630; rescue; puts 631; puts $!.class; end +$g = 0; def m_49; p = get_lambda{ puts 632; break; puts 633}; puts 634; method_use_lambda_and_yield(&p); puts 635; end; +begin; puts 636; m_49; puts 637; rescue; puts 638; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 639; break; puts 640}; puts 641; method_use_proc_and_yield(&p); puts 642; rescue; puts 643; puts $!.class; end +$g = 0; def m_50; p = get_lambda{ puts 644; break; puts 645}; puts 646; method_use_proc_and_yield(&p); puts 647; end; +begin; puts 648; m_50; puts 649; rescue; puts 650; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 651; break; puts 652}; puts 653; method_use_lambda_and_call(&p); puts 654; rescue; puts 655; puts $!.class; end +$g = 0; def m_51; p = get_lambda{ puts 656; break; puts 657}; puts 658; method_use_lambda_and_call(&p); puts 659; end; +begin; puts 660; m_51; puts 661; rescue; puts 662; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 663; break; puts 664}; puts 665; method_use_proc_and_call(&p); puts 666; rescue; puts 667; puts $!.class; end +$g = 0; def m_52; p = get_lambda{ puts 668; break; puts 669}; puts 670; method_use_proc_and_call(&p); puts 671; end; +begin; puts 672; m_52; puts 673; rescue; puts 674; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 675; break; puts 676}; puts 677; method_use_lambda_and_yield_2(&p); puts 678; rescue; puts 679; puts $!.class; end +$g = 0; def m_53; p = get_lambda{ puts 680; break; puts 681}; puts 682; method_use_lambda_and_yield_2(&p); puts 683; end; +begin; puts 684; m_53; puts 685; rescue; puts 686; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 687; break; puts 688}; puts 689; method_yield_in_loop(&p); puts 690; rescue; puts 691; puts $!.class; end +$g = 0; def m_54; p = get_lambda{ puts 692; break; puts 693}; puts 694; method_yield_in_loop(&p); puts 695; end; +begin; puts 696; m_54; puts 697; rescue; puts 698; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 699; break; puts 700}; puts 701; method_call_in_loop(&p); puts 702; rescue; puts 703; puts $!.class; end +$g = 0; def m_55; p = get_lambda{ puts 704; break; puts 705}; puts 706; method_call_in_loop(&p); puts 707; end; +begin; puts 708; m_55; puts 709; rescue; puts 710; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_proc{ puts 711; break; puts 712}; puts 713; iterator_via_yield(&p); puts 714; rescue; puts 715; puts $!.class; end +$g = 0; def m_56; p = get_proc{ puts 716; break; puts 717}; puts 718; iterator_via_yield(&p); puts 719; end; +begin; puts 720; m_56; puts 721; rescue; puts 722; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 723; break; puts 724}; puts 725; iterator_via_call(&p); puts 726; rescue; puts 727; puts $!.class; end +$g = 0; def m_57; p = get_proc{ puts 728; break; puts 729}; puts 730; iterator_via_call(&p); puts 731; end; +begin; puts 732; m_57; puts 733; rescue; puts 734; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 735; break; puts 736}; puts 737; method_call_iterator_via_yield(&p); puts 738; rescue; puts 739; puts $!.class; end +$g = 0; def m_58; p = get_proc{ puts 740; break; puts 741}; puts 742; method_call_iterator_via_yield(&p); puts 743; end; +begin; puts 744; m_58; puts 745; rescue; puts 746; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 747; break; puts 748}; puts 749; method_call_iterator_via_call(&p); puts 750; rescue; puts 751; puts $!.class; end +$g = 0; def m_59; p = get_proc{ puts 752; break; puts 753}; puts 754; method_call_iterator_via_call(&p); puts 755; end; +begin; puts 756; m_59; puts 757; rescue; puts 758; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 759; break; puts 760}; puts 761; method_use_lambda_and_yield(&p); puts 762; rescue; puts 763; puts $!.class; end +$g = 0; def m_60; p = get_proc{ puts 764; break; puts 765}; puts 766; method_use_lambda_and_yield(&p); puts 767; end; +begin; puts 768; m_60; puts 769; rescue; puts 770; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 771; break; puts 772}; puts 773; method_use_proc_and_yield(&p); puts 774; rescue; puts 775; puts $!.class; end +$g = 0; def m_61; p = get_proc{ puts 776; break; puts 777}; puts 778; method_use_proc_and_yield(&p); puts 779; end; +begin; puts 780; m_61; puts 781; rescue; puts 782; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 783; break; puts 784}; puts 785; method_use_lambda_and_call(&p); puts 786; rescue; puts 787; puts $!.class; end +$g = 0; def m_62; p = get_proc{ puts 788; break; puts 789}; puts 790; method_use_lambda_and_call(&p); puts 791; end; +begin; puts 792; m_62; puts 793; rescue; puts 794; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 795; break; puts 796}; puts 797; method_use_proc_and_call(&p); puts 798; rescue; puts 799; puts $!.class; end +$g = 0; def m_63; p = get_proc{ puts 800; break; puts 801}; puts 802; method_use_proc_and_call(&p); puts 803; end; +begin; puts 804; m_63; puts 805; rescue; puts 806; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 807; break; puts 808}; puts 809; method_use_lambda_and_yield_2(&p); puts 810; rescue; puts 811; puts $!.class; end +$g = 0; def m_64; p = get_proc{ puts 812; break; puts 813}; puts 814; method_use_lambda_and_yield_2(&p); puts 815; end; +begin; puts 816; m_64; puts 817; rescue; puts 818; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 819; break; puts 820}; puts 821; method_yield_in_loop(&p); puts 822; rescue; puts 823; puts $!.class; end +$g = 0; def m_65; p = get_proc{ puts 824; break; puts 825}; puts 826; method_yield_in_loop(&p); puts 827; end; +begin; puts 828; m_65; puts 829; rescue; puts 830; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 831; break; puts 832}; puts 833; method_call_in_loop(&p); puts 834; rescue; puts 835; puts $!.class; end +$g = 0; def m_66; p = get_proc{ puts 836; break; puts 837}; puts 838; method_call_in_loop(&p); puts 839; end; +begin; puts 840; m_66; puts 841; rescue; puts 842; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_block; puts 843; iterator_via_yield(&p); puts 844; rescue; puts 845; puts $!.class; end +$g = 0; def m_67; p = get_local_block; puts 846; iterator_via_yield(&p); puts 847; end; +begin; puts 848; m_67; puts 849; rescue; puts 850; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 851; iterator_via_call(&p); puts 852; rescue; puts 853; puts $!.class; end +$g = 0; def m_68; p = get_local_block; puts 854; iterator_via_call(&p); puts 855; end; +begin; puts 856; m_68; puts 857; rescue; puts 858; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 859; method_call_iterator_via_yield(&p); puts 860; rescue; puts 861; puts $!.class; end +$g = 0; def m_69; p = get_local_block; puts 862; method_call_iterator_via_yield(&p); puts 863; end; +begin; puts 864; m_69; puts 865; rescue; puts 866; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 867; method_call_iterator_via_call(&p); puts 868; rescue; puts 869; puts $!.class; end +$g = 0; def m_70; p = get_local_block; puts 870; method_call_iterator_via_call(&p); puts 871; end; +begin; puts 872; m_70; puts 873; rescue; puts 874; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 875; method_use_lambda_and_yield(&p); puts 876; rescue; puts 877; puts $!.class; end +$g = 0; def m_71; p = get_local_block; puts 878; method_use_lambda_and_yield(&p); puts 879; end; +begin; puts 880; m_71; puts 881; rescue; puts 882; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 883; method_use_proc_and_yield(&p); puts 884; rescue; puts 885; puts $!.class; end +$g = 0; def m_72; p = get_local_block; puts 886; method_use_proc_and_yield(&p); puts 887; end; +begin; puts 888; m_72; puts 889; rescue; puts 890; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 891; method_use_lambda_and_call(&p); puts 892; rescue; puts 893; puts $!.class; end +$g = 0; def m_73; p = get_local_block; puts 894; method_use_lambda_and_call(&p); puts 895; end; +begin; puts 896; m_73; puts 897; rescue; puts 898; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 899; method_use_proc_and_call(&p); puts 900; rescue; puts 901; puts $!.class; end +$g = 0; def m_74; p = get_local_block; puts 902; method_use_proc_and_call(&p); puts 903; end; +begin; puts 904; m_74; puts 905; rescue; puts 906; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 907; method_use_lambda_and_yield_2(&p); puts 908; rescue; puts 909; puts $!.class; end +$g = 0; def m_75; p = get_local_block; puts 910; method_use_lambda_and_yield_2(&p); puts 911; end; +begin; puts 912; m_75; puts 913; rescue; puts 914; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 915; method_yield_in_loop(&p); puts 916; rescue; puts 917; puts $!.class; end +$g = 0; def m_76; p = get_local_block; puts 918; method_yield_in_loop(&p); puts 919; end; +begin; puts 920; m_76; puts 921; rescue; puts 922; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 923; method_call_in_loop(&p); puts 924; rescue; puts 925; puts $!.class; end +$g = 0; def m_77; p = get_local_block; puts 926; method_call_in_loop(&p); puts 927; end; +begin; puts 928; m_77; puts 929; rescue; puts 930; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_lambda; puts 931; iterator_via_yield(&p); puts 932; rescue; puts 933; puts $!.class; end +$g = 0; def m_78; p = get_local_lambda; puts 934; iterator_via_yield(&p); puts 935; end; +begin; puts 936; m_78; puts 937; rescue; puts 938; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 939; iterator_via_call(&p); puts 940; rescue; puts 941; puts $!.class; end +$g = 0; def m_79; p = get_local_lambda; puts 942; iterator_via_call(&p); puts 943; end; +begin; puts 944; m_79; puts 945; rescue; puts 946; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 947; method_call_iterator_via_yield(&p); puts 948; rescue; puts 949; puts $!.class; end +$g = 0; def m_80; p = get_local_lambda; puts 950; method_call_iterator_via_yield(&p); puts 951; end; +begin; puts 952; m_80; puts 953; rescue; puts 954; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 955; method_call_iterator_via_call(&p); puts 956; rescue; puts 957; puts $!.class; end +$g = 0; def m_81; p = get_local_lambda; puts 958; method_call_iterator_via_call(&p); puts 959; end; +begin; puts 960; m_81; puts 961; rescue; puts 962; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 963; method_use_lambda_and_yield(&p); puts 964; rescue; puts 965; puts $!.class; end +$g = 0; def m_82; p = get_local_lambda; puts 966; method_use_lambda_and_yield(&p); puts 967; end; +begin; puts 968; m_82; puts 969; rescue; puts 970; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 971; method_use_proc_and_yield(&p); puts 972; rescue; puts 973; puts $!.class; end +$g = 0; def m_83; p = get_local_lambda; puts 974; method_use_proc_and_yield(&p); puts 975; end; +begin; puts 976; m_83; puts 977; rescue; puts 978; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 979; method_use_lambda_and_call(&p); puts 980; rescue; puts 981; puts $!.class; end +$g = 0; def m_84; p = get_local_lambda; puts 982; method_use_lambda_and_call(&p); puts 983; end; +begin; puts 984; m_84; puts 985; rescue; puts 986; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 987; method_use_proc_and_call(&p); puts 988; rescue; puts 989; puts $!.class; end +$g = 0; def m_85; p = get_local_lambda; puts 990; method_use_proc_and_call(&p); puts 991; end; +begin; puts 992; m_85; puts 993; rescue; puts 994; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 995; method_use_lambda_and_yield_2(&p); puts 996; rescue; puts 997; puts $!.class; end +$g = 0; def m_86; p = get_local_lambda; puts 998; method_use_lambda_and_yield_2(&p); puts 999; end; +begin; puts 1000; m_86; puts 1001; rescue; puts 1002; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1003; method_yield_in_loop(&p); puts 1004; rescue; puts 1005; puts $!.class; end +$g = 0; def m_87; p = get_local_lambda; puts 1006; method_yield_in_loop(&p); puts 1007; end; +begin; puts 1008; m_87; puts 1009; rescue; puts 1010; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1011; method_call_in_loop(&p); puts 1012; rescue; puts 1013; puts $!.class; end +$g = 0; def m_88; p = get_local_lambda; puts 1014; method_call_in_loop(&p); puts 1015; end; +begin; puts 1016; m_88; puts 1017; rescue; puts 1018; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_proc; puts 1019; iterator_via_yield(&p); puts 1020; rescue; puts 1021; puts $!.class; end +$g = 0; def m_89; p = get_local_proc; puts 1022; iterator_via_yield(&p); puts 1023; end; +begin; puts 1024; m_89; puts 1025; rescue; puts 1026; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1027; iterator_via_call(&p); puts 1028; rescue; puts 1029; puts $!.class; end +$g = 0; def m_90; p = get_local_proc; puts 1030; iterator_via_call(&p); puts 1031; end; +begin; puts 1032; m_90; puts 1033; rescue; puts 1034; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1035; method_call_iterator_via_yield(&p); puts 1036; rescue; puts 1037; puts $!.class; end +$g = 0; def m_91; p = get_local_proc; puts 1038; method_call_iterator_via_yield(&p); puts 1039; end; +begin; puts 1040; m_91; puts 1041; rescue; puts 1042; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1043; method_call_iterator_via_call(&p); puts 1044; rescue; puts 1045; puts $!.class; end +$g = 0; def m_92; p = get_local_proc; puts 1046; method_call_iterator_via_call(&p); puts 1047; end; +begin; puts 1048; m_92; puts 1049; rescue; puts 1050; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1051; method_use_lambda_and_yield(&p); puts 1052; rescue; puts 1053; puts $!.class; end +$g = 0; def m_93; p = get_local_proc; puts 1054; method_use_lambda_and_yield(&p); puts 1055; end; +begin; puts 1056; m_93; puts 1057; rescue; puts 1058; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1059; method_use_proc_and_yield(&p); puts 1060; rescue; puts 1061; puts $!.class; end +$g = 0; def m_94; p = get_local_proc; puts 1062; method_use_proc_and_yield(&p); puts 1063; end; +begin; puts 1064; m_94; puts 1065; rescue; puts 1066; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1067; method_use_lambda_and_call(&p); puts 1068; rescue; puts 1069; puts $!.class; end +$g = 0; def m_95; p = get_local_proc; puts 1070; method_use_lambda_and_call(&p); puts 1071; end; +begin; puts 1072; m_95; puts 1073; rescue; puts 1074; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1075; method_use_proc_and_call(&p); puts 1076; rescue; puts 1077; puts $!.class; end +$g = 0; def m_96; p = get_local_proc; puts 1078; method_use_proc_and_call(&p); puts 1079; end; +begin; puts 1080; m_96; puts 1081; rescue; puts 1082; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1083; method_use_lambda_and_yield_2(&p); puts 1084; rescue; puts 1085; puts $!.class; end +$g = 0; def m_97; p = get_local_proc; puts 1086; method_use_lambda_and_yield_2(&p); puts 1087; end; +begin; puts 1088; m_97; puts 1089; rescue; puts 1090; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1091; method_yield_in_loop(&p); puts 1092; rescue; puts 1093; puts $!.class; end +$g = 0; def m_98; p = get_local_proc; puts 1094; method_yield_in_loop(&p); puts 1095; end; +begin; puts 1096; m_98; puts 1097; rescue; puts 1098; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1099; method_call_in_loop(&p); puts 1100; rescue; puts 1101; puts $!.class; end +$g = 0; def m_99; p = get_local_proc; puts 1102; method_call_in_loop(&p); puts 1103; end; +begin; puts 1104; m_99; puts 1105; rescue; puts 1106; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1107; p = lambda{ puts 1108; break; puts 1109}; puts(p.call); puts 1110; rescue; puts 1111; puts $!.class; end +$g = 0; def m_100; puts 1112; p = lambda{ puts 1113; break; puts 1114}; puts(p.call); puts 1115; end; +begin; puts 1116; m_100; puts 1117; rescue; puts 1118; puts $!.class; end +$g = 0; begin; puts 1119; puts m_100; puts 1120; rescue; puts 1121; puts $!.class; end +$g = 0; def m_101; puts 1122; puts m_100; puts 1123; end; +begin; puts 1124; m_101; puts 1125; rescue; puts 1126; puts $!.class; end +$g = 0; begin; puts 1127; p = Proc.new{ puts 1128; break; puts 1129}; puts(p.call); puts 1130; rescue; puts 1131; puts $!.class; end +$g = 0; def m_102; puts 1132; p = Proc.new{ puts 1133; break; puts 1134}; puts(p.call); puts 1135; end; +begin; puts 1136; m_102; puts 1137; rescue; puts 1138; puts $!.class; end +$g = 0; begin; puts 1139; puts m_102; puts 1140; rescue; puts 1141; puts $!.class; end +$g = 0; def m_103; puts 1142; puts m_102; puts 1143; end; +begin; puts 1144; m_103; puts 1145; rescue; puts 1146; puts $!.class; end +$g = 0; begin; puts 1147; p = get_block{ puts 1148; break; puts 1149}; puts(p.call); puts 1150; rescue; puts 1151; puts $!.class; end +$g = 0; def m_104; puts 1152; p = get_block{ puts 1153; break; puts 1154}; puts(p.call); puts 1155; end; +begin; puts 1156; m_104; puts 1157; rescue; puts 1158; puts $!.class; end +$g = 0; begin; puts 1159; puts m_104; puts 1160; rescue; puts 1161; puts $!.class; end +$g = 0; def m_105; puts 1162; puts m_104; puts 1163; end; +begin; puts 1164; m_105; puts 1165; rescue; puts 1166; puts $!.class; end +$g = 0; begin; puts 1167; p = get_lambda{ puts 1168; break; puts 1169}; puts(p.call); puts 1170; rescue; puts 1171; puts $!.class; end +$g = 0; def m_106; puts 1172; p = get_lambda{ puts 1173; break; puts 1174}; puts(p.call); puts 1175; end; +begin; puts 1176; m_106; puts 1177; rescue; puts 1178; puts $!.class; end +$g = 0; begin; puts 1179; puts m_106; puts 1180; rescue; puts 1181; puts $!.class; end +$g = 0; def m_107; puts 1182; puts m_106; puts 1183; end; +begin; puts 1184; m_107; puts 1185; rescue; puts 1186; puts $!.class; end +$g = 0; begin; puts 1187; p = get_proc{ puts 1188; break; puts 1189}; puts(p.call); puts 1190; rescue; puts 1191; puts $!.class; end +$g = 0; def m_108; puts 1192; p = get_proc{ puts 1193; break; puts 1194}; puts(p.call); puts 1195; end; +begin; puts 1196; m_108; puts 1197; rescue; puts 1198; puts $!.class; end +$g = 0; begin; puts 1199; puts m_108; puts 1200; rescue; puts 1201; puts $!.class; end +$g = 0; def m_109; puts 1202; puts m_108; puts 1203; end; +begin; puts 1204; m_109; puts 1205; rescue; puts 1206; puts $!.class; end +$g = 0; begin; puts 1207; p = get_local_block; puts(p.call); puts 1208; rescue; puts 1209; puts $!.class; end +$g = 0; def m_110; puts 1210; p = get_local_block; puts(p.call); puts 1211; end; +begin; puts 1212; m_110; puts 1213; rescue; puts 1214; puts $!.class; end +$g = 0; begin; puts 1215; puts m_110; puts 1216; rescue; puts 1217; puts $!.class; end +$g = 0; def m_111; puts 1218; puts m_110; puts 1219; end; +begin; puts 1220; m_111; puts 1221; rescue; puts 1222; puts $!.class; end +$g = 0; begin; puts 1223; p = get_local_lambda; puts(p.call); puts 1224; rescue; puts 1225; puts $!.class; end +$g = 0; def m_112; puts 1226; p = get_local_lambda; puts(p.call); puts 1227; end; +begin; puts 1228; m_112; puts 1229; rescue; puts 1230; puts $!.class; end +$g = 0; begin; puts 1231; puts m_112; puts 1232; rescue; puts 1233; puts $!.class; end +$g = 0; def m_113; puts 1234; puts m_112; puts 1235; end; +begin; puts 1236; m_113; puts 1237; rescue; puts 1238; puts $!.class; end +$g = 0; begin; puts 1239; p = get_local_proc; puts(p.call); puts 1240; rescue; puts 1241; puts $!.class; end +$g = 0; def m_114; puts 1242; p = get_local_proc; puts(p.call); puts 1243; end; +begin; puts 1244; m_114; puts 1245; rescue; puts 1246; puts $!.class; end +$g = 0; begin; puts 1247; puts m_114; puts 1248; rescue; puts 1249; puts $!.class; end +$g = 0; def m_115; puts 1250; puts m_114; puts 1251; end; +begin; puts 1252; m_115; puts 1253; rescue; puts 1254; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1255; x = lambda { puts 1256; p = lambda{ puts 1257; break; puts 1258}; puts p.call; puts 1259}; puts x.call; puts 1260; rescue; puts 1261; puts $!.class; end +$g = 0; def m_116; puts 1262; x = lambda { puts 1263; p = lambda{ puts 1264; break; puts 1265}; puts p.call; puts 1266}; puts x.call; puts 1267; end; +begin; puts 1268; m_116; puts 1269; rescue; puts 1270; puts $!.class; end +$g = 0; begin; puts 1271; x = lambda { puts 1272; p = Proc.new{ puts 1273; break; puts 1274}; puts p.call; puts 1275}; puts x.call; puts 1276; rescue; puts 1277; puts $!.class; end +$g = 0; def m_117; puts 1278; x = lambda { puts 1279; p = Proc.new{ puts 1280; break; puts 1281}; puts p.call; puts 1282}; puts x.call; puts 1283; end; +begin; puts 1284; m_117; puts 1285; rescue; puts 1286; puts $!.class; end +$g = 0; begin; puts 1287; x = lambda { puts 1288; p = get_block{ puts 1289; break; puts 1290}; puts p.call; puts 1291}; puts x.call; puts 1292; rescue; puts 1293; puts $!.class; end +$g = 0; def m_118; puts 1294; x = lambda { puts 1295; p = get_block{ puts 1296; break; puts 1297}; puts p.call; puts 1298}; puts x.call; puts 1299; end; +begin; puts 1300; m_118; puts 1301; rescue; puts 1302; puts $!.class; end +$g = 0; begin; puts 1303; x = lambda { puts 1304; p = get_lambda{ puts 1305; break; puts 1306}; puts p.call; puts 1307}; puts x.call; puts 1308; rescue; puts 1309; puts $!.class; end +$g = 0; def m_119; puts 1310; x = lambda { puts 1311; p = get_lambda{ puts 1312; break; puts 1313}; puts p.call; puts 1314}; puts x.call; puts 1315; end; +begin; puts 1316; m_119; puts 1317; rescue; puts 1318; puts $!.class; end +$g = 0; begin; puts 1319; x = lambda { puts 1320; p = get_proc{ puts 1321; break; puts 1322}; puts p.call; puts 1323}; puts x.call; puts 1324; rescue; puts 1325; puts $!.class; end +$g = 0; def m_120; puts 1326; x = lambda { puts 1327; p = get_proc{ puts 1328; break; puts 1329}; puts p.call; puts 1330}; puts x.call; puts 1331; end; +begin; puts 1332; m_120; puts 1333; rescue; puts 1334; puts $!.class; end +$g = 0; begin; puts 1335; x = lambda { puts 1336; p = get_local_block; puts p.call; puts 1337}; puts x.call; puts 1338; rescue; puts 1339; puts $!.class; end +$g = 0; def m_121; puts 1340; x = lambda { puts 1341; p = get_local_block; puts p.call; puts 1342}; puts x.call; puts 1343; end; +begin; puts 1344; m_121; puts 1345; rescue; puts 1346; puts $!.class; end +$g = 0; begin; puts 1347; x = lambda { puts 1348; p = get_local_lambda; puts p.call; puts 1349}; puts x.call; puts 1350; rescue; puts 1351; puts $!.class; end +$g = 0; def m_122; puts 1352; x = lambda { puts 1353; p = get_local_lambda; puts p.call; puts 1354}; puts x.call; puts 1355; end; +begin; puts 1356; m_122; puts 1357; rescue; puts 1358; puts $!.class; end +$g = 0; begin; puts 1359; x = lambda { puts 1360; p = get_local_proc; puts p.call; puts 1361}; puts x.call; puts 1362; rescue; puts 1363; puts $!.class; end +$g = 0; def m_123; puts 1364; x = lambda { puts 1365; p = get_local_proc; puts p.call; puts 1366}; puts x.call; puts 1367; end; +begin; puts 1368; m_123; puts 1369; rescue; puts 1370; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1371; x = Proc.new { puts 1372; p = lambda{ puts 1373; break; puts 1374}; puts p.call; puts 1375}; puts x.call; puts 1376; rescue; puts 1377; puts $!.class; end +$g = 0; def m_124; puts 1378; x = Proc.new { puts 1379; p = lambda{ puts 1380; break; puts 1381}; puts p.call; puts 1382}; puts x.call; puts 1383; end; +begin; puts 1384; m_124; puts 1385; rescue; puts 1386; puts $!.class; end +$g = 0; begin; puts 1387; x = Proc.new { puts 1388; p = Proc.new{ puts 1389; break; puts 1390}; puts p.call; puts 1391}; puts x.call; puts 1392; rescue; puts 1393; puts $!.class; end +$g = 0; def m_125; puts 1394; x = Proc.new { puts 1395; p = Proc.new{ puts 1396; break; puts 1397}; puts p.call; puts 1398}; puts x.call; puts 1399; end; +begin; puts 1400; m_125; puts 1401; rescue; puts 1402; puts $!.class; end +$g = 0; begin; puts 1403; x = Proc.new { puts 1404; p = get_block{ puts 1405; break; puts 1406}; puts p.call; puts 1407}; puts x.call; puts 1408; rescue; puts 1409; puts $!.class; end +$g = 0; def m_126; puts 1410; x = Proc.new { puts 1411; p = get_block{ puts 1412; break; puts 1413}; puts p.call; puts 1414}; puts x.call; puts 1415; end; +begin; puts 1416; m_126; puts 1417; rescue; puts 1418; puts $!.class; end +$g = 0; begin; puts 1419; x = Proc.new { puts 1420; p = get_lambda{ puts 1421; break; puts 1422}; puts p.call; puts 1423}; puts x.call; puts 1424; rescue; puts 1425; puts $!.class; end +$g = 0; def m_127; puts 1426; x = Proc.new { puts 1427; p = get_lambda{ puts 1428; break; puts 1429}; puts p.call; puts 1430}; puts x.call; puts 1431; end; +begin; puts 1432; m_127; puts 1433; rescue; puts 1434; puts $!.class; end +$g = 0; begin; puts 1435; x = Proc.new { puts 1436; p = get_proc{ puts 1437; break; puts 1438}; puts p.call; puts 1439}; puts x.call; puts 1440; rescue; puts 1441; puts $!.class; end +$g = 0; def m_128; puts 1442; x = Proc.new { puts 1443; p = get_proc{ puts 1444; break; puts 1445}; puts p.call; puts 1446}; puts x.call; puts 1447; end; +begin; puts 1448; m_128; puts 1449; rescue; puts 1450; puts $!.class; end +$g = 0; begin; puts 1451; x = Proc.new { puts 1452; p = get_local_block; puts p.call; puts 1453}; puts x.call; puts 1454; rescue; puts 1455; puts $!.class; end +$g = 0; def m_129; puts 1456; x = Proc.new { puts 1457; p = get_local_block; puts p.call; puts 1458}; puts x.call; puts 1459; end; +begin; puts 1460; m_129; puts 1461; rescue; puts 1462; puts $!.class; end +$g = 0; begin; puts 1463; x = Proc.new { puts 1464; p = get_local_lambda; puts p.call; puts 1465}; puts x.call; puts 1466; rescue; puts 1467; puts $!.class; end +$g = 0; def m_130; puts 1468; x = Proc.new { puts 1469; p = get_local_lambda; puts p.call; puts 1470}; puts x.call; puts 1471; end; +begin; puts 1472; m_130; puts 1473; rescue; puts 1474; puts $!.class; end +$g = 0; begin; puts 1475; x = Proc.new { puts 1476; p = get_local_proc; puts p.call; puts 1477}; puts x.call; puts 1478; rescue; puts 1479; puts $!.class; end +$g = 0; def m_131; puts 1480; x = Proc.new { puts 1481; p = get_local_proc; puts p.call; puts 1482}; puts x.call; puts 1483; end; +begin; puts 1484; m_131; puts 1485; rescue; puts 1486; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1487; x = get_block { puts 1488; p = lambda{ puts 1489; break; puts 1490}; puts p.call; puts 1491}; puts x.call; puts 1492; rescue; puts 1493; puts $!.class; end +$g = 0; def m_132; puts 1494; x = get_block { puts 1495; p = lambda{ puts 1496; break; puts 1497}; puts p.call; puts 1498}; puts x.call; puts 1499; end; +begin; puts 1500; m_132; puts 1501; rescue; puts 1502; puts $!.class; end +$g = 0; begin; puts 1503; x = get_block { puts 1504; p = Proc.new{ puts 1505; break; puts 1506}; puts p.call; puts 1507}; puts x.call; puts 1508; rescue; puts 1509; puts $!.class; end +$g = 0; def m_133; puts 1510; x = get_block { puts 1511; p = Proc.new{ puts 1512; break; puts 1513}; puts p.call; puts 1514}; puts x.call; puts 1515; end; +begin; puts 1516; m_133; puts 1517; rescue; puts 1518; puts $!.class; end +$g = 0; begin; puts 1519; x = get_block { puts 1520; p = get_block{ puts 1521; break; puts 1522}; puts p.call; puts 1523}; puts x.call; puts 1524; rescue; puts 1525; puts $!.class; end +$g = 0; def m_134; puts 1526; x = get_block { puts 1527; p = get_block{ puts 1528; break; puts 1529}; puts p.call; puts 1530}; puts x.call; puts 1531; end; +begin; puts 1532; m_134; puts 1533; rescue; puts 1534; puts $!.class; end +$g = 0; begin; puts 1535; x = get_block { puts 1536; p = get_lambda{ puts 1537; break; puts 1538}; puts p.call; puts 1539}; puts x.call; puts 1540; rescue; puts 1541; puts $!.class; end +$g = 0; def m_135; puts 1542; x = get_block { puts 1543; p = get_lambda{ puts 1544; break; puts 1545}; puts p.call; puts 1546}; puts x.call; puts 1547; end; +begin; puts 1548; m_135; puts 1549; rescue; puts 1550; puts $!.class; end +$g = 0; begin; puts 1551; x = get_block { puts 1552; p = get_proc{ puts 1553; break; puts 1554}; puts p.call; puts 1555}; puts x.call; puts 1556; rescue; puts 1557; puts $!.class; end +$g = 0; def m_136; puts 1558; x = get_block { puts 1559; p = get_proc{ puts 1560; break; puts 1561}; puts p.call; puts 1562}; puts x.call; puts 1563; end; +begin; puts 1564; m_136; puts 1565; rescue; puts 1566; puts $!.class; end +$g = 0; begin; puts 1567; x = get_block { puts 1568; p = get_local_block; puts p.call; puts 1569}; puts x.call; puts 1570; rescue; puts 1571; puts $!.class; end +$g = 0; def m_137; puts 1572; x = get_block { puts 1573; p = get_local_block; puts p.call; puts 1574}; puts x.call; puts 1575; end; +begin; puts 1576; m_137; puts 1577; rescue; puts 1578; puts $!.class; end +$g = 0; begin; puts 1579; x = get_block { puts 1580; p = get_local_lambda; puts p.call; puts 1581}; puts x.call; puts 1582; rescue; puts 1583; puts $!.class; end +$g = 0; def m_138; puts 1584; x = get_block { puts 1585; p = get_local_lambda; puts p.call; puts 1586}; puts x.call; puts 1587; end; +begin; puts 1588; m_138; puts 1589; rescue; puts 1590; puts $!.class; end +$g = 0; begin; puts 1591; x = get_block { puts 1592; p = get_local_proc; puts p.call; puts 1593}; puts x.call; puts 1594; rescue; puts 1595; puts $!.class; end +$g = 0; def m_139; puts 1596; x = get_block { puts 1597; p = get_local_proc; puts p.call; puts 1598}; puts x.call; puts 1599; end; +begin; puts 1600; m_139; puts 1601; rescue; puts 1602; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1603; x = get_lambda { puts 1604; p = lambda{ puts 1605; break; puts 1606}; puts p.call; puts 1607}; puts x.call; puts 1608; rescue; puts 1609; puts $!.class; end +$g = 0; def m_140; puts 1610; x = get_lambda { puts 1611; p = lambda{ puts 1612; break; puts 1613}; puts p.call; puts 1614}; puts x.call; puts 1615; end; +begin; puts 1616; m_140; puts 1617; rescue; puts 1618; puts $!.class; end +$g = 0; begin; puts 1619; x = get_lambda { puts 1620; p = Proc.new{ puts 1621; break; puts 1622}; puts p.call; puts 1623}; puts x.call; puts 1624; rescue; puts 1625; puts $!.class; end +$g = 0; def m_141; puts 1626; x = get_lambda { puts 1627; p = Proc.new{ puts 1628; break; puts 1629}; puts p.call; puts 1630}; puts x.call; puts 1631; end; +begin; puts 1632; m_141; puts 1633; rescue; puts 1634; puts $!.class; end +$g = 0; begin; puts 1635; x = get_lambda { puts 1636; p = get_block{ puts 1637; break; puts 1638}; puts p.call; puts 1639}; puts x.call; puts 1640; rescue; puts 1641; puts $!.class; end +$g = 0; def m_142; puts 1642; x = get_lambda { puts 1643; p = get_block{ puts 1644; break; puts 1645}; puts p.call; puts 1646}; puts x.call; puts 1647; end; +begin; puts 1648; m_142; puts 1649; rescue; puts 1650; puts $!.class; end +$g = 0; begin; puts 1651; x = get_lambda { puts 1652; p = get_lambda{ puts 1653; break; puts 1654}; puts p.call; puts 1655}; puts x.call; puts 1656; rescue; puts 1657; puts $!.class; end +$g = 0; def m_143; puts 1658; x = get_lambda { puts 1659; p = get_lambda{ puts 1660; break; puts 1661}; puts p.call; puts 1662}; puts x.call; puts 1663; end; +begin; puts 1664; m_143; puts 1665; rescue; puts 1666; puts $!.class; end +$g = 0; begin; puts 1667; x = get_lambda { puts 1668; p = get_proc{ puts 1669; break; puts 1670}; puts p.call; puts 1671}; puts x.call; puts 1672; rescue; puts 1673; puts $!.class; end +$g = 0; def m_144; puts 1674; x = get_lambda { puts 1675; p = get_proc{ puts 1676; break; puts 1677}; puts p.call; puts 1678}; puts x.call; puts 1679; end; +begin; puts 1680; m_144; puts 1681; rescue; puts 1682; puts $!.class; end +$g = 0; begin; puts 1683; x = get_lambda { puts 1684; p = get_local_block; puts p.call; puts 1685}; puts x.call; puts 1686; rescue; puts 1687; puts $!.class; end +$g = 0; def m_145; puts 1688; x = get_lambda { puts 1689; p = get_local_block; puts p.call; puts 1690}; puts x.call; puts 1691; end; +begin; puts 1692; m_145; puts 1693; rescue; puts 1694; puts $!.class; end +$g = 0; begin; puts 1695; x = get_lambda { puts 1696; p = get_local_lambda; puts p.call; puts 1697}; puts x.call; puts 1698; rescue; puts 1699; puts $!.class; end +$g = 0; def m_146; puts 1700; x = get_lambda { puts 1701; p = get_local_lambda; puts p.call; puts 1702}; puts x.call; puts 1703; end; +begin; puts 1704; m_146; puts 1705; rescue; puts 1706; puts $!.class; end +$g = 0; begin; puts 1707; x = get_lambda { puts 1708; p = get_local_proc; puts p.call; puts 1709}; puts x.call; puts 1710; rescue; puts 1711; puts $!.class; end +$g = 0; def m_147; puts 1712; x = get_lambda { puts 1713; p = get_local_proc; puts p.call; puts 1714}; puts x.call; puts 1715; end; +begin; puts 1716; m_147; puts 1717; rescue; puts 1718; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1719; x = get_proc { puts 1720; p = lambda{ puts 1721; break; puts 1722}; puts p.call; puts 1723}; puts x.call; puts 1724; rescue; puts 1725; puts $!.class; end +$g = 0; def m_148; puts 1726; x = get_proc { puts 1727; p = lambda{ puts 1728; break; puts 1729}; puts p.call; puts 1730}; puts x.call; puts 1731; end; +begin; puts 1732; m_148; puts 1733; rescue; puts 1734; puts $!.class; end +$g = 0; begin; puts 1735; x = get_proc { puts 1736; p = Proc.new{ puts 1737; break; puts 1738}; puts p.call; puts 1739}; puts x.call; puts 1740; rescue; puts 1741; puts $!.class; end +$g = 0; def m_149; puts 1742; x = get_proc { puts 1743; p = Proc.new{ puts 1744; break; puts 1745}; puts p.call; puts 1746}; puts x.call; puts 1747; end; +begin; puts 1748; m_149; puts 1749; rescue; puts 1750; puts $!.class; end +$g = 0; begin; puts 1751; x = get_proc { puts 1752; p = get_block{ puts 1753; break; puts 1754}; puts p.call; puts 1755}; puts x.call; puts 1756; rescue; puts 1757; puts $!.class; end +$g = 0; def m_150; puts 1758; x = get_proc { puts 1759; p = get_block{ puts 1760; break; puts 1761}; puts p.call; puts 1762}; puts x.call; puts 1763; end; +begin; puts 1764; m_150; puts 1765; rescue; puts 1766; puts $!.class; end +$g = 0; begin; puts 1767; x = get_proc { puts 1768; p = get_lambda{ puts 1769; break; puts 1770}; puts p.call; puts 1771}; puts x.call; puts 1772; rescue; puts 1773; puts $!.class; end +$g = 0; def m_151; puts 1774; x = get_proc { puts 1775; p = get_lambda{ puts 1776; break; puts 1777}; puts p.call; puts 1778}; puts x.call; puts 1779; end; +begin; puts 1780; m_151; puts 1781; rescue; puts 1782; puts $!.class; end +$g = 0; begin; puts 1783; x = get_proc { puts 1784; p = get_proc{ puts 1785; break; puts 1786}; puts p.call; puts 1787}; puts x.call; puts 1788; rescue; puts 1789; puts $!.class; end +$g = 0; def m_152; puts 1790; x = get_proc { puts 1791; p = get_proc{ puts 1792; break; puts 1793}; puts p.call; puts 1794}; puts x.call; puts 1795; end; +begin; puts 1796; m_152; puts 1797; rescue; puts 1798; puts $!.class; end +$g = 0; begin; puts 1799; x = get_proc { puts 1800; p = get_local_block; puts p.call; puts 1801}; puts x.call; puts 1802; rescue; puts 1803; puts $!.class; end +$g = 0; def m_153; puts 1804; x = get_proc { puts 1805; p = get_local_block; puts p.call; puts 1806}; puts x.call; puts 1807; end; +begin; puts 1808; m_153; puts 1809; rescue; puts 1810; puts $!.class; end +$g = 0; begin; puts 1811; x = get_proc { puts 1812; p = get_local_lambda; puts p.call; puts 1813}; puts x.call; puts 1814; rescue; puts 1815; puts $!.class; end +$g = 0; def m_154; puts 1816; x = get_proc { puts 1817; p = get_local_lambda; puts p.call; puts 1818}; puts x.call; puts 1819; end; +begin; puts 1820; m_154; puts 1821; rescue; puts 1822; puts $!.class; end +$g = 0; begin; puts 1823; x = get_proc { puts 1824; p = get_local_proc; puts p.call; puts 1825}; puts x.call; puts 1826; rescue; puts 1827; puts $!.class; end +$g = 0; def m_155; puts 1828; x = get_proc { puts 1829; p = get_local_proc; puts p.call; puts 1830}; puts x.call; puts 1831; end; +begin; puts 1832; m_155; puts 1833; rescue; puts 1834; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1835; for i in [1, 2]; puts 1836; p = lambda{ puts 1837; break; puts 1838}; puts p.call; puts 1839; end; puts 1840; rescue; puts 1841; puts $!.class; end +$g = 0; def m_156; puts 1842; for i in [1, 2]; puts 1843; p = lambda{ puts 1844; break; puts 1845}; puts p.call; puts 1846; end; puts 1847; end; +begin; puts 1848; m_156; puts 1849; rescue; puts 1850; puts $!.class; end +$g = 0; begin; puts 1851; for i in [1, 2]; puts 1852; p = Proc.new{ puts 1853; break; puts 1854}; puts p.call; puts 1855; end; puts 1856; rescue; puts 1857; puts $!.class; end +$g = 0; def m_157; puts 1858; for i in [1, 2]; puts 1859; p = Proc.new{ puts 1860; break; puts 1861}; puts p.call; puts 1862; end; puts 1863; end; +begin; puts 1864; m_157; puts 1865; rescue; puts 1866; puts $!.class; end +$g = 0; begin; puts 1867; for i in [1, 2]; puts 1868; p = get_block{ puts 1869; break; puts 1870}; puts p.call; puts 1871; end; puts 1872; rescue; puts 1873; puts $!.class; end +$g = 0; def m_158; puts 1874; for i in [1, 2]; puts 1875; p = get_block{ puts 1876; break; puts 1877}; puts p.call; puts 1878; end; puts 1879; end; +begin; puts 1880; m_158; puts 1881; rescue; puts 1882; puts $!.class; end +$g = 0; begin; puts 1883; for i in [1, 2]; puts 1884; p = get_lambda{ puts 1885; break; puts 1886}; puts p.call; puts 1887; end; puts 1888; rescue; puts 1889; puts $!.class; end +$g = 0; def m_159; puts 1890; for i in [1, 2]; puts 1891; p = get_lambda{ puts 1892; break; puts 1893}; puts p.call; puts 1894; end; puts 1895; end; +begin; puts 1896; m_159; puts 1897; rescue; puts 1898; puts $!.class; end +$g = 0; begin; puts 1899; for i in [1, 2]; puts 1900; p = get_proc{ puts 1901; break; puts 1902}; puts p.call; puts 1903; end; puts 1904; rescue; puts 1905; puts $!.class; end +$g = 0; def m_160; puts 1906; for i in [1, 2]; puts 1907; p = get_proc{ puts 1908; break; puts 1909}; puts p.call; puts 1910; end; puts 1911; end; +begin; puts 1912; m_160; puts 1913; rescue; puts 1914; puts $!.class; end +$g = 0; begin; puts 1915; for i in [1, 2]; puts 1916; p = get_local_block; puts p.call; puts 1917; end; puts 1918; rescue; puts 1919; puts $!.class; end +$g = 0; def m_161; puts 1920; for i in [1, 2]; puts 1921; p = get_local_block; puts p.call; puts 1922; end; puts 1923; end; +begin; puts 1924; m_161; puts 1925; rescue; puts 1926; puts $!.class; end +$g = 0; begin; puts 1927; for i in [1, 2]; puts 1928; p = get_local_lambda; puts p.call; puts 1929; end; puts 1930; rescue; puts 1931; puts $!.class; end +$g = 0; def m_162; puts 1932; for i in [1, 2]; puts 1933; p = get_local_lambda; puts p.call; puts 1934; end; puts 1935; end; +begin; puts 1936; m_162; puts 1937; rescue; puts 1938; puts $!.class; end +$g = 0; begin; puts 1939; for i in [1, 2]; puts 1940; p = get_local_proc; puts p.call; puts 1941; end; puts 1942; rescue; puts 1943; puts $!.class; end +$g = 0; def m_163; puts 1944; for i in [1, 2]; puts 1945; p = get_local_proc; puts p.call; puts 1946; end; puts 1947; end; +begin; puts 1948; m_163; puts 1949; rescue; puts 1950; puts $!.class; end +end +test diff --git a/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_next.rb b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_next.rb new file mode 100644 index 0000000000..b9255cf558 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_next.rb @@ -0,0 +1,595 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +# helper +def myeval(line); puts 1; eval(line); puts 2; end +def call1(x); puts 3; call2(x); puts 4; end +def call2(x); puts 5; call3(x); puts 6; end +def call3(x); puts 7; puts x.call; puts 8; end + +# producer + +def get_block(&p); p; end +def get_lambda(&p); lambda(&p); end +def get_proc(&p); Proc.new(&p); end + +def get_local_block; get_block { puts 9; next; puts 10 }; end +def get_local_lambda; lambda { puts 11; next; puts 12 }; end +def get_local_proc; Proc.new { puts 13; next; puts 14 }; end + +# consumer + +# taking arguments +def iterator_via_yield; puts 15; x = yield; puts x; puts 16; end +def iterator_via_call(&p); puts 17; puts(p.call); puts 18; end + +def method_call_iterator_via_yield(&p); puts 19; iterator_via_yield(&p); puts 20; end +def method_call_iterator_via_call(&p); puts 21; iterator_via_call(&p); puts 22; end + +def method_use_lambda_and_yield; puts 23; x = lambda { puts 24; yield; puts 25 }; puts x.call; puts 26; end +def method_use_proc_and_yield; puts 27; x = Proc.new { puts 28; yield; puts 29 }; puts x.call; puts 30; end +def method_use_lambda_and_call(&p); puts 31; x = lambda { puts 32; p.call; puts 33 }; puts x.call; puts 34; end +def method_use_proc_and_call(&p); puts 35; x = Proc.new { puts 36; p.call; puts 37 }; puts x.call; puts 38; end + +def method_use_lambda_and_yield_2; puts 39; x = lambda { puts 40; yield; puts 41 }; call1(x); puts 42; end + +def method_yield_in_loop; puts 43; for i in [1, 2]; puts 44; yield; puts 45; end; puts 46; end +def method_call_in_loop(&p); puts 47; for i in [3, 4]; puts 48; p.call; puts 49; end; puts 50; end + +# created in-place +def test +$g = 0; begin; puts 51; iterator_via_yield { puts 52; next; puts 53}; puts 54; rescue; puts 55; puts $!.class; end +$g = 0; def m_1; puts 56; $g = 0; begin; puts 57; iterator_via_yield { puts 58; next; puts 59}; puts 60; rescue; puts 61; puts $!.class; end; puts 62; end; m_1 +$g = 0; begin; puts 63; iterator_via_call { puts 64; next; puts 65}; puts 66; rescue; puts 67; puts $!.class; end +$g = 0; def m_2; puts 68; $g = 0; begin; puts 69; iterator_via_call { puts 70; next; puts 71}; puts 72; rescue; puts 73; puts $!.class; end; puts 74; end; m_2 +$g = 0; begin; puts 75; method_call_iterator_via_yield { puts 76; next; puts 77}; puts 78; rescue; puts 79; puts $!.class; end +$g = 0; def m_3; puts 80; $g = 0; begin; puts 81; method_call_iterator_via_yield { puts 82; next; puts 83}; puts 84; rescue; puts 85; puts $!.class; end; puts 86; end; m_3 +$g = 0; begin; puts 87; method_call_iterator_via_call { puts 88; next; puts 89}; puts 90; rescue; puts 91; puts $!.class; end +$g = 0; def m_4; puts 92; $g = 0; begin; puts 93; method_call_iterator_via_call { puts 94; next; puts 95}; puts 96; rescue; puts 97; puts $!.class; end; puts 98; end; m_4 +$g = 0; begin; puts 99; method_use_lambda_and_yield { puts 100; next; puts 101}; puts 102; rescue; puts 103; puts $!.class; end +$g = 0; def m_5; puts 104; $g = 0; begin; puts 105; method_use_lambda_and_yield { puts 106; next; puts 107}; puts 108; rescue; puts 109; puts $!.class; end; puts 110; end; m_5 +$g = 0; begin; puts 111; method_use_proc_and_yield { puts 112; next; puts 113}; puts 114; rescue; puts 115; puts $!.class; end +$g = 0; def m_6; puts 116; $g = 0; begin; puts 117; method_use_proc_and_yield { puts 118; next; puts 119}; puts 120; rescue; puts 121; puts $!.class; end; puts 122; end; m_6 +$g = 0; begin; puts 123; method_use_lambda_and_call { puts 124; next; puts 125}; puts 126; rescue; puts 127; puts $!.class; end +$g = 0; def m_7; puts 128; $g = 0; begin; puts 129; method_use_lambda_and_call { puts 130; next; puts 131}; puts 132; rescue; puts 133; puts $!.class; end; puts 134; end; m_7 +$g = 0; begin; puts 135; method_use_proc_and_call { puts 136; next; puts 137}; puts 138; rescue; puts 139; puts $!.class; end +$g = 0; def m_8; puts 140; $g = 0; begin; puts 141; method_use_proc_and_call { puts 142; next; puts 143}; puts 144; rescue; puts 145; puts $!.class; end; puts 146; end; m_8 +$g = 0; begin; puts 147; method_use_lambda_and_yield_2 { puts 148; next; puts 149}; puts 150; rescue; puts 151; puts $!.class; end +$g = 0; def m_9; puts 152; $g = 0; begin; puts 153; method_use_lambda_and_yield_2 { puts 154; next; puts 155}; puts 156; rescue; puts 157; puts $!.class; end; puts 158; end; m_9 +$g = 0; begin; puts 159; method_yield_in_loop { puts 160; next; puts 161}; puts 162; rescue; puts 163; puts $!.class; end +$g = 0; def m_10; puts 164; $g = 0; begin; puts 165; method_yield_in_loop { puts 166; next; puts 167}; puts 168; rescue; puts 169; puts $!.class; end; puts 170; end; m_10 +$g = 0; begin; puts 171; method_call_in_loop { puts 172; next; puts 173}; puts 174; rescue; puts 175; puts $!.class; end +$g = 0; def m_11; puts 176; $g = 0; begin; puts 177; method_call_in_loop { puts 178; next; puts 179}; puts 180; rescue; puts 181; puts $!.class; end; puts 182; end; m_11 +end +test + + +# created locally or from method +def test +$g = 0; begin; p = lambda{ puts 183; next; puts 184}; puts 185; iterator_via_yield(&p); puts 186; rescue; puts 187; puts $!.class; end +$g = 0; def m_12; p = lambda{ puts 188; next; puts 189}; puts 190; iterator_via_yield(&p); puts 191; end; +begin; puts 192; m_12; puts 193; rescue; puts 194; puts $!.class; end +$g = 0; begin; p = lambda{ puts 195; next; puts 196}; puts 197; iterator_via_call(&p); puts 198; rescue; puts 199; puts $!.class; end +$g = 0; def m_13; p = lambda{ puts 200; next; puts 201}; puts 202; iterator_via_call(&p); puts 203; end; +begin; puts 204; m_13; puts 205; rescue; puts 206; puts $!.class; end +$g = 0; begin; p = lambda{ puts 207; next; puts 208}; puts 209; method_call_iterator_via_yield(&p); puts 210; rescue; puts 211; puts $!.class; end +$g = 0; def m_14; p = lambda{ puts 212; next; puts 213}; puts 214; method_call_iterator_via_yield(&p); puts 215; end; +begin; puts 216; m_14; puts 217; rescue; puts 218; puts $!.class; end +$g = 0; begin; p = lambda{ puts 219; next; puts 220}; puts 221; method_call_iterator_via_call(&p); puts 222; rescue; puts 223; puts $!.class; end +$g = 0; def m_15; p = lambda{ puts 224; next; puts 225}; puts 226; method_call_iterator_via_call(&p); puts 227; end; +begin; puts 228; m_15; puts 229; rescue; puts 230; puts $!.class; end +$g = 0; begin; p = lambda{ puts 231; next; puts 232}; puts 233; method_use_lambda_and_yield(&p); puts 234; rescue; puts 235; puts $!.class; end +$g = 0; def m_16; p = lambda{ puts 236; next; puts 237}; puts 238; method_use_lambda_and_yield(&p); puts 239; end; +begin; puts 240; m_16; puts 241; rescue; puts 242; puts $!.class; end +$g = 0; begin; p = lambda{ puts 243; next; puts 244}; puts 245; method_use_proc_and_yield(&p); puts 246; rescue; puts 247; puts $!.class; end +$g = 0; def m_17; p = lambda{ puts 248; next; puts 249}; puts 250; method_use_proc_and_yield(&p); puts 251; end; +begin; puts 252; m_17; puts 253; rescue; puts 254; puts $!.class; end +$g = 0; begin; p = lambda{ puts 255; next; puts 256}; puts 257; method_use_lambda_and_call(&p); puts 258; rescue; puts 259; puts $!.class; end +$g = 0; def m_18; p = lambda{ puts 260; next; puts 261}; puts 262; method_use_lambda_and_call(&p); puts 263; end; +begin; puts 264; m_18; puts 265; rescue; puts 266; puts $!.class; end +$g = 0; begin; p = lambda{ puts 267; next; puts 268}; puts 269; method_use_proc_and_call(&p); puts 270; rescue; puts 271; puts $!.class; end +$g = 0; def m_19; p = lambda{ puts 272; next; puts 273}; puts 274; method_use_proc_and_call(&p); puts 275; end; +begin; puts 276; m_19; puts 277; rescue; puts 278; puts $!.class; end +$g = 0; begin; p = lambda{ puts 279; next; puts 280}; puts 281; method_use_lambda_and_yield_2(&p); puts 282; rescue; puts 283; puts $!.class; end +$g = 0; def m_20; p = lambda{ puts 284; next; puts 285}; puts 286; method_use_lambda_and_yield_2(&p); puts 287; end; +begin; puts 288; m_20; puts 289; rescue; puts 290; puts $!.class; end +$g = 0; begin; p = lambda{ puts 291; next; puts 292}; puts 293; method_yield_in_loop(&p); puts 294; rescue; puts 295; puts $!.class; end +$g = 0; def m_21; p = lambda{ puts 296; next; puts 297}; puts 298; method_yield_in_loop(&p); puts 299; end; +begin; puts 300; m_21; puts 301; rescue; puts 302; puts $!.class; end +$g = 0; begin; p = lambda{ puts 303; next; puts 304}; puts 305; method_call_in_loop(&p); puts 306; rescue; puts 307; puts $!.class; end +$g = 0; def m_22; p = lambda{ puts 308; next; puts 309}; puts 310; method_call_in_loop(&p); puts 311; end; +begin; puts 312; m_22; puts 313; rescue; puts 314; puts $!.class; end +end +test + +def test +$g = 0; begin; p = Proc.new{ puts 315; next; puts 316}; puts 317; iterator_via_yield(&p); puts 318; rescue; puts 319; puts $!.class; end +$g = 0; def m_23; p = Proc.new{ puts 320; next; puts 321}; puts 322; iterator_via_yield(&p); puts 323; end; +begin; puts 324; m_23; puts 325; rescue; puts 326; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 327; next; puts 328}; puts 329; iterator_via_call(&p); puts 330; rescue; puts 331; puts $!.class; end +$g = 0; def m_24; p = Proc.new{ puts 332; next; puts 333}; puts 334; iterator_via_call(&p); puts 335; end; +begin; puts 336; m_24; puts 337; rescue; puts 338; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 339; next; puts 340}; puts 341; method_call_iterator_via_yield(&p); puts 342; rescue; puts 343; puts $!.class; end +$g = 0; def m_25; p = Proc.new{ puts 344; next; puts 345}; puts 346; method_call_iterator_via_yield(&p); puts 347; end; +begin; puts 348; m_25; puts 349; rescue; puts 350; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 351; next; puts 352}; puts 353; method_call_iterator_via_call(&p); puts 354; rescue; puts 355; puts $!.class; end +$g = 0; def m_26; p = Proc.new{ puts 356; next; puts 357}; puts 358; method_call_iterator_via_call(&p); puts 359; end; +begin; puts 360; m_26; puts 361; rescue; puts 362; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 363; next; puts 364}; puts 365; method_use_lambda_and_yield(&p); puts 366; rescue; puts 367; puts $!.class; end +$g = 0; def m_27; p = Proc.new{ puts 368; next; puts 369}; puts 370; method_use_lambda_and_yield(&p); puts 371; end; +begin; puts 372; m_27; puts 373; rescue; puts 374; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 375; next; puts 376}; puts 377; method_use_proc_and_yield(&p); puts 378; rescue; puts 379; puts $!.class; end +$g = 0; def m_28; p = Proc.new{ puts 380; next; puts 381}; puts 382; method_use_proc_and_yield(&p); puts 383; end; +begin; puts 384; m_28; puts 385; rescue; puts 386; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 387; next; puts 388}; puts 389; method_use_lambda_and_call(&p); puts 390; rescue; puts 391; puts $!.class; end +$g = 0; def m_29; p = Proc.new{ puts 392; next; puts 393}; puts 394; method_use_lambda_and_call(&p); puts 395; end; +begin; puts 396; m_29; puts 397; rescue; puts 398; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 399; next; puts 400}; puts 401; method_use_proc_and_call(&p); puts 402; rescue; puts 403; puts $!.class; end +$g = 0; def m_30; p = Proc.new{ puts 404; next; puts 405}; puts 406; method_use_proc_and_call(&p); puts 407; end; +begin; puts 408; m_30; puts 409; rescue; puts 410; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 411; next; puts 412}; puts 413; method_use_lambda_and_yield_2(&p); puts 414; rescue; puts 415; puts $!.class; end +$g = 0; def m_31; p = Proc.new{ puts 416; next; puts 417}; puts 418; method_use_lambda_and_yield_2(&p); puts 419; end; +begin; puts 420; m_31; puts 421; rescue; puts 422; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 423; next; puts 424}; puts 425; method_yield_in_loop(&p); puts 426; rescue; puts 427; puts $!.class; end +$g = 0; def m_32; p = Proc.new{ puts 428; next; puts 429}; puts 430; method_yield_in_loop(&p); puts 431; end; +begin; puts 432; m_32; puts 433; rescue; puts 434; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 435; next; puts 436}; puts 437; method_call_in_loop(&p); puts 438; rescue; puts 439; puts $!.class; end +$g = 0; def m_33; p = Proc.new{ puts 440; next; puts 441}; puts 442; method_call_in_loop(&p); puts 443; end; +begin; puts 444; m_33; puts 445; rescue; puts 446; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_block{ puts 447; next; puts 448}; puts 449; iterator_via_yield(&p); puts 450; rescue; puts 451; puts $!.class; end +$g = 0; def m_34; p = get_block{ puts 452; next; puts 453}; puts 454; iterator_via_yield(&p); puts 455; end; +begin; puts 456; m_34; puts 457; rescue; puts 458; puts $!.class; end +$g = 0; begin; p = get_block{ puts 459; next; puts 460}; puts 461; iterator_via_call(&p); puts 462; rescue; puts 463; puts $!.class; end +$g = 0; def m_35; p = get_block{ puts 464; next; puts 465}; puts 466; iterator_via_call(&p); puts 467; end; +begin; puts 468; m_35; puts 469; rescue; puts 470; puts $!.class; end +$g = 0; begin; p = get_block{ puts 471; next; puts 472}; puts 473; method_call_iterator_via_yield(&p); puts 474; rescue; puts 475; puts $!.class; end +$g = 0; def m_36; p = get_block{ puts 476; next; puts 477}; puts 478; method_call_iterator_via_yield(&p); puts 479; end; +begin; puts 480; m_36; puts 481; rescue; puts 482; puts $!.class; end +$g = 0; begin; p = get_block{ puts 483; next; puts 484}; puts 485; method_call_iterator_via_call(&p); puts 486; rescue; puts 487; puts $!.class; end +$g = 0; def m_37; p = get_block{ puts 488; next; puts 489}; puts 490; method_call_iterator_via_call(&p); puts 491; end; +begin; puts 492; m_37; puts 493; rescue; puts 494; puts $!.class; end +$g = 0; begin; p = get_block{ puts 495; next; puts 496}; puts 497; method_use_lambda_and_yield(&p); puts 498; rescue; puts 499; puts $!.class; end +$g = 0; def m_38; p = get_block{ puts 500; next; puts 501}; puts 502; method_use_lambda_and_yield(&p); puts 503; end; +begin; puts 504; m_38; puts 505; rescue; puts 506; puts $!.class; end +$g = 0; begin; p = get_block{ puts 507; next; puts 508}; puts 509; method_use_proc_and_yield(&p); puts 510; rescue; puts 511; puts $!.class; end +$g = 0; def m_39; p = get_block{ puts 512; next; puts 513}; puts 514; method_use_proc_and_yield(&p); puts 515; end; +begin; puts 516; m_39; puts 517; rescue; puts 518; puts $!.class; end +$g = 0; begin; p = get_block{ puts 519; next; puts 520}; puts 521; method_use_lambda_and_call(&p); puts 522; rescue; puts 523; puts $!.class; end +$g = 0; def m_40; p = get_block{ puts 524; next; puts 525}; puts 526; method_use_lambda_and_call(&p); puts 527; end; +begin; puts 528; m_40; puts 529; rescue; puts 530; puts $!.class; end +$g = 0; begin; p = get_block{ puts 531; next; puts 532}; puts 533; method_use_proc_and_call(&p); puts 534; rescue; puts 535; puts $!.class; end +$g = 0; def m_41; p = get_block{ puts 536; next; puts 537}; puts 538; method_use_proc_and_call(&p); puts 539; end; +begin; puts 540; m_41; puts 541; rescue; puts 542; puts $!.class; end +$g = 0; begin; p = get_block{ puts 543; next; puts 544}; puts 545; method_use_lambda_and_yield_2(&p); puts 546; rescue; puts 547; puts $!.class; end +$g = 0; def m_42; p = get_block{ puts 548; next; puts 549}; puts 550; method_use_lambda_and_yield_2(&p); puts 551; end; +begin; puts 552; m_42; puts 553; rescue; puts 554; puts $!.class; end +$g = 0; begin; p = get_block{ puts 555; next; puts 556}; puts 557; method_yield_in_loop(&p); puts 558; rescue; puts 559; puts $!.class; end +$g = 0; def m_43; p = get_block{ puts 560; next; puts 561}; puts 562; method_yield_in_loop(&p); puts 563; end; +begin; puts 564; m_43; puts 565; rescue; puts 566; puts $!.class; end +$g = 0; begin; p = get_block{ puts 567; next; puts 568}; puts 569; method_call_in_loop(&p); puts 570; rescue; puts 571; puts $!.class; end +$g = 0; def m_44; p = get_block{ puts 572; next; puts 573}; puts 574; method_call_in_loop(&p); puts 575; end; +begin; puts 576; m_44; puts 577; rescue; puts 578; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_lambda{ puts 579; next; puts 580}; puts 581; iterator_via_yield(&p); puts 582; rescue; puts 583; puts $!.class; end +$g = 0; def m_45; p = get_lambda{ puts 584; next; puts 585}; puts 586; iterator_via_yield(&p); puts 587; end; +begin; puts 588; m_45; puts 589; rescue; puts 590; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 591; next; puts 592}; puts 593; iterator_via_call(&p); puts 594; rescue; puts 595; puts $!.class; end +$g = 0; def m_46; p = get_lambda{ puts 596; next; puts 597}; puts 598; iterator_via_call(&p); puts 599; end; +begin; puts 600; m_46; puts 601; rescue; puts 602; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 603; next; puts 604}; puts 605; method_call_iterator_via_yield(&p); puts 606; rescue; puts 607; puts $!.class; end +$g = 0; def m_47; p = get_lambda{ puts 608; next; puts 609}; puts 610; method_call_iterator_via_yield(&p); puts 611; end; +begin; puts 612; m_47; puts 613; rescue; puts 614; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 615; next; puts 616}; puts 617; method_call_iterator_via_call(&p); puts 618; rescue; puts 619; puts $!.class; end +$g = 0; def m_48; p = get_lambda{ puts 620; next; puts 621}; puts 622; method_call_iterator_via_call(&p); puts 623; end; +begin; puts 624; m_48; puts 625; rescue; puts 626; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 627; next; puts 628}; puts 629; method_use_lambda_and_yield(&p); puts 630; rescue; puts 631; puts $!.class; end +$g = 0; def m_49; p = get_lambda{ puts 632; next; puts 633}; puts 634; method_use_lambda_and_yield(&p); puts 635; end; +begin; puts 636; m_49; puts 637; rescue; puts 638; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 639; next; puts 640}; puts 641; method_use_proc_and_yield(&p); puts 642; rescue; puts 643; puts $!.class; end +$g = 0; def m_50; p = get_lambda{ puts 644; next; puts 645}; puts 646; method_use_proc_and_yield(&p); puts 647; end; +begin; puts 648; m_50; puts 649; rescue; puts 650; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 651; next; puts 652}; puts 653; method_use_lambda_and_call(&p); puts 654; rescue; puts 655; puts $!.class; end +$g = 0; def m_51; p = get_lambda{ puts 656; next; puts 657}; puts 658; method_use_lambda_and_call(&p); puts 659; end; +begin; puts 660; m_51; puts 661; rescue; puts 662; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 663; next; puts 664}; puts 665; method_use_proc_and_call(&p); puts 666; rescue; puts 667; puts $!.class; end +$g = 0; def m_52; p = get_lambda{ puts 668; next; puts 669}; puts 670; method_use_proc_and_call(&p); puts 671; end; +begin; puts 672; m_52; puts 673; rescue; puts 674; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 675; next; puts 676}; puts 677; method_use_lambda_and_yield_2(&p); puts 678; rescue; puts 679; puts $!.class; end +$g = 0; def m_53; p = get_lambda{ puts 680; next; puts 681}; puts 682; method_use_lambda_and_yield_2(&p); puts 683; end; +begin; puts 684; m_53; puts 685; rescue; puts 686; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 687; next; puts 688}; puts 689; method_yield_in_loop(&p); puts 690; rescue; puts 691; puts $!.class; end +$g = 0; def m_54; p = get_lambda{ puts 692; next; puts 693}; puts 694; method_yield_in_loop(&p); puts 695; end; +begin; puts 696; m_54; puts 697; rescue; puts 698; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 699; next; puts 700}; puts 701; method_call_in_loop(&p); puts 702; rescue; puts 703; puts $!.class; end +$g = 0; def m_55; p = get_lambda{ puts 704; next; puts 705}; puts 706; method_call_in_loop(&p); puts 707; end; +begin; puts 708; m_55; puts 709; rescue; puts 710; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_proc{ puts 711; next; puts 712}; puts 713; iterator_via_yield(&p); puts 714; rescue; puts 715; puts $!.class; end +$g = 0; def m_56; p = get_proc{ puts 716; next; puts 717}; puts 718; iterator_via_yield(&p); puts 719; end; +begin; puts 720; m_56; puts 721; rescue; puts 722; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 723; next; puts 724}; puts 725; iterator_via_call(&p); puts 726; rescue; puts 727; puts $!.class; end +$g = 0; def m_57; p = get_proc{ puts 728; next; puts 729}; puts 730; iterator_via_call(&p); puts 731; end; +begin; puts 732; m_57; puts 733; rescue; puts 734; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 735; next; puts 736}; puts 737; method_call_iterator_via_yield(&p); puts 738; rescue; puts 739; puts $!.class; end +$g = 0; def m_58; p = get_proc{ puts 740; next; puts 741}; puts 742; method_call_iterator_via_yield(&p); puts 743; end; +begin; puts 744; m_58; puts 745; rescue; puts 746; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 747; next; puts 748}; puts 749; method_call_iterator_via_call(&p); puts 750; rescue; puts 751; puts $!.class; end +$g = 0; def m_59; p = get_proc{ puts 752; next; puts 753}; puts 754; method_call_iterator_via_call(&p); puts 755; end; +begin; puts 756; m_59; puts 757; rescue; puts 758; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 759; next; puts 760}; puts 761; method_use_lambda_and_yield(&p); puts 762; rescue; puts 763; puts $!.class; end +$g = 0; def m_60; p = get_proc{ puts 764; next; puts 765}; puts 766; method_use_lambda_and_yield(&p); puts 767; end; +begin; puts 768; m_60; puts 769; rescue; puts 770; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 771; next; puts 772}; puts 773; method_use_proc_and_yield(&p); puts 774; rescue; puts 775; puts $!.class; end +$g = 0; def m_61; p = get_proc{ puts 776; next; puts 777}; puts 778; method_use_proc_and_yield(&p); puts 779; end; +begin; puts 780; m_61; puts 781; rescue; puts 782; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 783; next; puts 784}; puts 785; method_use_lambda_and_call(&p); puts 786; rescue; puts 787; puts $!.class; end +$g = 0; def m_62; p = get_proc{ puts 788; next; puts 789}; puts 790; method_use_lambda_and_call(&p); puts 791; end; +begin; puts 792; m_62; puts 793; rescue; puts 794; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 795; next; puts 796}; puts 797; method_use_proc_and_call(&p); puts 798; rescue; puts 799; puts $!.class; end +$g = 0; def m_63; p = get_proc{ puts 800; next; puts 801}; puts 802; method_use_proc_and_call(&p); puts 803; end; +begin; puts 804; m_63; puts 805; rescue; puts 806; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 807; next; puts 808}; puts 809; method_use_lambda_and_yield_2(&p); puts 810; rescue; puts 811; puts $!.class; end +$g = 0; def m_64; p = get_proc{ puts 812; next; puts 813}; puts 814; method_use_lambda_and_yield_2(&p); puts 815; end; +begin; puts 816; m_64; puts 817; rescue; puts 818; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 819; next; puts 820}; puts 821; method_yield_in_loop(&p); puts 822; rescue; puts 823; puts $!.class; end +$g = 0; def m_65; p = get_proc{ puts 824; next; puts 825}; puts 826; method_yield_in_loop(&p); puts 827; end; +begin; puts 828; m_65; puts 829; rescue; puts 830; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 831; next; puts 832}; puts 833; method_call_in_loop(&p); puts 834; rescue; puts 835; puts $!.class; end +$g = 0; def m_66; p = get_proc{ puts 836; next; puts 837}; puts 838; method_call_in_loop(&p); puts 839; end; +begin; puts 840; m_66; puts 841; rescue; puts 842; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_block; puts 843; iterator_via_yield(&p); puts 844; rescue; puts 845; puts $!.class; end +$g = 0; def m_67; p = get_local_block; puts 846; iterator_via_yield(&p); puts 847; end; +begin; puts 848; m_67; puts 849; rescue; puts 850; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 851; iterator_via_call(&p); puts 852; rescue; puts 853; puts $!.class; end +$g = 0; def m_68; p = get_local_block; puts 854; iterator_via_call(&p); puts 855; end; +begin; puts 856; m_68; puts 857; rescue; puts 858; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 859; method_call_iterator_via_yield(&p); puts 860; rescue; puts 861; puts $!.class; end +$g = 0; def m_69; p = get_local_block; puts 862; method_call_iterator_via_yield(&p); puts 863; end; +begin; puts 864; m_69; puts 865; rescue; puts 866; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 867; method_call_iterator_via_call(&p); puts 868; rescue; puts 869; puts $!.class; end +$g = 0; def m_70; p = get_local_block; puts 870; method_call_iterator_via_call(&p); puts 871; end; +begin; puts 872; m_70; puts 873; rescue; puts 874; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 875; method_use_lambda_and_yield(&p); puts 876; rescue; puts 877; puts $!.class; end +$g = 0; def m_71; p = get_local_block; puts 878; method_use_lambda_and_yield(&p); puts 879; end; +begin; puts 880; m_71; puts 881; rescue; puts 882; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 883; method_use_proc_and_yield(&p); puts 884; rescue; puts 885; puts $!.class; end +$g = 0; def m_72; p = get_local_block; puts 886; method_use_proc_and_yield(&p); puts 887; end; +begin; puts 888; m_72; puts 889; rescue; puts 890; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 891; method_use_lambda_and_call(&p); puts 892; rescue; puts 893; puts $!.class; end +$g = 0; def m_73; p = get_local_block; puts 894; method_use_lambda_and_call(&p); puts 895; end; +begin; puts 896; m_73; puts 897; rescue; puts 898; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 899; method_use_proc_and_call(&p); puts 900; rescue; puts 901; puts $!.class; end +$g = 0; def m_74; p = get_local_block; puts 902; method_use_proc_and_call(&p); puts 903; end; +begin; puts 904; m_74; puts 905; rescue; puts 906; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 907; method_use_lambda_and_yield_2(&p); puts 908; rescue; puts 909; puts $!.class; end +$g = 0; def m_75; p = get_local_block; puts 910; method_use_lambda_and_yield_2(&p); puts 911; end; +begin; puts 912; m_75; puts 913; rescue; puts 914; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 915; method_yield_in_loop(&p); puts 916; rescue; puts 917; puts $!.class; end +$g = 0; def m_76; p = get_local_block; puts 918; method_yield_in_loop(&p); puts 919; end; +begin; puts 920; m_76; puts 921; rescue; puts 922; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 923; method_call_in_loop(&p); puts 924; rescue; puts 925; puts $!.class; end +$g = 0; def m_77; p = get_local_block; puts 926; method_call_in_loop(&p); puts 927; end; +begin; puts 928; m_77; puts 929; rescue; puts 930; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_lambda; puts 931; iterator_via_yield(&p); puts 932; rescue; puts 933; puts $!.class; end +$g = 0; def m_78; p = get_local_lambda; puts 934; iterator_via_yield(&p); puts 935; end; +begin; puts 936; m_78; puts 937; rescue; puts 938; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 939; iterator_via_call(&p); puts 940; rescue; puts 941; puts $!.class; end +$g = 0; def m_79; p = get_local_lambda; puts 942; iterator_via_call(&p); puts 943; end; +begin; puts 944; m_79; puts 945; rescue; puts 946; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 947; method_call_iterator_via_yield(&p); puts 948; rescue; puts 949; puts $!.class; end +$g = 0; def m_80; p = get_local_lambda; puts 950; method_call_iterator_via_yield(&p); puts 951; end; +begin; puts 952; m_80; puts 953; rescue; puts 954; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 955; method_call_iterator_via_call(&p); puts 956; rescue; puts 957; puts $!.class; end +$g = 0; def m_81; p = get_local_lambda; puts 958; method_call_iterator_via_call(&p); puts 959; end; +begin; puts 960; m_81; puts 961; rescue; puts 962; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 963; method_use_lambda_and_yield(&p); puts 964; rescue; puts 965; puts $!.class; end +$g = 0; def m_82; p = get_local_lambda; puts 966; method_use_lambda_and_yield(&p); puts 967; end; +begin; puts 968; m_82; puts 969; rescue; puts 970; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 971; method_use_proc_and_yield(&p); puts 972; rescue; puts 973; puts $!.class; end +$g = 0; def m_83; p = get_local_lambda; puts 974; method_use_proc_and_yield(&p); puts 975; end; +begin; puts 976; m_83; puts 977; rescue; puts 978; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 979; method_use_lambda_and_call(&p); puts 980; rescue; puts 981; puts $!.class; end +$g = 0; def m_84; p = get_local_lambda; puts 982; method_use_lambda_and_call(&p); puts 983; end; +begin; puts 984; m_84; puts 985; rescue; puts 986; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 987; method_use_proc_and_call(&p); puts 988; rescue; puts 989; puts $!.class; end +$g = 0; def m_85; p = get_local_lambda; puts 990; method_use_proc_and_call(&p); puts 991; end; +begin; puts 992; m_85; puts 993; rescue; puts 994; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 995; method_use_lambda_and_yield_2(&p); puts 996; rescue; puts 997; puts $!.class; end +$g = 0; def m_86; p = get_local_lambda; puts 998; method_use_lambda_and_yield_2(&p); puts 999; end; +begin; puts 1000; m_86; puts 1001; rescue; puts 1002; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1003; method_yield_in_loop(&p); puts 1004; rescue; puts 1005; puts $!.class; end +$g = 0; def m_87; p = get_local_lambda; puts 1006; method_yield_in_loop(&p); puts 1007; end; +begin; puts 1008; m_87; puts 1009; rescue; puts 1010; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1011; method_call_in_loop(&p); puts 1012; rescue; puts 1013; puts $!.class; end +$g = 0; def m_88; p = get_local_lambda; puts 1014; method_call_in_loop(&p); puts 1015; end; +begin; puts 1016; m_88; puts 1017; rescue; puts 1018; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_proc; puts 1019; iterator_via_yield(&p); puts 1020; rescue; puts 1021; puts $!.class; end +$g = 0; def m_89; p = get_local_proc; puts 1022; iterator_via_yield(&p); puts 1023; end; +begin; puts 1024; m_89; puts 1025; rescue; puts 1026; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1027; iterator_via_call(&p); puts 1028; rescue; puts 1029; puts $!.class; end +$g = 0; def m_90; p = get_local_proc; puts 1030; iterator_via_call(&p); puts 1031; end; +begin; puts 1032; m_90; puts 1033; rescue; puts 1034; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1035; method_call_iterator_via_yield(&p); puts 1036; rescue; puts 1037; puts $!.class; end +$g = 0; def m_91; p = get_local_proc; puts 1038; method_call_iterator_via_yield(&p); puts 1039; end; +begin; puts 1040; m_91; puts 1041; rescue; puts 1042; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1043; method_call_iterator_via_call(&p); puts 1044; rescue; puts 1045; puts $!.class; end +$g = 0; def m_92; p = get_local_proc; puts 1046; method_call_iterator_via_call(&p); puts 1047; end; +begin; puts 1048; m_92; puts 1049; rescue; puts 1050; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1051; method_use_lambda_and_yield(&p); puts 1052; rescue; puts 1053; puts $!.class; end +$g = 0; def m_93; p = get_local_proc; puts 1054; method_use_lambda_and_yield(&p); puts 1055; end; +begin; puts 1056; m_93; puts 1057; rescue; puts 1058; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1059; method_use_proc_and_yield(&p); puts 1060; rescue; puts 1061; puts $!.class; end +$g = 0; def m_94; p = get_local_proc; puts 1062; method_use_proc_and_yield(&p); puts 1063; end; +begin; puts 1064; m_94; puts 1065; rescue; puts 1066; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1067; method_use_lambda_and_call(&p); puts 1068; rescue; puts 1069; puts $!.class; end +$g = 0; def m_95; p = get_local_proc; puts 1070; method_use_lambda_and_call(&p); puts 1071; end; +begin; puts 1072; m_95; puts 1073; rescue; puts 1074; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1075; method_use_proc_and_call(&p); puts 1076; rescue; puts 1077; puts $!.class; end +$g = 0; def m_96; p = get_local_proc; puts 1078; method_use_proc_and_call(&p); puts 1079; end; +begin; puts 1080; m_96; puts 1081; rescue; puts 1082; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1083; method_use_lambda_and_yield_2(&p); puts 1084; rescue; puts 1085; puts $!.class; end +$g = 0; def m_97; p = get_local_proc; puts 1086; method_use_lambda_and_yield_2(&p); puts 1087; end; +begin; puts 1088; m_97; puts 1089; rescue; puts 1090; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1091; method_yield_in_loop(&p); puts 1092; rescue; puts 1093; puts $!.class; end +$g = 0; def m_98; p = get_local_proc; puts 1094; method_yield_in_loop(&p); puts 1095; end; +begin; puts 1096; m_98; puts 1097; rescue; puts 1098; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1099; method_call_in_loop(&p); puts 1100; rescue; puts 1101; puts $!.class; end +$g = 0; def m_99; p = get_local_proc; puts 1102; method_call_in_loop(&p); puts 1103; end; +begin; puts 1104; m_99; puts 1105; rescue; puts 1106; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1107; p = lambda{ puts 1108; next; puts 1109}; puts(p.call); puts 1110; rescue; puts 1111; puts $!.class; end +$g = 0; def m_100; puts 1112; p = lambda{ puts 1113; next; puts 1114}; puts(p.call); puts 1115; end; +begin; puts 1116; m_100; puts 1117; rescue; puts 1118; puts $!.class; end +$g = 0; begin; puts 1119; puts m_100; puts 1120; rescue; puts 1121; puts $!.class; end +$g = 0; def m_101; puts 1122; puts m_100; puts 1123; end; +begin; puts 1124; m_101; puts 1125; rescue; puts 1126; puts $!.class; end +$g = 0; begin; puts 1127; p = Proc.new{ puts 1128; next; puts 1129}; puts(p.call); puts 1130; rescue; puts 1131; puts $!.class; end +$g = 0; def m_102; puts 1132; p = Proc.new{ puts 1133; next; puts 1134}; puts(p.call); puts 1135; end; +begin; puts 1136; m_102; puts 1137; rescue; puts 1138; puts $!.class; end +$g = 0; begin; puts 1139; puts m_102; puts 1140; rescue; puts 1141; puts $!.class; end +$g = 0; def m_103; puts 1142; puts m_102; puts 1143; end; +begin; puts 1144; m_103; puts 1145; rescue; puts 1146; puts $!.class; end +$g = 0; begin; puts 1147; p = get_block{ puts 1148; next; puts 1149}; puts(p.call); puts 1150; rescue; puts 1151; puts $!.class; end +$g = 0; def m_104; puts 1152; p = get_block{ puts 1153; next; puts 1154}; puts(p.call); puts 1155; end; +begin; puts 1156; m_104; puts 1157; rescue; puts 1158; puts $!.class; end +$g = 0; begin; puts 1159; puts m_104; puts 1160; rescue; puts 1161; puts $!.class; end +$g = 0; def m_105; puts 1162; puts m_104; puts 1163; end; +begin; puts 1164; m_105; puts 1165; rescue; puts 1166; puts $!.class; end +$g = 0; begin; puts 1167; p = get_lambda{ puts 1168; next; puts 1169}; puts(p.call); puts 1170; rescue; puts 1171; puts $!.class; end +$g = 0; def m_106; puts 1172; p = get_lambda{ puts 1173; next; puts 1174}; puts(p.call); puts 1175; end; +begin; puts 1176; m_106; puts 1177; rescue; puts 1178; puts $!.class; end +$g = 0; begin; puts 1179; puts m_106; puts 1180; rescue; puts 1181; puts $!.class; end +$g = 0; def m_107; puts 1182; puts m_106; puts 1183; end; +begin; puts 1184; m_107; puts 1185; rescue; puts 1186; puts $!.class; end +$g = 0; begin; puts 1187; p = get_proc{ puts 1188; next; puts 1189}; puts(p.call); puts 1190; rescue; puts 1191; puts $!.class; end +$g = 0; def m_108; puts 1192; p = get_proc{ puts 1193; next; puts 1194}; puts(p.call); puts 1195; end; +begin; puts 1196; m_108; puts 1197; rescue; puts 1198; puts $!.class; end +$g = 0; begin; puts 1199; puts m_108; puts 1200; rescue; puts 1201; puts $!.class; end +$g = 0; def m_109; puts 1202; puts m_108; puts 1203; end; +begin; puts 1204; m_109; puts 1205; rescue; puts 1206; puts $!.class; end +$g = 0; begin; puts 1207; p = get_local_block; puts(p.call); puts 1208; rescue; puts 1209; puts $!.class; end +$g = 0; def m_110; puts 1210; p = get_local_block; puts(p.call); puts 1211; end; +begin; puts 1212; m_110; puts 1213; rescue; puts 1214; puts $!.class; end +$g = 0; begin; puts 1215; puts m_110; puts 1216; rescue; puts 1217; puts $!.class; end +$g = 0; def m_111; puts 1218; puts m_110; puts 1219; end; +begin; puts 1220; m_111; puts 1221; rescue; puts 1222; puts $!.class; end +$g = 0; begin; puts 1223; p = get_local_lambda; puts(p.call); puts 1224; rescue; puts 1225; puts $!.class; end +$g = 0; def m_112; puts 1226; p = get_local_lambda; puts(p.call); puts 1227; end; +begin; puts 1228; m_112; puts 1229; rescue; puts 1230; puts $!.class; end +$g = 0; begin; puts 1231; puts m_112; puts 1232; rescue; puts 1233; puts $!.class; end +$g = 0; def m_113; puts 1234; puts m_112; puts 1235; end; +begin; puts 1236; m_113; puts 1237; rescue; puts 1238; puts $!.class; end +$g = 0; begin; puts 1239; p = get_local_proc; puts(p.call); puts 1240; rescue; puts 1241; puts $!.class; end +$g = 0; def m_114; puts 1242; p = get_local_proc; puts(p.call); puts 1243; end; +begin; puts 1244; m_114; puts 1245; rescue; puts 1246; puts $!.class; end +$g = 0; begin; puts 1247; puts m_114; puts 1248; rescue; puts 1249; puts $!.class; end +$g = 0; def m_115; puts 1250; puts m_114; puts 1251; end; +begin; puts 1252; m_115; puts 1253; rescue; puts 1254; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1255; x = lambda { puts 1256; p = lambda{ puts 1257; next; puts 1258}; puts p.call; puts 1259}; puts x.call; puts 1260; rescue; puts 1261; puts $!.class; end +$g = 0; def m_116; puts 1262; x = lambda { puts 1263; p = lambda{ puts 1264; next; puts 1265}; puts p.call; puts 1266}; puts x.call; puts 1267; end; +begin; puts 1268; m_116; puts 1269; rescue; puts 1270; puts $!.class; end +$g = 0; begin; puts 1271; x = lambda { puts 1272; p = Proc.new{ puts 1273; next; puts 1274}; puts p.call; puts 1275}; puts x.call; puts 1276; rescue; puts 1277; puts $!.class; end +$g = 0; def m_117; puts 1278; x = lambda { puts 1279; p = Proc.new{ puts 1280; next; puts 1281}; puts p.call; puts 1282}; puts x.call; puts 1283; end; +begin; puts 1284; m_117; puts 1285; rescue; puts 1286; puts $!.class; end +$g = 0; begin; puts 1287; x = lambda { puts 1288; p = get_block{ puts 1289; next; puts 1290}; puts p.call; puts 1291}; puts x.call; puts 1292; rescue; puts 1293; puts $!.class; end +$g = 0; def m_118; puts 1294; x = lambda { puts 1295; p = get_block{ puts 1296; next; puts 1297}; puts p.call; puts 1298}; puts x.call; puts 1299; end; +begin; puts 1300; m_118; puts 1301; rescue; puts 1302; puts $!.class; end +$g = 0; begin; puts 1303; x = lambda { puts 1304; p = get_lambda{ puts 1305; next; puts 1306}; puts p.call; puts 1307}; puts x.call; puts 1308; rescue; puts 1309; puts $!.class; end +$g = 0; def m_119; puts 1310; x = lambda { puts 1311; p = get_lambda{ puts 1312; next; puts 1313}; puts p.call; puts 1314}; puts x.call; puts 1315; end; +begin; puts 1316; m_119; puts 1317; rescue; puts 1318; puts $!.class; end +$g = 0; begin; puts 1319; x = lambda { puts 1320; p = get_proc{ puts 1321; next; puts 1322}; puts p.call; puts 1323}; puts x.call; puts 1324; rescue; puts 1325; puts $!.class; end +$g = 0; def m_120; puts 1326; x = lambda { puts 1327; p = get_proc{ puts 1328; next; puts 1329}; puts p.call; puts 1330}; puts x.call; puts 1331; end; +begin; puts 1332; m_120; puts 1333; rescue; puts 1334; puts $!.class; end +$g = 0; begin; puts 1335; x = lambda { puts 1336; p = get_local_block; puts p.call; puts 1337}; puts x.call; puts 1338; rescue; puts 1339; puts $!.class; end +$g = 0; def m_121; puts 1340; x = lambda { puts 1341; p = get_local_block; puts p.call; puts 1342}; puts x.call; puts 1343; end; +begin; puts 1344; m_121; puts 1345; rescue; puts 1346; puts $!.class; end +$g = 0; begin; puts 1347; x = lambda { puts 1348; p = get_local_lambda; puts p.call; puts 1349}; puts x.call; puts 1350; rescue; puts 1351; puts $!.class; end +$g = 0; def m_122; puts 1352; x = lambda { puts 1353; p = get_local_lambda; puts p.call; puts 1354}; puts x.call; puts 1355; end; +begin; puts 1356; m_122; puts 1357; rescue; puts 1358; puts $!.class; end +$g = 0; begin; puts 1359; x = lambda { puts 1360; p = get_local_proc; puts p.call; puts 1361}; puts x.call; puts 1362; rescue; puts 1363; puts $!.class; end +$g = 0; def m_123; puts 1364; x = lambda { puts 1365; p = get_local_proc; puts p.call; puts 1366}; puts x.call; puts 1367; end; +begin; puts 1368; m_123; puts 1369; rescue; puts 1370; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1371; x = Proc.new { puts 1372; p = lambda{ puts 1373; next; puts 1374}; puts p.call; puts 1375}; puts x.call; puts 1376; rescue; puts 1377; puts $!.class; end +$g = 0; def m_124; puts 1378; x = Proc.new { puts 1379; p = lambda{ puts 1380; next; puts 1381}; puts p.call; puts 1382}; puts x.call; puts 1383; end; +begin; puts 1384; m_124; puts 1385; rescue; puts 1386; puts $!.class; end +$g = 0; begin; puts 1387; x = Proc.new { puts 1388; p = Proc.new{ puts 1389; next; puts 1390}; puts p.call; puts 1391}; puts x.call; puts 1392; rescue; puts 1393; puts $!.class; end +$g = 0; def m_125; puts 1394; x = Proc.new { puts 1395; p = Proc.new{ puts 1396; next; puts 1397}; puts p.call; puts 1398}; puts x.call; puts 1399; end; +begin; puts 1400; m_125; puts 1401; rescue; puts 1402; puts $!.class; end +$g = 0; begin; puts 1403; x = Proc.new { puts 1404; p = get_block{ puts 1405; next; puts 1406}; puts p.call; puts 1407}; puts x.call; puts 1408; rescue; puts 1409; puts $!.class; end +$g = 0; def m_126; puts 1410; x = Proc.new { puts 1411; p = get_block{ puts 1412; next; puts 1413}; puts p.call; puts 1414}; puts x.call; puts 1415; end; +begin; puts 1416; m_126; puts 1417; rescue; puts 1418; puts $!.class; end +$g = 0; begin; puts 1419; x = Proc.new { puts 1420; p = get_lambda{ puts 1421; next; puts 1422}; puts p.call; puts 1423}; puts x.call; puts 1424; rescue; puts 1425; puts $!.class; end +$g = 0; def m_127; puts 1426; x = Proc.new { puts 1427; p = get_lambda{ puts 1428; next; puts 1429}; puts p.call; puts 1430}; puts x.call; puts 1431; end; +begin; puts 1432; m_127; puts 1433; rescue; puts 1434; puts $!.class; end +$g = 0; begin; puts 1435; x = Proc.new { puts 1436; p = get_proc{ puts 1437; next; puts 1438}; puts p.call; puts 1439}; puts x.call; puts 1440; rescue; puts 1441; puts $!.class; end +$g = 0; def m_128; puts 1442; x = Proc.new { puts 1443; p = get_proc{ puts 1444; next; puts 1445}; puts p.call; puts 1446}; puts x.call; puts 1447; end; +begin; puts 1448; m_128; puts 1449; rescue; puts 1450; puts $!.class; end +$g = 0; begin; puts 1451; x = Proc.new { puts 1452; p = get_local_block; puts p.call; puts 1453}; puts x.call; puts 1454; rescue; puts 1455; puts $!.class; end +$g = 0; def m_129; puts 1456; x = Proc.new { puts 1457; p = get_local_block; puts p.call; puts 1458}; puts x.call; puts 1459; end; +begin; puts 1460; m_129; puts 1461; rescue; puts 1462; puts $!.class; end +$g = 0; begin; puts 1463; x = Proc.new { puts 1464; p = get_local_lambda; puts p.call; puts 1465}; puts x.call; puts 1466; rescue; puts 1467; puts $!.class; end +$g = 0; def m_130; puts 1468; x = Proc.new { puts 1469; p = get_local_lambda; puts p.call; puts 1470}; puts x.call; puts 1471; end; +begin; puts 1472; m_130; puts 1473; rescue; puts 1474; puts $!.class; end +$g = 0; begin; puts 1475; x = Proc.new { puts 1476; p = get_local_proc; puts p.call; puts 1477}; puts x.call; puts 1478; rescue; puts 1479; puts $!.class; end +$g = 0; def m_131; puts 1480; x = Proc.new { puts 1481; p = get_local_proc; puts p.call; puts 1482}; puts x.call; puts 1483; end; +begin; puts 1484; m_131; puts 1485; rescue; puts 1486; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1487; x = get_block { puts 1488; p = lambda{ puts 1489; next; puts 1490}; puts p.call; puts 1491}; puts x.call; puts 1492; rescue; puts 1493; puts $!.class; end +$g = 0; def m_132; puts 1494; x = get_block { puts 1495; p = lambda{ puts 1496; next; puts 1497}; puts p.call; puts 1498}; puts x.call; puts 1499; end; +begin; puts 1500; m_132; puts 1501; rescue; puts 1502; puts $!.class; end +$g = 0; begin; puts 1503; x = get_block { puts 1504; p = Proc.new{ puts 1505; next; puts 1506}; puts p.call; puts 1507}; puts x.call; puts 1508; rescue; puts 1509; puts $!.class; end +$g = 0; def m_133; puts 1510; x = get_block { puts 1511; p = Proc.new{ puts 1512; next; puts 1513}; puts p.call; puts 1514}; puts x.call; puts 1515; end; +begin; puts 1516; m_133; puts 1517; rescue; puts 1518; puts $!.class; end +$g = 0; begin; puts 1519; x = get_block { puts 1520; p = get_block{ puts 1521; next; puts 1522}; puts p.call; puts 1523}; puts x.call; puts 1524; rescue; puts 1525; puts $!.class; end +$g = 0; def m_134; puts 1526; x = get_block { puts 1527; p = get_block{ puts 1528; next; puts 1529}; puts p.call; puts 1530}; puts x.call; puts 1531; end; +begin; puts 1532; m_134; puts 1533; rescue; puts 1534; puts $!.class; end +$g = 0; begin; puts 1535; x = get_block { puts 1536; p = get_lambda{ puts 1537; next; puts 1538}; puts p.call; puts 1539}; puts x.call; puts 1540; rescue; puts 1541; puts $!.class; end +$g = 0; def m_135; puts 1542; x = get_block { puts 1543; p = get_lambda{ puts 1544; next; puts 1545}; puts p.call; puts 1546}; puts x.call; puts 1547; end; +begin; puts 1548; m_135; puts 1549; rescue; puts 1550; puts $!.class; end +$g = 0; begin; puts 1551; x = get_block { puts 1552; p = get_proc{ puts 1553; next; puts 1554}; puts p.call; puts 1555}; puts x.call; puts 1556; rescue; puts 1557; puts $!.class; end +$g = 0; def m_136; puts 1558; x = get_block { puts 1559; p = get_proc{ puts 1560; next; puts 1561}; puts p.call; puts 1562}; puts x.call; puts 1563; end; +begin; puts 1564; m_136; puts 1565; rescue; puts 1566; puts $!.class; end +$g = 0; begin; puts 1567; x = get_block { puts 1568; p = get_local_block; puts p.call; puts 1569}; puts x.call; puts 1570; rescue; puts 1571; puts $!.class; end +$g = 0; def m_137; puts 1572; x = get_block { puts 1573; p = get_local_block; puts p.call; puts 1574}; puts x.call; puts 1575; end; +begin; puts 1576; m_137; puts 1577; rescue; puts 1578; puts $!.class; end +$g = 0; begin; puts 1579; x = get_block { puts 1580; p = get_local_lambda; puts p.call; puts 1581}; puts x.call; puts 1582; rescue; puts 1583; puts $!.class; end +$g = 0; def m_138; puts 1584; x = get_block { puts 1585; p = get_local_lambda; puts p.call; puts 1586}; puts x.call; puts 1587; end; +begin; puts 1588; m_138; puts 1589; rescue; puts 1590; puts $!.class; end +$g = 0; begin; puts 1591; x = get_block { puts 1592; p = get_local_proc; puts p.call; puts 1593}; puts x.call; puts 1594; rescue; puts 1595; puts $!.class; end +$g = 0; def m_139; puts 1596; x = get_block { puts 1597; p = get_local_proc; puts p.call; puts 1598}; puts x.call; puts 1599; end; +begin; puts 1600; m_139; puts 1601; rescue; puts 1602; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1603; x = get_lambda { puts 1604; p = lambda{ puts 1605; next; puts 1606}; puts p.call; puts 1607}; puts x.call; puts 1608; rescue; puts 1609; puts $!.class; end +$g = 0; def m_140; puts 1610; x = get_lambda { puts 1611; p = lambda{ puts 1612; next; puts 1613}; puts p.call; puts 1614}; puts x.call; puts 1615; end; +begin; puts 1616; m_140; puts 1617; rescue; puts 1618; puts $!.class; end +$g = 0; begin; puts 1619; x = get_lambda { puts 1620; p = Proc.new{ puts 1621; next; puts 1622}; puts p.call; puts 1623}; puts x.call; puts 1624; rescue; puts 1625; puts $!.class; end +$g = 0; def m_141; puts 1626; x = get_lambda { puts 1627; p = Proc.new{ puts 1628; next; puts 1629}; puts p.call; puts 1630}; puts x.call; puts 1631; end; +begin; puts 1632; m_141; puts 1633; rescue; puts 1634; puts $!.class; end +$g = 0; begin; puts 1635; x = get_lambda { puts 1636; p = get_block{ puts 1637; next; puts 1638}; puts p.call; puts 1639}; puts x.call; puts 1640; rescue; puts 1641; puts $!.class; end +$g = 0; def m_142; puts 1642; x = get_lambda { puts 1643; p = get_block{ puts 1644; next; puts 1645}; puts p.call; puts 1646}; puts x.call; puts 1647; end; +begin; puts 1648; m_142; puts 1649; rescue; puts 1650; puts $!.class; end +$g = 0; begin; puts 1651; x = get_lambda { puts 1652; p = get_lambda{ puts 1653; next; puts 1654}; puts p.call; puts 1655}; puts x.call; puts 1656; rescue; puts 1657; puts $!.class; end +$g = 0; def m_143; puts 1658; x = get_lambda { puts 1659; p = get_lambda{ puts 1660; next; puts 1661}; puts p.call; puts 1662}; puts x.call; puts 1663; end; +begin; puts 1664; m_143; puts 1665; rescue; puts 1666; puts $!.class; end +$g = 0; begin; puts 1667; x = get_lambda { puts 1668; p = get_proc{ puts 1669; next; puts 1670}; puts p.call; puts 1671}; puts x.call; puts 1672; rescue; puts 1673; puts $!.class; end +$g = 0; def m_144; puts 1674; x = get_lambda { puts 1675; p = get_proc{ puts 1676; next; puts 1677}; puts p.call; puts 1678}; puts x.call; puts 1679; end; +begin; puts 1680; m_144; puts 1681; rescue; puts 1682; puts $!.class; end +$g = 0; begin; puts 1683; x = get_lambda { puts 1684; p = get_local_block; puts p.call; puts 1685}; puts x.call; puts 1686; rescue; puts 1687; puts $!.class; end +$g = 0; def m_145; puts 1688; x = get_lambda { puts 1689; p = get_local_block; puts p.call; puts 1690}; puts x.call; puts 1691; end; +begin; puts 1692; m_145; puts 1693; rescue; puts 1694; puts $!.class; end +$g = 0; begin; puts 1695; x = get_lambda { puts 1696; p = get_local_lambda; puts p.call; puts 1697}; puts x.call; puts 1698; rescue; puts 1699; puts $!.class; end +$g = 0; def m_146; puts 1700; x = get_lambda { puts 1701; p = get_local_lambda; puts p.call; puts 1702}; puts x.call; puts 1703; end; +begin; puts 1704; m_146; puts 1705; rescue; puts 1706; puts $!.class; end +$g = 0; begin; puts 1707; x = get_lambda { puts 1708; p = get_local_proc; puts p.call; puts 1709}; puts x.call; puts 1710; rescue; puts 1711; puts $!.class; end +$g = 0; def m_147; puts 1712; x = get_lambda { puts 1713; p = get_local_proc; puts p.call; puts 1714}; puts x.call; puts 1715; end; +begin; puts 1716; m_147; puts 1717; rescue; puts 1718; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1719; x = get_proc { puts 1720; p = lambda{ puts 1721; next; puts 1722}; puts p.call; puts 1723}; puts x.call; puts 1724; rescue; puts 1725; puts $!.class; end +$g = 0; def m_148; puts 1726; x = get_proc { puts 1727; p = lambda{ puts 1728; next; puts 1729}; puts p.call; puts 1730}; puts x.call; puts 1731; end; +begin; puts 1732; m_148; puts 1733; rescue; puts 1734; puts $!.class; end +$g = 0; begin; puts 1735; x = get_proc { puts 1736; p = Proc.new{ puts 1737; next; puts 1738}; puts p.call; puts 1739}; puts x.call; puts 1740; rescue; puts 1741; puts $!.class; end +$g = 0; def m_149; puts 1742; x = get_proc { puts 1743; p = Proc.new{ puts 1744; next; puts 1745}; puts p.call; puts 1746}; puts x.call; puts 1747; end; +begin; puts 1748; m_149; puts 1749; rescue; puts 1750; puts $!.class; end +$g = 0; begin; puts 1751; x = get_proc { puts 1752; p = get_block{ puts 1753; next; puts 1754}; puts p.call; puts 1755}; puts x.call; puts 1756; rescue; puts 1757; puts $!.class; end +$g = 0; def m_150; puts 1758; x = get_proc { puts 1759; p = get_block{ puts 1760; next; puts 1761}; puts p.call; puts 1762}; puts x.call; puts 1763; end; +begin; puts 1764; m_150; puts 1765; rescue; puts 1766; puts $!.class; end +$g = 0; begin; puts 1767; x = get_proc { puts 1768; p = get_lambda{ puts 1769; next; puts 1770}; puts p.call; puts 1771}; puts x.call; puts 1772; rescue; puts 1773; puts $!.class; end +$g = 0; def m_151; puts 1774; x = get_proc { puts 1775; p = get_lambda{ puts 1776; next; puts 1777}; puts p.call; puts 1778}; puts x.call; puts 1779; end; +begin; puts 1780; m_151; puts 1781; rescue; puts 1782; puts $!.class; end +$g = 0; begin; puts 1783; x = get_proc { puts 1784; p = get_proc{ puts 1785; next; puts 1786}; puts p.call; puts 1787}; puts x.call; puts 1788; rescue; puts 1789; puts $!.class; end +$g = 0; def m_152; puts 1790; x = get_proc { puts 1791; p = get_proc{ puts 1792; next; puts 1793}; puts p.call; puts 1794}; puts x.call; puts 1795; end; +begin; puts 1796; m_152; puts 1797; rescue; puts 1798; puts $!.class; end +$g = 0; begin; puts 1799; x = get_proc { puts 1800; p = get_local_block; puts p.call; puts 1801}; puts x.call; puts 1802; rescue; puts 1803; puts $!.class; end +$g = 0; def m_153; puts 1804; x = get_proc { puts 1805; p = get_local_block; puts p.call; puts 1806}; puts x.call; puts 1807; end; +begin; puts 1808; m_153; puts 1809; rescue; puts 1810; puts $!.class; end +$g = 0; begin; puts 1811; x = get_proc { puts 1812; p = get_local_lambda; puts p.call; puts 1813}; puts x.call; puts 1814; rescue; puts 1815; puts $!.class; end +$g = 0; def m_154; puts 1816; x = get_proc { puts 1817; p = get_local_lambda; puts p.call; puts 1818}; puts x.call; puts 1819; end; +begin; puts 1820; m_154; puts 1821; rescue; puts 1822; puts $!.class; end +$g = 0; begin; puts 1823; x = get_proc { puts 1824; p = get_local_proc; puts p.call; puts 1825}; puts x.call; puts 1826; rescue; puts 1827; puts $!.class; end +$g = 0; def m_155; puts 1828; x = get_proc { puts 1829; p = get_local_proc; puts p.call; puts 1830}; puts x.call; puts 1831; end; +begin; puts 1832; m_155; puts 1833; rescue; puts 1834; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1835; for i in [1, 2]; puts 1836; p = lambda{ puts 1837; next; puts 1838}; puts p.call; puts 1839; end; puts 1840; rescue; puts 1841; puts $!.class; end +$g = 0; def m_156; puts 1842; for i in [1, 2]; puts 1843; p = lambda{ puts 1844; next; puts 1845}; puts p.call; puts 1846; end; puts 1847; end; +begin; puts 1848; m_156; puts 1849; rescue; puts 1850; puts $!.class; end +$g = 0; begin; puts 1851; for i in [1, 2]; puts 1852; p = Proc.new{ puts 1853; next; puts 1854}; puts p.call; puts 1855; end; puts 1856; rescue; puts 1857; puts $!.class; end +$g = 0; def m_157; puts 1858; for i in [1, 2]; puts 1859; p = Proc.new{ puts 1860; next; puts 1861}; puts p.call; puts 1862; end; puts 1863; end; +begin; puts 1864; m_157; puts 1865; rescue; puts 1866; puts $!.class; end +$g = 0; begin; puts 1867; for i in [1, 2]; puts 1868; p = get_block{ puts 1869; next; puts 1870}; puts p.call; puts 1871; end; puts 1872; rescue; puts 1873; puts $!.class; end +$g = 0; def m_158; puts 1874; for i in [1, 2]; puts 1875; p = get_block{ puts 1876; next; puts 1877}; puts p.call; puts 1878; end; puts 1879; end; +begin; puts 1880; m_158; puts 1881; rescue; puts 1882; puts $!.class; end +$g = 0; begin; puts 1883; for i in [1, 2]; puts 1884; p = get_lambda{ puts 1885; next; puts 1886}; puts p.call; puts 1887; end; puts 1888; rescue; puts 1889; puts $!.class; end +$g = 0; def m_159; puts 1890; for i in [1, 2]; puts 1891; p = get_lambda{ puts 1892; next; puts 1893}; puts p.call; puts 1894; end; puts 1895; end; +begin; puts 1896; m_159; puts 1897; rescue; puts 1898; puts $!.class; end +$g = 0; begin; puts 1899; for i in [1, 2]; puts 1900; p = get_proc{ puts 1901; next; puts 1902}; puts p.call; puts 1903; end; puts 1904; rescue; puts 1905; puts $!.class; end +$g = 0; def m_160; puts 1906; for i in [1, 2]; puts 1907; p = get_proc{ puts 1908; next; puts 1909}; puts p.call; puts 1910; end; puts 1911; end; +begin; puts 1912; m_160; puts 1913; rescue; puts 1914; puts $!.class; end +$g = 0; begin; puts 1915; for i in [1, 2]; puts 1916; p = get_local_block; puts p.call; puts 1917; end; puts 1918; rescue; puts 1919; puts $!.class; end +$g = 0; def m_161; puts 1920; for i in [1, 2]; puts 1921; p = get_local_block; puts p.call; puts 1922; end; puts 1923; end; +begin; puts 1924; m_161; puts 1925; rescue; puts 1926; puts $!.class; end +$g = 0; begin; puts 1927; for i in [1, 2]; puts 1928; p = get_local_lambda; puts p.call; puts 1929; end; puts 1930; rescue; puts 1931; puts $!.class; end +$g = 0; def m_162; puts 1932; for i in [1, 2]; puts 1933; p = get_local_lambda; puts p.call; puts 1934; end; puts 1935; end; +begin; puts 1936; m_162; puts 1937; rescue; puts 1938; puts $!.class; end +$g = 0; begin; puts 1939; for i in [1, 2]; puts 1940; p = get_local_proc; puts p.call; puts 1941; end; puts 1942; rescue; puts 1943; puts $!.class; end +$g = 0; def m_163; puts 1944; for i in [1, 2]; puts 1945; p = get_local_proc; puts p.call; puts 1946; end; puts 1947; end; +begin; puts 1948; m_163; puts 1949; rescue; puts 1950; puts $!.class; end +end +test diff --git a/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_normal.rb b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_normal.rb new file mode 100644 index 0000000000..248a5e1327 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_normal.rb @@ -0,0 +1,595 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +# helper +def myeval(line); puts 1; eval(line); puts 2; end +def call1(x); puts 3; call2(x); puts 4; end +def call2(x); puts 5; call3(x); puts 6; end +def call3(x); puts 7; puts x.call; puts 8; end + +# producer + +def get_block(&p); p; end +def get_lambda(&p); lambda(&p); end +def get_proc(&p); Proc.new(&p); end + +def get_local_block; get_block { puts 9; puts "A"; puts 10 }; end +def get_local_lambda; lambda { puts 11; puts "A"; puts 12 }; end +def get_local_proc; Proc.new { puts 13; puts "A"; puts 14 }; end + +# consumer + +# taking arguments +def iterator_via_yield; puts 15; x = yield; puts x; puts 16; end +def iterator_via_call(&p); puts 17; puts(p.call); puts 18; end + +def method_call_iterator_via_yield(&p); puts 19; iterator_via_yield(&p); puts 20; end +def method_call_iterator_via_call(&p); puts 21; iterator_via_call(&p); puts 22; end + +def method_use_lambda_and_yield; puts 23; x = lambda { puts 24; yield; puts 25 }; puts x.call; puts 26; end +def method_use_proc_and_yield; puts 27; x = Proc.new { puts 28; yield; puts 29 }; puts x.call; puts 30; end +def method_use_lambda_and_call(&p); puts 31; x = lambda { puts 32; p.call; puts 33 }; puts x.call; puts 34; end +def method_use_proc_and_call(&p); puts 35; x = Proc.new { puts 36; p.call; puts 37 }; puts x.call; puts 38; end + +def method_use_lambda_and_yield_2; puts 39; x = lambda { puts 40; yield; puts 41 }; call1(x); puts 42; end + +def method_yield_in_loop; puts 43; for i in [1, 2]; puts 44; yield; puts 45; end; puts 46; end +def method_call_in_loop(&p); puts 47; for i in [3, 4]; puts 48; p.call; puts 49; end; puts 50; end + +# created in-place +def test +$g = 0; begin; puts 51; iterator_via_yield { puts 52; puts "A"; puts 53}; puts 54; rescue; puts 55; puts $!.class; end +$g = 0; def m_1; puts 56; $g = 0; begin; puts 57; iterator_via_yield { puts 58; puts "A"; puts 59}; puts 60; rescue; puts 61; puts $!.class; end; puts 62; end; m_1 +$g = 0; begin; puts 63; iterator_via_call { puts 64; puts "A"; puts 65}; puts 66; rescue; puts 67; puts $!.class; end +$g = 0; def m_2; puts 68; $g = 0; begin; puts 69; iterator_via_call { puts 70; puts "A"; puts 71}; puts 72; rescue; puts 73; puts $!.class; end; puts 74; end; m_2 +$g = 0; begin; puts 75; method_call_iterator_via_yield { puts 76; puts "A"; puts 77}; puts 78; rescue; puts 79; puts $!.class; end +$g = 0; def m_3; puts 80; $g = 0; begin; puts 81; method_call_iterator_via_yield { puts 82; puts "A"; puts 83}; puts 84; rescue; puts 85; puts $!.class; end; puts 86; end; m_3 +$g = 0; begin; puts 87; method_call_iterator_via_call { puts 88; puts "A"; puts 89}; puts 90; rescue; puts 91; puts $!.class; end +$g = 0; def m_4; puts 92; $g = 0; begin; puts 93; method_call_iterator_via_call { puts 94; puts "A"; puts 95}; puts 96; rescue; puts 97; puts $!.class; end; puts 98; end; m_4 +$g = 0; begin; puts 99; method_use_lambda_and_yield { puts 100; puts "A"; puts 101}; puts 102; rescue; puts 103; puts $!.class; end +$g = 0; def m_5; puts 104; $g = 0; begin; puts 105; method_use_lambda_and_yield { puts 106; puts "A"; puts 107}; puts 108; rescue; puts 109; puts $!.class; end; puts 110; end; m_5 +$g = 0; begin; puts 111; method_use_proc_and_yield { puts 112; puts "A"; puts 113}; puts 114; rescue; puts 115; puts $!.class; end +$g = 0; def m_6; puts 116; $g = 0; begin; puts 117; method_use_proc_and_yield { puts 118; puts "A"; puts 119}; puts 120; rescue; puts 121; puts $!.class; end; puts 122; end; m_6 +$g = 0; begin; puts 123; method_use_lambda_and_call { puts 124; puts "A"; puts 125}; puts 126; rescue; puts 127; puts $!.class; end +$g = 0; def m_7; puts 128; $g = 0; begin; puts 129; method_use_lambda_and_call { puts 130; puts "A"; puts 131}; puts 132; rescue; puts 133; puts $!.class; end; puts 134; end; m_7 +$g = 0; begin; puts 135; method_use_proc_and_call { puts 136; puts "A"; puts 137}; puts 138; rescue; puts 139; puts $!.class; end +$g = 0; def m_8; puts 140; $g = 0; begin; puts 141; method_use_proc_and_call { puts 142; puts "A"; puts 143}; puts 144; rescue; puts 145; puts $!.class; end; puts 146; end; m_8 +$g = 0; begin; puts 147; method_use_lambda_and_yield_2 { puts 148; puts "A"; puts 149}; puts 150; rescue; puts 151; puts $!.class; end +$g = 0; def m_9; puts 152; $g = 0; begin; puts 153; method_use_lambda_and_yield_2 { puts 154; puts "A"; puts 155}; puts 156; rescue; puts 157; puts $!.class; end; puts 158; end; m_9 +$g = 0; begin; puts 159; method_yield_in_loop { puts 160; puts "A"; puts 161}; puts 162; rescue; puts 163; puts $!.class; end +$g = 0; def m_10; puts 164; $g = 0; begin; puts 165; method_yield_in_loop { puts 166; puts "A"; puts 167}; puts 168; rescue; puts 169; puts $!.class; end; puts 170; end; m_10 +$g = 0; begin; puts 171; method_call_in_loop { puts 172; puts "A"; puts 173}; puts 174; rescue; puts 175; puts $!.class; end +$g = 0; def m_11; puts 176; $g = 0; begin; puts 177; method_call_in_loop { puts 178; puts "A"; puts 179}; puts 180; rescue; puts 181; puts $!.class; end; puts 182; end; m_11 +end +test + + +# created locally or from method +def test +$g = 0; begin; p = lambda{ puts 183; puts "A"; puts 184}; puts 185; iterator_via_yield(&p); puts 186; rescue; puts 187; puts $!.class; end +$g = 0; def m_12; p = lambda{ puts 188; puts "A"; puts 189}; puts 190; iterator_via_yield(&p); puts 191; end; +begin; puts 192; m_12; puts 193; rescue; puts 194; puts $!.class; end +$g = 0; begin; p = lambda{ puts 195; puts "A"; puts 196}; puts 197; iterator_via_call(&p); puts 198; rescue; puts 199; puts $!.class; end +$g = 0; def m_13; p = lambda{ puts 200; puts "A"; puts 201}; puts 202; iterator_via_call(&p); puts 203; end; +begin; puts 204; m_13; puts 205; rescue; puts 206; puts $!.class; end +$g = 0; begin; p = lambda{ puts 207; puts "A"; puts 208}; puts 209; method_call_iterator_via_yield(&p); puts 210; rescue; puts 211; puts $!.class; end +$g = 0; def m_14; p = lambda{ puts 212; puts "A"; puts 213}; puts 214; method_call_iterator_via_yield(&p); puts 215; end; +begin; puts 216; m_14; puts 217; rescue; puts 218; puts $!.class; end +$g = 0; begin; p = lambda{ puts 219; puts "A"; puts 220}; puts 221; method_call_iterator_via_call(&p); puts 222; rescue; puts 223; puts $!.class; end +$g = 0; def m_15; p = lambda{ puts 224; puts "A"; puts 225}; puts 226; method_call_iterator_via_call(&p); puts 227; end; +begin; puts 228; m_15; puts 229; rescue; puts 230; puts $!.class; end +$g = 0; begin; p = lambda{ puts 231; puts "A"; puts 232}; puts 233; method_use_lambda_and_yield(&p); puts 234; rescue; puts 235; puts $!.class; end +$g = 0; def m_16; p = lambda{ puts 236; puts "A"; puts 237}; puts 238; method_use_lambda_and_yield(&p); puts 239; end; +begin; puts 240; m_16; puts 241; rescue; puts 242; puts $!.class; end +$g = 0; begin; p = lambda{ puts 243; puts "A"; puts 244}; puts 245; method_use_proc_and_yield(&p); puts 246; rescue; puts 247; puts $!.class; end +$g = 0; def m_17; p = lambda{ puts 248; puts "A"; puts 249}; puts 250; method_use_proc_and_yield(&p); puts 251; end; +begin; puts 252; m_17; puts 253; rescue; puts 254; puts $!.class; end +$g = 0; begin; p = lambda{ puts 255; puts "A"; puts 256}; puts 257; method_use_lambda_and_call(&p); puts 258; rescue; puts 259; puts $!.class; end +$g = 0; def m_18; p = lambda{ puts 260; puts "A"; puts 261}; puts 262; method_use_lambda_and_call(&p); puts 263; end; +begin; puts 264; m_18; puts 265; rescue; puts 266; puts $!.class; end +$g = 0; begin; p = lambda{ puts 267; puts "A"; puts 268}; puts 269; method_use_proc_and_call(&p); puts 270; rescue; puts 271; puts $!.class; end +$g = 0; def m_19; p = lambda{ puts 272; puts "A"; puts 273}; puts 274; method_use_proc_and_call(&p); puts 275; end; +begin; puts 276; m_19; puts 277; rescue; puts 278; puts $!.class; end +$g = 0; begin; p = lambda{ puts 279; puts "A"; puts 280}; puts 281; method_use_lambda_and_yield_2(&p); puts 282; rescue; puts 283; puts $!.class; end +$g = 0; def m_20; p = lambda{ puts 284; puts "A"; puts 285}; puts 286; method_use_lambda_and_yield_2(&p); puts 287; end; +begin; puts 288; m_20; puts 289; rescue; puts 290; puts $!.class; end +$g = 0; begin; p = lambda{ puts 291; puts "A"; puts 292}; puts 293; method_yield_in_loop(&p); puts 294; rescue; puts 295; puts $!.class; end +$g = 0; def m_21; p = lambda{ puts 296; puts "A"; puts 297}; puts 298; method_yield_in_loop(&p); puts 299; end; +begin; puts 300; m_21; puts 301; rescue; puts 302; puts $!.class; end +$g = 0; begin; p = lambda{ puts 303; puts "A"; puts 304}; puts 305; method_call_in_loop(&p); puts 306; rescue; puts 307; puts $!.class; end +$g = 0; def m_22; p = lambda{ puts 308; puts "A"; puts 309}; puts 310; method_call_in_loop(&p); puts 311; end; +begin; puts 312; m_22; puts 313; rescue; puts 314; puts $!.class; end +end +test + +def test +$g = 0; begin; p = Proc.new{ puts 315; puts "A"; puts 316}; puts 317; iterator_via_yield(&p); puts 318; rescue; puts 319; puts $!.class; end +$g = 0; def m_23; p = Proc.new{ puts 320; puts "A"; puts 321}; puts 322; iterator_via_yield(&p); puts 323; end; +begin; puts 324; m_23; puts 325; rescue; puts 326; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 327; puts "A"; puts 328}; puts 329; iterator_via_call(&p); puts 330; rescue; puts 331; puts $!.class; end +$g = 0; def m_24; p = Proc.new{ puts 332; puts "A"; puts 333}; puts 334; iterator_via_call(&p); puts 335; end; +begin; puts 336; m_24; puts 337; rescue; puts 338; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 339; puts "A"; puts 340}; puts 341; method_call_iterator_via_yield(&p); puts 342; rescue; puts 343; puts $!.class; end +$g = 0; def m_25; p = Proc.new{ puts 344; puts "A"; puts 345}; puts 346; method_call_iterator_via_yield(&p); puts 347; end; +begin; puts 348; m_25; puts 349; rescue; puts 350; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 351; puts "A"; puts 352}; puts 353; method_call_iterator_via_call(&p); puts 354; rescue; puts 355; puts $!.class; end +$g = 0; def m_26; p = Proc.new{ puts 356; puts "A"; puts 357}; puts 358; method_call_iterator_via_call(&p); puts 359; end; +begin; puts 360; m_26; puts 361; rescue; puts 362; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 363; puts "A"; puts 364}; puts 365; method_use_lambda_and_yield(&p); puts 366; rescue; puts 367; puts $!.class; end +$g = 0; def m_27; p = Proc.new{ puts 368; puts "A"; puts 369}; puts 370; method_use_lambda_and_yield(&p); puts 371; end; +begin; puts 372; m_27; puts 373; rescue; puts 374; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 375; puts "A"; puts 376}; puts 377; method_use_proc_and_yield(&p); puts 378; rescue; puts 379; puts $!.class; end +$g = 0; def m_28; p = Proc.new{ puts 380; puts "A"; puts 381}; puts 382; method_use_proc_and_yield(&p); puts 383; end; +begin; puts 384; m_28; puts 385; rescue; puts 386; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 387; puts "A"; puts 388}; puts 389; method_use_lambda_and_call(&p); puts 390; rescue; puts 391; puts $!.class; end +$g = 0; def m_29; p = Proc.new{ puts 392; puts "A"; puts 393}; puts 394; method_use_lambda_and_call(&p); puts 395; end; +begin; puts 396; m_29; puts 397; rescue; puts 398; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 399; puts "A"; puts 400}; puts 401; method_use_proc_and_call(&p); puts 402; rescue; puts 403; puts $!.class; end +$g = 0; def m_30; p = Proc.new{ puts 404; puts "A"; puts 405}; puts 406; method_use_proc_and_call(&p); puts 407; end; +begin; puts 408; m_30; puts 409; rescue; puts 410; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 411; puts "A"; puts 412}; puts 413; method_use_lambda_and_yield_2(&p); puts 414; rescue; puts 415; puts $!.class; end +$g = 0; def m_31; p = Proc.new{ puts 416; puts "A"; puts 417}; puts 418; method_use_lambda_and_yield_2(&p); puts 419; end; +begin; puts 420; m_31; puts 421; rescue; puts 422; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 423; puts "A"; puts 424}; puts 425; method_yield_in_loop(&p); puts 426; rescue; puts 427; puts $!.class; end +$g = 0; def m_32; p = Proc.new{ puts 428; puts "A"; puts 429}; puts 430; method_yield_in_loop(&p); puts 431; end; +begin; puts 432; m_32; puts 433; rescue; puts 434; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 435; puts "A"; puts 436}; puts 437; method_call_in_loop(&p); puts 438; rescue; puts 439; puts $!.class; end +$g = 0; def m_33; p = Proc.new{ puts 440; puts "A"; puts 441}; puts 442; method_call_in_loop(&p); puts 443; end; +begin; puts 444; m_33; puts 445; rescue; puts 446; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_block{ puts 447; puts "A"; puts 448}; puts 449; iterator_via_yield(&p); puts 450; rescue; puts 451; puts $!.class; end +$g = 0; def m_34; p = get_block{ puts 452; puts "A"; puts 453}; puts 454; iterator_via_yield(&p); puts 455; end; +begin; puts 456; m_34; puts 457; rescue; puts 458; puts $!.class; end +$g = 0; begin; p = get_block{ puts 459; puts "A"; puts 460}; puts 461; iterator_via_call(&p); puts 462; rescue; puts 463; puts $!.class; end +$g = 0; def m_35; p = get_block{ puts 464; puts "A"; puts 465}; puts 466; iterator_via_call(&p); puts 467; end; +begin; puts 468; m_35; puts 469; rescue; puts 470; puts $!.class; end +$g = 0; begin; p = get_block{ puts 471; puts "A"; puts 472}; puts 473; method_call_iterator_via_yield(&p); puts 474; rescue; puts 475; puts $!.class; end +$g = 0; def m_36; p = get_block{ puts 476; puts "A"; puts 477}; puts 478; method_call_iterator_via_yield(&p); puts 479; end; +begin; puts 480; m_36; puts 481; rescue; puts 482; puts $!.class; end +$g = 0; begin; p = get_block{ puts 483; puts "A"; puts 484}; puts 485; method_call_iterator_via_call(&p); puts 486; rescue; puts 487; puts $!.class; end +$g = 0; def m_37; p = get_block{ puts 488; puts "A"; puts 489}; puts 490; method_call_iterator_via_call(&p); puts 491; end; +begin; puts 492; m_37; puts 493; rescue; puts 494; puts $!.class; end +$g = 0; begin; p = get_block{ puts 495; puts "A"; puts 496}; puts 497; method_use_lambda_and_yield(&p); puts 498; rescue; puts 499; puts $!.class; end +$g = 0; def m_38; p = get_block{ puts 500; puts "A"; puts 501}; puts 502; method_use_lambda_and_yield(&p); puts 503; end; +begin; puts 504; m_38; puts 505; rescue; puts 506; puts $!.class; end +$g = 0; begin; p = get_block{ puts 507; puts "A"; puts 508}; puts 509; method_use_proc_and_yield(&p); puts 510; rescue; puts 511; puts $!.class; end +$g = 0; def m_39; p = get_block{ puts 512; puts "A"; puts 513}; puts 514; method_use_proc_and_yield(&p); puts 515; end; +begin; puts 516; m_39; puts 517; rescue; puts 518; puts $!.class; end +$g = 0; begin; p = get_block{ puts 519; puts "A"; puts 520}; puts 521; method_use_lambda_and_call(&p); puts 522; rescue; puts 523; puts $!.class; end +$g = 0; def m_40; p = get_block{ puts 524; puts "A"; puts 525}; puts 526; method_use_lambda_and_call(&p); puts 527; end; +begin; puts 528; m_40; puts 529; rescue; puts 530; puts $!.class; end +$g = 0; begin; p = get_block{ puts 531; puts "A"; puts 532}; puts 533; method_use_proc_and_call(&p); puts 534; rescue; puts 535; puts $!.class; end +$g = 0; def m_41; p = get_block{ puts 536; puts "A"; puts 537}; puts 538; method_use_proc_and_call(&p); puts 539; end; +begin; puts 540; m_41; puts 541; rescue; puts 542; puts $!.class; end +$g = 0; begin; p = get_block{ puts 543; puts "A"; puts 544}; puts 545; method_use_lambda_and_yield_2(&p); puts 546; rescue; puts 547; puts $!.class; end +$g = 0; def m_42; p = get_block{ puts 548; puts "A"; puts 549}; puts 550; method_use_lambda_and_yield_2(&p); puts 551; end; +begin; puts 552; m_42; puts 553; rescue; puts 554; puts $!.class; end +$g = 0; begin; p = get_block{ puts 555; puts "A"; puts 556}; puts 557; method_yield_in_loop(&p); puts 558; rescue; puts 559; puts $!.class; end +$g = 0; def m_43; p = get_block{ puts 560; puts "A"; puts 561}; puts 562; method_yield_in_loop(&p); puts 563; end; +begin; puts 564; m_43; puts 565; rescue; puts 566; puts $!.class; end +$g = 0; begin; p = get_block{ puts 567; puts "A"; puts 568}; puts 569; method_call_in_loop(&p); puts 570; rescue; puts 571; puts $!.class; end +$g = 0; def m_44; p = get_block{ puts 572; puts "A"; puts 573}; puts 574; method_call_in_loop(&p); puts 575; end; +begin; puts 576; m_44; puts 577; rescue; puts 578; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_lambda{ puts 579; puts "A"; puts 580}; puts 581; iterator_via_yield(&p); puts 582; rescue; puts 583; puts $!.class; end +$g = 0; def m_45; p = get_lambda{ puts 584; puts "A"; puts 585}; puts 586; iterator_via_yield(&p); puts 587; end; +begin; puts 588; m_45; puts 589; rescue; puts 590; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 591; puts "A"; puts 592}; puts 593; iterator_via_call(&p); puts 594; rescue; puts 595; puts $!.class; end +$g = 0; def m_46; p = get_lambda{ puts 596; puts "A"; puts 597}; puts 598; iterator_via_call(&p); puts 599; end; +begin; puts 600; m_46; puts 601; rescue; puts 602; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 603; puts "A"; puts 604}; puts 605; method_call_iterator_via_yield(&p); puts 606; rescue; puts 607; puts $!.class; end +$g = 0; def m_47; p = get_lambda{ puts 608; puts "A"; puts 609}; puts 610; method_call_iterator_via_yield(&p); puts 611; end; +begin; puts 612; m_47; puts 613; rescue; puts 614; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 615; puts "A"; puts 616}; puts 617; method_call_iterator_via_call(&p); puts 618; rescue; puts 619; puts $!.class; end +$g = 0; def m_48; p = get_lambda{ puts 620; puts "A"; puts 621}; puts 622; method_call_iterator_via_call(&p); puts 623; end; +begin; puts 624; m_48; puts 625; rescue; puts 626; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 627; puts "A"; puts 628}; puts 629; method_use_lambda_and_yield(&p); puts 630; rescue; puts 631; puts $!.class; end +$g = 0; def m_49; p = get_lambda{ puts 632; puts "A"; puts 633}; puts 634; method_use_lambda_and_yield(&p); puts 635; end; +begin; puts 636; m_49; puts 637; rescue; puts 638; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 639; puts "A"; puts 640}; puts 641; method_use_proc_and_yield(&p); puts 642; rescue; puts 643; puts $!.class; end +$g = 0; def m_50; p = get_lambda{ puts 644; puts "A"; puts 645}; puts 646; method_use_proc_and_yield(&p); puts 647; end; +begin; puts 648; m_50; puts 649; rescue; puts 650; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 651; puts "A"; puts 652}; puts 653; method_use_lambda_and_call(&p); puts 654; rescue; puts 655; puts $!.class; end +$g = 0; def m_51; p = get_lambda{ puts 656; puts "A"; puts 657}; puts 658; method_use_lambda_and_call(&p); puts 659; end; +begin; puts 660; m_51; puts 661; rescue; puts 662; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 663; puts "A"; puts 664}; puts 665; method_use_proc_and_call(&p); puts 666; rescue; puts 667; puts $!.class; end +$g = 0; def m_52; p = get_lambda{ puts 668; puts "A"; puts 669}; puts 670; method_use_proc_and_call(&p); puts 671; end; +begin; puts 672; m_52; puts 673; rescue; puts 674; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 675; puts "A"; puts 676}; puts 677; method_use_lambda_and_yield_2(&p); puts 678; rescue; puts 679; puts $!.class; end +$g = 0; def m_53; p = get_lambda{ puts 680; puts "A"; puts 681}; puts 682; method_use_lambda_and_yield_2(&p); puts 683; end; +begin; puts 684; m_53; puts 685; rescue; puts 686; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 687; puts "A"; puts 688}; puts 689; method_yield_in_loop(&p); puts 690; rescue; puts 691; puts $!.class; end +$g = 0; def m_54; p = get_lambda{ puts 692; puts "A"; puts 693}; puts 694; method_yield_in_loop(&p); puts 695; end; +begin; puts 696; m_54; puts 697; rescue; puts 698; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 699; puts "A"; puts 700}; puts 701; method_call_in_loop(&p); puts 702; rescue; puts 703; puts $!.class; end +$g = 0; def m_55; p = get_lambda{ puts 704; puts "A"; puts 705}; puts 706; method_call_in_loop(&p); puts 707; end; +begin; puts 708; m_55; puts 709; rescue; puts 710; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_proc{ puts 711; puts "A"; puts 712}; puts 713; iterator_via_yield(&p); puts 714; rescue; puts 715; puts $!.class; end +$g = 0; def m_56; p = get_proc{ puts 716; puts "A"; puts 717}; puts 718; iterator_via_yield(&p); puts 719; end; +begin; puts 720; m_56; puts 721; rescue; puts 722; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 723; puts "A"; puts 724}; puts 725; iterator_via_call(&p); puts 726; rescue; puts 727; puts $!.class; end +$g = 0; def m_57; p = get_proc{ puts 728; puts "A"; puts 729}; puts 730; iterator_via_call(&p); puts 731; end; +begin; puts 732; m_57; puts 733; rescue; puts 734; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 735; puts "A"; puts 736}; puts 737; method_call_iterator_via_yield(&p); puts 738; rescue; puts 739; puts $!.class; end +$g = 0; def m_58; p = get_proc{ puts 740; puts "A"; puts 741}; puts 742; method_call_iterator_via_yield(&p); puts 743; end; +begin; puts 744; m_58; puts 745; rescue; puts 746; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 747; puts "A"; puts 748}; puts 749; method_call_iterator_via_call(&p); puts 750; rescue; puts 751; puts $!.class; end +$g = 0; def m_59; p = get_proc{ puts 752; puts "A"; puts 753}; puts 754; method_call_iterator_via_call(&p); puts 755; end; +begin; puts 756; m_59; puts 757; rescue; puts 758; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 759; puts "A"; puts 760}; puts 761; method_use_lambda_and_yield(&p); puts 762; rescue; puts 763; puts $!.class; end +$g = 0; def m_60; p = get_proc{ puts 764; puts "A"; puts 765}; puts 766; method_use_lambda_and_yield(&p); puts 767; end; +begin; puts 768; m_60; puts 769; rescue; puts 770; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 771; puts "A"; puts 772}; puts 773; method_use_proc_and_yield(&p); puts 774; rescue; puts 775; puts $!.class; end +$g = 0; def m_61; p = get_proc{ puts 776; puts "A"; puts 777}; puts 778; method_use_proc_and_yield(&p); puts 779; end; +begin; puts 780; m_61; puts 781; rescue; puts 782; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 783; puts "A"; puts 784}; puts 785; method_use_lambda_and_call(&p); puts 786; rescue; puts 787; puts $!.class; end +$g = 0; def m_62; p = get_proc{ puts 788; puts "A"; puts 789}; puts 790; method_use_lambda_and_call(&p); puts 791; end; +begin; puts 792; m_62; puts 793; rescue; puts 794; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 795; puts "A"; puts 796}; puts 797; method_use_proc_and_call(&p); puts 798; rescue; puts 799; puts $!.class; end +$g = 0; def m_63; p = get_proc{ puts 800; puts "A"; puts 801}; puts 802; method_use_proc_and_call(&p); puts 803; end; +begin; puts 804; m_63; puts 805; rescue; puts 806; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 807; puts "A"; puts 808}; puts 809; method_use_lambda_and_yield_2(&p); puts 810; rescue; puts 811; puts $!.class; end +$g = 0; def m_64; p = get_proc{ puts 812; puts "A"; puts 813}; puts 814; method_use_lambda_and_yield_2(&p); puts 815; end; +begin; puts 816; m_64; puts 817; rescue; puts 818; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 819; puts "A"; puts 820}; puts 821; method_yield_in_loop(&p); puts 822; rescue; puts 823; puts $!.class; end +$g = 0; def m_65; p = get_proc{ puts 824; puts "A"; puts 825}; puts 826; method_yield_in_loop(&p); puts 827; end; +begin; puts 828; m_65; puts 829; rescue; puts 830; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 831; puts "A"; puts 832}; puts 833; method_call_in_loop(&p); puts 834; rescue; puts 835; puts $!.class; end +$g = 0; def m_66; p = get_proc{ puts 836; puts "A"; puts 837}; puts 838; method_call_in_loop(&p); puts 839; end; +begin; puts 840; m_66; puts 841; rescue; puts 842; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_block; puts 843; iterator_via_yield(&p); puts 844; rescue; puts 845; puts $!.class; end +$g = 0; def m_67; p = get_local_block; puts 846; iterator_via_yield(&p); puts 847; end; +begin; puts 848; m_67; puts 849; rescue; puts 850; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 851; iterator_via_call(&p); puts 852; rescue; puts 853; puts $!.class; end +$g = 0; def m_68; p = get_local_block; puts 854; iterator_via_call(&p); puts 855; end; +begin; puts 856; m_68; puts 857; rescue; puts 858; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 859; method_call_iterator_via_yield(&p); puts 860; rescue; puts 861; puts $!.class; end +$g = 0; def m_69; p = get_local_block; puts 862; method_call_iterator_via_yield(&p); puts 863; end; +begin; puts 864; m_69; puts 865; rescue; puts 866; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 867; method_call_iterator_via_call(&p); puts 868; rescue; puts 869; puts $!.class; end +$g = 0; def m_70; p = get_local_block; puts 870; method_call_iterator_via_call(&p); puts 871; end; +begin; puts 872; m_70; puts 873; rescue; puts 874; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 875; method_use_lambda_and_yield(&p); puts 876; rescue; puts 877; puts $!.class; end +$g = 0; def m_71; p = get_local_block; puts 878; method_use_lambda_and_yield(&p); puts 879; end; +begin; puts 880; m_71; puts 881; rescue; puts 882; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 883; method_use_proc_and_yield(&p); puts 884; rescue; puts 885; puts $!.class; end +$g = 0; def m_72; p = get_local_block; puts 886; method_use_proc_and_yield(&p); puts 887; end; +begin; puts 888; m_72; puts 889; rescue; puts 890; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 891; method_use_lambda_and_call(&p); puts 892; rescue; puts 893; puts $!.class; end +$g = 0; def m_73; p = get_local_block; puts 894; method_use_lambda_and_call(&p); puts 895; end; +begin; puts 896; m_73; puts 897; rescue; puts 898; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 899; method_use_proc_and_call(&p); puts 900; rescue; puts 901; puts $!.class; end +$g = 0; def m_74; p = get_local_block; puts 902; method_use_proc_and_call(&p); puts 903; end; +begin; puts 904; m_74; puts 905; rescue; puts 906; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 907; method_use_lambda_and_yield_2(&p); puts 908; rescue; puts 909; puts $!.class; end +$g = 0; def m_75; p = get_local_block; puts 910; method_use_lambda_and_yield_2(&p); puts 911; end; +begin; puts 912; m_75; puts 913; rescue; puts 914; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 915; method_yield_in_loop(&p); puts 916; rescue; puts 917; puts $!.class; end +$g = 0; def m_76; p = get_local_block; puts 918; method_yield_in_loop(&p); puts 919; end; +begin; puts 920; m_76; puts 921; rescue; puts 922; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 923; method_call_in_loop(&p); puts 924; rescue; puts 925; puts $!.class; end +$g = 0; def m_77; p = get_local_block; puts 926; method_call_in_loop(&p); puts 927; end; +begin; puts 928; m_77; puts 929; rescue; puts 930; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_lambda; puts 931; iterator_via_yield(&p); puts 932; rescue; puts 933; puts $!.class; end +$g = 0; def m_78; p = get_local_lambda; puts 934; iterator_via_yield(&p); puts 935; end; +begin; puts 936; m_78; puts 937; rescue; puts 938; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 939; iterator_via_call(&p); puts 940; rescue; puts 941; puts $!.class; end +$g = 0; def m_79; p = get_local_lambda; puts 942; iterator_via_call(&p); puts 943; end; +begin; puts 944; m_79; puts 945; rescue; puts 946; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 947; method_call_iterator_via_yield(&p); puts 948; rescue; puts 949; puts $!.class; end +$g = 0; def m_80; p = get_local_lambda; puts 950; method_call_iterator_via_yield(&p); puts 951; end; +begin; puts 952; m_80; puts 953; rescue; puts 954; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 955; method_call_iterator_via_call(&p); puts 956; rescue; puts 957; puts $!.class; end +$g = 0; def m_81; p = get_local_lambda; puts 958; method_call_iterator_via_call(&p); puts 959; end; +begin; puts 960; m_81; puts 961; rescue; puts 962; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 963; method_use_lambda_and_yield(&p); puts 964; rescue; puts 965; puts $!.class; end +$g = 0; def m_82; p = get_local_lambda; puts 966; method_use_lambda_and_yield(&p); puts 967; end; +begin; puts 968; m_82; puts 969; rescue; puts 970; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 971; method_use_proc_and_yield(&p); puts 972; rescue; puts 973; puts $!.class; end +$g = 0; def m_83; p = get_local_lambda; puts 974; method_use_proc_and_yield(&p); puts 975; end; +begin; puts 976; m_83; puts 977; rescue; puts 978; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 979; method_use_lambda_and_call(&p); puts 980; rescue; puts 981; puts $!.class; end +$g = 0; def m_84; p = get_local_lambda; puts 982; method_use_lambda_and_call(&p); puts 983; end; +begin; puts 984; m_84; puts 985; rescue; puts 986; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 987; method_use_proc_and_call(&p); puts 988; rescue; puts 989; puts $!.class; end +$g = 0; def m_85; p = get_local_lambda; puts 990; method_use_proc_and_call(&p); puts 991; end; +begin; puts 992; m_85; puts 993; rescue; puts 994; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 995; method_use_lambda_and_yield_2(&p); puts 996; rescue; puts 997; puts $!.class; end +$g = 0; def m_86; p = get_local_lambda; puts 998; method_use_lambda_and_yield_2(&p); puts 999; end; +begin; puts 1000; m_86; puts 1001; rescue; puts 1002; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1003; method_yield_in_loop(&p); puts 1004; rescue; puts 1005; puts $!.class; end +$g = 0; def m_87; p = get_local_lambda; puts 1006; method_yield_in_loop(&p); puts 1007; end; +begin; puts 1008; m_87; puts 1009; rescue; puts 1010; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1011; method_call_in_loop(&p); puts 1012; rescue; puts 1013; puts $!.class; end +$g = 0; def m_88; p = get_local_lambda; puts 1014; method_call_in_loop(&p); puts 1015; end; +begin; puts 1016; m_88; puts 1017; rescue; puts 1018; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_proc; puts 1019; iterator_via_yield(&p); puts 1020; rescue; puts 1021; puts $!.class; end +$g = 0; def m_89; p = get_local_proc; puts 1022; iterator_via_yield(&p); puts 1023; end; +begin; puts 1024; m_89; puts 1025; rescue; puts 1026; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1027; iterator_via_call(&p); puts 1028; rescue; puts 1029; puts $!.class; end +$g = 0; def m_90; p = get_local_proc; puts 1030; iterator_via_call(&p); puts 1031; end; +begin; puts 1032; m_90; puts 1033; rescue; puts 1034; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1035; method_call_iterator_via_yield(&p); puts 1036; rescue; puts 1037; puts $!.class; end +$g = 0; def m_91; p = get_local_proc; puts 1038; method_call_iterator_via_yield(&p); puts 1039; end; +begin; puts 1040; m_91; puts 1041; rescue; puts 1042; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1043; method_call_iterator_via_call(&p); puts 1044; rescue; puts 1045; puts $!.class; end +$g = 0; def m_92; p = get_local_proc; puts 1046; method_call_iterator_via_call(&p); puts 1047; end; +begin; puts 1048; m_92; puts 1049; rescue; puts 1050; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1051; method_use_lambda_and_yield(&p); puts 1052; rescue; puts 1053; puts $!.class; end +$g = 0; def m_93; p = get_local_proc; puts 1054; method_use_lambda_and_yield(&p); puts 1055; end; +begin; puts 1056; m_93; puts 1057; rescue; puts 1058; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1059; method_use_proc_and_yield(&p); puts 1060; rescue; puts 1061; puts $!.class; end +$g = 0; def m_94; p = get_local_proc; puts 1062; method_use_proc_and_yield(&p); puts 1063; end; +begin; puts 1064; m_94; puts 1065; rescue; puts 1066; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1067; method_use_lambda_and_call(&p); puts 1068; rescue; puts 1069; puts $!.class; end +$g = 0; def m_95; p = get_local_proc; puts 1070; method_use_lambda_and_call(&p); puts 1071; end; +begin; puts 1072; m_95; puts 1073; rescue; puts 1074; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1075; method_use_proc_and_call(&p); puts 1076; rescue; puts 1077; puts $!.class; end +$g = 0; def m_96; p = get_local_proc; puts 1078; method_use_proc_and_call(&p); puts 1079; end; +begin; puts 1080; m_96; puts 1081; rescue; puts 1082; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1083; method_use_lambda_and_yield_2(&p); puts 1084; rescue; puts 1085; puts $!.class; end +$g = 0; def m_97; p = get_local_proc; puts 1086; method_use_lambda_and_yield_2(&p); puts 1087; end; +begin; puts 1088; m_97; puts 1089; rescue; puts 1090; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1091; method_yield_in_loop(&p); puts 1092; rescue; puts 1093; puts $!.class; end +$g = 0; def m_98; p = get_local_proc; puts 1094; method_yield_in_loop(&p); puts 1095; end; +begin; puts 1096; m_98; puts 1097; rescue; puts 1098; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1099; method_call_in_loop(&p); puts 1100; rescue; puts 1101; puts $!.class; end +$g = 0; def m_99; p = get_local_proc; puts 1102; method_call_in_loop(&p); puts 1103; end; +begin; puts 1104; m_99; puts 1105; rescue; puts 1106; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1107; p = lambda{ puts 1108; puts "A"; puts 1109}; puts(p.call); puts 1110; rescue; puts 1111; puts $!.class; end +$g = 0; def m_100; puts 1112; p = lambda{ puts 1113; puts "A"; puts 1114}; puts(p.call); puts 1115; end; +begin; puts 1116; m_100; puts 1117; rescue; puts 1118; puts $!.class; end +$g = 0; begin; puts 1119; puts m_100; puts 1120; rescue; puts 1121; puts $!.class; end +$g = 0; def m_101; puts 1122; puts m_100; puts 1123; end; +begin; puts 1124; m_101; puts 1125; rescue; puts 1126; puts $!.class; end +$g = 0; begin; puts 1127; p = Proc.new{ puts 1128; puts "A"; puts 1129}; puts(p.call); puts 1130; rescue; puts 1131; puts $!.class; end +$g = 0; def m_102; puts 1132; p = Proc.new{ puts 1133; puts "A"; puts 1134}; puts(p.call); puts 1135; end; +begin; puts 1136; m_102; puts 1137; rescue; puts 1138; puts $!.class; end +$g = 0; begin; puts 1139; puts m_102; puts 1140; rescue; puts 1141; puts $!.class; end +$g = 0; def m_103; puts 1142; puts m_102; puts 1143; end; +begin; puts 1144; m_103; puts 1145; rescue; puts 1146; puts $!.class; end +$g = 0; begin; puts 1147; p = get_block{ puts 1148; puts "A"; puts 1149}; puts(p.call); puts 1150; rescue; puts 1151; puts $!.class; end +$g = 0; def m_104; puts 1152; p = get_block{ puts 1153; puts "A"; puts 1154}; puts(p.call); puts 1155; end; +begin; puts 1156; m_104; puts 1157; rescue; puts 1158; puts $!.class; end +$g = 0; begin; puts 1159; puts m_104; puts 1160; rescue; puts 1161; puts $!.class; end +$g = 0; def m_105; puts 1162; puts m_104; puts 1163; end; +begin; puts 1164; m_105; puts 1165; rescue; puts 1166; puts $!.class; end +$g = 0; begin; puts 1167; p = get_lambda{ puts 1168; puts "A"; puts 1169}; puts(p.call); puts 1170; rescue; puts 1171; puts $!.class; end +$g = 0; def m_106; puts 1172; p = get_lambda{ puts 1173; puts "A"; puts 1174}; puts(p.call); puts 1175; end; +begin; puts 1176; m_106; puts 1177; rescue; puts 1178; puts $!.class; end +$g = 0; begin; puts 1179; puts m_106; puts 1180; rescue; puts 1181; puts $!.class; end +$g = 0; def m_107; puts 1182; puts m_106; puts 1183; end; +begin; puts 1184; m_107; puts 1185; rescue; puts 1186; puts $!.class; end +$g = 0; begin; puts 1187; p = get_proc{ puts 1188; puts "A"; puts 1189}; puts(p.call); puts 1190; rescue; puts 1191; puts $!.class; end +$g = 0; def m_108; puts 1192; p = get_proc{ puts 1193; puts "A"; puts 1194}; puts(p.call); puts 1195; end; +begin; puts 1196; m_108; puts 1197; rescue; puts 1198; puts $!.class; end +$g = 0; begin; puts 1199; puts m_108; puts 1200; rescue; puts 1201; puts $!.class; end +$g = 0; def m_109; puts 1202; puts m_108; puts 1203; end; +begin; puts 1204; m_109; puts 1205; rescue; puts 1206; puts $!.class; end +$g = 0; begin; puts 1207; p = get_local_block; puts(p.call); puts 1208; rescue; puts 1209; puts $!.class; end +$g = 0; def m_110; puts 1210; p = get_local_block; puts(p.call); puts 1211; end; +begin; puts 1212; m_110; puts 1213; rescue; puts 1214; puts $!.class; end +$g = 0; begin; puts 1215; puts m_110; puts 1216; rescue; puts 1217; puts $!.class; end +$g = 0; def m_111; puts 1218; puts m_110; puts 1219; end; +begin; puts 1220; m_111; puts 1221; rescue; puts 1222; puts $!.class; end +$g = 0; begin; puts 1223; p = get_local_lambda; puts(p.call); puts 1224; rescue; puts 1225; puts $!.class; end +$g = 0; def m_112; puts 1226; p = get_local_lambda; puts(p.call); puts 1227; end; +begin; puts 1228; m_112; puts 1229; rescue; puts 1230; puts $!.class; end +$g = 0; begin; puts 1231; puts m_112; puts 1232; rescue; puts 1233; puts $!.class; end +$g = 0; def m_113; puts 1234; puts m_112; puts 1235; end; +begin; puts 1236; m_113; puts 1237; rescue; puts 1238; puts $!.class; end +$g = 0; begin; puts 1239; p = get_local_proc; puts(p.call); puts 1240; rescue; puts 1241; puts $!.class; end +$g = 0; def m_114; puts 1242; p = get_local_proc; puts(p.call); puts 1243; end; +begin; puts 1244; m_114; puts 1245; rescue; puts 1246; puts $!.class; end +$g = 0; begin; puts 1247; puts m_114; puts 1248; rescue; puts 1249; puts $!.class; end +$g = 0; def m_115; puts 1250; puts m_114; puts 1251; end; +begin; puts 1252; m_115; puts 1253; rescue; puts 1254; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1255; x = lambda { puts 1256; p = lambda{ puts 1257; puts "A"; puts 1258}; puts p.call; puts 1259}; puts x.call; puts 1260; rescue; puts 1261; puts $!.class; end +$g = 0; def m_116; puts 1262; x = lambda { puts 1263; p = lambda{ puts 1264; puts "A"; puts 1265}; puts p.call; puts 1266}; puts x.call; puts 1267; end; +begin; puts 1268; m_116; puts 1269; rescue; puts 1270; puts $!.class; end +$g = 0; begin; puts 1271; x = lambda { puts 1272; p = Proc.new{ puts 1273; puts "A"; puts 1274}; puts p.call; puts 1275}; puts x.call; puts 1276; rescue; puts 1277; puts $!.class; end +$g = 0; def m_117; puts 1278; x = lambda { puts 1279; p = Proc.new{ puts 1280; puts "A"; puts 1281}; puts p.call; puts 1282}; puts x.call; puts 1283; end; +begin; puts 1284; m_117; puts 1285; rescue; puts 1286; puts $!.class; end +$g = 0; begin; puts 1287; x = lambda { puts 1288; p = get_block{ puts 1289; puts "A"; puts 1290}; puts p.call; puts 1291}; puts x.call; puts 1292; rescue; puts 1293; puts $!.class; end +$g = 0; def m_118; puts 1294; x = lambda { puts 1295; p = get_block{ puts 1296; puts "A"; puts 1297}; puts p.call; puts 1298}; puts x.call; puts 1299; end; +begin; puts 1300; m_118; puts 1301; rescue; puts 1302; puts $!.class; end +$g = 0; begin; puts 1303; x = lambda { puts 1304; p = get_lambda{ puts 1305; puts "A"; puts 1306}; puts p.call; puts 1307}; puts x.call; puts 1308; rescue; puts 1309; puts $!.class; end +$g = 0; def m_119; puts 1310; x = lambda { puts 1311; p = get_lambda{ puts 1312; puts "A"; puts 1313}; puts p.call; puts 1314}; puts x.call; puts 1315; end; +begin; puts 1316; m_119; puts 1317; rescue; puts 1318; puts $!.class; end +$g = 0; begin; puts 1319; x = lambda { puts 1320; p = get_proc{ puts 1321; puts "A"; puts 1322}; puts p.call; puts 1323}; puts x.call; puts 1324; rescue; puts 1325; puts $!.class; end +$g = 0; def m_120; puts 1326; x = lambda { puts 1327; p = get_proc{ puts 1328; puts "A"; puts 1329}; puts p.call; puts 1330}; puts x.call; puts 1331; end; +begin; puts 1332; m_120; puts 1333; rescue; puts 1334; puts $!.class; end +$g = 0; begin; puts 1335; x = lambda { puts 1336; p = get_local_block; puts p.call; puts 1337}; puts x.call; puts 1338; rescue; puts 1339; puts $!.class; end +$g = 0; def m_121; puts 1340; x = lambda { puts 1341; p = get_local_block; puts p.call; puts 1342}; puts x.call; puts 1343; end; +begin; puts 1344; m_121; puts 1345; rescue; puts 1346; puts $!.class; end +$g = 0; begin; puts 1347; x = lambda { puts 1348; p = get_local_lambda; puts p.call; puts 1349}; puts x.call; puts 1350; rescue; puts 1351; puts $!.class; end +$g = 0; def m_122; puts 1352; x = lambda { puts 1353; p = get_local_lambda; puts p.call; puts 1354}; puts x.call; puts 1355; end; +begin; puts 1356; m_122; puts 1357; rescue; puts 1358; puts $!.class; end +$g = 0; begin; puts 1359; x = lambda { puts 1360; p = get_local_proc; puts p.call; puts 1361}; puts x.call; puts 1362; rescue; puts 1363; puts $!.class; end +$g = 0; def m_123; puts 1364; x = lambda { puts 1365; p = get_local_proc; puts p.call; puts 1366}; puts x.call; puts 1367; end; +begin; puts 1368; m_123; puts 1369; rescue; puts 1370; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1371; x = Proc.new { puts 1372; p = lambda{ puts 1373; puts "A"; puts 1374}; puts p.call; puts 1375}; puts x.call; puts 1376; rescue; puts 1377; puts $!.class; end +$g = 0; def m_124; puts 1378; x = Proc.new { puts 1379; p = lambda{ puts 1380; puts "A"; puts 1381}; puts p.call; puts 1382}; puts x.call; puts 1383; end; +begin; puts 1384; m_124; puts 1385; rescue; puts 1386; puts $!.class; end +$g = 0; begin; puts 1387; x = Proc.new { puts 1388; p = Proc.new{ puts 1389; puts "A"; puts 1390}; puts p.call; puts 1391}; puts x.call; puts 1392; rescue; puts 1393; puts $!.class; end +$g = 0; def m_125; puts 1394; x = Proc.new { puts 1395; p = Proc.new{ puts 1396; puts "A"; puts 1397}; puts p.call; puts 1398}; puts x.call; puts 1399; end; +begin; puts 1400; m_125; puts 1401; rescue; puts 1402; puts $!.class; end +$g = 0; begin; puts 1403; x = Proc.new { puts 1404; p = get_block{ puts 1405; puts "A"; puts 1406}; puts p.call; puts 1407}; puts x.call; puts 1408; rescue; puts 1409; puts $!.class; end +$g = 0; def m_126; puts 1410; x = Proc.new { puts 1411; p = get_block{ puts 1412; puts "A"; puts 1413}; puts p.call; puts 1414}; puts x.call; puts 1415; end; +begin; puts 1416; m_126; puts 1417; rescue; puts 1418; puts $!.class; end +$g = 0; begin; puts 1419; x = Proc.new { puts 1420; p = get_lambda{ puts 1421; puts "A"; puts 1422}; puts p.call; puts 1423}; puts x.call; puts 1424; rescue; puts 1425; puts $!.class; end +$g = 0; def m_127; puts 1426; x = Proc.new { puts 1427; p = get_lambda{ puts 1428; puts "A"; puts 1429}; puts p.call; puts 1430}; puts x.call; puts 1431; end; +begin; puts 1432; m_127; puts 1433; rescue; puts 1434; puts $!.class; end +$g = 0; begin; puts 1435; x = Proc.new { puts 1436; p = get_proc{ puts 1437; puts "A"; puts 1438}; puts p.call; puts 1439}; puts x.call; puts 1440; rescue; puts 1441; puts $!.class; end +$g = 0; def m_128; puts 1442; x = Proc.new { puts 1443; p = get_proc{ puts 1444; puts "A"; puts 1445}; puts p.call; puts 1446}; puts x.call; puts 1447; end; +begin; puts 1448; m_128; puts 1449; rescue; puts 1450; puts $!.class; end +$g = 0; begin; puts 1451; x = Proc.new { puts 1452; p = get_local_block; puts p.call; puts 1453}; puts x.call; puts 1454; rescue; puts 1455; puts $!.class; end +$g = 0; def m_129; puts 1456; x = Proc.new { puts 1457; p = get_local_block; puts p.call; puts 1458}; puts x.call; puts 1459; end; +begin; puts 1460; m_129; puts 1461; rescue; puts 1462; puts $!.class; end +$g = 0; begin; puts 1463; x = Proc.new { puts 1464; p = get_local_lambda; puts p.call; puts 1465}; puts x.call; puts 1466; rescue; puts 1467; puts $!.class; end +$g = 0; def m_130; puts 1468; x = Proc.new { puts 1469; p = get_local_lambda; puts p.call; puts 1470}; puts x.call; puts 1471; end; +begin; puts 1472; m_130; puts 1473; rescue; puts 1474; puts $!.class; end +$g = 0; begin; puts 1475; x = Proc.new { puts 1476; p = get_local_proc; puts p.call; puts 1477}; puts x.call; puts 1478; rescue; puts 1479; puts $!.class; end +$g = 0; def m_131; puts 1480; x = Proc.new { puts 1481; p = get_local_proc; puts p.call; puts 1482}; puts x.call; puts 1483; end; +begin; puts 1484; m_131; puts 1485; rescue; puts 1486; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1487; x = get_block { puts 1488; p = lambda{ puts 1489; puts "A"; puts 1490}; puts p.call; puts 1491}; puts x.call; puts 1492; rescue; puts 1493; puts $!.class; end +$g = 0; def m_132; puts 1494; x = get_block { puts 1495; p = lambda{ puts 1496; puts "A"; puts 1497}; puts p.call; puts 1498}; puts x.call; puts 1499; end; +begin; puts 1500; m_132; puts 1501; rescue; puts 1502; puts $!.class; end +$g = 0; begin; puts 1503; x = get_block { puts 1504; p = Proc.new{ puts 1505; puts "A"; puts 1506}; puts p.call; puts 1507}; puts x.call; puts 1508; rescue; puts 1509; puts $!.class; end +$g = 0; def m_133; puts 1510; x = get_block { puts 1511; p = Proc.new{ puts 1512; puts "A"; puts 1513}; puts p.call; puts 1514}; puts x.call; puts 1515; end; +begin; puts 1516; m_133; puts 1517; rescue; puts 1518; puts $!.class; end +$g = 0; begin; puts 1519; x = get_block { puts 1520; p = get_block{ puts 1521; puts "A"; puts 1522}; puts p.call; puts 1523}; puts x.call; puts 1524; rescue; puts 1525; puts $!.class; end +$g = 0; def m_134; puts 1526; x = get_block { puts 1527; p = get_block{ puts 1528; puts "A"; puts 1529}; puts p.call; puts 1530}; puts x.call; puts 1531; end; +begin; puts 1532; m_134; puts 1533; rescue; puts 1534; puts $!.class; end +$g = 0; begin; puts 1535; x = get_block { puts 1536; p = get_lambda{ puts 1537; puts "A"; puts 1538}; puts p.call; puts 1539}; puts x.call; puts 1540; rescue; puts 1541; puts $!.class; end +$g = 0; def m_135; puts 1542; x = get_block { puts 1543; p = get_lambda{ puts 1544; puts "A"; puts 1545}; puts p.call; puts 1546}; puts x.call; puts 1547; end; +begin; puts 1548; m_135; puts 1549; rescue; puts 1550; puts $!.class; end +$g = 0; begin; puts 1551; x = get_block { puts 1552; p = get_proc{ puts 1553; puts "A"; puts 1554}; puts p.call; puts 1555}; puts x.call; puts 1556; rescue; puts 1557; puts $!.class; end +$g = 0; def m_136; puts 1558; x = get_block { puts 1559; p = get_proc{ puts 1560; puts "A"; puts 1561}; puts p.call; puts 1562}; puts x.call; puts 1563; end; +begin; puts 1564; m_136; puts 1565; rescue; puts 1566; puts $!.class; end +$g = 0; begin; puts 1567; x = get_block { puts 1568; p = get_local_block; puts p.call; puts 1569}; puts x.call; puts 1570; rescue; puts 1571; puts $!.class; end +$g = 0; def m_137; puts 1572; x = get_block { puts 1573; p = get_local_block; puts p.call; puts 1574}; puts x.call; puts 1575; end; +begin; puts 1576; m_137; puts 1577; rescue; puts 1578; puts $!.class; end +$g = 0; begin; puts 1579; x = get_block { puts 1580; p = get_local_lambda; puts p.call; puts 1581}; puts x.call; puts 1582; rescue; puts 1583; puts $!.class; end +$g = 0; def m_138; puts 1584; x = get_block { puts 1585; p = get_local_lambda; puts p.call; puts 1586}; puts x.call; puts 1587; end; +begin; puts 1588; m_138; puts 1589; rescue; puts 1590; puts $!.class; end +$g = 0; begin; puts 1591; x = get_block { puts 1592; p = get_local_proc; puts p.call; puts 1593}; puts x.call; puts 1594; rescue; puts 1595; puts $!.class; end +$g = 0; def m_139; puts 1596; x = get_block { puts 1597; p = get_local_proc; puts p.call; puts 1598}; puts x.call; puts 1599; end; +begin; puts 1600; m_139; puts 1601; rescue; puts 1602; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1603; x = get_lambda { puts 1604; p = lambda{ puts 1605; puts "A"; puts 1606}; puts p.call; puts 1607}; puts x.call; puts 1608; rescue; puts 1609; puts $!.class; end +$g = 0; def m_140; puts 1610; x = get_lambda { puts 1611; p = lambda{ puts 1612; puts "A"; puts 1613}; puts p.call; puts 1614}; puts x.call; puts 1615; end; +begin; puts 1616; m_140; puts 1617; rescue; puts 1618; puts $!.class; end +$g = 0; begin; puts 1619; x = get_lambda { puts 1620; p = Proc.new{ puts 1621; puts "A"; puts 1622}; puts p.call; puts 1623}; puts x.call; puts 1624; rescue; puts 1625; puts $!.class; end +$g = 0; def m_141; puts 1626; x = get_lambda { puts 1627; p = Proc.new{ puts 1628; puts "A"; puts 1629}; puts p.call; puts 1630}; puts x.call; puts 1631; end; +begin; puts 1632; m_141; puts 1633; rescue; puts 1634; puts $!.class; end +$g = 0; begin; puts 1635; x = get_lambda { puts 1636; p = get_block{ puts 1637; puts "A"; puts 1638}; puts p.call; puts 1639}; puts x.call; puts 1640; rescue; puts 1641; puts $!.class; end +$g = 0; def m_142; puts 1642; x = get_lambda { puts 1643; p = get_block{ puts 1644; puts "A"; puts 1645}; puts p.call; puts 1646}; puts x.call; puts 1647; end; +begin; puts 1648; m_142; puts 1649; rescue; puts 1650; puts $!.class; end +$g = 0; begin; puts 1651; x = get_lambda { puts 1652; p = get_lambda{ puts 1653; puts "A"; puts 1654}; puts p.call; puts 1655}; puts x.call; puts 1656; rescue; puts 1657; puts $!.class; end +$g = 0; def m_143; puts 1658; x = get_lambda { puts 1659; p = get_lambda{ puts 1660; puts "A"; puts 1661}; puts p.call; puts 1662}; puts x.call; puts 1663; end; +begin; puts 1664; m_143; puts 1665; rescue; puts 1666; puts $!.class; end +$g = 0; begin; puts 1667; x = get_lambda { puts 1668; p = get_proc{ puts 1669; puts "A"; puts 1670}; puts p.call; puts 1671}; puts x.call; puts 1672; rescue; puts 1673; puts $!.class; end +$g = 0; def m_144; puts 1674; x = get_lambda { puts 1675; p = get_proc{ puts 1676; puts "A"; puts 1677}; puts p.call; puts 1678}; puts x.call; puts 1679; end; +begin; puts 1680; m_144; puts 1681; rescue; puts 1682; puts $!.class; end +$g = 0; begin; puts 1683; x = get_lambda { puts 1684; p = get_local_block; puts p.call; puts 1685}; puts x.call; puts 1686; rescue; puts 1687; puts $!.class; end +$g = 0; def m_145; puts 1688; x = get_lambda { puts 1689; p = get_local_block; puts p.call; puts 1690}; puts x.call; puts 1691; end; +begin; puts 1692; m_145; puts 1693; rescue; puts 1694; puts $!.class; end +$g = 0; begin; puts 1695; x = get_lambda { puts 1696; p = get_local_lambda; puts p.call; puts 1697}; puts x.call; puts 1698; rescue; puts 1699; puts $!.class; end +$g = 0; def m_146; puts 1700; x = get_lambda { puts 1701; p = get_local_lambda; puts p.call; puts 1702}; puts x.call; puts 1703; end; +begin; puts 1704; m_146; puts 1705; rescue; puts 1706; puts $!.class; end +$g = 0; begin; puts 1707; x = get_lambda { puts 1708; p = get_local_proc; puts p.call; puts 1709}; puts x.call; puts 1710; rescue; puts 1711; puts $!.class; end +$g = 0; def m_147; puts 1712; x = get_lambda { puts 1713; p = get_local_proc; puts p.call; puts 1714}; puts x.call; puts 1715; end; +begin; puts 1716; m_147; puts 1717; rescue; puts 1718; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1719; x = get_proc { puts 1720; p = lambda{ puts 1721; puts "A"; puts 1722}; puts p.call; puts 1723}; puts x.call; puts 1724; rescue; puts 1725; puts $!.class; end +$g = 0; def m_148; puts 1726; x = get_proc { puts 1727; p = lambda{ puts 1728; puts "A"; puts 1729}; puts p.call; puts 1730}; puts x.call; puts 1731; end; +begin; puts 1732; m_148; puts 1733; rescue; puts 1734; puts $!.class; end +$g = 0; begin; puts 1735; x = get_proc { puts 1736; p = Proc.new{ puts 1737; puts "A"; puts 1738}; puts p.call; puts 1739}; puts x.call; puts 1740; rescue; puts 1741; puts $!.class; end +$g = 0; def m_149; puts 1742; x = get_proc { puts 1743; p = Proc.new{ puts 1744; puts "A"; puts 1745}; puts p.call; puts 1746}; puts x.call; puts 1747; end; +begin; puts 1748; m_149; puts 1749; rescue; puts 1750; puts $!.class; end +$g = 0; begin; puts 1751; x = get_proc { puts 1752; p = get_block{ puts 1753; puts "A"; puts 1754}; puts p.call; puts 1755}; puts x.call; puts 1756; rescue; puts 1757; puts $!.class; end +$g = 0; def m_150; puts 1758; x = get_proc { puts 1759; p = get_block{ puts 1760; puts "A"; puts 1761}; puts p.call; puts 1762}; puts x.call; puts 1763; end; +begin; puts 1764; m_150; puts 1765; rescue; puts 1766; puts $!.class; end +$g = 0; begin; puts 1767; x = get_proc { puts 1768; p = get_lambda{ puts 1769; puts "A"; puts 1770}; puts p.call; puts 1771}; puts x.call; puts 1772; rescue; puts 1773; puts $!.class; end +$g = 0; def m_151; puts 1774; x = get_proc { puts 1775; p = get_lambda{ puts 1776; puts "A"; puts 1777}; puts p.call; puts 1778}; puts x.call; puts 1779; end; +begin; puts 1780; m_151; puts 1781; rescue; puts 1782; puts $!.class; end +$g = 0; begin; puts 1783; x = get_proc { puts 1784; p = get_proc{ puts 1785; puts "A"; puts 1786}; puts p.call; puts 1787}; puts x.call; puts 1788; rescue; puts 1789; puts $!.class; end +$g = 0; def m_152; puts 1790; x = get_proc { puts 1791; p = get_proc{ puts 1792; puts "A"; puts 1793}; puts p.call; puts 1794}; puts x.call; puts 1795; end; +begin; puts 1796; m_152; puts 1797; rescue; puts 1798; puts $!.class; end +$g = 0; begin; puts 1799; x = get_proc { puts 1800; p = get_local_block; puts p.call; puts 1801}; puts x.call; puts 1802; rescue; puts 1803; puts $!.class; end +$g = 0; def m_153; puts 1804; x = get_proc { puts 1805; p = get_local_block; puts p.call; puts 1806}; puts x.call; puts 1807; end; +begin; puts 1808; m_153; puts 1809; rescue; puts 1810; puts $!.class; end +$g = 0; begin; puts 1811; x = get_proc { puts 1812; p = get_local_lambda; puts p.call; puts 1813}; puts x.call; puts 1814; rescue; puts 1815; puts $!.class; end +$g = 0; def m_154; puts 1816; x = get_proc { puts 1817; p = get_local_lambda; puts p.call; puts 1818}; puts x.call; puts 1819; end; +begin; puts 1820; m_154; puts 1821; rescue; puts 1822; puts $!.class; end +$g = 0; begin; puts 1823; x = get_proc { puts 1824; p = get_local_proc; puts p.call; puts 1825}; puts x.call; puts 1826; rescue; puts 1827; puts $!.class; end +$g = 0; def m_155; puts 1828; x = get_proc { puts 1829; p = get_local_proc; puts p.call; puts 1830}; puts x.call; puts 1831; end; +begin; puts 1832; m_155; puts 1833; rescue; puts 1834; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1835; for i in [1, 2]; puts 1836; p = lambda{ puts 1837; puts "A"; puts 1838}; puts p.call; puts 1839; end; puts 1840; rescue; puts 1841; puts $!.class; end +$g = 0; def m_156; puts 1842; for i in [1, 2]; puts 1843; p = lambda{ puts 1844; puts "A"; puts 1845}; puts p.call; puts 1846; end; puts 1847; end; +begin; puts 1848; m_156; puts 1849; rescue; puts 1850; puts $!.class; end +$g = 0; begin; puts 1851; for i in [1, 2]; puts 1852; p = Proc.new{ puts 1853; puts "A"; puts 1854}; puts p.call; puts 1855; end; puts 1856; rescue; puts 1857; puts $!.class; end +$g = 0; def m_157; puts 1858; for i in [1, 2]; puts 1859; p = Proc.new{ puts 1860; puts "A"; puts 1861}; puts p.call; puts 1862; end; puts 1863; end; +begin; puts 1864; m_157; puts 1865; rescue; puts 1866; puts $!.class; end +$g = 0; begin; puts 1867; for i in [1, 2]; puts 1868; p = get_block{ puts 1869; puts "A"; puts 1870}; puts p.call; puts 1871; end; puts 1872; rescue; puts 1873; puts $!.class; end +$g = 0; def m_158; puts 1874; for i in [1, 2]; puts 1875; p = get_block{ puts 1876; puts "A"; puts 1877}; puts p.call; puts 1878; end; puts 1879; end; +begin; puts 1880; m_158; puts 1881; rescue; puts 1882; puts $!.class; end +$g = 0; begin; puts 1883; for i in [1, 2]; puts 1884; p = get_lambda{ puts 1885; puts "A"; puts 1886}; puts p.call; puts 1887; end; puts 1888; rescue; puts 1889; puts $!.class; end +$g = 0; def m_159; puts 1890; for i in [1, 2]; puts 1891; p = get_lambda{ puts 1892; puts "A"; puts 1893}; puts p.call; puts 1894; end; puts 1895; end; +begin; puts 1896; m_159; puts 1897; rescue; puts 1898; puts $!.class; end +$g = 0; begin; puts 1899; for i in [1, 2]; puts 1900; p = get_proc{ puts 1901; puts "A"; puts 1902}; puts p.call; puts 1903; end; puts 1904; rescue; puts 1905; puts $!.class; end +$g = 0; def m_160; puts 1906; for i in [1, 2]; puts 1907; p = get_proc{ puts 1908; puts "A"; puts 1909}; puts p.call; puts 1910; end; puts 1911; end; +begin; puts 1912; m_160; puts 1913; rescue; puts 1914; puts $!.class; end +$g = 0; begin; puts 1915; for i in [1, 2]; puts 1916; p = get_local_block; puts p.call; puts 1917; end; puts 1918; rescue; puts 1919; puts $!.class; end +$g = 0; def m_161; puts 1920; for i in [1, 2]; puts 1921; p = get_local_block; puts p.call; puts 1922; end; puts 1923; end; +begin; puts 1924; m_161; puts 1925; rescue; puts 1926; puts $!.class; end +$g = 0; begin; puts 1927; for i in [1, 2]; puts 1928; p = get_local_lambda; puts p.call; puts 1929; end; puts 1930; rescue; puts 1931; puts $!.class; end +$g = 0; def m_162; puts 1932; for i in [1, 2]; puts 1933; p = get_local_lambda; puts p.call; puts 1934; end; puts 1935; end; +begin; puts 1936; m_162; puts 1937; rescue; puts 1938; puts $!.class; end +$g = 0; begin; puts 1939; for i in [1, 2]; puts 1940; p = get_local_proc; puts p.call; puts 1941; end; puts 1942; rescue; puts 1943; puts $!.class; end +$g = 0; def m_163; puts 1944; for i in [1, 2]; puts 1945; p = get_local_proc; puts p.call; puts 1946; end; puts 1947; end; +begin; puts 1948; m_163; puts 1949; rescue; puts 1950; puts $!.class; end +end +test diff --git a/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_raise.rb b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_raise.rb new file mode 100644 index 0000000000..24d14c8179 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_raise.rb @@ -0,0 +1,595 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +# helper +def myeval(line); puts 1; eval(line); puts 2; end +def call1(x); puts 3; call2(x); puts 4; end +def call2(x); puts 5; call3(x); puts 6; end +def call3(x); puts 7; puts x.call; puts 8; end + +# producer + +def get_block(&p); p; end +def get_lambda(&p); lambda(&p); end +def get_proc(&p); Proc.new(&p); end + +def get_local_block; get_block { puts 9; raise IOError; puts 10 }; end +def get_local_lambda; lambda { puts 11; raise IOError; puts 12 }; end +def get_local_proc; Proc.new { puts 13; raise IOError; puts 14 }; end + +# consumer + +# taking arguments +def iterator_via_yield; puts 15; x = yield; puts x; puts 16; end +def iterator_via_call(&p); puts 17; puts(p.call); puts 18; end + +def method_call_iterator_via_yield(&p); puts 19; iterator_via_yield(&p); puts 20; end +def method_call_iterator_via_call(&p); puts 21; iterator_via_call(&p); puts 22; end + +def method_use_lambda_and_yield; puts 23; x = lambda { puts 24; yield; puts 25 }; puts x.call; puts 26; end +def method_use_proc_and_yield; puts 27; x = Proc.new { puts 28; yield; puts 29 }; puts x.call; puts 30; end +def method_use_lambda_and_call(&p); puts 31; x = lambda { puts 32; p.call; puts 33 }; puts x.call; puts 34; end +def method_use_proc_and_call(&p); puts 35; x = Proc.new { puts 36; p.call; puts 37 }; puts x.call; puts 38; end + +def method_use_lambda_and_yield_2; puts 39; x = lambda { puts 40; yield; puts 41 }; call1(x); puts 42; end + +def method_yield_in_loop; puts 43; for i in [1, 2]; puts 44; yield; puts 45; end; puts 46; end +def method_call_in_loop(&p); puts 47; for i in [3, 4]; puts 48; p.call; puts 49; end; puts 50; end + +# created in-place +def test +$g = 0; begin; puts 51; iterator_via_yield { puts 52; raise IOError; puts 53}; puts 54; rescue; puts 55; puts $!.class; end +$g = 0; def m_1; puts 56; $g = 0; begin; puts 57; iterator_via_yield { puts 58; raise IOError; puts 59}; puts 60; rescue; puts 61; puts $!.class; end; puts 62; end; m_1 +$g = 0; begin; puts 63; iterator_via_call { puts 64; raise IOError; puts 65}; puts 66; rescue; puts 67; puts $!.class; end +$g = 0; def m_2; puts 68; $g = 0; begin; puts 69; iterator_via_call { puts 70; raise IOError; puts 71}; puts 72; rescue; puts 73; puts $!.class; end; puts 74; end; m_2 +$g = 0; begin; puts 75; method_call_iterator_via_yield { puts 76; raise IOError; puts 77}; puts 78; rescue; puts 79; puts $!.class; end +$g = 0; def m_3; puts 80; $g = 0; begin; puts 81; method_call_iterator_via_yield { puts 82; raise IOError; puts 83}; puts 84; rescue; puts 85; puts $!.class; end; puts 86; end; m_3 +$g = 0; begin; puts 87; method_call_iterator_via_call { puts 88; raise IOError; puts 89}; puts 90; rescue; puts 91; puts $!.class; end +$g = 0; def m_4; puts 92; $g = 0; begin; puts 93; method_call_iterator_via_call { puts 94; raise IOError; puts 95}; puts 96; rescue; puts 97; puts $!.class; end; puts 98; end; m_4 +$g = 0; begin; puts 99; method_use_lambda_and_yield { puts 100; raise IOError; puts 101}; puts 102; rescue; puts 103; puts $!.class; end +$g = 0; def m_5; puts 104; $g = 0; begin; puts 105; method_use_lambda_and_yield { puts 106; raise IOError; puts 107}; puts 108; rescue; puts 109; puts $!.class; end; puts 110; end; m_5 +$g = 0; begin; puts 111; method_use_proc_and_yield { puts 112; raise IOError; puts 113}; puts 114; rescue; puts 115; puts $!.class; end +$g = 0; def m_6; puts 116; $g = 0; begin; puts 117; method_use_proc_and_yield { puts 118; raise IOError; puts 119}; puts 120; rescue; puts 121; puts $!.class; end; puts 122; end; m_6 +$g = 0; begin; puts 123; method_use_lambda_and_call { puts 124; raise IOError; puts 125}; puts 126; rescue; puts 127; puts $!.class; end +$g = 0; def m_7; puts 128; $g = 0; begin; puts 129; method_use_lambda_and_call { puts 130; raise IOError; puts 131}; puts 132; rescue; puts 133; puts $!.class; end; puts 134; end; m_7 +$g = 0; begin; puts 135; method_use_proc_and_call { puts 136; raise IOError; puts 137}; puts 138; rescue; puts 139; puts $!.class; end +$g = 0; def m_8; puts 140; $g = 0; begin; puts 141; method_use_proc_and_call { puts 142; raise IOError; puts 143}; puts 144; rescue; puts 145; puts $!.class; end; puts 146; end; m_8 +$g = 0; begin; puts 147; method_use_lambda_and_yield_2 { puts 148; raise IOError; puts 149}; puts 150; rescue; puts 151; puts $!.class; end +$g = 0; def m_9; puts 152; $g = 0; begin; puts 153; method_use_lambda_and_yield_2 { puts 154; raise IOError; puts 155}; puts 156; rescue; puts 157; puts $!.class; end; puts 158; end; m_9 +$g = 0; begin; puts 159; method_yield_in_loop { puts 160; raise IOError; puts 161}; puts 162; rescue; puts 163; puts $!.class; end +$g = 0; def m_10; puts 164; $g = 0; begin; puts 165; method_yield_in_loop { puts 166; raise IOError; puts 167}; puts 168; rescue; puts 169; puts $!.class; end; puts 170; end; m_10 +$g = 0; begin; puts 171; method_call_in_loop { puts 172; raise IOError; puts 173}; puts 174; rescue; puts 175; puts $!.class; end +$g = 0; def m_11; puts 176; $g = 0; begin; puts 177; method_call_in_loop { puts 178; raise IOError; puts 179}; puts 180; rescue; puts 181; puts $!.class; end; puts 182; end; m_11 +end +test + + +# created locally or from method +def test +$g = 0; begin; p = lambda{ puts 183; raise IOError; puts 184}; puts 185; iterator_via_yield(&p); puts 186; rescue; puts 187; puts $!.class; end +$g = 0; def m_12; p = lambda{ puts 188; raise IOError; puts 189}; puts 190; iterator_via_yield(&p); puts 191; end; +begin; puts 192; m_12; puts 193; rescue; puts 194; puts $!.class; end +$g = 0; begin; p = lambda{ puts 195; raise IOError; puts 196}; puts 197; iterator_via_call(&p); puts 198; rescue; puts 199; puts $!.class; end +$g = 0; def m_13; p = lambda{ puts 200; raise IOError; puts 201}; puts 202; iterator_via_call(&p); puts 203; end; +begin; puts 204; m_13; puts 205; rescue; puts 206; puts $!.class; end +$g = 0; begin; p = lambda{ puts 207; raise IOError; puts 208}; puts 209; method_call_iterator_via_yield(&p); puts 210; rescue; puts 211; puts $!.class; end +$g = 0; def m_14; p = lambda{ puts 212; raise IOError; puts 213}; puts 214; method_call_iterator_via_yield(&p); puts 215; end; +begin; puts 216; m_14; puts 217; rescue; puts 218; puts $!.class; end +$g = 0; begin; p = lambda{ puts 219; raise IOError; puts 220}; puts 221; method_call_iterator_via_call(&p); puts 222; rescue; puts 223; puts $!.class; end +$g = 0; def m_15; p = lambda{ puts 224; raise IOError; puts 225}; puts 226; method_call_iterator_via_call(&p); puts 227; end; +begin; puts 228; m_15; puts 229; rescue; puts 230; puts $!.class; end +$g = 0; begin; p = lambda{ puts 231; raise IOError; puts 232}; puts 233; method_use_lambda_and_yield(&p); puts 234; rescue; puts 235; puts $!.class; end +$g = 0; def m_16; p = lambda{ puts 236; raise IOError; puts 237}; puts 238; method_use_lambda_and_yield(&p); puts 239; end; +begin; puts 240; m_16; puts 241; rescue; puts 242; puts $!.class; end +$g = 0; begin; p = lambda{ puts 243; raise IOError; puts 244}; puts 245; method_use_proc_and_yield(&p); puts 246; rescue; puts 247; puts $!.class; end +$g = 0; def m_17; p = lambda{ puts 248; raise IOError; puts 249}; puts 250; method_use_proc_and_yield(&p); puts 251; end; +begin; puts 252; m_17; puts 253; rescue; puts 254; puts $!.class; end +$g = 0; begin; p = lambda{ puts 255; raise IOError; puts 256}; puts 257; method_use_lambda_and_call(&p); puts 258; rescue; puts 259; puts $!.class; end +$g = 0; def m_18; p = lambda{ puts 260; raise IOError; puts 261}; puts 262; method_use_lambda_and_call(&p); puts 263; end; +begin; puts 264; m_18; puts 265; rescue; puts 266; puts $!.class; end +$g = 0; begin; p = lambda{ puts 267; raise IOError; puts 268}; puts 269; method_use_proc_and_call(&p); puts 270; rescue; puts 271; puts $!.class; end +$g = 0; def m_19; p = lambda{ puts 272; raise IOError; puts 273}; puts 274; method_use_proc_and_call(&p); puts 275; end; +begin; puts 276; m_19; puts 277; rescue; puts 278; puts $!.class; end +$g = 0; begin; p = lambda{ puts 279; raise IOError; puts 280}; puts 281; method_use_lambda_and_yield_2(&p); puts 282; rescue; puts 283; puts $!.class; end +$g = 0; def m_20; p = lambda{ puts 284; raise IOError; puts 285}; puts 286; method_use_lambda_and_yield_2(&p); puts 287; end; +begin; puts 288; m_20; puts 289; rescue; puts 290; puts $!.class; end +$g = 0; begin; p = lambda{ puts 291; raise IOError; puts 292}; puts 293; method_yield_in_loop(&p); puts 294; rescue; puts 295; puts $!.class; end +$g = 0; def m_21; p = lambda{ puts 296; raise IOError; puts 297}; puts 298; method_yield_in_loop(&p); puts 299; end; +begin; puts 300; m_21; puts 301; rescue; puts 302; puts $!.class; end +$g = 0; begin; p = lambda{ puts 303; raise IOError; puts 304}; puts 305; method_call_in_loop(&p); puts 306; rescue; puts 307; puts $!.class; end +$g = 0; def m_22; p = lambda{ puts 308; raise IOError; puts 309}; puts 310; method_call_in_loop(&p); puts 311; end; +begin; puts 312; m_22; puts 313; rescue; puts 314; puts $!.class; end +end +test + +def test +$g = 0; begin; p = Proc.new{ puts 315; raise IOError; puts 316}; puts 317; iterator_via_yield(&p); puts 318; rescue; puts 319; puts $!.class; end +$g = 0; def m_23; p = Proc.new{ puts 320; raise IOError; puts 321}; puts 322; iterator_via_yield(&p); puts 323; end; +begin; puts 324; m_23; puts 325; rescue; puts 326; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 327; raise IOError; puts 328}; puts 329; iterator_via_call(&p); puts 330; rescue; puts 331; puts $!.class; end +$g = 0; def m_24; p = Proc.new{ puts 332; raise IOError; puts 333}; puts 334; iterator_via_call(&p); puts 335; end; +begin; puts 336; m_24; puts 337; rescue; puts 338; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 339; raise IOError; puts 340}; puts 341; method_call_iterator_via_yield(&p); puts 342; rescue; puts 343; puts $!.class; end +$g = 0; def m_25; p = Proc.new{ puts 344; raise IOError; puts 345}; puts 346; method_call_iterator_via_yield(&p); puts 347; end; +begin; puts 348; m_25; puts 349; rescue; puts 350; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 351; raise IOError; puts 352}; puts 353; method_call_iterator_via_call(&p); puts 354; rescue; puts 355; puts $!.class; end +$g = 0; def m_26; p = Proc.new{ puts 356; raise IOError; puts 357}; puts 358; method_call_iterator_via_call(&p); puts 359; end; +begin; puts 360; m_26; puts 361; rescue; puts 362; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 363; raise IOError; puts 364}; puts 365; method_use_lambda_and_yield(&p); puts 366; rescue; puts 367; puts $!.class; end +$g = 0; def m_27; p = Proc.new{ puts 368; raise IOError; puts 369}; puts 370; method_use_lambda_and_yield(&p); puts 371; end; +begin; puts 372; m_27; puts 373; rescue; puts 374; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 375; raise IOError; puts 376}; puts 377; method_use_proc_and_yield(&p); puts 378; rescue; puts 379; puts $!.class; end +$g = 0; def m_28; p = Proc.new{ puts 380; raise IOError; puts 381}; puts 382; method_use_proc_and_yield(&p); puts 383; end; +begin; puts 384; m_28; puts 385; rescue; puts 386; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 387; raise IOError; puts 388}; puts 389; method_use_lambda_and_call(&p); puts 390; rescue; puts 391; puts $!.class; end +$g = 0; def m_29; p = Proc.new{ puts 392; raise IOError; puts 393}; puts 394; method_use_lambda_and_call(&p); puts 395; end; +begin; puts 396; m_29; puts 397; rescue; puts 398; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 399; raise IOError; puts 400}; puts 401; method_use_proc_and_call(&p); puts 402; rescue; puts 403; puts $!.class; end +$g = 0; def m_30; p = Proc.new{ puts 404; raise IOError; puts 405}; puts 406; method_use_proc_and_call(&p); puts 407; end; +begin; puts 408; m_30; puts 409; rescue; puts 410; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 411; raise IOError; puts 412}; puts 413; method_use_lambda_and_yield_2(&p); puts 414; rescue; puts 415; puts $!.class; end +$g = 0; def m_31; p = Proc.new{ puts 416; raise IOError; puts 417}; puts 418; method_use_lambda_and_yield_2(&p); puts 419; end; +begin; puts 420; m_31; puts 421; rescue; puts 422; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 423; raise IOError; puts 424}; puts 425; method_yield_in_loop(&p); puts 426; rescue; puts 427; puts $!.class; end +$g = 0; def m_32; p = Proc.new{ puts 428; raise IOError; puts 429}; puts 430; method_yield_in_loop(&p); puts 431; end; +begin; puts 432; m_32; puts 433; rescue; puts 434; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 435; raise IOError; puts 436}; puts 437; method_call_in_loop(&p); puts 438; rescue; puts 439; puts $!.class; end +$g = 0; def m_33; p = Proc.new{ puts 440; raise IOError; puts 441}; puts 442; method_call_in_loop(&p); puts 443; end; +begin; puts 444; m_33; puts 445; rescue; puts 446; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_block{ puts 447; raise IOError; puts 448}; puts 449; iterator_via_yield(&p); puts 450; rescue; puts 451; puts $!.class; end +$g = 0; def m_34; p = get_block{ puts 452; raise IOError; puts 453}; puts 454; iterator_via_yield(&p); puts 455; end; +begin; puts 456; m_34; puts 457; rescue; puts 458; puts $!.class; end +$g = 0; begin; p = get_block{ puts 459; raise IOError; puts 460}; puts 461; iterator_via_call(&p); puts 462; rescue; puts 463; puts $!.class; end +$g = 0; def m_35; p = get_block{ puts 464; raise IOError; puts 465}; puts 466; iterator_via_call(&p); puts 467; end; +begin; puts 468; m_35; puts 469; rescue; puts 470; puts $!.class; end +$g = 0; begin; p = get_block{ puts 471; raise IOError; puts 472}; puts 473; method_call_iterator_via_yield(&p); puts 474; rescue; puts 475; puts $!.class; end +$g = 0; def m_36; p = get_block{ puts 476; raise IOError; puts 477}; puts 478; method_call_iterator_via_yield(&p); puts 479; end; +begin; puts 480; m_36; puts 481; rescue; puts 482; puts $!.class; end +$g = 0; begin; p = get_block{ puts 483; raise IOError; puts 484}; puts 485; method_call_iterator_via_call(&p); puts 486; rescue; puts 487; puts $!.class; end +$g = 0; def m_37; p = get_block{ puts 488; raise IOError; puts 489}; puts 490; method_call_iterator_via_call(&p); puts 491; end; +begin; puts 492; m_37; puts 493; rescue; puts 494; puts $!.class; end +$g = 0; begin; p = get_block{ puts 495; raise IOError; puts 496}; puts 497; method_use_lambda_and_yield(&p); puts 498; rescue; puts 499; puts $!.class; end +$g = 0; def m_38; p = get_block{ puts 500; raise IOError; puts 501}; puts 502; method_use_lambda_and_yield(&p); puts 503; end; +begin; puts 504; m_38; puts 505; rescue; puts 506; puts $!.class; end +$g = 0; begin; p = get_block{ puts 507; raise IOError; puts 508}; puts 509; method_use_proc_and_yield(&p); puts 510; rescue; puts 511; puts $!.class; end +$g = 0; def m_39; p = get_block{ puts 512; raise IOError; puts 513}; puts 514; method_use_proc_and_yield(&p); puts 515; end; +begin; puts 516; m_39; puts 517; rescue; puts 518; puts $!.class; end +$g = 0; begin; p = get_block{ puts 519; raise IOError; puts 520}; puts 521; method_use_lambda_and_call(&p); puts 522; rescue; puts 523; puts $!.class; end +$g = 0; def m_40; p = get_block{ puts 524; raise IOError; puts 525}; puts 526; method_use_lambda_and_call(&p); puts 527; end; +begin; puts 528; m_40; puts 529; rescue; puts 530; puts $!.class; end +$g = 0; begin; p = get_block{ puts 531; raise IOError; puts 532}; puts 533; method_use_proc_and_call(&p); puts 534; rescue; puts 535; puts $!.class; end +$g = 0; def m_41; p = get_block{ puts 536; raise IOError; puts 537}; puts 538; method_use_proc_and_call(&p); puts 539; end; +begin; puts 540; m_41; puts 541; rescue; puts 542; puts $!.class; end +$g = 0; begin; p = get_block{ puts 543; raise IOError; puts 544}; puts 545; method_use_lambda_and_yield_2(&p); puts 546; rescue; puts 547; puts $!.class; end +$g = 0; def m_42; p = get_block{ puts 548; raise IOError; puts 549}; puts 550; method_use_lambda_and_yield_2(&p); puts 551; end; +begin; puts 552; m_42; puts 553; rescue; puts 554; puts $!.class; end +$g = 0; begin; p = get_block{ puts 555; raise IOError; puts 556}; puts 557; method_yield_in_loop(&p); puts 558; rescue; puts 559; puts $!.class; end +$g = 0; def m_43; p = get_block{ puts 560; raise IOError; puts 561}; puts 562; method_yield_in_loop(&p); puts 563; end; +begin; puts 564; m_43; puts 565; rescue; puts 566; puts $!.class; end +$g = 0; begin; p = get_block{ puts 567; raise IOError; puts 568}; puts 569; method_call_in_loop(&p); puts 570; rescue; puts 571; puts $!.class; end +$g = 0; def m_44; p = get_block{ puts 572; raise IOError; puts 573}; puts 574; method_call_in_loop(&p); puts 575; end; +begin; puts 576; m_44; puts 577; rescue; puts 578; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_lambda{ puts 579; raise IOError; puts 580}; puts 581; iterator_via_yield(&p); puts 582; rescue; puts 583; puts $!.class; end +$g = 0; def m_45; p = get_lambda{ puts 584; raise IOError; puts 585}; puts 586; iterator_via_yield(&p); puts 587; end; +begin; puts 588; m_45; puts 589; rescue; puts 590; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 591; raise IOError; puts 592}; puts 593; iterator_via_call(&p); puts 594; rescue; puts 595; puts $!.class; end +$g = 0; def m_46; p = get_lambda{ puts 596; raise IOError; puts 597}; puts 598; iterator_via_call(&p); puts 599; end; +begin; puts 600; m_46; puts 601; rescue; puts 602; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 603; raise IOError; puts 604}; puts 605; method_call_iterator_via_yield(&p); puts 606; rescue; puts 607; puts $!.class; end +$g = 0; def m_47; p = get_lambda{ puts 608; raise IOError; puts 609}; puts 610; method_call_iterator_via_yield(&p); puts 611; end; +begin; puts 612; m_47; puts 613; rescue; puts 614; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 615; raise IOError; puts 616}; puts 617; method_call_iterator_via_call(&p); puts 618; rescue; puts 619; puts $!.class; end +$g = 0; def m_48; p = get_lambda{ puts 620; raise IOError; puts 621}; puts 622; method_call_iterator_via_call(&p); puts 623; end; +begin; puts 624; m_48; puts 625; rescue; puts 626; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 627; raise IOError; puts 628}; puts 629; method_use_lambda_and_yield(&p); puts 630; rescue; puts 631; puts $!.class; end +$g = 0; def m_49; p = get_lambda{ puts 632; raise IOError; puts 633}; puts 634; method_use_lambda_and_yield(&p); puts 635; end; +begin; puts 636; m_49; puts 637; rescue; puts 638; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 639; raise IOError; puts 640}; puts 641; method_use_proc_and_yield(&p); puts 642; rescue; puts 643; puts $!.class; end +$g = 0; def m_50; p = get_lambda{ puts 644; raise IOError; puts 645}; puts 646; method_use_proc_and_yield(&p); puts 647; end; +begin; puts 648; m_50; puts 649; rescue; puts 650; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 651; raise IOError; puts 652}; puts 653; method_use_lambda_and_call(&p); puts 654; rescue; puts 655; puts $!.class; end +$g = 0; def m_51; p = get_lambda{ puts 656; raise IOError; puts 657}; puts 658; method_use_lambda_and_call(&p); puts 659; end; +begin; puts 660; m_51; puts 661; rescue; puts 662; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 663; raise IOError; puts 664}; puts 665; method_use_proc_and_call(&p); puts 666; rescue; puts 667; puts $!.class; end +$g = 0; def m_52; p = get_lambda{ puts 668; raise IOError; puts 669}; puts 670; method_use_proc_and_call(&p); puts 671; end; +begin; puts 672; m_52; puts 673; rescue; puts 674; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 675; raise IOError; puts 676}; puts 677; method_use_lambda_and_yield_2(&p); puts 678; rescue; puts 679; puts $!.class; end +$g = 0; def m_53; p = get_lambda{ puts 680; raise IOError; puts 681}; puts 682; method_use_lambda_and_yield_2(&p); puts 683; end; +begin; puts 684; m_53; puts 685; rescue; puts 686; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 687; raise IOError; puts 688}; puts 689; method_yield_in_loop(&p); puts 690; rescue; puts 691; puts $!.class; end +$g = 0; def m_54; p = get_lambda{ puts 692; raise IOError; puts 693}; puts 694; method_yield_in_loop(&p); puts 695; end; +begin; puts 696; m_54; puts 697; rescue; puts 698; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 699; raise IOError; puts 700}; puts 701; method_call_in_loop(&p); puts 702; rescue; puts 703; puts $!.class; end +$g = 0; def m_55; p = get_lambda{ puts 704; raise IOError; puts 705}; puts 706; method_call_in_loop(&p); puts 707; end; +begin; puts 708; m_55; puts 709; rescue; puts 710; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_proc{ puts 711; raise IOError; puts 712}; puts 713; iterator_via_yield(&p); puts 714; rescue; puts 715; puts $!.class; end +$g = 0; def m_56; p = get_proc{ puts 716; raise IOError; puts 717}; puts 718; iterator_via_yield(&p); puts 719; end; +begin; puts 720; m_56; puts 721; rescue; puts 722; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 723; raise IOError; puts 724}; puts 725; iterator_via_call(&p); puts 726; rescue; puts 727; puts $!.class; end +$g = 0; def m_57; p = get_proc{ puts 728; raise IOError; puts 729}; puts 730; iterator_via_call(&p); puts 731; end; +begin; puts 732; m_57; puts 733; rescue; puts 734; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 735; raise IOError; puts 736}; puts 737; method_call_iterator_via_yield(&p); puts 738; rescue; puts 739; puts $!.class; end +$g = 0; def m_58; p = get_proc{ puts 740; raise IOError; puts 741}; puts 742; method_call_iterator_via_yield(&p); puts 743; end; +begin; puts 744; m_58; puts 745; rescue; puts 746; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 747; raise IOError; puts 748}; puts 749; method_call_iterator_via_call(&p); puts 750; rescue; puts 751; puts $!.class; end +$g = 0; def m_59; p = get_proc{ puts 752; raise IOError; puts 753}; puts 754; method_call_iterator_via_call(&p); puts 755; end; +begin; puts 756; m_59; puts 757; rescue; puts 758; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 759; raise IOError; puts 760}; puts 761; method_use_lambda_and_yield(&p); puts 762; rescue; puts 763; puts $!.class; end +$g = 0; def m_60; p = get_proc{ puts 764; raise IOError; puts 765}; puts 766; method_use_lambda_and_yield(&p); puts 767; end; +begin; puts 768; m_60; puts 769; rescue; puts 770; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 771; raise IOError; puts 772}; puts 773; method_use_proc_and_yield(&p); puts 774; rescue; puts 775; puts $!.class; end +$g = 0; def m_61; p = get_proc{ puts 776; raise IOError; puts 777}; puts 778; method_use_proc_and_yield(&p); puts 779; end; +begin; puts 780; m_61; puts 781; rescue; puts 782; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 783; raise IOError; puts 784}; puts 785; method_use_lambda_and_call(&p); puts 786; rescue; puts 787; puts $!.class; end +$g = 0; def m_62; p = get_proc{ puts 788; raise IOError; puts 789}; puts 790; method_use_lambda_and_call(&p); puts 791; end; +begin; puts 792; m_62; puts 793; rescue; puts 794; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 795; raise IOError; puts 796}; puts 797; method_use_proc_and_call(&p); puts 798; rescue; puts 799; puts $!.class; end +$g = 0; def m_63; p = get_proc{ puts 800; raise IOError; puts 801}; puts 802; method_use_proc_and_call(&p); puts 803; end; +begin; puts 804; m_63; puts 805; rescue; puts 806; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 807; raise IOError; puts 808}; puts 809; method_use_lambda_and_yield_2(&p); puts 810; rescue; puts 811; puts $!.class; end +$g = 0; def m_64; p = get_proc{ puts 812; raise IOError; puts 813}; puts 814; method_use_lambda_and_yield_2(&p); puts 815; end; +begin; puts 816; m_64; puts 817; rescue; puts 818; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 819; raise IOError; puts 820}; puts 821; method_yield_in_loop(&p); puts 822; rescue; puts 823; puts $!.class; end +$g = 0; def m_65; p = get_proc{ puts 824; raise IOError; puts 825}; puts 826; method_yield_in_loop(&p); puts 827; end; +begin; puts 828; m_65; puts 829; rescue; puts 830; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 831; raise IOError; puts 832}; puts 833; method_call_in_loop(&p); puts 834; rescue; puts 835; puts $!.class; end +$g = 0; def m_66; p = get_proc{ puts 836; raise IOError; puts 837}; puts 838; method_call_in_loop(&p); puts 839; end; +begin; puts 840; m_66; puts 841; rescue; puts 842; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_block; puts 843; iterator_via_yield(&p); puts 844; rescue; puts 845; puts $!.class; end +$g = 0; def m_67; p = get_local_block; puts 846; iterator_via_yield(&p); puts 847; end; +begin; puts 848; m_67; puts 849; rescue; puts 850; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 851; iterator_via_call(&p); puts 852; rescue; puts 853; puts $!.class; end +$g = 0; def m_68; p = get_local_block; puts 854; iterator_via_call(&p); puts 855; end; +begin; puts 856; m_68; puts 857; rescue; puts 858; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 859; method_call_iterator_via_yield(&p); puts 860; rescue; puts 861; puts $!.class; end +$g = 0; def m_69; p = get_local_block; puts 862; method_call_iterator_via_yield(&p); puts 863; end; +begin; puts 864; m_69; puts 865; rescue; puts 866; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 867; method_call_iterator_via_call(&p); puts 868; rescue; puts 869; puts $!.class; end +$g = 0; def m_70; p = get_local_block; puts 870; method_call_iterator_via_call(&p); puts 871; end; +begin; puts 872; m_70; puts 873; rescue; puts 874; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 875; method_use_lambda_and_yield(&p); puts 876; rescue; puts 877; puts $!.class; end +$g = 0; def m_71; p = get_local_block; puts 878; method_use_lambda_and_yield(&p); puts 879; end; +begin; puts 880; m_71; puts 881; rescue; puts 882; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 883; method_use_proc_and_yield(&p); puts 884; rescue; puts 885; puts $!.class; end +$g = 0; def m_72; p = get_local_block; puts 886; method_use_proc_and_yield(&p); puts 887; end; +begin; puts 888; m_72; puts 889; rescue; puts 890; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 891; method_use_lambda_and_call(&p); puts 892; rescue; puts 893; puts $!.class; end +$g = 0; def m_73; p = get_local_block; puts 894; method_use_lambda_and_call(&p); puts 895; end; +begin; puts 896; m_73; puts 897; rescue; puts 898; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 899; method_use_proc_and_call(&p); puts 900; rescue; puts 901; puts $!.class; end +$g = 0; def m_74; p = get_local_block; puts 902; method_use_proc_and_call(&p); puts 903; end; +begin; puts 904; m_74; puts 905; rescue; puts 906; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 907; method_use_lambda_and_yield_2(&p); puts 908; rescue; puts 909; puts $!.class; end +$g = 0; def m_75; p = get_local_block; puts 910; method_use_lambda_and_yield_2(&p); puts 911; end; +begin; puts 912; m_75; puts 913; rescue; puts 914; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 915; method_yield_in_loop(&p); puts 916; rescue; puts 917; puts $!.class; end +$g = 0; def m_76; p = get_local_block; puts 918; method_yield_in_loop(&p); puts 919; end; +begin; puts 920; m_76; puts 921; rescue; puts 922; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 923; method_call_in_loop(&p); puts 924; rescue; puts 925; puts $!.class; end +$g = 0; def m_77; p = get_local_block; puts 926; method_call_in_loop(&p); puts 927; end; +begin; puts 928; m_77; puts 929; rescue; puts 930; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_lambda; puts 931; iterator_via_yield(&p); puts 932; rescue; puts 933; puts $!.class; end +$g = 0; def m_78; p = get_local_lambda; puts 934; iterator_via_yield(&p); puts 935; end; +begin; puts 936; m_78; puts 937; rescue; puts 938; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 939; iterator_via_call(&p); puts 940; rescue; puts 941; puts $!.class; end +$g = 0; def m_79; p = get_local_lambda; puts 942; iterator_via_call(&p); puts 943; end; +begin; puts 944; m_79; puts 945; rescue; puts 946; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 947; method_call_iterator_via_yield(&p); puts 948; rescue; puts 949; puts $!.class; end +$g = 0; def m_80; p = get_local_lambda; puts 950; method_call_iterator_via_yield(&p); puts 951; end; +begin; puts 952; m_80; puts 953; rescue; puts 954; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 955; method_call_iterator_via_call(&p); puts 956; rescue; puts 957; puts $!.class; end +$g = 0; def m_81; p = get_local_lambda; puts 958; method_call_iterator_via_call(&p); puts 959; end; +begin; puts 960; m_81; puts 961; rescue; puts 962; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 963; method_use_lambda_and_yield(&p); puts 964; rescue; puts 965; puts $!.class; end +$g = 0; def m_82; p = get_local_lambda; puts 966; method_use_lambda_and_yield(&p); puts 967; end; +begin; puts 968; m_82; puts 969; rescue; puts 970; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 971; method_use_proc_and_yield(&p); puts 972; rescue; puts 973; puts $!.class; end +$g = 0; def m_83; p = get_local_lambda; puts 974; method_use_proc_and_yield(&p); puts 975; end; +begin; puts 976; m_83; puts 977; rescue; puts 978; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 979; method_use_lambda_and_call(&p); puts 980; rescue; puts 981; puts $!.class; end +$g = 0; def m_84; p = get_local_lambda; puts 982; method_use_lambda_and_call(&p); puts 983; end; +begin; puts 984; m_84; puts 985; rescue; puts 986; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 987; method_use_proc_and_call(&p); puts 988; rescue; puts 989; puts $!.class; end +$g = 0; def m_85; p = get_local_lambda; puts 990; method_use_proc_and_call(&p); puts 991; end; +begin; puts 992; m_85; puts 993; rescue; puts 994; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 995; method_use_lambda_and_yield_2(&p); puts 996; rescue; puts 997; puts $!.class; end +$g = 0; def m_86; p = get_local_lambda; puts 998; method_use_lambda_and_yield_2(&p); puts 999; end; +begin; puts 1000; m_86; puts 1001; rescue; puts 1002; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1003; method_yield_in_loop(&p); puts 1004; rescue; puts 1005; puts $!.class; end +$g = 0; def m_87; p = get_local_lambda; puts 1006; method_yield_in_loop(&p); puts 1007; end; +begin; puts 1008; m_87; puts 1009; rescue; puts 1010; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1011; method_call_in_loop(&p); puts 1012; rescue; puts 1013; puts $!.class; end +$g = 0; def m_88; p = get_local_lambda; puts 1014; method_call_in_loop(&p); puts 1015; end; +begin; puts 1016; m_88; puts 1017; rescue; puts 1018; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_proc; puts 1019; iterator_via_yield(&p); puts 1020; rescue; puts 1021; puts $!.class; end +$g = 0; def m_89; p = get_local_proc; puts 1022; iterator_via_yield(&p); puts 1023; end; +begin; puts 1024; m_89; puts 1025; rescue; puts 1026; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1027; iterator_via_call(&p); puts 1028; rescue; puts 1029; puts $!.class; end +$g = 0; def m_90; p = get_local_proc; puts 1030; iterator_via_call(&p); puts 1031; end; +begin; puts 1032; m_90; puts 1033; rescue; puts 1034; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1035; method_call_iterator_via_yield(&p); puts 1036; rescue; puts 1037; puts $!.class; end +$g = 0; def m_91; p = get_local_proc; puts 1038; method_call_iterator_via_yield(&p); puts 1039; end; +begin; puts 1040; m_91; puts 1041; rescue; puts 1042; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1043; method_call_iterator_via_call(&p); puts 1044; rescue; puts 1045; puts $!.class; end +$g = 0; def m_92; p = get_local_proc; puts 1046; method_call_iterator_via_call(&p); puts 1047; end; +begin; puts 1048; m_92; puts 1049; rescue; puts 1050; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1051; method_use_lambda_and_yield(&p); puts 1052; rescue; puts 1053; puts $!.class; end +$g = 0; def m_93; p = get_local_proc; puts 1054; method_use_lambda_and_yield(&p); puts 1055; end; +begin; puts 1056; m_93; puts 1057; rescue; puts 1058; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1059; method_use_proc_and_yield(&p); puts 1060; rescue; puts 1061; puts $!.class; end +$g = 0; def m_94; p = get_local_proc; puts 1062; method_use_proc_and_yield(&p); puts 1063; end; +begin; puts 1064; m_94; puts 1065; rescue; puts 1066; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1067; method_use_lambda_and_call(&p); puts 1068; rescue; puts 1069; puts $!.class; end +$g = 0; def m_95; p = get_local_proc; puts 1070; method_use_lambda_and_call(&p); puts 1071; end; +begin; puts 1072; m_95; puts 1073; rescue; puts 1074; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1075; method_use_proc_and_call(&p); puts 1076; rescue; puts 1077; puts $!.class; end +$g = 0; def m_96; p = get_local_proc; puts 1078; method_use_proc_and_call(&p); puts 1079; end; +begin; puts 1080; m_96; puts 1081; rescue; puts 1082; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1083; method_use_lambda_and_yield_2(&p); puts 1084; rescue; puts 1085; puts $!.class; end +$g = 0; def m_97; p = get_local_proc; puts 1086; method_use_lambda_and_yield_2(&p); puts 1087; end; +begin; puts 1088; m_97; puts 1089; rescue; puts 1090; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1091; method_yield_in_loop(&p); puts 1092; rescue; puts 1093; puts $!.class; end +$g = 0; def m_98; p = get_local_proc; puts 1094; method_yield_in_loop(&p); puts 1095; end; +begin; puts 1096; m_98; puts 1097; rescue; puts 1098; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1099; method_call_in_loop(&p); puts 1100; rescue; puts 1101; puts $!.class; end +$g = 0; def m_99; p = get_local_proc; puts 1102; method_call_in_loop(&p); puts 1103; end; +begin; puts 1104; m_99; puts 1105; rescue; puts 1106; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1107; p = lambda{ puts 1108; raise IOError; puts 1109}; puts(p.call); puts 1110; rescue; puts 1111; puts $!.class; end +$g = 0; def m_100; puts 1112; p = lambda{ puts 1113; raise IOError; puts 1114}; puts(p.call); puts 1115; end; +begin; puts 1116; m_100; puts 1117; rescue; puts 1118; puts $!.class; end +$g = 0; begin; puts 1119; puts m_100; puts 1120; rescue; puts 1121; puts $!.class; end +$g = 0; def m_101; puts 1122; puts m_100; puts 1123; end; +begin; puts 1124; m_101; puts 1125; rescue; puts 1126; puts $!.class; end +$g = 0; begin; puts 1127; p = Proc.new{ puts 1128; raise IOError; puts 1129}; puts(p.call); puts 1130; rescue; puts 1131; puts $!.class; end +$g = 0; def m_102; puts 1132; p = Proc.new{ puts 1133; raise IOError; puts 1134}; puts(p.call); puts 1135; end; +begin; puts 1136; m_102; puts 1137; rescue; puts 1138; puts $!.class; end +$g = 0; begin; puts 1139; puts m_102; puts 1140; rescue; puts 1141; puts $!.class; end +$g = 0; def m_103; puts 1142; puts m_102; puts 1143; end; +begin; puts 1144; m_103; puts 1145; rescue; puts 1146; puts $!.class; end +$g = 0; begin; puts 1147; p = get_block{ puts 1148; raise IOError; puts 1149}; puts(p.call); puts 1150; rescue; puts 1151; puts $!.class; end +$g = 0; def m_104; puts 1152; p = get_block{ puts 1153; raise IOError; puts 1154}; puts(p.call); puts 1155; end; +begin; puts 1156; m_104; puts 1157; rescue; puts 1158; puts $!.class; end +$g = 0; begin; puts 1159; puts m_104; puts 1160; rescue; puts 1161; puts $!.class; end +$g = 0; def m_105; puts 1162; puts m_104; puts 1163; end; +begin; puts 1164; m_105; puts 1165; rescue; puts 1166; puts $!.class; end +$g = 0; begin; puts 1167; p = get_lambda{ puts 1168; raise IOError; puts 1169}; puts(p.call); puts 1170; rescue; puts 1171; puts $!.class; end +$g = 0; def m_106; puts 1172; p = get_lambda{ puts 1173; raise IOError; puts 1174}; puts(p.call); puts 1175; end; +begin; puts 1176; m_106; puts 1177; rescue; puts 1178; puts $!.class; end +$g = 0; begin; puts 1179; puts m_106; puts 1180; rescue; puts 1181; puts $!.class; end +$g = 0; def m_107; puts 1182; puts m_106; puts 1183; end; +begin; puts 1184; m_107; puts 1185; rescue; puts 1186; puts $!.class; end +$g = 0; begin; puts 1187; p = get_proc{ puts 1188; raise IOError; puts 1189}; puts(p.call); puts 1190; rescue; puts 1191; puts $!.class; end +$g = 0; def m_108; puts 1192; p = get_proc{ puts 1193; raise IOError; puts 1194}; puts(p.call); puts 1195; end; +begin; puts 1196; m_108; puts 1197; rescue; puts 1198; puts $!.class; end +$g = 0; begin; puts 1199; puts m_108; puts 1200; rescue; puts 1201; puts $!.class; end +$g = 0; def m_109; puts 1202; puts m_108; puts 1203; end; +begin; puts 1204; m_109; puts 1205; rescue; puts 1206; puts $!.class; end +$g = 0; begin; puts 1207; p = get_local_block; puts(p.call); puts 1208; rescue; puts 1209; puts $!.class; end +$g = 0; def m_110; puts 1210; p = get_local_block; puts(p.call); puts 1211; end; +begin; puts 1212; m_110; puts 1213; rescue; puts 1214; puts $!.class; end +$g = 0; begin; puts 1215; puts m_110; puts 1216; rescue; puts 1217; puts $!.class; end +$g = 0; def m_111; puts 1218; puts m_110; puts 1219; end; +begin; puts 1220; m_111; puts 1221; rescue; puts 1222; puts $!.class; end +$g = 0; begin; puts 1223; p = get_local_lambda; puts(p.call); puts 1224; rescue; puts 1225; puts $!.class; end +$g = 0; def m_112; puts 1226; p = get_local_lambda; puts(p.call); puts 1227; end; +begin; puts 1228; m_112; puts 1229; rescue; puts 1230; puts $!.class; end +$g = 0; begin; puts 1231; puts m_112; puts 1232; rescue; puts 1233; puts $!.class; end +$g = 0; def m_113; puts 1234; puts m_112; puts 1235; end; +begin; puts 1236; m_113; puts 1237; rescue; puts 1238; puts $!.class; end +$g = 0; begin; puts 1239; p = get_local_proc; puts(p.call); puts 1240; rescue; puts 1241; puts $!.class; end +$g = 0; def m_114; puts 1242; p = get_local_proc; puts(p.call); puts 1243; end; +begin; puts 1244; m_114; puts 1245; rescue; puts 1246; puts $!.class; end +$g = 0; begin; puts 1247; puts m_114; puts 1248; rescue; puts 1249; puts $!.class; end +$g = 0; def m_115; puts 1250; puts m_114; puts 1251; end; +begin; puts 1252; m_115; puts 1253; rescue; puts 1254; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1255; x = lambda { puts 1256; p = lambda{ puts 1257; raise IOError; puts 1258}; puts p.call; puts 1259}; puts x.call; puts 1260; rescue; puts 1261; puts $!.class; end +$g = 0; def m_116; puts 1262; x = lambda { puts 1263; p = lambda{ puts 1264; raise IOError; puts 1265}; puts p.call; puts 1266}; puts x.call; puts 1267; end; +begin; puts 1268; m_116; puts 1269; rescue; puts 1270; puts $!.class; end +$g = 0; begin; puts 1271; x = lambda { puts 1272; p = Proc.new{ puts 1273; raise IOError; puts 1274}; puts p.call; puts 1275}; puts x.call; puts 1276; rescue; puts 1277; puts $!.class; end +$g = 0; def m_117; puts 1278; x = lambda { puts 1279; p = Proc.new{ puts 1280; raise IOError; puts 1281}; puts p.call; puts 1282}; puts x.call; puts 1283; end; +begin; puts 1284; m_117; puts 1285; rescue; puts 1286; puts $!.class; end +$g = 0; begin; puts 1287; x = lambda { puts 1288; p = get_block{ puts 1289; raise IOError; puts 1290}; puts p.call; puts 1291}; puts x.call; puts 1292; rescue; puts 1293; puts $!.class; end +$g = 0; def m_118; puts 1294; x = lambda { puts 1295; p = get_block{ puts 1296; raise IOError; puts 1297}; puts p.call; puts 1298}; puts x.call; puts 1299; end; +begin; puts 1300; m_118; puts 1301; rescue; puts 1302; puts $!.class; end +$g = 0; begin; puts 1303; x = lambda { puts 1304; p = get_lambda{ puts 1305; raise IOError; puts 1306}; puts p.call; puts 1307}; puts x.call; puts 1308; rescue; puts 1309; puts $!.class; end +$g = 0; def m_119; puts 1310; x = lambda { puts 1311; p = get_lambda{ puts 1312; raise IOError; puts 1313}; puts p.call; puts 1314}; puts x.call; puts 1315; end; +begin; puts 1316; m_119; puts 1317; rescue; puts 1318; puts $!.class; end +$g = 0; begin; puts 1319; x = lambda { puts 1320; p = get_proc{ puts 1321; raise IOError; puts 1322}; puts p.call; puts 1323}; puts x.call; puts 1324; rescue; puts 1325; puts $!.class; end +$g = 0; def m_120; puts 1326; x = lambda { puts 1327; p = get_proc{ puts 1328; raise IOError; puts 1329}; puts p.call; puts 1330}; puts x.call; puts 1331; end; +begin; puts 1332; m_120; puts 1333; rescue; puts 1334; puts $!.class; end +$g = 0; begin; puts 1335; x = lambda { puts 1336; p = get_local_block; puts p.call; puts 1337}; puts x.call; puts 1338; rescue; puts 1339; puts $!.class; end +$g = 0; def m_121; puts 1340; x = lambda { puts 1341; p = get_local_block; puts p.call; puts 1342}; puts x.call; puts 1343; end; +begin; puts 1344; m_121; puts 1345; rescue; puts 1346; puts $!.class; end +$g = 0; begin; puts 1347; x = lambda { puts 1348; p = get_local_lambda; puts p.call; puts 1349}; puts x.call; puts 1350; rescue; puts 1351; puts $!.class; end +$g = 0; def m_122; puts 1352; x = lambda { puts 1353; p = get_local_lambda; puts p.call; puts 1354}; puts x.call; puts 1355; end; +begin; puts 1356; m_122; puts 1357; rescue; puts 1358; puts $!.class; end +$g = 0; begin; puts 1359; x = lambda { puts 1360; p = get_local_proc; puts p.call; puts 1361}; puts x.call; puts 1362; rescue; puts 1363; puts $!.class; end +$g = 0; def m_123; puts 1364; x = lambda { puts 1365; p = get_local_proc; puts p.call; puts 1366}; puts x.call; puts 1367; end; +begin; puts 1368; m_123; puts 1369; rescue; puts 1370; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1371; x = Proc.new { puts 1372; p = lambda{ puts 1373; raise IOError; puts 1374}; puts p.call; puts 1375}; puts x.call; puts 1376; rescue; puts 1377; puts $!.class; end +$g = 0; def m_124; puts 1378; x = Proc.new { puts 1379; p = lambda{ puts 1380; raise IOError; puts 1381}; puts p.call; puts 1382}; puts x.call; puts 1383; end; +begin; puts 1384; m_124; puts 1385; rescue; puts 1386; puts $!.class; end +$g = 0; begin; puts 1387; x = Proc.new { puts 1388; p = Proc.new{ puts 1389; raise IOError; puts 1390}; puts p.call; puts 1391}; puts x.call; puts 1392; rescue; puts 1393; puts $!.class; end +$g = 0; def m_125; puts 1394; x = Proc.new { puts 1395; p = Proc.new{ puts 1396; raise IOError; puts 1397}; puts p.call; puts 1398}; puts x.call; puts 1399; end; +begin; puts 1400; m_125; puts 1401; rescue; puts 1402; puts $!.class; end +$g = 0; begin; puts 1403; x = Proc.new { puts 1404; p = get_block{ puts 1405; raise IOError; puts 1406}; puts p.call; puts 1407}; puts x.call; puts 1408; rescue; puts 1409; puts $!.class; end +$g = 0; def m_126; puts 1410; x = Proc.new { puts 1411; p = get_block{ puts 1412; raise IOError; puts 1413}; puts p.call; puts 1414}; puts x.call; puts 1415; end; +begin; puts 1416; m_126; puts 1417; rescue; puts 1418; puts $!.class; end +$g = 0; begin; puts 1419; x = Proc.new { puts 1420; p = get_lambda{ puts 1421; raise IOError; puts 1422}; puts p.call; puts 1423}; puts x.call; puts 1424; rescue; puts 1425; puts $!.class; end +$g = 0; def m_127; puts 1426; x = Proc.new { puts 1427; p = get_lambda{ puts 1428; raise IOError; puts 1429}; puts p.call; puts 1430}; puts x.call; puts 1431; end; +begin; puts 1432; m_127; puts 1433; rescue; puts 1434; puts $!.class; end +$g = 0; begin; puts 1435; x = Proc.new { puts 1436; p = get_proc{ puts 1437; raise IOError; puts 1438}; puts p.call; puts 1439}; puts x.call; puts 1440; rescue; puts 1441; puts $!.class; end +$g = 0; def m_128; puts 1442; x = Proc.new { puts 1443; p = get_proc{ puts 1444; raise IOError; puts 1445}; puts p.call; puts 1446}; puts x.call; puts 1447; end; +begin; puts 1448; m_128; puts 1449; rescue; puts 1450; puts $!.class; end +$g = 0; begin; puts 1451; x = Proc.new { puts 1452; p = get_local_block; puts p.call; puts 1453}; puts x.call; puts 1454; rescue; puts 1455; puts $!.class; end +$g = 0; def m_129; puts 1456; x = Proc.new { puts 1457; p = get_local_block; puts p.call; puts 1458}; puts x.call; puts 1459; end; +begin; puts 1460; m_129; puts 1461; rescue; puts 1462; puts $!.class; end +$g = 0; begin; puts 1463; x = Proc.new { puts 1464; p = get_local_lambda; puts p.call; puts 1465}; puts x.call; puts 1466; rescue; puts 1467; puts $!.class; end +$g = 0; def m_130; puts 1468; x = Proc.new { puts 1469; p = get_local_lambda; puts p.call; puts 1470}; puts x.call; puts 1471; end; +begin; puts 1472; m_130; puts 1473; rescue; puts 1474; puts $!.class; end +$g = 0; begin; puts 1475; x = Proc.new { puts 1476; p = get_local_proc; puts p.call; puts 1477}; puts x.call; puts 1478; rescue; puts 1479; puts $!.class; end +$g = 0; def m_131; puts 1480; x = Proc.new { puts 1481; p = get_local_proc; puts p.call; puts 1482}; puts x.call; puts 1483; end; +begin; puts 1484; m_131; puts 1485; rescue; puts 1486; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1487; x = get_block { puts 1488; p = lambda{ puts 1489; raise IOError; puts 1490}; puts p.call; puts 1491}; puts x.call; puts 1492; rescue; puts 1493; puts $!.class; end +$g = 0; def m_132; puts 1494; x = get_block { puts 1495; p = lambda{ puts 1496; raise IOError; puts 1497}; puts p.call; puts 1498}; puts x.call; puts 1499; end; +begin; puts 1500; m_132; puts 1501; rescue; puts 1502; puts $!.class; end +$g = 0; begin; puts 1503; x = get_block { puts 1504; p = Proc.new{ puts 1505; raise IOError; puts 1506}; puts p.call; puts 1507}; puts x.call; puts 1508; rescue; puts 1509; puts $!.class; end +$g = 0; def m_133; puts 1510; x = get_block { puts 1511; p = Proc.new{ puts 1512; raise IOError; puts 1513}; puts p.call; puts 1514}; puts x.call; puts 1515; end; +begin; puts 1516; m_133; puts 1517; rescue; puts 1518; puts $!.class; end +$g = 0; begin; puts 1519; x = get_block { puts 1520; p = get_block{ puts 1521; raise IOError; puts 1522}; puts p.call; puts 1523}; puts x.call; puts 1524; rescue; puts 1525; puts $!.class; end +$g = 0; def m_134; puts 1526; x = get_block { puts 1527; p = get_block{ puts 1528; raise IOError; puts 1529}; puts p.call; puts 1530}; puts x.call; puts 1531; end; +begin; puts 1532; m_134; puts 1533; rescue; puts 1534; puts $!.class; end +$g = 0; begin; puts 1535; x = get_block { puts 1536; p = get_lambda{ puts 1537; raise IOError; puts 1538}; puts p.call; puts 1539}; puts x.call; puts 1540; rescue; puts 1541; puts $!.class; end +$g = 0; def m_135; puts 1542; x = get_block { puts 1543; p = get_lambda{ puts 1544; raise IOError; puts 1545}; puts p.call; puts 1546}; puts x.call; puts 1547; end; +begin; puts 1548; m_135; puts 1549; rescue; puts 1550; puts $!.class; end +$g = 0; begin; puts 1551; x = get_block { puts 1552; p = get_proc{ puts 1553; raise IOError; puts 1554}; puts p.call; puts 1555}; puts x.call; puts 1556; rescue; puts 1557; puts $!.class; end +$g = 0; def m_136; puts 1558; x = get_block { puts 1559; p = get_proc{ puts 1560; raise IOError; puts 1561}; puts p.call; puts 1562}; puts x.call; puts 1563; end; +begin; puts 1564; m_136; puts 1565; rescue; puts 1566; puts $!.class; end +$g = 0; begin; puts 1567; x = get_block { puts 1568; p = get_local_block; puts p.call; puts 1569}; puts x.call; puts 1570; rescue; puts 1571; puts $!.class; end +$g = 0; def m_137; puts 1572; x = get_block { puts 1573; p = get_local_block; puts p.call; puts 1574}; puts x.call; puts 1575; end; +begin; puts 1576; m_137; puts 1577; rescue; puts 1578; puts $!.class; end +$g = 0; begin; puts 1579; x = get_block { puts 1580; p = get_local_lambda; puts p.call; puts 1581}; puts x.call; puts 1582; rescue; puts 1583; puts $!.class; end +$g = 0; def m_138; puts 1584; x = get_block { puts 1585; p = get_local_lambda; puts p.call; puts 1586}; puts x.call; puts 1587; end; +begin; puts 1588; m_138; puts 1589; rescue; puts 1590; puts $!.class; end +$g = 0; begin; puts 1591; x = get_block { puts 1592; p = get_local_proc; puts p.call; puts 1593}; puts x.call; puts 1594; rescue; puts 1595; puts $!.class; end +$g = 0; def m_139; puts 1596; x = get_block { puts 1597; p = get_local_proc; puts p.call; puts 1598}; puts x.call; puts 1599; end; +begin; puts 1600; m_139; puts 1601; rescue; puts 1602; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1603; x = get_lambda { puts 1604; p = lambda{ puts 1605; raise IOError; puts 1606}; puts p.call; puts 1607}; puts x.call; puts 1608; rescue; puts 1609; puts $!.class; end +$g = 0; def m_140; puts 1610; x = get_lambda { puts 1611; p = lambda{ puts 1612; raise IOError; puts 1613}; puts p.call; puts 1614}; puts x.call; puts 1615; end; +begin; puts 1616; m_140; puts 1617; rescue; puts 1618; puts $!.class; end +$g = 0; begin; puts 1619; x = get_lambda { puts 1620; p = Proc.new{ puts 1621; raise IOError; puts 1622}; puts p.call; puts 1623}; puts x.call; puts 1624; rescue; puts 1625; puts $!.class; end +$g = 0; def m_141; puts 1626; x = get_lambda { puts 1627; p = Proc.new{ puts 1628; raise IOError; puts 1629}; puts p.call; puts 1630}; puts x.call; puts 1631; end; +begin; puts 1632; m_141; puts 1633; rescue; puts 1634; puts $!.class; end +$g = 0; begin; puts 1635; x = get_lambda { puts 1636; p = get_block{ puts 1637; raise IOError; puts 1638}; puts p.call; puts 1639}; puts x.call; puts 1640; rescue; puts 1641; puts $!.class; end +$g = 0; def m_142; puts 1642; x = get_lambda { puts 1643; p = get_block{ puts 1644; raise IOError; puts 1645}; puts p.call; puts 1646}; puts x.call; puts 1647; end; +begin; puts 1648; m_142; puts 1649; rescue; puts 1650; puts $!.class; end +$g = 0; begin; puts 1651; x = get_lambda { puts 1652; p = get_lambda{ puts 1653; raise IOError; puts 1654}; puts p.call; puts 1655}; puts x.call; puts 1656; rescue; puts 1657; puts $!.class; end +$g = 0; def m_143; puts 1658; x = get_lambda { puts 1659; p = get_lambda{ puts 1660; raise IOError; puts 1661}; puts p.call; puts 1662}; puts x.call; puts 1663; end; +begin; puts 1664; m_143; puts 1665; rescue; puts 1666; puts $!.class; end +$g = 0; begin; puts 1667; x = get_lambda { puts 1668; p = get_proc{ puts 1669; raise IOError; puts 1670}; puts p.call; puts 1671}; puts x.call; puts 1672; rescue; puts 1673; puts $!.class; end +$g = 0; def m_144; puts 1674; x = get_lambda { puts 1675; p = get_proc{ puts 1676; raise IOError; puts 1677}; puts p.call; puts 1678}; puts x.call; puts 1679; end; +begin; puts 1680; m_144; puts 1681; rescue; puts 1682; puts $!.class; end +$g = 0; begin; puts 1683; x = get_lambda { puts 1684; p = get_local_block; puts p.call; puts 1685}; puts x.call; puts 1686; rescue; puts 1687; puts $!.class; end +$g = 0; def m_145; puts 1688; x = get_lambda { puts 1689; p = get_local_block; puts p.call; puts 1690}; puts x.call; puts 1691; end; +begin; puts 1692; m_145; puts 1693; rescue; puts 1694; puts $!.class; end +$g = 0; begin; puts 1695; x = get_lambda { puts 1696; p = get_local_lambda; puts p.call; puts 1697}; puts x.call; puts 1698; rescue; puts 1699; puts $!.class; end +$g = 0; def m_146; puts 1700; x = get_lambda { puts 1701; p = get_local_lambda; puts p.call; puts 1702}; puts x.call; puts 1703; end; +begin; puts 1704; m_146; puts 1705; rescue; puts 1706; puts $!.class; end +$g = 0; begin; puts 1707; x = get_lambda { puts 1708; p = get_local_proc; puts p.call; puts 1709}; puts x.call; puts 1710; rescue; puts 1711; puts $!.class; end +$g = 0; def m_147; puts 1712; x = get_lambda { puts 1713; p = get_local_proc; puts p.call; puts 1714}; puts x.call; puts 1715; end; +begin; puts 1716; m_147; puts 1717; rescue; puts 1718; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1719; x = get_proc { puts 1720; p = lambda{ puts 1721; raise IOError; puts 1722}; puts p.call; puts 1723}; puts x.call; puts 1724; rescue; puts 1725; puts $!.class; end +$g = 0; def m_148; puts 1726; x = get_proc { puts 1727; p = lambda{ puts 1728; raise IOError; puts 1729}; puts p.call; puts 1730}; puts x.call; puts 1731; end; +begin; puts 1732; m_148; puts 1733; rescue; puts 1734; puts $!.class; end +$g = 0; begin; puts 1735; x = get_proc { puts 1736; p = Proc.new{ puts 1737; raise IOError; puts 1738}; puts p.call; puts 1739}; puts x.call; puts 1740; rescue; puts 1741; puts $!.class; end +$g = 0; def m_149; puts 1742; x = get_proc { puts 1743; p = Proc.new{ puts 1744; raise IOError; puts 1745}; puts p.call; puts 1746}; puts x.call; puts 1747; end; +begin; puts 1748; m_149; puts 1749; rescue; puts 1750; puts $!.class; end +$g = 0; begin; puts 1751; x = get_proc { puts 1752; p = get_block{ puts 1753; raise IOError; puts 1754}; puts p.call; puts 1755}; puts x.call; puts 1756; rescue; puts 1757; puts $!.class; end +$g = 0; def m_150; puts 1758; x = get_proc { puts 1759; p = get_block{ puts 1760; raise IOError; puts 1761}; puts p.call; puts 1762}; puts x.call; puts 1763; end; +begin; puts 1764; m_150; puts 1765; rescue; puts 1766; puts $!.class; end +$g = 0; begin; puts 1767; x = get_proc { puts 1768; p = get_lambda{ puts 1769; raise IOError; puts 1770}; puts p.call; puts 1771}; puts x.call; puts 1772; rescue; puts 1773; puts $!.class; end +$g = 0; def m_151; puts 1774; x = get_proc { puts 1775; p = get_lambda{ puts 1776; raise IOError; puts 1777}; puts p.call; puts 1778}; puts x.call; puts 1779; end; +begin; puts 1780; m_151; puts 1781; rescue; puts 1782; puts $!.class; end +$g = 0; begin; puts 1783; x = get_proc { puts 1784; p = get_proc{ puts 1785; raise IOError; puts 1786}; puts p.call; puts 1787}; puts x.call; puts 1788; rescue; puts 1789; puts $!.class; end +$g = 0; def m_152; puts 1790; x = get_proc { puts 1791; p = get_proc{ puts 1792; raise IOError; puts 1793}; puts p.call; puts 1794}; puts x.call; puts 1795; end; +begin; puts 1796; m_152; puts 1797; rescue; puts 1798; puts $!.class; end +$g = 0; begin; puts 1799; x = get_proc { puts 1800; p = get_local_block; puts p.call; puts 1801}; puts x.call; puts 1802; rescue; puts 1803; puts $!.class; end +$g = 0; def m_153; puts 1804; x = get_proc { puts 1805; p = get_local_block; puts p.call; puts 1806}; puts x.call; puts 1807; end; +begin; puts 1808; m_153; puts 1809; rescue; puts 1810; puts $!.class; end +$g = 0; begin; puts 1811; x = get_proc { puts 1812; p = get_local_lambda; puts p.call; puts 1813}; puts x.call; puts 1814; rescue; puts 1815; puts $!.class; end +$g = 0; def m_154; puts 1816; x = get_proc { puts 1817; p = get_local_lambda; puts p.call; puts 1818}; puts x.call; puts 1819; end; +begin; puts 1820; m_154; puts 1821; rescue; puts 1822; puts $!.class; end +$g = 0; begin; puts 1823; x = get_proc { puts 1824; p = get_local_proc; puts p.call; puts 1825}; puts x.call; puts 1826; rescue; puts 1827; puts $!.class; end +$g = 0; def m_155; puts 1828; x = get_proc { puts 1829; p = get_local_proc; puts p.call; puts 1830}; puts x.call; puts 1831; end; +begin; puts 1832; m_155; puts 1833; rescue; puts 1834; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1835; for i in [1, 2]; puts 1836; p = lambda{ puts 1837; raise IOError; puts 1838}; puts p.call; puts 1839; end; puts 1840; rescue; puts 1841; puts $!.class; end +$g = 0; def m_156; puts 1842; for i in [1, 2]; puts 1843; p = lambda{ puts 1844; raise IOError; puts 1845}; puts p.call; puts 1846; end; puts 1847; end; +begin; puts 1848; m_156; puts 1849; rescue; puts 1850; puts $!.class; end +$g = 0; begin; puts 1851; for i in [1, 2]; puts 1852; p = Proc.new{ puts 1853; raise IOError; puts 1854}; puts p.call; puts 1855; end; puts 1856; rescue; puts 1857; puts $!.class; end +$g = 0; def m_157; puts 1858; for i in [1, 2]; puts 1859; p = Proc.new{ puts 1860; raise IOError; puts 1861}; puts p.call; puts 1862; end; puts 1863; end; +begin; puts 1864; m_157; puts 1865; rescue; puts 1866; puts $!.class; end +$g = 0; begin; puts 1867; for i in [1, 2]; puts 1868; p = get_block{ puts 1869; raise IOError; puts 1870}; puts p.call; puts 1871; end; puts 1872; rescue; puts 1873; puts $!.class; end +$g = 0; def m_158; puts 1874; for i in [1, 2]; puts 1875; p = get_block{ puts 1876; raise IOError; puts 1877}; puts p.call; puts 1878; end; puts 1879; end; +begin; puts 1880; m_158; puts 1881; rescue; puts 1882; puts $!.class; end +$g = 0; begin; puts 1883; for i in [1, 2]; puts 1884; p = get_lambda{ puts 1885; raise IOError; puts 1886}; puts p.call; puts 1887; end; puts 1888; rescue; puts 1889; puts $!.class; end +$g = 0; def m_159; puts 1890; for i in [1, 2]; puts 1891; p = get_lambda{ puts 1892; raise IOError; puts 1893}; puts p.call; puts 1894; end; puts 1895; end; +begin; puts 1896; m_159; puts 1897; rescue; puts 1898; puts $!.class; end +$g = 0; begin; puts 1899; for i in [1, 2]; puts 1900; p = get_proc{ puts 1901; raise IOError; puts 1902}; puts p.call; puts 1903; end; puts 1904; rescue; puts 1905; puts $!.class; end +$g = 0; def m_160; puts 1906; for i in [1, 2]; puts 1907; p = get_proc{ puts 1908; raise IOError; puts 1909}; puts p.call; puts 1910; end; puts 1911; end; +begin; puts 1912; m_160; puts 1913; rescue; puts 1914; puts $!.class; end +$g = 0; begin; puts 1915; for i in [1, 2]; puts 1916; p = get_local_block; puts p.call; puts 1917; end; puts 1918; rescue; puts 1919; puts $!.class; end +$g = 0; def m_161; puts 1920; for i in [1, 2]; puts 1921; p = get_local_block; puts p.call; puts 1922; end; puts 1923; end; +begin; puts 1924; m_161; puts 1925; rescue; puts 1926; puts $!.class; end +$g = 0; begin; puts 1927; for i in [1, 2]; puts 1928; p = get_local_lambda; puts p.call; puts 1929; end; puts 1930; rescue; puts 1931; puts $!.class; end +$g = 0; def m_162; puts 1932; for i in [1, 2]; puts 1933; p = get_local_lambda; puts p.call; puts 1934; end; puts 1935; end; +begin; puts 1936; m_162; puts 1937; rescue; puts 1938; puts $!.class; end +$g = 0; begin; puts 1939; for i in [1, 2]; puts 1940; p = get_local_proc; puts p.call; puts 1941; end; puts 1942; rescue; puts 1943; puts $!.class; end +$g = 0; def m_163; puts 1944; for i in [1, 2]; puts 1945; p = get_local_proc; puts p.call; puts 1946; end; puts 1947; end; +begin; puts 1948; m_163; puts 1949; rescue; puts 1950; puts $!.class; end +end +test diff --git a/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_redo.rb b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_redo.rb new file mode 100644 index 0000000000..ba23f43829 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_redo.rb @@ -0,0 +1,595 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +# helper +def myeval(line); puts 1; eval(line); puts 2; end +def call1(x); puts 3; call2(x); puts 4; end +def call2(x); puts 5; call3(x); puts 6; end +def call3(x); puts 7; puts x.call; puts 8; end + +# producer + +def get_block(&p); p; end +def get_lambda(&p); lambda(&p); end +def get_proc(&p); Proc.new(&p); end + +def get_local_block; get_block { puts 9; $g += 1; redo if $g < 4;; puts 10 }; end +def get_local_lambda; lambda { puts 11; $g += 1; redo if $g < 4;; puts 12 }; end +def get_local_proc; Proc.new { puts 13; $g += 1; redo if $g < 4;; puts 14 }; end + +# consumer + +# taking arguments +def iterator_via_yield; puts 15; x = yield; puts x; puts 16; end +def iterator_via_call(&p); puts 17; puts(p.call); puts 18; end + +def method_call_iterator_via_yield(&p); puts 19; iterator_via_yield(&p); puts 20; end +def method_call_iterator_via_call(&p); puts 21; iterator_via_call(&p); puts 22; end + +def method_use_lambda_and_yield; puts 23; x = lambda { puts 24; yield; puts 25 }; puts x.call; puts 26; end +def method_use_proc_and_yield; puts 27; x = Proc.new { puts 28; yield; puts 29 }; puts x.call; puts 30; end +def method_use_lambda_and_call(&p); puts 31; x = lambda { puts 32; p.call; puts 33 }; puts x.call; puts 34; end +def method_use_proc_and_call(&p); puts 35; x = Proc.new { puts 36; p.call; puts 37 }; puts x.call; puts 38; end + +def method_use_lambda_and_yield_2; puts 39; x = lambda { puts 40; yield; puts 41 }; call1(x); puts 42; end + +def method_yield_in_loop; puts 43; for i in [1, 2]; puts 44; yield; puts 45; end; puts 46; end +def method_call_in_loop(&p); puts 47; for i in [3, 4]; puts 48; p.call; puts 49; end; puts 50; end + +# created in-place +def test +$g = 0; begin; puts 51; iterator_via_yield { puts 52; $g += 1; redo if $g < 4;; puts 53}; puts 54; rescue; puts 55; puts $!.class; end +$g = 0; def m_1; puts 56; $g = 0; begin; puts 57; iterator_via_yield { puts 58; $g += 1; redo if $g < 4;; puts 59}; puts 60; rescue; puts 61; puts $!.class; end; puts 62; end; m_1 +$g = 0; begin; puts 63; iterator_via_call { puts 64; $g += 1; redo if $g < 4;; puts 65}; puts 66; rescue; puts 67; puts $!.class; end +$g = 0; def m_2; puts 68; $g = 0; begin; puts 69; iterator_via_call { puts 70; $g += 1; redo if $g < 4;; puts 71}; puts 72; rescue; puts 73; puts $!.class; end; puts 74; end; m_2 +$g = 0; begin; puts 75; method_call_iterator_via_yield { puts 76; $g += 1; redo if $g < 4;; puts 77}; puts 78; rescue; puts 79; puts $!.class; end +$g = 0; def m_3; puts 80; $g = 0; begin; puts 81; method_call_iterator_via_yield { puts 82; $g += 1; redo if $g < 4;; puts 83}; puts 84; rescue; puts 85; puts $!.class; end; puts 86; end; m_3 +$g = 0; begin; puts 87; method_call_iterator_via_call { puts 88; $g += 1; redo if $g < 4;; puts 89}; puts 90; rescue; puts 91; puts $!.class; end +$g = 0; def m_4; puts 92; $g = 0; begin; puts 93; method_call_iterator_via_call { puts 94; $g += 1; redo if $g < 4;; puts 95}; puts 96; rescue; puts 97; puts $!.class; end; puts 98; end; m_4 +$g = 0; begin; puts 99; method_use_lambda_and_yield { puts 100; $g += 1; redo if $g < 4;; puts 101}; puts 102; rescue; puts 103; puts $!.class; end +$g = 0; def m_5; puts 104; $g = 0; begin; puts 105; method_use_lambda_and_yield { puts 106; $g += 1; redo if $g < 4;; puts 107}; puts 108; rescue; puts 109; puts $!.class; end; puts 110; end; m_5 +$g = 0; begin; puts 111; method_use_proc_and_yield { puts 112; $g += 1; redo if $g < 4;; puts 113}; puts 114; rescue; puts 115; puts $!.class; end +$g = 0; def m_6; puts 116; $g = 0; begin; puts 117; method_use_proc_and_yield { puts 118; $g += 1; redo if $g < 4;; puts 119}; puts 120; rescue; puts 121; puts $!.class; end; puts 122; end; m_6 +$g = 0; begin; puts 123; method_use_lambda_and_call { puts 124; $g += 1; redo if $g < 4;; puts 125}; puts 126; rescue; puts 127; puts $!.class; end +$g = 0; def m_7; puts 128; $g = 0; begin; puts 129; method_use_lambda_and_call { puts 130; $g += 1; redo if $g < 4;; puts 131}; puts 132; rescue; puts 133; puts $!.class; end; puts 134; end; m_7 +$g = 0; begin; puts 135; method_use_proc_and_call { puts 136; $g += 1; redo if $g < 4;; puts 137}; puts 138; rescue; puts 139; puts $!.class; end +$g = 0; def m_8; puts 140; $g = 0; begin; puts 141; method_use_proc_and_call { puts 142; $g += 1; redo if $g < 4;; puts 143}; puts 144; rescue; puts 145; puts $!.class; end; puts 146; end; m_8 +$g = 0; begin; puts 147; method_use_lambda_and_yield_2 { puts 148; $g += 1; redo if $g < 4;; puts 149}; puts 150; rescue; puts 151; puts $!.class; end +$g = 0; def m_9; puts 152; $g = 0; begin; puts 153; method_use_lambda_and_yield_2 { puts 154; $g += 1; redo if $g < 4;; puts 155}; puts 156; rescue; puts 157; puts $!.class; end; puts 158; end; m_9 +$g = 0; begin; puts 159; method_yield_in_loop { puts 160; $g += 1; redo if $g < 4;; puts 161}; puts 162; rescue; puts 163; puts $!.class; end +$g = 0; def m_10; puts 164; $g = 0; begin; puts 165; method_yield_in_loop { puts 166; $g += 1; redo if $g < 4;; puts 167}; puts 168; rescue; puts 169; puts $!.class; end; puts 170; end; m_10 +$g = 0; begin; puts 171; method_call_in_loop { puts 172; $g += 1; redo if $g < 4;; puts 173}; puts 174; rescue; puts 175; puts $!.class; end +$g = 0; def m_11; puts 176; $g = 0; begin; puts 177; method_call_in_loop { puts 178; $g += 1; redo if $g < 4;; puts 179}; puts 180; rescue; puts 181; puts $!.class; end; puts 182; end; m_11 +end +test + + +# created locally or from method +def test +$g = 0; begin; p = lambda{ puts 183; $g += 1; redo if $g < 4;; puts 184}; puts 185; iterator_via_yield(&p); puts 186; rescue; puts 187; puts $!.class; end +$g = 0; def m_12; p = lambda{ puts 188; $g += 1; redo if $g < 4;; puts 189}; puts 190; iterator_via_yield(&p); puts 191; end; +begin; puts 192; m_12; puts 193; rescue; puts 194; puts $!.class; end +$g = 0; begin; p = lambda{ puts 195; $g += 1; redo if $g < 4;; puts 196}; puts 197; iterator_via_call(&p); puts 198; rescue; puts 199; puts $!.class; end +$g = 0; def m_13; p = lambda{ puts 200; $g += 1; redo if $g < 4;; puts 201}; puts 202; iterator_via_call(&p); puts 203; end; +begin; puts 204; m_13; puts 205; rescue; puts 206; puts $!.class; end +$g = 0; begin; p = lambda{ puts 207; $g += 1; redo if $g < 4;; puts 208}; puts 209; method_call_iterator_via_yield(&p); puts 210; rescue; puts 211; puts $!.class; end +$g = 0; def m_14; p = lambda{ puts 212; $g += 1; redo if $g < 4;; puts 213}; puts 214; method_call_iterator_via_yield(&p); puts 215; end; +begin; puts 216; m_14; puts 217; rescue; puts 218; puts $!.class; end +$g = 0; begin; p = lambda{ puts 219; $g += 1; redo if $g < 4;; puts 220}; puts 221; method_call_iterator_via_call(&p); puts 222; rescue; puts 223; puts $!.class; end +$g = 0; def m_15; p = lambda{ puts 224; $g += 1; redo if $g < 4;; puts 225}; puts 226; method_call_iterator_via_call(&p); puts 227; end; +begin; puts 228; m_15; puts 229; rescue; puts 230; puts $!.class; end +$g = 0; begin; p = lambda{ puts 231; $g += 1; redo if $g < 4;; puts 232}; puts 233; method_use_lambda_and_yield(&p); puts 234; rescue; puts 235; puts $!.class; end +$g = 0; def m_16; p = lambda{ puts 236; $g += 1; redo if $g < 4;; puts 237}; puts 238; method_use_lambda_and_yield(&p); puts 239; end; +begin; puts 240; m_16; puts 241; rescue; puts 242; puts $!.class; end +$g = 0; begin; p = lambda{ puts 243; $g += 1; redo if $g < 4;; puts 244}; puts 245; method_use_proc_and_yield(&p); puts 246; rescue; puts 247; puts $!.class; end +$g = 0; def m_17; p = lambda{ puts 248; $g += 1; redo if $g < 4;; puts 249}; puts 250; method_use_proc_and_yield(&p); puts 251; end; +begin; puts 252; m_17; puts 253; rescue; puts 254; puts $!.class; end +$g = 0; begin; p = lambda{ puts 255; $g += 1; redo if $g < 4;; puts 256}; puts 257; method_use_lambda_and_call(&p); puts 258; rescue; puts 259; puts $!.class; end +$g = 0; def m_18; p = lambda{ puts 260; $g += 1; redo if $g < 4;; puts 261}; puts 262; method_use_lambda_and_call(&p); puts 263; end; +begin; puts 264; m_18; puts 265; rescue; puts 266; puts $!.class; end +$g = 0; begin; p = lambda{ puts 267; $g += 1; redo if $g < 4;; puts 268}; puts 269; method_use_proc_and_call(&p); puts 270; rescue; puts 271; puts $!.class; end +$g = 0; def m_19; p = lambda{ puts 272; $g += 1; redo if $g < 4;; puts 273}; puts 274; method_use_proc_and_call(&p); puts 275; end; +begin; puts 276; m_19; puts 277; rescue; puts 278; puts $!.class; end +$g = 0; begin; p = lambda{ puts 279; $g += 1; redo if $g < 4;; puts 280}; puts 281; method_use_lambda_and_yield_2(&p); puts 282; rescue; puts 283; puts $!.class; end +$g = 0; def m_20; p = lambda{ puts 284; $g += 1; redo if $g < 4;; puts 285}; puts 286; method_use_lambda_and_yield_2(&p); puts 287; end; +begin; puts 288; m_20; puts 289; rescue; puts 290; puts $!.class; end +$g = 0; begin; p = lambda{ puts 291; $g += 1; redo if $g < 4;; puts 292}; puts 293; method_yield_in_loop(&p); puts 294; rescue; puts 295; puts $!.class; end +$g = 0; def m_21; p = lambda{ puts 296; $g += 1; redo if $g < 4;; puts 297}; puts 298; method_yield_in_loop(&p); puts 299; end; +begin; puts 300; m_21; puts 301; rescue; puts 302; puts $!.class; end +$g = 0; begin; p = lambda{ puts 303; $g += 1; redo if $g < 4;; puts 304}; puts 305; method_call_in_loop(&p); puts 306; rescue; puts 307; puts $!.class; end +$g = 0; def m_22; p = lambda{ puts 308; $g += 1; redo if $g < 4;; puts 309}; puts 310; method_call_in_loop(&p); puts 311; end; +begin; puts 312; m_22; puts 313; rescue; puts 314; puts $!.class; end +end +test + +def test +$g = 0; begin; p = Proc.new{ puts 315; $g += 1; redo if $g < 4;; puts 316}; puts 317; iterator_via_yield(&p); puts 318; rescue; puts 319; puts $!.class; end +$g = 0; def m_23; p = Proc.new{ puts 320; $g += 1; redo if $g < 4;; puts 321}; puts 322; iterator_via_yield(&p); puts 323; end; +begin; puts 324; m_23; puts 325; rescue; puts 326; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 327; $g += 1; redo if $g < 4;; puts 328}; puts 329; iterator_via_call(&p); puts 330; rescue; puts 331; puts $!.class; end +$g = 0; def m_24; p = Proc.new{ puts 332; $g += 1; redo if $g < 4;; puts 333}; puts 334; iterator_via_call(&p); puts 335; end; +begin; puts 336; m_24; puts 337; rescue; puts 338; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 339; $g += 1; redo if $g < 4;; puts 340}; puts 341; method_call_iterator_via_yield(&p); puts 342; rescue; puts 343; puts $!.class; end +$g = 0; def m_25; p = Proc.new{ puts 344; $g += 1; redo if $g < 4;; puts 345}; puts 346; method_call_iterator_via_yield(&p); puts 347; end; +begin; puts 348; m_25; puts 349; rescue; puts 350; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 351; $g += 1; redo if $g < 4;; puts 352}; puts 353; method_call_iterator_via_call(&p); puts 354; rescue; puts 355; puts $!.class; end +$g = 0; def m_26; p = Proc.new{ puts 356; $g += 1; redo if $g < 4;; puts 357}; puts 358; method_call_iterator_via_call(&p); puts 359; end; +begin; puts 360; m_26; puts 361; rescue; puts 362; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 363; $g += 1; redo if $g < 4;; puts 364}; puts 365; method_use_lambda_and_yield(&p); puts 366; rescue; puts 367; puts $!.class; end +$g = 0; def m_27; p = Proc.new{ puts 368; $g += 1; redo if $g < 4;; puts 369}; puts 370; method_use_lambda_and_yield(&p); puts 371; end; +begin; puts 372; m_27; puts 373; rescue; puts 374; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 375; $g += 1; redo if $g < 4;; puts 376}; puts 377; method_use_proc_and_yield(&p); puts 378; rescue; puts 379; puts $!.class; end +$g = 0; def m_28; p = Proc.new{ puts 380; $g += 1; redo if $g < 4;; puts 381}; puts 382; method_use_proc_and_yield(&p); puts 383; end; +begin; puts 384; m_28; puts 385; rescue; puts 386; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 387; $g += 1; redo if $g < 4;; puts 388}; puts 389; method_use_lambda_and_call(&p); puts 390; rescue; puts 391; puts $!.class; end +$g = 0; def m_29; p = Proc.new{ puts 392; $g += 1; redo if $g < 4;; puts 393}; puts 394; method_use_lambda_and_call(&p); puts 395; end; +begin; puts 396; m_29; puts 397; rescue; puts 398; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 399; $g += 1; redo if $g < 4;; puts 400}; puts 401; method_use_proc_and_call(&p); puts 402; rescue; puts 403; puts $!.class; end +$g = 0; def m_30; p = Proc.new{ puts 404; $g += 1; redo if $g < 4;; puts 405}; puts 406; method_use_proc_and_call(&p); puts 407; end; +begin; puts 408; m_30; puts 409; rescue; puts 410; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 411; $g += 1; redo if $g < 4;; puts 412}; puts 413; method_use_lambda_and_yield_2(&p); puts 414; rescue; puts 415; puts $!.class; end +$g = 0; def m_31; p = Proc.new{ puts 416; $g += 1; redo if $g < 4;; puts 417}; puts 418; method_use_lambda_and_yield_2(&p); puts 419; end; +begin; puts 420; m_31; puts 421; rescue; puts 422; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 423; $g += 1; redo if $g < 4;; puts 424}; puts 425; method_yield_in_loop(&p); puts 426; rescue; puts 427; puts $!.class; end +$g = 0; def m_32; p = Proc.new{ puts 428; $g += 1; redo if $g < 4;; puts 429}; puts 430; method_yield_in_loop(&p); puts 431; end; +begin; puts 432; m_32; puts 433; rescue; puts 434; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 435; $g += 1; redo if $g < 4;; puts 436}; puts 437; method_call_in_loop(&p); puts 438; rescue; puts 439; puts $!.class; end +$g = 0; def m_33; p = Proc.new{ puts 440; $g += 1; redo if $g < 4;; puts 441}; puts 442; method_call_in_loop(&p); puts 443; end; +begin; puts 444; m_33; puts 445; rescue; puts 446; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_block{ puts 447; $g += 1; redo if $g < 4;; puts 448}; puts 449; iterator_via_yield(&p); puts 450; rescue; puts 451; puts $!.class; end +$g = 0; def m_34; p = get_block{ puts 452; $g += 1; redo if $g < 4;; puts 453}; puts 454; iterator_via_yield(&p); puts 455; end; +begin; puts 456; m_34; puts 457; rescue; puts 458; puts $!.class; end +$g = 0; begin; p = get_block{ puts 459; $g += 1; redo if $g < 4;; puts 460}; puts 461; iterator_via_call(&p); puts 462; rescue; puts 463; puts $!.class; end +$g = 0; def m_35; p = get_block{ puts 464; $g += 1; redo if $g < 4;; puts 465}; puts 466; iterator_via_call(&p); puts 467; end; +begin; puts 468; m_35; puts 469; rescue; puts 470; puts $!.class; end +$g = 0; begin; p = get_block{ puts 471; $g += 1; redo if $g < 4;; puts 472}; puts 473; method_call_iterator_via_yield(&p); puts 474; rescue; puts 475; puts $!.class; end +$g = 0; def m_36; p = get_block{ puts 476; $g += 1; redo if $g < 4;; puts 477}; puts 478; method_call_iterator_via_yield(&p); puts 479; end; +begin; puts 480; m_36; puts 481; rescue; puts 482; puts $!.class; end +$g = 0; begin; p = get_block{ puts 483; $g += 1; redo if $g < 4;; puts 484}; puts 485; method_call_iterator_via_call(&p); puts 486; rescue; puts 487; puts $!.class; end +$g = 0; def m_37; p = get_block{ puts 488; $g += 1; redo if $g < 4;; puts 489}; puts 490; method_call_iterator_via_call(&p); puts 491; end; +begin; puts 492; m_37; puts 493; rescue; puts 494; puts $!.class; end +$g = 0; begin; p = get_block{ puts 495; $g += 1; redo if $g < 4;; puts 496}; puts 497; method_use_lambda_and_yield(&p); puts 498; rescue; puts 499; puts $!.class; end +$g = 0; def m_38; p = get_block{ puts 500; $g += 1; redo if $g < 4;; puts 501}; puts 502; method_use_lambda_and_yield(&p); puts 503; end; +begin; puts 504; m_38; puts 505; rescue; puts 506; puts $!.class; end +$g = 0; begin; p = get_block{ puts 507; $g += 1; redo if $g < 4;; puts 508}; puts 509; method_use_proc_and_yield(&p); puts 510; rescue; puts 511; puts $!.class; end +$g = 0; def m_39; p = get_block{ puts 512; $g += 1; redo if $g < 4;; puts 513}; puts 514; method_use_proc_and_yield(&p); puts 515; end; +begin; puts 516; m_39; puts 517; rescue; puts 518; puts $!.class; end +$g = 0; begin; p = get_block{ puts 519; $g += 1; redo if $g < 4;; puts 520}; puts 521; method_use_lambda_and_call(&p); puts 522; rescue; puts 523; puts $!.class; end +$g = 0; def m_40; p = get_block{ puts 524; $g += 1; redo if $g < 4;; puts 525}; puts 526; method_use_lambda_and_call(&p); puts 527; end; +begin; puts 528; m_40; puts 529; rescue; puts 530; puts $!.class; end +$g = 0; begin; p = get_block{ puts 531; $g += 1; redo if $g < 4;; puts 532}; puts 533; method_use_proc_and_call(&p); puts 534; rescue; puts 535; puts $!.class; end +$g = 0; def m_41; p = get_block{ puts 536; $g += 1; redo if $g < 4;; puts 537}; puts 538; method_use_proc_and_call(&p); puts 539; end; +begin; puts 540; m_41; puts 541; rescue; puts 542; puts $!.class; end +$g = 0; begin; p = get_block{ puts 543; $g += 1; redo if $g < 4;; puts 544}; puts 545; method_use_lambda_and_yield_2(&p); puts 546; rescue; puts 547; puts $!.class; end +$g = 0; def m_42; p = get_block{ puts 548; $g += 1; redo if $g < 4;; puts 549}; puts 550; method_use_lambda_and_yield_2(&p); puts 551; end; +begin; puts 552; m_42; puts 553; rescue; puts 554; puts $!.class; end +$g = 0; begin; p = get_block{ puts 555; $g += 1; redo if $g < 4;; puts 556}; puts 557; method_yield_in_loop(&p); puts 558; rescue; puts 559; puts $!.class; end +$g = 0; def m_43; p = get_block{ puts 560; $g += 1; redo if $g < 4;; puts 561}; puts 562; method_yield_in_loop(&p); puts 563; end; +begin; puts 564; m_43; puts 565; rescue; puts 566; puts $!.class; end +$g = 0; begin; p = get_block{ puts 567; $g += 1; redo if $g < 4;; puts 568}; puts 569; method_call_in_loop(&p); puts 570; rescue; puts 571; puts $!.class; end +$g = 0; def m_44; p = get_block{ puts 572; $g += 1; redo if $g < 4;; puts 573}; puts 574; method_call_in_loop(&p); puts 575; end; +begin; puts 576; m_44; puts 577; rescue; puts 578; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_lambda{ puts 579; $g += 1; redo if $g < 4;; puts 580}; puts 581; iterator_via_yield(&p); puts 582; rescue; puts 583; puts $!.class; end +$g = 0; def m_45; p = get_lambda{ puts 584; $g += 1; redo if $g < 4;; puts 585}; puts 586; iterator_via_yield(&p); puts 587; end; +begin; puts 588; m_45; puts 589; rescue; puts 590; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 591; $g += 1; redo if $g < 4;; puts 592}; puts 593; iterator_via_call(&p); puts 594; rescue; puts 595; puts $!.class; end +$g = 0; def m_46; p = get_lambda{ puts 596; $g += 1; redo if $g < 4;; puts 597}; puts 598; iterator_via_call(&p); puts 599; end; +begin; puts 600; m_46; puts 601; rescue; puts 602; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 603; $g += 1; redo if $g < 4;; puts 604}; puts 605; method_call_iterator_via_yield(&p); puts 606; rescue; puts 607; puts $!.class; end +$g = 0; def m_47; p = get_lambda{ puts 608; $g += 1; redo if $g < 4;; puts 609}; puts 610; method_call_iterator_via_yield(&p); puts 611; end; +begin; puts 612; m_47; puts 613; rescue; puts 614; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 615; $g += 1; redo if $g < 4;; puts 616}; puts 617; method_call_iterator_via_call(&p); puts 618; rescue; puts 619; puts $!.class; end +$g = 0; def m_48; p = get_lambda{ puts 620; $g += 1; redo if $g < 4;; puts 621}; puts 622; method_call_iterator_via_call(&p); puts 623; end; +begin; puts 624; m_48; puts 625; rescue; puts 626; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 627; $g += 1; redo if $g < 4;; puts 628}; puts 629; method_use_lambda_and_yield(&p); puts 630; rescue; puts 631; puts $!.class; end +$g = 0; def m_49; p = get_lambda{ puts 632; $g += 1; redo if $g < 4;; puts 633}; puts 634; method_use_lambda_and_yield(&p); puts 635; end; +begin; puts 636; m_49; puts 637; rescue; puts 638; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 639; $g += 1; redo if $g < 4;; puts 640}; puts 641; method_use_proc_and_yield(&p); puts 642; rescue; puts 643; puts $!.class; end +$g = 0; def m_50; p = get_lambda{ puts 644; $g += 1; redo if $g < 4;; puts 645}; puts 646; method_use_proc_and_yield(&p); puts 647; end; +begin; puts 648; m_50; puts 649; rescue; puts 650; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 651; $g += 1; redo if $g < 4;; puts 652}; puts 653; method_use_lambda_and_call(&p); puts 654; rescue; puts 655; puts $!.class; end +$g = 0; def m_51; p = get_lambda{ puts 656; $g += 1; redo if $g < 4;; puts 657}; puts 658; method_use_lambda_and_call(&p); puts 659; end; +begin; puts 660; m_51; puts 661; rescue; puts 662; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 663; $g += 1; redo if $g < 4;; puts 664}; puts 665; method_use_proc_and_call(&p); puts 666; rescue; puts 667; puts $!.class; end +$g = 0; def m_52; p = get_lambda{ puts 668; $g += 1; redo if $g < 4;; puts 669}; puts 670; method_use_proc_and_call(&p); puts 671; end; +begin; puts 672; m_52; puts 673; rescue; puts 674; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 675; $g += 1; redo if $g < 4;; puts 676}; puts 677; method_use_lambda_and_yield_2(&p); puts 678; rescue; puts 679; puts $!.class; end +$g = 0; def m_53; p = get_lambda{ puts 680; $g += 1; redo if $g < 4;; puts 681}; puts 682; method_use_lambda_and_yield_2(&p); puts 683; end; +begin; puts 684; m_53; puts 685; rescue; puts 686; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 687; $g += 1; redo if $g < 4;; puts 688}; puts 689; method_yield_in_loop(&p); puts 690; rescue; puts 691; puts $!.class; end +$g = 0; def m_54; p = get_lambda{ puts 692; $g += 1; redo if $g < 4;; puts 693}; puts 694; method_yield_in_loop(&p); puts 695; end; +begin; puts 696; m_54; puts 697; rescue; puts 698; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 699; $g += 1; redo if $g < 4;; puts 700}; puts 701; method_call_in_loop(&p); puts 702; rescue; puts 703; puts $!.class; end +$g = 0; def m_55; p = get_lambda{ puts 704; $g += 1; redo if $g < 4;; puts 705}; puts 706; method_call_in_loop(&p); puts 707; end; +begin; puts 708; m_55; puts 709; rescue; puts 710; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_proc{ puts 711; $g += 1; redo if $g < 4;; puts 712}; puts 713; iterator_via_yield(&p); puts 714; rescue; puts 715; puts $!.class; end +$g = 0; def m_56; p = get_proc{ puts 716; $g += 1; redo if $g < 4;; puts 717}; puts 718; iterator_via_yield(&p); puts 719; end; +begin; puts 720; m_56; puts 721; rescue; puts 722; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 723; $g += 1; redo if $g < 4;; puts 724}; puts 725; iterator_via_call(&p); puts 726; rescue; puts 727; puts $!.class; end +$g = 0; def m_57; p = get_proc{ puts 728; $g += 1; redo if $g < 4;; puts 729}; puts 730; iterator_via_call(&p); puts 731; end; +begin; puts 732; m_57; puts 733; rescue; puts 734; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 735; $g += 1; redo if $g < 4;; puts 736}; puts 737; method_call_iterator_via_yield(&p); puts 738; rescue; puts 739; puts $!.class; end +$g = 0; def m_58; p = get_proc{ puts 740; $g += 1; redo if $g < 4;; puts 741}; puts 742; method_call_iterator_via_yield(&p); puts 743; end; +begin; puts 744; m_58; puts 745; rescue; puts 746; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 747; $g += 1; redo if $g < 4;; puts 748}; puts 749; method_call_iterator_via_call(&p); puts 750; rescue; puts 751; puts $!.class; end +$g = 0; def m_59; p = get_proc{ puts 752; $g += 1; redo if $g < 4;; puts 753}; puts 754; method_call_iterator_via_call(&p); puts 755; end; +begin; puts 756; m_59; puts 757; rescue; puts 758; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 759; $g += 1; redo if $g < 4;; puts 760}; puts 761; method_use_lambda_and_yield(&p); puts 762; rescue; puts 763; puts $!.class; end +$g = 0; def m_60; p = get_proc{ puts 764; $g += 1; redo if $g < 4;; puts 765}; puts 766; method_use_lambda_and_yield(&p); puts 767; end; +begin; puts 768; m_60; puts 769; rescue; puts 770; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 771; $g += 1; redo if $g < 4;; puts 772}; puts 773; method_use_proc_and_yield(&p); puts 774; rescue; puts 775; puts $!.class; end +$g = 0; def m_61; p = get_proc{ puts 776; $g += 1; redo if $g < 4;; puts 777}; puts 778; method_use_proc_and_yield(&p); puts 779; end; +begin; puts 780; m_61; puts 781; rescue; puts 782; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 783; $g += 1; redo if $g < 4;; puts 784}; puts 785; method_use_lambda_and_call(&p); puts 786; rescue; puts 787; puts $!.class; end +$g = 0; def m_62; p = get_proc{ puts 788; $g += 1; redo if $g < 4;; puts 789}; puts 790; method_use_lambda_and_call(&p); puts 791; end; +begin; puts 792; m_62; puts 793; rescue; puts 794; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 795; $g += 1; redo if $g < 4;; puts 796}; puts 797; method_use_proc_and_call(&p); puts 798; rescue; puts 799; puts $!.class; end +$g = 0; def m_63; p = get_proc{ puts 800; $g += 1; redo if $g < 4;; puts 801}; puts 802; method_use_proc_and_call(&p); puts 803; end; +begin; puts 804; m_63; puts 805; rescue; puts 806; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 807; $g += 1; redo if $g < 4;; puts 808}; puts 809; method_use_lambda_and_yield_2(&p); puts 810; rescue; puts 811; puts $!.class; end +$g = 0; def m_64; p = get_proc{ puts 812; $g += 1; redo if $g < 4;; puts 813}; puts 814; method_use_lambda_and_yield_2(&p); puts 815; end; +begin; puts 816; m_64; puts 817; rescue; puts 818; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 819; $g += 1; redo if $g < 4;; puts 820}; puts 821; method_yield_in_loop(&p); puts 822; rescue; puts 823; puts $!.class; end +$g = 0; def m_65; p = get_proc{ puts 824; $g += 1; redo if $g < 4;; puts 825}; puts 826; method_yield_in_loop(&p); puts 827; end; +begin; puts 828; m_65; puts 829; rescue; puts 830; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 831; $g += 1; redo if $g < 4;; puts 832}; puts 833; method_call_in_loop(&p); puts 834; rescue; puts 835; puts $!.class; end +$g = 0; def m_66; p = get_proc{ puts 836; $g += 1; redo if $g < 4;; puts 837}; puts 838; method_call_in_loop(&p); puts 839; end; +begin; puts 840; m_66; puts 841; rescue; puts 842; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_block; puts 843; iterator_via_yield(&p); puts 844; rescue; puts 845; puts $!.class; end +$g = 0; def m_67; p = get_local_block; puts 846; iterator_via_yield(&p); puts 847; end; +begin; puts 848; m_67; puts 849; rescue; puts 850; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 851; iterator_via_call(&p); puts 852; rescue; puts 853; puts $!.class; end +$g = 0; def m_68; p = get_local_block; puts 854; iterator_via_call(&p); puts 855; end; +begin; puts 856; m_68; puts 857; rescue; puts 858; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 859; method_call_iterator_via_yield(&p); puts 860; rescue; puts 861; puts $!.class; end +$g = 0; def m_69; p = get_local_block; puts 862; method_call_iterator_via_yield(&p); puts 863; end; +begin; puts 864; m_69; puts 865; rescue; puts 866; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 867; method_call_iterator_via_call(&p); puts 868; rescue; puts 869; puts $!.class; end +$g = 0; def m_70; p = get_local_block; puts 870; method_call_iterator_via_call(&p); puts 871; end; +begin; puts 872; m_70; puts 873; rescue; puts 874; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 875; method_use_lambda_and_yield(&p); puts 876; rescue; puts 877; puts $!.class; end +$g = 0; def m_71; p = get_local_block; puts 878; method_use_lambda_and_yield(&p); puts 879; end; +begin; puts 880; m_71; puts 881; rescue; puts 882; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 883; method_use_proc_and_yield(&p); puts 884; rescue; puts 885; puts $!.class; end +$g = 0; def m_72; p = get_local_block; puts 886; method_use_proc_and_yield(&p); puts 887; end; +begin; puts 888; m_72; puts 889; rescue; puts 890; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 891; method_use_lambda_and_call(&p); puts 892; rescue; puts 893; puts $!.class; end +$g = 0; def m_73; p = get_local_block; puts 894; method_use_lambda_and_call(&p); puts 895; end; +begin; puts 896; m_73; puts 897; rescue; puts 898; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 899; method_use_proc_and_call(&p); puts 900; rescue; puts 901; puts $!.class; end +$g = 0; def m_74; p = get_local_block; puts 902; method_use_proc_and_call(&p); puts 903; end; +begin; puts 904; m_74; puts 905; rescue; puts 906; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 907; method_use_lambda_and_yield_2(&p); puts 908; rescue; puts 909; puts $!.class; end +$g = 0; def m_75; p = get_local_block; puts 910; method_use_lambda_and_yield_2(&p); puts 911; end; +begin; puts 912; m_75; puts 913; rescue; puts 914; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 915; method_yield_in_loop(&p); puts 916; rescue; puts 917; puts $!.class; end +$g = 0; def m_76; p = get_local_block; puts 918; method_yield_in_loop(&p); puts 919; end; +begin; puts 920; m_76; puts 921; rescue; puts 922; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 923; method_call_in_loop(&p); puts 924; rescue; puts 925; puts $!.class; end +$g = 0; def m_77; p = get_local_block; puts 926; method_call_in_loop(&p); puts 927; end; +begin; puts 928; m_77; puts 929; rescue; puts 930; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_lambda; puts 931; iterator_via_yield(&p); puts 932; rescue; puts 933; puts $!.class; end +$g = 0; def m_78; p = get_local_lambda; puts 934; iterator_via_yield(&p); puts 935; end; +begin; puts 936; m_78; puts 937; rescue; puts 938; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 939; iterator_via_call(&p); puts 940; rescue; puts 941; puts $!.class; end +$g = 0; def m_79; p = get_local_lambda; puts 942; iterator_via_call(&p); puts 943; end; +begin; puts 944; m_79; puts 945; rescue; puts 946; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 947; method_call_iterator_via_yield(&p); puts 948; rescue; puts 949; puts $!.class; end +$g = 0; def m_80; p = get_local_lambda; puts 950; method_call_iterator_via_yield(&p); puts 951; end; +begin; puts 952; m_80; puts 953; rescue; puts 954; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 955; method_call_iterator_via_call(&p); puts 956; rescue; puts 957; puts $!.class; end +$g = 0; def m_81; p = get_local_lambda; puts 958; method_call_iterator_via_call(&p); puts 959; end; +begin; puts 960; m_81; puts 961; rescue; puts 962; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 963; method_use_lambda_and_yield(&p); puts 964; rescue; puts 965; puts $!.class; end +$g = 0; def m_82; p = get_local_lambda; puts 966; method_use_lambda_and_yield(&p); puts 967; end; +begin; puts 968; m_82; puts 969; rescue; puts 970; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 971; method_use_proc_and_yield(&p); puts 972; rescue; puts 973; puts $!.class; end +$g = 0; def m_83; p = get_local_lambda; puts 974; method_use_proc_and_yield(&p); puts 975; end; +begin; puts 976; m_83; puts 977; rescue; puts 978; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 979; method_use_lambda_and_call(&p); puts 980; rescue; puts 981; puts $!.class; end +$g = 0; def m_84; p = get_local_lambda; puts 982; method_use_lambda_and_call(&p); puts 983; end; +begin; puts 984; m_84; puts 985; rescue; puts 986; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 987; method_use_proc_and_call(&p); puts 988; rescue; puts 989; puts $!.class; end +$g = 0; def m_85; p = get_local_lambda; puts 990; method_use_proc_and_call(&p); puts 991; end; +begin; puts 992; m_85; puts 993; rescue; puts 994; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 995; method_use_lambda_and_yield_2(&p); puts 996; rescue; puts 997; puts $!.class; end +$g = 0; def m_86; p = get_local_lambda; puts 998; method_use_lambda_and_yield_2(&p); puts 999; end; +begin; puts 1000; m_86; puts 1001; rescue; puts 1002; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1003; method_yield_in_loop(&p); puts 1004; rescue; puts 1005; puts $!.class; end +$g = 0; def m_87; p = get_local_lambda; puts 1006; method_yield_in_loop(&p); puts 1007; end; +begin; puts 1008; m_87; puts 1009; rescue; puts 1010; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1011; method_call_in_loop(&p); puts 1012; rescue; puts 1013; puts $!.class; end +$g = 0; def m_88; p = get_local_lambda; puts 1014; method_call_in_loop(&p); puts 1015; end; +begin; puts 1016; m_88; puts 1017; rescue; puts 1018; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_proc; puts 1019; iterator_via_yield(&p); puts 1020; rescue; puts 1021; puts $!.class; end +$g = 0; def m_89; p = get_local_proc; puts 1022; iterator_via_yield(&p); puts 1023; end; +begin; puts 1024; m_89; puts 1025; rescue; puts 1026; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1027; iterator_via_call(&p); puts 1028; rescue; puts 1029; puts $!.class; end +$g = 0; def m_90; p = get_local_proc; puts 1030; iterator_via_call(&p); puts 1031; end; +begin; puts 1032; m_90; puts 1033; rescue; puts 1034; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1035; method_call_iterator_via_yield(&p); puts 1036; rescue; puts 1037; puts $!.class; end +$g = 0; def m_91; p = get_local_proc; puts 1038; method_call_iterator_via_yield(&p); puts 1039; end; +begin; puts 1040; m_91; puts 1041; rescue; puts 1042; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1043; method_call_iterator_via_call(&p); puts 1044; rescue; puts 1045; puts $!.class; end +$g = 0; def m_92; p = get_local_proc; puts 1046; method_call_iterator_via_call(&p); puts 1047; end; +begin; puts 1048; m_92; puts 1049; rescue; puts 1050; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1051; method_use_lambda_and_yield(&p); puts 1052; rescue; puts 1053; puts $!.class; end +$g = 0; def m_93; p = get_local_proc; puts 1054; method_use_lambda_and_yield(&p); puts 1055; end; +begin; puts 1056; m_93; puts 1057; rescue; puts 1058; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1059; method_use_proc_and_yield(&p); puts 1060; rescue; puts 1061; puts $!.class; end +$g = 0; def m_94; p = get_local_proc; puts 1062; method_use_proc_and_yield(&p); puts 1063; end; +begin; puts 1064; m_94; puts 1065; rescue; puts 1066; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1067; method_use_lambda_and_call(&p); puts 1068; rescue; puts 1069; puts $!.class; end +$g = 0; def m_95; p = get_local_proc; puts 1070; method_use_lambda_and_call(&p); puts 1071; end; +begin; puts 1072; m_95; puts 1073; rescue; puts 1074; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1075; method_use_proc_and_call(&p); puts 1076; rescue; puts 1077; puts $!.class; end +$g = 0; def m_96; p = get_local_proc; puts 1078; method_use_proc_and_call(&p); puts 1079; end; +begin; puts 1080; m_96; puts 1081; rescue; puts 1082; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1083; method_use_lambda_and_yield_2(&p); puts 1084; rescue; puts 1085; puts $!.class; end +$g = 0; def m_97; p = get_local_proc; puts 1086; method_use_lambda_and_yield_2(&p); puts 1087; end; +begin; puts 1088; m_97; puts 1089; rescue; puts 1090; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1091; method_yield_in_loop(&p); puts 1092; rescue; puts 1093; puts $!.class; end +$g = 0; def m_98; p = get_local_proc; puts 1094; method_yield_in_loop(&p); puts 1095; end; +begin; puts 1096; m_98; puts 1097; rescue; puts 1098; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1099; method_call_in_loop(&p); puts 1100; rescue; puts 1101; puts $!.class; end +$g = 0; def m_99; p = get_local_proc; puts 1102; method_call_in_loop(&p); puts 1103; end; +begin; puts 1104; m_99; puts 1105; rescue; puts 1106; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1107; p = lambda{ puts 1108; $g += 1; redo if $g < 4;; puts 1109}; puts(p.call); puts 1110; rescue; puts 1111; puts $!.class; end +$g = 0; def m_100; puts 1112; p = lambda{ puts 1113; $g += 1; redo if $g < 4;; puts 1114}; puts(p.call); puts 1115; end; +begin; puts 1116; m_100; puts 1117; rescue; puts 1118; puts $!.class; end +$g = 0; begin; puts 1119; puts m_100; puts 1120; rescue; puts 1121; puts $!.class; end +$g = 0; def m_101; puts 1122; puts m_100; puts 1123; end; +begin; puts 1124; m_101; puts 1125; rescue; puts 1126; puts $!.class; end +$g = 0; begin; puts 1127; p = Proc.new{ puts 1128; $g += 1; redo if $g < 4;; puts 1129}; puts(p.call); puts 1130; rescue; puts 1131; puts $!.class; end +$g = 0; def m_102; puts 1132; p = Proc.new{ puts 1133; $g += 1; redo if $g < 4;; puts 1134}; puts(p.call); puts 1135; end; +begin; puts 1136; m_102; puts 1137; rescue; puts 1138; puts $!.class; end +$g = 0; begin; puts 1139; puts m_102; puts 1140; rescue; puts 1141; puts $!.class; end +$g = 0; def m_103; puts 1142; puts m_102; puts 1143; end; +begin; puts 1144; m_103; puts 1145; rescue; puts 1146; puts $!.class; end +$g = 0; begin; puts 1147; p = get_block{ puts 1148; $g += 1; redo if $g < 4;; puts 1149}; puts(p.call); puts 1150; rescue; puts 1151; puts $!.class; end +$g = 0; def m_104; puts 1152; p = get_block{ puts 1153; $g += 1; redo if $g < 4;; puts 1154}; puts(p.call); puts 1155; end; +begin; puts 1156; m_104; puts 1157; rescue; puts 1158; puts $!.class; end +$g = 0; begin; puts 1159; puts m_104; puts 1160; rescue; puts 1161; puts $!.class; end +$g = 0; def m_105; puts 1162; puts m_104; puts 1163; end; +begin; puts 1164; m_105; puts 1165; rescue; puts 1166; puts $!.class; end +$g = 0; begin; puts 1167; p = get_lambda{ puts 1168; $g += 1; redo if $g < 4;; puts 1169}; puts(p.call); puts 1170; rescue; puts 1171; puts $!.class; end +$g = 0; def m_106; puts 1172; p = get_lambda{ puts 1173; $g += 1; redo if $g < 4;; puts 1174}; puts(p.call); puts 1175; end; +begin; puts 1176; m_106; puts 1177; rescue; puts 1178; puts $!.class; end +$g = 0; begin; puts 1179; puts m_106; puts 1180; rescue; puts 1181; puts $!.class; end +$g = 0; def m_107; puts 1182; puts m_106; puts 1183; end; +begin; puts 1184; m_107; puts 1185; rescue; puts 1186; puts $!.class; end +$g = 0; begin; puts 1187; p = get_proc{ puts 1188; $g += 1; redo if $g < 4;; puts 1189}; puts(p.call); puts 1190; rescue; puts 1191; puts $!.class; end +$g = 0; def m_108; puts 1192; p = get_proc{ puts 1193; $g += 1; redo if $g < 4;; puts 1194}; puts(p.call); puts 1195; end; +begin; puts 1196; m_108; puts 1197; rescue; puts 1198; puts $!.class; end +$g = 0; begin; puts 1199; puts m_108; puts 1200; rescue; puts 1201; puts $!.class; end +$g = 0; def m_109; puts 1202; puts m_108; puts 1203; end; +begin; puts 1204; m_109; puts 1205; rescue; puts 1206; puts $!.class; end +$g = 0; begin; puts 1207; p = get_local_block; puts(p.call); puts 1208; rescue; puts 1209; puts $!.class; end +$g = 0; def m_110; puts 1210; p = get_local_block; puts(p.call); puts 1211; end; +begin; puts 1212; m_110; puts 1213; rescue; puts 1214; puts $!.class; end +$g = 0; begin; puts 1215; puts m_110; puts 1216; rescue; puts 1217; puts $!.class; end +$g = 0; def m_111; puts 1218; puts m_110; puts 1219; end; +begin; puts 1220; m_111; puts 1221; rescue; puts 1222; puts $!.class; end +$g = 0; begin; puts 1223; p = get_local_lambda; puts(p.call); puts 1224; rescue; puts 1225; puts $!.class; end +$g = 0; def m_112; puts 1226; p = get_local_lambda; puts(p.call); puts 1227; end; +begin; puts 1228; m_112; puts 1229; rescue; puts 1230; puts $!.class; end +$g = 0; begin; puts 1231; puts m_112; puts 1232; rescue; puts 1233; puts $!.class; end +$g = 0; def m_113; puts 1234; puts m_112; puts 1235; end; +begin; puts 1236; m_113; puts 1237; rescue; puts 1238; puts $!.class; end +$g = 0; begin; puts 1239; p = get_local_proc; puts(p.call); puts 1240; rescue; puts 1241; puts $!.class; end +$g = 0; def m_114; puts 1242; p = get_local_proc; puts(p.call); puts 1243; end; +begin; puts 1244; m_114; puts 1245; rescue; puts 1246; puts $!.class; end +$g = 0; begin; puts 1247; puts m_114; puts 1248; rescue; puts 1249; puts $!.class; end +$g = 0; def m_115; puts 1250; puts m_114; puts 1251; end; +begin; puts 1252; m_115; puts 1253; rescue; puts 1254; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1255; x = lambda { puts 1256; p = lambda{ puts 1257; $g += 1; redo if $g < 4;; puts 1258}; puts p.call; puts 1259}; puts x.call; puts 1260; rescue; puts 1261; puts $!.class; end +$g = 0; def m_116; puts 1262; x = lambda { puts 1263; p = lambda{ puts 1264; $g += 1; redo if $g < 4;; puts 1265}; puts p.call; puts 1266}; puts x.call; puts 1267; end; +begin; puts 1268; m_116; puts 1269; rescue; puts 1270; puts $!.class; end +$g = 0; begin; puts 1271; x = lambda { puts 1272; p = Proc.new{ puts 1273; $g += 1; redo if $g < 4;; puts 1274}; puts p.call; puts 1275}; puts x.call; puts 1276; rescue; puts 1277; puts $!.class; end +$g = 0; def m_117; puts 1278; x = lambda { puts 1279; p = Proc.new{ puts 1280; $g += 1; redo if $g < 4;; puts 1281}; puts p.call; puts 1282}; puts x.call; puts 1283; end; +begin; puts 1284; m_117; puts 1285; rescue; puts 1286; puts $!.class; end +$g = 0; begin; puts 1287; x = lambda { puts 1288; p = get_block{ puts 1289; $g += 1; redo if $g < 4;; puts 1290}; puts p.call; puts 1291}; puts x.call; puts 1292; rescue; puts 1293; puts $!.class; end +$g = 0; def m_118; puts 1294; x = lambda { puts 1295; p = get_block{ puts 1296; $g += 1; redo if $g < 4;; puts 1297}; puts p.call; puts 1298}; puts x.call; puts 1299; end; +begin; puts 1300; m_118; puts 1301; rescue; puts 1302; puts $!.class; end +$g = 0; begin; puts 1303; x = lambda { puts 1304; p = get_lambda{ puts 1305; $g += 1; redo if $g < 4;; puts 1306}; puts p.call; puts 1307}; puts x.call; puts 1308; rescue; puts 1309; puts $!.class; end +$g = 0; def m_119; puts 1310; x = lambda { puts 1311; p = get_lambda{ puts 1312; $g += 1; redo if $g < 4;; puts 1313}; puts p.call; puts 1314}; puts x.call; puts 1315; end; +begin; puts 1316; m_119; puts 1317; rescue; puts 1318; puts $!.class; end +$g = 0; begin; puts 1319; x = lambda { puts 1320; p = get_proc{ puts 1321; $g += 1; redo if $g < 4;; puts 1322}; puts p.call; puts 1323}; puts x.call; puts 1324; rescue; puts 1325; puts $!.class; end +$g = 0; def m_120; puts 1326; x = lambda { puts 1327; p = get_proc{ puts 1328; $g += 1; redo if $g < 4;; puts 1329}; puts p.call; puts 1330}; puts x.call; puts 1331; end; +begin; puts 1332; m_120; puts 1333; rescue; puts 1334; puts $!.class; end +$g = 0; begin; puts 1335; x = lambda { puts 1336; p = get_local_block; puts p.call; puts 1337}; puts x.call; puts 1338; rescue; puts 1339; puts $!.class; end +$g = 0; def m_121; puts 1340; x = lambda { puts 1341; p = get_local_block; puts p.call; puts 1342}; puts x.call; puts 1343; end; +begin; puts 1344; m_121; puts 1345; rescue; puts 1346; puts $!.class; end +$g = 0; begin; puts 1347; x = lambda { puts 1348; p = get_local_lambda; puts p.call; puts 1349}; puts x.call; puts 1350; rescue; puts 1351; puts $!.class; end +$g = 0; def m_122; puts 1352; x = lambda { puts 1353; p = get_local_lambda; puts p.call; puts 1354}; puts x.call; puts 1355; end; +begin; puts 1356; m_122; puts 1357; rescue; puts 1358; puts $!.class; end +$g = 0; begin; puts 1359; x = lambda { puts 1360; p = get_local_proc; puts p.call; puts 1361}; puts x.call; puts 1362; rescue; puts 1363; puts $!.class; end +$g = 0; def m_123; puts 1364; x = lambda { puts 1365; p = get_local_proc; puts p.call; puts 1366}; puts x.call; puts 1367; end; +begin; puts 1368; m_123; puts 1369; rescue; puts 1370; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1371; x = Proc.new { puts 1372; p = lambda{ puts 1373; $g += 1; redo if $g < 4;; puts 1374}; puts p.call; puts 1375}; puts x.call; puts 1376; rescue; puts 1377; puts $!.class; end +$g = 0; def m_124; puts 1378; x = Proc.new { puts 1379; p = lambda{ puts 1380; $g += 1; redo if $g < 4;; puts 1381}; puts p.call; puts 1382}; puts x.call; puts 1383; end; +begin; puts 1384; m_124; puts 1385; rescue; puts 1386; puts $!.class; end +$g = 0; begin; puts 1387; x = Proc.new { puts 1388; p = Proc.new{ puts 1389; $g += 1; redo if $g < 4;; puts 1390}; puts p.call; puts 1391}; puts x.call; puts 1392; rescue; puts 1393; puts $!.class; end +$g = 0; def m_125; puts 1394; x = Proc.new { puts 1395; p = Proc.new{ puts 1396; $g += 1; redo if $g < 4;; puts 1397}; puts p.call; puts 1398}; puts x.call; puts 1399; end; +begin; puts 1400; m_125; puts 1401; rescue; puts 1402; puts $!.class; end +$g = 0; begin; puts 1403; x = Proc.new { puts 1404; p = get_block{ puts 1405; $g += 1; redo if $g < 4;; puts 1406}; puts p.call; puts 1407}; puts x.call; puts 1408; rescue; puts 1409; puts $!.class; end +$g = 0; def m_126; puts 1410; x = Proc.new { puts 1411; p = get_block{ puts 1412; $g += 1; redo if $g < 4;; puts 1413}; puts p.call; puts 1414}; puts x.call; puts 1415; end; +begin; puts 1416; m_126; puts 1417; rescue; puts 1418; puts $!.class; end +$g = 0; begin; puts 1419; x = Proc.new { puts 1420; p = get_lambda{ puts 1421; $g += 1; redo if $g < 4;; puts 1422}; puts p.call; puts 1423}; puts x.call; puts 1424; rescue; puts 1425; puts $!.class; end +$g = 0; def m_127; puts 1426; x = Proc.new { puts 1427; p = get_lambda{ puts 1428; $g += 1; redo if $g < 4;; puts 1429}; puts p.call; puts 1430}; puts x.call; puts 1431; end; +begin; puts 1432; m_127; puts 1433; rescue; puts 1434; puts $!.class; end +$g = 0; begin; puts 1435; x = Proc.new { puts 1436; p = get_proc{ puts 1437; $g += 1; redo if $g < 4;; puts 1438}; puts p.call; puts 1439}; puts x.call; puts 1440; rescue; puts 1441; puts $!.class; end +$g = 0; def m_128; puts 1442; x = Proc.new { puts 1443; p = get_proc{ puts 1444; $g += 1; redo if $g < 4;; puts 1445}; puts p.call; puts 1446}; puts x.call; puts 1447; end; +begin; puts 1448; m_128; puts 1449; rescue; puts 1450; puts $!.class; end +$g = 0; begin; puts 1451; x = Proc.new { puts 1452; p = get_local_block; puts p.call; puts 1453}; puts x.call; puts 1454; rescue; puts 1455; puts $!.class; end +$g = 0; def m_129; puts 1456; x = Proc.new { puts 1457; p = get_local_block; puts p.call; puts 1458}; puts x.call; puts 1459; end; +begin; puts 1460; m_129; puts 1461; rescue; puts 1462; puts $!.class; end +$g = 0; begin; puts 1463; x = Proc.new { puts 1464; p = get_local_lambda; puts p.call; puts 1465}; puts x.call; puts 1466; rescue; puts 1467; puts $!.class; end +$g = 0; def m_130; puts 1468; x = Proc.new { puts 1469; p = get_local_lambda; puts p.call; puts 1470}; puts x.call; puts 1471; end; +begin; puts 1472; m_130; puts 1473; rescue; puts 1474; puts $!.class; end +$g = 0; begin; puts 1475; x = Proc.new { puts 1476; p = get_local_proc; puts p.call; puts 1477}; puts x.call; puts 1478; rescue; puts 1479; puts $!.class; end +$g = 0; def m_131; puts 1480; x = Proc.new { puts 1481; p = get_local_proc; puts p.call; puts 1482}; puts x.call; puts 1483; end; +begin; puts 1484; m_131; puts 1485; rescue; puts 1486; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1487; x = get_block { puts 1488; p = lambda{ puts 1489; $g += 1; redo if $g < 4;; puts 1490}; puts p.call; puts 1491}; puts x.call; puts 1492; rescue; puts 1493; puts $!.class; end +$g = 0; def m_132; puts 1494; x = get_block { puts 1495; p = lambda{ puts 1496; $g += 1; redo if $g < 4;; puts 1497}; puts p.call; puts 1498}; puts x.call; puts 1499; end; +begin; puts 1500; m_132; puts 1501; rescue; puts 1502; puts $!.class; end +$g = 0; begin; puts 1503; x = get_block { puts 1504; p = Proc.new{ puts 1505; $g += 1; redo if $g < 4;; puts 1506}; puts p.call; puts 1507}; puts x.call; puts 1508; rescue; puts 1509; puts $!.class; end +$g = 0; def m_133; puts 1510; x = get_block { puts 1511; p = Proc.new{ puts 1512; $g += 1; redo if $g < 4;; puts 1513}; puts p.call; puts 1514}; puts x.call; puts 1515; end; +begin; puts 1516; m_133; puts 1517; rescue; puts 1518; puts $!.class; end +$g = 0; begin; puts 1519; x = get_block { puts 1520; p = get_block{ puts 1521; $g += 1; redo if $g < 4;; puts 1522}; puts p.call; puts 1523}; puts x.call; puts 1524; rescue; puts 1525; puts $!.class; end +$g = 0; def m_134; puts 1526; x = get_block { puts 1527; p = get_block{ puts 1528; $g += 1; redo if $g < 4;; puts 1529}; puts p.call; puts 1530}; puts x.call; puts 1531; end; +begin; puts 1532; m_134; puts 1533; rescue; puts 1534; puts $!.class; end +$g = 0; begin; puts 1535; x = get_block { puts 1536; p = get_lambda{ puts 1537; $g += 1; redo if $g < 4;; puts 1538}; puts p.call; puts 1539}; puts x.call; puts 1540; rescue; puts 1541; puts $!.class; end +$g = 0; def m_135; puts 1542; x = get_block { puts 1543; p = get_lambda{ puts 1544; $g += 1; redo if $g < 4;; puts 1545}; puts p.call; puts 1546}; puts x.call; puts 1547; end; +begin; puts 1548; m_135; puts 1549; rescue; puts 1550; puts $!.class; end +$g = 0; begin; puts 1551; x = get_block { puts 1552; p = get_proc{ puts 1553; $g += 1; redo if $g < 4;; puts 1554}; puts p.call; puts 1555}; puts x.call; puts 1556; rescue; puts 1557; puts $!.class; end +$g = 0; def m_136; puts 1558; x = get_block { puts 1559; p = get_proc{ puts 1560; $g += 1; redo if $g < 4;; puts 1561}; puts p.call; puts 1562}; puts x.call; puts 1563; end; +begin; puts 1564; m_136; puts 1565; rescue; puts 1566; puts $!.class; end +$g = 0; begin; puts 1567; x = get_block { puts 1568; p = get_local_block; puts p.call; puts 1569}; puts x.call; puts 1570; rescue; puts 1571; puts $!.class; end +$g = 0; def m_137; puts 1572; x = get_block { puts 1573; p = get_local_block; puts p.call; puts 1574}; puts x.call; puts 1575; end; +begin; puts 1576; m_137; puts 1577; rescue; puts 1578; puts $!.class; end +$g = 0; begin; puts 1579; x = get_block { puts 1580; p = get_local_lambda; puts p.call; puts 1581}; puts x.call; puts 1582; rescue; puts 1583; puts $!.class; end +$g = 0; def m_138; puts 1584; x = get_block { puts 1585; p = get_local_lambda; puts p.call; puts 1586}; puts x.call; puts 1587; end; +begin; puts 1588; m_138; puts 1589; rescue; puts 1590; puts $!.class; end +$g = 0; begin; puts 1591; x = get_block { puts 1592; p = get_local_proc; puts p.call; puts 1593}; puts x.call; puts 1594; rescue; puts 1595; puts $!.class; end +$g = 0; def m_139; puts 1596; x = get_block { puts 1597; p = get_local_proc; puts p.call; puts 1598}; puts x.call; puts 1599; end; +begin; puts 1600; m_139; puts 1601; rescue; puts 1602; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1603; x = get_lambda { puts 1604; p = lambda{ puts 1605; $g += 1; redo if $g < 4;; puts 1606}; puts p.call; puts 1607}; puts x.call; puts 1608; rescue; puts 1609; puts $!.class; end +$g = 0; def m_140; puts 1610; x = get_lambda { puts 1611; p = lambda{ puts 1612; $g += 1; redo if $g < 4;; puts 1613}; puts p.call; puts 1614}; puts x.call; puts 1615; end; +begin; puts 1616; m_140; puts 1617; rescue; puts 1618; puts $!.class; end +$g = 0; begin; puts 1619; x = get_lambda { puts 1620; p = Proc.new{ puts 1621; $g += 1; redo if $g < 4;; puts 1622}; puts p.call; puts 1623}; puts x.call; puts 1624; rescue; puts 1625; puts $!.class; end +$g = 0; def m_141; puts 1626; x = get_lambda { puts 1627; p = Proc.new{ puts 1628; $g += 1; redo if $g < 4;; puts 1629}; puts p.call; puts 1630}; puts x.call; puts 1631; end; +begin; puts 1632; m_141; puts 1633; rescue; puts 1634; puts $!.class; end +$g = 0; begin; puts 1635; x = get_lambda { puts 1636; p = get_block{ puts 1637; $g += 1; redo if $g < 4;; puts 1638}; puts p.call; puts 1639}; puts x.call; puts 1640; rescue; puts 1641; puts $!.class; end +$g = 0; def m_142; puts 1642; x = get_lambda { puts 1643; p = get_block{ puts 1644; $g += 1; redo if $g < 4;; puts 1645}; puts p.call; puts 1646}; puts x.call; puts 1647; end; +begin; puts 1648; m_142; puts 1649; rescue; puts 1650; puts $!.class; end +$g = 0; begin; puts 1651; x = get_lambda { puts 1652; p = get_lambda{ puts 1653; $g += 1; redo if $g < 4;; puts 1654}; puts p.call; puts 1655}; puts x.call; puts 1656; rescue; puts 1657; puts $!.class; end +$g = 0; def m_143; puts 1658; x = get_lambda { puts 1659; p = get_lambda{ puts 1660; $g += 1; redo if $g < 4;; puts 1661}; puts p.call; puts 1662}; puts x.call; puts 1663; end; +begin; puts 1664; m_143; puts 1665; rescue; puts 1666; puts $!.class; end +$g = 0; begin; puts 1667; x = get_lambda { puts 1668; p = get_proc{ puts 1669; $g += 1; redo if $g < 4;; puts 1670}; puts p.call; puts 1671}; puts x.call; puts 1672; rescue; puts 1673; puts $!.class; end +$g = 0; def m_144; puts 1674; x = get_lambda { puts 1675; p = get_proc{ puts 1676; $g += 1; redo if $g < 4;; puts 1677}; puts p.call; puts 1678}; puts x.call; puts 1679; end; +begin; puts 1680; m_144; puts 1681; rescue; puts 1682; puts $!.class; end +$g = 0; begin; puts 1683; x = get_lambda { puts 1684; p = get_local_block; puts p.call; puts 1685}; puts x.call; puts 1686; rescue; puts 1687; puts $!.class; end +$g = 0; def m_145; puts 1688; x = get_lambda { puts 1689; p = get_local_block; puts p.call; puts 1690}; puts x.call; puts 1691; end; +begin; puts 1692; m_145; puts 1693; rescue; puts 1694; puts $!.class; end +$g = 0; begin; puts 1695; x = get_lambda { puts 1696; p = get_local_lambda; puts p.call; puts 1697}; puts x.call; puts 1698; rescue; puts 1699; puts $!.class; end +$g = 0; def m_146; puts 1700; x = get_lambda { puts 1701; p = get_local_lambda; puts p.call; puts 1702}; puts x.call; puts 1703; end; +begin; puts 1704; m_146; puts 1705; rescue; puts 1706; puts $!.class; end +$g = 0; begin; puts 1707; x = get_lambda { puts 1708; p = get_local_proc; puts p.call; puts 1709}; puts x.call; puts 1710; rescue; puts 1711; puts $!.class; end +$g = 0; def m_147; puts 1712; x = get_lambda { puts 1713; p = get_local_proc; puts p.call; puts 1714}; puts x.call; puts 1715; end; +begin; puts 1716; m_147; puts 1717; rescue; puts 1718; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1719; x = get_proc { puts 1720; p = lambda{ puts 1721; $g += 1; redo if $g < 4;; puts 1722}; puts p.call; puts 1723}; puts x.call; puts 1724; rescue; puts 1725; puts $!.class; end +$g = 0; def m_148; puts 1726; x = get_proc { puts 1727; p = lambda{ puts 1728; $g += 1; redo if $g < 4;; puts 1729}; puts p.call; puts 1730}; puts x.call; puts 1731; end; +begin; puts 1732; m_148; puts 1733; rescue; puts 1734; puts $!.class; end +$g = 0; begin; puts 1735; x = get_proc { puts 1736; p = Proc.new{ puts 1737; $g += 1; redo if $g < 4;; puts 1738}; puts p.call; puts 1739}; puts x.call; puts 1740; rescue; puts 1741; puts $!.class; end +$g = 0; def m_149; puts 1742; x = get_proc { puts 1743; p = Proc.new{ puts 1744; $g += 1; redo if $g < 4;; puts 1745}; puts p.call; puts 1746}; puts x.call; puts 1747; end; +begin; puts 1748; m_149; puts 1749; rescue; puts 1750; puts $!.class; end +$g = 0; begin; puts 1751; x = get_proc { puts 1752; p = get_block{ puts 1753; $g += 1; redo if $g < 4;; puts 1754}; puts p.call; puts 1755}; puts x.call; puts 1756; rescue; puts 1757; puts $!.class; end +$g = 0; def m_150; puts 1758; x = get_proc { puts 1759; p = get_block{ puts 1760; $g += 1; redo if $g < 4;; puts 1761}; puts p.call; puts 1762}; puts x.call; puts 1763; end; +begin; puts 1764; m_150; puts 1765; rescue; puts 1766; puts $!.class; end +$g = 0; begin; puts 1767; x = get_proc { puts 1768; p = get_lambda{ puts 1769; $g += 1; redo if $g < 4;; puts 1770}; puts p.call; puts 1771}; puts x.call; puts 1772; rescue; puts 1773; puts $!.class; end +$g = 0; def m_151; puts 1774; x = get_proc { puts 1775; p = get_lambda{ puts 1776; $g += 1; redo if $g < 4;; puts 1777}; puts p.call; puts 1778}; puts x.call; puts 1779; end; +begin; puts 1780; m_151; puts 1781; rescue; puts 1782; puts $!.class; end +$g = 0; begin; puts 1783; x = get_proc { puts 1784; p = get_proc{ puts 1785; $g += 1; redo if $g < 4;; puts 1786}; puts p.call; puts 1787}; puts x.call; puts 1788; rescue; puts 1789; puts $!.class; end +$g = 0; def m_152; puts 1790; x = get_proc { puts 1791; p = get_proc{ puts 1792; $g += 1; redo if $g < 4;; puts 1793}; puts p.call; puts 1794}; puts x.call; puts 1795; end; +begin; puts 1796; m_152; puts 1797; rescue; puts 1798; puts $!.class; end +$g = 0; begin; puts 1799; x = get_proc { puts 1800; p = get_local_block; puts p.call; puts 1801}; puts x.call; puts 1802; rescue; puts 1803; puts $!.class; end +$g = 0; def m_153; puts 1804; x = get_proc { puts 1805; p = get_local_block; puts p.call; puts 1806}; puts x.call; puts 1807; end; +begin; puts 1808; m_153; puts 1809; rescue; puts 1810; puts $!.class; end +$g = 0; begin; puts 1811; x = get_proc { puts 1812; p = get_local_lambda; puts p.call; puts 1813}; puts x.call; puts 1814; rescue; puts 1815; puts $!.class; end +$g = 0; def m_154; puts 1816; x = get_proc { puts 1817; p = get_local_lambda; puts p.call; puts 1818}; puts x.call; puts 1819; end; +begin; puts 1820; m_154; puts 1821; rescue; puts 1822; puts $!.class; end +$g = 0; begin; puts 1823; x = get_proc { puts 1824; p = get_local_proc; puts p.call; puts 1825}; puts x.call; puts 1826; rescue; puts 1827; puts $!.class; end +$g = 0; def m_155; puts 1828; x = get_proc { puts 1829; p = get_local_proc; puts p.call; puts 1830}; puts x.call; puts 1831; end; +begin; puts 1832; m_155; puts 1833; rescue; puts 1834; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1835; for i in [1, 2]; puts 1836; p = lambda{ puts 1837; $g += 1; redo if $g < 4;; puts 1838}; puts p.call; puts 1839; end; puts 1840; rescue; puts 1841; puts $!.class; end +$g = 0; def m_156; puts 1842; for i in [1, 2]; puts 1843; p = lambda{ puts 1844; $g += 1; redo if $g < 4;; puts 1845}; puts p.call; puts 1846; end; puts 1847; end; +begin; puts 1848; m_156; puts 1849; rescue; puts 1850; puts $!.class; end +$g = 0; begin; puts 1851; for i in [1, 2]; puts 1852; p = Proc.new{ puts 1853; $g += 1; redo if $g < 4;; puts 1854}; puts p.call; puts 1855; end; puts 1856; rescue; puts 1857; puts $!.class; end +$g = 0; def m_157; puts 1858; for i in [1, 2]; puts 1859; p = Proc.new{ puts 1860; $g += 1; redo if $g < 4;; puts 1861}; puts p.call; puts 1862; end; puts 1863; end; +begin; puts 1864; m_157; puts 1865; rescue; puts 1866; puts $!.class; end +$g = 0; begin; puts 1867; for i in [1, 2]; puts 1868; p = get_block{ puts 1869; $g += 1; redo if $g < 4;; puts 1870}; puts p.call; puts 1871; end; puts 1872; rescue; puts 1873; puts $!.class; end +$g = 0; def m_158; puts 1874; for i in [1, 2]; puts 1875; p = get_block{ puts 1876; $g += 1; redo if $g < 4;; puts 1877}; puts p.call; puts 1878; end; puts 1879; end; +begin; puts 1880; m_158; puts 1881; rescue; puts 1882; puts $!.class; end +$g = 0; begin; puts 1883; for i in [1, 2]; puts 1884; p = get_lambda{ puts 1885; $g += 1; redo if $g < 4;; puts 1886}; puts p.call; puts 1887; end; puts 1888; rescue; puts 1889; puts $!.class; end +$g = 0; def m_159; puts 1890; for i in [1, 2]; puts 1891; p = get_lambda{ puts 1892; $g += 1; redo if $g < 4;; puts 1893}; puts p.call; puts 1894; end; puts 1895; end; +begin; puts 1896; m_159; puts 1897; rescue; puts 1898; puts $!.class; end +$g = 0; begin; puts 1899; for i in [1, 2]; puts 1900; p = get_proc{ puts 1901; $g += 1; redo if $g < 4;; puts 1902}; puts p.call; puts 1903; end; puts 1904; rescue; puts 1905; puts $!.class; end +$g = 0; def m_160; puts 1906; for i in [1, 2]; puts 1907; p = get_proc{ puts 1908; $g += 1; redo if $g < 4;; puts 1909}; puts p.call; puts 1910; end; puts 1911; end; +begin; puts 1912; m_160; puts 1913; rescue; puts 1914; puts $!.class; end +$g = 0; begin; puts 1915; for i in [1, 2]; puts 1916; p = get_local_block; puts p.call; puts 1917; end; puts 1918; rescue; puts 1919; puts $!.class; end +$g = 0; def m_161; puts 1920; for i in [1, 2]; puts 1921; p = get_local_block; puts p.call; puts 1922; end; puts 1923; end; +begin; puts 1924; m_161; puts 1925; rescue; puts 1926; puts $!.class; end +$g = 0; begin; puts 1927; for i in [1, 2]; puts 1928; p = get_local_lambda; puts p.call; puts 1929; end; puts 1930; rescue; puts 1931; puts $!.class; end +$g = 0; def m_162; puts 1932; for i in [1, 2]; puts 1933; p = get_local_lambda; puts p.call; puts 1934; end; puts 1935; end; +begin; puts 1936; m_162; puts 1937; rescue; puts 1938; puts $!.class; end +$g = 0; begin; puts 1939; for i in [1, 2]; puts 1940; p = get_local_proc; puts p.call; puts 1941; end; puts 1942; rescue; puts 1943; puts $!.class; end +$g = 0; def m_163; puts 1944; for i in [1, 2]; puts 1945; p = get_local_proc; puts p.call; puts 1946; end; puts 1947; end; +begin; puts 1948; m_163; puts 1949; rescue; puts 1950; puts $!.class; end +end +test diff --git a/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_retry.rb b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_retry.rb new file mode 100644 index 0000000000..9b3bc2d831 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_retry.rb @@ -0,0 +1,595 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +# helper +def myeval(line); puts 1; eval(line); puts 2; end +def call1(x); puts 3; call2(x); puts 4; end +def call2(x); puts 5; call3(x); puts 6; end +def call3(x); puts 7; puts x.call; puts 8; end + +# producer + +def get_block(&p); p; end +def get_lambda(&p); lambda(&p); end +def get_proc(&p); Proc.new(&p); end + +def get_local_block; get_block { puts 9; $g += 1; retry if $g < 4;; puts 10 }; end +def get_local_lambda; lambda { puts 11; $g += 1; retry if $g < 4;; puts 12 }; end +def get_local_proc; Proc.new { puts 13; $g += 1; retry if $g < 4;; puts 14 }; end + +# consumer + +# taking arguments +def iterator_via_yield; puts 15; x = yield; puts x; puts 16; end +def iterator_via_call(&p); puts 17; puts(p.call); puts 18; end + +def method_call_iterator_via_yield(&p); puts 19; iterator_via_yield(&p); puts 20; end +def method_call_iterator_via_call(&p); puts 21; iterator_via_call(&p); puts 22; end + +def method_use_lambda_and_yield; puts 23; x = lambda { puts 24; yield; puts 25 }; puts x.call; puts 26; end +def method_use_proc_and_yield; puts 27; x = Proc.new { puts 28; yield; puts 29 }; puts x.call; puts 30; end +def method_use_lambda_and_call(&p); puts 31; x = lambda { puts 32; p.call; puts 33 }; puts x.call; puts 34; end +def method_use_proc_and_call(&p); puts 35; x = Proc.new { puts 36; p.call; puts 37 }; puts x.call; puts 38; end + +def method_use_lambda_and_yield_2; puts 39; x = lambda { puts 40; yield; puts 41 }; call1(x); puts 42; end + +def method_yield_in_loop; puts 43; for i in [1, 2]; puts 44; yield; puts 45; end; puts 46; end +def method_call_in_loop(&p); puts 47; for i in [3, 4]; puts 48; p.call; puts 49; end; puts 50; end + +# created in-place +def test +$g = 0; begin; puts 51; iterator_via_yield { puts 52; $g += 1; retry if $g < 4;; puts 53}; puts 54; rescue; puts 55; puts $!.class; end +$g = 0; def m_1; puts 56; $g = 0; begin; puts 57; iterator_via_yield { puts 58; $g += 1; retry if $g < 4;; puts 59}; puts 60; rescue; puts 61; puts $!.class; end; puts 62; end; m_1 +$g = 0; begin; puts 63; iterator_via_call { puts 64; $g += 1; retry if $g < 4;; puts 65}; puts 66; rescue; puts 67; puts $!.class; end +$g = 0; def m_2; puts 68; $g = 0; begin; puts 69; iterator_via_call { puts 70; $g += 1; retry if $g < 4;; puts 71}; puts 72; rescue; puts 73; puts $!.class; end; puts 74; end; m_2 +$g = 0; begin; puts 75; method_call_iterator_via_yield { puts 76; $g += 1; retry if $g < 4;; puts 77}; puts 78; rescue; puts 79; puts $!.class; end +$g = 0; def m_3; puts 80; $g = 0; begin; puts 81; method_call_iterator_via_yield { puts 82; $g += 1; retry if $g < 4;; puts 83}; puts 84; rescue; puts 85; puts $!.class; end; puts 86; end; m_3 +$g = 0; begin; puts 87; method_call_iterator_via_call { puts 88; $g += 1; retry if $g < 4;; puts 89}; puts 90; rescue; puts 91; puts $!.class; end +$g = 0; def m_4; puts 92; $g = 0; begin; puts 93; method_call_iterator_via_call { puts 94; $g += 1; retry if $g < 4;; puts 95}; puts 96; rescue; puts 97; puts $!.class; end; puts 98; end; m_4 +$g = 0; begin; puts 99; method_use_lambda_and_yield { puts 100; $g += 1; retry if $g < 4;; puts 101}; puts 102; rescue; puts 103; puts $!.class; end +$g = 0; def m_5; puts 104; $g = 0; begin; puts 105; method_use_lambda_and_yield { puts 106; $g += 1; retry if $g < 4;; puts 107}; puts 108; rescue; puts 109; puts $!.class; end; puts 110; end; m_5 +$g = 0; begin; puts 111; method_use_proc_and_yield { puts 112; $g += 1; retry if $g < 4;; puts 113}; puts 114; rescue; puts 115; puts $!.class; end +$g = 0; def m_6; puts 116; $g = 0; begin; puts 117; method_use_proc_and_yield { puts 118; $g += 1; retry if $g < 4;; puts 119}; puts 120; rescue; puts 121; puts $!.class; end; puts 122; end; m_6 +$g = 0; begin; puts 123; method_use_lambda_and_call { puts 124; $g += 1; retry if $g < 4;; puts 125}; puts 126; rescue; puts 127; puts $!.class; end +$g = 0; def m_7; puts 128; $g = 0; begin; puts 129; method_use_lambda_and_call { puts 130; $g += 1; retry if $g < 4;; puts 131}; puts 132; rescue; puts 133; puts $!.class; end; puts 134; end; m_7 +$g = 0; begin; puts 135; method_use_proc_and_call { puts 136; $g += 1; retry if $g < 4;; puts 137}; puts 138; rescue; puts 139; puts $!.class; end +$g = 0; def m_8; puts 140; $g = 0; begin; puts 141; method_use_proc_and_call { puts 142; $g += 1; retry if $g < 4;; puts 143}; puts 144; rescue; puts 145; puts $!.class; end; puts 146; end; m_8 +$g = 0; begin; puts 147; method_use_lambda_and_yield_2 { puts 148; $g += 1; retry if $g < 4;; puts 149}; puts 150; rescue; puts 151; puts $!.class; end +$g = 0; def m_9; puts 152; $g = 0; begin; puts 153; method_use_lambda_and_yield_2 { puts 154; $g += 1; retry if $g < 4;; puts 155}; puts 156; rescue; puts 157; puts $!.class; end; puts 158; end; m_9 +$g = 0; begin; puts 159; method_yield_in_loop { puts 160; $g += 1; retry if $g < 4;; puts 161}; puts 162; rescue; puts 163; puts $!.class; end +$g = 0; def m_10; puts 164; $g = 0; begin; puts 165; method_yield_in_loop { puts 166; $g += 1; retry if $g < 4;; puts 167}; puts 168; rescue; puts 169; puts $!.class; end; puts 170; end; m_10 +$g = 0; begin; puts 171; method_call_in_loop { puts 172; $g += 1; retry if $g < 4;; puts 173}; puts 174; rescue; puts 175; puts $!.class; end +$g = 0; def m_11; puts 176; $g = 0; begin; puts 177; method_call_in_loop { puts 178; $g += 1; retry if $g < 4;; puts 179}; puts 180; rescue; puts 181; puts $!.class; end; puts 182; end; m_11 +end +test + + +# created locally or from method +def test +$g = 0; begin; p = lambda{ puts 183; $g += 1; retry if $g < 4;; puts 184}; puts 185; iterator_via_yield(&p); puts 186; rescue; puts 187; puts $!.class; end +$g = 0; def m_12; p = lambda{ puts 188; $g += 1; retry if $g < 4;; puts 189}; puts 190; iterator_via_yield(&p); puts 191; end; +begin; puts 192; m_12; puts 193; rescue; puts 194; puts $!.class; end +$g = 0; begin; p = lambda{ puts 195; $g += 1; retry if $g < 4;; puts 196}; puts 197; iterator_via_call(&p); puts 198; rescue; puts 199; puts $!.class; end +$g = 0; def m_13; p = lambda{ puts 200; $g += 1; retry if $g < 4;; puts 201}; puts 202; iterator_via_call(&p); puts 203; end; +begin; puts 204; m_13; puts 205; rescue; puts 206; puts $!.class; end +$g = 0; begin; p = lambda{ puts 207; $g += 1; retry if $g < 4;; puts 208}; puts 209; method_call_iterator_via_yield(&p); puts 210; rescue; puts 211; puts $!.class; end +$g = 0; def m_14; p = lambda{ puts 212; $g += 1; retry if $g < 4;; puts 213}; puts 214; method_call_iterator_via_yield(&p); puts 215; end; +begin; puts 216; m_14; puts 217; rescue; puts 218; puts $!.class; end +$g = 0; begin; p = lambda{ puts 219; $g += 1; retry if $g < 4;; puts 220}; puts 221; method_call_iterator_via_call(&p); puts 222; rescue; puts 223; puts $!.class; end +$g = 0; def m_15; p = lambda{ puts 224; $g += 1; retry if $g < 4;; puts 225}; puts 226; method_call_iterator_via_call(&p); puts 227; end; +begin; puts 228; m_15; puts 229; rescue; puts 230; puts $!.class; end +$g = 0; begin; p = lambda{ puts 231; $g += 1; retry if $g < 4;; puts 232}; puts 233; method_use_lambda_and_yield(&p); puts 234; rescue; puts 235; puts $!.class; end +$g = 0; def m_16; p = lambda{ puts 236; $g += 1; retry if $g < 4;; puts 237}; puts 238; method_use_lambda_and_yield(&p); puts 239; end; +begin; puts 240; m_16; puts 241; rescue; puts 242; puts $!.class; end +$g = 0; begin; p = lambda{ puts 243; $g += 1; retry if $g < 4;; puts 244}; puts 245; method_use_proc_and_yield(&p); puts 246; rescue; puts 247; puts $!.class; end +$g = 0; def m_17; p = lambda{ puts 248; $g += 1; retry if $g < 4;; puts 249}; puts 250; method_use_proc_and_yield(&p); puts 251; end; +begin; puts 252; m_17; puts 253; rescue; puts 254; puts $!.class; end +$g = 0; begin; p = lambda{ puts 255; $g += 1; retry if $g < 4;; puts 256}; puts 257; method_use_lambda_and_call(&p); puts 258; rescue; puts 259; puts $!.class; end +$g = 0; def m_18; p = lambda{ puts 260; $g += 1; retry if $g < 4;; puts 261}; puts 262; method_use_lambda_and_call(&p); puts 263; end; +begin; puts 264; m_18; puts 265; rescue; puts 266; puts $!.class; end +$g = 0; begin; p = lambda{ puts 267; $g += 1; retry if $g < 4;; puts 268}; puts 269; method_use_proc_and_call(&p); puts 270; rescue; puts 271; puts $!.class; end +$g = 0; def m_19; p = lambda{ puts 272; $g += 1; retry if $g < 4;; puts 273}; puts 274; method_use_proc_and_call(&p); puts 275; end; +begin; puts 276; m_19; puts 277; rescue; puts 278; puts $!.class; end +$g = 0; begin; p = lambda{ puts 279; $g += 1; retry if $g < 4;; puts 280}; puts 281; method_use_lambda_and_yield_2(&p); puts 282; rescue; puts 283; puts $!.class; end +$g = 0; def m_20; p = lambda{ puts 284; $g += 1; retry if $g < 4;; puts 285}; puts 286; method_use_lambda_and_yield_2(&p); puts 287; end; +begin; puts 288; m_20; puts 289; rescue; puts 290; puts $!.class; end +$g = 0; begin; p = lambda{ puts 291; $g += 1; retry if $g < 4;; puts 292}; puts 293; method_yield_in_loop(&p); puts 294; rescue; puts 295; puts $!.class; end +$g = 0; def m_21; p = lambda{ puts 296; $g += 1; retry if $g < 4;; puts 297}; puts 298; method_yield_in_loop(&p); puts 299; end; +begin; puts 300; m_21; puts 301; rescue; puts 302; puts $!.class; end +$g = 0; begin; p = lambda{ puts 303; $g += 1; retry if $g < 4;; puts 304}; puts 305; method_call_in_loop(&p); puts 306; rescue; puts 307; puts $!.class; end +$g = 0; def m_22; p = lambda{ puts 308; $g += 1; retry if $g < 4;; puts 309}; puts 310; method_call_in_loop(&p); puts 311; end; +begin; puts 312; m_22; puts 313; rescue; puts 314; puts $!.class; end +end +test + +def test +$g = 0; begin; p = Proc.new{ puts 315; $g += 1; retry if $g < 4;; puts 316}; puts 317; iterator_via_yield(&p); puts 318; rescue; puts 319; puts $!.class; end +$g = 0; def m_23; p = Proc.new{ puts 320; $g += 1; retry if $g < 4;; puts 321}; puts 322; iterator_via_yield(&p); puts 323; end; +begin; puts 324; m_23; puts 325; rescue; puts 326; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 327; $g += 1; retry if $g < 4;; puts 328}; puts 329; iterator_via_call(&p); puts 330; rescue; puts 331; puts $!.class; end +$g = 0; def m_24; p = Proc.new{ puts 332; $g += 1; retry if $g < 4;; puts 333}; puts 334; iterator_via_call(&p); puts 335; end; +begin; puts 336; m_24; puts 337; rescue; puts 338; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 339; $g += 1; retry if $g < 4;; puts 340}; puts 341; method_call_iterator_via_yield(&p); puts 342; rescue; puts 343; puts $!.class; end +$g = 0; def m_25; p = Proc.new{ puts 344; $g += 1; retry if $g < 4;; puts 345}; puts 346; method_call_iterator_via_yield(&p); puts 347; end; +begin; puts 348; m_25; puts 349; rescue; puts 350; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 351; $g += 1; retry if $g < 4;; puts 352}; puts 353; method_call_iterator_via_call(&p); puts 354; rescue; puts 355; puts $!.class; end +$g = 0; def m_26; p = Proc.new{ puts 356; $g += 1; retry if $g < 4;; puts 357}; puts 358; method_call_iterator_via_call(&p); puts 359; end; +begin; puts 360; m_26; puts 361; rescue; puts 362; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 363; $g += 1; retry if $g < 4;; puts 364}; puts 365; method_use_lambda_and_yield(&p); puts 366; rescue; puts 367; puts $!.class; end +$g = 0; def m_27; p = Proc.new{ puts 368; $g += 1; retry if $g < 4;; puts 369}; puts 370; method_use_lambda_and_yield(&p); puts 371; end; +begin; puts 372; m_27; puts 373; rescue; puts 374; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 375; $g += 1; retry if $g < 4;; puts 376}; puts 377; method_use_proc_and_yield(&p); puts 378; rescue; puts 379; puts $!.class; end +$g = 0; def m_28; p = Proc.new{ puts 380; $g += 1; retry if $g < 4;; puts 381}; puts 382; method_use_proc_and_yield(&p); puts 383; end; +begin; puts 384; m_28; puts 385; rescue; puts 386; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 387; $g += 1; retry if $g < 4;; puts 388}; puts 389; method_use_lambda_and_call(&p); puts 390; rescue; puts 391; puts $!.class; end +$g = 0; def m_29; p = Proc.new{ puts 392; $g += 1; retry if $g < 4;; puts 393}; puts 394; method_use_lambda_and_call(&p); puts 395; end; +begin; puts 396; m_29; puts 397; rescue; puts 398; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 399; $g += 1; retry if $g < 4;; puts 400}; puts 401; method_use_proc_and_call(&p); puts 402; rescue; puts 403; puts $!.class; end +$g = 0; def m_30; p = Proc.new{ puts 404; $g += 1; retry if $g < 4;; puts 405}; puts 406; method_use_proc_and_call(&p); puts 407; end; +begin; puts 408; m_30; puts 409; rescue; puts 410; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 411; $g += 1; retry if $g < 4;; puts 412}; puts 413; method_use_lambda_and_yield_2(&p); puts 414; rescue; puts 415; puts $!.class; end +$g = 0; def m_31; p = Proc.new{ puts 416; $g += 1; retry if $g < 4;; puts 417}; puts 418; method_use_lambda_and_yield_2(&p); puts 419; end; +begin; puts 420; m_31; puts 421; rescue; puts 422; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 423; $g += 1; retry if $g < 4;; puts 424}; puts 425; method_yield_in_loop(&p); puts 426; rescue; puts 427; puts $!.class; end +$g = 0; def m_32; p = Proc.new{ puts 428; $g += 1; retry if $g < 4;; puts 429}; puts 430; method_yield_in_loop(&p); puts 431; end; +begin; puts 432; m_32; puts 433; rescue; puts 434; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 435; $g += 1; retry if $g < 4;; puts 436}; puts 437; method_call_in_loop(&p); puts 438; rescue; puts 439; puts $!.class; end +$g = 0; def m_33; p = Proc.new{ puts 440; $g += 1; retry if $g < 4;; puts 441}; puts 442; method_call_in_loop(&p); puts 443; end; +begin; puts 444; m_33; puts 445; rescue; puts 446; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_block{ puts 447; $g += 1; retry if $g < 4;; puts 448}; puts 449; iterator_via_yield(&p); puts 450; rescue; puts 451; puts $!.class; end +$g = 0; def m_34; p = get_block{ puts 452; $g += 1; retry if $g < 4;; puts 453}; puts 454; iterator_via_yield(&p); puts 455; end; +begin; puts 456; m_34; puts 457; rescue; puts 458; puts $!.class; end +$g = 0; begin; p = get_block{ puts 459; $g += 1; retry if $g < 4;; puts 460}; puts 461; iterator_via_call(&p); puts 462; rescue; puts 463; puts $!.class; end +$g = 0; def m_35; p = get_block{ puts 464; $g += 1; retry if $g < 4;; puts 465}; puts 466; iterator_via_call(&p); puts 467; end; +begin; puts 468; m_35; puts 469; rescue; puts 470; puts $!.class; end +$g = 0; begin; p = get_block{ puts 471; $g += 1; retry if $g < 4;; puts 472}; puts 473; method_call_iterator_via_yield(&p); puts 474; rescue; puts 475; puts $!.class; end +$g = 0; def m_36; p = get_block{ puts 476; $g += 1; retry if $g < 4;; puts 477}; puts 478; method_call_iterator_via_yield(&p); puts 479; end; +begin; puts 480; m_36; puts 481; rescue; puts 482; puts $!.class; end +$g = 0; begin; p = get_block{ puts 483; $g += 1; retry if $g < 4;; puts 484}; puts 485; method_call_iterator_via_call(&p); puts 486; rescue; puts 487; puts $!.class; end +$g = 0; def m_37; p = get_block{ puts 488; $g += 1; retry if $g < 4;; puts 489}; puts 490; method_call_iterator_via_call(&p); puts 491; end; +begin; puts 492; m_37; puts 493; rescue; puts 494; puts $!.class; end +$g = 0; begin; p = get_block{ puts 495; $g += 1; retry if $g < 4;; puts 496}; puts 497; method_use_lambda_and_yield(&p); puts 498; rescue; puts 499; puts $!.class; end +$g = 0; def m_38; p = get_block{ puts 500; $g += 1; retry if $g < 4;; puts 501}; puts 502; method_use_lambda_and_yield(&p); puts 503; end; +begin; puts 504; m_38; puts 505; rescue; puts 506; puts $!.class; end +$g = 0; begin; p = get_block{ puts 507; $g += 1; retry if $g < 4;; puts 508}; puts 509; method_use_proc_and_yield(&p); puts 510; rescue; puts 511; puts $!.class; end +$g = 0; def m_39; p = get_block{ puts 512; $g += 1; retry if $g < 4;; puts 513}; puts 514; method_use_proc_and_yield(&p); puts 515; end; +begin; puts 516; m_39; puts 517; rescue; puts 518; puts $!.class; end +$g = 0; begin; p = get_block{ puts 519; $g += 1; retry if $g < 4;; puts 520}; puts 521; method_use_lambda_and_call(&p); puts 522; rescue; puts 523; puts $!.class; end +$g = 0; def m_40; p = get_block{ puts 524; $g += 1; retry if $g < 4;; puts 525}; puts 526; method_use_lambda_and_call(&p); puts 527; end; +begin; puts 528; m_40; puts 529; rescue; puts 530; puts $!.class; end +$g = 0; begin; p = get_block{ puts 531; $g += 1; retry if $g < 4;; puts 532}; puts 533; method_use_proc_and_call(&p); puts 534; rescue; puts 535; puts $!.class; end +$g = 0; def m_41; p = get_block{ puts 536; $g += 1; retry if $g < 4;; puts 537}; puts 538; method_use_proc_and_call(&p); puts 539; end; +begin; puts 540; m_41; puts 541; rescue; puts 542; puts $!.class; end +$g = 0; begin; p = get_block{ puts 543; $g += 1; retry if $g < 4;; puts 544}; puts 545; method_use_lambda_and_yield_2(&p); puts 546; rescue; puts 547; puts $!.class; end +$g = 0; def m_42; p = get_block{ puts 548; $g += 1; retry if $g < 4;; puts 549}; puts 550; method_use_lambda_and_yield_2(&p); puts 551; end; +begin; puts 552; m_42; puts 553; rescue; puts 554; puts $!.class; end +$g = 0; begin; p = get_block{ puts 555; $g += 1; retry if $g < 4;; puts 556}; puts 557; method_yield_in_loop(&p); puts 558; rescue; puts 559; puts $!.class; end +$g = 0; def m_43; p = get_block{ puts 560; $g += 1; retry if $g < 4;; puts 561}; puts 562; method_yield_in_loop(&p); puts 563; end; +begin; puts 564; m_43; puts 565; rescue; puts 566; puts $!.class; end +$g = 0; begin; p = get_block{ puts 567; $g += 1; retry if $g < 4;; puts 568}; puts 569; method_call_in_loop(&p); puts 570; rescue; puts 571; puts $!.class; end +$g = 0; def m_44; p = get_block{ puts 572; $g += 1; retry if $g < 4;; puts 573}; puts 574; method_call_in_loop(&p); puts 575; end; +begin; puts 576; m_44; puts 577; rescue; puts 578; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_lambda{ puts 579; $g += 1; retry if $g < 4;; puts 580}; puts 581; iterator_via_yield(&p); puts 582; rescue; puts 583; puts $!.class; end +$g = 0; def m_45; p = get_lambda{ puts 584; $g += 1; retry if $g < 4;; puts 585}; puts 586; iterator_via_yield(&p); puts 587; end; +begin; puts 588; m_45; puts 589; rescue; puts 590; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 591; $g += 1; retry if $g < 4;; puts 592}; puts 593; iterator_via_call(&p); puts 594; rescue; puts 595; puts $!.class; end +$g = 0; def m_46; p = get_lambda{ puts 596; $g += 1; retry if $g < 4;; puts 597}; puts 598; iterator_via_call(&p); puts 599; end; +begin; puts 600; m_46; puts 601; rescue; puts 602; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 603; $g += 1; retry if $g < 4;; puts 604}; puts 605; method_call_iterator_via_yield(&p); puts 606; rescue; puts 607; puts $!.class; end +$g = 0; def m_47; p = get_lambda{ puts 608; $g += 1; retry if $g < 4;; puts 609}; puts 610; method_call_iterator_via_yield(&p); puts 611; end; +begin; puts 612; m_47; puts 613; rescue; puts 614; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 615; $g += 1; retry if $g < 4;; puts 616}; puts 617; method_call_iterator_via_call(&p); puts 618; rescue; puts 619; puts $!.class; end +$g = 0; def m_48; p = get_lambda{ puts 620; $g += 1; retry if $g < 4;; puts 621}; puts 622; method_call_iterator_via_call(&p); puts 623; end; +begin; puts 624; m_48; puts 625; rescue; puts 626; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 627; $g += 1; retry if $g < 4;; puts 628}; puts 629; method_use_lambda_and_yield(&p); puts 630; rescue; puts 631; puts $!.class; end +$g = 0; def m_49; p = get_lambda{ puts 632; $g += 1; retry if $g < 4;; puts 633}; puts 634; method_use_lambda_and_yield(&p); puts 635; end; +begin; puts 636; m_49; puts 637; rescue; puts 638; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 639; $g += 1; retry if $g < 4;; puts 640}; puts 641; method_use_proc_and_yield(&p); puts 642; rescue; puts 643; puts $!.class; end +$g = 0; def m_50; p = get_lambda{ puts 644; $g += 1; retry if $g < 4;; puts 645}; puts 646; method_use_proc_and_yield(&p); puts 647; end; +begin; puts 648; m_50; puts 649; rescue; puts 650; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 651; $g += 1; retry if $g < 4;; puts 652}; puts 653; method_use_lambda_and_call(&p); puts 654; rescue; puts 655; puts $!.class; end +$g = 0; def m_51; p = get_lambda{ puts 656; $g += 1; retry if $g < 4;; puts 657}; puts 658; method_use_lambda_and_call(&p); puts 659; end; +begin; puts 660; m_51; puts 661; rescue; puts 662; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 663; $g += 1; retry if $g < 4;; puts 664}; puts 665; method_use_proc_and_call(&p); puts 666; rescue; puts 667; puts $!.class; end +$g = 0; def m_52; p = get_lambda{ puts 668; $g += 1; retry if $g < 4;; puts 669}; puts 670; method_use_proc_and_call(&p); puts 671; end; +begin; puts 672; m_52; puts 673; rescue; puts 674; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 675; $g += 1; retry if $g < 4;; puts 676}; puts 677; method_use_lambda_and_yield_2(&p); puts 678; rescue; puts 679; puts $!.class; end +$g = 0; def m_53; p = get_lambda{ puts 680; $g += 1; retry if $g < 4;; puts 681}; puts 682; method_use_lambda_and_yield_2(&p); puts 683; end; +begin; puts 684; m_53; puts 685; rescue; puts 686; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 687; $g += 1; retry if $g < 4;; puts 688}; puts 689; method_yield_in_loop(&p); puts 690; rescue; puts 691; puts $!.class; end +$g = 0; def m_54; p = get_lambda{ puts 692; $g += 1; retry if $g < 4;; puts 693}; puts 694; method_yield_in_loop(&p); puts 695; end; +begin; puts 696; m_54; puts 697; rescue; puts 698; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 699; $g += 1; retry if $g < 4;; puts 700}; puts 701; method_call_in_loop(&p); puts 702; rescue; puts 703; puts $!.class; end +$g = 0; def m_55; p = get_lambda{ puts 704; $g += 1; retry if $g < 4;; puts 705}; puts 706; method_call_in_loop(&p); puts 707; end; +begin; puts 708; m_55; puts 709; rescue; puts 710; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_proc{ puts 711; $g += 1; retry if $g < 4;; puts 712}; puts 713; iterator_via_yield(&p); puts 714; rescue; puts 715; puts $!.class; end +$g = 0; def m_56; p = get_proc{ puts 716; $g += 1; retry if $g < 4;; puts 717}; puts 718; iterator_via_yield(&p); puts 719; end; +begin; puts 720; m_56; puts 721; rescue; puts 722; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 723; $g += 1; retry if $g < 4;; puts 724}; puts 725; iterator_via_call(&p); puts 726; rescue; puts 727; puts $!.class; end +$g = 0; def m_57; p = get_proc{ puts 728; $g += 1; retry if $g < 4;; puts 729}; puts 730; iterator_via_call(&p); puts 731; end; +begin; puts 732; m_57; puts 733; rescue; puts 734; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 735; $g += 1; retry if $g < 4;; puts 736}; puts 737; method_call_iterator_via_yield(&p); puts 738; rescue; puts 739; puts $!.class; end +$g = 0; def m_58; p = get_proc{ puts 740; $g += 1; retry if $g < 4;; puts 741}; puts 742; method_call_iterator_via_yield(&p); puts 743; end; +begin; puts 744; m_58; puts 745; rescue; puts 746; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 747; $g += 1; retry if $g < 4;; puts 748}; puts 749; method_call_iterator_via_call(&p); puts 750; rescue; puts 751; puts $!.class; end +$g = 0; def m_59; p = get_proc{ puts 752; $g += 1; retry if $g < 4;; puts 753}; puts 754; method_call_iterator_via_call(&p); puts 755; end; +begin; puts 756; m_59; puts 757; rescue; puts 758; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 759; $g += 1; retry if $g < 4;; puts 760}; puts 761; method_use_lambda_and_yield(&p); puts 762; rescue; puts 763; puts $!.class; end +$g = 0; def m_60; p = get_proc{ puts 764; $g += 1; retry if $g < 4;; puts 765}; puts 766; method_use_lambda_and_yield(&p); puts 767; end; +begin; puts 768; m_60; puts 769; rescue; puts 770; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 771; $g += 1; retry if $g < 4;; puts 772}; puts 773; method_use_proc_and_yield(&p); puts 774; rescue; puts 775; puts $!.class; end +$g = 0; def m_61; p = get_proc{ puts 776; $g += 1; retry if $g < 4;; puts 777}; puts 778; method_use_proc_and_yield(&p); puts 779; end; +begin; puts 780; m_61; puts 781; rescue; puts 782; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 783; $g += 1; retry if $g < 4;; puts 784}; puts 785; method_use_lambda_and_call(&p); puts 786; rescue; puts 787; puts $!.class; end +$g = 0; def m_62; p = get_proc{ puts 788; $g += 1; retry if $g < 4;; puts 789}; puts 790; method_use_lambda_and_call(&p); puts 791; end; +begin; puts 792; m_62; puts 793; rescue; puts 794; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 795; $g += 1; retry if $g < 4;; puts 796}; puts 797; method_use_proc_and_call(&p); puts 798; rescue; puts 799; puts $!.class; end +$g = 0; def m_63; p = get_proc{ puts 800; $g += 1; retry if $g < 4;; puts 801}; puts 802; method_use_proc_and_call(&p); puts 803; end; +begin; puts 804; m_63; puts 805; rescue; puts 806; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 807; $g += 1; retry if $g < 4;; puts 808}; puts 809; method_use_lambda_and_yield_2(&p); puts 810; rescue; puts 811; puts $!.class; end +$g = 0; def m_64; p = get_proc{ puts 812; $g += 1; retry if $g < 4;; puts 813}; puts 814; method_use_lambda_and_yield_2(&p); puts 815; end; +begin; puts 816; m_64; puts 817; rescue; puts 818; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 819; $g += 1; retry if $g < 4;; puts 820}; puts 821; method_yield_in_loop(&p); puts 822; rescue; puts 823; puts $!.class; end +$g = 0; def m_65; p = get_proc{ puts 824; $g += 1; retry if $g < 4;; puts 825}; puts 826; method_yield_in_loop(&p); puts 827; end; +begin; puts 828; m_65; puts 829; rescue; puts 830; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 831; $g += 1; retry if $g < 4;; puts 832}; puts 833; method_call_in_loop(&p); puts 834; rescue; puts 835; puts $!.class; end +$g = 0; def m_66; p = get_proc{ puts 836; $g += 1; retry if $g < 4;; puts 837}; puts 838; method_call_in_loop(&p); puts 839; end; +begin; puts 840; m_66; puts 841; rescue; puts 842; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_block; puts 843; iterator_via_yield(&p); puts 844; rescue; puts 845; puts $!.class; end +$g = 0; def m_67; p = get_local_block; puts 846; iterator_via_yield(&p); puts 847; end; +begin; puts 848; m_67; puts 849; rescue; puts 850; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 851; iterator_via_call(&p); puts 852; rescue; puts 853; puts $!.class; end +$g = 0; def m_68; p = get_local_block; puts 854; iterator_via_call(&p); puts 855; end; +begin; puts 856; m_68; puts 857; rescue; puts 858; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 859; method_call_iterator_via_yield(&p); puts 860; rescue; puts 861; puts $!.class; end +$g = 0; def m_69; p = get_local_block; puts 862; method_call_iterator_via_yield(&p); puts 863; end; +begin; puts 864; m_69; puts 865; rescue; puts 866; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 867; method_call_iterator_via_call(&p); puts 868; rescue; puts 869; puts $!.class; end +$g = 0; def m_70; p = get_local_block; puts 870; method_call_iterator_via_call(&p); puts 871; end; +begin; puts 872; m_70; puts 873; rescue; puts 874; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 875; method_use_lambda_and_yield(&p); puts 876; rescue; puts 877; puts $!.class; end +$g = 0; def m_71; p = get_local_block; puts 878; method_use_lambda_and_yield(&p); puts 879; end; +begin; puts 880; m_71; puts 881; rescue; puts 882; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 883; method_use_proc_and_yield(&p); puts 884; rescue; puts 885; puts $!.class; end +$g = 0; def m_72; p = get_local_block; puts 886; method_use_proc_and_yield(&p); puts 887; end; +begin; puts 888; m_72; puts 889; rescue; puts 890; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 891; method_use_lambda_and_call(&p); puts 892; rescue; puts 893; puts $!.class; end +$g = 0; def m_73; p = get_local_block; puts 894; method_use_lambda_and_call(&p); puts 895; end; +begin; puts 896; m_73; puts 897; rescue; puts 898; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 899; method_use_proc_and_call(&p); puts 900; rescue; puts 901; puts $!.class; end +$g = 0; def m_74; p = get_local_block; puts 902; method_use_proc_and_call(&p); puts 903; end; +begin; puts 904; m_74; puts 905; rescue; puts 906; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 907; method_use_lambda_and_yield_2(&p); puts 908; rescue; puts 909; puts $!.class; end +$g = 0; def m_75; p = get_local_block; puts 910; method_use_lambda_and_yield_2(&p); puts 911; end; +begin; puts 912; m_75; puts 913; rescue; puts 914; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 915; method_yield_in_loop(&p); puts 916; rescue; puts 917; puts $!.class; end +$g = 0; def m_76; p = get_local_block; puts 918; method_yield_in_loop(&p); puts 919; end; +begin; puts 920; m_76; puts 921; rescue; puts 922; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 923; method_call_in_loop(&p); puts 924; rescue; puts 925; puts $!.class; end +$g = 0; def m_77; p = get_local_block; puts 926; method_call_in_loop(&p); puts 927; end; +begin; puts 928; m_77; puts 929; rescue; puts 930; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_lambda; puts 931; iterator_via_yield(&p); puts 932; rescue; puts 933; puts $!.class; end +$g = 0; def m_78; p = get_local_lambda; puts 934; iterator_via_yield(&p); puts 935; end; +begin; puts 936; m_78; puts 937; rescue; puts 938; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 939; iterator_via_call(&p); puts 940; rescue; puts 941; puts $!.class; end +$g = 0; def m_79; p = get_local_lambda; puts 942; iterator_via_call(&p); puts 943; end; +begin; puts 944; m_79; puts 945; rescue; puts 946; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 947; method_call_iterator_via_yield(&p); puts 948; rescue; puts 949; puts $!.class; end +$g = 0; def m_80; p = get_local_lambda; puts 950; method_call_iterator_via_yield(&p); puts 951; end; +begin; puts 952; m_80; puts 953; rescue; puts 954; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 955; method_call_iterator_via_call(&p); puts 956; rescue; puts 957; puts $!.class; end +$g = 0; def m_81; p = get_local_lambda; puts 958; method_call_iterator_via_call(&p); puts 959; end; +begin; puts 960; m_81; puts 961; rescue; puts 962; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 963; method_use_lambda_and_yield(&p); puts 964; rescue; puts 965; puts $!.class; end +$g = 0; def m_82; p = get_local_lambda; puts 966; method_use_lambda_and_yield(&p); puts 967; end; +begin; puts 968; m_82; puts 969; rescue; puts 970; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 971; method_use_proc_and_yield(&p); puts 972; rescue; puts 973; puts $!.class; end +$g = 0; def m_83; p = get_local_lambda; puts 974; method_use_proc_and_yield(&p); puts 975; end; +begin; puts 976; m_83; puts 977; rescue; puts 978; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 979; method_use_lambda_and_call(&p); puts 980; rescue; puts 981; puts $!.class; end +$g = 0; def m_84; p = get_local_lambda; puts 982; method_use_lambda_and_call(&p); puts 983; end; +begin; puts 984; m_84; puts 985; rescue; puts 986; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 987; method_use_proc_and_call(&p); puts 988; rescue; puts 989; puts $!.class; end +$g = 0; def m_85; p = get_local_lambda; puts 990; method_use_proc_and_call(&p); puts 991; end; +begin; puts 992; m_85; puts 993; rescue; puts 994; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 995; method_use_lambda_and_yield_2(&p); puts 996; rescue; puts 997; puts $!.class; end +$g = 0; def m_86; p = get_local_lambda; puts 998; method_use_lambda_and_yield_2(&p); puts 999; end; +begin; puts 1000; m_86; puts 1001; rescue; puts 1002; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1003; method_yield_in_loop(&p); puts 1004; rescue; puts 1005; puts $!.class; end +$g = 0; def m_87; p = get_local_lambda; puts 1006; method_yield_in_loop(&p); puts 1007; end; +begin; puts 1008; m_87; puts 1009; rescue; puts 1010; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1011; method_call_in_loop(&p); puts 1012; rescue; puts 1013; puts $!.class; end +$g = 0; def m_88; p = get_local_lambda; puts 1014; method_call_in_loop(&p); puts 1015; end; +begin; puts 1016; m_88; puts 1017; rescue; puts 1018; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_proc; puts 1019; iterator_via_yield(&p); puts 1020; rescue; puts 1021; puts $!.class; end +$g = 0; def m_89; p = get_local_proc; puts 1022; iterator_via_yield(&p); puts 1023; end; +begin; puts 1024; m_89; puts 1025; rescue; puts 1026; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1027; iterator_via_call(&p); puts 1028; rescue; puts 1029; puts $!.class; end +$g = 0; def m_90; p = get_local_proc; puts 1030; iterator_via_call(&p); puts 1031; end; +begin; puts 1032; m_90; puts 1033; rescue; puts 1034; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1035; method_call_iterator_via_yield(&p); puts 1036; rescue; puts 1037; puts $!.class; end +$g = 0; def m_91; p = get_local_proc; puts 1038; method_call_iterator_via_yield(&p); puts 1039; end; +begin; puts 1040; m_91; puts 1041; rescue; puts 1042; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1043; method_call_iterator_via_call(&p); puts 1044; rescue; puts 1045; puts $!.class; end +$g = 0; def m_92; p = get_local_proc; puts 1046; method_call_iterator_via_call(&p); puts 1047; end; +begin; puts 1048; m_92; puts 1049; rescue; puts 1050; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1051; method_use_lambda_and_yield(&p); puts 1052; rescue; puts 1053; puts $!.class; end +$g = 0; def m_93; p = get_local_proc; puts 1054; method_use_lambda_and_yield(&p); puts 1055; end; +begin; puts 1056; m_93; puts 1057; rescue; puts 1058; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1059; method_use_proc_and_yield(&p); puts 1060; rescue; puts 1061; puts $!.class; end +$g = 0; def m_94; p = get_local_proc; puts 1062; method_use_proc_and_yield(&p); puts 1063; end; +begin; puts 1064; m_94; puts 1065; rescue; puts 1066; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1067; method_use_lambda_and_call(&p); puts 1068; rescue; puts 1069; puts $!.class; end +$g = 0; def m_95; p = get_local_proc; puts 1070; method_use_lambda_and_call(&p); puts 1071; end; +begin; puts 1072; m_95; puts 1073; rescue; puts 1074; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1075; method_use_proc_and_call(&p); puts 1076; rescue; puts 1077; puts $!.class; end +$g = 0; def m_96; p = get_local_proc; puts 1078; method_use_proc_and_call(&p); puts 1079; end; +begin; puts 1080; m_96; puts 1081; rescue; puts 1082; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1083; method_use_lambda_and_yield_2(&p); puts 1084; rescue; puts 1085; puts $!.class; end +$g = 0; def m_97; p = get_local_proc; puts 1086; method_use_lambda_and_yield_2(&p); puts 1087; end; +begin; puts 1088; m_97; puts 1089; rescue; puts 1090; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1091; method_yield_in_loop(&p); puts 1092; rescue; puts 1093; puts $!.class; end +$g = 0; def m_98; p = get_local_proc; puts 1094; method_yield_in_loop(&p); puts 1095; end; +begin; puts 1096; m_98; puts 1097; rescue; puts 1098; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1099; method_call_in_loop(&p); puts 1100; rescue; puts 1101; puts $!.class; end +$g = 0; def m_99; p = get_local_proc; puts 1102; method_call_in_loop(&p); puts 1103; end; +begin; puts 1104; m_99; puts 1105; rescue; puts 1106; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1107; p = lambda{ puts 1108; $g += 1; retry if $g < 4;; puts 1109}; puts(p.call); puts 1110; rescue; puts 1111; puts $!.class; end +$g = 0; def m_100; puts 1112; p = lambda{ puts 1113; $g += 1; retry if $g < 4;; puts 1114}; puts(p.call); puts 1115; end; +begin; puts 1116; m_100; puts 1117; rescue; puts 1118; puts $!.class; end +$g = 0; begin; puts 1119; puts m_100; puts 1120; rescue; puts 1121; puts $!.class; end +$g = 0; def m_101; puts 1122; puts m_100; puts 1123; end; +begin; puts 1124; m_101; puts 1125; rescue; puts 1126; puts $!.class; end +$g = 0; begin; puts 1127; p = Proc.new{ puts 1128; $g += 1; retry if $g < 4;; puts 1129}; puts(p.call); puts 1130; rescue; puts 1131; puts $!.class; end +$g = 0; def m_102; puts 1132; p = Proc.new{ puts 1133; $g += 1; retry if $g < 4;; puts 1134}; puts(p.call); puts 1135; end; +begin; puts 1136; m_102; puts 1137; rescue; puts 1138; puts $!.class; end +$g = 0; begin; puts 1139; puts m_102; puts 1140; rescue; puts 1141; puts $!.class; end +$g = 0; def m_103; puts 1142; puts m_102; puts 1143; end; +begin; puts 1144; m_103; puts 1145; rescue; puts 1146; puts $!.class; end +$g = 0; begin; puts 1147; p = get_block{ puts 1148; $g += 1; retry if $g < 4;; puts 1149}; puts(p.call); puts 1150; rescue; puts 1151; puts $!.class; end +$g = 0; def m_104; puts 1152; p = get_block{ puts 1153; $g += 1; retry if $g < 4;; puts 1154}; puts(p.call); puts 1155; end; +begin; puts 1156; m_104; puts 1157; rescue; puts 1158; puts $!.class; end +$g = 0; begin; puts 1159; puts m_104; puts 1160; rescue; puts 1161; puts $!.class; end +$g = 0; def m_105; puts 1162; puts m_104; puts 1163; end; +begin; puts 1164; m_105; puts 1165; rescue; puts 1166; puts $!.class; end +$g = 0; begin; puts 1167; p = get_lambda{ puts 1168; $g += 1; retry if $g < 4;; puts 1169}; puts(p.call); puts 1170; rescue; puts 1171; puts $!.class; end +$g = 0; def m_106; puts 1172; p = get_lambda{ puts 1173; $g += 1; retry if $g < 4;; puts 1174}; puts(p.call); puts 1175; end; +begin; puts 1176; m_106; puts 1177; rescue; puts 1178; puts $!.class; end +$g = 0; begin; puts 1179; puts m_106; puts 1180; rescue; puts 1181; puts $!.class; end +$g = 0; def m_107; puts 1182; puts m_106; puts 1183; end; +begin; puts 1184; m_107; puts 1185; rescue; puts 1186; puts $!.class; end +$g = 0; begin; puts 1187; p = get_proc{ puts 1188; $g += 1; retry if $g < 4;; puts 1189}; puts(p.call); puts 1190; rescue; puts 1191; puts $!.class; end +$g = 0; def m_108; puts 1192; p = get_proc{ puts 1193; $g += 1; retry if $g < 4;; puts 1194}; puts(p.call); puts 1195; end; +begin; puts 1196; m_108; puts 1197; rescue; puts 1198; puts $!.class; end +$g = 0; begin; puts 1199; puts m_108; puts 1200; rescue; puts 1201; puts $!.class; end +$g = 0; def m_109; puts 1202; puts m_108; puts 1203; end; +begin; puts 1204; m_109; puts 1205; rescue; puts 1206; puts $!.class; end +$g = 0; begin; puts 1207; p = get_local_block; puts(p.call); puts 1208; rescue; puts 1209; puts $!.class; end +$g = 0; def m_110; puts 1210; p = get_local_block; puts(p.call); puts 1211; end; +begin; puts 1212; m_110; puts 1213; rescue; puts 1214; puts $!.class; end +$g = 0; begin; puts 1215; puts m_110; puts 1216; rescue; puts 1217; puts $!.class; end +$g = 0; def m_111; puts 1218; puts m_110; puts 1219; end; +begin; puts 1220; m_111; puts 1221; rescue; puts 1222; puts $!.class; end +$g = 0; begin; puts 1223; p = get_local_lambda; puts(p.call); puts 1224; rescue; puts 1225; puts $!.class; end +$g = 0; def m_112; puts 1226; p = get_local_lambda; puts(p.call); puts 1227; end; +begin; puts 1228; m_112; puts 1229; rescue; puts 1230; puts $!.class; end +$g = 0; begin; puts 1231; puts m_112; puts 1232; rescue; puts 1233; puts $!.class; end +$g = 0; def m_113; puts 1234; puts m_112; puts 1235; end; +begin; puts 1236; m_113; puts 1237; rescue; puts 1238; puts $!.class; end +$g = 0; begin; puts 1239; p = get_local_proc; puts(p.call); puts 1240; rescue; puts 1241; puts $!.class; end +$g = 0; def m_114; puts 1242; p = get_local_proc; puts(p.call); puts 1243; end; +begin; puts 1244; m_114; puts 1245; rescue; puts 1246; puts $!.class; end +$g = 0; begin; puts 1247; puts m_114; puts 1248; rescue; puts 1249; puts $!.class; end +$g = 0; def m_115; puts 1250; puts m_114; puts 1251; end; +begin; puts 1252; m_115; puts 1253; rescue; puts 1254; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1255; x = lambda { puts 1256; p = lambda{ puts 1257; $g += 1; retry if $g < 4;; puts 1258}; puts p.call; puts 1259}; puts x.call; puts 1260; rescue; puts 1261; puts $!.class; end +$g = 0; def m_116; puts 1262; x = lambda { puts 1263; p = lambda{ puts 1264; $g += 1; retry if $g < 4;; puts 1265}; puts p.call; puts 1266}; puts x.call; puts 1267; end; +begin; puts 1268; m_116; puts 1269; rescue; puts 1270; puts $!.class; end +$g = 0; begin; puts 1271; x = lambda { puts 1272; p = Proc.new{ puts 1273; $g += 1; retry if $g < 4;; puts 1274}; puts p.call; puts 1275}; puts x.call; puts 1276; rescue; puts 1277; puts $!.class; end +$g = 0; def m_117; puts 1278; x = lambda { puts 1279; p = Proc.new{ puts 1280; $g += 1; retry if $g < 4;; puts 1281}; puts p.call; puts 1282}; puts x.call; puts 1283; end; +begin; puts 1284; m_117; puts 1285; rescue; puts 1286; puts $!.class; end +$g = 0; begin; puts 1287; x = lambda { puts 1288; p = get_block{ puts 1289; $g += 1; retry if $g < 4;; puts 1290}; puts p.call; puts 1291}; puts x.call; puts 1292; rescue; puts 1293; puts $!.class; end +$g = 0; def m_118; puts 1294; x = lambda { puts 1295; p = get_block{ puts 1296; $g += 1; retry if $g < 4;; puts 1297}; puts p.call; puts 1298}; puts x.call; puts 1299; end; +begin; puts 1300; m_118; puts 1301; rescue; puts 1302; puts $!.class; end +$g = 0; begin; puts 1303; x = lambda { puts 1304; p = get_lambda{ puts 1305; $g += 1; retry if $g < 4;; puts 1306}; puts p.call; puts 1307}; puts x.call; puts 1308; rescue; puts 1309; puts $!.class; end +$g = 0; def m_119; puts 1310; x = lambda { puts 1311; p = get_lambda{ puts 1312; $g += 1; retry if $g < 4;; puts 1313}; puts p.call; puts 1314}; puts x.call; puts 1315; end; +begin; puts 1316; m_119; puts 1317; rescue; puts 1318; puts $!.class; end +$g = 0; begin; puts 1319; x = lambda { puts 1320; p = get_proc{ puts 1321; $g += 1; retry if $g < 4;; puts 1322}; puts p.call; puts 1323}; puts x.call; puts 1324; rescue; puts 1325; puts $!.class; end +$g = 0; def m_120; puts 1326; x = lambda { puts 1327; p = get_proc{ puts 1328; $g += 1; retry if $g < 4;; puts 1329}; puts p.call; puts 1330}; puts x.call; puts 1331; end; +begin; puts 1332; m_120; puts 1333; rescue; puts 1334; puts $!.class; end +$g = 0; begin; puts 1335; x = lambda { puts 1336; p = get_local_block; puts p.call; puts 1337}; puts x.call; puts 1338; rescue; puts 1339; puts $!.class; end +$g = 0; def m_121; puts 1340; x = lambda { puts 1341; p = get_local_block; puts p.call; puts 1342}; puts x.call; puts 1343; end; +begin; puts 1344; m_121; puts 1345; rescue; puts 1346; puts $!.class; end +$g = 0; begin; puts 1347; x = lambda { puts 1348; p = get_local_lambda; puts p.call; puts 1349}; puts x.call; puts 1350; rescue; puts 1351; puts $!.class; end +$g = 0; def m_122; puts 1352; x = lambda { puts 1353; p = get_local_lambda; puts p.call; puts 1354}; puts x.call; puts 1355; end; +begin; puts 1356; m_122; puts 1357; rescue; puts 1358; puts $!.class; end +$g = 0; begin; puts 1359; x = lambda { puts 1360; p = get_local_proc; puts p.call; puts 1361}; puts x.call; puts 1362; rescue; puts 1363; puts $!.class; end +$g = 0; def m_123; puts 1364; x = lambda { puts 1365; p = get_local_proc; puts p.call; puts 1366}; puts x.call; puts 1367; end; +begin; puts 1368; m_123; puts 1369; rescue; puts 1370; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1371; x = Proc.new { puts 1372; p = lambda{ puts 1373; $g += 1; retry if $g < 4;; puts 1374}; puts p.call; puts 1375}; puts x.call; puts 1376; rescue; puts 1377; puts $!.class; end +$g = 0; def m_124; puts 1378; x = Proc.new { puts 1379; p = lambda{ puts 1380; $g += 1; retry if $g < 4;; puts 1381}; puts p.call; puts 1382}; puts x.call; puts 1383; end; +begin; puts 1384; m_124; puts 1385; rescue; puts 1386; puts $!.class; end +$g = 0; begin; puts 1387; x = Proc.new { puts 1388; p = Proc.new{ puts 1389; $g += 1; retry if $g < 4;; puts 1390}; puts p.call; puts 1391}; puts x.call; puts 1392; rescue; puts 1393; puts $!.class; end +$g = 0; def m_125; puts 1394; x = Proc.new { puts 1395; p = Proc.new{ puts 1396; $g += 1; retry if $g < 4;; puts 1397}; puts p.call; puts 1398}; puts x.call; puts 1399; end; +begin; puts 1400; m_125; puts 1401; rescue; puts 1402; puts $!.class; end +$g = 0; begin; puts 1403; x = Proc.new { puts 1404; p = get_block{ puts 1405; $g += 1; retry if $g < 4;; puts 1406}; puts p.call; puts 1407}; puts x.call; puts 1408; rescue; puts 1409; puts $!.class; end +$g = 0; def m_126; puts 1410; x = Proc.new { puts 1411; p = get_block{ puts 1412; $g += 1; retry if $g < 4;; puts 1413}; puts p.call; puts 1414}; puts x.call; puts 1415; end; +begin; puts 1416; m_126; puts 1417; rescue; puts 1418; puts $!.class; end +$g = 0; begin; puts 1419; x = Proc.new { puts 1420; p = get_lambda{ puts 1421; $g += 1; retry if $g < 4;; puts 1422}; puts p.call; puts 1423}; puts x.call; puts 1424; rescue; puts 1425; puts $!.class; end +$g = 0; def m_127; puts 1426; x = Proc.new { puts 1427; p = get_lambda{ puts 1428; $g += 1; retry if $g < 4;; puts 1429}; puts p.call; puts 1430}; puts x.call; puts 1431; end; +begin; puts 1432; m_127; puts 1433; rescue; puts 1434; puts $!.class; end +$g = 0; begin; puts 1435; x = Proc.new { puts 1436; p = get_proc{ puts 1437; $g += 1; retry if $g < 4;; puts 1438}; puts p.call; puts 1439}; puts x.call; puts 1440; rescue; puts 1441; puts $!.class; end +$g = 0; def m_128; puts 1442; x = Proc.new { puts 1443; p = get_proc{ puts 1444; $g += 1; retry if $g < 4;; puts 1445}; puts p.call; puts 1446}; puts x.call; puts 1447; end; +begin; puts 1448; m_128; puts 1449; rescue; puts 1450; puts $!.class; end +$g = 0; begin; puts 1451; x = Proc.new { puts 1452; p = get_local_block; puts p.call; puts 1453}; puts x.call; puts 1454; rescue; puts 1455; puts $!.class; end +$g = 0; def m_129; puts 1456; x = Proc.new { puts 1457; p = get_local_block; puts p.call; puts 1458}; puts x.call; puts 1459; end; +begin; puts 1460; m_129; puts 1461; rescue; puts 1462; puts $!.class; end +$g = 0; begin; puts 1463; x = Proc.new { puts 1464; p = get_local_lambda; puts p.call; puts 1465}; puts x.call; puts 1466; rescue; puts 1467; puts $!.class; end +$g = 0; def m_130; puts 1468; x = Proc.new { puts 1469; p = get_local_lambda; puts p.call; puts 1470}; puts x.call; puts 1471; end; +begin; puts 1472; m_130; puts 1473; rescue; puts 1474; puts $!.class; end +$g = 0; begin; puts 1475; x = Proc.new { puts 1476; p = get_local_proc; puts p.call; puts 1477}; puts x.call; puts 1478; rescue; puts 1479; puts $!.class; end +$g = 0; def m_131; puts 1480; x = Proc.new { puts 1481; p = get_local_proc; puts p.call; puts 1482}; puts x.call; puts 1483; end; +begin; puts 1484; m_131; puts 1485; rescue; puts 1486; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1487; x = get_block { puts 1488; p = lambda{ puts 1489; $g += 1; retry if $g < 4;; puts 1490}; puts p.call; puts 1491}; puts x.call; puts 1492; rescue; puts 1493; puts $!.class; end +$g = 0; def m_132; puts 1494; x = get_block { puts 1495; p = lambda{ puts 1496; $g += 1; retry if $g < 4;; puts 1497}; puts p.call; puts 1498}; puts x.call; puts 1499; end; +begin; puts 1500; m_132; puts 1501; rescue; puts 1502; puts $!.class; end +$g = 0; begin; puts 1503; x = get_block { puts 1504; p = Proc.new{ puts 1505; $g += 1; retry if $g < 4;; puts 1506}; puts p.call; puts 1507}; puts x.call; puts 1508; rescue; puts 1509; puts $!.class; end +$g = 0; def m_133; puts 1510; x = get_block { puts 1511; p = Proc.new{ puts 1512; $g += 1; retry if $g < 4;; puts 1513}; puts p.call; puts 1514}; puts x.call; puts 1515; end; +begin; puts 1516; m_133; puts 1517; rescue; puts 1518; puts $!.class; end +$g = 0; begin; puts 1519; x = get_block { puts 1520; p = get_block{ puts 1521; $g += 1; retry if $g < 4;; puts 1522}; puts p.call; puts 1523}; puts x.call; puts 1524; rescue; puts 1525; puts $!.class; end +$g = 0; def m_134; puts 1526; x = get_block { puts 1527; p = get_block{ puts 1528; $g += 1; retry if $g < 4;; puts 1529}; puts p.call; puts 1530}; puts x.call; puts 1531; end; +begin; puts 1532; m_134; puts 1533; rescue; puts 1534; puts $!.class; end +$g = 0; begin; puts 1535; x = get_block { puts 1536; p = get_lambda{ puts 1537; $g += 1; retry if $g < 4;; puts 1538}; puts p.call; puts 1539}; puts x.call; puts 1540; rescue; puts 1541; puts $!.class; end +$g = 0; def m_135; puts 1542; x = get_block { puts 1543; p = get_lambda{ puts 1544; $g += 1; retry if $g < 4;; puts 1545}; puts p.call; puts 1546}; puts x.call; puts 1547; end; +begin; puts 1548; m_135; puts 1549; rescue; puts 1550; puts $!.class; end +$g = 0; begin; puts 1551; x = get_block { puts 1552; p = get_proc{ puts 1553; $g += 1; retry if $g < 4;; puts 1554}; puts p.call; puts 1555}; puts x.call; puts 1556; rescue; puts 1557; puts $!.class; end +$g = 0; def m_136; puts 1558; x = get_block { puts 1559; p = get_proc{ puts 1560; $g += 1; retry if $g < 4;; puts 1561}; puts p.call; puts 1562}; puts x.call; puts 1563; end; +begin; puts 1564; m_136; puts 1565; rescue; puts 1566; puts $!.class; end +$g = 0; begin; puts 1567; x = get_block { puts 1568; p = get_local_block; puts p.call; puts 1569}; puts x.call; puts 1570; rescue; puts 1571; puts $!.class; end +$g = 0; def m_137; puts 1572; x = get_block { puts 1573; p = get_local_block; puts p.call; puts 1574}; puts x.call; puts 1575; end; +begin; puts 1576; m_137; puts 1577; rescue; puts 1578; puts $!.class; end +$g = 0; begin; puts 1579; x = get_block { puts 1580; p = get_local_lambda; puts p.call; puts 1581}; puts x.call; puts 1582; rescue; puts 1583; puts $!.class; end +$g = 0; def m_138; puts 1584; x = get_block { puts 1585; p = get_local_lambda; puts p.call; puts 1586}; puts x.call; puts 1587; end; +begin; puts 1588; m_138; puts 1589; rescue; puts 1590; puts $!.class; end +$g = 0; begin; puts 1591; x = get_block { puts 1592; p = get_local_proc; puts p.call; puts 1593}; puts x.call; puts 1594; rescue; puts 1595; puts $!.class; end +$g = 0; def m_139; puts 1596; x = get_block { puts 1597; p = get_local_proc; puts p.call; puts 1598}; puts x.call; puts 1599; end; +begin; puts 1600; m_139; puts 1601; rescue; puts 1602; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1603; x = get_lambda { puts 1604; p = lambda{ puts 1605; $g += 1; retry if $g < 4;; puts 1606}; puts p.call; puts 1607}; puts x.call; puts 1608; rescue; puts 1609; puts $!.class; end +$g = 0; def m_140; puts 1610; x = get_lambda { puts 1611; p = lambda{ puts 1612; $g += 1; retry if $g < 4;; puts 1613}; puts p.call; puts 1614}; puts x.call; puts 1615; end; +begin; puts 1616; m_140; puts 1617; rescue; puts 1618; puts $!.class; end +$g = 0; begin; puts 1619; x = get_lambda { puts 1620; p = Proc.new{ puts 1621; $g += 1; retry if $g < 4;; puts 1622}; puts p.call; puts 1623}; puts x.call; puts 1624; rescue; puts 1625; puts $!.class; end +$g = 0; def m_141; puts 1626; x = get_lambda { puts 1627; p = Proc.new{ puts 1628; $g += 1; retry if $g < 4;; puts 1629}; puts p.call; puts 1630}; puts x.call; puts 1631; end; +begin; puts 1632; m_141; puts 1633; rescue; puts 1634; puts $!.class; end +$g = 0; begin; puts 1635; x = get_lambda { puts 1636; p = get_block{ puts 1637; $g += 1; retry if $g < 4;; puts 1638}; puts p.call; puts 1639}; puts x.call; puts 1640; rescue; puts 1641; puts $!.class; end +$g = 0; def m_142; puts 1642; x = get_lambda { puts 1643; p = get_block{ puts 1644; $g += 1; retry if $g < 4;; puts 1645}; puts p.call; puts 1646}; puts x.call; puts 1647; end; +begin; puts 1648; m_142; puts 1649; rescue; puts 1650; puts $!.class; end +$g = 0; begin; puts 1651; x = get_lambda { puts 1652; p = get_lambda{ puts 1653; $g += 1; retry if $g < 4;; puts 1654}; puts p.call; puts 1655}; puts x.call; puts 1656; rescue; puts 1657; puts $!.class; end +$g = 0; def m_143; puts 1658; x = get_lambda { puts 1659; p = get_lambda{ puts 1660; $g += 1; retry if $g < 4;; puts 1661}; puts p.call; puts 1662}; puts x.call; puts 1663; end; +begin; puts 1664; m_143; puts 1665; rescue; puts 1666; puts $!.class; end +$g = 0; begin; puts 1667; x = get_lambda { puts 1668; p = get_proc{ puts 1669; $g += 1; retry if $g < 4;; puts 1670}; puts p.call; puts 1671}; puts x.call; puts 1672; rescue; puts 1673; puts $!.class; end +$g = 0; def m_144; puts 1674; x = get_lambda { puts 1675; p = get_proc{ puts 1676; $g += 1; retry if $g < 4;; puts 1677}; puts p.call; puts 1678}; puts x.call; puts 1679; end; +begin; puts 1680; m_144; puts 1681; rescue; puts 1682; puts $!.class; end +$g = 0; begin; puts 1683; x = get_lambda { puts 1684; p = get_local_block; puts p.call; puts 1685}; puts x.call; puts 1686; rescue; puts 1687; puts $!.class; end +$g = 0; def m_145; puts 1688; x = get_lambda { puts 1689; p = get_local_block; puts p.call; puts 1690}; puts x.call; puts 1691; end; +begin; puts 1692; m_145; puts 1693; rescue; puts 1694; puts $!.class; end +$g = 0; begin; puts 1695; x = get_lambda { puts 1696; p = get_local_lambda; puts p.call; puts 1697}; puts x.call; puts 1698; rescue; puts 1699; puts $!.class; end +$g = 0; def m_146; puts 1700; x = get_lambda { puts 1701; p = get_local_lambda; puts p.call; puts 1702}; puts x.call; puts 1703; end; +begin; puts 1704; m_146; puts 1705; rescue; puts 1706; puts $!.class; end +$g = 0; begin; puts 1707; x = get_lambda { puts 1708; p = get_local_proc; puts p.call; puts 1709}; puts x.call; puts 1710; rescue; puts 1711; puts $!.class; end +$g = 0; def m_147; puts 1712; x = get_lambda { puts 1713; p = get_local_proc; puts p.call; puts 1714}; puts x.call; puts 1715; end; +begin; puts 1716; m_147; puts 1717; rescue; puts 1718; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1719; x = get_proc { puts 1720; p = lambda{ puts 1721; $g += 1; retry if $g < 4;; puts 1722}; puts p.call; puts 1723}; puts x.call; puts 1724; rescue; puts 1725; puts $!.class; end +$g = 0; def m_148; puts 1726; x = get_proc { puts 1727; p = lambda{ puts 1728; $g += 1; retry if $g < 4;; puts 1729}; puts p.call; puts 1730}; puts x.call; puts 1731; end; +begin; puts 1732; m_148; puts 1733; rescue; puts 1734; puts $!.class; end +$g = 0; begin; puts 1735; x = get_proc { puts 1736; p = Proc.new{ puts 1737; $g += 1; retry if $g < 4;; puts 1738}; puts p.call; puts 1739}; puts x.call; puts 1740; rescue; puts 1741; puts $!.class; end +$g = 0; def m_149; puts 1742; x = get_proc { puts 1743; p = Proc.new{ puts 1744; $g += 1; retry if $g < 4;; puts 1745}; puts p.call; puts 1746}; puts x.call; puts 1747; end; +begin; puts 1748; m_149; puts 1749; rescue; puts 1750; puts $!.class; end +$g = 0; begin; puts 1751; x = get_proc { puts 1752; p = get_block{ puts 1753; $g += 1; retry if $g < 4;; puts 1754}; puts p.call; puts 1755}; puts x.call; puts 1756; rescue; puts 1757; puts $!.class; end +$g = 0; def m_150; puts 1758; x = get_proc { puts 1759; p = get_block{ puts 1760; $g += 1; retry if $g < 4;; puts 1761}; puts p.call; puts 1762}; puts x.call; puts 1763; end; +begin; puts 1764; m_150; puts 1765; rescue; puts 1766; puts $!.class; end +$g = 0; begin; puts 1767; x = get_proc { puts 1768; p = get_lambda{ puts 1769; $g += 1; retry if $g < 4;; puts 1770}; puts p.call; puts 1771}; puts x.call; puts 1772; rescue; puts 1773; puts $!.class; end +$g = 0; def m_151; puts 1774; x = get_proc { puts 1775; p = get_lambda{ puts 1776; $g += 1; retry if $g < 4;; puts 1777}; puts p.call; puts 1778}; puts x.call; puts 1779; end; +begin; puts 1780; m_151; puts 1781; rescue; puts 1782; puts $!.class; end +$g = 0; begin; puts 1783; x = get_proc { puts 1784; p = get_proc{ puts 1785; $g += 1; retry if $g < 4;; puts 1786}; puts p.call; puts 1787}; puts x.call; puts 1788; rescue; puts 1789; puts $!.class; end +$g = 0; def m_152; puts 1790; x = get_proc { puts 1791; p = get_proc{ puts 1792; $g += 1; retry if $g < 4;; puts 1793}; puts p.call; puts 1794}; puts x.call; puts 1795; end; +begin; puts 1796; m_152; puts 1797; rescue; puts 1798; puts $!.class; end +$g = 0; begin; puts 1799; x = get_proc { puts 1800; p = get_local_block; puts p.call; puts 1801}; puts x.call; puts 1802; rescue; puts 1803; puts $!.class; end +$g = 0; def m_153; puts 1804; x = get_proc { puts 1805; p = get_local_block; puts p.call; puts 1806}; puts x.call; puts 1807; end; +begin; puts 1808; m_153; puts 1809; rescue; puts 1810; puts $!.class; end +$g = 0; begin; puts 1811; x = get_proc { puts 1812; p = get_local_lambda; puts p.call; puts 1813}; puts x.call; puts 1814; rescue; puts 1815; puts $!.class; end +$g = 0; def m_154; puts 1816; x = get_proc { puts 1817; p = get_local_lambda; puts p.call; puts 1818}; puts x.call; puts 1819; end; +begin; puts 1820; m_154; puts 1821; rescue; puts 1822; puts $!.class; end +$g = 0; begin; puts 1823; x = get_proc { puts 1824; p = get_local_proc; puts p.call; puts 1825}; puts x.call; puts 1826; rescue; puts 1827; puts $!.class; end +$g = 0; def m_155; puts 1828; x = get_proc { puts 1829; p = get_local_proc; puts p.call; puts 1830}; puts x.call; puts 1831; end; +begin; puts 1832; m_155; puts 1833; rescue; puts 1834; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1835; for i in [1, 2]; puts 1836; p = lambda{ puts 1837; $g += 1; retry if $g < 4;; puts 1838}; puts p.call; puts 1839; end; puts 1840; rescue; puts 1841; puts $!.class; end +$g = 0; def m_156; puts 1842; for i in [1, 2]; puts 1843; p = lambda{ puts 1844; $g += 1; retry if $g < 4;; puts 1845}; puts p.call; puts 1846; end; puts 1847; end; +begin; puts 1848; m_156; puts 1849; rescue; puts 1850; puts $!.class; end +$g = 0; begin; puts 1851; for i in [1, 2]; puts 1852; p = Proc.new{ puts 1853; $g += 1; retry if $g < 4;; puts 1854}; puts p.call; puts 1855; end; puts 1856; rescue; puts 1857; puts $!.class; end +$g = 0; def m_157; puts 1858; for i in [1, 2]; puts 1859; p = Proc.new{ puts 1860; $g += 1; retry if $g < 4;; puts 1861}; puts p.call; puts 1862; end; puts 1863; end; +begin; puts 1864; m_157; puts 1865; rescue; puts 1866; puts $!.class; end +$g = 0; begin; puts 1867; for i in [1, 2]; puts 1868; p = get_block{ puts 1869; $g += 1; retry if $g < 4;; puts 1870}; puts p.call; puts 1871; end; puts 1872; rescue; puts 1873; puts $!.class; end +$g = 0; def m_158; puts 1874; for i in [1, 2]; puts 1875; p = get_block{ puts 1876; $g += 1; retry if $g < 4;; puts 1877}; puts p.call; puts 1878; end; puts 1879; end; +begin; puts 1880; m_158; puts 1881; rescue; puts 1882; puts $!.class; end +$g = 0; begin; puts 1883; for i in [1, 2]; puts 1884; p = get_lambda{ puts 1885; $g += 1; retry if $g < 4;; puts 1886}; puts p.call; puts 1887; end; puts 1888; rescue; puts 1889; puts $!.class; end +$g = 0; def m_159; puts 1890; for i in [1, 2]; puts 1891; p = get_lambda{ puts 1892; $g += 1; retry if $g < 4;; puts 1893}; puts p.call; puts 1894; end; puts 1895; end; +begin; puts 1896; m_159; puts 1897; rescue; puts 1898; puts $!.class; end +$g = 0; begin; puts 1899; for i in [1, 2]; puts 1900; p = get_proc{ puts 1901; $g += 1; retry if $g < 4;; puts 1902}; puts p.call; puts 1903; end; puts 1904; rescue; puts 1905; puts $!.class; end +$g = 0; def m_160; puts 1906; for i in [1, 2]; puts 1907; p = get_proc{ puts 1908; $g += 1; retry if $g < 4;; puts 1909}; puts p.call; puts 1910; end; puts 1911; end; +begin; puts 1912; m_160; puts 1913; rescue; puts 1914; puts $!.class; end +$g = 0; begin; puts 1915; for i in [1, 2]; puts 1916; p = get_local_block; puts p.call; puts 1917; end; puts 1918; rescue; puts 1919; puts $!.class; end +$g = 0; def m_161; puts 1920; for i in [1, 2]; puts 1921; p = get_local_block; puts p.call; puts 1922; end; puts 1923; end; +begin; puts 1924; m_161; puts 1925; rescue; puts 1926; puts $!.class; end +$g = 0; begin; puts 1927; for i in [1, 2]; puts 1928; p = get_local_lambda; puts p.call; puts 1929; end; puts 1930; rescue; puts 1931; puts $!.class; end +$g = 0; def m_162; puts 1932; for i in [1, 2]; puts 1933; p = get_local_lambda; puts p.call; puts 1934; end; puts 1935; end; +begin; puts 1936; m_162; puts 1937; rescue; puts 1938; puts $!.class; end +$g = 0; begin; puts 1939; for i in [1, 2]; puts 1940; p = get_local_proc; puts p.call; puts 1941; end; puts 1942; rescue; puts 1943; puts $!.class; end +$g = 0; def m_163; puts 1944; for i in [1, 2]; puts 1945; p = get_local_proc; puts p.call; puts 1946; end; puts 1947; end; +begin; puts 1948; m_163; puts 1949; rescue; puts 1950; puts $!.class; end +end +test diff --git a/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_return.rb b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_return.rb new file mode 100644 index 0000000000..3c83bc6629 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_block_ctrl_flow_return.rb @@ -0,0 +1,595 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +# helper +def myeval(line); puts 1; eval(line); puts 2; end +def call1(x); puts 3; call2(x); puts 4; end +def call2(x); puts 5; call3(x); puts 6; end +def call3(x); puts 7; puts x.call; puts 8; end + +# producer + +def get_block(&p); p; end +def get_lambda(&p); lambda(&p); end +def get_proc(&p); Proc.new(&p); end + +def get_local_block; get_block { puts 9; return; puts 10 }; end +def get_local_lambda; lambda { puts 11; return; puts 12 }; end +def get_local_proc; Proc.new { puts 13; return; puts 14 }; end + +# consumer + +# taking arguments +def iterator_via_yield; puts 15; x = yield; puts x; puts 16; end +def iterator_via_call(&p); puts 17; puts(p.call); puts 18; end + +def method_call_iterator_via_yield(&p); puts 19; iterator_via_yield(&p); puts 20; end +def method_call_iterator_via_call(&p); puts 21; iterator_via_call(&p); puts 22; end + +def method_use_lambda_and_yield; puts 23; x = lambda { puts 24; yield; puts 25 }; puts x.call; puts 26; end +def method_use_proc_and_yield; puts 27; x = Proc.new { puts 28; yield; puts 29 }; puts x.call; puts 30; end +def method_use_lambda_and_call(&p); puts 31; x = lambda { puts 32; p.call; puts 33 }; puts x.call; puts 34; end +def method_use_proc_and_call(&p); puts 35; x = Proc.new { puts 36; p.call; puts 37 }; puts x.call; puts 38; end + +def method_use_lambda_and_yield_2; puts 39; x = lambda { puts 40; yield; puts 41 }; call1(x); puts 42; end + +def method_yield_in_loop; puts 43; for i in [1, 2]; puts 44; yield; puts 45; end; puts 46; end +def method_call_in_loop(&p); puts 47; for i in [3, 4]; puts 48; p.call; puts 49; end; puts 50; end + +# created in-place +def test +$g = 0; begin; puts 51; iterator_via_yield { puts 52; return; puts 53}; puts 54; rescue; puts 55; puts $!.class; end +$g = 0; def m_1; puts 56; $g = 0; begin; puts 57; iterator_via_yield { puts 58; return; puts 59}; puts 60; rescue; puts 61; puts $!.class; end; puts 62; end; m_1 +$g = 0; begin; puts 63; iterator_via_call { puts 64; return; puts 65}; puts 66; rescue; puts 67; puts $!.class; end +$g = 0; def m_2; puts 68; $g = 0; begin; puts 69; iterator_via_call { puts 70; return; puts 71}; puts 72; rescue; puts 73; puts $!.class; end; puts 74; end; m_2 +$g = 0; begin; puts 75; method_call_iterator_via_yield { puts 76; return; puts 77}; puts 78; rescue; puts 79; puts $!.class; end +$g = 0; def m_3; puts 80; $g = 0; begin; puts 81; method_call_iterator_via_yield { puts 82; return; puts 83}; puts 84; rescue; puts 85; puts $!.class; end; puts 86; end; m_3 +$g = 0; begin; puts 87; method_call_iterator_via_call { puts 88; return; puts 89}; puts 90; rescue; puts 91; puts $!.class; end +$g = 0; def m_4; puts 92; $g = 0; begin; puts 93; method_call_iterator_via_call { puts 94; return; puts 95}; puts 96; rescue; puts 97; puts $!.class; end; puts 98; end; m_4 +$g = 0; begin; puts 99; method_use_lambda_and_yield { puts 100; return; puts 101}; puts 102; rescue; puts 103; puts $!.class; end +$g = 0; def m_5; puts 104; $g = 0; begin; puts 105; method_use_lambda_and_yield { puts 106; return; puts 107}; puts 108; rescue; puts 109; puts $!.class; end; puts 110; end; m_5 +$g = 0; begin; puts 111; method_use_proc_and_yield { puts 112; return; puts 113}; puts 114; rescue; puts 115; puts $!.class; end +$g = 0; def m_6; puts 116; $g = 0; begin; puts 117; method_use_proc_and_yield { puts 118; return; puts 119}; puts 120; rescue; puts 121; puts $!.class; end; puts 122; end; m_6 +$g = 0; begin; puts 123; method_use_lambda_and_call { puts 124; return; puts 125}; puts 126; rescue; puts 127; puts $!.class; end +$g = 0; def m_7; puts 128; $g = 0; begin; puts 129; method_use_lambda_and_call { puts 130; return; puts 131}; puts 132; rescue; puts 133; puts $!.class; end; puts 134; end; m_7 +$g = 0; begin; puts 135; method_use_proc_and_call { puts 136; return; puts 137}; puts 138; rescue; puts 139; puts $!.class; end +$g = 0; def m_8; puts 140; $g = 0; begin; puts 141; method_use_proc_and_call { puts 142; return; puts 143}; puts 144; rescue; puts 145; puts $!.class; end; puts 146; end; m_8 +$g = 0; begin; puts 147; method_use_lambda_and_yield_2 { puts 148; return; puts 149}; puts 150; rescue; puts 151; puts $!.class; end +$g = 0; def m_9; puts 152; $g = 0; begin; puts 153; method_use_lambda_and_yield_2 { puts 154; return; puts 155}; puts 156; rescue; puts 157; puts $!.class; end; puts 158; end; m_9 +$g = 0; begin; puts 159; method_yield_in_loop { puts 160; return; puts 161}; puts 162; rescue; puts 163; puts $!.class; end +$g = 0; def m_10; puts 164; $g = 0; begin; puts 165; method_yield_in_loop { puts 166; return; puts 167}; puts 168; rescue; puts 169; puts $!.class; end; puts 170; end; m_10 +$g = 0; begin; puts 171; method_call_in_loop { puts 172; return; puts 173}; puts 174; rescue; puts 175; puts $!.class; end +$g = 0; def m_11; puts 176; $g = 0; begin; puts 177; method_call_in_loop { puts 178; return; puts 179}; puts 180; rescue; puts 181; puts $!.class; end; puts 182; end; m_11 +end +test + + +# created locally or from method +def test +$g = 0; begin; p = lambda{ puts 183; return; puts 184}; puts 185; iterator_via_yield(&p); puts 186; rescue; puts 187; puts $!.class; end +$g = 0; def m_12; p = lambda{ puts 188; return; puts 189}; puts 190; iterator_via_yield(&p); puts 191; end; +begin; puts 192; m_12; puts 193; rescue; puts 194; puts $!.class; end +$g = 0; begin; p = lambda{ puts 195; return; puts 196}; puts 197; iterator_via_call(&p); puts 198; rescue; puts 199; puts $!.class; end +$g = 0; def m_13; p = lambda{ puts 200; return; puts 201}; puts 202; iterator_via_call(&p); puts 203; end; +begin; puts 204; m_13; puts 205; rescue; puts 206; puts $!.class; end +$g = 0; begin; p = lambda{ puts 207; return; puts 208}; puts 209; method_call_iterator_via_yield(&p); puts 210; rescue; puts 211; puts $!.class; end +$g = 0; def m_14; p = lambda{ puts 212; return; puts 213}; puts 214; method_call_iterator_via_yield(&p); puts 215; end; +begin; puts 216; m_14; puts 217; rescue; puts 218; puts $!.class; end +$g = 0; begin; p = lambda{ puts 219; return; puts 220}; puts 221; method_call_iterator_via_call(&p); puts 222; rescue; puts 223; puts $!.class; end +$g = 0; def m_15; p = lambda{ puts 224; return; puts 225}; puts 226; method_call_iterator_via_call(&p); puts 227; end; +begin; puts 228; m_15; puts 229; rescue; puts 230; puts $!.class; end +$g = 0; begin; p = lambda{ puts 231; return; puts 232}; puts 233; method_use_lambda_and_yield(&p); puts 234; rescue; puts 235; puts $!.class; end +$g = 0; def m_16; p = lambda{ puts 236; return; puts 237}; puts 238; method_use_lambda_and_yield(&p); puts 239; end; +begin; puts 240; m_16; puts 241; rescue; puts 242; puts $!.class; end +$g = 0; begin; p = lambda{ puts 243; return; puts 244}; puts 245; method_use_proc_and_yield(&p); puts 246; rescue; puts 247; puts $!.class; end +$g = 0; def m_17; p = lambda{ puts 248; return; puts 249}; puts 250; method_use_proc_and_yield(&p); puts 251; end; +begin; puts 252; m_17; puts 253; rescue; puts 254; puts $!.class; end +$g = 0; begin; p = lambda{ puts 255; return; puts 256}; puts 257; method_use_lambda_and_call(&p); puts 258; rescue; puts 259; puts $!.class; end +$g = 0; def m_18; p = lambda{ puts 260; return; puts 261}; puts 262; method_use_lambda_and_call(&p); puts 263; end; +begin; puts 264; m_18; puts 265; rescue; puts 266; puts $!.class; end +$g = 0; begin; p = lambda{ puts 267; return; puts 268}; puts 269; method_use_proc_and_call(&p); puts 270; rescue; puts 271; puts $!.class; end +$g = 0; def m_19; p = lambda{ puts 272; return; puts 273}; puts 274; method_use_proc_and_call(&p); puts 275; end; +begin; puts 276; m_19; puts 277; rescue; puts 278; puts $!.class; end +$g = 0; begin; p = lambda{ puts 279; return; puts 280}; puts 281; method_use_lambda_and_yield_2(&p); puts 282; rescue; puts 283; puts $!.class; end +$g = 0; def m_20; p = lambda{ puts 284; return; puts 285}; puts 286; method_use_lambda_and_yield_2(&p); puts 287; end; +begin; puts 288; m_20; puts 289; rescue; puts 290; puts $!.class; end +$g = 0; begin; p = lambda{ puts 291; return; puts 292}; puts 293; method_yield_in_loop(&p); puts 294; rescue; puts 295; puts $!.class; end +$g = 0; def m_21; p = lambda{ puts 296; return; puts 297}; puts 298; method_yield_in_loop(&p); puts 299; end; +begin; puts 300; m_21; puts 301; rescue; puts 302; puts $!.class; end +$g = 0; begin; p = lambda{ puts 303; return; puts 304}; puts 305; method_call_in_loop(&p); puts 306; rescue; puts 307; puts $!.class; end +$g = 0; def m_22; p = lambda{ puts 308; return; puts 309}; puts 310; method_call_in_loop(&p); puts 311; end; +begin; puts 312; m_22; puts 313; rescue; puts 314; puts $!.class; end +end +test + +def test +$g = 0; begin; p = Proc.new{ puts 315; return; puts 316}; puts 317; iterator_via_yield(&p); puts 318; rescue; puts 319; puts $!.class; end +$g = 0; def m_23; p = Proc.new{ puts 320; return; puts 321}; puts 322; iterator_via_yield(&p); puts 323; end; +begin; puts 324; m_23; puts 325; rescue; puts 326; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 327; return; puts 328}; puts 329; iterator_via_call(&p); puts 330; rescue; puts 331; puts $!.class; end +$g = 0; def m_24; p = Proc.new{ puts 332; return; puts 333}; puts 334; iterator_via_call(&p); puts 335; end; +begin; puts 336; m_24; puts 337; rescue; puts 338; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 339; return; puts 340}; puts 341; method_call_iterator_via_yield(&p); puts 342; rescue; puts 343; puts $!.class; end +$g = 0; def m_25; p = Proc.new{ puts 344; return; puts 345}; puts 346; method_call_iterator_via_yield(&p); puts 347; end; +begin; puts 348; m_25; puts 349; rescue; puts 350; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 351; return; puts 352}; puts 353; method_call_iterator_via_call(&p); puts 354; rescue; puts 355; puts $!.class; end +$g = 0; def m_26; p = Proc.new{ puts 356; return; puts 357}; puts 358; method_call_iterator_via_call(&p); puts 359; end; +begin; puts 360; m_26; puts 361; rescue; puts 362; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 363; return; puts 364}; puts 365; method_use_lambda_and_yield(&p); puts 366; rescue; puts 367; puts $!.class; end +$g = 0; def m_27; p = Proc.new{ puts 368; return; puts 369}; puts 370; method_use_lambda_and_yield(&p); puts 371; end; +begin; puts 372; m_27; puts 373; rescue; puts 374; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 375; return; puts 376}; puts 377; method_use_proc_and_yield(&p); puts 378; rescue; puts 379; puts $!.class; end +$g = 0; def m_28; p = Proc.new{ puts 380; return; puts 381}; puts 382; method_use_proc_and_yield(&p); puts 383; end; +begin; puts 384; m_28; puts 385; rescue; puts 386; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 387; return; puts 388}; puts 389; method_use_lambda_and_call(&p); puts 390; rescue; puts 391; puts $!.class; end +$g = 0; def m_29; p = Proc.new{ puts 392; return; puts 393}; puts 394; method_use_lambda_and_call(&p); puts 395; end; +begin; puts 396; m_29; puts 397; rescue; puts 398; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 399; return; puts 400}; puts 401; method_use_proc_and_call(&p); puts 402; rescue; puts 403; puts $!.class; end +$g = 0; def m_30; p = Proc.new{ puts 404; return; puts 405}; puts 406; method_use_proc_and_call(&p); puts 407; end; +begin; puts 408; m_30; puts 409; rescue; puts 410; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 411; return; puts 412}; puts 413; method_use_lambda_and_yield_2(&p); puts 414; rescue; puts 415; puts $!.class; end +$g = 0; def m_31; p = Proc.new{ puts 416; return; puts 417}; puts 418; method_use_lambda_and_yield_2(&p); puts 419; end; +begin; puts 420; m_31; puts 421; rescue; puts 422; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 423; return; puts 424}; puts 425; method_yield_in_loop(&p); puts 426; rescue; puts 427; puts $!.class; end +$g = 0; def m_32; p = Proc.new{ puts 428; return; puts 429}; puts 430; method_yield_in_loop(&p); puts 431; end; +begin; puts 432; m_32; puts 433; rescue; puts 434; puts $!.class; end +$g = 0; begin; p = Proc.new{ puts 435; return; puts 436}; puts 437; method_call_in_loop(&p); puts 438; rescue; puts 439; puts $!.class; end +$g = 0; def m_33; p = Proc.new{ puts 440; return; puts 441}; puts 442; method_call_in_loop(&p); puts 443; end; +begin; puts 444; m_33; puts 445; rescue; puts 446; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_block{ puts 447; return; puts 448}; puts 449; iterator_via_yield(&p); puts 450; rescue; puts 451; puts $!.class; end +$g = 0; def m_34; p = get_block{ puts 452; return; puts 453}; puts 454; iterator_via_yield(&p); puts 455; end; +begin; puts 456; m_34; puts 457; rescue; puts 458; puts $!.class; end +$g = 0; begin; p = get_block{ puts 459; return; puts 460}; puts 461; iterator_via_call(&p); puts 462; rescue; puts 463; puts $!.class; end +$g = 0; def m_35; p = get_block{ puts 464; return; puts 465}; puts 466; iterator_via_call(&p); puts 467; end; +begin; puts 468; m_35; puts 469; rescue; puts 470; puts $!.class; end +$g = 0; begin; p = get_block{ puts 471; return; puts 472}; puts 473; method_call_iterator_via_yield(&p); puts 474; rescue; puts 475; puts $!.class; end +$g = 0; def m_36; p = get_block{ puts 476; return; puts 477}; puts 478; method_call_iterator_via_yield(&p); puts 479; end; +begin; puts 480; m_36; puts 481; rescue; puts 482; puts $!.class; end +$g = 0; begin; p = get_block{ puts 483; return; puts 484}; puts 485; method_call_iterator_via_call(&p); puts 486; rescue; puts 487; puts $!.class; end +$g = 0; def m_37; p = get_block{ puts 488; return; puts 489}; puts 490; method_call_iterator_via_call(&p); puts 491; end; +begin; puts 492; m_37; puts 493; rescue; puts 494; puts $!.class; end +$g = 0; begin; p = get_block{ puts 495; return; puts 496}; puts 497; method_use_lambda_and_yield(&p); puts 498; rescue; puts 499; puts $!.class; end +$g = 0; def m_38; p = get_block{ puts 500; return; puts 501}; puts 502; method_use_lambda_and_yield(&p); puts 503; end; +begin; puts 504; m_38; puts 505; rescue; puts 506; puts $!.class; end +$g = 0; begin; p = get_block{ puts 507; return; puts 508}; puts 509; method_use_proc_and_yield(&p); puts 510; rescue; puts 511; puts $!.class; end +$g = 0; def m_39; p = get_block{ puts 512; return; puts 513}; puts 514; method_use_proc_and_yield(&p); puts 515; end; +begin; puts 516; m_39; puts 517; rescue; puts 518; puts $!.class; end +$g = 0; begin; p = get_block{ puts 519; return; puts 520}; puts 521; method_use_lambda_and_call(&p); puts 522; rescue; puts 523; puts $!.class; end +$g = 0; def m_40; p = get_block{ puts 524; return; puts 525}; puts 526; method_use_lambda_and_call(&p); puts 527; end; +begin; puts 528; m_40; puts 529; rescue; puts 530; puts $!.class; end +$g = 0; begin; p = get_block{ puts 531; return; puts 532}; puts 533; method_use_proc_and_call(&p); puts 534; rescue; puts 535; puts $!.class; end +$g = 0; def m_41; p = get_block{ puts 536; return; puts 537}; puts 538; method_use_proc_and_call(&p); puts 539; end; +begin; puts 540; m_41; puts 541; rescue; puts 542; puts $!.class; end +$g = 0; begin; p = get_block{ puts 543; return; puts 544}; puts 545; method_use_lambda_and_yield_2(&p); puts 546; rescue; puts 547; puts $!.class; end +$g = 0; def m_42; p = get_block{ puts 548; return; puts 549}; puts 550; method_use_lambda_and_yield_2(&p); puts 551; end; +begin; puts 552; m_42; puts 553; rescue; puts 554; puts $!.class; end +$g = 0; begin; p = get_block{ puts 555; return; puts 556}; puts 557; method_yield_in_loop(&p); puts 558; rescue; puts 559; puts $!.class; end +$g = 0; def m_43; p = get_block{ puts 560; return; puts 561}; puts 562; method_yield_in_loop(&p); puts 563; end; +begin; puts 564; m_43; puts 565; rescue; puts 566; puts $!.class; end +$g = 0; begin; p = get_block{ puts 567; return; puts 568}; puts 569; method_call_in_loop(&p); puts 570; rescue; puts 571; puts $!.class; end +$g = 0; def m_44; p = get_block{ puts 572; return; puts 573}; puts 574; method_call_in_loop(&p); puts 575; end; +begin; puts 576; m_44; puts 577; rescue; puts 578; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_lambda{ puts 579; return; puts 580}; puts 581; iterator_via_yield(&p); puts 582; rescue; puts 583; puts $!.class; end +$g = 0; def m_45; p = get_lambda{ puts 584; return; puts 585}; puts 586; iterator_via_yield(&p); puts 587; end; +begin; puts 588; m_45; puts 589; rescue; puts 590; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 591; return; puts 592}; puts 593; iterator_via_call(&p); puts 594; rescue; puts 595; puts $!.class; end +$g = 0; def m_46; p = get_lambda{ puts 596; return; puts 597}; puts 598; iterator_via_call(&p); puts 599; end; +begin; puts 600; m_46; puts 601; rescue; puts 602; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 603; return; puts 604}; puts 605; method_call_iterator_via_yield(&p); puts 606; rescue; puts 607; puts $!.class; end +$g = 0; def m_47; p = get_lambda{ puts 608; return; puts 609}; puts 610; method_call_iterator_via_yield(&p); puts 611; end; +begin; puts 612; m_47; puts 613; rescue; puts 614; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 615; return; puts 616}; puts 617; method_call_iterator_via_call(&p); puts 618; rescue; puts 619; puts $!.class; end +$g = 0; def m_48; p = get_lambda{ puts 620; return; puts 621}; puts 622; method_call_iterator_via_call(&p); puts 623; end; +begin; puts 624; m_48; puts 625; rescue; puts 626; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 627; return; puts 628}; puts 629; method_use_lambda_and_yield(&p); puts 630; rescue; puts 631; puts $!.class; end +$g = 0; def m_49; p = get_lambda{ puts 632; return; puts 633}; puts 634; method_use_lambda_and_yield(&p); puts 635; end; +begin; puts 636; m_49; puts 637; rescue; puts 638; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 639; return; puts 640}; puts 641; method_use_proc_and_yield(&p); puts 642; rescue; puts 643; puts $!.class; end +$g = 0; def m_50; p = get_lambda{ puts 644; return; puts 645}; puts 646; method_use_proc_and_yield(&p); puts 647; end; +begin; puts 648; m_50; puts 649; rescue; puts 650; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 651; return; puts 652}; puts 653; method_use_lambda_and_call(&p); puts 654; rescue; puts 655; puts $!.class; end +$g = 0; def m_51; p = get_lambda{ puts 656; return; puts 657}; puts 658; method_use_lambda_and_call(&p); puts 659; end; +begin; puts 660; m_51; puts 661; rescue; puts 662; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 663; return; puts 664}; puts 665; method_use_proc_and_call(&p); puts 666; rescue; puts 667; puts $!.class; end +$g = 0; def m_52; p = get_lambda{ puts 668; return; puts 669}; puts 670; method_use_proc_and_call(&p); puts 671; end; +begin; puts 672; m_52; puts 673; rescue; puts 674; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 675; return; puts 676}; puts 677; method_use_lambda_and_yield_2(&p); puts 678; rescue; puts 679; puts $!.class; end +$g = 0; def m_53; p = get_lambda{ puts 680; return; puts 681}; puts 682; method_use_lambda_and_yield_2(&p); puts 683; end; +begin; puts 684; m_53; puts 685; rescue; puts 686; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 687; return; puts 688}; puts 689; method_yield_in_loop(&p); puts 690; rescue; puts 691; puts $!.class; end +$g = 0; def m_54; p = get_lambda{ puts 692; return; puts 693}; puts 694; method_yield_in_loop(&p); puts 695; end; +begin; puts 696; m_54; puts 697; rescue; puts 698; puts $!.class; end +$g = 0; begin; p = get_lambda{ puts 699; return; puts 700}; puts 701; method_call_in_loop(&p); puts 702; rescue; puts 703; puts $!.class; end +$g = 0; def m_55; p = get_lambda{ puts 704; return; puts 705}; puts 706; method_call_in_loop(&p); puts 707; end; +begin; puts 708; m_55; puts 709; rescue; puts 710; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_proc{ puts 711; return; puts 712}; puts 713; iterator_via_yield(&p); puts 714; rescue; puts 715; puts $!.class; end +$g = 0; def m_56; p = get_proc{ puts 716; return; puts 717}; puts 718; iterator_via_yield(&p); puts 719; end; +begin; puts 720; m_56; puts 721; rescue; puts 722; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 723; return; puts 724}; puts 725; iterator_via_call(&p); puts 726; rescue; puts 727; puts $!.class; end +$g = 0; def m_57; p = get_proc{ puts 728; return; puts 729}; puts 730; iterator_via_call(&p); puts 731; end; +begin; puts 732; m_57; puts 733; rescue; puts 734; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 735; return; puts 736}; puts 737; method_call_iterator_via_yield(&p); puts 738; rescue; puts 739; puts $!.class; end +$g = 0; def m_58; p = get_proc{ puts 740; return; puts 741}; puts 742; method_call_iterator_via_yield(&p); puts 743; end; +begin; puts 744; m_58; puts 745; rescue; puts 746; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 747; return; puts 748}; puts 749; method_call_iterator_via_call(&p); puts 750; rescue; puts 751; puts $!.class; end +$g = 0; def m_59; p = get_proc{ puts 752; return; puts 753}; puts 754; method_call_iterator_via_call(&p); puts 755; end; +begin; puts 756; m_59; puts 757; rescue; puts 758; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 759; return; puts 760}; puts 761; method_use_lambda_and_yield(&p); puts 762; rescue; puts 763; puts $!.class; end +$g = 0; def m_60; p = get_proc{ puts 764; return; puts 765}; puts 766; method_use_lambda_and_yield(&p); puts 767; end; +begin; puts 768; m_60; puts 769; rescue; puts 770; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 771; return; puts 772}; puts 773; method_use_proc_and_yield(&p); puts 774; rescue; puts 775; puts $!.class; end +$g = 0; def m_61; p = get_proc{ puts 776; return; puts 777}; puts 778; method_use_proc_and_yield(&p); puts 779; end; +begin; puts 780; m_61; puts 781; rescue; puts 782; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 783; return; puts 784}; puts 785; method_use_lambda_and_call(&p); puts 786; rescue; puts 787; puts $!.class; end +$g = 0; def m_62; p = get_proc{ puts 788; return; puts 789}; puts 790; method_use_lambda_and_call(&p); puts 791; end; +begin; puts 792; m_62; puts 793; rescue; puts 794; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 795; return; puts 796}; puts 797; method_use_proc_and_call(&p); puts 798; rescue; puts 799; puts $!.class; end +$g = 0; def m_63; p = get_proc{ puts 800; return; puts 801}; puts 802; method_use_proc_and_call(&p); puts 803; end; +begin; puts 804; m_63; puts 805; rescue; puts 806; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 807; return; puts 808}; puts 809; method_use_lambda_and_yield_2(&p); puts 810; rescue; puts 811; puts $!.class; end +$g = 0; def m_64; p = get_proc{ puts 812; return; puts 813}; puts 814; method_use_lambda_and_yield_2(&p); puts 815; end; +begin; puts 816; m_64; puts 817; rescue; puts 818; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 819; return; puts 820}; puts 821; method_yield_in_loop(&p); puts 822; rescue; puts 823; puts $!.class; end +$g = 0; def m_65; p = get_proc{ puts 824; return; puts 825}; puts 826; method_yield_in_loop(&p); puts 827; end; +begin; puts 828; m_65; puts 829; rescue; puts 830; puts $!.class; end +$g = 0; begin; p = get_proc{ puts 831; return; puts 832}; puts 833; method_call_in_loop(&p); puts 834; rescue; puts 835; puts $!.class; end +$g = 0; def m_66; p = get_proc{ puts 836; return; puts 837}; puts 838; method_call_in_loop(&p); puts 839; end; +begin; puts 840; m_66; puts 841; rescue; puts 842; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_block; puts 843; iterator_via_yield(&p); puts 844; rescue; puts 845; puts $!.class; end +$g = 0; def m_67; p = get_local_block; puts 846; iterator_via_yield(&p); puts 847; end; +begin; puts 848; m_67; puts 849; rescue; puts 850; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 851; iterator_via_call(&p); puts 852; rescue; puts 853; puts $!.class; end +$g = 0; def m_68; p = get_local_block; puts 854; iterator_via_call(&p); puts 855; end; +begin; puts 856; m_68; puts 857; rescue; puts 858; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 859; method_call_iterator_via_yield(&p); puts 860; rescue; puts 861; puts $!.class; end +$g = 0; def m_69; p = get_local_block; puts 862; method_call_iterator_via_yield(&p); puts 863; end; +begin; puts 864; m_69; puts 865; rescue; puts 866; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 867; method_call_iterator_via_call(&p); puts 868; rescue; puts 869; puts $!.class; end +$g = 0; def m_70; p = get_local_block; puts 870; method_call_iterator_via_call(&p); puts 871; end; +begin; puts 872; m_70; puts 873; rescue; puts 874; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 875; method_use_lambda_and_yield(&p); puts 876; rescue; puts 877; puts $!.class; end +$g = 0; def m_71; p = get_local_block; puts 878; method_use_lambda_and_yield(&p); puts 879; end; +begin; puts 880; m_71; puts 881; rescue; puts 882; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 883; method_use_proc_and_yield(&p); puts 884; rescue; puts 885; puts $!.class; end +$g = 0; def m_72; p = get_local_block; puts 886; method_use_proc_and_yield(&p); puts 887; end; +begin; puts 888; m_72; puts 889; rescue; puts 890; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 891; method_use_lambda_and_call(&p); puts 892; rescue; puts 893; puts $!.class; end +$g = 0; def m_73; p = get_local_block; puts 894; method_use_lambda_and_call(&p); puts 895; end; +begin; puts 896; m_73; puts 897; rescue; puts 898; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 899; method_use_proc_and_call(&p); puts 900; rescue; puts 901; puts $!.class; end +$g = 0; def m_74; p = get_local_block; puts 902; method_use_proc_and_call(&p); puts 903; end; +begin; puts 904; m_74; puts 905; rescue; puts 906; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 907; method_use_lambda_and_yield_2(&p); puts 908; rescue; puts 909; puts $!.class; end +$g = 0; def m_75; p = get_local_block; puts 910; method_use_lambda_and_yield_2(&p); puts 911; end; +begin; puts 912; m_75; puts 913; rescue; puts 914; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 915; method_yield_in_loop(&p); puts 916; rescue; puts 917; puts $!.class; end +$g = 0; def m_76; p = get_local_block; puts 918; method_yield_in_loop(&p); puts 919; end; +begin; puts 920; m_76; puts 921; rescue; puts 922; puts $!.class; end +$g = 0; begin; p = get_local_block; puts 923; method_call_in_loop(&p); puts 924; rescue; puts 925; puts $!.class; end +$g = 0; def m_77; p = get_local_block; puts 926; method_call_in_loop(&p); puts 927; end; +begin; puts 928; m_77; puts 929; rescue; puts 930; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_lambda; puts 931; iterator_via_yield(&p); puts 932; rescue; puts 933; puts $!.class; end +$g = 0; def m_78; p = get_local_lambda; puts 934; iterator_via_yield(&p); puts 935; end; +begin; puts 936; m_78; puts 937; rescue; puts 938; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 939; iterator_via_call(&p); puts 940; rescue; puts 941; puts $!.class; end +$g = 0; def m_79; p = get_local_lambda; puts 942; iterator_via_call(&p); puts 943; end; +begin; puts 944; m_79; puts 945; rescue; puts 946; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 947; method_call_iterator_via_yield(&p); puts 948; rescue; puts 949; puts $!.class; end +$g = 0; def m_80; p = get_local_lambda; puts 950; method_call_iterator_via_yield(&p); puts 951; end; +begin; puts 952; m_80; puts 953; rescue; puts 954; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 955; method_call_iterator_via_call(&p); puts 956; rescue; puts 957; puts $!.class; end +$g = 0; def m_81; p = get_local_lambda; puts 958; method_call_iterator_via_call(&p); puts 959; end; +begin; puts 960; m_81; puts 961; rescue; puts 962; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 963; method_use_lambda_and_yield(&p); puts 964; rescue; puts 965; puts $!.class; end +$g = 0; def m_82; p = get_local_lambda; puts 966; method_use_lambda_and_yield(&p); puts 967; end; +begin; puts 968; m_82; puts 969; rescue; puts 970; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 971; method_use_proc_and_yield(&p); puts 972; rescue; puts 973; puts $!.class; end +$g = 0; def m_83; p = get_local_lambda; puts 974; method_use_proc_and_yield(&p); puts 975; end; +begin; puts 976; m_83; puts 977; rescue; puts 978; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 979; method_use_lambda_and_call(&p); puts 980; rescue; puts 981; puts $!.class; end +$g = 0; def m_84; p = get_local_lambda; puts 982; method_use_lambda_and_call(&p); puts 983; end; +begin; puts 984; m_84; puts 985; rescue; puts 986; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 987; method_use_proc_and_call(&p); puts 988; rescue; puts 989; puts $!.class; end +$g = 0; def m_85; p = get_local_lambda; puts 990; method_use_proc_and_call(&p); puts 991; end; +begin; puts 992; m_85; puts 993; rescue; puts 994; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 995; method_use_lambda_and_yield_2(&p); puts 996; rescue; puts 997; puts $!.class; end +$g = 0; def m_86; p = get_local_lambda; puts 998; method_use_lambda_and_yield_2(&p); puts 999; end; +begin; puts 1000; m_86; puts 1001; rescue; puts 1002; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1003; method_yield_in_loop(&p); puts 1004; rescue; puts 1005; puts $!.class; end +$g = 0; def m_87; p = get_local_lambda; puts 1006; method_yield_in_loop(&p); puts 1007; end; +begin; puts 1008; m_87; puts 1009; rescue; puts 1010; puts $!.class; end +$g = 0; begin; p = get_local_lambda; puts 1011; method_call_in_loop(&p); puts 1012; rescue; puts 1013; puts $!.class; end +$g = 0; def m_88; p = get_local_lambda; puts 1014; method_call_in_loop(&p); puts 1015; end; +begin; puts 1016; m_88; puts 1017; rescue; puts 1018; puts $!.class; end +end +test + +def test +$g = 0; begin; p = get_local_proc; puts 1019; iterator_via_yield(&p); puts 1020; rescue; puts 1021; puts $!.class; end +$g = 0; def m_89; p = get_local_proc; puts 1022; iterator_via_yield(&p); puts 1023; end; +begin; puts 1024; m_89; puts 1025; rescue; puts 1026; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1027; iterator_via_call(&p); puts 1028; rescue; puts 1029; puts $!.class; end +$g = 0; def m_90; p = get_local_proc; puts 1030; iterator_via_call(&p); puts 1031; end; +begin; puts 1032; m_90; puts 1033; rescue; puts 1034; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1035; method_call_iterator_via_yield(&p); puts 1036; rescue; puts 1037; puts $!.class; end +$g = 0; def m_91; p = get_local_proc; puts 1038; method_call_iterator_via_yield(&p); puts 1039; end; +begin; puts 1040; m_91; puts 1041; rescue; puts 1042; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1043; method_call_iterator_via_call(&p); puts 1044; rescue; puts 1045; puts $!.class; end +$g = 0; def m_92; p = get_local_proc; puts 1046; method_call_iterator_via_call(&p); puts 1047; end; +begin; puts 1048; m_92; puts 1049; rescue; puts 1050; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1051; method_use_lambda_and_yield(&p); puts 1052; rescue; puts 1053; puts $!.class; end +$g = 0; def m_93; p = get_local_proc; puts 1054; method_use_lambda_and_yield(&p); puts 1055; end; +begin; puts 1056; m_93; puts 1057; rescue; puts 1058; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1059; method_use_proc_and_yield(&p); puts 1060; rescue; puts 1061; puts $!.class; end +$g = 0; def m_94; p = get_local_proc; puts 1062; method_use_proc_and_yield(&p); puts 1063; end; +begin; puts 1064; m_94; puts 1065; rescue; puts 1066; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1067; method_use_lambda_and_call(&p); puts 1068; rescue; puts 1069; puts $!.class; end +$g = 0; def m_95; p = get_local_proc; puts 1070; method_use_lambda_and_call(&p); puts 1071; end; +begin; puts 1072; m_95; puts 1073; rescue; puts 1074; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1075; method_use_proc_and_call(&p); puts 1076; rescue; puts 1077; puts $!.class; end +$g = 0; def m_96; p = get_local_proc; puts 1078; method_use_proc_and_call(&p); puts 1079; end; +begin; puts 1080; m_96; puts 1081; rescue; puts 1082; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1083; method_use_lambda_and_yield_2(&p); puts 1084; rescue; puts 1085; puts $!.class; end +$g = 0; def m_97; p = get_local_proc; puts 1086; method_use_lambda_and_yield_2(&p); puts 1087; end; +begin; puts 1088; m_97; puts 1089; rescue; puts 1090; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1091; method_yield_in_loop(&p); puts 1092; rescue; puts 1093; puts $!.class; end +$g = 0; def m_98; p = get_local_proc; puts 1094; method_yield_in_loop(&p); puts 1095; end; +begin; puts 1096; m_98; puts 1097; rescue; puts 1098; puts $!.class; end +$g = 0; begin; p = get_local_proc; puts 1099; method_call_in_loop(&p); puts 1100; rescue; puts 1101; puts $!.class; end +$g = 0; def m_99; p = get_local_proc; puts 1102; method_call_in_loop(&p); puts 1103; end; +begin; puts 1104; m_99; puts 1105; rescue; puts 1106; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1107; p = lambda{ puts 1108; return; puts 1109}; puts(p.call); puts 1110; rescue; puts 1111; puts $!.class; end +$g = 0; def m_100; puts 1112; p = lambda{ puts 1113; return; puts 1114}; puts(p.call); puts 1115; end; +begin; puts 1116; m_100; puts 1117; rescue; puts 1118; puts $!.class; end +$g = 0; begin; puts 1119; puts m_100; puts 1120; rescue; puts 1121; puts $!.class; end +$g = 0; def m_101; puts 1122; puts m_100; puts 1123; end; +begin; puts 1124; m_101; puts 1125; rescue; puts 1126; puts $!.class; end +$g = 0; begin; puts 1127; p = Proc.new{ puts 1128; return; puts 1129}; puts(p.call); puts 1130; rescue; puts 1131; puts $!.class; end +$g = 0; def m_102; puts 1132; p = Proc.new{ puts 1133; return; puts 1134}; puts(p.call); puts 1135; end; +begin; puts 1136; m_102; puts 1137; rescue; puts 1138; puts $!.class; end +$g = 0; begin; puts 1139; puts m_102; puts 1140; rescue; puts 1141; puts $!.class; end +$g = 0; def m_103; puts 1142; puts m_102; puts 1143; end; +begin; puts 1144; m_103; puts 1145; rescue; puts 1146; puts $!.class; end +$g = 0; begin; puts 1147; p = get_block{ puts 1148; return; puts 1149}; puts(p.call); puts 1150; rescue; puts 1151; puts $!.class; end +$g = 0; def m_104; puts 1152; p = get_block{ puts 1153; return; puts 1154}; puts(p.call); puts 1155; end; +begin; puts 1156; m_104; puts 1157; rescue; puts 1158; puts $!.class; end +$g = 0; begin; puts 1159; puts m_104; puts 1160; rescue; puts 1161; puts $!.class; end +$g = 0; def m_105; puts 1162; puts m_104; puts 1163; end; +begin; puts 1164; m_105; puts 1165; rescue; puts 1166; puts $!.class; end +$g = 0; begin; puts 1167; p = get_lambda{ puts 1168; return; puts 1169}; puts(p.call); puts 1170; rescue; puts 1171; puts $!.class; end +$g = 0; def m_106; puts 1172; p = get_lambda{ puts 1173; return; puts 1174}; puts(p.call); puts 1175; end; +begin; puts 1176; m_106; puts 1177; rescue; puts 1178; puts $!.class; end +$g = 0; begin; puts 1179; puts m_106; puts 1180; rescue; puts 1181; puts $!.class; end +$g = 0; def m_107; puts 1182; puts m_106; puts 1183; end; +begin; puts 1184; m_107; puts 1185; rescue; puts 1186; puts $!.class; end +$g = 0; begin; puts 1187; p = get_proc{ puts 1188; return; puts 1189}; puts(p.call); puts 1190; rescue; puts 1191; puts $!.class; end +$g = 0; def m_108; puts 1192; p = get_proc{ puts 1193; return; puts 1194}; puts(p.call); puts 1195; end; +begin; puts 1196; m_108; puts 1197; rescue; puts 1198; puts $!.class; end +$g = 0; begin; puts 1199; puts m_108; puts 1200; rescue; puts 1201; puts $!.class; end +$g = 0; def m_109; puts 1202; puts m_108; puts 1203; end; +begin; puts 1204; m_109; puts 1205; rescue; puts 1206; puts $!.class; end +$g = 0; begin; puts 1207; p = get_local_block; puts(p.call); puts 1208; rescue; puts 1209; puts $!.class; end +$g = 0; def m_110; puts 1210; p = get_local_block; puts(p.call); puts 1211; end; +begin; puts 1212; m_110; puts 1213; rescue; puts 1214; puts $!.class; end +$g = 0; begin; puts 1215; puts m_110; puts 1216; rescue; puts 1217; puts $!.class; end +$g = 0; def m_111; puts 1218; puts m_110; puts 1219; end; +begin; puts 1220; m_111; puts 1221; rescue; puts 1222; puts $!.class; end +$g = 0; begin; puts 1223; p = get_local_lambda; puts(p.call); puts 1224; rescue; puts 1225; puts $!.class; end +$g = 0; def m_112; puts 1226; p = get_local_lambda; puts(p.call); puts 1227; end; +begin; puts 1228; m_112; puts 1229; rescue; puts 1230; puts $!.class; end +$g = 0; begin; puts 1231; puts m_112; puts 1232; rescue; puts 1233; puts $!.class; end +$g = 0; def m_113; puts 1234; puts m_112; puts 1235; end; +begin; puts 1236; m_113; puts 1237; rescue; puts 1238; puts $!.class; end +$g = 0; begin; puts 1239; p = get_local_proc; puts(p.call); puts 1240; rescue; puts 1241; puts $!.class; end +$g = 0; def m_114; puts 1242; p = get_local_proc; puts(p.call); puts 1243; end; +begin; puts 1244; m_114; puts 1245; rescue; puts 1246; puts $!.class; end +$g = 0; begin; puts 1247; puts m_114; puts 1248; rescue; puts 1249; puts $!.class; end +$g = 0; def m_115; puts 1250; puts m_114; puts 1251; end; +begin; puts 1252; m_115; puts 1253; rescue; puts 1254; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1255; x = lambda { puts 1256; p = lambda{ puts 1257; return; puts 1258}; puts p.call; puts 1259}; puts x.call; puts 1260; rescue; puts 1261; puts $!.class; end +$g = 0; def m_116; puts 1262; x = lambda { puts 1263; p = lambda{ puts 1264; return; puts 1265}; puts p.call; puts 1266}; puts x.call; puts 1267; end; +begin; puts 1268; m_116; puts 1269; rescue; puts 1270; puts $!.class; end +$g = 0; begin; puts 1271; x = lambda { puts 1272; p = Proc.new{ puts 1273; return; puts 1274}; puts p.call; puts 1275}; puts x.call; puts 1276; rescue; puts 1277; puts $!.class; end +$g = 0; def m_117; puts 1278; x = lambda { puts 1279; p = Proc.new{ puts 1280; return; puts 1281}; puts p.call; puts 1282}; puts x.call; puts 1283; end; +begin; puts 1284; m_117; puts 1285; rescue; puts 1286; puts $!.class; end +$g = 0; begin; puts 1287; x = lambda { puts 1288; p = get_block{ puts 1289; return; puts 1290}; puts p.call; puts 1291}; puts x.call; puts 1292; rescue; puts 1293; puts $!.class; end +$g = 0; def m_118; puts 1294; x = lambda { puts 1295; p = get_block{ puts 1296; return; puts 1297}; puts p.call; puts 1298}; puts x.call; puts 1299; end; +begin; puts 1300; m_118; puts 1301; rescue; puts 1302; puts $!.class; end +$g = 0; begin; puts 1303; x = lambda { puts 1304; p = get_lambda{ puts 1305; return; puts 1306}; puts p.call; puts 1307}; puts x.call; puts 1308; rescue; puts 1309; puts $!.class; end +$g = 0; def m_119; puts 1310; x = lambda { puts 1311; p = get_lambda{ puts 1312; return; puts 1313}; puts p.call; puts 1314}; puts x.call; puts 1315; end; +begin; puts 1316; m_119; puts 1317; rescue; puts 1318; puts $!.class; end +$g = 0; begin; puts 1319; x = lambda { puts 1320; p = get_proc{ puts 1321; return; puts 1322}; puts p.call; puts 1323}; puts x.call; puts 1324; rescue; puts 1325; puts $!.class; end +$g = 0; def m_120; puts 1326; x = lambda { puts 1327; p = get_proc{ puts 1328; return; puts 1329}; puts p.call; puts 1330}; puts x.call; puts 1331; end; +begin; puts 1332; m_120; puts 1333; rescue; puts 1334; puts $!.class; end +$g = 0; begin; puts 1335; x = lambda { puts 1336; p = get_local_block; puts p.call; puts 1337}; puts x.call; puts 1338; rescue; puts 1339; puts $!.class; end +$g = 0; def m_121; puts 1340; x = lambda { puts 1341; p = get_local_block; puts p.call; puts 1342}; puts x.call; puts 1343; end; +begin; puts 1344; m_121; puts 1345; rescue; puts 1346; puts $!.class; end +$g = 0; begin; puts 1347; x = lambda { puts 1348; p = get_local_lambda; puts p.call; puts 1349}; puts x.call; puts 1350; rescue; puts 1351; puts $!.class; end +$g = 0; def m_122; puts 1352; x = lambda { puts 1353; p = get_local_lambda; puts p.call; puts 1354}; puts x.call; puts 1355; end; +begin; puts 1356; m_122; puts 1357; rescue; puts 1358; puts $!.class; end +$g = 0; begin; puts 1359; x = lambda { puts 1360; p = get_local_proc; puts p.call; puts 1361}; puts x.call; puts 1362; rescue; puts 1363; puts $!.class; end +$g = 0; def m_123; puts 1364; x = lambda { puts 1365; p = get_local_proc; puts p.call; puts 1366}; puts x.call; puts 1367; end; +begin; puts 1368; m_123; puts 1369; rescue; puts 1370; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1371; x = Proc.new { puts 1372; p = lambda{ puts 1373; return; puts 1374}; puts p.call; puts 1375}; puts x.call; puts 1376; rescue; puts 1377; puts $!.class; end +$g = 0; def m_124; puts 1378; x = Proc.new { puts 1379; p = lambda{ puts 1380; return; puts 1381}; puts p.call; puts 1382}; puts x.call; puts 1383; end; +begin; puts 1384; m_124; puts 1385; rescue; puts 1386; puts $!.class; end +$g = 0; begin; puts 1387; x = Proc.new { puts 1388; p = Proc.new{ puts 1389; return; puts 1390}; puts p.call; puts 1391}; puts x.call; puts 1392; rescue; puts 1393; puts $!.class; end +$g = 0; def m_125; puts 1394; x = Proc.new { puts 1395; p = Proc.new{ puts 1396; return; puts 1397}; puts p.call; puts 1398}; puts x.call; puts 1399; end; +begin; puts 1400; m_125; puts 1401; rescue; puts 1402; puts $!.class; end +$g = 0; begin; puts 1403; x = Proc.new { puts 1404; p = get_block{ puts 1405; return; puts 1406}; puts p.call; puts 1407}; puts x.call; puts 1408; rescue; puts 1409; puts $!.class; end +$g = 0; def m_126; puts 1410; x = Proc.new { puts 1411; p = get_block{ puts 1412; return; puts 1413}; puts p.call; puts 1414}; puts x.call; puts 1415; end; +begin; puts 1416; m_126; puts 1417; rescue; puts 1418; puts $!.class; end +$g = 0; begin; puts 1419; x = Proc.new { puts 1420; p = get_lambda{ puts 1421; return; puts 1422}; puts p.call; puts 1423}; puts x.call; puts 1424; rescue; puts 1425; puts $!.class; end +$g = 0; def m_127; puts 1426; x = Proc.new { puts 1427; p = get_lambda{ puts 1428; return; puts 1429}; puts p.call; puts 1430}; puts x.call; puts 1431; end; +begin; puts 1432; m_127; puts 1433; rescue; puts 1434; puts $!.class; end +$g = 0; begin; puts 1435; x = Proc.new { puts 1436; p = get_proc{ puts 1437; return; puts 1438}; puts p.call; puts 1439}; puts x.call; puts 1440; rescue; puts 1441; puts $!.class; end +$g = 0; def m_128; puts 1442; x = Proc.new { puts 1443; p = get_proc{ puts 1444; return; puts 1445}; puts p.call; puts 1446}; puts x.call; puts 1447; end; +begin; puts 1448; m_128; puts 1449; rescue; puts 1450; puts $!.class; end +$g = 0; begin; puts 1451; x = Proc.new { puts 1452; p = get_local_block; puts p.call; puts 1453}; puts x.call; puts 1454; rescue; puts 1455; puts $!.class; end +$g = 0; def m_129; puts 1456; x = Proc.new { puts 1457; p = get_local_block; puts p.call; puts 1458}; puts x.call; puts 1459; end; +begin; puts 1460; m_129; puts 1461; rescue; puts 1462; puts $!.class; end +$g = 0; begin; puts 1463; x = Proc.new { puts 1464; p = get_local_lambda; puts p.call; puts 1465}; puts x.call; puts 1466; rescue; puts 1467; puts $!.class; end +$g = 0; def m_130; puts 1468; x = Proc.new { puts 1469; p = get_local_lambda; puts p.call; puts 1470}; puts x.call; puts 1471; end; +begin; puts 1472; m_130; puts 1473; rescue; puts 1474; puts $!.class; end +$g = 0; begin; puts 1475; x = Proc.new { puts 1476; p = get_local_proc; puts p.call; puts 1477}; puts x.call; puts 1478; rescue; puts 1479; puts $!.class; end +$g = 0; def m_131; puts 1480; x = Proc.new { puts 1481; p = get_local_proc; puts p.call; puts 1482}; puts x.call; puts 1483; end; +begin; puts 1484; m_131; puts 1485; rescue; puts 1486; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1487; x = get_block { puts 1488; p = lambda{ puts 1489; return; puts 1490}; puts p.call; puts 1491}; puts x.call; puts 1492; rescue; puts 1493; puts $!.class; end +$g = 0; def m_132; puts 1494; x = get_block { puts 1495; p = lambda{ puts 1496; return; puts 1497}; puts p.call; puts 1498}; puts x.call; puts 1499; end; +begin; puts 1500; m_132; puts 1501; rescue; puts 1502; puts $!.class; end +$g = 0; begin; puts 1503; x = get_block { puts 1504; p = Proc.new{ puts 1505; return; puts 1506}; puts p.call; puts 1507}; puts x.call; puts 1508; rescue; puts 1509; puts $!.class; end +$g = 0; def m_133; puts 1510; x = get_block { puts 1511; p = Proc.new{ puts 1512; return; puts 1513}; puts p.call; puts 1514}; puts x.call; puts 1515; end; +begin; puts 1516; m_133; puts 1517; rescue; puts 1518; puts $!.class; end +$g = 0; begin; puts 1519; x = get_block { puts 1520; p = get_block{ puts 1521; return; puts 1522}; puts p.call; puts 1523}; puts x.call; puts 1524; rescue; puts 1525; puts $!.class; end +$g = 0; def m_134; puts 1526; x = get_block { puts 1527; p = get_block{ puts 1528; return; puts 1529}; puts p.call; puts 1530}; puts x.call; puts 1531; end; +begin; puts 1532; m_134; puts 1533; rescue; puts 1534; puts $!.class; end +$g = 0; begin; puts 1535; x = get_block { puts 1536; p = get_lambda{ puts 1537; return; puts 1538}; puts p.call; puts 1539}; puts x.call; puts 1540; rescue; puts 1541; puts $!.class; end +$g = 0; def m_135; puts 1542; x = get_block { puts 1543; p = get_lambda{ puts 1544; return; puts 1545}; puts p.call; puts 1546}; puts x.call; puts 1547; end; +begin; puts 1548; m_135; puts 1549; rescue; puts 1550; puts $!.class; end +$g = 0; begin; puts 1551; x = get_block { puts 1552; p = get_proc{ puts 1553; return; puts 1554}; puts p.call; puts 1555}; puts x.call; puts 1556; rescue; puts 1557; puts $!.class; end +$g = 0; def m_136; puts 1558; x = get_block { puts 1559; p = get_proc{ puts 1560; return; puts 1561}; puts p.call; puts 1562}; puts x.call; puts 1563; end; +begin; puts 1564; m_136; puts 1565; rescue; puts 1566; puts $!.class; end +$g = 0; begin; puts 1567; x = get_block { puts 1568; p = get_local_block; puts p.call; puts 1569}; puts x.call; puts 1570; rescue; puts 1571; puts $!.class; end +$g = 0; def m_137; puts 1572; x = get_block { puts 1573; p = get_local_block; puts p.call; puts 1574}; puts x.call; puts 1575; end; +begin; puts 1576; m_137; puts 1577; rescue; puts 1578; puts $!.class; end +$g = 0; begin; puts 1579; x = get_block { puts 1580; p = get_local_lambda; puts p.call; puts 1581}; puts x.call; puts 1582; rescue; puts 1583; puts $!.class; end +$g = 0; def m_138; puts 1584; x = get_block { puts 1585; p = get_local_lambda; puts p.call; puts 1586}; puts x.call; puts 1587; end; +begin; puts 1588; m_138; puts 1589; rescue; puts 1590; puts $!.class; end +$g = 0; begin; puts 1591; x = get_block { puts 1592; p = get_local_proc; puts p.call; puts 1593}; puts x.call; puts 1594; rescue; puts 1595; puts $!.class; end +$g = 0; def m_139; puts 1596; x = get_block { puts 1597; p = get_local_proc; puts p.call; puts 1598}; puts x.call; puts 1599; end; +begin; puts 1600; m_139; puts 1601; rescue; puts 1602; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1603; x = get_lambda { puts 1604; p = lambda{ puts 1605; return; puts 1606}; puts p.call; puts 1607}; puts x.call; puts 1608; rescue; puts 1609; puts $!.class; end +$g = 0; def m_140; puts 1610; x = get_lambda { puts 1611; p = lambda{ puts 1612; return; puts 1613}; puts p.call; puts 1614}; puts x.call; puts 1615; end; +begin; puts 1616; m_140; puts 1617; rescue; puts 1618; puts $!.class; end +$g = 0; begin; puts 1619; x = get_lambda { puts 1620; p = Proc.new{ puts 1621; return; puts 1622}; puts p.call; puts 1623}; puts x.call; puts 1624; rescue; puts 1625; puts $!.class; end +$g = 0; def m_141; puts 1626; x = get_lambda { puts 1627; p = Proc.new{ puts 1628; return; puts 1629}; puts p.call; puts 1630}; puts x.call; puts 1631; end; +begin; puts 1632; m_141; puts 1633; rescue; puts 1634; puts $!.class; end +$g = 0; begin; puts 1635; x = get_lambda { puts 1636; p = get_block{ puts 1637; return; puts 1638}; puts p.call; puts 1639}; puts x.call; puts 1640; rescue; puts 1641; puts $!.class; end +$g = 0; def m_142; puts 1642; x = get_lambda { puts 1643; p = get_block{ puts 1644; return; puts 1645}; puts p.call; puts 1646}; puts x.call; puts 1647; end; +begin; puts 1648; m_142; puts 1649; rescue; puts 1650; puts $!.class; end +$g = 0; begin; puts 1651; x = get_lambda { puts 1652; p = get_lambda{ puts 1653; return; puts 1654}; puts p.call; puts 1655}; puts x.call; puts 1656; rescue; puts 1657; puts $!.class; end +$g = 0; def m_143; puts 1658; x = get_lambda { puts 1659; p = get_lambda{ puts 1660; return; puts 1661}; puts p.call; puts 1662}; puts x.call; puts 1663; end; +begin; puts 1664; m_143; puts 1665; rescue; puts 1666; puts $!.class; end +$g = 0; begin; puts 1667; x = get_lambda { puts 1668; p = get_proc{ puts 1669; return; puts 1670}; puts p.call; puts 1671}; puts x.call; puts 1672; rescue; puts 1673; puts $!.class; end +$g = 0; def m_144; puts 1674; x = get_lambda { puts 1675; p = get_proc{ puts 1676; return; puts 1677}; puts p.call; puts 1678}; puts x.call; puts 1679; end; +begin; puts 1680; m_144; puts 1681; rescue; puts 1682; puts $!.class; end +$g = 0; begin; puts 1683; x = get_lambda { puts 1684; p = get_local_block; puts p.call; puts 1685}; puts x.call; puts 1686; rescue; puts 1687; puts $!.class; end +$g = 0; def m_145; puts 1688; x = get_lambda { puts 1689; p = get_local_block; puts p.call; puts 1690}; puts x.call; puts 1691; end; +begin; puts 1692; m_145; puts 1693; rescue; puts 1694; puts $!.class; end +$g = 0; begin; puts 1695; x = get_lambda { puts 1696; p = get_local_lambda; puts p.call; puts 1697}; puts x.call; puts 1698; rescue; puts 1699; puts $!.class; end +$g = 0; def m_146; puts 1700; x = get_lambda { puts 1701; p = get_local_lambda; puts p.call; puts 1702}; puts x.call; puts 1703; end; +begin; puts 1704; m_146; puts 1705; rescue; puts 1706; puts $!.class; end +$g = 0; begin; puts 1707; x = get_lambda { puts 1708; p = get_local_proc; puts p.call; puts 1709}; puts x.call; puts 1710; rescue; puts 1711; puts $!.class; end +$g = 0; def m_147; puts 1712; x = get_lambda { puts 1713; p = get_local_proc; puts p.call; puts 1714}; puts x.call; puts 1715; end; +begin; puts 1716; m_147; puts 1717; rescue; puts 1718; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1719; x = get_proc { puts 1720; p = lambda{ puts 1721; return; puts 1722}; puts p.call; puts 1723}; puts x.call; puts 1724; rescue; puts 1725; puts $!.class; end +$g = 0; def m_148; puts 1726; x = get_proc { puts 1727; p = lambda{ puts 1728; return; puts 1729}; puts p.call; puts 1730}; puts x.call; puts 1731; end; +begin; puts 1732; m_148; puts 1733; rescue; puts 1734; puts $!.class; end +$g = 0; begin; puts 1735; x = get_proc { puts 1736; p = Proc.new{ puts 1737; return; puts 1738}; puts p.call; puts 1739}; puts x.call; puts 1740; rescue; puts 1741; puts $!.class; end +$g = 0; def m_149; puts 1742; x = get_proc { puts 1743; p = Proc.new{ puts 1744; return; puts 1745}; puts p.call; puts 1746}; puts x.call; puts 1747; end; +begin; puts 1748; m_149; puts 1749; rescue; puts 1750; puts $!.class; end +$g = 0; begin; puts 1751; x = get_proc { puts 1752; p = get_block{ puts 1753; return; puts 1754}; puts p.call; puts 1755}; puts x.call; puts 1756; rescue; puts 1757; puts $!.class; end +$g = 0; def m_150; puts 1758; x = get_proc { puts 1759; p = get_block{ puts 1760; return; puts 1761}; puts p.call; puts 1762}; puts x.call; puts 1763; end; +begin; puts 1764; m_150; puts 1765; rescue; puts 1766; puts $!.class; end +$g = 0; begin; puts 1767; x = get_proc { puts 1768; p = get_lambda{ puts 1769; return; puts 1770}; puts p.call; puts 1771}; puts x.call; puts 1772; rescue; puts 1773; puts $!.class; end +$g = 0; def m_151; puts 1774; x = get_proc { puts 1775; p = get_lambda{ puts 1776; return; puts 1777}; puts p.call; puts 1778}; puts x.call; puts 1779; end; +begin; puts 1780; m_151; puts 1781; rescue; puts 1782; puts $!.class; end +$g = 0; begin; puts 1783; x = get_proc { puts 1784; p = get_proc{ puts 1785; return; puts 1786}; puts p.call; puts 1787}; puts x.call; puts 1788; rescue; puts 1789; puts $!.class; end +$g = 0; def m_152; puts 1790; x = get_proc { puts 1791; p = get_proc{ puts 1792; return; puts 1793}; puts p.call; puts 1794}; puts x.call; puts 1795; end; +begin; puts 1796; m_152; puts 1797; rescue; puts 1798; puts $!.class; end +$g = 0; begin; puts 1799; x = get_proc { puts 1800; p = get_local_block; puts p.call; puts 1801}; puts x.call; puts 1802; rescue; puts 1803; puts $!.class; end +$g = 0; def m_153; puts 1804; x = get_proc { puts 1805; p = get_local_block; puts p.call; puts 1806}; puts x.call; puts 1807; end; +begin; puts 1808; m_153; puts 1809; rescue; puts 1810; puts $!.class; end +$g = 0; begin; puts 1811; x = get_proc { puts 1812; p = get_local_lambda; puts p.call; puts 1813}; puts x.call; puts 1814; rescue; puts 1815; puts $!.class; end +$g = 0; def m_154; puts 1816; x = get_proc { puts 1817; p = get_local_lambda; puts p.call; puts 1818}; puts x.call; puts 1819; end; +begin; puts 1820; m_154; puts 1821; rescue; puts 1822; puts $!.class; end +$g = 0; begin; puts 1823; x = get_proc { puts 1824; p = get_local_proc; puts p.call; puts 1825}; puts x.call; puts 1826; rescue; puts 1827; puts $!.class; end +$g = 0; def m_155; puts 1828; x = get_proc { puts 1829; p = get_local_proc; puts p.call; puts 1830}; puts x.call; puts 1831; end; +begin; puts 1832; m_155; puts 1833; rescue; puts 1834; puts $!.class; end +end +test + +def test +$g = 0; begin; puts 1835; for i in [1, 2]; puts 1836; p = lambda{ puts 1837; return; puts 1838}; puts p.call; puts 1839; end; puts 1840; rescue; puts 1841; puts $!.class; end +$g = 0; def m_156; puts 1842; for i in [1, 2]; puts 1843; p = lambda{ puts 1844; return; puts 1845}; puts p.call; puts 1846; end; puts 1847; end; +begin; puts 1848; m_156; puts 1849; rescue; puts 1850; puts $!.class; end +$g = 0; begin; puts 1851; for i in [1, 2]; puts 1852; p = Proc.new{ puts 1853; return; puts 1854}; puts p.call; puts 1855; end; puts 1856; rescue; puts 1857; puts $!.class; end +$g = 0; def m_157; puts 1858; for i in [1, 2]; puts 1859; p = Proc.new{ puts 1860; return; puts 1861}; puts p.call; puts 1862; end; puts 1863; end; +begin; puts 1864; m_157; puts 1865; rescue; puts 1866; puts $!.class; end +$g = 0; begin; puts 1867; for i in [1, 2]; puts 1868; p = get_block{ puts 1869; return; puts 1870}; puts p.call; puts 1871; end; puts 1872; rescue; puts 1873; puts $!.class; end +$g = 0; def m_158; puts 1874; for i in [1, 2]; puts 1875; p = get_block{ puts 1876; return; puts 1877}; puts p.call; puts 1878; end; puts 1879; end; +begin; puts 1880; m_158; puts 1881; rescue; puts 1882; puts $!.class; end +$g = 0; begin; puts 1883; for i in [1, 2]; puts 1884; p = get_lambda{ puts 1885; return; puts 1886}; puts p.call; puts 1887; end; puts 1888; rescue; puts 1889; puts $!.class; end +$g = 0; def m_159; puts 1890; for i in [1, 2]; puts 1891; p = get_lambda{ puts 1892; return; puts 1893}; puts p.call; puts 1894; end; puts 1895; end; +begin; puts 1896; m_159; puts 1897; rescue; puts 1898; puts $!.class; end +$g = 0; begin; puts 1899; for i in [1, 2]; puts 1900; p = get_proc{ puts 1901; return; puts 1902}; puts p.call; puts 1903; end; puts 1904; rescue; puts 1905; puts $!.class; end +$g = 0; def m_160; puts 1906; for i in [1, 2]; puts 1907; p = get_proc{ puts 1908; return; puts 1909}; puts p.call; puts 1910; end; puts 1911; end; +begin; puts 1912; m_160; puts 1913; rescue; puts 1914; puts $!.class; end +$g = 0; begin; puts 1915; for i in [1, 2]; puts 1916; p = get_local_block; puts p.call; puts 1917; end; puts 1918; rescue; puts 1919; puts $!.class; end +$g = 0; def m_161; puts 1920; for i in [1, 2]; puts 1921; p = get_local_block; puts p.call; puts 1922; end; puts 1923; end; +begin; puts 1924; m_161; puts 1925; rescue; puts 1926; puts $!.class; end +$g = 0; begin; puts 1927; for i in [1, 2]; puts 1928; p = get_local_lambda; puts p.call; puts 1929; end; puts 1930; rescue; puts 1931; puts $!.class; end +$g = 0; def m_162; puts 1932; for i in [1, 2]; puts 1933; p = get_local_lambda; puts p.call; puts 1934; end; puts 1935; end; +begin; puts 1936; m_162; puts 1937; rescue; puts 1938; puts $!.class; end +$g = 0; begin; puts 1939; for i in [1, 2]; puts 1940; p = get_local_proc; puts p.call; puts 1941; end; puts 1942; rescue; puts 1943; puts $!.class; end +$g = 0; def m_163; puts 1944; for i in [1, 2]; puts 1945; p = get_local_proc; puts p.call; puts 1946; end; puts 1947; end; +begin; puts 1948; m_163; puts 1949; rescue; puts 1950; puts $!.class; end +end +test diff --git a/merlin/main/languages/ruby/Tests/Compat/test_eh.rb b/merlin/main/languages/ruby/Tests/Compat/test_eh.rb new file mode 100644 index 0000000000..4ccd516fc1 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_eh.rb @@ -0,0 +1,183 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +def less_than times + $retry_count += 1 + $retry_count <= times +end + +class CodeHolder + attr_accessor :depth + def initialize + @text = "" + @depth = 0 + end + def add(text) + @text += text + end + def clear + @text = "" + end + def print + puts @text + end +end + +SPACE = " " * 4 + +class Begin_Ensure_generator + def initialize(ch, beginBody, ensureBody) + @code = ch + @beginBody = beginBody + @ensureBody = ensureBody + end + def generate(indent = 1) + @code.add(SPACE*indent); @code.add("begin\n") + @beginBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("ensure\n") + @ensureBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("end\n") + end +end + +class Begin_Rescue_generator + def initialize(ch, beginBody, rescueBody) + @code = ch + @beginBody = beginBody + @rescueBody = rescueBody + end + def generate(indent = 1) + @code.add(SPACE*indent); @code.add("begin\n") + @beginBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("rescue\n") + @rescueBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("end\n") + end +end + +class Begin_Rescue_Else_generator + def initialize(ch, beginBody, rescueBody, elseBody) + @code = ch + @beginBody = beginBody + @rescueBody = rescueBody + @elseBody = elseBody + end + def generate(indent = 1) + @code.add(SPACE*indent); @code.add("begin\n") + @beginBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("rescue\n") + @rescueBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("else\n") + @elseBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("end\n") + end +end + +class Begin_Rescue_Ensure_generator + def initialize(ch, beginBody, rescueBody, ensureBody) + @code = ch + @beginBody = beginBody + @rescueBody = rescueBody + @ensureBody = ensureBody + end + def generate(indent = 1) + @code.add(SPACE*indent); @code.add("begin\n") + @beginBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("rescue\n") + @rescueBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("ensure\n") + @ensureBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("end\n") + end +end + +class Begin_Rescue_Else_Ensure_generator + def initialize(ch, beginBody, rescueBody, elseBody, ensureBody) + @code = ch + @beginBody = beginBody + @rescueBody = rescueBody + @elseBody = elseBody + @ensureBody = ensureBody + end + def generate(indent = 1) + @code.add(SPACE*indent); @code.add("begin\n") + @beginBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("rescue\n") + @rescueBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("else\n") + @elseBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("ensure\n") + @ensureBody.generate(indent + 1) + @code.add(SPACE*indent); @code.add("end\n") + end +end + +class Pass_generator + def initialize ch + @code = ch + end + def generate(indent = 1) + @code.add(SPACE*indent); @code.add("puts 1\n") + end +end + +class While_Loop_generator + def initialize(ch, condition, body) + @code = ch + @condition = condition + @body = body + end + def generate(indent = 1) + @code.add(SPACE*indent); @code.add("while \n") + @code.add(condition) + @body.generate(indent + 1) + @code.add(SPACE*indent); @code.add("end\n") + end +end + +def begin_ensure_maker(ch, body) + Begin_Ensure_generator.new(ch, body, body) +end + +def begin_rescue_maker(ch, body) + Begin_Rescue_generator.new(ch, body, body) +end + +def pass_maker(ch, body) + Pass_generator.new(ch) +end + +methods = [:begin_ensure_maker, :begin_rescue_maker, :pass_maker].collect! { |x| self.method(x) } + + +ch = CodeHolder.new + +def do_generate tc + for tc2 in methods + if ch.depth > 3 + yield tc.call(pass_maker(nil)) + else + + end + end +end + +for tc in methods + for y in do_generate(tc) + $ch.clear + x.generate + $ch.print + end +end diff --git a/merlin/main/languages/ruby/Tests/Compat/test_exception_regression.bsl b/merlin/main/languages/ruby/Tests/Compat/test_exception_regression.bsl new file mode 100644 index 0000000000..3167612de3 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_exception_regression.bsl @@ -0,0 +1,16 @@ +1 +IOError +2 +SyntaxError : f_2 +IOError +IOError : f_15100 +3 +StandardError + : f_16 +1NilClass3NilClass6RuntimeError7RuntimeErrorRuntimeError : f_132 +3 +TypeError +5 +IOError +4 +TypeError diff --git a/merlin/main/languages/ruby/Tests/Compat/test_exception_regression.rb b/merlin/main/languages/ruby/Tests/Compat/test_exception_regression.rb new file mode 100644 index 0000000000..fef36feabb --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_exception_regression.rb @@ -0,0 +1,82 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# tests failed from the long run exception tests. + +def f_2 + begin + 1/0 + rescue (raise IOError); + else; + ensure; + puts 1; puts $!.class; raise SyntaxError.new + end + puts $!.class +end +$g = 0; begin; print(f_2); rescue Exception; puts 2; print $!.class; end; puts " : f_2" + +def f_15100 + begin + raise IOError + rescue TypeError; + ensure; + puts $!.class; + end +end +$g = 0; begin; print(f_15100); rescue Exception; print $!.class; end; puts " : f_15100" + +def f_16 + begin + raise Exception + rescue ($!=StandardError.new;IOError) + puts 1, $!.class + end + puts 2, $!.class +end +$g = 0; begin; print(f_16); rescue Exception; puts 3, $!.class; end; puts " : f_16" + +def f_132 + print 1, $!.class + begin + #empty_stmt + rescue (print 2, $!.class; raise TypeError; IOError) + puts $!.class + else + print 3, $!.class + raise "4" + print 5, $!.class + ensure + print 6, $!.class + #empty_stmt + print 7, $!.class + end + print 8, $!.class +end +$g = 0; begin; print(f_132); rescue Exception; print $!.class; end; puts " : f_132" + +def f1 + begin + else + $! = TypeError.new + ensure; + puts 3, $!.class + $! = IOError.new + puts 5, $!.class + end + puts 4, $!.class +end + +f1 + diff --git a/merlin/main/languages/ruby/Tests/Compat/test_func_splat.rb b/merlin/main/languages/ruby/Tests/Compat/test_func_splat.rb new file mode 100644 index 0000000000..e3a505d6a3 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_func_splat.rb @@ -0,0 +1,261 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +def p *a + a.each { |x| print x.inspect, '; ' } + puts +end + +def test_0 + def f(a) + return a + end + begin; puts "repro:f(nil)"; p f(nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*nil)"; p f(*nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[nil])"; p f(*[nil]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,nil)"; p f(nil,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,nil,nil)"; p f(nil,nil,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,*[nil])"; p f(nil,*[nil]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,nil)"; p f(1,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([])"; p f([]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1)"; p f(1); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2)"; p f(1,2); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2,3)"; p f(1,2,3); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[])"; p f(1,[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[2])"; p f(1,[2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[2,3])"; p f(1,[2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([[]])"; p f([[]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1])"; p f([1]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2])"; p f([1,2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2,3])"; p f([1,2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2,*[3]])"; p f([1,2,*[3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[])"; p f(*[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*1)"; p f(*1); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*2)"; p f(1,*2); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2,*3)"; p f(1,2,*3); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[])"; p f(1,*[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[2])"; p f(1,*[2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[2,3])"; p f(1,*[2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[[]])"; p f(*[[]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1])"; p f(*[1]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,2])"; p f(*[1,2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,2,3])"; p f(*[1,2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,[2,3]])"; p f(*[1,[2,3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,*[2,3]])"; p f(*[1,*[2,3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,*1)"; p f(nil,*1); rescue ArgumentError; puts('except'); end; +end + +def test_1 + def f(*a) + return a + end + begin; puts "repro:f(nil)"; p f(nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*nil)"; p f(*nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[nil])"; p f(*[nil]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,nil)"; p f(nil,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,nil,nil)"; p f(nil,nil,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,*[nil])"; p f(nil,*[nil]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,nil)"; p f(1,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([])"; p f([]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1)"; p f(1); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2)"; p f(1,2); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2,3)"; p f(1,2,3); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[])"; p f(1,[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[2])"; p f(1,[2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[2,3])"; p f(1,[2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([[]])"; p f([[]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1])"; p f([1]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2])"; p f([1,2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2,3])"; p f([1,2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2,*[3]])"; p f([1,2,*[3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[])"; p f(*[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*1)"; p f(*1); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*2)"; p f(1,*2); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2,*3)"; p f(1,2,*3); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[])"; p f(1,*[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[2])"; p f(1,*[2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[2,3])"; p f(1,*[2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[[]])"; p f(*[[]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1])"; p f(*[1]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,2])"; p f(*[1,2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,2,3])"; p f(*[1,2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,[2,3]])"; p f(*[1,[2,3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,*[2,3]])"; p f(*[1,*[2,3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,*1)"; p f(nil,*1); rescue ArgumentError; puts('except'); end; +end + +def test_2 + def f(a,b) + return a,b + end + begin; puts "repro:f(nil)"; p f(nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*nil)"; p f(*nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[nil])"; p f(*[nil]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,nil)"; p f(nil,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,nil,nil)"; p f(nil,nil,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,*[nil])"; p f(nil,*[nil]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,nil)"; p f(1,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([])"; p f([]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1)"; p f(1); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2)"; p f(1,2); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2,3)"; p f(1,2,3); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[])"; p f(1,[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[2])"; p f(1,[2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[2,3])"; p f(1,[2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([[]])"; p f([[]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1])"; p f([1]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2])"; p f([1,2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2,3])"; p f([1,2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2,*[3]])"; p f([1,2,*[3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[])"; p f(*[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*1)"; p f(*1); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*2)"; p f(1,*2); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2,*3)"; p f(1,2,*3); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[])"; p f(1,*[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[2])"; p f(1,*[2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[2,3])"; p f(1,*[2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[[]])"; p f(*[[]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1])"; p f(*[1]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,2])"; p f(*[1,2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,2,3])"; p f(*[1,2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,[2,3]])"; p f(*[1,[2,3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,*[2,3]])"; p f(*[1,*[2,3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,*1)"; p f(nil,*1); rescue ArgumentError; puts('except'); end; +end + +def test_3 + def f(a,*b) + return a,b + end + begin; puts "repro:f(nil)"; p f(nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*nil)"; p f(*nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[nil])"; p f(*[nil]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,nil)"; p f(nil,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,nil,nil)"; p f(nil,nil,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,*[nil])"; p f(nil,*[nil]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,nil)"; p f(1,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([])"; p f([]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1)"; p f(1); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2)"; p f(1,2); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2,3)"; p f(1,2,3); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[])"; p f(1,[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[2])"; p f(1,[2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[2,3])"; p f(1,[2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([[]])"; p f([[]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1])"; p f([1]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2])"; p f([1,2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2,3])"; p f([1,2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2,*[3]])"; p f([1,2,*[3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[])"; p f(*[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*1)"; p f(*1); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*2)"; p f(1,*2); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2,*3)"; p f(1,2,*3); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[])"; p f(1,*[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[2])"; p f(1,*[2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[2,3])"; p f(1,*[2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[[]])"; p f(*[[]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1])"; p f(*[1]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,2])"; p f(*[1,2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,2,3])"; p f(*[1,2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,[2,3]])"; p f(*[1,[2,3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,*[2,3]])"; p f(*[1,*[2,3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,*1)"; p f(nil,*1); rescue ArgumentError; puts('except'); end; +end + +def test_4 + def f(a,b,c) + return a,b,c + end + begin; puts "repro:f(nil)"; p f(nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*nil)"; p f(*nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[nil])"; p f(*[nil]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,nil)"; p f(nil,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,nil,nil)"; p f(nil,nil,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,*[nil])"; p f(nil,*[nil]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,nil)"; p f(1,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([])"; p f([]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1)"; p f(1); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2)"; p f(1,2); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2,3)"; p f(1,2,3); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[])"; p f(1,[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[2])"; p f(1,[2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[2,3])"; p f(1,[2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([[]])"; p f([[]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1])"; p f([1]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2])"; p f([1,2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2,3])"; p f([1,2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2,*[3]])"; p f([1,2,*[3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[])"; p f(*[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*1)"; p f(*1); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*2)"; p f(1,*2); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2,*3)"; p f(1,2,*3); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[])"; p f(1,*[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[2])"; p f(1,*[2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[2,3])"; p f(1,*[2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[[]])"; p f(*[[]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1])"; p f(*[1]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,2])"; p f(*[1,2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,2,3])"; p f(*[1,2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,[2,3]])"; p f(*[1,[2,3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,*[2,3]])"; p f(*[1,*[2,3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,*1)"; p f(nil,*1); rescue ArgumentError; puts('except'); end; +end + +def test_5 + def f(a,b,*c) + return a,b,c + end + begin; puts "repro:f(nil)"; p f(nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*nil)"; p f(*nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[nil])"; p f(*[nil]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,nil)"; p f(nil,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,nil,nil)"; p f(nil,nil,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,*[nil])"; p f(nil,*[nil]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,nil)"; p f(1,nil); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([])"; p f([]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1)"; p f(1); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2)"; p f(1,2); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2,3)"; p f(1,2,3); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[])"; p f(1,[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[2])"; p f(1,[2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,[2,3])"; p f(1,[2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([[]])"; p f([[]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1])"; p f([1]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2])"; p f([1,2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2,3])"; p f([1,2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f([1,2,*[3]])"; p f([1,2,*[3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[])"; p f(*[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*1)"; p f(*1); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*2)"; p f(1,*2); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,2,*3)"; p f(1,2,*3); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[])"; p f(1,*[]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[2])"; p f(1,*[2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(1,*[2,3])"; p f(1,*[2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[[]])"; p f(*[[]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1])"; p f(*[1]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,2])"; p f(*[1,2]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,2,3])"; p f(*[1,2,3]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,[2,3]])"; p f(*[1,[2,3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(*[1,*[2,3]])"; p f(*[1,*[2,3]]); rescue ArgumentError; puts('except'); end; + begin; puts "repro:f(nil,*1)"; p f(nil,*1); rescue ArgumentError; puts('except'); end; +end + +test_0 +test_1 +test_2 +test_3 +test_4 +test_5 diff --git a/merlin/main/languages/ruby/Tests/Compat/test_parallel_assign1.rb b/merlin/main/languages/ruby/Tests/Compat/test_parallel_assign1.rb new file mode 100644 index 0000000000..d39e6e8ffe --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_parallel_assign1.rb @@ -0,0 +1,235 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +def i *a + a.each { |x| print x.inspect, '; ' } + puts +end + +puts "", '#1' + +# L(1+,-) = R(1,-) +r = (a, = 1); i a, r +r = (a, = []); i a, r +r = (a, = [1]); i a, r +r = (a, = [1,2]); i a, r + +puts '===' + +r = (a,x = 1); i a, r +r = (a,x = []); i a, r +r = (a,x = [1]); i a, r +r = (a,x = [1,2]); i a, r + +puts "", '#2' + +# L(1+,-) = R(0,*) +r = (a, = *1); i a, r +r = (a, = *[]); i a, r +r = (a, = *[1]); i a, r +r = (a, = *[1,2]); i a, r + +puts '===' + +r = (a,x = *1); i a, r +r = (a,x = *[]); i a, r +r = (a,x = *[1]); i a, r +r = (a,x = *[1,2]); i a, r + +puts "", '#3' + +# L(0,*) = R(1,-) +r = (*a = 1); i a, r +r = (*a = []); i a, r +r = (*a = [1]); i a, r +r = (*a = [1,2]); i a, r + +puts "", '#4' + +# L(0,*) = R(0,*) +r = (*b = *1); i b, r +r = (*b = *[]); i b, r +r = (*b = *[1]); i b, r +r = (*b = *[1,2]); i b, r + +puts "", '#5' + +# L(0,*) = R(1,*) +r = (*a = 2,*1); i a, r +r = (*a = 2,*[]); i a, r +r = (*a = 2,*[1]); i a, r +r = (*a = 2,*[1,2]); i a, r + +puts "", '#6' + +# L(1,-) = R(0,*) +r = (a = *1); i a, r +r = (a = *[]); i a, r +r = (a = *[1]); i a, r +r = (a = *[1,2]); i a, r + +puts "", '#6.1' + +# L((),-) = R(0,*) +r = ((a,) = *1); i a, r +r = ((a,) = *[]); i a, r +r = ((a,) = *[1]); i a, r +r = ((a,) = *[1,2]); i a, r + +puts "", '#7' + +# L(1,-) = R(1,-) +r = (a = 1); i a, r +r = (a = []); i a, r +r = (a = [1]); i a, r +r = (a = [1,2]); i a, r + +puts "", '#7.1' + +# L(1,-) = R(1,*) +r = (a = 1,*2); i a, r +r = (a = 1,*[]); i a, r +r = (a = 1,*[2]); i a, r +r = (a = 1,*[2,3]); i a, r + +puts "", '#7.2' + +# L(1,-) = R(1,-) +r = ((a,) = 1); i a, r +r = ((a,) = []); i a, r +r = ((a,) = [1]); i a, r +r = ((a,) = [1,2]); i a, r + +puts "", '#7.3' + +# L((),-) = R(1,*) +r = ((a,) = 1,*2); i a, r +r = ((a,) = 1,*[]); i a, r +r = ((a,) = 1,*[2]); i a, r +r = ((a,) = 1,*[2,3]); i a, r + +puts "", '#8' + +# L(1,*) = R(1,*) +r = (b,*a = 2,*1); i b,a, r +r = (b,*a = 2,*[]); i b,a, r +r = (b,*a = 2,*[1]); i b,a, r +r = (b,*a = 2,*[1,2]); i b,a, r + +puts "", '#9' + +# L(1,*) = R(0,*) +r = (a,*b = *1); i a,b, r +r = (a,*b = *[]); i a,b, r +r = (a,*b = *[1]); i a,b, r +r = (a,*b = *[1,2]); i a,b, r + +puts "", '#10' + +# L(1,*) = R(1,-) +r = (a,*b = 1); i a,b, r +r = (a,*b = []); i a,b, r +r = (a,*b = [1]); i a,b, r +r = (a,*b = [1,2]); i a,b, r + +puts "", '#11' + +# L(L(1+,-),-) = R(2,-) +r = ((b,) = 1); i b, r +r = ((b,) = []); i b, r +r = ((b,) = [1]); i b, r +r = ((b,) = [1,2]); i b, r +r = ((b,) = [[]]); i b, r +r = ((b,) = [[1]]); i b, r +r = ((b,) = [[1,2]]); i b, r + +puts "===" + +# L(L(1,-),-) = R(2,-) +r = ((b,x) = 1); i b, r +r = ((b,x) = []); i b, r +r = ((b,x) = [1]); i b, r +r = ((b,x) = [1,2]); i b, r +r = ((b,x) = [[]]); i b, r +r = ((b,x) = [[1]]); i b, r +r = ((b,x) = [[1,2]]); i b, r + +puts "", '#12' + +# L(1+L(0,*),-) = R(2,-) +r = (b,(*a) = 1,[2,*1]); i b,a, r +r = (b,(*a) = 1,[2,*[]]); i b,a, r +r = (b,(*a) = 1,[2,*[1]]); i b,a, r +r = (b,(*a) = 1,[2,*[1,2]]); i b,a, r + +puts "", '#13' + +# L(n,-) = R(1,-) +r = (a,b = 1); i a,b, r +r = (a,b = []); i a,b, r +r = (a,b = [1]); i a,b, r +r = (a,b = [1,2]); i a,b, r + +puts "", '#14' + +# L(n,*) = R(1,-) +r = (a,b,*c = 1); i a,b,c, r +r = (a,b,*c = []); i a,b,c, r +r = (a,b,*c = [1]); i a,b,c, r +r = (a,b,*c = [1,2]); i a,b,c, r + +puts "", '#15' + +# L(n,-) = R(0,*) +r = (a,b = *1); i a,b, r +r = (a,b = *[]); i a,b, r +r = (a,b = *[1]); i a,b, r +r = (a,b = *[1,2]); i a,b, r + +puts "", '#16' + +# L(n,*) = R(0,*) +r = (a,b,*c = *1); i a,b,c, r +r = (a,b,*c = *[]); i a,b,c, r +r = (a,b,*c = *[1]); i a,b,c, r +r = (a,b,*c = *[1,2]); i a,b,c, r + +puts "", '#17' + +# L((), -) = R(0,*) +r = ((z,) = *[]); i z,r +r = ((z,) = *nil); i z,r +r = ((z,) = *1); i z,r +r = ((z,) = *[1]); i z,r +r = ((z,) = *[1,2]); i z,r +r = ((z,) = *nil); i z,r +r = ((z,) = 1,*[]); i z,r + +puts "", '#18' + +# L(1, -) = R(0,*) +r = (z = *[]); i z,r +r = (z = *nil); i z,r +r = (z = *1); i z,r +r = (z = *[1]); i z,r +r = (z = *[1,2]); i z,r + +puts "", '#19' + +# L(1,-) = R(2,-) +r = (a = 1,2); i a,r +r = ((a,) = 1,2); i a,r +r = (((a,)) = 1,2); i a,r +r = ((((a,))) = 1,2); i a,r diff --git a/merlin/main/languages/ruby/Tests/Compat/test_parallel_assign2.rb b/merlin/main/languages/ruby/Tests/Compat/test_parallel_assign2.rb new file mode 100644 index 0000000000..005d008a5a --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_parallel_assign2.rb @@ -0,0 +1,231 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +def i *a + a.each { |x| print x.inspect, '; ' } + puts +end + +puts "", '#1' + +# L(1+,-) = R(1,-) +def y; yield 1; end; y { |a,| i a } +def y; yield []; end; y { |a,| i a } +def y; yield [1]; end; y { |a,| i a } +def y; yield [1,2]; end; y { |a,| i a } + +puts "===" + +def y; yield 1; end; y { |a,x| i a } +def y; yield []; end; y { |a,x| i a } +def y; yield [1]; end; y { |a,x| i a } +def y; yield [1,2]; end; y { |a,x| i a } + +puts "", '#2' + +# L(0,*) = R(0,*) +def y; yield *1; end; y { |a,| i a } +def y; yield *[]; end; y { |a,| i a } +def y; yield *[1]; end; y { |a,| i a } +def y; yield *[1,2]; end; y { |a,| i a } + +puts '===' + +def y; yield *1; end; y { |a,x| i a } +def y; yield *[]; end; y { |a,x| i a } +def y; yield *[1]; end; y { |a,x| i a } +def y; yield *[1,2]; end; y { |a,x| i a } + +puts "", '#3' + +# L(0,*) = R(1,-) +def y; yield 1; end; y { |*a| i a } +def y; yield []; end; y { |*a| i a } +def y; yield [1]; end; y { |*a| i a } +def y; yield [1,2]; end; y { |*a| i a } + +puts "", '#4' + +# L(0,*) = R(0,*) +def y; yield *1; end; y { |*a| i a } +def y; yield *[]; end; y { |*a| i a } +def y; yield *[1]; end; y { |*a| i a } +def y; yield *[1,2]; end; y { |*a| i a } + +puts "", '#5' + +# L(0,*) = R(1,*) +def y; yield 2,*1; end; y { |*a| i a } +def y; yield 2,*[]; end; y { |*a| i a } +def y; yield 2,*[1]; end; y { |*a| i a } +def y; yield 2,*[1,2]; end; y { |*a| i a } + +puts "", '#6' + +# L(1,-) = R(0,*) +def y; yield *1; end; y { |a| i a } +def y; yield *[]; end; y { |a| i a } +def y; yield *[1]; end; y { |a| i a } +def y; yield *[1,2]; end; y { |a| i a } + +puts "", '#6.1' + +# L((),-) = R(0,*) +def y; yield *1; end; y { |(a,)| i a } +def y; yield *[]; end; y { |(a,)| i a } +def y; yield *[1]; end; y { |(a,)| i a } +def y; yield *[1,2]; end; y { |(a,)| i a } + +puts "", '#7' + +# L(1,-) = R(1,-) +def y; yield 1; end; y { |a| i a } +def y; yield []; end; y { |a| i a } +def y; yield [1]; end; y { |a| i a } +def y; yield [1,2]; end; y { |a| i a } + +puts "", '#7.1' + +# L(1,-) = R(1,*) +def y; yield 1,*2; end; y { |a| i a } +def y; yield 1,*[]; end; y { |a| i a } +def y; yield 1,*[2]; end; y { |a| i a } +def y; yield 1,*[2,3]; end; y { |a| i a } + +puts "", '#7.2' + +# L((),-) = R(1,-) +def y; yield 1; end; y { |(a,)| i a } +def y; yield []; end; y { |(a,)| i a } +def y; yield [1]; end; y { |(a,)| i a } +def y; yield [1,2]; end; y { |(a,)| i a } + +puts "", '#7.3' + +# L((),-) = R(1,*) +def y; yield 1,*2; end; y { |(a,)| i a } +def y; yield 1,*[]; end; y { |(a,)| i a } +def y; yield 1,*[2]; end; y { |(a,)| i a } +def y; yield 1,*[2,3]; end; y { |(a,)| i a } + +puts "", '#8' + +# L(1,*) = R(1,*) +def y; yield 2,*1; end; y { |b,*a| i b,a } +def y; yield 2,*[]; end; y { |b,*a| i b,a } +def y; yield 2,*[1]; end; y { |b,*a| i b,a } +def y; yield 2,*[1,2]; end; y { |b,*a| i b,a } + +puts "", '#9' + +# L(1,*) = R(0,*) +def y; yield *1; end; y { |a,*b| i a,b } +def y; yield *[]; end; y { |a,*b| i a,b } +def y; yield *[1]; end; y { |a,*b| i a,b } +def y; yield *[1,2]; end; y { |a,*b| i a,b } + +puts "", '#10' + +# L(1,*) = R(1,-) +def y; yield 1; end; y { |a,*b| i a,b } +def y; yield []; end; y { |a,*b| i a,b } +def y; yield [1]; end; y { |a,*b| i a,b } +def y; yield [1,2]; end; y { |a,*b| i a,b } + +puts "", '#11' + +def y; yield 1; end; y { |(a,)| i a } +def y; yield []; end; y { |(a,)| i a } +def y; yield [1]; end; y { |(a,)| i a } +def y; yield [1,2]; end; y { |(a,)| i a } +def y; yield [[]]; end; y { |(a,)| i a } +def y; yield [[1]]; end; y { |(a,)| i a } +def y; yield [[1,2]]; end; y { |(a,)| i a } + +puts '===' + +def y; yield 1; end; y { |(a,x)| i a } +def y; yield []; end; y { |(a,x)| i a } +def y; yield [1]; end; y { |(a,x)| i a } +def y; yield [1,2]; end; y { |(a,x)| i a } +def y; yield [[]]; end; y { |(a,x)| i a } +def y; yield [[1]]; end; y { |(a,x)| i a } +def y; yield [[1,2]]; end; y { |(a,x)| i a } + +puts "", '#12' + +# L(1+L(0,*),-) = R(2,-) +def y; yield 1,[2,*1]; end; y { |b,(*a)| i a,b } +def y; yield 1,[2,*[]]; end; y { |b,(*a)| i a,b } +def y; yield 1,[2,*[1]]; end; y { |b,(*a)| i a,b } +def y; yield 1,[2,*[1,2]]; end; y { |b,(*a)| i a,b } + +puts "", '#13' + +# L(n,-) = R(1,-) +def y; yield 1; end; y { |a,b| i a,b } +def y; yield []; end; y { |a,b| i a,b } +def y; yield [1]; end; y { |a,b| i a,b } +def y; yield [1,2]; end; y { |a,b| i a,b } + +puts "", '#14' + +# L(n,*) = R(1,-) +def y; yield 1; end; y { |a,b,*c| i a,b,c } +def y; yield []; end; y { |a,b,*c| i a,b,c } +def y; yield [1]; end; y { |a,b,*c| i a,b,c } +def y; yield [1,2]; end; y { |a,b,*c| i a,b,c } + +puts "", '#15' + +# L(n,-) = R(0,*) +def y; yield *1; end; y { |a,b| i a,b } +def y; yield *[]; end; y { |a,b| i a,b } +def y; yield *[1]; end; y { |a,b| i a,b } +def y; yield *[1,2]; end; y { |a,b| i a,b } + +puts "", '#16' + +# L(n,*) = R(0,*) +def y; yield *1; end; y { |a,b,*c| i a,b,c } +def y; yield *[]; end; y { |a,b,*c| i a,b,c } +def y; yield *[1]; end; y { |a,b,*c| i a,b,c } +def y; yield *[1,2]; end; y { |a,b,*c| i a,b,c } + +puts "", '#17' + +# L((), -) = R(0,*) +def y; yield *[]; end; y { |(z,)| i z } +def y; yield *nil; end; y { |(z,)| i z } +def y; yield *1; end; y { |(z,)| i z } +def y; yield *[1]; end; y { |(z,)| i z } +def y; yield *[1,2]; end; y { |(z,)| i z } + +puts "", '#18' + +# L(1, -) = R(0,*) +def y; yield *[]; end; y { |(z,)| i z } +def y; yield *nil; end; y { |(z,)| i z } +def y; yield *1; end; y { |(z,)| i z } +def y; yield *[1]; end; y { |(z,)| i z } +def y; yield *[1,2]; end; y { |(z,)| i z } + +puts "", '#19' + +# L(1,-) = R(2,-) +def y; yield 1,2; end; y { |z,| i z } +def y; yield 1,2; end; y { |(z,)| i z } +def y; yield 1,2; end; y { |((z,))| i z } +def y; yield 1,2; end; y { |(((z,)))| i z } diff --git a/merlin/main/languages/ruby/Tests/Compat/test_parallel_assign3.rb b/merlin/main/languages/ruby/Tests/Compat/test_parallel_assign3.rb new file mode 100644 index 0000000000..9369c4f310 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_parallel_assign3.rb @@ -0,0 +1,82 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# testing array constructor [] and return values +# LHS is always L(1,-) + +def i *a + a.each { |x| print x.inspect, '; ' } + puts +end + +puts "", '#A1' + +# L(1,-) = R(0,*) +a = [*1]; i a +a = [*[]]; i a +a = [*[1]]; i a +a = [*[1,2]]; i a + +puts "", '#A2' + +# L(1,-) = R(1,-) +a = [1]; i a +a = [[]]; i a +a = [[1]]; i a +a = [[1,2]]; i a + +puts "", '#A3' + +# L(1,-) = R(1,*) +a = [1,*1]; i a +a = [1,*[]]; i a +a = [1,*[1]]; i a +a = [1,*[1,2]]; i a +a = [[],*[]]; i a +a = [[1],*[1]]; i a +a = [[1,2],*[1,2]]; i a + +puts "", '#R0' + +#L(1,-) = R(0,-) +def f; return; end; i f + +puts "", '#R1' + +# L(1,-) = R(0,*) +def f; return *1; end; i f +def f; return *[]; end; i f +def f; return *[1]; end; i f +def f; return *[1,2]; end; i f + +puts "", '#R2' + +# L(1,-) = R(1,-) +def f; return 1; end; i f +def f; return []; end; i f +def f; return [1]; end; i f +def f; return [1,2]; end; i f + +puts "", '#R3' + +# L(1,-) = R(1,*) +def f; return 1,*1; end; i f +def f; return 1,*[]; end; i f +def f; return 1,*[1]; end; i f +def f; return 1,*[1,2]; end; i f +def f; return [],*[]; end; i f +def f; return [1],*[1]; end; i f +def f; return [1,2],*[1,2]; end; i f + diff --git a/merlin/main/languages/ruby/Tests/Compat/test_range_bool.rb b/merlin/main/languages/ruby/Tests/Compat/test_range_bool.rb new file mode 100644 index 0000000000..8b74f47d5c --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Compat/test_range_bool.rb @@ -0,0 +1,67 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# +# expr1..expr2 or expr1...expr2 +# +# different boolean sequences are applied to expr1 and expr2, to check the behavior +# + +def get_bool_repr(x) + l = [] + 0.upto(4) do |i| + l << (x % 2 == 1) ? true: false + x = x / 2 + end + l +end + +$bool_sequences = [] + +32.times do |x| + $bool_sequences << get_bool_repr(x) +end + +def get_bool(i, j) + $bool_sequences[i][j] +end + +puts "2-dot" +32.times do |i| + 32.times do |j| + l = [] + 5.times do |k| + if get_bool(i, k)..get_bool(j, k) + l << k + end + end + l.each {|x| printf("%d", x) } + puts + end +end + +puts "3-dot" +32.times do |i| + 32.times do |j| + l = [] + 5.times do |k| + if get_bool(i, k)...get_bool(j, k) + l << k + end + end + l.each {|x| printf("%d", x) } + puts + end +end diff --git a/merlin/main/languages/ruby/Tests/Interop/derivation/class_spec.rb b/merlin/main/languages/ruby/Tests/Interop/derivation/class_spec.rb new file mode 100644 index 0000000000..e850ca7da3 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Interop/derivation/class_spec.rb @@ -0,0 +1,9 @@ +require File.dirname(__FILE__) + '/../spec_helper' +require 'rowantest.baseclasscs' + +describe "Basic .NET classes" do + it "map to Ruby classes" do + Merlin::Testing::BaseClass::EmptyClass.class.should == Class + end +end + diff --git a/merlin/main/languages/ruby/Tests/Interop/spec_helper.rb b/merlin/main/languages/ruby/Tests/Interop/spec_helper.rb new file mode 100644 index 0000000000..60bcdcb800 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Interop/spec_helper.rb @@ -0,0 +1,37 @@ +unless ENV['MSPEC_RUNNER'] + begin + require "pp" + require 'mspec/version' + require 'mspec/helpers' + require 'mspec/guards' + require 'mspec/runner/shared' + require 'mspec/matchers/be_ancestor_of' + require 'mspec/matchers/output' + require 'mspec/matchers/output_to_fd' + require 'mspec/matchers/complain' + require 'mspec/matchers/equal_element' + require 'mspec/matchers/equal_utf16' + require 'mspec/matchers/match_yaml' + + TOLERANCE = 0.00003 unless Object.const_defined?(:TOLERANCE) + rescue LoadError + puts "Please install the MSpec gem to run the specs." + exit 1 + end +end + +v = MSpec::VERSION.split('.').collect { |d| "1%02d" % d.to_i }.join.to_i +unless v >= 101104100 + puts "Please install MSpec version >= 1.4.0 to run the specs" + exit 1 +end + +$VERBOSE = nil unless ENV['OUTPUT_WARNINGS'] + +def has_tty? + if STDOUT.tty? then + yield + end +end + +$: << (ENV["MERLIN_ROOT"] + "\\Bin\\Debug") diff --git a/merlin/main/languages/ruby/Tests/Interop/test_basic.rb b/merlin/main/languages/ruby/Tests/Interop/test_basic.rb new file mode 100644 index 0000000000..a1101499e1 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Interop/test_basic.rb @@ -0,0 +1,275 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "../util/assert.rb" + +require "mscorlib" + +def test_stringbuilder + x = System::Text::StringBuilder.new + x.Append("abc") + x.Append("def") + x.Append(100) + x.Insert(3, "012") + + assert_equal(x.ToString, System::String::Concat("abc012def100")) + + x.Capacity = 20 + assert_equal(x.Capacity, 20) + assert_equal(x.Length, 12) +end + +def test_string + a = System::Char::Parse("a") + b = System::Char::Parse("b") + x = System::String.new(a, 2) + y = System::String.new(b, 3) + + str = System::String + assert_equal(str.Concat(x, y), System::String::Concat("aabbb")) + + assert_equal(str.Compare(x, y), -1) + assert_equal(str.Compare(y, x), 1) + assert_equal(str.Compare(x, x), 0) +end + +def test_field + # const field + assert_equal(System::Int32.MaxValue, 2147483647) + # enum + assert_equal(System::DayOfWeek.Sunday.ToString().to_str, 'Sunday') + # readonly field + assert_equal(System::DateTime.MaxValue.ToString().to_str, '12/31/9999 11:59:59 PM') + + assert_raise(NoMethodError) { print System::Int32.MaxValue2 } + + # setting to a read only field raises NoMethodError + assert_raise(NoMethodError) { System::Int32.MaxValue = 5 } + assert_raise(NoMethodError) { System::DayOfWeek.Sunday = 5 } + assert_raise(NoMethodError) { System::DateTime.MaxValue = 5 } + + # TODO: set field test (need a type with a writable static field) +end + +def test_event + require "System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" + + t = System::Timers::Timer.new(1000) + $flag = 0 + t.Elapsed do |sender, e| + $flag += 1 + sender.Enabled = false + end + t.Enabled = true + t.Start() + System::Threading::Thread.Sleep(3000) + t.Stop() + assert_equal($flag, 1) +end + +def test_generictypes + # TODO: more interesting tests when more features of .NET interop are working + list = System::Collections::Generic::List + + IntList = list.of(System::Int32) + a = IntList.new + a.add 1 + a.add 2 + a.add 3 + assert_equal(a.count, 3) + + IntList2 = list[Fixnum] + assert_equal(IntList2.new.to_string, 'System.Collections.Generic.List`1[System.Int32]'.to_clr_string) + + Dict = System::Collections::Generic::Dictionary[ClrString, ClrString] + assert_equal(Dict.new.to_string, 'System.Collections.Generic.Dictionary`2[System.String,System.String]'.to_clr_string) + + assert_raise(ArgumentError) { System::Type.of(Fixnum) } + assert_raise(ArgumentError) { list[System] } + assert_raise(ArgumentError) { list[1] } + assert_raise(ArgumentError) { list[] } + assert_raise(ArgumentError) { list.of(Fixnum, Fixnum) } + + #System::Nullable.of(System::Int32) + #System::IComparable.of(Fixnum) + System::Predicate.of(System::Int32) +end + + +def test_ienumerable + # .NET types that implement IEnumerable should + # implement each & include Enumerable + a = System::Collections::ArrayList.new + a.add 1 + a.add 2 + a.add 3 + b = [] + a.each { |i| b << i } + assert_equal(b, [1, 2, 3]) + c = a.collect { |i| i + 3 } + assert_equal(c, [4, 5, 6]) +end + +def test_icomparable + a = System::DateTime.new 1900, 1, 1 + b = System::DateTime.new 1900, 1, 15 + c = System::DateTime.new 1900, 2, 1 + assert_true { a < b } + assert_true { b <= c } + assert_equal(a <=> c, -1) + assert_true { b.between?(a,c) } + assert_false { b.between?(c,a) } +end + +def test_idictionary + d = System::Collections::Generic::Dictionary[Object, Object].new + d[:abc] = 'def' + assert_equal(d[:abc], 'def') + assert_equal(d.inspect, '{:abc=>"def"}') +end + +def test_ilist + a = System::Collections::ArrayList.new + a.Add(1) + a.Add(3) + a.Add(2) + a.Add(3) + assert_equal(a, [1, 3, 2, 3]) + assert_equal(a[1], 3) + b = System::Collections::ArrayList.new + b.Add(5) + b.Add(4) + b.Add(3) + b.Add(4) + b.Add(6) + assert_equal(b, [5, 4, 3, 4, 6]) + c = a | b + assert_equal(c, [1, 3, 2, 5, 4, 6]) +end + + # TODO: more interesting tests when more features of .NET interop are working + class Bob_test_inherit < System::Collections::ArrayList + def foo + count + end + end + +def test_inherit + + a = Bob_test_inherit.new + a.add 1 + a.Add 2 + a.add 3 + assert_equal(a.foo, 3) + assert_equal(a.Count, 3) + +end + +class System::Collections::ArrayList + def total + sum = 0 + each { |i| sum += i } + sum + end +end + +def test_monkeypatch + a = System::Collections::ArrayList.new + + b = System::Collections::ArrayList.new + a.add 3 + a << 2 << 1 + assert_equal(a.total, 6) + b.replace [4,5,6] + assert_equal(b.total, 15) + end + +def test_unmangling + max = 2147483647 + assert_equal(System::Int32.MaxValue, max) + assert_equal(System::Int32.max_value, max) + + # Can't unmangle names with leading, trailing, or consecutive underscores + assert_raise(NoMethodError) { System::Int32.max_value_ } + assert_raise(NoMethodError) { System::Int32._max_value } + assert_raise(NoMethodError) { System::Int32.max__value } + assert_raise(NoMethodError) { System::Int32.MaxValue_ } + assert_raise(NoMethodError) { System::Int32._MaxValue } + + # Also can't unmangle names with uppercase letters + assert_raise(NoMethodError) { System::Int32.maxValue } + assert_raise(NoMethodError) { System::Int32.max_Value } + assert_raise(NoMethodError) { System::Int32.Maxvalue } + assert_raise(NoMethodError) { System::Int32.Max_value } +end + +def test_invisible_types + # we should be able to call methods on a type + # that's not visible--as long as the method itself is + # on a visible type somewhere in the heirarchy + + # Test returning a non-visible type (RuntimeType in this case) + type = System::Type.get_type('System.Int32'.to_clr_string) + + # Calling properties/methods on a non-visible type + assert_equal(type.full_name, 'System.Int32'.to_clr_string) + assert_equal(type.is_assignable_from(type), true) +end + +def test_generics_give_type_error + assert_raise(TypeError) do + Class.new(System::Collections::Generic::List) + end + assert_raise(TypeError) do + Class.new do + include System::Collections::Generic::List + end + end +end + +def test_include_interface_after_type_creation + c = Class.new do + include System::Collections::Generic::List[System::Int32] + end + + # No object has been instantiated so we should be able to reopen the class and add a new interface + c.class_eval do + include System::IDisposable + end + + # After instantiating the object, this will give us an error + tmp = c.new + assert_raise(TypeError) do + c.class_eval do + include System::IDisposable + end + end +end + +test_event +test_field +test_string +test_stringbuilder +test_generictypes +test_ienumerable +# TODO: disabling this test until we figure out how to enable creating DateTime +# objects using their ctors and not Time's RubyConstructors +#test_icomparable +test_idictionary +test_ilist +test_inherit +test_monkeypatch +test_unmangling +test_invisible_types diff --git a/merlin/main/languages/ruby/Tests/Libraries/socket/manual_socket.rb b/merlin/main/languages/ruby/Tests/Libraries/socket/manual_socket.rb new file mode 100644 index 0000000000..56cfe3378b --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Libraries/socket/manual_socket.rb @@ -0,0 +1,40 @@ +# Very simple test of TCPServer + +# Currently this is a manual test, until we have threading +# Run the script in MRI or IronRuby, then connect to +# http://127.0.0.1:1234/ + +# To kill, use Ctrl+C then make a request +# (or kill your command window) + +# BUG: currently socket is a builtin in IronRuby +# BUG: also, for some reason we cannot 'rescue' a failed require in IronRuby, +# so instead we try TCPSocket and if it fails require it + +begin + TCPSocket +rescue + require 'socket' +end + +TCPServer.open('127.0.0.1', 1234) do |server| + reqnum = 0 + while true + socket = server.accept + req = socket.recv(10000) + reqnum += 1 + content = "Hello from Ruby TCPServer! Request number #{reqnum}" + puts req + puts + socket.send("HTTP/1.1 200 OK\r +Content-Type: text/plain; charset=utf-8\r +Server: ruby-manual-socket-test/0.1\r +Cache-Control: no-cache\r +Pragma: no-cache\r +Expires: -1\r +Content-Length: #{content.length}\r +Connection: Close\r\n\r\n", 0) + socket.send(content, 0) + socket.shutdown + end +end diff --git a/merlin/main/languages/ruby/Tests/Runtime/Block/block_common.rb b/merlin/main/languages/ruby/Tests/Runtime/Block/block_common.rb new file mode 100644 index 0000000000..12233c1cf3 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Block/block_common.rb @@ -0,0 +1,50 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +def take_block + x = yield + $g += 1000 + x +end + +def take_arg_and_block(arg) + x = yield arg + $g += 1000 + x +end + +def call_method_which_take_block(&p) + x = take_block(&p) + $g += 10000 + x +end + +def call_method_which_take_arg_and_block(arg, &p) + x = take_arg_and_block(arg, &p) + $g += 10000 + x +end + +def take_block_in_loop + for i in [1, 2, 3] + x = yield + $g += 1000 + end + x +end + +def take_block_return_block &p + p +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Block/test_block_given.rb b/merlin/main/languages/ruby/Tests/Runtime/Block/test_block_given.rb new file mode 100644 index 0000000000..ca7094b9b5 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Block/test_block_given.rb @@ -0,0 +1,73 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# also what could we pass to the block argument? it is necessary +# the rule is: +# - &arg is optional, either specify it or skip it. +# - proc is not taken as block argument, &proc works + + +def method + block_given? +end + +empty = lambda {} + +assert_equal(method, false) +x = method {} +assert_equal(x, true) +x = method(&empty) +assert_equal(x, true) +assert_raise(ArgumentError) { method(empty) } + + +def method_with_1_arg arg + block_given? +end + +assert_equal(method_with_1_arg(1), false) +x = method_with_1_arg(1) {} +assert_equal(x, true) +x = method_with_1_arg(1, &empty) +assert_equal(x, true) + +assert_raise(ArgumentError) { method_with_1_arg(1, empty) } +assert_equal(method_with_1_arg(empty), false) + +def method_with_explict_block &p + l = 1 + if p == nil + l += 10 + else + l += 100 + end + if block_given? + l += 1000 + else + l += 10000 + end + l +end + +assert_equal(method_with_explict_block, 10011) +assert_raise(ArgumentError) { method_with_explict_block(1) } +x = method_with_explict_block {} +assert_equal(x, 1101) +x = method_with_explict_block(&empty) +assert_equal(x, 1101) +assert_raise(ArgumentError) { method_with_explict_block(empty) } + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Block/test_break.rb b/merlin/main/languages/ruby/Tests/Runtime/Block/test_break.rb new file mode 100644 index 0000000000..d4af61c980 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Block/test_break.rb @@ -0,0 +1,103 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' +require 'block_common.rb' + +def test_flow_with_no_arg + $g = 0 + $r = take_block do + $g += 1 + break + $g += 10 + end + + assert_equal($r, nil) + assert_equal($g, 1) +end + +def test_flow_with_arg + $g = 0 + $r = take_block do + $g += 1 + break 1 + $g += 10 + end + + assert_equal($r, 1) + assert_equal($g, 1) +end + +def test_nested_call_with_arg + $g = 0 + $r = call_method_which_take_block do + $g += 1 + break 2 + $g += 10 + end + assert_equal($r, 2) + assert_equal($g, 1) +end + +def test_flow_with_arg_in_loop + $g = 0 + $r = take_block_in_loop do + $g += 1 + break 1 + $g += 10 + end + + assert_equal($r, 1) + assert_equal($g, 1) +end + +def test_proc_lambda + $g = 0 + p = lambda do + $g += 1 + break 8 + $g += 10 + end + + assert_raise(LocalJumpError) { take_block &p } + assert_equal($g, 1) + + $g = 0 + assert_equal(p.call, 8) # ?? + assert_equal($g, 1) +end + +def test_proc_new + $g = 0 + p = Proc.new do + $g += 1 + break 8 + $g += 10 + end + + assert_raise(LocalJumpError) { take_block &p } + assert_equal($g, 1) + + $g = 0 + assert_raise(LocalJumpError) { p.call } + assert_equal($g, 1) +end + +test_flow_with_no_arg +test_flow_with_arg +test_nested_call_with_arg +test_flow_with_arg_in_loop +test_proc_lambda +test_proc_new \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Block/test_closure.rb b/merlin/main/languages/ruby/Tests/Runtime/Block/test_closure.rb new file mode 100644 index 0000000000..697b5a31b8 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Block/test_closure.rb @@ -0,0 +1,100 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# TODO: +# assert_equal(foo(&p)) were replaced by x = foo(&p); assert_equal(x) +# known DLR bug (entering try-catch with non-empty stack) + + +require '../../util/assert.rb' + +def take_block + yield +end + +def test_use_local + local_var = 10 + p = lambda { 1 + local_var } + x = take_block(&p) + assert_equal(x, 11) + local_var = 100 + x = take_block(&p) + assert_equal(x, 101) +end + +def test_use_global + $g = 10 + p = lambda { 1 + $g } + x = take_block(&p) + assert_equal(x, 11) + $g = 100 + x = take_block(&p) + assert_equal(x, 101) +end + +def test_use_arg + def _take_arg(arg) + lambda { 1 + arg } + end + p = _take_arg(10) + x = take_block(&p) + assert_equal(x, 11) + p = _take_arg(100) + x = take_block(&p) + assert_equal(x, 101) +end + +def test_use_misc + $g = 1 + def _take_arg(arg1, arg2) + l1 = arg1 + l2 = 7 + lambda { $g + l1 + l2 + arg2 } + end + p = _take_arg(10, 100) + assert_equal(p.call, 118) + + $g = 2 + p = _take_arg(20, 200) + x = take_block(&p) + assert_equal(x, 229) +end + +def test_write_to_local + local_var = 1 + p = lambda { |x| local_var += x } + + p.call(10) + assert_equal(local_var, 11) + p.call(100) + assert_equal(local_var, 111) +end + +def test_write_to_global + $g = 1 + p = lambda { |x| $g += x } + + p.call(10) + assert_equal($g, 11) + p.call(100) + assert_equal($g, 111) +end + +test_use_local +test_use_global +test_use_arg +test_use_misc +test_write_to_local +test_write_to_global \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Block/test_create_invoke.rb b/merlin/main/languages/ruby/Tests/Runtime/Block/test_create_invoke.rb new file mode 100644 index 0000000000..1b023e6f94 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Block/test_create_invoke.rb @@ -0,0 +1,36 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' +require 'block_common.rb' + +def test_0_arg + b = lambda { 1 } + assert_equal(b.call, 1) + assert_equal(b.call(1, 2, 4), 1) +# assert_raise(ArgumentError) { b.call(1) } + + b = lambda { divide_by_zero } + assert_raise(ZeroDivisionError) { b.call } +end + +def test_1_arg + b = lambda { |x| x } + assert_equal(b.call(9), 9) + b.call(3, 4) # warning +end + +test_0_arg +test_1_arg \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Block/test_next.rb b/merlin/main/languages/ruby/Tests/Runtime/Block/test_next.rb new file mode 100644 index 0000000000..e1a57813d9 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Block/test_next.rb @@ -0,0 +1,69 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' +require 'block_common.rb' + +def test_flow_with_no_arg + $g = 0 + $r = take_block do + $g += 1 + next + $g += 10 + end + + assert_equal($r, nil) + assert_equal($g, 1001) +end + +def test_flow_with_arg + $g = 0 + $r = take_block do + $g += 1 + next 1 + $g += 10 + end + + assert_equal($r, 1) + assert_equal($g, 1001) +end + +def test_nested_call_with_arg + $g = 0 + $r = call_method_which_take_block do + $g += 1 + next 2 + $g += 10 + end + assert_equal($r, 2) + assert_equal($g, 11001) +end + +def test_flow_with_arg_in_loop + $g = 0 + $r = take_block_in_loop do + $g += 1 + next 1 + $g += 10 + end + + assert_equal($r, 1) + assert_equal($g, 3003) +end + +test_flow_with_no_arg +test_flow_with_arg +test_nested_call_with_arg +test_flow_with_arg_in_loop \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Block/test_redo.rb b/merlin/main/languages/ruby/Tests/Runtime/Block/test_redo.rb new file mode 100644 index 0000000000..9d794000bf --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Block/test_redo.rb @@ -0,0 +1,87 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' +require 'block_common.rb' + +def test_simple_call + $g = $c = 0 + $r = take_block do + $g += 1 + $c += 1 + redo if $c < 5 + $g += 10 + end + + assert_equal($r, 15) + assert_equal($g, 1015) +end + +def test_simple_call_in_loop + $g = $c = 0 + $r = take_block_in_loop do + $g += 1 + $c += 1 + redo if $c < 5 + $g += 10 + end + + assert_equal($r, 2037) + assert_equal($g, 3037) +end + +def test_nested_call + $g = $c = 0 + $r = call_method_which_take_block do + $g += 1 + $c += 1 + redo if $c < 5 + $g += 10 + end + assert_equal($r, 15) + assert_equal($g, 11015) +end + +def test_evaluate_args + $g = $c = 0 + $r = take_arg_and_block( ($g+=10; 1) ) do # not evaluated again + $g += 1 + $c += 1 + redo if $c < 4 + $g += 100 + end + + assert_equal($r, 114) + assert_equal($g, 1114) +end + +def test_evaluate_args_in_upper_level + $g = $c = 0 + $r = call_method_which_take_arg_and_block( ($g+=10; 1) ) do + $g += 1 + $c += 1 + redo if $c < 4 + $g += 100 + end + + assert_equal($r, 114) + assert_equal($g, 11114) +end + +test_simple_call +test_simple_call_in_loop +test_nested_call +test_evaluate_args +test_evaluate_args_in_upper_level \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Block/test_retry.rb b/merlin/main/languages/ruby/Tests/Runtime/Block/test_retry.rb new file mode 100644 index 0000000000..7deed2e471 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Block/test_retry.rb @@ -0,0 +1,87 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' +require 'block_common.rb' + +def test_simple_call + $g = $c = 0 + $r = take_block do + $g += 1 + $c += 1 + retry if $c < 5 + $g += 10 + end + + assert_equal($r, 15) + assert_equal($g, 1015) +end + +def test_simple_call_in_loop + $g = $c = 0 + $r = take_block_in_loop do + $g += 1 + $c += 1 + retry if $c < 5 + $g += 10 + end + + assert_equal($r, 2037) + assert_equal($g, 3037) +end + +def test_nested_call + $g = $c = 0 + $r = call_method_which_take_block do + $g += 1 + $c += 1 + retry if $c < 5 + $g += 10 + end + assert_equal($r, 15) + assert_equal($g, 11015) +end + +def test_evaluate_args + $g = $c = 0 + $r = take_arg_and_block( ($g+=10; 1) ) do # arguments be evaluated when retry + $g += 1 + $c += 1 + retry if $c < 4 + $g += 100 + end + + assert_equal($r, 144) + assert_equal($g, 1144) +end + +def test_evaluate_args_in_upper_level + $g = $c = 0 + $r = call_method_which_take_arg_and_block( ($g+=10; 1) ) do + $g += 1 + $c += 1 + retry if $c < 4 + $g += 100 + end + + assert_equal($r, 144) + assert_equal($g, 11144) +end + +test_simple_call +test_simple_call_in_loop +test_nested_call +test_evaluate_args +test_evaluate_args_in_upper_level \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Block/test_return.rb b/merlin/main/languages/ruby/Tests/Runtime/Block/test_return.rb new file mode 100644 index 0000000000..cc5082fe94 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Block/test_return.rb @@ -0,0 +1,85 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' +require 'block_common.rb' + +def test_flow_with_no_arg + $g = 0 + $r = take_block do + $g += 1 + return + $g += 10 + end + + assert_equal($r, nil) + assert_equal($g, 1001) +end + +def test_flow_with_arg + $g = 0 + $r = take_block do + $g += 1 + return 1 + $g += 10 + end + + assert_equal($r, 1) + assert_equal($g, 1001) +end + +def test_nested_call_with_arg + $g = 0 + $r = call_method_which_take_block do + $g += 1 + return 2 + $g += 10 + end + assert_equal($r, 2) + assert_equal($g, 11001) +end + +def test_flow_with_arg_in_loop + $g = 0 + $r = take_block_in_loop do + $g += 1 + return 1 + $g += 10 + end + + assert_equal($r, 1) + assert_equal($g, 1001) +end + +test_flow_with_no_arg +test_flow_with_arg +test_nested_call_with_arg +test_flow_with_arg_in_loop + +# outside any method +begin + $g = 0 + $r = 7 + $r = take_block do + $g += 1 + return + $g += 10 + end +rescue LocalJumpError + $g += 100 +end + +assert_equal($g, 101) +assert_equal($r, 7) \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Block/test_yield.rb b/merlin/main/languages/ruby/Tests/Runtime/Block/test_yield.rb new file mode 100644 index 0000000000..91d478c6f7 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Block/test_yield.rb @@ -0,0 +1,92 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +def method_1_yield + yield + $g += 100 +end + +proc_1_yield = lambda do + yield + $g += 100 +end + +def test_yield_with_yield + $g = 0 + method_1_yield { method_1_yield { "a" }} + assert_equal($g, 200) +end + +test_yield_with_yield + +def test_raise_exception + $g = 0 + x = nil + begin + x = method_1_yield { divide_by_zero } + rescue ZeroDivisionError + $g += 1 + end + assert_equal($g, 1) + assert_nil(x) +end + +def test_raise_exception_from_inner_yield + $g = 0 + x = nil + begin + x = method_1_yield { method_1_yield { divide_by_zero } } + rescue ZeroDivisionError + $g += 1 + end + assert_equal($g, 1) + assert_nil(x) +end + +def method_2_yields + $g += 10 + yield 1 + $g += 100 + yield 2 + $g += 1000 +end + +def test_raise_exception_from_sequence_yields + $g = 0 + begin + method_2_yields do |x| + divide_by_zero if x == 1 + end + rescue ZeroDivisionError + $g += 10000 + end + assert_equal($g, 10010) + + $g = 0 + begin + method_2_yields do |x| + divide_by_zero if x == 2 + end + rescue ZeroDivisionError + $g += 10000 + end + assert_equal($g, 10110) +end + +test_raise_exception +test_raise_exception_from_inner_yield +test_raise_exception_from_sequence_yields \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_access_ctrl.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_access_ctrl.rb new file mode 100644 index 0000000000..7f801ff2aa --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_access_ctrl.rb @@ -0,0 +1,149 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# status: complete + +require '../../util/assert.rb' + +# to check whether the caller can access public/protected/private methods. +# - class, instance methods? // class methods always public? +# - access the defined methods inside the class, derived class, and outside the class + +class My_base + public + def My_base.pub_sm; -100; end + def pub_im; 100; end + + protected + def My_base.prot_sm; -200; end + def prot_im; 200; end + + private + def My_base.priv_sm; -300; end + def priv_im; 300; end + + public + def instance_check_from_base + assert_equal(pub_im, 100) + assert_equal(prot_im, 200) + assert_equal(priv_im, 300) + + # accessing via self + assert_equal(self.pub_im, 100) + assert_equal(self.prot_im, 200) + assert_raise(NoMethodError) { self.priv_im } + + assert_equal(My_base.pub_sm, -100) + assert_equal(My_base.prot_sm, -200) + assert_equal(My_base.priv_sm, -300) + end + + def My_base.static_check_from_base + assert_equal(pub_sm, -100) + assert_equal(prot_sm, -200) + assert_equal(priv_sm, -300) + + assert_equal(self.pub_sm, -100) + assert_equal(self.prot_sm, -200) + assert_equal(self.priv_sm, -300) + + assert_equal(My_base.pub_sm, -100) + assert_equal(My_base.prot_sm, -200) + assert_equal(My_base.priv_sm, -300) + end + + def instance_check_as_arg_from_base arg + assert_equal(arg.pub_im, 100) + assert_equal(arg.prot_im, 200) + assert_raise(NoMethodError) { arg.priv_im } + end + + def My_base.static_check_as_arg_from_base arg + assert_equal(arg.pub_im, 100) + assert_raise(NoMethodError) { arg.prot_im } + assert_raise(NoMethodError) { arg.priv_im } + end +end + +x = My_base.new + +# calling outside +assert_equal(My_base.pub_sm, -100) +assert_equal(My_base.prot_sm, -200) +assert_equal(My_base.priv_sm, -300) + +assert_equal(x.pub_im, 100) +assert_raise(NoMethodError) { x.prot_im } # protected method `prot_im' called for # (NoMethodError) +assert_raise(NoMethodError) { x.priv_im } # private method `priv_im' called for # (NoMethodError) + +My_base.static_check_from_base +x.instance_check_from_base + +My_base.static_check_as_arg_from_base x +x.instance_check_as_arg_from_base x + +class My_derived < My_base + public + def instance_check_from_derived + assert_equal(pub_im, 100) + assert_equal(prot_im, 200) + assert_equal(priv_im, 300) + + assert_equal(self.pub_im, 100) + assert_equal(self.prot_im, 200) + assert_raise(NoMethodError) { self.priv_im } + + assert_equal(My_derived.pub_sm, -100) + assert_equal(My_derived.prot_sm, -200) + assert_equal(My_derived.priv_sm, -300) + end + + def My_base.static_check_from_derived + assert_equal(pub_sm, -100) + assert_equal(prot_sm, -200) + assert_equal(priv_sm, -300) + + assert_equal(self.pub_sm, -100) + assert_equal(self.prot_sm, -200) + assert_equal(self.priv_sm, -300) + + assert_equal(My_derived.pub_sm, -100) + assert_equal(My_derived.prot_sm, -200) + assert_equal(My_derived.priv_sm, -300) + end + + def instance_check_as_arg_from_derived arg + assert_equal(arg.pub_im, 100) + assert_equal(arg.prot_im, 200) + assert_raise(NoMethodError) { arg.priv_im } + end + + def My_derived.static_check_as_arg_from_derived arg + assert_equal(arg.pub_im, 100) + assert_raise(NoMethodError) { arg.prot_im } + assert_raise(NoMethodError) { arg.priv_im } + end +end + +y = My_derived.new + +y.instance_check_from_derived +My_derived.static_check_from_derived +y.instance_check_as_arg_from_derived y +My_derived.static_check_as_arg_from_derived y + +assert_equal(y.pub_im, 100) +assert_raise(NoMethodError) { y.prot_im } +assert_raise(NoMethodError) { y.priv_im } diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_change_self.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_change_self.rb new file mode 100644 index 0000000000..311bdd3abf --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_change_self.rb @@ -0,0 +1,176 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# status: almost complete, few nice-to-have improvements: +# - try other members types in the "CHANGE BASE TYPE" scenario + +require '../../util/assert.rb' + +# CHANGE THE MEMBERS +# - redefining the class with members of the new or same names + +class My + CONST1 = 10 + CONST2 = 20 + @@sv1 = 30 + @@sv2 = 40 + + def initialize + @iv1 = 50 + @iv2 = 60 + end + + def My.sm1; 70; end + def My.sm2; 80; end + + def im1; 90; end + def im2; 100; end + + def check + return @iv1, @iv2, @iv3, @@sv1, @@sv2, @@sv3 + end + + class Nested + def m1; 110; end + def m2; 120; end + end +end + +x_before = My.new +y_before = My::Nested.new + +# try those to-be-added/updated members +assert_equal(My::CONST2, 20) +assert_raise(NameError) { My::CONST3 } + +assert_equal(My::sm2, 80) +assert_raise(NoMethodError) { My::sm3 } + +assert_equal(x_before.im2, 100) +assert_raise(NoMethodError) { x_before.im3 } + +assert_equal(y_before.m2, 120) +assert_raise(NoMethodError) { y_before.m3 } + +# let us create something else in the middle +class Other; end + +class My + CONST2 = -10 # warning issued + CONST3 = -20 + @@sv2 = -30 + @@sv3 = -40 + + def initialize + @iv2 = -50 + @iv3 = -60 + end + + def My.sm2; -70; end + def My.sm3; -80; end + def im2; -90; end + def im3; -100; end + + class Nested + def m2; -110; end + def m3; -120; end + end +end + +x_after = My.new +y_after = My::Nested.new + +assert_equal(My::CONST1, 10) +assert_equal(My::CONST2, -10) +assert_equal(My::CONST3, -20) + +assert_equal(My.sm1, 70) +assert_equal(My.sm2, -70) +assert_equal(My.sm3, -80) + +assert_equal(x_after.im1, 90) +assert_equal(x_after.im2, -90) +assert_equal(x_after.im3, -100) + +assert_equal(x_after.check, [nil, -50, -60, 30, -30, -40]) + +assert_equal(y_after.m1, 110) +assert_equal(y_after.m2, -110) +assert_equal(y_after.m3, -120) + +# let us then check the object created before the re-design/open +assert_equal(x_before.im1, 90) +assert_equal(x_before.im2, -90) +assert_equal(x_before.im3, -100) + +assert_equal(x_before.check, [50, 60, nil, 30, -30, -40]) # difference is those variables assigned in "initialize" + +assert_equal(y_before.m1, 110) +assert_equal(y_before.m2, -110) +assert_equal(y_before.m3, -120) + +# CHANGE THE BASE TYPE +# - if a class of the same name already exists, the class and superclass must match. + +class My_base1; + def m0; 10; end +end +class My_base2; end + +class My_derived < My_base1; + def m1; 100; end + def m2; 200; end +end + +assert_raise(TypeError) { class My_derived < My_base2; end } # superclass mismatch for class My_derived (TypeError) +assert_raise(TypeError) { class My_derived < Object; end } +assert_raise(TypeError) { class My_base1 < My_base2; end } # My_base1 superclass was not specified + +x = My_derived.new +assert_equal(x.m2, 200) +assert_raise(NoMethodError) { x.m3 } + +class My_derived # able to change it WITHOUT superclass specified + def m2; -200; end + def m3; -300; end +end + +assert_equal(x.m0, 10) +assert_equal(x.m1, 100) +assert_equal(x.m2, -200) +assert_equal(x.m3, -300) + +class My_base1; + def m0; 1000; end + def m4; 4000; end +end + +assert_equal(x.m0, 1000) +assert_equal(x.m4, 4000) + +# CHANGE TYPE BY INCLUDING MODULE +module Mod + def helper; 24; end +end +assert_raise(NameError) { My_base8 } +class My_base8; end +class My_derived8 < My_base8; end +x = My_base8.new +assert_raise(NoMethodError) { x.helper } + +class My_base8; + include Mod +end +assert_equal(x.helper, 24) diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_class_variables.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_class_variables.rb new file mode 100644 index 0000000000..4707bd9bff --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_class_variables.rb @@ -0,0 +1,412 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/simple_test.rb' + +# make class_variable_set and class_variable_get callable +describe "Dynamic class variable methods" do + skip "(BUG: private visibility is not working) class variable methods have correct visibility" do + should_raise(NoMethodError, "private method `class_variable_get' called for Object:Class") do + Object.class_variable_get :@@foo + end + should_raise(NoMethodError, "private method `class_variable_set' called for Object:Class") do + Object.class_variable_set :@@foo, 'foo' + end + should_raise(NoMethodError, "private method `class_variable_get' called for Enumerable:Module") do + Enumerable.class_variable_get :@@foo + end + should_raise(NoMethodError, "private method `class_variable_set' called for Enumerable:Module") do + Enumerable.class_variable_set :@@foo, 'foo' + end + Object.class_variables.should == [] + Object.class_variable_defined?(:@@foo).should == false + Enumerable.class_variables.should == [] + Enumerable.class_variable_defined?(:@@foo).should == false + end + + it "class variable methods shouldn't exist on objects" do + should_raise(NoMethodError) { Object.new.class_variable_get :@@foo } + should_raise(NoMethodError) { Object.new.class_variable_set :@@foo, 'foo' } + should_raise(NoMethodError) { Object.new.class_variables } + should_raise(NoMethodError) { Object.new.class_variable_defined? :@@foo } + end + + it "Module#class_variable_set, Module#class_variable_get" do + # make the method visible + class Module + def class_var_get x + class_variable_get x + end + def class_var_set x, v + class_variable_set x, v + end + end + + class Bob + def bar; @@foo = 200; end + end + should_raise(NameError, 'uninitialized class variable @@foo in Bob') { Bob.class_var_get(:@@foo) } + Bob.class_variables.should == [] + Bob.class_var_set(:@@foo, 123).should == 123 + Bob.class_var_get('@@foo').should == 123 + Bob.class_variables.should == ['@@foo'] + Bob.class_variable_defined?(:@@foo).should == true + Bob.class_var_set('@@foo', '654').should == '654' + Bob.class_var_get(:@@foo).should == '654' + end + + it "bad class variable names cause a name error" do + should_raise(NameError) { Object.class_variable_defined?(:@iv) } + should_raise(NameError) { Object.class_variable_defined?("@iv") } + should_raise(NameError) { Object.class_variable_defined?(:iv) } + should_raise(NameError) { Object.class_variable_defined?("iv") } + should_raise(NameError) { Object.class_variable_defined?("@@iv@x") } + should_raise(NameError) { Object.class_var_get(:@iv) } + should_raise(NameError) { Object.class_var_get("@iv") } + should_raise(NameError) { Object.class_var_get(:iv) } + should_raise(NameError) { Object.class_var_get("iv") } + should_raise(NameError) { Object.class_var_get("@@iv@x") } + should_raise(NameError) { Object.class_var_set(:@iv, 10) } + should_raise(NameError) { Object.class_var_set('CV', 10) } + should_raise(NameError) { Object.class_var_set(:lv, 10) } + end +end + +describe "Class variables used inside a type" do + it "basic usages inside the class" do + class My_variables + @@sv = 10 + def check_sv; @@sv; end + def My_variables.check_sv; @@sv; end + def My_variables.check_sv2; @@sv2; end + end + My_variables::check_sv.should == 10 + My_variables.check_sv.should == 10 + should_raise(NameError, 'uninitialized class variable @@sv2 in My_variables') { My_variables.check_sv2 } + My_variables.class_variable_defined?(:@@sv).should == true + My_variables.class_variable_defined?(:@@sv2).should == false + + x = My_variables.new + should_raise(NoMethodError) { My_variables.sv } + should_raise(NoMethodError) { My_variables::sv } + x.check_sv.should == 10 + x.class.class_variables.should == ["@@sv"] + x.class.class_var_get(:@@sv).should == 10 + end + + it "class variables are different from instance variables" do + class My_variables2 + @@v = 555 + @v = 789 + def initialize + @@v = 123 + @v = 456 + end + def self.check_sv; @@v; end + def self.check_iv; @v; end + def check_sv; @@v; end + def check_iv; @v; end + end + + My_variables2.check_sv.should == 555 + My_variables2.check_iv.should == 789 + x = My_variables2.new + x.check_sv.should == 123 + x.check_iv.should == 456 + x.class.check_sv.should == 123 + x.class.check_iv.should == 789 + + My_variables2.class_variables.should == [ '@@v' ] + end +end + +describe "class variables are stored on the module/class where they are first set" do + it "class variables show all variables in the base & mixins" do + module Test1Module1 + def m1_a= x; @@a = x; end + def m1_a; @@a; end + end + module Test1Module2 + def m2_b= x; @@b = x; end + def m2_b; @@b; end + end + class Test1Class1 + include Test1Module1 + def c1_c= x; @@c = x; end + def c1_c; @@c; end + end + class Test1Class2 < Test1Class1 + include Test1Module2 + def c2_d= x; @@d = x; end + def c2_vars; [@@a, @@b, @@c, @@d]; end + end + + Test1Class2.class_variables.should == [] + x = Test1Class2.new + x.m1_a = 123 + x.m2_b = 456 + x.c1_c = 789 + x.c2_d = 555 + Test1Class2.class_variables.should == ["@@d", "@@b", "@@c", "@@a"] + Test1Class1.class_variables.should == ["@@c", "@@a"] + Test1Module1.class_variables.should == ["@@a"] + Test1Module2.class_variables.should == ["@@b"] + x.c2_vars.should == [123, 456, 789, 555] + [x.m1_a, x.m2_b, x.c1_c].should == [123, 456, 789] + end + + it "class variables stay where they are first set" do + module Test2Module1 + def m1_a= x; @@a = x; end + end + module Test2Module2 + def m2_b= x; @@b = x; end + end + class Test2Class1 + include Test2Module1 + def c1_c= x; @@c = x; end + end + class Test2Class2 < Test2Class1 + include Test2Module2 + def c2_a= x; @@a = x; end + def c2_b= x; @@b = x; end + def c2_c= x; @@c = x; end + def c2_d= x; @@d = x; end + def c2_vars; [@@a, @@b, @@c, @@d]; end + end + + Test2Class2.class_variables.should == [] + x = Test2Class2.new + x.c2_a = 12.1 + x.c2_b = 34.3 + x.c2_c = 56.5 + x.c2_d = 78.8 + Test2Class2.class_variables.sort.should == ["@@a", "@@b", "@@c", "@@d"] + x.c2_vars.should == [12.1, 34.3, 56.5, 78.8] + Test2Class1.class_variables.should == [] + Test2Module1.class_variables.should == [] + Test2Module2.class_variables.should == [] + + # now set the variables on the base class & included modules + x.c1_c = 'testC1' + Test2Class2.class_variables.sort.should == ["@@a", "@@b", "@@c", "@@d"] + Test2Class1.class_variables.should == ['@@c'] + Test2Module1.class_variables.should == [] + Test2Module2.class_variables.should == [] + x.m1_a = 'testM1' + Test2Class1.class_variables.should == ['@@c', '@@a'] + Test2Module1.class_variables.should == ['@@a'] + Test2Module2.class_variables.should == [] + x.m2_b = 'testM2' + Test2Class1.class_variables.should == ['@@c', '@@a'] + Test2Module1.class_variables.should == ['@@a'] + Test2Module2.class_variables.should == ['@@b'] + x.c2_vars.should == [12.1, 34.3, 56.5, 78.8] + Test2Module1.class_var_get('@@a').should == 'testM1' + Test2Module2.class_var_get('@@b').should == 'testM2' + Test2Class1.class_var_get('@@a').should == 'testM1' + Test2Class1.class_var_get('@@c').should == 'testC1' + end + + it "variables set on mixins or the super class can't be set only to the derived class" do + module Test3Module1 + def m1_a= x; @@a = x; end + def m1_a; @@a; end + end + module Test3Module2 + def m2_b= x; @@b = x; end + def m2_b; @@b; end + end + class Test3Class1 + include Test3Module1 + def c1_c= x; @@c = x; end + def c1_c; @@c; end + end + class Test3Class2 < Test3Class1 + include Test3Module2 + def c2_a= x; @@a = x; end + def c2_b= x; @@b = x; end + def c2_c= x; @@c = x; end + def c2_d= x; @@d = x; end + def c2_vars; [@@a, @@b, @@c, @@d]; end + def c2_vars_a_b; [@@a, @@b]; end + end + + Test3Class2.class_variables.should == [] + x = Test3Class2.new + x.m1_a = 123 + x.m2_b = 456 + x.c2_vars_a_b.should == [123, 456] + x.c1_c = 789 + x.c2_a = 'aaa' + x.c2_vars_a_b.should == ['aaa', 456] + x.c2_b = 'bbb' + x.c2_c = 'ccc' + x.c2_d = 'ddd' + Test3Class2.class_variables.sort.should == ["@@a", "@@b", "@@c", "@@d"] + Test3Class1.class_variables.should == ["@@c", "@@a"] + Test3Module1.class_variables.should == ["@@a"] + Test3Module2.class_variables.should == ["@@b"] + x.c2_vars.should == ['aaa', 'bbb', 'ccc', 'ddd'] + [x.m1_a, x.m2_b, x.c1_c].should == ['aaa', 'bbb', 'ccc'] + end + + it 'class variables with same name' do + module Test4Module1 + def m1_a= x; @@a = x; end + def m1_a; @@a; end + end + module Test4Module2 + def m2_a= x; @@a = x; end + def m2_a; @@a; end + end + class Test4Class1 + include Test4Module1 + def c1_a= x; @@a = x; end + def c1_a; @@a; end + end + class Test4Class2 < Test4Class1 + include Test4Module2 + def c2_a= x; @@a = x; end + def c2_a; @@a; end + end + + x = Test4Class2.new + + x.c1_a = 456 + should_raise(NameError) { x.m1_a } + should_raise(NameError) { x.m2_a } + x.c1_a.should == 456 + x.c2_a.should == 456 + + x.m1_a = 123 + x.m1_a.should == 123 + should_raise(NameError) { x.m2_a } + x.c1_a.should == 456 + x.c2_a.should == 456 + + x.c2_a = 789 + x.m1_a.should == 123 + should_raise(NameError) { x.m2_a } + x.c1_a.should == 789 + x.c2_a.should == 789 + + x.m2_a = 210 + x.m1_a.should == 123 + x.m2_a.should == 210 + x.c1_a.should == 789 + x.c2_a.should == 210 + + Test4Class2.class_var_get("@@a").should == 210 + Test4Class1.class_var_get("@@a").should == 789 + Test4Module2.class_var_get("@@a").should == 210 + Test4Module1.class_var_get("@@a").should == 123 + end +end + +describe "class variables are lexically bound to the surrounding class/module" do + it "class variables in nested modules get the correct scope" do + module Foo + @@bob = 123 + + def self.foo + @@bob + end + + module Bar + module Baz + def self.baz + @@bob = 555 + end + end + + @@bob = 456 + def self.bar + @@bob + end + end + + class Bob + @@bob = 789 + + def self.bob + @@bob + end + end + + module Zoo + def self.zoo + @@bob + end + end + end + + Foo.foo.should == 123 + Foo::Bar.bar.should == 456 + Foo::Bob.bob.should == 789 + Foo::Bar::Baz.baz.should == 555 + + should_raise(NameError, "uninitialized class variable @@bob in Foo::Zoo") { Foo::Zoo.zoo } + end + + it "blocks don't interfere with scoping" do + class Module + def call_test_block + yield + end + end + + module Foo + call_test_block do + @@abc = 'test1' + end + def self.get_abc; @@abc; end + + call_test_block do + module Bar + def self.get_def; @@def; end + @@def = 'test2' + end + end + end + + Foo.get_abc.should == 'test1' + Foo::Bar::get_def.should == 'test2' + end + + it "module level get/set methods set variables on Object (this test needs to be last)" do + # test module level get/set methods + def set_class_var x; @@osv = x; end + def get_class_var; @@osv; end + + class Using_module_get_set_functions + def check_sv; get_class_var; end + def set_sv x; set_class_var x; end + end + + x = Using_module_get_set_functions.new + should_raise(NameError) { x.check_sv } + x.set_sv(777).should == 777 + x.check_sv.should == 777 + x.class.class_variables.should == ['@@osv'] + Object.class_variables.should == ['@@osv'] + Object.class_var_get(:@@osv).should == 777 + x.class.class_var_get('@@osv').should == 777 + Array.class_var_get('@@osv').should == 777 + Comparable.class_variables.should == [] + end +end + +finished diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_class_variables2.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_class_variables2.rb new file mode 100644 index 0000000000..93c158c4ff --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_class_variables2.rb @@ -0,0 +1,226 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# status: complete (after fixing $x issue) +# - necessary update needed based on the implementation change. + +# test_class_variable.rb already covers most of aspects related to class variables. +# this test is more target on the current implementation. + +require '../../util/assert.rb' + +# Set/GetObjectClassVariable +class C100; + def m3; @@a100; end +end +x = C100.new +class << x + def m1; @@a100 = 1; end # expected warning + def m2; [@@a100, @@a110]; end # warning + @@a110 = 2 # warning +end + +assert_equal(@@a110, 2) +assert_raise(NameError, "uninitialized class variable @@a100 in Object") { x.m2 } +assert_equal(x.m1, 1) +assert_equal(x.m2, [1, 2]) +assert_equal(x.m3, 1) +assert_equal(@@a100, 1) + +y = C100.new +assert_equal(y.m3, 1) + +# GetClassVariableOwner + +@@a200 = 3 +class C200; + assert_equal(@@a200, 3) # look for the superclass Object + assert_raise(NameError, "uninitialized class variable @@a210 in C200") { @@a210 } + @@a200 = 4 + @@a210 = 5 + assert_equal(@@a200, 4) + assert_equal(@@a210, 5) +end +assert_equal(@@a200, 4) +assert_raise(NameError, "uninitialized class variable @@a210 in Object") { @@a210 } + +# inside singleton, emits SetClassVariable +# the class variables are in the module M300 +module M300 + class C300; end + $x = C300.new + + class << $x + @@a300 = 6 + def m1; @@a300; end + end + assert_equal(@@a300, 6) + @@a300 = 7 + assert_equal($x.m1, 7) + + def self.m2 + @@a300 + end + def self.m3= v + @@a300 = v + end +end +def M300.m4; @@a300; end +def M300.m5= v; @@a300 = v; end # add to the "Object" + +M300.m3= 8 +assert_equal(M300.m2, 8) +assert_raise(NameError, "uninitialized class variable @@a300 in Object") { M300.m4 } +M300.m5 = 9 +assert_equal(@@a300, 9) +assert_equal(M300.m4, 9) +assert_equal(M300.m2, 8) # remains 8 + +# walking the scope: GetClassVariableOwner +@@a400 = -1 +module M400 + @@a401 = 1 + @@a402 = 2 + + class C410 + @@a401 = 11 + @@a403 = 33 + + assert_equal(@@a400, -1) + assert_equal(@@a401, 11) + assert_raise(NameError, "uninitialized class variable @@a402 in M400::C410") { @@a402 } + assert_equal(@@a403, 33) + end + + $x = C410.new # update! + class << $x + @@a402 = 222 + @@a404 = 444 + assert_raise(NameError, "uninitialized class variable @@a400 in M400") { @@a400 } + assert_equal(@@a401, 1) + assert_equal(@@a402, 222) + assert_raise(NameError, "uninitialized class variable @@a403 in M400") { @@a403 } + assert_equal(@@a404, 444) + end + + module M420 + @@a401 = 1111 + @@a405 = 5555 + assert_raise(NameError, "uninitialized class variable @@a400 in M400::M420") { @@a400 } + assert_equal(@@a401, 1111) + assert_raise(NameError, "uninitialized class variable @@a402 in M400::M420") { @@a402 } + assert_equal(@@a405, 5555) + end + + assert_raise(NameError, "uninitialized class variable @@a400 in M400") { @@a400 } + assert_equal(@@a401, 1) + assert_equal(@@a402, 222) + assert_raise(NameError, "uninitialized class variable @@a403 in M400") { @@a403 } + assert_equal(@@a404, 444) +end + +# order impact: where the static variable is stored? +class C500 + def m0; @@a501; end +end +class C510 < C500 + def m1= v; @@a501 = v; end + def m2; @@a501; end +end +x = C510.new +x.m1 = -1 +assert_equal(x.m2, -1) +assert_raise(NameError, "uninitialized class variable @@a501 in C500") { x.m0 } + +# order +class C600 + def m1= v; @@a601 = v; end + def m2; @@a601; end +end + +class C610 < C600 + def m3= v; @@a601 = v; end + def m4; @@a601; end +end + +x = C610.new +assert_raise(NameError, "uninitialized class variable @@a601 in C610") { x.m4 } +x.m1 = -2 +assert_equal(x.m4, -2) +assert_equal(x.m2, -2) +x.m3 = -22 +assert_equal(x.m4, -22) +assert_equal(x.m2, -22) + +# TryResolveClassVariable +module M700 + @@a710 = 10 + @@a720 = 20 +end +module M701 + @@a720 = -20 + @@a730 = -30 + @@a740 = -40 +end + +module M702 + @@a741 = -41 +end + +class C703 + include M702 + + @@a740 = 400 + @@a750 = 500 + @@a760 = 600 +end + +class C705 < C703 + include M700, M701 + + @@a760 = -6000 + + assert_equal(@@a710, 10) + assert_equal(@@a720, 20) # in order search, the first hit wins + assert_equal(@@a730, -30) + assert_equal(@@a740, -40) # C705's mixins first + assert_equal(@@a750, 500) + assert_equal(@@a760, -6000) # starts from self + assert_equal(@@a741, -41) +end + +module M706 + include M700, M701 + + @@a750 = 500 + + assert_equal(@@a710, 10) + assert_equal(@@a720, 20) # in order search, the first hit wins + assert_equal(@@a730, -30) + assert_equal(@@a740, -40) + assert_equal(@@a750, 500) +end + +# SCOPE +def read_sv + return @@sv1 +end +class C800 + @@sv1 = 1 + def read; read_sv; end +end +assert_raise(NameError) { C800.new.read } +@@sv1 = 2 +assert_equal(C800.new.read, 2) \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_const.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_const.rb new file mode 100644 index 0000000000..82a7b8e431 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_const.rb @@ -0,0 +1,109 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# status: complete + +require '../../util/assert.rb' + +# how to define const, and how to read it later +TOP_LEVEL_CONST = 2 + +assert_equal(TOP_LEVEL_CONST, 2) +assert_equal(Object::TOP_LEVEL_CONST, 2) +assert_raise(NoMethodError) { Object.TOP_LEVEL_CONST } +assert_raise(TypeError) { self::TOP_LEVEL_CONST } # main is not a class/module (TypeError) + +def check + # access inside function + assert_equal(TOP_LEVEL_CONST, 2) + # try to declare one + # CONST_INSIDE_FUNCTION = 3 # syntax error expected (to be added) +end +check + +class My_const + # declare one inside class + CONST = 1 + + # access them inside class + def check + assert_equal(TOP_LEVEL_CONST, 2) + assert_equal(Object::TOP_LEVEL_CONST, 2) + + assert_equal(CONST, 1) + assert_equal(My_const::CONST, 1) + end + + def check2 + CONST2 + end + + # re-assign -> warning + CONST = 1 +end + +assert_equal(My_const::CONST, 1) +assert_raise(NameError) { Object::CONST } # uninitialized constant CONST (NameError) + +x = My_const.new +assert_raise(NoMethodError) { x.CONST } # undefined method `CONST' for # (NoMethodError) +assert_raise(TypeError) { x::CONST } # # is not a class/module (TypeError) +x.check + +assert_raise(NameError) { x.check2 } +# define outside class +My_const::CONST2 = 345 +assert_equal(x.check2, 345) + +# ORDER/SCOPE + +def foo + Foo +end +Foo = 123 + +class Bar + def bar + foo + end + def baz + Foo + end + Foo = 456 +end + +assert_equal(Bar.new.bar, 123) +assert_equal(Bar.new.baz, 456) +assert_equal(Bar::Foo, 456) +assert_raise(TypeError) { Bar.new::Foo } # # is not a class/module + +Foo = 789 # warning +Bar::Foo = 987 # warning +assert_equal(Bar.new.bar, 789) +assert_equal(Bar.new.baz, 987) + +# SCOPE + +class Object + def test_const_lookup + Array.new(3,5) + end +end + +x = module TestModule + # This function should look for Object::Array, not TestModule::Array + test_const_lookup +end +assert_equal(x, [5, 5, 5]) diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_const_vars_basic.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_const_vars_basic.rb new file mode 100644 index 0000000000..d158da0712 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_const_vars_basic.rb @@ -0,0 +1,220 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# status: complete + +require '../../util/assert.rb' + +# get/set const inside Object, class, module + +# get +assert_raise(NameError, "uninitialized constant S1") { S1 } +assert_raise(NameError, "uninitialized constant S2") { ::S2 } +assert_raise(NameError, "uninitialized constant S3") { Object::S3 } + +class SpecialClass; end +module SpecialModule; end + +assert_raise(NameError, "uninitialized constant SpecialClass::S7") { SpecialClass::S7 } +assert_raise(NameError, "uninitialized constant SpecialModule::S8") { SpecialModule::S8 } + +# set +S1 = 1 +::S2 = 2 +Object::S3 = 3 + +assert_raise(TypeError, "main is not a class/module") { self::S4 = 4 } +assert_raise(NameError, "uninitialized constant C") { C::S5 = 5 } +assert_raise(NameError, "uninitialized constant M") { M::S6 = 6 } + +assert_raise(NoMethodError, "undefined method `S7=' for SpecialClass:Class") { SpecialClass.S7 = 7 } +SpecialClass::S7 = 7 +SpecialModule::S8 = 8 + +# get +assert_equal(S1, 1) +assert_equal(::S1, 1) +assert_equal(Object::S1, 1) + +assert_equal(Object::S2, 2) +assert_equal(::S3, 3) + +sc = SpecialClass.new +Singleton = class << sc; self; end +assert_equal(SpecialClass::S1, 1) # warning: toplevel constant S1 referenced by SpecialClass::S1 +assert_equal(Singleton::S1, 1) # warning: toplevel constant S1 referenced by SpecialClass::S1 +assert_equal(Singleton::S7, 7) +assert_raise(TypeError) { sc::S7 } +assert_raise(NameError, "uninitialized constant SpecialClass::S8") { SpecialClass::S8 } + +assert_equal(SpecialModule::S8, 8) +assert_raise(NameError, "uninitialized constant SpecialModule::S1") { SpecialModule::S1 } +assert_raise(NameError, "uninitialized constant SpecialModule::S7") { SpecialModule::S7 } + +class C + # get those defined outside + assert_equal(::S1, 1) + assert_equal(Object::S2, 2) + assert_equal(S3, 3) + + assert_equal(SpecialClass::S7, 7) + assert_equal(SpecialModule::S8, 8) + + assert_equal(C::S2, 2) # warning: toplevel constant S2 referenced by C::S2 + assert_equal(C::SpecialClass::S7, 7) # warning + assert_equal(C::SpecialModule::S8, 8) # warning + + # set + S9 = 9 + ::S10 = 10 + C::S11 = 11 + self::S12 = 12 + + SpecialClass::S13 = 13 + SpecialModule::S14 = 14 + + # get those newly defined + assert_equal(S9, 9) + assert_equal(C::S9, 9) + assert_equal(self::S9, 9) + assert_raise(NameError, "uninitialized constant S9") { ::S9 } + + assert_equal(::S10, 10) + assert_equal(Object::S10, 10) + assert_equal(C::S10, 10) # warning + assert_equal(self::S10, 10) # warning + + assert_equal(self::S11, 11) + assert_equal(C::S12, 12) + + assert_equal(SpecialClass::S7, 7) + assert_equal(SpecialClass::S13, 13) + assert_equal(SpecialModule::S8, 8) + assert_equal(SpecialModule::S14, 14) + + assert_equal(SpecialClass::S10, 10) # warning + assert_raise(NameError, "uninitialized constant SpecialModule::S10") { SpecialModule::S10 } + + def C::m1; 1; end # succeed, defined to the C "class" + + # duplicate variable names + ::S2 = 20 # warning + assert_equal(Object::S2, 20) + S11 = 110 # warning + assert_equal(self::S11, 110) + + def self::method1_before_redefining_C; C; end + def self::method2_before_redefining_C; self::S9; end + + C = "ruby" + assert_equal(C, "ruby") + assert_equal(self::C, "ruby") + + assert_raise(TypeError, "ruby is not a class/module") { C::C } + assert_raise(TypeError, "ruby is not a class/module") { C::S12 } + + def C::m2; 2; end # succeed, defined to the C "ruby" + + # calling methods inside C + assert_raise(NoMethodError) { C::m1 } + assert_equal(C::m2, 2) + assert_equal(self::m1, 1) + assert_raise(NoMethodError) { self::m2 } + + def self::method1_after_redefining_C; C; end + def self::method2_after_redefining_C; self::S9; end +end + +# get +assert_equal(S2, 20) +assert_equal(S10, 10) +assert_equal(C::S11, 110) + +assert_equal(SpecialClass::S7, 7) +assert_equal(SpecialClass::S13, 13) +assert_equal(SpecialModule::S8, 8) +assert_equal(SpecialModule::S14, 14) + +# calling methods outside C +assert_equal(C::m1, 1) +assert_raise(NoMethodError) { C::C::m1 } +assert_raise(NoMethodError) { C::m2 } +assert_equal(C::C::m2, 2) + +# +assert_equal(C::method1_before_redefining_C, "ruby") +assert_equal(C::method2_before_redefining_C, 9) +assert_equal(C::method1_after_redefining_C, "ruby") +assert_equal(C::method2_after_redefining_C, 9) + +module M + # get + assert_equal(S1, 1) + assert_equal(C::S9, 9) + assert_equal(SpecialModule::S8, 8) + + # set + S15 = 15 + ::S16 = 16 + M::S17 = 17 + self::S18 = 18 + + SpecialClass::S19 = 19 + SpecialModule::S20 = 20 + + # get + assert_equal(M::S15, 15) + assert_equal(self::S15, 15) + + assert_equal(S16, 16) + assert_equal(C::S16, 16) # warning + assert_raise(NameError) { self::S16 } + assert_raise(NameError) { M::S16 } + + assert_equal(S17, 17) + assert_equal(S18, 18) + + assert_equal(SpecialClass::S7, 7) + assert_equal(SpecialClass::S13, 13) + assert_equal(SpecialClass::S19, 19) + + assert_equal(SpecialModule::S8, 8) + assert_equal(SpecialModule::S14, 14) + assert_equal(SpecialModule::S20, 20) + + module N + S21 = 21 + ::S22 = 22 + + assert_equal(S22, 22) + assert_equal(::S22, 22) + end + + assert_raise(NameError) { S21 } + assert_equal(N::S21, 21) + assert_equal(M::N::S21, 21) + + assert_equal(::S22, 22) + assert_equal(S22, 22) + assert_raise(NameError) { N::S22 } +end + +assert_equal(M::S15, 15) +assert_equal(S16, 16) +assert_raise(NameError) { M::S16 } +assert_raise(NameError) { ::S17 } +assert_equal(M::N::S21, 21) +assert_equal(S22, 22) + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_const_vars_in_singleton.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_const_vars_in_singleton.rb new file mode 100644 index 0000000000..d07bd9ddf6 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_const_vars_in_singleton.rb @@ -0,0 +1,95 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# status: complete + +require '../../util/assert.rb' + +# access const from singleton class + +class C100 + S101 = 1 + class NC + S102 = 2 + end + module NM + S103 = 3 + end +end + +x = C100.new + +singleton = class << x + S111 = 11 + class NC + S112 = 12 + end + module NM + S113 = 13 + end + + def self::check + assert_raise(NameError) { C100::S111 } + assert_raise(NameError) { NC::S102 } + assert_raise(NameError) { C100::NC::S112 } + assert_raise(NameError) { NM::S103 } + assert_raise(NameError) { C100::NM::S113} + + [ + S101, self::S101, C100::S101, + S111, self::S111, + C100::NC::S102, + NC::S112, self::NC::S112, + + C100::NM::S103, + NM::S113, self::NM::S113, + ] + end + + self +end + +assert_equal(singleton::S101, 1) +assert_raise(NameError) { singleton::NC::S102 } +assert_raise(NameError) { singleton::NM::S103 } + +assert_equal(singleton::S111, 11) +assert_equal(singleton::NC::S112, 12) +assert_equal(singleton::NM::S113, 13) + +assert_equal(singleton::check, [1, 1, 1, 11, 11, 2, 12, 12, 3, 13, 13]) + +## +class C200 + S101 = 1 + def C200::m1; 1; end + def m2; 2; end +end + +singleton = class << C200 + S111 = 11 + + def C200::m3; 3; end + def m4; 4; end + def self::m5; 5; end + + self +end + +assert_raise(NameError) { singleton::S101 } +assert_equal(singleton::S111, 11) +assert_raise(NoMethodError) { singleton::m3 } +assert_raise(TypeError) { singleton.new } #.m4 +assert_equal(singleton::m5, 5) diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_declare.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_declare.rb new file mode 100644 index 0000000000..419659219d --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_declare.rb @@ -0,0 +1,122 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# status: complete + +require '../../util/assert.rb' + +# WHAT CAN BE THE CLASS/MODULE NAME? + +# re-opening the type/module but the name is used as something else +C_FixNum = 10 +assert_raise(TypeError, "C_FixNum is not a class") { class C_FixNum; end } + +class C_Class; end +assert_raise(TypeError, "C_Class is not a module") { module C_Class; end } + +M_Str = "string" +assert_raise(TypeError, "M_Str is not a module") { module M_Str; end } + +module M_Module; end +assert_raise(TypeError, "M_Module is not a class") { class M_Module; end } + +# try to derive from itself +class B100 < Object; end +assert_raise(TypeError, "superclass mismatch for class B100") { class B100 < B100; end } +assert_raise(TypeError, "superclass mismatch for class B110") { + class B110 + class ::B110 < self; end + end +} +# unrelated +class B120 + class B120 < self; end + def m; 3; end +end +assert_equal(B120::B120.new.m, 3) + +# re-opening with different 'name' +class B200 + ::B210 = self + def m1; 1; end +end +class B210 + def m2; 2; end +end +x = B200.new +assert_equal([x.m1, x.m2, x.class.name], [1, 2, 'B200']) +y = B210.new +assert_equal([y.m1, y.m2, y.class.name], [1, 2, 'B200']) +assert_equal(B200, B210) + +# WHAT CAN BE THE BASE TYPE? + +assert_raise(TypeError, "superclass must be a Class (String given)") { class C < 'abc'; end } +assert_raise(TypeError, "superclass must be a Class (Fixnum given)") { class C < 1; end } +#assert_raise(NameError) { class C < object; end } + +class B300; end +x = B300 +class B310 < ('whatever'; "else"; x); end + +# WHERE YOU CAN DEFINE THE CLASS + +# based on current implementation +# Define*Class combination +class C100 + class C100 + S110 = 110 + end + class C100::C100 + S120 = 120 + end + class << self + S130 = 130 + end + S140 = 140 + C100::S150 = 150 +end +assert_equal(C100::S140, 140) +assert_equal(C100::C100::S110, 110) +assert_equal(C100::C100::C100::S120, 120) +assert_equal(C100::C100::S150, 150) +assert_raise(NameError) { C100::S130 } + +class C200 + class C200::C200 + S210 = 210 + end + class C200 + S220 = 220 + end + + C200::S230 = 230 + C200::C200::S240 = 240 # warning + + class ::C100 + S160 = 160 + end +end + +assert_equal(C200::C200::S210, 210) +assert_equal(C200::C200::S220, 220) +assert_equal(C200::C200::S230, 230) +assert_equal(C200::S240, 240) +assert_equal(C100::S160, 160) + +class C200::C200 + S250 = 250 +end +assert_equal(C200::C200::S250, 250) diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_find_const_in_methods.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_find_const_in_methods.rb new file mode 100644 index 0000000000..f1ab13a442 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_find_const_in_methods.rb @@ -0,0 +1,176 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# status: complete + +require '../../util/assert.rb' + +# reading CONST inside methods + +MyVar = 1 + +module MyModule + C100 = 100 + class MyNestedClass + C110 = 110 + end + module MyNestedModule + C120 = 120 + end +end + +class MyClass + C200 = 200 + class MyNestedClass + C210 = 210 + end + module MyNestedModule + C220 = 220 + end +end + +class TestClass + C300 = 300 + + class MyNestedClass + C310 = 310 + end + + def my_instance_method + assert_raise(NameError) { MyModule::MyVar } # can not find the top level constant referenced by top level MODULE + assert_raise(NameError) { C400 } + + assert_raise(TypeError) { self::C300 } + assert_raise(TypeError) { self::MyNestedClass } + assert_raise(TypeError) { self::MyNestedModule } + assert_raise(NameError) { TestClass::MyNestedModule::C400 } + + return [ + MyVar, ::MyVar, + MyModule::C100, MyModule::MyNestedClass::C110, MyModule::MyNestedModule::C120, + MyClass::C200, MyClass::MyNestedClass::C210, MyClass::MyNestedModule::C220, + + TestClass::C300, C300, + TestClass::C305, C305, + + TestClass::MyNestedClass::C310, MyNestedClass::C310, + TestClass::MyNestedModule::C320, MyNestedModule::C320, + + MyClass::MyVar, TestClass::MyVar, # warning + ] + end + + def self::my_static_method + assert_raise(NameError) { MyModule::MyVar } + assert_raise(NameError) { C400 } + assert_raise(NameError) { TestClass::MyNestedClass::C400 } + + return [ + MyVar, ::MyVar, + MyModule::C100, MyModule::MyNestedClass::C110, MyModule::MyNestedModule::C120, + MyClass::C200, MyClass::MyNestedClass::C210, MyClass::MyNestedModule::C220, + + TestClass::C300, C300, + TestClass::C305, C305, + + TestClass::MyNestedClass::C310, MyNestedClass::C310, + TestClass::MyNestedModule::C320, MyNestedModule::C320, + + MyClass::MyVar, TestClass::MyVar, # warning + + self::C300, self::C305, + self::MyNestedClass::C310, self::MyNestedModule::C320 + ] + end + + C305 = 305 # defined after my_instance_method + + module MyNestedModule + C320 = 320 + end +end + +assert_equal(TestClass.new.my_instance_method, [1, 1, 100, 110, 120, 200, 210, 220, 300, 300, 305, 305, 310, 310, 320, 320, 1, 1]) +assert_equal(TestClass::my_static_method, [1, 1, 100, 110, 120, 200, 210, 220, 300, 300, 305, 305, 310, 310, 320, 320, 1, 1, 300, 305, 310, 320]) + +# dup name +class C100 + S10 = 10 + + def C100::m1; + [ S10, self::S10, C100::S10, ::C100::S10 ] + end +end + +assert_equal(C100::m1, [10, 10, 10, 10]) + +# +class C200 + S10 = 10 + + def C200::m1; + assert_equal(C200.class, Class) # read C200 is ok + assert_raise(NameError, "uninitialized constant C200::C200::S10") { C200::S10 } + + [ S10, self::S10, ::C200::S10 ] + end + + class C200 + def self::m2; + assert_raise(NameError) { self::S10 } + assert_raise(NameError) { C200::S10 } + + [ + S10, ::C200::S10, + S20, self::S20, C200::S20, ::C200::C200::S20 + ] + end + S20 = 20 + end +end + +assert_equal(C200::m1, [10, 10, 10]) +#assert_equal(C200::C200::m2, [10, 10, 20, 20, 20, 20]) + +# module +module M300 + def M300::m1; 1; end + + class M300 + def m2; 2; end + def M300::m3; 3; end + + def m4; +# assert_raise(NameError) { m1 } +# assert_raise(NoMethodError) { M300::m1 } + + assert_raise(NoMethodError) { M300::m2 } + assert_raise(NoMethodError) { M300::M300::m2 } + +# assert_raise(NameError) { m3 } + + [ + ::M300::m1, + m2, self::m2, + M300::m3, + ] + end + end +end +assert_equal(M300::m1, 1) +assert_equal(M300::M300.new.m2, 2) +#assert_equal(M300::M300::m3, 3) + +assert_equal(M300::M300.new.m4, [1, 2, 2, 3]) \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_find_method.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_find_method.rb new file mode 100644 index 0000000000..43d472b415 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_find_method.rb @@ -0,0 +1,156 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# When a method is called, Ruby searches for it in a number of places in the following order: +# +# - Among the methods defined in that object (i.e., singleton methods). +# - Among the methods defined by that object's class. +# - Among the methods of the modules included by that class. +# - Among the methods of the superclass. +# - Among the methods of the modules included by that superclass. +# - Repeats Steps 4 and 5 until the top-level object is reached. + +class My + def method1; 10; end + def method2; 20; end +end + +x = My.new +assert_equal(x.method1, 10) +assert_raise(NoMethodError) { x.method_not_exists } + +class << x + def method2; 30; end + def method3; 40; end +end + +assert_equal(x.method1, 10) +assert_equal(x.method2, 30) +assert_equal(x.method3, 40) +assert_raise(NoMethodError) { x.method_not_exists } + +# include module +module Simple + def method1; -10; end + def method2; -20; end +end + +class My_with_module + include Simple + def method2; 20; end + def method3; 30; end +end + +x = My_with_module.new +assert_equal(x.method1, -10) +assert_equal(x.method2, 20) +assert_equal(x.method3, 30) +assert_raise(NoMethodError) { x.method_not_exists } + +class << x + def method4; 40; end +end +assert_equal(x.method1, -10) +assert_equal(x.method2, 20) +assert_equal(x.method3, 30) +assert_equal(x.method4, 40) +assert_raise(NoMethodError) { x.method_not_exists } + +# with superclass + +class My_base + def method6; -600; end + def method7; -700; end +end + +class My_derived < My_base + def method7; 700; end + def method8; 800; end +end + +x = My_derived.new +assert_equal(x.method6, -600) +assert_equal(x.method7, 700) +assert_equal(x.method8, 800) +assert_raise(NoMethodError) { x.method_not_exists } + +# base with included module + +class My_base_with_module + include Simple + def method6; -600; end + def method7; -700; end +end + +class My_derived2 < My_base_with_module + def method2; 200; end + def method7; 700; end + def method8; 800; end +end + +x = My_derived2.new +assert_equal(x.method1, -10) +assert_equal(x.method2, 200) +assert_equal(x.method6, -600) +assert_equal(x.method7, 700) +assert_equal(x.method8, 800) +assert_raise(NoMethodError) { x.method_not_exists } + +class << x +end +assert_equal(x.method1, -10) +assert_equal(x.method2, 200) +assert_equal(x.method6, -600) +assert_equal(x.method7, 700) +assert_equal(x.method8, 800) +assert_raise(NoMethodError) { x.method_not_exists } + +# multiple levels + +class My_level1 + include Simple + def method1; 100; end +end + +class My_level2 < My_level1 + def method2; 200; end +end + +class My_level3 < My_level2 + def method3; 300; end +end + +x = My_level3.new +assert_equal(x.method1, 100) +assert_equal(x.method2, 200) +assert_equal(x.method3, 300) +assert_raise(NoMethodError) { x.method_not_exists } + +# access control related to inheritance: the public override method in superclass + +class My_base_with_public_method + public + def method; 100; end +end + +class My_derived_with_private_method < My_base_with_public_method + private + def method; 200; end +end + +x = My_derived_with_private_method.new +#assert_raise(NoMethodError) { x.method } \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_include.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_include.rb new file mode 100644 index 0000000000..b286c924a5 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_include.rb @@ -0,0 +1,214 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +############################################### +puts 'test what can be included?' +############################################### + +class C101; end +module M101 + def m101; 1; end +end +module M102 + def m102; 2; end +end +module M103 + def m103; 3; end +end +module M104 + def m104; 4; end +end + +module M105 +# assert_raise(ArgumentError, "cyclic include detected") { include M105 } +end + +class C100 +# assert_raise(TypeError, "wrong argument type nil (expected Module)") { include nil } +# assert_raise(TypeError, "wrong argument type Fixnum10 (expected Module)") { include 1 } +# assert_raise(TypeError, "wrong argument type String (expected Module)") { include "str" } + assert_raise(TypeError, "wrong argument type Class (expected Module)") { include C101 } + + ## atomic: M101 is not mixed in + assert_raise(TypeError, "wrong argument type Class (expected Module)") { include M101, C101 } + assert_false { C100.include? M101 } + + # include a list + include M102 + include M102, M103 # list + include M103, M103 # twice +end + +x = C100.new +assert_raise(NoMethodError) { x.m101 } +assert_equal(x.m102, 2) +assert_equal(x.m103, 3) + +############################################### +puts "test where can 'include' be used?" +############################################### + +#assert_raise(NameError) { m104 } # !!! + +include M104 + +assert_equal(m104, 4) +assert_equal(x.m104, 4) +assert_equal(C100.m104, 4) + +############################################### +puts "test return value" +############################################### + +module M301; end +class C300 + $x = include M301 # !!! + assert_equal($x, C300) +end + +############################################### +puts "test transitive mix-in" +############################################### + +module M201 + def m201; 1; end +end +module M202 + include M201 + def m202; 2; end +end +module M203 + def m203; 3; end +end +module M204 + include M203 + def m204; 4; end +end +module M205 + include M202, M204 + def m205; 5; end +end + +class C200 + include M205 +end +x = C200.new +assert_equal(x.m201, 1) +assert_equal(x.m202, 2) +assert_equal(x.m203, 3) +assert_equal(x.m204, 4) +assert_equal(x.m205, 5) +assert_raise(NoMethodError) { x.m206 } + +assert_true { C200.include? M201 } +assert_false { M204.include? M204 } +assert_true { M202.include? M201 } + +############################################### +puts "test changes from the dependent modules" +############################################### + +module M201 + def m211; 11; end +end +module M207 + def m207; 7; end +end +module M204 + include M207 # no impact on C200, has impact on M204 + def m214; 14; end +end +assert_equal(x.m211, 11) +assert_equal(x.m214, 14) +assert_raise(NoMethodError) { x.m207 } +assert_true { M204.include? M207 } +assert_false { C200.include? M207 } + +############################################### +puts "test context" +############################################### + +module M401 + def m401; 1; end +end +module M402 + def m402; 2; end +end +module M403 + def m403; 3; end +end + +module M404 + module M405 + include M403 + def m405; 5; end + end + def m404; 4; end +end +class C410 + class C420 + include M402 + end + class C430 + include M404 + end + class C440 + include M404::M405 + end + include M401 +end + +{ C410.new => [1, 0, 0, 0, 0], + C410::C420.new => [0, 2, 0, 0, 0], + C410::C430.new => [0, 0, 0, 4, 0], + C410::C440.new => [0, 0, 3, 0, 5], +}.each do |k, v| + v.each do |result| + if result != 0 + assert_equal(k.send("m40#{result}"), result) + else + assert_raise(NoMethodError) { k.send("m40#{result}") } + end + end +end + +#assert_equal(C410.ancestors, [C410, M401, Object, M104, Kernel]) +#assert_equal(C410::C420.ancestors, [C410::C420, M402, Object, M104, Kernel]) +#assert_equal(C410::C440.ancestors, [C410::C440, M404::M405, M403, Object, M104, Kernel]) + +############################################### +puts "test include Kernel" +############################################### + +class C500 + include Kernel +end +#assert_equal(C500.new == 3, false) + +module M510 + include Kernel +end +class C520 + include M510 +end +#assert_equal(C520.new == 3, false) + +class C530 +end + +include Kernel +assert_equal(C530.new == 3, false) diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_instance_variables.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_instance_variables.rb new file mode 100644 index 0000000000..637a337886 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_instance_variables.rb @@ -0,0 +1,387 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/simple_test.rb' + +describe "Instance variables used in a type" do + it "basic usage" do + class My_variables + def initialize + @iv = 20 + end + + def check_iv; @iv; end + end + + x = My_variables.new + should_raise(NoMethodError) { x.iv } + + x.check_iv.should == 20 + + x.instance_variables.should == ["@iv"] + x.instance_variable_get(:@iv).should == 20 + end + + it "instance variables are initialized to nil" do + class My_instance_variable + @iv = 10 + def check_iv; @iv; end + def set_iv; @iv = 20; end + end + + x = My_instance_variable.new + x.check_iv.should == nil ## @iv is not assigned during the class creation + x.set_iv + x.check_iv.should == 20 + end + + it "instance variables are per instance" do + class TestVars1 + def x= v; @x=v; end + def x; @x; end + end + b = TestVars1.new + b.x = 5 + b.x.should == 5 + b2 = TestVars1.new + b2.x.should == nil + b.x.should == 5 + b2.x = "abc" + b2.x.should == "abc" + b.x.should == 5 + end + + it "instance variable assignment stores references (e.g. it doesn't copy the value)" do + b = TestVars1.new + val = "foo" + b.x = val + b.x.should == "foo" + val << "bar" + b.x.should == "foobar" + end + + it "multiple instance variables" do + class TestVars2 + def x= v; @x=v; end + def x; @x; end + def y= v; @y=v; end + def y; @y; end + def z= v; @z=v; end + def z; @z; end + end + + a = TestVars2.new + b = TestVars2.new + a.x = "a.x" + a.y = "a.y" + b.y = "b.y" + b.z = "b.z" + + [a.x, a.y, a.z].should == ["a.x", "a.y", nil] + [b.x, b.y, b.z].should == [nil, "b.y", "b.z"] + end +end + +describe "Instance variables are shared by base classes" do + it "derivied class and base use the same field for instance vars" do + class TestBase + def bx= v; @x=v; end + def bx; @x; end + end + class TestDerived < TestBase + def dx= v; @x=v; end + def dx; @x; end + end + + a = TestDerived.new + [a.bx,a.dx].should == [nil,nil] + a.bx = 123 + a.dx.should == 123 + a.bx.should == 123 + a.dx = "test" + a.bx.should == "test" + a.dx.should == "test" + end + + it "test with multiple base classes" do + class TestClass1 + def x1= v; @x=v; end + def x1; @x; end + end + class TestClass2 < TestClass1 + def x2= v; @x=v; end + def x2; @x; end + end + class TestClass3 < TestClass2 + def x3= v; @x=v; end + def x3; @x; end + end + class TestClass4 < TestClass3 + def x4= v; @x=v; end + def x4; @x; end + end + class TestClass5 < TestClass4 + def x5= v; @x=v; end + def x5; @x; end + end + + t = TestClass5.new + t.x5 = 5 + [t.x5,t.x4,t.x3,t.x2,t.x1].each { |i| i.should == 5 } + t.x4 = 4 + [t.x5,t.x4,t.x3,t.x2,t.x1].each { |i| i.should == 4 } + t.x3 = 3 + [t.x5,t.x4,t.x3,t.x2,t.x1].each { |i| i.should == 3 } + t.x2 = 2 + [t.x5,t.x4,t.x3,t.x2,t.x1].each { |i| i.should == 2 } + t.x1 = 1 + [t.x5,t.x4,t.x3,t.x2,t.x1].each { |i| i.should == 1 } + end + +end + +describe "Instance variables are shared by mixins" do + it "mixins and the containing class use the same instance variables" do + module TestMixin1 + def mx= v; @x=v; end + def mx; @x; end + end + class TestDerived < TestBase + include TestMixin1 + end + + a = TestDerived.new + [a.mx, a.bx, a.dx].should == [nil, nil, nil] + a.mx = :foobar + a.bx.should == :foobar + a.dx.should == :foobar + a.mx.should == :foobar + a.bx = 123 + a.dx.should == 123 + a.bx.should == 123 + a.mx.should == 123 + a.dx = "test" + a.bx.should == "test" + a.dx.should == "test" + a.mx.should == "test" + end + + it "the same mixin can be included in multiple classes" do + # if the same mixin is used by multiple classes, + # it always sees instance variables on the current instance. + + class TestDerived2 + include TestMixin1 + def d2x= v; @x=v; end + def d2x; @x; end + end + + a = TestDerived.new + b = TestDerived2.new + a.mx = :A + b.mx = :B + [a.dx,a.mx,b.d2x,b.mx].should == [:A,:A,:B,:B] + end +end + +describe "Instance variables used in modules/functions" do + it "test module level get/set methods" do + def set_instance_var; @iv = 123; end + def get_instance_var; @iv; end + + class Using_module_get_set_functions + def check_iv; get_instance_var; end + def set_iv; set_instance_var; end + end + + x = Using_module_get_set_functions.new + x.check_iv.should == nil + x.set_iv.should == 123 + x.check_iv.should == 123 + x.instance_variables.should == ["@iv"] + x.instance_variable_get(:@iv).should == 123 + end + + it "test module instance variables" do + instance_variables.should == [] + @modvar = {1=>2} + @modvar.should == {1=>2} + instance_variable_get(:@modvar).should == {1=>2} + instance_variables.should == ["@modvar"] + end + + it "test module instance variable is still set in a new scope" do + @modvar.should == {1=>2} + end +end + +describe "Instance variables used in class scope" do + class TestClassScope + @abc = "original" + @foo = "bar" + + def setvars x, y + @abc = x + @foo = y + end + + @abc = "test" + + def self.setvars x, y + @abc << x + @foo << y + end + def self.getvars + [@abc, @foo] + end + end + + it "instance vars in class scope set the var on the class" do + TestClassScope.instance_variables.sort.should == ["@abc", "@foo"] + TestClassScope.instance_variable_get(:@abc).should == "test" + TestClassScope.instance_variable_get(:@foo).should == "bar" + end + + it "instance variables on an instance don't interfere with the class's instance vars" do + t = TestClassScope.new + t.instance_variables.should == [] + t.setvars("test2", "foobar") + t.instance_variables.sort.should == ["@abc", "@foo"] + t.instance_variable_get(:@abc).should == "test2" + t.instance_variable_get(:@foo).should == "foobar" + TestClassScope.instance_variable_get(:@abc).should == "test" + TestClassScope.instance_variable_get(:@foo).should == "bar" + end + + it "class instance vars can be changed in the class method" do + TestClassScope.setvars "1", "2" + TestClassScope.instance_variable_get(:@abc).should == "test1" + TestClassScope.instance_variable_get(:@foo).should == "bar2" + t = TestClassScope.new + t.setvars "&", "*" + TestClassScope.setvars "3", "4" + TestClassScope.getvars.should == [ 'test13', 'bar24' ] + end +end + +describe "dynamic instance variable methods" do + it "basic usage" do + x = Object.new + x.instance_variables.should == [] + x.instance_variable_get(:@iv).should == nil + x.instance_variables.should == [] + x.instance_variable_set(:@iv, "test").should == "test" + x.instance_variables.should == ["@iv"] + x.instance_variable_defined?(:@iv).should == true + x.instance_variable_defined?("@iv").should == true + x.instance_variable_defined?(:@iv2).should == false + x.instance_variable_defined?("@iv2").should == false + x.inspect.split[1].should == '@iv="test">' + x.instance_variable_get(:@iv).should == "test" + x.instance_variable_get("@iv").should == "test" + x.instance_variable_set(:@iv, nil).should == nil + x.instance_variable_defined?(:@iv).should == true + end + + it "bad instance variable names cause a NameError" do + x = Object.new + should_raise(NameError) { x.instance_variable_get :@@iv } + should_raise(NameError) { x.instance_variable_get "@@iv" } + should_raise(NameError) { x.instance_variable_get :iv } + should_raise(NameError) { x.instance_variable_get "iv" } + should_raise(NameError) { x.instance_variable_get "@iv@x" } + should_raise(NameError) { x.instance_variable_set :@@iv, nil } + should_raise(NameError) { x.instance_variable_set "@@iv", nil } + should_raise(NameError) { x.instance_variable_set "@i@v", nil } + should_raise(NameError) { x.instance_variable_set :iv, nil } + should_raise(NameError) { x.instance_variable_defined? :@@iv } + should_raise(NameError) { x.instance_variable_defined? "@@iv" } + should_raise(NameError) { x.instance_variable_defined? "@i@v" } + should_raise(NameError) { x.instance_variable_defined? :iv } + end + + it "test setting instance vars on Fixnums (value equality)" do + 5.instance_variable_get(:@a).should == nil + 5.instance_variable_defined?(:@a).should == false + 5.instance_variables.should == [] + 5.instance_variable_set(:@a, "test").should == "test" + 5.instance_variable_get(:@a).should == "test" + 5.instance_variables.should == ["@a"] + (6-1).instance_variable_set(:@b, [1,2,3,4]).should == [1,2,3,4] + (1+4).instance_variables.sort.should == ["@a", "@b"] + (2+3).instance_variable_get(:@b).should == [1,2,3,4] + + # Ruby Fixnums are in the range 0x3FFFFFFF <= fixnum <= -0x40000000 + # However, in .NET we can use the full 32 bits, e.g. 0x7FFFFFFF <= int32 <= -0x80000000 + # at least test that we can represent the full Ruby Fixnum range + 0x3FFFFFFF.instance_variable_set(:@test, 123).should == 123 + 0x3FFFFFFF.instance_variable_get(:@test).should == 123 + (-0x40000000).instance_variable_set(:@test, 456).should == 456 + (-0x40000000).instance_variable_get(:@test).should == 456 + end + + it "test setting instance vars on Symbols (value equality)" do + :foo.instance_variable_get(:@a).should == nil + :foo.instance_variable_defined?(:@a).should == false + :foo.instance_variables.should == [] + :foo.instance_variable_set(:@a, "test").should == "test" + :foo.instance_variable_get(:@a).should == "test" + :foo.instance_variables.should == ["@a"] + "foo".to_sym.instance_variable_set(:@b, [1,2,3,4]).should == [1,2,3,4] + ("f" + "oo").to_sym.instance_variables.sort.should == ["@a", "@b"] + ("f" + "o" + "o").to_sym.instance_variable_get(:@b).should == [1,2,3,4] + + :@test.instance_variable_set(:@a, "test").should == "test" + "@test".to_sym.instance_variable_get(:@a).should == "test" + "@test".to_sym.instance_variables.should == ["@a"] + end + + it "test instance vars on strings (reference equality)" do + x = "abc" + x.instance_variable_get(:@a).should == nil + x.instance_variable_defined?(:@a).should == false + x.instance_variables.should == [] + x.instance_variable_set(:@a, "test").should == "test" + x.instance_variable_get(:@a).should == "test" + x.instance_variables.should == ["@a"] + y = "abc" + y.instance_variable_set(:@b, [1,2,3,4]).should == [1,2,3,4] + x.instance_variables.should == ["@a"] + y.instance_variables.should == ["@b"] + y.instance_variable_get(:@b).should == [1,2,3,4] + end + + it "test instance vars on nil (value equality)" do + nil.instance_variables.should == [] + nil.instance_variable_set(:@a, "test").should == "test" + nil.instance_variable_get(:@a).should == "test" + nil.instance_variables.should == ["@a"] + end + + it "test instance vars on floats (reference equality)" do + x = 2.0 + x.instance_variable_set(:@a, "test").should == "test" + x.instance_variable_get(:@a).should == "test" + x.instance_variables.should == ["@a"] + y = 2.0 + y.instance_variable_set(:@b, [1,2,3,4]).should == [1,2,3,4] + x.instance_variables.should == ["@a"] + y.instance_variables.should == ["@b"] + y.instance_variable_get(:@b).should == [1,2,3,4] + end + +end + +finished diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_methods.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_methods.rb new file mode 100644 index 0000000000..bff25492a6 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_methods.rb @@ -0,0 +1,69 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# basic usage of methods in class + +class My_methods + def My_methods.sm; 10; end + def im; 20; end +end + +x = My_methods.new + +assert_equal(x.im, 20) +assert_equal(x::im, 20) +assert_equal(My_methods.sm, 10) +assert_equal(My_methods::sm, 10) + +assert_raise(NoMethodError) { x.sm } +assert_raise(NoMethodError) { My_methods.im } + +class My_methods_with_same_name + def My_methods_with_same_name.m; 10; end + def m; 20; end +end + +x = My_methods_with_same_name.new +assert_equal(My_methods_with_same_name.m, 10) +assert_equal(x.m, 20) + + +# override method from mixin + + +# special methods: ==, eql? + +class C600 + def initialize x; @x = x; end + def x; @x; end + def ==(other); @v == other.x; end +end +a, b = C600.new(2), C600.new(2) +hash = {} + +#hash[a] = 3 +#hash[b] = 4 +#assert_equal(hash[a], 3) +class C600 + alias eql? == +end +hash[a] = 3 +hash[b] = 4 + +print hash[a] +print hash[b] +#assert_equal(hash[a], 3) diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_nested_const_vars.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_nested_const_vars.rb new file mode 100644 index 0000000000..b7b1696a03 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_nested_const_vars.rb @@ -0,0 +1,303 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +## +## not covering the constant lookup inside method +## + +## inside a module, defining classes which have the same name as previously defined global class, self + +class C100; + def C100::m1; 1; end +end + +module M100 + def C100::m2; 2; end + + assert_equal(C100::m1, 1) + + class C100 # new nested class + def C100::m3; 3; end + end + def C100::m4; 4; end # adding method to the nested class + class M100::C100; # re-opening the nested class + def C100::m5; 5; end; + def self::m5_1; 51; end; + end + class self::C100; # re-opening the nested class + def C100::m6; 6 end; + def self::m6_1; 61; end + end + + assert_raise(NoMethodError) { C100::m1 } + + class C100::C100 # nested C100 inside the nested class C100 + def C100::m7; 7; end # C100 is M100::C100 + def self::m8; 8; end + end + + class ::C100; # re-opening existing top-level class + def self::m9; 9; end + def C100::m9_1; 91; end + end + + class ::C110; # creating another new top-level class + def self::m10; 10; end + end + + class M100 # new nested class + def M100::m11; 11; end + end + + class M100::M100 # defining nested class M100 inside M100::M100 + def M100::m12; 12; end + def self::m13; 13; end + end + + assert_equal(M100::m12, 12) + assert_equal(M100::M100::m13, 13) +end + +assert_equal(C100::m1, 1) +assert_equal(C100::m2, 2) + +assert_equal(M100::C100::m3, 3) +assert_equal(M100::C100::m4, 4) +assert_equal(M100::C100::m5, 5) +assert_equal(M100::C100::m5_1, 51) +assert_equal(M100::C100::m6, 6) +assert_equal(M100::C100::m6_1, 61) + +assert_equal(M100::C100::m7, 7) +assert_equal(M100::C100::C100::m8, 8) + +assert_equal(C100::m9, 9) +assert_equal(M100::C100::m9_1, 91) + +assert_equal(C110::m10, 10) + +assert_equal(M100::M100::m12, 12) +assert_equal(M100::M100::M100::m13, 13) + +## inside a module, defining a nested module which has the same name as self +module M200 + module M200 + S1 = 1 + end + + assert_raise(NameError, "uninitialized constant M200::M200::M200") { M200::M200::S1 } + + module M200::M200 # + S2 = 2 + end + + module ::M200 + S3 = 3 + end + + module self::M200 + S4 = 4 + end + + assert_equal(M200::S1, 1) + assert_equal(M200::M200::S2, 2) + assert_equal(S3, 3) + assert_equal(M200::S4, 4) + + assert_raise(NameError, "uninitialized constant M200::M200::M200::S1") { M200::M200::S1 } +end + +assert_equal(M200::M200::S1, 1) +assert_equal(M200::M200::M200::S2, 2) +assert_equal(M200::S3, 3) +assert_equal(M200::M200::S4, 4) + +## inside a class, defining classes which have the same name as previously defined global module, or self + +module M300 + S1 = 1 +end + +class C300 + def C300; 301; end + def C300::m1; 1; end + + def M300; 302; end + def M300::m2; 2; end + + class C300 + def C300::m3; 3; end # C300::C300 + def self::m3_1; 31; end + def m3_2; 32; end + end + + class C300::C300 + def C300::m4; 4; end # C300::C300 + def self::m4_1; 41; end # C300::C300::C300 + end + + class C300 + class C300 + def self::m4_2; 42; end + class C300 + def self::m4_3; 43; end + end + def m4_4; 44; end + end + assert_equal(C300::m4_2, 42) + assert_equal(self::C300::m4_2, 42) + assert_equal(C300::C300::m4_3, 43) + assert_equal(::C300::C300::C300::C300::m4_3, 43) + assert_raise(NoMethodError) { C300::C300::C300::C300::m4_3 } # warning: toplevel constant C300 referenced by C300::C300::C300::C300::C300 + + assert_raise(NoMethodError) { C300::m1 } + assert_raise(NoMethodError) { C300::C300::m1 } + assert_equal(C300::C300::C300::m1, 1) # warning + assert_raise(NoMethodError) { C300::C300::C300::C300::m1 } # warning + + assert_equal(C300.new.m4_4, 44) + end + + class ::C300 + def self::m5; 5; end + def C300::m6; 6; end + end + class self::C300 + def self::m7; 7; end + def C300::m8; 8; end + end + + assert_raise(TypeError) { class ::M300; end } + + class M300 + def M300::m9; 9; end + end + + def M300::m10; 10; end +end + +assert_equal(C300::m1, 1) +assert_equal(M300::m2, 2) +assert_equal(M300::S1, 1) +assert_equal(C300.new.C300, 301) +assert_equal(C300.new.M300, 302) + +assert_equal(C300::C300::m3, 3) +assert_equal(C300::C300::m3_1, 31) +assert_raise(NoMethodError) { C300.new.m3_2 } +assert_equal(C300::C300.new.m3_2, 32) + +assert_equal(C300::C300::m4, 4) # different from module +assert_equal(C300::C300::C300::m4_1, 41) +assert_equal(C300::C300::C300.new.m4_4, 44) + +assert_equal(C300::m5, 5) +assert_equal(C300::C300::m6, 6) +assert_equal(C300::C300::m7, 7) +assert_equal(C300::C300::C300::m8, 8) + +assert_equal(C300::M300::m9, 9) +assert_equal(C300::M300::m10, 10) + +## inside a class, defining modules which have the same name as previously defined global module + +module M400 + S1 = 1 +end + +class C400 + module M400 + def M400::m1; 1; end + end + module ::M400 + def self::m1_1; 11; end + end + + def C400::m2; 2; end + + assert_equal(C400::m2, 2) + assert_equal(m2, 2) + + module C400::C400 + def self::m3; 3; end + def C400::m4; 4; end + end + + module self::C400 + def self::m5; 5; end + def C400::m6; 6; end + end + + assert_raise(NoMethodError) { C400::m2 } + assert_equal(m2, 2) + assert_equal(self::m2, 2) + + module C400 + def self::m7; 7; end + def C400::m8; 8; end + end + + assert_raise(NameError) { C400::C400 } + assert_equal(C400::m5, 5) + assert_equal(self::C400::m7, 7) + + module C400 + module C400 + def self::m9; 9; end + end + assert_equal(C400::m9, 9) + assert_equal(self::C400::m9, 9) + assert_raise(NameError) { C400::C400 } + end +end + +assert_equal(C400::M400::m1, 1) +assert_equal(M400::m1_1, 11) +assert_equal(C400::m2, 2) +assert_equal(C400::C400::m3, 3) +assert_equal(C400::C400::m4, 4) +assert_equal(C400::C400::m5, 5) +assert_equal(C400::C400::m6, 6) +assert_equal(C400::C400::m7, 7) +assert_equal(C400::C400::m8, 8) +assert_equal(C400::C400::C400::m9, 9) + + +# +def m1; 1; end + +module M500 + + module M510 + def M500.m2; 2; end + def M510.m3; 3; end + end + + assert_equal(m1, 1) + assert_raise(NoMethodError) { M500::m1 } + + assert_equal(m2, 2) + assert_equal(self::m2, 2) + assert_equal(M500::m2, 2) + + assert_equal(M510::m3, 3) + assert_equal(self::M510::m3, 3) + assert_equal(M500::M510::m3, 3) +end + +assert_equal(M500::m2, 2) +assert_equal(M500::M510::m3, 3) \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_new.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_new.rb new file mode 100644 index 0000000000..4066ed889c --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_new.rb @@ -0,0 +1,86 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# The visibility of methods named initialize is automatically made private +class My_with_explicit_public_initialize + public # even with public "explicitly" here + def initialize + end +end +#assert_raise(NoMethodError) { My_with_explicit_public_initialize.new.initialize } + +class My_with_implicit_public_initialize + def initialize + end +end +#assert_raise(NoMethodError) { My_with_implicit_public_initialize.new.initialize } +assert_raise(ArgumentError) { My_with_implicit_public_initialize.new 1 } + +# self defined "new", now you have no way to instantiate such object +class My_with_new + def My_with_new.new; 90; end +end +assert_equal(My_with_new.new, 90) + +# new with block args +class My_with_initialize + def initialize x, y + @x = x + @y = y + @z = yield + end + def check + [@x, @y, @z] + end +end + +x = My_with_initialize.new(*[1, 2]) { 3 } +assert_equal(x.check, [1, 2, 3]) +assert_raise(ArgumentError) { My_with_initialize.new 1 } + +# call superclass's initialize + +class My_base + def initialize + @x = 10 + @y = 20 + end + def check + [@x, @y, @z] + end +end + +class My_derived_without_super < My_base + def initialize + @y = 30 + @z = 40 + end +end + +x = My_derived_without_super.new +assert_equal(x.check, [nil, 30, 40]) + +class My_derived_with_super < My_base + def initialize + @y = 30 + @z = 40 + super # order! @y is re-assigned. + end +end + +x = My_derived_with_super.new +assert_equal(x.check, [10, 20, 40]) diff --git a/merlin/main/languages/ruby/Tests/Runtime/Class/test_self.rb b/merlin/main/languages/ruby/Tests/Runtime/Class/test_self.rb new file mode 100644 index 0000000000..8fecab9ffc --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Class/test_self.rb @@ -0,0 +1,56 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +assert_equal(self.to_s, "main") +assert_equal(self.class, Object) + +module Simple + assert_equal(self.to_s, "Simple") + assert_equal(self.class, Module) + + class My + assert_equal(self.to_s, "Simple::My") + assert_equal(self.class, Class) + + def My.check + assert_equal(self.to_s, "Simple::My") + assert_equal(self.class, Class) + end + + def check + assert_true { self.to_s.include? "# local_var + assert_isinstanceof($!, RuntimeError) + assert_isinstanceof(local_var, RuntimeError) + end + assert_nil($!) + assert_isinstanceof(local_var, RuntimeError) +end + +def test_life_time_of_manually_created + def no_raise + $! = IOError.new + $g = 1 + begin + empty_func + rescue + $g += 10 + else + $g += 100 + assert_isinstanceof($!, IOError) + ensure + $g += 1000 + assert_isinstanceof($!, IOError) + end + assert_isinstanceof($!, IOError) + end + + begin + no_raise + $g += 10000 + assert_isinstanceof($!, IOError) + rescue + $g += 100000 + ensure + $g += 1000000 + assert_nil($!) + end + assert_nil($!) + + assert_equal($g, 1011101) +end + +test_raised_and_rescued +test_raised_not_rescued +test_raised_in_else_block +test_multiple_exception_in_try_blocks +test_multiple_exception_in_rescue_blocks +test_manually_set +test_saved_to_local_variable + +test_life_time_of_manually_created \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_match.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_match.rb new file mode 100644 index 0000000000..20129fdf03 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_match.rb @@ -0,0 +1,118 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# how the match works + +$g = 0 + +# threequal always return true +class Module + def === other + $g += 10 + true + end +end + +def test_always_match + $g = 1 + begin + divide_by_zero + rescue LoadError + $g += 100 + end + assert_equal($g, 111) +end + +test_always_match + +class Module + def === other + $g += 10 + false + end +end + +# I thought "rescue" will fail, but apparently "===" did not happen +def test_always_not_match_but_rescue_by_nothing + $g = 1 + begin + divide_by_zero + rescue + $g += 100 + end + assert_equal($g, 101) +end + +test_always_not_match_but_rescue_by_nothing + +def test_always_not_match + $g = 1 + begin + divide_by_zero + rescue ZeroDivisionError + $g += 100 # not hit, although this looks be the expected exception + rescue + $g += 1000 + end + assert_equal($g, 1011) +end + +test_always_not_match + + +# the threequal will be called twice +class Module + def === other + $g += 10 + self == ZeroDivisionError + end +end + +def test_match_zero_error + $g = 1 + begin + divide_by_zero + rescue ArgumentError, ZeroDivisionError, TypeError + $g += 100 + end + assert_equal($g, 121) +end + +test_match_zero_error + +# update $! during the === operation +class Module + def === other + $g += 10 + $! = TypeError.new + true + end +end + +def test_match_changing_exception + $g = 1 + begin + divide_by_zero + rescue LoadError + assert_isinstanceof($!, TypeError) + $g += 100 + end + assert_nil($!) + assert_equal($g, 111) +end + +test_match_changing_exception \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_nested.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_nested.rb new file mode 100644 index 0000000000..b407368754 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_nested.rb @@ -0,0 +1,222 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# exception handled by inner rescue (with else) +def test_handled_by_inner_rescue_with_else + $g = 1 + begin + begin + divide_by_zero + rescue ZeroDivisionError + $g += 10 + else + $g += 100 + end + rescue ZeroDivisionError + $g += 1000 + else + $g += 10000 + end + + assert_equal($g, 10011) +end + +# handled by inner rescue (no else) +def test_handled_by_inner_rescue_without_else + $g = 1 + begin + begin + divide_by_zero + rescue ZeroDivisionError + $g += 10 + end + rescue ZeroDivisionError + $g += 1000 + end + + assert_equal($g, 11) +end + +# handled by outer rescue (with else) +def test_handled_by_outer_rescue_with_else + $g = 1 + begin + begin + divide_by_zero + rescue TypeError + $g += 10 + else + $g += 100 + end + rescue ZeroDivisionError + $g += 1000 + else + $g += 10000 + end + + assert_equal($g, 1001) +end + +# handled by outer rescue (no else) +def test_handled_by_outer_rescue_without_else + $g = 1 + begin + begin + divide_by_zero + rescue TypeError + $g += 10 + end + rescue ZeroDivisionError + $g += 1000 + end + + assert_equal($g, 1001) +end + +# exception throw by inner else +def test_raised_by_inner_else + $g = 1 + begin + begin + empty_func + rescue + $g += 10 + else + $g += 100 + raise "string" + end + rescue RuntimeError + $g += 1000 + end + assert_equal($g, 1101) +end + +# both ensure block should be hit +def test_ensure + $g = 1 + begin + begin + divide_by_zero + rescue + $g += 10 + ensure + $g += 100 + end + rescue + $g += 1000 + ensure + $g += 10000 + end + assert_equal($g, 10111) +end + + +def test_raise_inside_ensure + $g = 1 + def m + begin + divide_by_zero + ensure + assert_isinstanceof($!, ZeroDivisionError) + begin + raise IOError.new + ensure + assert_isinstanceof($!, IOError) + end + $g += 10 + end + end + + begin + m + rescue + assert_isinstanceof($!, IOError) + end + + assert_equal($g, 1) +end + +def test_not_raise_inside_ensure + $g = 1 + def m + begin + divide_by_zero + ensure + assert_isinstanceof($!, ZeroDivisionError) + begin + 1 + 1 # valid, not throw + ensure + assert_isinstanceof($!, ZeroDivisionError) + end + + assert_isinstanceof($!, ZeroDivisionError) #!! + $g += 10 + end + end + + begin + m + rescue + assert_isinstanceof($!, ZeroDivisionError) + end + + assert_equal($g, 11) +end + +def test_not_raise_inside_ensure2 + $g = 1 + def m + begin + divide_by_zero + ensure + assert_isinstanceof($!, ZeroDivisionError) + begin + 1 + 1 # valid, not throw + rescue + $g += 10 + else + $g += 100 + ensure + $g += 1000 + assert_isinstanceof($!, ZeroDivisionError) + end + + assert_isinstanceof($!, ZeroDivisionError) + $g += 10000 + end + end + + begin + m + rescue + assert_isinstanceof($!, ZeroDivisionError) + end + + assert_equal($g, 11101) +end + + +test_handled_by_inner_rescue_with_else +test_handled_by_inner_rescue_without_else +test_handled_by_outer_rescue_with_else +test_handled_by_outer_rescue_without_else +test_raised_by_inner_else +test_ensure + +test_raise_inside_ensure +test_not_raise_inside_ensure +test_not_raise_inside_ensure2 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_raise_nothing.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_raise_nothing.rb new file mode 100644 index 0000000000..22aff9d3c1 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_raise_nothing.rb @@ -0,0 +1,58 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# reraise the exception in $! or a new RuntimeError if $! is nil + +require '../../util/assert.rb' +# +assert_equal($!, nil) + +# raise with really nothing +def test_raise_nothing + $g = 1 + begin + raise + rescue Exception + $g += 10 + assert_isinstanceof($!, RuntimeError) + else + $g += 100 + end + assert_equal($g, 11) +end + +# reraise + +def test_reraise + def bad_thing + 1 / 0 + rescue + raise + end + + $g = 1 + begin + bad_thing + rescue + $g += 10 + assert_isinstanceof($!, ZeroDivisionError) + else + $g += 100 + end + assert_equal($g, 11) +end + +test_raise_nothing +test_reraise \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_raise_string.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_raise_string.rb new file mode 100644 index 0000000000..77119c6b5b --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_raise_string.rb @@ -0,0 +1,37 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# raise string - creates a new RuntimeError exception, setting its message to the given string + +require '../../util/assert.rb' + +def bad_thing(s) + raise s +end + +["", "a", "a bc", "\n34\7", "abc \r\ndef gh\r\n\r\n ijk"].each do |s| + $g = 1 + begin + bad_thing s + rescue + $g += 10 + assert_isinstanceof($!, RuntimeError) + assert_equal($!.message, s) + else + $g += 100 + end + assert_equal($g, 11) +end + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_raise_thing.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_raise_thing.rb new file mode 100644 index 0000000000..143ab4eef0 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_raise_thing.rb @@ -0,0 +1,181 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# raise thing [, string [stack trace]] +# +# creates an exception object by invoking the method exception on its first argument +# it then sets this exception's message/backtrace to its second/third arguments +# + +require '../../util/assert.rb' + +# not anything, but exception class/object(?) +def test_raise_nonsense_object + assert_raise(TypeError) { raise nil } + assert_raise(TypeError) { raise 0 } + assert_raise(TypeError) { raise Object } +end + +# raise exception CLASS without string +def test_raise_exception_class + $g = 1 + begin + raise ArgumentError + rescue + $g += 10 + assert_isinstanceof($!, ArgumentError) + #assert_equal($!.message, "ArgumentError") # bug: 293623 + else + $g += 100 + end + assert_equal($g, 11) +end + +# raise exception CLASS with string +def test_raise_exception_class_with_string + $g = 1 + begin + raise ArgumentError, 'the xth argument is invalid' + rescue + $g += 10 + assert_isinstanceof($!, ArgumentError) + assert_equal($!.message, 'the xth argument is invalid') + else + $g += 100 + end + assert_equal($g, 11) +end + +# raise exception OBJECT with string +def test_raise_exception_object_with_string + begin + raise ArgumentError, "try to get the object" + rescue ArgumentError => argError + else + assert_should_have_thrown + end + + assert_not_nil(argError) + + $g = 1 + begin + raise argError, "the thing is an exception object" + rescue + $g += 10 + assert_isinstanceof($!, ArgumentError) + assert_equal($!.message, 'the thing is an exception object') + else + $g += 100 + end + assert_equal($g, 11) + + begin + raise argError + rescue + assert_equal($!.message, 'try to get the object') + end +end + +# raise self-defined exception +class MyError < StandardError +end + +def test_raise_my_exception + $g = 1 + begin + raise MyError + rescue + $g += 10 + assert_isinstanceof($!, MyError) + #assert_equal($!.message, 'MyError') # bug: 293623 + else + $g += 100 + end + assert_equal($g, 11) + + $g = 1 + begin + raise MyError, "special line" + rescue + $g += 10 + assert_isinstanceof($!, MyError) + assert_equal($!.message, 'special line') + else + $g += 100 + end + assert_equal($g, 11) +end + +# raise object that has an exception method, +# 1. which returns not exception +class Foo1 + def exception; "10"; end +end + +# 2. which returns IOError exception +class Foo2 + def exception; $g += 10; IOError.new; end +end + +# 3. which causes exception itself +class Foo3 + def exception; divide_by_zero; end +end + +# 4, which asks for message +class Foo4 + def exception m; IOError.new m; end +end + +def test_raise_object_having_exception_method + $g = 1 + begin; raise Foo1.new; rescue TypeError; $g += 10; end + assert_equal($g, 11) + + $g = 1 + begin; raise Foo2.new; rescue IOError; $g += 100; end + assert_equal($g, 111) + + $g = 1 + begin; raise Foo3.new; rescue ZeroDivisionError; $g += 10; end + assert_equal($g, 11) + + $g = 1 + begin; raise Foo2.new, "message"; rescue ArgumentError; $g += 10; end + assert_equal($g, 11) + + $g = 1 + begin; raise Foo4.new, "message"; rescue IOError; $g += 10; assert_equal($!.message, "message"); end + assert_equal($g, 11) + + $g = 1 + begin; raise Foo4.new; rescue ArgumentError; $g += 10; end + assert_equal($g, 11) + + $g = 1 + begin; raise Foo2.new; rescue ($g+=100; Foo2); $g += 1000; rescue IOError; $g+=10000; end + assert_equal($g, 10111) + + $g = 1 + begin; divide_by_zero; rescue Foo2; rescue ZeroDivisionError; end + assert_equal($g, 1) +end + +test_raise_nonsense_object +test_raise_exception_class +test_raise_exception_class_with_string +test_raise_exception_object_with_string +test_raise_my_exception +test_raise_object_having_exception_method \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_regression.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_regression.rb new file mode 100644 index 0000000000..cbeb3659ef --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_regression.rb @@ -0,0 +1,25 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# could cause peverify failure +begin + 1 +rescue ZeroDivisionError => a +end + +assert_equal(IOError.new.message, "IOError") +assert_equal(IOError.new("anything").message, "anything") \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_many.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_many.rb new file mode 100644 index 0000000000..4a00d677ca --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_many.rb @@ -0,0 +1,214 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# first in the row +def test_first_match + $g = 1 + begin + divide_by_zero + rescue ZeroDivisionError, NameError, RuntimeError + $g += 10 + end + assert_equal($g, 11) +end + +# second in the row +def test_second_match + $g = 1 + begin + divide_by_zero + rescue NameError, ZeroDivisionError, RuntimeError + $g += 10 + end + assert_equal($g, 11) +end + +# last in the row +def test_last_match + $g = 1 + begin + divide_by_zero + rescue NameError, RuntimeError, ZeroDivisionError + $g += 10 + end + assert_equal($g, 11) +end + +# not in the row +def test_no_match + $g = 1 + def f + begin + divide_by_zero + rescue NameError, RuntimeError, TypeError + $g += 10 + end + end + + begin + f + rescue ZeroDivisionError + $g += 100 + end + + assert_equal($g, 101) +end + +# rescued by parent exception +def test_match_to_parent + $g = 1 + begin + divide_by_zero + rescue NameError, StandardError + $g += 10 + end + assert_equal($g, 11) +end + +# exact exception and its parent in the same row. parent first +def test_parent_before_exact_match + $g = 1 + begin + divide_by_zero + rescue NameError, StandardError, ZeroDivisionError + $g += 10 + end + assert_equal($g, 11) +end + +# exact exception and its parent in the same row. parent last +def test_exact_match_before_parent + $g = 1 + begin + divide_by_zero + rescue NameError, ZeroDivisionError, StandardError + $g += 10 + end + assert_equal($g, 11) +end + +# order of the parm get evaluated, and when? + +# rescue does not happen, the parm should not be evaluated. +def test_no_need_rescue + $g = 1 + begin + empty_func + rescue (l = 10, NameError) + $g += 10 + end + assert_equal($g, 1) +# assert_nil(l) +end + +# rescue does happen +def test_parm_evaluate_order + $g = 1 + $g1, $g2, $g3 = 10, 100, 1000 + + begin + divide_by_zero + rescue ($g1 = $g2 + 1; NameError), ($g2 = $g3 + 1; ZeroDivisionError), ($g3 = $g1 + 1; TypeError) + $g += 10 + rescue ($g += 100; StandandError) + $g += 100 + end + assert_equal($g, 11) + assert_equal($g1, 101) + assert_equal($g2, 1001) + assert_equal($g3, 102) # if the 3rd is not evaluated, $g3 would remain 1000 +end + +# multiple rescue clauses +def test_multiple_clauses + $g = 1 + begin + divide_by_zero + rescue NameError, TypeError + $g += 10 + rescue TypeError, ZeroDivisionError # TypeError is repeated + $g += 100 + end + assert_equal($g, 101) +end + +# rescue parm => var! need more +def test_assign_to_var + $g = 1 + new_var = nil + begin + divide_by_zero + rescue TypeError, ZeroDivisionError => new_var + $g += 10 + end + assert_equal($g, 11) + assert_isinstanceof(new_var, ZeroDivisionError) +end + +def test_raise_from_param_without_ensure + $g = 1 + def f + $g += 10 + raise "something" + end + + begin + begin + f + rescue TypeError, f, f + $g += 100 + end + rescue RuntimeError + $g += 1000 + end + assert_equal($g, 1021) +end + +def test_raise_from_param_with_ensure + $g = 1 + def f + $g += 10 + raise "something" + end + + begin + begin + f + rescue TypeError, f, f + $g += 100 + ensure + $g += 1000 + end + rescue RuntimeError + $g += 10000 + end + assert_equal($g, 11021) +end + +test_first_match +test_second_match +test_last_match +test_no_match +test_match_to_parent +test_parent_before_exact_match +test_exact_match_before_parent +test_no_need_rescue +test_parm_evaluate_order +test_multiple_clauses +test_assign_to_var +test_raise_from_param_without_ensure +test_raise_from_param_with_ensure \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_modifier.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_modifier.rb new file mode 100644 index 0000000000..ea551b2c50 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_modifier.rb @@ -0,0 +1,62 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# rescue StandardError and its children + +# no need to rescue +def test_never_rescued + $g = 1 + empty_func rescue $g+=10 rescue $g+=100 + assert_equal($g, 1) +end + +# need rescue, succeed +def test_rescue_succeed + $g = 1 + divide_by_zero rescue $g+=10 + assert_equal($g, 11) +end + +# need rescue, failed: LoadError is not inherited from StandardError +def test_rescue_not_succed + $g = 1 + def f + require "not_existing" rescue $g+=10 + end + begin + f + rescue LoadError + $g+= 100 + end + assert_equal($g, 101) +end + +# multiple rescue modifer +def test_cancat_rescue + $g = 1 + divide_by_zero rescue $g+=10 rescue $g+=100 + assert_equal($g, 11) + + $g = 1 + divide_by_zero rescue $g+=10; x rescue $g+=100 # x is defined nowhere, NameError is expected. + assert_equal($g, 111) +end + +test_never_rescued +test_rescue_succeed +test_rescue_not_succed +test_cancat_rescue \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_nothing.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_nothing.rb new file mode 100644 index 0000000000..b0cb812a6b --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_nothing.rb @@ -0,0 +1,138 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# rescue clause with no paramter is treated as if it had a parameter of StandardError + +# ZeroDivisionError is StandardError +def test_rescue_zeroerror + $g = 1 + begin + divide_by_zero + rescue + $g += 10 + end + assert_equal($g, 11) +end + +# LoadError is NOT StandardError +def test_rescue_loaderror + $g = 1 + def f + begin + require "not_existing" + rescue + $g += 10 + end + end + begin + f + rescue LoadError + $g += 100 + end + assert_equal($g, 101) +end + +# the behavior when such cluase in mutliple rescue sequences + +# empty before ZeroDivisionError +def test_rescued_by_nothing_before_exact_match + $g = 1 + begin + divide_by_zero + rescue + $g += 10 # hit + rescue ZeroDivisionError + $g += 100 + end + assert_equal($g, 11) +end + +# empty after ZeroDivisionError +def test_rescued_by_exact_match_before_nothing + $g = 1 + begin + divide_by_zero + rescue ZeroDivisionError + $g += 10 # hit + rescue + $g += 100 + end + assert_equal($g, 11) +end + +# empty after NameError +def test_rescued_by_nothing_after_no_match + $g = 1 + begin + divide_by_zero + rescue NameError + $g += 10 + rescue + $g += 100 + end + assert_equal($g, 101) +end + +# empty before LoadError +def test_rescued_not_by_nothing_but_by_loaderror + $g = 1 + begin + require "another_not_existing" + rescue + $g += 10 + rescue LoadError + $g += 100 # hit + end + assert_equal($g, 101) +end + +# rescue nothing twice, hit only once +def test_rescued_by_nothing_twice + $g = 1 + begin + divide_by_zero + rescue + $g += 10 # hit + rescue + $g += 100 + end + assert_equal($g, 11) +end + +# rescue nothing but save the ex +def test_save_exception_to_variable + begin + empty_func + rescue => ex # not raise + end + #assert_nil(ex) # bug 269526 + + begin + divide_by_zero + rescue => ex # raised + end + assert_equal(ex.class, ZeroDivisionError) +end + +test_rescue_zeroerror +test_rescue_loaderror +test_rescued_by_nothing_before_exact_match +test_rescued_by_exact_match_before_nothing +test_rescued_by_nothing_after_no_match +test_rescued_not_by_nothing_but_by_loaderror +test_rescued_by_nothing_twice +test_save_exception_to_variable diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_sequence.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_sequence.rb new file mode 100644 index 0000000000..2397c79732 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_sequence.rb @@ -0,0 +1,130 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# first match in the sequence +def test_match_the_first + $g = 1 + begin + divide_by_zero + rescue ZeroDivisionError + $g += 10 + rescue NameError + $g += 100 + rescue TypeError + $g += 1000 + end + assert_equal($g, 11) +end + +# second match in the sequence +def test_match_the_middle + $g = 1 + begin + divide_by_zero + rescue NameError + $g += 10 + rescue ZeroDivisionError + $g += 100 + rescue TypeError + $g += 1000 + end + assert_equal($g, 101) +end + +# last match in the sequence +def test_match_the_last + $g = 1 + begin + divide_by_zero + rescue NameError + $g += 10 + rescue TypeError + $g += 100 + rescue ZeroDivisionError + $g += 1000 + end + assert_equal($g, 1001) +end + +# parent/child relation + +# child before parent in the sequence +def test_match_exact_before_parent + $g = 1 + begin + divide_by_zero + rescue ZeroDivisionError + $g += 10 + rescue TypeError + $g += 100 + rescue StandardError + $g += 1000 + end + assert_equal($g, 11) +end + +# child after parent in the sequence +def test_match_parent_before_exact + $g = 1 + begin + divide_by_zero + rescue StandardError + $g += 10 + rescue TypeError + $g += 100 + rescue ZeroDivisionError + $g += 1000 + end + assert_equal($g, 11) +end + +# unusual cases + +# rescue twice by the exact exception +def test_exact_match_twice + $g = 1 + begin + divide_by_zero + rescue ZeroDivisionError + $g += 10 + rescue ZeroDivisionError + $g += 100 + end + assert_equal($g, 11) +end + +# rescue twice by the parent exception +def test_parent_match_twice + $g = 1 + begin + divide_by_zero + rescue StandardError + $g += 10 + rescue StandardError + $g += 100 + end + assert_equal($g, 11) +end + +test_match_the_first +test_match_the_middle +test_match_the_last +test_match_exact_before_parent +test_match_parent_before_exact +test_exact_match_twice +test_parent_match_twice + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_unusual.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_unusual.rb new file mode 100644 index 0000000000..f12049130f --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_rescue_unusual.rb @@ -0,0 +1,97 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +module MyModule +end + +class MyClass +end + +def rescue_by thing + begin + divide_by_zero + rescue thing + $g += 10 + end +end + +def test_rescue_by_not_class + [1, MyClass.new, nil, [1,2], {1=>2}, "thing" ].each do |x| + $g = 1 + begin + rescue_by x + rescue TypeError + $g += 100 + end + assert_equal($g, 101) + end +end + +def test_rescue_by_class + [MyClass, MyModule, Array, Dir].each do |x| + $g = 1 + begin + rescue_by x + rescue ZeroDivisionError + $g += 100 + end + assert_equal($g, 101) + end + + [Object, Exception, ].each do |x| + $g = 1 + begin + rescue_by Object + rescue + $g += 100 + end + assert_equal($g, 11) + end +end + +def test_exact_match_before_not_class + $g = 1 + begin + divide_by_zero + rescue ZeroDivisionError + $g += 10 + rescue 1 # will not be evaluated + $g += 100 + end + assert_equal($g, 11) +end + +def test_exact_match_after_not_class + $g = 1 + begin + begin + divide_by_zero + rescue 1 + $g += 10 + rescue ZeroDivisionError + $g += 100 + end + rescue TypeError + $g += 1000 + end + assert_equal($g, 1001) +end + +test_rescue_by_not_class +test_rescue_by_class +test_exact_match_before_not_class +test_exact_match_after_not_class \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_retry.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_retry.rb new file mode 100644 index 0000000000..045156ddfa --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_retry.rb @@ -0,0 +1,137 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# the retry statement can be used within a rescue clause to restart the enclosing begin/end block from the beginning + +def test_retry_in_rescue + def my_retry(gate) + $g = 1 + begin + $g += 10 + if $g < gate + divide_by_zero + end + rescue ZeroDivisionError + $g += 100 + retry + $g += 1000 + end + end + + my_retry(5) + assert_equal($g, 11) + + my_retry(12) + assert_equal($g, 121) + + my_retry(122) + assert_equal($g, 231) + + my_retry(232) + assert_equal($g, 341) +end + +# ensure will hit once and only once. +def test_related_to_ensure + $g = 1 + begin + $g += 10 + if $g < 342 + divide_by_zero + end + rescue ZeroDivisionError + $g += 100 + retry + $g += 1000 + ensure + $g += 10000 + end + assert_equal($g, 10451) +end + +# LJE NOT rescuable in the current function + +# inside the "try" block +def test_retry_in_try + $g = 1 + def f + begin + $g += 10 + retry + $g += 10 + rescue LocalJumpError + $g += 100 + end + end + + begin + f + rescue LocalJumpError + $g += 1000 + end + assert_equal($g, 1011) +end + +# inside the "else" block +def test_retry_in_else + $g = 1 + def f + begin + $g += 10 + rescue + $g += 100 + else + $g += 1000 + retry + $g += 1000 + end + end + + begin + f + rescue LocalJumpError + $g += 10000 + end + assert_equal($g, 11011) +end + +# inside the "ensure" block +def test_retry_in_ensure + $g = 1 + def f + begin + $g += 10 + ensure + $g += 100 + retry + $g += 100 + end + end + + begin + f + rescue LocalJumpError + $g += 1000 + end + assert_equal($g, 1111) +end + +test_retry_in_rescue +test_related_to_ensure +test_retry_in_try +test_retry_in_else +test_retry_in_ensure diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_return.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_return.rb new file mode 100644 index 0000000000..826a5b5458 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_return.rb @@ -0,0 +1,187 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +def test_return_from_try + def t + $g = 1 + begin + empty_func + return 10 + $g += 1 + rescue + $g += 10 + else + $g += 100 # won't hit + ensure + $g += 1000 + end + end + assert_equal(t, 10) + assert_equal($g, 1001) +end + +def test_return_from_else + def t + $g = 1 + begin + empty_func + rescue + $g += 10 + else + $g += 100 + return 20 + $g += 100 + ensure + $g += 1000 + end + end + assert_equal(t, 20) + assert_equal($g, 1101) +end + +def test_return_from_ensure_no_exception_raised + def t + $g = 1 + begin + empty_func + ensure + $g += 10 + return 30 + $g += 10 + end + end + assert_equal(t, 30) + assert_equal($g, 11) +end + +def test_return_from_ensure_when_exception_raised + def t + $g = 1 + begin + divide_by_zero + rescue ZeroDivisionError + $g += 10 + ensure + $g += 100 + return 40 + $g += 100 + end + end + assert_equal(t, 40) + assert_equal($g, 111) +end + +# never called return / sanity test codegen +def test_return_from_rescue_but_not_used + def t + $g = 1 + begin + empty_func + rescue ZeroDivisionError + $g += 10 + return 50 + $g += 10 + end + end + assert_nil(t) + assert_equal($g, 1) +end + +def test_return_from_rescue + def t + $g = 1 + begin + divide_by_zero + rescue ZeroDivisionError + $g += 10 + return 60 + $g += 10 + ensure + $g += 100 + end + end + assert_equal(t, 60) + assert_equal($g, 111) +end + +def test_return_from_rescue_after_retry + def t + $g = 1 + begin + $g += 1 + divide_by_zero + rescue ZeroDivisionError + $g += 10 + if $g < 30 + retry + end + return 70 + $g += 10 + ensure + $g += 100 + end + end + assert_equal(t, 70) + assert_equal($g, 134) +end + +def test_return_from_ensure_after_raise_in_begin + def t + $g = 1 + begin + $g += 10 + divide_by_zero + $g += 100 + ensure + $g += 100 + return 80 + end + end + + assert_equal(t, 80) + assert_equal($g, 111) +end + +def test_return_from_ensure_after_raise_in_rescue + def t + $g = 1 + begin + $g += 10 + divide_by_zero + $g += 10 + rescue + $g += 100 + raise "again" + $g += 100 + ensure + return 90 + end + end + + assert_equal(t, 90) # no need to rescue + assert_equal($g, 111) +end + +test_return_from_try +test_return_from_else +test_return_from_ensure_no_exception_raised +test_return_from_ensure_when_exception_raised +test_return_from_rescue_but_not_used +test_return_from_rescue +test_return_from_rescue_after_retry +test_return_from_ensure_after_raise_in_begin +test_return_from_ensure_after_raise_in_rescue \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Exception/test_scope.rb b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_scope.rb new file mode 100644 index 0000000000..7e50d6d9c2 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Exception/test_scope.rb @@ -0,0 +1,75 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# variables defined in each block, whether that block gets executed + +def test_begin_ensure + begin + local_begin = 1 + ensure + local_ensure = 2 + end + + assert_equal(local_begin, 1) + assert_equal(local_ensure, 2) +end + +def test_begin_rescue_else + begin + local_begin = 1 + rescue + local_rescue = 2 + else + local_else = 3 + end + + assert_equal(local_begin, 1) + assert_equal(local_else, 3) + #assert_nil(local_rescue) # bug: 269526 +end + +def test_rescue_not_raised + begin + local_begin = 1 + rescue (local_rescue = 2; RuntimeError) + local_rescue2 = 3 + end + + assert_equal(local_begin, 1) +# assert_nil(local_rescue) # bug: 269526 +# assert_nil(local_rescue2) # bug: 269526 +end + +def test_rescue_raised + begin + 1/0 + rescue IOError => local_ioerror + rescue StandardError => local_stderror + rescue (local_rescue = 2; RuntimeError) + local_rescue2 = 3 + end + +# assert_nil(local_ioerror) # bug: 269526 + assert_not_nil(local_stderror) +# assert_nil(local_rescue) # bug: 269526 +# assert_nil(local_rescue2) # bug: 269526 +end + +test_begin_ensure +test_begin_rescue_else +test_rescue_not_raised +test_rescue_raised \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_assignment.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_assignment.rb new file mode 100644 index 0000000000..5d2a5a317a --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_assignment.rb @@ -0,0 +1,87 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# set the variable or attribute on its left side (the lvalue) to refer to the value +# on the right (the rvalue). + +a = 3 +assert_equal(a, 3) + +a = nil +assert_nil(a) + +a = 4.5 +assert_equal(a, 4.5) + +def f; -10; end +a = f +assert_equal(a, -10) + +a = [] +assert_equal(a, []) + +a = *[] +assert_nil(a) + +a = [5.78] +assert_equal(a, [5.78]) + +a = *[7] +assert_equal(a, 7) + +a = *[8, 9] +assert_equal(a, [8, 9]) + +# multiple rvalues, convert to array +a = 7, 9 +assert_equal(a, [7, 9]) + +a = nil, 8 +assert_equal(a, [nil, 8]) + +a = nil, nil +assert_equal(a, [nil, nil]) + +# It then returns that value as the result of the assignment expression + +a = b = 10 +assert_equal(a, 10) +assert_equal(b, 10) + +a = b = 20 + 30 +assert_equal(a, 50) +assert_equal(b, 50) + +a = (b = 30 + 40) + 50 +assert_equal(a, 120) +assert_equal(b, 70) + +# order! +a = b = 99, 100 +assert_equal(a, [99, 100]) +assert_equal(b, 99) + +a = (b = 101, 102) +assert_equal(a, [101, 102]) +assert_equal(b, [101, 102]) + +s = "hello world" +assert_equal(s, "hello world") + +# object attribute or element reference (todo) +s[0] = 'H' +assert_equal(s, "Hello world") diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_boolean_expr.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_boolean_expr.rb new file mode 100644 index 0000000000..097c750260 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_boolean_expr.rb @@ -0,0 +1,85 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# page: 341 + +require '../../util/assert.rb' + +# globals false/nil are treated being false in a boolean context; all other values are treated as being true +def test_as_if_condition + def being_self(x) + if x then $g = 1 else $g = -1 end + end + def being_self_not(x) + if not x then $g = 1 else $g = -1 end + end + + $g = 0; being_self(nil); assert_equal($g, -1); + $g = 0; being_self(false); assert_equal($g, -1) + + $g = 0; being_self_not(nil); assert_equal($g, 1) + $g = 0; being_self_not(false); assert_equal($g, 1) + + $g = 0; being_self(true); assert_equal($g, 1) + $g = 0; being_self(1); assert_equal($g, 1) + $g = 0; being_self("hello"); assert_equal($g, 1) + $g = 0; being_self(/abc/); assert_equal($g, 1) + $g = 0; being_self(1..3); assert_equal($g, 1) + + $g = 0; being_self_not(true); assert_equal($g, -1) + $g = 0; being_self_not("hello"); assert_equal($g, -1) +end + +# and +def test_and + assert_equal( (true and true), true) + assert_equal( (true and false), false) + assert_equal( (false and true), false) + assert_equal( (false and false), false) + + # 2nd expression is not evaluated if 1st is false + assert_equal( (false and divide_by_zero), false) + + assert_equal( (true and true and false), false) +end + +# or +def test_or + assert_equal( (true or true), true) + assert_equal( (true or false), true) + assert_equal( (false or true), true) + assert_equal( (false or false), false) + + # 2nd expression is not evaluated if 1st is true + assert_equal( (true or divide_by_zero), true) + + assert_equal( (false or false or true), true) +end + +# not +def test_not + assert_equal( (not nil), true) + assert_equal( (not true), false) + assert_equal( (not 12), false) +end + +# todo +# precedence +# defined? + +test_as_if_condition +test_and +test_or +test_not \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_break_in_loop.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_break_in_loop.rb new file mode 100644 index 0000000000..7c9d91fb4d --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_break_in_loop.rb @@ -0,0 +1,96 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# more like unit test, see Compat/test_ctrl_flow for more complete coverage +def test_hit_break + def my_break(gate) + $g = 1 + while true + $g += 10 + if $g > gate + $g += 100 + break + $g += 1000 + end + $g += 10000 + end + end + + my_break(10); assert_equal($g, 111) + my_break(200); assert_equal($g, 10121) + my_break(20000); assert_equal($g, 20131) +end + +# never hit break +def test_break_not_hit + $g = 1 + while $g < 40 + $g += 10 + break if $g >= 50 + $g += 100 + end + assert_equal($g, 111) +end + +# break and next may optionally take one or more arguments +# if used within a block, they are returned as the value of the yield. +# if used within while/until/for, the value given to break is returned, to next is ignored,. + +def test_break_with_arg + x = while true + break + end + assert_equal(x, nil) + + x = while true + break 1 + end + assert_equal(x, 1) + + x = while true + break 2, 3 + end + assert_equal(x, [2, 3]) + + a, b = 1, 2 + x = while true + break a+=b, b+=a + end + assert_equal(x, [3, 5]) +end + +# only break the inner loop +def test_nested_loop + $g = 1 + x = 0 + while x < 5 + x += 1 + $g += 10 + while true + $g += 100 + break + $g += 1000 + end + $g += 10000 + end + assert_equal($g, 50551) +end + +test_hit_break +test_break_not_hit +test_break_with_arg +test_nested_loop diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_case.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_case.rb new file mode 100644 index 0000000000..c74e3dafa1 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_case.rb @@ -0,0 +1,425 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/simple_test.rb' + +def divide_by_zero; 1/0; end + +describe 'case expression with no argument' do + it 'empty case expression' do + def f(v) + case; when v; end + end + + f(false).should == nil + f(true).should == nil + end + + it 'empty case expression again' do + def f(v) + case + when false; 1 + when v + when true; 2 + end + end + f(false).should == 2 + f(true).should == nil + end + + it 'empty case with result' do + def f(v) + case + when v: 10 + end + end + + f(nil).should == nil + f(:ab).should == 10 + end + + it 'parallel cases' do + def f(x, y) + case; when x; $g+=10; 7; end + case; when y; $g+=100; 8; end + end + + $g = 1; f(false, false).should == nil; $g.should == 1 + $g = 1; f(true, false).should == nil; $g.should == 11 + $g = 1; f(false, true).should == 8; $g.should == 101 + $g = 1; f(true, true).should == 8; $g.should == 111 + end + + it 'nested cases' do + def f(x, y) + case; when x: + $g+= 10; 14 + case; when y: + $g += 100; 15 + end + end + end + + $g = 1; f(false, false).should == nil; $g.should == 1 + $g = 1; f(true, false).should == nil; $g.should == 11 + $g = 1; f(false, true).should == nil; $g.should == 1 + $g = 1; f(true, true).should == 15; $g.should == 111 + + def f(x, y, z, w) + case; when x; + $g += 10 + case; when y; $g += 100; end + case; when z; + $g += 1000 + if w; $g += 10000; end + $g += 1000 + end + $g += 10 + end + end + + $g = 1; f(false, false, true, true); $g.should == 1 + $g = 1; f(true, false, false, false); $g.should == 21 + $g = 1; f(true, false, false, true); $g.should == 21 + $g = 1; f(true, false, true, false); $g.should == 2021 + $g = 1; f(true, false, true, true); $g.should == 12021 + $g = 1; f(true, true, false, false); $g.should == 121 + $g = 1; f(true, true, false, true); $g.should == 121 + $g = 1; f(true, true, true, false); $g.should == 2121 + $g = 1; f(true, true, true, true); $g.should == 12121 + end + + it 'case with empty else' do + def f(x) + case when x; 23 + else + end + end + + f(true).should == 23 + f(false).should == nil + end + + it 'case with else' do + def f(x) + case + when x: 27 + else 28 + end + end + + f(true).should == 27 + f(false).should == 28 + end + + it 'case with 2 whens' do + def f(x, y) + case + when x + 33 + when y + 34 + end + end + + f(false, false).should == nil + f(false, true).should == 34 + f(true, false).should == 33 + f(true, true).should == 33 + end + + it 'case with 3 whens' do + def f(x, y, z) + case + when x; 41 + when y; 42 + when z; 43 + end + end + + f(nil, nil, nil).should == nil + f(nil, nil, 111).should == 43 + f(nil, 222, nil).should == 42 + f(nil, 333, 'a').should == 42 + f('b', nil, nil).should == 41 + f(444, nil, 'c').should == 41 + f(555, 'd', nil).should == 41 + f('e', :fg, 1.0).should == 41 + end + + it 'case with when and else' do + def f(x, y) + case + when x; 55 + when y; 56 + else; 57 + end + end + f(false, false).should == 57 + f(false, true).should == 56 + f(true, false).should == 55 + f(true, true).should == 55 + end + + it 'case with exception in test' do + def f + case + when ($g+=10; divide_by_zero; $g+=100) + $g += 1000 + else + $g += 10000 + end + end + $g = 1; begin; f; rescue; $g += 100000; end; $g.should == 100011 + end + + it 'case with expection in body' do + def f(x) + case + when x + $g += 10; divide_by_zero; $g+=100 + else + $g += 1000; divide_by_zero; $g+=10000 + end + end + $g = 1; begin; f true; rescue; $g += 100000; end; $g.should == 100011 + $g = 1; begin; f false; rescue; $g += 100000; end; $g.should == 101001 + end + + it 'how it handles redo/break/next/return' do + def f1; case; when true; next; end; end + def f2; case; when false; else; next; end; end + + def f3; case; when true; redo; end; end + def f4; case; when false; else; redo; end; end + + def f5; case; when true; retry; end; end + def f6; case; when false; else; retry; end; end + + should_raise(LocalJumpError) { f1 } + should_raise(LocalJumpError) { f2 } + should_raise(LocalJumpError) { f3 } + should_raise(LocalJumpError) { f4 } + should_raise(LocalJumpError) { f5 } + should_raise(LocalJumpError) { f6 } + + def f(x); case; when x; break 1; else; break 2; end; end + should_raise(LocalJumpError) { f(true) } + should_raise(LocalJumpError) { f(false) } + + begin + case; when true; return 1; end + rescue LocalJumpError + else + raise + end + + def f(x); case; when x; return 1; else; return 2; end; $g = 1; end + $g = 0; f(true).should == 1; $g.should == 0 + $g = 0; f(false).should == 2; $g.should == 0 + end + + it "stop evaluating multiple conditions after hitting true" do + def f(x, y) + case + when ($g += 1; false): + $g += 10 + when ($g += 100; x), ($g += 1000; y): + $g += 10000 + when ($g += 100000; true): + $g += 1000000 + end + end + $g = 0; f(true, true); $g.should == 10101 + $g = 0; f(true, false); $g.should == 10101 + $g = 0; f(false, true); $g.should == 11101 + $g = 0; f(false, false); $g.should == 1101101 + + def f(x, y) + case + when ($g += 1; x) || ($g += 10; y): $g += 100 + else $g += 1000 + end + end + $g = 0; f(true, true); $g.should == 101 + $g = 0; f(true, false); $g.should == 101 + $g = 0; f(false, true); $g.should == 111 + $g = 0; f(false, false); $g.should == 1011 + end + + it "allows then|: after when" do + def f(x) + case + when x then 1 + when (not x): 2 + end + end + f(true).should == 1 + f(false).should == 2 + end + +end + +describe 'case with an argument' do + it 'case calls ===' do + class Foo + @@foo = 0 + def === x + @@foo += 1 + false + end + def self.count + @@foo + end + end + + class Foo2 + @@foo2 = 0 + def === x + @@foo2 += 1 + false + end + def self.count + @@foo2 + end + end + + [Foo.count, Foo2.count].should == [0, 0] + x = (case Foo.new; when Foo2.new; end) + [Foo.count, Foo2.count].should == [0, 1] + y = (case Foo2.new; when Foo.new; end) + [Foo.count, Foo2.count].should == [1, 1] + y = (case Foo2.new; when Foo.new; end) + [Foo.count, Foo2.count].should == [2, 1] + x = (case Foo.new; when Foo2.new; end) + [Foo.count, Foo2.count].should == [2, 2] + end + + it 'throws when === is not defined' do +# class Foo3 +# undef === +# end +# x = 1 +# y = Foo3.new +# should_raise(NoMethodError) do +# case x; +# when y; 10 +# else; 20 +# end +# end +# +# z = case y; +# when x; 10 +# else; 20 +# end +# z.should == 20 + end + + it 'allows the expression to be literal' do + def f(x) + case 1; + when x; 10 + else; 20 + end + end + f(1).should == 10 + f(2).should == 20 + + def f(x) + case 'abc' + when x; 10 + else; 20 + end + end + f('bcd').should == 20 + f('abc').should == 10 + end + + it 'basic case usage' do + def foo(w, x=nil, y=nil, z=nil) + case w + when 123: :x123 + when 456: :x456 + when x: x + when 789, *y: :x789 + when *z: z + else + "unknown" + end + end + + class Bob + def === x + x === 'bob' + end + end + + foo(123).should == :x123 + foo(456).should == :x456 + foo(789).should == :x789 + foo(555).should == 'unknown' + foo(nil).should == nil + foo('bob', 'bob').should == 'bob' + b = Bob.new + foo(b, 'bob').should == 'unknown' + foo('bob', b).should == b + b2 = Bob.new + foo(b, b2).should == b2 + foo(1, nil, nil, [3, 2, 1, 4]).should == [3,2,1,4] + foo(2, nil, [3, 2, 1, 4], nil).should == :x789 + end + + it 'case value is only evaluated once' do + def foo x + $x += 1 + x + end + $x = 0 + def bar(x, y=nil) + case foo(x) + when 1: 1 + when 2: 2 + when 3: 3 + when 4: 4 + when 5, *[6,7,8,9]: 5 + when y: y + end + end + bar(3); $x.should == 1 + bar(7); $x.should == 2 + bar(9, 9); $x.should == 3 + bar('a'); $x.should == 4 + end + + it 'stops evaluating after finding one equality, etc' do + def f(x) + case x + when ($g += 1; false): $g += 1 + when ($g += 10; 10), ($g+= 100; 11) + $g += 1000 + when ($g += 10000; true) then $g += 100000 + else + $g += 1000000 + end + end + $g = 0; f(10); $g.should == 1011 + $g = 0; f(11); $g.should == 1111 + $g = 0; f(true); $g.should == 110111 + $g = 0; f(1); $g.should == 1010111 + end +end + +finished + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_for_loop.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_for_loop.rb new file mode 100644 index 0000000000..a1a331f9d6 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_for_loop.rb @@ -0,0 +1,80 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +def test_not_iterator + assert_raise(NoMethodError) { for var1 in 1 do end } +end + +def test_basic + $g = 0 + for var1 in [1] + $g += var1 + end + assert_equal($g, 1) + + $g = 0 + for var1 in [2, 3] + $g += var1 + end + assert_equal($g, 5) +end + +def test_assign + c = d = 0 + for var1, var2 in [5, 6] + c += var1 + assert_nil(var2) + end + assert_equal(c, 11) + + e = f = 0 + for var1, var2 in [ 7, [8, 9], [10, 11, 12] ] + e += var1 + if var1 == 7 + assert_nil(var2) + else + f += var2 + end + end + assert_equal(e, 25) + assert_equal(f, 20) +end + +# the local variables defined in the body of the for loop will be available outside the loop +def test_local_variable + for var1 in [1, 2] + var3 = 100 + end + assert_equal(var3, 100) +end + +def test_expr + expr = [1, 10, 100] + $g = 0 + for var1 in expr + $g += var1 + expr = [1000, 10000] # no impact + end + assert_equal($g, 111) + assert_equal(expr, [1000, 10000]) +end + +test_not_iterator +test_basic +test_assign +test_local_variable +test_expr \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_if.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_if.rb new file mode 100644 index 0000000000..80fa92b055 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_if.rb @@ -0,0 +1,189 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +require '../../util/assert.rb' + +def test_empty_if + def f(v) + if v; + end + end + + assert_equal(f(false), nil); + assert_equal(f(true), nil); +end + +def test_only_one_if + def f(v) + if v; 10; end + end + + assert_equal(f(false), nil); + assert_equal(f(true), 10); +end + +def test_parallel_ifs + def f(x, y) + if x; $g+=10; 7; end + if y; $g+=100; 8; end + end + + $g = 1; assert_equal(f(false, false), nil); assert_equal($g, 1) + $g = 1; assert_equal(f(true, false), nil); assert_equal($g, 11) + $g = 1; assert_equal(f(false, true), 8); assert_equal($g, 101) + $g = 1; assert_equal(f(true, true), 8); assert_equal($g, 111) +end + +def test_nested_ifs + def f(x, y) + if x; + $g+= 10; 14 + if y: + $g += 100; 15 + end + end + end + + $g = 1; assert_equal(f(false, false), nil); assert_equal($g, 1) + $g = 1; assert_equal(f(true, false), nil); assert_equal($g, 11) + $g = 1; assert_equal(f(false, true), nil); assert_equal($g, 1) + $g = 1; assert_equal(f(true, true), 15); assert_equal($g, 111) + + def f(x, y, z, w) + if x; + $g += 10 + if y; $g += 100; end + if z; + $g += 1000 + if w; $g += 10000; end + $g += 1000 + end + $g += 10 + end + end + + $g = 1; f(false, false, true, true); assert_equal($g, 1) + $g = 1; f(true, false, false, false); assert_equal($g, 21) + $g = 1; f(true, false, false, true); assert_equal($g, 21) + $g = 1; f(true, false, true, false); assert_equal($g, 2021) + $g = 1; f(true, false, true, true); assert_equal($g, 12021) + $g = 1; f(true, true, false, false); assert_equal($g, 121) + $g = 1; f(true, true, false, true); assert_equal($g, 121) + $g = 1; f(true, true, true, false); assert_equal($g, 2121) + $g = 1; f(true, true, true, true); assert_equal($g, 12121) +end + +def test_empty_else + def f(x) + if x; 23 + else + end + end + + assert_equal(f(true), 23) + assert_equal(f(false), nil) +end + +def test_if_else_only + def f(x) + if x; + 27 + else; + 28 + end + end + + assert_equal(f(true), 27) + assert_equal(f(false), 28) +end + +def test_one_elsif + def f(x, y) + if x + 33 + elsif y + 34 + end + end + + assert_equal(f(false, false), nil) + assert_equal(f(false, true), 34) + assert_equal(f(true, false), 33) + assert_equal(f(true, true), 33) +end + +def test_two_elsif + def f(x, y, z) + if x; 41 + elsif y; 42 + elsif z; 43 + end + end + + assert_equal(f(false, false, false), nil) + assert_equal(f(false, false, true), 43) + assert_equal(f(false, true, false), 42) + assert_equal(f(false, true, true), 42) + assert_equal(f(true, false, false), 41) + assert_equal(f(true, false, true), 41) + assert_equal(f(true, true, false), 41) + assert_equal(f(true, true, true), 41) +end + +def test_elsif_with_else + def f(x, y) + if x; 55 + elsif y; 56 + else; 57 + end + end + assert_equal(f(false, false), 57) + assert_equal(f(false, true), 56) + assert_equal(f(true, false), 55) + assert_equal(f(true, true), 55) +end + +def test_exception + def f + if ($g+=10; divide_by_zero; $g+=100) + $g += 1000 + else + $g += 10000 + end + end + $g = 1; begin; f; rescue; $g += 100000; end; assert_equal($g, 100011) + + def f(x) + if x + $g += 10; divide_by_zero; $g+=100 + else + $g += 1000; divide_by_zero; $g+=10000 + end + end + $g = 1; begin; f true; rescue; $g += 100000; end; assert_equal($g, 100011) + $g = 1; begin; f false; rescue; $g += 100000; end; assert_equal($g, 101001) +end + +test_empty_if +test_only_one_if +test_parallel_ifs +test_nested_ifs +test_empty_else +test_if_else_only +test_one_elsif +test_two_elsif +test_elsif_with_else +test_exception \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_nested_assignment.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_nested_assignment.rb new file mode 100644 index 0000000000..6cb8237d84 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_nested_assignment.rb @@ -0,0 +1,24 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +require '../../util/assert.rb' + +# left side of assignment may contain a parenthesized list of terms. +# it extracts the corresponding rvalue, assigning it to the parenthesized terms, before continuing +# with higher-level assignment. + +# (a) = 3: syntax error + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_next_in_loop.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_next_in_loop.rb new file mode 100644 index 0000000000..ab93bb2816 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_next_in_loop.rb @@ -0,0 +1,103 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +def test_always_next + $x = $y = 0 + bools = [true, true, true, false] + while bools[$x] + $x += 1 + next # always next + $y += 1 + end + assert_equal($x, 3) + assert_equal($y, 0) +end + +def test_next_in_the_middle + $x = $y = 0 + bools = [true, true, true, false] + while bools[$x] + $x += 1 + next if $x == 2 # next in the middle loop + $y += 1 + end + assert_equal($x, 3) + assert_equal($y, 2) +end + +def test_next_at_last + $x = $y = 0 + bools = [true, true, true, false] + while bools[$x] + $x += 1 + next if $x == 3 # next at the last loop + $y += 1 + end + assert_equal($x, 3) + assert_equal($y, 2) +end + +# "next" may take optional arguments + +# the values are ignored +def test_next_with_arg + $x = 0 + $y = while [true, false][$x] + $x += 1 + next 1, 2 + end + assert_equal($x, 1) + assert_equal($y, nil) +end + +# whether they do get evaluated +def test_evaluate_args + $x = 0 + a, b = 1, 2 + while [true, false][$x] + $x += 1 + next a += b, b += a + end + assert_equal(a, 3) + assert_equal(b, 5) +end + +# should next to the inner loop +def test_nested_loop + $x = $y = 0 + $g = 1 + while [true, true, false][$x] + $x += 1 + $g += 10 + while [true, false][$y] + $y += 1 + $g += 100 + next + $g += 1000 + end + $g += 10000 + end + + assert_equal($g, 20121) +end + +test_always_next +test_next_in_the_middle +test_next_at_last +test_next_with_arg +test_evaluate_args +test_nested_loop \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_parallel_assignment.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_parallel_assignment.rb new file mode 100644 index 0000000000..4f366c30cd --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_parallel_assignment.rb @@ -0,0 +1,215 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +def test_basic + a, b = 4, 5 + assert_equal(a, 4) + assert_equal(b, 5) +end + +def test_swap + a, b = 4, 5 + # swap + a, b = b, a + assert_equal(a, 5) + assert_equal(b, 4) +end + +# the values on the right side are evaluated in the order in which they apprear before any assignment +# is made to variables or attributes on the left + +def test_eval_order + x = 1 + a, b = x, x+=2 + assert_equal(a, 1) + assert_equal(b, 3) + + a, b = x+=2, x + assert_equal(a, 5) + assert_equal(b, 5) +end + +# same variable +def test_assign_to_same_var + a, a = 4, 7 + assert_equal(a, 7) + + a = 10 + a, a = 6, a+9 + assert_equal(a, 19) +end + +# when an assignment has more than one lvalue, the assignment expression returns an array of the rvalues + +def test_unmatch + # if an assignment contains more lvalues than rvalues, the excess lvalues are set to nil + a, b, c = 12 + assert_equal(a, 12) + assert_nil(b) + assert_nil(c) + + # if the assignment contains more rvalues than lvalues, the extra rvalues are ignored. + + a, b = 3, 4, 5 + assert_equal(a, 3) + assert_equal(b, 4) +end + +# if an assignment has just one lvalue and multiple rvalues, the rvalues are converted to an array and +# assigned to the lvalue: see Tests\Runtime\Expression\test_assignment.rb. + +# +# collapse/expand arrays +# +# if the last lvalue is preceded by *, all the remaining rvalues will be collected and assigned to that +# lvalue as an array. + +def test_left_star + x = [3, 5, 7, 9] + + *b = x + assert_equal(b, [[3, 5, 7, 9]]) + + a, *b = x + assert_equal(a, 3) + assert_equal(b, [5, 7, 9]) + + *b = x, 20 + assert_equal(b, [[3, 5, 7, 9], 20]) + + a, *b = x, 20 + assert_equal(a, [3, 5, 7, 9]) + assert_equal(b, [20]) + + *b = 30, x + assert_equal(b, [30, [3, 5, 7, 9]]) + + a, *b = 30, x + assert_equal(a, 30) + assert_equal(b, [[3, 5, 7, 9]]) +end + +# similarly, if the last rvalue is an array, you can prefix it with *, which effectively expands it into +# its constituent values in place. +def test_right_star + x = [3, 5, 7, 9] + + b = *x + assert_equal(b, [3, 5, 7, 9]) + + a, b = *x + assert_equal(a, 3) + assert_equal(b, 5) + + b = 40, *x + assert_equal(b, [40, 3, 5, 7, 9]) + + a, b = 40, *x + assert_equal(a, 40) + assert_equal(b, 3) +end + +## a, b = *x, 40, syntax error + +# both last rvalue/lvalue have * +def test_both_side_star + x = [3, 5, 7, 9] + + *b = *x + assert_equal(b, [3, 5, 7, 9]) + + *b = 40, *x + assert_equal(b, [40, 3, 5, 7, 9]) + + a, *b = *x + assert_equal(a, 3) + assert_equal(b, [5, 7, 9]) + + a, *b = 50, *x + assert_equal(a, 50) + assert_equal(b, [3, 5, 7, 9]) + + # this (having * ahead of rvalue) is not necessary if the rvalue is the only thing in the right side. + + a, b = x + assert_equal(a, 3) + assert_equal(b, 5) +end + +def test_eval_order2 + def f + $g += 10 + $a + end + + # simple assignment + $g = 1 + $x = $y = $z = 0 + $a = [1, 2, 3] + + f[begin $g+=100; $x = $g; 0 end] = ($g+=1000; $y = $g; 5) + assert_equal($a, [5, 2, 3]) + assert_equal($x, 111) + assert_equal($y, 1111) + + # a,b = 1 + $g = 1 + $x = $y = $z = 0 + $a = [1, 2, 3] + f[begin $g+=100; $x = $g; 0 end], f[begin $g+=1000; $y = $g; 1 end] = ($g+=10000; $z = $g; 5) + + assert_equal($a, [5, nil, 3]) + assert_equal($z, 10001) # z -> x -> y + assert_equal($x, 10111) + assert_equal($y, 11121) + + # (a,) = 1 + $g = 1 + $x = $y = $z = 0 + $a = [1, 2, 3] + (f[begin $g+=100; $x = $g; 0 end],) = ($g+=1000; $y = $g; 5) + assert_equal($a, [5, 2, 3]) + assert_equal($y, 1001) + assert_equal($x, 1111) +end + +def test_exception + # throw while eval'ing right side + $x = $y = $z = $w = 0 + begin $x, $y, $z = 1, divide_by_zero, 3; rescue; $w=1; end + assert_equal([$x, $y, $z, $w], [0, 0, 0, 1]) + + # throw while eval'ing left side + $x = $y = $z = 0 + def f; divide_by_zero; end + begin $x, f[2], $y = 1, 2, 3; rescue; $z =1; end + assert_equal([$x, $y, $z], [1, 0, 1]) + + $x, f, $y = 4, 5, 6 # f is variable + assert_equal(f, 5) +end + +test_basic +test_swap +test_eval_order +test_assign_to_same_var +test_unmatch +test_left_star +test_right_star +test_both_side_star +test_eval_order2 +test_exception diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_range_as_bool.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_range_as_bool.rb new file mode 100644 index 0000000000..ec338eb047 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_range_as_bool.rb @@ -0,0 +1,21 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +require '../../util/assert.rb' + +assert_raise(ArgumentError) { true..false } +assert_raise(ArgumentError) { false..false } + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_redo_in_loop.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_redo_in_loop.rb new file mode 100644 index 0000000000..07946618db --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_redo_in_loop.rb @@ -0,0 +1,61 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +def test_redo_at_first + $g = x = y = 0 + bools = [true, false, false, false] # if bools[x] is re-evaluated during the redo, it is "false" + while bools[x] + $g += 7 + x += 1 + redo if $g < 15 # redo at the first loop + y += 1 + end + assert_equal($g, 21) + assert_equal(x, 3) + assert_equal(y, 1) +end + +def test_redo_at_second + $g = x = y = 0 + bools = [true, true, false, true, false] + while bools[x] + $g += 8 + x += 1 + redo if $g > 9 and $g < 17 # redo at the second loop + y += 1 + end + assert_equal($g, 32) + assert_equal(x, 4) + assert_equal(y, 3) +end + +def test_redo_at_interval + $g = x = y = 0 + bools = [true, false, true, false, true, false] + while bools[x] + $g += 6 + x += 1 + redo if $g < 11 or ($g > 17 and $g < 23) # redo at the first loop, not redo, and redo again + y += 1 + end + assert_equal($g, 30) + assert_equal(x, 5) +end + +test_redo_at_first +test_redo_at_second +test_redo_at_interval \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_retry_in_loop.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_retry_in_loop.rb new file mode 100644 index 0000000000..c80db59620 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_retry_in_loop.rb @@ -0,0 +1,91 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# restarts the loop, and reevaluating the condition ?? + +def test_jump_error_happen + def retry_inside_while + $c = 0 + while $c < 7 + $c += 1 + retry if $c < 2 + end + end + + assert_raise(LocalJumpError) { retry_inside_while } + assert_equal($c, 1) + + def retry_inside_until + $c = 0 + until $c > 7 + $c += 2 + retry if $c < 4 + end + end + + assert_raise(LocalJumpError) { retry_inside_until } + assert_equal($c, 2) +end + +def test_jump_error_not_happen + def retry_inside_while + $c = 0 + while $c < 7 + $c += 1 + retry if false + end + end + retry_inside_while + + def retry_inside_until + $c = 0 + until $c > 7 + $c += 2 + retry if false + end + end + retry_inside_until +end + +def test_retry_inside_for_loop + expr = [ [1, 10], [100, 1000, 10000], [100000] ] + + $sum = 0 + $c = 0 + for x in expr[$c] # will not be evaluated again + $sum += x + $c += 1 + $sum += 1000000 + end + + assert_equal($sum, 2000011) + + $sum = 0 + $c = 0 + for x in expr[$c] # will be evaluated again, the 10 in the first array will be skipped + $sum += x + $c += 1 + retry if $c == 1 + $sum += 1000000 + end + + assert_equal($sum, 3011101) +end + +test_jump_error_happen +test_jump_error_not_happen +test_retry_inside_for_loop \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Expression/test_while_until.rb b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_while_until.rb new file mode 100644 index 0000000000..646c7dbbeb --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Expression/test_while_until.rb @@ -0,0 +1,115 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# page 344 + +require '../../util/assert.rb' + +# executes body zero or more times as long as bool-expression is true (for while), false (for until) + +def test_zero_times + g = 1 + x = while false + g += 10 + end + assert_equal(g, 1) + assert_nil(x) + + y = until true + g += 100 + end + assert_equal(g, 1) + assert_nil(y) +end + +def test_more_times + x = true + g = 0 + a = while x + g += 1 + if g > 2 + x = false + end + end + assert_equal(g, 3) + assert_nil(a) + + x = false + g = 0 + b = until x + g += 1 + if g > 5 + x = true + end + end + assert_equal(g, 6) + assert_nil(b) +end + +# while and until modifier +def test_modifer + # bad thing won't happen + divide_by_zero while false + divide_by_zero until true + + # + x = 0 + x += 1 while x > 5 + assert_equal(x, 0) + x = 0 + x += 1 while x < 5 + assert_equal(x, 5) + + x = 0 + x += 1 until x > 5 + assert_equal(x, 6) + x = 0 + x += 1 until x < 5 + assert_equal(x, 0) +end + +def test_evaluate_condition + g = c = 1 + x = true + while (g+=10; x) + c += 1 + x = c < 3 + end + assert_equal(g, 31) + + g = c = 1 + x = false + until (g+=10; x) + c += 1 + x = c > 3 + end + assert_equal(g, 41) +end + +# TODO +def test_expr_result + x = while true; return 3; end + assert_equal(x, 3) + x = while true; break 4; end + assert_equal(x, 4) + + +end + +test_zero_times +test_more_times +test_modifer +test_evaluate_condition +test_expr_result diff --git a/merlin/main/languages/ruby/Tests/Runtime/Method/test_coverage.rb b/merlin/main/languages/ruby/Tests/Runtime/Method/test_coverage.rb new file mode 100644 index 0000000000..afcb0c9863 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Method/test_coverage.rb @@ -0,0 +1,50 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# all args +def test_normal_arg + def m0; return; end + def m1 a; return a; end + def m2 a,b; return a, b; end + def m3 a,b,c; return a, b, c; end + def m4 a,b,c,d; return a, b, c, d; end + def m5 a,b,c,d,e; return a, b, c, d, e; end + def m6 a,b,c,d,e,f; return a, b, c, d, e, f; end + def m7 a,b,c,d,e,f,g; return a, b, c, d, e, f, g; end + def m8 a,b,c,d,e,f,g,h; return a, b, c, d, e, f, g, h; end + def m9 a,b,c,d,e,f,g,h,i; return a, b, c, d, e, f, g, h, i; end + + m9 1,2,4,5,6,7,8,9 +end + + +def test_defaultvalue_arg + def m3 a,b,c; return a, b, c; end + def m3 a,b,c=1; return a, b, c; end + def m3 a,b=1,c=2; return a, b, c; end + def m3 a=1,b=2,c=3; return a, b, c; end + + def m8 a,b,c,d,e,f,g,h; return a, b, c, d, e, f, g, h; end + def m8 a,b,c,d,e,f,g,h=1; return a, b, c, d, e, f, g, h; end + def m8 a,b,c,d,e,f,g=1,h=2; return a, b, c, d, e, f, g, h; end + def m8 a,b,c,d,e,f=1,g=2,h=3; return a, b, c, d, e, f, g, h; end + def m8 a,b,c,d,e=1,f=2,g=3,h=4; return a, b, c, d, e, f, g, h; end + def m8 a,b,c,d=1,e=2,f=3,g=4,h=5; return a, b, c, d, e, f, g, h; end + def m8 a,b,c=1,d=2,e=3,f=4,g=5,h=6; return a, b, c, d, e, f, g, h; end + def m8 a,b=1,c=2,d=3,e=4,f=5,g=6,h=7; return a, b, c, d, e, f, g, h; end + def m8 a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8; return a, b, c, d, e, f, g, h; end +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Method/test_defaultvalue.rb b/merlin/main/languages/ruby/Tests/Runtime/Method/test_defaultvalue.rb new file mode 100644 index 0000000000..5f0234b7be --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Method/test_defaultvalue.rb @@ -0,0 +1,185 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +def test_1_defaultvalue + def m(arg=7) + arg + end + + assert_return(7) { m } + assert_return(9) { m 9 } + assert_return(7) { m 7 } + assert_return("abc") { m "abc" } + assert_raise(ArgumentError) { m 7, 8 } + + assert_return(7) { m *[]} + assert_return(17) { m *[17]} +end + +def test_1_normal_1_default + def m(arg1, arg2=10) + [arg1, arg2] + end + + assert_raise(ArgumentError) { m } + assert_return([1, 10]) { m 1 } + assert_return([2, 3]) { m 2, 3 } + assert_raise(ArgumentError) { m 4, 5, 6 } + + assert_raise(ArgumentError) { m *[]} + assert_return([17, 10]) { m *[17]} +end + +def test_2_defaultvalues + def m(arg1=20, arg2=30) + [arg1, arg2] + end + + assert_return([20, 30]) { m } + assert_return(["a", 30]) { m "a" } + assert_return([3, 5]) { m 3, 5 } + assert_return([nil, nil]) { m nil, nil } + assert_raise(ArgumentError) { m 4, 5, 6 } + + assert_return([20, 30]) { m *[]} + assert_return(["a", 30]) { m *["a"]} + assert_return(["b", "c"]) { m "b", *["c"]} +end + +# what can be the default value +# when the default value is evaluated +# order of how default value is evaluated + +def test_local_var_as_defaultvalue + local_var = 100 + def m(arg = local_var) + arg + end + assert_raise(NameError) { m } # undefined local variable or method `local_var' for main:Object (NameError) + assert_return(120) { m 120 } + + local_var = 150 + assert_raise(NameError) { m } +end + +def test_global_var_as_defaultvalue + $global_var = 200 + def m(arg = $global_var) + arg + end + assert_return(200) { m } + + $global_var = 220 + assert_return(220) { m } + + $global_var = 300 + def m(arg1 = ($global_var+=1), arg2 = ($global_var+=11)) + return arg1, arg2 + end + assert_return([301, 312]) { m } +end + +def test_expression_as_defaultvalue + def m(arg1 = 400, arg2 = (arg1+=1; arg1 + 10)) # first arg changed after assign... + [arg1, arg2] + end + assert_return([401, 411]) { m } ## !!! + assert_return([501, 511]) { m 500 } + assert_return([600, 700]) { m 600, 700 } +end + +def test_use_second_arg_to_get_first_arg_default_value + def m(arg1 = arg2 + 1, arg2 = 100) + [arg1, arg2] + end + assert_raise(NameError) { m } +end + +def test_new_defined_local_var_by_defaultvalue + def m(a, b=(c = 10; 20), d=(e=b+30; e+40)) + return a, b, c, d, e + end + + assert_return([0, 20, 10, 90, 50]) { m 0 } + assert_return([1, 2, nil, 72, 32]) { m 1, 2 } + assert_return([3, 4, nil, 5, nil]) { m 3, 4, 5 } +end + +def test_array_as_defaultvalue + def m(a, b=(c=20, 30)) # comma + return a, b, c + end + + assert_return([10, [20, 30], [20, 30]]) { m 10 } + assert_return([40, 50, nil]) { m 40, 50 } +end + +def test_exception + $g = 1 + def m(a, b= begin; if a == 1; raise "msg"; end; end) + $g += 10 + rescue + $g += 100 + end + + m 2 + assert_equal($g, 11) + + $g = 1 + begin + m 1 + rescue + $g += 1000 + end + assert_equal($g, 1001) +end + +def test_statement + $g = 1 + def m(a, b=(def m; $g += 10; end; m)) + $g += 100 + end + m 7 + assert_equal($g, 111) +end + +def test_evaluate_during_invocation + $g = 1 + def m(a, b=($g += 10)) + $g += 100 + end + + m 1 + assert_equal($g, 111) + m 2 + assert_equal($g, 221) + m 1, 2 + assert_equal($g, 321) +end + +test_1_defaultvalue +test_1_normal_1_default +test_2_defaultvalues +#test_local_var_as_defaultvalue +test_global_var_as_defaultvalue +test_expression_as_defaultvalue +#test_use_second_arg_to_get_first_arg_default_value +#test_new_defined_local_var_by_defaultvalue +#test_array_as_defaultvalue +test_exception +test_statement +test_evaluate_during_invocation \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Method/test_mixed_arg.rb b/merlin/main/languages/ruby/Tests/Runtime/Method/test_mixed_arg.rb new file mode 100644 index 0000000000..da096f20bd --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Method/test_mixed_arg.rb @@ -0,0 +1,47 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# argument contains default value, and var-arg list + +require '../../util/assert.rb' + +def test_defaultvalue_vararg + def m(arg1=10, *arg2) + [arg1, arg2] + end + + assert_return([10, []]) { m } + assert_return([1, []]) { m 1 } + assert_return([2, [3]]) { m 2, 3 } + assert_return([4, [5, 6]]) { m 4, 5, 6 } + assert_return([7, [8]]) { m *[7, 8] } +end + +def test_normal_defaultvalue_vararg + def m(arg1, arg2=10, *arg3) + [arg1, arg2, arg3] + end + + assert_raise(ArgumentError) { m } + assert_return([1, 10, []]) { m 1 } + assert_return([2, 3, []]) { m 2, 3 } + assert_return([4, 5, [6]]) { m 4, 5, 6 } + assert_return([7, 8, [9, 10]]) { m 7, 8, 9, 10 } + assert_return([11, 12, [13, 14, 15]]) { m *[11, 12, 13, 14, 15] } +end + + +test_defaultvalue_vararg +test_normal_defaultvalue_vararg \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Method/test_normal_arg.rb b/merlin/main/languages/ruby/Tests/Runtime/Method/test_normal_arg.rb new file mode 100644 index 0000000000..72bd5266a7 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Method/test_normal_arg.rb @@ -0,0 +1,85 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# no arg required +def test_0_arg + def m + (9) # parenthesis around 9 - ok + end + + assert_return(9) { m } + assert_raise(ArgumentError) { m 1 } + assert_raise(ArgumentError) { m nil } + assert_raise(ArgumentError) { m [] } + assert_return(9) { m() } + + # expanding array + assert_return(9) { m(*[]) } + assert_raise(ArgumentError) { m(*nil) } + assert_raise(ArgumentError) { m(*[nil]) } + assert_raise(ArgumentError) { m(*[1]) } +end + +# one arg required +def test_1_arg + def m (arg) + arg + end + + assert_raise(ArgumentError) { m } + assert_return(99) { m 99 } + assert_return(nil) { m nil } + assert_return([]) { m [] } + assert_raise(ArgumentError) { m 1, 2 } + + # expanding array + assert_raise(ArgumentError) { m *[] } + assert_return(78) { m *[78] } + assert_return(79) { m *79 } + assert_return("b") { m *"b" } + assert_return(nil) { m *[nil] } + assert_return(nil) { m *nil } + + assert_raise(ArgumentError) { m *[80, 81] } + assert_return([82, 83]) { m *[[82, 83]] } +end + +# several args required +def test_5_args + def m arg1, arg2, arg3, arg4, arg5 + [arg1, arg2, arg3, arg4, arg5] + end + + assert_raise(ArgumentError) { m } + assert_raise(ArgumentError) { m 1 } + assert_raise(ArgumentError) { m 1, 2, 3, 4 } + assert_raise(ArgumentError) { m 1, 2, 3, 4, 5, 6 } + + # expanding array + assert_return([5,6,7,8,9]) { m *[5,6,7,8,9] } + assert_return([5,6,7,8,9]) { m 5, *[6,7,8,9] } + assert_return([5,6,7,8,9]) { m 5,6,7,8,*[9] } + assert_return([5,6,7,8,9]) { m 5,6,7,8,9,*[] } + + assert_raise(ArgumentError) { m 1, *[2, 3] } + assert_raise(ArgumentError) { m 1, *[2, 3, 4, 5, 6] } + # syntax error if nested expanding, more than 1 expanding, or not stay at last +end + +test_0_arg +test_1_arg +test_5_args \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Method/test_pass_args.rb b/merlin/main/languages/ruby/Tests/Runtime/Method/test_pass_args.rb new file mode 100644 index 0000000000..1b2f4bf737 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Method/test_pass_args.rb @@ -0,0 +1,53 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +def m1(arg); arg; end +def m3(arg1, arg2, arg3); return arg1, arg2, arg3; end + +# different types of value as arg +def test_arg_type + assert_return(3) { m1((3)) } + + assert_return(6) { m1(any_arg = 6) } # no keyword argument + assert_raise(NameError) { any_arg } + + assert_return(5) { m1(arg = 5) } + assert_raise(NameError) { arg } + + assert_return({'arg' => 3 }) { m1('arg' => 3) } + assert_return({'arg' => 3 }) { m1({'arg' => 3}) } + + assert_return({:arg => 3 }) { m1(:arg => 3) } + assert_return({:arg => 3 }) { m1({:arg => 3}) } +end + + +# order to evaluate the arg +def test_evaluate_order + x = 0 + assert_return([1, 11, 111]) { m3(x+=1, x+=10, x+=100) } +end + +def test_raise_when_evaluating + x = 0 + assert_raise(ZeroDivisionError) { m3(x+=1, (divide_by_zero;x+=10), x+=100) } + assert_equal(x, 1) +end + +#test_arg_type +test_evaluate_order +test_raise_when_evaluating diff --git a/merlin/main/languages/ruby/Tests/Runtime/Method/test_recursive.rb b/merlin/main/languages/ruby/Tests/Runtime/Method/test_recursive.rb new file mode 100644 index 0000000000..6bb0830414 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Method/test_recursive.rb @@ -0,0 +1,46 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + + +def test_call_itself + def m + m + end + assert_raise(SystemStackError) { m } +end + +def test_factorial + def factorial(m) + if m < 0 + raise ArgumentError, "m should be >=0" + end + + if m == 0 or m == 1 + return 1 + else + return m * factorial(m-1) + end + end + + assert_raise(ArgumentError) { factorial -4 } + assert_return(1) { factorial 0 } + assert_return(120) { factorial 5 } + #assert_return(2432902008176640000) { factorial 20 } # bug 282346 +end + +#test_call_itself # bug 280501 +test_factorial \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Method/test_return_value.rb b/merlin/main/languages/ruby/Tests/Runtime/Method/test_return_value.rb new file mode 100644 index 0000000000..f4db42574f --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Method/test_return_value.rb @@ -0,0 +1,38 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +def m x + if x > 2 + 10 + end +end + +assert_return(10) { m 3 } +assert_return(nil) { m 1 } + +def m + return 1, 2 +end + +assert_return([1,2]) { m } + +def m + return [2, 3] +end + +assert_return([2,3]) { m } + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Method/test_super.rb b/merlin/main/languages/ruby/Tests/Runtime/Method/test_super.rb new file mode 100644 index 0000000000..d32bf6fd85 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Method/test_super.rb @@ -0,0 +1,366 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/simple_test.rb' + +class Base1 + def foo + "Base1#foo()" + end + def goo(x) + "Base1#goo(#{x.inspect})" + end + def hoo(x,y=1,*z) + "Base1#hoo(#{x.inspect}, #{y.inspect}, *#{z.inspect})" + end + def woo(x) + "Base1#woo(#{x.inspect}), proc yielded: #{yield}" + end + def yoo(&b) + "Base1#yoo(), proc returned: #{b.call(123)}" + end + def zoo(x,y=1,*z,&b) + "Base1#zoo(#{x.inspect}, #{y.inspect}, *#{z.inspect}), proc returned: #{b.call(x, y)}" + end +end + +class Test1 < Base1 + def foo + "<" + super() + ">" + end + def goo(x) + "<" + super(x) + ">" + end + def hoo(*z) + "<" + super(*z) + ">" + end + def woo(x) + "<" + super(x) { yield } + ">" + end + def yoo(&b) + "<" + super(&b) + ">" + end + def zoo(x,y=1,*z,&b) + "<" + super(x,y,*z,&b) + ">" + end +end + +t = Test1.new + +describe "super calls with explict args" do + it "calling method with no args" do + t.foo.should == "" + end + + it "calling method with one arg" do + t.goo(42).should == "" + end + + it "calling method with optional & splatted args" do + t.hoo(42).should == "" + t.hoo(22, 33).should == "" + t.hoo(77, 88, 1, 2, 3).should == "" + end + + it "calling method with block" do + t.yoo { "the question" }.should == "" + t.woo(42) { "the answer" }.should == "" + end + + it "calling method with all kinds of arguments" do + t.zoo(42) { |x,y| x*y+1 }.should == "" + t.zoo(22, 33) { |x,y| x*y+2 }.should == "" + t.zoo(77, 88, 1, 2, 3) { |x,y| x*y+3 }.should == "" + end +end + + +class Test2 < Base1 + def foo + "<" + super + ">" + end + def goo(x) + "<" + super + ">" + end + def hoo(x,y=1,*z) + "<" + super + ">" + end + def woo(x) + "<" + super + ">" + end + def yoo(&b) + "<" + super + ">" + end + def zoo(x,y=1,*z,&b) + "<" + super + ">" + end +end + +t = Test2.new + +describe "super calls with implict args" do + it "calling method with no args" do + t.foo.should == "" + end + + it "calling method with one arg" do + t.goo(42).should == "" + end + + it "calling method with optional & splatted args" do + t.hoo(42).should == "" + t.hoo(22, 33).should == "" + t.hoo(77, 88, 1, 2, 3).should == "" + end + + it "calling method with block" do + t.yoo { "the question" }.should == "" + t.woo(42) { "the answer" }.should == "" + end + + it "calling method with all kinds of arguments" do + t.zoo(42) { |x,y| x*y+1 }.should == "" + t.zoo(22, 33) { |x,y| x*y+2 }.should == "" + t.zoo(77, 88, 1, 2, 3) { |x,y| x*y+3 }.should == "" + end +end + +class Base2 + def self.foo(x,y) + x + y + end + + def self.goo(x,y) + x * y + end +end + +class Base3 < Base2 + def Base3.foo(x,y) + super(x,y) + 123 + end + def self.goo(x,y) + super + 456 + end +end + +class Test3 < Base3 + def Test3.foo(x,y) + "foo: #{super(x,y)}" + end + def self.goo(x,y) + "goo: #{super}" + end +end + +describe "super calls on class/singleton methods" do + it "super works from singleton methods" do + x = "abc" + def x.to_s; super + "def"; end + x.to_s.should == "abcdef" + end + + it "super works in class methods" do + Test3.foo(4,5).should == "foo: 132" # 123 + (4+5) + Test3.goo(4,5).should == "goo: 476" # 456 + (4*5) + end +end + +class TestInit1 + def initialize(y) + @y = y + end + + def gety; @y; end +end + +class TestInit2 < TestInit1 + def initialize(x,y) + @x = x + super(y) + end + + def getx; @x; end +end + +class TestInit3 < TestInit2 + def initialize(x,y) + super + @z = @x + @y + end + + def getz; @z; end +end + +describe "super calls from initialize" do + it "super in initialize calls parent method" do + t2 = TestInit2.new(123, 456) + t2.getx.should == 123 + t2.gety.should == 456 + + t3 = TestInit3.new(22,44) + [t3.getx, t3.gety].should == [22, 44] + t3.getz.should == 66 + end +end + +module M0 + def foo + "foo" + end +end +module M1 + def foo + "" + super + "" + end +end +module M2 + def foo + "" + super + "" + end +end +module M3 + def foo + "" + super + "" + end +end +module M4 + def foo + "" + super + "" + end +end +module M5 + def foo + "" + super + "" + end +end + +class C1 + include M1 + include M0 + def foo + "" + super + "" + end +end +class C2 < C1 + include M3 + include M2 + def foo + "" + super + "" + end +end +class C3 < C2 + include M5 + include M4 + def foo + "" + super + "" + end +end + +describe "super skips one in the method resolution order (MRO)" do + it "class method will call mixin methods" do + module Mixin1 + def bar(x) + "Mixin1#bar(#{x})" + end + end + class Class1 + def bar(x) + "Class1#bar, " + super(x) + end + end + should_raise(NoMethodError) { Class1.new.bar(222) } + class Class1 + include Mixin1 + end + Class1.new.bar(555).should == "Class1#bar, Mixin1#bar(555)" + end + + it "mixin methods call each other" do + module Mixin2 + def bar(x) + "Mixin2#bar, " + super(x) + end + end + class Class2a + include Mixin1 + include Mixin2 + end + + Class2a.new.bar(888).should == "Mixin2#bar, Mixin1#bar(888)" + + # order is reversed if they are added on the same line + class Class2b + include Mixin1, Mixin2 + end + + Class2b.new.bar(888).should == "Mixin1#bar(888)" + end + + it "mixin method can call base class method" do + class Class3 + def bar(x) + "Class3#bar(#{x})" + end + end + class Class4 < Class3 + include Mixin2 + end + Class4.new.bar(111).should == "Mixin2#bar, Class3#bar(111)" + end + + it "super calls can be chained and follow the MRO" do + C3.new.foo.should == "foo" + end +end + +describe "super throws an error when no super method is available" do + it "super from top level code is an error" do + should_raise(NoMethodError, "super called outside of method") { super } + end + + it "super with no super method is an error" do + class Class5 + def zoo + super + end + end + + should_raise(NoMethodError, "super: no superclass method `zoo'") { Class5.new.zoo } + + x = Object.new + def x.zoo + super(1,2,3) + end + + should_raise(NoMethodError, "super: no superclass method `zoo'") { x.zoo } + end + + it "raise an error if super method has been undefined" do + class Class6 + def yoo; "yoo"; end + end + class Class7 < Class6 + def yoo; super; end + end + x = Class7.new + x.yoo.should == "yoo" + Class6.send(:undef_method, :yoo) + should_raise(NoMethodError, "super: no superclass method `yoo'") { x.yoo } + end +end + +finished + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Method/test_var_arg.rb b/merlin/main/languages/ruby/Tests/Runtime/Method/test_var_arg.rb new file mode 100644 index 0000000000..d038f06510 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Method/test_var_arg.rb @@ -0,0 +1,60 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# only one arg, and it is vararg +def test_1_vararg + def m(*args) + args + end + + assert_return([]) { m } + assert_return([1]) { m 1 } + assert_return([2, 3]) { m 2, 3 } + assert_return([[4]]) { m [4] } + assert_return([5, [6]]) { m 5, [6] } + assert_return([[]]) { m(m) } + + # expanding array as arg + assert_return([]) { m *[] } + assert_return([1]) { m *[1,] } + assert_return([1, 2, 3]) { m 1, *[2, 3,] } +end + +# two args, the last one is vararg +def test_1_normal_1_vararg + def m(arg1, *arg2) + [arg1, arg2] + end + + assert_raise(ArgumentError) { m } + assert_return([1, []]) { m 1 } + assert_return([2, [3]]) { m 2, 3 } + assert_return([4, [5, 6]]) { m 4, 5, 6 } + assert_return([7, [[8, 9]]]) { m 7, [8, 9] } + assert_return([[10], [11]]) { m [10], 11 } + + # expanding array as arg + assert_raise(ArgumentError) { m *[] } + assert_return([1, []]) { m *[1,] } + + assert_return([1, [2, 3]]) { m *[1, 2, 3] } + assert_return([1, [2, 3]]) { m 1, *[2, 3] } + assert_return([1, [2, 3]]) { m 1, 2, *[3] } +end + +test_1_vararg +test_1_normal_1_vararg \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_a.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_a.rb new file mode 100644 index 0000000000..fa823b425a --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_a.rb @@ -0,0 +1,42 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +module Pacific + SIZE = 100 + + def Pacific.listen + 101 + end + + class Ocean + FLAG = 102 + end + + module Hawaii + KIND = 103 + end +end + +TOP_SIZE = 200 + +def top_method + 201 +end + +class Top_Class + FLAG = 202 +end + +var = 203 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_a_require_b.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_a_require_b.rb new file mode 100644 index 0000000000..b876762596 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_a_require_b.rb @@ -0,0 +1,24 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +module A + X = 100 +end + +require "module_b_require_a" + +module A + Z = B::Y + 100 +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_b.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_b.rb new file mode 100644 index 0000000000..e7c99f527e --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_b.rb @@ -0,0 +1,43 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +module Pacific + SIZE2 = 300 + + def Pacific.listen2 + 301 + end + + class Ocean + FLAG2 = 302 + end + + module Hawaii + KIND2 = 303 + end +end + + +TOP_SIZE2 = 400 + +def top_method2 + 401 +end + +class Top_Class2 + FLAG = 402 +end + +var2 = 402 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_b_require_a.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_b_require_a.rb new file mode 100644 index 0000000000..5a5c4d6ba6 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_b_require_a.rb @@ -0,0 +1,20 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "module_b_require_a" + +module B + Y = A::X + 100 +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_c.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_c.rb new file mode 100644 index 0000000000..c5eb073164 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_c.rb @@ -0,0 +1,42 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +module Pacific + SIZE = 500 + + def Pacific.listen + 501 + end + + class Ocean + FLAG = 502 + end + + module Hawaii + KIND = 503 + end +end + +TOP_SIZE = 600 + +def top_method + 601 +end + +class Top_Class + FLAG = 602 +end + +var = 603 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_c_require_d.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_c_require_d.rb new file mode 100644 index 0000000000..4b9a1437e1 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_c_require_d.rb @@ -0,0 +1,20 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "module_d_require_c" + +module C + X = D::Y +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_d_require_c.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_d_require_c.rb new file mode 100644 index 0000000000..891596d285 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_d_require_c.rb @@ -0,0 +1,20 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require "module_c_require_d" + +module D + Y = C::X +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_require_loaded_bad_module.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_require_loaded_bad_module.rb new file mode 100644 index 0000000000..bd97568bd6 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_require_loaded_bad_module.rb @@ -0,0 +1,20 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +CONST_U = 83 + +require "module_with_divide_by_zero2" + +CONST_V = 94 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_require_unloaded_bad_module.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_require_unloaded_bad_module.rb new file mode 100644 index 0000000000..703c74592d --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_require_unloaded_bad_module.rb @@ -0,0 +1,20 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +CONST_X = 77 + +require "module_with_divide_by_zero2" + +CONST_Y = 99 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_var_as_method.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_var_as_method.rb new file mode 100644 index 0000000000..412491c95e --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_var_as_method.rb @@ -0,0 +1,18 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +def var + 800 +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_with_divide_by_zero.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_with_divide_by_zero.rb new file mode 100644 index 0000000000..9788f0d4c1 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_with_divide_by_zero.rb @@ -0,0 +1,19 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +module Module_With_Divide_By_Zero + CONST_ONE = 1 + CONST_TWO = 2 / 0 +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_with_divide_by_zero2.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_with_divide_by_zero2.rb new file mode 100644 index 0000000000..18bdbe4cca --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_with_divide_by_zero2.rb @@ -0,0 +1,19 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +module Module_With_Divide_By_Zero + CONST_THREE = 3 + CONST_FOUR = 4 / 0 +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_with_global.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_with_global.rb new file mode 100644 index 0000000000..81d6dc536e --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_with_global.rb @@ -0,0 +1,16 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +$global_var += 2 \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/module_with_syntax_error.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/module_with_syntax_error.rb new file mode 100644 index 0000000000..3c5418677e --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/module_with_syntax_error.rb @@ -0,0 +1,17 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +variable_in_module_with_syntax_error_one = 1 +variable_in_module_with_syntax_error_two = (1, 2) # syntax error \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/test_local_in_module.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/test_local_in_module.rb new file mode 100644 index 0000000000..7081ac9450 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/test_local_in_module.rb @@ -0,0 +1,79 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# page: 124 +# local variables in a loaded or required file are not propagated to the scope that +# loads or requires them. + +module Pacific + SIZE = -100 + + def Pacific.listen + -101 + end + + class Ocean + FLAG = -102 + end + + module Hawaii + KIND = -103 + end +end + +TOP_SIZE = -200 + +def top_method + -201 +end + +class Top_Class + FLAG = -202 +end + +var = -203 + +require "module_a" + +# things changed after require +assert_equal(Pacific::SIZE, 100) +assert_equal(Pacific::listen, 101) +assert_equal(Pacific::Ocean::FLAG, 102) +assert_equal(Pacific::Hawaii::KIND, 103) + +assert_equal(TOP_SIZE, 200) +assert_equal(top_method, 201) +assert_equal(Top_Class::FLAG, 202) + +# local var stays +assert_equal(var, -203) # !! + +## + +require "module_var_as_method" + +assert_equal(var, -203) +assert_equal(var(), 800) + +var = 403 +assert_equal(var, 403) +assert_equal(var(), 800) + +def var; 900; end +assert_equal(var, 403) +assert_equal(var(), 900) + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/test_module_action.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/test_module_action.rb new file mode 100644 index 0000000000..d538a0da93 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/test_module_action.rb @@ -0,0 +1,23 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +module Target + +end + +assert_raise(NoMethodError) { Target.new } # undefined method `new' for Target:Module +assert_raise(NoMethodError) { Target() } # undefined method `Target' for main:Object \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/test_module_path.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/test_module_path.rb new file mode 100644 index 0000000000..4991eb538b --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/test_module_path.rb @@ -0,0 +1,33 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# "require" load any given file only once +$global_var = 88 +assert_true { require "module_with_global.rb" } +assert_equal($global_var, 90) +assert_false { require "module_with_global" } +assert_equal($global_var, 90) +assert_true { require "module_with_Global" } # cap "G" +assert_equal($global_var, 92) +assert_true { require "./module_with_global" } # ?? +assert_equal($global_var, 94) + +# page: 124 +# accept relative and absolute paths; if given a relative path (or just a plain name), +# they will search every directory in the current load path ($:) for the file + +# todo \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/test_require_bad_module.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/test_require_bad_module.rb new file mode 100644 index 0000000000..3b53789cef --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/test_require_bad_module.rb @@ -0,0 +1,39 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +assert_raise(LoadError) { require "not_existing.rb" } +assert_raise(LoadError) { require "not_existing" } + +assert_raise(SyntaxError) { require "module_with_syntax_error" } +# require again the same file, no exception raised +assert_false { require "module_with_syntax_error" } +assert_raise(NameError) { variable_in_module_with_syntax_error_one } +assert_raise(NameError) { variable_in_module_with_syntax_error_two } + +assert_raise(ZeroDivisionError) { require "module_with_divide_by_zero" } +assert_false { require "module_with_divide_by_zero" } +assert_equal(Module_With_Divide_By_Zero::CONST_ONE, 1) +assert_raise(NameError) { Module_With_Divide_By_Zero::CONST_TWO } + +assert_raise(ZeroDivisionError) { require "module_require_unloaded_bad_module" } +assert_equal(CONST_X, 77) +assert_raise(NameError) { CONST_Y } + +require "module_require_loaded_bad_module" +assert_equal(CONST_U, 83) +assert_equal(CONST_V, 94) + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/test_require_cascade.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/test_require_cascade.rb new file mode 100644 index 0000000000..980431d82f --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/test_require_cascade.rb @@ -0,0 +1,35 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# recursive require + +# good +assert_true { require "module_a_require_b.rb" } +assert_false { require "module_b_require_a.rb" } +assert_equal([A::X, B::Y, A::Z], [100, 200, 300]) + +# bad +assert_raise(NameError) { require "module_c_require_d" } +assert_false { require "module_d_require_c" } ## ?? +assert_raise(NameError) { C } +D ## ?? +assert_raise(NameError) { D::Y } ## ?? + + +# reach the same module + + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/test_require_itself.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/test_require_itself.rb new file mode 100644 index 0000000000..fe035783cd --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/test_require_itself.rb @@ -0,0 +1,38 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + + +if defined? $global_var + $global_var += 10 +else + $global_var = 1 +end + +$global_var += 100 + +puts $global_var + +require "test_require_itself.rb" + +$global_var += 1000 + +puts $global_var + +require "./test_require_itselF.rb" + +$global_var += 10000 + +puts $global_var + diff --git a/merlin/main/languages/ruby/Tests/Runtime/Module/test_require_three_modules.rb b/merlin/main/languages/ruby/Tests/Runtime/Module/test_require_three_modules.rb new file mode 100644 index 0000000000..0650deefcc --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Runtime/Module/test_require_three_modules.rb @@ -0,0 +1,90 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require '../../util/assert.rb' + +# page: 117 +# a way of grouping together methods, classes, and constants + +# module define a namespace + +require "module_a" + +assert_raise(NoMethodError) { Pacific.SIZE } +assert_equal(Pacific.listen, 101) +assert_raise(NoMethodError) { Pacific.Ocean } +assert_raise(NoMethodError) { Pacific.Hawaii } + +assert_equal(Pacific::SIZE, 100) +assert_equal(Pacific::listen, 101) +assert_equal(Pacific::Ocean::FLAG, 102) +assert_equal(Pacific::Hawaii::KIND, 103) + +assert_equal(TOP_SIZE, 200) +assert_equal(top_method, 201) +assert_equal(Top_Class::FLAG, 202) +#assert_raise(NameError) { var } + +require "module_b" + +# unchanged +assert_equal(Pacific::SIZE, 100) +assert_equal(Pacific::listen, 101) +assert_equal(Pacific::Ocean::FLAG, 102) +assert_equal(Pacific::Hawaii::KIND, 103) + +# new +assert_equal(Pacific::SIZE2, 300) +assert_equal(Pacific::listen2, 301) +assert_equal(Pacific::Ocean::FLAG2, 302) +assert_equal(Pacific::Hawaii::KIND2, 303) + +# unchanged +assert_equal(TOP_SIZE, 200) +assert_equal(top_method, 201) +assert_equal(Top_Class::FLAG, 202) +#assert_raise(NameError) { var } + +# new +assert_equal(TOP_SIZE2, 400) +assert_equal(top_method2, 401) +assert_equal(Top_Class2::FLAG, 402) +#assert_raise(NameError) { var2 } + +require "module_c" # runtime warning: already initialized constant + +# unchanged +assert_equal(Pacific::SIZE2, 300) +assert_equal(Pacific::listen2, 301) +assert_equal(Pacific::Ocean::FLAG2, 302) +assert_equal(Pacific::Hawaii::KIND2, 303) + +# changed +assert_equal(Pacific::SIZE, 500) +assert_equal(Pacific::listen, 501) +assert_equal(Pacific::Ocean::FLAG, 502) +assert_equal(Pacific::Hawaii::KIND, 503) + +# changed +assert_equal(TOP_SIZE, 600) +assert_equal(top_method, 601) +assert_equal(Top_Class::FLAG, 602) +#assert_raise(NameError) { var } + +# unchanged +assert_equal(TOP_SIZE2, 400) +assert_equal(top_method2, 401) +assert_equal(Top_Class2::FLAG, 402) +#assert_raise(NameError) { var2 } diff --git a/merlin/main/languages/ruby/Tests/Scripts/compile_test.bat b/merlin/main/languages/ruby/Tests/Scripts/compile_test.bat new file mode 100644 index 0000000000..b1e041deda --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Scripts/compile_test.bat @@ -0,0 +1,10 @@ +@echo off + +%MERLIN_ROOT%\Test\Scripts\ir.cmd -save %* +%MERLIN_ROOT%\Test\Scripts\ir.cmd -load %* + +if NOT "%ERRORLEVEL%" == "0" ( + echo "" + echo PreCompilation failed + exit /b 1 +) diff --git a/merlin/main/languages/ruby/Tests/Scripts/irtest.bat b/merlin/main/languages/ruby/Tests/Scripts/irtest.bat new file mode 100644 index 0000000000..1b8dc507c1 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Scripts/irtest.bat @@ -0,0 +1,70 @@ +@echo off + +echo =================================================================== +echo == IronRuby.Tests.exe +echo =================================================================== + + +if "%ROWAN_BIN%" == "" ( + set ROWAN_BIN=%MERLIN_ROOT%\bin\debug +) + +%ROWAN_BIN%\IronRuby.Tests.exe + +if NOT "%ERRORLEVEL%" == "0" ( + echo "" + echo At least 1 of the dev unit tests failed + exit /b 1 +) + +echo =================================================================== +echo == IronRuby.Tests.exe /partial +echo =================================================================== + +pushd %ROWAN_BIN% + +%ROWAN_BIN%\IronRuby.Tests.exe /partial +set EXITLEVEL=%ERRORLEVEL% +popd + +if NOT "%EXITLEVEL%" == "0" ( + echo "" + echo At least 1 of the dev unit tests failed; args: /partial + exit /b 1 +) + +echo =================================================================== +echo == IronRuby.Tests.exe /interpret +echo =================================================================== + +pushd %ROWAN_BIN% + +%ROWAN_BIN%\IronRuby.Tests.exe /interpret +set EXITLEVEL=%ERRORLEVEL% +popd + +if NOT "%EXITLEVEL%" == "0" ( + echo "" + echo At least 1 of the dev unit tests failed; args: /interpret + exit /b 1 +) + +echo =================================================================== +echo == IronRuby.Tests.exe /partial /interpret +echo =================================================================== + +pushd %ROWAN_BIN% + +%ROWAN_BIN%\IronRuby.Tests.exe /partial /interpret +set EXITLEVEL=%ERRORLEVEL% +popd + +if NOT "%EXITLEVEL%" == "0" ( + echo "" + echo At least 1 of the dev unit tests failed; args: /partial /interpret + exit /b 1 +) + +echo OK + + diff --git a/merlin/main/languages/ruby/Tests/Syntax/array.rb b/merlin/main/languages/ruby/Tests/Syntax/array.rb new file mode 100644 index 0000000000..f295fb10a4 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/array.rb @@ -0,0 +1,164 @@ + +a = [] +a = [ ] +# Scenario: empty array +# Default: pass + +a = [ , ] +# Scenario: trialing comma after empty +# Default: syntax error + +a = [()] +a = [{}] +a = [1, ] # trialing comma +# Scenario: empty array +# Default: pass + +a = [1, ,] +# Scenario: +# Default: syntax error + +a = [.] +# Scenario: dot +# Default: syntax error + +a = [2.] +# Scenario: +# Default: syntax error + +a = [2.,] +# Scenario: +# Default: syntax error + +a = [1, []] +a = [2, "4", ] +a = ["]", 3, ] +a = [4, "[]]", ] +## two element + +# Scenario: unknown +# Default: pass + +a = [[]] +a = [[], ] +a = [[], []] +# Scenario: unknown +# Default: pass + +a = [[] []] +# Scenario: `[]': wrong number of arguments (0 for 1) (ArgumentError) +# Default: ArgumentError +# ParseOnly: pass + +a = [ + +] +a = [1 +] +a = [ +2] +a = [ +3 +] +a = [ +4, +5 +] +a = +[6] +# Scenario: unknown +# Default: pass + +a = [ +4 +, +5 +] +# Scenario: comma position +# Default: syntax error + +a = [ +4, +5 +, +] +# Scenario: comma position +# Default: syntax error + +a += [7] +# Scenario: +# Default: syntax error + +#a +#= +#[7] +## Scenario: unknown +## Default: syntax error + +a = %w{ } +a = %W{ } +a = %w() +a = %W( ) +# Scenario: empty +# Default: pass + +a = %w + +# Scenario: (Unterminated string meets end of file) +# Default: Unterminated + +a = %W + +# Scenario: unknown +# Default: Unterminated + + +a = %w (a) +# Scenario: space (Unterminated string meets end of file) +# Default: Unterminated + + +a = %W (bc) + +# Scenario: space +# Default: Unterminated + + +a = % w(def) + +# Scenario: space +# Default: syntax error + + +a = % W(ghij) + +# Scenario: space +# Default: syntax error + + +a = %W +(kflmn) + + +# Scenario: space +# Default: Unterminated + + +a = %w +(opqrst) + +# Scenario: space +# Default: Unterminated + + +a = %w{ a } +a = %W{ a } +a = %W{ #a } +a = %W{ #{a} } + +a= %w[ %w{} ] +a= %W{ %w{} } + +# Scenario: one element +# Default: pass diff --git a/merlin/main/languages/ruby/Tests/Syntax/class_basic.rb b/merlin/main/languages/ruby/Tests/Syntax/class_basic.rb new file mode 100644 index 0000000000..a348c10977 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/class_basic.rb @@ -0,0 +1,92 @@ +class end +# Scenario: no name +# Default: syntax error + +class +end +# Scenario: no name +# Default: syntax error + +class C end +# Scenario: same line +# Default: syntax error + +class C +end + +class # name in different line + C +end + +class D + + end + +class E; #semi-comma +end +# Scenario: unknown +# Default: pass + +class C, +end +# Scenario: comma after name +# Default: syntax error + +class MyClass +end + +class CLASS # all uppercase +end + +class My_Class # underscore +end +# Scenario: allowed: +# Default: pass + +class myclass +end +# Scenario: all lowercased +# Default: class/module name must be CONSTANT +# ParseOnly: Class/module name must be a constant + +class myClass +end +# Scenario: starting with lowercase +# Default: class/module name must be CONSTANT +# ParseOnly: Class/module name must be a constant + +class _Myclass +end +# Scenario: starting with underscore +# Default: class/module name must be CONSTANT +# ParseOnly: Class/module name must be a constant + +class Class +end +# Scenario: special class "Class" +# Default: pass + +class "string" +end +# Scenario: string as name +# Default: syntax error + +class < "string" +end +# Scenario: "<" +# Default: syntax error + +class My + def m + end +end + +class My.m +end +# Scenario: "." in the name +# Default: syntax error + +class << "string" +end +# Scenario: singleton +# Default: pass diff --git a/merlin/main/languages/ruby/Tests/Syntax/class_derive.rb b/merlin/main/languages/ruby/Tests/Syntax/class_derive.rb new file mode 100644 index 0000000000..41fb17e95d --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/class_derive.rb @@ -0,0 +1,108 @@ +class C < Object +end + +class D < C +end +# Scenario: normal +# Default: pass + +class C : Object +end +# Scenario: use : +# Default: syntax error + +class C(Object) +end +# Scenario: use () +# Default: syntax error + +class C() +end +# Scenario: use () again +# Default: syntax error + +class C < C +end +# Scenario: superclass mismatch for class C +# Default: NameError +# ParseOnly: pass + +class D < 1 +end +# Scenario: superclass must be a Class (Fixnum given) +# Default: TypeError +# ParseOnly: pass + +class D < Fixnum +end +# Scenario: Fixnum directly +# Default: pass + +class + C < # indicator for superclass + Object +end +# Scenario: unknown +# Default: pass + +class + C + < + Object +end +# Scenario: unknown +# Default: syntax error + +class + C + < Object +end +# Scenario: unknown +# Default: syntax error + +class C < + > Object +end +# Scenario: unknown +# Default: syntax error + +class A +end +class B +end + +class C(A, B) +end +# Scenario: multiple inheritance +# Default: syntax error + +class A +end +class B +end + +class C < A, B +end +# Scenario: multiple inheritance +# Default: syntax error + +class A +end +class B +end + +class C < A < B +end +# Scenario: superclass must be a Class (NilClass given) +# Default: TypeError +# ParseOnly: pass + +class A +end +class B +end + +class C : A, B +end +# Scenario: +# Default: syntax error diff --git a/merlin/main/languages/ruby/Tests/Syntax/class_lshift.rb b/merlin/main/languages/ruby/Tests/Syntax/class_lshift.rb new file mode 100644 index 0000000000..73d543c19c --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/class_lshift.rb @@ -0,0 +1,33 @@ +class << "string" +end + +class + << "string" +end + +class + << + "string" +end + +x = "string" +class << x +end + +class << + x +end +# Scenario: normal +# Default: pass + +class < < "string" +end +# Scenario: space between < < +# Default: syntax error + +class << 1 +end +# Scenario: no virtual class for Fixnum +# Default: TypeError +# ParseOnly: pass + diff --git a/merlin/main/languages/ruby/Tests/Syntax/codeblock_method.rb b/merlin/main/languages/ruby/Tests/Syntax/codeblock_method.rb new file mode 100644 index 0000000000..34e04199e7 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/codeblock_method.rb @@ -0,0 +1,144 @@ +#* define + +def method & block + 1 +end +method +method { 1 } +# Scenario: space between & and parameter name +# Default: pass + +def method &block + 1 +end +method +method { 1 } +# Scenario: without parenthesis, only arg +# Default: pass + +def method a, &block + 1 +end +method(1) +method(2) { 2 } +# Scenario: without parenthsis, two args +# Default: pass + +def method a, *b, &block + 1 +end +method(1) +method(1,2) +method(1, 2, 3) +method(1, 2, 3, 4) {5} +# Scenario: without parenthesis, 3 args +# Default: pass + +def method &block, a + 1 +end +# Scenario: without parenthsis, two args, & not last +# Default: syntax error + +def method a, &block, *c + 1 +end +# Scenario: without parenthesis, 3 args +# Default: syntax error + +def method a, &block, + +end +# Scenario: without parenthesis, comma as last +# Default: syntax error + +def method &a, &b + 1 +end +# Scenario: what about two block params +# Default: syntax error + +#* calling + +def method + 1 +end +method +method { 2 } +# Scenario: can pass block no matter what +# Default: pass + +def method &block + 1 +end +method +method { 3 } +# Scenario: ok to have no block passed in : block is optional +# Default: pass + +def method &block + 1 +end +method({1}) +# Scenario: can i add parenthesis around the block +# Default: odd number list for Hash + +def method a, &b + 1 +end +method 1 +method(2) +method(3) {4} # parenthesis is needed for normal args +# Scenario: two args +# Default: pass + +def method a, &b + 1 +end +method 1 {2} +# Scenario: without parenthesis +# Default: syntax error + +def method a, &b + 1 +end +method(1), {2} # odd number list for Hash +# Scenario: No comma between arg and block +# Default: syntax error + +def method a, &b + 1 +end +method {2} + +# Scenario: no normal args passed in: wrong number of arguments (0 for 1) +# Default: ArgumentError +# ParseOnly: pass + +def method c +end + +method(1) +method(1) do + 1 +end +method(1) { 1 } +method 1 do + 2 +end +# Scenario: paraenthesis or do-end, related to arguments +# Default: pass + +def method c +end +method 1 { 2 } +# Scenario: paraenthesis or do-end, related to arguments +# Default: syntax error + + +def method +end +p = lambda {} +method &p, 1 +# Scenario: pass proc as without () +# Default: syntax error diff --git a/merlin/main/languages/ruby/Tests/Syntax/generate.cmd b/merlin/main/languages/ruby/Tests/Syntax/generate.cmd new file mode 100644 index 0000000000..30e8e5af2b --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/generate.cmd @@ -0,0 +1 @@ +ruby run_syntax.rb -snap \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Syntax/hash.rb b/merlin/main/languages/ruby/Tests/Syntax/hash.rb new file mode 100644 index 0000000000..402f4be3be --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/hash.rb @@ -0,0 +1,100 @@ +## created by placing a list of key/value pairs between braces +## with either a comma or the sequence => between the key and +## the value + +h = {} +h = { } +# Scenario: empty +# Default: pass + +h = { , } +# Scenario: trialling comma +# Default: syntax error + +h = { "str", 100 } +h = { "str", 100, } +h = { "str" => 100 } +h = { "str" => 200, } +# Scenario: one pair +# Default: pass + +h = { +} +h = +{ } +# Scenario: empty +# Default: pass + +h += {} +# Scenario: +# Default: syntax error + +h = { + 100 + => + "str" + } +# Scenario: newline +# Default: syntax error + +h = { 100 + => "str" } +# Scenario: newline +# Default: syntax error + +h = { + 200 + , + "str" + } +# Scenario: newline +# Default: syntax error + +h = { 200 + , "str" } +# Scenario: newline +# Default: syntax error + +h = { 200, + "str" } +h = { 100 => + "str"} +# Scenario: new line +# Default: pass + +{ 10 } +# Scenario: odd number +# Default: odd number list for Hash + +{"a"=> 10, 'b'} +# Scenario: odd number +# Default: syntax error + +{"a"=> 10, 'b', "c" => 10} +# Scenario: odd number +# Default: syntax error + +{ 1, 2, 3 } +# Scenario: odd number +# Default: odd number list for Hash + +{'a', 10} +# Scenario: seperator "," +# Default: pass + +{10:20} +# Scenario: seperator ":" +# Default: syntax error + +{ 10 -> 20} +# Scenario: seperator "->" +# Default: syntax error + +{ "a" => 10, "b", 20} +# Scenario: mixed seperator +# Default: syntax error + +{ "a", 10, "b" => 20} +# Scenario: mixed seperator +# Default: syntax error diff --git a/merlin/main/languages/ruby/Tests/Syntax/method_basic.rb b/merlin/main/languages/ruby/Tests/Syntax/method_basic.rb new file mode 100644 index 0000000000..0f3aa19f30 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/method_basic.rb @@ -0,0 +1,232 @@ +def method end +# Scenario: "end" in the same line as "def" +# Default: syntax error + +def + method end +# Scenario: "def " in different line as method name +# Default: syntax error + +def + method +end +method +# Scenario: method name in the different line as "def" +# Default: pass + +def + method + a + end +method +# Scenario: "a" as method body here +# Default: NameError +# ParseOnly: pass + +$a = 10 +def + method + $a + end +method +# Scenario: "a" as method body here +# Default: pass + +def + method + a + end +method 1 +# Scenario: "a" as method body here +# Default: ArgumentError +# ParseOnly: pass + +def method 1 end +# Scenario: body/"end" in the "def" line +# Default: syntax error + +def method a 1 +end +# Scenario: body/parameter in the "def" line +# Default: syntax error + +def method() end +method +# Scenario: "end" in the "def" line, parameters specified by parenthesis, no body +# Default: pass + +def method() 1 end +method + +def method ( ) 1 end # space between the name and the parenthesis +method + +def method(a) 1 end +method 1 + +def method(a) 1 +end +method 1 +# Scenario: body/"end" in the "def" line, parameters specified by parenthesis +# Default: pass + +def "method" + 1 +end +# Scenario: "string" after def +# Default: syntax error + +def [] + 1 +end +# Scenario: empty array (operator) after def +# Default: pass + +def [1, 2] + 1 +end +# Scenario: non-empty array after def +# Default: syntax error + +def [i, j] + 1 +end +# Scenario: non-empty array after def +# Default: syntax error + +def 9method +end +# Scenario: method name starting with digit +# Default: syntax error + +def me1thod +end +me1thod +# Scenario: method name embeded with digit +# Default: pass + +def Method + 1 +end +Method + +# uppercase in the middle of the method name +def mEthod + 1 +end +mEthod + +def mETHOD + 1 +end +mETHOD +# Scenario: method name begins with Uppercase +# Default: pass + +def _method + 1 +end +_method + +def me_thod + 1 +end +me_thod +# Scenario: method name begins with _, or _ in +# Default: pass + +def .method + 1 +end +# Scenario: method name begins with . +# Default: syntax error + +def me.thod +end +# Scenario: method name with . inside: undefined local variable or method `me' for main:Object (NameError) +# Default: NameError +# ParseOnly: pass + +me = "hello" +def me.thod + 1 +end +me.thod +# Scenario: method name with . inside (works) +# Default: pass + +def me?thod +end +me? 1 +# Scenario: method name with "?" inside +# Default: pass + +def me?thod +end +me?thod +# Scenario: method name with "?" inside, and call it +# Default: NameError +# ParseOnly: pass + +def method? + 1 +end +method? +# Scenario: method name ends with "?" +# Default: pass + +def < + 1 +end +# Scenario: method name "<" +# Default: pass + +def < +end +< +# Scenario: method name "<", and call it +# Default: syntax error + +# Scenario: method name "<<" +# Default: pass +def << +end + +def method< +end +# Scenario: method name ends with "<" +# Default: syntax error + +def meth 'ruby', + 'framework' => '.net' + }) + +method( + 'language' => 'ruby', + 'framework' => '.net' + ) + +method({ + :language => 'ruby', + :framework => '.net' + }) + +method( + :language => 'ruby', + :framework => '.net' + ) +# Scenario: one arg +# Default: pass + +def method(a) + return a +end + +method { + 'language' => 'ruby', + 'framework' => '.net' + } +# Scenario: one arg: without parenthesis, use hash, different line +# Default: syntax error + +def method(a) + return a +end + +method { 'language' => 'ruby', 'framework' => '.net' } +# Scenario: one arg: without parenthesis, use hash, same line +# Default: syntax error + + +def method(a) + return a +end + +method :language => 'ruby', :framework => '.net' +# Scenario: one arg: without parenthesis, use Symbol +# Default: pass + + +def method(a) + return a +end + +method :language => 'ruby', + :framework => '.net' +# Scenario: one arg: without parenthesis, use Symbol +# Default: pass + +def method(a, b) + return a, b +end + +method({ + 'language' => 'ruby', + 'framework' => '.net' + }, 8) + +method(9, { + 'language' => 'ruby', + 'framework' => '.net' + }) + +method(12, 'framework' => '.net') +method(34, 'language' => 'ruby', 'framework' => '.net') +# Scenario: two args (postive) +# Default: pass + +def method(a, b) + return a, b +end +method('language' => 'ruby', 'framework' => '.net') +# Scenario: two args: wrong number of arguments (1 for 2) +# Default: ArgumentError +# ParseOnly: pass + +def method(a, b) + return a, b +end +method('language' => 'ruby', 34) +# Scenario: two args: hash arg must be the last +# Default: syntax error + +def method(a, b) + return a, b +end +method('language' => 'ruby', 2, 'framework' => '.net') +# Scenario: two args +# Default: syntax error diff --git a/merlin/main/languages/ruby/Tests/Syntax/method_defaultvalue.rb b/merlin/main/languages/ruby/Tests/Syntax/method_defaultvalue.rb new file mode 100644 index 0000000000..b7750565a0 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/method_defaultvalue.rb @@ -0,0 +1,269 @@ +def method a= +end +# Scenario: without parenthesis, without default value +# Default: syntax error + +def method a= + puts +end +method +# Scenario: without parenthesis, without default value (valid??) +# Default: pass + +def method a= + puts "hello" +end +method +# Scenario: without parenthesis, without default value +# Default: syntax error + +def method a=3 + 1 +end +method +method(10) +# Scenario: without parenthesis +# Default: pass + +def method a = 3 +end +method +method(10) +# Scenario: without parenthesis (but space) +# Default: pass + +def method a=3 end +# Scenario: without parenthesis, "end" in the same line as "def" +# Default: syntax error + +def method a= + 3 + 1 +end +method +method(10) +# Scenario: without parenthesis, default value in different line +# Default: pass + +def method a=3, +end +# Scenario: without parenthesis, with extra comma +# Default: syntax error + +def method(a=) +end +# Scenario: with parentheis, without default value +# Default: syntax error + +def method(a==3) +end +# Scenario: with parentheis, with == +# Default: syntax error + +def method(a=3) + a +end +method +method(10) +# Scenario: with parenthesis +# Default: pass + +def method(a + =3) + a +end +# Scenario: with parenthesis, = in different line +# Default: syntax error + +def method(a= + 3) +end +method +method(10) +# Scenario: with parenthesis, default value in different line +# Default: pass + +## TWO+ PARAMETERS +def method a, + b = 2 +end +method(1) +method(2, 3) +method(a=4) +method(a=4, b=a * 2) +# Scenario: without parenthesis, parameters in different lines +# Default: pass + +def method a=3, b +end +# Scenario: without parenthesis, default value before +# Default: syntax error + +def method a=3, + b +end +# Scenario: without parenthesis, parameters in different lines +# Default: syntax error + +def method a=3, b=4 +end +method +method(1) +method(1,2) +# Scenario: without parenthesis, two default values +# Default: pass + +def method (a, + b = 2) +end +method(1) +method(1, 10) +# Scenario: with parenthesis, parameters in different lines +# Default: pass + +def method (a=3, b) +end +# Scenario: with parenthesis, default value before +# Default: syntax error + +def method (a=3, + b) +end +# Scenario: with parenthesis, parameters in different lines +# Default: syntax error + +def method (a=3, b=4) +end +method +method(1) +method(1,2) +# Scenario: without parenthesis, two default values +# Default: pass + +#* calling +def method(a=-3, b=-4) + a + b +end + +method +method(-2) +method(-1, 0) +method(a = 1) +method(b = 2) +method(a = 3, b = 4) +method(b = 5, a = 6) + +method(7, a = 8) +method(a = 9, 10) +method(11, b = 12) +method(b=13, 14) +# Scenario: both have default values +# Default: pass + +def method(a, b=-4) + a + b +end + +method(-2) +method(-1, 0) +method(a = 1) +method(a = 3, b = 4) +method(b = 5, a = 6) + +method(7, a = 8) +method(a = 9, 10) +method(11, b = 12) +method(b=13, 14) +# Scenario: the second one has default value (positive) +# Default: pass + +def method(a, b=-4) + a + b +end +method +# Scenario: the second one has default value (negative 1): in `method': wrong number of arguments (0 for 1) +# Default: ArgumentError +# ParseOnly: pass + +def method(a, b=-4) + a + b +end +method(b = 2) # returns 2+(-4) = -2 +# Scenario: the second one has default value (negative 2 ??) +# Default: pass + +#* value +# value can be any expression? + +def method(c="string", d = 1.to_s()) + c + d +end +method +method(1, 2) +# Scenario: valid: string, expression +# Default: pass + +def method(c=c) +end +method(10) +# Scenario: value is itself +# Default: pass + +def method(c=-1, d=c) + puts c, d + c + d +end +method +method(1) +method(2, 3) +method(c=4) +method(d=5) +method(c=6, d=7) +method(d=8, c=9) +# Scenario: default value is another parameter +# Default: pass + +def method(c=-1, d=c+5) + c + d +end +method +method(1) +method(2, 3) +method(c=4) +method(d=5) +method(c=6, d=7) +method(d=8, c=9) +# Scenario: default value is expression with another parameter +# Default: pass + +$a = 10 +def method(c=-1, d=$a) + puts c, d + c + d +end +method +method(1) +method(2, 3) +method(c=4) +method(d=5) +method(c=6, d=7) +method(d=8, c=9) +# what is "a" now? +# Scenario: default value is another variable +# Default: pass + +def method(c=d, d=4) + c+d +end +method(3) +method(6, 7) +# Scenario: ok to define: parameter order +# Default: pass + +def method(c=d, d=4) + c+d +end +method +# Scenario: undefined local variable or method `d' for main:Object +# Default: NameError +# ParseOnly: pass diff --git a/merlin/main/languages/ruby/Tests/Syntax/method_nested.rb b/merlin/main/languages/ruby/Tests/Syntax/method_nested.rb new file mode 100644 index 0000000000..90e5ffff7e --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/method_nested.rb @@ -0,0 +1,80 @@ +# nested method definition + +#* syntax + +def method def nested end end +# Scenario: same line +# Default: syntax error + +def method def nested end +end +# Scenario: unknown +# Default: syntax error + +def method def nested +end end +# Scenario: +# Default: syntax error + +def method def nested +end +end +# Scenario: +# Default: syntax error + +def method def + nested end end +# Scenario: +# Default: syntax error + +def method def +nested end +end +# Scenario: unknown +# Default: syntax error + +def method def + nested +end +end +# Scenario: +# Default: syntax error + +def method + def + nested +end +end +method +# Scenario: unknown +# Default: pass + +def method + def + nested +end +method +# Scenario: missing end +# Default: syntax error + + +def method + def + nested +end +end +end +method +# Scenario: extra end +# Default: syntax error + +def method + a = 1 + def + nested + end + nested +end +method +# Scenario: unknown +# Default: pass diff --git a/merlin/main/languages/ruby/Tests/Syntax/method_parameter.rb b/merlin/main/languages/ruby/Tests/Syntax/method_parameter.rb new file mode 100644 index 0000000000..d6718bf6f3 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/method_parameter.rb @@ -0,0 +1,138 @@ +#* syntax + +#normal +def method a +end +method 1 + +def method a, b +end +method 1, 2 + +def method a, + b +end +method 1, 2 + +def method (a, + b) + a+b +end +method 1, 2 + +def method ( + a, + b) + a+ b +end +method(1, 2) +# Scenario: parameter position: +# Default: pass + +def method a, +end +# Scenario: extra comma, without parenthesis +# Default: syntax error + +def method(a,) +end +# Scenario: extra comma, with parenthesis +# Default: syntax error + +def method a b +end +# Scenario: missing comma +# Default: syntax error + +b = 10 +def method a b +end +# Scenario: missing comma, but the next one is valid symbol +# Default: syntax error + +#* paramname +def method("abc") +end +# Scenario: string literal as param name +# Default: syntax error + +def method(1) +end +# Scenario: digit as param name +# Default: syntax error + +#* parentheses +def method (a, b) + a+b +end +# Scenario: space is ok between method name and ( +# Default: pass + +def method(a, b) + a + b +end +method (1, 2) # warning +# Scenario: warning expected +# Default: don't put space before argument parentheses + + +def method(a, b) + a + b +end +method(1,2) +# Scenario: normal with parentheses +# Default: pass + +def method a, b + a + b +end +method(1,2) + +def method a + a * 2 +end +method(2) +# Scenario: normal without parentheses +# Default: pass + +#* samename + +def method a, a + 1 +end +# Scenario: same name +# Default: duplicate parameter name + +def method a, b, a + 1 +end +# Scenario: same name again +# Default: duplicate parameter name + +def method a, b, a=10 + 1 +end +# Scenario: with default value +# Default: duplicate optional argument name +# ParseOnly: duplicate parameter name + +def method a, b, c, *b + 1 +end +# Scenario: with varargs (??) +# Default: pass +# ParseOnly: duplicate parameter name +## COMPAT: duplicate rest argument name + +def method a, b, c, d, &c + 1 +end +# Scenario: with block +# Default: duplicate block argument name +# ParseOnly: duplicate parameter name + +def method(method) + 1 +end +# Scenario: same as method name (??) +# Default: pass diff --git a/merlin/main/languages/ruby/Tests/Syntax/method_return.rb b/merlin/main/languages/ruby/Tests/Syntax/method_return.rb new file mode 100644 index 0000000000..54601b6428 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/method_return.rb @@ -0,0 +1,28 @@ + +def m + 1, +end + +# Scenario: comma at the end +# Default: syntax error + +def m + (1, ) +end + +# Scenario: parenthesis around; simply (1) is ok +# Default: syntax error + +def m + 1, 2 +end + +# Scenario: return a python-like tuple +# Default: syntax error + +def m + (1, 2) +end + +# Scenario: return a python-like tuple +# Default: syntax error \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Syntax/method_varargs.rb b/merlin/main/languages/ruby/Tests/Syntax/method_varargs.rb new file mode 100644 index 0000000000..2c52ede058 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/method_varargs.rb @@ -0,0 +1,96 @@ +#* syntax + +def method * + a +end +method +# Scenario: parameter name in different line with * (w/o parenthesis) +# Default: pass + +def method(* + a) +end +method +# Scenario: parameter name in different line with * (w/ parenthesis) +# Default: pass + +def method(*a=10) +end +# Scenario: with default value +# Default: syntax error + +def method(**a) +end +# Scenario: with double star +# Default: syntax error + +def method(*a) + a +end +method +method(1) +method(2, 3) +method([]) +method(*[]) +method([4,5]) +method(*[6,7]) +method([8], 9) +# method(*[10, 11], 12) ?? +# Scenario: only one parameter, which is vararg +# Default: pass + +def method(*a, b) +end +# Scenario: two parameters, vararg first +# Default: syntax error + +def method(a, *b) + b +end +method(1) +method(1, 2) +method(1, 3, 4) +method(1, [5, 6]) +method(1, [7], 8) +# Scenario: two parameters, vararg last +# Default: pass + +def method(*a, b=2) +end +# Scenario: two parameters, vararg first, then default value +# Default: syntax error + +def method(a=2, *b) + b +end +method(1) +method(1, 2) +method(1, 3, 4) +method(1, [5, 6]) +method(1, [7], 8) +# Scenario: two parameters, vararg last +# Default: pass + +def method(a=2, b, *c) + return a, b, c +end +# Scenario: three parameters: b have been first? +# Default: syntax error + +def method(a, b=-2, *c) +end +method +# Scenario: three parameters: in `method': wrong number of arguments (0 for 1) +# Default: ArgumentError +# ParseOnly: pass + +def method(a, b=-2, *c) + return a, b, c +end +method(1) +method(2, 3) +method(4, 5, 6) +method(c=[8, 9], a=1) +method(1, 2, c=[10]) +# Scenario: three parameters (positive) +# Default: pass diff --git a/merlin/main/languages/ruby/Tests/Syntax/parseonly.py b/merlin/main/languages/ruby/Tests/Syntax/parseonly.py new file mode 100644 index 0000000000..011e854ea8 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/parseonly.py @@ -0,0 +1,84 @@ +import clr +import sys + +clr.AddReference("Microsoft.Scripting.Core") +clr.AddReference("Microsoft.Scripting") +clr.AddReference("IronRuby") + +from System.IO import File, Directory, Path +from System import Console, String +from Microsoft.Scripting.Hosting import ErrorListener, ScriptRuntime, ScriptRuntimeSetup + +def all_files(root): + for x in Directory.GetFiles(root): + yield Path.Combine(root, x) + + dirs = Directory.GetDirectories(root) + for d in dirs: + for x in all_files(d): + yield x + +class Logger(ErrorListener): + def __init__(self): + self.error = "" + + def ErrorReported(self, source, message, span, errorCode, severity): + print self.error + self.error += String.Format("{0}({1}:{2}): {3}: RB{4}: {5}", source.Path, span.Start.Line, span.Start.Column, severity, errorCode, message) + +rb = ScriptRuntime(ScriptRuntimeSetup.ReadConfiguration()).GetEngine("rb") + +import re +def extract_expected(content): + lines = content.split("\n") + pattern = re.compile(r"^#(.+):(.+)") + rules = {} + for l in lines: + mo = pattern.match(l) + if mo: + rules[mo.group(1).strip().lower()] = mo.group(2).strip() + if rules.has_key("parseonly"): + return rules["parseonly"] + return rules["default"] + +failures = 0 +skips = 0 + +for f in all_files(sys.argv[1]): + log = Logger() + content = File.ReadAllText(f) + + if "merlin_bug" in content: + sys.stdout.write("\ns( %s )" % f) + skips += 1 + continue + + source = rb.CreateScriptSourceFromString(content) + try: + source.Compile(log) + except: + failures += 1 + sys.stdout.write("\nC( %s )" % f) + else: + actual = log.error + expected = extract_expected(content) + + if expected == "pass": + if actual == "": + sys.stdout.write("+") + else: + failures += 1 + sys.stdout.write("\nX( %s )" % f) + sys.stdout.write("\n>> %s | %s" % (expected, actual )) + else: + if expected in actual: + sys.stdout.write("-") + else: + failures += 1 + sys.stdout.write("\n/( %s )" % f) + sys.stdout.write("\n>> %s | %s" % (expected, actual )) + +print "\n\nfailures: %s, skips: %s \n" % (failures, skips) +sys.exit(failures) + + diff --git a/merlin/main/languages/ruby/Tests/Syntax/range.rb b/merlin/main/languages/ruby/Tests/Syntax/range.rb new file mode 100644 index 0000000000..8fa7806c69 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/range.rb @@ -0,0 +1,87 @@ +## inclusive range + +10..30 +10.1..20 +10..20.5 +-10..30 +10..-40 +-40..-50 +-60..-10 + +'a'..'z' +'Z'..'A' + +x = 9 +x..x +x..-x +# Scenario: valid +# Default: pass + +10 +..15 +# Scenario: line starts with .. +# Default: syntax error + +10.. +20 +10 .. +20 +# Scenario: new line +# Default: pass + +10. .20 +# Scenario: space inside +# Default: syntax error + +10 .. 30 +10.. 40 +10 ..50 +# Scenario: space +# Default: pass + +x = [] +x..10 +# Scenario: array as range start +# Default: ArgumentError +# ParseOnly: pass + +## exclusive range + +10...20 +20...10 +10.20...-433.03 +# Scenario: valid +# Default: pass + +10 +...20 +# Scenario: newline +# Default: syntax error + +10 ... +-30 +# Scenario: newline +# Default: pass + +10.. .20 +# Scenario: space inside +# Default: syntax error + +10. ..20 +# Scenario: space inside +# Default: syntax error + +10 ... 20 +10... 30 +10 ...40 +# Scenario: space +# Default: pass + +10..20.30 +# Scenario: valid +# Default: pass + +10..20..30 +# Scenario: invalid +# Default: pass + diff --git a/merlin/main/languages/ruby/Tests/Syntax/run_syntax.rb b/merlin/main/languages/ruby/Tests/Syntax/run_syntax.rb new file mode 100644 index 0000000000..b9aa8e844b --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Syntax/run_syntax.rb @@ -0,0 +1,530 @@ +require "../common" + +if TestPath.get_environment_variable("MERLIN_ROOT").nil? + print "Skipping syntax test for now ... \n" + exit(0) +end + +class IntializationError +end + +class StateMachine + def initialize + @handlers = [] + @startState = nil + @endStates = [] + end + + def add_state(handler, end_state=false) + @handlers << handler + @endStates << handler if end_state + end + + def set_start(handler) + @startState = handler + end + + def run(target, line=nil) + raise IntializationError if @startState.nil? + raise IntializationError if @endStates.size == 0 + + handler = target.method(@startState) + while true + (newState, line) = handler.call(line) + if @endStates.include?(newState) + target.method(newState).call(line) + break + elsif not @handlers.include?(newState) + raise "Invalid target" + else + handler = target.method(newState) + end + end + end +end + +class LineParser + def initialize(file_name) + @stateMachine = StateMachine.new() + @file_name = file_name + @fp = File.open(file_name) # not close it explicitly yet + end + + def safe_readline + begin + line = @fp.readline() + rescue EOFError + return false + else + return line + end + end + + def eof(line) + # nothing + end + + def process + prepare + @stateMachine.run(self, "no effect") + end +end + +class SimpleParser < LineParser + attr_accessor :code_cb, :comment_cb + REGEX_COMMENT = /^\#/ + + def initialize(file_name) + super + end + + def prepare + @stateMachine.add_state(:eof, true) + @stateMachine.add_state(:code) + @stateMachine.add_state(:comment) + @stateMachine.add_state(:first_state) + @stateMachine.set_start(:first_state) + end + + def first_state(line) + line = safe_readline + return [:eof, line] unless line + if line =~ REGEX_COMMENT + return [:comment, line] + else + return [:code, line] + end + end + + def code(line) + while true + @code_cb.call(line) if @code_cb + line = safe_readline + return [:eof, line] unless line + return [:comment, line] if line =~ REGEX_COMMENT + end + end + + def comment(line) + while true + @comment_cb.call(line) if @comment_cb + line = safe_readline + return [:eof, line] unless line + return [:code, line] if line !~ REGEX_COMMENT + end + end +end + +def test_SimpleParser + p = SimpleParser.new(ARGV[0]) + p.code_cb = proc { |line| printf "CODE |%s", line } + p.comment_cb = proc { |line| printf "COMMENT |%s", line } + p.process +end + +class CodeSplitter < LineParser + attr_accessor :patched_code_handler, :unpatched_code_handler + + def initialize(file_name) + @count = 0 + @code_snippet = [] + super(file_name) + end + + def prepare + @stateMachine.add_state(:eof, true) + @stateMachine.add_state(:code) + @stateMachine.add_state(:patched) + @stateMachine.add_state(:unpatched) + @stateMachine.add_state(:first_state) + @stateMachine.set_start(:first_state) + end + + def first_state(line) + line = safe_readline + return [:eof, line] unless line + if line =~ /^\# Scenario/ + return [:patched, line] + elsif line =~ /^\#(\+|-)/ + return [:unpatched, line] + else + return [:code, line] + end + end + + def code(line) + while true + @code_snippet << line + line = safe_readline + return [:eof, line] unless line + return [:patched, line] if line =~ /^\# Scenario/ + return [:unpatched, line] if line =~ /^\#(\+|-)/ + end + end + + def patched(line) + _code(line, true) + end + + def unpatched(line) + _code(line, false) + end + + def _code(line, patched = true) + while true + @code_snippet << line + line = safe_readline + + if line and line =~ /^\#/ + next + else + if patched + patched_code_handler.call(@code_snippet) if patched_code_handler + else + unpatched_code_handler.call(@code_snippet) if unpatched_code_handler + end + @code_snippet = [] + return [:eof, line] unless line + return [:code, line] + end + end + end +end + +class SnippetShow + def run(test) + csp = CodeSplitter.new(test) + csp.patched_code_handler = proc { |snippet| puts "+" * 50; snippet.each { |l| printf("P |%s", l) } } + csp.unpatched_code_handler = proc { |snippet| puts "-" * 50; snippet.each { |l| printf("U |%s", l) } } + csp.process + end +end + +def test_SnippetShow + rp = SnippetShow.new() + rp.run(ARGV[0]) +end + +class ResultPatcher + attr_reader :total, :failure + def initialize(name, command) + @name = name + @command = command + @total = @failure = 0 + end + + def get_output(snippet) + temp_file = "temp.rb" + File.open(temp_file, "w") do |f| + snippet.each { |l| f << l } + end + + x = IO.popen("#@command #{temp_file} 2>&1", 'r+') do |io| + io.read + end + x.chomp + end + + def verify_patched_code(snippet) + output = get_output(snippet) + + # parse + h = { } + snippet.each do |l| + if l =~ /# (.+): (.+)/ + h[$1.downcase] = $2 + end + end + + if h.has_key?(@name.downcase) == false + append(snippet, output) + return + end + + expected = h[@name.downcase] + + rewrite = false + if $?.exitstatus == 0 # success + if expected != "pass" + rewrite = true + end + else # fail + if output.include?(expected) == false # but can not find the expected string + rewrite = true + end + end + + if rewrite + rewrite(snippet, output) + else + repeat(snippet) + end + end + + def repeat(snippet) + snippet.each { |line| @new_fp << line } + end + + def append(snippet, output) + snippet.each { |line| @new_fp << line } + @new_fp << "\# #{@name}: #{capture_result(output)}\n" + end + + def rewrite(snippet, output) + snippet.each do |line| + if line =~ /^# Default/ + @new_fp << "\# #{@name}: #{capture_result(output)}\n" + else + @new_fp << line + end + end + end + + def verify_unpatched_code(snippet) + output = get_output(snippet) + + comment = "unknown" + snippet.each do |l| + if l =~ /#\+(.+)/ + expected = true + comment = $1 + break + elsif l =~ /#-(.+)/ + expected = false + comment = $1 + break + end + end + + snippet.each do |l| + if l !~ /#(\+|-)/ + @new_fp << l + else + @new_fp << "\# Scenario: #{comment.strip}\n" + @new_fp << "\# #{@name}: " + @new_fp << ($?.exitstatus == 0 ? "pass" : capture_result(output)) + @new_fp << "\n" + end + end + end + + ERROR_MESSAGES = [ + 'syntax error', + 'ArgumentError', + 'NameError', + 'TypeError', + 'unterminated', + 'class/module name must be CONSTANT', + 'duplicate argument name', + 'duplicate optional argument name', + 'duplicate block argument name', + 'odd number list for Hash', + ] + + def capture_result(s, verbatim = false) + if verbatim + s.gsub("\n", "\\n") + else + last = s.index('\n') + if last + first_line = s[0...last] + + else + first_line = s + end + x = ERROR_MESSAGES.find { |m| first_line.include?(m) } + if x + x + else + s.gsub("\n", "\\n") + end + end + end + + def run(test) + csp = CodeSplitter.new(test) + @new_fp = File.open(test + "2", "w") + csp.patched_code_handler = proc { |snippet| verify_patched_code(snippet) } + csp.unpatched_code_handler = proc { |snippet| verify_unpatched_code(snippet) } + csp.process + end +end + +class TestRunner + attr_reader :total, :failure + def initialize(name, command) + @name = name + @command = command + @total = @failure = 0 + end + + def get_directory_hint(file_name) + pos = 0 + while pos = file_name.index(/_|\./, pos+1) do + dir_name = file_name[0...pos].gsub(/_/, "\\") + yield(dir_name) + end + dir_name + end + + def get_generated_file_name(dir_name) + @count += 1 + dir_name + "\\" + "%02d" % @count + ".g.rb" + end + + def get_output(snippet) + @generated = get_generated_file_name(@dir_name) + + File.open(@generated, "w") do |f| + snippet.each { |l| f << l } + end + + @total += 1 + x = IO.popen("#@command #{@generated} 2>&1", 'r+') do |io| + io.read + end + x.chomp + end + + def test_patched_code(snippet) + # parse + h = { } + snippet.each do |l| + if l =~ /# (.+): (.+)/ + h[$1.downcase] = $2 + end + end + + if h.has_key?(@name.downcase) + if h[@name.downcase].include? "merlin_bug" + printf "S" + return + end + end + + output = get_output(snippet) + + if not h.has_key?(@name.downcase) + if h.has_key?("default") + expected = h["default"] + else + raise "Can not find the expected behavior: %s\n" % @generated + end + else + expected = h[@name.downcase] + end + if $?.exitstatus == 0 # success + if expected != "pass" + @failure += 1 + printf "expected pass, but not. %s\n", @generated + else + printf "+" + end + else # fail + if output.include?(expected) == false # but can not find the expected string + @failure += 1 + printf "expected fail, but not. %s\n", @generated + else + printf "-" + end + end + end + + def test_unpatched_code(snippet) + raise "update tests" + end + + def run(test) + @rb_file = test + @count = 0 + @dir_name = get_directory_hint(@rb_file) do |d| + Dir.mkdir(d) unless File.exist? d + end + + csp = CodeSplitter.new(test) + csp.patched_code_handler = proc { |snippet| test_patched_code(snippet) } + csp.unpatched_code_handler = proc { |snippet| test_unpatched_code(snippet) } + csp.process + end +end + +# ruby.exe driver.rb -patch -driver:Default [file] +# ruby.exe driver.rb -patch -driver:ParseOnly [file] +# ruby.exe driver.rb -test -driver:Default [file] +# ruby.exe driver.rb -test -driver:ParseOnly [file] + +processor = TestRunner +driver_name = "default" +test_files = [] +not_run_list = ["run_syntax.rb", "parseonly.py"] +driver_list = { + "default" => TestPath::CRUBY_EXE, +} + +ARGV.each do |arg| + if arg =~ /-patch/i + processor = ResultPatcher + elsif arg =~ /-test/i + processor = TestRunner + elsif arg =~ /-driver:(.+)/i + driver_name = $1 + elsif arg =~ /-snap/ + else + test_files << arg + end +end + +if test_files.empty? + Dir.glob("*.rb") do |f| + test_files << f unless not_run_list.include? f + end +end + +printf "Start @ %s\n\n", Time.now + +if ARGV.include? "-snap" + class SnippetGenerator + def run(test) + count = 0 + csp = CodeSplitter.new(test) + + csp.patched_code_handler = proc { |snippet| + file_name = File.dirname(File.expand_path(test)) + "/generated/" + File.basename(test, ".rb") + "_%02d" % count + ".g.rb" + open(file_name, "w") do |f| + f << snippet + count += 1 + end + } + csp.unpatched_code_handler = proc { |snippet| raise "unexpected" } + csp.process + end + end + + gendir = File.join(TestPath::TEST_DIR, "syntax/generated") + Dir.mkdir(gendir) unless File.exists? gendir + Dir.glob(gendir + "/*.rb") { |f| File.delete f } + + cs = SnippetGenerator.new + test_files.each do |f| + puts f + cs.run(f) + end + + cmd = "#{TestPath::IPYTHON_EXE} parseonly.py #{TestPath::TEST_DIR}/syntax/generated > parsing.log" + print "\n\nRunning #{cmd} ... \n" + system(cmd) + exit($?.exitstatus) +else + printf "Current mode : %s\n", processor + printf "Current driver : %s\n", driver_name + + rp = processor.new(driver_name, driver_list[driver_name.downcase]) + test_files.each do |f| + printf "\n>>> testing %s\n", f + rp.run(f) + end + + printf "\n\nFinish @ %s\n", Time.now + printf "\nSummary [Total: %d, failure: %d]\n", rp.total, rp.failure + exit(rp.failure) +end diff --git a/merlin/main/languages/ruby/Tests/Tools/ParseOnly/ParseOnly.csproj b/merlin/main/languages/ruby/Tests/Tools/ParseOnly/ParseOnly.csproj new file mode 100644 index 0000000000..2218cf6445 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Tools/ParseOnly/ParseOnly.csproj @@ -0,0 +1,63 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {AE2EEA3B-3615-4188-AEE3-E4956788585C} + Exe + Properties + ParseOnly + ParseOnly + SAK + SAK + SAK + SAK + + + true + full + false + ..\bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\Bin\Debug\Microsoft.Scripting.Core.dll + + + False + ..\..\..\..\..\Bin\Debug\Microsoft.Scripting.dll + + + False + ..\..\..\..\..\Bin\Debug\Ruby.dll + + + + + + + + + + + + \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Tools/ParseOnly/ParseOnly.csproj.vspscc b/merlin/main/languages/ruby/Tests/Tools/ParseOnly/ParseOnly.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Tools/ParseOnly/ParseOnly.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/merlin/main/languages/ruby/Tests/Tools/ParseOnly/Program.cs b/merlin/main/languages/ruby/Tests/Tools/ParseOnly/Program.cs new file mode 100644 index 0000000000..9aeb325bea --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Tools/ParseOnly/Program.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; + +using Ruby; +using Ruby.Hosting; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Ruby.Compiler; +using Ruby.Compiler.AST; +using System.IO; + +namespace ParseOnly { + class Program { + class LoggingErrorSink : ErrorSink { + public override void Add(SourceUnit sourceUnit, string message, SourceSpan span, int errorCode, Severity severity) { + base.Add(sourceUnit, message, span, errorCode, severity); + + Console.Error.WriteLine("{0}({1}:{2}): {3}: RB{4}: {5}", sourceUnit.Name, span.Start.Line, span.Start.Column, + severity, errorCode, message); + } + } + + private static RubyEngine RB { get { return (RubyEngine)GetEngine("rb"); } } + + private static ScriptEngine GetEngine(string id) { + return ScriptDomainManager.CurrentManager.GetLanguageProvider(id).GetEngine(); + } + + static int Main(string[] args) { + SourceUnit unit = new SourceCodeUnit(RB, File.ReadAllText(args[0])); + LoggingErrorSink log = new LoggingErrorSink(); + new Parser().Parse(new CompilerContext(unit, new RubyCompilerOptions(), log)); + return log.ErrorCount + log.FatalErrorCount; + } + } +} diff --git a/merlin/main/languages/ruby/Tests/Tools/ParseOnly/Properties/AssemblyInfo.cs b/merlin/main/languages/ruby/Tests/Tools/ParseOnly/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..5b3d3a4049 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Tools/ParseOnly/Properties/AssemblyInfo.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Ruby.Runtime; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ParseOnly")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ParseOnly")] +[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e2b19010-65c7-4e92-b3f4-1cdb932d8a65")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion(RubyContext.IronRubyVersionString)] +[assembly: AssemblyFileVersion(RubyContext.IronRubyVersionString)] diff --git a/merlin/main/languages/ruby/Tests/Util/assert.rb b/merlin/main/languages/ruby/Tests/Util/assert.rb new file mode 100644 index 0000000000..7b96aad30e --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Util/assert.rb @@ -0,0 +1,91 @@ +def log_error(message) + puts message + raise +end + +def assert(boolean, message) + if boolean != true + log_error(message) + end +end + +def assert_unreachable() + assert(false, "should not be reachable") +end + +def assert_should_have_thrown(expected_exception) + assert(false, "should have thrown: " + expected_exception.to_s) +end + +def assert_equal(left, right, message="left: #{left}, right: #{right}") + assert(left == right, message) +end + +def assert_pair_equal *args + arg_count = args.length + log_error("Odd argument count : {#arg_count}") unless arg_count % 2 == 0 + 0.step arg_count, 2 do |i| + assert_equal(args[i], args[i+1], message="pair #{i} not equal") + end +end + +def assert_nil(obj, message=nil) + assert_equal(obj, nil, message) +end + +def assert_not_nil(obj, message = "should be not nil") + assert(obj != nil, message) +end + +def assert_isinstanceof(obj, kclass, message=nil) + assert_equal(obj.class, kclass, message) +end + +def assert_raise(expected_exception, expected_message=nil) + begin + yield + rescue Exception => actual_exception_obj + assert_isinstanceof( + actual_exception_obj, expected_exception, + "expect #{expected_exception}, but get #{actual_exception_obj.class}" + ) + if expected_message + # assert_equal(actual_exception_obj.message, expected_message) + end + else + assert_should_have_thrown(expected_exception) + end +end + +def assert_return(expected) + actual = yield + assert_equal(expected, actual) +end + +def assert_true(&b) + assert_return(true, &b) +end + +def assert_false(&b) + assert_return(false, &b) +end + +def divide_by_zero + 1/0 +end + +def empty_func +end + +def runtest(m, skiplist=[]) + m.private_methods.each do |m| + if m[0,5] == "test_" + puts "testing #{m}... " + if skiplist.include? m + puts "skipped" + else + m.method(m).call + end + end + end +end \ No newline at end of file diff --git a/merlin/main/languages/ruby/Tests/Util/exit.rb b/merlin/main/languages/ruby/Tests/Util/exit.rb new file mode 100644 index 0000000000..5a8383bc61 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Util/exit.rb @@ -0,0 +1,2 @@ +1+1 == 2 +exit diff --git a/merlin/main/languages/ruby/Tests/Util/simple_test.rb b/merlin/main/languages/ruby/Tests/Util/simple_test.rb new file mode 100644 index 0000000000..ae10f3a4cb --- /dev/null +++ b/merlin/main/languages/ruby/Tests/Util/simple_test.rb @@ -0,0 +1,126 @@ +$error_count = 0 +$error_list = [] + +class PositiveExpectation + def initialize(obj) + @obj = obj + end + + def ==(other) + if @obj != other + msg = "Equality expected for #{@obj.inspect} and #{other.inspect}" + $error_count += 1 + $error_list << msg + raise Exception.new(msg) + else + print '.' + end + end + + def equal?(other) + if not @obj.equal?(other) + msg = "Reference equality expected for #{@obj.inspect} and #{other.inspect}" + $error_count += 1 + $error_list << msg + raise Exception.new(msg) + else + print '.' + end + end +end + +class NegativeExpectation + def initialize(obj) + @obj = obj + end + + def ==(other) + if @obj == other + msg = "Inequality expected for #{@obj.inspect} and #{other.inspect}" + $error_count += 1 + $error_list << msg + raise Exception.new(msg) + else + print '.' + end + end + + def equal?(other) + if @obj.equal?(other) + msg = "Reference inequality expected for #{@obj.inspect} and #{other.inspect}" + $error_count += 1 + $error_list << msg + raise Exception.new(msg) + else + print '.' + end + end +end + +class Object + def should + PositiveExpectation.new(self) + end + + def should_not + NegativeExpectation.new(self) + end +end + +def it(name) + print "\n it #{name}: " + $name = name + yield +end + +def skip(name) + print "\n **skipping** #{name}" +end + +def describe(message) + puts "\n\n#{message}" + yield +end + +def should_raise(expected_exception, expected_message=nil) + begin + yield + msg = "#{$name} failed! expected #{expected_exception}, but no error happened" + $error_count += 1 + $error_list << msg + puts msg + rescue Exception => actual_exception + if expected_exception.name != actual_exception.class.name + msg = "#{$name} failed! expected #{expected_exception} but got #{actual_exception}" + $error_count += 1 + $error_list << msg + puts msg + elsif expected_message != nil and actual_exception.message != expected_message + msg = "#{$name} failed! expected message #{expected_message} but got #{actual_exception.message}" + $error_count += 1 + $error_list << msg + puts msg + else + print '.' + end + end +end + +def finished + if $error_count > 0 + puts "\n\nErrors:" + $error_list.each { |msg| puts msg } + end + puts "\n\nTests failed == #{$error_count}" +end + +def specify(name) + print "\n specify #{name}: " + $name = name + yield +end + +def context(message) + puts "\n\n#{message}" + yield +end diff --git a/merlin/main/languages/ruby/Tests/common.rb b/merlin/main/languages/ruby/Tests/common.rb new file mode 100644 index 0000000000..3e7f51dbc7 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/common.rb @@ -0,0 +1,181 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +module TestPath + def TestPath.get_environment_variable(x) + ENV[x.to_s] + end + + def TestPath.get_directory_name(dir) + File.basename dir + end + + def TestPath.get_directory(dir) + File.dirname dir + end + + if get_environment_variable('MERLIN_ROOT') + MERLIN_ROOT = get_environment_variable('MERLIN_ROOT') + TEST_DIR = MERLIN_ROOT + "/Languages/Ruby/Tests" + CORECLR_ROOT = MERLIN_ROOT + "/Utilities/Silverlight/x86ret" + CRUBY_EXE = MERLIN_ROOT + "/../External/Languages/Ruby/ruby-1.8.6/bin/ruby.exe" + + ROWAN_BIN = get_environment_variable('ROWAN_BIN') + + # assume we are running inside snap + if ROWAN_BIN + IRUBY_EXE = MERLIN_ROOT + "/Test/Scripts/ir.cmd" + IPYTHON_EXE = ROWAN_BIN + "/ipy.exe" + else + IRUBY_EXE = MERLIN_ROOT + "/Test/Scripts/ir.cmd" + IPYTHON_EXE = MERLIN_ROOT + "/bin/debug/ipy.exe" + end + + PARSEONLY_EXE = IPYTHON_EXE + " " + TEST_DIR + "/Tools/parseonly.py " + else + TEST_DIR = File.expand_path(File.dirname(__FILE__)) + MERLIN_ROOT = File.dirname(File.dirname(TEST_DIR)) + IRUBY_EXE = MERLIN_ROOT + "/Test/Scripts/ir.cmd" + CRUBY_EXE = "ruby.exe" + + IPYTHON_EXE = nil + PARSEONLY_EXE = nil + end + + # generate logs to TestResults directory, so they will be copied to the snap server + TEMP = get_environment_variable("INSTALLROOT") + RESULT_DIR = TEMP ? File.join(TEMP, "TestResults") : TEST_DIR +end + +module Test + class Logger + attr_reader :log_name + + def initialize(prefix) + current = Time.now.strftime("%m%d%H%M") + @log_name = File.join(TestPath::RESULT_DIR, "#{prefix}_#{current}.log") + end + + def append(*lines) + lines.each do |line| + open(@log_name, "a+") { |f| f << line << "\n" } + end + end + + def to_s + @log_name + end + end + + + class BaseDriver + attr_reader :name, :logger, :redirect_error, :append_to_log + + def initialize(name, redirect_error=true, append_to_log=true) + @name = name.downcase + @logger = Logger.new(name) + + @redirect_error = redirect_error + @append_to_log = append_to_log + @cmd_line = nil + end + + def change_dir_to_file(f) + saved = Dir.pwd + d = File.dirname(File.expand_path(f)) + Dir.chdir(d) + saved + end + + def run(f, log_file) + begin + saved = change_dir_to_file(f) + cmd_line = get_command_line(f) + if log_file + cmd_line << " #{@append_to_log ? ">>" : ">"} #{log_file} #{@redirect_error ? "2>&1" : ""}" + end + @logger.append("cd /d #{saved}", cmd_line) + system(cmd_line) + return $?.exitstatus + ensure + Dir.chdir(saved) + end + end + end + + class CRubyDriver < BaseDriver + def initialize(redirect_error=true, append_to_log=true) + super("cruby", redirect_error, append_to_log) + end + + def get_command_line(f) + "#{TestPath::CRUBY_EXE} -W0 #{File.basename(f)}" + end + + def to_s + "CRubyDriver" + end + end + + class IronRubyDriver < BaseDriver + attr_reader :mode + + @@mode_mapping = { + 1 => "-D -X:Interpret", + 2 => "-D", + 3 => "-D -X:SaveAssemblies", + } + + def initialize(mode, name, redirect_error=true, append_to_log=true) + @mode_string = @@mode_mapping[mode] + super(name, redirect_error, append_to_log) + end + + def get_command_line(f) + "#{TestPath::IRUBY_EXE} #{@mode_string} #{File.basename(f)}" + end + + def to_s + "IronRubyDriver ( #{TestPath::IRUBY_EXE} #{@mode_string} )" + end + end + + class CoreClrDriver < BaseDriver + def initialize(redirect_error=true, append_to_log=true) + super("coreclr", redirect_error, append_to_log) + end + + def run(f, logfile) + begin + f = File.expand_path(f) + saved = Dir.pwd + Dir.chdir(TestPath::CORECLR_ROOT) + cmd_line = "fxprun.exe thost.exe -fxprun_byname /nologo /lang:rb /run:#{f} /paths:#{File.dirname(f)} #{@append_to_log ? ">>" : ">"} #{logfile} #{@redirect_error ? "2>&1" : ""}" + @logger.append("cd /d #{TestPath::CORECLR_ROOT}", cmd_line) + system(cmd_line) + return $?.exitstatus + ensure + Dir.chdir(saved) + end + end + end + + # const + CRuby = CRubyDriver.new + Iron_m1 = IronRubyDriver.new(1, 'ironm1') + Iron_m2 = IronRubyDriver.new(2, 'ironm2') + Iron_m3 = IronRubyDriver.new(3, 'ironm3') + Iron_cc = CoreClrDriver.new +end diff --git a/merlin/main/languages/ruby/Tests/run.bat b/merlin/main/languages/ruby/Tests/run.bat new file mode 100644 index 0000000000..d1606803dc --- /dev/null +++ b/merlin/main/languages/ruby/Tests/run.bat @@ -0,0 +1,26 @@ +echo Running dev unit test first + +if "%ROWAN_BIN%" == "" ( + %MERLIN_ROOT%\bin\debug\IronRuby.Tests.exe + goto :SUITE +) + +%ROWAN_BIN%\IronRuby.Tests.exe + +:SUITE + +if NOT "%ERRORLEVEL%" == "0" ( + echo At least 1 of dev unit tests failed + exit /b 1 +) + +set RubyOpt_Old=%RubyOpt% +set RubyOpt= + +%MERLIN_ROOT%\..\external\languages\ruby\ruby-1.8.6\bin\ruby.exe %~dp0run.rb -checkin %* +set EXITCODE=%ERRORLEVEL% + +set RubyOpt=%RubyOpt_Old% +set RubyOpt_Old= + +exit /b %EXITCODE% diff --git a/merlin/main/languages/ruby/Tests/run.rb b/merlin/main/languages/ruby/Tests/run.rb new file mode 100644 index 0000000000..4707eac329 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/run.rb @@ -0,0 +1,153 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +trap("INT") { + puts + puts "!!! Interrupt by the user !!!" + exit(-1) +} + +THIS_DIRECTORY = File.expand_path(File.dirname(__FILE__)) +CURR_DIRECTORY = Dir.pwd + +require 'common' +require 'benchmark' + +$failures = 0 + +class TestListFile + def TestListFile.load + test_lst_file = File.join(THIS_DIRECTORY, "test.lst") + lines = '' + + # filter empty line and comment line + File.open(test_lst_file, 'r') do |f| + lines = f.readlines.grep(/^[^#\s]+/) + end + + lines.collect do |l| + parts = l.split + raise "unexpected test entry: #{l}" if parts.length > 2 + TestListFile.new(*parts) + end + end + + def initialize(file_name, driver_list='all') + @file_name = file_name + @driver_list = driver_list + end + + def should_run_with?(driver) + return true if @driver_list == "all" + return false if @driver_list == "none" + if @driver_list.include? '-' + # you do not have to specify "wanted" + @driver_list.include?(driver.name + "-") == false + else + @driver_list.include? driver.name + end + end + + def run_by(driver) + if !should_run_with? driver + print "s" + elsif driver.run(@file_name, driver.logger.log_name) == 0 + print "." + else + $failures += 1 + print "x(#{File.basename(@file_name)})" + end + end + + def run_skipped_by(driver) + if !should_run_with? driver + if driver.run(@file_name, driver.logger.log_name) == 0 + printf "p(%s)", File.basename(@file_name) + else + print "f" + end + end + end +end + +# current options which are running inside SNAP: +# -checkin +# -fast + +# drivers +applicable_drivers = [] +ARGV.each do |option| + case option + when /all/ + applicable_drivers << Test::Iron_m2 << Test::Iron_m3 << Test::Iron_cc + when /coreclr/ + applicable_drivers << Test::Iron_cc + when /fast|interpret/ + applicable_drivers << Test::Iron_m1 + when /compile/ + applicable_drivers << Test::Iron_m2 + when /verify/ + applicable_drivers << Test::Iron_m3 + when /cruby/ + applicable_drivers << Test::CRuby + when /checkin/ + applicable_drivers << Test::Iron_m2 << Test::Iron_m3 << Test::CRuby + when /neg/ + else + p "Invalid option: #{option}" + end +end +applicable_drivers = [ Test::CRuby, Test::Iron_m1 ] if applicable_drivers.empty? +applicable_drivers.uniq! + +test_files = TestListFile::load + +bms = [] +applicable_drivers.each do |driver| + bms << Benchmark.measure(driver.to_s) do + puts "#{driver}" + puts " log @ #{driver.logger} \n" + + if ARGV.include? "-neg" + test_files.each { |tf| tf.run_skipped_by(driver) } + else + test_files.each { |tf| tf.run_by(driver) } + end + puts "\n\n" + end +end + +bms.each { |t| puts t.format("%n\n%10.6r\n") } + +if applicable_drivers.include? Test::Iron_m2 and !ARGV.include? "-fast" + [ + 'syntax', + 'compat' + ].each do |s| + puts ">>> Running #{s} test \n" + ret = Test::CRuby.run(File.join(TestPath::TEST_DIR, "#{s}/run_#{s}.rb -snap "), nil) + if ret == 0 + puts "pass" + else + $failures += ret + puts "fail" + end + puts "\n" + end +end + +puts "\nSummary: #{$failures}\n" + +exit($failures) diff --git a/merlin/main/languages/ruby/Tests/run_builtin.rb b/merlin/main/languages/ruby/Tests/run_builtin.rb new file mode 100644 index 0000000000..1ed8a1e0e8 --- /dev/null +++ b/merlin/main/languages/ruby/Tests/run_builtin.rb @@ -0,0 +1,32 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require 'Find' + +ARGV.each do |type| + Find.find "Builtin/#{type}" do |path| + if ( /_svn/ =~ path ) + Find.prune + end + if ( /_spec\.rb/ =~ path ) then + print path, '(ruby): ' + system("ruby #{path}") + puts + print path, '(ir): ' + system("..\\..\\..\\Test\\Scripts\\ir.cmd #{path}") + puts + end + end +end diff --git a/merlin/main/languages/ruby/Tests/test.lst b/merlin/main/languages/ruby/Tests/test.lst new file mode 100644 index 0000000000..a8ab71ccec --- /dev/null +++ b/merlin/main/languages/ruby/Tests/test.lst @@ -0,0 +1,126 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +# +# format spec: test, drivers, tags +# - drivers: | all | none | cruby | ironm1 | coreclr +# all character !!lowercased!! (ctrl+u) +# + +# dirs: builtin,interop,runtime + +# ##### ### + +# begin +builtin\array\test_array.rb all +builtin\class\test_class.rb all +builtin\enumerable\test_enumerable.rb all +builtin\hash\test_hash.rb all +builtin\module\test_module.rb all +builtin\object\test_objects.rb all +builtin\range\test_range.rb all +builtin\string\test_string.rb all +builtin\stringscanner\test_strscan.rb cruby- +builtin\test_dir.rb coreclr- +# end + +# begin +interop\test_basic.rb ironm3 +# end + +# begin +runtime\block\test_block_given.rb all +runtime\block\test_break.rb all +runtime\block\test_closure.rb all +runtime\block\test_create_invoke.rb all +runtime\block\test_next.rb all +runtime\block\test_redo.rb all +runtime\block\test_retry.rb all +runtime\block\test_return.rb all +runtime\block\test_yield.rb all + +runtime\class\test_access_ctrl.rb cruby +runtime\class\test_change_self.rb all +runtime\class\test_class_variables.rb all +runtime\class\test_class_variables2.rb all +runtime\class\test_const.rb all +runtime\class\test_const_vars_basic.rb all +runtime\class\test_const_vars_in_singleton.rb all +runtime\class\test_declare.rb all +runtime\class\test_find_const_in_methods.rb all +runtime\class\test_find_method.rb all +runtime\class\test_include.rb all +runtime\class\test_instance_variables.rb all +runtime\class\test_methods.rb all +runtime\class\test_nested_const_vars.rb cruby +runtime\class\test_new.rb all +runtime\class\test_self.rb all +runtime\class\test_singleton_basic.rb all +runtime\class\test_top_level.rb all +runtime\class\test_variables.rb all +runtime\class\test_virtual_attr.rb all + +runtime\exception\test_else.rb all +runtime\exception\test_ensure.rb all +runtime\exception\test_final_value.rb all +runtime\exception\test_lifetime.rb all +runtime\exception\test_match.rb all +runtime\exception\test_nested.rb all +runtime\exception\test_raise_nothing.rb all +runtime\exception\test_raise_string.rb all +runtime\exception\test_raise_thing.rb all +runtime\exception\test_regression.rb all +runtime\exception\test_rescue_many.rb all +runtime\exception\test_rescue_modifier.rb all +runtime\exception\test_rescue_nothing.rb all +runtime\exception\test_rescue_sequence.rb all +runtime\exception\test_rescue_unusual.rb all +runtime\exception\test_retry.rb all +runtime\exception\test_return.rb cruby- +runtime\exception\test_scope.rb all + +runtime\expression\test_assignment.rb all +runtime\expression\test_boolean_expr.rb all +runtime\expression\test_break_in_loop.rb all +runtime\expression\test_case.rb all +runtime\expression\test_for_loop.rb all +runtime\expression\test_if.rb all +runtime\expression\test_nested_assignment.rb all +runtime\expression\test_next_in_loop.rb all +runtime\expression\test_parallel_assignment.rb all +runtime\expression\test_range_as_bool.rb all +runtime\expression\test_redo_in_loop.rb all +runtime\expression\test_retry_in_loop.rb all +runtime\expression\test_while_until.rb all + +runtime\method\test_coverage.rb all +runtime\method\test_defaultvalue.rb all +runtime\method\test_mixed_arg.rb all +runtime\method\test_normal_arg.rb all +runtime\method\test_pass_args.rb all +runtime\method\test_recursive.rb all +runtime\method\test_return_value.rb all +runtime\method\test_var_arg.rb all +runtime\method\test_super.rb all + +runtime\module\test_local_in_module.rb all +runtime\module\test_module_action.rb all +runtime\module\test_module_path.rb all +runtime\module\test_require_bad_module.rb none +runtime\module\test_require_cascade.rb none +runtime\module\test_require_itself.rb all +runtime\module\test_require_three_modules.rb all + +# end diff --git a/merlin/main/languages/ruby/Utils/IronRuby.Libraries.Scanner/IronRuby.Libraries.Scanner.csproj b/merlin/main/languages/ruby/Utils/IronRuby.Libraries.Scanner/IronRuby.Libraries.Scanner.csproj new file mode 100644 index 0000000000..2c93043583 --- /dev/null +++ b/merlin/main/languages/ruby/Utils/IronRuby.Libraries.Scanner/IronRuby.Libraries.Scanner.csproj @@ -0,0 +1,87 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {5F692D9C-968B-48BF-AC2F-6B4F54D9C1BB} + Exe + Properties + IronRuby.Libraries.Scanner + IronRuby.Libraries.Scanner + v3.5 + 512 + SAK + SAK + SAK + SAK + 1685 + + + true + full + false + ..\..\..\..\Bin\Debug\ + TRACE;DEBUG;SIGNED + prompt + 4 + + + pdbonly + true + ..\..\..\..\Bin\Release\ + TRACE;SIGNED + prompt + 4 + + + + + 3.5 + SystemCore + + + 3.5 + + + 3.5 + + + + + + + + + + + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4} + Microsoft.Scripting.Core + + + {8B0F1074-750E-4D64-BF23-A1E0F54261E5} + Microsoft.Scripting.ExtensionAttribute + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Scripting + + + {77323B06-15A2-4CF4-8A7A-86EAA2B66498} + IronRuby.Libraries + + + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD} + Ruby + + + + + diff --git a/merlin/main/languages/ruby/Utils/IronRuby.Libraries.Scanner/IronRuby.Libraries.Scanner.csproj.vspscc b/merlin/main/languages/ruby/Utils/IronRuby.Libraries.Scanner/IronRuby.Libraries.Scanner.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/merlin/main/languages/ruby/Utils/IronRuby.Libraries.Scanner/IronRuby.Libraries.Scanner.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/merlin/main/languages/ruby/Utils/IronRuby.Libraries.Scanner/Program.cs b/merlin/main/languages/ruby/Utils/IronRuby.Libraries.Scanner/Program.cs new file mode 100644 index 0000000000..0089629ff1 --- /dev/null +++ b/merlin/main/languages/ruby/Utils/IronRuby.Libraries.Scanner/Program.cs @@ -0,0 +1,208 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using IronRuby; +using Microsoft.Scripting.Utils; +using IronRuby.Runtime; +using IronRuby.Builtins; + +namespace IronRuby.Library.Scanner { + static class ExtensionMethods { + public static IEnumerable SelectCustomAttributes(this Type type) where T : Attribute { + return type.GetCustomAttributes(typeof(T), false).Cast(); + } + + public static T SelectAttribute(this Type type) where T : Attribute { + return type.GetCustomAttributes(typeof(T), false).Cast().First(); + } + + public static bool IsClassOrModule(this Type type) { + if (type.IsDefined(typeof(RubyClassAttribute), false)) { + + RubyClassAttribute classAttr = type.SelectAttribute(); + + // A RubyClass that is explicitly named + if (!String.IsNullOrEmpty(classAttr.Name)) + return true; + + // A RubyClass that defines a self-contained class + if (classAttr.Extends == null) + return true; + + // A RubyClass that includes a module eg ArrayOps + if (type.IsDefined(typeof(IncludesAttribute), false)) + return true; + + // Filters out RubyClasses that extend a type that isn't a RubyClass eg TypeTrackerOps + return classAttr.Extends.IsClassOrModule(); + + } else if (type.IsDefined(typeof(RubyModuleAttribute), false)) { + + // Filter out well-known RubyClass names + RubyModuleAttribute moduleAttr = type.SelectAttribute(); + + if ( moduleAttr.Name == RubyClass.ClassSingletonName + || moduleAttr.Name == RubyClass.ClassSingletonSingletonName + || moduleAttr.Name == RubyClass.MainSingletonName) + return false; + + // Filters out extension modules like ArrayOps + return moduleAttr.Extends == null; + } + return false; + } + + public static IEnumerable SelectCustomAttributes(this MethodInfo method) where T : Attribute { + return method.GetCustomAttributes(typeof(T), false).Cast(); + } + } + + class RubyClassInfo { + public Type ClrType { get; set; } + + public delegate void Block(IEnumerable methods); + + public string Name { + get { + RubyModuleAttribute attr = ClrType.SelectAttribute(); + if (String.IsNullOrEmpty(attr.Name)) { + if (attr.Extends == null) { + return attr.Name ?? ClrType.Name; + } else { + if (attr.Extends.IsDefined(typeof(RubyClassAttribute), false)) { + return attr.Extends.SelectAttribute().Name; + } else { + object x = attr.Extends; + return ""; // String.Empty; + } + } + } else { + return attr.Name; + } + } + } + + private Type LookupExtensionModuleType(Type includeAttrType) { + Type includedType; + Program.ExtensionModules.TryGetValue(includeAttrType, out includedType); + return includedType ?? includeAttrType; + } + + private void GetMethodNames(Type t, Block accumulate) { + var methods = (from m in t.GetMethods() + where m.IsDefined(typeof(RubyMethodAttribute), false) + select m.SelectCustomAttributes()); + + // TODO: is there a LINQ-esque way of flattening these arrays? + var flattened = new List(); + foreach (var methodBlock in methods) { + foreach (var method in methodBlock) { + flattened.Add(method); + } + } + + accumulate(flattened); + + foreach (IncludesAttribute attr in t.SelectCustomAttributes()) + foreach (Type includeType in attr.Types) + GetMethodNames(LookupExtensionModuleType(includeType), accumulate); + } + + private IEnumerable GetMethodNames(RubyMethodAttributes methodType) { + var result = new List(); + GetMethodNames(ClrType, methods => + result.AddRange((from m in methods + where m.MethodAttributes == methodType + select m.Name).Distinct())); + result.Sort(); + return result; + } + + public IEnumerable InstanceMethods { + get { return GetMethodNames(RubyMethodAttributes.PublicInstance); } + } + + public IEnumerable SingletonMethods { + get { return GetMethodNames(RubyMethodAttributes.PublicSingleton); } + } + } + + class Program { + static IEnumerable GetRubyTypes(Assembly a) { + return from rci in + (from t in a.GetTypes() + where t.IsClassOrModule() + select new RubyClassInfo { ClrType = t }) + orderby rci.Name + select rci; + } + + static Dictionary GetExtensionModules(Assembly a) { + var modules = from t in a.GetTypes() + where t.IsDefined(typeof(RubyModuleAttribute), false) + select new { Type = t, Attribute = t.SelectCustomAttributes().First() }; + + var result = new Dictionary(); + foreach(var m in modules) + if (m.Attribute.Extends != null) + result[m.Attribute.Extends] = m.Type; + return result; + } + +#if SIGNED + const string RubyAssembly = @"IronRuby.Libraries, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; +#else + const string RubyAssembly = @"IronRuby.Libraries, Version=1.0.0.0, Culture=neutral"; +#endif + internal static Dictionary ExtensionModules; + + static List ImplementedMethods = new List(); + + delegate IEnumerable GetMethodsBlock(RubyClassInfo rci); + + static void DumpMethods(IEnumerable types, GetMethodsBlock getMethods) { + foreach (RubyClassInfo rci in types) { + if (!String.IsNullOrEmpty(rci.Name)) + foreach (string methodName in getMethods(rci)) + ImplementedMethods.Add(String.Format("{0}#{1}", rci.Name, methodName)); + } + } + + static void Main(string[] args) { + var name = new AssemblyName(RubyAssembly); + var a = Assembly.Load(name); + + ExtensionModules = GetExtensionModules(a); + var types = GetRubyTypes(a); + + DumpMethods(types, t => t.InstanceMethods); + DumpMethods(types, t => t.SingletonMethods); + + ImplementedMethods.Sort(); + string current = null; + foreach (string method in ImplementedMethods) { + if (method != current) { + Console.WriteLine(method); + current = method; + } + } + } + } +} diff --git a/merlin/main/languages/ruby/Utils/IronRuby.Libraries.Scanner/Properties/AssemblyInfo.cs b/merlin/main/languages/ruby/Utils/IronRuby.Libraries.Scanner/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..fb615010f9 --- /dev/null +++ b/merlin/main/languages/ruby/Utils/IronRuby.Libraries.Scanner/Properties/AssemblyInfo.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using IronRuby.Runtime; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IronRuby.Libraries.Scanner")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("IronRuby.Libraries.Scanner")] +[assembly: AssemblyCopyright("Copyright © 2007")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5abf5ece-1006-4df2-9ca0-fe0a817ed220")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion(RubyContext.IronRubyVersionString)] +[assembly: AssemblyFileVersion(RubyContext.IronRubyVersionString)] diff --git a/merlin/main/languages/ruby/context.rb b/merlin/main/languages/ruby/context.rb new file mode 100644 index 0000000000..beecec0701 --- /dev/null +++ b/merlin/main/languages/ruby/context.rb @@ -0,0 +1,863 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** + +require 'rexml/document' +require 'fileutils' +require 'tempfile' + +ENV['HOME'] ||= ENV['USERPROFILE'] +SVN_ROOT = Pathname.new 'c:/svn/trunk' +DEFAULT_ROBOCOPY_OPTIONS = "/XF *#{EXCLUDED_EXTENSIONS.join(' *')} /NP /COPY:DAT /A-:R " + +class String + def change_tag!(name, value = '') + pattern = Regexp.new "\<#{name}\>(.*?)\<\/#{name}\>", Regexp::MULTILINE + self.gsub! pattern, "\<#{name}\>#{value}\<\/#{name}\>" + end + + def change_tag_value!(name, old_value, new_value) + pattern = Regexp.new "\<#{name}\>#{old_value}\<\/#{name}\>", Regexp::MULTILINE + self.gsub! pattern, "\<#{name}\>#{new_value}\<\/#{name}\>" + end + + def change_configuration!(name, value) + source = self.clone + group = "" + found = false + self.replace '' + source.each do |line| + match = line.match /\/ + index = match.nil? ? -1 : match.begin(0) + self << (found && index > 0 ? " " * 4 + "#{value}\n" : line) + found = true if line.include? group + found = false if line.include? "" + end + end +end + +module SvnProvider + def add(path) + exec_f "svn add --force #{path}" + end + + def delete(path) + exec_f "svn delete --force #{path}" + end + + def checkout(path) + end +end + +module TfsProvider + def add(path) + exec_f "tf add #{path}" + end + + def delete(path) + exec_f "tf delete #{path}" + end + + def checkout(path) + exec_f "tf checkout #{path}" + end +end + +class Configuration + class Group + attr_reader :super_group + + def initialize(name, super_group) + @name = name + @switches = {} + @references = [] + @super_group = super_group + end + + def switches(config, *args) + @switches[config] ||= [] + @switches[config] += args + end + + def references(*refs) + @references += refs + end + + def remove_switches(config, *args) + args.each { |arg| @switches[config].delete arg } unless @switches[config].nil? + end + + def get_switches(config) + all = @switches[:all].nil? ? [] : @switches[:all] + (@switches[config].nil? ? [] : @switches[config]) + all + end + + def get_references + @references + end + + def framework_path(*args, &b) + @framework_path = (if args.length == 0 + b.call + elsif args.length == 1 + args.first + else + raise 'framework_path must be called with either a path or a block that defines a path' + end << nil) + end + + def resolve_framework_path(path) + @framework_path.each do |candidate| + raise "cannot resolve path #{path}" if candidate.nil? + candidate_path = candidate + path + break candidate_path if candidate_path.file? + end + end + end + + def self.define(&b) + @master = {} + instance_eval(&b) + end + + def self.group(*args, &b) + name = args.first + super_group = args.length == 2 ? @master[args.last] : nil + @master[name] ||= Group.new(name, super_group) + @master[name].instance_eval(&b) + end + + def self.get_switches(group, config) + result = [] + current = @master[group] + while !current.nil? + result += current.get_switches(config) + current = current.super_group + end + result + end + + def self.get_references(group) + result = [] + current = @master[group] + while !current.nil? + result += current.get_references + current = current.super_group + end + result.map { |assembly| resolve_framework_path(group, assembly) } + end + + def self.resolve_framework_path(group, path) + @master[group].resolve_framework_path(path) + end +end + +Configuration.define do + group(:common) { + switches :all, 'nologo', 'noconfig', 'nowarn:1591,1701,1702', 'errorreport:prompt', 'warn:4', 'warnaserror-' + switches :debug, 'define:"DEBUG;TRACE;MICROSOFT_SCRIPTING_CORE"', 'debug+', 'debug:full', 'optimize-' + switches :release, 'define:TRACE', 'optimize+' + references 'System.dll' + } + group(:desktop, :common) { + references 'System.Configuration.dll' + } + group(:silverlight, :common) { + switches :all, 'define:SILVERLIGHT', 'nostdlib+', 'platform:AnyCPU' + references 'mscorlib.dll' + } + if ENV['mono'].nil? + group(:desktop) { + framework_path [Pathname.new(ENV['windir'].dup) + 'Microsoft.NET/Framework/v2.0.50727', + Pathname.new('c:\program files\reference assemblies\microsoft\framework\v3.5')] + } + group(:silverlight) { + framework_path [Pathname.new('c:/program files/microsoft silverlight/2.0.30523.6')] + } + else + group(:mono) { + framework_path { + libdir = IO.popen('pkg-config --variable=libdir mono').read.strip + [Pathname.new(libdir) + 'mono' + '2.0'] + } + switches :all, 'noconfig' + remove_switches ['warnaserror+'] + } + end +end + +class ProjectContext + Mapping = Struct.new(:merlin_path, :svn_path, :recurse) + + class CommandContext + + # Low-level command helpers + + def is_test? + ENV['test'] == 'true' + end + + def exec(cmd) + if is_test? + rake_output_message ">> #{cmd}" + else + sh cmd + end + end + + def exec_net(cmd) + if ENV['mono'].nil? + exec cmd + else + exec "mono #{cmd}" + end + end + + def exec_f(cmd) + begin + exec(cmd) + rescue + end + end + + # context is either :source or :target. project_context is reference + # to ProjectContext subclass class + + def initialize(context, project_context) + if context == :source || context == :target + @project_context = project_context + @context = context + else + raise "CommandContext#initialize(context) must be :source or :target" + end + end + + def get_mapping(name) + mapping = @project_context.resolve(name) + raise "cannot find #{name} in ProjectContext" if mapping.nil? + mapping + end + + # There are three things to consider when getting the source directory path + # 1) The context (source or destination) we are running in + # 2) The name of the mapping that we want to look up the source path in + # 3) Whether we are running in MERLIN_ROOT or SVN_ROOT + + def get_source_dir(name) + mapping = get_mapping name + context_path = @project_context.source + context_path + (@project_context.is_merlin? ? mapping.merlin_path : mapping.svn_path) + end + + # Getting the target directory path is the same as source except for the + # reversal of logic around whether to get svn_path or merlin_path based on + # is_merlin? status + + def get_target_dir(name) + mapping = get_mapping name + context_path = @project_context.target + context_path + (@project_context.is_merlin? ? mapping.svn_path : mapping.merlin_path) + end + + def get_relative_target_dir(name) + mapping = get_mapping name + @project_context.is_merlin? ? mapping.svn_path : mapping.merlin_path + end + + def is_recursive?(name) + get_mapping(name).recurse + end + + # General filesystem related methods + + def copy_dir(source_dir, target_dir, options = '') + log_filename = Pathname.new(Dir.tmpdir) + "rake_transform.log" + exec_f %Q{robocopy "#{source_dir}" "#{target_dir}" #{DEFAULT_ROBOCOPY_OPTIONS} "/LOG+:#{log_filename}" #{options}} + end + + def copy_to_temp_dir(name, temp_dir, extras = []) + IronRuby.source_context do + source = get_source_dir(name) + target = get_relative_target_dir(name) + + if is_recursive? name + source_dirs = source.filtered_subdirs(extras) + source_dirs.each { |dir| copy_dir dir, temp_dir + target + dir.relative_path_from(source) } + else + copy_dir source, temp_dir + target + end + end + end + + def del(name, *paths) + dir = name.is_a?(Symbol) ? get_source_dir(name) : name + Dir.chdir(dir) { paths.each { |path| exec_f %Q{del "#{path}"} } } + end + + def chdir(env, &b) + dir = env.is_a?(Symbol) ? get_source_dir(env) : env + Dir.chdir(dir) { instance_eval(&b) } + end + + def rd(name) + path = name.is_a?(Symbol) ? get_source_dir(name) : name + FileUtils.rm_rf path + end + + def mkdir(name) + path = name.is_a?(Symbol) ? get_source_dir(name) : name + FileUtils.mkdir_p path + end + + def generate_temp_dir + layout = Pathname.new(Dir.tmpdir) + 'layout' + del Pathname.new(Dir.tmpdir), 'rake_transform.log' + rd layout + mkdir layout + layout + end + + # Source transformation related methods + + def diff_directories(temp_dir) + source_dirs, target_dirs = [], [] + + nodes = [:root, :gppg, :dlr_core, :dlr_libs, :dlr_com, :ironruby, :libraries, :tests, :console, :generator, :test_runner, :scanner, :yaml, :stdlibs, :ironlibs] + nodes.each do |node| + if is_recursive? node + source_dirs += (temp_dir + get_relative_target_dir(node)).filtered_subdirs.map { |d| d.relative_path_from(temp_dir).downcase } + + # Target directory may not exist, so we only add if we find it there + if get_target_dir(node).directory? + target_dirs += get_target_dir(node).filtered_subdirs.map { |d| d.relative_path_from(@project_context.target).downcase } + end + else + # This is also an unusual case - since there is a 1:1 mapping by + # definition in a non-recursive directory mapping, this will be + # flagged only as a change candidate and not an add or a delete. + source_dirs << get_relative_target_dir(node).downcase + + # Target directory may not exist, so we only add if we find it there + target_dirs << get_relative_target_dir(node).downcase if get_target_dir(node).directory? + end + end + + added = source_dirs - target_dirs + removed = target_dirs - source_dirs + change_candidates = source_dirs & target_dirs + + return added, removed, change_candidates + end + + def push_to_target(temp_dir) + rake_output_message "\n#{'=' * 78}\nApplying source changes to target source repository\n\n" + rake_output_message "Computing directory structure changes ...\n" + + added, removed, change_candidates = diff_directories(temp_dir) + + dest = @project_context.target + + rake_output_message "Adding new directories to target source control\n" + added.each do |dir| + copy_dir(temp_dir + dir, dest + dir) + add(dest + dir) + end + + rake_output_message "Deleting directories from target source control\n" + removed.each do |dir| + rd dest + dir + delete dest + dir + end + + rake_output_message "Copying files in changed directories to target source control\n" + change_candidates.each do |dir| + src_file_list = (temp_dir + dir).filtered_files + dest_file_list = (dest + dir).filtered_files + + added = src_file_list - dest_file_list + removed = dest_file_list - src_file_list + change_candidates = src_file_list & dest_file_list + + added.each do |file| + copy temp_dir + dir + file, dest + dir + file + add dest + dir + file + end + + removed.each do |file| + delete dest + dir + file + end + + change_candidates.each do |file| + source_file = temp_dir + dir + file + dest_file = dest + dir + file + + if !compare_file(source_file, dest_file) + checkout dest_file + copy source_file, dest_file + end + end + end + end + + # Compiler-related methods + + def resgen(base_path, resource_map) + resource_map.each_pair do |input, output| + exec %Q{resgen "#{base_path + input.dup}" "#{build_path + output}"} + end + end + + def configuration + ENV['configuration'].nil? ? :debug : ENV['configuration'].to_sym + end + + def clr + if ENV['mono'].nil? + ENV['clr'].nil? ? :desktop : ENV['clr'].to_sym + else + :mono + end + end + + def platform + ENV['platform'].nil? ? :windows : ENV['platform'].to_sym + end + + def resolve_framework_path(file) + Configuration.resolve_framework_path(clr, file) + end + + def build_path + get_source_dir(:build) + "#{clr == :desktop ? configuration : "#{clr}_#{configuration}"}" + end + + def compiler_switches + Configuration.get_switches(clr, configuration) + end + + def references(refs, working_dir) + references = Configuration.get_references(clr) + refs.each do |ref| + references << if ref =~ /^\!/ + resolve_framework_path(ref[1..ref.length]) + else + (build_path + ref).relative_path_from(working_dir) + end + end unless refs.nil? + references + end + + def get_case_sensitive_path(pathname) + elements = pathname.split '\\' + result = Pathname.new '.' + elements.each do |element| + entry = result.entries.find { |p| p.downcase == element.downcase } + result = result + entry + end + result.to_s + end + + def get_compile_path_list(csproj) + csproj ||= '*.csproj' + cs_proj_files = Dir[csproj] + if cs_proj_files.length == 1 + doc = REXML::Document.new(File.open(cs_proj_files.first)) + result = doc.elements.collect("/Project/ItemGroup/Compile") { |c| c.attributes['Include'] } + result.delete_if { |e| e =~ /(Silverlight\\SilverlightVersion.cs|System\\Dynamic\\ComInterop\\Parameterized.System.Dynamic.ComInterop.cs)/ } + result.map! { |p| get_case_sensitive_path(p) } if ENV['mono'] + result + else + raise ArgumentError.new("Found more than one .csproj file in directory! #{cs_proj_files.join(", ")}") + end + end + + def compile(name, args) + working_dir = get_source_dir(name) + build_dir = build_path + + Dir.chdir(working_dir) do |p| + cs_args = ["out:\"#{build_dir + args[:output]}\""] + cs_args += references(args[:references], working_dir).map { |ref| "r:\"#{ref}\"" } + cs_args += compiler_switches + cs_args += args[:switches] unless args[:switches].nil? + + unless args[:resources].nil? + resgen working_dir, args[:resources] + args[:resources].each_value { |res| cs_args << "resource:\"#{build_path + res}\"" } + end + cmd = '' + cmd << CS_COMPILER + if cs_args.include?('noconfig') + cs_args.delete('noconfig') + cmd << " /noconfig" + end + temp = Tempfile.new(name.to_s) + cs_args.each { |opt| temp << ' /' + opt + "\n"} + options = get_compile_path_list(args[:csproj]).join("\n") + temp.puts options + temp.close + + cmd << " @" << temp.path + exec cmd + end + end + + # Project transformation methods + + def replace_output_path(contents, old, new) + contents.gsub! Regexp.new(Regexp.escape("#{old}"), Regexp::IGNORECASE), "#{new}" + end + + def replace_doc_path(contents, old, new) + contents.gsub! Regexp.new(Regexp.escape("#{old}"), Regexp::IGNORECASE), "#{new}" + end + + def replace_key_path(contents, old, new) + contents.gsub! Regexp.new(Regexp.escape("#{old}"), Regexp::IGNORECASE), "#{new}" + end + + def replace_import_project(contents, old, new) + contents.gsub! Regexp.new(Regexp.escape(""), Regexp::IGNORECASE), "" + end + + def replace_post_build_event(contents, old, new) + contents.gsub! Regexp.new(Regexp.escape("#{old}"), Regexp::IGNORECASE), "#{new}" + end + + def replace_app_config_path(contents, old, new) + contents.gsub! Regexp.new(Regexp.escape(%Q{}), Regexp::IGNORECASE), %Q{} + end + + def transform_project(name, project) + path = get_target_dir(name) + project + rake_output_message "Transforming: #{path}" + contents = path.read + + # Extract the project name from .csproj filename + project_name = /(.*)\.csproj/.match(project)[1] + + if @project_context.is_merlin? + contents.change_tag! 'SccProjectName' + contents.change_tag! 'SccLocalPath' + contents.change_tag! 'SccAuxPath' + contents.change_tag! 'SccProvider' + + contents.change_tag! 'DelaySign', 'false' + contents.change_tag! 'SignAssembly', 'false' + + contents.change_configuration! 'Debug|AnyCPU', 'TRACE;DEBUG' + contents.change_configuration! 'Release|AnyCPU', 'TRACE' + contents.change_configuration! 'Silverlight Debug|AnyCPU', 'TRACE;DEBUG;SILVERLIGHT' + contents.change_configuration! 'Silverlight Release|AnyCPU', 'TRACE;SILVERLIGHT' + + if block_given? + yield contents + else + replace_output_path contents, '..\..\..\Bin\Debug\\', '..\..\build\debug\\' + replace_output_path contents, '..\..\..\Bin\Release\\', '..\..\build\release\\' + replace_output_path contents, '..\..\..\Bin\Silverlight Debug\\', '..\..\build\silverlight debug\\' + replace_output_path contents, '..\..\..\Bin\Silverlight Release\\', '..\..\build\silverlight release\\' + end + else + contents.change_tag! 'SccProjectName', 'SAK' + contents.change_tag! 'SccLocalPath', 'SAK' + contents.change_tag! 'SccAuxPath', 'SAK' + contents.change_tag! 'SccProvider', 'SAK' + + contents.change_tag! 'DelaySign', 'true' + contents.change_tag! 'SignAssembly', 'true' + + contents.change_configuration! 'Debug|AnyCPU', 'TRACE;DEBUG;SIGNED' + contents.change_configuration! 'Release|AnyCPU', 'TRACE;SIGNED' + contents.change_configuration! 'Silverlight Debug|AnyCPU', 'TRACE;DEBUG;SILVERLIGHT' + contents.change_configuration! 'Silverlight Release|AnyCPU', 'TRACE;SILVERLIGHT' + + if block_given? + yield contents + else + replace_output_path contents, '..\..\build\debug\\', '..\..\..\Bin\Debug\\' + replace_output_path contents, '..\..\build\release\\', '..\..\..\Bin\Release\\' + replace_output_path contents, '..\..\build\silverlight debug', '..\..\..\Bin\Silverlight Debug\\' + replace_output_path contents, '..\..\build\silverlight release', '..\..\..\Bin\Silverlight Release\\' + replace_key_path contents, '..\..\RubyTestKey.snk', '..\..\..\MSSharedLibKey.snk' + end + end + path.open('w+') { |f| f.write contents } + end + end + + # The Rakefile must always be found in the root directory of the source tree. + + # If ENV['MERLIN_ROOT'] is defined, then we know that we are running on + # a machine with an enlistment in the MERLIN repository. This will enable + # features that require a source context and a destination context (such as + # pushing to / from MERLIN). Otherwise, destination context will always be + # nil and we will throw on an attempt to do operations that require a + # push. + + private + def self.init_context + @rakefile_dir = Pathname.new(File.dirname(File.expand_path(__FILE__)).downcase) + + if ENV['MERLIN_ROOT'].nil? + # Initialize the context for an external contributor who builds from + # a non-MERLIN command prompt + @source = @rakefile_dir + @target = nil + else + # Initialize @source and @target to point to the right places based + # on whether we are within MERLIN_ROOT or SVN_ROOT + @merlin_root = Pathname.new(ENV['MERLIN_ROOT'].downcase) + '../../' # hack for changes in TFS layout + @ruby_root = @merlin_root + 'merlin/main/languages/ruby' + + if @rakefile_dir == @ruby_root + @source = @merlin_root + @target = SVN_ROOT + elsif @rakefile_dir == SVN_ROOT + @source = @rakefile_dir + @target = @merlin_root + else + raise <<-EOF + Rakefile is at #{@rakefile_dir}. This is neither the SVN_ROOT nor + the MERLIN_ROOT. Possible causes of this are running from a + non-MERLIN command prompt (where MERLIN_ROOT environment variable + is defined) or if the SVN_ROOT constant in the Rakefile does not + point to where you downloaded the SVN repository for IronRuby. + EOF + end + end + + @map = {} + @initialized = true + end + + def self.make_pathname(path) + elements = path.split '/' + raise "must be an an array with at least one element: #{elements}" if elements.length < 1 + result = Pathname.new elements.first + (1..elements.length-1).each { |i| result += elements[i] } + result + end + + public + def self.map(name, args) + init_context unless @initialized + @map[name] = Mapping.new(make_pathname(args[:merlin]), make_pathname(args[:svn]), (args[:recurse].nil? ? true : args[:recurse])) + end + + def self.resolve(name) + @map[name] + end + + def self.is_merlin? + @merlin_root == @source + end + + def self.source_context(&b) + context = CommandContext.new(:source, self) + context.extend(is_merlin? ? SvnProvider : TfsProvider) + context.instance_eval(&b) + context + end + + def self.target_context(&b) + if @target.nil? + raise <<-EOF + Cannot invoke commands against target_context if you are not running in + a MERLIN_ROOT context. External folks should never see this error as they + should never be running commands that require moving things between + different contexts. + EOF + else + # Note that this is a bit unusual - the source control commands in the + # target are identical to the source control commands for the source. This + # is due to the semantics of the operation. The source is always + # authoritative in these kinds of push scenarios, so you'll never want to + # mutate the source repository, only the target repository. + context = CommandContext.new(:target, self) + context.extend(is_merlin? ? SvnProvider : TfsProvider) + context.instance_eval(&b) + context + end + end + + def self.source + @source + end + + def self.target + @target + end +end + +class IronRuby < ProjectContext + map :root, :merlin => 'merlin/main/languages/ruby', :svn => '.', :recurse => false + map :gppg, :merlin => 'merlin/main/utilities/gppg', :svn => 'bin', :recurse => false + map :dlr_core, :merlin => 'ndp/fx/src/core/microsoft/scripting', :svn => 'src/microsoft.scripting.core' + map :dlr_libs, :merlin => 'merlin/main/runtime/microsoft.scripting', :svn => 'src/microsoft.scripting' + map :dlr_com, :merlin => 'ndp/fx/src/dynamiccom', :svn => 'src/dynamiccom' + map :ironruby, :merlin => 'merlin/main/languages/ruby/ruby', :svn => 'src/ironruby' + map :libraries, :merlin => 'merlin/main/languages/ruby/libraries.lca_restricted', :svn => 'src/IronRuby.Libraries' + map :yaml, :merlin => 'merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml', :svn => 'src/yaml' + map :tests, :merlin => 'merlin/main/languages/ruby/tests', :svn => 'tests/ironruby' + map :console, :merlin => 'merlin/main/languages/ruby/console', :svn => 'utils/ironruby.console' + map :generator, :merlin => 'merlin/main/languages/ruby/classinitgenerator', :svn => 'utils/ironruby.classinitgenerator' + map :test_runner, :merlin => 'merlin/main/languages/ruby/ironruby.tests', :svn => 'utils/IronRuby.Tests' + map :scanner, :merlin => 'merlin/main/languages/ruby/utils/ironruby.libraries.scanner', :svn => 'utils/ironruby.libraries.scanner' + map :build, :merlin => 'merlin/main/bin', :svn => 'build' + map :stdlibs, :merlin => 'merlin/external/languages/ruby/redist-libs', :svn => 'lib' + map :ironlibs, :merlin => 'merlin/main/languages/ruby/libs', :svn => 'lib/IronRuby' + map :lang_root, :merlin => 'merlin/main', :svn => '.' +end + +# Spec runner helpers + +class MSpecRunner + attr_accessor :files, :examples, :expectations, :failures, :errors, :summaries + attr_reader :tempdir + + SUMMARY_PARSER = /(\d+) files?, (\d+) examples?, (\d+) expectations?, (\d+) failures?, (\d+) errors?/ + + def initialize + @files = 0 + @examples = 0 + @expectations = 0 + @failures = 0 + @errors = 0 + @summaries = [] + @tempdir = Dir.tmpdir + end + + def regression(ruby, klass, method = nil) + cmd = %Q{"#{UserEnvironment.mri_binary}" "#{UserEnvironment.mspec}/bin/mspec" ci -t #{ruby} -fm -V -B "#{UserEnvironment.config}" "#{UserEnvironment.rubyspec}/1.8/core/#{klass}"} + cmd += "/#{method}_spec.rb" unless method.nil? + cmd += " > #{tempdir}/out.txt" + system cmd + File.open("#{tempdir}/out.txt", 'r') do |f| + lines = f.readlines + lines.each do |line| + if SUMMARY_PARSER =~ line + @files += $1.to_i + @examples += $2.to_i + @expectations += $3.to_i + @failures += $4.to_i + @errors += $5.to_i + @summaries << "#{klass}: #{$1.to_i} files, #{$2.to_i} examples, #{$3.to_i} expectations, #{$4.to_i} failures, #{$5.to_i} errors" + end + end + end + end + + def all_core(method, ruby) + send method, ruby, '*' + end + + def why_regression(ruby, klass, method = nil) + cmd = %Q{#{UserEnvironment.mri_binary} "#{UserEnvironment.mspec}/bin/mspec" ci -t #{ruby} -fs -V -B "#{UserEnvironment.config}" "#{UserEnvironment.rubyspec}/1.8/core/#{klass}"} + cmd += "/#{method}_spec.rb" unless method.nil? + system cmd + end + + def test(ruby, klass, method = nil) + cmd = %Q{#{UserEnvironment.mri_binary} "#{UserEnvironment.mspec}/bin/mspec" run -t #{ruby} -Gcritical -V -fs -B "#{UserEnvironment.config}" "#{UserEnvironment.rubyspec}/1.8/core/#{klass}"} + cmd += "/#{method}_spec.rb" unless method.nil? + system cmd + end + + def baseline(ruby, klass, method = nil) + cmd = %Q{#{UserEnvironment.mri_binary} "#{UserEnvironment.mspec}/bin/mspec" tag -t #{ruby} -fs -V -Gcritical -B "#{UserEnvironment.config}" "#{UserEnvironment.rubyspec}/1.8/core/#{klass}"} + cmd << "/#{method}_spec.rb" unless method.nil? + system cmd + end + + def generate_critical_tags + lines = [] + return unless File.exist? "#{UserEnvironment.tags}\\critical_tags.txt" + File.open("#{UserEnvironment.tags}\\critical_tags.txt", 'r') do |f| + f.readlines.each do |line| + file,_,tag,desc = line.chomp.split(":") + fulltag = tag << ":" << desc + filepath = "#{UserEnvironment.tags}/1.8/#{file}" + filedir = File.dirname(filepath) + FileUtils.mkdir_p(filedir) unless File.exist?(filedir) + FileUtils.touch(filepath) unless File.exist?(filepath) + File.open(filepath, File::RDWR) do |tagfile| + if tagfile.readlines.grep(Regexp.new(Regexp.escape(tag))).empty? + tagfile.puts fulltag.strip + end + end + end + end + end + + def report + summaries.each { |s| puts s } + puts "\nSummary:\n" + puts "#{summaries.length} types, #{files} files, #{examples} examples, #{expectations} expectations, #{failures} failures, #{errors} errors" + end +end + +class UserEnvironment + + # Find path to named executable + + def self.find_executable(executable) + executable.downcase! + result = [] + search_path = ENV['PATH'].split(File::PATH_SEPARATOR) + search_path.each do |dir| + path = Pathname.new(dir) + file_path = path + executable + result << file_path.dirname if file_path.file? + file_path = path + (executable + '.exe') + result << file_path.dirname if file_path.file? + end + result + end + + def self.mri_binary + self.mri + '/bin/ruby.exe' + end + + def self.method_missing(sym, *args) + name = sym.to_s + if name =~ /\?$/ + File.exist?(self.send(name[0..-2])) + elsif self.constants.include?(name.upcase) + File.expand_path(UserEnvironment.const_get(name.upcase)) + else + raise NoMethodError.new("undefined method '#{name}' for #{self}", name, args) + end + end + + def initialize + path_to_config = ENV['HOME'] + '/.irconfig.rb' + load path_to_config if File.exist? path_to_config + + unless defined?(UserEnvironment::MRI) + ruby_exe_paths = UserEnvironment.find_executable 'ruby' + unless ruby_exe_paths.empty? + UserEnvironment.const_set(:MRI, Pathname.new(ruby_exe_paths.first + '\..\\')) + else + raise ArgumentError.new("Could not find ruby.exe on your path") + end + end + UserEnvironment.const_set(:TAGS, "#{ENV['HOME']}\\dev\\ironruby-tags".gsub('\\', '/')) unless defined?(UserEnvironment::TAGS) + UserEnvironment.const_set(:RUBYSPEC, "#{ENV['HOME']}\\dev\\rubyspec".gsub('\\', '/')) unless defined?(UserEnvironment::RUBYSPEC) + UserEnvironment.const_set(:MSPEC, "#{ENV['HOME']}\\dev\\mspec".gsub('\\', '/')) unless defined?(UserEnvironment::MSPEC) + UserEnvironment.const_set(:CONFIG, "#{ENV['HOME']}\\dev\\default.mspec".gsub('\\', '/')) unless defined?(UserEnvironment::CONFIG) + end +end + +UE = UserEnvironment.new diff --git a/merlin/main/languages/ruby/default.irconfig.rb b/merlin/main/languages/ruby/default.irconfig.rb new file mode 100644 index 0000000000..02a7a62a6d --- /dev/null +++ b/merlin/main/languages/ruby/default.irconfig.rb @@ -0,0 +1,32 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** +# Configuration file for IronRuby developers + +class UserEnvironment + # Location of your MRI installation. This is REQUIRED until + # we start packaging and shipping the MRI libs ourselves. + # We will find your Ruby install directory based on where + # ruby.exe is on your path. If you want to define it explicitly + # yourself, uncomment the following line: + # + # MRI = 'c:\ruby' + + # These constants must be defined if you want to run the RubySpec specs. + # + # CONFIG = '~/default.mspec' + # TAGS = 'c:/dev/ironruby-tags' + # RUBYSPEC = 'c:/dev/rubyspec' + # MSPEC = 'c:/dev/mspec' +end diff --git a/merlin/main/languages/ruby/default.mspec.rb b/merlin/main/languages/ruby/default.mspec.rb new file mode 100644 index 0000000000..e6d4c8d2cb --- /dev/null +++ b/merlin/main/languages/ruby/default.mspec.rb @@ -0,0 +1,29 @@ +# **************************************************************************** +# +# Copyright (c) Microsoft Corporation. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +# **************************************************************************** +class MSpecScript + # An ordered list of the directories containing specs to run + # as the CI process. + set :ci_files, [ + "rubyspec/1.8/core", + ] + + # The set of substitutions to transform a spec filename + # into a tag filename. + set :tags_patterns, [ + [%r(rubyspec/), 'ironruby-tags/'], + [/_spec.rb$/, '_tags.txt'] + ] +end + diff --git a/merlin/main/languages/ruby/runfirst.cmd b/merlin/main/languages/ruby/runfirst.cmd new file mode 100644 index 0000000000..2ddf697d6a --- /dev/null +++ b/merlin/main/languages/ruby/runfirst.cmd @@ -0,0 +1,2 @@ +copy default.irconfig.rb "%USERPROFILE%\.irconfig.rb" +copy default.mspec.rb "%USERPROFILE%\dev\default.mspec" diff --git a/merlin/main/languages/ruby/sfx_config.txt b/merlin/main/languages/ruby/sfx_config.txt new file mode 100644 index 0000000000..6fd41c89bd --- /dev/null +++ b/merlin/main/languages/ruby/sfx_config.txt @@ -0,0 +1,8 @@ +;!@Install@!UTF-8! +Title="IronRuby" +ExtractPathText="Installation path:" +BeginPrompt="IronRuby Alpha Installer" +InstallPath="c:\\ironruby" +GUIMode="2" +GUIFlags="64" +;!@InstallEnd@! diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ActionBinder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ActionBinder.cs new file mode 100644 index 0000000000..bee45fdde3 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ActionBinder.cs @@ -0,0 +1,576 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic; +using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + /// + /// Provides binding semantics for a language. This include conversions as well as support + /// for producing rules for actions. These optimized rules are used for calling methods, + /// performing operators, and getting members using the ActionBinder's conversion semantics. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + public abstract class ActionBinder { + private ScriptDomainManager _manager; + + public bool PrivateBinding { + get { return _manager.Configuration.PrivateBinding; } + } + + protected ActionBinder(ScriptDomainManager manager) { + _manager = manager; + } + + public virtual Expression Bind(OldDynamicAction action, object[] args, ReadOnlyCollection parameters, LabelTarget returnLabel) { + var builder = new RuleBuilder(parameters, returnLabel); + MakeRule(action, args, builder); + if (builder.Target != null) { + return builder.CreateRule(); + } + return null; + } + + public ScriptDomainManager Manager { + get { + return _manager; + } + } + + /// + /// Produces a rule for the specified Action for the given arguments. + /// + /// The Action that is being performed. + /// The arguments to the action as provided from the call site at runtime. + /// The rule builder that will hold the result + protected abstract void MakeRule(OldDynamicAction action, object[] args, RuleBuilder rule); + + /// + /// Converts an object at runtime into the specified type. + /// + public virtual object Convert(object obj, Type toType) { + if (obj == null) { + if (!toType.IsValueType) { + return null; + } + } else { + if (toType.IsValueType) { + if (toType == obj.GetType()) { + return obj; + } + } else { + if (toType.IsAssignableFrom(obj.GetType())) { + return obj; + } + } + } + throw Error.InvalidCast(obj != null ? obj.GetType().Name : "(null)", toType.Name); + } + + /// + /// Determines if a conversion exists from fromType to toType at the specified narrowing level. + /// toNotNullable is true if the target variable doesn't allow null values. + /// + public abstract bool CanConvertFrom(Type fromType, Type toType, bool toNotNullable, NarrowingLevel level); + + #region TODO: move to ParameterBinder + + public virtual bool ParametersEquivalent(ParameterWrapper parameter1, ParameterWrapper parameter2) { + return parameter1.Type == parameter2.Type && parameter1.ProhibitNull == parameter2.ProhibitNull; + } + + public virtual bool CanConvertFrom(Type fromType, ParameterWrapper toParameter, NarrowingLevel level) { + Assert.NotNull(fromType, toParameter); + + Type toType = toParameter.Type; + + if (fromType == Null.Type) { + if (toParameter.ProhibitNull) { + return false; + } + + if (toType.IsGenericType && toType.GetGenericTypeDefinition() == typeof(Nullable<>)) { + return true; + } + + if (!toType.IsValueType) { + return true; + } + } + + if (fromType == toType) { + return true; + } + + return CanConvertFrom(fromType, toType, toParameter.ProhibitNull, level); + } + + /// + /// Selects the best (of two) candidates for conversion from actualType + /// + public virtual Candidate SelectBestConversionFor(Type actualType, ParameterWrapper candidateOne, ParameterWrapper candidateTwo, NarrowingLevel level) { + return Candidate.Equivalent; + } + + /// + /// Provides ordering for two parameter types if there is no conversion between the two parameter types. + /// + public abstract Candidate PreferConvert(Type t1, Type t2); + + #endregion + + /// + /// Converts the provided expression to the given type. The expression is safe to evaluate multiple times. + /// + public virtual Expression ConvertExpression(Expression expr, Type toType, ConversionResultKind kind, Expression context) { + ContractUtils.RequiresNotNull(expr, "expr"); + ContractUtils.RequiresNotNull(toType, "toType"); + + Type exprType = expr.Type; + + if (toType == typeof(object)) { + if (exprType.IsValueType) { + return Expression.Convert(expr, toType); + } else { + return expr; + } + } + + if (toType.IsAssignableFrom(exprType)) { + return expr; + } + + Type visType = CompilerHelpers.GetVisibleType(toType); + Expression[] args; + if (context != null) { + args = new Expression[] { context, expr }; + } else { + args = new Expression[] { expr }; + } + + return Expression.Dynamic( + OldConvertToAction.Make(this, visType, kind), + visType, + args + ); + } + + /// + /// Gets the members that are visible from the provided type of the specified name. + /// + /// The default implemetnation first searches the type, then the flattened heirachy of the type, and then + /// registered extension methods. + /// + public virtual MemberGroup GetMember(OldDynamicAction action, Type type, string name) { + MemberInfo[] foundMembers = type.GetMember(name); + if (!PrivateBinding) { + foundMembers = CompilerHelpers.FilterNonVisibleMembers(type, foundMembers); + } + + MemberGroup members = new MemberGroup(foundMembers); + + // check for generic types w/ arity... + Type[] types = type.GetNestedTypes(BindingFlags.Public); + string genName = name + ReflectionUtils.GenericArityDelimiter; + List genTypes = null; + foreach (Type t in types) { + if (t.Name.StartsWith(genName)) { + if (genTypes == null) genTypes = new List(); + genTypes.Add(t); + } + } + + if (genTypes != null) { + List mt = new List(members); + foreach (Type t in genTypes) { + mt.Add(MemberTracker.FromMemberInfo(t)); + } + return MemberGroup.CreateInternal(mt.ToArray()); + } + + if (members.Count == 0) { + members = new MemberGroup(type.GetMember(name, BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)); + if (members.Count == 0) { + members = GetAllExtensionMembers(type, name); + } + } + + return members; + } + + #region Error Production + + public virtual ErrorInfo MakeContainsGenericParametersError(MemberTracker tracker) { + return ErrorInfo.FromException( + Expression.New( + typeof(InvalidOperationException).GetConstructor(new Type[] { typeof(string) }), + Expression.Constant(Strings.InvalidOperation_ContainsGenericParameters(tracker.DeclaringType.Name, tracker.Name)) + ) + ); + } + + public virtual ErrorInfo MakeMissingMemberErrorInfo(Type type, string name) { + return ErrorInfo.FromException( + Expression.New( + typeof(MissingMemberException).GetConstructor(new Type[] { typeof(string) }), + Expression.Constant(name) + ) + ); + } + + public virtual ErrorInfo MakeGenericAccessError(MemberTracker info) { + return ErrorInfo.FromException( + Expression.New( + typeof(MemberAccessException).GetConstructor(new Type[] { typeof(string) }), + Expression.Constant(info.Name) + ) + ); + } + + public ErrorInfo MakeStaticPropertyInstanceAccessError(PropertyTracker tracker, bool isAssignment, params Expression[] parameters) { + return MakeStaticPropertyInstanceAccessError(tracker, isAssignment, (IList)parameters); + } + + /// + /// Called when a set is attempting to assign to a field or property from a derived class through the base class. + /// + /// The default behavior is to allow the assignment. + /// + public virtual ErrorInfo MakeStaticAssignFromDerivedTypeError(Type accessingType, MemberTracker assigning, Expression assignedValue, Expression context) { + switch (assigning.MemberType) { + case TrackerTypes.Property: + PropertyTracker pt = (PropertyTracker)assigning; + MethodInfo setter = pt.GetSetMethod() ?? pt.GetSetMethod(true); + return ErrorInfo.FromValueNoError( + AstUtils.SimpleCallHelper( + setter, + ConvertExpression( + assignedValue, + setter.GetParameters()[0].ParameterType, + ConversionResultKind.ExplicitCast, + context + ) + ) + ); + case TrackerTypes.Field: + FieldTracker ft = (FieldTracker)assigning; + return ErrorInfo.FromValueNoError( + Expression.Assign( + Expression.Field(null, ft.Field), + ConvertExpression(assignedValue, ft.FieldType, ConversionResultKind.ExplicitCast, context) + ) + ); + default: + throw new InvalidOperationException(); + } + } + + /// + /// Creates an ErrorInfo object when a static property is accessed from an instance member. The default behavior is throw + /// an exception indicating that static members properties be accessed via an instance. Languages can override this to + /// customize the exception, message, or to produce an ErrorInfo object which reads or writes to the property being accessed. + /// + /// The static property being accessed through an instance + /// True if the user is assigning to the property, false if the user is reading from the property + /// The parameters being used to access the property. This includes the instance as the first entry, any index parameters, and the + /// value being assigned as the last entry if isAssignment is true. + /// + public virtual ErrorInfo MakeStaticPropertyInstanceAccessError(PropertyTracker tracker, bool isAssignment, IList parameters) { + ContractUtils.RequiresNotNull(tracker, "tracker"); + ContractUtils.Requires(tracker.IsStatic, "tracker", Strings.ExpectedStaticProperty); + ContractUtils.RequiresNotNull(parameters, "parameters"); + ContractUtils.RequiresNotNullItems(parameters, "parameters"); + + string message = isAssignment ? Strings.StaticAssignmentFromInstanceError(tracker.Name, tracker.DeclaringType.Name) : + Strings.StaticAccessFromInstanceError(tracker.Name, tracker.DeclaringType.Name); + + return ErrorInfo.FromException( + Expression.New( + typeof(MissingMemberException).GetConstructor(new Type[] { typeof(string) }), + Expression.Constant(message) + ) + ); + } + + public virtual ErrorInfo MakeConversionError(Type toType, Expression value) { + return ErrorInfo.FromException( + Expression.Call( + typeof(ScriptingRuntimeHelpers).GetMethod("CannotConvertError"), + Expression.Constant(toType), + Expression.Convert( + value, + typeof(object) + ) + ) + ); + } + + /// + /// Provides a way for the binder to provide a custom error message when lookup fails. Just + /// doing this for the time being until we get a more robust error return mechanism. + /// + /// Deprecated, use the non-generic version instead + /// + public virtual ErrorInfo MakeMissingMemberError(Type type, string name) { + return ErrorInfo.FromException( + Expression.New( + typeof(MissingMemberException).GetConstructor(new Type[] { typeof(string) }), + Expression.Constant(name) + ) + ); + } + + #endregion + + #region Deprecated Error production + + + /// + /// Provides a way for the binder to provide a custom error message when lookup fails. Just + /// doing this for the time being until we get a more robust error return mechanism. + /// + public virtual Expression MakeReadOnlyMemberError(RuleBuilder rule, Type type, string name) { + return rule.MakeError( + Expression.New( + typeof(MissingMemberException).GetConstructor(new Type[] { typeof(string) }), + Expression.Constant(name) + ) + ); + } + + /// + /// Provides a way for the binder to provide a custom error message when lookup fails. Just + /// doing this for the time being until we get a more robust error return mechanism. + /// + public virtual Expression MakeUndeletableMemberError(RuleBuilder rule, Type type, string name) { + return MakeReadOnlyMemberError(rule, type, name); + } + + #endregion + + public virtual ErrorInfo MakeEventValidation(RuleBuilder rule, MemberGroup members) { + EventTracker ev = (EventTracker)members[0]; + + // handles in place addition of events - this validates the user did the right thing. + return ErrorInfo.FromValueNoError( + Expression.Call( + typeof(ScriptingRuntimeHelpers).GetMethod("SetEvent"), + Expression.Constant(ev), + rule.Parameters[1] + ) + ); + } + + protected virtual string GetTypeName(Type t) { + return t.Name; + } + + /// + /// Gets the extension members of the given name from the provided type. Base classes are also + /// searched for their extension members. Once any of the types in the inheritance hierarchy + /// provide an extension member the search is stopped. + /// + public MemberGroup GetAllExtensionMembers(Type type, string name) { + Type curType = type; + do { + MemberGroup res = GetExtensionMembers(curType, name); + if (res.Count != 0) { + return res; + } + + curType = curType.BaseType; + } while (curType != null); + + return MemberGroup.EmptyGroup; + } + + /// + /// Gets the extension members of the given name from the provided type. Subclasses of the + /// type and their extension members are not searched. + /// + public MemberGroup GetExtensionMembers(Type declaringType, string name) { + IList extTypes = GetExtensionTypes(declaringType); + List members = new List(); + + foreach (Type ext in extTypes) { + foreach (MemberInfo mi in ext.GetMember(name, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)) { + if (ext != declaringType) { + members.Add(MemberTracker.FromMemberInfo(mi, declaringType)); + } else { + members.Add(MemberTracker.FromMemberInfo(mi)); + } + } + + // TODO: Support indexed getters/setters w/ multiple methods + MethodInfo getter = null, setter = null, deleter = null; + foreach (MemberInfo mi in ext.GetMember("Get" + name, MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)) { + if (!mi.IsDefined(typeof(PropertyMethodAttribute), false)) continue; + + Debug.Assert(getter == null); + getter = (MethodInfo)mi; + } + + foreach (MemberInfo mi in ext.GetMember("Set" + name, MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)) { + if (!mi.IsDefined(typeof(PropertyMethodAttribute), false)) continue; + Debug.Assert(setter == null); + setter = (MethodInfo)mi; + } + + foreach (MemberInfo mi in ext.GetMember("Delete" + name, MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)) { + if (!mi.IsDefined(typeof(PropertyMethodAttribute), false)) continue; + Debug.Assert(deleter == null); + deleter = (MethodInfo)mi; + } + + if (getter != null || setter != null || deleter != null) { + members.Add(new ExtensionPropertyTracker(name, getter, setter, deleter, declaringType)); + } + } + + if (members.Count != 0) { + return MemberGroup.CreateInternal(members.ToArray()); + } + return MemberGroup.EmptyGroup; + } + + public virtual IList GetExtensionTypes(Type t) { + // None are provided by default, languages need to know how to + // provide these on their own terms. + return new Type[0]; + } + + /// + /// Provides an opportunity for languages to replace all MemberInfo's with their own type. + /// + /// Alternatlely a language can expose MemberInfo's directly. + /// + /// The member which is being returned to the user. + /// Tthe type which the memberTrack was accessed from + /// + public virtual Expression ReturnMemberTracker(Type type, MemberTracker memberTracker) { + if (memberTracker.MemberType == TrackerTypes.Bound) { + BoundMemberTracker bmt = (BoundMemberTracker)memberTracker; + return Expression.New( + typeof(BoundMemberTracker).GetConstructor(new Type[] { typeof(MemberTracker), typeof(object) }), + Expression.Constant(bmt.BoundTo), + bmt.Instance); + } + + return Expression.Constant(memberTracker); + } + + /// + /// Builds an expression for a call to the provided method using the given expressions. If the + /// method is not static the first parameter is used for the instance. + /// + /// Parameters are converted using the binder's conversion rules. + /// + /// If an incorrect number of parameters is provided MakeCallExpression returns null. + /// + public Expression MakeCallExpression(Expression context, MethodInfo method, IList parameters) { + ParameterInfo[] infos = method.GetParameters(); + Expression callInst = null; + int parameter = 0, startArg = 0; + Expression[] callArgs = new Expression[infos.Length]; + + if (!method.IsStatic) { + callInst = AstUtils.Convert(parameters[0], method.DeclaringType); + parameter = 1; + } + if (infos.Length > 0 && typeof(CodeContext).IsAssignableFrom(infos[0].ParameterType)) { + startArg = 1; + callArgs[0] = context; + } + + for (int arg = startArg; arg < infos.Length; arg++) { + if (parameter < parameters.Count) { + callArgs[arg] = ConvertExpression( + parameters[parameter++], + infos[arg].ParameterType, + ConversionResultKind.ExplicitCast, + context + ); + } else { + return null; + } + } + + // check that we used all parameters + if (parameter != parameters.Count) { + return null; + } + + return AstUtils.SimpleCallHelper(callInst, method, callArgs); + } + + + public Expression MakeCallExpression(Expression context, MethodInfo method, params Expression[] parameters) { + return MakeCallExpression(context, method, (IList)parameters); + } + + #region TODO: move to ParameterBinder + + /// + /// Gets an expression that evaluates to the result of GetByRefArray operation. + /// + public virtual Expression GetByRefArrayExpression(Expression argumentArrayExpression) { + return argumentArrayExpression; + } + + /// + /// Handles binding of special parameters. + /// + /// True if the argument is handled by this method. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#")] + internal protected virtual bool BindSpecialParameter(ParameterInfo parameterInfo, List arguments, + List parameters, ref int index) { + + // CodeContext is implicitly provided at runtime, the user cannot provide it. + if (parameterInfo.ParameterType == typeof(CodeContext) && arguments.Count == 0) { + arguments.Add(new ContextArgBuilder(parameterInfo)); + return true; + } else if (parameterInfo.ParameterType.IsGenericType && parameterInfo.ParameterType.GetGenericTypeDefinition() == typeof(SiteLocalStorage<>)) { + arguments.Add(new SiteLocalStorageBuilder(parameterInfo)); + return true; + } + + return false; + } + + /// + /// Called before arguments binding. + /// + /// The number of parameter infos to skip. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#")] + internal protected virtual int PrepareParametersBinding(ParameterInfo[] parameterInfos, List arguments, + List parameters, ref int index) { + return 0; + } + + #endregion + } +} + diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Argument.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Argument.cs new file mode 100644 index 0000000000..5b3f7479f2 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Argument.cs @@ -0,0 +1,92 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using Microsoft.Contracts; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + /// + /// TODO: Alternatively, it should be sufficient to remember indices for this, list, dict and block. + /// + public struct Argument : IEquatable { + private readonly ArgumentType _kind; + private readonly SymbolId _name; + + public static readonly Argument Simple = new Argument(ArgumentType.Simple, SymbolId.Empty); + + public ArgumentType Kind { get { return _kind; } } + public SymbolId Name { get { return _name; } } + + public Argument(SymbolId name) { + _kind = ArgumentType.Named; + _name = name; + } + + public Argument(ArgumentType kind) { + _kind = kind; + _name = SymbolId.Empty; + } + + public Argument(ArgumentType kind, SymbolId name) { + ContractUtils.Requires((kind == ArgumentType.Named) ^ (name == SymbolId.Empty), "kind"); + _kind = kind; + _name = name; + } + + public override bool Equals(object obj) { + return obj is Argument && Equals((Argument)obj); + } + + [StateIndependent] + public bool Equals(Argument other) { + return _kind == other._kind && _name == other._name; + } + + public static bool operator ==(Argument left, Argument right) { + return left.Equals(right); + } + + public static bool operator !=(Argument left, Argument right) { + return !left.Equals(right); + } + + public override int GetHashCode() { + return _name.GetHashCode() ^ (int)_kind; + } + + public bool IsSimple { + get { + return Equals(Simple); + } + } + + public override string ToString() { + return _name == SymbolId.Empty ? _kind.ToString() : _kind.ToString() + ":" + SymbolTable.IdToString(_name); + } + + internal Expression CreateExpression() { + return Expression.New( + typeof(Argument).GetConstructor(new Type[] { typeof(ArgumentType), typeof(SymbolId) }), + Expression.Constant(_kind), + AstUtils.Constant(_name) + ); + } + } +} + + diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ArgumentType.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ArgumentType.cs new file mode 100644 index 0000000000..9be76a1f2b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ArgumentType.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Actions { + /// + /// Convention for an individual argument at a callsite. + /// + /// Multiple different callsites can match against a single declaration. + /// Some argument kinds can be "unrolled" into multiple arguments, such as list and dictionary. + /// + public enum ArgumentType { + /// + /// Simple unnamed positional argument. + /// In Python: foo(1,2,3) are all simple arguments. + /// + Simple, + + /// + /// Argument with associated name at the callsite + /// In Python: foo(a=1) + /// + Named, + + /// + /// Argument containing a list of arguments. + /// In Python: foo(*(1,2*2,3)) would match 'def foo(a,b,c)' with 3 declared arguments such that (a,b,c)=(1,4,3). + /// it could also match 'def foo(*l)' with 1 declared argument such that l=(1,4,3) + /// + List, + + /// + /// Argument containing a dictionary of named arguments. + /// In Python: foo(**{'a':1, 'b':2}) + /// + Dictionary, + + + Instance + }; +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/BinderHelper.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/BinderHelper.cs new file mode 100644 index 0000000000..890a8916d0 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/BinderHelper.cs @@ -0,0 +1,222 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public class BinderHelper { + internal BinderHelper() { } + + // This can produce a IsCallable rule that returns the immutable constant isCallable. + // Beware that objects can have a mutable callable property. Eg, in Python, assign or delete the __call__ attribute. + public static bool MakeIsCallableRule(CodeContext context, object self, bool isCallable, RuleBuilder rule) { + rule.MakeTest(CompilerHelpers.GetType(self)); + rule.Target = + rule.MakeReturn( + context.LanguageContext.Binder, + Ast.Constant(isCallable) + ); + + return true; + } + } + + public class BinderHelper : BinderHelper + where TAction : OldDynamicAction { + + private readonly CodeContext _context; + private readonly TAction _action; + + public BinderHelper(CodeContext context, TAction action) { + ContractUtils.RequiresNotNull(context, "context"); + ContractUtils.RequiresNotNull(action, "action"); + + _context = context; + _action = action; + } + + protected CodeContext Context { + get { + return _context; + } + } + + protected TAction Action { + get { + return _action; + } + } + + protected DefaultBinder Binder { + get { + return (DefaultBinder)_context.LanguageContext.Binder; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] // TODO: fix + public static UnaryExpression GetParamsList(RuleBuilder rule) { + return Ast.Convert( + rule.Parameters[rule.ParameterCount - 1], + typeof(IList) + ); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] // TODO: fix + public static Expression MakeParamsTest(object paramArg, Expression listArg) { + return Ast.AndAlso( + Ast.TypeIs(listArg, CompilerHelpers.GetType(paramArg)), + Ast.Equal( + Ast.Property( + Ast.Convert(listArg, typeof(ICollection)), + typeof(ICollection).GetProperty("Count") + ), + Ast.Constant(((IList)paramArg).Count) + ) + ); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] // TODO: fix + public static Type[] GetArgumentTypes(OldCallAction action, object[] args) { + List res = new List(); + for (int i = 1; i < args.Length; i++) { + switch (action.Signature.GetArgumentKind(i - 1)) { + case ArgumentType.Simple: + case ArgumentType.Instance: + case ArgumentType.Named: + res.Add(CompilerHelpers.GetType(args[i])); + continue; + + case ArgumentType.List: + IList list = args[i] as IList; + if (list == null) return null; + + for (int j = 0; j < list.Count; j++) { + res.Add(CompilerHelpers.GetType(list[j])); + } + break; + + case ArgumentType.Dictionary: + // caller needs to process these... + break; + + default: + throw new NotImplementedException(); + } + } + return res.ToArray(); + } + + internal MethodInfo GetMethod(Type type, string name) { + // declaring type takes precedence + MethodInfo mi = type.GetMethod(name); + if (mi != null) { + return mi; + } + + // then search extension types. + Type curType = type; + do { + IList extTypes = Binder.GetExtensionTypes(curType); + foreach (Type t in extTypes) { + MethodInfo next = t.GetMethod(name); + if (next != null) { + if (mi != null) { + throw new AmbiguousMatchException(String.Format("Found multiple members for {0} on type {1}", name, curType)); + } + + mi = next; + } + } + + if (mi != null) { + return mi; + } + + curType = curType.BaseType; + } while (curType != null); + + return null; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] // TODO: fix + public static Expression MakeNecessaryTests(RuleBuilder rule, Type[] testTypes, IList arguments) { + Expression typeTest = Ast.Constant(true); + + if (testTypes != null) { + for (int i = 0; i < testTypes.Length; i++) { + if (testTypes[i] != null) { + Debug.Assert(i < arguments.Count); + typeTest = Ast.AndAlso(typeTest, rule.MakeTypeTest(testTypes[i], arguments[i])); + } + } + } + + return typeTest; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] // TODO: fix + public static Expression MakeNecessaryTests(RuleBuilder rule, IList necessaryTests, Expression[] arguments) { + if (necessaryTests.Count == 0) { + return Ast.Constant(true); + } + + Type[] mostSpecificTypes = null; // This is the final types that will be checked after inspecting all the sets + + for (int i = 0; i < necessaryTests.Count; i++) { + Type[] currentSet = necessaryTests[i]; + if (currentSet == null) { + // The current set is missing. Ignore it + continue; + } + + // All the sets should be of the same size + Debug.Assert(currentSet.Length == arguments.Length); + + if (mostSpecificTypes == null) { + mostSpecificTypes = new Type[currentSet.Length]; + } + + // For each type in the current set, check the type with the corresponding type in the previous sets + for (int j = 0; j < currentSet.Length; j++) { + if (mostSpecificTypes[j] == null || mostSpecificTypes[j].IsAssignableFrom(currentSet[j])) { + // no test yet or more specific test + mostSpecificTypes[j] = currentSet[j]; + } else { + // All sets should have compatible types in each slot + Debug.Assert(currentSet[j] == null || currentSet[j].IsAssignableFrom(mostSpecificTypes[j])); + } + } + } + + return MakeNecessaryTests(rule, mostSpecificTypes, arguments); + } + + protected bool PrivateBinding { + get { + return Context.LanguageContext.DomainManager.Configuration.PrivateBinding; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/BinderHelpers.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/BinderHelpers.cs new file mode 100644 index 0000000000..450d4f366e --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/BinderHelpers.cs @@ -0,0 +1,39 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using Microsoft.Scripting.Generation; + +namespace Microsoft.Scripting.Actions { + + public static class BinderHelpers { + + public static bool IsParamsMethod(MethodBase method) { + return IsParamsMethod(method.GetParameters()); + } + + public static bool IsParamsMethod(ParameterInfo[] pis) { + foreach (ParameterInfo pi in pis) { + if (CompilerHelpers.IsParamArray(pi) || IsParamDictionary(pi)) return true; + } + return false; + } + + public static bool IsParamDictionary(ParameterInfo parameter) { + return parameter.IsDefined(typeof(ParamDictionaryAttribute), false); + } + + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/BoundMemberTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/BoundMemberTracker.cs new file mode 100644 index 0000000000..85c5063712 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/BoundMemberTracker.cs @@ -0,0 +1,77 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Actions { + public class BoundMemberTracker : MemberTracker { + private Expression _instance; + private MemberTracker _tracker; + private object _objInst; + + public BoundMemberTracker(MemberTracker tracker, Expression instance) { + _tracker = tracker; + _instance = instance; + } + + public BoundMemberTracker(MemberTracker tracker, object instance) { + _tracker = tracker; + _objInst = instance; + } + + public override TrackerTypes MemberType { + get { return TrackerTypes.Bound; } + } + + public override Type DeclaringType { + get { return _tracker.DeclaringType; } + } + + public override string Name { + get { return _tracker.Name; } + } + + public Expression Instance { + get { + return _instance; + } + } + + public object ObjectInstance { + get { + return _objInst; + } + } + + public MemberTracker BoundTo { + get { + return _tracker; + } + } + + public override Expression GetValue(Expression context, ActionBinder binder, Type type) { + return _tracker.GetBoundValue(context, binder, type, _instance); + } + + public override ErrorInfo GetError(ActionBinder binder) { + return _tracker.GetBoundError(binder, _instance); + } + + public override Expression SetValue(Expression context, ActionBinder binder, Type type, Expression value) { + return _tracker.SetBoundValue(context, binder, type, value, _instance); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/CallBinderHelper.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/CallBinderHelper.cs new file mode 100644 index 0000000000..9f1354b216 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/CallBinderHelper.cs @@ -0,0 +1,554 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + /// + /// Creates rules for performing method calls. Currently supports calling built-in functions, built-in method descriptors (w/o + /// a bound value) and bound built-in method descriptors (w/ a bound value), delegates, types defining a "Call" method marked + /// with SpecialName. + /// + /// The specific type of CallAction + public class CallBinderHelper : BinderHelper + where TAction : OldCallAction { + + private object[] _args; // the arguments the binder is binding to - args[0] is the target, args[1..n] are args to the target + private Expression _instance; // the instance or null if this is a non-instance call + private Type _instanceType; // the type of _instance, to override _instance.Type when doing private binding. + private Expression _test; // the test expression, built up and assigned at the end + private readonly RuleBuilder _rule; // the rule we end up producing + private readonly bool _reversedOperator; // if we're producing a binary operator or a reversed operator (should go away, Python specific). + private readonly MethodBase[] _targets; + private readonly NarrowingLevel _maxLevel; // the maximum narrowing level allowed + + public CallBinderHelper(CodeContext context, TAction action, object[] args, RuleBuilder rule) + : base(context, action) { + ContractUtils.RequiresNotEmpty(args, "args"); + + _maxLevel = NarrowingLevel.All; + _args = RemoveExplicitInstanceArgument(action, args); + _rule = rule; + _test = _rule.MakeTypeTest(CompilerHelpers.GetType(Callable), 0); + } + + public CallBinderHelper(CodeContext context, TAction action, object[] args, RuleBuilder rule, IList targets) + : this(context, action, args, rule) { + _targets = ArrayUtils.ToArray(targets); + _maxLevel = NarrowingLevel.All; + } + + public CallBinderHelper(CodeContext context, TAction action, object[] args, RuleBuilder rule, IList targets, NarrowingLevel maxLevel, bool isReversedOperator) + : this(context, action, args, rule) { + _targets = ArrayUtils.ToArray(targets); + _reversedOperator = isReversedOperator; + _maxLevel = maxLevel; + } + + public virtual void MakeRule() { + Type t = CompilerHelpers.GetType(Callable); + + MethodBase[] targets = GetTargetMethods(); + if (targets != null && targets.Length > 0) { + // we're calling a well-known MethodBase + MakeMethodBaseRule(targets); + } else { + // we can't call this object + MakeCannotCallRule(t); + } + + // if we produced an ActionOnCall rule we don't replace the test w/ our own. + if (_rule.Test == null) { + _rule.Test = _test; + } + } + + #region Method Call Rule + + private void MakeMethodBaseRule(MethodBase[] targets) { + Type[] argTypes; // will not include implicit instance argument (if any) + SymbolId[] argNames; // will include ArgumentKind.Dictionary keyword names + + + GetArgumentNamesAndTypes(out argNames, out argTypes); + + Type[] bindingArgs = argTypes; // will include instance argument (if any) + CallTypes callType = CallTypes.None; + if (_instance != null) { + bindingArgs = ArrayUtils.Insert(InstanceType, argTypes); + callType = CallTypes.ImplicitInstance; + } + + if (_reversedOperator && bindingArgs.Length >= 2) { + // we swap the arguments before binding, and swap back before calling. + ArrayUtils.SwapLastTwo(bindingArgs); + if (argNames.Length >= 2) { + ArrayUtils.SwapLastTwo(argNames); + } + } + + // attempt to bind to an individual method + MethodBinder binder = MethodBinder.MakeBinder(Binder, GetTargetName(targets), targets, argNames, NarrowingLevel.None, _maxLevel); + BindingTarget bt = binder.MakeBindingTarget(callType, bindingArgs); + + if (bt.Success) { + // if we succeed make the target for the rule + MethodBase target = bt.Method; + MethodInfo targetMethod = target as MethodInfo; + + if (targetMethod != null) { + target = CompilerHelpers.GetCallableMethod(targetMethod, Binder.PrivateBinding); + } + + Expression[] exprargs = FinishTestForCandidate(bt.ArgumentTests, argTypes); + + _rule.Target = _rule.MakeReturn( + Binder, + bt.MakeExpression(_rule, exprargs)); + } else { + // make an error rule + MakeInvalidParametersRule(bt); + } + } + + private static object[] RemoveExplicitInstanceArgument(TAction action, object[] args) { + //If an instance is explicitly passed in as an argument, ignore it. + //Calls that need an instance will pick it up from the bound objects + //passed in or the rule. CallType can differentiate between the type + //of call during method binding. + int instanceIndex = action.Signature.IndexOf(ArgumentType.Instance); + if (instanceIndex > -1) { + args = ArrayUtils.RemoveAt(args, instanceIndex + 1); + } + return args; + } + + private static string GetTargetName(MethodBase[] targets) { + return targets[0].IsConstructor ? targets[0].DeclaringType.Name : targets[0].Name; + } + + protected Expression[] FinishTestForCandidate(IList testTypes, Type[] explicitArgTypes) { + Expression[] exprArgs = MakeArgumentExpressions(); + Debug.Assert(exprArgs.Length == (explicitArgTypes.Length + ((_instance == null) ? 0 : 1))); + Debug.Assert(testTypes == null || exprArgs.Length == testTypes.Count); + + MakeSplatTests(); + + if (_reversedOperator) { + ArrayUtils.SwapLastTwo(exprArgs); + } + + if (explicitArgTypes.Length > 0 && testTypes != null) { + // We've already tested the instance, no need to test it again. So remove it before adding + // rules for the arguments + Expression[] exprArgsWithoutInstance = exprArgs; + List testTypesWithoutInstance = new List(testTypes); + for (int i = 0; i < exprArgs.Length; i++) { + if (exprArgs[i] == _instance) { + // We found the instance, so remove it + exprArgsWithoutInstance = ArrayUtils.RemoveAt(exprArgs, i); + testTypesWithoutInstance.RemoveAt(i); + break; + } + } + + _test = Ast.AndAlso(_test, MakeNecessaryTests(_rule, testTypesWithoutInstance.ToArray(), exprArgsWithoutInstance)); + } + + return exprArgs; + } + + /// + /// Gets expressions to access all the arguments. This includes the instance argument. Splat arguments are + /// unpacked in the output. The resulting array is similar to Rule.Parameters (but also different in some ways) + /// + protected Expression[] MakeArgumentExpressions() { + List exprargs = new List(); + if (_instance != null) { + exprargs.Add(_instance); + } + + for (int i = 0; i < Action.Signature.ArgumentCount; i++) { // ArgumentCount(Action, _rule) + switch (Action.Signature.GetArgumentKind(i)) { + case ArgumentType.Simple: + case ArgumentType.Named: + exprargs.Add(_rule.Parameters[i + 1]); + break; + + case ArgumentType.List: + IList list = (IList)_args[i + 1]; + for (int j = 0; j < list.Count; j++) { + exprargs.Add( + Ast.Call( + Ast.Convert( + _rule.Parameters[i + 1], + typeof(IList) + ), + typeof(IList).GetMethod("get_Item"), + Ast.Constant(j) + ) + ); + } + break; + + case ArgumentType.Dictionary: + IDictionary dict = (IDictionary)_args[i + 1]; + + IDictionaryEnumerator dictEnum = dict.GetEnumerator(); + while (dictEnum.MoveNext()) { + DictionaryEntry de = dictEnum.Entry; + + string strKey = de.Key as string; + if (strKey == null) continue; + + Expression dictExpr = _rule.Parameters[_rule.Parameters.Count - 1]; + exprargs.Add( + Ast.Call( + AstUtils.Convert(dictExpr, typeof(IDictionary)), + typeof(IDictionary).GetMethod("get_Item"), + Ast.Constant(strKey) + ) + ); + } + break; + } + } + return exprargs.ToArray(); + } + + #endregion + + #region Target acquisition + + protected virtual MethodBase[] GetTargetMethods() { + if (_targets != null) return _targets; + + object target = Callable; + MethodBase[] targets; + Delegate d; + MemberGroup mg; + MethodGroup mthgrp; + BoundMemberTracker bmt; + + if ((d = target as Delegate) != null) { + targets = GetDelegateTargets(d); + } else if ((mg = target as MemberGroup) != null) { + List foundTargets = new List(); + foreach (MemberTracker mt in mg) { + if (mt.MemberType == TrackerTypes.Method) { + foundTargets.Add(((MethodTracker)mt).Method); + } + } + targets = foundTargets.ToArray(); + } else if ((mthgrp = target as MethodGroup) != null) { + _test = Ast.AndAlso(_test, Ast.Equal(Ast.Convert(Rule.Parameters[0], typeof(object)), Ast.Constant(target))); + + List foundTargets = new List(); + foreach (MethodTracker mt in mthgrp.Methods) { + foundTargets.Add(mt.Method); + } + + targets = foundTargets.ToArray(); + } else if ((bmt = target as BoundMemberTracker) != null) { + targets = GetBoundMemberTargets(bmt); + } else { + targets = GetOperatorTargets(target); + } + + return targets; + } + + private MethodBase[] GetBoundMemberTargets(BoundMemberTracker bmt) { + Debug.Assert(bmt.Instance == null); // should be null for trackers that leak to user code + + MethodBase[] targets; + _instance = Ast.Convert( + Ast.Property( + Ast.Convert(Rule.Parameters[0], typeof(BoundMemberTracker)), + typeof(BoundMemberTracker).GetProperty("ObjectInstance") + ), + bmt.BoundTo.DeclaringType + ); + _test = Ast.AndAlso( + _test, + Ast.Equal( + Ast.Property( + Ast.Convert(Rule.Parameters[0], typeof(BoundMemberTracker)), + typeof(BoundMemberTracker).GetProperty("BoundTo") + ), + Ast.Constant(bmt.BoundTo) + ) + ); + _test = Ast.AndAlso( + _test, + Rule.MakeTypeTest( + CompilerHelpers.GetType(bmt.ObjectInstance), + Ast.Property( + Ast.Convert(Rule.Parameters[0], typeof(BoundMemberTracker)), + typeof(BoundMemberTracker).GetProperty("ObjectInstance") + ) + ) + ); + switch (bmt.BoundTo.MemberType) { + case TrackerTypes.MethodGroup: + targets = ((MethodGroup)bmt.BoundTo).GetMethodBases(); + break; + case TrackerTypes.Method: + targets = new MethodBase[] { ((MethodTracker)bmt.BoundTo).Method }; + break; + default: + throw new InvalidOperationException(); // nothing else binds yet + } + return targets; + } + + private MethodBase[] GetDelegateTargets(Delegate d) { + _instance = AstUtils.Convert(_rule.Parameters[0], d.GetType()); + return new MethodBase[] { d.GetType().GetMethod("Invoke") }; + } + + private MethodBase[] GetOperatorTargets(object target) { + MethodBase[] targets = null; + + // see if the type defines a well known Call method + Type targetType = CompilerHelpers.GetType(target); + + + + MemberGroup callMembers = Binder.GetMember(Action, targetType, "Call"); + List callTargets = new List(); + foreach (MemberTracker mi in callMembers) { + if (mi.MemberType == TrackerTypes.Method) { + MethodInfo method = ((MethodTracker)mi).Method; + if (method.IsSpecialName) { + callTargets.Add(method); + } + } + } + if (callTargets.Count > 0) { + targets = callTargets.ToArray(); + _instance = Ast.Convert(_rule.Parameters[0], CompilerHelpers.GetType(Callable)); + } + + return targets; + } + + #endregion + + #region Test support + + /// + /// Makes test for param arrays and param dictionary parameters. + /// + protected void MakeSplatTests() { + if (Action.Signature.HasListArgument()) { + MakeParamsArrayTest(); + } + + if (Action.Signature.HasDictionaryArgument()) { + MakeParamsDictionaryTest(); + } + } + + private void MakeParamsArrayTest() { + int listIndex = Action.Signature.IndexOf(ArgumentType.List); + Debug.Assert(listIndex != -1); + _test = Ast.AndAlso(_test, MakeParamsTest(_args[listIndex + 1], _rule.Parameters[listIndex + 1])); + } + + private void MakeParamsDictionaryTest() { + IDictionary dict = (IDictionary)_args[_args.Length - 1]; + IDictionaryEnumerator dictEnum = dict.GetEnumerator(); + + // verify the dictionary has the same count and arguments. + + string[] names = new string[dict.Count]; + int index = 0; + while (dictEnum.MoveNext()) { + string name = dictEnum.Entry.Key as string; + if (name == null) { + throw new ArgumentTypeException(String.Format("expected string for dictionary argument got {0}", dictEnum.Entry.Key)); + } + names[index++] = name; + } + + _test = Ast.AndAlso( + _test, + Ast.AndAlso( + Ast.TypeIs(_rule.Parameters[_rule.Parameters.Count - 1], typeof(IDictionary)), + Ast.Call( + typeof(ScriptingRuntimeHelpers).GetMethod("CheckDictionaryMembers"), + Ast.Convert(_rule.Parameters[_rule.Parameters.Count - 1], typeof(IDictionary)), + Ast.Constant(names) + ) + ) + ); + } + + #endregion + + #region Error support + + protected virtual void MakeCannotCallRule(Type type) { + _rule.Target = + _rule.MakeError( + Ast.New( + typeof(ArgumentTypeException).GetConstructor(new Type[] { typeof(string) }), + Ast.Constant(type.Name + " is not callable") + ) + ); + } + + private void MakeInvalidParametersRule(BindingTarget bt) { + MakeSplatTests(); + + if (_args.Length > 1) { + // we do an exact type check on all of the arguments types for a failed call. + Expression[] argExpr = MakeArgumentExpressions(); + SymbolId[] names; + Type[] vals; + GetArgumentNamesAndTypes(out names, out vals); + if (_instance != null) { + // target type was added to test already + argExpr = ArrayUtils.RemoveFirst(argExpr); + } + + _test = Ast.AndAlso(_test, MakeNecessaryTests(_rule, vals, argExpr)); + } + + _rule.Target = Binder.MakeInvalidParametersError(bt).MakeErrorForRule(_rule, Binder); + } + + #endregion + + #region Misc. Helpers + + /// + /// Gets all of the argument names and types. The instance argument is not included + /// + /// The names correspond to the end of argTypes. + /// ArgumentKind.Dictionary is unpacked in the return value. + /// This is set to an array of size 0 if there are no keyword arguments + /// Non named arguments are returned at the beginning. + /// ArgumentKind.List is unpacked in the return value. + protected void GetArgumentNamesAndTypes(out SymbolId[] argNames, out Type[] argTypes) { + // Get names of named arguments + argNames = Action.Signature.GetArgumentNames(); + + argTypes = GetArgumentTypes(Action, _args); + + if (Action.Signature.HasDictionaryArgument()) { + // need to get names from dictionary argument... + GetDictionaryNamesAndTypes(ref argNames, ref argTypes); + } + } + + private void GetDictionaryNamesAndTypes(ref SymbolId[] argNames, ref Type[] argTypes) { + Debug.Assert(Action.Signature.GetArgumentKind(Action.Signature.ArgumentCount - 1) == ArgumentType.Dictionary); + + List names = new List(argNames); + List types = new List(argTypes); + + IDictionary dict = (IDictionary)_args[_args.Length - 1]; + IDictionaryEnumerator dictEnum = dict.GetEnumerator(); + while (dictEnum.MoveNext()) { + DictionaryEntry de = dictEnum.Entry; + + if (de.Key is string) { + names.Add(SymbolTable.StringToId((string)de.Key)); + types.Add(CompilerHelpers.GetType(de.Value)); + } + } + + argNames = names.ToArray(); + argTypes = types.ToArray(); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] // TODO: fix + protected object[] Arguments { + get { + return _args; + } + } + + protected object[] GetExplicitArguments() { + return ArrayUtils.RemoveFirst(_args); + } + + protected object Callable { + get { + return _args[0]; + } + } + + public RuleBuilder Rule { + get { + return _rule; + } + } + + /// + /// The instance for the target method, or null if this is a non-instance call. + /// + /// If it is set, it will typically be set to extract the instance from the Callable. + /// + public Expression Instance { + get { + return _instance; + } + set { + Debug.Assert(!Action.Signature.HasInstanceArgument()); + _instance = value; + } + } + + public Type InstanceType { + get { + if (_instanceType != null) { + return _instanceType; + } + if (_instance != null) { + return _instance.Type; + } + return null; + } + set { + _instanceType = value; + } + } + + protected Expression Test { + get { + return _test; + } + set { + _test = value; + } + } + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/CallSignature.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/CallSignature.cs new file mode 100644 index 0000000000..43d5232aaa --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/CallSignature.cs @@ -0,0 +1,324 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using System.Text; +using Microsoft.Contracts; +using Microsoft.Scripting.Generation; + +namespace Microsoft.Scripting.Actions { + /// + /// Richly represents the signature of a callsite. + /// + public struct CallSignature : IEquatable { + // TODO: invariant _infos != null ==> _argumentCount == _infos.Length + + /// + /// Array of additional meta information about the arguments, such as named arguments. + /// Null for a simple signature that's just an expression list. eg: foo(a*b,c,d) + /// + private readonly Argument[] _infos; + + /// + /// Number of arguments in the signature. + /// + private readonly int _argumentCount; + + /// + /// All arguments are unnamed and matched by position. + /// + public bool IsSimple { + get { return _infos == null; } + } + + public int ArgumentCount { + get { + Debug.Assert(_infos == null || _infos.Length == _argumentCount); + return _argumentCount; + } + } + + #region Construction + + public CallSignature(CallSignature signature) { + _infos = signature.GetArgumentInfos(); + _argumentCount = signature._argumentCount; + } + + public CallSignature(int argumentCount) { + ContractUtils.Requires(argumentCount >= 0, "argumentCount"); + _argumentCount = argumentCount; + _infos = null; + } + + public CallSignature(params Argument[] infos) { + bool simple = true; + + if (infos != null) { + _argumentCount = infos.Length; + for (int i = 0; i < infos.Length; i++) { + if (infos[i].Kind != ArgumentType.Simple) { + simple = false; + break; + } + } + } else { + _argumentCount = 0; + } + + _infos = (!simple) ? infos : null; + } + + public CallSignature(params ArgumentType[] kinds) { + bool simple = true; + + if (kinds != null) { + _argumentCount = kinds.Length; + for (int i = 0; i < kinds.Length; i++) { + if (kinds[i] != ArgumentType.Simple) { + simple = false; + break; + } + } + } else { + _argumentCount = 0; + } + + if (!simple) { + _infos = new Argument[kinds.Length]; + for (int i = 0; i < kinds.Length; i++) { + _infos[i] = new Argument(kinds[i]); + } + } else { + _infos = null; + } + } + + #endregion + + #region IEquatable Members + + [StateIndependent] + public bool Equals(CallSignature other) { + if (_infos == null) { + return other._infos == null; + } else if (other._infos == null) { + return false; + } + + if (_infos.Length != other._infos.Length) return false; + + for (int i = 0; i < _infos.Length; i++) { + if (!_infos[i].Equals(other._infos[i])) return false; + } + + return true; + } + + #endregion + + #region Overrides + + public override bool Equals(object obj) { + return obj is CallSignature && Equals((CallSignature)obj); + } + + public static bool operator ==(CallSignature left, CallSignature right) { + return left.Equals(right); + } + + public static bool operator !=(CallSignature left, CallSignature right) { + return !left.Equals(right); + } + + public override string ToString() { + if (_infos == null) { + return "Simple"; + } + + StringBuilder sb = new StringBuilder("("); + for (int i = 0; i < _infos.Length; i++) { + if (i > 0) { + sb.Append(", "); + } + sb.Append(_infos[i].ToString()); + } + sb.Append(")"); + return sb.ToString(); + } + + public override int GetHashCode() { + int h = 6551; + if (_infos != null) { + foreach (Argument info in _infos) { + h ^= (h << 5) ^ info.GetHashCode(); + } + } + return h; + } + + #endregion + + #region Helpers + + public Argument[] GetArgumentInfos() { + return (_infos != null) ? ArrayUtils.Copy(_infos) : CompilerHelpers.MakeRepeatedArray(Argument.Simple, _argumentCount); + } + + public CallSignature InsertArgument(Argument info) { + return InsertArgumentAt(0, info); + } + + public CallSignature InsertArgumentAt(int index, Argument info) { + if (this.IsSimple) { + if (info.IsSimple) { + return new CallSignature(_argumentCount + 1); + } + + return new CallSignature(ArrayUtils.InsertAt(GetArgumentInfos(), index, info)); + } + + return new CallSignature(ArrayUtils.InsertAt(_infos, index, info)); + } + + public CallSignature RemoveFirstArgument() { + return RemoveArgumentAt(0); + } + + public CallSignature RemoveArgumentAt(int index) { + if (_argumentCount == 0) { + throw new InvalidOperationException(); + } + + if (IsSimple) { + return new CallSignature(_argumentCount - 1); + } + + return new CallSignature(ArrayUtils.RemoveAt(_infos, index)); + } + + public int IndexOf(ArgumentType kind) { + if (_infos == null) { + return (kind == ArgumentType.Simple && _argumentCount > 0) ? 0 : -1; + } + + for (int i = 0; i < _infos.Length; i++) { + if (_infos[i].Kind == kind) { + return i; + } + } + return -1; + } + + public bool HasDictionaryArgument() { + return IndexOf(ArgumentType.Dictionary) > -1; + } + + public bool HasInstanceArgument() { + return IndexOf(ArgumentType.Instance) > -1; + } + + public bool HasListArgument() { + return IndexOf(ArgumentType.List) > -1; + } + + internal bool HasNamedArgument() { + return IndexOf(ArgumentType.Named) > -1; + } + + /// + /// True if the OldCallAction includes an ArgumentInfo of ArgumentKind.Dictionary or ArgumentKind.Named. + /// + public bool HasKeywordArgument() { + if (_infos != null) { + foreach (Argument info in _infos) { + if (info.Kind == ArgumentType.Dictionary || info.Kind == ArgumentType.Named) { + return true; + } + } + } + return false; + } + + public ArgumentType GetArgumentKind(int index) { + // TODO: Contract.Requires(index >= 0 && index < _argumentCount, "index"); + return _infos != null ? _infos[index].Kind : ArgumentType.Simple; + } + + public SymbolId GetArgumentName(int index) { + ContractUtils.Requires(index >= 0 && index < _argumentCount); + return _infos != null ? _infos[index].Name : SymbolId.Empty; + } + + /// + /// Gets the number of positional arguments the user provided at the call site. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public int GetProvidedPositionalArgumentCount() { + int result = _argumentCount; + + if (_infos != null) { + for (int i = 0; i < _infos.Length; i++) { + ArgumentType kind = _infos[i].Kind; + + if (kind == ArgumentType.Dictionary || kind == ArgumentType.List || kind == ArgumentType.Named) { + result--; + } + } + } + + return result; + } + + public SymbolId[] GetArgumentNames() { + if (_infos == null) { + return SymbolId.EmptySymbols; + } + + List result = new List(); + foreach (Argument info in _infos) { + if (info.Name != SymbolId.Empty) { + result.Add(info.Name); + } + } + + return result.ToArray(); + } + + public Expression CreateExpression() { + if (_infos == null) { + return Expression.New( + typeof(CallSignature).GetConstructor(new Type[] { typeof(int) }), + Expression.Constant(ArgumentCount) + ); + } else { + Expression[] args = new Expression[_infos.Length]; + for (int i = 0; i < args.Length; i++) { + args[i] = _infos[i].CreateExpression(); + } + return Expression.New( + typeof(CallSignature).GetConstructor(new Type[] { typeof(Argument[]) }), + Expression.NewArrayInit(typeof(Argument), args) + ); + } + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ArgBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ArgBuilder.cs new file mode 100644 index 0000000000..26441d515a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ArgBuilder.cs @@ -0,0 +1,87 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Diagnostics; +using Microsoft.Scripting.Utils; +using System.Reflection; + +namespace Microsoft.Scripting.Actions.Calls { + /// + /// ArgBuilder provides an argument value used by the MethodBinder. One ArgBuilder exists for each + /// physical parameter defined on a method. + /// + /// Contrast this with ParameterWrapper which represents the logical argument passed to the method. + /// + public abstract class ArgBuilder { + // can be null, e.g. for ctor return value builder or custom arg builders + private readonly ParameterInfo _info; + + protected ArgBuilder(ParameterInfo info) { + _info = info; + } + + public abstract int Priority { + get; + } + + public ParameterInfo ParameterInfo { + get { return _info; } + } + + /// + /// Provides the Expression which provides the value to be passed to the argument. + /// If null is returned the argument is skipped (not passed to the callee). + /// + internal protected abstract Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed); + + /// + /// Returns the type required for the argument or null if the ArgBuilder + /// does not consume a type. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + public virtual Type Type { + get { + return null; + } + } + + /// + /// Provides an Expression which will update the provided value after a call to the method. May + /// return null if no update is required. + /// + internal virtual Expression UpdateFromReturn(ParameterBinder parameterBinder, IList parameters) { + return null; + } + + /// + /// If the argument produces a return value (e.g. a ref or out value) this provides + /// the additional value to be returned. + /// + internal virtual Expression ToReturnExpression(ParameterBinder parameterBinder) { + throw new InvalidOperationException(); + } + + /// + /// An assignable value that is passed to a byref parameter + /// After the call it will contain the updated value + /// + internal virtual Expression ByRefArgument { + get { return null; } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/BindingResult.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/BindingResult.cs new file mode 100644 index 0000000000..87e3336838 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/BindingResult.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Actions.Calls { + /// + /// Indicates the specific type of failure, if any, from binding to a method. + /// + public enum BindingResult { + /// + /// The binding succeeded. Only one method was applicable or had the best conversion. + /// + Success, + /// + /// More than one method was applicable for the provided parameters and no method was considered the best. + /// + AmbiguousMatch, + /// + /// There are no overloads that match the number of parameters required for the call + /// + IncorrectArgumentCount, + /// + /// None of the target method(s) can successfully be called. The failure can be due to: + /// 1. Arguments could not be successfully converted for the call + /// 2. Keyword arguments could not be assigned to positional arguments + /// 3. Keyword arguments could be assigned but would result in an argument being assigned + /// multiple times (keyword and positional arguments conflit or dupliate keyword arguments). + /// + CallFailure, + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/BindingTarget.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/BindingTarget.cs new file mode 100644 index 0000000000..49cd2f485a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/BindingTarget.cs @@ -0,0 +1,312 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic.Binders; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Actions.Calls { + /// + /// Encapsulates the result of an attempt to bind to one or methods using the MethodBinder. + /// + /// Users should first check the Result property to see if the binding was successful or + /// to determine the specific type of failure that occured. If the binding was successful + /// MakeExpression can then be called to create an expression which calls the method. + /// If the binding was a failure callers can then create a custom error message based upon + /// the reason the call failed. + /// + public sealed class BindingTarget { + private readonly BindingResult _result; // the result of the binding + private readonly string _name; // the name of the method being bound to + private readonly MethodTarget _target; // the MethodTarget if the binding was successful + private readonly Type[] _argTests; // Deprecated: if successful tests needed to disambiguate between overloads, MetaObject binding is preferred + private readonly MetaObject[] _restrictedArgs; // the arguments after they've been restricted to their known types + private readonly NarrowingLevel _level; // the NarrowingLevel at which the target succeeds on conversion + private readonly CallFailure[] _callFailures; // if failed on conversion the various conversion failures for all overloads + private readonly MethodTarget[] _ambiguousMatches; // list of methods which are ambiguous to bind to. + private readonly int[] _expectedArgs; // gets the acceptable number of parameters which can be passed to the method. + private readonly int _actualArgs; // gets the actual number of arguments provided + + /// + /// Creates a new BindingTarget when the method binding has succeeded + /// + internal BindingTarget(string name, int actualArgumentCount, MethodTarget target, NarrowingLevel level, Type[] argTests) { + _name = name; + _target = target; + _argTests = argTests; + _level = level; + _actualArgs = actualArgumentCount; + } + + /// + /// Creates a new BindingTarget when the method binding has succeeded + /// + internal BindingTarget(string name, int actualArgumentCount, MethodTarget target, NarrowingLevel level, MetaObject[] restrictedArgs) { + _name = name; + _target = target; + _restrictedArgs = restrictedArgs; + _level = level; + _actualArgs = actualArgumentCount; + } + + /// + /// Creates a new BindingTarget when the method binding has failed due to an incorrect argument count + /// + internal BindingTarget(string name, int actualArgumentCount, int[] expectedArgCount) { + _name = name; + _result = BindingResult.IncorrectArgumentCount; + _expectedArgs = expectedArgCount; + _actualArgs = actualArgumentCount; + } + + /// + /// Creates a new BindingTarget when the method binding has failued due to + /// one or more parameters which could not be converted. + /// + internal BindingTarget(string name, int actualArgumentCount, CallFailure[] failures) { + _name = name; + _result = BindingResult.CallFailure; + _callFailures = failures; + _actualArgs = actualArgumentCount; + } + + /// + /// Creates a new BindingTarget when the match was ambiguous + /// + internal BindingTarget(string name, int actualArgumentCount, MethodTarget[] ambiguousMatches) { + _name = name; + _result = BindingResult.AmbiguousMatch; + _ambiguousMatches = ambiguousMatches; + _actualArgs = actualArgumentCount; + } + + /// + /// Gets the result of the attempt to bind. + /// + public BindingResult Result { + get { + return _result; + } + } + + /// + /// Gets an Expression which calls the binding target if the method binding succeeded. + /// + /// Throws InvalidOperationException if the binding failed. + /// + /// OBSOLETE + /// + public Expression MakeExpression(RuleBuilder rule, IList parameters) { + ContractUtils.RequiresNotNull(rule, "rule"); + + if (_target == null) { + throw new InvalidOperationException("An expression cannot be produced because the method binding was unsuccessful."); + } + + return MakeExpression(new ParameterBinderWithCodeContext(_target.Binder._binder, rule.Context), parameters); + } + + /// + /// Gets an Expression which calls the binding target if the method binding succeeded. + /// + /// Throws InvalidOperationException if the binding failed. + /// + /// OBSOLETE + /// + public Expression MakeExpression(ParameterBinder parameterBinder, IList parameters) { + ContractUtils.RequiresNotNull(parameterBinder, "parameterBinder"); + ContractUtils.RequiresNotNull(parameters, "parameters"); + + if (_target == null) { + throw new InvalidOperationException("An expression cannot be produced because the method binding was unsuccessful."); + } + + return _target.MakeExpression(parameterBinder, parameters, ArgumentTests); + } + + /// + /// Gets an Expression which calls the binding target if the method binding succeeded. + /// + /// Throws InvalidOperationException if the binding failed. + /// + public Expression MakeExpression() { + if (_target == null) { + throw new InvalidOperationException("An expression cannot be produced because the method binding was unsuccessful."); + } + + return MakeExpression(new ParameterBinder(_target.Binder._binder)); + } + + /// + /// Gets an Expression which calls the binding target if the method binding succeeded. + /// + /// Throws InvalidOperationException if the binding failed. + /// + public Expression MakeExpression(ParameterBinder parameterBinder) { + ContractUtils.RequiresNotNull(parameterBinder, "parameterBinder"); + + if (_target == null) { + throw new InvalidOperationException("An expression cannot be produced because the method binding was unsuccessful."); + } else if (_restrictedArgs == null) { + throw new InvalidOperationException("An expression cannot be produced because the method binding was done with Expressions, not MetaObject's"); + } + + Expression[] exprs = new Expression[_restrictedArgs.Length]; + for (int i = 0; i < exprs.Length; i++) { + exprs[i] = _restrictedArgs[i].Expression; + } + + return _target.MakeExpression(parameterBinder, exprs); + } + + /// + /// Returns the method if the binding succeeded, or null if no method was applicable. + /// + public MethodBase Method { + get { + if (_target != null) { + return _target.Method; + } + + return null; + } + } + + /// + /// Gets the name of the method as supplied to the MethodBinder. + /// + public string Name { + get { + return _name; + } + } + + /// + /// Returns the MethodTarget if the binding succeeded, or null if no method was applicable. + /// + public MethodTarget MethodTarget { + get { + return _target; + } + } + + /// + /// Returns the methods which don't have any matches or null if Result == BindingResult.AmbiguousMatch + /// + public IEnumerable AmbiguousMatches { + get { + return _ambiguousMatches; + } + } + + /// + /// Returns the methods and their associated conversion failures if Result == BindingResult.CallFailure. + /// + public ICollection CallFailures { + get { + return _callFailures; + } + } + + /// + /// Returns the acceptable number of arguments which can be passed to the method if Result == BindingResult.IncorrectArgumentCount. + /// + public IList ExpectedArgumentCount { + get { + return _expectedArgs; + } + } + + /// + /// Returns the number of arguments provided to the call. 0 if the call succeeded or failed for a reason other + /// than argument count mismatch. + /// + public int ActualArgumentCount { + get { + return _actualArgs; + } + } + + /// + /// Gets the type tests that need to be performed to ensure that a call is + /// not applicable for an overload. + /// + /// The members of the array correspond to each of the arguments. An element is + /// null if no test is necessary. + /// + public IList ArgumentTests { + get { + return _argTests; + } + } + + /// + /// Gets the MetaObjects which we originally did binding against in their restricted form. + /// + /// The members of the array correspond to each of the arguments. All members of the array + /// have a value. + /// + public IList RestrictedArguments { + get { + return _restrictedArgs; + } + } + + /// + /// Returns the return type of the binding, or null if no method was applicable. + /// + public Type ReturnType { + get { + if (_target != null) { + return _target.ReturnType; + } + + return null; + } + } + + /// + /// Returns the NarrowingLevel of the method if the call succeeded. If the call + /// failed returns NarrowingLevel.None. + /// + public NarrowingLevel NarrowingLevel { + get { + return _level; + } + } + + /// + /// Returns true if the binding was succesful, false if it failed. + /// + /// This is an alias for BindingTarget.Result == BindingResult.Success. + /// + public bool Success { + get { + return _result == BindingResult.Success; + } + } + + internal MethodTarget GetTargetThrowing() { + if (_target == null) throw new InvalidOperationException(); + + return _target; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ByRefReturnBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ByRefReturnBuilder.cs new file mode 100644 index 0000000000..3196403b19 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ByRefReturnBuilder.cs @@ -0,0 +1,72 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions.Calls { + using Ast = System.Linq.Expressions.Expression; + + internal sealed class ByRefReturnBuilder : ReturnBuilder { + private IList _returnArgs; + + public ByRefReturnBuilder(IList returnArgs) + : base(typeof(object)) { + _returnArgs = returnArgs; + } + + internal override Expression ToExpression(ParameterBinder parameterBinder, IList args, IList parameters, Expression ret) { + if (_returnArgs.Count == 1) { + if (_returnArgs[0] == -1) { + return ret; + } + return Ast.Block(ret, args[_returnArgs[0]].ToReturnExpression(parameterBinder)); + } + + Expression[] retValues = new Expression[_returnArgs.Count]; + int rIndex = 0; + bool usesRet = false; + foreach (int index in _returnArgs) { + if (index == -1) { + usesRet = true; + retValues[rIndex++] = ret; + } else { + retValues[rIndex++] = args[index].ToReturnExpression(parameterBinder); + } + } + + Expression retArray = AstUtils.NewArrayHelper(typeof(object), retValues); + if (!usesRet) { + retArray = Ast.Block(ret, retArray); + } + + return parameterBinder.Binder.GetByRefArrayExpression(retArray); + } + + private static object GetValue(object[] args, object ret, int index) { + if (index == -1) return ConvertToObject(ret); + return ConvertToObject(args[index]); + } + + public override int CountOutParams { + get { return _returnArgs.Count; } + } + + + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/CallFailure.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/CallFailure.cs new file mode 100644 index 0000000000..845ba7abd0 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/CallFailure.cs @@ -0,0 +1,94 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Dynamic; + +namespace Microsoft.Scripting.Actions.Calls { + /// + /// Represents the reason why a call to a specific method could not be performed by the MethodBinder. + /// + /// The reason for the failure is specified by the CallFailureReason property. Once this property + /// has been consulted the other properties can be consulted for more detailed information regarding + /// the failure. + /// + /// If reason is ConversionFailure the ConversionResults property will be non-null. + /// If reason is UnassignableKeyword the KeywordArguments property will be non-null and include + /// the keywords which could not be assigned. + /// If reason is DuplicateKeyword the KeywordArguments property will be non-null and include + /// the keywords which were duplicated (either by the keywords themselves or by positional + /// arguments). + /// + /// MethodTarget is always set and indicates the method which failed to bind. + /// + public sealed class CallFailure { + private CallFailureReason _reason; // the reason the call failed + private MethodTarget _target; + private ConversionResult[] _results; + private SymbolId[] _keywordArgs; + + internal CallFailure(MethodTarget target, ConversionResult[] results) { + _target = target; + _results = results; + _reason = CallFailureReason.ConversionFailure; + } + + internal CallFailure(MethodTarget target, SymbolId[] keywordArgs, bool unassignable) { + _reason = unassignable ? CallFailureReason.UnassignableKeyword : CallFailureReason.DuplicateKeyword; + _target = target; + _keywordArgs = keywordArgs; + } + + /// + /// Gets the MethodTarget which the call failed for. + /// + public MethodTarget Target { + get { + return _target; + } + } + + /// + /// Gets the reason for the call failure which determines the other + /// properties of the CallFailure which should be consulted. + /// + public CallFailureReason Reason { + get{ + return _reason; + } + } + + /// + /// Gets a list of ConversionResult's for each parameter indicating + /// whether the conversion was successful or failed and the types + /// being converted. + /// + public IList ConversionResults { + get { + return _results; + } + } + + /// + /// Gets the list of keyword arguments that were either dupliated or + /// unassignable. + /// + public IList KeywordArguments { + get { + return _keywordArgs; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/CallFailureReason.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/CallFailureReason.cs new file mode 100644 index 0000000000..00920bb4d3 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/CallFailureReason.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Actions.Calls { + public enum CallFailureReason { + /// + /// Default value, their was no CallFailure. + /// + None, + /// + /// One of more parameters failed to be converted + /// + ConversionFailure, + /// + /// One or more keyword arguments could not be successfully assigned to a positional argument + /// + UnassignableKeyword, + /// + /// One or more keyword arguments were duplicated or would have taken the spot of a + /// provided positional argument. + /// + DuplicateKeyword + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/Candidate.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/Candidate.cs new file mode 100644 index 0000000000..4c0cb30a25 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/Candidate.cs @@ -0,0 +1,43 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Scripting.Actions.Calls { + public enum Candidate { + Equivalent = 0, + One = +1, + Two = -1, + Ambiguous = 2 + } + + internal static class CandidateExtension { + public static bool Chosen(this Candidate candidate) { + return candidate == Candidate.One || candidate == Candidate.Two; + } + + public static Candidate TheOther(this Candidate candidate) { + if (candidate == Candidate.One) { + return Candidate.Two; + } + if (candidate == Candidate.Two) { + return Candidate.One; + } + return candidate; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ContextArgBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ContextArgBuilder.cs new file mode 100644 index 0000000000..e261745403 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ContextArgBuilder.cs @@ -0,0 +1,41 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Diagnostics; +using System.Reflection; + +namespace Microsoft.Scripting.Actions.Calls { + + // TODO: Move to Pyhton + + /// + /// ArgBuilder which provides the CodeContext parameter to a method. + /// + public sealed class ContextArgBuilder : ArgBuilder { + public ContextArgBuilder(ParameterInfo info) + : base(info){ + } + + public override int Priority { + get { return -1; } + } + + internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { + return ((ParameterBinderWithCodeContext)parameterBinder).ContextExpression; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ConversionResult.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ConversionResult.cs new file mode 100644 index 0000000000..15a7f626f5 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ConversionResult.cs @@ -0,0 +1,67 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace Microsoft.Scripting.Actions.Calls { + /// + /// Represents information about a failure to convert an argument from one + /// type to another. + /// + public sealed class ConversionResult { + private readonly Type _fromType; + private readonly Type _toType; + private readonly int _position; + private readonly bool _failed; + + public ConversionResult(Type fromType, Type toType, int position, bool failed) { + _fromType = fromType; + _toType = toType; + _position = position; + _failed = failed; + } + + public Type From { + get { + return _fromType; + } + } + + public Type To { + get { + return _toType; + } + } + + public int Position { + get { + return _position; + } + } + + public bool Failed { + get { + return _failed; + } + } + + internal static void ReplaceLastFailure(IList failures, int newIndex, bool isFailure) { + ConversionResult failure = failures[failures.Count - 1]; + failures.RemoveAt(failures.Count - 1); + failures.Add(new ConversionResult(failure.From, failure.To, newIndex, isFailure)); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/DefaultArgBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/DefaultArgBuilder.cs new file mode 100644 index 0000000000..9e3eea40ad --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/DefaultArgBuilder.cs @@ -0,0 +1,51 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Generation; + +namespace Microsoft.Scripting.Actions.Calls { + using Ast = System.Linq.Expressions.Expression; + + /// + /// ArgBuilder which provides a default parameter value for a method call. + /// + internal sealed class DefaultArgBuilder : ArgBuilder { + public DefaultArgBuilder(ParameterInfo info) + : base(info) { + } + + public override int Priority { + get { return 2; } + } + + internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { + object val = ParameterInfo.DefaultValue; + if (val is Missing) { + val = CompilerHelpers.GetMissingValue(ParameterInfo.ParameterType); + } + + if (ParameterInfo.ParameterType.IsByRef) { + return Ast.Constant(val, ParameterInfo.ParameterType.GetElementType()); + } + + return parameterBinder.ConvertExpression(Ast.Constant(val), ParameterInfo, ParameterInfo.ParameterType); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/KeywordArgBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/KeywordArgBuilder.cs new file mode 100644 index 0000000000..7a783ce93e --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/KeywordArgBuilder.cs @@ -0,0 +1,93 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Actions.Calls { + /// + /// ArgBuilder which provides a value for a keyword argument. + /// + /// The KeywordArgBuilder calculates its position at emit time using it's initial + /// offset within the keyword arguments, the number of keyword arguments, and the + /// total number of arguments provided by the user. It then delegates to an + /// underlying ArgBuilder which only receives the single correct argument. + /// + /// Delaying the calculation of the position to emit time allows the method binding to be + /// done without knowing the exact the number of arguments provided by the user. Hence, + /// the method binder can be dependent only on the set of method overloads and keyword names, + /// but not the user arguments. While the number of user arguments could be determined + /// upfront, the current MethodBinder does not have this design. + /// + internal sealed class KeywordArgBuilder : ArgBuilder { + private readonly int _kwArgCount, _kwArgIndex; + private readonly ArgBuilder _builder; + + public KeywordArgBuilder(ArgBuilder builder, int kwArgCount, int kwArgIndex) + : base(builder.ParameterInfo) { + + Debug.Assert(BuilderExpectsSingleParameter(builder)); + _builder = builder; + + Debug.Assert(kwArgIndex < kwArgCount); + _kwArgCount = kwArgCount; + _kwArgIndex = kwArgIndex; + } + + public override int Priority { + get { return _builder.Priority; } + } + + /// + /// The underlying builder should expect a single parameter as KeywordArgBuilder is responsible + /// for calculating the correct parameter to use + /// + /// + internal static bool BuilderExpectsSingleParameter(ArgBuilder builder) { + return (((SimpleArgBuilder)builder).Index == 0); + } + + internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { + Debug.Assert(BuilderExpectsSingleParameter(_builder)); + int index = GetKeywordIndex(parameters.Count); + hasBeenUsed[index] = true; + return _builder.ToExpression(parameterBinder, new Expression[] { parameters[index] }, new bool[1]); + } + + public override Type Type { + get { + return _builder.Type; + } + } + + internal override Expression ToReturnExpression(ParameterBinder parameterBinder) { + return _builder.ToReturnExpression(parameterBinder); + } + + internal override Expression UpdateFromReturn(ParameterBinder parameterBinder, IList parameters) { + return _builder.UpdateFromReturn(parameterBinder, new Expression[] { parameters[GetKeywordIndex(parameters.Count)] }); + } + + private int GetKeywordIndex(int paramCount) { + return paramCount - _kwArgCount + _kwArgIndex; + } + + internal override Expression ByRefArgument { + get { return _builder.ByRefArgument; } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/KeywordConstructorReturnBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/KeywordConstructorReturnBuilder.cs new file mode 100644 index 0000000000..58de822ba6 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/KeywordConstructorReturnBuilder.cs @@ -0,0 +1,130 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions.Calls { + using Ast = System.Linq.Expressions.Expression; + + /// + /// Updates fields/properties of the returned value with unused keyword parameters. + /// + class KeywordConstructorReturnBuilder : ReturnBuilder { + private readonly ReturnBuilder _builder; + private readonly int _kwArgCount; + private readonly int[] _indexesUsed; + private readonly MemberInfo[] _membersSet; + private readonly bool _privateBinding; + + public KeywordConstructorReturnBuilder(ReturnBuilder builder, int kwArgCount, int[] indexesUsed, MemberInfo[] membersSet, + bool privateBinding) + : base(builder.ReturnType) { + _builder = builder; + _kwArgCount = kwArgCount; + _indexesUsed = indexesUsed; + _membersSet = membersSet; + _privateBinding = privateBinding; + } + + internal override Expression ToExpression(ParameterBinder parameterBinder, IList args, IList parameters, Expression ret) { + List sets = new List(); + + ParameterExpression tmp = parameterBinder.GetTemporary(ret.Type, "val"); + sets.Add( + Ast.Assign(tmp, ret) + ); + + for (int i = 0; i < _indexesUsed.Length; i++) { + Expression value = parameters[parameters.Count - _kwArgCount + _indexesUsed[i]]; + switch(_membersSet[i].MemberType) { + case MemberTypes.Field: + FieldInfo fi = (FieldInfo)_membersSet[i]; + if (!fi.IsLiteral && !fi.IsInitOnly) { + sets.Add( + Ast.Assign( + Ast.Field(tmp, fi), + ConvertToHelper(parameterBinder, value, fi.FieldType) + ) + ); + } else { + // call a helper which throws the error but "returns object" + sets.Add( + Ast.Convert( + Ast.Call( + typeof(ScriptingRuntimeHelpers).GetMethod("ReadOnlyAssignError"), + Ast.Constant(true), + Ast.Constant(fi.Name) + ), + fi.FieldType + ) + ); + } + break; + + case MemberTypes.Property: + PropertyInfo pi = (PropertyInfo)_membersSet[i]; + if (pi.GetSetMethod(_privateBinding) != null) { + sets.Add( + Ast.Assign( + Ast.Property(tmp, pi), + ConvertToHelper(parameterBinder, value, pi.PropertyType) + ) + ); + } else { + // call a helper which throws the error but "returns object" + sets.Add( + Ast.Convert( + Ast.Call( + typeof(ScriptingRuntimeHelpers).GetMethod("ReadOnlyAssignError"), + Ast.Constant(false), + Ast.Constant(pi.Name) + ), + pi.PropertyType + ) + ); + } + break; + } + } + + sets.Add( + tmp + ); + + Expression newCall = Ast.Block( + sets.ToArray() + ); + + return _builder.ToExpression(parameterBinder, args, parameters, newCall); + } + + private static Expression ConvertToHelper(ParameterBinder parameterBinder, Expression value, Type type) { + if (type == value.Type) { + return value; + } + + if (type.IsAssignableFrom(value.Type)) { + return AstUtils.Convert(value, type); + } + + return parameterBinder.GetDynamicConversion(value, type); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/MethodBinder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/MethodBinder.cs new file mode 100644 index 0000000000..34cef93d24 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/MethodBinder.cs @@ -0,0 +1,974 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Dynamic.Binders; +using Microsoft.Contracts; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Generation; + +namespace Microsoft.Scripting.Actions.Calls { + /// + /// Provides binding and overload resolution to .NET methods. + /// + /// MethodBinder's can be used for: + /// generating new AST code for calling a method + /// calling a method via reflection at runtime + /// (not implemented) performing an abstract call + /// + /// MethodBinder's support default arguments, optional arguments, by-ref (in and out), and keyword arguments. + /// + /// Implementation Details: + /// + /// The MethodBinder works by building up a TargetSet for each number of effective arguments that can be + /// passed to a set of overloads. For example a set of overloads such as: + /// foo(object a, object b, object c) + /// foo(int a, int b) + /// + /// would have 2 target sets - one for 3 parameters and one for 2 parameters. For parameter arrays + /// we fallback and create the appropriately sized TargetSet on demand. + /// + /// Each TargetSet consists of a set of MethodCandidate's. Each MethodCandidate knows the flattened + /// parameters that could be received. For example for a function such as: + /// foo(params int[] args) + /// + /// When this method is in a TargetSet of size 3 the MethodCandidate takes 3 parameters - all of them + /// ints; if it's in a TargetSet of size 4 it takes 4 parameters. Effectively a MethodCandidate is + /// a simplified view that allows all arguments to be treated as required positional arguments. + /// + /// Each MethodCandidate in turn refers to a MethodTarget. The MethodTarget is composed of a set + /// of ArgBuilder's and a ReturnBuilder which know how to consume the positional arguments and pass + /// them to the appropriate argument of the destination method. This includes routing keyword + /// arguments to the correct position, providing the default values for optional arguments, etc... + /// + /// After binding is finished the MethodCandidates are thrown away and a BindingTarget is returned. + /// The BindingTarget indicates whether the binding was successful and if not any additional information + /// that should be reported to the user about the failed binding. It also exposes the MethodTarget which + /// allows consumers to get the flattened list of required parameters for the call. MethodCandidates + /// are not exposed and are an internal implementation detail of the MethodBinder. + /// + public sealed class MethodBinder { + private readonly string _name; // the name of the method (possibly language specific name which isn't the same as the method base) + private readonly Dictionary _targetSets; // the methods as they map from # of arguments -> the possible TargetSet's. + private readonly SymbolId[] _kwArgs; // the names of the keyword arguments being provided + private readonly NarrowingLevel _minLevel, _maxLevel; // specifies the minimum and maximum narrowing levels for conversions during binding + internal readonly DefaultBinder _binder; // the ActionBinder which is being used for conversions + private List _paramsCandidates; // the methods which are params methods which need special treatment because they don't have fixed # of args + + #region Constructors + + private MethodBinder(ActionBinder binder, string name, IList methods, SymbolId[] kwArgs, NarrowingLevel minLevel, NarrowingLevel maxLevel) { + ContractUtils.RequiresNotNull(binder, "binder"); + ContractUtils.RequiresNotNull(name, "name"); + ContractUtils.RequiresNotNullItems(methods, "methods"); + ContractUtils.RequiresNotNull(kwArgs, "kwArgs"); + + _binder = binder as DefaultBinder; + if (_binder == null) { + throw new InvalidOperationException("MethodBinder requires an instance of DefaultBinder"); + } + _name = name; + _kwArgs = kwArgs; + _targetSets = new Dictionary(methods.Count); + _minLevel = minLevel; + _maxLevel = maxLevel; + + foreach (MethodBase method in methods) { + if (IsUnsupported(method)) continue; + + AddBasicMethodTargets(binder, method); + } + + if (_paramsCandidates != null) { + // For all the methods that take a params array, create MethodCandidates that clash with the + // other overloads of the method + foreach (MethodCandidate maker in _paramsCandidates) { + foreach (int count in _targetSets.Keys) { + MethodCandidate target = maker.MakeParamsExtended(binder, count, _kwArgs); + if (target != null) AddTarget(target); + } + } + } + } + + #endregion + + #region Public APIs + + /// + /// Creates a new MethodBinder for binding to the specified methods that will attempt to bind + /// at all defined NarrowingLevels. + /// + /// The provided ActionBinder is used for determining overload resolution. + /// + public static MethodBinder MakeBinder(ActionBinder binder, string name, IList mis) { + return new MethodBinder(binder, name, mis, SymbolId.EmptySymbols, NarrowingLevel.None, NarrowingLevel.All); + } + + /// + /// Creates a new MethodBinder for binding to the specified methods on a call which includes keyword arguments that + /// will attempt to bind at all defined NarrowingLevels. + /// + /// The provided ActionBinder is used for determining overload resolution. + /// + public static MethodBinder MakeBinder(ActionBinder binder, string name, IList mis, SymbolId[] keywordArgs) { + return new MethodBinder(binder, name, mis, keywordArgs, NarrowingLevel.None, NarrowingLevel.All); + } + + /// + /// Creates a new MethodBinder for binding to the specified methods this will attempt to bind at + /// the specified NarrowingLevels. + /// + /// The provided ActionBinder is used for determining overload resolution. + /// + public static MethodBinder MakeBinder(ActionBinder binder, string name, IList mis, NarrowingLevel minLevel, NarrowingLevel maxLevel) { + return new MethodBinder(binder, name, mis, SymbolId.EmptySymbols, minLevel, maxLevel); + } + + /// + /// Creates a new MethodBinder for binding to the specified methods on a call which includes keyword arguments that + /// will attempt to bind at the specified NarrowingLevels. + /// + /// The provided ActionBinder is used for determining overload resolution. + /// + public static MethodBinder MakeBinder(ActionBinder binder, string name, IList mis, SymbolId[] keywordArgs, NarrowingLevel minLevel, NarrowingLevel maxLevel) { + return new MethodBinder(binder, name, mis, keywordArgs, minLevel, maxLevel); + } + + /// + /// Creates a BindingTarget given the specified CallType and parameter types. + /// + /// The BindingTarget can then be tested for the success or particular type of + /// failure that prevents the method from being called. The BindingTarget can + /// also be called reflectively at runtime, create an Expression for embedding in + /// a RuleBuilder, or be used for performing an abstract call. + /// + public BindingTarget MakeBindingTarget(CallTypes callType, Type[] types) { + ContractUtils.RequiresNotNull(types, "types"); + ContractUtils.RequiresNotNullItems(types, "types"); + + TargetSet ts = GetTargetSet(types.Length); + if (ts != null && !ts.IsParamsDictionaryOnly()) { + return ts.MakeBindingTarget(callType, types, _kwArgs, _minLevel, _maxLevel); + } + + // no target set is applicable, report an error and the expected # of arguments. + int[] expectedArgs = new int[_targetSets.Count + (_paramsCandidates != null ? 1 : 0)]; + int i = 0; + foreach (KeyValuePair kvp in _targetSets) { + int count = kvp.Key; + if (callType == CallTypes.ImplicitInstance) { + foreach (MethodCandidate cand in kvp.Value._targets) { + if (!CompilerHelpers.IsStatic(cand.Target.Method)) { + // dispatch includes an instance method, bump + // one parameter off. + count--; + } + } + } + expectedArgs[i++] = count; + } + if (_paramsCandidates != null) { + expectedArgs[expectedArgs.Length - 1] = Int32.MaxValue; + } + + return new BindingTarget(Name, callType == CallTypes.None ? types.Length : types.Length - 1, expectedArgs); + } + + /// + /// Creates a BindingTarget given the specified CallType and parameter types. + /// + /// The BindingTarget can then be tested for the success or particular type of + /// failure that prevents the method from being called. The BindingTarget can + /// also be called reflectively at runtime, create an Expression for embedding in + /// a RuleBuilder, or be used for performing an abstract call. + /// + public BindingTarget MakeBindingTarget(CallTypes callType, MetaObject[] metaObjects) { + ContractUtils.RequiresNotNull(metaObjects, "types"); + ContractUtils.RequiresNotNullItems(metaObjects, "types"); + + TargetSet ts = GetTargetSet(metaObjects.Length); + if (ts != null && !ts.IsParamsDictionaryOnly()) { + return ts.MakeBindingTarget(callType, metaObjects, _kwArgs, _minLevel, _maxLevel); + } + + // no target set is applicable, report an error and the expected # of arguments. + int[] expectedArgs = new int[_targetSets.Count + (_paramsCandidates != null ? 1 : 0)]; + int i = 0; + foreach (KeyValuePair kvp in _targetSets) { + int count = kvp.Key; + if (callType == CallTypes.ImplicitInstance) { + foreach (MethodCandidate cand in kvp.Value._targets) { + if (!CompilerHelpers.IsStatic(cand.Target.Method)) { + // dispatch includes an instance method, bump + // one parameter off. + count--; + } + } + } + expectedArgs[i++] = count; + } + if (_paramsCandidates != null) { + expectedArgs[expectedArgs.Length - 1] = Int32.MaxValue; + } + + return new BindingTarget(Name, callType == CallTypes.None ? metaObjects.Length : metaObjects.Length - 1, expectedArgs); + } + + /// + /// Gets the name of the MethodBinder as provided at construction time. + /// + /// The name may differ from the name of the underlying method bases if the + /// language provides some mapping from .NET method names to language specific + /// method names. It is flowed through the MethodBinder primarily for error + /// reporting purposes. + /// + public string Name { + get { + return _name; + } + } + + [Confined] + public override string ToString() { + string res = ""; + foreach (TargetSet ts in _targetSets.Values) { + res += ts + Environment.NewLine; + } + return res; + } + + #endregion + + #region TargetSet construction + + private TargetSet GetTargetSet(int nargs) { + TargetSet ts; + + // see if we've precomputed the TargetSet... + if (_targetSets.TryGetValue(nargs, out ts)) { + return ts; + } else if (_paramsCandidates != null) { + // build a new target set specific to the number of + // arguments we have + ts = BuildTargetSet(nargs); + if (ts._targets.Count > 0) { + return ts; + } + } + + return null; + } + + private TargetSet BuildTargetSet(int count) { + TargetSet ts = new TargetSet(this, count); + if (_paramsCandidates != null) { + foreach (MethodCandidate maker in _paramsCandidates) { + MethodCandidate target = maker.MakeParamsExtended(_binder, count, _kwArgs); + if (target != null) ts.Add(target); + } + } + + return ts; + } + + private void AddTarget(MethodCandidate target) { + int count = target.Target.ParameterCount; + TargetSet set; + if (!_targetSets.TryGetValue(count, out set)) { + set = new TargetSet(this, count); + _targetSets[count] = set; + } + set.Add(target); + } + + private void AddSimpleTarget(MethodCandidate target) { + AddTarget(target); + if (BinderHelpers.IsParamsMethod(target.Target.Method)) { + if (_paramsCandidates == null) _paramsCandidates = new List(); + _paramsCandidates.Add(target); + } + } + + private static ArgBuilder MakeInstanceBuilder(ActionBinder binder, MethodBase method, List parameters, ref int argIndex) { + if (!CompilerHelpers.IsStatic(method)) { + parameters.Add(new ParameterWrapper(binder, method.DeclaringType, SymbolId.Empty, true)); + return new SimpleArgBuilder(method.DeclaringType, argIndex++, false, false); + } else { + return new NullArgBuilder(); + } + } + + private void AddBasicMethodTargets(ActionBinder binder, MethodBase method) { + Assert.NotNull(binder, method); + + var parameterInfos = method.GetParameters(); + var parameters = new List(); + var arguments = new List(parameterInfos.Length); + var defaultArguments = new List(); + int argIndex = 0; + var instanceBuilder = MakeInstanceBuilder(binder, method, parameters, ref argIndex); + + bool hasByRefOrOut = false; + bool hasDefaults = false; + + var infoIndex = binder.PrepareParametersBinding(parameterInfos, arguments, parameters, ref argIndex); + for (; infoIndex < parameterInfos.Length; infoIndex++) { + var pi = parameterInfos[infoIndex]; + + if (binder.BindSpecialParameter(pi, arguments, parameters, ref argIndex)) { + continue; + } + + int indexForArgBuilder, kwIndex = GetKeywordIndex(pi); + if (kwIndex == ParameterNotPassedByKeyword) { + // positional argument, we simply consume the next argument + indexForArgBuilder = argIndex++; + } else { + // keyword argument, we just tell the simple arg builder to consume arg 0. + // KeywordArgBuilder will then pass in the correct single argument based + // upon the actual argument number provided by the user. + indexForArgBuilder = 0; + } + + // if the parameter is default we need to build a default arg builder and then + // build a reduced method at the end. + if (!CompilerHelpers.IsMandatoryParameter(pi)) { + // We need to build the default builder even if we have a parameter for it already to + // get good consistency of our error messages. But consider a method like + // def foo(a=1, b=2) and the user calls it as foo(b=3). Then adding the default + // value breaks an otherwise valid call. This is because we only generate MethodCandidates + // filling in the defaults from right to left (so the method - 1 arg requires a, + // and the method minus 2 args requires b). So we only add the default if it's + // a positional arg or we don't already have a default value. + if (kwIndex == -1 || !hasDefaults) { + defaultArguments.Add(new DefaultArgBuilder(pi)); + hasDefaults = true; + } else { + defaultArguments.Add(null); + } + } else if (defaultArguments.Count > 0) { + // non-contigious default parameter + defaultArguments.Add(null); + } + + ArgBuilder ab; + if (pi.ParameterType.IsByRef) { + hasByRefOrOut = true; + Type refType = typeof(StrongBox<>).MakeGenericType(pi.ParameterType.GetElementType()); + var param = new ParameterWrapper(_binder, pi, refType, SymbolTable.StringToId(pi.Name), true, false, false); + parameters.Add(param); + ab = new ReferenceArgBuilder(pi, refType, indexForArgBuilder); + } else { + hasByRefOrOut |= CompilerHelpers.IsOutParameter(pi); + var param = new ParameterWrapper(_binder, pi); + parameters.Add(param); + ab = new SimpleArgBuilder(pi, indexForArgBuilder); + } + + if (kwIndex == ParameterNotPassedByKeyword) { + arguments.Add(ab); + } else { + Debug.Assert(KeywordArgBuilder.BuilderExpectsSingleParameter(ab)); + arguments.Add(new KeywordArgBuilder(ab, _kwArgs.Length, kwIndex)); + } + } + + ReturnBuilder returnBuilder = MakeKeywordReturnBuilder( + new ReturnBuilder(CompilerHelpers.GetReturnType(method)), + parameterInfos, + parameters, + _binder.AllowKeywordArgumentSetting(method)); + + if (hasDefaults) { + for (int defaultsUsed = 1; defaultsUsed < defaultArguments.Count + 1; defaultsUsed++) { + // if the left most default we'll use is not present then don't add a default. This happens in cases such as: + // a(a=1, b=2, c=3) and then call with a(a=5, c=3). We'll come through once for c (no default, skip), + // once for b (default present, emit) and then a (no default, skip again). W/o skipping we'd generate the same + // method multiple times. This also happens w/ non-contigious default values, e.g. foo(a, b=3, c) where we don't want + // to generate a default candidate for just c which matches the normal method. + if (defaultArguments[defaultArguments.Count - defaultsUsed] != null) { + AddSimpleTarget(MakeDefaultCandidate( + method, + parameters, + instanceBuilder, + arguments, + defaultArguments, + returnBuilder, + defaultsUsed)); + } + } + } + + if (hasByRefOrOut) { + AddSimpleTarget(MakeByRefReducedMethodTarget(binder, parameterInfos, method)); + } + + AddSimpleTarget(MakeMethodCandidate(method, parameters, instanceBuilder, arguments, returnBuilder)); + } + + private MethodCandidate MakeDefaultCandidate(MethodBase method, List parameters, ArgBuilder instanceBuilder, List argBuilders, List defaultBuilders, ReturnBuilder returnBuilder, int defaultsUsed) { + List defaultArgBuilders = new List(argBuilders); + List necessaryParams = parameters.GetRange(0, parameters.Count - defaultsUsed); + + for (int curDefault = 0; curDefault < defaultsUsed; curDefault++) { + int readIndex = defaultBuilders.Count - defaultsUsed + curDefault; + int writeIndex = defaultArgBuilders.Count - defaultsUsed + curDefault; + + if (defaultBuilders[readIndex] != null) { + defaultArgBuilders[writeIndex] = defaultBuilders[readIndex]; + } else { + necessaryParams.Add(parameters[parameters.Count - defaultsUsed + curDefault]); + } + } + + // shift any arguments forward that need to be... + int curArg = CompilerHelpers.IsStatic(method) ? 0 : 1; + for (int i = 0; i < defaultArgBuilders.Count; i++) { + SimpleArgBuilder sab = defaultArgBuilders[i] as SimpleArgBuilder; + if (sab != null) { + defaultArgBuilders[i] = sab.MakeCopy(curArg++); + } + } + + return MakeMethodCandidate(method, necessaryParams, instanceBuilder, defaultArgBuilders, returnBuilder); + } + + private MethodCandidate MakeByRefReducedMethodTarget(ActionBinder binder, ParameterInfo[] parameterInfos, MethodBase method) { + Assert.NotNull(binder, parameterInfos, method); + + var parameters = new List(); + var arguments = new List(); + int argIndex = 0; + var instanceBuilder = MakeInstanceBuilder(binder, method, parameters, ref argIndex); + + List returnArgs = new List(); + if (CompilerHelpers.GetReturnType(method) != typeof(void)) { + returnArgs.Add(-1); + } + + var infoIndex = binder.PrepareParametersBinding(parameterInfos, arguments, parameters, ref argIndex); + for (; infoIndex < parameterInfos.Length; infoIndex++) { + var pi = parameterInfos[infoIndex]; + + if (binder.BindSpecialParameter(pi, arguments, parameters, ref argIndex)) { + continue; + } + + // See KeywordArgBuilder.BuilderExpectsSingleParameter + int indexForArgBuilder = 0; + + int kwIndex = ParameterNotPassedByKeyword; + if (!CompilerHelpers.IsOutParameter(pi)) { + kwIndex = GetKeywordIndex(pi); + if (kwIndex == ParameterNotPassedByKeyword) { + indexForArgBuilder = argIndex++; + } + } + + ArgBuilder ab; + if (CompilerHelpers.IsOutParameter(pi)) { + returnArgs.Add(arguments.Count); + ab = new OutArgBuilder(pi); + } else if (pi.ParameterType.IsByRef) { + // if the parameter is marked as [In] it is not returned. + if ((pi.Attributes & (ParameterAttributes.In | ParameterAttributes.Out)) != ParameterAttributes.In) { + returnArgs.Add(arguments.Count); + } + ParameterWrapper param = new ParameterWrapper(_binder, pi, pi.ParameterType.GetElementType(), SymbolTable.StringToId(pi.Name), false, false, false); + parameters.Add(param); + ab = new ReturnReferenceArgBuilder(pi, indexForArgBuilder); + } else { + ParameterWrapper param = new ParameterWrapper(_binder, pi); + parameters.Add(param); + ab = new SimpleArgBuilder(pi, indexForArgBuilder); + } + + if (kwIndex == ParameterNotPassedByKeyword) { + arguments.Add(ab); + } else { + Debug.Assert(KeywordArgBuilder.BuilderExpectsSingleParameter(ab)); + arguments.Add(new KeywordArgBuilder(ab, _kwArgs.Length, kwIndex)); + } + } + + ReturnBuilder returnBuilder = MakeKeywordReturnBuilder( + new ByRefReturnBuilder(returnArgs), + parameterInfos, + parameters, + _binder.AllowKeywordArgumentSetting(method)); + + return MakeMethodCandidate(method, parameters, instanceBuilder, arguments, returnBuilder); + } + + private MethodCandidate MakeMethodCandidate(MethodBase method, List parameters, ArgBuilder instanceBuilder, List argBuilders, ReturnBuilder returnBuilder) { + return new MethodCandidate( + new MethodTarget(this, method, parameters.Count, instanceBuilder, argBuilders, returnBuilder), + parameters); + } + + private void GetMinAndMaxArgs(out int minArgs, out int maxArgs) { + List argCounts = new List(_targetSets.Keys); + argCounts.Sort(); + minArgs = argCounts[0]; + maxArgs = argCounts[argCounts.Count - 1]; + } + + private static bool IsUnsupported(MethodBase method) { + return (method.CallingConvention & CallingConventions.VarArgs) != 0 || method.ContainsGenericParameters; + } + + #endregion + + #region Keyword arg binding support + + private ReturnBuilder MakeKeywordReturnBuilder(ReturnBuilder returnBuilder, ParameterInfo[] methodParams, List parameters, bool isConstructor) { + if (isConstructor) { + List unusedNames = GetUnusedKeywordParameters(methodParams); + List bindableMembers = GetBindableMembers(returnBuilder, unusedNames); + List kwArgIndexs = new List(); + if (unusedNames.Count == bindableMembers.Count) { + + foreach (MemberInfo mi in bindableMembers) { + ParameterWrapper pw = new ParameterWrapper( + _binder, + mi.MemberType == MemberTypes.Property ? + ((PropertyInfo)mi).PropertyType : + ((FieldInfo)mi).FieldType, + SymbolTable.StringToId(mi.Name), + false); + + parameters.Add(pw); + kwArgIndexs.Add(GetKeywordIndex(mi.Name)); + } + + KeywordConstructorReturnBuilder kwBuilder = new KeywordConstructorReturnBuilder(returnBuilder, + _kwArgs.Length, + kwArgIndexs.ToArray(), + bindableMembers.ToArray(), + _binder.PrivateBinding); + + return kwBuilder; + } + + } + return returnBuilder; + } + + private static List GetBindableMembers(ReturnBuilder returnBuilder, List unusedNames) { + List bindableMembers = new List(); + + foreach (SymbolId si in unusedNames) { + string strName = SymbolTable.IdToString(si); + + Type curType = returnBuilder.ReturnType; + MemberInfo[] mis = curType.GetMember(strName); + while (mis.Length != 1 && curType != null) { + // see if we have a single member defined as the closest level + mis = curType.GetMember(strName, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.SetField | BindingFlags.SetProperty | BindingFlags.Instance); + + if (mis.Length > 1) { + break; + } + + curType = curType.BaseType; + } + + if (mis.Length == 1) { + switch (mis[0].MemberType) { + case MemberTypes.Property: + case MemberTypes.Field: + bindableMembers.Add(mis[0]); + break; + } + } + } + return bindableMembers; + } + + private List GetUnusedKeywordParameters(ParameterInfo[] methodParams) { + List unusedNames = new List(); + foreach (SymbolId si in _kwArgs) { + string strName = SymbolTable.IdToString(si); + bool found = false; + foreach (ParameterInfo pi in methodParams) { + if (pi.Name == strName) { + found = true; + break; + } + } + if (!found) { + unusedNames.Add(si); + } + } + return unusedNames; + } + + private const int ParameterNotPassedByKeyword = -1; + + // Check if the given parameter from the candidate's signature matches a keyword argument from the callsite. + // Return ParameterNotPassedByKeyword if no match. Else returns index into callsite's keyword arglist if there is a match. + private int GetKeywordIndex(ParameterInfo pi) { + return GetKeywordIndex(pi.Name); + } + + private int GetKeywordIndex(string kwName) { + for (int i = 0; i < _kwArgs.Length; i++) { + if (kwName == SymbolTable.IdToString(_kwArgs[i])) { + return i; + } + } + return ParameterNotPassedByKeyword; + } + + #endregion + + #region TargetSet + + /// + /// Represents a collection of MethodCandidate's which all accept the + /// same number of logical parameters. For example a params method + /// and a method with 3 parameters would both be a TargetSet for 3 parameters. + /// + internal class TargetSet { + private MethodBinder _binder; + private int _count; + internal List _targets; + + internal TargetSet(MethodBinder binder, int count) { + _count = count; + _targets = new List(); + _binder = binder; + } + + internal bool IsParamsDictionaryOnly() { + foreach (MethodCandidate target in _targets) { + if (!target.HasParamsDictionary()) { + return false; + } + } + return true; + } + + internal BindingTarget MakeBindingTarget(CallTypes callType, Type[] types, SymbolId[] names, NarrowingLevel minLevel, NarrowingLevel maxLevel) { + List lastFail = new List(); + List failures = null; + + // go through all available narrowing levels selecting candidates. + for (NarrowingLevel level = minLevel; level <= maxLevel; level++) { + List applicableTargets = new List(); + if (failures != null) { + failures.Clear(); + } + + foreach (MethodCandidate target in _targets) { + // skip params dictionaries - we want to only pick up the methods normalized + // to have argument names (which we created because the MethodBinder gets + // created w/ keyword arguments). + if (!target.HasParamsDictionary()) { + Type[] normalizedTypes; + CallFailure callFailure; + + if (!target.TryGetNormalizedArguments(types, names, out normalizedTypes, out callFailure)) { + // dup keyword arguments or unassigned keyword argument + if (failures == null) failures = new List(1); + failures.Add(callFailure); + } else if (target.IsApplicable(normalizedTypes, level, lastFail)) { + // success, remember the candidate... + applicableTargets.Add(GetCandidate(target, level)); + } else { + // conversion failure, remember the failures... + if (failures == null) failures = new List(1); + + failures.Add(new CallFailure(target.Target, lastFail.ToArray())); + lastFail.Clear(); + } + } + } + + // see if we managed to get a single method or if one method is better... + List result; + if (TryGetApplicableTarget(callType, applicableTargets, types, out result)) { + if (result.Count == 1) { + // only a single method is callable, success! + return MakeSuccessfulBindingTarget(callType, types, result); + } + + // more than one method found, no clear best method, report an ambigious match + return MakeAmbiguousBindingTarget(callType, types, result); + } + } + + Debug.Assert(failures != null); + return new BindingTarget(_binder.Name, callType == CallTypes.None ? types.Length : types.Length - 1, failures.ToArray()); + } + + internal BindingTarget MakeBindingTarget(CallTypes callType, MetaObject[] metaObjects, SymbolId[] names, NarrowingLevel minLevel, NarrowingLevel maxLevel) { + List lastFail = new List(); + List failures = null; + + // go through all available narrowing levels selecting candidates. + for (NarrowingLevel level = minLevel; level <= maxLevel; level++) { + List applicableTargets = new List(); + if (failures != null) { + failures.Clear(); + } + + foreach (MethodCandidate target in _targets) { + // skip params dictionaries - we want to only pick up the methods normalized + // to have argument names (which we created because the MethodBinder gets + // created w/ keyword arguments). + if (!target.HasParamsDictionary()) { + MetaObject[] normalizedObjects; + CallFailure callFailure; + + if (!target.TryGetNormalizedArguments(metaObjects, names, out normalizedObjects, out callFailure)) { + // dup keyword arguments or unassigned keyword argument + if (failures == null) failures = new List(1); + failures.Add(callFailure); + } else if (target.IsApplicable(normalizedObjects, level, lastFail)) { + // success, remember the candidate... + applicableTargets.Add(GetCandidate(target, level)); + } else { + // conversion failure, remember the failures... + if (failures == null) failures = new List(1); + + failures.Add(new CallFailure(target.Target, lastFail.ToArray())); + lastFail.Clear(); + } + } + } + + // see if we managed to get a single method or if one method is better... + List result; + if (TryGetApplicableTarget(callType, applicableTargets, metaObjects, out result)) { + if (result.Count == 1) { + // only a single method is callable, success! + return MakeSuccessfulBindingTarget(callType, metaObjects, result); + } + + // more than one method found, no clear best method, report an ambigious match + return MakeAmbiguousBindingTarget(callType, metaObjects, result); + } + } + + Debug.Assert(failures != null); + return new BindingTarget(_binder.Name, callType == CallTypes.None ? metaObjects.Length : metaObjects.Length - 1, failures.ToArray()); + } + + private static bool TryGetApplicableTarget(CallTypes callType, List applicableTargets, Type[] actualTypes, out List result) { + result = null; + if (applicableTargets.Count == 1) { + result = applicableTargets; + return true; + } + if (applicableTargets.Count > 1) { + MethodCandidate target = FindBest(callType, applicableTargets, actualTypes); + if (target != null) { + result = new List(new MethodCandidate[] { target }); + return true; + } else { + result = applicableTargets; + return true; + } + } + return false; + } + + private static bool TryGetApplicableTarget(CallTypes callType, List applicableTargets, MetaObject[] actualTypes, out List result) { + result = null; + if (applicableTargets.Count == 1) { + result = applicableTargets; + return true; + } + if (applicableTargets.Count > 1) { + MethodCandidate target = FindBest(callType, applicableTargets, actualTypes); + if (target != null) { + result = new List(new MethodCandidate[] { target }); + return true; + } else { + result = applicableTargets; + return true; + } + } + return false; + } + + private Type[] GetTypesForTest(Type[] types, IList candidates) { + // if we have a single target we need no tests. + if (_targets.Count == 1) return null; + + Type[] tests = new Type[types.Length]; + for (int i = 0; i < types.Length; i++) { + if (AreArgumentTypesOverloaded(i, types.Length, candidates)) { + tests[i] = types[i]; + } + } + + return tests; + } + + private MetaObject[] GetRestrictedMetaObjects(MethodCandidate target, MetaObject[] objects, IList candidates) { + IList parameters = target.Parameters; + + Debug.Assert(parameters.Count == objects.Length); + + MetaObject[] resObjects = new MetaObject[objects.Length]; + for (int i = 0; i < objects.Length; i++) { + if (_targets.Count > 0 && AreArgumentTypesOverloaded(i, objects.Length, candidates)) { + resObjects[i] = objects[i].Restrict(objects[i].LimitType); + } else if (parameters[i].Type.IsAssignableFrom(objects[i].Expression.Type)) { + // we have a strong enough type already + resObjects[i] = objects[i]; + } else { + resObjects[i] = objects[i].Restrict(objects[i].LimitType); + } + } + + return resObjects; + } + + private static bool AreArgumentTypesOverloaded(int argIndex, int argCount, IList methods) { + Type argType = null; + for (int i = 0; i < methods.Count; i++) { + IList pis = methods[i].Parameters; + if (pis.Count == 0) continue; + + int readIndex = argIndex; + if (pis[0].Type == typeof(CodeContext)) { + readIndex++; + } + + Type curType; + if (readIndex < pis.Count) { + if (readIndex == -1) { + curType = methods[i].Target.Method.DeclaringType; + } else if (pis[readIndex].IsParamsArray) { + if (argIndex == argCount - (pis.Count - readIndex)) { + // We're the params array argument and a single value is being passed + // directly to it. The params array could be in the middle for + // a params setter. so pis.Count - readIndex is usually 1 for the + // params at the end, and therefore types.Length - 1 is usually if we're + // the last argument. We always have to check this type to disambiguate + // between passing an object which is compatible with the arg array and + // passing an object which goes into the arg array. Maybe we could do + // better sometimes. + return true; + } + curType = pis[pis.Count - 1].Type.GetElementType(); + } else { + curType = pis[readIndex].Type; + } + } else if (pis[pis.Count - 1].IsParamsArray) { + curType = pis[pis.Count - 1].Type.GetElementType(); + } else { + continue; + } + + if (argType == null) { + argType = curType; + } else if (argType != curType) { + return true; + } + } + return false; + } + + private static bool IsBest(MethodCandidate candidate, List applicableTargets, CallTypes callType, Type[] actualTypes) { + foreach (MethodCandidate target in applicableTargets) { + if (candidate == target) { + continue; + } + + if (MethodCandidate.GetPreferredCandidate(candidate, target, callType, actualTypes) != Candidate.One) { + return false; + } + } + return true; + } + + private static MethodCandidate FindBest(CallTypes callType, List applicableTargets, Type[] actualTypes) { + foreach (MethodCandidate candidate in applicableTargets) { + if (IsBest(candidate, applicableTargets, callType, actualTypes)) return candidate; + } + return null; + } + + private static bool IsBest(MethodCandidate candidate, List applicableTargets, CallTypes callType, MetaObject[] actualTypes) { + foreach (MethodCandidate target in applicableTargets) { + if (candidate == target) { + continue; + } + + if (MethodCandidate.GetPreferredCandidate(candidate, target, callType, actualTypes) != Candidate.One) { + return false; + } + } + return true; + } + + private static MethodCandidate FindBest(CallTypes callType, List applicableTargets, MetaObject[] actualTypes) { + foreach (MethodCandidate candidate in applicableTargets) { + if (IsBest(candidate, applicableTargets, callType, actualTypes)) return candidate; + } + return null; + } + + internal void Add(MethodCandidate target) { + Debug.Assert(target.Parameters.Count == _count); + + _targets.Add(target); + } + + private static MethodCandidate GetCandidate(MethodCandidate target, NarrowingLevel level) { + if (level == NarrowingLevel.None) return target; + + return new MethodCandidate(target, level); + } + + private BindingTarget MakeSuccessfulBindingTarget(CallTypes callType, Type[] types, List result) { + MethodCandidate resTarget = result[0]; + return new BindingTarget(_binder.Name, callType == CallTypes.None ? types.Length : types.Length - 1, resTarget.Target, resTarget.NarrowingLevel, GetTypesForTest(types, _targets)); + } + + private BindingTarget MakeSuccessfulBindingTarget(CallTypes callType, MetaObject[] objects, List result) { + MethodCandidate resTarget = result[0]; + return new BindingTarget(_binder.Name, callType == CallTypes.None ? objects.Length : objects.Length - 1, resTarget.Target, resTarget.NarrowingLevel, GetRestrictedMetaObjects(resTarget, objects, _targets)); + } + + private BindingTarget MakeAmbiguousBindingTarget(CallTypes callType, T[] types, List result) { + MethodTarget[] methods = new MethodTarget[result.Count]; + for (int i = 0; i < result.Count; i++) { + methods[i] = result[i].Target; + } + + return new BindingTarget(_binder.Name, callType == CallTypes.None ? types.Length : types.Length - 1, methods); + } + + [Confined] + public override string ToString() { + return string.Format("TargetSet({0} on {1}, nargs={2})", _targets[0].Target.Method.Name, _targets[0].Target.Method.DeclaringType.FullName, _count); + } + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/MethodCandidate.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/MethodCandidate.cs new file mode 100644 index 0000000000..41f164d938 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/MethodCandidate.cs @@ -0,0 +1,293 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic.Binders; +using System.Text; +using Microsoft.Contracts; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Generation; + +namespace Microsoft.Scripting.Actions.Calls { + /// + /// MethodCandidate represents the different possible ways of calling a method or a set of method overloads. + /// A single method can result in multiple MethodCandidates. Some reasons include: + /// - Every optional parameter or parameter with a default value will result in a candidate + /// - The presence of ref and out parameters will add a candidate for languages which want to return the updated values as return values. + /// - ArgumentKind.List and ArgumentKind.Dictionary can result in a new candidate per invocation since the list might be different every time. + /// + /// Each MethodCandidate represents the parameter type for the candidate using ParameterWrapper. + /// + /// Contrast this with MethodTarget which represents the real physical invocation of a method + /// + class MethodCandidate { + private MethodTarget _target; + private List _parameters; + private NarrowingLevel _narrowingLevel; + + internal MethodCandidate(MethodCandidate previous, NarrowingLevel narrowingLevel) { + this._target = previous.Target; + this._parameters = previous._parameters; + _narrowingLevel = narrowingLevel; + } + + internal MethodCandidate(MethodTarget target, List parameters) { + Debug.Assert(target != null); + + _target = target; + _parameters = parameters; + _narrowingLevel = NarrowingLevel.None; + parameters.TrimExcess(); + } + + public MethodTarget Target { + get { return _target; } + } + + public NarrowingLevel NarrowingLevel { + get { + return _narrowingLevel; + } + } + + [Confined] + public override string ToString() { + return string.Format("MethodCandidate({0})", Target); + } + + internal bool IsApplicable(Type[] types, NarrowingLevel narrowingLevel, List conversionResults) { + // attempt to convert each parameter + bool res = true; + for (int i = 0; i < types.Length; i++) { + bool success = _parameters[i].HasConversionFrom(types[i], narrowingLevel); + + conversionResults.Add(new ConversionResult(types[i], _parameters[i].Type, i, !success)); + + res &= success; + } + + return res; + } + + internal bool IsApplicable(MetaObject[] objects, NarrowingLevel narrowingLevel, List conversionResults) { + // attempt to convert each parameter + bool res = true; + for (int i = 0; i < objects.Length; i++) { + /*if (objects[i].NeedsDeferral) { + conversionResults.Add(new ConversionResult(typeof(Dynamic), _parameters[i].Type, i, false)); + } else*/ { + bool success = _parameters[i].HasConversionFrom(objects[i].LimitType, narrowingLevel); + + conversionResults.Add(new ConversionResult(objects[i].LimitType, _parameters[i].Type, i, !success)); + + res &= success; + } + } + + return res; + } + + internal static Candidate GetPreferredCandidate(MethodCandidate one, MethodCandidate two, CallTypes callType, Type[] actualTypes) { + Candidate cmpParams = ParameterWrapper.GetPreferredParameters(one.Parameters, two.Parameters, actualTypes); + if (cmpParams.Chosen()) { + return cmpParams; + } + + Candidate ret = MethodTarget.CompareEquivalentParameters(one.Target, two.Target); + if (ret.Chosen()) { + return ret; + } + + if (CompilerHelpers.IsStatic(one.Target.Method) && !CompilerHelpers.IsStatic(two.Target.Method)) { + return callType == CallTypes.ImplicitInstance ? Candidate.Two : Candidate.One; + } else if (!CompilerHelpers.IsStatic(one.Target.Method) && CompilerHelpers.IsStatic(two.Target.Method)) { + return callType == CallTypes.ImplicitInstance ? Candidate.One : Candidate.Two; + } + + return Candidate.Equivalent; + } + + internal static Candidate GetPreferredCandidate(MethodCandidate one, MethodCandidate two, CallTypes callType, MetaObject[] actualTypes) { + Candidate cmpParams = ParameterWrapper.GetPreferredParameters(one.Parameters, two.Parameters, actualTypes); + if (cmpParams.Chosen()) { + return cmpParams; + } + + Candidate ret = MethodTarget.CompareEquivalentParameters(one.Target, two.Target); + if (ret.Chosen()) { + return ret; + } + + if (CompilerHelpers.IsStatic(one.Target.Method) && !CompilerHelpers.IsStatic(two.Target.Method)) { + return callType == CallTypes.ImplicitInstance ? Candidate.Two : Candidate.One; + } else if (!CompilerHelpers.IsStatic(one.Target.Method) && CompilerHelpers.IsStatic(two.Target.Method)) { + return callType == CallTypes.ImplicitInstance ? Candidate.One : Candidate.Two; + } + + return Candidate.Equivalent; + } + + + /// + /// Builds a new MethodCandidate which takes count arguments and the provided list of keyword arguments. + /// + /// The basic idea here is to figure out which parameters map to params or a dictionary params and + /// fill in those spots w/ extra ParameterWrapper's. + /// + internal MethodCandidate MakeParamsExtended(ActionBinder binder, int count, SymbolId[] names) { + Debug.Assert(BinderHelpers.IsParamsMethod(_target.Method)); + + List newParameters = new List(count); + // if we don't have a param array we'll have a param dict which is type object + Type elementType = null; + int index = -1, kwIndex = -1; + + // keep track of which kw args map to a real argument, and which ones + // map to the params dictionary. + List unusedNames = new List(names); + List unusedNameIndexes = new List(); + for (int i = 0; i < unusedNames.Count; i++) { + unusedNameIndexes.Add(i); + } + + for (int i = 0; i < _parameters.Count; i++) { + ParameterWrapper pw = _parameters[i]; + + if (_parameters[i].IsParamsDict) { + kwIndex = i; + } else if (_parameters[i].IsParamsArray) { + elementType = pw.Type.GetElementType(); + index = i; + } else { + for (int j = 0; j < unusedNames.Count; j++) { + if (unusedNames[j] == _parameters[i].Name) { + unusedNames.RemoveAt(j); + unusedNameIndexes.RemoveAt(j); + break; + } + } + newParameters.Add(pw); + } + } + + if (index != -1) { + while (newParameters.Count < (count - unusedNames.Count)) { + ParameterWrapper param = new ParameterWrapper(binder, elementType, SymbolId.Empty, false); + newParameters.Insert(System.Math.Min(index, newParameters.Count), param); + } + } + + if (kwIndex != -1) { + foreach (SymbolId si in unusedNames) { + newParameters.Add(new ParameterWrapper(binder, typeof(object), si, false)); + } + } else if (unusedNames.Count != 0) { + // unbound kw args and no where to put them, can't call... + // TODO: We could do better here because this results in an incorrect arg # error message. + return null; + } + + // if we have too many or too few args we also can't call + if (count != newParameters.Count) { + return null; + } + + return new MethodCandidate(_target.MakeParamsExtended(count, unusedNames.ToArray(), unusedNameIndexes.ToArray()), newParameters); + } + + internal string ToSignatureString(string name, CallTypes callType) { + StringBuilder buf = new StringBuilder(name); + buf.Append("("); + bool isFirstArg = true; + int i = 0; + if (callType == CallTypes.ImplicitInstance) i = 1; + for (; i < _parameters.Count; i++) { + if (isFirstArg) isFirstArg = false; + else buf.Append(", "); + buf.Append(_parameters[i].ToSignatureString()); + } + buf.Append(")"); + return buf.ToString(); //@todo add helper info for more interesting signatures + } + + internal IList Parameters { + get { + return _parameters; + } + } + + internal bool HasParamsDictionary() { + foreach (ParameterWrapper pw in _parameters) { + // can't bind to methods that are params dictionaries, only to their extended forms. + if (pw.IsParamsDict) return true; + } + return false; + } + + internal bool TryGetNormalizedArguments(T[] argTypes, SymbolId[] names, out T[] args, out CallFailure failure) { + if (names.Length == 0) { + // no named arguments, success! + args = argTypes; + failure = null; + return true; + } + + T[] res = new T[argTypes.Length]; + Array.Copy(argTypes, res, argTypes.Length - names.Length); + List unboundNames = null; + List duppedNames = null; + + for (int i = 0; i < names.Length; i++) { + bool found = false; + for (int j = 0; j < _parameters.Count; j++) { + if (_parameters[j].Name == names[i]) { + found = true; + + if (res[j] != null) { + if (duppedNames == null) duppedNames = new List(); + duppedNames.Add(names[i]); + } else { + res[j] = argTypes[i + argTypes.Length - names.Length]; + } + break; + } + } + + if (!found) { + if (unboundNames == null) unboundNames = new List(); + unboundNames.Add(names[i]); + } + } + + if (unboundNames != null) { + failure = new CallFailure(Target, unboundNames.ToArray(), true); + args = null; + return false; + } else if (duppedNames != null) { + failure = new CallFailure(Target, duppedNames.ToArray(), false); + args = null; + return false; + } + + failure = null; + args = res; + return true; + } + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/MethodTarget.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/MethodTarget.cs new file mode 100644 index 0000000000..45a0551517 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/MethodTarget.cs @@ -0,0 +1,395 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using Microsoft.Contracts; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Generation; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions.Calls { + using Ast = System.Linq.Expressions.Expression; + + /// + /// MethodTarget represents how a method is bound to the arguments of the call-site + /// + /// Contrast this with MethodCandidate which represents the logical view of the invocation of a method + /// + public sealed class MethodTarget { + private MethodBinder _binder; + private readonly MethodBase _method; + private int _parameterCount; + private IList _argBuilders; + private ArgBuilder _instanceBuilder; + private ReturnBuilder _returnBuilder; + + internal MethodTarget(MethodBinder binder, MethodBase method, int parameterCount, ArgBuilder instanceBuilder, IList argBuilders, ReturnBuilder returnBuilder) { + this._binder = binder; + this._method = method; + this._parameterCount = parameterCount; + this._instanceBuilder = instanceBuilder; + this._argBuilders = argBuilders; + this._returnBuilder = returnBuilder; + + //argBuilders.TrimExcess(); + } + + public MethodBinder Binder { + get { return _binder; } + } + + public MethodBase Method { + get { return _method; } + } + + public int ParameterCount { + get { return _parameterCount; } + } + + public Type ReturnType { + get { + return _returnBuilder.ReturnType; + } + } + + public Type[] GetParameterTypes() { + List res = new List(_argBuilders.Count); + for (int i = 0; i < _argBuilders.Count; i++) { + Type t = _argBuilders[i].Type; + if (t != null) { + res.Add(t); + } + } + + return res.ToArray(); + } + + public string GetSignatureString(CallTypes callType) { + StringBuilder buf = new StringBuilder(); + Type[] types = GetParameterTypes(); + if (callType == CallTypes.ImplicitInstance) { + types = ArrayUtils.RemoveFirst(types); + } + + string comma = ""; + buf.Append("("); + foreach (Type t in types) { + buf.Append(comma); + buf.Append(t.Name); + comma = ", "; + } + buf.Append(")"); + return buf.ToString(); + } + + [Confined] + public override string ToString() { + return string.Format("MethodTarget({0} on {1})", Method, Method.DeclaringType.FullName); + } + + internal Expression MakeExpression(ParameterBinder parameterBinder, IList parameters) { + bool[] usageMarkers; + Expression[] spilledArgs; + Expression[] args = GetArgumentExpressions(parameterBinder, parameters, out usageMarkers, out spilledArgs); + + MethodBase mb = Method; + MethodInfo mi = mb as MethodInfo; + Expression ret, call; + if (!mb.IsPublic || (mb.DeclaringType != null && !mb.DeclaringType.IsVisible)) { + if (mi != null) { + mi = CompilerHelpers.GetCallableMethod(mi, _binder._binder.PrivateBinding); + if (mi != null) mb = mi; + } + } + + ConstructorInfo ci = mb as ConstructorInfo; + Debug.Assert(mi != null || ci != null); + if (mb.IsPublic && (mb.DeclaringType == null || mb.DeclaringType.IsVisible)) { + // public method + if (mi != null) { + Expression instance = mi.IsStatic ? null : _instanceBuilder.ToExpression(parameterBinder, parameters, usageMarkers); + call = AstUtils.SimpleCallHelper(instance, mi, args); + } else { + call = AstUtils.SimpleNewHelper(ci, args); + } + } else { + // Private binding, invoke via reflection + if (mi != null) { + Expression instance = mi.IsStatic ? Ast.Constant(null) : _instanceBuilder.ToExpression(parameterBinder, parameters, usageMarkers); + Debug.Assert(instance != null, "Can't skip instance expression"); + + call = Ast.Call( + typeof(BinderOps).GetMethod("InvokeMethod"), + Ast.Constant(mi), + AstUtils.Convert(instance, typeof(object)), + AstUtils.NewArrayHelper(typeof(object), args) + ); + } else { + call = Ast.Call( + typeof(BinderOps).GetMethod("InvokeConstructor"), + Ast.Constant(ci), + AstUtils.NewArrayHelper(typeof(object), args) + ); + } + } + + if (spilledArgs != null) { + call = Expression.Block(spilledArgs.AddLast(call)); + } + + ret = _returnBuilder.ToExpression(parameterBinder, _argBuilders, parameters, call); + + List updates = null; + for (int i = 0; i < _argBuilders.Count; i++) { + Expression next = _argBuilders[i].UpdateFromReturn(parameterBinder, parameters); + if (next != null) { + if (updates == null) { + updates = new List(); + } + updates.Add(next); + } + } + + if (updates != null) { + if (ret.Type != typeof(void)) { + ParameterExpression temp = Ast.Variable(ret.Type, "$ret"); + updates.Insert(0, Ast.Assign(temp, ret)); + updates.Add(temp); + ret = Ast.Block(new [] { temp }, updates.ToArray()); + } else { + updates.Insert(0, ret); + ret = Ast.Convert( + Ast.Block(updates.ToArray()), + typeof(void) + ); + } + } + + if (parameterBinder.Temps != null) { + ret = Ast.Block(parameterBinder.Temps, ret); + } + + return ret; + } + + private Expression[] GetArgumentExpressions(ParameterBinder parameterBinder, IList parameters, out bool[] usageMarkers, out Expression[] spilledArgs) { + int minPriority = Int32.MaxValue; + int maxPriority = Int32.MinValue; + foreach (ArgBuilder ab in _argBuilders) { + minPriority = System.Math.Min(minPriority, ab.Priority); + maxPriority = System.Math.Max(maxPriority, ab.Priority); + } + + var args = new Expression[_argBuilders.Count]; + Expression[] actualArgs = null; + usageMarkers = new bool[parameters.Count]; + for (int priority = minPriority; priority <= maxPriority; priority++) { + for (int i = 0; i < _argBuilders.Count; i++) { + if (_argBuilders[i].Priority == priority) { + args[i] = _argBuilders[i].ToExpression(parameterBinder, parameters, usageMarkers); + + // see if this has a temp that needs to be passed as the actual argument + Expression byref = _argBuilders[i].ByRefArgument; + if (byref != null) { + if (actualArgs == null) { + actualArgs = new Expression[_argBuilders.Count]; + } + actualArgs[i] = byref; + } + } + } + } + + if (actualArgs != null) { + for (int i = 0, n = args.Length; i < n; i++) { + if (args[i] != null && actualArgs[i] == null) { + actualArgs[i] = parameterBinder.GetTemporary(args[i].Type, null); + args[i] = Expression.Assign(actualArgs[i], args[i]); + } + } + + spilledArgs = RemoveNulls(args); + return RemoveNulls(actualArgs); + } + + spilledArgs = null; + return RemoveNulls(args); + } + + private static Expression[] RemoveNulls(Expression[] args) { + int newLength = args.Length; + for (int i = 0; i < args.Length; i++) { + if (args[i] == null) { + newLength--; + } + } + + var result = new Expression[newLength]; + for (int i = 0, j = 0; i < args.Length; i++) { + if (args[i] != null) { + result[j++] = args[i]; + } + } + return result; + } + + /// + /// Creates a call to this MethodTarget with the specified parameters. Casts are inserted to force + /// the types to the provided known types. + /// + /// TODO: Remove RuleBuilder and knownTypes once we're fully meta + /// + /// ParameterBinder used to map arguments to parameters. + /// The explicit arguments + /// If non-null, the type for each element in parameters + /// + internal Expression MakeExpression(ParameterBinder parameterBinder, IList parameters, IList knownTypes) { + Debug.Assert(knownTypes == null || parameters.Count == knownTypes.Count); + + IList args = parameters; + if (knownTypes != null) { + args = new Expression[parameters.Count]; + for (int i = 0; i < args.Count; i++) { + args[i] = parameters[i]; + if (knownTypes[i] != null && !knownTypes[i].IsAssignableFrom(parameters[i].Type)) { + args[i] = Ast.Convert(parameters[i], CompilerHelpers.GetVisibleType(knownTypes[i])); + } + } + } + + return MakeExpression(parameterBinder, args); + } + + private static int FindMaxPriority(IList abs, int ceiling) { + int max = 0; + foreach (ArgBuilder ab in abs) { + if (ab.Priority > ceiling) continue; + + max = System.Math.Max(max, ab.Priority); + } + return max; + } + + internal static Candidate CompareEquivalentParameters(MethodTarget one, MethodTarget two) { + // Prefer normal methods over explicit interface implementations + if (two.Method.IsPrivate && !one.Method.IsPrivate) return Candidate.One; + if (one.Method.IsPrivate && !two.Method.IsPrivate) return Candidate.Two; + + // Prefer non-generic methods over generic methods + if (one.Method.IsGenericMethod) { + if (!two.Method.IsGenericMethod) { + return Candidate.Two; + } else { + //!!! Need to support selecting least generic method here + return Candidate.Equivalent; + } + } else if (two.Method.IsGenericMethod) { + return Candidate.One; + } + + //prefer methods without out params over those with them + switch (Compare(one._returnBuilder.CountOutParams, two._returnBuilder.CountOutParams)) { + case 1: return Candidate.Two; + case -1: return Candidate.One; + } + + //prefer methods using earlier conversions rules to later ones + for (int i = Int32.MaxValue; i >= 0; ) { + int maxPriorityThis = FindMaxPriority(one._argBuilders, i); + int maxPriorityOther = FindMaxPriority(two._argBuilders, i); + + if (maxPriorityThis < maxPriorityOther) return Candidate.One; + if (maxPriorityOther < maxPriorityThis) return Candidate.Two; + + i = maxPriorityThis - 1; + } + + return Candidate.Equivalent; + } + + private static int Compare(int x, int y) { + if (x < y) return -1; + else if (x > y) return +1; + else return 0; + } + + internal MethodTarget MakeParamsExtended(int argCount, SymbolId[] names, int[] nameIndexes) { + Debug.Assert(BinderHelpers.IsParamsMethod(Method)); + + List newArgBuilders = new List(_argBuilders.Count); + + // current argument that we consume, initially skip this if we have it. + int curArg = CompilerHelpers.IsStatic(_method) ? 0 : 1; + int kwIndex = -1; + ArgBuilder paramsDictBuilder = null; + + foreach (ArgBuilder ab in _argBuilders) { + SimpleArgBuilder sab = ab as SimpleArgBuilder; + if (sab != null) { + // we consume one or more incoming argument(s) + if (sab.IsParamsArray) { + // consume all the extra arguments + int paramsUsed = argCount - + GetConsumedArguments() - + names.Length + + (CompilerHelpers.IsStatic(_method) ? 1 : 0); + + newArgBuilders.Add(new ParamsArgBuilder( + sab.ParameterInfo, + sab.Type.GetElementType(), + curArg, + paramsUsed + )); + + curArg += paramsUsed; + } else if (sab.IsParamsDict) { + // consume all the kw arguments + kwIndex = newArgBuilders.Count; + paramsDictBuilder = sab; + } else { + // consume the argument, adjust its position: + newArgBuilders.Add(sab.MakeCopy(curArg++)); + } + } else { + // CodeContext, null, default, etc... we don't consume an + // actual incoming argument. + newArgBuilders.Add(ab); + } + } + + if (kwIndex != -1) { + newArgBuilders.Insert(kwIndex, new ParamsDictArgBuilder(paramsDictBuilder.ParameterInfo, curArg, names, nameIndexes)); + } + + return new MethodTarget(_binder, Method, argCount, _instanceBuilder, newArgBuilders, _returnBuilder); + } + + private int GetConsumedArguments() { + int consuming = 0; + foreach (ArgBuilder argb in _argBuilders) { + SimpleArgBuilder sab = argb as SimpleArgBuilder; + if (sab != null && !sab.IsParamsDict) consuming++; + } + return consuming; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/NarrowingLevel.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/NarrowingLevel.cs new file mode 100644 index 0000000000..f601ce687b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/NarrowingLevel.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Actions.Calls { + /// + /// Narrowing conversions are conversions that cannot be proved to always succeed, conversions that are + /// known to possibly lose information, and conversions across domains of types sufficiently different + /// to merit narrowing notation like casts. + /// + /// Its upto every language to define the levels for conversions. The narrowling levels can be used by + /// for method overload resolution, where the overload is based on the parameter types (and not the number + /// of parameters). + /// + public enum NarrowingLevel { + /// + /// Conversions at this level do not do any narrowing. Typically, this will include + /// implicit numeric conversions, Type.IsAssignableFrom, StringBuilder to string, etc. + /// + None, + /// + /// Language defined prefered narrowing conversion. First level that introduces narrowing + /// conversions. + /// + One, + /// + /// Language defined preferred narrowing conversion. Second level that introduces narrowing + /// conversions and should have more conversions than One. + /// + Two, + /// + /// Language defined preferred narrowing conversion. Third level that introduces narrowing + /// conversions and should have more conversions that Two. + /// + Three, + /// + /// A somewhat meaningful conversion is possible, but it will quite likely be lossy. + /// For eg. BigInteger to an Int32, Boolean to Int32, one-char string to a char, + /// larger number type to a smaller numeric type (where there is no overflow), etc + /// + All + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/NullArgBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/NullArgBuilder.cs new file mode 100644 index 0000000000..71be58b1a4 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/NullArgBuilder.cs @@ -0,0 +1,39 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; + +namespace Microsoft.Scripting.Actions.Calls { + using Ast = System.Linq.Expressions.Expression; + + /// + /// ArgBuilder which always produces null. + /// + internal class NullArgBuilder : ArgBuilder { + public NullArgBuilder() + : base(null) { + } + + public override int Priority { + get { return 0; } + } + + internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { + return Ast.Constant(null); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/OutArgBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/OutArgBuilder.cs new file mode 100644 index 0000000000..3215e854aa --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/OutArgBuilder.cs @@ -0,0 +1,75 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Actions.Calls { + using Ast = System.Linq.Expressions.Expression; + + /// + /// Builds the argument for an out argument when not passed a StrongBox. The out parameter + /// is returned as an additional return value. + /// + internal sealed class OutArgBuilder : ArgBuilder { + private readonly Type _parameterType; + private readonly bool _isRef; + private ParameterExpression _tmp; + + public OutArgBuilder(ParameterInfo info) + : base(info) { + + _parameterType = info.ParameterType.IsByRef ? info.ParameterType.GetElementType() : info.ParameterType; + _isRef = info.ParameterType.IsByRef; + } + + public override int Priority { + get { return 5; } + } + + internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { + if (_isRef) { + if (_tmp == null) { + _tmp = parameterBinder.GetTemporary(_parameterType, "outParam"); + } + return _tmp; + } + + return GetDefaultValue(); + } + + internal override Expression ToReturnExpression(ParameterBinder parameterBinder) { + if (_isRef) { + return _tmp; + } + + return GetDefaultValue(); + } + + internal override Expression ByRefArgument { + get { return _isRef ? _tmp : null; } + } + + private Expression GetDefaultValue() { + if (_parameterType.IsValueType) { + // default(T) + return Ast.Constant(Activator.CreateInstance(_parameterType)); + } + return Ast.Constant(null); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParameterBinder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParameterBinder.cs new file mode 100644 index 0000000000..78259bee38 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParameterBinder.cs @@ -0,0 +1,68 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Actions; +using System.Reflection; + +namespace Microsoft.Scripting.Actions.Calls { + /// + /// Helper class for emitting calls via the MethodBinder. + /// + public class ParameterBinder { + private readonly ActionBinder _actionBinder; + private List _temps; + + public ParameterBinder(ActionBinder actionBinder) { + Assert.NotNull(actionBinder); + + _actionBinder = actionBinder; + } + + public ActionBinder Binder { + get { return _actionBinder; } + } + + internal List Temps { + get { return _temps; } + } + + internal ParameterExpression GetTemporary(Type type, string name) { + Assert.NotNull(type); + + if (_temps == null) { + _temps = new List(); + } + + ParameterExpression res = Expression.Variable(type, name); + _temps.Add(res); + return res; + } + + public virtual Expression ConvertExpression(Expression expr, ParameterInfo info, Type toType) { + Assert.NotNull(expr, toType); + + return _actionBinder.ConvertExpression(expr, toType, ConversionResultKind.ExplicitCast, null); + } + + public virtual Expression GetDynamicConversion(Expression value, Type type) { + return Expression.Dynamic(OldConvertToAction.Make(_actionBinder, type), type, value); + } + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParameterBinderWithCodeContext.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParameterBinderWithCodeContext.cs new file mode 100644 index 0000000000..9f5c8d4281 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParameterBinderWithCodeContext.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Actions; +using System.Linq.Expressions; +using System.Reflection; + +namespace Microsoft.Scripting.Actions.Calls { + + // TODO: this should move to Python + public class ParameterBinderWithCodeContext : ParameterBinder { + private readonly Expression _context; + + public Expression ContextExpression { + get { return _context; } + } + + public ParameterBinderWithCodeContext(ActionBinder actionBinder, Expression codeContext) + : base(actionBinder) { + Assert.NotNull(actionBinder, codeContext); + + _context = codeContext; + } + + public override Expression ConvertExpression(Expression expr, ParameterInfo info, Type toType) { + return Binder.ConvertExpression(expr, toType, ConversionResultKind.ExplicitCast, _context); + } + + public override Expression GetDynamicConversion(Expression value, Type type) { + return Expression.Dynamic(OldConvertToAction.Make(Binder, type), type, _context, value); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParameterWrapper.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParameterWrapper.cs new file mode 100644 index 0000000000..89ca29d1fd --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParameterWrapper.cs @@ -0,0 +1,259 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Dynamic; +using System.Dynamic.Binders; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Generation; + +namespace Microsoft.Scripting.Actions.Calls { + /// + /// ParameterWrapper represents the logical view of a parameter. For eg. the byref-reduced signature + /// of a method with byref parameters will be represented using a ParameterWrapper of the underlying + /// element type, since the logical view of the byref-reduced signature is that the argument will be + /// passed by value (and the updated value is included in the return value). + /// + /// Contrast this with ArgBuilder which represents the real physical argument passed to the method. + /// + public sealed class ParameterWrapper { + private readonly Type _type; + private readonly bool _prohibitNull, _isParams, _isParamsDict; + private readonly ActionBinder _binder; + private readonly SymbolId _name; + + // Type and other properties may differ from the values on the info; info could also be unspecified. + private readonly ParameterInfo _info; + + /// + /// ParameterInfo is not available. + /// + public ParameterWrapper(ActionBinder binder, Type type, SymbolId name, bool prohibitNull) + : this(binder, null, type, name, prohibitNull, false, false) { + } + + public ParameterWrapper(ActionBinder binder, ParameterInfo info, Type type, SymbolId name, bool prohibitNull, bool isParams, bool isParamsDict) { + ContractUtils.RequiresNotNull(binder, "binder"); + ContractUtils.RequiresNotNull(type, "type"); + + _type = type; + _prohibitNull = prohibitNull; + _binder = binder; + _info = info; + _name = name; + _isParams = isParams; + _isParamsDict = isParamsDict; + } + + public ParameterWrapper(ActionBinder binder, ParameterInfo info) + : this(binder, info, info.ParameterType, SymbolId.Empty, false, false, false) { + + _prohibitNull = CompilerHelpers.ProhibitsNull(info); + _isParams = CompilerHelpers.IsParamArray(info); + _isParamsDict = BinderHelpers.IsParamDictionary(info); + if (_isParams || _isParamsDict) { + // params arrays & dictionaries don't allow assignment by keyword + _name = SymbolTable.StringToId(""); + } else { + _name = SymbolTable.StringToId(info.Name ?? ""); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + public Type Type { + get { return _type; } + } + + public bool ProhibitNull { + get { return _prohibitNull; } + } + + public ParameterInfo ParameterInfo { + get { return _info; } + } + + public bool HasConversionFrom(Type fromType, NarrowingLevel level) { + return _binder.CanConvertFrom(fromType, this, level); + } + + // TODO: remove + internal static Candidate GetPreferredParameters(IList parameters1, IList parameters2, Type[] actualTypes) { + Debug.Assert(parameters1.Count == parameters2.Count); + Debug.Assert(parameters1.Count == actualTypes.Length); + + Candidate result = Candidate.Equivalent; + for (int i = 0; i < actualTypes.Length; i++) { + Candidate preferred = GetPreferredParameter(parameters1[i], parameters2[i], actualTypes[i]); + + switch (result) { + case Candidate.Equivalent: + result = preferred; + break; + + case Candidate.One: + if (preferred == Candidate.Two) return Candidate.Ambiguous; + break; + + case Candidate.Two: + if (preferred == Candidate.One) return Candidate.Ambiguous; + break; + + case Candidate.Ambiguous: + if (preferred != Candidate.Equivalent) { + result = preferred; + } + break; + + default: + throw new InvalidOperationException(); + } + } + + return result; + } + + public static Candidate GetPreferredParameters(IList parameters1, IList parameters2, MetaObject[] actualTypes) { + Debug.Assert(parameters1.Count == parameters2.Count); + Debug.Assert(parameters1.Count == actualTypes.Length); + + Candidate result = Candidate.Equivalent; + for (int i = 0; i < actualTypes.Length; i++) { + Candidate preferred = GetPreferredParameter(parameters1[i], parameters2[i], actualTypes[i]); + + switch (result) { + case Candidate.Equivalent: + result = preferred; + break; + + case Candidate.One: + if (preferred == Candidate.Two) return Candidate.Ambiguous; + break; + + case Candidate.Two: + if (preferred == Candidate.One) return Candidate.Ambiguous; + break; + + case Candidate.Ambiguous: + if (preferred != Candidate.Equivalent) { + result = preferred; + } + break; + + default: + throw new InvalidOperationException(); + } + } + + return result; + } + + private static Candidate GetPreferredParameter(ParameterWrapper candidateOne, ParameterWrapper candidateTwo) { + Assert.NotNull(candidateOne, candidateTwo); + + if (candidateOne._binder.ParametersEquivalent(candidateOne, candidateTwo)) { + return Candidate.Equivalent; + } + + Type t1 = candidateOne.Type; + Type t2 = candidateTwo.Type; + + if (candidateOne._binder.CanConvertFrom(t2, candidateOne, NarrowingLevel.None)) { + if (candidateOne._binder.CanConvertFrom(t1, candidateTwo, NarrowingLevel.None)) { + return Candidate.Ambiguous; + } else { + return Candidate.Two; + } + } + + if (candidateOne._binder.CanConvertFrom(t1, candidateTwo, NarrowingLevel.None)) { + return Candidate.One; + } + + // Special additional rules to order numeric value types + Candidate preferred = candidateOne._binder.PreferConvert(t1, t2); + if (preferred.Chosen()) { + return preferred; + } + + preferred = candidateOne._binder.PreferConvert(t2, t1).TheOther(); + if (preferred.Chosen()) { + return preferred; + } + + return Candidate.Ambiguous; + } + + private static Candidate GetPreferredParameter(ParameterWrapper candidateOne, ParameterWrapper candidateTwo, Type actualType) { + Assert.NotNull(candidateOne, candidateTwo, actualType); + + if (candidateOne._binder.ParametersEquivalent(candidateOne, candidateTwo)) { + return Candidate.Equivalent; + } + + for (NarrowingLevel curLevel = NarrowingLevel.None; curLevel <= NarrowingLevel.All; curLevel++) { + Candidate candidate = candidateOne._binder.SelectBestConversionFor(actualType, candidateOne, candidateTwo, curLevel); + if (candidate.Chosen()) { + return candidate; + } + } + + return GetPreferredParameter(candidateOne, candidateTwo); + } + + private static Candidate GetPreferredParameter(ParameterWrapper candidateOne, ParameterWrapper candidateTwo, MetaObject actualType) { + Assert.NotNull(candidateOne, candidateTwo, actualType); + + if (candidateOne._binder.ParametersEquivalent(candidateOne, candidateTwo)) { + return Candidate.Equivalent; + } + + for (NarrowingLevel curLevel = NarrowingLevel.None; curLevel <= NarrowingLevel.All; curLevel++) { + Candidate candidate = candidateOne._binder.SelectBestConversionFor(actualType.LimitType, candidateOne, candidateTwo, curLevel); + if (candidate.Chosen()) { + return candidate; + } + } + + return GetPreferredParameter(candidateOne, candidateTwo); + } + + public SymbolId Name { + get { + return _name; + } + } + + public bool IsParamsArray { + get { + return _isParams; + } + } + + public bool IsParamsDict { + get { + return _isParamsDict; + } + } + + public string ToSignatureString() { + return Type.Name; + } + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParamsArgBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParamsArgBuilder.cs new file mode 100644 index 0000000000..88bf2d3721 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParamsArgBuilder.cs @@ -0,0 +1,65 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using System.Reflection; + +namespace Microsoft.Scripting.Actions.Calls { + using Ast = System.Linq.Expressions.Expression; + + internal sealed class ParamsArgBuilder : ArgBuilder { + private readonly int _start; + private readonly int _count; + private readonly Type _elementType; + + public ParamsArgBuilder(ParameterInfo info, Type elementType, int start, int count) + : base(info) { + + ContractUtils.RequiresNotNull(elementType, "elementType"); + ContractUtils.Requires(start >= 0, "start"); + ContractUtils.Requires(count >= 0, "count"); + + _start = start; + _count = count; + _elementType = elementType; + } + + public override int Priority { + get { return 4; } + } + + internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { + List elems = new List(_count); + for (int i = _start; i < _start + _count; i++) { + if (!hasBeenUsed[i]) { + elems.Add(parameterBinder.ConvertExpression(parameters[i], ParameterInfo, _elementType)); + hasBeenUsed[i] = true; + } + } + + return Ast.NewArrayInit(_elementType, elems); + } + + + public override Type Type { + get { + return _elementType.MakeArrayType(); + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParamsDictArgBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParamsDictArgBuilder.cs new file mode 100644 index 0000000000..9b3959e22d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ParamsDictArgBuilder.cs @@ -0,0 +1,86 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions.Calls { + using Ast = System.Linq.Expressions.Expression; + using Microsoft.Scripting.Utils; + + /// + /// Builds the parameter for a params dictionary argument - this collects all the extra name/value + /// pairs provided to the function into a SymbolDictionary which is passed to the function. + /// + internal sealed class ParamsDictArgBuilder : ArgBuilder { + private readonly SymbolId[] _names; + private readonly int[] _nameIndexes; + private readonly int _argIndex; + + public ParamsDictArgBuilder(ParameterInfo info, int argIndex, SymbolId[] names, int[] nameIndexes) + : base(info) { + Assert.NotNull(names, nameIndexes); + + _argIndex = argIndex; + _names = names; + _nameIndexes = nameIndexes; + } + + public override int Priority { + get { return 3; } + } + + internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { + Expression res = Ast.Call( + typeof(BinderOps).GetMethod("MakeSymbolDictionary"), + Ast.NewArrayInit(typeof(SymbolId), ConstantNames()), + AstUtils.NewArrayHelper(typeof(object), GetParameters(parameters, hasBeenUsed)) + ); + + return res; + } + + public override Type Type { + get { + return typeof(IAttributesCollection); + } + } + + private List GetParameters(IList parameters, bool[] hasBeenUsed) { + List res = new List(_nameIndexes.Length); + for (int i = 0; i < _nameIndexes.Length; i++) { + int parameterIndex = _nameIndexes[i] + _argIndex; + if (!hasBeenUsed[parameterIndex]) { + res.Add(parameters[parameterIndex]); + hasBeenUsed[parameterIndex] = true; + } + } + return res; + } + + private Expression[] ConstantNames() { + Expression[] res = new Expression[_names.Length]; + for (int i = 0; i < _names.Length; i++) { + res[i] = AstUtils.Constant(_names[i]); + } + return res; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ReferenceArgBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ReferenceArgBuilder.cs new file mode 100644 index 0000000000..4648faa10d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ReferenceArgBuilder.cs @@ -0,0 +1,86 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using RuntimeHelpers = Microsoft.Scripting.Runtime.ScriptingRuntimeHelpers; + +namespace Microsoft.Scripting.Actions.Calls { + + /// + /// An argument that the user wants to explicitly pass by-reference (with copy-in copy-out semantics). + /// The user passes a StrongBox[T] object whose value will get updated when the call returns. + /// + internal sealed class ReferenceArgBuilder : SimpleArgBuilder { + private readonly Type _elementType; + private ParameterExpression _tmp; + + public ReferenceArgBuilder(ParameterInfo info, Type parameterType, int index) + : base(info, parameterType, index, false, false) { + Debug.Assert(parameterType.GetGenericTypeDefinition() == typeof(StrongBox<>)); + _elementType = parameterType.GetGenericArguments()[0]; + } + + protected override SimpleArgBuilder Copy(int newIndex) { + return new ReferenceArgBuilder(ParameterInfo, Type, newIndex); + } + + public override int Priority { + get { return 5; } + } + + internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { + if (_tmp == null) { + _tmp = parameterBinder.GetTemporary(_elementType, "outParam"); + } + + hasBeenUsed[Index] = true; + Type boxType = typeof(StrongBox<>).MakeGenericType(_elementType); + return Expression.Condition( + Expression.TypeIs(parameters[Index], Type), + Expression.Assign( + _tmp, + Expression.Field( + AstUtils.Convert(parameters[Index], boxType), + boxType.GetField("Value") + ) + ), + Expression.Call( + typeof(RuntimeHelpers).GetMethod("IncorrectBoxType").MakeGenericMethod(_elementType), + AstUtils.Convert(parameters[Index], typeof(object)) + ) + ); + } + + internal override Expression UpdateFromReturn(ParameterBinder parameterBinder, IList parameters) { + return Expression.Assign( + Expression.Field( + Expression.Convert(parameters[Index], Type), + Type.GetField("Value") + ), + _tmp + ); + } + + internal override Expression ByRefArgument { + get { return _tmp; } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ReturnBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ReturnBuilder.cs new file mode 100644 index 0000000000..06941525c9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ReturnBuilder.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Actions.Calls { + + class ReturnBuilder { + private readonly Type _returnType; + + /// + /// Creates a ReturnBuilder + /// + /// the type the ReturnBuilder will leave on the stack + public ReturnBuilder(Type returnType) { + Debug.Assert(returnType != null); + + this._returnType = returnType; + } + + internal virtual Expression ToExpression(ParameterBinder parameterBinder, IList args, IList parameters, Expression ret) { + return ret; + } + + public virtual int CountOutParams { + get { return 0; } + } + + public Type ReturnType { + get { + return _returnType; + } + } + + protected static object ConvertToObject(object ret) { + if (ret is bool) { + return ScriptingRuntimeHelpers.BooleanToObject((bool)ret); + } else if (ret is int) { + return ScriptingRuntimeHelpers.Int32ToObject((int)ret); + } + return ret; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ReturnReferenceArgBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ReturnReferenceArgBuilder.cs new file mode 100644 index 0000000000..7906b5dc2d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/ReturnReferenceArgBuilder.cs @@ -0,0 +1,61 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; + +namespace Microsoft.Scripting.Actions.Calls { + using Ast = System.Linq.Expressions.Expression; + + /// + /// Builds a parameter for a reference argument when a StrongBox has not been provided. The + /// updated return value is returned as one of the resulting return values. + /// + internal sealed class ReturnReferenceArgBuilder : SimpleArgBuilder { + private ParameterExpression _tmp; + + public ReturnReferenceArgBuilder(ParameterInfo info, int index) + : base(info, info.ParameterType.GetElementType(), index, false, false) { + } + + internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { + if (_tmp == null) { + _tmp = parameterBinder.GetTemporary(Type, "outParam"); + } + + return Ast.Block(Ast.Assign(_tmp, base.ToExpression(parameterBinder, parameters, hasBeenUsed)), _tmp); + } + + protected override SimpleArgBuilder Copy(int newIndex) { + return new ReturnReferenceArgBuilder(ParameterInfo, newIndex); + } + + internal override Expression ToReturnExpression(ParameterBinder parameterBinder) { + return _tmp; + } + + internal override Expression ByRefArgument { + get { return _tmp; } + } + + public override int Priority { + get { + return 5; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/SimpleArgBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/SimpleArgBuilder.cs new file mode 100644 index 0000000000..cc8387917c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/SimpleArgBuilder.cs @@ -0,0 +1,108 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Generation; + +namespace Microsoft.Scripting.Actions.Calls { + /// + /// SimpleArgBuilder produces the value produced by the user as the argument value. It + /// also tracks information about the original parameter and is used to create extended + /// methods for params arrays and param dictionary functions. + /// + public class SimpleArgBuilder : ArgBuilder { + private int _index; + private readonly Type _parameterType; + private readonly bool _isParams, _isParamsDict; + + /// + /// Parameter info is not available for this argument. + /// + public SimpleArgBuilder(Type parameterType, int index, bool isParams, bool isParamsDict) + : this(null, parameterType, index, isParams, isParamsDict) { + } + + /// + /// Type and whether the parameter is a params-array or params-dictionary is derived from info. + /// + public SimpleArgBuilder(ParameterInfo info, int index) + : this(info, info.ParameterType, index, CompilerHelpers.IsParamArray(info), BinderHelpers.IsParamDictionary(info)) { + } + + protected SimpleArgBuilder(ParameterInfo info, Type parameterType, int index, bool isParams, bool isParamsDict) + : base(info) { + ContractUtils.Requires(index >= 0); + ContractUtils.RequiresNotNull(parameterType, "parameterType"); + + _index = index; + _parameterType = parameterType; + _isParams = isParams; + _isParamsDict = isParamsDict; + } + + internal SimpleArgBuilder MakeCopy(int newIndex) { + var result = Copy(newIndex); + // Copy() must be overriden in derived classes and return an instance of the derived class: + Debug.Assert(result.GetType() == GetType()); + return result; + } + + protected virtual SimpleArgBuilder Copy(int newIndex) { + return new SimpleArgBuilder(ParameterInfo, _parameterType, newIndex, _isParams, _isParamsDict); + } + + public override int Priority { + get { return 0; } + } + + public bool IsParamsArray { + get { + return _isParams; + } + } + + public bool IsParamsDict { + get { + return _isParamsDict; + } + } + + internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { + Debug.Assert(_index < parameters.Count); + Debug.Assert(_index < hasBeenUsed.Length); + Debug.Assert(parameters[_index] != null); + hasBeenUsed[_index] = true; + return parameterBinder.ConvertExpression(parameters[_index], ParameterInfo, _parameterType); + } + + public int Index { + get { + return _index; + } + } + + public override Type Type { + get { + return _parameterType; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/SiteLocalStorageBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/SiteLocalStorageBuilder.cs new file mode 100644 index 0000000000..389e503386 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Calls/SiteLocalStorageBuilder.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using System.Reflection; + +namespace Microsoft.Scripting.Actions.Calls { + public sealed class SiteLocalStorageBuilder : ArgBuilder { + public override int Priority { + get { return -1; } + } + + public SiteLocalStorageBuilder(ParameterInfo info) + : base(info) { + } + + internal protected override Expression ToExpression(ParameterBinder parameterBinder, IList parameters, bool[] hasBeenUsed) { + return Expression.Constant(Activator.CreateInstance(ParameterInfo.ParameterType)); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ComboActionRewriter.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ComboActionRewriter.cs new file mode 100644 index 0000000000..29c15de7ba --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ComboActionRewriter.cs @@ -0,0 +1,205 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Dynamic.Binders; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + /// + /// A tree rewriter which will find dynamic sites which consume dynamic sites and + /// turn them into a single combo dynamic site. The combo dynamic site will then run the + /// individual meta binders and produce the resulting code in a single dynamic site. + /// + public class ComboActionRewriter : ExpressionVisitor { + /// + /// A reducible node which we use to generate the combo dynamic sites. Each time we encounter + /// a dynamic site we replace it with a ComboDynamicSiteExpression. When a child of a dynamic site + /// turns out to be a ComboDynamicSiteExpression we will then merge the child with the parent updating + /// the binding mapping info. If any of the inputs cause side effects then we'll stop the combination. + /// + class ComboDynamicSiteExpression : Expression { + private readonly Expression[] _inputs; + private readonly List _binders; + private readonly Type _type; + + public ComboDynamicSiteExpression(Type type, List binders, Expression[] inputs) { + + _binders = binders; + _inputs = inputs; + _type = type; + } + + public override bool CanReduce { + get { return true; } + } + + protected override Type GetExpressionType() { + return _type; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + public Expression[] Inputs { + get { + return _inputs; + } + } + + public List Binders { + get { + return _binders; + } + } + + public override Expression Reduce() { + // we just reduce to a simple DynamicExpression + return Expression.Dynamic( + new ComboBinder(_binders), + Type, + _inputs + ); + } + } + + protected override Expression VisitDynamic(DynamicExpression node) { + MetaObjectBinder metaBinder = node.Binder as MetaObjectBinder; + + if (metaBinder == null) { + // don't rewrite non meta-binder nodes, we can't compose them + return node; + } + + // gather the real arguments for the new dynamic site node + var args = node.Arguments; + bool foundSideEffectingArgs = false; + List inputs = new List(); + + // parameter mapping is 1 List for each meta binder, the inner list + // contains the mapping info for each particular binder + + List binders = new List(); + List myInfo = new List(); + + int actionCount = 0; + for (int i = 0; i < args.Count; i++) { + Expression e = args[i]; + + if (!foundSideEffectingArgs) { + // attempt to combine the arguments... + Expression rewritten = Visit(e); + + ComboDynamicSiteExpression combo = rewritten as ComboDynamicSiteExpression; + ConstantExpression ce; + if (combo != null) { + // an action expression we can combine with our own expression + + // remember how many actions we have so far - if any of our children consume + // actions their offset is bumped up + int baseActionCount = actionCount; + + foreach (BinderMappingInfo comboInfo in combo.Binders) { + List newInfo = new List(); + + foreach (ParameterMappingInfo info in comboInfo.MappingInfo) { + if (info.IsParameter) { + // all of the inputs from the child now become ours + newInfo.Add(ParameterMappingInfo.Parameter(inputs.Count)); + inputs.Add(combo.Inputs[info.ParameterIndex]); + } else if (info.IsAction) { + newInfo.Add(ParameterMappingInfo.Action(info.ActionIndex + baseActionCount)); + actionCount++; + } else { + Debug.Assert(info.Constant != null); + + // constants can just flow through + newInfo.Add(info); + } + } + + binders.Add(new BinderMappingInfo(comboInfo.Binder, newInfo)); + } + + myInfo.Add(ParameterMappingInfo.Action(actionCount++)); + } else if ((ce = rewritten as ConstantExpression) != null) { + // we can hoist the constant into the combo + myInfo.Add(ParameterMappingInfo.Fixed(ce)); + } else if (IsSideEffectFree(rewritten)) { + // we can treat this as an input parameter + myInfo.Add(ParameterMappingInfo.Parameter(inputs.Count)); + inputs.Add(rewritten); + } else { + // this argument is doing something we don't understand - we have to leave + // it as is (an input we consume) and all the remaining arguments need to be + // evaluated normally as this could have side effects on them. + foundSideEffectingArgs = true; + myInfo.Add(ParameterMappingInfo.Parameter(inputs.Count)); + inputs.Add(e); + } + } else { + // we've already seen an argument which may have side effects, don't do + // any more combinations. + myInfo.Add(ParameterMappingInfo.Parameter(inputs.Count)); + inputs.Add(e); + } + } + binders.Add(new BinderMappingInfo(metaBinder, myInfo)); + // TODO: Remove any duplicate inputs (e.g. locals being fed in multiple times) + return new ComboDynamicSiteExpression(node.Type, binders, inputs.ToArray()); + } + + private bool IsSideEffectFree(Expression rewritten) { + if (rewritten is ParameterExpression || + rewritten is GlobalVariableExpression) { + return true; + } + + if (rewritten.NodeType == ExpressionType.TypeIs) { + return IsSideEffectFree(((UnaryExpression)rewritten).Operand); + } + + BinaryExpression be = rewritten as BinaryExpression; + if (be != null) { + if (be.Method == null && IsSideEffectFree(be.Left) && IsSideEffectFree(be.Right)) { + return true; + } + } + + MethodCallExpression mc = rewritten as MethodCallExpression; + if (mc != null && mc.Method != null) { + return mc.Method.IsDefined(typeof(NoSideEffectsAttribute), false); + } + + ConditionalExpression ce = rewritten as ConditionalExpression; + if (ce != null) { + return IsSideEffectFree(ce.Test) && IsSideEffectFree(ce.IfTrue) && IsSideEffectFree(ce.IfFalse); + } + + MemberExpression me = rewritten as MemberExpression; + if (me != null && me.Member is System.Reflection.FieldInfo) { + return false; + } + + return false; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ComboBinder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ComboBinder.cs new file mode 100644 index 0000000000..97688d12c3 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ComboBinder.cs @@ -0,0 +1,293 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Dynamic.Binders; +using System.Text; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + /// + /// A binder which can combine multiple binders into a single dynamic site. The creator + /// of this needs to perform the mapping of parameters, constants, and sub-site expressions + /// and provide a List of BinderMappingInfo representing this data. From there the ComboBinder + /// just processes the list to create the resulting code. + /// + public class ComboBinder : MetaObjectBinder { + private readonly BinderMappingInfo[] _metaBinders; + + public ComboBinder(params BinderMappingInfo[] binders) + : this((ICollection)binders) { + } + + public ComboBinder(ICollection binders) { + Assert.NotNullItems(binders); + + _metaBinders = ArrayUtils.ToArray(binders); + } + + public override MetaObject Bind(MetaObject target, params MetaObject[] args) { + args = ArrayUtils.Insert(target, args); + + List results = new List(_metaBinders.Length); + List steps = new List(); + List temps = new List(); + Restrictions restrictions = Restrictions.Empty; + + for (int i = 0; i < _metaBinders.Length; i++) { + BinderMappingInfo curBinder = _metaBinders[i]; + + MetaObject[] tmpargs = GetArguments(args, results, i); + MetaObject next = curBinder.Binder.Bind(tmpargs[0], ArrayUtils.RemoveFirst(tmpargs)); + + if (next.Expression.NodeType == ExpressionType.Throw) { + // end of the line... the expression is throwing, none of the other + // binders will have an opportunity to run. + steps.Add(next.Expression); + break; + } + + ParameterExpression tmp = Expression.Variable(next.Expression.Type, "comboTemp" + i.ToString()); + temps.Add(tmp); + + steps.Add(Expression.Assign(tmp, next.Expression)); + results.Add(new MetaObject(tmp, next.Restrictions)); + restrictions = restrictions.Merge(next.Restrictions); + } + + return new MetaObject( + Expression.Block( + temps.ToArray(), + steps.ToArray() + ), + restrictions + ); + } + + private MetaObject[] GetArguments(MetaObject[] args, IList results, int metaBinderIndex) { + BinderMappingInfo indices = _metaBinders[metaBinderIndex]; + + MetaObject[] res = new MetaObject[indices.MappingInfo.Count]; + for (int i = 0; i < res.Length; i++) { + ParameterMappingInfo mappingInfo = indices.MappingInfo[i]; + + if (mappingInfo.IsAction) { + // input is the result of a previous bind + res[i] = results[mappingInfo.ActionIndex]; + } else if (mappingInfo.IsParameter) { + // input is one of the original arguments + res[i] = args[mappingInfo.ParameterIndex]; + } else { + // input is a constant + res[i] = new MetaObject( + mappingInfo.Constant, + Restrictions.Empty, + mappingInfo.Constant.Value + ); + } + } + + return res; + } + + public override object CacheIdentity { + get { return this; } + } + + public override int GetHashCode() { + int res = 6551; + foreach (BinderMappingInfo metaBinder in _metaBinders) { + res ^= metaBinder.Binder.CacheIdentity.GetHashCode(); + + foreach (ParameterMappingInfo mapInfo in metaBinder.MappingInfo) { + res ^= mapInfo.GetHashCode(); + } + } + + return res; + } + + public override bool Equals(object obj) { + ComboBinder other = obj as ComboBinder; + if (other != null) { + if (_metaBinders.Length != other._metaBinders.Length) { + return false; + } + + for (int i = 0; i < _metaBinders.Length; i++) { + BinderMappingInfo self = _metaBinders[i]; + BinderMappingInfo otherBinders = other._metaBinders[i]; + + if (!self.Binder.CacheIdentity.Equals(otherBinders.Binder.CacheIdentity) || + self.MappingInfo.Count != otherBinders.MappingInfo.Count) { + return false; + } + + for (int j = 0; j < self.MappingInfo.Count; j++) { + if (!self.MappingInfo[j].Equals(otherBinders.MappingInfo[j])) { + return false; + } + } + } + return true; + } + + return false; + } + } + + /// + /// Provides a mapping for inputs of combo action expressions. The input can map + /// to either an input of the new dynamic site, an input of a previous DynamicExpression, + /// or a ConstantExpression which has been pulled out of the dynamic site arguments. + /// + public class ParameterMappingInfo { + private readonly int _parameterIndex; + private readonly int _actionIndex; + private ConstantExpression _fixedInput; + + private ParameterMappingInfo(int param, int action, ConstantExpression fixedInput) { + _parameterIndex = param; + _actionIndex = action; + _fixedInput = fixedInput; + } + + public static ParameterMappingInfo Parameter(int index) { + return new ParameterMappingInfo(index, -1, null); + } + + public static ParameterMappingInfo Action(int index) { + return new ParameterMappingInfo(-1, index, null); + } + + public static ParameterMappingInfo Fixed(ConstantExpression e) { + return new ParameterMappingInfo(-1, -1, e); + } + + public int ParameterIndex { + get { + return _parameterIndex; + } + } + + public int ActionIndex { + get { + return _actionIndex; + } + } + + public ConstantExpression Constant { + get { + return _fixedInput; + } + } + + public bool IsParameter { + get { + return _parameterIndex != -1; + } + } + + public bool IsAction { + get { + return _actionIndex != -1; + } + } + + public bool IsConstant { + get { + return _fixedInput != null; + } + } + + public override bool Equals(object obj) { + ParameterMappingInfo pmi = obj as ParameterMappingInfo; + if (pmi == null) { + return false; + } + + if (pmi.ParameterIndex == ParameterIndex && pmi.ActionIndex == ActionIndex) { + if (Constant != null) { + if (pmi.Constant == null) { + return false; + } + + return Constant.Value == pmi.Constant.Value; + } else { + return pmi.Constant == null; + } + } + + return false; + } + + public override int GetHashCode() { + int res = ParameterIndex.GetHashCode() ^ ActionIndex.GetHashCode(); + if (Constant != null) { + if (Constant.Value != null) { + res ^= Constant.Value.GetHashCode(); + } + } + return res; + } + + public override string ToString() { + if (IsAction) { + return "Action" + ActionIndex.ToString(); + } else if (IsParameter) { + return "Parameter" + ParameterIndex.ToString(); + } else { + object value = Constant.Value; + if (value == null) { + return "(null)"; + } + + return value.ToString(); + } + } + } + + /// + /// Contains the mapping information for a single Combo Binder. This includes the original + /// meta-binder and the mapping of parameters, sub-sites, and constants into the binding. + /// + public class BinderMappingInfo { + public MetaObjectBinder Binder; + public IList MappingInfo; + + public BinderMappingInfo(MetaObjectBinder binder, IList mappingInfo) { + Binder = binder; + MappingInfo = mappingInfo; + } + + public BinderMappingInfo(MetaObjectBinder binder, params ParameterMappingInfo[] mappingInfos) + : this(binder, (IList)mappingInfos) { + } + + public override string ToString() { + StringBuilder res = new StringBuilder(); + res.Append(Binder.ToString()); + res.Append(" "); + string comma = ""; + foreach (ParameterMappingInfo info in MappingInfo) { + res.Append(comma); + res.Append(info.ToString()); + comma = ", "; + } + return res.ToString(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ConditionalBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ConditionalBuilder.cs new file mode 100644 index 0000000000..6ff6d35e0f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ConditionalBuilder.cs @@ -0,0 +1,117 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Dynamic.Binders; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + /// + /// Builds up a series of conditionals when the False clause isn't yet known. We can + /// keep appending conditions and if true's. Each subsequent true branch becomes the + /// false branch of the previous condition and body. Finally a non-conditional terminating + /// branch must be added. + /// + class ConditionalBuilder { + private readonly List _conditions = new List(); + private readonly List _bodies = new List(); + private readonly List _variables = new List(); + private Expression _body; + private Restrictions _restrictions = Restrictions.Empty; + + /// + /// Adds a new conditional and body. The first call this becomes the top-level + /// conditional, subsequent calls will have it added as false statement of the + /// previous conditional. + /// + public void AddCondition(Expression condition, Expression body) { + Assert.NotNull(condition, body); + + _conditions.Add(condition); + _bodies.Add(body); + } + + /// + /// Adds the non-conditional terminating node. + /// + public void FinishCondition(Expression body) { + if (_body != null) throw new InvalidOperationException(); + + for (int i = _bodies.Count - 1; i >= 0; i--) { + Type t = _bodies[i].Type; + if (t != body.Type) { + if (t.IsSubclassOf(body.Type)) { + // subclass + t = body.Type; + } else if (body.Type.IsSubclassOf(t)) { + // keep t + } else { + // incompatible, both go to object + t = typeof(object); + } + } + + body = Ast.Condition( + _conditions[i], + AstUtils.Convert(_bodies[i], t), + AstUtils.Convert(body, t) + ); + } + + _body = Ast.Block( + _variables, + body + ); + } + + public Restrictions Restrictions { + get { + return _restrictions; + } + set { + ContractUtils.RequiresNotNull(value, "value"); + _restrictions = value; + } + } + + /// + /// Gets the resulting meta object for the full body. FinishCondition + /// must have been called. + /// + public MetaObject GetMetaObject(params MetaObject[] types) { + if (_body == null) { + throw new InvalidOperationException("FinishCondition should have been called"); + } + + return new MetaObject( + _body, + Restrictions.Combine(types).Merge(Restrictions) + ); + } + + /// + /// Adds a variable which will be scoped at the level of the final expression. + /// + public void AddVariable(ParameterExpression var) { + _variables.Add(var); + } + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ConstructorTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ConstructorTracker.cs new file mode 100644 index 0000000000..24ab5fd08c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ConstructorTracker.cs @@ -0,0 +1,51 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Actions { + public class ConstructorTracker : MemberTracker { + private ConstructorInfo _ctor; + + public ConstructorTracker(ConstructorInfo ctor) { + _ctor = ctor; + } + + public override Type DeclaringType { + get { return _ctor.DeclaringType; } + } + + public override TrackerTypes MemberType { + get { return TrackerTypes.Constructor; } + } + + public override string Name { + get { return _ctor.Name; } + } + + public bool IsPublic { + get { + return _ctor.IsPublic; + } + } + + [Confined] + public override string ToString() { + return _ctor.ToString(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ConvertToBinderHelper.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ConvertToBinderHelper.cs new file mode 100644 index 0000000000..00d4844465 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ConvertToBinderHelper.cs @@ -0,0 +1,630 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + using AstUtils = Microsoft.Scripting.Ast.Utils; + + /// + /// BinderHelper for producing rules related to performing conversions. + /// + public class ConvertToBinderHelper : BinderHelper { + private object _arg; + private readonly RuleBuilder _rule; + + public ConvertToBinderHelper(CodeContext context, OldConvertToAction action, object[] args, RuleBuilder rule) + : base(context, action) { + ContractUtils.Requires(args.Length == 1, "can only convert single argument"); + + _arg = args[0]; + _rule = rule; + } + + public void MakeRule() { + Type toType = Action.ToType; + Type knownType = CompilerHelpers.GetVisibleType(_rule.Parameters[0].Type); + + // check for conversion to object first... + if (TryConvertToObject(toType, knownType)) { + _rule.AddTest(Ast.Constant(true)); + return; + } + + // do checks that aren't based upon strongly knowing the object's type (and don't require tests) + if (TryAllConversions(toType, knownType)) { + _rule.AddTest(Ast.Constant(true)); + return; + } + + // try again w/ a test for the known-type + Type type = CompilerHelpers.GetType(_arg); + _rule.AddTest(_rule.MakeTypeTest(type, 0)); + + if (TryAllConversions(toType, type)) { + return; + } + + // finally try conversions that aren't based upon the incoming type at all but + // are last chance conversions based on the destination type + if (TryExtraConversions(toType)) { + return; + } + + // no conversion is available, make an error rule. + MakeErrorTarget(); + } + + #region Conversion attempt helpers + + /// + /// Checks if the conversion is to object and produces a target if it is. + /// + private bool TryConvertToObject(Type toType, Type knownType) { + if (toType == typeof(object)) { + if (knownType.IsValueType) { + MakeBoxingTarget(knownType); + } else { + MakePerfectMatchTarget(); + } + return true; + } + return false; + } + + /// + /// Checks if any conversions are available and if so builds the target for that conversion. + /// + private bool TryAllConversions(Type toType, Type knownType) { + return TryAssignableConversion(toType, knownType) || // known type -> known type + TryExtensibleConversion(toType, knownType) || // Extensible -> Extensible.Value + TryUserDefinedConversion(toType, knownType) || // op_Implicit + TryImplicitNumericConversion(toType, knownType) || // op_Implicit + TryNullableConversion(toType, knownType) || // null -> Nullable or T -> Nullable + TryEnumerableConversion(toType, knownType) || // IEnumerator -> IEnumerable / IEnumerator -> IEnumerable + TryNullConversion(toType, knownType) || // null -> reference type + TryComConversion(toType, knownType); // System.__ComObject -> interface + } + + /// + /// Checks if the conversion can be handled by a simple cast. + /// + private bool TryAssignableConversion(Type toType, Type type) { + if (toType.IsAssignableFrom(type) || + (type == typeof(Null) && (toType.IsClass || toType.IsInterface))) { + if (toType == typeof(IEnumerator) && typeof(IEnumerable).IsAssignableFrom(type)) { + // Special case to handle C#-defined enumerators that implement both IEnumerable and IEnumerator + return false; + } + // MakeSimpleConversionTarget handles the ConversionResultKind check + MakeSimpleConversionTarget(toType, type); + return true; + } + + return false; + } + + /// + /// Checks if the conversion can be handled by calling a user-defined conversion method. + /// + private bool TryUserDefinedConversion(Type toType, Type type) { + Type fromType = GetUnderlyingType(type); + + if (TryOneConversion(toType, type, fromType, "op_Implicit", true)) { + return true; + } + + if (TryOneConversion(toType, type, fromType, "ConvertTo" + toType.Name, true)) { + return true; + } + + if (Action.ResultKind == ConversionResultKind.ExplicitCast || Action.ResultKind == ConversionResultKind.ExplicitTry) { + // finally try explicit conversions + if (TryOneConversion(toType, type, fromType, "op_Explicit", false)) { + return true; + } + + if (TryOneConversion(toType, type, fromType, "ConvertTo" + toType.Name, false)) { + return true; + } + } + + return false; + } + + /// + /// Helper that checkes both types to see if either one defines the specified conversion + /// method. + /// + private bool TryOneConversion(Type toType, Type type, Type fromType, string methodName, bool isImplicit) { + MemberGroup conversions = Binder.GetMember(Action, fromType, methodName); + if (TryUserDefinedConversion(toType, type, conversions, isImplicit)) { + return true; + } + + // then on the type we're trying to convert to + conversions = Binder.GetMember(Action, toType, methodName); + if (TryUserDefinedConversion(toType, type, conversions, isImplicit)) { + return true; + } + return false; + } + + /// + /// Checks if any of the members of the MemberGroup provide the applicable conversion and + /// if so uses it to build a conversion rule. + /// + private bool TryUserDefinedConversion(Type toType, Type type, MemberGroup conversions, bool isImplicit) { + Type checkType = GetUnderlyingType(type); + + foreach (MemberTracker mt in conversions) { + if (mt.MemberType != TrackerTypes.Method) continue; + + MethodTracker method = (MethodTracker)mt; + + if (isImplicit && method.Method.IsDefined(typeof(ExplicitConversionMethodAttribute), true)) { + continue; + } + + if (method.Method.ReturnType == toType) { // TODO: IsAssignableFrom? IsSubclass? + ParameterInfo[] pis = method.Method.GetParameters(); + + if (pis.Length == 1 && pis[0].ParameterType.IsAssignableFrom(checkType)) { + // we can use this method + if (type == checkType) { + MakeConversionTarget(method, type, isImplicit); + } else { + MakeExtensibleConversionTarget(method, type, isImplicit); + } + return true; + } + } + } + return false; + } + + /// + /// Checks if the conversion is to applicable by extracting the value from Extensible of T. + /// + private bool TryExtensibleConversion(Type toType, Type type) { + Type extensibleType = typeof(Extensible<>).MakeGenericType(toType); + if (extensibleType.IsAssignableFrom(type)) { + MakeExtensibleTarget(extensibleType); + return true; + } + return false; + } + + /// + /// Checks if there's an implicit numeric conversion for primitive data types. + /// + private bool TryImplicitNumericConversion(Type toType, Type type) { + Type checkType = type; + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Extensible<>)) { + checkType = type.GetGenericArguments()[0]; + } + + if (TypeUtils.IsNumeric(toType) && TypeUtils.IsNumeric(checkType)) { + // check for an explicit conversion + int tox, toy, fromx, fromy; + if (TypeUtils.GetNumericConversionOrder(Type.GetTypeCode(toType), out tox, out toy) && + TypeUtils.GetNumericConversionOrder(Type.GetTypeCode(checkType), out fromx, out fromy)) { + if (TypeUtils.IsImplicitlyConvertible(fromx, fromy, tox, toy)) { + // MakeSimpleConversionTarget handles the ConversionResultKind check + if (type == checkType) { + MakeSimpleConversionTarget(toType, type); + } else { + MakeSimpleExtensibleConversionTarget(toType); + } + return true; + } + } + } + return false; + } + + /// + /// Checks if there's a conversion to/from Nullable of T. + /// + private bool TryNullableConversion(Type toType, Type knownType) { + if (toType.IsGenericType && toType.GetGenericTypeDefinition() == typeof(Nullable<>)) { + if (knownType == typeof(Null)) { + // null -> Nullable + MakeNullToNullableOfTTarget(toType); + return true; + } else if (knownType == toType.GetGenericArguments()[0]) { + MakeTToNullableOfTTarget(toType, knownType); + return true; + } else if (Action.ResultKind == ConversionResultKind.ExplicitCast || Action.ResultKind == ConversionResultKind.ExplicitTry) { + if (knownType != typeof(object)) { + // when doing an explicit cast we'll do things like int -> Nullable + MakeConvertingToTToNullableOfTTarget(toType); + return true; + } + } + } + + return false; + } + + /// + /// Checks if there's a conversion to IEnumerator or IEnumerator of T via calling GetEnumerator + /// + private bool TryEnumerableConversion(Type toType, Type knownType) { + if (toType == typeof(IEnumerator)) { + return MakeIEnumerableTarget(knownType); + } else if (toType.IsInterface && toType.IsGenericType && toType.GetGenericTypeDefinition() == typeof(IEnumerator<>)) { + return MakeIEnumeratorOfTTarget(toType, knownType); + } + return false; + } + + /// + /// Checks to see if there's a conversion of null to a reference type + /// + private bool TryNullConversion(Type toType, Type knownType) { + if (knownType == typeof(Null) && !toType.IsValueType) { + MakeNullTarget(toType); + return true; + } + return false; + } + + /// + /// Checks to see if there's a conversion of System.__ComObject to an interface type + /// + private bool TryComConversion(Type toType, Type knownType) { +#if !SILVERLIGHT // ComObject + if (knownType.IsCOMObject && (toType.IsInterface)) { + // Converting a COM object to any interface is always considered possible - it will result in + // a QueryInterface at runtime + MakeSimpleConversionTarget(toType, knownType); + return true; + } +#endif + return false; + } + + /// + /// Checks for any extra conversions which aren't based upon the incoming type of the object. + /// + private bool TryExtraConversions(Type toType) { + if (typeof(Delegate).IsAssignableFrom(toType) && toType != typeof(Delegate)) { + // generate a conversion to delegate + MakeDelegateTarget(toType); + return true; + } + return false; + } + + #endregion + + #region Rule production helpers + + /// + /// Helper to produce an error when a conversion cannot occur + /// + private void MakeErrorTarget() { + Expression target; + + switch (Action.ResultKind) { + case ConversionResultKind.ImplicitCast: + case ConversionResultKind.ExplicitCast: + target = Binder.MakeConversionError(Action.ToType, _rule.Parameters[0]).MakeErrorForRule(_rule, Binder); + break; + case ConversionResultKind.ImplicitTry: + case ConversionResultKind.ExplicitTry: + target = CompilerHelpers.GetTryConvertReturnValue(Context, _rule); + break; + default: + throw new InvalidOperationException(Action.ResultKind.ToString()); + } + _rule.Target =target; + } + + /// + /// Helper to produce a rule when no conversion is required (the strong type of the expression + /// input matches the type we're converting to) + /// + private void MakePerfectMatchTarget() { + _rule.Target = _rule.MakeReturn(Binder, _rule.Parameters[0]); + } + + /// + /// Helper to produce a rule which just boxes a value type + /// + private void MakeBoxingTarget(Type knownType) { + // MakeSimpleConversionTarget handles the ConversionResultKind check + MakeSimpleConversionTarget(typeof(object), knownType); + } + + /// + /// Helper to produce a conversion rule by calling the helper method to do the convert + /// + private void MakeConversionTarget(MethodTracker method, Type fromType, bool isImplicit) { + Expression ret = _rule.MakeReturn( + Binder, + Binder.MakeCallExpression(_rule.Context, method.Method, Ast.Convert(_rule.Parameters[0], fromType)) + ); + + ret = WrapForThrowingTry(isImplicit, ret); + + _rule.Target = ret; + } + + /// + /// Helper to produce a conversion rule by calling the helper method to do the convert + /// + private void MakeExtensibleConversionTarget(MethodTracker method, Type fromType, bool isImplicit) { + Expression ret = _rule.MakeReturn( + Binder, + Binder.MakeCallExpression(_rule.Context, method.Method, GetExtensibleValue(fromType)) + ); + + ret = WrapForThrowingTry(isImplicit, ret); + + _rule.Target = ret; + } + + /// + /// Helper to wrap explicit conversion call into try/catch incase it throws an exception. If + /// it throws the default value is returned. + /// + private Expression WrapForThrowingTry(bool isImplicit, Expression ret) { + if (!isImplicit && Action.ResultKind == ConversionResultKind.ExplicitTry) { + ret = AstUtils.Try(ret).Catch(typeof(Exception), CompilerHelpers.GetTryConvertReturnValue(Context, _rule)); + } + return ret; + } + + /// + /// Helper to produce a rule when no conversion is required (the strong type of the expression + /// input matches the type we're converting to or has an implicit conversion at the IL level) + /// + private void MakeSimpleConversionTarget(Type toType, Type knownType) { + if (toType.IsValueType && _rule.ReturnType == typeof(object) && _rule.Parameters[0].Type == typeof(object)) { + // boxed value type is being converted back to object. We've done + // the type check, there's no need to unbox & rebox the value. infact + // it breaks calls on instance methods so we need to avoid it. + _rule.Target = + _rule.MakeReturn( + Binder, + _rule.Parameters[0] + ); + } else { + Expression arg = _rule.Parameters[0]; + if (arg.Type != knownType && knownType != typeof(Null)) { + arg = Ast.Convert(arg, CompilerHelpers.GetVisibleType(knownType)); + } + _rule.Target = + _rule.MakeReturn( + Binder, + AstUtils.Convert(arg, CompilerHelpers.GetVisibleType(toType)) + ); + } + } + + /// + /// Helper to produce a rule when no conversion is required from an extensible type's + /// underlying storage to the type we're converting to. The type of extensible type + /// matches the type we're converting to or has an implicit conversion at the IL level. + /// + /// + private void MakeSimpleExtensibleConversionTarget(Type toType) { + Type extType = typeof(Extensible<>).MakeGenericType(toType); + _rule.Target = + _rule.MakeReturn( + Binder, + AstUtils.Convert( + GetExtensibleValue(extType), + toType + ) + ); + } + + /// + /// Helper to extract the value from an Extensible of T + /// + private void MakeExtensibleTarget(Type extensibleType) { + _rule.Target = + _rule.MakeReturn( + Binder, + Ast.Property(Ast.Convert(_rule.Parameters[0], extensibleType), extensibleType.GetProperty("Value")) + ); + } + + /// + /// Helper to convert a null value to nullable of T + /// + private void MakeNullToNullableOfTTarget(Type toType) { + _rule.Target = + _rule.MakeReturn( + Binder, + Ast.Call(typeof(ScriptingRuntimeHelpers).GetMethod("CreateInstance").MakeGenericMethod(toType)) + ); + } + + /// + /// Helper to produce the rule for converting T to Nullable of T + /// + private void MakeTToNullableOfTTarget(Type toType, Type knownType) { + // T -> Nullable + _rule.Target = + _rule.MakeReturn( + Binder, + Ast.New( + toType.GetConstructor(new Type[] { knownType }), + AstUtils.Convert(_rule.Parameters[0], knownType) + ) + ); + } + + /// + /// Helper to produce the rule for converting T to Nullable of T + /// + private void MakeConvertingToTToNullableOfTTarget(Type toType) { + Type valueType = toType.GetGenericArguments()[0]; + + // ConvertSelfToT -> Nullable + if (Action.ResultKind == ConversionResultKind.ExplicitCast) { + var action = OldConvertToAction.Make(Binder, valueType, Action.ResultKind); + var conversion = Expression.Dynamic(action, action.ToType, _rule.Context, _rule.Parameters[0]); + // if the conversion to T fails we just throw + _rule.Target = + _rule.MakeReturn( + Binder, + Ast.New( + toType.GetConstructor(new Type[] { valueType }), + conversion + ) + ); + } else { + // if the conversion to T succeeds then produce the nullable, otherwise return default(retType) + var conversion = Expression.Dynamic(OldConvertToAction.Make(Binder, valueType, Action.ResultKind), typeof(object), _rule.Context, _rule.Parameters[0]); + + ParameterExpression tmp = _rule.GetTemporary(typeof(object), "tmp"); + _rule.Target = + AstUtils.If( + Ast.NotEqual( + Ast.Assign(tmp, conversion), + Ast.Constant(null) + ), + _rule.MakeReturn( + Binder, + Ast.New( + toType.GetConstructor(new Type[] { valueType }), + Ast.Convert( + tmp, + valueType + ) + ) + ) + ).Else( + CompilerHelpers.GetTryConvertReturnValue(Context, _rule) + ); +} + } + + /// + /// Helper to extract the Value of an Extensible of T from the + /// expression being converted. + /// + private Expression GetExtensibleValue(Type extType) { + return Ast.Property( + AstUtils.Convert( + _rule.Parameters[0], + extType + ), + extType.GetProperty("Value") + ); + } + + /// + /// Helper that checks if fromType is an Extensible of T or a subtype of + /// Extensible of T and if so returns the T. Otherwise it returns fromType. + /// + /// This is used to treat extensible types the same as their underlying types. + /// + private static Type GetUnderlyingType(Type fromType) { + Type curType = fromType; + do { + if (curType.IsGenericType && curType.GetGenericTypeDefinition() == typeof(Extensible<>)) { + fromType = curType.GetGenericArguments()[0]; + } + curType = curType.BaseType; + } while (curType != null); + return fromType; + } + + /// + /// Makes a conversion target which converts IEnumerable -> IEnumerator + /// + private bool MakeIEnumerableTarget(Type knownType) { + bool canConvert = typeof(IEnumerable).IsAssignableFrom(knownType); +#if !SILVERLIGHT // ComObject + if (!canConvert) { + // Converting a COM object to any interface is always considered possible - it will result in + // a QueryInterface at runtime + canConvert = knownType.IsCOMObject; + } +#endif + if (canConvert) { + _rule.Target = + _rule.MakeReturn(Binder, + Ast.Call( + AstUtils.Convert(_rule.Parameters[0], typeof(IEnumerable)), + typeof(IEnumerable).GetMethod("GetEnumerator") + ) + ); + return true; + } + return false; + } + + /// + /// Makes a conversion target which converts IEnumerable of T to IEnumerator of T + /// + private bool MakeIEnumeratorOfTTarget(Type toType, Type knownType) { + Type enumType = typeof(IEnumerable<>).MakeGenericType(toType.GetGenericArguments()[0]); + if (enumType.IsAssignableFrom(knownType)) { + _rule.Target = + _rule.MakeReturn(Binder, + Ast.Call( + AstUtils.Convert(_rule.Parameters[0], enumType), + toType.GetMethod("GetEnumerator") + ) + ); + return true; + } + return false; + } + + /// + /// Creates a target which returns null for a reference type. + /// + /// + private void MakeNullTarget(Type toType) { + _rule.Target = _rule.MakeReturn(Binder, Ast.Convert(Ast.Constant(null), toType)); + } + + /// + /// Creates a target which creates a new dynamic method which contains a single + /// dynamic site that invokes the callable object. + /// + private void MakeDelegateTarget(Type toType) { + _rule.Target = + _rule.MakeReturn( + Binder, + Ast.Call( + typeof(BinderOps).GetMethod("GetDelegate"), + Ast.Property(_rule.Context, typeof(CodeContext), "LanguageContext"), + _rule.Parameters[0], + Ast.Constant(toType) + ) + ); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/CreateInstanceBinderHelper.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/CreateInstanceBinderHelper.cs new file mode 100644 index 0000000000..1cbc454983 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/CreateInstanceBinderHelper.cs @@ -0,0 +1,90 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public class CreateInstanceBinderHelper : CallBinderHelper { + public CreateInstanceBinderHelper(CodeContext context, OldCreateInstanceAction action, object[] args, RuleBuilder rule) + : base(context, action, args, rule) { + } + + public override void MakeRule() { + base.MakeRule(); + + if (Rule.IsError) { + // Constructing a delegate? + Type t = GetTargetType(Callable); + + if (typeof(Delegate).IsAssignableFrom(t) && Arguments.Length == 2) { + MethodInfo dc = GetDelegateCtor(t); + + // ScriptingRuntimeHelpers.CreateDelegate(CodeContext context, object callable); + + Rule.IsError = false; + Rule.Target = Rule.MakeReturn( + Binder, + Expression.Call(null, dc, Rule.Context, Rule.Parameters[1]) + ); + } + } + } + + private static MethodInfo GetDelegateCtor(Type t) { + return typeof(BinderOps).GetMethod("CreateDelegate").MakeGenericMethod(t); + } + + protected override MethodBase[] GetTargetMethods() { + object target = Arguments[0]; + Type t = GetTargetType(target); + + if (t != null) { + Test = Ast.AndAlso(Test, Ast.Equal(Rule.Parameters[0], Ast.Constant(target))); + + return CompilerHelpers.GetConstructors(t, Binder.PrivateBinding); + } + + return null; + } + + private static Type GetTargetType(object target) { + TypeTracker tt = target as TypeTracker; + if (tt != null) { + return tt.Type; + } + return target as Type; + } + + protected override void MakeCannotCallRule(Type type) { + string name = type.Name; + Type t = Arguments[0] as Type; + if (t != null) name = t.Name; + + Rule.Target = + Rule.MakeError( + Ast.New( + typeof(ArgumentTypeException).GetConstructor(new Type[] { typeof(string) }), + Ast.Constant("Cannot create instances of " + name) + ) + ); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/CustomTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/CustomTracker.cs new file mode 100644 index 0000000000..7f409325d8 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/CustomTracker.cs @@ -0,0 +1,29 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Actions { + /// + /// A custom member tracker which enables languages to plug in arbitrary + /// members into the lookup process. + /// + public abstract class CustomTracker : MemberTracker { + protected CustomTracker() { + } + + public sealed override TrackerTypes MemberType { + get { return TrackerTypes.Custom; } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.Conversions.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.Conversions.cs new file mode 100644 index 0000000000..642177c4ca --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.Conversions.cs @@ -0,0 +1,505 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic; +using System.Dynamic.Binders; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + using AstUtils = Microsoft.Scripting.Ast.Utils; + + public partial class DefaultBinder : ActionBinder { + public MetaObject ConvertTo(Type toType, ConversionResultKind kind, MetaObject arg) { + ContractUtils.RequiresNotNull(toType, "toType"); + ContractUtils.RequiresNotNull(arg, "arg"); + + Type knownType = arg.LimitType; + + // try all the conversions - first look for conversions against the expression type, + // these can be done w/o any additional tests. Then look for conversions against the + // restricted type. + Restrictions typeRestrictions = arg.Restrictions.Merge(Restrictions.GetTypeRestriction(arg.Expression, arg.LimitType)); + + return + TryConvertToObject(toType, arg.Expression.Type, arg) ?? + TryAllConversions(toType, kind, arg.Expression.Type, arg.Restrictions, arg) ?? + TryAllConversions(toType, kind, arg.LimitType, typeRestrictions, arg) ?? + MakeErrorTarget(toType, kind, typeRestrictions, arg); + } + + #region Conversion attempt helpers + + /// + /// Checks if the conversion is to object and produces a target if it is. + /// + private static MetaObject TryConvertToObject(Type toType, Type knownType, MetaObject arg) { + if (toType == typeof(object)) { + if (knownType.IsValueType) { + return MakeBoxingTarget(arg); + } else { + return arg; + } + } + return null; + } + + /// + /// Checks if any conversions are available and if so builds the target for that conversion. + /// + private MetaObject TryAllConversions(Type toType, ConversionResultKind kind, Type knownType, Restrictions restrictions, MetaObject arg) { + return + TryAssignableConversion(toType, knownType, restrictions, arg) ?? // known type -> known type + TryExtensibleConversion(toType, knownType, restrictions, arg) ?? // Extensible -> Extensible.Value + TryUserDefinedConversion(kind, toType, knownType, restrictions, arg) ?? // op_Implicit + TryImplicitNumericConversion(toType, knownType, restrictions, arg) ?? // op_Implicit + TryNullableConversion(toType, kind, knownType, restrictions, arg) ?? // null -> Nullable or T -> Nullable + TryNullConversion(toType, knownType, restrictions); // null -> reference type + } + + /// + /// Checks if the conversion can be handled by a simple cast. + /// + private static MetaObject TryAssignableConversion(Type toType, Type type, Restrictions restrictions, MetaObject arg) { + if (toType.IsAssignableFrom(type) || + (type == typeof(Null) && (toType.IsClass || toType.IsInterface))) { + // MakeSimpleConversionTarget handles the ConversionResultKind check + return MakeSimpleConversionTarget(toType, restrictions, arg); + } + + return null; + } + + /// + /// Checks if the conversion can be handled by calling a user-defined conversion method. + /// + private MetaObject TryUserDefinedConversion(ConversionResultKind kind, Type toType, Type type, Restrictions restrictions, MetaObject arg) { + Type fromType = GetUnderlyingType(type); + + MetaObject res = + TryOneConversion(kind, toType, type, fromType, "op_Implicit", true, restrictions, arg) ?? + TryOneConversion(kind, toType, type, fromType, "ConvertTo" + toType.Name, true, restrictions, arg); + + if (kind == ConversionResultKind.ExplicitCast || + kind == ConversionResultKind.ExplicitTry) { + // finally try explicit conversions + res = res ?? + TryOneConversion(kind, toType, type, fromType, "op_Explicit", false, restrictions, arg) ?? + TryOneConversion(kind, toType, type, fromType, "ConvertTo" + toType.Name, false, restrictions, arg); + } + + return res; + } + + /// + /// Helper that checkes both types to see if either one defines the specified conversion + /// method. + /// + private MetaObject TryOneConversion(ConversionResultKind kind, Type toType, Type type, Type fromType, string methodName, bool isImplicit, Restrictions restrictions, MetaObject arg) { + OldConvertToAction action = OldConvertToAction.Make(this, toType, kind); + + MemberGroup conversions = GetMember(action, fromType, methodName); + MetaObject res = TryUserDefinedConversion(kind, toType, type, conversions, isImplicit, restrictions, arg); + if (res != null) { + return res; + } + + // then on the type we're trying to convert to + conversions = GetMember(action, toType, methodName); + return TryUserDefinedConversion(kind, toType, type, conversions, isImplicit, restrictions, arg); + } + + /// + /// Checks if any of the members of the MemberGroup provide the applicable conversion and + /// if so uses it to build a conversion rule. + /// + private static MetaObject TryUserDefinedConversion(ConversionResultKind kind, Type toType, Type type, MemberGroup conversions, bool isImplicit, Restrictions restrictions, MetaObject arg) { + Type checkType = GetUnderlyingType(type); + + foreach (MemberTracker mt in conversions) { + if (mt.MemberType != TrackerTypes.Method) continue; + + MethodTracker method = (MethodTracker)mt; + + if (isImplicit && method.Method.IsDefined(typeof(ExplicitConversionMethodAttribute), true)) { + continue; + } + + if (method.Method.ReturnType == toType) { // TODO: IsAssignableFrom? IsSubclass? + ParameterInfo[] pis = method.Method.GetParameters(); + + if (pis.Length == 1 && pis[0].ParameterType.IsAssignableFrom(checkType)) { + // we can use this method + if (type == checkType) { + return MakeConversionTarget(kind, method, type, isImplicit, restrictions, arg); + } else { + return MakeExtensibleConversionTarget(kind, method, type, isImplicit, restrictions, arg); + } + } + } + } + return null; + } + + /// + /// Checks if the conversion is to applicable by extracting the value from Extensible of T. + /// + private static MetaObject TryExtensibleConversion(Type toType, Type type, Restrictions restrictions, MetaObject arg) { + Type extensibleType = typeof(Extensible<>).MakeGenericType(toType); + if (extensibleType.IsAssignableFrom(type)) { + return MakeExtensibleTarget(extensibleType, restrictions, arg); + } + return null; + } + + /// + /// Checks if there's an implicit numeric conversion for primitive data types. + /// + private static MetaObject TryImplicitNumericConversion(Type toType, Type type, Restrictions restrictions, MetaObject arg) { + Type checkType = type; + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Extensible<>)) { + checkType = type.GetGenericArguments()[0]; + } + + if (TypeUtils.IsNumeric(toType) && TypeUtils.IsNumeric(checkType)) { + // check for an explicit conversion + int toX, toY, fromX, fromY; + if (TypeUtils.GetNumericConversionOrder(Type.GetTypeCode(toType), out toX, out toY) && + TypeUtils.GetNumericConversionOrder(Type.GetTypeCode(checkType), out fromX, out fromY)) { + if (TypeUtils.IsImplicitlyConvertible(fromX, fromY, toX, toY)) { + // MakeSimpleConversionTarget handles the ConversionResultKind check + if (type == checkType) { + return MakeSimpleConversionTarget(toType, restrictions, arg); + } else { + return MakeSimpleExtensibleConversionTarget(toType, restrictions, arg); + } + } + } + } + return null; + } + + /// + /// Checks if there's a conversion to/from Nullable of T. + /// + private MetaObject TryNullableConversion(Type toType, ConversionResultKind kind, Type knownType, Restrictions restrictions, MetaObject arg) { + if (toType.IsGenericType && toType.GetGenericTypeDefinition() == typeof(Nullable<>)) { + if (knownType == typeof(Null)) { + // null -> Nullable + return MakeNullToNullableOfTTarget(toType, restrictions); + } else if (knownType == toType.GetGenericArguments()[0]) { + return MakeTToNullableOfTTarget(toType, knownType, restrictions, arg); + } else if (kind == ConversionResultKind.ExplicitCast || kind == ConversionResultKind.ExplicitTry) { + if (knownType != typeof(object)) { + // when doing an explicit cast we'll do things like int -> Nullable + return MakeConvertingToTToNullableOfTTarget(toType, kind, restrictions, arg); + } + } + } + + return null; + } + + /// + /// Checks to see if there's a conversion of null to a reference type + /// + private static MetaObject TryNullConversion(Type toType, Type knownType, Restrictions restrictions) { + if (knownType == typeof(Null) && !toType.IsValueType) { + return MakeNullTarget(toType, restrictions); + } + return null; + } + + #endregion + + #region Rule production helpers + + /// + /// Helper to produce an error when a conversion cannot occur + /// + private MetaObject MakeErrorTarget(Type toType, ConversionResultKind kind, Restrictions restrictions, MetaObject arg) { + MetaObject target; + + switch (kind) { + case ConversionResultKind.ImplicitCast: + case ConversionResultKind.ExplicitCast: + target = MakeError( + MakeConversionError(toType, arg.Expression), + restrictions + ); + break; + case ConversionResultKind.ImplicitTry: + case ConversionResultKind.ExplicitTry: + target = new MetaObject( + GetTryConvertReturnValue(toType), + restrictions + ); + break; + default: + throw new InvalidOperationException(kind.ToString()); + } + + return target; + } + + /// + /// Helper to produce a rule which just boxes a value type + /// + private static MetaObject MakeBoxingTarget(MetaObject arg) { + // MakeSimpleConversionTarget handles the ConversionResultKind check + return MakeSimpleConversionTarget(typeof(object), arg.Restrictions, arg); + } + + /// + /// Helper to produce a conversion rule by calling the helper method to do the convert + /// + private static MetaObject MakeConversionTarget(ConversionResultKind kind, MethodTracker method, Type fromType, bool isImplicit, Restrictions restrictions, MetaObject arg) { + Expression param = AstUtils.Convert(arg.Expression, fromType); + + return MakeConversionTargetWorker(kind, method, isImplicit, restrictions, param); + } + + /// + /// Helper to produce a conversion rule by calling the helper method to do the convert + /// + private static MetaObject MakeExtensibleConversionTarget(ConversionResultKind kind, MethodTracker method, Type fromType, bool isImplicit, Restrictions restrictions, MetaObject arg) { + return MakeConversionTargetWorker(kind, method, isImplicit, restrictions, GetExtensibleValue(fromType, arg)); + } + + /// + /// Helper to produce a conversion rule by calling the method to do the convert. This version takes the parameter + /// to be passed to the conversion function and we call it w/ our own value or w/ our Extensible.Value. + /// + private static MetaObject MakeConversionTargetWorker(ConversionResultKind kind, MethodTracker method, bool isImplicit, Restrictions restrictions, Expression param) { + return new MetaObject( + WrapForThrowingTry( + kind, + isImplicit, + AstUtils.SimpleCallHelper( + method.Method, + param + ), + method.Method.ReturnType + ), + restrictions + ); + } + + /// + /// Helper to wrap explicit conversion call into try/catch incase it throws an exception. If + /// it throws the default value is returned. + /// + private static Expression WrapForThrowingTry(ConversionResultKind kind, bool isImplicit, Expression ret, Type retType) { + if (!isImplicit && kind == ConversionResultKind.ExplicitTry) { + Expression convFailed = GetTryConvertReturnValue(retType); + ParameterExpression tmp = Ast.Variable(convFailed.Type == typeof(object) ? typeof(object) : ret.Type, "tmp"); + ret = Ast.Block( + new ParameterExpression[] { tmp }, + AstUtils.Try( + Ast.Assign(tmp, AstUtils.Convert(ret, tmp.Type)) + ).Catch( + typeof(Exception), + Ast.Assign(tmp, convFailed) + ), + tmp + ); + } + return ret; + } + + /// + /// Helper to produce a rule when no conversion is required (the strong type of the expression + /// input matches the type we're converting to or has an implicit conversion at the IL level) + /// + private static MetaObject MakeSimpleConversionTarget(Type toType, Restrictions restrictions, MetaObject arg) { + return new MetaObject( + AstUtils.Convert(arg.Expression, CompilerHelpers.GetVisibleType(toType)), + restrictions); + + /* + if (toType.IsValueType && _rule.ReturnType == typeof(object) && Expression.Type == typeof(object)) { + // boxed value type is being converted back to object. We've done + // the type check, there's no need to unbox & rebox the value. infact + // it breaks calls on instance methods so we need to avoid it. + _rule.Target = + _rule.MakeReturn( + Binder, + Expression + ); + } + * */ + } + + /// + /// Helper to produce a rule when no conversion is required from an extensible type's + /// underlying storage to the type we're converting to. The type of extensible type + /// matches the type we're converting to or has an implicit conversion at the IL level. + /// + private static MetaObject MakeSimpleExtensibleConversionTarget(Type toType, Restrictions restrictions, MetaObject arg) { + Type extType = typeof(Extensible<>).MakeGenericType(toType); + + return new MetaObject( + AstUtils.Convert( + GetExtensibleValue(extType, arg), + toType + ), + restrictions + ); + } + + /// + /// Helper to extract the value from an Extensible of T + /// + private static MetaObject MakeExtensibleTarget(Type extensibleType, Restrictions restrictions, MetaObject arg) { + return new MetaObject( + Ast.Property(Ast.Convert(arg.Expression, extensibleType), extensibleType.GetProperty("Value")), + restrictions + ); + } + + /// + /// Helper to convert a null value to nullable of T + /// + private static MetaObject MakeNullToNullableOfTTarget(Type toType, Restrictions restrictions) { + return new MetaObject( + Ast.Call(typeof(ScriptingRuntimeHelpers).GetMethod("CreateInstance").MakeGenericMethod(toType)), + restrictions + ); + } + + /// + /// Helper to produce the rule for converting T to Nullable of T + /// + private static MetaObject MakeTToNullableOfTTarget(Type toType, Type knownType, Restrictions restrictions, MetaObject arg) { + // T -> Nullable + return new MetaObject( + Ast.New( + toType.GetConstructor(new Type[] { knownType }), + AstUtils.Convert(arg.Expression, knownType) + ), + restrictions + ); + } + + /// + /// Helper to produce the rule for converting T to Nullable of T + /// + private MetaObject MakeConvertingToTToNullableOfTTarget(Type toType, ConversionResultKind kind, Restrictions restrictions, MetaObject arg) { + Type valueType = toType.GetGenericArguments()[0]; + + // ConvertSelfToT -> Nullable + if (kind == ConversionResultKind.ExplicitCast) { + // if the conversion to T fails we just throw + Expression conversion = ConvertExpression(arg.Expression, valueType, kind, Ast.Constant(null, typeof(CodeContext))); + + return new MetaObject( + Ast.New( + toType.GetConstructor(new Type[] { valueType }), + conversion + ), + restrictions + ); + } else { + Expression conversion = ConvertExpression(arg.Expression, valueType, kind, Ast.Constant(null, typeof(CodeContext))); + + // if the conversion to T succeeds then produce the nullable, otherwise return default(retType) + ParameterExpression tmp = Ast.Variable(typeof(object), "tmp"); + return new MetaObject( + Ast.Block( + new ParameterExpression[] { tmp }, + Ast.Condition( + Ast.NotEqual( + Ast.Assign(tmp, conversion), + Ast.Constant(null) + ), + Ast.New( + toType.GetConstructor(new Type[] { valueType }), + Ast.Convert( + tmp, + valueType + ) + ), + GetTryConvertReturnValue(toType) + ) + ), + restrictions + ); + } + } + + /// + /// Returns a value which indicates failure when a OldConvertToAction of ImplicitTry or + /// ExplicitTry. + /// + public static Expression GetTryConvertReturnValue(Type type) { + Expression res; + if (type.IsInterface || type.IsClass) { + res = Ast.Constant(null, type); + } else { + res = Ast.Constant(null); + } + + return res; + } + + /// + /// Helper to extract the Value of an Extensible of T from the + /// expression being converted. + /// + private static Expression GetExtensibleValue(Type extType, MetaObject arg) { + return Ast.Property( + AstUtils.Convert( + arg.Expression, + extType + ), + extType.GetProperty("Value") + ); + } + + /// + /// Helper that checks if fromType is an Extensible of T or a subtype of + /// Extensible of T and if so returns the T. Otherwise it returns fromType. + /// + /// This is used to treat extensible types the same as their underlying types. + /// + private static Type GetUnderlyingType(Type fromType) { + Type curType = fromType; + do { + if (curType.IsGenericType && curType.GetGenericTypeDefinition() == typeof(Extensible<>)) { + fromType = curType.GetGenericArguments()[0]; + } + curType = curType.BaseType; + } while (curType != null); + return fromType; + } + + /// + /// Creates a target which returns null for a reference type. + /// + private static MetaObject MakeNullTarget(Type toType, Restrictions restrictions) { + return new MetaObject( + Ast.Convert(Ast.Constant(null), toType), + restrictions + ); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.Create.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.Create.cs new file mode 100644 index 0000000000..89cf1ef452 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.Create.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic.Binders; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Actions.Calls; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public partial class DefaultBinder : ActionBinder { + public MetaObject Create(CallSignature signature, ParameterBinderWithCodeContext parameterBinder, MetaObject target, MetaObject[] args) { + Type t = GetTargetType(target.Value); + + if (t != null) { + + if (typeof(Delegate).IsAssignableFrom(t) && args.Length == 1) { + MethodInfo dc = GetDelegateCtor(t); + + // BinderOps.CreateDelegate(CodeContext context, object callable); + return new MetaObject( + Ast.Call(null, dc, parameterBinder.ContextExpression, args[0].Expression), + target.Restrictions.Merge(Restrictions.GetInstanceRestriction(target.Expression, target.Value)) + ); + } + + return CallMethod(parameterBinder, CompilerHelpers.GetConstructors(t, PrivateBinding), args, signature); + } + + return null; + } + + private static MethodInfo GetDelegateCtor(Type t) { + return typeof(BinderOps).GetMethod("CreateDelegate").MakeGenericMethod(t); + } + + private static Type GetTargetType(object target) { + TypeTracker tt = target as TypeTracker; + if (tt != null) { + return tt.Type; + } + return target as Type; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.DeleteMember.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.DeleteMember.cs new file mode 100644 index 0000000000..55b7c282f9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.DeleteMember.cs @@ -0,0 +1,148 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic.Binders; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public partial class DefaultBinder : ActionBinder { + /// + /// Builds a MetaObject for performing a member delete. Supports all built-in .NET members, the OperatorMethod + /// DeleteMember, and StrongBox instances. + /// + public MetaObject DeleteMember(string name, MetaObject target) { + ContractUtils.RequiresNotNull(name, "name"); + ContractUtils.RequiresNotNull(target, "target"); + + return DeleteMember( + name, + target, + Ast.Constant(null, typeof(CodeContext)) + ); + } + + public MetaObject DeleteMember(string name, MetaObject target, Expression codeContext) { + ContractUtils.RequiresNotNull(name, "name"); + ContractUtils.RequiresNotNull(target, "target"); + + return MakeDeleteMemberTarget( + new SetOrDeleteMemberInfo( + name, + codeContext + ), + target.Restrict(target.LimitType) + ); + } + + private MetaObject MakeDeleteMemberTarget(SetOrDeleteMemberInfo delInfo, MetaObject target) { + Type type = target.LimitType; + Restrictions restrictions = target.Restrictions; + Expression self = target.Expression; + + // needed for DeleteMember call until DynamicAction goes away + OldDynamicAction act = OldDeleteMemberAction.Make( + this, + delInfo.Name + ); + + if (typeof(TypeTracker).IsAssignableFrom(type)) { + restrictions = restrictions.Merge( + Restrictions.GetInstanceRestriction(target.Expression, target.Value) + ); + + type = ((TypeTracker)target.Value).Type; + self = null; + } + + delInfo.Body.Restrictions = restrictions; + + if (self == null || !MakeOperatorDeleteMemberBody(delInfo, self, type, "DeleteMember")) { + MemberGroup group = GetMember(act, type, delInfo.Name); + if (group.Count != 0) { + if (group[0].MemberType == TrackerTypes.Property) { + MethodInfo del = ((PropertyTracker)group[0]).GetDeleteMethod(PrivateBinding); + if (del != null) { + MakePropertyDeleteStatement(delInfo, self, del); + return delInfo.Body.GetMetaObject(target); + } + } + + delInfo.Body.FinishCondition(MakeError(MakeUndeletableMemberError(GetDeclaringMemberType(group), delInfo.Name))); + } else { + delInfo.Body.FinishCondition(MakeError(MakeMissingMemberError(type, delInfo.Name))); + } + } + + return delInfo.Body.GetMetaObject(target); + } + + private static Type GetDeclaringMemberType(MemberGroup group) { + Type t = typeof(object); + foreach (MemberTracker mt in group) { + if (t.IsAssignableFrom(mt.DeclaringType)) { + t = mt.DeclaringType; + } + } + return t; + } + + private void MakePropertyDeleteStatement(SetOrDeleteMemberInfo delInfo, Expression instance, MethodInfo delete) { + delInfo.Body.FinishCondition( + MakeCallExpression(delInfo.CodeContext, delete, instance) + ); + } + + /// if a member-injector is defined-on or registered-for this type call it + private bool MakeOperatorDeleteMemberBody(SetOrDeleteMemberInfo delInfo, Expression instance, Type type, string name) { + MethodInfo delMem = GetMethod(type, name); + + if (delMem != null && delMem.IsSpecialName) { + Expression call = MakeCallExpression(delInfo.CodeContext, delMem, instance, Ast.Constant(delInfo.Name)); + + if (delMem.ReturnType == typeof(bool)) { + delInfo.Body.AddCondition( + call, + Ast.Constant(null) + ); + } else { + delInfo.Body.FinishCondition(call); + } + + return delMem.ReturnType != typeof(bool); + } + return false; + } + + /// + /// Helper class for flowing information about the GetMember request. + /// + private sealed class SetOrDeleteMemberInfo { + public readonly string Name; + public readonly Expression CodeContext; + public readonly ConditionalBuilder Body = new ConditionalBuilder(); + + public SetOrDeleteMemberInfo(string name, Expression codeContext) { + Name = name; + CodeContext = codeContext; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.GetMember.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.GetMember.cs new file mode 100644 index 0000000000..0abdfc6b0f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.GetMember.cs @@ -0,0 +1,324 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Dynamic.Binders; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public partial class DefaultBinder : ActionBinder { + + /// + /// Builds a MetaObject for performing a member get. Supports all built-in .NET members, the OperatorMethod + /// GetBoundMember, and StrongBox instances. + /// + /// + /// The name of the member to retrieve. This name is not processed by the DefaultBinder and + /// is instead handed off to the GetMember API which can do name mangling, case insensitive lookups, etc... + /// + /// + /// The MetaObject from which the member is retrieved. + /// + public MetaObject GetMember(string name, MetaObject target) { + return GetMember(name, target, Ast.Constant(null, typeof(CodeContext))); + } + + /// + /// Builds a MetaObject for performing a member get. Supports all built-in .NET members, the OperatorMethod + /// GetBoundMember, and StrongBox instances. + /// + /// + /// The name of the member to retrieve. This name is not processed by the DefaultBinder and + /// is instead handed off to the GetMember API which can do name mangling, case insensitive lookups, etc... + /// + /// + /// The MetaObject from which the member is retrieved. + /// + /// + /// An expression which provides access to the CodeContext if its required for + /// accessing the member (e.g. for an extension property which takes CodeContext). By default this + /// a null CodeContext object is passed. + /// + public MetaObject GetMember(string name, MetaObject target, Expression codeContext) { + return GetMember( + name, + target, + codeContext, + false + ); + } + + /// + /// Builds a MetaObject for performing a member get. Supports all built-in .NET members, the OperatorMethod + /// GetBoundMember, and StrongBox instances. + /// + /// + /// The name of the member to retrieve. This name is not processed by the DefaultBinder and + /// is instead handed off to the GetMember API which can do name mangling, case insensitive lookups, etc... + /// + /// + /// The MetaObject from which the member is retrieved. + /// + /// + /// An expression which provides access to the CodeContext if its required for + /// accessing the member (e.g. for an extension property which takes CodeContext). By default this + /// a null CodeContext object is passed. + /// + /// + /// True if the operation should return Operation.Failed on failure, false if it + /// should return the exception produced by MakeMissingMemberError. + /// + public MetaObject GetMember(string name, MetaObject target, Expression codeContext, bool isNoThrow) { + ContractUtils.RequiresNotNull(name, "name"); + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNull(codeContext, "codeContext"); + + return MakeGetMemberTarget( + new GetMemberInfo( + name, + codeContext, + isNoThrow + ), + target + ); + } + + private MetaObject MakeGetMemberTarget(GetMemberInfo getMemInfo, MetaObject target) { + Type type = target.LimitType.IsCOMObject ? target.Expression.Type : target.LimitType; + Restrictions restrictions = target.Restrictions; + Expression self = target.Expression; + target = target.Restrict(target.LimitType); + + // needed for GetMember call until DynamicAction goes away + OldDynamicAction act = OldGetMemberAction.Make( + this, + getMemInfo.Name + ); + + // Specially recognized types: TypeTracker, NamespaceTracker, and StrongBox. + // TODO: TypeTracker and NamespaceTracker should technically be IDO's. + MemberGroup members = MemberGroup.EmptyGroup; + if (typeof(TypeTracker).IsAssignableFrom(type)) { + restrictions = restrictions.Merge( + Restrictions.GetInstanceRestriction(target.Expression, target.Value) + ); + + TypeGroup tg = target.Value as TypeGroup; + Type nonGen; + if (tg == null || tg.TryGetNonGenericType(out nonGen)) { + members = GetMember(act, ((TypeTracker)target.Value).Type, getMemInfo.Name); + if (members.Count > 0) { + // we have a member that's on the type associated w/ the tracker, return that... + type = ((TypeTracker)target.Value).Type; + self = null; + } + } + } + + if (members.Count == 0) { + // Get the members + members = GetMember(act, type, getMemInfo.Name); + } + + if (members.Count == 0) { + if (typeof(TypeTracker).IsAssignableFrom(type)) { + // ensure we don't have a non-generic type, and if we do report an error now. This matches + // the rule version of the default binder but should probably be removed long term + Type x = ((TypeTracker)target.Value).Type; + } else if (type.IsInterface) { + // all interfaces have object members + type = typeof(object); + members = GetMember(act, type, getMemInfo.Name); + } + } + + Expression propSelf = self; + // if lookup failed try the strong-box type if available. + if (members.Count == 0 && typeof(IStrongBox).IsAssignableFrom(type)) { + // properties/fields need the direct value, methods hold onto the strong box. + propSelf = Ast.Field(AstUtils.Convert(self, type), type.GetField("Value")); + + type = type.GetGenericArguments()[0]; + + members = GetMember( + act, + type, + getMemInfo.Name + ); + } + + MakeBodyHelper(getMemInfo, self, propSelf, type, members); + + getMemInfo.Body.Restrictions = restrictions; + return getMemInfo.Body.GetMetaObject(target); + } + + private void MakeBodyHelper(GetMemberInfo getMemInfo, Expression self, Expression propSelf, Type type, MemberGroup members) { + if (self != null) { + MakeOperatorGetMemberBody(getMemInfo, propSelf, type, "GetCustomMember"); + } + + Expression error; + TrackerTypes memberType = GetMemberType(members, out error); + + if (error == null) { + MakeSuccessfulMemberAccess(getMemInfo, self, propSelf, type, members, memberType); + } else { + getMemInfo.Body.FinishCondition(error); + } + } + + private void MakeSuccessfulMemberAccess(GetMemberInfo getMemInfo, Expression self, Expression propSelf, Type type, MemberGroup members, TrackerTypes memberType) { + switch (memberType) { + case TrackerTypes.TypeGroup: + case TrackerTypes.Type: + MakeTypeBody(getMemInfo, type, members); + break; + case TrackerTypes.Method: + // turn into a MethodGroup + MakeGenericBodyWorker(getMemInfo, type, ReflectionCache.GetMethodGroup(getMemInfo.Name, members), self); + break; + case TrackerTypes.Event: + case TrackerTypes.Field: + case TrackerTypes.Property: + case TrackerTypes.Constructor: + case TrackerTypes.Custom: + MakeGenericBody(getMemInfo, type, members, propSelf); + break; + case TrackerTypes.All: + // no members were found + if (self != null) { + MakeOperatorGetMemberBody(getMemInfo, propSelf, type, "GetBoundMember"); + } + + MakeMissingMemberRuleForGet(getMemInfo, type); + break; + default: + throw new InvalidOperationException(memberType.ToString()); + } + } + + private void MakeGenericBody(GetMemberInfo getMemInfo, Type type, MemberGroup members, Expression instance) { + MemberTracker bestMember = members[0]; + if (members.Count > 1) { + // if we were given multiple members pick the member closest to the type... + Type bestMemberDeclaringType = members[0].DeclaringType; + + for (int i = 1; i < members.Count; i++) { + MemberTracker mt = members[i]; + if (!IsTrackerApplicableForType(type, mt)) { + continue; + } + + if (members[i].DeclaringType.IsSubclassOf(bestMemberDeclaringType) || + !IsTrackerApplicableForType(type, bestMember)) { + bestMember = members[i]; + bestMemberDeclaringType = members[i].DeclaringType; + } + } + } + + MakeGenericBodyWorker(getMemInfo, type, bestMember, instance); + } + + private static bool IsTrackerApplicableForType(Type type, MemberTracker mt) { + return mt.DeclaringType == type || type.IsSubclassOf(mt.DeclaringType); + } + + private void MakeTypeBody(GetMemberInfo getMemInfo, Type type, MemberGroup members) { + TypeTracker typeTracker = (TypeTracker)members[0]; + for (int i = 1; i < members.Count; i++) { + typeTracker = TypeGroup.UpdateTypeEntity(typeTracker, (TypeTracker)members[i]); + } + + getMemInfo.Body.FinishCondition(typeTracker.GetValue(getMemInfo.CodeContext, this, type)); + } + + private void MakeGenericBodyWorker(GetMemberInfo getMemInfo, Type type, MemberTracker tracker, Expression instance) { + if (instance != null) { + tracker = tracker.BindToInstance(instance); + } + + Expression val = tracker.GetValue(getMemInfo.CodeContext, this, type); + + getMemInfo.Body.FinishCondition( + val != null ? + val : + MakeError(tracker.GetError(this)) + ); + } + + /// if a member-injector is defined-on or registered-for this type call it + private void MakeOperatorGetMemberBody(GetMemberInfo getMemInfo, Expression instance, Type type, string name) { + MethodInfo getMem = GetMethod(type, name); + if (getMem != null && getMem.IsSpecialName) { + ParameterExpression tmp = Ast.Variable(typeof(object), "getVal"); + getMemInfo.Body.AddVariable(tmp); + + getMemInfo.Body.AddCondition( + Ast.NotEqual( + Ast.Assign( + tmp, + MakeCallExpression( + getMemInfo.CodeContext, + getMem, + AstUtils.Convert(instance, type), + Ast.Constant(getMemInfo.Name) + ) + ), + Ast.Field(null, typeof(OperationFailed).GetField("Value")) + ), + tmp + ); + } + } + + private void MakeMissingMemberRuleForGet(GetMemberInfo getMemInfo, Type type) { + if (getMemInfo.IsNoThrow) { + getMemInfo.Body.FinishCondition( + Ast.Field(null, typeof(OperationFailed).GetField("Value")) + ); + } else { + getMemInfo.Body.FinishCondition( + MakeError(MakeMissingMemberError(type, getMemInfo.Name)) + ); + } + } + + + /// + /// Helper class for flowing information about the GetMember request. + /// + private sealed class GetMemberInfo { + public readonly string Name; + public readonly Expression CodeContext; + public readonly bool IsNoThrow; + public readonly ConditionalBuilder Body = new ConditionalBuilder(); + + public GetMemberInfo(string name, Expression codeContext, bool noThrow) { + Name = name; + CodeContext = codeContext; + IsNoThrow = noThrow; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.Invoke.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.Invoke.cs new file mode 100644 index 0000000000..b247abc1b4 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.Invoke.cs @@ -0,0 +1,293 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic; +using System.Dynamic.Binders; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Actions.Calls; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public partial class DefaultBinder : ActionBinder { + + /// + /// Provides default binding for performing a call on the specified meta objects. + /// + /// The signature describing the call + /// The object to be called + /// + /// Additional meta objects are the parameters for the call as specified by the CallSignature in the CallAction. + /// + /// A MetaObject representing the call or the error. + public MetaObject Call(CallSignature signature, MetaObject target, params MetaObject[] args) { + return Call(signature, new ParameterBinder(this), target, args); + } + + /// + /// Provides default binding for performing a call on the specified meta objects. + /// + /// The signature describing the call + /// The meta object to be called. + /// + /// Additional meta objects are the parameters for the call as specified by the CallSignature in the CallAction. + /// + /// ParameterBinder used to map arguments to parameters. + /// A MetaObject representing the call or the error. + public MetaObject Call(CallSignature signature, ParameterBinder parameterBinder, MetaObject target, params MetaObject[] args) { + ContractUtils.RequiresNotNullItems(args, "args"); + ContractUtils.RequiresNotNull(parameterBinder, "parameterBinder"); + + TargetInfo targetInfo = GetTargetInfo(signature, target, args); + + if (targetInfo != null) { + // we're calling a well-known MethodBase + return MakeMetaMethodCall(signature, parameterBinder, targetInfo); + } else { + // we can't call this object + return MakeCannotCallRule(target, target.LimitType); + } + } + + #region Method Call Rule + + private MetaObject MakeMetaMethodCall(CallSignature signature, ParameterBinder parameterBinder, TargetInfo targetInfo) { + Restrictions restrictions = Restrictions.Combine(targetInfo.Arguments).Merge(targetInfo.Restrictions); + if (targetInfo.Instance != null) { + restrictions = targetInfo.Instance.Restrictions.Merge(restrictions); + } + + if (targetInfo.Instance != null) { + return CallInstanceMethod( + parameterBinder, + targetInfo.Targets, + targetInfo.Instance, + targetInfo.Arguments, + signature, + restrictions + ); + } + + return CallMethod( + parameterBinder, + targetInfo.Targets, + targetInfo.Arguments, + signature, + restrictions); + } + + #endregion + + #region Target acquisition + + /// + /// Gets a TargetInfo object for performing a call on this object. + /// + /// If this object is a delegate we bind to the Invoke method. + /// If this object is a MemberGroup or MethodGroup we bind to the methods in the member group. + /// If this object is a BoundMemberTracker we bind to the methods with the bound instance. + /// If the underlying type has defined an operator Call method we'll bind to that method. + /// + private TargetInfo GetTargetInfo(CallSignature signature, MetaObject target, MetaObject[] args) { + Debug.Assert(target.HasValue); + object objTarget = target.Value; + + return + TryGetDelegateTargets(target, args, objTarget as Delegate) ?? + TryGetMemberGroupTargets(target, args, objTarget as MemberGroup) ?? + TryGetMethodGroupTargets(target, args, objTarget as MethodGroup) ?? + TryGetBoundMemberTargets(target, args, objTarget as BoundMemberTracker) ?? + TryGetOperatorTargets(target, args, target, signature); + } + + /// + /// Binds to the methods in a method group. + /// + private static TargetInfo TryGetMethodGroupTargets(MetaObject target, MetaObject[] args, MethodGroup mthgrp) { + if (mthgrp != null) { + List foundTargets = new List(); + + foreach (MethodTracker mt in mthgrp.Methods) { + foundTargets.Add(mt.Method); + } + + return new TargetInfo(null, ArrayUtils.Insert(target, args), Restrictions.GetInstanceRestriction(target.Expression, mthgrp), foundTargets.ToArray()); + } + return null; + } + + /// + /// Binds to the methods in a member group. + /// + /// TODO: We should really only have either MemberGroup or MethodGroup, not both. + /// + private static TargetInfo TryGetMemberGroupTargets(MetaObject target, MetaObject[] args, MemberGroup mg) { + if (mg != null) { + MethodBase[] targets; + List foundTargets = new List(); + foreach (MemberTracker mt in mg) { + if (mt.MemberType == TrackerTypes.Method) { + foundTargets.Add(((MethodTracker)mt).Method); + } + } + targets = foundTargets.ToArray(); + return new TargetInfo(null, ArrayUtils.Insert(target, args), targets); + } + return null; + } + + /// + /// Binds to the BoundMemberTracker and uses the instance in the tracker and restricts + /// based upon the object instance type. + /// + private TargetInfo TryGetBoundMemberTargets(MetaObject self, MetaObject[] args, BoundMemberTracker bmt) { + if (bmt != null) { + Debug.Assert(bmt.Instance == null); // should be null for trackers that leak to user code + + MethodBase[] targets; + + // instance is pulled from the BoundMemberTracker and restricted to the correct + // type. + MetaObject instance = new MetaObject( + Ast.Convert( + Ast.Property( + Ast.Convert(self.Expression, typeof(BoundMemberTracker)), + typeof(BoundMemberTracker).GetProperty("ObjectInstance") + ), + bmt.BoundTo.DeclaringType + ), + self.Restrictions + ).Restrict(CompilerHelpers.GetType(bmt.ObjectInstance)); + + // we also add a restriction to make sure we're going to the same BoundMemberTracker + Restrictions restrictions = Restrictions.GetExpressionRestriction( + Ast.Equal( + Ast.Property( + Ast.Convert(self.Expression, typeof(BoundMemberTracker)), + typeof(BoundMemberTracker).GetProperty("BoundTo") + ), + Ast.Constant(bmt.BoundTo) + ) + ); + + switch (bmt.BoundTo.MemberType) { + case TrackerTypes.MethodGroup: + targets = ((MethodGroup)bmt.BoundTo).GetMethodBases(); + break; + case TrackerTypes.Method: + targets = new MethodBase[] { ((MethodTracker)bmt.BoundTo).Method }; + break; + default: + throw new InvalidOperationException(); // nothing else binds yet + } + + return new TargetInfo(instance, args, restrictions, targets); + } + return null; + } + + /// + /// Binds to the Invoke method on a delegate if this is a delegate type. + /// + private static TargetInfo TryGetDelegateTargets(MetaObject target, MetaObject[] args, Delegate d) { + if (d != null) { + return new TargetInfo(target, args, d.GetType().GetMethod("Invoke")); + } + return null; + } + + /// + /// Attempts to bind to an operator Call method. + /// + private TargetInfo TryGetOperatorTargets(MetaObject self, MetaObject[] args, object target, CallSignature signature) { + MethodBase[] targets; + + Type targetType = CompilerHelpers.GetType(target); + + MemberGroup callMembers = GetMember(OldCallAction.Make(this, signature), targetType, "Call"); + List callTargets = new List(); + foreach (MemberTracker mi in callMembers) { + if (mi.MemberType == TrackerTypes.Method) { + MethodInfo method = ((MethodTracker)mi).Method; + if (method.IsSpecialName) { + callTargets.Add(method); + } + } + } + + Expression instance = null; + if (callTargets.Count > 0) { + targets = callTargets.ToArray(); + instance = Ast.Convert(self.Expression, CompilerHelpers.GetType(target)); + return new TargetInfo(null, ArrayUtils.Insert(self, args), targets); + } + + return null; + } + + #endregion + + #region Error support + + private MetaObject MakeCannotCallRule(MetaObject self, Type type) { + return MakeError( + ErrorInfo.FromException( + Ast.New( + typeof(ArgumentTypeException).GetConstructor(new Type[] { typeof(string) }), + Ast.Constant(type.Name + " is not callable") + ) + ), + self.Restrictions.Merge(Restrictions.GetTypeRestriction(self.Expression, type)) + ); + } + + + #endregion + + + /// + /// Encapsulates information about the target of the call. This includes an implicit instance for the call, + /// the methods that we'll be calling as well as any restrictions required to perform the call. + /// + class TargetInfo { + public readonly MetaObject Instance; + public readonly MetaObject[] Arguments; + public readonly MethodBase[] Targets; + public readonly Restrictions Restrictions; + + public TargetInfo(MetaObject instance, MetaObject[] arguments, params MethodBase[] args) : + this(instance, arguments, Restrictions.Empty, args) { + } + + public TargetInfo(MetaObject instance, MetaObject[] arguments, Restrictions restrictions, params MethodBase[] targets) { + Assert.NotNullItems(targets); + Assert.NotNull(restrictions); + + Instance = instance; + Arguments = arguments; + Targets = targets; + Restrictions = restrictions; + } + } + + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.MethodCalls.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.MethodCalls.cs new file mode 100644 index 0000000000..379ff5b18a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.MethodCalls.cs @@ -0,0 +1,585 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic.Binders; +using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public partial class DefaultBinder : ActionBinder { + + #region Public APIs + + /// + /// Performs binding against a set of overloaded methods using the specified arguments. All arguments + /// are treated as positional arguments. + /// + /// ParameterBinder used to map arguments to parameters. + /// The methods to be called + /// The arguments for the call + /// A meta object which results from the call. + public MetaObject CallMethod(ParameterBinder parameterBinder, IList targets, IList args) { + return CallMethod( + parameterBinder, + targets, + args, + new CallSignature(args.Count), + Restrictions.Empty + ); + } + + /// + /// Performs binding against a set of overloaded methods using the specified arguments. All arguments + /// are treated as positional arguments. + /// + /// ParameterBinder used to map arguments to parameters. + /// The methods to be called + /// The arguments for the call + /// The maximum narrowing level for arguments. The current narrowing level is flowed thorugh to the DefaultBinder. + /// The minimum narrowing level for the arguments. The current narrowing level is flowed thorugh to the DefaultBinder. + /// A meta object which results from the call. + public MetaObject CallMethod(ParameterBinder parameterBinder, IList targets, IList args, NarrowingLevel minLevel, NarrowingLevel maxLevel) { + return CallWorker( + parameterBinder, + targets, + args, + new CallSignature(args.Count), + CallTypes.None, + Restrictions.Empty, + minLevel, + maxLevel, + null + ); + } + + /// + /// Performs binding against a set of overloaded methods using the specified arguments. The arguments are + /// consumed as specified by the CallSignature object. + /// + /// ParameterBinder used to map arguments to parameters. + /// The methods to be called + /// The arguments for the call + /// The call signature which specified how the arguments will be consumed + /// A meta object which results from the call. + public MetaObject CallMethod(ParameterBinder parameterBinder, IList targets, IList args, CallSignature signature) { + return CallMethod(parameterBinder, targets, args, signature, Restrictions.Empty); + } + + /// + /// Performs binding against a set of overloaded methods using the specified arguments. The arguments are + /// consumed as specified by the CallSignature object. + /// + /// ParameterBinder used to map arguments to parameters. + /// The methods to be called + /// The arguments for the call + /// The call signature which specified how the arguments will be consumed + /// The name of the method or null to use the name from targets. + /// A meta object which results from the call. + public MetaObject CallMethod(ParameterBinder parameterBinder, IList targets, IList args, CallSignature signature, string name) { + return CallMethod(parameterBinder, targets, args, signature, Restrictions.Empty, name); + } + + /// + /// Performs binding against a set of overloaded methods using the specified arguments. The arguments are + /// consumed as specified by the CallSignature object. + /// + /// ParameterBinder used to map arguments to parameters. + /// The methods to be called + /// The arguments for the call + /// The call signature which specified how the arguments will be consumed + /// Additional restrictions which should be applied to the resulting MetaObject. + /// A meta object which results from the call. + public MetaObject CallMethod(ParameterBinder parameterBinder, IList targets, IList args, CallSignature signature, Restrictions restrictions) { + return CallWorker( + parameterBinder, + targets, + args, + signature, + CallTypes.None, + restrictions, + NarrowingLevel.None, + NarrowingLevel.All, + null + ); + } + + /// + /// Performs binding against a set of overloaded methods using the specified arguments. The arguments are + /// consumed as specified by the CallSignature object. + /// + /// ParameterBinder used to map arguments to parameters. + /// The methods to be called + /// The arguments for the call + /// The call signature which specified how the arguments will be consumed + /// Additional restrictions which should be applied to the resulting MetaObject. + /// The name of the method or null to use the name from targets. + /// A meta object which results from the call. + public MetaObject CallMethod(ParameterBinder parameterBinder, IList targets, IList args, CallSignature signature, Restrictions restrictions, string name) { + return CallWorker( + parameterBinder, + targets, + args, + signature, + CallTypes.None, + restrictions, + NarrowingLevel.None, + NarrowingLevel.All, + name + ); + } + + /// + /// Performs binding against a set of overloaded methods using the specified arguments and the specified + /// instance argument. The arguments are consumed as specified by the CallSignature object. + /// + /// ParameterBinder used to map arguments to parameters. + /// The methods to be called + /// The arguments for the call + /// The instance which will be provided for dispatching to an instance method. + /// The call signature which specified how the arguments will be consumed + /// Additional restrictions which should be applied to the resulting MetaObject. + /// A meta object which results from the call. + public MetaObject CallInstanceMethod(ParameterBinder parameterBinder, IList targets, MetaObject instance, IList args, CallSignature signature, Restrictions restrictions) { + ContractUtils.RequiresNotNull(instance, "instance"); + ContractUtils.RequiresNotNull(parameterBinder, "parameterBinder"); + + return CallWorker( + parameterBinder, + targets, + ArrayUtils.Insert(instance, args), + signature, + CallTypes.ImplicitInstance, + restrictions, + NarrowingLevel.None, + NarrowingLevel.All, + null + ); + } + + /// + /// Performs binding against a set of overloaded methods using the specified arguments. The arguments are + /// consumed as specified by the CallSignature object. + /// + /// ParameterBinder used to map arguments to parameters. + /// The methods to be called + /// The arguments for the call + /// The call signature which specified how the arguments will be consumed + /// Additional restrictions which should be applied to the resulting MetaObject. + /// The maximum narrowing level for arguments. The current narrowing level is flowed thorugh to the DefaultBinder. + /// The minimum narrowing level for the arguments. The current narrowing level is flowed thorugh to the DefaultBinder. + /// The resulting binding target which can be used for producing error information. + /// A meta object which results from the call. + public MetaObject CallMethod(ParameterBinder parameterBinder, IList targets, IList args, CallSignature signature, Restrictions restrictions, NarrowingLevel minLevel, NarrowingLevel maxLevel, out BindingTarget target) { + return CallWorker( + parameterBinder, + targets, + args, + signature, + CallTypes.None, + restrictions, + minLevel, + maxLevel, + null, + out target + ); + } + + /// + /// Performs binding against a set of overloaded methods using the specified arguments. The arguments are + /// consumed as specified by the CallSignature object. + /// + /// ParameterBinder used to map arguments to parameters. + /// The methods to be called + /// The arguments for the call + /// The call signature which specified how the arguments will be consumed + /// Additional restrictions which should be applied to the resulting MetaObject. + /// The maximum narrowing level for arguments. The current narrowing level is flowed thorugh to the DefaultBinder. + /// The minimum narrowing level for the arguments. The current narrowing level is flowed thorugh to the DefaultBinder. + /// The resulting binding target which can be used for producing error information. + /// The name of the method or null to use the name from targets. + /// A meta object which results from the call. + public MetaObject CallMethod(ParameterBinder parameterBinder, IList targets, IList args, CallSignature signature, Restrictions restrictions, NarrowingLevel minLevel, NarrowingLevel maxLevel, string name, out BindingTarget target) { + return CallWorker( + parameterBinder, + targets, + args, + signature, + CallTypes.None, + restrictions, + minLevel, + maxLevel, + name, + out target + ); + } + + /// + /// Performs binding against a set of overloaded methods using the specified arguments and the specified + /// instance argument. The arguments are consumed as specified by the CallSignature object. + /// + /// ParameterBinder used to map arguments to parameters. + /// The methods to be called + /// The arguments for the call + /// The call signature which specified how the arguments will be consumed + /// Additional restrictions which should be applied to the resulting MetaObject. + /// The instance which will be provided for dispatching to an instance method. + /// The maximum narrowing level for arguments. The current narrowing level is flowed thorugh to the DefaultBinder. + /// The minimum narrowing level for the arguments. The current narrowing level is flowed thorugh to the DefaultBinder. + /// The resulting binding target which can be used for producing error information. + /// A meta object which results from the call. + public MetaObject CallInstanceMethod(ParameterBinder parameterBinder, IList targets, MetaObject instance, IList args, CallSignature signature, Restrictions restrictions, NarrowingLevel minLevel, NarrowingLevel maxLevel, out BindingTarget target) { + return CallWorker( + parameterBinder, + targets, + ArrayUtils.Insert(instance, args), + signature, + CallTypes.ImplicitInstance, + restrictions, + minLevel, + maxLevel, + null, + out target + ); + } + + /// + /// Performs binding against a set of overloaded methods using the specified arguments and the specified + /// instance argument. The arguments are consumed as specified by the CallSignature object. + /// + /// ParameterBinder used to map arguments to parameters. + /// The methods to be called + /// The arguments for the call + /// The call signature which specified how the arguments will be consumed + /// Additional restrictions which should be applied to the resulting MetaObject. + /// The instance which will be provided for dispatching to an instance method. + /// The maximum narrowing level for arguments. The current narrowing level is flowed thorugh to the DefaultBinder. + /// The minimum narrowing level for the arguments. The current narrowing level is flowed thorugh to the DefaultBinder. + /// The resulting binding target which can be used for producing error information. + /// The name of the method or null to use the name from targets. + /// A meta object which results from the call. + public MetaObject CallInstanceMethod(ParameterBinder parameterBinder, IList targets, MetaObject instance, IList args, CallSignature signature, Restrictions restrictions, NarrowingLevel minLevel, NarrowingLevel maxLevel, string name, out BindingTarget target) { + return CallWorker( + parameterBinder, + targets, + ArrayUtils.Insert(instance, args), + signature, + CallTypes.ImplicitInstance, + restrictions, + minLevel, + maxLevel, + name, + out target + ); + } + + private MetaObject CallWorker(ParameterBinder parameterBinder, IList targets, IList args, CallSignature signature, CallTypes callType, Restrictions restrictions, NarrowingLevel minLevel, NarrowingLevel maxLevel, string name) { + BindingTarget dummy; + return CallWorker(parameterBinder, targets, args, signature, callType, restrictions, minLevel, maxLevel, name, out dummy); + } + + private MetaObject CallWorker(ParameterBinder parameterBinder, IList targets, IList args, CallSignature signature, CallTypes callType, Restrictions restrictions, NarrowingLevel minLevel, NarrowingLevel maxLevel, string name, out BindingTarget target) { + ContractUtils.RequiresNotNull(parameterBinder, "parameterBinder"); + ContractUtils.RequiresNotNullItems(args, "args"); + ContractUtils.RequiresNotNullItems(targets, "targets"); + ContractUtils.RequiresNotNull(restrictions, "restrictions"); + + MetaObject[] finalArgs; + SymbolId[] argNames; + + if (callType == CallTypes.ImplicitInstance) { + GetArgumentNamesAndTypes(signature, ArrayUtils.RemoveFirst(args), out argNames, out finalArgs); + finalArgs = ArrayUtils.Insert(args[0], finalArgs); + } else { + GetArgumentNamesAndTypes(signature, args, out argNames, out finalArgs); + } + + // attempt to bind to an individual method + MethodBinder binder = MethodBinder.MakeBinder( + this, + name ?? GetTargetName(targets), + targets, + argNames, + minLevel, + maxLevel); + target = binder.MakeBindingTarget(callType, finalArgs); + + if (target.Success) { + // if we succeed make the target for the rule + return new MetaObject( + target.MakeExpression(parameterBinder), + restrictions.Merge(MakeSplatTests(callType, signature, args).Merge(Restrictions.Combine(target.RestrictedArguments))) + ); + } + // make an error rule + return MakeInvalidParametersRule(callType, signature, this, args, restrictions, target); + } + + #endregion + + #region Restriction helpers + + private static Restrictions MakeSplatTests(CallTypes callType, CallSignature signature, IList args) { + return MakeSplatTests(callType, signature, false, args); + } + + /// + /// Makes test for param arrays and param dictionary parameters. + /// + private static Restrictions MakeSplatTests(CallTypes callType, CallSignature signature, bool testTypes, IList args) { + Restrictions res = Restrictions.Empty; + + if (signature.HasListArgument()) { + res = MakeParamsArrayTest(callType, signature, testTypes, args); + } + + if (signature.HasDictionaryArgument()) { + res = res.Merge(MakeParamsDictionaryTest(args, testTypes)); + } + + return res; + } + + /// + /// Pulls out the right argument to build the splat test. MakeParamsTest makes the actual test. + /// + private static Restrictions MakeParamsArrayTest(CallTypes callType, CallSignature signature, bool testTypes, IList args) { + int listIndex = signature.IndexOf(ArgumentType.List); + Debug.Assert(listIndex != -1); + if (callType == CallTypes.ImplicitInstance) { + listIndex++; + } + + return MakeParamsTest(args[listIndex].Value, args[listIndex].Expression, testTypes); + } + + /// + /// Builds the restrictions for calling with a splatted argument array. Ensures that the + /// argument is still an ICollection of object and that it has the same number of arguments. + /// + private static Restrictions MakeParamsTest(object paramArg, Expression listArg, bool testTypes) { + IList coll = (IList)paramArg; + + Restrictions res = Restrictions.GetExpressionRestriction( + Ast.AndAlso( + Ast.TypeIs(listArg, typeof(IList)), + Ast.Equal( + Ast.Property( + Ast.Convert(listArg, typeof(IList)), + typeof(ICollection).GetProperty("Count") + ), + Ast.Constant(coll.Count) + ) + ) + ); + + if (testTypes) { + for (int i = 0; i < coll.Count; i++) { + res = res.Merge( + Restrictions.GetTypeRestriction( + Ast.Call( + AstUtils.Convert( + listArg, + typeof(IList) + ), + typeof(IList).GetMethod("get_Item"), + Ast.Constant(i) + ), + CompilerHelpers.GetType(coll[i]) + ) + ); + } + } + + return res; + } + + /// + /// Builds the restrictions for calling with keyword arguments. The restrictions include + /// tests on the individual keys of the dictionary to ensure they have the same names. + /// + private static Restrictions MakeParamsDictionaryTest(IList args, bool testTypes) { + IDictionary dict = (IDictionary)args[args.Count - 1].Value; + IDictionaryEnumerator dictEnum = dict.GetEnumerator(); + + // verify the dictionary has the same count and arguments. + + string[] names = new string[dict.Count]; + Type[] types = testTypes ? new Type[dict.Count] : null; + int index = 0; + while (dictEnum.MoveNext()) { + string name = dictEnum.Entry.Key as string; + if (name == null) { + throw ScriptingRuntimeHelpers.SimpleTypeError(String.Format("expected string for dictionary argument got {0}", dictEnum.Entry.Key)); + } + names[index] = name; + if (types != null) { + types[index] = CompilerHelpers.GetType(dictEnum.Entry.Value); + } + index++; + } + + return Restrictions.GetExpressionRestriction( + Ast.AndAlso( + Ast.TypeIs(args[args.Count - 1].Expression, typeof(IDictionary)), + Ast.Call( + typeof(BinderOps).GetMethod("CheckDictionaryMembers"), + Ast.Convert(args[args.Count - 1].Expression, typeof(IDictionary)), + Ast.Constant(names), + testTypes ? Ast.Constant(types) : Ast.Constant(null, typeof(Type[])) + ) + ) + ); + } + + #endregion + + #region Misc. Helpers + + /// + /// Gets all of the argument names and types. The instance argument is not included + /// + /// The names correspond to the end of argTypes. + /// ArgumentKind.Dictionary is unpacked in the return value. + /// This is set to an array of size 0 if there are no keyword arguments + /// Non named arguments are returned at the beginning. + /// ArgumentKind.List is unpacked in the return value. + /// The MetaObject array which has the arguments for the call + /// The signature we're building the call for + private static void GetArgumentNamesAndTypes(CallSignature signature, IList args, out SymbolId[] argNames, out MetaObject[] resultingArgs) { + // Get names of named arguments + argNames = signature.GetArgumentNames(); + + resultingArgs = GetArgumentTypes(signature, args); + + if (signature.HasDictionaryArgument()) { + // need to get names from dictionary argument... + GetDictionaryNamesAndTypes(args, ref argNames, ref resultingArgs); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] // TODO: fix + private static MetaObject[] GetArgumentTypes(CallSignature signature, IList args) { + List res = new List(); + List namedObjects = null; + for (int i = 0; i < args.Count; i++) { + switch (signature.GetArgumentKind(i)) { + case ArgumentType.Named: + if (namedObjects == null) { + namedObjects = new List(); + } + namedObjects.Add(args[i]); + break; + case ArgumentType.Simple: + case ArgumentType.Instance: + res.Add(args[i]); + break; + case ArgumentType.List: + IList list = args[i].Value as IList; + if (list == null) return null; + + for (int j = 0; j < list.Count; j++) { + res.Add( + new MetaObject( + Ast.Call( + Ast.Convert( + args[i].Expression, + typeof(IList) + ), + typeof(IList).GetMethod("get_Item"), + Ast.Constant(j) + ), + args[i].Restrictions, + list[j] + ) + ); + } + break; + case ArgumentType.Dictionary: + // caller needs to process these... + break; + default: + throw new NotImplementedException(); + } + } + + if (namedObjects != null) { + res.AddRange(namedObjects); + } + + return res.ToArray(); + } + + private static void GetDictionaryNamesAndTypes(IList args, ref SymbolId[] argNames, ref MetaObject[] argTypes) { + List names = new List(argNames); + List types = new List(argTypes); + + IDictionary dict = (IDictionary)args[args.Count - 1].Value; + MetaObject dictMo = args[args.Count - 1]; + IDictionaryEnumerator dictEnum = dict.GetEnumerator(); + while (dictEnum.MoveNext()) { + DictionaryEntry de = dictEnum.Entry; + + if (de.Key is string) { + names.Add(SymbolTable.StringToId((string)de.Key)); + types.Add( + new MetaObject( + Ast.Call( + AstUtils.Convert(dictMo.Expression, typeof(IDictionary)), + typeof(IDictionary).GetMethod("get_Item"), + Ast.Constant(de.Key as string) + ), + dictMo.Restrictions, + de.Value + ) + ); + } + } + + argNames = names.ToArray(); + argTypes = types.ToArray(); + } + + private static MetaObject MakeInvalidParametersRule(CallTypes callType, CallSignature signature, DefaultBinder binder, IList args, Restrictions restrictions, BindingTarget bt) { + Restrictions restriction = MakeSplatTests(callType, signature, true, args); + + // restrict to the exact type of all parameters for errors + for (int i = 0; i < args.Count; i++) { + args[i] = args[i].Restrict(args[i].LimitType); + } + + return MakeError( + binder.MakeInvalidParametersError(bt), + restrictions.Merge(Restrictions.Combine(args).Merge(restriction)) + ); + } + + private static string GetTargetName(IList targets) { + return targets[0].IsConstructor ? targets[0].DeclaringType.Name : targets[0].Name; + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.Operations.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.Operations.cs new file mode 100644 index 0000000000..445f91252f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.Operations.cs @@ -0,0 +1,740 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic; +using System.Dynamic.Binders; +using System.Text; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Actions.Calls; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public partial class DefaultBinder : ActionBinder { + public MetaObject DoOperation(string operation, params MetaObject[] args) { + return DoOperation(operation, Ast.Constant(null, typeof(CodeContext)), args); + } + + public MetaObject DoOperation(string operation, Expression codeContext, params MetaObject[] args) { + ContractUtils.RequiresNotNull(operation, "operation"); + ContractUtils.RequiresNotNull(codeContext, "codeContext"); + ContractUtils.RequiresNotNullItems(args, "args"); + + return + MakeDefaultMemberRule(operation, args) ?? // see if we have a default member and we're doing indexing + MakeGeneralOperatorRule(operation, codeContext, args); // Then try comparison / other operators + } + + /// + /// Creates the MetaObject for indexing directly into arrays or indexing into objects which have + /// default members. Returns null if we're not an indexing operation. + /// + private MetaObject MakeDefaultMemberRule(string oper, MetaObject[] args) { + if (oper == StandardOperators.GetItem || oper == StandardOperators.SetItem) { + if (args[0].LimitType.IsArray) { + return MakeArrayIndexRule(oper, args); + } + + return MakeMethodIndexRule(oper, args); + } + + return null; + } + + /// + /// Creates the meta object for the rest of the operations: comparisons and all other + /// operators. If the operation cannot be completed a MetaObject which indicates an + /// error will be returned. + /// + private MetaObject MakeGeneralOperatorRule(string operation, Expression codeContext, MetaObject[] args) { + OperatorInfo info = OperatorInfo.GetOperatorInfo(operation); + MetaObject res; + + if (CompilerHelpers.IsComparisonOperator(operation)) { + res = MakeComparisonRule(info, codeContext, args); + } else { + res = MakeOperatorRule(info, codeContext, args); + } + + return res; + } + + #region Comparison operator + + private MetaObject MakeComparisonRule(OperatorInfo info, Expression codeContext, MetaObject[] args) { + return + TryComparisonMethod(info, codeContext, args[0], args) ?? // check the first type if it has an applicable method + TryComparisonMethod(info, codeContext, args[0], args) ?? // then check the second type + TryNumericComparison(info, args) ?? // try Compare: cmp(x,y) (>, <, >=, <=, ==, !=) 0 + TryInvertedComparison(info, args[0], args) ?? // try inverting the operator & result (e.g. if looking for Equals try NotEquals, LessThan for GreaterThan)... + TryInvertedComparison(info, args[0], args) ?? // inverted binding on the 2nd type + TryNullComparisonRule(args) ?? // see if we're comparing to null w/ an object ref or a Nullable + TryPrimitiveCompare(info, args) ?? // see if this is a primitive type where we're comparing the two values. + MakeOperatorError(info, args); // no comparisons are possible + } + + private MetaObject TryComparisonMethod(OperatorInfo info, Expression codeContext, MetaObject target, MetaObject[] args) { + MethodInfo[] targets = GetApplicableMembers(target.LimitType, info); + if (targets.Length > 0) { + return TryMakeBindingTarget(targets, args, codeContext, Restrictions.Empty); + } + + return null; + } + + private static MetaObject MakeOperatorError(OperatorInfo info, MetaObject[] args) { + return new MetaObject( + Ast.Throw( + AstUtils.ComplexCallHelper( + typeof(BinderOps).GetMethod("BadArgumentsForOperation"), + ArrayUtils.Insert((Expression)Ast.Constant(info.Operator), MetaObject.GetExpressions(args)) + ) + ), + Restrictions.Combine(args) + ); + } + + private MetaObject TryNumericComparison(OperatorInfo info, MetaObject[] args) { + MethodInfo[] targets = FilterNonMethods( + args[0].LimitType, + GetMember(OldDoOperationAction.Make(this, info.Operator), + args[0].LimitType, + "Compare") + ); + + if (targets.Length > 0) { + MethodBinder mb = MethodBinder.MakeBinder(this, targets[0].Name, targets); + BindingTarget target = mb.MakeBindingTarget(CallTypes.None, args); + if (target.Success) { + Expression call = AstUtils.Convert(target.MakeExpression(), typeof(int)); + switch (info.Operator) { + case Operators.GreaterThan: call = Ast.GreaterThan(call, Ast.Constant(0)); break; + case Operators.LessThan: call = Ast.LessThan(call, Ast.Constant(0)); break; + case Operators.GreaterThanOrEqual: call = Ast.GreaterThanOrEqual(call, Ast.Constant(0)); break; + case Operators.LessThanOrEqual: call = Ast.LessThanOrEqual(call, Ast.Constant(0)); break; + case Operators.Equals: call = Ast.Equal(call, Ast.Constant(0)); break; + case Operators.NotEquals: call = Ast.NotEqual(call, Ast.Constant(0)); break; + case Operators.Compare: + break; + } + + return new MetaObject( + call, + Restrictions.Combine(target.RestrictedArguments) + ); + } + } + + return null; + } + + private MetaObject TryInvertedComparison(OperatorInfo info, MetaObject target, MetaObject[] args) { + Operators revOp = GetInvertedOperator(info.Operator); + OperatorInfo revInfo = OperatorInfo.GetOperatorInfo(revOp); + Debug.Assert(revInfo != null); + + // try the 1st type's opposite function result negated + MethodBase[] targets = GetApplicableMembers(target.LimitType, revInfo); + if (targets.Length > 0) { + return TryMakeInvertedBindingTarget(targets, args); + } + + return null; + } + + /// + /// Produces a rule for comparing a value to null - supports comparing object references and nullable types. + /// + private static MetaObject TryNullComparisonRule(MetaObject[] args) { + Type otherType = args[0].LimitType; + + Restrictions restrictions = Restrictions.GetTypeRestriction(args[0].Expression, args[0].LimitType).Merge(Restrictions.Combine(args)); + + if (args[0].LimitType == typeof(Null)) { + if (!otherType.IsValueType) { + return new MetaObject( + Ast.Equal(args[0].Expression, Ast.Constant(null)), + restrictions + ); + } else if (otherType.GetGenericTypeDefinition() == typeof(Nullable<>)) { + return new MetaObject( + Ast.Property(args[0].Expression, otherType.GetProperty("HasValue")), + restrictions + ); + } + } else if (otherType == typeof(Null)) { + if (!args[0].LimitType.IsValueType) { + return new MetaObject( + Ast.Equal(args[0].Expression, Ast.Constant(null)), + restrictions + ); + } else if (args[0].LimitType.GetGenericTypeDefinition() == typeof(Nullable<>)) { + return new MetaObject( + Ast.Property(args[0].Expression, otherType.GetProperty("HasValue")), + restrictions + ); + } + } + + return null; + } + + private static MetaObject TryPrimitiveCompare(OperatorInfo info, MetaObject[] args) { + if (TypeUtils.GetNonNullableType(args[0].LimitType) == TypeUtils.GetNonNullableType(args[1].LimitType) && + TypeUtils.IsNumeric(args[0].LimitType)) { + Expression arg0 = args[0].Expression; + Expression arg1 = args[1].Expression; + + // TODO: Nullable Support + Expression expr; + switch (info.Operator) { + case Operators.Equals: expr = Ast.Equal(arg0, arg1); break; + case Operators.NotEquals: expr = Ast.NotEqual(arg0, arg1); break; + case Operators.GreaterThan: expr = Ast.GreaterThan(arg0, arg1); break; + case Operators.LessThan: expr = Ast.LessThan(arg0, arg1); break; + case Operators.GreaterThanOrEqual: expr = Ast.GreaterThanOrEqual(arg0, arg1); break; + case Operators.LessThanOrEqual: expr = Ast.LessThanOrEqual(arg0, arg1); break; + default: throw new InvalidOperationException(); + } + + return new MetaObject( + expr, + Restrictions.GetTypeRestriction(arg0, args[0].LimitType).Merge(Restrictions.GetTypeRestriction(arg1, args[0].LimitType)).Merge(Restrictions.Combine(args)) + ); + } + + return null; + } + + #endregion + + #region Operator Rule + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] // TODO: fix + private MetaObject MakeOperatorRule(OperatorInfo info, Expression codeContext, MetaObject[] args) { + return + TryForwardOperator(info, codeContext, args) ?? + TryReverseOperator(info, codeContext, args) ?? + TryInplaceOperator(info, codeContext, args) ?? + TryPrimitiveOperator(info, args) ?? + TryMakeDefaultUnaryRule(info, codeContext, args) ?? + MakeOperatorError(info, args); + } + + private static MetaObject TryPrimitiveOperator(OperatorInfo info, MetaObject[] args) { + if (args.Length == 2 && + TypeUtils.GetNonNullableType(args[0].LimitType) == TypeUtils.GetNonNullableType(args[1].LimitType) && + TypeUtils.IsArithmetic(args[0].LimitType)) { + // TODO: Nullable Support + Expression expr; + MetaObject self = args[0].Restrict(args[0].LimitType); + MetaObject arg0 = args[1].Restrict(args[0].LimitType); + + switch (info.Operator) { + case Operators.Add: expr = Ast.Add(self.Expression, arg0.Expression); break; + case Operators.Subtract: expr = Ast.Subtract(self.Expression, arg0.Expression); break; + case Operators.Divide: expr = Ast.Divide(self.Expression, arg0.Expression); break; + case Operators.Mod: expr = Ast.Modulo(self.Expression, arg0.Expression); break; + case Operators.Multiply: expr = Ast.Multiply(self.Expression, arg0.Expression); break; + case Operators.LeftShift: expr = Ast.LeftShift(self.Expression, arg0.Expression); break; + case Operators.RightShift: expr = Ast.RightShift(self.Expression, arg0.Expression); break; + case Operators.BitwiseAnd: expr = Ast.And(self.Expression, arg0.Expression); break; + case Operators.BitwiseOr: expr = Ast.Or(self.Expression, arg0.Expression); break; + case Operators.ExclusiveOr: expr = Ast.ExclusiveOr(self.Expression, arg0.Expression); break; + default: throw new InvalidOperationException(); + } + + return new MetaObject( + expr, + self.Restrictions.Merge(arg0.Restrictions) + ); + } + + return null; + } + + private MetaObject TryForwardOperator(OperatorInfo info, Expression codeContext, MetaObject[] args) { + // we need a special conversion for the return type on MemberNames + if (info.Operator != Operators.MemberNames) { + MethodInfo[] targets = GetApplicableMembers(args[0].LimitType, info); + Restrictions restrictions = Restrictions.Empty; + if (targets.Length == 0) { + targets = GetFallbackMembers(args[0].LimitType, info, args, out restrictions); + } + + if (targets.Length > 0) { + return TryMakeBindingTarget(targets, args, codeContext, restrictions); + } + } + + return null; + } + + private MetaObject TryReverseOperator(OperatorInfo info, Expression codeContext, MetaObject[] args) { + // we need a special conversion for the return type on MemberNames + if (info.Operator != Operators.MemberNames) { + if (args.Length > 0) { + MethodInfo[] targets = GetApplicableMembers(args[0].LimitType, info); + if (targets.Length > 0) { + return TryMakeBindingTarget(targets, args, codeContext, Restrictions.Empty); + } + } + } + + return null; + } + + private MetaObject TryInplaceOperator(OperatorInfo info, Expression codeContext, MetaObject[] args) { + Operators op = CompilerHelpers.InPlaceOperatorToOperator(info.Operator); + + if (op != Operators.None) { + // recurse to try and get the non-inplace action... + return MakeOperatorRule(OperatorInfo.GetOperatorInfo(op), codeContext, args); + } + + return null; + } + + private static MetaObject TryMakeDefaultUnaryRule(OperatorInfo info, Expression codeContext, MetaObject[] args) { + if (args.Length == 1) { + Restrictions restrictions = Restrictions.GetTypeRestriction(args[0].Expression, args[0].LimitType).Merge(Restrictions.Combine(args)); + switch (info.Operator) { + case Operators.IsTrue: + if (args[0].LimitType == typeof(bool)) { + return args[0]; + } + break; + case Operators.Negate: + if (TypeUtils.IsArithmetic(args[0].LimitType)) { + return new MetaObject( + Ast.Negate(args[0].Expression), + restrictions + ); + } + break; + case Operators.Not: + if (TypeUtils.IsIntegerOrBool(args[0].LimitType)) { + return new MetaObject( + Ast.Not(args[0].Expression), + restrictions + ); + } + break; + case Operators.Documentation: + object[] attrs = args[0].LimitType.GetCustomAttributes(typeof(DocumentationAttribute), true); + string documentation = String.Empty; + + if (attrs.Length > 0) { + documentation = ((DocumentationAttribute)attrs[0]).Documentation; + } + + return new MetaObject( + Ast.Constant(documentation), + restrictions + ); + case Operators.MemberNames: + if (typeof(IMembersList).IsAssignableFrom(args[0].LimitType)) { + return MakeIMembersListRule(codeContext, args[0]); + } + + MemberInfo[] members = args[0].LimitType.GetMembers(); + Dictionary mems = new Dictionary(); + foreach (MemberInfo mi in members) { + mems[mi.Name] = mi.Name; + } + + string[] res = new string[mems.Count]; + mems.Keys.CopyTo(res, 0); + + return new MetaObject( + Ast.Constant(res), + restrictions + ); + case Operators.CallSignatures: + return MakeCallSignatureResult(CompilerHelpers.GetMethodTargets(args[0].LimitType), args[0]); + case Operators.IsCallable: + // IsCallable() is tightly tied to Call actions. So in general, we need the call-action providers to also + // provide IsCallable() status. + // This is just a rough fallback. We could also attempt to simulate the default CallBinder logic to see + // if there are any applicable calls targets, but that would be complex (the callbinder wants the argument list, + // which we don't have here), and still not correct. + bool callable = false; + if (typeof(Delegate).IsAssignableFrom(args[0].LimitType) || + typeof(MethodGroup).IsAssignableFrom(args[0].LimitType)) { + callable = true; + } + + return new MetaObject( + Ast.Constant(callable), + restrictions + ); + } + } + return null; + } + + private static MetaObject MakeIMembersListRule(Expression codeContext, MetaObject target) { + return new MetaObject( + Ast.Call( + typeof(BinderOps).GetMethod("GetStringMembers"), + Ast.Call( + AstUtils.Convert(target.Expression, typeof(IMembersList)), + typeof(IMembersList).GetMethod("GetMemberNames"), + codeContext + ) + ), + Restrictions.GetTypeRestriction(target.Expression, target.LimitType).Merge(target.Restrictions) + ); + } + + private static MetaObject MakeCallSignatureResult(MethodBase[] methods, MetaObject target) { + List arrres = new List(); + + if (methods != null) { + foreach (MethodBase mb in methods) { + StringBuilder res = new StringBuilder(); + string comma = ""; + foreach (ParameterInfo param in mb.GetParameters()) { + if (param.ParameterType == typeof(CodeContext)) continue; + + res.Append(comma); + res.Append(param.ParameterType.Name); + res.Append(" "); + res.Append(param.Name); + comma = ", "; + } + arrres.Add(res.ToString()); + } + } + + return new MetaObject( + Ast.Constant(arrres.ToArray()), + Restrictions.GetTypeRestriction(target.Expression, target.LimitType).Merge(target.Restrictions) + ); + } + + #endregion + + #region Indexer Rule + + private static Type GetArgType(MetaObject[] args, int index) { + return args[index].HasValue ? args[index].LimitType : args[index].Expression.Type; + } + + private MetaObject MakeMethodIndexRule(string oper, MetaObject[] args) { + MethodInfo[] defaults = GetMethodsFromDefaults(args[0].LimitType.GetDefaultMembers(), oper); + if (defaults.Length != 0) { + MethodBinder binder = MethodBinder.MakeBinder( + this, + oper == StandardOperators.GetItem ? "get_Item" : "set_Item", + defaults); + + MetaObject[] selfWithArgs = args; + ParameterExpression arg2 = null; + + if (oper == StandardOperators.SetItem) { + Debug.Assert(args.Length >= 2); + + // need to save arg2 in a temp because it's also our result + arg2 = Ast.Variable(args[2].Expression.Type, "arg2Temp"); + + args[2] = new MetaObject( + Ast.Assign(arg2, args[2].Expression), + args[2].Restrictions + ); + } + + BindingTarget target = binder.MakeBindingTarget(CallTypes.ImplicitInstance, selfWithArgs); + + Restrictions restrictions = Restrictions.Combine(args); + + if (target.Success) { + if (oper == StandardOperators.GetItem) { + return new MetaObject( + target.MakeExpression(), + restrictions.Merge(Restrictions.Combine(target.RestrictedArguments)) + ); + } else { + return new MetaObject( + Ast.Block( + new ParameterExpression[] { arg2 }, + target.MakeExpression(), + arg2 + ), + restrictions.Merge(Restrictions.Combine(target.RestrictedArguments)) + ); + } + } + + return MakeError( + MakeInvalidParametersError(target), + restrictions + ); + } + + return null; + } + + private MetaObject MakeArrayIndexRule(string oper, MetaObject[] args) { + if (CanConvertFrom(GetArgType(args, 1), typeof(int), false, NarrowingLevel.All)) { + Restrictions restrictions = Restrictions.GetTypeRestriction(args[0].Expression, args[0].LimitType).Merge(Restrictions.Combine(args)); + + if (oper == StandardOperators.GetItem) { + return new MetaObject( + Ast.ArrayAccess( + args[0].Expression, + ConvertIfNeeded(args[1].Expression, typeof(int)) + ), + restrictions + ); + } else { + return new MetaObject( + Ast.Assign( + Ast.ArrayAccess( + args[0].Expression, + ConvertIfNeeded(args[1].Expression, typeof(int)) + ), + ConvertIfNeeded(args[2].Expression, args[0].LimitType.GetElementType()) + ), + restrictions.Merge(args[1].Restrictions) + ); + } + } + + return null; + } + + private MethodInfo[] GetMethodsFromDefaults(MemberInfo[] defaults, string op) { + List methods = new List(); + foreach (MemberInfo mi in defaults) { + PropertyInfo pi = mi as PropertyInfo; + + if (pi != null) { + if (op == StandardOperators.GetItem) { + MethodInfo method = pi.GetGetMethod(PrivateBinding); + if (method != null) methods.Add(method); + } else if (op == StandardOperators.SetItem) { + MethodInfo method = pi.GetSetMethod(PrivateBinding); + if (method != null) methods.Add(method); + } + } + } + + // if we received methods from both declaring type & base types we need to filter them + Dictionary dict = new Dictionary(); + foreach (MethodInfo mb in methods) { + MethodSignatureInfo args = new MethodSignatureInfo(mb.IsStatic, mb.GetParameters()); + MethodInfo other; + + if (dict.TryGetValue(args, out other)) { + if (other.DeclaringType.IsAssignableFrom(mb.DeclaringType)) { + // derived type replaces... + dict[args] = mb; + } + } else { + dict[args] = mb; + } + } + + return new List(dict.Values).ToArray(); + } + + #endregion + + #region Common helpers + + private MetaObject TryMakeBindingTarget(MethodInfo[] targets, MetaObject[] args, Expression codeContext, Restrictions restrictions) { + MethodBinder mb = MethodBinder.MakeBinder(this, targets[0].Name, targets); + + BindingTarget target = mb.MakeBindingTarget(CallTypes.None, args); + if (target.Success) { + return new MetaObject( + target.MakeExpression(new ParameterBinderWithCodeContext(this, codeContext)), + restrictions.Merge(Restrictions.Combine(target.RestrictedArguments)) + ); + } + + return null; + } + + private MetaObject TryMakeInvertedBindingTarget(MethodBase[] targets, MetaObject[] args) { + MethodBinder mb = MethodBinder.MakeBinder(this, targets[0].Name, targets); + MetaObject[] selfArgs = args; + BindingTarget target = mb.MakeBindingTarget(CallTypes.None, selfArgs); + + if (target.Success) { + return new MetaObject( + Ast.Not(target.MakeExpression()), + Restrictions.Combine(target.RestrictedArguments) + ); + } + + return null; + } + + private static Operators GetInvertedOperator(Operators op) { + switch (op) { + case Operators.LessThan: return Operators.GreaterThanOrEqual; + case Operators.LessThanOrEqual: return Operators.GreaterThan; + case Operators.GreaterThan: return Operators.LessThanOrEqual; + case Operators.GreaterThanOrEqual: return Operators.LessThan; + case Operators.Equals: return Operators.NotEquals; + case Operators.NotEquals: return Operators.Equals; + default: throw new InvalidOperationException(); + } + } + + private Expression ConvertIfNeeded(Expression expression, Type type) { + Assert.NotNull(expression, type); + + if (expression.Type != type) { + return ConvertExpression(expression, type, ConversionResultKind.ExplicitCast, Ast.Constant(null, typeof(CodeContext))); + } + return expression; + } + + private MethodInfo[] GetApplicableMembers(Type t, OperatorInfo info) { + Assert.NotNull(t, info); + + OldDoOperationAction act = OldDoOperationAction.Make(this, info.Operator); + + MemberGroup members = GetMember(act, t, info.Name); + if (members.Count == 0 && info.AlternateName != null) { + members = GetMember(act, t, info.AlternateName); + } + + // filter down to just methods + return FilterNonMethods(t, members); + } + + /// + /// Gets alternate members which are specially recognized by the DLR for specific types when + /// all other member lookup fails. + /// + private static MethodInfo[] GetFallbackMembers(Type t, OperatorInfo info, MetaObject[] args, out Restrictions restrictions) { + // if we have an event we need to make a strongly-typed event handler + + // TODO: Events, we need to look in the args and pull out the real values + + if (t == typeof(EventTracker)) { + EventTracker et = ((EventTracker)args[0].Value); + if (info.Operator == Operators.InPlaceAdd) { + restrictions = GetFallbackRestrictions(t, et, args[0]); + return new MethodInfo[] { typeof(BinderOps).GetMethod("EventTrackerInPlaceAdd").MakeGenericMethod(et.Event.EventHandlerType) }; + } else if (info.Operator == Operators.InPlaceSubtract) { + restrictions = GetFallbackRestrictions(t, et, args[0]); + return new MethodInfo[] { typeof(BinderOps).GetMethod("EventTrackerInPlaceRemove").MakeGenericMethod(et.Event.EventHandlerType) }; + } + } else if (t == typeof(BoundMemberTracker)) { + BoundMemberTracker bmt = ((BoundMemberTracker)args[0].Value); + if (bmt.BoundTo.MemberType == TrackerTypes.Event) { + EventTracker et = ((EventTracker)bmt.BoundTo); + + if (info.Operator == Operators.InPlaceAdd) { + restrictions = GetFallbackRestrictions(t, et, args[0]); + return new MethodInfo[] { typeof(BinderOps).GetMethod("BoundEventTrackerInPlaceAdd").MakeGenericMethod(et.Event.EventHandlerType) }; + } else if (info.Operator == Operators.InPlaceSubtract) { + restrictions = GetFallbackRestrictions(t, et, args[0]); + return new MethodInfo[] { typeof(BinderOps).GetMethod("BoundEventTrackerInPlaceRemove").MakeGenericMethod(et.Event.EventHandlerType) }; + } + } + } + + restrictions = Restrictions.Empty; + return new MethodInfo[0]; + } + + private static Restrictions GetFallbackRestrictions(Type t, EventTracker et, MetaObject self) { + if (t == typeof(EventTracker)) { + // + // Test Generated: + // BinderOps.GetEventHandlerType(((EventTracker)args[0]).Event) == et.Event.EventHandlerType + // + return Restrictions.GetExpressionRestriction( + Ast.Equal( + Ast.Call( + typeof(BinderOps).GetMethod("GetEventHandlerType"), + Ast.Property( + Ast.Convert( + self.Expression, + typeof(EventTracker) + ), + typeof(EventTracker).GetProperty("Event") + ) + ), + Ast.Constant(et.Event.EventHandlerType) + ) + ); + } else if (t == typeof(BoundMemberTracker)) { + // + // Test Generated: + // BinderOps.GetEventHandlerType(((EventTracker)((BoundMemberTracker)args[0]).BountTo).Event) == et.Event.EventHandlerType + // + return Restrictions.GetExpressionRestriction( + Ast.Equal( + Ast.Call( + typeof(BinderOps).GetMethod("GetEventHandlerType"), + Ast.Property( + Ast.Convert( + Ast.Property( + Ast.Convert( + self.Expression, + typeof(BoundMemberTracker) + ), + typeof(BoundMemberTracker).GetProperty("BoundTo") + ), + typeof(EventTracker) + ), + typeof(EventTracker).GetProperty("Event") + ) + ), + Ast.Constant(et.Event.EventHandlerType) + ) + ); + } + + return Restrictions.Empty; + } + + private static MethodInfo[] FilterNonMethods(Type t, MemberGroup members) { + Assert.NotNull(t, members); + + List methods = new List(members.Count); + foreach (MemberTracker mi in members) { + if (mi.MemberType == TrackerTypes.Method) { + MethodInfo method = ((MethodTracker)mi).Method; + + // don't call object methods for None type, but if someone added + // methods to null we'd call those. + if (method.DeclaringType != typeof(object) || t != typeof(Null)) { + methods.Add(method); + } + } + } + + return methods.ToArray(); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.SetMember.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.SetMember.cs new file mode 100644 index 0000000000..20296445cd --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.SetMember.cs @@ -0,0 +1,378 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Dynamic.Binders; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public partial class DefaultBinder : ActionBinder { + /// + /// Builds a MetaObject for performing a member get. Supports all built-in .NET members, the OperatorMethod + /// GetBoundMember, and StrongBox instances. + /// + /// + /// The name of the member to retrieve. This name is not processed by the DefaultBinder and + /// is instead handed off to the GetMember API which can do name mangling, case insensitive lookups, etc... + /// + /// + /// The MetaObject from which the member is retrieved. + /// + /// + /// The value being assigned to the target member. + /// + public MetaObject SetMember(string name, MetaObject target, MetaObject value) { + return SetMember(name, target, value, Ast.Constant(null, typeof(CodeContext))); + } + + /// + /// Builds a MetaObject for performing a member get. Supports all built-in .NET members, the OperatorMethod + /// GetBoundMember, and StrongBox instances. + /// + /// + /// The name of the member to retrieve. This name is not processed by the DefaultBinder and + /// is instead handed off to the GetMember API which can do name mangling, case insensitive lookups, etc... + /// + /// + /// The MetaObject from which the member is retrieved. + /// + /// + /// The value being assigned to the target member. + /// + /// + /// An expression which provides access to the CodeContext if its required for + /// accessing the member (e.g. for an extension property which takes CodeContext). By default this + /// a null CodeContext object is passed. + /// + public MetaObject SetMember(string name, MetaObject target, MetaObject value, Expression codeContext) { + ContractUtils.RequiresNotNull(name, "name"); + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNull(value, "value"); + ContractUtils.RequiresNotNull(codeContext, "codeContext"); + + return MakeSetMemberTarget( + new SetOrDeleteMemberInfo(name, codeContext), + target, + value + ); + } + + private MetaObject MakeSetMemberTarget(SetOrDeleteMemberInfo memInfo, MetaObject target, MetaObject value) { + Type type = target.LimitType.IsCOMObject ? target.Expression.Type : target.LimitType; + Expression self = target.Expression; + + target = target.Restrict(target.LimitType); + + memInfo.Body.Restrictions = target.Restrictions; + + if (typeof(TypeTracker).IsAssignableFrom(type)) { + type = ((TypeTracker)target.Value).Type; + self = null; + + memInfo.Body.Restrictions = memInfo.Body.Restrictions.Merge( + Restrictions.GetInstanceRestriction(target.Expression, target.Value) + ); + } + + MakeSetMemberRule(memInfo, type, self, value); + + return memInfo.Body.GetMetaObject(target, value); + } + + private void MakeSetMemberRule(SetOrDeleteMemberInfo memInfo, Type type, Expression self, MetaObject target) { + if (MakeOperatorSetMemberBody(memInfo, self, target, type, "SetMember")) { + return; + } + + // needed for GetMember call until DynamicAction goes away + OldDynamicAction act = OldSetMemberAction.Make( + this, + memInfo.Name + ); + + MemberGroup members = GetMember(act, type, memInfo.Name); + + // if lookup failed try the strong-box type if available. + if (members.Count == 0 && typeof(IStrongBox).IsAssignableFrom(type)) { + self = Ast.Field(AstUtils.Convert(self, type), type.GetField("Value")); + type = type.GetGenericArguments()[0]; + + members = GetMember(act, type, memInfo.Name); + } + + Expression error; + TrackerTypes memberTypes = GetMemberType(members, out error); + if (error == null) { + switch (memberTypes) { + case TrackerTypes.Method: + case TrackerTypes.TypeGroup: + case TrackerTypes.Type: + case TrackerTypes.Constructor: + memInfo.Body.FinishCondition( + MakeError(MakeReadOnlyMemberError(type, memInfo.Name)) + ); + break; + case TrackerTypes.Event: + memInfo.Body.FinishCondition( + MakeError(MakeEventValidation(members, self, target.Expression, memInfo.CodeContext)) + ); + break; + case TrackerTypes.Field: + MakeFieldRule(memInfo, self, target, type, members); + break; + case TrackerTypes.Property: + MakePropertyRule(memInfo, self, target, type, members); + break; + case TrackerTypes.Custom: + MakeGenericBody(memInfo, self, target, type, members[0]); + break; + case TrackerTypes.All: + // no match + if (MakeOperatorSetMemberBody(memInfo, self, target, type, "SetMemberAfter")) { + return; + } + + memInfo.Body.FinishCondition( + MakeError(MakeMissingMemberError(type, memInfo.Name)) + ); + break; + default: + throw new InvalidOperationException(); + } + } else { + memInfo.Body.FinishCondition(error); + } + } + + private void MakeGenericBody(SetOrDeleteMemberInfo memInfo, Expression instance, MetaObject target, Type type, MemberTracker tracker) { + if (instance != null) { + tracker = tracker.BindToInstance(instance); + } + + Expression val = tracker.SetValue(memInfo.CodeContext, this, type, target.Expression); + + if (val != null) { + memInfo.Body.FinishCondition(val); + } else { + memInfo.Body.FinishCondition( + MakeError(tracker.GetError(this)) + ); + } + } + + private void MakePropertyRule(SetOrDeleteMemberInfo memInfo, Expression instance, MetaObject target, Type targetType, MemberGroup properties) { + PropertyTracker info = (PropertyTracker)properties[0]; + + MethodInfo setter = info.GetSetMethod(true); + + // Allow access to protected getters TODO: this should go, it supports IronPython semantics. + if (setter != null && !setter.IsPublic && !(setter.IsFamily || setter.IsFamilyOrAssembly)) { + if (!PrivateBinding) { + setter = null; + } + } + + if (setter != null) { + setter = CompilerHelpers.GetCallableMethod(setter, PrivateBinding); + + if (info.IsStatic != (instance == null)) { + memInfo.Body.FinishCondition( + MakeError( + MakeStaticPropertyInstanceAccessError( + info, + true, + instance, + target.Expression + ) + ) + ); + } else if (info.IsStatic && info.DeclaringType != targetType) { + memInfo.Body.FinishCondition( + MakeError( + MakeStaticAssignFromDerivedTypeError(targetType, info, target.Expression, memInfo.CodeContext) + ) + ); + } else if (setter.ContainsGenericParameters) { + memInfo.Body.FinishCondition( + MakeGenericPropertyExpression(memInfo) + ); + } else if (setter.IsPublic && !setter.DeclaringType.IsValueType) { + if (instance == null) { + memInfo.Body.FinishCondition( + AstUtils.SimpleCallHelper( + setter, + ConvertExpression( + target.Expression, + setter.GetParameters()[0].ParameterType, + ConversionResultKind.ExplicitCast, + memInfo.CodeContext + ) + ) + ); + } else { + memInfo.Body.FinishCondition( + MakeReturnValue( + MakeCallExpression(memInfo.CodeContext, setter, instance, target.Expression), + target + ) + ); + } + } else { + // TODO: Should be able to do better w/ value types. + memInfo.Body.FinishCondition( + MakeReturnValue( + Ast.Call( + Ast.Constant(((ReflectedPropertyTracker)info).Property), // TODO: Private binding on extension properties + typeof(PropertyInfo).GetMethod("SetValue", new Type[] { typeof(object), typeof(object), typeof(object[]) }), + instance == null ? Ast.Constant(null) : AstUtils.Convert(instance, typeof(object)), + AstUtils.Convert(target.Expression, typeof(object)), + Ast.NewArrayInit(typeof(object)) + ), + target + ) + ); + } + } else { + memInfo.Body.FinishCondition( + MakeError( + MakeMissingMemberError(targetType, memInfo.Name) + ) + ); + } + } + + private void MakeFieldRule(SetOrDeleteMemberInfo memInfo, Expression instance, MetaObject target, Type targetType, MemberGroup fields) { + FieldTracker field = (FieldTracker)fields[0]; + + // TODO: Tmp variable for target + if (field.DeclaringType.IsGenericType && field.DeclaringType.GetGenericTypeDefinition() == typeof(StrongBox<>)) { + // work around a CLR bug where we can't access generic fields from dynamic methods. + Type[] generic = field.DeclaringType.GetGenericArguments(); + memInfo.Body.FinishCondition( + MakeReturnValue( + Ast.Assign( + Ast.Field( + AstUtils.Convert(instance, field.DeclaringType), + field.DeclaringType.GetField("Value") + ), + AstUtils.Convert(target.Expression, generic[0]) + ), + target + ) + ); + } else if (field.IsInitOnly || field.IsLiteral) { + memInfo.Body.FinishCondition( + MakeError( + MakeReadOnlyMemberError(targetType, memInfo.Name) + ) + ); + } else if (field.IsStatic && targetType != field.DeclaringType) { + memInfo.Body.FinishCondition( + MakeError( + MakeStaticAssignFromDerivedTypeError(targetType, field, target.Expression, memInfo.CodeContext) + ) + ); + } else if (field.DeclaringType.IsValueType && !field.IsStatic) { + memInfo.Body.FinishCondition( + Ast.Throw( + Ast.New( + typeof(ArgumentException).GetConstructor(new Type[] { typeof(string) }), + Ast.Constant("cannot assign to value types") + ) + ) + ); + } else if (field.IsPublic && field.DeclaringType.IsVisible) { + memInfo.Body.FinishCondition( + MakeReturnValue( + Ast.Assign( + Ast.Field( + field.IsStatic ? + null : + Ast.Convert(instance, field.DeclaringType), + field.Field + ), + ConvertExpression(target.Expression, field.FieldType, ConversionResultKind.ExplicitCast, memInfo.CodeContext) + ), + target + ) + ); + } else { + memInfo.Body.FinishCondition( + MakeReturnValue( + Ast.Call( + AstUtils.Convert(Ast.Constant(field.Field), typeof(FieldInfo)), + typeof(FieldInfo).GetMethod("SetValue", new Type[] { typeof(object), typeof(object) }), + field.IsStatic ? + Ast.Constant(null) : + (Expression)AstUtils.Convert(instance, typeof(object)), + AstUtils.Convert(target.Expression, typeof(object)) + ), + target + ) + ); + } + } + + private Expression MakeReturnValue(Expression expression, MetaObject target) { + return Ast.Block( + expression, + target.Expression + ); + } + + /// if a member-injector is defined-on or registered-for this type call it + private bool MakeOperatorSetMemberBody(SetOrDeleteMemberInfo memInfo, Expression self, MetaObject target, Type type, string name) { + if (self != null) { + MethodInfo setMem = GetMethod(type, name); + if (setMem != null && setMem.IsSpecialName) { + ParameterExpression tmp = Ast.Variable(target.Expression.Type, "setValue"); + memInfo.Body.AddVariable(tmp); + + Expression call = MakeCallExpression(memInfo.CodeContext, setMem, AstUtils.Convert(self, type), Ast.Constant(memInfo.Name), tmp); + + call = Ast.Block(Ast.Assign(tmp, target.Expression), call); + + if (setMem.ReturnType == typeof(bool)) { + memInfo.Body.AddCondition( + call, + tmp + ); + } else { + memInfo.Body.FinishCondition(Ast.Block(call, tmp)); + } + + return setMem.ReturnType != typeof(bool); + } + } + + return false; + } + + private static Expression MakeGenericPropertyExpression(SetOrDeleteMemberInfo memInfo) { + return Ast.New( + typeof(MemberAccessException).GetConstructor(new Type[] { typeof(string) }), + Ast.Constant(memInfo.Name) + ); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.cs new file mode 100644 index 0000000000..78cf3191b8 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/DefaultBinder.cs @@ -0,0 +1,348 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic.Binders; +using System.Text; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Actions.Calls; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + /// + /// Provides binding semantics for a language. This include conversions as well as support + /// for producing rules for actions. These optimized rules are used for calling methods, + /// performing operators, and getting members using the ActionBinder's conversion semantics. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + public abstract partial class DefaultBinder : ActionBinder { + protected DefaultBinder(ScriptDomainManager manager) + : base(manager) { + } + + /// + /// Produces a rule for the specified Action for the given arguments. + /// + /// The default implementation can produce rules for standard .NET types. Languages should + /// override this and provide any custom behavior they need and fallback to the default + /// implementation if no custom behavior is required. + /// + protected override void MakeRule(OldDynamicAction action, object[] args, RuleBuilder rule) { + ContractUtils.RequiresNotNull(action, "action"); + ContractUtils.RequiresNotNull(args, "args"); + + object[] extracted; + CodeContext callerContext = ExtractCodeContext(args, out extracted); + + ContractUtils.RequiresNotNull(callerContext, "callerContext"); + + switch (action.Kind) { + case DynamicActionKind.Call: + new CallBinderHelper(callerContext, (OldCallAction)action, extracted, rule).MakeRule(); + return; + case DynamicActionKind.GetMember: + new GetMemberBinderHelper(callerContext, (OldGetMemberAction)action, extracted, rule).MakeNewRule(); + return; + case DynamicActionKind.SetMember: + new SetMemberBinderHelper(callerContext, (OldSetMemberAction)action, extracted, rule).MakeNewRule(); + return; + case DynamicActionKind.CreateInstance: + new CreateInstanceBinderHelper(callerContext, (OldCreateInstanceAction)action, extracted, rule).MakeRule(); + return; + case DynamicActionKind.DoOperation: + new DoOperationBinderHelper(callerContext, (OldDoOperationAction)action, extracted, rule).MakeRule(); + return; + case DynamicActionKind.DeleteMember: + new DeleteMemberBinderHelper(callerContext, (OldDeleteMemberAction)action, extracted, rule).MakeRule(); + return; + case DynamicActionKind.ConvertTo: + new ConvertToBinderHelper(callerContext, (OldConvertToAction)action, extracted, rule).MakeRule(); + return; + default: + throw new NotImplementedException(action.ToString()); + } + } + + protected static CodeContext ExtractCodeContext(object[] args, out object[] extracted) { + CodeContext cc; + if (args.Length > 0 && (cc = args[0] as CodeContext) != null) { + extracted = ArrayUtils.ShiftLeft(args, 1); + } else { + cc = null; + extracted = args; + } + return cc; + } + + public virtual ErrorInfo MakeInvalidParametersError(BindingTarget target) { + switch (target.Result) { + case BindingResult.CallFailure: return MakeCallFailureError(target); + case BindingResult.AmbiguousMatch: return MakeAmbiguousCallError(target); + case BindingResult.IncorrectArgumentCount: return MakeIncorrectArgumentCountError(target); + default: throw new InvalidOperationException(); + } + } + + private static ErrorInfo MakeIncorrectArgumentCountError(BindingTarget target) { + int minArgs = Int32.MaxValue; + int maxArgs = Int32.MinValue; + foreach (int argCnt in target.ExpectedArgumentCount) { + minArgs = System.Math.Min(minArgs, argCnt); + maxArgs = System.Math.Max(maxArgs, argCnt); + } + + return ErrorInfo.FromException( + Ast.Call( + typeof(BinderOps).GetMethod("TypeErrorForIncorrectArgumentCount", new Type[] { + typeof(string), typeof(int), typeof(int) , typeof(int), typeof(int), typeof(bool), typeof(bool) + }), + Ast.Constant(target.Name, typeof(string)), // name + Ast.Constant(minArgs), // min formal normal arg cnt + Ast.Constant(maxArgs), // max formal normal arg cnt + Ast.Constant(0), // default cnt + Ast.Constant(target.ActualArgumentCount), // args provided + Ast.Constant(false), // hasArgList + Ast.Constant(false) // kwargs provided + ) + ); + } + + private ErrorInfo MakeAmbiguousCallError(BindingTarget target) { + StringBuilder sb = new StringBuilder("Multiple targets could match: "); + string outerComma = ""; + foreach (MethodTarget mt in target.AmbiguousMatches) { + Type[] types = mt.GetParameterTypes(); + string innerComma = ""; + + sb.Append(outerComma); + sb.Append(target.Name); + sb.Append('('); + foreach (Type t in types) { + sb.Append(innerComma); + sb.Append(GetTypeName(t)); + innerComma = ", "; + } + + sb.Append(')'); + outerComma = ", "; + } + + return ErrorInfo.FromException( + Ast.Call( + typeof(BinderOps).GetMethod("SimpleTypeError"), + Ast.Constant(sb.ToString(), typeof(string)) + ) + ); + } + + private ErrorInfo MakeCallFailureError(BindingTarget target) { + foreach (CallFailure cf in target.CallFailures) { + switch (cf.Reason) { + case CallFailureReason.ConversionFailure: + foreach (ConversionResult cr in cf.ConversionResults) { + if (cr.Failed) { + return ErrorInfo.FromException( + Ast.Call( + typeof(BinderOps).GetMethod("SimpleTypeError"), + Ast.Constant(String.Format("expected {0}, got {1}", GetTypeName(cr.To), GetTypeName(cr.From))) + ) + ); + } + } + break; + case CallFailureReason.DuplicateKeyword: + return ErrorInfo.FromException( + Ast.Call( + typeof(BinderOps).GetMethod("TypeErrorForDuplicateKeywordArgument"), + Ast.Constant(target.Name, typeof(string)), + Ast.Constant(SymbolTable.IdToString(cf.KeywordArguments[0]), typeof(string)) // TODO: Report all bad arguments? + ) + ); + case CallFailureReason.UnassignableKeyword: + return ErrorInfo.FromException( + Ast.Call( + typeof(BinderOps).GetMethod("TypeErrorForExtraKeywordArgument"), + Ast.Constant(target.Name, typeof(string)), + Ast.Constant(SymbolTable.IdToString(cf.KeywordArguments[0]), typeof(string)) // TODO: Report all bad arguments? + ) + ); + default: throw new InvalidOperationException(); + } + } + throw new InvalidOperationException(); + } + + /// + /// Provides a way for the binder to provide a custom error message when lookup fails. Just + /// doing this for the time being until we get a more robust error return mechanism. + /// + public virtual ErrorInfo MakeUndeletableMemberError(Type type, string name) { + return MakeReadOnlyMemberError(type, name); + } + + /// + /// Called when the user is accessing a protected or private member on a get. + /// + /// The default implementation allows access to the fields or properties using reflection. + /// + public virtual ErrorInfo MakeNonPublicMemberGetError(Expression codeContext, MemberTracker member, Type type, Expression instance) { + switch (member.MemberType) { + case TrackerTypes.Field: + FieldTracker ft = (FieldTracker)member; + + return ErrorInfo.FromValueNoError( + Ast.Call( + AstUtils.Convert(Ast.Constant(ft.Field), typeof(FieldInfo)), + typeof(FieldInfo).GetMethod("GetValue"), + AstUtils.Convert(instance, typeof(object)) + ) + ); + case TrackerTypes.Property: + PropertyTracker pt = (PropertyTracker)member; + + return ErrorInfo.FromValueNoError( + MemberTracker.FromMemberInfo(pt.GetGetMethod(true)).Call(codeContext, this, instance) + ); + default: + throw new InvalidOperationException(); + } + } + + /// + /// Provides a way for the binder to provide a custom error message when lookup fails. Just + /// doing this for the time being until we get a more robust error return mechanism. + /// + public virtual ErrorInfo MakeReadOnlyMemberError(Type type, string name) { + return ErrorInfo.FromException( + Expression.New( + typeof(MissingMemberException).GetConstructor(new Type[] { typeof(string) }), + Expression.Constant(name) + ) + ); + } + + public virtual ErrorInfo MakeEventValidation(MemberGroup members, Expression eventObject, Expression value, Expression codeContext) { + EventTracker ev = (EventTracker)members[0]; + + // handles in place addition of events - this validates the user did the right thing. + return ErrorInfo.FromValueNoError( + Expression.Call( + typeof(BinderOps).GetMethod("SetEvent"), + Expression.Constant(ev), + value + ) + ); + } + + /// + /// Checks to see if the language allows keyword arguments to be bound to instance fields or + /// properties and turned into sets. By default this is only allowed on contructors. + /// + protected internal virtual bool AllowKeywordArgumentSetting(MethodBase method) { + return CompilerHelpers.IsConstructor(method); + } + + public static Expression MakeError(ErrorInfo error) { + switch (error.Kind) { + case ErrorInfoKind.Error: + // error meta objecT? + return error.Expression; + case ErrorInfoKind.Exception: + return Expression.Throw(error.Expression); + case ErrorInfoKind.Success: + return error.Expression; + default: + throw new InvalidOperationException(); + } + } + + public static MetaObject MakeError(ErrorInfo error, Restrictions restrictions) { + return new MetaObject(MakeError(error), restrictions); + } + + protected TrackerTypes GetMemberType(MemberGroup members, out Expression error) { + error = null; + TrackerTypes memberType = TrackerTypes.All; + for (int i = 0; i < members.Count; i++) { + MemberTracker mi = members[i]; + if (mi.MemberType != memberType) { + if (memberType != TrackerTypes.All) { + error = MakeAmbiguousMatchError(members); + return TrackerTypes.All; + } + memberType = mi.MemberType; + } + } + return memberType; + } + + private static Expression MakeAmbiguousMatchError(MemberGroup members) { + StringBuilder sb = new StringBuilder(); + foreach (MemberTracker mi in members) { + if (sb.Length != 0) sb.Append(", "); + sb.Append(mi.MemberType); + sb.Append(" : "); + sb.Append(mi.ToString()); + } + + return Ast.Throw( + Ast.New( + typeof(AmbiguousMatchException).GetConstructor(new Type[] { typeof(string) }), + Ast.Constant(sb.ToString()) + ) + ); + } + + internal MethodInfo GetMethod(Type type, string name) { + // declaring type takes precedence + MethodInfo mi = type.GetMethod(name); + if (mi != null) { + return mi; + } + + // then search extension types. + Type curType = type; + do { + IList extTypes = GetExtensionTypes(curType); + foreach (Type t in extTypes) { + MethodInfo next = t.GetMethod(name); + if (next != null) { + if (mi != null) { + throw new AmbiguousMatchException(String.Format("Found multiple members for {0} on type {1}", name, curType)); + } + + mi = next; + } + } + + if (mi != null) { + return mi; + } + + curType = curType.BaseType; + } while (curType != null); + + return null; + } + } +} + diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/DeleteMemberBinderHelper.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/DeleteMemberBinderHelper.cs new file mode 100644 index 0000000000..77846bb097 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/DeleteMemberBinderHelper.cs @@ -0,0 +1,104 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + using AstUtils = Microsoft.Scripting.Ast.Utils; + + public class DeleteMemberBinderHelper : MemberBinderHelper { + private bool _isStatic; + + public DeleteMemberBinderHelper(CodeContext context, OldDeleteMemberAction action, object[] args, RuleBuilder rule) + : base(context, action, args, rule) { + } + + public void MakeRule() { + Rule.MakeTest(StrongBoxType ?? CompilerHelpers.GetType(Target)); + Rule.Target = MakeDeleteMemberTarget(); + } + + private Expression MakeDeleteMemberTarget() { + Type type = CompilerHelpers.GetType(Target); + + if (typeof(TypeTracker).IsAssignableFrom(type)) { + type = ((TypeTracker)Target).Type; + _isStatic = true; + Rule.AddTest(Ast.Equal(Rule.Parameters[0], Ast.Constant(Arguments[0]))); + } + + if (_isStatic || !MakeOperatorGetMemberBody(type, "DeleteMember")) { + MemberGroup group = Binder.GetMember(Action, type, StringName); + if (group.Count != 0) { + if (group[0].MemberType == TrackerTypes.Property) { + MethodInfo del = ((PropertyTracker)group[0]).GetDeleteMethod(PrivateBinding); + if (del != null) { + MakePropertyDeleteStatement(del); + return Body; + } + } + + MakeUndeletableMemberError(GetDeclaringMemberType(group)); + } else { + MakeMissingMemberError(type); + } + } + + return Body; + } + + private static Type GetDeclaringMemberType(MemberGroup group) { + Type t = typeof(object); + foreach (MemberTracker mt in group) { + if (t.IsAssignableFrom(mt.DeclaringType)) { + t = mt.DeclaringType; + } + } + return t; + } + + private void MakePropertyDeleteStatement(MethodInfo delete) { + AddToBody( + Rule.MakeReturn( + Binder, + Binder.MakeCallExpression(Rule.Context, delete, Rule.Parameters[0]) + ) + ); + } + + /// if a member-injector is defined-on or registered-for this type call it + private bool MakeOperatorGetMemberBody(Type type, string name) { + MethodInfo delMem = GetMethod(type, name); + if (delMem != null && delMem.IsSpecialName) { + Expression call = Binder.MakeCallExpression(Rule.Context, delMem, Rule.Parameters[0], Ast.Constant(StringName)); + Expression ret; + + if (delMem.ReturnType == typeof(bool)) { + ret = AstUtils.If(call, Rule.MakeReturn(Binder, Ast.Constant(null))); + } else { + ret = Rule.MakeReturn(Binder, call); + } + AddToBody( ret); + return delMem.ReturnType != typeof(bool); + } + return false; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/DoOperationBinderHelper.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/DoOperationBinderHelper.cs new file mode 100644 index 0000000000..8c863d16f5 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/DoOperationBinderHelper.cs @@ -0,0 +1,628 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic; +using System.Text; +using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public class DoOperationBinderHelper : BinderHelper { + private object[] _args; // arguments we were created with + private Type[] _types; // types of our arguments + private readonly RuleBuilder _rule; // the rule we're building and returning + + public DoOperationBinderHelper(CodeContext context, OldDoOperationAction action, object[] args, RuleBuilder rule) + : base(context, action) { + _args = args; + _types = CompilerHelpers.GetTypes(args); + _rule = rule; + _rule.MakeTest(_types); + } + + public void MakeRule() { + if (Action.Operation == Operators.GetItem || + Action.Operation == Operators.SetItem) { + // try default member first, then look for special name methods. + if (MakeDefaultMemberRule(Action.Operation)) { + return; + } + } + + OperatorInfo info = OperatorInfo.GetOperatorInfo(Action.Operation); + if (Action.IsComparision) { + MakeComparisonRule(info); + } else { + MakeOperatorRule(info); + } + } + + #region Comparison operator + + private void MakeComparisonRule(OperatorInfo info) { + // check the first type if it has an applicable method + MethodInfo[] targets = GetApplicableMembers(info); + if (targets.Length > 0 && TryMakeBindingTarget(targets)) { + return; + } + + // then check the 2nd type. + targets = GetApplicableMembers(_types[1], info); + if (targets.Length > 0 && TryMakeBindingTarget(targets)) { + return; + } + + // try Compare: cmp(x,y) (>, <, >=, <=, ==, !=) 0 + if (TryNumericComparison(info)) { + return; + } + + // try inverting the operator & result (e.g. if looking for Equals try NotEquals, LessThan for GreaterThan)... + Operators revOp = GetInvertedOperator(info.Operator); + OperatorInfo revInfo = OperatorInfo.GetOperatorInfo(revOp); + Debug.Assert(revInfo != null); + + // try the 1st type's opposite function result negated + targets = GetApplicableMembers(revInfo); + if (targets.Length > 0 && TryMakeInvertedBindingTarget(targets)) { + return; + } + + // then check the 2nd type. + targets = GetApplicableMembers(_types[1], revInfo); + if (targets.Length > 0 && TryMakeInvertedBindingTarget(targets)) { + return; + } + + // see if we're comparing to null w/ an object ref or a Nullable + if (TryMakeNullComparisonRule()) { + return; + } + + // see if this is a primitive type where we're comparing the two values. + if (TryPrimitiveCompare()) { + return; + } + + SetErrorTarget(info); + } + + private bool TryNumericComparison(OperatorInfo info) { + MethodInfo[] targets = FilterNonMethods(_types[0], Binder.GetMember(Action, _types[0], "Compare")); + if (targets.Length > 0) { + MethodBinder mb = MethodBinder.MakeBinder(Binder, targets[0].Name, targets); + BindingTarget target = mb.MakeBindingTarget(CallTypes.None, _types); + if (target.Success) { + Expression call = Ast.Convert(target.MakeExpression(_rule, _rule.Parameters), typeof(int)); + switch (info.Operator) { + case Operators.GreaterThan: call = Ast.GreaterThan(call, Ast.Constant(0)); break; + case Operators.LessThan: call = Ast.LessThan(call, Ast.Constant(0)); break; + case Operators.GreaterThanOrEqual: call = Ast.GreaterThanOrEqual(call, Ast.Constant(0)); break; + case Operators.LessThanOrEqual: call = Ast.LessThanOrEqual(call, Ast.Constant(0)); break; + case Operators.Equals: call = Ast.Equal(call, Ast.Constant(0)); break; + case Operators.NotEquals: call = Ast.NotEqual(call, Ast.Constant(0)); break; + case Operators.Compare: + break; + } + _rule.Target = _rule.MakeReturn(Binder, call); + return true; + } + } + return false; + } + + private bool TryPrimitiveCompare() { + if (TypeUtils.GetNonNullableType(_types[0]) == TypeUtils.GetNonNullableType(_types[1]) && + TypeUtils.IsNumeric(_types[0])) { + // TODO: Nullable Support + Expression expr; + switch (Action.Operation) { + case Operators.Equals: expr = Ast.Equal(Param0, Param1); break; + case Operators.NotEquals: expr = Ast.NotEqual(Param0, Param1); break; + case Operators.GreaterThan: expr = Ast.GreaterThan(Param0, Param1); break; + case Operators.LessThan: expr = Ast.LessThan(Param0, Param1); break; + case Operators.GreaterThanOrEqual: expr = Ast.GreaterThanOrEqual(Param0, Param1); break; + case Operators.LessThanOrEqual: expr = Ast.LessThanOrEqual(Param0, Param1); break; + default: throw new InvalidOperationException(); + } + _rule.Target = _rule.MakeReturn(Binder, expr); + return true; + } + return false; + } + + /// + /// Produces a rule for comparing a value to null - supports comparing object references and nullable types. + /// + private bool TryMakeNullComparisonRule() { + if (_types[0] == typeof(Null)) { + if (!_types[1].IsValueType) { + _rule.Target = _rule.MakeReturn(Binder, Ast.Equal(_rule.Parameters[1], Ast.Constant(null))); + } else if (_types[1].GetGenericTypeDefinition() == typeof(Nullable<>)) { + _rule.Target = _rule.MakeReturn(Binder, Ast.Property(Param1, _types[1].GetProperty("HasValue"))); + } else { + return false; + } + return true; + } else if (_types[1] == typeof(Null)) { + if (!_types[0].IsValueType) { + _rule.Target = _rule.MakeReturn(Binder, Ast.Equal(_rule.Parameters[0], Ast.Constant(null))); + } else if (_types[0].GetGenericTypeDefinition() == typeof(Nullable<>)) { + _rule.Target = _rule.MakeReturn(Binder, Ast.Property(Param0, _types[1].GetProperty("HasValue"))); + } else { + return false; + } + return true; + } + return false; + } + + #endregion + + #region Operator Rule + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] // TODO: fix + private void MakeOperatorRule(OperatorInfo info) { + MethodInfo[] targets = GetApplicableMembers(info); + if (targets.Length == 0) { + targets = GetFallbackMembers(_types[0], info); + } + + if (targets.Length > 0 && TryMakeBindingTarget(targets)) { + return; + } + + if (_types.Length > 1) { + targets = GetApplicableMembers(_types[1], info); + if (targets.Length > 0 && TryMakeBindingTarget(targets)) { + return; + } + } + + Operators op = CompilerHelpers.InPlaceOperatorToOperator(info.Operator) ; + if (op != Operators.None) { + // recurse to try and get the non-inplace action... + MakeOperatorRule(OperatorInfo.GetOperatorInfo(op)); + return; + } + + if (_types.Length == 2 && + TypeUtils.GetNonNullableType(_types[0]) == TypeUtils.GetNonNullableType(_types[1]) && + TypeUtils.IsArithmetic(_types[0])) { + // TODO: Nullable Support + Expression expr; + switch (info.Operator) { + case Operators.Add: expr = Ast.Add(Param0, Param1); break; + case Operators.Subtract: expr = Ast.Subtract(Param0, Param1); break; + case Operators.Divide: expr = Ast.Divide(Param0, Param1); break; + case Operators.Mod: expr = Ast.Modulo(Param0, Param1); break; + case Operators.Multiply:expr = Ast.Multiply(Param0, Param1); break; + case Operators.LeftShift: expr = Ast.LeftShift(Param0, Param1); break; + case Operators.RightShift: expr = Ast.RightShift(Param0, Param1); break; + case Operators.BitwiseAnd: expr = Ast.And(Param0, Param1); break; + case Operators.BitwiseOr: expr = Ast.Or(Param0, Param1); break; + case Operators.ExclusiveOr: expr = Ast.ExclusiveOr(Param0, Param1); break; + default: throw new InvalidOperationException(); + } + _rule.Target = _rule.MakeReturn(Binder, expr); + return; + } else if(_types.Length == 1 && TryMakeDefaultUnaryRule(info)) { + return; + } + + SetErrorTarget(info); + } + + private bool TryMakeDefaultUnaryRule(OperatorInfo info) { + switch (info.Operator) { + case Operators.IsTrue: + if (_types[0] == typeof(bool)) { + _rule.Target = _rule.MakeReturn(Binder, Param0); + return true; + } + break; + case Operators.Negate: + if (TypeUtils.IsArithmetic(_types[0])) { + _rule.Target = _rule.MakeReturn(Binder, Ast.Negate(Param0)); + return true; + } + break; + case Operators.Not: + if (TypeUtils.IsIntegerOrBool(_types[0])) { + _rule.Target = _rule.MakeReturn(Binder, Ast.Not(Param0)); + return true; + } + break; + case Operators.Documentation: + object[] attrs = _types[0].GetCustomAttributes(typeof(DocumentationAttribute), true); + string documentation = String.Empty; + + if (attrs.Length > 0) { + documentation = ((DocumentationAttribute)attrs[0]).Documentation; + } + + _rule.Target = _rule.MakeReturn(Binder, Ast.Constant(documentation)); + return true; + case Operators.MemberNames: + if (typeof(IMembersList).IsAssignableFrom(_types[0])) { + MakeIMembersListRule(); + return true; + } + + MemberInfo[] members = _types[0].GetMembers(); + Dictionary mems = new Dictionary(); + foreach (MemberInfo mi in members) { + mems[mi.Name] = mi.Name; + } + + string[] res = new string[mems.Count]; + mems.Keys.CopyTo(res, 0); + _rule.Target = _rule.MakeReturn(Binder, Ast.Constant(res)); + return true; + case Operators.CallSignatures: + MakeCallSignatureResult(CompilerHelpers.GetMethodTargets(_args[0])); + break; + case Operators.IsCallable: + // IsCallable() is tightly tied to Call actions. So in general, we need the call-action providers to also + // provide IsCallable() status. + // This is just a rough fallback. We could also attempt to simulate the default CallBinder logic to see + // if there are any applicable calls targets, but that would be complex (the callbinder wants the argument list, + // which we don't have here), and still not correct. + bool callable = false; + if (typeof(Delegate).IsAssignableFrom(_types[0]) || + typeof(MethodGroup).IsAssignableFrom(_types[0])) { + callable = true; + } + + _rule.Target = _rule.MakeReturn(Context.LanguageContext.Binder, Ast.Constant(callable)); + return true; + } + return false; + } + + private void MakeIMembersListRule() { + _rule.Target = + _rule.MakeReturn( + Binder, + Ast.Call( + typeof(ScriptingRuntimeHelpers).GetMethod("GetStringMembers"), + Ast.Call( + AstUtils.Convert(_rule.Parameters[0], typeof(IMembersList)), + typeof(IMembersList).GetMethod("GetMemberNames"), + _rule.Context + ) + ) + ); + } + + private void MakeCallSignatureResult(MethodBase[] methods) { + List arrres = new List(); + foreach (MethodBase mb in methods) { + StringBuilder res = new StringBuilder(); + string comma = ""; + foreach (ParameterInfo param in mb.GetParameters()) { + if (param.ParameterType == typeof(CodeContext)) continue; + + res.Append(comma); + res.Append(param.ParameterType.Name); + res.Append(" "); + res.Append(param.Name); + comma = ", "; + } + arrres.Add(res.ToString()); + } + + _rule.Target = _rule.MakeReturn(Binder, Ast.Constant(arrres.ToArray())); + } + + #endregion + + #region Indexer Rule + + private bool MakeDefaultMemberRule(Operators oper) { + if (_types[0].IsArray) { + if (Binder.CanConvertFrom(_types[1], typeof(int), false, NarrowingLevel.All)) { + if(oper == Operators.GetItem) { + _rule.Target = _rule.MakeReturn(Binder, + Ast.ArrayAccess( + Param0, + ConvertIfNeeded(Param1, typeof(int)) + ) + ); + } else { + _rule.Target = _rule.MakeReturn(Binder, + Ast.Assign( + Ast.ArrayAccess( + Param0, + ConvertIfNeeded(Param1, typeof(int)) + ), + ConvertIfNeeded(Param2, _types[0].GetElementType()) + ) + ); + } + return true; + } + } + + MethodInfo[] defaults = GetMethodsFromDefaults(_types[0].GetDefaultMembers(), oper); + if (defaults.Length != 0) { + MethodBinder binder = MethodBinder.MakeBinder(Binder, + oper == Operators.GetItem ? "get_Item" : "set_Item", + defaults); + + BindingTarget target = binder.MakeBindingTarget(CallTypes.ImplicitInstance, _types); + + if (target.Success) { + if (oper == Operators.GetItem) { + _rule.Target = _rule.MakeReturn(Binder, target.MakeExpression(_rule, _rule.Parameters)); + } else { + + _rule.Target = _rule.MakeReturn(Binder, + Ast.Block( + target.MakeExpression(_rule, _rule.Parameters), + _rule.Parameters[2] + ) + ); + } + } else { + _rule.Target = Binder.MakeInvalidParametersError(target).MakeErrorForRule(_rule, Binder); + } + return true; + } + + return false; + } + + private MethodInfo[] GetMethodsFromDefaults(MemberInfo[] defaults, Operators op) { + List methods = new List(); + foreach (MemberInfo mi in defaults) { + PropertyInfo pi = mi as PropertyInfo; + + if (pi != null) { + if (op == Operators.GetItem) { + MethodInfo method = pi.GetGetMethod(PrivateBinding); + if (method != null) methods.Add(method); + } else if (op == Operators.SetItem) { + MethodInfo method = pi.GetSetMethod(PrivateBinding); + if (method != null) methods.Add(method); + } + } + } + + // if we received methods from both declaring type & base types we need to filter them + Dictionary dict = new Dictionary(); + foreach (MethodInfo mb in methods) { + MethodSignatureInfo args = new MethodSignatureInfo(mb.IsStatic, mb.GetParameters()); + MethodInfo other; + + if (dict.TryGetValue(args, out other)) { + if (other.DeclaringType.IsAssignableFrom(mb.DeclaringType)) { + // derived type replaces... + dict[args] = mb; + } + } else { + dict[args] = mb; + } + } + + return new List(dict.Values).ToArray(); + } + + #endregion + + #region Common helpers + + public Expression Param0 { + get { return GetParamater(0); } + } + + public Expression Param1 { + get { return GetParamater(1); } + } + + public Expression Param2 { + get { return GetParamater(2); } + } + + private Expression GetParamater(int index) { + Expression expr = _rule.Parameters[index]; + if (_types[index].IsAssignableFrom(expr.Type)) return expr; + return Ast.Convert(expr, _types[index]); + } + + private bool TryMakeBindingTarget(MethodInfo[] targets) { + MethodBinder mb = MethodBinder.MakeBinder(Binder, targets[0].Name, targets); + BindingTarget target = mb.MakeBindingTarget(CallTypes.None, _types); + if (target.Success) { + Expression call = target.MakeExpression(_rule, _rule.Parameters); + _rule.Target = _rule.MakeReturn(Binder, call); + return true; + } + return false; + } + + private bool TryMakeInvertedBindingTarget(MethodInfo[] targets) { + MethodBinder mb = MethodBinder.MakeBinder(Binder, targets[0].Name, targets); + BindingTarget target = mb.MakeBindingTarget(CallTypes.None, _types); + if (target.Success) { + Expression call = target.MakeExpression(_rule, _rule.Parameters); + _rule.Target = _rule.MakeReturn(Binder, Ast.Not(call)); + return true; + } + return false; + } + + private static Operators GetInvertedOperator(Operators op) { + switch (op) { + case Operators.LessThan: return Operators.GreaterThanOrEqual; + case Operators.LessThanOrEqual: return Operators.GreaterThan; + case Operators.GreaterThan: return Operators.LessThanOrEqual; + case Operators.GreaterThanOrEqual: return Operators.LessThan; + case Operators.Equals: return Operators.NotEquals; + case Operators.NotEquals: return Operators.Equals; + default: throw new InvalidOperationException(); + } + } + + private Expression ConvertIfNeeded(Expression expression, Type type) { + if (expression.Type != type) { + return Expression.Dynamic( + OldConvertToAction.Make(Binder, type, ConversionResultKind.ExplicitCast), + type, + _rule.Context, + expression + ); + } + return expression; + } + + private void SetErrorTarget(OperatorInfo info) { + _rule.Target = + _rule.MakeError( + AstUtils.ComplexCallHelper( + typeof(BinderOps).GetMethod("BadArgumentsForOperation"), + ArrayUtils.Insert((Expression)Ast.Constant(info.Operator), _rule.Parameters) + ) + ); + } + + private MethodInfo[] GetApplicableMembers(OperatorInfo info) { + return GetApplicableMembers(CompilerHelpers.GetType(_args[0]), info); + } + + private MethodInfo[] GetApplicableMembers(Type t, OperatorInfo info) { + MemberGroup members = Binder.GetMember(Action, t, info.Name); + if (members.Count == 0 && info.AlternateName != null) { + members = Binder.GetMember(Action, t, info.AlternateName); + } + + // filter down to just methods + return FilterNonMethods(t, members); + } + + /// + /// Gets alternate members which are specially recognized by the DLR for specific types when + /// all other member lookup fails. + /// + private MethodInfo[] GetFallbackMembers(Type t, OperatorInfo info) { + // if we have an event we need to make a strongly-typed event handler + + if (t == typeof(EventTracker)) { + EventTracker et = ((EventTracker)_args[0]); + if (info.Operator == Operators.InPlaceAdd) { + AddFallbackMemberTest(t, et); + return new MethodInfo[] { typeof(BinderOps).GetMethod("EventTrackerInPlaceAdd").MakeGenericMethod(et.Event.EventHandlerType) }; + } else if (info.Operator == Operators.InPlaceSubtract) { + AddFallbackMemberTest(t, et); + return new MethodInfo[] { typeof(BinderOps).GetMethod("EventTrackerInPlaceRemove").MakeGenericMethod(et.Event.EventHandlerType) }; + } + } else if (t == typeof(BoundMemberTracker)) { + BoundMemberTracker bmt = ((BoundMemberTracker)_args[0]); + if (bmt.BoundTo.MemberType == TrackerTypes.Event) { + EventTracker et = ((EventTracker)bmt.BoundTo); + + if (info.Operator == Operators.InPlaceAdd) { + AddFallbackMemberTest(t, et); + return new MethodInfo[] { typeof(BinderOps).GetMethod("BoundEventTrackerInPlaceAdd").MakeGenericMethod(et.Event.EventHandlerType) }; + } else if (info.Operator == Operators.InPlaceSubtract) { + AddFallbackMemberTest(t, et); + return new MethodInfo[] { typeof(BinderOps).GetMethod("BoundEventTrackerInPlaceRemove").MakeGenericMethod(et.Event.EventHandlerType) }; + } + } + } + + return new MethodInfo[0]; + } + + private void AddFallbackMemberTest(Type t, EventTracker et) { + if(t == typeof(EventTracker)){ + // + // Test Generated: + // ScriptingRuntimeHelpers.GetEventHandlerType(((EventTracker)args[0]).Event) == et.Event.EventHandlerType + // + _rule.AddTest( + Ast.Equal( + Ast.Call( + typeof(ScriptingRuntimeHelpers).GetMethod("GetEventHandlerType"), + Ast.Property( + Ast.Convert( + _rule.Parameters[0], + typeof(EventTracker) + ), + typeof(EventTracker).GetProperty("Event") + ) + ), + Ast.Constant(et.Event.EventHandlerType) + ) + ); + } else if( t == typeof(BoundMemberTracker)){ + // + // Test Generated: + // ScriptingRuntimeHelpers.GetEventHandlerType(((EventTracker)((BoundMemberTracker)args[0]).BountTo).Event) == et.Event.EventHandlerType + // + _rule.AddTest( + Ast.Equal( + Ast.Call( + typeof(ScriptingRuntimeHelpers).GetMethod("GetEventHandlerType"), + Ast.Property( + Ast.Convert( + Ast.Property( + Ast.Convert( + _rule.Parameters[0], + typeof(BoundMemberTracker) + ), + typeof(BoundMemberTracker).GetProperty("BoundTo") + ), + typeof(EventTracker) + ), + typeof(EventTracker).GetProperty("Event") + ) + ), + Ast.Constant(et.Event.EventHandlerType) + ) + ); + } + } + + private static MethodInfo[] FilterNonMethods(Type t, MemberGroup members) { + List methods = new List(members.Count); + foreach (MemberTracker mi in members) { + if (mi.MemberType == TrackerTypes.Method) { + MethodInfo method = ((MethodTracker)mi).Method ; + + // don't call object methods for None type, but if someone added + // methods to null we'd call those. + if (method.DeclaringType != typeof(object) || t != typeof(Null)) { + methods.Add(method); + } + } + } + + return methods.ToArray(); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/DynamicSiteHelper.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/DynamicSiteHelper.cs new file mode 100644 index 0000000000..b6f08314c6 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/DynamicSiteHelper.cs @@ -0,0 +1,232 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Scripting.Utils; +using System.Reflection.Emit; +using Microsoft.Scripting.Generation; +using System.Reflection; + +namespace Microsoft.Scripting.Actions { + // TODO: replace this class with calls to LambdaExpression.GetDelegateType + public static class DynamicSiteHelpers { + + private static readonly Dictionary _siteCtors = new Dictionary(); + + private delegate object CreateSite(CallSiteBinder binder); + + private const int MaximumArity = 17; + + private static Dictionary, Type> _DelegateTypes; + + private const MethodAttributes CtorAttributes = MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public; + private const MethodImplAttributes ImplAttributes = MethodImplAttributes.Runtime | MethodImplAttributes.Managed; + private const MethodAttributes InvokeAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; + + private static readonly Type[] _DelegateCtorSignature = new Type[] { typeof(object), typeof(IntPtr) }; + + internal static Type MakeCallSiteType(params Type[] types) { + return typeof(CallSite<>).MakeGenericType(MakeCallSiteDelegate(types)); + } + // TODO: do we need this helper? + // if so, should it live on Expression? + internal static Type MakeCallSiteDelegate(params Type[] types) { + Debug.Assert(types != null); + return MakeDelegate(types.AddFirst(typeof(CallSite))); + } + + internal static CallSite MakeSite(CallSiteBinder binder, Type siteType) { + CreateSite ctor; + lock (_siteCtors) { + if (!_siteCtors.TryGetValue(siteType, out ctor)) { + _siteCtors[siteType] = ctor = (CreateSite)Delegate.CreateDelegate(typeof(CreateSite), siteType.GetMethod("Create")); + } + } + + return (CallSite)ctor(binder); + } + + private static Type MakeDelegate(Type[] types) { + return GetStandardDelegateType(types) ?? MakeCustomDelegate(types); + } + + public static Type GetStandardDelegateType(Type[] types) { + ContractUtils.RequiresNotEmpty(types, "types"); + + // Can only used predefined delegates if we have no byref types and + // the arity is small enough to fit in Func<...> or Action<...> + if (types.Length > MaximumArity || Any(types, t => t.IsByRef)) { + return null; + } + + Type result; + if (types[types.Length - 1] == typeof(void)) { + result = GetActionType(RemoveLast(types)); + } else { + result = GetFuncType(types); + } + Debug.Assert(result != null); + return result; + } + + private static Type GetFuncType(Type[] types) { + switch (types.Length) { + #region Generated Delegate Microsoft Scripting Scripting Func Types + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_delegate_func from: generate_dynsites.py + + case 1: return typeof(Func<>).MakeGenericType(types); + case 2: return typeof(Func<,>).MakeGenericType(types); + case 3: return typeof(Func<,,>).MakeGenericType(types); + case 4: return typeof(Func<,,,>).MakeGenericType(types); + case 5: return typeof(Func<,,,,>).MakeGenericType(types); + case 6: return typeof(Func<,,,,,>).MakeGenericType(types); + case 7: return typeof(Func<,,,,,,>).MakeGenericType(types); + case 8: return typeof(Func<,,,,,,,>).MakeGenericType(types); + case 9: return typeof(Func<,,,,,,,,>).MakeGenericType(types); + case 10: return typeof(Func<,,,,,,,,,>).MakeGenericType(types); + case 11: return typeof(Func<,,,,,,,,,,>).MakeGenericType(types); + case 12: return typeof(Func<,,,,,,,,,,,>).MakeGenericType(types); + case 13: return typeof(Func<,,,,,,,,,,,,>).MakeGenericType(types); + case 14: return typeof(Func<,,,,,,,,,,,,,>).MakeGenericType(types); + case 15: return typeof(Func<,,,,,,,,,,,,,,>).MakeGenericType(types); + case 16: return typeof(Func<,,,,,,,,,,,,,,,>).MakeGenericType(types); + case 17: return typeof(Func<,,,,,,,,,,,,,,,,>).MakeGenericType(types); + + // *** END GENERATED CODE *** + + #endregion + + default: return null; + } + } + + private static Type GetActionType(Type[] types) { + switch (types.Length) { + case 0: return typeof(Action); + + #region Generated Delegate Microsoft Scripting Action Types + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_delegate_action from: generate_dynsites.py + + case 1: return typeof(Action<>).MakeGenericType(types); + case 2: return typeof(Action<,>).MakeGenericType(types); + case 3: return typeof(Action<,,>).MakeGenericType(types); + case 4: return typeof(Action<,,,>).MakeGenericType(types); + case 5: return typeof(Action<,,,,>).MakeGenericType(types); + case 6: return typeof(Action<,,,,,>).MakeGenericType(types); + case 7: return typeof(Action<,,,,,,>).MakeGenericType(types); + case 8: return typeof(Action<,,,,,,,>).MakeGenericType(types); + case 9: return typeof(Action<,,,,,,,,>).MakeGenericType(types); + case 10: return typeof(Action<,,,,,,,,,>).MakeGenericType(types); + case 11: return typeof(Action<,,,,,,,,,,>).MakeGenericType(types); + case 12: return typeof(Action<,,,,,,,,,,,>).MakeGenericType(types); + case 13: return typeof(Action<,,,,,,,,,,,,>).MakeGenericType(types); + case 14: return typeof(Action<,,,,,,,,,,,,,>).MakeGenericType(types); + case 15: return typeof(Action<,,,,,,,,,,,,,,>).MakeGenericType(types); + case 16: return typeof(Action<,,,,,,,,,,,,,,,>).MakeGenericType(types); + + // *** END GENERATED CODE *** + + #endregion + default: return null; + } + } + + private static T[] AddFirst(this IList list, T item) { + T[] res = new T[list.Count + 1]; + res[0] = item; + list.CopyTo(res, 1); + return res; + } + + private static bool Any(this IEnumerable source, Func predicate) { + foreach (T element in source) { + if (predicate(element)) { + return true; + } + } + return false; + } + + private static T[] RemoveLast(this T[] array) { + T[] result = new T[array.Length - 1]; + Array.Copy(array, 0, result, 0, result.Length); + return result; + } + + private static Type MakeCustomDelegate(Type[] types) { + if (_DelegateTypes == null) { + Interlocked.CompareExchange( + ref _DelegateTypes, + new Dictionary, Type>(ListEqualityComparer.Instance), + null + ); + } + + bool found; + Type type; + + // + // LOCK to retrieve the delegate type, if any + // + + lock (_DelegateTypes) { + found = _DelegateTypes.TryGetValue(types, out type); + } + + if (!found && type != null) { + return type; + } + + // + // Create new delegate type + // + + type = MakeNewCustomDelegate(types); + + // + // LOCK to insert new delegate into the cache. If we already have one (racing threads), use the one from the cache + // + + lock (_DelegateTypes) { + Type conflict; + if (_DelegateTypes.TryGetValue(types, out conflict) && conflict != null) { + type = conflict; + } else { + _DelegateTypes[types] = type; + } + } + + return type; + } + + private static Type MakeNewCustomDelegate(Type[] types) { + Type returnType = types[types.Length - 1]; + Type[] parameters = types.RemoveLast(); + + TypeBuilder builder = Snippets.Shared.DefineDelegateType("Delegate" + types.Length); + builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _DelegateCtorSignature).SetImplementationFlags(ImplAttributes); + builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes); + return builder.CreateType(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ErrorInfo.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ErrorInfo.cs new file mode 100644 index 0000000000..c63ce62b42 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ErrorInfo.cs @@ -0,0 +1,127 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + /// + /// Encapsulates information about the result that should be produced when + /// a OldDynamicAction cannot be performed. The ErrorInfo can hold one of: + /// an expression which creates an Exception to be thrown + /// an expression which produces a value which should be returned + /// directly to the user and represents an error has occured (for + /// example undefined in JavaScript) + /// an expression which produces a value which should be returned + /// directly to the user but does not actually represent an error. + /// + /// ErrorInfo's are produced by an ActionBinder in response to a failed + /// binding. + /// + public sealed class ErrorInfo { + private readonly Expression _value; + private readonly ErrorInfoKind _kind; + + /// + /// Private constructor - consumers must use static From* factories + /// to create ErrorInfo objects. + /// + private ErrorInfo(Expression value, ErrorInfoKind kind) { + Debug.Assert(value != null); + + _value = value; + _kind = kind; + } + + /// + /// Creates a new ErrorInfo which represents an exception that should + /// be thrown. + /// + public static ErrorInfo FromException(Expression exceptionValue) { + ContractUtils.RequiresNotNull(exceptionValue, "exceptionValue"); + ContractUtils.Requires(typeof(Exception).IsAssignableFrom(exceptionValue.Type), "exceptionValue", Strings.MustBeExceptionInstance); + + return new ErrorInfo(exceptionValue, ErrorInfoKind.Exception); + } + + /// + /// Creates a new ErrorInfo which represents a value which should be + /// returned to the user. + /// + public static ErrorInfo FromValue(Expression resultValue) { + ContractUtils.RequiresNotNull(resultValue, "resultValue"); + + return new ErrorInfo(resultValue, ErrorInfoKind.Error); + } + + /// + /// Crates a new ErrorInfo which represents a value which should be returned + /// to the user but does not represent an error. + /// + /// + /// + public static ErrorInfo FromValueNoError(Expression resultValue) { + ContractUtils.RequiresNotNull(resultValue, "resultValue"); + + return new ErrorInfo(resultValue, ErrorInfoKind.Success); + } + + /// + /// Internal helper to produce the actual expression used for the error when emitting + /// the error into a rule. + /// + public Expression MakeErrorForRule(RuleBuilder rule, ActionBinder binder) { + switch (_kind) { + case ErrorInfoKind.Error: + rule.IsError = true; + return rule.MakeReturn(binder, _value); + case ErrorInfoKind.Success: + return rule.MakeReturn(binder, _value); + case ErrorInfoKind.Exception: + return rule.MakeError(_value); + default: throw new InvalidOperationException(); + } + } + + public ErrorInfoKind Kind { + get { + return _kind; + } + } + + public Expression Expression { + get { + return _value; + } + } + } + + public enum ErrorInfoKind { + /// + /// The ErrorInfo expression produces an exception + /// + Exception, + /// + /// The ErrorInfo expression produces a value which represents the error (e.g. undefined) + /// + Error, + /// + /// The ErrorInfo expression produces a value which is not an error + /// + Success + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/EventTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/EventTracker.cs new file mode 100644 index 0000000000..2e7db4dc9a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/EventTracker.cs @@ -0,0 +1,91 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Actions { + public class EventTracker : MemberTracker { + private EventInfo _event; + + internal EventTracker(EventInfo eventInfo) { + _event = eventInfo; + } + + public override Type DeclaringType { + get { return _event.DeclaringType; } + } + + public override TrackerTypes MemberType { + get { return TrackerTypes.Event; } + } + + public override string Name { + get { return _event.Name; } + } + + public EventInfo Event { + get { + return _event; + } + } + + /// + /// Doesn't need to check PrivateBinding setting: no method that is part of the event is public the entire event is private. + /// If the code has already a reference to the event tracker instance for a private event its "static-ness" is not influenced + /// by private-binding setting. + /// + public bool IsStatic { + get { + MethodInfo mi = Event.GetAddMethod(false) ?? + Event.GetRemoveMethod(false) ?? + Event.GetRaiseMethod(false) ?? + Event.GetAddMethod(true) ?? + Event.GetRemoveMethod(true) ?? + Event.GetRaiseMethod(true); + + MethodInfo m; + Debug.Assert( + ((m = Event.GetAddMethod(true)) == null || m.IsStatic == mi.IsStatic) && + ((m = Event.GetRaiseMethod(true)) == null || m.IsStatic == mi.IsStatic) && + ((m = Event.GetRaiseMethod(true)) == null || m.IsStatic == mi.IsStatic), + "Methods are either all static or all instance." + ); + + return mi.IsStatic; + } + } + + protected internal override Expression GetBoundValue(Expression context, ActionBinder binder, Type type, Expression instance) { + return binder.ReturnMemberTracker(type, new BoundMemberTracker(this, instance)); + } + + public override MemberTracker BindToInstance(Expression instance) { + if (IsStatic) { + return this; + } + + return new BoundMemberTracker(this, instance); + } + + [Confined] + public override string ToString() { + return _event.ToString(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ExtensionBinaryOperationBinder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ExtensionBinaryOperationBinder.cs new file mode 100644 index 0000000000..4b0f010c05 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ExtensionBinaryOperationBinder.cs @@ -0,0 +1,45 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Dynamic.Binders; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + public abstract class ExtensionBinaryOperationBinder : BinaryOperationBinder { + private readonly string _operation; + + protected ExtensionBinaryOperationBinder(string operation) + : base(ExpressionType.Extension) { + ContractUtils.RequiresNotNull(operation, "operation"); + _operation = operation; + } + + public string ExtensionOperation { + get { + return _operation; + } + } + + public override int GetHashCode() { + return base.GetHashCode() ^ _operation.GetHashCode(); + } + + public override bool Equals(object obj) { + var ebob = obj as ExtensionBinaryOperationBinder; + return ebob != null && base.Equals(obj) && _operation == ebob._operation; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ExtensionMethodTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ExtensionMethodTracker.cs new file mode 100644 index 0000000000..e2146dfb1f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ExtensionMethodTracker.cs @@ -0,0 +1,47 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + /// + /// Represents extension method. + /// + public class ExtensionMethodTracker : MethodTracker { + /// + /// The declaring type of the extension (the type this extension method extends) + /// + private readonly Type _declaringType; + + internal ExtensionMethodTracker(MethodInfo method, bool isStatic, Type declaringType) + : base(method, isStatic) { + ContractUtils.RequiresNotNull(declaringType, "declaringType"); + _declaringType = declaringType; + } + + /// + /// The declaring type of the extension method. Since this is an extension method, + /// the declaring type is in fact the type this extension method extends, + /// not Method.DeclaringType + /// + public override Type DeclaringType { + get { + return _declaringType; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ExtensionPropertyTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ExtensionPropertyTracker.cs new file mode 100644 index 0000000000..7bcef2fc18 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ExtensionPropertyTracker.cs @@ -0,0 +1,99 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Actions { + public class ExtensionPropertyTracker : PropertyTracker { + private string _name; + private Type _declaringType; + private MethodInfo _getter, _setter, _deleter; + + public ExtensionPropertyTracker(string name, MethodInfo getter, MethodInfo setter, MethodInfo deleter, Type declaringType) { + _name = name; + _getter = getter; + _setter = setter; + _deleter = deleter; + _declaringType = declaringType; + } + + public override string Name { + get { return _name; } + } + + public override Type DeclaringType { + get { return _declaringType; } + } + + public override bool IsStatic { + get { return IsStaticProperty(GetGetMethod(true) ?? GetSetMethod(true)); } + } + + public override MethodInfo GetGetMethod() { + if (_getter != null && _getter.IsPrivate) return null; + + return _getter; + } + + public override MethodInfo GetSetMethod() { + if (_setter != null && _setter.IsPrivate) return null; + + return _setter; + } + + public override MethodInfo GetGetMethod(bool privateMembers) { + if (privateMembers) return _getter; + + return GetGetMethod(); + } + + public override MethodInfo GetSetMethod(bool privateMembers) { + if (privateMembers) return _setter; + + return GetSetMethod(); + } + + public override MethodInfo GetDeleteMethod() { + if (_deleter != null && _deleter.IsPrivate) return null; + + return _deleter; + } + + public override MethodInfo GetDeleteMethod(bool privateMembers) { + if (privateMembers) return _deleter; + + return GetDeleteMethod(); + } + + public override ParameterInfo[] GetIndexParameters() { + return new ParameterInfo[0]; + } + + private static bool IsStaticProperty(MethodInfo method) { + return method.IsDefined(typeof(StaticExtensionMethodAttribute), false); + } + + public override Type PropertyType { + get { + if (_getter != null) return _getter.ReturnType; + + ParameterInfo[] pis = _setter.GetParameters(); + return pis[pis.Length - 1].ParameterType; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ExtensionUnaryOperationBinder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ExtensionUnaryOperationBinder.cs new file mode 100644 index 0000000000..05f9a278eb --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ExtensionUnaryOperationBinder.cs @@ -0,0 +1,45 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Dynamic.Binders; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + public abstract class ExtensionUnaryOperationBinder : UnaryOperationBinder { + private readonly string _operation; + + protected ExtensionUnaryOperationBinder(string operation) + : base(ExpressionType.Extension) { + ContractUtils.RequiresNotNull(operation, "operation"); + _operation = operation; + } + + public string ExtensionOperation { + get { + return _operation; + } + } + + public override int GetHashCode() { + return GetHashCode() ^ _operation.GetHashCode(); + } + + public override bool Equals(object obj) { + var euob = obj as ExtensionUnaryOperationBinder; + return euob != null && base.Equals(obj) && _operation == euob._operation; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/FieldTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/FieldTracker.cs new file mode 100644 index 0000000000..10017e5dec --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/FieldTracker.cs @@ -0,0 +1,146 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Contracts; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public class FieldTracker : MemberTracker { + private readonly FieldInfo _field; + + public FieldTracker(FieldInfo field) { + ContractUtils.RequiresNotNull(field, "field"); + _field = field; + } + + public override Type DeclaringType { + get { return _field.DeclaringType; } + } + + public override TrackerTypes MemberType { + get { return TrackerTypes.Field; } + } + + public override string Name { + get { return _field.Name; } + } + + public bool IsPublic { + get { + return _field.IsPublic; + } + } + + public bool IsInitOnly { + get { + return _field.IsInitOnly; + } + } + + public bool IsLiteral { + get { + return _field.IsLiteral; + } + } + + public Type FieldType { + get { + return _field.FieldType; + } + } + + public bool IsStatic { + get { + return _field.IsStatic; + } + } + + public FieldInfo Field { + get { + return _field; + } + } + + [Confined] + public override string ToString() { + return _field.ToString(); + } + + #region Public expression builders + + public override Expression GetValue(Expression context, ActionBinder binder, Type type) { + if (Field.IsLiteral) { + return Ast.Constant(Field.GetValue(null)); + } + + if (!IsStatic) { + // return the field tracker... + return binder.ReturnMemberTracker(type, this); + } + + if (Field.DeclaringType.ContainsGenericParameters) { + return null; + } + + if (IsPublic && DeclaringType.IsPublic) { + return Ast.Field(null, Field); + } + + return Ast.Call( + AstUtils.Convert(Ast.Constant(Field), typeof(FieldInfo)), + typeof(FieldInfo).GetMethod("GetValue"), + Ast.Constant(null) + ); + } + + public override ErrorInfo GetError(ActionBinder binder) { + // FieldTracker only has one error - accessing a static field from + // a generic type. + Debug.Assert(Field.DeclaringType.ContainsGenericParameters); + + return binder.MakeContainsGenericParametersError(this); + } + + #endregion + + #region Internal expression builders + + protected internal override Expression GetBoundValue(Expression context, ActionBinder binder, Type type, Expression instance) { + if (IsPublic && DeclaringType.IsVisible) { + return Ast.Field( + Ast.Convert(instance, Field.DeclaringType), + Field + ); + } + + return DefaultBinder.MakeError(((DefaultBinder)binder).MakeNonPublicMemberGetError(context, this, type, instance)); + } + + public override MemberTracker BindToInstance(Expression instance) { + if (IsStatic) return this; + + return new BoundMemberTracker(this, instance); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/GetMemberBinderHelper.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/GetMemberBinderHelper.cs new file mode 100644 index 0000000000..9daa2ba593 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/GetMemberBinderHelper.cs @@ -0,0 +1,226 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Generation; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + using AstUtils = Microsoft.Scripting.Ast.Utils; + + /// + /// Builds a rule for a GetMemberAction. Supports all built-in .NET members, the OperatorMethod + /// GetBoundMember, and StrongBox instances. + /// + /// The RuleMaker sets up it's initial state grabbing the incoming type and the type we should look members up + /// from. It then does the lookup for the members using the current context's ActionBinder and then binds to + /// the returned members if the match is non-ambiguous. + /// + /// The target of the rule is built up using a series of block statements as the body. + /// + public class GetMemberBinderHelper : MemberBinderHelper { + private Expression _instance; // the expression the specifies the instance or null for rule.Parameters[0] + private bool _isStatic; + + public GetMemberBinderHelper(CodeContext context, OldGetMemberAction action, object[] args, RuleBuilder rule) + : base(context, action, args, rule) { + } + + public Expression MakeMemberRuleTarget(Type instanceType, params MemberInfo[] members) { + // This should go away w/ abstract values when it'll be easier to compose rules. + return MakeRuleBody(instanceType, members); + } + + public void MakeNewRule() { + Rule.MakeTest(StrongBoxType ?? CompilerHelpers.GetType(Target)); + Rule.Target = MakeGetMemberTarget(); + } + + public Expression MakeRuleBody(Type type, params MemberInfo[] members) { + return MakeBodyHelper(type, new MemberGroup(members)); + } + + private Expression MakeGetMemberTarget() { + Type type = CompilerHelpers.GetType(Target); + if (typeof(TypeTracker).IsAssignableFrom(type)) { + type = ((TypeTracker)Target).Type; + _isStatic = true; + Rule.AddTest(Ast.Equal(Rule.Parameters[0], Ast.Constant(Arguments[0]))); + } + + if (typeof(NamespaceTracker).IsAssignableFrom(type)) { + Rule.AddTest(Ast.Equal(Rule.Parameters[0], Ast.Constant(Arguments[0]))); + } + + MemberGroup members = Binder.GetMember(Action, type, StringName); + + // if lookup failed try the strong-box type if available. + if (members.Count == 0 && StrongBoxType != null) { + type = StrongBoxType; + StrongBoxType = null; + + members = Binder.GetMember(Action, type, StringName); + } + + return MakeBodyHelper(type, members); + } + + private Expression MakeBodyHelper(Type type, MemberGroup members) { + if (!_isStatic) { + MakeOperatorGetMemberBody(type, "GetCustomMember"); + } + + Expression error; + TrackerTypes memberType = GetMemberType(members, out error); + if (error != null) { + AddToBody(Rule.MakeError(error)); + return Body; + } + + switch (memberType) { + case TrackerTypes.TypeGroup: + case TrackerTypes.Type: MakeTypeBody(type, members); break; + case TrackerTypes.Method: + // turn into a MethodGroup + MakeGenericBodyWorker(type, ReflectionCache.GetMethodGroup(StringName, members), _instance ?? Rule.Parameters[0]); + break; + case TrackerTypes.Event: + case TrackerTypes.Field: + case TrackerTypes.Property: + case TrackerTypes.Constructor: + case TrackerTypes.Custom: + MakeGenericBody(type, members); + break; + case TrackerTypes.All: + // no members were found + if (!_isStatic) { + MakeOperatorGetMemberBody(type, "GetBoundMember"); + } + + MakeMissingMemberRuleForGet(type); + break; + default: throw new InvalidOperationException(memberType.ToString()); + } + return Body; + } + + private void MakeGenericBody(Type type, MemberGroup members) { + MakeGenericBodyWorker(type, members[0], null); + } + + private void MakeGenericBody(Type type, MemberGroup members, Expression instance) { + MakeGenericBodyWorker(type, members[0], instance); + } + + private void MakeGenericBodyWorker(Type type, MemberTracker tracker, Expression instance) { + if (!_isStatic) { + tracker = tracker.BindToInstance(instance ?? Instance); + } + + Expression val = tracker.GetValue(Rule.Context, Binder, type); + Expression newBody; + if (val != null) { + newBody = Rule.MakeReturn(Binder, val); + } else { + newBody = tracker.GetError(Binder).MakeErrorForRule(Rule, Binder); + } + + AddToBody(newBody); + } + + private void MakeTypeBody(Type type, MemberGroup members) { + TypeTracker typeTracker = (TypeTracker)members[0]; + for (int i = 1; i < members.Count; i++) { + typeTracker = TypeGroup.UpdateTypeEntity(typeTracker, (TypeTracker)members[i]); + } + + AddToBody(Rule.MakeReturn(Binder, typeTracker.GetValue(Rule.Context, Binder, type))); + } + + /// if a member-injector is defined-on or registered-for this type call it + protected void MakeOperatorGetMemberBody(Type type, string name) { + MethodInfo getMem = GetMethod(type, name); + if (getMem != null && getMem.IsSpecialName) { + ParameterExpression tmp = Rule.GetTemporary(typeof(object), "getVal"); + AddToBody( + AstUtils.If( + Ast.NotEqual( + Ast.Assign( + tmp, + Binder.MakeCallExpression(Rule.Context, getMem, Instance, Ast.Constant(StringName)) + ), + Ast.Field(null, typeof(OperationFailed).GetField("Value")) + ), + Rule.MakeReturn(Binder, tmp) + ) + ); + } + } + + /// Gets the Expression that represents the instance we're looking up + public new Expression Instance { + get { + if (_instance != null) return _instance; + + return base.Instance; + } + protected set { + _instance = value; + } + } + + private MethodInfo[] GetCallableMethods(MemberGroup members) { + MethodInfo[] methods = new MethodInfo[members.Count]; + + for (int i = 0; i < members.Count; i++) { + methods[i] = CompilerHelpers.GetCallableMethod(((MethodTracker)members[i]).Method, Binder.PrivateBinding); + } + return methods; + } + + #region Error rules + + private void MakeIncorrectArgumentCountError() { + AddToBody( + Rule.MakeError( + MakeIncorrectArgumentExpression(0, 0) + ) + ); + } + + private void MakeGenericPropertyError() { + // TODO: Better exception + AddToBody( + Rule.MakeError( + MakeGenericPropertyExpression() + ) + ); + } + + private void MakeMissingMemberRuleForGet(Type type) { + if (!Action.IsNoThrow) { + MakeMissingMemberError(type); + } else { + AddToBody(Rule.MakeReturn(Binder, Ast.Field(null, typeof(OperationFailed).GetField("Value")))); + Rule.IsError = true; + } + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Interceptor.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Interceptor.cs new file mode 100644 index 0000000000..7135bb1625 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Interceptor.cs @@ -0,0 +1,79 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; + +namespace Microsoft.Scripting.Actions { + /// + /// Interceptor prototype. The interceptor is a call site binder that wraps + /// a real call site binder and can perform arbitrary operations on the expression + /// trees that the wrapped binder produces: + /// * Dumping the trees + /// * Additional rewriting + /// * Static compilation + /// * ... + /// + public static class Interceptor { + public static Expression Intercept(Expression expression) { + InterceptorWalker iw = new InterceptorWalker(); + return iw.Visit(expression); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] + public static LambdaExpression Intercept(LambdaExpression lambda) { + InterceptorWalker iw = new InterceptorWalker(); + return iw.Visit(lambda) as LambdaExpression; + } + + internal class InterceptorSiteBinder : CallSiteBinder { + private readonly CallSiteBinder _binder; + + internal InterceptorSiteBinder(CallSiteBinder binder) { + _binder = binder; + } + + public override object CacheIdentity { + get { return _binder.CacheIdentity; } + } + + public override Expression Bind(object[] args, ReadOnlyCollection parameters, LabelTarget returnLabel) { + Expression binding = _binder.Bind(args, parameters, returnLabel); + + // + // TODO: Implement interceptor action here + // + + // + // Call interceptor recursively to continue intercepting on rules + // + return Interceptor.Intercept(binding); + } + } + + internal class InterceptorWalker : ExpressionVisitor { + protected override Expression VisitDynamic(DynamicExpression node) { + CallSiteBinder binder = node.Binder; + if (!(binder is InterceptorSiteBinder)) { + binder = new InterceptorSiteBinder(binder); + return Expression.MakeDynamic(node.DelegateType, binder, node.Arguments); + } else { + return node; + } + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/MemberBinderHelper.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/MemberBinderHelper.cs new file mode 100644 index 0000000000..2696225c1f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/MemberBinderHelper.cs @@ -0,0 +1,177 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public class MemberBinderHelper : BinderHelper + where TActionKind : OldMemberAction { + + private readonly RuleBuilder _rule; // the rule being produced + private Type _strongBoxType; // null or the specific instantiated type of StrongBox + private object[] _args; // the arguments we're creating a rule for + private Expression _body = Ast.Empty(); // the body of the rule as it's built up + private object _target; + + public MemberBinderHelper(CodeContext context, TActionKind action, object[] args, RuleBuilder rule) + : base(context, action) { + ContractUtils.RequiresNotNull(args, "args"); + if (args.Length == 0) throw new ArgumentException("args must have at least one member"); + + _args = args; + + _target = args[0]; + if (CompilerHelpers.IsStrongBox(_target)) { + _strongBoxType = _target.GetType(); + _target = ((IStrongBox)_target).Value; + } + + _rule = rule; + } + + protected object Target { + get { + return _target; + } + } + + /// Gets the Expression that represents the instance we're looking up + protected Expression Instance { + get { + if (_strongBoxType == null) return _rule.Parameters[0]; + + return Ast.Field( + AstUtils.Convert(_rule.Parameters[0], _strongBoxType), + _strongBoxType.GetField("Value") + ); + } + } + + protected Type StrongBoxType { + get { + return _strongBoxType; + } + set { + _strongBoxType = value; + } + } + + protected RuleBuilder Rule { + get { + return _rule; + } + } + + /// helper to grab the name of the member we're looking up as a string + protected string StringName { + get { return SymbolTable.IdToString(Action.Name); } + } + + protected TrackerTypes GetMemberType(MemberGroup members, out Expression error) { + error = null; + TrackerTypes memberType = TrackerTypes.All; + for (int i = 0; i < members.Count; i++) { + MemberTracker mi = members[i]; + if (mi.MemberType != memberType) { + if (memberType != TrackerTypes.All) { + error = MakeAmbiguousMatchError(members); + return TrackerTypes.All; + } + memberType = mi.MemberType; + } + } + return memberType; + } + + protected Expression MakeGenericPropertyExpression() { + return Ast.New( + typeof(MemberAccessException).GetConstructor(new Type[] { typeof(string) }), + Ast.Constant(StringName) + ); + } + + protected Expression MakeIncorrectArgumentExpression(int provided, int expected) { + return Ast.Call( + typeof(BinderOps).GetMethod("TypeErrorForIncorrectArgumentCount", new Type[] { typeof(string), typeof(int), typeof(int) }), + Ast.Constant(StringName), + Ast.Constant(provided), + Ast.Constant(expected) + ); + } + + private static Expression MakeAmbiguousMatchError(MemberGroup members) { + StringBuilder sb = new StringBuilder(); + foreach (MethodTracker mi in members) { + if (sb.Length != 0) sb.Append(", "); + sb.Append(mi.MemberType); + sb.Append(" : "); + sb.Append(mi.ToString()); + } + + return Ast.New(typeof(AmbiguousMatchException).GetConstructor(new Type[] { typeof(string) }), + Ast.Constant(sb.ToString())); + } + + protected void MakeMissingMemberError(Type type) { + AddToBody(Binder.MakeMissingMemberError(type, StringName).MakeErrorForRule(Rule, Binder)); + } + + protected void MakeReadOnlyMemberError(Type type) { + AddToBody(Binder.MakeReadOnlyMemberError(Rule, type, StringName)); + } + + protected void MakeUndeletableMemberError(Type type) { + AddToBody(Binder.MakeUndeletableMemberError(Rule, type, StringName)); + } + + /// + /// There is no setter on Body. Use AddToBody to extend it instead. + /// + protected Expression Body { + get { + return _body; + } + } + + /// + /// Use this method to extend the Body. It will create BlockStatements as needed. + /// + /// + protected void AddToBody(Expression expression) { + if (_body is DefaultExpression) { + _body = expression; + } else { + _body = Ast.Block(_body, Expression.Void(expression)); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] // TODO: fix + protected object[] Arguments { + get { + return _args; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/MemberGroup.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/MemberGroup.cs new file mode 100644 index 0000000000..139754c121 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/MemberGroup.cs @@ -0,0 +1,100 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Reflection; +using Microsoft.Scripting.Utils; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Actions { + /// + /// MemberGroups are a collection of MemberTrackers which are commonly produced + /// on-demand to talk about the available members. They can consist of a mix of + /// different member types or multiple membes of the same type. + /// + /// The most common source of MemberGroups is from ActionBinder.GetMember. From here + /// the DLR will perform binding to the MemberTrackers frequently producing the value + /// resulted from the user. If the result of the action produces a member it's self + /// the ActionBinder can provide the value exposed to the user via ReturnMemberTracker. + /// + /// ActionBinder provides default functionality for both getting members from a type + /// as well as exposing the members to the user. Getting members from the type maps + /// closely to reflection and exposing them to the user exposes the MemberTrackers + /// directly. + /// + public class MemberGroup : IEnumerable { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly MemberGroup EmptyGroup = new MemberGroup(MemberTracker.EmptyTrackers); + + private readonly MemberTracker[] _members; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "noChecks")] + private MemberGroup(MemberTracker[] members, bool noChecks) { + Assert.NotNullItems(members); + _members = members; + } + + public MemberGroup(params MemberTracker[] members) { + ContractUtils.RequiresNotNullItems(members, "members"); + _members = members; + } + + public MemberGroup(params MemberInfo[] members) { + ContractUtils.RequiresNotNullItems(members, "members"); + + MemberTracker[] trackers = new MemberTracker[members.Length]; + for (int i = 0; i < trackers.Length; i++) { + trackers[i] = MemberTracker.FromMemberInfo(members[i]); + } + + _members = trackers; + } + + internal static MemberGroup CreateInternal(MemberTracker[] members) { + Assert.NotNullItems(members); + return new MemberGroup(members, true); + } + + public int Count { + get { + return _members.Length; + } + } + + public MemberTracker this[int index] { + get { + return _members[index]; + } + } + + #region IEnumerable Members + + [Pure] + public IEnumerator GetEnumerator() { + foreach (MemberTracker tracker in _members) yield return tracker; + } + + #endregion + + #region IEnumerable Members + + [Pure] + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + foreach (MemberTracker tracker in _members) yield return tracker; + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/MemberTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/MemberTracker.cs new file mode 100644 index 0000000000..608abb290f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/MemberTracker.cs @@ -0,0 +1,216 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + /// + /// Represents a logical member of a type. The member could either be real concrete member on a type or + /// an extension member. + /// + /// This seperates the "physical" members that .NET knows exist on types from the members that + /// logically exist on a type. It also provides other abstractions above the level of .NET reflection + /// such as MemberGroups and NamespaceTracker's. + /// + /// It also provides a wrapper around the reflection APIs which cannot be extended from partial trust. + /// + public abstract class MemberTracker { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")] + public static readonly MemberTracker[] EmptyTrackers = new MemberTracker[0]; + + private static readonly Dictionary _trackers = new Dictionary(); + + /// + /// We ensure we only produce one MemberTracker for each member which logically lives on the declaring type. So + /// for example if you get a member from a derived class which is declared on the base class it should be the same + /// as getting the member from the base class. Thats easy enough until you get into extension members here there + /// might be one extension member which is being applied to multiple types. Therefore we need to take into account the + /// extension type when ensuring that we only have 1 MemberTracker ever created. + /// + class MemberKey { + private readonly MemberInfo Member; + private readonly Type Extending; + + public MemberKey(MemberInfo member, Type extending) { + Member = member; + Extending = extending; + } + + public override int GetHashCode() { + int res = Member.GetHashCode(); + if (Extending != null) { + res ^= Extending.GetHashCode(); + } + return res; + } + + public override bool Equals(object obj) { + MemberKey other = obj as MemberKey; + if (other == null) return false; + + return other.Member == Member && + other.Extending == Extending; + } + } + + internal MemberTracker() { + } + + /// + /// The type of member tracker. + /// + public abstract TrackerTypes MemberType { + get; + } + + /// + /// The logical declaring type of the member. + /// + public abstract Type DeclaringType { + get; + } + + /// + /// The name of the member. + /// + public abstract string Name { + get; + } + + public static MemberTracker FromMemberInfo(MemberInfo member) { + return FromMemberInfo(member, null); + } + + public static MemberTracker FromMemberInfo(MemberInfo member, Type extending) { + ContractUtils.RequiresNotNull(member, "member"); + + lock (_trackers) { + MemberTracker res; + MemberKey key = new MemberKey(member, extending); + if (_trackers.TryGetValue(key, out res)) return res; + + switch (member.MemberType) { + case MemberTypes.Constructor: res = new ConstructorTracker((ConstructorInfo)member); break; + case MemberTypes.Event: res = new EventTracker((EventInfo)member); break; + case MemberTypes.Field: res = new FieldTracker((FieldInfo)member); break; + case MemberTypes.Method: + MethodInfo mi = (MethodInfo)member; + if (extending != null) { + res = new ExtensionMethodTracker(mi, member.IsDefined(typeof(StaticExtensionMethodAttribute), false), extending); + } else { + res = new MethodTracker(mi); + } + break; + case MemberTypes.TypeInfo: + case MemberTypes.NestedType: res = new NestedTypeTracker((Type)member); break; + case MemberTypes.Property: res = new ReflectedPropertyTracker((PropertyInfo)member); break; + default: throw Error.UnknownMemberType(member.MemberType); + } + + _trackers[key] = res; + return res; + } + } + + #region Public expression builders + + /// + /// Gets the expression that creates the value. + /// + /// Returns null if it's an error to get the value. The caller can then call GetErrorForGet to get + /// the correct error Expression (or null if they should provide a default). + /// + public virtual Expression GetValue(Expression context, ActionBinder binder, Type type) { + return binder.ReturnMemberTracker(type, this); + } + + /// + /// Gets an expression that assigns a value to the left hand side. + /// + /// Returns null if it's an error to assign to. The caller can then call GetErrorForSet to + /// get the correct error Expression (or null if a default error should be provided). + /// + public virtual Expression SetValue(Expression context, ActionBinder binder, Type type, Expression value) { + return null; + } + + /// + /// Gets an expression that performs a call on the object using the specified arguments. + /// + /// Returns null if it's an error to perform the specific operation. The caller can then call + /// GetErrorsForDoCall to get the correct error Expression (or null if a default error should be provided). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Call")] // TODO: fix + internal virtual Expression Call(Expression context, ActionBinder binder, params Expression[] arguments) { + return null; + } + + #endregion + + #region Public error expression builders + + /// + /// Returns the error associated with getting the value. + /// + /// A null return value indicates that the default error message should be provided by the caller. + /// + public virtual ErrorInfo GetError(ActionBinder binder) { + return null; + } + + /// + /// Returns the error associated with accessing this member via a bound instance. + /// + /// A null return value indicates that the default error message should be provided by the caller. + /// + public virtual ErrorInfo GetBoundError(ActionBinder binder, Expression instance) { + return null; + } + + /// + /// Helper for getting values that have been bound. Called from BoundMemberTracker. Custom member + /// trackers can override this to provide their own behaviors when bound to an instance. + /// + protected internal virtual Expression GetBoundValue(Expression context, ActionBinder binder, Type type, Expression instance) { + return GetValue(context, binder, type); + } + + /// + /// Helper for setting values that have been bound. Called from BoundMemberTracker. Custom member + /// trackers can override this to provide their own behaviors when bound to an instance. + /// + protected internal virtual Expression SetBoundValue(Expression context, ActionBinder binder, Type type, Expression value, Expression instance) { + return SetValue(context, binder, type, instance); + } + + /// + /// Binds the member tracker to the specified instance rturning a new member tracker if binding + /// is possible. If binding is not possible the existing member tracker will be returned. For example + /// binding to a static field results in returning the original MemberTracker. Binding to an instance + /// field results in a new BoundMemberTracker which will get GetBoundValue/SetBoundValue to pass the + /// instance through. + /// + public virtual MemberTracker BindToInstance(Expression instance) { + return this; + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/MethodGroup.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/MethodGroup.cs new file mode 100644 index 0000000000..9183c0bc31 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/MethodGroup.cs @@ -0,0 +1,184 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Actions { + /// + /// MethodGroup's represent a unique collection of method's. Typically this + /// unique set is all the methods which are overloaded by the same name including + /// methods with different arity. These methods represent a single logically + /// overloaded element of a .NET type. + /// + /// The base DLR binders will produce MethodGroup's when provided with a MemberGroup + /// which contains only methods. The MethodGroup's will be unique instances per + /// each unique group of methods. + /// + public class MethodGroup : MemberTracker { + private MethodTracker[] _methods; + private Dictionary _boundGenerics; + + internal MethodGroup(params MethodTracker[] methods) { + _methods = methods; + } + + public override TrackerTypes MemberType { + get { return TrackerTypes.MethodGroup; } + } + + public override Type DeclaringType { + get { return _methods[0].DeclaringType; } + } + + public override string Name { + get { return _methods[0].Name; } + } + + public bool ContainsInstance { + get { + foreach (MethodTracker mt in _methods) { + if (!mt.IsStatic) return true; + } + return false; + } + } + + public bool ContainsStatic { + get { + foreach (MethodTracker mt in _methods) { + if (mt.IsStatic) return true; + } + return false; + } + } + + public IList Methods { + get { + return _methods; + } + } + + public MethodBase[] GetMethodBases() { + MethodBase[] methods = new MethodBase[Methods.Count]; + for (int i = 0; i < Methods.Count; i++) { + methods[i] = Methods[i].Method; + } + return methods; + } + + public override Expression GetValue(Expression context, ActionBinder binder, Type type) { + return base.GetValue(context, binder, type); + } + + public override MemberTracker BindToInstance(Expression instance) { + if (ContainsInstance) { + return new BoundMemberTracker(this, instance); + } + + return this; + } + + protected internal override Expression GetBoundValue(Expression context, ActionBinder binder, Type type, Expression instance) { + return binder.ReturnMemberTracker(type, BindToInstance(instance)); + } + + /// + /// Returns a BuiltinFunction bound to the provided type arguments. Returns null if the binding + /// cannot be performed. + /// + public MethodGroup MakeGenericMethod(Type[] types) { + TypeList tl = new TypeList(types); + + // check for cached method first... + MethodGroup mg; + if (_boundGenerics != null) { + lock (_boundGenerics) { + if (_boundGenerics.TryGetValue(tl, out mg)) { + return mg; + } + } + } + + // Search for generic targets with the correct arity (number of type parameters). + // Compatible targets must be MethodInfos by definition (constructors never take + // type arguments). + List targets = new List(Methods.Count); + foreach (MethodTracker mt in Methods) { + MethodInfo mi = mt.Method; + if (mi.ContainsGenericParameters && mi.GetGenericArguments().Length == types.Length) + targets.Add((MethodTracker)MemberTracker.FromMemberInfo(mi.MakeGenericMethod(types))); + } + + if (targets.Count == 0) { + return null; + } + + // Build a new MethodGroup that will contain targets with bound type arguments & cache it. + mg = new MethodGroup(targets.ToArray()); + + EnsureBoundGenericDict(); + + lock (_boundGenerics) { + _boundGenerics[tl] = mg; + } + + return mg; + } + + private void EnsureBoundGenericDict() { + if (_boundGenerics == null) { + Interlocked.CompareExchange>( + ref _boundGenerics, + new Dictionary(1), + null); + } + } + + private class TypeList { + private Type[] _types; + + public TypeList(Type[] types) { + Debug.Assert(types != null); + _types = types; + } + + [Confined] + public override bool Equals(object obj) { + TypeList tl = obj as TypeList; + if (tl == null || _types.Length != tl._types.Length) return false; + + for (int i = 0; i < _types.Length; i++) { + if (_types[i] != tl._types[i]) return false; + } + return true; + } + + [Confined] + public override int GetHashCode() { + int hc = 6551; + foreach (Type t in _types) { + hc = (hc << 5) ^ t.GetHashCode(); + } + return hc; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/MethodTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/MethodTracker.cs new file mode 100644 index 0000000000..c5ae847c8f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/MethodTracker.cs @@ -0,0 +1,119 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Utils; +using Microsoft.Contracts; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + public class MethodTracker : MemberTracker { + private readonly MethodInfo _method; + private readonly bool _isStatic; + + internal MethodTracker(MethodInfo method) { + ContractUtils.RequiresNotNull(method, "method"); + _method = method; + _isStatic = method.IsStatic; + } + + internal MethodTracker(MethodInfo method, bool isStatic) { + ContractUtils.RequiresNotNull(method, "method"); + _method = method; + _isStatic = isStatic; + } + + public override Type DeclaringType { + get { return _method.DeclaringType; } + } + + public override TrackerTypes MemberType { + get { return TrackerTypes.Method; } + } + + public override string Name { + get { return _method.Name; } + } + + public MethodInfo Method { + get { + return _method; + } + } + + public bool IsPublic { + get { + return _method.IsPublic; + } + } + + public bool IsStatic { + get { + return _isStatic; + } + } + + [Confined] + public override string ToString() { + return _method.ToString(); + } + + public override MemberTracker BindToInstance(Expression instance) { + if (IsStatic) { + return this; + } + + return new BoundMemberTracker(this, instance); + } + + protected internal override Expression GetBoundValue(Expression context, ActionBinder binder, Type type, Expression instance) { + return binder.ReturnMemberTracker(type, BindToInstance(instance)); + } + + internal override System.Linq.Expressions.Expression Call(Expression context, ActionBinder binder, params Expression[] arguments) { + if (Method.IsPublic && Method.DeclaringType.IsVisible) { + // TODO: Need to use MethodBinder in here to make this right. + return binder.MakeCallExpression(context, Method, arguments); + } + + //methodInfo.Invoke(obj, object[] params) + if (Method.IsStatic) { + return Ast.Convert( + Ast.Call( + Ast.Constant(Method), + typeof(MethodInfo).GetMethod("Invoke", new Type[] { typeof(object), typeof(object[]) }), + Ast.Constant(null), + AstUtils.NewArrayHelper(typeof(object), arguments) + ), + Method.ReturnType); + } + + if (arguments.Length == 0) throw Error.NoInstanceForCall(); + + return Ast.Convert( + Ast.Call( + Ast.Constant(Method), + typeof(MethodInfo).GetMethod("Invoke", new Type[] { typeof(object), typeof(object[]) }), + arguments[0], + AstUtils.NewArrayHelper(typeof(object), ArrayUtils.RemoveFirst(arguments)) + ), + Method.ReturnType); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/NamespaceTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/NamespaceTracker.cs new file mode 100644 index 0000000000..dd30521ad9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/NamespaceTracker.cs @@ -0,0 +1,615 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Utils; +using System.Threading; +using Microsoft.Contracts; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Actions { + public class AssemblyLoadedEventArgs : EventArgs { + private Assembly _assembly; + + public AssemblyLoadedEventArgs(Assembly assembly) { + _assembly = assembly; + } + + public Assembly Assembly { + get { + return _assembly; + } + } + } + + /// + /// NamespaceTracker represent a CLS namespace. + /// + public class NamespaceTracker : MemberTracker, IOldDynamicObject, IAttributesCollection, IMembersList { + // _dict contains all the currently loaded entries. However, there may be pending types that have + // not yet been loaded in _typeNames + internal Dictionary _dict = new Dictionary(); + + internal readonly List _packageAssemblies = new List(); + internal readonly Dictionary _typeNames = new Dictionary(); + + private string _fullName; // null for the TopReflectedPackage + private TopNamespaceTracker _topPackage; + private int _id; + + private static int _masterId; + + #region Protected API Surface + + private NamespaceTracker() { + UpdateId(); + } + + [Confined] + public override string ToString() { + return base.ToString() + ":" + _fullName; + } + + protected NamespaceTracker(string name) + : this() { + _fullName = name; + } + + #endregion + + #region Internal API Surface + + internal NamespaceTracker GetOrMakeChildPackage(string childName, Assembly assem) { + // lock is held when this is called + Assert.NotNull(childName, assem); + Debug.Assert(childName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name + Debug.Assert(_packageAssemblies.Contains(assem)); // Parent namespace must contain all the assemblies of the child + + MemberTracker ret; + if (_dict.TryGetValue(childName, out ret)) { + // If we have a module, then we add the assembly to the InnerModule + // If it's not a module, we'll wipe it out below, eg "def System(): pass" then + // "import System" will result in the namespace being visible. + NamespaceTracker package = ret as NamespaceTracker; + if (package != null) { + if (!package._packageAssemblies.Contains(assem)) { + package._packageAssemblies.Add(assem); + package.UpdateId(); + } + return package; + } + } + + return MakeChildPackage(childName, assem); + } + + private NamespaceTracker MakeChildPackage(string childName, Assembly assem) { + // lock is held when this is called + Assert.NotNull(childName, assem); + NamespaceTracker rp = new NamespaceTracker(); + rp.SetTopPackage(_topPackage); + rp._packageAssemblies.Add(assem); + + rp._fullName = GetFullChildName(childName); + _dict[childName] = rp; + return rp; + } + + private string GetFullChildName(string childName) { + Assert.NotNull(childName); + Debug.Assert(childName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name + if (_fullName == null) { + return childName; + } + + return _fullName + Type.Delimiter + childName; + } + + private static Type LoadType(Assembly assem, string fullTypeName) { + Assert.NotNull(assem, fullTypeName); + Type type = assem.GetType(fullTypeName); + // We should ignore nested types. They will be loaded when the containing type is loaded + Debug.Assert(!ReflectionUtils.IsNested(type)); + return type; + } + + internal void AddTypeName(string typeName, Assembly assem) { + // lock is held when this is called + Assert.NotNull(typeName, assem); + Debug.Assert(typeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name + + if (!_typeNames.ContainsKey(assem)) { + _typeNames[assem] = new TypeNames(assem, _fullName); + } + _typeNames[assem].AddTypeName(typeName); + + string normalizedTypeName = ReflectionUtils.GetNormalizedTypeName(typeName); + if (_dict.ContainsKey(normalizedTypeName)) { + // A similarly named type, namespace, or module already exists. + Type newType = LoadType(assem, GetFullChildName(typeName)); + + object existingValue = _dict[normalizedTypeName]; + TypeTracker existingTypeEntity = existingValue as TypeTracker; + if (existingTypeEntity == null) { + // Replace the existing namespace or module with the new type + Debug.Assert(existingValue is NamespaceTracker); + _dict[normalizedTypeName] = MemberTracker.FromMemberInfo(newType); + } else { + // Unify the new type with the existing type + _dict[normalizedTypeName] = TypeGroup.UpdateTypeEntity(existingTypeEntity, ReflectionCache.GetTypeTracker(newType)); + } + return; + } + } + + /// + /// Loads all the types from all assemblies that contribute to the current namespace (but not child namespaces) + /// + private void LoadAllTypes() { + foreach (TypeNames typeNameList in _typeNames.Values) { + foreach (string typeName in typeNameList.GetNormalizedTypeNames()) { + object value; + if (!TryGetValue(SymbolTable.StringToId(typeName), out value)) { + Debug.Assert(false, "We should never get here as TryGetMember should raise a TypeLoadException"); + throw new TypeLoadException(typeName); + } + } + } + } + + #endregion + + public override string Name { + get { + return _fullName; + } + } + + protected void DiscoverAllTypes(Assembly assem) { + // lock is held when this is called + Assert.NotNull(assem); + + NamespaceTracker previousPackage = null; + string previousFullNamespace = String.Empty; // Note that String.Empty is not a valid namespace + + foreach (TypeName typeName in AssemblyTypeNames.GetTypeNames(assem, _topPackage.DomainManager.Configuration.PrivateBinding)) { + NamespaceTracker package; + Debug.Assert(typeName.Namespace != String.Empty); + if (typeName.Namespace == previousFullNamespace) { + // We have a cache hit. We dont need to call GetOrMakePackageHierarchy (which generates + // a fair amount of temporary substrings) + package = previousPackage; + } else { + package = GetOrMakePackageHierarchy(assem, typeName.Namespace); + previousFullNamespace = typeName.Namespace; + previousPackage = package; + } + + package.AddTypeName(typeName.Name, assem); + } + } + + /// + /// Populates the tree with nodes for each part of the namespace + /// + /// + /// Full namespace name. It can be null (for top-level types) + /// + private NamespaceTracker GetOrMakePackageHierarchy(Assembly assem, string fullNamespace) { + // lock is held when this is called + Assert.NotNull(assem); + + if (fullNamespace == null) { + // null is the top-level namespace + return this; + } + + NamespaceTracker ret = this; + string[] pieces = fullNamespace.Split(Type.Delimiter); + for (int i = 0; i < pieces.Length; i++) { + ret = ret.GetOrMakeChildPackage(pieces[i], assem); + } + + return ret; + } + /// + /// As a fallback, so if the type does exist in any assembly. This would happen if a new type was added + /// that was not in the hardcoded list of types. + /// This code is not accurate because: + /// 1. We dont deal with generic types (TypeCollision). + /// 2. Previous calls to GetCustomMemberNames (eg. "from foo import *" in Python) would not have included this type. + /// 3. This does not deal with new namespaces added to the assembly + /// + private MemberTracker CheckForUnlistedType(string nameString) { + Assert.NotNull(nameString); + + string fullTypeName = GetFullChildName(nameString); + foreach (Assembly assem in _packageAssemblies) { + Type type = assem.GetType(fullTypeName, false); + if (type == null || ReflectionUtils.IsNested(type)) { + continue; + } + + bool publishType = type.IsPublic || _topPackage.DomainManager.Configuration.PrivateBinding; + if (!publishType) { + continue; + } + + // We dont use TypeCollision.UpdateTypeEntity here because we do not handle generic type names + return ReflectionCache.GetTypeTracker(type); + } + + return null; + } + + #region IAttributesCollection Members + + public void Add(SymbolId name, object value) { + throw new InvalidOperationException(); + } + + public bool TryGetValue(SymbolId name, out object value) { + MemberTracker tmp; + bool res = TryGetValue(name, out tmp); + value = tmp; + return res; + } + + public bool TryGetValue(SymbolId name, out MemberTracker value) { + lock (this) { + LoadNamespaces(); + + if (_dict.TryGetValue(SymbolTable.IdToString(name), out value)) { + return true; + } + + MemberTracker existingTypeEntity = null; + string nameString = SymbolTable.IdToString(name); + + if (nameString.IndexOf(Type.Delimiter) != -1) { + value = null; + return false; + } + + // Look up the type names and load the type if its name exists + + foreach (KeyValuePair kvp in _typeNames) { + if (!kvp.Value.Contains(nameString)) { + continue; + } + + existingTypeEntity = kvp.Value.UpdateTypeEntity((TypeTracker)existingTypeEntity, nameString); + } + + if (existingTypeEntity == null) { + existingTypeEntity = CheckForUnlistedType(nameString); + } + + if (existingTypeEntity != null) { + _dict[SymbolTable.IdToString(name)] = existingTypeEntity; + value = existingTypeEntity; + return true; + } + + return false; + } + } + + public bool Remove(SymbolId name) { + throw new InvalidOperationException(); + } + + public bool ContainsKey(SymbolId name) { + object dummy; + return TryGetValue(name, out dummy); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + public object this[SymbolId name] { + get { + object res; + if (TryGetValue(name, out res)) { + return res; + } + throw new KeyNotFoundException(); + } + set { + throw new InvalidOperationException(); + } + } + + public IDictionary SymbolAttributes { + get { + LoadNamespaces(); + + Dictionary res = new Dictionary(); + foreach (KeyValuePair kvp in this) { + string strkey = kvp.Key as string; + if (strkey != null) { + res[SymbolTable.StringToId(strkey)] = kvp.Value; + } + } + + return res; + } + } + + public void AddObjectKey(object name, object value) { + throw new InvalidOperationException(); + } + + public bool TryGetObjectValue(object name, out object value) { + string str = name as string; + if (str != null) { + return TryGetValue(SymbolTable.StringToId(str), out value); + } + + value = null; + return false; + } + + public bool RemoveObjectKey(object name) { + throw new InvalidOperationException(); + } + + public bool ContainsObjectKey(object name) { + object dummy; + return TryGetObjectValue(name, out dummy); + } + + public IDictionary AsObjectKeyedDictionary() { + LoadNamespaces(); + + lock (this) { + Dictionary res = new Dictionary(); + foreach (KeyValuePair kvp in _dict) { + res[kvp.Key] = kvp.Value; + } + return res; + } + } + + public int Count { + get { return _dict.Count; } + } + + public ICollection Keys { + get { + LoadNamespaces(); + + lock (this) { + List res = new List(); + foreach (string s in _dict.Keys) res.Add(s); + + foreach (KeyValuePair kvp in _typeNames) { + foreach (string typeName in kvp.Value.GetNormalizedTypeNames()) { + if (!res.Contains(typeName)) { + res.Add(typeName); + } + } + } + + res.Sort(); + return res; + } + } + } + + #endregion + + #region IEnumerable> Members + + [Pure] + public IEnumerator> GetEnumerator() { + foreach (object key in Keys) { + yield return new KeyValuePair(key, this[SymbolTable.StringToId((string)key)]); + } + } + + #endregion + + #region IEnumerable Members + + [Pure] + IEnumerator IEnumerable.GetEnumerator() { + foreach (object key in Keys) { + yield return new KeyValuePair(key, this[SymbolTable.StringToId((string)key)]); + } + } + + #endregion + + public IList PackageAssemblies { + get { + LoadNamespaces(); + + return _packageAssemblies; + } + } + + protected virtual void LoadNamespaces() { + if (_topPackage != null) { + _topPackage.LoadNamespaces(); + } + } + + protected void SetTopPackage(TopNamespaceTracker pkg) { + Assert.NotNull(pkg); + _topPackage = pkg; + } + + /// + /// This stores all the public non-nested type names in a single namespace and from a single assembly. + /// This allows inspection of the namespace without eagerly loading all the types. Eagerly loading + /// types slows down startup, increases working set, and is semantically incorrect as it can trigger + /// TypeLoadExceptions sooner than required. + /// + internal class TypeNames { + List _simpleTypeNames = new List(); + Dictionary> _genericTypeNames = new Dictionary>(); + + Assembly _assembly; + string _fullNamespace; + + internal TypeNames(Assembly assembly, string fullNamespace) { + _assembly = assembly; + _fullNamespace = fullNamespace; + } + + internal bool Contains(string normalizedTypeName) { + Debug.Assert(normalizedTypeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name + Debug.Assert(ReflectionUtils.GetNormalizedTypeName(normalizedTypeName) == normalizedTypeName); + + return _simpleTypeNames.Contains(normalizedTypeName) || _genericTypeNames.ContainsKey(normalizedTypeName); + } + + internal MemberTracker UpdateTypeEntity(TypeTracker existingTypeEntity, string normalizedTypeName) { + Debug.Assert(normalizedTypeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name + Debug.Assert(ReflectionUtils.GetNormalizedTypeName(normalizedTypeName) == normalizedTypeName); + + // Look for a non-generic type + if (_simpleTypeNames.Contains(normalizedTypeName)) { + Type newType = LoadType(_assembly, GetFullChildName(normalizedTypeName)); + existingTypeEntity = TypeGroup.UpdateTypeEntity(existingTypeEntity, ReflectionCache.GetTypeTracker(newType)); + } + + // Look for generic types + if (_genericTypeNames.ContainsKey(normalizedTypeName)) { + List actualNames = _genericTypeNames[normalizedTypeName]; + foreach (string actualName in actualNames) { + Type newType = LoadType(_assembly, GetFullChildName(actualName)); + existingTypeEntity = TypeGroup.UpdateTypeEntity(existingTypeEntity, ReflectionCache.GetTypeTracker(newType)); + } + } + + return existingTypeEntity; + } + + internal void AddTypeName(string typeName) { + Debug.Assert(typeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name + + string normalizedName = ReflectionUtils.GetNormalizedTypeName(typeName); + if (normalizedName == typeName) { + _simpleTypeNames.Add(typeName); + } else { + List actualNames; + if (_genericTypeNames.ContainsKey(normalizedName)) { + actualNames = _genericTypeNames[normalizedName]; + } else { + actualNames = new List(); + _genericTypeNames[normalizedName] = actualNames; + } + actualNames.Add(typeName); + } + } + + string GetFullChildName(string childName) { + Debug.Assert(childName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name + if (_fullNamespace == null) { + return childName; + } + + return _fullNamespace + Type.Delimiter + childName; + } + + internal ICollection GetNormalizedTypeNames() { + List normalizedTypeNames = new List(); + + normalizedTypeNames.AddRange(_simpleTypeNames); + normalizedTypeNames.AddRange(_genericTypeNames.Keys); + + return normalizedTypeNames; + } + } + + #region IOldDynamicObject Members + + public bool GetRule(OldDynamicAction action, CodeContext context, object[] args, RuleBuilder rule) { + if (action.Kind == DynamicActionKind.GetMember) { + return MakeGetMemberRule((OldGetMemberAction)action, context, rule); + } + return false; + } + + private bool MakeGetMemberRule(OldGetMemberAction action, CodeContext context, RuleBuilder rule) { + object value; + if (TryGetValue(action.Name, out value)) { + Debug.Assert(value is MemberTracker); + MemberTracker memValue = (MemberTracker)value; + + rule.MakeTest(typeof(NamespaceTracker)); + rule.AddTest( + Expression.Equal( + Expression.Property( + Expression.Convert(rule.Parameters[0], typeof(NamespaceTracker)), + typeof(NamespaceTracker).GetProperty("Id") + ), + Expression.Constant(Id) + ) + ); + + + Expression target = context.LanguageContext.Binder.ReturnMemberTracker(memValue.DeclaringType, memValue); + + rule.Target = rule.MakeReturn(context.LanguageContext.Binder, target); + return true; + } + return false; + } + + #endregion + + public int Id { + get { + return _id; + } + } + + #region IMembersList Members + + public IList GetMemberNames(CodeContext context) { + return (IList)Keys; + } + + #endregion + + public override TrackerTypes MemberType { + get { return TrackerTypes.Namespace; } + } + + public override Type DeclaringType { + get { return null; } + } + + protected void UpdateId() { + _id = Interlocked.Increment(ref _masterId); + foreach (KeyValuePair kvp in _dict) { + NamespaceTracker ns = kvp.Value as NamespaceTracker; + if (ns == null) continue; + + lock (ns) { + // namespace trackers are trees so we always take this + // in order + ns.UpdateId(); + } + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/NestedTypeTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/NestedTypeTracker.cs new file mode 100644 index 0000000000..288e622176 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/NestedTypeTracker.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Actions { + public class NestedTypeTracker : TypeTracker { + private Type _type; + + public NestedTypeTracker(Type type) { + _type = type; + } + + public override Type DeclaringType { + get { return _type.DeclaringType; } + } + + public override TrackerTypes MemberType { + get { return TrackerTypes.Type; } + } + + public override string Name { + get { return _type.Name; } + } + + public override bool IsPublic { + get { + return _type.IsPublic; + } + } + + public override Type Type { + get { return _type; } + } + + public override bool IsGenericType { + get { return _type.IsGenericType; } + } + + [Confined] + public override string ToString() { + return _type.ToString(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/NoSideEffectsAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/NoSideEffectsAttribute.cs new file mode 100644 index 0000000000..627f093506 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/NoSideEffectsAttribute.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Scripting.Actions { + /// + /// Marks a method as not having side effects. used by the combo binder + /// to allow calls to methods. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor)] + public sealed class NoSideEffectsAttribute : Attribute { + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldCallAction.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldCallAction.cs new file mode 100644 index 0000000000..50650df6d9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldCallAction.cs @@ -0,0 +1,96 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + + +using System; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using Microsoft.Contracts; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + // TODO: rename to match InvocationExpression + public class OldCallAction : OldDynamicAction, IEquatable, IExpressionSerializable { + private readonly ActionBinder _binder; + private readonly CallSignature _signature; + + protected OldCallAction(ActionBinder binder, CallSignature callSignature) { + _binder = binder; + _signature = callSignature; + } + + public static OldCallAction Make(ActionBinder binder, CallSignature signature) { + ContractUtils.RequiresNotNull(binder, "binder"); + return new OldCallAction(binder, signature); + } + + public static OldCallAction Make(ActionBinder binder, int argumentCount) { + ContractUtils.Requires(argumentCount >= 0, "argumentCount"); + ContractUtils.RequiresNotNull(binder, "binder"); + return new OldCallAction(binder, new CallSignature(argumentCount)); + } + + public ActionBinder Binder { + get { + return _binder; + } + } + + public CallSignature Signature { + get { return _signature; } + } + + public override DynamicActionKind Kind { + get { return DynamicActionKind.Call; } + } + + public override Expression Bind(object[] args, ReadOnlyCollection parameters, LabelTarget returnLabel) { + return Binder.Bind(this, args, parameters, returnLabel); + } + + [StateIndependent] + public bool Equals(OldCallAction other) { + if (other == null || other.GetType() != GetType()) return false; + if ((object)_binder != (object)other._binder) return false; + return _signature.Equals(other._signature); + } + + [Confined] + public override bool Equals(object obj) { + return Equals(obj as OldCallAction); + } + + [Confined] + public override int GetHashCode() { + return ((int)Kind << 28 ^ _signature.GetHashCode()) ^ System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(_binder); + } + + [Confined] + public override string ToString() { + return base.ToString() + _signature.ToString(); + } + + public virtual Expression CreateExpression() { + return Expression.Call( + typeof(OldCallAction).GetMethod("Make", new Type[] { typeof(ActionBinder), typeof(CallSignature) }), + CreateActionBinderReadExpression(), + Signature.CreateExpression() + ); + } + } +} + + diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldConvertToAction.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldConvertToAction.cs new file mode 100644 index 0000000000..d12e6cd3a8 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldConvertToAction.cs @@ -0,0 +1,123 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using Microsoft.Contracts; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + + public class OldConvertToAction : OldDynamicAction, IEquatable, IExpressionSerializable { + private readonly ActionBinder _binder; + private readonly Type _type; + private readonly ConversionResultKind _resultKind; + + public static OldConvertToAction Make(ActionBinder binder, Type type) { + return Make(binder, type, ConversionResultKind.ImplicitCast); + } + + public static OldConvertToAction Make(ActionBinder binder, Type type, ConversionResultKind resultKind) { + ContractUtils.RequiresNotNull(binder, "binder"); + ContractUtils.RequiresNotNull(type, "type"); + return new OldConvertToAction(binder, type, resultKind); + } + + private OldConvertToAction(ActionBinder binder, Type type, ConversionResultKind resultKind) { + _binder = binder; + _type = type; + _resultKind = resultKind; + } + + public Type ToType { get { return _type; } } + public ConversionResultKind ResultKind { get { return _resultKind; } } + public override DynamicActionKind Kind { get { return DynamicActionKind.ConvertTo; } } + + public override Expression Bind(object[] args, ReadOnlyCollection parameters, LabelTarget returnLabel) { + return _binder.Bind(this, args, parameters, returnLabel); + } + + [Confined] + public override bool Equals(object obj) { + return Equals(obj as OldConvertToAction); + } + + [Confined] + public override int GetHashCode() { + return (int)Kind << 28 ^ (int)ResultKind ^ _type.GetHashCode() ^ System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(_binder); + } + + [Confined] + public override string ToString() { + return base.ToString() + " to " + _type.ToString(); + } + + public Expression CreateExpression() { + return Expression.Call( + typeof(OldConvertToAction).GetMethod("Make", new Type[] { typeof(ActionBinder), typeof(Type), typeof(ConversionResultKind) }), + CreateActionBinderReadExpression(), + Expression.Constant(_type), + Expression.Constant(_resultKind) + ); + } + + #region IEquatable Members + + [StateIndependent] + public bool Equals(OldConvertToAction other) { + if (other == null) return false; + if ((object)_binder != (object)other._binder) return false; + return _type == other._type && _resultKind == other._resultKind; + } + + #endregion + } + + /// + /// Determines the result of a conversion action. The result can either result in an exception, a value that + /// has been successfully converted or default(T), or a true/false result indicating if the value can be converted. + /// + public enum ConversionResultKind { + /// + /// Attempts to perform available implicit conversions and throws if there are no available conversions. + /// + ImplicitCast, + /// + /// Attempst to perform available implicit and explicit conversions and throws if there are no available conversions. + /// + ExplicitCast, + /// + /// Attempts to perform available implicit conversions and returns default(ReturnType) if no conversions can be performed. + /// + /// If the return type of the rule is a value type then the return value will be zero-initialized. If the return type + /// of the rule is object or another class then the return type will be null (even if the conversion is to a value type). + /// This enables ImplicitTry to be used to do TryConvertTo even if the type is value type (and the difference between + /// null and a real value can be distinguished). + /// + ImplicitTry, + /// + /// Attempts to perform available implicit and explicit conversions and returns default(ReturnType) if no conversions + /// can be performed. + /// + /// If the return type of the rule is a value type then the return value will be zero-initialized. If the return type + /// of the rule is object or another class then the return type will be null (even if the conversion is to a value type). + /// This enables ExplicitTry to be used to do TryConvertTo even if the type is value type (and the difference between + /// null and a real value can be distinguished). + /// + ExplicitTry + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldCreateInstanceAction.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldCreateInstanceAction.cs new file mode 100644 index 0000000000..bb1d50465d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldCreateInstanceAction.cs @@ -0,0 +1,56 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Utils; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Actions { + public class OldCreateInstanceAction : OldCallAction, IEquatable { + protected OldCreateInstanceAction(ActionBinder binder, CallSignature callSignature) + : base(binder, callSignature) { + } + + public static new OldCreateInstanceAction Make(ActionBinder binder, CallSignature signature) { + return new OldCreateInstanceAction(binder, signature); + } + + public static new OldCreateInstanceAction Make(ActionBinder binder, int argumentCount) { + ContractUtils.Requires(argumentCount >= 0, "argumentCount"); + return new OldCreateInstanceAction(binder, new CallSignature(argumentCount)); + } + + [Confined] + public override bool Equals(object obj) { + return Equals(obj as OldCreateInstanceAction); + } + + [StateIndependent] + public bool Equals(OldCreateInstanceAction other) { + return base.Equals(other); + } + + [Confined] + public override int GetHashCode() { + return base.GetHashCode(); + } + + public override DynamicActionKind Kind { + get { + return DynamicActionKind.CreateInstance; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldDeleteMemberAction.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldDeleteMemberAction.cs new file mode 100644 index 0000000000..7b1199848f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldDeleteMemberAction.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using Microsoft.Contracts; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + public class OldDeleteMemberAction : OldMemberAction, IEquatable, IExpressionSerializable { + private OldDeleteMemberAction(ActionBinder binder, SymbolId name) + : base(binder, name) { + } + + public static OldDeleteMemberAction Make(ActionBinder binder, string name) { + return Make(binder, SymbolTable.StringToId(name)); + } + + public static OldDeleteMemberAction Make(ActionBinder binder, SymbolId name) { + ContractUtils.RequiresNotNull(binder, "binder"); + return new OldDeleteMemberAction(binder, name); + } + + public override DynamicActionKind Kind { + get { return DynamicActionKind.DeleteMember; } + } + + public Expression CreateExpression() { + return Expression.Call( + typeof(OldDeleteMemberAction).GetMethod("Make", new Type[] { typeof(ActionBinder), typeof(SymbolId) }), + CreateActionBinderReadExpression(), + AstUtils.Constant(Name) + ); + } + + #region IEquatable Members + + [StateIndependent] + public bool Equals(OldDeleteMemberAction other) { + if (other == null) return false; + + return base.Equals(other); + } + + #endregion + + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldDoOperationAction.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldDoOperationAction.cs new file mode 100644 index 0000000000..7d7f4c669a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldDoOperationAction.cs @@ -0,0 +1,121 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using Microsoft.Contracts; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + + public class OldDoOperationAction : OldDynamicAction, IExpressionSerializable { + private readonly ActionBinder _binder; + private readonly Operators _operation; + + public static OldDoOperationAction Make(ActionBinder binder, Operators operation) { + ContractUtils.RequiresNotNull(binder, "binder"); + return new OldDoOperationAction(binder, operation); + } + + private OldDoOperationAction(ActionBinder binder, Operators operation) { + _binder = binder; + _operation = operation; + } + + public Operators Operation { + get { return _operation; } + } + + public override DynamicActionKind Kind { + get { return DynamicActionKind.DoOperation; } + } + + public ActionBinder Binder { + get { return _binder; } + } + + public override Expression Bind(object[] args, ReadOnlyCollection parameters, LabelTarget returnLabel) { + return Binder.Bind(this, args, parameters, returnLabel); + } + + [Confined] + public override bool Equals(object obj) { + OldDoOperationAction other = obj as OldDoOperationAction; + if (other == null) return false; + return _operation == other._operation && (object)_binder == (object)other._binder; + } + + [Confined] + public override int GetHashCode() { + return ((int)Kind << 28 ^ ((int)_operation)) ^ System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(_binder); + } + + //??? Do these belong here or mone Operators enum + public bool IsComparision { + get { + return CompilerHelpers.IsComparisonOperator(_operation); + } + } + + public bool IsUnary { + get { + switch (_operation) { + case Operators.OnesComplement: + case Operators.Negate: + case Operators.Positive: + case Operators.AbsoluteValue: + case Operators.Not: + + // Added for COM support... + case Operators.Documentation: + return true; + } + return false; + } + } + + public bool IsInPlace { + get { + return CompilerHelpers.InPlaceOperatorToOperator(_operation) != Operators.None; + } + } + + public Operators DirectOperation { + get { + Operators res = CompilerHelpers.InPlaceOperatorToOperator(_operation); + if (res != Operators.None) return res; + + throw new InvalidOperationException(); + } + } + + [Confined] + public override string ToString() { + return base.ToString() + " " + _operation.ToString(); + } + + public Expression CreateExpression() { + return Expression.Call( + typeof(OldDoOperationAction).GetMethod("Make", new Type[] { typeof(ActionBinder), typeof(Operators) }), + CreateActionBinderReadExpression(), + Expression.Constant(_operation) + ); + } + + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldDynamicAction.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldDynamicAction.cs new file mode 100644 index 0000000000..6106ba3d67 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldDynamicAction.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using Microsoft.Contracts; +using Microsoft.Scripting.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + public enum DynamicActionKind { + DoOperation, + ConvertTo, + GetMember, + SetMember, + DeleteMember, + Call, + CreateInstance + } + + public abstract class OldDynamicAction : CallSiteBinder { + protected OldDynamicAction() { + } + + public override object CacheIdentity { + get { return this; } + } + + public abstract DynamicActionKind Kind { get; } + + [Confined] + public override string ToString() { + return Kind.ToString(); + } + + protected static Expression CreateActionBinderReadExpression() { + return Expression.Property( + Expression.Property( + AstUtils.CodeContext(), + typeof(CodeContext), + "LanguageContext" + ), + typeof(LanguageContext), + "Binder" + ); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldGetMemberAction.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldGetMemberAction.cs new file mode 100644 index 0000000000..1fcdd767b5 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldGetMemberAction.cs @@ -0,0 +1,110 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using Microsoft.Contracts; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + [Flags] + public enum GetMemberBindingFlags { + /// + /// No member binding flags + /// + None, + /// + /// The result of the get should produce a value that is bound to the instance it was extracted from, if possible. + /// + Bound = 0x01, + /// + /// Instead of throwing the binder will return OperationFailed.Value if the member does not exist or is write-only. + /// + NoThrow, + } + + public class OldGetMemberAction : OldMemberAction, IEquatable, IExpressionSerializable { + private readonly GetMemberBindingFlags _flags; + + public static OldGetMemberAction Make(ActionBinder binder, string name) { + return Make(binder, SymbolTable.StringToId(name), GetMemberBindingFlags.Bound); + } + + public static OldGetMemberAction Make(ActionBinder binder, SymbolId name) { + return Make(binder, name, GetMemberBindingFlags.Bound); + } + + public static OldGetMemberAction Make(ActionBinder binder, string name, GetMemberBindingFlags bindingFlags) { + return Make(binder, SymbolTable.StringToId(name), bindingFlags); + } + + public static OldGetMemberAction Make(ActionBinder binder, SymbolId name, GetMemberBindingFlags bindingFlags) { + ContractUtils.RequiresNotNull(binder, "binder"); + return new OldGetMemberAction(binder, name, bindingFlags); + } + + private OldGetMemberAction(ActionBinder binder, SymbolId name, GetMemberBindingFlags bindingFlags) + : base(binder, name) { + _flags = bindingFlags; + } + + public override DynamicActionKind Kind { + get { return DynamicActionKind.GetMember; } + } + + [Confined] + public override bool Equals(object obj) { + return Equals(obj as OldGetMemberAction); + } + + [Confined] + public override int GetHashCode() { + return base.GetHashCode() ^ _flags.GetHashCode(); + } + + public bool IsBound { + get { + return (_flags & GetMemberBindingFlags.Bound) != 0; + } + } + + public bool IsNoThrow { + get { + return (_flags & GetMemberBindingFlags.NoThrow) != 0; + } + } + + public Expression CreateExpression() { + return Expression.Call( + typeof(OldGetMemberAction).GetMethod("Make", new Type[] { typeof(ActionBinder), typeof(SymbolId), typeof(GetMemberBindingFlags) }), + CreateActionBinderReadExpression(), + AstUtils.Constant(Name), + Expression.Constant(_flags) + ); + } + + #region IEquatable Members + + [StateIndependent] + public bool Equals(OldGetMemberAction other) { + if (other == null) return false; + return other.Name == Name && other._flags == _flags && base.Equals(other); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldMemberAction.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldMemberAction.cs new file mode 100644 index 0000000000..bada515f14 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldMemberAction.cs @@ -0,0 +1,69 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Actions { + public abstract class OldMemberAction : OldDynamicAction, IEquatable { + private readonly ActionBinder _binder; + private readonly SymbolId _name; + + public SymbolId Name { + get { return _name; } + } + + public ActionBinder Binder { + get { return _binder; } + } + + protected OldMemberAction(ActionBinder binder, SymbolId name) { + _binder = binder; + _name = name; + } + + public override Expression Bind(object[] args, ReadOnlyCollection parameters, LabelTarget returnLabel) { + return Binder.Bind(this, args, parameters, returnLabel); + } + + [Confined] + public override bool Equals(object obj) { + return Equals(obj as OldMemberAction); + } + + [Confined] + public override int GetHashCode() { + return (int)Kind << 28 ^ _name.GetHashCode() ^ System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(_binder); + } + + [Confined] + public override string ToString() { + return base.ToString() + " " + SymbolTable.IdToString(_name); + } + + #region IEquatable Members + + [StateIndependent] + public bool Equals(OldMemberAction other) { + if (other == null) return false; + if ((object)_binder != (object)other._binder) return false; + return _name == other._name && Kind == other.Kind; + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldSetMemberAction.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldSetMemberAction.cs new file mode 100644 index 0000000000..10689efc59 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/Old/OldSetMemberAction.cs @@ -0,0 +1,51 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + + public class OldSetMemberAction : OldMemberAction, IExpressionSerializable { + public static OldSetMemberAction Make(ActionBinder binder, string name) { + return Make(binder, SymbolTable.StringToId(name)); + } + + public static OldSetMemberAction Make(ActionBinder binder, SymbolId name) { + ContractUtils.RequiresNotNull(binder, "binder"); + return new OldSetMemberAction(binder, name); + } + + private OldSetMemberAction(ActionBinder binder, SymbolId name) + : base(binder, name) { + } + + public override DynamicActionKind Kind { + get { return DynamicActionKind.SetMember; } + } + + public Expression CreateExpression() { + return Expression.Call( + typeof(OldSetMemberAction).GetMethod("Make", new Type[] { typeof(ActionBinder), typeof(SymbolId) }), + CreateActionBinderReadExpression(), + AstUtils.Constant(Name) + ); + } + + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/OperationBinder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/OperationBinder.cs new file mode 100644 index 0000000000..8b32bc2d96 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/OperationBinder.cs @@ -0,0 +1,67 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Binders; +using Microsoft.Contracts; +using Microsoft.Scripting.Utils; +using System; + +namespace Microsoft.Scripting.Actions { + [Obsolete("Use ExtensionBinaryOperationBinder or ExtensionUnaryOperationBinder")] + public abstract class OperationBinder : MetaObjectBinder { + private string _operation; + + protected OperationBinder(string operation) { + _operation = operation; + } + + public string Operation { + get { + return _operation; + } + } + + public MetaObject FallbackOperation(MetaObject target, MetaObject[] args) { + return FallbackOperation(target, args, null); + } + + public abstract MetaObject FallbackOperation(MetaObject target, MetaObject[] args, MetaObject errorSuggestion); + + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNullItems(args, "args"); + + // Try to call BindOperation + var emo = target as OperationMetaObject; + if (emo != null) { + return emo.BindOperation(this, args); + } + + // Otherwise just fall back (it's as if they didn't override BindOperation) + return FallbackOperation(target, args); + } + + [Confined] + public override bool Equals(object obj) { + OperationBinder oa = obj as OperationBinder; + return oa != null && oa._operation == _operation; + } + + [Confined] + public override int GetHashCode() { + return 0x10000000 ^ _operation.GetHashCode(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/OperationMetaObject.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/OperationMetaObject.cs new file mode 100644 index 0000000000..9b5a990420 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/OperationMetaObject.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + public class OperationMetaObject : MetaObject { + public OperationMetaObject(Expression expression, Restrictions restrictions) + : base(expression, restrictions) { + } + + public OperationMetaObject(Expression expression, Restrictions restrictions, object value) + : base(expression, restrictions, value) { + } + + [Obsolete("Use ExtensionBinaryOperationBinder or ExtensionUnaryOperationBinder")] + public virtual MetaObject BindOperation(OperationBinder binder, params MetaObject[] args) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackOperation(this, args); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/OperatorInfo.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/OperatorInfo.cs new file mode 100644 index 0000000000..f5d5c5c674 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/OperatorInfo.cs @@ -0,0 +1,150 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Actions { + /// + /// OperatorInfo provides a mapping from DLR operators to their associated .NET methods. + /// + public class OperatorInfo { + private static readonly OperatorInfo[] _infos = MakeOperatorTable(); // table of Operators, names, and alt names for looking up methods. + + private readonly Operators _operator; + private readonly string _name; + private readonly string _altName; + + private OperatorInfo(Operators op, string name, string altName) { + _operator = op; + _name = name; + _altName = altName; + } + + /// + /// Given an operator returns the OperatorInfo associated with the operator or null + /// + public static OperatorInfo GetOperatorInfo(Operators op) { + foreach (OperatorInfo info in _infos) { + if (info._operator == op) return info; + } + return null; + } + + public static OperatorInfo GetOperatorInfo(string name) { + foreach (OperatorInfo info in _infos) { + if (info.Name == name || info.AlternateName == name) { + return info; + } + } + return null; + } + + /// + /// The operator the OperatorInfo provides info for. + /// + public Operators Operator { + get { return _operator; } + } + + /// + /// The primary method name associated with the method. This method name is + /// usally in the form of op_Operator (e.g. op_Addition). + /// + public string Name { + get { return _name; } + } + + /// + /// The secondary method name associated with the method. This method name is + /// usually a standard .NET method name with pascal casing (e.g. Add). + /// + public string AlternateName { + get { return _altName; } + } + + private static OperatorInfo[] MakeOperatorTable() { + List res = new List(); + + // alternate names from: http://msdn2.microsoft.com/en-us/library/2sk3x8a7(vs.71).aspx + // different in: + // comparisons all support alternative names, Xor is "ExclusiveOr" not "Xor" + + // unary operators as defined in Partition I Architecture 9.3.1: + res.Add(new OperatorInfo(Operators.Decrement, "op_Decrement", "Decrement")); // -- + res.Add(new OperatorInfo(Operators.Increment, "op_Increment", "Increment")); // ++ + res.Add(new OperatorInfo(Operators.Negate, "op_UnaryNegation", "Negate")); // - (unary) + res.Add(new OperatorInfo(Operators.Positive, "op_UnaryPlus", "Plus")); // + (unary) + res.Add(new OperatorInfo(Operators.Not, "op_LogicalNot", null)); // ! + res.Add(new OperatorInfo(Operators.IsTrue, "op_True", null)); // not defined + res.Add(new OperatorInfo(Operators.IsFalse, "op_False", null)); // not defined + //res.Add(new OperatorInfo(Operators.AddressOf, "op_AddressOf", null)); // & (unary) + res.Add(new OperatorInfo(Operators.OnesComplement, "op_OnesComplement", "OnesComplement")); // ~ + //res.Add(new OperatorInfo(Operators.PointerDereference, "op_PointerDereference", null)); // * (unary) + + // binary operators as defined in Partition I Architecture 9.3.2: + res.Add(new OperatorInfo(Operators.Add, "op_Addition", "Add")); // + + res.Add(new OperatorInfo(Operators.Subtract, "op_Subtraction", "Subtract")); // - + res.Add(new OperatorInfo(Operators.Multiply, "op_Multiply", "Multiply")); // * + res.Add(new OperatorInfo(Operators.Divide, "op_Division", "Divide")); // / + res.Add(new OperatorInfo(Operators.Mod, "op_Modulus", "Mod")); // % + res.Add(new OperatorInfo(Operators.ExclusiveOr, "op_ExclusiveOr", "ExclusiveOr")); // ^ + res.Add(new OperatorInfo(Operators.BitwiseAnd, "op_BitwiseAnd", "BitwiseAnd")); // & + res.Add(new OperatorInfo(Operators.BitwiseOr, "op_BitwiseOr", "BitwiseOr")); // | + res.Add(new OperatorInfo(Operators.And, "op_LogicalAnd", "And")); // && + res.Add(new OperatorInfo(Operators.Or, "op_LogicalOr", "Or")); // || + res.Add(new OperatorInfo(Operators.Assign, "op_Assign", "Assign")); // = + res.Add(new OperatorInfo(Operators.LeftShift, "op_LeftShift", "LeftShift")); // << + res.Add(new OperatorInfo(Operators.RightShift, "op_RightShift", "RightShift")); // >> + res.Add(new OperatorInfo(Operators.RightShiftSigned, "op_SignedRightShift", "RightShift")); // not defined + res.Add(new OperatorInfo(Operators.RightShiftUnsigned, "op_UnsignedRightShift", "RightShift")); // not defined + res.Add(new OperatorInfo(Operators.Equals, "op_Equality", "Equals")); // == + res.Add(new OperatorInfo(Operators.GreaterThan, "op_GreaterThan", "GreaterThan")); // > + res.Add(new OperatorInfo(Operators.LessThan, "op_LessThan", "LessThan")); // < + res.Add(new OperatorInfo(Operators.NotEquals, "op_Inequality", "NotEquals")); // != + res.Add(new OperatorInfo(Operators.GreaterThanOrEqual, "op_GreaterThanOrEqual", "GreaterThanOrEqual")); // >= + res.Add(new OperatorInfo(Operators.LessThanOrEqual, "op_LessThanOrEqual", "LessThanOrEqual")); // <= + res.Add(new OperatorInfo(Operators.InPlaceMultiply, "op_MultiplicationAssignment", "InPlaceMultiply")); // *= + res.Add(new OperatorInfo(Operators.InPlaceSubtract, "op_SubtractionAssignment", "InPlaceSubtract")); // -= + res.Add(new OperatorInfo(Operators.InPlaceExclusiveOr, "op_ExclusiveOrAssignment", "InPlaceExclusiveOr")); // ^= + res.Add(new OperatorInfo(Operators.InPlaceLeftShift, "op_LeftShiftAssignment", "InPlaceLeftShift")); // <<= + res.Add(new OperatorInfo(Operators.InPlaceRightShift, "op_RightShiftAssignment", "InPlaceRightShift")); // >>= + res.Add(new OperatorInfo(Operators.InPlaceRightShiftUnsigned, "op_UnsignedRightShiftAssignment", "InPlaceUnsignedRightShift")); // >>= + res.Add(new OperatorInfo(Operators.InPlaceMod, "op_ModulusAssignment", "InPlaceMod")); // %= + res.Add(new OperatorInfo(Operators.InPlaceAdd, "op_AdditionAssignment", "InPlaceAdd")); // += + res.Add(new OperatorInfo(Operators.InPlaceBitwiseAnd, "op_BitwiseAndAssignment", "InPlaceBitwiseAnd")); // &= + res.Add(new OperatorInfo(Operators.InPlaceBitwiseOr, "op_BitwiseOrAssignment", "InPlaceBitwiseOr")); // |= + res.Add(new OperatorInfo(Operators.InPlaceDivide, "op_DivisionAssignment", "InPlaceDivide")); // /= + res.Add(new OperatorInfo(Operators.Comma, "op_Comma", null)); // , + + // DLR Extended operators: + res.Add(new OperatorInfo(Operators.Compare, "op_Compare", "Compare")); // not defined + res.Add(new OperatorInfo(Operators.GetItem, "get_Item", "GetItem")); // x[y] + res.Add(new OperatorInfo(Operators.SetItem, "set_Item", "SetItem")); // x[y] = z + res.Add(new OperatorInfo(Operators.DeleteItem, "del_Item", "DeleteItem")); // not defined + + res.Add(new OperatorInfo(Operators.GetEnumerator, "GetEnumerator", null)); + res.Add(new OperatorInfo(Operators.Dispose, "Dispose", null)); + + res.Add(new OperatorInfo(Operators.MemberNames, "GetMemberNames", null)); + res.Add(new OperatorInfo(Operators.CodeRepresentation, "ToCodeString", null)); + res.Add(new OperatorInfo(Operators.CallSignatures, "GetCallSignatures", null)); + res.Add(new OperatorInfo(Operators.Documentation, "GetDocumentation", null)); + res.Add(new OperatorInfo(Operators.IsCallable, "IsCallable", null)); + + return res.ToArray(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/PropertyTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/PropertyTracker.cs new file mode 100644 index 0000000000..dd86078237 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/PropertyTracker.cs @@ -0,0 +1,163 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Actions { + + /// + /// Represents a logical Property as a member of a Type. This Property can either be a real + /// concrete Property on a type (implemented with a ReflectedPropertyTracker) or an extension + /// property (implemented with an ExtensionPropertyTracker). + /// + public abstract class PropertyTracker : MemberTracker { + public override TrackerTypes MemberType { + get { return TrackerTypes.Property; } + } + + public abstract MethodInfo GetGetMethod(); + public abstract MethodInfo GetSetMethod(); + public abstract MethodInfo GetGetMethod(bool privateMembers); + public abstract MethodInfo GetSetMethod(bool privateMembers); + + public virtual MethodInfo GetDeleteMethod() { + return null; + } + + public virtual MethodInfo GetDeleteMethod(bool privateMembers) { + return null; + } + + public abstract ParameterInfo[] GetIndexParameters(); + + public abstract bool IsStatic { + get; + } + + public abstract Type PropertyType { + get; + } + + #region Public expression builders + + public override Expression GetValue(Expression context, ActionBinder binder, Type type) { + if (!IsStatic || GetIndexParameters().Length > 0) { + // need to bind to a value or parameters to get the value. + return binder.ReturnMemberTracker(type, this); + } + + MethodInfo getter = ResolveGetter(binder.PrivateBinding); + if (getter == null || getter.ContainsGenericParameters) { + // no usable getter + return null; + } + + if (getter.IsPublic && getter.DeclaringType.IsPublic) { + return binder.MakeCallExpression(context, getter); + } + + // private binding is just a call to the getter method... + return MemberTracker.FromMemberInfo(getter).Call(context, binder); + } + + public override ErrorInfo GetError(ActionBinder binder) { + MethodInfo getter = ResolveGetter(binder.PrivateBinding); + + if (getter == null) { + return binder.MakeMissingMemberErrorInfo(DeclaringType, Name); + } + + if (getter.ContainsGenericParameters) { + return binder.MakeGenericAccessError(this); + } + + throw new InvalidOperationException(); + } + + #endregion + + #region Internal expression builders + + protected internal override Expression GetBoundValue(Expression context, ActionBinder binder, Type type, Expression instance) { + if (instance != null && IsStatic) { + return null; + } + + if (GetIndexParameters().Length > 0) { + // need to bind to a value or parameters to get the value. + return binder.ReturnMemberTracker(type, BindToInstance(instance)); + } + + MethodInfo getter = ResolveGetter(binder.PrivateBinding); + if (getter == null || getter.ContainsGenericParameters) { + // no usable getter + return null; + } + + if (getter.IsPublic && getter.DeclaringType.IsVisible) { + return binder.MakeCallExpression(context, getter, instance); + } + + // private binding is just a call to the getter method... + return DefaultBinder.MakeError(((DefaultBinder)binder).MakeNonPublicMemberGetError(context, this, type, instance)); + } + + public override ErrorInfo GetBoundError(ActionBinder binder, Expression instance) { + MethodInfo getter = ResolveGetter(binder.PrivateBinding); + + if (getter == null) { + return binder.MakeMissingMemberErrorInfo(DeclaringType, Name); + } + + if (getter.ContainsGenericParameters) { + return binder.MakeGenericAccessError(this); + } + + if (IsStatic) { + return binder.MakeStaticPropertyInstanceAccessError(this, false, instance); + } + + throw new InvalidOperationException(); + } + + public override MemberTracker BindToInstance(Expression instance) { + return new BoundMemberTracker(this, instance); + } + + #endregion + + #region Private expression builder helpers + + private MethodInfo ResolveGetter(bool privateBinding) { + MethodInfo getter = GetGetMethod(true); + + if (getter == null) return null; + + // Allow access to protected getters TODO: this should go, it supports IronPython semantics. + if (!getter.IsPublic && !(getter.IsFamily || getter.IsFamilyOrAssembly)) { + if (!privateBinding) { + getter = null; + } + } + return CompilerHelpers.GetCallableMethod(getter, privateBinding); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/ReflectedPropertyTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/ReflectedPropertyTracker.cs new file mode 100644 index 0000000000..538a9b25af --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/ReflectedPropertyTracker.cs @@ -0,0 +1,79 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Actions { + public class ReflectedPropertyTracker : PropertyTracker { + private PropertyInfo _propInfo; + + public ReflectedPropertyTracker(PropertyInfo property) { + _propInfo = property; + } + + public override string Name { + get { return _propInfo.Name; } + } + + public override Type DeclaringType { + get { return _propInfo.DeclaringType; } + } + + public override bool IsStatic { + get { + MethodInfo mi = GetGetMethod(true) ?? GetSetMethod(true); + + return mi.IsStatic; + } + } + + public override Type PropertyType { + get { return _propInfo.PropertyType; } + } + + public override MethodInfo GetGetMethod() { + return _propInfo.GetGetMethod(); + } + + public override MethodInfo GetSetMethod() { + return _propInfo.GetSetMethod(); + } + + public override MethodInfo GetGetMethod(bool privateMembers) { + return _propInfo.GetGetMethod(privateMembers); + } + + public override MethodInfo GetSetMethod(bool privateMembers) { + return _propInfo.GetSetMethod(privateMembers); + } + + public override ParameterInfo[] GetIndexParameters() { + return _propInfo.GetIndexParameters(); + } + + public PropertyInfo Property { + get { + return _propInfo; + } + } + + [Confined] + public override string ToString() { + return _propInfo.ToString(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/RuleBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/RuleBuilder.cs new file mode 100644 index 0000000000..a6198e7321 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/RuleBuilder.cs @@ -0,0 +1,292 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic; +using System.Dynamic.Binders; +using Microsoft.Contracts; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + + /// + /// Rule Builder + /// + /// A rule is the mechanism that LanguageBinders use to specify both what code to execute (the Target) + /// for a particular action on a particular set of objects, but also a Test that guards the Target. + /// Whenver the Test returns true, it is assumed that the Target will be the correct action to + /// take on the arguments. + /// + /// In the current design, a RuleBuilder is also used to provide a mini binding scope for the + /// parameters and temporary variables that might be needed by the Test and Target. This will + /// probably change in the future as we unify around the notion of Lambdas. + /// + /// TODO: remove once everyone is converted over to MetaObjects + /// + public sealed class RuleBuilder { + internal Expression _test; // the test that determines if the rule is applicable for its parameters + internal Expression _target; // the target that executes if the rule is true + internal readonly Expression _context; // CodeContext, if any. + private bool _error; // true if the rule represents an error + internal List _temps; // temporaries allocated by the rule + + // the parameters which the rule is processing + internal readonly IList _parameters; + + // the return label of the rule + internal readonly LabelTarget _return; + + /// + /// Completed rule + /// + private Expression _binding; + + public RuleBuilder(ReadOnlyCollection parameters, LabelTarget returnLabel) { + + if (parameters.Count > 0 && typeof(CodeContext).IsAssignableFrom(parameters[0].Type)) { + _context = parameters[0]; + var p = ArrayUtils.RemoveAt(parameters, 0); + _parameters = p; + } else { + // TODO: remove the copy when we have covariant IEnumerable + _parameters = parameters.ToArray(); + } + _return = returnLabel; + } + + public void Clear() { + _test = null; + _target = null; + _error = false; + _temps = null; + } + + /// + /// An expression that should return true if and only if Target should be executed + /// + public Expression Test { + get { return _test; } + set { + ContractUtils.RequiresNotNull(value, "value"); + ContractUtils.Requires(TypeUtils.IsBool(value.Type), "value", Strings.TypeOfTestMustBeBool); + _test = value; + } + } + + /// + /// The code to execute if the Test is true. + /// + public Expression Target { + get { return _target; } + set { _target = value; } + } + + public Expression Context { + get { + return _context; + } + } + + /// + /// Gets the logical parameters to the dynamic site in the form of Expressions. + /// + public IList Parameters { + get { + return _parameters; + } + } + + public LabelTarget ReturnLabel { + get { return _return; } + } + + /// + /// Allocates a temporary variable for use during the rule. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + public ParameterExpression GetTemporary(Type type, string name) { + ParameterExpression t = Expression.Variable(type, name); + AddTemporary(t); + return t; + } + + public void AddTemporary(ParameterExpression variable) { + ContractUtils.RequiresNotNull(variable, "variable"); + if (_temps == null) { + _temps = new List(); + } + _temps.Add(variable); + } + + public Expression MakeReturn(ActionBinder binder, Expression expr) { + // we create a temporary here so that ConvertExpression doesn't need to (because it has no way to declare locals). + if (expr.Type != typeof(void)) { + ParameterExpression variable = GetTemporary(expr.Type, "$retVal"); + Expression conv = binder.ConvertExpression(variable, ReturnType, ConversionResultKind.ExplicitCast, Context); + if (conv == variable) return MakeReturn(expr); + + return MakeReturn(Ast.Block(Ast.Assign(variable, expr), conv)); + } + return MakeReturn(binder.ConvertExpression(expr, ReturnType, ConversionResultKind.ExplicitCast, Context)); + } + + private Expression MakeReturn(Expression expression) { + return Ast.Return(_return, AstUtils.Convert(expression, _return.Type)); + } + + public Expression MakeError(Expression expr) { + if (expr != null) { + // TODO: Change to ConvertHelper + if (!TypeUtils.CanAssign(typeof(Exception), expr.Type)) { + expr = Ast.Convert(expr, typeof(Exception)); + } + } + + _error = true; + return Ast.Throw(expr); + } + + public bool IsError { + get { + return _error; + } + set { + _error = value; + } + } + + public void AddTest(Expression expression) { + ContractUtils.RequiresNotNull(expression, "expression"); + ContractUtils.Requires(TypeUtils.IsBool(expression.Type), "expression", Strings.TypeOfExpressionMustBeBool); + + if (_test == null) { + _test = expression; + } else { + _test = Ast.AndAlso(_test, expression); + } + } + + public void MakeTest(params Type[] types) { + _test = MakeTestForTypes(types, 0); + } + + public static Expression MakeTypeTestExpression(Type t, Expression expr) { + // we must always check for non-sealed types explicitly - otherwise we end up + // doing fast-path behavior on a subtype which overrides behavior that wasn't + // present for the base type. + //TODO there's a question about nulls here + if (CompilerHelpers.IsSealed(t) && t == expr.Type) { + if (t.IsValueType) { + return Ast.Constant(true); + } + return Ast.NotEqual(expr, Ast.Constant(null)); + } + + return Ast.AndAlso( + Ast.NotEqual( + expr, + Ast.Constant(null)), + Ast.Equal( + Ast.Call( + AstUtils.Convert(expr, typeof(object)), + typeof(object).GetMethod("GetType") + ), + Ast.Constant(t) + ) + ); + } + + public Expression MakeTestForTypes(Type[] types, int index) { + Expression test = MakeTypeTest(types[index], index); + if (index < types.Length - 1) { + Expression nextTests = MakeTestForTypes(types, index + 1); + if (ConstantCheck.Check(test, true)) { + return nextTests; + } else if (ConstantCheck.Check(nextTests, true)) { + return test; + } else { + return Ast.AndAlso(test, nextTests); + } + } else { + return test; + } + } + + public Expression MakeTypeTest(Type type, int index) { + return MakeTypeTest(type, Parameters[index]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + public Expression MakeTypeTest(Type type, Expression tested) { + if (type == null || type == typeof(Null)) { + return Ast.Equal(tested, Ast.Constant(null)); + } + + return MakeTypeTestExpression(type, tested); + } + + /// + /// Gets the number of logical parameters the dynamic site is provided with. + /// + public int ParameterCount { + get { + return _parameters.Count; + } + } + + public Expression MakeTypeTestExpression(Type t, int param) { + return MakeTypeTestExpression(t, Parameters[param]); + } + + [Confined] + public override string ToString() { + return string.Format("RuleBuilder({0})", _target); + } + + public Type ReturnType { + get { return _return.Type; } + } + + public Expression CreateRule() { + if (_binding == null) { + if (_test == null) { + throw Error.MissingTest(); + } + if (_target == null) { + throw Error.MissingTarget(); + } + + _binding = Expression.Block( + _temps != null ? _temps.ToArray() : new ParameterExpression[0], + Expression.Condition( + _test, + AstUtils.Convert(_target, typeof(void)), + Ast.Empty() + ) + ); + } + + return _binding; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/SetMemberBinderHelper.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/SetMemberBinderHelper.cs new file mode 100644 index 0000000000..73cce024e5 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/SetMemberBinderHelper.cs @@ -0,0 +1,258 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Actions { + using Ast = System.Linq.Expressions.Expression; + using AstUtils = Microsoft.Scripting.Ast.Utils; + + public sealed class SetMemberBinderHelper : MemberBinderHelper { + private bool _isStatic; + + public SetMemberBinderHelper(CodeContext context, OldSetMemberAction action, object[] args, RuleBuilder rule) + : base(context, action, args, rule) { + } + + public void MakeNewRule() { + Type targetType = CompilerHelpers.GetType(Target); + + Rule.MakeTest(StrongBoxType ?? targetType); + + if (typeof(TypeTracker).IsAssignableFrom(targetType)) { + targetType = ((TypeTracker)Target).Type; + _isStatic = true; + Rule.AddTest(Ast.Equal(Rule.Parameters[0], Ast.Constant(Arguments[0]))); + } + + MakeSetMemberRule(targetType); + Rule.Target = Body; + } + + private void MakeSetMemberRule(Type type) { + if (MakeOperatorSetMemberBody(type, "SetMember")) { + return; + } + + MemberGroup members = Binder.GetMember(Action, type, StringName); + + // if lookup failed try the strong-box type if available. + if (members.Count == 0 && StrongBoxType != null) { + type = StrongBoxType; + StrongBoxType = null; + + members = Binder.GetMember(Action, type, StringName); + } + + Expression error; + TrackerTypes memberTypes = GetMemberType(members, out error); + if (error == null) { + switch (memberTypes) { + case TrackerTypes.Method: + case TrackerTypes.TypeGroup: + case TrackerTypes.Type: + case TrackerTypes.Constructor: MakeReadOnlyMemberError(type); break; + case TrackerTypes.Event: AddToBody(Binder.MakeEventValidation(Rule, members).MakeErrorForRule(Rule, Binder)); break; + case TrackerTypes.Field: MakeFieldRule(type, members); break; + case TrackerTypes.Property: MakePropertyRule(type, members); break; + case TrackerTypes.Custom: + MakeGenericBody(type, members[0]); + break; + case TrackerTypes.All: + // no match + if (MakeOperatorSetMemberBody(type, "SetMemberAfter")) { + return; + } + MakeMissingMemberError(type); + break; + default: + throw new InvalidOperationException(); + } + } else { + AddToBody(Rule.MakeError(error)); + } + } + + private void MakeGenericBody(Type type, MemberTracker tracker) { + if (!_isStatic) { + tracker = tracker.BindToInstance(Instance); + } + + Expression val = tracker.SetValue(Rule.Context, Binder, type, Rule.Parameters[1]); + Expression newBody; + if (val != null) { + newBody = Rule.MakeReturn(Binder, val); + } else { + newBody = tracker.GetError(Binder).MakeErrorForRule(Rule, Binder); + } + + AddToBody(newBody); + } + + private void MakePropertyRule(Type targetType, MemberGroup properties) { + PropertyTracker info = (PropertyTracker)properties[0]; + + MethodInfo setter = info.GetSetMethod(true); + + // Allow access to protected getters TODO: this should go, it supports IronPython semantics. + if (setter != null && !setter.IsPublic && !(setter.IsFamily || setter.IsFamilyOrAssembly)) { + if (!PrivateBinding) { + setter = null; + } + } + + if (setter != null) { + setter = CompilerHelpers.GetCallableMethod(setter, Binder.PrivateBinding); + + if (info.IsStatic != _isStatic) { + AddToBody(Binder.MakeStaticPropertyInstanceAccessError(info, true, Rule.Parameters).MakeErrorForRule(Rule, Binder)); + } else if(info.IsStatic && info.DeclaringType != targetType) { + AddToBody(Binder.MakeStaticAssignFromDerivedTypeError(targetType, info, Rule.Parameters[1], Rule.Context).MakeErrorForRule(Rule, Binder)); + } else if (setter.ContainsGenericParameters) { + AddToBody(Rule.MakeError(MakeGenericPropertyExpression())); + } else if (setter.IsPublic && !setter.DeclaringType.IsValueType) { + if (_isStatic) { + AddToBody( + Rule.MakeReturn( + Binder, + AstUtils.SimpleCallHelper( + setter, + Binder.ConvertExpression( + Rule.Parameters[1], + setter.GetParameters()[0].ParameterType, + ConversionResultKind.ExplicitCast, + Rule.Context + ) + ) + ) + ); + } else { + AddToBody(Rule.MakeReturn(Binder, MakeReturnValue(Binder.MakeCallExpression(Rule.Context, setter, Rule.Parameters)))); + } + } else { + // TODO: Should be able to do better w/ value types. + AddToBody( + Rule.MakeReturn( + Binder, + MakeReturnValue( + Ast.Call( + Ast.Constant(((ReflectedPropertyTracker)info).Property), // TODO: Private binding on extension properties + typeof(PropertyInfo).GetMethod("SetValue", new Type[] { typeof(object), typeof(object), typeof(object[]) }), + AstUtils.Convert(Instance, typeof(object)), + AstUtils.Convert(Rule.Parameters[1], typeof(object)), + Ast.NewArrayInit(typeof(object)) + ) + ) + ) + ); + } + } else { + AddToBody(Binder.MakeMissingMemberError(targetType, StringName).MakeErrorForRule(Rule, Binder)); + } + } + + private void MakeFieldRule(Type targetType, MemberGroup fields) { + FieldTracker field = (FieldTracker)fields[0]; + + if (field.DeclaringType.IsGenericType && field.DeclaringType.GetGenericTypeDefinition() == typeof(StrongBox<>)) { + // work around a CLR bug where we can't access generic fields from dynamic methods. + Type[] generic = field.DeclaringType.GetGenericArguments(); + AddToBody( + Rule.MakeReturn(Binder, + MakeReturnValue( + Ast.Assign( + Ast.Field( + AstUtils.Convert(Instance, field.DeclaringType), + field.DeclaringType.GetField("Value") + ), + AstUtils.Convert(Rule.Parameters[1], generic[0]) + ) + ) + ) + ); + } else if (field.IsInitOnly || field.IsLiteral) { + AddToBody(Binder.MakeReadOnlyMemberError(Rule, targetType, StringName)); + } else if (field.IsStatic && targetType != field.DeclaringType) { + AddToBody(Binder.MakeStaticAssignFromDerivedTypeError(targetType, field, Rule.Parameters[1], Rule.Context).MakeErrorForRule(Rule, Binder)); + } else if (field.DeclaringType.IsValueType && !field.IsStatic) { + AddToBody(Rule.MakeError(Ast.New(typeof(ArgumentException).GetConstructor(new Type[] { typeof(string) }), Ast.Constant("cannot assign to value types")))); + } else if (field.IsPublic && field.DeclaringType.IsVisible) { + AddToBody( + Rule.MakeReturn( + Binder, + MakeReturnValue( + Ast.Assign( + Ast.Field( + field.IsStatic ? + null : + Ast.Convert(Rule.Parameters[0], field.DeclaringType), + field.Field + ), + Binder.ConvertExpression(Rule.Parameters[1], field.FieldType, ConversionResultKind.ExplicitCast, Rule.Context) + ) + ) + ) + ); + } else { + AddToBody( + Rule.MakeReturn( + Binder, + MakeReturnValue( + Ast.Call( + AstUtils.Convert(Ast.Constant(field.Field), typeof(FieldInfo)), + typeof(FieldInfo).GetMethod("SetValue", new Type[] { typeof(object), typeof(object) }), + field.IsStatic ? + Ast.Constant(null) : + (Expression)AstUtils.Convert(Instance, typeof(object)), + AstUtils.Convert(Rule.Parameters[1], typeof(object)) + ) + ) + ) + ); + } + } + + private Expression MakeReturnValue(Expression expression) { + return Ast.Block( + expression, + Rule.Parameters[1] + ); + } + + /// if a member-injector is defined-on or registered-for this type call it + private bool MakeOperatorSetMemberBody(Type type, string name) { + MethodInfo setMem = GetMethod(type, name); + if (setMem != null && setMem.IsSpecialName) { + Expression call = Binder.MakeCallExpression(Rule.Context, setMem, Rule.Parameters[0], Ast.Constant(StringName), Rule.Parameters[1]); + Expression ret; + + if (setMem.ReturnType == typeof(bool)) { + ret = AstUtils.If(call, Rule.MakeReturn(Binder, Rule.Parameters[1])); + } else { + ret = Rule.MakeReturn(Binder, Ast.Block(call, Rule.Parameters[1])); + } + AddToBody(ret); + return setMem.ReturnType != typeof(bool); + } + + return false; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/TopNamespaceTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/TopNamespaceTracker.cs new file mode 100644 index 0000000000..1bc45e4133 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/TopNamespaceTracker.cs @@ -0,0 +1,158 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + /// + /// Represents the top reflected package which contains extra information such as + /// all the assemblies loaded and the built-in modules. + /// + public class TopNamespaceTracker : NamespaceTracker { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] // TODO: fix + private int _lastDiscovery = 0; + private readonly ScriptDomainManager _manager; +#if !SILVERLIGHT + private static Dictionary _comTypeCache = new Dictionary(); +#endif + + public TopNamespaceTracker(ScriptDomainManager manager) + : base(null) { + ContractUtils.RequiresNotNull(manager, "manager"); + SetTopPackage(this); + + _manager = manager; + } + + #region Public API Surface + + /// + /// returns the package associated with the specified namespace and + /// updates the associated module to mark the package as imported. + /// + public NamespaceTracker TryGetPackage(string name) { + return TryGetPackage(SymbolTable.StringToId(name)); + } + + public NamespaceTracker TryGetPackage(SymbolId name) { + NamespaceTracker pm = TryGetPackageAny(name) as NamespaceTracker; + if (pm != null) { + return pm; + } + return null; + } + + public MemberTracker TryGetPackageAny(string name) { + return TryGetPackageAny(SymbolTable.StringToId(name)); + } + + public MemberTracker TryGetPackageAny(SymbolId name) { + MemberTracker ret; + if (TryGetValue(name, out ret)) { + return ret; + } + return null; + } + + public MemberTracker TryGetPackageLazy(SymbolId name) { + lock (this) { + MemberTracker ret; + if (_dict.TryGetValue(SymbolTable.IdToString(name), out ret)) { + return ret; + } + return null; + } + } + + /// + /// Ensures that the assembly is loaded + /// + /// + /// true if the assembly was loaded for the first time. + /// false if the assembly had already been loaded before + public bool LoadAssembly(Assembly assem) { + ContractUtils.RequiresNotNull(assem, "assem"); + + lock (this) { + if (_packageAssemblies.Contains(assem)) { + // The assembly is already loaded. There is nothing more to do + return false; + } + + _packageAssemblies.Add(assem); + UpdateId(); + PublishComTypes(assem); + } + + return true; + } + + #endregion + + /// + /// When an (interop) assembly is loaded, we scan it to discover the GUIDs of COM interfaces so that we can + /// associate the type definition with COM objects with that GUID. + /// Since scanning all loaded assemblies can be expensive, in the future, we might consider a more explicit + /// user binder to trigger scanning of COM types. + /// + public static void PublishComTypes(Assembly interopAssembly) { +#if !SILVERLIGHT + lock (_comTypeCache) { // We lock over the entire operation so that we can publish a consistent view + + foreach (Type type in AssemblyTypeNames.LoadTypesFromAssembly(interopAssembly, false)) { + if (type.IsImport && type.IsInterface) { + Type existing; + if (_comTypeCache.TryGetValue(type.GUID, out existing)) { + if (!existing.IsDefined(typeof(CoClassAttribute), false)) { + // prefer the type w/ CoClassAttribute on it. Example: + // MS.Office.Interop.Excel.Worksheet + // vs + // MS.Office.Interop.Excel._Worksheet + // Worksheet defines all the interfaces that the type supports and has CoClassAttribute. + // _Worksheet is just the interface for the worksheet. + // + // They both have the same GUID though. + _comTypeCache[type.GUID] = type; + } + } else { + _comTypeCache[type.GUID] = type; + } + } + } + } +#endif + } + + protected override void LoadNamespaces() { + lock (this) { + for (int i = _lastDiscovery; i < _packageAssemblies.Count; i++) { + DiscoverAllTypes(_packageAssemblies[i]); + } + _lastDiscovery = _packageAssemblies.Count; + } + } + + public ScriptDomainManager DomainManager { + get { + return _manager; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/TrackerTypes.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/TrackerTypes.cs new file mode 100644 index 0000000000..53a0afdffd --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/TrackerTypes.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Actions { + [Flags] + public enum TrackerTypes { + None = 0x00, + /// Specifies that the member is a constructor, representing a ConstructorTracker + Constructor = 0x01, + /// Specifies that the member is an event, representing a EventTracker + Event = 0x02, + /// Specifies that the member is a field, representing a FieldTracker + Field = 0x04, + /// Specifies that the member is a method, representing a MethodTracker + Method = 0x08, + /// Specifies that the member is a property, representing a PropertyTracker + Property = 0x10, + /// Specifies that the member is a property, representing a TypeTracker + Type = 0x20, + /// Specifies that the member is a namespace, representing a NamespaceTracker + Namespace = 0x40, + /// Specifies that the member is a group of method overloads, representing a MethodGroup + MethodGroup = 0x80, + /// Specifies that the member is a group of types that very by arity, representing a TypeGroup + TypeGroup = 0x100, + /// Specifies that the member is a custom meber, represetning a CustomTracker + Custom = 0x200, + /// Specifies that the member is a bound to an instance, representing a BoundMemberTracker + Bound = 0x400, + // + // Summary: + // Specifies all member types. + All = Constructor | Event | Field | Method | Property | Type | Namespace | MethodGroup | TypeGroup | Bound, + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/TypeGroup.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/TypeGroup.cs new file mode 100644 index 0000000000..702a7caf4f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/TypeGroup.cs @@ -0,0 +1,230 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using System.Text; +using Microsoft.Contracts; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Actions { + /// + /// A TypeCollision is used when we have a collsion between + /// two types with the same name. Currently this is only possible w/ generic + /// methods that should logically have arity as a portion of their name. For eg: + /// System.EventHandler and System.EventHandler[T] + /// System.Nullable and System.Nullable[T] + /// System.IComparable and System.IComparable[T] + /// + /// The TypeCollision provides an indexer but also is a real type. When used + /// as a real type it is the non-generic form of the type. + /// + /// The indexer allows the user to disambiguate between the generic and + /// non-generic versions. Therefore users must always provide additional + /// information to get the generic version. + /// + public sealed class TypeGroup : TypeTracker { + private Dictionary _typesByArity; + + private TypeGroup(Type t1, Type t2) { + Debug.Assert(ReflectionUtils.GetNormalizedTypeName(t1) == ReflectionUtils.GetNormalizedTypeName(t2)); + _typesByArity = new Dictionary(); + + Debug.Assert(GetGenericArity(t1) != GetGenericArity(t2)); + _typesByArity[GetGenericArity(t1)] = t1; + _typesByArity[GetGenericArity(t2)] = t2; + } + + private TypeGroup(Type t1, Dictionary existingTypes) { + _typesByArity = existingTypes; + _typesByArity[GetGenericArity(t1)] = t1; + } + + [Confined] + public override string ToString() { + StringBuilder repr = new StringBuilder(base.ToString()); + repr.Append(":" + NormalizedName + "("); + + bool pastFirstType = false; + foreach (Type type in Types) { + if (pastFirstType) { + repr.Append(", "); + } + repr.Append(type.Name); + pastFirstType = true; + } + repr.Append(")"); + + return repr.ToString(); + } + + public TypeTracker GetTypeForArity(int arity) { + Type typeWithMatchingArity; + if (!_typesByArity.TryGetValue(arity, out typeWithMatchingArity)) { + return null; + } + return ReflectionCache.GetTypeTracker(typeWithMatchingArity); + } + + /// The merged list so far. Could be null + /// The new type(s) to add to the merged list + /// The merged list. Could be a TypeTracker or TypeGroup + public static TypeTracker UpdateTypeEntity( + TypeTracker existingTypeEntity, + TypeTracker newType) { + + Debug.Assert(newType != null); + Debug.Assert(existingTypeEntity == null || (existingTypeEntity is NestedTypeTracker) || (existingTypeEntity is TypeGroup)); + + if (existingTypeEntity == null) { + return newType; + } + + NestedTypeTracker existingType = existingTypeEntity as NestedTypeTracker; + TypeGroup existingTypeCollision = existingTypeEntity as TypeGroup; +#if DEBUG + string existingEntityNormalizedName = (existingType != null) ? ReflectionUtils.GetNormalizedTypeName(existingType.Type) + : existingTypeCollision.NormalizedName; + string newEntityNormalizedName = ReflectionUtils.GetNormalizedTypeName(newType.Type); + Debug.Assert(existingEntityNormalizedName == newEntityNormalizedName); +#endif + + if (existingType != null) { + if (GetGenericArity(existingType.Type) == GetGenericArity(newType.Type)) { + return newType; + } + + return new TypeGroup(existingType.Type, newType.Type); + } + + // copy the dictionary and return a new collision + Dictionary copy = new Dictionary(existingTypeCollision._typesByArity); + return new TypeGroup(newType.Type, copy); + } + + /// Gets the arity of generic parameters + private static int GetGenericArity(Type type) { + if (!type.IsGenericType) { + return 0; + } + + Debug.Assert(type.IsGenericTypeDefinition); + return type.GetGenericArguments().Length; + } + + /// + /// This will throw an exception if all the colliding types are generic + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix + public Type NonGenericType { + get { + Type nonGenericType; + if (TryGetNonGenericType(out nonGenericType)) { + return nonGenericType; + } + + throw Error.NonGenericWithGenericGroup(NormalizedName); + } + } + + public bool TryGetNonGenericType(out Type nonGenericType) { + return _typesByArity.TryGetValue(0, out nonGenericType); + } + + private Type SampleType { + get { + IEnumerator e = Types.GetEnumerator(); + e.MoveNext(); + return e.Current; + } + } + + public IEnumerable Types { + get { + return _typesByArity.Values; + } + } + + public string NormalizedName { + get { + return ReflectionUtils.GetNormalizedTypeName(SampleType); + } + } + + + #region MemberTracker overrides + + public override TrackerTypes MemberType { + get { + return TrackerTypes.TypeGroup; + } + } + + /// + /// This returns the DeclaringType of all the types in the TypeGroup + /// + public override Type DeclaringType { + get { + return AnyType().DeclaringType; + } + } + + /// + /// This returns the base name of the TypeGroup (the name shared by all types minus arity) + /// + public override string Name { + get { + string name = AnyType().Name; + + if (name.IndexOf(ReflectionUtils.GenericArityDelimiter) != -1) { + return name.Substring(0, name.LastIndexOf(ReflectionUtils.GenericArityDelimiter)); + } + return name; + } + } + + /// + /// This will return the result only for the non-generic type if one exists, and will throw + /// an exception if all types in the TypeGroup are generic + /// + public override Type Type { + get { return NonGenericType; } + } + + public override bool IsGenericType { + get { return _typesByArity.Count > 0; } + } + + /// + /// This will return the result only for the non-generic type if one exists, and will throw + /// an exception if all types in the TypeGroup are generic + /// + public override bool IsPublic { + get { return NonGenericType.IsPublic; } + } + + #endregion + + private Type AnyType() { + foreach (KeyValuePair kvp in _typesByArity) { + return kvp.Value; + } + throw new InvalidOperationException(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Actions/TypeTracker.cs b/merlin/main/runtime/Microsoft.Scripting/Actions/TypeTracker.cs new file mode 100644 index 0000000000..1e9277928c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Actions/TypeTracker.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Actions { + public abstract class TypeTracker : MemberTracker, IMembersList { + internal TypeTracker() { + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + public abstract Type Type { + get; + } + + public abstract bool IsGenericType { + get; + } + + public abstract bool IsPublic { + get; + } + + #region IMembersList Members + + public IList GetMemberNames(CodeContext context) { + Dictionary members = new Dictionary(); + foreach (MemberInfo mi in Type.GetMembers()) { + if (mi.MemberType != MemberTypes.Constructor) { + members[mi.Name] = mi.Name; + } + } + + List res = new List(); + foreach (string key in members.Keys) { + res.Add(key); + } + return res; + } + + #endregion + + /// + /// Enables implicit Type to TypeTracker conversions accross dynamic languages. + /// + /// TODO: Should be explicit, but that breaks a JS test + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates")] + public static implicit operator Type(TypeTracker tracker) { + TypeGroup tg = tracker as TypeGroup; + if (tg != null) { + Type res; + if (!tg.TryGetNonGenericType(out res)) { + throw ScriptingRuntimeHelpers.SimpleTypeError("expected non-generic type, got generic-only type"); + } + return res; + } + return tracker.Type; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/ArgumentTypeException.cs b/merlin/main/runtime/Microsoft.Scripting/ArgumentTypeException.cs new file mode 100644 index 0000000000..a8cdaa97d6 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ArgumentTypeException.cs @@ -0,0 +1,38 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.Serialization; + +namespace Microsoft.Scripting { + [Serializable] + public class ArgumentTypeException : Exception { + public ArgumentTypeException() + : base() { + } + + public ArgumentTypeException(string message) + : base(message) { + } + + public ArgumentTypeException(string message, Exception innerException) + : base(message, innerException) { + } + +#if !SILVERLIGHT // SerializationInfo + protected ArgumentTypeException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/ActionExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/ActionExpression.cs new file mode 100644 index 0000000000..0f5ba25e84 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/ActionExpression.cs @@ -0,0 +1,155 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Dynamic; +using System.Dynamic.Binders; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + public static partial class Utils { + + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static DynamicExpression Operator(SourceSpan span, ActionBinder binder, Operators op, Type result, params Expression[] arguments) { + return Operator(binder, op, result, arguments); + } + + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static DynamicExpression DeleteMember(SourceSpan span, ActionBinder binder, string name, params Expression[] arguments) { + return DeleteMember(binder, name, arguments); + } + + /// + /// Creates DynamicExpression representing OldDoOperationAction. + /// + /// The binder responsible for binding the dynamic operation. + /// The operation to perform + /// Type of the result desired (The DynamicExpression is strongly typed) + /// Array of arguments for the action expression + /// New instance of the DynamicExpression + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + public static DynamicExpression Operator(ActionBinder binder, Operators op, Type resultType, params Expression[] arguments) { + return Expression.Dynamic(OldDoOperationAction.Make(binder, op), resultType, arguments); + } + + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + public static DynamicExpression GetMember(ActionBinder binder, string name, Type result, params Expression[] arguments) { + ContractUtils.RequiresNotNull(arguments, "arguments"); + ContractUtils.Requires(arguments.Length > 0, "arguments"); + + return Expression.Dynamic(OldGetMemberAction.Make(binder, name), result, arguments); + } + + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + public static DynamicExpression GetMember(ActionBinder binder, string name, GetMemberBindingFlags getMemberFlags, Type result, params Expression[] arguments) { + ContractUtils.RequiresNotNull(arguments, "arguments"); + ContractUtils.Requires(arguments.Length > 0, "arguments"); + + return Expression.Dynamic(OldGetMemberAction.Make(binder, name, getMemberFlags), result, arguments); + } + + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + private static DynamicExpression SetMember(ActionBinder binder, string name, Type result, params Expression[] arguments) { + ContractUtils.RequiresNotNull(arguments, "arguments"); + ContractUtils.Requires(arguments.Length >= 2, "arguments"); + + return Expression.Dynamic(OldSetMemberAction.Make(binder, name), result, arguments); + } + + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + public static DynamicExpression DeleteMember(ActionBinder binder, string name, params Expression[] arguments) { + ContractUtils.RequiresNotNull(arguments, "arguments"); + ContractUtils.Requires(arguments.Length >= 1, "arguments"); + + return Expression.Dynamic(OldDeleteMemberAction.Make(binder, name), typeof(object), arguments); + } + + // TODO: This helper should go. It does too much number magic. + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + public static DynamicExpression Call(ActionBinder binder, Type result, params Expression[] arguments) { + return Call(OldCallAction.Make(binder, arguments.Length - 2), result, arguments); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // TODO: fix + public static DynamicExpression Call(OldCallAction action, Type result, params Expression[] arguments) { + return Expression.Dynamic(action, result, arguments); + } + + /// + /// Creates DynamicExpression representing a CreateInstance action. + /// + /// The create instance action to perform. + /// Type of the result desired (The DynamicExpression is strongly typed) + /// Array of arguments for the action expression + /// New instance of the DynamicExpression + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // TODO: fix + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + public static DynamicExpression Create(OldCreateInstanceAction action, Type result, params Expression[] arguments) { + return Expression.Dynamic(action, result, arguments); + } + + // TODO: This helper should go. It does too much number magic. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // TODO: fix + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + public static DynamicExpression Create(ActionBinder binder, Type result, params Expression[] arguments) { + return Create(OldCreateInstanceAction.Make(binder, arguments.Length - 2), result, arguments); + } + + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + public static DynamicExpression ConvertTo(OldConvertToAction action, params Expression[] arguments) { + ContractUtils.RequiresNotNull(arguments, "arguments"); + ContractUtils.Requires(arguments.Length > 0, "arguments"); + + return Expression.Dynamic(action, action.ToType, arguments); + } + + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + public static DynamicExpression ConvertTo(ActionBinder binder, Type toType, params Expression[] arguments) { + ContractUtils.RequiresNotNull(toType, "toType"); + ContractUtils.RequiresNotNull(arguments, "arguments"); + ContractUtils.Requires(arguments.Length > 0, "arguments"); + + return Expression.Dynamic(OldConvertToAction.Make(binder, toType), toType, arguments); + } + + /// + /// Creates a new DynamicExpression which performs the specified conversion to the type. The ActionExpress + /// is strongly typed to the converted type. + /// + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + public static DynamicExpression ConvertTo(ActionBinder binder, Type toType, ConversionResultKind kind, params Expression[] arguments) { + ContractUtils.RequiresNotNull(toType, "toType"); + ContractUtils.RequiresNotNull(arguments, "arguments"); + ContractUtils.Requires(arguments.Length > 0, "arguments"); + + return Expression.Dynamic(OldConvertToAction.Make(binder, toType, kind), toType, arguments); + } + + [Obsolete("use Expression.Dynamic instead of old-style action factories")] + public static DynamicExpression ConvertTo(ActionBinder binder, Type toType, ConversionResultKind kind, Type actionExpressionType, params Expression[] arguments) { + ContractUtils.RequiresNotNull(toType, "toType"); + ContractUtils.RequiresNotNull(arguments, "arguments"); + ContractUtils.Requires(arguments.Length > 0, "arguments"); + + return Expression.Dynamic(OldConvertToAction.Make(binder, toType, kind), actionExpressionType, arguments); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/AssignmentExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/AssignmentExpression.cs new file mode 100644 index 0000000000..acb9f9ddf5 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/AssignmentExpression.cs @@ -0,0 +1,81 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using System.Linq.Expressions; +using System.Diagnostics; + +namespace Microsoft.Scripting.Ast { + + /// + /// Evaluates to the CodeContext that's currently in scope + /// + /// TODO: this should go away as an intrinsic in favor of languages + /// tracking their own scope chain explicitly + /// + public sealed class AssignmentExtensionExpression : Expression { + private readonly Expression _left, _right; + + internal AssignmentExtensionExpression(Expression left, Expression right) { + Debug.Assert(left is GlobalVariableExpression); + + _left = left; + _right = right; + } + + protected override System.Type GetExpressionType() { + return _right.Type; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + public Expression Value { + get { + return _right; + } + } + + public Expression Expression { + get { + return _left; + } + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) { + Expression left = visitor.Visit(_left); + Expression right = visitor.Visit(_right); + + if (left != _left || right != _right) { + return new AssignmentExtensionExpression(left, right); + } + + return this; + } + } + + public static partial class Utils { + public static Expression Assign(Expression left, Expression right) { + GlobalVariableExpression assignable = left as GlobalVariableExpression; + if (assignable != null) { + return new AssignmentExtensionExpression(left, right); + } + + return Expression.Assign(left, right); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/BinaryExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/BinaryExpression.cs new file mode 100644 index 0000000000..b4426680e5 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/BinaryExpression.cs @@ -0,0 +1,129 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + public static partial class Utils { + + /// + /// Null coalescing expression + /// {result} ::= ((tmp = {_left}) == null) ? {right} : tmp + /// '??' operator in C#. + /// + public static Expression Coalesce(Expression left, Expression right, out ParameterExpression temp) { + return CoalesceInternal(left, right, null, false, out temp); + } + + /// + /// True coalescing expression. + /// {result} ::= IsTrue(tmp = {left}) ? {right} : tmp + /// Generalized AND semantics. + /// + public static Expression CoalesceTrue(Expression left, Expression right, MethodInfo isTrue, out ParameterExpression temp) { + ContractUtils.RequiresNotNull(isTrue, "isTrue"); + return CoalesceInternal(left, right, isTrue, false, out temp); + } + + /// + /// False coalescing expression. + /// {result} ::= IsTrue(tmp = {left}) ? tmp : {right} + /// Generalized OR semantics. + /// + public static Expression CoalesceFalse(Expression left, Expression right, MethodInfo isTrue, out ParameterExpression temp) { + ContractUtils.RequiresNotNull(isTrue, "isTrue"); + return CoalesceInternal(left, right, isTrue, true, out temp); + } + + private static Expression CoalesceInternal(Expression left, Expression right, MethodInfo isTrue, bool isReverse, out ParameterExpression temp) { + ContractUtils.RequiresNotNull(left, "left"); + ContractUtils.RequiresNotNull(right, "right"); + + // A bit too strict, but on a safe side. + ContractUtils.Requires(left.Type == right.Type, "Expression types must match"); + + temp = Expression.Variable(left.Type, "tmp_left"); + + Expression condition; + if (isTrue != null) { + ContractUtils.Requires(isTrue.ReturnType == typeof(bool), "isTrue", "Predicate must return bool."); + ParameterInfo[] parameters = isTrue.GetParameters(); + ContractUtils.Requires(parameters.Length == 1, "isTrue", "Predicate must take one parameter."); + ContractUtils.Requires(isTrue.IsStatic && isTrue.IsPublic, "isTrue", "Predicate must be public and static."); + + Type pt = parameters[0].ParameterType; + ContractUtils.Requires(TypeUtils.CanAssign(pt, left.Type), "left", "Incorrect left expression type"); + condition = Expression.Call(isTrue, Expression.Assign(temp, left)); + } else { + ContractUtils.Requires(TypeUtils.CanCompareToNull(left.Type), "left", "Incorrect left expression type"); + condition = Expression.Equal(Expression.Assign(temp, left), Expression.Constant(null, left.Type)); + } + + Expression t, f; + if (isReverse) { + t = temp; + f = right; + } else { + t = right; + f = temp; + } + + return Expression.Condition(condition, t, f); + } + + public static Expression Coalesce(LambdaBuilder builder, Expression left, Expression right) { + ParameterExpression temp; + Expression result = Coalesce(left, right, out temp); + builder.AddHiddenVariable(temp); + return result; + } + + /// + /// True coalescing expression. + /// {result} ::= IsTrue(tmp = {left}) ? {right} : tmp + /// Generalized AND semantics. + /// + public static Expression CoalesceTrue(LambdaBuilder builder, Expression left, Expression right, MethodInfo isTrue) { + ContractUtils.RequiresNotNull(isTrue, "isTrue"); + ParameterExpression temp; + Expression result = CoalesceTrue(left, right, isTrue, out temp); + builder.AddHiddenVariable(temp); + return result; + } + + /// + /// False coalescing expression. + /// {result} ::= IsTrue(tmp = {left}) ? tmp : {right} + /// Generalized OR semantics. + /// + public static Expression CoalesceFalse(LambdaBuilder builder, Expression left, Expression right, MethodInfo isTrue) { + ContractUtils.RequiresNotNull(isTrue, "isTrue"); + ParameterExpression temp; + Expression result = CoalesceFalse(left, right, isTrue, out temp); + builder.AddHiddenVariable(temp); + return result; + } + + [Obsolete("use a NotEqual overload without SourceSpan")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static BinaryExpression NotEqual(Expression left, Expression right, SourceSpan span) { + return Expression.NotEqual(left, right); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/Block.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/Block.cs new file mode 100644 index 0000000000..22ab02f910 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/Block.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq.Expressions; +using System.Collections.ObjectModel; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + public static partial class Utils { + + // Helper to add a variable to a block + internal static Expression AddScopedVariable(Expression body, ParameterExpression variable, Expression variableInit) { + List vars = new List(); + List newBody = new List(); + + var exprs = new ReadOnlyCollection(new [] { body }); + var parent = body; + //Merge blocks if the current block has only one child that is another block, + //the blocks to merge must have the same type. + while (exprs.Count == 1 && exprs[0].NodeType == ExpressionType.Block && parent.Type == exprs[0].Type) { + BlockExpression scope = (BlockExpression)(exprs[0]); + vars.AddRange(scope.Variables); + parent = scope; + exprs = scope.Expressions; + } + + newBody.Add(Expression.Assign(variable, variableInit)); + newBody.AddRange(exprs); + vars.Add(variable); + return Expression.Block( + vars, + newBody.ToArray() + ); + } + + internal static BlockExpression BlockVoid(Expression[] expressions) { + if (expressions.Length == 0 || expressions[expressions.Length - 1].Type != typeof(void)) { + expressions = expressions.AddLast(Expression.Empty()); + } + return Expression.Block(expressions); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/BreakStatement.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/BreakStatement.cs new file mode 100644 index 0000000000..935ae629f6 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/BreakStatement.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Dynamic; + +namespace Microsoft.Scripting.Ast { + public static partial class Utils { + [Obsolete("Use Expression.Break(target, null, Expression.Annotate(span)) instead")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static GotoExpression Break(LabelTarget target, SourceSpan span) { + return Expression.Break(target, null); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/CodeContextExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/CodeContextExpression.cs new file mode 100644 index 0000000000..866209d692 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/CodeContextExpression.cs @@ -0,0 +1,109 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + + /// + /// Evaluates to the CodeContext that's currently in scope + /// + /// TODO: this should go away as an intrinsic in favor of languages + /// tracking their own scope chain explicitly + /// + public sealed class CodeContextExpression : Expression { + internal static readonly CodeContextExpression Instance = new CodeContextExpression(); + + internal CodeContextExpression() { + } + + protected override System.Type GetExpressionType() { + return typeof(CodeContext); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) { + return this; + } + } + + /// + /// Creates a new scope where the specified CodeContext will be valid + /// + /// TODO: this should go away as an intrinsic in favor of languages + /// tracking their own scope chain explicitly + /// + public sealed class CodeContextScopeExpression : Expression { + private readonly Expression _newContext; + private readonly Expression _body; + + internal CodeContextScopeExpression(Expression body, Expression newContext) { + _body = body; + _newContext = newContext; + } + + protected override Type GetExpressionType() { + return _body.Type; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + /// + /// The body where the new CodeContext can be used + /// + public Expression Body { + get { return _body; } + } + + /// + /// The expression that initializes the new CodeContext for this scope + /// + public Expression NewContext { + get { return _newContext; } + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) { + Expression newContext = visitor.Visit(_newContext); + Expression body = visitor.Visit(_body); + + if (newContext == _newContext && body == _body) { + return this; + } + + return Utils.CodeContextScope(body, newContext); + } + } + + public partial class Utils { + public static Expression CodeContext() { + return CodeContextExpression.Instance; + } + public static CodeContextScopeExpression CodeContextScope(Expression body, Expression newContext) { + ContractUtils.RequiresNotNull(body, "body"); + ContractUtils.RequiresNotNull(newContext, "newContext"); + ContractUtils.Requires(TypeUtils.AreAssignable(typeof(CodeContext), newContext.Type), "newContext"); + + return new CodeContextScopeExpression(body, newContext); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/ConditionalExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/ConditionalExpression.cs new file mode 100644 index 0000000000..f64e61dd1d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/ConditionalExpression.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Ast { + public static partial class Utils { + [Obsolete("use Expression.Condition instead")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse, SourceSpan span) { + return Expression.Condition(test, ifTrue, ifFalse); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/ConstantExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/ConstantExpression.cs new file mode 100644 index 0000000000..1e99ef2271 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/ConstantExpression.cs @@ -0,0 +1,103 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using Microsoft.Scripting.Math; + +namespace Microsoft.Scripting.Ast { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")] + public static partial class Utils { + /// + /// Wraps the given value in a WeakReference and returns a tree that will retrieve + /// the value from the WeakReference. + /// + public static MemberExpression WeakConstant(object value) { + System.Diagnostics.Debug.Assert(!(value is Expression)); + return Expression.Property( + Expression.Constant(new WeakReference(value)), + typeof(WeakReference).GetProperty("Target") + ); + } + + public static Expression Constant(object value) { + if (value is SymbolId) { + return new SymbolConstantExpression((SymbolId)value); + } + BigInteger bi = value as BigInteger; + if ((object)bi != null) { + return BigIntegerConstant(bi); + } else if (value is Complex64) { + return ComplexConstant((Complex64)value); + } else { + return Expression.Constant(value); + } + } + + private static Expression BigIntegerConstant(BigInteger value) { + int ival; + if (value.AsInt32(out ival)) { + return Expression.Call( + typeof(BigInteger).GetMethod("Create", new Type[] { typeof(int) }), + Expression.Constant(ival) + ); + } + + long lval; + if (value.AsInt64(out lval)) { + return Expression.Call( + typeof(BigInteger).GetMethod("Create", new Type[] { typeof(long) }), + Expression.Constant(lval) + ); + } + + return Expression.New( + typeof(BigInteger).GetConstructor(new Type[] { typeof(int), typeof(uint[]) }), + Expression.Constant((int)value.Sign), + CreateUIntArray(value.GetBits()) + ); + } + + private static Expression CreateUIntArray(uint[] array) { + Expression[] init = new Expression[array.Length]; + for (int i = 0; i < init.Length; i++) { + init[i] = Expression.Constant(array[i]); + } + return Expression.NewArrayInit(typeof(uint), init); + } + + private static Expression ComplexConstant(Complex64 value) { + if (value.Real != 0.0) { + if (value.Imag != 0.0) { + return Expression.Call( + typeof(Complex64).GetMethod("Make"), + Expression.Constant(value.Real), + Expression.Constant(value.Imag) + ); + } else { + return Expression.Call( + typeof(Complex64).GetMethod("MakeReal"), + Expression.Constant(value.Real) + ); + } + } else { + return Expression.Call( + typeof(Complex64).GetMethod("MakeImaginary"), + Expression.Constant(value.Imag) + ); + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/ContinueStatement.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/ContinueStatement.cs new file mode 100644 index 0000000000..a6e9337010 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/ContinueStatement.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Dynamic; + +namespace Microsoft.Scripting.Ast { + public static partial class Utils { + [Obsolete("Use Expression.Continue(target, Expression.Annotate(span)) instead")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static GotoExpression Continue(LabelTarget target, SourceSpan span) { + return Expression.Continue(target); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/DebugStatement.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/DebugStatement.cs new file mode 100644 index 0000000000..0acd0aeba6 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/DebugStatement.cs @@ -0,0 +1,55 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + public partial class Utils { + public static Expression DebugMarker(string marker) { + ContractUtils.RequiresNotNull(marker, "marker"); +#if DEBUG + return CallDebugWriteLine(marker); +#else + return Expression.Empty(); +#endif + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "marker")] + public static Expression DebugMark(Expression expression, string marker) { + ContractUtils.RequiresNotNull(expression, "expression"); + ContractUtils.RequiresNotNull(marker, "marker"); + +#if DEBUG + return Expression.Block( + CallDebugWriteLine(marker), + expression + ); +#else + return expression; +#endif + } + +#if DEBUG + private static MethodCallExpression CallDebugWriteLine(string marker) { + return Expression.Call( + typeof(Debug).GetMethod("WriteLine", new[] { typeof(string) }), + Expression.Constant(marker) + ); + } +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/DeleteStatement.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/DeleteStatement.cs new file mode 100644 index 0000000000..e39b0be9ff --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/DeleteStatement.cs @@ -0,0 +1,84 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Dynamic; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + /// + /// AST node representing deletion of the variable value. + /// TODO: Python specific? + /// + public sealed class DeleteStatement : Expression { + private readonly Expression _variable; + + internal DeleteStatement(Expression variable) { + _variable = variable; + } + + public override bool CanReduce { + get { return true; } + } + + protected override Type GetExpressionType() { + return typeof(void); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + public Expression Expression { + get { return _variable; } + } + + public override Expression Reduce() { + return Expression.Void( + Utils.Assign( + _variable, + Expression.Field(null, typeof(Uninitialized).GetField("Instance")) + ) + ); + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) { + Expression v = visitor.Visit(_variable); + if (v == _variable) { + return this; + } + return Utils.Delete(v); + } + } + + public static partial class Utils { + public static DeleteStatement Delete(Expression variable) { + ContractUtils.RequiresNotNull(variable, "variable"); + ContractUtils.Requires( + variable is ParameterExpression || variable is GlobalVariableExpression, + "variable", + "variable must be ParameterExpression or GlobalVariableExpression"); + return new DeleteStatement(variable); + } + + [Obsolete("use Delete overload without SourceSpan")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static DeleteStatement Delete(Expression variable, SourceSpan span) { + return Delete(variable); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/DeleteUnboundExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/DeleteUnboundExpression.cs new file mode 100644 index 0000000000..d49ca19404 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/DeleteUnboundExpression.cs @@ -0,0 +1,84 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Dynamic; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using System; + +namespace Microsoft.Scripting.Ast { + public class DeleteUnboundExpression : Expression { + private readonly SymbolId _name; + + internal DeleteUnboundExpression(SymbolId name) { + _name = name; + } + + public override bool CanReduce { + get { return true; } + } + + protected override Type GetExpressionType() { + return typeof(object); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + public SymbolId Name { + get { return _name; } + } + + public override Expression Reduce() { + return Expression.Call( + typeof(ExpressionHelpers).GetMethod("RemoveName"), + new [] { + Utils.CodeContext(), + AstUtils.Constant(_name) + } + ); + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) { + return this; + } + } + + public static partial class Utils { + public static DeleteUnboundExpression Delete(SymbolId name) { + ContractUtils.Requires(!name.IsInvalid && !name.IsEmpty, "name"); + return new DeleteUnboundExpression(name); + } + + [Obsolete("use Delete overload without SourceSpan")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static DeleteUnboundExpression Delete(SymbolId name, SourceSpan span) { + ContractUtils.Requires(!name.IsInvalid && !name.IsEmpty, "name"); + return new DeleteUnboundExpression(name); + } + } + + public static partial class ExpressionHelpers { + /// + /// Called from generated code, helper to remove a name + /// + public static object RemoveName(CodeContext context, SymbolId name) { + return context.LanguageContext.RemoveName(context, name); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/EmptyStatements.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/EmptyStatements.cs new file mode 100644 index 0000000000..059304f769 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/EmptyStatements.cs @@ -0,0 +1,33 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Ast { + public static partial class Utils { + [Obsolete("use Expression.Empty instead")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static DefaultExpression Empty(SourceSpan span) { + return Expression.Empty(); + } + } +} + + + + + diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/FinallyFlowControlExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/FinallyFlowControlExpression.cs new file mode 100644 index 0000000000..ad9f2563eb --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/FinallyFlowControlExpression.cs @@ -0,0 +1,79 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + /// + /// Wrapping a tree in this node enables jumps from finally blocks + /// It does this by generating control-flow logic in the tree + /// + /// Reducing this node requires a full tree walk of its body + /// (but not nested lambdas) + /// + /// WARNING: this node cannot contain jumps across blocks, because it + /// assumes any unknown jumps are jumps to an outer scope. + /// + public sealed class FinallyFlowControlExpression : Expression { + private readonly Expression _body; + private Expression _reduced; + + internal FinallyFlowControlExpression(Expression body) { + _body = body; + } + + public override bool CanReduce { + get { return true; } + } + + protected override Type GetExpressionType() { + return Body.Type; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + public Expression Body { + get { return _body; } + } + + public override Expression Reduce() { + if (_reduced == null) { + _reduced = new FlowControlRewriter().Reduce(_body); + } + return _reduced; + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) { + Expression b = visitor.Visit(_body); + if (b == _body) { + return this; + } + return new FinallyFlowControlExpression(b); + } + } + + public partial class Utils { + public static Expression FinallyFlowControl(Expression body) { + return new FinallyFlowControlExpression(body); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/FlowControlRewriter.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/FlowControlRewriter.cs new file mode 100644 index 0000000000..cf5acc612b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/FlowControlRewriter.cs @@ -0,0 +1,328 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + + /// + /// The purpose of this rewriter is simple: ETs do not allow jumps (break, continue, return, goto) + /// that would go through a finally/fault. So we replace them with code that instead stores a flag, + /// and then jumps to the end of the finally/fault. At the end of the try-finally, we emit a switch + /// that then jumps to the correct label. + /// + /// A few things that make this more complicated: + /// + /// 1. If a finally contains a jump out, then jumps in the try/catch need to be replaced as well. + /// It's to support cases like this: + /// # returns 234 + /// def foo(): + /// try: return 123 + /// finally: return 234 + /// + /// We need to replace the "return 123" because after it jumps, we'll go to the finally, which + /// might decide to jump again, but once the IL finally exits, it ignores the finally jump and + /// keeps going with the original jump. The moral of the story is: if any jumps in finally are + /// rewritten, try/catch jumps must be also. + /// + /// 2. To generate better code, we only have one state variable, so if we have to jump out of + /// multiple finallys we just keep jumping. It looks sort of like this: + /// foo: + /// try { ... } finally { + /// try { ... } finally { + /// ... + /// if (...) { + /// // was: goto foo; + /// $flow = 1; goto endInnerFinally; + /// } + /// ... + /// endInnerFinally: + /// } + /// switch ($flow) { + /// case 1: goto endOuterFinally; + /// } + /// ... + /// endOuterFinally: + /// } + /// switch ($flow) { + /// case 1: $flow = 0; goto foo; + /// } + /// ... + /// + /// TODO: the compiler could optimize the switch/goto pattern, but it doesn't yet + /// + internal sealed class FlowControlRewriter : ExpressionVisitor { + + private sealed class BlockInfo { + // Is this block a finally? + internal bool InFinally; + // Does this block need flow control? + internal bool HasFlow { + get { return FlowLabel != null; } + } + + // Labels defined in this block + // So we can figure out if we can just jump directly or if we need help + internal readonly Set LabelDefs = new Set(); + + // These two properties tell us what we need to emit in the flow control + // (if anything) + internal Set NeedFlowLabels; + + // To emit a jump that we can't do in IL, we set the state variable + // and then jump to FlowLabel. It's up to the code at FlowLabel to + // handle the jump + internal LabelTarget FlowLabel; + } + + private struct LabelInfo { + internal readonly int FlowState; + internal readonly ParameterExpression Variable; + + internal LabelInfo(int index, Type varType) { + FlowState = index; + if (varType != typeof(void)) { + Variable = Expression.Variable(varType, null); + } else { + Variable = null; + } + } + } + + private readonly Dictionary _labels = new Dictionary(); + private readonly Stack _blocks = new Stack(); + private ParameterExpression _flowVariable; + + // Rewriter entry point + internal Expression Reduce(Expression node) { + _blocks.Push(new BlockInfo()); + node = Visit(node); + + if (_flowVariable != null) { + var vars = new List(); + vars.Add(_flowVariable); + foreach (var info in _labels.Values) { + if (info.Variable != null) { + vars.Add(info.Variable); + } + } + node = Expression.Block(vars, node); + } + _blocks.Pop(); + return node; + } + + private void EnsureFlow(BlockInfo block) { + if (_flowVariable == null) { + _flowVariable = Expression.Variable(typeof(int), "$flow"); + } + if (!block.HasFlow) { + block.FlowLabel = Expression.Label(); + block.NeedFlowLabels = new Set(); + } + } + + private LabelInfo EnsureLabelInfo(LabelTarget target) { + LabelInfo result; + if (!_labels.TryGetValue(target, out result)) { + _labels.Add(target, result = new LabelInfo(_labels.Count + 1, target.Type)); + } + return result; + } + + protected override Expression VisitExtension(Expression node) { + var ffc = node as FinallyFlowControlExpression; + if (ffc != null) { + // Unwrap nested finally flow expressions + // We can generate better code by walking all of them now + return Visit(ffc.Body); + } + return base.VisitExtension(node); + } + + protected override Expression VisitLambda(Expression node) { + // don't recurse into nested lambdas + return node; + } + + protected override Expression VisitTry(TryExpression node) { + // Visit finally/fault block first + BlockInfo block = new BlockInfo { InFinally = true }; + _blocks.Push(block); + Expression @finally = Visit(node.Finally); + Expression fault = Visit(node.Fault); + block.InFinally = false; + + LabelTarget finallyEnd = block.FlowLabel; + if (finallyEnd != null) { + if (@finally != null) { + @finally = Expression.Label(finallyEnd, @finally); + } + if (fault != null) { + fault = Expression.Label(finallyEnd, fault); + } + // Make a new target, which will be emitted after the try + block.FlowLabel = Expression.Label(); + } + + Expression @try = Visit(node.Body); + IList handlers = Visit(node.Handlers, VisitCatchBlock); + _blocks.Pop(); + + if (@try == node.Body && + handlers == node.Handlers && + @finally == node.Finally && + fault == node.Fault) { + return node; + } + + if (!block.HasFlow) { + return Expression.MakeTry(@try, @finally, fault, handlers); + } + + // If there is a control flow in finally, emit outer: + // try { + // // try block body and all catch handling + // } catch (Exception all) { + // saved = all; + // } finally { + // finally_body + // if (saved != null) { + // throw saved; + // } + // } + // + // If we have a fault handler we turn this into the better: + // try { + // // try block body and all catch handling + // } catch (Exception all) { + // saved = all; + // fault_body + // throw saved + // } + + if (handlers.Count > 0) { + @try = Expression.MakeTry(@try, null, null, handlers); + } + + var all = Expression.Variable(typeof(Exception), "$exception"); + if (@finally != null) { + handlers = new[] { Expression.Catch(all.Type, Expression.Empty()) }; + @finally = Expression.Block( + @finally, + Expression.Condition( + Expression.NotEqual(all, Expression.Constant(null, all.Type)), + Expression.Throw(all), + Expression.Empty() + ) + ); + } else { + handlers = new[] { + Expression.Catch( + all.Type, + Expression.Block(fault, Expression.Throw(all)) + ) + }; + fault = null; + } + + // Emit flow control + return Expression.Block( + new ParameterExpression[] { all }, + Expression.MakeTry(@try, @finally, fault, handlers), + Expression.Label(block.FlowLabel), + MakeFlowControlSwitch(block) + ); + } + + private Expression MakeFlowControlSwitch(BlockInfo block) { + var cases = block.NeedFlowLabels.Map( + target => Expression.SwitchCase(_labels[target].FlowState, MakeFlowJump(target)) + ); + return Expression.Switch(_flowVariable, null, new ReadOnlyCollection(cases)); + } + + // Determine if we can break directly to the label, or if we need to dispatch again + // If we're breaking directly, we reset the _flowVariable, otherwise we just jump to + // the next FlowLabel + private Expression MakeFlowJump(LabelTarget target) { + foreach (var block in _blocks) { + if (block.LabelDefs.Contains(target)) { + break; + } + if (block.InFinally || block.HasFlow) { + EnsureFlow(block); + block.NeedFlowLabels.Add(target); + // If we need to go through another finally, just jump + // to its flow label + return Expression.Goto(block.FlowLabel); + } + } + // Got here without needing flow, reset the flag and emit the real goto + return Expression.Block( + Expression.Assign(_flowVariable, Expression.Constant(0)), + Expression.Goto(target, _labels[target].Variable) + ); + } + + protected override Expression VisitGoto(GotoExpression node) { + foreach (var block in _blocks) { + if (block.LabelDefs.Contains(node.Target)) { + break; + } + if (block.InFinally || block.HasFlow) { + EnsureFlow(block); + block.NeedFlowLabels.Add(node.Target); + LabelInfo info = EnsureLabelInfo(node.Target); + + var assignFlow = Expression.Assign(_flowVariable, Expression.Constant(info.FlowState)); + var gotoFlow = Expression.Goto(block.FlowLabel); + if (info.Variable == null) { + return Expression.Block(assignFlow, gotoFlow); + } + + var saveValue = Expression.Assign(info.Variable, node.Value); + return Expression.Block(saveValue, assignFlow, gotoFlow); + } + } + return base.VisitGoto(node); + } + + protected override Expression VisitBlock(BlockExpression node) { + // Grab all labels in the block and define them in the block's scope + // Labels defined immediately in the block are valid for the whole block + foreach (var e in node.Expressions) { + var label = e as LabelExpression; + if (label != null) { + VisitLabelTarget(label.Label); + } + } + return base.VisitBlock(node); + } + + protected override LabelTarget VisitLabelTarget(LabelTarget node) { + if (node != null) { + EnsureLabelInfo(node); + _blocks.Peek().LabelDefs.Add(node); + } + return node; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/GeneratorExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/GeneratorExpression.cs new file mode 100644 index 0000000000..1e14f29dcd --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/GeneratorExpression.cs @@ -0,0 +1,235 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + /// + /// A parameterless generator, that is of type IEnumerable, IEnumerable{T}, + /// IEnumerator, or IEnumerator{T}. Its body can contain a series of + /// YieldExpressions. Each call into MoveNext on the enumerator reenters + /// the generator, and executes until it reaches a YieldReturn or YieldBreak + /// expression + /// + public sealed class GeneratorExpression : Expression { + private readonly LabelTarget _label; + private readonly Expression _body; + private Expression _reduced; + private readonly Type _type; + + internal GeneratorExpression(Type type, LabelTarget label, Expression body) { + _label = label; + _body = body; + _type = type; + } + + public override bool CanReduce { + get { return true; } + } + + protected override Type GetExpressionType() { + return _type; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + /// + /// The label used by YieldBreak and YieldReturn expressions to yield + /// from this generator + /// + public new LabelTarget Label { + get { return _label; } + } + + /// + /// The body of the generator, which can contain YieldBreak and + /// YieldReturn expressions + /// + public Expression Body { + get { return _body; } + } + + public override Expression Reduce() { + if (_reduced == null) { + _reduced = new GeneratorRewriter(this).Reduce(); + } + return _reduced; + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) { + Expression b = visitor.Visit(_body); + if (b == _body) { + return this; + } + return Utils.Generator(_label, b, Type); + } + + internal bool IsEnumerable { + get { return Utils.IsEnumerableType(Type); } + } + } + + public partial class Utils { + /// + /// Creates a generator with type IEnumerable{T}, where T is the label.Type + /// + public static GeneratorExpression Generator(LabelTarget label, Expression body) { + ContractUtils.RequiresNotNull(label, "label"); + ContractUtils.RequiresNotNull(body, "body"); + ContractUtils.Requires(label.Type != typeof(void), "label", "label must have a non-void type"); + + return new GeneratorExpression(typeof(IEnumerable<>).MakeGenericType(label.Type), label, body); + } + public static GeneratorExpression Generator(LabelTarget label, Expression body, Type type) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(body, "body"); + ContractUtils.RequiresNotNull(label, "label"); + ContractUtils.Requires(label.Type != typeof(void), "label", "label must have a non-void type"); + ContractUtils.Requires(body.Type == typeof(void), "body", "generator body must have a void type"); + + // Generator type must be one of: IEnumerable, IEnumerator, + // IEnumerable, or IEnumerator, where T is label.Ttpe + if (type.IsGenericType) { + Type genType = type.GetGenericTypeDefinition(); + if (genType != typeof(IEnumerable<>) && genType != typeof(IEnumerator<>) + || type.GetGenericArguments()[0] != label.Type) { + throw GeneratorTypeMustBeEnumerableOfT(label.Type); + } + } else if (type != typeof(IEnumerable) && type != typeof(IEnumerator)) { + throw GeneratorTypeMustBeEnumerableOfT(label.Type); + } + + ContractUtils.RequiresNotNull(body, "body"); + + return new GeneratorExpression(type, label, body); + } + + private static ArgumentException GeneratorTypeMustBeEnumerableOfT(Type type) { + return new ArgumentException(string.Format("Generator must be of type IEnumerable, IEnumerator, IEnumerable, or IEnumerator, where T is '{0}'", type)); + } + + internal static bool IsEnumerableType(Type type) { + return type == typeof(IEnumerable) || + type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>); + } + + #region Generator lambda factories + + public static Expression GeneratorLambda(LabelTarget label, Expression body, params ParameterExpression[] parameters) { + return (Expression)GeneratorLambda(typeof(T), label, body, null, parameters); + } + + public static Expression GeneratorLambda(LabelTarget label, Expression body, string name, params ParameterExpression[] parameters) { + return (Expression)GeneratorLambda(typeof(T), label, body, name, parameters); + } + + public static Expression GeneratorLambda(LabelTarget label, Expression body, string name, IEnumerable parameters) { + return (Expression)GeneratorLambda(typeof(T), label, body, name, parameters); + } + + public static LambdaExpression GeneratorLambda(Type delegateType, LabelTarget label, Expression body, params ParameterExpression[] parameters) { + return GeneratorLambda(delegateType, label, body, null, parameters); + } + + public static LambdaExpression GeneratorLambda(Type delegateType, LabelTarget label, Expression body, string name, params ParameterExpression[] parameters) { + return GeneratorLambda(delegateType, label, body, name, (IEnumerable)parameters); + } + + // Creates a GeneratorLambda as a lambda containing a parameterless + // generator. In the case where we return an IEnumerator, it's a very + // simple, constant-time construction. However, if the result is + // IEnumerable, it will perform a full tree walk to ensure that each + // call to GetEnumerator() returns an IEnumerator with the same + // values for the parameters. + public static LambdaExpression GeneratorLambda( + Type delegateType, + LabelTarget label, + Expression body, + string name, + IEnumerable parameters) + { + ContractUtils.RequiresNotNull(delegateType, "delegateType"); + ContractUtils.Requires(typeof(Delegate).IsAssignableFrom(delegateType) && !delegateType.IsAbstract, "Lambda type parameter must be derived from System.Delegate"); + Type generatorType = delegateType.GetMethod("Invoke").GetReturnType(); + + var paramList = parameters.ToReadOnly(); + if (IsEnumerableType(generatorType)) { + // rewrite body + body = TransformEnumerable(body, paramList); + } + + return Expression.Lambda( + delegateType, + Utils.Generator(label, body, generatorType), + name, + paramList + ); + } + + // Creates a GeneratorLambda as a lambda containing a parameterless + // generator. Because we want parameters to be captured by value and + // not as variables, we have to do a transformation more like this: + /// + // static IEnumerable Foo(int count) { + // count *= 2; + // for (int i = 0; i < count; i++) { + // yield return i; + // } + // } + // + // Becomes: + // + // static IEnumerable Foo(int count) { + // return generator { + // int __count = count; + // __count *= 2; + // for (int i = 0; i < __count; i++) { + // yield return i; + // } + // } + // } + // + // This involves a full rewrite, unfortunately. + private static Expression TransformEnumerable(Expression body, ReadOnlyCollection paramList) { + if (paramList.Count == 0) { + return body; + } + int count = paramList.Count; + var vars = new ParameterExpression[count]; + var map = new Dictionary(count); + var block = new Expression[count + 1]; + for (int i = 0; i < count; i++) { + ParameterExpression param = paramList[i]; + vars[i] = Expression.Variable(param.Type, param.Name); + map.Add(param, vars[i]); + block[i] = Expression.Assign(vars[i], param); + } + block[count] = new LambdaParameterRewriter(map).Visit(body); + return Expression.Block( + new ReadOnlyCollection(vars), + new ReadOnlyCollection(block) + ); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/GeneratorRewriter.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/GeneratorRewriter.cs new file mode 100644 index 0000000000..a6758ec232 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/GeneratorRewriter.cs @@ -0,0 +1,761 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + /// + /// When finding a yield return or yield break, this rewriter flattens out + /// containing blocks, scopes, and expressions with stack state. All + /// scopes encountered have their variables promoted to the generator's + /// closure, so they survive yields. + /// + internal sealed class GeneratorRewriter : ExpressionVisitor { + // These two constants are used internally. They should not conflict + // with valid yield states. + private const int GotoRouterYielding = 0; + private const int GotoRouterNone = -1; + // The state of the generator before it starts and when it's done + internal const int NotStarted = -1; + internal const int Finished = 0; + + private sealed class YieldMarker { + // Note: Label can be mutated as we generate try blocks + internal LabelTarget Label = Expression.Label(); + internal readonly int State; + + internal YieldMarker(int state) { + State = state; + } + } + + private readonly GeneratorExpression _generator; + private readonly ParameterExpression _current; + private readonly ParameterExpression _state; + + // The one return label, or more than one if we're in a finally + private readonly Stack _returnLabels = new Stack(); + private ParameterExpression _gotoRouter; + private bool _inTryWithFinally; + + private readonly List _yields = new List(); + + private List _debugCookies; + + private readonly List _vars = new List(); + + // Possible optimization: reuse temps. Requires scoping them correctly, + // and then storing them back in a free list + private readonly List _temps = new List(); + + internal GeneratorRewriter(GeneratorExpression generator) { + _generator = generator; + _state = Expression.Parameter(typeof(int).MakeByRefType(), "state"); + _current = Expression.Parameter(_generator.Label.Type.MakeByRefType(), "current"); + _returnLabels.Push(Expression.Label()); + _gotoRouter = Expression.Variable(typeof(int), "$gotoRouter"); + } + + internal Expression Reduce() { + // Visit body + Expression body = Visit(_generator.Body); + Debug.Assert(_returnLabels.Count == 1); + + // Add the switch statement to the body + int count = _yields.Count; + var cases = new SwitchCase[count + 1]; + for (int i = 0; i < count; i++) { + cases[i] = Expression.SwitchCase(_yields[i].State, Expression.Goto(_yields[i].Label)); + } + cases[count] = Expression.SwitchCase(Finished, Expression.Goto(_returnLabels.Peek())); + + Type generatorNextOfT = typeof(GeneratorNext<>).MakeGenericType(_generator.Label.Type); + + // Create the lambda for the GeneratorNext, hoisting variables + // into a scope outside the lambda + var allVars = new List(_vars); + allVars.AddRange(_temps); + + body = Expression.Block( + allVars, + Expression.Lambda( + generatorNextOfT, + Expression.Block( + new ParameterExpression[] { _gotoRouter }, + Expression.Switch(Expression.Assign(_gotoRouter, _state), cases), + body, + Expression.Assign(_state, Expression.Constant(Finished)), + Expression.Label(_returnLabels.Peek()) + ), + _state, + _current + ) + ); + + // Enumerable factory takes Func> instead of GeneratorNext + if (_generator.IsEnumerable) { + body = Expression.Lambda(body); + } + + // We can't create a ConstantExpression of _debugCookies array here because we walk the tree + // after constants have already been rewritten. Instead we create a NewArrayExpression node + // which initializes the array with contents from _debugCookies + Expression debugCookiesArray = null; + if (_debugCookies != null) { + Expression[] debugCookies = new Expression[_debugCookies.Count]; + for(int i=0; i < _debugCookies.Count; i++) + debugCookies[i] = Expression.Constant(_debugCookies[i]); + + debugCookiesArray = Expression.NewArrayInit( + typeof(int), + debugCookies); + } + + // Generate a call to ScriptingRuntimeHelpers.MakeGenerator(args) + return Expression.Call( + typeof(ScriptingRuntimeHelpers), + "MakeGenerator", + new[] { _generator.Label.Type }, + (debugCookiesArray != null) + ? new[] { body, debugCookiesArray } + : new[] { body } + ); + } + + private YieldMarker GetYieldMarker(YieldExpression node) { + YieldMarker result = new YieldMarker(_yields.Count + 1); + _yields.Add(result); + if (node.YieldMarker != -1) { + if (_debugCookies == null) { + _debugCookies = new List(1); + _debugCookies.Add(Int32.MaxValue); + } + _debugCookies.Insert(result.State, node.YieldMarker); + } else if (_debugCookies != null) { + _debugCookies.Insert(result.State, Int32.MaxValue); + } + return result; + } + + private BinaryExpression ToTemp(ref Expression e) { + Debug.Assert(e != null); + var temp = Expression.Variable(e.Type, "$temp$" + _temps.Count); + _temps.Add(temp); + var result = Expression.Assign(temp, e); + e = temp; + return result; + } + + private BlockExpression ToTemp(ref ReadOnlyCollection args) { + int count = args.Count; + var block = new Expression[count]; + var newArgs = new Expression[count]; + args.CopyTo(newArgs, 0); + for (int i = 0; i < count; i++) { + block[i] = ToTemp(ref newArgs[i]); + } + args = new ReadOnlyCollection(newArgs); + return Expression.Block(block); + } + + #region VisitTry + + protected override Expression VisitTry(TryExpression node) { + int startYields = _yields.Count; + + bool savedInTryWithFinally = _inTryWithFinally; + if (node.Finally != null || node.Fault != null) { + _inTryWithFinally = true; + } + Expression @try = Visit(node.Body); + int tryYields = _yields.Count; + + IList handlers = Visit(node.Handlers, VisitCatchBlock); + int catchYields = _yields.Count; + + // push a new return label in case the finally block yields + _returnLabels.Push(Expression.Label()); + // only one of these can be non-null + Expression @finally = Visit(node.Finally); + Expression fault = Visit(node.Fault); + LabelTarget finallyReturn = _returnLabels.Pop(); + int finallyYields = _yields.Count; + + _inTryWithFinally = savedInTryWithFinally; + + if (@try == node.Body && + handlers == node.Handlers && + @finally == node.Finally && + fault == node.Fault) { + return node; + } + + // No yields, just return + if (startYields == _yields.Count) { + return Expression.MakeTry(@try, @finally, fault, handlers); + } + + if (fault != null && finallyYields != catchYields) { + // No one needs this yet, and it's not clear how we should get back to + // the fault + throw new NotSupportedException("yield in fault block is not supported"); + } + + // If try has yields, we need to build a new try body that + // dispatches to the yield labels + var tryStart = Expression.Label(); + if (tryYields != startYields) { + @try = Expression.Block(MakeYieldRouter(startYields, tryYields, tryStart), @try); + } + + // Transform catches with yield to deferred handlers + if (catchYields != tryYields) { + // Temps which are only needed temporarily, so they can go into + // a transient scope (contents will be lost each yield) + var temps = new List(); + var block = new List(); + + block.Add(MakeYieldRouter(tryYields, catchYields, tryStart)); + block.Add(null); // empty slot to fill in later + + for (int i = 0, n = handlers.Count; i < n; i++) { + CatchBlock c = handlers[i]; + + if (c == node.Handlers[i]) { + continue; + } + + if (handlers.IsReadOnly) { + handlers = handlers.ToArray(); + } + + // TODO: when CatchBlock's variable is scoped properly, this + // implementation will need to be different + var deferredVar = Expression.Variable(c.Test, null); + temps.Add(deferredVar); + handlers[i] = Expression.Catch(deferredVar, Expression.Empty(), c.Filter); + + // We need to rewrite rethrows into "throw deferredVar" + var catchBody = new RethrowRewriter { Exception = deferredVar }.Visit(c.Body); + + if (c.Variable != null) { + catchBody = Expression.Block(Expression.Assign(c.Variable, deferredVar), catchBody); + } else { + catchBody = Expression.Block(catchBody); + } + + block.Add( + Expression.Condition( + Expression.NotEqual(deferredVar, Expression.Constant(null, deferredVar.Type)), + Expression.Void(catchBody), + Expression.Empty() + ) + ); + } + + block[1] = Expression.MakeTry(@try, null, null, new ReadOnlyCollection(handlers)); + @try = Expression.Block(temps, block); + handlers = new CatchBlock[0]; // so we don't reuse these + } + + if (finallyYields != catchYields) { + // We need to add a catch block to save the exception, so we + // can rethrow in case there is a yield in the finally. Also, + // add logic for returning. It looks like this: + // try { ... } catch (Exception e) {} + // finally { + // if (_finallyReturnVar) goto finallyReturn; + // ... + // if (e != null) throw e; + // finallyReturn: + // } + // if (_finallyReturnVar) goto _return; + + // We need to add a catch(Exception), so if we have catches, + // wrap them in a try + if (handlers.Count > 0) { + @try = Expression.MakeTry(@try, null, null, handlers); + handlers = new CatchBlock[0]; + } + + // NOTE: the order of these routers is important + // The first call changes the labels to all point at "tryEnd", + // so the second router will jump to "tryEnd" + var tryEnd = Expression.Label(); + Expression inFinallyRouter = MakeYieldRouter(catchYields, finallyYields, tryEnd); + Expression inTryRouter = MakeYieldRouter(catchYields, finallyYields, tryStart); + + var exception = Expression.Variable(typeof(Exception), "$temp$" + _temps.Count); + _temps.Add(exception); + @try = Expression.Block( + Expression.TryCatchFinally( + Expression.Block( + inTryRouter, + @try, + Expression.Assign(exception, Expression.Constant(null, exception.Type)), + Expression.Label(tryEnd) + ), + Expression.Block( + MakeSkipFinallyBlock(finallyReturn), + inFinallyRouter, + @finally, + Expression.Condition( + Expression.NotEqual(exception, Expression.Constant(null, exception.Type)), + Expression.Throw(exception), + Expression.Empty() + ), + Expression.Label(finallyReturn) + ), + Expression.Catch(exception, Expression.Empty()) + ), + Expression.Condition( + Expression.Equal(_gotoRouter, Expression.Constant(GotoRouterYielding)), + Expression.Goto(_returnLabels.Peek()), + Expression.Empty() + ) + ); + + @finally = null; + } else if (@finally != null) { + // try or catch had a yield, modify finally so we can skip over it + @finally = Expression.Block( + MakeSkipFinallyBlock(finallyReturn), + @finally, + Expression.Label(finallyReturn) + ); + } + + // Make the outer try, if needed + if (handlers.Count > 0 || @finally != null || fault != null) { + @try = Expression.MakeTry(@try, @finally, fault, handlers); + } + + return Expression.Block(Expression.Label(tryStart), @try); + } + + private class RethrowRewriter : ExpressionVisitor { + internal ParameterExpression Exception; + + protected override Expression VisitUnary(UnaryExpression node) { + if (node.NodeType == ExpressionType.Throw && node.Operand == null) { + return Expression.Throw(Exception, node.Type); + } + return base.VisitUnary(node); + } + + protected override Expression VisitLambda(Expression node) { + return node; // don't recurse into lambdas + } + + protected override Expression VisitTry(TryExpression node) { + return node; // don't recurse into other try's + } + } + + // Skip the finally block if we are yielding, but not if we're doing a + // yield break + private Expression MakeSkipFinallyBlock(LabelTarget target) { + return Expression.Condition( + Expression.AndAlso( + Expression.Equal(_gotoRouter, Expression.Constant(GotoRouterYielding)), + Expression.NotEqual(_state, Expression.Constant(Finished)) + ), + Expression.Goto(target), + Expression.Empty() + ); + } + + // This is copied from the base implementation. + // Just want to make sure we disallow yield in filters + protected override CatchBlock VisitCatchBlock(CatchBlock node) { + ParameterExpression v = VisitAndConvert(node.Variable, "VisitCatchBlock"); + int yields = _yields.Count; + Expression f = Visit(node.Filter); + if (yields != _yields.Count) { + // No one needs this yet, and it's not clear what it should even do + throw new NotSupportedException("yield in filter is not allowed"); + } + Expression b = Visit(node.Body); + if (v == node.Variable && b == node.Body && f == node.Filter) { + return node; + } + return Expression.MakeCatchBlock(node.Test, v, b, f); + } + + #endregion + + private SwitchExpression MakeYieldRouter(int start, int end, LabelTarget newTarget) { + Debug.Assert(end > start); + var cases = new SwitchCase[end - start]; + for (int i = start; i < end; i++) { + YieldMarker y = _yields[i]; + cases[i - start] = Expression.SwitchCase(y.State, Expression.Goto(y.Label)); + // Any jumps from outer switch statements should go to the this + // router, not the original label (which they cannot legally jump to) + y.Label = newTarget; + } + return Expression.Switch(_gotoRouter, cases); + } + + protected override Expression VisitExtension(Expression node) { + var yield = node as YieldExpression; + if (yield != null) { + return VisitYield(yield); + } + + // We need to reduce here, otherwise we can't guarentee proper + // stack spilling of the resulting expression. + // In effect, generators are one of the last rewrites that should + // happen + return Visit(node.ReduceExtensions()); + } + + private Expression VisitYield(YieldExpression node) { + if (node.Target != _generator.Label) { + throw new InvalidOperationException("yield and generator must have the same LabelTarget object"); + } + + var value = Visit(node.Value); + + var block = new List(); + if (value == null) { + // Yield break + block.Add(Expression.Assign(_state, Expression.Constant(Finished))); + if (_inTryWithFinally) { + block.Add(Expression.Assign(_gotoRouter, Expression.Constant(GotoRouterYielding))); + } + block.Add(Expression.Goto(_returnLabels.Peek())); + return Expression.Block(block); + } + + // Yield return + block.Add(Expression.Assign(_current, value)); + YieldMarker marker = GetYieldMarker(node); + block.Add(Expression.Assign(_state, Expression.Constant(marker.State))); + if (_inTryWithFinally) { + block.Add(Expression.Assign(_gotoRouter, Expression.Constant(GotoRouterYielding))); + } + block.Add(Expression.Goto(_returnLabels.Peek())); + block.Add(Expression.Label(marker.Label)); + block.Add(Expression.Assign(_gotoRouter, Expression.Constant(GotoRouterNone))); + block.Add(Expression.Empty()); + return Expression.Block(block); + } + + protected override Expression VisitBlock(BlockExpression node) { + int yields = _yields.Count; + var b = Visit(node.Expressions); + if (b == node.Expressions) { + return node; + } + if (yields == _yields.Count) { + return Expression.Block(node.Variables, b); + } + + // save the variables for later + // (they'll be hoisted outside of the lambda) + _vars.AddRange(node.Variables); + + // Return a new block expression with the rewritten body except for that + // all the variables are removed. + return Expression.Block(null, b); + } + + protected override Expression VisitLambda(Expression node) { + // don't recurse into nested lambdas + return node; + } + + #region stack spilling (to permit yield in the middle of an expression) + + private Expression VisitAssign(BinaryExpression node) { + int yields = _yields.Count; + Expression left = Visit(node.Left); + Expression right = Visit(node.Right); + if (left == node.Left && right == node.Right) { + return node; + } + if (yields == _yields.Count) { + return Expression.Assign(left, right); + } + + var block = new List(); + + // If the left hand side did not rewrite itself, we may still need + // to rewrite to ensure proper evaluation order. Essentially, we + // want all of the left side evaluated first, then the value, then + // the assignment + if (left == node.Left) { + switch (left.NodeType) { + case ExpressionType.MemberAccess: + var member = (MemberExpression)node.Left; + Expression e = Visit(member.Expression); + block.Add(ToTemp(ref e)); + left = Expression.MakeMemberAccess(e, member.Member); + break; + case ExpressionType.Index: + var index = (IndexExpression)node.Left; + Expression o = Visit(index.Object); + ReadOnlyCollection a = Visit(index.Arguments); + if (o == index.Object && a == index.Arguments) { + return index; + } + block.Add(ToTemp(ref o)); + block.Add(ToTemp(ref a)); + left = Expression.MakeIndex(o, index.Indexer, a); + break; + case ExpressionType.Parameter: + // no action needed + break; + default: + // Extension should've been reduced by Visit above, + // and returned a different node + throw Assert.Unreachable; + } + } else { + // Get the last expression of the rewritten left side + var leftBlock = (BlockExpression)left; + left = leftBlock.Expressions[leftBlock.Expressions.Count - 1]; + block.AddRange(leftBlock.Expressions); + block.RemoveAt(block.Count - 1); + } + + if (right != node.Right) { + block.Add(ToTemp(ref right)); + } + + block.Add(Expression.Assign(left, right)); + return Expression.Block(block); + } + + protected override Expression VisitDynamic(DynamicExpression node) { + int yields = _yields.Count; + ReadOnlyCollection a = Visit(node.Arguments); + if (a == node.Arguments) { + return node; + } + if (yields == _yields.Count) { + return Expression.MakeDynamic(node.DelegateType, node.Binder, a); + } + return Expression.Block( + ToTemp(ref a), + Expression.MakeDynamic(node.DelegateType, node.Binder, a) + ); + } + + protected override Expression VisitIndex(IndexExpression node) { + int yields = _yields.Count; + Expression o = Visit(node.Object); + ReadOnlyCollection a = Visit(node.Arguments); + if (o == node.Object && a == node.Arguments) { + return node; + } + if (yields == _yields.Count) { + return Expression.MakeIndex(o, node.Indexer, a); + } + return Expression.Block( + ToTemp(ref o), + ToTemp(ref a), + Expression.MakeIndex(o, node.Indexer, a) + ); + } + + protected override Expression VisitInvocation(InvocationExpression node) { + int yields = _yields.Count; + Expression e = Visit(node.Expression); + ReadOnlyCollection a = Visit(node.Arguments); + if (e == node.Expression && a == node.Arguments) { + return node; + } + if (yields == _yields.Count) { + return Expression.Invoke(e, a); + } + return Expression.Block( + ToTemp(ref e), + ToTemp(ref a), + Expression.Invoke(e, a) + ); + } + + protected override Expression VisitMethodCall(MethodCallExpression node) { + int yields = _yields.Count; + Expression o = Visit(node.Object); + ReadOnlyCollection a = Visit(node.Arguments); + if (o == node.Object && a == node.Arguments) { + return node; + } + if (yields == _yields.Count) { + return Expression.Call(o, node.Method, a); + } + if (o == null) { + return Expression.Block( + ToTemp(ref a), + Expression.Call(null, node.Method, a) + ); + } + return Expression.Block( + ToTemp(ref o), + ToTemp(ref a), + Expression.Call(o, node.Method, a) + ); + } + + protected override Expression VisitNew(NewExpression node) { + int yields = _yields.Count; + ReadOnlyCollection a = Visit(node.Arguments); + if (a == node.Arguments) { + return node; + } + if (yields == _yields.Count) { + return (node.Members != null) + ? Expression.New(node.Constructor, a, node.Members) + : Expression.New(node.Constructor, a); + } + return Expression.Block( + ToTemp(ref a), + (node.Members != null) + ? Expression.New(node.Constructor, a, node.Members) + : Expression.New(node.Constructor, a) + ); + } + + protected override Expression VisitNewArray(NewArrayExpression node) { + int yields = _yields.Count; + ReadOnlyCollection e = Visit(node.Expressions); + if (e == node.Expressions) { + return node; + } + if (yields == _yields.Count) { + return (node.NodeType == ExpressionType.NewArrayInit) + ? Expression.NewArrayInit(node.Type.GetElementType(), e) + : Expression.NewArrayBounds(node.Type.GetElementType(), e); + } + return Expression.Block( + ToTemp(ref e), + (node.NodeType == ExpressionType.NewArrayInit) + ? Expression.NewArrayInit(node.Type.GetElementType(), e) + : Expression.NewArrayBounds(node.Type.GetElementType(), e) + ); + } + + protected override Expression VisitMember(MemberExpression node) { + int yields = _yields.Count; + Expression e = Visit(node.Expression); + if (e == node.Expression) { + return node; + } + if (yields == _yields.Count) { + return Expression.MakeMemberAccess(e, node.Member); + } + return Expression.Block( + ToTemp(ref e), + Expression.MakeMemberAccess(e, node.Member) + ); + } + + protected override Expression VisitBinary(BinaryExpression node) { + if (node.NodeType == ExpressionType.Assign) { + return VisitAssign(node); + } + // For OpAssign nodes: if has a yield, we need to do the generator + // transformation on the reduced value. + if (node.CanReduce) { + return Visit(node.Reduce()); + } + + int yields = _yields.Count; + Expression left = Visit(node.Left); + Expression right = Visit(node.Right); + if (left == node.Left && right == node.Right) { + return node; + } + if (yields == _yields.Count) { + return Expression.MakeBinary(node.NodeType, left, right, node.IsLiftedToNull, node.Method, node.Conversion); + } + + return Expression.Block( + ToTemp(ref left), + ToTemp(ref right), + Expression.MakeBinary(node.NodeType, left, right, node.IsLiftedToNull, node.Method, node.Conversion) + ); + } + + protected override Expression VisitTypeBinary(TypeBinaryExpression node) { + int yields = _yields.Count; + Expression e = Visit(node.Expression); + if (e == node.Expression) { + return node; + } + if (yields == _yields.Count) { + return Expression.TypeIs(e, node.TypeOperand); + } + return Expression.Block( + ToTemp(ref e), + Expression.TypeIs(e, node.TypeOperand) + ); + } + + protected override Expression VisitUnary(UnaryExpression node) { + // For OpAssign nodes: if has a yield, we need to do the generator + // transformation on the reduced value. + if (node.CanReduce) { + return Visit(node.Reduce()); + } + + int yields = _yields.Count; + Expression o = Visit(node.Operand); + if (o == node.Operand) { + return node; + } + if (yields == _yields.Count) { + return Expression.MakeUnary(node.NodeType, o, node.Type, node.Method); + } + return Expression.Block( + ToTemp(ref o), + Expression.MakeUnary(node.NodeType, o, node.Type, node.Method) + ); + } + + protected override Expression VisitMemberInit(MemberInitExpression node) { + // See if anything changed + int yields = _yields.Count; + Expression e = base.Visit(node); + if (yields == _yields.Count) { + return e; + } + // It has a yield. Reduce to basic nodes so we can jump in + return e.Reduce(); + } + + protected override Expression VisitListInit(ListInitExpression node) { + // See if anything changed + int yields = _yields.Count; + Expression e = base.Visit(node); + if (yields == _yields.Count) { + return e; + } + // It has a yield. Reduce to basic nodes so we can jump in + return e.Reduce(); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/GlobalVariableExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/GlobalVariableExpression.cs new file mode 100644 index 0000000000..ab47feb264 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/GlobalVariableExpression.cs @@ -0,0 +1,85 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Ast { + + /// + /// Represents a variable accessed from the host's scope + /// Note: this node isn't reducible; it needs a tree rewrite to work + /// See GlobalsRewriter + /// + public sealed class GlobalVariableExpression : Expression { + private readonly string _name; + private readonly bool _local; + private readonly Type _type; + + internal GlobalVariableExpression(Type type, string name, bool local) { + Debug.Assert(type != typeof(void)); + + _name = name; + _local = local; + _type = type; + } + + public override bool CanReduce { + get { return false; } + } + + protected override Type GetExpressionType() { + return _type; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + public string Name { + get { return _name; } + } + + /// + /// If using dynamic lookup, indicates that the variable should be + /// looked up in the innermost Scope rather than the top level scope + /// + /// TODO: Python specific, can it be removed? + /// + public bool IsLocal { + get { return _local; } + } + + // TODO: Remove? Useful for debugging + public override string ToString() { + return string.Format("Global {0} {1}", Type.Name, _name); + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) { + return this; + } + } + + public partial class Utils { + public static GlobalVariableExpression GlobalVariable(Type type, string name) { + return GlobalVariable(type, name, false); + } + + public static GlobalVariableExpression GlobalVariable(Type type, string name, bool local) { + return new GlobalVariableExpression(type, name, local); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/IfStatementBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/IfStatementBuilder.cs new file mode 100644 index 0000000000..99a54db6a6 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/IfStatementBuilder.cs @@ -0,0 +1,116 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + public sealed class IfStatementBuilder { + private readonly List _clauses = new List(); + + internal IfStatementBuilder() { + } + + public IfStatementBuilder ElseIf(Expression test, params Expression[] body) { + ContractUtils.RequiresNotNullItems(body, "body"); + return ElseIf(test, Utils.BlockVoid(body)); + } + + public IfStatementBuilder ElseIf(Expression test, Expression body) { + ContractUtils.RequiresNotNull(test, "test"); + ContractUtils.Requires(test.Type == typeof(bool), "test"); + ContractUtils.RequiresNotNull(body, "body"); + _clauses.Add(Utils.IfCondition(test, body)); + return this; + } + + public Expression Else(params Expression[] body) { + ContractUtils.RequiresNotNullItems(body, "body"); + return Else(Utils.BlockVoid(body)); + } + + public Expression Else(Expression body) { + ContractUtils.RequiresNotNull(body, "body"); + return BuildConditions(_clauses, body); + } + + internal static Expression BuildConditions(IList clauses, Expression @else) { + Expression result = @else != null ? Expression.Void(@else) : Expression.Empty(); + + int index = clauses.Count; + while (index-- > 0) { + IfStatementTest ist = clauses[index]; + + result = Expression.Condition( + ist.Test, + Expression.Void(ist.Body), + result + ); + } + + return result; + } + + public Expression ToStatement() { + return BuildConditions(_clauses, null); + } + + public static implicit operator Expression(IfStatementBuilder builder) { + ContractUtils.RequiresNotNull(builder, "builder"); + return builder.ToStatement(); + } + } + + public partial class Utils { + public static IfStatementBuilder If() { + return new IfStatementBuilder(); + } + + public static IfStatementBuilder If(Expression test, params Expression[] body) { + return If().ElseIf(test, body); + } + + public static IfStatementBuilder If(Expression test, Expression body) { + return If().ElseIf(test, body); + } + + public static Expression If(IfStatementTest[] tests, Expression @else) { + ContractUtils.RequiresNotNullItems(tests, "tests"); + return IfStatementBuilder.BuildConditions(tests, @else); + } + + public static Expression IfThen(Expression test, Expression body) { + return IfThenElse(test, body, null); + } + + public static Expression IfThen(Expression test, params Expression[] body) { + return IfThenElse(test, Utils.BlockVoid(body), null); + } + + public static Expression IfThenElse(Expression test, Expression body, Expression @else) { + return If( + new IfStatementTest[] { + Utils.IfCondition(test, body) + }, + @else + ); + } + + public static Expression Unless(Expression test, Expression body) { + return IfThenElse(test, Expression.Empty(), body); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/IfStatementTest.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/IfStatementTest.cs new file mode 100644 index 0000000000..711879e19c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/IfStatementTest.cs @@ -0,0 +1,48 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + + public sealed class IfStatementTest { + private readonly Expression _test; + private readonly Expression _body; + + internal IfStatementTest(Expression test, Expression body) { + _test = test; + _body = body; + } + + public Expression Test { + get { return _test; } + } + + public Expression Body { + get { return _body; } + } + } + + public partial class Utils { + public static IfStatementTest IfCondition(Expression test, Expression body) { + ContractUtils.RequiresNotNull(test, "test"); + ContractUtils.RequiresNotNull(body, "body"); + ContractUtils.Requires(test.Type == typeof(bool), "test", "Test must be boolean"); + + return new IfStatementTest(test, body); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/LabeledStatement.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/LabeledStatement.cs new file mode 100644 index 0000000000..b8145291e2 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/LabeledStatement.cs @@ -0,0 +1,29 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + + +using System; +using System.Dynamic; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Ast { + public static partial class Utils { + [Obsolete("use Expression.Label(label, body, Expression.Annotate(span)) instead")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static LabelExpression Labeled(LabelTarget label, Expression body, SourceSpan span) { + return Expression.Label(label, body); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/LambdaBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/LambdaBuilder.cs new file mode 100644 index 0000000000..8dda1febbf --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/LambdaBuilder.cs @@ -0,0 +1,670 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; +using RuntimeHelpers = Microsoft.Scripting.Runtime.ScriptingRuntimeHelpers; + +namespace Microsoft.Scripting.Ast { + /// + /// The builder for creating the LambdaExpression node. + /// + /// Since the nodes require that parameters and variables are created + /// before hand and then passed to the factories creating LambdaExpression + /// this builder keeps track of the different pieces and at the end creates + /// the LambdaExpression. + /// + /// TODO: This has some functionality related to CodeContext that should be + /// removed, in favor of languages handling their own local scopes + /// + public class LambdaBuilder { + private readonly List _locals = new List(); + private List _params = new List(); + private readonly List> _visibleVars = new List>(); + private string _name; + private Type _returnType; + private ParameterExpression _paramsArray; + private Expression _body; + private bool _dictionary; + private bool _global; + private bool _visible = true; + private bool _doNotAddContext; + private bool _completed; + + internal LambdaBuilder(string name, Type returnType) { + _name = name; + _returnType = returnType; + } + + /// + /// The name of the lambda. + /// Currently anonymous/unnamed lambdas are not allowed. + /// + public string Name { + get { + return _name; + } + set { + ContractUtils.RequiresNotNull(value, "value"); + _name = value; + } + } + + /// + /// Return type of the lambda being created. + /// + public Type ReturnType { + get { + return _returnType; + } + set { + ContractUtils.RequiresNotNull(value, "value"); + _returnType = value; + } + } + + /// + /// List of lambda's local variables for direct manipulation. + /// + public List Locals { + get { + return _locals; + } + } + + /// + /// List of lambda's parameters for direct manipulation + /// + public List Parameters { + get { + return _params; + } + } + + /// + /// The params array argument, if any. + /// + public ParameterExpression ParamsArray { + get { + return _paramsArray; + } + } + + /// + /// The body of the lambda. This must be non-null. + /// + public Expression Body { + get { + return _body; + } + set { + ContractUtils.RequiresNotNull(value, "value"); + _body = value; + } + } + + /// + /// The generated lambda should have dictionary of locals + /// instead of allocating them directly on the CLR stack. + /// + public bool Dictionary { + get { + return _dictionary; + } + set { + _dictionary = value; + } + } + + /// + /// The resulting lambda should be marked as global. + /// TODO: remove !!! + /// + public bool Global { + get { + return _global; + } + set { + _global = value; + } + } + + /// + /// The scope is visible (default). Invisible if false. + /// + public bool Visible { + get { + return _visible; + } + set { + _visible = value; + } + } + + /// + /// Prevents builder from inserting context scope. + /// Default is false (will insert if needed). + /// + public bool DoNotAddContext { + get { + return _doNotAddContext; + } + set { + _doNotAddContext = value; + } + } + + + /// + /// Creates a parameter on the lambda with a given name and type. + /// + /// Parameters maintain the order in which they are created, + /// however custom ordering is possible via direct access to + /// Parameters collection. + /// + public ParameterExpression Parameter(Type type, string name) { + ContractUtils.RequiresNotNull(type, "type"); + ParameterExpression result = Expression.Parameter(type, name); + _params.Add(result); + _visibleVars.Add(new KeyValuePair(result, false)); + return result; + } + + /// + /// Creates a parameter on the lambda with a given name and type. + /// + /// Parameters maintain the order in which they are created, + /// however custom ordering is possible via direct access to + /// Parameters collection. + /// + public ParameterExpression ClosedOverParameter(Type type, string name) { + ContractUtils.RequiresNotNull(type, "type"); + ParameterExpression result = Expression.Parameter(type, name); + _params.Add(result); + _visibleVars.Add(new KeyValuePair(result, true)); + return result; + } + + /// + /// adds existing parameter to the lambda. + /// + /// Parameters maintain the order in which they are created, + /// however custom ordering is possible via direct access to + /// Parameters collection. + /// + public void AddParameters(params ParameterExpression[] parameters) { + _params.AddRange(parameters); + } + + /// + /// Creates a hidden parameter on the lambda with a given name and type. + /// + /// Parameters maintain the order in which they are created, + /// however custom ordering is possible via direct access to + /// Parameters collection. + /// + public ParameterExpression CreateHiddenParameter(string name, Type type) { + ContractUtils.RequiresNotNull(type, "type"); + ParameterExpression result = Expression.Parameter(type, name); + _params.Add(result); + return result; + } + + /// + /// Creates a params array argument on the labmda. + /// + /// The params array argument is added to the signature immediately. Before the lambda is + /// created, the builder validates that it is still the last (since the caller can modify + /// the order of parameters explicitly by maniuplating the parameter list) + /// + public ParameterExpression CreateParamsArray(Type type, string name) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.Requires(type.IsArray, "type"); + ContractUtils.Requires(type.GetArrayRank() == 1, "type"); + ContractUtils.Requires(_paramsArray == null, "type", "Already have parameter array"); + + return _paramsArray = Parameter(type, name); + } + + /// + /// Creates a local variable with specified name and type. + /// TODO: simplify by pushing logic into callers + /// + public Expression ClosedOverVariable(Type type, string name) { + if (_global) { + // special treatment of lambdas marked as global + return Utils.GlobalVariable(type, name, true); + } + + ParameterExpression result = Expression.Variable(type, name); + _locals.Add(result); + _visibleVars.Add(new KeyValuePair(result, true)); + return result; + } + + /// + /// Creates a local variable with specified name and type. + /// TODO: simplify by pushing logic into callers + /// + public Expression Variable(Type type, string name) { + if (_global) { + // special treatment of lambdas marked as global + return Utils.GlobalVariable(type, name, true); + } + + ParameterExpression result = Expression.Variable(type, name); + _locals.Add(result); + _visibleVars.Add(new KeyValuePair(result, false)); + return result; + } + + /// + /// Creates a temporary variable with specified name and type. + /// + public ParameterExpression HiddenVariable(Type type, string name) { + ParameterExpression result = Expression.Variable(type, name); + _locals.Add(result); + return result; + } + + /// + /// Adds the temporary variable to the list of variables maintained + /// by the builder. This is useful in cases where the variable is + /// created outside of the builder. + /// + public void AddHiddenVariable(ParameterExpression temp) { + ContractUtils.RequiresNotNull(temp, "temp"); + _locals.Add(temp); + } + + /// + /// Creates the LambdaExpression from the builder. + /// After this operation, the builder can no longer be used to create other instances. + /// + /// Desired type of the lambda. + /// New LambdaExpression instance. + public LambdaExpression MakeLambda(Type lambdaType) { + Validate(); + EnsureSignature(lambdaType); + + LambdaExpression lambda = Expression.Lambda( + lambdaType, + AddDefaultReturn(MakeBody()), + _name, + new ReadOnlyCollection(_params.ToArray()) + ); + + // The builder is now completed + _completed = true; + + return lambda; + } + + /// + /// Creates the LambdaExpression from the builder. + /// After this operation, the builder can no longer be used to create other instances. + /// + /// New LambdaExpression instance. + public LambdaExpression MakeLambda() { + ContractUtils.Requires(_paramsArray == null, "Paramarray lambdas require explicit delegate type"); + Validate(); + + LambdaExpression lambda = Expression.Lambda( + GetLambdaType(_returnType, _params), + AddDefaultReturn(MakeBody()), + _name, + _params + ); + + // The builder is now completed + _completed = true; + + return lambda; + } + + + /// + /// Creates the generator LambdaExpression from the builder. + /// After this operation, the builder can no longer be used to create other instances. + /// + /// New LambdaExpression instance. + public LambdaExpression MakeGenerator(LabelTarget label, Type lambdaType) { + Validate(); + EnsureSignature(lambdaType); + + LambdaExpression lambda = Utils.GeneratorLambda( + lambdaType, + label, + MakeBody(), + _name, + _params + ); + + // The builder is now completed + _completed = true; + + return lambda; + } + + /// + /// Fixes up lambda body and parameters to match the signature of the given delegate if needed. + /// + /// + private void EnsureSignature(Type delegateType) { + System.Diagnostics.Debug.Assert(_params != null, "must have parameter list here"); + + //paramMapping is the dictionary where we record how we want to map parameters + //the key is the parameter, the value is the expression it should be redirected to + //so far the parameter can only be redirected to itself (means no change needed) or to + //a synthetic variable that is added to the lambda when the original parameter has no direct + //parameter backing in the delegate signature + // Example: + // delegate siganture del(x, params y[]) + // lambda signature lambda(a, b, param n[]) + // + // for the situation above the mapping will be , , + // where V1 and V2 are synthetic variables and initialized as follows - V1 = y[0] , V2 = {y[1], y[2],... y[n]} + ParameterInfo[] delegateParams = delegateType.GetMethod("Invoke").GetParameters(); + + bool delegateHasParamarray = delegateParams.Length > 0 && delegateParams[delegateParams.Length - 1].IsDefined(typeof(ParamArrayAttribute), false); + bool lambdaHasParamarray = ParamsArray != null; + + if (lambdaHasParamarray && !delegateHasParamarray) { + throw new ArgumentException("paramarray lambdas must have paramarray delegate type"); + } + + int copy = delegateHasParamarray ? delegateParams.Length - 1 : delegateParams.Length; + int unwrap = _params.Count - copy; + if (lambdaHasParamarray) unwrap--; + + // Lambda must have at least as many parameters as the delegate, not counting the paramarray + if (unwrap < 0) { + throw new ArgumentException("lambda does not have enough parameters"); + } + + // shortcircuit if no rewrite is needed. + if (!delegateHasParamarray) { + bool needRewrite = false; + for (int i = 0; i < copy; i++) { + if (_params[i].Type != delegateParams[i].ParameterType) { + needRewrite = true; + } + } + + if (!needRewrite) { + return; + } + } + + List newParams = new List(delegateParams.Length); + List backingVars = new List(); + List preambuleExpressions = new List(); + Dictionary paramMapping = new Dictionary(); + + for (int i = 0; i < copy; i++) { + // map to a converted variable + if (_params[i].Type != delegateParams[i].ParameterType) { + ParameterExpression newParameter = Expression.Parameter(delegateParams[i].ParameterType, delegateParams[i].Name); + ParameterExpression mappedParameter = _params[i]; + ParameterExpression backingVariable = Expression.Variable(mappedParameter.Type, mappedParameter.Name); + + newParams.Add(newParameter); + backingVars.Add(backingVariable); + paramMapping.Add(mappedParameter, backingVariable); + preambuleExpressions.Add( + Expression.Assign( + backingVariable, + Expression.Convert( + newParameter, + mappedParameter.Type + ) + ) + ); + } else { + //use the same parameter expression + newParams.Add(_params[i]); + paramMapping.Add(_params[i], _params[i]); + } + } + + if (delegateHasParamarray) { + ParameterInfo delegateParamarrayPi = delegateParams[delegateParams.Length - 1]; + ParameterExpression delegateParamarray = Expression.Parameter(delegateParamarrayPi.ParameterType, delegateParamarrayPi.Name); + + newParams.Add(delegateParamarray); + + //unwarap delegate paramarray into variables and map parameters to the variables + for (int i = 0; i < unwrap; i++) { + ParameterExpression mappedParameter = _params[copy + i]; + ParameterExpression backingVariable = Expression.Variable(mappedParameter.Type, mappedParameter.Name); + + backingVars.Add(backingVariable); + paramMapping.Add(mappedParameter, backingVariable); + preambuleExpressions.Add( + Expression.Assign( + backingVariable, + AstUtils.Convert( + Expression.ArrayAccess( + delegateParamarray, + Expression.Constant(i) + ), + mappedParameter.Type + ) + ) + ); + } + + //lambda's paramarray should get elements from the delegate paramarray after skipping those that we unwrapped. + if (lambdaHasParamarray) { + ParameterExpression mappedParameter = _paramsArray; + ParameterExpression backingVariable = Expression.Variable(mappedParameter.Type, mappedParameter.Name); + + backingVars.Add(backingVariable); + paramMapping.Add(mappedParameter, backingVariable); + + // Call the helper + MethodInfo shifter = typeof(RuntimeHelpers).GetMethod("ShiftParamsArray"); + shifter = shifter.MakeGenericMethod(delegateParamarrayPi.ParameterType.GetElementType()); + + preambuleExpressions.Add( + Expression.Assign( + backingVariable, + AstUtils.Convert( + Expression.Call( + shifter, + delegateParamarray, + Expression.Constant(unwrap) + ), + mappedParameter.Type + ) + ) + ); + } + } + + + Expression newBody = new LambdaParameterRewriter(paramMapping).Visit(_body); + + preambuleExpressions.Add(newBody); + _body = Expression.Block(preambuleExpressions); + + _paramsArray = null; + _locals.AddRange(backingVars); + _params = newParams; + + for (int i = 0; i < _visibleVars.Count; i++) { + ParameterExpression p = _visibleVars[i].Key as ParameterExpression; + ParameterExpression v; + if (p != null && paramMapping.TryGetValue(p, out v)) { + _visibleVars[i] = new KeyValuePair(v, _visibleVars[i].Value); + } + } + } + + + /// + /// Validates that the builder has enough information to create the lambda. + /// + private void Validate() { + if (_completed) { + throw new InvalidOperationException("The builder is closed"); + } + if (_returnType == null) { + throw new InvalidOperationException("Return type is missing"); + } + if (_name == null) { + throw new InvalidOperationException("Name is missing"); + } + if (_body == null) { + throw new InvalidOperationException("Body is missing"); + } + + if (_paramsArray != null && + (_params.Count == 0 || _params[_params.Count -1] != _paramsArray)) { + throw new InvalidOperationException("The params array parameter is not last in the parameter list"); + } + } + + private bool EmitDictionary { + get { return _dictionary; } + } + + private Expression MakeBody() { + Expression body = _body; + + // wrap a CodeContext scope if needed + if (!_global && !DoNotAddContext) { + + var vars = new List(_visibleVars.Count); + foreach (var v in _visibleVars) { + if (EmitDictionary || v.Value) { + vars.Add(v.Key); + } + } + + if (vars.Count > 0) { + body = Utils.CodeContextScope( + body, + Expression.Call( + typeof(RuntimeHelpers).GetMethod("CreateNestedCodeContext"), + Utils.VariableDictionary(vars), + Utils.CodeContext(), + Expression.Constant(_visible) + ) + ); + } + } + + // wrap a scope if needed + if (_locals != null && _locals.Count > 0) { + body = Expression.Block(new ReadOnlyCollection(_locals.ToArray()), body); + } + + return body; + } + + // Add a default return value if needed + private Expression AddDefaultReturn(Expression body) { + if (body.Type == typeof(void) && _returnType != typeof(void)) { + body = Expression.Block(body, Expression.Default(_returnType)); + } + return body; + } + + private static Type GetLambdaType(Type returnType, IList parameterList) { + ContractUtils.RequiresNotNull(returnType, "returnType"); + + bool action = returnType == typeof(void); + int paramCount = parameterList == null ? 0 : parameterList.Count; + + Type[] typeArgs = new Type[paramCount + (action ? 0 : 1)]; + for (int i = 0; i < paramCount; i++) { + ContractUtils.RequiresNotNull(parameterList[i], "parameter"); + typeArgs[i] = parameterList[i].Type; + } + + Type delegateType; + if (action) + delegateType = Expression.GetActionType(typeArgs); + else { + typeArgs[paramCount] = returnType; + delegateType = Expression.GetFuncType(typeArgs); + } + return delegateType; + } + + + private static T[] ToArray(List list) { + return list != null ? list.ToArray() : new T[0]; + } + } + + public static partial class Utils { + /// + /// Creates new instance of the LambdaBuilder with specified name, return type and a source span. + /// + /// Return type of the lambda being built. + /// Name of the lambda being built. + /// SourceSpan for the lambda being built. + /// New instance of the + [Obsolete("use a Lambda overload without SourceSpan")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static LambdaBuilder Lambda(Type returnType, string name, SourceSpan span) { + return Lambda(returnType, name); + } + + /// + /// Creates new instnace of the LambdaBuilder with specified name and a return type. + /// + /// Return type of the lambda being built. + /// Name for the lambda being built. + /// new LambdaBuilder instance + public static LambdaBuilder Lambda(Type returnType, string name) { + return new LambdaBuilder(name, returnType); + } + } +} + +namespace Microsoft.Scripting.Runtime { + public static partial class ScriptingRuntimeHelpers { + public static CodeContext CreateNestedCodeContext(CustomSymbolDictionary variables, CodeContext context, bool visible) { + Debug.Assert(variables.Count > 0); + return new CodeContext(new Scope(context.Scope, variables, visible), context.LanguageContext, context); + } + + /// + /// Used by prologue code that is injected in lambdas to ensure that delegate signature matches what + /// lambda body expects. Such code typically unwraps subset of the params array manually, + /// but then passes the rest in bulk if lambda body also expects params array. + /// + /// This calls ArrayUtils.ShiftLeft, but performs additional checks that + /// ArrayUtils.ShiftLeft assumes. + /// + public static T[] ShiftParamsArray(T[] array, int count) { + if (array != null && array.Length > count) { + return ArrayUtils.ShiftLeft(array, count); + } else { + return new T[0]; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/LambdaParameterRewriter.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/LambdaParameterRewriter.cs new file mode 100644 index 0000000000..1485f61e41 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/LambdaParameterRewriter.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Ast { + internal sealed class LambdaParameterRewriter : ExpressionVisitor { + private readonly Dictionary _map; + + internal LambdaParameterRewriter(Dictionary map) { + _map = map; + } + + // We don't need to worry about parameter shadowing, because we're + // replacing the instances consistently everywhere + protected override Expression VisitParameter(ParameterExpression node) { + ParameterExpression result; + if (_map.TryGetValue(node, out result)) { + return result; + } + return node; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/LoopStatement.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/LoopStatement.cs new file mode 100644 index 0000000000..defbcaf166 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/LoopStatement.cs @@ -0,0 +1,102 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + /// + /// Factory methods. + /// + public static partial class Utils { + public static LoopExpression While(Expression test, Expression body, Expression @else) { + return Loop(test, null, body, @else, null, null); + } + + public static LoopExpression While(Expression test, Expression body, Expression @else, LabelTarget @break, LabelTarget @continue) { + return Loop(test, null, body, @else, @break, @continue); + } + + [Obsolete("use While overload without SourceSpan")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "header")] + public static LoopExpression While(Expression test, Expression body, Expression @else, LabelTarget @break, LabelTarget @continue, SourceLocation header, SourceSpan span) { + return Loop(test, null, body, @else, @break, @continue); + } + + public static LoopExpression Infinite(Expression body) { + return Expression.Loop(body, null, null); + } + + public static LoopExpression Infinite(Expression body, LabelTarget @break, LabelTarget @continue) { + return Expression.Loop(body, @break, @continue); + } + + public static LoopExpression Loop(Expression test, Expression increment, Expression body, Expression @else) { + return Loop(test, increment, body, @else, null, null); + } + + [Obsolete("use Loop overload without SourceSpan")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "header")] + public static LoopExpression Loop(Expression test, Expression increment, Expression body, Expression @else, LabelTarget @break, LabelTarget @continue, SourceLocation header, SourceSpan span) { + return Loop(test, increment, body, @else, @break, @continue); + } + + public static LoopExpression Loop(Expression test, Expression increment, Expression body, Expression @else, LabelTarget @break, LabelTarget @continue) { + ContractUtils.RequiresNotNull(body, "body"); + if (test != null) { + ContractUtils.Requires(test.Type == typeof(bool), "test", "Test must be boolean"); + if (@break == null) { + @break = Expression.Label(); + } + } + + // for (;;) { + // if (test) { + // } else { + // else; + // break; + // } + // Body + // continue: + // Increment; + // } + + // If there is no test, 'else' will never execute and gets simply thrown away. + return Expression.Loop( + Expression.Block( + test != null + ? (Expression)Expression.Condition( + test, + Expression.Empty(), + Expression.Block( + @else != null ? @else : Expression.Empty(), + Expression.Break(@break) + ) + ) + : Expression.Empty(), + body, + @continue != null ? (Expression)Expression.Label(@continue) : Expression.Empty(), + increment != null ? increment : Expression.Empty() + ), + @break, + null + ); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/MethodCallExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/MethodCallExpression.cs new file mode 100644 index 0000000000..07ce87f8c2 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/MethodCallExpression.cs @@ -0,0 +1,232 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + public static partial class Utils { + + [Obsolete("use Expression.Call instead")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static MethodCallExpression Call(MethodInfo method, SourceSpan span, params Expression[] arguments) { + return Expression.Call(null, method, arguments); + } + + [Obsolete("use Expression.Call instead")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static MethodCallExpression Call(Expression instance, MethodInfo method, SourceSpan span, params Expression[] arguments) { + return Expression.Call(instance, method, arguments); + } + + + /// + /// The helper to create the AST method call node. Will add conversions (Expression.Convert()) + /// to parameters and instance if necessary. + /// + public static MethodCallExpression SimpleCallHelper(MethodInfo method, params Expression[] arguments) { + ContractUtils.RequiresNotNull(method, "method"); + ContractUtils.Requires(method.IsStatic, "method", "Method must be static"); + return SimpleCallHelper(null, method, arguments); + } + + /// + /// The helper to create the AST method call node. Will add conversions (Expression.Convert()) + /// to parameters and instance if necessary. + /// + public static MethodCallExpression SimpleCallHelper(Expression instance, MethodInfo method, params Expression[] arguments) { + ContractUtils.RequiresNotNull(method, "method"); + ContractUtils.Requires(instance != null ^ method.IsStatic, "instance"); + ContractUtils.RequiresNotNullItems(arguments, "arguments"); + + ParameterInfo[] parameters = method.GetParameters(); + + ContractUtils.Requires(arguments.Length == parameters.Length, "arguments", "Incorrect number of arguments"); + + if (instance != null) { + instance = Convert(instance, method.DeclaringType); + } + + Expression[] convertedArguments = ArgumentConvertHelper(arguments, parameters); + + ReadOnlyCollection finalArgs; + if (convertedArguments == arguments) { + // we didn't convert anything, just convert the users original + // array to a ROC. + finalArgs = convertedArguments.ToReadOnly(); + } else { + // we already copied the array so just stick it in a ROC. + finalArgs = new ReadOnlyCollection(convertedArguments); + } + + // the arguments are now all correct, avoid re-validating the call parameters and + // directly create the expression. + return Expression.Call(instance, method, finalArgs); + } + + private static Expression[] ArgumentConvertHelper(Expression[] arguments, ParameterInfo[] parameters) { + Debug.Assert(arguments != null); + Debug.Assert(arguments != null); + + Expression[] clone = null; + for (int arg = 0; arg < arguments.Length; arg++) { + Expression argument = arguments[arg]; + if (!CompatibleParameterTypes(parameters[arg].ParameterType, argument.Type)) { + // Clone the arguments array if needed + if (clone == null) { + clone = new Expression[arguments.Length]; + // Copy the expressions into the clone + for (int i = 0; i < arg; i++) { + clone[i] = arguments[i]; + } + } + + argument = ArgumentConvertHelper(argument, parameters[arg].ParameterType); + } + + if (clone != null) { + clone[arg] = argument; + } + } + return clone ?? arguments; + } + + private static Expression ArgumentConvertHelper(Expression argument, Type type) { + if (argument.Type != type) { + if (type.IsByRef) { + type = type.GetElementType(); + } + if (argument.Type != type) { + argument = Convert(argument, type); + } + } + return argument; + } + + private static bool CompatibleParameterTypes(Type parameter, Type argument) { + if (parameter == argument || + (!parameter.IsValueType && !argument.IsValueType && parameter.IsAssignableFrom(argument))) { + return true; + } + if (parameter.IsByRef && parameter.GetElementType() == argument) { + return true; + } + return false; + } + + /// + /// The complex call helper to create the AST method call node. + /// Will add conversions (Expression.Convert()), deals with default parameter values and params arrays. + /// + public static Expression ComplexCallHelper(MethodInfo method, params Expression[] arguments) { + ContractUtils.RequiresNotNull(method, "method"); + ContractUtils.Requires(method.IsStatic, "method", "Method must be static"); + return ComplexCallHelper(null, method, arguments); + } + + // FxCop is wrong on this one. "method" is required as MethodInfo by the call to "Call" factory + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] + public static Expression ComplexCallHelper(Expression instance, MethodInfo method, params Expression[] arguments) { + ContractUtils.RequiresNotNull(method, "method"); + ContractUtils.RequiresNotNullItems(arguments, "arguments"); + ContractUtils.Requires(instance != null ^ method.IsStatic, "instance"); + + ParameterInfo[] parameters = method.GetParameters(); + bool hasParamArray = parameters.Length > 0 && parameters[parameters.Length - 1].IsParamArray(); + + if (instance != null) { + instance = Convert(instance, method.DeclaringType); + } + + Expression[] clone = null; + + int current = 0; // current parameter being populated + int consumed = 0; // arguments so far consumed + + // Validate the argument array, or populate the clone + while (current < parameters.Length) { + ParameterInfo parameter = parameters[current]; + Expression argument; + + // last parameter ... params array? + if ((current == parameters.Length - 1) && hasParamArray) { + // do we have any arguments to pass in? + if (consumed < arguments.Length) { + // Exactly one argument? If it is array of the right type, it goes directly + if ((consumed == arguments.Length - 1) && + CompatibleParameterTypes(parameter.ParameterType, arguments[consumed].Type)) { + argument = arguments[consumed++]; + } else { + Type elementType = parameter.ParameterType.GetElementType(); + Expression[] paramArray = new Expression[arguments.Length - consumed]; + int paramIndex = 0; + while (consumed < arguments.Length) { + paramArray[paramIndex++] = Convert(arguments[consumed++], elementType); + } + argument = Expression.NewArrayInit(elementType, paramArray); + } + } else { + // No. Create an empty array. + argument = Expression.NewArrayInit(parameter.ParameterType.GetElementType()); + } + } else { + if (consumed < arguments.Length) { + // We have argument. + argument = arguments[consumed++]; + } else { + // Missing argument, try default value. + ContractUtils.Requires(!parameter.IsMandatoryParameter(), "arguments", "Argument not provided for a mandatory parameter"); + argument = CreateDefaultValueExpression(parameter); + } + } + + // Add conversion if needed + argument = ArgumentConvertHelper(argument, parameter.ParameterType); + + // Do we need to make array clone? + if (clone == null && !(current < arguments.Length && (object)argument == (object)arguments[current])) { + clone = new Expression[parameters.Length]; + for (int i = 0; i < current; i++) { + clone[i] = arguments[i]; + } + } + + if (clone != null) { + clone[current] = argument; + } + + // Next parameter + current++; + } + ContractUtils.Requires(consumed == arguments.Length, "arguments", "Incorrect number of arguments"); + return Expression.Call(instance, method, clone != null ? clone : arguments); + } + + private static Expression CreateDefaultValueExpression(ParameterInfo parameter) { + if (parameter.HasDefaultValue()) { + return Expression.Constant(parameter.DefaultValue, parameter.ParameterType); + } else { + throw new NotSupportedException("missing parameter value not supported"); + } + } + + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/NewArrayExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/NewArrayExpression.cs new file mode 100644 index 0000000000..136bc4f551 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/NewArrayExpression.cs @@ -0,0 +1,65 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + public static partial class Utils { + public static NewArrayExpression NewArrayHelper(Type type, IEnumerable initializers) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + + if (type.Equals(typeof(void))) { + throw new ArgumentException("Argument type cannot be System.Void."); + } + + ReadOnlyCollection initializerList = initializers.ToReadOnly(); + + Expression[] clone = null; + for (int i = 0; i < initializerList.Count; i++) { + Expression initializer = initializerList[i]; + ContractUtils.RequiresNotNull(initializer, "initializers"); + + if (!TypeUtils.AreReferenceAssignable(type, initializer.Type)) { + if (clone == null) { + clone = new Expression[initializerList.Count]; + for (int j = 0; j < i; j++) { + clone[j] = initializerList[j]; + } + } + if (type.IsSubclassOf(typeof(Expression)) && TypeUtils.AreAssignable(type, initializer.GetType())) { + initializer = Expression.Quote(initializer); + } else { + initializer = Expression.Convert(initializer, type); + } + + } + if (clone != null) { + clone[i] = initializer; + } + } + + if (clone != null) { + initializerList = new ReadOnlyCollection(clone); + } + + return Expression.NewArrayInit(type, initializerList); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/NewExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/NewExpression.cs new file mode 100644 index 0000000000..1dc4a60535 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/NewExpression.cs @@ -0,0 +1,32 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + public static partial class Utils { + public static NewExpression SimpleNewHelper(ConstructorInfo constructor, params Expression[] arguments) { + ContractUtils.RequiresNotNull(constructor, "constructor"); + ContractUtils.RequiresNotNullItems(arguments, "arguments"); + + ParameterInfo[] parameters = constructor.GetParameters(); + ContractUtils.Requires(arguments.Length == parameters.Length, "arguments", "Incorrect number of arguments"); + + return Expression.New(constructor, ArgumentConvertHelper(arguments, parameters)); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/SourceFileInformation.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/SourceFileInformation.cs new file mode 100644 index 0000000000..7b84d0acfc --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/SourceFileInformation.cs @@ -0,0 +1,65 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +namespace Microsoft.Scripting { + /// + /// Stores information needed to emit debugging symbol information for a + /// source file, in particular the file name and unique language identifier + /// + public sealed class SourceFileInformation { + private readonly string _fileName; + + // TODO: save storage space if these are not supplied? + private readonly Guid _language; + private readonly Guid _vendor; + + public SourceFileInformation(string fileName) { + _fileName = fileName; + } + + public SourceFileInformation(string fileName, Guid language) { + _fileName = fileName; + _language = language; + } + + public SourceFileInformation(string fileName, Guid language, Guid vendor) { + _fileName = fileName; + _language = language; + _vendor = vendor; + } + + /// + /// The source file name + /// + public string FileName { + get { return _fileName; } + } + + /// + /// Returns the language's unique identifier, if any + /// + public Guid LanguageGuid { + get { return _language; } + } + + /// + /// Returns the language vendor's unique identifier, if any + /// + public Guid VendorGuid { + get { return _vendor; } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/SwitchBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/SwitchBuilder.cs new file mode 100644 index 0000000000..876935d761 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/SwitchBuilder.cs @@ -0,0 +1,82 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + + // TODO: remove builders? + public sealed class SwitchBuilder { + private Expression _value; + private readonly List _cases = new List(); + private bool _default; + private LabelTarget _label; + + internal SwitchBuilder(Expression value, LabelTarget label) { + _value = value; + _label = label; + } + + public SwitchBuilder Test(Expression value) { + ContractUtils.RequiresNotNull(value, "value"); + _value = value; + return this; + } + + public SwitchBuilder Default(Expression body) { + ContractUtils.Requires(_default == false, "body", "Already has default clause"); + _cases.Add(Expression.DefaultCase(body)); + _default = true; + return this; + } + + public SwitchBuilder Case(int value, Expression body) { + _cases.Add(Expression.SwitchCase(value, body)); + return this; + } + + public Expression ToExpression() { + ContractUtils.Requires(_value != null); + return Expression.Switch(_value, _label, _cases); + } + + public static implicit operator Expression(SwitchBuilder builder) { + ContractUtils.RequiresNotNull(builder, "builder"); + return builder.ToExpression(); + } + } + + // TODO: do we really need so many overloads? + // let's use optional args instead + public partial class Utils { + public static SwitchBuilder Switch() { + return Switch(null, null); + } + + public static SwitchBuilder Switch(LabelTarget label) { + return Switch(null, label); + } + + public static SwitchBuilder Switch(Expression value) { + return Switch(value, null); + } + + public static SwitchBuilder Switch(Expression value, LabelTarget label) { + return new SwitchBuilder(value, label); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/SymbolConstantExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/SymbolConstantExpression.cs new file mode 100644 index 0000000000..19b44b3a17 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/SymbolConstantExpression.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; + +namespace Microsoft.Scripting.Ast { + + /// + /// Represents a SymbolId constant + /// This node is reducible, and also rewritten by GlobalOptimizedRewriter + /// + /// TODO: this node exists so GlobalOptimizedRewriter can recognize and + /// rewrite a strongly typed node. Once that functionality is gone it + /// should go away. + /// + internal sealed class SymbolConstantExpression : Expression { + private readonly SymbolId _value; + + internal SymbolConstantExpression(SymbolId value) { + + _value = value; + } + + public override bool CanReduce { + get { return true; } + } + + protected override Type GetExpressionType() { + return typeof(SymbolId); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + public SymbolId Value { + get { return _value; } + } + + private static readonly Expression _SymbolIdEmpty = Expression.Field(null, typeof(SymbolId).GetField("Empty")); + private static readonly Expression _SymbolIdInvalid = Expression.Field(null, typeof(SymbolId).GetField("Invalid")); + private static readonly ConstructorInfo _SymbolIdCtor = typeof(SymbolId).GetConstructor(new[] { typeof(int) }); + + public override Expression Reduce() { + return GetExpression(_value); + } + + internal static Expression GetExpression(SymbolId value) { + if (value == SymbolId.Empty) { + return _SymbolIdEmpty; + } else if (value == SymbolId.Invalid) { + return _SymbolIdInvalid; + } else { + return Expression.New(_SymbolIdCtor, Expression.Constant(value.Id)); + } + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) { + return this; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/ThrowStatement.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/ThrowStatement.cs new file mode 100644 index 0000000000..126ab828e8 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/ThrowStatement.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Ast { + public static partial class Utils { + [Obsolete("use Expression.Rethrow instead")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static UnaryExpression Rethrow(SourceSpan span) { + return Expression.Throw(null, typeof(void)); + } + + [Obsolete("use Expression.Throw instead")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static UnaryExpression Throw(Expression value, SourceSpan span) { + return Expression.Throw(value, typeof(void)); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/TryStatementBuilder.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/TryStatementBuilder.cs new file mode 100644 index 0000000000..a81cca53c1 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/TryStatementBuilder.cs @@ -0,0 +1,153 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Dynamic; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + public sealed class TryStatementBuilder { + private readonly List _catchBlocks = new List(); + private Expression _try; + private Expression _finally, _fault; + + internal TryStatementBuilder(Expression body) { + _try = body; + } + + public TryStatementBuilder Catch(Type type, Expression body) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(body, "body"); + if (_finally != null) { + throw Error.FinallyAlreadyDefined(); + } + + _catchBlocks.Add(Expression.Catch(type, body)); + return this; + } + + public TryStatementBuilder Catch(Type type, params Expression[] body) { + return Catch(type, Utils.BlockVoid(body)); + } + + public TryStatementBuilder Catch(ParameterExpression holder, params Expression[] body) { + return Catch(holder, Utils.BlockVoid(body)); + } + + public TryStatementBuilder Catch(ParameterExpression holder, Expression body) { + ContractUtils.RequiresNotNull(holder, "holder"); + ContractUtils.RequiresNotNull(body, "body"); + + if (_finally != null) { + throw Error.FinallyAlreadyDefined(); + } + + _catchBlocks.Add(Expression.Catch(holder, body)); + return this; + } + + public TryStatementBuilder Filter(Type type, Expression condition, params Expression[] body) { + return Filter(type, condition, Utils.BlockVoid(body)); + } + + public TryStatementBuilder Filter(Type type, Expression condition, Expression body) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(condition, "condition"); + ContractUtils.RequiresNotNull(body, "body"); + + _catchBlocks.Add(Expression.Catch(type, body, condition)); + return this; + } + + public TryStatementBuilder Filter(ParameterExpression holder, Expression condition, params Expression[] body) { + return Filter(holder, condition, Utils.BlockVoid(body)); + } + + public TryStatementBuilder Filter(ParameterExpression holder, Expression condition, Expression body) { + ContractUtils.RequiresNotNull(holder, "holder"); + ContractUtils.RequiresNotNull(condition, "condition"); + ContractUtils.RequiresNotNull(body, "body"); + + _catchBlocks.Add(Expression.Catch(holder, body, condition)); + return this; + } + + public TryStatementBuilder Finally(params Expression[] body) { + return Finally(Utils.BlockVoid(body)); + } + + public TryStatementBuilder Finally(Expression body) { + ContractUtils.RequiresNotNull(body, "body"); + if (_finally != null) { + throw Error.FinallyAlreadyDefined(); + } else if (_fault != null) { + throw Error.CannotHaveFaultAndFinally(); + } + + _finally = body; + return this; + } + + public TryStatementBuilder Fault(params Expression[] body) { + ContractUtils.RequiresNotNullItems(body, "body"); + + if (_finally != null) { + throw Error.CannotHaveFaultAndFinally(); + } else if (_fault != null) { + throw Error.FaultAlreadyDefined(); + } + + if (body.Length == 1) { + _fault = body[0]; + } else { + _fault = Utils.BlockVoid(body); + } + + return this; + } + + public static implicit operator Expression(TryStatementBuilder builder) { + return ToStatement(builder); + } + + public static Expression ToStatement(TryStatementBuilder builder) { + ContractUtils.RequiresNotNull(builder, "builder"); + var result = Expression.MakeTry( + builder._try, + builder._finally, + builder._fault, + builder._catchBlocks + ); + if (result.Finally != null || result.Fault != null) { + return Utils.FinallyFlowControl(result); + } + return result; + } + } + + public partial class Utils { + public static TryStatementBuilder Try(params Expression[] body) { + ContractUtils.RequiresNotNull(body, "body"); + return new TryStatementBuilder(Utils.BlockVoid(body)); + } + + public static TryStatementBuilder Try(Expression body) { + ContractUtils.RequiresNotNull(body, "body"); + return new TryStatementBuilder(body); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/UnaryExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/UnaryExpression.cs new file mode 100644 index 0000000000..2f63297ec8 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/UnaryExpression.cs @@ -0,0 +1,31 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + public static partial class Utils { + public static Expression Convert(Expression expression, Type type) { + ContractUtils.RequiresNotNull(expression, "expression"); + + if (expression.Type == type) { + return expression; + } + return Expression.Convert(expression, type); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/UnboundAssignment.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/UnboundAssignment.cs new file mode 100644 index 0000000000..5ce9c8b8ab --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/UnboundAssignment.cs @@ -0,0 +1,89 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Dynamic; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Ast { + public class UnboundAssignment : Expression { + private readonly SymbolId _name; + private readonly Expression _value; + + internal UnboundAssignment(SymbolId name, Expression value) { + _name = name; + _value = value; + } + + public override bool CanReduce { + get { return true; } + } + + protected override Type GetExpressionType() { + return typeof(object); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + public SymbolId Name { + get { return _name; } + } + + public Expression Value { + get { return _value; } + } + + public override Expression Reduce() { + return Expression.Call( + null, + typeof(ScriptingRuntimeHelpers).GetMethod("SetName"), + new Expression[] { + Utils.CodeContext(), + AstUtils.Constant(_name), + Expression.Convert(_value, typeof(object)) + } + ); + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) { + Expression v = visitor.Visit(_value); + if (v == _value) { + return this; + } + return Utils.Assign(_name, v); + } + } + + /// + /// Factory methods. + /// + public static partial class Utils { + [Obsolete("use Assign overload without SourceSpan")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "span")] + public static UnboundAssignment Assign(SymbolId name, Expression value, SourceSpan span) { + return Assign(name, value); + } + public static UnboundAssignment Assign(SymbolId name, Expression value) { + ContractUtils.Requires(!name.IsEmpty && !name.IsInvalid, "name", "Invalid or empty name is not allowed"); + ContractUtils.RequiresNotNull(value, "value"); + return new UnboundAssignment(name, value); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/UnboundExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/UnboundExpression.cs new file mode 100644 index 0000000000..02141c18b2 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/UnboundExpression.cs @@ -0,0 +1,68 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Ast { + public class UnboundExpression : Expression { + private readonly SymbolId _name; + + internal UnboundExpression(SymbolId name) { + _name = name; + } + + public override bool CanReduce { + get { return true; } + } + + protected override Type GetExpressionType() { + return typeof(object); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + public SymbolId Name { + get { return _name; } + } + + public override Expression Reduce() { + return Expression.Call( + typeof(ScriptingRuntimeHelpers).GetMethod("LookupName"), + Utils.CodeContext(), + AstUtils.Constant(_name) + ); + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) { + return this; + } + } + + /// + /// Factory methods + /// + public static partial class Utils { + public static UnboundExpression Read(SymbolId name) { + ContractUtils.Requires(!name.IsInvalid && !name.IsEmpty, "name", "Invalid or empty name is not allowed"); + return new UnboundExpression(name); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/VariableDictionaryExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/VariableDictionaryExpression.cs new file mode 100644 index 0000000000..8ea4ef8c9b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/VariableDictionaryExpression.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + + public partial class Utils { + public static Expression VariableDictionary(params ParameterExpression[] variables) { + return VariableDictionary((IEnumerable)variables); + } + public static Expression VariableDictionary(IEnumerable variables) { + var vars = variables.ToReadOnly(); + return Expression.New( + typeof(LocalsDictionary).GetConstructor(new[] { typeof(IList), typeof(SymbolId[]) }), + Expression.RuntimeVariables(vars), + Expression.Constant(vars.Map(v => SymbolTable.StringToIdOrEmpty(v.Name))) + ); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Ast/YieldExpression.cs b/merlin/main/runtime/Microsoft.Scripting/Ast/YieldExpression.cs new file mode 100644 index 0000000000..f31f1c12be --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Ast/YieldExpression.cs @@ -0,0 +1,107 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Ast { + /// + /// Represents either a YieldBreak or YieldReturn in a GeneratorExpression + /// If Value is non-null, it's a YieldReturn; otherwise it's a YieldBreak + /// and executing it will stop enumeration of the generator, causing + /// MoveNext to return false. + /// + public sealed class YieldExpression : Expression { + private readonly LabelTarget _target; + private readonly Expression _value; + private readonly int _yieldMarker; + + internal YieldExpression(LabelTarget target, Expression value, int yieldMarker) { + _target = target; + _value = value; + _yieldMarker = yieldMarker; + } + + public override bool CanReduce { + get { return false; } + } + + protected override Type GetExpressionType() { + return typeof(void); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Extension; + } + + /// + /// The value yieled from this expression, if it is a yield return + /// + public Expression Value { + get { return _value; } + } + + /// + /// The label used to yield from this generator + /// + public LabelTarget Target { + get { return _target; } + } + + public int YieldMarker { + get { + return _yieldMarker; + } + } + + protected override Expression VisitChildren(ExpressionVisitor visitor) { + Expression v = visitor.Visit(_value); + if (v == _value) { + return this; + } + return Utils.MakeYield(_target, v, YieldMarker); + } + } + + public partial class Utils { + public static YieldExpression YieldBreak(LabelTarget target) { + return MakeYield(target, null, -1); + } + public static YieldExpression YieldReturn(LabelTarget target, Expression value) { + return MakeYield(target, value, -1); + } + public static YieldExpression YieldReturn(LabelTarget target, Expression value, int yieldMarker) { + ContractUtils.RequiresNotNull(value, "value"); + return MakeYield(target, value, yieldMarker); + } + public static YieldExpression MakeYield(LabelTarget target, Expression value, int yieldMarker) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.Requires(target.Type != typeof(void), "target", "generator label must have a non-void type"); + if (value != null) { + if (!TypeUtils.AreReferenceAssignable(target.Type, value.Type)) { + // C# autoquotes generator return values + if (target.Type.IsSubclassOf(typeof(Expression)) && + TypeUtils.AreAssignable(target.Type, value.GetType())) { + value = Expression.Quote(value); + } + throw new ArgumentException(string.Format("Expression of type '{0}' cannot be yielded to a generator label of type '{1}'", value.Type, target.Type)); + } + } + + return new YieldExpression(target, value, yieldMarker); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComClassMetaObject.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComClassMetaObject.cs new file mode 100644 index 0000000000..e9f274bcc5 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComClassMetaObject.cs @@ -0,0 +1,43 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Linq.Expressions; +using System.Dynamic.Binders; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.ComInterop { + + internal class ComClassMetaObject : MetaObject { + internal ComClassMetaObject(Expression expression, ComTypeClassDesc cls) + : base(expression, Restrictions.Empty, cls) { + } + + public override MetaObject BindCreateInstance(CreateInstanceBinder binder, MetaObject[] args) { + return new MetaObject( + Expression.Call( + AstUtils.Convert(Expression, typeof(ComTypeClassDesc)), + typeof(ComTypeClassDesc).GetMethod("CreateInstance") + ), + Restrictions.Combine(args).Merge( + Restrictions.GetTypeRestriction(Expression, typeof(ComTypeClassDesc)) + ) + ); + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComEventDesc.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComEventDesc.cs new file mode 100644 index 0000000000..9944ac841a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComEventDesc.cs @@ -0,0 +1,27 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System; + +namespace Microsoft.Scripting.ComInterop { + internal class ComEventDesc { + internal Guid sourceIID; + internal int dispid; + }; +} + +#endif \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComHresults.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComHresults.cs new file mode 100644 index 0000000000..285ef3b6ea --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComHresults.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +namespace Microsoft.Scripting.ComInterop { + + internal static class ComHresults { + + internal const int S_OK = 0; + + internal const int CONNECT_E_NOCONNECTION = unchecked((int)0x80040200); + + internal const int DISP_E_UNKNOWNINTERFACE = unchecked((int)0x80020001); + internal const int DISP_E_MEMBERNOTFOUND = unchecked((int)0x80020003); + internal const int DISP_E_PARAMNOTFOUND = unchecked((int)0x80020004); + internal const int DISP_E_TYPEMISMATCH = unchecked((int)0x80020005); + internal const int DISP_E_UNKNOWNNAME = unchecked((int)0x80020006); // GetIDsOfName + internal const int DISP_E_NONAMEDARGS = unchecked((int)0x80020007); + internal const int DISP_E_BADVARTYPE = unchecked((int)0x80020008); + internal const int DISP_E_EXCEPTION = unchecked((int)0x80020009); + internal const int DISP_E_OVERFLOW = unchecked((int)0x8002000A); + internal const int DISP_E_BADINDEX = unchecked((int)0x8002000B); // GetTypeInfo + internal const int DISP_E_UNKNOWNLCID = unchecked((int)0x8002000C); + internal const int DISP_E_ARRAYISLOCKED = unchecked((int)0x8002000D); // VariantClear + internal const int DISP_E_BADPARAMCOUNT = unchecked((int)0x8002000E); + internal const int DISP_E_PARAMNOTOPTIONAL = unchecked((int)0x8002000F); + + internal const int E_NOINTERFACE = unchecked((int)0x80004002); + internal const int E_FAIL = unchecked((int)0x80004005); + + internal const int TYPE_E_LIBNOTREGISTERED = unchecked((int)0x8002801D); + + internal static bool IsSuccess(int hresult) { + return hresult >= 0; + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComInterop.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComInterop.cs new file mode 100644 index 0000000000..563440e9ff --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComInterop.cs @@ -0,0 +1,94 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System; +using System.Runtime.InteropServices; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace Microsoft.Scripting.ComInterop { + + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIDispatch), + Guid("00020400-0000-0000-C000-000000000046") + ] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1040:AvoidEmptyInterfaces")] + internal interface IDispatchForReflection { + } + + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("00020400-0000-0000-C000-000000000046"), + ] + internal interface IDispatch { + + [PreserveSig] + int TryGetTypeInfoCount(out uint pctinfo); + + [PreserveSig] + int TryGetTypeInfo(uint iTInfo, int lcid, out IntPtr info); + + [PreserveSig] + int TryGetIDsOfNames( + ref Guid iid, + [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] + string[] names, + uint cNames, + int lcid, + [Out] + [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4, SizeParamIndex = 2)] + int[] rgDispId); + + [PreserveSig] + int TryInvoke( + int dispIdMember, + ref Guid riid, + int lcid, + ComTypes.INVOKEKIND wFlags, + ref ComTypes.DISPPARAMS pDispParams, + out object VarResult, + out ComTypes.EXCEPINFO pExcepInfo, + out uint puArgErr); + } + + /// + /// Layout of the IDispatch vtable + /// + internal enum IDispatchMethodIndices { + IUnknown_QueryInterface, + IUnknown_AddRef, + IUnknown_Release, + + IDispatch_GetTypeInfoCount , + IDispatch_GetTypeInfo, + IDispatch_GetIDsOfNames, + IDispatch_Invoke + } + + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("B196B283-BAB4-101A-B69C-00AA00341D07") + ] + internal interface IProvideClassInfo { + void GetClassInfo(out IntPtr info); + } + +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComMethodDesc.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComMethodDesc.cs new file mode 100644 index 0000000000..32c739ce59 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComMethodDesc.cs @@ -0,0 +1,183 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using System.Globalization; +using Marshal = System.Runtime.InteropServices.Marshal; + +namespace Microsoft.Scripting.ComInterop { + + public class ComMethodDesc { + + private readonly bool _hasTypeInfo; + private readonly int _memid; // this is the member id extracted from FUNCDESC.memid + private readonly string _name; + internal readonly INVOKEKIND InvokeKind; + private readonly ComParamDesc _returnValue; + private readonly ComParamDesc[] _parameters; + + private ComMethodDesc(int dispId) { + _memid = dispId; + } + + internal ComMethodDesc(string name, int dispId) + : this(dispId) { + // no ITypeInfo constructor + _name = name; + } + + internal ComMethodDesc(string name, int dispId, INVOKEKIND invkind) + : this(name, dispId) { + InvokeKind = invkind; + } + + internal ComMethodDesc(ITypeInfo typeInfo, FUNCDESC funcDesc) + : this(funcDesc.memid) { + + _hasTypeInfo = true; + InvokeKind = funcDesc.invkind; + + ELEMDESC returnValue = funcDesc.elemdescFunc; + _returnValue = new ComParamDesc(ref returnValue); + + int cNames; + string[] rgNames = new string[1 + funcDesc.cParams]; + typeInfo.GetNames(_memid, rgNames, rgNames.Length, out cNames); + if (IsPropertyPut) { + rgNames[rgNames.Length - 1] = "value"; + cNames++; + } + Debug.Assert(cNames == rgNames.Length); + _name = rgNames[0]; + + _parameters = new ComParamDesc[funcDesc.cParams]; + + int offset = 0; + for (int i = 0; i < funcDesc.cParams; i++) { + ELEMDESC elemDesc = (ELEMDESC)Marshal.PtrToStructure( + new IntPtr(funcDesc.lprgelemdescParam.ToInt64() + offset), + typeof(ELEMDESC)); + + _parameters[i] = new ComParamDesc(ref elemDesc, rgNames[1 + i]); + + offset += Marshal.SizeOf(typeof(ELEMDESC)); + } + } + + internal bool HasTypeInfo { + get { + return _hasTypeInfo; + } + } + + internal string DispIdString { + get { + return String.Format(CultureInfo.InvariantCulture, "[DISPID={0}]", _memid); + } + } + + public string Name { + get { + Debug.Assert(_name != null); + return _name; + } + } + + public int DispId { + get { return _memid; } + } + + public bool IsPropertyGet { + get { + return (InvokeKind & INVOKEKIND.INVOKE_PROPERTYGET) != 0; + } + } + + public bool IsPropertyPut { + get { + return (InvokeKind & (INVOKEKIND.INVOKE_PROPERTYPUT | INVOKEKIND.INVOKE_PROPERTYPUTREF)) != 0; + } + } + + public bool IsPropertyPutRef { + get { + return (InvokeKind & INVOKEKIND.INVOKE_PROPERTYPUTREF) != 0; + } + } + + internal ComParamDesc[] Parameters { + get { + Debug.Assert((_parameters != null) == _hasTypeInfo); + return _parameters; + } + } + + public bool HasByrefOrOutParameters { + get { + if (!_hasTypeInfo) { + // We have just a dispId and not ITypeInfo to get the list of parameters. + // We have to assume that all parameters are In parameters. The user will explicitly have + // to pass StrongBox objects to represent ref or out parameters. + return false; + } + + for (int i = 0; i < _parameters.Length; i++) { + if (_parameters[i].ByReference || _parameters[i].IsOut) { + return true; + } + } + + return false; + } + } + + public string Signature { + get { + if (!_hasTypeInfo) { + return _name + "(...)"; + } + + StringBuilder result = new StringBuilder(); + if (_returnValue.ParameterType == null) { + result.Append("void"); + } else { + result.Append(_returnValue.ToString()); + } + result.Append(" "); + result.Append(_name); + result.Append("("); + for (int i = 0; i < _parameters.Length; i++) { + result.Append(_parameters[i].ToString()); + if (i < (_parameters.Length - 1)) { + result.Append(", "); + } + } + result.Append(")"); + return result.ToString(); + } + } + + public override string ToString() { + return Signature; + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComParamDesc.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComParamDesc.cs new file mode 100644 index 0000000000..dfc36f7133 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComParamDesc.cs @@ -0,0 +1,244 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using Marshal = System.Runtime.InteropServices.Marshal; +using VarEnum = System.Runtime.InteropServices.VarEnum; + +namespace Microsoft.Scripting.ComInterop { + + /// + /// The parameter description of a method defined in a type library + /// + public class ComParamDesc { + # region private fields + + private readonly bool _isOut; // is an output parameter? + private readonly bool _isOpt; // is an optional parameter? + private readonly bool _byRef; // is a reference or pointer parameter? + private readonly bool _isArray; + private readonly VarEnum _vt; + private readonly string _name; + private readonly Type _type; + private readonly object _defaultValue; + + # endregion + + # region ctor + + /// + /// Creates a representation for the paramter of a COM method + /// + internal ComParamDesc(ref ELEMDESC elemDesc, string name) { + // Ensure _defaultValue is set to DBNull.Value regardless of whether or not the + // default value is extracted from the parameter description. Failure to do so + // yields a runtime exception in the ToString() function. + _defaultValue = DBNull.Value; + + if (!String.IsNullOrEmpty(name)) { + // This is a parameter, not a return value + this._isOut = (elemDesc.desc.paramdesc.wParamFlags & PARAMFLAG.PARAMFLAG_FOUT) != 0; + this._isOpt = (elemDesc.desc.paramdesc.wParamFlags & PARAMFLAG.PARAMFLAG_FOPT) != 0; + // TODO: The PARAMDESCEX struct has a memory issue that needs to be resolved. For now, we ignore it. + //_defaultValue = PARAMDESCEX.GetDefaultValue(ref elemDesc.desc.paramdesc); + } + + _name = name; + _vt = (VarEnum)elemDesc.tdesc.vt; + TYPEDESC typeDesc = elemDesc.tdesc; + while (true) { + if (_vt == VarEnum.VT_PTR) { + this._byRef = true; + } else if (_vt == VarEnum.VT_ARRAY) { + this._isArray = true; + } else { + break; + } + + TYPEDESC childTypeDesc = (TYPEDESC)Marshal.PtrToStructure(typeDesc.lpValue, typeof(TYPEDESC)); + _vt = (VarEnum)childTypeDesc.vt; + typeDesc = childTypeDesc; + } + + VarEnum vtWithoutByref = _vt; + if ((_vt & VarEnum.VT_BYREF) != 0) { + vtWithoutByref = (_vt & ~VarEnum.VT_BYREF); + _byRef = true; + } + + _type = GetTypeForVarEnum(vtWithoutByref); + } + + /// + /// Creates a representation for the return value of a COM method + /// TODO: Return values should be represented by a different type + /// + internal ComParamDesc(ref ELEMDESC elemDesc) + : this(ref elemDesc, String.Empty) { + } + + //internal struct PARAMDESCEX { + // private ulong _cByte; + // private Variant _varDefaultValue; + + // internal void Dummy() { + // _cByte = 0; + // throw Error.MethodShouldNotBeCalled(); + // } + + // internal static object GetDefaultValue(ref PARAMDESC paramdesc) { + // if ((paramdesc.wParamFlags & PARAMFLAG.PARAMFLAG_FHASDEFAULT) == 0) { + // return DBNull.Value; + // } + + // PARAMDESCEX varValue = (PARAMDESCEX)Marshal.PtrToStructure(paramdesc.lpVarValue, typeof(PARAMDESCEX)); + // if (varValue._cByte != (ulong)(Marshal.SizeOf((typeof(PARAMDESCEX))))) { + // throw Error.DefaultValueCannotBeRead(); + // } + + // return varValue._varDefaultValue.ToObject(); + // } + //} + + private static Type GetTypeForVarEnum(VarEnum vt) { + Type type; + + switch (vt) { + // VarEnums which can be used in VARIANTs, but which cannot occur in a TYPEDESC + case VarEnum.VT_EMPTY: + case VarEnum.VT_NULL: + case VarEnum.VT_RECORD: + throw new InvalidOperationException(string.Format("Unexpected VarEnum {0}.", vt)); + + // VarEnums which are not used in VARIANTs, but which can occur in a TYPEDESC + case VarEnum.VT_VOID: + type = null; + break; + +#if DISABLE // TODO: WTypes.h indicates that these cannot be used in VARIANTs, but Type.InvokeMember seems to allow it + case VarEnum.VT_I8: + case VarEnum.UI8: +#endif + case VarEnum.VT_HRESULT: + type = typeof(int); + break; + + case ((VarEnum)37): // VT_INT_PTR: + case VarEnum.VT_PTR: + type = typeof(IntPtr); + break; + + case ((VarEnum)38): // VT_UINT_PTR: + type = typeof(UIntPtr); + break; + + case VarEnum.VT_SAFEARRAY: + case VarEnum.VT_CARRAY: + type = typeof(Array); + break; + + case VarEnum.VT_LPSTR: + case VarEnum.VT_LPWSTR: + type = typeof(string); + break; + + case VarEnum.VT_USERDEFINED: + type = typeof(object); + break; + + // For VarEnums that can be used in VARIANTs and well as TYPEDESCs, just use VarEnumSelector + default: + type = VarEnumSelector.GetManagedMarshalType(vt); + break; + } + + return type; + } + + public override string ToString() { + StringBuilder result = new StringBuilder(); + if (_isOpt) { + result.Append("[Optional] "); + } + + if (_isOut) { + result.Append("[out]"); + } + + result.Append(_type.Name); + + if (_isArray) { + result.Append("[]"); + } + + if (_byRef) { + result.Append("&"); + } + + result.Append(" "); + result.Append(_name); + + if (_defaultValue != DBNull.Value) { + result.Append("="); + result.Append(_defaultValue.ToString()); + } + + return result.ToString(); + } + + # endregion + + # region properties + + public bool IsOut { + get { return _isOut; } + } + + public bool IsOptional { + get { return _isOpt; } + } + + public bool ByReference { + get { return _byRef; } + } + + public bool IsArray { + get { return _isArray; } + } + + public Type ParameterType { + get { + return _type; + } + } + + /// + /// DBNull.Value if there is no default value + /// + internal object DefaultValue { + get { + return _defaultValue; + } + } + + # endregion + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComRuntimeHelpers.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComRuntimeHelpers.cs new file mode 100644 index 0000000000..efcba7718d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComRuntimeHelpers.cs @@ -0,0 +1,188 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using Microsoft.Scripting.Utils; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace Microsoft.Scripting.ComInterop { + + internal static class ComRuntimeHelpers { + + internal static void GetInfoFromType(ComTypes.ITypeInfo typeInfo, out string name, out string documentation) { + int dwHelpContext; + string strHelpFile; + + typeInfo.GetDocumentation(-1, out name, out documentation, out dwHelpContext, out strHelpFile); + } + + internal static string GetNameOfMethod(ComTypes.ITypeInfo typeInfo, int memid) { + int cNames; + string[] rgNames = new string[1]; + typeInfo.GetNames(memid, rgNames, 1, out cNames); + return rgNames[0]; + } + + internal static string GetNameOfLib(ComTypes.ITypeLib typeLib) { + string name; + string strDocString; + int dwHelpContext; + string strHelpFile; + + typeLib.GetDocumentation(-1, out name, out strDocString, out dwHelpContext, out strHelpFile); + return name; + } + + internal static string GetNameOfType(ComTypes.ITypeInfo typeInfo) { + string name; + string documentation; + GetInfoFromType(typeInfo, out name, out documentation); + + return name; + } + + /// + /// Look for typeinfo using IDispatch.GetTypeInfo + /// + /// + /// + /// Some COM objects just dont expose typeinfo. In these cases, this method will return null. + /// Some COM objects do intend to expose typeinfo, but may not be able to do so if the type-library is not properly + /// registered. This will be considered as acceptable or as an error condition depending on throwIfMissingExpectedTypeInfo + /// + internal static ComTypes.ITypeInfo GetITypeInfoFromIDispatch(IDispatch dispatch, bool throwIfMissingExpectedTypeInfo) { + uint typeCount; + int hresult = dispatch.TryGetTypeInfoCount(out typeCount); + Marshal.ThrowExceptionForHR(hresult); + Debug.Assert(typeCount <= 1); + if (typeCount == 0) { + return null; + } + + IntPtr typeInfoPtr = IntPtr.Zero; + + hresult = dispatch.TryGetTypeInfo(0, 0, out typeInfoPtr); + if (!ComHresults.IsSuccess(hresult)) { + CheckIfMissingTypeInfoIsExpected(hresult, throwIfMissingExpectedTypeInfo); + return null; + } + if (typeInfoPtr == IntPtr.Zero) { // be defensive against components that return IntPtr.Zero + if (throwIfMissingExpectedTypeInfo) { + Marshal.ThrowExceptionForHR(ComHresults.E_FAIL); + } + return null; + } + + ComTypes.ITypeInfo typeInfo = null; + try { + typeInfo = Marshal.GetObjectForIUnknown(typeInfoPtr) as ComTypes.ITypeInfo; + } finally { + Marshal.Release(typeInfoPtr); + } + + return typeInfo; + } + + /// + /// This method should be called when typeinfo is not available for an object. The function + /// will check if the typeinfo is expected to be missing. This can include error cases where + /// the same error is guaranteed to happen all the time, on all machines, under all circumstances. + /// In such cases, we just have to operate without the typeinfo. + /// + /// However, if accessing the typeinfo is failing in a transient way, we might want to throw + /// an exception so that we will eagerly predictably indicate the problem. + /// + private static void CheckIfMissingTypeInfoIsExpected(int hresult, bool throwIfMissingExpectedTypeInfo) { + Debug.Assert(!ComHresults.IsSuccess(hresult)); + + // Word.Basic always returns this because of an incorrect implementation of IDispatch.GetTypeInfo + // Any implementation that returns E_NOINTERFACE is likely to do so in all environments + if (hresult == ComHresults.E_NOINTERFACE) { + return; + } + + // This assert is potentially over-restrictive since COM components can behave in quite unexpected ways. + // However, asserting the common expected cases ensures that we find out about the unexpected scenarios, and + // can investigate the scenarios to ensure that there is no bug in our own code. + Debug.Assert(hresult == ComHresults.TYPE_E_LIBNOTREGISTERED); + + if (throwIfMissingExpectedTypeInfo) { + Marshal.ThrowExceptionForHR(hresult); + } + } + + // TODO: we should be throwing a different exception here + // COMException sounds right but it has a specific meaning to the + // runtime + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] + internal static ComTypes.TYPEATTR GetTypeAttrForTypeInfo(ComTypes.ITypeInfo typeInfo) { + IntPtr pAttrs = IntPtr.Zero; + typeInfo.GetTypeAttr(out pAttrs); + + // GetTypeAttr should never return null, this is just to be safe + if (pAttrs == IntPtr.Zero) { + throw new COMException("Cannot retrieve type information"); + } + + try { + return (ComTypes.TYPEATTR)Marshal.PtrToStructure(pAttrs, typeof(ComTypes.TYPEATTR)); + } finally { + typeInfo.ReleaseTypeAttr(pAttrs); + } + } + + // TODO: we should be throwing a different exception here + // COMException sounds right but it has a specific meaning to the + // runtime + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] + internal static ComTypes.TYPELIBATTR GetTypeAttrForTypeLib(ComTypes.ITypeLib typeLib) { + IntPtr pAttrs = IntPtr.Zero; + typeLib.GetLibAttr(out pAttrs); + + // GetTypeAttr should never return null, this is just to be safe + if (pAttrs == IntPtr.Zero) { + throw new COMException("Cannot retrieve type information"); + } + + try { + return (ComTypes.TYPELIBATTR)Marshal.PtrToStructure(pAttrs, typeof(ComTypes.TYPELIBATTR)); + } finally { + typeLib.ReleaseTLibAttr(pAttrs); + } + } + } + + public static class UnsafeNativeMethods { + [System.Runtime.Versioning.ResourceExposure(System.Runtime.Versioning.ResourceScope.None)] + [System.Runtime.Versioning.ResourceConsumption(System.Runtime.Versioning.ResourceScope.Process, System.Runtime.Versioning.ResourceScope.Process)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")] // TODO: fix + [DllImport("oleaut32.dll", PreserveSig = false)] + internal static extern void VariantClear(IntPtr variant); + + [System.Runtime.Versioning.ResourceExposure(System.Runtime.Versioning.ResourceScope.Machine)] + [System.Runtime.Versioning.ResourceConsumption(System.Runtime.Versioning.ResourceScope.Machine, System.Runtime.Versioning.ResourceScope.Machine)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")] // TODO: fix + [DllImport("oleaut32.dll", PreserveSig = false)] + internal static extern ComTypes.ITypeLib LoadRegTypeLib(ref Guid clsid, short majorVersion, short minorVersion, int lcid); + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComType.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComType.cs new file mode 100644 index 0000000000..6ccf10daa8 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComType.cs @@ -0,0 +1,27 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +namespace Microsoft.Scripting.ComInterop { + + public enum ComType { + Class, + Enum, + Interface + }; +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeClassDesc.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeClassDesc.cs new file mode 100644 index 0000000000..23d8e481ba --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeClassDesc.cs @@ -0,0 +1,90 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Dynamic.Binders; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace Microsoft.Scripting.ComInterop { + + public class ComTypeClassDesc : ComTypeDesc, IDynamicObject { + LinkedList _itfs; // implemented interfaces + LinkedList _sourceItfs; // source interfaces supported by this coclass + private Type _typeObj; + + public object CreateInstance() { + if (_typeObj == null) { + _typeObj = System.Type.GetTypeFromCLSID(Guid); + } + return System.Activator.CreateInstance(System.Type.GetTypeFromCLSID(Guid)); + } + + internal ComTypeClassDesc(ComTypes.ITypeInfo typeInfo, ComTypeLibDesc typeLibDesc) : + base(typeInfo, ComType.Class, typeLibDesc) { + ComTypes.TYPEATTR typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo); + Guid = typeAttr.guid; + + for (int i = 0; i < typeAttr.cImplTypes; i++) { + int hRefType; + typeInfo.GetRefTypeOfImplType(i, out hRefType); + ComTypes.ITypeInfo currentTypeInfo; + typeInfo.GetRefTypeInfo(hRefType, out currentTypeInfo); + + ComTypes.IMPLTYPEFLAGS implTypeFlags; + typeInfo.GetImplTypeFlags(i, out implTypeFlags); + + bool isSourceItf = (implTypeFlags & ComTypes.IMPLTYPEFLAGS.IMPLTYPEFLAG_FSOURCE) != 0; + AddInterface(currentTypeInfo, isSourceItf); + } + } + + private void AddInterface(ComTypes.ITypeInfo itfTypeInfo, bool isSourceItf) { + string itfName = ComRuntimeHelpers.GetNameOfType(itfTypeInfo); + + if (isSourceItf) { + if (_sourceItfs == null) { + _sourceItfs = new LinkedList(); + } + _sourceItfs.AddLast(itfName); + } else { + if (_itfs == null) { + _itfs = new LinkedList(); + } + _itfs.AddLast(itfName); + } + } + + internal bool Implements(string itfName, bool isSourceItf) { + if (isSourceItf) + return _sourceItfs.Contains(itfName); + else + return _itfs.Contains(itfName); + } + + #region IDynamicObject Members + + public MetaObject GetMetaObject(Expression parameter) { + return new ComClassMetaObject(parameter, this); + } + + #endregion + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeDesc.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeDesc.cs new file mode 100644 index 0000000000..804139d24a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeDesc.cs @@ -0,0 +1,119 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.InteropServices.ComTypes; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace Microsoft.Scripting.ComInterop { + + public class ComTypeDesc : ComTypeLibMemberDesc { + private string _typeName; + private string _documentation; + private Guid _guid; + private Dictionary _funcs; + private Dictionary _puts; + private Dictionary _events; + private ComMethodDesc _getItem; + private ComMethodDesc _setItem; + private readonly ComTypeLibDesc _typeLibDesc; + private static readonly Dictionary _EmptyEventsDict = new Dictionary(); + + internal ComTypeDesc(ITypeInfo typeInfo, ComType memberType, ComTypeLibDesc typeLibDesc) : base(memberType) { + if (typeInfo != null) { + ComRuntimeHelpers.GetInfoFromType(typeInfo, out _typeName, out _documentation); + } + _typeLibDesc = typeLibDesc; + } + + internal static ComTypeDesc FromITypeInfo(ComTypes.ITypeInfo typeInfo, ComTypeLibDesc typeLibDesc) { + ComTypes.TYPEATTR typeAttr; + typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo); + if (typeAttr.typekind == ComTypes.TYPEKIND.TKIND_COCLASS) { + return new ComTypeClassDesc(typeInfo, typeLibDesc); + } else if (typeAttr.typekind == ComTypes.TYPEKIND.TKIND_ENUM) { + return new ComTypeEnumDesc(typeInfo, typeLibDesc); + } else if ((typeAttr.typekind == ComTypes.TYPEKIND.TKIND_DISPATCH) || + (typeAttr.typekind == ComTypes.TYPEKIND.TKIND_INTERFACE)) { + ComTypeDesc typeDesc = new ComTypeDesc(typeInfo, ComType.Interface, typeLibDesc); + return typeDesc; + } else { + throw new InvalidOperationException("Attempting to wrap an unsupported enum type."); + } + } + + internal static ComTypeDesc CreateEmptyTypeDesc() { + ComTypeDesc typeDesc = new ComTypeDesc(null, ComType.Interface, null); + typeDesc._funcs = new Dictionary(); + typeDesc._events = _EmptyEventsDict; + + return typeDesc; + } + + internal static Dictionary EmptyEvents { + get { return _EmptyEventsDict; } + } + + internal Dictionary Funcs { + get { return _funcs; } + set { _funcs = value; } + } + + internal Dictionary Puts { + get { return _puts; } + set { _puts = value; } + } + + internal Dictionary Events { + get { return _events; } + set { _events = value; } + } + + // this property is public - accessed by an AST + public string TypeName { + get { return _typeName; } + } + + internal string Documentation { + get { return _documentation; } + } + + // this property is public - accessed by an AST + public ComTypeLibDesc TypeLib { + get { return _typeLibDesc; } + } + + internal Guid Guid { + get { return _guid; } + set { _guid = value; } + } + + internal ComMethodDesc GetItem { + get { return _getItem; } + set { _getItem = value; } + } + + internal ComMethodDesc SetItem { + get { return _setItem; } + set { _setItem = value; } + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeEnumDesc.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeEnumDesc.cs new file mode 100644 index 0000000000..16d1b6d2f9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeEnumDesc.cs @@ -0,0 +1,98 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Dynamic.Binders; +using System.Globalization; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace Microsoft.Scripting.ComInterop { + + public sealed class ComTypeEnumDesc : ComTypeDesc, IDynamicObject { + private readonly string[] _memberNames; + private readonly object[] _memberValues; + + public override string ToString() { + return String.Format(CultureInfo.CurrentCulture, "", TypeName); + } + + internal ComTypeEnumDesc(ComTypes.ITypeInfo typeInfo, ComTypeLibDesc typeLibDesc) : + base(typeInfo, ComType.Enum, typeLibDesc) { + ComTypes.TYPEATTR typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo); + string[] memberNames = new string[typeAttr.cVars]; + object[] memberValues = new object[typeAttr.cVars]; + + IntPtr p = IntPtr.Zero; + + // For each enum member get name and value. + for (int i = 0; i < typeAttr.cVars; i++) { + typeInfo.GetVarDesc(i, out p); + + // Get the enum member value (as object). + ComTypes.VARDESC varDesc; + + try { + varDesc = (ComTypes.VARDESC)Marshal.PtrToStructure(p, typeof(ComTypes.VARDESC)); + + if (varDesc.varkind == ComTypes.VARKIND.VAR_CONST) { + memberValues[i] = Marshal.GetObjectForNativeVariant(varDesc.desc.lpvarValue); + } + } finally { + typeInfo.ReleaseVarDesc(p); + } + + // Get the enum member name + memberNames[i] = ComRuntimeHelpers.GetNameOfMethod(typeInfo, varDesc.memid); + } + + _memberNames = memberNames; + _memberValues = memberValues; + } + + MetaObject IDynamicObject.GetMetaObject(Expression parameter) { + return new TypeEnumMetaObject(this, parameter); + } + + public object GetValue(string enumValueName) { + for (int i = 0; i < _memberNames.Length; i++) { + if (_memberNames[i] == enumValueName) { + return _memberValues[i]; + } + } + + throw new MissingMemberException(enumValueName); + } + + internal bool HasMember(string name) { + for (int i = 0; i < _memberNames.Length; i++) { + if (_memberNames[i] == name) + return true; + } + + return false; + } + + // TODO: internal + public string[] GetMemberNames() { + return (string[])this._memberNames.Clone(); + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeLibDesc.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeLibDesc.cs new file mode 100644 index 0000000000..f105664d59 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeLibDesc.cs @@ -0,0 +1,213 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Dynamic.Binders; +using System.Globalization; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace Microsoft.Scripting.ComInterop { + + public sealed class ComTypeLibDesc : IDynamicObject { + + // typically typelibs contain very small number of coclasses + // so we will just use the linked list as it performs better + // on small number of entities + LinkedList _classes; + Dictionary _enums; + string _typeLibName; + ComTypes.TYPELIBATTR _typeLibAttributes; + + private static Dictionary _CachedTypeLibDesc = new Dictionary(); + + private ComTypeLibDesc() { + _enums = new Dictionary(); + _classes = new LinkedList(); + } + + public override string ToString() { + return String.Format(CultureInfo.CurrentCulture, "", _typeLibName); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + public string Documentation { + get { return String.Empty; } + } + + #region IDynamicObject Members + + MetaObject IDynamicObject.GetMetaObject(Expression parameter) { + return new TypeLibMetaObject(parameter, this); + } + + #endregion + + /// + /// Reads the latest registered type library for the corresponding GUID, + /// reads definitions of CoClass'es and Enum's from this library + /// and creates a IDynamicObject that allows to instantiate coclasses + /// and get actual values for the enums. + /// + /// Type Library Guid + /// ComTypeLibDesc object + [System.Runtime.Versioning.ResourceExposure(System.Runtime.Versioning.ResourceScope.Machine)] + [System.Runtime.Versioning.ResourceConsumption(System.Runtime.Versioning.ResourceScope.Machine, System.Runtime.Versioning.ResourceScope.Machine)] + public static ComTypeLibInfo CreateFromGuid(Guid typeLibGuid) { + // passing majorVersion = -1, minorVersion = -1 will always + // load the latest typelib + ComTypes.ITypeLib typeLib = UnsafeNativeMethods.LoadRegTypeLib(ref typeLibGuid, -1, -1, 0); + + return new ComTypeLibInfo(GetFromTypeLib(typeLib)); + } + + /// + /// Gets an ITypeLib object from OLE Automation compatible RCW , + /// reads definitions of CoClass'es and Enum's from this library + /// and creates a IDynamicObject that allows to instantiate coclasses + /// and get actual values for the enums. + /// + /// OLE automation compatible RCW + /// ComTypeLibDesc object + public static ComTypeLibInfo CreateFromObject(object rcw) { + if (Marshal.IsComObject(rcw) == false) { + throw new ArgumentException("COM object is expected."); + } + + ComTypes.ITypeInfo typeInfo = ComRuntimeHelpers.GetITypeInfoFromIDispatch(rcw as IDispatch, true); + + ComTypes.ITypeLib typeLib; + int typeInfoIndex; + typeInfo.GetContainingTypeLib(out typeLib, out typeInfoIndex); + + return new ComTypeLibInfo(GetFromTypeLib(typeLib)); + } + + internal static ComTypeLibDesc GetFromTypeLib(ComTypes.ITypeLib typeLib) { + // check whether we have already loaded this type library + ComTypes.TYPELIBATTR typeLibAttr = ComRuntimeHelpers.GetTypeAttrForTypeLib(typeLib); + ComTypeLibDesc typeLibDesc; + lock (_CachedTypeLibDesc) { + if (_CachedTypeLibDesc.TryGetValue(typeLibAttr.guid, out typeLibDesc)) { + return typeLibDesc; + } + } + + typeLibDesc = new ComTypeLibDesc(); + + typeLibDesc._typeLibName = ComRuntimeHelpers.GetNameOfLib(typeLib); + typeLibDesc._typeLibAttributes = typeLibAttr; + + int countTypes = typeLib.GetTypeInfoCount(); + for (int i = 0; i < countTypes; i++) { + ComTypes.TYPEKIND typeKind; + typeLib.GetTypeInfoType(i, out typeKind); + + ComTypes.ITypeInfo typeInfo; + if (typeKind == ComTypes.TYPEKIND.TKIND_COCLASS) { + typeLib.GetTypeInfo(i, out typeInfo); + ComTypeClassDesc classDesc = new ComTypeClassDesc(typeInfo, typeLibDesc); + typeLibDesc._classes.AddLast(classDesc); + } else if (typeKind == ComTypes.TYPEKIND.TKIND_ENUM) { + typeLib.GetTypeInfo(i, out typeInfo); + ComTypeEnumDesc enumDesc = new ComTypeEnumDesc(typeInfo, typeLibDesc); + typeLibDesc._enums.Add(enumDesc.TypeName, enumDesc); + } + } + + // cached the typelib using the guid as the dictionary key + lock (_CachedTypeLibDesc) { + _CachedTypeLibDesc.Add(typeLibAttr.guid, typeLibDesc); + } + + return typeLibDesc; + } + + public object GetTypeLibObjectDesc(string member) { + foreach (ComTypeClassDesc coclass in _classes) { + if (member == coclass.TypeName) { + return coclass; + } + } + + ComTypeEnumDesc enumDesc; + if (_enums != null && _enums.TryGetValue(member, out enumDesc) == true) + return enumDesc; + + return null; + } + + // TODO: internal + public string[] GetMemberNames() { + string[] retval = new string[_enums.Count + _classes.Count]; + int i = 0; + + foreach (ComTypeClassDesc coclass in _classes) { + retval[i++] = coclass.TypeName; + } + + foreach (KeyValuePair enumDesc in _enums) { + retval[i++] = enumDesc.Key; + } + + return retval; + } + + internal bool HasMember(string member) { + foreach (ComTypeClassDesc coclass in _classes) { + if (member == coclass.TypeName) { + return true; + } + } + + if (_enums.ContainsKey(member) == true) + return true; + + return false; + } + + public Guid Guid { + get { return _typeLibAttributes.guid; } + } + + public short VersionMajor { + get { return _typeLibAttributes.wMajorVerNum; } + } + + public short VersionMinor { + get { return _typeLibAttributes.wMinorVerNum; } + } + + public string Name { + get { return _typeLibName; } + } + + internal ComTypeClassDesc GetCoClassForInterface(string itfName) { + foreach (ComTypeClassDesc coclass in _classes) { + if (coclass.Implements(itfName, false)) { + return coclass; + } + } + + return null; + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeLibInfo.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeLibInfo.cs new file mode 100644 index 0000000000..86131a872b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeLibInfo.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System; +using System.Linq.Expressions; +using System.Dynamic.Binders; + +namespace Microsoft.Scripting.ComInterop { + + public sealed class ComTypeLibInfo : IDynamicObject { + private readonly ComTypeLibDesc _typeLibDesc; + + internal ComTypeLibInfo(ComTypeLibDesc typeLibDesc) { + _typeLibDesc = typeLibDesc; + } + + public string Name { + get { return _typeLibDesc.Name; } + } + + public Guid Guid { + get { return _typeLibDesc.Guid; } + } + + public short VersionMajor { + get { return _typeLibDesc.VersionMajor; } + } + + public short VersionMinor { + get { return _typeLibDesc.VersionMinor; } + } + + public ComTypeLibDesc TypeLibDesc { + get { return _typeLibDesc; } + } + + // TODO: internal + public string[] GetMemberNames() { + return new string[] { this.Name, "Guid", "Name", "VersionMajor", "VersionMinor" }; + } + + MetaObject IDynamicObject.GetMetaObject(Expression parameter) { + return new TypeLibInfoMetaObject(parameter, this); + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeLibMemberDesc.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeLibMemberDesc.cs new file mode 100644 index 0000000000..1892a2d7c6 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/ComTypeLibMemberDesc.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +namespace Microsoft.Scripting.ComInterop { + + public class ComTypeLibMemberDesc { + readonly ComType _kind; + + internal ComTypeLibMemberDesc(ComType kind) { + _kind = kind; + } + + public ComType Kind { + get { return _kind; } + } + } + +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/TypeEnumMetaObject.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/TypeEnumMetaObject.cs new file mode 100644 index 0000000000..7c1b14d2a8 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/TypeEnumMetaObject.cs @@ -0,0 +1,80 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Dynamic.Binders; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.ComInterop { + + internal class TypeEnumMetaObject : MetaObject { + private readonly ComTypeEnumDesc _desc; + + internal TypeEnumMetaObject(ComTypeEnumDesc desc, Expression expression) + : base(expression, Restrictions.Empty, desc) { + _desc = desc; + } + + public override MetaObject BindGetMember(GetMemberBinder binder) { + if (_desc.HasMember(binder.Name)) { + return new MetaObject( + // return (.bound $arg0).GetValue("") + Expression.Constant(((ComTypeEnumDesc)Value).GetValue(binder.Name)), + EnumRestrictions() + ); + } + + throw new NotImplementedException(); + } + + public override IEnumerable GetDynamicMemberNames() { + return _desc.GetMemberNames(); + } + + private Restrictions EnumRestrictions() { + return Restrictions.GetTypeRestriction( + Expression, typeof(ComTypeEnumDesc) + ).Merge( + // ((ComTypeEnumDesc)).TypeLib.Guid == + Restrictions.GetExpressionRestriction( + Expression.Equal( + Expression.Property( + Expression.Property( + AstUtils.Convert(Expression, typeof(ComTypeEnumDesc)), + typeof(ComTypeDesc).GetProperty("TypeLib")), + typeof(ComTypeLibDesc).GetProperty("Guid")), + Expression.Constant(_desc.TypeLib.Guid) + ) + ) + ).Merge( + Restrictions.GetExpressionRestriction( + Expression.Equal( + Expression.Property( + AstUtils.Convert(Expression, typeof(ComTypeEnumDesc)), + typeof(ComTypeEnumDesc).GetProperty("TypeName") + ), + Expression.Constant(_desc.TypeName) + ) + ) + ); + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/TypeLibInfoMetaObject.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/TypeLibInfoMetaObject.cs new file mode 100644 index 0000000000..c965870c78 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/TypeLibInfoMetaObject.cs @@ -0,0 +1,77 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Collections.Generic; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.ComInterop { + + internal sealed class TypeLibInfoMetaObject : MetaObject { + private readonly ComTypeLibInfo _info; + + internal TypeLibInfoMetaObject(Expression expression, ComTypeLibInfo info) + : base(expression, Restrictions.Empty, info) { + _info = info; + } + + public override MetaObject BindGetMember(GetMemberBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + string name = binder.Name; + + if (name == _info.Name) { + name = "TypeLibDesc"; + } else if (name != "Guid" && + name != "Name" && + name != "VersionMajor" && + name != "VersionMinor") { + + return binder.FallbackGetMember(this); + } + + return new MetaObject( + Expression.Property( + AstUtils.Convert(Expression, typeof(ComTypeLibInfo)), + typeof(ComTypeLibInfo).GetProperty(name) + ), + ComTypeLibInfoRestrictions(this) + ); + } + + public override IEnumerable GetDynamicMemberNames() { + return _info.GetMemberNames(); + } + + private Restrictions ComTypeLibInfoRestrictions(params MetaObject[] args) { + return Restrictions.Combine(args).Merge(Restrictions.GetTypeRestriction(Expression, typeof(ComTypeLibInfo))); + } + + private MetaObject RestrictThisToType() { + return new MetaObject( + AstUtils.Convert( + Expression, + typeof(ComTypeLibInfo) + ), + Restrictions.GetTypeRestriction(Expression, typeof(ComTypeLibInfo)) + ); + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/TypeLibMetaObject.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/TypeLibMetaObject.cs new file mode 100644 index 0000000000..4eda429422 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/TypeLibMetaObject.cs @@ -0,0 +1,69 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Collections.Generic; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.ComInterop { + + internal class TypeLibMetaObject : MetaObject { + private readonly ComTypeLibDesc _lib; + + internal TypeLibMetaObject(Expression expression, ComTypeLibDesc lib) + : base(expression, Restrictions.Empty, lib) { + _lib = lib; + } + + public override MetaObject BindGetMember(GetMemberBinder binder) { + if (_lib.HasMember(binder.Name)) { + Restrictions restrictions = + Restrictions.GetTypeRestriction( + Expression, typeof(ComTypeLibDesc) + ).Merge( + Restrictions.GetExpressionRestriction( + Expression.Equal( + Expression.Property( + AstUtils.Convert( + Expression, typeof(ComTypeLibDesc) + ), + typeof(ComTypeLibDesc).GetProperty("Guid") + ), + Expression.Constant(_lib.Guid) + ) + ) + ); + + return new MetaObject( + Expression.Constant( + ((ComTypeLibDesc)Value).GetTypeLibObjectDesc(binder.Name) + ), + restrictions + ); + } + + return base.BindGetMember(binder); + } + + public override IEnumerable GetDynamicMemberNames() { + return _lib.GetMemberNames(); + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/ComInterop/VarEnumSelector.cs b/merlin/main/runtime/Microsoft.Scripting/ComInterop/VarEnumSelector.cs new file mode 100644 index 0000000000..4681db7d13 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ComInterop/VarEnumSelector.cs @@ -0,0 +1,143 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Microsoft.Scripting.ComInterop { + + internal static class VarEnumSelector { + private static readonly Dictionary _ComToManagedPrimitiveTypes = CreateComToManagedPrimitiveTypes(); + + /// + /// Gets the managed type that an object needs to be coverted to in order for it to be able + /// to be represented as a Variant. + /// + /// In general, there is a many-to-many mapping between Type and VarEnum. However, this method + /// returns a simple mapping that is needed for the current implementation. The reason for the + /// many-to-many relation is: + /// 1. Int32 maps to VT_I4 as well as VT_ERROR, and Decimal maps to VT_DECIMAL and VT_CY. However, + /// this changes if you throw the wrapper types into the mix. + /// 2. There is no Type to represent COM types. __ComObject is a private type, and Object is too + /// general. + /// + internal static Type GetManagedMarshalType(VarEnum varEnum) { + Debug.Assert((varEnum & VarEnum.VT_BYREF) == 0); + + if (varEnum == VarEnum.VT_CY) { + return typeof(CurrencyWrapper); + } + + if (IsPrimitiveType(varEnum)) { + return _ComToManagedPrimitiveTypes[varEnum]; + } + + switch (varEnum) { + case VarEnum.VT_EMPTY: + case VarEnum.VT_NULL: + case VarEnum.VT_UNKNOWN: + case VarEnum.VT_DISPATCH: + case VarEnum.VT_VARIANT: + return typeof(Object); + + case VarEnum.VT_ERROR: + return typeof(ErrorWrapper); + + default: + throw new InvalidOperationException(string.Format("Unexpected VarEnum {0}.", varEnum)); + } + } + + private static Dictionary CreateComToManagedPrimitiveTypes() { + Dictionary dict = new Dictionary(); + + #region Generated ComToManagedPrimitiveTypes + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_ComToManagedPrimitiveTypes from: generate_comdispatch.py + + dict[VarEnum.VT_I1] = typeof(SByte); + dict[VarEnum.VT_I2] = typeof(Int16); + dict[VarEnum.VT_I4] = typeof(Int32); + dict[VarEnum.VT_I8] = typeof(Int64); + dict[VarEnum.VT_UI1] = typeof(Byte); + dict[VarEnum.VT_UI2] = typeof(UInt16); + dict[VarEnum.VT_UI4] = typeof(UInt32); + dict[VarEnum.VT_UI8] = typeof(UInt64); + dict[VarEnum.VT_INT] = typeof(IntPtr); + dict[VarEnum.VT_UINT] = typeof(UIntPtr); + dict[VarEnum.VT_BOOL] = typeof(bool); + dict[VarEnum.VT_R4] = typeof(Single); + dict[VarEnum.VT_R8] = typeof(Double); + dict[VarEnum.VT_DECIMAL] = typeof(Decimal); + dict[VarEnum.VT_DATE] = typeof(DateTime); + dict[VarEnum.VT_BSTR] = typeof(String); + + // *** END GENERATED CODE *** + + #endregion + + dict[VarEnum.VT_CY] = typeof(CurrencyWrapper); + dict[VarEnum.VT_ERROR] = typeof(ErrorWrapper); + + return dict; + } + + /// + /// Primitive types are the basic COM types. It includes valuetypes like ints, but also reference tyeps + /// like BStrs. It does not include composite types like arrays and user-defined COM types (IUnknown/IDispatch). + /// + private static bool IsPrimitiveType(VarEnum varEnum) { + switch (varEnum) { + #region Generated Variant IsPrimitiveType + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_IsPrimitiveType from: generate_comdispatch.py + + case VarEnum.VT_I1: + case VarEnum.VT_I2: + case VarEnum.VT_I4: + case VarEnum.VT_I8: + case VarEnum.VT_UI1: + case VarEnum.VT_UI2: + case VarEnum.VT_UI4: + case VarEnum.VT_UI8: + case VarEnum.VT_INT: + case VarEnum.VT_UINT: + case VarEnum.VT_BOOL: + case VarEnum.VT_ERROR: + case VarEnum.VT_R4: + case VarEnum.VT_R8: + case VarEnum.VT_DECIMAL: + case VarEnum.VT_CY: + case VarEnum.VT_DATE: + case VarEnum.VT_BSTR: + + // *** END GENERATED CODE *** + + #endregion + return true; + } + + return false; + } + } +} + +#endif \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/CompilerOptions.cs b/merlin/main/runtime/Microsoft.Scripting/CompilerOptions.cs new file mode 100644 index 0000000000..07718a5d60 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/CompilerOptions.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting { + + /// + /// Class that represents compiler options. + /// Note that this class is likely to change when hosting API becomes part of .Net + /// + [Serializable] + public class CompilerOptions +#if !SILVERLIGHT + : ICloneable +#endif + { + public CompilerOptions() { + } + + public virtual object Clone() { + return base.MemberwiseClone(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/DebugOptions.cs b/merlin/main/runtime/Microsoft.Scripting/DebugOptions.cs new file mode 100644 index 0000000000..a7e4b3bce3 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/DebugOptions.cs @@ -0,0 +1,89 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Security; +using System; + +namespace Microsoft.Scripting { + + /// + /// This class holds onto internal debugging options used in this assembly. + /// These options can be set via environment variables DLR_{option-name}. + /// Boolean options map "true" to true and other values to false. + /// + /// These options are for internal debugging only, and should not be + /// exposed through any public APIs. + /// + internal static class DebugOptions { + + private static bool ReadOption(string name) { +#if SILVERLIGHT + return false; +#else + string envVar = ReadString(name); + return envVar != null && envVar.ToLowerInvariant() == "true"; +#endif + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "name")] + private static bool ReadDebugOption(string name) { +#if DEBUG + return ReadOption(name); +#else + return false; +#endif + } + + private static string ReadString(string name) { +#if SILVERLIGHT + return null; +#else + try { + return Environment.GetEnvironmentVariable("DLR_" + name); + } catch (SecurityException) { + return null; + } +#endif + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "name")] + private static string ReadDebugString(string name) { +#if DEBUG + return ReadString(name); +#else + return null; +#endif + } + + // TODO: this is a Python specific option (for optimized modules) + // Can it be debug-only, or moved to Python? + private readonly static bool _lightweightScopes = ReadOption("LightweightScopes"); + + private readonly static bool _trackPerformance = ReadDebugOption("TrackPerformance"); + + /// + /// Generate optimized scopes that can be garbage collected + /// (globals are stored in an array instead of static fields on a + /// generated type) + /// + internal static bool LightweightScopes { + get { return _lightweightScopes; } + } + + internal static bool TrackPerformance { + get { return _trackPerformance; } + } + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/ErrorSink.cs b/merlin/main/runtime/Microsoft.Scripting/ErrorSink.cs new file mode 100644 index 0000000000..66ed7935f7 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ErrorSink.cs @@ -0,0 +1,103 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic; +using System.Threading; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting { + + public class ErrorSink { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly ErrorSink Default = new ErrorSink(); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly ErrorSink Null = new NullErrorSink(); + + protected ErrorSink() { + } + + //TODO: need localization + public virtual void Add(SourceUnit source, string message, SourceSpan span, int errorCode, Severity severity) { + throw new SyntaxErrorException(message, source, span, errorCode, severity); + } + + /// + /// This overload will be called when a SourceUnit is not available. This can happen if the code is being executed remotely, + /// since SourceUnit cannot be marshaled across AppDomains. + /// + public virtual void Add(string message, string path, string code, string line, SourceSpan span, int errorCode, Severity severity) { + throw new SyntaxErrorException(message, path, code, line, span, errorCode, severity); + } + } + + internal sealed class NullErrorSink : ErrorSink { + internal NullErrorSink() { + } + + public override void Add(SourceUnit source, string message, SourceSpan span, int errorCode, Severity severity) { + } + } + + public class ErrorCounter : ErrorSink { + private readonly ErrorSink _sink; + + private int _fatalErrorCount; + private int _errorCount; + private int _warningCount; + + public int FatalErrorCount { + get { return _fatalErrorCount; } + } + + public int ErrorCount { + get { return _errorCount; } + } + + public int WarningCount { + get { return _warningCount; } + } + + public bool AnyError { + get { + return _errorCount > 0 || _fatalErrorCount > 0; + } + } + + public ErrorCounter() + : this(ErrorSink.Null) { + } + + public ErrorCounter(ErrorSink sink) { + ContractUtils.RequiresNotNull(sink, "sink"); + _sink = sink; + } + + protected virtual void CountError(Severity severity) { + if (severity == Severity.FatalError) Interlocked.Increment(ref _fatalErrorCount); + else if (severity == Severity.Error) Interlocked.Increment(ref _errorCount); + else if (severity == Severity.Warning) Interlocked.Increment(ref _warningCount); + } + + public void ClearCounters() { + _warningCount = _errorCount = _fatalErrorCount = 0; + } + + public override void Add(SourceUnit source, string message, SourceSpan span, int errorCode, Severity severity) { + CountError(severity); + _sink.Add(source, message, span, errorCode, severity); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/AssemblyGen.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/AssemblyGen.cs new file mode 100644 index 0000000000..ba1ec299a6 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/AssemblyGen.cs @@ -0,0 +1,370 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using System.Resources; +using System.Security; +using System.Text; +using System.Threading; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Generation { + public sealed class AssemblyGen { + private readonly AssemblyBuilder _myAssembly; + private readonly ModuleBuilder _myModule; + private readonly PortableExecutableKinds _peKind; + private readonly ImageFileMachine _machine; + private readonly bool _isDebuggable; + +#if !SILVERLIGHT + private readonly string _outFileName; // can be null iff !SaveAndReloadAssemblies + private readonly string _outDir; // null means the current directory + private const string peverify_exe = "peverify.exe"; +#endif + + private int _index; + + internal bool IsDebuggable { + get { +#if !SILVERLIGHT + Debug.Assert(_isDebuggable == (_myModule.GetSymWriter() != null)); +#endif + return _isDebuggable; + } + } + + public AssemblyGen(AssemblyName name, string outDir, string outFileExtension, bool isDebuggable) + : this(name, outDir, outFileExtension, isDebuggable, PortableExecutableKinds.ILOnly, ImageFileMachine.I386) { + } + + internal AssemblyGen(AssemblyName name, string outDir, string outFileExtension, bool isDebuggable, + PortableExecutableKinds peKind, ImageFileMachine machine) { + + ContractUtils.RequiresNotNull(name, "name"); + +#if SILVERLIGHT // AssemblyBuilderAccess.RunAndSave, Environment.CurrentDirectory + _myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); + _myModule = _myAssembly.DefineDynamicModule(name.Name, isDebuggable); +#else + if (outFileExtension == null) { + outFileExtension = ".dll"; + } + + if (outDir != null) { + try { + outDir = Path.GetFullPath(outDir); + } catch (Exception) { + throw Error.InvalidOutputDir(); + } + try { + Path.Combine(outDir, name.Name + outFileExtension); + } catch (ArgumentException) { + throw Error.InvalidAsmNameOrExtension(); + } + + _outFileName = name.Name + outFileExtension; + _outDir = outDir; + } + + // mark the assembly transparent so that it works in partial trust: + CustomAttributeBuilder[] attributes = new CustomAttributeBuilder[] { + new CustomAttributeBuilder(typeof(SecurityTransparentAttribute).GetConstructor(Type.EmptyTypes), new object[0]) + }; + + if (outDir != null) { + _myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave, outDir, + null, null, null, null, false, attributes); + + _myModule = _myAssembly.DefineDynamicModule(name.Name, _outFileName, isDebuggable); + } else { + _myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run, attributes); + _myModule = _myAssembly.DefineDynamicModule(name.Name, isDebuggable); + } + + _myAssembly.DefineVersionInfoResource(); +#endif + _machine = machine; + _peKind = peKind; + _isDebuggable = isDebuggable; + + if (isDebuggable) { + SetDebuggableAttributes(); + } + } + + internal void SetDebuggableAttributes() { + DebuggableAttribute.DebuggingModes attrs = + DebuggableAttribute.DebuggingModes.Default | + DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | + DebuggableAttribute.DebuggingModes.DisableOptimizations; + + Type[] argTypes = new Type[] { typeof(DebuggableAttribute.DebuggingModes) }; + Object[] argValues = new Object[] { attrs }; + + _myAssembly.SetCustomAttribute(new CustomAttributeBuilder( + typeof(DebuggableAttribute).GetConstructor(argTypes), argValues) + ); + + _myModule.SetCustomAttribute(new CustomAttributeBuilder( + typeof(DebuggableAttribute).GetConstructor(argTypes), argValues) + ); + } + +#if !SILVERLIGHT // IResourceWriter + internal void AddResourceFile(string name, string file, ResourceAttributes attribute) { + IResourceWriter rw = _myModule.DefineResource(Path.GetFileName(file), name, attribute); + + string ext = Path.GetExtension(file); + if (String.Equals(ext, ".resources", StringComparison.OrdinalIgnoreCase)) { + ResourceReader rr = new ResourceReader(file); + using (rr) { + System.Collections.IDictionaryEnumerator de = rr.GetEnumerator(); + + while (de.MoveNext()) { + string key = de.Key as string; + rw.AddResource(key, de.Value); + } + } + } else { + rw.AddResource(name, File.ReadAllBytes(file)); + } + } +#endif + + #region Dump and Verify + + public string SaveAssembly() { +#if !SILVERLIGHT // AssemblyBuilder.Save + _myAssembly.Save(_outFileName, _peKind, _machine); + return Path.Combine(_outDir, _outFileName); +#else + return null; +#endif + } + + internal void Verify() { +#if !SILVERLIGHT + PeVerifyThis(); +#endif + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + internal static void PeVerifyAssemblyFile(string fileLocation) { +#if !SILVERLIGHT + string outDir = Path.GetDirectoryName(fileLocation); + string outFileName = Path.GetFileName(fileLocation); + string peverifyPath = FindPeverify(); + if (peverifyPath == null) { + return; + } + + int exitCode = 0; + string strOut = null; + string verifyFile = null; + + try { + string pythonPath = new FileInfo(Assembly.GetEntryAssembly().Location).DirectoryName; + + string assemblyFile = Path.Combine(outDir, outFileName).ToLower(CultureInfo.InvariantCulture); + string assemblyName = Path.GetFileNameWithoutExtension(outFileName); + string assemblyExtension = Path.GetExtension(outFileName); + Random rnd = new System.Random(); + + for (int i = 0; ; i++) { + string verifyName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}_{2}{3}", assemblyName, i, rnd.Next(1, 100), assemblyExtension); + verifyName = Path.Combine(Path.GetTempPath(), verifyName); + + try { + File.Copy(assemblyFile, verifyName); + verifyFile = verifyName; + break; + } catch (IOException) { + } + } + + // copy any DLLs or EXEs created by the process during the run... + CopyFilesCreatedSinceStart(Path.GetTempPath(), Environment.CurrentDirectory, outFileName); + CopyDirectory(Path.GetTempPath(), pythonPath); + if (Snippets.Shared.SnippetsDirectory != null && Snippets.Shared.SnippetsDirectory != Path.GetTempPath()) { + CopyFilesCreatedSinceStart(Path.GetTempPath(), Snippets.Shared.SnippetsDirectory, outFileName); + } + + // /IGNORE=80070002 ignores errors related to files we can't find, this happens when we generate assemblies + // and then peverify the result. Note if we can't resolve a token thats in an external file we still + // generate an error. + ProcessStartInfo psi = new ProcessStartInfo(peverifyPath, "/IGNORE=80070002 \"" + verifyFile + "\""); + psi.UseShellExecute = false; + psi.RedirectStandardOutput = true; + Process proc = Process.Start(psi); + Thread thread = new Thread( + new ThreadStart( + delegate { + using (StreamReader sr = proc.StandardOutput) { + strOut = sr.ReadToEnd(); + } + } + )); + + thread.Start(); + proc.WaitForExit(); + thread.Join(); + exitCode = proc.ExitCode; + proc.Close(); + } catch (Exception e) { + strOut = "Unexpected exception: " + e.ToString(); + exitCode = 1; + } + + if (exitCode != 0) { + Console.WriteLine("Verification failed w/ exit code {0}: {1}", exitCode, strOut); + throw Error.VerificationException( + outFileName, + verifyFile, + strOut ?? ""); + } + + if (verifyFile != null) { + File.Delete(verifyFile); + } +#endif + } + +#if !SILVERLIGHT + internal static string FindPeverify() { + string path = System.Environment.GetEnvironmentVariable("PATH"); + string[] dirs = path.Split(';'); + foreach (string dir in dirs) { + string file = Path.Combine(dir, peverify_exe); + if (File.Exists(file)) { + return file; + } + } + return null; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private void PeVerifyThis() { + string fileLocation = Path.Combine(_outDir, _outFileName); + PeVerifyAssemblyFile(fileLocation); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private static void CopyFilesCreatedSinceStart(string pythonPath, string dir, string outFileName) { + DateTime start = Process.GetCurrentProcess().StartTime; + foreach (string filename in Directory.GetFiles(dir)) { + FileInfo fi = new FileInfo(filename); + if (fi.Name != outFileName) { + if (fi.LastWriteTime - start >= TimeSpan.Zero) { + try { + File.Copy(filename, Path.Combine(pythonPath, fi.Name), true); + } catch (Exception e) { + Console.WriteLine("Error copying {0}: {1}", filename, e.Message); + } + } + } + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private static void CopyDirectory(string to, string from) { + foreach (string filename in Directory.GetFiles(from)) { + FileInfo fi = new FileInfo(filename); + string toFile = Path.Combine(to, fi.Name); + FileInfo toInfo = new FileInfo(toFile); + + if (fi.Extension.ToLowerInvariant() == ".dll" || fi.Extension.ToLowerInvariant() == ".exe") { + if (!File.Exists(toFile) || toInfo.CreationTime != fi.CreationTime) { + try { + File.Copy(filename, toFile, true); + } catch (Exception e) { + Console.WriteLine("Error copying {0}: {1}", filename, e.Message); + } + } + } + } + } +#endif + #endregion + + public TypeBuilder DefinePublicType(string name, Type parent, bool preserveName) { + return DefineType(name, parent, TypeAttributes.Public, preserveName); + } + + internal TypeBuilder DefineType(string name, Type parent, TypeAttributes attr, bool preserveName) { + ContractUtils.RequiresNotNull(name, "name"); + ContractUtils.RequiresNotNull(parent, "parent"); + + StringBuilder sb = new StringBuilder(name); + if (!preserveName) { + int index = Interlocked.Increment(ref _index); + sb.Append("$"); + sb.Append(index); + } + + // There is a bug in Reflection.Emit that leads to + // Unhandled Exception: System.Runtime.InteropServices.COMException (0x80131130): Record not found on lookup. + // if there is any of the characters []*&+,\ in the type name and a method defined on the type is called. + sb.Replace('+', '_').Replace('[', '_').Replace(']', '_').Replace('*', '_').Replace('&', '_').Replace(',', '_').Replace('\\', '_'); + + name = sb.ToString(); + + return _myModule.DefineType(name, attr, parent); + } + +#if !SILVERLIGHT + internal void SetEntryPoint(MethodInfo mi, PEFileKinds kind) { + _myAssembly.SetEntryPoint(mi, kind); + } +#endif + + internal AssemblyBuilder AssemblyBuilder { + get { return _myAssembly; } + } + + internal ModuleBuilder ModuleBuilder { + get { return _myModule; } + } + + private const MethodAttributes CtorAttributes = MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public; + private const MethodImplAttributes ImplAttributes = MethodImplAttributes.Runtime | MethodImplAttributes.Managed; + private const MethodAttributes InvokeAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; + private const TypeAttributes DelegateAttributes = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass; + private static readonly Type[] _DelegateCtorSignature = new Type[] { typeof(object), typeof(IntPtr) }; + + internal Type MakeDelegateType(string name, Type[] parameters, Type returnType) { + TypeBuilder builder = DefineType(name, typeof(MulticastDelegate), DelegateAttributes, false); + builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _DelegateCtorSignature).SetImplementationFlags(ImplAttributes); + builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes); + return builder.CreateType(); + } + } + + internal static class SymbolGuids { + internal static readonly Guid LanguageType_ILAssembly = + new Guid(-1358664493, -12063, 0x11d2, 0x97, 0x7c, 0, 160, 0xc9, 180, 0xd5, 12); + + internal static readonly Guid DocumentType_Text = + new Guid(0x5a869d0b, 0x6611, 0x11d3, 0xbd, 0x2a, 0, 0, 0xf8, 8, 0x49, 0xbd); + + internal static readonly Guid LanguageVendor_Microsoft = + new Guid(-1723120188, -6423, 0x11d2, 0x90, 0x3f, 0, 0xc0, 0x4f, 0xa3, 2, 0xa1); + } +} + diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/CompilerHelpers.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/CompilerHelpers.cs new file mode 100644 index 0000000000..d01c13a4b1 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/CompilerHelpers.cs @@ -0,0 +1,650 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Dynamic; +using Microsoft.Contracts; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using RuntimeHelpers = Microsoft.Scripting.Runtime.ScriptingRuntimeHelpers; + +namespace Microsoft.Scripting.Generation { + using Ast = System.Linq.Expressions.Expression; + + public static class CompilerHelpers { + public static readonly MethodAttributes PublicStatic = MethodAttributes.Public | MethodAttributes.Static; + private static readonly MethodInfo _CreateInstanceMethod = typeof(RuntimeHelpers).GetMethod("CreateInstance"); + + public static string[] GetArgumentNames(ParameterInfo[] parameterInfos) { + string[] ret = new string[parameterInfos.Length]; + for (int i = 0; i < parameterInfos.Length; i++) ret[i] = parameterInfos[i].Name; + return ret; + } + + public static Type[] GetTypesWithThis(MethodBase mi) { + Type[] types = ReflectionUtils.GetParameterTypes(mi.GetParameters()); + if (IsStatic(mi)) { + return types; + } + + // TODO (Spec#): do not specify type arg + return ArrayUtils.Insert(mi.DeclaringType, types); + } + + + public static Type GetReturnType(MethodBase mi) { + if (mi.IsConstructor) return mi.DeclaringType; + else return ((MethodInfo)mi).ReturnType; + } + + public static int GetStaticNumberOfArgs(MethodBase method) { + if (IsStatic(method)) return method.GetParameters().Length; + + return method.GetParameters().Length + 1; + } + + public static bool IsParamArray(ParameterInfo parameter) { + return parameter.IsDefined(typeof(ParamArrayAttribute), false); + } + + public static bool IsOutParameter(ParameterInfo pi) { + // not using IsIn/IsOut properties as they are not available in Silverlight: + return (pi.Attributes & (ParameterAttributes.Out | ParameterAttributes.In)) == ParameterAttributes.Out; + } + + public static int GetOutAndByRefParameterCount(MethodBase method) { + int res = 0; + ParameterInfo[] pis = method.GetParameters(); + for (int i = 0; i < pis.Length; i++) { + if (IsByRefParameter(pis[i])) res++; + } + return res; + } + + /// + /// Returns true if the specified parameter is mandatory, i.e. is not optional and doesn't have a default value. + /// + public static bool IsMandatoryParameter(ParameterInfo pi) { + return (pi.Attributes & (ParameterAttributes.Optional | ParameterAttributes.HasDefault)) == 0; + } + + public static bool HasDefaultValue(ParameterInfo pi) { + return (pi.Attributes & ParameterAttributes.HasDefault) != 0; + } + + public static bool IsByRefParameter(ParameterInfo pi) { + // not using IsIn/IsOut properties as they are not available in Silverlight: + if (pi.ParameterType.IsByRef) return true; + + return (pi.Attributes & (ParameterAttributes.Out)) == ParameterAttributes.Out; + } + + public static bool ProhibitsNull(ParameterInfo parameter) { + return parameter.IsDefined(typeof(NotNullAttribute), false); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public static object GetMissingValue(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + if (type.IsByRef) type = type.GetElementType(); + if (type.IsEnum) return Activator.CreateInstance(type); + + switch (Type.GetTypeCode(type)) { + default: + case TypeCode.Object: + // struct + if (type.IsSealed && type.IsValueType) { + return Activator.CreateInstance(type); + } else if (type == typeof(object)) { + // parameter of type object receives the actual Missing value + return Missing.Value; + } else if (!type.IsValueType) { + return null; + } else { + throw Error.CantCreateDefaultTypeFor(type); + } + case TypeCode.Empty: + case TypeCode.DBNull: + case TypeCode.String: + return null; + + case TypeCode.Boolean: return false; + case TypeCode.Char: return '\0'; + case TypeCode.SByte: return (sbyte)0; + case TypeCode.Byte: return (byte)0; + case TypeCode.Int16: return (short)0; + case TypeCode.UInt16: return (ushort)0; + case TypeCode.Int32: return (int)0; + case TypeCode.UInt32: return (uint)0; + case TypeCode.Int64: return 0L; + case TypeCode.UInt64: return 0UL; + case TypeCode.Single: return 0.0f; + case TypeCode.Double: return 0.0D; + case TypeCode.Decimal: return (decimal)0; + case TypeCode.DateTime: return DateTime.MinValue; + } + } + + public static bool IsStatic(MethodBase mi) { + return mi.IsConstructor || mi.IsStatic; + } + + /// + /// True if the MethodBase is method which is going to construct an object + /// + public static bool IsConstructor(MethodBase mb) { + if (mb.IsConstructor) { + return true; + } + + if (mb.IsGenericMethod) { + MethodInfo mi = mb as MethodInfo; + + if (mi.GetGenericMethodDefinition() == _CreateInstanceMethod) { + return true; + } + } + + return false; + } + + public static T[] MakeRepeatedArray(T item, int count) { + T[] ret = new T[count]; + for (int i = 0; i < count; i++) ret[i] = item; + return ret; + } + + /// + /// A helper routine to check if a type can be treated as sealed - i.e. there + /// can never be a subtype of this given type. This corresponds to a type + /// that is either declared "Sealed" or is a ValueType and thus unable to be + /// extended. + /// + public static bool IsSealed(Type type) { + return type.IsSealed || type.IsValueType; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public static Operators OperatorToReverseOperator(Operators op) { + switch (op) { + case Operators.LessThan: return Operators.GreaterThan; + case Operators.LessThanOrEqual: return Operators.GreaterThanOrEqual; + case Operators.GreaterThan: return Operators.LessThan; + case Operators.GreaterThanOrEqual: return Operators.LessThanOrEqual; + case Operators.Equals: return Operators.Equals; + case Operators.NotEquals: return Operators.NotEquals; + case Operators.DivMod: return Operators.ReverseDivMod; + case Operators.ReverseDivMod: return Operators.DivMod; + #region Generated Operator Reversal + + // *** BEGIN GENERATED CODE *** + // generated by function: operator_reversal from: generate_ops.py + + case Operators.Add: return Operators.ReverseAdd; + case Operators.ReverseAdd: return Operators.Add; + case Operators.Subtract: return Operators.ReverseSubtract; + case Operators.ReverseSubtract: return Operators.Subtract; + case Operators.Power: return Operators.ReversePower; + case Operators.ReversePower: return Operators.Power; + case Operators.Multiply: return Operators.ReverseMultiply; + case Operators.ReverseMultiply: return Operators.Multiply; + case Operators.FloorDivide: return Operators.ReverseFloorDivide; + case Operators.ReverseFloorDivide: return Operators.FloorDivide; + case Operators.Divide: return Operators.ReverseDivide; + case Operators.ReverseDivide: return Operators.Divide; + case Operators.TrueDivide: return Operators.ReverseTrueDivide; + case Operators.ReverseTrueDivide: return Operators.TrueDivide; + case Operators.Mod: return Operators.ReverseMod; + case Operators.ReverseMod: return Operators.Mod; + case Operators.LeftShift: return Operators.ReverseLeftShift; + case Operators.ReverseLeftShift: return Operators.LeftShift; + case Operators.RightShift: return Operators.ReverseRightShift; + case Operators.ReverseRightShift: return Operators.RightShift; + case Operators.BitwiseAnd: return Operators.ReverseBitwiseAnd; + case Operators.ReverseBitwiseAnd: return Operators.BitwiseAnd; + case Operators.BitwiseOr: return Operators.ReverseBitwiseOr; + case Operators.ReverseBitwiseOr: return Operators.BitwiseOr; + case Operators.ExclusiveOr: return Operators.ReverseExclusiveOr; + case Operators.ReverseExclusiveOr: return Operators.ExclusiveOr; + + // *** END GENERATED CODE *** + + #endregion + } + return Operators.None; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public static string OperatorToReverseOperator(string op) { + switch (op) { + case StandardOperators.LessThan: return StandardOperators.GreaterThan; + case StandardOperators.LessThanOrEqual: return StandardOperators.GreaterThanOrEqual; + case StandardOperators.GreaterThan: return StandardOperators.LessThan; + case StandardOperators.GreaterThanOrEqual: return StandardOperators.LessThanOrEqual; + case StandardOperators.Equal: return StandardOperators.Equal; + case StandardOperators.NotEqual: return StandardOperators.NotEqual; + } + return StandardOperators.None; + } + + public static string InPlaceOperatorToOperator(string op) { + if (op.StartsWith("InPlace")) { + return op.Substring(7); + } + + return StandardOperators.None; + } + + public static Operators InPlaceOperatorToOperator(Operators op) { + switch (op) { + case Operators.InPlaceAdd: return Operators.Add; + case Operators.InPlaceBitwiseAnd: return Operators.BitwiseAnd; + case Operators.InPlaceBitwiseOr: return Operators.BitwiseOr; + case Operators.InPlaceDivide: return Operators.Divide; + case Operators.InPlaceFloorDivide: return Operators.FloorDivide; + case Operators.InPlaceLeftShift: return Operators.LeftShift; + case Operators.InPlaceMod: return Operators.Mod; + case Operators.InPlaceMultiply: return Operators.Multiply; + case Operators.InPlacePower: return Operators.Power; + case Operators.InPlaceRightShift: return Operators.RightShift; + case Operators.InPlaceSubtract: return Operators.Subtract; + case Operators.InPlaceTrueDivide: return Operators.TrueDivide; + case Operators.InPlaceExclusiveOr: return Operators.ExclusiveOr; + case Operators.InPlaceRightShiftUnsigned: return Operators.RightShiftUnsigned; + default: return Operators.None; + } + + } + public static bool IsComparisonOperator(Operators op) { + switch (op) { + case Operators.LessThan: return true; + case Operators.LessThanOrEqual: return true; + case Operators.GreaterThan: return true; + case Operators.GreaterThanOrEqual: return true; + case Operators.Equals: return true; + case Operators.NotEquals: return true; + case Operators.Compare: return true; + } + return false; + } + + public static bool IsComparisonOperator(string op) { + switch (op) { + case StandardOperators.LessThan: return true; + case StandardOperators.LessThanOrEqual: return true; + case StandardOperators.GreaterThan: return true; + case StandardOperators.GreaterThanOrEqual: return true; + case StandardOperators.Equal: return true; + case StandardOperators.NotEqual: return true; + case StandardOperators.Compare: return true; + } + return false; + } + + /// + /// Returns the System.Type for any object, including null. The type of null + /// is represented by None.Type and all other objects just return the + /// result of Object.GetType + /// + public static Type GetType(object obj) { + return obj == null ? Null.Type : obj.GetType(); + } + + /// + /// Simply returns a Type[] from calling GetType on each element of args. + /// + public static Type[] GetTypes(object[] args) { + Type[] types = new Type[args.Length]; + for (int i = 0; i < args.Length; i++) { + types[i] = GetType(args[i]); + } + return types; + } + + internal static Type[] GetTypes(IList args) { + Type[] types = new Type[args.Count]; + for (int i = 0, n = types.Length; i < n; i++) { + types[i] = args[i].Type; + } + return types; + } + + public static bool CanOptimizeMethod(MethodBase method) { + if (method.ContainsGenericParameters || + method.IsFamily || + method.IsPrivate || + method.IsFamilyOrAssembly || + !method.DeclaringType.IsVisible) { + return false; + } + return true; + } + + /// + /// Given a MethodInfo which may be declared on a non-public type this attempts to + /// return a MethodInfo which will dispatch to the original MethodInfo but is declared + /// on a public type. + /// + /// Returns null if a public method cannot be obtained. + /// + public static MethodInfo TryGetCallableMethod(MethodInfo method) { + if (method.DeclaringType.IsVisible) return method; + // first try and get it from the base type we're overriding... + method = method.GetBaseDefinition(); + + if (method.DeclaringType.IsVisible) return method; + if (method.DeclaringType.IsInterface) return method; + // maybe we can get it from an interface... + Type[] interfaces = method.DeclaringType.GetInterfaces(); + foreach (Type iface in interfaces) { + InterfaceMapping mapping = method.DeclaringType.GetInterfaceMap(iface); + for (int i = 0; i < mapping.TargetMethods.Length; i++) { + if (mapping.TargetMethods[i] == method) { + return mapping.InterfaceMethods[i]; + } + } + } + + return method; + } + + /// + /// Non-public types can have public members that we find when calling type.GetMember(...). This + /// filters out the non-visible members by attempting to resolve them to the correct visible type. + /// + /// If no correct visible type can be found then the member is not visible and we won't call it. + /// + public static MemberInfo[] FilterNonVisibleMembers(Type type, MemberInfo[] foundMembers) { + if (!type.IsVisible && foundMembers.Length > 0) { + // need to remove any members that we can't get through other means + List foundVisible = null; + MemberInfo visible; + MethodInfo mi; + for (int i = 0; i < foundMembers.Length; i++) { + visible = null; + switch (foundMembers[i].MemberType) { + case MemberTypes.Method: + visible = TryGetCallableMethod((MethodInfo)foundMembers[i]); + break; + case MemberTypes.Property: + PropertyInfo pi = (PropertyInfo)foundMembers[i]; + mi = pi.GetGetMethod() ?? pi.GetSetMethod(); + visible = TryGetCallableMethod(mi); + if (visible != null) { + visible = visible.DeclaringType.GetProperty(pi.Name); + } + break; + case MemberTypes.Event: + EventInfo ei = (EventInfo)foundMembers[i]; + mi = ei.GetAddMethod() ?? ei.GetRemoveMethod() ?? ei.GetRaiseMethod(); + visible = TryGetCallableMethod(mi); + if (visible != null) { + visible = visible.DeclaringType.GetEvent(ei.Name); + } + break; + // all others can't be exposed out this way + } + if (visible != null) { + if (foundVisible == null) { + foundVisible = new List(); + } + foundVisible.Add(visible); + } + } + + if (foundVisible != null) { + foundMembers = foundVisible.ToArray(); + } else { + foundMembers = new MemberInfo[0]; + } + } + return foundMembers; + } + + /// + /// Given a MethodInfo which may be declared on a non-public type this attempts to + /// return a MethodInfo which will dispatch to the original MethodInfo but is declared + /// on a public type. + /// + /// Throws InvalidOperationException if the method cannot be obtained. + /// + public static MethodInfo GetCallableMethod(MethodInfo method, bool privateBinding) { + MethodInfo mi = TryGetCallableMethod(method); + if (mi == null) { + if (!privateBinding) { + throw Error.NoCallableMethods(method.DeclaringType, method.Name); + } + } + return mi; + } + + public static bool CanOptimizeField(FieldInfo fi) { + return fi.IsPublic && fi.DeclaringType.IsVisible; + } + + public static Type GetVisibleType(object value) { + return GetVisibleType(GetType(value)); + } + + public static Type GetVisibleType(Type t) { + while (!t.IsVisible) { + t = t.BaseType; + } + return t; + } + + public static MethodBase[] GetConstructors(Type t, bool privateBinding) { + if (t.IsArray) { + // The JIT verifier doesn't like new int[](3) even though it appears as a ctor. + // We could do better and return newarr in the future. + return new MethodBase[] { GetArrayCtor(t) }; + } + + BindingFlags bf = BindingFlags.Instance | BindingFlags.Public; + if (privateBinding) { + bf |= BindingFlags.NonPublic; + } + + ConstructorInfo[] ci = t.GetConstructors(bf); + + if (t.IsValueType) { + // structs don't define a parameterless ctor, add a generic method for that. + return ArrayUtils.Insert(GetStructDefaultCtor(t), ci); + } + + return ci; + } + + private static MethodBase GetStructDefaultCtor(Type t) { + return typeof(RuntimeHelpers).GetMethod("CreateInstance").MakeGenericMethod(t); + } + + private static MethodBase GetArrayCtor(Type t) { + return typeof(RuntimeHelpers).GetMethod("CreateArray").MakeGenericMethod(t.GetElementType()); + } + + public static bool HasImplicitConversion(Type fromType, Type toType) { + if (CompilerHelpers.HasImplicitConversion(fromType, toType, toType.GetMember("op_Implicit"))) { + return true; + } + + Type curType = fromType; + do { + if (CompilerHelpers.HasImplicitConversion(fromType, toType, curType.GetMember("op_Implicit"))) { + return true; + } + curType = curType.BaseType; + } while (curType != null); + + return false; + } + + public static bool TryImplicitConversion(Object value, Type to, out object result) { + if (CompilerHelpers.TryImplicitConvert(value, to, to.GetMember("op_Implicit"), out result)) { + return true; + } + + Type curType = CompilerHelpers.GetType(value); + do { + if (CompilerHelpers.TryImplicitConvert(value, to, curType.GetMember("op_Implicit"), out result)) { + return true; + } + curType = curType.BaseType; + } while (curType != null); + + return false; + } + + private static bool TryImplicitConvert(Object value, Type to, MemberInfo[] implicitConv, out object result) { + foreach (MethodInfo mi in implicitConv) { + if (to.IsValueType == mi.ReturnType.IsValueType && to.IsAssignableFrom(mi.ReturnType)) { + if (mi.IsStatic) { + result = mi.Invoke(null, new object[] { value }); + } else { + result = mi.Invoke(value, ArrayUtils.EmptyObjects); + } + return true; + } + } + + result = null; + return false; + } + + private static bool HasImplicitConversion(Type fromType, Type to, MemberInfo[] implicitConv) { + foreach (MethodInfo mi in implicitConv) { + if (mi.ReturnType == to && mi.GetParameters()[0].ParameterType.IsAssignableFrom(fromType)) { + return true; + } + } + + return false; + } + + public static bool IsStrongBox(object target) { + Type t = CompilerHelpers.GetType(target); + + return IsStrongBox(t); + } + + public static bool IsStrongBox(Type t) { + return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(StrongBox<>); + } + + /// + /// Returns a value which indicates failure when a OldConvertToAction of ImplicitTry or + /// ExplicitTry. + /// + public static Expression GetTryConvertReturnValue(Type type) { + Expression res; + if (type.IsInterface || type.IsClass || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))) { + res = Ast.Constant(null, type); + } else { + res = Ast.Constant(Activator.CreateInstance(type)); + } + + return res; + } + + /// + /// Returns a value which indicates failure when a ConvertToAction of ImplicitTry or + /// ExplicitTry. + /// + public static Expression GetTryConvertReturnValue(CodeContext context, RuleBuilder rule) { + rule.IsError = true; + return rule.MakeReturn(context.LanguageContext.Binder, GetTryConvertReturnValue(rule.ReturnType)); + } + + public static MethodBase[] GetMethodTargets(object obj) { + Type t = CompilerHelpers.GetType(obj); + + if (typeof(Delegate).IsAssignableFrom(t)) { + MethodInfo mi = t.GetMethod("Invoke"); + return new MethodBase[] { mi }; + } else if (typeof(BoundMemberTracker).IsAssignableFrom(t)) { + BoundMemberTracker bmt = obj as BoundMemberTracker; + if (bmt.BoundTo.MemberType == TrackerTypes.Method) { + } + } else if (typeof(MethodGroup).IsAssignableFrom(t)) { + } else if (typeof(MemberGroup).IsAssignableFrom(t)) { + } else { + return MakeCallSignatureForCallableObject(t); + } + + return null; + } + + private static MethodBase[] MakeCallSignatureForCallableObject(Type t) { + List res = new List(); + MemberInfo[] members = t.GetMember("Call"); + foreach (MemberInfo mi in members) { + if (mi.MemberType == MemberTypes.Method) { + MethodInfo method = mi as MethodInfo; + if (method.IsSpecialName) { + res.Add(method); + } + } + } + return res.ToArray(); + } + + public static Type[] GetSiteTypes(IList arguments, Type returnType) { + int count = arguments.Count; + + Type[] ret = new Type[count + 1]; + + for (int i = 0; i < count; i++) { + ret[i] = arguments[i].Type; + } + + ret[count] = returnType; + + NonNullType.AssertInitialized(ret); + return ret; + } + + public static Type[] GetExpressionTypes(Expression[] expressions) { + ContractUtils.RequiresNotNull(expressions, "expressions"); + + Type[] res = new Type[expressions.Length]; + for (int i = 0; i < res.Length; i++) { + ContractUtils.RequiresNotNull(expressions[i], "expressions[i]"); + + res[i] = expressions[i].Type; + } + + return res; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] + public static Type GetReturnType(LambdaExpression lambda) { + return lambda.Type.GetMethod("Invoke").ReturnType; + } + + public static Type MakeCallSiteType(params Type[] types) { + return typeof(CallSite<>).MakeGenericType(DelegateHelpers.MakeDelegate(types)); + } + + public static Type MakeCallSiteDelegateType(Type[] types) { + return DelegateHelpers.MakeDelegate(types); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/ConstantCheck.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/ConstantCheck.cs new file mode 100644 index 0000000000..be97d52159 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/ConstantCheck.cs @@ -0,0 +1,123 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Generation { + + public static class ConstantCheck { + + /// + /// Tests to see if the expression is a constant with the given value. + /// + /// The expression to examine + /// The constant value to check for. + /// true/false + public static bool Check(Expression expression, object value) { + ContractUtils.RequiresNotNull(expression, "expression"); + return IsConstant(expression, value); + } + + + /// + /// Tests to see if the expression is a constant with the given value. + /// + /// The expression to examine + /// The constant value to check for. + /// true/false + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + internal static bool IsConstant(Expression e, object value) { + switch (e.NodeType) { + case ExpressionType.AndAlso: + return CheckAndAlso((BinaryExpression)e, value); + + case ExpressionType.OrElse: + return CheckOrElse((BinaryExpression)e, value); + + case ExpressionType.Constant: + return CheckConstant((ConstantExpression)e, value); + + case ExpressionType.TypeIs: + return Check((TypeBinaryExpression)e, value); + + default: + return false; + } + } + + //CONFORMING + internal static bool IsNull(Expression e) { + return IsConstant(e, null); + } + + + private static bool CheckAndAlso(BinaryExpression node, object value) { + Debug.Assert(node.NodeType == ExpressionType.AndAlso); + + if (node.Method != null) { + return false; + } + //TODO: we can propagate through conversion, but it may not worth it. + if (node.Conversion != null) { + return false; + } + + if (value is bool) { + if ((bool)value) { + return IsConstant(node.Left, true) && IsConstant(node.Right, true); + } else { + // if left isn't a constant it has to be evaluated + return IsConstant(node.Left, false); + } + } + return false; + } + + private static bool CheckOrElse(BinaryExpression node, object value) { + Debug.Assert(node.NodeType == ExpressionType.OrElse); + + if (node.Method != null) { + return false; + } + + if (value is bool) { + if ((bool)value) { + return IsConstant(node.Left, true); + } else { + return IsConstant(node.Left, false) && IsConstant(node.Right, false); + } + } + return false; + } + + private static bool CheckConstant(ConstantExpression node, object value) { + if (value == null) { + return node.Value == null; + } else { + return value.Equals(node.Value); + } + } + + private static bool Check(TypeBinaryExpression node, object value) { + // allow constant TypeIs expressions to be optimized away + if (value is bool && ((bool)value) == true) { + return node.TypeOperand.IsAssignableFrom(node.Expression.Type); + } + return false; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/DelegateHelpers.Generated.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/DelegateHelpers.Generated.cs new file mode 100644 index 0000000000..5b0af2e86c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/DelegateHelpers.Generated.cs @@ -0,0 +1,109 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Generation { + internal static partial class DelegateHelpers { + + #region Generated Maximum Delegate Arity + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_max_delegate_arity from: generate_dynsites.py + + private const int MaximumArity = 17; + + // *** END GENERATED CODE *** + + #endregion + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + internal static Type MakeDelegate(Type[] types) { + Debug.Assert(types != null && types.Length > 0); + + // Can only used predefined delegates if we have no byref types and + // the arity is small enough to fit in Func<...> or Action<...> + if (types.Length > MaximumArity || types.Any(t => t.IsByRef)) { + return MakeCustomDelegate(types); + } + + Type returnType = types[types.Length - 1]; + if (returnType == typeof(void)) { + types = types.RemoveLast(); + switch (types.Length) { + case 0: return typeof(Action); + #region Generated Delegate Action Types + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_delegate_action from: generate_dynsites.py + + case 1: return typeof(Action<>).MakeGenericType(types); + case 2: return typeof(Action<,>).MakeGenericType(types); + case 3: return typeof(Action<,,>).MakeGenericType(types); + case 4: return typeof(Action<,,,>).MakeGenericType(types); + case 5: return typeof(Action<,,,,>).MakeGenericType(types); + case 6: return typeof(Action<,,,,,>).MakeGenericType(types); + case 7: return typeof(Action<,,,,,,>).MakeGenericType(types); + case 8: return typeof(Action<,,,,,,,>).MakeGenericType(types); + case 9: return typeof(Action<,,,,,,,,>).MakeGenericType(types); + case 10: return typeof(Action<,,,,,,,,,>).MakeGenericType(types); + case 11: return typeof(Action<,,,,,,,,,,>).MakeGenericType(types); + case 12: return typeof(Action<,,,,,,,,,,,>).MakeGenericType(types); + case 13: return typeof(Action<,,,,,,,,,,,,>).MakeGenericType(types); + case 14: return typeof(Action<,,,,,,,,,,,,,>).MakeGenericType(types); + case 15: return typeof(Action<,,,,,,,,,,,,,,>).MakeGenericType(types); + case 16: return typeof(Action<,,,,,,,,,,,,,,,>).MakeGenericType(types); + + // *** END GENERATED CODE *** + + #endregion + } + } else { + switch (types.Length) { + #region Generated Delegate Func Types + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_delegate_func from: generate_dynsites.py + + case 1: return typeof(Func<>).MakeGenericType(types); + case 2: return typeof(Func<,>).MakeGenericType(types); + case 3: return typeof(Func<,,>).MakeGenericType(types); + case 4: return typeof(Func<,,,>).MakeGenericType(types); + case 5: return typeof(Func<,,,,>).MakeGenericType(types); + case 6: return typeof(Func<,,,,,>).MakeGenericType(types); + case 7: return typeof(Func<,,,,,,>).MakeGenericType(types); + case 8: return typeof(Func<,,,,,,,>).MakeGenericType(types); + case 9: return typeof(Func<,,,,,,,,>).MakeGenericType(types); + case 10: return typeof(Func<,,,,,,,,,>).MakeGenericType(types); + case 11: return typeof(Func<,,,,,,,,,,>).MakeGenericType(types); + case 12: return typeof(Func<,,,,,,,,,,,>).MakeGenericType(types); + case 13: return typeof(Func<,,,,,,,,,,,,>).MakeGenericType(types); + case 14: return typeof(Func<,,,,,,,,,,,,,>).MakeGenericType(types); + case 15: return typeof(Func<,,,,,,,,,,,,,,>).MakeGenericType(types); + case 16: return typeof(Func<,,,,,,,,,,,,,,,>).MakeGenericType(types); + case 17: return typeof(Func<,,,,,,,,,,,,,,,,>).MakeGenericType(types); + + // *** END GENERATED CODE *** + + #endregion + } + } + throw Assert.Unreachable; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/DelegateHelpers.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/DelegateHelpers.cs new file mode 100644 index 0000000000..cb77e78882 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/DelegateHelpers.cs @@ -0,0 +1,90 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.Threading; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Generation { + internal static partial class DelegateHelpers { + + private static Dictionary, Type> _DelegateTypes; + + private static Type MakeCustomDelegate(Type[] types) { + if (_DelegateTypes == null) { + Interlocked.CompareExchange( + ref _DelegateTypes, + new Dictionary, Type>(ListEqualityComparer.Instance), + null + ); + } + + bool found; + Type type; + + // + // LOCK to retrieve the delegate type, if any + // + + lock (_DelegateTypes) { + found = _DelegateTypes.TryGetValue(types, out type); + } + + if (!found && type != null) { + return type; + } + + // + // Create new delegate type + // + + type = MakeNewCustomDelegate(types); + + // + // LOCK to insert new delegate into the cache. If we already have one (racing threads), use the one from the cache + // + + lock (_DelegateTypes) { + Type conflict; + if (_DelegateTypes.TryGetValue(types, out conflict) && conflict != null) { + type = conflict; + } else { + _DelegateTypes[types] = type; + } + } + + return type; + } + + private const MethodAttributes CtorAttributes = MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public; + private const MethodImplAttributes ImplAttributes = MethodImplAttributes.Runtime | MethodImplAttributes.Managed; + private const MethodAttributes InvokeAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; + + private static readonly Type[] _DelegateCtorSignature = new Type[] { typeof(object), typeof(IntPtr) }; + + private static Type MakeNewCustomDelegate(Type[] types) { + Type returnType = types[types.Length - 1]; + Type[] parameters = types.RemoveLast(); + + TypeBuilder builder = Snippets.Shared.DefineDelegateType("Delegate" + types.Length); + builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _DelegateCtorSignature).SetImplementationFlags(ImplAttributes); + builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes); + return builder.CreateType(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/DynamicILGen.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/DynamicILGen.cs new file mode 100644 index 0000000000..bfdf4e8048 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/DynamicILGen.cs @@ -0,0 +1,82 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Reflection.Emit; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Generation { + + public abstract class DynamicILGen : ILGen { + internal DynamicILGen(ILGenerator il) + : base(il) { + } + + public T CreateDelegate() { + MethodInfo mi; + return CreateDelegate(out mi); + } + + public abstract T CreateDelegate(out MethodInfo mi); + + public abstract MethodInfo Finish(); + } + + class DynamicILGenMethod : DynamicILGen { + private readonly DynamicMethod _dm; + + internal DynamicILGenMethod(DynamicMethod dm, ILGenerator il) + : base(il) { + _dm = dm; + } + + public override T CreateDelegate(out MethodInfo mi) { + ContractUtils.Requires(typeof(T).IsSubclassOf(typeof(Delegate)), "T"); + mi = _dm; + return (T)(object)_dm.CreateDelegate(typeof(T), null); + } + + public override MethodInfo Finish() { + return _dm; + } + } + + class DynamicILGenType : DynamicILGen { + private readonly TypeBuilder _tb; + private readonly MethodBuilder _mb; + + internal DynamicILGenType(TypeBuilder tb, MethodBuilder mb, ILGenerator il) + : base(il) { + _tb = tb; + _mb = mb; + } + + public override T CreateDelegate(out MethodInfo mi) { + ContractUtils.Requires(typeof(T).IsSubclassOf(typeof(Delegate)), "T"); + mi = CreateMethod(); + return (T)(object)Delegate.CreateDelegate(typeof(T), mi); + } + + private MethodInfo CreateMethod() { + Type t = _tb.CreateType(); + return t.GetMethod(_mb.Name); + } + + public override MethodInfo Finish() { + return CreateMethod(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalArrayRewriter.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalArrayRewriter.cs new file mode 100644 index 0000000000..8b7411dc68 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalArrayRewriter.cs @@ -0,0 +1,94 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection.Emit; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Generation { + + /// + /// Rewrites globals to elements on a closed over array + /// + internal class GlobalArrayRewriter : GlobalOptimizedRewriter { + internal GlobalArrayRewriter() { + } + + internal GlobalArrayRewriter(Dictionary symbolDict, TypeGen typeGen) + : base(symbolDict) { + TypeGen = typeGen; + } + + // This starts as a List, but becomes readonly when we're finished allocating + private IList _names = new List(); + private ParameterExpression _array; + + internal ReadOnlyCollection Names { + get { + ReadOnlyCollection names = _names as ReadOnlyCollection; + if (names == null) { + // can't add any more items after this + _names = names = new ReadOnlyCollection(_names); + } + return names; + } + } + + protected override Expression VisitLambda(Expression node) { + // Only run this for the top-level lambda + if (_array == null) { + _array = Expression.Variable(typeof(ModuleGlobalWrapper[]), "$globals"); + Expression body = AstUtils.AddScopedVariable( + node.Body, + _array, + Expression.Call(typeof(ScriptingRuntimeHelpers).GetMethod("GetGlobalArray"), Context) + ); + Debug.Assert(node.NodeType == ExpressionType.Lambda); + node = Expression.Lambda( + body, + node.Name, + node.Parameters + ); + } + + return base.VisitLambda(node); + } + + protected override Expression MakeWrapper(GlobalVariableExpression variable) { + Debug.Assert(!_names.IsReadOnly); + int index = _names.Count; + _names.Add(variable.Name); + return Expression.ArrayAccess(_array, Expression.Constant(index)); + } + + internal IAttributesCollection CreateDictionary() { + return new GlobalsDictionary(SymbolTable.StringsToIds(Names)); + } + } +} + +namespace Microsoft.Scripting.Runtime { + public static partial class ScriptingRuntimeHelpers { + // TODO: remove and get the array some other way + public static ModuleGlobalWrapper[] GetGlobalArray(CodeContext context) { + return ((GlobalsDictionary)context.GlobalScope.Dict).Data; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalLookupRewriter.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalLookupRewriter.cs new file mode 100644 index 0000000000..7eef43acf6 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalLookupRewriter.cs @@ -0,0 +1,112 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Generation { + + /// + /// Converts globals to late bound lookups on the scope + /// TODO: move to IronPython + /// + public sealed class GlobalLookupRewriter : GlobalRewriter { + // Global & top-level locals are in seperate namespaces for named lookup + private readonly Dictionary _globalNames = new Dictionary(); + private readonly Dictionary _localNames = new Dictionary(); + + // TODO: internal + public GlobalLookupRewriter() { } + + protected override Expression RewriteGet(GlobalVariableExpression node) { + EnsureUniqueName(node); + + return AstUtils.Convert( + Expression.Call( + typeof(ScriptingRuntimeHelpers).GetMethod(node.IsLocal ? "LookupName" : "LookupGlobalName"), + new Expression[]{ + Context, + AstUtils.Constant(SymbolTable.StringToId(node.Name)) + } + ), + node.Type + ); + } + + protected override Expression RewriteSet(AssignmentExtensionExpression node) { + GlobalVariableExpression lvalue = (GlobalVariableExpression)node.Expression; + EnsureUniqueName(lvalue); + + return AstUtils.Convert( + Expression.Call( + typeof(ScriptingRuntimeHelpers).GetMethod(lvalue.IsLocal ? "SetName" : "SetGlobalName"), + new Expression[]{ + Context, + AstUtils.Constant(SymbolTable.StringToId(lvalue.Name)), + Visit(node.Value) + } + ), + node.Type + ); + } + + private void EnsureUniqueName(GlobalVariableExpression node) { + EnsureUniqueName(node.IsLocal ? _localNames : _globalNames, node); + } + } +} + +namespace Microsoft.Scripting.Runtime { + // TODO: move these to Microsoft.Scripting + public static partial class ScriptingRuntimeHelpers { + /// + /// Called from generated code, helper to do a global name lookup + /// + public static object LookupGlobalName(CodeContext context, SymbolId name) { + // TODO: could we get rid of new context creation: + CodeContext moduleScopedContext = new CodeContext(context.GlobalScope, context.LanguageContext); + return context.LanguageContext.LookupName(moduleScopedContext, name); + } + + /// + /// Called from generated code, helper to do global name assignment + /// + public static object SetGlobalName(CodeContext context, SymbolId name, object value) { + // TODO: could we get rid of new context creation: + CodeContext moduleScopedContext = new CodeContext(context.GlobalScope, context.LanguageContext); + context.LanguageContext.SetName(moduleScopedContext, name, value); + return value; + } + + /// + /// Called from generated code, helper to do name lookup + /// + public static object LookupName(CodeContext context, SymbolId name) { + return context.LanguageContext.LookupName(context, name); + } + + /// + /// Called from generated code, helper to do name assignment + /// + public static object SetName(CodeContext context, SymbolId name, object value) { + context.LanguageContext.SetName(context, name, value); + return value; + } + + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalOptimizedRewriter.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalOptimizedRewriter.cs new file mode 100644 index 0000000000..ad797e85ec --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalOptimizedRewriter.cs @@ -0,0 +1,122 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Generation { + + internal abstract class GlobalOptimizedRewriter : GlobalRewriter { + private readonly Dictionary _mapToExpression = new Dictionary(); + private readonly Dictionary _globalNames = new Dictionary(); + + internal GlobalOptimizedRewriter() { + _indirectSymbolIds = new Dictionary(); + } + + internal GlobalOptimizedRewriter(Dictionary symbolDict) { + _indirectSymbolIds = symbolDict; + } + + protected abstract Expression MakeWrapper(GlobalVariableExpression variable); + + protected override Expression RewriteGet(GlobalVariableExpression node) { + return AstUtils.Convert(MapToExpression(node), node.Type); + } + + protected override Expression RewriteSet(AssignmentExtensionExpression node) { + GlobalVariableExpression lvalue = (GlobalVariableExpression)node.Expression; + + return AstUtils.Convert( + Expression.Assign( + MapToExpression(lvalue), + AstUtils.Convert(Visit(node.Value), typeof(object)) + ), + node.Type + ); + } + + protected Expression MapToExpression(GlobalVariableExpression variable) { + Expression result; + if (_mapToExpression.TryGetValue(variable, out result)) { + return result; + } + + EnsureUniqueName(_globalNames, variable); + + result = Expression.Property( + MakeWrapper(variable), + typeof(ModuleGlobalWrapper).GetProperty("CurrentValue") + ); + + return _mapToExpression[variable] = result; + } + + // TODO: Do we really need this optimization? + // it adds complexity + #region SymbolId rewrite support + + // TypeGen, possibly null + protected TypeGen TypeGen { get; set; } + + // If TypeGen is non-null, we rewrite SymbolIds to static field accesses + private readonly Dictionary _indirectSymbolIds; + + protected override Expression VisitExtension(Expression node) { + var symbol = node as SymbolConstantExpression; + if (symbol != null && TypeGen != null) { + return GetSymbolExpression(symbol.Value); + } + return base.VisitExtension(node); + } + + protected void EmitSymbolId(ILGen cg, SymbolId id) { + cg.Emit(OpCodes.Ldsfld, GetSymbolField(id)); + } + + private Expression GetSymbolExpression(SymbolId id) { + return Expression.Field(null, GetSymbolField(id)); + } + + private FieldInfo GetSymbolField(SymbolId id) { + Debug.Assert(TypeGen != null); + + if (id == SymbolId.Empty) { + return typeof(SymbolId).GetField("Empty"); + } + FieldBuilder value; + if (!_indirectSymbolIds.TryGetValue(id, out value)) { + // create field, emit fix-up... + + value = TypeGen.AddStaticField(typeof(SymbolId), FieldAttributes.Public, SymbolTable.IdToString(id)); + ILGen init = TypeGen.TypeInitializer; + if (_indirectSymbolIds.Count == 0) { + init.EmitType(TypeGen.TypeBuilder); + init.EmitCall(typeof(ScriptingRuntimeHelpers), "InitializeSymbols"); + } + _indirectSymbolIds[id] = value; + } + return value; + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalRewriter.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalRewriter.cs new file mode 100644 index 0000000000..580e3cbdbf --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalRewriter.cs @@ -0,0 +1,168 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Runtime; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Generation { + + /// + /// Rewrites known extension nodes into primitive ones: + /// * GlobalVariableExpression + /// * CodeContextExpression + /// * CodeContextScopeExpression + /// + /// TODO: remove all of the functionality related to CodeContext, once + /// Python and JS fix their scope implementations to and the CodeContext* + /// nodes go away. All that should be left is global support, and even that + /// can go once OptimizedModules moves into Python. + /// + public abstract class GlobalRewriter : StaticLambdaRewriter { + private Expression _context; + + // Rewrite entry points + public Expression RewriteLambda(LambdaExpression lambda) { + return RewriteLambda(lambda, lambda.Name); + } + + public Expression RewriteLambda(LambdaExpression lambda, string name) { + Debug.Assert(_context == null); + Debug.Assert(lambda.Parameters.Count == 0); + + // Fix up the top-level lambda to have a scope and language parameters + ParameterExpression scopeParameter = Expression.Parameter(typeof(Scope), "$scope"); + ParameterExpression languageParameter = Expression.Parameter(typeof(LanguageContext), "$language"); + ParameterExpression contextVariable = Expression.Variable(typeof(CodeContext), "$globalContext"); + + _context = contextVariable; + lambda = (LambdaExpression)Visit(lambda); + + return Expression.Lambda( + AstUtils.AddScopedVariable( + lambda.Body, + contextVariable, + Expression.Call(typeof(ScriptingRuntimeHelpers).GetMethod("CreateTopLevelCodeContext"), scopeParameter, languageParameter) + ), + name, + new[] { scopeParameter, languageParameter } + ); + } + + protected abstract Expression RewriteGet(GlobalVariableExpression node); + protected abstract Expression RewriteSet(AssignmentExtensionExpression node); + + #region rewriter overrides + + protected override Expression VisitExtension(Expression node) { + if (node is YieldExpression || + node is GeneratorExpression || + node is FinallyFlowControlExpression) { + // These should be rewritten last, when doing finaly compilation + // for now, just walk them so we can find other nodes + return base.VisitExtension(node); + } + + GlobalVariableExpression global = node as GlobalVariableExpression; + if (global != null) { + return RewriteGet(global); + } + + CodeContextExpression cc = node as CodeContextExpression; + if (cc != null) { + return _context; + } + + CodeContextScopeExpression ccs = node as CodeContextScopeExpression; + if (ccs != null) { + return Rewrite(ccs); + } + + AssignmentExtensionExpression aee = node as AssignmentExtensionExpression; + if (aee != null) { + return Rewrite(aee); + } + + // Must remove extension nodes because they could contain + // one of the above node types. See, e.g. DeleteUnboundExpression + return Visit(node.ReduceExtensions()); + } + + private Expression Rewrite(AssignmentExtensionExpression node) { + Expression lvalue = node.Expression; + + GlobalVariableExpression global = lvalue as GlobalVariableExpression; + if (global != null) { + return RewriteSet(node); + } + + return node; + } + + #endregion + + #region CodeContext support + + protected Expression Context { + get { return _context; } + set { _context = value; } + } + + private Expression Rewrite(CodeContextScopeExpression ccs) { + Expression saved = _context; + ParameterExpression nested = Expression.Variable(typeof(CodeContext), "$frame"); + + // rewrite body with nested context + _context = nested; + Expression body = Visit(ccs.Body); + _context = saved; + + // wrap the body in a scope that initializes the nested context + return AstUtils.AddScopedVariable(body, nested, Visit(ccs.NewContext)); + } + + #endregion + + #region helpers + + protected static void EnsureUniqueName(IDictionary varsByName, GlobalVariableExpression node) { + GlobalVariableExpression n2; + if (varsByName.TryGetValue(node.Name, out n2)) { + if (node == n2) { + return; + } + throw Error.GlobalsMustBeUnique(); + } + + varsByName.Add(node.Name, node); + } + + #endregion + } +} + +namespace Microsoft.Scripting.Runtime { + public static partial class ScriptingRuntimeHelpers { + // emitted by GlobalRewriter + // TODO: Python and JScript should do this + public static CodeContext CreateTopLevelCodeContext(Scope scope, LanguageContext context) { + context.EnsureScopeExtension(scope.ModuleScope); + return new CodeContext(scope, context); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalStaticFieldRewriter.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalStaticFieldRewriter.cs new file mode 100644 index 0000000000..90f55b9a9b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/GlobalStaticFieldRewriter.cs @@ -0,0 +1,347 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Generation { + + /// + /// Rewrites globals to static fields on a type + /// Also rewrites constants to static fields + /// + internal sealed class GlobalStaticFieldRewriter : GlobalOptimizedRewriter { + private readonly Dictionary _fields = new Dictionary(); + private FieldBuilder _contextField; + + // TODO: remove this static data, and switch to instance data. + // It's only static currently because NewTypeMaker has a dependency on + // OptimizedScriptCode.InitializeFields + private readonly Dictionary _constantCache = new Dictionary(ReferenceEqualityComparer.Instance); + private List _staticFields = new List(); + private static readonly List _staticData = new List(); + private static readonly object _nullVal = new object(); + [MultiRuntimeAware] + private static int _lastCheck, _empties; + + internal GlobalStaticFieldRewriter(TypeGen typeGen) { + TypeGen = typeGen; + } + + protected override Expression VisitLambda(Expression node) { + // only run this for top lambda + if (_contextField == null) { + // Optimization: use the static field codecontext rather than + // the argument. It's faster for the nested functions that + // would otherwise need to close over the context argument. + _contextField = TypeGen.TypeBuilder.DefineField( + CodeContext.ContextFieldName, + typeof(CodeContext), + FieldAttributes.Public | FieldAttributes.Static + ); + Context = Expression.Field(null, _contextField); + } + return base.VisitLambda(node); + } + + protected override Expression MakeWrapper(GlobalVariableExpression variable) { + Debug.Assert(!_fields.ContainsKey(variable)); + + FieldBuilder field = TypeGen.TypeBuilder.DefineField( + variable.Name, + typeof(ModuleGlobalWrapper), + FieldAttributes.Assembly | FieldAttributes.Static + ); + + _fields.Add(variable, field); + + return Expression.Field(null, field); + } + + #region runtime constant support + + protected override Expression VisitConstant(ConstantExpression node) { + object data = node.Value; + Type type = node.Type; + + // if the constant can be emitted into IL, nothing to do + if (CanEmitConstant(data, type)) { + return node; + } + + type = TypeUtils.GetConstantType(type); + + int index; + if (!_constantCache.TryGetValue(data, out index)) { + int number = AddStaticData(data); + FieldBuilder field = TypeGen.AddStaticField(type, "#Constant" + number); + index = _staticFields.Count; + _staticFields.Add(field); + _constantCache.Add(data, index); + } + + return Expression.Field(null, _staticFields[index]); + } + + // Matches ILGen.TryEmitConstant + private static bool CanEmitConstant(object value, Type type) { + if (value == null || CanEmitILConstant(type)) { + return true; + } + + Type t = value as Type; + if (t != null && ILGen.ShouldLdtoken(t)) { + return true; + } + + MethodBase mb = value as MethodBase; + if (mb != null && ILGen.ShouldLdtoken(mb)) { + return true; + } + + return false; + } + + // Matches ILGen.TryEmitILConstant + private static bool CanEmitILConstant(Type type) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Char: + case TypeCode.Byte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + case TypeCode.Decimal: + case TypeCode.String: + return true; + } + return false; + } + + private static int AddStaticData(object data) { + lock (_staticData) { + if (_empties != 0) { + while (_lastCheck < _staticData.Count) { + if (_staticData[_lastCheck] == null) { + _staticData[_lastCheck] = data == null ? _nullVal : data; + _empties--; + return _lastCheck; + } + _lastCheck++; + } + } + + _lastCheck = 0; + _staticData.Add(data == null ? _nullVal : data); + return _staticData.Count - 1; + } + } + + internal static object GetConstantData(int index) { + lock (_staticData) { + object res = _staticData[index]; + _staticData[index] = null; + _empties++; + Debug.Assert(res != null); + return res == _nullVal ? null : res; + } + } + + internal static object GetConstantDataReusable(int index) { + lock (_staticData) { + object res = _staticData[index]; + Debug.Assert(res != null); + return res == _nullVal ? null : res; + } + } + + #endregion + + internal void EmitDictionary() { + MakeGetMethod(); + MakeSetMethod(); + MakeRawKeysMethod(); + MakeInitialization(); + } + + #region EmitDictionary implementation + + private void MakeInitialization() { + TypeGen.TypeBuilder.AddInterfaceImplementation(typeof(IModuleDictionaryInitialization)); + MethodInfo baseMethod = typeof(IModuleDictionaryInitialization).GetMethod("InitializeModuleDictionary"); + ILGen cg = TypeGen.DefineExplicitInterfaceImplementation(baseMethod); + + Label ok = cg.DefineLabel(); + cg.EmitFieldGet(_contextField); + cg.Emit(OpCodes.Ldnull); + cg.Emit(OpCodes.Ceq); + cg.Emit(OpCodes.Brtrue_S, ok); + cg.EmitNew(typeof(InvalidOperationException), Type.EmptyTypes); + cg.Emit(OpCodes.Throw); + cg.MarkLabel(ok); + + // arg0 -> this + // arg1 -> MyModuleDictType.ContextSlot + cg.EmitLoadArg(1); + cg.EmitFieldSet(_contextField); + + ConstructorInfo wrapperCtor = typeof(ModuleGlobalWrapper).GetConstructor(new Type[] { typeof(CodeContext), typeof(SymbolId) }); + foreach (KeyValuePair kv in _fields) { + // wrapper = new ModuleGlobalWrapper(context, name); + cg.EmitLoadArg(1); + EmitSymbolId(cg, SymbolTable.StringToId(kv.Key.Name)); + cg.Emit(OpCodes.Newobj, wrapperCtor); + cg.Emit(OpCodes.Stsfld, kv.Value); + } + + cg.Emit(OpCodes.Ret); + } + + // + // This generates a method like the following: + // + // TryGetExtraValue(int name, object out value) { + // if (name1 == name) { + // value = type.name1Slot.RawValue; + // return value != Uninitialized.Instance; + // } + // if (name2 == name) { + // value = type.name2Slot.RawValue; + // return value != Uninitialized.Instance; + // } + // ... + // return false + // } + + private void MakeGetMethod() { + MethodInfo baseMethod = typeof(CustomSymbolDictionary).GetMethod("TryGetExtraValue", BindingFlags.NonPublic | BindingFlags.Instance); + ILGen cg = TypeGen.DefineMethodOverride(baseMethod); + + foreach (KeyValuePair kv in _fields) { + SymbolId name = SymbolTable.StringToId(kv.Key.Name); + + EmitSymbolId(cg, name); + // arg0 -> this + cg.EmitLoadArg(1); + cg.EmitCall(typeof(SymbolId), "op_Equality"); + + Label next = cg.DefineLabel(); + cg.Emit(OpCodes.Brfalse_S, next); + + cg.EmitLoadArg(2); + + // Expects to push as an object. + EmitGetRawFromObject(cg, kv.Value); + cg.Emit(OpCodes.Stind_Ref); + + EmitGetRawFromObject(cg, kv.Value); + cg.EmitFieldGet(typeof(Uninitialized), "Instance"); + cg.Emit(OpCodes.Ceq); + cg.Emit(OpCodes.Not); + cg.Emit(OpCodes.Ret); + cg.MarkLabel(next); + } + cg.EmitInt(0); + cg.Emit(OpCodes.Ret); + } + + private static void EmitGetRawFromObject(ILGen cg, FieldBuilder wrapper) { + cg.EmitFieldGet(wrapper); + cg.EmitPropertyGet(typeof(ModuleGlobalWrapper), "RawValue"); + } + + // This generates a method like the following: + // + // TrySetExtraValue(object name, object value) { + // if (name1 == name) { + // type.name1Slot = value; + // return 1; + // } + // if (name2 == name) { + // type.name2Slot = value; + // return 1; + // } + // ... + // return 0 + // } + + private void MakeSetMethod() { + MethodInfo baseMethod = typeof(CustomSymbolDictionary).GetMethod("TrySetExtraValue", BindingFlags.NonPublic | BindingFlags.Instance); + ILGen cg = TypeGen.DefineMethodOverride(baseMethod); + + foreach (KeyValuePair kv in _fields) { + SymbolId name = SymbolTable.StringToId(kv.Key.Name); + + EmitSymbolId(cg, name); + // arg0 -> this + cg.EmitLoadArg(1); + cg.EmitCall(typeof(SymbolId), "op_Equality"); + + Label next = cg.DefineLabel(); + cg.Emit(OpCodes.Brfalse_S, next); + + cg.EmitFieldGet(kv.Value); + cg.EmitLoadArg(2); + cg.EmitPropertySet(typeof(ModuleGlobalWrapper), "CurrentValue"); + + cg.EmitInt(1); + cg.Emit(OpCodes.Ret); + cg.MarkLabel(next); + } + cg.EmitInt(0); + cg.Emit(OpCodes.Ret); + } + + private ILGen MakeRawKeysMethod() { + FieldBuilder rawKeysCache = TypeGen.AddStaticField(typeof(SymbolId[]), "ExtraKeysCache"); + ILGen init = TypeGen.TypeInitializer; + + init.EmitInt(_fields.Count); + init.Emit(OpCodes.Newarr, typeof(SymbolId)); + + int current = 0; + foreach (GlobalVariableExpression variable in _fields.Keys) { + init.Emit(OpCodes.Dup); + init.EmitInt(current++); + EmitSymbolId(init, SymbolTable.StringToId(variable.Name)); + init.EmitStoreElement(typeof(SymbolId)); + } + + init.Emit(OpCodes.Stsfld, rawKeysCache); + + MethodInfo baseMethod = typeof(CustomSymbolDictionary).GetMethod("GetExtraKeys", BindingFlags.Public | BindingFlags.Instance); + ILGen cg = TypeGen.DefineExplicitInterfaceImplementation(baseMethod); + + cg.Emit(OpCodes.Ldsfld, rawKeysCache); + cg.Emit(OpCodes.Ret); + return cg; + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/IExpressionSerializable.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/IExpressionSerializable.cs new file mode 100644 index 0000000000..6ef26bfd04 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/IExpressionSerializable.cs @@ -0,0 +1,26 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Runtime { + /// + /// Enables an object to be serializable to an Expression tree. The expression tree can then + /// be emitted into an assembly enabling the de-serialization of the object. + /// + public interface IExpressionSerializable { + Expression CreateExpression(); + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/ILGen.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/ILGen.cs new file mode 100644 index 0000000000..6547aac775 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/ILGen.cs @@ -0,0 +1,1669 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.SymbolStore; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Generation { + + public delegate void EmitArrayHelper(int index); + + // TODO: change to extension methods for ILGenerator + public class ILGen { + private readonly ILGenerator _ilg; + private readonly KeyedQueue _freeLocals = new KeyedQueue(); + + // TODO: remove Python dependency + public ILGen(ILGenerator ilg) { + ContractUtils.RequiresNotNull(ilg, "ilg"); + + _ilg = ilg; + } + + #region ILGenerator Methods + + /// + /// Begins a catch block. + /// + public virtual void BeginCatchBlock(Type exceptionType) { + _ilg.BeginCatchBlock(exceptionType); + } + + /// + /// Begins an exception block for a filtered exception. + /// + public virtual void BeginExceptFilterBlock() { + _ilg.BeginExceptFilterBlock(); + } + + /// + /// Begins an exception block for a non-filtered exception. + /// + /// + public virtual Label BeginExceptionBlock() { + return _ilg.BeginExceptionBlock(); + } + + /// + /// Begins an exception fault block + /// + public virtual void BeginFaultBlock() { + _ilg.BeginFaultBlock(); + } + + /// + /// Begins a finally block + /// + public virtual void BeginFinallyBlock() { + _ilg.BeginFinallyBlock(); + } + + /// + /// Ends an exception block. + /// + public virtual void EndExceptionBlock() { + _ilg.EndExceptionBlock(); + } + + /// + /// Begins a lexical scope. + /// + public virtual void BeginScope() { + _ilg.BeginScope(); + } + + /// + /// Ends a lexical scope. + /// + public virtual void EndScope() { + _ilg.EndScope(); + } + + /// + /// Declares a local variable of the specified type. + /// + public virtual LocalBuilder DeclareLocal(Type localType) { + return _ilg.DeclareLocal(localType); + } + + /// + /// Declares a local variable of the specified type, optionally + /// pinning the object referred to by the variable. + /// + public virtual LocalBuilder DeclareLocal(Type localType, bool pinned) { + return _ilg.DeclareLocal(localType, pinned); + } + + /// + /// Declares a new label. + /// + public virtual Label DefineLabel() { + return _ilg.DefineLabel(); + } + + /// + /// Marks the label at the current position. + /// + public virtual void MarkLabel(Label loc) { + _ilg.MarkLabel(loc); + } + + /// + /// Emits an instruction. + /// + public virtual void Emit(OpCode opcode) { + _ilg.Emit(opcode); + } + + /// + /// Emits an instruction with a byte argument. + /// + public virtual void Emit(OpCode opcode, byte arg) { + _ilg.Emit(opcode, arg); + } + + /// + /// Emits an instruction with the metadata token for the specified contructor. + /// + public virtual void Emit(OpCode opcode, ConstructorInfo con) { + _ilg.Emit(opcode, con); + } + + /// + /// Emits an instruction with a double argument. + /// + public virtual void Emit(OpCode opcode, double arg) { + _ilg.Emit(opcode, arg); + } + + /// + /// Emits an instruction with the metadata token for the specified field. + /// + public virtual void Emit(OpCode opcode, FieldInfo field) { + _ilg.Emit(opcode, field); + } + + /// + /// Emits an instruction with a float argument. + /// + public virtual void Emit(OpCode opcode, float arg) { + _ilg.Emit(opcode, arg); + } + + /// + /// Emits an instruction with an int argument. + /// + public virtual void Emit(OpCode opcode, int arg) { + _ilg.Emit(opcode, arg); + } + + /// + /// Emits an instruction with a label argument. + /// + public virtual void Emit(OpCode opcode, Label label) { + _ilg.Emit(opcode, label); + } + + /// + /// Emits an instruction with multiple target labels (switch). + /// + public virtual void Emit(OpCode opcode, Label[] labels) { + _ilg.Emit(opcode, labels); + } + + /// + /// Emits an instruction with a reference to a local variable. + /// + public virtual void Emit(OpCode opcode, LocalBuilder local) { + _ilg.Emit(opcode, local); + } + + /// + /// Emits an instruction with a long argument. + /// + public virtual void Emit(OpCode opcode, long arg) { + _ilg.Emit(opcode, arg); + } + + /// + /// Emits an instruction with the metadata token for a specified method. + /// + public virtual void Emit(OpCode opcode, MethodInfo meth) { + _ilg.Emit(opcode, meth); + } + + /// + /// Emits an instruction with a signed byte argument. + /// + [CLSCompliant(false)] + public virtual void Emit(OpCode opcode, sbyte arg) { + _ilg.Emit(opcode, arg); + } + + /// + /// Emits an instruction with a short argument. + /// + public virtual void Emit(OpCode opcode, short arg) { + _ilg.Emit(opcode, arg); + } + +#if !SILVERLIGHT + /// + /// Emits an instruction with a signature token. + /// + public virtual void Emit(OpCode opcode, SignatureHelper signature) { + _ilg.Emit(opcode, signature); + } +#endif + + /// + /// Emits an instruction with a string argument. + /// + public virtual void Emit(OpCode opcode, string str) { + _ilg.Emit(opcode, str); + } + + /// + /// Emits an instruction with the metadata token for a specified type argument. + /// + public virtual void Emit(OpCode opcode, Type cls) { + _ilg.Emit(opcode, cls); + } + + /// + /// Emits a call or a virtual call to the varargs method. + /// + public virtual void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[] optionalParameterTypes) { + _ilg.EmitCall(opcode, methodInfo, optionalParameterTypes); + } + +#if !SILVERLIGHT + /// + /// Emits an unmanaged indirect call instruction. + /// + public virtual void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes) { + _ilg.EmitCalli(opcode, unmanagedCallConv, returnType, parameterTypes); + } + + /// + /// Emits a managed indirect call instruction. + /// + public virtual void EmitCalli(OpCode opcode, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes) { + _ilg.EmitCalli(opcode, callingConvention, returnType, parameterTypes, optionalParameterTypes); + } +#endif + + /// + /// Marks a sequence point. + /// + public virtual void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { + _ilg.MarkSequencePoint(document, startLine, startColumn, endLine, endColumn); + } + + /// + /// Specifies the namespace to be used in evaluating locals and watches for the + /// current active lexical scope. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames")] // TODO: fix + public virtual void UsingNamespace(string usingNamespace) { + _ilg.UsingNamespace(usingNamespace); + } + + #endregion + + #region Simple helpers + + [Conditional("DEBUG")] + internal void EmitDebugWriteLine(string message) { + EmitString(message); + EmitCall(typeof(Debug), "WriteLine", new Type[] { typeof(string) }); + } + + internal void Emit(OpCode opcode, MethodBase methodBase) { + Debug.Assert(methodBase is MethodInfo || methodBase is ConstructorInfo); + + if (methodBase.MemberType == MemberTypes.Constructor) { + Emit(opcode, (ConstructorInfo)methodBase); + } else { + Emit(opcode, (MethodInfo)methodBase); + } + } + + #endregion + + #region Instruction helpers + + public void EmitLoadArg(int index) { + ContractUtils.Requires(index >= 0, "index"); + + switch (index) { + case 0: + Emit(OpCodes.Ldarg_0); + break; + case 1: + Emit(OpCodes.Ldarg_1); + break; + case 2: + Emit(OpCodes.Ldarg_2); + break; + case 3: + Emit(OpCodes.Ldarg_3); + break; + default: + if (index <= Byte.MaxValue) { + Emit(OpCodes.Ldarg_S, (byte)index); + } else { + this.Emit(OpCodes.Ldarg, index); + } + break; + } + } + + public void EmitLoadArgAddress(int index) { + ContractUtils.Requires(index >= 0, "index"); + + if (index <= Byte.MaxValue) { + Emit(OpCodes.Ldarga_S, (byte)index); + } else { + Emit(OpCodes.Ldarga, index); + } + } + + public void EmitStoreArg(int index) { + ContractUtils.Requires(index >= 0, "index"); + + if (index <= Byte.MaxValue) { + Emit(OpCodes.Starg_S, (byte)index); + } else { + Emit(OpCodes.Starg, index); + } + } + + /// + /// Emits a Ldind* instruction for the appropriate type + /// + public void EmitLoadValueIndirect(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + if (type.IsValueType) { + if (type == typeof(int)) { + Emit(OpCodes.Ldind_I4); + } else if (type == typeof(uint)) { + Emit(OpCodes.Ldind_U4); + } else if (type == typeof(short)) { + Emit(OpCodes.Ldind_I2); + } else if (type == typeof(ushort)) { + Emit(OpCodes.Ldind_U2); + } else if (type == typeof(long) || type == typeof(ulong)) { + Emit(OpCodes.Ldind_I8); + } else if (type == typeof(char)) { + Emit(OpCodes.Ldind_I2); + } else if (type == typeof(bool)) { + Emit(OpCodes.Ldind_I1); + } else if (type == typeof(float)) { + Emit(OpCodes.Ldind_R4); + } else if (type == typeof(double)) { + Emit(OpCodes.Ldind_R8); + } else { + Emit(OpCodes.Ldobj, type); + } + } else { + Emit(OpCodes.Ldind_Ref); + } + } + + + /// + /// Emits a Stind* instruction for the appropriate type. + /// + public void EmitStoreValueIndirect(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + if (type.IsValueType) { + if (type == typeof(int)) { + Emit(OpCodes.Stind_I4); + } else if (type == typeof(short)) { + Emit(OpCodes.Stind_I2); + } else if (type == typeof(long) || type == typeof(ulong)) { + Emit(OpCodes.Stind_I8); + } else if (type == typeof(char)) { + Emit(OpCodes.Stind_I2); + } else if (type == typeof(bool)) { + Emit(OpCodes.Stind_I1); + } else if (type == typeof(float)) { + Emit(OpCodes.Stind_R4); + } else if (type == typeof(double)) { + Emit(OpCodes.Stind_R8); + } else { + Emit(OpCodes.Stobj, type); + } + } else { + Emit(OpCodes.Stind_Ref); + } + } + + // Emits the Ldelem* instruction for the appropriate type + //CONFORMING + public void EmitLoadElement(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + if (!type.IsValueType) { + Emit(OpCodes.Ldelem_Ref); + } else if (type.IsEnum) { + Emit(OpCodes.Ldelem, type); + } else { + switch (Type.GetTypeCode(type)) { + case TypeCode.Boolean: + case TypeCode.SByte: + Emit(OpCodes.Ldelem_I1); + break; + case TypeCode.Byte: + Emit(OpCodes.Ldelem_U1); + break; + case TypeCode.Int16: + Emit(OpCodes.Ldelem_I2); + break; + case TypeCode.Char: + case TypeCode.UInt16: + Emit(OpCodes.Ldelem_U2); + break; + case TypeCode.Int32: + Emit(OpCodes.Ldelem_I4); + break; + case TypeCode.UInt32: + Emit(OpCodes.Ldelem_U4); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + Emit(OpCodes.Ldelem_I8); + break; + case TypeCode.Single: + Emit(OpCodes.Ldelem_R4); + break; + case TypeCode.Double: + Emit(OpCodes.Ldelem_R8); + break; + default: + Emit(OpCodes.Ldelem, type); + break; + } + } + } + + /// + /// Emits a Stelem* instruction for the appropriate type. + /// + public void EmitStoreElement(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + if (type.IsEnum) { + Emit(OpCodes.Stelem, type); + return; + } + switch (Type.GetTypeCode(type)) { + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Byte: + Emit(OpCodes.Stelem_I1); + break; + case TypeCode.Char: + case TypeCode.Int16: + case TypeCode.UInt16: + Emit(OpCodes.Stelem_I2); + break; + case TypeCode.Int32: + case TypeCode.UInt32: + Emit(OpCodes.Stelem_I4); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + Emit(OpCodes.Stelem_I8); + break; + case TypeCode.Single: + Emit(OpCodes.Stelem_R4); + break; + case TypeCode.Double: + Emit(OpCodes.Stelem_R8); + break; + default: + if (type.IsValueType) { + Emit(OpCodes.Stelem, type); + } else { + Emit(OpCodes.Stelem_Ref); + } + break; + } + } + + public void EmitType(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + Emit(OpCodes.Ldtoken, type); + EmitCall(typeof(Type), "GetTypeFromHandle"); + } + + public void EmitUnbox(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + Emit(OpCodes.Unbox_Any, type); + } + + #endregion + + #region Fields, properties and methods + + public void EmitPropertyGet(Type type, string name) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(name, "name"); + + PropertyInfo pi = type.GetProperty(name); + ContractUtils.Requires(pi != null, "name", Strings.PropertyDoesNotExist); + + EmitPropertyGet(pi); + } + + public void EmitPropertyGet(PropertyInfo pi) { + ContractUtils.RequiresNotNull(pi, "pi"); + + if (!pi.CanRead) { + throw Error.CantReadProperty(); + } + + EmitCall(pi.GetGetMethod()); + } + + public void EmitPropertySet(Type type, string name) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(name, "name"); + + PropertyInfo pi = type.GetProperty(name); + ContractUtils.Requires(pi != null, "name", Strings.PropertyDoesNotExist); + + EmitPropertySet(pi); + } + + public void EmitPropertySet(PropertyInfo pi) { + ContractUtils.RequiresNotNull(pi, "pi"); + + if (!pi.CanWrite) { + throw Error.CantWriteProperty(); + } + + EmitCall(pi.GetSetMethod()); + } + + public void EmitFieldAddress(FieldInfo fi) { + ContractUtils.RequiresNotNull(fi, "fi"); + + if (fi.IsStatic) { + Emit(OpCodes.Ldsflda, fi); + } else { + Emit(OpCodes.Ldflda, fi); + } + } + + public void EmitFieldGet(Type type, String name) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(name, "name"); + + FieldInfo fi = type.GetField(name); + ContractUtils.Requires(fi != null, "name", Strings.FieldDoesNotExist); + EmitFieldGet(fi); + } + + public void EmitFieldSet(Type type, String name) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(name, "name"); + + FieldInfo fi = type.GetField(name); + ContractUtils.Requires(fi != null, "name", Strings.FieldDoesNotExist); + EmitFieldSet(fi); + } + + public void EmitFieldGet(FieldInfo fi) { + ContractUtils.RequiresNotNull(fi, "fi"); + + if (fi.IsStatic) { + Emit(OpCodes.Ldsfld, fi); + } else { + Emit(OpCodes.Ldfld, fi); + } + } + + public void EmitFieldSet(FieldInfo fi) { + ContractUtils.RequiresNotNull(fi, "fi"); + + if (fi.IsStatic) { + Emit(OpCodes.Stsfld, fi); + } else { + Emit(OpCodes.Stfld, fi); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")] + public void EmitNew(ConstructorInfo ci) { + ContractUtils.RequiresNotNull(ci, "ci"); + + if (ci.DeclaringType.ContainsGenericParameters) { + throw Error.IllegalNew_GenericParams(ci.DeclaringType); + } + + Emit(OpCodes.Newobj, ci); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")] + public void EmitNew(Type type, Type[] paramTypes) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(paramTypes, "paramTypes"); + + ConstructorInfo ci = type.GetConstructor(paramTypes); + ContractUtils.Requires(ci != null, "type", Strings.TypeDoesNotHaveConstructorForTheSignature); + EmitNew(ci); + } + + public void EmitCall(MethodInfo mi) { + ContractUtils.RequiresNotNull(mi, "mi"); + + if (mi.IsVirtual && !mi.DeclaringType.IsValueType) { + Emit(OpCodes.Callvirt, mi); + } else { + Emit(OpCodes.Call, mi); + } + } + + public void EmitCall(Type type, String name) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(name, "name"); + + MethodInfo mi = type.GetMethod(name); + ContractUtils.Requires(mi != null, "type", Strings.TypeDoesNotHaveMethodForName); + + EmitCall(mi); + } + + public void EmitCall(Type type, String name, Type[] paramTypes) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(name, "name"); + ContractUtils.RequiresNotNull(paramTypes, "paramTypes"); + + MethodInfo mi = type.GetMethod(name, paramTypes); + ContractUtils.Requires(mi != null, "type", Strings.TypeDoesNotHaveMethodForNameSignature); + + EmitCall(mi); + } + + #endregion + + #region Constants + + public void EmitNull() { + Emit(OpCodes.Ldnull); + } + + public void EmitString(string value) { + ContractUtils.RequiresNotNull(value, "value"); + Emit(OpCodes.Ldstr, value); + } + + public void EmitBoolean(bool value) { + if (value) { + Emit(OpCodes.Ldc_I4_1); + } else { + Emit(OpCodes.Ldc_I4_0); + } + } + + public void EmitChar(char value) { + EmitInt(value); + Emit(OpCodes.Conv_U2); + } + + public void EmitByte(byte value) { + EmitInt(value); + Emit(OpCodes.Conv_U1); + } + + [CLSCompliant(false)] + public void EmitSByte(sbyte value) { + EmitInt(value); + Emit(OpCodes.Conv_I1); + } + + public void EmitShort(short value) { + EmitInt(value); + Emit(OpCodes.Conv_I2); + } + + [CLSCompliant(false)] + public void EmitUShort(ushort value) { + EmitInt(value); + Emit(OpCodes.Conv_U2); + } + + public void EmitInt(int value) { + OpCode c; + switch (value) { + case -1: + c = OpCodes.Ldc_I4_M1; + break; + case 0: + c = OpCodes.Ldc_I4_0; + break; + case 1: + c = OpCodes.Ldc_I4_1; + break; + case 2: + c = OpCodes.Ldc_I4_2; + break; + case 3: + c = OpCodes.Ldc_I4_3; + break; + case 4: + c = OpCodes.Ldc_I4_4; + break; + case 5: + c = OpCodes.Ldc_I4_5; + break; + case 6: + c = OpCodes.Ldc_I4_6; + break; + case 7: + c = OpCodes.Ldc_I4_7; + break; + case 8: + c = OpCodes.Ldc_I4_8; + break; + default: + if (value >= -128 && value <= 127) { + Emit(OpCodes.Ldc_I4_S, (sbyte)value); + } else { + Emit(OpCodes.Ldc_I4, value); + } + return; + } + Emit(c); + } + + [CLSCompliant(false)] + public void EmitUInt(uint value) { + EmitInt((int)value); + Emit(OpCodes.Conv_U4); + } + + public void EmitLong(long value) { + Emit(OpCodes.Ldc_I8, value); + } + + [CLSCompliant(false)] + public void EmitULong(ulong value) { + Emit(OpCodes.Ldc_I8, (long)value); + Emit(OpCodes.Conv_U8); + } + + public void EmitDouble(double value) { + Emit(OpCodes.Ldc_R8, value); + } + + public void EmitSingle(float value) { + Emit(OpCodes.Ldc_R4, value); + } + + private void EmitSimpleConstant(object value) { + if (!TryEmitConstant(value, value == null ? typeof(object) : value.GetType())) { + throw Error.CanotEmitConstant(value, value.GetType()); + } + } + + //CONFORMING + // + // Note: we support emitting a lot more things as IL constants than + // Linq does + internal bool TryEmitConstant(object value, Type type) { + if (value == null) { + // Smarter than the Linq implementation which uses the initobj + // pattern for all value types (works, but requires a local and + // more IL) + EmitDefault(type); + return true; + } + + // Handle the easy cases + if (TryEmitILConstant(value, type)) { + return true; + } + + // Check for a few more types that we support emitting as constants + Type t = value as Type; + if (t != null && ShouldLdtoken(t)) { + EmitType(t); + return true; + } + + MethodBase mb = value as MethodBase; + if (mb != null && ShouldLdtoken(mb)) { + Emit(OpCodes.Ldtoken, mb); + Type dt = mb.DeclaringType; + if (dt != null && dt.IsGenericType) { + Emit(OpCodes.Ldtoken, dt); + EmitCall(typeof(MethodBase).GetMethod("GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle) })); + } else { + EmitCall(typeof(MethodBase).GetMethod("GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle) })); + } + type = TypeUtils.GetConstantType(type); + if (type != typeof(MethodBase)) { + Emit(OpCodes.Castclass, type); + } + return true; + } + + return false; + } + + // TODO: Can we always ldtoken and let restrictedSkipVisibility sort things out? + public static bool ShouldLdtoken(Type t) { + return t is TypeBuilder || t.IsGenericParameter || t.IsVisible; + } + + public static bool ShouldLdtoken(MethodBase mb) { + // Can't ldtoken on a DynamicMethod + if (mb is DynamicMethod) { + return false; + } + + Type dt = mb.DeclaringType; + return dt == null || ShouldLdtoken(dt); + } + + //CONFORMING + private bool TryEmitILConstant(object value, Type type) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Boolean: + EmitBoolean((bool)value); + return true; + case TypeCode.SByte: + EmitSByte((sbyte)value); + return true; + case TypeCode.Int16: + EmitShort((short)value); + return true; + case TypeCode.Int32: + EmitInt((int)value); + return true; + case TypeCode.Int64: + EmitLong((long)value); + return true; + case TypeCode.Single: + EmitSingle((float)value); + return true; + case TypeCode.Double: + EmitDouble((double)value); + return true; + case TypeCode.Char: + EmitChar((char)value); + return true; + case TypeCode.Byte: + EmitByte((byte)value); + return true; + case TypeCode.UInt16: + EmitUShort((ushort)value); + return true; + case TypeCode.UInt32: + EmitUInt((uint)value); + return true; + case TypeCode.UInt64: + EmitULong((ulong)value); + return true; + case TypeCode.Decimal: + EmitDecimal((decimal)value); + return true; + case TypeCode.String: + EmitString((string)value); + return true; + default: + return false; + } + } + + #endregion + + // TODO: deprecate in favor of the Linq versions + #region Conversions + + public void EmitImplicitCast(Type from, Type to) { + if (!TryEmitCast(from, to, true)) { + throw Error.NoImplicitCast(from, to); + } + } + + public void EmitExplicitCast(Type from, Type to) { + if (!TryEmitCast(from, to, false)) { + throw Error.NoExplicitCast(from, to); + } + } + + public bool TryEmitImplicitCast(Type from, Type to) { + return TryEmitCast(from, to, true); + } + + public bool TryEmitExplicitCast(Type from, Type to) { + return TryEmitCast(from, to, false); + } + + private bool TryEmitCast(Type from, Type to, bool implicitOnly) { + ContractUtils.RequiresNotNull(from, "from"); + ContractUtils.RequiresNotNull(to, "to"); + + // No cast necessary if identical types + if (from == to) { + return true; + } + + if (to.IsAssignableFrom(from)) { + // T -> Nullable + if (TypeUtils.IsNullableType(to)) { + Type nonNullableTo = TypeUtils.GetNonNullableType(to); + if (TryEmitCast(from, nonNullableTo, true)) { + EmitNew(to.GetConstructor(new Type[] { nonNullableTo })); + } else { + return false; + } + } + + if (from.IsValueType) { + if (to == typeof(object)) { + EmitBoxing(from); + return true; + } + } + + if (to.IsInterface) { + Emit(OpCodes.Box, from); + return true; + } + + if (from.IsEnum && to == typeof(Enum)) { + Emit(OpCodes.Box, from); + return true; + } + + // They are assignable and reference types. + return true; + } + + if (to == typeof(void)) { + Emit(OpCodes.Pop); + return true; + } + + if (to.IsValueType && from == typeof(object)) { + if (implicitOnly) { + return false; + } + Emit(OpCodes.Unbox_Any, to); + return true; + } + + if (to.IsValueType != from.IsValueType) { + return false; + } + + if (!to.IsValueType) { + if (implicitOnly) { + return false; + } + Emit(OpCodes.Castclass, to); + return true; + } + + if (to.IsEnum) { + to = Enum.GetUnderlyingType(to); + } + if (from.IsEnum) { + from = Enum.GetUnderlyingType(from); + } + + if (to == from) { + return true; + } + + if (EmitNumericCast(from, to, implicitOnly)) { + return true; + } + + return false; + } + + public bool EmitNumericCast(Type from, Type to, bool implicitOnly) { + TypeCode fc = Type.GetTypeCode(from); + TypeCode tc = Type.GetTypeCode(to); + int fromx, fromy, tox, toy; + + if (!TypeUtils.GetNumericConversionOrder(fc, out fromx, out fromy) || + !TypeUtils.GetNumericConversionOrder(tc, out tox, out toy)) { + // numeric <-> non-numeric + return false; + } + + bool isImplicit = TypeUtils.IsImplicitlyConvertible(fromx, fromy, tox, toy); + + if (implicitOnly && !isImplicit) { + return false; + } + + // IL conversion instruction also needed for floating point -> integer: + if (!isImplicit || toy == 2 || tox == 2) { + switch (tc) { + case TypeCode.SByte: + Emit(OpCodes.Conv_I1); + break; + case TypeCode.Int16: + Emit(OpCodes.Conv_I2); + break; + case TypeCode.Int32: + Emit(OpCodes.Conv_I4); + break; + case TypeCode.Int64: + Emit(OpCodes.Conv_I8); + break; + case TypeCode.Byte: + Emit(OpCodes.Conv_U1); + break; + case TypeCode.UInt16: + Emit(OpCodes.Conv_U1); + break; + case TypeCode.UInt32: + Emit(OpCodes.Conv_U2); + break; + case TypeCode.UInt64: + Emit(OpCodes.Conv_U4); + break; + case TypeCode.Single: + Emit(OpCodes.Conv_R4); + break; + case TypeCode.Double: + Emit(OpCodes.Conv_R8); + break; + default: + throw Assert.Unreachable; + } + } + + return true; + } + + /// + /// Boxes the value of the stack. No-op for reference types. Void is + /// converted to a null reference. For almost all value types this + /// method will box them in the standard way. Int32 and Boolean are + /// handled with optimized conversions that reuse the same object for + /// small values. For Int32 this is purely a performance optimization. + /// For Boolean this is use to ensure that True and False are always + /// the same objects. + /// + public void EmitBoxing(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + if (type.IsValueType) { + if (type == typeof(void)) { + Emit(OpCodes.Ldnull); + } else if (type == typeof(int)) { + EmitCall(typeof(RuntimeOps), "Int32ToObject"); + } else if (type == typeof(bool)) { + var label = DefineLabel(); + var end = DefineLabel(); + Emit(OpCodes.Brtrue_S, label); + Emit(OpCodes.Ldsfld, typeof(RuntimeOps).GetField("False")); + Emit(OpCodes.Br_S, end); + MarkLabel(label); + Emit(OpCodes.Ldsfld, typeof(RuntimeOps).GetField("True")); + MarkLabel(end); + } else { + Emit(OpCodes.Box, type); + } + } + } + + #endregion + + #region Linq Conversions + + //CONFORMING + // (plus support for None, Void conversions) + internal void EmitConvertToType(Type typeFrom, Type typeTo, bool isChecked) { + typeFrom = TypeUtils.GetNonNoneType(typeFrom); + + if (typeFrom == typeTo) { + return; + } + + // void -> non-void: default(T) + if (typeFrom == typeof(void)) { + EmitDefault(typeTo); + return; + } + + // non-void -> void: pop + if (typeTo == typeof(void)) { + Emit(OpCodes.Pop); + return; + } + + bool isTypeFromNullable = TypeUtils.IsNullableType(typeFrom); + bool isTypeToNullable = TypeUtils.IsNullableType(typeTo); + + Type nnExprType = TypeUtils.GetNonNullableType(typeFrom); + Type nnType = TypeUtils.GetNonNullableType(typeTo); + + if (typeFrom.IsInterface || // interface cast + typeTo.IsInterface || + typeFrom == typeof(object) || // boxing cast + typeTo == typeof(object)) { + EmitCastToType(typeFrom, typeTo); + } else if (isTypeFromNullable || isTypeToNullable) { + EmitNullableConversion(typeFrom, typeTo, isChecked); + } else if (!(TypeUtils.IsConvertible(typeFrom) && TypeUtils.IsConvertible(typeTo)) // primitive runtime conversion + && + (nnExprType.IsAssignableFrom(nnType) || // down cast + nnType.IsAssignableFrom(nnExprType))) // up cast + { + EmitCastToType(typeFrom, typeTo); + } else if (typeFrom.IsArray && typeTo.IsArray) { + // See DevDiv Bugs #94657. + EmitCastToType(typeFrom, typeTo); + } else { + EmitNumericConversion(typeFrom, typeTo, isChecked); + } + } + + //CONFORMING + private void EmitCastToType(Type typeFrom, Type typeTo) { + if (!typeFrom.IsValueType && typeTo.IsValueType) { + Emit(OpCodes.Unbox_Any, typeTo); + } else if (typeFrom.IsValueType && !typeTo.IsValueType) { + EmitBoxing(typeFrom); + if (typeTo != typeof(object)) { + Emit(OpCodes.Castclass, typeTo); + } + } else if (!typeFrom.IsValueType && !typeTo.IsValueType) { + Emit(OpCodes.Castclass, typeTo); + } else { + throw Error.InvalidCast(typeFrom, typeTo); + } + } + + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private void EmitNumericConversion(Type typeFrom, Type typeTo, bool isChecked) { + bool isFromUnsigned = TypeUtils.IsUnsigned(typeFrom); + bool isFromFloatingPoint = TypeUtils.IsFloatingPoint(typeFrom); + if (typeTo == typeof(Single)) { + if (isFromUnsigned) + Emit(OpCodes.Conv_R_Un); + Emit(OpCodes.Conv_R4); + } else if (typeTo == typeof(Double)) { + if (isFromUnsigned) + Emit(OpCodes.Conv_R_Un); + Emit(OpCodes.Conv_R8); + } else { + TypeCode tc = Type.GetTypeCode(typeTo); + if (isChecked) { + if (isFromUnsigned) { + switch (tc) { + case TypeCode.SByte: + Emit(OpCodes.Conv_Ovf_I1_Un); + break; + case TypeCode.Int16: + Emit(OpCodes.Conv_Ovf_I2_Un); + break; + case TypeCode.Int32: + Emit(OpCodes.Conv_Ovf_I4_Un); + break; + case TypeCode.Int64: + Emit(OpCodes.Conv_Ovf_I8_Un); + break; + case TypeCode.Byte: + Emit(OpCodes.Conv_Ovf_U1_Un); + break; + case TypeCode.UInt16: + case TypeCode.Char: + Emit(OpCodes.Conv_Ovf_U2_Un); + break; + case TypeCode.UInt32: + Emit(OpCodes.Conv_Ovf_U4_Un); + break; + case TypeCode.UInt64: + Emit(OpCodes.Conv_Ovf_U8_Un); + break; + default: + throw Error.UnhandledConvert(typeTo); + } + } else { + switch (tc) { + case TypeCode.SByte: + Emit(OpCodes.Conv_Ovf_I1); + break; + case TypeCode.Int16: + Emit(OpCodes.Conv_Ovf_I2); + break; + case TypeCode.Int32: + Emit(OpCodes.Conv_Ovf_I4); + break; + case TypeCode.Int64: + Emit(OpCodes.Conv_Ovf_I8); + break; + case TypeCode.Byte: + Emit(OpCodes.Conv_Ovf_U1); + break; + case TypeCode.UInt16: + case TypeCode.Char: + Emit(OpCodes.Conv_Ovf_U2); + break; + case TypeCode.UInt32: + Emit(OpCodes.Conv_Ovf_U4); + break; + case TypeCode.UInt64: + Emit(OpCodes.Conv_Ovf_U8); + break; + default: + throw Error.UnhandledConvert(typeTo); + } + } + } else { + if (isFromUnsigned) { + switch (tc) { + case TypeCode.SByte: + case TypeCode.Byte: + Emit(OpCodes.Conv_U1); + break; + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Char: + Emit(OpCodes.Conv_U2); + break; + case TypeCode.Int32: + case TypeCode.UInt32: + Emit(OpCodes.Conv_U4); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + Emit(OpCodes.Conv_U8); + break; + default: + throw Error.UnhandledConvert(typeTo); + } + } else { + switch (tc) { + case TypeCode.SByte: + case TypeCode.Byte: + Emit(OpCodes.Conv_I1); + break; + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Char: + Emit(OpCodes.Conv_I2); + break; + case TypeCode.Int32: + case TypeCode.UInt32: + Emit(OpCodes.Conv_I4); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + Emit(OpCodes.Conv_I8); + break; + default: + throw Error.UnhandledConvert(typeTo); + } + } + } + } + } + + //CONFORMING + private void EmitNullableToNullableConversion(Type typeFrom, Type typeTo, bool isChecked) { + Debug.Assert(TypeUtils.IsNullableType(typeFrom)); + Debug.Assert(TypeUtils.IsNullableType(typeTo)); + Label labIfNull = default(Label); + Label labEnd = default(Label); + LocalBuilder locFrom = null; + LocalBuilder locTo = null; + locFrom = DeclareLocal(typeFrom); + Emit(OpCodes.Stloc, locFrom); + locTo = DeclareLocal(typeTo); + // test for null + Emit(OpCodes.Ldloca, locFrom); + EmitHasValue(typeFrom); + labIfNull = DefineLabel(); + Emit(OpCodes.Brfalse_S, labIfNull); + Emit(OpCodes.Ldloca, locFrom); + EmitGetValueOrDefault(typeFrom); + Type nnTypeFrom = TypeUtils.GetNonNullableType(typeFrom); + Type nnTypeTo = TypeUtils.GetNonNullableType(typeTo); + EmitConvertToType(nnTypeFrom, nnTypeTo, isChecked); + // construct result type + ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo }); + Emit(OpCodes.Newobj, ci); + Emit(OpCodes.Stloc, locTo); + labEnd = DefineLabel(); + Emit(OpCodes.Br_S, labEnd); + // if null then create a default one + MarkLabel(labIfNull); + Emit(OpCodes.Ldloca, locTo); + Emit(OpCodes.Initobj, typeTo); + MarkLabel(labEnd); + Emit(OpCodes.Ldloc, locTo); + } + + //CONFORMING + private void EmitNonNullableToNullableConversion(Type typeFrom, Type typeTo, bool isChecked) { + Debug.Assert(!TypeUtils.IsNullableType(typeFrom)); + Debug.Assert(TypeUtils.IsNullableType(typeTo)); + LocalBuilder locTo = null; + locTo = DeclareLocal(typeTo); + Type nnTypeTo = TypeUtils.GetNonNullableType(typeTo); + EmitConvertToType(typeFrom, nnTypeTo, isChecked); + ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo }); + Emit(OpCodes.Newobj, ci); + Emit(OpCodes.Stloc, locTo); + Emit(OpCodes.Ldloc, locTo); + } + + //CONFORMING + private void EmitNullableToNonNullableConversion(Type typeFrom, Type typeTo, bool isChecked) { + Debug.Assert(TypeUtils.IsNullableType(typeFrom)); + Debug.Assert(!TypeUtils.IsNullableType(typeTo)); + if (typeTo.IsValueType) + EmitNullableToNonNullableStructConversion(typeFrom, typeTo, isChecked); + else + EmitNullableToReferenceConversion(typeFrom); + } + + //CONFORMING + private void EmitNullableToNonNullableStructConversion(Type typeFrom, Type typeTo, bool isChecked) { + Debug.Assert(TypeUtils.IsNullableType(typeFrom)); + Debug.Assert(!TypeUtils.IsNullableType(typeTo)); + Debug.Assert(typeTo.IsValueType); + LocalBuilder locFrom = null; + locFrom = DeclareLocal(typeFrom); + Emit(OpCodes.Stloc, locFrom); + Emit(OpCodes.Ldloca, locFrom); + EmitGetValue(typeFrom); + Type nnTypeFrom = TypeUtils.GetNonNullableType(typeFrom); + EmitConvertToType(nnTypeFrom, typeTo, isChecked); + } + + //CONFORMING + private void EmitNullableToReferenceConversion(Type typeFrom) { + Debug.Assert(TypeUtils.IsNullableType(typeFrom)); + // We've got a conversion from nullable to Object, ValueType, Enum, etc. Just box it so that + // we get the nullable semantics. + Emit(OpCodes.Box, typeFrom); + } + + //CONFORMING + private void EmitNullableConversion(Type typeFrom, Type typeTo, bool isChecked) { + bool isTypeFromNullable = TypeUtils.IsNullableType(typeFrom); + bool isTypeToNullable = TypeUtils.IsNullableType(typeTo); + Debug.Assert(isTypeFromNullable || isTypeToNullable); + if (isTypeFromNullable && isTypeToNullable) + EmitNullableToNullableConversion(typeFrom, typeTo, isChecked); + else if (isTypeFromNullable) + EmitNullableToNonNullableConversion(typeFrom, typeTo, isChecked); + else + EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked); + } + + //CONFORMING + internal void EmitHasValue(Type nullableType) { + MethodInfo mi = nullableType.GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public); + Debug.Assert(nullableType.IsValueType); + Emit(OpCodes.Call, mi); + } + + //CONFORMING + internal void EmitGetValue(Type nullableType) { + MethodInfo mi = nullableType.GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public); + Debug.Assert(nullableType.IsValueType); + Emit(OpCodes.Call, mi); + } + + //CONFORMING + internal void EmitGetValueOrDefault(Type nullableType) { + MethodInfo mi = nullableType.GetMethod("GetValueOrDefault", System.Type.EmptyTypes); + Debug.Assert(nullableType.IsValueType); + Emit(OpCodes.Call, mi); + } + + #endregion + + #region Arrays + + /// + /// Emits an array of constant values provided in the given list. + /// The array is strongly typed. + /// + public void EmitArray(IList items) { + ContractUtils.RequiresNotNull(items, "items"); + + EmitInt(items.Count); + Emit(OpCodes.Newarr, typeof(T)); + for (int i = 0; i < items.Count; i++) { + Emit(OpCodes.Dup); + EmitInt(i); + EmitSimpleConstant(items[i]); + EmitStoreElement(typeof(T)); + } + } + + /// + /// Emits an array of values of count size. The items are emitted via the callback + /// which is provided with the current item index to emit. + /// + public void EmitArray(Type elementType, int count, EmitArrayHelper emit) { + ContractUtils.RequiresNotNull(elementType, "elementType"); + ContractUtils.RequiresNotNull(emit, "emit"); + ContractUtils.Requires(count >= 0, "count", Strings.CountCannotBeNegative); + + EmitInt(count); + Emit(OpCodes.Newarr, elementType); + for (int i = 0; i < count; i++) { + Emit(OpCodes.Dup); + EmitInt(i); + + emit(i); + + EmitStoreElement(elementType); + } + } + + /// + /// Emits an array construction code. + /// The code assumes that bounds for all dimensions + /// are already emitted. + /// + public void EmitArray(Type arrayType) { + ContractUtils.RequiresNotNull(arrayType, "arrayType"); + ContractUtils.Requires(arrayType.IsArray, "arrayType", Strings.ArrayTypeMustBeArray); + + int rank = arrayType.GetArrayRank(); + if (rank == 1) { + Emit(OpCodes.Newarr, arrayType.GetElementType()); + } else { + Type[] types = new Type[rank]; + for (int i = 0; i < rank; i++) { + types[i] = typeof(int); + } + EmitNew(arrayType, types); + } + } + + #endregion + + #region Support for emitting constants + + public void EmitDecimal(decimal value) { + if (Decimal.Truncate(value) == value) { + if (Int32.MinValue <= value && value <= Int32.MaxValue) { + int intValue = Decimal.ToInt32(value); + EmitInt(intValue); + EmitNew(typeof(Decimal).GetConstructor(new Type[] { typeof(int) })); + } else if (Int64.MinValue <= value && value <= Int64.MaxValue) { + long longValue = Decimal.ToInt64(value); + EmitLong(longValue); + EmitNew(typeof(Decimal).GetConstructor(new Type[] { typeof(long) })); + } else { + EmitDecimalBits(value); + } + } else { + EmitDecimalBits(value); + } + } + + private void EmitDecimalBits(decimal value) { + int[] bits = Decimal.GetBits(value); + EmitInt(bits[0]); + EmitInt(bits[1]); + EmitInt(bits[2]); + EmitBoolean((bits[3] & 0x80000000) != 0); + EmitByte((byte)(bits[3] >> 16)); + EmitNew(typeof(decimal).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(bool), typeof(byte) })); + } + + /// + /// Emits default(T) + /// Semantics match C# compiler behavior + /// + internal void EmitDefault(Type type) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Object: + case TypeCode.DateTime: + if (type.IsValueType) { + // Type.GetTypeCode on an enum returns the underlying + // integer TypeCode, so we won't get here. + Debug.Assert(!type.IsEnum); + + // This is the IL for default(T) if T is a generic type + // parameter, so it should work for any type. It's also + // the standard pattern for structs. + LocalBuilder lb = GetLocal(type); + Emit(OpCodes.Ldloca, lb); + Emit(OpCodes.Initobj, type); + Emit(OpCodes.Ldloc, lb); + FreeLocal(lb); + } else { + Emit(OpCodes.Ldnull); + } + break; + + case TypeCode.Empty: + case TypeCode.String: + case TypeCode.DBNull: + Emit(OpCodes.Ldnull); + break; + + case TypeCode.Boolean: + case TypeCode.Char: + case TypeCode.SByte: + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + Emit(OpCodes.Ldc_I4_0); + break; + + case TypeCode.Int64: + case TypeCode.UInt64: + Emit(OpCodes.Ldc_I4_0); + Emit(OpCodes.Conv_I8); + break; + + case TypeCode.Single: + Emit(OpCodes.Ldc_R4, default(Single)); + break; + + case TypeCode.Double: + Emit(OpCodes.Ldc_R8, default(Double)); + break; + + case TypeCode.Decimal: + Emit(OpCodes.Ldc_I4_0); + Emit(OpCodes.Newobj, typeof(Decimal).GetConstructor(new Type[] { typeof(int) })); + break; + + default: + throw Assert.Unreachable; + } + } + + public void EmitMissingValue(Type type) { + LocalBuilder lb; + + switch (Type.GetTypeCode(type)) { + default: + case TypeCode.Object: + if (type == typeof(object)) { + // parameter of type object receives the actual Missing value + Emit(OpCodes.Ldsfld, typeof(Missing).GetField("Value")); + } else if (!type.IsValueType) { + // reference type + EmitNull(); + } else if (type.IsSealed && !type.IsEnum) { + lb = DeclareLocal(type); + Emit(OpCodes.Ldloca, lb); + Emit(OpCodes.Initobj, type); + Emit(OpCodes.Ldloc, lb); + } else { + throw Error.NoDefaultValue(); + } + break; + + case TypeCode.Empty: + case TypeCode.DBNull: + EmitNull(); + break; + + case TypeCode.Boolean: + case TypeCode.Char: + case TypeCode.SByte: + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + EmitInt(0); + break; + + case TypeCode.Int64: + case TypeCode.UInt64: + EmitLong(0); + break; + + case TypeCode.Single: + EmitSingle(default(Single)); + break; + + case TypeCode.Double: + Emit(OpCodes.Ldc_R8, default(Double)); + break; + + case TypeCode.Decimal: + EmitFieldGet(typeof(Decimal).GetField("Zero")); + break; + + case TypeCode.DateTime: + lb = DeclareLocal(typeof(DateTime)); + Emit(OpCodes.Ldloca, lb); + Emit(OpCodes.Initobj, typeof(DateTime)); + Emit(OpCodes.Ldloc, lb); + break; + + case TypeCode.String: + EmitNull(); + break; + } + } + + #endregion + + #region LocalTemps + + internal LocalBuilder GetLocal(Type type) { + Debug.Assert(type != null); + + LocalBuilder local; + if (_freeLocals.TryDequeue(type, out local)) { + Debug.Assert(type == local.LocalType); + return local; + } + + return DeclareLocal(type); + } + + // TODO: make "local" a ref param and null it out + internal void FreeLocal(LocalBuilder local) { + if (local != null) { + _freeLocals.Enqueue(local.LocalType, local); + } + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/KeyedQueue.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/KeyedQueue.cs new file mode 100644 index 0000000000..805862947e --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/KeyedQueue.cs @@ -0,0 +1,84 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; + +namespace Microsoft.Scripting.Generation { + + /// + /// A simple dictionary of queues, keyed off a particular type + /// This is useful for storing free lists of variables + /// + internal sealed class KeyedQueue { + private readonly Dictionary> _data; + + internal KeyedQueue() { + _data = new Dictionary>(); + } + + internal void Enqueue(K key, V value) { + Queue queue; + if (!_data.TryGetValue(key, out queue)) { + _data.Add(key, queue = new Queue()); + } + queue.Enqueue(value); + } + + internal V Dequeue(K key) { + Queue queue; + if (!_data.TryGetValue(key, out queue)) { + throw Error.QueueEmpty(); + } + V result = queue.Dequeue(); + if (queue.Count == 0) { + _data.Remove(key); + } + return result; + } + + internal bool TryDequeue(K key, out V value) { + Queue queue; + if (_data.TryGetValue(key, out queue) && queue.Count > 0) { + value = queue.Dequeue(); + if (queue.Count == 0) { + _data.Remove(key); + } + return true; + } + value = default(V); + return false; + } + + internal V Peek(K key) { + Queue queue; + if (!_data.TryGetValue(key, out queue)) { + throw Error.QueueEmpty(); + } + return queue.Peek(); + } + + internal int GetCount(K key) { + Queue queue; + if (!_data.TryGetValue(key, out queue)) { + return 0; + } + return queue.Count; + } + + internal void Clear() { + _data.Clear(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/MethodSignatureInfo.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/MethodSignatureInfo.cs new file mode 100644 index 0000000000..eee6b9f587 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/MethodSignatureInfo.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Generation { + /// + /// Helper class to remove methods w/ identical signatures. Used for GetDefaultMembers + /// which returns members from all types in the hierarchy. + /// + public class MethodSignatureInfo { + private ParameterInfo[] _pis; + private bool _isStatic; + + public MethodSignatureInfo(bool isStatic, ParameterInfo[] pis) { + _isStatic = isStatic; + _pis = pis; + } + + [Confined] + public override bool Equals(object obj) { + MethodSignatureInfo args = obj as MethodSignatureInfo; + if (args == null) return false; + + if (args._isStatic != _isStatic || args._pis.Length != _pis.Length) return false; + + for (int i = 0; i < _pis.Length; i++) { + ParameterInfo self = _pis[i]; + ParameterInfo other = args._pis[i]; + + if (self.ParameterType != other.ParameterType) + return false; + } + + return true; + } + + [Confined] + public override int GetHashCode() { + int hash = 6551; + foreach (ParameterInfo pi in _pis) { + hash ^= pi.ParameterType.GetHashCode(); + } + return hash; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/ParameterInfoWrapper.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/ParameterInfoWrapper.cs new file mode 100644 index 0000000000..a9b829988b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/ParameterInfoWrapper.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Generation { + /// + /// This helper type lets us build a fake ParameterInfo object with a specific type and name + /// to pass along to methods that expect ParameterInfos. This is currently found useful + /// for the NewTypeMaker code and may be useful in other situations as well. + /// + public class ParameterInfoWrapper : ParameterInfo { + private Type _type; + private string _name; + + public ParameterInfoWrapper(Type parameterType) { + _type = parameterType; + } + + public ParameterInfoWrapper(Type parameterType, string parameterName) { + _type = parameterType; + _name = parameterName; + } + + public override Type ParameterType { + get { + return _type; + } + } + + public override string Name { + get { + if (_name != null) return _name; + + return base.Name; + } + } + + public override object[] GetCustomAttributes(bool inherit) { + return ArrayUtils.EmptyObjects; + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) { + return ArrayUtils.EmptyObjects; + } + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/SiteLocalStorage.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/SiteLocalStorage.cs new file mode 100644 index 0000000000..6470cb2e1d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/SiteLocalStorage.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Generation { + /// + /// Provides storage which is flowed into a callers site. The same storage object is + /// flowed for multiple calls enabling the callee to cache data that can be re-used + /// across multiple calls. + /// + /// Data is a public field so that this works properly with DynamicSite's as the reference + /// type (and EnsureInitialize) + /// + public class SiteLocalStorage { + public T Data; + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/Snippets.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/Snippets.cs new file mode 100644 index 0000000000..b5a3c726d5 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/Snippets.cs @@ -0,0 +1,231 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using System.Threading; +using Microsoft.Scripting.Utils; +using System.Collections.Generic; + +namespace Microsoft.Scripting.Generation { + + // TODO: This should be a static class + // TODO: simplify initialization logic & state + public sealed class Snippets { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly Snippets Shared = new Snippets(); + + private Snippets() { } + + private int _methodNameIndex; + + private AssemblyGen _assembly; + private AssemblyGen _debugAssembly; + + // TODO: options should be internal + private string _snippetsDirectory; + private bool _saveSnippets; + + /// + /// Directory where snippet assembly will be saved if SaveSnippets is set. + /// + public string SnippetsDirectory { + get { return _snippetsDirectory; } + } + + /// + /// Save snippets to an assembly (see also SnippetsDirectory, SnippetsFileName). + /// + public bool SaveSnippets { + get { return _saveSnippets; } + } + + private AssemblyGen GetAssembly(bool emitSymbols) { + return (emitSymbols) ? + GetOrCreateAssembly(emitSymbols, ref _debugAssembly) : + GetOrCreateAssembly(emitSymbols, ref _assembly); + } + + private AssemblyGen GetOrCreateAssembly(bool emitSymbols, ref AssemblyGen assembly) { + if (assembly == null) { + string suffix = (emitSymbols) ? ".debug" : ""; + suffix += ".scripting"; + Interlocked.CompareExchange(ref assembly, CreateNewAssembly(suffix, emitSymbols), null); + } + return assembly; + } + + private AssemblyGen CreateNewAssembly(string nameSuffix, bool emitSymbols) { + string dir; + + if (_saveSnippets) { + dir = _snippetsDirectory ?? Directory.GetCurrentDirectory(); + } else { + dir = null; + } + + string name = "Snippets" + nameSuffix; + + return new AssemblyGen(new AssemblyName(name), dir, ".dll", emitSymbols); + } + + internal string GetMethodILDumpFile(MethodBase method) { + string fullName = ((method.DeclaringType != null) ? method.DeclaringType.Name + "." : "") + method.Name; + + if (fullName.Length > 100) { + fullName = fullName.Substring(0, 100); + } + + string filename = String.Format("{0}_{1}.il", IOUtils.ToValidFileName(fullName), Interlocked.Increment(ref _methodNameIndex)); + + string dir = _snippetsDirectory ?? Path.Combine(Path.GetTempPath(), "__DLRIL"); + Directory.CreateDirectory(dir); + return Path.Combine(dir, filename); + } + + internal static void SetSaveAssemblies(string directory) { + Shared.ConfigureSaveAssemblies(directory); + } + + private void ConfigureSaveAssemblies(string directory) { + _saveSnippets = true; + _snippetsDirectory = directory; + } + + public static void SaveAndVerifyAssemblies() { + if (!Shared.SaveSnippets) { + return; + } + // Invoke the core Snippets.SaveAssemblies via reflection to get the locations of assemlies + // to be verified. Verify them using PEVerify.exe. + // Do this before verifying outer ring assemblies because they will depend on + // the core ones. + // The order needs to be + // 1) Save inner ring assemblies. + // 2) Save outer ring assemblies. This has to happen before verifying inner ring assemblies because + // inner ring assemblies have dependency on outer ring assemlies via generated IL. + // 3) Verify inner ring assemblies. + // 4) Verify outer ring assemblies. + Assembly core = typeof(System.Linq.Expressions.Expression).Assembly; + Type snippets = core.GetType("System.Linq.Expressions.Compiler.Snippets"); + //The type may not exist. + string[] coreAssemblyLocations = null; + if (snippets != null) { + MethodInfo saveAssemblies = snippets.GetMethod("SaveAssemblies", BindingFlags.NonPublic | BindingFlags.Static); + //The method may not exist. + if (saveAssemblies != null) { + coreAssemblyLocations = (string[])saveAssemblies.Invoke(null, null); + } + } + + string[] outerAssemblyLocations = Shared.SaveAssemblies(); + + if (coreAssemblyLocations != null) { + foreach (var file in coreAssemblyLocations) { + AssemblyGen.PeVerifyAssemblyFile(file); + } + } + //verify outer ring assemblies + foreach (var file in outerAssemblyLocations) { + AssemblyGen.PeVerifyAssemblyFile(file); + } + } + + // Return the assembly locations that need to be verified + private string[] SaveAssemblies() { + if (!SaveSnippets) { + return new string[0]; + } + + List assemlyLocations = new List(); + + // first save all assemblies to disk: + if (_assembly != null) { + string assemblyLocation = _assembly.SaveAssembly(); + if (assemblyLocation != null) { + assemlyLocations.Add(assemblyLocation); + } + _assembly = null; + } + + if (_debugAssembly != null) { + string debugAssemblyLocation = _debugAssembly.SaveAssembly(); + if (debugAssemblyLocation != null) { + assemlyLocations.Add(debugAssemblyLocation); + } + _debugAssembly = null; + } + + return assemlyLocations.ToArray(); + } + + public DynamicILGen CreateDynamicMethod(string methodName, Type returnType, Type[] parameterTypes, bool isDebuggable) { + + ContractUtils.RequiresNotEmpty(methodName, "methodName"); + ContractUtils.RequiresNotNull(returnType, "returnType"); + ContractUtils.RequiresNotNullItems(parameterTypes, "parameterTypes"); + + if (Snippets.Shared.SaveSnippets) { + AssemblyGen assembly = GetAssembly(isDebuggable); + TypeBuilder tb = assembly.DefinePublicType(methodName, typeof(object), false); + MethodBuilder mb = tb.DefineMethod(methodName, CompilerHelpers.PublicStatic, returnType, parameterTypes); + return new DynamicILGenType(tb, mb, mb.GetILGenerator()); + } else { + DynamicMethod dm = RawCreateDynamicMethod(methodName, returnType, parameterTypes); + return new DynamicILGenMethod(dm, dm.GetILGenerator()); + } + } + + public TypeBuilder DefinePublicType(string name, Type parent) { + return GetAssembly(false).DefinePublicType(name, parent, false); + } + + public TypeGen DefineType(string name, Type parent, bool preserveName, bool emitDebugSymbols) { + AssemblyGen ag = GetAssembly(emitDebugSymbols); + TypeBuilder tb = ag.DefinePublicType(name, parent, preserveName); + return new TypeGen(ag, tb); + } + + internal DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes) { + string uniqueName = name + "##" + Interlocked.Increment(ref _methodNameIndex); + return RawCreateDynamicMethod(uniqueName, returnType, parameterTypes); + } + + internal TypeBuilder DefineDelegateType(string name) { + AssemblyGen assembly = GetAssembly(false); + return assembly.DefineType( + name, + typeof(MulticastDelegate), + TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, + false + ); + } + + private static DynamicMethod RawCreateDynamicMethod(string name, Type returnType, Type[] parameterTypes) { +#if SILVERLIGHT // Module-hosted DynamicMethod is not available in SILVERLIGHT + return new DynamicMethod(name, returnType, parameterTypes); +#else + // + // WARNING: we set restrictedSkipVisibility == true (last parameter) + // setting this bit will allow accessing nonpublic members + // for more information see http://msdn.microsoft.com/en-us/library/bb348332.aspx + // + return new DynamicMethod(name, returnType, parameterTypes, true); +#endif + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/StaticLambdaRewriter.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/StaticLambdaRewriter.cs new file mode 100644 index 0000000000..a9854982d1 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/StaticLambdaRewriter.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Actions; + +namespace Microsoft.Scripting.Generation { + /// + /// Rewrites dynamic sites so that the lambda can be emitted as a non-dynamic method. + /// + public abstract class StaticLambdaRewriter : ExpressionVisitor { + protected override Expression VisitDynamic(DynamicExpression node) { + Type siteType = typeof(CallSite<>).MakeGenericType(node.DelegateType); + + // Rewite call site as constant + var siteExpr = VisitConstant(Expression.Constant(DynamicSiteHelpers.MakeSite(node.Binder, siteType))); + + // Rewrite all of the arguments + var args = Visit(node.Arguments); + + var siteVar = Expression.Variable(siteExpr.Type, "$site"); + + // ($site = siteExpr).Target.Invoke($site, *args) + return Expression.Block( + new [] { siteVar }, + Expression.Call( + Expression.Field( + Expression.Assign(siteVar, siteExpr), + siteType.GetField("Target") + ), + node.DelegateType.GetMethod("Invoke"), + ArrayUtils.Insert(siteVar, args) + ) + ); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/ToDiskRewriter.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/ToDiskRewriter.cs new file mode 100644 index 0000000000..9170516baa --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/ToDiskRewriter.cs @@ -0,0 +1,213 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Generation { + + /// + /// Serializes constants and dynamic sites so the code can be saved to disk + /// + internal sealed class ToDiskRewriter : StaticLambdaRewriter { + private static int _uniqueNameId; + private List _constants; + private ParameterExpression _constantPool; + private Dictionary _delegateTypes; + private int _depth; + private readonly TypeGen _typeGen; + + internal ToDiskRewriter(TypeGen typeGen) { + _typeGen = typeGen; + } + + public LambdaExpression RewriteLambda(LambdaExpression lambda) { + return (LambdaExpression)Visit(lambda); + } + + protected override Expression VisitLambda(Expression node) { + _depth++; + try { + + // Visit the lambda first, so we walk the tree and find any + // constants we need to rewrite. + node = (Expression)base.VisitLambda(node); + + if (_depth != 1) { + return node; + } + + var body = node.Body; + + if (_constants != null) { + // Rewrite the constants, they can contain embedded + // CodeContextExpressions + for (int i = 0; i < _constants.Count; i++) { + _constants[i] = Visit(_constants[i]); + } + + // Add the consant pool variable to the top lambda + body = AstUtils.AddScopedVariable( + body, + _constantPool, + Expression.NewArrayInit(typeof(object), _constants) + ); + } + + // Rewrite the lambda + return Expression.Lambda( + body, + node.Name + "$" + Interlocked.Increment(ref _uniqueNameId), + node.Parameters + ); + + } finally { + _depth--; + } + } + + protected override Expression VisitExtension(Expression node) { + if (node.NodeType == ExpressionType.Dynamic) { + // the node was dynamic, the dynamic nodes were removed, + // we now need to rewrite any call sites. + return VisitDynamic((DynamicExpression)node); + } + + return base.VisitExtension(node); + } + + protected override Expression VisitConstant(ConstantExpression node) { + var site = node.Value as CallSite; + if (site != null) { + return RewriteCallSite(site); + } + + var exprSerializable = node.Value as IExpressionSerializable; + if (exprSerializable != null) { + return Visit(exprSerializable.CreateExpression()); + } + + var symbols = node.Value as SymbolId[]; + if (symbols != null) { + return Expression.NewArrayInit( + typeof(SymbolId), + new ReadOnlyCollection( + symbols.Map(s => SymbolConstantExpression.GetExpression(s)) + ) + ); + } + + return base.VisitConstant(node); + } + + // If the DynamicExpression uses a transient (in-memory) type for its + // delegate, we need to replace it with a new delegate type that can be + // saved to disk + protected override Expression VisitDynamic(DynamicExpression node) { + Type delegateType; + if (RewriteDelegate(node.DelegateType, out delegateType)) { + node = Expression.MakeDynamic(delegateType, node.Binder, node.Arguments); + } + return base.VisitDynamic(node); + } + + private bool RewriteDelegate(Type delegateType, out Type newDelegateType) { + if (!ShouldRewriteDelegate(delegateType)) { + newDelegateType = null; + return false; + } + + if (_delegateTypes == null) { + _delegateTypes = new Dictionary(); + } + + // TODO: should caching move to AssemblyGen? + if (!_delegateTypes.TryGetValue(delegateType, out newDelegateType)) { + MethodInfo invoke = delegateType.GetMethod("Invoke"); + + newDelegateType = _typeGen.AssemblyGen.MakeDelegateType( + delegateType.Name, + invoke.GetParameters().Map(p => p.ParameterType), + invoke.ReturnType + ); + + _delegateTypes[delegateType] = newDelegateType; + } + + return true; + } + + private bool ShouldRewriteDelegate(Type delegateType) { + // We need to replace a transient delegateType with one stored in + // the assembly we're saving to disk. + // + // One complication: + // SaveAssemblies mode prevents us from detecting the module as + // transient. If that option is turned on, always replace delegates + // that live in another AssemblyBuilder + + var module = delegateType.Module as ModuleBuilder; + if (module == null) { + return false; + } + + if (module.IsTransient()) { + return true; + } + + if (Snippets.Shared.SaveSnippets && module.Assembly != _typeGen.AssemblyGen.AssemblyBuilder) { + return true; + } + + return false; + } + + private Expression RewriteCallSite(CallSite site) { + IExpressionSerializable serializer = site.Binder as IExpressionSerializable; + if (serializer == null) { + throw Error.GenNonSerializableBinder(); + } + + // add the initialization code that we'll generate later into the outermost + // lambda and then return an index into the array we'll be creating. + if (_constantPool == null) { + _constantPool = Expression.Variable(typeof(object[]), "$constantPool"); + _constants = new List(); + } + + Type siteType = site.GetType(); + + _constants.Add(Expression.Call(siteType.GetMethod("Create"), serializer.CreateExpression())); + + // rewrite the node... + return Visit( + AstUtils.Convert( + Expression.ArrayAccess(_constantPool, Expression.Constant(_constants.Count - 1)), + siteType + ) + ); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Generation/TypeGen.cs b/merlin/main/runtime/Microsoft.Scripting/Generation/TypeGen.cs new file mode 100644 index 0000000000..10af5ab9ea --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Generation/TypeGen.cs @@ -0,0 +1,107 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Reflection.Emit; +using Microsoft.Contracts; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Generation { + public sealed class TypeGen { + private readonly AssemblyGen _myAssembly; + private readonly TypeBuilder _myType; + + private ILGen _initGen; // The IL generator for the .cctor() + + /// + /// Gets the Compiler associated with the Type Initializer (cctor) creating it if necessary. + /// + public ILGen TypeInitializer { + get { + if (_initGen == null) { + _initGen = new ILGen(_myType.DefineTypeInitializer().GetILGenerator()); + } + return _initGen; + } + } + + internal AssemblyGen AssemblyGen { + get { return _myAssembly; } + } + + public TypeBuilder TypeBuilder { + get { return _myType; } + } + + public TypeGen(AssemblyGen myAssembly, TypeBuilder myType) { + Assert.NotNull(myAssembly, myType); + + _myAssembly = myAssembly; + _myType = myType; + } + + [Confined] + public override string ToString() { + return _myType.ToString(); + } + + public Type FinishType() { + if (_initGen != null) _initGen.Emit(OpCodes.Ret); + + Type ret = _myType.CreateType(); + + //Console.WriteLine("finished: " + ret.FullName); + return ret; + } + + public FieldBuilder AddStaticField(Type fieldType, string name) { + return _myType.DefineField(name, fieldType, FieldAttributes.Public | FieldAttributes.Static); + } + + public FieldBuilder AddStaticField(Type fieldType, FieldAttributes attributes, string name) { + return _myType.DefineField(name, fieldType, attributes | FieldAttributes.Static); + } + + public ILGen DefineExplicitInterfaceImplementation(MethodInfo baseMethod) { + ContractUtils.RequiresNotNull(baseMethod, "baseMethod"); + + MethodAttributes attrs = baseMethod.Attributes & ~(MethodAttributes.Abstract | MethodAttributes.Public); + attrs |= MethodAttributes.NewSlot | MethodAttributes.Final; + + Type[] baseSignature = baseMethod.GetParameters().Map(p => p.ParameterType); + MethodBuilder mb = _myType.DefineMethod( + baseMethod.DeclaringType.Name + "." + baseMethod.Name, + attrs, + baseMethod.ReturnType, + baseSignature); + + TypeBuilder.DefineMethodOverride(mb, baseMethod); + return new ILGen(mb.GetILGenerator()); + } + + private const MethodAttributes MethodAttributesToEraseInOveride = + MethodAttributes.Abstract | MethodAttributes.ReservedMask; + + public ILGen DefineMethodOverride(MethodInfo baseMethod) { + MethodAttributes finalAttrs = baseMethod.Attributes & ~MethodAttributesToEraseInOveride; + Type[] baseSignature = baseMethod.GetParameters().Map(p => p.ParameterType); + MethodBuilder mb = _myType.DefineMethod(baseMethod.Name, finalAttrs, baseMethod.ReturnType, baseSignature); + + TypeBuilder.DefineMethodOverride(mb, baseMethod); + return new ILGen(mb.GetILGenerator()); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/GlobalSuppressions.cs b/merlin/main/runtime/Microsoft.Scripting/GlobalSuppressions.cs new file mode 100644 index 0000000000..4afa6eec3b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/GlobalSuppressions.cs @@ -0,0 +1,16 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Scripting.Hosting.Providers")] diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/CompiledCode.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/CompiledCode.cs new file mode 100644 index 0000000000..e0716b175e --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/CompiledCode.cs @@ -0,0 +1,113 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.Remoting; +using System.Dynamic; +using Microsoft.Scripting.Utils; +using System.Security.Permissions; +using System.Threading; + +namespace Microsoft.Scripting.Hosting { + + /// + /// Hosting API counterpart for . + /// + public sealed class CompiledCode +#if !SILVERLIGHT + : MarshalByRefObject +#endif + { + private readonly ScriptEngine _engine; + private readonly ScriptCode _code; + + private ScriptScope _defaultScope; + + internal ScriptCode ScriptCode { get { return _code; } } + + internal CompiledCode(ScriptEngine engine, ScriptCode code) { + Assert.NotNull(engine); + Assert.NotNull(code); + + _engine = engine; + _code = code; + } + + /// + /// Engine that compiled this code. + /// + public ScriptEngine Engine { + get { return _engine; } + } + + /// + /// Default scope for this code. + /// + public ScriptScope DefaultScope { + get { + if (_defaultScope == null) { + Interlocked.CompareExchange(ref _defaultScope, new ScriptScope(_engine, _code.CreateScope()), null); + } + return _defaultScope; + } + } + + /// + /// Executes code in a default scope. + /// + public object Execute() { + return _code.Run(DefaultScope.Scope); + } + + /// + /// Execute code within a given scope and returns the result. + /// + public object Execute(ScriptScope scope) { + ContractUtils.RequiresNotNull(scope, "scope"); + return _code.Run(scope.Scope); + } + + /// + /// Executes code in in a default scope and converts to a given type. + /// + public T Execute() { + return _engine.Operations.ConvertTo(Execute()); + } + + /// + /// Execute code within a given scope and converts result to a given type. + /// + public T Execute(ScriptScope scope) { + return _engine.Operations.ConvertTo(Execute(scope)); + } + + +#if !SILVERLIGHT + public ObjectHandle ExecuteAndWrap() { + return new ObjectHandle(Execute()); + } + + public ObjectHandle ExecuteAndWrap(ScriptScope scope) { + return new ObjectHandle(Execute(scope)); + } + + // TODO: Figure out what is the right lifetime + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] + public override object InitializeLifetimeService() { + return null; + } +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/LanguageElement.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/LanguageElement.cs new file mode 100644 index 0000000000..e1ad49e507 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/LanguageElement.cs @@ -0,0 +1,79 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Configuration; +using System; +using System.Collections.Generic; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting.Configuration { + // + // + public class LanguageElement : ConfigurationElement { + private const string _Names = "names"; + private const string _Extensions = "extensions"; + private const string _Type = "type"; + private const string _DisplayName = "displayName"; + + private static ConfigurationPropertyCollection _Properties = new ConfigurationPropertyCollection { + new ConfigurationProperty(_Names, typeof(string), null), + new ConfigurationProperty(_Extensions, typeof(string), null), + new ConfigurationProperty(_Type, typeof(string), null, ConfigurationPropertyOptions.IsRequired), + new ConfigurationProperty(_DisplayName, typeof(string), null) + }; + + protected override ConfigurationPropertyCollection Properties { + get { return _Properties; } + } + + public string Names { + get { return (string)this[_Names]; } + set { this[_Names] = value; } + } + + public string Extensions { + get { return (string)this[_Extensions]; } + set { this[_Extensions] = value; } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + public string Type { + get { return (string)this[_Type]; } + set { this[_Type] = value; } + } + + public string DisplayName { + get { return (string)this[_DisplayName]; } + set { this[_DisplayName] = value; } + } + + public string[] GetNamesArray() { + return Split(Names); + } + + public string[] GetExtensionsArray() { + return Split(Extensions); + } + + private static string[] Split(string str) { + return (str != null) ? str.Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries) : ArrayUtils.EmptyStrings; + } + } +} + +#endif \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/LanguageElementCollection.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/LanguageElementCollection.cs new file mode 100644 index 0000000000..fbf45f52c5 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/LanguageElementCollection.cs @@ -0,0 +1,48 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Configuration; +using System; +using System.Collections.Generic; + +namespace Microsoft.Scripting.Hosting.Configuration { + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface")] + public class LanguageElementCollection : ConfigurationElementCollection { + public override ConfigurationElementCollectionType CollectionType { + get { return ConfigurationElementCollectionType.BasicMap; } + } + + protected override bool ThrowOnDuplicate { + get { return false; } + } + + protected override ConfigurationElement CreateNewElement() { + return new LanguageElement(); + } + + protected override string ElementName { + get { return "language"; } + } + + protected override object GetElementKey(ConfigurationElement element) { + return ((LanguageElement)element).Type; + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/OptionElement.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/OptionElement.cs new file mode 100644 index 0000000000..14d39f825d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/OptionElement.cs @@ -0,0 +1,91 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Configuration; +using System; +using System.Collections.Generic; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Hosting.Configuration { + + public class OptionElement : ConfigurationElement { + private const string _Option = "option"; + private const string _Value = "value"; + private const string _Language = "language"; + + private static ConfigurationPropertyCollection _Properties = new ConfigurationPropertyCollection() { + new ConfigurationProperty(_Option, typeof(string), String.Empty, ConfigurationPropertyOptions.IsRequired | ConfigurationPropertyOptions.IsKey), + new ConfigurationProperty(_Value, typeof(string), String.Empty, ConfigurationPropertyOptions.IsRequired), + new ConfigurationProperty(_Language, typeof(string), String.Empty, ConfigurationPropertyOptions.IsKey), + }; + + protected override ConfigurationPropertyCollection Properties { + get { return _Properties; } + } + + public string Name { + get { return (string)this[_Option]; } + set { this[_Option] = value; } + } + + public string Value { + get { return (string)this[_Value]; } + set { this[_Value] = value; } + } + + public string Language { + get { return (string)this[_Language]; } + set { this[_Language] = value; } + } + + internal object GetKey() { + return new Key(Name, Language); + } + + internal sealed class Key : IEquatable { + private readonly string _option; + private readonly string _language; + + public string Option { get { return _option; } } + public string Language { get { return _language; } } + + public Key(string option, string language) { + _option = option; + _language = language; + } + + public override bool Equals(object obj) { + return Equals(obj as Key); + } + + public bool Equals(Key other) { + return other != null && + DlrConfiguration.OptionNameComparer.Equals(_option, other._option) && + DlrConfiguration.LanguageNameComparer.Equals(_language, other._language); + } + + public override int GetHashCode() { + return _option.GetHashCode() ^ (_language ?? String.Empty).GetHashCode(); + } + + public override string ToString() { + return (String.IsNullOrEmpty(_language) ? String.Empty : _language + ":") + _option; + } + } + } +} +#endif \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/OptionElementCollection.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/OptionElementCollection.cs new file mode 100644 index 0000000000..f3b827a0f7 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/OptionElementCollection.cs @@ -0,0 +1,48 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Configuration; +using System; +using System.Collections.Generic; + +namespace Microsoft.Scripting.Hosting.Configuration { + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface")] + public class OptionElementCollection : ConfigurationElementCollection { + public OptionElementCollection() { + AddElementName = "set"; + } + + public override ConfigurationElementCollectionType CollectionType { + get { return ConfigurationElementCollectionType.AddRemoveClearMap; } + } + + protected override ConfigurationElement CreateNewElement() { + return new OptionElement(); + } + + protected override bool ThrowOnDuplicate { + get { return false; } + } + + protected override object GetElementKey(ConfigurationElement element) { + return ((OptionElement)element).GetKey(); + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/Section.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/Section.cs new file mode 100644 index 0000000000..518748d229 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Configuration/Section.cs @@ -0,0 +1,182 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Xml; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting.Configuration { + + // + // + //
    + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + public class Section : ConfigurationSection { + public static readonly string SectionName = "microsoft.scripting"; + + private const string _DebugMode = "debugMode"; + private const string _PrivateBinding = "privateBinding"; + private const string _Languages = "languages"; + private const string _Options = "options"; + + private static ConfigurationPropertyCollection _Properties = new ConfigurationPropertyCollection() { + new ConfigurationProperty(_DebugMode, typeof(bool?), null), + new ConfigurationProperty(_PrivateBinding, typeof(bool?), null), + new ConfigurationProperty(_Languages, typeof(LanguageElementCollection), null, ConfigurationPropertyOptions.IsDefaultCollection), + new ConfigurationProperty(_Options, typeof(OptionElementCollection), null), + }; + + protected override ConfigurationPropertyCollection Properties { + get { return _Properties; } + } + + public bool? DebugMode { + get { return (bool?)base[_DebugMode]; } + set { base[_DebugMode] = value; } + } + + public bool? PrivateBinding { + get { return (bool?)base[_PrivateBinding]; } + set { base[_PrivateBinding] = value; } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public IEnumerable GetLanguages() { + var languages = this[_Languages] as LanguageElementCollection; + if (languages == null) { + yield break; + } + + foreach (var languageConfig in languages) { + yield return (LanguageElement)languageConfig; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public IEnumerable GetOptions() { + var options = this[_Options] as OptionElementCollection; + if (options == null) { + yield break; + } + + foreach (var option in options) { + yield return (OptionElement)option; + } + } + + private static Section LoadFromFile(Stream configFileStream) { + var result = new Section(); + using (var reader = XmlReader.Create(configFileStream)) { + if (reader.ReadToDescendant("configuration") && reader.ReadToDescendant(SectionName)) { + result.DeserializeElement(reader, false); + } else { + return null; + } + } + return result; + } + + internal static void LoadRuntimeSetup(ScriptRuntimeSetup setup, Stream configFileStream) { + Section config; + if (configFileStream != null) { + config = LoadFromFile(configFileStream); + } else { + config = System.Configuration.ConfigurationManager.GetSection(Section.SectionName) as Section; + } + + if (config == null) { + return; + } + + if (config.DebugMode.HasValue) { + setup.DebugMode = config.DebugMode.Value; + } + if (config.PrivateBinding.HasValue) { + setup.PrivateBinding = config.PrivateBinding.Value; + } + + foreach (var languageConfig in config.GetLanguages()) { + var provider = languageConfig.Type; + var names = languageConfig.GetNamesArray(); + var extensions = languageConfig.GetExtensionsArray(); + var displayName = languageConfig.DisplayName ?? ((names.Length > 0) ? names[0] : languageConfig.Type); + + // Honor the latest-wins behavior of the tag for options that were already included in the setup object; + // Keep the options though. + bool found = false; + foreach (var language in setup.LanguageSetups) { + if (language.TypeName == provider) { + language.Names.Clear(); + foreach (string name in names) { + language.Names.Add(name); + } + language.FileExtensions.Clear(); + foreach (string extension in extensions) { + language.FileExtensions.Add(extension); + } + language.DisplayName = displayName; + found = true; + break; + } + } + if (!found) { + setup.LanguageSetups.Add(new LanguageSetup(provider, displayName, names, extensions)); + } + } + + foreach (var option in config.GetOptions()) { + if (String.IsNullOrEmpty(option.Language)) { + // common option: + setup.Options[option.Name] = option.Value; + } else { + // language specific option: + bool found = false; + foreach (var language in setup.LanguageSetups) { + if (language.Names.Any(s => DlrConfiguration.LanguageNameComparer.Equals(s, option.Language))) { + language.Options[option.Name] = option.Value; + found = true; + break; + } + } + if (!found) { + throw new ConfigurationErrorsException(string.Format("Unknown language name: '{0}'", option.Language)); + } + } + } + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ErrorListener.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ErrorListener.cs new file mode 100644 index 0000000000..97475899a4 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ErrorListener.cs @@ -0,0 +1,48 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using System.Security.Permissions; + +namespace Microsoft.Scripting.Hosting { + + /// + /// The host can use this class to track for errors reported during script parsing and compilation. + /// Hosting API counterpart for . + /// + public abstract class ErrorListener +#if !SILVERLIGHT + : MarshalByRefObject +#endif + { + protected ErrorListener() { + } + + internal void ReportError(ScriptSource source, string message, SourceSpan span, int errorCode, Severity severity) { + ErrorReported(source, message, span, errorCode, severity); + } + + public abstract void ErrorReported(ScriptSource source, string message, SourceSpan span, int errorCode, Severity severity); + +#if !SILVERLIGHT + // TODO: Figure out what is the right lifetime + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] + public override object InitializeLifetimeService() { + return null; + } +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ErrorListenerProxy.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ErrorListenerProxy.cs new file mode 100644 index 0000000000..8573987dda --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ErrorListenerProxy.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic; + +namespace Microsoft.Scripting.Hosting { + + /// + /// Bridges ErrorSink and ErrorListener. + /// Errors reported by language compilers to ErrorSink are forwarded to the ErrorListener provided by the host. + /// + /// + /// This proxy is created in the scenario when the compiler is processing a single SourceUnit. + /// Therefore it could maintain one to one mapping from SourceUnit to ScriptSource. + /// In a case, which shouldn't happen, that the compiler reports an error in a different SourceUnit we just create + /// a new instance of the ScriptSource each time. + /// + /// TODO: Consider compilation of multiple source units and creating a hashtable mapping SourceUnits to ScriptSources + /// within the context of compilation unit. + /// + internal sealed class ErrorListenerProxySink : ErrorSink { + private readonly ErrorListener _listener; + private readonly ScriptSource _source; + + public ErrorListenerProxySink(ScriptSource source, ErrorListener listener) { + _listener = listener; + _source = source; + } + + public override void Add(SourceUnit sourceUnit, string message, SourceSpan span, int errorCode, Severity severity) { + if (_listener != null) { + + ScriptSource scriptSource; + if (sourceUnit != _source.SourceUnit) { + scriptSource = new ScriptSource(_source.Engine.Runtime.GetEngine(sourceUnit.LanguageContext), sourceUnit); + } else { + scriptSource = _source; + } + + _listener.ErrorReported(scriptSource, message, span, errorCode, severity); + } else { + throw new SyntaxErrorException(message, sourceUnit, span, errorCode, severity); + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ErrorSinkProxyListener.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ErrorSinkProxyListener.cs new file mode 100644 index 0000000000..541521969c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ErrorSinkProxyListener.cs @@ -0,0 +1,43 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Hosting { + /// + /// Bridges ErrorListener and ErrorSink. It provides the reverse functionality as ErrorSinkProxyListener + /// + internal sealed class ErrorSinkProxyListener : ErrorListener { + private ErrorSink _errorSink; + + internal ErrorSinkProxyListener(ErrorSink errorSink) { + _errorSink = errorSink; + } + + public override void ErrorReported(ScriptSource source, string message, SourceSpan span, int errorCode, Severity severity) { + // Note that we cannot use "source.SourceUnit" since "source" may be a proxy object, and we will not be able to marshall + // "source.SourceUnit" to the current AppDomain + + string code = null; + string line = null; + try { + code = source.GetCode(); + line = source.GetCodeLine(span.Start.Line); + } catch (System.IO.IOException) { + // could not get source code. + } + + _errorSink.Add(message, source.Path, code, line, span, errorCode, severity); + } + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ExceptionOperations.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ExceptionOperations.cs new file mode 100644 index 0000000000..48b0e24e84 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ExceptionOperations.cs @@ -0,0 +1,45 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Hosting { + public sealed class ExceptionOperations +#if !SILVERLIGHT + : MarshalByRefObject +#endif + { + private readonly LanguageContext _context; + + internal ExceptionOperations(LanguageContext context) { + _context = context; + } + + public string FormatException(Exception exception) { + return _context.FormatException(exception); + } + + public void GetExceptionMessage(Exception exception, out string message, out string errorTypeName) { + _context.GetExceptionMessage(exception, out message, out errorTypeName); + } + + public bool HandleException(Exception exception) { + ContractUtils.RequiresNotNull(exception, "exception"); + return false; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/LanguageSetup.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/LanguageSetup.cs new file mode 100644 index 0000000000..29b1befde9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/LanguageSetup.cs @@ -0,0 +1,181 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Threading; + +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting { + /// + /// Stores information needed to setup a language + /// + [Serializable] + public sealed class LanguageSetup { + private string _typeName; + private string _displayName; + private IList _names; + private IList _fileExtensions; + private IDictionary _options; + private bool _frozen; + private bool? _interpretedMode, _exceptionDetail, _perfStats; + + /// + /// Creates a new LanguageSetup + /// + /// assembly qualified type name of the language + /// provider + public LanguageSetup(string typeName) + : this(typeName, "", ArrayUtils.EmptyStrings, ArrayUtils.EmptyStrings) { + } + + /// + /// Creates a new LanguageSetup with the provided options + /// TODO: remove this overload? + /// + public LanguageSetup(string typeName, string displayName) + : this(typeName, displayName, ArrayUtils.EmptyStrings, ArrayUtils.EmptyStrings) { + } + + /// + /// Creates a new LanguageSetup with the provided options + /// + public LanguageSetup(string typeName, string displayName, IEnumerable names, IEnumerable fileExtensions) { + ContractUtils.RequiresNotEmpty(typeName, "typeName"); + ContractUtils.RequiresNotNull(displayName, "displayName"); + ContractUtils.RequiresNotNull(names, "names"); + ContractUtils.RequiresNotNull(fileExtensions, "fileExtensions"); + + _typeName = typeName; + _displayName = displayName; + _names = new List(names); + _fileExtensions = new List(fileExtensions); + _options = new Dictionary(); + } + + /// + /// Gets an option as a strongly typed value. + /// + public T GetOption(string name, T defaultValue) { + object value; + if (_options != null && _options.TryGetValue(name, out value)) { + if (value is T) { + return (T)value; + } + return (T)Convert.ChangeType(value, typeof(T), Thread.CurrentThread.CurrentCulture); + } + return defaultValue; + } + + /// + /// The assembly qualified type name of the language provider + /// + public string TypeName { + get { return _typeName; } + set { + ContractUtils.RequiresNotEmpty(value, "value"); + CheckFrozen(); + _typeName = value; + } + } + + /// + /// Display name of the language. If empty, it will be set to the first + /// name in the Names list. + /// + public string DisplayName { + get { return _displayName; } + set { + ContractUtils.RequiresNotNull(value, "value"); + CheckFrozen(); + _displayName = value; + } + } + + /// + /// Case-insensitive language names. + /// + public IList Names { + get { return _names; } + } + + /// + /// Case-insensitive file extension, optionally starts with a dot. + /// + public IList FileExtensions { + get { return _fileExtensions; } + } + + /// + /// Option names are case-sensitive. + /// + public IDictionary Options { + get { return _options; } + } + + public bool InterpretedMode { + get { return GetCachedOption("InterpretedMode", ref _interpretedMode); } + set { + CheckFrozen(); + Options["InterpretedMode"] = value; + } + } + + public bool ExceptionDetail { + get { return GetCachedOption("ExceptionDetail", ref _exceptionDetail); } + set { + CheckFrozen(); + Options["ExceptionDetail"] = value; + } + } + + public bool PerfStats { + get { return GetCachedOption("PerfStats", ref _perfStats); } + set { + CheckFrozen(); + Options["PerfStats"] = value; + } + } + + private bool GetCachedOption(string name, ref bool? storage) { + if (storage.HasValue) { + return storage.Value; + } + + if (_frozen) { + storage = GetOption(name, false); + return storage.Value; + } + + return GetOption(name, false); + } + + internal void Freeze() { + _frozen = true; + + _names = new ReadOnlyCollection(ArrayUtils.MakeArray(_names)); + _fileExtensions = new ReadOnlyCollection(ArrayUtils.MakeArray(_fileExtensions)); + _options = new ReadOnlyDictionary(new Dictionary(_options)); + } + + private void CheckFrozen() { + if (_frozen) { + throw new InvalidOperationException("Cannot modify LanguageSetup after it has been used to create a ScriptRuntime"); + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ObjectOperations.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ObjectOperations.cs new file mode 100644 index 0000000000..7239dae19d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ObjectOperations.cs @@ -0,0 +1,890 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.Remoting; +using System.Security.Permissions; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Linq.Expressions; +using System.Dynamic.Binders; + +namespace Microsoft.Scripting.Hosting { + + /// + /// ObjectOperations provide a large catalogue of object operations such as member access, conversions, + /// indexing, and things like addition. There are several introspection and tool support services available + /// for more advanced hosts. + /// + /// You get ObjectOperation instances from ScriptEngine, and they are bound to their engines for the semantics + /// of the operations. There is a default instance of ObjectOperations you can share across all uses of the + /// engine. However, very advanced hosts can create new instances. + /// + public sealed class ObjectOperations +#if !SILVERLIGHT + : MarshalByRefObject +#endif + { + + private readonly DynamicOperations _ops; + private readonly ScriptEngine _engine; + + // friend class: DynamicOperations + internal ObjectOperations(DynamicOperations ops, ScriptEngine engine) { + Assert.NotNull(ops); + Assert.NotNull(engine); + _ops = ops; + _engine = engine; + } + + public ScriptEngine Engine { + get { return _engine; } + } + +#pragma warning disable 618 + + #region Local Operations + + /// + /// Returns true if the object can be called, false if it cannot. + /// + /// Even if an object is callable Call may still fail if an incorrect number of arguments or type of arguments are provided. + /// + public bool IsCallable(object obj) { + return _ops.DoOperation(StandardOperators.IsCallable, obj); + } + + /// + /// Calls the provided object with the given parameters and returns the result. + /// + /// The prefered way of calling objects is to convert the object to a strongly typed delegate + /// using the ConvertTo methods and then invoking that delegate. + /// + public object Call(object obj, params object[] parameters) { + return _ops.Invoke(obj, parameters); + } + + /// + /// Invokes a member on the provided object with the given parameters and returns the result. + /// + public object InvokeMember(object obj, string memberName, params object[] parameters) { + return _ops.InvokeMember(obj, memberName, parameters); + } + + /// + /// Creates a new instance from the provided object using the given parameters, and returns the result. + /// + public object CreateInstance(object obj, params object[] parameters) { + return _ops.CreateInstance(obj, parameters); + } + + /// + /// Gets the member name from the object obj. Throws an exception if the member does not exist or is write-only. + /// + public object GetMember(object obj, string name) { + return _ops.GetMember(obj, name); + } + + /// + /// Gets the member name from the object obj and converts it to the type T. Throws an exception if the + /// member does not exist, is write-only, or cannot be converted. + /// + public T GetMember(object obj, string name) { + return _ops.GetMember(obj, name); + } + + /// + /// Gets the member name from the object obj. Returns true if the member is successfully retrieved and + /// stores the value in the value out param. + /// + public bool TryGetMember(object obj, string name, out object value) { + return _ops.TryGetMember(obj, name, out value); + } + + /// + /// Returns true if the object has a member named name, false if the member does not exist. + /// + public bool ContainsMember(object obj, string name) { + return _ops.ContainsMember(obj, name); + } + + /// + /// Removes the member name from the object obj. Returns true if the member was successfully removed + /// or false if the member does not exist. + /// + public bool RemoveMember(object obj, string name) { + return _ops.RemoveMember(obj, name); + } + + /// + /// Sets the member name on object obj to value. + /// + public void SetMember(object obj, string name, object value) { + _ops.SetMember(obj, name, value); + } + + /// + /// Sets the member name on object obj to value. This overload can be used to avoid + /// boxing and casting of strongly typed members. + /// + public void SetMember(object obj, string name, T value) { + _ops.SetMember(obj, name, value); + } + + /// + /// Gets the member name from the object obj. Throws an exception if the member does not exist or is write-only. + /// + public object GetMember(object obj, string name, bool ignoreCase) { + return _ops.GetMember(obj, name, ignoreCase); + } + + /// + /// Gets the member name from the object obj and converts it to the type T. Throws an exception if the + /// member does not exist, is write-only, or cannot be converted. + /// + public T GetMember(object obj, string name, bool ignoreCase) { + return _ops.GetMember(obj, name, ignoreCase); + } + + /// + /// Gets the member name from the object obj. Returns true if the member is successfully retrieved and + /// stores the value in the value out param. + /// + public bool TryGetMember(object obj, string name, bool ignoreCase, out object value) { + return _ops.TryGetMember(obj, name, ignoreCase, out value); + } + + /// + /// Returns true if the object has a member named name, false if the member does not exist. + /// + public bool ContainsMember(object obj, string name, bool ignoreCase) { + return _ops.ContainsMember(obj, name, ignoreCase); + } + + /// + /// Removes the member name from the object obj. Returns true if the member was successfully removed + /// or false if the member does not exist. + /// + public bool RemoveMember(object obj, string name, bool ignoreCase) { + return _ops.RemoveMember(obj, name, ignoreCase); + } + + /// + /// Sets the member name on object obj to value. + /// + public void SetMember(object obj, string name, object value, bool ignoreCase) { + _ops.SetMember(obj, name, value, ignoreCase); + } + + /// + /// Sets the member name on object obj to value. This overload can be used to avoid + /// boxing and casting of strongly typed members. + /// + public void SetMember(object obj, string name, T value, bool ignoreCase) { + _ops.SetMember(obj, name, value, ignoreCase); + } + + /// + /// Convers the object obj to the type T. + /// + public T ConvertTo(object obj) { + return _ops.ConvertTo(obj); + } + + /// + /// Converts the object obj to the type type. + /// + public object ConvertTo(object obj, Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + return _ops.ConvertTo(obj, type); + } + + /// + /// Converts the object obj to the type T. Returns true if the value can be converted, false if it cannot. + /// + public bool TryConvertTo(object obj, out T result) { + return _ops.TryConvertTo(obj, out result); + } + + /// + /// Converts the object obj to the type type. Returns true if the value can be converted, false if it cannot. + /// + public bool TryConvertTo(object obj, Type type, out object result) { + return _ops.TryConvertTo(obj, type, out result); + } + + /// + /// Converts the object obj to the type T including explicit conversions which may lose information. + /// + public T ExplicitConvertTo(object obj) { + return _ops.ExplicitConvertTo(obj); + } + + /// + /// Converts the object obj to the type type including explicit conversions which may lose information. + /// + public object ExplicitConvertTo(object obj, Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + return _ops.ExplicitConvertTo(obj, type); + } + + /// + /// Converts the object obj to the type T including explicit conversions which may lose information. + /// + /// Returns true if the value can be converted, false if it cannot. + /// + public bool TryExplicitConvertTo(object obj, out T result) { + return _ops.TryExplicitConvertTo(obj, out result); + } + + /// + /// Converts the object obj to the type type including explicit conversions which may lose information. + /// + /// Returns true if the value can be converted, false if it cannot. + /// + public bool TryExplicitConvertTo(object obj, Type type, out object result) { + return _ops.TryExplicitConvertTo(obj, type, out result); + } + + + /// + /// Performs a generic unary operation on the specified target and returns the result. + /// + public object DoOperation(ExpressionType operation, object target) { + return _ops.DoOperation(operation, target); + } + + /// + /// Performs a generic unary operation on the strongly typed target and returns the value as the specified type + /// + public TResult DoOperation(ExpressionType operation, TTarget target) { + return _ops.DoOperation(operation, target); + } + + /// + /// Performs the generic binary operation on the specified targets and returns the result. + /// + public object DoOperation(ExpressionType operation, object target, object other) { + return _ops.DoOperation(operation, target, other); + } + + /// + /// Peforms the generic binary operation on the specified strongly typed targets and returns + /// the strongly typed result. + /// + public TResult DoOperation(ExpressionType operation, TTarget target, TOther other) { + return _ops.DoOperation(operation, target, other); + } + + /// + /// Performs a generic unary operation on the specified target and returns the result. + /// + public object DoOperation(Operators op, object target) { + return _ops.DoOperation(StandardOperators.FromOperator(op), target); + } + + /// + /// Performs a generic unary operation on the strongly typed target and returns the value as the specified type + /// + public TResult DoOperation(Operators op, TTarget target) { + return _ops.DoOperation(StandardOperators.FromOperator(op), target); + } + + /// + /// Performs the generic binary operation on the specified targets and returns the result. + /// + public object DoOperation(Operators op, object target, object other) { + return _ops.DoOperation(op, target, other); + } + + /// + /// Peforms the generic binary operation on the specified strongly typed targets and returns + /// the strongly typed result. + /// + public TResult DoOperation(Operators op, TTarget target, TOther other) { + return _ops.DoOperation(StandardOperators.FromOperator(op), target, other); + } + + /// + /// Performs addition on the specified targets and returns the result. Throws an exception + /// if the operation cannot be performed. + /// + public object Add(object self, object other) { + return _ops.DoOperation(Operators.Add, self, other); + } + + /// + /// Performs subtraction on the specified targets and returns the result. Throws an exception + /// if the operation cannot be performed. + /// + public object Subtract(object self, object other) { + return _ops.DoOperation(Operators.Subtract, self, other); + } + + /// + /// Raises the first object to the power of the second object. Throws an exception + /// if the operation cannot be performed. + /// + public object Power(object self, object other) { + return _ops.DoOperation(Operators.Power, self, other); + } + + /// + /// Multiplies the two objects. Throws an exception + /// if the operation cannot be performed. + /// + public object Multiply(object self, object other) { + return _ops.DoOperation(Operators.Multiply, self, other); + } + + /// + /// Divides the first object by the second object. Throws an exception + /// if the operation cannot be performed. + /// + public object Divide(object self, object other) { + return _ops.DoOperation(Operators.Divide, self, other); + } + + /// + /// Performs modulus of the 1st object by the second object. Throws an exception + /// if the operation cannot be performed. + /// + public object Modulus(object self, object other) { + return _ops.DoOperation(Operators.Mod, self, other); + } + + /// + /// Shifts the left object left by the right object. Throws an exception if the + /// operation cannot be performed. + /// + public object LeftShift(object self, object other) { + return _ops.DoOperation(Operators.LeftShift, self, other); + } + + /// + /// Shifts the left object right by the right object. Throws an exception if the + /// operation cannot be performed. + /// + public object RightShift(object self, object other) { + return _ops.DoOperation(Operators.RightShift, self, other); + } + + /// + /// Performs a bitwise-and of the two operands. Throws an exception if the operation + /// cannot be performed. + /// + public object BitwiseAnd(object self, object other) { + return _ops.DoOperation(Operators.BitwiseAnd, self, other); + } + + /// + /// Performs a bitwise-or of the two operands. Throws an exception if the operation + /// cannot be performed. + /// + public object BitwiseOr(object self, object other) { + return _ops.DoOperation(Operators.BitwiseOr, self, other); + } + + /// + /// Performs a exclusive-or of the two operands. Throws an exception if the operation + /// cannot be performed. + /// + public object ExclusiveOr(object self, object other) { + return _ops.DoOperation(Operators.ExclusiveOr, self, other); + } + + /// + /// Compares the two objects and returns true if the left object is less than the right object. + /// Throws an exception if hte comparison cannot be performed. + /// + public bool LessThan(object self, object other) { + return _ops.DoOperation(StandardOperators.LessThan, self, other); + } + + /// + /// Compares the two objects and returns true if the left object is greater than the right object. + /// Throws an exception if hte comparison cannot be performed. + /// + public bool GreaterThan(object self, object other) { + return _ops.DoOperation(StandardOperators.GreaterThan, self, other); + } + + /// + /// Compares the two objects and returns true if the left object is less than or equal to the right object. + /// Throws an exception if hte comparison cannot be performed. + /// + public bool LessThanOrEqual(object self, object other) { + return _ops.DoOperation(StandardOperators.LessThanOrEqual, self, other); + } + + /// + /// Compares the two objects and returns true if the left object is greater than or equal to the right object. + /// Throws an exception if hte comparison cannot be performed. + /// + public bool GreaterThanOrEqual(object self, object other) { + return _ops.DoOperation(StandardOperators.GreaterThanOrEqual, self, other); + } + + /// + /// Compares the two objects and returns true if the left object is equal to the right object. + /// Throws an exception if the comparison cannot be performed. + /// + public bool Equal(object self, object other) { + return _ops.DoOperation(StandardOperators.Equal, self, other); + } + + /// + /// Compares the two objects and returns true if the left object is not equal to the right object. + /// Throws an exception if hte comparison cannot be performed. + /// + public bool NotEqual(object self, object other) { + return _ops.DoOperation(StandardOperators.NotEqual, self, other); + } + + /// + /// Returns a string which describes the object as it appears in source code + /// + public string GetCodeRepresentation(object obj) { + return obj.ToString(); + //return _ops.DoOperation(StandardOperators.CodeRepresentation, obj); + } + + /// + /// Returns a list of strings which contain the known members of the object. + /// + public IList GetMemberNames(object obj) { + return _ops.GetMemberNames(obj); + } + + /// + /// Returns a string providing documentation for the specified object. + /// + public string GetDocumentation(object obj) { + return _ops.DoOperation(StandardOperators.Documentation, obj); + } + + /// + /// Returns a list of signatures applicable for calling the specified object in a form displayable to the user. + /// + public IList GetCallSignatures(object obj) { + return _ops.DoOperation>(StandardOperators.CallSignatures, obj); + } + + #endregion + +#pragma warning restore 618 + + #region Remote APIs + +#if !SILVERLIGHT + // ObjectHandle overloads + // + + /// + /// Returns true if the remote object is callable. + /// + public bool IsCallable(ObjectHandle obj) { + return IsCallable(GetLocalObject(obj)); + } + + /// + /// Calls the specified remote object with the specified remote parameters. + /// + /// Though delegates are preferable for calls they may not always be usable for remote objects. + /// + public ObjectHandle Call(ObjectHandle obj, params ObjectHandle[] parameters) { + ContractUtils.RequiresNotNull(parameters, "parameters"); + + return new ObjectHandle(Call(GetLocalObject(obj), GetLocalObjects(parameters))); + } + + /// + /// Calls the specified remote object with the local parameters which will be serialized + /// to the remote app domain. + /// + public ObjectHandle Call(ObjectHandle obj, params object[] parameters) { + return new ObjectHandle(Call(GetLocalObject(obj), parameters)); + } + + public ObjectHandle Create(ObjectHandle obj, params ObjectHandle[] parameters) { + return new ObjectHandle(CreateInstance(GetLocalObject(obj), GetLocalObjects(parameters))); + } + + public ObjectHandle Create(ObjectHandle obj, params object[] parameters) { + return new ObjectHandle(CreateInstance(GetLocalObject(obj), parameters)); + } + + /// + /// Sets the remote object as a member on the provided remote object. + /// + public void SetMember(ObjectHandle obj, string name, ObjectHandle value) { + SetMember(GetLocalObject(obj), name, GetLocalObject(value)); + } + + /// + /// Sets the member name on the remote object obj to value. This overload can be used to avoid + /// boxing and casting of strongly typed members. + /// + public void SetMember(ObjectHandle obj, string name, T value) { + SetMember(GetLocalObject(obj), name, value); + } + + /// + /// Gets the member name on the remote object. Throws an exception if the member is not defined or + /// is write-only. + /// + public ObjectHandle GetMember(ObjectHandle obj, string name) { + return new ObjectHandle(GetMember(GetLocalObject(obj), name)); + } + + /// + /// Gets the member name on the remote object. Throws an exception if the member is not defined or + /// is write-only. + /// + public T GetMember(ObjectHandle obj, string name) { + return GetMember(GetLocalObject(obj), name); + } + + /// + /// Gets the member name on the remote object. Returns false if the member is not defined or + /// is write-only. + /// + public bool TryGetMember(ObjectHandle obj, string name, out ObjectHandle value) { + object val; + if (TryGetMember(GetLocalObject(obj), name, out val)) { + value = new ObjectHandle(val); + return true; + } + + value = null; + return false; + } + + /// + /// Tests to see if the member name is defined on the remote object. + /// + public bool ContainsMember(ObjectHandle obj, string name) { + return ContainsMember(GetLocalObject(obj), name); + } + + /// + /// Removes the member from the remote object + /// + public bool RemoveMember(ObjectHandle obj, string name) { + return RemoveMember(GetLocalObject(obj), name); + } + + /// + /// Converts the remote object into the specified type returning a handle to + /// the new remote object. + /// + public ObjectHandle ConvertTo(ObjectHandle obj) { + return new ObjectHandle(ConvertTo(GetLocalObject(obj))); + } + + /// + /// Converts the remote object into the specified type returning a handle to + /// the new remote object. + /// + public ObjectHandle ConvertTo(ObjectHandle obj, Type type) { + return new ObjectHandle(ConvertTo(GetLocalObject(obj), type)); + } + + /// + /// Converts the remote object into the specified type returning a handle to + /// the new remote object. Returns true if the value can be converted, + /// false if it cannot. + /// + public bool TryConvertTo(ObjectHandle obj, out ObjectHandle result) { + T resultObj; + if (TryConvertTo(GetLocalObject(obj), out resultObj)) { + result = new ObjectHandle(resultObj); + return true; + } + result = null; + return false; + } + + /// + /// Converts the remote object into the specified type returning a handle to + /// the new remote object. Returns true if the value can be converted, + /// false if it cannot. + /// + public bool TryConvertTo(ObjectHandle obj, Type type, out ObjectHandle result) { + object resultObj; + if (TryConvertTo(GetLocalObject(obj), type, out resultObj)) { + result = new ObjectHandle(resultObj); + return true; + } + result = null; + return false; + } + + /// + /// Converts the object obj to the type T including explicit conversions which may lose information. + /// + public ObjectHandle ExplicitConvertTo(ObjectHandle obj) { + return new ObjectHandle(_ops.ExplicitConvertTo(GetLocalObject(obj))); + } + + /// + /// Converts the object obj to the type type including explicit conversions which may lose information. + /// + public ObjectHandle ExplicitConvertTo(ObjectHandle obj, Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + return new ObjectHandle(_ops.ExplicitConvertTo(GetLocalObject(obj), type)); + } + + /// + /// Converts the object obj to the type T including explicit conversions which may lose information. + /// + /// Returns true if the value can be converted, false if it cannot. + /// + public bool TryExplicitConvertTo(ObjectHandle obj, out ObjectHandle result) { + T outp; + bool res = _ops.TryExplicitConvertTo(GetLocalObject(obj), out outp); + if (res) { + result = new ObjectHandle(obj); + } else { + result = null; + } + return res; + } + + /// + /// Converts the object obj to the type type including explicit conversions which may lose information. + /// + /// Returns true if the value can be converted, false if it cannot. + /// + public bool TryExplicitConvertTo(ObjectHandle obj, Type type, out ObjectHandle result) { + object outp; + bool res = _ops.TryExplicitConvertTo(GetLocalObject(obj), type, out outp); + if (res) { + result = new ObjectHandle(obj); + } else { + result = null; + } + return res; + } + + /// + /// Unwraps the remote object and converts it into the specified type before + /// returning it. + /// + public T Unwrap(ObjectHandle obj) { + return ConvertTo(GetLocalObject(obj)); + } + + /// + /// Performs the specified unary operator on the remote object. + /// + public object DoOperation(Operators op, ObjectHandle target) { + return DoOperation(op, GetLocalObject(target)); + } + + /// + /// Performs the specified binary operator on the remote object. + /// + public ObjectHandle DoOperation(Operators op, ObjectHandle target, ObjectHandle other) { + return new ObjectHandle(DoOperation(op, GetLocalObject(target), GetLocalObject(other))); + } + + /// + /// Adds the two remote objects. Throws an exception if the operation cannot be performed. + /// + public ObjectHandle Add(ObjectHandle self, ObjectHandle other) { + return new ObjectHandle(Add(GetLocalObject(self), GetLocalObject(other))); + } + + /// + /// Subtracts the 1st remote object from the second. Throws an exception if the operation cannot be performed. + /// + public ObjectHandle Subtract(ObjectHandle self, ObjectHandle other) { + return new ObjectHandle(Subtract(GetLocalObject(self), GetLocalObject(other))); + } + + /// + /// Raises the 1st remote object to the power of the 2nd. Throws an exception if the operation cannot be performed. + /// + public ObjectHandle Power(ObjectHandle self, ObjectHandle other) { + return new ObjectHandle(Power(GetLocalObject(self), GetLocalObject(other))); + } + + /// + /// Multiplies the two remote objects. Throws an exception if the operation cannot be performed. + /// + public ObjectHandle Multiply(ObjectHandle self, ObjectHandle other) { + return new ObjectHandle(Multiply(GetLocalObject(self), GetLocalObject(other))); + } + + /// + /// Divides the 1st remote object by the 2nd. Throws an exception if the operation cannot be performed. + /// + public ObjectHandle Divide(ObjectHandle self, ObjectHandle other) { + return new ObjectHandle(Divide(GetLocalObject(self), GetLocalObject(other))); + } + + /// + /// Performs modulus on the 1st remote object by the 2nd. Throws an exception if the operation cannot be performed. + /// + public ObjectHandle Modulus(ObjectHandle self, ObjectHandle other) { + return new ObjectHandle(Modulus(GetLocalObject(self), GetLocalObject(other))); + } + + /// + /// Shifts the 1st remote object left by the 2nd remote object. Throws an exception if the operation cannot be performed. + /// + public ObjectHandle LeftShift(ObjectHandle self, ObjectHandle other) { + return new ObjectHandle(LeftShift(GetLocalObject(self), GetLocalObject(other))); + } + + /// + /// Shifts the 1st remote object right by the 2nd remote object. Throws an exception if the operation cannot be performed. + /// + public ObjectHandle RightShift(ObjectHandle self, ObjectHandle other) { + return new ObjectHandle(RightShift(GetLocalObject(self), GetLocalObject(other))); + } + + /// + /// Performs bitwise-and on the two remote objects. Throws an exception if the operation cannot be performed. + /// + public ObjectHandle BitwiseAnd(ObjectHandle self, ObjectHandle other) { + return new ObjectHandle(BitwiseAnd(GetLocalObject(self), GetLocalObject(other))); + } + + /// + /// Performs bitwise-or on the two remote objects. Throws an exception if the operation cannot be performed. + /// + public ObjectHandle BitwiseOr(ObjectHandle self, ObjectHandle other) { + return new ObjectHandle(BitwiseOr(GetLocalObject(self), GetLocalObject(other))); + } + + /// + /// Performs exclusive-or on the two remote objects. Throws an exception if the operation cannot be performed. + /// + public ObjectHandle ExclusiveOr(ObjectHandle self, ObjectHandle other) { + return new ObjectHandle(ExclusiveOr(GetLocalObject(self), GetLocalObject(other))); + } + + /// + /// Compares the two remote objects and returns true if the 1st is less than the 2nd. Throws an exception if the operation cannot be performed. + /// + public bool LessThan(ObjectHandle self, ObjectHandle other) { + return LessThan(GetLocalObject(self), GetLocalObject(other)); + } + + /// + /// Compares the two remote objects and returns true if the 1st is greater than the 2nd. Throws an exception if the operation cannot be performed. + /// + public bool GreaterThan(ObjectHandle self, ObjectHandle other) { + return GreaterThan(GetLocalObject(self), GetLocalObject(other)); + } + + /// + /// Compares the two remote objects and returns true if the 1st is less than or equal to the 2nd. Throws an exception if the operation cannot be performed. + /// + public bool LessThanOrEqual(ObjectHandle self, ObjectHandle other) { + return LessThanOrEqual(GetLocalObject(self), GetLocalObject(other)); + } + + /// + /// Compares the two remote objects and returns true if the 1st is greater than or equal to than the 2nd. Throws an exception if the operation cannot be performed. + /// + public bool GreaterThanOrEqual(ObjectHandle self, ObjectHandle other) { + return GreaterThanOrEqual(GetLocalObject(self), GetLocalObject(other)); + } + + /// + /// Compares the two remote objects and returns true if the 1st is equal to the 2nd. Throws an exception if the operation cannot be performed. + /// + public bool Equal(ObjectHandle self, ObjectHandle other) { + return Equal(GetLocalObject(self), GetLocalObject(other)); + } + + /// + /// Compares the two remote objects and returns true if the 1st is not equal to the 2nd. Throws an exception if the operation cannot be performed. + /// + public bool NotEqual(ObjectHandle self, ObjectHandle other) { + return NotEqual(GetLocalObject(self), GetLocalObject(other)); + } + + /// + /// Returns a string which describes the remote object as it appears in source code + /// + public string GetCodeRepresentation(ObjectHandle obj) { + return GetCodeRepresentation(GetLocalObject(obj)); + } + + /// + /// Returns a list of strings which contain the known members of the remote object. + /// + public IList GetMemberNames(ObjectHandle obj) { + return GetMemberNames(GetLocalObject(obj)); + } + + /// + /// Returns a string providing documentation for the specified remote object. + /// + public string GetDocumentation(ObjectHandle obj) { + return GetDocumentation(GetLocalObject(obj)); + } + + /// + /// Returns a list of signatures applicable for calling the specified object in a form displayable to the user. + /// + public IList GetCallSignatures(ObjectHandle obj) { + return GetCallSignatures(GetLocalObject(obj)); + } + + /// + /// Helper to unwrap an object - in the future maybe we should validate the current app domain. + /// + private static object GetLocalObject(ObjectHandle obj) { + ContractUtils.RequiresNotNull(obj, "obj"); + + return obj.Unwrap(); + } + + /// + /// Helper to unwrap multiple objects + /// + private static object[] GetLocalObjects(ObjectHandle[] ohs) { + Debug.Assert(ohs != null); + + object[] res = new object[ohs.Length]; + for (int i = 0; i < res.Length; i++) { + res[i] = GetLocalObject(ohs[i]); + } + + return res; + } +#endif + + #endregion + +#if !SILVERLIGHT + // TODO: Figure out what is the right lifetime + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] + public override object InitializeLifetimeService() { + return null; + } +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Providers/HostingHelpers.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Providers/HostingHelpers.cs new file mode 100644 index 0000000000..3acd7e1189 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Providers/HostingHelpers.cs @@ -0,0 +1,71 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.Dynamic; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Hosting.Providers { + + /// + /// Advanced APIs for HAPI providers. These methods should not be used by hosts. + /// They are provided for other hosting API implementers that would like to leverage existing HAPI and + /// extend it with language specific functionality, for example. + /// + public static class HostingHelpers { + public static ScriptDomainManager GetDomainManager(ScriptRuntime runtime) { + ContractUtils.RequiresNotNull(runtime, "runtime"); + return runtime.Manager; + } + + public static LanguageContext GetLanguageContext(ScriptEngine engine) { + ContractUtils.RequiresNotNull(engine, "engine"); + return engine.LanguageContext; + } + + public static SourceUnit GetSourceUnit(ScriptSource scriptSource) { + ContractUtils.RequiresNotNull(scriptSource, "scriptSource"); + return scriptSource.SourceUnit; + } + + public static ScriptCode GetScriptCode(CompiledCode compiledCode) { + ContractUtils.RequiresNotNull(compiledCode, "compiledCode"); + return compiledCode.ScriptCode; + } + + public static SharedIO GetSharedIO(ScriptIO io) { + ContractUtils.RequiresNotNull(io, "io"); + return io.SharedIO; + } + + public static Scope GetScope(ScriptScope scriptScope) { + ContractUtils.RequiresNotNull(scriptScope, "scriptScope"); + return scriptScope.Scope; + } + + public static ScriptScope CreateScriptScope(ScriptEngine engine, Scope scope) { + return new ScriptScope(engine, scope); + } + + /// + /// Performs a callback in the ScriptEngine's app domain and returns the result. + /// + public static TRet CallEngine(ScriptEngine engine, Func f, T arg) { + return engine.Call(f, arg); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptEngine.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptEngine.cs new file mode 100644 index 0000000000..bb2f43de1d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptEngine.cs @@ -0,0 +1,788 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.CodeDom; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.Remoting; +using System.Security.Permissions; +using System.Text; +using System.Threading; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting { + + /// + /// Represents a language in Hosting API. + /// Hosting API counterpart for . + /// + [DebuggerDisplay("{Configuration.DisplayName}")] + public sealed class ScriptEngine +#if !SILVERLIGHT + : MarshalByRefObject +#endif + { + private readonly LanguageContext _language; + private readonly ScriptRuntime _runtime; + private LanguageSetup _config; + private ObjectOperations _operations; + private Scope _dummyScope; + + internal ScriptEngine(ScriptRuntime runtime, LanguageContext context) { + Debug.Assert(runtime != null); + Debug.Assert(context != null); + + _runtime = runtime; + _language = context; + } + + #region Object Operations + + /// + /// Returns a default ObjectOperations for the engine. + /// + /// Because an ObjectOperations object caches rules for the types of + /// objects and operations it processes, using the default ObjectOperations for + /// many objects could degrade the caching benefits. Eventually the cache for + /// some operations could degrade to a point where ObjectOperations stops caching and + /// does a full search for an implementation of the requested operation for the given objects. + /// + /// Another reason to create a new ObjectOperations instance is to have it bound + /// to the specific view of a ScriptScope. Languages may attach per-language + /// behavior to a ScriptScope which would alter how the operations are performed. + /// + /// For simple hosting situations, this is sufficient behavior. + /// + /// + /// + public ObjectOperations Operations { + get { + if (_operations == null) { + Interlocked.CompareExchange(ref _operations, CreateOperations(), null); + } + + return _operations; + } + } + + /// + /// Returns a new ObjectOperations object. See the Operations property for why you might want to call this. + /// + public ObjectOperations CreateOperations() { + return new ObjectOperations(new DynamicOperations(_language), this); + } + + /// + /// Returns a new ObjectOperations object that inherits any semantics particular to the provided ScriptScope. + /// + /// See the Operations property for why you might want to call this. + /// + public ObjectOperations CreateOperations(ScriptScope scope) { + ContractUtils.RequiresNotNull(scope, "scope"); + + return new ObjectOperations(new DynamicOperations(_language), this); + } + + #endregion + + #region Code Execution (for convenience) + + /// + /// Executes an expression within a new scope. + /// + /// The engine doesn't support code execution. + /// is a null reference. + public object Execute(string expression) { + return Execute(expression, CreateScope()); + } + + /// + /// Executes an expression within the specified scope. + /// + /// The engine doesn't support code execution. + /// is a null reference. + /// is a null reference. + public object Execute(string expression, ScriptScope scope) { + return CreateScriptSourceFromString(expression).Execute(scope); + } + + /// + /// Executes an expression within a new scope and converts result to the given type. + /// + /// The engine doesn't support code execution. + /// is a null reference. + public T Execute(string expression) { + return Operations.ConvertTo(Execute(expression)); + } + + /// + /// Executes an expression within the specified scope and converts result to the given type. + /// + /// The engine doesn't support code execution. + /// is a null reference. + /// is a null reference. + public T Execute(string expression, ScriptScope scope) { + return Operations.ConvertTo(Execute(expression, scope)); + } + + /// + /// Executes content of the specified file in a new scope and returns that scope. + /// + /// The engine doesn't support code execution. + /// is a null reference. + public ScriptScope ExecuteFile(string path) { + return ExecuteFile(path, CreateScope()); + } + + /// + /// Executes content of the specified file against the given scope. + /// + /// The . + /// The engine doesn't support code execution. + /// is a null reference. + /// is a null reference. + public ScriptScope ExecuteFile(string path, ScriptScope scope) { + CreateScriptSourceFromFile(path).Execute(scope); + return scope; + } + +#if !SILVERLIGHT + /// + /// Executes the expression in the specified scope and return a result. + /// Returns an ObjectHandle wrapping the resulting value of running the code. + /// + public ObjectHandle ExecuteAndWrap(string expression, ScriptScope scope) { + return new ObjectHandle(Execute(expression, scope)); + } + + /// + /// Executes the code in an empty scope. + /// Returns an ObjectHandle wrapping the resulting value of running the code. + /// + public ObjectHandle ExecuteAndWrap(string expression) { + return new ObjectHandle(Execute(expression)); + } +#endif + + #endregion + + #region Scopes + + public ScriptScope CreateScope() { + return new ScriptScope(this, new Scope()); + } + + public ScriptScope CreateScope(IAttributesCollection dictionary) { + ContractUtils.RequiresNotNull(dictionary, "dictionary"); + return new ScriptScope(this, new Scope(dictionary)); + } + + /// + /// This method returns the ScriptScope in which a ScriptSource of given path was executed. + /// + /// The ScriptSource.Path property is the key to finding the ScriptScope. Hosts need + /// to make sure they create a ScriptSource and set its Path property appropriately. + /// + /// GetScope is primarily useful for tools that need to map files to their execution scopes. For example, + /// an editor and interpreter tool might run a file Foo that imports or requires a file Bar. + /// + /// The editor's user might later open the file Bar and want to execute expressions in its context. + /// The tool would need to find Bar's ScriptScope for setting the appropriate context in its interpreter window. + /// This method helps with this scenario. + /// + public ScriptScope GetScope(string path) { + ContractUtils.RequiresNotNull(path, "path"); + Scope scope = _language.GetScope(path); + return (scope != null) ? new ScriptScope(this, scope) : null; + } + + #endregion + + #region Source Unit Creation + + /// + /// Return a ScriptSource object from string contents with the current engine as the language binding. + /// + /// The default SourceCodeKind is Expression. + /// + /// The ScriptSource's Path property defaults to null. + /// + public ScriptSource CreateScriptSourceFromString(string expression) { + ContractUtils.RequiresNotNull(expression, "expression"); + + return CreateScriptSource(new SourceStringContentProvider(expression), null, SourceCodeKind.Expression); + } + + /// + /// Return a ScriptSource object from string contents with the current engine as the language binding. + /// + /// The ScriptSource's Path property defaults to null. + /// + public ScriptSource CreateScriptSourceFromString(string code, SourceCodeKind kind) { + ContractUtils.RequiresNotNull(code, "code"); + ContractUtils.Requires(EnumBounds.IsValid(kind), "kind"); + + return CreateScriptSource(new SourceStringContentProvider(code), null, kind); + } + + /// + /// Return a ScriptSource object from string contents with the current engine as the language binding. + /// + /// The default SourceCodeKind is Expression. + /// + public ScriptSource CreateScriptSourceFromString(string expression, string path) { + ContractUtils.RequiresNotNull(expression, "expression"); + + return CreateScriptSource(new SourceStringContentProvider(expression), path, SourceCodeKind.Expression); + } + + /// + /// Return a ScriptSource object from string contents. These are helpers for creating ScriptSources' with the right language binding. + /// + public ScriptSource CreateScriptSourceFromString(string code, string path, SourceCodeKind kind) { + ContractUtils.RequiresNotNull(code, "code"); + ContractUtils.Requires(EnumBounds.IsValid(kind), "kind"); + + return CreateScriptSource(new SourceStringContentProvider(code), path, kind); + } + + /// + /// Return a ScriptSource object from file contents with the current engine as the language binding. + /// + /// The path's extension does NOT have to be in ScriptRuntime.GetRegisteredFileExtensions + /// or map to this language engine with ScriptRuntime.GetEngineByFileExtension. + /// + /// The default SourceCodeKind is File. + /// + /// The ScriptSource's Path property will be the path argument. + /// + /// The encoding defaults to System.Text.Encoding.Default. + /// + public ScriptSource CreateScriptSourceFromFile(string path) { + return CreateScriptSourceFromFile(path, StringUtils.DefaultEncoding, SourceCodeKind.File); + } + + /// + /// Return a ScriptSource object from file contents with the current engine as the language binding. + /// + /// The path's extension does NOT have to be in ScriptRuntime.GetRegisteredFileExtensions + /// or map to this language engine with ScriptRuntime.GetEngineByFileExtension. + /// + /// The default SourceCodeKind is File. + /// + /// The ScriptSource's Path property will be the path argument. + /// + public ScriptSource CreateScriptSourceFromFile(string path, Encoding encoding) { + return CreateScriptSourceFromFile(path, encoding, SourceCodeKind.File); + } + + /// + /// Return a ScriptSource object from file contents with the current engine as the language binding. + /// + /// The path's extension does NOT have to be in ScriptRuntime.GetRegisteredFileExtensions + /// or map to this language engine with ScriptRuntime.GetEngineByFileExtension. + /// + /// The ScriptSource's Path property will be the path argument. + /// + public ScriptSource CreateScriptSourceFromFile(string path, Encoding encoding, SourceCodeKind kind) { + ContractUtils.RequiresNotNull(path, "path"); + ContractUtils.RequiresNotNull(encoding, "encoding"); + ContractUtils.Requires(EnumBounds.IsValid(kind), "kind"); + if (!_language.CanCreateSourceCode) throw new NotSupportedException("Invariant engine cannot create scripts"); + + return new ScriptSource(this, _language.CreateFileUnit(path, encoding, kind)); + } + +#if !SILVERLIGHT + /// + /// This method returns a ScriptSource object from a System.CodeDom.CodeObject. + /// This is a factory method for creating a ScriptSources with this language binding. + /// + /// The expected CodeDom support is extremely minimal for syntax-independent expression of semantics. + /// + /// Languages may do more, but hosts should only expect CodeMemberMethod support, + /// and only sub nodes consisting of the following: + /// CodeSnippetStatement + /// CodeSnippetExpression + /// CodePrimitiveExpression + /// CodeMethodInvokeExpression + /// CodeExpressionStatement (for holding MethodInvoke) + /// + public ScriptSource CreateScriptSource(CodeObject content) { + return CreateScriptSource(content, null, SourceCodeKind.File); + } + + /// + /// This method returns a ScriptSource object from a System.CodeDom.CodeObject. + /// This is a factory method for creating a ScriptSources with this language binding. + /// + /// The expected CodeDom support is extremely minimal for syntax-independent expression of semantics. + /// + /// Languages may do more, but hosts should only expect CodeMemberMethod support, + /// and only sub nodes consisting of the following: + /// CodeSnippetStatement + /// CodeSnippetExpression + /// CodePrimitiveExpression + /// CodeMethodInvokeExpression + /// CodeExpressionStatement (for holding MethodInvoke) + /// + public ScriptSource CreateScriptSource(CodeObject content, string path) { + return CreateScriptSource(content, path, SourceCodeKind.File); + } + + /// + /// This method returns a ScriptSource object from a System.CodeDom.CodeObject. + /// This is a factory method for creating a ScriptSources with this language binding. + /// + /// The expected CodeDom support is extremely minimal for syntax-independent expression of semantics. + /// + /// Languages may do more, but hosts should only expect CodeMemberMethod support, + /// and only sub nodes consisting of the following: + /// CodeSnippetStatement + /// CodeSnippetExpression + /// CodePrimitiveExpression + /// CodeMethodInvokeExpression + /// CodeExpressionStatement (for holding MethodInvoke) + /// + public ScriptSource CreateScriptSource(CodeObject content, SourceCodeKind kind) { + return CreateScriptSource(content, null, kind); + } + + /// + /// This method returns a ScriptSource object from a System.CodeDom.CodeObject. + /// This is a factory method for creating a ScriptSources with this language binding. + /// + /// The expected CodeDom support is extremely minimal for syntax-independent expression of semantics. + /// + /// Languages may do more, but hosts should only expect CodeMemberMethod support, + /// and only sub nodes consisting of the following: + /// CodeSnippetStatement + /// CodeSnippetExpression + /// CodePrimitiveExpression + /// CodeMethodInvokeExpression + /// CodeExpressionStatement (for holding MethodInvoke) + /// + public ScriptSource CreateScriptSource(CodeObject content, string path, SourceCodeKind kind) { + ContractUtils.RequiresNotNull(content, "content"); + if (!_language.CanCreateSourceCode) throw new NotSupportedException("Invariant engine cannot create scripts"); + + return new ScriptSource(this, _language.GenerateSourceCode(content, path, kind)); + } +#endif + + /// + /// These methods return ScriptSource objects from stream contents with the current engine as the language binding. + /// + /// The default SourceCodeKind is File. + /// + /// The encoding defaults to Encoding.Default. + /// + public ScriptSource CreateScriptSource(StreamContentProvider content, string path) { + ContractUtils.RequiresNotNull(content, "content"); + + return CreateScriptSource(content, path, StringUtils.DefaultEncoding, SourceCodeKind.File); + } + + /// + /// These methods return ScriptSource objects from stream contents with the current engine as the language binding. + /// + /// The default SourceCodeKind is File. + /// + public ScriptSource CreateScriptSource(StreamContentProvider content, string path, Encoding encoding) { + ContractUtils.RequiresNotNull(content, "content"); + ContractUtils.RequiresNotNull(encoding, "encoding"); + + return CreateScriptSource(content, path, encoding, SourceCodeKind.File); + } + + /// + /// These methods return ScriptSource objects from stream contents with the current engine as the language binding. + /// + /// The encoding defaults to Encoding.Default. + /// + public ScriptSource CreateScriptSource(StreamContentProvider content, string path, Encoding encoding, SourceCodeKind kind) { + ContractUtils.RequiresNotNull(content, "content"); + ContractUtils.RequiresNotNull(encoding, "encoding"); + ContractUtils.Requires(EnumBounds.IsValid(kind), "kind"); + + return CreateScriptSource(new LanguageBoundTextContentProvider(_language, content, encoding), path, kind); + } + + /// + /// This method returns a ScriptSource with the content provider supplied with the current engine as the language binding. + /// + /// This helper lets you own the content provider so that you can implement a stream over internal host data structures, such as an editor's text representation. + /// + public ScriptSource CreateScriptSource(TextContentProvider contentProvider, string path, SourceCodeKind kind) { + ContractUtils.RequiresNotNull(contentProvider, "contentProvider"); + ContractUtils.Requires(EnumBounds.IsValid(kind), "kind"); + if (!_language.CanCreateSourceCode) throw new NotSupportedException("Invariant engine cannot create scripts"); + + return new ScriptSource(this, _language.CreateSourceUnit(contentProvider, path, kind)); + } + + #endregion + + #region Scope Variable Access + + /// + /// Fetches the value of a variable stored in the scope. + /// + /// If there is no engine associated with the scope (see ScriptRuntime.CreateScope), then the name lookup is + /// a literal lookup of the name in the scope's dictionary. Therefore, it is case-sensitive for example. + /// + /// If there is a default engine, then the name lookup uses that language's semantics. + /// + public object GetVariable(ScriptScope scope, string name) { + ContractUtils.RequiresNotNull(scope, "scope"); + ContractUtils.RequiresNotNull(name, "name"); + + return _language.LookupName(GetCodeContext(scope), SymbolTable.StringToId(name)); + } + + /// + /// This method removes the variable name and returns whether + /// the variable was bound in the scope when you called this method. + /// + /// If there is no engine associated with the scope (see ScriptRuntime.CreateScope), + /// then the name lookup is a literal lookup of the name in the scope's dictionary. Therefore, + /// it is case-sensitive for example. If there is a default engine, then the name lookup uses that language's semantics. + /// + /// Some languages may refuse to remove some variables. If the scope has a default language that has bound + /// variables that cannot be removed, the language engine throws an exception. + /// + public bool RemoveVariable(ScriptScope scope, string name) { + ContractUtils.RequiresNotNull(scope, "scope"); + ContractUtils.RequiresNotNull(name, "name"); + + return _language.RemoveName(GetCodeContext(scope), SymbolTable.StringToId(name)); + } + + /// + /// Assigns a value to a variable in the scope, overwriting any previous value. + /// + /// If there is no engine associated with the scope (see ScriptRuntime.CreateScope), + /// then the name lookup is a literal lookup of the name in the scope's dictionary. Therefore, + /// it is case-sensitive for example. + /// + /// If there is a default engine, then the name lookup uses that language's semantics. + /// + public void SetVariable(ScriptScope scope, string name, object value) { + ContractUtils.RequiresNotNull(scope, "scope"); + ContractUtils.RequiresNotNull(name, "name"); + + _language.SetName(GetCodeContext(scope), SymbolTable.StringToId(name), value); + } + + /// + /// Fetches the value of a variable stored in the scope and returns + /// a Boolean indicating success of the lookup. + /// + /// When the method's result is false, then it assigns null to value. + /// + /// If there is no engine associated with the scope (see ScriptRuntime.CreateScope), + /// then the name lookup is a literal lookup of the name in the scope's dictionary. Therefore, + /// it is case-sensitive for example. + /// + /// If there is a default engine, then the name lookup uses that language's semantics. + /// + public bool TryGetVariable(ScriptScope scope, string name, out object value) { + ContractUtils.RequiresNotNull(scope, "scope"); + ContractUtils.RequiresNotNull(name, "name"); + + return _language.TryLookupName(GetCodeContext(scope), SymbolTable.StringToId(name), out value); + } + + /// + /// Fetches the value of a variable stored in the scope. + /// + /// If there is no engine associated with the scope (see ScriptRuntime.CreateScope), then the name lookup is + /// a literal lookup of the name in the scope's dictionary. Therefore, it is case-sensitive for example. + /// + /// If there is a default engine, then the name lookup uses that language's semantics. + /// + /// Throws an exception if the engine cannot perform the requested type conversion. + /// + public T GetVariable(ScriptScope scope, string name) { + ContractUtils.RequiresNotNull(scope, "scope"); + ContractUtils.RequiresNotNull(name, "name"); + + return Operations.ConvertTo(GetVariable(scope, name)); + } + + /// + /// Fetches the value of a variable stored in the scope and returns + /// a Boolean indicating success of the lookup. + /// + /// When the method's result is false, then it assigns default(T) to value. + /// + /// If there is no engine associated with the scope (see ScriptRuntime.CreateScope), + /// then the name lookup is a literal lookup of the name in the scope's dictionary. Therefore, + /// it is case-sensitive for example. + /// + /// If there is a default engine, then the name lookup uses that language's semantics. + /// + /// Throws an exception if the engine cannot perform the requested type conversion, + /// then it return false and assigns value to default(T). + /// + public bool TryGetVariable(ScriptScope scope, string name, out T value) { + ContractUtils.RequiresNotNull(scope, "scope"); + ContractUtils.RequiresNotNull(name, "name"); + + object res; + if (TryGetVariable(scope, name, out res)) { + return Operations.TryConvertTo(res, out value); + } + + value = default(T); + return false; + } + + /// + /// This method returns whether the variable is bound in this scope. + /// + /// If there is no engine associated with the scope (see ScriptRuntime.CreateScope), + /// then the name lookup is a literal lookup of the name in the scope's dictionary. Therefore, + /// it is case-sensitive for example. + /// + /// If there is a default engine, then the name lookup uses that language's semantics. + /// + public bool ContainsVariable(ScriptScope scope, string name) { + ContractUtils.RequiresNotNull(scope, "scope"); + ContractUtils.RequiresNotNull(name, "name"); + + object dummy; + return TryGetVariable(scope, name, out dummy); + } + + #endregion + + #region Remoting Support +#if !SILVERLIGHT + + /// + /// Fetches the value of a variable stored in the scope and returns an the wrapped object as an ObjectHandle. + /// + /// If there is no engine associated with the scope (see ScriptRuntime.CreateScope), then the name lookup is + /// a literal lookup of the name in the scope's dictionary. Therefore, it is case-sensitive for example. + /// + /// If there is a default engine, then the name lookup uses that language's semantics. + /// + public ObjectHandle GetVariableHandle(ScriptScope scope, string name) { + ContractUtils.RequiresNotNull(scope, "scope"); + ContractUtils.RequiresNotNull(name, "name"); + + return new ObjectHandle(GetVariable(scope, name)); + } + + /// + /// Assigns a value to a variable in the scope, overwriting any previous value. + /// + /// The ObjectHandle value is unwrapped before performing the assignment. + /// + /// If there is no engine associated with the scope (see ScriptRuntime.CreateScope), + /// then the name lookup is a literal lookup of the name in the scope's dictionary. Therefore, + /// it is case-sensitive for example. + /// + /// If there is a default engine, then the name lookup uses that language's semantics. + /// + public void SetVariable(ScriptScope scope, string name, ObjectHandle value) { + ContractUtils.RequiresNotNull(scope, "scope"); + ContractUtils.RequiresNotNull(name, "name"); + + SetVariable(scope, name, value.Unwrap()); + } + + /// + /// Fetches the value of a variable stored in the scope and returns + /// a Boolean indicating success of the lookup. + /// + /// When the method's result is false, then it assigns null to the value. Otherwise + /// an ObjectHandle wrapping the object is assigned to value. + /// + /// If there is no engine associated with the scope (see ScriptRuntime.CreateScope), + /// then the name lookup is a literal lookup of the name in the scope's dictionary. Therefore, + /// it is case-sensitive for example. + /// + /// If there is a default engine, then the name lookup uses that language's semantics. + /// + public bool TryGetVariableHandle(ScriptScope scope, string name, out ObjectHandle value) { + ContractUtils.RequiresNotNull(scope, "scope"); + ContractUtils.RequiresNotNull(name, "name"); + + object res; + if (TryGetVariable(scope, name, out res)) { + value = new ObjectHandle(res); + return true; + } + value = null; + return false; + } +#endif + #endregion + + #region Additional Services + + /// + /// This method returns a language-specific service. + /// + /// It provides a point of extensibility for a language implementation + /// to offer more functionality than the standard engine members discussed here. + /// + public TService GetService(params object[] args) where TService : class { + if (typeof(TService) == typeof(TokenCategorizer)) { + TokenizerService service = _language.GetService(ArrayUtils.Insert((object)_language, args)); + return (service != null) ? (TService)(object)new TokenCategorizer(service) : null; + } + if (typeof(TService) == typeof(ExceptionOperations)) { + ExceptionOperations service = _language.GetService(); + return (service != null) ? (TService)(object)service : (TService)(object)new ExceptionOperations(_language); + } + return _language.GetService(args); + } + + #endregion + + #region Misc. engine information + + /// + /// This property returns readon-only LanguageOptions this engine is using. + /// + /// + /// The values are determined during runtime initialization and read-only afterwards. + /// You can change the settings via a configuration file or explicitly using ScriptRuntimeSetup class. + /// + public LanguageSetup Setup { + get { + if (_config == null) { + // The user shouldn't be able to get a hold of the invariant engine + Debug.Assert(!(_language is InvariantContext)); + + // Find the matching language configuration + LanguageConfiguration config = _runtime.Manager.Configuration.GetLanguageConfig(_language); + Debug.Assert(config != null); + + foreach (var language in _runtime.Setup.LanguageSetups) { + if (config.ProviderName == new AssemblyQualifiedTypeName(language.TypeName)) { + return _config = language; + } + } + } + return _config; + } + } + + /// + /// This property returns the ScriptRuntime for the context in which this engine executes. + /// + public ScriptRuntime Runtime { + get { + return _runtime; + } + } + + /// + /// This property returns the engine's version as a string. The format is language-dependent. + /// + public Version LanguageVersion { + get { + return _language.LanguageVersion; + } + } + + #endregion + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public CompilerOptions GetCompilerOptions() { + return _language.GetCompilerOptions(); + } + + public CompilerOptions GetCompilerOptions(ScriptScope scope) { + return _language.GetCompilerOptions(scope.Scope); + } + + /// + /// Sets the search paths used by the engine for loading files when a script wants + /// to import or require another file of code. + /// + /// The language doesn't allow to set search paths. + public void SetSearchPaths(ICollection paths) { + ContractUtils.RequiresNotNull(paths, "paths"); + ContractUtils.RequiresNotNullItems(paths, "paths"); + + _language.SetSearchPaths(paths); + } + + /// + /// Gets the search paths used by the engine for loading files when a script wants + /// to import or require another file of code. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public ICollection GetSearchPaths() { + return _language.GetSearchPaths(); + } + + #region Private implementation details + + // Gets a LanguageContext for the specified module that captures the current state + // of the module which will be used for compilation and execution of the next piece of code against the module. + private CodeContext GetCodeContext(ScriptScope scriptScope) { + Scope scope; + + if (scriptScope != null) { + scope = scriptScope.Scope; + } else { + // TODO: we need a scope to CodeContext; could we allow CodeContext w/o scope? + if (_dummyScope == null) { + Interlocked.CompareExchange(ref _dummyScope, new Scope(), null); + } + scope = _dummyScope; + } + + return new CodeContext(scope, _language); + } + + #endregion + + #region Internal API Surface + + internal LanguageContext LanguageContext { + get { + return _language; + } + } + + internal TRet Call(Func f, T arg) { + return f(_language, arg); + } + + #endregion + + #region Remote API +#if !SILVERLIGHT + + // TODO: Figure out what is the right lifetime + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] + public override object InitializeLifetimeService() { + return null; + } + +#endif + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptHost.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptHost.cs new file mode 100644 index 0000000000..05a7415eb9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptHost.cs @@ -0,0 +1,96 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Dynamic; +using System.Text; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting { + + /// + /// ScriptHost is collocated with ScriptRuntime in the same app-domain. + /// The host can implement a derived class to consume some notifications and/or + /// customize operations like TryGetSourceUnit,ResolveSourceUnit, etc. + /// + /// The areguments to the the constructor of the derived class are specified in ScriptRuntimeSetup + /// instance that enters ScriptRuntime initialization. + /// + /// If the host is remote with respect to DLR (i.e. also with respect to ScriptHost) + /// and needs to access objects living in its app-domain it can pass MarshalByRefObject + /// as an argument to its ScriptHost subclass constructor. + /// + public class ScriptHost +#if !SILVERLIGHT + : MarshalByRefObject +#endif + { + /// + /// The runtime the host is attached to. + /// + private ScriptRuntime _runtime; + + public ScriptHost() { + } + + // Called by ScriptRuntime when it is completely initialized. + // Notifies the host implementation that the runtime is available now. + internal void SetRuntime(ScriptRuntime runtime) { + Assert.NotNull(runtime); + _runtime = runtime; + + RuntimeAttached(); + } + + public ScriptRuntime Runtime { + get { + if (_runtime == null) { + throw new InvalidOperationException("Host not initialized"); + } + return _runtime; + } + } + + public virtual PlatformAdaptationLayer PlatformAdaptationLayer { + get { + return PlatformAdaptationLayer.Default; + } + } + + #region Notifications + + /// + /// Invoked after the initialization of the associated Runtime is finished. + /// The host can override this method to perform additional initialization of runtime (like loading assemblies etc.). + /// + protected virtual void RuntimeAttached() { + // nop + } + + /// + /// Invoked after a new language is loaded into the Runtime. + /// The host can override this method to perform additional initialization of language engines. + /// + internal protected virtual void EngineCreated(ScriptEngine engine) { + // nop + } + + #endregion + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptHostProxy.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptHostProxy.cs new file mode 100644 index 0000000000..5f04997574 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptHostProxy.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Text; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting { + /// + /// Provides hosting to DLR. Forwards DLR requests to the ScriptHost. + /// + internal sealed class ScriptHostProxy : DynamicRuntimeHostingProvider { + private readonly ScriptHost _host; + + public ScriptHostProxy(ScriptHost host) { + Assert.NotNull(host); + _host = host; + } + + public override PlatformAdaptationLayer PlatformAdaptationLayer { + get { return _host.PlatformAdaptationLayer; } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptIO.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptIO.cs new file mode 100644 index 0000000000..5c6657784f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptIO.cs @@ -0,0 +1,111 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Security.Permissions; +using System.Text; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting { + /// + /// Provides host-redirectable IO streams used by DLR languages for default IO. + /// + public sealed class ScriptIO +#if !SILVERLIGHT + : MarshalByRefObject +#endif + { + + private readonly SharedIO _io; + + public Stream InputStream { get { return _io.InputStream; } } + public Stream OutputStream { get { return _io.OutputStream; } } + public Stream ErrorStream { get { return _io.ErrorStream; } } + + public TextReader InputReader { get { return _io.InputReader; } } + public TextWriter OutputWriter { get { return _io.OutputWriter; } } + public TextWriter ErrorWriter { get { return _io.ErrorWriter; } } + + public Encoding InputEncoding { get { return _io.InputEncoding; } } + public Encoding OutputEncoding { get { return _io.OutputEncoding; } } + public Encoding ErrorEncoding { get { return _io.ErrorEncoding; } } + + internal SharedIO SharedIO { get { return _io; } } + + internal ScriptIO(SharedIO io) { + Assert.NotNull(io); + _io = io; + } + + /// + /// Used if the host stores the output as binary data. + /// + /// Binary stream to write data to. + /// Encoding used to convert textual data written to the output by the script. + public void SetOutput(Stream stream, Encoding encoding) { + ContractUtils.RequiresNotNull(stream, "stream"); + ContractUtils.RequiresNotNull(encoding, "encoding"); + _io.SetOutput(stream, new StreamWriter(stream, encoding)); + } + + /// + /// Used if the host handles both kinds of data (textual and binary) by itself. + /// + public void SetOutput(Stream stream, TextWriter writer) { + ContractUtils.RequiresNotNull(stream, "stream"); + ContractUtils.RequiresNotNull(writer, "writer"); + _io.SetOutput(stream, writer); + } + + public void SetErrorOutput(Stream stream, Encoding encoding) { + ContractUtils.RequiresNotNull(stream, "stream"); + ContractUtils.RequiresNotNull(encoding, "encoding"); + _io.SetErrorOutput(stream, new StreamWriter(stream, encoding)); + } + + public void SetErrorOutput(Stream stream, TextWriter writer) { + ContractUtils.RequiresNotNull(stream, "stream"); + ContractUtils.RequiresNotNull(writer, "writer"); + _io.SetErrorOutput(stream, writer); + } + + public void SetInput(Stream stream, Encoding encoding) { + ContractUtils.RequiresNotNull(stream, "stream"); + ContractUtils.RequiresNotNull(encoding, "encoding"); + _io.SetInput(stream, new StreamReader(stream, encoding), encoding); + } + + public void SetInput(Stream stream, TextReader reader, Encoding encoding) { + ContractUtils.RequiresNotNull(stream, "stream"); + ContractUtils.RequiresNotNull(reader, "writer"); + ContractUtils.RequiresNotNull(encoding, "encoding"); + _io.SetInput(stream, reader, encoding); + } + + public void RedirectToConsole() { + _io.RedirectToConsole(); + } + +#if !SILVERLIGHT + // TODO: Figure out what is the right lifetime + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] + public override object InitializeLifetimeService() { + return null; + } +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptRuntime.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptRuntime.cs new file mode 100644 index 0000000000..999f57b811 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptRuntime.cs @@ -0,0 +1,360 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Security.Permissions; +using System.Text; +using System.Threading; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting { + /// + /// Represents a Dynamic Language Runtime in Hosting API. + /// Hosting API counterpart for . + /// + public sealed class ScriptRuntime +#if !SILVERLIGHT + : MarshalByRefObject +#endif + { + private readonly Dictionary _engines; + private readonly ScriptDomainManager _manager; + private readonly InvariantContext _invariantContext; + private readonly ScriptIO _io; + private readonly ScriptHost _host; + private readonly ScriptRuntimeSetup _setup; + private ScriptScope _globals; + private ScriptEngine _invariantEngine; + + /// + /// Creates ScriptRuntime in the current app-domain and initialized according to the the specified settings. + /// Creates an instance of host class specified in the setup and associates it with the created runtime. + /// Both Runtime and ScriptHost are collocated in the current app-domain. + /// + public ScriptRuntime(ScriptRuntimeSetup setup) { + ContractUtils.RequiresNotNull(setup, "setup"); + + // Do this first, so we detect configuration errors immediately + DlrConfiguration config = setup.ToConfiguration(); + _setup = setup; + + _host = ReflectionUtils.CreateInstance(setup.HostType, setup.HostArguments.ToArray()); + + ScriptHostProxy hostProxy = new ScriptHostProxy(_host); + + _manager = new ScriptDomainManager(hostProxy, config); + _invariantContext = new InvariantContext(_manager); + + _io = new ScriptIO(_manager.SharedIO); + _engines = new Dictionary(); + + bool freshEngineCreated; + _globals = new ScriptScope(GetEngineNoLockNoNotification(_invariantContext, out freshEngineCreated), _manager.Globals); + + // runtime needs to be all set at this point, host code is called + + _host.SetRuntime(this); + } + + internal ScriptDomainManager Manager { + get { return _manager; } + } + + public ScriptHost Host { + get { return _host; } + } + + public ScriptIO IO { + get { return _io; } + } + + /// + /// Creates a new runtime with languages set up according to the current application configuration + /// (using System.Configuration). + /// + public static ScriptRuntime CreateFromConfiguration() { + return new ScriptRuntime(ScriptRuntimeSetup.ReadConfiguration()); + } + + #region Remoting + +#if !SILVERLIGHT + + /// + /// Creates ScriptRuntime in the current app-domain and initialized according to the the specified settings. + /// Creates an instance of host class specified in the setup and associates it with the created runtime. + /// Both Runtime and ScriptHost are collocated in the specified app-domain. + /// + public static ScriptRuntime CreateRemote(AppDomain domain, ScriptRuntimeSetup setup) { + ContractUtils.RequiresNotNull(domain, "domain"); + return (ScriptRuntime)domain.CreateInstanceAndUnwrap( + typeof(ScriptRuntime).Assembly.FullName, + typeof(ScriptRuntime).FullName, + false, + BindingFlags.Default, + null, + new object[] { setup }, + null, + null, + null + ); + } + + // TODO: Figure out what is the right lifetime + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] + public override object InitializeLifetimeService() { + return null; + } +#endif + #endregion + + public ScriptRuntimeSetup Setup { + get { + return _setup; + } + } + + #region Engines + + public ScriptEngine GetEngine(string languageName) { + ContractUtils.RequiresNotNull(languageName, "languageName"); + + ScriptEngine engine; + if (!TryGetEngine(languageName, out engine)) { + throw new ArgumentException(String.Format("Unknown language name: '{0}'", languageName)); + } + + return engine; + } + + public ScriptEngine GetEngineByTypeName(string assemblyQualifiedTypeName) { + ContractUtils.RequiresNotNull(assemblyQualifiedTypeName, "assemblyQualifiedTypeName"); + return GetEngine(_manager.GetLanguageByTypeName(assemblyQualifiedTypeName)); + } + + /// + /// + public ScriptEngine GetEngineByFileExtension(string fileExtension) { + ContractUtils.RequiresNotNull(fileExtension, "fileExtension"); + + ScriptEngine engine; + if (!TryGetEngineByFileExtension(fileExtension, out engine)) { + throw new ArgumentException(String.Format("Unknown file extension: '{0}'", fileExtension)); + } + + return engine; + } + + public bool TryGetEngine(string languageName, out ScriptEngine engine) { + LanguageContext language; + if (!_manager.TryGetLanguage(languageName, out language)) { + engine = null; + return false; + } + + engine = GetEngine(language); + return true; + } + + public bool TryGetEngineByFileExtension(string fileExtension, out ScriptEngine engine) { + LanguageContext language; + if (!_manager.TryGetLanguageByFileExtension(fileExtension, out language)) { + engine = null; + return false; + } + + engine = GetEngine(language); + return true; + } + + /// + /// Gets engine for the specified language. + /// + internal ScriptEngine GetEngine(LanguageContext language) { + Assert.NotNull(language); + + ScriptEngine engine; + bool freshEngineCreated; + lock (_engines) { + engine = GetEngineNoLockNoNotification(language, out freshEngineCreated); + } + + if (freshEngineCreated && !ReferenceEquals(language, _invariantContext)) { + _host.EngineCreated(engine); + } + + return engine; + } + + /// + /// Looks up the engine for the specified language. It the engine hasn't been created in this Runtime, it is instantiated here. + /// The method doesn't lock nor send notifications to the host. + /// + private ScriptEngine GetEngineNoLockNoNotification(LanguageContext language, out bool freshEngineCreated) { + Debug.Assert(_engines != null, "Invalid ScriptRuntime initialiation order"); + + ScriptEngine engine; + if (freshEngineCreated = !_engines.TryGetValue(language, out engine)) { + engine = new ScriptEngine(this, language); + Thread.MemoryBarrier(); + _engines.Add(language, engine); + } + return engine; + } + + #endregion + + #region Compilation, Module Creation + + public ScriptScope CreateScope() { + return InvariantEngine.CreateScope(); + } + + public ScriptScope CreateScope(string languageId) { + return GetEngine(languageId).CreateScope(); + } + + public ScriptScope CreateScope(IAttributesCollection dictionary) { + return InvariantEngine.CreateScope(dictionary); + } + + public ScriptScope CreateScope(string languageId, IAttributesCollection dictionary) { + return GetEngine(languageId).CreateScope(dictionary); + } + + #endregion + + // TODO: file IO exceptions, parse exceptions, execution exceptions, etc. + /// + /// path is empty, contains one or more of the invalid characters defined in GetInvalidPathChars or doesn't have an extension. + /// + public ScriptScope ExecuteFile(string path) { + ContractUtils.RequiresNotEmpty(path, "path"); + string extension = Path.GetExtension(path); + + ScriptEngine engine; + if (!TryGetEngineByFileExtension(extension, out engine)) { + throw new ArgumentException(String.Format("File extension '{0}' is not associated with any language.", extension)); + } + + return engine.ExecuteFile(path); + } + + /// path is null + /// file extension does not map to language engine + /// language does not have any search paths + /// file does exist in language's search path + public ScriptScope UseFile(string path) { + ContractUtils.RequiresNotEmpty(path, "path"); + string extension = Path.GetExtension(path); + + ScriptEngine engine; + if (!TryGetEngineByFileExtension(extension, out engine)) { + throw new ArgumentException(string.Format("File extension '{0}' is not associated with any language.", extension)); + } + + var searchPaths = engine.GetSearchPaths(); + if (searchPaths.Count == 0) { + throw new InvalidOperationException(string.Format("No search paths defined for language '{0}'", engine.Setup.DisplayName)); + } + + // See if the file is already loaded, if so return the scope + foreach (string searchPath in searchPaths) { + string filePath = Path.Combine(searchPath, path); + ScriptScope scope = engine.GetScope(filePath); + if (scope != null) { + return scope; + } + } + + // Find the file on disk, then load it + foreach (string searchPath in searchPaths) { + string filePath = Path.Combine(searchPath, path); + if (_manager.Platform.FileExists(filePath)) { + return ExecuteFile(filePath); + } + } + + // Didn't find the file, throw + string allPaths = searchPaths.Aggregate((x, y) => x + ", " + y); + throw new FileNotFoundException(string.Format("File '{0}' not found in language's search path: {1}", path, allPaths)); + } + + /// + /// This property returns the "global object" or name bindings of the ScriptRuntime as a ScriptScope. + /// + /// You can set the globals scope, which you might do if you created a ScriptScope with an + /// IAttributesCollection so that your host could late bind names. + /// + public ScriptScope Globals { + get { return _globals; } + set { + ContractUtils.RequiresNotNull(value, "value"); + + // TODO: this is wrong, we ignore other parts of the scope here + _globals = value; + _manager.SetGlobalsDictionary(_globals.Scope.Dict); + } + } + + /// + /// This method walks the assembly's namespaces and name bindings to ScriptRuntime.Globals + /// to represent the types available in the assembly. Each top-level namespace name gets + /// bound in Globals to a dynamic object representing the namespace. Within each top-level + /// namespace object, nested namespace names are bound to dynamic objects representing each + /// tier of nested namespaces. When this method encounters the same namespace-qualified name, + /// it merges names together objects representing the namespaces. + /// + /// + public void LoadAssembly(Assembly assembly) { + _manager.LoadAssembly(assembly); + } + + public ObjectOperations Operations { + get { + return InvariantEngine.Operations; + } + } + + public ObjectOperations CreateOperations() { + return InvariantEngine.CreateOperations(); + } + + public void Shutdown() { + List lcs; + lock (_engines) { + lcs = new List(_engines.Keys); + } + + foreach (var language in lcs) { + language.Shutdown(); + } + } + + internal ScriptEngine InvariantEngine { + get { + if (_invariantEngine == null) { + _invariantEngine = GetEngine(_invariantContext); + } + return _invariantEngine; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptRuntimeSetup.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptRuntimeSetup.cs new file mode 100644 index 0000000000..e173c88aae --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptRuntimeSetup.cs @@ -0,0 +1,208 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Reflection; +using System.IO; +using System.Collections.ObjectModel; + +namespace Microsoft.Scripting.Hosting { + /// + /// Stores information needed to setup a ScriptRuntime + /// + [Serializable] + public sealed class ScriptRuntimeSetup { + // host specification: + private Type _hostType; + private IList _hostArguments; + + // languages available in the runtime: + private IList _languageSetups; + + // DLR options: + private bool _debugMode; + private bool _privateBinding; + + // common language options: + private IDictionary _options; + + // true if the ScriptRuntimeSetup is no longer mutable because it's been + // used to start a ScriptRuntime + private bool _frozen; + + public ScriptRuntimeSetup() { + _languageSetups = new List(); + _options = new Dictionary(); + _hostType = typeof(ScriptHost); + _hostArguments = ArrayUtils.EmptyObjects; + } + + /// + /// The list of language setup information for languages to load into + /// the runtime + /// + public IList LanguageSetups { + get { return _languageSetups; } + } + + /// + /// Indicates that the script runtime is in debug mode. + /// This means: + /// + /// 1) Symbols are emitted for debuggable methods (methods associated with SourceUnit). + /// 2) Debuggable methods are emitted to non-collectable types (this is due to CLR limitations on dynamic method debugging). + /// 3) JIT optimization is disabled for all methods + /// 4) Languages may disable optimizations based on this value. + /// + public bool DebugMode { + get { return _debugMode; } + set { + CheckFrozen(); + _debugMode = value; + } + } + + /// + /// Ignore CLR visibility checks + /// + public bool PrivateBinding { + get { return _privateBinding; } + set { + CheckFrozen(); + _privateBinding = value; + } + } + + /// + /// Can be any derived class of ScriptHost. When set, it allows the + /// host to override certain methods to control behavior of the runtime + /// + public Type HostType { + get { return _hostType; } + set { + ContractUtils.RequiresNotNull(value, "value"); + ContractUtils.Requires(typeof(ScriptHost).IsAssignableFrom(value), "value", "Must be ScriptHost or a derived type of ScriptHost"); + CheckFrozen(); + _hostType = value; + } + } + + /// + /// Option names are case-sensitive. + /// + public IDictionary Options { + get { return _options; } + } + + /// + /// Arguments passed to the host type when it is constructed + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public IList HostArguments { + get { + return _hostArguments; + } + set { + ContractUtils.RequiresNotNull(value, "value"); + CheckFrozen(); + _hostArguments = value; + } + } + + internal DlrConfiguration ToConfiguration() { + ContractUtils.Requires(_languageSetups.Count > 0, "ScriptRuntimeSetup must have at least one LanguageSetup"); + + // prepare + ReadOnlyCollection setups = new ReadOnlyCollection(ArrayUtils.MakeArray(_languageSetups)); + var hostArguments = new ReadOnlyCollection(ArrayUtils.MakeArray(_hostArguments)); + var options = new ReadOnlyDictionary(new Dictionary(_options)); + var config = new DlrConfiguration(_debugMode, _privateBinding, options); + + // validate + foreach (var language in setups) { + config.AddLanguage( + language.TypeName, + language.DisplayName, + language.Names, + language.FileExtensions, + language.Options + ); + } + + // commit + _languageSetups = setups; + _options = options; + _hostArguments = hostArguments; + + Freeze(setups); + + return config; + } + + private void Freeze(ReadOnlyCollection setups) { + foreach (var language in setups) { + language.Freeze(); + } + + _frozen = true; + } + + private void CheckFrozen() { + if (_frozen) { + throw new InvalidOperationException("Cannot modify ScriptRuntimeSetup after it has been used to create a ScriptRuntime"); + } + } + + /// + /// Reads setup from .NET configuration system (.config files). + /// If there is no configuration available returns an empty setup. + /// + public static ScriptRuntimeSetup ReadConfiguration() { +#if SILVERLIGHT + return new ScriptRuntimeSetup(); +#else + var setup = new ScriptRuntimeSetup(); + Configuration.Section.LoadRuntimeSetup(setup, null); + return setup; +#endif + } + +#if !SILVERLIGHT + /// + /// Reads setup from a specified XML stream. + /// + public static ScriptRuntimeSetup ReadConfiguration(Stream configFileStream) { + ContractUtils.RequiresNotNull(configFileStream, "configFileStream"); + var setup = new ScriptRuntimeSetup(); + Configuration.Section.LoadRuntimeSetup(setup, configFileStream); + return setup; + } + + /// + /// Reads setup from a specified XML file. + /// + public static ScriptRuntimeSetup ReadConfiguration(string configFilePath) { + ContractUtils.RequiresNotNull(configFilePath, "configFilePath"); + + using (var stream = File.OpenRead(configFilePath)) { + return ReadConfiguration(stream); + } + } +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptScope.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptScope.cs new file mode 100644 index 0000000000..e96c522000 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptScope.cs @@ -0,0 +1,333 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Runtime.Remoting; +using System.Runtime.Serialization; +using System.Dynamic.Binders; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Hosting { + /// + /// A ScriptScope is a unit of execution for code. It consists of a global Scope which + /// all code executes in. A ScriptScope can have an arbitrary initializer and arbitrary + /// reloader. + /// + /// ScriptScope is not thread safe. Host should either lock when multiple threads could + /// access the same module or should make a copy for each thread. + /// + /// Hosting API counterpart for . + /// +#if SILVERLIGHT + public sealed class ScriptScope : IDynamicObject { +#else + [DebuggerTypeProxy(typeof(ScriptScope.DebugView))] + public sealed class ScriptScope : MarshalByRefObject, IDynamicObject { +#endif + private readonly Scope _scope; + private readonly ScriptEngine _engine; + + internal ScriptScope(ScriptEngine engine, Scope scope) { + Assert.NotNull(engine); + Assert.NotNull(scope); + + _scope = scope; + _engine = engine; + } + + internal Scope Scope { + get { return _scope; } + } + + /// + /// Gets an engine for the language associated with this scope. + /// Returns invariant engine if the scope is language agnostic. + /// + public ScriptEngine Engine { + get { + // InvariantContext should not have an engine + // TODO: If _engine itself could be set to null, we wouldn't + // need this check + if (_engine.LanguageContext is InvariantContext) { + return null; + } + return _engine; + } + } + + /// + /// Gets a value stored in the scope under the given name. + /// + /// The specified name is not defined in the scope. + /// is a null reference. + public object GetVariable(string name) { + return _scope.LookupName(_engine.LanguageContext, SymbolTable.StringToId(name)); + } + + /// + /// Gets a value stored in the scope under the given name. + /// Converts the result to the specified type using the conversion that the language associated with the scope defines. + /// If no language is associated with the scope, the default CLR conversion is attempted. + /// + /// The specified name is not defined in the scope. + /// is a null reference. + public T GetVariable(string name) { + return _engine.Operations.ConvertTo(_engine.GetVariable(this, name)); + } + + /// + /// Tries to get a value stored in the scope under the given name. + /// + /// is a null reference. + public bool TryGetVariable(string name, out object value) { + return _scope.TryGetName(_engine.LanguageContext, SymbolTable.StringToId(name), out value); + } + + /// + /// Tries to get a value stored in the scope under the given name. + /// Converts the result to the specified type using the conversion that the language associated with the scope defines. + /// If no language is associated with the scope, the default CLR conversion is attempted. + /// + /// is a null reference. + public bool TryGetVariable(string name, out T value) { + object result; + if (_scope.TryGetName(_engine.LanguageContext, SymbolTable.StringToId(name), out result)) { + value = _engine.Operations.ConvertTo(result); + return true; + } + value = default(T); + return false; + } + + /// + /// Sets the name to the specified value. + /// + /// is a null reference. + public void SetVariable(string name, object value) { + _scope.SetName(SymbolTable.StringToId(name), value); + } + +#if !SILVERLIGHT + /// + /// Gets a handle for a value stored in the scope under the given name. + /// + /// The specified name is not defined in the scope. + /// is a null reference. + public ObjectHandle GetVariableHandle(string name) { + return new ObjectHandle(GetVariable(name)); + } + + /// + /// Tries to get a handle for a value stored in the scope under the given name. + /// Returns true if there is such name, false otherwise. + /// + /// is a null reference. + public bool TryGetVariableHandle(string name, out ObjectHandle handle) { + object value; + if (TryGetVariable(name, out value)) { + handle = new ObjectHandle(value); + return true; + } else { + handle = null; + return false; + } + } + + /// + /// Sets the name to the specified value. + /// + /// + /// The value held by the handle isn't from the scope's app-domain and isn't serializable or MarshalByRefObject. + /// + /// or is a null reference. + public void SetVariable(string name, ObjectHandle handle) { + ContractUtils.RequiresNotNull(handle, "handle"); + SetVariable(name, handle.Unwrap()); + } +#endif + + /// + /// Determines if this context or any outer scope contains the defined name. + /// + /// is a null reference. + public bool ContainsVariable(string name) { + return _scope.ContainsName(_engine.LanguageContext, SymbolTable.StringToId(name)); + } + + /// + /// Removes the variable of the given name from this scope. + /// + /// true if the value existed in the scope before it has been removed. + /// is a null reference. + public bool RemoveVariable(string name) { + return _scope.TryRemoveName(_engine.LanguageContext, SymbolTable.StringToId(name)); + } + + /// + /// Gets a list of variable names stored in the scope. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public IEnumerable GetVariableNames() { + // Remoting: we eagerly enumerate all variables to avoid cross domain calls for each item. + + var result = new List(); + foreach (var entry in _scope.Items) { + result.Add(SymbolTable.IdToString(entry.Key)); + } + result.TrimExcess(); + return result; + } + + /// + /// Gets an array of variable names and their values stored in the scope. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public IEnumerable> GetItems() { + // Remoting: we eagerly enumerate all variables to avoid cross domain calls for each item. + + var result = new List>(); + foreach (var entry in _scope.Items) { + result.Add(new KeyValuePair(SymbolTable.IdToString(entry.Key), entry.Value)); + } + result.TrimExcess(); + return result; + } + + #region DebugView +#if !SILVERLIGHT + internal sealed class DebugView { + private readonly ScriptScope _scope; + + public DebugView(ScriptScope scope) { + Assert.NotNull(scope); + _scope = scope; + } + + public ScriptEngine Language { + get { return _scope._engine; } + } + + public System.Collections.Hashtable Variables { + get { + System.Collections.Hashtable result = new System.Collections.Hashtable(); + foreach (KeyValuePair variable in _scope.GetItems()) { + result[variable.Key] = variable.Value; + } + return result; + } + } + } +#endif + #endregion + + #region IDynamicObject implementation + + MetaObject IDynamicObject.GetMetaObject(Expression parameter) { + return new Meta(parameter, this); + } + + private sealed class Meta : MetaObject { + internal Meta(Expression parameter, ScriptScope scope) + : base(parameter, Restrictions.Empty, scope) { + } + + // TODO: support for IgnoreCase in underlying ScriptScope APIs + public override MetaObject BindGetMember(GetMemberBinder action) { + var result = Expression.Variable(typeof(object), "result"); + var fallback = action.FallbackGetMember(this); + + return new MetaObject( + Expression.Block( + new ParameterExpression[] { result }, + Expression.Condition( + Expression.Call( + AstUtils.Convert(Expression, typeof(ScriptScope)), + typeof(ScriptScope).GetMethod("TryGetVariable", new[] { typeof(string), typeof(object).MakeByRefType() }), + Expression.Constant(action.Name), + result + ), + result, + Expression.Convert(fallback.Expression, typeof(object)) + ) + ), + Restrictions.GetTypeRestriction(Expression, typeof(ScriptScope)).Merge(fallback.Restrictions) + ); + } + + // TODO: support for IgnoreCase in underlying ScriptScope APIs + public override MetaObject BindSetMember(SetMemberBinder action, MetaObject value) { + return new MetaObject( + Expression.Call( + AstUtils.Convert(Expression, typeof(ScriptScope)), + typeof(ScriptScope).GetMethod("SetVariable", new[] { typeof(string), typeof(object) }), + Expression.Constant(action.Name), + Expression.Convert(value.Expression, typeof(object)) + ), + Restrictions.Merge(value.Restrictions).Merge(Restrictions.GetTypeRestriction(Expression, typeof(ScriptScope))) + ); + } + + // TODO: support for IgnoreCase in underlying ScriptScope APIs + public override MetaObject BindDeleteMember(DeleteMemberBinder action) { + var fallback = action.FallbackDeleteMember(this); + return new MetaObject( + Expression.Condition( + Expression.Call( + AstUtils.Convert(Expression, typeof(ScriptScope)), + typeof(ScriptScope).GetMethod("RemoveVariable"), + Expression.Constant(action.Name) + ), + Expression.Empty(), + Expression.Convert(fallback.Expression, typeof(void)) + ), + Restrictions.Merge(Restrictions.GetTypeRestriction(Expression, typeof(ScriptScope))).Merge(fallback.Restrictions) + ); + } + + // TODO: support for IgnoreCase in underlying ScriptScope APIs + public override MetaObject BindInvokeMember(InvokeMemberBinder action, MetaObject[] args) { + var fallback = action.FallbackInvokeMember(this, args); + var result = Expression.Variable(typeof(object), "result"); + + var fallbackInvoke = action.FallbackInvoke(new MetaObject(result, Restrictions.Empty), args, null); + + return new MetaObject( + Expression.Block( + new ParameterExpression[] { result }, + Expression.Condition( + Expression.Call( + AstUtils.Convert(Expression, typeof(ScriptScope)), + typeof(ScriptScope).GetMethod("TryGetVariable", new[] { typeof(string), typeof(object).MakeByRefType() }), + Expression.Constant(action.Name), + result + ), + Expression.Convert(fallbackInvoke.Expression, typeof(object)), + Expression.Convert(fallback.Expression, typeof(object)) + ) + ), + Restrictions.Combine(args).Merge(Restrictions.GetTypeRestriction(Expression, typeof(ScriptScope))).Merge(fallback.Restrictions) + ); + } + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptSource.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptSource.cs new file mode 100644 index 0000000000..be6ca69779 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/ScriptSource.cs @@ -0,0 +1,299 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Diagnostics; +using System.Runtime.Remoting; +using System.Dynamic; +using System.Security.Permissions; +using System.Text; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Hosting { + /// + /// Hosting counterpart for . + /// + [DebuggerDisplay("{Path ?? \"\"}")] + public sealed class ScriptSource +#if !SILVERLIGHT + : MarshalByRefObject +#endif + { + private readonly ScriptEngine _engine; + private readonly SourceUnit _unit; + + internal SourceUnit SourceUnit { + get { return _unit; } + } + + /// + /// Identification of the source unit. Assigned by the host. + /// The format and semantics is host dependent (could be a path on file system or URL). + /// null for anonymous script source. + /// Cannot be an empty string. + /// + public string Path { + get { return _unit.Path; } + } + + public SourceCodeKind Kind { + get { return _unit.Kind; } + } + + public ScriptEngine Engine { + get { return _engine; } + } + + internal ScriptSource(ScriptEngine engine, SourceUnit sourceUnit) { + Assert.NotNull(engine, sourceUnit); + _unit = sourceUnit; + _engine = engine; + } + + #region Compilation and Execution + + /// + /// Compile the ScriptSource into CompileCode object that can be executed + /// repeatedly in its default scope or in other scopes without having to recompile the code. + /// + /// Code cannot be compiled. + public CompiledCode Compile() { + return CompileInternal(null, null); + } + + /// + /// Errors are reported to the specified listener. + /// Returns null if the parser cannot compile the code due to errors. + /// + public CompiledCode Compile(ErrorListener errorListener) { + ContractUtils.RequiresNotNull(errorListener, "errorListener"); + + return CompileInternal(null, errorListener); + } + + /// + /// Errors are reported to the specified listener. + /// Returns null if the parser cannot compile the code due to error(s). + /// + public CompiledCode Compile(CompilerOptions compilerOptions) { + ContractUtils.RequiresNotNull(compilerOptions, "compilerOptions"); + + return CompileInternal(compilerOptions, null); + } + + /// + /// Errors are reported to the specified listener. + /// Returns null if the parser cannot compile the code due to error(s). + /// + public CompiledCode Compile(CompilerOptions compilerOptions, ErrorListener errorListener) { + ContractUtils.RequiresNotNull(errorListener, "errorListener"); + ContractUtils.RequiresNotNull(compilerOptions, "compilerOptions"); + + return CompileInternal(compilerOptions, errorListener); + } + + private CompiledCode CompileInternal(CompilerOptions compilerOptions, ErrorListener errorListener) { + ErrorSink errorSink = new ErrorListenerProxySink(this, errorListener); + ScriptCode code = _unit.Compile(compilerOptions ?? _unit.LanguageContext.GetCompilerOptions(), errorSink); + + return (code != null) ? new CompiledCode(_engine, code) : null; + } + + /// + /// Executes the code in the specified scope. + /// Returns an object that is the resulting value of running the code. + /// + /// When the ScriptSource is a file or statement, the engine decides what is + /// an appropriate value to return. Some languages return the value produced + /// by the last expression or statement, but languages that are not expression + /// based may return null. + /// + /// Code cannot be compiled. + public object Execute(ScriptScope scope) { + ContractUtils.RequiresNotNull(scope, "scope"); + + return _unit.Execute(scope.Scope, ErrorSink.Default); + } + + /// + /// Executes the code in an empty scope. + /// + public object Execute() { + return _unit.Execute(new Scope(), ErrorSink.Default); + } + + /// + /// Executes the code in a specified scope and converts the result to the specified type. + /// The conversion is language specific. + /// + public T Execute(ScriptScope scope) { + return _engine.Operations.ConvertTo(Execute(scope)); + } + + /// + /// Executes the code in an empty scope and converts the result to the specified type. + /// The conversion is language specific. + /// + public T Execute() { + return _engine.Operations.ConvertTo(Execute()); + } + +#if !SILVERLIGHT + /// + /// Executes the code in the specified scope and return a result. + /// Returns an ObjectHandle wrapping the resulting value of running the code. + /// + public ObjectHandle ExecuteAndWrap(ScriptScope scope) { + return new ObjectHandle(Execute(scope)); + } + + /// + /// Executes the code in an empty scope. + /// Returns an ObjectHandle wrapping the resulting value of running the code. + /// + public ObjectHandle ExecuteAndWrap() { + return new ObjectHandle(Execute()); + } +#endif + + /// + /// Runs a specified code as if it was a program launched from OS command shell. + /// and returns a process exit code indicating the success or error condition + /// of executing the code. + /// + /// Exact behavior depends on the language. Some languages have a dedicated "exit" exception that + /// carries the exit code, in which case the exception is cought and the exit code is returned. + /// The default behavior returns the result of program's execution converted to an integer + /// using a language specific conversion. + /// + /// Code cannot be compiled. + public int ExecuteProgram() { + return _unit.LanguageContext.ExecuteProgram(_unit); + } + + #endregion + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public ScriptCodeParseResult GetCodeProperties() { + return _unit.GetCodeProperties(); + } + + public ScriptCodeParseResult GetCodeProperties(CompilerOptions options) { + return _unit.GetCodeProperties(options); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public SourceCodeReader GetReader() { + return _unit.GetReader(); + } + + /// + /// Detects the encoding of the content. + /// + /// + /// An encoding that is used by the reader of the script source to transcode its content to Unicode text. + /// Null if the content is already textual and no transcoding is performed. + /// + /// + /// Note that the default encoding specified when the script source is created could be overridden by + /// an encoding that is found in the content preamble (Unicode BOM or a language specific encoding preamble). + /// In that case the preamble encoding is returned. Otherwise, the default encoding is returned. + /// + /// An I/O error occurs. + public Encoding DetectEncoding() { + using (var reader = _unit.GetReader()) { + return reader.Encoding; + } + } + + /// + /// Reads specified range of lines (or less) from the source unit. + /// + /// 1-based number of the first line to fetch. + /// The number of lines to fetch. + /// + /// Which character sequences are considered line separators is language specific. + /// If language doesn't specify otherwise "\r", "\n", "\r\n" are recognized line separators. + /// + /// An I/O error occurs. + public string[] GetCodeLines(int start, int count) { + return _unit.GetCodeLines(start, count); + } + + /// + /// Reads a specified line. + /// + /// 1-based line number. + /// Line content. Line separator is not included. + /// An I/O error occurs. + /// + /// Which character sequences are considered line separators is language specific. + /// If language doesn't specify otherwise "\r", "\n", "\r\n" are recognized line separators. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public string GetCodeLine(int line) { + return _unit.GetCodeLine(line); + } + + /// + /// Gets script source content. + /// + /// Entire content. + /// An I/O error occurs. + /// + /// The result includes language specific preambles (e.g. "#coding:UTF-8" encoding preamble recognized by Ruby), + /// but not the preamble defined by the content encoding (e.g. BOM). + /// The entire content of the source unit is encoded by single encoding (if it is read from binary stream). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public string GetCode() { + return _unit.GetCode(); + } + + // TODO: can this be removed? no one uses it + #region line number mapping + + public int MapLine(int line) { + return _unit.MapLine(line); + } + + public SourceSpan MapLine(SourceSpan span) { + return new SourceSpan(_unit.MakeLocation(span.Start), _unit.MakeLocation(span.End)); + } + + public SourceLocation MapLine(SourceLocation location) { + return _unit.MakeLocation(location); + } + + // TODO: remove this? we don't support file mapping + // (but it's still in the hosting spec) + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "line")] + public string MapLinetoFile(int line) { + return _unit.Path; + } + + #endregion + +#if !SILVERLIGHT + // TODO: Figure out what is the right lifetime + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] + public override object InitializeLifetimeService() { + return null; + } +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/BasicConsole.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/BasicConsole.cs new file mode 100644 index 0000000000..ed3a6edeac --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/BasicConsole.cs @@ -0,0 +1,182 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Dynamic; +using Microsoft.Scripting.Utils; +using System.Threading; + +namespace Microsoft.Scripting.Hosting.Shell { + + public class BasicConsole : IConsole, IDisposable { + + private TextWriter _output; + private TextWriter _errorOutput; + private AutoResetEvent _ctrlCEvent; + private Thread _creatingThread; + + public TextWriter Output { + get { return _output; } + set { + ContractUtils.RequiresNotNull(value, "value"); + _output = value; + } + } + + public TextWriter ErrorOutput { + get { return _errorOutput; } + set { + ContractUtils.RequiresNotNull(value, "value"); + _errorOutput = value; + } + } + + protected AutoResetEvent CtrlCEvent { + get { return _ctrlCEvent; } + set { _ctrlCEvent = value; } + } + + protected Thread CreatingThread { + get { return _creatingThread; } + set { _creatingThread = value; } + } + + private ConsoleColor _promptColor; + private ConsoleColor _outColor; + private ConsoleColor _errorColor; + private ConsoleColor _warningColor; + + public BasicConsole(bool colorful) { + _output = System.Console.Out; + _errorOutput = System.Console.Error; + SetupColors(colorful); + + _creatingThread = Thread.CurrentThread; + +#if !SILVERLIGHT // ConsoleCancelEventHandler + Console.CancelKeyPress += new ConsoleCancelEventHandler(delegate(object sender, ConsoleCancelEventArgs e) { + if (e.SpecialKey == ConsoleSpecialKey.ControlC) { + e.Cancel = true; + _ctrlCEvent.Set(); + _creatingThread.Abort(new KeyboardInterruptException("")); + } + }); +#endif + _ctrlCEvent = new AutoResetEvent(false); + } + + private void SetupColors(bool colorful) { + + if (colorful) { + _promptColor = PickColor(ConsoleColor.Gray, ConsoleColor.White); + _outColor = PickColor(ConsoleColor.Green, ConsoleColor.White); + _errorColor = PickColor(ConsoleColor.Red, ConsoleColor.White); + _warningColor = PickColor(ConsoleColor.Yellow, ConsoleColor.White); + } else { +#if !SILVERLIGHT + _promptColor = _outColor = _errorColor = _warningColor = Console.ForegroundColor; +#endif + } + } + + private static ConsoleColor PickColor(ConsoleColor best, ConsoleColor other) { +#if SILVERLIGHT + return best; +#else + if (Console.BackgroundColor != best) { + return best; + } + + return other; +#endif + } + + protected void WriteColor(TextWriter output, string str, ConsoleColor c) { +#if !SILVERLIGHT // Console.ForegroundColor + ConsoleColor origColor = Console.ForegroundColor; + Console.ForegroundColor = c; +#endif + output.Write(str); + output.Flush(); + +#if !SILVERLIGHT // Console.ForegroundColor + Console.ForegroundColor = origColor; +#endif + } + + #region IConsole Members + + public virtual string ReadLine(int autoIndentSize) { + Write("".PadLeft(autoIndentSize), Style.Prompt); + + string res = Console.In.ReadLine(); + if (res == null) { + // we have a race - the Ctrl-C event is delivered + // after ReadLine returns. We need to wait for a little + // bit to see which one we got. This will cause a slight + // delay when shutting down the process via ctrl-z, but it's + // not really perceptible. In the ctrl-C case we will return + // as soon as the event is signaled. +#if SILVERLIGHT + if (_ctrlCEvent != null && _ctrlCEvent.WaitOne(100)) +#else + if (_ctrlCEvent != null && _ctrlCEvent.WaitOne(100, false)) +#endif + { + // received ctrl-C + return ""; + } else { + // received ctrl-Z + return null; + } + } + return "".PadLeft(autoIndentSize) + res; + } + + public virtual void Write(string text, Style style) { + switch (style) { + case Style.Prompt: WriteColor(_output, text, _promptColor); break; + case Style.Out: WriteColor(_output, text, _outColor); break; + case Style.Error: WriteColor(_errorOutput, text, _errorColor); break; + case Style.Warning: WriteColor(_errorOutput, text, _warningColor); break; + } + } + + public void WriteLine(string text, Style style) { + Write(text + Environment.NewLine, style); + } + + public void WriteLine() { + Write(Environment.NewLine, Style.Out); + } + + #endregion + + #region IDisposable Members + + public void Dispose() { + if (_ctrlCEvent != null) { + _ctrlCEvent.Close(); + } + + GC.SuppressFinalize(this); + } + + #endregion + } + +} + diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/CommandLine.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/CommandLine.cs new file mode 100644 index 0000000000..b758aae88e --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/CommandLine.cs @@ -0,0 +1,449 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using Microsoft.Scripting.Hosting.Providers; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting.Shell { + /// + /// Command line hosting service. + /// + public class CommandLine { + private LanguageContext _language; + private IConsole _console; + private ConsoleOptions _options; + private ScriptScope _scope; + private ScriptEngine _engine; + private ICommandDispatcher _commandDispatcher; + private int? _terminatingExitCode; + + protected IConsole Console { get { return _console; } } + protected ConsoleOptions Options { get { return _options; } } + protected ScriptEngine Engine { get { return _engine; } } + public ScriptScope ScriptScope { get { return _scope; } protected set { _scope = value; } } + + /// + /// Scope is not remotable, and this only works in the same AppDomain. + /// + protected Scope Scope { + get { + if (_scope == null) { + return null; + } + return _scope.Scope; + } + set { + _scope = new ScriptScope(_engine, value); + } + } + + protected LanguageContext Language { + get { + // LanguageContext is not remotable, and this only works in the same AppDomain. + if (_language == null) { + _language = HostingHelpers.GetLanguageContext(_engine); + } + return _language; + } + } + + protected virtual string Prompt { get { return ">>> "; } } + public virtual string PromptContinuation { get { return "... "; } } + protected virtual string Logo { get { return null; } } + + public CommandLine() { + } + + protected virtual void Initialize() { + if (_commandDispatcher == null) { + _commandDispatcher = CreateCommandDispatcher(); + } + } + + protected virtual Scope CreateScope() { + return new Scope(); + } + + protected virtual ICommandDispatcher CreateCommandDispatcher() { + return new SimpleCommandDispatcher(); + } + + public virtual void Terminate(int exitCode) { + // The default implementation just sets a flag. Derived types can support better termination + _terminatingExitCode = exitCode; + } + + /// + /// Executes the comand line - depending upon the options provided we will + /// either run a single file, a single command, or enter the interactive loop. + /// + public int Run(ScriptEngine engine, IConsole console, ConsoleOptions options) { + ContractUtils.RequiresNotNull(engine, "engine"); + ContractUtils.RequiresNotNull(console, "console"); + ContractUtils.RequiresNotNull(options, "options"); + + _engine = engine; + _options = options; + _console = console; + + Initialize(); + + try { + return Run(); + +#if !SILVERLIGHT // ThreadAbortException.ExceptionState + } catch (System.Threading.ThreadAbortException tae) { + if (tae.ExceptionState is KeyboardInterruptException) { + Thread.ResetAbort(); + return -1; + } else { + throw; + } +#endif + } finally { + Shutdown(); + _engine = null; + _options = null; + _console = null; + } + } + + /// + /// Runs the command line. Languages can override this to provide custom behavior other than: + /// 1. Running a single command + /// 2. Running a file + /// 3. Entering the interactive console loop. + /// + /// + protected virtual int Run() { + int result; + + if (_options.Command != null) { + result = RunCommand(_options.Command); + } else if (_options.FileName != null) { + result = RunFile(_options.FileName); + } else { + return RunInteractive(); + } + + if (_options.Introspection) { + return RunInteractiveLoop(); + } + + return result; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + protected virtual void Shutdown() { + try { + _engine.Runtime.Shutdown(); + } catch (Exception e) { + UnhandledException(e); + } + } + + protected virtual int RunFile(string fileName) { + return RunFile(_engine.CreateScriptSourceFromFile(fileName)); + } + + protected virtual int RunCommand(string command) { + return RunFile(_engine.CreateScriptSourceFromString(command, SourceCodeKind.Statements)); + } + + /// + /// Runs the specified filename + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + protected virtual int RunFile(ScriptSource source) { + int result = 1; + + if (Options.HandleExceptions) { + try { + result = source.ExecuteProgram(); + } catch (Exception e) { + UnhandledException(e); + } + } else { + result = source.ExecuteProgram(); + } + + return result; + } + + protected void PrintLogo() { + if (Logo != null) { + _console.Write(Logo, Style.Out); + } + } + + #region Interactivity + + /// + /// Starts the interactive loop. Performs any initialization necessary before + /// starting the loop and then calls RunInteractiveLoop to start the loop. + /// + /// Returns the exit code when the interactive loop is completed. + /// + protected virtual int RunInteractive() { + PrintLogo(); + return RunInteractiveLoop(); + } + + /// + /// Runs the interactive loop. Repeatedly parse and run interactive actions + /// until an exit code is received. If any exceptions are unhandled displays + /// them to the console + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + protected int RunInteractiveLoop() { + if (_scope == null) { + _scope = _engine.CreateScope(); + } + +#if !SILVERLIGHT // Remote console + string remoteRuntimeChannel = _options.RemoteRuntimeChannel; + if (remoteRuntimeChannel != null) { + // Publish the ScriptScope so that the host can use it + Remote.RemoteRuntimeServer.StartServer(remoteRuntimeChannel, _scope); + return 0; + } +#endif + int? res = null; + + do { + if (Options.HandleExceptions) { + try { + res = TryInteractiveAction(); +#if SILVERLIGHT + } catch (ExitProcessException e) { + res = e.ExitCode; +#endif + } catch (Exception e) { + if (CommandLine.IsFatalException(e)) { + // Some exceptions are too dangerous to try to catch + throw; + } + + // There should be no unhandled exceptions in the interactive session + // We catch all (most) exceptions here, and just display it, + // and keep on going + UnhandledException(e); + } + } else { + res = TryInteractiveAction(); + } + + } while (res == null); + + return res.Value; + } + + internal static bool IsFatalException(Exception e) { + ThreadAbortException tae = e as ThreadAbortException; + if (tae != null) { +#if SILVERLIGHT // ThreadAbortException.ExceptionState + return true; +#else + if ((tae.ExceptionState as KeyboardInterruptException) == null) { + return true; + } +#endif + } + return false; + } + + protected virtual void UnhandledException(Exception e) { + ExceptionOperations exceptionOperations = _engine.GetService(); + _console.WriteLine(exceptionOperations.FormatException(e), Style.Error); + } + + /// + /// Attempts to run a single interaction and handle any language-specific + /// exceptions. Base classes can override this and call the base implementation + /// surrounded with their own exception handling. + /// + /// Returns null if successful and execution should continue, or an exit code. + /// + protected virtual int? TryInteractiveAction() { + int? result = null; + + try { + result = RunOneInteraction(); +#if SILVERLIGHT // ThreadAbortException.ExceptionState + } catch (ThreadAbortException) { +#else + } catch (ThreadAbortException tae) { + KeyboardInterruptException pki = tae.ExceptionState as KeyboardInterruptException; + if (pki != null) { + UnhandledException(tae); + Thread.ResetAbort(); + } else { + throw; + } +#endif + } + + return result; + } + + /// + /// Parses a single interactive command or a set of statements and executes it. + /// + /// Returns null if successful and execution should continue, or the appropiate exit code. + /// + /// We check if the code read is an interactive command or statements is by checking for NewLine + /// If the code contains NewLine, it's a set of statements (most probably from SendToConsole) + /// If the code does not contain a NewLine, it's an interactive command typed by the user at the prompt + /// + private int? RunOneInteraction() { + bool continueInteraction; + string s = ReadStatement(out continueInteraction); + + if (continueInteraction == false) { + return (_terminatingExitCode == null) ? 0 : _terminatingExitCode; + } + + if (String.IsNullOrEmpty(s)) { + // Is it an empty line? + _console.Write(String.Empty, Style.Out); + return null; + } + + ExecuteCommand(_engine.CreateScriptSourceFromString(s, + (s.Contains(Environment.NewLine))? SourceCodeKind.Statements : SourceCodeKind.InteractiveCode)); + return null; + } + + protected object ExecuteCommand(ScriptSource source) { + ErrorListener errorListener = new ErrorSinkProxyListener(ErrorSink); + CompiledCode compiledCode = source.Compile(_engine.GetCompilerOptions(_scope), errorListener); + return _commandDispatcher.Execute(compiledCode, _scope); + } + + protected virtual ErrorSink ErrorSink { + get { return ErrorSink.Default; } + } + + /// + /// Private helper function to see if we should treat the current input as a blank link. + /// + /// We do this if we only have auto-indent text. + /// + private static bool TreatAsBlankLine(string line, int autoIndentSize) { + if (line.Length == 0) return true; + if (autoIndentSize != 0 && line.Trim().Length == 0 && line.Length == autoIndentSize) { + return true; + } + + return false; + } + + /// + /// Read a statement, which can potentially be a multiple-line statement suite (like a class declaration). + /// + /// Should the console session continue, or did the user indicate + /// that it should be terminated? + /// Expression to evaluate. null for empty input + protected string ReadStatement(out bool continueInteraction) { + StringBuilder b = new StringBuilder(); + int autoIndentSize = 0; + + _console.Write(Prompt, Style.Prompt); + + while (true) { + string line = ReadLine(autoIndentSize); + continueInteraction = true; + + if (line == null || (_terminatingExitCode != null)) { + continueInteraction = false; + return null; + } + + bool allowIncompleteStatement = TreatAsBlankLine(line, autoIndentSize); + b.Append(line); + b.Append("\n"); + + string code = b.ToString(); + + ScriptSource command = _engine.CreateScriptSourceFromString(code, + (code.Contains(Environment.NewLine))? SourceCodeKind.Statements : SourceCodeKind.InteractiveCode); + ScriptCodeParseResult props = command.GetCodeProperties(_engine.GetCompilerOptions(_scope)); + + if (SourceCodePropertiesUtils.IsCompleteOrInvalid(props, allowIncompleteStatement)) { + return props != ScriptCodeParseResult.Empty ? code : null; + } + + if (_options.AutoIndent && _options.AutoIndentSize != 0) { + autoIndentSize = GetNextAutoIndentSize(code); + } + + // Keep on reading input + _console.Write(PromptContinuation, Style.Prompt); + } + } + + /// + /// Gets the next level for auto-indentation + /// + protected virtual int GetNextAutoIndentSize(string text) { + return 0; + } + + protected virtual string ReadLine(int autoIndentSize) { + return _console.ReadLine(autoIndentSize); + } + + internal protected virtual TextWriter GetOutputWriter(bool isErrorOutput) { + return isErrorOutput ? System.Console.Error : System.Console.Out; + } + + //private static DynamicSite> _memberCompletionSite = + // new DynamicSite>(OldDoOperationAction.Make(Operators.GetMemberNames)); + + public IList GetMemberNames(string code) { + object value = _engine.CreateScriptSourceFromString(code, SourceCodeKind.Expression).Execute(_scope); + return _engine.Operations.GetMemberNames(value); + // TODO: why doesn't this work ??? + //return _memberCompletionSite.Invoke(new CodeContext(_scope, _engine), value); + } + + public virtual IList GetGlobals(string name) { + List res = new List(); + foreach (SymbolId scopeName in _scope.Scope.Keys) { + string strName = SymbolTable.IdToString(scopeName); + if (strName.StartsWith(name)) { + res.Add(strName); + } + } + + return res; + } + + #endregion + + class SimpleCommandDispatcher : ICommandDispatcher { + public object Execute(CompiledCode compiledCode, ScriptScope scope) { + return compiledCode.Execute(scope); + } + } + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ConsoleHost.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ConsoleHost.cs new file mode 100644 index 0000000000..19823e72bf --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ConsoleHost.cs @@ -0,0 +1,430 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting.Shell { + /// + /// Core functionality to implement an interactive console. This should be derived for concrete implementations + /// + public abstract class ConsoleHost { + private int _exitCode; + private ConsoleHostOptionsParser _optionsParser; + private ScriptRuntime _runtime; + private ScriptEngine _engine; + internal OptionsParser _languageOptionsParser; + private IConsole _console; + private CommandLine _commandLine; + + public ConsoleHostOptions Options { get { return _optionsParser.Options; } } + public ScriptRuntimeSetup RuntimeSetup { get { return _optionsParser.RuntimeSetup; } } + + public ScriptEngine Engine { get { return _engine; } protected set { _engine = value; } } + public ScriptRuntime Runtime { get { return _runtime; } protected set { _runtime = value; } } + protected int ExitCode { get { return _exitCode; } set { _exitCode = value; } } + protected ConsoleHostOptionsParser ConsoleHostOptionsParser { get { return _optionsParser; } set { _optionsParser = value; } } + protected IConsole ConsoleIO { get { return _console; } set { _console = value; } } + protected CommandLine CommandLine { get { return _commandLine; } } + + protected ConsoleHost() { + } + + + /// + /// Console Host entry-point .exe name. + /// + protected virtual string ExeName { + get { +#if !SILVERLIGHT + Assembly entryAssembly = Assembly.GetEntryAssembly(); + //Can be null if called from unmanaged code (VS integration scenario) + return entryAssembly != null ? entryAssembly.GetName().Name : "ConsoleHost"; +#else + return "ConsoleHost"; +#endif + } + } + + #region Customization + + protected virtual void ParseHostOptions(string[] args) { + _optionsParser.Parse(args); + } + + protected virtual ScriptRuntimeSetup CreateRuntimeSetup() { + var setup = ScriptRuntimeSetup.ReadConfiguration(); + + string provider = Provider.AssemblyQualifiedName; + + if (!setup.LanguageSetups.Any(s => s.TypeName == provider)) { + var languageSetup = CreateLanguageSetup(); + if (languageSetup != null) { + setup.LanguageSetups.Add(languageSetup); + } + } + + return setup; + } + + protected virtual LanguageSetup CreateLanguageSetup() { + return null; + } + + protected virtual PlatformAdaptationLayer PlatformAdaptationLayer { + get { return PlatformAdaptationLayer.Default; } + } + + protected virtual Type Provider { + get { return null; } + } + + private string GetLanguageProvider(ScriptRuntimeSetup setup) { + var providerType = Provider; + if (providerType != null) { + return providerType.AssemblyQualifiedName; + } + + if (Options.HasLanguageProvider) { + return Options.LanguageProvider; + } + + if (Options.RunFile != null) { + string ext = Path.GetExtension(Options.RunFile); + foreach (var lang in setup.LanguageSetups) { + if (lang.FileExtensions.Any(e => DlrConfiguration.FileExtensionComparer.Equals(e, ext))) { + return lang.TypeName; + } + } + } + + throw new InvalidOptionException("No language specified."); + } + + protected virtual CommandLine CreateCommandLine() { + return new CommandLine(); + } + + protected virtual OptionsParser CreateOptionsParser() { + return new OptionsParser(); + } + + protected virtual IConsole CreateConsole(ScriptEngine engine, CommandLine commandLine, ConsoleOptions options) { + ContractUtils.RequiresNotNull(options, "options"); + + if (options.TabCompletion) { + return CreateSuperConsole(commandLine, options.ColorfulConsole); + } else { + return new BasicConsole(options.ColorfulConsole); + } + } + + // The advanced console functions are in a special non-inlined function so that + // dependencies are pulled in only if necessary. + [MethodImplAttribute(MethodImplOptions.NoInlining)] + private static IConsole CreateSuperConsole(CommandLine commandLine, bool isColorful) { + return new SuperConsole(commandLine, isColorful); + } + + #endregion + + /// + /// Request (from another thread) the console REPL loop to terminate + /// + /// The caller can specify the exitCode corresponding to the event triggering + /// the termination. This will be returned from CommandLine.Run + public virtual void Terminate(int exitCode) { + _commandLine.Terminate(exitCode); + } + + /// + /// To be called from entry point. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public virtual int Run(string[] args) { + + var runtimeSetup = CreateRuntimeSetup(); + var options = new ConsoleHostOptions(); + _optionsParser = new ConsoleHostOptionsParser(options, runtimeSetup); + + try { + ParseHostOptions(args); + } catch (InvalidOptionException e) { + Console.Error.WriteLine("Invalid argument: " + e.Message); + return _exitCode = 1; + } + + SetEnvironment(); + + string provider = GetLanguageProvider(runtimeSetup); + + LanguageSetup languageSetup = null; + foreach (var language in runtimeSetup.LanguageSetups) { + if (language.TypeName == provider) { + languageSetup = language; + } + } + if (languageSetup == null) { + // the language doesn't have a setup -> create a default one: + languageSetup = new LanguageSetup(Provider.AssemblyQualifiedName, Provider.Name); + runtimeSetup.LanguageSetups.Add(languageSetup); + } + + // inserts search paths for all languages (/paths option): + InsertSearchPaths(runtimeSetup.Options, Options.SourceUnitSearchPaths); + + _languageOptionsParser = CreateOptionsParser(); + + try { + _languageOptionsParser.Parse(Options.IgnoredArgs.ToArray(), runtimeSetup, languageSetup, PlatformAdaptationLayer); + } catch (InvalidOptionException e) { + Console.Error.WriteLine(e.Message); + return _exitCode = -1; + } + + _runtime = new ScriptRuntime(runtimeSetup); + + try { + _engine = _runtime.GetEngineByTypeName(provider); + } catch (Exception e) { + Console.Error.WriteLine(e.Message); + return _exitCode = 1; + } + + Execute(); + return _exitCode; + } + + private static void InsertSearchPaths(IDictionary options, ICollection paths) { + if (options != null && paths != null && paths.Count > 0) { + var existingPaths = new List(LanguageOptions.GetSearchPathsOption(options) ?? (IEnumerable)ArrayUtils.EmptyStrings); + existingPaths.InsertRange(0, paths); + options["SearchPaths"] = existingPaths; + } + } + + #region Printing help + + protected virtual void PrintHelp() { + Console.WriteLine(GetHelp()); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + protected virtual string GetHelp() { + StringBuilder sb = new StringBuilder(); + + string[,] optionsHelp = Options.GetHelp(); + + sb.AppendLine(String.Format("Usage: {0}.exe [] [--] []", ExeName)); + sb.AppendLine(); + + sb.AppendLine("DLR options (both slash or dash could be used to prefix options):"); + ArrayUtils.PrintTable(sb, optionsHelp); + sb.AppendLine(); + + sb.AppendLine("Language specific command line:"); + PrintLanguageHelp(sb); + sb.AppendLine(); + + return sb.ToString(); + } + + public void PrintLanguageHelp(StringBuilder output) { + ContractUtils.RequiresNotNull(output, "output"); + + string commandLine, comments; + string[,] options, environmentVariables; + + CreateOptionsParser().GetHelp(out commandLine, out options, out environmentVariables, out comments); + + // only display language specific options if one or more optinos exists. + if (commandLine != null || options != null || environmentVariables != null || comments != null) { + if (commandLine != null) { + output.AppendLine(commandLine); + output.AppendLine(); + } + + if (options != null) { + output.AppendLine("Options:"); + ArrayUtils.PrintTable(output, options); + output.AppendLine(); + } + + if (environmentVariables != null) { + output.AppendLine("Environment variables:"); + ArrayUtils.PrintTable(output, environmentVariables); + output.AppendLine(); + } + + if (comments != null) { + output.Append(comments); + output.AppendLine(); + } + + output.AppendLine(); + } + } + + #endregion + + private void Execute() { +#if !SILVERLIGHT + if (_languageOptionsParser.CommonConsoleOptions.IsMta) { + Thread thread = new Thread(ExecuteInternal); + thread.SetApartmentState(ApartmentState.MTA); + thread.Start(); + thread.Join(); + return; + } +#endif + ExecuteInternal(); + } + + protected virtual void ExecuteInternal() { + Debug.Assert(_engine != null); + + switch (Options.RunAction) { + case ConsoleHostOptions.Action.None: + case ConsoleHostOptions.Action.RunConsole: + _exitCode = RunCommandLine(); + break; + + case ConsoleHostOptions.Action.RunFile: + _exitCode = RunFile(); + break; + + default: + throw Assert.Unreachable; + } + } + + private void SetEnvironment() { + Debug.Assert(Options.EnvironmentVars != null); + +#if !SILVERLIGHT + foreach (string env in Options.EnvironmentVars) { + if (!String.IsNullOrEmpty(env)) { + string[] var_def = env.Split('='); + System.Environment.SetEnvironmentVariable(var_def[0], (var_def.Length > 1) ? var_def[1] : ""); + } + } +#endif + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private int RunFile() { + Debug.Assert(_engine != null); + + int result = 0; + try { + return _engine.CreateScriptSourceFromFile(Options.RunFile).ExecuteProgram(); +#if SILVERLIGHT + } catch (ExitProcessException e) { + result = e.ExitCode; +#endif + } catch (Exception e) { + UnhandledException(Engine, e); + result = 1; + } finally { + try { + Snippets.SaveAndVerifyAssemblies(); + } catch (Exception) { + result = 1; + } + } + + return result; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + private int RunCommandLine() { + Debug.Assert(_engine != null); + + _commandLine = CreateCommandLine(); + ConsoleOptions consoleOptions = _languageOptionsParser.CommonConsoleOptions; + + if (consoleOptions.PrintVersionAndExit) { + Console.WriteLine("{0} {1} on .NET {2}", Engine.Setup.DisplayName, Engine.LanguageVersion, typeof(String).Assembly.GetName().Version); + return 0; + } + + if (consoleOptions.PrintUsageAndExit) { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("Usage: {0}.exe ", ExeName); + PrintLanguageHelp(sb); + Console.Write(sb.ToString()); + return 0; + } + + if (_console == null) { + _console = CreateConsole(Engine, _commandLine, consoleOptions); + } + + int exitCode = 0; + try { + if (consoleOptions.HandleExceptions) { + try { + exitCode = _commandLine.Run(Engine, _console, consoleOptions); + } catch (Exception e) { + if (CommandLine.IsFatalException(e)) { + // Some exceptions are too dangerous to try to catch + throw; + } + UnhandledException(Engine, e); + exitCode = 1; + } + } else { + exitCode = _commandLine.Run(Engine, _console, consoleOptions); + } + } finally { + try { + Snippets.SaveAndVerifyAssemblies(); + } catch (Exception) { + exitCode = 1; + } + } + + return exitCode; + } + + protected virtual void UnhandledException(ScriptEngine engine, Exception e) { + Console.Error.Write("Unhandled exception"); + Console.Error.WriteLine(':'); + + ExceptionOperations es = engine.GetService(); + Console.Error.WriteLine(es.FormatException(e)); + } + + protected static void PrintException(TextWriter output, Exception e) { + Debug.Assert(output != null); + ContractUtils.RequiresNotNull(e, "e"); + + while (e != null) { + output.WriteLine(e); + e = e.InnerException; + } + } + } +} + diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ConsoleHostOptions.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ConsoleHostOptions.cs new file mode 100644 index 0000000000..e4fa5a3cd1 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ConsoleHostOptions.cs @@ -0,0 +1,56 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting.Utils; +using System.Reflection; + +namespace Microsoft.Scripting.Hosting.Shell { + + public class ConsoleHostOptions { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] // TODO: fix + public enum Action { + None, + RunConsole, + RunFile, + DisplayHelp + } + + private readonly List _ignoredArgs = new List(); + private readonly List _environmentVars = new List(); + + public List IgnoredArgs { get { return _ignoredArgs; } } + public string RunFile { get; set; } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] // TODO: fix + public string[] SourceUnitSearchPaths { get; set; } + public Action RunAction { get; set; } + public List EnvironmentVars { get { return _environmentVars; } } + public string LanguageProvider { get; set; } + public bool HasLanguageProvider { get; set; } + + public ConsoleHostOptions() { + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional")] // TODO: fix + public string[,] GetHelp() { + return new string[,] { + { "/help", "Displays this help." }, + { "/lang:", "Specify language by the associated extension (py, js, vb, rb). Determined by an extension of the first file. Defaults to IronPython." }, + { "/paths:", "Semicolon separated list of import paths (/run only)." }, + { "/setenv:", "Sets specified environment variables for the console process. Not available on Silverlight." }, + }; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ConsoleHostOptionsParser.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ConsoleHostOptionsParser.cs new file mode 100644 index 0000000000..68cadc3d5b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ConsoleHostOptionsParser.cs @@ -0,0 +1,150 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Globalization; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Hosting.Shell { + + public class ConsoleHostOptionsParser { + private readonly ConsoleHostOptions _options; + private readonly ScriptRuntimeSetup _runtimeSetup; + + public ConsoleHostOptions Options { get { return _options; } } + public ScriptRuntimeSetup RuntimeSetup { get { return _runtimeSetup; } } + + public ConsoleHostOptionsParser(ConsoleHostOptions options, ScriptRuntimeSetup runtimeSetup) { + ContractUtils.RequiresNotNull(options, "options"); + ContractUtils.RequiresNotNull(runtimeSetup, "runtimeSetup"); + + _options = options; + _runtimeSetup = runtimeSetup; + } + + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public void Parse(string[] args) { + ContractUtils.RequiresNotNull(args, "args"); + + int i = 0; + while (i < args.Length) { + string name, value; + string current = args[i++]; + ParseOption(current, out name, out value); + + switch (name) { + case "console": + _options.RunAction = ConsoleHostOptions.Action.RunConsole; + break; + + case "run": + OptionValueRequired(name, value); + + _options.RunAction = ConsoleHostOptions.Action.RunFile; + _options.RunFile = value; + break; + + case "lang": + OptionValueRequired(name, value); + + string provider = null; + foreach (var language in _runtimeSetup.LanguageSetups) { + if (language.Names.Any(n => DlrConfiguration.LanguageNameComparer.Equals(n, value))) { + provider = language.TypeName; + break; + } + } + if (provider == null) { + throw new InvalidOptionException(String.Format("Unknown language id '{0}'.", value)); + } + + _options.LanguageProvider = provider; + _options.HasLanguageProvider = true; + break; + + case "path": + case "paths": + OptionValueRequired(name, value); + _options.SourceUnitSearchPaths = value.Split(';'); + break; + + case "setenv": + OptionNotAvailableOnSilverlight(name); + _options.EnvironmentVars.AddRange(value.Split(';')); + break; + + // first unknown/non-option: + case null: + default: + _options.IgnoredArgs.Add(current); + goto case ""; + + // host/passthru argument separator + case "/": + case "": + // ignore all arguments starting with the next one (arguments are not parsed): + while (i < args.Length) { + _options.IgnoredArgs.Add(args[i++]); + } + break; + } + } + } + + /// + /// name == null means that the argument doesn't specify an option; the value contains the entire argument + /// name == "" means that the option name is empty (argument separator); the value is null then + /// + private void ParseOption(string arg, out string name, out string value) { + Debug.Assert(arg != null); + + int colon = arg.IndexOf(':'); + + if (colon >= 0) { + name = arg.Substring(0, colon); + value = arg.Substring(colon + 1); + } else { + name = arg; + value = null; + } + + if (name.StartsWith("--")) name = name.Substring("--".Length); + else if (name.StartsWith("-") && name.Length > 1) name = name.Substring("-".Length); + else if (name.StartsWith("/") && name.Length > 1) name = name.Substring("/".Length); + else { + value = name; + name = null; + } + + if (name != null) { + name = name.ToLower(CultureInfo.InvariantCulture); + } + } + + protected void OptionValueRequired(string optionName, string value) { + if (value == null) { + throw new InvalidOptionException(String.Format(CultureInfo.CurrentCulture, "Argument expected for the {0} option.", optionName)); + } + } + + [Conditional("SILVERLIGHT")] + private void OptionNotAvailableOnSilverlight(string optionName) { + throw new InvalidOptionException(String.Format("Option '{0}' is not available on Silverlight.", optionName)); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ConsoleOptions.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ConsoleOptions.cs new file mode 100644 index 0000000000..0d8adddf1a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ConsoleOptions.cs @@ -0,0 +1,139 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting.Shell { + + [Serializable] + public class ConsoleOptions { + private string _command; + private string _filename; + private bool _printVersionAndExit; + private int _autoIndentSize = 4; + private string[] _remainingArgs; + private bool _introspection; + private bool _autoIndent; + private bool _handleExceptions = true; + private bool _tabCompletion; + private bool _colorfulConsole; + private bool _printUsageAndExit; + private bool _isMta; +#if !SILVERLIGHT // Remote console + private string _remoteRuntimeChannel; +#endif + + public bool AutoIndent { + get { return _autoIndent; } + set { _autoIndent = value; } + } + + public bool HandleExceptions { + get { return _handleExceptions; } + set { _handleExceptions = value; } + } + + public bool TabCompletion { + get { return _tabCompletion; } + set { _tabCompletion = value; } + } + + public bool ColorfulConsole { + get { return _colorfulConsole; } + set { _colorfulConsole = value; } + } + + public bool PrintUsageAndExit { + get { return _printUsageAndExit; } + set { _printUsageAndExit = value; } + } + + /// + /// Literal script command given using -c option + /// + public string Command { + get { return _command; } + set { _command = value; } + } + + /// + /// Filename to execute passed on the command line options. + /// + public string FileName { + get { return _filename; } + set { _filename = value; } + } + + /// + /// Only print the version of the script interpreter and exit + /// + public bool PrintVersionAndExit { + get { return _printVersionAndExit; } + set { _printVersionAndExit = value; } + } + + public int AutoIndentSize { + get { return _autoIndentSize; } + set { _autoIndentSize = value; } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] // TODO: fix + public string[] RemainingArgs { + get { return _remainingArgs; } + set { _remainingArgs = value; } + } + + public bool Introspection { + get { return _introspection; } + set { _introspection = value; } + } + + public bool IsMta { + get { return _isMta; } + set { _isMta = value; } + } + +#if !SILVERLIGHT // Remote console + public string RemoteRuntimeChannel { + get { return _remoteRuntimeChannel; } + set { _remoteRuntimeChannel = value; } + } +#endif + + public ConsoleOptions() { + } + + protected ConsoleOptions(ConsoleOptions options) { + ContractUtils.RequiresNotNull(options, "options"); + + _command = options._command; + _filename = options._filename; + _printVersionAndExit = options._printVersionAndExit; + _autoIndentSize = options._autoIndentSize; + _remainingArgs = ArrayUtils.Copy(options._remainingArgs); + _introspection = options._introspection; + _autoIndent = options._autoIndent; + _handleExceptions = options._handleExceptions; + _tabCompletion = options._tabCompletion; + _colorfulConsole = options._colorfulConsole; + _printUsageAndExit = options._printUsageAndExit; + _isMta = options._isMta; +#if !SILVERLIGHT // Remote console + _remoteRuntimeChannel = options._remoteRuntimeChannel; +#endif + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ICommandDispatcher.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ICommandDispatcher.cs new file mode 100644 index 0000000000..f848e1983c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/ICommandDispatcher.cs @@ -0,0 +1,24 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Hosting.Shell { + /// + /// Used to dispatch a single interactive command. It can be used to control things like which Thread + /// the command is executed on, how long the command is allowed to execute, etc + /// + public interface ICommandDispatcher { + object Execute(CompiledCode compiledCode, ScriptScope scope); + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/IConsole.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/IConsole.cs new file mode 100644 index 0000000000..fd0cf912eb --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/IConsole.cs @@ -0,0 +1,44 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.IO; + +namespace Microsoft.Scripting.Hosting.Shell { + /// + /// Handles input and output for the console. It is comparable to System.IO.TextReader, + /// System.IO.TextWriter, System.Console, etc + /// + public interface IConsole { + /// + /// Read a single line of interactive input, or a block of multi-line statements. + /// + /// An event-driven GUI console can implement this method by creating a thread that + /// blocks and waits for an event indicating that input is available + /// + /// The indentation level to be used for the current suite of a compound statement. + /// The console can ignore this argument if it does not want to support auto-indentation + /// null if the input stream has been closed. A string with a command to execute otherwise. + /// It can be a multi-line string which should be processed as block of statements + /// + string ReadLine(int autoIndentSize); + + void Write(string text, Style style); + void WriteLine(string text, Style style); + void WriteLine(); + + TextWriter Output { get; set; } + TextWriter ErrorOutput { get; set; } + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/OptionsParser.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/OptionsParser.cs new file mode 100644 index 0000000000..07e2770e33 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/OptionsParser.cs @@ -0,0 +1,312 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting.Shell { + + [Serializable] + public class InvalidOptionException : Exception { + public InvalidOptionException() { } + public InvalidOptionException(string message) : base(message) { } + public InvalidOptionException(string message, Exception inner) : base(message, inner) { } + +#if !SILVERLIGHT // SerializationInfo + protected InvalidOptionException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif + } + + public abstract class OptionsParser { + private ScriptRuntimeSetup _runtimeSetup; + private LanguageSetup _languageSetup; + private PlatformAdaptationLayer _platform; + + private List _ignoredArgs = new List(); + private string[] _args; + private int _current = -1; + + protected OptionsParser() { + } + + public ScriptRuntimeSetup RuntimeSetup { + get { return _runtimeSetup; } + } + + public LanguageSetup LanguageSetup { + get { return _languageSetup; } + } + + public PlatformAdaptationLayer Platform { + get { return _platform; } + } + + public abstract ConsoleOptions CommonConsoleOptions { + get; + } + + public IList IgnoredArgs { + get { return _ignoredArgs; } + } + + /// On error. + public void Parse(string[] args, ScriptRuntimeSetup setup, LanguageSetup languageSetup, PlatformAdaptationLayer platform) { + ContractUtils.RequiresNotNull(args, "args"); + ContractUtils.RequiresNotNull(setup, "setup"); + ContractUtils.RequiresNotNull(languageSetup, "languageSetup"); + ContractUtils.RequiresNotNull(platform, "platform"); + + _args = args; + _runtimeSetup = setup; + _languageSetup = languageSetup; + _platform = platform; + _current = 0; + try { + BeforeParse(); + while (_current < args.Length) { + ParseArgument(args[_current++]); + } + AfterParse(); + } finally { + _args = null; + _runtimeSetup = null; + _languageSetup = null; + _platform = null; + _current = -1; + } + } + + protected virtual void BeforeParse() { + // nop + } + + protected virtual void AfterParse() { + } + + protected abstract void ParseArgument(string arg); + + protected void IgnoreRemainingArgs() { + while (_current < _args.Length) { + _ignoredArgs.Add(_args[_current++]); + } + } + + protected string[] PopRemainingArgs() { + string[] result = ArrayUtils.ShiftLeft(_args, _current); + _current = _args.Length; + return result; + } + + protected string PeekNextArg() { + if (_current < _args.Length) + return _args[_current]; + else + throw new InvalidOptionException(String.Format(CultureInfo.CurrentCulture, "Argument expected for the {0} option.", _current > 0 ? _args[_current - 1] : "")); + } + + protected string PopNextArg() { + string result = PeekNextArg(); + _current++; + return result; + } + + protected void PushArgBack() { + _current--; + } + + protected static Exception InvalidOptionValue(string option, string value) { + return new InvalidOptionException(String.Format("'{0}' is not a valid value for option '{1}'", value, option)); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional")] // TODO: fix + public abstract void GetHelp(out string commandLine, out string[,] options, out string[,] environmentVariables, out string comments); + } + + public class OptionsParser : OptionsParser + where TConsoleOptions : ConsoleOptions, new() { + + private TConsoleOptions _consoleOptions; + + private bool _saveAssemblies = false; + private string _assembliesDir = null; + + public OptionsParser() { + } + + public TConsoleOptions ConsoleOptions { + get { + if (_consoleOptions == null) { + _consoleOptions = new TConsoleOptions(); + } + + return _consoleOptions; + } + set { + ContractUtils.RequiresNotNull(value, "value"); + _consoleOptions = value; + } + } + + public sealed override ConsoleOptions CommonConsoleOptions { + get { return ConsoleOptions; } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + protected override void ParseArgument(string arg) { + ContractUtils.RequiresNotNull(arg, "arg"); + + // the following extension switches are in alphabetic order + switch (arg) { + case "-h": + case "-help": + case "-?": + ConsoleOptions.PrintUsageAndExit = true; + IgnoreRemainingArgs(); + break; + + case "-D": + RuntimeSetup.DebugMode = true; + break; + + case "-X:PrivateBinding": + RuntimeSetup.PrivateBinding = true; + break; + + case "-X:PassExceptions": ConsoleOptions.HandleExceptions = false; break; + // TODO: #if !IRONPYTHON_WINDOW + case "-X:ColorfulConsole": ConsoleOptions.ColorfulConsole = true; break; + case "-X:TabCompletion": ConsoleOptions.TabCompletion = true; break; + case "-X:AutoIndent": ConsoleOptions.AutoIndent = true; break; + //#endif + + case "-X:LightweightScopes": + SetDlrOption(arg.Substring(3)); + break; + +#if DEBUG + case "-X:AssembliesDir": + _assembliesDir = PopNextArg(); + break; + + case "-X:SaveAssemblies": + _saveAssemblies = true; + break; + + case "-X:TrackPerformance": + SetDlrOption(arg.Substring(3)); + break; +#endif + + case "-X:Interpret": + LanguageSetup.Options["InterpretedMode"] = ScriptingRuntimeHelpers.True; + break; + + case "-X:ExceptionDetail": + case "-X:ShowClrExceptions": + case "-X:PerfStats": + // TODO: separate options dictionary? + LanguageSetup.Options[arg.Substring(3)] = ScriptingRuntimeHelpers.True; + break; + +#if !SILVERLIGHT // Remote console + case Remote.RemoteRuntimeServer.RemoteRuntimeArg: + ConsoleOptions.RemoteRuntimeChannel = PopNextArg(); + break; +#endif + default: + ConsoleOptions.FileName = arg.Trim(); + break; + } + + if (_saveAssemblies) { + //Set SaveAssemblies on for inner ring by calling System.Linq.Expressions.Compiler.Snippets.SetSaveAssemblies via Reflection. + Assembly core = typeof(System.Linq.Expressions.Expression).Assembly; + Type snippets = core.GetType("System.Linq.Expressions.Compiler.Snippets"); + //The type may not exist. + if (snippets != null) { + MethodInfo configSaveAssemblies = snippets.GetMethod("SetSaveAssemblies", BindingFlags.NonPublic | BindingFlags.Static); + //The method may not exist. + if (configSaveAssemblies != null) { + string[] coreAssemblyLocations = (string[])configSaveAssemblies.Invoke(null, new[] { _assembliesDir }); + } + } + //Sets SaveAssemblies on for outer ring. + Snippets.SetSaveAssemblies(_assembliesDir); + } + + } + + internal static void SetDlrOption(string option) { + SetDlrOption(option, "true"); + } + + // Note: this works because it runs before the compiler picks up the + // environment variable + internal static void SetDlrOption(string option, string value) { +#if !SILVERLIGHT + Environment.SetEnvironmentVariable("DLR_" + option, value); +#endif + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional")] // TODO: fix + public override void GetHelp(out string commandLine, out string[,] options, out string[,] environmentVariables, out string comments) { + + commandLine = "[options] [file|- [arguments]]"; + + options = new string[,] { + { "-c cmd", "Program passed in as string (terminates option list)" }, + { "-h", "Display usage" }, +#if !IRONPYTHON_WINDOW + { "-i", "Inspect interactively after running script" }, +#endif + { "-V", "Print the version number and exit" }, + { "-D", "Enable application debugging" }, + + { "-X:AutoIndent", "" }, + { "-X:ExceptionDetail", "Enable ExceptionDetail mode" }, + { "-X:Interpret", "Enable interpreted mode" }, + { "-X:LightweightScopes", "Generate optimized scopes that can be garbage collected" }, + { "-X:MaxRecursion", "Set the maximum recursion level" }, + { "-X:PassExceptions", "Do not catch exceptions that are unhandled by script code" }, + { "-X:PrivateBinding", "Enable binding to private members" }, + { "-X:ShowClrExceptions", "Display CLS Exception information" }, + +#if !SILVERLIGHT + { "-X:TabCompletion", "Enable TabCompletion mode" }, + { "-X:ColorfulConsole", "Enable ColorfulConsole" }, +#endif +#if DEBUG + { "-X:AssembliesDir ", "Set the directory for saving generated assemblies" }, + { "-X:SaveAssemblies", "Save generated assemblies" }, + { "-X:TrackPerformance", "Track performance sensitive areas" }, +#if !SILVERLIGHT // Remote console + { Remote.RemoteRuntimeServer.RemoteRuntimeArg + " ", + "Start a remoting server for a remote console session." }, +#endif +#endif + }; + + environmentVariables = new string[0, 0]; + + comments = null; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/ConsoleRestartManager.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/ConsoleRestartManager.cs new file mode 100644 index 0000000000..cb2fec6df4 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/ConsoleRestartManager.cs @@ -0,0 +1,223 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // Remoting + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +namespace Microsoft.Scripting.Hosting.Shell.Remote { + /// + /// Supports detecting the remote runtime being killed, and starting up a new one. + /// + /// Threading model: + /// + /// ConsoleRestartManager creates a separate thread on which to create and execute the consoles. + /// There are usually atleast three threads involved: + /// + /// 1. Main app thread: Instantiates ConsoleRestartManager and accesses its APIs. This thread has to stay + /// responsive to user input and so the ConsoleRestartManager APIs cannot be long-running or blocking. + /// Since the remote runtime process can terminate asynchronously, the current RemoteConsoleHost can + /// change at any time (if auto-restart is enabled). The app should typically not care which instance of + /// RemoteConsoleHost is currently being used. The flowchart of this thread is: + /// Create ConsoleRestartManager + /// ConsoleRestartManager.Start + /// Loop: + /// Respond to user input | Send user input to console for execution | BreakExecution | RestartConsole | GetMemberNames + /// ConsoleRestartManager.Terminate + /// TODO: Currently, BreakExecution and GetMemberNames are called by the main thread synchronously. + /// Since they execute code in the remote runtime, they could take arbitrarily long. We should change + /// this so that the main app thread can never be blocked indefinitely. + /// + /// 2. Console thread: Dedicated thread for creating RemoteConsoleHosts and executing code (which could + /// take a long time or block indefinitely). + /// Wait for ConsoleRestartManager.Start to be called + /// Loop: + /// Create RemoteConsoleHost + /// Wait for signal for: + /// Execute code | RestartConsole | Process.Exited + /// + /// 3. CompletionPort async callbacks: + /// Process.Exited | Process.OutputDataReceived | Process.ErrorDataReceived + /// + /// 4. Finalizer thred + /// Some objects may have a Finalize method (which possibly calls Dispose). Not many (if any) types + /// should have a Finalize method. + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors")] // TODO: This is public only because the test (RemoteConsole.py) needs it to be so. The test should be rewritten + public abstract class ConsoleRestartManager { + private RemoteConsoleHost _remoteConsoleHost; + private Thread _consoleThread; + private bool _exitOnNormalExit; + private bool _terminating; + + /// + /// Accessing _remoteConsoleHost from a thread other than console thread can result in race. + /// If _remoteConsoleHost is accessed while holding _accessLock, it is guaranteed to be + /// null or non-disposed. + /// + private object _accessLock = new object(); + + /// + /// This is created on the "creating thread", and goes on standby. Start needs to be called for activation. + /// + /// A host might want one of two behaviors: + /// 1. Keep the REPL loop alive indefinitely, even when a specific instance of the RemoteConsoleHost terminates normally + /// 2. Close the REPL loop when an instance of the RemoteConsoleHost terminates normally, and restart the loop + /// only if the instance terminates abnormally. + public ConsoleRestartManager(bool exitOnNormalExit) { + _exitOnNormalExit = exitOnNormalExit; + _consoleThread = new Thread(Run); + _consoleThread.Name = "Console thread"; + } + + protected object AccessLock { get { return _accessLock; } } + + public Thread ConsoleThread { get { return _consoleThread; } } + + protected RemoteConsoleHost CurrentConsoleHost { get { return _remoteConsoleHost; } } + + public abstract RemoteConsoleHost CreateRemoteConsoleHost(); + + /// + /// Needs to be called for activation. + /// + public void Start() { + Debug.Assert(Thread.CurrentThread != _consoleThread); + + if (_consoleThread.IsAlive) { + throw new InvalidOperationException("Console thread is already running."); + } + _consoleThread.Start(); + } + + private void Run() { +#if DEBUG + try { + RunWorker(); + } catch (Exception e) { + Debug.Assert(false, "Unhandled exception on console thread:\n\n" + e.ToString()); + } +#else + RunWorker(); +#endif + } + + private void RunWorker() { + Debug.Assert(Thread.CurrentThread == _consoleThread); + + while (true) { + RemoteConsoleHost remoteConsoleHost = CreateRemoteConsoleHost(); + + // Reading _terminating and setting of _remoteConsoleHost should be done atomically. + // Terminate() does the reverse operation (setting _terminating reading _remoteConsoleHost) atomically + lock (_accessLock) { + if (_terminating) { + return; + } + + _remoteConsoleHost = remoteConsoleHost; + } + + try { + try { + int exitCode = remoteConsoleHost.Run(new string[0]); + + if (_exitOnNormalExit && exitCode == 0) { + return; + } + } catch (RemoteRuntimeStartupException) { + } + } finally { + lock (_accessLock) { + remoteConsoleHost.Dispose(); + _remoteConsoleHost = null; + } + } + } + } + + // TODO: We have to catch all exceptions as we are executing user code in the remote runtime, and we cannot control what + // exception it may throw. This could be fixed if we built our own remoting channel which returned an error code + // instead of propagating exceptions back from the remote runtime. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public IList GetMemberNames(string expression) { + Debug.Assert(Thread.CurrentThread != _consoleThread); + + lock (_accessLock) { + if (_remoteConsoleHost == null) { + return null; + } + + ScriptEngine engine = _remoteConsoleHost.Engine; + try { + ScriptScope scope = _remoteConsoleHost.ScriptScope; + ObjectOperations operations = engine.CreateOperations(scope); + ScriptSource src = engine.CreateScriptSourceFromString(expression, SourceCodeKind.Expression); + return operations.GetMemberNames(src.ExecuteAndWrap(scope)); + } catch { + return null; + } + } + } + + public void BreakExecution() { + Debug.Assert(Thread.CurrentThread != _consoleThread); + + lock (_accessLock) { + if (_remoteConsoleHost == null) { + return; + } + + try { + _remoteConsoleHost.AbortCommand(); + } catch (System.Runtime.Remoting.RemotingException) { + // The remote runtime may be terminated or non-responsive + } + } + } + + public void RestartConsole() { + Debug.Assert(Thread.CurrentThread != _consoleThread); + + lock (_accessLock) { + if (_remoteConsoleHost == null) { + return; + } + + _remoteConsoleHost.Terminate(0); + } + } + + /// + /// Request (from another thread) the console REPL loop to terminate + /// + public void Terminate() { + Debug.Assert(Thread.CurrentThread != _consoleThread); + + lock (_accessLock) { + _terminating = true; + _remoteConsoleHost.Terminate(0); + } + + _consoleThread.Join(); + } + } +} + +#endif \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/RemoteCommandDispatcher.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/RemoteCommandDispatcher.cs new file mode 100644 index 0000000000..4c6575858f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/RemoteCommandDispatcher.cs @@ -0,0 +1,88 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // Remoting + +using System; +using System.Diagnostics; +using System.Threading; + +namespace Microsoft.Scripting.Hosting.Shell.Remote { + /// + /// This allows the RemoteConsoleHost to abort a long-running operation. The RemoteConsoleHost itself + /// does not know which ThreadPool thread might be processing the remote call, and so it needs + /// cooperation from the remote runtime server. + /// + public class RemoteCommandDispatcher : MarshalByRefObject, ICommandDispatcher { + /// + /// Since OnOutputDataReceived is sent async, it can arrive late. The remote console + /// cannot know if all output from the current command has been received. So + /// RemoteCommandDispatcher writes out a marker to indicate the end of the output + /// + internal const string OutputCompleteMarker = "{7FF032BB-DB03-4255-89DE-641CA195E5FA}"; + + private ScriptScope _scriptScope; + private Thread _executingThread; + + public RemoteCommandDispatcher(ScriptScope scope) { + _scriptScope = scope; + } + + public ScriptScope ScriptScope { get { return _scriptScope; } } + + public object Execute(CompiledCode compiledCode, ScriptScope scope) { + Debug.Assert(_executingThread == null); + _executingThread = Thread.CurrentThread; + + try { + object result = compiledCode.Execute(scope); + + Console.WriteLine(RemoteCommandDispatcher.OutputCompleteMarker); + + return result; + } catch (ThreadAbortException tae) { + KeyboardInterruptException pki = tae.ExceptionState as KeyboardInterruptException; + if (pki != null) { + // Most exceptions get propagated back to the client. However, ThreadAbortException is handled + // differently by the remoting infrastructure, and gets wrapped in a RemotingException + // ("An error occurred while processing the request on the server"). So we filter it out + // and raise the KeyboardInterruptException + Thread.ResetAbort(); + throw pki; + } else { + throw; + } + } finally { + _executingThread = null; + } + } + + /// + /// Aborts the current active call to Execute by doing Thread.Abort + /// + /// true if a Thread.Abort was actually called. false if there is no active call to Execute + public bool AbortCommand() { + Thread executingThread = _executingThread; + if (executingThread == null) { + return false; + } + + executingThread.Abort(new KeyboardInterruptException("")); + return true; + } + } +} + +#endif \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/RemoteConsoleCommandLine.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/RemoteConsoleCommandLine.cs new file mode 100644 index 0000000000..08c0b3ead0 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/RemoteConsoleCommandLine.cs @@ -0,0 +1,85 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // Remoting + +using System; +using System.Diagnostics; +using System.Runtime.Remoting; +using System.Threading; + +namespace Microsoft.Scripting.Hosting.Shell.Remote { + /// + /// Customize the CommandLine for remote scenarios + /// + public class RemoteConsoleCommandLine : CommandLine { + private RemoteConsoleCommandDispatcher _remoteConsoleCommandDispatcher; + + public RemoteConsoleCommandLine(ScriptScope scope, RemoteCommandDispatcher remoteCommandDispatcher, AutoResetEvent remoteOutputReceived) { + _remoteConsoleCommandDispatcher = new RemoteConsoleCommandDispatcher(remoteCommandDispatcher, remoteOutputReceived); + Debug.Assert(scope != null); + ScriptScope = scope; + } + + protected override ICommandDispatcher CreateCommandDispatcher() { + return _remoteConsoleCommandDispatcher; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + internal void UnhandledExceptionWorker(Exception e) { + try { + base.UnhandledException(e); + } catch (Exception exceptionDuringHandling) { + // All bets are off while accessing the remote runtime. So we catch all exceptions. + // However, in most cases, we only expect to see RemotingException here. + if (!(exceptionDuringHandling is RemotingException)) { + Console.WriteLine(String.Format("({0} thrown while trying to display unhandled exception)", exceptionDuringHandling.GetType()), Style.Error); + } + + // The remote server may have shutdown. So just do something simple + Console.WriteLine(e.ToString(), Style.Error); + } + } + + protected override void UnhandledException(Exception e) { + UnhandledExceptionWorker(e); + } + + /// + /// CommandDispatcher to ensure synchronize output from the remote runtime + /// + class RemoteConsoleCommandDispatcher : ICommandDispatcher { + private RemoteCommandDispatcher _remoteCommandDispatcher; + private AutoResetEvent _remoteOutputReceived; + + internal RemoteConsoleCommandDispatcher(RemoteCommandDispatcher remoteCommandDispatcher, AutoResetEvent remoteOutputReceived) { + _remoteCommandDispatcher = remoteCommandDispatcher; + _remoteOutputReceived = remoteOutputReceived; + } + + public object Execute(CompiledCode compiledCode, ScriptScope scope) { + // Delegate the operation to the RemoteCommandDispatcher which will execute the code in the remote runtime + object result = _remoteCommandDispatcher.Execute(compiledCode, scope); + + // Output is received async, and so we need explicit synchronization in the remote console + _remoteOutputReceived.WaitOne(); + + return result; + } + } + } +} + +#endif \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/RemoteConsoleHost.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/RemoteConsoleHost.cs new file mode 100644 index 0000000000..c2f5748e38 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/RemoteConsoleHost.cs @@ -0,0 +1,290 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // Remoting + +using System; +using System.Diagnostics; +using System.Runtime.Remoting; +using System.Runtime.Remoting.Channels; +using System.Runtime.Remoting.Channels.Ipc; +using System.Runtime.Serialization; +using System.Threading; + +namespace Microsoft.Scripting.Hosting.Shell.Remote { + /// + /// ConsoleHost where the ScriptRuntime is hosted in a separate process (referred to as the remote runtime server) + /// + /// The RemoteConsoleHost spawns the remote runtime server and specifies an IPC channel name to use to communicate + /// with each other. The remote runtime server creates and initializes a ScriptRuntime and a ScriptEngine, and publishes + /// it over the specified IPC channel at a well-known URI. Note that the RemoteConsoleHost cannot easily participate + /// in the initialization of the ScriptEngine as classes like LanguageContext are not remotable. + /// + /// The RemoteConsoleHost then starts the interactive loop and executes commands on the ScriptEngine over the remoting channel. + /// The RemoteConsoleHost listens to stdout of the remote runtime server and echos it locally to the user. + /// + public abstract class RemoteConsoleHost : ConsoleHost, IDisposable { + Process _remoteRuntimeProcess; + internal RemoteCommandDispatcher _remoteCommandDispatcher; + private string _channelName = RemoteConsoleHost.GetChannelName(); + private IpcChannel _clientChannel; + private AutoResetEvent _remoteOutputReceived = new AutoResetEvent(false); + private ScriptScope _scriptScope; + + #region Private methods + + private static string GetChannelName() { + return "RemoteRuntime-" + Guid.NewGuid().ToString(); + } + + private ProcessStartInfo GetProcessStartInfo() { + ProcessStartInfo processInfo = new ProcessStartInfo(); + processInfo.Arguments = RemoteRuntimeServer.RemoteRuntimeArg + " " + _channelName; + processInfo.CreateNoWindow = true; + + // Set UseShellExecute to false to enable redirection. + processInfo.UseShellExecute = false; + + // Redirect the standard streams. The output streams will be read asynchronously using an event handler. + processInfo.RedirectStandardError = true; + processInfo.RedirectStandardOutput = true; + // The input stream can be ignored as the remote server process does not need to read any input + processInfo.RedirectStandardInput = true; + + CustomizeRemoteRuntimeStartInfo(processInfo); + Debug.Assert(processInfo.FileName != null); + return processInfo; + } + + private void StartRemoteRuntimeProcess() { + Process process = new Process(); + + process.StartInfo = GetProcessStartInfo(); + + process.OutputDataReceived += new DataReceivedEventHandler(OnOutputDataReceived); + process.ErrorDataReceived += new DataReceivedEventHandler(OnErrorDataReceived); + + process.Exited += new EventHandler(OnRemoteRuntimeExited); + _remoteRuntimeProcess = process; + + process.Start(); + + // Start the asynchronous read of the output streams. + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + // wire up exited + process.EnableRaisingEvents = true; + + // Wait for the output marker to know when the startup output is complete + _remoteOutputReceived.WaitOne(); + + if (process.HasExited) { + throw new RemoteRuntimeStartupException("Remote runtime terminated during startup with exitcode " + process.ExitCode); + } + } + + private T GetRemoteObject(string uri) { + T result = (T)Activator.GetObject(typeof(T), "ipc://" + _channelName + "/" + uri); + + // Ensure that the remote object is responsive by calling a virtual method (which will be executed remotely) + Debug.Assert(result.ToString() != null); + + return result; + } + + private void InitializeRemoteScriptEngine() { + StartRemoteRuntimeProcess(); + + _remoteCommandDispatcher = GetRemoteObject(RemoteRuntimeServer.CommandDispatcherUri); + + _scriptScope = _remoteCommandDispatcher.ScriptScope; + Engine = _scriptScope.Engine; + + // Register a channel for the reverse direction, when the remote runtime process wants to fire events + // or throw an exception + string clientChannelName = _channelName.Replace("RemoteRuntime", "RemoteConsole"); + _clientChannel = RemoteRuntimeServer.CreateChannel(clientChannelName, clientChannelName); + ChannelServices.RegisterChannel(_clientChannel, false); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + protected virtual void OnRemoteRuntimeExited(object sender, EventArgs args) { + Debug.Assert(((Process)sender).HasExited); + Debug.Assert(sender == _remoteRuntimeProcess || _remoteRuntimeProcess == null); + + EventHandler remoteRuntimeExited = RemoteRuntimeExited; + if (remoteRuntimeExited != null) { + remoteRuntimeExited(sender, args); + } + + // StartRemoteRuntimeProcess also blocks on this event. Signal it in case the + // remote runtime terminates during startup itself. + _remoteOutputReceived.Set(); + + // Nudge the ConsoleHost to exit the REPL loop + Terminate(_remoteRuntimeProcess.ExitCode); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] // TODO: This is protected only for test code, which could be rewritten to not require this to be protected + protected virtual void OnOutputDataReceived(object sender, DataReceivedEventArgs eventArgs) { + if (String.IsNullOrEmpty(eventArgs.Data)) { + return; + } + + string output = eventArgs.Data as string; + + if (output.Contains(RemoteCommandDispatcher.OutputCompleteMarker)) { + Debug.Assert(output == RemoteCommandDispatcher.OutputCompleteMarker); + _remoteOutputReceived.Set(); + } else { + ConsoleIO.WriteLine(output, Style.Out); + } + } + + private void OnErrorDataReceived(object sender, DataReceivedEventArgs eventArgs) { + if (!String.IsNullOrEmpty(eventArgs.Data)) { + ConsoleIO.WriteLine((string)eventArgs.Data, Style.Error); + } + } + + #endregion + + public override void Terminate(int exitCode) { + if (CommandLine == null) { + // Terminate may be called during startup when CommandLine has not been initialized. + // We could fix this by initializing CommandLine before starting the remote runtime process + return; + } + + base.Terminate(exitCode); + } + + protected override CommandLine CreateCommandLine() { + return new RemoteConsoleCommandLine(_scriptScope, _remoteCommandDispatcher, _remoteOutputReceived); + } + + public ScriptScope ScriptScope { get { return CommandLine.ScriptScope; } } + public Process RemoteRuntimeProcess { get { return _remoteRuntimeProcess; } } + + // TODO: We have to catch all exceptions as we are executing user code in the remote runtime, and we cannot control what + // exception it may throw. This could be fixed if we built our own remoting channel which returned an error code + // instead of propagating exceptions back from the remote runtime. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + protected override void UnhandledException(ScriptEngine engine, Exception e) { + ((RemoteConsoleCommandLine)CommandLine).UnhandledExceptionWorker(e); + } + /// + /// Called if the remote runtime process exits by itself. ie. without the remote console killing it. + /// + internal event EventHandler RemoteRuntimeExited; + + /// + /// Allows the console to customize the environment variables, working directory, etc. + /// + /// At the least, processInfo.FileName should be initialized + public abstract void CustomizeRemoteRuntimeStartInfo(ProcessStartInfo processInfo); + + /// + /// Aborts the current active call to Execute by doing Thread.Abort + /// + /// true if a Thread.Abort was actually called. false if there is no active call to Execute + public bool AbortCommand() { + return _remoteCommandDispatcher.AbortCommand(); + } + + public override int Run(string[] args) { + var runtimeSetup = CreateRuntimeSetup(); + var options = new ConsoleHostOptions(); + ConsoleHostOptionsParser = new ConsoleHostOptionsParser(options, runtimeSetup); + + try { + ParseHostOptions(args); + } catch (InvalidOptionException e) { + Console.Error.WriteLine("Invalid argument: " + e.Message); + return ExitCode = 1; + } + + _languageOptionsParser = CreateOptionsParser(); + + // Create IConsole early (with default settings) in order to be able to display startup output + ConsoleIO = CreateConsole(null, null, new ConsoleOptions()); + + InitializeRemoteScriptEngine(); + Runtime = Engine.Runtime; + + ExecuteInternal(); + + return ExitCode; + } + + #region IDisposable Members + + public virtual void Dispose(bool disposing) { + if (!disposing) { + // Managed fields cannot be reliably accessed during finalization since they may already have been finalized + return; + } + + _remoteOutputReceived.Close(); + + if (_clientChannel != null) { + ChannelServices.UnregisterChannel(_clientChannel); + _clientChannel = null; + } + + if (_remoteRuntimeProcess != null) { + _remoteRuntimeProcess.Exited -= OnRemoteRuntimeExited; + + // Closing stdin is a signal to the remote runtime to exit the process. + _remoteRuntimeProcess.StandardInput.Close(); + _remoteRuntimeProcess.WaitForExit(5000); + + if (!_remoteRuntimeProcess.HasExited) { + _remoteRuntimeProcess.Kill(); + _remoteRuntimeProcess.WaitForExit(); + } + + _remoteRuntimeProcess = null; + } + } + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } + + [Serializable] + public class RemoteRuntimeStartupException : Exception { + public RemoteRuntimeStartupException() { } + + public RemoteRuntimeStartupException(string message) + : base(message) { + } + + public RemoteRuntimeStartupException(string message, Exception innerException) + : base(message, innerException) { + } + + protected RemoteRuntimeStartupException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } + } +} + +#endif \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/RemoteRuntimeServer.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/RemoteRuntimeServer.cs new file mode 100644 index 0000000000..bd3b3cb72d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Remote/RemoteRuntimeServer.cs @@ -0,0 +1,95 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // Remoting + +using System; +using System.Diagnostics; +using System.Runtime.Remoting; +using System.Runtime.Remoting.Channels; +using System.Runtime.Remoting.Channels.Ipc; +using System.Runtime.Remoting.Lifetime; + +namespace Microsoft.Scripting.Hosting.Shell.Remote { + /// + /// The remote runtime server uses this class to publish an initialized ScriptEngine and ScriptRuntime + /// over a remoting channel. + /// + public static class RemoteRuntimeServer { + internal const string CommandDispatcherUri = "CommandDispatcherUri"; + internal const string RemoteRuntimeArg = "-X:RemoteRuntimeChannel"; + + private static TimeSpan GetSevenDays() { + return new TimeSpan(7, 0, 0, 0); // days,hours,mins,secs + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods")] // TODO: Microsoft.Scripting does not need to be APTCA + internal static IpcChannel CreateChannel(string channelName, string portName) { + // The Hosting API classes require TypeFilterLevel.Full to be remoted + BinaryServerFormatterSinkProvider serverProv = new BinaryServerFormatterSinkProvider(); + serverProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full; + System.Collections.IDictionary properties = new System.Collections.Hashtable(); + properties["name"] = channelName; + properties["portName"] = portName; + // exclusiveAddressUse corresponds to the FILE_FLAG_FIRST_PIPE_INSTANCE flag of CreateNamedPipe. + // Setting it to true seems to cause "Failed to create an IPC Port: Access is denied." occasionally. + // TODO: Setting this to false is secure only if we use ACLs as well. + properties["exclusiveAddressUse"] = false; + + // Create the channel. + IpcChannel channel = new IpcChannel(properties, null, serverProv); + return channel; + } + + /// + /// Publish objects so that the host can use it, and then block indefinitely (until the input stream is open). + /// + /// Note that we should publish only one object, and then have other objects be accessible from it. Publishing + /// multiple objects can cause problems if the client does a call like "remoteProxy1(remoteProxy2)" as remoting + /// will not be able to know if the server object for both the proxies is on the same server. + /// + /// The IPC channel that the remote console expects to use to communicate with the ScriptEngine + /// A intialized ScriptScope that is ready to start processing script commands + internal static void StartServer(string remoteRuntimeChannelName, ScriptScope scope) { + Debug.Assert(ChannelServices.GetChannel(remoteRuntimeChannelName) == null); + + IpcChannel channel = CreateChannel("ipc", remoteRuntimeChannelName); + + LifetimeServices.LeaseTime = GetSevenDays(); + LifetimeServices.LeaseManagerPollTime = GetSevenDays(); + LifetimeServices.RenewOnCallTime = GetSevenDays(); + LifetimeServices.SponsorshipTimeout = GetSevenDays(); + + ChannelServices.RegisterChannel(channel, false); + + try { + RemoteCommandDispatcher remoteCommandDispatcher = new RemoteCommandDispatcher(scope); + RemotingServices.Marshal(remoteCommandDispatcher, CommandDispatcherUri); + + // Let the remote console know that the startup output (if any) is complete. We use this instead of + // a named event as we want all the startup output to reach the remote console before it proceeds. + Console.WriteLine(RemoteCommandDispatcher.OutputCompleteMarker); + + // Block on Console.In. This is used to determine when the host process exits, since ReadLine will return null then. + string input = System.Console.ReadLine(); + Debug.Assert(input == null); + } finally { + ChannelServices.UnregisterChannel(channel); + } + } + } +} + +#endif \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Style.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Style.cs new file mode 100644 index 0000000000..c848f56b68 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/Style.cs @@ -0,0 +1,20 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Hosting.Shell { + public enum Style { + Prompt, Out, Error, Warning + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/SuperConsole.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/SuperConsole.cs new file mode 100644 index 0000000000..a8de577301 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/Shell/SuperConsole.cs @@ -0,0 +1,617 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Scripting.Utils; +using System.Text; + +namespace Microsoft.Scripting.Hosting.Shell { +#if !SILVERLIGHT // SuperConsole (ConsoleColor) + public sealed class SuperConsole : BasicConsole { + + #region Nested types: History, SuperConsoleOptions, Cursor + + /// + /// Class managing the command history. + /// + class History { + protected List _list = new List(); + private int _current; + private bool _increment; // increment on Next() + + public string Current { + get { + return _current >= 0 && _current < _list.Count ? _list[_current] : String.Empty; + } + } + + public void Add(string line, bool setCurrentAsLast) { + if (line != null && line.Length > 0) { + int oldCount = _list.Count; + _list.Add(line); + if (setCurrentAsLast || _current == oldCount) { + _current = _list.Count; + } else { + _current++; + } + // Do not increment on the immediately following Next() + _increment = false; + } + } + + public string Previous() { + if (_current > 0) { + _current--; + _increment = true; + } + return Current; + } + + public string Next() { + if (_current + 1 < _list.Count) { + if (_increment) _current++; + _increment = true; + } + return Current; + } + } + + /// + /// List of available options + /// + class SuperConsoleOptions { + private List _list = new List(); + private int _current; + private string _root; + + public int Count { + get { + return _list.Count; + } + } + + private string Current { + get { + return _current >= 0 && _current < _list.Count ? _list[_current] : String.Empty; + } + } + + public void Clear() { + _list.Clear(); + _current = -1; + } + + public void Add(string line) { + if (line != null && line.Length > 0) { + _list.Add(line); + } + } + + public string Previous() { + if (_list.Count > 0) { + _current = ((_current - 1) + _list.Count) % _list.Count; + } + return Current; + } + + public string Next() { + if (_list.Count > 0) { + _current = (_current + 1) % _list.Count; + } + return Current; + } + + public string Root { + get { + return _root; + } + set { + _root = value; + } + } + } + + /// + /// Cursor position management + /// + struct Cursor { + /// + /// Beginning position of the cursor - top coordinate. + /// + private int _anchorTop; + /// + /// Beginning position of the cursor - left coordinate. + /// + private int _anchorLeft; + + public void Anchor() { + _anchorTop = Console.CursorTop; + _anchorLeft = Console.CursorLeft; + } + + public void Reset() { + Console.CursorTop = _anchorTop; + Console.CursorLeft = _anchorLeft; + } + + public void Place(int index) { + Console.CursorLeft = (_anchorLeft + index) % Console.BufferWidth; + int cursorTop = _anchorTop + (_anchorLeft + index) / Console.BufferWidth; + if (cursorTop >= Console.BufferHeight) { + _anchorTop -= cursorTop - Console.BufferHeight + 1; + cursorTop = Console.BufferHeight - 1; + } + Console.CursorTop = cursorTop; + } + + public static void Move(int delta) { + int position = Console.CursorTop * Console.BufferWidth + Console.CursorLeft + delta; + + Console.CursorLeft = position % Console.BufferWidth; + Console.CursorTop = position / Console.BufferWidth; + } + } + + #endregion + + /// + /// The console input buffer. + /// + private StringBuilder _input = new StringBuilder(); + + /// + /// Current position - index into the input buffer + /// + private int _current; + + /// + /// The number of white-spaces displayed for the auto-indenation of the current line + /// + private int _autoIndentSize; + + /// + /// Length of the output currently rendered on screen. + /// + private int _rendered; + + /// + /// Command history + /// + private History _history = new History(); + + /// + /// Tab options available in current context + /// + private SuperConsoleOptions _options = new SuperConsoleOptions(); + + /// + /// Cursort anchor - position of cursor when the routine was called + /// + private Cursor _cursor; + + /// + /// The command line that this console is attached to. + /// + private CommandLine _commandLine; + + public SuperConsole(CommandLine commandLine, bool colorful) + : base(colorful) { + ContractUtils.RequiresNotNull(commandLine, "commandLine"); + _commandLine = commandLine; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private bool GetOptions() { + _options.Clear(); + + int len; + for (len = _input.Length; len > 0; len--) { + char c = _input[len - 1]; + if (Char.IsLetterOrDigit(c)) { + continue; + } else if (c == '.' || c == '_') { + continue; + } else { + break; + } + } + + string name = _input.ToString(len, _input.Length - len); + if (name.Trim().Length > 0) { + int lastDot = name.LastIndexOf('.'); + string attr, pref, root; + if (lastDot < 0) { + attr = String.Empty; + pref = name; + root = _input.ToString(0, len); + } else { + attr = name.Substring(0, lastDot); + pref = name.Substring(lastDot + 1); + root = _input.ToString(0, len + lastDot + 1); + } + + try { + IList result; + if (String.IsNullOrEmpty(attr)) { + result = _commandLine.GetGlobals(name); + } else { + result = _commandLine.GetMemberNames(attr); + } + + _options.Root = root; + foreach (string option in result) { + if (option.StartsWith(pref, StringComparison.CurrentCultureIgnoreCase)) { + _options.Add(option); + } + } + } catch { + _options.Clear(); + } + return true; + } else { + return false; + } + } + + private void SetInput(string line) { + _input.Length = 0; + _input.Append(line); + + _current = _input.Length; + + Render(); + } + + private void Initialize() { + _cursor.Anchor(); + _input.Length = 0; + _current = 0; + _rendered = 0; + } + + // Check if the user is backspacing the auto-indentation. In that case, we go back all the way to + // the previous indentation level. + // Return true if we did backspace the auto-indenation. + private bool BackspaceAutoIndentation() { + if (_input.Length == 0 || _input.Length > _autoIndentSize) return false; + + // Is the auto-indenation all white space, or has the user since edited the auto-indentation? + for (int i = 0; i < _input.Length; i++) { + if (_input[i] != ' ') return false; + } + + // Calculate the previous indentation level + //!!! int newLength = ((input.Length - 1) / ConsoleOptions.AutoIndentSize) * ConsoleOptions.AutoIndentSize; + int newLength = _input.Length - 4; + + int backspaceSize = _input.Length - newLength; + _input.Remove(newLength, backspaceSize); + _current -= backspaceSize; + Render(); + return true; + } + + private void OnBackspace() { + if (BackspaceAutoIndentation()) return; + + if (_input.Length > 0 && _current > 0) { + _input.Remove(_current - 1, 1); + _current--; + Render(); + } + } + + private void OnDelete() { + if (_input.Length > 0 && _current < _input.Length) { + _input.Remove(_current, 1); + Render(); + } + } + + private void Insert(ConsoleKeyInfo key) { + char c; + if (key.Key == ConsoleKey.F6) { + Debug.Assert(FinalLineText.Length == 1); + + c = FinalLineText[0]; + } else { + c = key.KeyChar; + } + Insert(c); + } + + private void Insert(char c) { + if (_current == _input.Length) { + if (Char.IsControl(c)) { + string s = MapCharacter(c); + _current++; + _input.Append(c); + Output.Write(s); + _rendered += s.Length; + } else { + _current++; + _input.Append(c); + Output.Write(c); + _rendered++; + } + } else { + _input.Insert(_current, c); + _current++; + Render(); + } + } + + private static string MapCharacter(char c) { + if (c == 13) return "\r\n"; + if (c <= 26) return "^" + ((char)(c + 'A' - 1)).ToString(); + + return "^?"; + } + + private static int GetCharacterSize(char c) { + if (Char.IsControl(c)) { + return MapCharacter(c).Length; + } else { + return 1; + } + } + + private void Render() { + _cursor.Reset(); + StringBuilder output = new StringBuilder(); + int position = -1; + for (int i = 0; i < _input.Length; i++) { + if (i == _current) { + position = output.Length; + } + char c = _input[i]; + if (Char.IsControl(c)) { + output.Append(MapCharacter(c)); + } else { + output.Append(c); + } + } + + if (_current == _input.Length) { + position = output.Length; + } + + string text = output.ToString(); + Output.Write(text); + + if (text.Length < _rendered) { + Output.Write(new String(' ', _rendered - text.Length)); + } + _rendered = text.Length; + _cursor.Place(position); + } + + private void MoveLeft(ConsoleModifiers keyModifiers) { + if ((keyModifiers & ConsoleModifiers.Control) != 0) { + // move back to the start of the previous word + if (_input.Length > 0 && _current != 0) { + bool nonLetter = IsSeperator(_input[_current - 1]); + while (_current > 0 && (_current - 1 < _input.Length)) { + MoveLeft(); + + if (IsSeperator(_input[_current]) != nonLetter) { + if (!nonLetter) { + MoveRight(); + break; + } + + nonLetter = false; + } + } + } + } else { + MoveLeft(); + } + } + + private static bool IsSeperator(char ch) { + return !Char.IsLetter(ch); + } + + private void MoveRight(ConsoleModifiers keyModifiers) { + if ((keyModifiers & ConsoleModifiers.Control) != 0) { + // move to the next word + if (_input.Length != 0 && _current < _input.Length) { + bool nonLetter = IsSeperator(_input[_current]); + while (_current < _input.Length) { + MoveRight(); + + if (_current == _input.Length) break; + if (IsSeperator(_input[_current]) != nonLetter) { + if (nonLetter) + break; + + nonLetter = true; + } + } + } + } else { + MoveRight(); + } + } + + private void MoveRight() { + if (_current < _input.Length) { + char c = _input[_current]; + _current++; + Cursor.Move(GetCharacterSize(c)); + } + } + + private void MoveLeft() { + if (_current > 0 && (_current - 1 < _input.Length)) { + _current--; + char c = _input[_current]; + Cursor.Move(-GetCharacterSize(c)); + } + } + + private const int TabSize = 4; + private void InsertTab() { + for (int i = TabSize - (_current % TabSize); i > 0; i--) { + Insert(' '); + } + } + + private void MoveHome() { + _current = 0; + _cursor.Reset(); + } + + private void MoveEnd() { + _current = _input.Length; + _cursor.Place(_rendered); + } + + public override string ReadLine(int autoIndentSize) { + Initialize(); + + _autoIndentSize = autoIndentSize; + for (int i = 0; i < _autoIndentSize; i++) + Insert(' '); + + bool inputChanged = false; + bool optionsObsolete = false; + + for (; ; ) { + ConsoleKeyInfo key = Console.ReadKey(true); + + switch (key.Key) { + case ConsoleKey.Backspace: + OnBackspace(); + inputChanged = optionsObsolete = true; + break; + case ConsoleKey.Delete: + OnDelete(); + inputChanged = optionsObsolete = true; + break; + case ConsoleKey.Enter: + return OnEnter(inputChanged); + case ConsoleKey.Tab: { + bool prefix = false; + if (optionsObsolete) { + prefix = GetOptions(); + optionsObsolete = false; + } + + // Displays the next option in the option list, + // or beeps if no options available for current input prefix. + // If no input prefix, simply print tab. + DisplayNextOption(key, prefix); + inputChanged = true; + break; + } + case ConsoleKey.UpArrow: + SetInput(_history.Previous()); + optionsObsolete = true; + inputChanged = false; + break; + case ConsoleKey.DownArrow: + SetInput(_history.Next()); + optionsObsolete = true; + inputChanged = false; + break; + case ConsoleKey.RightArrow: + MoveRight(key.Modifiers); + optionsObsolete = true; + break; + case ConsoleKey.LeftArrow: + MoveLeft(key.Modifiers); + optionsObsolete = true; + break; + case ConsoleKey.Escape: + SetInput(String.Empty); + inputChanged = optionsObsolete = true; + break; + case ConsoleKey.Home: + MoveHome(); + optionsObsolete = true; + break; + case ConsoleKey.End: + MoveEnd(); + optionsObsolete = true; + break; + case ConsoleKey.LeftWindows: + case ConsoleKey.RightWindows: + // ignore these + continue; + + default: + if (key.KeyChar == '\x0D') goto case ConsoleKey.Enter; // Ctrl-M + if (key.KeyChar == '\x08') goto case ConsoleKey.Backspace; // Ctrl-H + Insert(key); + inputChanged = optionsObsolete = true; + break; + } + } + } + + /// + /// Displays the next option in the option list, + /// or beeps if no options available for current input prefix. + /// If no input prefix, simply print tab. + /// + /// + /// + private void DisplayNextOption(ConsoleKeyInfo key, bool prefix) { + if (_options.Count > 0) { + string part = (key.Modifiers & ConsoleModifiers.Shift) != 0 ? _options.Previous() : _options.Next(); + SetInput(_options.Root + part); + } else { + if (prefix) { + Console.Beep(); + } else { + InsertTab(); + } + } + } + + /// + /// Handle the enter key. Adds the current input (if not empty) to the history. + /// + /// + /// The input string. + private string OnEnter(bool inputChanged) { + Output.Write("\n"); + string line = _input.ToString(); + if (line == FinalLineText) return null; + if (line.Length > 0) { + _history.Add(line, inputChanged); + } + return line; + } + + string FinalLineText { + get { + return Environment.OSVersion.Platform != PlatformID.Unix ? "\x1A" : "\x04"; + } + } + } +#else + public sealed class SuperConsole : BasicConsole { + public SuperConsole(CommandLine commandLine, bool isColorful) + : base(isColorful) { + } + } +#endif +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Hosting/TokenCategorizer.cs b/merlin/main/runtime/Microsoft.Scripting/Hosting/TokenCategorizer.cs new file mode 100644 index 0000000000..37566f0f8e --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Hosting/TokenCategorizer.cs @@ -0,0 +1,106 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Dynamic; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Hosting { + public sealed class TokenCategorizer +#if !SILVERLIGHT + : MarshalByRefObject +#endif + { + private readonly TokenizerService _tokenizer; + + internal TokenCategorizer(TokenizerService tokenizer) { + Assert.NotNull(tokenizer); + _tokenizer = tokenizer; + } + + public void Initialize(object state, ScriptSource scriptSource, SourceLocation initialLocation) { + _tokenizer.Initialize(state, scriptSource.SourceUnit.GetReader(), scriptSource.SourceUnit, initialLocation); + } + + /// + /// The current internal state of the scanner. + /// + public object CurrentState { + get { return _tokenizer.CurrentState; } + } + + /// + /// The current startLocation of the scanner. + /// + public SourceLocation CurrentPosition { + get { return _tokenizer.CurrentPosition; } + } + + /// + /// Move the tokenizer past the next token and return its category. + /// + /// The token information associated with the token just scanned. + public TokenInfo ReadToken() { + return _tokenizer.ReadToken(); + } + + public bool IsRestartable { + get { return _tokenizer.IsRestartable; } + } + + // TODO: Should be ErrorListener + public ErrorSink ErrorSink { + get { return _tokenizer.ErrorSink; } + set { _tokenizer.ErrorSink = value; } + } + + /// + /// Move the tokenizer past the next token. + /// + /// False if the end of stream has been reached, true otherwise. + public bool SkipToken() { + return _tokenizer.SkipToken(); + } + + /// + /// Get all tokens over a block of the stream. + /// + /// + /// + /// The scanner should return full tokens. If startLocation + length lands in the middle of a token, the full token + /// should be returned. + /// + /// s + /// The mininum number of characters to process while getting tokens. + /// A enumeration of tokens. + public IEnumerable ReadTokens(int characterCount) { + return _tokenizer.ReadTokens(characterCount); + } + + /// + /// Scan from startLocation to at least startLocation + length. + /// + /// The mininum number of characters to process while getting tokens. + /// + /// This method is used to determine state at arbitrary startLocation. + /// + /// False if the end of stream has been reached, true otherwise. + public bool SkipTokens(int characterCount) { + return _tokenizer.SkipTokens(characterCount); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/IAttributesCollection.cs b/merlin/main/runtime/Microsoft.Scripting/IAttributesCollection.cs new file mode 100644 index 0000000000..a8680fb478 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/IAttributesCollection.cs @@ -0,0 +1,56 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; + +namespace Microsoft.Scripting { + /// + /// This interface represents a dictionary that can be accessed using symbols and also arbitrary objects. + /// This should conceptually inherit from IDictionary<object, object>, but we do not do that as we want the default indexer + /// property to be indexed by SymbolId, not by object. + /// + public interface IAttributesCollection : IEnumerable> { + /// + /// Access using SymbolId keys + /// + void Add(SymbolId name, object value); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + bool TryGetValue(SymbolId name, out object value); + + bool Remove(SymbolId name); + + bool ContainsKey(SymbolId name); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers")] + object this[SymbolId name] { get; set; } + + // This returns just the attributes that are keyed using SymbolIds. It will ignore any object-keyed attributes + IDictionary SymbolAttributes { get; } + + // + // Access using object keys + // + void AddObjectKey(object name, object value); + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + bool TryGetObjectValue(object name, out object value); + bool RemoveObjectKey(object name); + bool ContainsObjectKey(object name); + IDictionary AsObjectKeyedDictionary(); + + int Count { get; } + ICollection Keys { get; } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/IValueEquality.cs b/merlin/main/runtime/Microsoft.Scripting/IValueEquality.cs new file mode 100644 index 0000000000..f429a250ca --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/IValueEquality.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting { + /// + /// Provides hashing and equality based upon the value of the object instead of the reference. + /// + public interface IValueEquality { + /// + /// Gets the hash code for the value of the instance. + /// + /// A hash code + /// The type is mutable and cannot be hashed by value + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + int GetValueHashCode(); + + /// + /// Determines if two values are equal + /// + /// The object to compare the current object against. + /// Returns true if the objects are equal, false if they are not. + bool ValueEquals(object other); + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/CommaAddress.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/CommaAddress.cs new file mode 100644 index 0000000000..aad0efccbd --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/CommaAddress.cs @@ -0,0 +1,55 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Interpretation { + internal class CommaAddress : EvaluationAddress { + private List _addrs; + + internal CommaAddress(BlockExpression address, List addresses) + : base(address) { + _addrs = addresses; + } + + internal override object GetValue(InterpreterState state, bool outParam) { + object result = null; + for (int i = 0; i < _addrs.Count; i++) { + EvaluationAddress current = _addrs[i]; + + if (current != null) { + object val = current.GetValue(state, outParam); + if (i == Index) { + result = val; + } + } + } + return result; + } + + internal override object AssignValue(InterpreterState state, object value) { + EvaluationAddress addr = _addrs[Index]; + if (addr != null) return addr.AssignValue(state, value); + return null; + } + + internal int Index { + get { + return ((BlockExpression)Expression).Expressions.Count - 1; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/ControlFlow.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/ControlFlow.cs new file mode 100644 index 0000000000..1a1749765a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/ControlFlow.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Interpretation { + // Singleton objects of this enum type are used as return values from Statement.Execute() to handle control flow. + enum ControlFlowKind { + NextStatement, + Goto, + Yield, + NextForYield + }; + + sealed class ControlFlow { + internal readonly ControlFlowKind Kind; + internal readonly LabelTarget Label; + internal readonly object Value; + + private ControlFlow(ControlFlowKind kind) + : this(kind, null, null) { + } + + private ControlFlow(ControlFlowKind kind, LabelTarget label, object value) { + Kind = kind; + Label = label; + Value = value; + } + + internal static ControlFlow YieldReturn(object value) { + Debug.Assert(!(value is ControlFlow)); + + return new ControlFlow(ControlFlowKind.Yield, null, value); + } + + internal static ControlFlow Goto(LabelTarget label, object value) { + return new ControlFlow(ControlFlowKind.Goto, label, value); + } + + // Hold on to one instance for each member of the ControlFlow enumeration to avoid unnecessary allocation + internal static readonly ControlFlow NextStatement = new ControlFlow(ControlFlowKind.NextStatement); + internal static readonly ControlFlow NextForYield = new ControlFlow(ControlFlowKind.NextForYield); + internal static readonly ControlFlow YieldBreak = new ControlFlow(ControlFlowKind.Yield); + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/EvaluationAddress.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/EvaluationAddress.cs new file mode 100644 index 0000000000..241af51b1c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/EvaluationAddress.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Interpretation { + internal class EvaluationAddress { + private readonly Expression _expr; + + internal EvaluationAddress(Expression expression) { + _expr = expression; + } + + internal virtual object GetValue(InterpreterState state, bool outParam) { + return Interpreter.Evaluate(state, _expr); + } + + internal virtual object AssignValue(InterpreterState state, object value) { + return Interpreter.EvaluateAssign(state, _expr, value); + } + + internal Expression Expression { + get { + return _expr; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/InterpretedScriptCode.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/InterpretedScriptCode.cs new file mode 100644 index 0000000000..f351682a2c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/InterpretedScriptCode.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Interpretation { + internal sealed class CallSiteInfo { + public int Counter { get; set; } + public CallSite CallSite { get; set; } + public Interpreter.MatchCallerTarget CallerTarget { get; set; } + } + + public class InterpretedScriptCode : ScriptCode { + // call sites allocated for the tree: + private Dictionary _callSites; + + internal Dictionary CallSites { + get { + if (_callSites == null) { + Interlocked.CompareExchange(ref _callSites, new Dictionary(), null); + } + + return _callSites; + } + } + + internal bool HasCallSites { + get { return _callSites != null; } + } + + public InterpretedScriptCode(LambdaExpression code, SourceUnit sourceUnit) + : base(code, sourceUnit) { + } + + public override void EnsureCompiled() { + // nop + } + + protected override object InvokeTarget(LambdaExpression code, Scope scope) { + return Interpreter.TopLevelExecute(this, scope, LanguageContext); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/Interpreter.Block.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/Interpreter.Block.cs new file mode 100644 index 0000000000..ffe4f9745a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/Interpreter.Block.cs @@ -0,0 +1,222 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpretation { + /// + /// Interpreter partial class. This part contains interpretation code for lambdas. + /// + public static partial class Interpreter { + private static int _DelegateCounter; + private static WeakDictionary _Delegates; + + private static object DoExecute(InterpreterState state, LambdaExpression lambda) { + object ret = Interpreter.Interpret(state, lambda.Body); + + ControlFlow cf = ret as ControlFlow; + if (cf != null) { + return cf.Value; + } else { + return ret; + } + } + + /// + /// Called by the code:LambdaInvoker.Invoke from the delegate generated below by + /// code:GetDelegateForInterpreter. + /// + /// This method must repackage arguments to match the lambdas signature, which + /// may mean repackaging the parameter arrays. + /// + /// Input are two arrays - regular arguments passed into the generated delegate, + /// and (if the delegate had params array), the parameter array, separately. + /// + internal static object InterpretLambda(InterpreterState lexicalParentState, LambdaExpression lambda, object[] args) { + Assert.NotNull(lexicalParentState, lambda, args); + + var state = InterpreterState.Current.Update( + (caller) => lexicalParentState.CreateForLambda(lambda, caller, args) + ); + + try { + object result = Interpret(state, lambda.Body); + + var cf = result as ControlFlow; + if (cf != null) { + return (cf.Kind == ControlFlowKind.Yield) ? cf.Value : null; + } + + return result; + } finally { + InterpreterState.Current.Value = state.Caller; + } + } + + /// + /// Gets the delegate associated with the LambdaExpression. + /// Either it uses cached MethodInfo and creates delegate from it, or it will generate + /// completely new dynamic method, store it in a cache and use it to create the delegate. + /// + private static Delegate GetDelegateForInterpreter(InterpreterState state, LambdaExpression lambda) { + MethodInfo method; + if (!LambdaInvoker.TryGetGenericInvokeMethod(lambda.Parameters.Count, out method) || HasByRefParameter(lambda)) { + return GenerateDelegateForInterpreter(state, lambda); + } + + Type[] signature = GetSignature(lambda); + method = method.MakeGenericMethod(signature); + return ReflectionUtils.CreateDelegate(method, lambda.Type, new LambdaInvoker(lambda, state)); + } + + private static bool HasByRefParameter(LambdaExpression lambda) { + for (int i = 0; i < lambda.Parameters.Count; i++) { + if (lambda.Parameters[i].IsByRef) { + return true; + } + } + return false; + } + + private static Type[] GetSignature(LambdaExpression lambda) { + Type[] result = new Type[1 + lambda.Parameters.Count]; + for (int i = 0; i < lambda.Parameters.Count; i++) { + result[i] = lambda.Parameters[i].Type; + } + result[lambda.Parameters.Count] = lambda.ReturnType; + return result; + } + + private static Delegate GenerateDelegateForInterpreter(InterpreterState state, LambdaExpression lambda) { + if (_Delegates == null) { + Interlocked.CompareExchange>( + ref _Delegates, + new WeakDictionary(), + null + ); + } + + bool found; + MethodInfo method; + + // + // LOCK to find the MethodInfo + // + + lock (_Delegates) { + found = _Delegates.TryGetValue(lambda, out method); + } + + if (!found) { + method = CreateDelegateForInterpreter(lambda.Type); + + // + // LOCK to store the MethodInfo + // (and maybe find one added while we were creating new delegate, in which case + // throw away the new one and use the one from the cache. + // + + lock (_Delegates) { + MethodInfo conflict; + if (!_Delegates.TryGetValue(lambda, out conflict)) { + _Delegates.Add(lambda, method); + } else { + method = conflict; + } + } + } + + return ReflectionUtils.CreateDelegate(method, lambda.Type, new LambdaInvoker(lambda, state)); + } + + /// + /// The core of the interpreter, calling back onto itself via delegates. + /// + private static MethodInfo CreateDelegateForInterpreter(Type type) { + Debug.Assert(type != typeof(Delegate) && typeof(Delegate).IsAssignableFrom(type)); + + // + // Get the desired signature + // + MethodInfo invoke = type.GetMethod("Invoke"); + ParameterInfo[] parameters = invoke.GetParameters(); + + string name = "Interpreted_" + Interlocked.Increment(ref _DelegateCounter); + + Type[] signature = CreateInterpreterSignature(parameters); + DynamicILGen il = Snippets.Shared.CreateDynamicMethod(name, invoke.ReturnType, signature, false); + + // Collect all arguments received by the delegate into an array + // and pass them to the Interpreter along with the LambdaInvoker + + // LambdaInvoker + il.EmitLoadArg(0); + int count = parameters.Length; + + // Create the array + il.EmitInt(count); + il.Emit(OpCodes.Newarr, typeof(object)); + for (int i = 0; i < count; i++) { + il.Emit(OpCodes.Dup); + il.EmitInt(i); + il.EmitLoadArg(i + 1); + EmitExplicitCast(il, parameters[i].ParameterType, typeof(object)); + il.EmitStoreElement(typeof(object)); + } + + // Call back to interpreter + il.EmitCall(LambdaInvoker.GetInvokeMethod()); + + // Cast back to the delegate return type + EmitExplicitCast(il, typeof(object), invoke.ReturnType); + + // And return whatever the result was. + il.Emit(OpCodes.Ret); + + // + // We are done (for now), finish the MethodInfo + // + return il.Finish(); + } + + private static void EmitExplicitCast(ILGen il, Type from, Type to) { + if (!il.TryEmitExplicitCast(from, to)) { + throw new ArgumentException(String.Format("Cannot cast from '{0}' to '{1}'", from, to)); + } + } + + private static Type[] CreateInterpreterSignature(ParameterInfo[] parameters) { + Type[] signature = new Type[parameters.Length + 1]; + + // First one is always LambdaInvoker. + signature[0] = typeof(LambdaInvoker); + + // The rest is copied from the parameter infos. + for (int i = 0; i < parameters.Length; i++) { + signature[i + 1] = parameters[i].ParameterType; + } + + return signature; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/Interpreter.Generated.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/Interpreter.Generated.cs new file mode 100644 index 0000000000..86dae6fdb1 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/Interpreter.Generated.cs @@ -0,0 +1,123 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpretation { + public partial class Interpreter { + + private delegate object InterpretDelegate(InterpreterState state, Expression expression); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private static object Interpret(InterpreterState state, Expression expr) { + switch (expr.NodeType) { + #region Generated Ast Interpreter + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_interpreter from: generate_tree.py + + case ExpressionType.Add: return InterpretBinaryExpression(state, expr); + case ExpressionType.AddChecked: return InterpretBinaryExpression(state, expr); + case ExpressionType.And: return InterpretBinaryExpression(state, expr); + case ExpressionType.AndAlso: return InterpretAndAlsoBinaryExpression(state, expr); + case ExpressionType.ArrayLength: return InterpretUnaryExpression(state, expr); + case ExpressionType.ArrayIndex: return InterpretBinaryExpression(state, expr); + case ExpressionType.Call: return InterpretMethodCallExpression(state, expr); + case ExpressionType.Coalesce: return InterpretCoalesceBinaryExpression(state, expr); + case ExpressionType.Conditional: return InterpretConditionalExpression(state, expr); + case ExpressionType.Constant: return InterpretConstantExpression(state, expr); + case ExpressionType.Convert: return InterpretConvertUnaryExpression(state, expr); + case ExpressionType.ConvertChecked: return InterpretConvertUnaryExpression(state, expr); + case ExpressionType.Divide: return InterpretBinaryExpression(state, expr); + case ExpressionType.Equal: return InterpretBinaryExpression(state, expr); + case ExpressionType.ExclusiveOr: return InterpretBinaryExpression(state, expr); + case ExpressionType.GreaterThan: return InterpretBinaryExpression(state, expr); + case ExpressionType.GreaterThanOrEqual: return InterpretBinaryExpression(state, expr); + case ExpressionType.Invoke: return InterpretInvocationExpression(state, expr); + case ExpressionType.Lambda: return InterpretLambdaExpression(state, expr); + case ExpressionType.LeftShift: return InterpretBinaryExpression(state, expr); + case ExpressionType.LessThan: return InterpretBinaryExpression(state, expr); + case ExpressionType.LessThanOrEqual: return InterpretBinaryExpression(state, expr); + case ExpressionType.ListInit: return InterpretListInitExpression(state, expr); + case ExpressionType.MemberAccess: return InterpretMemberExpression(state, expr); + case ExpressionType.MemberInit: return InterpretMemberInitExpression(state, expr); + case ExpressionType.Modulo: return InterpretBinaryExpression(state, expr); + case ExpressionType.Multiply: return InterpretBinaryExpression(state, expr); + case ExpressionType.MultiplyChecked: return InterpretBinaryExpression(state, expr); + case ExpressionType.Negate: return InterpretUnaryExpression(state, expr); + case ExpressionType.UnaryPlus: return InterpretUnaryExpression(state, expr); + case ExpressionType.NegateChecked: return InterpretUnaryExpression(state, expr); + case ExpressionType.New: return InterpretNewExpression(state, expr); + case ExpressionType.NewArrayInit: return InterpretNewArrayExpression(state, expr); + case ExpressionType.NewArrayBounds: return InterpretNewArrayExpression(state, expr); + case ExpressionType.Not: return InterpretUnaryExpression(state, expr); + case ExpressionType.NotEqual: return InterpretBinaryExpression(state, expr); + case ExpressionType.Or: return InterpretBinaryExpression(state, expr); + case ExpressionType.OrElse: return InterpretOrElseBinaryExpression(state, expr); + case ExpressionType.Parameter: return InterpretParameterExpression(state, expr); + case ExpressionType.Power: return InterpretBinaryExpression(state, expr); + case ExpressionType.Quote: return InterpretQuoteUnaryExpression(state, expr); + case ExpressionType.RightShift: return InterpretBinaryExpression(state, expr); + case ExpressionType.Subtract: return InterpretBinaryExpression(state, expr); + case ExpressionType.SubtractChecked: return InterpretBinaryExpression(state, expr); + case ExpressionType.TypeAs: return InterpretUnaryExpression(state, expr); + case ExpressionType.TypeIs: return InterpretTypeBinaryExpression(state, expr); + case ExpressionType.Assign: return InterpretAssignBinaryExpression(state, expr); + case ExpressionType.Block: return InterpretBlockExpression(state, expr); + case ExpressionType.DebugInfo: return InterpretDebugInfoExpression(state, expr); + case ExpressionType.Decrement: return InterpretUnaryExpression(state, expr); + case ExpressionType.Dynamic: return InterpretDynamicExpression(state, expr); + case ExpressionType.Default: return InterpretEmptyExpression(state, expr); + case ExpressionType.Extension: return InterpretExtensionExpression(state, expr); + case ExpressionType.Goto: return InterpretGotoExpression(state, expr); + case ExpressionType.Increment: return InterpretUnaryExpression(state, expr); + case ExpressionType.Index: return InterpretIndexExpression(state, expr); + case ExpressionType.Label: return InterpretLabelExpression(state, expr); + case ExpressionType.RuntimeVariables: return InterpretRuntimeVariablesExpression(state, expr); + case ExpressionType.Loop: return InterpretLoopExpression(state, expr); + case ExpressionType.Switch: return InterpretSwitchExpression(state, expr); + case ExpressionType.Throw: return InterpretThrowUnaryExpression(state, expr); + case ExpressionType.Try: return InterpretTryExpression(state, expr); + case ExpressionType.Unbox: return InterpretUnboxUnaryExpression(state, expr); + case ExpressionType.AddAssign: + case ExpressionType.AndAssign: + case ExpressionType.DivideAssign: + case ExpressionType.ExclusiveOrAssign: + case ExpressionType.LeftShiftAssign: + case ExpressionType.ModuloAssign: + case ExpressionType.MultiplyAssign: + case ExpressionType.OrAssign: + case ExpressionType.PowerAssign: + case ExpressionType.RightShiftAssign: + case ExpressionType.SubtractAssign: + case ExpressionType.AddAssignChecked: + case ExpressionType.MultiplyAssignChecked: + case ExpressionType.SubtractAssignChecked: + case ExpressionType.PreIncrementAssign: + case ExpressionType.PreDecrementAssign: + case ExpressionType.PostIncrementAssign: + case ExpressionType.PostDecrementAssign: + case ExpressionType.TypeEqual: + return InterpretReducibleExpression(state, expr); + + // *** END GENERATED CODE *** + + #endregion + default: throw Assert.Unreachable; + }; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/Interpreter.MatchCaller.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/Interpreter.MatchCaller.cs new file mode 100644 index 0000000000..8ac842701a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/Interpreter.MatchCaller.cs @@ -0,0 +1,195 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Scripting.Generation; + +namespace Microsoft.Scripting.Interpretation { + public static partial class Interpreter { + internal delegate object MatchCallerTarget(CallSite site, object[] args); + + /// + /// MatchCaller allows to call match maker delegate with the signature (object, CallSite, object[]) + /// It is used by the call site cache lookup logic when searching for applicable rule. + /// + internal static class MatchCaller { + public static object Target0(CallSite site, object[] args) { + return ((CallSite>)site).Target(site); + } + + public static object Target1(CallSite site, object[] args) { + return ((CallSite>)site).Target(site, args[0]); + } + + public static object Target2(CallSite site, object[] args) { + return ((CallSite>)site).Target(site, args[0], args[1]); + } + + public static object Target3(CallSite site, object[] args) { + return ((CallSite>)site).Target(site, args[0], args[1], args[2]); + } + + public static object Target4(CallSite site, object[] args) { + return ((CallSite>)site).Target(site, args[0], args[1], args[2], args[3]); + } + + public static object Target5(CallSite site, object[] args) { + return ((CallSite>)site).Target(site, args[0], args[1], args[2], args[3], args[4]); + } + + public static object Target6(CallSite site, object[] args) { + return ((CallSite>)site).Target(site, args[0], args[1], args[2], args[3], args[4], args[5]); + } + + public static object Target7(CallSite site, object[] args) { + return ((CallSite>)site).Target(site, args[0], args[1], args[2], args[3], args[4], args[5], args[6]); + } + + public static object Target8(CallSite site, object[] args) { + return ((CallSite>)site).Target(site, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); + } + + public static object Target9(CallSite site, object[] args) { + return ((CallSite>)site).Target(site, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); + } + + private struct RefFixer { + internal readonly LocalBuilder Temp; + internal readonly int Index; + + internal RefFixer(LocalBuilder temp, int index) { + Temp = temp; + Index = index; + } + } + + // TODO: Should this really be Type -> WeakReference? + // Issue #1, we'll end up growing the dictionary for each unique type + // Issue #2, we'll lose the generated delegate in the first gen-0 + // collection. + // + // We probably need to replace this with an actual cache that holds + // onto the delegates and ages them out. + // + private static readonly Dictionary _Callers = new Dictionary(); + private static readonly Type[] _CallerSignature = new Type[] { typeof(CallSite), typeof(object[]) }; + + internal static MatchCallerTarget GetCaller(Type type) { + MatchCallerTarget target; + + // LOCK to extract the weak reference with the updater DynamicMethod + lock (_Callers) { + if (!_Callers.TryGetValue(type, out target)) { + target = (MatchCallerTarget)CreateCaller(type); + _Callers[type] = target; + } + } + + return target; + } + + private static int _id; + + /// + /// Uses LCG to create method such as this: + /// + /// object MatchCaller(object target, CallSite site, object[] args) { + /// return ((ActualDelegateType)target)(site, args[0], args[1], args[2], ...); + /// } + /// + /// inserting appropriate casts and boxings as needed. + /// + /// Type of the delegate to call + /// A MatchCallerTarget delegate. + private static object CreateCaller(Type type) { + PerfTrack.NoteEvent(PerfTrack.Categories.Count, "Interpreter.MatchCaller.CreateCaller"); + + MethodInfo invoke = type.GetMethod("Invoke"); + ParameterInfo[] parameters = invoke.GetParameters(); + + var il = Snippets.Shared.CreateDynamicMethod("_istub_" + Interlocked.Increment(ref _id), typeof(object), _CallerSignature, false); + Type siteType = typeof(CallSite<>).MakeGenericType(type); + + List fixers = null; + + // Emit the call site and cast it to the right type + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Castclass, siteType); + il.Emit(OpCodes.Ldfld, siteType.GetField("Target")); + + // CallSite + il.Emit(OpCodes.Ldarg_0); + + // Arguments + for (int i = 1; i < parameters.Length; i++) { + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldc_I4, i - 1); + il.Emit(OpCodes.Ldelem_Ref); + Type pt = parameters[i].ParameterType; + if (pt.IsByRef) { + RefFixer rf = new RefFixer(il.DeclareLocal(pt.GetElementType()), i - 1); + if (rf.Temp.LocalType.IsValueType) { + il.Emit(OpCodes.Unbox_Any, rf.Temp.LocalType); + } else if (rf.Temp.LocalType != typeof(object)) { + il.Emit(OpCodes.Castclass, rf.Temp.LocalType); + } + il.Emit(OpCodes.Stloc, rf.Temp); + il.Emit(OpCodes.Ldloca, rf.Temp); + + if (fixers == null) { + fixers = new List(); + } + fixers.Add(rf); + } else if (pt.IsValueType) { + il.Emit(OpCodes.Unbox_Any, pt); + } else if (pt != typeof(object)) { + il.Emit(OpCodes.Castclass, pt); + } + } + + // Call the delegate + il.Emit(OpCodes.Callvirt, invoke); + + // Propagate the ref parameters back into the array. + if (fixers != null) { + foreach (RefFixer rf in fixers) { + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldc_I4, rf.Index); + il.Emit(OpCodes.Ldloc, rf.Temp); + if (rf.Temp.LocalType.IsValueType) { + il.Emit(OpCodes.Box, rf.Temp.LocalType); + } + il.Emit(OpCodes.Stelem_Ref); + } + } + + // Return value + if (invoke.ReturnType == typeof(void)) { + il.Emit(OpCodes.Ldnull); + } else if (invoke.ReturnType.IsValueType) { + il.Emit(OpCodes.Box, invoke.ReturnType); + } + + il.Emit(OpCodes.Ret); + return il.CreateDelegate(); + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/Interpreter.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/Interpreter.cs new file mode 100644 index 0000000000..f8db2978f7 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/Interpreter.cs @@ -0,0 +1,1613 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Dynamic; +using System.Dynamic.Binders; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Scripting.Interpretation")] + +namespace Microsoft.Scripting.Interpretation { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + public static partial class Interpreter { + #region Entry points + + public static object TopLevelExecute(InterpretedScriptCode scriptCode, params object[] args) { + ContractUtils.RequiresNotNull(scriptCode, "scriptCode"); + + var state = InterpreterState.Current.Update( + (caller) => InterpreterState.CreateForTopLambda(scriptCode, scriptCode.Code, caller, args) + ); + + try { + return DoExecute(state, scriptCode.Code); + } finally { + InterpreterState.Current.Value = state.Caller; + } + } + + internal static object Evaluate(InterpreterState state, Expression expression) { + object result = Interpret(state, expression); + + if (result is ControlFlow) { + throw new InvalidOperationException("Invalid expression"); + } + + return result; + } + + internal static object ExecuteGenerator(InterpreterState state, Expression expression) { + return Interpret(state, expression); + } + + #endregion + + /// + /// Evaluates expression and checks it for ControlFlow. If it is control flow, returns true, + /// otherwise returns false. + /// + /// + /// + /// Result of the evaluation + /// true if control flow, false if not + private static bool InterpretAndCheckFlow(InterpreterState state, Expression node, out object result) { + result = Interpret(state, node); + + return result != ControlFlow.NextForYield && result is ControlFlow; + } + + /// + /// Evaluates an expression and checks to see if the ControlFlow is NextForYield. If it is then we are currently + /// searching for the next yield and we need to execute any additional nodes in a larger compound node. + /// + private static bool InterpretAndCheckYield(InterpreterState state, Expression target, out object res) { + res = Interpret(state, target); + if (res != ControlFlow.NextForYield) { + return true; + } + return false; + } + + // Individual expressions and statements + + private static object InterpretConstantExpression(InterpreterState state, Expression expr) { + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + ConstantExpression node = (ConstantExpression)expr; + return node.Value; + } + + private static object InterpretConditionalExpression(InterpreterState state, Expression expr) { + ConditionalExpression node = (ConditionalExpression)expr; + object test; + + if (InterpretAndCheckFlow(state, node.Test, out test)) { + return test; + } + + if (test == ControlFlow.NextForYield || (bool)test) { + if (InterpretAndCheckYield(state, node.IfTrue, out test)) { + return test; + } + } + + return Interpret(state, node.IfFalse); + } + + private static bool IsInputParameter(ParameterInfo pi) { + return !pi.IsOut || (pi.Attributes & ParameterAttributes.In) != 0; + } + + private static object InvokeMethod(InterpreterState state, MethodInfo method, object instance, params object[] parameters) { + // TODO: Cache !!! + ReflectedCaller _caller = null; + + if (_caller == null) { + _caller = ReflectedCaller.Create(method); + } + + try { + if (instance == null) { + return _caller.Invoke(parameters); + } else { + return _caller.InvokeInstance(instance, parameters); + } + } catch (Exception e) { + state.ScriptCode.LanguageContext.InterpretExceptionThrow(state, e, false); + throw; + } + } + + private static object InterpretInvocationExpression(InterpreterState state, Expression expr) { + InvocationExpression node = (InvocationExpression)expr; + + // TODO: this should have the same semantics of the compiler + // in particular, it doesn't handle the case where the left hand + // side returns a lambda that we need to interpret + return InterpretMethodCallExpression(state, Expression.Call(node.Expression, node.Expression.Type.GetMethod("Invoke"), ArrayUtils.ToArray(node.Arguments))); + } + + private static object InterpretIndexExpression(InterpreterState state, Expression expr) { + var node = (IndexExpression)expr; + + if (node.Indexer != null) { + return InterpretMethodCallExpression( + state, + Expression.Call(node.Object, node.Indexer.GetGetMethod(true), node.Arguments) + ); + } + + if (node.Arguments.Count != 1) { + var get = node.Object.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance); + return InterpretMethodCallExpression( + state, + Expression.Call(node.Object, get, node.Arguments) + ); + } + + object array, index; + + if (InterpretAndCheckFlow(state, node.Object, out array)) { + return array; + } + if (InterpretAndCheckFlow(state, node.Arguments[0], out index)) { + return index; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + return ((Array)array).GetValue((int)index); + } + + private static object InterpretMethodCallExpression(InterpreterState state, Expression expr) { + MethodCallExpression node = (MethodCallExpression)expr; + + object instance = null; + // Evaluate the instance first (if the method is non-static) + if (!node.Method.IsStatic) { + if (InterpretAndCheckFlow(state, node.Object, out instance)) { + return instance; + } + } + + var parameterInfos = node.Method.GetParameters(); + + object[] parameters; + if (!state.TryGetStackState(expr, out parameters)) { + parameters = new object[parameterInfos.Length]; + } + + Debug.Assert(parameters.Length == parameterInfos.Length); + + int lastByRefParamIndex = -1; + var paramAddrs = new EvaluationAddress[parameterInfos.Length]; + for (int i = 0; i < parameterInfos.Length; i++) { + ParameterInfo info = parameterInfos[i]; + + if (info.ParameterType.IsByRef) { + lastByRefParamIndex = i; + paramAddrs[i] = EvaluateAddress(state, node.Arguments[i]); + + object value = paramAddrs[i].GetValue(state, !IsInputParameter(info)); + if (IsInputParameter(info)) { + if (value != ControlFlow.NextForYield) { + // implict cast? + parameters[i] = Cast.Explicit(value, info.ParameterType.GetElementType()); + } + } + } else if (IsInputParameter(info)) { + Expression arg = node.Arguments[i]; + object argValue = null; + if (arg != null) { + if (InterpretAndCheckFlow(state, arg, out argValue)) { + if (state.CurrentYield != null) { + state.SaveStackState(node, parameters); + } + + return argValue; + } + } + + if (argValue != ControlFlow.NextForYield) { + parameters[i] = argValue; + } + } + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + try { + object res; + try { + // Call the method + res = InvokeMethod(state, node.Method, instance, parameters); + } finally { + // expose by-ref args + for (int i = 0; i <= lastByRefParamIndex; i++) { + if (parameterInfos[i].ParameterType.IsByRef) { + paramAddrs[i].AssignValue(state, parameters[i]); + } + } + } + + // back propagate instance on value types if the instance supports it. + if (node.Method.DeclaringType != null && node.Method.DeclaringType.IsValueType && !node.Method.IsStatic) { + EvaluateAssign(state, node.Object, instance); + } + + return res; + } catch (TargetInvocationException e) { + // Unwrap the real (inner) exception and raise it + throw ExceptionHelpers.UpdateForRethrow(e.InnerException); + } + } + + private static object InterpretAndAlsoBinaryExpression(InterpreterState state, Expression expr) { + BinaryExpression node = (BinaryExpression)expr; + object ret; + if (InterpretAndCheckFlow(state, node.Left, out ret)) { + return ret; + } + + if (ret == ControlFlow.NextForYield || (bool)ret) { + return Interpret(state, node.Right); + } + + return ret; + } + + private static object InterpretOrElseBinaryExpression(InterpreterState state, Expression expr) { + BinaryExpression node = (BinaryExpression)expr; + object ret; + if (InterpretAndCheckFlow(state, node.Left, out ret)) { + return ret; + } + + if (ret == ControlFlow.NextForYield || !(bool)ret) { + return Interpret(state, node.Right); + } + + return ret; + } + + // TODO: support conversion lambda + private static object InterpretCoalesceBinaryExpression(InterpreterState state, Expression expr) { + BinaryExpression node = (BinaryExpression)expr; + + object ret; + if (InterpretAndCheckFlow(state, node.Left, out ret)) { + return ret; + } + + if (ret == ControlFlow.NextForYield || ret == null) { + return Interpret(state, node.Right); + } + + return ret; + } + + private static object InterpretReducibleExpression(InterpreterState state, Expression expr) { + Debug.Assert(expr.CanReduce); + + //expr is an OpAssignement expression. + //Reduce it before interpreting. + return Interpret(state, expr.Reduce()); + } + + private static object InterpretBinaryExpression(InterpreterState state, Expression expr) { + BinaryExpression node = (BinaryExpression)expr; + + object left, right; + + if (InterpretAndCheckFlow(state, node.Left, out left)) { + return left; + } + if (InterpretAndCheckFlow(state, node.Right, out right)) { + return right; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + if (node.Method != null) { + return node.Method.Invoke(null, new object[] { left, right }); + } else { + return EvaluateBinaryOperator(node.NodeType, left, right); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private static object EvaluateBinaryOperator(ExpressionType nodeType, object l, object r) { + switch (nodeType) { + case ExpressionType.ArrayIndex: + Array array = (Array)l; + int index = (int)r; + return array.GetValue(index); + + case ExpressionType.GreaterThan: + return ScriptingRuntimeHelpers.BooleanToObject(((IComparable)l).CompareTo(r) > 0); + case ExpressionType.LessThan: + return ScriptingRuntimeHelpers.BooleanToObject(((IComparable)l).CompareTo(r) < 0); + case ExpressionType.GreaterThanOrEqual: + return ScriptingRuntimeHelpers.BooleanToObject(((IComparable)l).CompareTo(r) >= 0); + case ExpressionType.LessThanOrEqual: + return ScriptingRuntimeHelpers.BooleanToObject(((IComparable)l).CompareTo(r) <= 0); + case ExpressionType.Equal: + return ScriptingRuntimeHelpers.BooleanToObject(TestEquals(l, r)); + + case ExpressionType.NotEqual: + return ScriptingRuntimeHelpers.BooleanToObject(!TestEquals(l, r)); + + case ExpressionType.Multiply: + return EvalMultiply(l, r); + case ExpressionType.Add: + return EvalAdd(l, r); + case ExpressionType.Subtract: + return EvalSub(l, r); + case ExpressionType.Divide: + return EvalDiv(l, r); + case ExpressionType.Modulo: + return EvalMod(l, r); + case ExpressionType.And: + return EvalAnd(l, r); + case ExpressionType.Or: + return EvalOr(l, r); + case ExpressionType.ExclusiveOr: + return EvalXor(l, r); + case ExpressionType.AddChecked: + return EvalAddChecked(l, r); + case ExpressionType.MultiplyChecked: + return EvalMultiplyChecked(l, r); + case ExpressionType.SubtractChecked: + return EvalSubChecked(l, r); + case ExpressionType.Power: + return EvalPower(l, r); + + default: + throw new NotImplementedException(nodeType.ToString()); + } + } + + private static object EvalMultiply(object l, object r) { + if (l is int) return ScriptingRuntimeHelpers.Int32ToObject((int)l * (int)r); + if (l is uint) return (uint)l * (uint)r; + if (l is short) return (short)((short)l * (short)r); + if (l is ushort) return (ushort)((ushort)l * (ushort)r); + if (l is long) return (long)l * (long)r; + if (l is ulong) return (ulong)l * (ulong)r; + if (l is float) return (float)l * (float)r; + if (l is double) return (double)l * (double)r; + throw new InvalidOperationException("multiply: {0} " + CompilerHelpers.GetType(l).Name); + } + private static object EvalMultiplyChecked(object l, object r) { + if (l is int) return ScriptingRuntimeHelpers.Int32ToObject(checked((int)l * (int)r)); + if (l is uint) return checked((uint)l * (uint)r); + if (l is short) return checked((short)((short)l * (short)r)); + if (l is ushort) return checked((ushort)((ushort)l * (ushort)r)); + if (l is long) return checked((long)l * (long)r); + if (l is ulong) return checked((ulong)l * (ulong)r); + if (l is float) return checked((float)l * (float)r); + if (l is double) return checked((double)l * (double)r); + throw new InvalidOperationException("multiply: {0} " + CompilerHelpers.GetType(l).Name); + } + + private static object EvalAdd(object l, object r) { + if (l is int) return ScriptingRuntimeHelpers.Int32ToObject((int)l + (int)r); + if (l is uint) return (uint)l + (uint)r; + if (l is short) return (short)((short)l + (short)r); + if (l is ushort) return (ushort)((ushort)l + (ushort)r); + if (l is long) return (long)l + (long)r; + if (l is ulong) return (ulong)l + (ulong)r; + if (l is float) return (float)l + (float)r; + if (l is double) return (double)l + (double)r; + throw new InvalidOperationException("add: {0} " + CompilerHelpers.GetType(l).Name); + } + + private static object EvalAddChecked(object l, object r) { + if (l is int) return ScriptingRuntimeHelpers.Int32ToObject(checked((int)l + (int)r)); + if (l is uint) return checked((uint)l + (uint)r); + if (l is short) return checked((short)((short)l + (short)r)); + if (l is ushort) return checked((ushort)((ushort)l + (ushort)r)); + if (l is long) return checked((long)l + (long)r); + if (l is ulong) return checked((ulong)l + (ulong)r); + if (l is float) return checked((float)l + (float)r); + if (l is double) return checked((double)l + (double)r); + throw new InvalidOperationException("add: {0} " + CompilerHelpers.GetType(l).Name); + } + + private static object EvalSub(object l, object r) { + if (l is int) return ScriptingRuntimeHelpers.Int32ToObject((int)l - (int)r); + if (l is uint) return (uint)l - (uint)r; + if (l is short) return (short)((short)l - (short)r); + if (l is ushort) return (ushort)((ushort)l - (ushort)r); + if (l is long) return (long)l - (long)r; + if (l is ulong) return (ulong)l - (ulong)r; + if (l is float) return (float)l - (float)r; + if (l is double) return (double)l - (double)r; + throw new InvalidOperationException("sub: {0} " + CompilerHelpers.GetType(l).Name); + } + private static object EvalSubChecked(object l, object r) { + if (l is int) return ScriptingRuntimeHelpers.Int32ToObject(checked((int)l - (int)r)); + if (l is uint) return checked((uint)l - (uint)r); + if (l is short) return checked((short)((short)l - (short)r)); + if (l is ushort) return checked((ushort)((ushort)l - (ushort)r)); + if (l is long) return checked((long)l - (long)r); + if (l is ulong) return checked((ulong)l - (ulong)r); + if (l is float) return checked((float)l - (float)r); + if (l is double) return checked((double)l - (double)r); + throw new InvalidOperationException("sub: {0} " + CompilerHelpers.GetType(l).Name); + } + + private static object EvalMod(object l, object r) { + if (l is int) return ScriptingRuntimeHelpers.Int32ToObject((int)l % (int)r); + if (l is uint) return (uint)l % (uint)r; + if (l is short) return (short)((short)l % (short)r); + if (l is ushort) return (ushort)((ushort)l % (ushort)r); + if (l is long) return (long)l % (long)r; + if (l is ulong) return (ulong)l % (ulong)r; + if (l is float) return (float)l % (float)r; + if (l is double) return (double)l % (double)r; + throw new InvalidOperationException("mod: {0} " + CompilerHelpers.GetType(l).Name); + } + + private static object EvalDiv(object l, object r) { + if (l is int) return ScriptingRuntimeHelpers.Int32ToObject((int)l / (int)r); + if (l is uint) return (uint)l / (uint)r; + if (l is short) return (short)((short)l / (short)r); + if (l is ushort) return (ushort)((ushort)l / (ushort)r); + if (l is long) return (long)l / (long)r; + if (l is ulong) return (ulong)l / (ulong)r; + if (l is float) return (float)l / (float)r; + if (l is double) return (double)l / (double)r; + throw new InvalidOperationException("div: {0} " + CompilerHelpers.GetType(l).Name); + } + + private static object EvalAnd(object l, object r) { + if (l is int) return ScriptingRuntimeHelpers.Int32ToObject((int)l & (int)r); + if (l is uint) return (uint)l & (uint)r; + if (l is short) return (short)((short)l & (short)r); + if (l is ushort) return (ushort)((ushort)l & (ushort)r); + if (l is long) return (long)l & (long)r; + if (l is ulong) return (ulong)l & (ulong)r; + throw new InvalidOperationException("and: {0} " + CompilerHelpers.GetType(l).Name); + } + + private static object EvalOr(object l, object r) { + if (l is int) return ScriptingRuntimeHelpers.Int32ToObject((int)l | (int)r); + if (l is uint) return (uint)l | (uint)r; + if (l is short) return (short)((short)l | (short)r); + if (l is ushort) return (ushort)((ushort)l | (ushort)r); + if (l is long) return (long)l | (long)r; + if (l is ulong) return (ulong)l | (ulong)r; + throw new InvalidOperationException("or: {0} " + CompilerHelpers.GetType(l).Name); + } + + private static object EvalXor(object l, object r) { + if (l is int) return ScriptingRuntimeHelpers.Int32ToObject((int)l ^ (int)r); + if (l is uint) return (uint)l ^ (uint)r; + if (l is short) return (short)((short)l ^ (short)r); + if (l is ushort) return (ushort)((ushort)l ^ (ushort)r); + if (l is long) return (long)l ^ (long)r; + if (l is ulong) return (ulong)l ^ (ulong)r; + throw new InvalidOperationException("xor: {0} " + CompilerHelpers.GetType(l).Name); + } + + private static object EvalPower(object l, object r) { + return System.Math.Pow((double)l, (double)r); + } + + private static object EvalCoalesce(object l, object r) { + return l ?? r; + } + + + private static bool TestEquals(object l, object r) { + // We don't need to go through the same type checks as the emit case, + // since we know we're always dealing with boxed objects. + + return Object.Equals(l, r); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")] + private static object InterpretQuoteUnaryExpression(InterpreterState state, Expression expr) { + // TODO: should we do all the fancy tree rewrite stuff here? + return ((UnaryExpression)expr).Operand; + } + + private static object InterpretUnboxUnaryExpression(InterpreterState state, Expression expr) { + UnaryExpression node = (UnaryExpression)expr; + + object value; + if (InterpretAndCheckFlow(state, node.Operand, out value)) { + return value; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + if (value != null && node.Type == value.GetType()) { + return value; + } + + throw new InvalidCastException(string.Format("cannot unbox value to type '{0}'", node.Type)); + } + + private static object InterpretConvertUnaryExpression(InterpreterState state, Expression expr) { + UnaryExpression node = (UnaryExpression)expr; + + object value; + if (InterpretAndCheckFlow(state, node.Operand, out value)) { + return value; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + if (node.Type == typeof(void)) { + return null; + } + + // TODO: distinguish between Convert and ConvertChecked + // TODO: semantics should match compiler + return Cast.Explicit(value, node.Type); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private static object InterpretUnaryExpression(InterpreterState state, Expression expr) { + UnaryExpression node = (UnaryExpression)expr; + + object value; + if (InterpretAndCheckFlow(state, node.Operand, out value)) { + return value; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + switch (node.NodeType) { + case ExpressionType.TypeAs: + if (value != null && expr.Type.IsAssignableFrom(value.GetType())) { + return value; + } else { + return null; + } + + case ExpressionType.Not: + if (value is bool) return (bool)value ? ScriptingRuntimeHelpers.False : ScriptingRuntimeHelpers.True; + if (value is int) return ScriptingRuntimeHelpers.Int32ToObject((int)~(int)value); + if (value is long) return (long)~(long)value; + if (value is short) return (short)~(short)value; + if (value is uint) return (uint)~(uint)value; + if (value is ulong) return (ulong)~(ulong)value; + if (value is ushort) return (ushort)~(ushort)value; + if (value is byte) return (byte)~(byte)value; + if (value is sbyte) return (sbyte)~(sbyte)value; + throw new InvalidOperationException("can't perform unary not on type " + CompilerHelpers.GetType(value).Name); + + case ExpressionType.Negate: + if (value is int) return ScriptingRuntimeHelpers.Int32ToObject((int)(-(int)value)); + if (value is long) return (long)(-(long)value); + if (value is short) return (short)(-(short)value); + if (value is float) return -(float)value; + if (value is double) return -(double)value; + throw new InvalidOperationException("can't negate type " + CompilerHelpers.GetType(value).Name); + + case ExpressionType.UnaryPlus: + if (value is int) return ScriptingRuntimeHelpers.Int32ToObject((int)+(int)value); + if (value is long) return (long)+(long)value; + if (value is short) return (short)+(short)value; + if (value is uint) return (uint)+(uint)value; + if (value is ulong) return (ulong)+(ulong)value; + if (value is ushort) return (ushort)+(ushort)value; + if (value is byte) return (byte)+(byte)value; + if (value is sbyte) return (sbyte)+(sbyte)value; + throw new InvalidOperationException("can't perform unary plus on type " + CompilerHelpers.GetType(value).Name); + + case ExpressionType.NegateChecked: + if (value is int) return ScriptingRuntimeHelpers.Int32ToObject(checked((int)(-(int)value))); + if (value is long) return checked((long)(-(long)value)); + if (value is short) return checked((short)(-(short)value)); + if (value is float) return checked(-(float)value); + if (value is double) return checked(-(double)value); + throw new InvalidOperationException("can't negate type " + CompilerHelpers.GetType(value).Name); + + case ExpressionType.ArrayLength: + System.Array arr = (System.Array)value; + return arr.Length; + + default: + throw new NotImplementedException(); + } + } + + private static object InterpretRuntimeVariablesExpression(InterpreterState state, Expression expr) { + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + RuntimeVariablesExpression node = (RuntimeVariablesExpression)expr; + return new InterpreterVariables(state, node); + } + + private static object InterpretNewExpression(InterpreterState state, Expression expr) { + NewExpression node = (NewExpression)expr; + + object[] args = new object[node.Arguments.Count]; + for (int i = 0; i < node.Arguments.Count; i++) { + object argValue; + if (InterpretAndCheckFlow(state, node.Arguments[i], out argValue)) { + return argValue; + } + args[i] = argValue; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + try { + return node.Constructor.Invoke(args); + } catch (TargetInvocationException e) { + throw ExceptionHelpers.UpdateForRethrow(e.InnerException); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")] + private static object InterpretListInitExpression(InterpreterState state, Expression expr) { + throw new NotImplementedException("InterpretListInitExpression"); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")] + private static object InterpretMemberInitExpression(InterpreterState state, Expression expr) { + throw new NotImplementedException("InterpretMemberInitExpression"); + } + + private static object InterpretTypeBinaryExpression(InterpreterState state, Expression expr) { + TypeBinaryExpression node = (TypeBinaryExpression)expr; + object value; + if (InterpretAndCheckFlow(state, node.Expression, out value)) { + return value; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + return ScriptingRuntimeHelpers.BooleanToObject( + node.TypeOperand.IsInstanceOfType(value) + ); + } + + private static object InterpretDynamicExpression(InterpreterState state, Expression expr) { + DynamicExpression node = (DynamicExpression)expr; + var arguments = node.Arguments; + + object[] args; + if (!state.TryGetStackState(node, out args)) { + args = new object[arguments.Count]; + } + + for (int i = 0, n = arguments.Count; i < n; i++) { + object argValue; + if (InterpretAndCheckFlow(state, arguments[i], out argValue)) { + if (state.CurrentYield != null) { + state.SaveStackState(node, args); + } + + return argValue; + } + if (argValue != ControlFlow.NextForYield) { + args[i] = argValue; + } + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + var metaAction = node.Binder as MetaObjectBinder; + if (metaAction != null) { + return InterpretMetaAction(state, metaAction, node, args); + } + + PerfTrack.NoteEvent(PerfTrack.Categories.Count, "Interpreter.Site: Compiling non-meta-action"); + var callSiteInfo = GetCallSite(state, node); + return callSiteInfo.CallerTarget(callSiteInfo.CallSite, args); + } + + private const int SiteCompileThreshold = 2; + + private static object InterpretMetaAction(InterpreterState state, MetaObjectBinder action, DynamicExpression node, object[] argValues) { + var callSites = state.LambdaState.ScriptCode.CallSites; + CallSiteInfo callSiteInfo; + + // TODO: better locking + lock (callSites) { + if (!callSites.TryGetValue(node, out callSiteInfo)) { + callSiteInfo = new CallSiteInfo(); + callSites.Add(node, callSiteInfo); + } + } + + callSiteInfo.Counter++; + if (callSiteInfo.Counter > SiteCompileThreshold) { + if (callSiteInfo.CallSite == null) { + SetCallSite(callSiteInfo, node); + } + return callSiteInfo.CallerTarget(callSiteInfo.CallSite, argValues); + } + + PerfTrack.NoteEvent(PerfTrack.Categories.Count, "Interpreter: Interpreting meta-action"); + + if (argValues.Length == 0) { + throw new InvalidOperationException(); + } + + MetaObject[] args = MetaObject.EmptyMetaObjects; + if (argValues.Length != 1) { + args = new MetaObject[argValues.Length - 1]; + for (int i = 0; i < args.Length; i++) { + args[i] = MetaObject.ObjectToMetaObject( + argValues[i + 1], + Expression.Constant(argValues[i + 1]) + ); + } + } + + MetaObject binding = action.Bind( + MetaObject.ObjectToMetaObject( + argValues[0], + Expression.Constant(argValues[0]) + ), + args + ); + + if (binding == null) { + throw new InvalidOperationException("Bind cannot return null."); + } + + // restrictions ignored, they should be valid: + AssertTrueRestrictions(state, binding); + + var result = Interpret(state, binding.Expression); + return result; + } + + [Conditional("DEBUG")] + private static void AssertTrueRestrictions(InterpreterState state, MetaObject binding) { + var test = binding.Restrictions.ToExpression(); + var result = Interpret(state, test); + Debug.Assert(result is bool && (bool)result); + } + + private static CallSiteInfo GetCallSite(InterpreterState state, DynamicExpression node) { + CallSiteInfo callSiteInfo; + var callSites = state.LambdaState.ScriptCode.CallSites; + + // TODO: better locking + lock (callSites) { + if (!callSites.TryGetValue(node, out callSiteInfo)) { + callSiteInfo = new CallSiteInfo(); + SetCallSite(callSiteInfo, node); + callSites.Add(node, callSiteInfo); + } + } + + return callSiteInfo; + } + + // The ReflectiveCaller cache + private static readonly Dictionary, ReflectedCaller> _executeSites = new Dictionary, ReflectedCaller>(); + + private static void SetCallSite(CallSiteInfo info, DynamicExpression node) { + var arguments = node.Arguments; + + // TODO: remove CodeContext special case + if (arguments.Count > 0 && arguments[0].Type != typeof(CodeContext)) { + switch (arguments.Count) { + case 0: + info.CallSite = CallSite>.Create(node.Binder); + info.CallerTarget = new MatchCallerTarget(MatchCaller.Target0); + return; + + case 1: + info.CallSite = CallSite>.Create(node.Binder); + info.CallerTarget = new MatchCallerTarget(MatchCaller.Target1); + return; + + case 2: + info.CallSite = CallSite>.Create(node.Binder); + info.CallerTarget = new MatchCallerTarget(MatchCaller.Target2); + return; + + case 3: + info.CallSite = CallSite>.Create(node.Binder); + info.CallerTarget = new MatchCallerTarget(MatchCaller.Target3); + return; + + case 4: + info.CallSite = CallSite>.Create(node.Binder); + info.CallerTarget = new MatchCallerTarget(MatchCaller.Target4); + return; + + case 5: + info.CallSite = CallSite>.Create(node.Binder); + info.CallerTarget = new MatchCallerTarget(MatchCaller.Target5); + return; + + case 6: + info.CallSite = CallSite>.Create(node.Binder); + info.CallerTarget = new MatchCallerTarget(MatchCaller.Target6); + return; + + case 7: + info.CallSite = CallSite>.Create(node.Binder); + info.CallerTarget = new MatchCallerTarget(MatchCaller.Target7); + return; + + case 8: + info.CallSite = CallSite>.Create(node.Binder); + info.CallerTarget = new MatchCallerTarget(MatchCaller.Target8); + return; + + case 9: + info.CallSite = CallSite>.Create(node.Binder); + info.CallerTarget = new MatchCallerTarget(MatchCaller.Target9); + return; + } + } + + var callSite = CreateCallSite(node); + info.CallSite = callSite; + info.CallerTarget = MatchCaller.GetCaller((callSite.GetType().GetGenericArguments()[0])); + } + + private static CallSite CreateCallSite(DynamicExpression node) { + var arguments = node.Arguments; + + // non-optimized signatures: + Type[] types = CompilerHelpers.GetSiteTypes(arguments, node.Type); + + int i = (arguments.Count > 0 && arguments[0].Type != typeof(CodeContext)) ? 1 : 0; + + for (; i < arguments.Count; i++) { + if (!arguments[i].Type.IsByRef) { + types[i] = typeof(object); + } + } + + ReflectedCaller rc; + lock (_executeSites) { + ValueArray array = new ValueArray(types); + if (!_executeSites.TryGetValue(array, out rc)) { + Type delegateType = DynamicSiteHelpers.MakeCallSiteDelegate(types); + MethodInfo target = typeof(InterpreterHelpers).GetMethod("CreateSite").MakeGenericMethod(delegateType); + _executeSites[array] = rc = ReflectedCaller.Create(target); + } + } + + return (CallSite)rc.Invoke(node.Binder); + } + + private static object InterpretIndexAssignment(InterpreterState state, BinaryExpression node) { + var index = (IndexExpression)node.Left; + + object instance, value; + var args = new object[index.Arguments.Count]; + + if (InterpretAndCheckFlow(state, index.Object, out instance)) { + return instance; + } + + for (int i = 0; i < index.Arguments.Count; i++) { + object arg; + if (InterpretAndCheckFlow(state, index.Arguments[i], out arg)) { + return arg; + } + args[i] = arg; + } + + if (InterpretAndCheckFlow(state, node.Right, out value)) { + return value; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + if (index.Indexer != null) { + // For indexed properties, just call the setter + InvokeMethod(state, index.Indexer.GetSetMethod(true), instance, args); + } else if (index.Arguments.Count != 1) { + // Multidimensional arrays, call set + var set = index.Object.Type.GetMethod("Set", BindingFlags.Public | BindingFlags.Instance); + InvokeMethod(state, set, instance, args); + } else { + ((Array)instance).SetValue(value, (int)args[0]); + } + + return value; + } + + private static object InterpretVariableAssignment(InterpreterState state, Expression expr) { + var node = (BinaryExpression)expr; + object value; + if (InterpretAndCheckFlow(state, node.Right, out value)) { + return value; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + EvaluateAssignVariable(state, node.Left, value); + return value; + } + + private static object InterpretAssignBinaryExpression(InterpreterState state, Expression expr) { + var node = (BinaryExpression)expr; + switch (node.Left.NodeType) { + case ExpressionType.Index: + return InterpretIndexAssignment(state, node); + case ExpressionType.MemberAccess: + return InterpretMemberAssignment(state, node); + case ExpressionType.Parameter: + case ExpressionType.Extension: + return InterpretVariableAssignment(state, node); + default: + throw new InvalidOperationException("Invalid lvalue for assignment: " + node.Left.NodeType); + } + } + + private static object InterpretParameterExpression(InterpreterState state, Expression expr) { + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + return state.GetValue(expr); + } + + private static object InterpretLambdaExpression(InterpreterState state, Expression expr) { + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + LambdaExpression node = (LambdaExpression)expr; + return GetDelegateForInterpreter(state, node); + } + + private static object InterpretMemberAssignment(InterpreterState state, BinaryExpression node) { + var left = (MemberExpression)node.Left; + + object target = null, value; + if (left.Expression != null) { + if (InterpretAndCheckFlow(state, left.Expression, out target)) { + return target; + } + } + if (InterpretAndCheckFlow(state, node.Right, out value)) { + return value; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + switch (left.Member.MemberType) { + case MemberTypes.Field: + FieldInfo field = (FieldInfo)left.Member; + field.SetValue(target, value); + break; + case MemberTypes.Property: + PropertyInfo property = (PropertyInfo)left.Member; + property.SetValue(target, value, null); + break; + default: + Debug.Assert(false, "Invalid member type"); + break; + } + return value; + } + + private static object InterpretMemberExpression(InterpreterState state, Expression expr) { + MemberExpression node = (MemberExpression)expr; + + object self = null; + if (node.Expression != null) { + if (InterpretAndCheckFlow(state, node.Expression, out self)) { + return self; + } + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + switch (node.Member.MemberType) { + case MemberTypes.Field: + FieldInfo field = (FieldInfo)node.Member; + return field.GetValue(self); + case MemberTypes.Property: + PropertyInfo property = (PropertyInfo)node.Member; + return property.GetValue(self, ArrayUtils.EmptyObjects); + default: + Debug.Assert(false, "Invalid member type"); + break; + } + + throw new InvalidOperationException(); + } + + private static object InterpretNewArrayExpression(InterpreterState state, Expression expr) { + NewArrayExpression node = (NewArrayExpression)expr; + ConstructorInfo constructor; + + if (node.NodeType == ExpressionType.NewArrayBounds) { + int rank = node.Type.GetArrayRank(); + Type[] types = new Type[rank]; + object[] bounds = new object[rank]; + for (int i = 0; i < rank; i++) { + types[i] = typeof(int); + object value; + if (InterpretAndCheckFlow(state, node.Expressions[i], out value)) { + return value; + } + bounds[i] = value; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + constructor = expr.Type.GetConstructor(types); + return constructor.Invoke(bounds); + } else { + // this must be ExpressionType.NewArrayInit + object[] values; + if (!state.TryGetStackState(node, out values)) { + values = new object[node.Expressions.Count]; + } + + for (int i = 0; i < node.Expressions.Count; i++) { + object value; + if (InterpretAndCheckFlow(state, node.Expressions[i], out value)) { + if (state.CurrentYield != null) { + // yield w/ expressions on the stack, we need to save the currently + // evaluated nodes for when we come back. + state.SaveStackState(node, values); + } + + return value; + } + + if (value != ControlFlow.NextForYield) { + values[i] = value; + } + } + + if (state.CurrentYield != null) { + // we were just walking looking for yields, this has no result. + return ControlFlow.NextForYield; + } + + if (node.Type != typeof(object[])) { + constructor = expr.Type.GetConstructor(new Type[] { typeof(int) }); + Array contents = (Array)constructor.Invoke(new object[] { node.Expressions.Count }); + // value arrays cannot be cast to object arrays + + for (int i = 0; i < node.Expressions.Count; i++) { + contents.SetValue(values[i], i); + } + return contents; + } + + return values; + } + } + + private static object InterpretGotoExpression(InterpreterState state, Expression expr) { + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + var node = (GotoExpression)expr; + + object value = null; + if (node.Value != null) { + value = Interpret(state, node.Value); + ControlFlow cf = value as ControlFlow; + if (cf != null) { + // propagate + return cf; + } + } + + return ControlFlow.Goto(node.Target, value); + } + + private static object InterpretEmptyExpression(InterpreterState state, Expression expr) { + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + DefaultExpression node = (DefaultExpression)expr; + return ControlFlow.NextStatement; + } + + /// + /// Labeled statement makes break/continue go to the end of the contained expression. + /// + private static object InterpretLabelExpression(InterpreterState state, Expression expr) { + LabelExpression node = (LabelExpression)expr; + + object res = ControlFlow.NextStatement; + if (node.DefaultValue != null) { + res = Interpret(state, node.DefaultValue); + var cf = res as ControlFlow; + if (cf != null && cf.Kind == ControlFlowKind.Goto && cf.Label == node.Label) { + res = cf.Value; + } + } + + return res; + } + + private static object InterpretLoopExpression(InterpreterState state, Expression expr) { + LoopExpression node = (LoopExpression)expr; + + for (; ; ) { + ControlFlow cf; + + object body = Interpret(state, node.Body); + if ((cf = body as ControlFlow) != null) { + if (cf.Kind == ControlFlowKind.Goto) { + if (cf.Label == node.BreakLabel) { + // Break out of the loop and execute next statement outside + return ControlFlow.NextStatement; + } else if (cf.Label != node.ContinueLabel) { + return cf; + } + } else if (cf.Kind == ControlFlowKind.Yield) { + return body; + } + } + } + } + + private static object InterpretDebugInfoExpression(InterpreterState state, Expression expr) { + var node = (DebugInfoExpression)expr; + + if (state.CurrentYield == null) { + // Note: setting index to 0 because we don't have one available + // Index should be removed from SourceLocation + state.CurrentLocation = new SourceLocation(0, node.StartLine, node.StartColumn); + } + + return Interpret(state, node.Expression); + } + + private static object InterpretBlockExpression(InterpreterState state, Expression expr) { + BlockExpression node = (BlockExpression)expr; + + InterpreterState child = state; + if (node.Variables.Count > 0) { + // restore scope if we yielded + if (!state.TryGetStackState(node, out child)) { + // otherwise, create a new nested scope + child = state.CreateForScope(node); + } + } + + try { + var expressions = node.Expressions; + int count = expressions.Count; + + if (count > 0) { + int current = 0; + + for (; ; ) { + object val = null; + Expression ce = expressions[current]; + + if (InterpretAndCheckFlow(child, ce, out val)) { + // Control flow + if (val != ControlFlow.NextStatement) { + ControlFlow cf = (ControlFlow)val; + if (cf.Kind == ControlFlowKind.Goto) { + // Is the goto within the block? + for (int target = 0; target < count; target++) { + LabelExpression le = expressions[target] as LabelExpression; + if (le != null && le.Label == cf.Label) { + // Reset to execute the code from after the label + // We are going to the label and since label is at the end of the + // expression, set to target and we'll advance below. + current = target; + val = null; + goto Next; + } + } + } + + return cf; + } + } + + Next: + // Next expression + current++; + + // Last expression? + if (current >= count) { + return node.Type != typeof(void) ? val : ControlFlow.NextStatement; + } + } + } + } finally { + if (node.Variables.Count > 0) { + if (state.CurrentYield != null) { + // save scope if yielding so we can restore it + state.SaveStackState(node, child); + } + } + } + + return ControlFlow.NextStatement; + } + + private static object InterpretSwitchExpression(InterpreterState state, Expression expr) { + // TODO: yield aware switch + SwitchExpression node = (SwitchExpression)expr; + + object testValue; + if (InterpretAndCheckFlow(state, node.Test, out testValue)) { + return testValue; + } + + int test = (int)testValue; + ReadOnlyCollection cases = node.SwitchCases; + int target = 0; + while (target < cases.Count) { + SwitchCase sc = cases[target]; + if (sc.IsDefault || sc.Value == test) { + break; + } + + target++; + } + + while (target < cases.Count) { + SwitchCase sc = cases[target]; + object result = Interpret(state, sc.Body); + + ControlFlow cf = result as ControlFlow; + if (cf != null) { + if (cf.Label == node.BreakLabel) { + return ControlFlow.NextStatement; + } else if (cf.Kind == ControlFlowKind.Yield || cf.Kind == ControlFlowKind.Goto) { + return cf; + } + } + target++; + } + + return ControlFlow.NextStatement; + } + + #region Exceptions + + [ThreadStatic] + private static List _evalExceptions; + + private static void PopEvalException() { + _evalExceptions.RemoveAt(_evalExceptions.Count - 1); + if (_evalExceptions.Count == 0) _evalExceptions = null; + } + + private static void PushEvalException(Exception exc) { + if (_evalExceptions == null) _evalExceptions = new List(); + _evalExceptions.Add(exc); + } + + private static Exception LastEvalException { + get { + if (_evalExceptions == null || _evalExceptions.Count == 0) { + throw new InvalidOperationException("rethrow outside of catch block"); + } + + return _evalExceptions[_evalExceptions.Count - 1]; + } + } + + private static object InterpretThrowUnaryExpression(InterpreterState state, Expression expr) { + UnaryExpression node = (UnaryExpression)expr; + Exception ex; + + if (node.Operand == null) { + ex = LastEvalException; + } else { + object exception; + if (InterpretAndCheckFlow(state, node.Operand, out exception)) { + return exception; + } + + ex = (Exception)exception; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + state.LambdaState.ScriptCode.LanguageContext.InterpretExceptionThrow(state, ex, true); + throw ex; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2219:DoNotRaiseExceptionsInExceptionClauses")] + private static object InterpretTryExpression(InterpreterState state, Expression expr) { + // TODO: Yield aware + TryExpression node = (TryExpression)expr; + bool rethrow = false, catchFaulted = false; + Exception savedExc = null; + object ret = ControlFlow.NextStatement; + + try { + if (!InterpretAndCheckFlow(state, node.Body, out ret)) { + ret = ControlFlow.NextStatement; + } + } catch (Exception exc) { + rethrow = true; + savedExc = exc; + if (node.Handlers != null) { + PushEvalException(exc); + try { + ret = ControlFlowKind.NextStatement; + foreach (CatchBlock handler in node.Handlers) { + if (handler.Test.IsInstanceOfType(exc)) { + if (handler.Variable != null) { + EvaluateAssignVariable(state, handler.Variable, exc); + } + + if (handler.Filter != null) { + object filterResult; + if (InterpretAndCheckFlow(state, handler.Filter, out filterResult)) { + ret = filterResult; + break; + } else if (!((bool)filterResult)) { + // handler doesn't apply, check next handler. + continue; + } + } + + rethrow = false; + catchFaulted = true; + object body; + if (InterpretAndCheckFlow(state, handler.Body, out body)) { + ret = body; + } + catchFaulted = false; + break; + } + } + } finally { + PopEvalException(); + } + } + } finally { + if (node.Finally != null || ((rethrow || catchFaulted) && node.Fault != null)) { + Expression faultOrFinally = node.Finally ?? node.Fault; + + object result; + if (InterpretAndCheckFlow(state, faultOrFinally, out result) && + result != ControlFlow.NextStatement) { + ret = result; + rethrow = false; + } + } + if (rethrow) { + throw ExceptionHelpers.UpdateForRethrow(savedExc); + } + } + + return ret; + } + + private static object InterpretYieldExpression(InterpreterState state, YieldExpression node) { + // Yield break + if (node.Value == null) { + return ControlFlow.YieldBreak; + } + + if (state.CurrentYield == node) { + // we've just advanced past the current yield, start executing code again. + state.CurrentYield = null; + return ControlFlow.NextStatement; + } + + object res; + if (InterpretAndCheckFlow(state, node.Value, out res) && res != ControlFlow.NextStatement) { + // yield contains flow control. + return res; + } + + if (state.CurrentYield == null) { + // we are the yield, we just ran our code, and now we + // need to return the result. + state.CurrentYield = node; + + return ControlFlow.YieldReturn(res); + } + + return ControlFlow.NextForYield; + } + + private static object InterpretGeneratorExpression(InterpreterState state, GeneratorExpression generator) { + // Fast path for object + if (generator.Label.Type == typeof(object)) { + return InterpretGenerator(state, generator); + } + + // TODO: slow path + return ReflectedCaller.Create( + typeof(Interpreter).GetMethod( + "InterpretGenerator", BindingFlags.NonPublic | BindingFlags.Static + ).MakeGenericMethod(generator.Label.Type) + ).Invoke(state, generator); + } + + private static object InterpretGenerator(InterpreterState state, GeneratorExpression generator) { + var caller = InterpreterState.Current.Value; + if (generator.IsEnumerable) { + return new GeneratorEnumerable( + () => new GeneratorInvoker(generator, state.CreateForGenerator(caller)).Invoke + ); + } else { + return new GeneratorEnumerator( + new GeneratorInvoker(generator, state.CreateForGenerator(caller)).Invoke + ); + } + } + + private static object InterpretExtensionExpression(InterpreterState state, Expression expr) { + var ffc = expr as FinallyFlowControlExpression; + if (ffc != null) { + return Interpret(state, ffc.Body); + } + + var yield = expr as YieldExpression; + if (yield != null) { + return InterpretYieldExpression(state, yield); + } + + var generator = expr as GeneratorExpression; + if (generator != null) { + return InterpretGeneratorExpression(state, generator); + } + + return Interpret(state, expr.ReduceExtensions()); + } + + #endregion + + internal static object EvaluateAssign(InterpreterState state, Expression node, object value) { + switch (node.NodeType) { + case ExpressionType.Parameter: + case ExpressionType.Extension: + return EvaluateAssignVariable(state, node, value); + // TODO: this is wierd, why are we supporting assign to assignment? + case ExpressionType.Assign: + return EvaluateAssignVariable(state, ((BinaryExpression)node).Left, value); + case ExpressionType.MemberAccess: + return EvaluateAssign(state, (MemberExpression)node, value); + default: + return value; + } + } + + private static object EvaluateAssignVariable(InterpreterState state, Expression var, object value) { + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + state.SetValue(var, value); + return value; + } + + private static object EvaluateAssign(InterpreterState state, MemberExpression node, object value) { + object self = null; + if (InterpretAndCheckFlow(state, node.Expression, out self)) { + return self; + } + + if (state.CurrentYield != null) { + return ControlFlow.NextForYield; + } + + switch (node.Member.MemberType) { + case MemberTypes.Field: + FieldInfo field = (FieldInfo)node.Member; + field.SetValue(self, value); + return value; + case MemberTypes.Property: + PropertyInfo property = (PropertyInfo)node.Member; + property.SetValue(self, value, ArrayUtils.EmptyObjects); + return value; + default: + Debug.Assert(false, "Invalid member type"); + break; + } + + throw new InvalidOperationException(); + } + + + private static EvaluationAddress EvaluateAddress(InterpreterState state, Expression node) { + switch (node.NodeType) { + case ExpressionType.Parameter: + return new VariableAddress(node); + case ExpressionType.Block: + return EvaluateAddress(state, (BlockExpression)node); + case ExpressionType.Conditional: + return EvaluateAddress(state, (ConditionalExpression)node); + default: + return new EvaluationAddress(node); + } + } + + + private static EvaluationAddress EvaluateAddress(InterpreterState state, BlockExpression node) { + if (node.Type == typeof(void)) { + throw new NotSupportedException("Address of block without value"); + } + + List addresses = new List(); + foreach (Expression current in node.Expressions) { + addresses.Add(EvaluateAddress(state, current)); + } + return new CommaAddress(node, addresses); + } + + private static EvaluationAddress EvaluateAddress(InterpreterState state, ConditionalExpression node) { + object test = (bool)Interpret(state, node.Test); + + if ((bool)test) { + return EvaluateAddress(state, node.IfTrue); + } else { + return EvaluateAddress(state, node.IfFalse); + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/InterpreterHelpers.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/InterpreterHelpers.cs new file mode 100644 index 0000000000..f21654d0e0 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/InterpreterHelpers.cs @@ -0,0 +1,24 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Runtime.CompilerServices; + +namespace Microsoft.Scripting.Interpretation { + public static class InterpreterHelpers { + public static CallSite CreateSite(CallSiteBinder action) where T : class { + return CallSite.Create(action); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/InterpreterState.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/InterpreterState.cs new file mode 100644 index 0000000000..c25c1271e6 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/InterpreterState.cs @@ -0,0 +1,195 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Dynamic; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpretation { + + internal sealed class LambdaState { + internal Dictionary SpilledStack; + internal YieldExpression CurrentYield; + + internal readonly InterpretedScriptCode ScriptCode; + internal readonly LambdaExpression Lambda; + internal readonly InterpreterState Caller; + internal SourceLocation CurrentLocation; + + public LambdaState(InterpretedScriptCode scriptCode, LambdaExpression lambda, InterpreterState caller) { + Assert.NotNull(scriptCode, lambda); + ScriptCode = scriptCode; + Lambda = lambda; + Caller = caller; + } + } + + /// + /// Represents variable storage for one lambda/scope expression in the + /// interpreter. + /// + public sealed class InterpreterState { + // Current thread's interpreted method frame. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly ThreadLocal Current = new ThreadLocal(); + + private readonly InterpreterState _lexicalParent; + private readonly Dictionary _vars = new Dictionary(); + + // per-lambda state, not-null + private readonly LambdaState _lambdaState; + + private InterpreterState(InterpreterState parent, LambdaState lambdaState) { + Assert.NotNull(lambdaState); + _lexicalParent = parent; + _lambdaState = lambdaState; + } + + internal static InterpreterState CreateForTopLambda(InterpretedScriptCode scriptCode, LambdaExpression lambda, InterpreterState caller, params object[] args) { + return CreateForLambda(scriptCode, lambda, null, caller, args); + } + + internal InterpreterState CreateForLambda(LambdaExpression lambda, InterpreterState caller, object[] args) { + return CreateForLambda(_lambdaState.ScriptCode, lambda, this, caller, args); + } + + internal InterpreterState CreateForGenerator(InterpreterState caller) { + return new InterpreterState(this, new LambdaState(_lambdaState.ScriptCode, caller.Lambda, caller)); + } + + private static InterpreterState CreateForLambda(InterpretedScriptCode scriptCode, LambdaExpression lambda, + InterpreterState lexicalParent, InterpreterState caller, object[] args) { + + InterpreterState state = new InterpreterState(lexicalParent, new LambdaState(scriptCode, lambda, caller)); + + Debug.Assert(args.Length == lambda.Parameters.Count, "number of parameters should match number of arguments"); + + // + // Populate all parameters ... + // + for (int i = 0; i < lambda.Parameters.Count; i++ ) { + state._vars.Add(lambda.Parameters[i], args[i]); + } + + return state; + } + + internal InterpreterState CreateForScope(BlockExpression scope) { + InterpreterState state = new InterpreterState(this, _lambdaState); + foreach (ParameterExpression v in scope.Variables) { + // initialize variables to default(T) + object value; + if (v.Type.IsValueType) { + value = Activator.CreateInstance(v.Type); + } else { + value = null; + } + state._vars.Add(v, value); + } + return state; + } + + public InterpreterState Caller { + get { return _lambdaState.Caller; } + } + + public LambdaExpression Lambda { + get { return _lambdaState.Lambda; } + } + + public ScriptCode ScriptCode { + get { return _lambdaState.ScriptCode; } + } + + public SourceLocation CurrentLocation { + get { return _lambdaState.CurrentLocation; } + internal set { + _lambdaState.CurrentLocation = value; + } + } + + internal LambdaState LambdaState { + get { return _lambdaState; } + } + + internal YieldExpression CurrentYield { + get { return _lambdaState.CurrentYield; } + set { _lambdaState.CurrentYield = value; } + } + + internal bool TryGetStackState(Expression node, out T value) { + object val; + if (_lambdaState.SpilledStack != null && _lambdaState.SpilledStack.TryGetValue(node, out val)) { + _lambdaState.SpilledStack.Remove(node); + + value = (T)val; + return true; + } + + value = default(T); + return false; + } + + internal void SaveStackState(Expression node, object value) { + Debug.Assert(_lambdaState.CurrentYield != null); + + if (_lambdaState.SpilledStack == null) { + _lambdaState.SpilledStack = new Dictionary(); + } + + _lambdaState.SpilledStack[node] = value; + } + + internal object GetValue(Expression variable) { + InterpreterState state = this; + for (; ; ) { + object value; + if (state._vars.TryGetValue(variable, out value)) { + return value; + } + state = state._lexicalParent; + + // Couldn't find variable + if (state == null) { + throw InvalidVariableReference(variable); + } + } + } + + internal void SetValue(Expression variable, object value) { + InterpreterState state = this; + for (; ; ) { + if (state._vars.ContainsKey(variable)) { + state._vars[variable] = value; + return; + } + state = state._lexicalParent; + + // Couldn't find variable + if (state == null) { + throw InvalidVariableReference(variable); + } + } + } + + private static Exception InvalidVariableReference(Expression variable) { + return new InvalidOperationException(string.Format("Variable '{0}' is not defined in an outer scope", variable)); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/InterpreterVariables.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/InterpreterVariables.cs new file mode 100644 index 0000000000..ddff999b40 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/InterpreterVariables.cs @@ -0,0 +1,137 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpretation { + + /// + /// An ILocalVariables implementation for the interpreter + /// + /// TODO: This isn't quite correct, because it doesn't implement the + /// LocalScopeExpression.IsClosure feature that only exposes variables that + /// would otherwise be lifted. To implement it correctly would require a + /// full variable binding pass, something the interpreter doesn't need + /// today. The only thing that this breaks is Python's func_closure + /// + internal sealed class InterpreterVariables : IList { + + // TODO: InterpreterState should store values in strongly typed + // StrongBox, which gives us the correct cast error if the wrong + // type is set at runtime. + private sealed class InterpreterBox : IStrongBox { + private readonly InterpreterState _state; + private readonly Expression _variable; + + internal InterpreterBox(InterpreterState state, Expression variable) { + _state = state; + _variable = variable; + } + + public object Value { + get { return _state.GetValue(_variable); } + set { _state.SetValue(_variable, value); } + } + } + + private readonly InterpreterState _state; + private readonly ReadOnlyCollection _vars; + + internal InterpreterVariables(InterpreterState state, RuntimeVariablesExpression node) { + _state = state; + _vars = node.Variables; + } + + public int Count { + get { return _vars.Count; } + } + + public IStrongBox this[int index] { + get { + return new InterpreterBox(_state, _vars[index]); + } + set { + throw CollectionReadOnly(); + } + } + + public int IndexOf(IStrongBox item) { + for (int i = 0, n = _vars.Count; i < n; i++) { + if (this[i] == item) { + return i; + } + } + return -1; + } + + public bool Contains(IStrongBox item) { + return IndexOf(item) >= 0; + } + + public void CopyTo(IStrongBox[] array, int arrayIndex) { + ContractUtils.RequiresNotNull(array, "array"); + int count = _vars.Count; + if (arrayIndex < 0 || arrayIndex + count > array.Length) { + throw new ArgumentOutOfRangeException("arrayIndex"); + } + for (int i = 0; i < count; i++) { + array[arrayIndex++] = this[i]; + } + } + + bool ICollection.IsReadOnly { + get { return true; } + } + + public IEnumerator GetEnumerator() { + for (int i = 0, n = _vars.Count; i < n; i++) { + yield return this[i]; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + void IList.Insert(int index, IStrongBox item) { + throw CollectionReadOnly(); + } + + void IList.RemoveAt(int index) { + throw CollectionReadOnly(); + } + + void ICollection.Add(IStrongBox item) { + throw CollectionReadOnly(); + } + + void ICollection.Clear() { + throw CollectionReadOnly(); + } + + bool ICollection.Remove(IStrongBox item) { + throw CollectionReadOnly(); + } + + private static Exception CollectionReadOnly() { + throw new NotSupportedException("Collection is read-only."); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/LambdaInvoker.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/LambdaInvoker.cs new file mode 100644 index 0000000000..9ee162c427 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/LambdaInvoker.cs @@ -0,0 +1,147 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpretation { + /// + /// Helper class used by the interpreter to package lambda as a delegate, + /// allow it being called, and then resume interpretation. + /// + public class LambdaInvoker { + private readonly LambdaExpression _lambda; + private readonly InterpreterState _state; + + internal LambdaInvoker(LambdaExpression lambda, InterpreterState state) { + Assert.NotNull(lambda, state); + + _lambda = lambda; + _state = state; + } + + private static MethodInfo _invoke; + private static MethodInfo _invoke0; + private static MethodInfo _invoke1; + private static MethodInfo _invoke2; + private static MethodInfo _invoke3; + private static MethodInfo _invoke4; + private static MethodInfo _invoke5; + private static MethodInfo _invoke6; + private static MethodInfo _invoke7; + + internal static MethodInfo GetInvokeMethod() { + return GetMethod(ref _invoke, "Invoke"); + } + + /// + /// Selects an Invoke method declared in this class given the number of parameters. + /// Note that the selected overload could only be used if no by-ref parameters are needed in the signature. + /// Returns false if there is no overload with that many parameters. + /// + internal static bool TryGetGenericInvokeMethod(int paramCount, out MethodInfo genericMethod) { + switch (paramCount) { + case 0: genericMethod = GetMethod(ref _invoke0, "Invoke0"); return true; + case 1: genericMethod = GetMethod(ref _invoke1, "Invoke1"); return true; + case 2: genericMethod = GetMethod(ref _invoke2, "Invoke2"); return true; + case 3: genericMethod = GetMethod(ref _invoke3, "Invoke3"); return true; + case 4: genericMethod = GetMethod(ref _invoke4, "Invoke4"); return true; + case 5: genericMethod = GetMethod(ref _invoke5, "Invoke5"); return true; + case 6: genericMethod = GetMethod(ref _invoke6, "Invoke6"); return true; + case 7: genericMethod = GetMethod(ref _invoke7, "Invoke7"); return true; + + default: + genericMethod = null; + return false; + } + } + + private static MethodInfo GetMethod(ref MethodInfo method, string name) { + if (method == null) { + Interlocked.CompareExchange(ref method, typeof(LambdaInvoker).GetMethod(name), null); + } + return method; + } + + public object Invoke(object[] args) { + return Interpreter.InterpretLambda(_state, _lambda, args); + } + + public TResult Invoke0() { + return (TResult)Interpreter.InterpretLambda(_state, _lambda, ArrayUtils.EmptyObjects); + } + + public TResult Invoke1(T1 arg1) { + return (TResult)Interpreter.InterpretLambda(_state, _lambda, new object[] { arg1 }); + } + + public TResult Invoke2(T1 arg1, T2 arg2) { + return (TResult)Interpreter.InterpretLambda(_state, _lambda, new object[] { arg1, arg2 }); + } + + public TResult Invoke3(T1 arg1, T2 arg2, T3 arg3) { + return (TResult)Interpreter.InterpretLambda(_state, _lambda, new object[] { arg1, arg2, arg3 }); + } + + public TResult Invoke4(T1 arg1, T2 arg2, T3 arg3, T4 arg4) { + return (TResult)Interpreter.InterpretLambda(_state, _lambda, new object[] { arg1, arg2, arg3, arg4 }); + } + + public TResult Invoke5(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { + return (TResult)Interpreter.InterpretLambda(_state, _lambda, new object[] { arg1, arg2, arg3, arg4, arg5 }); + } + + public TResult Invoke6(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { + return (TResult)Interpreter.InterpretLambda(_state, _lambda, new object[] { arg1, arg2, arg3, arg4, arg5, arg6 }); + } + + public TResult Invoke7(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { + return (TResult)Interpreter.InterpretLambda(_state, _lambda, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7 }); + } + } + + internal sealed class GeneratorInvoker { + private readonly GeneratorExpression _generator; + private readonly InterpreterState _state; + + // Arbitrary constant, chosen to be different from "Finished" + private const int InterpretingGenerator = GeneratorRewriter.Finished + 1; + + internal GeneratorInvoker(GeneratorExpression generator, InterpreterState state) { + _generator = generator; + _state = state; + } + + /// + /// Triggers interpretation of the Lambda + /// + public void Invoke(ref int state, ref T current) { + object res = Interpreter.ExecuteGenerator(_state, _generator.Body); + ControlFlow cf = res as ControlFlow; + if (cf != null && cf.Kind == ControlFlowKind.Yield && _state.CurrentYield != null) { + current = (T)cf.Value; + state = InterpretingGenerator; + return; + } + + //current = default(T); + state = GeneratorRewriter.Finished; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Interpretation/VariableAddress.cs b/merlin/main/runtime/Microsoft.Scripting/Interpretation/VariableAddress.cs new file mode 100644 index 0000000000..7fd4817638 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Interpretation/VariableAddress.cs @@ -0,0 +1,32 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Interpretation { + internal sealed class VariableAddress : EvaluationAddress { + internal VariableAddress(Expression expr) + : base(expr) { + } + + internal override object GetValue(InterpreterState state, bool outParam) { + if (outParam) { + return null; + } + + return base.GetValue(state, outParam); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/InvalidImplementationException.cs b/merlin/main/runtime/Microsoft.Scripting/InvalidImplementationException.cs new file mode 100644 index 0000000000..1f15b3cc48 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/InvalidImplementationException.cs @@ -0,0 +1,38 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.Serialization; + +namespace Microsoft.Scripting { + [Serializable] + public class InvalidImplementationException : Exception { + public InvalidImplementationException() + : base() { + } + + public InvalidImplementationException(string message) + : base(message) { + } + + public InvalidImplementationException(string message, Exception e) + : base(message, e) { + } + +#if !SILVERLIGHT // SerializationInfo + protected InvalidImplementationException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/KeyboardInterruptException.cs b/merlin/main/runtime/Microsoft.Scripting/KeyboardInterruptException.cs new file mode 100644 index 0000000000..0cdf427e7f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/KeyboardInterruptException.cs @@ -0,0 +1,31 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.Serialization; + +namespace Microsoft.Scripting { + [Serializable] + public class KeyboardInterruptException : Exception { + public KeyboardInterruptException() : base() { } + public KeyboardInterruptException(string msg) : base(msg) { } + public KeyboardInterruptException(string message, Exception innerException) + : base(message, innerException) { + } +#if !SILVERLIGHT // SerializationInfo + protected KeyboardInterruptException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/LanguageOptions.cs b/merlin/main/runtime/Microsoft.Scripting/LanguageOptions.cs new file mode 100644 index 0000000000..289088d009 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/LanguageOptions.cs @@ -0,0 +1,129 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Microsoft.Scripting.Utils; +using System.IO; +using System.Threading; + +namespace Microsoft.Scripting { + + [Serializable] + public class LanguageOptions { + private bool _exceptionDetail; + private bool _showClrExceptions; + private bool _interpretedMode; + private readonly bool _perfStats; + private readonly ReadOnlyCollection _searchPaths; + + /// + /// Interpret code instead of emitting it. + /// + public bool InterpretedMode { + get { return _interpretedMode; } + set { _interpretedMode = value; } + } + + /// + /// Display exception detail (callstack) when exception gets caught + /// + public bool ExceptionDetail { + get { return _exceptionDetail; } + set { _exceptionDetail = value; } + } + + public bool ShowClrExceptions { + get { return _showClrExceptions; } + set { _showClrExceptions = value; } + } + + /// + /// Whether to gather performance statistics. + /// + public bool PerfStats { + get { return _perfStats; } + } + + /// + /// Initial file search paths provided by the host. + /// + public ReadOnlyCollection SearchPaths { + get { return _searchPaths; } + } + + public LanguageOptions() + : this(null) { + } + + public LanguageOptions(IDictionary options) { + _interpretedMode = GetOption(options, "InterpretedMode", false); + _exceptionDetail = GetOption(options, "ExceptionDetail", false); + _showClrExceptions = GetOption(options, "ShowClrExceptions", false); + _perfStats = GetOption(options, "PerfStats", false); + _searchPaths = GetSearchPathsOption(options) ?? new ReadOnlyCollection(new[] { "." }); + } + + public static T GetOption(IDictionary options, string name, T defaultValue) { + object value; + if (options != null && options.TryGetValue(name, out value)) { + if (value is T) { + return (T)value; + } + return (T)Convert.ChangeType(value, typeof(T), Thread.CurrentThread.CurrentCulture); + } + return defaultValue; + } + + /// + /// Reads an option whose value is expected to be a collection of non-null strings. + /// Reaturns a read-only copy of the option's value. + /// + public static ReadOnlyCollection GetStringCollectionOption(IDictionary options, string name, params char[] separators) { + object value; + if (options == null || !options.TryGetValue(name, out value)) { + return null; + } + + // a collection: + var collection = value as ICollection; + if (collection != null) { + foreach (var item in collection) { + if (item == null) { + throw new ArgumentException(String.Format("Invalid value for option {0}: collection shouldn't containt null items", name)); + } + } + + return new ReadOnlyCollection(ArrayUtils.MakeArray(collection)); + } + + // a string: + var strValue = value as string; + if (strValue != null && separators != null && separators.Length > 0) { + return new ReadOnlyCollection(StringUtils.Split(strValue, separators, Int32.MaxValue, StringSplitOptions.RemoveEmptyEntries)); + } + + throw new ArgumentException(String.Format("Invalid value for option {0}", name)); + } + + public static ReadOnlyCollection GetSearchPathsOption(IDictionary options) { + return GetStringCollectionOption(options, "SearchPaths", Path.PathSeparator); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + protected static readonly ReadOnlyCollection EmptyStringCollection = new ReadOnlyCollection(ArrayUtils.EmptyStrings); + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Math/BigInteger.cs b/merlin/main/runtime/Microsoft.Scripting/Math/BigInteger.cs new file mode 100644 index 0000000000..f7381e701c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Math/BigInteger.cs @@ -0,0 +1,1773 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Text; +using Microsoft.Contracts; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Math { + /// + /// arbitrary precision integers + /// + [Serializable] + public class BigInteger : IFormattable, IComparable, IConvertible, IEquatable { + private const int BitsPerDigit = 32; + private const ulong Base = 0x100000000; + + // -1 if negative, +1 if positive, 0 if zero. + private readonly short sign; + + // Non-null. data[0] is the least significant 32 bits. + private readonly uint[] data; + + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly BigInteger Zero = new BigInteger(0, new uint[0]); + [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly BigInteger One = new BigInteger(+1, new uint[] { 1 }); + private const int bias = 1075; + + [CLSCompliant(false)] + public static BigInteger Create(ulong v) { + return new BigInteger(+1, (uint)v, (uint)(v >> BitsPerDigit)); + } + + [CLSCompliant(false)] + public static BigInteger Create(uint v) { + if (v == 0) return Zero; + else if (v == 1) return One; + else return new BigInteger(+1, v); + } + + public static BigInteger Create(long v) { + ulong x; + int s = +1; + if (v < 0) { + x = (ulong)-v; s = -1; + } else { + x = (ulong)v; + } + + return new BigInteger(s, (uint)x, (uint)(x >> BitsPerDigit)); + } + + public static BigInteger Create(int v) { + if (v == 0) return Zero; + else if (v == 1) return One; + else if (v < 0) return new BigInteger(-1, (uint)-v); + else return new BigInteger(+1, (uint)v); + } + + private const Int32 DecimalScaleFactorMask = 0x00FF0000; + private const Int32 DecimalSignMask = unchecked((Int32)0x80000000); + + public static BigInteger Create(decimal v) { + // First truncate to get scale to 0 and extract bits + int[] bits = Decimal.GetBits(Decimal.Truncate(v)); + + Debug.Assert(bits.Length == 4 && (bits[3] & DecimalScaleFactorMask) == 0); + + int size = 3; + while (size > 0 && bits[size - 1] == 0) size--; + + if (size == 0) { + return BigInteger.Zero; + } + + UInt32[] array = new UInt32[size]; + array[0] = (UInt32)bits[0]; + if (size > 1) array[1] = (UInt32)bits[1]; + if (size > 2) array[2] = (UInt32)bits[2]; + + return new BigInteger(((bits[3] & DecimalSignMask) != 0) ? -1 : +1, array); + } + + /// + /// Create a BigInteger from a little-endian twos-complement byte array + /// (inverse of ToByteArray()) + /// + public static BigInteger Create(byte[] v) { + ContractUtils.RequiresNotNull(v, "v"); + if (v.Length == 0) return Create(0); + + int byteCount = v.Length; + int unalignedBytes = byteCount % 4; + int dwordCount = byteCount / 4 + (unalignedBytes == 0 ? 0 : 1); + uint[] data = new uint[dwordCount]; + + bool isNegative = (v[byteCount - 1] & 0x80) == 0x80; + + bool isZero = true; + + // Copy all dwords, except but don't do the last one if it's not a full four bytes + int curDword, curByte, byteInDword; + curByte = 3; + for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) { + byteInDword = 0; + while (byteInDword < 4) { + if (v[curByte] != 0x00) isZero = false; + data[curDword] <<= 8; + data[curDword] |= v[curByte]; + curByte--; + byteInDword++; + } + curByte += 8; + } + + // Copy the last dword specially if it's not aligned + if (unalignedBytes != 0) { + if (isNegative) data[dwordCount - 1] = 0xffffffff; + for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--) { + if (v[curByte] != 0x00) isZero = false; + data[curDword] <<= 8; + data[curDword] |= v[curByte]; + } + } + + if (isZero) return Zero; + + if (isNegative) { + makeTwosComplement(data); + return new BigInteger(-1, data); + } + return new BigInteger(1, data); + } + + + private static bool Negative(byte[] v) { + return ((v[7] & 0x80) != 0); + } + + private static ushort Exponent(byte[] v) { + return (ushort)((((ushort)(v[7] & 0x7F)) << (ushort)4) | (((ushort)(v[6] & 0xF0)) >> 4)); + } + + private static ulong Mantissa(byte[] v) { + uint i1 = ((uint)v[0] | ((uint)v[1] << 8) | ((uint)v[2] << 16) | ((uint)v[3] << 24)); + uint i2 = ((uint)v[4] | ((uint)v[5] << 8) | ((uint)(v[6] & 0xF) << 16)); + + return (ulong)((ulong)i1 | ((ulong)i2 << 32)); + } + + public static BigInteger Create(double v) { + if (Double.IsNaN(v) || Double.IsInfinity(v)) { + throw new OverflowException(); + } + + byte[] bytes = System.BitConverter.GetBytes(v); + ulong mantissa = Mantissa(bytes); + if (mantissa == 0) { + // 1.0 * 2**exp, we have a power of 2 + int exponent = Exponent(bytes); + if (exponent == 0) return Zero; + + BigInteger res = Negative(bytes) ? Negate(One) : One; + res = res << (exponent - 0x3ff); + return res; + } else { + // 1.mantissa * 2**exp + int exponent = Exponent(bytes); + mantissa |= 0x10000000000000ul; + BigInteger res = BigInteger.Create(mantissa); + res = exponent > bias ? res << (exponent - bias) : res >> (bias - exponent); + return Negative(bytes) ? res * (-1) : res; + } + } + + public static implicit operator BigInteger(byte i) { + return Create((uint)i); + } + + [CLSCompliant(false)] + public static implicit operator BigInteger(sbyte i) { + return Create((int)i); + } + + public static implicit operator BigInteger(short i) { + return Create((int)i); + } + + [CLSCompliant(false)] + public static implicit operator BigInteger(ushort i) { + return Create((uint)i); + } + + [CLSCompliant(false)] + public static implicit operator BigInteger(uint i) { + return Create(i); + } + + public static implicit operator BigInteger(int i) { + return Create(i); + } + + [CLSCompliant(false)] + public static implicit operator BigInteger(ulong i) { + return Create(i); + } + + public static implicit operator BigInteger(long i) { + return Create(i); + } + + public static implicit operator BigInteger(decimal i) { + return Create(i); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix + public static implicit operator double(BigInteger i) { + if (object.ReferenceEquals(i, null)) { + throw new ArgumentNullException("i"); + } + return i.ToFloat64(); + } + + public static explicit operator byte(BigInteger self) { + int tmp; + if (self.AsInt32(out tmp)) { + return checked((byte)tmp); + } + throw new OverflowException(); + } + + [CLSCompliant(false)] + public static explicit operator sbyte(BigInteger self) { + int tmp; + if (self.AsInt32(out tmp)) { + return checked((sbyte)tmp); + } + throw new OverflowException(); + } + + [CLSCompliant(false)] + public static explicit operator UInt16(BigInteger self) { + int tmp; + if (self.AsInt32(out tmp)) { + return checked((UInt16)tmp); + } + throw new OverflowException(); + } + + public static explicit operator Int16(BigInteger self) { + int tmp; + if (self.AsInt32(out tmp)) { + return checked((Int16)tmp); + } + throw new OverflowException(); + } + + [CLSCompliant(false)] + public static explicit operator UInt32(BigInteger self) { + uint tmp; + if (self.AsUInt32(out tmp)) { + return tmp; + } + throw new OverflowException(); + } + + public static explicit operator Int32(BigInteger self) { + int tmp; + if (self.AsInt32(out tmp)) { + return tmp; + } + throw new OverflowException(); + } + + public static explicit operator Int64(BigInteger self) { + long tmp; + if (self.AsInt64(out tmp)) { + return tmp; + } + throw new OverflowException(); + } + + [CLSCompliant(false)] + public static explicit operator UInt64(BigInteger self) { + ulong tmp; + if (self.AsUInt64(out tmp)) { + return tmp; + } + throw new OverflowException(); + } + + public static explicit operator float(BigInteger self) { + if (object.ReferenceEquals(self, null)) { + throw new ArgumentNullException("self"); + } + return checked((float)self.ToFloat64()); + } + + public static explicit operator decimal(BigInteger self) { + decimal res; + if (self.AsDecimal(out res)) { + return res; + } + throw new OverflowException(); + } + + public static explicit operator BigInteger(double self) { + return Create(self); + } + + public BigInteger(BigInteger copy) { + if (object.ReferenceEquals(copy, null)) { + throw new ArgumentNullException("copy"); + } + this.sign = copy.sign; + this.data = copy.data; + } + + [CLSCompliant(false)] + public BigInteger(int sign, params uint[] data) { + ContractUtils.RequiresNotNull(data, "data"); + if (sign != -1 && sign != 0 && sign != 1) throw new ArgumentException(MathResources.InvalidArgument, "sign"); + if (GetLength(data) != 0) { + if (sign == 0) throw new ArgumentException(MathResources.InvalidArgument, "sign"); + } else sign = 0; + + this.data = data; + this.sign = (short)sign; + } + + /// + /// Return the magnitude of this BigInteger as an array of zero or more uints. + /// Element zero is the value of the least significant four bytes, element one is + /// the value of the four next most significant bytes, etc. + /// + /// The returned data is the unsigned magnitude of the number. To determine the sign, + /// use GetSign(). + /// + /// It is guaranteed that the highest element of the returned array is never zero. + /// This means that if the value of this BigInteger is zero, a zero-length array + /// is returned. + /// + [CLSCompliant(false)] + public uint[] GetBits() { + if (sign == 0) return new uint[0]; + int mostSignificantNonZeroWord; + for ( + mostSignificantNonZeroWord = data.Length - 1; + mostSignificantNonZeroWord >= 0 && data[mostSignificantNonZeroWord] == 0; + mostSignificantNonZeroWord-- + ) ; + uint[] bits = new uint[mostSignificantNonZeroWord + 1]; + Array.Copy(data, bits, mostSignificantNonZeroWord + 1); + return bits; + } + + /// + /// Return the sign of this BigInteger: -1, 0, or 1. + /// + public short Sign { + get { + return sign; + } + } + + public bool AsInt64(out long ret) { + ret = 0; + if (sign == 0) return true; + if (Length > 2) return false; + if (data.Length == 1) { + ret = sign * (long)data[0]; + return true; + } + ulong tmp = (((ulong)data[1]) << 32 | (ulong)data[0]); + if (tmp > 0x8000000000000000) return false; + if (tmp == 0x8000000000000000 && sign == 1) return false; + ret = ((long)tmp) * sign; + return true; + } + + [CLSCompliant(false)] + public bool AsUInt32(out uint ret) { + ret = 0; + if (sign == 0) return true; + if (sign < 0) return false; + if (Length > 1) return false; + ret = data[0]; + return true; + } + + [CLSCompliant(false)] + public bool AsUInt64(out ulong ret) { + ret = 0; + if (sign == 0) return true; + if (sign < 0) return false; + if (Length > 2) return false; + ret = (ulong)data[0]; + if (data.Length > 1) { + ret |= ((ulong)data[1]) << 32; + } + return true; + } + + public bool AsInt32(out int ret) { + ret = 0; + if (sign == 0) return true; + if (Length > 1) return false; + if (data[0] > 0x80000000) return false; + if (data[0] == 0x80000000 && sign == 1) return false; + ret = (int)data[0]; + ret *= sign; + return true; + } + + public bool AsDecimal(out Decimal ret) { + if (sign == 0) { + ret = Decimal.Zero; + return true; + } + + int length = Length; + if (length > 3) { + ret = default(Decimal); + return false; + } + + int lo = 0, mi = 0, hi = 0; + if (length > 2) hi = (Int32)data[2]; + if (length > 1) mi = (Int32)data[1]; + if (length > 0) lo = (Int32)data[0]; + + ret = new Decimal(lo, mi, hi, sign < 0, 0); + return true; + } + + + [CLSCompliant(false)] + public uint ToUInt32() { + uint ret; + if (AsUInt32(out ret)) return ret; + throw new OverflowException(MathResources.BigIntWontFitUInt); + } + + public int ToInt32() { + int ret; + if (AsInt32(out ret)) return ret; + throw new OverflowException(MathResources.BigIntWontFitInt); + } + + public decimal ToDecimal() { + decimal ret; + if (AsDecimal(out ret)) return ret; + throw new OverflowException(MathResources.BigIntWontFitDecimal); + } + + [CLSCompliant(false)] + public ulong ToUInt64() { + ulong ret; + if (AsUInt64(out ret)) return ret; + throw new OverflowException(MathResources.BigIntWontFitULong); + } + + public long ToInt64() { + long ret; + if (AsInt64(out ret)) return ret; + throw new OverflowException(MathResources.BigIntWontFitLong); + } + + public bool TryToFloat64(out double result) { + return StringUtils.TryParseDouble(ToString(10), + System.Globalization.NumberStyles.Number, + System.Globalization.CultureInfo.InvariantCulture.NumberFormat, + out result); + } + + public double ToFloat64() { + return double.Parse( + ToString(10), + System.Globalization.CultureInfo.InvariantCulture.NumberFormat + ); + } + + public int Length { + get { + return GetLength(data); + } + } + + private static int GetLength(uint[] data) { + int ret = data.Length - 1; + while (ret >= 0 && data[ret] == 0) ret--; + return ret + 1; + } + + + private static uint[] copy(uint[] v) { + uint[] ret = new uint[v.Length]; + Array.Copy(v, ret, v.Length); + return ret; + } + + private static uint[] resize(uint[] v, int len) { + if (v.Length == len) return v; + uint[] ret = new uint[len]; + int n = System.Math.Min(v.Length, len); + for (int i = 0; i < n; i++) { + ret[i] = v[i]; + } + return ret; + } + + private static uint[] InternalAdd(uint[] x, int xl, uint[] y, int yl) { + Debug.Assert(xl >= yl); + uint[] z = new uint[xl]; + + int i; + ulong sum = 0; + for (i = 0; i < yl; i++) { + sum = sum + x[i] + y[i]; + z[i] = (uint)sum; + sum >>= BitsPerDigit; + } + + for (; i < xl && sum != 0; i++) { + sum = sum + x[i]; + z[i] = (uint)sum; + sum >>= BitsPerDigit; + } + if (sum != 0) { + z = resize(z, xl + 1); + z[i] = (uint)sum; + } else { + for (; i < xl; i++) { + z[i] = x[i]; + } + } + return z; + } + + private static uint[] sub(uint[] x, int xl, uint[] y, int yl) { + Debug.Assert(xl >= yl); + uint[] z = new uint[xl]; + + int i; + bool borrow = false; + for (i = 0; i < yl; i++) { + uint xi = x[i]; + uint yi = y[i]; + if (borrow) { + if (xi == 0) { + xi = 0xffffffff; + borrow = true; + } else { + xi -= 1; + borrow = false; + } + } + if (yi > xi) borrow = true; + z[i] = xi - yi; + } + + if (borrow) { + for (; i < xl; i++) { + uint xi = x[i]; + z[i] = xi - 1; + if (xi != 0) { i++; break; } + } + } + for (; i < xl; i++) { + z[i] = x[i]; + } + return z; // may have leading zeros + } + + private static uint[] add0(uint[] x, int xl, uint[] y, int yl) { + if (xl >= yl) return InternalAdd(x, xl, y, yl); + else return InternalAdd(y, yl, x, xl); + } + + public static int Compare(BigInteger x, BigInteger y) { + if (object.ReferenceEquals(x, null)) { + throw new ArgumentNullException("x"); + } + if (object.ReferenceEquals(y, null)) { + throw new ArgumentNullException("y"); + } + if (x.sign == y.sign) { + int xl = x.Length; + int yl = y.Length; + if (xl == yl) { + for (int i = xl - 1; i >= 0; i--) { + if (x.data[i] == y.data[i]) continue; + return x.data[i] > y.data[i] ? x.sign : -x.sign; + } + return 0; + } else { + return xl > yl ? +x.sign : -x.sign; + } + } else { + return x.sign > y.sign ? +1 : -1; + } + } + + public static bool operator ==(BigInteger x, int y) { + return x == (BigInteger)y; + } + + public static bool operator !=(BigInteger x, int y) { + return !(x == y); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix + public static bool operator ==(BigInteger x, double y) { + if (object.ReferenceEquals(x, null)) { + throw new ArgumentNullException("x"); + } + + // we can hold all double values, but not all double values + // can hold BigInteger values, and we may lose precision. Convert + // the double to a big int, then compare. + + if ((y % 1) != 0) return false; // not a whole number, can't be equal + + return x == BigInteger.Create(y); + } + + public static bool operator ==(double x, BigInteger y) { + return y == x; + } + + public static bool operator !=(BigInteger x, double y) { + return !(x == y); + } + + public static bool operator !=(double x, BigInteger y) { + return !(x == y); + } + + + public static bool operator ==(BigInteger x, BigInteger y) { + return Compare(x, y) == 0; + } + + public static bool operator !=(BigInteger x, BigInteger y) { + return Compare(x, y) != 0; + } + public static bool operator <(BigInteger x, BigInteger y) { + return Compare(x, y) < 0; + } + public static bool operator <=(BigInteger x, BigInteger y) { + return Compare(x, y) <= 0; + } + public static bool operator >(BigInteger x, BigInteger y) { + return Compare(x, y) > 0; + } + public static bool operator >=(BigInteger x, BigInteger y) { + return Compare(x, y) >= 0; + } + + public static BigInteger Add(BigInteger x, BigInteger y) { + return x + y; + } + + public static BigInteger operator +(BigInteger x, BigInteger y) { + if (object.ReferenceEquals(x, null)) { + throw new ArgumentNullException("x"); + } + if (object.ReferenceEquals(y, null)) { + throw new ArgumentNullException("y"); + } + + if (x.sign == y.sign) { + return new BigInteger(x.sign, add0(x.data, x.Length, y.data, y.Length)); + } else { + return x - new BigInteger(-y.sign, y.data); //??? performance issue + } + } + + public static BigInteger Subtract(BigInteger x, BigInteger y) { + return x - y; + } + + public static BigInteger operator -(BigInteger x, BigInteger y) { + int c = Compare(x, y); + if (c == 0) return Zero; + + if (x.sign == y.sign) { + uint[] z; + switch (c * x.sign) { + case +1: + z = sub(x.data, x.Length, y.data, y.Length); + break; + case -1: + z = sub(y.data, y.Length, x.data, x.Length); + break; + default: + return Zero; + } + return new BigInteger(c, z); + } else { + uint[] z = add0(x.data, x.Length, y.data, y.Length); + return new BigInteger(c, z); + } + } + + public static BigInteger Multiply(BigInteger x, BigInteger y) { + return x * y; + } + + public static BigInteger operator *(BigInteger x, BigInteger y) { + if (object.ReferenceEquals(x, null)) { + throw new ArgumentNullException("x"); + } + if (object.ReferenceEquals(y, null)) { + throw new ArgumentNullException("y"); + } + int xl = x.Length; + int yl = y.Length; + int zl = xl + yl; + uint[] xd = x.data, yd = y.data, zd = new uint[zl]; + + for (int xi = 0; xi < xl; xi++) { + uint xv = xd[xi]; + int zi = xi; + ulong carry = 0; + for (int yi = 0; yi < yl; yi++) { + carry = carry + ((ulong)xv) * yd[yi] + zd[zi]; + zd[zi++] = (uint)carry; + carry >>= BitsPerDigit; + } + while (carry != 0) { + carry += zd[zi]; + zd[zi++] = (uint)carry; + carry >>= BitsPerDigit; + } + } + + return new BigInteger(x.sign * y.sign, zd); + } + + public static BigInteger Divide(BigInteger x, BigInteger y) { + return x / y; + } + + public static BigInteger operator /(BigInteger x, BigInteger y) { + BigInteger dummy; + return DivRem(x, y, out dummy); + } + + public static BigInteger Mod(BigInteger x, BigInteger y) { + return x % y; + } + + public static BigInteger operator %(BigInteger x, BigInteger y) { + BigInteger ret; + DivRem(x, y, out ret); + return ret; + } + + private static int GetNormalizeShift(uint value) { + int shift = 0; + + if ((value & 0xFFFF0000) == 0) { value <<= 16; shift += 16; } + if ((value & 0xFF000000) == 0) { value <<= 8; shift += 8; } + if ((value & 0xF0000000) == 0) { value <<= 4; shift += 4; } + if ((value & 0xC0000000) == 0) { value <<= 2; shift += 2; } + if ((value & 0x80000000) == 0) { value <<= 1; shift += 1; } + + return shift; + } + + [Conditional("DEBUG")] + [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.Scripting.Math.BigInteger")] + private static void TestNormalize(uint[] u, uint[] un, int shift) { + BigInteger i = new BigInteger(1, u); + BigInteger j = new BigInteger(1, un); + BigInteger k = j >> shift; + + Debug.Assert(i == k); + } + + [Conditional("DEBUG")] + private static void TestDivisionStep(uint[] un, uint[] vn, uint[] q, uint[] u, uint[] v) { + int n = GetLength(v); + int shift = GetNormalizeShift(v[n - 1]); + + BigInteger uni = new BigInteger(1, un); + BigInteger vni = new BigInteger(1, vn); + BigInteger qi = new BigInteger(1, q); + BigInteger ui = new BigInteger(1, u); + + BigInteger expected = vni * qi + uni; + BigInteger usi = ui << shift; + + Debug.Assert(expected == usi); + } + + [Conditional("DEBUG")] + [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.Scripting.Math.BigInteger")] + private static void TestResult(uint[] u, uint[] v, uint[] q, uint[] r) { + BigInteger ui = new BigInteger(1, u); + BigInteger vi = new BigInteger(1, v); + BigInteger qi = new BigInteger(1, q); + BigInteger ri = new BigInteger(1, r); + + BigInteger viqi = vi * qi; + BigInteger expected = viqi + ri; + Debug.Assert(ui == expected); + Debug.Assert(ri < vi); + } + + private static void Normalize(uint[] u, int l, uint[] un, int shift) { + Debug.Assert(un.Length == l || un.Length == l + 1); + Debug.Assert(un.Length == l + 1 || ((u[l - 1] << shift) >> shift) == u[l - 1]); + Debug.Assert(0 <= shift && shift < 32); + + uint carry = 0; + int i; + if (shift > 0) { + int rshift = BitsPerDigit - shift; + for (i = 0; i < l; i++) { + uint ui = u[i]; + un[i] = (ui << shift) | carry; + carry = ui >> rshift; + } + } else { + for (i = 0; i < l; i++) { + un[i] = u[i]; + } + } + + while (i < un.Length) { + un[i++] = 0; + } + + if (carry != 0) { + Debug.Assert(l == un.Length - 1); + un[l] = carry; + } + + TestNormalize(u, un, shift); + } + + private static void Unnormalize(uint[] un, out uint[] r, int shift) { + Debug.Assert(0 <= shift && shift < 32); + + int length = GetLength(un); + r = new uint[length]; + + if (shift > 0) { + int lshift = 32 - shift; + uint carry = 0; + for (int i = length - 1; i >= 0; i--) { + uint uni = un[i]; + r[i] = (uni >> shift) | carry; + carry = (uni << lshift); + } + } else { + for (int i = 0; i < length; i++) { + r[i] = un[i]; + } + } + + TestNormalize(r, un, shift); + } + + private static void DivModUnsigned(uint[] u, uint[] v, out uint[] q, out uint[] r) { + int m = GetLength(u); + int n = GetLength(v); + + if (n <= 1) { + if (n == 0) { + throw new DivideByZeroException(); + } + + // Divide by single digit + // + ulong rem = 0; + uint v0 = v[0]; + q = new uint[m]; + r = new uint[1]; + + for (int j = m - 1; j >= 0; j--) { + rem *= Base; + rem += u[j]; + + ulong div = rem / v0; + rem -= div * v0; + q[j] = (uint)div; + } + r[0] = (uint)rem; + } else if (m >= n) { + int shift = GetNormalizeShift(v[n - 1]); + + uint[] un = new uint[m + 1]; + uint[] vn = new uint[n]; + + Normalize(u, m, un, shift); + Normalize(v, n, vn, shift); + + q = new uint[m - n + 1]; + r = null; + + TestDivisionStep(un, vn, q, u, v); + + // Main division loop + // + for (int j = m - n; j >= 0; j--) { + ulong rr, qq; + int i; + + rr = Base * un[j + n] + un[j + n - 1]; + qq = rr / vn[n - 1]; + rr -= qq * vn[n - 1]; + + Debug.Assert((Base * un[j + n] + un[j + n - 1]) == qq * vn[n - 1] + rr); + + for (; ; ) { + // Estimate too big ? + // + if ((qq >= Base) || (qq * vn[n - 2] > (rr * Base + un[j + n - 2]))) { + qq--; + rr += (ulong)vn[n - 1]; + if (rr < Base) continue; + } + break; + } + + Debug.Assert((Base * un[j + n] + un[j + n - 1]) == qq * vn[n - 1] + rr); + + // Multiply and subtract + // + long b = 0; + long t = 0; + for (i = 0; i < n; i++) { + ulong p = vn[i] * qq; + t = (long)un[i + j] - (long)(uint)p - b; + un[i + j] = (uint)t; + p >>= 32; + t >>= 32; + Debug.Assert(t == 0 || t == -1 || t == -2); + b = (long)p - t; + } + t = (long)un[j + n] - b; + un[j + n] = (uint)t; + + // Store the calculated value + // + q[j] = (uint)qq; + + // Add back vn[0..n] to un[j..j+n] + // + if (t < 0) { + q[j]--; + ulong c = 0; + for (i = 0; i < n; i++) { + c = (ulong)vn[i] + un[j + i] + c; + un[j + i] = (uint)c; + c >>= 32; + } + c += (ulong)un[j + n]; + un[j + n] = (uint)c; + } + + TestDivisionStep(un, vn, q, u, v); + } + + Unnormalize(un, out r, shift); + + // Test normalized value ... Call TestNormalize + // only pass the values in different order. + // + TestNormalize(r, un, shift); + } else { + q = new uint[] { 0 }; + r = u; + } + + TestResult(u, v, q, r); + } + + public static BigInteger DivRem(BigInteger x, BigInteger y, out BigInteger remainder) { + if (object.ReferenceEquals(x, null)) { + throw new ArgumentNullException("x"); + } + if (object.ReferenceEquals(y, null)) { + throw new ArgumentNullException("y"); + } + + uint[] q; + uint[] r; + + DivModUnsigned(x.data, y.data, out q, out r); + + remainder = new BigInteger(x.sign, r); + return new BigInteger(x.sign * y.sign, q); + } + + private static uint div(uint[] n, ref int nl, uint d) { + ulong rem = 0; + int i = nl; + bool seenNonZero = false; + while (--i >= 0) { + rem <<= BitsPerDigit; + rem |= n[i]; + uint v = (uint)(rem / d); + n[i] = v; + if (v == 0) { + if (!seenNonZero) nl--; + } else { + seenNonZero = true; + } + rem %= d; + } + return (uint)rem; + } + + private static uint extend(uint v, ref bool seenNonZero) { + if (seenNonZero) { + return ~v; + } else { + if (v == 0) { + return 0; + } else { + seenNonZero = true; + return ~v + 1; + } + } + } + + private static uint getOne(bool isNeg, uint[] data, int i, ref bool seenNonZero) { + if (i < data.Length) { + uint ret = data[i]; + return isNeg ? extend(ret, ref seenNonZero) : ret; + } else { + return isNeg ? uint.MaxValue : 0; + } + } + + /// + /// Do an in-place twos complement of d and also return the result. + /// + private static uint[] makeTwosComplement(uint[] d) { + // first do complement and +1 as long as carry is needed + int i = 0; + uint v = 0; + for (; i < d.Length; i++) { + v = ~d[i] + 1; + d[i] = v; + if (v != 0) { i++; break; } + } + + if (v != 0) { + // now ones complement is sufficient + for (; i < d.Length; i++) { + d[i] = ~d[i]; + } + } else { + //??? this is weird + d = resize(d, d.Length + 1); + d[d.Length - 1] = 1; + } + return d; + } + + public static BigInteger BitwiseAnd(BigInteger x, BigInteger y) { + return x & y; + } + + public static BigInteger operator &(BigInteger x, BigInteger y) { + if (object.ReferenceEquals(x, null)) { + throw new ArgumentNullException("x"); + } + if (object.ReferenceEquals(y, null)) { + throw new ArgumentNullException("y"); + } + int xl = x.Length, yl = y.Length; + uint[] xd = x.data, yd = y.data; + + int zl = System.Math.Max(xl, yl); + uint[] zd = new uint[zl]; + + bool negx = x.sign == -1, negy = y.sign == -1; + bool seenNonZeroX = false, seenNonZeroY = false; + for (int i = 0; i < zl; i++) { + uint xu = getOne(negx, xd, i, ref seenNonZeroX); + uint yu = getOne(negy, yd, i, ref seenNonZeroY); + zd[i] = xu & yu; + } + + if (negx && negy) { + + return new BigInteger(-1, makeTwosComplement(zd)); + } else if (negx || negy) { + return new BigInteger(+1, zd); + } else { + return new BigInteger(+1, zd); + } + } + + public static BigInteger BitwiseOr(BigInteger x, BigInteger y) { + return x | y; + } + + public static BigInteger operator |(BigInteger x, BigInteger y) { + if (object.ReferenceEquals(x, null)) { + throw new ArgumentNullException("x"); + } + if (object.ReferenceEquals(y, null)) { + throw new ArgumentNullException("y"); + } + int xl = x.Length, yl = y.Length; + uint[] xd = x.data, yd = y.data; + + int zl = System.Math.Max(xl, yl); + uint[] zd = new uint[zl]; + + bool negx = x.sign == -1, negy = y.sign == -1; + bool seenNonZeroX = false, seenNonZeroY = false; + for (int i = 0; i < zl; i++) { + uint xu = getOne(negx, xd, i, ref seenNonZeroX); + uint yu = getOne(negy, yd, i, ref seenNonZeroY); + zd[i] = xu | yu; + } + + if (negx && negy) { + return new BigInteger(-1, makeTwosComplement(zd)); + } else if (negx || negy) { + return new BigInteger(-1, makeTwosComplement(zd)); + } else { + return new BigInteger(+1, zd); + } + } + + public static BigInteger Xor(BigInteger x, BigInteger y) { + return x ^ y; + } + + public static BigInteger operator ^(BigInteger x, BigInteger y) { + if (object.ReferenceEquals(x, null)) { + throw new ArgumentNullException("x"); + } + if (object.ReferenceEquals(y, null)) { + throw new ArgumentNullException("y"); + } + int xl = x.Length, yl = y.Length; + uint[] xd = x.data, yd = y.data; + + int zl = System.Math.Max(xl, yl); + uint[] zd = new uint[zl]; + + bool negx = x.sign == -1, negy = y.sign == -1; + bool seenNonZeroX = false, seenNonZeroY = false; + for (int i = 0; i < zl; i++) { + uint xu = getOne(negx, xd, i, ref seenNonZeroX); + uint yu = getOne(negy, yd, i, ref seenNonZeroY); + zd[i] = xu ^ yu; + } + + if (negx && negy) { + return new BigInteger(+1, zd); + } else if (negx || negy) { + return new BigInteger(-1, makeTwosComplement(zd)); + } else { + return new BigInteger(+1, zd); + } + } + + public static BigInteger LeftShift(BigInteger x, int shift) { + return x << shift; + } + + public static BigInteger operator <<(BigInteger x, int shift) { + if (object.ReferenceEquals(x, null)) { + throw new ArgumentNullException("x"); + } + if (shift == 0) return x; + else if (shift < 0) return x >> -shift; + + int digitShift = shift / BitsPerDigit; + int smallShift = shift - (digitShift * BitsPerDigit); + + int xl = x.Length; + uint[] xd = x.data; + int zl = xl + digitShift + 1; + uint[] zd = new uint[zl]; + + if (smallShift == 0) { + for (int i = 0; i < xl; i++) { + zd[i + digitShift] = xd[i]; + } + } else { + int carryShift = BitsPerDigit - smallShift; + uint carry = 0; + int i; + for (i = 0; i < xl; i++) { + uint rot = xd[i]; + zd[i + digitShift] = rot << smallShift | carry; + carry = rot >> carryShift; + } + zd[i + digitShift] = carry; + } + return new BigInteger(x.sign, zd); + } + + public static BigInteger RightShift(BigInteger x, int shift) { + return x >> shift; + } + + public static BigInteger operator >>(BigInteger x, int shift) { + if (object.ReferenceEquals(x, null)) { + throw new ArgumentNullException("x"); + } + if (shift == 0) return x; + else if (shift < 0) return x << -shift; + + int digitShift = shift / BitsPerDigit; + int smallShift = shift - (digitShift * BitsPerDigit); + + int xl = x.Length; + uint[] xd = x.data; + int zl = xl - digitShift; + if (zl < 0) zl = 0; + uint[] zd = new uint[zl]; + + if (smallShift == 0) { + for (int i = xl - 1; i >= digitShift; i--) { + zd[i - digitShift] = xd[i]; + } + } else { + int carryShift = BitsPerDigit - smallShift; + uint carry = 0; + for (int i = xl - 1; i >= digitShift; i--) { + uint rot = xd[i]; + zd[i - digitShift] = rot >> smallShift | carry; + carry = rot << carryShift; + } + } + return new BigInteger(x.sign, zd); + } + + public static BigInteger Negate(BigInteger x) { + return -x; + } + + public static BigInteger operator -(BigInteger x) { + if (object.ReferenceEquals(x, null)) { + throw new ArgumentNullException("x"); + } + return new BigInteger(-x.sign, x.data); + } + + public BigInteger OnesComplement() { + return ~this; + } + + public static BigInteger operator ~(BigInteger x) { + if (object.ReferenceEquals(x, null)) { + throw new ArgumentNullException("x"); + } + return -(x + One); + } + + public BigInteger Abs() { + if (this.sign == -1) return -this; + else return this; + } + + public BigInteger Power(int exp) { + if (exp == 0) return One; + if (exp < 0) { + throw new ArgumentOutOfRangeException(MathResources.NonNegativePower); + } + BigInteger factor = this; + BigInteger result = One; + while (exp != 0) { + if ((exp & 1) != 0) result = result * factor; + if (exp == 1) break; // avoid costly factor.square() + factor = factor.Square(); + exp >>= 1; + } + return result; + } + + public BigInteger ModPow(int power, BigInteger mod) { + if (object.ReferenceEquals(mod, null)) { + throw new ArgumentNullException("mod"); + } + + if (power < 0) { + throw new ArgumentOutOfRangeException(MathResources.NonNegativePower); + } + BigInteger factor = this; + BigInteger result = One % mod; // Handle special case of power=0, mod=1 + while (power != 0) { + if ((power & 1) != 0) { + result = result * factor; + result = result % mod; + } + if (power == 1) break; // avoid costly factor.Square() + factor = factor.Square(); + factor = factor % mod; + power >>= 1; + } + return result; + } + + public BigInteger ModPow(BigInteger power, BigInteger mod) { + if (object.ReferenceEquals(power, null)) { + throw new ArgumentNullException("power"); + } + if (object.ReferenceEquals(mod, null)) { + throw new ArgumentNullException("mod"); + } + + if (power < 0) { + throw new ArgumentOutOfRangeException(MathResources.NonNegativePower); + } + + BigInteger factor = this; + BigInteger result = One % mod; + while (power != Zero) { + if (power.IsOdd()) { + result = result * factor; + result = result % mod; + } + if (power == One) break; // avoid costly factor.Square() + factor = factor.Square(); + factor = factor % mod; + power >>= 1; + } + return result; + } + + public BigInteger Square() { + return this * this; + } + + [Confined] + public override string ToString() { + return ToString(10); + } + + // generated by scripts/radix_generator.py + private static readonly uint[] maxCharsPerDigit = { 0, 0, 31, 20, 15, 13, 12, 11, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }; + private static readonly uint[] groupRadixValues = { 0, 0, 2147483648, 3486784401, 1073741824, 1220703125, 2176782336, 1977326743, 1073741824, 3486784401, 1000000000, 2357947691, 429981696, 815730721, 1475789056, 2562890625, 268435456, 410338673, 612220032, 893871739, 1280000000, 1801088541, 2494357888, 3404825447, 191102976, 244140625, 308915776, 387420489, 481890304, 594823321, 729000000, 887503681, 1073741824, 1291467969, 1544804416, 1838265625, 2176782336 }; + + [CLSCompliant(false), Confined] + public string ToString(uint radix) { + if (radix < 2) { + throw ExceptionUtils.MakeArgumentOutOfRangeException("radix", radix, MathResources.RadixLessThan2); + } + if (radix > 36) { + throw ExceptionUtils.MakeArgumentOutOfRangeException("radix", radix, MathResources.RadixGreaterThan36); + } + + int len = Length; + if (len == 0) return "0"; + + List digitGroups = new List(); + + uint[] d = copy(data); + int dl = Length; + + uint groupRadix = groupRadixValues[radix]; + while (dl > 0) { + uint rem = div(d, ref dl, groupRadix); + digitGroups.Add(rem); + } + + StringBuilder ret = new StringBuilder(); + if (sign == -1) ret.Append("-"); + int digitIndex = digitGroups.Count - 1; + + char[] tmpDigits = new char[maxCharsPerDigit[radix]]; + + AppendRadix((uint)digitGroups[digitIndex--], radix, tmpDigits, ret, false); + while (digitIndex >= 0) { + AppendRadix((uint)digitGroups[digitIndex--], radix, tmpDigits, ret, true); + } + return ret.ToString(); + } + + private static void AppendRadix(uint rem, uint radix, char[] tmp, StringBuilder buf, bool leadingZeros) { + const string symbols = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + int digits = tmp.Length; + int i = digits; + while (i > 0 && (leadingZeros || rem != 0)) { + uint digit = rem % radix; + rem /= radix; + tmp[--i] = symbols[(int)digit]; + } + if (leadingZeros) buf.Append(tmp); + else buf.Append(tmp, i, digits - i); + } + + [Confined] + public override int GetHashCode() { + // The Object.GetHashCode function needs to be consistent with the Object.Equals function. + // Languages that build on top of this may have a more flexible equality function and + // so may not be able to use this hash function directly. + // For example, Python allows BigInteger(10) == int32(10), so hashing a BigInt over the Int32 + // domain should return the same value as a hash of the Int32. + + // If this is in the int32 range, this hash function returns the integer. + if (data.Length == 0) { + return 0; + } + + // Add up all uints. We want to incorporate all bits to get good hash distribution. + uint total = 0; + foreach (uint x in data) { + total = unchecked(total + x); + } + + int hash = unchecked((int)total); + + // The sign is not part of the data array, so explicitly incorporate that. + // This is also needed to ensure that hash(-x) == -x for int32. + if (IsNegative()) { + return unchecked(-hash); + } else { + return hash; + } + } + + [Confined] + public override bool Equals(object obj) { + return Equals(obj as BigInteger); + } + + [StateIndependent] + public bool Equals(BigInteger other) { + if (object.ReferenceEquals(other, null)) return false; + return this == other; + } + + + public bool IsNegative() { + return sign < 0; + } + + public bool IsZero() { + return sign == 0; + } + + public bool IsPositive() { + return sign > 0; + } + + private bool IsOdd() { + // must have the lowest-order bit set to 1 + return (data != null && data.Length > 0 && ((data[0] & 1) != 0)); + } + + + public double Log(Double newBase) { + if (IsNegative() || newBase == 1.0D || this == Zero || (newBase == 0.0D && this != One)) { + return Double.NaN; + } else if (newBase == Double.PositiveInfinity) { + return this == One ? 0.0D : Double.NaN; + } + + int length = GetLength(data) - 1; + int bitCount = -1; + for (int curBit = 31; curBit >= 0; curBit--) { + if ((data[length] & (1 << curBit)) != 0) { + bitCount = curBit + length * 32; + break; + } + } + + long bitlen = bitCount; + Double c = 0, d = 1; + + BigInteger testBit = BigInteger.One; + long tempBitlen = bitlen; + while (tempBitlen > Int32.MaxValue) { + testBit = testBit << Int32.MaxValue; + tempBitlen -= Int32.MaxValue; + } + testBit = testBit << (int)tempBitlen; + + for (long curbit = bitlen; curbit >= 0; --curbit) { + if ((this & testBit) != BigInteger.Zero) + c += d; + d *= 0.5; + testBit = testBit >> 1; + } + return (System.Math.Log(c) + System.Math.Log(2) * bitlen) / System.Math.Log(newBase); + } + + /// + /// Calculates the natural logarithm of the BigInteger. + /// + public double Log() { + return Log(System.Math.E); + } + + /// + /// Calculates log base 10 of a BigInteger. + /// + public double Log10() { + return Log(10); + } + + #region IComparable Members + + public int CompareTo(object obj) { + if (obj == null) { + return 1; + } + BigInteger o = obj as BigInteger; + if (object.ReferenceEquals(o, null)) { + throw new ArgumentException(MathResources.ExpectedInteger); + } + return Compare(this, o); + } + + #endregion + + #region IConvertible Members + + [Confined] + public TypeCode GetTypeCode() { + return TypeCode.Object; + } + + [Confined] + public bool ToBoolean(IFormatProvider provider) { + return this != Zero; + } + + [Confined] + public byte ToByte(IFormatProvider provider) { + uint ret; + if (AsUInt32(out ret) && (ret & ~0xFF) == 0) { + return (byte)ret; + } + throw new OverflowException(MathResources.BigIntWontFitByte); + } + + /// + /// Return the value of this BigInteger as a little-endian twos-complement + /// byte array, using the fewest number of bytes possible. If the value is zero, + /// return an array of one byte whose element is 0x00. + /// + public byte[] ToByteArray() { + // We could probably make this more efficient by eliminating one of the passes. + // The current code does one pass for uint array -> byte array conversion, + // and then a another pass to remove unneeded bytes at the top of the array. + if (0 == sign) return new byte[] { 0 }; + + uint[] dwords; + byte highByte; + + if (-1 == sign) { + dwords = (uint[])this.data.Clone(); + makeTwosComplement(dwords); + highByte = 0xff; + } else { + dwords = this.data; + highByte = 0x00; + } + + byte[] bytes = new byte[4 * dwords.Length]; + int curByte = 0; + uint dword; + for (int i = 0; i < dwords.Length; i++) { + dword = dwords[i]; + for (int j = 0; j < 4; j++) { + bytes[curByte++] = (byte)(dword & 0xff); + dword >>= 8; + } + } + + // find highest significant byte + int msb; + for (msb = bytes.Length - 1; msb > 0; msb--) { + if (bytes[msb] != highByte) break; + } + // ensure high bit is 0 if positive, 1 if negative + bool needExtraByte = (bytes[msb] & 0x80) != (highByte & 0x80); + + byte[] trimmedBytes = new byte[msb + 1 + (needExtraByte ? 1 : 0)]; + Array.Copy(bytes, trimmedBytes, msb + 1); + + if (needExtraByte) trimmedBytes[trimmedBytes.Length - 1] = highByte; + + return trimmedBytes; + } + + [Confined] + public char ToChar(IFormatProvider provider) { + int ret; + if (AsInt32(out ret) && (ret <= Char.MaxValue) && (ret >= Char.MinValue)) { + return (char)ret; + } + throw new OverflowException(MathResources.BigIntWontFitChar); + } + + [Confined] + public DateTime ToDateTime(IFormatProvider provider) { + throw new NotImplementedException(); + } + + [Confined] + public decimal ToDecimal(IFormatProvider provider) { + decimal ret; + if (AsDecimal(out ret)) return ret; + throw new OverflowException(MathResources.BigIntWontFitDecimal); + } + + [Confined] + public double ToDouble(IFormatProvider provider) { + return ToFloat64(); + } + + [Confined] + public short ToInt16(IFormatProvider provider) { + int ret; + if (AsInt32(out ret) && (ret <= short.MaxValue) && (ret >= short.MinValue)) { + return (short)ret; + } + throw new OverflowException(MathResources.BigIntWontFitShort); + } + + [Confined] + public int ToInt32(IFormatProvider provider) { + int ret; + if (AsInt32(out ret)) { + return ret; + } + throw new OverflowException(MathResources.BigIntWontFitInt); + } + + [Confined] + public long ToInt64(IFormatProvider provider) { + long ret; + if (AsInt64(out ret)) { + return ret; + } + throw new OverflowException(MathResources.BigIntWontFitLong); + } + + [CLSCompliant(false), Confined] + public sbyte ToSByte(IFormatProvider provider) { + int ret; + if (AsInt32(out ret) && (ret <= sbyte.MaxValue) && (ret >= sbyte.MinValue)) { + return (sbyte)ret; + } + throw new OverflowException(MathResources.BigIntWontFitSByte); + } + + [Confined] + public float ToSingle(IFormatProvider provider) { + return checked((float)ToDouble(provider)); + } + + [Confined] + public string ToString(IFormatProvider provider) { + return ToString(); + } + + [Confined] + public object ToType(Type conversionType, IFormatProvider provider) { + if (conversionType == typeof(BigInteger)) { + return this; + } + throw new NotImplementedException(); + } + + [CLSCompliant(false), Confined] + public ushort ToUInt16(IFormatProvider provider) { + uint ret; + if (AsUInt32(out ret) && ret <= ushort.MaxValue) { + return (ushort)ret; + } + throw new OverflowException(MathResources.BigIntWontFitUShort); + } + + [CLSCompliant(false), Confined] + public uint ToUInt32(IFormatProvider provider) { + uint ret; + if (AsUInt32(out ret)) { + return ret; + } + throw new OverflowException(MathResources.BigIntWontFitUInt); + } + + [CLSCompliant(false), Confined] + public ulong ToUInt64(IFormatProvider provider) { + ulong ret; + if (AsUInt64(out ret)) { + return ret; + } + throw new OverflowException(MathResources.BigIntWontFitULong); + } + + #endregion + + #region IFormattable Members + + string IFormattable.ToString(string format, IFormatProvider formatProvider) { + if (format == null) return this.ToString(); + + switch (format[0]) { + case 'd': + case 'D': + if (format.Length > 1) { + int precision = Convert.ToInt32(format.Substring(1), CultureInfo.InvariantCulture.NumberFormat); + string baseStr = ToString(10); + if (baseStr.Length < precision) { + string additional = new String('0', precision - baseStr.Length); + if (baseStr[0] != '-') { + return additional + baseStr; + } else { + return "-" + additional + baseStr.Substring(1); + } + } + return baseStr; + } + return ToString(10); + case 'x': + case 'X': + StringBuilder res = new StringBuilder(ToString(16)); + if (format[0] == 'x') { + for (int i = 0; i < res.Length; i++) { + if (res[i] >= 'A' && res[i] <= 'F') { + res[i] = Char.ToLower(res[i], CultureInfo.InvariantCulture); + } + } + } + + if (format.Length > 1) { + int precision = Convert.ToInt32(format.Substring(1), CultureInfo.InvariantCulture.NumberFormat); + if (res.Length < precision) { + string additional = new String('0', precision - res.Length); + if (res[0] != '-') { + res.Insert(0, additional); + } else { + res.Insert(1, additional); + } + } + } + + return res.ToString(); + default: + throw new NotImplementedException(MathResources.FormatNotImplemented); + } + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Math/Complex64.cs b/merlin/main/runtime/Microsoft.Scripting/Math/Complex64.cs new file mode 100644 index 0000000000..f67f5a72ef --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Math/Complex64.cs @@ -0,0 +1,275 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Math { + /// + /// Implementation of the complex number data type. + /// + [Serializable] + public struct Complex64 { + private readonly double real, imag; + + public static Complex64 MakeImaginary(double imag) { + return new Complex64(0.0, imag); + } + + public static Complex64 MakeReal(double real) { + return new Complex64(real, 0.0); + } + + public static Complex64 Make(double real, double imag) { + return new Complex64(real, imag); + } + + public Complex64(double real) + : this(real, 0.0) { + } + + public Complex64(double real, double imag) { + this.real = real; + this.imag = imag; + } + + public bool IsZero { + get { + return real == 0.0 && imag == 0.0; + } + } + + public double Real { + get { + return real; + } + } + + public double Imag { + get { + return imag; + } + } + + public Complex64 Conjugate() { + return new Complex64(real, -imag); + } + + + public override string ToString() { + if (real == 0.0) return imag.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat) + "j"; + else if (imag < 0.0) return string.Format(System.Globalization.CultureInfo.InvariantCulture.NumberFormat, "({0}{1}j)", real, imag); + else return string.Format(System.Globalization.CultureInfo.InvariantCulture.NumberFormat, "({0}+{1}j)", real, imag); + } + + public static implicit operator Complex64(int i) { + return MakeReal(i); + } + + [CLSCompliant(false)] + public static implicit operator Complex64(uint i) { + return MakeReal(i); + } + + public static implicit operator Complex64(short i) { + return MakeReal(i); + } + + [CLSCompliant(false)] + public static implicit operator Complex64(ushort i) { + return MakeReal(i); + } + + public static implicit operator Complex64(long l) { + return MakeReal(l); + } + [CLSCompliant(false)] + public static implicit operator Complex64(ulong i) { + return MakeReal(i); + } + + [CLSCompliant(false)] + public static implicit operator Complex64(sbyte i) { + return MakeReal(i); + } + + public static implicit operator Complex64(byte i) { + return MakeReal(i); + } + + public static implicit operator Complex64(float f) { + return MakeReal(f); + } + + public static implicit operator Complex64(double d) { + return MakeReal(d); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix + public static implicit operator Complex64(BigInteger i) { + if (object.ReferenceEquals(i, null)) { + throw new ArgumentException(MathResources.InvalidArgument, "i"); + } + + // throws an overflow exception if we can't handle the value. + return MakeReal(i.ToFloat64()); + } + + public static bool operator ==(Complex64 x, Complex64 y) { + return x.real == y.real && x.imag == y.imag; + } + + public static bool operator !=(Complex64 x, Complex64 y) { + return x.real != y.real || x.imag != y.imag; + } + + public static Complex64 Add(Complex64 x, Complex64 y) { + return x + y; + } + + public static Complex64 operator +(Complex64 x, Complex64 y) { + return new Complex64(x.real + y.real, x.imag + y.imag); + } + + public static Complex64 Subtract(Complex64 x, Complex64 y) { + return x - y; + } + + public static Complex64 operator -(Complex64 x, Complex64 y) { + return new Complex64(x.real - y.real, x.imag - y.imag); + } + + public static Complex64 Multiply(Complex64 x, Complex64 y) { + return x * y; + } + + public static Complex64 operator *(Complex64 x, Complex64 y) { + return new Complex64(x.real * y.real - x.imag * y.imag, x.real * y.imag + x.imag * y.real); + } + + public static Complex64 Divide(Complex64 x, Complex64 y) { + return x / y; + } + + public static Complex64 operator /(Complex64 a, Complex64 b) { + if (b.IsZero) throw new DivideByZeroException(MathResources.ComplexDivizionByZero); + + double real, imag, den, r; + + if (System.Math.Abs(b.real) >= System.Math.Abs(b.imag)) { + r = b.imag / b.real; + den = b.real + r * b.imag; + real = (a.real + a.imag * r) / den; + imag = (a.imag - a.real * r) / den; + } else { + r = b.real / b.imag; + den = b.imag + r * b.real; + real = (a.real * r + a.imag) / den; + imag = (a.imag * r - a.real) / den; + } + + return new Complex64(real, imag); + } + + public static Complex64 Negate(Complex64 x) { + return -x; + } + + public static Complex64 operator -(Complex64 x) { + return new Complex64(-x.real, -x.imag); + } + + public static Complex64 Plus(Complex64 x) { + return +x; + } + + public static Complex64 operator +(Complex64 x) { + return x; + } + + public static double Hypot(double x, double y) { + // + // sqrt(x*x + y*y) == sqrt(x*x * (1 + (y*y)/(x*x))) == + // sqrt(x*x) * sqrt(1 + (y/x)*(y/x)) == + // abs(x) * sqrt(1 + (y/x)*(y/x)) + // + + // First, get abs + if (x < 0.0) x = -x; + if (y < 0.0) y = -y; + + // Obvious cases + if (x == 0.0) return y; + if (y == 0.0) return x; + + // Divide smaller number by bigger number to safeguard the (y/x)*(y/x) + if (x < y) { double temp = y; y = x; x = temp; } + + y /= x; + + // calculate abs(x) * sqrt(1 + (y/x)*(y/x)) + return x * System.Math.Sqrt(1 + y * y); + } + + public double Abs() { + return Hypot(real, imag); + } + + public Complex64 Power(Complex64 y) { + double c = y.real; + double d = y.imag; + int power = (int)c; + + if (power == c && power >= 0 && d == .0) { + Complex64 result = new Complex64(1.0); + if (power == 0) return result; + Complex64 factor = this; + while (power != 0) { + if ((power & 1) != 0) { + result = result * factor; + } + factor = factor * factor; + power >>= 1; + } + return result; + } else if (IsZero) { + return y.IsZero ? Complex64.MakeReal(1.0) : Complex64.MakeReal(0.0); + } else { + double a = real; + double b = imag; + double powers = a * a + b * b; + double arg = System.Math.Atan2(b, a); + double mul = System.Math.Pow(powers, c / 2) * System.Math.Exp(-d * arg); + double common = c * arg + .5 * d * System.Math.Log(powers); + return new Complex64(mul * System.Math.Cos(common), mul * System.Math.Sin(common)); + } + } + + public override int GetHashCode() { + // The Object.GetHashCode function needs to be consistent with the Object.Equals function. + // Languages that build on top of this may have a more flexible equality function and + // so may not be able to use this hash function directly. + // For example, Python allows that c=Complex64(1.5, 0), f = 1.5f, c==f. + // so then the hash(f) == hash(c). Since the python (and other languages) can define an arbitrary + // hash(float) function, the language may need to define a matching hash(complex) function for + // the cases where the float and complex numbers overlap. + return (int)real + (int)imag * 1000003; + } + + public override bool Equals(object obj) { + if (!(obj is Complex64)) return false; + return this == ((Complex64)obj); + } + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Math/MathResources.Designer.cs b/merlin/main/runtime/Microsoft.Scripting/Math/MathResources.Designer.cs new file mode 100644 index 0000000000..05b90ce871 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Math/MathResources.Designer.cs @@ -0,0 +1,216 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.1412 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Scripting.Math { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class MathResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal MathResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Scripting.Math.MathResources", typeof(MathResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to big integer won't fit into byte. + /// + internal static string BigIntWontFitByte { + get { + return ResourceManager.GetString("BigIntWontFitByte", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to big integer won't fit into char. + /// + internal static string BigIntWontFitChar { + get { + return ResourceManager.GetString("BigIntWontFitChar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to big integer won't fit into decimal. + /// + internal static string BigIntWontFitDecimal { + get { + return ResourceManager.GetString("BigIntWontFitDecimal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to big integer won't fit into int. + /// + internal static string BigIntWontFitInt { + get { + return ResourceManager.GetString("BigIntWontFitInt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to big integer won't fit into long. + /// + internal static string BigIntWontFitLong { + get { + return ResourceManager.GetString("BigIntWontFitLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to big integer won't fit into sbyte. + /// + internal static string BigIntWontFitSByte { + get { + return ResourceManager.GetString("BigIntWontFitSByte", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to big integer won't fit into short. + /// + internal static string BigIntWontFitShort { + get { + return ResourceManager.GetString("BigIntWontFitShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to big integer won't fit into uint. + /// + internal static string BigIntWontFitUInt { + get { + return ResourceManager.GetString("BigIntWontFitUInt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to big integer won't fit into ulong. + /// + internal static string BigIntWontFitULong { + get { + return ResourceManager.GetString("BigIntWontFitULong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to big integer won't fit into ushort. + /// + internal static string BigIntWontFitUShort { + get { + return ResourceManager.GetString("BigIntWontFitUShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to complex division by zero. + /// + internal static string ComplexDivizionByZero { + get { + return ResourceManager.GetString("ComplexDivizionByZero", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to expected integer. + /// + internal static string ExpectedInteger { + get { + return ResourceManager.GetString("ExpectedInteger", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to format not implemented. + /// + internal static string FormatNotImplemented { + get { + return ResourceManager.GetString("FormatNotImplemented", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to invalid argument. + /// + internal static string InvalidArgument { + get { + return ResourceManager.GetString("InvalidArgument", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to power must be >= 0. + /// + internal static string NonNegativePower { + get { + return ResourceManager.GetString("NonNegativePower", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to radix must be <= 36. + /// + internal static string RadixGreaterThan36 { + get { + return ResourceManager.GetString("RadixGreaterThan36", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to radix must be >= 2. + /// + internal static string RadixLessThan2 { + get { + return ResourceManager.GetString("RadixLessThan2", resourceCulture); + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Math/MathResources.resx b/merlin/main/runtime/Microsoft.Scripting/Math/MathResources.resx new file mode 100644 index 0000000000..53badb59a9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Math/MathResources.resx @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + big integer won't fit into byte + + + big integer won't fit into char + + + big integer won't fit into decimal + + + big integer won't fit into int + + + big integer won't fit into long + + + big integer won't fit into sbyte + + + big integer won't fit into short + + + big integer won't fit into uint + + + big integer won't fit into ulong + + + big integer won't fit into ushort + + + complex division by zero + + + expected integer + + + format not implemented + + + invalid argument + + + power must be >= 0 + + + radix must be <= 36 + + + radix must be >= 2 + + \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Microsoft.Scripting.csproj b/merlin/main/runtime/Microsoft.Scripting/Microsoft.Scripting.csproj new file mode 100644 index 0000000000..c1292695b1 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Microsoft.Scripting.csproj @@ -0,0 +1,529 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Library + Properties + Microsoft.Scripting + Microsoft.Scripting + SAK + SAK + SAK + SAK + ..\..\Bin\Release\Microsoft.Scripting.XML + 1685 + 1591 + 2.0 + + + pdbonly + true + ..\..\Bin\FxCop\ + TRACE;SIGNED + prompt + 4 + true + true + ..\..\MSSharedLibKey.snk + true + true + -Microsoft.Usage#CA2209;+!Microsoft.Design#CA1012;-!Microsoft.Design#CA2210;+!Microsoft.Design#CA1040;+!Microsoft.Design#CA1005;+!Microsoft.Design#CA1020;-!Microsoft.Design#CA1021;+!Microsoft.Design#CA1010;+!Microsoft.Design#CA1011;+!Microsoft.Design#CA1009;+!Microsoft.Design#CA1050;+!Microsoft.Design#CA1026;+!Microsoft.Design#CA1019;+!Microsoft.Design#CA1031;+!Microsoft.Design#CA1047;+!Microsoft.Design#CA1000;+!Microsoft.Design#CA1048;-!Microsoft.Design#CA1051;-!Microsoft.Design#CA1002;+!Microsoft.Design#CA1061;-!Microsoft.Design#CA1006;+!Microsoft.Design#CA1046;+!Microsoft.Design#CA1045;+!Microsoft.Design#CA1065;+!Microsoft.Design#CA1038;+!Microsoft.Design#CA1008;+!Microsoft.Design#CA1028;+!Microsoft.Design#CA1064;-!Microsoft.Design#CA1004;+!Microsoft.Design#CA1035;-!Microsoft.Design#CA1063;+!Microsoft.Design#CA1032;+!Microsoft.Design#CA1023;-!Microsoft.Design#CA1033;+!Microsoft.Design#CA1039;+!Microsoft.Design#CA1016;+!Microsoft.Design#CA1014;+!Microsoft.Design#CA1017;+!Microsoft.Design#CA1018;+!Microsoft.Design#CA1027;+!Microsoft.Design#CA1059;+!Microsoft.Design#CA1060;+!Microsoft.Design#CA1034;+!Microsoft.Design#CA1013;+!Microsoft.Design#CA1036;+!Microsoft.Design#CA1044;+!Microsoft.Design#CA1041;+!Microsoft.Design#CA1025;+!Microsoft.Design#CA1052;+!Microsoft.Design#CA1053;+!Microsoft.Design#CA1057;+!Microsoft.Design#CA1058;+!Microsoft.Design#CA1001;+!Microsoft.Design#CA1049;+!Microsoft.Design#CA1054;+!Microsoft.Design#CA1056;+!Microsoft.Design#CA1055;+!Microsoft.Design#CA1030;+!Microsoft.Design#CA1003;-!Microsoft.Design#CA1007;+!Microsoft.Design#CA1043;+!Microsoft.Design#CA1024;+!Microsoft.Globalization#CA1301;+!Microsoft.Globalization#CA1302;-!Microsoft.Globalization#CA1308;+!Microsoft.Globalization#CA1306;+!Microsoft.Globalization#CA1304;-!Microsoft.Globalization#CA1305;+!Microsoft.Globalization#CA2101;+!Microsoft.Globalization#CA1300;-!Microsoft.Globalization#CA1307;+!Microsoft.Globalization#CA1309;+!Microsoft.Interoperability#CA1403;+!Microsoft.Interoperability#CA1406;+!Microsoft.Interoperability#CA1413;+!Microsoft.Interoperability#CA1402;+!Microsoft.Interoperability#CA1407;+!Microsoft.Interoperability#CA1404;+!Microsoft.Interoperability#CA1410;+!Microsoft.Interoperability#CA1411;+!Microsoft.Interoperability#CA1405;+!Microsoft.Interoperability#CA1409;+!Microsoft.Interoperability#CA1415;+!Microsoft.Interoperability#CA1408;+!Microsoft.Interoperability#CA1414;+!Microsoft.Interoperability#CA1412;+!Microsoft.Interoperability#CA1400;+!Microsoft.Interoperability#CA1401;+!Microsoft.Maintainability#CA1506;+!Microsoft.Maintainability#CA1502;+!Microsoft.Maintainability#CA1501;+!Microsoft.Maintainability#CA1505;+!Microsoft.Maintainability#CA1504;+!Microsoft.Maintainability#CA1500;+!Microsoft.Mobility#CA1600;+!Microsoft.Mobility#CA1601;-!Microsoft.Naming#CA1702;+!Microsoft.Naming#CA1700;+!Microsoft.Naming#CA1712;+!Microsoft.Naming#CA1713;+!Microsoft.Naming#CA1714;+!Microsoft.Naming#CA1709;-!Microsoft.Naming#CA1704;+!Microsoft.Naming#CA1708;+!Microsoft.Naming#CA1715;-!Microsoft.Naming#CA1710;-!Microsoft.Naming#CA1720;+!Microsoft.Naming#CA1707;+!Microsoft.Naming#CA1722;-!Microsoft.Naming#CA1711;+!Microsoft.Naming#CA1716;+!Microsoft.Naming#CA1717;+!Microsoft.Naming#CA1725;+!Microsoft.Naming#CA1719;+!Microsoft.Naming#CA1721;-!Microsoft.Naming#CA1701;-!Microsoft.Naming#CA1703;+!Microsoft.Naming#CA1724;-!Microsoft.Naming#CA1726;+!Microsoft.Performance#CA1809;-!Microsoft.Performance#CA1811;+!Microsoft.Performance#CA1812;+!Microsoft.Performance#CA1813;+!Microsoft.Performance#CA1823;+!Microsoft.Performance#CA1800;-!Microsoft.Performance#CA1805;+!Microsoft.Performance#CA1810;+!Microsoft.Performance#CA1824;-!Microsoft.Performance#CA1822;+!Microsoft.Performance#CA1815;+!Microsoft.Performance#CA1814;+!Microsoft.Performance#CA1819;+!Microsoft.Performance#CA1821;+!Microsoft.Performance#CA1804;+!Microsoft.Performance#CA1820;+!Microsoft.Performance#CA1802;+!Microsoft.Portability#CA1901;+!Microsoft.Portability#CA1900;+!Microsoft.Reliability#CA2001;+!Microsoft.Reliability#CA2002;+!Microsoft.Reliability#CA2003;+!Microsoft.Reliability#CA2004;+!Microsoft.Reliability#CA2006;+!Microsoft.Security#CA2116;+!Microsoft.Security#CA2117;+!Microsoft.Security#CA2105;+!Microsoft.Security#CA2115;+!Microsoft.Security#CA2102;+!Microsoft.Security#CA2104;+!Microsoft.Security#CA2122;+!Microsoft.Security#CA2114;+!Microsoft.Security#CA2123;+!Microsoft.Security#CA2111;+!Microsoft.Security#CA2108;+!Microsoft.Security#CA2107;+!Microsoft.Security#CA2103;+!Microsoft.Security#CA2118;+!Microsoft.Security#CA2109;+!Microsoft.Security#CA2119;+!Microsoft.Security#CA2106;+!Microsoft.Security#CA2112;+!Microsoft.Security#CA2120;+!Microsoft.Security#CA2121;+!Microsoft.Security#CA2126;+!Microsoft.Security#CA2124;+!Microsoft.Security#CA2127;+!Microsoft.Security#CA2128;+!Microsoft.Security#CA2129;+!Microsoft.Usage#CA2243;+!Microsoft.Usage#CA2236;+!Microsoft.Usage#CA1816;+!Microsoft.Usage#CA2227;+!Microsoft.Usage#CA2213;+!Microsoft.Usage#CA2216;+!Microsoft.Usage#CA2214;+!Microsoft.Usage#CA2222;+!Microsoft.Usage#CA1806;+!Microsoft.Usage#CA2217;+!Microsoft.Usage#CA2212;+!Microsoft.Usage#CA2219;+!Microsoft.Usage#CA2201;+!Microsoft.Usage#CA2228;+!Microsoft.Usage#CA2221;+!Microsoft.Usage#CA2220;+!Microsoft.Usage#CA2240;+!Microsoft.Usage#CA2229;+!Microsoft.Usage#CA2238;+!Microsoft.Usage#CA2207;+!Microsoft.Usage#CA2208;+!Microsoft.Usage#CA2235;+!Microsoft.Usage#CA2237;+!Microsoft.Usage#CA2232;+!Microsoft.Usage#CA2223;+!Microsoft.Usage#CA2211;+!Microsoft.Usage#CA2233;+!Microsoft.Usage#CA2225;+!Microsoft.Usage#CA2226;+!Microsoft.Usage#CA2231;+!Microsoft.Usage#CA2224;+!Microsoft.Usage#CA2218;+!Microsoft.Usage#CA2234;+!Microsoft.Usage#CA2239;+!Microsoft.Usage#CA2200;+!Microsoft.Usage#CA1801;+!Microsoft.Usage#CA2242;+!Microsoft.Usage#CA2205;+!Microsoft.Usage#CA2230 + + + pdbonly + true + ..\..\Bin\Debug\ + TRACE;DEBUG;SIGNED + prompt + 4 + true + true + ..\..\MSSharedLibKey.snk + true + 1591 + true + + + true + full + false + ..\..\Bin\Debug\ + ..\..\Bin\Debug\Microsoft.Scripting.xml + DEBUG;TRACE;SIGNED + prompt + 4 + true + false + true + ..\..\MSSharedLibKey.snk + true + + + pdbonly + true + ..\..\Bin\Release\ + ..\..\Bin\Release\Microsoft.Scripting.xml + TRACE;SIGNED + prompt + 4 + true + false + true + ..\..\MSSharedLibKey.snk + true + 1929379840 + + + true + ..\..\Bin\Silverlight Debug\ + TRACE;DEBUG;SILVERLIGHT + true + full + AnyCPU + false + prompt + true + ..\..\Bin\Silverlight Debug\Microsoft.Scripting.xml + true + ..\..\SilverlightKey.snk + true + 1591,618 + true + ..\..\Utilities\Silverlight\x86ret\ + + + ..\..\Bin\Silverlight Release\ + TRACE;SILVERLIGHT + ..\..\Bin\Silverlight Release\Microsoft.Scripting.xml + true + 1591,618 + true + pdbonly + AnyCPU + prompt + true + ..\..\SilverlightKey.snk + true + true + ..\..\Utilities\Silverlight\x86ret\ + + + + + + False + $(SilverlightSdkPath)\mscorlib.dll + + + False + $(SilverlightSdkPath)\System.dll + + + + + + + + Properties\SilverlightVersion.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MathResources.resx + True + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4} + Microsoft.Scripting.Core + False + + + {8B0F1074-750E-4D64-BF23-A1E0F54261E5} + Microsoft.Scripting.ExtensionAttribute + + + + + Properties\SilverlightKey.snk + + + + + ResXFileCodeGenerator + MathResources.Designer.cs + Designer + + + + + + + + + + + \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Microsoft.Scripting.csproj.vspscc b/merlin/main/runtime/Microsoft.Scripting/Microsoft.Scripting.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Microsoft.Scripting.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Microsoft.Scripting.txt b/merlin/main/runtime/Microsoft.Scripting/Microsoft.Scripting.txt new file mode 100644 index 0000000000..9f0b04f537 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Microsoft.Scripting.txt @@ -0,0 +1,217 @@ +##################################################################################### +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +##################################################################################### + +# NOTE: do not use \", use ' instead +# NOTE: Use # or ; for comments + +# These are generated with generate_exception_factory.py + +InvalidOperation_ContainsGenericParameters=Cannot access member {1} declared on type {0} because the type contains generic parameters. +MissingType=Type '{0}' is missing or cannot be loaded. +StaticAccessFromInstanceError=static property "{0}" of "{1}" can only be read through a type, not an instance +StaticAssignmentFromInstanceError=static property "{0}" of "{1}" can only be assigned to through a type, not an instance + +# ContractUtils strings + +MethodPreconditionViolated=Method precondition violated +InvalidArgumentValue=Invalid argument value +NonEmptyStringRequired=Non-empty string required +NonEmptyCollectionRequired=Non-empty collection required + +MustBeExceptionInstance=must by an Exception instance +TypeOfTestMustBeBool=Type of test must be bool +TypeOfExpressionMustBeBool=Type of the expression must be bool +EmptyStringIsInvalidPath=Empty string is not a valid path. +InvalidDelegate=Invalid delegate type (Invoke method not found). +ExpectedStaticProperty=expected only static property +PropertyDoesNotExist=Property doesn't exist on the provided type +FieldDoesNotExist=Field doesn't exist on provided type +TypeDoesNotHaveConstructorForTheSignature=Type doesn't have constructor with a given signature +TypeDoesNotHaveMethodForName=Type doesn't have a method with a given name. +TypeDoesNotHaveMethodForNameSignature=Type doesn't have a method with a given name and signature. +CountCannotBeNegative=Count must be non-negative. +ArrayTypeMustBeArray=arrayType must be an array type + +## ExceptionType=ArgumentException +MustHaveCodeOrTarget=Either code or target must be specified. + +# end ContractUtils strings + +## ExceptionType=InvalidOperationException +TypeParameterIsNotDelegate=Type parameter is {0}. Expected a delegate. + +## ExceptionType=InvalidOperationException +InvalidCast=Cannot cast from type '{0}' to type '{1} + +#DLR exceptions + +## ExceptionType=InvalidOperationException +UnknownMemberType=unknown member type: '{0}'. + +## ExceptionType=InvalidOperationException +FirstArgumentMustBeCallSite=RuleBuilder can only be used with delegates whose first argument is CallSite. + +## ExceptionType=InvalidOperationException +NoInstanceForCall=no instance for call. + +## ExceptionType=InvalidOperationException +MissingTest=Missing Test. + +## ExceptionType=InvalidOperationException +MissingTarget=Missing Target. + +## ExceptionType=TypeLoadException +NonGenericWithGenericGroup=The operation requires a non-generic type for {0}, but this represents generic types only + +## ExceptionType=ArgumentException +InvalidOperation=Invalid operation: '{0}' + +## ExceptionType=InvalidOperationException +FinallyAlreadyDefined=Finally already defined. + +## ExceptionType=InvalidOperationException +CannotHaveFaultAndFinally=Can not have fault and finally. + +## ExceptionType=InvalidOperationException +FaultAlreadyDefined=Fault already defined. + +## ExceptionType=ArgumentException +CantCreateDefaultTypeFor=Cannot create default value for type {0}. + +## ExceptionType=ArgumentException +UnhandledConvert=Unhandled convert: {0} + +## ExceptionType=InvalidOperationException +NoCallableMethods={0}.{1} has no publiclly visible method. + +## ExceptionType=ArgumentException +GlobalsMustBeUnique=Global/top-level local variable names must be unique. + +## ExceptionType=ArgumentException +GenNonSerializableBinder=Generating code from non-serializable CallSiteBinder. + +## ExceptionType=ArgumentException +InvalidPath=pecified path is invalid. + +## ExceptionType=ArgumentTypeException +DictionaryNotHashable=Dictionaries are not hashable. + +## ExceptionType=InvalidOperationException +LanguageRegistered=language already registered. + +## ExceptionType=NotImplementedException +MethodOrOperatorNotImplemented=The method or operation is not implemented. + +## ExceptionType=InvalidOperationException +NoException=No exception. + +## ExceptionType=ArgumentException +ExtensionMustBePublic=Extension type {0} must be public. + +## ExceptionType=InvalidOperationException +AlreadyInitialized=Already initialized. + +## ExceptionType=InvalidImplementationException +MustReturnScopeExtension=CreateScopeExtension must return a scope extension. + +## ExceptionType=ArgumentException +InvalidParamNumForService=Invalid number of parameters for the service. + +## ExceptionType=ArgumentException +InvalidArgumentType=Invalid type of argument {0}; expecting {1}. + +## ExceptionType=ArgumentException +CannotChangeNonCachingValue=Cannot change non-caching value. + +## ExceptionType=Microsoft.Scripting.Runtime.UnboundLocalException +ReferencedBeforeAssignment=Local variable '{0}' referenced before assignment. + +## ExceptionType=MissingMemberException +FieldReadonly=Field {0} is read-only + +## ExceptionType=MissingMemberException +PropertyReadonly=Property {0} is read-only + +## ExceptionType=ArgumentException +UnexpectedEvent=Expected event from {0}.{1}, got event from {2}.{3}. + +## ExceptionType=ArgumentTypeException +ExpectedBoundEvent=expected bound event, got {0}. + +## ExceptionType=ArgumentTypeException +UnexpectedType=Expected type {0}, got {1}. + +## ExceptionType=MemberAccessException +MemberWriteOnly=can only write to member {0}. + +## ExceptionType=InvalidOperationException +NoCodeToCompile=No code to compile. + +## ExceptionType=ArgumentException +InvalidStreamType=Invalid stream type: {0}. + +## ExceptionType=InvalidOperationException +QueueEmpty=Queue empty. + +## ExceptionType=InvalidOperationException +EnumerationNotStarted=Enumeration has not started. Call MoveNext. + +## ExceptionType=InvalidOperationException +EnumerationFinished=Enumeration already finished. + +## ExceptionType=InvalidOperationException +CantAddCasing=can't add another casing for identifier {0} + +## ExceptionType=InvalidOperationException +CantAddIdentifier=can't add new identifier {0} + +InvalidCtorImplementation=Type '{0}' doesn't provide a suitable public constructor or its implementation is faulty: {1} + +## ExceptionType=ArgumentException +InvalidOutputDir=Invalid output directory. + +## ExceptionType=ArgumentException +InvalidAsmNameOrExtension=Invalid assembly name or file extension. + +## ExceptionType=ArgumentException +CanotEmitConstant=Cannot emit constant {0} ({1}) + +## ExceptionType=ArgumentException +NoImplicitCast=No implicit cast from {0} to {1} + +## ExceptionType=ArgumentException +NoExplicitCast=No explicit cast from {0} to {1} + +## ExceptionType=MissingMemberException +NameNotDefined=name '{0}' not defined + +## ExceptionType=ArgumentException +NoDefaultValue=No default value for a given type. + +## ExceptionType=ArgumentException +UnknownLanguageProviderType=Specified language provider type is not registered. + +## ExceptionType=InvalidOperationException +CantReadProperty=can't read from property + +## ExceptionType=InvalidOperationException +CantWriteProperty=can't write to property + +## ExceptionType=ArgumentException +IllegalNew_GenericParams=Cannot create instance of {0} because it contains generic parameters + +## ExceptionType=System.Security.VerificationException +VerificationException=Non-verifiable assembly generated: {0}:\nAssembly preserved as {1}\nError text:\n{2}\n + diff --git a/merlin/main/runtime/Microsoft.Scripting/MultiRuntimeAwareAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/MultiRuntimeAwareAttribute.cs new file mode 100644 index 0000000000..5d4cbd5f28 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/MultiRuntimeAwareAttribute.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; + +namespace Microsoft.Scripting { + /// + /// marks a field, class, or struct as being safe to have statics which can be accessed + /// from multiple runtimes. + /// + /// Static fields which are not read-only or marked with this attribute will be flagged + /// by a test which looks for state being shared between runtimes. Before applying this + /// attribute you should ensure that it is safe to share the state. This is typically + /// state which is lazy initialized or state which is caching values which are identical + /// in all runtimes and are immutable. + /// + [Conditional("DEBUG")] + [AttributeUsage(AttributeTargets.Field)] + public sealed class MultiRuntimeAwareAttribute : Attribute { + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/PerfTrack.cs b/merlin/main/runtime/Microsoft.Scripting/PerfTrack.cs new file mode 100644 index 0000000000..89d783b521 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/PerfTrack.cs @@ -0,0 +1,156 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using System.Dynamic.Binders; + +namespace Microsoft.Scripting { + /// + /// This class is useful for quickly collecting performance counts for expensive + /// operations. Usually this means operations involving either reflection or + /// code gen. Long-term we need to see if this can be plugged better into the + /// standard performance counter architecture. + /// + public static class PerfTrack { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1717:OnlyFlagsEnumsShouldHavePluralNames")] // TODO: fix + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] // TODO: fix + public enum Categories { + /// + /// temporary categories for quick investigation, use a custom key if you + /// need to track multiple items, and if you want to keep it then create + /// a new Categories entry and rename all your temporary entries. + /// + Temporary, + ReflectedTypes, + Exceptions, // exceptions thrown + Properties, // properties got or set + Fields, // fields got or set + Methods, // methods called through MethodBase.Invoke()... + Compiler, // Methods compiled via the ReflectOptimizer + DelegateCreate, // we've created a new method for delegates + DictInvoke, // Dictionary accesses + OperatorInvoke, // Invoking an operator against a type + OverAllocate, // a spot where we have an un-ideal algorithm that needs to allocate more than necessary + Rules, // related to rules / actions. + RuleEvaluation, // a rule was evaluated + Count + } + + [MultiRuntimeAware] + private static int totalEvents; + private static readonly Dictionary> _events = MakeEventsDictionary(); + private static readonly Dictionary summaryStats = new Dictionary(); + + private static Dictionary> MakeEventsDictionary() { + Dictionary> result = new Dictionary>(); + + // We do not use Enum.GetValues here since it is not available in SILVERLIGHT + for (int i = 0; i <= (int)Categories.Count; i++) { + result[(Categories)i] = new Dictionary(); + } + + return result; + } + + public static void DumpHistogram(IDictionary histogram) { + var keys = ArrayUtils.MakeArray(histogram.Keys); + var values = ArrayUtils.MakeArray(histogram.Values); + Array.Sort(values, keys); + for (int i = 0; i < keys.Length; i++) { + Console.WriteLine("{0} {1}", keys[i], values[i]); + } + } + + public static void AddHistograms(IDictionary result, IDictionary addend) { + foreach (var entry in addend) { + int value; + result[entry.Key] = entry.Value + (result.TryGetValue(entry.Key, out value) ? value : 0); + } + } + + public static void DumpStats() { + if (totalEvents == 0) return; + + // numbers from AMD Opteron 244 1.8 Ghz, 2.00GB of ram, + // running on IronPython 1.0 Beta 4 against Whidbey RTM. + const double CALL_TIME = 0.0000051442355; + const double THROW_TIME = 0.000025365656; + const double FIELD_TIME = 0.0000018080093; + + Console.WriteLine(); + Console.WriteLine("---- Performance Details ----"); + Console.WriteLine(); + + foreach (KeyValuePair> kvpCategories in _events) { + if (kvpCategories.Value.Count > 0) { + Console.WriteLine("Category : " + kvpCategories.Key); + DumpHistogram(kvpCategories.Value); + Console.WriteLine(); + } + } + + Console.WriteLine(); + Console.WriteLine("---- Performance Summary ----"); + Console.WriteLine(); + double knownTimes = 0; + foreach (KeyValuePair kvp in summaryStats) { + switch (kvp.Key) { + case Categories.Exceptions: + Console.WriteLine("Total Exception ({0}) = {1} (throwtime = ~{2} secs)", kvp.Key, kvp.Value, kvp.Value * THROW_TIME); + knownTimes += kvp.Value * THROW_TIME; + break; + case Categories.Fields: + Console.WriteLine("Total field = {0} (time = ~{1} secs)", kvp.Value, kvp.Value * FIELD_TIME); + knownTimes += kvp.Value * FIELD_TIME; + break; + case Categories.Methods: + Console.WriteLine("Total calls = {0} (calltime = ~{1} secs)", kvp.Value, kvp.Value * CALL_TIME); + knownTimes += kvp.Value * CALL_TIME; + break; + //case Categories.Properties: + default: + Console.WriteLine("Total {1} = {0}", kvp.Value, kvp.Key); + break; + } + } + + Console.WriteLine(); + Console.WriteLine("Total Known Times: {0}", knownTimes); + } + + [Conditional("DEBUG")] + public static void NoteEvent(Categories category, object key) { + if (!DebugOptions.TrackPerformance) return; + + Dictionary categoryEvents = _events[category]; + totalEvents++; + lock (categoryEvents) { + string name = key.ToString(); + Exception ex = key as Exception; + if (ex != null) name = ex.GetType().ToString(); + int v; + if (!categoryEvents.TryGetValue(name, out v)) categoryEvents[name] = 1; + else categoryEvents[name] = v + 1; + + if (!summaryStats.TryGetValue(category, out v)) summaryStats[category] = 1; + else summaryStats[category] = v + 1; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/PlatformAdaptationLayer.cs b/merlin/main/runtime/Microsoft.Scripting/PlatformAdaptationLayer.cs new file mode 100644 index 0000000000..9e819d4c0b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/PlatformAdaptationLayer.cs @@ -0,0 +1,290 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq.Expressions; +using System.Reflection; +using System.Security; + +namespace Microsoft.Scripting { + +#if SILVERLIGHT + public class ExitProcessException : Exception { + + public int ExitCode { get { return exitCode; } } + int exitCode; + + public ExitProcessException(int exitCode) { + this.exitCode = exitCode; + } + } +#endif + + /// + /// Abstracts system operations that are used by DLR and could potentially be platform specific. + /// The host can implement its PAL to adapt DLR to the platform it is running on. + /// For example, the Silverlight host adapts some file operations to work against files on the server. + /// + [Serializable] + public class PlatformAdaptationLayer { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly PlatformAdaptationLayer Default = new PlatformAdaptationLayer(); + +#if SILVERLIGHT + // this dictionary is readonly after initialization: + private Dictionary _assemblyFullNames = new Dictionary(); + + public PlatformAdaptationLayer() { + LoadSilverlightAssemblyNameMapping(); + } + + // TODO: remove the need for this + private void LoadSilverlightAssemblyNameMapping() { + // non-trasparent assemblies + AssemblyName platformKeyVer = new AssemblyName(typeof(object).Assembly.FullName); + AddAssemblyMappings(platformKeyVer, + "mscorlib", + "System", + "System.Core", + "System.Net", + "System.Runtime.Serialization", + "System.ServiceModel.Web", + "System.Windows", + "System.Windows.Browser", + "System.Xml", + "Microsoft.VisualBasic" + ); + + // DLR + language assemblies + AssemblyName languageKeyVer = new AssemblyName(typeof(PlatformAdaptationLayer).Assembly.FullName); + AddAssemblyMappings(languageKeyVer, + "Microsoft.Scripting", + "Microsoft.Scripting.ExtensionAttribute", + "Microsoft.Scripting.Core", + "Microsoft.Scripting.Silverlight", + "IronPython", + "IronPython.Modules", + "IronRuby", + "IronRuby.Libraries", + "Microsoft.JScript.Compiler", + "Microsoft.JScript.Runtime" + ); + + // transparent assemblies => same version as mscorlib but uses transparent key (same as languages) + AssemblyName transparentKeyVer = new AssemblyName(typeof(object).Assembly.FullName); + transparentKeyVer.SetPublicKeyToken(languageKeyVer.GetPublicKeyToken()); + AddAssemblyMappings(transparentKeyVer, + "System.ServiceModel", + "System.ServiceModel.Syndication", + "System.Windows.Controls", + "System.Windows.Controls.Data", + "System.Windows.Controls.Data.Design", + "System.Windows.Controls.Design", + "System.Windows.Controls.Extended", + "System.Windows.Controls.Extended.Design", + "System.Xml.Linq", + "System.Xml.Serialization" + ); + } + + private void AddAssemblyMappings(AssemblyName keyVersion, params string[] names) { + foreach (string asm in names) { + keyVersion.Name = asm; + _assemblyFullNames.Add(asm.ToLower(), keyVersion.FullName); + } + } + + protected string LookupFullName(string name) { + AssemblyName asm = new AssemblyName(name); + if (asm.Version != null || asm.GetPublicKeyToken() != null || asm.GetPublicKey() != null) { + return name; + } + return _assemblyFullNames.ContainsKey(name.ToLower()) ? _assemblyFullNames[name.ToLower()] : name; + } +#endif + #region Assembly Loading + + public virtual Assembly LoadAssembly(string name) { +#if !SILVERLIGHT + return Assembly.Load(name); +#else + return Assembly.Load(LookupFullName(name)); +#endif + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFile")] + public virtual Assembly LoadAssemblyFromPath(string path) { +#if !SILVERLIGHT + return Assembly.LoadFile(path); +#else + throw new NotImplementedException(); +#endif + } + + public virtual void TerminateScriptExecution(int exitCode) { +#if !SILVERLIGHT + System.Environment.Exit(exitCode); +#else + throw new ExitProcessException(exitCode); +#endif + } + + #endregion + + #region Virtual File System + + public virtual StringComparer PathComparer { + get { + return StringComparer.Ordinal; + } + } + + public virtual bool FileExists(string path) { +#if !SILVERLIGHT + return File.Exists(path); +#else + throw new NotImplementedException(); +#endif + } + + public virtual bool DirectoryExists(string path) { +#if !SILVERLIGHT + return Directory.Exists(path); +#else + throw new NotImplementedException(); +#endif + } + + public virtual Stream OpenInputFileStream(string path, FileMode mode, FileAccess access, FileShare share) { +#if !SILVERLIGHT + return new FileStream(path, mode, access, share); +#else + throw new NotImplementedException(); +#endif + } + + public virtual Stream OpenInputFileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) { +#if !SILVERLIGHT + return new FileStream(path, mode, access, share, bufferSize); +#else + throw new NotImplementedException(); +#endif + } + + public virtual Stream OpenInputFileStream(string path) { +#if !SILVERLIGHT + return new FileStream(path, FileMode.Open, FileAccess.Read); +#else + throw new NotImplementedException(); +#endif + } + + public virtual Stream OpenOutputFileStream(string path) { +#if !SILVERLIGHT + return new FileStream(path, FileMode.Create, FileAccess.Write); +#else + throw new NotImplementedException(); +#endif + } + + public virtual string[] GetFiles(string path, string searchPattern) { +#if !SILVERLIGHT + return Directory.GetFiles(path, searchPattern); +#else + throw new NotImplementedException(); +#endif + } + + public virtual string[] GetDirectories(string path, string searchPattern) { +#if !SILVERLIGHT + return Directory.GetDirectories(path, searchPattern); +#else + throw new NotImplementedException(); +#endif + } + + /// Invalid path. + public virtual string GetFullPath(string path) { +#if !SILVERLIGHT + try { + return Path.GetFullPath(path); + } catch (Exception) { + throw Error.InvalidPath(); + } +#else + throw new NotImplementedException(); +#endif + } + + /// Invalid path. + public virtual bool IsAbsolutePath(string path) { +#if !SILVERLIGHT + // GetPathRoot returns either : + // "" -> relative to the current dir + // "\" -> relative to the drive of the current dir + // "X:" -> relative to the current dir, possibly on a different drive + // "X:\" -> absolute + return + Environment.OSVersion.Platform != PlatformID.Unix && Path.GetPathRoot(path).EndsWith(@":\") || + Environment.OSVersion.Platform == PlatformID.Unix && Path.IsPathRooted(path); +#else + throw new NotImplementedException(); +#endif + } + + public virtual string CurrentDirectory { + get { +#if !SILVERLIGHT + return Environment.CurrentDirectory; +#else + throw new NotImplementedException(); +#endif + } + } + + #endregion + + #region Environmental Variables + + public virtual string GetEnvironmentVariable(string key) { +#if !SILVERLIGHT + return Environment.GetEnvironmentVariable(key); +#else + throw new NotImplementedException(); +#endif + } + + public virtual void SetEnvironmentVariable(string key, string value) { +#if !SILVERLIGHT + Environment.SetEnvironmentVariable(key, value); +#else + throw new NotImplementedException(); +#endif + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public virtual System.Collections.IDictionary GetEnvironmentVariables() { +#if !SILVERLIGHT + return Environment.GetEnvironmentVariables(); +#else + throw new NotImplementedException(); +#endif + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Properties/AssemblyInfo.cs b/merlin/main/runtime/Microsoft.Scripting/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b8167b6516 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Properties/AssemblyInfo.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.Scripting")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Microsoft.Scripting")] +[assembly: AssemblyCopyright(" Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +[assembly: CLSCompliant(true)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1bbee69c-30c5-41df-8912-b81da6d658c2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: SecurityTransparent] + +[assembly: System.Resources.NeutralResourcesLanguage("en-US")] + +[assembly: InternalsVisibleTo("TestInternalDLR, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] + +#if !SILVERLIGHT +[assembly: AssemblyVersion("1.0.0.5000")] +[assembly: AssemblyFileVersion("1.0.0.00")] +[assembly: AssemblyInformationalVersion("1.0")] +[assembly: AllowPartiallyTrustedCallers] +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/AmbiguousFileNameException.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/AmbiguousFileNameException.cs new file mode 100644 index 0000000000..40d71ab0ce --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/AmbiguousFileNameException.cs @@ -0,0 +1,68 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.Serialization; +using System.Security.Permissions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting { + [Serializable] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")] + public class AmbiguousFileNameException : Exception { + private readonly string _firstPath; + private readonly string _secondPath; + + public string FirstPath { + get { return _firstPath; } + } + + public string SecondPath { + get { return _secondPath; } + } + + public AmbiguousFileNameException(string firstPath, string secondPath) + : this(firstPath, secondPath, null, null) { + } + + public AmbiguousFileNameException(string firstPath, string secondPath, string message) + : this(firstPath, secondPath, message, null) { + } + + public AmbiguousFileNameException(string firstPath, string secondPath, string message, Exception inner) + : base(message ?? string.Format("File name is ambiguous; more files are matching the same name (including '{0}' and '{1}')", firstPath, secondPath), inner) { + ContractUtils.RequiresNotNull(firstPath, "firstPath"); + ContractUtils.RequiresNotNull(secondPath, "secondPath"); + + _firstPath = firstPath; + _secondPath = secondPath; + } + + +#if !SILVERLIGHT + protected AmbiguousFileNameException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] + public override void GetObjectData(SerializationInfo info, StreamingContext context) { + info.AddValue("firstPath", _firstPath); + info.AddValue("secondPath", _secondPath); + + base.GetObjectData(info, context); + } +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/AssemblyTypeNames.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/AssemblyTypeNames.cs new file mode 100644 index 0000000000..4fcdc002ab --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/AssemblyTypeNames.cs @@ -0,0 +1,4037 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + + internal struct TypeName { + private readonly string _namespace; + private readonly string _typeName; + + internal TypeName(Type type) { + Debug.Assert(!ReflectionUtils.IsNested(type)); + _namespace = type.Namespace; + _typeName = type.Name; + } + + internal TypeName(string nameSpace, string typeName) { + _namespace = nameSpace; + _typeName = typeName; + } + + internal string Namespace { get { return _namespace; } } + internal string Name { get { return _typeName; } } + + public override int GetHashCode() { + int hash = 13 << 20; + if (_namespace != null) hash ^= _namespace.GetHashCode(); + if (_typeName != null) hash ^= _typeName.GetHashCode(); + return hash; + } + + public override bool Equals(object obj) { + if (!(obj is TypeName)) { + return false; + } + TypeName tn = (TypeName)obj; + return tn._namespace == _namespace && tn._typeName == _typeName; + } + + public static bool operator ==(TypeName a, TypeName b) { + return a._namespace == b._namespace && a._typeName == b._typeName; + } + + public static bool operator !=(TypeName a, TypeName b) { + return !(a == b); + } + } + + // TODO: Only used by ComObjectWityTypeInfo. Remove when gone! + internal static class AssemblyTypeNames { + public static IEnumerable GetTypeNames(Assembly assem, bool includePrivateTypes) { +#if !SILVERLIGHT + AssemblyName assemblyName = new AssemblyName(assem.FullName); + switch (assemblyName.Name) { + case "mscorlib": return Get_mscorlib_TypeNames(); + case "System": return Get_System_TypeNames(); + case "System.Xml": return Get_SystemXml_TypeNames(); + case "System.Drawing": return Get_SystemDrawing_TypeNames(); + case "System.Windows.Forms": return Get_SystemWindowsForms_TypeNames(); + } +#endif + + Type[] types = LoadTypesFromAssembly(assem, includePrivateTypes); + + return GetTypeNames(types); + } + + static IEnumerable GetTypeNames(Type[] types) { + foreach (Type t in types) { + if (ReflectionUtils.IsNested(t)) continue; + TypeName typeName = new TypeName(t); + yield return typeName; + } + } + + static Type[] GetAllTypesFromAssembly(Assembly asm) { +#if SILVERLIGHT // ReflectionTypeLoadException + try { + return asm.GetTypes(); + } catch (Exception) { + return Type.EmptyTypes; + } +#else + try { + return asm.GetTypes(); + } catch (ReflectionTypeLoadException rtlException) { + return rtlException.Types; + } +#endif + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public static Type[] LoadTypesFromAssembly(Assembly asm, bool includePrivateTypes) { + if (includePrivateTypes) { + return GetAllTypesFromAssembly(asm); + } + + try { + return asm.GetExportedTypes(); + } catch (NotSupportedException) { + // GetExportedTypes does not work with dynamic assemblies + } catch (Exception) { + // Some type loads may cause exceptions. Unfortunately, there is no way to ask GetExportedTypes + // for just the list of types that we successfully loaded. + } + + Type[] allTypes = GetAllTypesFromAssembly(asm); + + Predicate isPublicTypeDelegate = delegate(Type t) { return t != null && t.IsPublic; }; + return ArrayUtils.FindAll(allTypes, isPublicTypeDelegate); + } + +#if !SILVERLIGHT + static IEnumerable GetTypeNames(string [] namespaces, string [][] types, TypeName [] orcasTypes) { + Debug.Assert(namespaces.Length == types.Length); + + for (int i = 0; i < namespaces.Length; i++) { + for (int j = 0; j < types[i].Length; j++) { + TypeName typeName = new TypeName(namespaces[i], types[i][j]); + yield return typeName; + } + } + + if (IsOrcas) { + foreach(TypeName orcasType in orcasTypes) { + yield return orcasType; + } + } + } + + static bool IsOrcas { + get { + Type t = typeof(object).Assembly.GetType("System.DateTimeOffset", false); + return t != null; + } + } + + #region Generated Well-known assembly type names + + // *** BEGIN GENERATED CODE *** + + static IEnumerable Get_mscorlib_TypeNames() { + // mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + // Total number of types = 1264 + + string [] namespaces = new string[47] { + "Microsoft.Win32", + "Microsoft.Win32.SafeHandles", + "System", + "System.Collections", + "System.Collections.Generic", + "System.Collections.ObjectModel", + "System.Configuration.Assemblies", + "System.Deployment.Internal", + "System.Diagnostics", + "System.Diagnostics.CodeAnalysis", + "System.Diagnostics.SymbolStore", + "System.Globalization", + "System.IO", + "System.IO.IsolatedStorage", + "System.Reflection", + "System.Reflection.Emit", + "System.Resources", + "System.Runtime", + "System.Runtime.CompilerServices", + "System.Runtime.ConstrainedExecution", + "System.Runtime.Hosting", + "System.Runtime.InteropServices", + "System.Runtime.InteropServices.ComTypes", + "System.Runtime.InteropServices.Expando", + "System.Runtime.Remoting", + "System.Runtime.Remoting.Activation", + "System.Runtime.Remoting.Channels", + "System.Runtime.Remoting.Contexts", + "System.Runtime.Remoting.Lifetime", + "System.Runtime.Remoting.Messaging", + "System.Runtime.Remoting.Metadata", + "System.Runtime.Remoting.Metadata.W3cXsd2001", + "System.Runtime.Remoting.Proxies", + "System.Runtime.Remoting.Services", + "System.Runtime.Serialization", + "System.Runtime.Serialization.Formatters", + "System.Runtime.Serialization.Formatters.Binary", + "System.Runtime.Versioning", + "System.Security", + "System.Security.AccessControl", + "System.Security.Cryptography", + "System.Security.Cryptography.X509Certificates", + "System.Security.Permissions", + "System.Security.Policy", + "System.Security.Principal", + "System.Text", + "System.Threading", + }; + + string [][] types = new string[47][] { + + new string[6] { // Microsoft.Win32 + "Registry", + "RegistryHive", + "RegistryKey", + "RegistryKeyPermissionCheck", + "RegistryValueKind", + "RegistryValueOptions", + }, + + new string[6] { // Microsoft.Win32.SafeHandles + "CriticalHandleMinusOneIsInvalid", + "CriticalHandleZeroOrMinusOneIsInvalid", + "SafeFileHandle", + "SafeHandleMinusOneIsInvalid", + "SafeHandleZeroOrMinusOneIsInvalid", + "SafeWaitHandle", + }, + + new string[171] { // System + "AccessViolationException", + "Action`1", + "ActivationContext", + "Activator", + "AppDomain", + "AppDomainInitializer", + "AppDomainManager", + "AppDomainManagerInitializationOptions", + "AppDomainSetup", + "AppDomainUnloadedException", + "ApplicationException", + "ApplicationId", + "ApplicationIdentity", + "ArgIterator", + "ArgumentException", + "ArgumentNullException", + "ArgumentOutOfRangeException", + "ArithmeticException", + "Array", + "ArraySegment`1", + "ArrayTypeMismatchException", + "AssemblyLoadEventArgs", + "AssemblyLoadEventHandler", + "AsyncCallback", + "Attribute", + "AttributeTargets", + "AttributeUsageAttribute", + "BadImageFormatException", + "Base64FormattingOptions", + "BitConverter", + "Boolean", + "Buffer", + "Byte", + "CLSCompliantAttribute", + "CannotUnloadAppDomainException", + "Char", + "CharEnumerator", + "Comparison`1", + "Console", + "ConsoleCancelEventArgs", + "ConsoleCancelEventHandler", + "ConsoleColor", + "ConsoleKey", + "ConsoleKeyInfo", + "ConsoleModifiers", + "ConsoleSpecialKey", + "ContextBoundObject", + "ContextMarshalException", + "ContextStaticAttribute", + "Convert", + "Converter`2", + "CrossAppDomainDelegate", + "DBNull", + "DataMisalignedException", + "DateTime", + "DateTimeKind", + "DayOfWeek", + "Decimal", + "Delegate", + "DivideByZeroException", + "DllNotFoundException", + "Double", + "DuplicateWaitObjectException", + "EntryPointNotFoundException", + "Enum", + "Environment", + "EnvironmentVariableTarget", + "EventArgs", + "EventHandler", + "EventHandler`1", + "Exception", + "ExecutionEngineException", + "FieldAccessException", + "FlagsAttribute", + "FormatException", + "GC", + "Guid", + "IAppDomainSetup", + "IAsyncResult", + "ICloneable", + "IComparable", + "IComparable`1", + "IConvertible", + "ICustomFormatter", + "IDisposable", + "IEquatable`1", + "IFormatProvider", + "IFormattable", + "IServiceProvider", + "IndexOutOfRangeException", + "InsufficientMemoryException", + "Int16", + "Int32", + "Int64", + "IntPtr", + "InvalidCastException", + "InvalidOperationException", + "InvalidProgramException", + "LoaderOptimization", + "LoaderOptimizationAttribute", + "LocalDataStoreSlot", + "MTAThreadAttribute", + "MarshalByRefObject", + "Math", + "MemberAccessException", + "MethodAccessException", + "MidpointRounding", + "MissingFieldException", + "MissingMemberException", + "MissingMethodException", + "ModuleHandle", + "MulticastDelegate", + "MulticastNotSupportedException", + "NonSerializedAttribute", + "NotFiniteNumberException", + "NotImplementedException", + "NotSupportedException", + "NullReferenceException", + "Nullable", + "Nullable`1", + "Object", + "ObjectDisposedException", + "ObsoleteAttribute", + "OperatingSystem", + "OperationCanceledException", + "OutOfMemoryException", + "OverflowException", + "ParamArrayAttribute", + "PlatformID", + "PlatformNotSupportedException", + "Predicate`1", + "Random", + "RankException", + "ResolveEventArgs", + "ResolveEventHandler", + "RuntimeArgumentHandle", + "RuntimeFieldHandle", + "RuntimeMethodHandle", + "RuntimeTypeHandle", + "SByte", + "STAThreadAttribute", + "SerializableAttribute", + "Single", + "StackOverflowException", + "String", + "StringComparer", + "StringComparison", + "StringSplitOptions", + "SystemException", + "ThreadStaticAttribute", + "TimeSpan", + "TimeZone", + "TimeoutException", + "Type", + "TypeCode", + "TypeInitializationException", + "TypeLoadException", + "TypeUnloadedException", + "TypedReference", + "UInt16", + "UInt32", + "UInt64", + "UIntPtr", + "UnauthorizedAccessException", + "UnhandledExceptionEventArgs", + "UnhandledExceptionEventHandler", + "ValueType", + "Version", + "Void", + "WeakReference", + "_AppDomain", + }, + + new string[22] { // System.Collections + "ArrayList", + "BitArray", + "CaseInsensitiveComparer", + "CaseInsensitiveHashCodeProvider", + "CollectionBase", + "Comparer", + "DictionaryBase", + "DictionaryEntry", + "Hashtable", + "ICollection", + "IComparer", + "IDictionary", + "IDictionaryEnumerator", + "IEnumerable", + "IEnumerator", + "IEqualityComparer", + "IHashCodeProvider", + "IList", + "Queue", + "ReadOnlyCollectionBase", + "SortedList", + "Stack", + }, + + new string[13] { // System.Collections.Generic + "Comparer`1", + "Dictionary`2", + "EqualityComparer`1", + "ICollection`1", + "IComparer`1", + "IDictionary`2", + "IEnumerable`1", + "IEnumerator`1", + "IEqualityComparer`1", + "IList`1", + "KeyNotFoundException", + "KeyValuePair`2", + "List`1", + }, + + new string[3] { // System.Collections.ObjectModel + "Collection`1", + "KeyedCollection`2", + "ReadOnlyCollection`1", + }, + + new string[3] { // System.Configuration.Assemblies + "AssemblyHash", + "AssemblyHashAlgorithm", + "AssemblyVersionCompatibility", + }, + + new string[2] { // System.Deployment.Internal + "InternalActivationContextHelper", + "InternalApplicationIdentityHelper", + }, + + new string[14] { // System.Diagnostics + "ConditionalAttribute", + "DebuggableAttribute", + "Debugger", + "DebuggerBrowsableAttribute", + "DebuggerBrowsableState", + "DebuggerDisplayAttribute", + "DebuggerHiddenAttribute", + "DebuggerNonUserCodeAttribute", + "DebuggerStepThroughAttribute", + "DebuggerStepperBoundaryAttribute", + "DebuggerTypeProxyAttribute", + "DebuggerVisualizerAttribute", + "StackFrame", + "StackTrace", + }, + + new string[1] { // System.Diagnostics.CodeAnalysis + "SuppressMessageAttribute", + }, + + new string[15] { // System.Diagnostics.SymbolStore + "ISymbolBinder", + "ISymbolBinder1", + "ISymbolDocument", + "ISymbolDocumentWriter", + "ISymbolMethod", + "ISymbolNamespace", + "ISymbolReader", + "ISymbolScope", + "ISymbolVariable", + "ISymbolWriter", + "SymAddressKind", + "SymDocumentType", + "SymLanguageType", + "SymLanguageVendor", + "SymbolToken", + }, + + new string[37] { // System.Globalization + "Calendar", + "CalendarAlgorithmType", + "CalendarWeekRule", + "CharUnicodeInfo", + "ChineseLunisolarCalendar", + "CompareInfo", + "CompareOptions", + "CultureInfo", + "CultureTypes", + "DateTimeFormatInfo", + "DateTimeStyles", + "DaylightTime", + "DigitShapes", + "EastAsianLunisolarCalendar", + "GregorianCalendar", + "GregorianCalendarTypes", + "HebrewCalendar", + "HijriCalendar", + "IdnMapping", + "JapaneseCalendar", + "JapaneseLunisolarCalendar", + "JulianCalendar", + "KoreanCalendar", + "KoreanLunisolarCalendar", + "NumberFormatInfo", + "NumberStyles", + "PersianCalendar", + "RegionInfo", + "SortKey", + "StringInfo", + "TaiwanCalendar", + "TaiwanLunisolarCalendar", + "TextElementEnumerator", + "TextInfo", + "ThaiBuddhistCalendar", + "UmAlQuraCalendar", + "UnicodeCategory", + }, + + new string[35] { // System.IO + "BinaryReader", + "BinaryWriter", + "BufferedStream", + "Directory", + "DirectoryInfo", + "DirectoryNotFoundException", + "DriveInfo", + "DriveNotFoundException", + "DriveType", + "EndOfStreamException", + "File", + "FileAccess", + "FileAttributes", + "FileInfo", + "FileLoadException", + "FileMode", + "FileNotFoundException", + "FileOptions", + "FileShare", + "FileStream", + "FileSystemInfo", + "IOException", + "MemoryStream", + "Path", + "PathTooLongException", + "SearchOption", + "SeekOrigin", + "Stream", + "StreamReader", + "StreamWriter", + "StringReader", + "StringWriter", + "TextReader", + "TextWriter", + "UnmanagedMemoryStream", + }, + + new string[6] { // System.IO.IsolatedStorage + "INormalizeForIsolatedStorage", + "IsolatedStorage", + "IsolatedStorageException", + "IsolatedStorageFile", + "IsolatedStorageFileStream", + "IsolatedStorageScope", + }, + + new string[76] { // System.Reflection + "AmbiguousMatchException", + "Assembly", + "AssemblyAlgorithmIdAttribute", + "AssemblyCompanyAttribute", + "AssemblyConfigurationAttribute", + "AssemblyCopyrightAttribute", + "AssemblyCultureAttribute", + "AssemblyDefaultAliasAttribute", + "AssemblyDelaySignAttribute", + "AssemblyDescriptionAttribute", + "AssemblyFileVersionAttribute", + "AssemblyFlagsAttribute", + "AssemblyInformationalVersionAttribute", + "AssemblyKeyFileAttribute", + "AssemblyKeyNameAttribute", + "AssemblyName", + "AssemblyNameFlags", + "AssemblyNameProxy", + "AssemblyProductAttribute", + "AssemblyTitleAttribute", + "AssemblyTrademarkAttribute", + "AssemblyVersionAttribute", + "Binder", + "BindingFlags", + "CallingConventions", + "ConstructorInfo", + "CustomAttributeData", + "CustomAttributeFormatException", + "CustomAttributeNamedArgument", + "CustomAttributeTypedArgument", + "DefaultMemberAttribute", + "EventAttributes", + "EventInfo", + "ExceptionHandlingClause", + "ExceptionHandlingClauseOptions", + "FieldAttributes", + "FieldInfo", + "GenericParameterAttributes", + "ICustomAttributeProvider", + "IReflect", + "ImageFileMachine", + "InterfaceMapping", + "InvalidFilterCriteriaException", + "LocalVariableInfo", + "ManifestResourceInfo", + "MemberFilter", + "MemberInfo", + "MemberTypes", + "MethodAttributes", + "MethodBase", + "MethodBody", + "MethodImplAttributes", + "MethodInfo", + "Missing", + "Module", + "ModuleResolveEventHandler", + "ObfuscateAssemblyAttribute", + "ObfuscationAttribute", + "ParameterAttributes", + "ParameterInfo", + "ParameterModifier", + "Pointer", + "PortableExecutableKinds", + "ProcessorArchitecture", + "PropertyAttributes", + "PropertyInfo", + "ReflectionTypeLoadException", + "ResourceAttributes", + "ResourceLocation", + "StrongNameKeyPair", + "TargetException", + "TargetInvocationException", + "TargetParameterCountException", + "TypeAttributes", + "TypeDelegator", + "TypeFilter", + }, + + new string[37] { // System.Reflection.Emit + "AssemblyBuilder", + "AssemblyBuilderAccess", + "ConstructorBuilder", + "CustomAttributeBuilder", + "DynamicILInfo", + "DynamicMethod", + "EnumBuilder", + "EventBuilder", + "EventToken", + "FieldBuilder", + "FieldToken", + "FlowControl", + "GenericTypeParameterBuilder", + "ILGenerator", + "Label", + "LocalBuilder", + "MethodBuilder", + "MethodRental", + "MethodToken", + "ModuleBuilder", + "OpCode", + "OpCodeType", + "OpCodes", + "OperandType", + "PEFileKinds", + "PackingSize", + "ParameterBuilder", + "ParameterToken", + "PropertyBuilder", + "PropertyToken", + "SignatureHelper", + "SignatureToken", + "StackBehaviour", + "StringToken", + "TypeBuilder", + "TypeToken", + "UnmanagedMarshal", + }, + + new string[11] { // System.Resources + "IResourceReader", + "IResourceWriter", + "MissingManifestResourceException", + "MissingSatelliteAssemblyException", + "NeutralResourcesLanguageAttribute", + "ResourceManager", + "ResourceReader", + "ResourceSet", + "ResourceWriter", + "SatelliteContractVersionAttribute", + "UltimateResourceFallbackLocation", + }, + + new string[2] { // System.Runtime + "GCSettings", + "MemoryFailPoint", + }, + + new string[50] { // System.Runtime.CompilerServices + "AccessedThroughPropertyAttribute", + "CallConvCdecl", + "CallConvFastcall", + "CallConvStdcall", + "CallConvThiscall", + "CompilationRelaxations", + "CompilationRelaxationsAttribute", + "CompilerGeneratedAttribute", + "CompilerGlobalScopeAttribute", + "CompilerMarshalOverride", + "CustomConstantAttribute", + "DateTimeConstantAttribute", + "DecimalConstantAttribute", + "DefaultDependencyAttribute", + "DependencyAttribute", + "DiscardableAttribute", + "FixedAddressValueTypeAttribute", + "FixedBufferAttribute", + "HasCopySemanticsAttribute", + "IDispatchConstantAttribute", + "IUnknownConstantAttribute", + "IndexerNameAttribute", + "InternalsVisibleToAttribute", + "IsBoxed", + "IsByValue", + "IsConst", + "IsCopyConstructed", + "IsExplicitlyDereferenced", + "IsImplicitlyDereferenced", + "IsJitIntrinsic", + "IsLong", + "IsPinned", + "IsSignUnspecifiedByte", + "IsUdtReturn", + "IsVolatile", + "LoadHint", + "MethodCodeType", + "MethodImplAttribute", + "MethodImplOptions", + "NativeCppClassAttribute", + "RequiredAttributeAttribute", + "RuntimeCompatibilityAttribute", + "RuntimeHelpers", + "RuntimeWrappedException", + "ScopelessEnumAttribute", + "SpecialNameAttribute", + "StringFreezingAttribute", + "SuppressIldasmAttribute", + "TypeForwardedToAttribute", + "UnsafeValueTypeAttribute", + }, + + new string[5] { // System.Runtime.ConstrainedExecution + "Cer", + "Consistency", + "CriticalFinalizerObject", + "PrePrepareMethodAttribute", + "ReliabilityContractAttribute", + }, + + new string[2] { // System.Runtime.Hosting + "ActivationArguments", + "ApplicationActivator", + }, + + new string[165] { // System.Runtime.InteropServices + "ArrayWithOffset", + "AssemblyRegistrationFlags", + "AutomationProxyAttribute", + "BINDPTR", + "BIND_OPTS", + "BStrWrapper", + "BestFitMappingAttribute", + "CALLCONV", + "COMException", + "CONNECTDATA", + "CallingConvention", + "CharSet", + "ClassInterfaceAttribute", + "ClassInterfaceType", + "CoClassAttribute", + "ComAliasNameAttribute", + "ComCompatibleVersionAttribute", + "ComConversionLossAttribute", + "ComDefaultInterfaceAttribute", + "ComEventInterfaceAttribute", + "ComImportAttribute", + "ComInterfaceType", + "ComMemberType", + "ComRegisterFunctionAttribute", + "ComSourceInterfacesAttribute", + "ComUnregisterFunctionAttribute", + "ComVisibleAttribute", + "CriticalHandle", + "CurrencyWrapper", + "DESCKIND", + "DISPPARAMS", + "DefaultCharSetAttribute", + "DispIdAttribute", + "DispatchWrapper", + "DllImportAttribute", + "ELEMDESC", + "EXCEPINFO", + "ErrorWrapper", + "ExporterEventKind", + "ExtensibleClassFactory", + "ExternalException", + "FILETIME", + "FUNCDESC", + "FUNCFLAGS", + "FUNCKIND", + "FieldOffsetAttribute", + "GCHandle", + "GCHandleType", + "GuidAttribute", + "HandleRef", + "ICustomAdapter", + "ICustomFactory", + "ICustomMarshaler", + "IDLDESC", + "IDLFLAG", + "IDispatchImplAttribute", + "IDispatchImplType", + "IMPLTYPEFLAGS", + "INVOKEKIND", + "IRegistrationServices", + "ITypeLibConverter", + "ITypeLibExporterNameProvider", + "ITypeLibExporterNotifySink", + "ITypeLibImporterNotifySink", + "ImportedFromTypeLibAttribute", + "ImporterEventKind", + "InAttribute", + "InterfaceTypeAttribute", + "InvalidComObjectException", + "InvalidOleVariantTypeException", + "LCIDConversionAttribute", + "LIBFLAGS", + "LayoutKind", + "Marshal", + "MarshalAsAttribute", + "MarshalDirectiveException", + "ObjectCreationDelegate", + "OptionalAttribute", + "OutAttribute", + "PARAMDESC", + "PARAMFLAG", + "PreserveSigAttribute", + "PrimaryInteropAssemblyAttribute", + "ProgIdAttribute", + "RegistrationClassContext", + "RegistrationConnectionType", + "RegistrationServices", + "RuntimeEnvironment", + "SEHException", + "STATSTG", + "SYSKIND", + "SafeArrayRankMismatchException", + "SafeArrayTypeMismatchException", + "SafeHandle", + "SetWin32ContextInIDispatchAttribute", + "StructLayoutAttribute", + "TYPEATTR", + "TYPEDESC", + "TYPEFLAGS", + "TYPEKIND", + "TYPELIBATTR", + "TypeLibConverter", + "TypeLibExporterFlags", + "TypeLibFuncAttribute", + "TypeLibFuncFlags", + "TypeLibImportClassAttribute", + "TypeLibImporterFlags", + "TypeLibTypeAttribute", + "TypeLibTypeFlags", + "TypeLibVarAttribute", + "TypeLibVarFlags", + "TypeLibVersionAttribute", + "UCOMIBindCtx", + "UCOMIConnectionPoint", + "UCOMIConnectionPointContainer", + "UCOMIEnumConnectionPoints", + "UCOMIEnumConnections", + "UCOMIEnumMoniker", + "UCOMIEnumString", + "UCOMIEnumVARIANT", + "UCOMIMoniker", + "UCOMIPersistFile", + "UCOMIRunningObjectTable", + "UCOMIStream", + "UCOMITypeComp", + "UCOMITypeInfo", + "UCOMITypeLib", + "UnknownWrapper", + "UnmanagedFunctionPointerAttribute", + "UnmanagedType", + "VARDESC", + "VARFLAGS", + "VarEnum", + "VariantWrapper", + "_Activator", + "_Assembly", + "_AssemblyBuilder", + "_AssemblyName", + "_Attribute", + "_ConstructorBuilder", + "_ConstructorInfo", + "_CustomAttributeBuilder", + "_EnumBuilder", + "_EventBuilder", + "_EventInfo", + "_Exception", + "_FieldBuilder", + "_FieldInfo", + "_ILGenerator", + "_LocalBuilder", + "_MemberInfo", + "_MethodBase", + "_MethodBuilder", + "_MethodInfo", + "_MethodRental", + "_Module", + "_ModuleBuilder", + "_ParameterBuilder", + "_ParameterInfo", + "_PropertyBuilder", + "_PropertyInfo", + "_SignatureHelper", + "_Thread", + "_Type", + "_TypeBuilder", + }, + + new string[46] { // System.Runtime.InteropServices.ComTypes + "BINDPTR", + "BIND_OPTS", + "CALLCONV", + "CONNECTDATA", + "DESCKIND", + "DISPPARAMS", + "ELEMDESC", + "EXCEPINFO", + "FILETIME", + "FUNCDESC", + "FUNCFLAGS", + "FUNCKIND", + "IBindCtx", + "IConnectionPoint", + "IConnectionPointContainer", + "IDLDESC", + "IDLFLAG", + "IEnumConnectionPoints", + "IEnumConnections", + "IEnumMoniker", + "IEnumString", + "IEnumVARIANT", + "IMPLTYPEFLAGS", + "IMoniker", + "INVOKEKIND", + "IPersistFile", + "IRunningObjectTable", + "IStream", + "ITypeComp", + "ITypeInfo", + "ITypeInfo2", + "ITypeLib", + "ITypeLib2", + "LIBFLAGS", + "PARAMDESC", + "PARAMFLAG", + "STATSTG", + "SYSKIND", + "TYPEATTR", + "TYPEDESC", + "TYPEFLAGS", + "TYPEKIND", + "TYPELIBATTR", + "VARDESC", + "VARFLAGS", + "VARKIND", + }, + + new string[1] { // System.Runtime.InteropServices.Expando + "IExpando", + }, + + new string[20] { // System.Runtime.Remoting + "ActivatedClientTypeEntry", + "ActivatedServiceTypeEntry", + "CustomErrorsModes", + "IChannelInfo", + "IEnvoyInfo", + "IObjectHandle", + "IRemotingTypeInfo", + "InternalRemotingServices", + "ObjRef", + "ObjectHandle", + "RemotingConfiguration", + "RemotingException", + "RemotingServices", + "RemotingTimeoutException", + "ServerException", + "SoapServices", + "TypeEntry", + "WellKnownClientTypeEntry", + "WellKnownObjectMode", + "WellKnownServiceTypeEntry", + }, + + new string[5] { // System.Runtime.Remoting.Activation + "ActivatorLevel", + "IActivator", + "IConstructionCallMessage", + "IConstructionReturnMessage", + "UrlAttribute", + }, + + new string[29] { // System.Runtime.Remoting.Channels + "BaseChannelObjectWithProperties", + "BaseChannelSinkWithProperties", + "BaseChannelWithProperties", + "ChannelDataStore", + "ChannelServices", + "ClientChannelSinkStack", + "IChannel", + "IChannelDataStore", + "IChannelReceiver", + "IChannelReceiverHook", + "IChannelSender", + "IChannelSinkBase", + "IClientChannelSink", + "IClientChannelSinkProvider", + "IClientChannelSinkStack", + "IClientFormatterSink", + "IClientFormatterSinkProvider", + "IClientResponseChannelSinkStack", + "ISecurableChannel", + "IServerChannelSink", + "IServerChannelSinkProvider", + "IServerChannelSinkStack", + "IServerFormatterSinkProvider", + "IServerResponseChannelSinkStack", + "ITransportHeaders", + "ServerChannelSinkStack", + "ServerProcessing", + "SinkProviderData", + "TransportHeaders", + }, + + new string[15] { // System.Runtime.Remoting.Contexts + "Context", + "ContextAttribute", + "ContextProperty", + "CrossContextDelegate", + "IContextAttribute", + "IContextProperty", + "IContextPropertyActivator", + "IContributeClientContextSink", + "IContributeDynamicSink", + "IContributeEnvoySink", + "IContributeObjectSink", + "IContributeServerContextSink", + "IDynamicMessageSink", + "IDynamicProperty", + "SynchronizationAttribute", + }, + + new string[5] { // System.Runtime.Remoting.Lifetime + "ClientSponsor", + "ILease", + "ISponsor", + "LeaseState", + "LifetimeServices", + }, + + new string[24] { // System.Runtime.Remoting.Messaging + "AsyncResult", + "CallContext", + "ConstructionCall", + "ConstructionResponse", + "Header", + "HeaderHandler", + "ILogicalThreadAffinative", + "IMessage", + "IMessageCtrl", + "IMessageSink", + "IMethodCallMessage", + "IMethodMessage", + "IMethodReturnMessage", + "IRemotingFormatter", + "InternalMessageWrapper", + "LogicalCallContext", + "MessageSurrogateFilter", + "MethodCall", + "MethodCallMessageWrapper", + "MethodResponse", + "MethodReturnMessageWrapper", + "OneWayAttribute", + "RemotingSurrogateSelector", + "ReturnMessage", + }, + + new string[7] { // System.Runtime.Remoting.Metadata + "SoapAttribute", + "SoapFieldAttribute", + "SoapMethodAttribute", + "SoapOption", + "SoapParameterAttribute", + "SoapTypeAttribute", + "XmlFieldOrderOption", + }, + + new string[32] { // System.Runtime.Remoting.Metadata.W3cXsd2001 + "ISoapXsd", + "SoapAnyUri", + "SoapBase64Binary", + "SoapDate", + "SoapDateTime", + "SoapDay", + "SoapDuration", + "SoapEntities", + "SoapEntity", + "SoapHexBinary", + "SoapId", + "SoapIdref", + "SoapIdrefs", + "SoapInteger", + "SoapLanguage", + "SoapMonth", + "SoapMonthDay", + "SoapName", + "SoapNcName", + "SoapNegativeInteger", + "SoapNmtoken", + "SoapNmtokens", + "SoapNonNegativeInteger", + "SoapNonPositiveInteger", + "SoapNormalizedString", + "SoapNotation", + "SoapPositiveInteger", + "SoapQName", + "SoapTime", + "SoapToken", + "SoapYear", + "SoapYearMonth", + }, + + new string[2] { // System.Runtime.Remoting.Proxies + "ProxyAttribute", + "RealProxy", + }, + + new string[3] { // System.Runtime.Remoting.Services + "EnterpriseServicesHelper", + "ITrackingHandler", + "TrackingServices", + }, + + new string[26] { // System.Runtime.Serialization + "Formatter", + "FormatterConverter", + "FormatterServices", + "IDeserializationCallback", + "IFormatter", + "IFormatterConverter", + "IObjectReference", + "ISerializable", + "ISerializationSurrogate", + "ISurrogateSelector", + "ObjectIDGenerator", + "ObjectManager", + "OnDeserializedAttribute", + "OnDeserializingAttribute", + "OnSerializedAttribute", + "OnSerializingAttribute", + "OptionalFieldAttribute", + "SerializationBinder", + "SerializationEntry", + "SerializationException", + "SerializationInfo", + "SerializationInfoEnumerator", + "SerializationObjectManager", + "StreamingContext", + "StreamingContextStates", + "SurrogateSelector", + }, + + new string[10] { // System.Runtime.Serialization.Formatters + "FormatterAssemblyStyle", + "FormatterTypeStyle", + "IFieldInfo", + "ISoapMessage", + "InternalRM", + "InternalST", + "ServerFault", + "SoapFault", + "SoapMessage", + "TypeFilterLevel", + }, + + new string[1] { // System.Runtime.Serialization.Formatters.Binary + "BinaryFormatter", + }, + + new string[4] { // System.Runtime.Versioning + "ResourceConsumptionAttribute", + "ResourceExposureAttribute", + "ResourceScope", + "VersioningHelper", + }, + + new string[27] { // System.Security + "AllowPartiallyTrustedCallersAttribute", + "CodeAccessPermission", + "HostProtectionException", + "HostSecurityManager", + "HostSecurityManagerOptions", + "IEvidenceFactory", + "IPermission", + "ISecurityEncodable", + "ISecurityPolicyEncodable", + "IStackWalk", + "NamedPermissionSet", + "PermissionSet", + "PolicyLevelType", + "SecureString", + "SecurityContext", + "SecurityCriticalAttribute", + "SecurityCriticalScope", + "SecurityElement", + "SecurityException", + "SecurityManager", + "SecurityTransparentAttribute", + "SecurityTreatAsSafeAttribute", + "SecurityZone", + "SuppressUnmanagedCodeSecurityAttribute", + "UnverifiableCodeAttribute", + "VerificationException", + "XmlSyntaxException", + }, + + new string[64] { // System.Security.AccessControl + "AccessControlActions", + "AccessControlModification", + "AccessControlSections", + "AccessControlType", + "AccessRule", + "AceEnumerator", + "AceFlags", + "AceQualifier", + "AceType", + "AuditFlags", + "AuditRule", + "AuthorizationRule", + "AuthorizationRuleCollection", + "CommonAce", + "CommonAcl", + "CommonObjectSecurity", + "CommonSecurityDescriptor", + "CompoundAce", + "CompoundAceType", + "ControlFlags", + "CryptoKeyAccessRule", + "CryptoKeyAuditRule", + "CryptoKeyRights", + "CryptoKeySecurity", + "CustomAce", + "DirectoryObjectSecurity", + "DirectorySecurity", + "DiscretionaryAcl", + "EventWaitHandleAccessRule", + "EventWaitHandleAuditRule", + "EventWaitHandleRights", + "EventWaitHandleSecurity", + "FileSecurity", + "FileSystemAccessRule", + "FileSystemAuditRule", + "FileSystemRights", + "FileSystemSecurity", + "GenericAce", + "GenericAcl", + "GenericSecurityDescriptor", + "InheritanceFlags", + "KnownAce", + "MutexAccessRule", + "MutexAuditRule", + "MutexRights", + "MutexSecurity", + "NativeObjectSecurity", + "ObjectAccessRule", + "ObjectAce", + "ObjectAceFlags", + "ObjectAuditRule", + "ObjectSecurity", + "PrivilegeNotHeldException", + "PropagationFlags", + "QualifiedAce", + "RawAcl", + "RawSecurityDescriptor", + "RegistryAccessRule", + "RegistryAuditRule", + "RegistryRights", + "RegistrySecurity", + "ResourceType", + "SecurityInfos", + "SystemAcl", + }, + + new string[78] { // System.Security.Cryptography + "AsymmetricAlgorithm", + "AsymmetricKeyExchangeDeformatter", + "AsymmetricKeyExchangeFormatter", + "AsymmetricSignatureDeformatter", + "AsymmetricSignatureFormatter", + "CipherMode", + "CryptoAPITransform", + "CryptoConfig", + "CryptoStream", + "CryptoStreamMode", + "CryptographicException", + "CryptographicUnexpectedOperationException", + "CspKeyContainerInfo", + "CspParameters", + "CspProviderFlags", + "DES", + "DESCryptoServiceProvider", + "DSA", + "DSACryptoServiceProvider", + "DSAParameters", + "DSASignatureDeformatter", + "DSASignatureFormatter", + "DeriveBytes", + "FromBase64Transform", + "FromBase64TransformMode", + "HMAC", + "HMACMD5", + "HMACRIPEMD160", + "HMACSHA1", + "HMACSHA256", + "HMACSHA384", + "HMACSHA512", + "HashAlgorithm", + "ICryptoTransform", + "ICspAsymmetricAlgorithm", + "KeyNumber", + "KeySizes", + "KeyedHashAlgorithm", + "MACTripleDES", + "MD5", + "MD5CryptoServiceProvider", + "MaskGenerationMethod", + "PKCS1MaskGenerationMethod", + "PaddingMode", + "PasswordDeriveBytes", + "RC2", + "RC2CryptoServiceProvider", + "RIPEMD160", + "RIPEMD160Managed", + "RNGCryptoServiceProvider", + "RSA", + "RSACryptoServiceProvider", + "RSAOAEPKeyExchangeDeformatter", + "RSAOAEPKeyExchangeFormatter", + "RSAPKCS1KeyExchangeDeformatter", + "RSAPKCS1KeyExchangeFormatter", + "RSAPKCS1SignatureDeformatter", + "RSAPKCS1SignatureFormatter", + "RSAParameters", + "RandomNumberGenerator", + "Rfc2898DeriveBytes", + "Rijndael", + "RijndaelManaged", + "RijndaelManagedTransform", + "SHA1", + "SHA1CryptoServiceProvider", + "SHA1Managed", + "SHA256", + "SHA256Managed", + "SHA384", + "SHA384Managed", + "SHA512", + "SHA512Managed", + "SignatureDescription", + "SymmetricAlgorithm", + "ToBase64Transform", + "TripleDES", + "TripleDESCryptoServiceProvider", + }, + + new string[3] { // System.Security.Cryptography.X509Certificates + "X509Certificate", + "X509ContentType", + "X509KeyStorageFlags", + }, + + new string[56] { // System.Security.Permissions + "CodeAccessSecurityAttribute", + "EnvironmentPermission", + "EnvironmentPermissionAccess", + "EnvironmentPermissionAttribute", + "FileDialogPermission", + "FileDialogPermissionAccess", + "FileDialogPermissionAttribute", + "FileIOPermission", + "FileIOPermissionAccess", + "FileIOPermissionAttribute", + "GacIdentityPermission", + "GacIdentityPermissionAttribute", + "HostProtectionAttribute", + "HostProtectionResource", + "IUnrestrictedPermission", + "IsolatedStorageContainment", + "IsolatedStorageFilePermission", + "IsolatedStorageFilePermissionAttribute", + "IsolatedStoragePermission", + "IsolatedStoragePermissionAttribute", + "KeyContainerPermission", + "KeyContainerPermissionAccessEntry", + "KeyContainerPermissionAccessEntryCollection", + "KeyContainerPermissionAccessEntryEnumerator", + "KeyContainerPermissionAttribute", + "KeyContainerPermissionFlags", + "PermissionSetAttribute", + "PermissionState", + "PrincipalPermission", + "PrincipalPermissionAttribute", + "PublisherIdentityPermission", + "PublisherIdentityPermissionAttribute", + "ReflectionPermission", + "ReflectionPermissionAttribute", + "ReflectionPermissionFlag", + "RegistryPermission", + "RegistryPermissionAccess", + "RegistryPermissionAttribute", + "SecurityAction", + "SecurityAttribute", + "SecurityPermission", + "SecurityPermissionAttribute", + "SecurityPermissionFlag", + "SiteIdentityPermission", + "SiteIdentityPermissionAttribute", + "StrongNameIdentityPermission", + "StrongNameIdentityPermissionAttribute", + "StrongNamePublicKeyBlob", + "UIPermission", + "UIPermissionAttribute", + "UIPermissionClipboard", + "UIPermissionWindow", + "UrlIdentityPermission", + "UrlIdentityPermissionAttribute", + "ZoneIdentityPermission", + "ZoneIdentityPermissionAttribute", + }, + + new string[40] { // System.Security.Policy + "AllMembershipCondition", + "ApplicationDirectory", + "ApplicationDirectoryMembershipCondition", + "ApplicationSecurityInfo", + "ApplicationSecurityManager", + "ApplicationTrust", + "ApplicationTrustCollection", + "ApplicationTrustEnumerator", + "ApplicationVersionMatch", + "CodeConnectAccess", + "CodeGroup", + "Evidence", + "FileCodeGroup", + "FirstMatchCodeGroup", + "GacInstalled", + "GacMembershipCondition", + "Hash", + "HashMembershipCondition", + "IApplicationTrustManager", + "IIdentityPermissionFactory", + "IMembershipCondition", + "NetCodeGroup", + "PermissionRequestEvidence", + "PolicyException", + "PolicyLevel", + "PolicyStatement", + "PolicyStatementAttribute", + "Publisher", + "PublisherMembershipCondition", + "Site", + "SiteMembershipCondition", + "StrongName", + "StrongNameMembershipCondition", + "TrustManagerContext", + "TrustManagerUIContext", + "UnionCodeGroup", + "Url", + "UrlMembershipCondition", + "Zone", + "ZoneMembershipCondition", + }, + + new string[18] { // System.Security.Principal + "GenericIdentity", + "GenericPrincipal", + "IIdentity", + "IPrincipal", + "IdentityNotMappedException", + "IdentityReference", + "IdentityReferenceCollection", + "NTAccount", + "PrincipalPolicy", + "SecurityIdentifier", + "TokenAccessLevels", + "TokenImpersonationLevel", + "WellKnownSidType", + "WindowsAccountType", + "WindowsBuiltInRole", + "WindowsIdentity", + "WindowsImpersonationContext", + "WindowsPrincipal", + }, + + new string[25] { // System.Text + "ASCIIEncoding", + "Decoder", + "DecoderExceptionFallback", + "DecoderExceptionFallbackBuffer", + "DecoderFallback", + "DecoderFallbackBuffer", + "DecoderFallbackException", + "DecoderReplacementFallback", + "DecoderReplacementFallbackBuffer", + "Encoder", + "EncoderExceptionFallback", + "EncoderExceptionFallbackBuffer", + "EncoderFallback", + "EncoderFallbackBuffer", + "EncoderFallbackException", + "EncoderReplacementFallback", + "EncoderReplacementFallbackBuffer", + "Encoding", + "EncodingInfo", + "NormalizationForm", + "StringBuilder", + "UTF32Encoding", + "UTF7Encoding", + "UTF8Encoding", + "UnicodeEncoding", + }, + + new string[41] { // System.Threading + "AbandonedMutexException", + "ApartmentState", + "AsyncFlowControl", + "AutoResetEvent", + "CompressedStack", + "ContextCallback", + "EventResetMode", + "EventWaitHandle", + "ExecutionContext", + "HostExecutionContext", + "HostExecutionContextManager", + "IOCompletionCallback", + "Interlocked", + "LockCookie", + "ManualResetEvent", + "Monitor", + "Mutex", + "NativeOverlapped", + "Overlapped", + "ParameterizedThreadStart", + "ReaderWriterLock", + "RegisteredWaitHandle", + "SendOrPostCallback", + "SynchronizationContext", + "SynchronizationLockException", + "Thread", + "ThreadAbortException", + "ThreadInterruptedException", + "ThreadPool", + "ThreadPriority", + "ThreadStart", + "ThreadStartException", + "ThreadState", + "ThreadStateException", + "Timeout", + "Timer", + "TimerCallback", + "WaitCallback", + "WaitHandle", + "WaitHandleCannotBeOpenedException", + "WaitOrTimerCallback", + }, + + }; + TypeName [] orcasTypes = { + new TypeName("System", "DateTimeOffset"), + new TypeName("System", "GCCollectionMode"), + new TypeName("System.Runtime", "GCLatencyMode"), + }; + + return GetTypeNames(namespaces, types, orcasTypes); + } + + static IEnumerable Get_System_TypeNames() { + // System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + // Total number of types = 889 + + string [] namespaces = new string[36] { + "Microsoft.CSharp", + "Microsoft.VisualBasic", + "Microsoft.Win32", + "System", + "System.CodeDom", + "System.CodeDom.Compiler", + "System.Collections.Generic", + "System.Collections.Specialized", + "System.ComponentModel", + "System.ComponentModel.Design", + "System.ComponentModel.Design.Serialization", + "System.Configuration", + "System.Diagnostics", + "System.IO", + "System.IO.Compression", + "System.IO.Ports", + "System.Media", + "System.Net", + "System.Net.Cache", + "System.Net.Configuration", + "System.Net.Mail", + "System.Net.Mime", + "System.Net.NetworkInformation", + "System.Net.Security", + "System.Net.Sockets", + "System.Runtime.InteropServices", + "System.Runtime.InteropServices.ComTypes", + "System.Security.AccessControl", + "System.Security.Authentication", + "System.Security.Cryptography", + "System.Security.Cryptography.X509Certificates", + "System.Security.Permissions", + "System.Text.RegularExpressions", + "System.Threading", + "System.Timers", + "System.Web", + }; + + string [][] types = new string[36][] { + + new string[1] { // Microsoft.CSharp + "CSharpCodeProvider", + }, + + new string[1] { // Microsoft.VisualBasic + "VBCodeProvider", + }, + + new string[20] { // Microsoft.Win32 + "IntranetZoneCredentialPolicy", + "PowerModeChangedEventArgs", + "PowerModeChangedEventHandler", + "PowerModes", + "SessionEndReasons", + "SessionEndedEventArgs", + "SessionEndedEventHandler", + "SessionEndingEventArgs", + "SessionEndingEventHandler", + "SessionSwitchEventArgs", + "SessionSwitchEventHandler", + "SessionSwitchReason", + "SystemEvents", + "TimerElapsedEventArgs", + "TimerElapsedEventHandler", + "UserPreferenceCategory", + "UserPreferenceChangedEventArgs", + "UserPreferenceChangedEventHandler", + "UserPreferenceChangingEventArgs", + "UserPreferenceChangingEventHandler", + }, + + new string[20] { // System + "FileStyleUriParser", + "FtpStyleUriParser", + "GenericUriParser", + "GenericUriParserOptions", + "GopherStyleUriParser", + "HttpStyleUriParser", + "LdapStyleUriParser", + "NetPipeStyleUriParser", + "NetTcpStyleUriParser", + "NewsStyleUriParser", + "Uri", + "UriBuilder", + "UriComponents", + "UriFormat", + "UriFormatException", + "UriHostNameType", + "UriKind", + "UriParser", + "UriPartial", + "UriTypeConverter", + }, + + new string[86] { // System.CodeDom + "CodeArgumentReferenceExpression", + "CodeArrayCreateExpression", + "CodeArrayIndexerExpression", + "CodeAssignStatement", + "CodeAttachEventStatement", + "CodeAttributeArgument", + "CodeAttributeArgumentCollection", + "CodeAttributeDeclaration", + "CodeAttributeDeclarationCollection", + "CodeBaseReferenceExpression", + "CodeBinaryOperatorExpression", + "CodeBinaryOperatorType", + "CodeCastExpression", + "CodeCatchClause", + "CodeCatchClauseCollection", + "CodeChecksumPragma", + "CodeComment", + "CodeCommentStatement", + "CodeCommentStatementCollection", + "CodeCompileUnit", + "CodeConditionStatement", + "CodeConstructor", + "CodeDefaultValueExpression", + "CodeDelegateCreateExpression", + "CodeDelegateInvokeExpression", + "CodeDirectionExpression", + "CodeDirective", + "CodeDirectiveCollection", + "CodeEntryPointMethod", + "CodeEventReferenceExpression", + "CodeExpression", + "CodeExpressionCollection", + "CodeExpressionStatement", + "CodeFieldReferenceExpression", + "CodeGotoStatement", + "CodeIndexerExpression", + "CodeIterationStatement", + "CodeLabeledStatement", + "CodeLinePragma", + "CodeMemberEvent", + "CodeMemberField", + "CodeMemberMethod", + "CodeMemberProperty", + "CodeMethodInvokeExpression", + "CodeMethodReferenceExpression", + "CodeMethodReturnStatement", + "CodeNamespace", + "CodeNamespaceCollection", + "CodeNamespaceImport", + "CodeNamespaceImportCollection", + "CodeObject", + "CodeObjectCreateExpression", + "CodeParameterDeclarationExpression", + "CodeParameterDeclarationExpressionCollection", + "CodePrimitiveExpression", + "CodePropertyReferenceExpression", + "CodePropertySetValueReferenceExpression", + "CodeRegionDirective", + "CodeRegionMode", + "CodeRemoveEventStatement", + "CodeSnippetCompileUnit", + "CodeSnippetExpression", + "CodeSnippetStatement", + "CodeSnippetTypeMember", + "CodeStatement", + "CodeStatementCollection", + "CodeThisReferenceExpression", + "CodeThrowExceptionStatement", + "CodeTryCatchFinallyStatement", + "CodeTypeConstructor", + "CodeTypeDeclaration", + "CodeTypeDeclarationCollection", + "CodeTypeDelegate", + "CodeTypeMember", + "CodeTypeMemberCollection", + "CodeTypeOfExpression", + "CodeTypeParameter", + "CodeTypeParameterCollection", + "CodeTypeReference", + "CodeTypeReferenceCollection", + "CodeTypeReferenceExpression", + "CodeTypeReferenceOptions", + "CodeVariableDeclarationStatement", + "CodeVariableReferenceExpression", + "FieldDirection", + "MemberAttributes", + }, + + new string[19] { // System.CodeDom.Compiler + "CodeCompiler", + "CodeDomProvider", + "CodeGenerator", + "CodeGeneratorOptions", + "CodeParser", + "CompilerError", + "CompilerErrorCollection", + "CompilerInfo", + "CompilerParameters", + "CompilerResults", + "Executor", + "GeneratedCodeAttribute", + "GeneratorSupport", + "ICodeCompiler", + "ICodeGenerator", + "ICodeParser", + "IndentedTextWriter", + "LanguageOptions", + "TempFileCollection", + }, + + new string[6] { // System.Collections.Generic + "LinkedListNode`1", + "LinkedList`1", + "Queue`1", + "SortedDictionary`2", + "SortedList`2", + "Stack`1", + }, + + new string[11] { // System.Collections.Specialized + "BitVector32", + "CollectionsUtil", + "HybridDictionary", + "IOrderedDictionary", + "ListDictionary", + "NameObjectCollectionBase", + "NameValueCollection", + "OrderedDictionary", + "StringCollection", + "StringDictionary", + "StringEnumerator", + }, + + new string[172] { // System.ComponentModel + "AddingNewEventArgs", + "AddingNewEventHandler", + "AmbientValueAttribute", + "ArrayConverter", + "AsyncCompletedEventArgs", + "AsyncCompletedEventHandler", + "AsyncOperation", + "AsyncOperationManager", + "AttributeCollection", + "AttributeProviderAttribute", + "BackgroundWorker", + "BaseNumberConverter", + "BindableAttribute", + "BindableSupport", + "BindingDirection", + "BindingList`1", + "BooleanConverter", + "BrowsableAttribute", + "ByteConverter", + "CancelEventArgs", + "CancelEventHandler", + "CategoryAttribute", + "CharConverter", + "CollectionChangeAction", + "CollectionChangeEventArgs", + "CollectionChangeEventHandler", + "CollectionConverter", + "ComplexBindingPropertiesAttribute", + "Component", + "ComponentCollection", + "ComponentConverter", + "ComponentEditor", + "ComponentResourceManager", + "Container", + "ContainerFilterService", + "CultureInfoConverter", + "CustomTypeDescriptor", + "DataObjectAttribute", + "DataObjectFieldAttribute", + "DataObjectMethodAttribute", + "DataObjectMethodType", + "DateTimeConverter", + "DecimalConverter", + "DefaultBindingPropertyAttribute", + "DefaultEventAttribute", + "DefaultPropertyAttribute", + "DefaultValueAttribute", + "DescriptionAttribute", + "DesignOnlyAttribute", + "DesignTimeVisibleAttribute", + "DesignerAttribute", + "DesignerCategoryAttribute", + "DesignerSerializationVisibility", + "DesignerSerializationVisibilityAttribute", + "DisplayNameAttribute", + "DoWorkEventArgs", + "DoWorkEventHandler", + "DoubleConverter", + "EditorAttribute", + "EditorBrowsableAttribute", + "EditorBrowsableState", + "EnumConverter", + "EventDescriptor", + "EventDescriptorCollection", + "EventHandlerList", + "ExpandableObjectConverter", + "ExtenderProvidedPropertyAttribute", + "GuidConverter", + "HandledEventArgs", + "HandledEventHandler", + "IBindingList", + "IBindingListView", + "ICancelAddNew", + "IChangeTracking", + "IComNativeDescriptorHandler", + "IComponent", + "IContainer", + "ICustomTypeDescriptor", + "IDataErrorInfo", + "IEditableObject", + "IExtenderProvider", + "IIntellisenseBuilder", + "IListSource", + "INestedContainer", + "INestedSite", + "INotifyPropertyChanged", + "IRaiseItemChangedEvents", + "IRevertibleChangeTracking", + "ISite", + "ISupportInitialize", + "ISupportInitializeNotification", + "ISynchronizeInvoke", + "ITypeDescriptorContext", + "ITypedList", + "ImmutableObjectAttribute", + "InheritanceAttribute", + "InheritanceLevel", + "InitializationEventAttribute", + "InstallerTypeAttribute", + "InstanceCreationEditor", + "Int16Converter", + "Int32Converter", + "Int64Converter", + "InvalidAsynchronousStateException", + "InvalidEnumArgumentException", + "LicFileLicenseProvider", + "License", + "LicenseContext", + "LicenseException", + "LicenseManager", + "LicenseProvider", + "LicenseProviderAttribute", + "LicenseUsageMode", + "ListBindableAttribute", + "ListChangedEventArgs", + "ListChangedEventHandler", + "ListChangedType", + "ListSortDescription", + "ListSortDescriptionCollection", + "ListSortDirection", + "LocalizableAttribute", + "LookupBindingPropertiesAttribute", + "MarshalByValueComponent", + "MaskedTextProvider", + "MaskedTextResultHint", + "MemberDescriptor", + "MergablePropertyAttribute", + "MultilineStringConverter", + "NestedContainer", + "NotifyParentPropertyAttribute", + "NullableConverter", + "ParenthesizePropertyNameAttribute", + "PasswordPropertyTextAttribute", + "ProgressChangedEventArgs", + "ProgressChangedEventHandler", + "PropertyChangedEventArgs", + "PropertyChangedEventHandler", + "PropertyDescriptor", + "PropertyDescriptorCollection", + "PropertyTabAttribute", + "PropertyTabScope", + "ProvidePropertyAttribute", + "ReadOnlyAttribute", + "RecommendedAsConfigurableAttribute", + "ReferenceConverter", + "RefreshEventArgs", + "RefreshEventHandler", + "RefreshProperties", + "RefreshPropertiesAttribute", + "RunInstallerAttribute", + "RunWorkerCompletedEventArgs", + "RunWorkerCompletedEventHandler", + "SByteConverter", + "SettingsBindableAttribute", + "SingleConverter", + "StringConverter", + "SyntaxCheck", + "TimeSpanConverter", + "ToolboxItemAttribute", + "ToolboxItemFilterAttribute", + "ToolboxItemFilterType", + "TypeConverter", + "TypeConverterAttribute", + "TypeDescriptionProvider", + "TypeDescriptionProviderAttribute", + "TypeDescriptor", + "TypeListConverter", + "UInt16Converter", + "UInt32Converter", + "UInt64Converter", + "WarningException", + "Win32Exception", + }, + + new string[57] { // System.ComponentModel.Design + "ActiveDesignerEventArgs", + "ActiveDesignerEventHandler", + "CheckoutException", + "CommandID", + "ComponentChangedEventArgs", + "ComponentChangedEventHandler", + "ComponentChangingEventArgs", + "ComponentChangingEventHandler", + "ComponentEventArgs", + "ComponentEventHandler", + "ComponentRenameEventArgs", + "ComponentRenameEventHandler", + "DesignerCollection", + "DesignerEventArgs", + "DesignerEventHandler", + "DesignerOptionService", + "DesignerTransaction", + "DesignerTransactionCloseEventArgs", + "DesignerTransactionCloseEventHandler", + "DesignerVerb", + "DesignerVerbCollection", + "DesigntimeLicenseContext", + "DesigntimeLicenseContextSerializer", + "HelpContextType", + "HelpKeywordAttribute", + "HelpKeywordType", + "IComponentChangeService", + "IComponentDiscoveryService", + "IComponentInitializer", + "IDesigner", + "IDesignerEventService", + "IDesignerFilter", + "IDesignerHost", + "IDesignerOptionService", + "IDictionaryService", + "IEventBindingService", + "IExtenderListService", + "IExtenderProviderService", + "IHelpService", + "IInheritanceService", + "IMenuCommandService", + "IReferenceService", + "IResourceService", + "IRootDesigner", + "ISelectionService", + "IServiceContainer", + "ITreeDesigner", + "ITypeDescriptorFilterService", + "ITypeDiscoveryService", + "ITypeResolutionService", + "MenuCommand", + "SelectionTypes", + "ServiceContainer", + "ServiceCreatorCallback", + "StandardCommands", + "StandardToolWindows", + "ViewTechnology", + }, + + new string[18] { // System.ComponentModel.Design.Serialization + "ComponentSerializationService", + "ContextStack", + "DefaultSerializationProviderAttribute", + "DesignerLoader", + "DesignerSerializerAttribute", + "IDesignerLoaderHost", + "IDesignerLoaderService", + "IDesignerSerializationManager", + "IDesignerSerializationProvider", + "IDesignerSerializationService", + "INameCreationService", + "InstanceDescriptor", + "MemberRelationship", + "MemberRelationshipService", + "ResolveNameEventArgs", + "ResolveNameEventHandler", + "RootDesignerSerializerAttribute", + "SerializationStore", + }, + + new string[54] { // System.Configuration + "AppSettingsReader", + "ApplicationScopedSettingAttribute", + "ApplicationSettingsBase", + "ApplicationSettingsGroup", + "ClientSettingsSection", + "ConfigXmlDocument", + "ConfigurationException", + "ConfigurationSettings", + "DefaultSettingValueAttribute", + "DictionarySectionHandler", + "IApplicationSettingsProvider", + "IConfigurationSectionHandler", + "IConfigurationSystem", + "IPersistComponentSettings", + "ISettingsProviderService", + "IgnoreSectionHandler", + "LocalFileSettingsProvider", + "NameValueFileSectionHandler", + "NameValueSectionHandler", + "NoSettingsVersionUpgradeAttribute", + "SettingAttribute", + "SettingChangingEventArgs", + "SettingChangingEventHandler", + "SettingElement", + "SettingElementCollection", + "SettingValueElement", + "SettingsAttributeDictionary", + "SettingsBase", + "SettingsContext", + "SettingsDescriptionAttribute", + "SettingsGroupDescriptionAttribute", + "SettingsGroupNameAttribute", + "SettingsLoadedEventArgs", + "SettingsLoadedEventHandler", + "SettingsManageability", + "SettingsManageabilityAttribute", + "SettingsProperty", + "SettingsPropertyCollection", + "SettingsPropertyIsReadOnlyException", + "SettingsPropertyNotFoundException", + "SettingsPropertyValue", + "SettingsPropertyValueCollection", + "SettingsPropertyWrongTypeException", + "SettingsProvider", + "SettingsProviderAttribute", + "SettingsProviderCollection", + "SettingsSavingEventHandler", + "SettingsSerializeAs", + "SettingsSerializeAsAttribute", + "SingleTagSectionHandler", + "SpecialSetting", + "SpecialSettingAttribute", + "UserScopedSettingAttribute", + "UserSettingsGroup", + }, + + new string[76] { // System.Diagnostics + "BooleanSwitch", + "ConsoleTraceListener", + "CorrelationManager", + "CounterCreationData", + "CounterCreationDataCollection", + "CounterSample", + "CounterSampleCalculator", + "DataReceivedEventArgs", + "DataReceivedEventHandler", + "Debug", + "DefaultTraceListener", + "DelimitedListTraceListener", + "DiagnosticsConfigurationHandler", + "EntryWrittenEventArgs", + "EntryWrittenEventHandler", + "EventInstance", + "EventLog", + "EventLogEntry", + "EventLogEntryCollection", + "EventLogEntryType", + "EventLogPermission", + "EventLogPermissionAccess", + "EventLogPermissionAttribute", + "EventLogPermissionEntry", + "EventLogPermissionEntryCollection", + "EventLogTraceListener", + "EventSourceCreationData", + "EventTypeFilter", + "FileVersionInfo", + "ICollectData", + "InstanceData", + "InstanceDataCollection", + "InstanceDataCollectionCollection", + "MonitoringDescriptionAttribute", + "OverflowAction", + "PerformanceCounter", + "PerformanceCounterCategory", + "PerformanceCounterCategoryType", + "PerformanceCounterInstanceLifetime", + "PerformanceCounterManager", + "PerformanceCounterPermission", + "PerformanceCounterPermissionAccess", + "PerformanceCounterPermissionAttribute", + "PerformanceCounterPermissionEntry", + "PerformanceCounterPermissionEntryCollection", + "PerformanceCounterType", + "Process", + "ProcessModule", + "ProcessModuleCollection", + "ProcessPriorityClass", + "ProcessStartInfo", + "ProcessThread", + "ProcessThreadCollection", + "ProcessWindowStyle", + "SourceFilter", + "SourceLevels", + "SourceSwitch", + "Stopwatch", + "Switch", + "SwitchAttribute", + "SwitchLevelAttribute", + "TextWriterTraceListener", + "ThreadPriorityLevel", + "ThreadState", + "ThreadWaitReason", + "Trace", + "TraceEventCache", + "TraceEventType", + "TraceFilter", + "TraceLevel", + "TraceListener", + "TraceListenerCollection", + "TraceOptions", + "TraceSource", + "TraceSwitch", + "XmlWriterTraceListener", + }, + + new string[13] { // System.IO + "ErrorEventArgs", + "ErrorEventHandler", + "FileSystemEventArgs", + "FileSystemEventHandler", + "FileSystemWatcher", + "IODescriptionAttribute", + "InternalBufferOverflowException", + "InvalidDataException", + "NotifyFilters", + "RenamedEventArgs", + "RenamedEventHandler", + "WaitForChangedResult", + "WatcherChangeTypes", + }, + + new string[3] { // System.IO.Compression + "CompressionMode", + "DeflateStream", + "GZipStream", + }, + + new string[13] { // System.IO.Ports + "Handshake", + "Parity", + "SerialData", + "SerialDataReceivedEventArgs", + "SerialDataReceivedEventHandler", + "SerialError", + "SerialErrorReceivedEventArgs", + "SerialErrorReceivedEventHandler", + "SerialPinChange", + "SerialPinChangedEventArgs", + "SerialPinChangedEventHandler", + "SerialPort", + "StopBits", + }, + + new string[3] { // System.Media + "SoundPlayer", + "SystemSound", + "SystemSounds", + }, + + new string[87] { // System.Net + "AuthenticationManager", + "AuthenticationSchemeSelector", + "AuthenticationSchemes", + "Authorization", + "BindIPEndPoint", + "Cookie", + "CookieCollection", + "CookieContainer", + "CookieException", + "CredentialCache", + "DecompressionMethods", + "Dns", + "DnsPermission", + "DnsPermissionAttribute", + "DownloadDataCompletedEventArgs", + "DownloadDataCompletedEventHandler", + "DownloadProgressChangedEventArgs", + "DownloadProgressChangedEventHandler", + "DownloadStringCompletedEventArgs", + "DownloadStringCompletedEventHandler", + "EndPoint", + "EndpointPermission", + "FileWebRequest", + "FileWebResponse", + "FtpStatusCode", + "FtpWebRequest", + "FtpWebResponse", + "GlobalProxySelection", + "HttpContinueDelegate", + "HttpListener", + "HttpListenerBasicIdentity", + "HttpListenerContext", + "HttpListenerException", + "HttpListenerPrefixCollection", + "HttpListenerRequest", + "HttpListenerResponse", + "HttpRequestHeader", + "HttpResponseHeader", + "HttpStatusCode", + "HttpVersion", + "HttpWebRequest", + "HttpWebResponse", + "IAuthenticationModule", + "ICertificatePolicy", + "ICredentialPolicy", + "ICredentials", + "ICredentialsByHost", + "IPAddress", + "IPEndPoint", + "IPHostEntry", + "IWebProxy", + "IWebProxyScript", + "IWebRequestCreate", + "NetworkAccess", + "NetworkCredential", + "OpenReadCompletedEventArgs", + "OpenReadCompletedEventHandler", + "OpenWriteCompletedEventArgs", + "OpenWriteCompletedEventHandler", + "ProtocolViolationException", + "SecurityProtocolType", + "ServicePoint", + "ServicePointManager", + "SocketAddress", + "SocketPermission", + "SocketPermissionAttribute", + "TransportType", + "UploadDataCompletedEventArgs", + "UploadDataCompletedEventHandler", + "UploadFileCompletedEventArgs", + "UploadFileCompletedEventHandler", + "UploadProgressChangedEventArgs", + "UploadProgressChangedEventHandler", + "UploadStringCompletedEventArgs", + "UploadStringCompletedEventHandler", + "UploadValuesCompletedEventArgs", + "UploadValuesCompletedEventHandler", + "WebClient", + "WebException", + "WebExceptionStatus", + "WebHeaderCollection", + "WebPermission", + "WebPermissionAttribute", + "WebProxy", + "WebRequest", + "WebRequestMethods", + "WebResponse", + }, + + new string[5] { // System.Net.Cache + "HttpCacheAgeControl", + "HttpRequestCacheLevel", + "HttpRequestCachePolicy", + "RequestCacheLevel", + "RequestCachePolicy", + }, + + new string[29] { // System.Net.Configuration + "AuthenticationModuleElement", + "AuthenticationModuleElementCollection", + "AuthenticationModulesSection", + "BypassElement", + "BypassElementCollection", + "ConnectionManagementElement", + "ConnectionManagementElementCollection", + "ConnectionManagementSection", + "DefaultProxySection", + "FtpCachePolicyElement", + "HttpCachePolicyElement", + "HttpWebRequestElement", + "Ipv6Element", + "MailSettingsSectionGroup", + "ModuleElement", + "NetSectionGroup", + "PerformanceCountersElement", + "ProxyElement", + "RequestCachingSection", + "ServicePointManagerElement", + "SettingsSection", + "SmtpNetworkElement", + "SmtpSection", + "SmtpSpecifiedPickupDirectoryElement", + "SocketElement", + "WebProxyScriptElement", + "WebRequestModuleElement", + "WebRequestModuleElementCollection", + "WebRequestModulesSection", + }, + + new string[22] { // System.Net.Mail + "AlternateView", + "AlternateViewCollection", + "Attachment", + "AttachmentBase", + "AttachmentCollection", + "DeliveryNotificationOptions", + "LinkedResource", + "LinkedResourceCollection", + "MailAddress", + "MailAddressCollection", + "MailMessage", + "MailPriority", + "SendCompletedEventHandler", + "SmtpAccess", + "SmtpClient", + "SmtpDeliveryMethod", + "SmtpException", + "SmtpFailedRecipientException", + "SmtpFailedRecipientsException", + "SmtpPermission", + "SmtpPermissionAttribute", + "SmtpStatusCode", + }, + + new string[5] { // System.Net.Mime + "ContentDisposition", + "ContentType", + "DispositionTypeNames", + "MediaTypeNames", + "TransferEncoding", + }, + + new string[45] { // System.Net.NetworkInformation + "DuplicateAddressDetectionState", + "GatewayIPAddressInformation", + "GatewayIPAddressInformationCollection", + "IPAddressCollection", + "IPAddressInformation", + "IPAddressInformationCollection", + "IPGlobalProperties", + "IPGlobalStatistics", + "IPInterfaceProperties", + "IPStatus", + "IPv4InterfaceProperties", + "IPv4InterfaceStatistics", + "IPv6InterfaceProperties", + "IcmpV4Statistics", + "IcmpV6Statistics", + "MulticastIPAddressInformation", + "MulticastIPAddressInformationCollection", + "NetBiosNodeType", + "NetworkAddressChangedEventHandler", + "NetworkAvailabilityChangedEventHandler", + "NetworkAvailabilityEventArgs", + "NetworkChange", + "NetworkInformationAccess", + "NetworkInformationException", + "NetworkInformationPermission", + "NetworkInformationPermissionAttribute", + "NetworkInterface", + "NetworkInterfaceComponent", + "NetworkInterfaceType", + "OperationalStatus", + "PhysicalAddress", + "Ping", + "PingCompletedEventArgs", + "PingCompletedEventHandler", + "PingException", + "PingOptions", + "PingReply", + "PrefixOrigin", + "SuffixOrigin", + "TcpConnectionInformation", + "TcpState", + "TcpStatistics", + "UdpStatistics", + "UnicastIPAddressInformation", + "UnicastIPAddressInformationCollection", + }, + + new string[8] { // System.Net.Security + "AuthenticatedStream", + "AuthenticationLevel", + "LocalCertificateSelectionCallback", + "NegotiateStream", + "ProtectionLevel", + "RemoteCertificateValidationCallback", + "SslPolicyErrors", + "SslStream", + }, + + new string[24] { // System.Net.Sockets + "AddressFamily", + "IOControlCode", + "IPPacketInformation", + "IPv6MulticastOption", + "LingerOption", + "MulticastOption", + "NetworkStream", + "ProtocolFamily", + "ProtocolType", + "SelectMode", + "Socket", + "SocketError", + "SocketException", + "SocketFlags", + "SocketInformation", + "SocketInformationOptions", + "SocketOptionLevel", + "SocketOptionName", + "SocketShutdown", + "SocketType", + "TcpClient", + "TcpListener", + "TransmitFileOptions", + "UdpClient", + }, + + new string[3] { // System.Runtime.InteropServices + "DefaultParameterValueAttribute", + "HandleCollector", + "StandardOleMarshalObject", + }, + + new string[11] { // System.Runtime.InteropServices.ComTypes + "ADVF", + "DATADIR", + "DVASPECT", + "FORMATETC", + "IAdviseSink", + "IDataObject", + "IEnumFORMATETC", + "IEnumSTATDATA", + "STATDATA", + "STGMEDIUM", + "TYMED", + }, + + new string[4] { // System.Security.AccessControl + "SemaphoreAccessRule", + "SemaphoreAuditRule", + "SemaphoreRights", + "SemaphoreSecurity", + }, + + new string[6] { // System.Security.Authentication + "AuthenticationException", + "CipherAlgorithmType", + "ExchangeAlgorithmType", + "HashAlgorithmType", + "InvalidCredentialException", + "SslProtocols", + }, + + new string[6] { // System.Security.Cryptography + "AsnEncodedData", + "AsnEncodedDataCollection", + "AsnEncodedDataEnumerator", + "Oid", + "OidCollection", + "OidEnumerator", + }, + + new string[33] { // System.Security.Cryptography.X509Certificates + "OpenFlags", + "PublicKey", + "StoreLocation", + "StoreName", + "X500DistinguishedName", + "X500DistinguishedNameFlags", + "X509BasicConstraintsExtension", + "X509Certificate2", + "X509Certificate2Collection", + "X509Certificate2Enumerator", + "X509CertificateCollection", + "X509Chain", + "X509ChainElement", + "X509ChainElementCollection", + "X509ChainElementEnumerator", + "X509ChainPolicy", + "X509ChainStatus", + "X509ChainStatusFlags", + "X509EnhancedKeyUsageExtension", + "X509Extension", + "X509ExtensionCollection", + "X509ExtensionEnumerator", + "X509FindType", + "X509IncludeOption", + "X509KeyUsageExtension", + "X509KeyUsageFlags", + "X509NameType", + "X509RevocationFlag", + "X509RevocationMode", + "X509Store", + "X509SubjectKeyIdentifierExtension", + "X509SubjectKeyIdentifierHashAlgorithm", + "X509VerificationFlags", + }, + + new string[5] { // System.Security.Permissions + "ResourcePermissionBase", + "ResourcePermissionBaseEntry", + "StorePermission", + "StorePermissionAttribute", + "StorePermissionFlags", + }, + + new string[12] { // System.Text.RegularExpressions + "Capture", + "CaptureCollection", + "Group", + "GroupCollection", + "Match", + "MatchCollection", + "MatchEvaluator", + "Regex", + "RegexCompilationInfo", + "RegexOptions", + "RegexRunner", + "RegexRunnerFactory", + }, + + new string[4] { // System.Threading + "Semaphore", + "SemaphoreFullException", + "ThreadExceptionEventArgs", + "ThreadExceptionEventHandler", + }, + + new string[4] { // System.Timers + "ElapsedEventArgs", + "ElapsedEventHandler", + "Timer", + "TimersDescriptionAttribute", + }, + + new string[3] { // System.Web + "AspNetHostingPermission", + "AspNetHostingPermissionAttribute", + "AspNetHostingPermissionLevel", + }, + + }; + TypeName [] orcasTypes = { + new TypeName("System.ComponentModel.Design.Serialization", "IDesignerLoaderHost2"), + new TypeName("System.ComponentModel", "INotifyPropertyChanging"), + new TypeName("System.ComponentModel", "PropertyChangingEventArgs"), + new TypeName("System.ComponentModel", "PropertyChangingEventHandler"), + new TypeName("System.Configuration", "IdnElement"), + new TypeName("System.Configuration", "IriParsingElement"), + new TypeName("System.Configuration", "UriSection"), + new TypeName("System.Net.Sockets", "SendPacketsElement"), + new TypeName("System.Net.Sockets", "SocketAsyncEventArgs"), + new TypeName("System.Net.Sockets", "SocketAsyncOperation"), + new TypeName("System", "UriIdnScope"), + }; + + return GetTypeNames(namespaces, types, orcasTypes); + } + + static IEnumerable Get_SystemDrawing_TypeNames() { + // System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + // Total number of types = 177 + + string [] namespaces = new string[6] { + "System.Drawing", + "System.Drawing.Design", + "System.Drawing.Drawing2D", + "System.Drawing.Imaging", + "System.Drawing.Printing", + "System.Drawing.Text", + }; + + string [][] types = new string[6][] { + + new string[54] { // System.Drawing + "Bitmap", + "Brush", + "Brushes", + "BufferedGraphics", + "BufferedGraphicsContext", + "BufferedGraphicsManager", + "CharacterRange", + "Color", + "ColorConverter", + "ColorTranslator", + "ContentAlignment", + "CopyPixelOperation", + "Font", + "FontConverter", + "FontFamily", + "FontStyle", + "Graphics", + "GraphicsUnit", + "IDeviceContext", + "Icon", + "IconConverter", + "Image", + "ImageAnimator", + "ImageConverter", + "ImageFormatConverter", + "KnownColor", + "Pen", + "Pens", + "Point", + "PointConverter", + "PointF", + "Rectangle", + "RectangleConverter", + "RectangleF", + "Region", + "RotateFlipType", + "Size", + "SizeConverter", + "SizeF", + "SizeFConverter", + "SolidBrush", + "StringAlignment", + "StringDigitSubstitute", + "StringFormat", + "StringFormatFlags", + "StringTrimming", + "StringUnit", + "SystemBrushes", + "SystemColors", + "SystemFonts", + "SystemIcons", + "SystemPens", + "TextureBrush", + "ToolboxBitmapAttribute", + }, + + new string[18] { // System.Drawing.Design + "CategoryNameCollection", + "IPropertyValueUIService", + "IToolboxItemProvider", + "IToolboxService", + "IToolboxUser", + "PaintValueEventArgs", + "PropertyValueUIHandler", + "PropertyValueUIItem", + "PropertyValueUIItemInvokeHandler", + "ToolboxComponentsCreatedEventArgs", + "ToolboxComponentsCreatedEventHandler", + "ToolboxComponentsCreatingEventArgs", + "ToolboxComponentsCreatingEventHandler", + "ToolboxItem", + "ToolboxItemCollection", + "ToolboxItemCreatorCallback", + "UITypeEditor", + "UITypeEditorEditStyle", + }, + + new string[36] { // System.Drawing.Drawing2D + "AdjustableArrowCap", + "Blend", + "ColorBlend", + "CombineMode", + "CompositingMode", + "CompositingQuality", + "CoordinateSpace", + "CustomLineCap", + "DashCap", + "DashStyle", + "FillMode", + "FlushIntention", + "GraphicsContainer", + "GraphicsPath", + "GraphicsPathIterator", + "GraphicsState", + "HatchBrush", + "HatchStyle", + "InterpolationMode", + "LineCap", + "LineJoin", + "LinearGradientBrush", + "LinearGradientMode", + "Matrix", + "MatrixOrder", + "PathData", + "PathGradientBrush", + "PathPointType", + "PenAlignment", + "PenType", + "PixelOffsetMode", + "QualityMode", + "RegionData", + "SmoothingMode", + "WarpMode", + "WrapMode", + }, + + new string[33] { // System.Drawing.Imaging + "BitmapData", + "ColorAdjustType", + "ColorChannelFlag", + "ColorMap", + "ColorMapType", + "ColorMatrix", + "ColorMatrixFlag", + "ColorMode", + "ColorPalette", + "EmfPlusRecordType", + "EmfType", + "Encoder", + "EncoderParameter", + "EncoderParameterValueType", + "EncoderParameters", + "EncoderValue", + "FrameDimension", + "ImageAttributes", + "ImageCodecFlags", + "ImageCodecInfo", + "ImageFlags", + "ImageFormat", + "ImageLockMode", + "MetaHeader", + "Metafile", + "MetafileFrameUnit", + "MetafileHeader", + "MetafileType", + "PaletteFlags", + "PixelFormat", + "PlayRecordCallback", + "PropertyItem", + "WmfPlaceableFileHeader", + }, + + new string[30] { // System.Drawing.Printing + "Duplex", + "InvalidPrinterException", + "Margins", + "MarginsConverter", + "PageSettings", + "PaperKind", + "PaperSize", + "PaperSource", + "PaperSourceKind", + "PreviewPageInfo", + "PreviewPrintController", + "PrintAction", + "PrintController", + "PrintDocument", + "PrintEventArgs", + "PrintEventHandler", + "PrintPageEventArgs", + "PrintPageEventHandler", + "PrintRange", + "PrinterResolution", + "PrinterResolutionKind", + "PrinterSettings", + "PrinterUnit", + "PrinterUnitConvert", + "PrintingPermission", + "PrintingPermissionAttribute", + "PrintingPermissionLevel", + "QueryPageSettingsEventArgs", + "QueryPageSettingsEventHandler", + "StandardPrintController", + }, + + new string[6] { // System.Drawing.Text + "FontCollection", + "GenericFontFamilies", + "HotkeyPrefix", + "InstalledFontCollection", + "PrivateFontCollection", + "TextRenderingHint", + }, + + }; + TypeName [] orcasTypes = { + }; + + return GetTypeNames(namespaces, types, orcasTypes); + } + + static IEnumerable Get_SystemWindowsForms_TypeNames() { + // System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + // Total number of types = 777 + + string [] namespaces = new string[7] { + "System.Resources", + "System.Windows.Forms", + "System.Windows.Forms.ComponentModel.Com2Interop", + "System.Windows.Forms.Design", + "System.Windows.Forms.Layout", + "System.Windows.Forms.PropertyGridInternal", + "System.Windows.Forms.VisualStyles", + }; + + string [][] types = new string[7][] { + + new string[5] { // System.Resources + "ResXDataNode", + "ResXFileRef", + "ResXResourceReader", + "ResXResourceSet", + "ResXResourceWriter", + }, + + new string[705] { // System.Windows.Forms + "AccessibleEvents", + "AccessibleNavigation", + "AccessibleObject", + "AccessibleRole", + "AccessibleSelection", + "AccessibleStates", + "AmbientProperties", + "AnchorStyles", + "Appearance", + "Application", + "ApplicationContext", + "ArrangeDirection", + "ArrangeStartingPosition", + "ArrowDirection", + "AutoCompleteMode", + "AutoCompleteSource", + "AutoCompleteStringCollection", + "AutoScaleMode", + "AutoSizeMode", + "AutoValidate", + "AxHost", + "BaseCollection", + "BatteryChargeStatus", + "Binding", + "BindingCompleteContext", + "BindingCompleteEventArgs", + "BindingCompleteEventHandler", + "BindingCompleteState", + "BindingContext", + "BindingManagerBase", + "BindingManagerDataErrorEventArgs", + "BindingManagerDataErrorEventHandler", + "BindingMemberInfo", + "BindingNavigator", + "BindingSource", + "BindingsCollection", + "BootMode", + "Border3DSide", + "Border3DStyle", + "BorderStyle", + "BoundsSpecified", + "Button", + "ButtonBase", + "ButtonBorderStyle", + "ButtonRenderer", + "ButtonState", + "CacheVirtualItemsEventArgs", + "CacheVirtualItemsEventHandler", + "CaptionButton", + "CharacterCasing", + "CheckBox", + "CheckBoxRenderer", + "CheckState", + "CheckedListBox", + "Clipboard", + "CloseReason", + "ColorDepth", + "ColorDialog", + "ColumnClickEventArgs", + "ColumnClickEventHandler", + "ColumnHeader", + "ColumnHeaderAutoResizeStyle", + "ColumnHeaderConverter", + "ColumnHeaderStyle", + "ColumnReorderedEventArgs", + "ColumnReorderedEventHandler", + "ColumnStyle", + "ColumnWidthChangedEventArgs", + "ColumnWidthChangedEventHandler", + "ColumnWidthChangingEventArgs", + "ColumnWidthChangingEventHandler", + "ComboBox", + "ComboBoxRenderer", + "ComboBoxStyle", + "CommonDialog", + "ContainerControl", + "ContentsResizedEventArgs", + "ContentsResizedEventHandler", + "ContextMenu", + "ContextMenuStrip", + "Control", + "ControlBindingsCollection", + "ControlEventArgs", + "ControlEventHandler", + "ControlPaint", + "ControlStyles", + "ControlUpdateMode", + "ConvertEventArgs", + "ConvertEventHandler", + "CreateParams", + "CurrencyManager", + "Cursor", + "CursorConverter", + "Cursors", + "DataFormats", + "DataGrid", + "DataGridBoolColumn", + "DataGridCell", + "DataGridColumnStyle", + "DataGridLineStyle", + "DataGridParentRowsLabelStyle", + "DataGridPreferredColumnWidthTypeConverter", + "DataGridTableStyle", + "DataGridTextBox", + "DataGridTextBoxColumn", + "DataGridView", + "DataGridViewAdvancedBorderStyle", + "DataGridViewAdvancedCellBorderStyle", + "DataGridViewAutoSizeColumnMode", + "DataGridViewAutoSizeColumnModeEventArgs", + "DataGridViewAutoSizeColumnModeEventHandler", + "DataGridViewAutoSizeColumnsMode", + "DataGridViewAutoSizeColumnsModeEventArgs", + "DataGridViewAutoSizeColumnsModeEventHandler", + "DataGridViewAutoSizeModeEventArgs", + "DataGridViewAutoSizeModeEventHandler", + "DataGridViewAutoSizeRowMode", + "DataGridViewAutoSizeRowsMode", + "DataGridViewBand", + "DataGridViewBindingCompleteEventArgs", + "DataGridViewBindingCompleteEventHandler", + "DataGridViewButtonCell", + "DataGridViewButtonColumn", + "DataGridViewCell", + "DataGridViewCellBorderStyle", + "DataGridViewCellCancelEventArgs", + "DataGridViewCellCancelEventHandler", + "DataGridViewCellCollection", + "DataGridViewCellContextMenuStripNeededEventArgs", + "DataGridViewCellContextMenuStripNeededEventHandler", + "DataGridViewCellErrorTextNeededEventArgs", + "DataGridViewCellErrorTextNeededEventHandler", + "DataGridViewCellEventArgs", + "DataGridViewCellEventHandler", + "DataGridViewCellFormattingEventArgs", + "DataGridViewCellFormattingEventHandler", + "DataGridViewCellMouseEventArgs", + "DataGridViewCellMouseEventHandler", + "DataGridViewCellPaintingEventArgs", + "DataGridViewCellPaintingEventHandler", + "DataGridViewCellParsingEventArgs", + "DataGridViewCellParsingEventHandler", + "DataGridViewCellStateChangedEventArgs", + "DataGridViewCellStateChangedEventHandler", + "DataGridViewCellStyle", + "DataGridViewCellStyleContentChangedEventArgs", + "DataGridViewCellStyleContentChangedEventHandler", + "DataGridViewCellStyleConverter", + "DataGridViewCellStyleScopes", + "DataGridViewCellToolTipTextNeededEventArgs", + "DataGridViewCellToolTipTextNeededEventHandler", + "DataGridViewCellValidatingEventArgs", + "DataGridViewCellValidatingEventHandler", + "DataGridViewCellValueEventArgs", + "DataGridViewCellValueEventHandler", + "DataGridViewCheckBoxCell", + "DataGridViewCheckBoxColumn", + "DataGridViewClipboardCopyMode", + "DataGridViewColumn", + "DataGridViewColumnCollection", + "DataGridViewColumnDesignTimeVisibleAttribute", + "DataGridViewColumnDividerDoubleClickEventArgs", + "DataGridViewColumnDividerDoubleClickEventHandler", + "DataGridViewColumnEventArgs", + "DataGridViewColumnEventHandler", + "DataGridViewColumnHeaderCell", + "DataGridViewColumnHeadersHeightSizeMode", + "DataGridViewColumnSortMode", + "DataGridViewColumnStateChangedEventArgs", + "DataGridViewColumnStateChangedEventHandler", + "DataGridViewComboBoxCell", + "DataGridViewComboBoxColumn", + "DataGridViewComboBoxDisplayStyle", + "DataGridViewComboBoxEditingControl", + "DataGridViewContentAlignment", + "DataGridViewDataErrorContexts", + "DataGridViewDataErrorEventArgs", + "DataGridViewDataErrorEventHandler", + "DataGridViewEditMode", + "DataGridViewEditingControlShowingEventArgs", + "DataGridViewEditingControlShowingEventHandler", + "DataGridViewElement", + "DataGridViewElementStates", + "DataGridViewHeaderBorderStyle", + "DataGridViewHeaderCell", + "DataGridViewHitTestType", + "DataGridViewImageCell", + "DataGridViewImageCellLayout", + "DataGridViewImageColumn", + "DataGridViewLinkCell", + "DataGridViewLinkColumn", + "DataGridViewPaintParts", + "DataGridViewRow", + "DataGridViewRowCancelEventArgs", + "DataGridViewRowCancelEventHandler", + "DataGridViewRowCollection", + "DataGridViewRowContextMenuStripNeededEventArgs", + "DataGridViewRowContextMenuStripNeededEventHandler", + "DataGridViewRowDividerDoubleClickEventArgs", + "DataGridViewRowDividerDoubleClickEventHandler", + "DataGridViewRowErrorTextNeededEventArgs", + "DataGridViewRowErrorTextNeededEventHandler", + "DataGridViewRowEventArgs", + "DataGridViewRowEventHandler", + "DataGridViewRowHeaderCell", + "DataGridViewRowHeadersWidthSizeMode", + "DataGridViewRowHeightInfoNeededEventArgs", + "DataGridViewRowHeightInfoNeededEventHandler", + "DataGridViewRowHeightInfoPushedEventArgs", + "DataGridViewRowHeightInfoPushedEventHandler", + "DataGridViewRowPostPaintEventArgs", + "DataGridViewRowPostPaintEventHandler", + "DataGridViewRowPrePaintEventArgs", + "DataGridViewRowPrePaintEventHandler", + "DataGridViewRowStateChangedEventArgs", + "DataGridViewRowStateChangedEventHandler", + "DataGridViewRowsAddedEventArgs", + "DataGridViewRowsAddedEventHandler", + "DataGridViewRowsRemovedEventArgs", + "DataGridViewRowsRemovedEventHandler", + "DataGridViewSelectedCellCollection", + "DataGridViewSelectedColumnCollection", + "DataGridViewSelectedRowCollection", + "DataGridViewSelectionMode", + "DataGridViewSortCompareEventArgs", + "DataGridViewSortCompareEventHandler", + "DataGridViewTextBoxCell", + "DataGridViewTextBoxColumn", + "DataGridViewTextBoxEditingControl", + "DataGridViewTopLeftHeaderCell", + "DataGridViewTriState", + "DataObject", + "DataSourceUpdateMode", + "DateBoldEventArgs", + "DateBoldEventHandler", + "DateRangeEventArgs", + "DateRangeEventHandler", + "DateTimePicker", + "DateTimePickerFormat", + "Day", + "DialogResult", + "DockStyle", + "DockingAttribute", + "DockingBehavior", + "DomainUpDown", + "DragAction", + "DragDropEffects", + "DragEventArgs", + "DragEventHandler", + "DrawItemEventArgs", + "DrawItemEventHandler", + "DrawItemState", + "DrawListViewColumnHeaderEventArgs", + "DrawListViewColumnHeaderEventHandler", + "DrawListViewItemEventArgs", + "DrawListViewItemEventHandler", + "DrawListViewSubItemEventArgs", + "DrawListViewSubItemEventHandler", + "DrawMode", + "DrawToolTipEventArgs", + "DrawToolTipEventHandler", + "DrawTreeNodeEventArgs", + "DrawTreeNodeEventHandler", + "ErrorBlinkStyle", + "ErrorIconAlignment", + "ErrorProvider", + "FeatureSupport", + "FileDialog", + "FixedPanel", + "FlatButtonAppearance", + "FlatStyle", + "FlowDirection", + "FlowLayoutPanel", + "FlowLayoutSettings", + "FolderBrowserDialog", + "FontDialog", + "Form", + "FormBorderStyle", + "FormClosedEventArgs", + "FormClosedEventHandler", + "FormClosingEventArgs", + "FormClosingEventHandler", + "FormCollection", + "FormStartPosition", + "FormWindowState", + "FrameStyle", + "GetChildAtPointSkip", + "GiveFeedbackEventArgs", + "GiveFeedbackEventHandler", + "GridColumnStylesCollection", + "GridItem", + "GridItemCollection", + "GridItemType", + "GridTableStylesCollection", + "GridTablesFactory", + "GroupBox", + "GroupBoxRenderer", + "HScrollBar", + "HScrollProperties", + "HandledMouseEventArgs", + "Help", + "HelpEventArgs", + "HelpEventHandler", + "HelpNavigator", + "HelpProvider", + "HorizontalAlignment", + "HtmlDocument", + "HtmlElement", + "HtmlElementCollection", + "HtmlElementErrorEventArgs", + "HtmlElementErrorEventHandler", + "HtmlElementEventArgs", + "HtmlElementEventHandler", + "HtmlElementInsertionOrientation", + "HtmlHistory", + "HtmlWindow", + "HtmlWindowCollection", + "IBindableComponent", + "IButtonControl", + "ICommandExecutor", + "IComponentEditorPageSite", + "IContainerControl", + "ICurrencyManagerProvider", + "IDataGridColumnStyleEditingNotificationService", + "IDataGridEditingService", + "IDataGridViewEditingCell", + "IDataGridViewEditingControl", + "IDataObject", + "IDropTarget", + "IFeatureSupport", + "IFileReaderService", + "IMessageFilter", + "IWin32Window", + "IWindowTarget", + "ImageIndexConverter", + "ImageKeyConverter", + "ImageLayout", + "ImageList", + "ImageListStreamer", + "ImeMode", + "InputLanguage", + "InputLanguageChangedEventArgs", + "InputLanguageChangedEventHandler", + "InputLanguageChangingEventArgs", + "InputLanguageChangingEventHandler", + "InputLanguageCollection", + "InsertKeyMode", + "InvalidateEventArgs", + "InvalidateEventHandler", + "ItemActivation", + "ItemBoundsPortion", + "ItemChangedEventArgs", + "ItemChangedEventHandler", + "ItemCheckEventArgs", + "ItemCheckEventHandler", + "ItemCheckedEventArgs", + "ItemCheckedEventHandler", + "ItemDragEventArgs", + "ItemDragEventHandler", + "KeyEventArgs", + "KeyEventHandler", + "KeyPressEventArgs", + "KeyPressEventHandler", + "Keys", + "KeysConverter", + "Label", + "LabelEditEventArgs", + "LabelEditEventHandler", + "LayoutEventArgs", + "LayoutEventHandler", + "LayoutSettings", + "LeftRightAlignment", + "LinkArea", + "LinkBehavior", + "LinkClickedEventArgs", + "LinkClickedEventHandler", + "LinkConverter", + "LinkLabel", + "LinkLabelLinkClickedEventArgs", + "LinkLabelLinkClickedEventHandler", + "LinkState", + "ListBindingConverter", + "ListBindingHelper", + "ListBox", + "ListControl", + "ListControlConvertEventArgs", + "ListControlConvertEventHandler", + "ListView", + "ListViewAlignment", + "ListViewGroup", + "ListViewGroupCollection", + "ListViewHitTestInfo", + "ListViewHitTestLocations", + "ListViewInsertionMark", + "ListViewItem", + "ListViewItemConverter", + "ListViewItemMouseHoverEventArgs", + "ListViewItemMouseHoverEventHandler", + "ListViewItemSelectionChangedEventArgs", + "ListViewItemSelectionChangedEventHandler", + "ListViewItemStates", + "ListViewVirtualItemsSelectionRangeChangedEventArgs", + "ListViewVirtualItemsSelectionRangeChangedEventHandler", + "MainMenu", + "MaskFormat", + "MaskInputRejectedEventArgs", + "MaskInputRejectedEventHandler", + "MaskedTextBox", + "MdiClient", + "MdiLayout", + "MeasureItemEventArgs", + "MeasureItemEventHandler", + "Menu", + "MenuGlyph", + "MenuItem", + "MenuMerge", + "MenuStrip", + "MergeAction", + "Message", + "MessageBox", + "MessageBoxButtons", + "MessageBoxDefaultButton", + "MessageBoxIcon", + "MessageBoxOptions", + "MethodInvoker", + "MonthCalendar", + "MouseButtons", + "MouseEventArgs", + "MouseEventHandler", + "NativeWindow", + "NavigateEventArgs", + "NavigateEventHandler", + "NodeLabelEditEventArgs", + "NodeLabelEditEventHandler", + "NotifyIcon", + "NumericUpDown", + "NumericUpDownAcceleration", + "NumericUpDownAccelerationCollection", + "OSFeature", + "OpacityConverter", + "OpenFileDialog", + "Orientation", + "OwnerDrawPropertyBag", + "Padding", + "PaddingConverter", + "PageSetupDialog", + "PaintEventArgs", + "PaintEventHandler", + "Panel", + "PictureBox", + "PictureBoxSizeMode", + "PopupEventArgs", + "PopupEventHandler", + "PowerLineStatus", + "PowerState", + "PowerStatus", + "PreProcessControlState", + "PreviewKeyDownEventArgs", + "PreviewKeyDownEventHandler", + "PrintControllerWithStatusDialog", + "PrintDialog", + "PrintPreviewControl", + "PrintPreviewDialog", + "ProfessionalColorTable", + "ProfessionalColors", + "ProgressBar", + "ProgressBarRenderer", + "ProgressBarStyle", + "PropertyGrid", + "PropertyManager", + "PropertySort", + "PropertyTabChangedEventArgs", + "PropertyTabChangedEventHandler", + "PropertyValueChangedEventArgs", + "PropertyValueChangedEventHandler", + "QueryAccessibilityHelpEventArgs", + "QueryAccessibilityHelpEventHandler", + "QueryContinueDragEventArgs", + "QueryContinueDragEventHandler", + "QuestionEventArgs", + "QuestionEventHandler", + "RadioButton", + "RadioButtonRenderer", + "RelatedImageListAttribute", + "RetrieveVirtualItemEventArgs", + "RetrieveVirtualItemEventHandler", + "RichTextBox", + "RichTextBoxFinds", + "RichTextBoxLanguageOptions", + "RichTextBoxScrollBars", + "RichTextBoxSelectionAttribute", + "RichTextBoxSelectionTypes", + "RichTextBoxStreamType", + "RichTextBoxWordPunctuations", + "RightToLeft", + "RowStyle", + "SaveFileDialog", + "Screen", + "ScreenOrientation", + "ScrollBar", + "ScrollBarRenderer", + "ScrollBars", + "ScrollButton", + "ScrollEventArgs", + "ScrollEventHandler", + "ScrollEventType", + "ScrollOrientation", + "ScrollProperties", + "ScrollableControl", + "SearchDirectionHint", + "SearchForVirtualItemEventArgs", + "SearchForVirtualItemEventHandler", + "SecurityIDType", + "SelectedGridItemChangedEventArgs", + "SelectedGridItemChangedEventHandler", + "SelectionMode", + "SelectionRange", + "SelectionRangeConverter", + "SendKeys", + "Shortcut", + "SizeGripStyle", + "SizeType", + "SortOrder", + "SplitContainer", + "Splitter", + "SplitterCancelEventArgs", + "SplitterCancelEventHandler", + "SplitterEventArgs", + "SplitterEventHandler", + "SplitterPanel", + "StatusBar", + "StatusBarDrawItemEventArgs", + "StatusBarDrawItemEventHandler", + "StatusBarPanel", + "StatusBarPanelAutoSize", + "StatusBarPanelBorderStyle", + "StatusBarPanelClickEventArgs", + "StatusBarPanelClickEventHandler", + "StatusBarPanelStyle", + "StatusStrip", + "StructFormat", + "SystemInformation", + "SystemParameter", + "TabAlignment", + "TabAppearance", + "TabControl", + "TabControlAction", + "TabControlCancelEventArgs", + "TabControlCancelEventHandler", + "TabControlEventArgs", + "TabControlEventHandler", + "TabDrawMode", + "TabPage", + "TabRenderer", + "TabSizeMode", + "TableLayoutCellPaintEventArgs", + "TableLayoutCellPaintEventHandler", + "TableLayoutColumnStyleCollection", + "TableLayoutControlCollection", + "TableLayoutPanel", + "TableLayoutPanelCellBorderStyle", + "TableLayoutPanelCellPosition", + "TableLayoutPanelGrowStyle", + "TableLayoutRowStyleCollection", + "TableLayoutSettings", + "TableLayoutStyle", + "TableLayoutStyleCollection", + "TextBox", + "TextBoxBase", + "TextBoxRenderer", + "TextDataFormat", + "TextFormatFlags", + "TextImageRelation", + "TextRenderer", + "ThreadExceptionDialog", + "TickStyle", + "Timer", + "ToolBar", + "ToolBarAppearance", + "ToolBarButton", + "ToolBarButtonClickEventArgs", + "ToolBarButtonClickEventHandler", + "ToolBarButtonStyle", + "ToolBarTextAlign", + "ToolStrip", + "ToolStripArrowRenderEventArgs", + "ToolStripArrowRenderEventHandler", + "ToolStripButton", + "ToolStripComboBox", + "ToolStripContainer", + "ToolStripContentPanel", + "ToolStripContentPanelRenderEventArgs", + "ToolStripContentPanelRenderEventHandler", + "ToolStripControlHost", + "ToolStripDropDown", + "ToolStripDropDownButton", + "ToolStripDropDownCloseReason", + "ToolStripDropDownClosedEventArgs", + "ToolStripDropDownClosedEventHandler", + "ToolStripDropDownClosingEventArgs", + "ToolStripDropDownClosingEventHandler", + "ToolStripDropDownDirection", + "ToolStripDropDownItem", + "ToolStripDropDownItemAccessibleObject", + "ToolStripDropDownMenu", + "ToolStripGripDisplayStyle", + "ToolStripGripRenderEventArgs", + "ToolStripGripRenderEventHandler", + "ToolStripGripStyle", + "ToolStripItem", + "ToolStripItemAlignment", + "ToolStripItemClickedEventArgs", + "ToolStripItemClickedEventHandler", + "ToolStripItemCollection", + "ToolStripItemDisplayStyle", + "ToolStripItemEventArgs", + "ToolStripItemEventHandler", + "ToolStripItemImageRenderEventArgs", + "ToolStripItemImageRenderEventHandler", + "ToolStripItemImageScaling", + "ToolStripItemOverflow", + "ToolStripItemPlacement", + "ToolStripItemRenderEventArgs", + "ToolStripItemRenderEventHandler", + "ToolStripItemTextRenderEventArgs", + "ToolStripItemTextRenderEventHandler", + "ToolStripLabel", + "ToolStripLayoutStyle", + "ToolStripManager", + "ToolStripManagerRenderMode", + "ToolStripMenuItem", + "ToolStripOverflow", + "ToolStripOverflowButton", + "ToolStripPanel", + "ToolStripPanelRenderEventArgs", + "ToolStripPanelRenderEventHandler", + "ToolStripPanelRow", + "ToolStripProfessionalRenderer", + "ToolStripProgressBar", + "ToolStripRenderEventArgs", + "ToolStripRenderEventHandler", + "ToolStripRenderMode", + "ToolStripRenderer", + "ToolStripSeparator", + "ToolStripSeparatorRenderEventArgs", + "ToolStripSeparatorRenderEventHandler", + "ToolStripSplitButton", + "ToolStripStatusLabel", + "ToolStripStatusLabelBorderSides", + "ToolStripSystemRenderer", + "ToolStripTextBox", + "ToolStripTextDirection", + "ToolTip", + "ToolTipIcon", + "TrackBar", + "TrackBarRenderer", + "TreeNode", + "TreeNodeCollection", + "TreeNodeConverter", + "TreeNodeMouseClickEventArgs", + "TreeNodeMouseClickEventHandler", + "TreeNodeMouseHoverEventArgs", + "TreeNodeMouseHoverEventHandler", + "TreeNodeStates", + "TreeView", + "TreeViewAction", + "TreeViewCancelEventArgs", + "TreeViewCancelEventHandler", + "TreeViewDrawMode", + "TreeViewEventArgs", + "TreeViewEventHandler", + "TreeViewHitTestInfo", + "TreeViewHitTestLocations", + "TreeViewImageIndexConverter", + "TreeViewImageKeyConverter", + "TypeValidationEventArgs", + "TypeValidationEventHandler", + "UICues", + "UICuesEventArgs", + "UICuesEventHandler", + "UnhandledExceptionMode", + "UpDownBase", + "UpDownEventArgs", + "UpDownEventHandler", + "UserControl", + "VScrollBar", + "VScrollProperties", + "ValidationConstraints", + "View", + "WebBrowser", + "WebBrowserBase", + "WebBrowserDocumentCompletedEventArgs", + "WebBrowserDocumentCompletedEventHandler", + "WebBrowserEncryptionLevel", + "WebBrowserNavigatedEventArgs", + "WebBrowserNavigatedEventHandler", + "WebBrowserNavigatingEventArgs", + "WebBrowserNavigatingEventHandler", + "WebBrowserProgressChangedEventArgs", + "WebBrowserProgressChangedEventHandler", + "WebBrowserReadyState", + "WebBrowserRefreshOption", + "WebBrowserSiteBase", + "WindowsFormsSection", + "WindowsFormsSynchronizationContext", + }, + + new string[3] { // System.Windows.Forms.ComponentModel.Com2Interop + "Com2Variant", + "ICom2PropertyPageDisplayService", + "IComPropertyBrowser", + }, + + new string[9] { // System.Windows.Forms.Design + "ComponentEditorForm", + "ComponentEditorPage", + "EventsTab", + "IUIService", + "IWindowsFormsEditorService", + "PropertyTab", + "ToolStripItemDesignerAvailability", + "ToolStripItemDesignerAvailabilityAttribute", + "WindowsFormsComponentEditor", + }, + + new string[3] { // System.Windows.Forms.Layout + "ArrangedElementCollection", + "LayoutEngine", + "TableLayoutSettingsTypeConverter", + }, + + new string[3] { // System.Windows.Forms.PropertyGridInternal + "IRootGridEntry", + "PropertiesTab", + "PropertyGridCommands", + }, + + new string[49] { // System.Windows.Forms.VisualStyles + "BackgroundType", + "BooleanProperty", + "BorderType", + "CheckBoxState", + "ColorProperty", + "ComboBoxState", + "ContentAlignment", + "EdgeEffects", + "EdgeStyle", + "Edges", + "EnumProperty", + "FilenameProperty", + "FillType", + "FontProperty", + "GlyphFontSizingType", + "GlyphType", + "GroupBoxState", + "HitTestCode", + "HitTestOptions", + "HorizontalAlign", + "IconEffect", + "ImageOrientation", + "ImageSelectType", + "IntegerProperty", + "MarginProperty", + "OffsetType", + "PointProperty", + "PushButtonState", + "RadioButtonState", + "ScrollBarArrowButtonState", + "ScrollBarSizeBoxState", + "ScrollBarState", + "SizingType", + "StringProperty", + "TabItemState", + "TextBoxState", + "TextMetrics", + "TextMetricsCharacterSet", + "TextMetricsPitchAndFamilyValues", + "TextShadowType", + "ThemeSizeType", + "ToolBarState", + "TrackBarThumbState", + "TrueSizeScalingType", + "VerticalAlignment", + "VisualStyleElement", + "VisualStyleInformation", + "VisualStyleRenderer", + "VisualStyleState", + }, + + }; + TypeName [] orcasTypes = { + new TypeName("System.Windows.Forms", "FileDialogCustomPlace"), + new TypeName("System.Windows.Forms", "FileDialogCustomPlacesCollection"), + }; + + return GetTypeNames(namespaces, types, orcasTypes); + } + + static IEnumerable Get_SystemXml_TypeNames() { + // System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + // Total number of types = 255 + + string [] namespaces = new string[7] { + "System.Xml", + "System.Xml.Schema", + "System.Xml.Serialization", + "System.Xml.Serialization.Advanced", + "System.Xml.Serialization.Configuration", + "System.Xml.XPath", + "System.Xml.Xsl", + }; + + string [][] types = new string[7][] { + + new string[61] { // System.Xml + "ConformanceLevel", + "EntityHandling", + "Formatting", + "IHasXmlNode", + "IXmlLineInfo", + "IXmlNamespaceResolver", + "NameTable", + "NewLineHandling", + "ReadState", + "ValidationType", + "WhitespaceHandling", + "WriteState", + "XmlAttribute", + "XmlAttributeCollection", + "XmlCDataSection", + "XmlCharacterData", + "XmlComment", + "XmlConvert", + "XmlDateTimeSerializationMode", + "XmlDeclaration", + "XmlDocument", + "XmlDocumentFragment", + "XmlDocumentType", + "XmlElement", + "XmlEntity", + "XmlEntityReference", + "XmlException", + "XmlImplementation", + "XmlLinkedNode", + "XmlNameTable", + "XmlNamedNodeMap", + "XmlNamespaceManager", + "XmlNamespaceScope", + "XmlNode", + "XmlNodeChangedAction", + "XmlNodeChangedEventArgs", + "XmlNodeChangedEventHandler", + "XmlNodeList", + "XmlNodeOrder", + "XmlNodeReader", + "XmlNodeType", + "XmlNotation", + "XmlOutputMethod", + "XmlParserContext", + "XmlProcessingInstruction", + "XmlQualifiedName", + "XmlReader", + "XmlReaderSettings", + "XmlResolver", + "XmlSecureResolver", + "XmlSignificantWhitespace", + "XmlSpace", + "XmlText", + "XmlTextReader", + "XmlTextWriter", + "XmlTokenizedType", + "XmlUrlResolver", + "XmlValidatingReader", + "XmlWhitespace", + "XmlWriter", + "XmlWriterSettings", + }, + + new string[87] { // System.Xml.Schema + "IXmlSchemaInfo", + "ValidationEventArgs", + "ValidationEventHandler", + "XmlAtomicValue", + "XmlSchema", + "XmlSchemaAll", + "XmlSchemaAnnotated", + "XmlSchemaAnnotation", + "XmlSchemaAny", + "XmlSchemaAnyAttribute", + "XmlSchemaAppInfo", + "XmlSchemaAttribute", + "XmlSchemaAttributeGroup", + "XmlSchemaAttributeGroupRef", + "XmlSchemaChoice", + "XmlSchemaCollection", + "XmlSchemaCollectionEnumerator", + "XmlSchemaCompilationSettings", + "XmlSchemaComplexContent", + "XmlSchemaComplexContentExtension", + "XmlSchemaComplexContentRestriction", + "XmlSchemaComplexType", + "XmlSchemaContent", + "XmlSchemaContentModel", + "XmlSchemaContentProcessing", + "XmlSchemaContentType", + "XmlSchemaDatatype", + "XmlSchemaDatatypeVariety", + "XmlSchemaDerivationMethod", + "XmlSchemaDocumentation", + "XmlSchemaElement", + "XmlSchemaEnumerationFacet", + "XmlSchemaException", + "XmlSchemaExternal", + "XmlSchemaFacet", + "XmlSchemaForm", + "XmlSchemaFractionDigitsFacet", + "XmlSchemaGroup", + "XmlSchemaGroupBase", + "XmlSchemaGroupRef", + "XmlSchemaIdentityConstraint", + "XmlSchemaImport", + "XmlSchemaInclude", + "XmlSchemaInference", + "XmlSchemaInferenceException", + "XmlSchemaInfo", + "XmlSchemaKey", + "XmlSchemaKeyref", + "XmlSchemaLengthFacet", + "XmlSchemaMaxExclusiveFacet", + "XmlSchemaMaxInclusiveFacet", + "XmlSchemaMaxLengthFacet", + "XmlSchemaMinExclusiveFacet", + "XmlSchemaMinInclusiveFacet", + "XmlSchemaMinLengthFacet", + "XmlSchemaNotation", + "XmlSchemaNumericFacet", + "XmlSchemaObject", + "XmlSchemaObjectCollection", + "XmlSchemaObjectEnumerator", + "XmlSchemaObjectTable", + "XmlSchemaParticle", + "XmlSchemaPatternFacet", + "XmlSchemaRedefine", + "XmlSchemaSequence", + "XmlSchemaSet", + "XmlSchemaSimpleContent", + "XmlSchemaSimpleContentExtension", + "XmlSchemaSimpleContentRestriction", + "XmlSchemaSimpleType", + "XmlSchemaSimpleTypeContent", + "XmlSchemaSimpleTypeList", + "XmlSchemaSimpleTypeRestriction", + "XmlSchemaSimpleTypeUnion", + "XmlSchemaTotalDigitsFacet", + "XmlSchemaType", + "XmlSchemaUnique", + "XmlSchemaUse", + "XmlSchemaValidationException", + "XmlSchemaValidationFlags", + "XmlSchemaValidator", + "XmlSchemaValidity", + "XmlSchemaWhiteSpaceFacet", + "XmlSchemaXPath", + "XmlSeverityType", + "XmlTypeCode", + "XmlValueGetter", + }, + + new string[75] { // System.Xml.Serialization + "CodeExporter", + "CodeGenerationOptions", + "CodeIdentifier", + "CodeIdentifiers", + "IXmlSerializable", + "IXmlTextParser", + "ImportContext", + "SchemaImporter", + "SoapAttributeAttribute", + "SoapAttributeOverrides", + "SoapAttributes", + "SoapCodeExporter", + "SoapElementAttribute", + "SoapEnumAttribute", + "SoapIgnoreAttribute", + "SoapIncludeAttribute", + "SoapReflectionImporter", + "SoapSchemaExporter", + "SoapSchemaImporter", + "SoapSchemaMember", + "SoapTypeAttribute", + "UnreferencedObjectEventArgs", + "UnreferencedObjectEventHandler", + "XmlAnyAttributeAttribute", + "XmlAnyElementAttribute", + "XmlAnyElementAttributes", + "XmlArrayAttribute", + "XmlArrayItemAttribute", + "XmlArrayItemAttributes", + "XmlAttributeAttribute", + "XmlAttributeEventArgs", + "XmlAttributeEventHandler", + "XmlAttributeOverrides", + "XmlAttributes", + "XmlChoiceIdentifierAttribute", + "XmlCodeExporter", + "XmlDeserializationEvents", + "XmlElementAttribute", + "XmlElementAttributes", + "XmlElementEventArgs", + "XmlElementEventHandler", + "XmlEnumAttribute", + "XmlIgnoreAttribute", + "XmlIncludeAttribute", + "XmlMapping", + "XmlMappingAccess", + "XmlMemberMapping", + "XmlMembersMapping", + "XmlNamespaceDeclarationsAttribute", + "XmlNodeEventArgs", + "XmlNodeEventHandler", + "XmlReflectionImporter", + "XmlReflectionMember", + "XmlRootAttribute", + "XmlSchemaEnumerator", + "XmlSchemaExporter", + "XmlSchemaImporter", + "XmlSchemaProviderAttribute", + "XmlSchemas", + "XmlSerializationCollectionFixupCallback", + "XmlSerializationFixupCallback", + "XmlSerializationGeneratedCode", + "XmlSerializationReadCallback", + "XmlSerializationReader", + "XmlSerializationWriteCallback", + "XmlSerializationWriter", + "XmlSerializer", + "XmlSerializerAssemblyAttribute", + "XmlSerializerFactory", + "XmlSerializerImplementation", + "XmlSerializerNamespaces", + "XmlSerializerVersionAttribute", + "XmlTextAttribute", + "XmlTypeAttribute", + "XmlTypeMapping", + }, + + new string[2] { // System.Xml.Serialization.Advanced + "SchemaImporterExtension", + "SchemaImporterExtensionCollection", + }, + + new string[6] { // System.Xml.Serialization.Configuration + "DateTimeSerializationSection", + "SchemaImporterExtensionElement", + "SchemaImporterExtensionElementCollection", + "SchemaImporterExtensionsSection", + "SerializationSectionGroup", + "XmlSerializerSection", + }, + + new string[13] { // System.Xml.XPath + "IXPathNavigable", + "XPathDocument", + "XPathException", + "XPathExpression", + "XPathItem", + "XPathNamespaceScope", + "XPathNavigator", + "XPathNodeIterator", + "XPathNodeType", + "XPathResultType", + "XmlCaseOrder", + "XmlDataType", + "XmlSortOrder", + }, + + new string[11] { // System.Xml.Xsl + "IXsltContextFunction", + "IXsltContextVariable", + "XslCompiledTransform", + "XslTransform", + "XsltArgumentList", + "XsltCompileException", + "XsltContext", + "XsltException", + "XsltMessageEncounteredEventArgs", + "XsltMessageEncounteredEventHandler", + "XsltSettings", + }, + + }; + TypeName [] orcasTypes = { + new TypeName("System.Xml.Serialization.Configuration", "RootedPathValidator"), + }; + + return GetTypeNames(namespaces, types, orcasTypes); + } + + + // *** END GENERATED CODE *** + + #endregion + +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/BaseSymbolDictionary.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/BaseSymbolDictionary.cs new file mode 100644 index 0000000000..0367bf0176 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/BaseSymbolDictionary.cs @@ -0,0 +1,96 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Runtime { + + /// + /// Base class for SymbolId dictionaries. + /// + /// SymbolId dictionaries are fast dictionaries used for looking up members of classes, + /// function environments, function locals, and other places which are typically indexed by + /// string names. + /// + /// SymbolId dictionaries support both keying by SymbolId (the common case) and object keys + /// (supporting late bound access to the dictionary as a normal Dictionary<object, object> + /// when exposed directly to user code). When indexed by objects null is a valid value for the + /// key. + /// + public abstract class BaseSymbolDictionary : IValueEquality { + private static readonly object _nullObject = new object(); + private const int ObjectKeysId = -2; + internal static readonly SymbolId ObjectKeys = new SymbolId(ObjectKeysId); + + /// + /// Creates a new SymbolIdDictBase from the specified creating context which will be + /// used for comparisons. + /// + protected BaseSymbolDictionary() { + } + + #region IValueEquality Members + + public int GetValueHashCode() { + throw Error.DictionaryNotHashable(); + } + + public virtual bool ValueEquals(object other) { + if (Object.ReferenceEquals(this, other)) return true; + + IAttributesCollection oth = other as IAttributesCollection; + IAttributesCollection ths = this as IAttributesCollection; + if (oth == null) return false; + + if (oth.Count != ths.Count) return false; + + foreach (KeyValuePair o in ths) { + object res; + if (!oth.TryGetObjectValue(o.Key, out res)) + return false; + + IValueEquality ve = res as IValueEquality; + if(ve != null) { + if(!ve.ValueEquals(o.Value)) return false; + } else if ((ve = (o.Value as IValueEquality))!= null) { + if(!ve.Equals(res)) return false; + } else if(res != null) { + if(!res.Equals(o.Value)) return false; + } else if(o.Value != null) { + if(!o.Value.Equals(res)) return false; + } // else both null and are equal + } + return true; + } + + #endregion + + public static object NullToObj(object o) { + if (o == null) return _nullObject; + return o; + } + + public static object ObjToNull(object o) { + if (o == _nullObject) return null; + return o; + } + + public static bool IsNullObject(object o) { + return o == _nullObject; + } + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/BinderOps.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/BinderOps.cs new file mode 100644 index 0000000000..2c3979e131 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/BinderOps.cs @@ -0,0 +1,349 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Dynamic.Binders; +using System.Text; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + /// + /// Helper methods that calls are generated to from the default DLR binders. + /// + public static class BinderOps { + + #region CreateDelegate support + + /// Table of dynamicly generated delegates which are shared based upon method signature. + private static readonly Publisher _dynamicDelegateCache = new Publisher(); + + public static T CreateDelegate(LanguageContext context, object callable) { + return (T)(object)GetDelegate(context, callable, typeof(T)); + } + + /// + /// Creates a delegate with a given signature that could be used to invoke this object from non-dynamic code (w/o code context). + /// A stub is created that makes appropriate conversions/boxing and calls the object. + /// The stub should be executed within a context of this object's language. + /// + /// The delegate or a null reference if the object is not callable. + public static Delegate GetDelegate(LanguageContext context, object callableObject, Type delegateType) { + ContractUtils.RequiresNotNull(context, "context"); + ContractUtils.RequiresNotNull(delegateType, "delegateType"); + + Delegate result = callableObject as Delegate; + if (result != null) { + if (!delegateType.IsAssignableFrom(result.GetType())) { + throw ScriptingRuntimeHelpers.SimpleTypeError(String.Format("Cannot cast {0} to {1}.", result.GetType(), delegateType)); + } + + return result; + } + + IDynamicObject dynamicObject = callableObject as IDynamicObject; + if (dynamicObject != null) { + + MethodInfo invoke; + + if (!typeof(Delegate).IsAssignableFrom(delegateType) || (invoke = delegateType.GetMethod("Invoke")) == null) { + throw ScriptingRuntimeHelpers.SimpleTypeError("A specific delegate type is required."); + } + + ParameterInfo[] parameters = invoke.GetParameters(); + DelegateSignatureInfo signatureInfo = new DelegateSignatureInfo( + context, + invoke.ReturnType, + parameters + ); + + DelegateInfo delegateInfo = _dynamicDelegateCache.GetOrCreateValue(signatureInfo, + delegate() { + // creation code + return signatureInfo.GenerateDelegateStub(); + }); + + + result = delegateInfo.CreateDelegate(delegateType, dynamicObject); + if (result != null) { + return result; + } + } + + throw ScriptingRuntimeHelpers.SimpleTypeError("Object is not callable."); + } + + #endregion + + /// + /// Helper function to combine an object array with a sequence of additional parameters that has been splatted for a function call. + /// + public static object[] GetCombinedParameters(object[] initialArgs, object additionalArgs) { + IList listArgs = additionalArgs as IList; + if (listArgs == null) { + IEnumerable ie = additionalArgs as IEnumerable; + if (ie == null) { + throw new InvalidOperationException("args must be iterable"); + } + listArgs = new List(); + foreach (object o in ie) { + listArgs.Add(o); + } + } + + object[] res = new object[initialArgs.Length + listArgs.Count]; + Array.Copy(initialArgs, res, initialArgs.Length); + listArgs.CopyTo(res, initialArgs.Length); + return res; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "2#")] // TODO: fix + public static object[] GetCombinedKeywordParameters(object[] initialArgs, IAttributesCollection additionalArgs, ref string[] extraNames) { + List args = new List(initialArgs); + List newNames = extraNames == null ? new List(additionalArgs.Count) : new List(extraNames); + foreach(KeyValuePair kvp in additionalArgs) { + if (kvp.Key is string) { + newNames.Add((string)kvp.Key); + args.Add(kvp.Value); + } + } + extraNames = newNames.ToArray(); + return args.ToArray(); + } + + public static SymbolDictionary MakeSymbolDictionary(SymbolId[] names, object[] values) { + SymbolDictionary res = new SymbolDictionary(); + for (int i = 0; i < names.Length; i++) { + ((IAttributesCollection)res)[names[i]] = values[i]; + } + return res; + } + + #region Event support + + public static EventTracker EventTrackerInPlaceAdd(CodeContext context, EventTracker self, T target) { + MethodInfo add = self.Event.GetAddMethod(context.LanguageContext.DomainManager.Configuration.PrivateBinding); + add.Invoke(null, new object[] { target }); + return self; + } + + public static EventTracker EventTrackerInPlaceRemove(CodeContext context, EventTracker self, T target) { + MethodInfo remove = self.Event.GetRemoveMethod(context.LanguageContext.DomainManager.Configuration.PrivateBinding); + remove.Invoke(null, new object[] { target }); + return self; + } + + public static BoundMemberTracker BoundEventTrackerInPlaceAdd(CodeContext context, BoundMemberTracker self, T target) { + if (self.BoundTo.MemberType == TrackerTypes.Event) { + EventTracker et = (EventTracker)self.BoundTo; + + MethodInfo add = et.Event.GetAddMethod(context.LanguageContext.DomainManager.Configuration.PrivateBinding); + add.Invoke(self.ObjectInstance, new object[] { target }); + return self; + } + throw new InvalidOperationException(); + } + + public static BoundMemberTracker BoundEventTrackerInPlaceRemove(CodeContext context, BoundMemberTracker self, T target) { + if (self.BoundTo.MemberType == TrackerTypes.Event) { + EventTracker et = (EventTracker)self.BoundTo; + + MethodInfo remove = et.Event.GetRemoveMethod(context.LanguageContext.DomainManager.Configuration.PrivateBinding); + remove.Invoke(self.ObjectInstance, new object[] { target }); + return self; + } + throw new InvalidOperationException(); + } + + #endregion + + public static ArgumentTypeException BadArgumentsForOperation(Operators op, params object[] args) { + StringBuilder message = new StringBuilder("unsupported operand type(s) for operation "); + message.Append(op.ToString()); + message.Append(": "); + string comma = ""; + + foreach (object o in args) { + message.Append(comma); + message.Append(CompilerHelpers.GetType(o)); + comma = ", "; + } + + throw new ArgumentTypeException(message.ToString()); + } + + + // formalNormalArgumentCount - does not include FuncDefFlags.ArgList and FuncDefFlags.KwDict + // defaultArgumentCount - How many arguments in the method declaration have a default value? + // providedArgumentCount - How many arguments are passed in at the call site? + // hasArgList - Is the method declaration of the form "foo(*argList)"? + // keywordArgumentsProvided - Does the call site specify keyword arguments? + public static ArgumentTypeException TypeErrorForIncorrectArgumentCount( + string methodName, + int formalNormalArgumentCount, + int defaultArgumentCount, + int providedArgumentCount, + bool hasArgList, + bool keywordArgumentsProvided) { + return TypeErrorForIncorrectArgumentCount(methodName, formalNormalArgumentCount, formalNormalArgumentCount, defaultArgumentCount, providedArgumentCount, hasArgList, keywordArgumentsProvided); + } + + public static ArgumentTypeException TypeErrorForIncorrectArgumentCount( + string methodName, + int minFormalNormalArgumentCount, + int maxFormalNormalArgumentCount, + int defaultArgumentCount, + int providedArgumentCount, + bool hasArgList, + bool keywordArgumentsProvided) { + + int formalCount; + string formalCountQualifier; + string nonKeyword = keywordArgumentsProvided ? "non-keyword " : ""; + + if (defaultArgumentCount > 0 || hasArgList || minFormalNormalArgumentCount != maxFormalNormalArgumentCount) { + if (providedArgumentCount < minFormalNormalArgumentCount || maxFormalNormalArgumentCount == Int32.MaxValue) { + formalCountQualifier = "at least"; + formalCount = minFormalNormalArgumentCount - defaultArgumentCount; + } else { + formalCountQualifier = "at most"; + formalCount = maxFormalNormalArgumentCount; + } + } else if (minFormalNormalArgumentCount == 0) { + return ScriptingRuntimeHelpers.SimpleTypeError(string.Format("{0}() takes no arguments ({1} given)", methodName, providedArgumentCount)); + } else { + formalCountQualifier = "exactly"; + formalCount = minFormalNormalArgumentCount; + } + + return new ArgumentTypeException(string.Format( + "{0}() takes {1} {2} {3}argument{4} ({5} given)", + methodName, // 0 + formalCountQualifier, // 1 + formalCount, // 2 + nonKeyword, // 3 + formalCount == 1 ? "" : "s", // 4 + providedArgumentCount)); // 5 + } + + public static ArgumentTypeException TypeErrorForIncorrectArgumentCount(string name, int formalNormalArgumentCount, int defaultArgumentCount, int providedArgumentCount) { + return TypeErrorForIncorrectArgumentCount(name, formalNormalArgumentCount, defaultArgumentCount, providedArgumentCount, false, false); + } + + public static ArgumentTypeException TypeErrorForIncorrectArgumentCount(string name, int expected, int received) { + return TypeErrorForIncorrectArgumentCount(name, expected, 0, received); + } + + public static ArgumentTypeException TypeErrorForExtraKeywordArgument(string name, string argumentName) { + return new ArgumentTypeException(String.Format("{0}() got an unexpected keyword argument '{1}'", name, argumentName)); + } + + public static ArgumentTypeException TypeErrorForDuplicateKeywordArgument(string name, string argumentName) { + return new ArgumentTypeException(String.Format("{0}() got multiple values for keyword argument '{1}'", name, argumentName)); + } + + public static ArgumentTypeException SimpleTypeError(string message) { + return new ArgumentTypeException(message); + } + + public static object InvokeMethod(MethodBase mb, object obj, object[] args) { + try { + return mb.Invoke(obj, args); + } catch (TargetInvocationException tie) { + throw tie.InnerException; + } + } + + public static object InvokeConstructor(ConstructorInfo ci, object[] args) { + try { + return ci.Invoke(args); + } catch (TargetInvocationException tie) { + throw tie.InnerException; + } + } + + // TODO: just emit this in the generated code + public static bool CheckDictionaryMembers(IDictionary dict, string[] names, Type[] types) { + if (dict.Count != names.Length) return false; + + for (int i = 0; i < names.Length; i++) { + string name = names[i]; + + if (!dict.Contains(name)) { + return false; + } + + if (types != null) { + if (CompilerHelpers.GetType(dict[name]) != types[i]) { + return false; + } + } + } + return true; + } + + public static IList GetStringMembers(IList members) { + List res = new List(); + foreach (object o in members) { + string str = o as string; + if (str != null) { + res.Add(str); + } + } + return res; + } + + /// + /// EventInfo.EventHandlerType getter is marked SecuritySafeCritical in CoreCLR + /// This method is to get to the property without using Reflection + /// + /// + /// + public static Type GetEventHandlerType(EventInfo eventInfo) { + ContractUtils.RequiresNotNull(eventInfo, "eventInfo"); + return eventInfo.EventHandlerType; + } + + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // TODO: fix + public static void SetEvent(EventTracker eventTracker, object value) { + EventTracker et = value as EventTracker; + if (et != null) { + if (et != eventTracker) { + throw new ArgumentException(String.Format("expected event from {0}.{1}, got event from {2}.{3}", + eventTracker.DeclaringType.Name, + eventTracker.Name, + et.DeclaringType.Name, + et.Name)); + } + return; + } + + BoundMemberTracker bmt = value as BoundMemberTracker; + if (bmt == null) throw new ArgumentTypeException("expected bound event, got " + CompilerHelpers.GetType(value).Name); + if (bmt.BoundTo.MemberType != TrackerTypes.Event) throw new ArgumentTypeException("expected bound event, got " + bmt.BoundTo.MemberType.ToString()); + + if (bmt.BoundTo != eventTracker) throw new ArgumentException(String.Format("expected event from {0}.{1}, got event from {2}.{3}", + eventTracker.DeclaringType.Name, + eventTracker.Name, + bmt.BoundTo.DeclaringType.Name, + bmt.BoundTo.Name)); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/BinderType.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/BinderType.cs new file mode 100644 index 0000000000..f4f1adf311 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/BinderType.cs @@ -0,0 +1,35 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Runtime { + public enum BinderType { + /// + /// The MethodBinder will perform normal method binding. + /// + Normal, + /// + /// The MethodBinder will return the languages definition of NotImplemented if the arguments are + /// incompatible with the signature. + /// + BinaryOperator, + ComparisonOperator, + /// + /// The MethodBinder will set properties/fields for unused keyword arguments on the instance + /// that gets returned from the method. + /// + Constructor + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/CallTargets.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/CallTargets.cs new file mode 100644 index 0000000000..651406e941 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/CallTargets.cs @@ -0,0 +1,24 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Runtime { + /// + /// The delegate representing the DLR Main function + /// + // TODO: remove in favor of Func + public delegate object DlrMainCallTarget(Scope scope, LanguageContext context); +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/CallTypes.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/CallTypes.cs new file mode 100644 index 0000000000..ccd61f8fb9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/CallTypes.cs @@ -0,0 +1,24 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Runtime { + [Flags] + public enum CallTypes { + None = 0, + ImplicitInstance, + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/Cast.Generated.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/Cast.Generated.cs new file mode 100644 index 0000000000..5673d830a4 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/Cast.Generated.cs @@ -0,0 +1,1685 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.CodeDom.Compiler; +using System.Diagnostics; + +namespace Microsoft.Scripting.Runtime { + /// + /// Implements explicit casts supported by the runtime. + /// + [GeneratedCode("DLR", "2.0")] + public static partial class Cast { + + #region Generated Type Casts + + // *** BEGIN GENERATED CODE *** + // generated by function: generate_type_casts from: generate_casts.py + + public static Boolean ExplicitCastToBoolean(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == BooleanType) { + return (Boolean)(Boolean)o; + } else if (type == NullableBooleanType) { + return (Boolean)(Nullable)o; + } + } + throw InvalidCast(o, "Boolean"); + } + + public static Byte ExplicitCastToByte(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == Int32Type) { + return (Byte)(Int32)o; + } else if (type == DoubleType) { + return (Byte)(Double)o; + } else if (type == Int64Type) { + return (Byte)(Int64)o; + } else if (type == Int16Type) { + return (Byte)(Int16)o; + } else if (type == UInt32Type) { + return (Byte)(UInt32)o; + } else if (type == UInt64Type) { + return (Byte)(UInt64)o; + } else if (type == UInt16Type) { + return (Byte)(UInt16)o; + } else if (type == SByteType) { + return (Byte)(SByte)o; + } else if (type == ByteType) { + return (Byte)(Byte)o; + } else if (type == SingleType) { + return (Byte)(Single)o; + } else if (type == CharType) { + return (Byte)(Char)o; + } else if (type == DecimalType) { + return (Byte)(Decimal)o; + } else if (type.IsEnum) { + return (Byte)ExplicitCastEnumToByte(o); + } else if (type == NullableInt32Type) { + return (Byte)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Byte)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Byte)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Byte)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Byte)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Byte)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Byte)(Nullable)o; + } else if (type == NullableSByteType) { + return (Byte)(Nullable)o; + } else if (type == NullableByteType) { + return (Byte)(Nullable)o; + } else if (type == NullableSingleType) { + return (Byte)(Nullable)o; + } else if (type == NullableCharType) { + return (Byte)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Byte)(Nullable)o; + } + } + throw InvalidCast(o, "Byte"); + } + + public static Char ExplicitCastToChar(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == Int32Type) { + return (Char)(Int32)o; + } else if (type == DoubleType) { + return (Char)(Double)o; + } else if (type == Int64Type) { + return (Char)(Int64)o; + } else if (type == Int16Type) { + return (Char)(Int16)o; + } else if (type == UInt32Type) { + return (Char)(UInt32)o; + } else if (type == UInt64Type) { + return (Char)(UInt64)o; + } else if (type == UInt16Type) { + return (Char)(UInt16)o; + } else if (type == SByteType) { + return (Char)(SByte)o; + } else if (type == ByteType) { + return (Char)(Byte)o; + } else if (type == SingleType) { + return (Char)(Single)o; + } else if (type == CharType) { + return (Char)(Char)o; + } else if (type == DecimalType) { + return (Char)(Decimal)o; + } else if (type.IsEnum) { + return (Char)ExplicitCastEnumToInt32(o); + } else if (type == NullableInt32Type) { + return (Char)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Char)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Char)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Char)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Char)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Char)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Char)(Nullable)o; + } else if (type == NullableSByteType) { + return (Char)(Nullable)o; + } else if (type == NullableByteType) { + return (Char)(Nullable)o; + } else if (type == NullableSingleType) { + return (Char)(Nullable)o; + } else if (type == NullableCharType) { + return (Char)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Char)(Nullable)o; + } + } + throw InvalidCast(o, "Char"); + } + + public static Decimal ExplicitCastToDecimal(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == Int32Type) { + return (Decimal)(Int32)o; + } else if (type == DoubleType) { + return (Decimal)(Double)o; + } else if (type == Int64Type) { + return (Decimal)(Int64)o; + } else if (type == Int16Type) { + return (Decimal)(Int16)o; + } else if (type == UInt32Type) { + return (Decimal)(UInt32)o; + } else if (type == UInt64Type) { + return (Decimal)(UInt64)o; + } else if (type == UInt16Type) { + return (Decimal)(UInt16)o; + } else if (type == SByteType) { + return (Decimal)(SByte)o; + } else if (type == ByteType) { + return (Decimal)(Byte)o; + } else if (type == SingleType) { + return (Decimal)(Single)o; + } else if (type == CharType) { + return (Decimal)(Char)o; + } else if (type == DecimalType) { + return (Decimal)(Decimal)o; + } else if (type.IsEnum) { + return (Decimal)ExplicitCastEnumToInt64(o); + } else if (type == NullableInt32Type) { + return (Decimal)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Decimal)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Decimal)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Decimal)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Decimal)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Decimal)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Decimal)(Nullable)o; + } else if (type == NullableSByteType) { + return (Decimal)(Nullable)o; + } else if (type == NullableByteType) { + return (Decimal)(Nullable)o; + } else if (type == NullableSingleType) { + return (Decimal)(Nullable)o; + } else if (type == NullableCharType) { + return (Decimal)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Decimal)(Nullable)o; + } + } + throw InvalidCast(o, "Decimal"); + } + + public static Double ExplicitCastToDouble(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == Int32Type) { + return (Double)(Int32)o; + } else if (type == DoubleType) { + return (Double)(Double)o; + } else if (type == Int64Type) { + return (Double)(Int64)o; + } else if (type == Int16Type) { + return (Double)(Int16)o; + } else if (type == UInt32Type) { + return (Double)(UInt32)o; + } else if (type == UInt64Type) { + return (Double)(UInt64)o; + } else if (type == UInt16Type) { + return (Double)(UInt16)o; + } else if (type == SByteType) { + return (Double)(SByte)o; + } else if (type == ByteType) { + return (Double)(Byte)o; + } else if (type == SingleType) { + return (Double)(Single)o; + } else if (type == CharType) { + return (Double)(Char)o; + } else if (type == DecimalType) { + return (Double)(Decimal)o; + } else if (type.IsEnum) { + return (Double)ExplicitCastEnumToInt64(o); + } else if (type == NullableInt32Type) { + return (Double)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Double)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Double)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Double)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Double)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Double)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Double)(Nullable)o; + } else if (type == NullableSByteType) { + return (Double)(Nullable)o; + } else if (type == NullableByteType) { + return (Double)(Nullable)o; + } else if (type == NullableSingleType) { + return (Double)(Nullable)o; + } else if (type == NullableCharType) { + return (Double)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Double)(Nullable)o; + } + } + throw InvalidCast(o, "Double"); + } + + public static Int16 ExplicitCastToInt16(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == Int32Type) { + return (Int16)(Int32)o; + } else if (type == DoubleType) { + return (Int16)(Double)o; + } else if (type == Int64Type) { + return (Int16)(Int64)o; + } else if (type == Int16Type) { + return (Int16)(Int16)o; + } else if (type == UInt32Type) { + return (Int16)(UInt32)o; + } else if (type == UInt64Type) { + return (Int16)(UInt64)o; + } else if (type == UInt16Type) { + return (Int16)(UInt16)o; + } else if (type == SByteType) { + return (Int16)(SByte)o; + } else if (type == ByteType) { + return (Int16)(Byte)o; + } else if (type == SingleType) { + return (Int16)(Single)o; + } else if (type == CharType) { + return (Int16)(Char)o; + } else if (type == DecimalType) { + return (Int16)(Decimal)o; + } else if (type.IsEnum) { + return (Int16)ExplicitCastEnumToInt16(o); + } else if (type == NullableInt32Type) { + return (Int16)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Int16)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Int16)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Int16)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Int16)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Int16)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Int16)(Nullable)o; + } else if (type == NullableSByteType) { + return (Int16)(Nullable)o; + } else if (type == NullableByteType) { + return (Int16)(Nullable)o; + } else if (type == NullableSingleType) { + return (Int16)(Nullable)o; + } else if (type == NullableCharType) { + return (Int16)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Int16)(Nullable)o; + } + } + throw InvalidCast(o, "Int16"); + } + + public static Int32 ExplicitCastToInt32(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == Int32Type) { + return (Int32)(Int32)o; + } else if (type == DoubleType) { + return (Int32)(Double)o; + } else if (type == Int64Type) { + return (Int32)(Int64)o; + } else if (type == Int16Type) { + return (Int32)(Int16)o; + } else if (type == UInt32Type) { + return (Int32)(UInt32)o; + } else if (type == UInt64Type) { + return (Int32)(UInt64)o; + } else if (type == UInt16Type) { + return (Int32)(UInt16)o; + } else if (type == SByteType) { + return (Int32)(SByte)o; + } else if (type == ByteType) { + return (Int32)(Byte)o; + } else if (type == SingleType) { + return (Int32)(Single)o; + } else if (type == CharType) { + return (Int32)(Char)o; + } else if (type == DecimalType) { + return (Int32)(Decimal)o; + } else if (type.IsEnum) { + return (Int32)ExplicitCastEnumToInt32(o); + } else if (type == NullableInt32Type) { + return (Int32)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Int32)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Int32)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Int32)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Int32)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Int32)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Int32)(Nullable)o; + } else if (type == NullableSByteType) { + return (Int32)(Nullable)o; + } else if (type == NullableByteType) { + return (Int32)(Nullable)o; + } else if (type == NullableSingleType) { + return (Int32)(Nullable)o; + } else if (type == NullableCharType) { + return (Int32)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Int32)(Nullable)o; + } + } + throw InvalidCast(o, "Int32"); + } + + public static Int64 ExplicitCastToInt64(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == Int32Type) { + return (Int64)(Int32)o; + } else if (type == DoubleType) { + return (Int64)(Double)o; + } else if (type == Int64Type) { + return (Int64)(Int64)o; + } else if (type == Int16Type) { + return (Int64)(Int16)o; + } else if (type == UInt32Type) { + return (Int64)(UInt32)o; + } else if (type == UInt64Type) { + return (Int64)(UInt64)o; + } else if (type == UInt16Type) { + return (Int64)(UInt16)o; + } else if (type == SByteType) { + return (Int64)(SByte)o; + } else if (type == ByteType) { + return (Int64)(Byte)o; + } else if (type == SingleType) { + return (Int64)(Single)o; + } else if (type == CharType) { + return (Int64)(Char)o; + } else if (type == DecimalType) { + return (Int64)(Decimal)o; + } else if (type.IsEnum) { + return (Int64)ExplicitCastEnumToInt64(o); + } else if (type == NullableInt32Type) { + return (Int64)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Int64)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Int64)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Int64)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Int64)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Int64)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Int64)(Nullable)o; + } else if (type == NullableSByteType) { + return (Int64)(Nullable)o; + } else if (type == NullableByteType) { + return (Int64)(Nullable)o; + } else if (type == NullableSingleType) { + return (Int64)(Nullable)o; + } else if (type == NullableCharType) { + return (Int64)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Int64)(Nullable)o; + } + } + throw InvalidCast(o, "Int64"); + } + + [CLSCompliant(false)] + public static SByte ExplicitCastToSByte(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == Int32Type) { + return (SByte)(Int32)o; + } else if (type == DoubleType) { + return (SByte)(Double)o; + } else if (type == Int64Type) { + return (SByte)(Int64)o; + } else if (type == Int16Type) { + return (SByte)(Int16)o; + } else if (type == UInt32Type) { + return (SByte)(UInt32)o; + } else if (type == UInt64Type) { + return (SByte)(UInt64)o; + } else if (type == UInt16Type) { + return (SByte)(UInt16)o; + } else if (type == SByteType) { + return (SByte)(SByte)o; + } else if (type == ByteType) { + return (SByte)(Byte)o; + } else if (type == SingleType) { + return (SByte)(Single)o; + } else if (type == CharType) { + return (SByte)(Char)o; + } else if (type == DecimalType) { + return (SByte)(Decimal)o; + } else if (type.IsEnum) { + return (SByte)ExplicitCastEnumToSByte(o); + } else if (type == NullableInt32Type) { + return (SByte)(Nullable)o; + } else if (type == NullableDoubleType) { + return (SByte)(Nullable)o; + } else if (type == NullableInt64Type) { + return (SByte)(Nullable)o; + } else if (type == NullableInt16Type) { + return (SByte)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (SByte)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (SByte)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (SByte)(Nullable)o; + } else if (type == NullableSByteType) { + return (SByte)(Nullable)o; + } else if (type == NullableByteType) { + return (SByte)(Nullable)o; + } else if (type == NullableSingleType) { + return (SByte)(Nullable)o; + } else if (type == NullableCharType) { + return (SByte)(Nullable)o; + } else if (type == NullableDecimalType) { + return (SByte)(Nullable)o; + } + } + throw InvalidCast(o, "SByte"); + } + + public static Single ExplicitCastToSingle(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == Int32Type) { + return (Single)(Int32)o; + } else if (type == DoubleType) { + return (Single)(Double)o; + } else if (type == Int64Type) { + return (Single)(Int64)o; + } else if (type == Int16Type) { + return (Single)(Int16)o; + } else if (type == UInt32Type) { + return (Single)(UInt32)o; + } else if (type == UInt64Type) { + return (Single)(UInt64)o; + } else if (type == UInt16Type) { + return (Single)(UInt16)o; + } else if (type == SByteType) { + return (Single)(SByte)o; + } else if (type == ByteType) { + return (Single)(Byte)o; + } else if (type == SingleType) { + return (Single)(Single)o; + } else if (type == CharType) { + return (Single)(Char)o; + } else if (type == DecimalType) { + return (Single)(Decimal)o; + } else if (type.IsEnum) { + return (Single)ExplicitCastEnumToInt64(o); + } else if (type == NullableInt32Type) { + return (Single)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Single)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Single)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Single)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Single)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Single)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Single)(Nullable)o; + } else if (type == NullableSByteType) { + return (Single)(Nullable)o; + } else if (type == NullableByteType) { + return (Single)(Nullable)o; + } else if (type == NullableSingleType) { + return (Single)(Nullable)o; + } else if (type == NullableCharType) { + return (Single)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Single)(Nullable)o; + } + } + throw InvalidCast(o, "Single"); + } + + [CLSCompliant(false)] + public static UInt16 ExplicitCastToUInt16(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == Int32Type) { + return (UInt16)(Int32)o; + } else if (type == DoubleType) { + return (UInt16)(Double)o; + } else if (type == Int64Type) { + return (UInt16)(Int64)o; + } else if (type == Int16Type) { + return (UInt16)(Int16)o; + } else if (type == UInt32Type) { + return (UInt16)(UInt32)o; + } else if (type == UInt64Type) { + return (UInt16)(UInt64)o; + } else if (type == UInt16Type) { + return (UInt16)(UInt16)o; + } else if (type == SByteType) { + return (UInt16)(SByte)o; + } else if (type == ByteType) { + return (UInt16)(Byte)o; + } else if (type == SingleType) { + return (UInt16)(Single)o; + } else if (type == CharType) { + return (UInt16)(Char)o; + } else if (type == DecimalType) { + return (UInt16)(Decimal)o; + } else if (type.IsEnum) { + return (UInt16)ExplicitCastEnumToUInt16(o); + } else if (type == NullableInt32Type) { + return (UInt16)(Nullable)o; + } else if (type == NullableDoubleType) { + return (UInt16)(Nullable)o; + } else if (type == NullableInt64Type) { + return (UInt16)(Nullable)o; + } else if (type == NullableInt16Type) { + return (UInt16)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (UInt16)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (UInt16)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (UInt16)(Nullable)o; + } else if (type == NullableSByteType) { + return (UInt16)(Nullable)o; + } else if (type == NullableByteType) { + return (UInt16)(Nullable)o; + } else if (type == NullableSingleType) { + return (UInt16)(Nullable)o; + } else if (type == NullableCharType) { + return (UInt16)(Nullable)o; + } else if (type == NullableDecimalType) { + return (UInt16)(Nullable)o; + } + } + throw InvalidCast(o, "UInt16"); + } + + [CLSCompliant(false)] + public static UInt32 ExplicitCastToUInt32(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == Int32Type) { + return (UInt32)(Int32)o; + } else if (type == DoubleType) { + return (UInt32)(Double)o; + } else if (type == Int64Type) { + return (UInt32)(Int64)o; + } else if (type == Int16Type) { + return (UInt32)(Int16)o; + } else if (type == UInt32Type) { + return (UInt32)(UInt32)o; + } else if (type == UInt64Type) { + return (UInt32)(UInt64)o; + } else if (type == UInt16Type) { + return (UInt32)(UInt16)o; + } else if (type == SByteType) { + return (UInt32)(SByte)o; + } else if (type == ByteType) { + return (UInt32)(Byte)o; + } else if (type == SingleType) { + return (UInt32)(Single)o; + } else if (type == CharType) { + return (UInt32)(Char)o; + } else if (type == DecimalType) { + return (UInt32)(Decimal)o; + } else if (type.IsEnum) { + return (UInt32)ExplicitCastEnumToUInt32(o); + } else if (type == NullableInt32Type) { + return (UInt32)(Nullable)o; + } else if (type == NullableDoubleType) { + return (UInt32)(Nullable)o; + } else if (type == NullableInt64Type) { + return (UInt32)(Nullable)o; + } else if (type == NullableInt16Type) { + return (UInt32)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (UInt32)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (UInt32)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (UInt32)(Nullable)o; + } else if (type == NullableSByteType) { + return (UInt32)(Nullable)o; + } else if (type == NullableByteType) { + return (UInt32)(Nullable)o; + } else if (type == NullableSingleType) { + return (UInt32)(Nullable)o; + } else if (type == NullableCharType) { + return (UInt32)(Nullable)o; + } else if (type == NullableDecimalType) { + return (UInt32)(Nullable)o; + } + } + throw InvalidCast(o, "UInt32"); + } + + [CLSCompliant(false)] + public static UInt64 ExplicitCastToUInt64(object o) { + if (o != null) { + Type type = o.GetType(); + if (type == Int32Type) { + return (UInt64)(Int32)o; + } else if (type == DoubleType) { + return (UInt64)(Double)o; + } else if (type == Int64Type) { + return (UInt64)(Int64)o; + } else if (type == Int16Type) { + return (UInt64)(Int16)o; + } else if (type == UInt32Type) { + return (UInt64)(UInt32)o; + } else if (type == UInt64Type) { + return (UInt64)(UInt64)o; + } else if (type == UInt16Type) { + return (UInt64)(UInt16)o; + } else if (type == SByteType) { + return (UInt64)(SByte)o; + } else if (type == ByteType) { + return (UInt64)(Byte)o; + } else if (type == SingleType) { + return (UInt64)(Single)o; + } else if (type == CharType) { + return (UInt64)(Char)o; + } else if (type == DecimalType) { + return (UInt64)(Decimal)o; + } else if (type.IsEnum) { + return (UInt64)ExplicitCastEnumToUInt64(o); + } else if (type == NullableInt32Type) { + return (UInt64)(Nullable)o; + } else if (type == NullableDoubleType) { + return (UInt64)(Nullable)o; + } else if (type == NullableInt64Type) { + return (UInt64)(Nullable)o; + } else if (type == NullableInt16Type) { + return (UInt64)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (UInt64)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (UInt64)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (UInt64)(Nullable)o; + } else if (type == NullableSByteType) { + return (UInt64)(Nullable)o; + } else if (type == NullableByteType) { + return (UInt64)(Nullable)o; + } else if (type == NullableSingleType) { + return (UInt64)(Nullable)o; + } else if (type == NullableCharType) { + return (UInt64)(Nullable)o; + } else if (type == NullableDecimalType) { + return (UInt64)(Nullable)o; + } + } + throw InvalidCast(o, "UInt64"); + } + + public static Nullable ExplicitCastToNullableBoolean(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == BooleanType) { + return (Nullable)(Boolean)o; + } else if (type == NullableBooleanType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "Boolean"); + } + + public static Nullable ExplicitCastToNullableByte(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == Int32Type) { + return (Nullable)(Int32)o; + } else if (type == DoubleType) { + return (Nullable)(Double)o; + } else if (type == Int64Type) { + return (Nullable)(Int64)o; + } else if (type == Int16Type) { + return (Nullable)(Int16)o; + } else if (type == UInt32Type) { + return (Nullable)(UInt32)o; + } else if (type == UInt64Type) { + return (Nullable)(UInt64)o; + } else if (type == UInt16Type) { + return (Nullable)(UInt16)o; + } else if (type == SByteType) { + return (Nullable)(SByte)o; + } else if (type == ByteType) { + return (Nullable)(Byte)o; + } else if (type == SingleType) { + return (Nullable)(Single)o; + } else if (type == CharType) { + return (Nullable)(Char)o; + } else if (type == DecimalType) { + return (Nullable)(Decimal)o; + } else if (type.IsEnum) { + return (Nullable)ExplicitCastEnumToByte(o); + } else if (type == NullableInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableSByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableSingleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableCharType) { + return (Nullable)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "Byte"); + } + + public static Nullable ExplicitCastToNullableChar(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == Int32Type) { + return (Nullable)(Int32)o; + } else if (type == DoubleType) { + return (Nullable)(Double)o; + } else if (type == Int64Type) { + return (Nullable)(Int64)o; + } else if (type == Int16Type) { + return (Nullable)(Int16)o; + } else if (type == UInt32Type) { + return (Nullable)(UInt32)o; + } else if (type == UInt64Type) { + return (Nullable)(UInt64)o; + } else if (type == UInt16Type) { + return (Nullable)(UInt16)o; + } else if (type == SByteType) { + return (Nullable)(SByte)o; + } else if (type == ByteType) { + return (Nullable)(Byte)o; + } else if (type == SingleType) { + return (Nullable)(Single)o; + } else if (type == CharType) { + return (Nullable)(Char)o; + } else if (type == DecimalType) { + return (Nullable)(Decimal)o; + } else if (type.IsEnum) { + return (Nullable)ExplicitCastEnumToInt32(o); + } else if (type == NullableInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableSByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableSingleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableCharType) { + return (Nullable)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "Char"); + } + + public static Nullable ExplicitCastToNullableDecimal(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == Int32Type) { + return (Nullable)(Int32)o; + } else if (type == DoubleType) { + return (Nullable)(Double)o; + } else if (type == Int64Type) { + return (Nullable)(Int64)o; + } else if (type == Int16Type) { + return (Nullable)(Int16)o; + } else if (type == UInt32Type) { + return (Nullable)(UInt32)o; + } else if (type == UInt64Type) { + return (Nullable)(UInt64)o; + } else if (type == UInt16Type) { + return (Nullable)(UInt16)o; + } else if (type == SByteType) { + return (Nullable)(SByte)o; + } else if (type == ByteType) { + return (Nullable)(Byte)o; + } else if (type == SingleType) { + return (Nullable)(Single)o; + } else if (type == CharType) { + return (Nullable)(Char)o; + } else if (type == DecimalType) { + return (Nullable)(Decimal)o; + } else if (type.IsEnum) { + return (Nullable)ExplicitCastEnumToInt64(o); + } else if (type == NullableInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableSByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableSingleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableCharType) { + return (Nullable)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "Decimal"); + } + + public static Nullable ExplicitCastToNullableDouble(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == Int32Type) { + return (Nullable)(Int32)o; + } else if (type == DoubleType) { + return (Nullable)(Double)o; + } else if (type == Int64Type) { + return (Nullable)(Int64)o; + } else if (type == Int16Type) { + return (Nullable)(Int16)o; + } else if (type == UInt32Type) { + return (Nullable)(UInt32)o; + } else if (type == UInt64Type) { + return (Nullable)(UInt64)o; + } else if (type == UInt16Type) { + return (Nullable)(UInt16)o; + } else if (type == SByteType) { + return (Nullable)(SByte)o; + } else if (type == ByteType) { + return (Nullable)(Byte)o; + } else if (type == SingleType) { + return (Nullable)(Single)o; + } else if (type == CharType) { + return (Nullable)(Char)o; + } else if (type == DecimalType) { + return (Nullable)(Decimal)o; + } else if (type.IsEnum) { + return (Nullable)ExplicitCastEnumToInt64(o); + } else if (type == NullableInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableSByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableSingleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableCharType) { + return (Nullable)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "Double"); + } + + public static Nullable ExplicitCastToNullableInt16(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == Int32Type) { + return (Nullable)(Int32)o; + } else if (type == DoubleType) { + return (Nullable)(Double)o; + } else if (type == Int64Type) { + return (Nullable)(Int64)o; + } else if (type == Int16Type) { + return (Nullable)(Int16)o; + } else if (type == UInt32Type) { + return (Nullable)(UInt32)o; + } else if (type == UInt64Type) { + return (Nullable)(UInt64)o; + } else if (type == UInt16Type) { + return (Nullable)(UInt16)o; + } else if (type == SByteType) { + return (Nullable)(SByte)o; + } else if (type == ByteType) { + return (Nullable)(Byte)o; + } else if (type == SingleType) { + return (Nullable)(Single)o; + } else if (type == CharType) { + return (Nullable)(Char)o; + } else if (type == DecimalType) { + return (Nullable)(Decimal)o; + } else if (type.IsEnum) { + return (Nullable)ExplicitCastEnumToInt16(o); + } else if (type == NullableInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableSByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableSingleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableCharType) { + return (Nullable)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "Int16"); + } + + public static Nullable ExplicitCastToNullableInt32(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == Int32Type) { + return (Nullable)(Int32)o; + } else if (type == DoubleType) { + return (Nullable)(Double)o; + } else if (type == Int64Type) { + return (Nullable)(Int64)o; + } else if (type == Int16Type) { + return (Nullable)(Int16)o; + } else if (type == UInt32Type) { + return (Nullable)(UInt32)o; + } else if (type == UInt64Type) { + return (Nullable)(UInt64)o; + } else if (type == UInt16Type) { + return (Nullable)(UInt16)o; + } else if (type == SByteType) { + return (Nullable)(SByte)o; + } else if (type == ByteType) { + return (Nullable)(Byte)o; + } else if (type == SingleType) { + return (Nullable)(Single)o; + } else if (type == CharType) { + return (Nullable)(Char)o; + } else if (type == DecimalType) { + return (Nullable)(Decimal)o; + } else if (type.IsEnum) { + return (Nullable)ExplicitCastEnumToInt32(o); + } else if (type == NullableInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableSByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableSingleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableCharType) { + return (Nullable)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "Int32"); + } + + public static Nullable ExplicitCastToNullableInt64(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == Int32Type) { + return (Nullable)(Int32)o; + } else if (type == DoubleType) { + return (Nullable)(Double)o; + } else if (type == Int64Type) { + return (Nullable)(Int64)o; + } else if (type == Int16Type) { + return (Nullable)(Int16)o; + } else if (type == UInt32Type) { + return (Nullable)(UInt32)o; + } else if (type == UInt64Type) { + return (Nullable)(UInt64)o; + } else if (type == UInt16Type) { + return (Nullable)(UInt16)o; + } else if (type == SByteType) { + return (Nullable)(SByte)o; + } else if (type == ByteType) { + return (Nullable)(Byte)o; + } else if (type == SingleType) { + return (Nullable)(Single)o; + } else if (type == CharType) { + return (Nullable)(Char)o; + } else if (type == DecimalType) { + return (Nullable)(Decimal)o; + } else if (type.IsEnum) { + return (Nullable)ExplicitCastEnumToInt64(o); + } else if (type == NullableInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableSByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableSingleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableCharType) { + return (Nullable)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "Int64"); + } + + [CLSCompliant(false)] + public static Nullable ExplicitCastToNullableSByte(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == Int32Type) { + return (Nullable)(Int32)o; + } else if (type == DoubleType) { + return (Nullable)(Double)o; + } else if (type == Int64Type) { + return (Nullable)(Int64)o; + } else if (type == Int16Type) { + return (Nullable)(Int16)o; + } else if (type == UInt32Type) { + return (Nullable)(UInt32)o; + } else if (type == UInt64Type) { + return (Nullable)(UInt64)o; + } else if (type == UInt16Type) { + return (Nullable)(UInt16)o; + } else if (type == SByteType) { + return (Nullable)(SByte)o; + } else if (type == ByteType) { + return (Nullable)(Byte)o; + } else if (type == SingleType) { + return (Nullable)(Single)o; + } else if (type == CharType) { + return (Nullable)(Char)o; + } else if (type == DecimalType) { + return (Nullable)(Decimal)o; + } else if (type.IsEnum) { + return (Nullable)ExplicitCastEnumToSByte(o); + } else if (type == NullableInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableSByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableSingleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableCharType) { + return (Nullable)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "SByte"); + } + + public static Nullable ExplicitCastToNullableSingle(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == Int32Type) { + return (Nullable)(Int32)o; + } else if (type == DoubleType) { + return (Nullable)(Double)o; + } else if (type == Int64Type) { + return (Nullable)(Int64)o; + } else if (type == Int16Type) { + return (Nullable)(Int16)o; + } else if (type == UInt32Type) { + return (Nullable)(UInt32)o; + } else if (type == UInt64Type) { + return (Nullable)(UInt64)o; + } else if (type == UInt16Type) { + return (Nullable)(UInt16)o; + } else if (type == SByteType) { + return (Nullable)(SByte)o; + } else if (type == ByteType) { + return (Nullable)(Byte)o; + } else if (type == SingleType) { + return (Nullable)(Single)o; + } else if (type == CharType) { + return (Nullable)(Char)o; + } else if (type == DecimalType) { + return (Nullable)(Decimal)o; + } else if (type.IsEnum) { + return (Nullable)ExplicitCastEnumToInt64(o); + } else if (type == NullableInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableSByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableSingleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableCharType) { + return (Nullable)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "Single"); + } + + [CLSCompliant(false)] + public static Nullable ExplicitCastToNullableUInt16(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == Int32Type) { + return (Nullable)(Int32)o; + } else if (type == DoubleType) { + return (Nullable)(Double)o; + } else if (type == Int64Type) { + return (Nullable)(Int64)o; + } else if (type == Int16Type) { + return (Nullable)(Int16)o; + } else if (type == UInt32Type) { + return (Nullable)(UInt32)o; + } else if (type == UInt64Type) { + return (Nullable)(UInt64)o; + } else if (type == UInt16Type) { + return (Nullable)(UInt16)o; + } else if (type == SByteType) { + return (Nullable)(SByte)o; + } else if (type == ByteType) { + return (Nullable)(Byte)o; + } else if (type == SingleType) { + return (Nullable)(Single)o; + } else if (type == CharType) { + return (Nullable)(Char)o; + } else if (type == DecimalType) { + return (Nullable)(Decimal)o; + } else if (type.IsEnum) { + return (Nullable)ExplicitCastEnumToUInt16(o); + } else if (type == NullableInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableSByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableSingleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableCharType) { + return (Nullable)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "UInt16"); + } + + [CLSCompliant(false)] + public static Nullable ExplicitCastToNullableUInt32(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == Int32Type) { + return (Nullable)(Int32)o; + } else if (type == DoubleType) { + return (Nullable)(Double)o; + } else if (type == Int64Type) { + return (Nullable)(Int64)o; + } else if (type == Int16Type) { + return (Nullable)(Int16)o; + } else if (type == UInt32Type) { + return (Nullable)(UInt32)o; + } else if (type == UInt64Type) { + return (Nullable)(UInt64)o; + } else if (type == UInt16Type) { + return (Nullable)(UInt16)o; + } else if (type == SByteType) { + return (Nullable)(SByte)o; + } else if (type == ByteType) { + return (Nullable)(Byte)o; + } else if (type == SingleType) { + return (Nullable)(Single)o; + } else if (type == CharType) { + return (Nullable)(Char)o; + } else if (type == DecimalType) { + return (Nullable)(Decimal)o; + } else if (type.IsEnum) { + return (Nullable)ExplicitCastEnumToUInt32(o); + } else if (type == NullableInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableSByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableSingleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableCharType) { + return (Nullable)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "UInt32"); + } + + [CLSCompliant(false)] + public static Nullable ExplicitCastToNullableUInt64(object o) { + if (o == null) { + return new Nullable(); + } + Type type = o.GetType(); + if (type == Int32Type) { + return (Nullable)(Int32)o; + } else if (type == DoubleType) { + return (Nullable)(Double)o; + } else if (type == Int64Type) { + return (Nullable)(Int64)o; + } else if (type == Int16Type) { + return (Nullable)(Int16)o; + } else if (type == UInt32Type) { + return (Nullable)(UInt32)o; + } else if (type == UInt64Type) { + return (Nullable)(UInt64)o; + } else if (type == UInt16Type) { + return (Nullable)(UInt16)o; + } else if (type == SByteType) { + return (Nullable)(SByte)o; + } else if (type == ByteType) { + return (Nullable)(Byte)o; + } else if (type == SingleType) { + return (Nullable)(Single)o; + } else if (type == CharType) { + return (Nullable)(Char)o; + } else if (type == DecimalType) { + return (Nullable)(Decimal)o; + } else if (type.IsEnum) { + return (Nullable)ExplicitCastEnumToUInt64(o); + } else if (type == NullableInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableDoubleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt32Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt64Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableUInt16Type) { + return (Nullable)(Nullable)o; + } else if (type == NullableSByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableByteType) { + return (Nullable)(Nullable)o; + } else if (type == NullableSingleType) { + return (Nullable)(Nullable)o; + } else if (type == NullableCharType) { + return (Nullable)(Nullable)o; + } else if (type == NullableDecimalType) { + return (Nullable)(Nullable)o; + } + throw InvalidCast(o, "UInt64"); + } + + + // *** END GENERATED CODE *** + + #endregion + + #region Generated Nullable Instance + + // *** BEGIN GENERATED CODE *** + // generated by function: generate_nullable_instance from: generate_casts.py + + public static object NewNullableInstance(Type type) { + if (type == Int32Type) { + return new Nullable(); + } else if (type == DoubleType) { + return new Nullable(); + } else if (type == BooleanType) { + return new Nullable(); + } else if (type == Int64Type) { + return new Nullable(); + } else if (type == Int16Type) { + return new Nullable(); + } else if (type == UInt32Type) { + return new Nullable(); + } else if (type == UInt64Type) { + return new Nullable(); + } else if (type == UInt16Type) { + return new Nullable(); + } else if (type == SByteType) { + return new Nullable(); + } else if (type == ByteType) { + return new Nullable(); + } else if (type == SingleType) { + return new Nullable(); + } else if (type == CharType) { + return new Nullable(); + } else if (type == DecimalType) { + return new Nullable(); + } else { + return NewNullableInstanceSlow(type); + } + } + + // *** END GENERATED CODE *** + + #endregion + + #region Generated Enum Casts + + // *** BEGIN GENERATED CODE *** + // generated by function: generate_enum_casts from: generate_casts.py + + internal static Byte ExplicitCastEnumToByte(object o) { + Debug.Assert(o is Enum); + switch (((Enum)o).GetTypeCode()) { + case TypeCode.Byte: return (Byte)(Byte)o; + case TypeCode.SByte: return (Byte)(SByte)o; + case TypeCode.Int16: return (Byte)(Int16)o; + case TypeCode.UInt16: return (Byte)(UInt16)o; + case TypeCode.Int32: return (Byte)(Int32)o; + case TypeCode.UInt32: return (Byte)(UInt32)o; + case TypeCode.Int64: return (Byte)(Int64)o; + case TypeCode.UInt64: return (Byte)(UInt64)o; + } + throw new InvalidOperationException("Invalid enum"); + } + + internal static SByte ExplicitCastEnumToSByte(object o) { + Debug.Assert(o is Enum); + switch (((Enum)o).GetTypeCode()) { + case TypeCode.Byte: return (SByte)(Byte)o; + case TypeCode.SByte: return (SByte)(SByte)o; + case TypeCode.Int16: return (SByte)(Int16)o; + case TypeCode.UInt16: return (SByte)(UInt16)o; + case TypeCode.Int32: return (SByte)(Int32)o; + case TypeCode.UInt32: return (SByte)(UInt32)o; + case TypeCode.Int64: return (SByte)(Int64)o; + case TypeCode.UInt64: return (SByte)(UInt64)o; + } + throw new InvalidOperationException("Invalid enum"); + } + + internal static Int16 ExplicitCastEnumToInt16(object o) { + Debug.Assert(o is Enum); + switch (((Enum)o).GetTypeCode()) { + case TypeCode.Byte: return (Int16)(Byte)o; + case TypeCode.SByte: return (Int16)(SByte)o; + case TypeCode.Int16: return (Int16)(Int16)o; + case TypeCode.UInt16: return (Int16)(UInt16)o; + case TypeCode.Int32: return (Int16)(Int32)o; + case TypeCode.UInt32: return (Int16)(UInt32)o; + case TypeCode.Int64: return (Int16)(Int64)o; + case TypeCode.UInt64: return (Int16)(UInt64)o; + } + throw new InvalidOperationException("Invalid enum"); + } + + internal static UInt16 ExplicitCastEnumToUInt16(object o) { + Debug.Assert(o is Enum); + switch (((Enum)o).GetTypeCode()) { + case TypeCode.Byte: return (UInt16)(Byte)o; + case TypeCode.SByte: return (UInt16)(SByte)o; + case TypeCode.Int16: return (UInt16)(Int16)o; + case TypeCode.UInt16: return (UInt16)(UInt16)o; + case TypeCode.Int32: return (UInt16)(Int32)o; + case TypeCode.UInt32: return (UInt16)(UInt32)o; + case TypeCode.Int64: return (UInt16)(Int64)o; + case TypeCode.UInt64: return (UInt16)(UInt64)o; + } + throw new InvalidOperationException("Invalid enum"); + } + + internal static Int32 ExplicitCastEnumToInt32(object o) { + Debug.Assert(o is Enum); + switch (((Enum)o).GetTypeCode()) { + case TypeCode.Byte: return (Int32)(Byte)o; + case TypeCode.SByte: return (Int32)(SByte)o; + case TypeCode.Int16: return (Int32)(Int16)o; + case TypeCode.UInt16: return (Int32)(UInt16)o; + case TypeCode.Int32: return (Int32)(Int32)o; + case TypeCode.UInt32: return (Int32)(UInt32)o; + case TypeCode.Int64: return (Int32)(Int64)o; + case TypeCode.UInt64: return (Int32)(UInt64)o; + } + throw new InvalidOperationException("Invalid enum"); + } + + internal static UInt32 ExplicitCastEnumToUInt32(object o) { + Debug.Assert(o is Enum); + switch (((Enum)o).GetTypeCode()) { + case TypeCode.Byte: return (UInt32)(Byte)o; + case TypeCode.SByte: return (UInt32)(SByte)o; + case TypeCode.Int16: return (UInt32)(Int16)o; + case TypeCode.UInt16: return (UInt32)(UInt16)o; + case TypeCode.Int32: return (UInt32)(Int32)o; + case TypeCode.UInt32: return (UInt32)(UInt32)o; + case TypeCode.Int64: return (UInt32)(Int64)o; + case TypeCode.UInt64: return (UInt32)(UInt64)o; + } + throw new InvalidOperationException("Invalid enum"); + } + + internal static Int64 ExplicitCastEnumToInt64(object o) { + Debug.Assert(o is Enum); + switch (((Enum)o).GetTypeCode()) { + case TypeCode.Byte: return (Int64)(Byte)o; + case TypeCode.SByte: return (Int64)(SByte)o; + case TypeCode.Int16: return (Int64)(Int16)o; + case TypeCode.UInt16: return (Int64)(UInt16)o; + case TypeCode.Int32: return (Int64)(Int32)o; + case TypeCode.UInt32: return (Int64)(UInt32)o; + case TypeCode.Int64: return (Int64)(Int64)o; + case TypeCode.UInt64: return (Int64)(UInt64)o; + } + throw new InvalidOperationException("Invalid enum"); + } + + internal static UInt64 ExplicitCastEnumToUInt64(object o) { + Debug.Assert(o is Enum); + switch (((Enum)o).GetTypeCode()) { + case TypeCode.Byte: return (UInt64)(Byte)o; + case TypeCode.SByte: return (UInt64)(SByte)o; + case TypeCode.Int16: return (UInt64)(Int16)o; + case TypeCode.UInt16: return (UInt64)(UInt16)o; + case TypeCode.Int32: return (UInt64)(Int32)o; + case TypeCode.UInt32: return (UInt64)(UInt32)o; + case TypeCode.Int64: return (UInt64)(Int64)o; + case TypeCode.UInt64: return (UInt64)(UInt64)o; + } + throw new InvalidOperationException("Invalid enum"); + } + + + // *** END GENERATED CODE *** + + #endregion + + #region Generated Type Cache + + // *** BEGIN GENERATED CODE *** + // generated by function: generate_type_cache from: generate_casts.py + + internal static readonly Type BooleanType = typeof(Boolean); + internal static readonly Type ByteType = typeof(Byte); + internal static readonly Type CharType = typeof(Char); + internal static readonly Type DecimalType = typeof(Decimal); + internal static readonly Type DoubleType = typeof(Double); + internal static readonly Type Int16Type = typeof(Int16); + internal static readonly Type Int32Type = typeof(Int32); + internal static readonly Type Int64Type = typeof(Int64); + internal static readonly Type ObjectType = typeof(Object); + internal static readonly Type SByteType = typeof(SByte); + internal static readonly Type SingleType = typeof(Single); + internal static readonly Type UInt16Type = typeof(UInt16); + internal static readonly Type UInt32Type = typeof(UInt32); + internal static readonly Type UInt64Type = typeof(UInt64); + + internal static readonly Type NullableBooleanType = typeof(Nullable); + internal static readonly Type NullableByteType = typeof(Nullable); + internal static readonly Type NullableCharType = typeof(Nullable); + internal static readonly Type NullableDecimalType = typeof(Nullable); + internal static readonly Type NullableDoubleType = typeof(Nullable); + internal static readonly Type NullableInt16Type = typeof(Nullable); + internal static readonly Type NullableInt32Type = typeof(Nullable); + internal static readonly Type NullableInt64Type = typeof(Nullable); + internal static readonly Type NullableSByteType = typeof(Nullable); + internal static readonly Type NullableSingleType = typeof(Nullable); + internal static readonly Type NullableUInt16Type = typeof(Nullable); + internal static readonly Type NullableUInt32Type = typeof(Nullable); + internal static readonly Type NullableUInt64Type = typeof(Nullable); + + // *** END GENERATED CODE *** + + #endregion + + internal static readonly Type NullableType = typeof(Nullable<>); + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/Cast.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/Cast.cs new file mode 100644 index 0000000000..d3c266520b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/Cast.cs @@ -0,0 +1,109 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; + +namespace Microsoft.Scripting.Runtime { + /// + /// Implements explicit casts supported by the runtime. + /// + public static partial class Cast { + + /// + /// Explicitly casts the object to a given type (and returns it as object) + /// + public static object Explicit(object o, Type to) { + if (o == null) { + // Null objects can be only cast to Nullable or any reference type + if (to.IsValueType) { + if (to.IsGenericType && to.GetGenericTypeDefinition() == NullableType) { + return NewNullableInstance(to.GetGenericArguments()[0]); + } else if (to == typeof(void)) { + return null; + } else { + throw new InvalidCastException(String.Format("Cannot cast null to a value type {0}", to.Name)); + } + } else { + // Explicit cast to reference type is simply null + return null; + } + } + + if (to.IsValueType) { + return ExplicitCastToValueType(o, to); + } else { + Type type = o.GetType(); + if (to.IsInstanceOfType(o) || to.IsAssignableFrom(type)) { + return o; + } else { + throw new InvalidCastException(String.Format("Cannot cast {0} to {1}", type.Name, to.Name)); + } + } + } + + public static T Explicit(object o) { + return (T)Explicit(o, typeof(T)); + } + + private static object ExplicitCastToValueType(object o, Type to) { + Debug.Assert(o != null); + Debug.Assert(to.IsValueType); + + if (to == Int32Type) return ScriptingRuntimeHelpers.Int32ToObject(ExplicitCastToInt32(o)); + if (to == DoubleType) return ExplicitCastToDouble(o); + if (to == BooleanType) return ScriptingRuntimeHelpers.BooleanToObject(ExplicitCastToBoolean(o)); + if (to == ByteType) return ExplicitCastToByte(o); + if (to == CharType) return ExplicitCastToChar(o); + if (to == DecimalType) return ExplicitCastToDecimal(o); + if (to == Int16Type) return ExplicitCastToInt16(o); + if (to == Int64Type) return ExplicitCastToInt64(o); + if (to == SByteType) return ExplicitCastToSByte(o); + if (to == SingleType) return ExplicitCastToSingle(o); + if (to == UInt16Type) return ExplicitCastToUInt16(o); + if (to == UInt32Type) return ExplicitCastToUInt32(o); + if (to == UInt64Type) return ExplicitCastToUInt64(o); + + if (to == NullableBooleanType) return ExplicitCastToNullableBoolean(o); + if (to == NullableByteType) return ExplicitCastToNullableByte(o); + if (to == NullableCharType) return ExplicitCastToNullableChar(o); + if (to == NullableDecimalType) return ExplicitCastToNullableDecimal(o); + if (to == NullableDoubleType) return ExplicitCastToNullableDouble(o); + if (to == NullableInt16Type) return ExplicitCastToNullableInt16(o); + if (to == NullableInt32Type) return ExplicitCastToNullableInt32(o); + if (to == NullableInt64Type) return ExplicitCastToNullableInt64(o); + if (to == NullableSByteType) return ExplicitCastToNullableSByte(o); + if (to == NullableSingleType) return ExplicitCastToNullableSingle(o); + if (to == NullableUInt16Type) return ExplicitCastToNullableUInt16(o); + if (to == NullableUInt32Type) return ExplicitCastToNullableUInt32(o); + if (to == NullableUInt64Type) return ExplicitCastToNullableUInt64(o); + + if (to.IsAssignableFrom(o.GetType())) { + return o; + } + + throw new InvalidCastException(); + } + + private static object NewNullableInstanceSlow(Type type) { + Type concrete = NullableType.MakeGenericType(type); + return Activator.CreateInstance(concrete); + } + + private static InvalidCastException InvalidCast(object o, string typeName) { + return new InvalidCastException(String.Format("Cannot cast {0} to {1}", o == null ? "(null)" : o.GetType().Name, typeName)); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/CodeContext.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/CodeContext.cs new file mode 100644 index 0000000000..ec27f953f3 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/CodeContext.cs @@ -0,0 +1,70 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + /// + /// TODO: Rename to LocalScope + /// + public class CodeContext { + public const string ContextFieldName = "__global_context"; + + // TODO: move to subclasses + private readonly Scope _scope; + + // TODO: move to subclasses + private readonly LanguageContext _languageContext; + + private readonly CodeContext _parent; + + public CodeContext Parent { + get { return _parent; } + } + + // TODO: remove + public Scope Scope { + get { + return _scope; + } + } + + public virtual Scope GlobalScope { + get { + Debug.Assert(_scope != null, "Global scope not available"); + return _scope.ModuleScope; + } + } + + public LanguageContext LanguageContext { + get { + return _languageContext; + } + } + + public CodeContext(Scope scope, LanguageContext languageContext) + : this(scope, languageContext, null) { + } + + public CodeContext(Scope scope, LanguageContext languageContext, CodeContext parent) { + Assert.NotNull(languageContext); + + _languageContext = languageContext; + _scope = scope; + _parent = parent; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/CodeDomCodeGen.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/CodeDomCodeGen.cs new file mode 100644 index 0000000000..d97c4ff59a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/CodeDomCodeGen.cs @@ -0,0 +1,130 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.CodeDom; +using System.Dynamic; +using Microsoft.Scripting.Utils; + +#if !SILVERLIGHT // CodeDom objects are not available in Silverlight + +namespace Microsoft.Scripting.Runtime { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")] // TODO: fix + public abstract class CodeDomCodeGen { + // This is the key used in the UserData of the CodeDom objects to track + // the source location of the CodeObject in the original source file. + protected static readonly object SourceSpanKey = typeof(SourceSpan); + + // Stores the code as it is generated + private PositionTrackingWriter _writer; + protected PositionTrackingWriter Writer { get { return _writer; } } + + abstract protected void WriteExpressionStatement(CodeExpressionStatement s); + abstract protected void WriteFunctionDefinition(CodeMemberMethod func); + abstract protected string QuoteString(string val); + + public SourceUnit GenerateCode(CodeMemberMethod codeDom, LanguageContext context, string path, SourceCodeKind kind) { + ContractUtils.RequiresNotNull(codeDom, "codeDom"); + ContractUtils.RequiresNotNull(context, "context"); + ContractUtils.Requires(path == null || path.Length > 0, "path"); + + // Convert the CodeDom to source code + if (_writer != null) { + _writer.Close(); + } + _writer = new PositionTrackingWriter(); + + WriteFunctionDefinition(codeDom); + + return CreateSourceUnit(context, path, kind); + } + + private SourceUnit CreateSourceUnit(LanguageContext context, string path, SourceCodeKind kind) { + string code = _writer.ToString(); + SourceUnit src = context.CreateSnippet(code, path, kind); + src.SetLineMapping(_writer.GetLineMap()); + return src; + } + + virtual protected void WriteArgumentReferenceExpression(CodeArgumentReferenceExpression e) { + _writer.Write(e.ParameterName); + } + + virtual protected void WriteSnippetExpression(CodeSnippetExpression e) { + _writer.Write(e.Value); + } + + virtual protected void WriteSnippetStatement(CodeSnippetStatement s) { + _writer.Write(s.Value); + _writer.Write('\n'); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] // TODO: fix + protected void WriteStatement(CodeStatement s) { + // Save statement source location + if (s.LinePragma != null) { + _writer.MapLocation(s.LinePragma); + } + + if (s is CodeExpressionStatement) { + WriteExpressionStatement((CodeExpressionStatement)s); + } else if (s is CodeSnippetStatement) { + WriteSnippetStatement((CodeSnippetStatement)s); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] // TODO: fix + protected void WriteExpression(CodeExpression e) { + if (e is CodeSnippetExpression) { + WriteSnippetExpression((CodeSnippetExpression)e); + } else if (e is CodePrimitiveExpression) { + WritePrimitiveExpression((CodePrimitiveExpression)e); + } else if (e is CodeMethodInvokeExpression) { + WriteCallExpression((CodeMethodInvokeExpression)e); + } else if (e is CodeArgumentReferenceExpression) { + WriteArgumentReferenceExpression((CodeArgumentReferenceExpression)e); + } + } + + protected void WritePrimitiveExpression(CodePrimitiveExpression e) { + object val = e.Value; + + string strVal = val as string; + if (strVal != null) { + _writer.Write(QuoteString(strVal)); + } else { + _writer.Write(val); + } + } + + protected void WriteCallExpression(CodeMethodInvokeExpression m) { + if (m.Method.TargetObject != null) { + WriteExpression(m.Method.TargetObject); + _writer.Write("."); + } + + _writer.Write(m.Method.MethodName); + _writer.Write("("); + for (int i = 0; i < m.Parameters.Count; ++i) { + if (i != 0) { + _writer.Write(","); + } + WriteExpression(m.Parameters[i]); + } + _writer.Write(")"); + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/CompilerContext.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/CompilerContext.cs new file mode 100644 index 0000000000..40857817f2 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/CompilerContext.cs @@ -0,0 +1,82 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + + /// + /// Represents the context that is flowed for doing Compiler. Languages can derive + /// from this class to provide additional contextual information. + /// + public sealed class CompilerContext { + + /// + /// Source unit currently being compiled in the CompilerContext + /// + private readonly SourceUnit _sourceUnit; + + /// + /// Current error sink. + /// + private readonly ErrorSink _errors; + + /// + /// Sink for parser callbacks (e.g. brace matching, etc.). + /// + private readonly ParserSink _parserSink; + + /// + /// Compiler specific options. + /// + private readonly CompilerOptions _options; + + public SourceUnit SourceUnit { + get { + return _sourceUnit; + } + } + + public ParserSink ParserSink { + get { + return _parserSink; + } + } + + public ErrorSink Errors { + get { return _errors; } + } + + public CompilerOptions Options { + get { return _options; } + } + + public CompilerContext(SourceUnit sourceUnit, CompilerOptions options, ErrorSink errorSink) + : this(sourceUnit, options, errorSink, ParserSink.Null) { + } + + public CompilerContext(SourceUnit sourceUnit, CompilerOptions options, ErrorSink errorSink, ParserSink parserSink) { + ContractUtils.RequiresNotNull(sourceUnit, "sourceUnit"); + ContractUtils.RequiresNotNull(errorSink, "errorSink"); + ContractUtils.RequiresNotNull(parserSink, "parserSink"); + ContractUtils.RequiresNotNull(options, "options"); + + _sourceUnit = sourceUnit; + _options = options; + _errors = errorSink; + _parserSink = parserSink; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ContextId.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ContextId.cs new file mode 100644 index 0000000000..2b2df2c639 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ContextId.cs @@ -0,0 +1,109 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Runtime { + /// + /// Represents a language context. Typically there is at most 1 context + /// associated with each language, but some languages may use more than one context + /// to identify code that should be treated differently. Contexts are used during + /// member and operator lookup. + /// + [Serializable] + public struct ContextId : IEquatable { + private int _id; + private static Dictionary _contexts = new Dictionary(); + private static int _maxId = 1; + + public static readonly ContextId Empty = new ContextId(); + + internal ContextId(int id) { + _id = id; + } + + /// + /// Registers a language within the system with the specified name. + /// + public static ContextId RegisterContext(object identifier) { + lock(_contexts) { + ContextId res; + if (_contexts.TryGetValue(identifier, out res)) { + throw Error.LanguageRegistered(); + } + + ContextId id = new ContextId(); + id._id = _maxId++; + + return id; + } + } + + /// + /// Looks up the context ID for the specified context identifier + /// + public static ContextId LookupContext(object identifier) { + ContextId res; + lock (_contexts) { + if (_contexts.TryGetValue(identifier, out res)) { + return res; + } + } + + return ContextId.Empty; + } + + public int Id { + get { + return _id; + } + } + + #region IEquatable Members + + [StateIndependent] + public bool Equals(ContextId other) { + return this._id == other._id; + } + + #endregion + + #region Object overrides + + public override int GetHashCode() { + return _id; + } + + public override bool Equals(object obj) { + if (!(obj is ContextId)) return false; + + ContextId other = (ContextId)obj; + return other._id == _id; + } + + #endregion + + public static bool operator ==(ContextId self, ContextId other) { + return self.Equals(other); + } + + public static bool operator !=(ContextId self, ContextId other) { + return !self.Equals(other); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/CustomSymbolDictionary.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/CustomSymbolDictionary.cs new file mode 100644 index 0000000000..bd72312271 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/CustomSymbolDictionary.cs @@ -0,0 +1,624 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using Microsoft.Contracts; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + + /// + /// Abstract base class used for optimized thread-safe SymbolDictionaries. + /// + /// Implementers derive from this class and override the GetExtraKeys, TrySetExtraValue, + /// and TryGetExtraValue methods. When looking up a value first the extra keys will be + /// searched using the optimized Try*ExtraValue functions. If the value isn't found there + /// then the value is stored in the underlying .NET dictionary. + /// + /// Implementors can optionally override the object key functionality to store object keys + /// using their own mechanism. By default object keys are stored in their own dictionary + /// which is stored in the primary SymbolId dictionary under an invalid symbol id. + /// + public abstract class CustomSymbolDictionary : BaseSymbolDictionary, IDictionary, IDictionary, IAttributesCollection { + private Dictionary _data; + + protected CustomSymbolDictionary() { + } + + /// + /// Gets a list of the extra keys that are cached by the the optimized implementation + /// of the module. + /// + public abstract SymbolId[] GetExtraKeys(); + + /// + /// Try to set the extra value and return true if the specified key was found in the + /// list of extra values. + /// + protected internal abstract bool TrySetExtraValue(SymbolId key, object value); + + /// + /// Try to get the extra value and returns true if the specified key was found in the + /// list of extra values. Returns true even if the value is Uninitialized. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + protected internal abstract bool TryGetExtraValue(SymbolId key, out object value); + + private void InitializeData() { + Debug.Assert(_data == null); + + _data = new Dictionary(); + } + + /// + /// Field dictionaries are usually indexed using literal strings, which is handled using the Symbols. + /// However, Python does allow non-string keys too. We handle this case by lazily creating an object-keyed dictionary, + /// and keeping it in the symbol-indexed dictionary. Such access is slower, which is acceptable. + /// + private Dictionary GetObjectKeysDictionary() { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData == null) { + if (_data == null) InitializeData(); + objData = new Dictionary(); + _data.Add(ObjectKeys, objData); + } + return objData; + } + + private Dictionary GetObjectKeysDictionaryIfExists() { + if (_data == null) return null; + + object objData; + if (_data.TryGetValue(ObjectKeys, out objData)) + return (Dictionary)objData; + return null; + } + + #region IDictionary Members + + void IDictionary.Add(object key, object value) { + Debug.Assert(!(key is SymbolId)); + string strKey = key as string; + if (strKey != null) { + lock (this) { + if (_data == null) InitializeData(); + SymbolId keyId = SymbolTable.StringToId(strKey); + if (TrySetExtraValue(keyId, value)) + return; + _data.Add(keyId, value); + } + } else { + AddObjectKey(key, value); + } + } + + [Confined] + bool IDictionary.ContainsKey(object key) { + Debug.Assert(!(key is SymbolId)); + lock (this) { + object dummy; + return AsObjectKeyedDictionary().TryGetValue(key, out dummy); + } + } + + ICollection IDictionary.Keys { + get { + List res = new List(); + lock (this) if (_data != null) { + foreach (SymbolId x in _data.Keys) { + if (x == ObjectKeys) continue; + res.Add(SymbolTable.IdToString(x)); + } + } + + foreach (SymbolId key in GetExtraKeys()) { + if (key.Id < 0) break; + + object dummy; + if (TryGetExtraValue(key, out dummy) && dummy != Uninitialized.Instance) { + res.Add(SymbolTable.IdToString(key)); + } + } + + GetObjectKeys(res); + return res; + } + } + + bool IDictionary.Remove(object key) { + Debug.Assert(!(key is SymbolId)); + + string strKey = key as string; + if (strKey != null) { + lock (this) { + SymbolId fieldId = SymbolTable.StringToId(strKey); + if (TrySetExtraValue(fieldId, Uninitialized.Instance)) return true; + + if (_data == null) return false; + return _data.Remove(fieldId); + } + } + + return RemoveObjectKey(key); + } + + bool IDictionary.TryGetValue(object key, out object value) { + Debug.Assert(!(key is SymbolId)); + + string strKey = key as string; + if (strKey != null) { + lock (this) { + SymbolId fieldId = SymbolTable.StringToId(strKey); + + if (TryGetExtraValue(fieldId, out value) && value != Uninitialized.Instance) return true; + + if (_data == null) return false; + return _data.TryGetValue(fieldId, out value); + } + } + + return TryGetObjectValue(key, out value); + } + + ICollection IDictionary.Values { + get { + List res = new List(); + lock (this) { + if (_data != null) { + foreach (SymbolId x in _data.Keys) { + if (x == ObjectKeys) continue; + res.Add(_data[x]); + } + } + } + + foreach (SymbolId key in GetExtraKeys()) { + if (key.Id < 0) break; + + object value; + if (TryGetExtraValue(key, out value) && value != Uninitialized.Instance) { + res.Add(value); + } + } + + GetObjectValues(res); + return res; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + public object this[object key] { + get { + Debug.Assert(!(key is SymbolId)); + + string strKey = key as string; + object res; + if (strKey != null) { + lock (this) { + SymbolId id = SymbolTable.StringToId(strKey); + + if (TryGetExtraValue(id, out res) && !(res is Uninitialized)) return res; + + if (_data == null) { + throw new KeyNotFoundException(key.ToString()); + } + + return _data[id]; + } + } + + if (TryGetObjectValue(key, out res)) + return res; + + throw new KeyNotFoundException(key.ToString()); + } + set { + Debug.Assert(!(key is SymbolId)); + + string strKey = key as string; + if (strKey != null) { + lock (this) { + SymbolId id = SymbolTable.StringToId(strKey); + if (TrySetExtraValue(id, value)) return; + + if (_data == null) InitializeData(); + _data[id] = value; + } + } else { + AddObjectKey(key, value); + } + } + } + + #endregion + + #region ICollection> Members + + public void Add(KeyValuePair item) { + throw new NotImplementedException(); + } + + public void Clear() { + lock (this) { + foreach (SymbolId key in GetExtraKeys()) { + if (key.Id < 0) break; + + TrySetExtraValue(key, Uninitialized.Instance); + } + _data = null; + } + } + + [Confined] + public bool Contains(KeyValuePair item) { + throw new NotImplementedException(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) { + ContractUtils.RequiresNotNull(array, "array"); + ContractUtils.RequiresArrayRange(array, arrayIndex, Count, "araryIndex", "Count"); + + foreach (KeyValuePair kvp in ((IEnumerable>)this)) { + array[arrayIndex++] = kvp; + } + } + + public int Count { + get { + int count = GetObjectKeyCount(); + + lock (this) { + if (_data != null) { + foreach (KeyValuePair o in _data) { + if (o.Key == SymbolId.Invalid) break; + if (o.Key != ObjectKeys) count++; + } + } + + foreach (SymbolId key in GetExtraKeys()) { + if (key.Id < 0) break; + + object dummy; + if (TryGetExtraValue(key, out dummy) && dummy != Uninitialized.Instance) count++; + } + } + + return count; + } + } + + public bool IsReadOnly { + get { return false; } + } + + public bool Remove(KeyValuePair item) { + throw new NotImplementedException(); + } + + #endregion + + #region IEnumerable> Members + + [Pure] + IEnumerator> IEnumerable>.GetEnumerator() { + if (_data != null) { + foreach (KeyValuePair o in _data) { + if (o.Key == SymbolId.Invalid) break; + if (o.Key == ObjectKeys) continue; + yield return new KeyValuePair(SymbolTable.IdToString(o.Key), o.Value); + } + } + + foreach (SymbolId o in GetExtraKeys()) { + if (o.Id < 0) break; + + object val; + if (TryGetExtraValue(o, out val) && val != Uninitialized.Instance) { + yield return new KeyValuePair(SymbolTable.IdToString(o), val); + } + } + + IDictionaryEnumerator objItems = GetObjectItems(); + if (objItems != null) { + while (objItems.MoveNext()) { + yield return new KeyValuePair(objItems.Key, objItems.Value); + } + } + } + + #endregion + + #region IEnumerable Members + + [Pure] + public System.Collections.IEnumerator GetEnumerator() { + List l = new List(this.Keys); + for (int i = 0; i < l.Count; i++) { + object baseVal = l[i]; + object nullVal = l[i] = BaseSymbolDictionary.ObjToNull(l[i]); + if (baseVal != nullVal) { + // we've transformed null, stop looking + break; + } + } + return l.GetEnumerator(); + } + + #endregion + + #region IAttributesDictionary Members + + public void Add(SymbolId name, object value) { + lock (this) { + if (TrySetExtraValue(name, value)) return; + + if (_data == null) InitializeData(); + _data.Add(name, value); + } + } + + public bool ContainsKey(SymbolId name) { + object value; + if (TryGetExtraValue(name, out value) && value != Uninitialized.Instance) return true; + if (_data == null) return false; + + lock (this) return _data.ContainsKey(name); + } + + public bool Remove(SymbolId name) { + object value; + if (TryGetExtraValue(name, out value)) { + if (value == Uninitialized.Instance) return false; + if (TrySetExtraValue(name, Uninitialized.Instance)) return true; + } + + if (_data == null) return false; + + lock (this) return _data.Remove(name); + } + + public bool TryGetValue(SymbolId name, out object value) { + if (TryGetExtraValue(name, out value) && value != Uninitialized.Instance) return true; + + if (_data == null) return false; + + lock (this) return _data.TryGetValue(name, out value); + } + + object IAttributesCollection.this[SymbolId name] { + get { + object res; + if (TryGetExtraValue(name, out res) && res != Uninitialized.Instance) return res; + + lock (this) { + if (_data == null) throw new KeyNotFoundException(SymbolTable.IdToString(name)); + return _data[name]; + } + } + set { + if (TrySetExtraValue(name, value)) return; + + lock (this) { + if (_data == null) InitializeData(); + _data[name] = value; + } + } + } + + public IDictionary SymbolAttributes { + get { + Dictionary d = new Dictionary(); + lock (this) { + if (_data != null) { + foreach (KeyValuePair name in _data) { + if (name.Key == ObjectKeys) continue; + d.Add(name.Key, name.Value); + } + } + foreach (SymbolId extraKey in GetExtraKeys()) { + object value; + if (TryGetExtraValue(extraKey, out value) && !(value is Uninitialized)) + d.Add(extraKey, value); + } + } + return d; + } + } + + /// + /// Appends the object keys to the provided list. + /// + protected virtual void GetObjectKeys(List res) { + lock (this) { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) res.AddRange(objData.Keys); + } + } + + /// + /// Appends the values stored under object keys to the provided list. + /// + protected virtual void GetObjectValues(List res) { + lock (this) { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) res.AddRange(objData.Values); + } + } + + /// + /// Gets the count of object keys. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] // TODO: fix + protected virtual int GetObjectKeyCount() { + lock (this) { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) { + return objData.Count; + } + } + return 0; + } + + /// + /// Gets an IDictionaryEnumerator for all of the object key/value pairs. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] // TODO: fix + public virtual IDictionaryEnumerator GetObjectItems() { + lock (this) { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) { + return objData.GetEnumerator(); + } + } + return null; + } + + /// + /// Stores the specified value under the specified object key. + /// + public virtual void AddObjectKey(object name, object value) { + lock (this) { + Dictionary objData = GetObjectKeysDictionary(); + objData[name] = value; + } + } + + public bool ContainsObjectKey(object name) { + return AsObjectKeyedDictionary().ContainsKey(name); + } + + /// + /// Removes the specified object key from the dictionary. + /// + public virtual bool RemoveObjectKey(object name) { + lock (this) { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData == null) return false; + return objData.Remove(name); + } + } + + /// + /// Attemps to get the value stored under the specified object key. + /// + /// Returns true if the key was found, false if not found. + /// + public virtual bool TryGetObjectValue(object name, out object value) { + string strKey = name as string; + if (strKey != null) { + return ((IAttributesCollection)this).TryGetValue(SymbolTable.StringToId(strKey), out value); + } + + lock (this) { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) + return objData.TryGetValue(name, out value); + } + value = null; + return false; + } + + public ICollection Keys { get { return AsObjectKeyedDictionary().Keys; } } + + public IDictionary AsObjectKeyedDictionary() { + return this; + } + + #endregion + + #region IDictionary Members + + [Pure] + void IDictionary.Add(object key, object value) { + AsObjectKeyedDictionary().Add(key, value); + } + + [Pure] + public bool Contains(object key) { + return AsObjectKeyedDictionary().ContainsKey(key); + } + + [Pure] + IDictionaryEnumerator IDictionary.GetEnumerator() { + List enums = new List(); + + enums.Add(new ExtraKeyEnumerator(this)); + + if (_data != null) enums.Add(new TransformDictionaryEnumerator(_data)); + + IDictionaryEnumerator objItems = GetObjectItems(); + if (objItems != null) { + enums.Add(objItems); + } + + return new DictionaryUnionEnumerator(enums); + } + + public bool IsFixedSize { + get { return false; } + } + + ICollection IDictionary.Keys { + get { return new List(AsObjectKeyedDictionary().Keys); } + } + + void IDictionary.Remove(object key) { + Debug.Assert(!(key is SymbolId)); + string strKey = key as string; + if (strKey != null) { + SymbolId id = SymbolTable.StringToId(strKey); + if (TrySetExtraValue(id, Uninitialized.Instance)) return; + + lock (this) if (_data != null) _data.Remove(id); + } else { + RemoveObjectKey(key); + } + } + + ICollection IDictionary.Values { + get { + return new List(AsObjectKeyedDictionary().Values); + } + } + + object IDictionary.this[object key] { + get { return AsObjectKeyedDictionary()[key]; } + set { AsObjectKeyedDictionary()[key] = value; } + } + + #endregion + + public void CopyTo(Array array, int index) { + throw Error.MethodOrOperatorNotImplemented(); + } + + public bool IsSynchronized { + get { + return true; + } + } + + public object SyncRoot { + get { + // TODO: Sync root shouldn't be this, it should be data. + return this; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/DefaultLanguageContext.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/DefaultLanguageContext.cs new file mode 100644 index 0000000000..af3c9f588c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/DefaultLanguageContext.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Actions.Calls; + +namespace Microsoft.Scripting.Runtime { + // TODO: invariant context + internal class DefaultActionBinder : DefaultBinder { + private Type[] _extensionTypes; + + public DefaultActionBinder(ScriptDomainManager manager, Type[] extensionTypes) + : base(manager) { + this._extensionTypes = extensionTypes; + } + + public override IList GetExtensionTypes(Type t) { + return _extensionTypes; + } + + // A bunch of conversion code + public override object Convert(object obj, Type toType) { + throw new NotImplementedException(); + } + + public override bool CanConvertFrom(Type fromType, Type toType, bool toNotNullable, NarrowingLevel level) { + // TODO: None -> nullable reference types? + return toType.IsAssignableFrom(fromType); + } + + public override Candidate PreferConvert(Type t1, Type t2) { + throw new NotImplementedException(); + } + + public override Expression ConvertExpression(Expression expr, Type toType, ConversionResultKind kind, Expression context) { + if (toType.IsAssignableFrom(expr.Type)) { + return expr; + } + return Expression.Convert(expr, toType); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/DelegateCallBinder.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/DelegateCallBinder.cs new file mode 100644 index 0000000000..7adde0675a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/DelegateCallBinder.cs @@ -0,0 +1,71 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Actions { + /// + /// Call site binder used by the DelegateSignatureInfo to call IDynamicObject + /// + class DelegateCallBinder : CallSiteBinder { + private readonly int _args; + + internal DelegateCallBinder(int args) { + _args = args; + } + + public override int GetHashCode() { + return _args ^ 31321; + } + + public override bool Equals(object obj) { + DelegateCallBinder dcb = obj as DelegateCallBinder; + return dcb != null && dcb._args == _args; + } + + public override object CacheIdentity { + get { return this; } + } + + private static CodeContext ExtractCodeContext(ref object[] args) { + CodeContext cc = null; + if (args.Length > 0 && (cc = args[0] as CodeContext) != null) { + args = ArrayUtils.ShiftLeft(args, 1); + } + return cc; + } + + public override Expression Bind(object[] args, ReadOnlyCollection parameters, LabelTarget returnLabel) { + ContractUtils.RequiresNotNull(args, "args"); + CodeContext cc = ExtractCodeContext(ref args); + ContractUtils.Requires(args.Length > 0); + IOldDynamicObject ido = args[0] as IOldDynamicObject; + ContractUtils.RequiresNotNull(ido, "args"); + + OldCallAction ca = OldCallAction.Make(cc.LanguageContext.Binder, _args); + var builder = new RuleBuilder(parameters, returnLabel); + if (!ido.GetRule(ca, cc, args, builder)) { + throw new InvalidOperationException("Cannot perform call."); + } + + return builder.CreateRule(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/DelegateInfo.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/DelegateInfo.cs new file mode 100644 index 0000000000..e666fe921c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/DelegateInfo.cs @@ -0,0 +1,71 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Reflection; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + /// + /// Used as the value for the ScriptingRuntimeHelpers.GetDelegate method caching system + /// + internal sealed class DelegateInfo { + private readonly MethodInfo _method; + private readonly object[] _constants; + private WeakDictionary _constantMap = new WeakDictionary(); + + internal DelegateInfo(MethodInfo method, object[] constants) { + Assert.NotNull(method, constants); + + _method = method; + _constants = constants; + } + + internal Delegate CreateDelegate(Type delegateType, object target) { + Assert.NotNull(delegateType, target); + + // to enable: + // function x() { } + // someClass.someEvent += delegateType(x) + // someClass.someEvent -= delegateType(x) + // + // we need to avoid re-creating the object array because they won't + // be compare equal when removing the delegate if they're difference + // instances. Therefore we use a weak hashtable to get back the + // original object array. The values also need to be weak to avoid + // creating a circular reference from the constants target back to the + // target. This is fine because as long as the delegate is referenced + // the object array will stay alive. Once the delegate is gone it's not + // wired up anywhere and -= will never be used again. + + object[] clone; + lock (_constantMap) { + WeakReference cloneRef; + + if (!_constantMap.TryGetValue(target, out cloneRef) || + (clone = (object[])cloneRef.Target) == null) { + _constantMap[target] = new WeakReference(clone = (object[])_constants.Clone()); + + Debug.Assert(clone[0] == DelegateSignatureInfo.TargetPlaceHolder); + + clone[0] = target; + } + } + + return ReflectionUtils.CreateDelegate(_method, delegateType, clone); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/DelegateSignatureInfo.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/DelegateSignatureInfo.cs new file mode 100644 index 0000000000..6653fa9fd9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/DelegateSignatureInfo.cs @@ -0,0 +1,229 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Dynamic.Binders; +using System.Text; +using Microsoft.Contracts; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + /// + /// Used as the key for the ScriptingRuntimeHelpers.GetDelegate method caching system + /// + internal sealed class DelegateSignatureInfo { + private readonly LanguageContext _context; + private readonly Type _returnType; + private readonly ParameterInfo[] _parameters; + + internal static readonly object TargetPlaceHolder = new object(); + + internal DelegateSignatureInfo(LanguageContext context, Type returnType, ParameterInfo[] parameters) { + Assert.NotNull(context, returnType); + Assert.NotNullItems(parameters); + + _context = context; + _parameters = parameters; + _returnType = returnType; + } + + [Confined] + public override bool Equals(object obj) { + DelegateSignatureInfo dsi = obj as DelegateSignatureInfo; + + if (dsi == null || + dsi._context != _context || + dsi._parameters.Length != _parameters.Length || + dsi._returnType != _returnType) { + return false; + } + + for (int i = 0; i < _parameters.Length; i++) { + if (dsi._parameters[i] != _parameters[i]) { + return false; + } + } + + return true; + } + + [Confined] + public override int GetHashCode() { + int hashCode = 5331; + + for (int i = 0; i < _parameters.Length; i++) { + hashCode ^= _parameters[i].GetHashCode(); + } + hashCode ^= _returnType.GetHashCode() ^ _context.GetHashCode(); + return hashCode; + } + + [Confined] + public override string ToString() { + StringBuilder text = new StringBuilder(); + text.Append(_returnType.ToString()); + text.Append("("); + for (int i = 0; i < _parameters.Length; i++) { + if (i != 0) text.Append(", "); + text.Append(_parameters[i].ParameterType.Name); + } + text.Append(")"); + return text.ToString(); + } + + internal DelegateInfo GenerateDelegateStub() { + PerfTrack.NoteEvent(PerfTrack.Categories.DelegateCreate, ToString()); + + Type[] delegateParams = new Type[_parameters.Length]; + for (int i = 0; i < _parameters.Length; i++) { + delegateParams[i] = _parameters[i].ParameterType; + } + + // Create the method + DynamicILGen cg = Snippets.Shared.CreateDynamicMethod(ToString(), _returnType, ArrayUtils.Insert(typeof(object[]), delegateParams), false); + + // Emit the stub + object[] constants = EmitClrCallStub(cg); + + // Save the constants in the delegate info class + return new DelegateInfo(cg.Finish(), constants); + } + + /// + /// Generates stub to receive the CLR call and then call the dynamic language code. + /// + private object[] EmitClrCallStub(ILGen cg) { + + List fixers = new List(0); + ArgumentInfo[] args = new ArgumentInfo[_parameters.Length]; + for (int i = 0; i < args.Length; i++) { + args[i] = Expression.PositionalArg(i); + } + ConvertBinder convert = _context.CreateConvertBinder(_returnType, true); + InvokeBinder action = _context.CreateInvokeBinder(args); + + + // Create strongly typed return type from the site. + // This will, among other things, generate tighter code. + Type[] siteTypes = MakeSiteSignature(); + + Type siteType = DynamicSiteHelpers.MakeCallSiteType(siteTypes); + CallSite callSite = DynamicSiteHelpers.MakeSite(action, siteType); + + Type convertSiteType = null; + CallSite convertSite = null; + + if (_returnType != typeof(void)) { + convertSiteType = DynamicSiteHelpers.MakeCallSiteType(typeof(object), _returnType); + convertSite = DynamicSiteHelpers.MakeSite(convert, convertSiteType); + } + + // build up constants array + object[] constants = new object[] { TargetPlaceHolder, callSite, convertSite }; + const int TargetIndex = 0, CallSiteIndex = 1, ConvertSiteIndex = 2; + + LocalBuilder convertSiteLocal = null; + FieldInfo convertTarget = null; + if (_returnType != typeof(void)) { + // load up the conversesion logic on the stack + convertSiteLocal = cg.DeclareLocal(convertSiteType); + EmitConstantGet(cg, ConvertSiteIndex, convertSiteType); + + cg.Emit(OpCodes.Dup); + cg.Emit(OpCodes.Stloc, convertSiteLocal); + + convertTarget = convertSiteType.GetField("Target"); + cg.EmitFieldGet(convertTarget); + cg.Emit(OpCodes.Ldloc, convertSiteLocal); + } + + // load up the invoke logic on the stack + LocalBuilder site = cg.DeclareLocal(siteType); + EmitConstantGet(cg, CallSiteIndex, siteType); + cg.Emit(OpCodes.Dup); + cg.Emit(OpCodes.Stloc, site); + + FieldInfo target = siteType.GetField("Target"); + cg.EmitFieldGet(target); + cg.Emit(OpCodes.Ldloc, site); + + EmitConstantGet(cg, TargetIndex, typeof(object)); + + for (int i = 0; i < _parameters.Length; i++) { + if (_parameters[i].ParameterType.IsByRef) { + ReturnFixer rf = ReturnFixer.EmitArgument(cg, i + 1, _parameters[i].ParameterType); + if (rf != null) fixers.Add(rf); + } else { + cg.EmitLoadArg(i + 1); + } + } + + // emit the invoke for the call + cg.EmitCall(target.FieldType, "Invoke"); + + // emit the invoke for the convert + if (_returnType == typeof(void)) { + cg.Emit(OpCodes.Pop); + } else { + cg.EmitCall(convertTarget.FieldType, "Invoke"); + } + + // fixup any references + foreach (ReturnFixer rf in fixers) { + rf.FixReturn(cg); + } + + cg.Emit(OpCodes.Ret); + return constants; + } + + private static void EmitConstantGet(ILGen il, int index, Type type) { + il.Emit(OpCodes.Ldarg_0); + il.EmitInt(index); + il.Emit(OpCodes.Ldelem_Ref); + if (type != typeof(object)) { + il.Emit(OpCodes.Castclass, type); + } + } + + private Type[] MakeSiteSignature() { + Type[] sig = new Type[_parameters.Length + 2]; + + // target object + sig[0] = typeof(object); + + // arguments + for (int i = 0; i < _parameters.Length; i++) { + if (_parameters[i].IsByRefParameter()) { + sig[i + 1] = typeof(object); + } else { + sig[i + 1] = _parameters[i].ParameterType; + } + } + + // return type + sig[sig.Length - 1] = typeof(object); + + return sig; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/DlrCachedCodeAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/DlrCachedCodeAttribute.cs new file mode 100644 index 0000000000..88c92e6c3f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/DlrCachedCodeAttribute.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + /// + /// An attribute that is applied to saved ScriptCode's to be used to re-create the ScriptCode + /// from disk. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public sealed class DlrCachedCodeAttribute : Attribute { + } + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public sealed class CachedOptimizedCodeAttribute : Attribute { + private readonly string[] _names; + + // C# requires a constructor with CLS compliant types: + public CachedOptimizedCodeAttribute() { + _names = ArrayUtils.EmptyStrings; + } + + public CachedOptimizedCodeAttribute(string[] names) { + ContractUtils.RequiresNotNull(names, "names"); + _names = names; + } + + /// + /// Gets names stored in optimized scope. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + public string[] Names { + get { + return _names; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/DlrConfiguration.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/DlrConfiguration.cs new file mode 100644 index 0000000000..e1f6d79333 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/DlrConfiguration.cs @@ -0,0 +1,324 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Threading; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + /// + /// Singleton for each language. + /// + internal sealed class LanguageConfiguration { + private readonly AssemblyQualifiedTypeName _providerName; + private readonly string _displayName; + private readonly IDictionary _options; + private LanguageContext _context; + + public LanguageContext LanguageContext { + get { return _context; } + } + + public AssemblyQualifiedTypeName ProviderName { + get { return _providerName; } + } + + public string DisplayName { + get { return _displayName; } + } + + public LanguageConfiguration(AssemblyQualifiedTypeName providerName, string displayName, IDictionary options) { + _providerName = providerName; + _displayName = displayName; + _options = options; + } + + /// + /// Must not be called under a lock as it can potentially call a user code. + /// + /// + /// The language context's implementation failed to instantiate. + internal LanguageContext LoadLanguageContext(ScriptDomainManager domainManager, out bool alreadyLoaded) { + if (_context == null) { + + // Let assembly load errors bubble out + var assembly = domainManager.Platform.LoadAssembly(_providerName.AssemblyName.FullName); + + Type type = assembly.GetType(_providerName.TypeName); + if (type == null) { + throw new InvalidOperationException( + string.Format( + "Failed to load language '{0}': assembly '{1}' does not contain type '{2}'", + _displayName, assembly.Location, _providerName.TypeName + )); + } + + if (!type.IsSubclassOf(typeof(LanguageContext))) { + throw new InvalidOperationException( + string.Format( + "Failed to load language '{0}': type '{1}' is not a valid language provider because it does not inherit from LanguageContext", + _displayName, type + )); + } + + var context = ReflectionUtils.CreateInstance(type, domainManager, _options); + alreadyLoaded = Interlocked.CompareExchange(ref _context, context, null) != null; + } else { + alreadyLoaded = true; + } + + return _context; + } + } + + public sealed class DlrConfiguration { + private bool _frozen; + + private readonly bool _debugMode; + private readonly bool _privateBinding; + private readonly IDictionary _options; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly StringComparer FileExtensionComparer = StringComparer.OrdinalIgnoreCase; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly StringComparer LanguageNameComparer = StringComparer.OrdinalIgnoreCase; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly StringComparer OptionNameComparer = StringComparer.Ordinal; + + private readonly Dictionary _languageNames; + private readonly Dictionary _languageExtensions; + private readonly Dictionary _languageConfigurations; + private readonly Dictionary _loadedProviderTypes; + + public DlrConfiguration(bool debugMode, bool privateBinding, IDictionary options) { + ContractUtils.RequiresNotNull(options, "options"); + _debugMode = debugMode; + _privateBinding = privateBinding; + _options = options; + + _languageNames = new Dictionary(LanguageNameComparer); + _languageExtensions = new Dictionary(FileExtensionComparer); + _languageConfigurations = new Dictionary(); + _loadedProviderTypes = new Dictionary(); + } + + /// + /// Whether the application is in debug mode. + /// This means: + /// + /// 1) Symbols are emitted for debuggable methods (methods associated with SourceUnit). + /// 2) Debuggable methods are emitted to non-collectable types (this is due to CLR limitations on dynamic method debugging). + /// 3) JIT optimization is disabled for all methods + /// 4) Languages may disable optimizations based on this value. + /// + public bool DebugMode { + get { return _debugMode; } + } + + /// + /// Ignore CLR visibility checks. + /// + public bool PrivateBinding { + get { return _privateBinding; } + } + + internal IDictionary Options { + get { return _options; } + } + + internal IDictionary Languages { + get { return _languageConfigurations; } + } + + public void AddLanguage(string languageTypeName, string displayName, IList names, IList fileExtensions, + IDictionary options) { + AddLanguage(languageTypeName, displayName, names, fileExtensions, options, null); + } + + internal void AddLanguage(string languageTypeName, string displayName, IList names, IList fileExtensions, + IDictionary options, string paramName) { + ContractUtils.Requires(!_frozen, "Configuration cannot be modified once the runtime is initialized"); + ContractUtils.Requires( + CollectionUtils.TrueForAll(names, (id) => !String.IsNullOrEmpty(id) && !_languageNames.ContainsKey(id)), + paramName ?? "names", + "Language name should not be null, empty or duplicated between languages" + ); + ContractUtils.Requires( + CollectionUtils.TrueForAll(fileExtensions, (ext) => !String.IsNullOrEmpty(ext) && !_languageExtensions.ContainsKey(ext)), + paramName ?? "fileExtensions", + "File extension should not be null, empty or duplicated between languages" + ); + ContractUtils.RequiresNotNull(displayName, paramName ?? "displayName"); + + if (string.IsNullOrEmpty(displayName)) { + ContractUtils.Requires(names.Count > 0, paramName ?? "displayName", "Must have a non-empty display name or a a non-empty list of language names"); + displayName = names[0]; + } + + var aqtn = AssemblyQualifiedTypeName.ParseArgument(languageTypeName, paramName ?? "languageTypeName"); + if (_languageConfigurations.ContainsKey(aqtn)) { + throw new ArgumentException(string.Format("Duplicate language with type name '{0}'", aqtn), "languageTypeName"); + } + + // Add global language options first, they can be rewritten by language specific ones: + var mergedOptions = new Dictionary(_options); + + // Replace global options with language-specific options + foreach (var option in options) { + mergedOptions[option.Key] = option.Value; + } + + var config = new LanguageConfiguration(aqtn, displayName, mergedOptions); + + _languageConfigurations.Add(aqtn, config); + + // allow duplicate ids in identifiers and extensions lists: + foreach (var name in names) { + _languageNames[name] = config; + } + + foreach (var ext in fileExtensions) { + _languageExtensions[NormalizeExtension(ext)] = config; + } + } + + internal static string NormalizeExtension(string extension) { + return extension[0] == '.' ? extension : "." + extension; + } + + internal void Freeze() { + Debug.Assert(!_frozen); + _frozen = true; + } + + internal bool TryLoadLanguage(ScriptDomainManager manager, AssemblyQualifiedTypeName providerName, out LanguageContext language) { + Assert.NotNull(manager); + LanguageConfiguration config; + + if (_languageConfigurations.TryGetValue(providerName, out config)) { + language = LoadLanguageContext(manager, config); + return true; + } + + language = null; + return false; + } + + internal bool TryLoadLanguage(ScriptDomainManager manager, string str, bool isExtension, out LanguageContext language) { + Assert.NotNull(manager, str); + + var dict = (isExtension) ? _languageExtensions : _languageNames; + + LanguageConfiguration config; + if (dict.TryGetValue(str, out config)) { + language = LoadLanguageContext(manager, config); + return true; + } + + language = null; + return false; + } + + private LanguageContext LoadLanguageContext(ScriptDomainManager manager, LanguageConfiguration config) { + bool alreadyLoaded; + var language = config.LoadLanguageContext(manager, out alreadyLoaded); + + if (!alreadyLoaded) { + // Checks whether a single language is not registered under two different AQTNs. + // We can only do it now because there is no way how to ensure that two AQTNs don't refer to the same type w/o loading the type. + // The check takes place after config.LoadLanguageContext is called to avoid calling user code while holding a lock. + lock (_loadedProviderTypes) { + LanguageConfiguration existingConfig; + Type type = language.GetType(); + if (_loadedProviderTypes.TryGetValue(type, out existingConfig)) { + throw new InvalidOperationException(String.Format("Language implemented by type '{0}' has already been loaded using name '{1}'", + config.ProviderName, existingConfig.ProviderName)); + } + + _loadedProviderTypes.Add(type, config); + } + } + return language; + } + + public string[] GetLanguageNames(LanguageContext context) { + ContractUtils.RequiresNotNull(context, "context"); + + List result = new List(); + + foreach (var entry in _languageNames) { + if (entry.Value.LanguageContext == context) { + result.Add(entry.Key); + } + } + + return result.ToArray(); + } + + internal string[] GetLanguageNames(LanguageConfiguration config) { + List result = new List(); + + foreach (var entry in _languageNames) { + if (entry.Value == config) { + result.Add(entry.Key); + } + } + + return result.ToArray(); + } + + public string[] GetLanguageNames() { + return ArrayUtils.MakeArray(_languageNames.Keys); + } + + public string[] GetFileExtensions(LanguageContext context) { + var result = new List(); + foreach (var entry in _languageExtensions) { + if (entry.Value.LanguageContext == context) { + result.Add(entry.Key); + } + } + + return result.ToArray(); + } + + internal string[] GetFileExtensions(LanguageConfiguration config) { + var result = new List(); + foreach (var entry in _languageExtensions) { + if (entry.Value == config) { + result.Add(entry.Key); + } + } + + return result.ToArray(); + } + + public string[] GetFileExtensions() { + return ArrayUtils.MakeArray(_languageExtensions.Keys); + } + + internal LanguageConfiguration GetLanguageConfig(LanguageContext context) { + foreach (var config in _languageConfigurations.Values) { + if (config.LanguageContext == context) { + return config; + } + } + return null; + } + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/DocumentationAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/DocumentationAttribute.cs new file mode 100644 index 0000000000..2d8a9b7a43 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/DocumentationAttribute.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Runtime { + /// + /// Provides a mechanism for providing documentation stored in an assembly as metadata. + /// + /// Applying this attribute will enable documentation to be provided to the user at run-time + /// even if XML Docuementation files are unavailable. + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class DocumentationAttribute : Attribute { + private readonly string _doc; + + public DocumentationAttribute(string documentation) { + _doc = documentation; + } + + public string Documentation { + get { return _doc; } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/DynamicLanguageProviderAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/DynamicLanguageProviderAttribute.cs new file mode 100644 index 0000000000..20f3fc049b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/DynamicLanguageProviderAttribute.cs @@ -0,0 +1,83 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if SILVERLIGHT + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Runtime { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments")] + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)] + public sealed class DynamicLanguageProviderAttribute : Attribute { + private readonly Type _languageContextType; + private readonly string[] _fileExtensions; + private readonly string[] _names; + private readonly string _displayName; + + /// + /// LanguageContext implementation. + /// + public Type LanguageContextType { + get { return _languageContextType; } + } + + /// + /// Default display name. + /// + public string DisplayName { + get { return _displayName; } + } + + /// + /// Default file extensions. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + public string[] FileExtensions { + get { return _fileExtensions; } + } + + /// + /// Default names for the language. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + public string[] Names { + get { return _names; } + } + + public DynamicLanguageProviderAttribute(Type languageContextType, string displayName, string names, string fileExtensions) + : this(languageContextType, displayName, + StringUtils.Split(names, new[] { ';', ',' }, Int32.MaxValue, StringSplitOptions.RemoveEmptyEntries), + StringUtils.Split(fileExtensions, new[] { ';', ',' }, Int32.MaxValue, StringSplitOptions.RemoveEmptyEntries)) { + } + + public DynamicLanguageProviderAttribute(Type languageContextType, string displayName, string[] names, string[] fileExtensions) { + ContractUtils.RequiresNotNull(languageContextType, "languageContextType"); + ContractUtils.RequiresNotNull(displayName, "displayName"); + ContractUtils.RequiresNotNull(names, "names"); + ContractUtils.RequiresNotNull(fileExtensions, "fileExtensions"); + + _languageContextType = languageContextType; + _displayName = displayName; + _names = names; + _fileExtensions = fileExtensions; + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/DynamicOperations.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/DynamicOperations.cs new file mode 100644 index 0000000000..677a16fcc2 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/DynamicOperations.cs @@ -0,0 +1,613 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using Microsoft.Contracts; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + + /// + /// ObjectOperations provide a large catalogue of object operations such as member access, conversions, + /// indexing, and things like addition. There are several introspection and tool support services available + /// for more advanced hosts. + /// + /// You get ObjectOperation instances from ScriptEngine, and they are bound to their engines for the semantics + /// of the operations. There is a default instance of ObjectOperations you can share across all uses of the + /// engine. However, very advanced hosts can create new instances. + /// + public sealed class DynamicOperations { + private readonly LanguageContext _lc; + + /// a dictionary of SiteKey's which are used to cache frequently used operations, logically a set + private Dictionary _sites = new Dictionary(); + + /// the # of sites we had created at the last cleanup + private int LastCleanup; + + /// the total number of sites we've ever created + private int SitesCreated; + + /// the number of sites required before we'll try cleaning up the cache... + private const int CleanupThreshold = 20; + + /// the minimum difference between the average that is required to remove + private const int RemoveThreshold = 2; + + /// the maximum number we'll remove on a single cache cleanup + private const int StopCleanupThreshold = CleanupThreshold / 2; + + /// the number of sites we should clear after if we can't make progress cleaning up otherwise + private const int ClearThreshold = 50; + + internal DynamicOperations(LanguageContext lc) { + ContractUtils.RequiresNotNull(lc, "lc"); + _lc = lc; + } + + #region Basic Operations + + /// + /// Calls the provided object with the given parameters and returns the result. + /// + /// The prefered way of calling objects is to convert the object to a strongly typed delegate + /// using the ConvertTo methods and then invoking that delegate. + /// + public object Invoke(object obj, params object[] parameters) { + // we support a couple of parameters instead of just splatting because JS doesn't yet support splatted arguments for function calls. + switch (parameters.Length) { + case 0: { + CallSite> site; + site = GetSite(_lc.CreateInvokeBinder()); + return site.Target(site, obj); + } + case 1: { + CallSite> site; + site = GetSite(_lc.CreateInvokeBinder(Expression.PositionalArg(0))); + return site.Target(site, obj, parameters[0]); + } + case 2: { + CallSite> site; + site = GetSite(_lc.CreateInvokeBinder(Expression.PositionalArg(0), Expression.PositionalArg(1))); + return site.Target(site, obj, parameters[0], parameters[1]); + } + default: + throw new NotImplementedException(); + } + } + + /// + /// Invokes a member on the provided object with the given parameters and returns the result. + /// + public object InvokeMember(object obj, string memberName, params object[] parameters) { + return InvokeMember(obj, memberName, false, parameters); + } + + /// + /// Invokes a member on the provided object with the given parameters and returns the result. + /// + public object InvokeMember(object obj, string memberName, bool ignoreCase, params object[] parameters) { + // we support a couple of parameters instead of just splatting because JS doesn't yet support splatted arguments for function calls. + switch (parameters.Length) { + case 0: { + CallSite> site; + site = GetSite(_lc.CreateCallBinder(memberName, ignoreCase)); + return site.Target(site, obj); + } + case 1: { + CallSite> site; + site = GetSite(_lc.CreateCallBinder(memberName, ignoreCase, Expression.PositionalArg(0))); + return site.Target(site, obj, parameters[0]); + } + case 2: { + CallSite> site; + site = GetSite(_lc.CreateCallBinder(memberName, ignoreCase, Expression.PositionalArg(0), Expression.PositionalArg(1))); + return site.Target(site, obj, parameters[0], parameters[1]); + } + default: + throw new NotImplementedException(); + } + } + + /// + /// Creates a new instance from the provided object using the given parameters, and returns the result. + /// + public object CreateInstance(object obj, params object[] parameters) { + // we support a couple of parameters instead of just splatting because JS doesn't yet support splatted arguments for function calls. + switch (parameters.Length) { + case 0: { + CallSite> site; + site = GetSite(_lc.CreateCreateBinder()); + return site.Target(site, obj); + } + case 1: { + CallSite> site; + site = GetSite(_lc.CreateCreateBinder(Expression.PositionalArg(0))); + return site.Target(site, obj, parameters[0]); + } + case 2: { + CallSite> site; + site = GetSite(_lc.CreateCreateBinder(Expression.PositionalArg(0), Expression.PositionalArg(1))); + return site.Target(site, obj, parameters[0], parameters[1]); + } + default: + throw new NotImplementedException(); + } + } + + /// + /// Gets the member name from the object obj. Throws an exception if the member does not exist or is write-only. + /// + public object GetMember(object obj, string name) { + return GetMember(obj, name, false); + } + + /// + /// Gets the member name from the object obj and converts it to the type T. Throws an exception if the + /// member does not exist, is write-only, or cannot be converted. + /// + public T GetMember(object obj, string name) { + return GetMember(obj, name, false); + } + + /// + /// Gets the member name from the object obj. Returns true if the member is successfully retrieved and + /// stores the value in the value out param. + /// + public bool TryGetMember(object obj, string name, out object value) { + return TryGetMember(obj, name, false, out value); + } + + /// + /// Returns true if the object has a member named name, false if the member does not exist. + /// + public bool ContainsMember(object obj, string name) { + return ContainsMember(obj, name, false); + } + + /// + /// Removes the member name from the object obj. Returns true if the member was successfully removed + /// or false if the member does not exist. + /// + public bool RemoveMember(object obj, string name) { + return RemoveMember(obj, name, false); + } + + /// + /// Sets the member name on object obj to value. + /// + public void SetMember(object obj, string name, object value) { + SetMember(obj, name, value, false); + } + + /// + /// Sets the member name on object obj to value. This overload can be used to avoid + /// boxing and casting of strongly typed members. + /// + public void SetMember(object obj, string name, T value) { + SetMember(obj, name, value, false); + } + + /// + /// Gets the member name from the object obj. Throws an exception if the member does not exist or is write-only. + /// + public object GetMember(object obj, string name, bool ignoreCase) { + CallSite> site; + site = GetSite(_lc.CreateGetMemberBinder(name, ignoreCase)); + return site.Target(site, obj); + } + + /// + /// Gets the member name from the object obj and converts it to the type T. Throws an exception if the + /// member does not exist, is write-only, or cannot be converted. + /// + public T GetMember(object obj, string name, bool ignoreCase) { + CallSite> convertSite = GetSite(_lc.CreateConvertBinder(typeof(T), false)); + CallSite> site = GetSite(_lc.CreateGetMemberBinder(name, ignoreCase)); + return convertSite.Target(convertSite, site.Target(site, obj)); + } + + /// + /// Gets the member name from the object obj. Returns true if the member is successfully retrieved and + /// stores the value in the value out param. + /// + public bool TryGetMember(object obj, string name, bool ignoreCase, out object value) { + try { + value = GetMember(obj, name, ignoreCase); + return true; + } catch (MissingMemberException) { + value = null; + return false; + } + } + + /// + /// Returns true if the object has a member named name, false if the member does not exist. + /// + public bool ContainsMember(object obj, string name, bool ignoreCase) { + object dummy; + return TryGetMember(obj, name, ignoreCase, out dummy); + } + + /// + /// Removes the member name from the object obj. Returns true if the member was successfully removed + /// or false if the member does not exist. + /// + public bool RemoveMember(object obj, string name, bool ignoreCase) { + CallSite> site; + site = GetSite(_lc.CreateDeleteMemberBinder(name, ignoreCase)); + return site.Target(site, obj); + } + + /// + /// Sets the member name on object obj to value. + /// + public void SetMember(object obj, string name, object value, bool ignoreCase) { + CallSite> site; + site = GetSite(_lc.CreateSetMemberBinder(name, ignoreCase)); + site.Target(site, obj, value); + } + + /// + /// Sets the member name on object obj to value. This overload can be used to avoid + /// boxing and casting of strongly typed members. + /// + public void SetMember(object obj, string name, T value, bool ignoreCase) { + CallSite> site; + site = GetSite(_lc.CreateSetMemberBinder(name, ignoreCase)); + site.Target(site, obj, value); + } + + /// + /// Convers the object obj to the type T. + /// + public T ConvertTo(object obj) { + CallSite> site; + site = GetSite(_lc.CreateConvertBinder(typeof(T), false)); + return site.Target(site, obj); + } + + /// + /// Converts the object obj to the type type. + /// + public object ConvertTo(object obj, Type type) { + CallSite> site; + site = GetSite(_lc.CreateConvertBinder(type, false)); + return site.Target(site, obj); + } + + /// + /// Converts the object obj to the type T. Returns true if the value can be converted, false if it cannot. + /// + public bool TryConvertTo(object obj, out T result) { + try { + result = ConvertTo(obj); + return true; + } catch (ArgumentTypeException) { + result = default(T); + return false; + } catch (InvalidCastException) { + result = default(T); + return false; + } + } + + /// + /// Converts the object obj to the type type. Returns true if the value can be converted, false if it cannot. + /// + public bool TryConvertTo(object obj, Type type, out object result) { + try { + result = ConvertTo(obj, type); + return true; + } catch (ArgumentTypeException) { + result = null; + return false; + } catch (InvalidCastException) { + result = null; + return false; + } + } + + /// + /// Convers the object obj to the type T including explicit conversions which may lose information. + /// + public T ExplicitConvertTo(object obj) { + CallSite> site; + site = GetSite(_lc.CreateConvertBinder(typeof(T), true)); + return site.Target(site, obj); + } + + /// + /// Converts the object obj to the type type including explicit conversions which may lose information. + /// + public object ExplicitConvertTo(object obj, Type type) { + CallSite> site; + site = GetSite(_lc.CreateConvertBinder(type, true)); + return site.Target(site, obj); + } + + /// + /// Converts the object obj to the type type including explicit conversions which may lose information. + /// + /// Returns true if the value can be converted, false if it cannot. + /// + public bool TryExplicitConvertTo(object obj, Type type, out object result) { + try { + result = ExplicitConvertTo(obj, type); + return true; + } catch (ArgumentTypeException) { + result = null; + return false; + } catch (InvalidCastException) { + result = null; + return false; + } + } + + /// + /// Converts the object obj to the type T. Returns true if the value can be converted, false if it cannot. + /// + public bool TryExplicitConvertTo(object obj, out T result) { + try { + result = ExplicitConvertTo(obj); + return true; + } catch (ArgumentTypeException) { + result = default(T); + return false; + } catch (InvalidCastException) { + result = default(T); + return false; + } + } + + /// + /// Performs a generic unary operation on the strongly typed target and returns the value as the specified type + /// + public TResult DoOperation(ExpressionType operation, TTarget target) { + var site = GetSite(_lc.CreateUnaryOperationBinder(operation)); + return site.Target(site, target); + } + + /// + /// Peforms the generic binary operation on the specified strongly typed targets and returns + /// the strongly typed result. + /// + public TResult DoOperation(ExpressionType operation, TTarget target, TOther other) { + var site = GetSite(_lc.CreateBinaryOperationBinder(operation)); + return site.Target(site, target, other); + } + + /// + /// Performs a generic unary operation on the specified target and returns the result. + /// + [Obsolete("Use UnaryOperation or BinaryOperation")] + public object DoOperation(string op, object target) { + return DoOperation(op, target); + } + + /// + /// Performs a generic unary operation on the strongly typed target and returns the value as the specified type + /// + [Obsolete("Use UnaryOperation or BinaryOperation")] + public TResult DoOperation(string op, TTarget target) { + CallSite> site; + site = GetSite(_lc.CreateOperationBinder(op)); + return site.Target(site, target); + } + + /// + /// Performs the generic binary operation on the specified targets and returns the result. + /// + [Obsolete("Use UnaryOperation or BinaryOperation")] + public object DoOperation(Operators op, object target, object other) { + return DoOperation(op.ToString(), target, other); + } + + /// + /// Peforms the generic binary operation on the specified strongly typed targets and returns + /// the strongly typed result. + /// + [Obsolete("Use UnaryOperation or BinaryOperation")] + public TResult DoOperation(string op, TTarget target, TOther other) { + CallSite> site; + site = GetSite(_lc.CreateOperationBinder(op)); + return site.Target(site, target, other); + } + + /// + /// Returns a list of strings which contain the known members of the object. + /// + public IList GetMemberNames(object obj) { + return _lc.GetMemberNames(obj); + } + + #endregion + + #region Private implementation details + + /// + /// Helper to create a new dynamic site w/ the specified type parameters for the provided action. + /// + /// This will either get the site from the cache or create a new site and return it. The cache + /// may be cleaned if it's gotten too big since the last usage. + /// + private CallSite> GetSite(CallSiteBinder action) { + return GetSiteWorker>>(action, CallSite>.Create); + } + + /// + /// Helper to create a new dynamic site w/ the specified type parameters for the provided action. + /// + /// This will either get the site from the cache or create a new site and return it. The cache + /// may be cleaned if it's gotten too big since the last usage. + /// + private CallSite> GetSite(CallSiteBinder action) { + return GetSiteWorker>>(action, CallSite>.Create); + } + + /// + /// Helper to create a new dynamic site w/ the specified type parameters for the provided action. + /// + /// This will either get the site from the cache or create a new site and return it. The cache + /// may be cleaned if it's gotten too big since the last usage. + /// + private CallSite> GetSite(CallSiteBinder action) { + return GetSiteWorker>>(action, CallSite>.Create); + } + + /// + /// Helper to create to get or create the dynamic site - called by the GetSite methods. + /// + private T GetSiteWorker(CallSiteBinder action, Func ctor) where T : CallSite { + SiteKey sk = new SiteKey(typeof(T), action); + + lock (_sites) { + SiteKey old; + if (!_sites.TryGetValue(sk, out old)) { + SitesCreated++; + if (SitesCreated < 0) { + // overflow, just reset back to zero... + SitesCreated = 0; + LastCleanup = 0; + } + sk.Site = ctor(sk.Action); + _sites[sk] = sk; + } else { + sk = old; + } + + sk.HitCount++; + } + + Cleanup(); + + return (T)sk.Site; + } + + /// + /// Removes items from the cache that have the lowest usage... + /// + private void Cleanup() { + lock (_sites) { + // cleanup only if we have too many sites and we've created a bunch since our last cleanup + if (_sites.Count > CleanupThreshold && (LastCleanup < SitesCreated - CleanupThreshold)) { + LastCleanup = SitesCreated; + + // calculate the average use, remove up to StopCleanupThreshold that are below average. + int totalUse = 0; + foreach (SiteKey sk in _sites.Keys) { + totalUse += sk.HitCount; + } + + int avgUse = totalUse / _sites.Count; + if (avgUse == 1 && _sites.Count > ClearThreshold) { + // we only have a bunch of one-off requests + _sites.Clear(); + return; + } + + List toRemove = null; + foreach (SiteKey sk in _sites.Keys) { + if (sk.HitCount < (avgUse - RemoveThreshold)) { + if (toRemove == null) { + toRemove = new List(); + } + + toRemove.Add(sk); + if (toRemove.Count > StopCleanupThreshold) { + // if we have a setup like weight(100), weight(1), weight(1), weight(1), ... we don't want + // to just run through and remove all of the weight(1)'s. + break; + } + } + + } + + if (toRemove != null) { + foreach (SiteKey sk in toRemove) { + _sites.Remove(sk); + } + + // reset all hit counts so the next time through is fair + // to newly added members which may take precedence. + foreach (SiteKey sk in _sites.Keys) { + sk.HitCount = 0; + } + } + } + } + } + + /// + /// Helper class for tracking all of our unique dynamic sites and their + /// usage patterns. We hash on the combination of the action and site type. + /// + /// We also track the hit count and the key holds the site associated w/ the + /// key. Logically this is a set based upon the action and site-type but we + /// store it in a dictionary. + /// + private class SiteKey : IEquatable { + // the key portion of the data + internal readonly CallSiteBinder Action; + private readonly Type _siteType; + + // not used for equality, used for caching strategy + public int HitCount; + public CallSite Site; + + public SiteKey(Type siteType, CallSiteBinder action) { + Debug.Assert(siteType != null); + Debug.Assert(action != null); + + Action = action; + _siteType = siteType; + } + + [Confined] + public override bool Equals(object obj) { + return Equals(obj as SiteKey); + } + + [Confined] + public override int GetHashCode() { + return Action.GetHashCode() ^ _siteType.GetHashCode(); + } + + #region IEquatable Members + + [StateIndependent] + public bool Equals(SiteKey other) { + if (other == null) return false; + + return other.Action.Equals(Action) && + other._siteType == _siteType; + } + + #endregion +#if DEBUG + [Confined] + public override string ToString() { + return String.Format("{0} {1}", Action.ToString(), HitCount); + } +#endif + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/DynamicRuntimeHostingProvider.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/DynamicRuntimeHostingProvider.cs new file mode 100644 index 0000000000..0d3f0fbaa0 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/DynamicRuntimeHostingProvider.cs @@ -0,0 +1,32 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Text; + +namespace Microsoft.Scripting.Runtime { + /// + /// DLR requires any Hosting API provider to implement this class and provide its instance upon Runtime initialization. + /// DLR calls on it to perform basic host/system dependent operations. + /// + [Serializable] + public abstract class DynamicRuntimeHostingProvider { + /// + /// Abstracts system operations that are used by DLR and could potentially be platform specific. + /// + public abstract PlatformAdaptationLayer PlatformAdaptationLayer { get; } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/DynamicStackFrame.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/DynamicStackFrame.cs new file mode 100644 index 0000000000..d735c3fd47 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/DynamicStackFrame.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; + +namespace Microsoft.Scripting.Runtime { + /// + /// Helper for storing information about stack frames. + /// + public class DynamicStackFrame { + private CodeContext _context; + private string _funcName; + private string _filename; + private int _lineNo; + private MethodBase _method; + + public DynamicStackFrame(CodeContext context, MethodBase method, string funcName, string filename, int line) { + _context = context; + _funcName = funcName; + _filename = filename; + _lineNo = line; + _method = method; + } + + public CodeContext CodeContext { + get { + return _context; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public MethodBase GetMethod() { + return _method; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public string GetMethodName() { + return _funcName; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public string GetFileName() { + return _filename; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public int GetFileLineNumber() { + return _lineNo; + } + + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ExceptionHelpers.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ExceptionHelpers.cs new file mode 100644 index 0000000000..fc78f246d4 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ExceptionHelpers.cs @@ -0,0 +1,278 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Dynamic; +using System.Threading; + +namespace Microsoft.Scripting.Runtime { + /// + /// These are some generally useful helper methods for handling exceptions. + /// + public static class ExceptionHelpers { + private const string prevStackTraces = "PreviousStackTraces"; + + /// + /// Keeps track of exceptions being handled in interpreted mode (so we can support rethrow statements). + /// + [ThreadStatic] + internal static List _caughtExceptions; + + [ThreadStatic] + private static List _currentExceptions; + + [ThreadStatic] + private static List _stackFrames; + + /// + /// Gets the list of exceptions that are currently being handled by the user. + /// + /// These represent active catch blocks on the stack. + /// + public static List CurrentExceptions { + get { + return _currentExceptions; + } + } + + public static Exception LastException { + get { + if (_caughtExceptions != null && _caughtExceptions.Count > 0) { + return _caughtExceptions[_caughtExceptions.Count - 1]; + } else { + throw Error.NoException(); + } + } + } + + /// + /// Updates an exception before it's getting re-thrown so + /// we can present a reasonable stack trace to the user. + /// + public static Exception UpdateForRethrow(Exception rethrow) { +#if !SILVERLIGHT + List prev; + + StackTrace st = new StackTrace(rethrow, true); + + if (!TryGetAssociatedStackTraces(rethrow, out prev)) { + prev = new List(); + AssociateStackTraces(rethrow, prev); + } + + prev.Add(st); + +#endif + return rethrow; + } + + private static void AssociateStackTraces(Exception e, List traces) { + e.Data[prevStackTraces] = traces; + } + + private static bool TryGetAssociatedStackTraces(Exception e, out List traces) { + traces = e.Data[prevStackTraces] as List; + return traces != null; + } + + /// + /// Returns all the stack traces associates with an exception + /// + public static IList GetExceptionStackTraces(Exception rethrow) { + List result; + return TryGetAssociatedStackTraces(rethrow, out result) ? result : null; + } + + public static void UpdateStackTrace(CodeContext context, MethodBase method, string funcName, string filename, int line) { + Debug.Assert(filename != null); + if (_stackFrames == null) _stackFrames = new List(); + + Debug.Assert(line != SourceLocation.None.Line); + + _stackFrames.Add(new DynamicStackFrame(context, method, funcName, filename, line)); + } + + public static List AssociateDynamicStackFrames(Exception clrException) { + if (_stackFrames != null) { + clrException.Data[typeof(DynamicStackFrame)] = _stackFrames; + } + return _stackFrames; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public static List DynamicStackFrames { + get { + return _stackFrames; + } + set { + _stackFrames = value; + } + } + + public static void PushExceptionHandler(Exception clrException) { + // _currentExceptions is thread static + if (_currentExceptions == null) { + _currentExceptions = new List(); + } + _currentExceptions.Add(clrException); + + AssociateDynamicStackFrames(clrException); + } + + public static void PopExceptionHandler() { + // _currentExceptions is thread static + Debug.Assert(_currentExceptions != null); + Debug.Assert(_currentExceptions.Count != 0); + +#if !SILVERLIGHT + ThreadAbortException tae = _currentExceptions[_currentExceptions.Count - 1] as ThreadAbortException; + if (tae != null && tae.ExceptionState is KeyboardInterruptException) { + Thread.ResetAbort(); + } +#endif + _currentExceptions.RemoveAt(_currentExceptions.Count - 1); + } + + + #region stack frame filtering + + /// + /// Walks all stack frames, filtering out DLR frames + /// Does not walk the frames in the InnerException, if any + /// Frames are returned in CLR order (inner to outer) + /// + public static IEnumerable GetStackFrames(Exception e) { + return GetStackFrames(e, false); + } + + /// + /// Walks all stack frames, filtering out DLR frames + /// Does not walk the frames in the InnerException, if any + /// Frames are returned in CLR order (inner to outer), unless reverse is set + /// + public static IEnumerable GetStackFrames(Exception e, bool reverseOrder) { + IList traces = ExceptionHelpers.GetExceptionStackTraces(e); + List dynamicFrames = new List(ScriptingRuntimeHelpers.GetDynamicStackFrames(e)); + // dynamicFrames is stored in the opposite order that we are walking, + // so we can always pop them from the back of the List, which is O(1) + if (!reverseOrder) { + dynamicFrames.Reverse(); + } + + if (traces != null) { + foreach (StackTrace trace in WalkList(traces, reverseOrder)) { + foreach (DynamicStackFrame result in GetStackFrames(trace, dynamicFrames, reverseOrder)) { + yield return result; + } + } + } + + // TODO: new StackTrace(...) is SecurityCritical in Silverlight +#if SILVERLIGHT + foreach (DynamicStackFrame result in dynamicFrames) { + yield return result; + } +#else + foreach (DynamicStackFrame result in GetStackFrames(new StackTrace(e, true), dynamicFrames, reverseOrder)) { + yield return result; + } +#endif + + //TODO: we would like to be able to assert this; + // right now, we cannot, because we are not using dynamic frames for non-interpreted dynamic methods. + // (we create the frames, but we do not consume them in FormatStackTrace.) + //Debug.Assert(dynamicFrames.Count == 0); + } + + private static IEnumerable WalkList(IList list, bool reverseOrder) { + if (reverseOrder) { + for (int i = list.Count - 1; i >= 0; i--) { + yield return list[i]; + } + } else { + for (int i = 0; i < list.Count; i++) { + yield return list[i]; + } + } + } + + private static IEnumerable GetStackFrames(StackTrace trace, List dynamicFrames, bool reverseOrder) { + StackFrame[] frames = trace.GetFrames(); + if (frames == null) { + yield break; + } + + foreach (StackFrame frame in WalkList(frames, reverseOrder)) { + MethodBase method = frame.GetMethod(); + Type parentType = method.DeclaringType; + if (parentType != null) { + string typeName = parentType.FullName; + if (typeName == "System.Linq.Expressions.LambdaExpression" && method.Name == "DoExecute") { + // Evaluated frame -- Replace with dynamic frame + Debug.Assert(dynamicFrames.Count > 0); + //if (dynamicFrames.Count == 0) continue; + yield return dynamicFrames[dynamicFrames.Count - 1]; + + dynamicFrames.RemoveAt(dynamicFrames.Count - 1); + continue; + } + if (typeName.StartsWith("System.Reflection.") || + typeName.StartsWith("System.Runtime") || + typeName.StartsWith("Microsoft.Scripting") || // TODO: This shouldn't be here!! + typeName.StartsWith("System.Dynamic")) { + continue; + } + } + // filter out the _stub_ methods + if(method.Name.StartsWith("_stub_")) { + continue; + } + + if (dynamicFrames.Count > 0 && frame.GetMethod() == dynamicFrames[dynamicFrames.Count - 1].GetMethod()) { + yield return dynamicFrames[dynamicFrames.Count - 1]; + dynamicFrames.RemoveAt(dynamicFrames.Count - 1); + } else { + yield return GetStackFrame(frame); + } + } + } + + private static DynamicStackFrame GetStackFrame(StackFrame frame) { + MethodBase method = frame.GetMethod(); + string methodName = method.Name; + string filename = frame.GetFileName(); + int line = frame.GetFileLineNumber(); + + int dollar = method.Name.IndexOf('$'); + if (dollar != -1) { + methodName = methodName.Substring(0, dollar); + } + + if (String.IsNullOrEmpty(filename)) { + if (method.DeclaringType != null) { + filename = method.DeclaringType.Assembly.GetName().Name; + line = 0; + } + } + + return new DynamicStackFrame(null, method, methodName, filename, line); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ExplicitConversionMethodAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ExplicitConversionMethodAttribute.cs new file mode 100644 index 0000000000..f1478e5d16 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ExplicitConversionMethodAttribute.cs @@ -0,0 +1,24 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Runtime { + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class ExplicitConversionMethodAttribute : Attribute { + public ExplicitConversionMethodAttribute() { } + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/Extensible.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/Extensible.cs new file mode 100644 index 0000000000..49f6e2d311 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/Extensible.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Runtime { + public class Extensible { + private T _value; + + public Extensible() { } + public Extensible(T value) { this._value = value; } + + public T Value { + get { return _value; } + } + + [Confined] + public override bool Equals(object obj) { + return _value.Equals(obj); + } + + [Confined] + public override int GetHashCode() { + return _value.GetHashCode(); + } + + [Confined] + public override string ToString() { + return _value.ToString(); + } + + public static implicit operator T(Extensible extensible) { + return extensible.Value; + } + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ExtensionTypeAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ExtensionTypeAttribute.cs new file mode 100644 index 0000000000..0aeec8e5ea --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ExtensionTypeAttribute.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Runtime { + /// + /// Marks a class in the assembly as being an extension type for another type. + /// + [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = true)] + public sealed class ExtensionTypeAttribute : Attribute { + private readonly Type _extensionType; + private readonly Type _extends; + + /// + /// Marks a type in the assembly as being an extension type for another type. + /// + /// The type which is being extended + /// The type which provides the extension members. + public ExtensionTypeAttribute(Type extends, Type extensionType) { + if (extends == null) { + throw new ArgumentNullException("extends"); + } + if (extensionType != null && !extensionType.IsPublic && !extensionType.IsNestedPublic) { + throw Error.ExtensionMustBePublic(extensionType.FullName); + } + + _extends = extends; + _extensionType = extensionType; + } + + /// + /// The type which contains extension members which are added to the type being extended. + /// + public Type ExtensionType { + get { + return _extensionType; + } + } + + /// + /// The type which is being extended by the extension type. + /// + public Type Extends { + get { + return _extends; + } + } + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ExtraKeyEnumerator.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ExtraKeyEnumerator.cs new file mode 100644 index 0000000000..3547cdeaeb --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ExtraKeyEnumerator.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + class ExtraKeyEnumerator : CheckedDictionaryEnumerator { + private CustomSymbolDictionary _idDict; + private int _curIndex = -1; + + public ExtraKeyEnumerator(CustomSymbolDictionary idDict) { + _idDict = idDict; + } + + protected override object GetKey() { + return SymbolTable.IdToString(_idDict.GetExtraKeys()[_curIndex]); + } + + protected override object GetValue() { + object val; + bool hasExtraValue = _idDict.TryGetExtraValue(_idDict.GetExtraKeys()[_curIndex], out val); + Debug.Assert(hasExtraValue && !(val is Uninitialized)); + return val; + } + + protected override bool DoMoveNext() { + if (_idDict.GetExtraKeys().Length == 0) + return false; + + while (_curIndex < (_idDict.GetExtraKeys().Length - 1)) { + _curIndex++; + if (_idDict.GetExtraKeys()[_curIndex].Id < 0) break; + + object val; + if (_idDict.TryGetExtraValue(_idDict.GetExtraKeys()[_curIndex], out val) && val != Uninitialized.Instance) { + return true; + } + } + return false; + } + + protected override void DoReset() { + _curIndex = -1; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/Generator.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/Generator.cs new file mode 100644 index 0000000000..2e56ae5e28 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/Generator.cs @@ -0,0 +1,140 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Scripting.Ast; + +// Suppress these for GeneratorNext. It's not a public API for users so the warning is irrelevant +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#", Scope = "member", Target = "Microsoft.Scripting.Runtime.GeneratorNext`1.#Invoke(System.Int32&,!0&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#", Scope = "member", Target = "Microsoft.Scripting.Runtime.GeneratorNext`1.#Invoke(System.Int32&,!0&)")] + +namespace Microsoft.Scripting.Runtime { + public interface IDebuggableGenerator { + int YieldMarkerLocation { get; set; } + } + + // TODO: does this need to be public? + public delegate void GeneratorNext(ref int state, ref T current); + + internal class GeneratorEnumerable : IEnumerable { + protected readonly Func> _next; + + internal GeneratorEnumerable(Func> next) { + _next = next; + } + + IEnumerator IEnumerable.GetEnumerator() { + return new GeneratorEnumerator(_next()); + } + IEnumerator IEnumerable.GetEnumerator() { + return ((IEnumerable)this).GetEnumerator(); + } + } + + internal class GeneratorEnumerator : IEnumerator { + private readonly GeneratorNext _next; + private T _current; + protected int _state; + + internal GeneratorEnumerator(GeneratorNext next) { + _next = next; + _state = GeneratorRewriter.NotStarted; + } + + T IEnumerator.Current { + get { return _current; } + } + + bool IEnumerator.MoveNext() { + _next(ref _state, ref _current); + return _state != GeneratorRewriter.Finished; + } + + object IEnumerator.Current { + get { return ((IEnumerator)this).Current; } + } + + void IEnumerator.Reset() { + throw new NotSupportedException(); + } + + void IDisposable.Dispose() { + // TODO: call back into MoveNext, running any finally blocks that + // have not been run. This is needed for a complete generator + // implementation but is not needed yet. + + GC.SuppressFinalize(this); + } + } + + internal sealed class DebugGeneratorEnumerable : GeneratorEnumerable, IEnumerable { + private readonly int[] _yieldMarkers; + internal DebugGeneratorEnumerable(Func> next, int[] yieldMarkers) + : base(next) { + _yieldMarkers = yieldMarkers; + } + IEnumerator IEnumerable.GetEnumerator() { + return new DebugGeneratorEnumerator(_next(), _yieldMarkers); + } + } + + internal sealed class DebugGeneratorEnumerator : GeneratorEnumerator, IDebuggableGenerator { + private readonly int[] _yieldMarkers; + internal DebugGeneratorEnumerator(GeneratorNext next, int[] yieldMarkers) + : base(next) { + _yieldMarkers = yieldMarkers; + } + + int IDebuggableGenerator.YieldMarkerLocation { + get { + if (_state < _yieldMarkers.Length) + return _yieldMarkers[_state]; + + throw new InvalidOperationException("unknown yield marker"); + } + set { + for (int i = 0; i < _yieldMarkers.Length; i++) { + if (_yieldMarkers[i] == value) { + _state = i; + return; + } + } + + throw new InvalidOperationException("unknown yield marker"); + } + } + } + + public partial class ScriptingRuntimeHelpers { + [Obsolete("do not call this method", true)] + public static IEnumerable MakeGenerator(Func> next) { + return new GeneratorEnumerable(next); + } + [Obsolete("do not call this method", true)] + public static IEnumerator MakeGenerator(GeneratorNext next) { + return new GeneratorEnumerator(next); + } + [Obsolete("do not call this method", true)] + public static IEnumerable MakeGenerator(Func> next, int[] yieldMarkers) { + return new DebugGeneratorEnumerable(next, yieldMarkers); + } + [Obsolete("do not call this method", true)] + public static IEnumerator MakeGenerator(GeneratorNext next, int[] yieldMarkers) { + return new DebugGeneratorEnumerator(next, yieldMarkers); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/GlobalsDictionary.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/GlobalsDictionary.cs new file mode 100644 index 0000000000..37569afb64 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/GlobalsDictionary.cs @@ -0,0 +1,107 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Runtime { + /// + /// Intended for internal use to initialization optimized module dictionaries. Exposed publicly because + /// generated types implement this interface. + /// + public interface IModuleDictionaryInitialization { + void InitializeModuleDictionary(CodeContext context); + } + + /// + /// Dictionary backed by an array used for collectable globals. See also + /// GlobalArrayRewriter + /// + public sealed class GlobalsDictionary : CustomSymbolDictionary, IModuleDictionaryInitialization { + private readonly ModuleGlobalWrapper[] _data; + private readonly SymbolId[] _names; + + // lazily created mapping + private Dictionary _indexes; + + public GlobalsDictionary(SymbolId[] names) { + Debug.Assert(names != null); + + _data = new ModuleGlobalWrapper[names.Length]; + _names = names; + } + + internal ModuleGlobalWrapper[] Data { + get { return _data; } + } + + public override SymbolId[] GetExtraKeys() { + return _names; + } + + protected internal override bool TryGetExtraValue(SymbolId key, out object value) { + EnsureIndexes(); + + int index; + if (_indexes.TryGetValue(key, out index)) { + object raw = _data[index].RawValue; + if (raw != Uninitialized.Instance) { + value = raw; + return true; + } + } + + value = null; + return false; + } + + protected internal override bool TrySetExtraValue(SymbolId key, object value) { + EnsureIndexes(); + + int index; + if (_indexes.TryGetValue(key, out index)) { + _data[index].CurrentValue = value; + return true; + } + return false; + } + + private void EnsureIndexes() { + if (_indexes == null) { + int count = _names.Length; + Dictionary indexes = new Dictionary(count); + for (int index = 0; index < count; index++) { + indexes[_names[index]] = index; + } + _indexes = indexes; + } + } + + void IModuleDictionaryInitialization.InitializeModuleDictionary(CodeContext context) { + if (_names.Length == 0) { + return; + } + + if (_data[0] != null) { + throw Error.AlreadyInitialized(); + } + + for (int i = 0; i < _names.Length; i++) { + _data[i] = new ModuleGlobalWrapper(context, _names[i]); + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/IDynamicObject.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/IDynamicObject.cs new file mode 100644 index 0000000000..f055cbf181 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/IDynamicObject.cs @@ -0,0 +1,26 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Actions; + +namespace Microsoft.Scripting.Runtime { + /// + /// Old dynamic interop protocol. Will be removed soon. + /// + public interface IOldDynamicObject { + bool GetRule(OldDynamicAction action, CodeContext context, object[] args, RuleBuilder rule); + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/IMembersList.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/IMembersList.cs new file mode 100644 index 0000000000..c3807140e8 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/IMembersList.cs @@ -0,0 +1,30 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; + +namespace Microsoft.Scripting.Runtime { + + /// + /// Provides a list of all the members of an instance. ie. all the keys in the + /// dictionary of the object. Note that it can contain objects that are not strings. + /// + /// Such keys can be added in IronPython using syntax like: + /// obj.__dict__[100] = someOtherObject + /// + public interface IMembersList { + IList GetMemberNames(CodeContext context); + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/IRestrictedMetaObject.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/IRestrictedMetaObject.cs new file mode 100644 index 0000000000..db13fdae55 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/IRestrictedMetaObject.cs @@ -0,0 +1,30 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Dynamic.Binders; + +namespace Microsoft.Scripting.Runtime { + /// + /// Indicates that a MetaObject is already representing a restricted type. Useful + /// when we're already restricted to a known type but this isn't captured in + /// the type info (e.g. the type is not sealed). + /// + public interface IRestrictedMetaObject { + MetaObject Restrict(Type type); + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ISlice.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ISlice.cs new file mode 100644 index 0000000000..aa4eb063d8 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ISlice.cs @@ -0,0 +1,38 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Runtime { + /// + /// A useful interface for taking slices of numeric arrays, inspired by Python's Slice objects. + /// + public interface ISlice { + /// + /// The starting index of the slice or null if no first index defined + /// + object Start { get; } + + /// + /// The ending index of the slice or null if no ending index defined + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Stop")] // TODO: fix + object Stop { get; } + + /// + /// The length of step to take + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Step")] // TODO: fix + object Step { get; } + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/IdDispenser.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/IdDispenser.cs new file mode 100644 index 0000000000..b7c4aa5e9e --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/IdDispenser.cs @@ -0,0 +1,189 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Contracts; +using SRC = System.Runtime.CompilerServices; + +namespace Microsoft.Scripting.Runtime { + public static class IdDispenser { + // The one and only comparer instance. + private static readonly IEqualityComparer _comparer = new WrapperComparer(); + [MultiRuntimeAware] + private static Dictionary _hashtable = new Dictionary(_comparer); + private static readonly Object _synchObject = new Object(); // The one and only global lock instance. + // We do not need to worry about duplicates that to using long for unique Id. + // It takes more than 100 years to overflow long on year 2005 hardware. + [MultiRuntimeAware] + private static long _currentId = 42; // Last unique Id we have given out. + +#if !SILVERLIGHT // GC.CollectionCount + // cleanupId and cleanupGC are used for efficient scheduling of hashtable cleanups + [MultiRuntimeAware] + private static long _cleanupId; // currentId at the time of last cleanup + [MultiRuntimeAware] + private static int _cleanupGC; // GC.CollectionCount(0) at the time of last cleanup +#endif + /// + /// Given an ID returns the object associated with that ID. + /// + public static object GetObject(long id) { + lock (_synchObject) { + foreach (Wrapper w in _hashtable.Keys) { + if (w.Target != null) { + if (w.Id == id) return w.Target; + } + } + return null; + } + } + + /// + /// Gets a unique ID for an object + /// + public static long GetId(Object o) { + if (o == null) + return 0; + + lock (_synchObject) { + // If the object exists then return it's existing ID. + object res; + if (_hashtable.TryGetValue(o, out res)) { + return ((Wrapper)res).Id; + } + + long uniqueId = checked(++_currentId); + +#if !SILVERLIGHT // GC.CollectionCount + long change = uniqueId - _cleanupId; + + // Cleanup the table if it is a while since we have done it last time. + // Take the size of the table into account. + if (change > 1234 + _hashtable.Count / 2) { + // It makes sense to do the cleanup only if a GC has happened in the meantime. + // WeakReferences can become zero only during the GC. + int currentGC = GC.CollectionCount(0); + if (currentGC != _cleanupGC) { + Cleanup(); + + _cleanupId = uniqueId; + _cleanupGC = currentGC; + } else { + _cleanupId += 1234; + } + } +#endif + Wrapper w = new Wrapper(o, uniqueId); + _hashtable[w] = w; + + return uniqueId; + } + } + + /// + /// Goes over the hashtable and removes empty entries + /// + private static void Cleanup() { + int liveCount = 0; + int emptyCount = 0; + + foreach (Wrapper w in _hashtable.Keys) { + if (w.Target != null) + liveCount++; + else + emptyCount++; + } + + // Rehash the table if there is a significant number of empty slots + if (emptyCount > liveCount / 4) { + Dictionary newtable = new Dictionary(liveCount + liveCount / 4, _comparer); + + foreach (Wrapper w in _hashtable.Keys) { + if (w.Target != null) + newtable[w] = w; + } + + _hashtable = newtable; + } + } + + /// + /// Weak-ref wrapper caches the weak reference, our hash code, and the object ID. + /// + private sealed class Wrapper { + private WeakReference _weakReference; + private int _hashCode; + private long _id; + + public Wrapper(Object obj, long uniqueId) { + _weakReference = new WeakReference(obj, true); + + _hashCode = (obj == null) ? 0 : SRC.RuntimeHelpers.GetHashCode(obj); + _id = uniqueId; + } + + public long Id { + get { + return _id; + } + } + + public Object Target { + get { + return _weakReference.Target; + } + } + + [Confined] + public override int GetHashCode() { + return _hashCode; + } + } + + /// + /// WrapperComparer treats Wrapper as transparent envelope + /// + private sealed class WrapperComparer : IEqualityComparer { + bool IEqualityComparer.Equals(Object x, Object y) { + + Wrapper wx = x as Wrapper; + if (wx != null) + x = wx.Target; + + Wrapper wy = y as Wrapper; + if (wy != null) + y = wy.Target; + + return Object.ReferenceEquals(x, y); + } + + int IEqualityComparer.GetHashCode(Object obj) { + + Wrapper wobj = obj as Wrapper; + if (wobj != null) + return wobj.GetHashCode(); + + return GetHashCodeWorker(obj); + } + + private static int GetHashCodeWorker(object o) { + if (o == null) return 0; + return SRC.RuntimeHelpers.GetHashCode(o); + } + } + + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ImplicitConversionMethodAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ImplicitConversionMethodAttribute.cs new file mode 100644 index 0000000000..940b17e591 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ImplicitConversionMethodAttribute.cs @@ -0,0 +1,24 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Runtime { + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class ImplicitConversionMethodAttribute : Attribute { + public ImplicitConversionMethodAttribute() { } + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/InvariantContext.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/InvariantContext.cs new file mode 100644 index 0000000000..e36dca1a85 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/InvariantContext.cs @@ -0,0 +1,41 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Runtime { + /// + /// Singleton LanguageContext which represents a language-neutral LanguageContext + /// + internal sealed class InvariantContext : LanguageContext { + // friend: ScriptDomainManager + internal InvariantContext(ScriptDomainManager manager) + : base(manager) { + // TODO: use InvariantBinder + Binder = new DefaultActionBinder(manager, Type.EmptyTypes); + } + + public override bool CanCreateSourceCode { + get { return false; } + } + + protected internal override ScriptCode CompileSourceCode(SourceUnit sourceUnit, CompilerOptions options, ErrorSink errorSink) { + // invariant language doesn't have a grammar: + throw new NotSupportedException(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/LanguageBoundTextContentProvider.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/LanguageBoundTextContentProvider.cs new file mode 100644 index 0000000000..f71202614c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/LanguageBoundTextContentProvider.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.IO; +using System.Dynamic; +using System.Text; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + /// + /// Internal class which binds a LanguageContext, StreamContentProvider, and Encoding together to produce + /// a TextContentProvider which reads binary data with the correct language semantics. + /// + internal sealed class LanguageBoundTextContentProvider : TextContentProvider { + private readonly LanguageContext _context; + private readonly StreamContentProvider _streamProvider; + private readonly Encoding _defaultEncoding; + + public LanguageBoundTextContentProvider(LanguageContext context, StreamContentProvider streamProvider, Encoding defaultEncoding) { + Assert.NotNull(context, streamProvider, defaultEncoding); + _context = context; + _streamProvider = streamProvider; + _defaultEncoding = defaultEncoding; + } + + public override SourceCodeReader GetReader() { + return _context.GetSourceReader(_streamProvider.GetStream(), _defaultEncoding); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/LanguageContext.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/LanguageContext.cs new file mode 100644 index 0000000000..fc3751e53b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/LanguageContext.cs @@ -0,0 +1,706 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Dynamic.Binders; +using System.Text; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Interpretation; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + /// + /// Provides language specific facilities which are typicalled called by the runtime. + /// + public abstract class LanguageContext { + private readonly ScriptDomainManager _domainManager; + private static readonly ModuleGlobalCache _noCache = new ModuleGlobalCache(ModuleGlobalCache.NotCaching); + private ActionBinder _binder; + private readonly ContextId _id; + + protected LanguageContext(ScriptDomainManager domainManager) { + ContractUtils.RequiresNotNull(domainManager, "domainManager"); + + _domainManager = domainManager; + _id = domainManager.GenerateContextId(); + } + + public ActionBinder Binder { + get { + return _binder; + } + protected set { + _binder = value; + } + } + + /// + /// Provides the ContextId which includes members that should only be shown for this LanguageContext. + /// + /// ContextId's are used for filtering by Scope's. + /// + public ContextId ContextId { + get { return _id; } + } + + /// + /// Gets the ScriptDomainManager that this LanguageContext is running within. + /// + public ScriptDomainManager DomainManager { + get { return _domainManager; } + } + + /// + /// Whether the language can parse code and create source units. + /// + public virtual bool CanCreateSourceCode { + get { return true; } + } + + #region Scope + + public virtual Scope GetScope(string path) { + return null; + } + + public ScopeExtension GetScopeExtension(Scope scope) { + ContractUtils.RequiresNotNull(scope, "scope"); + return scope.GetExtension(ContextId); + } + + public ScopeExtension EnsureScopeExtension(Scope scope) { + ContractUtils.RequiresNotNull(scope, "scope"); + ScopeExtension extension = scope.GetExtension(ContextId); + + if (extension == null) { + extension = CreateScopeExtension(scope); + if (extension == null) { + throw Error.MustReturnScopeExtension(); + } + return scope.SetExtension(ContextId, extension); + } + + return extension; + } + + /// + /// Factory for ModuleContext creation. + /// It is guaranteed that this method is called once per each ScriptScope the language code is executed within. + /// + public virtual ScopeExtension CreateScopeExtension(Scope scope) { + return new ScopeExtension(scope); + } + + #endregion + + #region Source Code Parsing & Compilation + + /// + /// Provides a text reader for source code that is to be read from a given stream. + /// + /// The stream open for reading. The stream must also allow seeking. + /// An encoding that should be used if the stream doesn't have Unicode or language specific preamble. + /// The reader. + /// An I/O error occurs. + public virtual SourceCodeReader GetSourceReader(Stream stream, Encoding defaultEncoding) { + ContractUtils.RequiresNotNull(stream, "stream"); + ContractUtils.RequiresNotNull(defaultEncoding, "defaultEncoding"); + ContractUtils.Requires(stream.CanRead && stream.CanSeek, "stream", "The stream must support reading and seeking"); + + var result = new StreamReader(stream, defaultEncoding, true); + result.Peek(); + return new SourceCodeReader(result, result.CurrentEncoding); + } + + /// + /// Creates the language specific CompilerContext object for code compilation. The + /// language should flow any relevant options from the LanguageContext to the + /// newly created CompilerContext. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] // TODO: fix + public virtual CompilerOptions GetCompilerOptions() { + return new CompilerOptions(); + } + + /// + /// Creates compiler options initialized by the options associated with the module. + /// + public virtual CompilerOptions GetCompilerOptions(Scope scope) { + return GetCompilerOptions(); + } + + /// + /// Parses the source code within a specified compiler context. + /// The source unit to parse is held on by the context. + /// + /// null on failure. + /// Could also set the code properties and line/file mappings on the source unit. + internal protected abstract ScriptCode CompileSourceCode(SourceUnit sourceUnit, CompilerOptions options, ErrorSink errorSink); + + internal protected virtual ScriptCode LoadCompiledCode(DlrMainCallTarget method, string path) { + return ScriptCode.Load(method, this, path); + } + + #endregion + + + /// + /// Looks up the name in the provided Scope using the current language's semantics. + /// + public virtual bool TryLookupName(CodeContext context, SymbolId name, out object value) { + if (context.Scope.TryLookupName(this, name, out value) && value != Uninitialized.Instance) { + return true; + } + + return TryLookupGlobal(context, name, out value); + } + + /// + /// Looks up the name in the provided scope using the current language's semantics. + /// + /// If the name cannot be found throws the language appropriate exception or returns + /// the language's appropriate default value. + /// + public virtual object LookupName(CodeContext context, SymbolId name) { + object value; + if (!TryLookupName(context, name, out value) || value == Uninitialized.Instance) { + throw MissingName(name); + } + + return value; + } + + /// + /// Attempts to set the name in the provided scope using the current language's semantics. + /// + public virtual void SetName(CodeContext context, SymbolId name, object value) { + context.Scope.SetName(name, value); + } + + /// + /// Attempts to remove the name from the provided scope using the current language's semantics. + /// + public virtual bool RemoveName(CodeContext context, SymbolId name) { + return context.Scope.RemoveName(this, name); + } + + /// + /// Attemps to lookup a global variable using the language's semantics called from + /// the provided Scope. The default implementation will attempt to lookup the variable + /// at the host level. + /// + public virtual bool TryLookupGlobal(CodeContext context, SymbolId name, out object value) { + value = null; + return false; + } + + /// + /// Called when a lookup has failed and an exception should be thrown. Enables the + /// language context to throw the appropriate exception for their language when + /// name lookup fails. + /// + protected internal virtual Exception MissingName(SymbolId name) { + return Error.NameNotDefined(SymbolTable.IdToString(name)); + } + + /// + /// Returns a ModuleGlobalCache for the given name. + /// + /// This cache enables fast access to global values when a SymbolId is not defined after searching the Scope's. Usually + /// a language implements lookup of the global value via TryLookupGlobal. When GetModuleCache returns a ModuleGlobalCache + /// a cached value can be used instead of calling TryLookupGlobal avoiding a possibly more expensive lookup from the + /// LanguageContext. The ModuleGlobalCache can be held onto and have its value updated when the cache is invalidated. + /// + /// By default this returns a cache which indicates no caching should occur and the LanguageContext will be + /// consulted when a module value is not available. If a LanguageContext only caches some values it can return + /// the value from the base method when the value should not be cached. + /// + protected internal virtual ModuleGlobalCache GetModuleCache(SymbolId name) { + return _noCache; + } + + /// + /// Calls the function with given arguments + /// + /// + /// The function to call + /// The argumetns with which to call the function. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Call")] // TODO: fix + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Function")] // TODO: fix + public virtual object Call(CodeContext context, object function, object[] args) { + return null; + } + + /// + /// Calls the function with instance as the "this" value. + /// + /// + /// The function to call + /// The instance to pass as "this". + /// The rest of the arguments. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Function")] // TODO: fix + public virtual object CallWithThis(CodeContext context, object function, object instance, object[] args) { + return null; + } + + /// + /// Calls the function with arguments, extra arguments in tuple and dictionary of keyword arguments + /// + /// + /// The function to call + /// The arguments + /// Argument names + /// tuple of extra arguments + /// keyword dictionary + /// The result of the function call. + public virtual object CallWithArgsKeywordsTupleDict(CodeContext context, object func, object[] args, string[] names, object argsTuple, object kwDict) { + return null; + } + + /// + /// Calls function with arguments and additional arguments in the tuple + /// + /// + /// The function to call + /// Argument array + /// Tuple with extra arguments + /// The result of calling the function "func" + public virtual object CallWithArgsTuple(CodeContext context, object func, object[] args, object argsTuple) { + return null; + } + + /// + /// Calls the function with arguments, some of which are keyword arguments. + /// + /// + /// Function to call + /// Argument array + /// Names for some of the arguments + /// The result of calling the function "func" + public virtual object CallWithKeywordArgs(CodeContext context, object func, object[] args, string[] names) { + return null; + } + + // used only by ReflectedEvent.HandlerList + public virtual bool EqualReturnBool(CodeContext context, object x, object y) { + return false; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFile")] + public virtual Assembly LoadAssemblyFromFile(string file) { +#if SILVERLIGHT + return null; +#else + return Assembly.LoadFile(file); +#endif + } + + #region ScriptEngine API + + public virtual Version LanguageVersion { + get { + return new Version(0, 0); + } + } + + public virtual void SetSearchPaths(ICollection paths) { + throw new NotSupportedException(); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public virtual ICollection GetSearchPaths() { + return Options.SearchPaths; + } + +#if !SILVERLIGHT + // Convert a CodeDom to source code, and output the generated code and the line number mappings (if any) + public virtual SourceUnit GenerateSourceCode(System.CodeDom.CodeObject codeDom, string path, SourceCodeKind kind) { + throw new NotImplementedException(); + } +#endif + + public virtual TService GetService(params object[] args) where TService : class { + return null; + } + + //TODO these three properties should become abstract and updated for all implementations + public virtual Guid LanguageGuid { + get { + return Guid.Empty; + } + } + + public virtual Guid VendorGuid { + get { + return Guid.Empty; + } + } + + public virtual void Shutdown() { + } + + public virtual string FormatException(Exception exception) { + return exception.ToString(); + } + + public virtual Microsoft.Scripting.LanguageOptions Options { + get { + return new Microsoft.Scripting.LanguageOptions(); + } + } + + #region Source Units + + public SourceUnit CreateSnippet(string code, SourceCodeKind kind) { + return CreateSnippet(code, null, kind); + } + + public SourceUnit CreateSnippet(string code, string id, SourceCodeKind kind) { + ContractUtils.RequiresNotNull(code, "code"); + + return CreateSourceUnit(new SourceStringContentProvider(code), id, kind); + } + + public SourceUnit CreateFileUnit(string path) { + return CreateFileUnit(path, StringUtils.DefaultEncoding); + } + + public SourceUnit CreateFileUnit(string path, Encoding encoding) { + return CreateFileUnit(path, encoding, SourceCodeKind.File); + } + + public SourceUnit CreateFileUnit(string path, Encoding encoding, SourceCodeKind kind) { + ContractUtils.RequiresNotNull(path, "path"); + ContractUtils.RequiresNotNull(encoding, "encoding"); + + TextContentProvider provider = new LanguageBoundTextContentProvider(this, new FileStreamContentProvider(DomainManager.Platform, path), encoding); + return CreateSourceUnit(provider, path, kind); + } + + public SourceUnit CreateFileUnit(string path, string content) { + ContractUtils.RequiresNotNull(path, "path"); + ContractUtils.RequiresNotNull(content, "content"); + + TextContentProvider provider = new SourceStringContentProvider(content); + return CreateSourceUnit(provider, path, SourceCodeKind.File); + } + + public SourceUnit CreateSourceUnit(StreamContentProvider contentProvider, string path, Encoding encoding, SourceCodeKind kind) { + ContractUtils.RequiresNotNull(contentProvider, "contentProvider"); + ContractUtils.RequiresNotNull(encoding, "encoding"); + ContractUtils.Requires(path == null || path.Length > 0, "path", Strings.EmptyStringIsInvalidPath); + ContractUtils.Requires(EnumBounds.IsValid(kind), "kind"); + ContractUtils.Requires(CanCreateSourceCode); + + return new SourceUnit(this, new LanguageBoundTextContentProvider(this, contentProvider, encoding), path, kind); + } + + public SourceUnit CreateSourceUnit(TextContentProvider contentProvider, string path, SourceCodeKind kind) { + ContractUtils.RequiresNotNull(contentProvider, "contentProvider"); + ContractUtils.Requires(path == null || path.Length > 0, "path", Strings.EmptyStringIsInvalidPath); + ContractUtils.Requires(EnumBounds.IsValid(kind), "kind"); + ContractUtils.Requires(CanCreateSourceCode); + + return new SourceUnit(this, contentProvider, path, kind); + } + + #endregion + + #endregion + + private static T GetArg(object[] arg, int index, bool optional) { + if (!optional && index >= arg.Length) { + throw Error.InvalidParamNumForService(); + } + + if (!(arg[index] is T)) { + throw Error.InvalidArgumentType(String.Format("arg[{0}]", index), typeof(T)); + } + + return (T)arg[index]; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public virtual ErrorSink GetCompilerErrorSink() { + return ErrorSink.Null; + } + + public virtual void GetExceptionMessage(Exception exception, out string message, out string errorTypeName) { + message = exception.Message; + errorTypeName = exception.GetType().Name; + } + + public virtual int ExecuteProgram(SourceUnit program) { + ContractUtils.RequiresNotNull(program, "program"); + + object returnValue = program.Execute(); + + CodeContext context = new CodeContext(new Scope(), this); + + CallSite> site = + CallSite>.Create(OldConvertToAction.Make(Binder, typeof(int), ConversionResultKind.ExplicitTry)); + + object exitCode = site.Target(site, context, returnValue); + return (exitCode != null) ? (int)exitCode : 0; + } + + #region Object Operations Support + + internal static MetaObject ErrorMetaObject(MetaObject target, MetaObject[] args, MetaObject onBindingError) { + return onBindingError ?? MetaObject.CreateThrow(target, args, typeof(NotImplementedException), ArrayUtils.EmptyObjects); + } + + public virtual UnaryOperationBinder CreateUnaryOperationBinder(ExpressionType operation) { + return new DefaultUnaryOperationBinder(operation); + } + + private sealed class DefaultUnaryOperationBinder : UnaryOperationBinder { + internal DefaultUnaryOperationBinder(ExpressionType operation) + : base(operation) { + } + + public override MetaObject FallbackUnaryOperation(MetaObject target, MetaObject errorSuggestion) { + return ErrorMetaObject(target, new[] { target }, errorSuggestion); + } + + public override object CacheIdentity { + get { return this; } + } + } + + public virtual BinaryOperationBinder CreateBinaryOperationBinder(ExpressionType operation) { + return new DefaultBinaryOperationBinder(operation); + } + + private sealed class DefaultBinaryOperationBinder : BinaryOperationBinder { + internal DefaultBinaryOperationBinder(ExpressionType operation) + : base(operation) { + } + + public override MetaObject FallbackBinaryOperation(MetaObject target, MetaObject arg, MetaObject errorSuggestion) { + return ErrorMetaObject(target, new[] { target, arg }, errorSuggestion); + } + + public override object CacheIdentity { + get { return this; } + } + } + + [Obsolete("Use UnaryOperation or BinaryOperation")] + private class DefaultOperationAction : OperationBinder { + internal DefaultOperationAction(string operation) + : base(operation) { + } + + public override MetaObject FallbackOperation(MetaObject target, MetaObject[] args, MetaObject onBindingError) { + return ErrorMetaObject(target, args, onBindingError); + } + + public override object CacheIdentity { + get { return this; } + } + } + + [Obsolete("Use UnaryOperation or BinaryOperation")] + public virtual OperationBinder CreateOperationBinder(string operation) { + return new DefaultOperationAction(operation); + } + + private class DefaultConvertAction : ConvertBinder { + internal DefaultConvertAction(Type type, bool @explicit) + : base(type, @explicit) { + } + + public override MetaObject FallbackConvert(MetaObject self, MetaObject onBindingError) { + if (Type.IsAssignableFrom(self.LimitType)) { + return new MetaObject( + self.Expression, + Restrictions.GetTypeRestriction(self.Expression, self.LimitType) + ); + } + + return onBindingError ?? + MetaObject.CreateThrow( + self, + MetaObject.EmptyMetaObjects, + typeof(ArgumentTypeException), + String.Format("Expected {0}, got {1}", Type.FullName, self.LimitType.FullName) + ); + } + + public override object CacheIdentity { + get { return this; } + } + } + + public virtual ConvertBinder CreateConvertBinder(Type toType, bool explicitCast) { + return new DefaultConvertAction(toType, explicitCast); + } + + private class DefaultGetMemberAction : GetMemberBinder { + internal DefaultGetMemberAction(string name, bool ignoreCase) + : base(name, ignoreCase) { + } + + public override MetaObject FallbackGetMember(MetaObject self, MetaObject onBindingError) { + return ErrorMetaObject(self, MetaObject.EmptyMetaObjects, onBindingError); + } + + public override object CacheIdentity { + get { return this; } + } + } + + public virtual GetMemberBinder CreateGetMemberBinder(string name, bool ignoreCase) { + return new DefaultGetMemberAction(name, ignoreCase); + } + + private class DefaultSetMemberAction : SetMemberBinder { + internal DefaultSetMemberAction(string name, bool ignoreCase) + : base(name, ignoreCase) { + } + + public override MetaObject FallbackSetMember(MetaObject self, MetaObject value, MetaObject onBindingError) { + return ErrorMetaObject(self, new MetaObject[] { value }, onBindingError); + } + + public override object CacheIdentity { + get { return this; } + } + } + + public virtual SetMemberBinder CreateSetMemberBinder(string name, bool ignoreCase) { + return new DefaultSetMemberAction(name, ignoreCase); + } + + private class DefaultDeleteMemberAction : DeleteMemberBinder { + internal DefaultDeleteMemberAction(string name, bool ignoreCase) + : base(name, ignoreCase) { + } + + public override MetaObject FallbackDeleteMember(MetaObject self, MetaObject onBindingError) { + return ErrorMetaObject(self, MetaObject.EmptyMetaObjects, onBindingError); + } + + public override object CacheIdentity { + get { return this; } + } + } + + public virtual DeleteMemberBinder CreateDeleteMemberBinder(string name, bool ignoreCase) { + return new DefaultDeleteMemberAction(name, ignoreCase); + } + + private class DefaultCallAction : InvokeMemberBinder { + internal DefaultCallAction(string name, bool ignoreCase, params ArgumentInfo[] arguments) + : base(name, ignoreCase, arguments) { + } + + public override MetaObject FallbackInvokeMember(MetaObject target, MetaObject[] args, MetaObject onBindingError) { + return ErrorMetaObject(target, args.AddFirst(target), onBindingError); + } + + public override MetaObject FallbackInvoke(MetaObject target, MetaObject[] args, MetaObject onBindingError) { + return ErrorMetaObject(target, args.AddFirst(target), onBindingError); + } + + public override object CacheIdentity { + get { return this; } + } + } + + public virtual InvokeMemberBinder CreateCallBinder(string name, bool ignoreCase, params ArgumentInfo[] arguments) { + return new DefaultCallAction(name, ignoreCase, arguments); + } + + private class DefaultInvokeAction : InvokeBinder { + internal DefaultInvokeAction(params ArgumentInfo[] arguments) + : base(arguments) { + } + + public override MetaObject FallbackInvoke(MetaObject target, MetaObject[] args, MetaObject onBindingError) { + return ErrorMetaObject(target, args, onBindingError); + } + + public override object CacheIdentity { + get { return this; } + } + } + + public virtual InvokeBinder CreateInvokeBinder(params ArgumentInfo[] arguments) { + return new DefaultInvokeAction(arguments); + } + + private class DefaultCreateAction : CreateInstanceBinder { + internal DefaultCreateAction(params ArgumentInfo[] arguments) + : base(arguments) { + } + + public override MetaObject FallbackCreateInstance(MetaObject target, MetaObject[] args, MetaObject onBindingError) { + return ErrorMetaObject(target, args, onBindingError); + } + + public override object CacheIdentity { + get { return this; } + } + } + + public virtual CreateInstanceBinder CreateCreateBinder(params ArgumentInfo[] arguments) { + return new DefaultCreateAction(arguments); + } + + #endregion + + /// + /// Called by an interpreter when an exception is about to be thrown by an interpreted or + /// when a CLR method is called that threw an exception. + /// + /// + /// The current interpreted frame state. The frame is either throwing the exception or + /// is the interpreted frame that is calling a CLR method that threw or propagated the exception. + /// + /// The exception to be (re)thrown. + /// Whether the exception is thrown by an interpreted code. + /// + /// The method can be called multiple times for a single exception if the interpreted code calls some CLR code that + /// calls an interpreted code that throws an exception. The method is called at each interpeted/non-interpreted frame boundary + /// and in the frame that raised the exception. + /// + internal protected virtual void InterpretExceptionThrow(InterpreterState state, Exception exception, bool isInterpretedThrow) { + Assert.NotNull(state, exception); + // nop + } + + /// + /// Gets the member names associated with the object + /// By default, only returns IDO names + /// + internal protected virtual IList GetMemberNames(object obj) { + var ido = obj as IDynamicObject; + if (ido != null) { + var mo = ido.GetMetaObject(Expression.Parameter(typeof(object), null)); + return mo.GetDynamicMemberNames().ToReadOnly(); + } + return EmptyArray.Instance; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/LocalsDictionary.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/LocalsDictionary.cs new file mode 100644 index 0000000000..4cce36a1bb --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/LocalsDictionary.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.CompilerServices; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + + /// + /// Creates a dictionary of locals in this scope + /// + public sealed class LocalsDictionary : CustomSymbolDictionary { + private readonly IList _locals; + private readonly SymbolId[] _symbols; + private Dictionary _boxes; + + public LocalsDictionary(IList locals, SymbolId[] symbols) { + Assert.NotNull(locals, symbols); + _locals = locals; + _symbols = symbols; + } + + private void EnsureBoxes() { + if (_boxes == null) { + int count = _symbols.Length; + Dictionary boxes = new Dictionary(count); + for (int i = 0; i < count; i++) { + boxes[_symbols[i]] = _locals[i]; + } + _boxes = boxes; + } + } + + public override SymbolId[] GetExtraKeys() { + return _symbols; + } + + protected internal override bool TrySetExtraValue(SymbolId key, object value) { + EnsureBoxes(); + + IStrongBox box; + if (_boxes.TryGetValue(key, out box)) { + box.Value = value; + return true; + } + + return false; + } + + protected internal override bool TryGetExtraValue(SymbolId key, out object value) { + EnsureBoxes(); + + IStrongBox box; + if (_boxes.TryGetValue(key, out box)) { + value = box.Value; + return true; + } + value = null; + return false; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/MetaObjectExtensions.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/MetaObjectExtensions.cs new file mode 100644 index 0000000000..a11af5557e --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/MetaObjectExtensions.cs @@ -0,0 +1,87 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Dynamic; +using System.Dynamic.Binders; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Utils; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Runtime { + public static class MetaObjectExtensions { + public static bool NeedsDeferral(this MetaObject self) { + if (self.HasValue) { + return false; + } + + if (self.Expression.Type.IsSealedOrValueType()) { + return typeof(IDynamicObject).IsAssignableFrom(self.Expression.Type); + } + + return true; + } + + public static MetaObject Restrict(this MetaObject self, Type type) { + ContractUtils.RequiresNotNull(self, "self"); + ContractUtils.RequiresNotNull(type, "type"); + + IRestrictedMetaObject rmo = self as IRestrictedMetaObject; + if (rmo != null) { + return rmo.Restrict(type); + } + + if (type == self.Expression.Type) { + if (type.IsSealedOrValueType()) { + return self; + } + + if (self.Expression.NodeType == ExpressionType.New || + self.Expression.NodeType == ExpressionType.NewArrayBounds || + self.Expression.NodeType == ExpressionType.NewArrayInit) { + return self; + } + } + + if (type == typeof(Null)) { + return new MetaObject( + Expression.Constant(null), + self.Restrictions.Merge(Restrictions.GetInstanceRestriction(self.Expression, null)), + self.Value + ); + } + + if (self.HasValue) { + return new MetaObject( + AstUtils.Convert( + self.Expression, + CompilerHelpers.GetVisibleType(type) + ), + self.Restrictions.Merge(Restrictions.GetTypeRestriction(self.Expression, type)), + self.Value + ); + } + + return new MetaObject( + AstUtils.Convert( + self.Expression, + CompilerHelpers.GetVisibleType(type) + ), + self.Restrictions.Merge(Restrictions.GetTypeRestriction(self.Expression, type)) + ); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ModuleChangeEventArgs.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ModuleChangeEventArgs.cs new file mode 100644 index 0000000000..4e335ff2cf --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ModuleChangeEventArgs.cs @@ -0,0 +1,69 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Runtime { + /// + /// Event args for when a ScriptScope has had its contents changed. + /// + public class ModuleChangeEventArgs : EventArgs { + private SymbolId _name; + private ModuleChangeType _type; + private object _value; + + /// + /// Creates a new ModuleChangeEventArgs object with the specified name and type. + /// + public ModuleChangeEventArgs(SymbolId name, ModuleChangeType changeType) { + _name = name; + _type = changeType; + } + + /// + /// Creates a nwe ModuleChangeEventArgs with the specified name, type, and changed value. + /// + public ModuleChangeEventArgs(SymbolId name, ModuleChangeType changeType, object value) { + _name = name; + _type = changeType; + _value = value; + } + + /// + /// Gets the name of the symbol that has changed. + /// + public SymbolId Name { + get { + return _name; + } + } + + /// + /// Gets the way in which the symbol has changed: Set or Delete. + /// + public ModuleChangeType ChangeType { + get { + return _type; + } + } + + /// + /// The the symbol has been set provides the new value. + /// + public object Value { + get { return _value; } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ModuleChangeEventType.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ModuleChangeEventType.cs new file mode 100644 index 0000000000..6e6ccf225c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ModuleChangeEventType.cs @@ -0,0 +1,30 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Runtime { + /// + /// The way in which a module has changed : Set or Delete + /// + public enum ModuleChangeType { + /// + /// A new value has been set in the module (or a previous value has changed). + /// + Set, + /// + /// A value has been removed from the module. + /// + Delete, + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ModuleGlobalCache.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ModuleGlobalCache.cs new file mode 100644 index 0000000000..a7923bc929 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ModuleGlobalCache.cs @@ -0,0 +1,84 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using Microsoft.Scripting.Utils; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Runtime { + /// + /// Cached global value. Created and maintained on a per-language basis. Default + /// implementation returns a singleton which indicates caching is not occuring. + /// + public sealed class ModuleGlobalCache { + private object _value; + + internal static readonly object NotCaching = new object(); + + /// + /// Creates a new ModuleGlobalCache with the specified value. + /// + public ModuleGlobalCache(object value) { + _value = value; + } + + /// + /// True if the ModuleGlobalCache is participating in a caching strategy. + /// + public bool IsCaching { + get { + return _value != NotCaching; + } + } + + /// + /// True if their is currently a value associated with this global variable. False if + /// it is currently unassigned. + /// + public bool HasValue { + get { + return _value != Uninitialized.Instance; + } + } + + /// + /// Gets or sets the current cached value + /// + public object Value { + get { + return _value; + } + set { + if (_value == NotCaching) throw Error.CannotChangeNonCachingValue(); + _value = value; + } + } + + /// + /// Event handler for when the value has changed. Language implementors should call this when + /// the cached value is invalidated. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] + public void Changed(object sender, ModuleChangeEventArgs e) { + ContractUtils.RequiresNotNull(e, "e"); + + switch (e.ChangeType) { + case ModuleChangeType.Delete: Value = Uninitialized.Instance; break; + case ModuleChangeType.Set: Value = e.Value; break; + default: Debug.Assert(false, "unknown ModuleChangeType"); break; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ModuleGlobalWrapper.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ModuleGlobalWrapper.cs new file mode 100644 index 0000000000..d901be77c1 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ModuleGlobalWrapper.cs @@ -0,0 +1,109 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Runtime { + /// + /// Provides cached global variable for modules to enable optimized access to + /// module globals. Both the module global value and the cached value can be held + /// onto and the cached value can be invalidated by the providing LanguageContext. + /// + /// The cached value is provided by the LanguageContext.GetModuleCache API. + /// +#if !SILVERLIGHT + [DebuggerDisplay("{Display}")] +#endif + public sealed class ModuleGlobalWrapper { + private object _value; + private ModuleGlobalCache _global; + private SymbolId _name; + private CodeContext _context; + + public ModuleGlobalWrapper(CodeContext context, SymbolId name) { + _value = Uninitialized.Instance; + _context = context; + _global = context.LanguageContext.GetModuleCache(name); + _name = name; + } + + public object CurrentValue { + get { + if (_value != Uninitialized.Instance) return _value; + + return GetCachedValue(); + } + set { + if (value == Uninitialized.Instance && _value == Uninitialized.Instance) { + throw _context.LanguageContext.MissingName(_name); + + } + _value = value; + } + } + + private object GetCachedValue() { + if (_global.IsCaching) { + if (_global.HasValue) return _global.Value; + } else { + object value; + // HACK: Shouldn't look in the GlobalScope here, but need to until JSGlobalObject + // unifies w/ module dictionary. + if (_context.GlobalScope.TryGetName(_context.LanguageContext, _name, out value)) { + return value; + } + + if (_context.LanguageContext.TryLookupGlobal(_context, _name, out value)) { + return value; + } + } + + // TODO: support returning undefined + throw _context.LanguageContext.MissingName(_name); + } + + public object RawValue { + get { + return _value; + } + } + + public string Display { + get { + if (_value != Uninitialized.Instance) return GetStringDisplay(_value); + + if (_global.IsCaching && _global.HasValue) return GetStringDisplay(_global.Value); + object value; + if (_context.LanguageContext.TryLookupGlobal(_context, _name, out value)) + return GetStringDisplay(value); + + return GetStringDisplay(Uninitialized.Instance); + } + } + + private static string GetStringDisplay(object val) { + return val == null ? "(null)" : val.ToString(); + } + + [Confined] + public override string ToString() { + return string.Format("ModuleGlobal: {0} Value: {1} ({2})", + _name, + _value, + RawValue == Uninitialized.Instance ? "Module Local" : "Global"); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/NotNullAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/NotNullAttribute.cs new file mode 100644 index 0000000000..28ecd4f58f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/NotNullAttribute.cs @@ -0,0 +1,29 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Runtime { + /// + /// This attribute marks a parameter that is not allowed to be null. + /// It is used by the method binding infrastructure to generate better error + /// messages and method selection. + /// + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] + public sealed class NotNullAttribute : Attribute { + public NotNullAttribute() { + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/NullTextContentProvider.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/NullTextContentProvider.cs new file mode 100644 index 0000000000..db5dc2cc8a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/NullTextContentProvider.cs @@ -0,0 +1,35 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.IO; +using System.Text; + +namespace Microsoft.Scripting.Runtime { + /// + /// A NullTextContentProvider to be provided when we have a pre-compiled ScriptCode which doesn't + /// have source code associated with it. + /// + public sealed class NullTextContentProvider : TextContentProvider { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly NullTextContentProvider Null = new NullTextContentProvider(); + + private NullTextContentProvider() { + } + + public override SourceCodeReader GetReader() { + return SourceCodeReader.Null; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/OperationFailed.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/OperationFailed.cs new file mode 100644 index 0000000000..afda84c953 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/OperationFailed.cs @@ -0,0 +1,27 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Runtime { + /// + /// Singleton instance returned from an operator method when the operator method cannot provide a value. + /// + public sealed class OperationFailed { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly OperationFailed Value = new OperationFailed(); + + private OperationFailed() { + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/OperatorSlotAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/OperatorSlotAttribute.cs new file mode 100644 index 0000000000..00e8e903e9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/OperatorSlotAttribute.cs @@ -0,0 +1,29 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Runtime { + /// + /// Represents an ops-extension method which is added as an operator. + /// + /// The name must be a well-formed name such as "Add" that matches the CLS + /// naming conventions for adding overloads associated with op_* methods. + /// + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] + public sealed class OperatorSlotAttribute : Attribute { + public OperatorSlotAttribute() { } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/Operators.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/Operators.cs new file mode 100644 index 0000000000..f8e5cadb62 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/Operators.cs @@ -0,0 +1,623 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Runtime { + + //////////////////////////////////////////////////////////////////////////////// + // + // TODO: Need to figure out the correct string values for these operators - should + // they be op_Addition, Addition, Add, +, or something else? + // + //////////////////////////////////////////////////////////////////////////////// + + public static class StandardOperators { + public const string None = ""; + + public static string FromOperator(Operators op) { + switch(op) { + case Operators.Equals: return "Equal"; + case Operators.NotEquals: return "NotEqual"; + default: + return op.ToString(); + } + } + + public static Operators ToOperator(string op) { + switch (op) { + case "Equal": return Operators.Equals; + case "NotEqual": return Operators.NotEquals; + default: + return (Operators)System.Enum.Parse(typeof(Operators), op, false); + } + } + + /// + /// Unary operator. + /// + /// Gets information about the type of parameters, returned as a string. + /// + public const string CallSignatures = "CallSignatures"; + /// + /// Unary operator. + /// + /// Checks whether the object is callable or not, returns true if it is. + /// + public const string IsCallable = "IsCallable"; + /// + /// Unary operator. + /// + /// Gets various documentation about the object returned as a string + /// + public const string Documentation = "Documentation"; + + #region Generated Table of Standard Operators + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_OperatorStringTable from: generate_ops.py + + ///Operator for performing add + public const string Add = "Add"; + ///Operator for performing sub + public const string Subtract = "Subtract"; + ///Operator for performing pow + public const string Power = "Power"; + ///Operator for performing mul + public const string Multiply = "Multiply"; + ///Operator for performing floordiv + public const string FloorDivide = "FloorDivide"; + ///Operator for performing div + public const string Divide = "Divide"; + ///Operator for performing truediv + public const string TrueDivide = "TrueDivide"; + ///Operator for performing mod + public const string Mod = "Mod"; + ///Operator for performing lshift + public const string LeftShift = "LeftShift"; + ///Operator for performing rshift + public const string RightShift = "RightShift"; + ///Operator for performing and + public const string BitwiseAnd = "BitwiseAnd"; + ///Operator for performing or + public const string BitwiseOr = "BitwiseOr"; + ///Operator for performing xor + public const string ExclusiveOr = "ExclusiveOr"; + ///Operator for performing lt + public const string LessThan = "LessThan"; + ///Operator for performing gt + public const string GreaterThan = "GreaterThan"; + ///Operator for performing le + public const string LessThanOrEqual = "LessThanOrEqual"; + ///Operator for performing ge + public const string GreaterThanOrEqual = "GreaterThanOrEqual"; + ///Operator for performing eq + public const string Equal = "Equal"; + ///Operator for performing ne + public const string NotEqual = "NotEqual"; + ///Operator for performing lg + public const string LessThanGreaterThan = "LessThanGreaterThan"; + ///Operator for performing in-place add + public const string InPlaceAdd = "InPlaceAdd"; + ///Operator for performing in-place sub + public const string InPlaceSubtract = "InPlaceSubtract"; + ///Operator for performing in-place pow + public const string InPlacePower = "InPlacePower"; + ///Operator for performing in-place mul + public const string InPlaceMultiply = "InPlaceMultiply"; + ///Operator for performing in-place floordiv + public const string InPlaceFloorDivide = "InPlaceFloorDivide"; + ///Operator for performing in-place div + public const string InPlaceDivide = "InPlaceDivide"; + ///Operator for performing in-place truediv + public const string InPlaceTrueDivide = "InPlaceTrueDivide"; + ///Operator for performing in-place mod + public const string InPlaceMod = "InPlaceMod"; + ///Operator for performing in-place lshift + public const string InPlaceLeftShift = "InPlaceLeftShift"; + ///Operator for performing in-place rshift + public const string InPlaceRightShift = "InPlaceRightShift"; + ///Operator for performing in-place and + public const string InPlaceBitwiseAnd = "InPlaceBitwiseAnd"; + ///Operator for performing in-place or + public const string InPlaceBitwiseOr = "InPlaceBitwiseOr"; + ///Operator for performing in-place xor + public const string InPlaceExclusiveOr = "InPlaceExclusiveOr"; + + // *** END GENERATED CODE *** + + #endregion + + /// + /// Binary operator. + /// + /// Checks to see if the instance contains another object. Returns true or false. + /// + /// The standard name for this operator is "Contains". + /// + public const string Contains = "Contains"; + /// + /// n-ary operator. + /// + /// Gets the value at the specified index from the instance. + /// + /// One or more indexes can be provided as individual arguments. + /// Obsolete: Use GetIndexAction instead + /// + public const string GetItem = "GetItem"; + /// + /// n-ary operator. + /// + /// Sets the value at the specified index in the instance. + /// + /// One or more indexes can be provided as individual arguments. The last value provided is the value to be set. + /// Obsolete: Use SetIndexAction instead + /// + public const string SetItem = "SetItem"; + /// + /// n-ary operator. + /// + /// Removes the item from the specified index in the instance. + /// + /// One or more indexes can be provided as individual arguments. + /// Obsolete: Use DeleteIndexAction instead + /// + public const string DeleteItem = "DeleteItem"; + /// + /// Binary or Ternary operator. + /// + /// Gets the specified range of elements (slice) from the instance. + /// + /// The slice parameters may include the start index, the end index, and the step value. The step value is optional. + /// + /// A value of Type.Missing may be provided if no parameter was explicitly provided for a start, stop or step parameter. + /// + public const string GetSlice = "GetSlice"; + /// + /// n-ary operator. + /// + /// Sets the specified range of elements in the instance. + /// + /// The slice parameters may include the start index, the end index, and the step value. The step + /// value is optional. The last parameter is the value to be assigned. + /// + /// A value of Type.Missing may be provided if no parameter was explicitly provided for a start, stop or step parameter. + /// + public const string SetSlice = "SetSlice"; + /// + /// n-ary operator. + /// + /// Removes the specified range of elements from the instance. + /// + /// The slice parameters may include the start index, the end index, and the step value. The step value is + /// optional. + /// + /// A value of Type.Missing may be provided if no parameter was explicitly provided for a start, stop or step parameter. + /// + public const string DeleteSlice = "DeleteSlice"; + /// + /// Unary operator. + /// + /// Returns the number of items stored in the object. + /// + public const string Length = "Length"; + /// + /// Binary operator. + /// + /// Compares two instances returning an integer indicating the relationship between them. May + /// throw if the object types are uncomparable. + /// + /// The standard name for this operator is "Compare". + /// + public const string Compare = "Compare"; + /// + /// Binary operator. + /// + /// Returns both the dividend and quotioent of x / y. + /// + public const string DivMod = "DivMod"; + /// + /// Attribute customization operator. Returns a list of names that should be displayed as + /// being part of the object. + /// + /// Arguments are the instance to get the list of member names from. + /// + /// Return value is IList<SymbolId>. + /// + public const string MemberNames = "GetMemberNames"; + /// + /// Unary operator. + /// + /// Get the absolute value of the instance. + /// + public const string AbsoluteValue = "AbsoluteValue"; + /// + /// Unary operator. + /// + /// Gets the positive value of the instance. + /// + public const string Positive = "Positive"; + /// + /// Unary operator. + /// + /// Negates the instance and return the new value. + /// + public const string Negate = "Negate"; + /// + /// Unary operator. + /// + /// Returns the ones complement of the instance. + /// + public const string OnesComplement = "OnesComplement"; + + /// + /// Unary operator. + /// + /// Performs boolean negation on the value. + /// + public const string Not = "Not"; + } + + /// + /// Enum representing different types of operators. + /// + /// Operators can be Unary, Binary, or Ternary. An individual operator can have one or + /// more arity. + /// + /// Each operator is associated with a standard name. If a method is named using the standard + /// name and is marked with OperatorMethodAttribute then the method will automatically be + /// detected as an operator. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1717:OnlyFlagsEnumsShouldHavePluralNames")] // TODO: fix + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2217:DoNotMarkEnumsWithFlags")] + public enum Operators { + None, + /// + /// Binary operator. + /// + /// Attempt to call the object. Arguments are the object and the arguments for the call. The + /// arguments for the call can either be an object array (normal call) or a KwCallInfo class for + /// performing a keyword based call. + /// + /// The standard name for this operator is "Call". + /// + Call, + /// + /// Unary operator. + /// + /// Returns a string which defines the object in code or a language specific format for + /// objects which cannot be represented in code. This operator generally is not used in + /// a non-language specific scenario. + /// + CodeRepresentation, + /// + /// Unary operator. + /// + /// Gets the list of members that belong to the current object returned as an IList of string + /// + MemberNames, + /// + /// Unary operator. + /// + /// Gets various documentation about the object returned as a string + /// + Documentation, + /// + /// Unary operator. + /// + /// Gets information about the type of parameters, returned as a string. + /// + CallSignatures, + /// + /// Unary operator. + /// + /// Checks whether the object is callable or not, returns true if it is. + /// + IsCallable, + + #region Generated Table of Operators + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_OperatorTable from: generate_ops.py + + ///Operator for performing add + Add, + ///Operator for performing sub + Subtract, + ///Operator for performing pow + Power, + ///Operator for performing mul + Multiply, + ///Operator for performing floordiv + FloorDivide, + ///Operator for performing div + Divide, + ///Operator for performing truediv + TrueDivide, + ///Operator for performing mod + Mod, + ///Operator for performing lshift + LeftShift, + ///Operator for performing rshift + RightShift, + ///Operator for performing and + BitwiseAnd, + ///Operator for performing or + BitwiseOr, + ///Operator for performing xor + ExclusiveOr, + ///Operator for performing lt + LessThan, + ///Operator for performing gt + GreaterThan, + ///Operator for performing le + LessThanOrEqual, + ///Operator for performing ge + GreaterThanOrEqual, + ///Operator for performing eq + Equals, + ///Operator for performing ne + NotEquals, + ///Operator for performing lg + LessThanGreaterThan, + ///Operator for performing in-place add + InPlaceAdd, + ///Operator for performing in-place sub + InPlaceSubtract, + ///Operator for performing in-place pow + InPlacePower, + ///Operator for performing in-place mul + InPlaceMultiply, + ///Operator for performing in-place floordiv + InPlaceFloorDivide, + ///Operator for performing in-place div + InPlaceDivide, + ///Operator for performing in-place truediv + InPlaceTrueDivide, + ///Operator for performing in-place mod + InPlaceMod, + ///Operator for performing in-place lshift + InPlaceLeftShift, + ///Operator for performing in-place rshift + InPlaceRightShift, + ///Operator for performing in-place and + InPlaceBitwiseAnd, + ///Operator for performing in-place or + InPlaceBitwiseOr, + ///Operator for performing in-place xor + InPlaceExclusiveOr, + ///Operator for performing reverse add + ReverseAdd, + ///Operator for performing reverse sub + ReverseSubtract, + ///Operator for performing reverse pow + ReversePower, + ///Operator for performing reverse mul + ReverseMultiply, + ///Operator for performing reverse floordiv + ReverseFloorDivide, + ///Operator for performing reverse div + ReverseDivide, + ///Operator for performing reverse truediv + ReverseTrueDivide, + ///Operator for performing reverse mod + ReverseMod, + ///Operator for performing reverse lshift + ReverseLeftShift, + ///Operator for performing reverse rshift + ReverseRightShift, + ///Operator for performing reverse and + ReverseBitwiseAnd, + ///Operator for performing reverse or + ReverseBitwiseOr, + ///Operator for performing reverse xor + ReverseExclusiveOr, + + // *** END GENERATED CODE *** + + #endregion + + /// + /// Binary operator. + /// + /// Checks to see if the instance contains another object. Returns true or false. + /// + /// The standard name for this operator is "Contains". + /// + Contains, + /// + /// n-ary operator. + /// + /// Gets the value at the specified index from the instance. + /// + /// One or more indexes can be provided as individual arguments. + /// Obsolete: Use GetIndexAction instead + /// + GetItem, + /// + /// n-ary operator. + /// + /// Sets the value at the specified index in the instance. + /// + /// One or more indexes can be provided as individual arguments. The last value provided is the value to be set. + /// Obsolete: Use SetIndexAction instead + /// + SetItem, + /// + /// n-ary operator. + /// + /// Removes the item from the specified index in the instance. + /// + /// One or more indexes can be provided as individual arguments. + /// Obsolete: Use DeleteIndexAction instead + /// + DeleteItem, + /// + /// Binary or Ternary operator. + /// + /// Gets the specified range of elements (slice) from the instance. + /// + /// The slice parameters may include the start index, the end index, and the step value. The step value is optional. + /// + /// A value of Type.Missing may be provided if no parameter was explicitly provided for a start, stop or step parameter. + /// + GetSlice, + /// + /// n-ary operator. + /// + /// Sets the specified range of elements in the instance. + /// + /// The slice parameters may include the start index, the end index, and the step value. The step + /// value is optional. The last parameter is the value to be assigned. + /// + /// A value of Type.Missing may be provided if no parameter was explicitly provided for a start, stop or step parameter. + /// + SetSlice, + /// + /// n-ary operator. + /// + /// Removes the specified range of elements from the instance. + /// + /// The slice parameters may include the start index, the end index, and the step value. The step value is + /// optional. + /// + /// A value of Type.Missing may be provided if no parameter was explicitly provided for a start, stop or step parameter. + /// + DeleteSlice, + /// + /// Unary operator. + /// + /// Returns the number of items stored in the object. + /// + Length, + /// + /// Binary operator. + /// + /// Compares two instances returning an integer indicating the relationship between them. May + /// throw if the object types are uncomparable. + /// + /// The standard name for this operator is "Compare". + /// + Compare, + /// + /// Binary operator. + /// + /// Returns both the dividend and quotioent of x / y. + /// + DivMod, + /// + /// Binary operator. + /// + /// Returns both the dividend and quotient of y / x. + /// + ReverseDivMod, + /// + /// Member lookup customization (called after type lookup). + /// + /// Arguments are the instance to get the member from and a SymbolId which represents the member. + /// + /// The return value is the member. + /// + /// The standard name for this operator is "GetMember". + /// + GetMember, + /// + /// Member lookup customization for bound attributes + /// + /// Arguments are the instance to get the member from and a SymbolId which represents the bound member. + /// + /// The return value is the bound member. + /// + /// /// The standard name for this operator is "GetBoundMember". + /// + GetBoundMember, + /// + /// Member set customization. + /// + /// Arguments are the instance, the SymbolId to get, and the new value for the member. + /// + /// The return value is ignored. + /// + /// The standard name for this operator is "SetMember". + /// + SetMember, + /// + /// Member delete customization. + /// + /// Arguments are the instance and the SymbolId for the member to delete. + /// + /// The return value is ignored. + /// + /// The standard name for this operator is "DeleteMember". + /// + DeleteMember, + /// + /// Attribute customization operator. Returns a list of names that should be displayed as + /// being part of the object. + /// + /// Arguments are the instance to get the list of member names from. + /// + /// Return value is IList<SymbolId>. + /// + /// /// The standard name for this operator is "GetMemberNames". + /// + GetMemberNames, + /// + /// Unary operator. + /// + /// Get the absolute value of the instance. + /// + AbsoluteValue, + /// + /// Unary operator. + /// + /// Gets the positive value of the instance. + /// + Positive, + /// + /// Unary operator. + /// + /// Negates the instance and return the new value. + /// + Negate, + /// + /// Unary operator. + /// + /// Returns the ones complement of the instance. + /// + OnesComplement, + + RightShiftUnsigned, //Operator for performing rshiftu + InPlaceRightShiftUnsigned, //Operator for performing in-place rshiftu + ReverseRightShiftUnsigned, //Operator for performing reverse rshiftu + RightShiftSigned, + Not, // boolean negation + Increment, + Decrement, + Assign, + IsFalse, + IsTrue, + Or, + And, + IntegralDivide, + Concatenate, + Like, + Comma, + + GetEnumerator, + Dispose, + + IdMask = 0x7fffffff, + UserDefinedFlag = unchecked((int)0x80000000), + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/OptimizedScriptCode.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/OptimizedScriptCode.cs new file mode 100644 index 0000000000..02460ac7d5 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/OptimizedScriptCode.cs @@ -0,0 +1,219 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Security; +using System.Security.Permissions; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + + public class OptimizedScriptCode : ScriptCode { + private Scope _optimizedScope; + private DlrMainCallTarget _optimizedTarget, _unoptimizedTarget; + + public OptimizedScriptCode(LambdaExpression code, SourceUnit sourceUnit) + : base(code, null, sourceUnit) { + Debug.Assert(code.Parameters.Count == 0, "GlobalRewritter shouldn't have been applied yet"); + } + + public OptimizedScriptCode(Scope optimizedScope, DlrMainCallTarget optimizedTarget, SourceUnit sourceUnit) + : base(null, optimizedTarget, sourceUnit) { + ContractUtils.RequiresNotNull(optimizedScope, "optimizedScope"); + + _optimizedScope = optimizedScope; + _optimizedTarget = optimizedTarget; + } + + public override Scope CreateScope() { + return MakeOptimizedScope(); + } + + public override void EnsureCompiled() { + MakeOptimizedScope(); + } + + private Scope MakeOptimizedScope() { + Debug.Assert((_optimizedTarget == null) == (_optimizedScope == null)); + + if (_optimizedScope != null) { + return _optimizedScope; + } + + return CompileOptimizedScope(); + } + + protected override object InvokeTarget(LambdaExpression code, Scope scope) { + if (scope == _optimizedScope) { + return _optimizedTarget(scope, LanguageContext); + } + + // new scope, compile unoptimized code and use that. + if (_unoptimizedTarget == null) { + // TODO: fix generated DLR ASTs - languages should remove their usage + // of GlobalVariables and then this can go away. + Expression lambda = new GlobalLookupRewriter().RewriteLambda(code); + + _unoptimizedTarget = lambda.Compile(SourceUnit.EmitDebugSymbols); + } + + + return _unoptimizedTarget(scope, LanguageContext); + } + + /// + /// Creates the methods and optimized Scope's which get associated with each ScriptCode. + /// + private Scope CompileOptimizedScope() { + DlrMainCallTarget target; + IAttributesCollection globals; + if (UseLightweightScopes) { + CompileWithArrayGlobals(out target, out globals); + } else { + CompileWithStaticGlobals(out target, out globals); + } + + // Force creation of names used in other script codes into all optimized dictionaries + Scope scope = new Scope(globals); + ((IModuleDictionaryInitialization)globals).InitializeModuleDictionary(new CodeContext(scope, LanguageContext)); + + // everything succeeded, commit the results + _optimizedTarget = target; + _optimizedScope = scope; + + return scope; + } + + private static bool UseLightweightScopes { + get { + + if (DebugOptions.LightweightScopes) { + return true; + } + +#if !SILVERLIGHT + try { + // Static field compiler requires ReflectionEmit (in CLR V2) or UnmanagedCode (in CLR V2 SP1) permission. + // If we are running in partial-trust, fall through to generated dynamic code. + // TODO: Symbol information requires unmanaged code permission. Move the error + // handling lower and just don't dump PDBs. + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); + } catch (SecurityException) { + return true; + } +#endif + return false; + } + } + + private void CompileWithArrayGlobals(out DlrMainCallTarget target, out IAttributesCollection globals) { + GlobalArrayRewriter rewriter = new GlobalArrayRewriter(); + Expression lambda = rewriter.RewriteLambda(Code); + + // Compile target + target = lambda.Compile(SourceUnit.EmitDebugSymbols); + + // Create globals + globals = rewriter.CreateDictionary(); + } + + private void CompileWithStaticGlobals(out DlrMainCallTarget target, out IAttributesCollection globals) { + // Create typegen + TypeGen typeGen = Snippets.Shared.DefineType(MakeDebugName(), typeof(CustomSymbolDictionary), false, SourceUnit.EmitDebugSymbols); + typeGen.TypeBuilder.DefineDefaultConstructor(MethodAttributes.Public); + + // Create rewriter + GlobalStaticFieldRewriter rewriter = new GlobalStaticFieldRewriter(typeGen); + + // Compile lambda + LambdaExpression lambda = rewriter.RewriteLambda(Code, "Initialize"); + lambda.CompileToMethod(typeGen.TypeBuilder, CompilerHelpers.PublicStatic, SourceUnit.EmitDebugSymbols); + + // Create globals dictionary, finish type + rewriter.EmitDictionary(); + Type type = typeGen.FinishType(); + globals = (IAttributesCollection)Activator.CreateInstance(type); + + // Create target + target = (DlrMainCallTarget)Delegate.CreateDelegate(typeof(DlrMainCallTarget), type.GetMethod("Initialize")); + + // TODO: clean this up after clarifying dynamic site initialization logic + InitializeFields(type); + } + + // + // Initialization of dynamic sites stored in static fields + // + + public static void InitializeFields(Type type) { + InitializeFields(type, false); + } + + public static void InitializeFields(Type type, bool reusable) { + if (type == null) return; + + const string slotStorageName = "#Constant"; + foreach (FieldInfo fi in type.GetFields()) { + if (fi.Name.StartsWith(slotStorageName)) { + object value; + if (reusable) { + value = GlobalStaticFieldRewriter.GetConstantDataReusable(Int32.Parse(fi.Name.Substring(slotStorageName.Length))); + } else { + value = GlobalStaticFieldRewriter.GetConstantData(Int32.Parse(fi.Name.Substring(slotStorageName.Length))); + } + Debug.Assert(value != null); + fi.SetValue(null, value); + } + } + } + + protected override MethodBuilder CompileForSave(TypeGen typeGen, Dictionary symbolDict) { + // first, serialize constants and dynamic sites: + ToDiskRewriter diskRewriter = new ToDiskRewriter(typeGen); + LambdaExpression lambda = diskRewriter.RewriteLambda(Code); + + // rewrite global variables: + var globalRewriter = new GlobalArrayRewriter(symbolDict, typeGen); + lambda = globalRewriter.RewriteLambda(lambda); + + MethodBuilder builder = lambda.CompileToMethod(typeGen.TypeBuilder, CompilerHelpers.PublicStatic | MethodAttributes.SpecialName, false); + + builder.SetCustomAttribute(new CustomAttributeBuilder( + typeof(CachedOptimizedCodeAttribute).GetConstructor(new Type[] { typeof(string[]) }), + new object[] { ArrayUtils.ToArray(globalRewriter.Names) } + )); + + return builder; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + private string MakeDebugName() { +#if DEBUG + if (SourceUnit != null && SourceUnit.HasPath) { + return "OptScope_" + ReflectionUtils.ToValidTypeName(Path.GetFileNameWithoutExtension(IOUtils.ToValidPath(SourceUnit.Path))); + } +#endif + return "S"; + } + + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ParamDictionaryAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ParamDictionaryAttribute.cs new file mode 100644 index 0000000000..ec9be1bb58 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ParamDictionaryAttribute.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting { + /// + /// This attribute is the dictionary equivalent of the System.ParamArrayAttribute. + /// It is used to mark a parameter that can accept an arbitrary dictionary of + /// name/value pairs for a method called with named arguments. This parameter + /// must be applied to a type that implements IDictionary(string, object) or + /// IDictionary(SymbolId, object). + /// + /// For eg. in this Python method, + /// def foo(**paramDict): print paramDict + /// foo(a=1, b=2) + /// paramDict will be {"a":1, "b":2} + /// + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] + public sealed class ParamDictionaryAttribute : Attribute { + public ParamDictionaryAttribute() { + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ParserSink.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ParserSink.cs new file mode 100644 index 0000000000..67de9af9e5 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ParserSink.cs @@ -0,0 +1,46 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic; + +namespace Microsoft.Scripting.Runtime { + + public class ParserSink { + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly ParserSink Null = new ParserSink(); + + public virtual void MatchPair(SourceSpan opening, SourceSpan closing, int priority) { + } + + public virtual void MatchTriple(SourceSpan opening, SourceSpan middle, SourceSpan closing, int priority) { + } + + public virtual void EndParameters(SourceSpan span) { + } + + public virtual void NextParameter(SourceSpan span) { + } + + public virtual void QualifyName(SourceSpan selector, SourceSpan span, string name) { + } + + public virtual void StartName(SourceSpan span, string name) { + } + + public virtual void StartParameters(SourceSpan context) { + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/PositionTrackingWriter.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/PositionTrackingWriter.cs new file mode 100644 index 0000000000..5af19886d1 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/PositionTrackingWriter.cs @@ -0,0 +1,108 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.CodeDom; +using System.Collections.Generic; +using System.IO; + +#if !SILVERLIGHT // requires CodeDom support + +namespace Microsoft.Scripting.Runtime { + /// + /// Efficiently tracks (line,column) information as text is added, and + /// collects line mappings between the original and generated source code + /// so we can generate correct debugging information later + /// + public class PositionTrackingWriter : StringWriter { + List> _lineMap = new List>(); + List> _fileMap = new List>(); + + int _line = 1; + int _column = 1; + + public PositionTrackingWriter() { } + + /// + /// Marks the current position of the writer as corresponding to the + /// original location passed in + /// + /// the line pragma corresponding to the + /// current position in the generated code + public void MapLocation(CodeLinePragma linePragma) { + _lineMap.Add(new KeyValuePair(_line, linePragma.LineNumber)); + _fileMap.Add(new KeyValuePair(_line, linePragma.FileName)); + } + + public KeyValuePair[] GetLineMap() { + return _lineMap.ToArray(); + } + + public KeyValuePair[] GetFileMap() { + return _fileMap.ToArray(); + } + + public override void Write(char value) { + if (value != '\n') { + ++_column; + } else { + _column = 1; + ++_line; + } + base.Write(value); + } + + public override void Write(string value) { + UpdateLineColumn(value); + base.Write(value); + } + + public override void Write(char[] buffer, int index, int count) { + UpdateLineColumn(buffer, index, count); + base.Write(buffer, index, count); + } + + private void UpdateLineColumn(string value) { + int lastPos = 0, pos; + while ((pos = 1 + value.IndexOf('\n', lastPos)) > 0) { + ++_line; + lastPos = pos; + } + + if (lastPos > 0) { + _column = value.Length - lastPos + 1; + } else { + _column += value.Length; + } + } + + private void UpdateLineColumn(char[] buffer, int index, int count) { + int end = index + count; + int lastPos = index, pos; + while ((pos = 1 + Array.IndexOf(buffer, '\n', lastPos, end - lastPos)) > 0) { + ++_line; + lastPos = pos; + } + + if (lastPos > 0) { + _column = count - lastPos + 1; + } else { + _column += count; + } + } + } +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/PropertyMethodAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/PropertyMethodAttribute.cs new file mode 100644 index 0000000000..c50afc8368 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/PropertyMethodAttribute.cs @@ -0,0 +1,26 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Runtime { + /// + /// Represents an ops-extension method which is used to implement a property. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class PropertyMethodAttribute : Attribute { + public PropertyMethodAttribute() { } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ReflectionCache.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ReflectionCache.cs new file mode 100644 index 0000000000..562b87000a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ReflectionCache.cs @@ -0,0 +1,180 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.Scripting.Utils; +using Microsoft.Contracts; +using Microsoft.Scripting.Actions; + +namespace Microsoft.Scripting.Runtime { + /// + /// Provides a cache of reflection members. Only one set of values is ever handed out per a + /// specific request. + /// + public static class ReflectionCache { + private static readonly Dictionary _functions = new Dictionary(); + private static readonly Dictionary _typeCache = new Dictionary(); + + public static MethodGroup GetMethodGroup(Type type, string name) { + return GetMethodGroup(type, name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.InvokeMethod, null); + } + + /// + /// Gets a singleton method group from the provided type. + /// + /// The provided method group will be unique based upon the methods defined, not based upon the type/name + /// combination. In other words calling GetMethodGroup on a base type and a derived type that introduces + /// no new methods under a given name will result in the same method group for both types. + /// + public static MethodGroup GetMethodGroup(Type type, string name, BindingFlags bindingFlags, MemberFilter filter) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(name, "name"); + + MemberInfo[] mems = type.FindMembers(MemberTypes.Method, + bindingFlags, + filter ?? delegate(MemberInfo mem, object filterCritera) { + return mem.Name == name; + }, + null); + + MethodGroup res = null; + if (mems.Length != 0) { + MethodInfo[] methods = ArrayUtils.ConvertAll( + mems, + delegate(MemberInfo x) { return (MethodInfo)x; } + ); + res = GetMethodGroup(name, methods); + } + return res; + } + + public static MethodGroup GetMethodGroup(string name, MethodBase[] methods) { + MethodGroup res = null; + MethodBaseCache cache = new MethodBaseCache(name, methods); + lock (_functions) { + if (!_functions.TryGetValue(cache, out res)) { + _functions[cache] = res = new MethodGroup( + ArrayUtils.ConvertAll( + methods, + delegate(MethodBase x) { + return (MethodTracker)MemberTracker.FromMemberInfo(x); + } + ) + ); + } + } + return res; + } + + public static MethodGroup GetMethodGroup(string name, MemberGroup mems) { + MethodGroup res = null; + + MethodBase[] bases = new MethodBase[mems.Count]; + MethodTracker[] trackers = new MethodTracker[mems.Count]; + for (int i = 0; i < bases.Length; i++) { + trackers[i] = (MethodTracker)mems[i]; + bases[i] = trackers[i].Method; + } + + if (mems.Count != 0) { + MethodBaseCache cache = new MethodBaseCache(name, bases); + lock (_functions) { + if (!_functions.TryGetValue(cache, out res)) { + _functions[cache] = res = new MethodGroup(trackers); + } + } + } + + return res; + } + + public static TypeTracker GetTypeTracker(Type type) { + TypeTracker res; + + lock (_typeCache) { + if (!_typeCache.TryGetValue(type, out res)) { + _typeCache[type] = res = new NestedTypeTracker(type); + } + } + + return res; + } + + /// + /// TODO: Make me private again + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] // TODO: fix + public class MethodBaseCache { + private MethodBase[] _members; + private string _name; + + public MethodBaseCache(string name, MethodBase[] members) { + // sort by token so that the Equals / GetHashCode doesn't have + // to line up members if reflection returns them in different orders. + Array.Sort(members, delegate(MethodBase x, MethodBase y) { + long res = x.MethodHandle.Value.ToInt64() - y.MethodHandle.Value.ToInt64(); + if (res == 0) return 0; + if (res < 0) return -1; + return 1; + }); + _name = name; + _members = members; + } + + [Confined] + public override bool Equals(object obj) { + MethodBaseCache other = obj as MethodBaseCache; + if (other == null || _members.Length != other._members.Length) return false; + if (other._name != _name) return false; + + for (int i = 0; i < _members.Length; i++) { + if (_members[i].DeclaringType != other._members[i].DeclaringType || + _members[i].MetadataToken != other._members[i].MetadataToken || + _members[i].IsGenericMethod != other._members[i].IsGenericMethod) { + return false; + } + + if (_members[i].IsGenericMethod) { + Type[] args = _members[i].GetGenericArguments(); + Type[] otherArgs = other._members[i].GetGenericArguments(); + + if (args.Length != otherArgs.Length) { + return false; + } + + for (int j = 0; j < args.Length; j++) { + if (args[j] != otherArgs[j]) return false; + } + } + } + + return true; + } + + [Confined] + public override int GetHashCode() { + int res = 6551; + foreach (MemberInfo mi in _members) { + res ^= res << 5 ^ mi.DeclaringType.GetHashCode() ^ mi.MetadataToken; + } + res ^= _name.GetHashCode(); + + return res; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/RestrictedMetaObject.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/RestrictedMetaObject.cs new file mode 100644 index 0000000000..a9510a43fd --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/RestrictedMetaObject.cs @@ -0,0 +1,55 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Dynamic.Binders; +using System.Linq.Expressions; +using AstUtils = Microsoft.Scripting.Ast.Utils; + +namespace Microsoft.Scripting.Runtime { + public class RestrictedMetaObject : MetaObject, IRestrictedMetaObject { + public RestrictedMetaObject(Expression expression, Restrictions restriction, object value) : base(expression, restriction, value) { + } + + public RestrictedMetaObject(Expression expression, Restrictions restriction) + : base(expression, restriction) { + } + + #region IRestrictedMetaObject Members + + public MetaObject Restrict(Type type) { + if (type == LimitType) { + return this; + } + + if (HasValue) { + return new RestrictedMetaObject( + AstUtils.Convert(Expression, type), + Restrictions.GetTypeRestriction(Expression, type), + Value + ); + } + + return new RestrictedMetaObject( + AstUtils.Convert(Expression, type), + Restrictions.GetTypeRestriction(Expression, type) + ); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ReturnFixer.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ReturnFixer.cs new file mode 100644 index 0000000000..4c1fd48ccf --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ReturnFixer.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; + +namespace Microsoft.Scripting.Generation { + sealed class ReturnFixer { + private readonly LocalBuilder _refSlot; + private readonly int _argIndex; + private readonly Type _argType; + + private ReturnFixer(LocalBuilder refSlot, int argIndex, Type argType) { + Debug.Assert(refSlot.LocalType.IsGenericType && refSlot.LocalType.GetGenericTypeDefinition() == typeof(StrongBox<>)); + _refSlot = refSlot; + _argIndex = argIndex; + _argType = argType; + } + + internal static ReturnFixer EmitArgument(ILGen cg, int argIndex, Type argType) { + cg.EmitLoadArg(argIndex); + + if (!argType.IsByRef) { + cg.EmitBoxing(argType); + return null; + } + + Type elementType = argType.GetElementType(); + cg.EmitLoadValueIndirect(elementType); + Type concreteType = typeof(StrongBox<>).MakeGenericType(elementType); + cg.EmitNew(concreteType, new Type[] { elementType }); + + LocalBuilder refSlot = cg.DeclareLocal(concreteType); + cg.Emit(OpCodes.Dup); + cg.Emit(OpCodes.Stloc, refSlot); + return new ReturnFixer(refSlot, argIndex, argType); + } + + internal void FixReturn(ILGen cg) { + cg.EmitLoadArg(_argIndex); + cg.Emit(OpCodes.Ldloc, _refSlot); + cg.Emit(OpCodes.Ldfld, _refSlot.LocalType.GetField("Value")); + cg.EmitStoreValueIndirect(_argType.GetElementType()); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/Scope.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/Scope.cs new file mode 100644 index 0000000000..55b9cac01b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/Scope.cs @@ -0,0 +1,820 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using Microsoft.Scripting.Utils; +using System.Threading; + +namespace Microsoft.Scripting.Runtime { + + /// + /// Represents a context of execution. A context of execution has a set of variables + /// associated with it (its dictionary) and a parent context. + /// + /// When looking up a name from a context first the local context is searched. If the + /// name is not found there the name lookup will be done against the parent context. + /// + /// Scopes can have language-sensitive variables that are only exposed to a single + /// language based upon its calling context. When searching the + /// language-sensitive dictionary is searched first. If no matches are found the lookup + /// is delegated back to the LanguageContext. If the LanguageContext fails to lookup + /// the name it delegates back to the (Host or ScriptRuntime?) + /// + /// Each member of the Scope can optionally have a certain set of attributes associated + /// with it (ScopeMemberAttributes). These permit members of the scope to be read-only, + /// non-deletable, or hidden from enumeration. + /// + /// Scopes, like IAttrbibuteCollections, support both being indexed by SymbolId for fast + /// access as well as being indexed by object. The preferred access is via SymbolId and + /// object access is provided for languages which require additional semantics. All + /// features supported for feature IDs are also supported for objects (e.g. context-sentsitivity + /// and attributes) but the object API does not contain all the same sets of overloads provided + /// for convenience. + /// + /// TODO: Thread safety + /// + public class Scope { + private ScopeExtension[] _extensions; // resizable + private readonly Scope _parent; + private IAttributesCollection _dict; + + // TODO: remove + private ScopeAttributeDictionary _attrs; + private ContextSensitiveScope _contextScopes; + private bool _isVisible; + + /// + /// Creates a new top-level scope with a new empty dictionary. The scope + /// is marked as being visible. + /// + public Scope() + : this(null, null) { + } + + /// + /// Creates a new top-level Scope with the provided dictionary + /// + public Scope(IAttributesCollection dictionary) + : this(null, dictionary) { + } + + /// + /// Creates a new Scope with the provided parent and dictionary. + /// + public Scope(Scope parent, IAttributesCollection dictionary) + : this(parent, dictionary, true) { + } + + /// + /// Creates a new Scope with the provided parent, dictionary and visibility. + /// + public Scope(Scope parent, IAttributesCollection dictionary, bool isVisible) { + _parent = parent; + _dict = dictionary ?? new SymbolDictionary(); + _isVisible = isVisible; + _extensions = ScopeExtension.EmptyArray; + } + + public ScopeExtension GetExtension(ContextId languageContextId) { + return (languageContextId.Id < _extensions.Length) ? _extensions[languageContextId.Id] : null; + } + + public ScopeExtension SetExtension(ContextId languageContextId, ScopeExtension extension) { + ContractUtils.RequiresNotNull(extension, "extension"); + + if (languageContextId.Id >= _extensions.Length) { + Array.Resize(ref _extensions, languageContextId.Id + 1); + } + + ScopeExtension original = Interlocked.CompareExchange(ref _extensions[languageContextId.Id], extension, null); + return original ?? extension; + } + + #region Obsolete (TODO) + + /// + /// Gets the parent of this Scope or null if the Scope has no parent. + /// + public Scope Parent { + get { + return _parent; + } + } + + /// + /// Gets if the context is visible at this scope. Visibility is a per-language feature that enables + /// languages to include members in the Scope chain but hide them when directly exposed to the user. + /// + public bool IsVisible { + get { + return _isVisible; + } + } + + /// + /// Returns the list of keys which are available to all languages. Keys marked with the + /// DontEnumerate flag will not be returned. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] // TODO: fix + public IEnumerable Keys { + get { + foreach (object name in _dict.Keys) { + string strName = name as string; + if (strName == null) continue; + + SymbolId si = SymbolTable.StringToId(strName); + if (_attrs == null || _attrs.CheckEnumerable(si)) { + yield return si; + } + } + } + } + + /// + /// Returns the list of Keys and Items which are available to all languages. Keys marked + /// with the DontEnumerate flag will not be returned. + /// + public IEnumerable> Items { + get { + foreach (KeyValuePair kvp in _dict.SymbolAttributes) { + if (_attrs == null || _attrs.CheckEnumerable(kvp.Key)) { + yield return kvp; + } + } + } + } + + /// + /// Returns the list of Keys available to all languages in addition to those keys + /// which are only available to the provided LanguageContext. + /// + /// Keys marked with the DontEnumerate flag will not be returned. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] // TODO: fix + public IEnumerable GetKeys(LanguageContext context) { + foreach (SymbolId si in _dict.SymbolAttributes.Keys) { + if (_attrs == null || _attrs.CheckEnumerable(si)) yield return si; + } + + if (_contextScopes != null) { + foreach (KeyValuePair kvp in _contextScopes.GetItems(context)) { + if (kvp.Key is SymbolId) { + if (_dict.ContainsKey((SymbolId)kvp.Key)) continue; + + yield return (SymbolId)kvp.Key; + } + } + } + } + + /// + /// Trys to lookup the provided name in the current scope. + /// + public bool TryGetName(SymbolId name, out object value) { + return TryGetName(null, name, out value); + } + + /// + /// Trys to lookup the provided name in the current scope. Search includes + /// names that are only visible to the provided LanguageContext. + /// + public bool TryGetName(LanguageContext context, SymbolId name, out object value) { + if (_contextScopes != null && context != null) { + if (_contextScopes.TryGetName(context, name, out value)) { + return true; + } + } + + if (_dict.TryGetValue(name, out value)) return true; + + value = null; + return false; + } + + /// + /// Trys to lookup the provided name in the current scope's context specific dictionary. + /// Search includes names that are only visible to the provided LanguageContext. + /// + public bool TryGetNameForContext(LanguageContext context, SymbolId name, out object value) { + if (_contextScopes != null && context != null) { + if (_contextScopes.TryGetName(context, name, out value)) { + return true; + } + } + + value = null; + return false; + } + + /// + /// Attempts to lookup the provided name in this scope or any outer scope. + /// + public bool TryLookupName(SymbolId name, out object value) { + return TryLookupName(null, name, out value); + } + + /// + /// Attempts to lookup the provided name in this scope or any outer scope. Lookup + /// includes searching for names that are only visible to the provided LanguageContext. + /// + public bool TryLookupName(LanguageContext context, SymbolId name, out object value) { + Scope curScope = this; + do { + if (curScope == this || curScope.IsVisible) { + if (curScope.TryGetName(context, name, out value)) { + return true; + } + } + + curScope = curScope.Parent; + } while (curScope != null); + + value = null; + return false; + } + + /// + /// Attempts to lookup the provided name in this scope or any outer scope. If the + /// name is not defined MissingMemberException is thrown. + /// + public object LookupName(SymbolId name) { + return LookupName(null, name); + } + + /// + /// Attempts to lookup the provided name in this scope or any outer scope. The + /// search includes looking for names that are only visible to the provided LanguageContext. + /// + /// If the name is not defined the language defined MissingName exception is thrown. + /// + public object LookupName(LanguageContext context, SymbolId name) { + object res; + if (!TryLookupName(context, name, out res)) { + throw context.MissingName(name); + } + + return res; + } + + /// + /// Sets the name to the specified value for the current context. + /// + /// The name has already been published and marked as ReadOnly + public void SetName(SymbolId name, object value) { + if (_attrs != null) _attrs.CheckWritable(name); + + _dict[name] = value; + } + + /// + /// Sets the name to the specified value for the current context. + /// + /// Provides the ScopeMemberAttributes which should be set on the provided object. + /// + /// The name has already been published and marked as ReadOnly + public void SetName(SymbolId name, object value, ScopeMemberAttributes attributes) { + if (_attrs != null) _attrs.CheckWritable(name); + + if (_attrs == null) { + _attrs = new ScopeAttributeDictionary(); + } + _attrs.Set(name, attributes); + _dict[name] = value; + } + + /// + /// Sets a name that is only available in the specified context. + /// + /// Provides the ScopeMemberAttributes which should be set on the provided object. + /// + /// The name has already been published and marked as ReadOnly + public void SetName(ContextId context, SymbolId name, object value, ScopeMemberAttributes attributes) { + if (_attrs != null) _attrs.CheckWritable(name); + + if (context == ContextId.Empty) { + SetName(name, value); + } else { + if (_contextScopes == null) _contextScopes = new ContextSensitiveScope(); + _contextScopes.SetName(context, name, value, attributes); + } + } + + /// + /// Removes all members from the dictionary and any context-sensitive dictionaries. + /// + public void Clear() { + if (_contextScopes != null) _contextScopes.Clear(); + + List ids = new List(_dict.Keys); + foreach (object name in ids) { + if (_attrs == null || _attrs.CheckDeletable(name)) { + _dict.RemoveObjectKey(name); + } + } + } + + /// + /// Determines if this context or any outer scope contains the defined name. + /// + public bool ContainsName(SymbolId name) { + return ContainsName(null, name); + } + + /// + /// Determines if this context or any outer scope contains the defined name that + /// is available from the provided LanguageContext. + /// + public bool ContainsName(LanguageContext context, SymbolId name) { + object tmp; + return TryLookupName(context, name, out tmp); + } + + /// + /// Removes the provided name from this scope + /// + public void RemoveName(SymbolId name) { + RemoveName(null, name); + } + + /// + /// Removes the provided name from this scope removing names + /// visible to both the current context and all contexts. + /// + public bool RemoveName(LanguageContext context, SymbolId name) { + if (!TryRemoveName(context, name)) { + throw context.MissingName(name); + } + return true; + } + + /// + /// Removes the provided name from this scope removing names + /// visible to both the current context and all contexts. + /// + public void RemoveNameForContext(LanguageContext context, SymbolId name) { + if (!TryRemoveForContext(context, name)) { + throw context.MissingName(name); + } + } + + /// + /// Attemps to remove the provided name from this scope + /// + public bool TryRemoveName(SymbolId name) { + return TryRemoveName(null, name); + } + + /// + /// Attemps to remove the provided name from this scope removing names visible + /// to both the current context and all contexts. + /// + public bool TryRemoveName(LanguageContext context, SymbolId name) { + bool fRemoved = false; + + if (_contextScopes != null && context != null) { + fRemoved = _contextScopes.TryRemoveName(context, name); + } + + // TODO: Ideally, we could do this without having to do two lookups. + object removedObject; + if ((_attrs == null || _attrs.CheckDeletable(name)) + && _dict.TryGetValue(name, out removedObject) && removedObject != Uninitialized.Instance) { + fRemoved = _dict.Remove(name) || fRemoved; + } + + return fRemoved; + } + + /// + /// Attemps to remove the provided name from this scope's context specific dictionary + /// + public bool TryRemoveForContext(LanguageContext context, SymbolId name) { + if (_contextScopes != null) { + return _contextScopes.TryRemoveName(context, name); + } + + return false; + } + + // Emitted by TupleSlotFactory + /// + /// Gets the outer-most scope associated with this scope. + /// + public Scope ModuleScope { + get { + Scope cur = this; + while (cur.Parent != null) cur = cur.Parent; + + return cur; + } + } + + /// + /// Default scope dictionary + /// + public IAttributesCollection Dict { + get { + return _dict; + } + } + + #region Object key access + + public bool TryRemoveObjectName(object name) { + return TryRemoveObjectName(null, name); + } + + /// + /// Attemps to remove the provided object name from this scope removing names visible + /// to both the current context and all contexts. + /// + public bool TryRemoveObjectName(LanguageContext context, object name) { + bool fRemoved = false; + + if (_contextScopes != null && context != null) fRemoved = _contextScopes.TryRemoveObjectName(context, name); + + if (_attrs == null || _attrs.CheckDeletable(name)) { + fRemoved = _dict.RemoveObjectKey(name) || fRemoved; + } + + return fRemoved; + } + + public bool TryGetObjectName(object name, out object value) { + return TryGetObjectName(null, name, out value); + } + + /// + /// Trys to lookup the provided name in the current scope. Search includes + /// names that are only visible to the provided LanguageContext. + /// + public bool TryGetObjectName(LanguageContext context, object name, out object value) { + if (_contextScopes != null && context != null) { + if (_contextScopes.TryGetObjectName(context, name, out value)) { + return true; + } + } + + if (_dict.TryGetObjectValue(name, out value)) return true; + + value = null; + return false; + } + + /// + /// Attempts to lookup the provided object name in this scope or any outer scope. Lookup + /// includes searching for names that are visible to the provided LanguageContext as well + /// as those available to all contexts. + /// + public bool TryLookupObjectName(LanguageContext context, object name, out object value) { + Scope curScope = this; + do { + if (curScope == this || curScope.IsVisible) { + if (curScope.TryGetObjectName(context, name, out value)) { + return true; + } + } + + curScope = curScope.Parent; + } while (curScope != null); + + value = null; + return false; + } + + /// + /// Sets the name to the specified value for the current context. + /// + /// The name is an arbitrary object. + /// + public void SetObjectName(object name, object value) { + SetObjectName(ContextId.Empty, name, value, ScopeMemberAttributes.None); + } + + /// + /// Sets the name to the specified value for the current context. + /// + /// The name is an arbitrary object. + /// + public void SetObjectName(ContextId context, object name, object value, ScopeMemberAttributes attributes) { + int id = context.Id; + if (id == 0) { + if (_attrs != null) _attrs.CheckWritable(name); + + _dict.AddObjectKey(name, value); + if (attributes != ScopeMemberAttributes.None) { + if (_attrs == null) _attrs = new ScopeAttributeDictionary(); + _attrs.Set(name, attributes); + } + } else { + if (_contextScopes == null) _contextScopes = new ContextSensitiveScope(); + _contextScopes.SetObjectName(context, name, value, attributes); + } + } + + /// + /// Returns the list of Keys available to all languages in addition to those keys + /// which are only available to the provided LanguageContext. + /// + /// Keys marked with the DontEnumerate flag will not be returned. + /// + public IEnumerable GetAllKeys(LanguageContext context) { + foreach (object key in _dict.Keys) { + if (_attrs == null || _attrs.CheckEnumerable(key)) yield return key; + } + + if (_contextScopes != null && context != null) { + foreach (KeyValuePair kvp in _contextScopes.GetItems(context)) { + if (_dict.ContainsObjectKey(kvp.Key)) continue; + + if (_attrs == null || _attrs.CheckEnumerable(kvp.Key)) yield return kvp.Key; + } + } + } + + public IEnumerable> GetAllItems() { + return GetAllItems(null); + } + + /// + /// Returns the list of Keys and Values available to all languages in addition to those + /// keys which are only available to the provided LanguageContext. + /// + /// Keys marked with DontEnumerate flag will not be returned. + /// + public IEnumerable> GetAllItems(LanguageContext context) { + foreach (KeyValuePair kvp in _dict) { + if (_attrs == null || _attrs.CheckEnumerable(kvp.Key)) { + yield return kvp; + } + } + + if (_contextScopes != null && context != null) { + // TODO: Filter dups + foreach (KeyValuePair kvp in _contextScopes.GetItems(context)) { + if (_attrs == null || _attrs.CheckEnumerable(kvp.Key)) yield return kvp; + } + } + } + + #endregion + + /// + /// Helper class to hold onto all the context-sensitive information for a Scope. + /// + private class ContextSensitiveScope { + private List _dicts; + private List _attrs; + + public ContextSensitiveScope() { + _dicts = new List(); + } + + public void SetName(ContextId context, SymbolId name, object value, ScopeMemberAttributes attrs) { + int id = context.Id; + + EnsureDictionary(id); + EnsureAttrDict(id); + + if (_attrs != null && id < _attrs.Count) _attrs[id].CheckWritable(name); + + _dicts[id][name] = value; + _attrs[id].Set(name, attrs); + } + + public void SetObjectName(ContextId context, object name, object value, ScopeMemberAttributes attrs) { + int id = context.Id; + + EnsureDictionary(id); + if (attrs != ScopeMemberAttributes.None) EnsureAttrDict(id); + + if (_attrs != null && id < _attrs.Count) _attrs[id].CheckWritable(name); + + _dicts[id].AddObjectKey(name, value); + if (attrs != ScopeMemberAttributes.None) _attrs[id].Set(name, attrs); + } + + public bool TryGetObjectName(LanguageContext context, object name, out object value) { + int id = context.ContextId.Id; + if (id < _dicts.Count && _dicts[id] != null) { + if (_dicts[id].TryGetObjectValue(name, out value)) { + return true; + } + } + + value = null; + return false; + } + + public bool TryGetName(LanguageContext context, SymbolId name, out object value) { + int id = context.ContextId.Id; + if (id < _dicts.Count && _dicts[id] != null) { + if (_dicts[id].TryGetValue(name, out value)) { + return true; + } + } + + value = null; + return false; + } + + public bool TryRemoveName(LanguageContext context, SymbolId name) { + bool fRemoved = false; + int id = context.ContextId.Id; + + if (id < _dicts.Count && _dicts[id] != null) { + if (_attrs == null || id > _attrs.Count || _attrs[id].CheckDeletable(name)) { + fRemoved = _dicts[id].Remove(name); + } + } + + return fRemoved; + } + + public bool TryRemoveObjectName(LanguageContext context, object name) { + bool fRemoved = false; + int id = context.ContextId.Id; + + if (id < _dicts.Count && _dicts[id] != null) { + if (_attrs == null || id > _attrs.Count || _attrs[id].CheckDeletable(name)) { + fRemoved = _dicts[id].RemoveObjectKey(name); + } + } + + return fRemoved; + } + + public IEnumerable> GetItems(LanguageContext lc) { + int id = lc.ContextId.Id; + + if (id < _dicts.Count && _dicts[id] != null) { + foreach (KeyValuePair kvp in _dicts[id]) { + if (_attrs == null || id > _attrs.Count || _attrs[id].CheckEnumerable(kvp.Key)) { + yield return kvp; + } + } + } + yield break; + } + + public void Clear() { + if (_attrs == null) { + // fast clear, no attributes preventing us from clearing + _dicts = new List(); + return; + } + + for (int i = 0; i < _dicts.Count; i++) { + if (_attrs[i] == null) { + // no per-context attributes preventing us from clearing + _dicts[i] = null; + continue; + } + + // need to check each key + if (_dicts[i] != null) { + List keys = new List(_dicts[i].SymbolAttributes.Keys); + foreach (SymbolId key in keys) { + if (_attrs == null || i > _attrs.Count || _attrs[i].CheckDeletable(key)) { + _dicts[i].Remove(key); + } + } + } + } + } + + private void EnsureDictionary(int id) { + while (_dicts.Count <= id) { + _dicts.Add(null); + } + if (_dicts[id] == null) { + _dicts[id] = new SymbolDictionary(); + } + } + + private void EnsureAttrDict(int id) { + if (_attrs == null) _attrs = new List(); + + while (_attrs.Count <= id) { + _attrs.Add(null); + } + if (_attrs[id] == null) { + _attrs[id] = new ScopeAttributeDictionary(); + } + } + } + + /// + /// Helper class to hold the attributes for both SymbolId and object attributes. + /// + private class ScopeAttributeDictionary { + private Dictionary _attrs; + private Dictionary _objectAttrs; + + public ScopeAttributeDictionary() { + } + + public void Set(SymbolId name, ScopeMemberAttributes value) { + if (_attrs == null) _attrs = new Dictionary(); + + _attrs[name] = value; + } + + public void Set(object name, ScopeMemberAttributes value) { + if (_objectAttrs == null) _objectAttrs = new Dictionary(); + + _objectAttrs[name] = value; + } + + public void CheckWritable(SymbolId name) { + ScopeMemberAttributes scopeAttrs; + if (_attrs != null && _attrs.TryGetValue(name, out scopeAttrs) && (scopeAttrs & ScopeMemberAttributes.ReadOnly) != 0) { + throw Error.MemberWriteOnly(SymbolTable.IdToString(name)); + } + } + + public bool CheckDeletable(SymbolId name) { + ScopeMemberAttributes scopeAttrs; + if (_attrs != null && _attrs.TryGetValue(name, out scopeAttrs) && (scopeAttrs & ScopeMemberAttributes.DontDelete) != 0) { + return false; + } + return true; + } + + public bool CheckEnumerable(SymbolId name) { + ScopeMemberAttributes scopeAttrs; + return _attrs == null || + !_attrs.TryGetValue(name, out scopeAttrs) || + (scopeAttrs & ScopeMemberAttributes.DontEnumerate) == 0; + } + + public void CheckWritable(object name) { + if (name is SymbolId) { + CheckWritable((SymbolId)name); + return; + } + + ScopeMemberAttributes scopeAttrs; + if (_objectAttrs != null) { + if (_objectAttrs.TryGetValue(name, out scopeAttrs) && (scopeAttrs & ScopeMemberAttributes.ReadOnly) != 0) { + throw Error.MemberWriteOnly(name.ToString()); + } + } else { + string stringName = name as string; + if (stringName != null) { + CheckWritable(SymbolTable.StringToId(stringName)); + } + } + } + + public bool CheckDeletable(object name) { + if (name is SymbolId) return CheckDeletable((SymbolId)name); + + ScopeMemberAttributes scopeAttrs; + if (_objectAttrs != null) { + return !_objectAttrs.TryGetValue(name, out scopeAttrs) || + (scopeAttrs & ScopeMemberAttributes.DontDelete) == 0; + } else { + string stringName = name as string; + if (stringName != null) { + return CheckDeletable(SymbolTable.StringToId(stringName)); + } + } + return true; + } + + public bool CheckEnumerable(object name) { + if (name is SymbolId) return CheckEnumerable((SymbolId)name); + + ScopeMemberAttributes scopeAttrs; + if (_objectAttrs != null) { + return !_objectAttrs.TryGetValue(name, out scopeAttrs) || + (scopeAttrs & ScopeMemberAttributes.DontEnumerate) == 0; + } else { + string stringName = name as string; + if (stringName != null) { + return CheckEnumerable(SymbolTable.StringToId(stringName)); + } + } + return true; + } + } + + #endregion + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ScopeExtension.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ScopeExtension.cs new file mode 100644 index 0000000000..7a4cfb47db --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ScopeExtension.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + + // TODO: this class should be abstract + public class ScopeExtension { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")] + public static readonly ScopeExtension[] EmptyArray = new ScopeExtension[0]; + + private readonly Scope _scope; + + public Scope Scope { + get { return _scope; } + } + + public ScopeExtension(Scope scope) { + ContractUtils.RequiresNotNull(scope, "scope"); + _scope = scope; + } + + internal protected virtual void ModuleReloading() { + } + + internal protected virtual void ModuleReloaded() { + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ScopeMemberAttributes.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ScopeMemberAttributes.cs new file mode 100644 index 0000000000..6942ddea9d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ScopeMemberAttributes.cs @@ -0,0 +1,41 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Runtime { + /// + /// Common attributes used to control attributes of a Scope. + /// + [Flags] + public enum ScopeMemberAttributes { + /// + /// The member has no Scope attributes. + /// + None = 0x0000, + /// + /// The member can only be read from and cannot be written to + /// + ReadOnly = 0x0001, + /// + /// The member can be read from or written to but cannot be deleted + /// + DontDelete = 0x0002, + /// + /// The member can be read or written but is not visible in the displayed list of members. + /// + DontEnumerate = 0x0004, + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ScriptCode.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ScriptCode.cs new file mode 100644 index 0000000000..eb51d16bcb --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ScriptCode.cs @@ -0,0 +1,277 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Threading; +using Microsoft.Contracts; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Diagnostics; + +namespace Microsoft.Scripting { + /// + /// ScriptCode is an instance of compiled code that is bound to a specific LanguageContext + /// but not a specific ScriptScope. The code can be re-executed multiple times in different + /// scopes. Hosting API counterpart for this class is CompiledCode. + /// + public class ScriptCode { + // TODO: should probably store this as Expression + private readonly LambdaExpression _code; + private readonly SourceUnit _sourceUnit; + private DlrMainCallTarget _target; + + public ScriptCode(LambdaExpression code, SourceUnit sourceUnit) + : this(code, null, sourceUnit) { + } + + public ScriptCode(LambdaExpression code, DlrMainCallTarget target, SourceUnit sourceUnit) { + if (code == null && target == null) { + throw Error.MustHaveCodeOrTarget(); + } + + ContractUtils.RequiresNotNull(sourceUnit, "sourceUnit"); + + _code = code; + _sourceUnit = sourceUnit; + _target = target; + } + + public LanguageContext LanguageContext { + get { return _sourceUnit.LanguageContext; } + } + + public DlrMainCallTarget Target { + get { return _target; } + } + + public SourceUnit SourceUnit { + get { return _sourceUnit; } + } + + public LambdaExpression Code { + get { return _code; } + } + + public virtual Scope CreateScope() { + return new Scope(); + } + + public virtual void EnsureCompiled() { + EnsureTarget(_code); + } + + public object Run(Scope scope) { + return InvokeTarget(_code, scope); + } + + public object Run() { + return Run(CreateScope()); + } + + protected virtual object InvokeTarget(LambdaExpression code, Scope scope) { + return EnsureTarget(code)(scope, LanguageContext); + } + + private DlrMainCallTarget EnsureTarget(LambdaExpression code) { + if (_target == null) { + var lambda = code as Expression; + if (lambda == null) { + // If language APIs produced the wrong delegate type, + // rewrite the lambda with the correct type + lambda = Expression.Lambda(code.Body, code.Name, code.Parameters); + } + Interlocked.CompareExchange(ref _target, lambda.Compile(SourceUnit.EmitDebugSymbols), null); + } + return _target; + } + + internal MethodBuilder CompileToDisk(TypeGen typeGen, Dictionary symbolDict) { + if (_code == null) { + throw Error.NoCodeToCompile(); + } + + MethodBuilder mb = CompileForSave(typeGen, symbolDict); + return mb; + } + + public static ScriptCode Load(DlrMainCallTarget method, LanguageContext language, string path) { + SourceUnit su = new SourceUnit(language, NullTextContentProvider.Null, path, SourceCodeKind.File); + return new ScriptCode(null, method, su); + } + + protected virtual MethodBuilder CompileForSave(TypeGen typeGen, Dictionary symbolDict) { + var diskRewriter = new ToDiskRewriter(typeGen); + var lambda = diskRewriter.RewriteLambda(_code); + + return lambda.CompileToMethod(typeGen.TypeBuilder, CompilerHelpers.PublicStatic | MethodAttributes.SpecialName, false); + } + + /// + /// This takes an assembly name including extension and saves the provided ScriptCode objects into the assembly. + /// + /// The provided script codes can constitute code from multiple languages. The assemblyName can be either a fully qualified + /// or a relative path. The DLR will simply save the assembly to the desired location. The assembly is created by the DLR and + /// if a file already exists than an exception is raised. + /// + /// The DLR determines the internal format of the ScriptCode and the DLR can feel free to rev this as appropriate. + /// + public static void SaveToAssembly(string assemblyName, params ScriptCode[] codes) { + ContractUtils.RequiresNotNull(assemblyName, "assemblyName"); + ContractUtils.RequiresNotNullItems(codes, "codes"); + + // break the assemblyName into it's dir/name/extension + string dir = Path.GetDirectoryName(assemblyName); + if (String.IsNullOrEmpty(dir)) { + dir = Environment.CurrentDirectory; + } + + string name = Path.GetFileNameWithoutExtension(assemblyName); + string ext = Path.GetExtension(assemblyName); + + // build the assembly & type gen that all the script codes will live in... + AssemblyGen ag = new AssemblyGen(new AssemblyName(name), dir, ext, /*emitSymbols*/false); + TypeBuilder tb = ag.DefinePublicType("DLRCachedCode", typeof(object), true); + TypeGen tg = new TypeGen(ag, tb); + var symbolDict = new Dictionary(); + // then compile all of the code + + Dictionary>> langCtxBuilders = new Dictionary>>(); + foreach (ScriptCode sc in codes) { + List> builders; + if (!langCtxBuilders.TryGetValue(sc.LanguageContext.GetType(), out builders)) { + langCtxBuilders[sc.LanguageContext.GetType()] = builders = new List>(); + } + + builders.Add( + new KeyValuePair( + sc.CompileToDisk(tg, symbolDict), + sc + ) + ); + } + + MethodBuilder mb = tb.DefineMethod( + "GetScriptCodeInfo", + MethodAttributes.SpecialName | MethodAttributes.Public | MethodAttributes.Static, + typeof(Tuple), + Type.EmptyTypes); + + ILGen ilgen = new ILGen(mb.GetILGenerator()); + + var langsWithBuilders = langCtxBuilders.ToArray(); + + // lang ctx array + ilgen.EmitArray(typeof(Type), langsWithBuilders.Length, (index) => { + ilgen.Emit(OpCodes.Ldtoken, langsWithBuilders[index].Key); + ilgen.EmitCall(typeof(Type).GetMethod("GetTypeFromHandle", new[] { typeof(RuntimeTypeHandle) })); + }); + + // builders array of array + ilgen.EmitArray(typeof(DlrMainCallTarget[]), langsWithBuilders.Length, (index) => { + List> builders = langsWithBuilders[index].Value; + + ilgen.EmitArray(typeof(DlrMainCallTarget), builders.Count, (innerIndex) => { + ilgen.EmitNull(); + ilgen.Emit(OpCodes.Ldftn, builders[innerIndex].Key); + ilgen.EmitNew( + typeof(DlrMainCallTarget), + new[] { typeof(object), typeof(IntPtr) } + ); + }); + }); + + // paths array of array + ilgen.EmitArray(typeof(string[]), langsWithBuilders.Length, (index) => { + List> builders = langsWithBuilders[index].Value; + + ilgen.EmitArray(typeof(string), builders.Count, (innerIndex) => { + ilgen.EmitString(builders[innerIndex].Value._sourceUnit.Path); + }); + }); + + // 4th element in tuple - always null... + ilgen.EmitNull(); + + ilgen.EmitNew( + typeof(Tuple), + new[] { typeof(Type[]), typeof(DlrMainCallTarget[][]), typeof(string[][]), typeof(object) } + ); + ilgen.Emit(OpCodes.Ret); + + mb.SetCustomAttribute(new CustomAttributeBuilder( + typeof(DlrCachedCodeAttribute).GetConstructor(Type.EmptyTypes), + ArrayUtils.EmptyObjects + )); + + tg.FinishType(); + ag.SaveAssembly(); + } + + /// + /// This will take an assembly object which the user has loaded and return a new set of ScriptCodes which have + /// been loaded into the provided ScriptDomainManager. + /// + /// If the language associated with the ScriptCodes has not already been loaded the DLR will load the + /// LanguageContext into the ScriptDomainManager based upon the saved LanguageContext type. + /// + /// If the LanguageContext or the version of the DLR the language was compiled against is unavailable a + /// TypeLoadException will be raised unless policy has been applied by the administrator to redirect bindings. + /// + public static ScriptCode[] LoadFromAssembly(ScriptDomainManager runtime, Assembly assembly) { + ContractUtils.RequiresNotNull(runtime, "runtime"); + ContractUtils.RequiresNotNull(assembly, "assembly"); + + // get the type which has our cached code... + Type t = assembly.GetType("DLRCachedCode"); + if (t == null) { + return new ScriptCode[0]; + } + + List codes = new List(); + + MethodInfo mi = t.GetMethod("GetScriptCodeInfo"); + if (mi.IsSpecialName && mi.IsDefined(typeof(DlrCachedCodeAttribute), false)) { + var infos = (Tuple)mi.Invoke(null, ArrayUtils.EmptyObjects); + + for (int i = 0; i < infos.Item000.Length; i++) { + Type curType = infos.Item000[i]; + LanguageContext lc = runtime.GetLanguage(curType); + + Debug.Assert(infos.Item001[i].Length == infos.Item002[i].Length); + + DlrMainCallTarget[] methods = infos.Item001[i]; + string[] names = infos.Item002[i]; + + for (int j = 0; j < methods.Length; j++) { + codes.Add(lc.LoadCompiledCode(methods[j], names[j])); + } + } + } + + return codes.ToArray(); + } + + [Confined] + public override string ToString() { + return String.Format("ScriptCode '{0}' from {1}", SourceUnit.Path, LanguageContext.GetType().Name); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ScriptDomainManager.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ScriptDomainManager.cs new file mode 100644 index 0000000000..b7a0b9e96a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ScriptDomainManager.cs @@ -0,0 +1,331 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using System.Dynamic; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Utils; +using System.Threading; + +namespace Microsoft.Scripting.Runtime { + + [Serializable] + public class MissingTypeException : Exception { + public MissingTypeException() { + } + + public MissingTypeException(string name) + : this(name, null) { + } + + public MissingTypeException(string name, Exception e) : + base(Strings.MissingType(name), e) { + } + +#if !SILVERLIGHT // SerializationInfo + protected MissingTypeException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")] // TODO: fix + public sealed class ScriptDomainManager { + private readonly DynamicRuntimeHostingProvider _hostingProvider; + private readonly SharedIO _sharedIO; + + // last id assigned to a language context: + private int _lastContextId; + + private ScopeAttributesWrapper _scopeWrapper; + private Scope _globals; + private readonly DlrConfiguration _configuration; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + public PlatformAdaptationLayer Platform { + get { + PlatformAdaptationLayer result = _hostingProvider.PlatformAdaptationLayer; + if (result == null) { + throw new InvalidImplementationException(); + } + return result; + } + } + + public SharedIO SharedIO { + get { return _sharedIO; } + } + + public DynamicRuntimeHostingProvider Host { + get { return _hostingProvider; } + } + + public DlrConfiguration Configuration { + get { return _configuration; } + } + + public ScriptDomainManager(DynamicRuntimeHostingProvider hostingProvider, DlrConfiguration configuration) { + ContractUtils.RequiresNotNull(hostingProvider, "hostingProvider"); + ContractUtils.RequiresNotNull(configuration, "configuration"); + + configuration.Freeze(); + + _hostingProvider = hostingProvider; + _configuration = configuration; + + _sharedIO = new SharedIO(); + + // create the initial default scope + _scopeWrapper = new ScopeAttributesWrapper(this); + _globals = new Scope(_scopeWrapper); + } + + #region Language Registration + + internal ContextId GenerateContextId() { + return new ContextId(Interlocked.Increment(ref _lastContextId)); + } + + public LanguageContext GetLanguage(Type providerType) { + ContractUtils.RequiresNotNull(providerType, "providerType"); + return GetLanguageByTypeName(providerType.AssemblyQualifiedName); + } + + public LanguageContext GetLanguageByTypeName(string providerAssemblyQualifiedTypeName) { + ContractUtils.RequiresNotNull(providerAssemblyQualifiedTypeName, "providerAssemblyQualifiedTypeName"); + var aqtn = AssemblyQualifiedTypeName.ParseArgument(providerAssemblyQualifiedTypeName, "providerAssemblyQualifiedTypeName"); + + LanguageContext language; + if (!_configuration.TryLoadLanguage(this, aqtn, out language)) { + throw Error.UnknownLanguageProviderType(); + } + return language; + } + + public bool TryGetLanguage(string languageName, out LanguageContext language) { + ContractUtils.RequiresNotNull(languageName, "languageName"); + return _configuration.TryLoadLanguage(this, languageName, false, out language); + } + + public LanguageContext GetLanguageByName(string languageName) { + LanguageContext language; + if (!TryGetLanguage(languageName, out language)) { + throw new ArgumentException(String.Format("Unknown language name: '{0}'", languageName)); + } + return language; + } + + public bool TryGetLanguageByFileExtension(string fileExtension, out LanguageContext language) { + ContractUtils.RequiresNotEmpty(fileExtension, "fileExtension"); + return _configuration.TryLoadLanguage(this, DlrConfiguration.NormalizeExtension(fileExtension), true, out language); + } + + public LanguageContext GetLanguageByExtension(string fileExtension) { + LanguageContext language; + if (!TryGetLanguageByFileExtension(fileExtension, out language)) { + throw new ArgumentException(String.Format("Unknown file extension: '{0}'", fileExtension)); + } + return language; + } + + #endregion + + /// + /// A collection of environment variables. + /// + public Scope Globals { + get { + return _globals; + } + } + + public void SetGlobalsDictionary(IAttributesCollection dictionary) { + ContractUtils.RequiresNotNull(dictionary, "dictionary"); + + _scopeWrapper.Dict = dictionary; + } + + public event EventHandler AssemblyLoaded; + + public bool LoadAssembly(Assembly assembly) { + ContractUtils.RequiresNotNull(assembly, "assembly"); + + if (_scopeWrapper.LoadAssembly(assembly)) { + // only deliver the event if we've never added the assembly before + EventHandler assmLoaded = AssemblyLoaded; + if (assmLoaded != null) { + assmLoaded(this, new AssemblyLoadedEventArgs(assembly)); + } + + return true; + } + + return false; + } + + #region ScopeAttributesWrapper + + private class ScopeAttributesWrapper : IAttributesCollection { + private IAttributesCollection _dict = new SymbolDictionary(); + private readonly TopNamespaceTracker _tracker; + + public ScopeAttributesWrapper(ScriptDomainManager manager) { + _tracker = new TopNamespaceTracker(manager); + } + + public IAttributesCollection Dict { + set { + Assert.NotNull(_dict); + + _dict = value; + } + } + + public bool LoadAssembly(Assembly asm) { + return _tracker.LoadAssembly(asm); + } + + public List GetLoadedAssemblies() { + return _tracker._packageAssemblies; + } + + #region IAttributesCollection Members + + public void Add(SymbolId name, object value) { + _dict[name] = value; + } + + public bool TryGetValue(SymbolId name, out object value) { + if (_dict.TryGetValue(name, out value)) { + return true; + } + + value = _tracker.TryGetPackageAny(name); + return value != null; + } + + public bool Remove(SymbolId name) { + return _dict.Remove(name); + } + + public bool ContainsKey(SymbolId name) { + return _dict.ContainsKey(name) || _tracker.TryGetPackageAny(name) != null; + } + + public object this[SymbolId name] { + get { + object value; + if (TryGetValue(name, out value)) { + return value; + } + + throw new KeyNotFoundException(); + } + set { + Add(name, value); + } + } + + public IDictionary SymbolAttributes { + get { return _dict.SymbolAttributes; } + } + + public void AddObjectKey(object name, object value) { + _dict.AddObjectKey(name, value); + } + + public bool TryGetObjectValue(object name, out object value) { + return _dict.TryGetObjectValue(name, out value); + } + + public bool RemoveObjectKey(object name) { + return _dict.RemoveObjectKey(name); + } + + public bool ContainsObjectKey(object name) { + return _dict.ContainsObjectKey(name); + } + + public IDictionary AsObjectKeyedDictionary() { + return _dict.AsObjectKeyedDictionary(); + } + + public int Count { + get { + int count = _dict.Count + _tracker.Count; + foreach (object o in _tracker.Keys) { + if (ContainsObjectKey(o)) { + count--; + } + } + return count; + } + } + + public ICollection Keys { + get { + List keys = new List(_dict.Keys); + foreach (object o in _tracker.Keys) { + if (!_dict.ContainsObjectKey(o)) { + keys.Add(o); + } + } + return keys; + } + } + + #endregion + + #region IEnumerable> Members + + public IEnumerator> GetEnumerator() { + foreach (KeyValuePair kvp in _dict) { + yield return kvp; + } + foreach (KeyValuePair kvp in _tracker) { + if (!_dict.ContainsObjectKey(kvp.Key)) { + yield return kvp; + } + } + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() { + foreach (KeyValuePair kvp in _dict) { + yield return kvp.Key; + } + foreach (KeyValuePair kvp in _tracker) { + if (!_dict.ContainsObjectKey(kvp.Key)) { + yield return kvp.Key; + } + } + } + + #endregion + } + + public Assembly[] GetLoadedAssemblyList() { + return _scopeWrapper.GetLoadedAssemblies().ToArray(); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/ScriptingRuntimeHelpers.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/ScriptingRuntimeHelpers.cs new file mode 100644 index 0000000000..eed6111187 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/ScriptingRuntimeHelpers.cs @@ -0,0 +1,249 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + /// + /// These are some generally useful helper methods. Currently the only methods are those to + /// cached boxed representations of commonly used primitive types so that they can be shared. + /// This is useful to most dynamic languages that use object as a universal type. + /// + /// The methods in RuntimeHelepers are caleld by the generated code. From here the methods may + /// dispatch to other parts of the runtime to get bulk of the work done, but the entry points + /// should be here. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + public static partial class ScriptingRuntimeHelpers { + private static readonly string[] chars = MakeSingleCharStrings(); + + private static string[] MakeSingleCharStrings() { + string[] result = new string[255]; + + for (char ch = (char)0; ch < result.Length; ch++) { + result[ch] = new string(ch, 1); + } + + return result; + } + + public static object True { + get { return RuntimeOps.True; } + } + public static object False { + get { return RuntimeOps.False; } + } + public static object BooleanToObject(bool value) { + return value ? RuntimeOps.True : RuntimeOps.False; + } + public static object Int32ToObject(int value) { + return RuntimeOps.Int32ToObject(value); + } + + public static string CharToString(char ch) { + if (ch < 255) return chars[ch]; + return new string(ch, 1); + } + + public static ArgumentTypeException SimpleTypeError(string message) { + return new ArgumentTypeException(message); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // TODO: fix + public static Exception CannotConvertError(Type toType, object value) { + return SimpleTypeError(String.Format("Cannot convert {0}({1}) to {2}", CompilerHelpers.GetType(value).Name, value, toType.Name)); + } + + public static Exception SimpleAttributeError(string message) { + //TODO: localize + return new MissingMemberException(message); + } + + public static void ThrowUnboundLocalError(SymbolId name) { + throw Error.ReferencedBeforeAssignment(SymbolTable.IdToString(name)); + } + + public static object ReadOnlyAssignError(bool field, string fieldName) { + if (field) { + throw Error.FieldReadonly(fieldName); + } else { + throw Error.PropertyReadonly(fieldName); + } + } + + public static DynamicStackFrame[] GetDynamicStackFrames(Exception e) { + return GetDynamicStackFrames(e, true); + } + + public static DynamicStackFrame[] GetDynamicStackFrames(Exception e, bool filter) { + List frames = e.Data[typeof(DynamicStackFrame)] as List; + + if (frames == null) { + // we may have missed a dynamic catch, and our host is looking + // for the exception... + frames = ExceptionHelpers.AssociateDynamicStackFrames(e); + ExceptionHelpers.DynamicStackFrames = null; + } + + if (frames == null) { + return new DynamicStackFrame[0]; + } + + if (!filter) return frames.ToArray(); +#if !SILVERLIGHT + frames = new List(frames); + List res = new List(); + + // the list of _stackFrames we build up in ScriptingRuntimeHelpers can have + // too many frames if exceptions are thrown from script code and + // caught outside w/o calling GetDynamicStackFrames. Therefore we + // filter down to only script frames which we know are associated + // w/ the exception here. + try { + StackTrace outermostTrace = new StackTrace(e); + IList otherTraces = ExceptionHelpers.GetExceptionStackTraces(e) ?? new List(); + List clrFrames = new List(); + foreach (StackTrace trace in otherTraces) { + clrFrames.AddRange(trace.GetFrames() ?? new StackFrame[0]); // rare, sometimes GetFrames returns null + } + clrFrames.AddRange(outermostTrace.GetFrames() ?? new StackFrame[0]); // rare, sometimes GetFrames returns null + + int lastFound = 0; + foreach (StackFrame clrFrame in clrFrames) { + MethodBase method = clrFrame.GetMethod(); + + for (int j = lastFound; j < frames.Count; j++) { + MethodBase other = frames[j].GetMethod(); + // method info's don't always compare equal, check based + // upon name/module/declaring type which will always be a correct + // check for dynamic methods. + if (method.Module == other.Module && + method.DeclaringType == other.DeclaringType && + method.Name == other.Name) { + res.Add(frames[j]); + frames.RemoveAt(j); + lastFound = j; + break; + } + } + } + } catch (MemberAccessException) { + // can't access new StackTrace(e) due to security + } + return res.ToArray(); +#else + return frames.ToArray(); +#endif + } + + /// + /// Helper method to create an instance. Work around for Silverlight where Activator.CreateInstance + /// is SecuritySafeCritical. + /// + /// TODO: Why can't we just emit the right thing for default(T)? + /// It's always null for reference types and it's well defined for value types + /// + public static T CreateInstance() { + return default(T); + } + + // TODO: can't we just emit a new array? + public static T[] CreateArray(int args) { + return new T[args]; + } + + /// + /// EventInfo.EventHandlerType getter is marked SecuritySafeCritical in CoreCLR + /// This method is to get to the property without using Reflection + /// + /// + /// + public static Type GetEventHandlerType(EventInfo eventInfo) { + ContractUtils.RequiresNotNull(eventInfo, "eventInfo"); + return eventInfo.EventHandlerType; + } + + public static IList GetStringMembers(IList members) { + List res = new List(); + foreach (object o in members) { + string str = o as string; + if (str != null) { + res.Add(str); + } + } + return res; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // TODO: fix + public static void SetEvent(EventTracker eventTracker, object value) { + EventTracker et = value as EventTracker; + if (et != null) { + if (et != eventTracker) { + throw Error.UnexpectedEvent(eventTracker.DeclaringType.Name, + eventTracker.Name, + et.DeclaringType.Name, + et.Name); + } + return; + } + + BoundMemberTracker bmt = value as BoundMemberTracker; + if (bmt == null) { + throw Error.ExpectedBoundEvent(CompilerHelpers.GetType(value).Name); + } + if (bmt.BoundTo.MemberType != TrackerTypes.Event) throw Error.ExpectedBoundEvent(bmt.BoundTo.MemberType.ToString()); + + if (bmt.BoundTo != eventTracker) throw Error.UnexpectedEvent( + eventTracker.DeclaringType.Name, + eventTracker.Name, + bmt.BoundTo.DeclaringType.Name, + bmt.BoundTo.Name); + } + + // TODO: just emit this in the generated code + public static bool CheckDictionaryMembers(IDictionary dict, string[] names) { + if (dict.Count != names.Length) return false; + + foreach (string name in names) { + if (!dict.Contains(name)) { + return false; + } + } + return true; + } + + // TODO: just emit this in the generated code + public static T IncorrectBoxType(object received) { + throw Error.UnexpectedType("StrongBox<" + typeof(T).Name + ">", CompilerHelpers.GetType(received).Name); + } + + public static void InitializeSymbols(Type t) { + foreach (FieldInfo fi in t.GetFields()) { + if (fi.FieldType == typeof(SymbolId)) { + Debug.Assert(((SymbolId)fi.GetValue(null)) == SymbolId.Empty); + fi.SetValue(null, SymbolTable.StringToId(fi.Name)); + } + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/SharedIO.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/SharedIO.cs new file mode 100644 index 0000000000..74f501b0a5 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/SharedIO.cs @@ -0,0 +1,239 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Linq.Expressions; +using System.Text; +using System.Threading; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + public sealed class SharedIO { + // prevents this object from transitions to an inconsistent state, doesn't sync output or input: + private readonly object _mutex = new object(); + + #region Proxies + + private sealed class StreamProxy : Stream { + private readonly ConsoleStreamType _type; + private readonly SharedIO _io; + + public StreamProxy(SharedIO io, ConsoleStreamType type) { + Assert.NotNull(io); + _io = io; + _type = type; + } + + public override bool CanRead { + get { return _type == ConsoleStreamType.Input; } + } + + public override bool CanSeek { + get { return false; } + } + + public override bool CanWrite { + get { return !CanRead; } + } + + public override void Flush() { + _io.GetStream(_type).Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) { + return _io.GetStream(_type).Read(buffer, offset, count); + } + + public override void Write(byte[] buffer, int offset, int count) { + _io.GetStream(_type).Write(buffer, offset, count); + } + + public override long Length { + get { + throw new NotSupportedException(); + } + } + + public override long Position { + get { + throw new NotSupportedException(); + } + set { + throw new NotSupportedException(); + } + } + + public override long Seek(long offset, SeekOrigin origin) { + throw new NotSupportedException(); + } + + public override void SetLength(long value) { + throw new NotSupportedException(); + } + } + + #endregion + + // lazily initialized to Console by default: + private Stream _inputStream; + private Stream _outputStream; + private Stream _errorStream; + + private TextReader _inputReader; + private TextWriter _outputWriter; + private TextWriter _errorWriter; + + private Encoding _inputEncoding; + + public Stream InputStream { get { InitializeInput(); return _inputStream; } } + public Stream OutputStream { get { InitializeOutput(); return _outputStream; } } + public Stream ErrorStream { get { InitializeErrorOutput(); return _errorStream; } } + + public TextReader InputReader { get { InitializeInput(); return _inputReader; } } + public TextWriter OutputWriter { get { InitializeOutput(); return _outputWriter; } } + public TextWriter ErrorWriter { get { InitializeErrorOutput(); return _errorWriter; } } + + public Encoding InputEncoding { get { InitializeInput(); return _inputEncoding; } } + public Encoding OutputEncoding { get { InitializeOutput(); return _outputWriter.Encoding; } } + public Encoding ErrorEncoding { get { InitializeErrorOutput(); return _errorWriter.Encoding; } } + + internal SharedIO() { + } + + private void InitializeInput() { + if (_inputStream == null) { + lock (_mutex) { + if (_inputStream == null) { +#if SILVERLIGHT + _inputEncoding = StringUtils.DefaultEncoding; + _inputStream = new TextStream(Console.In, _inputEncoding); +#else + _inputStream = Console.OpenStandardInput(); + _inputEncoding = Console.InputEncoding; +#endif + _inputReader = Console.In; + } + } + } + } + + private void InitializeOutput() { + if (_outputStream == null) { + lock (_mutex) { + if (_outputStream == null) { +#if SILVERLIGHT + _outputStream = new TextStream(Console.Out); +#else + _outputStream = Console.OpenStandardOutput(); +#endif + _outputWriter = Console.Out; + } + } + } + } + + private void InitializeErrorOutput() { + if (_errorStream == null) { +#if SILVERLIGHT + Stream errorStream = new TextStream(Console.Error); +#else + Stream errorStream = Console.OpenStandardError(); +#endif + Interlocked.CompareExchange(ref _errorStream, errorStream, null); + Interlocked.CompareExchange(ref _errorWriter, Console.Error, null); + } + } + + /// + /// Only host should redirect I/O. + /// + public void SetOutput(Stream stream, TextWriter writer) { + Assert.NotNull(stream, writer); + lock (_mutex) { + _outputStream = stream; + _outputWriter = writer; + } + } + + public void SetErrorOutput(Stream stream, TextWriter writer) { + Assert.NotNull(stream, writer); + lock (_mutex) { + _errorStream = stream; + _errorWriter = writer; + } + } + + public void SetInput(Stream stream, TextReader reader, Encoding encoding) { + Assert.NotNull(stream, reader, encoding); + lock (_mutex) { + _inputStream = stream; + _inputReader = reader; + _inputEncoding = encoding; + } + } + + public void RedirectToConsole() { + lock (_mutex) { + _inputEncoding = null; + _inputStream = null; + _outputStream = null; + _errorStream = null; + _inputReader = null; + _outputWriter = null; + _errorWriter = null; + } + } + + public Stream GetStream(ConsoleStreamType type) { + switch (type) { + case ConsoleStreamType.Input: return InputStream; + case ConsoleStreamType.Output: return OutputStream; + case ConsoleStreamType.ErrorOutput: return ErrorStream; + } + throw Error.InvalidStreamType(type); + } + + public TextWriter GetWriter(ConsoleStreamType type) { + switch (type) { + case ConsoleStreamType.Output: return OutputWriter; + case ConsoleStreamType.ErrorOutput: return ErrorWriter; + } + throw Error.InvalidStreamType(type); + } + + public Encoding GetEncoding(ConsoleStreamType type) { + switch (type) { + case ConsoleStreamType.Input: return InputEncoding; + case ConsoleStreamType.Output: return OutputEncoding; + case ConsoleStreamType.ErrorOutput: return ErrorEncoding; + } + throw Error.InvalidStreamType(type); + } + + public TextReader GetReader(out Encoding encoding) { + TextReader reader; + lock (_mutex) { + reader = InputReader; + encoding = InputEncoding; + } + return reader; + } + + public Stream GetStreamProxy(ConsoleStreamType type) { + return new StreamProxy(this, type); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/SourceStringContentProvider.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/SourceStringContentProvider.cs new file mode 100644 index 0000000000..b55d23620c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/SourceStringContentProvider.cs @@ -0,0 +1,38 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Dynamic; +using Microsoft.Scripting.Utils; +using System.Text; + +namespace Microsoft.Scripting { + + [Serializable] + internal sealed class SourceStringContentProvider : TextContentProvider { + private readonly string _code; + + internal SourceStringContentProvider(string code) { + ContractUtils.RequiresNotNull(code, "code"); + + _code = code; + } + + public override SourceCodeReader GetReader() { + return new SourceCodeReader(new StringReader(_code), null); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/StaticExtensionMethodAttribute.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/StaticExtensionMethodAttribute.cs new file mode 100644 index 0000000000..ffddbc3d62 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/StaticExtensionMethodAttribute.cs @@ -0,0 +1,25 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Runtime { + /// + /// Indicates an extension method should be added as a static method, not a instance method. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class StaticExtensionMethodAttribute : Attribute { + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/StreamContentProvider.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/StreamContentProvider.cs new file mode 100644 index 0000000000..dd37a89ac2 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/StreamContentProvider.cs @@ -0,0 +1,44 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; + +namespace Microsoft.Scripting { + /// + /// Provides a factory to create streams over one source of binary content. + /// + /// StreamContentProvider's are used when opening a file of an unknown encoding. The + /// StreamContentProvider will be wrapped in a TextContentProvider provided by the language + /// which can support a language specific way of interpreting the binary data into text. + /// + /// For example some languages allow a marker at the beginning of the file which specifies + /// the encoding of the rest of the file. + /// + [Serializable] + public abstract class StreamContentProvider { + /// + /// Creates a new Stream which is backed by the content the StreamContentProvider was created for. + /// + /// For example if the StreamContentProvider was backing a file then GetStream re-opens the file and returns + /// the new stream. + /// + /// This method may be called multiple times. For example once to compile the code and again to get + /// the source code to display error messages. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public abstract Stream GetStream(); + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/SymbolDictionary.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/SymbolDictionary.cs new file mode 100644 index 0000000000..2d074fdc2e --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/SymbolDictionary.cs @@ -0,0 +1,499 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Contracts; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + /// + /// Simple thread-safe SymbolDictionary used for storing collections of members. + /// + /// Like all SymbolDictionaries this supports both indexing using SymbolId's (IAttributesCollection) + /// and via object keys (IDictionary<object, object>). + /// + public sealed class SymbolDictionary : BaseSymbolDictionary, IDictionary, IDictionary, IAttributesCollection { + private Dictionary _data = new Dictionary(); + + public SymbolDictionary() { + } + + public SymbolDictionary(IAttributesCollection from) { + // enumeration of a dictionary requires locking + // the target dictionary. + lock (from) { + foreach (KeyValuePair kvp in from) { + AsObjectKeyedDictionary().Add(kvp.Key, kvp.Value); + } + } + + } + + /// + /// Symbol dictionaries are usually indexed using literal strings, which is handled using the Symbols. + /// However, some languages allow non-string keys too. We handle this case by lazily creating an object-keyed dictionary, + /// and keeping it in the symbol-indexed dictionary. Such access is slower, which is acceptable. + /// + private Dictionary GetObjectKeysDictionary() { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData == null) { + objData = new Dictionary(); + _data.Add(BaseSymbolDictionary.ObjectKeys, objData); + } + return objData; + } + + private Dictionary GetObjectKeysDictionaryIfExists() { + object objData; + if (_data.TryGetValue(BaseSymbolDictionary.ObjectKeys, out objData)) + return (Dictionary)objData; + return null; + } + + #region IDictionary Members + + void IDictionary.Add(object key, object value) { + Debug.Assert(!(key is SymbolId)); + + string strKey = key as string; + lock (this) { + if (strKey != null) { + _data.Add(SymbolTable.StringToId(strKey), value); + } else { + Dictionary objData = GetObjectKeysDictionary(); + objData[key] = value; + } + } + } + + [Confined] + bool IDictionary.ContainsKey(object key) { + Debug.Assert(!(key is SymbolId)); + string strKey = key as string; + lock (this) { + if (strKey != null) { + if (!SymbolTable.StringHasId(strKey)) { + // Avoid creating a SymbolID if this string does not already have one + return false; + } + return _data.ContainsKey(SymbolTable.StringToId(strKey)); + } else { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData == null) return false; + return objData.ContainsKey(key); + } + } + } + + ICollection IDictionary.Keys { + get { + // data.Keys is typed as ICollection. Hence, we cannot return as a ICollection. + // Instead, we need to copy the data to a List + List res = new List(); + + lock (this) { + foreach (SymbolId x in _data.Keys) { + if (x == BaseSymbolDictionary.ObjectKeys) continue; + res.Add(SymbolTable.IdToString(x)); + } + + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) res.AddRange(objData.Keys); + } + + return res; + } + } + + bool IDictionary.Remove(object key) { + Debug.Assert(!(key is SymbolId)); + string strKey = key as string; + lock (this) { + if (strKey != null) { + return _data.Remove(SymbolTable.StringToId(strKey)); + } else { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData == null) return false; + return objData.Remove(key); + } + } + } + + bool IDictionary.TryGetValue(object key, out object value) { + Debug.Assert(!(key is SymbolId)); + string strKey = key as string; + lock (this) { + if (strKey != null) { + return _data.TryGetValue(SymbolTable.StringToId(strKey), out value); + } else { + value = null; + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData == null) return false; + return objData.TryGetValue(key, out value); + } + } + } + + ICollection IDictionary.Values { + get { + // Are there any object-keys? If not we can use a fast-path + lock (this) { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData == null) + return _data.Values; + + // There any object-keys. We need to flatten out all the values + List res = new List(); + + foreach (KeyValuePair x in _data) { + if (x.Key == BaseSymbolDictionary.ObjectKeys) continue; + res.Add(x.Value); + } + + foreach (object o in objData.Values) { + res.Add(o); + } + + return res; + } + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + public object this[object key] { + get { + Debug.Assert(!(key is SymbolId)); + string strKey = key as string; + lock (this) { + if (strKey != null) { + object value; + if (_data.TryGetValue(SymbolTable.StringToId(strKey), out value)) + return value; + } else { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) + return objData[key]; + } + } + throw new KeyNotFoundException(String.Format("'{0}'", key)); + } + set { + Debug.Assert(!(key is SymbolId)); + string strKey = key as string; + lock (this) { + if (strKey != null) { + _data[SymbolTable.StringToId(strKey)] = value; + } else { + Dictionary objData = GetObjectKeysDictionary(); + objData[key] = value; + } + } + } + } + + #endregion + + #region ICollection> Members + + public void Add(KeyValuePair item) { + string strKey = item.Key as string; + lock (this) { + if (strKey != null) { + _data.Add(SymbolTable.StringToId(strKey), item.Value); + } else { + Dictionary objData = GetObjectKeysDictionary(); + objData[item.Key] = item.Value; + } + } + } + + public void Clear() { + lock (this) _data.Clear(); + } + + [Confined] + public bool Contains(KeyValuePair item) { + object value; + if (AsObjectKeyedDictionary().TryGetValue(item.Key, out value) && value == item.Value) return true; + return false; + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) { + ContractUtils.RequiresNotNull(array, "array"); + + lock (this) { + ContractUtils.RequiresArrayRange(array, arrayIndex, Count, "arrayIndex", "array"); + foreach (KeyValuePair o in ((IEnumerable>)this)) { + array[arrayIndex++] = o; + } + } + } + + public int Count { + get { + lock (this) { + int count = _data.Count; + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) { + // -1 is because data contains objData + count += objData.Count - 1; + } + return count; + } + } + } + + public bool IsReadOnly { + get { return false; } + } + + public bool Remove(KeyValuePair item) { + lock (this) { + string strKey = item.Key as string; + if (strKey != null) { + object value; + if (AsObjectKeyedDictionary().TryGetValue(strKey, out value) && value == item.Value) { + _data.Remove(SymbolTable.StringToId(strKey)); + return true; + } + } + } + return false; + } + + #endregion + + #region IEnumerable> Members + + [Pure] + IEnumerator> IEnumerable>.GetEnumerator() { + lock (this) { + foreach (KeyValuePair o in _data) { + if (o.Key == BaseSymbolDictionary.ObjectKeys) continue; + yield return new KeyValuePair(SymbolTable.IdToString(o.Key), o.Value); + } + + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) { + foreach (KeyValuePair o in objData) { + yield return o; + } + } + } + } + + #endregion + + #region IEnumerable Members + + [Pure] + public System.Collections.IEnumerator GetEnumerator() { + foreach (KeyValuePair o in _data) { + if (o.Key == BaseSymbolDictionary.ObjectKeys) continue; + yield return SymbolTable.IdToString(o.Key); + } + + IDictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) { + foreach (object o in objData.Keys) + yield return o; + } + } + + #endregion + + #region IAttributesDictionary Members + + public void Add(SymbolId name, object value) { + lock (this) _data.Add(name, value); + } + + public bool ContainsKey(SymbolId name) { + lock (this) return _data.ContainsKey(name); + } + + public bool Remove(SymbolId name) { + lock (this) return _data.Remove(name); + } + + public bool TryGetValue(SymbolId name, out object value) { + lock (this) return _data.TryGetValue(name, out value); + } + + object IAttributesCollection.this[SymbolId name] { + get { + lock (this) return _data[name]; + } + set { + lock (this) _data[name] = value; + } + } + + public IDictionary SymbolAttributes { + get { + lock (this) { + if (GetObjectKeysDictionaryIfExists() == null) return _data; + + Dictionary d = new Dictionary(); + foreach (KeyValuePair name in _data) { + if (name.Key == BaseSymbolDictionary.ObjectKeys) continue; + d.Add(name.Key, name.Value); + } + return d; + } + } + } + + public void AddObjectKey(object name, object value) { + AsObjectKeyedDictionary().Add(name, value); + } + + public bool ContainsObjectKey(object name) { + return AsObjectKeyedDictionary().ContainsKey(name); + } + + public bool RemoveObjectKey(object name) { + return AsObjectKeyedDictionary().Remove(name); + } + + public bool TryGetObjectValue(object name, out object value) { + return AsObjectKeyedDictionary().TryGetValue(name, out value); + } + + public ICollection Keys { get { return AsObjectKeyedDictionary().Keys; } } + + public IDictionary AsObjectKeyedDictionary() { + return this; + } + + #endregion + + #region IDictionary Members + + void IDictionary.Add(object key, object value) { + AsObjectKeyedDictionary().Add(key, value); + } + + [Pure] + public bool Contains(object key) { + lock (this) return AsObjectKeyedDictionary().ContainsKey(key); + } + + [Pure] + IDictionaryEnumerator IDictionary.GetEnumerator() { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData == null) return new TransformDictionaryEnumerator(_data); + + List enums = new List(); + enums.Add(new TransformDictionaryEnumerator(_data)); + + Dictionary.Enumerator objDataEnumerator = objData.GetEnumerator(); + enums.Add(objDataEnumerator); + + return new DictionaryUnionEnumerator(enums); + } + + public bool IsFixedSize { + get { return false; } + } + + ICollection IDictionary.Keys { + get { + // data.Keys is typed as ICollection. Hence, we cannot return as a ICollection. + // Instead, we need to copy the data to a List. + List res = new List(); + + lock (this) { + foreach (SymbolId x in _data.Keys) { + if (x == BaseSymbolDictionary.ObjectKeys) continue; + res.Add(SymbolTable.IdToString(x)); + } + + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) res.AddRange(objData.Keys); + } + + return res; + } + } + + void IDictionary.Remove(object key) { + Debug.Assert(!(key is SymbolId)); + string strKey = key as string; + lock (this) { + if (strKey != null) { + _data.Remove(SymbolTable.StringToId(strKey)); + } else { + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) + objData.Remove(key); + } + } + } + + ICollection IDictionary.Values { + get { + List res = new List(); + + lock (this) { + foreach (KeyValuePair x in _data) { + if (x.Key == BaseSymbolDictionary.ObjectKeys) continue; + res.Add(x.Value); + } + + Dictionary objData = GetObjectKeysDictionaryIfExists(); + if (objData != null) res.AddRange(objData.Values); + } + + return res; + } + } + + object IDictionary.this[object key] { + get { return AsObjectKeyedDictionary()[key]; } + set { AsObjectKeyedDictionary()[key] = value; } + } + + #endregion + + public void CopyTo(Array array, int index) { + ContractUtils.RequiresNotNull(array, "array"); + + lock (this) { + ContractUtils.RequiresListRange(array, index, Count, "index", "array"); + foreach (object o in this) { + array.SetValue(o, index++); + } + } + } + + public bool IsSynchronized { + get { + return true; + } + } + + public object SyncRoot { + get { + // TODO: We should really lock on something else... + return this; + } + } + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/TokenInfo.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/TokenInfo.cs new file mode 100644 index 0000000000..d71b1c5ddb --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/TokenInfo.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Dynamic; +using Microsoft.Contracts; + +namespace Microsoft.Scripting { + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] // TODO: fix + [Serializable] + public struct TokenInfo : IEquatable { + private TokenCategory _category; + private TokenTriggers _trigger; + private SourceSpan _span; + + public TokenCategory Category { + get { return _category; } + set { _category = value; } + } + + public TokenTriggers Trigger { + get { return _trigger; } + set { _trigger = value; } + } + + public SourceSpan SourceSpan { + get { return _span; } + set { _span = value; } + } + + public TokenInfo(SourceSpan span, TokenCategory category, TokenTriggers trigger) { + _category = category; + _trigger = trigger; + _span = span; + } + + #region IEquatable Members + + [StateIndependent] + public bool Equals(TokenInfo other) { + return _category == other._category && _trigger == other._trigger && _span == other._span; + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/TokenTriggers.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/TokenTriggers.cs new file mode 100644 index 0000000000..60b0ba4a61 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/TokenTriggers.cs @@ -0,0 +1,35 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting { + + /// + /// See also Microsoft.VisualStudio.Package.TokenTriggers. + /// + [Flags] + public enum TokenTriggers { + None = 0, + MemberSelect = 1, + MatchBraces = 2, + ParameterStart = 16, + ParameterNext = 32, + ParameterEnd = 64, + Parameter = 128, + MethodTip = 240, + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/TokenizerBuffer.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/TokenizerBuffer.cs new file mode 100644 index 0000000000..9bdb7ad594 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/TokenizerBuffer.cs @@ -0,0 +1,477 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ +//#define DUMP_TOKENS + +using System; +using System.Diagnostics; +using System.IO; +using System.Dynamic; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + public sealed class TokenizerBuffer { + + //private const char EndOfData = '\uFFFF'; + private const int FirstColumn = 1; + public const int EndOfFile = -1; + public const int InvalidCharacter = -2; + + // Whether to allow multiple forms of EOLN. + // If false only '\n' is treated as a line separator otherwise '\n', '\r\n' and '\r' are treated as separators. + private readonly bool _multiEolns; + private readonly TextReader _reader; + + private char[] _buffer; + + // Set to true when the buffer is resized. The contents of the buffer is shifted to the beginning at that point, + // which discards the no longer used data in the buffer. + private bool _bufferResized; + + // current position in the buffer, points to the next character read: + private int _position; + + // location corresponding to the current token's first character: + private SourceLocation _tokenStartLocation; + + // token end location; lazily calculated: + private SourceLocation _tokenEndLocation; + + // index of the first/last+1 valid character in the buffer: + private int _start; + private int _end; + + // index of the last+1 character of the current token (token start is _start): + private int _tokenEnd; + + public TextReader Reader { + get { + return _reader; + } + } + + public bool AtBeginning { + get { + return _position == 0 && !_bufferResized; + } + } + + public int TokenLength { + get { + Debug.Assert(_tokenEnd != -1, "Token end not marked"); + return _tokenEnd - _start; + } + } + + public int TokenRelativePosition { + get { + CheckInvariants(); + + return _position - _start; + } + } + + public int Position { + get { + CheckInvariants(); + return _position; + } + } + + public SourceSpan TokenSpan { + get { + return new SourceSpan(TokenStart, TokenEnd); + } + } + + public SourceLocation TokenStart { + get { + return _tokenStartLocation; + } + } + + public SourceLocation TokenEnd { + get { + Debug.Assert(_tokenEnd != -1, "Token end not marked"); + return _tokenEndLocation; + } + } + + public TokenizerBuffer(TextReader reader, SourceLocation initialLocation, int initialCapacity, bool multiEolns) { + ContractUtils.RequiresNotNull(reader, "reader"); + ContractUtils.Requires(initialCapacity > 0, "initialCapacity"); + + _reader = reader; + _buffer = new char[initialCapacity]; + + _tokenEnd = -1; + _multiEolns = multiEolns; + + _tokenEndLocation = SourceLocation.Invalid; + _tokenStartLocation = initialLocation; + + CheckInvariants(); + } + + public int Read() { + int result = Peek(); + _position++; + return result; + } + + public bool Read(int ch) { + CheckInvariants(); + if (Peek() == ch) { + _position++; + CheckInvariants(); + return true; + } else { + return false; + } + } + + public bool Read(string str) { + Debug.Assert(!String.IsNullOrEmpty(str)); + CheckInvariants(); + + int old_pos = _position; + + // ensure sufficient data loaded: + SeekRelative(str.Length - 1); + if (Read() == EndOfFile) { + Seek(old_pos); + CheckInvariants(); + return false; + } + + Debug.Assert(_position + str.Length <= _buffer.Length); + + int i = 0; + while (i < str.Length && _buffer[i] == str[i]) i++; + + if (i != str.Length) { + Seek(old_pos); + CheckInvariants(); + return false; + } + + CheckInvariants(); + return true; + } + + public int Peek() { + CheckInvariants(); + + if (_position >= _end) { + RefillBuffer(); + + // eof: + if (_position >= _end) { + CheckInvariants(); + return EndOfFile; + } + } + + Debug.Assert(_position < _end); + + int result = _buffer[_position]; + CheckInvariants(); + return result; + } + + private void RefillBuffer() { + if (_end == _buffer.Length) { + int new_size = System.Math.Max(System.Math.Max((_end - _start) * 2, _buffer.Length), _position); + ResizeInternal(ref _buffer, new_size, _start, _end - _start); + _end -= _start; + _position -= _start; + _start = 0; + _bufferResized = true; + } + + // make the buffer full: + int count = _reader.Read(_buffer, _end, _buffer.Length - _end); + _end += count; + + ClearInvalidChars(); + } + + public void Back() { + SeekRelative(-1); + } + + /// + /// Sets the current position inside current token or one character behind it. + /// + public void Seek(int offset) { + CheckInvariants(); + Debug.Assert(offset >= 0); + // no upper limit, we can seek beyond end in which case we are reading EOFs + + _position = _start + offset; + + CheckInvariants(); + } + + /// + /// Sets the current position inside current token or one character behind it. + /// A relative displacement with respect to the current position in the token is specified. + /// + public void SeekRelative(int disp) { + CheckInvariants(); + Debug.Assert(disp >= _start - _position); + // no upper limit, we can seek beyond end in which case we are reading EOFs + + _position += disp; + + CheckInvariants(); + } + + /// + /// Marks token end. Enables to read the current token. + /// + public void MarkMultiLineTokenEnd() { + CheckInvariants(); + + _tokenEnd = System.Math.Min(_position, _end); + _tokenEndLocation = (_multiEolns) ? GetTokenEndMultiEolns() : GetTokenEndSingleEoln(); + + DumpToken(); + + CheckInvariants(); + } + + public void MarkSingleLineTokenEnd() { + CheckInvariants(); + + _tokenEnd = System.Math.Min(_position, _end); + int token_length = _tokenEnd - _start; + + // no eolns in the token: + Debug.Assert(Array.IndexOf(_buffer, '\n', _start, token_length) == -1); + Debug.Assert(!_multiEolns || Array.IndexOf(_buffer, '\r', _start, token_length) == -1); + + _tokenEndLocation = new SourceLocation( + _tokenStartLocation.Index + token_length, + _tokenStartLocation.Line, + _tokenStartLocation.Column + token_length + ); + + DumpToken(); + + CheckInvariants(); + } + + public void MarkMultiLineTokenEnd(int disp) { + SeekRelative(disp); + MarkMultiLineTokenEnd(); + } + + public void MarkSingleLineTokenEnd(int disp) { + SeekRelative(disp); + MarkSingleLineTokenEnd(); + } + + public void MarkTokenEnd(bool isMultiLine) { + if (isMultiLine) + MarkMultiLineTokenEnd(); + else + MarkSingleLineTokenEnd(); + } + + /// + /// Marks token start. It means the buffer can drop the current token. + /// Can be called even if no token has been read yet. + /// + public void DiscardToken() { + CheckInvariants(); + + // no token marked => mark it now: + if (_tokenEnd == -1) MarkMultiLineTokenEnd(); + + // the current token's end is the next token's start: + _tokenStartLocation = _tokenEndLocation; + _start = _tokenEnd; + _tokenEnd = -1; +#if DEBUG + _tokenEndLocation = SourceLocation.Invalid; +#endif + CheckInvariants(); + } + + public char GetChar(int offset) { + Debug.Assert(offset >= 0 && offset < _end); + return _buffer[_start + offset]; + } + + public char GetCharRelative(int disp) { + CheckInvariants(); + Debug.Assert(disp >= _start - _position); + + return _buffer[_position + disp]; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public string GetTokenString() { + CheckInvariants(); + Debug.Assert(_tokenEnd != -1, "Token end not marked"); + + return new String(_buffer, _start, _tokenEnd - _start); + } + + public string GetTokenSubstring(int offset) { + return GetTokenSubstring(offset, _tokenEnd - _start - offset); + } + + public string GetTokenSubstring(int offset, int length) { + CheckInvariants(); + Debug.Assert(_tokenEnd != -1, "Token end not marked"); + Debug.Assert(offset >= 0 && offset <= _tokenEnd - _start && length >= 0 && length <= _tokenEnd - _start - offset); + + return new String(_buffer, _start + offset, length); + } + + private SourceLocation GetTokenEndSingleEoln() { + int end_line = _tokenStartLocation.Line; + int end_column = _tokenStartLocation.Column; + + for (int i = _start; i < _tokenEnd; i++) { + if (_buffer[i] == '\n') { + end_column = FirstColumn; + end_line++; + } else { + end_column++; + } + } + return new SourceLocation(_tokenStartLocation.Index + _tokenEnd - _start, end_line, end_column); + } + + private SourceLocation GetTokenEndMultiEolns() { + int end_line = _tokenStartLocation.Line; + int end_column = _tokenStartLocation.Column; + + int i = _start; + while (i < _tokenEnd - 1) { + if (_buffer[i] == '\n') { + end_column = FirstColumn; + end_line++; + } else if (_buffer[i] == '\r') { + end_column = FirstColumn; + end_line++; + + Debug.Assert(i + 1 < _buffer.Length); + if (_buffer[i + 1] == '\n') i++; + } else + end_column++; + + i++; + } + + if (i < _tokenEnd) { + if (_buffer[i] == '\n') { + end_column = FirstColumn; + end_line++; + } else if (_buffer[i] == '\r') { + end_column = FirstColumn; + end_line++; + } else { + end_column++; + } + } + + return new SourceLocation(_tokenStartLocation.Index + _tokenEnd - _start, end_line, end_column); + } + + public bool IsEoln(int current) { + if (current == '\n') return true; + + if (current == '\r' && _multiEolns) { + + if (Peek() == '\n') { + return true; + } + + return true; + } + + return false; + } + + public int ReadEolnOpt(int current) { + if (current == '\n') return 1; + + if (current == '\r' && _multiEolns) { + + if (Peek() == '\n') { + SeekRelative(+1); + return 2; + } + + return 1; + } + + return 0; + } + + /// + /// Reads till the end of line and returns the character that stopped the reading. + /// The returned character is not skipped. + /// + public int ReadLine() { + int ch; + do { ch = Read(); } while (ch != EndOfFile && !IsEoln(ch)); + Back(); + return ch; + } + + /// + /// Resizes an array to a speficied new size and copies a portion of the original array into its beginning. + /// + private static void ResizeInternal(ref char[] array, int newSize, int start, int count) { + Debug.Assert(array != null && newSize > 0 && count >= 0 && newSize >= count && start >= 0); + + char[] result = (newSize != array.Length) ? new char[newSize] : array; + + Buffer.BlockCopy(array, start * sizeof(char), result, 0, count * sizeof(char)); + + array = result; + } + + [Conditional("DEBUG")] + private void ClearInvalidChars() { + for (int i = 0; i < _start; i++) _buffer[i] = '\0'; + for (int i = _end; i < _buffer.Length; i++) _buffer[i] = '\0'; + } + + [Conditional("DEBUG")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + private void CheckInvariants() { + Debug.Assert(_buffer.Length >= 1); + + // _start == _end when discarding token and at beginning, when == 0 + Debug.Assert(_start >= 0 && _start <= _end); + + Debug.Assert(_end >= 0 && _end <= _buffer.Length); + + // position beyond _end means we are reading EOFs: + Debug.Assert(_position >= _start); + Debug.Assert(_tokenEnd >= -1 && _tokenEnd <= _end); + } + + [Conditional("DUMP_TOKENS")] + private void DumpToken() { + Console.WriteLine("--> `{0}` {1}", GetTokenString().Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t"), TokenSpan); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/TokenizerService.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/TokenizerService.cs new file mode 100644 index 0000000000..85bd9d2bf2 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/TokenizerService.cs @@ -0,0 +1,101 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Dynamic; +using System.IO; + +namespace Microsoft.Scripting.Runtime { + public abstract class TokenizerService { + + // static contract: + protected TokenizerService() { + } + + public abstract void Initialize(object state, TextReader sourceReader, SourceUnit sourceUnit, SourceLocation initialLocation); + + /// + /// The current internal state of the scanner. + /// + public abstract object CurrentState { get; } + + /// + /// The current startLocation of the scanner. + /// + public abstract SourceLocation CurrentPosition { get; } + + /// + /// Move the tokenizer past the next token and return its category. + /// + /// The token information associated with the token just scanned. + public abstract TokenInfo ReadToken(); + + public abstract bool IsRestartable { get; } + public abstract ErrorSink ErrorSink { get; set; } + + /// + /// Move the tokenizer past the next token. + /// + /// False if the end of stream has been reached, true otherwise. + public virtual bool SkipToken() { + return ReadToken().Category != TokenCategory.EndOfStream; + } + + // TODO: shouldn't be virutal (JS tokenizer needs to be fixed) + /// + /// Get all tokens over a block of the stream. + /// + /// + /// + /// The scanner should return full tokens. If startLocation + length lands in the middle of a token, the full token + /// should be returned. + /// + /// + /// The mininum number of characters to process while getting tokens. + /// A enumeration of tokens. + public virtual IEnumerable ReadTokens(int countOfChars) { + List tokens = new List(); + + int start_index = CurrentPosition.Index; + + while (CurrentPosition.Index - start_index < countOfChars) { + TokenInfo token = ReadToken(); + if (token.Category == TokenCategory.EndOfStream) break; + tokens.Add(token); + } + + return tokens; + } + + /// + /// Scan from startLocation to at least startLocation + length. + /// + /// The mininum number of characters to process while getting tokens. + /// + /// This method is used to determine state at arbitrary startLocation. + /// + /// False if the end of stream has been reached, true otherwise. + public bool SkipTokens(int countOfChars) { + bool eos = false; + int start_index = CurrentPosition.Index; + + while (CurrentPosition.Index - start_index < countOfChars && (eos = SkipToken())) { + ; + } + + return eos; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/TransformDictEnumerator.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/TransformDictEnumerator.cs new file mode 100644 index 0000000000..eff10c9614 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/TransformDictEnumerator.cs @@ -0,0 +1,50 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Runtime { + /// + /// Exposes a IDictionary<SymbolId, object> as a IDictionary<object, object< + /// + public class TransformDictionaryEnumerator : CheckedDictionaryEnumerator { + private IEnumerator> _backing; + + public TransformDictionaryEnumerator(IDictionary backing) { + _backing = backing.GetEnumerator(); + } + + protected override object GetKey() { + return SymbolTable.IdToString(_backing.Current.Key); + } + + protected override object GetValue() { + return _backing.Current.Value; + } + + protected override bool DoMoveNext() { + bool result = _backing.MoveNext(); + if (result && _backing.Current.Key == CustomSymbolDictionary.ObjectKeys) { + result = MoveNext(); + } + return result; + } + + protected override void DoReset() { + _backing.Reset(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/UnboundNameException.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/UnboundNameException.cs new file mode 100644 index 0000000000..7c2340e25b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/UnboundNameException.cs @@ -0,0 +1,43 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.Serialization; + +namespace Microsoft.Scripting.Runtime { + [Serializable] + public class UnboundNameException : Exception { + public UnboundNameException() : base() { } + public UnboundNameException(string msg) : base(msg) { } + public UnboundNameException(string message, Exception innerException) + : base(message, innerException) { + } +#if !SILVERLIGHT // SerializationInfo + protected UnboundNameException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif + } + + [Serializable] + public class UnboundLocalException : UnboundNameException { + public UnboundLocalException() : base() { } + public UnboundLocalException(string msg) : base(msg) { } + public UnboundLocalException(string message, Exception innerException) + : base(message, innerException) { + } +#if !SILVERLIGHT // SerializationInfo + protected UnboundLocalException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Runtime/Uninitialized.cs b/merlin/main/runtime/Microsoft.Scripting/Runtime/Uninitialized.cs new file mode 100644 index 0000000000..712cdf8dd3 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Runtime/Uninitialized.cs @@ -0,0 +1,22 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Runtime { + public sealed class Uninitialized { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public readonly static Uninitialized Instance = new Uninitialized(); + private Uninitialized() { } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/ScriptCodeParseResult.cs b/merlin/main/runtime/Microsoft.Scripting/ScriptCodeParseResult.cs new file mode 100644 index 0000000000..4a22003337 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/ScriptCodeParseResult.cs @@ -0,0 +1,53 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting { + public enum ScriptCodeParseResult { + /// + /// Source code is a syntactically correct. + /// + Complete, + + /// + /// Source code represents an empty statement/expression. + /// + Empty, + + /// + /// Source code is already invalid and no suffix can make it syntactically correct. + /// + Invalid, + + /// + /// Last token is incomplete. Source code can still be completed correctly. + /// + IncompleteToken, + + /// + /// Last statement is incomplete. Source code can still be completed correctly. + /// + IncompleteStatement, + } + + // TODO: rename or remove + public static class SourceCodePropertiesUtils { + public static bool IsCompleteOrInvalid(/*this*/ ScriptCodeParseResult props, bool allowIncompleteStatement) { + return + props == ScriptCodeParseResult.Invalid || + props != ScriptCodeParseResult.IncompleteToken && + (allowIncompleteStatement || props != ScriptCodeParseResult.IncompleteStatement); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Severity.cs b/merlin/main/runtime/Microsoft.Scripting/Severity.cs new file mode 100644 index 0000000000..21bd048652 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Severity.cs @@ -0,0 +1,23 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting { + public enum Severity { + Ignore, + Warning, + Error, + FatalError, + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/SourceCodeKind.cs b/merlin/main/runtime/Microsoft.Scripting/SourceCodeKind.cs new file mode 100644 index 0000000000..54f0c06b62 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/SourceCodeKind.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.ComponentModel; + +namespace Microsoft.Scripting { + + /// + /// Defines a kind of the source code. The parser sets its initial state accordingly. + /// + public enum SourceCodeKind { + [EditorBrowsable(EditorBrowsableState.Never)] + Unspecified = 0, + + /// + /// The code is an expression. + /// + Expression = 1, + + /// + /// The code is a sequence of statements. + /// + Statements = 2, + + /// + /// The code is a single statement. + /// + SingleStatement = 3, + + /// + /// The code is a content of a file. + /// + File = 4, + + /// + /// The code is an interactive command. + /// + InteractiveCode = 5 + } +} + +namespace Microsoft.Scripting.Utils { + public static partial class EnumBounds { + public static bool IsValid(/*this*/ SourceCodeKind value) { + return value > SourceCodeKind.Unspecified && value <= SourceCodeKind.InteractiveCode; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/SourceCodeReader.cs b/merlin/main/runtime/Microsoft.Scripting/SourceCodeReader.cs new file mode 100644 index 0000000000..be3a4b6f68 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/SourceCodeReader.cs @@ -0,0 +1,86 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.IO; +using Microsoft.Scripting.Utils; +using System.Text; + +namespace Microsoft.Scripting { + + /// + /// Source code reader. + /// + public class SourceCodeReader : TextReader { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + new public static readonly SourceCodeReader Null = new SourceCodeReader(TextReader.Null, null); + + private readonly TextReader _textReader; + private readonly Encoding _encoding; + + public SourceCodeReader(TextReader textReader, Encoding encoding) { + ContractUtils.RequiresNotNull(textReader, "textReader"); + + _encoding = encoding; + _textReader = textReader; + } + + /// + /// Encoding that is used by the reader to convert binary data read from an underlying binary stream. + /// Null if the reader is reading from a textual source (not performing any byte to character transcoding). + /// + public Encoding Encoding { + get { return _encoding; } + } + + public TextReader BaseReader { + get { return _textReader; } + } + + public override string ReadLine() { + return _textReader.ReadLine(); + } + + /// + /// Seeks the first character of a specified line in the text stream. + /// + /// Line number. The current position is assumed to be line #1. + /// + /// Returns true if the line is found, false otherwise. + /// + public virtual bool SeekLine(int line) { + return IOUtils.SeekLine(_textReader, line); + } + + public override string ReadToEnd() { + return _textReader.ReadToEnd(); + } + + public override int Read(char[] buffer, int index, int count) { + return _textReader.Read(buffer, index, count); + } + + public override int Peek() { + return _textReader.Peek(); + } + + public override int Read() { + return _textReader.Read(); + } + + protected override void Dispose(bool disposing) { + _textReader.Dispose(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/SourceFileContentProvider.cs b/merlin/main/runtime/Microsoft.Scripting/SourceFileContentProvider.cs new file mode 100644 index 0000000000..7365db201f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/SourceFileContentProvider.cs @@ -0,0 +1,67 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting { + + /// + /// Provides a StreamContentProvider for a stream of content backed by a file on disk. + /// + [Serializable] + internal sealed class FileStreamContentProvider : StreamContentProvider { + private readonly string _path; + private readonly PALHolder _pal; + + internal string Path { + get { return _path; } + } + + #region Construction + + internal FileStreamContentProvider(PlatformAdaptationLayer manager, string path) { + ContractUtils.RequiresNotNull(path, "path"); + + _path = path; + _pal = new PALHolder(manager); + } + + #endregion + + public override Stream GetStream() { + return _pal.GetStream(Path); + } + + [Serializable] + private class PALHolder +#if !SILVERLIGHT + : MarshalByRefObject +#endif + { + [NonSerialized] + private readonly PlatformAdaptationLayer _pal; + + internal PALHolder(PlatformAdaptationLayer pal) { + _pal = pal; + } + + internal Stream GetStream(string path) { + return _pal.OpenInputFileStream(path); + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/SourceLocation.cs b/merlin/main/runtime/Microsoft.Scripting/SourceLocation.cs new file mode 100644 index 0000000000..1cad614773 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/SourceLocation.cs @@ -0,0 +1,206 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Globalization; + +namespace Microsoft.Scripting { + /// + /// Represents a location in source code. + /// + [Serializable] + public struct SourceLocation { + // TODO: remove index + private readonly int _index; + + private readonly int _line; + private readonly int _column; + + /// + /// Creates a new source location. + /// + /// The index in the source stream the location represents (0-based). + /// The line in the source stream the location represents (1-based). + /// The column in the source stream the location represents (1-based). + public SourceLocation(int index, int line, int column) { + ValidateLocation(index, line, column); + + _index = index; + _line = line; + _column = column; + } + + private static void ValidateLocation(int index, int line, int column) { + if (index < 0) { + throw ErrorOutOfRange("index", 0); + } + if (line < 1) { + throw ErrorOutOfRange("line", 1); + } + if (column < 1) { + throw ErrorOutOfRange("column", 1); + } + } + + private static Exception ErrorOutOfRange(object p0, object p1) { + return new ArgumentOutOfRangeException(string.Format("{0} must be greater than or equal to {1}", p0, p1)); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")] + private SourceLocation(int index, int line, int column, bool noChecks) { + _index = index; + _line = line; + _column = column; + } + + /// + /// The index in the source stream the location represents (0-based). + /// + public int Index { + get { return _index; } + } + + /// + /// The line in the source stream the location represents (1-based). + /// + public int Line { + get { return _line; } + } + + /// + /// The column in the source stream the location represents (1-based). + /// + public int Column { + get { return _column; } + } + + /// + /// Compares two specified location values to see if they are equal. + /// + /// One location to compare. + /// The other location to compare. + /// True if the locations are the same, False otherwise. + public static bool operator ==(SourceLocation left, SourceLocation right) { + return left._index == right._index && left._line == right._line && left._column == right._column; + } + + /// + /// Compares two specified location values to see if they are not equal. + /// + /// One location to compare. + /// The other location to compare. + /// True if the locations are not the same, False otherwise. + public static bool operator !=(SourceLocation left, SourceLocation right) { + return left._index != right._index || left._line != right._line || left._column != right._column; + } + + /// + /// Compares two specified location values to see if one is before the other. + /// + /// One location to compare. + /// The other location to compare. + /// True if the first location is before the other location, False otherwise. + public static bool operator <(SourceLocation left, SourceLocation right) { + return left._index < right._index; + } + + /// + /// Compares two specified location values to see if one is after the other. + /// + /// One location to compare. + /// The other location to compare. + /// True if the first location is after the other location, False otherwise. + public static bool operator >(SourceLocation left, SourceLocation right) { + return left._index > right._index; + } + + /// + /// Compares two specified location values to see if one is before or the same as the other. + /// + /// One location to compare. + /// The other location to compare. + /// True if the first location is before or the same as the other location, False otherwise. + public static bool operator <=(SourceLocation left, SourceLocation right) { + return left._index <= right._index; + } + + /// + /// Compares two specified location values to see if one is after or the same as the other. + /// + /// One location to compare. + /// The other location to compare. + /// True if the first location is after or the same as the other location, False otherwise. + public static bool operator >=(SourceLocation left, SourceLocation right) { + return left._index >= right._index; + } + + /// + /// Compares two specified location values. + /// + /// One location to compare. + /// The other location to compare. + /// 0 if the locations are equal, -1 if the left one is less than the right one, 1 otherwise. + public static int Compare(SourceLocation left, SourceLocation right) { + if (left < right) return -1; + if (right > left) return 1; + + return 0; + } + + /// + /// A location that is valid but represents no location at all. + /// + public static readonly SourceLocation None = new SourceLocation(0, 0xfeefee, 0, true); + + /// + /// An invalid location. + /// + public static readonly SourceLocation Invalid = new SourceLocation(0, 0, 0, true); + + /// + /// A minimal valid location. + /// + public static readonly SourceLocation MinValue = new SourceLocation(0, 1, 1); + + /// + /// Whether the location is a valid location. + /// + /// True if the location is valid, False otherwise. + public bool IsValid { + get { + return this._line != 0 && this._column != 0; + } + } + + public override bool Equals(object obj) { + if (!(obj is SourceLocation)) return false; + + SourceLocation other = (SourceLocation)obj; + return other._index == _index && other._line == _line && other._column == _column; + } + + public override int GetHashCode() { + return (_line << 16) ^ _column; + } + + public override string ToString() { + return "(" + _line + "," + _column + ")"; + } + + internal string ToDebugString() { + return String.Format(CultureInfo.CurrentCulture, "({0},{1},{2})", _index, _line, _column); + } + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/SourceSpan.cs b/merlin/main/runtime/Microsoft.Scripting/SourceSpan.cs new file mode 100644 index 0000000000..86b8aeb827 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/SourceSpan.cs @@ -0,0 +1,131 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Globalization; + +namespace Microsoft.Scripting { + + /// + /// Stores the location of a span of text in a source file. + /// + [Serializable] + public struct SourceSpan { + private readonly SourceLocation _start; + private readonly SourceLocation _end; + + /// + /// Constructs a new span with a specific start and end location. + /// + /// The beginning of the span. + /// The end of the span. + public SourceSpan(SourceLocation start, SourceLocation end) { + ValidateLocations(start, end); + this._start = start; + this._end = end; + } + + private static void ValidateLocations(SourceLocation start, SourceLocation end) { + if (start.IsValid && end.IsValid) { + if (start > end) { + throw new ArgumentException("Start and End must be well ordered"); + } + } else { + if (start.IsValid || end.IsValid) { + throw new ArgumentException("Start and End must both be valid or both invalid"); + } + } + } + + /// + /// The start location of the span. + /// + public SourceLocation Start { + get { return _start; } + } + + /// + /// The end location of the span. Location of the first character behind the span. + /// + public SourceLocation End { + get { return _end; } + } + + /// + /// Length of the span (number of characters inside the span). + /// + public int Length { + get { return _end.Index - _start.Index; } + } + + /// + /// A valid span that represents no location. + /// + public static readonly SourceSpan None = new SourceSpan(SourceLocation.None, SourceLocation.None); + + /// + /// An invalid span. + /// + public static readonly SourceSpan Invalid = new SourceSpan(SourceLocation.Invalid, SourceLocation.Invalid); + + /// + /// Whether the locations in the span are valid. + /// + public bool IsValid { + get { return _start.IsValid && _end.IsValid; } + } + + /// + /// Compares two specified Span values to see if they are equal. + /// + /// One span to compare. + /// The other span to compare. + /// True if the spans are the same, False otherwise. + public static bool operator ==(SourceSpan left, SourceSpan right) { + return left._start == right._start && left._end == right._end; + } + + /// + /// Compares two specified Span values to see if they are not equal. + /// + /// One span to compare. + /// The other span to compare. + /// True if the spans are not the same, False otherwise. + public static bool operator !=(SourceSpan left, SourceSpan right) { + return left._start != right._start || left._end != right._end; + } + + public override bool Equals(object obj) { + if (!(obj is SourceSpan)) return false; + + SourceSpan other = (SourceSpan)obj; + return _start == other._start && _end == other._end; + } + + public override string ToString() { + return _start.ToString() + " - " + _end.ToString(); + } + + public override int GetHashCode() { + // 7 bits for each column (0-128), 9 bits for each row (0-512), xor helps if + // we have a bigger file. + return (_start.Column) ^ (_end.Column << 7) ^ (_start.Line << 14) ^ (_end.Line << 23); + } + + internal string ToDebugString() { + return String.Format(CultureInfo.CurrentCulture, "{0}-{1}", _start.ToDebugString(), _end.ToDebugString()); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/SourceUnit.cs b/merlin/main/runtime/Microsoft.Scripting/SourceUnit.cs new file mode 100644 index 0000000000..d143e0bf30 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/SourceUnit.cs @@ -0,0 +1,253 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Dynamic; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; +using System.Text; + +namespace Microsoft.Scripting { + [DebuggerDisplay("{_path ?? \"\"}")] + public sealed class SourceUnit { + private readonly SourceCodeKind _kind; + private readonly string _path; + private readonly LanguageContext _language; + private readonly TextContentProvider _contentProvider; + + // SourceUnit is serializable => updated parse result is transmitted + // back to the host unless the unit is passed by-ref + private ScriptCodeParseResult? _parseResult; + private KeyValuePair[] _lineMap; + + /// + /// Identification of the source unit. Assigned by the host. + /// The format and semantics is host dependent (could be a path on file system or URL). + /// Empty string for anonymous source units. + /// + public string Path { + get { return _path; } + } + + public bool HasPath { + get { return _path != null; } + } + + public SourceCodeKind Kind { + get { return _kind; } + } + + public SymbolDocumentInfo Document { + get { + // _path is valid to be null. In that case we cannot create a valid SymbolDocumentInfo. + return _path == null ? null : Expression.SymbolDocument(_path, _language.LanguageGuid, _language.VendorGuid); + } + } + + /// + /// LanguageContext of the language of the unit. + /// + public LanguageContext LanguageContext { + get { return _language; } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public ScriptCodeParseResult GetCodeProperties() { + return GetCodeProperties(_language.GetCompilerOptions()); + } + + public ScriptCodeParseResult GetCodeProperties(CompilerOptions options) { + ContractUtils.RequiresNotNull(options, "options"); + + _language.CompileSourceCode(this, options, ErrorSink.Null); + return _parseResult ?? ScriptCodeParseResult.Complete; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] // TODO: fix + public ScriptCodeParseResult? CodeProperties { + get { return _parseResult; } + set { _parseResult = value; } + } + + public SourceUnit(LanguageContext context, TextContentProvider contentProvider, string path, SourceCodeKind kind) { + Assert.NotNull(context, contentProvider); + Debug.Assert(path == null || path.Length > 0); + Debug.Assert(context.CanCreateSourceCode); + + _language = context; + _contentProvider = contentProvider; + _kind = kind; + _path = path; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public SourceCodeReader GetReader() { + return _contentProvider.GetReader(); + } + + /// + /// Reads specified range of lines (or less) from the source unit. + /// Line numbers starts with 1. + /// + public string[] GetCodeLines(int start, int count) { + ContractUtils.Requires(start > 0, "start"); + ContractUtils.Requires(count > 0, "count"); + + List result = new List(count); + + using (SourceCodeReader reader = GetReader()) { + reader.SeekLine(start); + while (count > 0) { + string line = reader.ReadLine(); + if (line == null) break; + result.Add(line); + count--; + } + } + + return result.ToArray(); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public string GetCodeLine(int line) { + string[] lines = GetCodeLines(line, 1); + return (lines.Length > 0) ? lines[0] : null; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public string GetCode() { + using (SourceCodeReader reader = GetReader()) { + return reader.ReadToEnd(); + } + } + + #region Line/File mapping + + public SourceLocation MakeLocation(int index, int line, int column) { + return new SourceLocation(index, MapLine(line), column); + } + public SourceLocation MakeLocation(SourceLocation loc) { + return new SourceLocation(loc.Index, MapLine(loc.Line), loc.Column); + } + + public int MapLine(int line) { + if (_lineMap != null) { + int match = BinarySearch(_lineMap, line); + int delta = line - _lineMap[match].Key; + line = _lineMap[match].Value + delta; + if (line < 1) { + line = 1; // this is the minimum value + } + } + + return line; + } + + private static int BinarySearch(KeyValuePair[] array, int line) { + int match = Array.BinarySearch(array, new KeyValuePair(line, default(T)), new KeyComparer()); + if (match < 0) { + // If we couldn't find an exact match for this line number, get the nearest + // matching line number less than this one + match = ~match - 1; + + // If our index = -1, it means that this line is before any line numbers that + // we know about. If that's the case, use the first entry in the list + if (match == -1) { + match = 0; + } + } + return match; + } + + + private class KeyComparer : IComparer> { + public int Compare(KeyValuePair x, KeyValuePair y) { + return x.Key - y.Key; + } + } + + #endregion + + #region Parsing, Compilation, Execution + + public bool EmitDebugSymbols { + get { + return HasPath && LanguageContext.DomainManager.Configuration.DebugMode; + } + } + + public ScriptCode Compile() { + return Compile(ErrorSink.Default); + } + + public ScriptCode Compile(ErrorSink errorSink) { + return Compile(_language.GetCompilerOptions(), errorSink); + } + + /// + /// Errors are reported to the specified sink. + /// Returns null if the parser cannot compile the code due to error(s). + /// + public ScriptCode Compile(CompilerOptions options, ErrorSink errorSink) { + ContractUtils.RequiresNotNull(errorSink, "errorSink"); + ContractUtils.RequiresNotNull(options, "options"); + + return _language.CompileSourceCode(this, options, errorSink); + } + + public object Execute(Scope scope) { + return Execute(scope, ErrorSink.Default); + } + + public object Execute(Scope scope, ErrorSink errorSink) { + ContractUtils.RequiresNotNull(scope, "scope"); + + ScriptCode compiledCode = Compile(_language.GetCompilerOptions(scope), errorSink); + + if (compiledCode == null) { + throw new SyntaxErrorException(); + } + + return compiledCode.Run(scope); + } + + /// + /// Executes in an optimized scope. + /// + public object Execute() { + return Compile().Run(); + } + + /// + /// Executes in an optimized scope. + /// + public object Execute(CompilerOptions options, ErrorSink errorSink) { + return Compile(options, errorSink).Run(); + } + + public int ExecuteProgram() { + return _language.ExecuteProgram(this); + } + + #endregion + + public void SetLineMapping(KeyValuePair[] lineMap) { + _lineMap = (lineMap == null || lineMap.Length == 0) ? null : lineMap; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/SpecSharp.cs b/merlin/main/runtime/Microsoft.Scripting/SpecSharp.cs new file mode 100644 index 0000000000..3345059475 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/SpecSharp.cs @@ -0,0 +1,48 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SPECSHARP + +using System; +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Contracts { + [Conditional("SPECSHARP"), AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = false, Inherited = true)] + internal sealed class StateIndependentAttribute : Attribute { + } + + [Conditional("SPECSHARP"), AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = false, Inherited = true)] + internal sealed class PureAttribute : Attribute { + } + + [Conditional("SPECSHARP"), AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = false, Inherited = true)] + internal sealed class ConfinedAttribute : Attribute { + } + + [Conditional("SPECSHARP"), AttributeUsage(AttributeTargets.Field)] + internal sealed class StrictReadonlyAttribute : Attribute { + } + + internal static class NonNullType { + [DebuggerStepThrough] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")] + public static void AssertInitialized(T[] array) where T : class { + Assert.NotNullItems(array); + } + } +} + +#endif \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/SymbolId.cs b/merlin/main/runtime/Microsoft.Scripting/SymbolId.cs new file mode 100644 index 0000000000..1dc9721de0 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/SymbolId.cs @@ -0,0 +1,170 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.Serialization; +using System.Security.Permissions; +using Microsoft.Contracts; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting { + [Serializable] + public struct SymbolId : ISerializable, IComparable, IComparable, IEquatable { + private readonly int _id; + + public SymbolId(int value) { + _id = value; + } + + public SymbolId(SymbolId value) { + _id = value._id; + } + + public int Id { + get { return _id; } + } + + public SymbolId CaseInsensitiveIdentifier { + get { return new SymbolId(_id & 0x00FFFFFF); } + } + + public int CaseInsensitiveId { + get { return _id & 0x00FFFFFF; } + } + + [Confined] + public override bool Equals(object obj) { + if (!(obj is SymbolId)) return false; + SymbolId other = (SymbolId)obj; + return _id == other._id; + } + + [StateIndependent] + public bool Equals(SymbolId other) { + return _id == other._id; + } + + [Confined] + public bool CaseInsensitiveEquals(SymbolId other) { + return (_id & 0x00FFFFFF) == (other._id & 0x00FFFFFF); + } + + [Confined] + public override int GetHashCode() { + return _id; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + [Confined] + public int GetCaseInsensitiveHashCode() { + return (_id & 0x00FFFFFF); + } + + /// + /// Override of ToString. + /// DO NOT USE THIS METHOD TO RETRIEVE STRING THAT THE SYMBOL REPRESENTS + /// Use SymbolTable.IdToString(SymbolId) instead. + /// + [Confined] + public override string ToString() { + return SymbolTable.IdToString(this); + } + + public static explicit operator SymbolId(string s) { + return SymbolTable.StringToId(s); + } + + public static bool operator ==(SymbolId a, SymbolId b) { + return a._id == b._id; + } + + public static bool operator !=(SymbolId a, SymbolId b) { + return a._id != b._id; + } + + public static bool operator <(SymbolId a, SymbolId b) { + return a._id < b._id; + } + + public static bool operator >(SymbolId a, SymbolId b) { + return a._id > b._id; + } + + #region IComparable Members + + public int CompareTo(object obj) { + if (!(obj is SymbolId)) { + return -1; + } + + return CompareTo((SymbolId)obj); + } + + public int CompareTo(SymbolId other) { + // Note that we could just compare _id which will result in a faster comparison. However, that will + // mean that sorting will depend on the order in which the symbols were interned. This will often + // not be expected. Hence, we just compare the symbol strings + + string thisString = SymbolTable.IdToString(this); + string otherString = SymbolTable.IdToString(other); + return thisString.CompareTo(otherString); + } + + #endregion + + #region Cross-Domain/Process Serialization Support + +#if !SILVERLIGHT // Security, SerializationInfo, StreamingContext + // When leaving a context we serialize out our ID as a name + // rather than a raw ID. When we enter a new context we + // consult it's FieldTable to get the ID of the symbol name in + // the new context. + + private SymbolId(SerializationInfo info, StreamingContext context) { + ContractUtils.RequiresNotNull(info, "info"); + + _id = SymbolTable.StringToId(info.GetString("symbolName"))._id; + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] + public void GetObjectData(SerializationInfo info, StreamingContext context) { + ContractUtils.RequiresNotNull(info, "info"); + + info.AddValue("symbolName", SymbolTable.IdToString(this)); + } +#endif + + #endregion + + public const int EmptyId = 0; + /// SymbolId for null string + public static readonly SymbolId Empty = new SymbolId(EmptyId); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")] + public static readonly SymbolId[] EmptySymbols = new SymbolId[0]; + + public bool IsEmpty { + get { return _id == EmptyId; } + } + + public const int InvalidId = -1; + /// SymbolId to represent invalid value + public static readonly SymbolId Invalid = new SymbolId(InvalidId); + + public bool IsInvalid { + get { return _id == InvalidId; } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/SymbolTable.cs b/merlin/main/runtime/Microsoft.Scripting/SymbolTable.cs new file mode 100644 index 0000000000..b3e5b7c5ba --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/SymbolTable.cs @@ -0,0 +1,159 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting { + public static class SymbolTable { + private static readonly object _lockObj = new object(); + + private static readonly Dictionary _idDict = new Dictionary(InitialTableSize); + + private const int InitialTableSize = 256; + private static readonly Dictionary _fieldDict = CreateFieldDictionary(); + [MultiRuntimeAware] + private static int _nextCaseInsensitiveId = 1; + + private static Dictionary CreateFieldDictionary() { + Dictionary result = new Dictionary(InitialTableSize); + result[0] = null; // initialize the null string + return result; + } + + public static SymbolId StringToId(string field) { + ContractUtils.RequiresNotNull(field, "field"); + + int res; + lock (_lockObj) { + // First, look up the identifier case-sensitively. + if (!_idDict.TryGetValue(field, out res)) { + string invariantField = field.ToUpper(System.Globalization.CultureInfo.InvariantCulture); + + // OK, didn't find it, so let's look up the case-insensitive + // identifier. + if (_idDict.TryGetValue(invariantField, out res)) { + // OK, this is a new casing of an existing identifier. + Debug.Assert(res < 0, "Must have invariant bit set!"); + + // Throw if we've exhausted the number of casings. + if (unchecked(((uint)res & 0x00FFFFFF) == 0x00FFFFFF)) { + throw Error.CantAddCasing(field); + } + + int invariantRes = res + 0x01000000; + + // Mask off the high bit. + res = unchecked((int)((uint)res & 0x7FFFFFFF)); + + _idDict[field] = res; + _idDict[invariantField] = invariantRes; + _fieldDict[res] = field; + } else { + // This is a whole new identifier. + + if (_nextCaseInsensitiveId == int.MaxValue) { + throw Error.CantAddIdentifier(field); + } + + // register new id... + res = _nextCaseInsensitiveId++; + // Console.WriteLine("Registering {0} as {1}", field, res); + + _fieldDict[res] = invariantField; + + if (field != invariantField) { + res |= 0x01000000; + _idDict[field] = res; + _fieldDict[res] = field; + } + + _idDict[invariantField] = unchecked((int)(((uint)res | 0x80000000) + 0x01000000)); + } + } else { + // If this happens to be the invariant field, then we need to + // mask off the top byte, since that's just used to pick the next + // id for this identifier. + if (res < 0) { + res &= 0x00FFFFFF; + } + } + } + return new SymbolId(res); + } + + public static SymbolId StringToCaseInsensitiveId(string field) { + return StringToId(field.ToUpper(System.Globalization.CultureInfo.InvariantCulture)); + } + + public static SymbolId[] QualifiedStringToIds(string fields) { + if (fields != null) { + string[] strings = fields.Split('.'); + SymbolId[] identifiers = new SymbolId[strings.Length]; + + for (int i = 0; i < strings.Length; i++) { + identifiers[i] = StringToId(strings[i]); + } + + return identifiers; + } + + return null; + } + + public static string IdToString(SymbolId id) { + return _fieldDict[id.Id]; + } + + // Tries to lookup the SymbolId to see if it is valid + public static bool ContainsId(SymbolId id) { + return _fieldDict.ContainsKey(id.Id); + } + + public static string[] IdsToStrings(IList ids) { + string[] ret = new string[ids.Count]; + for (int i = 0; i < ids.Count; i++) { + if (ids[i] == SymbolId.Empty) ret[i] = null; + else ret[i] = IdToString(ids[i]); + } + return ret; + } + + public static SymbolId[] StringsToIds(IList strings) { + SymbolId[] ret = new SymbolId[strings.Count]; + for (int i = 0; i < strings.Count; i++) { + if (strings[i] == null) ret[i] = SymbolId.Empty; + else ret[i] = StringToId(strings[i]); + } + return ret; + } + + public static bool StringHasId(string symbol) { + ContractUtils.RequiresNotNull(symbol, "symbol"); + + lock (_lockObj) { + return _idDict.ContainsKey(symbol); + } + } + + internal static SymbolId StringToIdOrEmpty(string value) { + if (value == null) { + return SymbolId.Empty; + } + return StringToId(value); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/SyntaxErrorException.cs b/merlin/main/runtime/Microsoft.Scripting/SyntaxErrorException.cs new file mode 100644 index 0000000000..d2bb221080 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/SyntaxErrorException.cs @@ -0,0 +1,141 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.Serialization; +using System.Dynamic; +using System.Security.Permissions; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting { + + [Serializable] + public class SyntaxErrorException : Exception { + private SourceSpan _span; + + private string _sourceCode; + private string _sourceLine; + private string _sourcePath; + + private Severity _severity; + private int _errorCode; + + public SyntaxErrorException() : base() { } + + public SyntaxErrorException(string message) : base(message) { } + + public SyntaxErrorException(string message, Exception innerException) + : base(message, innerException) { + } + + public SyntaxErrorException(string message, SourceUnit sourceUnit, SourceSpan span, int errorCode, Severity severity) + : base(message) { + ContractUtils.RequiresNotNull(message, "message"); + + _span = span; + _severity = severity; + _errorCode = errorCode; + if (sourceUnit != null) { + _sourcePath = sourceUnit.Path; + try { + _sourceCode = sourceUnit.GetCode(); + _sourceLine = sourceUnit.GetCodeLine(Line); + } catch (System.IO.IOException) { + // could not get source code. + } + } + } + + public SyntaxErrorException(string message, string path, string code, string line, SourceSpan span, int errorCode, Severity severity) + : base(message) { + ContractUtils.RequiresNotNull(message, "message"); + + _span = span; + _severity = severity; + _errorCode = errorCode; + + _sourcePath = path; + _sourceCode = code; + _sourceLine = line; + } + +#if !SILVERLIGHT + protected SyntaxErrorException(SerializationInfo info, StreamingContext context) + : base(info, context) { + + _span = (SourceSpan)info.GetValue("Span", typeof(SourceSpan)); + _sourceCode = info.GetString("SourceCode"); + _sourcePath = info.GetString("SourcePath"); + _severity = (Severity)info.GetValue("Severity", typeof(Severity)); + _errorCode = info.GetInt32("ErrorCode"); + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] + public override void GetObjectData(SerializationInfo info, StreamingContext context) { + ContractUtils.RequiresNotNull(info, "info"); + + base.GetObjectData(info, context); + info.AddValue("Span", _span); + info.AddValue("SourceCode", _sourceCode); + info.AddValue("SourcePath", _sourcePath); + info.AddValue("Severity", _severity); + info.AddValue("ErrorCode", _errorCode); + } +#endif + + /// + /// Unmapped span. + /// + public SourceSpan RawSpan { + get { return _span; } + } + + public string SourceCode { + get { return _sourceCode; } + } + + public string SourcePath { + get { return _sourcePath; } + } + + public Severity Severity { + get { return _severity; } + } + + public int Line { + get { return _span.Start.Line; } + } + + public int Column { + get { return _span.Start.Column; } + } + + public int ErrorCode { + get { return _errorCode; } + } + + // TODO: fix + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public string GetSymbolDocumentName() { + return _sourcePath; + } + + // TODO: fix + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public string GetCodeLine() { + return _sourceLine; + } + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/TextContentProvider.cs b/merlin/main/runtime/Microsoft.Scripting/TextContentProvider.cs new file mode 100644 index 0000000000..41acd89011 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/TextContentProvider.cs @@ -0,0 +1,43 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Text; + +namespace Microsoft.Scripting { + + /// + /// Provides a factory to create TextReader's over one source of textual content. + /// + /// TextContentProvider's are used when reading from a source which is already decoded + /// or has a known specific decoding. + /// + /// For example a text editor might provide a TextContentProvider whose backing is + /// an in-memory text buffer that the user can actively edit. + /// + [Serializable] + public abstract class TextContentProvider { + + /// + /// Creates a new TextReader which is backed by the content the TextContentProvider was created for. + /// + /// This method may be called multiple times. For example once to compile the code and again to get + /// the source code to display error messages. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public abstract SourceCodeReader GetReader(); + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/TokenCategory.cs b/merlin/main/runtime/Microsoft.Scripting/TokenCategory.cs new file mode 100644 index 0000000000..fce16eff85 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/TokenCategory.cs @@ -0,0 +1,233 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting { + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] + public enum TokenCategory { + None, + + /// + /// A token marking an end of stream. + /// + EndOfStream, + + /// + /// A space, tab, or newline. + /// + WhiteSpace, + + /// + /// A block comment. + /// + Comment, + + /// + /// A single line comment. + /// + LineComment, + + /// + /// A documentation comment. + /// + DocComment, + + /// + /// A numeric literal. + /// + NumericLiteral, + + /// + /// A character literal. + /// + CharacterLiteral, + + /// + /// A string literal. + /// + StringLiteral, + + /// + /// A regular expression literal. + /// + RegularExpressionLiteral, + + /// + /// A keyword. + /// + Keyword, + + /// + /// A directive (e.g. #line). + /// + Directive, + + /// + /// A punctuation character that has a specific meaning in a language. + /// + Operator, + + /// + /// A token that operates as a separator between two language elements. + /// + Delimiter, + + /// + /// An identifier (variable, $variable, @variable, @@variable, $variable$, function!, function?, [variable], i'variable', ...) + /// + Identifier, + + /// + /// Braces, parenthesis, brackets. + /// + Grouping, + + /// + /// Errors. + /// + Error, + + LanguageDefined = 0x100 + } + + // not currently used, just for info + public enum TokenKind { + Default, + + // Errors: + Error, + + // Whitespace: + Whitespace, + EndOfLine, + LineJoin, // Python: \ + Indentation, + + // Comments: + SingleLineComment, // #..., //..., '... + MultiLineComment, // /* ... */ + NestableCommentStart, // Lua: --[[ + NestableCommentEnd, // ]] + + // DocComments: + SingleLineDocComment, // + MultiLineDocComment, // Ruby: =begin =end PHP: /** */ + + // Directives: + Directive, // #line, etc. + + // Keywords: + Keyword, + + // Identifiers: + Identifier, // identifier + VerbatimIdentifier, // PHP/CLR: i'...', + Variable, // Ruby: @identifier, @@identifier; PHP, Ruby: $identifier, + + // Numbers: + IntegerLiteral, + FloatLiteral, + + // Characters: + CharacterLiteral, + + // Strings: + String, + UnicodeString, + FormattedString, + FormattedUnicodeString, + + // Groupings: + LeftParenthesis, // ( + RightParenthesis, // ) + LeftBracket, // [ + RightBracket, // ] + LeftBrace, // { + RightBrace, // } + + // Delimiters: + Comma, // , + Dot, // . + Semicolon, // ; + Colon, // : + DoubleColon, // :: + TripleColon, // PHP/CLR: ::: + + // Operators: + Plus, // + + PlusPlus, // ++ + PlusEqual, // += + Minus, // - + MinusMinus, // -- + MinusEqual, // -= + Mul, // * + MulEqual, // *= + Div, // / + DivEqual, // /= + FloorDivide, // // + FloorDivideEqual, // //= + Mod, // % + ModEqual, // %= + Power, // Python: ** + PowerEqual, // Python, Ruby: **= + LeftShift, // << + LeftShiftEqual, // <<= + RightShift, // >> + RightShiftEqual, // >>= + BitwiseAnd, // & + BitwiseAndEqual, // &= + BitwiseOr, // | + BitwiseOrEqual, // |= + Xor, // ^ + XorEqual, // ^= + BooleanAnd, // && + BooleanAndEqual, // Ruby: &&= + BooleanOr, // || + BooleanOrEqual, // Ruby: ||= + Twiddle, // ~ + TwiddleEqual, // ~= + LessThan, // < + GreaterThan, // > + LessThanOrEqual, // <= + GreaterThanOrEqual, // >= + Assign, // = + AssignAlias, // PHP: =& + AssignColon, // := + Equal, // == + StrictEqual, // === + Not, // ! + NotEqual, // != + StrictNotEqual, // !== + Unequal, // <> + CompareEqual, // Ruby: <=> + Match, // =~ + NotMatch, // !~ + Arrow, // PHP: -> + DoubleArrow, // PHP, Ruby: => + BackQuote, // ` + DoubleDot, // Ruby: .. + TripleDot, // Ruby: ... + At, // @ + DoubleAt, // @@ + Question, // ? + DoubleQuestion, // ?? + Backslash, // \ + DoubleBackslash, // \\ + Dollar, // $ + DoubleDollar, // $$ + + LanguageDefined, + } +} \ No newline at end of file diff --git a/merlin/main/runtime/Microsoft.Scripting/Tuple.cs b/merlin/main/runtime/Microsoft.Scripting/Tuple.cs new file mode 100644 index 0000000000..976eb4e0d1 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Tuple.cs @@ -0,0 +1,1846 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using System.Dynamic; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting { + public abstract class Tuple { + public const int MaxSize = 128; + private static readonly Dictionary _sizeDict = new Dictionary(); + + public abstract object GetValue(int index); + public abstract void SetValue(int index, object value); + + internal void SetValue(int size, int index, object value) { + if (size < Tuple.MaxSize) { + // fast path + SetValue(index, value); + } else { + // slow path + Tuple res = this; + int lastAccess = -1; + foreach (int i in GetAccessPath(size, index)) { + if (lastAccess != -1) { + res = (Tuple)res.GetValue(lastAccess); + } + lastAccess = i; + } + res.SetValue(lastAccess, value); + } + } + + internal object GetValue(int size, int index) { + if (size < Tuple.MaxSize) { + // fast path + return GetValue(index); + } else { + // slow path + object res = this; + foreach (int i in GetAccessPath(size, index)) { + res = ((Tuple)res).GetValue(i); + } + return res; + } + } + + /// + /// Gets the unbound generic Tuple type which has at lease size slots or null if a large enough tuple is not available. + /// + public static Type GetTupleType(int size) { + #region Generated Tuple Get From Size + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_get_size from: generate_tuples.py + + if (size <= Tuple.MaxSize) { + if (size <= 1) { + return typeof(Tuple<>); + } else if (size <= 2) { + return typeof(Tuple<, >); + } else if (size <= 4) { + return typeof(Tuple<, , , >); + } else if (size <= 8) { + return typeof(Tuple<, , , , , , , >); + } else if (size <= 16) { + return typeof(Tuple<, , , , , , , , , , , , , , , >); + } else if (size <= 32) { + return typeof(Tuple<, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , >); + } else if (size <= 64) { + return typeof(Tuple<, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , >); + } else { + return typeof(Tuple<, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , >); + } + } + + // *** END GENERATED CODE *** + + #endregion + + return null; + } + + /// + /// Creates a generic tuple with the specified types. + /// + /// If the number of slots fits within the maximum tuple size then we simply + /// create a single tuple. If it's greater then we create nested tuples + /// (e.g. a Tuple`2 which contains a Tuple`128 and a Tuple`8 if we had a size of 136). + /// + public static Type MakeTupleType(params Type[] types) { + ContractUtils.RequiresNotNull(types, "types"); + + return MakeTupleType(types, 0, types.Length); + } + + /// + /// Gets the number of usable slots in the provided Tuple type including slots available in nested tuples. + /// + public static int GetSize(Type tupleType) { + ContractUtils.RequiresNotNull(tupleType, "tupleType"); + + int count = 0; + lock(_sizeDict) if (_sizeDict.TryGetValue(tupleType, out count)) return count; + Stack types = new Stack(tupleType.GetGenericArguments()); + + while (types.Count != 0) { + Type t = types.Pop(); + + if (typeof(Tuple).IsAssignableFrom(t)) { + foreach (Type subtype in t.GetGenericArguments()) { + types.Push(subtype); + } + continue; + } + + if (t == typeof(Null)) continue; + + count++; + } + + lock (_sizeDict) _sizeDict[tupleType] = count; + return count; + } + + /// + /// Creates a new instance of tupleType with the specified args. If the tuple is a nested + /// tuple the values are added in their nested forms. + /// + public static Tuple MakeTuple(Type tupleType, params object[] args) { + ContractUtils.RequiresNotNull(tupleType, "tupleType"); + ContractUtils.RequiresNotNull(args, "args"); + + return MakeTuple(tupleType, 0, args.Length, args); + } + + /// + /// Gets the values from a tuple including unpacking nested values. + /// + public static object[] GetTupleValues(Tuple tuple) { + ContractUtils.RequiresNotNull(tuple, "tuple"); + + List res = new List(); + + GetTupleValues(tuple, res); + + return res.ToArray(); + } + + /// + /// Gets the series of properties that needs to be accessed to access a logical item in a potentially nested tuple. + /// + public static IEnumerable GetAccessPath(Type tupleType, int index) { + return GetAccessProperties(tupleType, GetSize(tupleType), index); + } + + /// + /// Gets the series of properties that needs to be accessed to access a logical item in a potentially nested tuple. + /// + internal static IEnumerable GetAccessProperties(Type tupleType, int size, int index) { + ContractUtils.RequiresNotNull(tupleType, "tupleType"); + + if (index < 0 || index >= size) throw new ArgumentException("index"); + + foreach (int curIndex in GetAccessPath(size, index)) { + PropertyInfo pi = tupleType.GetProperty("Item" + String.Format("{0:D3}", curIndex)); + Debug.Assert(pi != null); + yield return pi; + tupleType = pi.PropertyType; + } + } + + internal static IEnumerable GetAccessPath(int size, int index) { + // We get the final index by breaking the index into groups of bits. The more significant bits + // represent the indexes into the outermost tuples and the least significant bits index into the + // inner most tuples. The mask is initialized to mask the upper bits and adjust is initialized + // and adjust is the value we need to divide by to get the index in the least significant bits. + // As we go through we shift the mask and adjust down each loop to pull out the inner slot. Logically + // everything in here is shifting bits (not multiplying or dividing) because NewTuple.MaxSize is a + // power of 2. + int depth = 0; + int mask = Tuple.MaxSize - 1; + int adjust = 1; + int count = size; + while (count > Tuple.MaxSize) { + depth++; + count /= Tuple.MaxSize; + mask *= Tuple.MaxSize; + adjust *= Tuple.MaxSize; + } + + while (depth-- >= 0) { + Debug.Assert(mask != 0); + + int curIndex = (index & mask) / adjust; + + yield return curIndex; + + mask /= Tuple.MaxSize; + adjust /= Tuple.MaxSize; + } + } + + private static void GetTupleValues(Tuple tuple, List args) { + Type[] types = tuple.GetType().GetGenericArguments(); + for (int i = 0; i < types.Length; i++) { + if (typeof(Tuple).IsAssignableFrom(types[i])) { + GetTupleValues((Tuple)tuple.GetValue(i), args); + } else if (types[i] != typeof(Null)) { + args.Add(tuple.GetValue(i)); + } + } + } + + private static Tuple MakeTuple(Type tupleType, int start, int end, object[] args) { + int size = end - start; + + Tuple res = (Tuple)Activator.CreateInstance(tupleType); + if (size > Tuple.MaxSize) { + int multiplier = 1; + while (size > Tuple.MaxSize) { + size = (size + Tuple.MaxSize - 1) / Tuple.MaxSize; + multiplier *= Tuple.MaxSize; + } + for (int i = 0; i < size; i++) { + int newStart = start + (i * multiplier); + int newEnd = System.Math.Min(end, start + ((i + 1) * multiplier)); + + PropertyInfo pi = tupleType.GetProperty("Item" + String.Format("{0:D3}", i)); + res.SetValue(i, CreateTupleInstance(pi.PropertyType, newStart, newEnd, args)); + } + } else { + int argCnt = tupleType.GetGenericArguments().Length; + for (int i = start; i < end; i++) { + res.SetValue(i - start, args[i]); + } + } + + return res; + + } + + private static Tuple CreateTupleInstance(Type tupleType, int start, int end, object[] args) { + if (args == null) return (Tuple)Activator.CreateInstance(tupleType); + + object[] realArgs = new object[tupleType.GetGenericArguments().Length]; + Array.Copy(args, start, realArgs, 0, end - start); + return (Tuple)Activator.CreateInstance(tupleType, realArgs); + } + + private static Type MakeTupleType(Type[] types, int start, int end) { + int size = end - start; + + Type type = GetTupleType(size); + if (type != null) { + Type[] typeArr = new Type[type.GetGenericArguments().Length]; + int index = 0; + for (int i = start; i < end; i++) { + typeArr[index++] = types[i]; + } + while (index < typeArr.Length) { + typeArr[index++] = typeof(Null); + } + return type.MakeGenericType(typeArr); + } + + int multiplier = 1; + while (size > Tuple.MaxSize) { + size = (size + Tuple.MaxSize - 1) / Tuple.MaxSize; + multiplier *= Tuple.MaxSize; + } + + type = Tuple.GetTupleType(size); + Debug.Assert(type != null); + Type[] nestedTypes = new Type[type.GetGenericArguments().Length]; + for (int i = 0; i < size; i++) { + + int newStart = start + (i * multiplier); + int newEnd = System.Math.Min(end, start + ((i + 1) * multiplier)); + nestedTypes[i] = MakeTupleType(types, newStart, newEnd); + } + for (int i = size; i < nestedTypes.Length; i++) { + nestedTypes[i] = typeof(Null); + } + + return type.MakeGenericType(nestedTypes); + } + + public abstract int Capacity { + get; + } + + internal static void EmitNew(ILGen cg, Type tupleType, int start, int end) { + int size = end - start; + Debug.Assert(tupleType != null); + Debug.Assert(tupleType.IsSubclassOf(typeof(Tuple))); + + cg.EmitNew(tupleType.GetConstructor(Type.EmptyTypes)); + + if (size > Tuple.MaxSize) { + int multiplier = 1; + while (size > Tuple.MaxSize) { + size = (size + Tuple.MaxSize - 1) / Tuple.MaxSize; + multiplier *= Tuple.MaxSize; + } + for (int i = 0; i < size; i++) { + int newStart = start + (i * multiplier); + int newEnd = System.Math.Min(end, start + ((i + 1) * multiplier)); + + PropertyInfo pi = tupleType.GetProperty("Item" + String.Format("{0:D3}", i)); + + // dup tuple that we originally constructed + cg.Emit(OpCodes.Dup); + + // construct nested tuple + EmitNew(cg, pi.PropertyType, newStart, newEnd); + + // save into original tuple + cg.EmitPropertySet(pi); + } + } + } + } + + #region Generated Tuples + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_tuples from: generate_tuples.py + + [GeneratedCode("DLR", "2.0")] + public class Tuple : Tuple { + public Tuple() { } + + public Tuple(T0 item0) + : base() { + _item0 = item0; + } + + private T0 _item0; + + public T0 Item000 { + get { return _item0; } + set { _item0 = value; } + } + + public override object GetValue(int index) { + switch(index) { + case 0: return Item000; + default: throw new ArgumentOutOfRangeException("index"); + } + } + + public override void SetValue(int index, object value) { + switch(index) { + case 0: Item000 = (T0)value; break; + default: throw new ArgumentOutOfRangeException("index"); + } + } + public override int Capacity { + get { + return 1; + } + } + } + [GeneratedCode("DLR", "2.0")] + public class Tuple : Tuple { + public Tuple() { } + + public Tuple(T0 item0, T1 item1) + : base(item0) { + _item1 = item1; + } + + private T1 _item1; + + public T1 Item001 { + get { return _item1; } + set { _item1 = value; } + } + + public override object GetValue(int index) { + switch(index) { + case 0: return Item000; + case 1: return Item001; + default: throw new ArgumentOutOfRangeException("index"); + } + } + + public override void SetValue(int index, object value) { + switch(index) { + case 0: Item000 = (T0)value; break; + case 1: Item001 = (T1)value; break; + default: throw new ArgumentOutOfRangeException("index"); + } + } + public override int Capacity { + get { + return 2; + } + } + } + [GeneratedCode("DLR", "2.0")] + public class Tuple : Tuple { + public Tuple() { } + + public Tuple(T0 item0, T1 item1, T2 item2, T3 item3) + : base(item0, item1) { + _item2 = item2; + _item3 = item3; + } + + private T2 _item2; + private T3 _item3; + + public T2 Item002 { + get { return _item2; } + set { _item2 = value; } + } + public T3 Item003 { + get { return _item3; } + set { _item3 = value; } + } + + public override object GetValue(int index) { + switch(index) { + case 0: return Item000; + case 1: return Item001; + case 2: return Item002; + case 3: return Item003; + default: throw new ArgumentOutOfRangeException("index"); + } + } + + public override void SetValue(int index, object value) { + switch(index) { + case 0: Item000 = (T0)value; break; + case 1: Item001 = (T1)value; break; + case 2: Item002 = (T2)value; break; + case 3: Item003 = (T3)value; break; + default: throw new ArgumentOutOfRangeException("index"); + } + } + public override int Capacity { + get { + return 4; + } + } + } + [GeneratedCode("DLR", "2.0")] + public class Tuple : Tuple { + public Tuple() { } + + public Tuple(T0 item0, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) + : base(item0, item1, item2, item3) { + _item4 = item4; + _item5 = item5; + _item6 = item6; + _item7 = item7; + } + + private T4 _item4; + private T5 _item5; + private T6 _item6; + private T7 _item7; + + public T4 Item004 { + get { return _item4; } + set { _item4 = value; } + } + public T5 Item005 { + get { return _item5; } + set { _item5 = value; } + } + public T6 Item006 { + get { return _item6; } + set { _item6 = value; } + } + public T7 Item007 { + get { return _item7; } + set { _item7 = value; } + } + + public override object GetValue(int index) { + switch(index) { + case 0: return Item000; + case 1: return Item001; + case 2: return Item002; + case 3: return Item003; + case 4: return Item004; + case 5: return Item005; + case 6: return Item006; + case 7: return Item007; + default: throw new ArgumentOutOfRangeException("index"); + } + } + + public override void SetValue(int index, object value) { + switch(index) { + case 0: Item000 = (T0)value; break; + case 1: Item001 = (T1)value; break; + case 2: Item002 = (T2)value; break; + case 3: Item003 = (T3)value; break; + case 4: Item004 = (T4)value; break; + case 5: Item005 = (T5)value; break; + case 6: Item006 = (T6)value; break; + case 7: Item007 = (T7)value; break; + default: throw new ArgumentOutOfRangeException("index"); + } + } + public override int Capacity { + get { + return 8; + } + } + } + [GeneratedCode("DLR", "2.0")] + public class Tuple : Tuple { + public Tuple() { } + + public Tuple(T0 item0, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8, T9 item9, T10 item10, T11 item11, T12 item12, T13 item13, T14 item14, T15 item15) + : base(item0, item1, item2, item3, item4, item5, item6, item7) { + _item8 = item8; + _item9 = item9; + _item10 = item10; + _item11 = item11; + _item12 = item12; + _item13 = item13; + _item14 = item14; + _item15 = item15; + } + + private T8 _item8; + private T9 _item9; + private T10 _item10; + private T11 _item11; + private T12 _item12; + private T13 _item13; + private T14 _item14; + private T15 _item15; + + public T8 Item008 { + get { return _item8; } + set { _item8 = value; } + } + public T9 Item009 { + get { return _item9; } + set { _item9 = value; } + } + public T10 Item010 { + get { return _item10; } + set { _item10 = value; } + } + public T11 Item011 { + get { return _item11; } + set { _item11 = value; } + } + public T12 Item012 { + get { return _item12; } + set { _item12 = value; } + } + public T13 Item013 { + get { return _item13; } + set { _item13 = value; } + } + public T14 Item014 { + get { return _item14; } + set { _item14 = value; } + } + public T15 Item015 { + get { return _item15; } + set { _item15 = value; } + } + + public override object GetValue(int index) { + switch(index) { + case 0: return Item000; + case 1: return Item001; + case 2: return Item002; + case 3: return Item003; + case 4: return Item004; + case 5: return Item005; + case 6: return Item006; + case 7: return Item007; + case 8: return Item008; + case 9: return Item009; + case 10: return Item010; + case 11: return Item011; + case 12: return Item012; + case 13: return Item013; + case 14: return Item014; + case 15: return Item015; + default: throw new ArgumentOutOfRangeException("index"); + } + } + + public override void SetValue(int index, object value) { + switch(index) { + case 0: Item000 = (T0)value; break; + case 1: Item001 = (T1)value; break; + case 2: Item002 = (T2)value; break; + case 3: Item003 = (T3)value; break; + case 4: Item004 = (T4)value; break; + case 5: Item005 = (T5)value; break; + case 6: Item006 = (T6)value; break; + case 7: Item007 = (T7)value; break; + case 8: Item008 = (T8)value; break; + case 9: Item009 = (T9)value; break; + case 10: Item010 = (T10)value; break; + case 11: Item011 = (T11)value; break; + case 12: Item012 = (T12)value; break; + case 13: Item013 = (T13)value; break; + case 14: Item014 = (T14)value; break; + case 15: Item015 = (T15)value; break; + default: throw new ArgumentOutOfRangeException("index"); + } + } + public override int Capacity { + get { + return 16; + } + } + } + [GeneratedCode("DLR", "2.0")] + public class Tuple : Tuple { + public Tuple() { } + + public Tuple(T0 item0, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8, T9 item9, T10 item10, T11 item11, T12 item12, T13 item13, T14 item14, T15 item15, T16 item16, T17 item17, T18 item18, T19 item19, T20 item20, T21 item21, T22 item22, T23 item23, T24 item24, T25 item25, T26 item26, T27 item27, T28 item28, T29 item29, T30 item30, T31 item31) + : base(item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15) { + _item16 = item16; + _item17 = item17; + _item18 = item18; + _item19 = item19; + _item20 = item20; + _item21 = item21; + _item22 = item22; + _item23 = item23; + _item24 = item24; + _item25 = item25; + _item26 = item26; + _item27 = item27; + _item28 = item28; + _item29 = item29; + _item30 = item30; + _item31 = item31; + } + + private T16 _item16; + private T17 _item17; + private T18 _item18; + private T19 _item19; + private T20 _item20; + private T21 _item21; + private T22 _item22; + private T23 _item23; + private T24 _item24; + private T25 _item25; + private T26 _item26; + private T27 _item27; + private T28 _item28; + private T29 _item29; + private T30 _item30; + private T31 _item31; + + public T16 Item016 { + get { return _item16; } + set { _item16 = value; } + } + public T17 Item017 { + get { return _item17; } + set { _item17 = value; } + } + public T18 Item018 { + get { return _item18; } + set { _item18 = value; } + } + public T19 Item019 { + get { return _item19; } + set { _item19 = value; } + } + public T20 Item020 { + get { return _item20; } + set { _item20 = value; } + } + public T21 Item021 { + get { return _item21; } + set { _item21 = value; } + } + public T22 Item022 { + get { return _item22; } + set { _item22 = value; } + } + public T23 Item023 { + get { return _item23; } + set { _item23 = value; } + } + public T24 Item024 { + get { return _item24; } + set { _item24 = value; } + } + public T25 Item025 { + get { return _item25; } + set { _item25 = value; } + } + public T26 Item026 { + get { return _item26; } + set { _item26 = value; } + } + public T27 Item027 { + get { return _item27; } + set { _item27 = value; } + } + public T28 Item028 { + get { return _item28; } + set { _item28 = value; } + } + public T29 Item029 { + get { return _item29; } + set { _item29 = value; } + } + public T30 Item030 { + get { return _item30; } + set { _item30 = value; } + } + public T31 Item031 { + get { return _item31; } + set { _item31 = value; } + } + + public override object GetValue(int index) { + switch(index) { + case 0: return Item000; + case 1: return Item001; + case 2: return Item002; + case 3: return Item003; + case 4: return Item004; + case 5: return Item005; + case 6: return Item006; + case 7: return Item007; + case 8: return Item008; + case 9: return Item009; + case 10: return Item010; + case 11: return Item011; + case 12: return Item012; + case 13: return Item013; + case 14: return Item014; + case 15: return Item015; + case 16: return Item016; + case 17: return Item017; + case 18: return Item018; + case 19: return Item019; + case 20: return Item020; + case 21: return Item021; + case 22: return Item022; + case 23: return Item023; + case 24: return Item024; + case 25: return Item025; + case 26: return Item026; + case 27: return Item027; + case 28: return Item028; + case 29: return Item029; + case 30: return Item030; + case 31: return Item031; + default: throw new ArgumentOutOfRangeException("index"); + } + } + + public override void SetValue(int index, object value) { + switch(index) { + case 0: Item000 = (T0)value; break; + case 1: Item001 = (T1)value; break; + case 2: Item002 = (T2)value; break; + case 3: Item003 = (T3)value; break; + case 4: Item004 = (T4)value; break; + case 5: Item005 = (T5)value; break; + case 6: Item006 = (T6)value; break; + case 7: Item007 = (T7)value; break; + case 8: Item008 = (T8)value; break; + case 9: Item009 = (T9)value; break; + case 10: Item010 = (T10)value; break; + case 11: Item011 = (T11)value; break; + case 12: Item012 = (T12)value; break; + case 13: Item013 = (T13)value; break; + case 14: Item014 = (T14)value; break; + case 15: Item015 = (T15)value; break; + case 16: Item016 = (T16)value; break; + case 17: Item017 = (T17)value; break; + case 18: Item018 = (T18)value; break; + case 19: Item019 = (T19)value; break; + case 20: Item020 = (T20)value; break; + case 21: Item021 = (T21)value; break; + case 22: Item022 = (T22)value; break; + case 23: Item023 = (T23)value; break; + case 24: Item024 = (T24)value; break; + case 25: Item025 = (T25)value; break; + case 26: Item026 = (T26)value; break; + case 27: Item027 = (T27)value; break; + case 28: Item028 = (T28)value; break; + case 29: Item029 = (T29)value; break; + case 30: Item030 = (T30)value; break; + case 31: Item031 = (T31)value; break; + default: throw new ArgumentOutOfRangeException("index"); + } + } + public override int Capacity { + get { + return 32; + } + } + } + [GeneratedCode("DLR", "2.0")] + public class Tuple : Tuple { + public Tuple() { } + + public Tuple(T0 item0, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8, T9 item9, T10 item10, T11 item11, T12 item12, T13 item13, T14 item14, T15 item15, T16 item16, T17 item17, T18 item18, T19 item19, T20 item20, T21 item21, T22 item22, T23 item23, T24 item24, T25 item25, T26 item26, T27 item27, T28 item28, T29 item29, T30 item30, T31 item31, T32 item32, T33 item33, T34 item34, T35 item35, T36 item36, T37 item37, T38 item38, T39 item39, T40 item40, T41 item41, T42 item42, T43 item43, T44 item44, T45 item45, T46 item46, T47 item47, T48 item48, T49 item49, T50 item50, T51 item51, T52 item52, T53 item53, T54 item54, T55 item55, T56 item56, T57 item57, T58 item58, T59 item59, T60 item60, T61 item61, T62 item62, T63 item63) + : base(item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18, item19, item20, item21, item22, item23, item24, item25, item26, item27, item28, item29, item30, item31) { + _item32 = item32; + _item33 = item33; + _item34 = item34; + _item35 = item35; + _item36 = item36; + _item37 = item37; + _item38 = item38; + _item39 = item39; + _item40 = item40; + _item41 = item41; + _item42 = item42; + _item43 = item43; + _item44 = item44; + _item45 = item45; + _item46 = item46; + _item47 = item47; + _item48 = item48; + _item49 = item49; + _item50 = item50; + _item51 = item51; + _item52 = item52; + _item53 = item53; + _item54 = item54; + _item55 = item55; + _item56 = item56; + _item57 = item57; + _item58 = item58; + _item59 = item59; + _item60 = item60; + _item61 = item61; + _item62 = item62; + _item63 = item63; + } + + private T32 _item32; + private T33 _item33; + private T34 _item34; + private T35 _item35; + private T36 _item36; + private T37 _item37; + private T38 _item38; + private T39 _item39; + private T40 _item40; + private T41 _item41; + private T42 _item42; + private T43 _item43; + private T44 _item44; + private T45 _item45; + private T46 _item46; + private T47 _item47; + private T48 _item48; + private T49 _item49; + private T50 _item50; + private T51 _item51; + private T52 _item52; + private T53 _item53; + private T54 _item54; + private T55 _item55; + private T56 _item56; + private T57 _item57; + private T58 _item58; + private T59 _item59; + private T60 _item60; + private T61 _item61; + private T62 _item62; + private T63 _item63; + + public T32 Item032 { + get { return _item32; } + set { _item32 = value; } + } + public T33 Item033 { + get { return _item33; } + set { _item33 = value; } + } + public T34 Item034 { + get { return _item34; } + set { _item34 = value; } + } + public T35 Item035 { + get { return _item35; } + set { _item35 = value; } + } + public T36 Item036 { + get { return _item36; } + set { _item36 = value; } + } + public T37 Item037 { + get { return _item37; } + set { _item37 = value; } + } + public T38 Item038 { + get { return _item38; } + set { _item38 = value; } + } + public T39 Item039 { + get { return _item39; } + set { _item39 = value; } + } + public T40 Item040 { + get { return _item40; } + set { _item40 = value; } + } + public T41 Item041 { + get { return _item41; } + set { _item41 = value; } + } + public T42 Item042 { + get { return _item42; } + set { _item42 = value; } + } + public T43 Item043 { + get { return _item43; } + set { _item43 = value; } + } + public T44 Item044 { + get { return _item44; } + set { _item44 = value; } + } + public T45 Item045 { + get { return _item45; } + set { _item45 = value; } + } + public T46 Item046 { + get { return _item46; } + set { _item46 = value; } + } + public T47 Item047 { + get { return _item47; } + set { _item47 = value; } + } + public T48 Item048 { + get { return _item48; } + set { _item48 = value; } + } + public T49 Item049 { + get { return _item49; } + set { _item49 = value; } + } + public T50 Item050 { + get { return _item50; } + set { _item50 = value; } + } + public T51 Item051 { + get { return _item51; } + set { _item51 = value; } + } + public T52 Item052 { + get { return _item52; } + set { _item52 = value; } + } + public T53 Item053 { + get { return _item53; } + set { _item53 = value; } + } + public T54 Item054 { + get { return _item54; } + set { _item54 = value; } + } + public T55 Item055 { + get { return _item55; } + set { _item55 = value; } + } + public T56 Item056 { + get { return _item56; } + set { _item56 = value; } + } + public T57 Item057 { + get { return _item57; } + set { _item57 = value; } + } + public T58 Item058 { + get { return _item58; } + set { _item58 = value; } + } + public T59 Item059 { + get { return _item59; } + set { _item59 = value; } + } + public T60 Item060 { + get { return _item60; } + set { _item60 = value; } + } + public T61 Item061 { + get { return _item61; } + set { _item61 = value; } + } + public T62 Item062 { + get { return _item62; } + set { _item62 = value; } + } + public T63 Item063 { + get { return _item63; } + set { _item63 = value; } + } + + public override object GetValue(int index) { + switch(index) { + case 0: return Item000; + case 1: return Item001; + case 2: return Item002; + case 3: return Item003; + case 4: return Item004; + case 5: return Item005; + case 6: return Item006; + case 7: return Item007; + case 8: return Item008; + case 9: return Item009; + case 10: return Item010; + case 11: return Item011; + case 12: return Item012; + case 13: return Item013; + case 14: return Item014; + case 15: return Item015; + case 16: return Item016; + case 17: return Item017; + case 18: return Item018; + case 19: return Item019; + case 20: return Item020; + case 21: return Item021; + case 22: return Item022; + case 23: return Item023; + case 24: return Item024; + case 25: return Item025; + case 26: return Item026; + case 27: return Item027; + case 28: return Item028; + case 29: return Item029; + case 30: return Item030; + case 31: return Item031; + case 32: return Item032; + case 33: return Item033; + case 34: return Item034; + case 35: return Item035; + case 36: return Item036; + case 37: return Item037; + case 38: return Item038; + case 39: return Item039; + case 40: return Item040; + case 41: return Item041; + case 42: return Item042; + case 43: return Item043; + case 44: return Item044; + case 45: return Item045; + case 46: return Item046; + case 47: return Item047; + case 48: return Item048; + case 49: return Item049; + case 50: return Item050; + case 51: return Item051; + case 52: return Item052; + case 53: return Item053; + case 54: return Item054; + case 55: return Item055; + case 56: return Item056; + case 57: return Item057; + case 58: return Item058; + case 59: return Item059; + case 60: return Item060; + case 61: return Item061; + case 62: return Item062; + case 63: return Item063; + default: throw new ArgumentOutOfRangeException("index"); + } + } + + public override void SetValue(int index, object value) { + switch(index) { + case 0: Item000 = (T0)value; break; + case 1: Item001 = (T1)value; break; + case 2: Item002 = (T2)value; break; + case 3: Item003 = (T3)value; break; + case 4: Item004 = (T4)value; break; + case 5: Item005 = (T5)value; break; + case 6: Item006 = (T6)value; break; + case 7: Item007 = (T7)value; break; + case 8: Item008 = (T8)value; break; + case 9: Item009 = (T9)value; break; + case 10: Item010 = (T10)value; break; + case 11: Item011 = (T11)value; break; + case 12: Item012 = (T12)value; break; + case 13: Item013 = (T13)value; break; + case 14: Item014 = (T14)value; break; + case 15: Item015 = (T15)value; break; + case 16: Item016 = (T16)value; break; + case 17: Item017 = (T17)value; break; + case 18: Item018 = (T18)value; break; + case 19: Item019 = (T19)value; break; + case 20: Item020 = (T20)value; break; + case 21: Item021 = (T21)value; break; + case 22: Item022 = (T22)value; break; + case 23: Item023 = (T23)value; break; + case 24: Item024 = (T24)value; break; + case 25: Item025 = (T25)value; break; + case 26: Item026 = (T26)value; break; + case 27: Item027 = (T27)value; break; + case 28: Item028 = (T28)value; break; + case 29: Item029 = (T29)value; break; + case 30: Item030 = (T30)value; break; + case 31: Item031 = (T31)value; break; + case 32: Item032 = (T32)value; break; + case 33: Item033 = (T33)value; break; + case 34: Item034 = (T34)value; break; + case 35: Item035 = (T35)value; break; + case 36: Item036 = (T36)value; break; + case 37: Item037 = (T37)value; break; + case 38: Item038 = (T38)value; break; + case 39: Item039 = (T39)value; break; + case 40: Item040 = (T40)value; break; + case 41: Item041 = (T41)value; break; + case 42: Item042 = (T42)value; break; + case 43: Item043 = (T43)value; break; + case 44: Item044 = (T44)value; break; + case 45: Item045 = (T45)value; break; + case 46: Item046 = (T46)value; break; + case 47: Item047 = (T47)value; break; + case 48: Item048 = (T48)value; break; + case 49: Item049 = (T49)value; break; + case 50: Item050 = (T50)value; break; + case 51: Item051 = (T51)value; break; + case 52: Item052 = (T52)value; break; + case 53: Item053 = (T53)value; break; + case 54: Item054 = (T54)value; break; + case 55: Item055 = (T55)value; break; + case 56: Item056 = (T56)value; break; + case 57: Item057 = (T57)value; break; + case 58: Item058 = (T58)value; break; + case 59: Item059 = (T59)value; break; + case 60: Item060 = (T60)value; break; + case 61: Item061 = (T61)value; break; + case 62: Item062 = (T62)value; break; + case 63: Item063 = (T63)value; break; + default: throw new ArgumentOutOfRangeException("index"); + } + } + public override int Capacity { + get { + return 64; + } + } + } + [GeneratedCode("DLR", "2.0")] + public class Tuple : Tuple { + public Tuple() { } + + public Tuple(T0 item0, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8, T9 item9, T10 item10, T11 item11, T12 item12, T13 item13, T14 item14, T15 item15, T16 item16, T17 item17, T18 item18, T19 item19, T20 item20, T21 item21, T22 item22, T23 item23, T24 item24, T25 item25, T26 item26, T27 item27, T28 item28, T29 item29, T30 item30, T31 item31, T32 item32, T33 item33, T34 item34, T35 item35, T36 item36, T37 item37, T38 item38, T39 item39, T40 item40, T41 item41, T42 item42, T43 item43, T44 item44, T45 item45, T46 item46, T47 item47, T48 item48, T49 item49, T50 item50, T51 item51, T52 item52, T53 item53, T54 item54, T55 item55, T56 item56, T57 item57, T58 item58, T59 item59, T60 item60, T61 item61, T62 item62, T63 item63, T64 item64, T65 item65, T66 item66, T67 item67, T68 item68, T69 item69, T70 item70, T71 item71, T72 item72, T73 item73, T74 item74, T75 item75, T76 item76, T77 item77, T78 item78, T79 item79, T80 item80, T81 item81, T82 item82, T83 item83, T84 item84, T85 item85, T86 item86, T87 item87, T88 item88, T89 item89, T90 item90, T91 item91, T92 item92, T93 item93, T94 item94, T95 item95, T96 item96, T97 item97, T98 item98, T99 item99, T100 item100, T101 item101, T102 item102, T103 item103, T104 item104, T105 item105, T106 item106, T107 item107, T108 item108, T109 item109, T110 item110, T111 item111, T112 item112, T113 item113, T114 item114, T115 item115, T116 item116, T117 item117, T118 item118, T119 item119, T120 item120, T121 item121, T122 item122, T123 item123, T124 item124, T125 item125, T126 item126, T127 item127) + : base(item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18, item19, item20, item21, item22, item23, item24, item25, item26, item27, item28, item29, item30, item31, item32, item33, item34, item35, item36, item37, item38, item39, item40, item41, item42, item43, item44, item45, item46, item47, item48, item49, item50, item51, item52, item53, item54, item55, item56, item57, item58, item59, item60, item61, item62, item63) { + _item64 = item64; + _item65 = item65; + _item66 = item66; + _item67 = item67; + _item68 = item68; + _item69 = item69; + _item70 = item70; + _item71 = item71; + _item72 = item72; + _item73 = item73; + _item74 = item74; + _item75 = item75; + _item76 = item76; + _item77 = item77; + _item78 = item78; + _item79 = item79; + _item80 = item80; + _item81 = item81; + _item82 = item82; + _item83 = item83; + _item84 = item84; + _item85 = item85; + _item86 = item86; + _item87 = item87; + _item88 = item88; + _item89 = item89; + _item90 = item90; + _item91 = item91; + _item92 = item92; + _item93 = item93; + _item94 = item94; + _item95 = item95; + _item96 = item96; + _item97 = item97; + _item98 = item98; + _item99 = item99; + _item100 = item100; + _item101 = item101; + _item102 = item102; + _item103 = item103; + _item104 = item104; + _item105 = item105; + _item106 = item106; + _item107 = item107; + _item108 = item108; + _item109 = item109; + _item110 = item110; + _item111 = item111; + _item112 = item112; + _item113 = item113; + _item114 = item114; + _item115 = item115; + _item116 = item116; + _item117 = item117; + _item118 = item118; + _item119 = item119; + _item120 = item120; + _item121 = item121; + _item122 = item122; + _item123 = item123; + _item124 = item124; + _item125 = item125; + _item126 = item126; + _item127 = item127; + } + + private T64 _item64; + private T65 _item65; + private T66 _item66; + private T67 _item67; + private T68 _item68; + private T69 _item69; + private T70 _item70; + private T71 _item71; + private T72 _item72; + private T73 _item73; + private T74 _item74; + private T75 _item75; + private T76 _item76; + private T77 _item77; + private T78 _item78; + private T79 _item79; + private T80 _item80; + private T81 _item81; + private T82 _item82; + private T83 _item83; + private T84 _item84; + private T85 _item85; + private T86 _item86; + private T87 _item87; + private T88 _item88; + private T89 _item89; + private T90 _item90; + private T91 _item91; + private T92 _item92; + private T93 _item93; + private T94 _item94; + private T95 _item95; + private T96 _item96; + private T97 _item97; + private T98 _item98; + private T99 _item99; + private T100 _item100; + private T101 _item101; + private T102 _item102; + private T103 _item103; + private T104 _item104; + private T105 _item105; + private T106 _item106; + private T107 _item107; + private T108 _item108; + private T109 _item109; + private T110 _item110; + private T111 _item111; + private T112 _item112; + private T113 _item113; + private T114 _item114; + private T115 _item115; + private T116 _item116; + private T117 _item117; + private T118 _item118; + private T119 _item119; + private T120 _item120; + private T121 _item121; + private T122 _item122; + private T123 _item123; + private T124 _item124; + private T125 _item125; + private T126 _item126; + private T127 _item127; + + public T64 Item064 { + get { return _item64; } + set { _item64 = value; } + } + public T65 Item065 { + get { return _item65; } + set { _item65 = value; } + } + public T66 Item066 { + get { return _item66; } + set { _item66 = value; } + } + public T67 Item067 { + get { return _item67; } + set { _item67 = value; } + } + public T68 Item068 { + get { return _item68; } + set { _item68 = value; } + } + public T69 Item069 { + get { return _item69; } + set { _item69 = value; } + } + public T70 Item070 { + get { return _item70; } + set { _item70 = value; } + } + public T71 Item071 { + get { return _item71; } + set { _item71 = value; } + } + public T72 Item072 { + get { return _item72; } + set { _item72 = value; } + } + public T73 Item073 { + get { return _item73; } + set { _item73 = value; } + } + public T74 Item074 { + get { return _item74; } + set { _item74 = value; } + } + public T75 Item075 { + get { return _item75; } + set { _item75 = value; } + } + public T76 Item076 { + get { return _item76; } + set { _item76 = value; } + } + public T77 Item077 { + get { return _item77; } + set { _item77 = value; } + } + public T78 Item078 { + get { return _item78; } + set { _item78 = value; } + } + public T79 Item079 { + get { return _item79; } + set { _item79 = value; } + } + public T80 Item080 { + get { return _item80; } + set { _item80 = value; } + } + public T81 Item081 { + get { return _item81; } + set { _item81 = value; } + } + public T82 Item082 { + get { return _item82; } + set { _item82 = value; } + } + public T83 Item083 { + get { return _item83; } + set { _item83 = value; } + } + public T84 Item084 { + get { return _item84; } + set { _item84 = value; } + } + public T85 Item085 { + get { return _item85; } + set { _item85 = value; } + } + public T86 Item086 { + get { return _item86; } + set { _item86 = value; } + } + public T87 Item087 { + get { return _item87; } + set { _item87 = value; } + } + public T88 Item088 { + get { return _item88; } + set { _item88 = value; } + } + public T89 Item089 { + get { return _item89; } + set { _item89 = value; } + } + public T90 Item090 { + get { return _item90; } + set { _item90 = value; } + } + public T91 Item091 { + get { return _item91; } + set { _item91 = value; } + } + public T92 Item092 { + get { return _item92; } + set { _item92 = value; } + } + public T93 Item093 { + get { return _item93; } + set { _item93 = value; } + } + public T94 Item094 { + get { return _item94; } + set { _item94 = value; } + } + public T95 Item095 { + get { return _item95; } + set { _item95 = value; } + } + public T96 Item096 { + get { return _item96; } + set { _item96 = value; } + } + public T97 Item097 { + get { return _item97; } + set { _item97 = value; } + } + public T98 Item098 { + get { return _item98; } + set { _item98 = value; } + } + public T99 Item099 { + get { return _item99; } + set { _item99 = value; } + } + public T100 Item100 { + get { return _item100; } + set { _item100 = value; } + } + public T101 Item101 { + get { return _item101; } + set { _item101 = value; } + } + public T102 Item102 { + get { return _item102; } + set { _item102 = value; } + } + public T103 Item103 { + get { return _item103; } + set { _item103 = value; } + } + public T104 Item104 { + get { return _item104; } + set { _item104 = value; } + } + public T105 Item105 { + get { return _item105; } + set { _item105 = value; } + } + public T106 Item106 { + get { return _item106; } + set { _item106 = value; } + } + public T107 Item107 { + get { return _item107; } + set { _item107 = value; } + } + public T108 Item108 { + get { return _item108; } + set { _item108 = value; } + } + public T109 Item109 { + get { return _item109; } + set { _item109 = value; } + } + public T110 Item110 { + get { return _item110; } + set { _item110 = value; } + } + public T111 Item111 { + get { return _item111; } + set { _item111 = value; } + } + public T112 Item112 { + get { return _item112; } + set { _item112 = value; } + } + public T113 Item113 { + get { return _item113; } + set { _item113 = value; } + } + public T114 Item114 { + get { return _item114; } + set { _item114 = value; } + } + public T115 Item115 { + get { return _item115; } + set { _item115 = value; } + } + public T116 Item116 { + get { return _item116; } + set { _item116 = value; } + } + public T117 Item117 { + get { return _item117; } + set { _item117 = value; } + } + public T118 Item118 { + get { return _item118; } + set { _item118 = value; } + } + public T119 Item119 { + get { return _item119; } + set { _item119 = value; } + } + public T120 Item120 { + get { return _item120; } + set { _item120 = value; } + } + public T121 Item121 { + get { return _item121; } + set { _item121 = value; } + } + public T122 Item122 { + get { return _item122; } + set { _item122 = value; } + } + public T123 Item123 { + get { return _item123; } + set { _item123 = value; } + } + public T124 Item124 { + get { return _item124; } + set { _item124 = value; } + } + public T125 Item125 { + get { return _item125; } + set { _item125 = value; } + } + public T126 Item126 { + get { return _item126; } + set { _item126 = value; } + } + public T127 Item127 { + get { return _item127; } + set { _item127 = value; } + } + + public override object GetValue(int index) { + switch(index) { + case 0: return Item000; + case 1: return Item001; + case 2: return Item002; + case 3: return Item003; + case 4: return Item004; + case 5: return Item005; + case 6: return Item006; + case 7: return Item007; + case 8: return Item008; + case 9: return Item009; + case 10: return Item010; + case 11: return Item011; + case 12: return Item012; + case 13: return Item013; + case 14: return Item014; + case 15: return Item015; + case 16: return Item016; + case 17: return Item017; + case 18: return Item018; + case 19: return Item019; + case 20: return Item020; + case 21: return Item021; + case 22: return Item022; + case 23: return Item023; + case 24: return Item024; + case 25: return Item025; + case 26: return Item026; + case 27: return Item027; + case 28: return Item028; + case 29: return Item029; + case 30: return Item030; + case 31: return Item031; + case 32: return Item032; + case 33: return Item033; + case 34: return Item034; + case 35: return Item035; + case 36: return Item036; + case 37: return Item037; + case 38: return Item038; + case 39: return Item039; + case 40: return Item040; + case 41: return Item041; + case 42: return Item042; + case 43: return Item043; + case 44: return Item044; + case 45: return Item045; + case 46: return Item046; + case 47: return Item047; + case 48: return Item048; + case 49: return Item049; + case 50: return Item050; + case 51: return Item051; + case 52: return Item052; + case 53: return Item053; + case 54: return Item054; + case 55: return Item055; + case 56: return Item056; + case 57: return Item057; + case 58: return Item058; + case 59: return Item059; + case 60: return Item060; + case 61: return Item061; + case 62: return Item062; + case 63: return Item063; + case 64: return Item064; + case 65: return Item065; + case 66: return Item066; + case 67: return Item067; + case 68: return Item068; + case 69: return Item069; + case 70: return Item070; + case 71: return Item071; + case 72: return Item072; + case 73: return Item073; + case 74: return Item074; + case 75: return Item075; + case 76: return Item076; + case 77: return Item077; + case 78: return Item078; + case 79: return Item079; + case 80: return Item080; + case 81: return Item081; + case 82: return Item082; + case 83: return Item083; + case 84: return Item084; + case 85: return Item085; + case 86: return Item086; + case 87: return Item087; + case 88: return Item088; + case 89: return Item089; + case 90: return Item090; + case 91: return Item091; + case 92: return Item092; + case 93: return Item093; + case 94: return Item094; + case 95: return Item095; + case 96: return Item096; + case 97: return Item097; + case 98: return Item098; + case 99: return Item099; + case 100: return Item100; + case 101: return Item101; + case 102: return Item102; + case 103: return Item103; + case 104: return Item104; + case 105: return Item105; + case 106: return Item106; + case 107: return Item107; + case 108: return Item108; + case 109: return Item109; + case 110: return Item110; + case 111: return Item111; + case 112: return Item112; + case 113: return Item113; + case 114: return Item114; + case 115: return Item115; + case 116: return Item116; + case 117: return Item117; + case 118: return Item118; + case 119: return Item119; + case 120: return Item120; + case 121: return Item121; + case 122: return Item122; + case 123: return Item123; + case 124: return Item124; + case 125: return Item125; + case 126: return Item126; + case 127: return Item127; + default: throw new ArgumentOutOfRangeException("index"); + } + } + + public override void SetValue(int index, object value) { + switch(index) { + case 0: Item000 = (T0)value; break; + case 1: Item001 = (T1)value; break; + case 2: Item002 = (T2)value; break; + case 3: Item003 = (T3)value; break; + case 4: Item004 = (T4)value; break; + case 5: Item005 = (T5)value; break; + case 6: Item006 = (T6)value; break; + case 7: Item007 = (T7)value; break; + case 8: Item008 = (T8)value; break; + case 9: Item009 = (T9)value; break; + case 10: Item010 = (T10)value; break; + case 11: Item011 = (T11)value; break; + case 12: Item012 = (T12)value; break; + case 13: Item013 = (T13)value; break; + case 14: Item014 = (T14)value; break; + case 15: Item015 = (T15)value; break; + case 16: Item016 = (T16)value; break; + case 17: Item017 = (T17)value; break; + case 18: Item018 = (T18)value; break; + case 19: Item019 = (T19)value; break; + case 20: Item020 = (T20)value; break; + case 21: Item021 = (T21)value; break; + case 22: Item022 = (T22)value; break; + case 23: Item023 = (T23)value; break; + case 24: Item024 = (T24)value; break; + case 25: Item025 = (T25)value; break; + case 26: Item026 = (T26)value; break; + case 27: Item027 = (T27)value; break; + case 28: Item028 = (T28)value; break; + case 29: Item029 = (T29)value; break; + case 30: Item030 = (T30)value; break; + case 31: Item031 = (T31)value; break; + case 32: Item032 = (T32)value; break; + case 33: Item033 = (T33)value; break; + case 34: Item034 = (T34)value; break; + case 35: Item035 = (T35)value; break; + case 36: Item036 = (T36)value; break; + case 37: Item037 = (T37)value; break; + case 38: Item038 = (T38)value; break; + case 39: Item039 = (T39)value; break; + case 40: Item040 = (T40)value; break; + case 41: Item041 = (T41)value; break; + case 42: Item042 = (T42)value; break; + case 43: Item043 = (T43)value; break; + case 44: Item044 = (T44)value; break; + case 45: Item045 = (T45)value; break; + case 46: Item046 = (T46)value; break; + case 47: Item047 = (T47)value; break; + case 48: Item048 = (T48)value; break; + case 49: Item049 = (T49)value; break; + case 50: Item050 = (T50)value; break; + case 51: Item051 = (T51)value; break; + case 52: Item052 = (T52)value; break; + case 53: Item053 = (T53)value; break; + case 54: Item054 = (T54)value; break; + case 55: Item055 = (T55)value; break; + case 56: Item056 = (T56)value; break; + case 57: Item057 = (T57)value; break; + case 58: Item058 = (T58)value; break; + case 59: Item059 = (T59)value; break; + case 60: Item060 = (T60)value; break; + case 61: Item061 = (T61)value; break; + case 62: Item062 = (T62)value; break; + case 63: Item063 = (T63)value; break; + case 64: Item064 = (T64)value; break; + case 65: Item065 = (T65)value; break; + case 66: Item066 = (T66)value; break; + case 67: Item067 = (T67)value; break; + case 68: Item068 = (T68)value; break; + case 69: Item069 = (T69)value; break; + case 70: Item070 = (T70)value; break; + case 71: Item071 = (T71)value; break; + case 72: Item072 = (T72)value; break; + case 73: Item073 = (T73)value; break; + case 74: Item074 = (T74)value; break; + case 75: Item075 = (T75)value; break; + case 76: Item076 = (T76)value; break; + case 77: Item077 = (T77)value; break; + case 78: Item078 = (T78)value; break; + case 79: Item079 = (T79)value; break; + case 80: Item080 = (T80)value; break; + case 81: Item081 = (T81)value; break; + case 82: Item082 = (T82)value; break; + case 83: Item083 = (T83)value; break; + case 84: Item084 = (T84)value; break; + case 85: Item085 = (T85)value; break; + case 86: Item086 = (T86)value; break; + case 87: Item087 = (T87)value; break; + case 88: Item088 = (T88)value; break; + case 89: Item089 = (T89)value; break; + case 90: Item090 = (T90)value; break; + case 91: Item091 = (T91)value; break; + case 92: Item092 = (T92)value; break; + case 93: Item093 = (T93)value; break; + case 94: Item094 = (T94)value; break; + case 95: Item095 = (T95)value; break; + case 96: Item096 = (T96)value; break; + case 97: Item097 = (T97)value; break; + case 98: Item098 = (T98)value; break; + case 99: Item099 = (T99)value; break; + case 100: Item100 = (T100)value; break; + case 101: Item101 = (T101)value; break; + case 102: Item102 = (T102)value; break; + case 103: Item103 = (T103)value; break; + case 104: Item104 = (T104)value; break; + case 105: Item105 = (T105)value; break; + case 106: Item106 = (T106)value; break; + case 107: Item107 = (T107)value; break; + case 108: Item108 = (T108)value; break; + case 109: Item109 = (T109)value; break; + case 110: Item110 = (T110)value; break; + case 111: Item111 = (T111)value; break; + case 112: Item112 = (T112)value; break; + case 113: Item113 = (T113)value; break; + case 114: Item114 = (T114)value; break; + case 115: Item115 = (T115)value; break; + case 116: Item116 = (T116)value; break; + case 117: Item117 = (T117)value; break; + case 118: Item118 = (T118)value; break; + case 119: Item119 = (T119)value; break; + case 120: Item120 = (T120)value; break; + case 121: Item121 = (T121)value; break; + case 122: Item122 = (T122)value; break; + case 123: Item123 = (T123)value; break; + case 124: Item124 = (T124)value; break; + case 125: Item125 = (T125)value; break; + case 126: Item126 = (T126)value; break; + case 127: Item127 = (T127)value; break; + default: throw new ArgumentOutOfRangeException("index"); + } + } + public override int Capacity { + get { + return 128; + } + } + } + + // *** END GENERATED CODE *** + + #endregion +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ArrayUtils.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ArrayUtils.cs new file mode 100644 index 0000000000..d3bae8f4c9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ArrayUtils.cs @@ -0,0 +1,313 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace Microsoft.Scripting.Utils { + public static class ArrayUtils { + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")] + public static readonly string[] EmptyStrings = new string[0]; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")] + public static readonly object[] EmptyObjects = new object[0]; + + public static TOutput[] ConvertAll(TInput[] input, Converter conv) { +#if SILVERLIGHT + ContractUtils.RequiresNotNull(input, "input"); + ContractUtils.RequiresNotNull(conv, "conv"); + + TOutput[] res = new TOutput[input.Length]; + for (int i = 0; i < input.Length; i++) { + res[i] = conv(input[i]); + } + return res; +#else + return System.Array.ConvertAll(input, conv); +#endif + } + + public static T[] FindAll(T[] array, Predicate match) { +#if SILVERLIGHT + if (array == null) { + throw new ArgumentNullException("array"); + } + + if (match == null) { + throw new ArgumentNullException("match"); + } + + List list = new List(); + for (int i = 0; i < array.Length; i++) { + if (match(array[i])) { + list.Add(array[i]); + } + } + return list.ToArray(); +#else + return System.Array.FindAll(array, match); +#endif + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", MessageId = "1#")] // TODO: fix + public static void PrintTable(StringBuilder output, string[,] table) { + ContractUtils.RequiresNotNull(output, "output"); + ContractUtils.RequiresNotNull(table, "table"); + + int max_width = 0; + for (int i = 0; i < table.GetLength(0); i++) { + if (table[i, 0].Length > max_width) { + max_width = table[i, 0].Length; + } + } + + for (int i = 0; i < table.GetLength(0); i++) { + output.Append(" "); + output.Append(table[i, 0]); + + for (int j = table[i, 0].Length; j < max_width + 1; j++) { + output.Append(' '); + } + + output.AppendLine(table[i, 1]); + } + } + + public static T[] Copy(T[] array) { + return (array.Length > 0) ? (T[])array.Clone() : array; + } + + public static T[] MakeArray(ICollection list) { + if (list.Count == 0) { + return new T[0]; + } + + T[] res = new T[list.Count]; + list.CopyTo(res, 0); + return res; + } + + public static T[] MakeArray(ICollection elements, int reservedSlotsBefore, int reservedSlotsAfter) { + if (reservedSlotsAfter < 0) throw new ArgumentOutOfRangeException("reservedSlotsAfter"); + if (reservedSlotsBefore < 0) throw new ArgumentOutOfRangeException("reservedSlotsBefore"); + + if (elements == null) { + return new T[reservedSlotsBefore + reservedSlotsAfter]; + } + + T[] result = new T[reservedSlotsBefore + elements.Count + reservedSlotsAfter]; + elements.CopyTo(result, reservedSlotsBefore); + return result; + } + + public static T[] RotateRight(T[] array, int count) { + ContractUtils.RequiresNotNull(array, "array"); + if ((count < 0) || (count > array.Length)) throw new ArgumentOutOfRangeException("count"); + + T[] result = new T[array.Length]; + // The head of the array is shifted, and the tail will be rotated to the head of the resulting array + int sizeOfShiftedArray = array.Length - count; + Array.Copy(array, 0, result, count, sizeOfShiftedArray); + Array.Copy(array, sizeOfShiftedArray, result, 0, count); + return result; + } + + public static T[] ShiftRight(T[] array, int count) { + ContractUtils.RequiresNotNull(array, "array"); + if (count < 0) throw new ArgumentOutOfRangeException("count"); + + T[] result = new T[array.Length + count]; + System.Array.Copy(array, 0, result, count, array.Length); + return result; + } + + public static T[] ShiftLeft(T[] array, int count) { + ContractUtils.RequiresNotNull(array, "array"); + if (count < 0) throw new ArgumentOutOfRangeException("count"); + + T[] result = new T[array.Length - count]; + System.Array.Copy(array, count, result, 0, result.Length); + return result; + } + + public static T[] Insert(T item, IList list) { + T[] res = new T[list.Count + 1]; + res[0] = item; + list.CopyTo(res, 1); + return res; + } + + public static T[] Insert(T item1, T item2, IList list) { + T[] res = new T[list.Count + 2]; + res[0] = item1; + res[1] = item2; + list.CopyTo(res, 2); + return res; + } + + public static T[] Insert(T item, T[] array) { + T[] result = ShiftRight(array, 1); + result[0] = item; + return result; + } + + public static T[] Insert(T item1, T item2, T[] array) { + T[] result = ShiftRight(array, 2); + result[0] = item1; + result[1] = item2; + return result; + } + + public static T[] Append(T[] array, T item) { + ContractUtils.RequiresNotNull(array, "array"); + + System.Array.Resize(ref array, array.Length + 1); + array[array.Length - 1] = item; + return array; + } + + public static T[] AppendRange(T[] array, IList items) { + return AppendRange(array, items, 0); + } + + public static T[] AppendRange(T[] array, IList items, int additionalItemCount) { + ContractUtils.RequiresNotNull(array, "array"); + if (additionalItemCount < 0) throw new ArgumentOutOfRangeException("additionalItemCount"); + + int j = array.Length; + + System.Array.Resize(ref array, array.Length + items.Count + additionalItemCount); + + for (int i = 0; i < items.Count; i++, j++) { + array[j] = items[i]; + } + + return array; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional")] // TODO: fix + public static T[,] Concatenate(T[,] array1, T[,] array2) { + int columnsCount = array1.GetLength(1); + Debug.Assert(array2.GetLength(1) == columnsCount); + + int row1Count = array1.GetLength(0); + int row2Count = array2.GetLength(0); + int totalRowsCount = row1Count + row2Count; + T[,] result = new T[totalRowsCount, columnsCount]; + + for (int i = 0; i < row1Count; i++) { + for (int j = 0; j < columnsCount; j++) { + result[i, j] = array1[i, j]; + } + } + + for (int i = 0; i < row2Count; i++) { + for (int j = 0; j < columnsCount; j++) { + result[(i + row1Count), j] = array2[i, j]; + } + } + + return result; + } + + public static void SwapLastTwo(T[] array) { + Debug.Assert(array != null && array.Length >= 2); + + T temp = array[array.Length - 1]; + array[array.Length - 1] = array[array.Length - 2]; + array[array.Length - 2] = temp; + } + + public static T[] RemoveFirst(IList list) { + return ShiftLeft(MakeArray(list), 1); + } + + public static T[] RemoveFirst(T[] array) { + return ShiftLeft(array, 1); + } + + public static T[] RemoveLast(T[] array) { + ContractUtils.RequiresNotNull(array, "array"); + + System.Array.Resize(ref array, array.Length - 1); + return array; + } + + public static T[] RemoveAt(IList list, int indexToRemove) { + return RemoveAt(MakeArray(list), indexToRemove); + } + + public static T[] RemoveAt(T[] array, int indexToRemove) { + ContractUtils.RequiresNotNull(array, "array"); + ContractUtils.Requires(indexToRemove >= 0 && indexToRemove < array.Length, "index"); + + T[] result = new T[array.Length - 1]; + if (indexToRemove > 0) { + Array.Copy(array, 0, result, 0, indexToRemove); + } + int remaining = array.Length - indexToRemove - 1; + if (remaining > 0) { + Array.Copy(array, array.Length - remaining, result, result.Length - remaining, remaining); + } + return result; + } + + public static T[] InsertAt(IList list, int index, params T[] items) { + return InsertAt(MakeArray(list), index, items); + } + + public static T[] InsertAt(T[] array, int index, params T[] items) { + ContractUtils.RequiresNotNull(array, "array"); + ContractUtils.RequiresNotNull(items, "items"); + ContractUtils.Requires(index >= 0 && index <= array.Length, "index"); + + if (items.Length == 0) { + return Copy(array); + } + + T[] result = new T[array.Length + items.Length]; + if (index > 0) { + Array.Copy(array, 0, result, 0, index); + } + Array.Copy(items, 0, result, index, items.Length); + + int remaining = array.Length - index; + if (remaining > 0) { + Array.Copy(array, array.Length - remaining, result, result.Length - remaining, remaining); + } + return result; + } + + /// + /// Converts a generic ICollection of T into an array of T. + /// + /// If the collection is already an array of T the original collection is returned. + /// + public static T[] ToArray(ICollection list) { + T[] res = list as T[]; + if (res == null) { + res = new T[list.Count]; + int i = 0; + foreach (T obj in list) { + res[i++] = obj; + } + } + return res; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/AssemblyQualifiedTypeName.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/AssemblyQualifiedTypeName.cs new file mode 100644 index 0000000000..6d18cdfec9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/AssemblyQualifiedTypeName.cs @@ -0,0 +1,98 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; + +namespace Microsoft.Scripting.Utils { + [Serializable] + internal struct AssemblyQualifiedTypeName : IEquatable { + public readonly string TypeName; + public readonly AssemblyName AssemblyName; + + public AssemblyQualifiedTypeName(string typeName, AssemblyName assemblyName) { + ContractUtils.RequiresNotNull(typeName, "typeName"); + ContractUtils.RequiresNotNull(assemblyName, "assemblyName"); + + TypeName = typeName; + AssemblyName = assemblyName; + } + + public AssemblyQualifiedTypeName(Type type) { + TypeName = type.FullName; + AssemblyName = type.Assembly.GetName(); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public AssemblyQualifiedTypeName(string assemblyQualifiedTypeName) { + ContractUtils.RequiresNotNull(assemblyQualifiedTypeName, "assemblyQualifiedTypeName"); + + int firstColon = assemblyQualifiedTypeName.IndexOf(","); + if (firstColon != -1) { + TypeName = assemblyQualifiedTypeName.Substring(0, firstColon).Trim(); + var assemblyNameStr = assemblyQualifiedTypeName.Substring(firstColon + 1).Trim(); + if (TypeName.Length > 0 && assemblyNameStr.Length > 0) { + try { + AssemblyName = new AssemblyName(assemblyNameStr); + return; + } catch (Exception e) { + throw new ArgumentException(String.Format("Invalid assembly qualified name '{0}': {1}", assemblyQualifiedTypeName, e.Message), e); + } + } + } + + throw new ArgumentException(String.Format("Invalid assembly qualified name '{0}'", assemblyQualifiedTypeName)); + } + + internal static AssemblyQualifiedTypeName ParseArgument(string str, string argumentName) { + Assert.NotEmpty(argumentName); + try { + return new AssemblyQualifiedTypeName(str); + } catch (ArgumentException e) { +#if SILVERLIGHT + throw new ArgumentException(e.Message, argumentName); +#else + throw new ArgumentException(e.Message, argumentName, e.InnerException); +#endif + } + } + + public bool Equals(AssemblyQualifiedTypeName other) { + return TypeName == other.TypeName && AssemblyName.FullName == other.AssemblyName.FullName; + } + + public override bool Equals(object obj) { + return obj is AssemblyQualifiedTypeName && Equals((AssemblyQualifiedTypeName)obj); + } + + public override int GetHashCode() { + return TypeName.GetHashCode() ^ AssemblyName.FullName.GetHashCode(); + } + + public override string ToString() { + return TypeName + ", " + AssemblyName.FullName; + } + + public static bool operator ==(AssemblyQualifiedTypeName name, AssemblyQualifiedTypeName other) { + return name.Equals(other); + } + + public static bool operator !=(AssemblyQualifiedTypeName name, AssemblyQualifiedTypeName other) { + return !name.Equals(other); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/Assert.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/Assert.cs new file mode 100644 index 0000000000..50d4b38301 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/Assert.cs @@ -0,0 +1,78 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#define DEBUG + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.Scripting.Utils { + + public static class Assert { + + public static Exception Unreachable { + get { + Debug.Assert(false, "Unreachable"); + return new InvalidOperationException("Code supposed to be unreachable"); + } + } + + [Conditional("DEBUG")] + public static void NotNull(object var) { + Debug.Assert(var != null); + } + + [Conditional("DEBUG")] + public static void NotNull(object var1, object var2) { + Debug.Assert(var1 != null && var2 != null); + } + + [Conditional("DEBUG")] + public static void NotNull(object var1, object var2, object var3) { + Debug.Assert(var1 != null && var2 != null && var3 != null); + } + + [Conditional("DEBUG")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1025:ReplaceRepetitiveArgumentsWithParamsArray")] + public static void NotNull(object var1, object var2, object var3, object var4) { + Debug.Assert(var1 != null && var2 != null && var3 != null && var4 != null); + } + + [Conditional("DEBUG")] + public static void NotEmpty(string str) { + Debug.Assert(!String.IsNullOrEmpty(str)); + } + + [Conditional("DEBUG")] + public static void NotEmpty(ICollection array) { + Debug.Assert(array != null && array.Count > 0); + } + + [Conditional("DEBUG")] + public static void NotNullItems(IEnumerable items) where T : class { + Debug.Assert(items != null); + foreach (object item in items) { + Debug.Assert(item != null); + } + } + + [Conditional("DEBUG")] + public static void IsTrue(Func predicate) { + ContractUtils.RequiresNotNull(predicate, "predicate"); + Debug.Assert(predicate()); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/CheckedDictionaryEnumerator.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/CheckedDictionaryEnumerator.cs new file mode 100644 index 0000000000..d1660de678 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/CheckedDictionaryEnumerator.cs @@ -0,0 +1,115 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Utils { + /// + /// Not all .NET enumerators throw exceptions if accessed in an invalid state. This type + /// can be used to throw exceptions from enumerators implemented in IronPython. + /// + public abstract class CheckedDictionaryEnumerator : IDictionaryEnumerator, IEnumerator> { + private EnumeratorState _enumeratorState = EnumeratorState.NotStarted; + + private void CheckEnumeratorState() { + if (_enumeratorState == EnumeratorState.NotStarted) + throw Error.EnumerationNotStarted(); + else if (_enumeratorState == EnumeratorState.Ended) + throw Error.EnumerationFinished(); + } + + #region IDictionaryEnumerator Members + public DictionaryEntry Entry { + get { + CheckEnumeratorState(); + return new DictionaryEntry(Key, Value); + } + } + + public object Key { + get { + CheckEnumeratorState(); + return GetKey(); + } + } + + public object Value { + get { + CheckEnumeratorState(); + return GetValue(); + } + } + #endregion + + #region IEnumerator Members + public bool MoveNext() { + if (_enumeratorState == EnumeratorState.Ended) + throw Error.EnumerationFinished(); + + bool result = DoMoveNext(); + if (result) + _enumeratorState = EnumeratorState.Started; + else + _enumeratorState = EnumeratorState.Ended; + return result; + } + + public object Current { get { return Entry; } } + + public void Reset() { + DoReset(); + _enumeratorState = EnumeratorState.NotStarted; + } + #endregion + + #region IEnumerator> Members + + KeyValuePair IEnumerator>.Current { + get { return new KeyValuePair(Key, Value); } + } + + #endregion + + #region IDisposable Members + + public void Dispose() { + GC.SuppressFinalize(this); + } + + #endregion + + #region Methods that a sub-type needs to implement + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] // TODO: fix + protected abstract object GetKey(); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] // TODO: fix + protected abstract object GetValue(); + + protected abstract bool DoMoveNext(); + protected abstract void DoReset(); + + #endregion + + private enum EnumeratorState { + NotStarted, + Started, + Ended + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/CollectionExtensions.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/CollectionExtensions.cs new file mode 100644 index 0000000000..739f69ae11 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/CollectionExtensions.cs @@ -0,0 +1,223 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; + +namespace Microsoft.Scripting.Utils { + internal static class CollectionExtensions { + /// + /// Wraps the provided enumerable into a ReadOnlyCollection{T} + /// + /// Copies all of the data into a new array, so the data can't be + /// changed after creation. The exception is if the enumerable is + /// already a ReadOnlyCollection{T}, in which case we just return it. + /// + internal static ReadOnlyCollection ToReadOnly(this IEnumerable enumerable) { + if (enumerable == null) { + return EmptyReadOnlyCollection.Instance; + } + + var roCollection = enumerable as ReadOnlyCollection; + if (roCollection != null) { + return roCollection; + } + + var collection = enumerable as ICollection; + if (collection != null) { + int count = collection.Count; + if (count == 0) { + return EmptyReadOnlyCollection.Instance; + } + + T[] array = new T[count]; + collection.CopyTo(array, 0); + return new ReadOnlyCollection(array); + } + + // ToArray trims the excess space and speeds up access + return new ReadOnlyCollection(new List(enumerable).ToArray()); + } + + // We could probably improve the hashing here + internal static int ListHashCode(this IEnumerable list) { + var cmp = EqualityComparer.Default; + int h = 6551; + foreach (T t in list) { + h ^= (h << 5) ^ cmp.GetHashCode(t); + } + return h; + } + + internal static bool ListEquals(this ICollection first, ICollection second) { + if (first.Count != second.Count) { + return false; + } + var cmp = EqualityComparer.Default; + var f = first.GetEnumerator(); + var s = second.GetEnumerator(); + while (f.MoveNext()) { + s.MoveNext(); + + if (!cmp.Equals(f.Current, s.Current)) { + return false; + } + } + return true; + } + + internal static IEnumerable Select(this IEnumerable enumerable, Func select) { + foreach (T t in enumerable) { + yield return select(t); + } + } + + // Name needs to be different so it doesn't conflict with Enumerable.Select + internal static U[] Map(this ICollection collection, Func select) { + int count = collection.Count; + U[] result = new U[count]; + count = 0; + foreach (T t in collection) { + result[count++] = select(t); + } + return result; + } + + internal static IEnumerable Where(this IEnumerable enumerable, Func where) { + foreach (T t in enumerable) { + if (where(t)) { + yield return t; + } + } + } + + internal static List ToList(this IEnumerable enumerable) { + return new List(enumerable); + } + + internal static T[] ToArray(this IEnumerable enumerable) { + var c = enumerable as ICollection; + if (c != null) { + var result = new T[c.Count]; + c.CopyTo(result, 0); + return result; + } + return new List(enumerable).ToArray(); + } + + internal static bool Any(this IEnumerable source) { + using (IEnumerator e = source.GetEnumerator()) { + return e.MoveNext(); + } + } + + internal static bool Any(this IEnumerable source, Func predicate) { + foreach (T element in source) { + if (predicate(element)) { + return true; + } + } + return false; + } + + internal static bool All(this IEnumerable source, Func predicate) { + foreach (T element in source) { + if (!predicate(element)) { + return false; + } + } + return true; + } + + internal static TSource Aggregate(this IEnumerable source, Func func) { + using (IEnumerator e = source.GetEnumerator()) { + if (!e.MoveNext()) throw new ArgumentException("Collection is empty", "source"); + TSource result = e.Current; + while (e.MoveNext()) result = func(result, e.Current); + return result; + } + } + + internal static TAccumulate Aggregate(this IEnumerable source, TAccumulate seed, Func func) { + TAccumulate result = seed; + foreach (TSource element in source) result = func(result, element); + return result; + } + + internal static T[] RemoveFirst(this T[] array) { + T[] result = new T[array.Length - 1]; + Array.Copy(array, 1, result, 0, result.Length); + return result; + } + + internal static T[] RemoveLast(this T[] array) { + T[] result = new T[array.Length - 1]; + Array.Copy(array, 0, result, 0, result.Length); + return result; + } + + internal static T[] AddFirst(this IList list, T item) { + T[] res = new T[list.Count + 1]; + res[0] = item; + list.CopyTo(res, 1); + return res; + } + + internal static T[] AddLast(this IList list, T item) { + T[] res = new T[list.Count + 1]; + list.CopyTo(res, 0); + res[list.Count] = item; + return res; + } + + internal static T[] RemoveAt(this T[] array, int indexToRemove) { + Debug.Assert(array != null); + Debug.Assert(indexToRemove >= 0 && indexToRemove < array.Length); + + T[] result = new T[array.Length - 1]; + if (indexToRemove > 0) { + Array.Copy(array, 0, result, 0, indexToRemove); + } + int remaining = array.Length - indexToRemove - 1; + if (remaining > 0) { + Array.Copy(array, array.Length - remaining, result, result.Length - remaining, remaining); + } + return result; + } + + internal static T[] RotateRight(this T[] array, int count) { + Debug.Assert(count >= 0 && count <= array.Length); + + T[] result = new T[array.Length]; + // The head of the array is shifted, and the tail will be rotated to the head of the resulting array + int sizeOfShiftedArray = array.Length - count; + Array.Copy(array, 0, result, count, sizeOfShiftedArray); + Array.Copy(array, sizeOfShiftedArray, result, 0, count); + return result; + } + } + + + internal static class EmptyReadOnlyCollection { + internal static ReadOnlyCollection Instance = new ReadOnlyCollection(new T[0]); + } + // TODO: Should we use this everywhere for empty arrays? + // my thought is, probably more hassle than its worth + internal static class EmptyArray { + internal static T[] Instance = new T[0]; + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/CollectionUtils.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/CollectionUtils.cs new file mode 100644 index 0000000000..6f1eb62b69 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/CollectionUtils.cs @@ -0,0 +1,149 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Utils { + public static class CollectionUtils { + + public static void AddRange(ICollection collection, IEnumerable items) { + ContractUtils.RequiresNotNull(collection, "collection"); + ContractUtils.RequiresNotNull(items, "items"); + + List list = collection as List; + if (list != null) { + list.AddRange(items); + } else { + foreach (T item in items) { + collection.Add(item); + } + } + } + + public static IEnumerable ToEnumerable(IEnumerable enumerable) { + foreach (T item in enumerable) { + yield return item; + } + } + + public static IEnumerator ToCovariant(IEnumerator enumerator) + where T : TSuper { + + ContractUtils.RequiresNotNull(enumerator, "enumerator"); + + while (enumerator.MoveNext()) { + yield return enumerator.Current; + } + } + + public static IEnumerable ToCovariant(IEnumerable enumerable) + where T : TSuper { + return new CovariantConvertor(enumerable); + } + + private class CovariantConvertor : IEnumerable where T : TSuper { + private IEnumerable _enumerable; + + public CovariantConvertor(IEnumerable enumerable) { + ContractUtils.RequiresNotNull(enumerable, "enumerable"); + _enumerable = enumerable; + } + + [Pure] + public IEnumerator GetEnumerator() { + return CollectionUtils.ToCovariant(_enumerable.GetEnumerator()); + } + + [Pure] + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + } + + public static List MakeList(T item) { + List result = new List(); + result.Add(item); + return result; + } + + public static int CountOf(IList list, T item) where T : IEquatable { + if (list == null) return 0; + + int result = 0; + for (int i = 0; i < list.Count; i++) { + if (list[i].Equals(item)) { + result++; + } + } + return result; + } + + public static bool TrueForAll(IList collection, Predicate predicate) { + ContractUtils.RequiresNotNull(collection, "collection"); + ContractUtils.RequiresNotNull(predicate, "predicate"); + + foreach (T item in collection) { + if (!predicate(item)) return false; + } + + return true; + } + + public static List GetRange(IList list, int index, int count) { + ContractUtils.RequiresNotNull(list, "list"); + ContractUtils.RequiresArrayRange(list, index, count, "index", "count"); + + List result = new List(count); + int stop = index + count; + for (int i = index; i < stop; i++) { + result.Add(list[i]); + } + return result; + } + + public static void InsertRange(IList collection, int index, IEnumerable items) { + ContractUtils.RequiresNotNull(collection, "collection"); + ContractUtils.RequiresNotNull(items, "items"); + ContractUtils.RequiresArrayInsertIndex(collection, index, "index"); + + List list = collection as List; + if (list != null) { + list.InsertRange(index, items); + } else { + int i = index; + foreach (T obj in items) { + collection.Insert(i++, obj); + } + } + } + + public static void RemoveRange(IList collection, int index, int count) { + ContractUtils.RequiresNotNull(collection, "collection"); + ContractUtils.RequiresArrayRange(collection, index, count, "index", "count"); + + List list = collection as List; + if (list != null) { + list.RemoveRange(index, count); + } else { + for (int i = index + count - 1; i >= index; i--) { + collection.RemoveAt(i); + } + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ConsoleStreamType.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ConsoleStreamType.cs new file mode 100644 index 0000000000..a98d2dd17b --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ConsoleStreamType.cs @@ -0,0 +1,22 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace Microsoft.Scripting.Utils { + public enum ConsoleStreamType { + Input, + Output, + ErrorOutput, + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ContractUtils.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ContractUtils.cs new file mode 100644 index 0000000000..46f061dc76 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ContractUtils.cs @@ -0,0 +1,166 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Microsoft.Scripting.Utils { + public static class ContractUtils { + + public static void Requires(bool precondition) { + if (!precondition) { + throw new ArgumentException(Strings.MethodPreconditionViolated); + } + } + + public static void Requires(bool precondition, string paramName) { + Assert.NotEmpty(paramName); + + if (!precondition) { + throw new ArgumentException(Strings.InvalidArgumentValue, paramName); + } + } + + public static void Requires(bool precondition, string paramName, string message) { + Assert.NotEmpty(paramName); + + if (!precondition) { + throw new ArgumentException(message, paramName); + } + } + + public static void RequiresNotNull(object value, string paramName) { + Assert.NotEmpty(paramName); + + if (value == null) { + throw new ArgumentNullException(paramName); + } + } + + public static void RequiresNotEmpty(string str, string paramName) { + RequiresNotNull(str, paramName); + if (str.Length == 0) { + throw new ArgumentException(Strings.NonEmptyStringRequired, paramName); + } + } + + public static void RequiresNotEmpty(ICollection collection, string paramName) { + RequiresNotNull(collection, paramName); + if (collection.Count == 0) { + throw new ArgumentException(Strings.NonEmptyCollectionRequired, paramName); + } + } + + /// + /// Requires the specified index to point inside the array. + /// + /// Array is null. + /// Index is outside the array. + public static void RequiresArrayIndex(IList array, int index, string indexName) { + Assert.NotEmpty(indexName); + Assert.NotNull(array); + + if (index < 0 || index >= array.Count) throw new ArgumentOutOfRangeException(indexName); + } + + /// + /// Requires the specified index to point inside the array or at the end + /// + /// Array is null. + /// Index is outside the array. + public static void RequiresArrayInsertIndex(IList array, int index, string indexName) { + Assert.NotEmpty(indexName); + Assert.NotNull(array); + + if (index < 0 || index > array.Count) throw new ArgumentOutOfRangeException(indexName); + } + + /// + /// Requires the range [offset, offset + count] to be a subset of [0, array.Count]. + /// + /// Array is null. + /// Offset or count are out of range. + public static void RequiresArrayRange(IList array, int offset, int count, string offsetName, string countName) { + Assert.NotEmpty(offsetName); + Assert.NotEmpty(countName); + Assert.NotNull(array); + + if (count < 0) throw new ArgumentOutOfRangeException(countName); + if (offset < 0 || array.Count - offset < count) throw new ArgumentOutOfRangeException(offsetName); + } + + /// + /// Requires the range [offset, offset + count] to be a subset of [0, array.Count]. + /// + /// Array is null. + /// Offset or count are out of range. + public static void RequiresListRange(IList array, int offset, int count, string offsetName, string countName) { + Assert.NotEmpty(offsetName); + Assert.NotEmpty(countName); + Assert.NotNull(array); + + if (count < 0) throw new ArgumentOutOfRangeException(countName); + if (offset < 0 || array.Count - offset < count) throw new ArgumentOutOfRangeException(offsetName); + } + + /// + /// Requires the range [offset, offset + count] to be a subset of [0, array.Count]. + /// + /// String is null. + /// Offset or count are out of range. + public static void RequiresArrayRange(string str, int offset, int count, string offsetName, string countName) { + Assert.NotEmpty(offsetName); + Assert.NotEmpty(countName); + Assert.NotNull(str); + + if (count < 0) throw new ArgumentOutOfRangeException(countName); + if (offset < 0 || str.Length - offset < count) throw new ArgumentOutOfRangeException(offsetName); + } + + /// + /// Requires the array and all its items to be non-null. + /// + public static void RequiresNotNullItems(IList array, string arrayName) { + Assert.NotNull(arrayName); + RequiresNotNull(array, arrayName); + + for (int i = 0; i < array.Count; i++) { + if (array[i] == null) { + throw ExceptionUtils.MakeArgumentItemNullException(i, arrayName); + } + } + } + + /// + /// Requires the enumerable collection and all its items to be non-null. + /// + public static void RequiresNotNullItems(IEnumerable collection, string collectionName) { + Assert.NotNull(collectionName); + RequiresNotNull(collection, collectionName); + + int i = 0; + foreach (var item in collection) { + if (item == null) { + throw ExceptionUtils.MakeArgumentItemNullException(i, collectionName); + } + i++; + } + } + + + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/CopyOnWriteList.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/CopyOnWriteList.cs new file mode 100644 index 0000000000..1639aa7206 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/CopyOnWriteList.cs @@ -0,0 +1,159 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Threading; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Utils { + /// + /// List optimized for few writes and multiple reads. It provides thread-safe read and write access. + /// Iteration is not thread-safe by default, but GetCopyForRead allows for iteration + /// without taking a lock. + /// + public class CopyOnWriteList : IList { + List _list = new List(); + + List GetNewListForWrite() { + List oldList = _list; + List newList = new List(oldList.Count + 1); + newList.AddRange(oldList); + return newList; + } + + /// + /// Gets a copy of the contents of the list. The copy will not change even if the original + /// CopyOnWriteList object is modified. This method should be used to iterate the list in + /// a thread-safe way if no lock is taken. Iterating on the original list is not guaranteed + /// to be thread-safe. + /// + /// The returned copy should not be modified by the caller. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] // TODO: fix + public List GetCopyForRead() { + // Just return the underlying list + return _list; + } + + #region IList Members + + public int IndexOf(T item) { + return _list.IndexOf(item); + } + + public void Insert(int index, T item) { + List oldList, replacedList; + do { + oldList = _list; + List newList = GetNewListForWrite(); + newList.Insert(index, item); + replacedList = Interlocked.CompareExchange(ref _list, newList, oldList); + } while (replacedList != oldList); + } + + public void RemoveAt(int index) { + List oldList, replacedList; + do { + oldList = _list; + List newList = GetNewListForWrite(); + newList.RemoveAt(index); + replacedList = Interlocked.CompareExchange(ref _list, newList, oldList); + } while (replacedList != oldList); + } + + public T this[int index] { + get { + return _list[index]; + } + + set { + List oldList, replacedList; + do { + oldList = _list; + List newList = GetNewListForWrite(); + newList[index] = value; + replacedList = Interlocked.CompareExchange(ref _list, newList, oldList); + } while (replacedList != oldList); + } + } + + #endregion + + #region ICollection Members + + public void Add(T item) { + List oldList, replacedList; + do { + oldList = _list; + List newList = GetNewListForWrite(); + newList.Add(item); + replacedList = Interlocked.CompareExchange(ref _list, newList, oldList); + } while (replacedList != oldList); + } + + public void Clear() { + _list = new List(); + } + + [Confined] + public bool Contains(T item) { + return _list.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) { + _list.CopyTo(array, arrayIndex); + } + + public int Count { + get { return _list.Count; } + } + + public bool IsReadOnly { + get { return false; } + } + + public bool Remove(T item) { + List oldList, replacedList; + bool ret; + do { + oldList = _list; + List newList = GetNewListForWrite(); + ret = newList.Remove(item); + replacedList = Interlocked.CompareExchange(ref _list, newList, oldList); + } while (replacedList != oldList); + + return ret; + } + + #endregion + + #region IEnumerable Members + + [Pure] + public IEnumerator GetEnumerator() { + return _list.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + [Pure] + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return ((System.Collections.IEnumerable)_list).GetEnumerator(); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/DictionaryUnionEnumerator.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/DictionaryUnionEnumerator.cs new file mode 100644 index 0000000000..4462a9b681 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/DictionaryUnionEnumerator.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; + +namespace Microsoft.Scripting.Utils { + /// + /// Presents a flat enumerable view of multiple dictionaries + /// + public class DictionaryUnionEnumerator : CheckedDictionaryEnumerator { + private IList _enums; + private int _current = 0; + + public DictionaryUnionEnumerator(IList enums) { + _enums = enums; + } + + protected override object GetKey() { + return _enums[_current].Key; + } + + protected override object GetValue() { + return _enums[_current].Value; + } + + protected override bool DoMoveNext() { + // Have we already walked over all the enumerators in the list? + if (_current == _enums.Count) + return false; + + // Are there any more entries in the current enumerator? + if (_enums[_current].MoveNext()) + return true; + + // Move to the next enumerator in the list + _current++; + + // Make sure that the next enumerator is ready to be used + return DoMoveNext(); + } + + protected override void DoReset() { + for (int i = 0; i < _enums.Count; i++) { + _enums[i].Reset(); + } + _current = 0; + } + + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ExceptionFactory.Generated.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ExceptionFactory.Generated.cs new file mode 100644 index 0000000000..97e1963cb8 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ExceptionFactory.Generated.cs @@ -0,0 +1,1064 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting { + + internal static partial class Strings { + private static string FormatString(string format, params object[] args) { + return string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args); + } + } + + #region Generated Microsoft.Scripting Exception Factory + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_expr_factory_scripting from: generate_exception_factory.py + + /// + /// Strongly-typed and parameterized string factory. + /// + + internal static partial class Strings { + /// + /// A string like "Cannot access member {1} declared on type {0} because the type contains generic parameters." + /// + internal static string InvalidOperation_ContainsGenericParameters(object p0, object p1) { + return FormatString("Cannot access member {1} declared on type {0} because the type contains generic parameters.", p0, p1); + } + + /// + /// A string like "Type '{0}' is missing or cannot be loaded." + /// + internal static string MissingType(object p0) { + return FormatString("Type '{0}' is missing or cannot be loaded.", p0); + } + + /// + /// A string like "static property "{0}" of "{1}" can only be read through a type, not an instance" + /// + internal static string StaticAccessFromInstanceError(object p0, object p1) { + return FormatString("static property \"{0}\" of \"{1}\" can only be read through a type, not an instance", p0, p1); + } + + /// + /// A string like "static property "{0}" of "{1}" can only be assigned to through a type, not an instance" + /// + internal static string StaticAssignmentFromInstanceError(object p0, object p1) { + return FormatString("static property \"{0}\" of \"{1}\" can only be assigned to through a type, not an instance", p0, p1); + } + + /// + /// A string like "Method precondition violated" + /// + internal static string MethodPreconditionViolated { + get { + return "Method precondition violated"; + } + } + + /// + /// A string like "Invalid argument value" + /// + internal static string InvalidArgumentValue { + get { + return "Invalid argument value"; + } + } + + /// + /// A string like "Non-empty string required" + /// + internal static string NonEmptyStringRequired { + get { + return "Non-empty string required"; + } + } + + /// + /// A string like "Non-empty collection required" + /// + internal static string NonEmptyCollectionRequired { + get { + return "Non-empty collection required"; + } + } + + /// + /// A string like "must by an Exception instance" + /// + internal static string MustBeExceptionInstance { + get { + return "must by an Exception instance"; + } + } + + /// + /// A string like "Type of test must be bool" + /// + internal static string TypeOfTestMustBeBool { + get { + return "Type of test must be bool"; + } + } + + /// + /// A string like "Type of the expression must be bool" + /// + internal static string TypeOfExpressionMustBeBool { + get { + return "Type of the expression must be bool"; + } + } + + /// + /// A string like "Empty string is not a valid path." + /// + internal static string EmptyStringIsInvalidPath { + get { + return "Empty string is not a valid path."; + } + } + + /// + /// A string like "Invalid delegate type (Invoke method not found)." + /// + internal static string InvalidDelegate { + get { + return "Invalid delegate type (Invoke method not found)."; + } + } + + /// + /// A string like "expected only static property" + /// + internal static string ExpectedStaticProperty { + get { + return "expected only static property"; + } + } + + /// + /// A string like "Property doesn't exist on the provided type" + /// + internal static string PropertyDoesNotExist { + get { + return "Property doesn't exist on the provided type"; + } + } + + /// + /// A string like "Field doesn't exist on provided type" + /// + internal static string FieldDoesNotExist { + get { + return "Field doesn't exist on provided type"; + } + } + + /// + /// A string like "Type doesn't have constructor with a given signature" + /// + internal static string TypeDoesNotHaveConstructorForTheSignature { + get { + return "Type doesn't have constructor with a given signature"; + } + } + + /// + /// A string like "Type doesn't have a method with a given name." + /// + internal static string TypeDoesNotHaveMethodForName { + get { + return "Type doesn't have a method with a given name."; + } + } + + /// + /// A string like "Type doesn't have a method with a given name and signature." + /// + internal static string TypeDoesNotHaveMethodForNameSignature { + get { + return "Type doesn't have a method with a given name and signature."; + } + } + + /// + /// A string like "Count must be non-negative." + /// + internal static string CountCannotBeNegative { + get { + return "Count must be non-negative."; + } + } + + /// + /// A string like "arrayType must be an array type" + /// + internal static string ArrayTypeMustBeArray { + get { + return "arrayType must be an array type"; + } + } + + /// + /// A string like "Either code or target must be specified." + /// + internal static string MustHaveCodeOrTarget { + get { + return "Either code or target must be specified."; + } + } + + /// + /// A string like "Type parameter is {0}. Expected a delegate." + /// + internal static string TypeParameterIsNotDelegate(object p0) { + return FormatString("Type parameter is {0}. Expected a delegate.", p0); + } + + /// + /// A string like "Cannot cast from type '{0}' to type '{1}" + /// + internal static string InvalidCast(object p0, object p1) { + return FormatString("Cannot cast from type '{0}' to type '{1}", p0, p1); + } + + /// + /// A string like "unknown member type: '{0}'. " + /// + internal static string UnknownMemberType(object p0) { + return FormatString("unknown member type: '{0}'. ", p0); + } + + /// + /// A string like "RuleBuilder can only be used with delegates whose first argument is CallSite." + /// + internal static string FirstArgumentMustBeCallSite { + get { + return "RuleBuilder can only be used with delegates whose first argument is CallSite."; + } + } + + /// + /// A string like "no instance for call." + /// + internal static string NoInstanceForCall { + get { + return "no instance for call."; + } + } + + /// + /// A string like "Missing Test." + /// + internal static string MissingTest { + get { + return "Missing Test."; + } + } + + /// + /// A string like "Missing Target." + /// + internal static string MissingTarget { + get { + return "Missing Target."; + } + } + + /// + /// A string like "The operation requires a non-generic type for {0}, but this represents generic types only" + /// + internal static string NonGenericWithGenericGroup(object p0) { + return FormatString("The operation requires a non-generic type for {0}, but this represents generic types only", p0); + } + + /// + /// A string like "Invalid operation: '{0}'" + /// + internal static string InvalidOperation(object p0) { + return FormatString("Invalid operation: '{0}'", p0); + } + + /// + /// A string like "Finally already defined." + /// + internal static string FinallyAlreadyDefined { + get { + return "Finally already defined."; + } + } + + /// + /// A string like "Can not have fault and finally." + /// + internal static string CannotHaveFaultAndFinally { + get { + return "Can not have fault and finally."; + } + } + + /// + /// A string like "Fault already defined." + /// + internal static string FaultAlreadyDefined { + get { + return "Fault already defined."; + } + } + + /// + /// A string like "Cannot create default value for type {0}." + /// + internal static string CantCreateDefaultTypeFor(object p0) { + return FormatString("Cannot create default value for type {0}.", p0); + } + + /// + /// A string like "Unhandled convert: {0}" + /// + internal static string UnhandledConvert(object p0) { + return FormatString("Unhandled convert: {0}", p0); + } + + /// + /// A string like "{0}.{1} has no publiclly visible method." + /// + internal static string NoCallableMethods(object p0, object p1) { + return FormatString("{0}.{1} has no publiclly visible method.", p0, p1); + } + + /// + /// A string like "Global/top-level local variable names must be unique." + /// + internal static string GlobalsMustBeUnique { + get { + return "Global/top-level local variable names must be unique."; + } + } + + /// + /// A string like "Generating code from non-serializable CallSiteBinder." + /// + internal static string GenNonSerializableBinder { + get { + return "Generating code from non-serializable CallSiteBinder."; + } + } + + /// + /// A string like "pecified path is invalid." + /// + internal static string InvalidPath { + get { + return "pecified path is invalid."; + } + } + + /// + /// A string like "Dictionaries are not hashable." + /// + internal static string DictionaryNotHashable { + get { + return "Dictionaries are not hashable."; + } + } + + /// + /// A string like "language already registered." + /// + internal static string LanguageRegistered { + get { + return "language already registered."; + } + } + + /// + /// A string like "The method or operation is not implemented." + /// + internal static string MethodOrOperatorNotImplemented { + get { + return "The method or operation is not implemented."; + } + } + + /// + /// A string like "No exception." + /// + internal static string NoException { + get { + return "No exception."; + } + } + + /// + /// A string like "Extension type {0} must be public." + /// + internal static string ExtensionMustBePublic(object p0) { + return FormatString("Extension type {0} must be public.", p0); + } + + /// + /// A string like "Already initialized." + /// + internal static string AlreadyInitialized { + get { + return "Already initialized."; + } + } + + /// + /// A string like "CreateScopeExtension must return a scope extension." + /// + internal static string MustReturnScopeExtension { + get { + return "CreateScopeExtension must return a scope extension."; + } + } + + /// + /// A string like "Invalid number of parameters for the service." + /// + internal static string InvalidParamNumForService { + get { + return "Invalid number of parameters for the service."; + } + } + + /// + /// A string like "Invalid type of argument {0}; expecting {1}." + /// + internal static string InvalidArgumentType(object p0, object p1) { + return FormatString("Invalid type of argument {0}; expecting {1}.", p0, p1); + } + + /// + /// A string like "Cannot change non-caching value." + /// + internal static string CannotChangeNonCachingValue { + get { + return "Cannot change non-caching value."; + } + } + + /// + /// A string like "Local variable '{0}' referenced before assignment." + /// + internal static string ReferencedBeforeAssignment(object p0) { + return FormatString("Local variable '{0}' referenced before assignment.", p0); + } + + /// + /// A string like "Field {0} is read-only" + /// + internal static string FieldReadonly(object p0) { + return FormatString("Field {0} is read-only", p0); + } + + /// + /// A string like "Property {0} is read-only" + /// + internal static string PropertyReadonly(object p0) { + return FormatString("Property {0} is read-only", p0); + } + + /// + /// A string like "Expected event from {0}.{1}, got event from {2}.{3}." + /// + internal static string UnexpectedEvent(object p0, object p1, object p2, object p3) { + return FormatString("Expected event from {0}.{1}, got event from {2}.{3}.", p0, p1, p2, p3); + } + + /// + /// A string like "expected bound event, got {0}." + /// + internal static string ExpectedBoundEvent(object p0) { + return FormatString("expected bound event, got {0}.", p0); + } + + /// + /// A string like "Expected type {0}, got {1}." + /// + internal static string UnexpectedType(object p0, object p1) { + return FormatString("Expected type {0}, got {1}.", p0, p1); + } + + /// + /// A string like "can only write to member {0}." + /// + internal static string MemberWriteOnly(object p0) { + return FormatString("can only write to member {0}.", p0); + } + + /// + /// A string like "No code to compile." + /// + internal static string NoCodeToCompile { + get { + return "No code to compile."; + } + } + + /// + /// A string like "Invalid stream type: {0}." + /// + internal static string InvalidStreamType(object p0) { + return FormatString("Invalid stream type: {0}.", p0); + } + + /// + /// A string like "Queue empty." + /// + internal static string QueueEmpty { + get { + return "Queue empty."; + } + } + + /// + /// A string like "Enumeration has not started. Call MoveNext." + /// + internal static string EnumerationNotStarted { + get { + return "Enumeration has not started. Call MoveNext."; + } + } + + /// + /// A string like "Enumeration already finished." + /// + internal static string EnumerationFinished { + get { + return "Enumeration already finished."; + } + } + + /// + /// A string like "can't add another casing for identifier {0}" + /// + internal static string CantAddCasing(object p0) { + return FormatString("can't add another casing for identifier {0}", p0); + } + + /// + /// A string like "can't add new identifier {0}" + /// + internal static string CantAddIdentifier(object p0) { + return FormatString("can't add new identifier {0}", p0); + } + + /// + /// A string like "Type '{0}' doesn't provide a suitable public constructor or its implementation is faulty: {1}" + /// + internal static string InvalidCtorImplementation(object p0, object p1) { + return FormatString("Type '{0}' doesn't provide a suitable public constructor or its implementation is faulty: {1}", p0, p1); + } + + /// + /// A string like "Invalid output directory." + /// + internal static string InvalidOutputDir { + get { + return "Invalid output directory."; + } + } + + /// + /// A string like "Invalid assembly name or file extension." + /// + internal static string InvalidAsmNameOrExtension { + get { + return "Invalid assembly name or file extension."; + } + } + + /// + /// A string like "Cannot emit constant {0} ({1})" + /// + internal static string CanotEmitConstant(object p0, object p1) { + return FormatString("Cannot emit constant {0} ({1})", p0, p1); + } + + /// + /// A string like "No implicit cast from {0} to {1}" + /// + internal static string NoImplicitCast(object p0, object p1) { + return FormatString("No implicit cast from {0} to {1}", p0, p1); + } + + /// + /// A string like "No explicit cast from {0} to {1}" + /// + internal static string NoExplicitCast(object p0, object p1) { + return FormatString("No explicit cast from {0} to {1}", p0, p1); + } + + /// + /// A string like "name '{0}' not defined" + /// + internal static string NameNotDefined(object p0) { + return FormatString("name '{0}' not defined", p0); + } + + /// + /// A string like "No default value for a given type." + /// + internal static string NoDefaultValue { + get { + return "No default value for a given type."; + } + } + + /// + /// A string like "Specified language provider type is not registered." + /// + internal static string UnknownLanguageProviderType { + get { + return "Specified language provider type is not registered."; + } + } + + /// + /// A string like "can't read from property" + /// + internal static string CantReadProperty { + get { + return "can't read from property"; + } + } + + /// + /// A string like "can't write to property" + /// + internal static string CantWriteProperty { + get { + return "can't write to property"; + } + } + + /// + /// A string like "Cannot create instance of {0} because it contains generic parameters" + /// + internal static string IllegalNew_GenericParams(object p0) { + return FormatString("Cannot create instance of {0} because it contains generic parameters", p0); + } + + /// + /// A string like "Non-verifiable assembly generated: {0}:\nAssembly preserved as {1}\nError text:\n{2}\n" + /// + internal static string VerificationException(object p0, object p1, object p2) { + return FormatString("Non-verifiable assembly generated: {0}:\nAssembly preserved as {1}\nError text:\n{2}\n", p0, p1, p2); + } + + } + /// + /// Strongly-typed and parameterized exception factory. + /// + + internal static partial class Error { + /// + /// ArgumentException with message like "Either code or target must be specified." + /// + internal static Exception MustHaveCodeOrTarget() { + return new ArgumentException(Strings.MustHaveCodeOrTarget); + } + + /// + /// InvalidOperationException with message like "Type parameter is {0}. Expected a delegate." + /// + internal static Exception TypeParameterIsNotDelegate(object p0) { + return new InvalidOperationException(Strings.TypeParameterIsNotDelegate(p0)); + } + + /// + /// InvalidOperationException with message like "Cannot cast from type '{0}' to type '{1}" + /// + internal static Exception InvalidCast(object p0, object p1) { + return new InvalidOperationException(Strings.InvalidCast(p0, p1)); + } + + /// + /// InvalidOperationException with message like "unknown member type: '{0}'. " + /// + internal static Exception UnknownMemberType(object p0) { + return new InvalidOperationException(Strings.UnknownMemberType(p0)); + } + + /// + /// InvalidOperationException with message like "RuleBuilder can only be used with delegates whose first argument is CallSite." + /// + internal static Exception FirstArgumentMustBeCallSite() { + return new InvalidOperationException(Strings.FirstArgumentMustBeCallSite); + } + + /// + /// InvalidOperationException with message like "no instance for call." + /// + internal static Exception NoInstanceForCall() { + return new InvalidOperationException(Strings.NoInstanceForCall); + } + + /// + /// InvalidOperationException with message like "Missing Test." + /// + internal static Exception MissingTest() { + return new InvalidOperationException(Strings.MissingTest); + } + + /// + /// InvalidOperationException with message like "Missing Target." + /// + internal static Exception MissingTarget() { + return new InvalidOperationException(Strings.MissingTarget); + } + + /// + /// TypeLoadException with message like "The operation requires a non-generic type for {0}, but this represents generic types only" + /// + internal static Exception NonGenericWithGenericGroup(object p0) { + return new TypeLoadException(Strings.NonGenericWithGenericGroup(p0)); + } + + /// + /// ArgumentException with message like "Invalid operation: '{0}'" + /// + internal static Exception InvalidOperation(object p0) { + return new ArgumentException(Strings.InvalidOperation(p0)); + } + + /// + /// InvalidOperationException with message like "Finally already defined." + /// + internal static Exception FinallyAlreadyDefined() { + return new InvalidOperationException(Strings.FinallyAlreadyDefined); + } + + /// + /// InvalidOperationException with message like "Can not have fault and finally." + /// + internal static Exception CannotHaveFaultAndFinally() { + return new InvalidOperationException(Strings.CannotHaveFaultAndFinally); + } + + /// + /// InvalidOperationException with message like "Fault already defined." + /// + internal static Exception FaultAlreadyDefined() { + return new InvalidOperationException(Strings.FaultAlreadyDefined); + } + + /// + /// ArgumentException with message like "Cannot create default value for type {0}." + /// + internal static Exception CantCreateDefaultTypeFor(object p0) { + return new ArgumentException(Strings.CantCreateDefaultTypeFor(p0)); + } + + /// + /// ArgumentException with message like "Unhandled convert: {0}" + /// + internal static Exception UnhandledConvert(object p0) { + return new ArgumentException(Strings.UnhandledConvert(p0)); + } + + /// + /// InvalidOperationException with message like "{0}.{1} has no publiclly visible method." + /// + internal static Exception NoCallableMethods(object p0, object p1) { + return new InvalidOperationException(Strings.NoCallableMethods(p0, p1)); + } + + /// + /// ArgumentException with message like "Global/top-level local variable names must be unique." + /// + internal static Exception GlobalsMustBeUnique() { + return new ArgumentException(Strings.GlobalsMustBeUnique); + } + + /// + /// ArgumentException with message like "Generating code from non-serializable CallSiteBinder." + /// + internal static Exception GenNonSerializableBinder() { + return new ArgumentException(Strings.GenNonSerializableBinder); + } + + /// + /// ArgumentException with message like "pecified path is invalid." + /// + internal static Exception InvalidPath() { + return new ArgumentException(Strings.InvalidPath); + } + + /// + /// ArgumentTypeException with message like "Dictionaries are not hashable." + /// + internal static Exception DictionaryNotHashable() { + return new ArgumentTypeException(Strings.DictionaryNotHashable); + } + + /// + /// InvalidOperationException with message like "language already registered." + /// + internal static Exception LanguageRegistered() { + return new InvalidOperationException(Strings.LanguageRegistered); + } + + /// + /// NotImplementedException with message like "The method or operation is not implemented." + /// + internal static Exception MethodOrOperatorNotImplemented() { + return new NotImplementedException(Strings.MethodOrOperatorNotImplemented); + } + + /// + /// InvalidOperationException with message like "No exception." + /// + internal static Exception NoException() { + return new InvalidOperationException(Strings.NoException); + } + + /// + /// ArgumentException with message like "Extension type {0} must be public." + /// + internal static Exception ExtensionMustBePublic(object p0) { + return new ArgumentException(Strings.ExtensionMustBePublic(p0)); + } + + /// + /// InvalidOperationException with message like "Already initialized." + /// + internal static Exception AlreadyInitialized() { + return new InvalidOperationException(Strings.AlreadyInitialized); + } + + /// + /// InvalidImplementationException with message like "CreateScopeExtension must return a scope extension." + /// + internal static Exception MustReturnScopeExtension() { + return new InvalidImplementationException(Strings.MustReturnScopeExtension); + } + + /// + /// ArgumentException with message like "Invalid number of parameters for the service." + /// + internal static Exception InvalidParamNumForService() { + return new ArgumentException(Strings.InvalidParamNumForService); + } + + /// + /// ArgumentException with message like "Invalid type of argument {0}; expecting {1}." + /// + internal static Exception InvalidArgumentType(object p0, object p1) { + return new ArgumentException(Strings.InvalidArgumentType(p0, p1)); + } + + /// + /// ArgumentException with message like "Cannot change non-caching value." + /// + internal static Exception CannotChangeNonCachingValue() { + return new ArgumentException(Strings.CannotChangeNonCachingValue); + } + + /// + /// Microsoft.Scripting.Runtime.UnboundLocalException with message like "Local variable '{0}' referenced before assignment." + /// + internal static Exception ReferencedBeforeAssignment(object p0) { + return new Microsoft.Scripting.Runtime.UnboundLocalException(Strings.ReferencedBeforeAssignment(p0)); + } + + /// + /// MissingMemberException with message like "Field {0} is read-only" + /// + internal static Exception FieldReadonly(object p0) { + return new MissingMemberException(Strings.FieldReadonly(p0)); + } + + /// + /// MissingMemberException with message like "Property {0} is read-only" + /// + internal static Exception PropertyReadonly(object p0) { + return new MissingMemberException(Strings.PropertyReadonly(p0)); + } + + /// + /// ArgumentException with message like "Expected event from {0}.{1}, got event from {2}.{3}." + /// + internal static Exception UnexpectedEvent(object p0, object p1, object p2, object p3) { + return new ArgumentException(Strings.UnexpectedEvent(p0, p1, p2, p3)); + } + + /// + /// ArgumentTypeException with message like "expected bound event, got {0}." + /// + internal static Exception ExpectedBoundEvent(object p0) { + return new ArgumentTypeException(Strings.ExpectedBoundEvent(p0)); + } + + /// + /// ArgumentTypeException with message like "Expected type {0}, got {1}." + /// + internal static Exception UnexpectedType(object p0, object p1) { + return new ArgumentTypeException(Strings.UnexpectedType(p0, p1)); + } + + /// + /// MemberAccessException with message like "can only write to member {0}." + /// + internal static Exception MemberWriteOnly(object p0) { + return new MemberAccessException(Strings.MemberWriteOnly(p0)); + } + + /// + /// InvalidOperationException with message like "No code to compile." + /// + internal static Exception NoCodeToCompile() { + return new InvalidOperationException(Strings.NoCodeToCompile); + } + + /// + /// ArgumentException with message like "Invalid stream type: {0}." + /// + internal static Exception InvalidStreamType(object p0) { + return new ArgumentException(Strings.InvalidStreamType(p0)); + } + + /// + /// InvalidOperationException with message like "Queue empty." + /// + internal static Exception QueueEmpty() { + return new InvalidOperationException(Strings.QueueEmpty); + } + + /// + /// InvalidOperationException with message like "Enumeration has not started. Call MoveNext." + /// + internal static Exception EnumerationNotStarted() { + return new InvalidOperationException(Strings.EnumerationNotStarted); + } + + /// + /// InvalidOperationException with message like "Enumeration already finished." + /// + internal static Exception EnumerationFinished() { + return new InvalidOperationException(Strings.EnumerationFinished); + } + + /// + /// InvalidOperationException with message like "can't add another casing for identifier {0}" + /// + internal static Exception CantAddCasing(object p0) { + return new InvalidOperationException(Strings.CantAddCasing(p0)); + } + + /// + /// InvalidOperationException with message like "can't add new identifier {0}" + /// + internal static Exception CantAddIdentifier(object p0) { + return new InvalidOperationException(Strings.CantAddIdentifier(p0)); + } + + /// + /// ArgumentException with message like "Invalid output directory." + /// + internal static Exception InvalidOutputDir() { + return new ArgumentException(Strings.InvalidOutputDir); + } + + /// + /// ArgumentException with message like "Invalid assembly name or file extension." + /// + internal static Exception InvalidAsmNameOrExtension() { + return new ArgumentException(Strings.InvalidAsmNameOrExtension); + } + + /// + /// ArgumentException with message like "Cannot emit constant {0} ({1})" + /// + internal static Exception CanotEmitConstant(object p0, object p1) { + return new ArgumentException(Strings.CanotEmitConstant(p0, p1)); + } + + /// + /// ArgumentException with message like "No implicit cast from {0} to {1}" + /// + internal static Exception NoImplicitCast(object p0, object p1) { + return new ArgumentException(Strings.NoImplicitCast(p0, p1)); + } + + /// + /// ArgumentException with message like "No explicit cast from {0} to {1}" + /// + internal static Exception NoExplicitCast(object p0, object p1) { + return new ArgumentException(Strings.NoExplicitCast(p0, p1)); + } + + /// + /// MissingMemberException with message like "name '{0}' not defined" + /// + internal static Exception NameNotDefined(object p0) { + return new MissingMemberException(Strings.NameNotDefined(p0)); + } + + /// + /// ArgumentException with message like "No default value for a given type." + /// + internal static Exception NoDefaultValue() { + return new ArgumentException(Strings.NoDefaultValue); + } + + /// + /// ArgumentException with message like "Specified language provider type is not registered." + /// + internal static Exception UnknownLanguageProviderType() { + return new ArgumentException(Strings.UnknownLanguageProviderType); + } + + /// + /// InvalidOperationException with message like "can't read from property" + /// + internal static Exception CantReadProperty() { + return new InvalidOperationException(Strings.CantReadProperty); + } + + /// + /// InvalidOperationException with message like "can't write to property" + /// + internal static Exception CantWriteProperty() { + return new InvalidOperationException(Strings.CantWriteProperty); + } + + /// + /// ArgumentException with message like "Cannot create instance of {0} because it contains generic parameters" + /// + internal static Exception IllegalNew_GenericParams(object p0) { + return new ArgumentException(Strings.IllegalNew_GenericParams(p0)); + } + + /// + /// System.Security.VerificationException with message like "Non-verifiable assembly generated: {0}:\nAssembly preserved as {1}\nError text:\n{2}\n" + /// + internal static Exception VerificationException(object p0, object p1, object p2) { + return new System.Security.VerificationException(Strings.VerificationException(p0, p1, p2)); + } + + } + + // *** END GENERATED CODE *** + + #endregion + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ExceptionUtils.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ExceptionUtils.cs new file mode 100644 index 0000000000..b5cbef80cc --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ExceptionUtils.cs @@ -0,0 +1,32 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Utils { + public static class ExceptionUtils { + public static ArgumentOutOfRangeException MakeArgumentOutOfRangeException(string paramName, object actualValue, string message) { +#if SILVERLIGHT // ArgumentOutOfRangeException ctor overload + throw new ArgumentOutOfRangeException(paramName, string.Format("{0} (actual value is '{1}')", message, actualValue)); +#else + throw new ArgumentOutOfRangeException(paramName, actualValue, message); +#endif + } + + public static ArgumentNullException MakeArgumentItemNullException(int index, string arrayName) { + return new ArgumentNullException(String.Format("{0}[{1}]", arrayName, index)); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/IOUtils.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/IOUtils.cs new file mode 100644 index 0000000000..bb40524c8d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/IOUtils.cs @@ -0,0 +1,139 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.IO; +using System.Text; + +namespace Microsoft.Scripting.Utils { + public static class IOUtils { + /// + /// Seeks the first character of a specified line in the text stream. + /// + /// The reader. + /// Line number. The current position is assumed to be line #1. + /// + /// Returns true if the line is found, false otherwise. + /// + public static bool SeekLine(TextReader reader, int line) { + ContractUtils.RequiresNotNull(reader, "reader"); + if (line < 1) throw new ArgumentOutOfRangeException("line"); + if (line == 1) return true; + + int current_line = 1; + + for (; ; ) { + int c = reader.Read(); + + if (c == '\r') { + if (reader.Peek() == '\n') { + reader.Read(); + } + + current_line++; + if (current_line == line) return true; + + } else if (c == '\n') { + current_line++; + if (current_line == line) return true; + } else if (c == -1) { + return false; + } + } + } + + /// + /// Reads characters to a string until end position or a terminator is reached. + /// Doesn't include the terminator into the resulting string. + /// Returns null, if the reader is at the end position. + /// + public static string ReadTo(TextReader reader, char terminator) { + ContractUtils.RequiresNotNull(reader, "reader"); + + StringBuilder result = new StringBuilder(); + int ch; + for (; ; ) { + ch = reader.Read(); + + if (ch == -1) break; + if (ch == terminator) return result.ToString(); + + result.Append((char)ch); + } + return (result.Length > 0) ? result.ToString() : null; + } + + /// + /// Reads characters until end position or a terminator is reached. + /// Returns true if the character has been found (the reader is positioned right behind the character), + /// false otherwise. + /// + public static bool SeekTo(TextReader reader, char c) { + ContractUtils.RequiresNotNull(reader, "reader"); + + for (; ; ) { + int ch = reader.Read(); + if (ch == -1) return false; + if (ch == c) return true; + } + } + + public static string ToValidPath(string path) { + return ToValidPath(path, false, true); + } + + public static string ToValidPath(string path, bool isMask) { + return ToValidPath(path, isMask, true); + } + + public static string ToValidFileName(string path) { + return ToValidPath(path, false, false); + } + + private static string ToValidPath(string path, bool isMask, bool isPath) { + Debug.Assert(!isMask || isPath); + + if (String.IsNullOrEmpty(path)) { + return "_"; + } + + StringBuilder sb = new StringBuilder(path); + + if (isPath) { + foreach (char c in Path.GetInvalidPathChars()) { + sb.Replace(c, '_'); + } + } else { +#if SILVERLIGHT + foreach (char c in Path.GetInvalidPathChars()) { + sb.Replace(c, '_'); + } + sb.Replace(':', '_').Replace('*', '_').Replace('?', '_').Replace('\\', '_').Replace('/', '_'); +#else + foreach (char c in Path.GetInvalidFileNameChars()) { + sb.Replace(c, '_'); + } +#endif + } + + if (!isMask) { + sb.Replace('*', '_').Replace('?', '_'); + } + + return sb.ToString(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ListEqualityComparer.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ListEqualityComparer.cs new file mode 100644 index 0000000000..189711b8ca --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ListEqualityComparer.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; + +namespace Microsoft.Scripting.Utils { + // Compares two ICollection's using element equality + internal sealed class ListEqualityComparer : EqualityComparer> { + internal static readonly ListEqualityComparer Instance = new ListEqualityComparer(); + + private ListEqualityComparer() { } + + // EqualityComparer handles null and object identity for us + public override bool Equals(ICollection x, ICollection y) { + return x.ListEquals(y); + } + + public override int GetHashCode(ICollection obj) { + return obj.ListHashCode(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/MathUtils.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/MathUtils.cs new file mode 100644 index 0000000000..929c0e08c9 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/MathUtils.cs @@ -0,0 +1,70 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.Scripting.Utils { + using Math = System.Math; + + public static class MathUtils { + /// + /// Behaves like Math.Round(value, MidpointRounding.AwayFromZero) + /// Needed because CoreCLR doesn't support this particular overload of Math.Round + /// + public static double RoundAwayFromZero(double value) { +#if !SILVERLIGHT + return Math.Round(value, MidpointRounding.AwayFromZero); +#else + if (value < 0) { + return -RoundAwayFromZero(-value); + } + + // we can assume positive value + double result = Math.Floor(value); + if (value - result >= 0.5) { + result += 1.0; + } + return result; +#endif + } + + private static readonly double[] _RoundPowersOfTens = new double[] { 1E0, 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, 1E9, 1E10, 1E11, 1E12, 1E13, 1E14, 1E15 }; + + private static double GetPowerOf10(int precision) { + return (precision < 16) ? _RoundPowersOfTens[precision] : Math.Pow(10, precision); + } + + /// + /// Behaves like Math.Round(value, precision, MidpointRounding.AwayFromZero) + /// However, it works correctly on negative precisions and cases where precision is + /// outside of the [-15, 15] range. + /// + /// (This function is also needed because CoreCLR lacks this overload.) + /// + public static double RoundAwayFromZero(double value, int precision) { + if (precision >= 0) { + double num = GetPowerOf10(precision); + return RoundAwayFromZero(value * num) / num; + } else { + // Note: this code path could be merged with the precision >= 0 path, + // (by extending the cache to negative powers of 10) + // but the results seem to be more precise if we do it this way + double num = GetPowerOf10(-precision); + return RoundAwayFromZero(value / num) * num; + } + } + } + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/Publisher.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/Publisher.cs new file mode 100644 index 0000000000..c7ffc8aa1d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/Publisher.cs @@ -0,0 +1,135 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic; +using System.Threading; + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Scripting.Utils")] + + +namespace Microsoft.Scripting.Utils { + /// + /// Thread safe dictionary that allows lazy-creation where readers will block for + /// the creation of the lazily created value. Call GetOrCreateValue w/ a key + /// and a callback function. If the value exists it is returned, if not the create + /// callback is called (w/o any locks held). The create call back will only be called + /// once for each key. + /// + public class Publisher { + private readonly Dictionary> data = new Dictionary>(); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] // TODO: fix + public TValue GetOrCreateValue(TKey key, Func create) { + lock (data) { + PublishInfo pubValue; + if (data.TryGetValue(key, out pubValue)) { + if (pubValue.Value == null && pubValue.Exception == null) { + pubValue.PrepareForWait(); + Monitor.Exit(data); + + try { + pubValue.WaitForPublish(); + } finally { + Monitor.Enter(data); + pubValue.FinishWait(); + } + } + + if (pubValue.Exception != null) throw new Exception("Error", pubValue.Exception); + + return pubValue.Value; + } + + TValue ret; + // publish the empty PublishInfo + data[key] = pubValue = new PublishInfo(); + // release our lock while we create the new value + // then re-acquire the lock and publish the info. + Monitor.Exit(data); + try { + try { + ret = create(); + Debug.Assert(ret != null, "Can't publish a null value"); + } finally { + Monitor.Enter(data); + } + } catch (Exception e) { + pubValue.PublishError(e); + throw; + } + + pubValue.PublishValue(ret); + return ret; + } + } + + /// + /// Helper class which stores the published value + /// + class PublishInfo { + public PublishInfo() { + } + + // TODO: seems to be FxCop bug + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public T Value; + + // TODO: seems to be FxCop bug + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public Exception Exception; + + // TODO: seems to be FxCop bug + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + private ManualResetEvent _waitEvent; + + // TODO: seems to be FxCop bug + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + private int _waiters; + + public void PublishValue(T value) { + Value = value; + if (_waitEvent != null) _waitEvent.Set(); + } + + public void PublishError(Exception e) { + Exception = e; + } + + public void PrepareForWait() { + if (_waitEvent == null) { + ManualResetEvent mre = new ManualResetEvent(false); + if (Interlocked.CompareExchange(ref _waitEvent, mre, null) != null) { + mre.Close(); + } + } + _waiters++; + } + + public void WaitForPublish() { + _waitEvent.WaitOne(); + } + + public void FinishWait() { + _waiters--; + if (_waiters == 0) _waitEvent.Close(); + } + } + } + + +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ReadOnlyDictionary.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ReadOnlyDictionary.cs new file mode 100644 index 0000000000..b522efa81f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ReadOnlyDictionary.cs @@ -0,0 +1,196 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace Microsoft.Scripting.Utils { + + // Like ReadOnlyCollection: wraps an IDictionary in a read-only wrapper + [Serializable] + internal sealed class ReadOnlyDictionary : IDictionary { + + // For wrapping non-readonly Keys, Values collections + // Not used for standard dictionaries, which return read-only Keys and Values + private sealed class ReadOnlyWrapper : ICollection { + // no idea why this warning is here + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + private readonly ICollection _collection; + + internal ReadOnlyWrapper(ICollection collection) { + _collection = collection; + } + + #region ICollection Members + + public void Add(T item) { + throw new NotSupportedException("Collection is read-only."); + } + + public void Clear() { + throw new NotSupportedException("Collection is read-only."); + } + + public bool Contains(T item) { + return _collection.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) { + _collection.CopyTo(array, arrayIndex); + } + + public int Count { + get { return _collection.Count; } + } + + public bool IsReadOnly { + get { return true; } + } + + public bool Remove(T item) { + throw new NotSupportedException("Collection is read-only."); + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + return _collection.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return _collection.GetEnumerator(); + } + + #endregion + } + + private readonly IDictionary _dict; + + internal ReadOnlyDictionary(IDictionary dict) { + ReadOnlyDictionary rodict = dict as ReadOnlyDictionary; + _dict = (rodict != null) ? rodict._dict : dict; + } + + #region IDictionary Members + + public bool ContainsKey(K key) { + return _dict.ContainsKey(key); + } + + public ICollection Keys { + get { + ICollection keys = _dict.Keys; + if (!keys.IsReadOnly) { + return new ReadOnlyWrapper(keys); + } + return keys; + } + } + + public bool TryGetValue(K key, out V value) { + return _dict.TryGetValue(key, out value); + } + + public ICollection Values { + get { + ICollection values = _dict.Values; + if (!values.IsReadOnly) { + return new ReadOnlyWrapper(values); + } + return values; + } + } + + public V this[K key] { + get { + return _dict[key]; + } + } + + + void IDictionary.Add(K key, V value) { + throw new NotSupportedException("Collection is read-only."); + } + + bool IDictionary.Remove(K key) { + throw new NotSupportedException("Collection is read-only."); + } + + V IDictionary.this[K key] { + get { + return _dict[key]; + } + set { + throw new NotSupportedException("Collection is read-only."); + } + } + + #endregion + + #region ICollection> Members + + public bool Contains(KeyValuePair item) { + return _dict.Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) { + _dict.CopyTo(array, arrayIndex); + } + + public int Count { + get { return _dict.Count; } + } + + public bool IsReadOnly { + get { return true; } + } + + void ICollection>.Add(KeyValuePair item) { + throw new NotSupportedException("Collection is read-only."); + } + + void ICollection>.Clear() { + throw new NotSupportedException("Collection is read-only."); + } + + bool ICollection>.Remove(KeyValuePair item) { + throw new NotSupportedException("Collection is read-only."); + } + + #endregion + + #region IEnumerable> Members + + public IEnumerator> GetEnumerator() { + return _dict.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return _dict.GetEnumerator(); + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ReferenceEqualityComparer.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ReferenceEqualityComparer.cs new file mode 100644 index 0000000000..803b763e8c --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ReferenceEqualityComparer.cs @@ -0,0 +1,33 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Microsoft.Scripting.Utils { + internal sealed class ReferenceEqualityComparer : IEqualityComparer { + internal static readonly ReferenceEqualityComparer Instance = new ReferenceEqualityComparer(); + + private ReferenceEqualityComparer() { } + + public bool Equals(T x, T y) { + return object.ReferenceEquals(x, y); + } + + public int GetHashCode(T obj) { + return RuntimeHelpers.GetHashCode(obj); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ReflectedCaller.Generated.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ReflectedCaller.Generated.cs new file mode 100644 index 0000000000..80c5c4754a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ReflectedCaller.Generated.cs @@ -0,0 +1,404 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Dynamic; + +namespace Microsoft.Scripting.Utils { + #region Generated Reflected Caller + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_all from: generate_reflected_calls.py + + public partial class ReflectedCaller { + private const int MaxHelpers = 7; + private const int MaxArgs = 3; + + public virtual object InvokeInstance(object instance, params object[] args) { + switch(args.Length) { + case 0: return Invoke(instance); + case 1: return Invoke(instance, args[0]); + case 2: return Invoke(instance, args[0], args[1]); + case 3: return Invoke(instance, args[0], args[1], args[2]); + case 4: return Invoke(instance, args[0], args[1], args[2], args[3]); + case 5: return Invoke(instance, args[0], args[1], args[2], args[3], args[4]); + default: throw new InvalidOperationException(); + } + } + + public virtual object Invoke(params object[] args) { + switch(args.Length) { + case 0: return Invoke(); + case 1: return Invoke(args[0]); + case 2: return Invoke(args[0], args[1]); + case 3: return Invoke(args[0], args[1], args[2]); + case 4: return Invoke(args[0], args[1], args[2], args[3]); + case 5: return Invoke(args[0], args[1], args[2], args[3], args[4]); + case 6: return Invoke(args[0], args[1], args[2], args[3], args[4], args[5]); + default: throw new InvalidOperationException(); + } + } + + public virtual object Invoke() { throw new InvalidOperationException(); } + public virtual object Invoke(object arg0) { throw new InvalidOperationException(); } + public virtual object Invoke(object arg0, object arg1) { throw new InvalidOperationException(); } + public virtual object Invoke(object arg0, object arg1, object arg2) { throw new InvalidOperationException(); } + public virtual object Invoke(object arg0, object arg1, object arg2, object arg3) { throw new InvalidOperationException(); } + public virtual object Invoke(object arg0, object arg1, object arg2, object arg3, object arg4) { throw new InvalidOperationException(); } + public virtual object Invoke(object arg0, object arg1, object arg2, object arg3, object arg4, object arg5) { throw new InvalidOperationException(); } + + /// + /// Fast creation works if we have a known primitive types for the entire + /// method siganture. If we have any non-primitive types then FastCreate + /// falls back to SlowCreate which works for all types. + /// + /// Fast creation is fast because it avoids using reflection (MakeGenericType + /// and Activator.CreateInstance) to create the types. It does this through + /// calling a series of generic methods picking up each strong type of the + /// signature along the way. When it runs out of types it news up the + /// appropriate ReflectedCaller with the strong-types that have been built up. + /// + /// One relaxation is that for return types which are non-primitive types + /// we can fallback to object due to relaxed delegates. + /// + private static ReflectedCaller FastCreate(MethodInfo target, ParameterInfo[] pi) { + Type t = TryGetParameterOrReturnType(target, pi, 0); + if (t == null) { + return new ActionHelper(target); + } + + if (t.IsEnum) return SlowCreate(target, pi); + switch (Type.GetTypeCode(t)) { + case TypeCode.Object: { + if (t != typeof(object) && (IndexIsNotReturnType(0, target, pi) || t.IsValueType)) { + // if we're on the return type relaxed delegates makes it ok to use object + goto default; + } + return FastCreate(target, pi); + } + case TypeCode.Int16: return FastCreate(target, pi); + case TypeCode.Int32: return FastCreate(target, pi); + case TypeCode.Int64: return FastCreate(target, pi); + case TypeCode.Boolean: return FastCreate(target, pi); + case TypeCode.Char: return FastCreate(target, pi); + case TypeCode.Byte: return FastCreate(target, pi); + case TypeCode.Decimal: return FastCreate(target, pi); + case TypeCode.DateTime: return FastCreate(target, pi); + case TypeCode.Double: return FastCreate(target, pi); + case TypeCode.Single: return FastCreate(target, pi); + case TypeCode.UInt16: return FastCreate(target, pi); + case TypeCode.UInt32: return FastCreate(target, pi); + case TypeCode.UInt64: return FastCreate(target, pi); + case TypeCode.String: return FastCreate(target, pi); + case TypeCode.SByte: return FastCreate(target, pi); + default: return SlowCreate(target, pi); + } + } + + private static ReflectedCaller FastCreate(MethodInfo target, ParameterInfo[] pi) { + Type t = TryGetParameterOrReturnType(target, pi, 1); + if (t == null) { + if (target.ReturnType == typeof(void)) { + return new ActionHelper(target); + } + return new InvokeHelper(target); + } + + if (t.IsEnum) return SlowCreate(target, pi); + switch (Type.GetTypeCode(t)) { + case TypeCode.Object: { + if (t != typeof(object) && (IndexIsNotReturnType(1, target, pi) || t.IsValueType)) { + // if we're on the return type relaxed delegates makes it ok to use object + goto default; + } + return FastCreate(target, pi); + } + case TypeCode.Int16: return FastCreate(target, pi); + case TypeCode.Int32: return FastCreate(target, pi); + case TypeCode.Int64: return FastCreate(target, pi); + case TypeCode.Boolean: return FastCreate(target, pi); + case TypeCode.Char: return FastCreate(target, pi); + case TypeCode.Byte: return FastCreate(target, pi); + case TypeCode.Decimal: return FastCreate(target, pi); + case TypeCode.DateTime: return FastCreate(target, pi); + case TypeCode.Double: return FastCreate(target, pi); + case TypeCode.Single: return FastCreate(target, pi); + case TypeCode.UInt16: return FastCreate(target, pi); + case TypeCode.UInt32: return FastCreate(target, pi); + case TypeCode.UInt64: return FastCreate(target, pi); + case TypeCode.String: return FastCreate(target, pi); + case TypeCode.SByte: return FastCreate(target, pi); + default: return SlowCreate(target, pi); + } + } + + private static ReflectedCaller FastCreate(MethodInfo target, ParameterInfo[] pi) { + Type t = TryGetParameterOrReturnType(target, pi, 2); + if (t == null) { + if (target.ReturnType == typeof(void)) { + return new ActionHelper(target); + } + return new InvokeHelper(target); + } + + if (t.IsEnum) return SlowCreate(target, pi); + switch (Type.GetTypeCode(t)) { + case TypeCode.Object: { + Debug.Assert(pi.Length == 2); + if (t.IsValueType) goto default; + + return new InvokeHelper(target); + } + case TypeCode.Int16: return new InvokeHelper(target); + case TypeCode.Int32: return new InvokeHelper(target); + case TypeCode.Int64: return new InvokeHelper(target); + case TypeCode.Boolean: return new InvokeHelper(target); + case TypeCode.Char: return new InvokeHelper(target); + case TypeCode.Byte: return new InvokeHelper(target); + case TypeCode.Decimal: return new InvokeHelper(target); + case TypeCode.DateTime: return new InvokeHelper(target); + case TypeCode.Double: return new InvokeHelper(target); + case TypeCode.Single: return new InvokeHelper(target); + case TypeCode.UInt16: return new InvokeHelper(target); + case TypeCode.UInt32: return new InvokeHelper(target); + case TypeCode.UInt64: return new InvokeHelper(target); + case TypeCode.String: return new InvokeHelper(target); + case TypeCode.SByte: return new InvokeHelper(target); + default: return SlowCreate(target, pi); + } + } + + private static Type GetHelperType(MethodInfo info, Type[] arrTypes) { + Type t; + if (info.ReturnType == typeof(void)) { + switch (arrTypes.Length) { + case 0: t = typeof(ActionHelper); break; + case 1: t = typeof(ActionHelper<>).MakeGenericType(arrTypes); break; + case 2: t = typeof(ActionHelper<,>).MakeGenericType(arrTypes); break; + case 3: t = typeof(ActionHelper<,,>).MakeGenericType(arrTypes); break; + case 4: t = typeof(ActionHelper<,,,>).MakeGenericType(arrTypes); break; + case 5: t = typeof(ActionHelper<,,,,>).MakeGenericType(arrTypes); break; + case 6: t = typeof(ActionHelper<,,,,,>).MakeGenericType(arrTypes); break; + default: throw new InvalidOperationException(); + } + } else { + switch (arrTypes.Length) { + case 1: t = typeof(InvokeHelper<>).MakeGenericType(arrTypes); break; + case 2: t = typeof(InvokeHelper<,>).MakeGenericType(arrTypes); break; + case 3: t = typeof(InvokeHelper<,,>).MakeGenericType(arrTypes); break; + case 4: t = typeof(InvokeHelper<,,,>).MakeGenericType(arrTypes); break; + case 5: t = typeof(InvokeHelper<,,,,>).MakeGenericType(arrTypes); break; + case 6: t = typeof(InvokeHelper<,,,,,>).MakeGenericType(arrTypes); break; + case 7: t = typeof(InvokeHelper<,,,,,,>).MakeGenericType(arrTypes); break; + default: throw new InvalidOperationException(); + } + } + return t; + } + } + + sealed class ActionHelper : ReflectedCaller { + private Action _target; + + public ActionHelper(MethodInfo target) { + _target = (Action)Delegate.CreateDelegate(typeof(Action), target); + } + + public override object Invoke() { + _target(); + return null; + } + } + + sealed class ActionHelper : ReflectedCaller { + private Action _target; + + public ActionHelper(MethodInfo target) { + _target = (Action)Delegate.CreateDelegate(typeof(Action), target); + } + + public override object Invoke(object arg0) { + _target(arg0 != null ? (T0)arg0 : default(T0)); + return null; + } + } + + sealed class ActionHelper : ReflectedCaller { + private Action _target; + + public ActionHelper(MethodInfo target) { + _target = (Action)Delegate.CreateDelegate(typeof(Action), target); + } + + public override object Invoke(object arg0, object arg1) { + _target(arg0 != null ? (T0)arg0 : default(T0), arg1 != null ? (T1)arg1 : default(T1)); + return null; + } + } + + sealed class ActionHelper : ReflectedCaller { + private Action _target; + + public ActionHelper(MethodInfo target) { + _target = (Action)Delegate.CreateDelegate(typeof(Action), target); + } + + public override object Invoke(object arg0, object arg1, object arg2) { + _target(arg0 != null ? (T0)arg0 : default(T0), arg1 != null ? (T1)arg1 : default(T1), arg2 != null ? (T2)arg2 : default(T2)); + return null; + } + } + + sealed class ActionHelper : ReflectedCaller { + private Action _target; + + public ActionHelper(MethodInfo target) { + _target = (Action)Delegate.CreateDelegate(typeof(Action), target); + } + + public override object Invoke(object arg0, object arg1, object arg2, object arg3) { + _target(arg0 != null ? (T0)arg0 : default(T0), arg1 != null ? (T1)arg1 : default(T1), arg2 != null ? (T2)arg2 : default(T2), arg3 != null ? (T3)arg3 : default(T3)); + return null; + } + } + + sealed class ActionHelper : ReflectedCaller { + private Action _target; + + public ActionHelper(MethodInfo target) { + _target = (Action)Delegate.CreateDelegate(typeof(Action), target); + } + + public override object Invoke(object arg0, object arg1, object arg2, object arg3, object arg4) { + _target(arg0 != null ? (T0)arg0 : default(T0), arg1 != null ? (T1)arg1 : default(T1), arg2 != null ? (T2)arg2 : default(T2), arg3 != null ? (T3)arg3 : default(T3), arg4 != null ? (T4)arg4 : default(T4)); + return null; + } + } + + sealed class ActionHelper : ReflectedCaller { + private Action _target; + + public ActionHelper(MethodInfo target) { + _target = (Action)Delegate.CreateDelegate(typeof(Action), target); + } + + public override object Invoke(object arg0, object arg1, object arg2, object arg3, object arg4, object arg5) { + _target(arg0 != null ? (T0)arg0 : default(T0), arg1 != null ? (T1)arg1 : default(T1), arg2 != null ? (T2)arg2 : default(T2), arg3 != null ? (T3)arg3 : default(T3), arg4 != null ? (T4)arg4 : default(T4), arg5 != null ? (T5)arg5 : default(T5)); + return null; + } + } + + sealed class InvokeHelper : ReflectedCaller { + private Func _target; + + public InvokeHelper(MethodInfo target) { + _target = (Func)Delegate.CreateDelegate(typeof(Func), target); + } + + public override object Invoke() { + return _target(); + } + } + + sealed class InvokeHelper : ReflectedCaller { + private Func _target; + + public InvokeHelper(MethodInfo target) { + _target = (Func)Delegate.CreateDelegate(typeof(Func), target); + } + + public override object Invoke(object arg0) { + return _target(arg0 != null ? (T0)arg0 : default(T0)); + } + } + + sealed class InvokeHelper : ReflectedCaller { + private Func _target; + + public InvokeHelper(MethodInfo target) { + _target = (Func)Delegate.CreateDelegate(typeof(Func), target); + } + + public override object Invoke(object arg0, object arg1) { + return _target(arg0 != null ? (T0)arg0 : default(T0), arg1 != null ? (T1)arg1 : default(T1)); + } + } + + sealed class InvokeHelper : ReflectedCaller { + private Func _target; + + public InvokeHelper(MethodInfo target) { + _target = (Func)Delegate.CreateDelegate(typeof(Func), target); + } + + public override object Invoke(object arg0, object arg1, object arg2) { + return _target(arg0 != null ? (T0)arg0 : default(T0), arg1 != null ? (T1)arg1 : default(T1), arg2 != null ? (T2)arg2 : default(T2)); + } + } + + sealed class InvokeHelper : ReflectedCaller { + private Func _target; + + public InvokeHelper(MethodInfo target) { + _target = (Func)Delegate.CreateDelegate(typeof(Func), target); + } + + public override object Invoke(object arg0, object arg1, object arg2, object arg3) { + return _target(arg0 != null ? (T0)arg0 : default(T0), arg1 != null ? (T1)arg1 : default(T1), arg2 != null ? (T2)arg2 : default(T2), arg3 != null ? (T3)arg3 : default(T3)); + } + } + + sealed class InvokeHelper : ReflectedCaller { + private Func _target; + + public InvokeHelper(MethodInfo target) { + _target = (Func)Delegate.CreateDelegate(typeof(Func), target); + } + + public override object Invoke(object arg0, object arg1, object arg2, object arg3, object arg4) { + return _target(arg0 != null ? (T0)arg0 : default(T0), arg1 != null ? (T1)arg1 : default(T1), arg2 != null ? (T2)arg2 : default(T2), arg3 != null ? (T3)arg3 : default(T3), arg4 != null ? (T4)arg4 : default(T4)); + } + } + + sealed class InvokeHelper : ReflectedCaller { + private Func _target; + + public InvokeHelper(MethodInfo target) { + _target = (Func)Delegate.CreateDelegate(typeof(Func), target); + } + + public override object Invoke(object arg0, object arg1, object arg2, object arg3, object arg4, object arg5) { + return _target(arg0 != null ? (T0)arg0 : default(T0), arg1 != null ? (T1)arg1 : default(T1), arg2 != null ? (T2)arg2 : default(T2), arg3 != null ? (T3)arg3 : default(T3), arg4 != null ? (T4)arg4 : default(T4), arg5 != null ? (T5)arg5 : default(T5)); + } + } + + sealed partial class SlowReflectedCaller : ReflectedCaller { + public override object Invoke() { + return InvokeWorker(); + } + public override object Invoke(object arg0) { + return InvokeWorker(arg0); + } + public override object Invoke(object arg0, object arg1) { + return InvokeWorker(arg0, arg1); + } + } + + // *** END GENERATED CODE *** + + #endregion +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ReflectedCaller.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ReflectedCaller.cs new file mode 100644 index 0000000000..472874c35d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ReflectedCaller.cs @@ -0,0 +1,193 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Utils { + public partial class ReflectedCaller { + internal ReflectedCaller() { } + private static readonly Dictionary _cache = new Dictionary(); + + /// + /// Creates a new ReflectedCaller which can be used to quickly invoke the provided MethodInfo. + /// + public static ReflectedCaller Create(MethodInfo info) { + if ((!info.IsStatic && info.DeclaringType.IsValueType) || + info is System.Reflection.Emit.DynamicMethod) { + return new SlowReflectedCaller(info); + } + + ParameterInfo[] pis = info.GetParameters(); + int argCnt = pis.Length; + if (!info.IsStatic) argCnt++; + if (argCnt >= MaxHelpers) { + // no delegate for this size, fallback to reflection invoke + return new SlowReflectedCaller(info); + } + + foreach (ParameterInfo pi in pis) { + if (pi.ParameterType.IsByRef) { + // we don't support ref args via generics. + return new SlowReflectedCaller(info); + } + } + + // see if we've created one w/ a delegate + ReflectedCaller res; + if (ShouldCache(info)) { + lock (_cache) { + if (_cache.TryGetValue(info, out res)) { + return res; + } + } + } + + // create it + try { + if (argCnt < MaxArgs) { + res = FastCreate(info, pis); + } else { + res = SlowCreate(info, pis); + } + } catch (TargetInvocationException tie) { + if (!(tie.InnerException is NotSupportedException)) { + throw; + } + + res = new SlowReflectedCaller(info); + } catch (NotSupportedException) { + // if Delegate.CreateDelegate can't handle the method fallback to + // the slow reflection version. For example this can happen w/ + // a generic method defined on an interface and implemented on a class. + res = new SlowReflectedCaller(info); + } + + // cache it for future users if it's a reasonable method to cache + if (ShouldCache(info)) { + lock (_cache) { + _cache[info] = res; + } + } + + return res; + } + + private static bool ShouldCache(MethodInfo info) { + return !(info is DynamicMethod); + } + + /// + /// Gets the next type or null if no more types are available. + /// + private static Type TryGetParameterOrReturnType(MethodInfo target, ParameterInfo[] pi, int index) { + if (!target.IsStatic) { + index--; + if (index < 0) { + return target.DeclaringType; + } + } + + if (index < pi.Length) { + // next in signature + return pi[index].ParameterType; + } + + if (target.ReturnType == typeof(void) || index > pi.Length) { + // no more parameters + return null; + } + + // last parameter on Invoke is return type + return target.ReturnType; + } + + private static bool IndexIsNotReturnType(int index, MethodInfo target, ParameterInfo[] pi) { + return pi.Length != index || (pi.Length == index && !target.IsStatic); + } + + /// + /// Uses reflection to create new instance of the appropriate ReflectedCaller + /// + private static ReflectedCaller SlowCreate(MethodInfo info, ParameterInfo[] pis) { + List types = new List(); + if (!info.IsStatic) types.Add(info.DeclaringType); + foreach (ParameterInfo pi in pis) { + types.Add(pi.ParameterType); + } + if (info.ReturnType != typeof(void)) { + types.Add(info.ReturnType); + } + Type[] arrTypes = types.ToArray(); + + return (ReflectedCaller)Activator.CreateInstance(GetHelperType(info, arrTypes), info); + } + } + + sealed partial class SlowReflectedCaller : ReflectedCaller { + private MethodInfo _target; + + public SlowReflectedCaller(MethodInfo target) { + _target = target; + } + + public override object Invoke(params object[] args) { + return InvokeWorker(args); + } + + public override object InvokeInstance(object instance, params object[] args) { + if (_target.IsStatic) { + try { + return _target.Invoke(null, args); + } catch (TargetInvocationException e) { + throw ExceptionHelpers.UpdateForRethrow(e.InnerException); + } + } + + try { + return _target.Invoke(instance, args); + } catch (TargetInvocationException e) { + throw ExceptionHelpers.UpdateForRethrow(e.InnerException); + } + } + + private object InvokeWorker(params object[] args) { + if (_target.IsStatic) { + try { + return _target.Invoke(null, args); + } catch (TargetInvocationException e) { + throw ExceptionHelpers.UpdateForRethrow(e.InnerException); + } + } + + try { + return _target.Invoke(args[0], GetNonStaticArgs(args)); + } catch (TargetInvocationException e) { + throw ExceptionHelpers.UpdateForRethrow(e.InnerException); + } + } + + private static object[] GetNonStaticArgs(object[] args) { + object[] newArgs = new object[args.Length - 1]; + for (int i = 0; i < newArgs.Length; i++) { + newArgs[i] = args[i + 1]; + } + return newArgs; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ReflectionUtils.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ReflectionUtils.cs new file mode 100644 index 0000000000..38e35c99a2 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ReflectionUtils.cs @@ -0,0 +1,292 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Dynamic; + +namespace Microsoft.Scripting.Utils { + public static class ReflectionUtils { + + // Generic type names have the arity (number of generic type paramters) appended at the end. + // For eg. the mangled name of System.List is "List`1". This mangling is done to enable multiple + // generic types to exist as long as they have different arities. + public const char GenericArityDelimiter = '`'; + +#if SILVERLIGHT + public static bool IsNested(Type t) { + return t.DeclaringType != null; + } +#else + public static bool IsNested(Type t) { return t.IsNested; } +#endif + + public static StringBuilder FormatSignature(StringBuilder result, MethodBase method) { + ContractUtils.RequiresNotNull(result, "result"); + ContractUtils.RequiresNotNull(method, "method"); + + MethodInfo methodInfo = method as MethodInfo; + if (methodInfo != null) { + FormatTypeName(result, methodInfo.ReturnType); + result.Append(' '); + } + + MethodBuilder builder = method as MethodBuilder; + if (builder != null) { + result.Append(builder.Signature); + return result; + } + + ConstructorBuilder cb = method as ConstructorBuilder; + if (cb != null) { + result.Append(cb.Signature); + return result; + } + + FormatTypeName(result, method.DeclaringType); + result.Append("::"); + result.Append(method.Name); + + if (!method.IsConstructor) { + FormatTypeArgs(result, method.GetGenericArguments()); + } + + result.Append("("); + + if (!method.ContainsGenericParameters) { + ParameterInfo[] ps = method.GetParameters(); + for (int i = 0; i < ps.Length; i++) { + if (i > 0) result.Append(", "); + FormatTypeName(result, ps[i].ParameterType); + if (!System.String.IsNullOrEmpty(ps[i].Name)) { + result.Append(" "); + result.Append(ps[i].Name); + } + } + } else { + result.Append("?"); + } + + result.Append(")"); + return result; + } + + public static StringBuilder FormatTypeName(StringBuilder result, Type type) { + Assert.NotNull(result, type); + + if (type.IsGenericType) { + string genericName = type.GetGenericTypeDefinition().FullName.Replace('+', '.'); + int tickIndex = genericName.IndexOf('`'); + result.Append(tickIndex != -1 ? genericName.Substring(0, tickIndex) : genericName); + FormatTypeArgs(result, type.GetGenericArguments()); + } else if (type.IsGenericParameter) { + result.Append(type.Name); + } else { + result.Append(type.FullName.Replace('+', '.')); + } + return result; + } + + public static StringBuilder FormatTypeArgs(StringBuilder result, Type[] types) { + Assert.NotNull(result, types); + if (types.Length > 0) { + result.Append("<"); + + for (int i = 0; i < types.Length; i++) { + if (i > 0) result.Append(", "); + FormatTypeName(result, types[i]); + } + + result.Append(">"); + } + return result; + } + + public static T CreateInstance(Type actualType, params object[] args) { + Type type = typeof(T); + + Debug.Assert(type.IsAssignableFrom(actualType)); + + try { + return (T)Activator.CreateInstance(actualType, args); + } catch (TargetInvocationException e) { + throw new InvalidImplementationException(Strings.InvalidCtorImplementation(actualType, e.InnerException.Message), e.InnerException); + } catch (Exception e) { + throw new InvalidImplementationException(Strings.InvalidCtorImplementation(actualType, e.Message), e); + } + } + + public static object InvokeDelegate(Delegate d, params object[] args) { +#if SILVERLIGHT + // delegates: + // - close (target != null) + // - static (target becomes the first argument) + // - instance (no argument shuffling) + // - open (target == null) + // - static (no argument shuffling) + // - instance (first argument becomes the target) + + object target = d.Target; + + if (d.Method.IsStatic && target != null) { + // closed static -> target needs to be passed as the first arg: + object[] new_args = new object[args.Length + 1]; + args.CopyTo(new_args, 1); + new_args[0] = d.Target; + + target = null; + args = new_args; + + } else if (!d.Method.IsStatic && target == null) { + + // open instance -> the first arg is the target: + object[] new_args = new object[args.Length - 1]; + System.Array.Copy(args, 1, new_args, 0, new_args.Length); + + target = args[0]; + args = new_args; + } + + return d.Method.Invoke(target, args); +#else + return d.DynamicInvoke(args); +#endif + } + + /// + /// Creates an open delegate for the given (dynamic)method. + /// + public static Delegate CreateDelegate(MethodInfo methodInfo, Type delegateType) { + ContractUtils.RequiresNotNull(delegateType, "delegateType"); + ContractUtils.RequiresNotNull(methodInfo, "methodInfo"); + + DynamicMethod dm = methodInfo as DynamicMethod; + if (dm != null) { + return dm.CreateDelegate(delegateType); + } else { + return Delegate.CreateDelegate(delegateType, methodInfo); + } + } + + /// + /// Creates a closed delegate for the given (dynamic)method. + /// + public static Delegate CreateDelegate(MethodInfo methodInfo, Type delegateType, object target) { + ContractUtils.RequiresNotNull(methodInfo, "methodInfo"); + ContractUtils.RequiresNotNull(delegateType, "delegateType"); + + DynamicMethod dm = methodInfo as DynamicMethod; + if (dm != null) { + return dm.CreateDelegate(delegateType, target); + } else { + return Delegate.CreateDelegate(delegateType, target, methodInfo); + } + } + + public static void GetDelegateSignature(Type delegateType, out ParameterInfo[] parameterInfos, out ParameterInfo returnInfo) { + ContractUtils.RequiresNotNull(delegateType, "delegateType"); + + MethodInfo invokeMethod = delegateType.GetMethod("Invoke"); + ContractUtils.Requires(invokeMethod != null, "delegateType", Strings.InvalidDelegate); + + parameterInfos = invokeMethod.GetParameters(); + returnInfo = invokeMethod.ReturnParameter; + } + + public static MethodInfo[] GetMethodInfos(Delegate[] delegates) { + MethodInfo[] result = new MethodInfo[delegates.Length]; + for (int i = 0; i < delegates.Length; i++) result[i] = delegates[i].Method; + return result; + } + + public static MethodBase[] GetMethodInfos(MemberInfo[] members) { + return ArrayUtils.ConvertAll( + members, + delegate(MemberInfo inp) { return (MethodBase)inp; }); + } + + public static Type[] GetParameterTypes(ParameterInfo[] parameterInfos) { + Type[] result = new Type[parameterInfos.Length]; + for (int i = 0; i < parameterInfos.Length; i++) { + result[i] = parameterInfos[i].ParameterType; + } + return result; + } + + public static bool SignatureEquals(MethodInfo method, params Type[] requiredSignature) { + ContractUtils.RequiresNotNull(method, "method"); + + Type[] actualTypes = ReflectionUtils.GetParameterTypes(method.GetParameters()); + Debug.Assert(actualTypes.Length == requiredSignature.Length - 1); + int i = 0; + while (i < actualTypes.Length) { + if (actualTypes[i] != requiredSignature[i]) return false; + i++; + } + + return method.ReturnType == requiredSignature[i]; + } + + internal static string ToValidTypeName(string str) { + if (String.IsNullOrEmpty(str)) { + return "_"; + } + + StringBuilder sb = new StringBuilder(str); + for (int i = 0; i < str.Length; i++) { + if (str[i] == '\0' || str[i] == '.' || str[i] == '*' || str[i] == '+' || str[i] == '[' || str[i] == ']' || str[i] == '\\') { + sb[i] = '_'; + } + } + return sb.ToString(); + } + + /// + /// Like Type.GetInterfaces, but only returns the interfaces implemented by this type + /// and not its parents. + /// + public static List GetDeclaredInterfaces(Type type) { + IList baseInterfaces = (type.BaseType != null) ? type.BaseType.GetInterfaces() : Type.EmptyTypes; + List interfaces = new List(); + foreach (Type iface in type.GetInterfaces()) { + if (!baseInterfaces.Contains(iface)) { + interfaces.Add(iface); + } + } + return interfaces; + } + + public static string GetNormalizedTypeName(Type type) { + string name = type.Name; + if (type.IsGenericType) { + return GetNormalizedTypeName(name); + } + return name; + } + + public static string GetNormalizedTypeName(string typeName) { + Debug.Assert(typeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name + int backtick = typeName.IndexOf(ReflectionUtils.GenericArityDelimiter); + if (backtick != -1) return typeName.Substring(0, backtick); + return typeName; + } + + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/Set.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/Set.cs new file mode 100644 index 0000000000..bc8063d5f6 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/Set.cs @@ -0,0 +1,78 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; + +namespace Microsoft.Scripting.Utils { + + /// + /// A simple hashset, built on Dictionary{K, V} + /// + internal sealed class Set : ICollection { + private readonly Dictionary _data; + + internal Set() { + _data = new Dictionary(); + } + + internal Set(IEqualityComparer comparer) { + _data = new Dictionary(comparer); + } + + internal Set(IList list) { + _data = new Dictionary(list.Count); + foreach (T t in list) { + _data.Add(t, null); + } + } + + public void Add(T item) { + _data[item] = null; + } + + public void Clear() { + _data.Clear(); + } + + public bool Contains(T item) { + return _data.ContainsKey(item); + } + + public void CopyTo(T[] array, int arrayIndex) { + _data.Keys.CopyTo(array, arrayIndex); + } + + public int Count { + get { return _data.Count; } + } + + public bool IsReadOnly { + get { return false; } + } + + public bool Remove(T item) { + return _data.Remove(item); + } + + public IEnumerator GetEnumerator() { + return _data.Keys.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return _data.Keys.GetEnumerator(); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/StringUtils.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/StringUtils.cs new file mode 100644 index 0000000000..2f44002e4f --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/StringUtils.cs @@ -0,0 +1,304 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Microsoft.Scripting.Utils { + public static class StringUtils { + + public static Encoding DefaultEncoding { + get { +#if !SILVERLIGHT + return Encoding.Default; +#else + return Encoding.UTF8; +#endif + } + } + + public static string GetSuffix(string str, char separator, bool includeSeparator) { + ContractUtils.RequiresNotNull(str, "str"); + int last = str.LastIndexOf(separator); + return (last != -1) ? str.Substring(includeSeparator ? last : last + 1) : null; + } + + public static string GetLongestPrefix(string str, char separator, bool includeSeparator) { + ContractUtils.RequiresNotNull(str, "str"); + int last = str.LastIndexOf(separator); + return (last != -1) ? str.Substring(0, (includeSeparator || last == 0) ? last : last - 1) : null; + } + + public static int CountOf(string str, char c) { + if (System.String.IsNullOrEmpty(str)) return 0; + + int result = 0; + for (int i = 0; i < str.Length; i++) { + if (c == str[i]) { + result++; + } + } + return result; + } + + public static string[] Split(string str, string separator, int maxComponents, StringSplitOptions options) { + ContractUtils.RequiresNotNull(str, "str"); +#if SILVERLIGHT + if (string.IsNullOrEmpty(separator)) throw new ArgumentNullException("separator"); + + bool keep_empty = (options & StringSplitOptions.RemoveEmptyEntries) != StringSplitOptions.RemoveEmptyEntries; + + List result = new List(maxComponents == Int32.MaxValue ? 1 : maxComponents + 1); + + int i = 0; + int next; + while (maxComponents > 1 && i < str.Length && (next = str.IndexOf(separator, i)) != -1) { + + if (next > i || keep_empty) { + result.Add(str.Substring(i, next - i)); + maxComponents--; + } + + i = next + separator.Length; + } + + if (i < str.Length || keep_empty) { + result.Add(str.Substring(i)); + } + + return result.ToArray(); +#else + return str.Split(new string[] { separator }, maxComponents, options); +#endif + } + + public static string[] Split(string str, char[] separators, int maxComponents, StringSplitOptions options) { + ContractUtils.RequiresNotNull(str, "str"); +#if SILVERLIGHT + if (separators == null) return SplitOnWhiteSpace(str, maxComponents, options); + + bool keep_empty = (options & StringSplitOptions.RemoveEmptyEntries) != StringSplitOptions.RemoveEmptyEntries; + + List result = new List(maxComponents == Int32.MaxValue ? 1 : maxComponents + 1); + + int i = 0; + int next; + while (maxComponents > 1 && i < str.Length && (next = str.IndexOfAny(separators, i)) != -1) { + + if (next > i || keep_empty) { + result.Add(str.Substring(i, next - i)); + maxComponents--; + } + + i = next + 1; + } + + if (i < str.Length || keep_empty) { + result.Add(str.Substring(i)); + } + + return result.ToArray(); +#else + return str.Split(separators, maxComponents, options); +#endif + } + +#if SILVERLIGHT + public static string[] SplitOnWhiteSpace(string str, int maxComponents, StringSplitOptions options) { + ContractUtils.RequiresNotNull(str, "str"); + + bool keep_empty = (options & StringSplitOptions.RemoveEmptyEntries) != StringSplitOptions.RemoveEmptyEntries; + + List result = new List(maxComponents == Int32.MaxValue ? 1 : maxComponents + 1); + + int i = 0; + int next; + while (maxComponents > 1 && i < str.Length && (next = IndexOfWhiteSpace(str, i)) != -1) { + + if (next > i || keep_empty) { + result.Add(str.Substring(i, next - i)); + maxComponents--; + } + + i = next + 1; + } + + if (i < str.Length || keep_empty) { + result.Add(str.Substring(i)); + } + + return result.ToArray(); + } + + public static int IndexOfWhiteSpace(string str, int start) { + ContractUtils.RequiresNotNull(str, "str"); + if (start < 0 || start > str.Length) throw new ArgumentOutOfRangeException("start"); + + while (start < str.Length && !Char.IsWhiteSpace(str[start])) start++; + + return (start == str.Length) ? -1 : start; + } +#endif + + /// + /// Splits text and optionally indents first lines - breaks along words, not characters. + /// + public static string SplitWords(string text, bool indentFirst, int lineWidth) { + ContractUtils.RequiresNotNull(text, "text"); + + const string indent = " "; + + if (text.Length <= lineWidth || lineWidth <= 0) { + if (indentFirst) return indent + text; + return text; + } + + StringBuilder res = new StringBuilder(); + int start = 0, len = lineWidth; + while (start != text.Length) { + if (len >= lineWidth) { + // find last space to break on + while (len != 0 && !Char.IsWhiteSpace(text[start + len - 1])) + len--; + } + + if (res.Length != 0) res.Append(' '); + if (indentFirst || res.Length != 0) res.Append(indent); + + if (len == 0) { + int copying = System.Math.Min(lineWidth, text.Length - start); + res.Append(text, start, copying); + start += copying; + } else { + res.Append(text, start, len); + start += len; + } + res.AppendLine(); + len = System.Math.Min(lineWidth, text.Length - start); + } + return res.ToString(); + } + + public static string AddSlashes(string str) { + ContractUtils.RequiresNotNull(str, "str"); + + // TODO: optimize + StringBuilder result = new StringBuilder(str.Length); + for (int i = 0; i < str.Length; i++) { + switch (str[i]) { + case '\a': result.Append("\\a"); break; + case '\b': result.Append("\\b"); break; + case '\f': result.Append("\\f"); break; + case '\n': result.Append("\\n"); break; + case '\r': result.Append("\\r"); break; + case '\t': result.Append("\\t"); break; + case '\v': result.Append("\\v"); break; + default: result.Append(str[i]); break; + } + } + + return result.ToString(); + } + + public static bool TryParseDouble(string s, NumberStyles style, IFormatProvider provider, out double result) { +#if SILVERLIGHT // Double.TryParse + try { + result = Double.Parse(s, style, provider); + return true; + } catch { + result = 0.0; + return false; + } +#else + return Double.TryParse(s, style, provider, out result); +#endif + } + + public static bool TryParseInt32(string s, out int result) { +#if SILVERLIGHT // Int32.TryParse + try { + result = Int32.Parse(s); + return true; + } catch { + result = 0; + return false; + } +#else + return Int32.TryParse(s, out result); +#endif + } + + public static bool TryParseDateTimeExact(string s, string format, IFormatProvider provider, DateTimeStyles style, out DateTime result) { +#if SILVERLIGHT // DateTime.ParseExact + try { + result = DateTime.ParseExact(s, format, provider, style); + return true; + } catch { + result = DateTime.MinValue; + return false; + } +#else + return DateTime.TryParseExact(s, format, provider, style, out result); +#endif + } + + public static bool TryParseDate(string s, IFormatProvider provider, DateTimeStyles style, out DateTime result) { +#if SILVERLIGHT // DateTime.Parse + try { + result = DateTime.Parse(s, provider, style); + return true; + } catch { + result = DateTime.MinValue; + return false; + } +#else + return DateTime.TryParse(s, provider, style, out result); +#endif + } + +#if SILVERLIGHT + private static Dictionary _cultureInfoCache = new Dictionary(); +#endif + + // Aims to be equivalent to Culture.GetCultureInfo for Silverlight + public static CultureInfo GetCultureInfo(string name) { +#if SILVERLIGHT + lock (_cultureInfoCache) { + CultureInfo result; + if (_cultureInfoCache.TryGetValue(name, out result)) { + return result; + } + _cultureInfoCache[name] = result = new CultureInfo(name); + return result; + } +#else + return CultureInfo.GetCultureInfo(name); +#endif + } + + // Like string.Split, but enumerates + public static IEnumerable Split(string str, string sep) { + int start = 0, end; + while ((end = str.IndexOf(sep, start)) != -1) { + yield return str.Substring(start, end - start); + + start = end + sep.Length; + } + yield return str.Substring(start); + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/SynchronizedDictionary.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/SynchronizedDictionary.cs new file mode 100644 index 0000000000..70e9e04658 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/SynchronizedDictionary.cs @@ -0,0 +1,175 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; + +namespace Microsoft.Scripting.Utils { + + /// + /// Dictionary[TKey, TValue] is not thread-safe in the face of concurrent reads and writes. SynchronizedDictionary + /// provides a thread-safe implementation. It holds onto a Dictionary[TKey, TValue] instead of inheriting from + /// it so that users who need to do manual synchronization can access the underlying Dictionary[TKey, TValue]. + /// + internal class SynchronizedDictionary : + IDictionary, + ICollection>, + IEnumerable> { + + Dictionary _dictionary = new Dictionary(); + + /// + /// This returns the raw unsynchronized Dictionary[TKey, TValue]. Users are responsible for locking + /// on it before accessing it. Also, it should not be arbitrarily handed out to other code since deadlocks + /// can be caused if other code incorrectly locks on it. + /// + internal Dictionary UnderlyingDictionary { + get { + return _dictionary; + } + } + + #region IDictionary Members + + public void Add(TKey key, TValue value) { + lock (_dictionary) { + _dictionary.Add(key, value); + } + } + + public bool ContainsKey(TKey key) { + lock (_dictionary) { + return _dictionary.ContainsKey(key); + } + } + + public ICollection Keys { + get { + lock (_dictionary) { + return _dictionary.Keys; + } + } + } + + public bool Remove(TKey key) { + lock (_dictionary) { + return _dictionary.Remove(key); + } + } + + public bool TryGetValue(TKey key, out TValue value) { + lock (_dictionary) { + return _dictionary.TryGetValue(key, out value); + } + } + + public ICollection Values { + get { + lock (_dictionary) { + return _dictionary.Values; + } + } + } + + public TValue this[TKey key] { + get { + lock (_dictionary) { + return _dictionary[key]; + } + } + set { + lock (_dictionary) { + _dictionary[key] = value; + } + } + } + + #endregion + + #region ICollection> Members + + private ICollection> AsICollection() { + return ((ICollection>)_dictionary); + } + + public void Add(KeyValuePair item) { + lock (_dictionary) { + AsICollection().Add(item); + } + } + + public void Clear() { + lock (_dictionary) { + AsICollection().Clear(); + } + } + + public bool Contains(KeyValuePair item) { + lock (_dictionary) { + return AsICollection().Contains(item); + } + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) { + lock (_dictionary) { + AsICollection().CopyTo(array, arrayIndex); + } + } + + public int Count { + get { + lock (_dictionary) { + return AsICollection().Count; + } + } + } + + public bool IsReadOnly { + get { + lock (_dictionary) { + return AsICollection().IsReadOnly; + } + } + } + + public bool Remove(KeyValuePair item) { + lock (_dictionary) { + return AsICollection().Remove(item); + } + } + + #endregion + + #region IEnumerable> Members + + public IEnumerator> GetEnumerator() { + lock (_dictionary) { + return _dictionary.GetEnumerator(); + } + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() { + lock (_dictionary) { + return _dictionary.GetEnumerator(); + } + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/TextStream.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/TextStream.cs new file mode 100644 index 0000000000..3af9aa8153 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/TextStream.cs @@ -0,0 +1,140 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if SILVERLIGHT // only needed in SL, see SharedIO + +using System; +using System.IO; +using System.Text; + +namespace Microsoft.Scripting.Utils { + internal abstract class TextStreamBase : Stream { + + private readonly bool _buffered; + + protected TextStreamBase(bool buffered) { + _buffered = buffered; + } + + public abstract Encoding Encoding { get; } + public abstract TextReader Reader { get; } + public abstract TextWriter Writer { get; } + + public sealed override bool CanSeek { + get { return false; } + } + + public sealed override bool CanWrite { + get { return Writer != null; } + } + + public sealed override bool CanRead { + get { return Reader != null; } + } + + public sealed override void Flush() { + if (!CanWrite) throw new InvalidOperationException(); + Writer.Flush(); + } + + public sealed override int Read(byte[] buffer, int offset, int count) { + if (!CanRead) throw new InvalidOperationException(); + ContractUtils.RequiresArrayRange(buffer, offset, count, "offset", "count"); + + char[] charBuffer = new char[count]; + int realCount = Reader.Read(charBuffer, 0, count); + return Encoding.GetBytes(charBuffer, 0, realCount, buffer, offset); + } + + public sealed override void Write(byte[] buffer, int offset, int count) { + ContractUtils.RequiresArrayRange(buffer, offset, count, "offset", "count"); + char[] charBuffer = Encoding.GetChars(buffer, offset, count); + Writer.Write(charBuffer, 0, charBuffer.Length); + if (!_buffered) Writer.Flush(); + } + +#region Invalid Operations + + public sealed override long Length { + get { + throw new InvalidOperationException(); + } + } + + public sealed override long Position { + get { + throw new InvalidOperationException(); + } + set { + throw new InvalidOperationException(); + } + } + + public sealed override long Seek(long offset, SeekOrigin origin) { + throw new InvalidOperationException(); + } + + public sealed override void SetLength(long value) { + throw new InvalidOperationException(); + } + +#endregion + } + + internal sealed class TextStream : TextStreamBase { + + private readonly TextReader _reader; + private readonly TextWriter _writer; + private readonly Encoding _encoding; + + public override Encoding Encoding { + get { return _encoding; } + } + + public override TextReader Reader { + get { return _reader; } + } + + public override TextWriter Writer { + get { return _writer; } + } + + internal TextStream(TextReader reader, Encoding encoding) + : base(true) { + ContractUtils.RequiresNotNull(reader, "reader"); + ContractUtils.RequiresNotNull(encoding, "encoding"); + + _reader = reader; + _encoding = encoding; + } + + internal TextStream(TextWriter writer) + : this(writer, writer.Encoding, true) { + } + + internal TextStream(TextWriter writer, Encoding encoding, bool buffered) + : base(buffered) { + ContractUtils.RequiresNotNull(writer, "writer"); + ContractUtils.RequiresNotNull(encoding, "encoding"); + + _writer = writer; + _encoding = encoding; + } + } + + +} + +#endif diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ThreadLocal.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ThreadLocal.cs new file mode 100644 index 0000000000..713ada9233 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ThreadLocal.cs @@ -0,0 +1,184 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Diagnostics; + +namespace Microsoft.Scripting.Utils { + /// + /// Provides fast strongly typed thread local storage. This is significantly faster than + /// Thread.GetData/SetData. + /// + public class ThreadLocal { + private StorageInfo[] _stores; // array of storage indexed by managed thread ID + private static readonly StorageInfo[] Updating = new StorageInfo[0]; // a marker used when updating the array + + #region Public API + + /// + /// Gets or sets the value for the current thread. + /// + public T Value { + get { + return GetStorageInfo().Value; + } + set { + GetStorageInfo().Value = value; + } + } + + /// + /// Gets the current value if its not == null or calls the provided function + /// to create a new value. + /// + public T GetOrCreate(Func func) { + Assert.NotNull(func); + + StorageInfo si = GetStorageInfo(); + T res = si.Value; + if (res == null) { + si.Value = res = func(); + } + + return res; + } + + /// + /// Calls the provided update function with the current value and + /// replaces the current value with the result of the function. + /// + public T Update(Func updater) { + Assert.NotNull(updater); + + StorageInfo si = GetStorageInfo(); + return si.Value = updater(si.Value); + } + + #endregion + + #region Storage implementation + + /// + /// Gets the StorageInfo for the current thread. + /// + private StorageInfo GetStorageInfo() { + return GetStorageInfo(_stores); + } + + private StorageInfo GetStorageInfo(StorageInfo[] curStorage) { + int threadId = Thread.CurrentThread.ManagedThreadId; + + // fast path if we already have a value in the array + if (curStorage != null && curStorage.Length > threadId) { + StorageInfo res = curStorage[threadId]; + + if (res != null && res.Thread == Thread.CurrentThread) { + return res; + } + } + + return RetryOrCreateStorageInfo(curStorage); + } + + /// + /// Called when the fast path storage lookup fails. if we encountered the Empty storage + /// during the initial fast check then spin until we hit non-empty storage and try the fast + /// path again. + /// + private StorageInfo RetryOrCreateStorageInfo(StorageInfo[] curStorage) { + if (curStorage == Updating) { + // we need to retry + while ((curStorage = _stores) == Updating) { + Thread.Sleep(0); + } + + // we now have a non-empty storage info to retry with + return GetStorageInfo(curStorage); + } + + // we need to mutator the StorageInfo[] array or create a new StorageInfo + return CreateStorageInfo(); + } + + /// + /// Creates the StorageInfo for the thread when one isn't already present. + /// + private StorageInfo CreateStorageInfo() { + // we do our own locking, tell hosts this is a bad time to interrupt us. +#if !SILVERLIGHT + Thread.BeginCriticalRegion(); +#endif + StorageInfo[] curStorage = Updating; + try { + int threadId = Thread.CurrentThread.ManagedThreadId; + StorageInfo newInfo = new StorageInfo(Thread.CurrentThread); + + // set to updating while potentially resizing/mutating, then we'll + // set back to the current value. + while ((curStorage = Interlocked.Exchange(ref _stores, Updating)) == Updating) { + // another thread is already updating... + Thread.Sleep(0); + } + + // check and make sure we have a space in the array for our value + if (curStorage == null) { + curStorage = new StorageInfo[threadId + 1]; + } else if (curStorage.Length <= threadId) { + StorageInfo[] newStorage = new StorageInfo[threadId + 1]; + for (int i = 0; i < curStorage.Length; i++) { + // leave out the threads that have exited + if (curStorage[i] != null && curStorage[i].Thread.IsAlive) { + newStorage[i] = curStorage[i]; + } + } + curStorage = newStorage; + } + + // create our StorageInfo in the array, the empty check ensures we're only here + // when we need to create. + Debug.Assert(curStorage[threadId] == null || curStorage[threadId].Thread != Thread.CurrentThread); + + return curStorage[threadId] = newInfo; + } finally { + if (curStorage != Updating) { + // let others access the storage again + Interlocked.Exchange(ref _stores, curStorage); + } +#if !SILVERLIGHT + Thread.EndCriticalRegion(); +#endif + } + } + + /// + /// Helper class for storing the value. We need to track if a ManagedThreadId + /// has been re-used so we also store the thread which owns the value. + /// + private class StorageInfo { + public readonly Thread Thread; // the thread that owns the StorageInfo + public T Value; // the current value for the owning thread + + public StorageInfo(Thread curThread) { + Assert.NotNull(curThread); + + Thread = curThread; + } + } + + #endregion + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/TypeExtensions.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/TypeExtensions.cs new file mode 100644 index 0000000000..50b8cd8043 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/TypeExtensions.cs @@ -0,0 +1,194 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Text; +using System.Reflection.Emit; +using System.Diagnostics; + +namespace Microsoft.Scripting.Utils { + + // Extensions on System.Type and friends + internal static class TypeExtensions { + + /// + /// Creates an open delegate for the given (dynamic)method. + /// + internal static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType) { + Debug.Assert(methodInfo != null && delegateType != null); + + var dm = methodInfo as DynamicMethod; + if (dm != null) { + return dm.CreateDelegate(delegateType); + } else { + return Delegate.CreateDelegate(delegateType, methodInfo); + } + } + + /// + /// Creates a closed delegate for the given (dynamic)method. + /// + internal static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType, object target) { + Debug.Assert(methodInfo != null && delegateType != null); + + var dm = methodInfo as DynamicMethod; + if (dm != null) { + return dm.CreateDelegate(delegateType, target); + } else { + return Delegate.CreateDelegate(delegateType, target, methodInfo); + } + } + + internal static T CreateDelegate(this MethodInfo methodInfo) { + return (T)(object)methodInfo.CreateDelegate(typeof(T)); + } + internal static T CreateDelegate(this MethodInfo methodInfo, object target) { + return (T)(object)methodInfo.CreateDelegate(typeof(T), target); + } + + + internal static Type GetReturnType(this MethodBase mi) { + return (mi.IsConstructor) ? mi.DeclaringType : ((MethodInfo)mi).ReturnType; + } + + /// + /// A helper routine to check if a type can be treated as sealed - i.e. there + /// can never be a subtype of this given type. This corresponds to a type + /// that is either declared "Sealed" or is a ValueType and thus unable to be + /// extended. + /// + /// TODO: this should not be needed. Type.IsSealed does the right thing. + /// + internal static bool IsSealedOrValueType(this Type type) { + return type.IsSealed || type.IsValueType; + } + + internal static bool IsParamArray(this ParameterInfo parameter) { + return parameter.IsDefined(typeof(ParamArrayAttribute), false); + } + + internal static bool IsOutParameter(this ParameterInfo pi) { + // not using IsIn/IsOut properties as they are not available in Silverlight: + return (pi.Attributes & (ParameterAttributes.Out | ParameterAttributes.In)) == ParameterAttributes.Out; + } + + /// + /// Returns true if the specified parameter is mandatory, i.e. is not optional and doesn't have a default value. + /// + internal static bool IsMandatoryParameter(this ParameterInfo pi) { + return (pi.Attributes & (ParameterAttributes.Optional | ParameterAttributes.HasDefault)) == 0; + } + + internal static bool HasDefaultValue(this ParameterInfo pi) { + return (pi.Attributes & ParameterAttributes.HasDefault) != 0; + } + + internal static bool IsByRefParameter(this ParameterInfo pi) { + // not using IsIn/IsOut properties as they are not available in Silverlight: + if (pi.ParameterType.IsByRef) return true; + + return (pi.Attributes & (ParameterAttributes.Out)) == ParameterAttributes.Out; + } + + internal static string FormatTypeName(this Type type) { + Debug.Assert(type != null); + var str = new StringBuilder(); + FormatTypeName(str, type); + return str.ToString(); + } + + internal static string FormatSignature(this MethodBase method) { + var str = new StringBuilder(); + FormatSignature(str, method); + return str.ToString(); + } + + private static void FormatSignature(StringBuilder result, MethodBase method) { + ContractUtils.RequiresNotNull(result, "result"); + ContractUtils.RequiresNotNull(method, "method"); + + MethodInfo methodInfo = method as MethodInfo; + if (methodInfo != null) { + FormatTypeName(result, methodInfo.ReturnType); + result.Append(' '); + } + + MethodBuilder builder = method as MethodBuilder; + if (builder != null) { + result.Append(builder.Signature); + return; + } + + ConstructorBuilder cb = method as ConstructorBuilder; + if (cb != null) { + result.Append(cb.Signature); + return; + } + + FormatTypeName(result, method.DeclaringType); + result.Append("::"); + result.Append(method.Name); + + if (!method.IsConstructor) { + FormatTypeArgs(result, method.GetGenericArguments()); + } + + result.Append("("); + + if (!method.ContainsGenericParameters) { + ParameterInfo[] ps = method.GetParameters(); + for (int i = 0; i < ps.Length; i++) { + if (i > 0) result.Append(", "); + FormatTypeName(result, ps[i].ParameterType); + if (!System.String.IsNullOrEmpty(ps[i].Name)) { + result.Append(" "); + result.Append(ps[i].Name); + } + } + } else { + result.Append("?"); + } + + result.Append(")"); + } + + private static void FormatTypeName(StringBuilder result, Type type) { + if (type.IsGenericType) { + string genericName = type.GetGenericTypeDefinition().FullName.Replace('+', '.'); + int tickIndex = genericName.IndexOf('`'); + result.Append(tickIndex != -1 ? genericName.Substring(0, tickIndex) : genericName); + FormatTypeArgs(result, type.GetGenericArguments()); + } else if (type.IsGenericParameter) { + result.Append(type.Name); + } else { + result.Append(type.FullName.Replace('+', '.')); + } + } + + private static void FormatTypeArgs(StringBuilder result, Type[] types) { + if (types.Length > 0) { + result.Append("<"); + + for (int i = 0; i < types.Length; i++) { + if (i > 0) result.Append(", "); + FormatTypeName(result, types[i]); + } + + result.Append(">"); + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/TypeUtils.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/TypeUtils.cs new file mode 100644 index 0000000000..7b14c40214 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/TypeUtils.cs @@ -0,0 +1,327 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic; +using Microsoft.Scripting.Generation; + +namespace Microsoft.Scripting.Utils { + static class TypeUtils { + internal static Type GetNonNullableType(Type type) { + if (IsNullableType(type)) { + return type.GetGenericArguments()[0]; + } + return type; + } + + internal static bool IsNullableType(Type type) { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + internal static bool IsBool(Type type) { + return GetNonNullableType(type) == typeof(bool); + } + + internal static bool IsNumeric(Type type) { + type = GetNonNullableType(type); + if (!type.IsEnum) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Char: + case TypeCode.SByte: + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Double: + case TypeCode.Single: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + return true; + } + } + return false; + } + + internal static bool IsArithmetic(Type type) { + type = GetNonNullableType(type); + if (!type.IsEnum) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Double: + case TypeCode.Single: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + return true; + } + } + return false; + } + + internal static bool IsUnsigned(Type type) { + type = GetNonNullableType(type); + if (!type.IsEnum) { + switch (Type.GetTypeCode(type)) { + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + return true; + } + } + return false; + } + + internal static bool IsIntegerOrBool(Type type) { + type = GetNonNullableType(type); + if (!type.IsEnum) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Int64: + case TypeCode.Int32: + case TypeCode.Int16: + case TypeCode.UInt64: + case TypeCode.UInt32: + case TypeCode.UInt16: + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Byte: + return true; + } + } + return false; + } + + internal static bool CanAssign(Type to, Expression from) { + if (CanAssign(to, from.Type)) return true; + + if (to.IsValueType && + to.IsGenericType && + to.GetGenericTypeDefinition() == typeof(Nullable<>) && + ConstantCheck.Check(from, null)) { + return true; + } + + return false; + } + + internal static bool CanAssign(Type to, Type from) { + if (to == from) { + return true; + } + // Reference types + if (!to.IsValueType && !from.IsValueType) { + if (to.IsAssignableFrom(from)) { + return true; + } + // Arrays can be assigned if they have same rank and assignable element types. + if (to.IsArray && from.IsArray && + to.GetArrayRank() == from.GetArrayRank() && + CanAssign(to.GetElementType(), from.GetElementType())) { + return true; + } + } + + return false; + } + + internal static bool IsGeneric(Type type) { + return type.ContainsGenericParameters || type.IsGenericTypeDefinition; + } + + internal static bool CanCompareToNull(Type type) { + // This is a bit too conservative. + return !type.IsValueType; + } + + /// + /// Returns a numerical code of the size of a type. All types get both a horizontal + /// and vertical code. Types that are lower in both dimensions have implicit conversions + /// to types that are higher in both dimensions. + /// + internal static bool GetNumericConversionOrder(TypeCode code, out int x, out int y) { + // implicit conversions: + // 0 1 2 3 4 + // 0: U1 -> U2 -> U4 -> U8 + // | | | + // v v v + // 1: I1 -> I2 -> I4 -> I8 + // | | + // v v + // 2: R4 -> R8 + + switch (code) { + case TypeCode.Byte: x = 0; y = 0; break; + case TypeCode.UInt16: x = 1; y = 0; break; + case TypeCode.UInt32: x = 2; y = 0; break; + case TypeCode.UInt64: x = 3; y = 0; break; + + case TypeCode.SByte: x = 0; y = 1; break; + case TypeCode.Int16: x = 1; y = 1; break; + case TypeCode.Int32: x = 2; y = 1; break; + case TypeCode.Int64: x = 3; y = 1; break; + + case TypeCode.Single: x = 1; y = 2; break; + case TypeCode.Double: x = 2; y = 2; break; + + default: + x = y = 0; + return false; + } + return true; + } + + internal static bool IsImplicitlyConvertible(int fromX, int fromY, int toX, int toY) { + return fromX <= toX && fromY <= toY; + } + + internal static bool HasBuiltinEquality(Type left, Type right) { + // Reference type can be compared to interfaces + if (left.IsInterface && !right.IsValueType || + right.IsInterface && !left.IsValueType) { + return true; + } + + // Reference types compare if they are assignable + if (!left.IsValueType && !right.IsValueType) { + if (CanAssign(left, right) || CanAssign(right, left)) { + return true; + } + } + + // Nullable vs null + if (NullVsNullable(left, right) || NullVsNullable(right, left)) { + return true; + } + + if (left != right) { + return false; + } + + if (left == typeof(bool) || IsNumeric(left) || left.IsEnum) { + return true; + } + + return false; + } + + private static bool NullVsNullable(Type left, Type right) { + return IsNullableType(left) && right == typeof(Null); + } + + // keep in sync with System.Core version + internal static bool AreReferenceAssignable(Type dest, Type src) { + // WARNING: This actually implements "Is this identity assignable and/or reference assignable?" + if (dest == src) { + return true; + } + if (!dest.IsValueType && !src.IsValueType && AreAssignable(dest, src)) { + return true; + } + return false; + } + + // keep in sync with System.Core version + internal static bool AreAssignable(Type dest, Type src) { + if (dest == src) { + return true; + } + if (dest.IsAssignableFrom(src)) { + return true; + } + if (dest.IsArray && src.IsArray && dest.GetArrayRank() == src.GetArrayRank() && AreReferenceAssignable(dest.GetElementType(), src.GetElementType())) { + return true; + } + if (src.IsArray && dest.IsGenericType && + (dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IEnumerable<>) + || dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IList<>) + || dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.ICollection<>)) + && dest.GetGenericArguments()[0] == src.GetElementType()) { + return true; + } + return false; + } + + // keep in sync with System.Core version + internal static Type GetConstantType(Type type) { + // If it's a visible type, we're done + if (type.IsVisible) { + return type; + } + + // Get the visible base type + Type bt = type; + do { + bt = bt.BaseType; + } while (!bt.IsVisible); + + // If it's one of the known reflection types, + // return the known type. + if (bt == typeof(Type) || + bt == typeof(ConstructorInfo) || + bt == typeof(EventInfo) || + bt == typeof(FieldInfo) || + bt == typeof(MethodInfo) || + bt == typeof(PropertyInfo)) { + return bt; + } + + // else return the original type + return type; + } + + internal static bool IsConvertible(Type type) { + type = GetNonNullableType(type); + if (type.IsEnum) { + return true; + } + switch (Type.GetTypeCode(type)) { + case TypeCode.Boolean: + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Char: + return true; + default: + return false; + } + } + + internal static Type GetNonNoneType(Type type) { + return (type == typeof(Null)) ? typeof(object) : type; + } + + internal static bool IsFloatingPoint(Type type) { + type = GetNonNullableType(type); + switch (Type.GetTypeCode(type)) { + case TypeCode.Single: + case TypeCode.Double: + return true; + default: + return false; + } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/ValueArray.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/ValueArray.cs new file mode 100644 index 0000000000..269fc3d47a --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/ValueArray.cs @@ -0,0 +1,69 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Utils { + + /// + /// Represents an array that has value equality. + /// + public class ValueArray : IEquatable> { + private readonly T[] _array; + + public ValueArray(T[] array) { + ContractUtils.RequiresNotNull(array, "array"); + ContractUtils.RequiresNotNullItems(array, "array"); + _array = array; + } + + #region IEquatable> Members + + [StateIndependent] + public bool Equals(ValueArray other) { + if (other == null) return false; + + if (other._array.Length != _array.Length) { + return false; + } + + for (int i = 0; i < _array.Length; i++) { + if (!_array[i].Equals(other._array[i])) { + return false; + } + } + + return true; + } + + #endregion + + [Confined] + public override bool Equals(object obj) { + return Equals(obj as ValueArray); + } + + [Confined] + public override int GetHashCode() { + int val = 6551; + + for (int i = 0; i < _array.Length; i++) { + val ^= _array[i].GetHashCode(); + } + return val; + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/WeakDictionary.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/WeakDictionary.cs new file mode 100644 index 0000000000..8e17c08687 --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/WeakDictionary.cs @@ -0,0 +1,397 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Contracts; + +namespace Microsoft.Scripting.Utils { + /// + /// Similar to Dictionary[TKey,TValue], but it also ensures that the keys will not be kept alive + /// if the only reference is from this collection. The value will be kept alive as long as the key + /// is alive. + /// + /// This currently has a limitation that the caller is responsible for ensuring that an object used as + /// a key is not also used as a value in *any* instance of a WeakHash. Otherwise, it will result in the + /// object being kept alive forever. This effectively means that the owner of the WeakHash should be the + /// only one who has access to the object used as a value. + /// + /// Currently, there is also no guarantee of how long the values will be kept alive even after the keys + /// get collected. This could be fixed by triggerring CheckCleanup() to be called on every garbage-collection + /// by having a dummy watch-dog object with a finalizer which calls CheckCleanup(). + /// + public class WeakDictionary : IDictionary { + // The one and only comparer instance. + static readonly IEqualityComparer comparer = new WeakComparer(); + + IDictionary dict = new Dictionary(comparer); + int version, cleanupVersion; + +#if SILVERLIGHT // GC + WeakReference cleanupGC = new WeakReference(new object()); +#else + int cleanupGC = 0; +#endif + + public WeakDictionary() { + } + + #region IDictionary Members + + public void Add(TKey key, TValue value) { + CheckCleanup(); + + // If the WeakHash already holds this value as a key, it will lead to a circular-reference and result + // in the objects being kept alive forever. The caller needs to ensure that this cannot happen. + Debug.Assert(!dict.ContainsKey(value)); + + dict.Add(new WeakObject(key), value); + } + + [Confined] + public bool ContainsKey(TKey key) { + // We dont have to worry about creating "new WeakObject(key)" since the comparer + // can compare raw objects with WeakObject. + return dict.ContainsKey(key); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix + public ICollection Keys { + get { + // TODO: + throw new NotImplementedException(); + } + } + + public bool Remove(TKey key) { + return dict.Remove(key); + } + + public bool TryGetValue(TKey key, out TValue value) { + return dict.TryGetValue(key, out value); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix + public ICollection Values { + get { + // TODO: + throw new NotImplementedException(); + } + } + + public TValue this[TKey key] { + get { + return dict[key]; + } + set { + // If the WeakHash already holds this value as a key, it will lead to a circular-reference and result + // in the objects being kept alive forever. The caller needs to ensure that this cannot happen. + Debug.Assert(!dict.ContainsKey(value)); + + dict[new WeakObject(key)] = value; + } + } + + /// + /// Check if any of the keys have gotten collected + /// + /// Currently, there is also no guarantee of how long the values will be kept alive even after the keys + /// get collected. This could be fixed by triggerring CheckCleanup() to be called on every garbage-collection + /// by having a dummy watch-dog object with a finalizer which calls CheckCleanup(). + /// + void CheckCleanup() { + version++; + + long change = version - cleanupVersion; + + // Cleanup the table if it is a while since we have done it last time. + // Take the size of the table into account. + if (change > 1234 + dict.Count / 2) { + // It makes sense to do the cleanup only if a GC has happened in the meantime. + // WeakReferences can become zero only during the GC. + + bool garbage_collected; +#if SILVERLIGHT // GC.CollectionCount + garbage_collected = !cleanupGC.IsAlive; + if (garbage_collected) cleanupGC = new WeakReference(new object()); +#else + int currentGC = GC.CollectionCount(0); + garbage_collected = currentGC != cleanupGC; + if (garbage_collected) cleanupGC = currentGC; +#endif + if (garbage_collected) { + Cleanup(); + cleanupVersion = version; + } else { + cleanupVersion += 1234; + } + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")] + private void Cleanup() { + + int liveCount = 0; + int emptyCount = 0; + + foreach (WeakObject w in dict.Keys) { + if (w.Target != null) + liveCount++; + else + emptyCount++; + } + + // Rehash the table if there is a significant number of empty slots + if (emptyCount > liveCount / 4) { + Dictionary newtable = new Dictionary(liveCount + liveCount / 4, comparer); + + foreach (WeakObject w in dict.Keys) { + object target = w.Target; + + if (target != null) + newtable[w] = dict[w]; + + GC.KeepAlive(target); + } + + dict = newtable; + } + } + #endregion + + #region ICollection> Members + + public void Add(KeyValuePair item) { + // TODO: + throw new NotImplementedException(); + } + + public void Clear() { + // TODO: + throw new NotImplementedException(); + } + + [Confined] + public bool Contains(KeyValuePair item) { + // TODO: + throw new NotImplementedException(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) { + // TODO: + throw new NotImplementedException(); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix + public int Count { + get { + // TODO: + throw new NotImplementedException(); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix + public bool IsReadOnly { + get { + // TODO: + throw new NotImplementedException(); + } + } + + public bool Remove(KeyValuePair item) { + // TODO: + throw new NotImplementedException(); + } + + #endregion + + #region IEnumerable> Members + + [Pure] + public IEnumerator> GetEnumerator() { + // TODO: + throw new NotImplementedException(); + } + + #endregion + + #region IEnumerable Members + + [Pure] + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + // TODO: + throw new NotImplementedException(); + } + + #endregion + } + + internal class WeakObject { + WeakReference weakReference; + int hashCode; + + public WeakObject(T obj) { + weakReference = new WeakReference(obj, true); + hashCode = (obj == null) ? 0 : obj.GetHashCode(); + } + + public T Target { + get { + return (T)weakReference.Target; + } + } + + [Confined] + public override int GetHashCode() { + return hashCode; + } + + [Confined] + public override bool Equals(object obj) { + object target = weakReference.Target; + if (target == null) { + return false; + } + + return ((T)target).Equals(obj); + } + } + + // WeakComparer treats WeakObject as transparent envelope + sealed class WeakComparer : IEqualityComparer { + bool IEqualityComparer.Equals(T x, T y) { + WeakObject wx = x as WeakObject; + if (wx != null) + x = wx.Target; + + WeakObject wy = y as WeakObject; + if (wy != null) + y = wy.Target; + + return Object.Equals(x, y); + } + + int IEqualityComparer.GetHashCode(T obj) { + WeakObject wobj = obj as WeakObject; + if (wobj != null) + return wobj.GetHashCode(); + + return (obj == null) ? 0 : obj.GetHashCode(); + } + } + + public sealed class HybridMapping { + private Dictionary _dict = new Dictionary(); + private readonly Object _synchObject = new Object(); + private readonly int _offset; + private int _current; + + private const int SIZE = 4096; + private const int MIN_RANGE = SIZE / 2; + + public HybridMapping() : this(0) { + } + + public HybridMapping(int offset) { + if (offset < 0 || (SIZE - offset) < MIN_RANGE) { + throw new InvalidOperationException("HybridMapping is full"); + } + _offset = offset; + _current = offset; + } + + private void NextKey() { + if (++_current >= SIZE) { + _current = _offset; + } + } + + public int WeakAdd(T value) { + lock (_synchObject) { + int saved = _current; + while (_dict.ContainsKey(_current)) { + NextKey(); + if (_current == saved) + throw new InvalidOperationException("HybridMapping is full"); + } + _dict.Add(_current, new WeakObject(value)); + return _current; + } + } + + public int StrongAdd(T value) { + lock (_synchObject) { + int saved = _current; + while (_dict.ContainsKey(_current)) { + NextKey(); + if (_current == saved) + throw new InvalidOperationException("HybridMapping is full"); + } + _dict.Add(_current, value); + return _current; + } + } + + public T GetObjectFromId(int id) { + object ret; + if (_dict.TryGetValue(id, out ret)) { + WeakObject weakObj = ret as WeakObject; + if (weakObj != null) { + return weakObj.Target; + } + if (ret is T) { + return (T)ret; + } + + throw new InvalidOperationException("Unexpected dictionary content: type " + ret.GetType()); + } else + return default(T); + } + + public int GetIdFromObject(T value) { + lock (_synchObject) { + foreach (KeyValuePair kv in _dict) { + if (kv.Value is WeakObject) { + object target = ((WeakObject)kv.Value).Target; + if (target != null && target.Equals(value)) + return kv.Key; + } else if (kv.Value is T) { + object target = (T)(kv.Value); + if (target.Equals(value)) + return kv.Key; + } + } + } + return -1; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] // TODO: fix (rename?) + public void RemoveOnId(int id) { + lock (_synchObject) { + _dict.Remove(id); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] // TODO: fix + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] // TODO: fix (rename?) + public void RemoveOnObject(T value) { + try { + int id = GetIdFromObject(value); + RemoveOnId(id); + } catch { } + } + } +} diff --git a/merlin/main/runtime/Microsoft.Scripting/Utils/WeakHandle.cs b/merlin/main/runtime/Microsoft.Scripting/Utils/WeakHandle.cs new file mode 100644 index 0000000000..795325d87d --- /dev/null +++ b/merlin/main/runtime/Microsoft.Scripting/Utils/WeakHandle.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.Scripting.Utils { + +#if SILVERLIGHT + // TODO: finalizers in user types aren't supported in Silverlight + // we need to come up with another solution for Python's _weakref library + public struct WeakHandle { + private WeakReference _weakRef; + + public WeakHandle(object target, bool trackResurrection) { + _weakRef = new WeakReference(target, trackResurrection); + GC.SuppressFinalize(this._weakRef); + } + + public bool IsAlive { get { return _weakRef != null && _weakRef.IsAlive; } } + public object Target { get { return _weakRef != null ? _weakRef.Target : null; } } + + public void Free() { + if (_weakRef != null) { + GC.ReRegisterForFinalize(_weakRef); + _weakRef.Target = null; + _weakRef = null; + } + } + } +#else + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] // TODO: fix + public struct WeakHandle { + + private GCHandle weakRef; + + public WeakHandle(object target, bool trackResurrection) { + this.weakRef = GCHandle.Alloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak); + } + + public bool IsAlive { get { return weakRef.IsAllocated; } } + public object Target { get { return weakRef.Target; } } + public void Free() { weakRef.Free(); } + } +#endif +} diff --git a/ndp/fx/src/DynamicCom/GlobalSuppressions.cs b/ndp/fx/src/DynamicCom/GlobalSuppressions.cs new file mode 100644 index 0000000000..49e062dcfa --- /dev/null +++ b/ndp/fx/src/DynamicCom/GlobalSuppressions.cs @@ -0,0 +1,17 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. +// +// To add a suppression to this file, right-click the message in the +// Error List, point to "Suppress Message(s)", and click +// "In Project Suppression File". +// You do not need to add suppressions to this file manually. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Dynamic.ComInterop.SR.#GetObject(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Dynamic.ComInterop.SR.#GetString(System.String,System.Boolean&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Dynamic.ComInterop.SR.#Resources")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Dynamic.ComInterop.Error.#ArgumentNull(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Dynamic.ComInterop.Error.#ArgumentOutOfRange(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Dynamic.ComInterop.Error.#NotImplemented()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Dynamic.ComInterop.Error.#NotSupported()")] \ No newline at end of file diff --git a/ndp/fx/src/DynamicCom/System.Dynamic.ComInterop.asmmeta b/ndp/fx/src/DynamicCom/System.Dynamic.ComInterop.asmmeta new file mode 100644 index 0000000000..deb414c9de --- /dev/null +++ b/ndp/fx/src/DynamicCom/System.Dynamic.ComInterop.asmmeta @@ -0,0 +1,3144 @@ +#include "versions.h" +#include "ndp40.versions.h" + +.assembly extern System +{ + .publickeytoken = SYSTEM_ASSEMBLY_PUBLIC_KEY_TOKEN_IL + .ver SYSTEM_ASSEMBLY_VERSION_IL +} +.assembly extern System.Core +{ + .publickeytoken = SYSTEM_CORE_ASSEMBLY_PUBLIC_KEY_TOKEN_IL + .ver SYSTEM_CORE_ASSEMBLY_VERSION_IL +} +.assembly extern mscorlib +{ + .publickeytoken = MSCORLIB_ASSEMBLY_PUBLIC_KEY_TOKEN_IL + .ver MSCORLIB_ASSEMBLY_VERSION_IL +} +.assembly System.Dynamic.ComInterop +{ + .custom instance void [mscorlib]System.CLSCompliantAttribute::.ctor(bool) = { bool(true) } + .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = { bool(false) } + .custom instance void [mscorlib]System.Security.AllowPartiallyTrustedCallersAttribute::.ctor() = { } + .permissionset reqmin = (2E 01 80 84 53 79 73 74 65 6D 2E 53 65 63 75 72 69 74 79 2E 50 65 72 6D 69 73 73 69 6F 6E 73 2E 53 65 63 75 72 69 74 79 50 65 72 6D 69 73 73 69 6F 6E 41 74 74 72 69 62 75 74 65 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 15 01 54 02 10 53 6B 69 70 56 65 72 69 66 69 63 61 74 69 6F 6E 01) + .publickey = SYSTEM_DYNAMIC_COMINTEROP_ASSEMBLY_PUBLIC_KEY_IL + .hash algorithm 0x00008004 + .ver SYSTEM_DYNAMIC_COMINTEROP_ASSEMBLY_VERSION_IL +} +.class private abstract sealed AssemblyRef + extends [mscorlib]System.Object +{ + .field static assembly literal string ASPBrowserCapsFactory = "ASP.BrowserCapsFactory, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b7bd7678b977bd8f" + .field static assembly literal string ASPBrowserCapsPublicKey = "b7bd7678b977bd8f" + .field static assembly literal string EcmaPublicKey = "b77a5c561934e089" + .field static assembly literal string EcmaPublicKeyFull = "00000000000000000400000000000000" + .field static assembly literal string EcmaPublicKeyToken = "b77a5c561934e089" + .field static assembly literal string MicrosoftJScript = "Microsoft.JScript, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string MicrosoftPublicKey = "b03f5f7f11d50a3a" + .field static assembly literal string MicrosoftPublicKeyFull = "002400000480000094000000060200000024000052534131000400000100010007D1FA57C4AED9F0A32E84AA0FAEFD0DE9E8FD6AEC8F87FB03766C834C99921EB23BE79AD9D5DCC1DD9AD236132102900B723CF980957FC4E177108FC607774F29E8320E92EA05ECE4E821C0A5EFE8F1645C4C0C93C1AB99285D622CAA652C1DFAD63D745D6F2DE5F17E5EAF0FC4963D261C8A12436518206DC093344D5AD293" + .field static assembly literal string MicrosoftPublicKeyToken = "b03f5f7f11d50a3a" + .field static assembly literal string MicrosoftVSDesigner = "Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string MicrosoftVSDesignerMobile = "Microsoft.VSDesigner.Mobile, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string MicrosoftVisualStudio = "Microsoft.VisualStudio, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string MicrosoftVisualStudioWeb = "Microsoft.VisualStudio.Web, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string MicrosoftVisualStudioWindowsForms = "Microsoft.VisualStudio.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string MicrosoftWebDesign = "Microsoft.Web.Design.Client, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string Mscorlib = "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" + .field static assembly literal string PlatformPublicKey = "b77a5c561934e089" + .field static assembly literal string PlatformPublicKeyFull = "00000000000000000400000000000000" + .field static assembly literal string PlatformPublicKeyToken = "b77a5c561934e089" + .field static assembly literal string SharedLibPublicKey = "31bf3856ad364e35" + .field static assembly literal string SharedLibPublicKeyFull = "0024000004800000940000000602000000240000525341310004000001000100B5FC90E7027F67871E773A8FDE8938C81DD402BA65B9201D60593E96C492651E889CC13F1415EBB53FAC1131AE0BD333C5EE6021672D9718EA31A8AEBD0DA0072F25D87DBA6FC90FFD598ED4DA35E44C398C454307E8E33B8426143DAEC9F596836F97C8F74750E5975C64E2189F45DEF46B2A2B1247ADC3652BF5C308055DA9" + .field static assembly literal string SharedLibPublicKeyToken = "31bf3856ad364e35" + .field static assembly literal string SilverlightPlatformPublicKey = "7cec85d7bea7798e" + .field static assembly literal string SilverlightPlatformPublicKeyFull = "00240000048000009400000006020000002400005253413100040000010001008D56C76F9E8649383049F383C44BE0EC204181822A6C31CF5EB7EF486944D032188EA1D3920763712CCB12D75FB77E9811149E6148E5D32FBAAB37611C1878DDC19E20EF135D0CB2CFF2BFEC3D115810C3D9069638FE4BE215DBF795861920E5AB6F7DB2E2CEEF136AC23D5DD2BF031700AEC232F6C6B1C785B4305C123B37AB" + .field static assembly literal string SilverlightPlatformPublicKeyToken = "7cec85d7bea7798e" + .field static assembly literal string SilverlightPublicKey = "31bf3856ad364e35" + .field static assembly literal string SilverlightPublicKeyFull = "0024000004800000940000000602000000240000525341310004000001000100B5FC90E7027F67871E773A8FDE8938C81DD402BA65B9201D60593E96C492651E889CC13F1415EBB53FAC1131AE0BD333C5EE6021672D9718EA31A8AEBD0DA0072F25D87DBA6FC90FFD598ED4DA35E44C398C454307E8E33B8426143DAEC9F596836F97C8F74750E5975C64E2189F45DEF46B2A2B1247ADC3652BF5C308055DA9" + .field static assembly literal string SilverlightPublicKeyToken = "31bf3856ad364e35" + .field static assembly literal string System = "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" + .field static assembly literal string SystemComponentModelDataAnnotations = "System.ComponentModel.DataAnnotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" + .field static assembly literal string SystemConfiguration = "System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemConfigurationInstall = "System.Configuration.Install, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemCore = "System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" + .field static assembly literal string SystemData = "System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" + .field static assembly literal string SystemDataOracleClient = "System.Data.OracleClient, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" + .field static assembly literal string SystemDeployment = "System.Deployment, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemDesign = "System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemDirectoryServices = "System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemDrawing = "System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemDrawingDesign = "System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemEnterpriseServices = "System.EnterpriseServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemManagement = "System.Management, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemMessaging = "System.Messaging, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemRuntimeRemoting = "System.Runtime.Remoting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" + .field static assembly literal string SystemRuntimeSerializationFormattersSoap = "System.Runtime.Serialization.Formatters.Soap, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemSecurity = "System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemServiceProcess = "System.ServiceProcess, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemWeb = "System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemWebAbstractions = "System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" + .field static assembly literal string SystemWebDynamicData = "System.Web.DynamicData, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" + .field static assembly literal string SystemWebDynamicDataDesign = "System.Web.DynamicData.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" + .field static assembly literal string SystemWebEntityDesign = "System.Web.Entity.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" + .field static assembly literal string SystemWebExtensions = "System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" + .field static assembly literal string SystemWebExtensionsDesign = "System.Web.Extensions.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" + .field static assembly literal string SystemWebMobile = "System.Web.Mobile, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemWebRegularExpressions = "System.Web.RegularExpressions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemWebRouting = "System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" + .field static assembly literal string SystemWebServices = "System.Web.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" + .field static assembly literal string SystemWindowsForms = "System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" + .field static assembly literal string SystemXml = "System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" + .field static assembly literal string VJSharpCodeProvider = "VJSharpCodeProvider, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" +} +.class private abstract sealed FXAssembly + extends [mscorlib]System.Object +{ + .field static assembly literal string Version = "4.0.0.0" +} +.class private abstract sealed ThisAssembly + extends [mscorlib]System.Object +{ + .field static assembly literal string Copyright = bytearray(A9 00 20 00 4D 00 69 00 63 00 72 00 6F 00 73 00 6F 00 66 00 74 00 20 00 43 00 6F 00 72 00 70 00 6F 00 72 00 61 00 74 00 69 00 6F 00 6E 00 2E 00 20 00 20 00 41 00 6C 00 6C 00 20 00 72 00 69 00 67 00 68 00 74 00 73 00 20 00 72 00 65 00 73 00 65 00 72 00 76 00 65 00 64 00 2E 00) + .field static assembly literal int32 DailyBuildNumber = int32(0x00002B62) + .field static assembly literal string DefaultAlias = "System.Dynamic.ComInterop.dll" + .field static assembly literal string Description = "System.Dynamic.ComInterop.dll" + .field static assembly literal string InformationalVersion = "4.0.11106.0" + .field static assembly literal string Title = "System.Dynamic.ComInterop.dll" + .field static assembly literal string Version = "4.0.0.0" +} +.namespace System.Dynamic.ComInterop +{ + .class private abstract ArgBuilder + extends [mscorlib]System.Object + { + .method assembly virtual hidebysig newslot strict abstract + instance class [System.Core]System.Linq.Expressions.Expression Marshal(class [System.Core]System.Linq.Expressions.Expression parameter) + { + } + .method assembly virtual hidebysig newslot strict + instance class [System.Core]System.Linq.Expressions.Expression MarshalToRef(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig newslot strict + instance class [System.Core]System.Linq.Expressions.Expression UnmarshalFromRef(class [System.Core]System.Linq.Expressions.Expression newValue) + { + ret + } + .method family hidebysig specialname + instance void .ctor() + { + ret + } + } + .class private abstract sealed Assert + extends [mscorlib]System.Object + { + .method assembly static hidebysig specialname + class [mscorlib]System.Exception get_Unreachable() + { + ret + } + .method assembly static hidebysig + void NotNull(object var) + { + .custom instance void [mscorlib]System.Diagnostics.ConditionalAttribute::.ctor(string) = { string('DEBUG') } + ret + } + .method assembly static hidebysig + void NotNull(object var1, object var2) + { + .custom instance void [mscorlib]System.Diagnostics.ConditionalAttribute::.ctor(string) = { string('DEBUG') } + ret + } + .method assembly static hidebysig + void NotNull(object var1, object var2, object var3) + { + .custom instance void [mscorlib]System.Diagnostics.ConditionalAttribute::.ctor(string) = { string('DEBUG') } + ret + } + .method assembly static hidebysig + void NotNull(object var1, object var2, object var3, object var4) + { + .custom instance void [mscorlib]System.Diagnostics.ConditionalAttribute::.ctor(string) = { string('DEBUG') } + ret + } + .method assembly static hidebysig + void NotEmpty(string str) + { + .custom instance void [mscorlib]System.Diagnostics.ConditionalAttribute::.ctor(string) = { string('DEBUG') } + ret + } + .method assembly static hidebysig + void NotEmpty(class [mscorlib]'System.Collections.Generic.ICollection`1' 'array') + { + .custom instance void [mscorlib]System.Diagnostics.ConditionalAttribute::.ctor(string) = { string('DEBUG') } + ret + } + .method assembly static hidebysig + void NotNullItems(class [mscorlib]'System.Collections.Generic.IEnumerable`1' items) + { + .custom instance void [mscorlib]System.Diagnostics.ConditionalAttribute::.ctor(string) = { string('DEBUG') } + ret + } + .method assembly static hidebysig + void IsTrue(class [System.Core]'System.Func`1' predicate) + { + .custom instance void [mscorlib]System.Diagnostics.ConditionalAttribute::.ctor(string) = { string('DEBUG') } + ret + } + .property class [mscorlib]System.Exception Unreachable() + { + .get class [mscorlib]System.Exception System.Dynamic.ComInterop.Assert::get_Unreachable() + } + } + .class private sealed BoolArgBuilder + extends System.Dynamic.ComInterop.SimpleArgBuilder + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Type parameterType) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression MarshalToRef(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression UnmarshalFromRef(class [System.Core]System.Linq.Expressions.Expression 'value') + { + ret + } + } + .class private sealed BoundDispEvent + extends [mscorlib]System.Object + { + .method assembly hidebysig specialname + instance void .ctor(object rcw, valuetype [mscorlib]System.Guid sourceIid, int32 dispid) + { + ret + } + .method public hidebysig specialname + instance object op_AdditionAssignment(object func) + { + ret + } + .method public hidebysig specialname + instance object InPlaceAdd(object func) + { + ret + } + .method public hidebysig specialname + instance object InPlaceSubtract(object func) + { + ret + } + } + .class private abstract sealed CollectionExtensions + extends [mscorlib]System.Object + { + .method assembly static hidebysig + !!1[] Map(class [mscorlib]'System.Collections.Generic.ICollection`1' collection, class [System.Core]'System.Func`2' select) + { + ret + } + .method assembly static hidebysig + bool Any(class [mscorlib]'System.Collections.Generic.IEnumerable`1' source, class [System.Core]'System.Func`2' predicate) + { + ret + } + .method assembly static hidebysig + !!0[] RemoveFirst(!!0[] 'array') + { + ret + } + .method assembly static hidebysig + !!0[] AddFirst(class [mscorlib]'System.Collections.Generic.IList`1' list, !!0 item) + { + ret + } + .method assembly static hidebysig + !!0[] AddLast(class [mscorlib]'System.Collections.Generic.IList`1' list, !!0 item) + { + ret + } + } + .class public abstract sealed ComBinder + extends [mscorlib]System.Object + { + .method public static hidebysig + bool IsComObject(object 'value') + { + ret + } + .method public static hidebysig + bool TryBindGetMember(class [System.Core]System.Dynamic.Binders.GetMemberBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject& 'instance') + { + ret + } + .method public static hidebysig + bool TryBindSetMember(class [System.Core]System.Dynamic.Binders.SetMemberBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject& 'instance', class [System.Core]System.Dynamic.Binders.MetaObject 'value') + { + ret + } + .method public static hidebysig + bool TryBindInvokeMember(class [System.Core]System.Dynamic.Binders.InvokeMemberBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject& 'instance', class [System.Core]System.Dynamic.Binders.MetaObject[] args) + { + ret + } + .method public static hidebysig + bool TryBindGetIndex(class [System.Core]System.Dynamic.Binders.GetIndexBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject& 'instance', class [System.Core]System.Dynamic.Binders.MetaObject[] args) + { + ret + } + .method public static hidebysig + bool TryBindSetIndex(class [System.Core]System.Dynamic.Binders.SetIndexBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject& 'instance', class [System.Core]System.Dynamic.Binders.MetaObject[] args, class [System.Core]System.Dynamic.Binders.MetaObject 'value') + { + ret + } + .method public static hidebysig + class [mscorlib]'System.Collections.Generic.IEnumerable`1' GetDynamicMemberNames(object 'value') + { + ret + } + .method public static hidebysig + class [mscorlib]'System.Collections.Generic.IEnumerable`1'> GetDynamicDataMembers(object 'value') + { + ret + } + } + .class private abstract sealed ComBinderHelpers + extends [mscorlib]System.Object + { + .method assembly static hidebysig + bool IsStrongBoxArg(class [System.Core]System.Dynamic.Binders.MetaObject o) + { + ret + } + .method assembly static hidebysig + class [System.Core]System.Dynamic.Binders.MetaObject RewriteStrongBoxAsRef(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder action, class [System.Core]System.Dynamic.Binders.MetaObject target, class [System.Core]System.Dynamic.Binders.MetaObject[] args) + { + ret + } + } + .class private abstract sealed ComDispIds + extends [mscorlib]System.Object + { + .field static assembly literal int32 'DISPID_NEWENUM' = int32(0xFFFFFFFC) + .field static assembly literal int32 'DISPID_PROPERTYPUT' = int32(0xFFFFFFFD) + .field static assembly literal int32 'DISPID_VALUE' = int32(0x00000000) + } + .class private ComEventDesc + extends [mscorlib]System.Object + { + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .field assembly int32 dispid + .field assembly valuetype [mscorlib]System.Guid sourceIID + } + .class private sealed ComEventSink + extends [mscorlib]System.MarshalByRefObject + implements [mscorlib]System.Reflection.IReflect, [mscorlib]System.IDisposable + { + .method public static hidebysig + class System.Dynamic.ComInterop.ComEventSink FromRuntimeCallableWrapper(object rcw, valuetype [mscorlib]System.Guid sourceIid, bool createIfNotFound) + { + ret + } + .method public hidebysig + instance void AddHandler(int32 dispid, object func) + { + ret + } + .method public hidebysig + instance void RemoveHandler(int32 dispid, object func) + { + ret + } + .method public hidebysig + instance object ExecuteHandler(string name, object[] args) + { + ret + } + .method public final virtual hidebysig newslot + instance class [mscorlib]System.Reflection.FieldInfo GetField(string name, valuetype [mscorlib]System.Reflection.BindingFlags bindingAttr) + { + ret + } + .method public final virtual hidebysig newslot + instance class [mscorlib]System.Reflection.FieldInfo[] GetFields(valuetype [mscorlib]System.Reflection.BindingFlags bindingAttr) + { + ret + } + .method public final virtual hidebysig newslot + instance class [mscorlib]System.Reflection.MemberInfo[] GetMember(string name, valuetype [mscorlib]System.Reflection.BindingFlags bindingAttr) + { + ret + } + .method public final virtual hidebysig newslot + instance class [mscorlib]System.Reflection.MemberInfo[] GetMembers(valuetype [mscorlib]System.Reflection.BindingFlags bindingAttr) + { + ret + } + .method public final virtual hidebysig newslot + instance class [mscorlib]System.Reflection.MethodInfo GetMethod(string name, valuetype [mscorlib]System.Reflection.BindingFlags bindingAttr) + { + ret + } + .method public final virtual hidebysig newslot + instance class [mscorlib]System.Reflection.MethodInfo GetMethod(string name, valuetype [mscorlib]System.Reflection.BindingFlags bindingAttr, class [mscorlib]System.Reflection.Binder binder, class [mscorlib]System.Type[] types, valuetype [mscorlib]System.Reflection.ParameterModifier[] modifiers) + { + ret + } + .method public final virtual hidebysig newslot + instance class [mscorlib]System.Reflection.MethodInfo[] GetMethods(valuetype [mscorlib]System.Reflection.BindingFlags bindingAttr) + { + ret + } + .method public final virtual hidebysig newslot + instance class [mscorlib]System.Reflection.PropertyInfo GetProperty(string name, valuetype [mscorlib]System.Reflection.BindingFlags bindingAttr, class [mscorlib]System.Reflection.Binder binder, class [mscorlib]System.Type returnType, class [mscorlib]System.Type[] types, valuetype [mscorlib]System.Reflection.ParameterModifier[] modifiers) + { + ret + } + .method public final virtual hidebysig newslot + instance class [mscorlib]System.Reflection.PropertyInfo GetProperty(string name, valuetype [mscorlib]System.Reflection.BindingFlags bindingAttr) + { + ret + } + .method public final virtual hidebysig newslot + instance class [mscorlib]System.Reflection.PropertyInfo[] GetProperties(valuetype [mscorlib]System.Reflection.BindingFlags bindingAttr) + { + ret + } + .method public final virtual hidebysig newslot specialname + instance class [mscorlib]System.Type get_UnderlyingSystemType() + { + ret + } + .method public final virtual hidebysig newslot + instance object InvokeMember(string name, valuetype [mscorlib]System.Reflection.BindingFlags invokeAttr, class [mscorlib]System.Reflection.Binder binder, object target, object[] args, valuetype [mscorlib]System.Reflection.ParameterModifier[] modifiers, class [mscorlib]System.Globalization.CultureInfo culture, string[] namedParameters) + { + ret + } + .method public final virtual hidebysig newslot + instance void Dispose() + { + ret + } + .method family virtual hidebysig + instance void Finalize() + { + ret + } + .property instance class [mscorlib]System.Type UnderlyingSystemType() + { + .get instance class [mscorlib]System.Type System.Dynamic.ComInterop.ComEventSink::get_UnderlyingSystemType() + } + } + .class private sealed ComEventSinkProxy + extends [mscorlib]System.Runtime.Remoting.Proxies.RealProxy + { + .method public hidebysig specialname + instance void .ctor(class System.Dynamic.ComInterop.ComEventSink sink, valuetype [mscorlib]System.Guid sinkIid) + { + ret + } + .method public virtual hidebysig + instance native int SupportsInterface(valuetype [mscorlib]System.Guid& iid) + { + .permissionset linkcheck = (2E 01 80 84 53 79 73 74 65 6D 2E 53 65 63 75 72 69 74 79 2E 50 65 72 6D 69 73 73 69 6F 6E 73 2E 53 65 63 75 72 69 74 79 50 65 72 6D 69 73 73 69 6F 6E 41 74 74 72 69 62 75 74 65 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 80 8D 01 54 55 7F 53 79 73 74 65 6D 2E 53 65 63 75 72 69 74 79 2E 50 65 72 6D 69 73 73 69 6F 6E 73 2E 53 65 63 75 72 69 74 79 50 65 72 6D 69 73 73 69 6F 6E 46 6C 61 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 05 46 6C 61 67 73 00 10 00 00) + ret + } + .method public virtual hidebysig + instance class [mscorlib]System.Runtime.Remoting.Messaging.IMessage Invoke(class [mscorlib]System.Runtime.Remoting.Messaging.IMessage msg) + { + .permissionset linkcheck = (2E 01 80 84 53 79 73 74 65 6D 2E 53 65 63 75 72 69 74 79 2E 50 65 72 6D 69 73 73 69 6F 6E 73 2E 53 65 63 75 72 69 74 79 50 65 72 6D 69 73 73 69 6F 6E 41 74 74 72 69 62 75 74 65 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 80 8D 01 54 55 7F 53 79 73 74 65 6D 2E 53 65 63 75 72 69 74 79 2E 50 65 72 6D 69 73 73 69 6F 6E 73 2E 53 65 63 75 72 69 74 79 50 65 72 6D 69 73 73 69 6F 6E 46 6C 61 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 05 46 6C 61 67 73 00 10 00 00) + ret + } + } + .class private ComEventSinksContainer + extends class [mscorlib]'System.Collections.Generic.List`1' + implements [mscorlib]System.IDisposable + { + .method public static hidebysig + class System.Dynamic.ComInterop.ComEventSinksContainer FromRuntimeCallableWrapper(object rcw, bool createIfNotFound) + { + ret + } + .method public final virtual hidebysig newslot + instance void Dispose() + { + ret + } + .method family virtual hidebysig + instance void Finalize() + { + ret + } + } + .class private ComFallbackMetaObject + extends [System.Core]System.Dynamic.Binders.MetaObject + { + .method assembly hidebysig specialname + instance void .ctor(class [System.Core]System.Linq.Expressions.Expression expression, class [System.Core]System.Dynamic.Binders.Restrictions restrictions, object arg) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindGetIndex(class [System.Core]System.Dynamic.Binders.GetIndexBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject[] indexes) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindSetIndex(class [System.Core]System.Dynamic.Binders.SetIndexBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject[] indexes, class [System.Core]System.Dynamic.Binders.MetaObject 'value') + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindGetMember(class [System.Core]System.Dynamic.Binders.GetMemberBinder binder) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindInvokeMember(class [System.Core]System.Dynamic.Binders.InvokeMemberBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject[] args) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindSetMember(class [System.Core]System.Dynamic.Binders.SetMemberBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject 'value') + { + ret + } + .method family virtual hidebysig newslot + instance class System.Dynamic.ComInterop.ComUnwrappedMetaObject UnwrapSelf() + { + ret + } + } + .class private abstract sealed ComHresults + extends [mscorlib]System.Object + { + .method assembly static hidebysig + bool IsSuccess(int32 'hresult') + { + ret + } + .field static assembly literal int32 'CONNECT_E_NOCONNECTION' = int32(0x80040200) + .field static assembly literal int32 'DISP_E_ARRAYISLOCKED' = int32(0x8002000D) + .field static assembly literal int32 'DISP_E_BADINDEX' = int32(0x8002000B) + .field static assembly literal int32 'DISP_E_BADPARAMCOUNT' = int32(0x8002000E) + .field static assembly literal int32 'DISP_E_BADVARTYPE' = int32(0x80020008) + .field static assembly literal int32 'DISP_E_EXCEPTION' = int32(0x80020009) + .field static assembly literal int32 'DISP_E_MEMBERNOTFOUND' = int32(0x80020003) + .field static assembly literal int32 'DISP_E_NONAMEDARGS' = int32(0x80020007) + .field static assembly literal int32 'DISP_E_OVERFLOW' = int32(0x8002000A) + .field static assembly literal int32 'DISP_E_PARAMNOTFOUND' = int32(0x80020004) + .field static assembly literal int32 'DISP_E_PARAMNOTOPTIONAL' = int32(0x8002000F) + .field static assembly literal int32 'DISP_E_TYPEMISMATCH' = int32(0x80020005) + .field static assembly literal int32 'DISP_E_UNKNOWNINTERFACE' = int32(0x80020001) + .field static assembly literal int32 'DISP_E_UNKNOWNLCID' = int32(0x8002000C) + .field static assembly literal int32 'DISP_E_UNKNOWNNAME' = int32(0x80020006) + .field static assembly literal int32 'E_FAIL' = int32(0x80004005) + .field static assembly literal int32 'E_NOINTERFACE' = int32(0x80004002) + .field static assembly literal int32 'S_OK' = int32(0x00000000) + .field static assembly literal int32 'TYPE_E_LIBNOTREGISTERED' = int32(0x8002801D) + } + .class private ComInvokeAction + extends [System.Core]System.Dynamic.Binders.InvokeBinder + { + .method public virtual hidebysig specialname + instance object get_CacheIdentity() + { + ret + } + .method assembly hidebysig specialname + instance void .ctor(class [System.Core]System.Linq.Expressions.ArgumentInfo[] arguments) + { + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = { } + ret + } + .method public virtual hidebysig + instance int32 GetHashCode() + { + ret + } + .method public virtual hidebysig + instance bool Equals(object obj) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject FallbackInvoke(class [System.Core]System.Dynamic.Binders.MetaObject target, class [System.Core]System.Dynamic.Binders.MetaObject[] args, class [System.Core]System.Dynamic.Binders.MetaObject errorSuggestion) + { + ret + } + .property instance object CacheIdentity() + { + .get instance object System.Dynamic.ComInterop.ComInvokeAction::get_CacheIdentity() + } + } + .class private sealed ComInvokeBinder + extends [mscorlib]System.Object + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]'System.Collections.Generic.IList`1' arguments, class [System.Core]System.Dynamic.Binders.MetaObject[] args, class [System.Core]System.Dynamic.Binders.Restrictions restrictions, class [System.Core]System.Linq.Expressions.Expression 'method', class [System.Core]System.Linq.Expressions.Expression dispatch, class System.Dynamic.ComInterop.ComMethodDesc methodDesc) + { + ret + } + .method assembly hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject Invoke() + { + ret + } + } + .class private ComMetaObject + extends [System.Core]System.Dynamic.Binders.MetaObject + { + .method assembly hidebysig specialname + instance void .ctor(class [System.Core]System.Linq.Expressions.Expression expression, class [System.Core]System.Dynamic.Binders.Restrictions restrictions, object arg) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindInvokeMember(class [System.Core]System.Dynamic.Binders.InvokeMemberBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject[] args) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindGetMember(class [System.Core]System.Dynamic.Binders.GetMemberBinder binder) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindSetMember(class [System.Core]System.Dynamic.Binders.SetMemberBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject 'value') + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindGetIndex(class [System.Core]System.Dynamic.Binders.GetIndexBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject[] indexes) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindSetIndex(class [System.Core]System.Dynamic.Binders.SetIndexBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject[] indexes, class [System.Core]System.Dynamic.Binders.MetaObject 'value') + { + ret + } + } + .class private sealed ComMethodDesc + extends [mscorlib]System.Object + { + .method assembly hidebysig specialname + instance void .ctor(string name, int32 dispId) + { + ret + } + .method assembly hidebysig specialname + instance void .ctor(string name, int32 dispId, valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.INVOKEKIND' invkind) + { + ret + } + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo, valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.FUNCDESC' funcDesc) + { + ret + } + .method assembly hidebysig specialname + instance bool get_HasTypeInfo() + { + ret + } + .method assembly hidebysig specialname + instance string get_DispIdString() + { + ret + } + .method public hidebysig specialname + instance string get_Name() + { + ret + } + .method public hidebysig specialname + instance int32 get_DispId() + { + ret + } + .method public hidebysig specialname + instance bool get_IsPropertyGet() + { + ret + } + .method public hidebysig specialname + instance bool get_IsDataMember() + { + ret + } + .method public hidebysig specialname + instance bool get_IsPropertyPut() + { + ret + } + .method public hidebysig specialname + instance bool get_IsPropertyPutRef() + { + ret + } + .method assembly hidebysig specialname + instance class System.Dynamic.ComInterop.ComParamDesc[] get_Parameters() + { + ret + } + .method public hidebysig specialname + instance bool get_HasByrefOrOutParameters() + { + ret + } + .field assembly initonly valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.INVOKEKIND' InvokeKind + .property instance int32 DispId() + { + .get instance int32 System.Dynamic.ComInterop.ComMethodDesc::get_DispId() + } + .property instance string DispIdString() + { + .get instance string System.Dynamic.ComInterop.ComMethodDesc::get_DispIdString() + } + .property instance bool HasByrefOrOutParameters() + { + .get instance bool System.Dynamic.ComInterop.ComMethodDesc::get_HasByrefOrOutParameters() + } + .property instance bool HasTypeInfo() + { + .get instance bool System.Dynamic.ComInterop.ComMethodDesc::get_HasTypeInfo() + } + .property instance bool IsDataMember() + { + .get instance bool System.Dynamic.ComInterop.ComMethodDesc::get_IsDataMember() + } + .property instance bool IsPropertyGet() + { + .get instance bool System.Dynamic.ComInterop.ComMethodDesc::get_IsPropertyGet() + } + .property instance bool IsPropertyPut() + { + .get instance bool System.Dynamic.ComInterop.ComMethodDesc::get_IsPropertyPut() + } + .property instance bool IsPropertyPutRef() + { + .get instance bool System.Dynamic.ComInterop.ComMethodDesc::get_IsPropertyPutRef() + } + .property instance string Name() + { + .get instance string System.Dynamic.ComInterop.ComMethodDesc::get_Name() + } + .property instance class System.Dynamic.ComInterop.ComParamDesc[] Parameters() + { + .get instance class System.Dynamic.ComInterop.ComParamDesc[] System.Dynamic.ComInterop.ComMethodDesc::get_Parameters() + } + } + .class private ComObject + extends [mscorlib]System.Object + implements [System.Core]System.Dynamic.Binders.IDynamicObject + { + .method assembly hidebysig specialname + instance void .ctor(object rcw) + { + ret + } + .method assembly hidebysig specialname + instance object get_RuntimeCallableWrapper() + { + ret + } + .method public static hidebysig + class System.Dynamic.ComInterop.ComObject ObjectToComObject(object rcw) + { + ret + } + .method assembly static hidebysig + class [System.Core]System.Linq.Expressions.MemberExpression RcwFromComObject(class [System.Core]System.Linq.Expressions.Expression comObject) + { + ret + } + .method assembly static hidebysig + class [System.Core]System.Linq.Expressions.MethodCallExpression RcwToComObject(class [System.Core]System.Linq.Expressions.Expression rcw) + { + ret + } + .method assembly virtual hidebysig newslot strict specialname + instance class [mscorlib]'System.Collections.Generic.IEnumerable`1' get_MemberNames() + { + ret + } + .method assembly virtual hidebysig newslot strict specialname + instance class [mscorlib]'System.Collections.Generic.IEnumerable`1'> get_DataMembers() + { + ret + } + .method private final virtual hidebysig newslot + instance class [System.Core]System.Dynamic.Binders.MetaObject System.Dynamic.Binders.IDynamicObject.GetMetaObject(class [System.Core]System.Linq.Expressions.Expression parameter) + { + .override [System.Core]System.Dynamic.Binders.IDynamicObject::GetMetaObject + ret + } + .method assembly static hidebysig + bool IsComObject(object obj) + { + ret + } + .property instance class [mscorlib]'System.Collections.Generic.IEnumerable`1'> DataMembers() + { + .get instance class [mscorlib]'System.Collections.Generic.IEnumerable`1'> System.Dynamic.ComInterop.ComObject::get_DataMembers() + } + .property instance class [mscorlib]'System.Collections.Generic.IEnumerable`1' MemberNames() + { + .get instance class [mscorlib]'System.Collections.Generic.IEnumerable`1' System.Dynamic.ComInterop.ComObject::get_MemberNames() + } + .property instance object RuntimeCallableWrapper() + { + .get instance object System.Dynamic.ComInterop.ComObject::get_RuntimeCallableWrapper() + } + } + .class private sealed ComParamDesc + extends [mscorlib]System.Object + { + .class nested assembly sequential sealed 'PARAMDESCEX' + extends [mscorlib]System.ValueType + { + .method assembly hidebysig + instance void Dummy() + { + ret + } + .method assembly static hidebysig + object GetDefaultValue(valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.PARAMDESC'& paramdesc) + { + ret + } + + //This private field was generated by ASMMETA as a placeholder and does not exist in the actual assembly. + .field private int32 __Asmmeta_Private_Field + } + .method assembly hidebysig specialname + instance void .ctor(valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.ELEMDESC'& elemDesc, string name) + { + ret + } + .method public virtual hidebysig + instance string ToString() + { + ret + } + .method public hidebysig specialname + instance bool get_IsOut() + { + ret + } + .method public hidebysig specialname + instance bool get_ByReference() + { + ret + } + .property instance bool ByReference() + { + .get instance bool System.Dynamic.ComInterop.ComParamDesc::get_ByReference() + } + .property instance bool IsOut() + { + .get instance bool System.Dynamic.ComInterop.ComParamDesc::get_IsOut() + } + } + .class private abstract sealed ComRuntimeHelpers + extends [mscorlib]System.Object + { + .method public static hidebysig + void CheckThrowException(int32 'hresult', valuetype System.Dynamic.ComInterop.ExcepInfo& excepInfo, uint32 argErr, string message) + { + ret + } + .method assembly static hidebysig + void GetInfoFromType(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo, [out] string& name, [out] string& documentation) + { + ret + } + .method assembly static hidebysig + string GetNameOfMethod(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo, int32 memid) + { + ret + } + .method assembly static hidebysig + string GetNameOfLib(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeLib typeLib) + { + ret + } + .method assembly static hidebysig + string GetNameOfType(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeInfo GetITypeInfoFromIDispatch(class System.Dynamic.ComInterop.IDispatch dispatch, bool throwIfMissingExpectedTypeInfo) + { + ret + } + .method assembly static hidebysig + valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.TYPEATTR' GetTypeAttrForTypeInfo(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo) + { + ret + } + .method assembly static hidebysig + valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.TYPELIBATTR' GetTypeAttrForTypeLib(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeLib typeLib) + { + ret + } + .method public static hidebysig + class System.Dynamic.ComInterop.BoundDispEvent CreateComEvent(object rcw, valuetype [mscorlib]System.Guid sourceIid, int32 dispid) + { + ret + } + .method public static hidebysig + class System.Dynamic.ComInterop.DispCallable CreateDispCallable(class System.Dynamic.ComInterop.IDispatch dispatch, class System.Dynamic.ComInterop.ComMethodDesc 'method') + { + ret + } + } + .class private sealed ComTypeClassDesc + extends System.Dynamic.ComInterop.ComTypeDesc + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo) + { + ret + } + .method assembly hidebysig + instance bool Implements(string itfName, bool isSourceItf) + { + ret + } + } + .class private ComTypeDesc + extends [mscorlib]System.Object + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo) + { + ret + } + .method assembly static hidebysig + class System.Dynamic.ComInterop.ComTypeDesc FromITypeInfo(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo) + { + ret + } + .method assembly static hidebysig + class System.Dynamic.ComInterop.ComTypeDesc CreateEmptyTypeDesc() + { + ret + } + .method assembly static hidebysig specialname + class [mscorlib]'System.Collections.Generic.Dictionary`2' get_EmptyEvents() + { + ret + } + .method assembly hidebysig specialname + instance class [mscorlib]'System.Collections.Generic.Dictionary`2' get_Funcs() + { + ret + } + .method assembly hidebysig specialname + instance void set_Funcs(class [mscorlib]'System.Collections.Generic.Dictionary`2' 'value') + { + ret + } + .method assembly hidebysig specialname + instance class [mscorlib]'System.Collections.Generic.Dictionary`2' get_Puts() + { + ret + } + .method assembly hidebysig specialname + instance void set_Puts(class [mscorlib]'System.Collections.Generic.Dictionary`2' 'value') + { + ret + } + .method assembly hidebysig specialname + instance class [mscorlib]'System.Collections.Generic.Dictionary`2' get_Events() + { + ret + } + .method assembly hidebysig specialname + instance void set_Events(class [mscorlib]'System.Collections.Generic.Dictionary`2' 'value') + { + ret + } + .method public hidebysig specialname + instance string get_TypeName() + { + ret + } + .method assembly hidebysig specialname + instance valuetype [mscorlib]System.Guid get_Guid() + { + ret + } + .method assembly hidebysig specialname + instance void set_Guid(valuetype [mscorlib]System.Guid 'value') + { + ret + } + .method assembly hidebysig specialname + instance class System.Dynamic.ComInterop.ComMethodDesc get_GetItem() + { + ret + } + .method assembly hidebysig specialname + instance void set_GetItem(class System.Dynamic.ComInterop.ComMethodDesc 'value') + { + ret + } + .method assembly hidebysig specialname + instance class System.Dynamic.ComInterop.ComMethodDesc get_SetItem() + { + ret + } + .method assembly hidebysig specialname + instance void set_SetItem(class System.Dynamic.ComInterop.ComMethodDesc 'value') + { + ret + } + .property class [mscorlib]'System.Collections.Generic.Dictionary`2' EmptyEvents() + { + .get class [mscorlib]'System.Collections.Generic.Dictionary`2' System.Dynamic.ComInterop.ComTypeDesc::get_EmptyEvents() + } + .property instance class [mscorlib]'System.Collections.Generic.Dictionary`2' Events() + { + .get instance class [mscorlib]'System.Collections.Generic.Dictionary`2' System.Dynamic.ComInterop.ComTypeDesc::get_Events() + .set instance void System.Dynamic.ComInterop.ComTypeDesc::set_Events(class [mscorlib]'System.Collections.Generic.Dictionary`2') + } + .property instance class [mscorlib]'System.Collections.Generic.Dictionary`2' Funcs() + { + .get instance class [mscorlib]'System.Collections.Generic.Dictionary`2' System.Dynamic.ComInterop.ComTypeDesc::get_Funcs() + .set instance void System.Dynamic.ComInterop.ComTypeDesc::set_Funcs(class [mscorlib]'System.Collections.Generic.Dictionary`2') + } + .property instance class System.Dynamic.ComInterop.ComMethodDesc GetItem() + { + .get instance class System.Dynamic.ComInterop.ComMethodDesc System.Dynamic.ComInterop.ComTypeDesc::get_GetItem() + .set instance void System.Dynamic.ComInterop.ComTypeDesc::set_GetItem(class System.Dynamic.ComInterop.ComMethodDesc) + } + .property instance valuetype [mscorlib]System.Guid Guid() + { + .get instance valuetype [mscorlib]System.Guid System.Dynamic.ComInterop.ComTypeDesc::get_Guid() + .set instance void System.Dynamic.ComInterop.ComTypeDesc::set_Guid(valuetype [mscorlib]System.Guid) + } + .property instance class [mscorlib]'System.Collections.Generic.Dictionary`2' Puts() + { + .get instance class [mscorlib]'System.Collections.Generic.Dictionary`2' System.Dynamic.ComInterop.ComTypeDesc::get_Puts() + .set instance void System.Dynamic.ComInterop.ComTypeDesc::set_Puts(class [mscorlib]'System.Collections.Generic.Dictionary`2') + } + .property instance class System.Dynamic.ComInterop.ComMethodDesc SetItem() + { + .get instance class System.Dynamic.ComInterop.ComMethodDesc System.Dynamic.ComInterop.ComTypeDesc::get_SetItem() + .set instance void System.Dynamic.ComInterop.ComTypeDesc::set_SetItem(class System.Dynamic.ComInterop.ComMethodDesc) + } + .property instance string TypeName() + { + .get instance string System.Dynamic.ComInterop.ComTypeDesc::get_TypeName() + } + } + .class private sealed ComTypeEnumDesc + extends System.Dynamic.ComInterop.ComTypeDesc + { + .method public virtual hidebysig + instance string ToString() + { + ret + } + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo) + { + ret + } + } + .class private sealed ComTypeLibDesc + extends [mscorlib]System.Object + { + .method public virtual hidebysig + instance string ToString() + { + ret + } + .method assembly static hidebysig + class System.Dynamic.ComInterop.ComTypeLibDesc GetFromTypeLib(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeLib typeLib) + { + ret + } + .method assembly hidebysig + instance class System.Dynamic.ComInterop.ComTypeClassDesc GetCoClassForInterface(string itfName) + { + ret + } + } + .class private sealed ComUnwrappedMetaObject + extends [System.Core]System.Dynamic.Binders.MetaObject + { + .method assembly hidebysig specialname + instance void .ctor(class [System.Core]System.Linq.Expressions.Expression expression, class [System.Core]System.Dynamic.Binders.Restrictions restrictions, object 'value') + { + ret + } + } + .class private abstract sealed ContractUtils + extends [mscorlib]System.Object + { + .method assembly static hidebysig + void Requires(bool precondition, string paramName) + { + ret + } + .method assembly static hidebysig + void Requires(bool precondition, string paramName, string message) + { + ret + } + .method assembly static hidebysig + void RequiresNotNull(object 'value', string paramName) + { + ret + } + } + .class private ConversionArgBuilder + extends System.Dynamic.ComInterop.ArgBuilder + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Type parameterType, class System.Dynamic.ComInterop.SimpleArgBuilder innerBuilder) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression Marshal(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression MarshalToRef(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + } + .class private ConvertibleArgBuilder + extends System.Dynamic.ComInterop.ArgBuilder + { + .method assembly hidebysig specialname + instance void .ctor() + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression Marshal(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression MarshalToRef(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + } + .class private sealed CurrencyArgBuilder + extends System.Dynamic.ComInterop.SimpleArgBuilder + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Type parameterType) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression Marshal(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression MarshalToRef(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression UnmarshalFromRef(class [System.Core]System.Linq.Expressions.Expression 'value') + { + ret + } + } + .class private sealed DateTimeArgBuilder + extends System.Dynamic.ComInterop.SimpleArgBuilder + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Type parameterType) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression MarshalToRef(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression UnmarshalFromRef(class [System.Core]System.Linq.Expressions.Expression 'value') + { + ret + } + } + .class private sealed DispCallable + extends [mscorlib]System.Object + implements [System.Core]System.Dynamic.Binders.IDynamicObject + { + .method assembly hidebysig specialname + instance void .ctor(class System.Dynamic.ComInterop.IDispatch dispatch, class System.Dynamic.ComInterop.ComMethodDesc methodDesc) + { + ret + } + .method public virtual hidebysig + instance string ToString() + { + ret + } + .method public hidebysig specialname + instance class System.Dynamic.ComInterop.IDispatch get_DispatchObject() + { + ret + } + .method public hidebysig specialname + instance class System.Dynamic.ComInterop.ComMethodDesc get_ComMethodDesc() + { + ret + } + .method public final virtual hidebysig newslot + instance class [System.Core]System.Dynamic.Binders.MetaObject GetMetaObject(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method public virtual hidebysig + instance bool Equals(object obj) + { + ret + } + .method public virtual hidebysig + instance int32 GetHashCode() + { + ret + } + .property instance class System.Dynamic.ComInterop.ComMethodDesc ComMethodDesc() + { + .get instance class System.Dynamic.ComInterop.ComMethodDesc System.Dynamic.ComInterop.DispCallable::get_ComMethodDesc() + } + .property instance class System.Dynamic.ComInterop.IDispatch DispatchObject() + { + .get instance class System.Dynamic.ComInterop.IDispatch System.Dynamic.ComInterop.DispCallable::get_DispatchObject() + } + } + .class private DispCallableMetaObject + extends [System.Core]System.Dynamic.Binders.MetaObject + { + .method assembly hidebysig specialname + instance void .ctor(class [System.Core]System.Linq.Expressions.Expression expression, class System.Dynamic.ComInterop.DispCallable callable) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindGetIndex(class [System.Core]System.Dynamic.Binders.GetIndexBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject[] indexes) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindSetIndex(class [System.Core]System.Dynamic.Binders.SetIndexBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject[] indexes, class [System.Core]System.Dynamic.Binders.MetaObject 'value') + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindInvoke(class [System.Core]System.Dynamic.Binders.InvokeBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject[] args) + { + ret + } + } + .class private DispatchArgBuilder + extends System.Dynamic.ComInterop.SimpleArgBuilder + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Type parameterType) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression Marshal(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression MarshalToRef(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression UnmarshalFromRef(class [System.Core]System.Linq.Expressions.Expression 'value') + { + ret + } + } + .class private abstract sealed 'EmptyArray`1' + extends [mscorlib]System.Object + { + .field static assembly !0[] Instance + } + .class private abstract sealed Error + extends [mscorlib]System.Object + { + .method assembly static hidebysig + class [mscorlib]System.Exception RemovingUnregisteredEvent() + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception COMObjectDoesNotSupportEvents() + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception COMObjectDoesNotSupportSourceInterface() + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception RemovingUnregisteredHandler() + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception SetComObjectDataFailed() + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception MethodShouldNotBeCalled() + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception DefaultValueCannotBeRead() + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception UnexpectedVarEnum(object p0) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception DispBadParamCount(object p0) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception DispMemberNotFound(object p0) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception DispNoNamedArgs(object p0) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception DispOverflow(object p0) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception DispTypeMismatch(object p0, object p1) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception DispParamNotOptional(object p0) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception CannotRetrieveTypeInformation() + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception GetIDsOfNamesInvalid(object p0) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception UnsupportedEnumType() + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception CouldNotGetDispId(object p0, object p1) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception AmbiguousConversion(object p0, object p1) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception VariantGetAccessorNYI(object p0) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception ArgumentNull(string paramName) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception ArgumentOutOfRange(string paramName) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception NotImplemented() + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Exception NotSupported() + { + ret + } + } + .class private ErrorArgBuilder + extends System.Dynamic.ComInterop.SimpleArgBuilder + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Type parameterType) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression Marshal(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression UnmarshalFromRef(class [System.Core]System.Linq.Expressions.Expression 'value') + { + ret + } + } + .class private sequential sealed ExcepInfo + extends [mscorlib]System.ValueType + { + .method assembly hidebysig + instance void Dummy() + { + ret + } + .method assembly hidebysig + instance class [mscorlib]System.Exception GetException() + { + ret + } + + //This private field was generated by ASMMETA as a placeholder and does not exist in the actual assembly. + .field private int32 __Asmmeta_Private_Field + } + .class private abstract sealed Helpers + extends [mscorlib]System.Object + { + .method assembly static hidebysig + class [System.Core]System.Linq.Expressions.Expression Convert(class [System.Core]System.Linq.Expressions.Expression expression, class [mscorlib]System.Type 'type') + { + ret + } + .method assembly static hidebysig + void EmitInt(class [mscorlib]System.Reflection.Emit.ILGenerator gen, int32 'value') + { + ret + } + } + .class private interface abstract import IDispatch + { + .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = { string('00020400-0000-0000-C000-000000000046') } + .custom instance void [mscorlib]System.Runtime.InteropServices.InterfaceTypeAttribute::.ctor(valuetype [mscorlib]System.Runtime.InteropServices.ComInterfaceType) = { int32(1) } + .method public virtual hidebysig newslot abstract + instance int32 TryGetTypeInfoCount([out] uint32& pctinfo) + preservesig + { + } + .method public virtual hidebysig newslot abstract + instance int32 TryGetTypeInfo(uint32 iTInfo, int32 lcid, [out] native int& info) + preservesig + { + } + .method public virtual hidebysig newslot abstract + instance int32 TryGetIDsOfNames(valuetype [mscorlib]System.Guid& iid, string[] marshal(lpwstr[ + 2]) names, uint32 cNames, int32 lcid, [out] int32[] marshal(int32[ + 2]) rgDispId) + preservesig + { + } + .method public virtual hidebysig newslot abstract + instance int32 TryInvoke(int32 dispIdMember, valuetype [mscorlib]System.Guid& riid, int32 lcid, valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.INVOKEKIND' wFlags, valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.DISPPARAMS'& pDispParams, [out] object& VarResult, [out] valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.EXCEPINFO'& pExcepInfo, [out] uint32& puArgErr) + preservesig + { + } + } + .class private sealed IDispatchComObject + extends System.Dynamic.ComInterop.ComObject + implements [System.Core]System.Dynamic.Binders.IDynamicObject + { + .method assembly hidebysig specialname + instance void .ctor(class System.Dynamic.ComInterop.IDispatch rcw) + { + ret + } + .method public virtual hidebysig + instance string ToString() + { + ret + } + .method public hidebysig specialname + instance class System.Dynamic.ComInterop.ComTypeDesc get_ComTypeDesc() + { + ret + } + .method public hidebysig specialname + instance class System.Dynamic.ComInterop.IDispatch get_DispatchObject() + { + ret + } + .method public hidebysig + instance bool TryGetGetItem([out] class System.Dynamic.ComInterop.DispCallable& 'value') + { + ret + } + .method public hidebysig + instance bool TryGetSetItem([out] class System.Dynamic.ComInterop.DispCallable& 'value') + { + ret + } + .method public hidebysig + instance bool SlowTryGetSetItem([out] class System.Dynamic.ComInterop.DispCallable& 'value') + { + ret + } + .method assembly hidebysig + instance bool TryGetIDOfName(string name) + { + ret + } + .method assembly hidebysig + instance bool TryGetMemberMethod(string name, [out] class System.Dynamic.ComInterop.ComMethodDesc& 'method') + { + ret + } + .method assembly hidebysig + instance bool TryGetMemberEvent(string name, [out] class System.Dynamic.ComInterop.ComEventDesc& event) + { + ret + } + .method assembly hidebysig + instance bool TryGetMemberMethodExplicit(string name, [out] class System.Dynamic.ComInterop.ComMethodDesc& 'method') + { + ret + } + .method assembly virtual hidebysig strict specialname + instance class [mscorlib]'System.Collections.Generic.IEnumerable`1' get_MemberNames() + { + ret + } + .method assembly virtual hidebysig strict specialname + instance class [mscorlib]'System.Collections.Generic.IEnumerable`1'> get_DataMembers() + { + ret + } + .method private final virtual hidebysig newslot + instance class [System.Core]System.Dynamic.Binders.MetaObject System.Dynamic.Binders.IDynamicObject.GetMetaObject(class [System.Core]System.Linq.Expressions.Expression parameter) + { + .override [System.Core]System.Dynamic.Binders.IDynamicObject::GetMetaObject + ret + } + .method assembly static hidebysig + void GetFuncDescForDescIndex(class [mscorlib]System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo, int32 funcIndex, [out] valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.FUNCDESC'& funcDesc, [out] native int& funcDescHandle) + { + ret + } + .method assembly hidebysig + instance bool TryGetPropertySetter(string name, [out] class System.Dynamic.ComInterop.ComMethodDesc& 'method') + { + ret + } + .method assembly hidebysig + instance bool TryGetEventHandler(string name, [out] class System.Dynamic.ComInterop.ComEventDesc& event) + { + ret + } + .property instance class System.Dynamic.ComInterop.ComTypeDesc ComTypeDesc() + { + .get instance class System.Dynamic.ComInterop.ComTypeDesc System.Dynamic.ComInterop.IDispatchComObject::get_ComTypeDesc() + } + .property instance class [mscorlib]'System.Collections.Generic.IEnumerable`1'> DataMembers() + { + .get instance class [mscorlib]'System.Collections.Generic.IEnumerable`1'> System.Dynamic.ComInterop.IDispatchComObject::get_DataMembers() + } + .property instance class System.Dynamic.ComInterop.IDispatch DispatchObject() + { + .get instance class System.Dynamic.ComInterop.IDispatch System.Dynamic.ComInterop.IDispatchComObject::get_DispatchObject() + } + .property instance class [mscorlib]'System.Collections.Generic.IEnumerable`1' MemberNames() + { + .get instance class [mscorlib]'System.Collections.Generic.IEnumerable`1' System.Dynamic.ComInterop.IDispatchComObject::get_MemberNames() + } + } + .class private interface abstract import IDispatchForReflection + { + .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = { string('00020400-0000-0000-C000-000000000046') } + .custom instance void [mscorlib]System.Runtime.InteropServices.InterfaceTypeAttribute::.ctor(valuetype [mscorlib]System.Runtime.InteropServices.ComInterfaceType) = { int32(2) } + } + .class private sealed IDispatchMetaObject + extends System.Dynamic.ComInterop.ComFallbackMetaObject + { + .method assembly hidebysig specialname + instance void .ctor(class [System.Core]System.Linq.Expressions.Expression expression, class System.Dynamic.ComInterop.ComTypeDesc wrapperType, class System.Dynamic.ComInterop.IDispatchComObject self) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindInvokeMember(class [System.Core]System.Dynamic.Binders.InvokeMemberBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject[] args) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindGetMember(class [System.Core]System.Dynamic.Binders.GetMemberBinder binder) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindGetIndex(class [System.Core]System.Dynamic.Binders.GetIndexBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject[] indexes) + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindSetIndex(class [System.Core]System.Dynamic.Binders.SetIndexBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject[] indexes, class [System.Core]System.Dynamic.Binders.MetaObject 'value') + { + ret + } + .method public virtual hidebysig + instance class [System.Core]System.Dynamic.Binders.MetaObject BindSetMember(class [System.Core]System.Dynamic.Binders.SetMemberBinder binder, class [System.Core]System.Dynamic.Binders.MetaObject 'value') + { + ret + } + .method family virtual hidebysig + instance class System.Dynamic.ComInterop.ComUnwrappedMetaObject UnwrapSelf() + { + ret + } + } + .class private sealed IDispatchMethodIndices + extends [mscorlib]System.Enum + { + .field public rtspecialname specialname int32 value__ + .field static public literal valuetype System.Dynamic.ComInterop.IDispatchMethodIndices IDispatch_GetIDsOfNames = int32(0x00000005) + .field static public literal valuetype System.Dynamic.ComInterop.IDispatchMethodIndices IDispatch_GetTypeInfo = int32(0x00000004) + .field static public literal valuetype System.Dynamic.ComInterop.IDispatchMethodIndices IDispatch_GetTypeInfoCount = int32(0x00000003) + .field static public literal valuetype System.Dynamic.ComInterop.IDispatchMethodIndices IDispatch_Invoke = int32(0x00000006) + .field static public literal valuetype System.Dynamic.ComInterop.IDispatchMethodIndices IUnknown_AddRef = int32(0x00000001) + .field static public literal valuetype System.Dynamic.ComInterop.IDispatchMethodIndices IUnknown_QueryInterface = int32(0x00000000) + .field static public literal valuetype System.Dynamic.ComInterop.IDispatchMethodIndices IUnknown_Release = int32(0x00000002) + } + .class private interface abstract import IProvideClassInfo + { + .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = { string('B196B283-BAB4-101A-B69C-00AA00341D07') } + .custom instance void [mscorlib]System.Runtime.InteropServices.InterfaceTypeAttribute::.ctor(valuetype [mscorlib]System.Runtime.InteropServices.ComInterfaceType) = { int32(1) } + .method public virtual hidebysig newslot abstract + instance void GetClassInfo([out] native int& info) + { + } + } + .class private sealed NullArgBuilder + extends System.Dynamic.ComInterop.ArgBuilder + { + .method assembly hidebysig specialname + instance void .ctor() + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression Marshal(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + } + .class private sealed SR + extends [mscorlib]System.Object + { + .method assembly hidebysig specialname + instance void .ctor() + { + ret + } + .method public static hidebysig specialname + class [mscorlib]System.Resources.ResourceManager get_Resources() + { + ret + } + .method public static hidebysig + string GetString(string name, object[] args) + { + .param [2] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = { } + ret + } + .method public static hidebysig + string GetString(string name) + { + ret + } + .method public static hidebysig + string GetString(string name, [out] bool& usedFallback) + { + ret + } + .method public static hidebysig + object GetObject(string name) + { + ret + } + .field static assembly literal string AmbiguousConversion = "AmbiguousConversion" + .field static assembly literal string COMObjectDoesNotSupportEvents = "COMObjectDoesNotSupportEvents" + .field static assembly literal string COMObjectDoesNotSupportSourceInterface = "COMObjectDoesNotSupportSourceInterface" + .field static assembly literal string CannotRetrieveTypeInformation = "CannotRetrieveTypeInformation" + .field static assembly literal string ComObjectExpected = "ComObjectExpected" + .field static assembly literal string CouldNotGetDispId = "CouldNotGetDispId" + .field static assembly literal string DefaultValueCannotBeRead = "DefaultValueCannotBeRead" + .field static assembly literal string DispBadParamCount = "DispBadParamCount" + .field static assembly literal string DispMemberNotFound = "DispMemberNotFound" + .field static assembly literal string DispNoNamedArgs = "DispNoNamedArgs" + .field static assembly literal string DispOverflow = "DispOverflow" + .field static assembly literal string DispParamNotOptional = "DispParamNotOptional" + .field static assembly literal string DispTypeMismatch = "DispTypeMismatch" + .field static assembly literal string GetIDsOfNamesInvalid = "GetIDsOfNamesInvalid" + .field static assembly literal string InvalidArgumentValue = "InvalidArgumentValue" + .field static assembly literal string MethodShouldNotBeCalled = "MethodShouldNotBeCalled" + .field static assembly literal string RemovingUnregisteredEvent = "RemovingUnregisteredEvent" + .field static assembly literal string RemovingUnregisteredHandler = "RemovingUnregisteredHandler" + .field static assembly literal string SetComObjectDataFailed = "SetComObjectDataFailed" + .field static assembly literal string UnexpectedVarEnum = "UnexpectedVarEnum" + .field static assembly literal string UnsupportedEnumType = "UnsupportedEnumType" + .field static assembly literal string VariantGetAccessorNYI = "VariantGetAccessorNYI" + .property class [mscorlib]System.Resources.ResourceManager Resources() + { + .get class [mscorlib]System.Resources.ResourceManager 'System.Dynamic.ComInterop.SR'::get_Resources() + } + } + .class private sealed SRCategoryAttribute + extends [System]System.ComponentModel.CategoryAttribute + { + .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = { int32(32767) } + .method public hidebysig specialname + instance void .ctor(string category) + { + ret + } + .method family virtual hidebysig + instance string GetLocalizedString(string 'value') + { + ret + } + } + .class private sealed SRDescriptionAttribute + extends [System]System.ComponentModel.DescriptionAttribute + { + .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = { int32(32767) } + .method public hidebysig specialname + instance void .ctor(string description) + { + ret + } + .method public virtual hidebysig specialname + instance string get_Description() + { + ret + } + .property instance string Description() + { + .get instance string System.Dynamic.ComInterop.SRDescriptionAttribute::get_Description() + } + } + .class private sealed 'Set`1' + extends [mscorlib]System.Object + implements class [mscorlib]'System.Collections.Generic.ICollection`1', class [mscorlib]'System.Collections.Generic.IEnumerable`1', [mscorlib]System.Collections.IEnumerable + { + .method assembly hidebysig specialname + instance void .ctor() + { + ret + } + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]'System.Collections.Generic.IEqualityComparer`1' comparer) + { + ret + } + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]'System.Collections.Generic.IList`1' list) + { + ret + } + .method public final virtual hidebysig newslot + instance void Add(!0 item) + { + ret + } + .method public final virtual hidebysig newslot + instance void Clear() + { + ret + } + .method public final virtual hidebysig newslot + instance bool Contains(!0 item) + { + ret + } + .method public final virtual hidebysig newslot + instance void CopyTo(!0[] 'array', int32 arrayIndex) + { + ret + } + .method public final virtual hidebysig newslot specialname + instance int32 get_Count() + { + ret + } + .method public final virtual hidebysig newslot specialname + instance bool get_IsReadOnly() + { + ret + } + .method public final virtual hidebysig newslot + instance bool Remove(!0 item) + { + ret + } + .method public final virtual hidebysig newslot + instance class [mscorlib]'System.Collections.Generic.IEnumerator`1' GetEnumerator() + { + ret + } + .method private final virtual hidebysig newslot + instance class [mscorlib]System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + .override [mscorlib]System.Collections.IEnumerable::GetEnumerator + ret + } + .property instance int32 Count() + { + .get instance int32 'System.Dynamic.ComInterop.Set`1'::get_Count() + } + .property instance bool IsReadOnly() + { + .get instance bool 'System.Dynamic.ComInterop.Set`1'::get_IsReadOnly() + } + } + .class private SimpleArgBuilder + extends System.Dynamic.ComInterop.ArgBuilder + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Type parameterType) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression Marshal(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression UnmarshalFromRef(class [System.Core]System.Linq.Expressions.Expression newValue) + { + ret + } + } + .class private sealed SplatCallSite + extends [mscorlib]System.Object + { + .class nested assembly sealed SplatCaller + extends [mscorlib]System.MulticastDelegate + { + .method public hidebysig specialname + instance void .ctor(object 'object', native int 'method') + runtime + { + } + .method public virtual hidebysig newslot + instance object Invoke(object[] args) + runtime + { + } + .method public virtual hidebysig newslot + instance class [mscorlib]System.IAsyncResult BeginInvoke(object[] args, class [mscorlib]System.AsyncCallback callback, object 'object') + runtime + { + } + .method public virtual hidebysig newslot + instance object EndInvoke(class [mscorlib]System.IAsyncResult result) + runtime + { + } + } + .method public hidebysig specialname + instance void .ctor(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder binder) + { + ret + } + .method public hidebysig + instance object Invoke(object[] args) + { + ret + } + .method public static hidebysig + object CallHelper0(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper1(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper2(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper3(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper4(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper5(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper6(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper7(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper8(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper9(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper10(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper11(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper12(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper13(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper14(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + .method public static hidebysig + object CallHelper15(class [System.Core]'System.Runtime.CompilerServices.CallSite`1'> site, object[] args) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('used by generated code') bool(true) } + ret + } + } + .class private StringArgBuilder + extends System.Dynamic.ComInterop.SimpleArgBuilder + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Type parameterType) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression Marshal(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression MarshalToRef(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression UnmarshalFromRef(class [System.Core]System.Linq.Expressions.Expression 'value') + { + ret + } + } + .class private abstract sealed Strings + extends [mscorlib]System.Object + { + .method assembly static hidebysig specialname + string get_InvalidArgumentValue() + { + ret + } + .method assembly static hidebysig specialname + string get_ComObjectExpected() + { + ret + } + .method assembly static hidebysig specialname + string get_RemovingUnregisteredEvent() + { + ret + } + .method assembly static hidebysig specialname + string get_COMObjectDoesNotSupportEvents() + { + ret + } + .method assembly static hidebysig specialname + string get_COMObjectDoesNotSupportSourceInterface() + { + ret + } + .method assembly static hidebysig specialname + string get_RemovingUnregisteredHandler() + { + ret + } + .method assembly static hidebysig specialname + string get_SetComObjectDataFailed() + { + ret + } + .method assembly static hidebysig specialname + string get_MethodShouldNotBeCalled() + { + ret + } + .method assembly static hidebysig specialname + string get_DefaultValueCannotBeRead() + { + ret + } + .method assembly static hidebysig + string UnexpectedVarEnum(object p0) + { + ret + } + .method assembly static hidebysig + string DispBadParamCount(object p0) + { + ret + } + .method assembly static hidebysig + string DispMemberNotFound(object p0) + { + ret + } + .method assembly static hidebysig + string DispNoNamedArgs(object p0) + { + ret + } + .method assembly static hidebysig + string DispOverflow(object p0) + { + ret + } + .method assembly static hidebysig + string DispTypeMismatch(object p0, object p1) + { + ret + } + .method assembly static hidebysig + string DispParamNotOptional(object p0) + { + ret + } + .method assembly static hidebysig specialname + string get_CannotRetrieveTypeInformation() + { + ret + } + .method assembly static hidebysig + string GetIDsOfNamesInvalid(object p0) + { + ret + } + .method assembly static hidebysig specialname + string get_UnsupportedEnumType() + { + ret + } + .method assembly static hidebysig + string CouldNotGetDispId(object p0, object p1) + { + ret + } + .method assembly static hidebysig + string AmbiguousConversion(object p0, object p1) + { + ret + } + .method assembly static hidebysig + string VariantGetAccessorNYI(object p0) + { + ret + } + .property string COMObjectDoesNotSupportEvents() + { + .get string System.Dynamic.ComInterop.Strings::get_COMObjectDoesNotSupportEvents() + } + .property string COMObjectDoesNotSupportSourceInterface() + { + .get string System.Dynamic.ComInterop.Strings::get_COMObjectDoesNotSupportSourceInterface() + } + .property string CannotRetrieveTypeInformation() + { + .get string System.Dynamic.ComInterop.Strings::get_CannotRetrieveTypeInformation() + } + .property string ComObjectExpected() + { + .get string System.Dynamic.ComInterop.Strings::get_ComObjectExpected() + } + .property string DefaultValueCannotBeRead() + { + .get string System.Dynamic.ComInterop.Strings::get_DefaultValueCannotBeRead() + } + .property string InvalidArgumentValue() + { + .get string System.Dynamic.ComInterop.Strings::get_InvalidArgumentValue() + } + .property string MethodShouldNotBeCalled() + { + .get string System.Dynamic.ComInterop.Strings::get_MethodShouldNotBeCalled() + } + .property string RemovingUnregisteredEvent() + { + .get string System.Dynamic.ComInterop.Strings::get_RemovingUnregisteredEvent() + } + .property string RemovingUnregisteredHandler() + { + .get string System.Dynamic.ComInterop.Strings::get_RemovingUnregisteredHandler() + } + .property string SetComObjectDataFailed() + { + .get string System.Dynamic.ComInterop.Strings::get_SetComObjectDataFailed() + } + .property string UnsupportedEnumType() + { + .get string System.Dynamic.ComInterop.Strings::get_UnsupportedEnumType() + } + } + .class private sealed 'SynchronizedDictionary`2' + extends [mscorlib]System.Object + implements class [mscorlib]'System.Collections.Generic.IDictionary`2', class [mscorlib]'System.Collections.Generic.ICollection`1'>, class [mscorlib]'System.Collections.Generic.IEnumerable`1'>, [mscorlib]System.Collections.IEnumerable + { + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = { string('Item') } + .method assembly hidebysig specialname + instance class [mscorlib]'System.Collections.Generic.Dictionary`2' get_UnderlyingDictionary() + { + ret + } + .method public final virtual hidebysig newslot + instance void Add(!0 key, !1 'value') + { + ret + } + .method public final virtual hidebysig newslot + instance bool ContainsKey(!0 key) + { + ret + } + .method public final virtual hidebysig newslot specialname + instance class [mscorlib]'System.Collections.Generic.ICollection`1' get_Keys() + { + ret + } + .method public final virtual hidebysig newslot + instance bool Remove(!0 key) + { + ret + } + .method public final virtual hidebysig newslot + instance bool TryGetValue(!0 key, [out] !1& 'value') + { + ret + } + .method public final virtual hidebysig newslot specialname + instance class [mscorlib]'System.Collections.Generic.ICollection`1' get_Values() + { + ret + } + .method public final virtual hidebysig newslot specialname + instance !1 get_Item(!0 key) + { + ret + } + .method public final virtual hidebysig newslot specialname + instance void set_Item(!0 key, !1 'value') + { + ret + } + .method public final virtual hidebysig newslot + instance void Add(valuetype [mscorlib]'System.Collections.Generic.KeyValuePair`2' item) + { + ret + } + .method public final virtual hidebysig newslot + instance void Clear() + { + ret + } + .method public final virtual hidebysig newslot + instance bool Contains(valuetype [mscorlib]'System.Collections.Generic.KeyValuePair`2' item) + { + ret + } + .method public final virtual hidebysig newslot + instance void CopyTo(valuetype [mscorlib]'System.Collections.Generic.KeyValuePair`2'[] 'array', int32 arrayIndex) + { + ret + } + .method public final virtual hidebysig newslot specialname + instance int32 get_Count() + { + ret + } + .method public final virtual hidebysig newslot specialname + instance bool get_IsReadOnly() + { + ret + } + .method public final virtual hidebysig newslot + instance bool Remove(valuetype [mscorlib]'System.Collections.Generic.KeyValuePair`2' item) + { + ret + } + .method public final virtual hidebysig newslot + instance class [mscorlib]'System.Collections.Generic.IEnumerator`1'> GetEnumerator() + { + ret + } + .method private final virtual hidebysig newslot + instance class [mscorlib]System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + .override [mscorlib]System.Collections.IEnumerable::GetEnumerator + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance int32 Count() + { + .get instance int32 'System.Dynamic.ComInterop.SynchronizedDictionary`2'::get_Count() + } + .property instance bool IsReadOnly() + { + .get instance bool 'System.Dynamic.ComInterop.SynchronizedDictionary`2'::get_IsReadOnly() + } + .property instance !1 Item(!0) + { + .get instance !1 'System.Dynamic.ComInterop.SynchronizedDictionary`2'::get_Item(!0) + .set instance void 'System.Dynamic.ComInterop.SynchronizedDictionary`2'::set_Item(!0, !1) + } + .property instance class [mscorlib]'System.Collections.Generic.ICollection`1' Keys() + { + .get instance class [mscorlib]'System.Collections.Generic.ICollection`1' 'System.Dynamic.ComInterop.SynchronizedDictionary`2'::get_Keys() + } + .property instance class [mscorlib]'System.Collections.Generic.Dictionary`2' UnderlyingDictionary() + { + .get instance class [mscorlib]'System.Collections.Generic.Dictionary`2' 'System.Dynamic.ComInterop.SynchronizedDictionary`2'::get_UnderlyingDictionary() + } + .property instance class [mscorlib]'System.Collections.Generic.ICollection`1' Values() + { + .get instance class [mscorlib]'System.Collections.Generic.ICollection`1' 'System.Dynamic.ComInterop.SynchronizedDictionary`2'::get_Values() + } + } + .class private abstract sealed TypeExtensions + extends [mscorlib]System.Object + { + .method assembly static hidebysig + class [mscorlib]System.Delegate CreateDelegate(class [mscorlib]System.Reflection.MethodInfo methodInfo, class [mscorlib]System.Type delegateType, object target) + { + ret + } + .method assembly static hidebysig + !!0 CreateDelegate(class [mscorlib]System.Reflection.MethodInfo methodInfo, object target) + { + ret + } + } + .class private abstract sealed TypeUtils + extends [mscorlib]System.Object + { + .method assembly static hidebysig + class [mscorlib]System.Type GetNonNullableType(class [mscorlib]System.Type 'type') + { + ret + } + .method assembly static hidebysig + bool IsNullableType(class [mscorlib]System.Type 'type') + { + ret + } + .method assembly static hidebysig + bool AreReferenceAssignable(class [mscorlib]System.Type dest, class [mscorlib]System.Type src) + { + ret + } + .method assembly static hidebysig + bool AreAssignable(class [mscorlib]System.Type dest, class [mscorlib]System.Type src) + { + ret + } + .method assembly static hidebysig + bool IsImplicitlyConvertible(class [mscorlib]System.Type source, class [mscorlib]System.Type destination) + { + ret + } + .method assembly static hidebysig + bool IsImplicitlyConvertible(class [mscorlib]System.Type source, class [mscorlib]System.Type destination, bool considerUserDefined) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Reflection.MethodInfo GetUserDefinedCoercionMethod(class [mscorlib]System.Type convertFrom, class [mscorlib]System.Type convertToType, bool implicitOnly) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Reflection.MethodInfo FindConversionOperator(class [mscorlib]System.Reflection.MethodInfo[] methods, class [mscorlib]System.Type typeFrom, class [mscorlib]System.Type typeTo, bool implicitOnly) + { + ret + } + .field static assembly literal valuetype [mscorlib]System.Reflection.MethodAttributes PublicStatic = int32(0x00000016) + } + .class private UnknownArgBuilder + extends System.Dynamic.ComInterop.SimpleArgBuilder + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Type parameterType) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression Marshal(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression MarshalToRef(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression UnmarshalFromRef(class [System.Core]System.Linq.Expressions.Expression 'value') + { + ret + } + } + .class private abstract sealed UnsafeMethods + extends [mscorlib]System.Object + { + .permissionset demand = (2E 01 7F 53 79 73 74 65 6D 2E 53 65 63 75 72 69 74 79 2E 50 65 72 6D 69 73 73 69 6F 6E 73 2E 50 65 72 6D 69 73 73 69 6F 6E 53 65 74 41 74 74 72 69 62 75 74 65 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 11 01 54 02 0C 55 6E 72 65 73 74 72 69 63 74 65 64 01) + .method assembly static hidebysig + native int ConvertSByteByrefToPtr(int8& 'value') + { + ret + } + .method assembly static hidebysig + native int ConvertInt16ByrefToPtr(int16& 'value') + { + ret + } + .method public static hidebysig + native int ConvertInt32ByrefToPtr(int32& 'value') + { + ret + } + .method assembly static hidebysig + native int ConvertInt64ByrefToPtr(int64& 'value') + { + ret + } + .method assembly static hidebysig + native int ConvertByteByrefToPtr(uint8& 'value') + { + ret + } + .method assembly static hidebysig + native int ConvertUInt16ByrefToPtr(uint16& 'value') + { + ret + } + .method assembly static hidebysig + native int ConvertUInt32ByrefToPtr(uint32& 'value') + { + ret + } + .method assembly static hidebysig + native int ConvertUInt64ByrefToPtr(uint64& 'value') + { + ret + } + .method assembly static hidebysig + native int ConvertIntPtrByrefToPtr(native int& 'value') + { + ret + } + .method assembly static hidebysig + native int ConvertUIntPtrByrefToPtr(native uint& 'value') + { + ret + } + .method assembly static hidebysig + native int ConvertSingleByrefToPtr(float32& 'value') + { + ret + } + .method assembly static hidebysig + native int ConvertDoubleByrefToPtr(float64& 'value') + { + ret + } + .method assembly static hidebysig + native int ConvertDecimalByrefToPtr(valuetype [mscorlib]System.Decimal& 'value') + { + ret + } + .method public static hidebysig + native int ConvertVariantByrefToPtr(valuetype System.Dynamic.ComInterop.Variant& 'value') + { + ret + } + .method public static hidebysig + valuetype System.Dynamic.ComInterop.Variant GetVariantForObject(object obj) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('do not use this method') bool(true) } + ret + } + .method public static hidebysig + object GetObjectForVariant(valuetype System.Dynamic.ComInterop.Variant 'variant') + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('do not use this method') bool(true) } + ret + } + .method public static hidebysig + int32 IUnknownRelease(native int interfacePointer) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('do not use this method') bool(true) } + ret + } + .method public static hidebysig + void IUnknownReleaseNotZero(native int interfacePointer) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('do not use this method') bool(true) } + ret + } + .method public static hidebysig + int32 IDispatchInvoke(native int dispatchPointer, int32 memberDispId, valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.INVOKEKIND' 'flags', valuetype [mscorlib]'System.Runtime.InteropServices.ComTypes.DISPPARAMS'& dispParams, [out] valuetype System.Dynamic.ComInterop.Variant& result, [out] valuetype System.Dynamic.ComInterop.ExcepInfo& excepInfo, [out] uint32& argErr) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('do not use this method') bool(true) } + ret + } + .method public static hidebysig + native int GetIdsOfNamedParameters(class System.Dynamic.ComInterop.IDispatch dispatch, string[] names, int32 methodDispId, [out] valuetype [mscorlib]System.Runtime.InteropServices.GCHandle& pinningHandle) + { + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = { string('do not use this method') bool(true) } + ret + } + .field static assembly initonly native int NullInterfaceId + } + .class private abstract sealed UnsafeNativeMethods + extends [mscorlib]System.Object + { + .method assembly static hidebysig pinvokeimpl("dummy" winapi) + void VariantClear(native int 'variant') + { + } + } + .class private VarEnumSelector + extends [mscorlib]System.Object + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Type[] explicitArgTypes) + { + ret + } + .method assembly hidebysig specialname + instance class System.Dynamic.ComInterop.VariantBuilder[] get_VariantBuilders() + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Type GetManagedMarshalType(valuetype [mscorlib]System.Runtime.InteropServices.VarEnum varEnum) + { + ret + } + .property instance class System.Dynamic.ComInterop.VariantBuilder[] VariantBuilders() + { + .get instance class System.Dynamic.ComInterop.VariantBuilder[] System.Dynamic.ComInterop.VarEnumSelector::get_VariantBuilders() + } + } + .class private explicit sealed Variant + extends [mscorlib]System.ValueType + { + .method public virtual hidebysig + instance string ToString() + { + ret + } + .method assembly static hidebysig + bool IsPrimitiveType(valuetype [mscorlib]System.Runtime.InteropServices.VarEnum varEnum) + { + ret + } + .method public hidebysig + instance object ToObject() + { + ret + } + .method public hidebysig + instance void Clear() + { + ret + } + .method public hidebysig specialname + instance valuetype [mscorlib]System.Runtime.InteropServices.VarEnum get_VariantType() + { + ret + } + .method public hidebysig specialname + instance void set_VariantType(valuetype [mscorlib]System.Runtime.InteropServices.VarEnum 'value') + { + ret + } + .method assembly hidebysig specialname + instance bool get_IsEmpty() + { + ret + } + .method public hidebysig + instance void SetAsNull() + { + ret + } + .method public hidebysig + instance void SetAsIConvertible(class [mscorlib]System.IConvertible 'value') + { + ret + } + .method public hidebysig specialname + instance int8 get_AsI1() + { + ret + } + .method public hidebysig specialname + instance void set_AsI1(int8 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefI1(int8& 'value') + { + ret + } + .method public hidebysig specialname + instance int16 get_AsI2() + { + ret + } + .method public hidebysig specialname + instance void set_AsI2(int16 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefI2(int16& 'value') + { + ret + } + .method public hidebysig specialname + instance int32 get_AsI4() + { + ret + } + .method public hidebysig specialname + instance void set_AsI4(int32 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefI4(int32& 'value') + { + ret + } + .method public hidebysig specialname + instance int64 get_AsI8() + { + ret + } + .method public hidebysig specialname + instance void set_AsI8(int64 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefI8(int64& 'value') + { + ret + } + .method public hidebysig specialname + instance uint8 get_AsUi1() + { + ret + } + .method public hidebysig specialname + instance void set_AsUi1(uint8 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefUi1(uint8& 'value') + { + ret + } + .method public hidebysig specialname + instance uint16 get_AsUi2() + { + ret + } + .method public hidebysig specialname + instance void set_AsUi2(uint16 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefUi2(uint16& 'value') + { + ret + } + .method public hidebysig specialname + instance uint32 get_AsUi4() + { + ret + } + .method public hidebysig specialname + instance void set_AsUi4(uint32 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefUi4(uint32& 'value') + { + ret + } + .method public hidebysig specialname + instance uint64 get_AsUi8() + { + ret + } + .method public hidebysig specialname + instance void set_AsUi8(uint64 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefUi8(uint64& 'value') + { + ret + } + .method public hidebysig specialname + instance native int get_AsInt() + { + ret + } + .method public hidebysig specialname + instance void set_AsInt(native int 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefInt(native int& 'value') + { + ret + } + .method public hidebysig specialname + instance native uint get_AsUint() + { + ret + } + .method public hidebysig specialname + instance void set_AsUint(native uint 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefUint(native uint& 'value') + { + ret + } + .method public hidebysig specialname + instance bool get_AsBool() + { + ret + } + .method public hidebysig specialname + instance void set_AsBool(bool 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefBool(int16& 'value') + { + ret + } + .method public hidebysig specialname + instance int32 get_AsError() + { + ret + } + .method public hidebysig specialname + instance void set_AsError(int32 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefError(int32& 'value') + { + ret + } + .method public hidebysig specialname + instance float32 get_AsR4() + { + ret + } + .method public hidebysig specialname + instance void set_AsR4(float32 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefR4(float32& 'value') + { + ret + } + .method public hidebysig specialname + instance float64 get_AsR8() + { + ret + } + .method public hidebysig specialname + instance void set_AsR8(float64 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefR8(float64& 'value') + { + ret + } + .method public hidebysig specialname + instance valuetype [mscorlib]System.Decimal get_AsDecimal() + { + ret + } + .method public hidebysig specialname + instance void set_AsDecimal(valuetype [mscorlib]System.Decimal 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefDecimal(valuetype [mscorlib]System.Decimal& 'value') + { + ret + } + .method public hidebysig specialname + instance valuetype [mscorlib]System.Decimal get_AsCy() + { + ret + } + .method public hidebysig specialname + instance void set_AsCy(valuetype [mscorlib]System.Decimal 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefCy(int64& 'value') + { + ret + } + .method public hidebysig specialname + instance valuetype [mscorlib]System.DateTime get_AsDate() + { + ret + } + .method public hidebysig specialname + instance void set_AsDate(valuetype [mscorlib]System.DateTime 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefDate(float64& 'value') + { + ret + } + .method public hidebysig specialname + instance string get_AsBstr() + { + ret + } + .method public hidebysig specialname + instance void set_AsBstr(string 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefBstr(native int& 'value') + { + ret + } + .method public hidebysig specialname + instance object get_AsUnknown() + { + ret + } + .method public hidebysig specialname + instance void set_AsUnknown(object 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefUnknown(native int& 'value') + { + ret + } + .method public hidebysig specialname + instance object get_AsDispatch() + { + ret + } + .method public hidebysig specialname + instance void set_AsDispatch(object 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefDispatch(native int& 'value') + { + ret + } + .method public hidebysig specialname + instance object get_AsVariant() + { + ret + } + .method public hidebysig specialname + instance void set_AsVariant(object 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefVariant(valuetype System.Dynamic.ComInterop.Variant& 'value') + { + ret + } + .method public hidebysig + instance void SetAsByrefVariantIndirect(valuetype System.Dynamic.ComInterop.Variant& 'value') + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Reflection.PropertyInfo GetAccessor(valuetype [mscorlib]System.Runtime.InteropServices.VarEnum varType) + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Reflection.MethodInfo GetByrefSetter(valuetype [mscorlib]System.Runtime.InteropServices.VarEnum varType) + { + ret + } + + //This private field was generated by ASMMETA as a placeholder and does not exist in the actual assembly. + .field private int32 __Asmmeta_Private_Field + .property instance bool AsBool() + { + .get instance bool System.Dynamic.ComInterop.Variant::get_AsBool() + .set instance void System.Dynamic.ComInterop.Variant::set_AsBool(bool) + } + .property instance string AsBstr() + { + .get instance string System.Dynamic.ComInterop.Variant::get_AsBstr() + .set instance void System.Dynamic.ComInterop.Variant::set_AsBstr(string) + } + .property instance valuetype [mscorlib]System.Decimal AsCy() + { + .get instance valuetype [mscorlib]System.Decimal System.Dynamic.ComInterop.Variant::get_AsCy() + .set instance void System.Dynamic.ComInterop.Variant::set_AsCy(valuetype [mscorlib]System.Decimal) + } + .property instance valuetype [mscorlib]System.DateTime AsDate() + { + .get instance valuetype [mscorlib]System.DateTime System.Dynamic.ComInterop.Variant::get_AsDate() + .set instance void System.Dynamic.ComInterop.Variant::set_AsDate(valuetype [mscorlib]System.DateTime) + } + .property instance valuetype [mscorlib]System.Decimal AsDecimal() + { + .get instance valuetype [mscorlib]System.Decimal System.Dynamic.ComInterop.Variant::get_AsDecimal() + .set instance void System.Dynamic.ComInterop.Variant::set_AsDecimal(valuetype [mscorlib]System.Decimal) + } + .property instance object AsDispatch() + { + .get instance object System.Dynamic.ComInterop.Variant::get_AsDispatch() + .set instance void System.Dynamic.ComInterop.Variant::set_AsDispatch(object) + } + .property instance int32 AsError() + { + .get instance int32 System.Dynamic.ComInterop.Variant::get_AsError() + .set instance void System.Dynamic.ComInterop.Variant::set_AsError(int32) + } + .property instance int8 AsI1() + { + .get instance int8 System.Dynamic.ComInterop.Variant::get_AsI1() + .set instance void System.Dynamic.ComInterop.Variant::set_AsI1(int8) + } + .property instance int16 AsI2() + { + .get instance int16 System.Dynamic.ComInterop.Variant::get_AsI2() + .set instance void System.Dynamic.ComInterop.Variant::set_AsI2(int16) + } + .property instance int32 AsI4() + { + .get instance int32 System.Dynamic.ComInterop.Variant::get_AsI4() + .set instance void System.Dynamic.ComInterop.Variant::set_AsI4(int32) + } + .property instance int64 AsI8() + { + .get instance int64 System.Dynamic.ComInterop.Variant::get_AsI8() + .set instance void System.Dynamic.ComInterop.Variant::set_AsI8(int64) + } + .property instance native int AsInt() + { + .get instance native int System.Dynamic.ComInterop.Variant::get_AsInt() + .set instance void System.Dynamic.ComInterop.Variant::set_AsInt(native int) + } + .property instance float32 AsR4() + { + .get instance float32 System.Dynamic.ComInterop.Variant::get_AsR4() + .set instance void System.Dynamic.ComInterop.Variant::set_AsR4(float32) + } + .property instance float64 AsR8() + { + .get instance float64 System.Dynamic.ComInterop.Variant::get_AsR8() + .set instance void System.Dynamic.ComInterop.Variant::set_AsR8(float64) + } + .property instance uint8 AsUi1() + { + .get instance uint8 System.Dynamic.ComInterop.Variant::get_AsUi1() + .set instance void System.Dynamic.ComInterop.Variant::set_AsUi1(uint8) + } + .property instance uint16 AsUi2() + { + .get instance uint16 System.Dynamic.ComInterop.Variant::get_AsUi2() + .set instance void System.Dynamic.ComInterop.Variant::set_AsUi2(uint16) + } + .property instance uint32 AsUi4() + { + .get instance uint32 System.Dynamic.ComInterop.Variant::get_AsUi4() + .set instance void System.Dynamic.ComInterop.Variant::set_AsUi4(uint32) + } + .property instance uint64 AsUi8() + { + .get instance uint64 System.Dynamic.ComInterop.Variant::get_AsUi8() + .set instance void System.Dynamic.ComInterop.Variant::set_AsUi8(uint64) + } + .property instance native uint AsUint() + { + .get instance native uint System.Dynamic.ComInterop.Variant::get_AsUint() + .set instance void System.Dynamic.ComInterop.Variant::set_AsUint(native uint) + } + .property instance object AsUnknown() + { + .get instance object System.Dynamic.ComInterop.Variant::get_AsUnknown() + .set instance void System.Dynamic.ComInterop.Variant::set_AsUnknown(object) + } + .property instance object AsVariant() + { + .get instance object System.Dynamic.ComInterop.Variant::get_AsVariant() + .set instance void System.Dynamic.ComInterop.Variant::set_AsVariant(object) + } + .property instance bool IsEmpty() + { + .get instance bool System.Dynamic.ComInterop.Variant::get_IsEmpty() + } + .property instance valuetype [mscorlib]System.Runtime.InteropServices.VarEnum VariantType() + { + .get instance valuetype [mscorlib]System.Runtime.InteropServices.VarEnum System.Dynamic.ComInterop.Variant::get_VariantType() + .set instance void System.Dynamic.ComInterop.Variant::set_VariantType(valuetype [mscorlib]System.Runtime.InteropServices.VarEnum) + } + } + .class private VariantArgBuilder + extends System.Dynamic.ComInterop.SimpleArgBuilder + { + .method assembly hidebysig specialname + instance void .ctor(class [mscorlib]System.Type parameterType) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression Marshal(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression MarshalToRef(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly virtual hidebysig strict + instance class [System.Core]System.Linq.Expressions.Expression UnmarshalFromRef(class [System.Core]System.Linq.Expressions.Expression 'value') + { + ret + } + } + .class private abstract sealed VariantArray + extends [mscorlib]System.Object + { + .method assembly static hidebysig + class [System.Core]System.Linq.Expressions.MemberExpression GetStructField(class [System.Core]System.Linq.Expressions.ParameterExpression variantArray, int32 'field') + { + ret + } + .method assembly static hidebysig + class [mscorlib]System.Type GetStructType(int32 args) + { + ret + } + } + .class private sequential sealed VariantArray1 + extends [mscorlib]System.ValueType + { + .field public valuetype System.Dynamic.ComInterop.Variant Element0 + } + .class private sequential sealed VariantArray2 + extends [mscorlib]System.ValueType + { + .field public valuetype System.Dynamic.ComInterop.Variant Element0 + .field public valuetype System.Dynamic.ComInterop.Variant Element1 + } + .class private sequential sealed VariantArray4 + extends [mscorlib]System.ValueType + { + .field public valuetype System.Dynamic.ComInterop.Variant Element0 + .field public valuetype System.Dynamic.ComInterop.Variant Element1 + .field public valuetype System.Dynamic.ComInterop.Variant Element2 + .field public valuetype System.Dynamic.ComInterop.Variant Element3 + } + .class private sequential sealed VariantArray8 + extends [mscorlib]System.ValueType + { + .field public valuetype System.Dynamic.ComInterop.Variant Element0 + .field public valuetype System.Dynamic.ComInterop.Variant Element1 + .field public valuetype System.Dynamic.ComInterop.Variant Element2 + .field public valuetype System.Dynamic.ComInterop.Variant Element3 + .field public valuetype System.Dynamic.ComInterop.Variant Element4 + .field public valuetype System.Dynamic.ComInterop.Variant Element5 + .field public valuetype System.Dynamic.ComInterop.Variant Element6 + .field public valuetype System.Dynamic.ComInterop.Variant Element7 + } + .class private VariantBuilder + extends [mscorlib]System.Object + { + .method assembly hidebysig specialname + instance class [System.Core]System.Linq.Expressions.ParameterExpression get_TempVariable() + { + ret + } + .method assembly hidebysig specialname + instance void .ctor(valuetype [mscorlib]System.Runtime.InteropServices.VarEnum targetComType, class System.Dynamic.ComInterop.ArgBuilder builder) + { + ret + } + .method assembly hidebysig specialname + instance bool get_IsByRef() + { + ret + } + .method assembly hidebysig + instance class [System.Core]System.Linq.Expressions.Expression InitializeArgumentVariant(class [System.Core]System.Linq.Expressions.MemberExpression 'variant', class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .method assembly hidebysig + instance class [System.Core]System.Linq.Expressions.Expression Clear() + { + ret + } + .method assembly hidebysig + instance class [System.Core]System.Linq.Expressions.Expression UpdateFromReturn(class [System.Core]System.Linq.Expressions.Expression parameter) + { + ret + } + .property instance bool IsByRef() + { + .get instance bool System.Dynamic.ComInterop.VariantBuilder::get_IsByRef() + } + .property instance class [System.Core]System.Linq.Expressions.ParameterExpression TempVariable() + { + .get instance class [System.Core]System.Linq.Expressions.ParameterExpression System.Dynamic.ComInterop.VariantBuilder::get_TempVariable() + } + } +} diff --git a/ndp/fx/src/DynamicCom/System.Dynamic.ComInterop.csproj b/ndp/fx/src/DynamicCom/System.Dynamic.ComInterop.csproj new file mode 100644 index 0000000000..44c68ac63a --- /dev/null +++ b/ndp/fx/src/DynamicCom/System.Dynamic.ComInterop.csproj @@ -0,0 +1,184 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D} + Library + Properties + true + System.Dynamic.ComInterop + Microsoft.Dynamic.ComInterop + SAK + SAK + SAK + SAK + ..\..\..\..\Merlin\Main\Bin\Release\Microsoft.Dynamic.ComInterop.XML + 1591 + 2.0 + + + pdbonly + true + ..\..\..\..\Merlin\Main\Bin\FxCop\ + DEBUG;TRACE;SIGNED;MICROSOFT_SCRIPTING_CORE + prompt + 4 + true + true + ..\..\..\..\Merlin\Main\MSSharedLibKey.snk + true + true + -Microsoft.Usage#CA2209;+!Microsoft.Design#CA1012;-!Microsoft.Design#CA2210;+!Microsoft.Design#CA1040;+!Microsoft.Design#CA1005;+!Microsoft.Design#CA1020;-!Microsoft.Design#CA1021;+!Microsoft.Design#CA1010;+!Microsoft.Design#CA1011;+!Microsoft.Design#CA1009;+!Microsoft.Design#CA1050;+!Microsoft.Design#CA1026;+!Microsoft.Design#CA1019;+!Microsoft.Design#CA1031;+!Microsoft.Design#CA1047;+!Microsoft.Design#CA1000;+!Microsoft.Design#CA1048;+!Microsoft.Design#CA1051;+!Microsoft.Design#CA1002;+!Microsoft.Design#CA1061;+!Microsoft.Design#CA1006;+!Microsoft.Design#CA1046;+!Microsoft.Design#CA1045;+!Microsoft.Design#CA1065;+!Microsoft.Design#CA1038;+!Microsoft.Design#CA1008;+!Microsoft.Design#CA1028;+!Microsoft.Design#CA1064;-!Microsoft.Design#CA1004;+!Microsoft.Design#CA1035;+!Microsoft.Design#CA1063;+!Microsoft.Design#CA1032;+!Microsoft.Design#CA1023;+!Microsoft.Design#CA1033;+!Microsoft.Design#CA1039;+!Microsoft.Design#CA1016;+!Microsoft.Design#CA1014;+!Microsoft.Design#CA1017;+!Microsoft.Design#CA1018;+!Microsoft.Design#CA1027;+!Microsoft.Design#CA1059;+!Microsoft.Design#CA1060;+!Microsoft.Design#CA1034;+!Microsoft.Design#CA1013;+!Microsoft.Design#CA1036;+!Microsoft.Design#CA1044;+!Microsoft.Design#CA1041;+!Microsoft.Design#CA1025;+!Microsoft.Design#CA1052;+!Microsoft.Design#CA1053;+!Microsoft.Design#CA1057;+!Microsoft.Design#CA1058;+!Microsoft.Design#CA1001;+!Microsoft.Design#CA1049;+!Microsoft.Design#CA1054;+!Microsoft.Design#CA1056;+!Microsoft.Design#CA1055;+!Microsoft.Design#CA1030;+!Microsoft.Design#CA1003;+!Microsoft.Design#CA1007;+!Microsoft.Design#CA1043;+!Microsoft.Design#CA1024;+!Microsoft.Globalization#CA1301;+!Microsoft.Globalization#CA1302;+!Microsoft.Globalization#CA1308;+!Microsoft.Globalization#CA1306;+!Microsoft.Globalization#CA1304;+!Microsoft.Globalization#CA1305;+!Microsoft.Globalization#CA2101;+!Microsoft.Globalization#CA1300;+!Microsoft.Globalization#CA1307;+!Microsoft.Globalization#CA1309;+!Microsoft.Interoperability#CA1403;+!Microsoft.Interoperability#CA1406;+!Microsoft.Interoperability#CA1413;+!Microsoft.Interoperability#CA1402;+!Microsoft.Interoperability#CA1407;+!Microsoft.Interoperability#CA1404;+!Microsoft.Interoperability#CA1410;+!Microsoft.Interoperability#CA1411;+!Microsoft.Interoperability#CA1405;+!Microsoft.Interoperability#CA1409;+!Microsoft.Interoperability#CA1415;+!Microsoft.Interoperability#CA1408;+!Microsoft.Interoperability#CA1414;+!Microsoft.Interoperability#CA1412;+!Microsoft.Interoperability#CA1400;+!Microsoft.Interoperability#CA1401;+!Microsoft.Maintainability#CA1506;+!Microsoft.Maintainability#CA1502;+!Microsoft.Maintainability#CA1501;+!Microsoft.Maintainability#CA1505;+!Microsoft.Maintainability#CA1504;+!Microsoft.Maintainability#CA1500;+!Microsoft.Mobility#CA1600;+!Microsoft.Mobility#CA1601;-!Microsoft.Naming#CA1702;+!Microsoft.Naming#CA1700;+!Microsoft.Naming#CA1712;+!Microsoft.Naming#CA1713;+!Microsoft.Naming#CA1714;+!Microsoft.Naming#CA1709;-!Microsoft.Naming#CA1704;+!Microsoft.Naming#CA1708;+!Microsoft.Naming#CA1715;-!Microsoft.Naming#CA1710;+!Microsoft.Naming#CA1720;+!Microsoft.Naming#CA1707;+!Microsoft.Naming#CA1722;-!Microsoft.Naming#CA1711;+!Microsoft.Naming#CA1716;+!Microsoft.Naming#CA1717;+!Microsoft.Naming#CA1725;+!Microsoft.Naming#CA1719;+!Microsoft.Naming#CA1721;+!Microsoft.Naming#CA1701;+!Microsoft.Naming#CA1703;+!Microsoft.Naming#CA1724;-!Microsoft.Naming#CA1726;+!Microsoft.Performance#CA1809;+!Microsoft.Performance#CA1811;+!Microsoft.Performance#CA1812;+!Microsoft.Performance#CA1813;+!Microsoft.Performance#CA1823;+!Microsoft.Performance#CA1800;+!Microsoft.Performance#CA1805;+!Microsoft.Performance#CA1810;+!Microsoft.Performance#CA1824;+!Microsoft.Performance#CA1822;+!Microsoft.Performance#CA1815;+!Microsoft.Performance#CA1814;+!Microsoft.Performance#CA1819;+!Microsoft.Performance#CA1821;+!Microsoft.Performance#CA1804;+!Microsoft.Performance#CA1820;+!Microsoft.Performance#CA1802;+!Microsoft.Portability#CA1901;+!Microsoft.Portability#CA1900;+!Microsoft.Reliability#CA2001;+!Microsoft.Reliability#CA2002;+!Microsoft.Reliability#CA2003;+!Microsoft.Reliability#CA2004;+!Microsoft.Reliability#CA2006;+!Microsoft.Security#CA2116;+!Microsoft.Security#CA2117;+!Microsoft.Security#CA2105;+!Microsoft.Security#CA2115;+!Microsoft.Security#CA2102;+!Microsoft.Security#CA2104;+!Microsoft.Security#CA2122;+!Microsoft.Security#CA2114;+!Microsoft.Security#CA2123;+!Microsoft.Security#CA2111;+!Microsoft.Security#CA2108;+!Microsoft.Security#CA2107;+!Microsoft.Security#CA2103;+!Microsoft.Security#CA2118;+!Microsoft.Security#CA2109;+!Microsoft.Security#CA2119;+!Microsoft.Security#CA2106;+!Microsoft.Security#CA2112;+!Microsoft.Security#CA2120;+!Microsoft.Security#CA2121;+!Microsoft.Security#CA2126;+!Microsoft.Security#CA2124;+!Microsoft.Security#CA2127;+!Microsoft.Security#CA2128;+!Microsoft.Security#CA2129;+!Microsoft.Usage#CA2243;+!Microsoft.Usage#CA2236;+!Microsoft.Usage#CA1816;+!Microsoft.Usage#CA2227;+!Microsoft.Usage#CA2213;+!Microsoft.Usage#CA2216;+!Microsoft.Usage#CA2214;+!Microsoft.Usage#CA2222;+!Microsoft.Usage#CA1806;+!Microsoft.Usage#CA2217;+!Microsoft.Usage#CA2212;+!Microsoft.Usage#CA2219;+!Microsoft.Usage#CA2201;+!Microsoft.Usage#CA2228;+!Microsoft.Usage#CA2221;+!Microsoft.Usage#CA2220;+!Microsoft.Usage#CA2240;+!Microsoft.Usage#CA2229;+!Microsoft.Usage#CA2238;+!Microsoft.Usage#CA2207;+!Microsoft.Usage#CA2208;+!Microsoft.Usage#CA2235;+!Microsoft.Usage#CA2237;+!Microsoft.Usage#CA2232;+!Microsoft.Usage#CA2223;+!Microsoft.Usage#CA2211;+!Microsoft.Usage#CA2233;+!Microsoft.Usage#CA2225;+!Microsoft.Usage#CA2226;+!Microsoft.Usage#CA2231;+!Microsoft.Usage#CA2224;+!Microsoft.Usage#CA2218;+!Microsoft.Usage#CA2234;+!Microsoft.Usage#CA2239;+!Microsoft.Usage#CA2200;+!Microsoft.Usage#CA1801;+!Microsoft.Usage#CA2242;+!Microsoft.Usage#CA2205;+!Microsoft.Usage#CA2230 + + + true + full + false + ..\..\..\..\Merlin\Main\Bin\Debug\ + ..\..\..\..\Merlin\Main\Bin\Debug\Microsoft.Dynamic.ComInterop.xml + DEBUG;TRACE;SIGNED;MICROSOFT_SCRIPTING_CORE + prompt + 4 + true + false + true + ..\..\..\..\Merlin\Main\MSSharedLibKey.snk + true + + + pdbonly + true + ..\..\..\..\Merlin\Main\Bin\Release\ + ..\..\..\..\Merlin\Main\Bin\Release\Microsoft.Dynamic.ComInterop.XML + TRACE;SIGNED;MICROSOFT_SCRIPTING_CORE + prompt + 4 + true + false + true + ..\..\..\..\Merlin\Main\MSSharedLibKey.snk + true + 1929379840 + + + + System.Dynamic.ComInterop + Library + true + ECMA + true + true + true + true + true + $(DefineConstants) + System.Dynamic.ComInterop + 0688 + true + true + + + + + + + + + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4} + Microsoft.Scripting.Core + False + + + {8B0F1074-750E-4D64-BF23-A1E0F54261E5} + Microsoft.Scripting.ExtensionAttribute + + + + + + + + + + System.Dynamic.ComInterop + true + internal + true + System.Dynamic.ComInterop.SR + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(DefineConstants);PRODUCTION_BUILD + $(InternalPath)\sdk\ref\WinFX\$(WINFX_REFS_VERSION) + + + \ No newline at end of file diff --git a/ndp/fx/src/DynamicCom/System.Dynamic.ComInterop.csproj.vspscc b/ndp/fx/src/DynamicCom/System.Dynamic.ComInterop.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/ndp/fx/src/DynamicCom/System.Dynamic.ComInterop.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ArgBuilder.cs new file mode 100644 index 0000000000..0521874d66 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ArgBuilder.cs @@ -0,0 +1,56 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Linq.Expressions; + +namespace System.Dynamic.ComInterop { + /// + /// ArgBuilder provides an argument value used by the MethodBinder. One ArgBuilder exists for each + /// physical parameter defined on a method. + /// + /// Contrast this with ParameterWrapper which represents the logical argument passed to the method. + /// + internal abstract class ArgBuilder { + /// + /// Provides the Expression which provides the value to be passed to the argument. + /// + internal abstract Expression Marshal(Expression parameter); + + /// + /// Provides the Expression which provides the value to be passed to the argument. + /// This method is called when result is intended to be used ByRef. + /// + /// TODO: merge with Unwrap. They're distict because some of the variant + /// magic is happening in helpers in Variant.cs, rather than in Unwrap. + /// So UnwrapByRef has to duplicate this logic. The logic should just + /// move into Unwrap + /// + internal virtual Expression MarshalToRef(Expression parameter) { + return Marshal(parameter); + } + + /// + /// Provides an Expression which will update the provided value after a call to the method. + /// May return null if no update is required. + /// + internal virtual Expression UnmarshalFromRef(Expression newValue) { + return newValue; + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Assert.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Assert.cs new file mode 100644 index 0000000000..7679fd0c44 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Assert.cs @@ -0,0 +1,78 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#define DEBUG + +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Dynamic.ComInterop { + + // TODO: do we really need this class? + internal static class Assert { + + internal static Exception Unreachable { + get { + Debug.Assert(false, "Unreachable"); + return new InvalidOperationException("Code supposed to be unreachable"); + } + } + + [Conditional("DEBUG")] + internal static void NotNull(object var) { + Debug.Assert(var != null); + } + + [Conditional("DEBUG")] + internal static void NotNull(object var1, object var2) { + Debug.Assert(var1 != null && var2 != null); + } + + [Conditional("DEBUG")] + internal static void NotNull(object var1, object var2, object var3) { + Debug.Assert(var1 != null && var2 != null && var3 != null); + } + + [Conditional("DEBUG")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1025:ReplaceRepetitiveArgumentsWithParamsArray")] + internal static void NotNull(object var1, object var2, object var3, object var4) { + Debug.Assert(var1 != null && var2 != null && var3 != null && var4 != null); + } + + [Conditional("DEBUG")] + internal static void NotEmpty(string str) { + Debug.Assert(!String.IsNullOrEmpty(str)); + } + + [Conditional("DEBUG")] + internal static void NotEmpty(ICollection array) { + Debug.Assert(array != null && array.Count > 0); + } + + [Conditional("DEBUG")] + internal static void NotNullItems(IEnumerable items) where T : class { + Debug.Assert(items != null); + foreach (object item in items) { + Debug.Assert(item != null); + } + } + + [Conditional("DEBUG")] + internal static void IsTrue(Func predicate) { + ContractUtils.RequiresNotNull(predicate, "predicate"); + Debug.Assert(predicate()); + } + } +} diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/BoolArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/BoolArgBuilder.cs new file mode 100644 index 0000000000..c8fda30ce9 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/BoolArgBuilder.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; + +using System.Diagnostics; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + internal sealed class BoolArgBuilder : SimpleArgBuilder { + internal BoolArgBuilder(Type parameterType) + : base(parameterType) { + Debug.Assert(parameterType == typeof(bool)); + } + + internal override Expression MarshalToRef(Expression parameter) { + // parameter ? -1 : 0 + return Expression.Condition( + Marshal(parameter), + Expression.Constant((Int16)(-1)), + Expression.Constant((Int16)0) + ); + } + + internal override Expression UnmarshalFromRef(Expression value) { + //parameter = temp != 0 + return base.UnmarshalFromRef( + Expression.NotEqual( + value, + Expression.Constant((Int16)0) + ) + ); + } + } +} + +#endif \ No newline at end of file diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/BoundDispEvent.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/BoundDispEvent.cs new file mode 100644 index 0000000000..b74d350362 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/BoundDispEvent.cs @@ -0,0 +1,68 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Linq.Expressions; +using System.Runtime.CompilerServices; + +namespace System.Dynamic.ComInterop { + // TODO: Either this should be an IDO, or we need to return a delegate + // (instead of this object) when the event is requested. The latter + // approach seems preferrable, because then languages could use their + // normal syntax for adding to delegates. But it's tricky because we + // wouldn't have notification whether the event has handlers or not, so + // we'd have to always hook the COM event once the delegate is fetched + // + // Note: returning a delegate has an additional benefit: we wouldn't need + // SplatCallSite. + internal sealed class BoundDispEvent { + private object _rcw; + private Guid _sourceIid; + private int _dispid; + + internal BoundDispEvent(object rcw, Guid sourceIid, int dispid) { + _rcw = rcw; + _sourceIid = sourceIid; + _dispid = dispid; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates")] + [SpecialName] + public object op_AdditionAssignment(object func) { + return InPlaceAdd(func); + } + + [SpecialName] + public object InPlaceAdd(object func) { + ComEventSink comEventSink = ComEventSink.FromRuntimeCallableWrapper(_rcw, _sourceIid, true); + comEventSink.AddHandler(_dispid, func); + return this; + } + + [SpecialName] + public object InPlaceSubtract(object func) { + ComEventSink comEventSink = ComEventSink.FromRuntimeCallableWrapper(_rcw, _sourceIid, false); + if (comEventSink == null) { + throw Error.RemovingUnregisteredEvent(); + } + + comEventSink.RemoveHandler(_dispid, func); + return this; + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/CollectionExtensions.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/CollectionExtensions.cs new file mode 100644 index 0000000000..6318f9353a --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/CollectionExtensions.cs @@ -0,0 +1,69 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; + +namespace System.Dynamic.ComInterop { + internal static class CollectionExtensions { + + // Name needs to be different so it doesn't conflict with Enumerable.Select + internal static U[] Map(this ICollection collection, Func select) { + int count = collection.Count; + U[] result = new U[count]; + count = 0; + foreach (T t in collection) { + result[count++] = select(t); + } + return result; + } + + internal static bool Any(this IEnumerable source, Func predicate) { + foreach (T element in source) { + if (predicate(element)) { + return true; + } + } + return false; + } + + internal static T[] RemoveFirst(this T[] array) { + T[] result = new T[array.Length - 1]; + Array.Copy(array, 1, result, 0, result.Length); + return result; + } + + internal static T[] AddFirst(this IList list, T item) { + T[] res = new T[list.Count + 1]; + res[0] = item; + list.CopyTo(res, 1); + return res; + } + + internal static T[] AddLast(this IList list, T item) { + T[] res = new T[list.Count + 1]; + list.CopyTo(res, 0); + res[list.Count] = item; + return res; + } + } + + // TODO: Should we use this everywhere for empty arrays? + // my thought is, probably more hassle than its worth + internal static class EmptyArray { + internal static T[] Instance = new T[0]; + } +} diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComBinder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComBinder.cs new file mode 100644 index 0000000000..bafcbc7aa0 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComBinder.cs @@ -0,0 +1,124 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System; +using System.Dynamic.Binders; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Dynamic.Utils; + +// Will be moved into its own assembly soon +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Dynamic.ComInterop")] + +namespace System.Dynamic.ComInterop { + + // TODO: as long as COM is wrapping its IDOs, we don't actually need to + // take ref parameters, because it won't call fallback until later. + + /// + /// Provides helper methods to bind COM objects dynamically. + /// + public static class ComBinder { + + /// + /// Determines if an object is a COM object. + /// + /// The object to test. + /// True if the object is a COM object, False otherwise. + public static bool IsComObject(object value) { + return ComObject.IsComObject(value); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#")] + public static bool TryBindGetMember(GetMemberBinder binder, ref MetaObject instance) { + if (TryGetMetaObject(ref instance)) { + instance = instance.BindGetMember(binder); + return true; + } + return false; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#")] + public static bool TryBindSetMember(SetMemberBinder binder, ref MetaObject instance, MetaObject value) { + if (TryGetMetaObject(ref instance)) { + instance = instance.BindSetMember(binder, value); + return true; + } + return false; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#")] + public static bool TryBindInvokeMember(InvokeMemberBinder binder, ref MetaObject instance, MetaObject[] args) { + if (TryGetMetaObject(ref instance)) { + instance = instance.BindInvokeMember(binder, args); + return true; + } + return false; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#")] + public static bool TryBindGetIndex(GetIndexBinder binder, ref MetaObject instance, MetaObject[] args) { + if (TryGetMetaObject(ref instance)) { + instance = instance.BindGetIndex(binder, args); + return true; + } + return false; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#")] + public static bool TryBindSetIndex(SetIndexBinder binder, ref MetaObject instance, MetaObject[] args, MetaObject value) { + if (TryGetMetaObject(ref instance)) { + instance = instance.BindSetIndex(binder, args, value); + return true; + } + return false; + } + + public static IEnumerable GetDynamicMemberNames(object value) { + ContractUtils.RequiresNotNull(value, "value"); + ContractUtils.Requires(IsComObject(value), "value", Strings.ComObjectExpected); + + return ComObject.ObjectToComObject(value).MemberNames; + } + + // IEnumerable> is a standard idiom + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static IEnumerable> GetDynamicDataMembers(object value) { + ContractUtils.RequiresNotNull(value, "value"); + ContractUtils.Requires(IsComObject(value), "value", Strings.ComObjectExpected); + + return ComObject.ObjectToComObject(value).DataMembers; + } + + private static bool TryGetMetaObject(ref MetaObject instance) { + // If we're already a COM MO don't make a new one + // (we do this to prevent recursion if we call Fallback from COM) + if (instance is ComUnwrappedMetaObject) { + return false; + } + + if (IsComObject(instance.Value)) { + instance = new ComMetaObject(instance.Expression, instance.Restrictions, instance.Value); + return true; + } + + return false; + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComBinderHelpers.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComBinderHelpers.cs new file mode 100644 index 0000000000..8c6a3fc991 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComBinderHelpers.cs @@ -0,0 +1,104 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Diagnostics; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Linq.Expressions; +using System.Linq.Expressions.Compiler; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Dynamic.ComInterop { + internal static class ComBinderHelpers { + + internal static bool PreferPut(Type type) { + Debug.Assert(type != null); + + if (type.IsValueType || type.IsArray) return true; + + if (type == typeof(String) || + type == typeof(DBNull) || + type == typeof(System.Reflection.Missing) || + type == typeof(CurrencyWrapper)) { + + return true; + } else { + return false; + } + } + + internal static bool IsStrongBoxArg(MetaObject o) { + if (o.IsByRef) return false; + + Type t = o.LimitType; + return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(StrongBox<>); + } + + internal static MetaObject RewriteStrongBoxAsRef(CallSiteBinder action, MetaObject target, MetaObject[] args) { + Debug.Assert(action != null && target != null && args != null); + + var restrictions = target.Restrictions.Merge(Restrictions.Combine(args)); + + Expression[] argExpressions = new Expression[args.Length + 1]; + Type[] signatureTypes = new Type[args.Length + 3]; // args + CallSite, target, returnType + + signatureTypes[0] = typeof(CallSite); + + //TODO: we are not restricting on target type here, but in theory we could. + //It is a tradeoff between rule reuse and higher polymorphism of the site. + argExpressions[0] = target.Expression; + signatureTypes[1] = target.Expression.Type; + + for (int i = 0; i < args.Length; i++) { + MetaObject currArgument = args[i]; + if (IsStrongBoxArg(currArgument)) { + restrictions = restrictions.Merge(Restrictions.GetTypeRestriction(currArgument.Expression, currArgument.LimitType)); + + // we have restricted this argument to LimitType so we can convert and conversion will be trivial cast. + Expression boxedValueAccessor = Expression.Field( + Helpers.Convert( + currArgument.Expression, + currArgument.LimitType + ), + currArgument.LimitType.GetField("Value") + ); + + argExpressions[i + 1] = boxedValueAccessor; + signatureTypes[i + 2] = boxedValueAccessor.Type.MakeByRefType(); + } else { + argExpressions[i + 1] = currArgument.Expression; + signatureTypes[i + 2] = currArgument.Expression.Type; + } + } + + // Last signatureType is the return value + signatureTypes[signatureTypes.Length - 1] = typeof(object); + + return new MetaObject( + Expression.MakeDynamic( + Expression.GetDelegateType(signatureTypes), + action, + argExpressions + ), + restrictions + ); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComDispIds.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComDispIds.cs new file mode 100644 index 0000000000..a05a639f5d --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComDispIds.cs @@ -0,0 +1,26 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +namespace System.Dynamic.ComInterop { + internal static class ComDispIds { + internal const int DISPID_VALUE = 0; + internal const int DISPID_PROPERTYPUT = -3; + internal const int DISPID_NEWENUM = -4; + } +} + +#endif \ No newline at end of file diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComEventDesc.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComEventDesc.cs new file mode 100644 index 0000000000..ef47acc312 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComEventDesc.cs @@ -0,0 +1,25 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +namespace System.Dynamic.ComInterop { + internal class ComEventDesc { + internal Guid sourceIID; + internal int dispid; + }; +} + +#endif \ No newline at end of file diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComEventSink.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComEventSink.cs new file mode 100644 index 0000000000..b215310cda --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComEventSink.cs @@ -0,0 +1,350 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace System.Dynamic.ComInterop { + /// + /// This class implements an event sink for a particular RCW. + /// Unlike the implementation of events in TlbImp'd assemblies, + /// we will create only one event sink per RCW (theoretically RCW might have + /// several ComEventSink evenk sinks - but all these implement different source intefaces). + /// Each ComEventSink contains a list of ComEventSinkMethod objects - which represent + /// a single method on the source interface an a multicast delegate to redirect + /// the calls. Notice that we are chaining multicast delegates so that same + /// ComEventSinkMedhod can invoke multiple event handlers). + /// + /// ComEventSink implements an IDisposable pattern to Unadvise from the connection point. + /// Typically, when RCW is finalized the corresponding Dispose will be triggered by + /// ComEventSinksContainer finalizer. Notice that lifetime of ComEventSinksContainer + /// is bound to the lifetime of the RCW. + /// + internal sealed class ComEventSink : MarshalByRefObject, IReflect, IDisposable { + + #region private fields + + private Guid _sourceIid; + private ComTypes.IConnectionPoint _connectionPoint; + private int _adviseCookie; + private List _comEventSinkMethods; + private object _lockObject = new object(); // We cannot lock on ComEventSink since it causes a DoNotLockOnObjectsWithWeakIdentity warning + + #endregion + + #region private classes + + /// + /// Contains a methods DISPID (in a string formatted of "[DISPID=N]" + /// and a chained list of delegates to invoke + /// + private class ComEventSinkMethod { + public string _name; + public Delegate _target; + } + + delegate object ComEventCallHandler(object[] args); + + private class ComEventCallContext { + private static SplatCallSite _site = new SplatCallSite(new ComInvokeAction()); + + public object _func; + + public ComEventCallContext(object func) { + _func = func; + } + + public object Call(object[] args) { + return _site.Invoke(args.AddFirst(_func)); + } + } + + #endregion + + #region ctor + + private ComEventSink(object rcw, Guid sourceIid) { + Initialize(rcw, sourceIid); + } + + #endregion + + private void Initialize(object rcw, Guid sourceIid) { + _sourceIid = sourceIid; + _adviseCookie = -1; + + Debug.Assert(_connectionPoint == null, "re-initializing event sink w/o unadvising from connection point"); + + ComTypes.IConnectionPointContainer cpc = rcw as ComTypes.IConnectionPointContainer; + if (cpc == null) + throw Error.COMObjectDoesNotSupportEvents(); + + cpc.FindConnectionPoint(ref _sourceIid, out _connectionPoint); + if (_connectionPoint == null) + throw Error.COMObjectDoesNotSupportSourceInterface(); + + // Read the comments for ComEventSinkProxy about why we need it + ComEventSinkProxy proxy = new ComEventSinkProxy(this, _sourceIid); + _connectionPoint.Advise(proxy.GetTransparentProxy(), out _adviseCookie); + } + + #region static methods + + public static ComEventSink FromRuntimeCallableWrapper(object rcw, Guid sourceIid, bool createIfNotFound) { + List comEventSinks = ComEventSinksContainer.FromRuntimeCallableWrapper(rcw, createIfNotFound); + ComEventSink comEventSink = null; + + lock (comEventSinks) { + + foreach (ComEventSink sink in comEventSinks) { + if (sink._sourceIid == sourceIid) { + comEventSink = sink; + break; + } else if (sink._sourceIid == Guid.Empty) { + // we found a ComEventSink object that + // was previously disposed. Now we will reuse it. + sink.Initialize(rcw, sourceIid); + comEventSink = sink; + } + } + + if (comEventSink == null && createIfNotFound == true) { + comEventSink = new ComEventSink(rcw, sourceIid); + comEventSinks.Add(comEventSink); + } + } + + return comEventSink; + } + + #endregion + + public void AddHandler(int dispid, object func) { + string name = String.Format(CultureInfo.InvariantCulture, "[DISPID={0}]", dispid); + ComEventCallHandler handler = new ComEventCallContext(func).Call; + + lock (_lockObject) { + ComEventSinkMethod sinkMethod; + sinkMethod = FindSinkMethod(name); + + if (sinkMethod == null) { + if (_comEventSinkMethods == null) { + _comEventSinkMethods = new List(); + } + + sinkMethod = new ComEventSinkMethod(); + sinkMethod._name = name; + _comEventSinkMethods.Add(sinkMethod); + } + + sinkMethod._target = Delegate.Combine(sinkMethod._target, handler); + } + } + + public void RemoveHandler(int dispid, object func) { + + string name = String.Format(CultureInfo.InvariantCulture, "[DISPID={0}]", dispid); + + lock (_lockObject) { + + ComEventSinkMethod sinkEntry = FindSinkMethod(name); + if (sinkEntry == null) + throw Error.RemovingUnregisteredHandler(); + + // Remove the delegate from multicast delegate chain. + // We will need to find the delegate that corresponds + // to the func handler we want to remove. This will be + // easy since we Target property of the delegate object + // is a ComEventCallContext object. + Delegate[] delegates = sinkEntry._target.GetInvocationList(); + foreach (Delegate d in delegates) { + ComEventCallContext callContext = d.Target as ComEventCallContext; + if (callContext != null && callContext._func.Equals(func)) { + sinkEntry._target = Delegate.Remove(sinkEntry._target, d); + break; + } + } + + // If the delegates chain is empty - we can remove + // corresponding ComEvenSinkEntry + if (sinkEntry._target == null) + _comEventSinkMethods.Remove(sinkEntry); + + // We can Unadvise from the ConnectionPoint if no more sink entries + // are registered for this interface + //(calling Dispose will call IConnectionPoint.Unadvise). + if (_comEventSinkMethods.Count == 0) { + // notice that we do not remove + // ComEventSinkEntry from the list, we will re-use this data structure + // if a new handler needs to be attached. + Dispose(); + } + } + } + + public object ExecuteHandler(string name, object[] args) { + ComEventSinkMethod site; + site = FindSinkMethod(name); + + if (site != null && site._target != null) { + // TODO: currently we only pass parameters by value + // TODO: however modifiers might specify that some params + // TODO: are by ref. Should we wrap those into IStrongBox-like objects? + return site._target.DynamicInvoke(new object[] { args }); + } + + return null; + } + + #region IReflect + + #region Unimplemented members + + public FieldInfo GetField(string name, BindingFlags bindingAttr) { + return null; + } + + public FieldInfo[] GetFields(BindingFlags bindingAttr) { + return new FieldInfo[0]; + } + + public MemberInfo[] GetMember(string name, BindingFlags bindingAttr) { + return new MemberInfo[0]; + } + + public MemberInfo[] GetMembers(BindingFlags bindingAttr) { + return new MemberInfo[0]; + } + + public MethodInfo GetMethod(string name, BindingFlags bindingAttr) { + return null; + } + + public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) { + return null; + } + + public MethodInfo[] GetMethods(BindingFlags bindingAttr) { + return new MethodInfo[0]; + } + + public PropertyInfo GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) { + return null; + } + + public PropertyInfo GetProperty(string name, BindingFlags bindingAttr) { + return null; + } + + public PropertyInfo[] GetProperties(BindingFlags bindingAttr) { + return new PropertyInfo[0]; + } + + #endregion + + public Type UnderlyingSystemType { + get { + return typeof(object); + } + } + + public object InvokeMember( + string name, + BindingFlags invokeAttr, + Binder binder, + object target, + object[] args, + ParameterModifier[] modifiers, + CultureInfo culture, + string[] namedParameters) { + + return ExecuteHandler(name, args); + } + + #endregion + + #region IDisposable + + public void Dispose() { + DisposeAll(); + GC.SuppressFinalize(this); + } + + #endregion + + ~ComEventSink() { + DisposeAll(); + } + + private void DisposeAll() { + if (_connectionPoint == null) { + return; + } + + if (_adviseCookie == -1) { + return; + } + + try { + _connectionPoint.Unadvise(_adviseCookie); + + // _connectionPoint has entered the CLR in the constructor + // for this object and hence its ref counter has been increased + // by us. We have not exposed it to other components and + // hence it is safe to call RCO on it w/o worrying about + // killing the RCW for other objects that link to it. + Marshal.ReleaseComObject(_connectionPoint); + } catch (Exception ex) { + // if something has gone wrong, and the object is no longer attached to the CLR, + // the Unadvise is going to throw. In this case, since we're going away anyway, + // we'll ignore the failure and quietly go on our merry way. + COMException exCOM = ex as COMException; + if (exCOM != null && exCOM.ErrorCode == ComHresults.CONNECT_E_NOCONNECTION) { + Debug.Assert(false, "IConnectionPoint::Unadvise returned CONNECT_E_NOCONNECTION."); + throw; + } + } finally { + _connectionPoint = null; + _adviseCookie = -1; + _sourceIid = Guid.Empty; + } + } + + private ComEventSinkMethod FindSinkMethod(string name) { + if (_comEventSinkMethods == null) + return null; + + ComEventSinkMethod site; + site = _comEventSinkMethods.Find( + delegate(ComEventSinkMethod element) { + return element._name == name; + } + ); + + return site; + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComEventSinkProxy.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComEventSinkProxy.cs new file mode 100644 index 0000000000..548058743f --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComEventSinkProxy.cs @@ -0,0 +1,129 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Globalization; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Remoting; +using System.Runtime.Remoting.Messaging; +using System.Runtime.Remoting.Proxies; +using System.Dynamic.Utils; +using System.Security.Permissions; + +namespace System.Dynamic.ComInterop { + /// + /// ComEventSinkProxy class is responsible for handling QIs for sourceIid + /// on instances of ComEventSink. + /// + /// Background: When a COM even sink advises to a connection point it is + /// supposed to hand over the dispinterface. Now, some hosts will trust + /// the COM client to pass the correct pointer, but some will not. + /// E.g. Excel's implementation of Connection Points will not cause a + /// QI on the pointer that has been passed, however Word will QI the + /// pointer to return the required interface. + /// + /// ComEventSink does not, strongly speaking, implements the interface + /// that it claims to implement - it is just "faking" it by using IReflect. + /// Thus, Word's QIs on the pointer passed to ICP::Advise would fail. To + /// prevent this we take advangate of RealProxy's ability of + /// "dressing up" like other classes and hence successfully respond to QIs + /// for interfaces that it does not really support( it is OK to say + /// "I implement this interface" for event sinks only since the common + /// practice is to use IDistpach.Invoke when calling into event sinks). + /// + internal sealed class ComEventSinkProxy : RealProxy { + + private Guid _sinkIid; + private ComEventSink _sink; + private static readonly MethodInfo _methodInfoInvokeMember = typeof(ComEventSink).GetMethod("InvokeMember", BindingFlags.Instance | BindingFlags.Public); + + #region ctors + + private ComEventSinkProxy() { + } + + public ComEventSinkProxy(ComEventSink sink, Guid sinkIid) + : base(typeof(ComEventSink)) { + _sink = sink; + _sinkIid = sinkIid; + } + + #endregion + + #region Base Class Overrides + + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] // to match the base method + public override IntPtr SupportsInterface(ref Guid iid) { + // if the iid is the sink iid, we ask the base class for an rcw to IDispatch + if (iid == _sinkIid) { + IntPtr retVal = IntPtr.Zero; + retVal = Marshal.GetIDispatchForObject(_sink); + return retVal; + } + + return base.SupportsInterface(ref iid); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] // TODO: fix + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] // to match the base method + public override IMessage Invoke(IMessage msg) { + ContractUtils.RequiresNotNull(msg, "msg"); + + //Only know how to handle method calls (property and fields accessors count as methods) + IMethodCallMessage methodCallMessage = msg as IMethodCallMessage; + if (methodCallMessage == null) + throw new NotSupportedException(); + + // ComEventSink.InvokeMember is handled specially. + // The reason we need to do that is due to how namedParameters arg (7th element in the IMethodCallMessage.Args array) + // is marshalled when called through RealProxy.Invoke. + // In RealProxy.Invoke namedParameters is typed as object[], while InvokeMember expects it to be string[]. + // If we simply let this call go through (with RemotingServices.ExecuteMessage) + // we get an InvalidCastException when Remoting tries to pass namedParameters (of type object[]) + // to InvokeMember (which expects namedParameters to be string[]). + // Since we don't use namedParameters in ComEventSink.InvokeMember - we simply ignore it here + // and pass-in null. + MethodInfo methodInfo = (MethodInfo)methodCallMessage.MethodBase; + if (methodInfo == _methodInfoInvokeMember) { + object retVal = null; + + try { + // InvokeMember(string name, BindingFlags bindingFlags, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) + retVal = ((IReflect)_sink).InvokeMember( + /*name*/ methodCallMessage.Args[0] as string, + /*bindingFlags*/ (BindingFlags)methodCallMessage.Args[1], + /*binder*/ methodCallMessage.Args[2] as Binder, + /*target*/ null, + /*args*/ methodCallMessage.Args[4] as object[], + /*modifiers*/ methodCallMessage.Args[5] as ParameterModifier[], + /*culture*/ methodCallMessage.Args[6] as CultureInfo, + /*namedParameters*/ null); + } catch (Exception ex) { + return new ReturnMessage(ex.InnerException, methodCallMessage); + } + + return new ReturnMessage(retVal, methodCallMessage.Args, methodCallMessage.ArgCount, null, methodCallMessage); + } + + return RemotingServices.ExecuteMessage(_sink, methodCallMessage); + } + + #endregion + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComEventSinksContainer.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComEventSinksContainer.cs new file mode 100644 index 0000000000..caab5f5827 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComEventSinksContainer.cs @@ -0,0 +1,80 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.InteropServices; + +namespace System.Dynamic.ComInterop { + /// + /// ComEventSinksContainer is just a regular list with a finalizer. + /// This list is usually attached as a custom data for RCW object and + /// is finalized whenever RCW is finalized. + /// + internal class ComEventSinksContainer : List, IDisposable { + private ComEventSinksContainer() { + } + + private static readonly object _ComObjectEventSinksKey = new object(); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")] + public static ComEventSinksContainer FromRuntimeCallableWrapper(object rcw, bool createIfNotFound) { + // !!! Marshal.Get/SetComObjectData has a LinkDemand for UnmanagedCode which will turn into + // a full demand. We need to avoid this by making this method SecurityCritical + object data = Marshal.GetComObjectData(rcw, _ComObjectEventSinksKey); + if (data != null || createIfNotFound == false) { + return (ComEventSinksContainer)data; + } + + lock (_ComObjectEventSinksKey) { + data = Marshal.GetComObjectData(rcw, _ComObjectEventSinksKey); + if (data != null) { + return (ComEventSinksContainer)data; + } + + ComEventSinksContainer comEventSinks = new ComEventSinksContainer(); + if (!Marshal.SetComObjectData(rcw, _ComObjectEventSinksKey, comEventSinks)) { + throw Error.SetComObjectDataFailed(); + } + + return comEventSinks; + } + } + + #region IDisposable Members + + public void Dispose() { + DisposeAll(); + GC.SuppressFinalize(this); + } + + #endregion + + private void DisposeAll() { + foreach (ComEventSink sink in this) { + sink.Dispose(); + } + } + + ~ComEventSinksContainer() { + DisposeAll(); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComFallbackMetaObject.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComFallbackMetaObject.cs new file mode 100644 index 0000000000..0282721965 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComFallbackMetaObject.cs @@ -0,0 +1,80 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Linq.Expressions; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Diagnostics; + +namespace System.Dynamic.ComInterop { + // + // ComFallbackMetaObject just delegates everything to the binder. + // + // Note that before performing FallBack on a ComObject we need to unwrap it so that + // binder would act upon the actual object (typically Rcw) + // + // Also: we don't need to implement these for any operations other than those + // supported by ComBinder + internal class ComFallbackMetaObject : MetaObject { + internal ComFallbackMetaObject(Expression expression, Restrictions restrictions, object arg) + : base(expression, restrictions, arg) { + } + + public override MetaObject BindGetIndex(GetIndexBinder binder, MetaObject[] indexes) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackGetIndex(UnwrapSelf(), indexes); + } + + public override MetaObject BindSetIndex(SetIndexBinder binder, MetaObject[] indexes, MetaObject value) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackSetIndex(UnwrapSelf(), indexes, value); + } + + public override MetaObject BindGetMember(GetMemberBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackGetMember(UnwrapSelf()); + } + + public override MetaObject BindInvokeMember(InvokeMemberBinder binder, MetaObject[] args) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackInvokeMember(UnwrapSelf(), args); + } + + public override MetaObject BindSetMember(SetMemberBinder binder, MetaObject value) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackSetMember(UnwrapSelf(), value); + } + + protected virtual ComUnwrappedMetaObject UnwrapSelf() { + return new ComUnwrappedMetaObject( + ComObject.RcwFromComObject(Expression), + Restrictions.Merge(Restrictions.GetTypeRestriction(Expression, LimitType)), + ((ComObject)Value).RuntimeCallableWrapper + ); + } + } + + // This type exists as a signal type, so ComBinder knows not to try to bind + // again when we're trying to fall back + internal sealed class ComUnwrappedMetaObject : MetaObject { + internal ComUnwrappedMetaObject(Expression expression, Restrictions restrictions, object value) + : base(expression, restrictions, value) { + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComHresults.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComHresults.cs new file mode 100644 index 0000000000..14bda91b06 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComHresults.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +namespace System.Dynamic.ComInterop { + + internal static class ComHresults { + + internal const int S_OK = 0; + + internal const int CONNECT_E_NOCONNECTION = unchecked((int)0x80040200); + + internal const int DISP_E_UNKNOWNINTERFACE = unchecked((int)0x80020001); + internal const int DISP_E_MEMBERNOTFOUND = unchecked((int)0x80020003); + internal const int DISP_E_PARAMNOTFOUND = unchecked((int)0x80020004); + internal const int DISP_E_TYPEMISMATCH = unchecked((int)0x80020005); + internal const int DISP_E_UNKNOWNNAME = unchecked((int)0x80020006); // GetIDsOfName + internal const int DISP_E_NONAMEDARGS = unchecked((int)0x80020007); + internal const int DISP_E_BADVARTYPE = unchecked((int)0x80020008); + internal const int DISP_E_EXCEPTION = unchecked((int)0x80020009); + internal const int DISP_E_OVERFLOW = unchecked((int)0x8002000A); + internal const int DISP_E_BADINDEX = unchecked((int)0x8002000B); // GetTypeInfo + internal const int DISP_E_UNKNOWNLCID = unchecked((int)0x8002000C); + internal const int DISP_E_ARRAYISLOCKED = unchecked((int)0x8002000D); // VariantClear + internal const int DISP_E_BADPARAMCOUNT = unchecked((int)0x8002000E); + internal const int DISP_E_PARAMNOTOPTIONAL = unchecked((int)0x8002000F); + + internal const int E_NOINTERFACE = unchecked((int)0x80004002); + internal const int E_FAIL = unchecked((int)0x80004005); + + internal const int TYPE_E_LIBNOTREGISTERED = unchecked((int)0x8002801D); + + internal static bool IsSuccess(int hresult) { + return hresult >= 0; + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComInterop.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComInterop.cs new file mode 100644 index 0000000000..b6d5611dc3 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComInterop.cs @@ -0,0 +1,92 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Runtime.InteropServices; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace System.Dynamic.ComInterop { + + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIDispatch), + Guid("00020400-0000-0000-C000-000000000046") + ] + internal interface IDispatchForReflection { + } + + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("00020400-0000-0000-C000-000000000046"), + ] + internal interface IDispatch { + + [PreserveSig] + int TryGetTypeInfoCount(out uint pctinfo); + + [PreserveSig] + int TryGetTypeInfo(uint iTInfo, int lcid, out IntPtr info); + + [PreserveSig] + int TryGetIDsOfNames( + ref Guid iid, + [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] + string[] names, + uint cNames, + int lcid, + [Out] + [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4, SizeParamIndex = 2)] + int[] rgDispId); + + [PreserveSig] + int TryInvoke( + int dispIdMember, + ref Guid riid, + int lcid, + ComTypes.INVOKEKIND wFlags, + ref ComTypes.DISPPARAMS pDispParams, + out object VarResult, + out ComTypes.EXCEPINFO pExcepInfo, + out uint puArgErr); + } + + /// + /// Layout of the IDispatch vtable + /// + internal enum IDispatchMethodIndices { + IUnknown_QueryInterface, + IUnknown_AddRef, + IUnknown_Release, + + IDispatch_GetTypeInfoCount , + IDispatch_GetTypeInfo, + IDispatch_GetIDsOfNames, + IDispatch_Invoke + } + + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("B196B283-BAB4-101A-B69C-00AA00341D07") + ] + internal interface IProvideClassInfo { + void GetClassInfo(out IntPtr info); + } + +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComInvokeAction.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComInvokeAction.cs new file mode 100644 index 0000000000..f11b2fe52e --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComInvokeAction.cs @@ -0,0 +1,46 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Dynamic.Binders; + +namespace System.Dynamic.ComInterop { + class ComInvokeAction : InvokeBinder { + public override object CacheIdentity { + get { return this; } + } + + internal ComInvokeAction(params ArgumentInfo[] arguments) + : base(arguments) { + } + + public override int GetHashCode() { + return base.GetHashCode(); + } + + public override bool Equals(object obj) { + return base.Equals(obj as ComInvokeAction); + } + + public override MetaObject FallbackInvoke(MetaObject target, MetaObject[] args, MetaObject errorSuggestion) { + return errorSuggestion ?? MetaObject.CreateThrow(target, args, typeof(NotSupportedException), "Cannot perform call"); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComInvokeBinder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComInvokeBinder.cs new file mode 100644 index 0000000000..089e9b6a0d --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComInvokeBinder.cs @@ -0,0 +1,523 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace System.Dynamic.ComInterop { + internal sealed class ComInvokeBinder { + private readonly ComMethodDesc _methodDesc; + private readonly Expression _method; // ComMethodDesc to be called + private readonly Expression _dispatch; // IDispatch + + private readonly IList _arguments; + private readonly MetaObject[] _args; + private readonly Expression _instance; + + private Restrictions _restrictions; + + private VarEnumSelector _varEnumSelector; + private string[] _keywordArgNames; + private int _totalExplicitArgs; // Includes the individial elements of ArgumentKind.Dictionary (if any) + + private ParameterExpression _dispatchObject; + private ParameterExpression _dispatchPointer; + private ParameterExpression _dispId; + private ParameterExpression _dispParams; + private ParameterExpression _paramVariants; + private ParameterExpression _invokeResult; + private ParameterExpression _returnValue; + private ParameterExpression _dispIdsOfKeywordArgsPinned; + private ParameterExpression _propertyPutDispId; + + internal ComInvokeBinder(IList arguments, MetaObject[] args, Restrictions restrictions, Expression method, Expression dispatch, ComMethodDesc methodDesc) { + ContractUtils.RequiresNotNull(arguments, "arguments"); + ContractUtils.RequiresNotNull(args, "args"); + ContractUtils.RequiresNotNull(method, "method"); + ContractUtils.RequiresNotNull(dispatch, "dispatch"); + ContractUtils.Requires(TypeUtils.AreReferenceAssignable(typeof(ComMethodDesc), method.Type), "method"); + ContractUtils.Requires(TypeUtils.AreReferenceAssignable(typeof(IDispatch), dispatch.Type), "dispatch"); + + _method = method; + _dispatch = dispatch; + _methodDesc = methodDesc; + + _arguments = arguments; + _args = args; + _restrictions = restrictions; + + // Set Instance to some value so that CallBinderHelper has the right number of parameters to work with + _instance = dispatch; + } + + private ParameterExpression DispatchObjectVariable { + get { return EnsureVariable(ref _dispatchObject, typeof(IDispatch), "dispatchObject"); } + } + + private ParameterExpression DispatchPointerVariable { + get { return EnsureVariable(ref _dispatchPointer, typeof(IntPtr), "dispatchPointer"); } + } + + private ParameterExpression DispIdVariable { + get { return EnsureVariable(ref _dispId, typeof(int), "dispId"); } + } + + private ParameterExpression DispParamsVariable { + get { return EnsureVariable(ref _dispParams, typeof(ComTypes.DISPPARAMS), "dispParams"); } + } + + private ParameterExpression InvokeResultVariable { + get { return EnsureVariable(ref _invokeResult, typeof(Variant), "invokeResult"); } + } + + private ParameterExpression ReturnValueVariable { + get { return EnsureVariable(ref _returnValue, typeof(object), "returnValue"); } + } + + private ParameterExpression DispIdsOfKeywordArgsPinnedVariable { + get { return EnsureVariable(ref _dispIdsOfKeywordArgsPinned, typeof(GCHandle), "dispIdsOfKeywordArgsPinned"); } + } + + private ParameterExpression PropertyPutDispIdVariable { + get { return EnsureVariable(ref _propertyPutDispId, typeof(int), "propertyPutDispId"); } + } + + private ParameterExpression ParamVariantsVariable { + get { + if (_paramVariants == null) { + _paramVariants = Expression.Variable(VariantArray.GetStructType(_args.Length), "paramVariants"); + } + return _paramVariants; + } + } + + private static ParameterExpression EnsureVariable(ref ParameterExpression var, Type type, string name) { + if (var != null) { + return var; + } + return var = Expression.Variable(type, name); + } + + private static Type MarshalType(MetaObject mo) { + Type marshalType = mo.LimitType; + if (mo.IsByRef) { + // None just means that no value was supplied. + if (marshalType == typeof(Null)) { + marshalType = mo.Expression.Type; + } + marshalType = marshalType.MakeByRefType(); + } + return marshalType; + } + + internal MetaObject Invoke() { + _keywordArgNames = GetArgumentNames(); + // will not include implicit instance argument (if any) + Type[] explicitArgTypes = _args.Map(a => a.LimitType); + Type[] marshalArgTypes = _args.Map(a => MarshalType(a)); + + Expression[] explicitArgExprs = _args.Map(a => a.Expression); + _totalExplicitArgs = explicitArgTypes.Length; + + _varEnumSelector = new VarEnumSelector(marshalArgTypes); + + // We already tested the instance, so no need to test it again + for (int i = 0; i < explicitArgTypes.Length; i++) { + _restrictions = _restrictions.Merge(Restrictions.GetTypeRestriction(explicitArgExprs[i], explicitArgTypes[i])); + } + + return new MetaObject( + CreateScope(MakeIDispatchInvokeTarget()), + Restrictions.Combine(_args).Merge(_restrictions) + ); + } + + private static void AddNotNull(List list, ParameterExpression var) { + if (var != null) list.Add(var); + } + + private Expression CreateScope(Expression expression) { + List vars = new List(); + AddNotNull(vars, _dispatchObject); + AddNotNull(vars, _dispatchPointer); + AddNotNull(vars, _dispId); + AddNotNull(vars, _dispParams); + AddNotNull(vars, _paramVariants); + AddNotNull(vars, _invokeResult); + AddNotNull(vars, _returnValue); + AddNotNull(vars, _dispIdsOfKeywordArgsPinned); + AddNotNull(vars, _propertyPutDispId); + return vars.Count > 0 ? Expression.Block(vars, expression) : expression; + } + + private Expression GenerateTryBlock() { + // + // Declare variables + // + ParameterExpression excepInfo = Expression.Variable(typeof(ExcepInfo), "excepInfo"); + ParameterExpression argErr = Expression.Variable(typeof(uint), "argErr"); + ParameterExpression hresult = Expression.Variable(typeof(int), "hresult"); + + List tryStatements = new List(); + Expression expr; + + if (_keywordArgNames.Length > 0) { + string[] names = _keywordArgNames.AddFirst(_methodDesc.Name); + + tryStatements.Add( + Expression.Assign( + Expression.Field( + DispParamsVariable, + typeof(ComTypes.DISPPARAMS).GetField("rgdispidNamedArgs") + ), + Expression.Call(typeof(UnsafeMethods).GetMethod("GetIdsOfNamedParameters"), + DispatchObjectVariable, + Expression.Constant(names), + DispIdVariable, + DispIdsOfKeywordArgsPinnedVariable + ) + ) + ); + } + + // + // Marshal the arguments to Variants + // + // For a call like this: + // comObj.Foo(100, 101, 102, x=123, z=125) + // DISPPARAMS needs to be setup like this: + // cArgs: 5 + // cNamedArgs: 2 + // rgArgs: 123, 125, 102, 101, 100 + // rgdispidNamedArgs: dx, dz (the dispids of x and z respectively) + + Expression[] parameters = MakeArgumentExpressions(); + + int reverseIndex = _varEnumSelector.VariantBuilders.Length - 1; + int positionalArgs = _varEnumSelector.VariantBuilders.Length - _keywordArgNames.Length; // args passed by position order and not by name + for (int i = 0; i < _varEnumSelector.VariantBuilders.Length; i++, reverseIndex--) { + int variantIndex; + if (i >= positionalArgs) { + // Named arguments are in order at the start of rgArgs + variantIndex = i - positionalArgs; + } else { + // Positial arguments are in reverse order at the tail of rgArgs + variantIndex = reverseIndex; + } + VariantBuilder variantBuilder = _varEnumSelector.VariantBuilders[i]; + + Expression marshal = variantBuilder.InitializeArgumentVariant( + VariantArray.GetStructField(ParamVariantsVariable, variantIndex), + parameters[i + 1] + ); + + if (marshal != null) { + tryStatements.Add(marshal); + } + } + + + // + // Call Invoke + // + + ComTypes.INVOKEKIND invokeKind; + if (_methodDesc.IsPropertyPut) { + if (_methodDesc.IsPropertyPutRef) { + invokeKind = ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF; + } else { + invokeKind = ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT; + } + } else { + // INVOKE_PROPERTYGET should only be needed for COM objects without typeinfo, where we might have to treat properties as methods + invokeKind = ComTypes.INVOKEKIND.INVOKE_FUNC | ComTypes.INVOKEKIND.INVOKE_PROPERTYGET; + } + + MethodCallExpression invoke = Expression.Call( + typeof(UnsafeMethods).GetMethod("IDispatchInvoke"), + DispatchPointerVariable, + DispIdVariable, + Expression.Constant(invokeKind), + DispParamsVariable, + InvokeResultVariable, + excepInfo, + argErr + ); + + expr = Expression.Assign(hresult, invoke); + tryStatements.Add(expr); + + // + // ComRuntimeHelpers.CheckThrowException(hresult, excepInfo, argErr, ThisParameter); + // + expr = Expression.Call( + typeof(ComRuntimeHelpers).GetMethod("CheckThrowException"), + hresult, + excepInfo, + argErr, + Expression.Constant(_methodDesc.Name, typeof(string)) + ); + tryStatements.Add(expr); + + // + // _returnValue = (ReturnType)_invokeResult.ToObject(); + // + Expression invokeResultObject = + Expression.Call( + InvokeResultVariable, + typeof(Variant).GetMethod("ToObject")); + + VariantBuilder[] variants = _varEnumSelector.VariantBuilders; + + Expression[] parametersForUpdates = MakeArgumentExpressions(); + tryStatements.Add(Expression.Assign(ReturnValueVariable, invokeResultObject)); + + for (int i = 0, n = variants.Length; i < n; i++) { + Expression updateFromReturn = variants[i].UpdateFromReturn(parametersForUpdates[i + 1]); + if (updateFromReturn != null) { + tryStatements.Add(updateFromReturn); + } + } + + tryStatements.Add(Expression.Empty()); + + return Expression.Block(new[] { excepInfo, argErr, hresult }, tryStatements); + } + + private Expression GenerateFinallyBlock() { + List finallyStatements = new List(); + + // + // UnsafeMethods.IUnknownRelease(dispatchPointer); + // + finallyStatements.Add( + Expression.Call( + typeof(UnsafeMethods).GetMethod("IUnknownRelease"), + DispatchPointerVariable + ) + ); + + // + // Clear memory allocated for marshalling + // + for (int i = 0, n = _varEnumSelector.VariantBuilders.Length; i < n; i++) { + Expression clear = _varEnumSelector.VariantBuilders[i].Clear(); + if (clear != null) { + finallyStatements.Add(clear); + } + } + + // + // _invokeResult.Clear() + // + + finallyStatements.Add( + Expression.Call( + InvokeResultVariable, + typeof(Variant).GetMethod("Clear") + ) + ); + + // + // _dispIdsOfKeywordArgsPinned.Free() + // + if (_dispIdsOfKeywordArgsPinned != null) { + finallyStatements.Add( + Expression.Call( + DispIdsOfKeywordArgsPinnedVariable, + typeof(GCHandle).GetMethod("Free") + ) + ); + } + + finallyStatements.Add(Expression.Empty()); + return Expression.Block(finallyStatements); + } + + /// + /// Create a stub for the target of the optimized lopop. + /// + /// + private Expression MakeIDispatchInvokeTarget() { + Debug.Assert(_varEnumSelector.VariantBuilders.Length == _totalExplicitArgs); + + List exprs = new List(); + + // + // _dispId = ((DispCallable)this).ComMethodDesc.DispId; + // + exprs.Add( + Expression.Assign( + DispIdVariable, + Expression.Property(_method, typeof(ComMethodDesc).GetProperty("DispId")) + ) + ); + + // + // _dispParams.rgvararg = RuntimeHelpers.UnsafeMethods.ConvertVariantByrefToPtr(ref _paramVariants._element0) + // + if (_totalExplicitArgs != 0) { + exprs.Add( + Expression.Assign( + Expression.Field( + DispParamsVariable, + typeof(ComTypes.DISPPARAMS).GetField("rgvarg") + ), + Expression.Call( + typeof(UnsafeMethods).GetMethod("ConvertVariantByrefToPtr"), + VariantArray.GetStructField(ParamVariantsVariable, 0) + ) + ) + ); + } + + // + // _dispParams.cArgs = ; + // + exprs.Add( + Expression.Assign( + Expression.Field( + DispParamsVariable, + typeof(ComTypes.DISPPARAMS).GetField("cArgs") + ), + Expression.Constant(_totalExplicitArgs) + ) + ); + + if (_methodDesc.IsPropertyPut) { + // + // dispParams.cNamedArgs = 1; + // dispParams.rgdispidNamedArgs = RuntimeHelpers.UnsafeMethods.GetNamedArgsForPropertyPut() + // + exprs.Add( + Expression.Assign( + Expression.Field( + DispParamsVariable, + typeof(ComTypes.DISPPARAMS).GetField("cNamedArgs") + ), + Expression.Constant(1) + ) + ); + + exprs.Add( + Expression.Assign( + PropertyPutDispIdVariable, + Expression.Constant(ComDispIds.DISPID_PROPERTYPUT) + ) + ); + + exprs.Add( + Expression.Assign( + Expression.Field( + DispParamsVariable, + typeof(ComTypes.DISPPARAMS).GetField("rgdispidNamedArgs") + ), + Expression.Call( + typeof(UnsafeMethods).GetMethod("ConvertInt32ByrefToPtr"), + PropertyPutDispIdVariable + ) + ) + ); + } else { + // + // _dispParams.cNamedArgs = N; + // + exprs.Add( + Expression.Assign( + Expression.Field( + DispParamsVariable, + typeof(ComTypes.DISPPARAMS).GetField("cNamedArgs") + ), + Expression.Constant(_keywordArgNames.Length) + ) + ); + } + + // + // _dispatchObject = _dispatch + // _dispatchPointer = Marshal.GetIDispatchForObject(_dispatchObject); + // + + exprs.Add(Expression.Assign(DispatchObjectVariable, _dispatch)); + + exprs.Add( + Expression.Assign( + DispatchPointerVariable, + Expression.Call( + typeof(Marshal).GetMethod("GetIDispatchForObject"), + DispatchObjectVariable + ) + ) + ); + + Expression tryStatements = GenerateTryBlock(); + Expression finallyStatements = GenerateFinallyBlock(); + + exprs.Add(Expression.TryFinally(tryStatements, finallyStatements)); + + exprs.Add(ReturnValueVariable); + var vars = new List(); + foreach (var variant in _varEnumSelector.VariantBuilders) { + if (variant.TempVariable != null) { + vars.Add(variant.TempVariable); + } + } + return Expression.Block(vars, exprs); + } + + /// + /// Gets expressions to access all the arguments. This includes the instance argument. + /// + private Expression[] MakeArgumentExpressions() { + if (_instance == null) { + return MetaObject.GetExpressions(_args); + } + + Expression[] res = new Expression[_args.Length + 1]; + res[0] = _instance; + for (int i = 0; i < _args.Length; i++) { + res[i + 1] = _args[i].Expression; + } + return res; + } + + /// + /// Gets all of the argument names. The instance argument is not included + /// + private string[] GetArgumentNames() { + // Get names of named arguments + if (_arguments.Count == 0) { + return new string[0]; + } else { + var result = new List(); + foreach (ArgumentInfo arg in _arguments) { + if (arg.ArgumentType == ArgumentKind.Named) { + result.Add(((NamedArgumentInfo)arg).Name); + } + } + return result.ToArray(); + } + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComMetaObject.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComMetaObject.cs new file mode 100644 index 0000000000..a7a5eb97d8 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComMetaObject.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Linq.Expressions; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Collections.Generic; + +namespace System.Dynamic.ComInterop { + + // Note: we only need to support the operations used by ComBinder + internal class ComMetaObject : MetaObject { + internal ComMetaObject(Expression expression, Restrictions restrictions, object arg) + : base(expression, restrictions, arg) { + } + + public override MetaObject BindInvokeMember(InvokeMemberBinder binder, MetaObject[] args) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.Defer(args.AddFirst(WrapSelf())); + } + + public override MetaObject BindGetMember(GetMemberBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.Defer(WrapSelf()); + } + + public override MetaObject BindSetMember(SetMemberBinder binder, MetaObject value) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.Defer(WrapSelf(), value); + } + + public override MetaObject BindGetIndex(GetIndexBinder binder, MetaObject[] indexes) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.Defer(WrapSelf(), indexes); + } + + public override MetaObject BindSetIndex(SetIndexBinder binder, MetaObject[] indexes, MetaObject value) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.Defer(WrapSelf(), indexes.AddLast(value)); + } + + private MetaObject WrapSelf() { + return new MetaObject( + ComObject.RcwToComObject(Expression), + Restrictions.GetExpressionRestriction( + Expression.AndAlso( + Expression.NotEqual( + Helpers.Convert(Expression, typeof(object)), + Expression.Constant(null) + ), + Expression.Call( + typeof(System.Runtime.InteropServices.Marshal).GetMethod("IsComObject"), + Helpers.Convert(Expression, typeof(object)) + ) + ) + ) + ); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComMethodDesc.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComMethodDesc.cs new file mode 100644 index 0000000000..e9b80e016e --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComMethodDesc.cs @@ -0,0 +1,154 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Diagnostics; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using System.Globalization; +using Marshal = System.Runtime.InteropServices.Marshal; + +namespace System.Dynamic.ComInterop { + + internal sealed class ComMethodDesc { + + private readonly bool _hasTypeInfo; + private readonly int _memid; // this is the member id extracted from FUNCDESC.memid + private readonly string _name; + internal readonly INVOKEKIND InvokeKind; + private readonly ComParamDesc[] _parameters; + + private ComMethodDesc(int dispId) { + _memid = dispId; + } + + internal ComMethodDesc(string name, int dispId) + : this(dispId) { + // no ITypeInfo constructor + _name = name; + } + + internal ComMethodDesc(string name, int dispId, INVOKEKIND invkind) + : this(name, dispId) { + InvokeKind = invkind; + } + + internal ComMethodDesc(ITypeInfo typeInfo, FUNCDESC funcDesc) + : this(funcDesc.memid) { + + _hasTypeInfo = true; + InvokeKind = funcDesc.invkind; + + int cNames; + string[] rgNames = new string[1 + funcDesc.cParams]; + typeInfo.GetNames(_memid, rgNames, rgNames.Length, out cNames); + if (IsPropertyPut && rgNames[rgNames.Length - 1] == null) { + rgNames[rgNames.Length - 1] = "value"; + cNames++; + } + Debug.Assert(cNames == rgNames.Length); + _name = rgNames[0]; + + _parameters = new ComParamDesc[funcDesc.cParams]; + + int offset = 0; + for (int i = 0; i < funcDesc.cParams; i++) { + ELEMDESC elemDesc = (ELEMDESC)Marshal.PtrToStructure( + new IntPtr(funcDesc.lprgelemdescParam.ToInt64() + offset), + typeof(ELEMDESC)); + + _parameters[i] = new ComParamDesc(ref elemDesc, rgNames[1 + i]); + + offset += Marshal.SizeOf(typeof(ELEMDESC)); + } + } + + internal bool HasTypeInfo { + get { + return _hasTypeInfo; + } + } + + internal string DispIdString { + get { + return String.Format(CultureInfo.InvariantCulture, "[DISPID={0}]", _memid); + } + } + + public string Name { + get { + Debug.Assert(_name != null); + return _name; + } + } + + public int DispId { + get { return _memid; } + } + + public bool IsPropertyGet { + get { + return (InvokeKind & INVOKEKIND.INVOKE_PROPERTYGET) != 0; + } + } + + public bool IsDataMember { + get { + return DispId != ComDispIds.DISPID_NEWENUM && IsPropertyGet; + } + } + + public bool IsPropertyPut { + get { + return (InvokeKind & (INVOKEKIND.INVOKE_PROPERTYPUT | INVOKEKIND.INVOKE_PROPERTYPUTREF)) != 0; + } + } + + public bool IsPropertyPutRef { + get { + return (InvokeKind & INVOKEKIND.INVOKE_PROPERTYPUTREF) != 0; + } + } + + internal ComParamDesc[] Parameters { + get { + Debug.Assert((_parameters != null) == _hasTypeInfo); + return _parameters; + } + } + + public bool HasByrefOrOutParameters { + get { + if (!_hasTypeInfo) { + // We have just a dispId and not ITypeInfo to get the list of parameters. + // We have to assume that all parameters are In parameters. The user will explicitly have + // to pass StrongBox objects to represent ref or out parameters. + return false; + } + + for (int i = 0; i < _parameters.Length; i++) { + if (_parameters[i].ByReference || _parameters[i].IsOut) { + return true; + } + } + + return false; + } + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComObject.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComObject.cs new file mode 100644 index 0000000000..f70eae297e --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComObject.cs @@ -0,0 +1,131 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace System.Dynamic.ComInterop { + /// + /// This is a helper class for runtime-callable-wrappers of COM instances. We create one instance of this type + /// for every generic RCW instance. + /// + internal class ComObject : IDynamicObject { + /// + /// The runtime-callable wrapper + /// + private readonly object _rcw; + + internal ComObject(object rcw) { + Debug.Assert(ComObject.IsComObject(rcw)); + _rcw = rcw; + } + + internal object RuntimeCallableWrapper { + get { + return _rcw; + } + } + + private readonly static object _ComObjectInfoKey = new object(); + + /// + /// This is the factory method to get the ComObject corresponding to an RCW + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] + public static ComObject ObjectToComObject(object rcw) { + Debug.Assert(ComObject.IsComObject(rcw)); + + // Marshal.Get/SetComObjectData has a LinkDemand for UnmanagedCode which will turn into + // a full demand. We could avoid this by making this method SecurityCritical + object data = Marshal.GetComObjectData(rcw, _ComObjectInfoKey); + if (data != null) { + return (ComObject)data; + } + + lock (_ComObjectInfoKey) { + data = Marshal.GetComObjectData(rcw, _ComObjectInfoKey); + if (data != null) { + return (ComObject)data; + } + + ComObject comObjectInfo = CreateComObject(rcw); + if (!Marshal.SetComObjectData(rcw, _ComObjectInfoKey, comObjectInfo)) { + throw Error.SetComObjectDataFailed(); + } + + return comObjectInfo; + } + } + + // Expression that unwraps ComObject + internal static MemberExpression RcwFromComObject(Expression comObject) { + Debug.Assert(comObject != null && typeof(ComObject).IsAssignableFrom(comObject.Type), "must be ComObject"); + + return Expression.Property( + Helpers.Convert(comObject, typeof(ComObject)), + typeof(ComObject).GetProperty("RuntimeCallableWrapper", BindingFlags.NonPublic | BindingFlags.Instance) + ); + } + + // Expression that finds or creates a ComObject that corresponds to given Rcw + internal static MethodCallExpression RcwToComObject(Expression rcw) { + return Expression.Call( + typeof(ComObject).GetMethod("ObjectToComObject"), + Helpers.Convert(rcw, typeof(object)) + ); + } + + private static ComObject CreateComObject(object rcw) { + IDispatch dispatchObject = rcw as IDispatch; + if (dispatchObject != null) { + // We can do method invocations on IDispatch objects + return new IDispatchComObject(dispatchObject); + } + + // There is not much we can do in this case + return new ComObject(rcw); + } + + internal virtual IEnumerable MemberNames { + get { return EmptyArray.Instance; } + } + + internal virtual IEnumerable> DataMembers { + get { return EmptyArray>.Instance; } + } + + MetaObject IDynamicObject.GetMetaObject(Expression parameter) { + return new ComFallbackMetaObject(parameter, Restrictions.Empty, this); + } + + private static readonly Type ComObjectType = typeof(object).Assembly.GetType("System.__ComObject"); + + internal static bool IsComObject(object obj) { + // we can't use System.Runtime.InteropServices.Marshal.IsComObject(obj) since it doesn't work in partial trust + return obj != null && ComObjectType.IsAssignableFrom(obj.GetType()); + } + + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComParamDesc.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComParamDesc.cs new file mode 100644 index 0000000000..e07bac8295 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComParamDesc.cs @@ -0,0 +1,206 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Diagnostics; +using System.Linq.Expressions; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using Marshal = System.Runtime.InteropServices.Marshal; +using VarEnum = System.Runtime.InteropServices.VarEnum; + +namespace System.Dynamic.ComInterop { + + /// + /// The parameter description of a method defined in a type library + /// + internal sealed class ComParamDesc { + # region private fields + + private readonly bool _isOut; // is an output parameter? + private readonly bool _isOpt; // is an optional parameter? + private readonly bool _byRef; // is a reference or pointer parameter? + private readonly bool _isArray; + private readonly VarEnum _vt; + private readonly string _name; + private readonly Type _type; + private readonly object _defaultValue; + + # endregion + + /// + /// Creates a representation for the paramter of a COM method + /// + internal ComParamDesc(ref ELEMDESC elemDesc, string name) { + // Ensure _defaultValue is set to DBNull.Value regardless of whether or not the + // default value is extracted from the parameter description. Failure to do so + // yields a runtime exception in the ToString() function. + _defaultValue = DBNull.Value; + + if (!String.IsNullOrEmpty(name)) { + // This is a parameter, not a return value + this._isOut = (elemDesc.desc.paramdesc.wParamFlags & PARAMFLAG.PARAMFLAG_FOUT) != 0; + this._isOpt = (elemDesc.desc.paramdesc.wParamFlags & PARAMFLAG.PARAMFLAG_FOPT) != 0; + // TODO: The PARAMDESCEX struct has a memory issue that needs to be resolved. For now, we ignore it. + //_defaultValue = PARAMDESCEX.GetDefaultValue(ref elemDesc.desc.paramdesc); + } + + _name = name; + _vt = (VarEnum)elemDesc.tdesc.vt; + TYPEDESC typeDesc = elemDesc.tdesc; + while (true) { + if (_vt == VarEnum.VT_PTR) { + this._byRef = true; + } else if (_vt == VarEnum.VT_ARRAY) { + this._isArray = true; + } else { + break; + } + + TYPEDESC childTypeDesc = (TYPEDESC)Marshal.PtrToStructure(typeDesc.lpValue, typeof(TYPEDESC)); + _vt = (VarEnum)childTypeDesc.vt; + typeDesc = childTypeDesc; + } + + VarEnum vtWithoutByref = _vt; + if ((_vt & VarEnum.VT_BYREF) != 0) { + vtWithoutByref = (_vt & ~VarEnum.VT_BYREF); + _byRef = true; + } + + _type = GetTypeForVarEnum(vtWithoutByref); + } + + internal struct PARAMDESCEX { + private ulong _cByte; + private Variant _varDefaultValue; + + internal void Dummy() { + _cByte = 0; + throw Error.MethodShouldNotBeCalled(); + } + + internal static object GetDefaultValue(ref PARAMDESC paramdesc) { + if ((paramdesc.wParamFlags & PARAMFLAG.PARAMFLAG_FHASDEFAULT) == 0) { + return DBNull.Value; + } + + PARAMDESCEX varValue = (PARAMDESCEX)Marshal.PtrToStructure(paramdesc.lpVarValue, typeof(PARAMDESCEX)); + if (varValue._cByte != (ulong)(Marshal.SizeOf((typeof(PARAMDESCEX))))) { + throw Error.DefaultValueCannotBeRead(); + } + + return varValue._varDefaultValue.ToObject(); + } + } + + private static Type GetTypeForVarEnum(VarEnum vt) { + Type type; + + switch (vt) { + // VarEnums which can be used in VARIANTs, but which cannot occur in a TYPEDESC + case VarEnum.VT_EMPTY: + case VarEnum.VT_NULL: + case VarEnum.VT_RECORD: + throw Error.UnexpectedVarEnum(vt); + + // VarEnums which are not used in VARIANTs, but which can occur in a TYPEDESC + case VarEnum.VT_VOID: + type = null; + break; + +#if DISABLE // TODO: WTypes.h indicates that these cannot be used in VARIANTs, but Type.InvokeMember seems to allow it + case VarEnum.VT_I8: + case VarEnum.UI8: +#endif + case VarEnum.VT_HRESULT: + type = typeof(int); + break; + + case ((VarEnum)37): // VT_INT_PTR: + case VarEnum.VT_PTR: + type = typeof(IntPtr); + break; + + case ((VarEnum)38): // VT_UINT_PTR: + type = typeof(UIntPtr); + break; + + case VarEnum.VT_SAFEARRAY: + case VarEnum.VT_CARRAY: + type = typeof(Array); + break; + + case VarEnum.VT_LPSTR: + case VarEnum.VT_LPWSTR: + type = typeof(string); + break; + + case VarEnum.VT_USERDEFINED: + type = typeof(object); + break; + + // For VarEnums that can be used in VARIANTs and well as TYPEDESCs, just use VarEnumSelector + default: + type = VarEnumSelector.GetManagedMarshalType(vt); + break; + } + + return type; + } + + public override string ToString() { + StringBuilder result = new StringBuilder(); + if (_isOpt) { + result.Append("[Optional] "); + } + + if (_isOut) { + result.Append("[out]"); + } + + result.Append(_type.Name); + + if (_isArray) { + result.Append("[]"); + } + + if (_byRef) { + result.Append("&"); + } + + result.Append(" "); + result.Append(_name); + + if (_defaultValue != DBNull.Value) { + result.Append("="); + result.Append(_defaultValue.ToString()); + } + + return result.ToString(); + } + + public bool IsOut { + get { return _isOut; } + } + + public bool ByReference { + get { return _byRef; } + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComRuntimeHelpers.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComRuntimeHelpers.cs new file mode 100644 index 0000000000..227216a3aa --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComRuntimeHelpers.cs @@ -0,0 +1,688 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Diagnostics; +using System.Dynamic.Utils; +using System.Linq.Expressions; +using System.Linq.Expressions.Compiler; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using ComTypes = System.Runtime.InteropServices.ComTypes; +using System.Security; +using System.Security.Permissions; + +namespace System.Dynamic.ComInterop { + + internal static class ComRuntimeHelpers { + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#")] + public static void CheckThrowException(int hresult, ref ExcepInfo excepInfo, uint argErr, string message) { + if (ComHresults.IsSuccess(hresult)) { + return; + } + + switch (hresult) { + case ComHresults.DISP_E_BADPARAMCOUNT: + // The number of elements provided to DISPPARAMS is different from the number of arguments + // accepted by the method or property. + throw Error.DispBadParamCount(message); + + case ComHresults.DISP_E_BADVARTYPE: + //One of the arguments in rgvarg is not a valid variant type. + break; + + case ComHresults.DISP_E_EXCEPTION: + // The application needs to raise an exception. In this case, the structure passed in pExcepInfo + // should be filled in. + throw excepInfo.GetException(); + + case ComHresults.DISP_E_MEMBERNOTFOUND: + // The requested member does not exist, or the call to Invoke tried to set the value of a + // read-only property. + throw Error.DispMemberNotFound(message); + + case ComHresults.DISP_E_NONAMEDARGS: + // This implementation of IDispatch does not support named arguments. + throw Error.DispNoNamedArgs(message); + + case ComHresults.DISP_E_OVERFLOW: + // One of the arguments in rgvarg could not be coerced to the specified type. + throw Error.DispOverflow(message); + + case ComHresults.DISP_E_PARAMNOTFOUND: + // One of the parameter DISPIDs does not correspond to a parameter on the method. In this case, + // puArgErr should be set to the first argument that contains the error. + break; + + case ComHresults.DISP_E_TYPEMISMATCH: + // One or more of the arguments could not be coerced. The index within rgvarg of the first + // parameter with the incorrect type is returned in the puArgErr parameter. + throw Error.DispTypeMismatch(argErr, message); + + case ComHresults.DISP_E_UNKNOWNINTERFACE: + // The interface identifier passed in riid is not IID_NULL. + break; + + case ComHresults.DISP_E_UNKNOWNLCID: + // The member being invoked interprets string arguments according to the LCID, and the + // LCID is not recognized. + break; + + case ComHresults.DISP_E_PARAMNOTOPTIONAL: + // A required parameter was omitted. + throw Error.DispParamNotOptional(message); + } + + Marshal.ThrowExceptionForHR(hresult); + } + + internal static void GetInfoFromType(ComTypes.ITypeInfo typeInfo, out string name, out string documentation) { + int dwHelpContext; + string strHelpFile; + + typeInfo.GetDocumentation(-1, out name, out documentation, out dwHelpContext, out strHelpFile); + } + + internal static string GetNameOfMethod(ComTypes.ITypeInfo typeInfo, int memid) { + int cNames; + string[] rgNames = new string[1]; + typeInfo.GetNames(memid, rgNames, 1, out cNames); + return rgNames[0]; + } + + internal static string GetNameOfLib(ComTypes.ITypeLib typeLib) { + string name; + string strDocString; + int dwHelpContext; + string strHelpFile; + + typeLib.GetDocumentation(-1, out name, out strDocString, out dwHelpContext, out strHelpFile); + return name; + } + + internal static string GetNameOfType(ComTypes.ITypeInfo typeInfo) { + string name; + string documentation; + GetInfoFromType(typeInfo, out name, out documentation); + + return name; + } + + /// + /// Look for typeinfo using IDispatch.GetTypeInfo + /// + /// + /// + /// Some COM objects just dont expose typeinfo. In these cases, this method will return null. + /// Some COM objects do intend to expose typeinfo, but may not be able to do so if the type-library is not properly + /// registered. This will be considered as acceptable or as an error condition depending on throwIfMissingExpectedTypeInfo + /// + internal static ComTypes.ITypeInfo GetITypeInfoFromIDispatch(IDispatch dispatch, bool throwIfMissingExpectedTypeInfo) { + uint typeCount; + int hresult = dispatch.TryGetTypeInfoCount(out typeCount); + Marshal.ThrowExceptionForHR(hresult); + Debug.Assert(typeCount <= 1); + if (typeCount == 0) { + return null; + } + + IntPtr typeInfoPtr = IntPtr.Zero; + + hresult = dispatch.TryGetTypeInfo(0, 0, out typeInfoPtr); + if (!ComHresults.IsSuccess(hresult)) { + CheckIfMissingTypeInfoIsExpected(hresult, throwIfMissingExpectedTypeInfo); + return null; + } + if (typeInfoPtr == IntPtr.Zero) { // be defensive against components that return IntPtr.Zero + if (throwIfMissingExpectedTypeInfo) { + Marshal.ThrowExceptionForHR(ComHresults.E_FAIL); + } + return null; + } + + ComTypes.ITypeInfo typeInfo = null; + try { + typeInfo = Marshal.GetObjectForIUnknown(typeInfoPtr) as ComTypes.ITypeInfo; + } finally { + Marshal.Release(typeInfoPtr); + } + + return typeInfo; + } + + /// + /// This method should be called when typeinfo is not available for an object. The function + /// will check if the typeinfo is expected to be missing. This can include error cases where + /// the same error is guaranteed to happen all the time, on all machines, under all circumstances. + /// In such cases, we just have to operate without the typeinfo. + /// + /// However, if accessing the typeinfo is failing in a transient way, we might want to throw + /// an exception so that we will eagerly predictably indicate the problem. + /// + private static void CheckIfMissingTypeInfoIsExpected(int hresult, bool throwIfMissingExpectedTypeInfo) { + Debug.Assert(!ComHresults.IsSuccess(hresult)); + + // Word.Basic always returns this because of an incorrect implementation of IDispatch.GetTypeInfo + // Any implementation that returns E_NOINTERFACE is likely to do so in all environments + if (hresult == ComHresults.E_NOINTERFACE) { + return; + } + + // This assert is potentially over-restrictive since COM components can behave in quite unexpected ways. + // However, asserting the common expected cases ensures that we find out about the unexpected scenarios, and + // can investigate the scenarios to ensure that there is no bug in our own code. + Debug.Assert(hresult == ComHresults.TYPE_E_LIBNOTREGISTERED); + + if (throwIfMissingExpectedTypeInfo) { + Marshal.ThrowExceptionForHR(hresult); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] + internal static ComTypes.TYPEATTR GetTypeAttrForTypeInfo(ComTypes.ITypeInfo typeInfo) { + IntPtr pAttrs = IntPtr.Zero; + typeInfo.GetTypeAttr(out pAttrs); + + // GetTypeAttr should never return null, this is just to be safe + if (pAttrs == IntPtr.Zero) { + throw Error.CannotRetrieveTypeInformation(); + } + + try { + return (ComTypes.TYPEATTR)Marshal.PtrToStructure(pAttrs, typeof(ComTypes.TYPEATTR)); + } finally { + typeInfo.ReleaseTypeAttr(pAttrs); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] + internal static ComTypes.TYPELIBATTR GetTypeAttrForTypeLib(ComTypes.ITypeLib typeLib) { + IntPtr pAttrs = IntPtr.Zero; + typeLib.GetLibAttr(out pAttrs); + + // GetTypeAttr should never return null, this is just to be safe + if (pAttrs == IntPtr.Zero) { + throw Error.CannotRetrieveTypeInformation(); + } + + try { + return (ComTypes.TYPELIBATTR)Marshal.PtrToStructure(pAttrs, typeof(ComTypes.TYPELIBATTR)); + } finally { + typeLib.ReleaseTLibAttr(pAttrs); + } + } + + public static BoundDispEvent CreateComEvent(object rcw, Guid sourceIid, int dispid) { + return new BoundDispEvent(rcw, sourceIid, dispid); + } + + public static DispCallable CreateDispCallable(IDispatch dispatch, ComMethodDesc method) { + return new DispCallable(dispatch, method); + } + } + + /// + /// This class contains methods that either cannot be expressed in C#, or which require writing unsafe code. + /// Callers of these methods need to use them extremely carefully as incorrect use could cause GC-holes + /// and other problems. + /// + /// + + // Security Note: In Microsoft.Scripting.Core we are running against the V2 CLR and are 100% SecurityTransparent. + // A call to any of these unsafe methods will result in the CLR performing a full demand against the callers stack + // and verifying that we have full trust for the unverifable code. In V4 CLR we are in System.Core which is SecurityCritical + // and uses the V4 transparency model. There we mark ourselves as SecuritySafeCritical but then demand unrestricted + // permission so again our callers stack needs full trust to access COM interop and unsafe code. +#if !MICROSOFT_SCRIPTING_CORE + [SecuritySafeCritical] + [PermissionSet(SecurityAction.Demand, Unrestricted = true)] +#endif + internal static class UnsafeMethods { + #region public members + + #region Generated ConvertByrefToPtr + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_ConvertByrefToPtr from: generate_comdispatch.py + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + internal static unsafe IntPtr ConvertSByteByrefToPtr(ref SByte value) { + fixed (SByte *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + internal static unsafe IntPtr ConvertInt16ByrefToPtr(ref Int16 value) { + fixed (Int16 *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + public static unsafe IntPtr ConvertInt32ByrefToPtr(ref Int32 value) { + fixed (Int32 *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + internal static unsafe IntPtr ConvertInt64ByrefToPtr(ref Int64 value) { + fixed (Int64 *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + internal static unsafe IntPtr ConvertByteByrefToPtr(ref Byte value) { + fixed (Byte *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + internal static unsafe IntPtr ConvertUInt16ByrefToPtr(ref UInt16 value) { + fixed (UInt16 *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + internal static unsafe IntPtr ConvertUInt32ByrefToPtr(ref UInt32 value) { + fixed (UInt32 *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + internal static unsafe IntPtr ConvertUInt64ByrefToPtr(ref UInt64 value) { + fixed (UInt64 *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + internal static unsafe IntPtr ConvertIntPtrByrefToPtr(ref IntPtr value) { + fixed (IntPtr *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + internal static unsafe IntPtr ConvertUIntPtrByrefToPtr(ref UIntPtr value) { + fixed (UIntPtr *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + internal static unsafe IntPtr ConvertSingleByrefToPtr(ref Single value) { + fixed (Single *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + internal static unsafe IntPtr ConvertDoubleByrefToPtr(ref Double value) { + fixed (Double *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] + internal static unsafe IntPtr ConvertDecimalByrefToPtr(ref Decimal value) { + fixed (Decimal *x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + + // *** END GENERATED CODE *** + + #endregion + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] // TODO: fix + public static unsafe IntPtr ConvertVariantByrefToPtr(ref Variant value) { + fixed (Variant* x = &value) { + AssertByrefPointsToStack(new IntPtr(x)); + return new IntPtr(x); + } + } + + [Obsolete("do not use this method", true)] + public static Variant GetVariantForObject(object obj) { + Variant variant = default(Variant); + System.Runtime.InteropServices.Marshal.GetNativeVariantForObject(obj, UnsafeMethods.ConvertVariantByrefToPtr(ref variant)); + return variant; + } + + [Obsolete("do not use this method", true)] + public static object GetObjectForVariant(Variant variant) { + IntPtr ptr = UnsafeMethods.ConvertVariantByrefToPtr(ref variant); + return System.Runtime.InteropServices.Marshal.GetObjectForNativeVariant(ptr); + } + + [Obsolete("do not use this method", true)] + public static int IUnknownRelease(IntPtr interfacePointer) { + return _IUnknownRelease(interfacePointer); + } + + [Obsolete("do not use this method", true)] + public static void IUnknownReleaseNotZero(IntPtr interfacePointer) { + if (interfacePointer != IntPtr.Zero) { + IUnknownRelease(interfacePointer); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] // TODO: fix + [Obsolete("do not use this method", true)] + public static int IDispatchInvoke( + IntPtr dispatchPointer, + int memberDispId, + ComTypes.INVOKEKIND flags, + ref ComTypes.DISPPARAMS dispParams, + out Variant result, + out ExcepInfo excepInfo, + out uint argErr + ) { + + int hresult = _IDispatchInvoke( + dispatchPointer, + memberDispId, + flags, + ref dispParams, + out result, + out excepInfo, + out argErr + ); + + if (hresult == ComHresults.DISP_E_MEMBERNOTFOUND + && (flags & ComTypes.INVOKEKIND.INVOKE_FUNC) != 0 + && (flags & (ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT | ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF)) == 0) { + + // Re-invoke with no result argument to accomodate Word + hresult = _IDispatchInvokeNoResult( + dispatchPointer, + memberDispId, + ComTypes.INVOKEKIND.INVOKE_FUNC, + ref dispParams, + out result, + out excepInfo, + out argErr); + } + return hresult; + } + + [Obsolete("do not use this method", true)] + public static IntPtr GetIdsOfNamedParameters(IDispatch dispatch, string[] names, int methodDispId, out GCHandle pinningHandle) { + pinningHandle = GCHandle.Alloc(null, GCHandleType.Pinned); + int[] dispIds = new int[names.Length]; + Guid empty = Guid.Empty; + int hresult = dispatch.TryGetIDsOfNames(ref empty, names, (uint)names.Length, 0, dispIds); + if (hresult < 0) { + Marshal.ThrowExceptionForHR(hresult); + } + + if (methodDispId != dispIds[0]) { + throw Error.GetIDsOfNamesInvalid(names[0]); + } + + int[] keywordArgDispIds = dispIds.RemoveFirst(); // Remove the dispId of the method name + + pinningHandle.Target = keywordArgDispIds; + return Marshal.UnsafeAddrOfPinnedArrayElement(keywordArgDispIds, 0); + } + + #endregion + + #region non-public members + + private static void EmitLoadArg(ILGenerator il, int index) { + ContractUtils.Requires(index >= 0, "index"); + + switch (index) { + case 0: + il.Emit(OpCodes.Ldarg_0); + break; + case 1: + il.Emit(OpCodes.Ldarg_1); + break; + case 2: + il.Emit(OpCodes.Ldarg_2); + break; + case 3: + il.Emit(OpCodes.Ldarg_3); + break; + default: + if (index <= Byte.MaxValue) { + il.Emit(OpCodes.Ldarg_S, (byte)index); + } else { + il.Emit(OpCodes.Ldarg, index); + } + break; + } + } + + /// + /// TODO: Used only in DEBUG build. Remove? + /// + /// Ensure that "value" is a local variable in some caller's frame. So converting + /// the byref to an IntPtr is a safe operation. Alternatively, we could also allow + /// allowed "value" to be a pinned object. + /// + [Conditional("DEBUG")] + private static void AssertByrefPointsToStack(IntPtr ptr) { + if (Marshal.ReadInt32(ptr) == _dummyMarker) { + // Prevent recursion + return; + } + int dummy = _dummyMarker; + IntPtr ptrToLocal = ConvertInt32ByrefToPtr(ref dummy); + Debug.Assert(ptrToLocal.ToInt64() < ptr.ToInt64()); + Debug.Assert((ptr.ToInt64() - ptrToLocal.ToInt64()) < (16 * 1024)); + } + + private const int _dummyMarker = 0x10101010; + + /// + /// We will emit an indirect call to an unmanaged function pointer from the vtable of the given interface pointer. + /// This approach can take only ~300 instructions on x86 compared with ~900 for Marshal.Release. We are relying on + /// the JIT-compiler to do pinvoke-stub-inlining and calling the pinvoke target directly. + /// + private delegate int IUnknownReleaseDelegate(IntPtr interfacePointer); + private static readonly IUnknownReleaseDelegate _IUnknownRelease = Create_IUnknownRelease(); + + private static IUnknownReleaseDelegate Create_IUnknownRelease() { + DynamicMethod dm = new DynamicMethod("IUnknownRelease", typeof(int), new Type[] { typeof(IntPtr) }, Assembly.GetExecutingAssembly().ManifestModule); + + ILGenerator method = dm.GetILGenerator(); + + // return functionPtr(...) + + method.Emit(OpCodes.Ldarg_0); + + // functionPtr = *(IntPtr*)(*(interfacePointer) + VTABLE_OFFSET) + int iunknownReleaseOffset = ((int)IDispatchMethodIndices.IUnknown_Release) * Marshal.SizeOf(typeof(IntPtr)); + method.Emit(OpCodes.Ldarg_0); + method.Emit(OpCodes.Ldind_I); + method.Emit(OpCodes.Ldc_I4, iunknownReleaseOffset); + method.Emit(OpCodes.Add); + method.Emit(OpCodes.Ldind_I); + + SignatureHelper signature = SignatureHelper.GetMethodSigHelper(CallingConvention.Winapi, typeof(int)); + signature.AddArgument(typeof(IntPtr)); + method.Emit(OpCodes.Calli, signature); + + method.Emit(OpCodes.Ret); + + return (IUnknownReleaseDelegate)dm.CreateDelegate(typeof(IUnknownReleaseDelegate)); + } + + internal static readonly IntPtr NullInterfaceId = GetNullInterfaceId(); + + private static IntPtr GetNullInterfaceId() { + int size = Marshal.SizeOf(Guid.Empty); + IntPtr ptr = Marshal.AllocHGlobal(size); + for (int i = 0; i < size; i++) { + Marshal.WriteByte(ptr, i, 0); + } + return ptr; + } + + /// + /// We will emit an indirect call to an unmanaged function pointer from the vtable of the given IDispatch interface pointer. + /// It is not possible to express this in C#. Using an indirect pinvoke call allows us to do our own marshalling. + /// We can allocate the Variant arguments cheaply on the stack. We are relying on the JIT-compiler to do + /// pinvoke-stub-inlining and calling the pinvoke target directly. + /// The alternative of calling via a managed interface declaration of IDispatch would have a performance + /// penalty of going through a CLR stub that would have to re-push the arguments on the stack, etc. + /// Marshal.GetDelegateForFunctionPointer could be used here, but its too expensive (~2000 instructions on x86). + /// + private delegate int IDispatchInvokeDelegate( + IntPtr dispatchPointer, + int memberDispId, + ComTypes.INVOKEKIND flags, + ref ComTypes.DISPPARAMS dispParams, + out Variant result, + out ExcepInfo excepInfo, + out uint argErr + ); + + private static readonly IDispatchInvokeDelegate _IDispatchInvoke = Create_IDispatchInvoke(true); + private static IDispatchInvokeDelegate _IDispatchInvokeNoResultImpl; + + private static IDispatchInvokeDelegate _IDispatchInvokeNoResult { + get { + if (_IDispatchInvokeNoResultImpl == null) { + lock (_IDispatchInvoke) { + if (_IDispatchInvokeNoResultImpl == null) { + _IDispatchInvokeNoResultImpl = Create_IDispatchInvoke(false); + } + } + } + return _IDispatchInvokeNoResultImpl; + } + } + + private static IDispatchInvokeDelegate Create_IDispatchInvoke(bool returnResult) { + const int dispatchPointerIndex = 0; + const int memberDispIdIndex = 1; + const int flagsIndex = 2; + const int dispParamsIndex = 3; + const int resultIndex = 4; + const int exceptInfoIndex = 5; + const int argErrIndex = 6; + Debug.Assert(argErrIndex + 1 == typeof(IDispatchInvokeDelegate).GetMethod("Invoke").GetParameters().Length); + + Type[] paramTypes = new Type[argErrIndex + 1]; + paramTypes[dispatchPointerIndex] = typeof(IntPtr); + paramTypes[memberDispIdIndex] = typeof(int); + paramTypes[flagsIndex] = typeof(ComTypes.INVOKEKIND); + paramTypes[dispParamsIndex] = typeof(ComTypes.DISPPARAMS).MakeByRefType(); + paramTypes[resultIndex] = typeof(Variant).MakeByRefType(); + paramTypes[exceptInfoIndex] = typeof(ExcepInfo).MakeByRefType(); + paramTypes[argErrIndex] = typeof(uint).MakeByRefType(); + + // Define the dynamic method in our assembly so we skip verification + DynamicMethod dm = new DynamicMethod("IDispatchInvoke", typeof(int), paramTypes, Assembly.GetExecutingAssembly().ManifestModule); + ILGenerator method = dm.GetILGenerator(); + + // return functionPtr(...) + + EmitLoadArg(method, dispatchPointerIndex); + EmitLoadArg(method, memberDispIdIndex); + + // burn the address of our empty IID in directly. This is never freed, relocated, etc... + // Note passing this as a Guid directly results in a ~30% perf hit for IDispatch invokes so + // we also pass it directly as an IntPtr instead. + if (IntPtr.Size == 4) { + method.Emit(OpCodes.Ldc_I4, UnsafeMethods.NullInterfaceId.ToInt32()); // riid + } else { + method.Emit(OpCodes.Ldc_I8, UnsafeMethods.NullInterfaceId.ToInt64()); // riid + } + method.Emit(OpCodes.Conv_I); + + method.Emit(OpCodes.Ldc_I4_0); // lcid + EmitLoadArg(method, flagsIndex); + + EmitLoadArg(method, dispParamsIndex); + + if (returnResult) { + EmitLoadArg(method, resultIndex); + } else { + method.Emit(OpCodes.Ldsfld, typeof(IntPtr).GetField("Zero")); + } + EmitLoadArg(method, exceptInfoIndex); + EmitLoadArg(method, argErrIndex); + + // functionPtr = *(IntPtr*)(*(dispatchPointer) + VTABLE_OFFSET) + int idispatchInvokeOffset = ((int)IDispatchMethodIndices.IDispatch_Invoke) * Marshal.SizeOf(typeof(IntPtr)); + EmitLoadArg(method, dispatchPointerIndex); + method.Emit(OpCodes.Ldind_I); + method.Emit(OpCodes.Ldc_I4, idispatchInvokeOffset); + method.Emit(OpCodes.Add); + method.Emit(OpCodes.Ldind_I); + + SignatureHelper signature = SignatureHelper.GetMethodSigHelper(CallingConvention.Winapi, typeof(int)); + Type[] invokeParamTypes = new Type[] { + typeof(IntPtr), // dispatchPointer + typeof(int), // memberDispId + typeof(IntPtr), // riid + typeof(int), // lcid + typeof(ushort), // flags + typeof(IntPtr), // dispParams + typeof(IntPtr), // result + typeof(IntPtr), // excepInfo + typeof(IntPtr), // argErr + }; + signature.AddArguments(invokeParamTypes, null, null); + method.Emit(OpCodes.Calli, signature); + + method.Emit(OpCodes.Ret); + return (IDispatchInvokeDelegate)dm.CreateDelegate(typeof(IDispatchInvokeDelegate)); + } + + #endregion + } + + internal static class UnsafeNativeMethods { + [System.Runtime.Versioning.ResourceExposure(System.Runtime.Versioning.ResourceScope.None)] + [System.Runtime.Versioning.ResourceConsumption(System.Runtime.Versioning.ResourceScope.Process, System.Runtime.Versioning.ResourceScope.Process)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")] // TODO: fix + [DllImport("oleaut32.dll", PreserveSig = false)] + internal static extern void VariantClear(IntPtr variant); + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComTypeClassDesc.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComTypeClassDesc.cs new file mode 100644 index 0000000000..7a055fe6b5 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComTypeClassDesc.cs @@ -0,0 +1,73 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Dynamic.Binders; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace System.Dynamic.ComInterop { + + internal sealed class ComTypeClassDesc : ComTypeDesc { + private LinkedList _itfs; // implemented interfaces + private LinkedList _sourceItfs; // source interfaces supported by this coclass + + internal ComTypeClassDesc(ComTypes.ITypeInfo typeInfo) : + base(typeInfo) { + ComTypes.TYPEATTR typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo); + Guid = typeAttr.guid; + + for (int i = 0; i < typeAttr.cImplTypes; i++) { + int hRefType; + typeInfo.GetRefTypeOfImplType(i, out hRefType); + ComTypes.ITypeInfo currentTypeInfo; + typeInfo.GetRefTypeInfo(hRefType, out currentTypeInfo); + + ComTypes.IMPLTYPEFLAGS implTypeFlags; + typeInfo.GetImplTypeFlags(i, out implTypeFlags); + + bool isSourceItf = (implTypeFlags & ComTypes.IMPLTYPEFLAGS.IMPLTYPEFLAG_FSOURCE) != 0; + AddInterface(currentTypeInfo, isSourceItf); + } + } + + private void AddInterface(ComTypes.ITypeInfo itfTypeInfo, bool isSourceItf) { + string itfName = ComRuntimeHelpers.GetNameOfType(itfTypeInfo); + + if (isSourceItf) { + if (_sourceItfs == null) { + _sourceItfs = new LinkedList(); + } + _sourceItfs.AddLast(itfName); + } else { + if (_itfs == null) { + _itfs = new LinkedList(); + } + _itfs.AddLast(itfName); + } + } + + internal bool Implements(string itfName, bool isSourceItf) { + if (isSourceItf) + return _sourceItfs.Contains(itfName); + else + return _itfs.Contains(itfName); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComTypeDesc.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComTypeDesc.cs new file mode 100644 index 0000000000..9ac99aeeb5 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComTypeDesc.cs @@ -0,0 +1,111 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.InteropServices.ComTypes; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace System.Dynamic.ComInterop { + + internal class ComTypeDesc { + private string _typeName; + private string _documentation; + private Guid _guid; + private Dictionary _funcs; + private Dictionary _puts; + private Dictionary _putRefs; + private Dictionary _events; + private ComMethodDesc _getItem; + private ComMethodDesc _setItem; + private static readonly Dictionary _EmptyEventsDict = new Dictionary(); + + internal ComTypeDesc(ITypeInfo typeInfo) { + if (typeInfo != null) { + ComRuntimeHelpers.GetInfoFromType(typeInfo, out _typeName, out _documentation); + } + } + + internal static ComTypeDesc FromITypeInfo(ComTypes.ITypeInfo typeInfo) { + ComTypes.TYPEATTR typeAttr; + typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo); + if (typeAttr.typekind == ComTypes.TYPEKIND.TKIND_COCLASS) { + return new ComTypeClassDesc(typeInfo); + } else if (typeAttr.typekind == ComTypes.TYPEKIND.TKIND_ENUM) { + return new ComTypeEnumDesc(typeInfo); + } else if ((typeAttr.typekind == ComTypes.TYPEKIND.TKIND_DISPATCH) || + (typeAttr.typekind == ComTypes.TYPEKIND.TKIND_INTERFACE)) { + ComTypeDesc typeDesc = new ComTypeDesc(typeInfo); + return typeDesc; + } else { + throw Error.UnsupportedEnumType(); + } + } + + internal static ComTypeDesc CreateEmptyTypeDesc() { + ComTypeDesc typeDesc = new ComTypeDesc(null); + typeDesc._funcs = new Dictionary(); + typeDesc._events = _EmptyEventsDict; + + return typeDesc; + } + + internal static Dictionary EmptyEvents { + get { return _EmptyEventsDict; } + } + + internal Dictionary Funcs { + get { return _funcs; } + set { _funcs = value; } + } + + internal Dictionary Puts { + get { return _puts; } + set { _puts = value; } + } + internal Dictionary PutRefs { + get { return _putRefs; } + set { _putRefs = value; } + } + + internal Dictionary Events { + get { return _events; } + set { _events = value; } + } + + public string TypeName { + get { return _typeName; } + } + + internal Guid Guid { + get { return _guid; } + set { _guid = value; } + } + + internal ComMethodDesc GetItem { + get { return _getItem; } + set { _getItem = value; } + } + + internal ComMethodDesc SetItem { + get { return _setItem; } + set { _setItem = value; } + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComTypeEnumDesc.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComTypeEnumDesc.cs new file mode 100644 index 0000000000..eca7e16c72 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComTypeEnumDesc.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Dynamic.Binders; +using System.Globalization; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace System.Dynamic.ComInterop { + internal sealed class ComTypeEnumDesc : ComTypeDesc { + public override string ToString() { + return String.Format(CultureInfo.CurrentCulture, "", TypeName); + } + + internal ComTypeEnumDesc(ComTypes.ITypeInfo typeInfo) : + base(typeInfo) { + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComTypeLibDesc.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComTypeLibDesc.cs new file mode 100644 index 0000000000..98effcdeef --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ComTypeLibDesc.cs @@ -0,0 +1,98 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Dynamic.Binders; +using System.Globalization; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace System.Dynamic.ComInterop { + + internal sealed class ComTypeLibDesc { + + // typically typelibs contain very small number of coclasses + // so we will just use the linked list as it performs better + // on small number of entities + LinkedList _classes; + Dictionary _enums; + string _typeLibName; + + private static Dictionary _CachedTypeLibDesc = new Dictionary(); + + private ComTypeLibDesc() { + _enums = new Dictionary(); + _classes = new LinkedList(); + } + + public override string ToString() { + return String.Format(CultureInfo.CurrentCulture, "", _typeLibName); + } + + internal static ComTypeLibDesc GetFromTypeLib(ComTypes.ITypeLib typeLib) { + // check whether we have already loaded this type library + ComTypes.TYPELIBATTR typeLibAttr = ComRuntimeHelpers.GetTypeAttrForTypeLib(typeLib); + ComTypeLibDesc typeLibDesc; + lock (_CachedTypeLibDesc) { + if (_CachedTypeLibDesc.TryGetValue(typeLibAttr.guid, out typeLibDesc)) { + return typeLibDesc; + } + } + + typeLibDesc = new ComTypeLibDesc(); + + typeLibDesc._typeLibName = ComRuntimeHelpers.GetNameOfLib(typeLib); + + int countTypes = typeLib.GetTypeInfoCount(); + for (int i = 0; i < countTypes; i++) { + ComTypes.TYPEKIND typeKind; + typeLib.GetTypeInfoType(i, out typeKind); + + ComTypes.ITypeInfo typeInfo; + if (typeKind == ComTypes.TYPEKIND.TKIND_COCLASS) { + typeLib.GetTypeInfo(i, out typeInfo); + ComTypeClassDesc classDesc = new ComTypeClassDesc(typeInfo); + typeLibDesc._classes.AddLast(classDesc); + } else if (typeKind == ComTypes.TYPEKIND.TKIND_ENUM) { + typeLib.GetTypeInfo(i, out typeInfo); + ComTypeEnumDesc enumDesc = new ComTypeEnumDesc(typeInfo); + typeLibDesc._enums.Add(enumDesc.TypeName, enumDesc); + } + } + + // cached the typelib using the guid as the dictionary key + lock (_CachedTypeLibDesc) { + _CachedTypeLibDesc.Add(typeLibAttr.guid, typeLibDesc); + } + + return typeLibDesc; + } + + internal ComTypeClassDesc GetCoClassForInterface(string itfName) { + foreach (ComTypeClassDesc coclass in _classes) { + if (coclass.Implements(itfName, false)) { + return coclass; + } + } + + return null; + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ContractUtils.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ContractUtils.cs new file mode 100644 index 0000000000..9babd946a0 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ContractUtils.cs @@ -0,0 +1,50 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; + +namespace System.Dynamic.ComInterop { + + // TODO: Update with the newest version of the managed contracts stuff + internal static class ContractUtils { + + internal static void Requires(bool precondition, string paramName) { + Assert.NotEmpty(paramName); + + if (!precondition) { + throw new ArgumentException(Strings.InvalidArgumentValue, paramName); + } + } + + internal static void Requires(bool precondition, string paramName, string message) { + Assert.NotEmpty(paramName); + + if (!precondition) { + throw new ArgumentException(message, paramName); + } + } + + internal static void RequiresNotNull(object value, string paramName) { + Assert.NotEmpty(paramName); + + if (value == null) { + throw new ArgumentNullException(paramName); + } + } + } +} diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ConversionArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ConversionArgBuilder.cs new file mode 100644 index 0000000000..229638c2fa --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ConversionArgBuilder.cs @@ -0,0 +1,45 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Diagnostics; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + + internal class ConversionArgBuilder : ArgBuilder { + private SimpleArgBuilder _innerBuilder; + private Type _parameterType; + + internal ConversionArgBuilder(Type parameterType, SimpleArgBuilder innerBuilder) { + _parameterType = parameterType; + _innerBuilder = innerBuilder; + } + + internal override Expression Marshal(Expression parameter) { + return _innerBuilder.Marshal(Helpers.Convert(parameter, _parameterType)); + } + + internal override Expression MarshalToRef(Expression parameter) { + //we are not supporting conversion InOut + throw Assert.Unreachable; + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ConvertibleArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ConvertibleArgBuilder.cs new file mode 100644 index 0000000000..bdf27aacee --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ConvertibleArgBuilder.cs @@ -0,0 +1,39 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Globalization; +using System.Linq.Expressions; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + + internal class ConvertibleArgBuilder : ArgBuilder { + internal ConvertibleArgBuilder() { + } + + internal override Expression Marshal(Expression parameter) { + return Helpers.Convert(parameter, typeof(IConvertible)); + } + + internal override Expression MarshalToRef(Expression parameter) { + //we are not supporting convertible InOut + throw Assert.Unreachable; + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/CurrencyArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/CurrencyArgBuilder.cs new file mode 100644 index 0000000000..e3919598d8 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/CurrencyArgBuilder.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + + +#if !SILVERLIGHT // ComObject + +using System.Diagnostics; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + internal sealed class CurrencyArgBuilder : SimpleArgBuilder { + internal CurrencyArgBuilder(Type parameterType) + : base(parameterType) { + Debug.Assert(parameterType == typeof(CurrencyWrapper)); + } + + internal override Expression Marshal(Expression parameter) { + // parameter.WrappedObject + return Expression.Property( + Helpers.Convert(base.Marshal(parameter), typeof(CurrencyWrapper)), + "WrappedObject" + ); + } + + internal override Expression MarshalToRef(Expression parameter) { + // Decimal.ToOACurrency(parameter.WrappedObject) + return Expression.Call( + typeof(Decimal).GetMethod("ToOACurrency"), + Marshal(parameter) + ); + } + + internal override Expression UnmarshalFromRef(Expression value) { + // Decimal.FromOACurrency(value) + return base.UnmarshalFromRef( + Expression.New( + typeof(CurrencyWrapper).GetConstructor(new Type[] { typeof(Decimal) }), + Expression.Call( + typeof(Decimal).GetMethod("FromOACurrency"), + value + ) + ) + ); + } + } +} + +#endif \ No newline at end of file diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/DateTimeArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/DateTimeArgBuilder.cs new file mode 100644 index 0000000000..c709c9c646 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/DateTimeArgBuilder.cs @@ -0,0 +1,53 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + internal sealed class DateTimeArgBuilder : SimpleArgBuilder { + internal DateTimeArgBuilder(Type parameterType) + : base(parameterType) { + Debug.Assert(parameterType == typeof(DateTime)); + } + + internal override Expression MarshalToRef(Expression parameter) { + // parameter.ToOADate() + return Expression.Call( + Marshal(parameter), + typeof(DateTime).GetMethod("ToOADate") + ); + } + + internal override Expression UnmarshalFromRef(Expression value) { + // DateTime.FromOADate(value) + return base.UnmarshalFromRef( + Expression.Call( + typeof(DateTime).GetMethod("FromOADate"), + value + ) + ); + } + } +} + +#endif \ No newline at end of file diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/DispCallable.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/DispCallable.cs new file mode 100644 index 0000000000..db07b93ed8 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/DispCallable.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Linq.Expressions; +using System.Dynamic.Binders; +using Microsoft.Contracts; +using System.Globalization; + +namespace System.Dynamic.ComInterop { + /// + /// This represents a bound dispmethod on a IDispatch object. + /// + internal sealed class DispCallable : IDynamicObject { + + private readonly IDispatch _dispatch; + private readonly ComMethodDesc _methodDesc; + + internal DispCallable(IDispatch dispatch, ComMethodDesc methodDesc) { + _dispatch = dispatch; + _methodDesc = methodDesc; + } + + public override string ToString() { + return String.Format(CultureInfo.CurrentCulture, "", _methodDesc.Name); + } + + public IDispatch DispatchObject { + get { return _dispatch; } + } + + public ComMethodDesc ComMethodDesc { + get { return _methodDesc; } + } + + public MetaObject GetMetaObject(Expression parameter) { + return new DispCallableMetaObject(parameter, this); + } + + public override bool Equals(object obj) { + var other = obj as DispCallable; + return other != null && other._dispatch == _dispatch && other._methodDesc == _methodDesc; + } + + public override int GetHashCode() { + return _dispatch.GetHashCode() ^ _methodDesc.GetHashCode(); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/DispCallableMetaObject.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/DispCallableMetaObject.cs new file mode 100644 index 0000000000..d48f4be5c5 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/DispCallableMetaObject.cs @@ -0,0 +1,79 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Dynamic.Binders; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + internal class DispCallableMetaObject : MetaObject { + private readonly DispCallable _callable; + + internal DispCallableMetaObject(Expression expression, DispCallable callable) + : base(expression, Restrictions.Empty, callable) { + _callable = callable; + } + + public override MetaObject BindGetIndex(GetIndexBinder binder, MetaObject[] indexes) { + if (_callable.ComMethodDesc.IsPropertyGet) { + if (indexes.Any(arg => ComBinderHelpers.IsStrongBoxArg(arg))) { + return ComBinderHelpers.RewriteStrongBoxAsRef(binder, this, indexes); + } + return BindComInvoke(binder.Arguments, indexes); + } + return base.BindGetIndex(binder, indexes); + } + + public override MetaObject BindSetIndex(SetIndexBinder binder, MetaObject[] indexes, MetaObject value) { + if (_callable.ComMethodDesc.IsPropertyPut) { + if (indexes.Any(arg => ComBinderHelpers.IsStrongBoxArg(arg))) { + return ComBinderHelpers.RewriteStrongBoxAsRef(binder, this, indexes.AddLast(value)); + } + return BindComInvoke(binder.Arguments, indexes.AddLast(value)); + } + return base.BindSetIndex(binder, indexes, value); + } + + public override MetaObject BindInvoke(InvokeBinder binder, MetaObject[] args) { + if (args.Any(arg => ComBinderHelpers.IsStrongBoxArg(arg))) { + return ComBinderHelpers.RewriteStrongBoxAsRef(binder, this, args); + } + return BindComInvoke(binder.Arguments, args); + } + + private MetaObject BindComInvoke(IList argInfo, MetaObject[] args) { + var callable = Expression; + var dispCall = Expression.Convert(callable, typeof(DispCallable)); + var methodDesc = Expression.Property(dispCall, typeof(DispCallable).GetProperty("ComMethodDesc")); + var methodRestriction = Expression.Equal(methodDesc, Expression.Constant(_callable.ComMethodDesc)); + + return new ComInvokeBinder( + argInfo, + args, + Restrictions.GetTypeRestriction(callable, Value.GetType()).Merge( + Restrictions.GetExpressionRestriction(methodRestriction) + ), + methodDesc, + Expression.Property(dispCall, typeof(DispCallable).GetProperty("DispatchObject")), + _callable.ComMethodDesc + ).Invoke(); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/DispatchArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/DispatchArgBuilder.cs new file mode 100644 index 0000000000..7a8a221699 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/DispatchArgBuilder.cs @@ -0,0 +1,87 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + + internal class DispatchArgBuilder : SimpleArgBuilder { + private bool _isWrapper; + + internal DispatchArgBuilder(Type parameterType) + : base(parameterType) { + + _isWrapper = parameterType == typeof(DispatchWrapper); + } + + internal override Expression Marshal(Expression parameter) { + parameter = base.Marshal(parameter); + + // parameter.WrappedObject + if (_isWrapper) { + parameter = Expression.Property( + Helpers.Convert(parameter, typeof(DispatchWrapper)), + typeof(DispatchWrapper).GetProperty("WrappedObject") + ); + }; + + return Helpers.Convert(parameter, typeof(object)); + } + + internal override Expression MarshalToRef(Expression parameter) { + parameter = Marshal(parameter); + + // parameter == null ? IntPtr.Zero : Marshal.GetIDispatchForObject(parameter); + return Expression.Condition( + Expression.Equal(parameter, Expression.Constant(null)), + Expression.Constant(IntPtr.Zero), + Expression.Call( + typeof(Marshal).GetMethod("GetIDispatchForObject"), + parameter + ) + ); + } + + internal override Expression UnmarshalFromRef(Expression value) { + // value == IntPtr.Zero ? null : Marshal.GetObjectForIUnknown(value); + Expression unmarshal = Expression.Condition( + Expression.Equal(value, Expression.Constant(IntPtr.Zero)), + Expression.Constant(null), + Expression.Call( + typeof(Marshal).GetMethod("GetObjectForIUnknown"), + value + ) + ); + + if (_isWrapper) { + unmarshal = Expression.New( + typeof(DispatchWrapper).GetConstructor(new Type[] { typeof(object) }), + unmarshal + ); + } + + return base.UnmarshalFromRef(unmarshal); + + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ErrorArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ErrorArgBuilder.cs new file mode 100644 index 0000000000..d51dc83df8 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ErrorArgBuilder.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + internal class ErrorArgBuilder : SimpleArgBuilder { + internal ErrorArgBuilder(Type parameterType) + : base(parameterType) { + + Debug.Assert(parameterType == typeof(ErrorWrapper)); + } + + internal override Expression Marshal(Expression parameter) { + // parameter.ErrorCode + return Expression.Property( + Helpers.Convert(base.MarshalToRef(parameter), typeof(ErrorWrapper)), + "ErrorCode" + ); + } + + internal override Expression UnmarshalFromRef(Expression value) { + // new ErrorWrapper(value) + return base.UnmarshalFromRef( + Expression.New( + typeof(ErrorWrapper).GetConstructor(new Type[] { typeof(int) }), + value + ) + ); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Errors.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Errors.cs new file mode 100644 index 0000000000..f83ddb52b4 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Errors.cs @@ -0,0 +1,365 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + + +namespace System.Dynamic.ComInterop { + +#if MICROSOFT_SCRIPTING_CORE + internal static partial class Strings { + private static string FormatString(string format, params object[] args) { + return string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args); + } + } + + #region Generated Com Exception Factory + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_expr_factory_com from: generate_exception_factory.py + + /// + /// Strongly-typed and parameterized string factory. + /// + + internal static partial class Strings { + /// + /// A string like "Invalid argument value" + /// + internal static string InvalidArgumentValue { + get { + return "Invalid argument value"; + } + } + + /// + /// A string like "COM object is expected." + /// + internal static string ComObjectExpected { + get { + return "COM object is expected."; + } + } + + /// + /// A string like "Removing an event handler that is not registered." + /// + internal static string RemovingUnregisteredEvent { + get { + return "Removing an event handler that is not registered."; + } + } + + /// + /// A string like "COM object does not support events." + /// + internal static string COMObjectDoesNotSupportEvents { + get { + return "COM object does not support events."; + } + } + + /// + /// A string like "COM object does not support specified source interface." + /// + internal static string COMObjectDoesNotSupportSourceInterface { + get { + return "COM object does not support specified source interface."; + } + } + + /// + /// A string like "Removing not registered handler." + /// + internal static string RemovingUnregisteredHandler { + get { + return "Removing not registered handler."; + } + } + + /// + /// A string like "Marshal.SetComObjectData failed." + /// + internal static string SetComObjectDataFailed { + get { + return "Marshal.SetComObjectData failed."; + } + } + + /// + /// A string like "This method exists only to keep the compiler happy." + /// + internal static string MethodShouldNotBeCalled { + get { + return "This method exists only to keep the compiler happy."; + } + } + + /// + /// A string like "Default value of COM parameter cannot be read properly." + /// + internal static string DefaultValueCannotBeRead { + get { + return "Default value of COM parameter cannot be read properly."; + } + } + + /// + /// A string like "Unexpected VarEnum {0}." + /// + internal static string UnexpectedVarEnum(object p0) { + return FormatString("Unexpected VarEnum {0}.", p0); + } + + /// + /// A string like "Error while invoking {0}." + /// + internal static string DispBadParamCount(object p0) { + return FormatString("Error while invoking {0}.", p0); + } + + /// + /// A string like "Error while invoking {0}." + /// + internal static string DispMemberNotFound(object p0) { + return FormatString("Error while invoking {0}.", p0); + } + + /// + /// A string like "Error while invoking {0}. Named arguments are not supported." + /// + internal static string DispNoNamedArgs(object p0) { + return FormatString("Error while invoking {0}. Named arguments are not supported.", p0); + } + + /// + /// A string like "Error while invoking {0}." + /// + internal static string DispOverflow(object p0) { + return FormatString("Error while invoking {0}.", p0); + } + + /// + /// A string like "Could not convert argument {0} for call to {1}." + /// + internal static string DispTypeMismatch(object p0, object p1) { + return FormatString("Could not convert argument {0} for call to {1}.", p0, p1); + } + + /// + /// A string like "Error while invoking {0}. A required parameter was omitted." + /// + internal static string DispParamNotOptional(object p0) { + return FormatString("Error while invoking {0}. A required parameter was omitted.", p0); + } + + /// + /// A string like "ResolveComReference.CannotRetrieveTypeInformation." + /// + internal static string CannotRetrieveTypeInformation { + get { + return "ResolveComReference.CannotRetrieveTypeInformation."; + } + } + + /// + /// A string like "IDispatch::GetIDsOfNames behaved unexpectedly for {0}." + /// + internal static string GetIDsOfNamesInvalid(object p0) { + return FormatString("IDispatch::GetIDsOfNames behaved unexpectedly for {0}.", p0); + } + + /// + /// A string like "Attempting to wrap an unsupported enum type." + /// + internal static string UnsupportedEnumType { + get { + return "Attempting to wrap an unsupported enum type."; + } + } + + /// + /// A string like "Could not get dispatch ID for {0} (error: {1})." + /// + internal static string CouldNotGetDispId(object p0, object p1) { + return FormatString("Could not get dispatch ID for {0} (error: {1}).", p0, p1); + } + + /// + /// A string like "There are valid conversions from {0} to {1}." + /// + internal static string AmbiguousConversion(object p0, object p1) { + return FormatString("There are valid conversions from {0} to {1}.", p0, p1); + } + + /// + /// A string like "Variant.GetAccessor cannot handle {0}." + /// + internal static string VariantGetAccessorNYI(object p0) { + return FormatString("Variant.GetAccessor cannot handle {0}.", p0); + } + + } + /// + /// Strongly-typed and parameterized exception factory. + /// + + internal static partial class Error { + /// + /// InvalidOperationException with message like "Removing an event handler that is not registered." + /// + internal static Exception RemovingUnregisteredEvent() { + return new InvalidOperationException(Strings.RemovingUnregisteredEvent); + } + + /// + /// ArgumentException with message like "COM object does not support events." + /// + internal static Exception COMObjectDoesNotSupportEvents() { + return new ArgumentException(Strings.COMObjectDoesNotSupportEvents); + } + + /// + /// ArgumentException with message like "COM object does not support specified source interface." + /// + internal static Exception COMObjectDoesNotSupportSourceInterface() { + return new ArgumentException(Strings.COMObjectDoesNotSupportSourceInterface); + } + + /// + /// InvalidOperationException with message like "Removing not registered handler." + /// + internal static Exception RemovingUnregisteredHandler() { + return new InvalidOperationException(Strings.RemovingUnregisteredHandler); + } + + /// + /// InvalidOperationException with message like "Marshal.SetComObjectData failed." + /// + internal static Exception SetComObjectDataFailed() { + return new InvalidOperationException(Strings.SetComObjectDataFailed); + } + + /// + /// InvalidOperationException with message like "This method exists only to keep the compiler happy." + /// + internal static Exception MethodShouldNotBeCalled() { + return new InvalidOperationException(Strings.MethodShouldNotBeCalled); + } + + /// + /// InvalidProgramException with message like "Default value of COM parameter cannot be read properly." + /// + internal static Exception DefaultValueCannotBeRead() { + return new InvalidProgramException(Strings.DefaultValueCannotBeRead); + } + + /// + /// InvalidOperationException with message like "Unexpected VarEnum {0}." + /// + internal static Exception UnexpectedVarEnum(object p0) { + return new InvalidOperationException(Strings.UnexpectedVarEnum(p0)); + } + + /// + /// System.Reflection.TargetParameterCountException with message like "Error while invoking {0}." + /// + internal static Exception DispBadParamCount(object p0) { + return new System.Reflection.TargetParameterCountException(Strings.DispBadParamCount(p0)); + } + + /// + /// MissingMemberException with message like "Error while invoking {0}." + /// + internal static Exception DispMemberNotFound(object p0) { + return new MissingMemberException(Strings.DispMemberNotFound(p0)); + } + + /// + /// ArgumentException with message like "Error while invoking {0}. Named arguments are not supported." + /// + internal static Exception DispNoNamedArgs(object p0) { + return new ArgumentException(Strings.DispNoNamedArgs(p0)); + } + + /// + /// OverflowException with message like "Error while invoking {0}." + /// + internal static Exception DispOverflow(object p0) { + return new OverflowException(Strings.DispOverflow(p0)); + } + + /// + /// ArgumentException with message like "Could not convert argument {0} for call to {1}." + /// + internal static Exception DispTypeMismatch(object p0, object p1) { + return new ArgumentException(Strings.DispTypeMismatch(p0, p1)); + } + + /// + /// ArgumentException with message like "Error while invoking {0}. A required parameter was omitted." + /// + internal static Exception DispParamNotOptional(object p0) { + return new ArgumentException(Strings.DispParamNotOptional(p0)); + } + + /// + /// InvalidOperationException with message like "ResolveComReference.CannotRetrieveTypeInformation." + /// + internal static Exception CannotRetrieveTypeInformation() { + return new InvalidOperationException(Strings.CannotRetrieveTypeInformation); + } + + /// + /// ArgumentException with message like "IDispatch::GetIDsOfNames behaved unexpectedly for {0}." + /// + internal static Exception GetIDsOfNamesInvalid(object p0) { + return new ArgumentException(Strings.GetIDsOfNamesInvalid(p0)); + } + + /// + /// InvalidOperationException with message like "Attempting to wrap an unsupported enum type." + /// + internal static Exception UnsupportedEnumType() { + return new InvalidOperationException(Strings.UnsupportedEnumType); + } + + /// + /// MissingMemberException with message like "Could not get dispatch ID for {0} (error: {1})." + /// + internal static Exception CouldNotGetDispId(object p0, object p1) { + return new MissingMemberException(Strings.CouldNotGetDispId(p0, p1)); + } + + /// + /// System.Reflection.AmbiguousMatchException with message like "There are valid conversions from {0} to {1}." + /// + internal static Exception AmbiguousConversion(object p0, object p1) { + return new System.Reflection.AmbiguousMatchException(Strings.AmbiguousConversion(p0, p1)); + } + + /// + /// NotImplementedException with message like "Variant.GetAccessor cannot handle {0}." + /// + internal static Exception VariantGetAccessorNYI(object p0) { + return new NotImplementedException(Strings.VariantGetAccessorNYI(p0)); + } + + } + + // *** END GENERATED CODE *** + + #endregion + +#endif +} diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ExcepInfo.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ExcepInfo.cs new file mode 100644 index 0000000000..df1569638a --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/ExcepInfo.cs @@ -0,0 +1,110 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.InteropServices; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace System.Dynamic.ComInterop { + /// + /// This is similar to ComTypes.EXCEPINFO, but lets us do our own custom marshaling + /// + [StructLayout(LayoutKind.Sequential)] + internal struct ExcepInfo { + private short wCode; + private short wReserved; + private IntPtr bstrSource; + private IntPtr bstrDescription; + private IntPtr bstrHelpFile; + private int dwHelpContext; + private IntPtr pvReserved; + private IntPtr pfnDeferredFillIn; + private int scode; + +#if DEBUG + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2207:InitializeValueTypeStaticFieldsInline")] + static ExcepInfo() { + Debug.Assert(Marshal.SizeOf(typeof(ExcepInfo)) == Marshal.SizeOf(typeof(ComTypes.EXCEPINFO))); + } +#endif + private static string ConvertAndFreeBstr(ref IntPtr bstr) { + if (bstr == IntPtr.Zero) { + return null; + } + + string result = Marshal.PtrToStringBSTR(bstr); + Marshal.FreeBSTR(bstr); + bstr = IntPtr.Zero; + return result; + } + + internal void Dummy() { + wCode = 0; + wReserved = 0; wReserved++; + bstrSource = IntPtr.Zero; + bstrDescription = IntPtr.Zero; + bstrHelpFile = IntPtr.Zero; + dwHelpContext = 0; + pfnDeferredFillIn = IntPtr.Zero; + pvReserved = IntPtr.Zero; + scode = 0; + + throw Error.MethodShouldNotBeCalled(); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] + internal Exception GetException() { + Debug.Assert(pfnDeferredFillIn == IntPtr.Zero); // TODO +#if DEBUG + System.Diagnostics.Debug.Assert(wReserved != -1); + wReserved = -1; // to ensure that the method gets called only once +#endif + + int errorCode = (scode != 0) ? scode : wCode; + Exception exception = Marshal.GetExceptionForHR(errorCode); + + string message = ConvertAndFreeBstr(ref bstrDescription); + if (message != null) { + // If we have a custom message, create a new Exception object with the message set correctly. + // We need to create a new object because "exception.Message" is a read-only property. + if (exception is COMException) { + exception = new COMException(message, errorCode); + } else { + Type exceptionType = exception.GetType(); + ConstructorInfo ctor = exceptionType.GetConstructor(new Type[] { typeof(string) }); + if (ctor != null) { + exception = (Exception)ctor.Invoke(new object[] { message }); + } + } + } + + exception.Source = ConvertAndFreeBstr(ref bstrSource); + + string helpLink = ConvertAndFreeBstr(ref bstrHelpFile); + if (dwHelpContext != 0) { + helpLink += "#" + dwHelpContext; + } + exception.HelpLink = helpLink; + + return exception; + } + } +} + +#endif \ No newline at end of file diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Helpers.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Helpers.cs new file mode 100644 index 0000000000..f5c77a56df --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Helpers.cs @@ -0,0 +1,80 @@ + +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection.Emit; +using System.Text; +using System.Linq.Expressions; + +namespace System.Dynamic.ComInterop { + + // Miscellaneous helpers that don't belong anywhere else + internal static class Helpers { + + internal static Expression Convert(Expression expression, Type type) { + if (expression.Type == type) { + return expression; + } + return Expression.Convert(expression, type); + } + + internal static void EmitInt(this ILGenerator gen, int value) { + OpCode c; + switch (value) { + case -1: + c = OpCodes.Ldc_I4_M1; + break; + case 0: + c = OpCodes.Ldc_I4_0; + break; + case 1: + c = OpCodes.Ldc_I4_1; + break; + case 2: + c = OpCodes.Ldc_I4_2; + break; + case 3: + c = OpCodes.Ldc_I4_3; + break; + case 4: + c = OpCodes.Ldc_I4_4; + break; + case 5: + c = OpCodes.Ldc_I4_5; + break; + case 6: + c = OpCodes.Ldc_I4_6; + break; + case 7: + c = OpCodes.Ldc_I4_7; + break; + case 8: + c = OpCodes.Ldc_I4_8; + break; + default: + if (value >= -128 && value <= 127) { + gen.Emit(OpCodes.Ldc_I4_S, (sbyte)value); + } else { + gen.Emit(OpCodes.Ldc_I4, value); + } + return; + } + gen.Emit(c); + } + } +} diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/IDispatchComObject.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/IDispatchComObject.cs new file mode 100644 index 0000000000..7a873e98f3 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/IDispatchComObject.cs @@ -0,0 +1,662 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Globalization; +using System.Linq.Expressions; +using System.Linq.Expressions.Compiler; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; +using ComTypes = System.Runtime.InteropServices.ComTypes; + +namespace System.Dynamic.ComInterop { + + /// + /// An object that implements IDispatch + /// + /// This currently has the following issues: + /// 1. If we prefer ComObjectWithTypeInfo over IDispatchComObject, then we will often not + /// IDispatchComObject since implementations of IDispatch often rely on a registered type library. + /// If we prefer IDispatchComObject over ComObjectWithTypeInfo, users get a non-ideal experience. + /// 2. IDispatch cannot distinguish between properties and methods with 0 arguments (and non-0 + /// default arguments?). So obj.foo() is ambiguous as it could mean invoking method foo, + /// or it could mean invoking the function pointer returned by property foo. + /// We are attempting to find whether we need to call a method or a property by examining + /// the ITypeInfo associated with the IDispatch. ITypeInfo tell's use what parameters the method + /// expects, is it a method or a property, what is the default property of the object, how to + /// create an enumerator for collections etc. + /// 3. IronPython processes the signature and converts ref arguments into return values. + /// However, since the signature of a DispMethod is not available beforehand, this conversion + /// is not possible. There could be other signature conversions that may be affected. How does + /// VB6 deal with ref arguments and IDispatch? + /// + /// We also support events for IDispatch objects: + /// Background: + /// COM objects support events through a mechanism known as Connect Points. + /// Connection Points are separate objects created off the actual COM + /// object (this is to prevent circular references between event sink + /// and event source). When clients want to sink events generated by + /// COM object they would implement callback interfaces (aka source + /// interfaces) and hand it over (advise) to the Connection Point. + /// + /// Implementation details: + /// When IDispatchComObject.TryGetMember request is received we first check + /// whether the requested member is a property or a method. If this check + /// fails we will try to determine whether an event is requested. To do + /// so we will do the following set of steps: + /// 1. Verify the COM object implements IConnectionPointContainer + /// 2. Attempt to find COM object's coclass's description + /// a. Query the object for IProvideClassInfo interface. Go to 3, if found + /// b. From object's IDispatch retrieve primary interface description + /// c. Scan coclasses declared in object's type library. + /// d. Find coclass implementing this particular primary interface + /// 3. Scan coclass for all its source interfaces. + /// 4. Check whether to any of the methods on the source interfaces matches + /// the request name + /// + /// Once we determine that TryGetMember requests an event we will return + /// an instance of BoundDispEvent class. This class has InPlaceAdd and + /// InPlaceSubtract operators defined. Calling InPlaceAdd operator will: + /// 1. An instance of ComEventSinksContainer class is created (unless + /// RCW already had one). This instance is hanged off the RCW in attempt + /// to bind the lifetime of event sinks to the lifetime of the RCW itself, + /// meaning event sink will be collected once the RCW is collected (this + /// is the same way event sinks lifetime is controlled by PIAs). + /// Notice: ComEventSinksContainer contains a Finalizer which will go and + /// unadvise all event sinks. + /// Notice: ComEventSinksContainer is a list of ComEventSink objects. + /// 2. Unless we have already created a ComEventSink for the required + /// source interface, we will create and advise a new ComEventSink. Each + /// ComEventSink implements a single source interface that COM object + /// supports. + /// 3. ComEventSink contains a map between method DISPIDs to the + /// multicast delegate that will be invoked when the event is raised. + /// 4. ComEventSink implements IReflect interface which is exposed as + /// custom IDispatch to COM consumers. This allows us to intercept calls + /// to IDispatch.Invoke and apply custom logic - in particular we will + /// just find and invoke the multicast delegate corresponding to the invoked + /// dispid. + /// + + internal sealed class IDispatchComObject : ComObject, IDynamicObject { + + private readonly IDispatch _dispatchObject; + private ComTypeDesc _comTypeDesc; + private static Dictionary _CacheComTypeDesc = new Dictionary(); + + private DispCallable _getter, _setter; + + + internal IDispatchComObject(IDispatch rcw) + : base(rcw) { + _dispatchObject = rcw; + } + + public override string ToString() { + EnsureScanDefinedMethods(); + + string typeName = this._comTypeDesc.TypeName; + if (String.IsNullOrEmpty(typeName)) + typeName = "IDispatch"; + + return String.Format(CultureInfo.CurrentCulture, "{0} ({1})", RuntimeCallableWrapper.ToString(), typeName); + } + + public ComTypeDesc ComTypeDesc { + get { + EnsureScanDefinedMethods(); + return _comTypeDesc; + } + } + + public IDispatch DispatchObject { + get { + return _dispatchObject; + } + } + + private static int GetIDsOfNames(IDispatch dispatch, string name, out int dispId) { + int[] dispIds = new int[1]; + Guid emtpyRiid = Guid.Empty; + int hresult = dispatch.TryGetIDsOfNames( + ref emtpyRiid, + new string[] { name }, + 1, + 0, + dispIds); + + dispId = dispIds[0]; + return hresult; + } + + static int Invoke(IDispatch dispatch, int memberDispId, out object result) { + Guid emtpyRiid = Guid.Empty; + ComTypes.DISPPARAMS dispParams = new ComTypes.DISPPARAMS(); + ComTypes.EXCEPINFO excepInfo = new ComTypes.EXCEPINFO(); + uint argErr; + int hresult = dispatch.TryInvoke( + memberDispId, + ref emtpyRiid, + 0, + ComTypes.INVOKEKIND.INVOKE_PROPERTYGET, + ref dispParams, + out result, + out excepInfo, + out argErr); + + return hresult; + } + + public bool TryGetGetItem(out DispCallable value) { + if (_getter != null) { + value = _getter; + return true; + } + + return SlowTryGetGetItem(out value); + } + + private bool SlowTryGetGetItem(out DispCallable value) { + EnsureScanDefinedMethods(); + + ComMethodDesc methodDesc = _comTypeDesc.GetItem; + + // The following attempts to get a method corresponding to "[PROPERTYGET, DISPID(0)] HRESULT Item(...)". + // However, without type information, we really don't know whether or not we have a property getter. + // All we can do is verify that the found dispId is DISPID_VALUE. So, if we find a dispId of DISPID_VALUE, + // we happily package it up as a property getter; otherwise, it's a no go... + if (methodDesc == null) { + int dispId; + string name = "Item"; + int hresult = GetIDsOfNames(_dispatchObject, name, out dispId); + if (hresult == ComHresults.DISP_E_UNKNOWNNAME) { + value = null; + return false; + } else if (hresult != ComHresults.S_OK) { + throw Error.CouldNotGetDispId(name, string.Format(CultureInfo.InvariantCulture, "0x{1:X})", hresult)); + } + + methodDesc = new ComMethodDesc(name, dispId, ComTypes.INVOKEKIND.INVOKE_PROPERTYGET); + _comTypeDesc.GetItem = methodDesc; + } + + Interlocked.CompareExchange( + ref _getter, + new DispCallable(_dispatchObject, methodDesc), + null); + + value = _getter; + return true; + } + + public bool TryGetSetItem(out DispCallable value) { + if (_setter != null) { + value = _setter; + return true; + } + + return SlowTryGetSetItem(out value); + } + + public bool SlowTryGetSetItem(out DispCallable value) { + EnsureScanDefinedMethods(); + + ComMethodDesc methodDesc = _comTypeDesc.SetItem; + + // The following attempts to get a method corresponding to "[PROPERTYPUT, DISPID(0)] HRESULT Item(...)". + // However, without type information, we really don't know whether or not we have a property setter. + // All we can do is verify that the found dispId is DISPID_VALUE. So, if we find a dispId of DISPID_VALUE, + // we happily package it up as a property setter; otherwise, it's a no go... + if (methodDesc == null) { + int dispId; + string name = "Item"; + int hresult = GetIDsOfNames(_dispatchObject, name, out dispId); + if (hresult == ComHresults.DISP_E_UNKNOWNNAME) { + value = null; + return false; + } else if (hresult != ComHresults.S_OK) { + throw Error.CouldNotGetDispId(name, string.Format(CultureInfo.InvariantCulture, "0x{1:X})", hresult)); + } else if (dispId != ComDispIds.DISPID_VALUE) { + value = null; + return false; + } + + methodDesc = new ComMethodDesc(name, dispId, ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT); + _comTypeDesc.SetItem = methodDesc; + } + + Interlocked.CompareExchange( + ref _setter, + new DispCallable(_dispatchObject, methodDesc), + null); + + value = _setter; + return true; + } + + internal bool TryGetIDOfName(string name) { + int dispId; + return GetIDsOfNames(_dispatchObject, name, out dispId) == ComHresults.S_OK; + } + + internal bool TryGetMemberMethod(string name, out ComMethodDesc method) { + EnsureScanDefinedMethods(); + return _comTypeDesc.Funcs.TryGetValue(name, out method); + } + + internal bool TryGetMemberEvent(string name, out ComEventDesc @event) { + EnsureScanDefinedEvents(); + return _comTypeDesc.Events.TryGetValue(name, out @event); + } + + internal bool TryGetMemberMethodExplicit(string name, out ComMethodDesc method) { + EnsureScanDefinedMethods(); + + // TODO: We have a thread-safety issue here right now + // TODO: since we are mutating _funcs array + // TODO: The workaround is to use Hashtable (which is thread-safe + // TODO: on read operations) to fetch the value out. + int dispId; + int hresult = GetIDsOfNames(_dispatchObject, name, out dispId); + + if (hresult == ComHresults.S_OK) { + ComMethodDesc cmd = new ComMethodDesc(name, dispId); + _comTypeDesc.Funcs.Add(name, cmd); + method = cmd; + return true; + } else if (hresult == ComHresults.DISP_E_UNKNOWNNAME) { + method = null; + return false; + } else { + throw Error.CouldNotGetDispId(name, string.Format(CultureInfo.InvariantCulture, "0x{1:X})", hresult)); + } + } + + internal bool TryGetPropertySetterExplicit(string name, out ComMethodDesc method, Type limitType) { + EnsureScanDefinedMethods(); + + // TODO: We have a thread-safety issue here right now + // TODO: since we are mutating _funcs array + // TODO: The workaround is to use Hashtable (which is thread-safe + // TODO: on read operations) to fetch the value out. + int dispId; + int hresult = GetIDsOfNames(_dispatchObject, name, out dispId); + + if (hresult == ComHresults.S_OK) { + // we do not know whether we have put or putref here + // and we will not guess and pretend we found both. + ComMethodDesc put = new ComMethodDesc(name, dispId, ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT); + _comTypeDesc.Puts.Add(name, put); + + ComMethodDesc putref = new ComMethodDesc(name, dispId, ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF); + _comTypeDesc.PutRefs.Add(name, putref); + + if (ComBinderHelpers.PreferPut(limitType)) { + method = put; + } else { + method = putref; + } + return true; + } else if (hresult == ComHresults.DISP_E_UNKNOWNNAME) { + method = null; + return false; + } else { + throw Error.CouldNotGetDispId(name, string.Format(CultureInfo.InvariantCulture, "0x{1:X})", hresult)); + } + } + + internal override IEnumerable MemberNames { + get { + EnsureScanDefinedMethods(); + EnsureScanDefinedEvents(); + + var names = new List(); + + foreach (string name in _comTypeDesc.Funcs.Keys) { + names.Add(name); + } + + if (_comTypeDesc.Events != null && _comTypeDesc.Events.Count > 0) { + foreach (string name in _comTypeDesc.Events.Keys) { + names.Add(name); + } + } + + return names.ToArray(); + } + } + + internal override IEnumerable> DataMembers { + get { + EnsureScanDefinedMethods(); + + Type comType = RuntimeCallableWrapper.GetType(); + var members = new List>(); + foreach (ComMethodDesc method in _comTypeDesc.Funcs.Values) { + if (method.IsDataMember) { + object value = comType.InvokeMember(method.Name, BindingFlags.GetProperty, null, RuntimeCallableWrapper, EmptyArray.Instance, CultureInfo.InvariantCulture); + members.Add(new KeyValuePair(method.Name, value)); + } + } + + return members.ToArray(); + } + } + + MetaObject IDynamicObject.GetMetaObject(Expression parameter) { + EnsureScanDefinedMethods(); + return new IDispatchMetaObject(parameter, _comTypeDesc, this); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] + internal static void GetFuncDescForDescIndex(ComTypes.ITypeInfo typeInfo, int funcIndex, out ComTypes.FUNCDESC funcDesc, out IntPtr funcDescHandle) { + IntPtr pFuncDesc = IntPtr.Zero; + typeInfo.GetFuncDesc(funcIndex, out pFuncDesc); + + // GetFuncDesc should never return null, this is just to be safe + if (pFuncDesc == IntPtr.Zero) { + throw Error.CannotRetrieveTypeInformation(); + } + + funcDesc = (ComTypes.FUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(ComTypes.FUNCDESC)); + funcDescHandle = pFuncDesc; + } + + private void EnsureScanDefinedEvents() { + + // _comTypeDesc.Events is null if we have not yet attempted + // to scan the object for events. + if (_comTypeDesc != null && _comTypeDesc.Events != null) { + return; + } + + // check type info in the type descriptions cache + ComTypes.ITypeInfo typeInfo = ComRuntimeHelpers.GetITypeInfoFromIDispatch(_dispatchObject, true); + if (typeInfo == null) { + _comTypeDesc = ComTypeDesc.CreateEmptyTypeDesc(); + return; + } + + ComTypes.TYPEATTR typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo); + + if (_comTypeDesc == null) { + lock (_CacheComTypeDesc) { + if (_CacheComTypeDesc.TryGetValue(typeAttr.guid, out _comTypeDesc) == true && + _comTypeDesc.Events != null) { + return; + } + } + } + + ComTypeDesc typeDesc = ComTypeDesc.FromITypeInfo(typeInfo); + + ComTypes.ITypeInfo classTypeInfo = null; + Dictionary events = null; + + var cpc = RuntimeCallableWrapper as ComTypes.IConnectionPointContainer; + if (cpc == null) { + // No ICPC - this object does not support events + events = ComTypeDesc.EmptyEvents; + } else if ((classTypeInfo = GetCoClassTypeInfo(this.RuntimeCallableWrapper, typeInfo)) == null) { + // no class info found - this object may support events + // but we could not discover those + Debug.Assert(false, "object support IConnectionPoint but no class info found"); + events = ComTypeDesc.EmptyEvents; + } else { + events = new Dictionary(); + + ComTypes.TYPEATTR classTypeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(classTypeInfo); + for (int i = 0; i < classTypeAttr.cImplTypes; i++) { + int hRefType; + classTypeInfo.GetRefTypeOfImplType(i, out hRefType); + + ComTypes.ITypeInfo interfaceTypeInfo; + classTypeInfo.GetRefTypeInfo(hRefType, out interfaceTypeInfo); + + ComTypes.IMPLTYPEFLAGS flags; + classTypeInfo.GetImplTypeFlags(i, out flags); + if ((flags & ComTypes.IMPLTYPEFLAGS.IMPLTYPEFLAG_FSOURCE) != 0) { + ScanSourceInterface(interfaceTypeInfo, ref events); + } + } + + if (events.Count == 0) { + events = ComTypeDesc.EmptyEvents; + } + } + + lock (_CacheComTypeDesc) { + ComTypeDesc cachedTypeDesc; + if (_CacheComTypeDesc.TryGetValue(typeAttr.guid, out cachedTypeDesc)) { + _comTypeDesc = cachedTypeDesc; + } else { + _comTypeDesc = typeDesc; + _CacheComTypeDesc.Add(typeAttr.guid, _comTypeDesc); + } + _comTypeDesc.Events = events; + } + + } + + private static void ScanSourceInterface(ComTypes.ITypeInfo sourceTypeInfo, ref Dictionary events) { + ComTypes.TYPEATTR sourceTypeAttribute = ComRuntimeHelpers.GetTypeAttrForTypeInfo(sourceTypeInfo); + + for (int index = 0; index < sourceTypeAttribute.cFuncs; index++) { + IntPtr funcDescHandleToRelease = IntPtr.Zero; + + try { + ComTypes.FUNCDESC funcDesc; + GetFuncDescForDescIndex(sourceTypeInfo, index, out funcDesc, out funcDescHandleToRelease); + + // we are not interested in hidden or restricted functions for now. + if ((funcDesc.wFuncFlags & (int)ComTypes.FUNCFLAGS.FUNCFLAG_FHIDDEN) != 0) { + continue; + } + if ((funcDesc.wFuncFlags & (int)ComTypes.FUNCFLAGS.FUNCFLAG_FRESTRICTED) != 0) { + continue; + } + + string name = ComRuntimeHelpers.GetNameOfMethod(sourceTypeInfo, funcDesc.memid); + + // Sometimes coclass has multiple source interfaces. Usually this is caused by + // adding new events and putting them on new interfaces while keeping the + // old interfaces around. This may cause name collisioning which we are + // resolving by keeping only the first event with the same name. + if (events.ContainsKey(name) == false) { + ComEventDesc eventDesc = new ComEventDesc(); + eventDesc.dispid = funcDesc.memid; + eventDesc.sourceIID = sourceTypeAttribute.guid; + events.Add(name, eventDesc); + } + } finally { + if (funcDescHandleToRelease != IntPtr.Zero) { + sourceTypeInfo.ReleaseFuncDesc(funcDescHandleToRelease); + } + } + } + } + + private static ComTypes.ITypeInfo GetCoClassTypeInfo(object rcw, ComTypes.ITypeInfo typeInfo) { + Debug.Assert(typeInfo != null); + + IProvideClassInfo provideClassInfo = rcw as IProvideClassInfo; + if (provideClassInfo != null) { + IntPtr typeInfoPtr = IntPtr.Zero; + try { + provideClassInfo.GetClassInfo(out typeInfoPtr); + if (typeInfoPtr != IntPtr.Zero) { + return Marshal.GetObjectForIUnknown(typeInfoPtr) as ComTypes.ITypeInfo; + } + } finally { + if (typeInfoPtr != IntPtr.Zero) { + Marshal.Release(typeInfoPtr); + } + } + } + + // retrieving class information through IPCI has failed - + // we can try scanning the typelib to find the coclass + + ComTypes.ITypeLib typeLib; + int typeInfoIndex; + typeInfo.GetContainingTypeLib(out typeLib, out typeInfoIndex); + string typeName = ComRuntimeHelpers.GetNameOfType(typeInfo); + + ComTypeLibDesc typeLibDesc = ComTypeLibDesc.GetFromTypeLib(typeLib); + ComTypeClassDesc coclassDesc = typeLibDesc.GetCoClassForInterface(typeName); + if (coclassDesc == null) { + return null; + } + + ComTypes.ITypeInfo typeInfoCoClass; + Guid coclassGuid = coclassDesc.Guid; + typeLib.GetTypeInfoOfGuid(ref coclassGuid, out typeInfoCoClass); + return typeInfoCoClass; + } + + private void EnsureScanDefinedMethods() { + if (_comTypeDesc != null && _comTypeDesc.Funcs != null) + return; + + ComTypes.ITypeInfo typeInfo = ComRuntimeHelpers.GetITypeInfoFromIDispatch(_dispatchObject, true); + if (typeInfo == null) { + _comTypeDesc = ComTypeDesc.CreateEmptyTypeDesc(); + return; + } + + ComTypes.TYPEATTR typeAttr = ComRuntimeHelpers.GetTypeAttrForTypeInfo(typeInfo); + + if (_comTypeDesc == null) { + lock (_CacheComTypeDesc) { + if (_CacheComTypeDesc.TryGetValue(typeAttr.guid, out _comTypeDesc) == true && + _comTypeDesc.Funcs != null) { + return; + } + } + } + + ComTypeDesc typeDesc = ComTypeDesc.FromITypeInfo(typeInfo); + + ComMethodDesc getItem = null; + ComMethodDesc setItem = null; + Dictionary funcs = new Dictionary(typeAttr.cFuncs); + Dictionary puts = new Dictionary(); + Dictionary putrefs = new Dictionary(); + Set usedDispIds = new Set(); + + for (int definedFuncIndex = 0; definedFuncIndex < typeAttr.cFuncs; definedFuncIndex++) { + IntPtr funcDescHandleToRelease = IntPtr.Zero; + + try { + ComTypes.FUNCDESC funcDesc; + GetFuncDescForDescIndex(typeInfo, definedFuncIndex, out funcDesc, out funcDescHandleToRelease); + + if ((funcDesc.wFuncFlags & (int)ComTypes.FUNCFLAGS.FUNCFLAG_FRESTRICTED) != 0) { + // This function is not meant for the script user to use. + continue; + } + + ComMethodDesc method = new ComMethodDesc(typeInfo, funcDesc); + + if ((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT) != 0) { + puts.Add(method.Name, method); + continue; + } + if ((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF) != 0) { + putrefs.Add(method.Name, method); + continue; + } + + usedDispIds.Add(funcDesc.memid); + + if (funcDesc.memid == ComDispIds.DISPID_NEWENUM) { + funcs.Add("GetEnumerator", method); + continue; + } + + funcs.Add(method.Name, method); + + // for the special dispId == 0, we need to store the method descriptor + // for the Do(GetItem) binder. + if (funcDesc.memid == ComDispIds.DISPID_VALUE) { + getItem = method; + } + } finally { + if (funcDescHandleToRelease != IntPtr.Zero) { + typeInfo.ReleaseFuncDesc(funcDescHandleToRelease); + } + } + } + + ProcessPut(funcs, puts, usedDispIds, ref setItem); + ProcessPut(funcs, putrefs, usedDispIds, ref setItem); + + lock (_CacheComTypeDesc) { + ComTypeDesc cachedTypeDesc; + if (_CacheComTypeDesc.TryGetValue(typeAttr.guid, out cachedTypeDesc)) { + _comTypeDesc = cachedTypeDesc; + } else { + _comTypeDesc = typeDesc; + _CacheComTypeDesc.Add(typeAttr.guid, _comTypeDesc); + } + _comTypeDesc.Funcs = funcs; + _comTypeDesc.Puts = puts; + _comTypeDesc.PutRefs = putrefs; + _comTypeDesc.GetItem = getItem; + _comTypeDesc.SetItem = setItem; + } + } + + private static void ProcessPut(Dictionary funcs, Dictionary methods, Set usedDispIds, ref ComMethodDesc setItem) { + foreach (ComMethodDesc method in methods.Values) { + if (!usedDispIds.Contains(method.DispId)) { + funcs.Add(method.Name, method); + usedDispIds.Add(method.DispId); + } + + // for the special dispId == 0, we need to store + // the method descriptor for the Do(SetItem) binder. + if (method.DispId == ComDispIds.DISPID_VALUE && setItem == null) { + setItem = method; + } + } + } + + internal bool TryGetPropertySetter(string name, out ComMethodDesc method, Type limitType) { + EnsureScanDefinedMethods(); + + if (ComBinderHelpers.PreferPut(limitType)) { + return _comTypeDesc.Puts.TryGetValue(name, out method) || + _comTypeDesc.PutRefs.TryGetValue(name, out method); + } else { + return _comTypeDesc.PutRefs.TryGetValue(name, out method) || + _comTypeDesc.Puts.TryGetValue(name, out method); + } + } + + internal bool TryGetEventHandler(string name, out ComEventDesc @event) { + EnsureScanDefinedEvents(); + return _comTypeDesc.Events.TryGetValue(name, out @event); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/IDispatchMetaObject.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/IDispatchMetaObject.cs new file mode 100644 index 0000000000..afcb07096c --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/IDispatchMetaObject.cs @@ -0,0 +1,251 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Linq.Expressions; + +namespace System.Dynamic.ComInterop { + + internal sealed class IDispatchMetaObject : ComFallbackMetaObject { + private readonly ComTypeDesc _wrapperType; + private readonly IDispatchComObject _self; + + internal IDispatchMetaObject(Expression expression, ComTypeDesc wrapperType, IDispatchComObject self) + : base(expression, Restrictions.Empty, self) { + _wrapperType = wrapperType; + _self = self; + } + + public override MetaObject BindInvokeMember(InvokeMemberBinder binder, MetaObject[] args) { + ContractUtils.RequiresNotNull(binder, "binder"); + + if (args.Any(arg => ComBinderHelpers.IsStrongBoxArg(arg))) { + return ComBinderHelpers.RewriteStrongBoxAsRef(binder, this, args); + } + + ComMethodDesc methodDesc; + + if (_wrapperType.Funcs.TryGetValue(binder.Name, out methodDesc) || + _self.TryGetMemberMethodExplicit(binder.Name, out methodDesc)) { + return new ComInvokeBinder( + binder.Arguments, + args, + IDispatchRestriction(), + Expression.Constant(methodDesc), + Expression.Property( + Expression.Convert(Expression, typeof(IDispatchComObject)), + typeof(IDispatchComObject).GetProperty("DispatchObject") + ), + methodDesc + ).Invoke(); + } + + return base.BindInvokeMember(binder, args); + } + + public override MetaObject BindGetMember(GetMemberBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + + ComMethodDesc method; + ComEventDesc @event; + + // 1. Try methods + if (_self.TryGetMemberMethod(binder.Name, out method)) { + return BindGetMember(method); + } + + // 2. Try events + if (_self.TryGetMemberEvent(binder.Name, out @event)) { + return BindEvent(@event); + } + + // 3. Try methods explicitly by name + if (_self.TryGetMemberMethodExplicit(binder.Name, out method)) { + return BindGetMember(method); + } + + // 4. Fallback + return base.BindGetMember(binder); + } + + private MetaObject BindGetMember(ComMethodDesc method) { + Restrictions restrictions = IDispatchRestriction(); + Expression dispatch = + Expression.Property( + Helpers.Convert(Expression, typeof(IDispatchComObject)), + typeof(IDispatchComObject).GetProperty("DispatchObject") + ); + + if (method.IsDataMember) { + if (method.Parameters.Length == 0) { + return new ComInvokeBinder( + new ArgumentInfo[0], + MetaObject.EmptyMetaObjects, + restrictions, + Expression.Constant(method), + dispatch, + method + ).Invoke(); + } + } + + return new MetaObject( + Expression.Call( + typeof(ComRuntimeHelpers).GetMethod("CreateDispCallable"), + dispatch, + Expression.Constant(method) + ), + restrictions + ); + } + + private MetaObject BindEvent(ComEventDesc @event) { + // BoundDispEvent CreateComEvent(object rcw, Guid sourceIid, int dispid) + Expression result = + Expression.Call( + typeof(ComRuntimeHelpers).GetMethod("CreateComEvent"), + ComObject.RcwFromComObject(Expression), + Expression.Constant(@event.sourceIID), + Expression.Constant(@event.dispid) + ); + + return new MetaObject( + result, + IDispatchRestriction() + ); + } + + public override MetaObject BindGetIndex(GetIndexBinder binder, MetaObject[] indexes) { + ContractUtils.RequiresNotNull(binder, "binder"); + return IndexOperation(binder.FallbackGetIndex(UnwrapSelf(), indexes), indexes, "TryGetGetItem"); + } + + public override MetaObject BindSetIndex(SetIndexBinder binder, MetaObject[] indexes, MetaObject value) { + ContractUtils.RequiresNotNull(binder, "binder"); + return IndexOperation(binder.FallbackSetIndex(UnwrapSelf(), indexes, value), indexes.AddLast(value), "TryGetSetItem"); + } + + private MetaObject IndexOperation(MetaObject fallback, MetaObject[] args, string method) { + ParameterExpression callable = Expression.Variable(typeof(DispCallable), "callable"); + + Expression[] callArgs = new Expression[args.Length + 1]; + for (int i = 0; i < args.Length; i++) { + callArgs[i + 1] = args[i].Expression; + } + callArgs[0] = callable; + + Expression result = Expression.Block( + new ParameterExpression[] { callable }, + Expression.Condition( + Expression.Call( + Expression.Convert(Expression, typeof(IDispatchComObject)), + typeof(IDispatchComObject).GetMethod(method), + callable + ), + Expression.Dynamic(new ComInvokeAction(), typeof(object), callArgs), + Helpers.Convert(fallback.Expression, typeof(object)) + ) + ); + + return new MetaObject( + result, + Restrictions.Combine(args).Merge(IDispatchRestriction()).Merge(fallback.Restrictions) + ); + } + + + public override MetaObject BindSetMember(SetMemberBinder binder, MetaObject value) { + ContractUtils.RequiresNotNull(binder, "binder"); + + return + // 1. Check for simple property put + TryPropertyPut(binder, value) ?? + + // 2. Check for event handler hookup where the put is dropped + TryEventHandlerNoop(binder, value) ?? + + // 3. Fallback + base.BindSetMember(binder, value); + } + + private MetaObject TryPropertyPut(SetMemberBinder binder, MetaObject value) { + ComMethodDesc method; + if (_self.TryGetPropertySetter(binder.Name, out method, value.LimitType) || + _self.TryGetPropertySetterExplicit(binder.Name, out method, value.LimitType)) { + Restrictions restrictions = IDispatchRestriction(); + Expression dispatch = + Expression.Property( + Helpers.Convert(Expression, typeof(IDispatchComObject)), + typeof(IDispatchComObject).GetProperty("DispatchObject") + ); + + return new ComInvokeBinder( + new ArgumentInfo[0], + new[] { value }, + restrictions, + Expression.Constant(method), + dispatch, + method + ).Invoke(); + } + + return null; + } + + private MetaObject TryEventHandlerNoop(SetMemberBinder binder, MetaObject value) { + ComEventDesc @event; + if (_self.TryGetEventHandler(binder.Name, out @event) && value.LimitType == typeof(BoundDispEvent)) { + // Drop the event property set. + return new MetaObject( + Expression.Constant(null), + value.Restrictions.Merge(IDispatchRestriction()).Merge(Restrictions.GetTypeRestriction(value.Expression, typeof(BoundDispEvent))) + ); + } + + return null; + } + + private Restrictions IDispatchRestriction() { + Expression @this = Expression; + return Restrictions.GetTypeRestriction( + @this, typeof(IDispatchComObject) + ).Merge( + Restrictions.GetExpressionRestriction( + Expression.Equal( + Expression.Property( + Expression.Convert(@this, typeof(IDispatchComObject)), + typeof(IDispatchComObject).GetProperty("ComTypeDesc") + ), + Expression.Constant(_wrapperType) + ) + ) + ); + } + + protected override ComUnwrappedMetaObject UnwrapSelf() { + return new ComUnwrappedMetaObject( + ComObject.RcwFromComObject(Expression), + IDispatchRestriction(), + _self.RuntimeCallableWrapper + ); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/NullArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/NullArgBuilder.cs new file mode 100644 index 0000000000..1d2338f675 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/NullArgBuilder.cs @@ -0,0 +1,35 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Diagnostics; +using System.Linq.Expressions; + +namespace System.Dynamic.ComInterop { + + /// + /// ArgBuilder which always produces null. + /// + internal sealed class NullArgBuilder : ArgBuilder { + internal NullArgBuilder() { } + + internal override Expression Marshal(Expression parameter) { + return Expression.Constant(null); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Set.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Set.cs new file mode 100644 index 0000000000..3a092fd45c --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Set.cs @@ -0,0 +1,80 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; + +namespace System.Dynamic.ComInterop { + + /// + /// A simple hashset, built on Dictionary{K, V} + /// + /// TODO: should remove this in favor of HashSet{T} + /// + internal sealed class Set : ICollection { + private readonly Dictionary _data; + + internal Set() { + _data = new Dictionary(); + } + + internal Set(IEqualityComparer comparer) { + _data = new Dictionary(comparer); + } + + internal Set(IList list) { + _data = new Dictionary(list.Count); + foreach (T t in list) { + _data.Add(t, null); + } + } + + public void Add(T item) { + _data[item] = null; + } + + public void Clear() { + _data.Clear(); + } + + public bool Contains(T item) { + return _data.ContainsKey(item); + } + + public void CopyTo(T[] array, int arrayIndex) { + _data.Keys.CopyTo(array, arrayIndex); + } + + public int Count { + get { return _data.Count; } + } + + public bool IsReadOnly { + get { return false; } + } + + public bool Remove(T item) { + return _data.Remove(item); + } + + public IEnumerator GetEnumerator() { + return _data.Keys.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return _data.Keys.GetEnumerator(); + } + } +} diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/SimpleArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/SimpleArgBuilder.cs new file mode 100644 index 0000000000..a2b6a4fd4d --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/SimpleArgBuilder.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT + +using System.Diagnostics; +using System.Globalization; +using System.Linq.Expressions; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + /// + /// SimpleArgBuilder produces the value produced by the user as the argument value. It + /// also tracks information about the original parameter and is used to create extended + /// methods for params arrays and param dictionary functions. + /// + internal class SimpleArgBuilder : ArgBuilder { + private Type _parameterType; + + internal SimpleArgBuilder(Type parameterType) { + _parameterType = parameterType; + } + + internal override Expression Marshal(Expression parameter) { + Debug.Assert(parameter != null); + return Helpers.Convert(parameter, _parameterType); + } + + internal override Expression UnmarshalFromRef(Expression newValue) { + Debug.Assert(newValue != null && newValue.Type.IsAssignableFrom(_parameterType)); + + return base.UnmarshalFromRef(newValue); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/SplatCallSite.Generated.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/SplatCallSite.Generated.cs new file mode 100644 index 0000000000..95daf154f3 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/SplatCallSite.Generated.cs @@ -0,0 +1,134 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Runtime.CompilerServices; + +namespace System.Dynamic.ComInterop { + + internal sealed partial class SplatCallSite { + // TODO: is it worth having the generated helpers? + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "args")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper0(CallSite> site, object[] args) { + return site.Target(site); + } + + #region Generated SplatCallSite call helpers + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_splatsite from: generate_dynsites.py + + // + // Splatting targets for dynamic sites + // + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper1(CallSite> site, object[] args) { + return site.Target(site, args[0]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper2(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper3(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper4(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2], args[3]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper5(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2], args[3], args[4]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper6(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2], args[3], args[4], args[5]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper7(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2], args[3], args[4], args[5], args[6]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper8(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper9(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper10(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper11(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper12(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper13(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper14(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13]); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [Obsolete("used by generated code", true)] + public static object CallHelper15(CallSite> site, object[] args) { + return site.Target(site, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14]); + } + + + // *** END GENERATED CODE *** + + #endregion + } +} diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/SplatCallSite.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/SplatCallSite.cs new file mode 100644 index 0000000000..3e33e2329c --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/SplatCallSite.cs @@ -0,0 +1,110 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Linq.Expressions.Compiler; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Linq.Expressions; + +namespace System.Dynamic.ComInterop { + + // TODO: this entire class should go away. + // Instead we should be using a dynamic convert action to get a delegate + internal sealed partial class SplatCallSite { + internal delegate object SplatCaller(object[] args); + + // TODO: Should free these eventually + private readonly SynchronizedDictionary _callers = new SynchronizedDictionary(); + private readonly CallSiteBinder _binder; + + public SplatCallSite(CallSiteBinder binder) { + _binder = binder; + } + + public object Invoke(object[] args) { + Debug.Assert(args != null); + + SplatCaller caller; + if (!_callers.TryGetValue(args.Length, out caller)) { + _callers[args.Length] = caller = MakeCaller(args.Length); + } + + return caller(args); + } + + private SplatCaller MakeCaller(int args) { + MethodInfo mi = GetType().GetMethod("CallHelper" + args); + if (mi != null) { + Type delegateType = mi.GetParameters()[0].ParameterType.GetGenericArguments()[0]; + CallSite site = CallSite.Create(delegateType, _binder); + return (SplatCaller)Delegate.CreateDelegate(typeof(SplatCaller), site, mi); + } + return MakeBigCaller(args); + } + + /// + /// Uses LCG to create method such as this: + /// + /// object SplatCaller(CallSite{T} site, object[] args) { + /// return site.Target(site, args[0], args[1], args[2], ...); + /// } + /// + /// where the CallSite is bound to the delegate + /// + /// the number of arguments + /// a SplatCaller delegate. + private SplatCaller MakeBigCaller(int args) { + // Get the dynamic site type + var siteDelegateTypeArgs = new Type[args + 2]; + siteDelegateTypeArgs[0] = typeof(CallSite); + for (int i = 1, n = siteDelegateTypeArgs.Length; i < n; i++) { + siteDelegateTypeArgs[i] = typeof(object); + } + Type siteDelegateType = Expression.GetDelegateType(siteDelegateTypeArgs); + + // Create the callsite and get its type + CallSite callSite = CallSite.Create(siteDelegateType, _binder); + Type siteType = callSite.GetType(); + + var method = new DynamicMethod("_stub_SplatCaller", typeof(object), new Type[] { siteType, typeof(object[]) }, true); + var gen = method.GetILGenerator(); + + // Emit the site's target + gen.Emit(OpCodes.Ldarg_0); + gen.Emit(OpCodes.Ldfld, siteType.GetField("Target")); + + // Emit the site + gen.Emit(OpCodes.Ldarg_0); + + // Emit the arguments + for (int i = 0; i < args; i++) { + gen.Emit(OpCodes.Ldarg_1); + gen.EmitInt(i); + gen.Emit(OpCodes.Ldelem_Ref); + } + + // Invoke the target + gen.Emit(OpCodes.Callvirt, siteDelegateType.GetMethod("Invoke")); + gen.Emit(OpCodes.Ret); + + // Create the delegate + return method.CreateDelegate(callSite); + } + } +} diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/StringArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/StringArgBuilder.cs new file mode 100644 index 0000000000..9e005b5a47 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/StringArgBuilder.cs @@ -0,0 +1,86 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + + internal class StringArgBuilder : SimpleArgBuilder { + private bool _isWrapper; + + internal StringArgBuilder(Type parameterType) + : base(parameterType) { + + Debug.Assert(parameterType == typeof(string) || + parameterType == typeof(BStrWrapper)); + + _isWrapper = parameterType == typeof(BStrWrapper); + } + + internal override Expression Marshal(Expression parameter) { + parameter = base.Marshal(parameter); + + // parameter.WrappedObject + if (_isWrapper) { + parameter = Expression.Property( + Helpers.Convert(parameter, typeof(BStrWrapper)), + typeof(BStrWrapper).GetProperty("WrappedObject") + ); + }; + + return parameter; + } + + internal override Expression MarshalToRef(Expression parameter) { + parameter = Marshal(parameter); + + + // Marshal.StringToBSTR(parameter) + return Expression.Call( + typeof(Marshal).GetMethod("StringToBSTR"), + parameter + ); + } + + internal override Expression UnmarshalFromRef(Expression value) { + // value == IntPtr.Zero ? null : Marshal.PtrToStringBSTR(value); + Expression unmarshal = Expression.Condition( + Expression.Equal(value, Expression.Constant(IntPtr.Zero)), + Expression.Constant(null, typeof(string)), // default value + Expression.Call( + typeof(Marshal).GetMethod("PtrToStringBSTR"), + value + ) + ); + + if (_isWrapper) { + unmarshal = Expression.New( + typeof(BStrWrapper).GetConstructor(new Type[] { typeof(string) }), + unmarshal + ); + }; + + return base.UnmarshalFromRef(unmarshal); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/SynchronizedDictionary.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/SynchronizedDictionary.cs new file mode 100644 index 0000000000..71a149acd0 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/SynchronizedDictionary.cs @@ -0,0 +1,175 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; + +namespace System.Dynamic.ComInterop { + + /// + /// Dictionary[TKey, TValue] is not thread-safe in the face of concurrent reads and writes. SynchronizedDictionary + /// provides a thread-safe implementation. It holds onto a Dictionary[TKey, TValue] instead of inheriting from + /// it so that users who need to do manual synchronization can access the underlying Dictionary[TKey, TValue]. + /// + internal sealed class SynchronizedDictionary : + IDictionary, + ICollection>, + IEnumerable> { + + Dictionary _dictionary = new Dictionary(); + + /// + /// This returns the raw unsynchronized Dictionary[TKey, TValue]. Users are responsible for locking + /// on it before accessing it. Also, it should not be arbitrarily handed out to other code since deadlocks + /// can be caused if other code incorrectly locks on it. + /// + internal Dictionary UnderlyingDictionary { + get { + return _dictionary; + } + } + + #region IDictionary Members + + public void Add(TKey key, TValue value) { + lock (_dictionary) { + _dictionary.Add(key, value); + } + } + + public bool ContainsKey(TKey key) { + lock (_dictionary) { + return _dictionary.ContainsKey(key); + } + } + + public ICollection Keys { + get { + lock (_dictionary) { + return _dictionary.Keys; + } + } + } + + public bool Remove(TKey key) { + lock (_dictionary) { + return _dictionary.Remove(key); + } + } + + public bool TryGetValue(TKey key, out TValue value) { + lock (_dictionary) { + return _dictionary.TryGetValue(key, out value); + } + } + + public ICollection Values { + get { + lock (_dictionary) { + return _dictionary.Values; + } + } + } + + public TValue this[TKey key] { + get { + lock (_dictionary) { + return _dictionary[key]; + } + } + set { + lock (_dictionary) { + _dictionary[key] = value; + } + } + } + + #endregion + + #region ICollection> Members + + private ICollection> AsICollection() { + return ((ICollection>)_dictionary); + } + + public void Add(KeyValuePair item) { + lock (_dictionary) { + AsICollection().Add(item); + } + } + + public void Clear() { + lock (_dictionary) { + AsICollection().Clear(); + } + } + + public bool Contains(KeyValuePair item) { + lock (_dictionary) { + return AsICollection().Contains(item); + } + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) { + lock (_dictionary) { + AsICollection().CopyTo(array, arrayIndex); + } + } + + public int Count { + get { + lock (_dictionary) { + return AsICollection().Count; + } + } + } + + public bool IsReadOnly { + get { + lock (_dictionary) { + return AsICollection().IsReadOnly; + } + } + } + + public bool Remove(KeyValuePair item) { + lock (_dictionary) { + return AsICollection().Remove(item); + } + } + + #endregion + + #region IEnumerable> Members + + public IEnumerator> GetEnumerator() { + lock (_dictionary) { + return _dictionary.GetEnumerator(); + } + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() { + lock (_dictionary) { + return _dictionary.GetEnumerator(); + } + } + + #endregion + } +} diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/System.Dynamic.ComInterop.txt b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/System.Dynamic.ComInterop.txt new file mode 100644 index 0000000000..ad5e32b294 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/System.Dynamic.ComInterop.txt @@ -0,0 +1,81 @@ +##################################################################################### +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# This source code is subject to terms and conditions of the Microsoft Public License. A +# copy of the license can be found in the License.html file at the root of this distribution. If +# you cannot locate the Microsoft Public License, please send an email to +# ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound +# by the terms of the Microsoft Public License. +# +# You must not remove this notice, or any other, from this software. +# +# +##################################################################################### + +; NOTE: do not use \", use ' instead +; NOTE: Use # or ; for comments + +InvalidArgumentValue=Invalid argument value +ComObjectExpected=COM object is expected. + +## ExceptionType=InvalidOperationException +RemovingUnregisteredEvent=Removing an event handler that is not registered. + +## ExceptionType=ArgumentException +COMObjectDoesNotSupportEvents=COM object does not support events. + +## ExceptionType=ArgumentException +COMObjectDoesNotSupportSourceInterface=COM object does not support specified source interface. + +## ExceptionType=InvalidOperationException +RemovingUnregisteredHandler=Removing not registered handler. + +## ExceptionType=InvalidOperationException +SetComObjectDataFailed=Marshal.SetComObjectData failed. + +## ExceptionType=InvalidOperationException +MethodShouldNotBeCalled=This method exists only to keep the compiler happy. + +## ExceptionType=InvalidProgramException +DefaultValueCannotBeRead=Default value of COM parameter cannot be read properly. + +## ExceptionType=InvalidOperationException +UnexpectedVarEnum=Unexpected VarEnum {0}. + +## ExceptionType=System.Reflection.TargetParameterCountException +DispBadParamCount=Error while invoking {0}. + +## ExceptionType=MissingMemberException +DispMemberNotFound=Error while invoking {0}. + +## ExceptionType=ArgumentException +DispNoNamedArgs=Error while invoking {0}. Named arguments are not supported. + +## ExceptionType=OverflowException +DispOverflow=Error while invoking {0}. + +## ExceptionType=ArgumentException +DispTypeMismatch=Could not convert argument {0} for call to {1}. + +## ExceptionType=ArgumentException +DispParamNotOptional=Error while invoking {0}. A required parameter was omitted. + +## ExceptionType=InvalidOperationException +CannotRetrieveTypeInformation=ResolveComReference.CannotRetrieveTypeInformation. + +## ExceptionType=ArgumentException +GetIDsOfNamesInvalid=IDispatch::GetIDsOfNames behaved unexpectedly for {0}. + +## ExceptionType=InvalidOperationException +UnsupportedEnumType=Attempting to wrap an unsupported enum type. + +## ExceptionType=MissingMemberException +CouldNotGetDispId=Could not get dispatch ID for {0} (error: {1}). + +## ExceptionType=System.Reflection.AmbiguousMatchException +AmbiguousConversion=There are valid conversions from {0} to {1}. + +## ExceptionType=NotImplementedException +VariantGetAccessorNYI=Variant.GetAccessor cannot handle {0}. + diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/TypeExtensions.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/TypeExtensions.cs new file mode 100644 index 0000000000..810266e553 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/TypeExtensions.cs @@ -0,0 +1,45 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; + +namespace System.Dynamic.ComInterop { + + // Extensions on System.Type and friends + internal static class TypeExtensions { + + /// + /// Creates a closed delegate for the given (dynamic)method. + /// + internal static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType, object target) { + Debug.Assert(methodInfo != null && delegateType != null); + + var dm = methodInfo as DynamicMethod; + if (dm != null) { + return dm.CreateDelegate(delegateType, target); + } else { + return Delegate.CreateDelegate(delegateType, target, methodInfo); + } + } + + // Warning: This can be slower than you might expect due to the generic type argument & static method + internal static T CreateDelegate(this MethodInfo methodInfo, object target) { + return (T)(object)methodInfo.CreateDelegate(typeof(T), target); + } + } +} diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/TypeUtils.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/TypeUtils.cs new file mode 100644 index 0000000000..a15225b2b9 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/TypeUtils.cs @@ -0,0 +1,258 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; + +namespace System.Dynamic.ComInterop { + + internal static class TypeUtils { + private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + internal const MethodAttributes PublicStatic = MethodAttributes.Public | MethodAttributes.Static; + + //CONFORMING + internal static Type GetNonNullableType(Type type) { + if (IsNullableType(type)) { + return type.GetGenericArguments()[0]; + } + return type; + } + + //CONFORMING + internal static bool IsNullableType(this Type type) { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + //CONFORMING + internal static bool AreReferenceAssignable(Type dest, Type src) { + // WARNING: This actually implements "Is this identity assignable and/or reference assignable?" + if (dest == src) { + return true; + } + if (!dest.IsValueType && !src.IsValueType && AreAssignable(dest, src)) { + return true; + } + return false; + } + //CONFORMING + internal static bool AreAssignable(Type dest, Type src) { + if (dest == src) { + return true; + } + if (dest.IsAssignableFrom(src)) { + return true; + } + if (dest.IsArray && src.IsArray && dest.GetArrayRank() == src.GetArrayRank() && AreReferenceAssignable(dest.GetElementType(), src.GetElementType())) { + return true; + } + if (src.IsArray && dest.IsGenericType && + (dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IEnumerable<>) + || dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IList<>) + || dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.ICollection<>)) + && dest.GetGenericArguments()[0] == src.GetElementType()) { + return true; + } + return false; + } + + //CONFORMING + internal static bool IsImplicitlyConvertible(Type source, Type destination) { + return IsIdentityConversion(source, destination) || + IsImplicitNumericConversion(source, destination) || + IsImplicitReferenceConversion(source, destination) || + IsImplicitBoxingConversion(source, destination) || + IsImplicitNullableConversion(source, destination); + } + + internal static bool IsImplicitlyConvertible(Type source, Type destination, bool considerUserDefined) { + return IsImplicitlyConvertible(source, destination) || + (considerUserDefined && GetUserDefinedCoercionMethod(source, destination, true) != null); + } + + //CONFORMING + internal static MethodInfo GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType, bool implicitOnly) { + // check for implicit coercions first + Type nnExprType = TypeUtils.GetNonNullableType(convertFrom); + Type nnConvType = TypeUtils.GetNonNullableType(convertToType); + // try exact match on types + MethodInfo[] eMethods = nnExprType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + MethodInfo method = FindConversionOperator(eMethods, convertFrom, convertToType, implicitOnly); + if (method != null) { + return method; + } + MethodInfo[] cMethods = nnConvType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + method = FindConversionOperator(cMethods, convertFrom, convertToType, implicitOnly); + if (method != null) { + return method; + } + // try lifted conversion + if (nnExprType != convertFrom || nnConvType != convertToType) { + method = FindConversionOperator(eMethods, nnExprType, nnConvType, implicitOnly); + if (method == null) { + method = FindConversionOperator(cMethods, nnExprType, nnConvType, implicitOnly); + } + if (method != null) { + return method; + } + } + return null; + } + + //CONFORMING + internal static MethodInfo FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo, bool implicitOnly) { + foreach (MethodInfo mi in methods) { + if (mi.Name != "op_Implicit" && (implicitOnly || mi.Name != "op_Explicit")) + continue; + if (mi.ReturnType != typeTo) + continue; + ParameterInfo[] pis = mi.GetParameters(); + if (pis[0].ParameterType != typeFrom) + continue; + return mi; + } + return null; + } + + + //CONFORMING + private static bool IsIdentityConversion(Type source, Type destination) { + return source == destination; + } + + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private static bool IsImplicitNumericConversion(Type source, Type destination) { + TypeCode tcSource = Type.GetTypeCode(source); + TypeCode tcDest = Type.GetTypeCode(destination); + + switch (tcSource) { + case TypeCode.SByte: + switch (tcDest) { + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.Byte: + switch (tcDest) { + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.Int16: + switch (tcDest) { + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.UInt16: + switch (tcDest) { + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.Int32: + switch (tcDest) { + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.UInt32: + switch (tcDest) { + case TypeCode.UInt32: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.Int64: + case TypeCode.UInt64: + switch (tcDest) { + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.Char: + switch (tcDest) { + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.Single: + return (tcDest == TypeCode.Double); + } + return false; + } + + //CONFORMING + private static bool IsImplicitReferenceConversion(Type source, Type destination) { + return AreAssignable(destination, source); + } + + //CONFORMING + private static bool IsImplicitBoxingConversion(Type source, Type destination) { + if (source.IsValueType && (destination == typeof(object) || destination == typeof(System.ValueType))) + return true; + if (source.IsEnum && destination == typeof(System.Enum)) + return true; + return false; + } + + //CONFORMING + private static bool IsImplicitNullableConversion(Type source, Type destination) { + if (IsNullableType(destination)) + return IsImplicitlyConvertible(GetNonNullableType(source), GetNonNullableType(destination)); + return false; + } + } +} diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/UnknownArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/UnknownArgBuilder.cs new file mode 100644 index 0000000000..0ac7a55f53 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/UnknownArgBuilder.cs @@ -0,0 +1,86 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + internal class UnknownArgBuilder : SimpleArgBuilder { + private bool _isWrapper; + + internal UnknownArgBuilder(Type parameterType) + : base(parameterType) { + + _isWrapper = parameterType == typeof(UnknownWrapper); + } + + internal override Expression Marshal(Expression parameter) { + parameter = base.Marshal(parameter); + + // parameter.WrappedObject + if (_isWrapper) { + parameter = Expression.Property( + Helpers.Convert(parameter, typeof(UnknownWrapper)), + typeof(UnknownWrapper).GetProperty("WrappedObject") + ); + }; + + return Helpers.Convert(parameter, typeof(object)); + } + + internal override Expression MarshalToRef(Expression parameter) { + parameter = Marshal(parameter); + + // parameter == null ? IntPtr.Zero : Marshal.GetIUnknownForObject(parameter); + return Expression.Condition( + Expression.Equal(parameter, Expression.Constant(null)), + Expression.Constant(IntPtr.Zero), + Expression.Call( + typeof(Marshal).GetMethod("GetIUnknownForObject"), + parameter + ) + ); + } + + + internal override Expression UnmarshalFromRef(Expression value) { + // value == IntPtr.Zero ? null : Marshal.GetObjectForIUnknown(value); + Expression unmarshal = Expression.Condition( + Expression.Equal(value, Expression.Constant(IntPtr.Zero)), + Expression.Constant(null), + Expression.Call( + typeof(Marshal).GetMethod("GetObjectForIUnknown"), + value + ) + ); + + if (_isWrapper) { + unmarshal = Expression.New( + typeof(UnknownWrapper).GetConstructor(new Type[] { typeof(object) }), + unmarshal + ); + }; + + return base.UnmarshalFromRef(unmarshal); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/VarEnumSelector.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/VarEnumSelector.cs new file mode 100644 index 0000000000..7a639e01ef --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/VarEnumSelector.cs @@ -0,0 +1,407 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + /// + /// If a managed user type (as opposed to a primitive type or a COM object) is passed as an argument to a COM call, we need + /// to determine the VarEnum type we will marshal it as. We have the following options: + /// 1. Raise an exception. Languages with their own version of primitive types would not be able to call + /// COM methods using the language's types (for eg. strings in IronRuby are not System.String). An explicit + /// cast would be needed. + /// 2. We could marshal it as VT_DISPATCH. Then COM code will be able to access all the APIs in a late-bound manner, + /// but old COM components will probably malfunction if they expect a primitive type. + /// 3. We could guess which primitive type is the closest match. This will make COM components be as easily + /// accessible as .NET methods. + /// 4. We could use the type library to check what the expected type is. However, the type library may not be available. + /// + /// VarEnumSelector implements option # 3 + /// + internal class VarEnumSelector { + private readonly VariantBuilder[] _variantBuilders; + + private static readonly Dictionary _ComToManagedPrimitiveTypes = CreateComToManagedPrimitiveTypes(); + private static readonly IList> _ComPrimitiveTypeFamilies = CreateComPrimitiveTypeFamilies(); + + internal VarEnumSelector(Type[] explicitArgTypes) { + _variantBuilders = new VariantBuilder[explicitArgTypes.Length]; + + for (int i = 0; i < explicitArgTypes.Length; i++) { + _variantBuilders[i] = GetVariantBuilder(explicitArgTypes[i]); + } + } + + internal VariantBuilder[] VariantBuilders { + get { + return _variantBuilders; + } + } + + /// + /// Gets the managed type that an object needs to be coverted to in order for it to be able + /// to be represented as a Variant. + /// + /// In general, there is a many-to-many mapping between Type and VarEnum. However, this method + /// returns a simple mapping that is needed for the current implementation. The reason for the + /// many-to-many relation is: + /// 1. Int32 maps to VT_I4 as well as VT_ERROR, and Decimal maps to VT_DECIMAL and VT_CY. However, + /// this changes if you throw the wrapper types into the mix. + /// 2. There is no Type to represent COM types. __ComObject is a private type, and Object is too + /// general. + /// + internal static Type GetManagedMarshalType(VarEnum varEnum) { + Debug.Assert((varEnum & VarEnum.VT_BYREF) == 0); + + if (varEnum == VarEnum.VT_CY) { + return typeof(CurrencyWrapper); + } + + if (Variant.IsPrimitiveType(varEnum)) { + return _ComToManagedPrimitiveTypes[varEnum]; + } + + switch (varEnum) { + case VarEnum.VT_EMPTY: + case VarEnum.VT_NULL: + case VarEnum.VT_UNKNOWN: + case VarEnum.VT_DISPATCH: + case VarEnum.VT_VARIANT: + return typeof(Object); + + case VarEnum.VT_ERROR: + return typeof(ErrorWrapper); + + default: + throw Error.UnexpectedVarEnum(varEnum); + } + } + + private static Dictionary CreateComToManagedPrimitiveTypes() { + Dictionary dict = new Dictionary(); + + #region Generated ComToManagedPrimitiveTypes + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_ComToManagedPrimitiveTypes from: generate_comdispatch.py + + dict[VarEnum.VT_I1] = typeof(SByte); + dict[VarEnum.VT_I2] = typeof(Int16); + dict[VarEnum.VT_I4] = typeof(Int32); + dict[VarEnum.VT_I8] = typeof(Int64); + dict[VarEnum.VT_UI1] = typeof(Byte); + dict[VarEnum.VT_UI2] = typeof(UInt16); + dict[VarEnum.VT_UI4] = typeof(UInt32); + dict[VarEnum.VT_UI8] = typeof(UInt64); + dict[VarEnum.VT_INT] = typeof(IntPtr); + dict[VarEnum.VT_UINT] = typeof(UIntPtr); + dict[VarEnum.VT_BOOL] = typeof(bool); + dict[VarEnum.VT_R4] = typeof(Single); + dict[VarEnum.VT_R8] = typeof(Double); + dict[VarEnum.VT_DECIMAL] = typeof(Decimal); + dict[VarEnum.VT_DATE] = typeof(DateTime); + dict[VarEnum.VT_BSTR] = typeof(String); + + // *** END GENERATED CODE *** + + #endregion + + dict[VarEnum.VT_CY] = typeof(CurrencyWrapper); + dict[VarEnum.VT_ERROR] = typeof(ErrorWrapper); + + return dict; + } + + #region Primitive COM types + + /// + /// Creates a family of COM types such that within each family, there is a completely non-lossy + /// conversion from a type to an earlier type in the family. + /// + private static IList> CreateComPrimitiveTypeFamilies() { + VarEnum[][] typeFamilies = new VarEnum[][] { + new VarEnum[] { VarEnum.VT_I8, VarEnum.VT_I4, VarEnum.VT_I2, VarEnum.VT_I1 }, + new VarEnum[] { VarEnum.VT_UI8, VarEnum.VT_UI4, VarEnum.VT_UI2, VarEnum.VT_UI1 }, + new VarEnum[] { VarEnum.VT_INT }, + new VarEnum[] { VarEnum.VT_UINT }, + new VarEnum[] { VarEnum.VT_BOOL }, + new VarEnum[] { VarEnum.VT_DATE }, + new VarEnum[] { VarEnum.VT_R8, VarEnum.VT_R4 }, + new VarEnum[] { VarEnum.VT_DECIMAL }, + new VarEnum[] { VarEnum.VT_BSTR }, + + // wrappers + new VarEnum[] { VarEnum.VT_CY }, + new VarEnum[] { VarEnum.VT_ERROR }, + }; + + return typeFamilies; + } + + /// + /// Get the (one representative type for each) primitive type families that the argument can be converted to + /// + private static List GetConversionsToComPrimitiveTypeFamilies(Type argumentType) { + List compatibleComTypes = new List(); + + foreach (IList typeFamily in _ComPrimitiveTypeFamilies) { + foreach (VarEnum candidateType in typeFamily) { + Type candidateManagedType = _ComToManagedPrimitiveTypes[candidateType]; + if (TypeUtils.IsImplicitlyConvertible(argumentType, candidateManagedType, true)) { + compatibleComTypes.Add(candidateType); + // Move on to the next type family. We need atmost one type from each family + break; + } + } + } + return compatibleComTypes; + } + + /// + /// If there is more than one type family that the argument can be converted to, we will throw a + /// AmbiguousMatchException instead of randomly picking a winner. + /// + private static void CheckForAmbiguousMatch(Type argumentType, List compatibleComTypes) { + if (compatibleComTypes.Count <= 1) { + return; + } + + String typeNames = ""; + for (int i = 0; i < compatibleComTypes.Count; i++) { + string typeName = _ComToManagedPrimitiveTypes[compatibleComTypes[i]].Name; + if (i == (compatibleComTypes.Count - 1)) { + typeNames += " and "; + } else if (i != 0) { + typeNames += ", "; + } + typeNames += typeName; + } + + + throw Error.AmbiguousConversion(argumentType.Name, typeNames); + } + + private static bool TryGetPrimitiveComType(Type argumentType, out VarEnum primitiveVarEnum) { + // Look for an exact match with a COM primitive type + + foreach (KeyValuePair kvp in _ComToManagedPrimitiveTypes) { + if (kvp.Value == argumentType) { + primitiveVarEnum = kvp.Key; + return true; + } + } + + primitiveVarEnum = VarEnum.VT_VOID; // error + return false; + } + + /// + /// Is there a unique primitive type that has the best conversion for the argument + /// + private static bool TryGetPrimitiveComTypeViaConversion(Type argumentType, out VarEnum primitiveVarEnum) { + // Look for a unique type family that the argument can be converted to. + List compatibleComTypes = GetConversionsToComPrimitiveTypeFamilies(argumentType); + CheckForAmbiguousMatch(argumentType, compatibleComTypes); + if (compatibleComTypes.Count == 1) { + primitiveVarEnum = compatibleComTypes[0]; + return true; + } + + primitiveVarEnum = VarEnum.VT_VOID; // error + return false; + } + + #endregion + + // Type.InvokeMember tries to marshal objects as VT_DISPATCH, and falls back to VT_UNKNOWN if IDispatch + // cannot be supported. For now, we will just support VT_DISPATCH. We might want to move to VT_UNKNOWN + // as doing a QueryInterface for IID_IDispatch could be expensive, and it should be upto the callee + // to do the QueryInterface if needed + const VarEnum VT_DISPATCH_OR_UNKNOWN = VarEnum.VT_DISPATCH; + + private VarEnum GetComType(ref Type argumentType) { + if (argumentType == typeof(Missing)) { + //TODO: consider specialcasing marshaling for Missing as VT_ERROR | E_PARAMNOTFOUND + return VarEnum.VT_RECORD; + } + + if (argumentType.IsArray) { + return VarEnum.VT_ARRAY; + } + + if (argumentType == typeof(UnknownWrapper)) { + return VarEnum.VT_UNKNOWN; + } else if (argumentType == typeof(DispatchWrapper)) { + return VarEnum.VT_DISPATCH; + } else if (argumentType == typeof(VariantWrapper)) { + return VarEnum.VT_VARIANT; + } else if (argumentType == typeof(BStrWrapper)) { + return VarEnum.VT_BSTR; + } else if (argumentType == typeof(ErrorWrapper)) { + return VarEnum.VT_ERROR; + } else if (argumentType == typeof(CurrencyWrapper)) { + return VarEnum.VT_CY; + } + + // Many languages require an explicit cast for an enum to be used as the underlying type. + // However, we want to allow this conversion for COM without requiring an explicit cast + // so that enums from interop assemblies can be used as arguments. + if (argumentType.IsEnum) { + argumentType = Enum.GetUnderlyingType(argumentType); + return GetComType(ref argumentType); + } + + // COM cannot express valuetype nulls so we will convert to underlying type + // it will throw if there is no value + if (TypeUtils.IsNullableType(argumentType)) { + argumentType = TypeUtils.GetNonNullableType(argumentType); + return GetComType(ref argumentType); + } + + if (argumentType.IsCOMObject) { + return VT_DISPATCH_OR_UNKNOWN; + } + + VarEnum primitiveVarEnum; + if (TryGetPrimitiveComType(argumentType, out primitiveVarEnum)) { + return primitiveVarEnum; + } + + if (argumentType.IsValueType) { + return VarEnum.VT_RECORD; + } + + // We could not find a way to marshal the type as a specific COM type + // So we just indicate that it is an unknown value type (VT_RECORD) + // or unknown reference type (VT_DISPATCH_OR_UNKNOWN) + // Note that callers may still find a less generic marshalling method if + // the type implements IConvertible and if it is applicable + + //default marshal type + return VT_DISPATCH_OR_UNKNOWN; + } + + /// + /// Get the COM Variant type that argument should be marshaled as for a call to COM + /// + private VariantBuilder GetVariantBuilder(Type argumentType) { + if (argumentType == Null.Type) { + return new VariantBuilder(VarEnum.VT_EMPTY, new NullArgBuilder()); + } + + if (argumentType == typeof(DBNull)) { + return new VariantBuilder(VarEnum.VT_NULL, new NullArgBuilder()); + } + + ArgBuilder argBuilder; + + if (argumentType.IsByRef) { + Type elementType = argumentType.GetElementType(); + + VarEnum elementVarEnum; + if (elementType == typeof(object) || elementType == typeof(DBNull)) { + //no meaningful value to pass ByRef. + //perhaps the calee will replace it with something. + //need to pass as a variant reference + elementVarEnum = VarEnum.VT_VARIANT; + } else { + elementVarEnum = GetComType(ref elementType); + } + + argBuilder = GetSimpleArgBuilder(elementType, elementVarEnum); + return new VariantBuilder(elementVarEnum | VarEnum.VT_BYREF, argBuilder); + } + + Debug.Assert(!(argumentType.IsGenericType && argumentType.GetGenericTypeDefinition() == typeof(StrongBox<>)), "should not have StrongBox here"); + + VarEnum varEnum = GetComType(ref argumentType); + argBuilder = GetByValArgBuilder(argumentType, ref varEnum); + + return new VariantBuilder(varEnum, argBuilder); + } + + + // This helper is called when we are looking for a ByVal marhsalling + // In a ByVal case we can take into account conversions or IConvertible if all other + // attempts to find marshalling type failed + private static ArgBuilder GetByValArgBuilder(Type elementType, ref VarEnum elementVarEnum) { + // if VT indicates that marshalling type is unknown + if (elementVarEnum == VarEnum.VT_RECORD || elementVarEnum == VT_DISPATCH_OR_UNKNOWN) { + //trying to find a conversion. + VarEnum convertibleTo; + if (TryGetPrimitiveComTypeViaConversion(elementType, out convertibleTo)) { + elementVarEnum = convertibleTo; + Type marshalType = GetManagedMarshalType(elementVarEnum); + return new ConversionArgBuilder(elementType, GetSimpleArgBuilder(marshalType, elementVarEnum)); + } + + //checking for IConvertible. + if (typeof(IConvertible).IsAssignableFrom(elementType)) { + return new ConvertibleArgBuilder(); + } + } + return GetSimpleArgBuilder(elementType, elementVarEnum); + } + + // This helper can produce a builder for types that are directly supported by Variant. + private static SimpleArgBuilder GetSimpleArgBuilder(Type elementType, VarEnum elementVarEnum) { + SimpleArgBuilder argBuilder; + + switch (elementVarEnum) { + case VarEnum.VT_BSTR: + argBuilder = new StringArgBuilder(elementType); + break; + case VarEnum.VT_BOOL: + argBuilder = new BoolArgBuilder(elementType); + break; + case VarEnum.VT_DATE: + argBuilder = new DateTimeArgBuilder(elementType); + break; + case VarEnum.VT_CY: + argBuilder = new CurrencyArgBuilder(elementType); + break; + case VarEnum.VT_DISPATCH: + argBuilder = new DispatchArgBuilder(elementType); + break; + case VarEnum.VT_UNKNOWN: + argBuilder = new UnknownArgBuilder(elementType); + break; + case VarEnum.VT_VARIANT: + case VarEnum.VT_ARRAY: + case VarEnum.VT_RECORD: + argBuilder = new VariantArgBuilder(elementType); + break; + case VarEnum.VT_ERROR: + argBuilder = new ErrorArgBuilder(elementType); + break; + default: + argBuilder = new SimpleArgBuilder(elementType); + break; + } + + return argBuilder; + } + } +} + +#endif \ No newline at end of file diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Variant.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Variant.cs new file mode 100644 index 0000000000..35ae31a16a --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/Variant.cs @@ -0,0 +1,846 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.CodeDom.Compiler; +using System.Diagnostics; +using System.Globalization; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Dynamic.Utils; +using System.Runtime.CompilerServices; + +namespace System.Dynamic.ComInterop { + + /// + /// Variant is the basic COM type for late-binding. It can contain any other COM data type. + /// This type definition precisely matches the unmanaged data layout so that the struct can be passed + /// to and from COM calls. + /// + [StructLayout(LayoutKind.Explicit)] + internal struct Variant { + +#if DEBUG + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2207:InitializeValueTypeStaticFieldsInline")] + static Variant() { + // Variant size is the size of 4 pointers (16 bytes) on a 32-bit processor, + // and 3 pointers (24 bytes) on a 64-bit processor. + int intPtrSize = Marshal.SizeOf(typeof(IntPtr)); + int variantSize = Marshal.SizeOf(typeof(Variant)); + if (intPtrSize == 4) { + Debug.Assert(variantSize == (4 * intPtrSize)); + } else { + Debug.Assert(intPtrSize == 8); + Debug.Assert(variantSize == (3 * intPtrSize)); + } + } +#endif + + // Most of the data types in the Variant are carried in _typeUnion + [FieldOffset(0)] + private TypeUnion _typeUnion; + + // Decimal is the largest data type and it needs to use the space that is normally unused in TypeUnion._wReserved1, etc. + // Hence, it is declared to completely overlap with TypeUnion. A Decimal does not use the first two bytes, and so + // TypeUnion._vt can still be used to encode the type. + [FieldOffset(0)] + private Decimal _decimal; + + [StructLayout(LayoutKind.Sequential)] + private struct TypeUnion { + internal ushort _vt; + internal ushort _wReserved1; + internal ushort _wReserved2; + internal ushort _wReserved3; + + internal UnionTypes _unionTypes; + } + + [StructLayout(LayoutKind.Sequential)] + private struct Record { + private IntPtr _record; + private IntPtr _recordInfo; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] + [StructLayout(LayoutKind.Explicit)] + private struct UnionTypes { + #region Generated Variant union types + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_UnionTypes from: generate_comdispatch.py + + [FieldOffset(0)] internal SByte _i1; + [FieldOffset(0)] internal Int16 _i2; + [FieldOffset(0)] internal Int32 _i4; + [FieldOffset(0)] internal Int64 _i8; + [FieldOffset(0)] internal Byte _ui1; + [FieldOffset(0)] internal UInt16 _ui2; + [FieldOffset(0)] internal UInt32 _ui4; + [FieldOffset(0)] internal UInt64 _ui8; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + [FieldOffset(0)] internal IntPtr _int; + [FieldOffset(0)] internal UIntPtr _uint; + [FieldOffset(0)] internal Int16 _bool; + [FieldOffset(0)] internal Int32 _error; + [FieldOffset(0)] internal Single _r4; + [FieldOffset(0)] internal Double _r8; + [FieldOffset(0)] internal Int64 _cy; + [FieldOffset(0)] internal Double _date; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + [FieldOffset(0)] internal IntPtr _bstr; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + [FieldOffset(0)] internal IntPtr _unknown; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + [FieldOffset(0)] internal IntPtr _dispatch; + + // *** END GENERATED CODE *** + + #endregion + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + [FieldOffset(0)] + internal IntPtr _byref; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + [FieldOffset(0)] + internal Record _record; + } + + public override string ToString() { + return String.Format(CultureInfo.CurrentCulture, "{0} ({1}", ToObject().ToString(), VariantType); + } + + /// + /// Primitive types are the basic COM types. It includes valuetypes like ints, but also reference tyeps + /// like BStrs. It does not include composite types like arrays and user-defined COM types (IUnknown/IDispatch). + /// + internal static bool IsPrimitiveType(VarEnum varEnum) { + switch (varEnum) { + #region Generated Variant IsPrimitiveType + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_IsPrimitiveType from: generate_comdispatch.py + + case VarEnum.VT_I1: + case VarEnum.VT_I2: + case VarEnum.VT_I4: + case VarEnum.VT_I8: + case VarEnum.VT_UI1: + case VarEnum.VT_UI2: + case VarEnum.VT_UI4: + case VarEnum.VT_UI8: + case VarEnum.VT_INT: + case VarEnum.VT_UINT: + case VarEnum.VT_BOOL: + case VarEnum.VT_ERROR: + case VarEnum.VT_R4: + case VarEnum.VT_R8: + case VarEnum.VT_DECIMAL: + case VarEnum.VT_CY: + case VarEnum.VT_DATE: + case VarEnum.VT_BSTR: + + // *** END GENERATED CODE *** + + #endregion + return true; + } + + return false; + } + + /// + /// Get the managed object representing the Variant. + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public object ToObject() { + // Check the simple case upfront + if (IsEmpty) { + return null; + } + + switch (VariantType) { + case VarEnum.VT_NULL: return DBNull.Value; + + #region Generated Variant ToObject + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_ToObject from: generate_comdispatch.py + + case VarEnum.VT_I1: return AsI1; + case VarEnum.VT_I2: return AsI2; + case VarEnum.VT_I4: return RuntimeOps.Int32ToObject(AsI4); + case VarEnum.VT_I8: return AsI8; + case VarEnum.VT_UI1: return AsUi1; + case VarEnum.VT_UI2: return AsUi2; + case VarEnum.VT_UI4: return AsUi4; + case VarEnum.VT_UI8: return AsUi8; + case VarEnum.VT_INT: return AsInt; + case VarEnum.VT_UINT: return AsUint; + case VarEnum.VT_BOOL: return AsBool ? RuntimeOps.True : RuntimeOps.False; + case VarEnum.VT_ERROR: return AsError; + case VarEnum.VT_R4: return AsR4; + case VarEnum.VT_R8: return AsR8; + case VarEnum.VT_DECIMAL: return AsDecimal; + case VarEnum.VT_CY: return AsCy; + case VarEnum.VT_DATE: return AsDate; + case VarEnum.VT_BSTR: return AsBstr; + case VarEnum.VT_UNKNOWN: return AsUnknown; + case VarEnum.VT_DISPATCH: return AsDispatch; + case VarEnum.VT_VARIANT: return AsVariant; + + // *** END GENERATED CODE *** + + #endregion + + default: + return AsVariant; + } + } + + /// + /// Release any unmanaged memory associated with the Variant + /// + /// + public void Clear() { + // We do not need to call OLE32's VariantClear for primitive types or ByRefs + // to safe ourselves the cost of interop transition. + // ByRef indicates the memory is not owned by the VARIANT itself while + // primitive types do not have any resources to free up. + // Hence, only safearrays, BSTRs, interfaces and user types are + // handled differently. + VarEnum vt = VariantType; + if ((vt & VarEnum.VT_BYREF) != 0) { + VariantType = VarEnum.VT_EMPTY; + } else if ( + ((vt & VarEnum.VT_ARRAY) != 0) || + ((vt) == VarEnum.VT_BSTR) || + ((vt) == VarEnum.VT_UNKNOWN) || + ((vt) == VarEnum.VT_DISPATCH) || + ((vt) == VarEnum.VT_RECORD) + ) { + IntPtr variantPtr = UnsafeMethods.ConvertVariantByrefToPtr(ref this); + UnsafeNativeMethods.VariantClear(variantPtr); + Debug.Assert(IsEmpty); + } else { + VariantType = VarEnum.VT_EMPTY; + } + } + + public VarEnum VariantType { + get { + return (VarEnum)_typeUnion._vt; + } + set { + _typeUnion._vt = (ushort)value; + } + } + + internal bool IsEmpty { + get { + return _typeUnion._vt == ((ushort)VarEnum.VT_EMPTY); + } + } + + public void SetAsNull() { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_NULL; + } + + public void SetAsIConvertible(IConvertible value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + + TypeCode tc = value.GetTypeCode(); + CultureInfo ci = CultureInfo.CurrentCulture; + + switch (tc) { + case TypeCode.Empty: SetAsNull(); break; + case TypeCode.Object: AsUnknown = value; break; + case TypeCode.DBNull: SetAsNull(); break; + case TypeCode.Boolean: AsBool = value.ToBoolean(ci); break; + case TypeCode.Char: AsUi2 = value.ToChar(ci); break; + case TypeCode.SByte: AsI1 = value.ToSByte(ci); break; + case TypeCode.Byte: AsUi1 = value.ToByte(ci); break; + case TypeCode.Int16: AsI2 = value.ToInt16(ci); break; + case TypeCode.UInt16: AsUi2 = value.ToUInt16(ci); break; + case TypeCode.Int32: AsI4 = value.ToInt32(ci); break; + case TypeCode.UInt32: AsUi4 = value.ToUInt32(ci); break; + case TypeCode.Int64: AsI8 = value.ToInt64(ci); break; + case TypeCode.UInt64: AsI8 = value.ToInt64(ci); break; + case TypeCode.Single: AsR4 = value.ToSingle(ci); break; + case TypeCode.Double: AsR8 = value.ToDouble(ci); break; + case TypeCode.Decimal: AsDecimal = value.ToDecimal(ci); break; + case TypeCode.DateTime: AsDate = value.ToDateTime(ci); break; + case TypeCode.String: AsBstr = value.ToString(ci); break; + + default: + throw Assert.Unreachable; + } + } + + #region Generated Variant accessors + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_accessors from: generate_comdispatch.py + + // VT_I1 + + public SByte AsI1 { + get { + Debug.Assert(VariantType == VarEnum.VT_I1); + return _typeUnion._unionTypes._i1; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_I1; + _typeUnion._unionTypes._i1 = value; + } + } + + public void SetAsByrefI1(ref SByte value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_I1 | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertSByteByrefToPtr(ref value); + } + + // VT_I2 + + public Int16 AsI2 { + get { + Debug.Assert(VariantType == VarEnum.VT_I2); + return _typeUnion._unionTypes._i2; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_I2; + _typeUnion._unionTypes._i2 = value; + } + } + + public void SetAsByrefI2(ref Int16 value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_I2 | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertInt16ByrefToPtr(ref value); + } + + // VT_I4 + + public Int32 AsI4 { + get { + Debug.Assert(VariantType == VarEnum.VT_I4); + return _typeUnion._unionTypes._i4; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_I4; + _typeUnion._unionTypes._i4 = value; + } + } + + public void SetAsByrefI4(ref Int32 value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_I4 | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertInt32ByrefToPtr(ref value); + } + + // VT_I8 + + public Int64 AsI8 { + get { + Debug.Assert(VariantType == VarEnum.VT_I8); + return _typeUnion._unionTypes._i8; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_I8; + _typeUnion._unionTypes._i8 = value; + } + } + + public void SetAsByrefI8(ref Int64 value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_I8 | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertInt64ByrefToPtr(ref value); + } + + // VT_UI1 + + public Byte AsUi1 { + get { + Debug.Assert(VariantType == VarEnum.VT_UI1); + return _typeUnion._unionTypes._ui1; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UI1; + _typeUnion._unionTypes._ui1 = value; + } + } + + public void SetAsByrefUi1(ref Byte value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_UI1 | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertByteByrefToPtr(ref value); + } + + // VT_UI2 + + public UInt16 AsUi2 { + get { + Debug.Assert(VariantType == VarEnum.VT_UI2); + return _typeUnion._unionTypes._ui2; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UI2; + _typeUnion._unionTypes._ui2 = value; + } + } + + public void SetAsByrefUi2(ref UInt16 value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_UI2 | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertUInt16ByrefToPtr(ref value); + } + + // VT_UI4 + + public UInt32 AsUi4 { + get { + Debug.Assert(VariantType == VarEnum.VT_UI4); + return _typeUnion._unionTypes._ui4; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UI4; + _typeUnion._unionTypes._ui4 = value; + } + } + + public void SetAsByrefUi4(ref UInt32 value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_UI4 | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertUInt32ByrefToPtr(ref value); + } + + // VT_UI8 + + public UInt64 AsUi8 { + get { + Debug.Assert(VariantType == VarEnum.VT_UI8); + return _typeUnion._unionTypes._ui8; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UI8; + _typeUnion._unionTypes._ui8 = value; + } + } + + public void SetAsByrefUi8(ref UInt64 value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_UI8 | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertUInt64ByrefToPtr(ref value); + } + + // VT_INT + + public IntPtr AsInt { + get { + Debug.Assert(VariantType == VarEnum.VT_INT); + return _typeUnion._unionTypes._int; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_INT; + _typeUnion._unionTypes._int = value; + } + } + + public void SetAsByrefInt(ref IntPtr value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_INT | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertIntPtrByrefToPtr(ref value); + } + + // VT_UINT + + public UIntPtr AsUint { + get { + Debug.Assert(VariantType == VarEnum.VT_UINT); + return _typeUnion._unionTypes._uint; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UINT; + _typeUnion._unionTypes._uint = value; + } + } + + public void SetAsByrefUint(ref UIntPtr value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_UINT | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertUIntPtrByrefToPtr(ref value); + } + + // VT_BOOL + + public bool AsBool { + get { + Debug.Assert(VariantType == VarEnum.VT_BOOL); + return _typeUnion._unionTypes._bool != 0; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_BOOL; + _typeUnion._unionTypes._bool = value ? (Int16)(-1) : (Int16)0; + } + } + + public void SetAsByrefBool(ref Int16 value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_BOOL | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertInt16ByrefToPtr(ref value); + } + + // VT_ERROR + + public Int32 AsError { + get { + Debug.Assert(VariantType == VarEnum.VT_ERROR); + return _typeUnion._unionTypes._error; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_ERROR; + _typeUnion._unionTypes._error = value; + } + } + + public void SetAsByrefError(ref Int32 value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_ERROR | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertInt32ByrefToPtr(ref value); + } + + // VT_R4 + + public Single AsR4 { + get { + Debug.Assert(VariantType == VarEnum.VT_R4); + return _typeUnion._unionTypes._r4; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_R4; + _typeUnion._unionTypes._r4 = value; + } + } + + public void SetAsByrefR4(ref Single value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_R4 | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertSingleByrefToPtr(ref value); + } + + // VT_R8 + + public Double AsR8 { + get { + Debug.Assert(VariantType == VarEnum.VT_R8); + return _typeUnion._unionTypes._r8; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_R8; + _typeUnion._unionTypes._r8 = value; + } + } + + public void SetAsByrefR8(ref Double value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_R8 | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertDoubleByrefToPtr(ref value); + } + + // VT_DECIMAL + + public Decimal AsDecimal { + get { + Debug.Assert(VariantType == VarEnum.VT_DECIMAL); + // The first byte of Decimal is unused, but usually set to 0 + Variant v = this; + v._typeUnion._vt = 0; + return v._decimal; + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_DECIMAL; + _decimal = value; + // _vt overlaps with _decimal, and should be set after setting _decimal + _typeUnion._vt = (ushort)VarEnum.VT_DECIMAL; + } + } + + public void SetAsByrefDecimal(ref Decimal value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_DECIMAL | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertDecimalByrefToPtr(ref value); + } + + // VT_CY + + public Decimal AsCy { + get { + Debug.Assert(VariantType == VarEnum.VT_CY); + return Decimal.FromOACurrency(_typeUnion._unionTypes._cy); + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_CY; + _typeUnion._unionTypes._cy = Decimal.ToOACurrency(value); + } + } + + public void SetAsByrefCy(ref Int64 value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_CY | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertInt64ByrefToPtr(ref value); + } + + // VT_DATE + + public DateTime AsDate { + get { + Debug.Assert(VariantType == VarEnum.VT_DATE); + return DateTime.FromOADate(_typeUnion._unionTypes._date); + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_DATE; + _typeUnion._unionTypes._date = value.ToOADate(); + } + } + + public void SetAsByrefDate(ref Double value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_DATE | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertDoubleByrefToPtr(ref value); + } + + // VT_BSTR + + public String AsBstr { + get { + Debug.Assert(VariantType == VarEnum.VT_BSTR); + return (string)Marshal.GetObjectForNativeVariant(UnsafeMethods.ConvertVariantByrefToPtr(ref this)); + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_BSTR; + Marshal.GetNativeVariantForObject(value, UnsafeMethods.ConvertVariantByrefToPtr(ref this)); + } + } + + public void SetAsByrefBstr(ref IntPtr value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_BSTR | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertIntPtrByrefToPtr(ref value); + } + + // VT_UNKNOWN + + public Object AsUnknown { + get { + Debug.Assert(VariantType == VarEnum.VT_UNKNOWN); + return Marshal.GetObjectForIUnknown(_typeUnion._unionTypes._unknown); + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UNKNOWN; + _typeUnion._unionTypes._unknown = Marshal.GetIUnknownForObject(value); + } + } + + public void SetAsByrefUnknown(ref IntPtr value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_UNKNOWN | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertIntPtrByrefToPtr(ref value); + } + + // VT_DISPATCH + + public Object AsDispatch { + get { + Debug.Assert(VariantType == VarEnum.VT_DISPATCH); + return Marshal.GetObjectForIUnknown(_typeUnion._unionTypes._dispatch); + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_DISPATCH; + _typeUnion._unionTypes._dispatch = Marshal.GetIDispatchForObject(value); + } + } + + public void SetAsByrefDispatch(ref IntPtr value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_DISPATCH | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertIntPtrByrefToPtr(ref value); + } + + + // *** END GENERATED CODE *** + + #endregion + + // VT_VARIANT + + public Object AsVariant { + get { + return Marshal.GetObjectForNativeVariant(UnsafeMethods.ConvertVariantByrefToPtr(ref this)); + } + set { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + Marshal.GetNativeVariantForObject(value, UnsafeMethods.ConvertVariantByrefToPtr(ref this)); + } + } + + public void SetAsByrefVariant(ref Variant value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = (VarEnum.VT_VARIANT | VarEnum.VT_BYREF); + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertVariantByrefToPtr(ref value); + } + + // constructs a ByRef variant to pass contents of another variant ByRef. + public void SetAsByrefVariantIndirect(ref Variant value) { + Debug.Assert(IsEmpty); // The setter can only be called once as VariantClear might be needed otherwise + Debug.Assert((value.VariantType & VarEnum.VT_BYREF) == 0, "double indirection"); + + switch (value.VariantType){ + case VarEnum.VT_EMPTY: + case VarEnum.VT_NULL: + // these cannot combine with VT_BYREF. Should try passing as a variant reference + SetAsByrefVariant(ref value); + return; + case VarEnum.VT_RECORD: + // VT_RECORD's are weird in that regardless of is the VT_BYREF flag is set or not + // they have the same internal representation. + _typeUnion._unionTypes._record = value._typeUnion._unionTypes._record; + break; + case VarEnum.VT_DECIMAL: + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertDecimalByrefToPtr(ref value._decimal); + break; + default: + _typeUnion._unionTypes._byref = UnsafeMethods.ConvertIntPtrByrefToPtr(ref value._typeUnion._unionTypes._byref); + break; + } + VariantType = (value.VariantType | VarEnum.VT_BYREF); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + internal static System.Reflection.PropertyInfo GetAccessor(VarEnum varType) { + switch (varType) { + + #region Generated Variant accessors PropertyInfos + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_accessor_propertyinfo from: generate_comdispatch.py + + case VarEnum.VT_I1: return typeof(Variant).GetProperty("AsI1"); + case VarEnum.VT_I2: return typeof(Variant).GetProperty("AsI2"); + case VarEnum.VT_I4: return typeof(Variant).GetProperty("AsI4"); + case VarEnum.VT_I8: return typeof(Variant).GetProperty("AsI8"); + case VarEnum.VT_UI1: return typeof(Variant).GetProperty("AsUi1"); + case VarEnum.VT_UI2: return typeof(Variant).GetProperty("AsUi2"); + case VarEnum.VT_UI4: return typeof(Variant).GetProperty("AsUi4"); + case VarEnum.VT_UI8: return typeof(Variant).GetProperty("AsUi8"); + case VarEnum.VT_INT: return typeof(Variant).GetProperty("AsInt"); + case VarEnum.VT_UINT: return typeof(Variant).GetProperty("AsUint"); + case VarEnum.VT_BOOL: return typeof(Variant).GetProperty("AsBool"); + case VarEnum.VT_ERROR: return typeof(Variant).GetProperty("AsError"); + case VarEnum.VT_R4: return typeof(Variant).GetProperty("AsR4"); + case VarEnum.VT_R8: return typeof(Variant).GetProperty("AsR8"); + case VarEnum.VT_DECIMAL: return typeof(Variant).GetProperty("AsDecimal"); + case VarEnum.VT_CY: return typeof(Variant).GetProperty("AsCy"); + case VarEnum.VT_DATE: return typeof(Variant).GetProperty("AsDate"); + case VarEnum.VT_BSTR: return typeof(Variant).GetProperty("AsBstr"); + case VarEnum.VT_UNKNOWN: return typeof(Variant).GetProperty("AsUnknown"); + case VarEnum.VT_DISPATCH: return typeof(Variant).GetProperty("AsDispatch"); + + // *** END GENERATED CODE *** + + #endregion + + case VarEnum.VT_VARIANT: + case VarEnum.VT_RECORD: + case VarEnum.VT_ARRAY: + return typeof(Variant).GetProperty("AsVariant"); + + default: + throw Error.VariantGetAccessorNYI(varType); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + internal static System.Reflection.MethodInfo GetByrefSetter(VarEnum varType) { + switch (varType) { + + #region Generated Variant byref setter + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_byref_setters from: generate_comdispatch.py + + case VarEnum.VT_I1: return typeof(Variant).GetMethod("SetAsByrefI1"); + case VarEnum.VT_I2: return typeof(Variant).GetMethod("SetAsByrefI2"); + case VarEnum.VT_I4: return typeof(Variant).GetMethod("SetAsByrefI4"); + case VarEnum.VT_I8: return typeof(Variant).GetMethod("SetAsByrefI8"); + case VarEnum.VT_UI1: return typeof(Variant).GetMethod("SetAsByrefUi1"); + case VarEnum.VT_UI2: return typeof(Variant).GetMethod("SetAsByrefUi2"); + case VarEnum.VT_UI4: return typeof(Variant).GetMethod("SetAsByrefUi4"); + case VarEnum.VT_UI8: return typeof(Variant).GetMethod("SetAsByrefUi8"); + case VarEnum.VT_INT: return typeof(Variant).GetMethod("SetAsByrefInt"); + case VarEnum.VT_UINT: return typeof(Variant).GetMethod("SetAsByrefUint"); + case VarEnum.VT_BOOL: return typeof(Variant).GetMethod("SetAsByrefBool"); + case VarEnum.VT_ERROR: return typeof(Variant).GetMethod("SetAsByrefError"); + case VarEnum.VT_R4: return typeof(Variant).GetMethod("SetAsByrefR4"); + case VarEnum.VT_R8: return typeof(Variant).GetMethod("SetAsByrefR8"); + case VarEnum.VT_DECIMAL: return typeof(Variant).GetMethod("SetAsByrefDecimal"); + case VarEnum.VT_CY: return typeof(Variant).GetMethod("SetAsByrefCy"); + case VarEnum.VT_DATE: return typeof(Variant).GetMethod("SetAsByrefDate"); + case VarEnum.VT_BSTR: return typeof(Variant).GetMethod("SetAsByrefBstr"); + case VarEnum.VT_UNKNOWN: return typeof(Variant).GetMethod("SetAsByrefUnknown"); + case VarEnum.VT_DISPATCH: return typeof(Variant).GetMethod("SetAsByrefDispatch"); + + // *** END GENERATED CODE *** + + #endregion + + case VarEnum.VT_VARIANT: + return typeof(Variant).GetMethod("SetAsByrefVariant"); + case VarEnum.VT_RECORD: + case VarEnum.VT_ARRAY: + return typeof(Variant).GetMethod("SetAsByrefVariantIndirect"); + + default: + throw Error.VariantGetAccessorNYI(varType); + } + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/VariantArgBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/VariantArgBuilder.cs new file mode 100644 index 0000000000..5bfe036e36 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/VariantArgBuilder.cs @@ -0,0 +1,83 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + internal class VariantArgBuilder : SimpleArgBuilder { + private bool _isWrapper; + + internal VariantArgBuilder(Type parameterType) + : base(parameterType) { + + _isWrapper = parameterType == typeof(VariantWrapper); + } + + internal override Expression Marshal(Expression parameter) { + parameter = base.Marshal(parameter); + + // parameter.WrappedObject + if (_isWrapper) { + parameter = Expression.Property( + Helpers.Convert(parameter, typeof(VariantWrapper)), + typeof(VariantWrapper).GetProperty("WrappedObject") + ); + }; + + return Helpers.Convert(parameter, typeof(object)); + } + + internal override Expression MarshalToRef(Expression parameter) { + parameter = Marshal(parameter); + + // parameter == null ? IntPtr.Zero : UnsafeMethods.GetVariantForObject(parameter); + return Expression.Condition( + Expression.Equal(parameter, Expression.Constant(null)), + Expression.Constant(new Variant()), + Expression.Call( + typeof(UnsafeMethods).GetMethod("GetVariantForObject"), + parameter + ) + ); + } + + + internal override Expression UnmarshalFromRef(Expression value) { + // value == IntPtr.Zero ? null : Marshal.GetObjectForNativeVariant(value); + + Expression unmarshal = Expression.Call( + typeof(UnsafeMethods).GetMethod("GetObjectForVariant"), + value + ); + + if (_isWrapper) { + unmarshal = Expression.New( + typeof(VariantWrapper).GetConstructor(new Type[] { typeof(object) }), + unmarshal + ); + }; + + return base.UnmarshalFromRef(unmarshal); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/VariantArray.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/VariantArray.cs new file mode 100644 index 0000000000..23ef4a9425 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/VariantArray.cs @@ -0,0 +1,129 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq.Expressions; +using System.Linq.Expressions.Compiler; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using System.Security; + +namespace System.Dynamic.ComInterop { + + [StructLayout(LayoutKind.Sequential)] + internal struct VariantArray1 { + public Variant Element0; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct VariantArray2 { + public Variant Element0, Element1; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct VariantArray4 { + public Variant Element0, Element1, Element2, Element3; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct VariantArray8 { + public Variant Element0, Element1, Element2, Element3, Element4, Element5, Element6, Element7; + } + + // + // Helper for getting the right VariantArray struct for a given number of + // arguments. Will generate a struct if needed. + // + // We use this because we don't have stackalloc or pinning in Expression + // Trees, so we can't create an array of Variants directly. + // + internal static class VariantArray { + // Don't need a dictionary for this, it will have very few elements + // (guarenteed less than 28, in practice 0-2) + private static readonly List _generatedTypes = new List(0); + + internal static MemberExpression GetStructField(ParameterExpression variantArray, int field) { + return Expression.Field(variantArray, "Element" + field); + } + + internal static Type GetStructType(int args) { + Debug.Assert(args >= 0); + if (args <= 1) return typeof(VariantArray1); + if (args <= 2) return typeof(VariantArray2); + if (args <= 4) return typeof(VariantArray4); + if (args <= 8) return typeof(VariantArray8); + + int size = 1; + while (args > size) { + size *= 2; + } + + lock (_generatedTypes) { + // See if we can find an existing type + foreach (Type t in _generatedTypes) { + int arity = int.Parse(t.Name.Substring("VariantArray".Length), CultureInfo.InvariantCulture); + if (size == arity) { + return t; + } + } + + // Else generate a new type + Type type = CreateCustomType(size); + _generatedTypes.Add(type); + return type; + } + } + + private static Type CreateCustomType(int size) { + var attrs = TypeAttributes.NotPublic | TypeAttributes.SequentialLayout; + TypeBuilder type = DynamicModule.DefineType("VariantArray" + size, attrs, typeof(ValueType)); + for (int i = 0; i < size; i++) { + type.DefineField("Element" + i, typeof(Variant), FieldAttributes.Public); + } + return type.CreateType(); + } + + private static readonly object _lock = new object(); + private static ModuleBuilder _dynamicModule; + private static ModuleBuilder DynamicModule { + get { + if (_dynamicModule != null) { + return _dynamicModule; + } + lock (_lock) { + if (_dynamicModule == null) { + // mark the assembly transparent so that it works in partial trust: + var attributes = new[] { + new CustomAttributeBuilder(typeof(SecurityTransparentAttribute).GetConstructor(Type.EmptyTypes), new object[0]) + }; + + string name = typeof(VariantArray).Namespace + ".DynamicAssembly"; + var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(name), AssemblyBuilderAccess.Run, attributes); + assembly.DefineVersionInfoResource(); + _dynamicModule = assembly.DefineDynamicModule(name); + } + return _dynamicModule; + } + } + } + } +} + +#endif \ No newline at end of file diff --git a/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/VariantBuilder.cs b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/VariantBuilder.cs new file mode 100644 index 0000000000..09cfc02788 --- /dev/null +++ b/ndp/fx/src/DynamicCom/System/Dynamic/ComInterop/VariantBuilder.cs @@ -0,0 +1,172 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !SILVERLIGHT // ComObject + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Dynamic.Utils; + +namespace System.Dynamic.ComInterop { + + /// + /// VariantBuilder handles packaging of arguments into a Variant for a call to IDispatch.Invoke + /// + internal class VariantBuilder { + + private MemberExpression _variant; + private readonly ArgBuilder _argBuilder; + private readonly VarEnum _targetComType; + internal ParameterExpression TempVariable { get; private set; } + + internal VariantBuilder(VarEnum targetComType, ArgBuilder builder) { + _targetComType = targetComType; + _argBuilder = builder; + } + + internal bool IsByRef { + get { return (_targetComType & VarEnum.VT_BYREF) != 0; } + } + + internal Expression InitializeArgumentVariant(MemberExpression variant, Expression parameter) { + //NOTE: we must remember our variant + //the reason is that argument order does not map exactly to the order of variants for invoke + //and when we are doing clean-up we must be sure we are cleaning the variant we have initialized. + + _variant = variant; + + if (IsByRef) { + // temp = argument + // paramVariants._elementN.SetAsByrefT(ref temp) + Debug.Assert(TempVariable == null); + var argExpr = _argBuilder.MarshalToRef(parameter); + + TempVariable = Expression.Variable(argExpr.Type, null); + return Expression.Block( + Expression.Assign(TempVariable, argExpr), + Expression.Call( + variant, + Variant.GetByrefSetter(_targetComType & ~VarEnum.VT_BYREF), + TempVariable + ) + ); + } + + Expression argument = _argBuilder.Marshal(parameter); + + //TODO: we need to make this cleaner. + // it is not nice that we need a special case for IConvertible. + // we are forced to special case it now since it does not have + // a corresponding _targetComType. + if (_argBuilder is ConvertibleArgBuilder) { + return Expression.Call( + variant, + typeof(Variant).GetMethod("SetAsIConvertible"), + argument + ); + } + + if (Variant.IsPrimitiveType(_targetComType) || + (_targetComType == VarEnum.VT_DISPATCH) || + (_targetComType == VarEnum.VT_UNKNOWN) || + (_targetComType == VarEnum.VT_VARIANT) || + (_targetComType == VarEnum.VT_RECORD) || + (_targetComType == VarEnum.VT_ARRAY)){ + // paramVariants._elementN.AsT = (cast)argN + return Expression.Assign( + Expression.Property( + variant, + Variant.GetAccessor(_targetComType) + ), + argument + ); + } + + switch (_targetComType) { + case VarEnum.VT_EMPTY: + return null; + + case VarEnum.VT_NULL: + // paramVariants._elementN.SetAsNull(); + return Expression.Call(variant, typeof(Variant).GetMethod("SetAsNull")); + + default: + Debug.Assert(false, "Unexpected VarEnum"); + return null; + } + } + + private static Expression Release(Expression pUnk) { + return Expression.Call(typeof(UnsafeMethods).GetMethod("IUnknownReleaseNotZero"), pUnk); + } + + internal Expression Clear() { + if (IsByRef) { + if (_argBuilder is StringArgBuilder) { + Debug.Assert(TempVariable != null); + return Expression.Call(typeof(Marshal).GetMethod("FreeBSTR"), TempVariable); + } else if (_argBuilder is DispatchArgBuilder) { + Debug.Assert(TempVariable != null); + return Release(TempVariable); + } else if (_argBuilder is UnknownArgBuilder) { + Debug.Assert(TempVariable != null); + return Release(TempVariable); + } else if (_argBuilder is VariantArgBuilder) { + Debug.Assert(TempVariable != null); + return Expression.Call(TempVariable, typeof(Variant).GetMethod("Clear")); + } + return null; + } + + + switch (_targetComType) { + case VarEnum.VT_EMPTY: + case VarEnum.VT_NULL: + return null; + + case VarEnum.VT_BSTR: + case VarEnum.VT_UNKNOWN: + case VarEnum.VT_DISPATCH: + case VarEnum.VT_ARRAY: + case VarEnum.VT_RECORD: + case VarEnum.VT_VARIANT: + // paramVariants._elementN.Clear() + return Expression.Call(_variant, typeof(Variant).GetMethod("Clear")); + + default: + Debug.Assert(Variant.IsPrimitiveType(_targetComType), "Unexpected VarEnum"); + return null; + } + } + + internal Expression UpdateFromReturn(Expression parameter) { + if (TempVariable == null) { + return null; + } + return Expression.Assign( + parameter, + Helpers.Convert( + _argBuilder.UnmarshalFromRef(TempVariable), + parameter.Type + ) + ); + } + } +} + +#endif diff --git a/ndp/fx/src/DynamicCom/makefile b/ndp/fx/src/DynamicCom/makefile new file mode 100644 index 0000000000..84abb1cb0d --- /dev/null +++ b/ndp/fx/src/DynamicCom/makefile @@ -0,0 +1,7 @@ + +# +# DO NOT EDIT THIS FILE!!! Modify the project file in this directory +# This file merely allows the MSBuild project file in this directory to be integrated with Build.Exe +# +!INCLUDE $(NTMAKEENV)\msbuild.def + \ No newline at end of file diff --git a/ndp/fx/src/DynamicCom/misc/AssemblyAttributes.cs b/ndp/fx/src/DynamicCom/misc/AssemblyAttributes.cs new file mode 100644 index 0000000000..7aa6eae227 --- /dev/null +++ b/ndp/fx/src/DynamicCom/misc/AssemblyAttributes.cs @@ -0,0 +1,61 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +[assembly: System.Security.SecurityCritical] + +#if MICROSOFT_SCRIPTING_CORE + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.Dynamic.ComInterop")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Microsoft.Dynamic.ComInterop")] +[assembly: AssemblyCopyright(" Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +[assembly: CLSCompliant(true)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: System.Resources.NeutralResourcesLanguage("en-US")] + +[assembly: AssemblyVersion("1.0.0.5000")] +[assembly: AssemblyFileVersion("1.0.0.00")] +[assembly: AssemblyInformationalVersion("1.0")] +[assembly: AllowPartiallyTrustedCallers] + +#endif diff --git a/ndp/fx/src/DynamicCom/sources b/ndp/fx/src/DynamicCom/sources new file mode 100644 index 0000000000..a9832d3d0c --- /dev/null +++ b/ndp/fx/src/DynamicCom/sources @@ -0,0 +1,12 @@ + +# +# DO NOT EDIT THIS FILE!!! Modify the project file in this directory +# This file merely allows the MSBuild project file in this directory to be integrated with Build.Exe +# +TARGETTYPE=NOTARGET +CLR_TARGETTYPE=DLL +MSBuildProjectFile=System.Dynamic.ComInterop.csproj +SOURCES= + +# Ensure that System.AddIn.Contract.dll is completely built first. +SYNCHRONIZE_DRAIN=1 diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/AutoRuleTemplate.cs b/ndp/fx/src/core/microsoft/scripting/Actions/AutoRuleTemplate.cs new file mode 100644 index 0000000000..b2c29f67af --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/AutoRuleTemplate.cs @@ -0,0 +1,179 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; + +namespace System.Dynamic.Binders { + /// + /// Handles auto-templating of rules. There are three important actions this performs: + /// 1. Detects if templating is possible between two rules + /// 2. Re-writes a non-templated rule into templated form + /// 3. Extracts the constants from a non-templated rule which is compatible with a + /// templated rule so that they can be used by the existing generated code. + /// + /// Auto-templating is currently only used for serially monomorphic call sites where we + /// can easily avoid code gen. It is not used for polymorphic call sites although such + /// a feature could be enabled in the future. + /// + internal static class AutoRuleTemplate { + /// + /// The entry point into auto-rule tempating. This consumes the monomorphic rule which is currently + /// stored in the cache as well as the rule that was just produced by the binder. + /// + /// The original rule that is currently stored in the cache. This rule may + /// or may not be a templated rule. + /// The new rule produced by a binder. + internal static CallSiteRule CopyOrCreateTemplatedRule(CallSiteRule from, CallSiteRule to) where T : class { + List newConstants; // the constants which need to be replaced in our new rule. + bool tooSpecific; + + from = FindCompatibleRuleForTemplate(from, to, out newConstants, out tooSpecific); + if (from == null) { + // trees are incompatible + return to; + } + + // We have 2 rules which are compatible. We should create a new template or + // re-use the existing one. + object[] templateArgs = GetConstantValues(newConstants); + + Expression newBody = new TemplateRuleRewriter(newConstants).Visit(to.Binding); + + if (from.Template == null || tooSpecific) { + // create a new one - either we are going from a non-templated rule to using a templated rule, + // or we are further generalizing an existing rule. We need to re-write the incoming tree + // to be templated over the necessary constants and return the new rule bound to the template. + + return new CallSiteRule(newBody, null, new TemplateData()); + } + + // we have compatible templated rules, we can just swap out the constant pool and + // be on our merry way. + + // get the old target + Delegate target = (Delegate)(object)from.RuleSet.GetTarget(); + + // create a new delegate closed over our new argument array + T dlg; + DynamicMethod templateMethod = from.TemplateMethod as DynamicMethod; + if (templateMethod != null) { + dlg = (T)(object)templateMethod.CreateDelegate(typeof(T), CloneData(target.Target, templateArgs)); + } else { + dlg = (T)(object)Delegate.CreateDelegate(typeof(T), CloneData(target.Target, templateArgs), target.Method); + } + + // create a new rule which is bound to the new delegate w/ the expression tree from the old code. + return new CallSiteRule(newBody, dlg, from.Template); + } + + private static CallSiteRule FindCompatibleRuleForTemplate(CallSiteRule from, CallSiteRule to, out List newConstants, out bool tooSpecific) where T : class { + // no templates exist for this rule, just compare the raw trees... + if (TreeComparer.Compare(to.Binding, from.Binding, out newConstants, out tooSpecific)) { + // the rules are not compatbile, don't add template values... + return from; + } + + return null; + } + + /// + /// Clones the delegate target to create new delegate around it. + /// The delegates created by the compiler are closed over the instance of Closure class. + /// + private static object CloneData(object data, params object[] newData) { + Debug.Assert(data != null); + + Closure closure = data as Closure; + if (closure != null) { + Debug.Assert(closure.Locals == null); + return new Closure(CopyArray(newData, closure.Constants), null); + } + + throw Error.BadDelegateData(); + } + + private static object[] CopyArray(object[] newData, object[] oldData) { + int copiedCount = 0; + + object[] res = new object[oldData.Length]; + for (int i = 0; i < oldData.Length; i++) { + ITemplatedValue itv = oldData[i] as ITemplatedValue; + if (itv == null) { + res[i] = oldData[i]; + continue; + } + copiedCount++; + + res[i] = itv.CopyWithNewValue(newData[itv.Index]); + } + + Debug.Assert(copiedCount == newData.Length); + return res; + } + + private static object[] GetConstantValues(List newConstants) { + object[] res = new object[newConstants.Count]; + int index = 0; + foreach (ConstantExpression ce in newConstants) { + res[index++] = ce.Value; + } + return res; + } + + internal class TemplateRuleRewriter : ExpressionVisitor { + private readonly List _constants; + private static CacheDict> _templateCtors = new CacheDict>(20); + + public TemplateRuleRewriter(List constants) { + _constants = constants; + } + + protected internal override Expression VisitConstant(ConstantExpression node) { + int index = _constants.IndexOf(node); + if (index != -1) { + // clear the constant from the list... This is incase the rule contains the + // same ConstantExpression instance multiple times and we're replacing the + // multiple entries. In that case we want each constant duplicated value + // to line up with a single index. + _constants[index] = null; + + // this is a constant we want to re-write, replace w/ a templated constant + object value = node.Value; + + Type elementType = TypeUtils.GetConstantType(node.Type); + + Func ctor; + if (!_templateCtors.TryGetValue(elementType, out ctor)) { + MethodInfo genMethod = typeof(TemplatedValue<>).MakeGenericType(new Type[] { elementType }).GetMethod("Make", BindingFlags.NonPublic | BindingFlags.Static); + _templateCtors[elementType] = ctor = (Func)genMethod.CreateDelegate(typeof(Func)); ; + } + + object constVal = ctor(value, index); + + return Expression.Property(Expression.Constant(constVal), ctor.Method.ReturnType.GetProperty("Value")); + } + + return base.VisitConstant(node); + } + } + } +} + diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/BinaryOperationBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/BinaryOperationBinder.cs new file mode 100644 index 0000000000..6495deb10f --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/BinaryOperationBinder.cs @@ -0,0 +1,109 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; +using Microsoft.Contracts; +using System.Linq.Expressions; + +namespace System.Dynamic.Binders { + public abstract class BinaryOperationBinder : MetaObjectBinder { + private ExpressionType _operation; + + protected BinaryOperationBinder(ExpressionType operation) { + ContractUtils.Requires(OperationIsValid(operation), "operation"); + _operation = operation; + } + + public ExpressionType Operation { + get { + return _operation; + } + } + + public MetaObject FallbackBinaryOperation(MetaObject target, MetaObject arg) { + return FallbackBinaryOperation(target, arg, null); + } + + public abstract MetaObject FallbackBinaryOperation(MetaObject target, MetaObject arg, MetaObject errorSuggestion); + + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNullItems(args, "args"); + ContractUtils.Requires(args.Length == 1); + + return target.BindBinaryOperation(this, args[0]); + } + + [Confined] + public override bool Equals(object obj) { + BinaryOperationBinder oa = obj as BinaryOperationBinder; + return oa != null && oa._operation == _operation; + } + + [Confined] + public override int GetHashCode() { + return BinaryOperationBinderHash ^ (int)_operation; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + internal static bool OperationIsValid(ExpressionType operation) { + switch (operation) { + #region Generated Binary Operation Binder Validator + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_binop_validator from: generate_tree.py + + case ExpressionType.Add: + case ExpressionType.And: + case ExpressionType.Divide: + case ExpressionType.Equal: + case ExpressionType.ExclusiveOr: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.LeftShift: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + case ExpressionType.Modulo: + case ExpressionType.Multiply: + case ExpressionType.NotEqual: + case ExpressionType.Or: + case ExpressionType.Power: + case ExpressionType.RightShift: + case ExpressionType.Subtract: + case ExpressionType.AddAssign: + case ExpressionType.AndAssign: + case ExpressionType.DivideAssign: + case ExpressionType.ExclusiveOrAssign: + case ExpressionType.LeftShiftAssign: + case ExpressionType.ModuloAssign: + case ExpressionType.MultiplyAssign: + case ExpressionType.OrAssign: + case ExpressionType.PowerAssign: + case ExpressionType.RightShiftAssign: + case ExpressionType.SubtractAssign: + + // *** END GENERATED CODE *** + + #endregion + + case ExpressionType.Extension: + return true; + + default: + return false; + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/BinaryOperationOnIndexBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/BinaryOperationOnIndexBinder.cs new file mode 100644 index 0000000000..f16dd19f4a --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/BinaryOperationOnIndexBinder.cs @@ -0,0 +1,155 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic.Utils; +using System.Linq.Expressions; +using Microsoft.Contracts; + +namespace System.Dynamic.Binders { + /// + /// A Binder that is responsible for runtime binding of operation: + /// a[b] (op)= c + /// + public abstract class BinaryOperationOnIndexBinder : MetaObjectBinder { + private ExpressionType _operation; + private readonly ReadOnlyCollection _arguments; + + /// + /// Constructor of the OperationOnIndexBinder object, representing "a[b] (op)= c" operation. + /// + /// Binary operation to be performed. + /// Description of the indexes (named, positional) + protected BinaryOperationOnIndexBinder(ExpressionType operation, params ArgumentInfo[] arguments) + : this(operation, (IEnumerable)arguments) { + } + + protected BinaryOperationOnIndexBinder(ExpressionType operation, IEnumerable arguments) { + ContractUtils.Requires(BinaryOperationBinder.OperationIsValid(operation), "operation"); + _operation = operation; + _arguments = arguments.ToReadOnly(); + } + + /// + /// The operation to be performed. + /// + public ExpressionType Operation { + get { + return _operation; + } + } + + /// + /// Descriptions of arguments to the indexer. This allows for named and positional arguments. + /// + public ReadOnlyCollection Arguments { + get { return _arguments; } + } + + /// + /// Implements Equality operation for the OperationOnIndexBinder + /// + /// Instance to comapre equal to. + /// true/false + [Confined] + public override bool Equals(object obj) { + BinaryOperationOnIndexBinder ia = obj as BinaryOperationOnIndexBinder; + return ia != null && ia._operation == _operation && ia._arguments.ListEquals(_arguments); + } + + /// + /// Calculates hash code for the OperationOnIndexBinder + /// + /// The hash code. + [Confined] + public override int GetHashCode() { + return BinaryOperationOnIndexBinderHash ^ (int)_operation ^ _arguments.ListHashCode(); + } + + /// + /// Performs binding of the operation on the target (represented as meta object) and + /// list of arguments (indexes and right-hand value) represented as meta objects + /// + /// Target of the operation. + /// List of indexes and right-hand value + /// MetaObject representing the binding. + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNull(args, "args"); + ContractUtils.Requires(args.Length >= 2, "args"); + + MetaObject value = args[args.Length - 1]; + MetaObject[] indexes = args.RemoveLast(); + + ContractUtils.RequiresNotNull(value, "args"); + ContractUtils.RequiresNotNullItems(indexes, "args"); + + return target.BindBinaryOperationOnIndex(this, indexes, value); + } + + /// + /// Implements a binding logic for the binary operation part of the binding. + /// This is called by the target when the target implements the whole operation: + /// a[b] += c + /// as: + /// a[b] = a[b] + c + /// to let the language participate in the binding of the binary operation only. + /// + /// Target of the operation. + /// Right-hand operator value + /// MetaObject representing the binding result. + public MetaObject FallbackBinaryOperation(MetaObject target, MetaObject arg) { + return FallbackBinaryOperation(target, arg, null); + } + + /// + /// Implements a binding logic for the binary operation part of the binding. + /// This is called by the target when the target implements the whole operation: + /// a[b] += c + /// as: + /// a[b] = a[b] + c + /// to let the language participate in the binding of the binary operation only. + /// + /// Target of the operation. + /// Right-hand operator value + /// The representaiton of the binding error that the target meta object recommends the language to use if the language cannot bind. This allows the target meta object to participate in the error handling process. + /// MetaObject representing the binding result. + public abstract MetaObject FallbackBinaryOperation(MetaObject target, MetaObject arg, MetaObject errorSuggestion); + + /// + /// Implements a binding logic for the operation. This is called by the target when + /// the target lets the executing language participate in the binding process. + /// + /// Target of the operation. + /// List of indexes + /// Right-hand value + /// MetaObject representing the binding. + public MetaObject FallbackBinaryOperationOnIndex(MetaObject target, MetaObject[] indexes, MetaObject value) { + return FallbackBinaryOperationOnIndex(target, indexes, value, null); + } + + /// + /// Implements a binding logic for the operation. This is called by the target when + /// the target lets the executing language participate in the binding process. + /// + /// Target of the operation. + /// List of indexes + /// Right-hand value + /// The representaiton of the binding error that the target meta object recommends the language to use if the language cannot bind. This allows the target meta object to participate in the error handling process. + /// MetaObject representing the binding. + public abstract MetaObject FallbackBinaryOperationOnIndex(MetaObject target, MetaObject[] indexes, MetaObject value, MetaObject errorSuggestion); + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/BinaryOperationOnMemberBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/BinaryOperationOnMemberBinder.cs new file mode 100644 index 0000000000..673ae14331 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/BinaryOperationOnMemberBinder.cs @@ -0,0 +1,148 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; +using System.Linq.Expressions; +using Microsoft.Contracts; + +namespace System.Dynamic.Binders { + /// + /// A Binder that is responsible for runtime binding of operation: + /// a.b (op)= c + /// + public abstract class BinaryOperationOnMemberBinder : MetaObjectBinder { + private readonly ExpressionType _operation; + private readonly string _name; + private readonly bool _ignoreCase; + + /// + /// Constructor of the OperationOnIndexBinder object, representing "a.b (op)= c" operation. + /// + /// Binary operation to be performed. + /// Name of the member for the operation. + /// Ignore case of the member. + protected BinaryOperationOnMemberBinder(ExpressionType operation, string name, bool ignoreCase) { + ContractUtils.RequiresNotNull(name, "name"); + ContractUtils.Requires(BinaryOperationBinder.OperationIsValid(operation), "operation"); + _operation = operation; + _name = name; + _ignoreCase = ignoreCase; + } + + /// + /// The operation to be performed. + /// + public ExpressionType Operation { + get { + return _operation; + } + } + + /// + /// Name of the member for the operation. + /// + public string Name { + get { + return _name; + } + } + + /// + /// Ignore case of the member. + /// + public bool IgnoreCase { + get { + return _ignoreCase; + } + } + + /// + /// Implements a binding logic for the binary operation part of the binding. + /// This is called by the target when the target implements the whole operation: + /// a[b] += c + /// as: + /// a[b] = a[b] + c + /// to let the language participate in the binding of the binary operation only. + /// + /// Target of the operation. + /// Right-hand operator value + /// MetaObject representing the binding result. + public MetaObject FallbackBinaryOperation(MetaObject target, MetaObject arg) { + return FallbackBinaryOperation(target, arg, null); + } + + /// + /// Implements a binding logic for the binary operation part of the binding. + /// This is called by the target when the target implements the whole operation: + /// a[b] += c + /// as: + /// a[b] = a[b] + c + /// to let the language participate in the binding of the binary operation only. + /// + /// Target of the operation. + /// Right-hand operator value + /// The representaiton of the binding error that the target meta object recommends the language to use if the language cannot bind. This allows the target meta object to participate in the error handling process. + /// MetaObject representing the binding result. + public abstract MetaObject FallbackBinaryOperation(MetaObject target, MetaObject arg, MetaObject errorSuggestion); + + /// + /// Implements a binding logic for the operation. This is called by the target when + /// the target lets the executing language participate in the binding process. + /// + /// Target of the operation. + /// The right-hand value + /// MetaObject representing the binding. + public MetaObject FallbackBinaryOperationOnMember(MetaObject target, MetaObject value) { + return FallbackBinaryOperationOnMember(target, value, null); + } + + /// + /// Implements a binding logic for the operation. This is called by the target when + /// the target lets the executing language participate in the binding process. + /// + /// Target of the operation. + /// The right-hand value + /// The representaiton of the binding error that the target meta object recommends the language to use if the language cannot bind. This allows the target meta object to participate in the error handling process. + /// MetaObject representing the binding. + public abstract MetaObject FallbackBinaryOperationOnMember(MetaObject target, MetaObject value, MetaObject errorSuggestion); + + public sealed override MetaObject Bind(MetaObject target, params MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.Requires(args != null && args.Length == 1, "args"); + + return target.BindBinaryOperationOnMember(this, args[0]); + } + + /// + /// Implements Equality operation for the OperationOnMemberBinder + /// + /// Instance to comapre equal to. + /// true/false + [Confined] + public override bool Equals(object obj) { + BinaryOperationOnMemberBinder gma = obj as BinaryOperationOnMemberBinder; + return gma != null && gma._operation == _operation && gma._name == _name && gma._ignoreCase == _ignoreCase; + } + + /// + /// Calculates hash code for the OperationOnMemberBinder + /// + /// The hash code. + [Confined] + public override int GetHashCode() { + return BinaryOperationOnMemberBinderHash ^ (int)_operation ^ _name.GetHashCode() ^ (_ignoreCase ? unchecked((int)0x80000000) : 0); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/CallSite.cs b/ndp/fx/src/core/microsoft/scripting/Actions/CallSite.cs new file mode 100644 index 0000000000..ccf3d1f403 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/CallSite.cs @@ -0,0 +1,631 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Linq.Expressions; +using System.Linq.Expressions.Compiler; +using System.Reflection; +using System.Threading; + +namespace System.Runtime.CompilerServices { + + // + // A CallSite provides a fast mechanism for call-site caching of dynamic dispatch + // behvaior. Each site will hold onto a delegate that provides a fast-path dispatch + // based on previous types that have been seen at the call-site. This delegate will + // call UpdateAndExecute if it is called with types that it hasn't seen before. + // Updating the binding will typically create (or lookup) a new delegate + // that supports fast-paths for both the new type and for any types that + // have been seen previously. + // + // DynamicSites will generate the fast-paths specialized for sets of runtime argument + // types. However, they will generate exactly the right amount of code for the types + // that are seen in the program so that int addition will remain as fast as it would + // be with custom implementation of the addition, and the user-defined types can be + // as fast as ints because they will all have the same optimal dynamically generated + // fast-paths. + // + // DynamicSites don't encode any particular caching policy, but use their + // CallSiteBinding to encode a caching policy. + // + + + /// + /// A Dynamic Call Site base class. This type is used as a parameter type to the + /// dynamic site targets. The first parameter of the delegate (T) below must be + /// of this type. + /// + public abstract class CallSite { + + // Cache of CallSite constructors for a given delegate type + private static CacheDict> _SiteCtors; + + /// + /// The Binder responsible for binding operations at this call site. + /// This binder is invoked by the UpdateAndExecute below if all Level 0, + /// Level 1 and Level 2 caches experience cache miss. + /// + internal readonly CallSiteBinder _binder; + + // only CallSite derives from this + internal CallSite(CallSiteBinder binder) { + _binder = binder; + } + + /// + /// Class responsible for binding dynamic operations on the dynamic site. + /// + public CallSiteBinder Binder { + get { return _binder; } + } + + /// + /// Creates a CallSite with the given delegate type and binder. + /// + /// The CallSite delegate type. + /// The CallSite binder. + /// The new CallSite. + public static CallSite Create(Type delegateType, CallSiteBinder binder) { + ContractUtils.RequiresNotNull(delegateType, "delegateType"); + ContractUtils.RequiresNotNull(binder, "binder"); + ContractUtils.Requires(delegateType.IsSubclassOf(typeof(Delegate)), "delegateType", Strings.TypeMustBeDerivedFromSystemDelegate); + + if (_SiteCtors == null) { + // It's okay to just set this, worst case we're just throwing away some data + _SiteCtors = new CacheDict>(100); + } + Func ctor; + lock (_SiteCtors) { + if (!_SiteCtors.TryGetValue(delegateType, out ctor)) { + MethodInfo method = typeof(CallSite<>).MakeGenericType(delegateType).GetMethod("Create"); + ctor = (Func)Delegate.CreateDelegate(typeof(Func), method); + _SiteCtors.Add(delegateType, ctor); + } + } + return ctor(binder); + } + } + + /// + /// Dynamic site type. + /// + /// The delegate type. + public sealed partial class CallSite : CallSite where T : class { + /// + /// The update delegate. Called when the dynamic site experiences cache miss. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public readonly T Update; + + /// + /// The Level 0 cache - a delegate specialized based on the site history. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + public T Target; + + /// + /// The Level 1 cache - a history of the dynamic site. + /// + internal RuleSet Rules; + + /// + /// The Level 2 cache - all rules produced for the same generic instantiation + /// of the dynamic site (all dynamic sites with matching delegate type). + /// + private static Dictionary> _cache; + + // Cached update delegate for all sites with a given T + private static T _CachedUpdate; + + private CallSite(CallSiteBinder binder) + : base(binder) { + Target = Update = GetUpdateDelegate(); + } + + internal CallSite(CallSiteBinder binder, T update) + : base(binder) { + Target = Update = update; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] + public static CallSite Create(CallSiteBinder binder) { + return new CallSite(binder); + } + + private T GetUpdateDelegate() { + // This is intentionally non-static to speed up creation - in particular MakeUpdateDelegate + // as static generic methods are more expensive than instance methods. We call a ref helper + // so we only access the generic static field once. + return GetUpdateDelegate(ref _CachedUpdate); + } + + private T GetUpdateDelegate(ref T addr) { + if (addr == null) { + // reduce creation cost by not using Interlocked.CompareExchange. Calling I.CE causes + // us to spend 25% of our creation time in JIT_GenericHandle. Instead we'll rarely + // create 2 delegates with no other harm caused. + addr = MakeUpdateDelegate(); + } + return addr; + } + + // TODO: Remove Me + private static void ClearRuleCache() { + if (_cache != null) { + lock (_cache) { + _cache.Clear(); + } + } + } + + internal RuleTree RuleCache { + get { + RuleTree tree; + object cookie = _binder.CacheIdentity; + + if (_cache == null) { + Interlocked.CompareExchange( + ref _cache, + new Dictionary>(), + null); + } + + lock (_cache) { + if (!_cache.TryGetValue(cookie, out tree)) { + _cache[cookie] = tree = new RuleTree(); + } + } + + return tree; + } + } + + internal T MakeUpdateDelegate() { + Type target = typeof(T); + Type[] args; + MethodInfo invoke = target.GetMethod("Invoke"); + + if (target.IsGenericType && IsSimpleSignature(invoke, out args)) { + MethodInfo method = null; + + if (invoke.ReturnType == typeof(void)) { + if (target == DelegateHelpers.GetActionType(args.AddFirst(typeof(CallSite)))) { + method = typeof(UpdateDelegates).GetMethod("UpdateAndExecuteVoid" + args.Length, BindingFlags.NonPublic | BindingFlags.Static); + } + } else { + if (target == DelegateHelpers.GetFuncType(args.AddFirst(typeof(CallSite)))) { + method = typeof(UpdateDelegates).GetMethod("UpdateAndExecute" + (args.Length - 1), BindingFlags.NonPublic | BindingFlags.Static); + } + } + if (method != null) { + return (T)(object)method.MakeGenericMethod(args).CreateDelegate(target); + } + } + + return CreateCustomUpdateDelegate(invoke); + } + + + private static bool IsSimpleSignature(MethodInfo invoke, out Type[] sig) { + ParameterInfo[] pis = invoke.GetParametersCached(); + ContractUtils.Requires(pis.Length > 0 && pis[0].ParameterType == typeof(CallSite), "T"); + + Type[] args = new Type[invoke.ReturnType != typeof(void) ? pis.Length : pis.Length - 1]; + bool supported = true; + + for (int i = 1; i < pis.Length; i++) { + ParameterInfo pi = pis[i]; + if (pi.IsByRefParameter()) { + supported = false; + } + args[i - 1] = pi.ParameterType; + } + if (invoke.ReturnType != typeof(void)) { + args[args.Length - 1] = invoke.ReturnType; + } + sig = args; + return supported; + } + + // + // WARNING: If you're changing this method, make sure you update the + // pregenerated versions as well, which are generated by + // generate_dynsites.py + // The two implementations *must* be kept functionally equivalent! + // + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + private T CreateCustomUpdateDelegate(MethodInfo invoke) { + var body = new List(); + var vars = new List(); + var @params = invoke.GetParametersCached().Map(p => Expression.Parameter(p.ParameterType, p.Name)); + var @return = Expression.Label(invoke.GetReturnType()); + var typeArgs = new[] { typeof(T) }; + + var site = @params[0]; + var arguments = @params.RemoveFirst(); + + //var @this = (CallSite)site; + var @this = Expression.Variable(typeof(CallSite), "this"); + vars.Add(@this); + body.Add(Expression.Assign(@this, Expression.Convert(site, @this.Type))); + + //CallSiteRule[] applicable; + var applicable = Expression.Variable(typeof(CallSiteRule[]), "applicable"); + vars.Add(applicable); + + //CallSiteRule rule; + var rule = Expression.Variable(typeof(CallSiteRule), "rule"); + vars.Add(rule); + + //T ruleTarget, startingTarget = @this.Target; + var ruleTarget = Expression.Variable(typeof(T), "ruleTarget"); + vars.Add(ruleTarget); + var startingTarget = Expression.Variable(typeof(T), "startingTarget"); + vars.Add(startingTarget); + body.Add(Expression.Assign(startingTarget, Expression.Field(@this, "Target"))); + + //TRet result; + ParameterExpression result = null; + if (@return.Type != typeof(void)) { + vars.Add(result = Expression.Variable(@return.Type, "result")); + } + + //int count, index; + var count = Expression.Variable(typeof(int), "count"); + vars.Add(count); + var index = Expression.Variable(typeof(int), "index"); + vars.Add(index); + + //CallSiteRule originalRule = null; + var originalRule = Expression.Variable(typeof(CallSiteRule), "originalRule"); + vars.Add(originalRule); + + //// + //// Create matchmaker and its site. We'll need them regardless. + //// + //// **** THIS DIFFERS FROM THE GENERATED CODE TO NOT EXPOSE the Matchmaker class + //// **** Instead we close over the match bool here and create a delegate each time + //// **** through. This is less efficient than caching the delegate but avoids + //// **** exposing the large Matchmaker class. + //// + //bool match = true; + //site = CreateMatchmaker( + // @this, + // (mm_site, %(matchmakerArgs)s) => { + // match = false; + // %(returnDefault)s; + // } + //); + var match = Expression.Variable(typeof(bool), "match"); + vars.Add(match); + var resetMatch = Expression.Assign(match, Expression.Constant(true)); + body.Add(resetMatch); + body.Add( + Expression.Assign( + site, + Expression.Call( + typeof(CallSiteOps), + "CreateMatchmaker", + typeArgs, + @this, + Expression.Lambda( + Expression.Block( + Expression.Assign(match, Expression.Constant(false)), + Expression.Default(@return.Type) + ), + new ReadOnlyCollection(@params) + ) + ) + ) + ); + + //// + //// Level 1 cache lookup + //// + //if ((applicable = CallSiteOps.GetRules(@this)) != null) { + // for (index = 0, count = applicable.Length; index < count; index++) { + // rule = applicable[index]; + + // // + // // Execute the rule + // // + // ruleTarget = CallSiteOps.SetTarget(@this, rule); + + // try { + // %(setResult)s ruleTarget(site, %(args)s); + // if (match) { + // %(returnResult)s; + // } + // } finally { + // if (match) { + // // + // // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // // see them again. The site is polymorphic. Update the delegate and keep running + // // + // CallSiteOps.SetPolymorphicTarget(@this); + // } + // } + + // if (startingTarget == ruleTarget) { + // // our rule was previously monomorphic, if we produce another monomorphic + // // rule we should try and share code between the two. + // originalRule = rule; + // } + + // // Rule didn't match, try the next one + // match = true; + // } + //} + Expression invokeRule; + if (@return.Type == typeof(void)) { + invokeRule = Expression.Block( + Expression.Invoke(ruleTarget, new ReadOnlyCollection(@params)), + IfThen(match, Expression.Return(@return)) + ); + } else { + invokeRule = Expression.Block( + Expression.Assign(result, Expression.Invoke(ruleTarget, new ReadOnlyCollection(@params))), + IfThen(match, Expression.Return(@return, result)) + ); + } + + var getRule = Expression.Assign( + ruleTarget, + Expression.Call( + typeof(CallSiteOps), + "SetTarget", + typeArgs, + @this, + Expression.Assign(rule, Expression.ArrayAccess(applicable, index)) + ) + ); + + var checkOriginalRule = IfThen( + Expression.Equal( + Expression.Convert(startingTarget, typeof(object)), + Expression.Convert(ruleTarget, typeof(object)) + ), + Expression.Assign(originalRule, rule) + ); + + var tryRule = Expression.TryFinally( + invokeRule, + IfThen( + match, + Expression.Call(typeof(CallSiteOps), "SetPolymorphicTarget", typeArgs, @this) + ) + ); + + var @break = Expression.Label(); + + var breakIfDone = IfThen( + Expression.Equal(index, count), + Expression.Break(@break) + ); + + var incrementIndex = Expression.PreIncrementAssign(index); + + body.Add( + IfThen( + Expression.NotEqual( + Expression.Assign(applicable, Expression.Call(typeof(CallSiteOps), "GetRules", typeArgs, @this)), + Expression.Constant(null, applicable.Type) + ), + Expression.Block( + Expression.Assign(count, Expression.ArrayLength(applicable)), + Expression.Assign(index, Expression.Constant(0)), + Expression.Loop( + Expression.Block( + breakIfDone, + getRule, + tryRule, + checkOriginalRule, + resetMatch, + incrementIndex + ), + @break, + null + ) + ) + ) + ); + + //// + //// Level 2 cache lookup + //// + //var args = new object[] { arg0, arg1, ... }; + var args = Expression.Variable(typeof(object[]), "args"); + vars.Add(args); + body.Add( + Expression.Assign( + args, + Expression.NewArrayInit(typeof(object), arguments.Map(p => Convert(p, typeof(object)))) + ) + ); + + //// + //// Any applicable rules in level 2 cache? + //// + //if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + // count = applicable.Length; + // for (index = 0; index < count; index++) { + // rule = applicable[index]; + // + // // + // // Execute the rule + // // + // ruleTarget = CallSiteOps.SetTarget(@this, rule); + // + // try { + // result = ruleTarget(site, arg0); + // if (match) { + // return result; + // } + // } finally { + // if (match) { + // // + // // Rule worked. Add it to level 1 cache + // // + // + // CallSiteOps.AddRule(@this, rule); + // // and then move it to the front of the L2 cache + // @this.RuleCache.MoveRule(rule, args); + // } + // } + // + // if (startingTarget == ruleTarget) { + // // If we've gone megamorphic we can still template off the L2 cache + // originalRule = rule; + // } + // + // // Rule didn't match, try the next one + // match = true; + // } + //} + + tryRule = Expression.TryFinally( + invokeRule, + IfThen(match, + Expression.Block( + Expression.Call(typeof(CallSiteOps), "AddRule", typeArgs, @this, rule), + Expression.Call(typeof(CallSiteOps), "MoveRule", typeArgs, @this, rule, args) + ) + ) + ); + + body.Add( + IfThen( + Expression.NotEqual( + Expression.Assign( + applicable, + Expression.Call(typeof(CallSiteOps), "FindApplicableRules", typeArgs, @this, args) + ), + Expression.Constant(null, applicable.Type) + ), + Expression.Block( + Expression.Assign(count, Expression.ArrayLength(applicable)), + Expression.Assign(index, Expression.Constant(0)), + Expression.Loop( + Expression.Block( + breakIfDone, + getRule, + tryRule, + checkOriginalRule, + resetMatch, + incrementIndex + ), + @break, + null + ) + ) + ) + ); + + //// + //// Miss on Level 0, 1 and 2 caches. Create new rule + //// + + //rule = null; + //for (; ; ) { + // rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // // + // // Execute the rule on the matchmaker site + // // + // ruleTarget = CallSiteOps.SetTarget(@this, rule); + + // try { + // %(setResult)s ruleTarget(site, %(args)s); + // if (match) { + // %(returnResult)s; + // } + // } finally { + // if (match) { + // // + // // The rule worked. Add it to level 1 cache. + // // + // CallSiteOps.AddRule(@this, rule); + // } + // } + + // // Rule we got back didn't work, try another one + // match = true; + //} + body.Add(Expression.Assign(rule, Expression.Constant(null, rule.Type))); + + getRule = Expression.Assign( + ruleTarget, + Expression.Call( + typeof(CallSiteOps), + "SetTarget", + typeArgs, + @this, + Expression.Assign( + rule, + Expression.Call(typeof(CallSiteOps), "CreateNewRule", typeArgs, @this, rule, originalRule, args) + ) + ) + ); + + body.Add( + Expression.Loop( + Expression.Block(getRule, tryRule, resetMatch), + null, null + ) + ); + + body.Add(Expression.Default(@return.Type)); + + var lambda = Expression.Lambda( + Expression.Label( + @return, + Expression.Block( + new ReadOnlyCollection(vars), + new ReadOnlyCollection(body) + ) + ), + // TODO: fix the name '_stub_', for now it's the easy way to + // get languages to skip this frame in backtraces + "_stub_", + new ReadOnlyCollection(@params) + ); + + // Need to compile with forceDynamic because T could be invisible, + // or one of the argument types could be invisible + MethodInfo method; + return LambdaCompiler.CompileLambda(lambda, true, out method); + } + + // TODO: is this general enough that it should be on Expression? + /// + /// Behaves like an "if" statement in imperative languages. The type is + /// always treated as void regardless of the body's type. The else + /// branch is empty + /// + private static ConditionalExpression IfThen(Expression test, Expression ifTrue) { + return Expression.Condition(test, Expression.Void(ifTrue), Expression.Empty()); + } + private static Expression Convert(Expression arg, Type type) { + if (TypeUtils.AreReferenceAssignable(type, arg.Type)) { + return arg; + } + return Expression.Convert(arg, type); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/CallSiteBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/CallSiteBinder.cs new file mode 100644 index 0000000000..2cb331bffd --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/CallSiteBinder.cs @@ -0,0 +1,46 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.ObjectModel; +using System.Linq.Expressions; + +namespace System.Runtime.CompilerServices { + /// + /// Class responsible for binding dynamic operations on the dynamic site. + /// + public abstract class CallSiteBinder { + protected CallSiteBinder() { + } + + /// + /// Key used for the DLR caching + /// + public abstract object CacheIdentity { get; } + + /// + /// The bind call to produce the binding. + /// + /// Array of arguments to the call + /// Array of ParameterExpressions that represent to parameters of the call site + /// LabelTarget used to return the result of the call site + /// + /// An Expression that performs tests on the arguments, and + /// returns a result if the test is valid. If the tests fail, Bind + /// will be called again to produce a new Expression for the new + /// argument types + /// + public abstract Expression Bind(object[] args, ReadOnlyCollection parameters, LabelTarget returnLabel); + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/CallSiteOps.cs b/ndp/fx/src/core/microsoft/scripting/Actions/CallSiteOps.cs new file mode 100644 index 0000000000..797efe28d0 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/CallSiteOps.cs @@ -0,0 +1,117 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +namespace System.Runtime.CompilerServices { + + // Conceptually these are instance methods on CallSite but + // we don't want users to see them + + /// + /// Do not use this type. It is for internal use by CallSite + /// + public static class CallSiteOps { + + [Obsolete("do not use this method", true)] + public static T SetTarget(CallSite site, CallSiteRule rule) where T : class { + return site.Target = rule.RuleSet.GetTarget(); + } + + [Obsolete("do not use this method", true)] + public static void AddRule(CallSite site, CallSiteRule rule) where T : class { + lock (site) { + if (site.Rules == null) { + site.Rules = rule.RuleSet; + } else { + site.Rules = site.Rules.AddRule(rule); + } + } + } + + [Obsolete("do not use this method", true)] + public static void MoveRule(CallSite site, CallSiteRule rule, object [] args) where T : class { + site.RuleCache.MoveRule(rule, args); + } + + [Obsolete("do not use this method", true)] + public static CallSite CreateMatchmaker(CallSite site, T matchmaker) where T : class { + return new CallSite(site.Binder, matchmaker); + } + + [Obsolete("do not use this method", true)] + public static CallSiteRule CreateNewRule(CallSite site, CallSiteRule oldRule, CallSiteRule originalRule, object[] args) where T : class { + if (oldRule != null) { + // + // The rule didn't work and since we optimistically added it into the + // level 2 cache. Remove it now since the rule is no good. + // + site.RuleCache.RemoveRule(args, oldRule); + } + + Expression binding = site.Binder.Bind(args, CallSiteRule.Parameters, CallSiteRule.ReturnLabel); + + // + // Check the produced rule + // + if (binding == null) { + throw Error.NoOrInvalidRuleProduced(); + } + + var rule = new CallSiteRule(binding); + + if (originalRule != null) { + // compare our new rule and our original monomorphic rule. If they only differ from constants + // then we'll want to re-use the code gen if possible. + rule = AutoRuleTemplate.CopyOrCreateTemplatedRule(originalRule, rule); + } + + // + // Add the rule to the level 2 cache. This is an optimistic add so that cache miss + // on another site can find this existing rule rather than building a new one. We + // add the originally added rule, not the templated one, to the global cache. That + // allows sites to template on their own. + // + site.RuleCache.AddRule(args, rule); + + return rule; + } + + [Obsolete("do not use this method", true)] + public static CallSiteRule[] FindApplicableRules(CallSite site, object[] args) where T : class { + return site.RuleCache.FindApplicableRules(args); + } + + [Obsolete("do not use this method", true)] + public static void SetPolymorphicTarget(CallSite site) where T : class { + T target = site.Rules.GetTarget(); + // If the site has gone megamorphic, we'll have an empty RuleSet + // with no target. In that case, we don't want to clear out the + // target + if (target != null) { + site.Target = target; + } + } + + [Obsolete("do not use this method", true)] + public static CallSiteRule[] GetRules(CallSite site) where T : class { + if (site.Rules == null) { + return EmptyArray>.Instance; + } + return site.Rules.GetRules(); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/CallSiteRule.cs b/ndp/fx/src/core/microsoft/scripting/Actions/CallSiteRule.cs new file mode 100644 index 0000000000..32304f665d --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/CallSiteRule.cs @@ -0,0 +1,141 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.ObjectModel; +using System.Globalization; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic.Binders; +using System.Dynamic.Utils; + +namespace System.Runtime.CompilerServices { + /// + /// This type is only used by CallSite internally. Do not use + /// + public sealed class CallSiteRule where T : class { + + internal static readonly ReadOnlyCollection Parameters; + internal static readonly LabelTarget ReturnLabel; + + // cctor will be lazily executed when a given T is first referenced + // TODO: are lazily initialized properties faster? (would need locks for thread safety) + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static CallSiteRule() { + Type target = typeof(T); + if (!typeof(Delegate).IsAssignableFrom(target)) { + throw Error.TypeParameterIsNotDelegate(target); + } + + MethodInfo invoke = target.GetMethod("Invoke"); + ParameterInfo[] pis = invoke.GetParametersCached(); + if (pis[0].ParameterType != typeof(CallSite)) { + throw Error.FirstArgumentMustBeCallSite(); + } + + var @params = new ParameterExpression[pis.Length - 1]; + for (int i = 0; i < @params.Length; i++) { + @params[i] = Expression.Parameter(pis[i + 1].ParameterType, "$arg" + i); + } + + Parameters = new ReadOnlyCollection(@params); + ReturnLabel = Expression.Label(invoke.GetReturnType()); + } + + /// + /// The rule set that includes only this rule. + /// + internal readonly SmallRuleSet RuleSet; + + /// + /// The binding expression tree + /// + private readonly Expression _binding; + + /// + /// Template data - null for methods which aren't templated. Non-null for methods which + /// have been templated. The same template data is shared across all templated rules with + /// the same target method. + /// + private readonly TemplateData _template; + + internal CallSiteRule(Expression binding) { + _binding = binding; + RuleSet = new SmallRuleSet(new[] { this }); + } + + internal CallSiteRule(Expression binding, T target, TemplateData template) { + _binding = binding; + RuleSet = new SmallRuleSet(target, new CallSiteRule[] { this }); + _template = template; + } + + /// + /// The expression representing the bound operation + /// + internal Expression Binding { + get { return _binding; } + } + + internal TemplateData Template { + get { + return _template; + } + } + + /// + /// Gets or sets the method which is used for templating. If the rule is + /// not templated then this is a nop (and returns null for the getter). + /// + /// The method is tracked here independently from the delegate for the + /// common case of the method being a DynamicMethod. In order to re-bind + /// the existing DynamicMethod to a new set of templated parameters we need + /// to have the original method. + /// + internal MethodInfo TemplateMethod { + get { + if (_template != null) { + return _template.Method; + } + return null; + } + set { + if (_template != null) { + _template.Method = value; + } + } + } + +#if MICROSOFT_SCRIPTING_CORE + public string Dump { + get { + using (System.IO.StringWriter writer = new System.IO.StringWriter(CultureInfo.CurrentCulture)) { + ExpressionWriter.Dump(_binding, "Rule", writer); + return writer.ToString(); + } + } + } +#endif + } + + /// + /// Data used for tracking templating information in a rule. + /// + /// Currently we just track the method so we can retarget to + /// new constant pools. + /// + internal class TemplateData where T : class { + internal MethodInfo Method; + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/ConvertBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/ConvertBinder.cs new file mode 100644 index 0000000000..ecfe0ce4f4 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/ConvertBinder.cs @@ -0,0 +1,66 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; +using Microsoft.Contracts; + +namespace System.Dynamic.Binders { + public abstract class ConvertBinder : MetaObjectBinder { + private readonly Type _type; + private readonly bool _explicit; + + protected ConvertBinder(Type type, bool @explicit) { + _type = type; + _explicit = @explicit; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + public Type Type { + get { + return _type; + } + } + + public bool Explicit { + get { + return _explicit; + } + } + + public MetaObject FallbackConvert(MetaObject target) { + return FallbackConvert(target, null); + } + + public abstract MetaObject FallbackConvert(MetaObject target, MetaObject errorSuggestion); + + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.Requires(args.Length == 0); + + return target.BindConvert(this); + } + + [Confined] + public override bool Equals(object obj) { + ConvertBinder ca = obj as ConvertBinder; + return ca != null && ca._type == _type && ca._explicit == _explicit; + } + + [Confined] + public override int GetHashCode() { + return ConvertBinderHash ^ _type.GetHashCode() ^ (_explicit ? 0x8000000 : 0); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/CreateInstanceBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/CreateInstanceBinder.cs new file mode 100644 index 0000000000..67fb47017a --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/CreateInstanceBinder.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Dynamic.Utils; +using Microsoft.Contracts; + +namespace System.Dynamic.Binders { + public abstract class CreateInstanceBinder : MetaObjectBinder { + private readonly ReadOnlyCollection _arguments; + + protected CreateInstanceBinder(IEnumerable arguments) { + _arguments = arguments.ToReadOnly(); + } + + protected CreateInstanceBinder(params ArgumentInfo[] arguments) + : this((IEnumerable)arguments) { + } + + public ReadOnlyCollection Arguments { + get { + return _arguments; + } + } + + public MetaObject FallbackCreateInstance(MetaObject target, MetaObject[] args) { + return FallbackCreateInstance(target, args, null); + } + + public abstract MetaObject FallbackCreateInstance(MetaObject target, MetaObject[] args, MetaObject errorSuggestion); + + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNullItems(args, "args"); + + return target.BindCreateInstance(this, args); + } + + [Confined] + public override bool Equals(object obj) { + CreateInstanceBinder ca = obj as CreateInstanceBinder; + return ca != null && ca._arguments.ListEquals(_arguments); + } + + [Confined] + public override int GetHashCode() { + return CreateInstanceBinderHash ^ _arguments.ListHashCode(); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/DeleteIndexBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/DeleteIndexBinder.cs new file mode 100644 index 0000000000..82442e3c7f --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/DeleteIndexBinder.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Dynamic.Utils; +using Microsoft.Contracts; + +namespace System.Dynamic.Binders { + public abstract class DeleteIndexBinder : MetaObjectBinder { + private readonly ReadOnlyCollection _arguments; + + protected DeleteIndexBinder(params ArgumentInfo[] arguments) + : this((IEnumerable)arguments) { + } + + protected DeleteIndexBinder(IEnumerable arguments) { + _arguments = arguments.ToReadOnly(); + } + + public ReadOnlyCollection Arguments { + get { return _arguments; } + } + + [Confined] + public override bool Equals(object obj) { + DeleteIndexBinder ia = obj as DeleteIndexBinder; + return ia != null && ia._arguments.ListEquals(_arguments); + } + + [Confined] + public override int GetHashCode() { + return DeleteIndexBinderHash ^ _arguments.ListHashCode(); + } + + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNullItems(args, "args"); + + return target.BindDeleteIndex(this, args); + } + + public MetaObject FallbackDeleteIndex(MetaObject target, MetaObject[] indexes) { + return FallbackDeleteIndex(target, indexes, null); + } + + public abstract MetaObject FallbackDeleteIndex(MetaObject target, MetaObject[] indexes, MetaObject errorSuggestion); + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/DeleteMemberBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/DeleteMemberBinder.cs new file mode 100644 index 0000000000..d37fad46e3 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/DeleteMemberBinder.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; + +namespace System.Dynamic.Binders { + public abstract class DeleteMemberBinder : MetaObjectBinder { + private readonly string _name; + private readonly bool _ignoreCase; + + protected DeleteMemberBinder(string name, bool ignoreCase) { + ContractUtils.RequiresNotNull(name, "name"); + + _name = name; + _ignoreCase = ignoreCase; + } + + public string Name { + get { + return _name; + } + } + + public bool IgnoreCase { + get { + return _ignoreCase; + } + } + + public MetaObject FallbackDeleteMember(MetaObject target) { + return FallbackDeleteMember(target, null); + } + + public abstract MetaObject FallbackDeleteMember(MetaObject target, MetaObject errorSuggestion); + + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.Requires(args.Length == 0); + + return target.BindDeleteMember(this); + } + + public override int GetHashCode() { + return DeleteMemberBinderHash ^ _name.GetHashCode() ^ (_ignoreCase ? 0x8000000 : 0); ; + } + + public override bool Equals(object obj) { + DeleteMemberBinder dma = obj as DeleteMemberBinder; + return dma != null && dma._name == _name && dma._ignoreCase == _ignoreCase; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/DynamicObject.cs b/ndp/fx/src/core/microsoft/scripting/Actions/DynamicObject.cs new file mode 100644 index 0000000000..581eb7b3dc --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/DynamicObject.cs @@ -0,0 +1,589 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using System.Dynamic.Utils; + +namespace System.Dynamic.Binders { + /// + /// Provides a simple class that can be inherited from to create an object with dynamic behavior + /// at runtime. Subclasses can override the various binder methods (GetMember, SetMember, Call, etc...) + /// to provide custom behavior that will be invoked at runtime. + /// + /// If a method is not overridden then the Dynamic object does not directly support that behavior and + /// the call site will determine how the binder should be performed. + /// + public class DynamicObject : IDynamicObject { + + /// + /// Enables derived types to create a new instance of Dynamic. Dynamic instances cannot be + /// directly instantiated because they have no implementation of dynamic behavior. + /// + protected DynamicObject() { + } + + #region Public Virtual APIs + + /// + /// When overridden in a derived class provides the non-Meta implementation of getting a member. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TryGetMember(GetMemberBinder binder, out object result) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of setting a member. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + public virtual bool TrySetMember(SetMemberBinder binder, object value) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of deleting a member. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + public virtual bool TryDeleteMember(DeleteMemberBinder binder) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of calling a member + /// in the expando. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of converting the + /// Dynamic object to another type. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TryConvert(ConvertBinder binder, out object result) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of creating an instance + /// of the Dynamic object. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of invoking the + /// Dynamic object. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TryInvoke(InvokeBinder binder, object[] args, out object result) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of + /// performing a binary operation. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of + /// performing a unary operation. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TryUnaryOperation(UnaryOperationBinder binder, out object result) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of + /// performing a get index operation. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TryGetIndex(GetIndexBinder binder, object[] args, out object result) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of + /// performing a set index operation. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of + /// performing a delete index operation. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + public virtual bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of + /// performing an operation on member "a.b (op)=c" operation. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TryBinaryOperationOnMember(BinaryOperationOnMemberBinder binder, object value, out object result) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of + /// performing an operation on index "a[i,j,k] (op)= c" operation. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TryBinaryOperationOnIndex(BinaryOperationOnIndexBinder binder, object[] indexes, object value, out object result) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of + /// performing an operation on member "a.b (op)" operation. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TryUnaryOperationOnMember(UnaryOperationOnMemberBinder binder, out object result) { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class provides the non-Meta implementation of + /// performing an operation on index "a[i,j,k] (op)" operation. + /// + /// When not overridden the call site requesting the binder determines the behavior. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] + public virtual bool TryUnaryOperationOnIndex(UnaryOperationOnIndexBinder binder, object[] indexes, out object result) { + throw new NotSupportedException(); + } + + #endregion + + #region MetaDynamic + + private sealed class MetaDynamic : MetaObject { + + internal MetaDynamic(Expression expression, DynamicObject value) + : base(expression, Restrictions.Empty, value) { + } + + public override MetaObject BindGetMember(GetMemberBinder binder) { + if (IsOverridden("TryGetMember")) { + return CallMethodWithResult("TryGetMember", binder, NoArgs, (e) => binder.FallbackGetMember(this, e)); + } + + return base.BindGetMember(binder); + } + + public override MetaObject BindSetMember(SetMemberBinder binder, MetaObject value) { + if (IsOverridden("TrySetMember")) { + return CallMethodReturnLast("TrySetMember", binder, GetArgs(value), (e) => binder.FallbackSetMember(this, value, e)); + } + + return base.BindSetMember(binder, value); + } + + public override MetaObject BindDeleteMember(DeleteMemberBinder binder) { + if (IsOverridden("TryDeleteMember")) { + return CallMethodNoResult("TryDeleteMember", binder, NoArgs, (e) => binder.FallbackDeleteMember(this, e)); + } + + return base.BindDeleteMember(binder); + } + + public override MetaObject BindConvert(ConvertBinder binder) { + if (IsOverridden("TryConvert")) { + return CallMethodWithResult("TryConvert", binder, NoArgs, (e) => binder.FallbackConvert(this, e)); + } + + return base.BindConvert(binder); + } + + public override MetaObject BindInvokeMember(InvokeMemberBinder binder, MetaObject[] args) { + if (IsOverridden("TryInvokeMember")) { + return CallMethodWithResult("TryInvokeMember", binder, GetArgArray(args), (e) => binder.FallbackInvokeMember(this, args, e)); + } + + return base.BindInvokeMember(binder, args); + } + + + public override MetaObject BindCreateInstance(CreateInstanceBinder binder, MetaObject[] args) { + if (IsOverridden("TryCreateInstance")) { + return CallMethodWithResult("TryCreateInstance", binder, GetArgArray(args), (e) => binder.FallbackCreateInstance(this, args, e)); + } + + return base.BindCreateInstance(binder, args); + } + + public override MetaObject BindInvoke(InvokeBinder binder, MetaObject[] args) { + if (IsOverridden("TryInvoke")) { + return CallMethodWithResult("TryInvoke", binder, GetArgArray(args), (e) => binder.FallbackInvoke(this, args, e)); + } + + return base.BindInvoke(binder, args); + } + + public override MetaObject BindBinaryOperation(BinaryOperationBinder binder, MetaObject arg) { + if (IsOverridden("TryBinaryOperation")) { + return CallMethodWithResult("TryBinaryOperation", binder, GetArgs(arg), (e) => binder.FallbackBinaryOperation(this, arg, e)); + } + + return base.BindBinaryOperation(binder, arg); + } + + public override MetaObject BindUnaryOperation(UnaryOperationBinder binder) { + if (IsOverridden("TryUnaryOperation")) { + return CallMethodWithResult("TryUnaryOperation", binder, NoArgs, (e) => binder.FallbackUnaryOperation(this, e)); + } + + return base.BindUnaryOperation(binder); + } + + public override MetaObject BindGetIndex(GetIndexBinder binder, MetaObject[] indexes) { + if (IsOverridden("TryGetIndex")) { + return CallMethodWithResult("TryGetIndex", binder, GetArgArray(indexes), (e) => binder.FallbackGetIndex(this, indexes, e)); + } + + return base.BindGetIndex(binder, indexes); + } + + public override MetaObject BindSetIndex(SetIndexBinder binder, MetaObject[] indexes, MetaObject value) { + if (IsOverridden("TrySetIndex")) { + return CallMethodReturnLast("TrySetIndex", binder, GetArgArray(indexes, value), (e) => binder.FallbackSetIndex(this, indexes, value, e)); + } + + return base.BindSetIndex(binder, indexes, value); + } + + public override MetaObject BindDeleteIndex(DeleteIndexBinder binder, MetaObject[] indexes) { + if (IsOverridden("TryDeleteIndex")) { + return CallMethodNoResult("TryDeleteIndex", binder, GetArgArray(indexes), (e) => binder.FallbackDeleteIndex(this, indexes, e)); + } + + return base.BindDeleteIndex(binder, indexes); + } + + public override MetaObject BindBinaryOperationOnMember(BinaryOperationOnMemberBinder binder, MetaObject value) { + if (IsOverridden("TryBinaryOperationOnMember")) { + return CallMethodWithResult("TryBinaryOperationOnMember", binder, GetArgs(value), (e) => binder.FallbackBinaryOperationOnMember(this, value, e)); + } + + return base.BindBinaryOperationOnMember(binder, value); + } + + public override MetaObject BindBinaryOperationOnIndex(BinaryOperationOnIndexBinder binder, MetaObject[] indexes, MetaObject value) { + if (IsOverridden("TryBinaryOperationOnIndex")) { + return CallMethodWithResult("TryBinaryOperationOnIndex", binder, GetArgArray(indexes, value), (e) => binder.FallbackBinaryOperationOnIndex(this, indexes, value, e)); + } + + return base.BindBinaryOperationOnIndex(binder, indexes, value); + } + + + public override MetaObject BindUnaryOperationOnMember(UnaryOperationOnMemberBinder binder) { + if (IsOverridden("TryUnaryOperationOnMember")) { + return CallMethodWithResult("TryUnaryOperationOnMember", binder, NoArgs, (e) => binder.FallbackUnaryOperationOnMember(this, e)); + } + + return base.BindUnaryOperationOnMember(binder); + } + + public override MetaObject BindUnaryOperationOnIndex(UnaryOperationOnIndexBinder binder, MetaObject[] indexes) { + if (IsOverridden("TryUnaryOperationOnIndex")) { + return CallMethodWithResult("TryUnaryOperationOnIndex", binder, GetArgArray(indexes), (e) => binder.FallbackUnaryOperationOnIndex(this, indexes, e)); + } + + return base.BindUnaryOperationOnIndex(binder, indexes); + } + + private delegate MetaObject Fallback(MetaObject errorSuggestion); + + private readonly static Expression[] NoArgs = EmptyArray.Instance; + + private static Expression[] GetArgs(params MetaObject[] args) { + Expression[] paramArgs = MetaObject.GetExpressions(args); + + for (int i = 0; i < paramArgs.Length; i++) { + paramArgs[i] = Helpers.Convert(args[i].Expression, typeof(object)); + } + + return paramArgs; + } + + private static Expression[] GetArgArray(MetaObject[] args) { + return new[] { Expression.NewArrayInit(typeof(object), GetArgs(args)) }; + } + + private static Expression[] GetArgArray(MetaObject[] args, MetaObject value) { + return new[] { + Expression.NewArrayInit(typeof(object), GetArgs(args)), + Helpers.Convert(value.Expression, typeof(object)) + }; + } + + private static ConstantExpression Constant(MetaObjectBinder binder) { + Type t = binder.GetType(); + while (!t.IsVisible) { + t = t.BaseType; + } + return Expression.Constant(binder, t); + } + + /// + /// Helper method for generating a MetaObject which calls a + /// specific method on Dynamic that returns a result + /// + private MetaObject CallMethodWithResult(string methodName, MetaObjectBinder binder, Expression[] args, Fallback fallback) { + // + // First, call fallback to do default binding + // This produces either an error or a call to a .NET member + // + MetaObject fallbackResult = fallback(null); + + // + // Build a new expression like: + // { + // object result; + // TryGetMember(payload, out result) ? result : fallbackResult + // } + // + var result = Expression.Parameter(typeof(object), null); + + var callArgs = new Expression[args.Length + 2]; + Array.Copy(args, 0, callArgs, 1, args.Length); + callArgs[0] = Constant(binder); + callArgs[callArgs.Length - 1] = result; + + var callDynamic = new MetaObject( + Expression.Block( + new[] { result }, + Expression.Condition( + Expression.Call( + GetLimitedSelf(), + typeof(DynamicObject).GetMethod(methodName), + callArgs + ), + result, + Expression.Convert(fallbackResult.Expression, typeof(object)) + ) + ), + GetRestrictions().Merge(fallbackResult.Restrictions) + ); + + // + // Now, call fallback again using our new MO as the error + // When we do this, one of two things can happen: + // 1. Binding will succeed, and it will ignore our call to + // the dynamic method, OR + // 2. Binding will fail, and it will use the MO we created + // above. + // + return fallback(callDynamic); + } + + + /// + /// Helper method for generating a MetaObject which calls a + /// specific method on Dynamic, but uses one of the arguments for + /// the result. + /// + private MetaObject CallMethodReturnLast(string methodName, MetaObjectBinder binder, Expression[] args, Fallback fallback) { + // + // First, call fallback to do default binding + // This produces either an error or a call to a .NET member + // + MetaObject fallbackResult = fallback(null); + + // + // Build a new expression like: + // { + // object result; + // TrySetMember(payload, result = value) ? result : fallbackResult + // } + // + + var result = Expression.Parameter(typeof(object), null); + var callArgs = args.AddFirst(Constant(binder)); + callArgs[args.Length] = Expression.Assign(result, callArgs[args.Length]); + + var callDynamic = new MetaObject( + Expression.Block( + new[] { result }, + Expression.Condition( + Expression.Call( + GetLimitedSelf(), + typeof(DynamicObject).GetMethod(methodName), + callArgs + ), + result, + Expression.Convert(fallbackResult.Expression, typeof(object)) + ) + ), + GetRestrictions().Merge(fallbackResult.Restrictions) + ); + + // + // Now, call fallback again using our new MO as the error + // When we do this, one of two things can happen: + // 1. Binding will succeed, and it will ignore our call to + // the dynamic method, OR + // 2. Binding will fail, and it will use the MO we created + // above. + // + return fallback(callDynamic); + } + + + /// + /// Helper method for generating a MetaObject which calls a + /// specific method on Dynamic, but uses one of the arguments for + /// the result. + /// + private MetaObject CallMethodNoResult(string methodName, MetaObjectBinder binder, Expression[] args, Fallback fallback) { + // + // First, call fallback to do default binding + // This produces either an error or a call to a .NET member + // + MetaObject fallbackResult = fallback(null); + + // + // Build a new expression like: + // TryDeleteMember(payload) ? null : fallbackResult + // + var callDynamic = new MetaObject( + Expression.Condition( + Expression.Call( + GetLimitedSelf(), + typeof(DynamicObject).GetMethod(methodName), + args.AddFirst(Constant(binder)) + ), + Expression.Constant(null), + Expression.Convert(fallbackResult.Expression, typeof(object)) + ), + GetRestrictions().Merge(fallbackResult.Restrictions) + ); + + // + // Now, call fallback again using our new MO as the error + // When we do this, one of two things can happen: + // 1. Binding will succeed, and it will ignore our call to + // the dynamic method, OR + // 2. Binding will fail, and it will use the MO we created + // above. + // + return fallback(callDynamic); + } + + /// + /// Checks if the derived type has overridden the specified method. If there is no + /// implementation for the method provided then Dynamic falls back to the base class + /// behavior which lets the call site determine how the binder is performed. + /// + private bool IsOverridden(string method) { + var methods = Value.GetType().GetMember(method, MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance); + + foreach (MethodInfo mi in methods) { + if (mi.DeclaringType != typeof(DynamicObject) && mi.GetBaseDefinition().DeclaringType == typeof(DynamicObject)) { + return true; + } + } + + return false; + } + + /// + /// Returns a Restrictions object which includes our current restrictions merged + /// with a restriction limiting our type + /// + private Restrictions GetRestrictions() { + Debug.Assert(Restrictions == Restrictions.Empty, "We don't merge, restrictions are always empty"); + + return Restrictions.GetTypeRestriction(Expression, LimitType); + } + + /// + /// Returns our Expression converted to our known LimitType + /// + private Expression GetLimitedSelf() { + return Helpers.Convert( + Expression, + LimitType + ); + } + + private new DynamicObject Value { + get { + return (DynamicObject)base.Value; + } + } + } + + #endregion + + #region IDynamicObject Members + + /// + /// The provided MetaObject will dispatch to the Dynamic virtual methods. + /// The object can be encapsulated inside of another MetaObject to + /// provide custom behavior for individual actions. + /// + MetaObject IDynamicObject.GetMetaObject(Expression parameter) { + return new MetaDynamic(parameter, this); + } + + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/EmptyRuleSet.cs b/ndp/fx/src/core/microsoft/scripting/Actions/EmptyRuleSet.cs new file mode 100644 index 0000000000..1d19aadfe0 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/EmptyRuleSet.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Runtime.CompilerServices; + +namespace System.Dynamic.Binders { + internal sealed class EmptyRuleSet : RuleSet where T : class { + internal static readonly RuleSet FixedInstance = new EmptyRuleSet(); + + private EmptyRuleSet() { + } + + internal override RuleSet AddRule(CallSiteRule newRule) { + return this; + } + + internal override CallSiteRule[] GetRules() { + // TODO: could return an empty array, which would save the null + // check in UpdateAndExecute + return null; + } + + internal override T GetTarget() { + // Return null so CallSiteOps.SetPolymorphicTarget won't update the target + return null; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/ExpandoClass.cs b/ndp/fx/src/core/microsoft/scripting/Actions/ExpandoClass.cs new file mode 100644 index 0000000000..9ead5a7705 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/ExpandoClass.cs @@ -0,0 +1,175 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Dynamic.Utils; +using System.Text; + +namespace System.Dynamic.Binders { + /// + /// Represents a dynamically assigned class. Expando objects which share the same + /// members will share the same class. Classes are dynamically assigned as the + /// expando object gains members. + /// + internal class ExpandoClass { + private readonly string[] _keys; // list of names associated with each element in the data array, sorted + private readonly int _hashCode; // pre-calculated hash code of all the keys the class contains + private Dictionary> _transitions; // cached transitions + + private const int EmptyHashCode = 6551; // hash code of the empty ExpandoClass. + + internal static ExpandoClass Empty = new ExpandoClass(); // The empty Expando class - all Expando objects start off w/ this class. + + /// + /// Constructs the empty ExpandoClass. This is the class used when an + /// empty Expando object is initially constructed. + /// + internal ExpandoClass() { + _hashCode = EmptyHashCode; + _keys = new string[0]; + } + + /// + /// Constructs a new ExpandoClass that can hold onto the specified keys. The + /// keys must be sorted ordinally. The hash code must be precalculated for + /// the keys. + /// + internal ExpandoClass(string[] keys, int hashCode) { + _hashCode = hashCode; + _keys = keys; + } + + /// + /// Finds or creates a new ExpandoClass given the existing set of keys + /// in this ExpandoClass plus the new key to be added. + /// + internal ExpandoClass FindNewClass(string newKey, bool ignoreCase) { + // just XOR the newKey hash code + int hashCode = _hashCode ^ newKey.GetHashCode(); + + lock (this) { + List infos = GetTransitionList(hashCode); + + for(int i = 0; i + /// Gets a new object array for storing the data that matches this + /// ExpandoClass given the old ExpandoClass and the instances associated + /// data array. + /// + internal object[] GetNewKeys(object[] oldData) { + if (oldData.Length >= _keys.Length) { + // we have extra space in our buffer, just initialize it to Uninitialized. + oldData[_keys.Length - 1] = ExpandoObject.Uninitialized; + return oldData; + } + + // we've grown too much - we need a new object array + object[] res = new object[GetAlignedSize(_keys.Length)]; + Array.Copy(oldData, res, oldData.Length); + res[oldData.Length] = ExpandoObject.Uninitialized; + return res; + } + + private static int GetAlignedSize(int len) { + // the alignment of the array for storage of values (must be a power of two) + const int DataArrayAlignment = 8; + + // round up and then mask off lower bits + return (len + (DataArrayAlignment - 1)) & (~(DataArrayAlignment - 1)); + } + + /// + /// Gets the lists of transitions that are valid from this ExpandoClass + /// to an ExpandoClass whos keys hash to the apporopriate hash code. + /// + private List GetTransitionList(int hashCode) { + if (_transitions == null) { + _transitions = new Dictionary>(); + } + + List infos; + if (!_transitions.TryGetValue(hashCode, out infos)) { + _transitions[hashCode] = infos = new List(); + } + + return infos; + } + + /// + /// Gets the index at which the value should be stored for the specified name. + /// + internal int GetValueIndex(string name, bool caseInsensitive) { + for (int i = 0; i < _keys.Length; i++) { + if (string.Equals( + _keys[i], + name, + GetStringComparison(caseInsensitive))) { + return i; + } + } + + return -1; + } + + /// + /// Gets the name of the specified index. Used for getting the name to + /// create a new expando class when all we have is the class and old index. + /// + internal string GetIndexName(int index) { + return _keys[index]; + } + + /// + /// Gets the names of the keys that can be stored in the Expando class. The + /// list is sorted ordinally. + /// + internal string[] Keys { + get { + return _keys; + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/ExpandoObject.cs b/ndp/fx/src/core/microsoft/scripting/Actions/ExpandoObject.cs new file mode 100644 index 0000000000..e36391d596 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/ExpandoObject.cs @@ -0,0 +1,476 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Dynamic.Binders; +using System.Dynamic.Utils; + +namespace System.Dynamic.Binders { + /// + /// Simple type which implements IDynamicObject to support getting/setting/deleting members + /// at runtime. + /// + public sealed class ExpandoObject : IDynamicObject { + private ExpandoData _data; // the data currently being held by the Expando object + + internal static object Uninitialized = new object(); // A marker object used to identify that a value is uninitialized. + + /// + /// Creates a new Expando object with no members. + /// + public ExpandoObject() { + _data = ExpandoData.Empty; + } + + #region Get/Set/Delete Helpers + + /// + /// Gets the data stored for the specified class at the specified index. If the + /// class has changed a full lookup for the slot will be performed and the correct + /// value will be retrieved. + /// + internal object GetValue(ExpandoClass klass, int index, bool caseInsensitive) { + Debug.Assert(index != -1); + + // read the data now. The data is immutable so we get a consistent view. + // If there's a concurrent writer they will replace data and it just appears + // that we won the race + ExpandoData data = _data; + object res = Uninitialized; + if (data.Class != klass) { + // the class has changed, we need to get the correct index and return + // the value there. + index = data.Class.GetValueIndex(klass.GetIndexName(index), caseInsensitive); + } + + // index is now known to be correct + res = data.Data[index]; + + if (res == Uninitialized) { + throw new MissingMemberException(klass.GetIndexName(index)); + } + + return res; + } + + /// + /// Sets the data for the specified class at the specified index. If the class has + /// changed then a full look for the slot will be performed. If the new class does + /// not have the provided slot then the Expando's class will change. + /// + internal void SetValue(ExpandoClass klass, int index, bool caseInsensitive, object value) { + Debug.Assert(index != -1); + + lock (this) { + ExpandoData data = _data; + + if (data.Class != klass) { + // the class has changed, we need to get the correct index and set + // the value there. If we don't have the value then we need to + // promote the class - that should only happen when we have multiple + // concurrent writers. + string name = klass.GetIndexName(index); + index = data.Class.GetValueIndex(name, caseInsensitive); + if (index == -1) { + ExpandoClass newClass = data.Class.FindNewClass(name, caseInsensitive); + + data = PromoteClassWorker(data.Class, newClass); + index = data.Class.GetValueIndex(name, caseInsensitive); + + Debug.Assert(index != -1); + } + } + + data.Data[index] = value; + } + } + + /// + /// Gets the data stored for the specified class at the specified index. If the + /// class has changed a full lookup for the slot will be performed and the correct + /// value will be retrieved. + /// + internal bool DeleteValue(ExpandoClass klass, int index, bool caseInsensitive) { + Debug.Assert(index != -1); + + lock (this) { + ExpandoData data = _data; + + if (data.Class != klass) { + // the class has changed, we need to get the correct index. If there is + // no associated index we simply can't have the value and we return + // false. + index = data.Class.GetValueIndex(klass.GetIndexName(index), caseInsensitive); + if (index == -1) { + return false; + } + } + + object curValue = data.Data[index]; + data.Data[index] = Uninitialized; + + return curValue != Uninitialized; + } + } + + /// + /// Exposes the ExpandoClass which we've associated with this + /// Expando object. Used for type checks in rules. + /// + internal ExpandoClass Class { + get { + return _data.Class; + } + } + + /// + /// Promotes the class from the old type to the new type and returns the new + /// ExpandoData object. + /// + private ExpandoData PromoteClassWorker(ExpandoClass oldClass, ExpandoClass newClass) { + Debug.Assert(oldClass != newClass); + + lock (this) { + if (_data.Class == oldClass) { + _data = new ExpandoData(newClass, newClass.GetNewKeys(_data.Data)); + } + return _data; + } + } + + /// + /// Internal helper to promote a class. Called from our RuntimeOps helper. This + /// version simply doesn't expose the ExpandoData object which is a private + /// data structure. + /// + internal void PromoteClass(ExpandoClass oldClass, ExpandoClass newClass) { + PromoteClassWorker(oldClass, newClass); + } + + #endregion + + #region IDynamicObject Members + + MetaObject IDynamicObject.GetMetaObject(Expression parameter) { + return new MetaExpando(parameter, this); + } + + #endregion + + #region MetaExpando + + private class MetaExpando : MetaObject { + public MetaExpando(Expression expression, ExpandoObject value) + : base(expression, Restrictions.Empty, value) { + } + + public override MetaObject BindGetMember(GetMemberBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + + ExpandoClass klass = Value.Class; + + int index = klass.GetValueIndex(binder.Name, binder.IgnoreCase); + string methodName = binder.IgnoreCase ? "ExpandoGetValueIgnoreCase" : "ExpandoGetValue"; + + Expression target; + if (index == -1) { + // the key does not exist, report a MissingMemberException + target = Expression.Convert( + Expression.Throw( + Expression.New( + typeof(MissingMemberException).GetConstructor(new Type[] { typeof(string) }), + Expression.Constant(binder.Name) + ) + ), + typeof(object) + ); + } else { + target = Expression.Call( + typeof(RuntimeOps).GetMethod(methodName), + GetLimitedSelf(), + Expression.Constant(klass), + Expression.Constant(index) + ); + } + + // add the dynamic test for the target + return new MetaObject( + AddDynamicTestAndDefer( + binder, + new MetaObject[] { this }, + klass, + null, + target + ), + GetRestrictions() + ); + } + + public override MetaObject BindSetMember(SetMemberBinder binder, MetaObject value) { + ContractUtils.RequiresNotNull(binder, "binder"); + ContractUtils.RequiresNotNull(value, "value"); + + ExpandoClass klass; + int index; + + ExpandoClass originalClass = GetClassEnsureIndex(binder.Name, binder.IgnoreCase, out klass, out index); + + string methodName = binder.IgnoreCase ? "ExpandoSetValueIgnoreCase" : "ExpandoSetValue"; + + return new MetaObject( + AddDynamicTestAndDefer( + binder, + new MetaObject[] { this, value }, + klass, + originalClass, + Expression.Convert( + Expression.Call( + typeof(RuntimeOps).GetMethod(methodName), + GetLimitedSelf(), + Expression.Constant(klass), + Expression.Constant(index), + Helpers.Convert( + value.Expression, + typeof(object) + ) + ), + typeof(object) + ) + ), + GetRestrictions() + ); + } + + public override MetaObject BindDeleteMember(DeleteMemberBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + + ExpandoClass klass; + int index; + + ExpandoClass originalClass = GetClassEnsureIndex(binder.Name, binder.IgnoreCase, out klass, out index); + + string methodName = binder.IgnoreCase ? "ExpandoDeleteValueIgnoreCase" : "ExpandoDeleteValue"; + + return new MetaObject( + AddDynamicTestAndDefer( + binder, + new MetaObject[] { this }, + klass, + originalClass, + Expression.Convert( + Expression.Call( + typeof(RuntimeOps).GetMethod(methodName), + GetLimitedSelf(), + Expression.Constant(klass), + Expression.Constant(index) + ), + typeof(object) + ) + ), + GetRestrictions() + ); + } + + public override IEnumerable GetDynamicMemberNames() { + return new ReadOnlyCollection(Value._data.Class.Keys); + } + + /// + /// Adds a dynamic test which checks if the version has changed. The test is only necessary for + /// performance as the methods will do the correct thing if called with an incorrect version. + /// + private Expression AddDynamicTestAndDefer(MetaObjectBinder binder, MetaObject[] args, ExpandoClass klass, ExpandoClass originalClass, Expression ifTestSucceeds) { + if (originalClass != null) { + // we are accessing a member which has not yet been defined on this class. + // We force a class promotion after the type check. If the class changes the + // promotion will fail and the set/delete will do a full lookup using the new + // class to discover the name. + Debug.Assert(originalClass != klass); + + ifTestSucceeds = Expression.Block( + Expression.Call( + null, + typeof(RuntimeOps).GetMethod("ExpandoPromoteClass"), + GetLimitedSelf(), + Expression.Constant(originalClass), + Expression.Constant(klass) + ), + ifTestSucceeds + ); + } + + return Expression.Condition( + Expression.Call( + null, + typeof(RuntimeOps).GetMethod("ExpandoCheckVersion"), + GetLimitedSelf(), + Expression.Constant(originalClass ?? klass) + ), + ifTestSucceeds, + binder.Defer(args).Expression + ); + } + + /// + /// Gets the class and the index associated with the given name. Does not update the expando object. Instead + /// this returns both the original and desired new class. A rule is created which includes the test for the + /// original class, the promotion to the new class, and the set/delete based on the class post-promotion. + /// + private ExpandoClass GetClassEnsureIndex(string name, bool ignoreCase, out ExpandoClass klass, out int index) { + ExpandoClass originalClass = Value.Class; + + index = originalClass.GetValueIndex(name, ignoreCase); + if (index == -1) { + // go ahead and find a new class now... + ExpandoClass newClass = originalClass.FindNewClass(name, ignoreCase); + + klass = newClass; + index = newClass.GetValueIndex(name, ignoreCase); + + Debug.Assert(index != -1); + return originalClass; + } else { + klass = originalClass; + return null; + } + } + + /// + /// Returns our Expression converted to our known LimitType + /// + private Expression GetLimitedSelf() { + return Helpers.Convert( + Expression, + LimitType + ); + } + + /// + /// Returns a Restrictions object which includes our current restrictions merged + /// with a restriction limiting our type + /// + private Restrictions GetRestrictions() { + Debug.Assert(Restrictions == Restrictions.Empty, "We don't merge, restrictions are always empty"); + + return Restrictions.GetTypeRestriction(Expression, LimitType); + } + + public new ExpandoObject Value { + get { + return (ExpandoObject)base.Value; + } + } + } + + #endregion + + #region ExpandoData + + /// + /// Stores the class and the data associated with the class as one atomic + /// pair. This enables us to do a class check in a thread safe manner w/o + /// requiring locks. + /// + private class ExpandoData { + internal static ExpandoData Empty = new ExpandoData(); + + /// + /// the dynamically assigned class associated with the Expando object + /// + internal readonly ExpandoClass Class; + /// + /// data stored in the expando object, key names are stored in the class. + /// + /// Expando._data must be locked when mutating the value. Otherwise a copy of it + /// could be made and lose values. + /// + internal readonly object[] Data; + + /// + /// Constructs an empty ExpandoData object with the empty class and no data. + /// + private ExpandoData() { + Class = ExpandoClass.Empty; + Data = new object[0]; + } + + /// + /// Constructs a new ExpandoData object with the specified class and data. + /// + internal ExpandoData(ExpandoClass klass, object[] data) { + Class = klass; + Data = data; + } + } + + #endregion + } +} + +namespace System.Runtime.CompilerServices { + public static partial class RuntimeOps { + [Obsolete("used by generated code", true)] + public static object ExpandoGetValue(ExpandoObject expando, object indexClass, int index) { + ContractUtils.RequiresNotNull(expando, "expando"); + return expando.GetValue((ExpandoClass)indexClass, index, false); + } + + [Obsolete("used by generated code", true)] + public static object ExpandoGetValueIgnoreCase(ExpandoObject expando, object indexClass, int index) { + ContractUtils.RequiresNotNull(expando, "expando"); + return expando.GetValue((ExpandoClass)indexClass, index, true); + } + + [Obsolete("used by generated code", true)] + public static void ExpandoSetValue(ExpandoObject expando, object indexClass, int index, object value) { + ContractUtils.RequiresNotNull(expando, "expando"); + expando.SetValue((ExpandoClass)indexClass, index, false, value); + } + + [Obsolete("used by generated code", true)] + public static void ExpandoSetValueIgnoreCase(ExpandoObject expando, object indexClass, int index, object value) { + ContractUtils.RequiresNotNull(expando, "expando"); + expando.SetValue((ExpandoClass)indexClass, index, true, value); + } + + [Obsolete("used by generated code", true)] + public static bool ExpandoDeleteValue(ExpandoObject expando, object indexClass, int index) { + ContractUtils.RequiresNotNull(expando, "expando"); + return expando.DeleteValue((ExpandoClass)indexClass, index, false); + } + + [Obsolete("used by generated code", true)] + public static bool ExpandoDeleteValueIgnoreCase(ExpandoObject expando, object indexClass, int index) { + ContractUtils.RequiresNotNull(expando, "expando"); + return expando.DeleteValue((ExpandoClass)indexClass, index, true); + } + + [Obsolete("used by generated code", true)] + public static bool ExpandoCheckVersion(ExpandoObject expando, object version) { + ContractUtils.RequiresNotNull(expando, "expando"); + return expando.Class == version; + } + + public static void ExpandoPromoteClass(ExpandoObject expando, object oldClass, object newClass) { + ContractUtils.RequiresNotNull(expando, "expando"); + expando.PromoteClass((ExpandoClass)oldClass, (ExpandoClass)newClass); + } + } +} + diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/GetIndexBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/GetIndexBinder.cs new file mode 100644 index 0000000000..d3594663ec --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/GetIndexBinder.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Dynamic.Utils; +using Microsoft.Contracts; + +namespace System.Dynamic.Binders { + public abstract class GetIndexBinder : MetaObjectBinder { + private readonly ReadOnlyCollection _arguments; + + protected GetIndexBinder(params ArgumentInfo[] arguments) + : this((IEnumerable)arguments) { + } + + protected GetIndexBinder(IEnumerable arguments) { + _arguments = arguments.ToReadOnly(); + } + + public ReadOnlyCollection Arguments { + get { return _arguments; } + } + + [Confined] + public override bool Equals(object obj) { + GetIndexBinder ia = obj as GetIndexBinder; + return ia != null && ia._arguments.ListEquals(_arguments); + } + + [Confined] + public override int GetHashCode() { + return GetIndexBinderHash ^ _arguments.ListHashCode(); + } + + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNullItems(args, "args"); + + return target.BindGetIndex(this, args); + } + + public MetaObject FallbackGetIndex(MetaObject target, MetaObject[] indexes) { + return FallbackGetIndex(target, indexes, null); + } + + public abstract MetaObject FallbackGetIndex(MetaObject target, MetaObject[] indexes, MetaObject errorSuggestion); + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/GetMemberBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/GetMemberBinder.cs new file mode 100644 index 0000000000..985afcb919 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/GetMemberBinder.cs @@ -0,0 +1,67 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using Microsoft.Contracts; +using System.Dynamic.Utils; + +namespace System.Dynamic.Binders { + public abstract class GetMemberBinder : MetaObjectBinder { + private readonly string _name; + private readonly bool _ignoreCase; + + protected GetMemberBinder(string name, bool ignoreCase) { + ContractUtils.RequiresNotNull(name, "name"); + + _name = name; + _ignoreCase = ignoreCase; + } + + public string Name { + get { + return _name; + } + } + + public bool IgnoreCase { + get { + return _ignoreCase; + } + } + + public MetaObject FallbackGetMember(MetaObject target) { + return FallbackGetMember(target, null); + } + + public abstract MetaObject FallbackGetMember(MetaObject target, MetaObject errorSuggestion); + + public sealed override MetaObject Bind(MetaObject target, params MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.Requires(args.Length == 0); + + return target.BindGetMember(this); + } + + [Confined] + public override bool Equals(object obj) { + GetMemberBinder gma = obj as GetMemberBinder; + return gma != null && gma._name == _name && gma._ignoreCase == _ignoreCase; + } + + [Confined] + public override int GetHashCode() { + return GetMemberBinderHash ^ _name.GetHashCode() ^ (_ignoreCase ? unchecked((int)0x80000000) : 0); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/IDynamicObject.cs b/ndp/fx/src/core/microsoft/scripting/Actions/IDynamicObject.cs new file mode 100644 index 0000000000..f8951c3714 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/IDynamicObject.cs @@ -0,0 +1,22 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; + +namespace System.Dynamic.Binders { + public interface IDynamicObject { + MetaObject GetMetaObject(Expression parameter); + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/InvokeBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/InvokeBinder.cs new file mode 100644 index 0000000000..45c41b150f --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/InvokeBinder.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Dynamic.Utils; +using Microsoft.Contracts; + +namespace System.Dynamic.Binders { + public abstract class InvokeBinder : MetaObjectBinder { + private readonly ReadOnlyCollection _arguments; + + protected InvokeBinder(params ArgumentInfo[] arguments) + : this((IEnumerable)arguments) { + } + + protected InvokeBinder(IEnumerable arguments) { + _arguments = arguments.ToReadOnly(); + } + + public ReadOnlyCollection Arguments { + get { return _arguments; } + } + + public MetaObject FallbackInvoke(MetaObject target, MetaObject[] args) { + return FallbackInvoke(target, args, null); + } + + public abstract MetaObject FallbackInvoke(MetaObject target, MetaObject[] args, MetaObject errorSuggestion); + + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNullItems(args, "args"); + + return target.BindInvoke(this, args); + } + + [Confined] + public override bool Equals(object obj) { + InvokeBinder ia = obj as InvokeBinder; + return ia != null && ia._arguments.ListEquals(_arguments); + } + + [Confined] + public override int GetHashCode() { + return InvokeBinderHash ^ _arguments.ListHashCode(); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/InvokeMemberBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/InvokeMemberBinder.cs new file mode 100644 index 0000000000..85d4066519 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/InvokeMemberBinder.cs @@ -0,0 +1,81 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Dynamic.Utils; +using Microsoft.Contracts; + +namespace System.Dynamic.Binders { + public abstract class InvokeMemberBinder : MetaObjectBinder { + private readonly string _name; + private readonly bool _ignoreCase; + private readonly ReadOnlyCollection _arguments; + + protected InvokeMemberBinder(string name, bool ignoreCase, IEnumerable arguments) { + _name = name; + _ignoreCase = ignoreCase; + _arguments = arguments.ToReadOnly(); + } + + protected InvokeMemberBinder(string name, bool ignoreCase, params ArgumentInfo[] arguments) + : this(name, ignoreCase, (IEnumerable)arguments) { + } + + public string Name { + get { + return _name; + } + } + + public bool IgnoreCase { + get { + return _ignoreCase; + } + } + + public ReadOnlyCollection Arguments { + get { + return _arguments; + } + } + + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNullItems(args, "args"); + + return target.BindInvokeMember(this, args); + } + + public MetaObject FallbackInvokeMember(MetaObject target, MetaObject[] args) { + return FallbackInvokeMember(target, args, null); + } + + public abstract MetaObject FallbackInvokeMember(MetaObject target, MetaObject[] args, MetaObject errorSuggestion); + public abstract MetaObject FallbackInvoke(MetaObject target, MetaObject[] args, MetaObject errorSuggestion); + + [Confined] + public override bool Equals(object obj) { + InvokeMemberBinder ca = obj as InvokeMemberBinder; + return ca != null && ca._name == _name && ca._ignoreCase == _ignoreCase && ca._arguments.ListEquals(_arguments); + } + + [Confined] + public override int GetHashCode() { + return InvokeMemberBinderHash ^ _name.GetHashCode() ^ (_ignoreCase ? 0x8000000 : 0) ^ _arguments.ListHashCode(); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/MetaObject.cs b/ndp/fx/src/core/microsoft/scripting/Actions/MetaObject.cs new file mode 100644 index 0000000000..cf51ea3a6a --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/MetaObject.cs @@ -0,0 +1,302 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Dynamic.Utils; +using System.Linq.Expressions; +using System.Reflection; + +namespace System.Dynamic.Binders { + public class MetaObject { + private readonly Expression _expression; + private readonly Restrictions _restrictions; + private readonly object _value; + private readonly bool _hasValue; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")] + public static readonly MetaObject[] EmptyMetaObjects = new MetaObject[0]; + + public MetaObject(Expression expression, Restrictions restrictions) { + ContractUtils.RequiresNotNull(expression, "expression"); + ContractUtils.RequiresNotNull(restrictions, "restrictions"); + + _expression = expression; + _restrictions = restrictions; + } + + public MetaObject(Expression expression, Restrictions restrictions, object value) + : this(expression, restrictions) { + _value = value; + _hasValue = true; + } + + public Expression Expression { + get { + return _expression; + } + } + + public Restrictions Restrictions { + get { + return _restrictions; + } + } + + public object Value { + get { + return _value; + } + } + + public bool HasValue { + get { + return _hasValue; + } + } + + public Type RuntimeType { + get { + if (_hasValue) { + Type ct = Expression.Type; + // valuetype at compile tyme, type cannot change. + if (ct.IsValueType) { + return ct; + } + if (_value != null) { + return _value.GetType(); + } else { + return typeof(Null); + } + } else { + return null; + } + } + } + + public Type LimitType { + get { + return RuntimeType ?? Expression.Type; + } + } + + // TODO: do we want to keep this in its current form? + // It doesn't offer much value anymore + // (but it would be useful as a virtual property) + public bool IsDynamicObject { + get { + return _value is IDynamicObject; + } + } + + public bool IsByRef { + get { + ParameterExpression pe = _expression as ParameterExpression; + return pe != null && pe.IsByRef; + } + } + + public virtual MetaObject BindConvert(ConvertBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackConvert(this); + } + + public virtual MetaObject BindGetMember(GetMemberBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackGetMember(this); + } + + public virtual MetaObject BindSetMember(SetMemberBinder binder, MetaObject value) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackSetMember(this, value); + } + + public virtual MetaObject BindDeleteMember(DeleteMemberBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackDeleteMember(this); + } + + public virtual MetaObject BindGetIndex(GetIndexBinder binder, MetaObject[] indexes) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackGetIndex(this, indexes); + } + + public virtual MetaObject BindSetIndex(SetIndexBinder binder, MetaObject[] indexes, MetaObject value) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackSetIndex(this, indexes, value); + } + + public virtual MetaObject BindDeleteIndex(DeleteIndexBinder binder, MetaObject[] indexes) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackDeleteIndex(this, indexes); + } + + public virtual MetaObject BindInvokeMember(InvokeMemberBinder binder, MetaObject[] args) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackInvokeMember(this, args); + } + + public virtual MetaObject BindInvoke(InvokeBinder binder, MetaObject[] args) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackInvoke(this, args); + } + + public virtual MetaObject BindCreateInstance(CreateInstanceBinder binder, MetaObject[] args) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackCreateInstance(this, args); + } + + public virtual MetaObject BindUnaryOperation(UnaryOperationBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackUnaryOperation(this); + } + + public virtual MetaObject BindBinaryOperation(BinaryOperationBinder binder, MetaObject arg) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackBinaryOperation(this, arg); + } + + /// + /// Binds an operation a.b (op)= c + /// + /// Binder implementing the language semantics. + /// Meta Object representing the right-side argument. + /// MetaObject representing the result of the binding. + public virtual MetaObject BindBinaryOperationOnMember(BinaryOperationOnMemberBinder binder, MetaObject value) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackBinaryOperationOnMember(this, value); + } + + + /// + /// Binds an operation a[i,j,k] (op)= c + /// + /// Binder implementing the language semantics. + /// The array of MetaObjects representing the indexes for the index operation. + /// The MetaObject representing the right-hand value of the operation. + /// MetaObject representing the result of the binding. + public virtual MetaObject BindBinaryOperationOnIndex(BinaryOperationOnIndexBinder binder, MetaObject[] indexes, MetaObject value) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackBinaryOperationOnIndex(this, indexes, value); + } + + /// + /// Binds the unary operation performed on a result of index operation on the object. + /// + /// The binder implementing the language semantics. + /// The array of MetaObject representing the indexes for the index operation. + /// The MetaObject representing the result of the binding. + public virtual MetaObject BindUnaryOperationOnIndex(UnaryOperationOnIndexBinder binder, MetaObject[] indexes) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackUnaryOperationOnIndex(this, indexes); + } + + /// + /// Binds the unary operation performed on a result of get member operation on the object. + /// + /// The binder implementing the language semantics. + /// The MetaObject representing the result of the binding. + public virtual MetaObject BindUnaryOperationOnMember(UnaryOperationOnMemberBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + return binder.FallbackUnaryOperationOnMember(this); + } + + /// + /// Returns the enumeration of all dynamic member names. + /// + /// The list of dynamic members. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public virtual IEnumerable GetDynamicMemberNames() { + return EmptyArray.Instance; + } + + /// + /// Returns the enumeration of key-value pairs of all dynamic data members. Data members include members + /// such as properties, fields, but not necessarily methods. The key value pair includes the member name + /// and the value. + /// + /// The list of key-value pairs representing data member name and its value. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public virtual IEnumerable> GetDynamicDataMembers() { + return EmptyArray>.Instance; + } + + // Internal helpers + + internal static Type[] GetTypes(MetaObject[] objects) { + Type[] res = new Type[objects.Length]; + for (int i = 0; i < objects.Length; i++) { + res[i] = objects[i].RuntimeType ?? objects[i].Expression.Type; + } + + return res; + } + + public static Expression[] GetExpressions(MetaObject[] objects) { + ContractUtils.RequiresNotNull(objects, "objects"); + + Expression[] res = new Expression[objects.Length]; + for (int i = 0; i < objects.Length; i++) { + MetaObject mo = objects[i]; + ContractUtils.RequiresNotNull(mo, "objects"); + Expression expr = mo.Expression; + ContractUtils.RequiresNotNull(expr, "objects"); + res[i] = expr; + } + + return res; + } + + public static MetaObject ObjectToMetaObject(object argValue, Expression parameterExpression) { + IDynamicObject ido = argValue as IDynamicObject; + if (ido != null) { + return ido.GetMetaObject(parameterExpression); + } else { + return new MetaObject(parameterExpression, Restrictions.Empty, argValue); + } + } + + public static MetaObject CreateThrow(MetaObject target, MetaObject[] args, Type exception, params object[] exceptionArgs) { + return CreateThrow( + target, + args, + exception, + exceptionArgs != null ? exceptionArgs.Map((arg) => Expression.Constant(arg)) : null + ); + } + + public static MetaObject CreateThrow(MetaObject target, MetaObject[] args, Type exception, params Expression[] exceptionArgs) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNull(exception, "exception"); + + Type[] argTypes = exceptionArgs != null ? exceptionArgs.Map((arg) => arg.Type) : Type.EmptyTypes; + ConstructorInfo constructor = exception.GetConstructor(argTypes); + + if (constructor == null) { + throw new ArgumentException(Strings.TypeDoesNotHaveConstructorForTheSignature); + } + + return new MetaObject( + Expression.Throw( + Expression.New( + exception.GetConstructor(argTypes), + exceptionArgs + ) + ), + target.Restrictions.Merge(Restrictions.Combine(args)) + ); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/MetaObjectBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/MetaObjectBinder.cs new file mode 100644 index 0000000000..d76851cb42 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/MetaObjectBinder.cs @@ -0,0 +1,195 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Linq.Expressions.Compiler; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; + +namespace System.Dynamic.Binders { + public abstract class MetaObjectBinder : CallSiteBinder { + + #region Standard Binder Kinds + + internal const int OperationBinderHash = 0x4000000; + internal const int UnaryOperationBinderHash = 0x8000000; + internal const int BinaryOperationBinderHash = 0xc000000; + internal const int GetMemberBinderHash = 0x10000000; + internal const int SetMemberBinderHash = 0x14000000; + internal const int DeleteMemberBinderHash = 0x18000000; + internal const int GetIndexBinderHash = 0x1c000000; + internal const int SetIndexBinderHash = 0x20000000; + internal const int DeleteIndexBinderHash = 0x24000000; + internal const int InvokeMemberBinderHash = 0x28000000; + internal const int ConvertBinderHash = 0x2c000000; + internal const int CreateInstanceBinderHash = 0x30000000; + internal const int InvokeBinderHash = 0x34000000; + internal const int BinaryOperationOnMemberBinderHash = 0x38000000; + internal const int BinaryOperationOnIndexBinderHash = 0x3c000000; + internal const int UnaryOperationOnMemberBinderHash = 0x40000000; + internal const int UnaryOperationOnIndexBinderHash = 0x44000000; + + #endregion + + #region Public APIs + + public sealed override Expression Bind(object[] args, ReadOnlyCollection parameters, LabelTarget returnLabel) { + if (args.Length == 0) { + throw new InvalidOperationException(); + } + + MetaObject[] mos; + if (args.Length != 1) { + mos = new MetaObject[args.Length - 1]; + for (int i = 1; i < args.Length; i++) { + mos[i - 1] = MetaObject.ObjectToMetaObject(args[i], parameters[i]); + } + } else { + mos = MetaObject.EmptyMetaObjects; + } + + MetaObject binding = Bind( + MetaObject.ObjectToMetaObject(args[0], parameters[0]), + mos + ); + + if (binding == null) { + throw Error.BindingCannotBeNull(); + } + + return GetMetaObjectRule(binding, returnLabel); + } + + public abstract MetaObject Bind(MetaObject target, MetaObject[] args); + + public MetaObject Defer(MetaObject target, params MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + + if (args == null) { + return MakeDeferred( + target.Restrictions, + target + ); + } else { + return MakeDeferred( + target.Restrictions.Merge(Restrictions.Combine(args)), + args.AddFirst(target) + ); + } + } + + public MetaObject Defer(params MetaObject[] args) { + return MakeDeferred( + Restrictions.Combine(args), + args + ); + } + + private MetaObject MakeDeferred(Restrictions rs, params MetaObject[] args) { + var exprs = MetaObject.GetExpressions(args); + + // TODO: we should really be using the same delegate as the CallSite + Type delegateType = DelegateHelpers.MakeDeferredSiteDelegate(args, typeof(object)); + + // Because we know the arguments match the delegate type (we just created the argument types) + // we go directly to DynamicExpression.Make to avoid a bunch of unnecessary argument validation + return new MetaObject( + DynamicExpression.Make( + typeof(object), + delegateType, + this, + new ReadOnlyCollection(exprs) + ), + rs + ); + } + + #endregion + + private Expression AddReturn(Expression body, LabelTarget @return) { + switch (body.NodeType) { + case ExpressionType.Conditional: + ConditionalExpression conditional = (ConditionalExpression)body; + if (IsDeferExpression(conditional.IfTrue)) { + return Expression.Condition( + Expression.Not(conditional.Test), + Expression.Return(@return, Helpers.Convert(conditional.IfFalse, @return.Type)), + Expression.Empty() + ); + } else if (IsDeferExpression(conditional.IfFalse)) { + return Expression.Condition( + conditional.Test, + Expression.Return(@return, Helpers.Convert(conditional.IfTrue, @return.Type)), + Expression.Empty() + ); + } + return Expression.Condition( + conditional.Test, + AddReturn(conditional.IfTrue, @return), + AddReturn(conditional.IfFalse, @return) + ); + case ExpressionType.Throw: + return body; + case ExpressionType.Block: + // block could have a throw which we need to run through to avoid + // trying to convert it + BlockExpression block = (BlockExpression)body; + + int count = block.ExpressionCount; + Expression[] nodes = new Expression[count]; + + for (int i = 0; i < nodes.Length - 1; i++) { + nodes[i] = block.GetExpression(i); + } + nodes[nodes.Length - 1] = AddReturn(block.GetExpression(count - 1), @return); + + return Expression.Block(block.Variables, nodes); + default: + return Expression.Return(@return, Helpers.Convert(body, @return.Type)); + } + } + + private bool IsDeferExpression(Expression e) { + if (e.NodeType == ExpressionType.Dynamic) { + return ((DynamicExpression)e).Binder == this; + } + + if (e.NodeType == ExpressionType.Convert) { + return IsDeferExpression(((UnaryExpression)e).Operand); + } + + return false; + } + + private Expression GetMetaObjectRule(MetaObject binding, LabelTarget @return) { + Debug.Assert(binding != null); + + Expression body = AddReturn(binding.Expression, @return); + + if (binding.Restrictions != Restrictions.Empty) { + // add the test only if we have one + body = Expression.Condition( + binding.Restrictions.ToExpression(), + body, + Expression.Empty() + ); + } + + return body; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/Restrictions.cs b/ndp/fx/src/core/microsoft/scripting/Actions/Restrictions.cs new file mode 100644 index 0000000000..cadb2b436e --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/Restrictions.cs @@ -0,0 +1,237 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Dynamic.Utils; + +namespace System.Dynamic.Binders { + public sealed class Restrictions { + private class Restriction { + internal enum RestrictionKind { + Type, + Instance, + Custom + }; + + private readonly RestrictionKind _kind; + + // Simplification ... for now just one kind of restriction rather than hierarchy. + private readonly Expression _expression; + private readonly Type _type; + private readonly object _instance; // TODO: WeakRef ??? + + internal RestrictionKind Kind { + get { return _kind; } + } + + internal Expression Expression { + get { return _expression; } + } + + internal Type Type { + get { return _type; } + } + + internal object Instance { + get { return _instance; } + } + + internal Restriction(Expression parameter, Type type) { + _kind = RestrictionKind.Type; + _expression = parameter; + _type = type; + } + + internal Restriction(Expression parameter, object instance) { + _kind = RestrictionKind.Instance; + _expression = parameter; + _instance = instance; + } + + internal Restriction(Expression expression) { + _kind = RestrictionKind.Custom; + _expression = expression; + } + + public override bool Equals(object obj) { + Restriction other = obj as Restriction; + if (other == null) { + return false; + } + + if (other.Kind != Kind || + other.Expression != Expression) { + return false; + } + + switch (other.Kind) { + case RestrictionKind.Instance: + return other.Instance == Instance; + case RestrictionKind.Type: + return other.Type == Type; + default: + return false; + } + } + + public override int GetHashCode() { + // lots of collisions but we don't hash Restrictions ever + return (int)Kind ^ Expression.GetHashCode(); + } + } + + private readonly Restriction[] _restrictions; + + private Restrictions(params Restriction[] restrictions) { + _restrictions = restrictions; + } + + private bool IsEmpty { + get { + return _restrictions.Length == 0; + } + } + + public Restrictions Merge(Restrictions restrictions) { + if (restrictions.IsEmpty) { + return this; + } else if (IsEmpty) { + return restrictions; + } else { + List res = new List(_restrictions.Length + restrictions._restrictions.Length); + AddRestrictions(_restrictions, res); + AddRestrictions(restrictions._restrictions, res); + + return new Restrictions(res.ToArray()); + } + } + + /// + /// Adds unique restrictions and doesn't add restrictions which are alerady present + /// + private static void AddRestrictions(Restriction[] list, List res) { + foreach (Restriction r in list) { + bool found = false; + for (int j = 0; j < res.Count; j++) { + if (res[j] == r) { + found = true; + } + } + + if (!found) { + res.Add(r); + } + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly Restrictions Empty = new Restrictions(); + + public static Restrictions GetTypeRestriction(Expression expression, Type type) { + ContractUtils.RequiresNotNull(expression, "expression"); + ContractUtils.RequiresNotNull(type, "type"); + + if (expression.Type == type && type.IsSealedOrValueType()) { + return Restrictions.Empty; + } + + return new Restrictions(new Restriction(expression, type)); + } + + public static Restrictions GetInstanceRestriction(Expression expression, object instance) { + ContractUtils.RequiresNotNull(expression, "expression"); + + return new Restrictions(new Restriction(expression, instance)); + } + + public static Restrictions GetExpressionRestriction(Expression expression) { + ContractUtils.RequiresNotNull(expression, "expression"); + ContractUtils.Requires(expression.Type == typeof(bool), "expression"); + return new Restrictions(new Restriction(expression)); + } + + public static Restrictions Combine(IList contributingObjects) { + Restrictions res = Restrictions.Empty; + if (contributingObjects != null) { + foreach (MetaObject mo in contributingObjects) { + if (mo != null) { + res = res.Merge(mo.Restrictions); + } + } + } + return res; + } + + public Expression ToExpression() { + // TODO: Currently totally unoptimized and unordered + Expression test = null; + foreach (Restriction r in _restrictions) { + Expression one; + switch (r.Kind) { + case Restriction.RestrictionKind.Type: + one = CreateTypeRestriction(r.Expression, r.Type); + break; + case Restriction.RestrictionKind.Instance: + one = CreateInstanceRestriction(r.Expression, r.Instance); + break; + case Restriction.RestrictionKind.Custom: + one = r.Expression; + break; + default: + throw new InvalidOperationException(); + } + + if (one != null) { + if (test == null) { + test = one; + } else { + test = Expression.AndAlso(test, one); + } + } + } + + return test ?? Expression.Constant(true); + } + + /// + /// Creates one type identity test + /// + private static Expression CreateTypeRestriction(Expression expression, Type rt) { + // Null is special. True if expression produces null. + if (rt == typeof(Null)) { + return Expression.Equal(expression, Expression.Constant(null)); + } + return Expression.TypeEqual(expression, rt); + } + + private static Expression CreateInstanceRestriction(Expression expression, object value) { + if (value == null) { + return Expression.Equal( + expression, + Expression.Constant(null, expression.Type) + ); + } + + return Expression.Equal( + expression, + Expression.Property( + Expression.Constant(new WeakReference(value)), + typeof(WeakReference).GetProperty("Target") + ) + ); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/RuleSet.cs b/ndp/fx/src/core/microsoft/scripting/Actions/RuleSet.cs new file mode 100644 index 0000000000..9b76033b3f --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/RuleSet.cs @@ -0,0 +1,31 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Runtime.CompilerServices; + +namespace System.Dynamic.Binders { + + /// + /// A RuleSet is a collection of rules to apply to the objects at a DynamicSite. Each Rule also + /// includes a target that is to be called if the rules' conditions are met. + /// RuleSets are all immutable. + /// + internal abstract class RuleSet where T : class { + internal abstract RuleSet AddRule(CallSiteRule newRule); + internal abstract CallSiteRule[] GetRules(); + internal abstract T GetTarget(); + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/RuleTree.cs b/ndp/fx/src/core/microsoft/scripting/Actions/RuleTree.cs new file mode 100644 index 0000000000..1e4cee298c --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/RuleTree.cs @@ -0,0 +1,141 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; + +namespace System.Dynamic.Binders { + /// + /// This uses linear search to find a rule. Clearly that doesn't scale super well. + /// We will address this in the future. + /// + /// + internal class RuleTree where T : class { + private RuleTable _ruleTable = new RuleTable(); + private const int MaxRulesPerSignaturePerCallSiteBinder = 100; + + internal RuleTree() { + } + + /// + /// Looks through the rule list, prunes invalid rules and returns rules that apply + /// + internal CallSiteRule[] FindApplicableRules(object[] args) { + // + // 1. Get the rule list that would apply to the arguments at hand + // + LinkedList> list = GetRuleList(args); + + lock (list) { + // + // Clone the list for execution + // + int live = list.Count; + CallSiteRule[] rules = null; + + if (live > 0) { + rules = new CallSiteRule[live]; + int index = 0; + + LinkedListNode> node = list.First; + while (node != null) { + rules[index++] = node.Value; + node = node.Next; + } + Debug.Assert(index == live); + } + // + // End of lock + // + + return rules; + } + } + + private LinkedList> GetRuleList(object[] args) { + LinkedList> ruleList; + lock (_ruleTable) { + RuleTable curTable = _ruleTable; + foreach (object arg in args) { + Type objType = TypeUtils.GetTypeForBinding(arg); + if (curTable.NextTable == null) { + curTable.NextTable = new Dictionary(1); + } + + RuleTable nextTable; + if (!curTable.NextTable.TryGetValue(objType, out nextTable)) { + curTable.NextTable[objType] = nextTable = new RuleTable(); + } + + curTable = nextTable; + } + + if (curTable.Rules == null) { + curTable.Rules = new LinkedList>(); + } + + ruleList = curTable.Rules; + } + return ruleList; + } + + internal void AddRule(object[] args, CallSiteRule rule) { + LinkedList> list = GetRuleList(args); + lock (list) { + list.AddFirst(rule); + if (list.Count > MaxRulesPerSignaturePerCallSiteBinder) { + list.RemoveLast(); + } + } + } + + internal void RemoveRule(object[] args, CallSiteRule rule) { + LinkedList> list = GetRuleList(args); + lock (list) { + LinkedListNode> node = list.First; + EqualityComparer> cmp = EqualityComparer>.Default; + while (node != null) { + if (cmp.Equals(node.Value, rule)) { + list.Remove(node); + break; + } + node = node.Next; + } + } + } + + internal void MoveRule(CallSiteRule rule, object[] args) { + LinkedList> list = GetRuleList(args); + lock (list) { + LinkedListNode> node = list.First; + while (node != null) { + if (node.Value == rule) { + list.Remove(node); + list.AddFirst(node); + break; + } + node = node.Next; + } + } + } + + private class RuleTable { + internal Dictionary NextTable; + internal LinkedList> Rules; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/SetIndexBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/SetIndexBinder.cs new file mode 100644 index 0000000000..40b6a34a81 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/SetIndexBinder.cs @@ -0,0 +1,69 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Dynamic.Utils; +using Microsoft.Contracts; + +namespace System.Dynamic.Binders { + public abstract class SetIndexBinder : MetaObjectBinder { + private readonly ReadOnlyCollection _arguments; + + protected SetIndexBinder(params ArgumentInfo[] arguments) + : this((IEnumerable)arguments) { + } + + protected SetIndexBinder(IEnumerable arguments) { + _arguments = arguments.ToReadOnly(); + } + + public ReadOnlyCollection Arguments { + get { return _arguments; } + } + + [Confined] + public override bool Equals(object obj) { + SetIndexBinder ia = obj as SetIndexBinder; + return ia != null && ia._arguments.ListEquals(_arguments); + } + + [Confined] + public override int GetHashCode() { + return SetMemberBinderHash ^ _arguments.ListHashCode(); + } + + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNull(args, "args"); + ContractUtils.Requires(args.Length >= 2, "args"); + + MetaObject value = args[args.Length - 1]; + MetaObject[] indexes = args.RemoveLast(); + + ContractUtils.RequiresNotNull(value, "args"); + ContractUtils.RequiresNotNullItems(indexes, "args"); + + return target.BindSetIndex(this, indexes, value); + } + + public MetaObject FallbackSetIndex(MetaObject target, MetaObject[] indexes, MetaObject value) { + return FallbackSetIndex(target, indexes, value, null); + } + + public abstract MetaObject FallbackSetIndex(MetaObject target, MetaObject[] indexes, MetaObject value, MetaObject errorSuggestion); + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/SetMemberBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/SetMemberBinder.cs new file mode 100644 index 0000000000..a36c8baecf --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/SetMemberBinder.cs @@ -0,0 +1,65 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; + +namespace System.Dynamic.Binders { + public abstract class SetMemberBinder : MetaObjectBinder { + private readonly string _name; + private readonly bool _ignoreCase; + + protected SetMemberBinder(string name, bool ignoreCase) { + ContractUtils.RequiresNotNull(name, "name"); + + _name = name; + _ignoreCase = ignoreCase; + } + + public string Name { + get { + return _name; + } + } + + public bool IgnoreCase { + get { + return _ignoreCase; + } + } + + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNullItems(args, "args"); + ContractUtils.Requires(args.Length == 1); + + return target.BindSetMember(this, args[0]); + } + + public MetaObject FallbackSetMember(MetaObject target, MetaObject value) { + return FallbackSetMember(target, value, null); + } + + public abstract MetaObject FallbackSetMember(MetaObject target, MetaObject value, MetaObject errorSuggestion); + + public override int GetHashCode() { + return SetMemberBinderHash ^ _name.GetHashCode() ^ (_ignoreCase ? 0x8000000 : 0); + } + + public override bool Equals(object obj) { + SetMemberBinder sa = obj as SetMemberBinder; + return sa != null && sa._name == _name && sa._ignoreCase == _ignoreCase; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/SmallRuleSet.cs b/ndp/fx/src/core/microsoft/scripting/Actions/SmallRuleSet.cs new file mode 100644 index 0000000000..c236c286c0 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/SmallRuleSet.cs @@ -0,0 +1,130 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Linq.Expressions.Compiler; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; + +namespace System.Dynamic.Binders { + + /// + /// This holds a set of rules for a particular DynamicSite. Any given + /// SmallRuleSet instance is immutable and therefore they may be cached + /// and shared. At the moment, the only ones that are shared are + /// SmallRuleSets with a single rule. + /// + /// When a new rule is added, then a new SmallRuleSet will be created + /// that contains all existing rules that are still considered valid with + /// the new rule added to the front of the list. The target generated for + /// this type will simply try each of the rules in order and emit the + /// standard DynamicSite.UpdateBindingAndInvoke fallback call at the end. + /// + /// + internal class SmallRuleSet : RuleSet where T : class { + private T _target; + private const int MaxRules = 10; + private readonly CallSiteRule[] _rules; + + internal SmallRuleSet(CallSiteRule[] rules) { + _rules = rules; + } + + internal SmallRuleSet(T target, CallSiteRule[] rules) { + _rules = rules; + _target = target; + } + + internal override RuleSet AddRule(CallSiteRule newRule) { + List> newRules = new List>(); + newRules.Add(newRule); + foreach (CallSiteRule rule in _rules) { + newRules.Add(rule); + } + + if (newRules.Count > MaxRules) { + return EmptyRuleSet.FixedInstance; + } else { + return new SmallRuleSet(newRules.ToArray()); + } + } + + internal override CallSiteRule[] GetRules() { + return _rules; + } + + internal override T GetTarget() { + if (_target == null) { + _target = MakeTarget(); + } + return _target; + } + + private T MakeTarget() { + Debug.Assert(_rules.Length > 1 || this == _rules[0].RuleSet); + + MethodInfo method; + // We need to forceDynamic because: + // * T may not be a visible type + // * Rules generated by COM need skipVisibility to access internal + // COM objects and methods + T t = LambdaCompiler.CompileLambda(Stitch(), true, out method); + + if (_rules.Length == 1) { + _rules[0].TemplateMethod = method; + } + + return t; + } + + private Expression Stitch() { + Type targetType = typeof(T); + Type siteType = typeof(CallSite); + + // TODO: we could cache this on Rule + MethodInfo invoke = targetType.GetMethod("Invoke"); + + int length = _rules.Length; + Expression[] body = new Expression[length + 1]; + for (int i = 0; i < length; i++) { + body[i] = _rules[i].Binding; + } + + var @params = CallSiteRule.Parameters.AddFirst(Expression.Parameter(typeof(CallSite), "$site")); + + body[_rules.Length] = Expression.Label( + CallSiteRule.ReturnLabel, + Expression.Call( + Expression.Field( + Expression.Convert(@params[0], siteType), + siteType.GetField("Update") + ), + invoke, + new ReadOnlyCollection(@params) + ) + ); + + return new Expression( + "_stub_", + Expression.Block(body), + new ReadOnlyCollection(@params) + ); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/TemplatedValue.cs b/ndp/fx/src/core/microsoft/scripting/Actions/TemplatedValue.cs new file mode 100644 index 0000000000..c28e2c3b16 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/TemplatedValue.cs @@ -0,0 +1,63 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace System.Runtime.CompilerServices { + internal interface ITemplatedValue { + int Index { get; } + object CopyWithNewValue(object value); + object ObjectValue { get; } + } + + public class TemplatedValue : ITemplatedValue { + private T _value; + private int _index; + + // constructor is internal as this type should be used by DLR only + internal TemplatedValue(T value, int index) { + _index = index; + _value = value; + } + + internal static TemplatedValue Make(object value, int index) { + return new TemplatedValue((T)value, index); + } + + public T Value { + get { + return _value; + } + } + + #region ITemplatedValue Members + + int ITemplatedValue.Index { + get { + return _index; + } + } + + object ITemplatedValue.CopyWithNewValue(object value) { + return new TemplatedValue((T)value, _index); + } + + object ITemplatedValue.ObjectValue { + get { + return _value; + } + } + + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/UnaryOperationBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/UnaryOperationBinder.cs new file mode 100644 index 0000000000..27ab54fbc8 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/UnaryOperationBinder.cs @@ -0,0 +1,84 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Linq.Expressions; +using System.Dynamic.Utils; +using Microsoft.Contracts; + +namespace System.Dynamic.Binders { + public abstract class UnaryOperationBinder : MetaObjectBinder { + private ExpressionType _operation; + + protected UnaryOperationBinder(ExpressionType operation) { + ContractUtils.ReferenceEquals(OperationIsValid(operation), "operation"); + _operation = operation; + } + + public ExpressionType Operation { + get { + return _operation; + } + } + + internal MetaObject FallbackUnaryOperation(MetaObject target) { + return FallbackUnaryOperation(target, null); + } + + public abstract MetaObject FallbackUnaryOperation(MetaObject target, MetaObject errorSuggestion); + + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.Requires(args == null || args.Length == 0, "args"); + + return target.BindUnaryOperation(this); + } + + [Confined] + public override bool Equals(object obj) { + UnaryOperationBinder oa = obj as UnaryOperationBinder; + return oa != null && oa._operation == _operation; + } + + [Confined] + public override int GetHashCode() { + return UnaryOperationBinderHash ^ (int)_operation; + } + + internal static bool OperationIsValid(ExpressionType operation) { + switch (operation) { + #region Generated Unary Operation Binder Validator + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_unop_validator from: generate_tree.py + + case ExpressionType.Negate: + case ExpressionType.UnaryPlus: + case ExpressionType.Not: + case ExpressionType.Decrement: + case ExpressionType.Increment: + + // *** END GENERATED CODE *** + + #endregion + + case ExpressionType.Extension: + return true; + + default: + return false; + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/UnaryOperationOnIndexBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/UnaryOperationOnIndexBinder.cs new file mode 100644 index 0000000000..253f3329a5 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/UnaryOperationOnIndexBinder.cs @@ -0,0 +1,145 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic.Utils; +using System.Linq.Expressions; +using Microsoft.Contracts; + +namespace System.Dynamic.Binders { + /// + /// A Binder that is responsible for runtime binding of operation: + /// (op) a[b] + /// For example : ++ a[b] + /// + public abstract class UnaryOperationOnIndexBinder : MetaObjectBinder { + private ExpressionType _operation; + private readonly ReadOnlyCollection _arguments; + + /// + /// The constructor of the OperationOnIndexBinder object, representing "(op) a[b]" operation. + /// + /// Binary operation to be performed. + /// Description of the indexes (named, positional) + protected UnaryOperationOnIndexBinder(ExpressionType operation, params ArgumentInfo[] arguments) + : this(operation, (IEnumerable)arguments) { + } + + protected UnaryOperationOnIndexBinder(ExpressionType operation, IEnumerable arguments) { + ContractUtils.Requires(UnaryOperationBinder.OperationIsValid(operation), "operation"); + _operation = operation; + _arguments = arguments.ToReadOnly(); + } + + /// + /// The operation to be performed. + /// + public ExpressionType Operation { + get { + return _operation; + } + } + + /// + /// Descriptions of arguments to the indexer. This allows for named and positional arguments. + /// + public ReadOnlyCollection Arguments { + get { return _arguments; } + } + + /// + /// Implements Equality operation for the OperationOnIndexBinder + /// + /// Instance to comapre equal to. + /// true/false + [Confined] + public override bool Equals(object obj) { + UnaryOperationOnIndexBinder ia = obj as UnaryOperationOnIndexBinder; + return ia != null && ia._operation == _operation && ia._arguments.ListEquals(_arguments); + } + + /// + /// Calculates hash code for the OperationOnIndexBinder + /// + /// The hash code. + [Confined] + public override int GetHashCode() { + return UnaryOperationOnIndexBinderHash ^ (int)_operation ^ _arguments.ListHashCode(); + } + + /// + /// Performs binding of the operation on the target (represented as meta object) and + /// list of arguments (indexes and right-hand value) represented as meta objects + /// + /// Target of the operation. + /// List of indexes and right-hand value + /// MetaObject representing the binding. + public sealed override MetaObject Bind(MetaObject target, MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.RequiresNotNullItems(args, "args"); + + return target.BindUnaryOperationOnIndex(this, args); + } + + /// + /// Implements a binding logic for the binary operation part of the binding. + /// This is called by the target when the target implements the whole operation: + /// (op) a[b] + /// as: + /// a[b] = (op) a[b] + /// to let the language participate in the binding of the binary operation only. + /// + /// Target of the operation. + /// MetaObject representing the binding result. + public MetaObject FallbackUnaryOperation(MetaObject target) { + return FallbackUnaryOperation(target, null); + } + + /// + /// Implements a binding logic for the binary operation part of the binding. + /// This is called by the target when the target implements the whole operation: + /// (op) a[b] + /// as: + /// a[b] = (op) a[b] + /// to let the language participate in the binding of the binary operation only. + /// + /// Target of the operation. + /// The representaiton of the binding error that the target meta object recommends the language to use if the language cannot bind. This allows the target meta object to participate in the error handling process. + /// MetaObject representing the binding result. + public abstract MetaObject FallbackUnaryOperation(MetaObject target, MetaObject errorSuggestion); + + /// + /// Implements a binding logic for the operation. This is called by the target when + /// the target lets the executing language participate in the binding process. + /// + /// Target of the operation. + /// List of indexes and right-hand value + /// MetaObject representing the binding. + public MetaObject FallbackUnaryOperationOnIndex(MetaObject target, MetaObject[] indexes) { + return FallbackUnaryOperationOnIndex(target, indexes, null); + } + + /// + /// Implements a binding logic for the operation. This is called by the target when + /// the target lets the executing language participate in the binding process. + /// + /// Target of the operation. + /// List of indexes and right-hand value + /// The representaiton of the binding error that the target meta object recommends the language to use if the language cannot bind. This allows the target meta object to participate in the error handling process. + /// MetaObject representing the binding. + public abstract MetaObject FallbackUnaryOperationOnIndex(MetaObject target, MetaObject[] indexes, MetaObject errorSuggestion); + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/UnaryOperationOnMemberBinder.cs b/ndp/fx/src/core/microsoft/scripting/Actions/UnaryOperationOnMemberBinder.cs new file mode 100644 index 0000000000..ed47787b35 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/UnaryOperationOnMemberBinder.cs @@ -0,0 +1,146 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; +using System.Linq.Expressions; +using Microsoft.Contracts; + +namespace System.Dynamic.Binders { + /// + /// A Binder that is responsible for runtime binding of operation: + /// a.b (op)= c + /// + public abstract class UnaryOperationOnMemberBinder : MetaObjectBinder { + private readonly ExpressionType _operation; + private readonly string _name; + private readonly bool _ignoreCase; + + /// + /// Constructor of the OperationOnIndexBinder object, representing "a.b (op)= c" operation. + /// + /// Binary operation to be performed. + /// Name of the member for the operation. + /// Ignore case of the member. + protected UnaryOperationOnMemberBinder(ExpressionType operation, string name, bool ignoreCase) { + ContractUtils.RequiresNotNull(name, "name"); + ContractUtils.Requires(UnaryOperationBinder.OperationIsValid(operation), "operation"); + _operation = operation; + _name = name; + _ignoreCase = ignoreCase; + } + + /// + /// The operation to be performed. + /// + public ExpressionType Operation { + get { + return _operation; + } + } + + /// + /// Name of the member for the operation. + /// + public string Name { + get { + return _name; + } + } + + /// + /// Ignore case of the member. + /// + public bool IgnoreCase { + get { + return _ignoreCase; + } + } + + /// + /// Implements a binding logic for the binary operation part of the binding. + /// This is called by the target when the target implements the whole operation: + /// a[b] += c + /// as: + /// a[b] = a[b] + c + /// to let the language participate in the binding of the binary operation only. + /// + /// Target of the operation. + /// MetaObject representing the binding result. + public MetaObject FallbackUnaryOperation(MetaObject target) { + return FallbackUnaryOperation(target); + } + + /// + /// Implements a binding logic for the binary operation part of the binding. + /// This is called by the target when the target implements the whole operation: + /// a[b] += c + /// as: + /// a[b] = a[b] + c + /// to let the language participate in the binding of the binary operation only. + /// + /// Target of the operation. + /// The representaiton of the binding error that the target meta object recommends the language to use if the language cannot bind. This allows the target meta object to participate in the error handling process. + /// MetaObject representing the binding result. + public abstract MetaObject FallbackUnaryOperation(MetaObject target, MetaObject errorSuggestion); + + /// + /// Implements a binding logic for the operation. This is called by the target when + /// the target lets the executing language participate in the binding process. + /// + /// Target of the operation. + /// MetaObject representing the binding. + public MetaObject FallbackUnaryOperationOnMember(MetaObject target) { + return FallbackUnaryOperationOnMember(target, null); + } + + /// + /// Implements a binding logic for the operation. This is called by the target when + /// the target lets the executing language participate in the binding process. + /// + /// Target of the operation. + /// The representation of the binding error that the target meta + /// object recommends the language to use if the language cannot bind. + /// This allows the target meta object to participate in the error handling process. + /// MetaObject representing the binding. + public abstract MetaObject FallbackUnaryOperationOnMember(MetaObject target, MetaObject errorSuggestion); + + public sealed override MetaObject Bind(MetaObject target, params MetaObject[] args) { + ContractUtils.RequiresNotNull(target, "target"); + ContractUtils.Requires(args == null || args.Length == 0, "args"); + + return target.BindUnaryOperationOnMember(this); + } + + /// + /// Implements Equality operation for the OperationOnMemberBinder + /// + /// Instance to comapre equal to. + /// true/false + [Confined] + public override bool Equals(object obj) { + UnaryOperationOnMemberBinder gma = obj as UnaryOperationOnMemberBinder; + return gma != null && gma._operation == _operation && gma._name == _name && gma._ignoreCase == _ignoreCase; + } + + /// + /// Calculates hash code for the OperationOnMemberBinder + /// + /// The hash code. + [Confined] + public override int GetHashCode() { + return UnaryOperationOnMemberBinderHash ^ (int)_operation ^ _name.GetHashCode() ^ (_ignoreCase ? unchecked((int)0x80000000) : 0); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Actions/UpdateDelegates.Generated.cs b/ndp/fx/src/core/microsoft/scripting/Actions/UpdateDelegates.Generated.cs new file mode 100644 index 0000000000..40a5a60044 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Actions/UpdateDelegates.Generated.cs @@ -0,0 +1,3246 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Dynamic.Binders { + internal static partial class UpdateDelegates { + + /// + /// Caches a single Matchmaker and its delegate to avoid expensive delegate + /// recreation. We just Interlock.Exchange this out each time we need one + /// and replace it when we're done. If multiple threads are operating we'll + /// sometimes end up creating multiple delegates which is as bad w/o the + /// cache. + /// + private static class MatchmakerCache { + public static Matchmaker Info; + } + + private partial class Matchmaker { + internal bool Match; + internal Delegate Delegete; + } + + // + // WARNING: do not edit these methods here. The real source code lives + // in two places: generate_dynsites.py, which generates the methods in + // this file, and UpdateDelegates.cs, which dynamically generates + // methods like these at run time. If you want to make a change, edit + // *both* of those files instead + // + + #region Generated UpdateAndExecute Methods + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_update_targets from: generate_dynsites.py + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static TRet UpdateAndExecute1(CallSite site, T0 arg0) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Func ruleTarget, startingTarget = @this.Target; + TRet result; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.Fallback1; + } else { + ruleTarget = (Func)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal TRet Fallback1(CallSite site, T0 arg0) { + Match = false; + return default(TRet); + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static TRet UpdateAndExecute2(CallSite site, T0 arg0, T1 arg1) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Func ruleTarget, startingTarget = @this.Target; + TRet result; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.Fallback2; + } else { + ruleTarget = (Func)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal TRet Fallback2(CallSite site, T0 arg0, T1 arg1) { + Match = false; + return default(TRet); + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static TRet UpdateAndExecute3(CallSite site, T0 arg0, T1 arg1, T2 arg2) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Func ruleTarget, startingTarget = @this.Target; + TRet result; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.Fallback3; + } else { + ruleTarget = (Func)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal TRet Fallback3(CallSite site, T0 arg0, T1 arg1, T2 arg2) { + Match = false; + return default(TRet); + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static TRet UpdateAndExecute4(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Func ruleTarget, startingTarget = @this.Target; + TRet result; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.Fallback4; + } else { + ruleTarget = (Func)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal TRet Fallback4(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3) { + Match = false; + return default(TRet); + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static TRet UpdateAndExecute5(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Func ruleTarget, startingTarget = @this.Target; + TRet result; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.Fallback5; + } else { + ruleTarget = (Func)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3, arg4 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal TRet Fallback5(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { + Match = false; + return default(TRet); + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static TRet UpdateAndExecute6(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Func ruleTarget, startingTarget = @this.Target; + TRet result; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.Fallback6; + } else { + ruleTarget = (Func)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3, arg4, arg5 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal TRet Fallback6(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { + Match = false; + return default(TRet); + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static TRet UpdateAndExecute7(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Func ruleTarget, startingTarget = @this.Target; + TRet result; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.Fallback7; + } else { + ruleTarget = (Func)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal TRet Fallback7(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { + Match = false; + return default(TRet); + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static TRet UpdateAndExecute8(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Func ruleTarget, startingTarget = @this.Target; + TRet result; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.Fallback8; + } else { + ruleTarget = (Func)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal TRet Fallback8(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { + Match = false; + return default(TRet); + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static TRet UpdateAndExecute9(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Func ruleTarget, startingTarget = @this.Target; + TRet result; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.Fallback9; + } else { + ruleTarget = (Func)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal TRet Fallback9(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { + Match = false; + return default(TRet); + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static TRet UpdateAndExecute10(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Func ruleTarget, startingTarget = @this.Target; + TRet result; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.Fallback10; + } else { + ruleTarget = (Func)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + result = ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + if (mm.Match) { + return result; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal TRet Fallback10(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) { + Match = false; + return default(TRet); + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static void UpdateAndExecuteVoid1(CallSite site, T0 arg0) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Action ruleTarget, startingTarget = @this.Target; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.FallbackVoid1; + } else { + ruleTarget = (Action)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal void FallbackVoid1(CallSite site, T0 arg0) { + Match = false; + return; + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static void UpdateAndExecuteVoid2(CallSite site, T0 arg0, T1 arg1) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Action ruleTarget, startingTarget = @this.Target; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.FallbackVoid2; + } else { + ruleTarget = (Action)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal void FallbackVoid2(CallSite site, T0 arg0, T1 arg1) { + Match = false; + return; + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static void UpdateAndExecuteVoid3(CallSite site, T0 arg0, T1 arg1, T2 arg2) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Action ruleTarget, startingTarget = @this.Target; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.FallbackVoid3; + } else { + ruleTarget = (Action)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal void FallbackVoid3(CallSite site, T0 arg0, T1 arg1, T2 arg2) { + Match = false; + return; + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static void UpdateAndExecuteVoid4(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Action ruleTarget, startingTarget = @this.Target; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.FallbackVoid4; + } else { + ruleTarget = (Action)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal void FallbackVoid4(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3) { + Match = false; + return; + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static void UpdateAndExecuteVoid5(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Action ruleTarget, startingTarget = @this.Target; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.FallbackVoid5; + } else { + ruleTarget = (Action)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3, arg4 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal void FallbackVoid5(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { + Match = false; + return; + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static void UpdateAndExecuteVoid6(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Action ruleTarget, startingTarget = @this.Target; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.FallbackVoid6; + } else { + ruleTarget = (Action)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3, arg4, arg5 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal void FallbackVoid6(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { + Match = false; + return; + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static void UpdateAndExecuteVoid7(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Action ruleTarget, startingTarget = @this.Target; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.FallbackVoid7; + } else { + ruleTarget = (Action)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal void FallbackVoid7(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { + Match = false; + return; + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static void UpdateAndExecuteVoid8(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Action ruleTarget, startingTarget = @this.Target; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.FallbackVoid8; + } else { + ruleTarget = (Action)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal void FallbackVoid8(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { + Match = false; + return; + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static void UpdateAndExecuteVoid9(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Action ruleTarget, startingTarget = @this.Target; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.FallbackVoid9; + } else { + ruleTarget = (Action)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal void FallbackVoid9(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { + Match = false; + return; + } + } + + + [Obsolete("pregenerated CallSite.Update delegate", true)] + internal static void UpdateAndExecuteVoid10(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) { + // + // Declare the locals here upfront. It actually saves JIT stack space. + // + var @this = (CallSite>)site; + CallSiteRule>[] applicable; + CallSiteRule> rule; + Action ruleTarget, startingTarget = @this.Target; + + int count, index; + CallSiteRule> originalRule = null; + + // get the matchmaker & its delegate + Matchmaker mm = Interlocked.Exchange(ref MatchmakerCache>.Info, null); + if (mm == null) { + mm = new Matchmaker(); + mm.Delegete = ruleTarget = mm.FallbackVoid10; + } else { + ruleTarget = (Action)mm.Delegete; + } + + try { + // + // Create matchmaker and its site. We'll need them regardless. + // + mm.Match = true; + site = CallSiteOps.CreateMatchmaker( + @this, + ruleTarget + ); + + // + // Level 1 cache lookup + // + if ((applicable = CallSiteOps.GetRules(@this)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Match in Level 1 cache. We saw the arguments that match the rule before and now we + // see them again. The site is polymorphic. Update the delegate and keep running + // + CallSiteOps.SetPolymorphicTarget(@this); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // our rule was previously monomorphic, if we produce another monomorphic + // rule we should try and share code between the two. + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + // + // Level 2 cache lookup + // + var args = new object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 }; + + // + // Any applicable rules in level 2 cache? + // + if ((applicable = CallSiteOps.FindApplicableRules(@this, args)) != null) { + for (index = 0, count = applicable.Length; index < count; index++) { + rule = applicable[index]; + + // + // Execute the rule + // + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // Rule worked. Add it to level 1 cache + // + CallSiteOps.AddRule(@this, rule); + // and then move it to the front of the L2 cache + @this.RuleCache.MoveRule(rule, args); + } + } + + if ((object)startingTarget == (object)ruleTarget) { + // If we've gone megamorphic we can still template off the L2 cache + originalRule = rule; + } + + // Rule didn't match, try the next one + mm.Match = true; + } + } + + + // + // Miss on Level 0, 1 and 2 caches. Create new rule + // + + rule = null; + + for (; ; ) { + rule = CallSiteOps.CreateNewRule(@this, rule, originalRule, args); + + // + // Execute the rule on the matchmaker site + // + + ruleTarget = CallSiteOps.SetTarget(@this, rule); + + try { + ruleTarget(site, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + if (mm.Match) { + return; + } + } finally { + if (mm.Match) { + // + // The rule worked. Add it to level 1 cache. + // + CallSiteOps.AddRule(@this, rule); + } + } + + // Rule we got back didn't work, try another one + mm.Match = true; + } + } finally { + Interlocked.Exchange(ref MatchmakerCache>.Info, mm); + } + } + + private partial class Matchmaker { + internal void FallbackVoid10(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) { + Match = false; + return; + } + } + + + // *** END GENERATED CODE *** + + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/ArgumentInfo.cs b/ndp/fx/src/core/microsoft/scripting/Ast/ArgumentInfo.cs new file mode 100644 index 0000000000..75fe052abf --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/ArgumentInfo.cs @@ -0,0 +1,101 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; +using Microsoft.Contracts; + +namespace System.Linq.Expressions { + public enum ArgumentKind { + Positional, + Named + } + + public abstract class ArgumentInfo { + internal ArgumentInfo() { + } + + public ArgumentKind ArgumentType { + get { return GetArgumentType(); } + } + + internal abstract ArgumentKind GetArgumentType(); + } + + public sealed class PositionalArgumentInfo : ArgumentInfo { + private readonly int _position; + + internal PositionalArgumentInfo(int position) { + _position = position; + } + + public int Position { + get { return _position; } + } + + internal override ArgumentKind GetArgumentType() { + return ArgumentKind.Positional; + } + + [Confined] + public override bool Equals(object obj) { + PositionalArgumentInfo arg = obj as PositionalArgumentInfo; + return arg != null && arg._position == _position; + } + + [Confined] + public override int GetHashCode() { + return _position; + } + } + + public sealed class NamedArgumentInfo : ArgumentInfo { + private readonly string _name; + + internal NamedArgumentInfo(string name) { + _name = name; + } + + public string Name { + get { return _name; } + } + + internal override ArgumentKind GetArgumentType() { + return ArgumentKind.Named; + } + + [Confined] + public override bool Equals(object obj) { + NamedArgumentInfo arg = obj as NamedArgumentInfo; + return arg != null && arg._name == _name; + } + + [Confined] + public override int GetHashCode() { + return _name.GetHashCode(); + } + } + + public partial class Expression { + public static PositionalArgumentInfo PositionalArg(int position) { + ContractUtils.Requires(position >= 0, "position", Strings.MustBePositive); + return new PositionalArgumentInfo(position); + } + public static NamedArgumentInfo NamedArg(string name) { + // TODO: should we allow the empty string? + ContractUtils.Requires(!string.IsNullOrEmpty(name), "name"); + return new NamedArgumentInfo(name); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/BinaryExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/BinaryExpression.cs new file mode 100644 index 0000000000..4fdb89cb47 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/BinaryExpression.cs @@ -0,0 +1,1471 @@ + +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Dynamic.Utils; +using System.Text; + +namespace System.Linq.Expressions { + //CONFORMING + public class BinaryExpression : Expression { + private readonly Expression _left; + private readonly Expression _right; + + internal BinaryExpression(Expression left, Expression right) { + _left = left; + _right = right; + } + + public override bool CanReduce { + get { + //Only OpAssignments are reducible. + return IsOpAssignment(NodeType); + } + } + + private static bool IsOpAssignment(ExpressionType op) { + switch (op) { + case ExpressionType.AddAssign: + case ExpressionType.SubtractAssign: + case ExpressionType.MultiplyAssign: + case ExpressionType.AddAssignChecked: + case ExpressionType.SubtractAssignChecked: + case ExpressionType.MultiplyAssignChecked: + case ExpressionType.DivideAssign: + case ExpressionType.ModuloAssign: + case ExpressionType.PowerAssign: + case ExpressionType.AndAssign: + case ExpressionType.OrAssign: + case ExpressionType.RightShiftAssign: + case ExpressionType.LeftShiftAssign: + case ExpressionType.ExclusiveOrAssign: + return true; + } + return false; + } + + public Expression Right { + get { return _right; } + } + + public Expression Left { + get { return _left; } + } + + public MethodInfo Method { + get { return GetMethod(); } + } + + internal virtual MethodInfo GetMethod() { + return null; + } + + public override Expression Reduce() { + //Only reduce OpAssignment expressions. + if (IsOpAssignment(NodeType)) { + switch (_left.NodeType) { + case ExpressionType.MemberAccess: + return ReduceMember(); + + case ExpressionType.Index: + return ReduceIndex(); + + default: + return ReduceVariable(); + } + } + return this; + } + + //Return the corresponding Op of an assignment op. + private static ExpressionType GetBinaryOpFromAssignmentOp(ExpressionType op) { + Debug.Assert(IsOpAssignment(op)); + switch (op) { + case ExpressionType.AddAssign: + return ExpressionType.Add ; + case ExpressionType.AddAssignChecked: + return ExpressionType.AddChecked; + case ExpressionType.SubtractAssign: + return ExpressionType.Subtract; + case ExpressionType.SubtractAssignChecked: + return ExpressionType.SubtractChecked; + case ExpressionType.MultiplyAssign: + return ExpressionType.Multiply; + case ExpressionType.MultiplyAssignChecked: + return ExpressionType.MultiplyChecked; + case ExpressionType.DivideAssign: + return ExpressionType.Divide; + case ExpressionType.ModuloAssign: + return ExpressionType.Modulo; + case ExpressionType.PowerAssign: + return ExpressionType.Power; + case ExpressionType.AndAssign: + return ExpressionType.And; + case ExpressionType.OrAssign: + return ExpressionType.Or; + case ExpressionType.RightShiftAssign: + return ExpressionType.RightShift; + case ExpressionType.LeftShiftAssign: + return ExpressionType.LeftShift; + case ExpressionType.ExclusiveOrAssign: + return ExpressionType.ExclusiveOr; + default: + //must be an error + throw Error.InvalidOperation("op"); + } + + } + + private Expression ReduceVariable() { + // v (op)= r + // ... is reduced into ... + // v = v (op) r + var op = GetBinaryOpFromAssignmentOp(NodeType); + return Expression.Assign( + _left, + Expression.MakeBinary(op, _left, _right, false, Method) + ); + } + + private Expression ReduceMember() { + MemberExpression member = (MemberExpression)_left; + + if (member.Expression == null) { + //static member, reduce the same as variable + return ReduceVariable(); + } else { + // left.b (op)= r + // ... is reduced into ... + // temp1 = left + // temp2 = temp1.b (op) r + // temp1.b = temp2 + // temp2 + ParameterExpression temp1 = Variable(member.Expression.Type, "temp1"); + + // 1. temp1 = left + Expression e1 = Expression.Assign(temp1, member.Expression); + + // 2. temp2 = temp1.b (op) r + var op = GetBinaryOpFromAssignmentOp(NodeType); + Expression e2 = Expression.MakeBinary(op, Expression.MakeMemberAccess(temp1, member.Member), _right, false, Method); + ParameterExpression temp2 = Variable(e2.Type, "temp2"); + e2 = Expression.Assign(temp2, e2); + + // 3. temp1.b = temp2 + Expression e3 = Expression.Assign(Expression.MakeMemberAccess(temp1, member.Member), temp2); + + // 3. temp2 + Expression e4 = temp2; + + return Expression.Block( + new ParameterExpression[] { temp1, temp2 }, + e1, e2, e3, e4 + ); + } + } + + private Expression ReduceIndex() { + // left[a0, a1, ... aN] (op)= r + // + // ... is reduced into ... + // + // tempObj = left + // tempArg0 = a0 + // ... + // tempArgN = aN + // tempValue = tempObj[tempArg0, ... tempArgN] (op) r + // tempObj[tempArg0, ... tempArgN] = tempValue + + var index = (IndexExpression)_left; + + var vars = new List(index.Arguments.Count + 2); + var exprs = new List(index.Arguments.Count + 3); + + var tempObj = Expression.Variable(index.Object.Type, "tempObj"); + vars.Add(tempObj); + exprs.Add(Expression.Assign(tempObj, index.Object)); + + var tempArgs = new List(index.Arguments.Count); + foreach (var arg in index.Arguments) { + var tempArg = Expression.Variable(arg.Type, "tempArg" + tempArgs.Count); + vars.Add(tempArg); + tempArgs.Add(tempArg); + exprs.Add(Expression.Assign(tempArg, arg)); + } + + var tempIndex = Expression.MakeIndex(tempObj, index.Indexer, tempArgs); + + // tempValue = tempObj[tempArg0, ... tempArgN] (op) r + var binaryOp = GetBinaryOpFromAssignmentOp(NodeType); + var op = Expression.MakeBinary(binaryOp, tempIndex, _right, false, Method); + var tempValue = Expression.Variable(op.Type, "tempValue"); + vars.Add(tempValue); + exprs.Add(Expression.Assign(tempValue, op)); + + // tempObj[tempArg0, ... tempArgN] = tempValue + exprs.Add(Expression.Assign(tempIndex, tempValue)); + + return Expression.Block(vars, exprs); + } + + public LambdaExpression Conversion { + get { return GetConversion(); } + } + + internal virtual LambdaExpression GetConversion() { + return null; + } + + public bool IsLifted { + get { + if (NodeType == ExpressionType.Coalesce || NodeType == ExpressionType.Assign) { + return false; + } + if (TypeUtils.IsNullableType(_left.Type)) { + MethodInfo method = GetMethod(); + return method == null || method.GetParametersCached()[0].ParameterType != _left.Type; + } + return false; + } + } + + public bool IsLiftedToNull { + get { + return IsLifted && TypeUtils.IsNullableType(Type); + } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitBinary(this); + } + + internal static Expression Create(ExpressionType nodeType, Expression left, Expression right, Type type, MethodInfo method, LambdaExpression conversion) { + if (nodeType == ExpressionType.Assign) { + Debug.Assert(method == null && type == left.Type); + return new AssignBinaryExpression(left, right); + } + if (conversion != null) { + Debug.Assert(method == null && type == right.Type && nodeType == ExpressionType.Coalesce); + return new CoalesceConversionBinaryExpression(left, right, conversion); + } + if (method != null) { + return new MethodBinaryExpression(nodeType, left, right, type, method); + } + if (type == typeof(bool)) { + return new LogicalBinaryExpression(nodeType, left, right); + } + return new SimpleBinaryExpression(nodeType, left, right, type); + } + } + + // Optimized representation of simple logical expressions: + // && || == != > < >= <= + internal sealed class LogicalBinaryExpression : BinaryExpression { + private readonly ExpressionType _nodeType; + + internal LogicalBinaryExpression(ExpressionType nodeType, Expression left, Expression right) + : base(left, right) { + _nodeType = nodeType; + } + + protected override Type GetExpressionType() { + return typeof(bool); + } + + protected override ExpressionType GetNodeKind() { + return _nodeType; + } + } + + // Optimized assignment node, only holds onto children + internal sealed class AssignBinaryExpression : BinaryExpression { + internal AssignBinaryExpression(Expression left, Expression right) + : base(left, right) { + } + + protected override Type GetExpressionType() { + return Left.Type; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Assign; + } + } + + // Coalesce with conversion + // This is not a frequently used node, but rather we want to save every + // other BinaryExpression from holding onto the null conversion lambda + internal sealed class CoalesceConversionBinaryExpression : BinaryExpression { + private readonly LambdaExpression _conversion; + + internal CoalesceConversionBinaryExpression(Expression left, Expression right, LambdaExpression conversion) + : base(left, right) { + _conversion = conversion; + } + + internal override LambdaExpression GetConversion() { + return _conversion; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Coalesce; + } + + protected override Type GetExpressionType() { + return Right.Type; + } + } + + // Class that handles most binary expressions + // If needed, it can be optimized even more (often Type == left.Type) + internal class SimpleBinaryExpression : BinaryExpression { + private readonly ExpressionType _nodeType; + private readonly Type _type; + + internal SimpleBinaryExpression(ExpressionType nodeType, Expression left, Expression right, Type type) + : base(left, right) { + _nodeType = nodeType; + _type = type; + } + + protected override ExpressionType GetNodeKind() { + return _nodeType; + } + + protected override Type GetExpressionType() { + return _type; + } + } + + // Class that handles binary expressions with a method + // If needed, it can be optimized even more (often Type == method.ReturnType) + internal sealed class MethodBinaryExpression : SimpleBinaryExpression { + private readonly MethodInfo _method; + + internal MethodBinaryExpression(ExpressionType nodeType, Expression left, Expression right, Type type, MethodInfo method) + : base(nodeType, left, right, type) { + _method = method; + } + + internal override MethodInfo GetMethod() { + return _method; + } + } + + public partial class Expression { + + #region Assign + + /// + /// Performs an assignment variable = value + /// + public static BinaryExpression Assign(Expression left, Expression right) { + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + TypeUtils.ValidateType(left.Type); + TypeUtils.ValidateType(right.Type); + if (!TypeUtils.AreReferenceAssignable(left.Type, right.Type)) { + throw Error.ExpressionTypeDoesNotMatchAssignment(right.Type, left.Type); + } + return new AssignBinaryExpression(left, right); + } + + #endregion + + //CONFORMING + private static BinaryExpression GetUserDefinedBinaryOperator(ExpressionType binaryType, string name, Expression left, Expression right, bool liftToNull) { + // try exact match first + MethodInfo method = GetUserDefinedBinaryOperator(binaryType, left.Type, right.Type, name); + if (method != null) { + return new MethodBinaryExpression(binaryType, left, right, method.ReturnType, method); + } + // try lifted call + if (TypeUtils.IsNullableType(left.Type) && TypeUtils.IsNullableType(right.Type)) { + Type nnLeftType = TypeUtils.GetNonNullableType(left.Type); + Type nnRightType = TypeUtils.GetNonNullableType(right.Type); + method = GetUserDefinedBinaryOperator(binaryType, nnLeftType, nnRightType, name); + if (method != null && method.ReturnType.IsValueType && !TypeUtils.IsNullableType(method.ReturnType)) { + if (method.ReturnType != typeof(bool) || liftToNull) { + return new MethodBinaryExpression(binaryType, left, right, TypeUtils.GetNullableType(method.ReturnType), method); + } else { + return new MethodBinaryExpression(binaryType, left, right, typeof(bool), method); + } + } + } + return null; + } + + //CONFORMING + private static BinaryExpression GetMethodBasedBinaryOperator(ExpressionType binaryType, Expression left, Expression right, MethodInfo method, bool liftToNull) { + System.Diagnostics.Debug.Assert(method != null); + ValidateOperator(method); + ParameterInfo[] pms = method.GetParametersCached(); + if (pms.Length != 2) + throw Error.IncorrectNumberOfMethodCallArguments(method); + if (ParameterIsAssignable(pms[0], left.Type) && ParameterIsAssignable(pms[1], right.Type)) { + ValidateParamswithOperandsOrThrow(pms[0].ParameterType, left.Type, binaryType, method.Name); + ValidateParamswithOperandsOrThrow(pms[1].ParameterType, right.Type, binaryType, method.Name); + return new MethodBinaryExpression(binaryType, left, right, method.ReturnType, method); + + } + // check for lifted call + if (TypeUtils.IsNullableType(left.Type) && TypeUtils.IsNullableType(right.Type) && + ParameterIsAssignable(pms[0], TypeUtils.GetNonNullableType(left.Type)) && + ParameterIsAssignable(pms[1], TypeUtils.GetNonNullableType(right.Type)) && + method.ReturnType.IsValueType && !TypeUtils.IsNullableType(method.ReturnType)) { + if (method.ReturnType != typeof(bool) || liftToNull) { + return new MethodBinaryExpression(binaryType, left, right, TypeUtils.GetNullableType(method.ReturnType), method); + } else { + return new MethodBinaryExpression(binaryType, left, right, typeof(bool), method); + } + } + throw Error.OperandTypesDoNotMatchParameters(binaryType, method.Name); + } + + private static BinaryExpression GetMethodBasedAssignOperator(ExpressionType binaryType, Expression left, Expression right, MethodInfo method, bool liftToNull) { + BinaryExpression b = GetMethodBasedBinaryOperator(binaryType, left, right, method, liftToNull); + // return type must be assignable back to the left type + if (!TypeUtils.AreReferenceAssignable(left.Type, b.Type)) { + throw Error.UserDefinedOpMustHaveValidReturnType(binaryType, b.Method.Name); + } + return b; + } + + //CONFORMING + private static BinaryExpression GetUserDefinedBinaryOperatorOrThrow(ExpressionType binaryType, string name, Expression left, Expression right, bool liftToNull) { + BinaryExpression b = GetUserDefinedBinaryOperator(binaryType, name, left, right, liftToNull); + if (b != null) { + ParameterInfo[] pis = b.Method.GetParametersCached(); + ValidateParamswithOperandsOrThrow(pis[0].ParameterType, left.Type, binaryType, name); + ValidateParamswithOperandsOrThrow(pis[1].ParameterType, right.Type, binaryType, name); + return b; + } + throw Error.BinaryOperatorNotDefined(binaryType, left.Type, right.Type); + } + + private static BinaryExpression GetUserDefinedAssignOperatorOrThrow(ExpressionType binaryType, string name, Expression left, Expression right, bool liftToNull) { + BinaryExpression b = GetUserDefinedBinaryOperatorOrThrow(binaryType, name, left, right, liftToNull); + // return type must be assignable back to the left type + if (!TypeUtils.AreReferenceAssignable(left.Type, b.Type)) { + throw Error.UserDefinedOpMustHaveValidReturnType(binaryType, b.Method.Name); + } + return b; + } + + //CONFORMING + private static MethodInfo GetUserDefinedBinaryOperator(ExpressionType binaryType, Type leftType, Type rightType, string name) { + // UNDONE: This algorithm is wrong, we should be checking for uniqueness and erroring if + // UNDONE: it is defined on both types. + Type[] types = new Type[] { leftType, rightType }; + Type nnLeftType = TypeUtils.GetNonNullableType(leftType); + Type nnRightType = TypeUtils.GetNonNullableType(rightType); + BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + MethodInfo method = nnLeftType.GetMethod(name, flags, null, types, null); + if (method == null && leftType != rightType) { + method = nnRightType.GetMethod(name, flags, null, types, null); + } + + if (IsLiftingConditionalLogicalOperator(leftType, rightType, method, binaryType)) { + method = GetUserDefinedBinaryOperator(binaryType, nnLeftType, nnRightType, name); + } + return method; + } + + //CONFORMING + private static bool IsLiftingConditionalLogicalOperator(Type left, Type right, MethodInfo method, ExpressionType binaryType) { + return TypeUtils.IsNullableType(right) && + TypeUtils.IsNullableType(left) && + method == null && + (binaryType == ExpressionType.AndAlso || binaryType == ExpressionType.OrElse); + } + + //CONFORMING + private static bool ParameterIsAssignable(ParameterInfo pi, Type argType) { + Type pType = pi.ParameterType; + if (pType.IsByRef) + pType = pType.GetElementType(); + return TypeUtils.AreReferenceAssignable(pType, argType); + } + + //CONFORMING + private static void ValidateParamswithOperandsOrThrow(Type paramType, Type operandType, ExpressionType exprType, string name) { + if (TypeUtils.IsNullableType(paramType) && !TypeUtils.IsNullableType(operandType)) { + throw Error.OperandTypesDoNotMatchParameters(exprType, name); + } + } + + //CONFORMING + private static void ValidateOperator(MethodInfo method) { + System.Diagnostics.Debug.Assert(method != null); + ValidateMethodInfo(method); + if (!method.IsStatic) + throw Error.UserDefinedOperatorMustBeStatic(method); + if (method.ReturnType == typeof(void)) + throw Error.UserDefinedOperatorMustNotBeVoid(method); + } + + //TODO: consider moving to utils. It is used in many places. + //CONFORMING + private static void ValidateMethodInfo(MethodInfo method) { + if (method.IsGenericMethodDefinition) + throw Error.MethodIsGeneric(method); + if (method.ContainsGenericParameters) + throw Error.MethodContainsGenericParameters(method); + } + + //CONFORMING + private static bool IsNullComparison(Expression left, Expression right) { + // If we have x==null, x!=null, null==x or null!=x where x is + // nullable but not null, then this is treated as a call to x.HasValue + // and is legal even if there is no equality operator defined on the + // type of x. + if (IsNullConstant(left) && !IsNullConstant(right) && TypeUtils.IsNullableType(right.Type)) { + return true; + } + if (IsNullConstant(right) && !IsNullConstant(left) && TypeUtils.IsNullableType(left.Type)) { + return true; + } + return false; + } + + //CONFORMING + // Note: this has different meaning than ConstantCheck.IsNull + // That function attempts to determine if the result of a tree will be + // null at runtime. This function is used at tree construction time and + // only looks for a ConstantExpression with a null Value. It can't + // become "smarter" or that would break tree construction. + private static bool IsNullConstant(Expression e) { + var c = e as ConstantExpression; + return c != null && c.Value == null; + } + + //CONFORMING + private static void ValidateUserDefinedConditionalLogicOperator(ExpressionType nodeType, Type left, Type right, MethodInfo method) { + ValidateOperator(method); + ParameterInfo[] pms = method.GetParametersCached(); + if (pms.Length != 2) + throw Error.IncorrectNumberOfMethodCallArguments(method); + if (!ParameterIsAssignable(pms[0], left)) { + if (!(TypeUtils.IsNullableType(left) && ParameterIsAssignable(pms[0], TypeUtils.GetNonNullableType(left)))) + throw Error.OperandTypesDoNotMatchParameters(nodeType, method.Name); + } + if (!ParameterIsAssignable(pms[1], right)) { + if (!(TypeUtils.IsNullableType(right) && ParameterIsAssignable(pms[1], TypeUtils.GetNonNullableType(right)))) + throw Error.OperandTypesDoNotMatchParameters(nodeType, method.Name); + } + if (pms[0].ParameterType != pms[1].ParameterType) + throw Error.UserDefinedOpMustHaveConsistentTypes(nodeType, method.Name); + if (method.ReturnType != pms[0].ParameterType) + throw Error.UserDefinedOpMustHaveConsistentTypes(nodeType, method.Name); + if (IsValidLiftedConditionalLogicalOperator(left, right, pms)) { + left = TypeUtils.GetNonNullableType(left); + right = TypeUtils.GetNonNullableType(left); + } + MethodInfo opTrue = TypeUtils.GetBooleanOperator(method.DeclaringType, "op_True"); + MethodInfo opFalse = TypeUtils.GetBooleanOperator(method.DeclaringType, "op_False"); + if (opTrue == null || opTrue.ReturnType != typeof(bool) || + opFalse == null || opFalse.ReturnType != typeof(bool)) { + throw Error.LogicalOperatorMustHaveBooleanOperators(nodeType, method.Name); + } + } + + //CONFORMING + private static bool IsValidLiftedConditionalLogicalOperator(Type left, Type right, ParameterInfo[] pms) { + return left == right && TypeUtils.IsNullableType(right) && pms[1].ParameterType == TypeUtils.GetNonNullableType(right); + } + + //CONFORMING + public static BinaryExpression MakeBinary(ExpressionType binaryType, Expression left, Expression right) { + return MakeBinary(binaryType, left, right, false, null, null); + } + //CONFORMING + public static BinaryExpression MakeBinary(ExpressionType binaryType, Expression left, Expression right, bool liftToNull, MethodInfo method) { + return MakeBinary(binaryType, left, right, liftToNull, method, null); + } + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public static BinaryExpression MakeBinary(ExpressionType binaryType, Expression left, Expression right, bool liftToNull, MethodInfo method, LambdaExpression conversion) { + switch (binaryType) { + case ExpressionType.Add: + return Add(left, right, method); + case ExpressionType.AddChecked: + return AddChecked(left, right, method); + case ExpressionType.Subtract: + return Subtract(left, right, method); + case ExpressionType.SubtractChecked: + return SubtractChecked(left, right, method); + case ExpressionType.Multiply: + return Multiply(left, right, method); + case ExpressionType.MultiplyChecked: + return MultiplyChecked(left, right, method); + case ExpressionType.Divide: + return Divide(left, right, method); + case ExpressionType.Modulo: + return Modulo(left, right, method); + case ExpressionType.Power: + return Power(left, right, method); + case ExpressionType.And: + return And(left, right, method); + case ExpressionType.AndAlso: + return AndAlso(left, right, method); + case ExpressionType.Or: + return Or(left, right, method); + case ExpressionType.OrElse: + return OrElse(left, right, method); + case ExpressionType.LessThan: + return LessThan(left, right, liftToNull, method); + case ExpressionType.LessThanOrEqual: + return LessThanOrEqual(left, right, liftToNull, method); + case ExpressionType.GreaterThan: + return GreaterThan(left, right, liftToNull, method); + case ExpressionType.GreaterThanOrEqual: + return GreaterThanOrEqual(left, right, liftToNull, method); + case ExpressionType.Equal: + return Equal(left, right, liftToNull, method); + case ExpressionType.NotEqual: + return NotEqual(left, right, liftToNull, method); + case ExpressionType.ExclusiveOr: + return ExclusiveOr(left, right, method); + case ExpressionType.Coalesce: + return Coalesce(left, right, conversion); + case ExpressionType.ArrayIndex: + return ArrayIndex(left, right); + case ExpressionType.RightShift: + return RightShift(left, right, method); + case ExpressionType.LeftShift: + return LeftShift(left, right, method); + case ExpressionType.Assign: + return Assign(left, right); + case ExpressionType.AddAssign: + return AddAssign(left, right, method); + case ExpressionType.AndAssign: + return AndAssign(left, right, method); + case ExpressionType.DivideAssign: + return DivideAssign(left, right, method); + case ExpressionType.ExclusiveOrAssign: + return ExclusiveOrAssign(left, right, method); + case ExpressionType.LeftShiftAssign: + return LeftShiftAssign(left, right, method); + case ExpressionType.ModuloAssign: + return ModuloAssign(left, right, method); + case ExpressionType.MultiplyAssign: + return MultiplyAssign(left, right, method); + case ExpressionType.OrAssign: + return OrAssign(left, right, method); + case ExpressionType.PowerAssign: + return PowerAssign(left, right, method); + case ExpressionType.RightShiftAssign: + return RightShiftAssign(left, right, method); + case ExpressionType.SubtractAssign: + return SubtractAssign(left, right, method); + case ExpressionType.AddAssignChecked: + return AddAssignChecked(left, right, method); + case ExpressionType.SubtractAssignChecked: + return SubtractAssignChecked(left, right, method); + case ExpressionType.MultiplyAssignChecked: + return MultiplyAssignChecked(left, right, method); + default: + throw Error.UnhandledBinary(binaryType); + } + } + + #region Equality Operators + + //CONFORMING + public static BinaryExpression Equal(Expression left, Expression right) { + return Equal(left, right, false, null); + } + //CONFORMING + public static BinaryExpression Equal(Expression left, Expression right, bool liftToNull, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + return GetEqualityComparisonOperator(ExpressionType.Equal, "op_Equality", left, right, liftToNull); + } + return GetMethodBasedBinaryOperator(ExpressionType.Equal, left, right, method, liftToNull); + } + + //CONFORMING + public static BinaryExpression NotEqual(Expression left, Expression right) { + return NotEqual(left, right, false, null); + } + //CONFORMING + public static BinaryExpression NotEqual(Expression left, Expression right, bool liftToNull, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + return GetEqualityComparisonOperator(ExpressionType.NotEqual, "op_Inequality", left, right, liftToNull); + } + return GetMethodBasedBinaryOperator(ExpressionType.NotEqual, left, right, method, liftToNull); + } + + //CONFORMING + private static BinaryExpression GetEqualityComparisonOperator(ExpressionType binaryType, string opName, Expression left, Expression right, bool liftToNull) { + // known comparison - numeric types, bools, object, enums + if (left.Type == right.Type && (TypeUtils.IsNumeric(left.Type) || + left.Type == typeof(object) || + TypeUtils.IsBool(left.Type) || + TypeUtils.GetNonNullableType(left.Type).IsEnum)) { + if (TypeUtils.IsNullableType(left.Type) && liftToNull) { + return new SimpleBinaryExpression(binaryType, left, right, typeof(bool?)); + } else { + return new LogicalBinaryExpression(binaryType, left, right); + } + } + // look for user defined operator + BinaryExpression b = GetUserDefinedBinaryOperator(binaryType, opName, left, right, liftToNull); + if (b != null) { + return b; + } + if (TypeUtils.HasBuiltInEqualityOperator(left.Type, right.Type) || IsNullComparison(left, right)) { + if (TypeUtils.IsNullableType(left.Type) && liftToNull) { + return new SimpleBinaryExpression(binaryType, left, right, typeof(bool?)); + } else { + return new LogicalBinaryExpression(binaryType, left, right); + } + } + throw Error.BinaryOperatorNotDefined(binaryType, left.Type, right.Type); + } + + #endregion + + #region Comparison Expressions + + //CONFORMING + public static BinaryExpression GreaterThan(Expression left, Expression right) { + return GreaterThan(left, right, false, null); + } + //CONFORMING + public static BinaryExpression GreaterThan(Expression left, Expression right, bool liftToNull, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + return GetComparisonOperator(ExpressionType.GreaterThan, "op_GreaterThan", left, right, liftToNull); + } + return GetMethodBasedBinaryOperator(ExpressionType.GreaterThan, left, right, method, liftToNull); + } + + //CONFORMING + public static BinaryExpression LessThan(Expression left, Expression right) { + return LessThan(left, right, false, null); + } + //CONFORMING + public static BinaryExpression LessThan(Expression left, Expression right, bool liftToNull, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + return GetComparisonOperator(ExpressionType.LessThan, "op_LessThan", left, right, liftToNull); + } + return GetMethodBasedBinaryOperator(ExpressionType.LessThan, left, right, method, liftToNull); + } + + //CONFORMING + public static BinaryExpression GreaterThanOrEqual(Expression left, Expression right) { + return GreaterThanOrEqual(left, right, false, null); + } + //CONFORMING + public static BinaryExpression GreaterThanOrEqual(Expression left, Expression right, bool liftToNull, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + return GetComparisonOperator(ExpressionType.GreaterThanOrEqual, "op_GreaterThanOrEqual", left, right, liftToNull); + } + return GetMethodBasedBinaryOperator(ExpressionType.GreaterThanOrEqual, left, right, method, liftToNull); + } + + //CONFORMING + public static BinaryExpression LessThanOrEqual(Expression left, Expression right) { + return LessThanOrEqual(left, right, false, null); + } + //CONFORMING + public static BinaryExpression LessThanOrEqual(Expression left, Expression right, bool liftToNull, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + return GetComparisonOperator(ExpressionType.LessThanOrEqual, "op_LessThanOrEqual", left, right, liftToNull); + } + return GetMethodBasedBinaryOperator(ExpressionType.LessThanOrEqual, left, right, method, liftToNull); + } + + //CONFORMING + private static BinaryExpression GetComparisonOperator(ExpressionType binaryType, string opName, Expression left, Expression right, bool liftToNull) { + if (left.Type == right.Type && TypeUtils.IsNumeric(left.Type)) { + if (TypeUtils.IsNullableType(left.Type) && liftToNull) { + return new SimpleBinaryExpression(binaryType, left, right, typeof(bool?)); + } else { + return new LogicalBinaryExpression(binaryType, left, right); + } + } + return GetUserDefinedBinaryOperatorOrThrow(binaryType, opName, left, right, liftToNull); + } + + #endregion + + #region Boolean Expressions + + //CONFORMING + public static BinaryExpression AndAlso(Expression left, Expression right) { + return AndAlso(left, right, null); + } + //CONFORMING + public static BinaryExpression AndAlso(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + Type returnType; + if (method == null) { + if (left.Type == right.Type) { + if (left.Type == typeof(bool)) { + return new LogicalBinaryExpression(ExpressionType.AndAlso, left, right); + } else if (left.Type == typeof(bool?)) { + return new SimpleBinaryExpression(ExpressionType.AndAlso, left, right, left.Type); + } + } + method = GetUserDefinedBinaryOperator(ExpressionType.AndAlso, left.Type, right.Type, "op_BitwiseAnd"); + if (method != null) { + ValidateUserDefinedConditionalLogicOperator(ExpressionType.AndAlso, left.Type, right.Type, method); + returnType = (TypeUtils.IsNullableType(left.Type) && method.ReturnType == TypeUtils.GetNonNullableType(left.Type)) ? left.Type : method.ReturnType; + return new MethodBinaryExpression(ExpressionType.AndAlso, left, right, returnType, method); + } + throw Error.BinaryOperatorNotDefined(ExpressionType.AndAlso, left.Type, right.Type); + } + ValidateUserDefinedConditionalLogicOperator(ExpressionType.AndAlso, left.Type, right.Type, method); + returnType = (TypeUtils.IsNullableType(left.Type) && method.ReturnType == TypeUtils.GetNonNullableType(left.Type)) ? left.Type : method.ReturnType; + return new MethodBinaryExpression(ExpressionType.AndAlso, left, right, returnType, method); + } + + //CONFORMING + public static BinaryExpression OrElse(Expression left, Expression right) { + return OrElse(left, right, null); + } + //CONFORMING + public static BinaryExpression OrElse(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + Type returnType; + if (method == null) { + if (left.Type == right.Type) { + if (left.Type == typeof(bool)) { + return new LogicalBinaryExpression(ExpressionType.OrElse, left, right); + } else if (left.Type == typeof(bool?)) { + return new SimpleBinaryExpression(ExpressionType.OrElse, left, right, left.Type); + } + } + method = GetUserDefinedBinaryOperator(ExpressionType.OrElse, left.Type, right.Type, "op_BitwiseOr"); + if (method != null) { + ValidateUserDefinedConditionalLogicOperator(ExpressionType.OrElse, left.Type, right.Type, method); + returnType = (TypeUtils.IsNullableType(left.Type) && method.ReturnType == TypeUtils.GetNonNullableType(left.Type)) ? left.Type : method.ReturnType; + return new MethodBinaryExpression(ExpressionType.OrElse, left, right, returnType, method); + } + throw Error.BinaryOperatorNotDefined(ExpressionType.OrElse, left.Type, right.Type); + } + ValidateUserDefinedConditionalLogicOperator(ExpressionType.OrElse, left.Type, right.Type, method); + returnType = (TypeUtils.IsNullableType(left.Type) && method.ReturnType == TypeUtils.GetNonNullableType(left.Type)) ? left.Type : method.ReturnType; + return new MethodBinaryExpression(ExpressionType.OrElse, left, right, returnType, method); + } + + #endregion + + #region Coalescing Expressions + + //CONFORMING + public static BinaryExpression Coalesce(Expression left, Expression right) { + return Coalesce(left, right, null); + } + + //CONFORMING + public static BinaryExpression Coalesce(Expression left, Expression right, LambdaExpression conversion) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + + if (conversion == null) { + Type resultType = ValidateCoalesceArgTypes(left.Type, right.Type); + return new SimpleBinaryExpression(ExpressionType.Coalesce, left, right, resultType); + } + + if (left.Type.IsValueType && !TypeUtils.IsNullableType(left.Type)) { + throw Error.CoalesceUsedOnNonNullType(); + } + + Type delegateType = conversion.Type; + Debug.Assert(typeof(System.Delegate).IsAssignableFrom(delegateType) && delegateType != typeof(System.Delegate)); + MethodInfo method = delegateType.GetMethod("Invoke"); + if (method.ReturnType == typeof(void)) + throw Error.UserDefinedOperatorMustNotBeVoid(conversion); + ParameterInfo[] pms = method.GetParametersCached(); + Debug.Assert(pms.Length == conversion.Parameters.Count); + if (pms.Length != 1) + throw Error.IncorrectNumberOfMethodCallArguments(conversion); + // The return type must match exactly. + // CONSIDER: We could weaken this restriction and + // CONSIDER: say that the return type must be assignable to from + // CONSIDER: the return type of the lambda. + if (method.ReturnType != right.Type) { + throw Error.OperandTypesDoNotMatchParameters(ExpressionType.Coalesce, conversion.ToString()); + } + // The parameter of the conversion lambda must either be assignable + // from the erased or unerased type of the left hand side. + if (!ParameterIsAssignable(pms[0], TypeUtils.GetNonNullableType(left.Type)) && + !ParameterIsAssignable(pms[0], left.Type)) { + throw Error.OperandTypesDoNotMatchParameters(ExpressionType.Coalesce, conversion.ToString()); + } + return new CoalesceConversionBinaryExpression(left, right, conversion); + } + + //CONFORMING + private static Type ValidateCoalesceArgTypes(Type left, Type right) { + Type leftStripped = TypeUtils.GetNonNullableType(left); + if (left.IsValueType && !TypeUtils.IsNullableType(left)) { + throw Error.CoalesceUsedOnNonNullType(); + } else if (TypeUtils.IsNullableType(left) && TypeUtils.IsImplicitlyConvertible(right, leftStripped)) { + return leftStripped; + } else if (TypeUtils.IsImplicitlyConvertible(right, left)) { + return left; + } else if (TypeUtils.IsImplicitlyConvertible(leftStripped, right)) { + return right; + } else { + throw Error.ArgumentTypesMustMatch(); + } + } + + + + #endregion + + #region Arithmetic Expressions + + //CONFORMING + public static BinaryExpression Add(Expression left, Expression right) { + return Add(left, right, null); + } + //CONFORMING + public static BinaryExpression Add(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.Add, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.Add, "op_Addition", left, right, true); + } + return GetMethodBasedBinaryOperator(ExpressionType.Add, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression AddAssign(Expression left, Expression right) { + return AddAssign(left, right, null); + } + //CONFORMING + public static BinaryExpression AddAssign(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.AddAssign, left, right, left.Type); + } + return GetUserDefinedAssignOperatorOrThrow(ExpressionType.AddAssign, "op_Addition", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.AddAssign, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression AddAssignChecked(Expression left, Expression right) { + return AddAssignChecked(left, right, null); + } + //CONFORMING + public static BinaryExpression AddAssignChecked(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.AddAssignChecked, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.AddAssignChecked, "op_Addition", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.AddAssignChecked, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression AddChecked(Expression left, Expression right) { + return AddChecked(left, right, null); + } + //CONFORMING + public static BinaryExpression AddChecked(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.AddChecked, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.AddChecked, "op_Addition", left, right, false); + } + return GetMethodBasedBinaryOperator(ExpressionType.AddChecked, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression Subtract(Expression left, Expression right) { + return Subtract(left, right, null); + } + //CONFORMING + public static BinaryExpression Subtract(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.Subtract, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.Subtract, "op_Subtraction", left, right, true); + } + return GetMethodBasedBinaryOperator(ExpressionType.Subtract, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression SubtractAssign(Expression left, Expression right) { + return SubtractAssign(left, right, null); + } + //CONFORMING + public static BinaryExpression SubtractAssign(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.SubtractAssign, left, right, left.Type); + } + return GetUserDefinedAssignOperatorOrThrow(ExpressionType.SubtractAssign, "op_Subtraction", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.SubtractAssign, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression SubtractAssignChecked(Expression left, Expression right) { + return SubtractAssignChecked(left, right, null); + } + //CONFORMING + public static BinaryExpression SubtractAssignChecked(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.SubtractAssignChecked, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.SubtractAssignChecked, "op_Subtraction", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.SubtractAssignChecked, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression SubtractChecked(Expression left, Expression right) { + return SubtractChecked(left, right, null); + } + //CONFORMING + public static BinaryExpression SubtractChecked(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.SubtractChecked, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.SubtractChecked, "op_Subtraction", left, right, true); + } + return GetMethodBasedBinaryOperator(ExpressionType.SubtractChecked, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression Divide(Expression left, Expression right) { + return Divide(left, right, null); + } + //CONFORMING + public static BinaryExpression Divide(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.Divide, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.Divide, "op_Division", left, right, true); + } + return GetMethodBasedBinaryOperator(ExpressionType.Divide, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression DivideAssign(Expression left, Expression right) { + return DivideAssign(left, right, null); + } + //CONFORMING + public static BinaryExpression DivideAssign(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.DivideAssign, left, right, left.Type); + } + return GetUserDefinedAssignOperatorOrThrow(ExpressionType.DivideAssign, "op_Division", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.DivideAssign, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression Modulo(Expression left, Expression right) { + return Modulo(left, right, null); + } + //CONFORMING + public static BinaryExpression Modulo(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.Modulo, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.Modulo, "op_Modulus", left, right, true); + } + return GetMethodBasedBinaryOperator(ExpressionType.Modulo, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression ModuloAssign(Expression left, Expression right) { + return ModuloAssign(left, right, null); + } + //CONFORMING + public static BinaryExpression ModuloAssign(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.ModuloAssign, left, right, left.Type); + } + return GetUserDefinedAssignOperatorOrThrow(ExpressionType.ModuloAssign, "op_Modulus", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.ModuloAssign, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression Multiply(Expression left, Expression right) { + return Multiply(left, right, null); + } + //CONFORMING + public static BinaryExpression Multiply(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.Multiply, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.Multiply, "op_Multiply", left, right, true); + } + return GetMethodBasedBinaryOperator(ExpressionType.Multiply, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression MultiplyAssign(Expression left, Expression right) { + return MultiplyAssign(left, right, null); + } + //CONFORMING + public static BinaryExpression MultiplyAssign(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.MultiplyAssign, left, right, left.Type); + } + return GetUserDefinedAssignOperatorOrThrow(ExpressionType.MultiplyAssign, "op_Multiply", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.MultiplyAssign, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression MultiplyAssignChecked(Expression left, Expression right) { + return MultiplyAssignChecked(left, right, null); + } + //CONFORMING + public static BinaryExpression MultiplyAssignChecked(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.MultiplyAssignChecked, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.MultiplyAssignChecked, "op_Multiply", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.MultiplyAssignChecked, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression MultiplyChecked(Expression left, Expression right) { + return MultiplyChecked(left, right, null); + } + //CONFORMING + public static BinaryExpression MultiplyChecked(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.MultiplyChecked, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.MultiplyChecked, "op_Multiply", left, right, true); + } + return GetMethodBasedBinaryOperator(ExpressionType.MultiplyChecked, left, right, method, true); + } + + private static bool IsSimpleShift(Type left, Type right) { + return TypeUtils.IsInteger(left) + && TypeUtils.GetNonNullableType(right) == typeof(int) + && (left.IsNullableType() == right.IsNullableType()); + } + + //CONFORMING + public static BinaryExpression LeftShift(Expression left, Expression right) { + return LeftShift(left, right, null); + } + //CONFORMING + public static BinaryExpression LeftShift(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (IsSimpleShift(left.Type, right.Type)) { + return new SimpleBinaryExpression(ExpressionType.LeftShift, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.LeftShift, "op_LeftShift", left, right, true); + } + return GetMethodBasedBinaryOperator(ExpressionType.LeftShift, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression LeftShiftAssign(Expression left, Expression right) { + return LeftShiftAssign(left, right, null); + } + //CONFORMING + public static BinaryExpression LeftShiftAssign(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (IsSimpleShift(left.Type, right.Type)) { + return new SimpleBinaryExpression(ExpressionType.LeftShiftAssign, left, right, left.Type); + } + return GetUserDefinedAssignOperatorOrThrow(ExpressionType.LeftShiftAssign, "op_LeftShift", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.LeftShiftAssign, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression RightShift(Expression left, Expression right) { + return RightShift(left, right, null); + } + //CONFORMING + public static BinaryExpression RightShift(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (IsSimpleShift(left.Type, right.Type)) { + return new SimpleBinaryExpression(ExpressionType.RightShift, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.RightShift, "op_RightShift", left, right, true); + } + return GetMethodBasedBinaryOperator(ExpressionType.RightShift, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression RightShiftAssign(Expression left, Expression right) { + return RightShiftAssign(left, right, null); + } + //CONFORMING + public static BinaryExpression RightShiftAssign(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (IsSimpleShift(left.Type, right.Type)) { + return new SimpleBinaryExpression(ExpressionType.RightShiftAssign, left, right, left.Type); + } + return GetUserDefinedAssignOperatorOrThrow(ExpressionType.RightShiftAssign, "op_RightShift", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.RightShiftAssign, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression And(Expression left, Expression right) { + return And(left, right, null); + } + //CONFORMING + public static BinaryExpression And(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsIntegerOrBool(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.And, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.And, "op_BitwiseAnd", left, right, true); + } + return GetMethodBasedBinaryOperator(ExpressionType.And, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression AndAssign(Expression left, Expression right) { + return AndAssign(left, right, null); + } + //CONFORMING + public static BinaryExpression AndAssign(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsIntegerOrBool(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.AndAssign, left, right, left.Type); + } + return GetUserDefinedAssignOperatorOrThrow(ExpressionType.AndAssign, "op_BitwiseAnd", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.AndAssign, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression Or(Expression left, Expression right) { + return Or(left, right, null); + } + //CONFORMING + public static BinaryExpression Or(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsIntegerOrBool(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.Or, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.Or, "op_BitwiseOr", left, right, true); + } + return GetMethodBasedBinaryOperator(ExpressionType.Or, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression OrAssign(Expression left, Expression right) { + return OrAssign(left, right, null); + } + //CONFORMING + public static BinaryExpression OrAssign(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsIntegerOrBool(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.OrAssign, left, right, left.Type); + } + return GetUserDefinedAssignOperatorOrThrow(ExpressionType.OrAssign, "op_BitwiseOr", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.OrAssign, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression ExclusiveOr(Expression left, Expression right) { + return ExclusiveOr(left, right, null); + } + //CONFORMING + public static BinaryExpression ExclusiveOr(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsIntegerOrBool(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.ExclusiveOr, left, right, left.Type); + } + return GetUserDefinedBinaryOperatorOrThrow(ExpressionType.ExclusiveOr, "op_ExclusiveOr", left, right, true); + } + return GetMethodBasedBinaryOperator(ExpressionType.ExclusiveOr, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression ExclusiveOrAssign(Expression left, Expression right) { + return ExclusiveOrAssign(left, right, null); + } + //CONFORMING + public static BinaryExpression ExclusiveOrAssign(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + if (left.Type == right.Type && TypeUtils.IsIntegerOrBool(left.Type)) { + return new SimpleBinaryExpression(ExpressionType.ExclusiveOrAssign, left, right, left.Type); + } + return GetUserDefinedAssignOperatorOrThrow(ExpressionType.ExclusiveOrAssign, "op_ExclusiveOr", left, right, true); + } + return GetMethodBasedAssignOperator(ExpressionType.ExclusiveOrAssign, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression Power(Expression left, Expression right) { + return Power(left, right, null); + } + //CONFORMING + public static BinaryExpression Power(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + Type mathType = typeof(System.Math); + method = mathType.GetMethod("Pow", BindingFlags.Static | BindingFlags.Public); + if (method == null) { + throw Error.BinaryOperatorNotDefined(ExpressionType.Power, left.Type, right.Type); + } + } + return GetMethodBasedBinaryOperator(ExpressionType.Power, left, right, method, true); + } + + //CONFORMING + public static BinaryExpression PowerAssign(Expression left, Expression right) { + return PowerAssign(left, right, null); + } + //CONFORMING + public static BinaryExpression PowerAssign(Expression left, Expression right, MethodInfo method) { + RequiresCanRead(left, "left"); + RequiresCanWrite(left, "left"); + RequiresCanRead(right, "right"); + if (method == null) { + Type mathType = typeof(System.Math); + method = mathType.GetMethod("Pow", BindingFlags.Static | BindingFlags.Public); + if (method == null) { + throw Error.BinaryOperatorNotDefined(ExpressionType.PowerAssign, left.Type, right.Type); + } + } + return GetMethodBasedAssignOperator(ExpressionType.PowerAssign, left, right, method, true); + } + + #endregion + + #region ArrayIndex Expression + + //CONFORMING + public static BinaryExpression ArrayIndex(Expression array, Expression index) { + RequiresCanRead(array, "array"); + RequiresCanRead(index, "index"); + if (index.Type != typeof(int)) + throw Error.ArgumentMustBeArrayIndexType(); + + Type arrayType = array.Type; + if (!arrayType.IsArray) + throw Error.ArgumentMustBeArray(); + if (arrayType.GetArrayRank() != 1) + throw Error.IncorrectNumberOfIndexes(); + + return new SimpleBinaryExpression(ExpressionType.ArrayIndex, array, index, arrayType.GetElementType()); + } + + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/BlockExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/BlockExpression.cs new file mode 100644 index 0000000000..1b0343f7ac --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/BlockExpression.cs @@ -0,0 +1,608 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; + +using System.Dynamic.Utils; +using System.Threading; + +namespace System.Linq.Expressions { + /// + /// Defines a block where variables are defined. The compiler will + /// automatically close over these variables if they're referenced in a + /// nested LambdaExpession. + /// + /// Specialized subclasses exist which actually implement the storage + /// for the BlockExpression. + /// + public class BlockExpression : Expression { + + public ReadOnlyCollection Expressions { + get { return GetOrMakeExpressions(); } + } + + /// + /// The variables in this block. + /// + public ReadOnlyCollection Variables { + get { + return GetOrMakeVariables(); + } + } + + internal BlockExpression() { + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitBlock(this); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Block; + } + + protected override Type GetExpressionType() { + return GetExpression(ExpressionCount - 1).Type; + } + + internal virtual Expression GetExpression(int index) { + throw new NotImplementedException(); + } + + internal virtual int ExpressionCount { + get { + throw new NotImplementedException(); + } + } + + internal virtual ReadOnlyCollection GetOrMakeExpressions() { + throw new NotImplementedException(GetType().FullName); + } + + internal virtual ParameterExpression GetVariable(int index) { + throw new NotImplementedException(); + } + + internal virtual int VariableCount { + get { + return 0; + } + } + + internal virtual ReadOnlyCollection GetOrMakeVariables() { + return EmptyReadOnlyCollection.Instance; + } + + /// + /// Makes a copy of this node replacing the parameters/args with the provided values. The + /// shape of the parameters/args needs to match the shape of the current block - in other + /// words there should be the same # of parameters and args. + /// + /// parameters can be null in which case the existing parameters are used. + /// + /// This helper is provided to allow re-writing of nodes to not depend on the specific optimized + /// subclass of BlockExpression which is being used. + /// + internal virtual BlockExpression Rewrite(IList variables, Expression[] args) { + throw new NotImplementedException(); + } + + /// + /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T. + /// + /// This is similar to the ReturnReadOnly which only takes a single argument. This version + /// supports nodes which hold onto 5 Expressions and puts all of the arguments into the + /// ReadOnlyCollection. + /// + /// Ultimately this means if we create the ROC we will be slightly more wasteful as we'll + /// have a ROC + some fields in the type. The DLR internally avoids accessing anything + /// which would force the ROC to be created. + /// + /// This is used by BlockExpression5 and MethodCallExpression5. + /// + internal static ReadOnlyCollection ReturnReadOnlyExpressions(BlockExpression provider, ref object collection) { + Expression tObj = collection as Expression; + if (tObj != null) { + // otherwise make sure only one ROC ever gets exposed + Interlocked.CompareExchange( + ref collection, + new ReadOnlyCollection(new BlockExpressionList(provider, tObj)), + tObj + ); + } + + // and return what is not guaranteed to be a ROC + return (ReadOnlyCollection)collection; + } + } + + #region Specialized Subclasses + + internal sealed class Block2 : BlockExpression { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider + private readonly Expression _arg1; // storage for the 2nd argument. + + internal Block2(Expression arg0, Expression arg1) { + _arg0 = arg0; + _arg1 = arg1; + } + + internal override Expression GetExpression(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + default: throw new InvalidOperationException(); + } + } + + internal override int ExpressionCount { + get { + return 2; + } + } + + internal override ReadOnlyCollection GetOrMakeExpressions() { + return ReturnReadOnlyExpressions(this, ref _arg0); + } + + internal override BlockExpression Rewrite(IList variables, Expression[] args) { + Debug.Assert(args.Length == 2); + Debug.Assert(variables == null || variables.Count == 0); + + return new Block2(args[0], args[1]); + } + } + + internal sealed class Block3 : BlockExpression { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider + private readonly Expression _arg1, _arg2; // storage for the 2nd and 3rd arguments. + + internal Block3(Expression arg0, Expression arg1, Expression arg2) { + _arg0 = arg0; + _arg1 = arg1; + _arg2 = arg2; + } + + internal override Expression GetExpression(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + case 2: return _arg2; + default: throw new InvalidOperationException(); + } + } + + internal override int ExpressionCount { + get { + return 3; + } + } + + internal override ReadOnlyCollection GetOrMakeExpressions() { + return ReturnReadOnlyExpressions(this, ref _arg0); + } + + internal override BlockExpression Rewrite(IList variables, Expression[] args) { + Debug.Assert(args.Length == 3); + Debug.Assert(variables == null || variables.Count == 0); + + return new Block3(args[0], args[1], args[2]); + } + } + + internal sealed class Block4 : BlockExpression { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider + private readonly Expression _arg1, _arg2, _arg3; // storarg for the 2nd, 3rd, and 4th arguments. + + internal Block4(Expression arg0, Expression arg1, Expression arg2, Expression arg3) { + _arg0 = arg0; + _arg1 = arg1; + _arg2 = arg2; + _arg3 = arg3; + } + + internal override Expression GetExpression(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + case 2: return _arg2; + case 3: return _arg3; + default: throw new InvalidOperationException(); + } + } + + internal override int ExpressionCount { + get { + return 4; + } + } + + internal override ReadOnlyCollection GetOrMakeExpressions() { + return ReturnReadOnlyExpressions(this, ref _arg0); + } + + internal override BlockExpression Rewrite(IList variables, Expression[] args) { + Debug.Assert(args.Length == 4); + Debug.Assert(variables == null || variables.Count == 0); + + return new Block4(args[0], args[1], args[2], args[3]); + } + } + + internal sealed class Block5 : BlockExpression { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider + private readonly Expression _arg1, _arg2, _arg3, _arg4; // storage for the 2nd - 5th args. + + internal Block5(Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) { + _arg0 = arg0; + _arg1 = arg1; + _arg2 = arg2; + _arg3 = arg3; + _arg4 = arg4; + } + + internal override Expression GetExpression(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + case 2: return _arg2; + case 3: return _arg3; + case 4: return _arg4; + default: throw new InvalidOperationException(); + } + } + + internal override int ExpressionCount { + get { + return 5; + } + } + + internal override ReadOnlyCollection GetOrMakeExpressions() { + return ReturnReadOnlyExpressions(this, ref _arg0); + } + + internal override BlockExpression Rewrite(IList variables, Expression[] args) { + Debug.Assert(args.Length == 5); + Debug.Assert(variables == null || variables.Count == 0); + + return new Block5(args[0], args[1], args[2], args[3], args[4]); + } + } + + internal sealed class BlockN : BlockExpression { + private IList _expressions; // either the original IList or a ReadOnlyCollection if the user has accessed it. + + internal BlockN(IList expressions) { + Debug.Assert(expressions.Count != 0); + + _expressions = expressions; + } + + internal override Expression GetExpression(int index) { + Debug.Assert(index >= 0 && index < _expressions.Count); + + return _expressions[index]; + } + + internal override int ExpressionCount { + get { + return _expressions.Count; + } + } + + internal override ReadOnlyCollection GetOrMakeExpressions() { + return ReturnReadOnly(ref _expressions); + } + + internal override BlockExpression Rewrite(IList variables, Expression[] args) { + Debug.Assert(variables == null || variables.Count == 0); + + return new BlockN(args); + } + } + + internal class ScopeExpression : BlockExpression { + private IList _variables; // list of variables or ReadOnlyCollection if the user has accessed the ROC + + internal ScopeExpression(IList variables) { + _variables = variables; + } + + internal override int VariableCount { + get { + return _variables.Count; + } + } + + internal override ParameterExpression GetVariable(int index) { + return _variables[index]; + } + + internal override ReadOnlyCollection GetOrMakeVariables() { + return ReturnReadOnly(ref _variables); + } + + protected IList VariablesList { + get { + return _variables; + } + } + } + + internal sealed class Scope1 : ScopeExpression { + private object _body; + + internal Scope1(IList variables, Expression body) + : base(variables) { + _body = body; + } + + internal override Expression GetExpression(int index) { + switch (index) { + case 0: return ReturnObject(_body); + default: throw new InvalidOperationException(); + } + } + + internal override int ExpressionCount { + get { + return 1; + } + } + + internal override ReadOnlyCollection GetOrMakeExpressions() { + return ReturnReadOnlyExpressions(this, ref _body); + } + + internal override BlockExpression Rewrite(IList variables, Expression[] args) { + Debug.Assert(args.Length == 1); + Debug.Assert(variables == null || variables.Count == VariableCount); + + return new Scope1(variables ?? VariablesList, args[0]); + } + } + + internal sealed class ScopeN : ScopeExpression { + private IList _body; + + internal ScopeN(IList variables, IList body) + : base(variables) { + _body = body; + } + + internal override Expression GetExpression(int index) { + return _body[index]; + } + + internal override int ExpressionCount { + get { + return _body.Count; + } + } + + internal override ReadOnlyCollection GetOrMakeExpressions() { + return ReturnReadOnly(ref _body); + } + + internal override BlockExpression Rewrite(IList variables, Expression[] args) { + Debug.Assert(args.Length == ExpressionCount); + Debug.Assert(variables == null || variables.Count == VariableCount); + + return new ScopeN(variables ?? VariablesList, args); + } + } + + #endregion + + #region Block List Classes + + /// + /// Provides a wrapper around an IArgumentProvider which exposes the argument providers + /// members out as an IList of Expression. This is used to avoid allocating an array + /// which needs to be stored inside of a ReadOnlyCollection. Instead this type has + /// the same amount of overhead as an array without duplicating the storage of the + /// elements. This ensures that internally we can avoid creating and copying arrays + /// while users of the Expression trees also don't pay a size penalty for this internal + /// optimization. See IArgumentProvider for more general information on the Expression + /// tree optimizations being used here. + /// + internal class BlockExpressionList : IList { + private readonly BlockExpression _block; + private readonly Expression _arg0; + + internal BlockExpressionList(BlockExpression provider, Expression arg0) { + _block = provider; + _arg0 = arg0; + } + + #region IList Members + + public int IndexOf(Expression item) { + if (_arg0 == item) { + return 0; + } + + for (int i = 1; i < _block.ExpressionCount; i++) { + if (_block.GetExpression(i) == item) { + return i; + } + } + + return -1; + } + + public void Insert(int index, Expression item) { + throw new NotImplementedException(); + } + + public void RemoveAt(int index) { + throw new NotImplementedException(); + } + + public Expression this[int index] { + get { + if (index == 0) { + return _arg0; + } + + return _block.GetExpression(index); + } + set { + throw new NotImplementedException(); + } + } + + #endregion + + #region ICollection Members + + public void Add(Expression item) { + throw new NotImplementedException(); + } + + public void Clear() { + throw new NotImplementedException(); + } + + public bool Contains(Expression item) { + return IndexOf(item) != -1; + } + + public void CopyTo(Expression[] array, int arrayIndex) { + array[arrayIndex++] = _arg0; + for (int i = 1; i < _block.ExpressionCount; i++) { + array[arrayIndex++] = _block.GetExpression(i); + } + } + + public int Count { + get { return _block.ExpressionCount; } + } + + public bool IsReadOnly { + get { return true; } + } + + public bool Remove(Expression item) { + throw new NotImplementedException(); + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + yield return _arg0; + + for (int i = 1; i < _block.ExpressionCount; i++) { + yield return _block.GetExpression(i); + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + yield return _arg0; + + for (int i = 1; i < _block.ExpressionCount; i++) { + yield return _block.GetExpression(i); + } + } + + #endregion + } + + #endregion + + public partial class Expression { + + public static BlockExpression Block(Expression arg0, Expression arg1) { + RequiresCanRead(arg0, "arg0"); + RequiresCanRead(arg1, "arg1"); + + return new Block2(arg0, arg1); + } + + public static BlockExpression Block(Expression arg0, Expression arg1, Expression arg2) { + RequiresCanRead(arg0, "arg0"); + RequiresCanRead(arg1, "arg1"); + RequiresCanRead(arg2, "arg2"); + return new Block3(arg0, arg1, arg2); + } + + public static BlockExpression Block(Expression arg0, Expression arg1, Expression arg2, Expression arg3) { + RequiresCanRead(arg0, "arg0"); + RequiresCanRead(arg1, "arg1"); + RequiresCanRead(arg2, "arg2"); + RequiresCanRead(arg3, "arg3"); + return new Block4(arg0, arg1, arg2, arg3); + } + + public static BlockExpression Block(Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) { + RequiresCanRead(arg0, "arg0"); + RequiresCanRead(arg1, "arg1"); + RequiresCanRead(arg2, "arg2"); + RequiresCanRead(arg3, "arg3"); + RequiresCanRead(arg4, "arg4"); + + return new Block5(arg0, arg1, arg2, arg3, arg4); + } + + public static BlockExpression Block(params Expression[] expressions) { + switch (expressions.Length) { + case 2: return Block(expressions[0], expressions[1]); + case 3: return Block(expressions[0], expressions[1], expressions[2]); + case 4: return Block(expressions[0], expressions[1], expressions[2], expressions[3]); + case 5: return Block(expressions[0], expressions[1], expressions[2], expressions[3], expressions[4]); + default: + ContractUtils.RequiresNotEmpty(expressions, "expressions"); + RequiresCanRead(expressions, "expressions"); + return new BlockN(expressions); + } + } + + /// + /// Creates a list of expressions whose value is the value of the last expression. + /// + public static BlockExpression Block(IEnumerable expressions) { + return Block(EmptyReadOnlyCollection.Instance, expressions); + } + + public static BlockExpression Block(IEnumerable variables, params Expression[] expressions) { + return Block(variables, (IEnumerable)expressions); + } + + public static BlockExpression Block(IEnumerable variables, IEnumerable expressions) { + ContractUtils.RequiresNotNull(expressions, "expressions"); + RequiresCanRead(expressions, "expressions"); + var expressionList = expressions.ToReadOnly(); + ContractUtils.RequiresNotEmpty(expressionList, "expressions"); + var varList = variables.ToReadOnly(); + ContractUtils.RequiresNotNullItems(varList, "variables"); + Expression.RequireVariablesNotByRef(varList, "variables"); + + if (expressionList.Count == 1) { + return new Scope1(varList, expressionList[0]); + } else { + return new ScopeN(varList, expressionList); + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/CatchBlock.cs b/ndp/fx/src/core/microsoft/scripting/Ast/CatchBlock.cs new file mode 100644 index 0000000000..f9e7f47a07 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/CatchBlock.cs @@ -0,0 +1,86 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + + // TODO: probably should not have Annotations, since it's part of TryExpression + // They can either go there on on the body + public sealed class CatchBlock { + private readonly Type _test; + private readonly ParameterExpression _var; + private readonly Expression _body; + private readonly Expression _filter; + + internal CatchBlock(Type test, ParameterExpression variable, Expression body, Expression filter) { + _test = test; + _var = variable; + _body = body; + _filter = filter; + } + + public ParameterExpression Variable { + get { return _var; } + } + + public Type Test { + get { return _test; } + } + + public Expression Body { + get { return _body; } + } + + public Expression Filter { + get { + return _filter; + } + } + } + + public partial class Expression { + public static CatchBlock Catch(Type type, Expression body) { + return MakeCatchBlock(type, null, body, null); + } + + public static CatchBlock Catch(ParameterExpression variable, Expression body) { + ContractUtils.RequiresNotNull(variable, "variable"); + return MakeCatchBlock(variable.Type, variable, body, null); + } + + public static CatchBlock Catch(Type type, Expression body, Expression filter) { + return MakeCatchBlock(type, null, body, filter); + } + + public static CatchBlock Catch(ParameterExpression variable, Expression body, Expression filter) { + ContractUtils.RequiresNotNull(variable, "variable"); + return MakeCatchBlock(variable.Type, variable, body, filter); + } + + public static CatchBlock MakeCatchBlock(Type type, ParameterExpression variable, Expression body, Expression filter) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.Requires(variable == null || variable.Type.Equals(type), "variable"); + Expression.RequireVariableNotByRef(variable, "variable"); + RequiresCanRead(body, "body"); + if (filter != null) { + RequiresCanRead(filter, "filter"); + ContractUtils.Requires(filter.Type == typeof(bool), Strings.ArgumentMustBeBoolean); + } + + return new CatchBlock(type, variable, body, filter); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/ConditionalExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/ConditionalExpression.cs new file mode 100644 index 0000000000..4f75d26b78 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/ConditionalExpression.cs @@ -0,0 +1,97 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; +using System.Text; + +namespace System.Linq.Expressions { + //CONFORMING + public class ConditionalExpression : Expression { + private readonly Expression _test; + private readonly Expression _true; + + internal ConditionalExpression(Expression test, Expression ifTrue) { + _test = test; + _true = ifTrue; + } + + internal static ConditionalExpression Make(Expression test, Expression ifTrue, Expression ifFalse) { + if (ifFalse == DefaultExpression.VoidInstance) { + return new ConditionalExpression(test, ifTrue); + } else { + return new FullConditionalExpression(test, ifTrue, ifFalse); + } + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Conditional; + } + + protected override Type GetExpressionType() { + return IfTrue.Type; + } + + public Expression Test { + get { return _test; } + } + + public Expression IfTrue { + get { return _true; } + } + + public Expression IfFalse { + get { return GetFalse(); } + } + + internal virtual Expression GetFalse() { + return DefaultExpression.VoidInstance; + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitConditional(this); + } + } + + internal class FullConditionalExpression : ConditionalExpression { + private readonly Expression _false; + + internal FullConditionalExpression(Expression test, Expression ifTrue, Expression ifFalse) + : base(test, ifTrue) { + _false = ifFalse; + } + + internal override Expression GetFalse() { + return _false; + } + } + + public partial class Expression { + //CONFORMING + public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse) { + RequiresCanRead(test, "test"); + RequiresCanRead(ifTrue, "ifTrue"); + RequiresCanRead(ifFalse, "ifFalse"); + + if (test.Type != typeof(bool)) { + throw Error.ArgumentMustBeBoolean(); + } + if (ifTrue.Type != ifFalse.Type) { + throw Error.ArgumentTypesMustMatch(); + } + + return ConditionalExpression.Make(test, ifTrue, ifFalse); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/ConstantExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/ConstantExpression.cs new file mode 100644 index 0000000000..2eaa95908e --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/ConstantExpression.cs @@ -0,0 +1,127 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; +using System.Text; +using System.Dynamic; + +namespace System.Linq.Expressions { + //CONFORMING + public class ConstantExpression : Expression { + internal static readonly ConstantExpression TrueLiteral = ConstantExpression.Make(true, typeof(bool)); + internal static readonly ConstantExpression FalseLiteral = ConstantExpression.Make(false, typeof(bool)); + internal static readonly ConstantExpression NullLiteral = ConstantExpression.Make(null, typeof(object)); + internal static readonly ConstantExpression EmptyStringLiteral = ConstantExpression.Make(String.Empty, typeof(string)); + internal static readonly ConstantExpression[] IntCache = new ConstantExpression[100]; + + // TODO: Constant subclass that stores the unboxed value? + private readonly object _value; + + internal ConstantExpression(object value) { + _value = value; + } + + internal static ConstantExpression Make(object value, Type type) { + if ((value == null && type == typeof(object)) || (value != null && value.GetType() == type)) { + return new ConstantExpression(value); + } else { + return new TypedConstantExpression(value, type); + } + } + + protected override Type GetExpressionType() { + if(_value == null) { + return typeof(object); + } + return _value.GetType(); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Constant; + } + + public object Value { + get { return _value; } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitConstant(this); + } + } + + internal class TypedConstantExpression : ConstantExpression { + private readonly Type _type; + + internal TypedConstantExpression(object value, Type type) + : base(value) { + _type = type; + } + + protected override Type GetExpressionType() { + return _type; + } + } + + public partial class Expression { + public static ConstantExpression Constant(bool value) { + return value ? ConstantExpression.TrueLiteral : ConstantExpression.FalseLiteral; + } + + //CONFORMING + public static ConstantExpression Constant(object value) { + if (value == null) { + return ConstantExpression.NullLiteral; + } + + Type t = value.GetType(); + if (!t.IsEnum) { + switch (Type.GetTypeCode(t)) { + case TypeCode.Boolean: + return Constant((bool)value); + case TypeCode.Int32: + int x = (int)value; + int cacheIndex = x + 2; + if (cacheIndex >= 0 && cacheIndex < ConstantExpression.IntCache.Length) { + ConstantExpression res; + if ((res = ConstantExpression.IntCache[cacheIndex]) == null) { + ConstantExpression.IntCache[cacheIndex] = res = ConstantExpression.Make(x, typeof(int)); + } + return res; + } + break; + case TypeCode.String: + if (String.IsNullOrEmpty((string)value)) { + return ConstantExpression.EmptyStringLiteral; + } + break; + } + } + + return ConstantExpression.Make(value, value == null ? typeof(object) : value.GetType()); + } + + //CONFORMING + public static ConstantExpression Constant(object value, Type type) { + ContractUtils.RequiresNotNull(type, "type"); + if (value == null && type.IsValueType && !TypeUtils.IsNullableType(type)) { + throw Error.ArgumentTypesMustMatch(); + } + if (value != null && !type.IsAssignableFrom(value.GetType())) { + throw Error.ArgumentTypesMustMatch(); + } + return ConstantExpression.Make(value, type); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/DebugInfoExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/DebugInfoExpression.cs new file mode 100644 index 0000000000..4fd0c64e73 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/DebugInfoExpression.cs @@ -0,0 +1,129 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + /// + /// Wraps an expression, emitting a sequence point around it + /// + /// This allows the debugger to highlight the correct source code when + /// debugging. + /// + public sealed class DebugInfoExpression : Expression { + private readonly Expression _expression; + private readonly int _startLine, _startColumn, _endLine, _endColumn; + private readonly SymbolDocumentInfo _document; + + internal DebugInfoExpression(Expression body, SymbolDocumentInfo document, int startLine, int startColumn, int endLine, int endColumn) { + _expression = body; + _document = document; + _startLine = startLine; + _startColumn = startColumn; + _endLine = endLine; + _endColumn = endColumn; + } + + protected override Type GetExpressionType() { + return _expression.Type; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.DebugInfo; + } + + public override bool CanReduce { + get { + return true; + } + } + + public int StartLine { + get { return _startLine; } + } + + public int StartColumn { + get { return _startColumn; } + } + + public int EndLine { + get { return _endLine; } + } + + public int EndColumn { + get { return _endColumn; } + } + + /// + /// Information about the source file + /// + public SymbolDocumentInfo Document { + get { return _document; } + } + + /// + /// The underlying expression to be evaluated + /// + public Expression Expression { + get { return _expression; } + } + + /// + /// Returns the underlying expression + /// + public override Expression Reduce() { + return _expression; + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitDebugInfo(this); + } + } + + public partial class Expression { + public static DebugInfoExpression DebugInfo(Expression body, SymbolDocumentInfo document, int startLine, int startColumn, int endLine, int endColumn) { + ContractUtils.RequiresNotNull(body, "body"); + ContractUtils.RequiresNotNull(document, "document"); + + ValidateSpan(startLine, startColumn, endLine, endColumn); + + return new DebugInfoExpression(body, document, startLine, startColumn, endLine, endColumn); + } + + private static void ValidateSpan(int startLine, int startColumn, int endLine, int endColumn) { + if (startLine < 1) { + throw Error.OutOfRange("startLine", 1); + } + if (startColumn < 1) { + throw Error.OutOfRange("startColumn", 1); + } + if (endLine < 1) { + throw Error.OutOfRange("endLine", 1); + } + if (endColumn < 1) { + throw Error.OutOfRange("endColumn", 1); + } + if (startLine > endLine) { + throw Error.StartEndMustBeOrdered(); + } + if (startLine == endLine && startColumn > endColumn) { + throw Error.StartEndMustBeOrdered(); + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/DefaultExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/DefaultExpression.cs new file mode 100644 index 0000000000..33292b34be --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/DefaultExpression.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + // Represents default(T) in the tree + public sealed class DefaultExpression : Expression { + internal static readonly DefaultExpression VoidInstance = new DefaultExpression(typeof(void)); + + private readonly Type _type; + + internal DefaultExpression(Type type) { + _type = type; + } + + protected override Type GetExpressionType() { + return _type; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Default; + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitDefault(this); + } + } + + public partial class Expression { + public static DefaultExpression Empty() { + return DefaultExpression.VoidInstance; + } + + public static DefaultExpression Default(Type type) { + if (type == typeof(void)) { + return Empty(); + } + return new DefaultExpression(type); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/DynamicExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/DynamicExpression.cs new file mode 100644 index 0000000000..f7bd4cc368 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/DynamicExpression.cs @@ -0,0 +1,581 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions.Compiler; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + /// + /// A late-bound operation. The precise semantics is determined by the + /// Binder. If the Binder is one of the standard dynamic operations + /// supported by MetaObject, the run-time behavior can be infered from the + /// StandardAction + /// + public class DynamicExpression : Expression, IArgumentProvider { + private readonly CallSiteBinder _binder; + private readonly Type _delegateType; + + internal DynamicExpression(Type delegateType, CallSiteBinder binder) { + Debug.Assert(delegateType.GetMethod("Invoke").GetReturnType() == typeof(object) || GetType() != typeof(DynamicExpression)); + _delegateType = delegateType; + _binder = binder; + } + + internal static DynamicExpression Make(Type returnType, Type delegateType, CallSiteBinder binder, ReadOnlyCollection arguments) { + if (returnType == typeof(object)) { + return new DynamicExpressionN(delegateType, binder, arguments); + } else { + return new TypedDynamicExpressionN(returnType, delegateType, binder, arguments); + } + } + + protected override Type GetExpressionType() { + return typeof(object); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Dynamic; + } + /// + /// The CallSiteBinder, which determines the runtime behavior of the + /// dynamic site + /// + public CallSiteBinder Binder { + get { return _binder; } + } + + /// + /// The type of the CallSite's delegate + /// + public Type DelegateType { + get { return _delegateType; } + } + + /// + /// Arguments to the dynamic operation + /// + public ReadOnlyCollection Arguments { + get { return GetOrMakeArguments(); } + } + + internal virtual ReadOnlyCollection GetOrMakeArguments() { + throw new NotImplementedException(); + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitDynamic(this); + } + + /// + /// Makes a copy of this node replacing the args with the provided values. The + /// number of the args needs to match the number of the current block. + /// + /// This helper is provided to allow re-writing of nodes to not depend on the specific optimized + /// subclass of DynamicExpression which is being used. + /// + internal virtual DynamicExpression Rewrite(Expression[] args) { + throw new NotImplementedException(); + } + + #region IArgumentProvider Members + + Expression IArgumentProvider.GetArgument(int index) { + throw new NotImplementedException(); + } + + int IArgumentProvider.ArgumentCount { + get { throw new NotImplementedException(); } + } + + #endregion + } + + #region Specialized Subclasses + + internal class DynamicExpressionN : DynamicExpression, IArgumentProvider { + private IList _arguments; // storage for the original IList or ROC. See IArgumentProvider for more info. + + internal DynamicExpressionN(Type delegateType, CallSiteBinder binder, IList arguments) + : base(delegateType, binder) { + _arguments = arguments; + } + + Expression IArgumentProvider.GetArgument(int index) { + return _arguments[index]; + } + + int IArgumentProvider.ArgumentCount { + get { + return _arguments.Count; + } + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(ref _arguments); + } + + internal override DynamicExpression Rewrite(Expression[] args) { + Debug.Assert(args.Length == ((IArgumentProvider)this).ArgumentCount); + + return new DynamicExpressionN(DelegateType, Binder, args); + } + } + + internal class TypedDynamicExpressionN : DynamicExpressionN { + private readonly Type _returnType; + + internal TypedDynamicExpressionN(Type returnType, Type delegateType, CallSiteBinder binder, IList arguments) + : base(delegateType, binder, arguments) { + Debug.Assert(delegateType.GetMethod("Invoke").GetReturnType() == returnType); + _returnType = returnType; + } + + protected override Type GetExpressionType() { + return _returnType; + } + + internal override DynamicExpression Rewrite(Expression[] args) { + Debug.Assert(args.Length == ((IArgumentProvider)this).ArgumentCount); + + return new TypedDynamicExpressionN(GetExpressionType(), DelegateType, Binder, args); + } + } + + internal class DynamicExpression1 : DynamicExpression, IArgumentProvider { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider for more info. + + internal DynamicExpression1(Type delegateType, CallSiteBinder binder, Expression arg0) + : base(delegateType, binder) { + _arg0 = arg0; + } + + Expression IArgumentProvider.GetArgument(int index) { + switch(index) { + case 0: return ReturnObject(_arg0); + default: throw new InvalidOperationException(); + } + } + + int IArgumentProvider.ArgumentCount { + get { + return 1; + } + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(this, ref _arg0); + } + + internal override DynamicExpression Rewrite(Expression[] args) { + Debug.Assert(args.Length == 1); + + return new DynamicExpression1(DelegateType, Binder, args[0]); + } + } + + internal sealed class TypedDynamicExpression1 : DynamicExpression1 { + private readonly Type _retType; + + internal TypedDynamicExpression1(Type retType, Type delegateType, CallSiteBinder binder, Expression arg0) + : base(delegateType, binder, arg0) { + _retType = retType; + } + + protected override Type GetExpressionType() { + return _retType; + } + + internal override DynamicExpression Rewrite(Expression[] args) { + Debug.Assert(args.Length == 1); + + return new TypedDynamicExpression1(GetExpressionType(), DelegateType, Binder, args[0]); + } + } + + internal class DynamicExpression2 : DynamicExpression, IArgumentProvider { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider for more info. + private readonly Expression _arg1; // storage for the 2nd argument + + internal DynamicExpression2(Type delegateType, CallSiteBinder binder, Expression arg0, Expression arg1) + : base(delegateType, binder) { + _arg0 = arg0; + _arg1 = arg1; + } + + Expression IArgumentProvider.GetArgument(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + default: throw new InvalidOperationException(); + } + } + + int IArgumentProvider.ArgumentCount { + get { + return 2; + } + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(this, ref _arg0); + } + + internal override DynamicExpression Rewrite(Expression[] args) { + Debug.Assert(args.Length == 2); + + return new DynamicExpression2(DelegateType, Binder, args[0], args[1]); + } + } + + internal sealed class TypedDynamicExpression2 : DynamicExpression2 { + private readonly Type _retType; + + internal TypedDynamicExpression2(Type retType, Type delegateType, CallSiteBinder binder, Expression arg0, Expression arg1) + : base(delegateType, binder, arg0, arg1) { + _retType = retType; + } + + protected override Type GetExpressionType() { + return _retType; + } + + internal override DynamicExpression Rewrite(Expression[] args) { + Debug.Assert(args.Length == 2); + + return new TypedDynamicExpression2(GetExpressionType(), DelegateType, Binder, args[0], args[1]); + } + } + + internal class DynamicExpression3 : DynamicExpression, IArgumentProvider { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider for more info. + private readonly Expression _arg1, _arg2; // storage for the 2nd & 3rd arguments + + internal DynamicExpression3(Type delegateType, CallSiteBinder binder, Expression arg0, Expression arg1, Expression arg2) + : base(delegateType, binder) { + _arg0 = arg0; + _arg1 = arg1; + _arg2 = arg2; + } + + Expression IArgumentProvider.GetArgument(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + case 2: return _arg2; + default: throw new InvalidOperationException(); + } + } + + int IArgumentProvider.ArgumentCount { + get { + return 3; + } + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(this, ref _arg0); + } + + internal override DynamicExpression Rewrite(Expression[] args) { + Debug.Assert(args.Length == 3); + + return new DynamicExpression3(DelegateType, Binder, args[0], args[1], args[2]); + } + } + + internal sealed class TypedDynamicExpression3 : DynamicExpression3 { + private readonly Type _retType; + + internal TypedDynamicExpression3(Type retType, Type delegateType, CallSiteBinder binder, Expression arg0, Expression arg1, Expression arg2) + : base(delegateType, binder, arg0, arg1, arg2) { + _retType = retType; + } + + protected override Type GetExpressionType() { + return _retType; + } + + + internal override DynamicExpression Rewrite(Expression[] args) { + Debug.Assert(args.Length == 3); + + return new TypedDynamicExpression3(GetExpressionType(), DelegateType, Binder, args[0], args[1], args[2]); + } + } + + internal class DynamicExpression4 : DynamicExpression, IArgumentProvider { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider for more info. + private readonly Expression _arg1, _arg2, _arg3; // storage for the 2nd - 4th arguments + + internal DynamicExpression4(Type delegateType, CallSiteBinder binder, Expression arg0, Expression arg1, Expression arg2, Expression arg3) + : base(delegateType, binder) { + _arg0 = arg0; + _arg1 = arg1; + _arg2 = arg2; + _arg3 = arg3; + } + + Expression IArgumentProvider.GetArgument(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + case 2: return _arg2; + case 3: return _arg3; + default: throw new InvalidOperationException(); + } + } + + int IArgumentProvider.ArgumentCount { + get { + return 4; + } + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(this, ref _arg0); + } + + internal override DynamicExpression Rewrite(Expression[] args) { + Debug.Assert(args.Length == 4); + + return new DynamicExpression4(DelegateType, Binder, args[0], args[1], args[2], args[3]); + } + } + + internal sealed class TypedDynamicExpression4 : DynamicExpression4 { + private readonly Type _retType; + + internal TypedDynamicExpression4(Type retType, Type delegateType, CallSiteBinder binder, Expression arg0, Expression arg1, Expression arg2, Expression arg3) + : base(delegateType, binder, arg0, arg1, arg2, arg3) { + _retType = retType; + } + + protected override Type GetExpressionType() { + return _retType; + } + + internal override DynamicExpression Rewrite(Expression[] args) { + Debug.Assert(args.Length == 4); + + return new TypedDynamicExpression4(GetExpressionType(), DelegateType, Binder, args[0], args[1], args[2], args[3]); + } + } + + #endregion + + public partial class Expression { + + public static DynamicExpression MakeDynamic(Type delegateType, CallSiteBinder binder, params Expression[] arguments) { + return MakeDynamic(delegateType, binder, (IEnumerable)arguments); + } + + public static DynamicExpression MakeDynamic(Type delegateType, CallSiteBinder binder, IEnumerable arguments) { + ContractUtils.RequiresNotNull(delegateType, "delegateType"); + ContractUtils.RequiresNotNull(binder, "binder"); + ContractUtils.Requires(delegateType.IsSubclassOf(typeof(Delegate)), "delegateType", Strings.TypeMustBeDerivedFromSystemDelegate); + + var method = GetValidMethodForDynamic(delegateType); + + var args = arguments.ToReadOnly(); + ValidateArgumentTypes(method, ExpressionType.Dynamic, ref args); + + return DynamicExpression.Make(method.GetReturnType(), delegateType, binder, args); + } + + private static System.Reflection.MethodInfo GetValidMethodForDynamic(Type delegateType) { + var method = delegateType.GetMethod("Invoke"); + var pi = method.GetParametersCached(); + ContractUtils.Requires(pi.Length > 0 && pi[0].ParameterType == typeof(CallSite), "delegateType", Strings.FirstArgumentMustBeCallSite); + return method; + } + + public static DynamicExpression Dynamic(CallSiteBinder binder, Type returnType, params Expression[] arguments) { + return Dynamic(binder, returnType, (IEnumerable)arguments); + } + + public static DynamicExpression Dynamic(CallSiteBinder binder, Type returnType, Expression arg0) { + ContractUtils.RequiresNotNull(binder, "binder"); + ValidateDynamicArgument(arg0); + + DelegateHelpers.TypeInfo info = DelegateHelpers.GetNextTypeInfo( + returnType, + DelegateHelpers.GetNextTypeInfo( + arg0.Type, + DelegateHelpers.NextTypeInfo(typeof(CallSite)) + ) + ); + + Type delegateType = info.DelegateType; + if (delegateType == null) { + delegateType = info.MakeDelegateType(returnType, arg0); + } + + if (returnType == typeof(object)) { + return new DynamicExpression1(delegateType, binder, arg0); + } else { + return new TypedDynamicExpression1(returnType, delegateType, binder, arg0); + } + } + + public static DynamicExpression Dynamic(CallSiteBinder binder, Type returnType, Expression arg0, Expression arg1) { + ContractUtils.RequiresNotNull(binder, "binder"); + ValidateDynamicArgument(arg0); + ValidateDynamicArgument(arg1); + + DelegateHelpers.TypeInfo info = DelegateHelpers.GetNextTypeInfo( + returnType, + DelegateHelpers.GetNextTypeInfo( + arg1.Type, + DelegateHelpers.GetNextTypeInfo( + arg0.Type, + DelegateHelpers.NextTypeInfo(typeof(CallSite)) + ) + ) + ); + + Type delegateType = info.DelegateType; + if (delegateType == null) { + delegateType = info.MakeDelegateType(returnType, arg0, arg1); + } + + if (returnType == typeof(object)) { + return new DynamicExpression2(delegateType, binder, arg0, arg1); + } else { + return new TypedDynamicExpression2(returnType, delegateType, binder, arg0, arg1); + } + } + + public static DynamicExpression Dynamic(CallSiteBinder binder, Type returnType, Expression arg0, Expression arg1, Expression arg2) { + ContractUtils.RequiresNotNull(binder, "binder"); + ValidateDynamicArgument(arg0); + ValidateDynamicArgument(arg1); + ValidateDynamicArgument(arg2); + + DelegateHelpers.TypeInfo info = DelegateHelpers.GetNextTypeInfo( + returnType, + DelegateHelpers.GetNextTypeInfo( + arg2.Type, + DelegateHelpers.GetNextTypeInfo( + arg1.Type, + DelegateHelpers.GetNextTypeInfo( + arg0.Type, + DelegateHelpers.NextTypeInfo(typeof(CallSite)) + ) + ) + ) + ); + + Type delegateType = info.DelegateType; + if (delegateType == null) { + delegateType = info.MakeDelegateType(returnType, arg0, arg1, arg2); + } + + if (returnType == typeof(object)) { + return new DynamicExpression3(delegateType, binder, arg0, arg1, arg2); + } else { + return new TypedDynamicExpression3(returnType, delegateType, binder, arg0, arg1, arg2); + } + } + + public static DynamicExpression Dynamic(CallSiteBinder binder, Type returnType, Expression arg0, Expression arg1, Expression arg2, Expression arg3) { + ContractUtils.RequiresNotNull(binder, "binder"); + ValidateDynamicArgument(arg0); + ValidateDynamicArgument(arg1); + ValidateDynamicArgument(arg2); + ValidateDynamicArgument(arg3); + + DelegateHelpers.TypeInfo info = DelegateHelpers.GetNextTypeInfo( + returnType, + DelegateHelpers.GetNextTypeInfo( + arg3.Type, + DelegateHelpers.GetNextTypeInfo( + arg2.Type, + DelegateHelpers.GetNextTypeInfo( + arg1.Type, + DelegateHelpers.GetNextTypeInfo( + arg0.Type, + DelegateHelpers.NextTypeInfo(typeof(CallSite)) + ) + ) + ) + ) + ); + + Type delegateType = info.DelegateType; + if (delegateType == null) { + delegateType = info.MakeDelegateType(returnType, arg0, arg1, arg2, arg3); + } + + if (returnType == typeof(object)) { + return new DynamicExpression4(delegateType, binder, arg0, arg1, arg2, arg3); + } else { + return new TypedDynamicExpression4(returnType, delegateType, binder, arg0, arg1, arg2, arg3); + } + } + + public static DynamicExpression Dynamic(CallSiteBinder binder, Type returnType, IEnumerable arguments) { + ContractUtils.RequiresNotNull(arguments, "arguments"); + ContractUtils.RequiresNotNull(returnType, "returnType"); + + var args = arguments.ToReadOnly(); + ContractUtils.RequiresNotEmpty(args, "args"); + return MakeDynamic(binder, returnType, args); + } + + private static DynamicExpression MakeDynamic(CallSiteBinder binder, Type returnType, ReadOnlyCollection args) { + ContractUtils.RequiresNotNull(binder, "binder"); + + for (int i = 0; i < args.Count; i++) { + Expression arg = args[i]; + + ValidateDynamicArgument(arg); + } + + Type delegateType = DelegateHelpers.MakeCallSiteDelegate(args, returnType); + + // Since we made a delegate with argument types that exactly match, + // we can skip delegate and argument validation + if (returnType == typeof(object)) { + switch (args.Count) { + case 1: return new DynamicExpression1(delegateType, binder, args[0]); + case 2: return new DynamicExpression2(delegateType, binder, args[0], args[1]); + case 3: return new DynamicExpression3(delegateType, binder, args[0], args[1], args[2]); + case 4: return new DynamicExpression4(delegateType, binder, args[0], args[1], args[2], args[3]); + default: return new DynamicExpressionN(delegateType, binder, args); + } + } else { + switch (args.Count) { + case 1: return new TypedDynamicExpression1(returnType, delegateType, binder, args[0]); + case 2: return new TypedDynamicExpression2(returnType, delegateType, binder, args[0], args[1]); + case 3: return new TypedDynamicExpression3(returnType, delegateType, binder, args[0], args[1], args[2]); + case 4: return new TypedDynamicExpression4(returnType, delegateType, binder, args[0], args[1], args[2], args[3]); + default: return new TypedDynamicExpressionN(returnType, delegateType, binder, args); + } + } + } + + private static void ValidateDynamicArgument(Expression arg) { + RequiresCanRead(arg, "arguments"); + var type = arg.Type; + ContractUtils.RequiresNotNull(type, "type"); + TypeUtils.ValidateType(type); + ContractUtils.Requires(type != typeof(void), Strings.ArgumentTypeCannotBeVoid); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/ElementInit.cs b/ndp/fx/src/core/microsoft/scripting/Ast/ElementInit.cs new file mode 100644 index 0000000000..ecec6a3a3f --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/ElementInit.cs @@ -0,0 +1,94 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Text; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + //CONFORMING + // TODO: If annotations is added here, make sure MemberInitExpression.Reduce + // methods and ExpressionVisitor.Visit methods preserve it + public sealed class ElementInit : IArgumentProvider { + private MethodInfo _addMethod; + private ReadOnlyCollection _arguments; + + internal ElementInit(MethodInfo addMethod, ReadOnlyCollection arguments) { + _addMethod = addMethod; + _arguments = arguments; + } + public MethodInfo AddMethod { + get { return _addMethod; } + } + public ReadOnlyCollection Arguments { + get { return _arguments; } + } + + Expression IArgumentProvider.GetArgument(int index) { + return _arguments[index]; + } + + int IArgumentProvider.ArgumentCount { + get { + return _arguments.Count; + } + } + + public override string ToString() { + return ExpressionStringBuilder.ElementInitBindingToString(this); + } + } + + + public partial class Expression { + //CONFORMING + public static ElementInit ElementInit(MethodInfo addMethod, params Expression[] arguments) { + return ElementInit(addMethod, arguments as IEnumerable); + } + //CONFORMING + public static ElementInit ElementInit(MethodInfo addMethod, IEnumerable arguments) { + ContractUtils.RequiresNotNull(addMethod, "addMethod"); + ContractUtils.RequiresNotNull(arguments, "arguments"); + RequiresCanRead(arguments, "arguments"); + ValidateElementInitAddMethodInfo(addMethod); + ReadOnlyCollection argumentsRO = arguments.ToReadOnly(); + ValidateArgumentTypes(addMethod, ExpressionType.Call, ref argumentsRO); + return new ElementInit(addMethod, argumentsRO); + } + + //CONFORMING + private static void ValidateElementInitAddMethodInfo(MethodInfo addMethod) { + ValidateMethodInfo(addMethod); + ParameterInfo[] pis = addMethod.GetParametersCached(); + if (pis.Length == 0) { + throw Error.ElementInitializerMethodWithZeroArgs(); + } + if (!addMethod.Name.Equals("Add", StringComparison.OrdinalIgnoreCase)) { + throw Error.ElementInitializerMethodNotAdd(); + } + if (addMethod.IsStatic) { + throw Error.ElementInitializerMethodStatic(); + } + foreach (ParameterInfo pi in pis) { + if (pi.ParameterType.IsByRef) { + throw Error.ElementInitializerMethodNoRefOutParam(pi.Name, addMethod.Name); + } + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/Expression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/Expression.cs new file mode 100644 index 0000000000..40f6a67a6c --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/Expression.cs @@ -0,0 +1,625 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Reflection; +using System.Dynamic.Utils; +using System.Text; +using System.Threading; +using System.Diagnostics; +using System.IO; + +namespace System.Linq.Expressions { + /// + /// Expression is the base type for all nodes in Expression Trees + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + public abstract partial class Expression { + private static CacheDict _LambdaDelegateCache = new CacheDict(40); + private static CacheDict, LambdaExpression>> _exprCtors; + private static MethodInfo _lambdaCtorMethod; + + // protected ctors are part of API surface area + +#if !MICROSOFT_SCRIPTING_CORE + /****************************************************************************************************** + * BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG + * + * We need to switch to using ConditionalWeakHandle whenever that becomes available in our tools drop. + * Once that's done we can remove WeakDictionary entirely and just use mscorlib's ConditionalWeakTable. + */ + + [Obsolete] + private class WeakDictionary : IDictionary { + // The one and only comparer instance. + static readonly IEqualityComparer comparer = new WeakComparer(); + + IDictionary dict = new Dictionary(comparer); + int version, cleanupVersion; + + #if SILVERLIGHT // GC + WeakReference cleanupGC = new WeakReference(new object()); + #else + int cleanupGC = 0; + #endif + + public WeakDictionary() { + } + + #region IDictionary Members + + public void Add(TKey key, TValue value) { + CheckCleanup(); + + // If the WeakHash already holds this value as a key, it will lead to a circular-reference and result + // in the objects being kept alive forever. The caller needs to ensure that this cannot happen. + Debug.Assert(!dict.ContainsKey(value)); + + dict.Add(new WeakObject(key), value); + } + + public bool ContainsKey(TKey key) { + // We dont have to worry about creating "new WeakObject(key)" since the comparer + // can compare raw objects with WeakObject. + return dict.ContainsKey(key); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix + public ICollection Keys { + get { + // TODO: + throw new NotImplementedException(); + } + } + + public bool Remove(TKey key) { + return dict.Remove(key); + } + + public bool TryGetValue(TKey key, out TValue value) { + return dict.TryGetValue(key, out value); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix + public ICollection Values { + get { + // TODO: + throw new NotImplementedException(); + } + } + + public TValue this[TKey key] { + get { + return dict[key]; + } + set { + // If the WeakHash already holds this value as a key, it will lead to a circular-reference and result + // in the objects being kept alive forever. The caller needs to ensure that this cannot happen. + Debug.Assert(!dict.ContainsKey(value)); + + dict[new WeakObject(key)] = value; + } + } + + /// + /// Check if any of the keys have gotten collected + /// + /// Currently, there is also no guarantee of how long the values will be kept alive even after the keys + /// get collected. This could be fixed by triggerring CheckCleanup() to be called on every garbage-collection + /// by having a dummy watch-dog object with a finalizer which calls CheckCleanup(). + /// + void CheckCleanup() { + version++; + + long change = version - cleanupVersion; + + // Cleanup the table if it is a while since we have done it last time. + // Take the size of the table into account. + if (change > 1234 + dict.Count / 2) { + // It makes sense to do the cleanup only if a GC has happened in the meantime. + // WeakReferences can become zero only during the GC. + + bool garbage_collected; + #if SILVERLIGHT // GC.CollectionCount + garbage_collected = !cleanupGC.IsAlive; + if (garbage_collected) cleanupGC = new WeakReference(new object()); + #else + int currentGC = GC.CollectionCount(0); + garbage_collected = currentGC != cleanupGC; + if (garbage_collected) cleanupGC = currentGC; + #endif + if (garbage_collected) { + Cleanup(); + cleanupVersion = version; + } else { + cleanupVersion += 1234; + } + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")] + private void Cleanup() { + + int liveCount = 0; + int emptyCount = 0; + + foreach (WeakObject w in dict.Keys) { + if (w.Target != null) + liveCount++; + else + emptyCount++; + } + + // Rehash the table if there is a significant number of empty slots + if (emptyCount > liveCount / 4) { + Dictionary newtable = new Dictionary(liveCount + liveCount / 4, comparer); + + foreach (WeakObject w in dict.Keys) { + object target = w.Target; + + if (target != null) + newtable[w] = dict[w]; + + } + + dict = newtable; + } + } + #endregion + + #region ICollection> Members + + public void Add(KeyValuePair item) { + // TODO: + throw new NotImplementedException(); + } + + public void Clear() { + // TODO: + throw new NotImplementedException(); + } + + public bool Contains(KeyValuePair item) { + // TODO: + throw new NotImplementedException(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) { + // TODO: + throw new NotImplementedException(); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix + public int Count { + get { + // TODO: + throw new NotImplementedException(); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix + public bool IsReadOnly { + get { + // TODO: + throw new NotImplementedException(); + } + } + + public bool Remove(KeyValuePair item) { + // TODO: + throw new NotImplementedException(); + } + + #endregion + + #region IEnumerable> Members + + public IEnumerator> GetEnumerator() { + // TODO: + throw new NotImplementedException(); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + // TODO: + throw new NotImplementedException(); + } + + #endregion + + // WeakComparer treats WeakObject as transparent envelope + sealed class WeakComparer : IEqualityComparer { + bool IEqualityComparer.Equals(T x, T y) { + WeakObject wx = x as WeakObject; + if (wx != null) + x = wx.Target; + + WeakObject wy = y as WeakObject; + if (wy != null) + y = wy.Target; + + return Object.Equals(x, y); + } + + int IEqualityComparer.GetHashCode(T obj) { + WeakObject wobj = obj as WeakObject; + if (wobj != null) + return wobj.GetHashCode(); + + return (obj == null) ? 0 : obj.GetHashCode(); + } + } + + internal class WeakObject { + WeakReference weakReference; + int hashCode; + + public WeakObject(T obj) { + weakReference = new WeakReference(obj, true); + hashCode = (obj == null) ? 0 : obj.GetHashCode(); + } + + public T Target { + get { + return (T)weakReference.Target; + } + } + + public override int GetHashCode() { + return hashCode; + } + + public override bool Equals(object obj) { + object target = weakReference.Target; + if (target == null) { + return false; + } + + return ((T)target).Equals(obj); + } + } + } + +#pragma warning disable 612 + private static WeakDictionary _legacyCtorSupportTable; +#pragma warning restore 612 + + // LinqV1 ctor + [Obsolete("use a different constructor that does not take ExpressionType. Then override GetExpressionType and GetNodeKind to provide the values that would be specified to this constructor.")] + protected Expression(ExpressionType nodeType, Type type) { + // Can't enforce anything that V1 didn't + if(_legacyCtorSupportTable == null) { + Interlocked.CompareExchange( + ref _legacyCtorSupportTable, + new WeakDictionary(), + null + ); + } + + _legacyCtorSupportTable[this] = new ExtensionInfo(nodeType, type); + } +#endif + + protected Expression() { + } + + //CONFORMING + public ExpressionType NodeType { + get { return GetNodeKind(); } + } + + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + public Type Type { + get { return GetExpressionType(); } + } + + /// + /// Indicates that the node can be reduced to a simpler node. If this + /// returns true, Reduce() can be called to produce the reduced form. + /// + public virtual bool CanReduce { + get { return false; } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + protected virtual ExpressionType GetNodeKind() { +#if !MICROSOFT_SCRIPTING_CORE + ExtensionInfo extInfo; + if (_legacyCtorSupportTable.TryGetValue(this, out extInfo)) { + return extInfo.NodeType; + } +#endif + + // the base type failed to overload GetNodeKind + throw new InvalidOperationException(); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + protected virtual Type GetExpressionType() { +#if !MICROSOFT_SCRIPTING_CORE + ExtensionInfo extInfo; + if (_legacyCtorSupportTable.TryGetValue(this, out extInfo)) { + return extInfo.Type; + } +#endif + + // the base type failed to overload GetExpressionType + throw new InvalidOperationException(); + } + + /// + /// Reduces this node to a simpler expression. If CanReduce returns + /// true, this should return a valid expression. This method is + /// allowed to return another node which itself must be reduced. + /// + /// the reduced expression + public virtual Expression Reduce() { + ContractUtils.Requires(!CanReduce, "this", Strings.ReducibleMustOverrideReduce); + return this; + } + + /// + /// Override this to provide logic to walk the node's children. A + /// typical implementation will call visitor.Visit on each of its + /// children, and if any of them change, should return a new copy of + /// itself with the modified children. + /// + /// The default implementation will reduce the node and then walk it + /// This will throw an exception if the node is not reducible + /// + protected internal virtual Expression VisitChildren(ExpressionVisitor visitor) { + ContractUtils.Requires(CanReduce, "this", Strings.MustBeReducible); + return visitor.Visit(ReduceExtensions()); + } + + // Visitor pattern: this is the method that dispatches back to the visitor + // NOTE: this is unlike the Visit method, which provides a hook for + // derived classes to extend the visitor framework to be able to walk + // themselves + internal virtual Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitExtension(this); + } + + /// + /// Reduces this node to a simpler expression. If CanReduce returns + /// true, this should return a valid expression. This method is + /// allowed to return another node which itself must be reduced. + /// + /// Unlike Reduce, this method checks that the reduced node satisfies + /// certain invaraints. + /// + /// the reduced expression + public Expression ReduceAndCheck() { + ContractUtils.Requires(CanReduce, "this", Strings.MustBeReducible); + + var newNode = Reduce(); + + // 1. Reduction must return a new, non-null node + // 2. Reduction must return a new node whose result type can be assigned to the type of the original node + ContractUtils.Requires(newNode != null && newNode != this, "this", Strings.MustReduceToDifferent); + ContractUtils.Requires(TypeUtils.AreReferenceAssignable(Type, newNode.Type), "this", Strings.ReducedNotCompatible); + return newNode; + } + + /// + /// Reduces the expression to a known node type (i.e. not an Extension node) + /// or simply returns the expression if it is already a known type. + /// + /// the reduced expression + public Expression ReduceExtensions() { + var node = this; + while (node.NodeType == ExpressionType.Extension) { + node = node.ReduceAndCheck(); + } + return node; + } + + //CONFORMING + public override string ToString() { + return ExpressionStringBuilder.ExpressionToString(this); + } + +#if MICROSOFT_SCRIPTING_CORE + public string Dump { + get { + using (System.IO.StringWriter writer = new System.IO.StringWriter(CultureInfo.CurrentCulture)) { + ExpressionWriter.Dump(this, GetType().Name, writer); + return writer.ToString(); + } + } + } + + public void DumpExpression(string descr, TextWriter writer) { + ExpressionWriter.Dump(this, descr, writer); + } +#endif + + /// + /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T. + /// + /// This is called from various methods where we internally hold onto an IList of T + /// or a ROC of T. We check to see if we've already returned a ROC of T and if so + /// simply return the other one. Otherwise we do a thread-safe replacement of hte + /// list w/ a ROC which wraps it. + /// + /// Ultimately this saves us from having to allocate a ReadOnlyCollection for our + /// data types because the compiler is capable of going directly to the IList of T. + /// + internal static ReadOnlyCollection ReturnReadOnly(ref IList collection) { + IList value = collection; + + // if it's already read-only just return it. + ReadOnlyCollection res = value as ReadOnlyCollection; + if (res != null) { + return res; + } + + // otherwise make sure only ROC every gets exposed + Interlocked.CompareExchange>( + ref collection, + value.ToReadOnly(), + value + ); + + // and return it + return (ReadOnlyCollection)collection; + } + + /// + /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T. + /// + /// This is similar to the ReturnReadOnly of T. This version supports nodes which hold + /// onto multiple Expressions where one is typed to object. That object field holds either + /// an expression or a ReadOnlyCollection of Expressions. When it holds a ReadOnlyCollection + /// the IList which backs it is a ListArgumentProvider which uses the Expression which + /// implements IArgumentProvider to get 2nd and additional values. The ListArgumentProvider + /// continues to hold onto the 1st expression. + /// + /// This enables users to get the ReadOnlyCollection w/o it consuming more memory than if + /// it was just an array. Meanwhile The DLR internally avoids accessing which would force + /// the ROC to be created resulting in a typical memory savings. + /// + internal static ReadOnlyCollection ReturnReadOnly(IArgumentProvider provider, ref object collection) { + Expression tObj = collection as Expression; + if (tObj != null) { + // otherwise make sure only one ROC ever gets exposed + Interlocked.CompareExchange( + ref collection, + new ReadOnlyCollection(new ListArgumentProvider(provider, tObj)), + tObj + ); + } + + // and return what is not guaranteed to be a ROC + return (ReadOnlyCollection)collection; + } + + /// + /// Helper which is used for specialized subtypes which use ReturnReadOnly(ref object, ...). + /// This is the reverse version of ReturnReadOnly which takes an IArgumentProvider. + /// + /// This is used to return the 1st argument. The 1st argument is typed as object and either + /// contains a ReadOnlyCollection or the Expression. We check for the Expression and if it's + /// present we return that, otherwise we return the 1st element of the ReadOnlyCollection. + /// + internal static T ReturnObject(object collectionOrT) where T : class { + T t = collectionOrT as T; + if (t != null) { + return t; + } + + return ((ReadOnlyCollection)collectionOrT)[0]; + } + + private static void RequiresCanRead(Expression expression, string paramName) { + if (expression == null) { + throw new ArgumentNullException(paramName); + } + + // validate that we can read the node + switch (expression.NodeType) { + case ExpressionType.Index: + IndexExpression index = (IndexExpression)expression; + if (index.Indexer != null && !index.Indexer.CanRead) { + throw new ArgumentException(Strings.ExpressionMustBeReadable, paramName); + } + break; + case ExpressionType.MemberAccess: + MemberExpression member = (MemberExpression)expression; + MemberInfo memberInfo = member.Member; + if (memberInfo.MemberType == MemberTypes.Property) { + PropertyInfo prop = (PropertyInfo)memberInfo; + if (!prop.CanRead) { + throw new ArgumentException(Strings.ExpressionMustBeReadable, paramName); + } + } + break; + } + } + + private static void RequiresCanRead(IEnumerable items, string paramName) { + if (items != null) { + // this is called a lot, avoid allocating an enumerator if we can... + IList listItems = items as IList; + if (listItems != null) { + for (int i = 0; i < listItems.Count; i++) { + RequiresCanRead(listItems[i], paramName); + } + return; + } + + foreach (var i in items) { + RequiresCanRead(i, paramName); + } + } + } + private static void RequiresCanWrite(Expression expression, string paramName) { + if (expression == null) { + throw new ArgumentNullException(paramName); + } + + bool canWrite = false; + switch (expression.NodeType) { + case ExpressionType.Index: + IndexExpression index = (IndexExpression)expression; + if (index.Indexer != null) { + canWrite = index.Indexer.CanWrite; + } else { + canWrite = true; + } + break; + case ExpressionType.MemberAccess: + MemberExpression member = (MemberExpression)expression; + switch (member.Member.MemberType) { + case MemberTypes.Property: + PropertyInfo prop = (PropertyInfo)member.Member; + canWrite = prop.CanWrite; + break; + case MemberTypes.Field: + FieldInfo field = (FieldInfo)member.Member; + canWrite = !(field.IsInitOnly || field.IsLiteral); + break; + } + break; + case ExpressionType.Parameter: + case ExpressionType.ArrayIndex: + canWrite = true; + break; + } + + if (!canWrite) { + throw new ArgumentException(Strings.ExpressionMustBeWriteable, paramName); + } + } + +#if !MICROSOFT_SCRIPTING_CORE + struct ExtensionInfo { + public ExtensionInfo(ExpressionType nodeType, Type type) { + NodeType = nodeType; + Type = type; + } + + internal readonly ExpressionType NodeType; + internal readonly Type Type; + } +#endif + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionStringBuilder.cs b/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionStringBuilder.cs new file mode 100644 index 0000000000..bedd72f6bd --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionStringBuilder.cs @@ -0,0 +1,906 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Dynamic; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.CodeDom.Compiler; +using System.Text; +using System.Globalization; +using System.Runtime.CompilerServices; + +namespace System.Linq.Expressions { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + internal sealed class ExpressionStringBuilder : ExpressionVisitor { + [Flags] + private enum Flow { + None, + Space, + NewLine, + + Break = 0x8000 // newline if column > MaxColumn + }; + + private const int Tab = 4; + private const int MaxColumn = 80; + + private StringBuilder _out; + private int _column; + + private int _depth; + private Flow _flow; + private bool _inBlock; + + //Associate every unique label or anonymous parameter in the tree with an inteter. + //The label is displayed as Label_#. + private Dictionary _ids; + + private ExpressionStringBuilder(StringBuilder sb) { + _out = sb; + } + + private void AddLabel(LabelTarget label) { + if (_ids == null) { + _ids = new Dictionary(); + _ids.Add(label, 0); + } else { + if (!_ids.ContainsKey(label)) { + _ids.Add(label, _ids.Count); + } + } + } + + private int GetLabelId(LabelTarget label) { + if (_ids == null) { + _ids = new Dictionary(); + AddLabel(label); + return 0; + } else { + int id; + if (!_ids.TryGetValue(label, out id)) { + //label is met the first time + id = _ids.Count; + AddLabel(label); + } + return id; + } + } + + private void AddParam(ParameterExpression p) { + if (_ids == null) { + _ids = new Dictionary(); + _ids.Add(_ids, 0); + } else { + if (!_ids.ContainsKey(p)) { + _ids.Add(p, _ids.Count); + } + } + } + + private int GetParamId(ParameterExpression p) { + if (_ids == null) { + _ids = new Dictionary(); + AddParam(p); + return 0; + } else { + int id; + if (!_ids.TryGetValue(p, out id)) { + //p is met the first time + id = _ids.Count; + AddParam(p); + } + return id; + } + } + + private int Depth { + get { return _depth; } + } + + private void Indent() { + _depth += Tab; + } + private void Dedent() { + _depth -= Tab; + } + + private void NewLine() { + _flow = Flow.NewLine; + } + + #region The printing code + + private void Out(string s) { + Out(Flow.None, s, Flow.None); + } + + private void Out(Flow before, string s) { + Out(before, s, Flow.None); + } + + private void Out(string s, Flow after) { + Out(Flow.None, s, after); + } + + private void Out(Flow before, string s, Flow after) { + switch (GetFlow(before)) { + case Flow.None: + break; + case Flow.Space: + Write(" "); + break; + case Flow.NewLine: + WriteLine(); + Write(new String(' ', Depth)); + break; + } + Write(s); + _flow = after; + } + + private void WriteLine() { + _out.Append(Environment.NewLine); + _column = 0; + } + private void Write(string s) { + _out.Append(s); + _column += s.Length; + } + + private Flow GetFlow(Flow flow) { + Flow last; + + last = CheckBreak(_flow); + flow = CheckBreak(flow); + + // Get the biggest flow that is requested None < Space < NewLine + return (Flow)System.Math.Max((int)last, (int)flow); + } + + private Flow CheckBreak(Flow flow) { + if ((flow & Flow.Break) != 0) { + if (_column > (MaxColumn + Depth)) { + flow = Flow.NewLine; + } else { + flow &= ~Flow.Break; + } + } + return flow; + } + + #endregion + + #region Output an expresstion tree to a string + + /// + /// Output a given expression tree to a string. + /// + internal static string ExpressionToString(Expression node) { + Debug.Assert(node != null); + ExpressionStringBuilder esb = new ExpressionStringBuilder(new StringBuilder()); + return esb.OutputExpressionToString(node); + } + + /// + /// Output a given member binding to a string. + /// + internal static string MemberBindingToString(MemberBinding node) { + Debug.Assert(node != null); + ExpressionStringBuilder esb = new ExpressionStringBuilder(new StringBuilder()); + return esb.OutputMemberBindingToString(node); + } + + /// + /// Output a given ElementInit to a string. + /// + internal static string ElementInitBindingToString(ElementInit node) { + Debug.Assert(node != null); + ExpressionStringBuilder esb = new ExpressionStringBuilder(new StringBuilder()); + return esb.OutputElementInitToString(node); + } + + private string OutputExpressionToString(Expression node) { + Visit(node); + return _out.ToString(); + } + + private string OutputMemberBindingToString(MemberBinding node) { + VisitMemberBinding(node); + return _out.ToString(); + } + + private string OutputElementInitToString(ElementInit node) { + VisitElementInit(node); + return _out.ToString(); + } + + // More proper would be to make this a virtual method on Action + private static string FormatBinder(CallSiteBinder binder) { + ConvertBinder convert; + GetMemberBinder getMember; + SetMemberBinder setMember; + DeleteMemberBinder deleteMember; + GetIndexBinder getIndex; + SetIndexBinder setIndex; + DeleteIndexBinder deleteIndex; + InvokeMemberBinder call; + InvokeBinder invoke; + CreateInstanceBinder create; + UnaryOperationBinder unary; + BinaryOperationBinder binary; + + if ((convert = binder as ConvertBinder) != null) { + return "Convert " + convert.Type; + } else if ((getMember = binder as GetMemberBinder) != null) { + return "GetMember " + getMember.Name; + } else if ((setMember = binder as SetMemberBinder) != null) { + return "SetMember " + setMember.Name; + } else if ((deleteMember = binder as DeleteMemberBinder) != null) { + return "DeleteMember " + deleteMember.Name; + } else if ((getIndex = binder as GetIndexBinder) != null) { + return "GetIndex"; + } else if ((setIndex = binder as SetIndexBinder) != null) { + return "SetIndex"; + } else if ((deleteIndex = binder as DeleteIndexBinder) != null) { + return "DeleteIndex"; + } else if ((call = binder as InvokeMemberBinder) != null) { + return "Call " + call.Name; + } else if ((invoke = binder as InvokeBinder) != null) { + return "Invoke"; + } else if ((create = binder as CreateInstanceBinder) != null) { + return "Create"; + } else if ((unary = binder as UnaryOperationBinder) != null) { + return unary.Operation.ToString(); + } else if ((binary = binder as BinaryOperationBinder) != null) { + return binary.Operation.ToString(); + } else { + return "CallSiteBinder"; + } + } + + private void VisitExpressions(char open, IList expressions, string separate) where T : Expression { + VisitExpressions(open, expressions, false, separate); + } + + private void VisitExpressions(char open, IList expressions, bool forceMultiline, string separate) where T : Expression { + VisitExpressions(open, expressions, forceMultiline, (e) => Visit(e), separate); + } + + private void VisitExpressions(char open, IList expressions, bool forceMultiline, Action visit, string separate) { + Out(open.ToString()); + if (expressions != null) { + if (forceMultiline) { + Indent(); + bool isFirst = true; + foreach (T e in expressions) { + if (isFirst) { + NewLine(); + isFirst = false; + } else { + Out(separate, Flow.NewLine); + } + visit(e); + } + Dedent(); + } else { + bool isFirst = true; + foreach (T e in expressions) { + if (isFirst) { + isFirst = false; + } else { + Out(separate, Flow.Space); + } + visit(e); + } + } + } + + string close; + switch (open) { + case '(': close = ")"; break; + case '{': close = "}"; break; + case '[': close = "]"; break; + case '<': close = ">"; break; + default: throw Assert.Unreachable; + } + if (forceMultiline) { + Out(Flow.NewLine, close, Flow.Break); + } else { + Out(close, Flow.Break); + } + } + + protected internal override Expression VisitDynamic(DynamicExpression node) { + Out(FormatBinder(node.Binder)); + VisitExpressions('(', node.Arguments, ","); + return node; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + protected internal override Expression VisitBinary(BinaryExpression node) { + if (node.NodeType == ExpressionType.ArrayIndex) { + Visit(node.Left); + Out("["); + Visit(node.Right); + Out("]"); + } else { + string op; + switch (node.NodeType) { + case ExpressionType.Assign: op = "="; break; + case ExpressionType.Equal: op = "=="; break; + case ExpressionType.NotEqual: op = "!="; break; + case ExpressionType.AndAlso: op = "AndAlso"; break; + case ExpressionType.OrElse: op = "OrElse"; break; + case ExpressionType.GreaterThan: op = ">"; break; + case ExpressionType.LessThan: op = "<"; break; + case ExpressionType.GreaterThanOrEqual: op = ">="; break; + case ExpressionType.LessThanOrEqual: op = "<="; break; + case ExpressionType.Add: op = "+"; break; + case ExpressionType.AddAssign: op = "+="; break; + case ExpressionType.AddAssignChecked: op = "+="; break; + case ExpressionType.AddChecked: op = "+"; break; + case ExpressionType.Subtract: op = "-"; break; + case ExpressionType.SubtractAssign: op = "-="; break; + case ExpressionType.SubtractAssignChecked: op = "-="; break; + case ExpressionType.SubtractChecked: op = "-"; break; + case ExpressionType.Divide: op = "/"; break; + case ExpressionType.DivideAssign: op = "/="; break; + case ExpressionType.Modulo: op = "%"; break; + case ExpressionType.ModuloAssign: op = "%="; break; + case ExpressionType.Multiply: op = "*"; break; + case ExpressionType.MultiplyAssign: op = "*="; break; + case ExpressionType.MultiplyAssignChecked: op = "*="; break; + case ExpressionType.MultiplyChecked: op = "*"; break; + case ExpressionType.LeftShift: op = "<<"; break; + case ExpressionType.LeftShiftAssign: op = "<<="; break; + case ExpressionType.RightShift: op = ">>"; break; + case ExpressionType.RightShiftAssign: op = ">>="; break; + case ExpressionType.And: + if (node.Type == typeof(bool) || node.Type == typeof(bool?)) { + op = "And"; + } else { + op = "&"; + } + break; + case ExpressionType.AndAssign: + if (node.Type == typeof(bool) || node.Type == typeof(bool?)) { + op = "&&="; + } else { + op = "&="; + } + break; + case ExpressionType.Or: + if (node.Type == typeof(bool) || node.Type == typeof(bool?)) { + op = "Or"; + } else { + op = "|"; + } + break; + case ExpressionType.OrAssign: + if (node.Type == typeof(bool) || node.Type == typeof(bool?)) { + op = "||="; + } else { op = "|="; } + break; + case ExpressionType.ExclusiveOr: op = "^"; break; + case ExpressionType.ExclusiveOrAssign: op = "^="; break; + case ExpressionType.Power: op = "^"; break; + case ExpressionType.PowerAssign: op = "**="; break; + //TODO: need to handle conversion lambda + case ExpressionType.Coalesce: op = "??"; break; + + default: + throw new InvalidOperationException(); + } + Out("("); + Visit(node.Left); + Out(Flow.Space, op, Flow.Space | Flow.Break); + Visit(node.Right); + Out(")"); + } + return node; + } + + protected internal override Expression VisitParameter(ParameterExpression node) { + if (node.IsByRef) { + Out("ref "); + } + if (String.IsNullOrEmpty(node.Name)) { + int id = GetParamId(node); + Out("Param_" + id); + } else { + Out(node.Name); + } + return node; + } + + protected internal override Expression VisitLambda(Expression node) { + if (node.Parameters.Count == 1) { + // p => body + Visit(node.Parameters[0]); + } else { + // (p1, p2, ..., pn) => body + VisitExpressions('(', node.Parameters, false, ","); + } + Out(" => "); + Visit(node.Body); + return node; + } + + protected internal override Expression VisitListInit(ListInitExpression node) { + Visit(node.NewExpression); + Out(" {"); + for (int i = 0, n = node.Initializers.Count; i < n; i++) { + if (i > 0) { + Out(", "); + } + Out(node.Initializers[i].ToString()); + } + Out("}"); + return node; + } + + protected internal override Expression VisitConditional(ConditionalExpression node) { + Out("IIF("); + Visit(node.Test); + Out(", "); + Visit(node.IfTrue); + Out(", "); + Visit(node.IfFalse); + Out(")"); + return node; + } + + protected internal override Expression VisitConstant(ConstantExpression node) { + + if (node.Value != null) { + string sValue = node.Value.ToString(); + if (node.Value is string) { + Out("\""); + Out(sValue); + Out("\""); + } else if (sValue == node.Value.GetType().ToString()) { + Out("value("); + Out(sValue); + Out(")"); + } else { + Out(sValue); + } + } else { + Out("null"); + } + return node; + } + + protected internal override Expression VisitDebugInfo(DebugInfoExpression node) { + Visit(node.Expression); + return node; + } + + protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) { + VisitExpressions('(', node.Variables, ","); + return node; + } + + // Prints ".instanceField" or "declaringType.staticField" + private void OutMember(Expression instance, MemberInfo member) { + if (instance != null) { + Visit(instance); + Out("." + member.Name); + } else { + // For static members, include the type name + Out(member.DeclaringType.Name + "." + member.Name); + } + } + + protected internal override Expression VisitMember(MemberExpression node) { + OutMember(node.Expression, node.Member); + return node; + } + + protected internal override Expression VisitMemberInit(MemberInitExpression node) { + if (node.NewExpression.Arguments.Count == 0 && + node.NewExpression.Type.Name.Contains("<")) { + // anonymous type constructor + Out("new"); + } else { + Visit(node.NewExpression); + } + Out(" {"); + for (int i = 0, n = node.Bindings.Count; i < n; i++) { + MemberBinding b = node.Bindings[i]; + if (i > 0) { + Out(", "); + } + VisitMemberBinding(b); + } + Out("}"); + return node; + } + + protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) { + Out(assignment.Member.Name); + Out(" = "); + Visit(assignment.Expression); + return assignment; + } + + protected override MemberListBinding VisitMemberListBinding(MemberListBinding binding) { + Out(binding.Member.Name); + Out(" = {"); + for (int i = 0, n = binding.Initializers.Count; i < n; i++) { + if (i > 0) { + Out(", "); + } + VisitElementInit(binding.Initializers[i]); + } + Out("}"); + return binding; + } + + protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) { + Out(binding.Member.Name); + Out(" = {"); + for (int i = 0, n = binding.Bindings.Count; i < n; i++) { + if (i > 0) { + Out(", "); + } + VisitMemberBinding(binding.Bindings[i]); + } + Out("}"); + return binding; + } + + protected override ElementInit VisitElementInit(ElementInit initializer) { + Out(initializer.AddMethod.ToString()); + VisitExpressions('(', initializer.Arguments, ","); + return initializer; + } + + protected internal override Expression VisitInvocation(InvocationExpression node) { + Out("Invoke("); + Visit(node.Expression); + for (int i = 0, n = node.Arguments.Count; i < n; i++) { + Out(", "); + Visit(node.Arguments[i]); + } + Out(")"); + return node; + } + + protected internal override Expression VisitMethodCall(MethodCallExpression node) { + int start = 0; + Expression ob = node.Object; + + if (Attribute.GetCustomAttribute(node.Method, typeof(ExtensionAttribute)) != null) { + start = 1; + ob = node.Arguments[0]; + } + + if (ob != null) { + Visit(ob); + Out("."); + } + Out(node.Method.Name); + Out("("); + for (int i = start, n = node.Arguments.Count; i < n; i++) { + if (i > start) + Out(", "); + Visit(node.Arguments[i]); + } + Out(")"); + return node; + } + + protected internal override Expression VisitNewArray(NewArrayExpression node) { + switch (node.NodeType) { + case ExpressionType.NewArrayBounds: + // new MyType[](expr1, expr2) + Out("new " + node.Type.ToString()); + VisitExpressions('(', node.Expressions, ","); + break; + case ExpressionType.NewArrayInit: + // new [] {expr1, expr2} + Out("new [] "); + VisitExpressions('{', node.Expressions, ","); + break; + } + return node; + } + + protected internal override Expression VisitNew(NewExpression node) { + Out("new " + node.Type.Name); + Out("("); + for (int i = 0; i < node.Arguments.Count; i++) { + if (i > 0) { + Out(", "); + } + if (node.Members != null) { + Out(node.Members[i].Name); + Out(" = "); + } + Visit(node.Arguments[i]); + } + Out(")"); + return node; + } + + protected internal override Expression VisitTypeBinary(TypeBinaryExpression node) { + Out("("); + Visit(node.Expression); + switch (node.NodeType) { + case ExpressionType.TypeIs: + Out(Flow.Space, "Is", Flow.Space); + break; + case ExpressionType.TypeEqual: + Out(Flow.Space, "TypeEqual", Flow.Space); + break; + } + Out(node.TypeOperand.Name); + Out(")"); + return node; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + protected internal override Expression VisitUnary(UnaryExpression node) { + switch (node.NodeType) { + case ExpressionType.TypeAs: + Out("("); + break; + case ExpressionType.Not: + Out("Not("); + break; + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + Out("-"); + break; + case ExpressionType.UnaryPlus: + Out("+"); + break; + case ExpressionType.Quote: + break; + case ExpressionType.Throw: + Out("throw("); + break; + case ExpressionType.Increment: + Out("Increment("); + break; + case ExpressionType.Decrement: + Out("Decrement("); + break; + case ExpressionType.PreIncrementAssign: + Out("++"); + break; + case ExpressionType.PreDecrementAssign: + Out("--"); + break; + default: + Out(node.NodeType.ToString()); + Out("("); + break; + } + + Visit(node.Operand); + + switch (node.NodeType) { + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + case ExpressionType.UnaryPlus: + case ExpressionType.PreDecrementAssign: + case ExpressionType.PreIncrementAssign: + case ExpressionType.Quote: + break; + case ExpressionType.TypeAs: + Out(Flow.Space, "As", Flow.Space | Flow.Break); + Out(node.Type.Name); + Out(")"); + break; + case ExpressionType.PostIncrementAssign: + Out("++"); + break; + case ExpressionType.PostDecrementAssign: + Out("--"); + break; + default: + Out(")"); + break; + } + return node; + } + + protected internal override Expression VisitBlock(BlockExpression node) { + if (!_inBlock) { + Indent(); + } + bool oldBlock = _inBlock; + _inBlock = true; + + Out("{"); + Indent(); + foreach (var v in node.Variables) { + NewLine(); + Out("var "); + Visit(v); + Out(";"); + } + foreach (var e in node.Expressions) { + NewLine(); + Visit(e); + Out(";"); + } + Dedent(); + Out(Flow.NewLine, "}"); + _inBlock = oldBlock; + if (!_inBlock) { + Dedent(); + } + return node; + } + + protected internal override Expression VisitDefault(DefaultExpression node) { + return node; + } + + protected internal override Expression VisitLabel(LabelExpression node) { + Visit(node.DefaultValue); + DumpLabel(node.Label); + Out(":"); + return node; + } + + protected internal override Expression VisitGoto(GotoExpression node) { + if (!_inBlock) { + Indent(); + } + Out(node.Kind.ToString().ToLower(CultureInfo.CurrentCulture), Flow.Space); + DumpLabel(node.Target); + if (node.Value != null) { + Out(Flow.Space, "("); + Visit(node.Value); + Out(")", Flow.Space); + } + if (!_inBlock) { + Dedent(); + } + return node; + } + + protected internal override Expression VisitLoop(LoopExpression node) { + if (!_inBlock) { + Indent(); + } + Out("loop", Flow.Space); + if (node.ContinueLabel != null) { + NewLine(); + DumpLabel(node.ContinueLabel); + Out(Flow.Space, ":"); + } + Out(" {", Flow.NewLine); + Visit(node.Body); + Out(Flow.NewLine, "}"); + if (node.BreakLabel != null) { + NewLine(); + DumpLabel(node.BreakLabel); + Out(Flow.Space, ":", Flow.NewLine); + } + if (!_inBlock) { + Dedent(); + } + return node; + } + + protected override SwitchCase VisitSwitchCase(SwitchCase node) { + Indent(); + if (node.IsDefault) { + Out("default"); + } else { + Out("case " + node.Value); + } + Out(":", Flow.NewLine); + Indent(); + Visit(node.Body); + Dedent(); + NewLine(); + Dedent(); + return node; + } + + protected internal override Expression VisitSwitch(SwitchExpression node) { + Out("switch "); + if (node.BreakLabel != null) { + DumpLabel(node.BreakLabel); + Out(Flow.Space, ":"); + } + Out("("); + Visit(node.Test); + Out(") {", Flow.NewLine); + Visit(node.SwitchCases, VisitSwitchCase); + Out("}", Flow.NewLine); + return node; + } + + protected override CatchBlock VisitCatchBlock(CatchBlock node) { + Out(Flow.NewLine, "} catch (" + node.Test.Name); + if (node.Variable != null) { + Out(Flow.Space, node.Variable.Name ?? ""); + } + if (node.Filter != null) { + Out(") if (", Flow.Break); + Visit(node.Filter); + } + Out(") {", Flow.NewLine); + Indent(); + Visit(node.Body); + Dedent(); + return node; + } + + protected internal override Expression VisitTry(TryExpression node) { + if (!_inBlock) { + Indent(); + } + Out(Flow.NewLine, "try {", Flow.NewLine); + Visit(node.Body); + Visit(node.Handlers, VisitCatchBlock); + if (node.Finally != null) { + Out(Flow.NewLine, "} finally {", Flow.NewLine); + Visit(node.Finally); + } else if (node.Fault != null) { + Out(Flow.NewLine, "} fault {", Flow.NewLine); + Visit(node.Fault); + } + Out(Flow.NewLine, "}"); + if (!_inBlock) { + Dedent(); + } + return node; + } + + protected internal override Expression VisitIndex(IndexExpression node) { + if (node.Object != null) { + Visit(node.Object); + } else { + Debug.Assert(node.Indexer != null); + Out(node.Indexer.DeclaringType.Name); + } + if (node.Indexer != null){ + Out("."); + Out(node.Indexer.Name); + } + + VisitExpressions('[', node.Arguments, ","); + return node; + } + + protected internal override Expression VisitExtension(Expression node) { + Out("extension", Flow.Space); + + Out(node.GetType().Name, Flow.Space); + Out("("); + // walk it + base.VisitExtension(node); + Out(")"); + return node; + } + + private void DumpLabel(LabelTarget target) { + int labelId = GetLabelId(target); + Out("Label_" + labelId); + } + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionTreeVisitor.Generated.cs b/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionTreeVisitor.Generated.cs new file mode 100644 index 0000000000..f05ec4c332 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionTreeVisitor.Generated.cs @@ -0,0 +1,413 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Scripting.Utils; + +namespace System.Linq.Expressions { + public partial class ExpressionTreeVisitor { + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public Expression VisitNode(Expression node) { + if (node == null) { + return null; + } + + switch (node.NodeType) { + #region Generated ExpressionVisitor Switch + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_visitor_switch from: generate_tree.py + + // Add + case ExpressionType.Add: + return DefaultVisitBinaryExpression(node); + // AddChecked + case ExpressionType.AddChecked: + return DefaultVisitBinaryExpression(node); + // And + case ExpressionType.And: + return DefaultVisitBinaryExpression(node); + // AndAlso + case ExpressionType.AndAlso: + return DefaultVisitBinaryExpression(node); + // ArrayLength + case ExpressionType.ArrayLength: + return DefaultVisitUnaryExpression(node); + // ArrayIndex + case ExpressionType.ArrayIndex: + return DefaultVisitBinaryExpression(node); + // Call + case ExpressionType.Call: + return DefaultVisitMethodCallExpression(node); + // Coalesce + case ExpressionType.Coalesce: + return DefaultVisitBinaryExpression(node); + // Conditional + case ExpressionType.Conditional: + return DefaultVisitConditionalExpression(node); + // Constant + case ExpressionType.Constant: + return DefaultVisitConstantExpression(node); + // Convert + case ExpressionType.Convert: + return DefaultVisitUnaryExpression(node); + // ConvertChecked + case ExpressionType.ConvertChecked: + return DefaultVisitUnaryExpression(node); + // Divide + case ExpressionType.Divide: + return DefaultVisitBinaryExpression(node); + // Equal + case ExpressionType.Equal: + return DefaultVisitBinaryExpression(node); + // ExclusiveOr + case ExpressionType.ExclusiveOr: + return DefaultVisitBinaryExpression(node); + // GreaterThan + case ExpressionType.GreaterThan: + return DefaultVisitBinaryExpression(node); + // GreaterThanOrEqual + case ExpressionType.GreaterThanOrEqual: + return DefaultVisitBinaryExpression(node); + // Invoke + case ExpressionType.Invoke: + return DefaultVisitInvocationExpression(node); + // Lambda + case ExpressionType.Lambda: + return DefaultVisitLambdaExpression(node); + // LeftShift + case ExpressionType.LeftShift: + return DefaultVisitBinaryExpression(node); + // LessThan + case ExpressionType.LessThan: + return DefaultVisitBinaryExpression(node); + // LessThanOrEqual + case ExpressionType.LessThanOrEqual: + return DefaultVisitBinaryExpression(node); + // ListInit + case ExpressionType.ListInit: + return DefaultVisitListInitExpression(node); + // MemberAccess + case ExpressionType.MemberAccess: + return DefaultVisitMemberExpression(node); + // MemberInit + case ExpressionType.MemberInit: + return DefaultVisitMemberInitExpression(node); + // Modulo + case ExpressionType.Modulo: + return DefaultVisitBinaryExpression(node); + // Multiply + case ExpressionType.Multiply: + return DefaultVisitBinaryExpression(node); + // MultiplyChecked + case ExpressionType.MultiplyChecked: + return DefaultVisitBinaryExpression(node); + // Negate + case ExpressionType.Negate: + return DefaultVisitUnaryExpression(node); + // UnaryPlus + case ExpressionType.UnaryPlus: + return DefaultVisitUnaryExpression(node); + // NegateChecked + case ExpressionType.NegateChecked: + return DefaultVisitUnaryExpression(node); + // New + case ExpressionType.New: + return DefaultVisitNewExpression(node); + // NewArrayInit + case ExpressionType.NewArrayInit: + return DefaultVisitNewArrayExpression(node); + // NewArrayBounds + case ExpressionType.NewArrayBounds: + return DefaultVisitNewArrayExpression(node); + // Not + case ExpressionType.Not: + return DefaultVisitUnaryExpression(node); + // NotEqual + case ExpressionType.NotEqual: + return DefaultVisitBinaryExpression(node); + // Or + case ExpressionType.Or: + return DefaultVisitBinaryExpression(node); + // OrElse + case ExpressionType.OrElse: + return DefaultVisitBinaryExpression(node); + // Parameter + case ExpressionType.Parameter: + return DefaultVisitParameterExpression(node); + // Power + case ExpressionType.Power: + return DefaultVisitBinaryExpression(node); + // Quote + case ExpressionType.Quote: + return DefaultVisitUnaryExpression(node); + // RightShift + case ExpressionType.RightShift: + return DefaultVisitBinaryExpression(node); + // Subtract + case ExpressionType.Subtract: + return DefaultVisitBinaryExpression(node); + // SubtractChecked + case ExpressionType.SubtractChecked: + return DefaultVisitBinaryExpression(node); + // TypeAs + case ExpressionType.TypeAs: + return DefaultVisitUnaryExpression(node); + // TypeIs + case ExpressionType.TypeIs: + return DefaultVisitTypeBinaryExpression(node); + // Assign + case ExpressionType.Assign: + return DefaultVisitAssignmentExpression(node); + // Block + case ExpressionType.Block: + return DefaultVisitBlock(node); + // Generator + case ExpressionType.Generator: + return DefaultVisitLambdaExpression(node); + // DoStatement + case ExpressionType.DoStatement: + return DefaultVisitDoStatement(node); + // Dynamic + case ExpressionType.Dynamic: + return DefaultVisitDynamicExpression(node); + // EmptyStatement + case ExpressionType.EmptyStatement: + return DefaultVisitEmptyStatement(node); + // Extension + case ExpressionType.Extension: + return DefaultVisitExtensionExpression(node); + // Goto + case ExpressionType.Goto: + return DefaultVisitGotoExpression(node); + // Index + case ExpressionType.Index: + return DefaultVisitIndexExpression(node); + // Label + case ExpressionType.Label: + return DefaultVisitLabelExpression(node); + // LocalScope + case ExpressionType.LocalScope: + return DefaultVisitLocalScopeExpression(node); + // LoopStatement + case ExpressionType.LoopStatement: + return DefaultVisitLoopStatement(node); + // OnesComplement + case ExpressionType.OnesComplement: + return DefaultVisitUnaryExpression(node); + // ReturnStatement + case ExpressionType.ReturnStatement: + return DefaultVisitReturnStatement(node); + // Scope + case ExpressionType.Scope: + return DefaultVisitScopeExpression(node); + // SwitchStatement + case ExpressionType.SwitchStatement: + return DefaultVisitSwitchStatement(node); + // ThrowStatement + case ExpressionType.ThrowStatement: + return DefaultVisitThrowStatement(node); + // TryStatement + case ExpressionType.TryStatement: + return DefaultVisitTryStatement(node); + // Unbox + case ExpressionType.Unbox: + return DefaultVisitUnaryExpression(node); + // Variable + case ExpressionType.Variable: + return DefaultVisitVariableExpression(node); + // YieldStatement + case ExpressionType.YieldStatement: + return DefaultVisitYieldStatement(node); + + // *** END GENERATED CODE *** + + #endregion + + default: + throw Assert.Unreachable; + } + } + + #region Generated ExpressionVisitor Methods + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_visitor_methods from: generate_tree.py + + // AssignmentExpression + private Expression DefaultVisitAssignmentExpression(Expression node) { + return Visit((AssignmentExpression)node); + } + + // BinaryExpression + private Expression DefaultVisitBinaryExpression(Expression node) { + return Visit((BinaryExpression)node); + } + + // Block + private Expression DefaultVisitBlock(Expression node) { + return Visit((Block)node); + } + + // ConditionalExpression + private Expression DefaultVisitConditionalExpression(Expression node) { + return Visit((ConditionalExpression)node); + } + + // ConstantExpression + private Expression DefaultVisitConstantExpression(Expression node) { + return Visit((ConstantExpression)node); + } + + // DoStatement + private Expression DefaultVisitDoStatement(Expression node) { + return Visit((DoStatement)node); + } + + // DynamicExpression + private Expression DefaultVisitDynamicExpression(Expression node) { + return Visit((DynamicExpression)node); + } + + // EmptyStatement + private Expression DefaultVisitEmptyStatement(Expression node) { + return Visit((EmptyStatement)node); + } + + // GotoExpression + private Expression DefaultVisitGotoExpression(Expression node) { + return Visit((GotoExpression)node); + } + + // IndexExpression + private Expression DefaultVisitIndexExpression(Expression node) { + return Visit((IndexExpression)node); + } + + // InvocationExpression + private Expression DefaultVisitInvocationExpression(Expression node) { + return Visit((InvocationExpression)node); + } + + // LabelExpression + private Expression DefaultVisitLabelExpression(Expression node) { + return Visit((LabelExpression)node); + } + + // LambdaExpression + private Expression DefaultVisitLambdaExpression(Expression node) { + return Visit((LambdaExpression)node); + } + + // ListInitExpression + private Expression DefaultVisitListInitExpression(Expression node) { + return Visit((ListInitExpression)node); + } + + // LocalScopeExpression + private Expression DefaultVisitLocalScopeExpression(Expression node) { + return Visit((LocalScopeExpression)node); + } + + // LoopStatement + private Expression DefaultVisitLoopStatement(Expression node) { + return Visit((LoopStatement)node); + } + + // MemberExpression + private Expression DefaultVisitMemberExpression(Expression node) { + return Visit((MemberExpression)node); + } + + // MemberInitExpression + private Expression DefaultVisitMemberInitExpression(Expression node) { + return Visit((MemberInitExpression)node); + } + + // MethodCallExpression + private Expression DefaultVisitMethodCallExpression(Expression node) { + return Visit((MethodCallExpression)node); + } + + // NewArrayExpression + private Expression DefaultVisitNewArrayExpression(Expression node) { + return Visit((NewArrayExpression)node); + } + + // NewExpression + private Expression DefaultVisitNewExpression(Expression node) { + return Visit((NewExpression)node); + } + + // ParameterExpression + private Expression DefaultVisitParameterExpression(Expression node) { + return Visit((ParameterExpression)node); + } + + // ReturnStatement + private Expression DefaultVisitReturnStatement(Expression node) { + return Visit((ReturnStatement)node); + } + + // ScopeExpression + private Expression DefaultVisitScopeExpression(Expression node) { + return Visit((ScopeExpression)node); + } + + // SwitchStatement + private Expression DefaultVisitSwitchStatement(Expression node) { + return Visit((SwitchStatement)node); + } + + // ThrowStatement + private Expression DefaultVisitThrowStatement(Expression node) { + return Visit((ThrowStatement)node); + } + + // TryStatement + private Expression DefaultVisitTryStatement(Expression node) { + return Visit((TryStatement)node); + } + + // TypeBinaryExpression + private Expression DefaultVisitTypeBinaryExpression(Expression node) { + return Visit((TypeBinaryExpression)node); + } + + // UnaryExpression + private Expression DefaultVisitUnaryExpression(Expression node) { + return Visit((UnaryExpression)node); + } + + // VariableExpression + private Expression DefaultVisitVariableExpression(Expression node) { + return Visit((VariableExpression)node); + } + + // YieldStatement + private Expression DefaultVisitYieldStatement(Expression node) { + return Visit((YieldStatement)node); + } + + // *** END GENERATED CODE *** + + #endregion + + // ExpressionType.Extension + private Expression DefaultVisitExtensionExpression(Expression node) { + return VisitExtension(node); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionType.cs b/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionType.cs new file mode 100644 index 0000000000..97499704f4 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionType.cs @@ -0,0 +1,112 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace System.Linq.Expressions { + + public enum ExpressionType { + + #region Generated Expression Tree Node Types + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_tree_nodes from: generate_tree.py + + Add, + AddChecked, + And, + AndAlso, + ArrayLength, + ArrayIndex, + Call, + Coalesce, + Conditional, + Constant, + Convert, + ConvertChecked, + Divide, + Equal, + ExclusiveOr, + GreaterThan, + GreaterThanOrEqual, + Invoke, + Lambda, + LeftShift, + LessThan, + LessThanOrEqual, + ListInit, + MemberAccess, + MemberInit, + Modulo, + Multiply, + MultiplyChecked, + Negate, + UnaryPlus, + NegateChecked, + New, + NewArrayInit, + NewArrayBounds, + Not, + NotEqual, + Or, + OrElse, + Parameter, + Power, + Quote, + RightShift, + Subtract, + SubtractChecked, + TypeAs, + TypeIs, + Assign, + Block, + DebugInfo, + Decrement, + Dynamic, + Default, + Extension, + Goto, + Increment, + Index, + Label, + RuntimeVariables, + Loop, + Switch, + Throw, + Try, + Unbox, + AddAssign, + AndAssign, + DivideAssign, + ExclusiveOrAssign, + LeftShiftAssign, + ModuloAssign, + MultiplyAssign, + OrAssign, + PowerAssign, + RightShiftAssign, + SubtractAssign, + AddAssignChecked, + MultiplyAssignChecked, + SubtractAssignChecked, + PreIncrementAssign, + PreDecrementAssign, + PostIncrementAssign, + PostDecrementAssign, + TypeEqual, + + // *** END GENERATED CODE *** + + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionVisitor.cs b/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionVisitor.cs new file mode 100644 index 0000000000..e959b7d9f0 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionVisitor.cs @@ -0,0 +1,467 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + /// + /// Base class for visiting and rewriting trees. Subclasses can override + /// individual Visit methods from which they can return rewritten nodes. + /// If a node is rewritten, all parent nodes will be rewritten + /// automatically. + /// + /// TODO: rename back to ExpressionVisitor (fix the Linq test that has a copy) + /// TODO: needs public API vetting + /// + public abstract class ExpressionVisitor { + + public Expression Visit(Expression node) { + return (node == null) ? null : node.Accept(this); + } + + public ReadOnlyCollection Visit(ReadOnlyCollection nodes) { + Expression[] newNodes = null; + for (int i = 0, n = nodes.Count; i < n; i++) { + Expression node = nodes[i].Accept(this); + + if (newNodes != null) { + newNodes[i] = node; + } else if (!object.ReferenceEquals(node, nodes[i])) { + newNodes = new Expression[n]; + for (int j = 0; j < i; j++) { + newNodes[j] = nodes[j]; + } + newNodes[i] = node; + } + } + if (newNodes == null) { + return nodes; + } + return new ReadOnlyCollection(newNodes); + } + + internal Expression[] VisitArguments(IArgumentProvider nodes) { + Expression[] newNodes = null; + for (int i = 0, n = nodes.ArgumentCount; i < n; i++) { + Expression curNode = nodes.GetArgument(i); + Expression node = curNode.Accept(this); + + if (newNodes != null) { + newNodes[i] = node; + } else if (!object.ReferenceEquals(node, curNode)) { + newNodes = new Expression[n]; + for (int j = 0; j < i; j++) { + newNodes[j] = nodes.GetArgument(j); + } + newNodes[i] = node; + } + } + return newNodes; + } + + /// + /// Visits all nodes in the collection using a specified element visitor. + /// + /// Element type. + /// Input collection. + /// Delegate that visits a single element. + /// Collection of visited nodes. Original collection is returned if no nodes were modified. + protected static ReadOnlyCollection Visit(ReadOnlyCollection nodes, Func elementVisitor) { + T[] newNodes = null; + for (int i = 0, n = nodes.Count; i < n; i++) { + T node = elementVisitor(nodes[i]); + if (newNodes != null) { + newNodes[i] = node; + } else if (!object.ReferenceEquals(node, nodes[i])) { + newNodes = new T[n]; + for (int j = 0; j < i; j++) { + newNodes[j] = nodes[j]; + } + newNodes[i] = node; + } + } + if (newNodes == null) { + return nodes; + } + return new ReadOnlyCollection(newNodes); + } + + protected T VisitAndConvert(T node, string callerName) where T : Expression { + if (node == null) { + return null; + } + node = node.Accept(this) as T; + if (node == null) { + throw Error.MustRewriteToSameType(callerName, typeof(T), callerName); + } + return node; + } + + /// + /// Visits all of the nodes in the collection, and tries to convert each + /// result back to the original type. If any conversion fails, it + /// throws an error + /// + protected ReadOnlyCollection VisitAndConvert(ReadOnlyCollection nodes, string callerName) where T : Expression { + T[] newNodes = null; + for (int i = 0, n = nodes.Count; i < n; i++) { + T node = nodes[i].Accept(this) as T; + if (node == null) { + throw Error.MustRewriteToSameType(callerName, typeof(T), callerName); + } + + if (newNodes != null) { + newNodes[i] = node; + } else if (!object.ReferenceEquals(node, nodes[i])) { + newNodes = new T[n]; + for (int j = 0; j < i; j++) { + newNodes[j] = nodes[j]; + } + newNodes[i] = node; + } + } + if (newNodes == null) { + return nodes; + } + return new ReadOnlyCollection(newNodes); + } + + protected internal virtual Expression VisitBinary(BinaryExpression node) { + // Walk children in evaluation order: left, conversion, right + Expression l = Visit(node.Left); + LambdaExpression c = VisitAndConvert(node.Conversion, "VisitBinary"); + Expression r = Visit(node.Right); + if (l == node.Left && r == node.Right && c == node.Conversion) { + return node; + } + return Expression.MakeBinary(node.NodeType, l, r, node.IsLiftedToNull, node.Method, c); + } + + protected internal virtual Expression VisitBlock(BlockExpression node) { + int count = node.ExpressionCount; + Expression[] nodes = null; + for (int i = 0; i < count; i++) { + Expression oldNode = node.GetExpression(i); + Expression newNode = Visit(oldNode); + + if (oldNode != newNode) { + if (nodes == null) { + nodes = new Expression[count]; + } + nodes[i] = newNode; + } + } + var v = VisitAndConvert(node.Variables, "VisitBlock"); + + if (v == node.Variables && nodes == null) { + return node; + } else { + for (int i = 0; i < count; i++) { + if (nodes[i] == null) { + nodes[i] = node.GetExpression(i); + } + } + } + + return node.Rewrite(v, nodes); + } + + protected internal virtual Expression VisitConditional(ConditionalExpression node) { + Expression t = Visit(node.Test); + Expression l = Visit(node.IfTrue); + Expression r = Visit(node.IfFalse); + if (t == node.Test && l == node.IfTrue && r == node.IfFalse) { + return node; + } + return Expression.Condition(t, l, r); + } + + protected internal virtual Expression VisitConstant(ConstantExpression node) { + return node; + } + + protected internal virtual Expression VisitDebugInfo(DebugInfoExpression node) { + Expression e = Visit(node.Expression); + if (e == node.Expression) { + return node; + } + return Expression.DebugInfo(e, node.Document, node.StartLine, node.StartColumn, node.EndLine, node.EndColumn); + } + + protected internal virtual Expression VisitDynamic(DynamicExpression node) { + Expression[] a = VisitArguments((IArgumentProvider)node); + if (a == null) { + return node; + } + + return node.Rewrite(a); + } + + protected internal virtual Expression VisitDefault(DefaultExpression node) { + return node; + } + + /// + /// Override called for Extension nodes. This can be overriden to + /// rewrite certain extension nodes. If it's not overriden, this method + /// will call into Expression.Visit, which gives the node a chance to + /// walk its children + /// + protected internal virtual Expression VisitExtension(Expression node) { + return node.VisitChildren(this); + } + + protected internal virtual Expression VisitGoto(GotoExpression node) { + LabelTarget t = VisitLabelTarget(node.Target); + Expression v = Visit(node.Value); + if (t == node.Target && v == node.Value) { + return node; + } + return Expression.MakeGoto(node.Kind, t, v); + } + + protected internal virtual Expression VisitInvocation(InvocationExpression node) { + Expression e = Visit(node.Expression); + Expression[] a = VisitArguments(node); + if (e == node.Expression && a == null) { + return node; + } + return Expression.Invoke(e, a); + } + + protected virtual LabelTarget VisitLabelTarget(LabelTarget node) { + return node; + } + + protected internal virtual Expression VisitLabel(LabelExpression node) { + LabelTarget l = VisitLabelTarget(node.Label); + Expression d = Visit(node.DefaultValue); + if (l == node.Label && d == node.DefaultValue) { + return node; + } + return Expression.Label(l, d); + } + + protected internal virtual Expression VisitLambda(Expression node) { + Expression b = Visit(node.Body); + var p = VisitAndConvert(node.Parameters, "VisitLambda"); + if (b == node.Body && p == node.Parameters) { + return node; + } + return Expression.Lambda(b, node.Name, p); + } + + protected internal virtual Expression VisitLoop(LoopExpression node) { + LabelTarget @break = VisitLabelTarget(node.BreakLabel); + LabelTarget @continue = VisitLabelTarget(node.ContinueLabel); + Expression b = Visit(node.Body); + if (@break == node.BreakLabel && + @continue == node.ContinueLabel && + b == node.Body) { + return node; + } + return Expression.Loop(b, @break, @continue); + } + + protected internal virtual Expression VisitMember(MemberExpression node) { + Expression e = Visit(node.Expression); + if (e == node.Expression) { + return node; + } + return Expression.MakeMemberAccess(e, node.Member); + } + + protected internal virtual Expression VisitIndex(IndexExpression node) { + Expression o = Visit(node.Object); + IList a = VisitArguments(node); + if (o == node.Object && a == null) { + return node; + } + return Expression.MakeIndex(o, node.Indexer, a); + } + + protected internal virtual Expression VisitMethodCall(MethodCallExpression node) { + Expression o = Visit(node.Object); + Expression[] a = VisitArguments((IArgumentProvider)node); + if (o == node.Object && a == null) { + return node; + } + + return node.Rewrite(o, a); + } + + protected internal virtual Expression VisitNewArray(NewArrayExpression node) { + ReadOnlyCollection e = Visit(node.Expressions); + if (e == node.Expressions) { + return node; + } + if (node.NodeType == ExpressionType.NewArrayInit) { + return Expression.NewArrayInit(node.Type.GetElementType(), e); + } + return Expression.NewArrayBounds(node.Type.GetElementType(), e); + } + + protected internal virtual Expression VisitNew(NewExpression node) { + ReadOnlyCollection a = Visit(node.Arguments); + if (a == node.Arguments) { + return node; + } + if (node.Members != null) { + return Expression.New(node.Constructor, a, node.Members); + } + return Expression.New(node.Constructor, a); + } + + protected internal virtual Expression VisitParameter(ParameterExpression node) { + return node; + } + + protected internal virtual Expression VisitRuntimeVariables(RuntimeVariablesExpression node) { + var v = VisitAndConvert(node.Variables, "VisitRuntimeVariables"); + if (v == node.Variables) { + return node; + } + return Expression.RuntimeVariables(v); + } + + protected virtual SwitchCase VisitSwitchCase(SwitchCase node) { + Expression b = Visit(node.Body); + if (b == node.Body) { + return node; + } + if (node.IsDefault) { + return Expression.DefaultCase(b); + } + return Expression.SwitchCase(node.Value, b); + } + + protected internal virtual Expression VisitSwitch(SwitchExpression node) { + LabelTarget l = VisitLabelTarget(node.BreakLabel); + Expression t = Visit(node.Test); + ReadOnlyCollection c = Visit(node.SwitchCases, VisitSwitchCase); + + if (l == node.BreakLabel && t == node.Test && c == node.SwitchCases) { + return node; + } + return Expression.Switch(t, l, c); + } + + protected virtual CatchBlock VisitCatchBlock(CatchBlock node) { + ParameterExpression v = VisitAndConvert(node.Variable, "VisitCatchBlock"); + Expression f = Visit(node.Filter); + Expression b = Visit(node.Body); + if (v == node.Variable && b == node.Body && f == node.Filter) { + return node; + } + return Expression.MakeCatchBlock(node.Test, v, b, f); + } + + protected internal virtual Expression VisitTry(TryExpression node) { + Expression b = Visit(node.Body); + ReadOnlyCollection h = Visit(node.Handlers, VisitCatchBlock); + Expression y = Visit(node.Finally); + Expression f = Visit(node.Fault); + + if (b == node.Body && + h == node.Handlers && + y == node.Finally && + f == node.Fault) { + return node; + } + return Expression.MakeTry(b, y, f, h); + } + + protected internal virtual Expression VisitTypeBinary(TypeBinaryExpression node) { + Expression e = Visit(node.Expression); + if (e == node.Expression) { + return node; + } + return Expression.TypeIs(e, node.TypeOperand); + } + + protected internal virtual Expression VisitUnary(UnaryExpression node) { + Expression o = Visit(node.Operand); + if (o == node.Operand) { + return node; + } + return Expression.MakeUnary(node.NodeType, o, node.Type, node.Method); + } + + protected internal virtual Expression VisitMemberInit(MemberInitExpression node) { + NewExpression n = VisitAndConvert(node.NewExpression, "VisitMemberInit"); + ReadOnlyCollection bindings = Visit(node.Bindings, VisitMemberBinding); + if (n == node.NewExpression && bindings == node.Bindings) { + return node; + } + return Expression.MemberInit(n, bindings); + } + + protected internal virtual Expression VisitListInit(ListInitExpression node) { + NewExpression n = VisitAndConvert(node.NewExpression, "VisitListInit"); + ReadOnlyCollection initializers = Visit(node.Initializers, VisitElementInit); + if (n == node.NewExpression && initializers == node.Initializers) { + return node; + } + return Expression.ListInit(n, initializers); + } + + protected virtual ElementInit VisitElementInit(ElementInit initializer) { + ReadOnlyCollection arguments = Visit(initializer.Arguments); + if (arguments == initializer.Arguments) { + return initializer; + } + return Expression.ElementInit(initializer.AddMethod, arguments); + } + + protected virtual MemberBinding VisitMemberBinding(MemberBinding binding) { + switch (binding.BindingType) { + case MemberBindingType.Assignment: + return VisitMemberAssignment((MemberAssignment)binding); + case MemberBindingType.MemberBinding: + return VisitMemberMemberBinding((MemberMemberBinding)binding); + case MemberBindingType.ListBinding: + return VisitMemberListBinding((MemberListBinding)binding); + default: + throw Error.UnhandledBindingType(binding.BindingType); + } + } + + protected virtual MemberAssignment VisitMemberAssignment(MemberAssignment assignment) { + Expression e = Visit(assignment.Expression); + if (e == assignment.Expression) { + return assignment; + } + return Expression.Bind(assignment.Member, e); + } + + protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) { + ReadOnlyCollection bindings = Visit(binding.Bindings, VisitMemberBinding); + if (bindings == binding.Bindings) { + return binding; + } + return Expression.MemberBind(binding.Member, bindings); + } + + protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding binding) { + ReadOnlyCollection initializers = Visit(binding.Initializers, VisitElementInit); + if (initializers == binding.Initializers) { + return binding; + } + return Expression.ListBind(binding.Member, initializers); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionWriter.cs b/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionWriter.cs new file mode 100644 index 0000000000..1a3050ac16 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/ExpressionWriter.cs @@ -0,0 +1,923 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ +#if MICROSOFT_SCRIPTING_CORE + +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Dynamic; +using System.Dynamic.Binders; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + internal sealed class ExpressionWriter : ExpressionVisitor { + [Flags] + private enum Flow { + None, + Space, + NewLine, + + Break = 0x8000 // newline if column > MaxColumn + }; + + private struct LambdaId { + private readonly LambdaExpression _lambda; + private readonly int _id; + + internal LambdaId(LambdaExpression lambda, int id) { + _lambda = lambda; + _id = id; + } + + internal LambdaExpression Lambda { + get { return _lambda; } + } + internal int Id { + get { return _id; } + } + } + + private const int Tab = 4; + private const int MaxColumn = 120; + + private TextWriter _out; + private int _column; + + private Queue _lambdaIds; + private int _blockid; + private Stack _stack = new Stack(); + private int _delta; + private Flow _flow; + + private ExpressionWriter(TextWriter file) { + _out = file; + } + + private int Base { + get { + return _stack.Count > 0 ? _stack.Peek() : 0; + } + } + + private int Delta { + get { return _delta; } + } + + private int Depth { + get { return Base + Delta; } + } + + private void Indent() { + _delta += Tab; + } + private void Dedent() { + _delta -= Tab; + } + + private void NewLine() { + _flow = Flow.NewLine; + } + + /// + /// Write out the given AST + /// + internal static void Dump(Expression node, string descr, TextWriter writer) { + Debug.Assert(node != null); + Debug.Assert(writer != null); + + ExpressionWriter dv = new ExpressionWriter(writer); + dv.DoDump(node, descr); + } + + private void DoDump(Expression node, string description) { + if (description != null) { + WritePrologue(description); + } + + Visit(node); + WriteLine(); + + WriteLambdas(); + WriteLine(); + } + + private void WritePrologue(string name) { + WriteLine("//"); + WriteLine("// AST: {0}", name); + WriteLine("//"); + WriteLine(); + } + + private void WriteLambdas() { + Debug.Assert(_stack.Count == 0); + + while (_lambdaIds != null && _lambdaIds.Count > 0) { + LambdaId b = _lambdaIds.Dequeue(); + WriteLine(); + WriteLine("//"); + WriteLine("// LAMBDA: {0}({1})", b.Lambda.Name, b.Id); + WriteLine("//"); + DumpLambda(b.Lambda); + WriteLine(); + + Debug.Assert(_stack.Count == 0); + } + } + + private int Enqueue(LambdaExpression lambda) { + if (_lambdaIds == null) { + _lambdaIds = new Queue(); + } + _lambdaIds.Enqueue(new LambdaId(lambda, ++_blockid)); + return _blockid; + } + + #region The printing code + + private void Out(string s) { + Out(Flow.None, s, Flow.None); + } + + private void Out(Flow before, string s) { + Out(before, s, Flow.None); + } + + private void Out(string s, Flow after) { + Out(Flow.None, s, after); + } + + private void Out(Flow before, string s, Flow after) { + switch (GetFlow(before)) { + case Flow.None: + break; + case Flow.Space: + Write(" "); + break; + case Flow.NewLine: + WriteLine(); + Write(new String(' ', Depth)); + break; + } + Write(s); + _flow = after; + } + + private void WriteLine() { + _out.WriteLine(); + _column = 0; + } + private void WriteLine(string s) { + _out.WriteLine(s); + _column = 0; + } + private void WriteLine(string format, object arg0) { + string s = String.Format(CultureInfo.CurrentCulture, format, arg0); + WriteLine(s); + } + private void WriteLine(string format, object arg0, object arg1) { + string s = String.Format(CultureInfo.CurrentCulture, format, arg0, arg1); + WriteLine(s); + } + private void Write(string s) { + _out.Write(s); + _column += s.Length; + } + + private Flow GetFlow(Flow flow) { + Flow last; + + last = CheckBreak(_flow); + flow = CheckBreak(flow); + + // Get the biggest flow that is requested None < Space < NewLine + return (Flow)System.Math.Max((int)last, (int)flow); + } + + private Flow CheckBreak(Flow flow) { + if ((flow & Flow.Break) != 0) { + if (_column > (MaxColumn + Depth)) { + flow = Flow.NewLine; + } else { + flow &= ~Flow.Break; + } + } + return flow; + } + + #endregion + + #region The AST Output + + // More proper would be to make this a virtual method on Action + private static string FormatBinder(CallSiteBinder binder) { + ConvertBinder convert; + GetMemberBinder getMember; + SetMemberBinder setMember; + DeleteMemberBinder deleteMember; + GetIndexBinder getIndex; + SetIndexBinder setIndex; + DeleteIndexBinder deleteIndex; + InvokeMemberBinder call; + InvokeBinder invoke; + CreateInstanceBinder create; + UnaryOperationBinder unary; + BinaryOperationBinder binary; + + if ((convert = binder as ConvertBinder) != null) { + return "Convert " + convert.Type; + } else if ((getMember = binder as GetMemberBinder) != null) { + return "GetMember " + getMember.Name; + } else if ((setMember = binder as SetMemberBinder) != null) { + return "SetMember " + setMember.Name; + } else if ((deleteMember = binder as DeleteMemberBinder) != null) { + return "DeleteMember " + deleteMember.Name; + } else if ((getIndex = binder as GetIndexBinder) != null) { + return "GetIndex"; + } else if ((setIndex = binder as SetIndexBinder) != null) { + return "SetIndex"; + } else if ((deleteIndex = binder as DeleteIndexBinder) != null) { + return "DeleteIndex"; + } else if ((call = binder as InvokeMemberBinder) != null) { + return "Call " + call.Name; + } else if ((invoke = binder as InvokeBinder) != null) { + return "Invoke"; + } else if ((create = binder as CreateInstanceBinder) != null) { + return "Create "; + } else if ((unary = binder as UnaryOperationBinder) != null) { + return "UnaryOperation " + unary.Operation; + } else if ((binary = binder as BinaryOperationBinder) != null) { + return "BinaryOperation " + binary.Operation; + } else { + return "CallSiteBinder(" + binder.ToString() + ") "; + } + } + + private void VisitExpressions(char open, IList expressions) where T : Expression { + VisitExpressions(open, expressions, false); + } + + private void VisitExpressions(char open, IList expressions, bool forceMultiline) where T : Expression { + VisitExpressions(open, expressions, forceMultiline, (e) => Visit(e)); + } + + private void VisitDeclarations(char open, IList expressions, bool forceMultiline) { + VisitExpressions(open, expressions, forceMultiline, (variable) => + { + Out(variable.Type.ToString()); + if (variable.IsByRef) { + Out("&"); + } + Out(" "); + Out(variable.Name ?? ".anonymous"); + }); + } + + private void VisitExpressions(char open, IList expressions, bool forceMultiline, Action visit) { + + bool multiline = expressions != null && (forceMultiline || expressions.Count > 1); + + Out(open.ToString()); + if (expressions != null) { + Indent(); + bool isFirst = true; + foreach (T e in expressions) { + if (isFirst) { + if (multiline) { + NewLine(); + } + isFirst = false; + } else { + Out(",", Flow.NewLine); + } + visit(e); + } + Dedent(); + } + + string close; + switch (open) { + case '(': close = ")"; break; + case '{': close = "}"; break; + case '[': close = "]"; break; + case '<': close = ">"; break; + default: throw Assert.Unreachable; + } + if (multiline) { + Out(Flow.NewLine, close, Flow.Break); + } else { + Out(close, Flow.Break); + } + } + + protected internal override Expression VisitDynamic(DynamicExpression node) { + Out(".site", Flow.Space); + + Out("("); + Out(node.Type.Name); + Out(")", Flow.Space); + + Out(FormatBinder(node.Binder)); + VisitExpressions('(', node.Arguments); + return node; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + protected internal override Expression VisitBinary(BinaryExpression node) { + if (node.NodeType == ExpressionType.ArrayIndex) { + ParenthesizedVisit(node, node.Left); + Out("["); + Visit(node.Right); + Out("]"); + } else { + bool parenthesizeLeft = NeedsParentheses(node, node.Left); + bool parenthesizeRight = NeedsParentheses(node, node.Right); + + string op; + bool isChecked = false; + Flow beforeOp = Flow.Space; + switch (node.NodeType) { + case ExpressionType.Assign: op = "="; break; + case ExpressionType.Equal: op = "=="; break; + case ExpressionType.NotEqual: op = "!="; break; + case ExpressionType.AndAlso: op = "&&"; beforeOp = Flow.NewLine; break; + case ExpressionType.OrElse: op = "||"; beforeOp = Flow.NewLine; break; + case ExpressionType.GreaterThan: op = ">"; break; + case ExpressionType.LessThan: op = "<"; break; + case ExpressionType.GreaterThanOrEqual: op = ">="; break; + case ExpressionType.LessThanOrEqual: op = "<="; break; + case ExpressionType.Add: op = "+"; break; + case ExpressionType.AddAssign: op = "+="; break; + case ExpressionType.AddAssignChecked: op = "+="; isChecked = true; break; + case ExpressionType.AddChecked: op = "+"; isChecked = true; break; + case ExpressionType.Subtract: op = "-"; break; + case ExpressionType.SubtractAssign: op = "-="; break; + case ExpressionType.SubtractAssignChecked: op = "-="; isChecked = true; break; + case ExpressionType.SubtractChecked: op = "-"; isChecked = true; break; + case ExpressionType.Divide: op = "/"; break; + case ExpressionType.DivideAssign: op = "/="; break; + case ExpressionType.Modulo: op = "%"; break; + case ExpressionType.ModuloAssign: op = "%="; break; + case ExpressionType.Multiply: op = "*"; break; + case ExpressionType.MultiplyAssign: op = "*="; break; + case ExpressionType.MultiplyAssignChecked: op = "*="; isChecked = true; break; + case ExpressionType.MultiplyChecked: op = "*"; isChecked = true; break; + case ExpressionType.LeftShift: op = "<<"; break; + case ExpressionType.LeftShiftAssign: op = "<<="; break; + case ExpressionType.RightShift: op = ">>"; break; + case ExpressionType.RightShiftAssign: op = ">>="; break; + case ExpressionType.And: op = "&"; break; + case ExpressionType.AndAssign: op = "&="; break; + case ExpressionType.Or: op = "|"; break; + case ExpressionType.OrAssign: op = "|="; break; + case ExpressionType.ExclusiveOr: op = "^"; break; + case ExpressionType.ExclusiveOrAssign: op = "^="; break; + case ExpressionType.Power: op = "**"; break; + case ExpressionType.PowerAssign: op = "**="; break; + //TODO: need to handle conversion lambda + case ExpressionType.Coalesce: op = "??"; break; + + default: + throw new InvalidOperationException(); + } + if (isChecked) { + Out(Flow.Break, "checked(", Flow.None); + } + + + if (parenthesizeLeft) { + Out("(", Flow.None); + } + Visit(node.Left); + if (parenthesizeLeft) { + Out(Flow.None, ")", Flow.Break); + } + + Out(beforeOp, op, Flow.Space | Flow.Break); + + if (parenthesizeRight) { + Out("(", Flow.None); + } + Visit(node.Right); + if (parenthesizeRight) { + Out(Flow.None, ")", Flow.Break); + } + } + return node; + } + + protected internal override Expression VisitParameter(ParameterExpression node) { + Out("$" + node.Name); + return node; + } + + protected internal override Expression VisitLambda(Expression node) { + int id = Enqueue(node); + Out( + String.Format(CultureInfo.CurrentCulture, + "{0} ({1} {2} #{3})", + ".lambda", + node.Name, + node.Type, + id + ) + ); + return node; + } + + // TODO: calculate tree depth? + private static bool IsSimpleExpression(Expression node) { + var binary = node as BinaryExpression; + if (binary != null) { + return !(binary.Left is BinaryExpression || binary.Right is BinaryExpression); + } + + return false; + } + + protected internal override Expression VisitConditional(ConditionalExpression node) { + if (IsSimpleExpression(node.Test)) { + Out(".if ("); + Visit(node.Test); + Out(") {", Flow.NewLine); + } else { + Out(".if (", Flow.NewLine); + Indent(); + Visit(node.Test); + Dedent(); + Out(Flow.NewLine, ") {", Flow.NewLine); + } + Indent(); + Visit(node.IfTrue); + Dedent(); + Out(Flow.NewLine, "} .else {", Flow.NewLine); + Indent(); + Visit(node.IfFalse); + Dedent(); + Out(Flow.NewLine, "}", Flow.NewLine); + return node; + } + + private static string Constant(object value) { + if (value == null) { + return ".null"; + } + + ITemplatedValue itv = value as ITemplatedValue; + if (itv != null) { + return ".template" + itv.Index.ToString(CultureInfo.CurrentCulture) + " (" + itv.ObjectValue.ToString() + ")"; + } + + string s; + if ((s = value as string) != null) { + return "\"" + s + "\""; + } + if (value is int || value is double) { + return String.Format(CultureInfo.CurrentCulture, "{0:G}", value); + } + if (value is bool) { + return value.ToString(); + } + return ".const<" + value.GetType().Name + ">(" + value.ToString() + ")"; + } + + protected internal override Expression VisitConstant(ConstantExpression node) { + Out(Constant(node.Value)); + return node; + } + + protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) { + Out(".localScope"); + VisitExpressions('(', node.Variables); + return node; + } + + // Prints ".instanceField" or "declaringType.staticField" + private void OutMember(Expression node, Expression instance, MemberInfo member) { + if (instance != null) { + ParenthesizedVisit(node, instance); + Out("." + member.Name); + } else { + // For static members, include the type name + Out(member.DeclaringType.Name + "." + member.Name); + } + } + + protected internal override Expression VisitMember(MemberExpression node) { + OutMember(node, node.Expression, node.Member); + return node; + } + + protected internal override Expression VisitInvocation(InvocationExpression node) { + ParenthesizedVisit(node, node.Expression); + Out(".Invoke"); + VisitExpressions('(', node.Arguments); + return node; + } + + private static bool NeedsParentheses(Expression parent, Expression child) { + return GetOperatorPrecedence(child) < GetOperatorPrecedence(parent); + } + + // the greater the higher + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private static int GetOperatorPrecedence(Expression node) { + switch (node.NodeType) { + case ExpressionType.Assign: + case ExpressionType.ExclusiveOrAssign: + case ExpressionType.AddAssign: + case ExpressionType.SubtractAssign: + case ExpressionType.DivideAssign: + case ExpressionType.ModuloAssign: + case ExpressionType.MultiplyAssign: + case ExpressionType.LeftShiftAssign: + case ExpressionType.RightShiftAssign: + case ExpressionType.AndAssign: + case ExpressionType.OrAssign: + case ExpressionType.PowerAssign: + return 0; + + case ExpressionType.Coalesce: + return 1; + + case ExpressionType.OrElse: + return 2; + + case ExpressionType.AndAlso: + return 3; + + case ExpressionType.Or: + return 4; + + case ExpressionType.ExclusiveOr: + return 5; + + case ExpressionType.And: + return 6; + + case ExpressionType.Equal: + case ExpressionType.NotEqual: + return 7; + + case ExpressionType.GreaterThan: + case ExpressionType.LessThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.LessThanOrEqual: + return 8; + + case ExpressionType.LeftShift: + case ExpressionType.RightShift: + return 9; + + case ExpressionType.Add: + case ExpressionType.Subtract: + return 10; + + case ExpressionType.Divide: + case ExpressionType.Modulo: + case ExpressionType.Multiply: + return 11; + + case ExpressionType.Power: + return 12; + + case ExpressionType.Negate: + case ExpressionType.UnaryPlus: + case ExpressionType.Not: + case ExpressionType.Convert: + case ExpressionType.PreIncrementAssign: + case ExpressionType.PreDecrementAssign: + return 13; + + case ExpressionType.PostIncrementAssign: + case ExpressionType.PostDecrementAssign: + case ExpressionType.NegateChecked: + case ExpressionType.ConvertChecked: + case ExpressionType.AddChecked: + case ExpressionType.SubtractChecked: + case ExpressionType.MultiplyChecked: + case ExpressionType.MultiplyAssignChecked: + case ExpressionType.SubtractAssignChecked: + case ExpressionType.AddAssignChecked: + return 14; + + case ExpressionType.Call: + case ExpressionType.Parameter: + case ExpressionType.Constant: + default: + return 15; + } + } + + private void ParenthesizedVisit(Expression parent, Expression nodeToVisit) { + if (NeedsParentheses(parent, nodeToVisit)) { + Out("("); + Visit(nodeToVisit); + Out(")"); + } else { + Visit(nodeToVisit); + } + } + + protected internal override Expression VisitMethodCall(MethodCallExpression node) { + if (node.Object != null) { + ParenthesizedVisit(node, node.Object); + Out("."); + } + if (node.Method.ReflectedType != null) { + Out("'" + node.Method.ReflectedType.Name + "." + node.Method.Name + "'"); + } else { + Out("'" + node.Method.Name + "'"); + } + VisitExpressions('(', node.Arguments); + return node; + } + + protected internal override Expression VisitNewArray(NewArrayExpression node) { + if (node.NodeType == ExpressionType.NewArrayBounds) { + // .new MyType[expr1, expr2] + Out(".new " + node.Type.GetElementType().Name, Flow.Space); + VisitExpressions('[', node.Expressions); + } else { + // .new MyType {expr1, expr2} + Out(".new " + node.Type.Name, Flow.Space); + VisitExpressions('{', node.Expressions); + } + return node; + } + + protected internal override Expression VisitNew(NewExpression node) { + Out(".new " + node.Type.Name); + VisitExpressions('(', node.Arguments); + return node; + } + + protected internal override Expression VisitTypeBinary(TypeBinaryExpression node) { + Visit(node.Expression); + switch (node.NodeType) { + case ExpressionType.TypeIs: + Out(Flow.Space, ".is", Flow.Space); + break; + case ExpressionType.TypeEqual: + Out(Flow.Space, ".TypeEqual", Flow.Space); + break; + } + Out(node.TypeOperand.Name); + return node; + } + + protected internal override Expression VisitUnary(UnaryExpression node) { + switch (node.NodeType) { + case ExpressionType.Convert: + Out("(" + node.Type.Name + ")"); + break; + case ExpressionType.ConvertChecked: + Out("checked((" + node.Type.Name + ")"); + break; + case ExpressionType.TypeAs: + break; + case ExpressionType.Not: + Out(node.Type == typeof(bool) ? "!" : "~"); + break; + case ExpressionType.Negate: + Out("-"); + break; + case ExpressionType.NegateChecked: + Out("checked(-"); + break; + case ExpressionType.UnaryPlus: + Out("+"); + break; + case ExpressionType.ArrayLength: + break; + case ExpressionType.Quote: + Out("'"); + break; + case ExpressionType.Throw: + Out(".throw "); + break; + } + + bool parenthesize = NeedsParentheses(node, node.Operand); + if (parenthesize) { + Out("("); + } + + Visit(node.Operand); + + if (parenthesize) { + Out(")"); + } + + switch (node.NodeType) { + case ExpressionType.Convert: + case ExpressionType.Throw: + break; + + case ExpressionType.ConvertChecked: + case ExpressionType.NegateChecked: + Out(")"); + break; + + case ExpressionType.TypeAs: + Out(Flow.Space, "as", Flow.Space | Flow.Break); + Out(node.Type.Name); + break; + + case ExpressionType.ArrayLength: + Out(".Length"); + break; + } + return node; + } + + protected internal override Expression VisitBlock(BlockExpression node) { + Out(node.Type == typeof(void) ? ".block " : ".comma "); + + if (node.Variables.Count > 0) { + VisitDeclarations('(', node.Variables, true); + } + + Out(" "); + + VisitExpressions('{', node.Expressions, true); + + return node; + } + + protected internal override Expression VisitDefault(DefaultExpression node) { + if (node.Type == typeof(void)) { + Out("/*empty*/"); + } else { + Out(".default(" + node.Type + ")"); + } + return node; + } + + protected internal override Expression VisitLabel(LabelExpression node) { + DumpLabel(node.Label); + Out(":", Flow.NewLine); + Visit(node.DefaultValue); + return node; + } + + protected internal override Expression VisitGoto(GotoExpression node) { + Out("." + node.Kind.ToString().ToLower(CultureInfo.CurrentCulture), Flow.Space); + DumpLabel(node.Target); + ParenthesizedVisit(node, node.Value); + Out("", Flow.Space); + return node; + } + + protected internal override Expression VisitLoop(LoopExpression node) { + Out(".loop", Flow.Space); + if (node.BreakLabel != null) { + Out("break:"); + DumpLabel(node.BreakLabel); + Out(Flow.Space, ""); + } + if (node.ContinueLabel != null) { + Out("continue:"); + DumpLabel(node.ContinueLabel); + Out(Flow.Space, ""); + } + Out(" {", Flow.NewLine); + Indent(); + Visit(node.Body); + Dedent(); + Out(Flow.NewLine, "}"); return node; + } + + protected override SwitchCase VisitSwitchCase(SwitchCase node) { + if (node.IsDefault) { + Out(".default"); + } else { + Out(".case " + node.Value); + } + Out(":", Flow.NewLine); + Indent(); Indent(); + Visit(node.Body); + Dedent(); Dedent(); + NewLine(); + return node; + } + + protected internal override Expression VisitSwitch(SwitchExpression node) { + Out(".switch "); + if (node.BreakLabel != null) { + DumpLabel(node.BreakLabel); + Out(" "); + } + Out("("); + Visit(node.Test); + Out(") {", Flow.NewLine); + Visit(node.SwitchCases, VisitSwitchCase); + Out("}", Flow.NewLine); + return node; + } + + protected override CatchBlock VisitCatchBlock(CatchBlock node) { + Out(Flow.NewLine, "} .catch (" + node.Test.Name); + if (node.Variable != null) { + Out(Flow.Space, node.Variable.Name ?? ""); + } + if (node.Filter != null) { + Out(") if (", Flow.Break); + Visit(node.Filter); + } + Out(") {", Flow.NewLine); + Indent(); + Visit(node.Body); + Dedent(); + return node; + } + + protected internal override Expression VisitTry(TryExpression node) { + Out(".try {", Flow.NewLine); + Indent(); + Visit(node.Body); + Dedent(); + Visit(node.Handlers, VisitCatchBlock); + if (node.Finally != null) { + Out(Flow.NewLine, "} .finally {", Flow.NewLine); + Indent(); + Visit(node.Finally); + Dedent(); + } else if (node.Fault != null) { + Out(Flow.NewLine, "} .fault {", Flow.NewLine); + Indent(); + Visit(node.Fault); + Dedent(); + } + + Out(Flow.NewLine, "}", Flow.NewLine); + return node; + } + + protected internal override Expression VisitIndex(IndexExpression node) { + if (node.Indexer != null) { + OutMember(node, node.Object, node.Indexer); + } else { + Visit(node.Object); + Out("."); + } + + VisitExpressions('[', node.Arguments); + return node; + } + + protected internal override Expression VisitExtension(Expression node) { + Out(".extension", Flow.Space); + + Out(node.GetType().Name, Flow.Space); + Out("(", Flow.Space); + // walk it + base.VisitExtension(node); + Out(")", Flow.NewLine); + return node; + } + + private static string GetLambdaInfo(LambdaExpression lambda) { + return String.Format(CultureInfo.CurrentCulture, ".lambda {0} {1} ()", lambda.ReturnType, lambda.Name); + } + + private void DumpLabel(LabelTarget target) { + if (string.IsNullOrEmpty(target.Name)) { + Out(String.Format(CultureInfo.CurrentCulture, ".label 0x{0:x8}", target.GetHashCode())); + } else { + Out(String.Format(CultureInfo.CurrentCulture, ".label '{0}'", target.Name)); + } + } + + private void DumpLambda(LambdaExpression node) { + Out(GetLambdaInfo(node)); + + VisitDeclarations('(', node.Parameters, true); + + Out(Flow.Space, "{", Flow.NewLine); + Indent(); + Visit(node.Body); + Dedent(); + Out(Flow.NewLine, "}"); + } + + #endregion + } +} + +#endif \ No newline at end of file diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/GotoExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/GotoExpression.cs new file mode 100644 index 0000000000..c27f4da7c1 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/GotoExpression.cs @@ -0,0 +1,133 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; +using System.Text; + +namespace System.Linq.Expressions { + public enum GotoExpressionKind { + Goto, + Return, + Break, + Continue, + } + + public sealed class GotoExpression : Expression { + private readonly GotoExpressionKind _kind; + private readonly Expression _value; + private readonly LabelTarget _target; + + internal GotoExpression(GotoExpressionKind kind, LabelTarget target, Expression value) { + _kind = kind; + _value = value; + _target = target; + } + + protected override Type GetExpressionType() { + return typeof(void); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Goto; + } + + /// + /// The value passed to the target, or null if the target is of type + /// System.Void + /// + public Expression Value { + get { return _value; } + } + + /// + /// The target label where this node jumps to + /// + public LabelTarget Target { + get { return _target; } + } + + /// + /// The kind of the goto. For information purposes only. + /// + public GotoExpressionKind Kind { + get { return _kind; } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitGoto(this); + } + } + + /// + /// Factory methods + /// + public partial class Expression { + public static GotoExpression Break(LabelTarget target) { + return MakeGoto(GotoExpressionKind.Break, target, null); + } + + public static GotoExpression Break(LabelTarget target, Expression value) { + return MakeGoto(GotoExpressionKind.Break, target, value); + } + + public static GotoExpression Continue(LabelTarget target) { + return MakeGoto(GotoExpressionKind.Continue, target, null); + } + + public static GotoExpression Return(LabelTarget target) { + return MakeGoto(GotoExpressionKind.Return, target, null); + } + + public static GotoExpression Return(LabelTarget target, Expression value) { + return MakeGoto(GotoExpressionKind.Return, target, value); + } + + public static GotoExpression Goto(LabelTarget target) { + return MakeGoto(GotoExpressionKind.Goto, target, null); + } + + public static GotoExpression Goto(LabelTarget target, Expression value) { + return MakeGoto(GotoExpressionKind.Goto, target, value); + } + + public static GotoExpression MakeGoto(GotoExpressionKind kind, LabelTarget target, Expression value) { + ValidateGoto(target, ref value, "target", "value"); + return new GotoExpression(kind, target, value); + } + + private static void ValidateGoto(LabelTarget target, ref Expression value, string targetParameter, string valueParameter) { + ContractUtils.RequiresNotNull(target, targetParameter); + if (value == null) { + ContractUtils.Requires(target.Type == typeof(void), Strings.LabelMustBeVoidOrHaveExpression); + } else { + ValidateGotoType(target.Type, ref value, valueParameter); + } + } + + // Standard argument validation, taken from ValidateArgumentTypes + private static void ValidateGotoType(Type expectedType, ref Expression value, string paramName) { + RequiresCanRead(value, paramName); + if (!TypeUtils.AreReferenceAssignable(expectedType, value.Type)) { + // C# autoquotes return values, so we'll do that here + if (TypeUtils.IsSameOrSubclass(typeof(Expression), expectedType) && + expectedType.IsAssignableFrom(value.GetType())) { + value = Expression.Quote(value); + } + throw Error.ExpressionTypeDoesNotMatchLabel(value.Type, expectedType); + } + } + + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/IArgumentProvider.cs b/ndp/fx/src/core/microsoft/scripting/Ast/IArgumentProvider.cs new file mode 100644 index 0000000000..672db37df7 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/IArgumentProvider.cs @@ -0,0 +1,72 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Linq.Expressions { + /// + /// Provides an internal interface for accessing the arguments that multiple tree + /// nodes (DynamicExpression, ElementInit, MethodCallExpression, InvocationExpression, NewExpression, + /// and InexExpression). + /// + /// This enables two optimizations which reduce the size of the trees. The first is it enables + /// the nodes to hold onto an IList of T instead of a ReadOnlyCollection. This saves the cost + /// of allocating the ReadOnlyCollection for each node. The second is that it enables specialized + /// subclasses to be created which hold onto a specific number of arguments. For example Block2, + /// Block3, Block4. These nodes can therefore avoid allocating both a ReadOnlyCollection and an + /// array for storing their elements saving 32 bytes per node. + /// + /// Meanwhile the nodes can continue to expose the original LINQ properties of ReadOnlyCollections. They + /// do this by re-using 1 field for storing both the array or an element that would normally be stored + /// in the array. + /// + /// For the array case the collection is typed to IList of T instead of ReadOnlyCollection of T. + /// When the node is initially constructed it is an array. When the compiler accesses the members it + /// uses this interface. If a user accesses the members the array is promoted to a ReadOnlyCollection. + /// + /// For the object case we store the 1st argument in a field typed to object and when the node is initially + /// constructed this holds directly onto the Expression. When the compiler accesses the members + /// it again uses this interface and the accessor for the 1st argument uses Expression.ReturnObject to + /// return the object which handles the Expression or ReadOnlyCollection case. When the user accesses + /// the ReadOnlyCollection then the object field is updated to hold directly onto the ReadOnlyCollection. + /// + /// It is important that the Expressions consistently return the same ReadOnlyCollection otherwise the + /// re-writer will be broken and it would be a breaking change from LINQ v1. The problem is that currently + /// users can rely on object identity to tell if the node has changed. Storing the ROC in an overloaded + /// field enables us to both reduce memory usage as well as maintain compatibility and an easy to use external + /// API. + /// + internal interface IArgumentProvider { + Expression GetArgument(int index); + int ArgumentCount { + get; + } + } + + static class ArgumentProviderOps { + internal static T[] Map(this IArgumentProvider collection, Func select) { + int count = collection.ArgumentCount; + T[] result = new T[count]; + count = 0; + for (int i = 0; i < count; i++) { + result[i] = select(collection.GetArgument(i)); + } + return result; + } + + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/IndexExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/IndexExpression.cs new file mode 100644 index 0000000000..9c8ddab933 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/IndexExpression.cs @@ -0,0 +1,258 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Reflection; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Text; + +namespace System.Linq.Expressions { + /// + /// Represents property or array indexing + /// + public sealed class IndexExpression : Expression, IArgumentProvider { + private readonly Expression _instance; + private readonly PropertyInfo _indexer; + private IList _arguments; + + internal IndexExpression( + Expression instance, + PropertyInfo indexer, + IList arguments) { + + if (indexer == null) { + Debug.Assert(instance != null && instance.Type.IsArray); + Debug.Assert(instance.Type.GetArrayRank() == arguments.Count); + } + + _instance = instance; + _indexer = indexer; + _arguments = arguments; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Index; + } + + protected override Type GetExpressionType() { + if (_indexer != null) { + return _indexer.PropertyType; + } + return _instance.Type.GetElementType(); + } + + public Expression Object { + get { return _instance; } + } + + /// + /// If this is an indexed property, returns the property + /// If this is an array indexing operation, returns null + /// + public PropertyInfo Indexer { + get { return _indexer; } + } + + public ReadOnlyCollection Arguments { + get { return ReturnReadOnly(ref _arguments); } + } + + Expression IArgumentProvider.GetArgument(int index) { + return _arguments[index]; + } + + int IArgumentProvider.ArgumentCount { + get { + return _arguments.Count; + } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitIndex(this); + } + } + + + /// + /// Factory methods. + /// + public partial class Expression { + + public static IndexExpression MakeIndex(Expression instance, PropertyInfo indexer, IEnumerable arguments) { + if (indexer != null) { + return Property(instance, indexer, arguments); + } else { + return ArrayAccess(instance, arguments); + } + } + + #region ArrayAccess + + public static IndexExpression ArrayAccess(Expression array, params Expression[] indexes) { + return ArrayAccess(array, (IEnumerable)indexes); + } + + public static IndexExpression ArrayAccess(Expression array, IEnumerable indexes) { + RequiresCanRead(array, "array"); + + Type arrayType = array.Type; + if (!arrayType.IsArray) { + throw Error.ArgumentMustBeArray(); + } + + var indexList = indexes.ToReadOnly(); + if (arrayType.GetArrayRank() != indexList.Count) { + throw Error.IncorrectNumberOfIndexes(); + } + + foreach (Expression e in indexList) { + RequiresCanRead(e, "indexes"); + if (e.Type != typeof(int)) { + throw Error.ArgumentMustBeArrayIndexType(); + } + } + + return new IndexExpression(array, null, indexList); + } + + #endregion + + #region Property + + public static IndexExpression Property(Expression instance, PropertyInfo indexer, params Expression[] arguments) { + return Property(instance, indexer, (IEnumerable)arguments); + } + + public static IndexExpression Property(Expression instance, PropertyInfo indexer, IEnumerable arguments) { + var argList = arguments.ToReadOnly(); + ValidateIndexedProperty(instance, indexer, ref argList); + return new IndexExpression(instance, indexer, argList); + } + + // CTS places no restrictions on properties (see ECMA-335 8.11.3), + // so we validate that the property conforms to CLS rules here. + // + // TODO: Do we still need all of this now that we take PropertyInfo? + // Does reflection help us out at all? Expression.Property skips all of + // these checks, so either it needs more checks or we need less here. + private static void ValidateIndexedProperty(Expression instance, PropertyInfo property, ref ReadOnlyCollection argList) { + + // If both getter and setter specified, all their parameter types + // should match, with exception of the last setter parameter which + // should match the type returned by the get method. + // Accessor parameters cannot be ByRef. + + ContractUtils.RequiresNotNull(property, "property"); + ContractUtils.Requires(!property.PropertyType.IsByRef, "property", Strings.PropertyCannotHaveRefType); + ContractUtils.Requires(property.PropertyType != typeof(void), "property", Strings.PropertyTypeCannotBeVoid); + + ParameterInfo[] getParameters = null; + MethodInfo getter = property.GetGetMethod(true); + if (getter != null) { + getParameters = getter.GetParametersCached(); + ValidateAccessor(instance, getter, getParameters, ref argList); + } + + MethodInfo setter = property.GetSetMethod(true); + if (setter != null) { + ParameterInfo[] setParameters = setter.GetParametersCached(); + ContractUtils.Requires(setParameters.Length > 0, "property", Strings.SetterHasNoParams); + + // valueType is the type of the value passed to the setter (last parameter) + Type valueType = setParameters[setParameters.Length - 1].ParameterType; + ContractUtils.Requires(!valueType.IsByRef, "property", Strings.PropertyCannotHaveRefType); + ContractUtils.Requires(setter.ReturnType == typeof(void), "property", Strings.SetterMustBeVoid); + ContractUtils.Requires(property.PropertyType == valueType, "property", Strings.PropertyTyepMustMatchSetter); + + if (getter != null) { + ContractUtils.Requires(!(getter.IsStatic ^ setter.IsStatic), "property", Strings.BothAccessorsMustBeStatic); + ContractUtils.Requires(getParameters.Length == setParameters.Length - 1, "property", Strings.IndexesOfSetGetMustMatch); + + for (int i = 0; i < getParameters.Length; i++) { + ContractUtils.Requires(getParameters[i].ParameterType == setParameters[i].ParameterType, "property", Strings.IndexesOfSetGetMustMatch); + } + } else { + ValidateAccessor(instance, setter, setParameters.RemoveLast(), ref argList); + } + } + + if (getter == null && setter == null) { + throw Error.PropertyDoesNotHaveAccessor(property); + } + } + + private static void ValidateAccessor(Expression instance, MethodInfo method, ParameterInfo[] indexes, ref ReadOnlyCollection arguments) { + ContractUtils.RequiresNotNull(arguments, "arguments"); + + ValidateMethodInfo(method); + ContractUtils.Requires((method.CallingConvention & CallingConventions.VarArgs) == 0, "method", Strings.AccessorsCannotHaveVarArgs); + if (method.IsStatic) { + ContractUtils.Requires(instance == null, "instance", Strings.OnlyStaticMethodsHaveNullInstance); + } else { + ContractUtils.Requires(instance != null, "method", Strings.OnlyStaticMethodsHaveNullInstance); + RequiresCanRead(instance, "instance"); + ValidateCallInstanceType(instance.Type, method); + } + + ValidateAccessorArgumentTypes(method, indexes, ref arguments); + } + + private static void ValidateAccessorArgumentTypes(MethodInfo method, ParameterInfo[] indexes, ref ReadOnlyCollection arguments) { + if (indexes.Length > 0) { + if (indexes.Length != arguments.Count) { + throw Error.IncorrectNumberOfMethodCallArguments(method); + } + Expression[] newArgs = null; + for (int i = 0, n = indexes.Length; i < n; i++) { + Expression arg = arguments[i]; + ParameterInfo pi = indexes[i]; + RequiresCanRead(arg, "arguments"); + + Type pType = pi.ParameterType; + ContractUtils.Requires(!pType.IsByRef, "indexes", Strings.AccessorsCannotHaveByRefArgs); + TypeUtils.ValidateType(pType); + + if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) { + if (TypeUtils.IsSameOrSubclass(typeof(Expression), pType) && pType.IsAssignableFrom(arg.GetType())) { + arg = Expression.Quote(arg); + } else { + throw Error.ExpressionTypeDoesNotMatchMethodParameter(arg.Type, pType, method); + } + } + if (newArgs == null && arg != arguments[i]) { + newArgs = new Expression[arguments.Count]; + for (int j = 0; j < i; j++) { + newArgs[j] = arguments[j]; + } + } + if (newArgs != null) { + newArgs[i] = arg; + } + } + if (newArgs != null) { + arguments = new ReadOnlyCollection(newArgs); + } + + } else if (arguments.Count > 0) { + throw Error.IncorrectNumberOfMethodCallArguments(method); + } + } + + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/InvocationExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/InvocationExpression.cs new file mode 100644 index 0000000000..e5e779879f --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/InvocationExpression.cs @@ -0,0 +1,99 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Text; + +namespace System.Linq.Expressions { + //CONFORMING + public sealed class InvocationExpression : Expression, IArgumentProvider { + private IList _arguments; + private readonly Expression _lambda; + private readonly Type _returnType; + + internal InvocationExpression(Expression lambda, IList arguments, Type returnType) { + + _lambda = lambda; + _arguments = arguments; + _returnType = returnType; + } + + protected override Type GetExpressionType() { + return _returnType; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Invoke; + } + + public Expression Expression { + get { return _lambda; } + } + + public ReadOnlyCollection Arguments { + get { return ReturnReadOnly(ref _arguments); } + } + + Expression IArgumentProvider.GetArgument(int index) { + return _arguments[index]; + } + + int IArgumentProvider.ArgumentCount { + get { + return _arguments.Count; + } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitInvocation(this); + } + } + + /// + /// Factory methods. + /// + public partial class Expression { + + //CONFORMING + public static InvocationExpression Invoke(Expression expression, params Expression[] arguments) { + return Invoke(expression, arguments.ToReadOnly()); + } + + //CONFORMING + public static InvocationExpression Invoke(Expression expression, IEnumerable arguments) { + RequiresCanRead(expression, "expression"); + + Type delegateType = expression.Type; + if (delegateType == typeof(Delegate)) { + throw Error.ExpressionTypeNotInvocable(delegateType); + } else if (!typeof(Delegate).IsAssignableFrom(expression.Type)) { + Type exprType = TypeUtils.FindGenericType(typeof(Expression<>), expression.Type); + if (exprType == null) { + throw Error.ExpressionTypeNotInvocable(expression.Type); + } + delegateType = exprType.GetGenericArguments()[0]; + } + + var mi = delegateType.GetMethod("Invoke"); + var args = arguments.ToReadOnly(); + ValidateArgumentTypes(mi, ExpressionType.Invoke, ref args); + return new InvocationExpression(expression, args, mi.ReturnType); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/LabelExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/LabelExpression.cs new file mode 100644 index 0000000000..3172ba4cd8 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/LabelExpression.cs @@ -0,0 +1,68 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + /// + /// Represents a label, which can be placed in any Expression context. If + /// it is jumped to, it will get the value provided by the corresponding + /// GotoExpression. Otherwise, it gets the value in DefaultValue. If the + /// Type equals System.Void, no value should be provided + /// + public sealed class LabelExpression : Expression { + private readonly Expression _defaultValue; + private readonly LabelTarget _label; + + internal LabelExpression(LabelTarget label, Expression defaultValue) { + _label = label; + _defaultValue = defaultValue; + } + + protected override Type GetExpressionType() { + return _label.Type; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Label; + } + + new public LabelTarget Label { + get { return _label; } + } + + /// + /// The value of the LabelExpression when the label is reached through + /// normal control flow (e.g. is not jumped to) + /// + public Expression DefaultValue { + get { return _defaultValue; } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitLabel(this); + } + } + + public partial class Expression { + public static LabelExpression Label(LabelTarget target) { + return Label(target, null); + } + public static LabelExpression Label(LabelTarget target, Expression defaultValue) { + ValidateGoto(target, ref defaultValue, "label", "defaultValue"); + return new LabelExpression(target, defaultValue); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/LabelTarget.cs b/ndp/fx/src/core/microsoft/scripting/Ast/LabelTarget.cs new file mode 100644 index 0000000000..fa382a2dcf --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/LabelTarget.cs @@ -0,0 +1,66 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + + /// + /// Used to denote the target of a GotoExpression + /// + public sealed class LabelTarget { + private readonly Type _type; + private readonly string _name; + + internal LabelTarget(Type type, string name) { + _type = type; + _name = name; + } + + // TODO: Annotations instead of name ? + public string Name { + get { return _name; } + } + + /// + /// The type of value that is passed when jumping to the label + /// (or System.Void if no value should be passed) + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + public Type Type { + get { return _type; } + } + } + + public partial class Expression { + public static LabelTarget Label() { + return Label(typeof(void), null); + } + + public static LabelTarget Label(string name) { + return Label(typeof(void), name); + } + + public static LabelTarget Label(Type type) { + return Label(type, null); + } + + public static LabelTarget Label(Type type, string name) { + ContractUtils.RequiresNotNull(type, "type"); + TypeUtils.ValidateType(type); + return new LabelTarget(type, name); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/LabeledStatement.cs b/ndp/fx/src/core/microsoft/scripting/Ast/LabeledStatement.cs new file mode 100644 index 0000000000..2b6cc05bf4 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/LabeledStatement.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Scripting.Utils; + +namespace System.Linq.Expressions { + /// + /// Represents a labeled statement + /// break and continue statements will jump to the end of body + /// + public sealed class LabeledStatement : Expression { + private readonly Expression _expression; + private readonly LabelTarget _label; + + internal LabeledStatement(Annotations annotations, LabelTarget label, Expression expression) + : base(ExpressionType.LabeledStatement, typeof(void), annotations) { + _label = label; + _expression = expression; + } + + // TODO: Resolve Label and Expression.Label() + new public LabelTarget Label { + get { return _label; } + } + + public Expression Statement { + get { return _expression; } + } + + internal override Expression Accept(ExpressionTreeVisitor visitor) { + return visitor.VisitLabeled(this); + } + } + + public partial class Expression { + public static LabeledStatement Labeled(LabelTarget label, Expression body) { + return Labeled(label, body, Annotations.Empty); + } + + public static LabeledStatement Labeled(LabelTarget label, Expression body, Annotations annotations) { + ContractUtils.RequiresNotNull(label, "label"); + RequiresCanRead(body, "body"); + return new LabeledStatement(annotations, label, body); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/LambdaExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/LambdaExpression.cs new file mode 100644 index 0000000000..e6262ec17e --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/LambdaExpression.cs @@ -0,0 +1,364 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions.Compiler; +using System.Reflection; +using System.Reflection.Emit; +using System.Dynamic.Utils; +using System.Text; +using System.Threading; + +namespace System.Linq.Expressions { + //CONFORMING + /// + /// This captures a block of code that should correspond to a .NET method + /// body. It takes input through parameters and is expected to be fully + /// bound. This code can then be generated in a variety of ways. The + /// variables can be kept as .NET locals or hoisted into an object bound to + /// the delegate. This is the primary unit used for passing around + /// Expression Trees in LINQ and the DLR. + /// + public abstract class LambdaExpression : Expression { + private readonly string _name; + private readonly Expression _body; + private readonly ReadOnlyCollection _parameters; + private readonly Type _delegateType; + + internal LambdaExpression( + Type delegateType, + string name, + Expression body, + ReadOnlyCollection parameters + ) { + + Assert.NotNull(delegateType); + + _name = name; + _body = body; + _parameters = parameters; + _delegateType = delegateType; + } + + protected override Type GetExpressionType() { + return _delegateType; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Lambda; + } + + public ReadOnlyCollection Parameters { + get { return _parameters; } + } + + public string Name { + get { return _name; } + } + + public Expression Body { + get { return _body; } + } + + public Type ReturnType { + get { return Type.GetMethod("Invoke").ReturnType; } + } + + public Delegate Compile() { + return LambdaCompiler.CompileLambda(this, false); + } + + public Delegate Compile(bool emitDebugSymbols) { + return LambdaCompiler.CompileLambda(this, emitDebugSymbols); + } + + // TODO: add an overload that returns a DynamicMethod? + // TODO: this method doesn't expose the full power of TypeBuilder.DefineMethod + public MethodBuilder CompileToMethod(TypeBuilder type, MethodAttributes attributes, bool emitDebugSymbols) { + ContractUtils.RequiresNotNull(type, "type"); + if (emitDebugSymbols) { + var module = type.Module as ModuleBuilder; + ContractUtils.Requires(module != null, "method", Strings.InvalidTypeBuilder); + } + return LambdaCompiler.CompileLambda(this, type, attributes, emitDebugSymbols); + } + + internal abstract LambdaExpression Accept(StackSpiller spiller); + } + + //CONFORMING + public sealed class Expression : LambdaExpression { + internal Expression( + string name, + Expression body, + ReadOnlyCollection parameters + ) + : base(typeof(TDelegate), name, body, parameters) { + } + + public new TDelegate Compile() { + return LambdaCompiler.CompileLambda(this, false); + } + + public new TDelegate Compile(bool emitDebugSymbols) { + return LambdaCompiler.CompileLambda(this, emitDebugSymbols); + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitLambda(this); + } + + internal override LambdaExpression Accept(StackSpiller spiller) { + return spiller.Rewrite(this); + } + } + + + public partial class Expression { + //internal lambda factory that creates an instance of Expression + internal static LambdaExpression Lambda( + ExpressionType nodeType, + Type delegateType, + string name, + Expression body, + ReadOnlyCollection parameters + ) { + if (nodeType == ExpressionType.Lambda) { + // got or create a delegate to the public Expression.Lambda method and call that will be used for + // creating instances of this delegate type + Func, LambdaExpression> func; + + if (_exprCtors == null) { + EnsureLambdaFastPathInitialized(); + } + + lock (_exprCtors) { + if (!_exprCtors.TryGetValue(delegateType, out func)) { + _exprCtors[delegateType] = func = (Func, LambdaExpression>) + Delegate.CreateDelegate( + typeof(Func, LambdaExpression>), + _lambdaCtorMethod.MakeGenericMethod(delegateType) + ); + } + } + + return func(body, name, parameters); + } + + return SlowMakeLambda(nodeType, delegateType, name, body, parameters); + } + + private static void EnsureLambdaFastPathInitialized() { + Interlocked.CompareExchange( + ref _exprCtors, + new CacheDict, LambdaExpression>>(200), + null + ); + + EnsureLambdaCtor(); + } + + private static void EnsureLambdaCtor() { + MethodInfo[] methods = (MethodInfo[])typeof(Expression).GetMember("Lambda", MemberTypes.Method, BindingFlags.Public | BindingFlags.Static); + foreach (MethodInfo mi in methods) { + if (!mi.IsGenericMethod) { + continue; + } + + ParameterInfo[] pis = mi.GetParameters(); + if (pis.Length == 3) { + if (pis[0].ParameterType == typeof(Expression) && + pis[1].ParameterType == typeof(string) && + pis[2].ParameterType == typeof(IEnumerable)) { + _lambdaCtorMethod = mi; + break; + } + } + } + Debug.Assert(_lambdaCtorMethod != null); + } + + private static LambdaExpression SlowMakeLambda(ExpressionType nodeType, Type delegateType, string name, Expression body, ReadOnlyCollection parameters) { + Type ot = typeof(Expression<>); + Type ct = ot.MakeGenericType(new Type[] { delegateType }); + Type[] ctorTypes = new Type[] { + typeof(ExpressionType), // nodeType, + typeof(string), // name, + typeof(Expression), // body, + typeof(ReadOnlyCollection) // parameters) + }; + ConstructorInfo ctor = ct.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, ctorTypes, null); + return (LambdaExpression)ctor.Invoke(new object[] { nodeType, name, body, parameters }); + } + + //CONFORMING + public static Expression Lambda(Expression body, params ParameterExpression[] parameters) { + return Lambda(body, (IEnumerable)parameters); + } + + //CONFORMING + public static Expression Lambda(Expression body, IEnumerable parameters) { + return Lambda(body, "lambda_method", parameters); + } + + //CONFORMING + public static Expression Lambda(Expression body, String name, IEnumerable parameters) { + ReadOnlyCollection parameterList = parameters.ToReadOnly(); + ValidateLambdaArgs(typeof(TDelegate), ref body, parameterList); + return new Expression(name, body, parameterList); + } + + + public static LambdaExpression Lambda(Expression body, params ParameterExpression[] parameters) { + return Lambda(body, (IEnumerable)parameters); + } + + public static LambdaExpression Lambda(Expression body, IEnumerable parameters) { + return Lambda(body, "lambda_method", parameters); + } + + //CONFORMING + public static LambdaExpression Lambda(Type delegateType, Expression body, params ParameterExpression[] parameters) { + return Lambda(delegateType, body, "lambda_method", parameters); + } + + //CONFORMING + public static LambdaExpression Lambda(Type delegateType, Expression body, IEnumerable parameters) { + return Lambda(delegateType, body, "lambda_method", parameters); + } + + //CONFORMING + public static LambdaExpression Lambda(Expression body, string name, IEnumerable parameters) { + ContractUtils.RequiresNotNull(name, "name"); + ContractUtils.RequiresNotNull(body, "body"); + + ReadOnlyCollection parameterList = parameters.ToReadOnly(); + + bool binder = body.Type == typeof(void); + + int paramCount = parameterList.Count; + Type[] typeArgs = new Type[paramCount + (binder ? 0 : 1)]; + for (int i = 0; i < paramCount; i++) { + ContractUtils.RequiresNotNull(parameterList[i], "parameter"); + typeArgs[i] = parameterList[i].Type; + } + + Type delegateType; + if (binder) + delegateType = GetActionType(typeArgs); + else { + typeArgs[paramCount] = body.Type; + delegateType = GetFuncType(typeArgs); + } + + return Lambda(ExpressionType.Lambda, delegateType, name, body, parameterList); + } + + //CONFORMING + public static LambdaExpression Lambda(Type delegateType, Expression body, string name, IEnumerable parameters) { + ReadOnlyCollection paramList = parameters.ToReadOnly(); + ValidateLambdaArgs(delegateType, ref body, paramList); + + return Lambda(ExpressionType.Lambda, delegateType, name, body, paramList); + } + + //CONFORMING + private static void ValidateLambdaArgs(Type delegateType, ref Expression body, ReadOnlyCollection parameters) { + ContractUtils.RequiresNotNull(delegateType, "delegateType"); + RequiresCanRead(body, "body"); + + if (!typeof(Delegate).IsAssignableFrom(delegateType) || delegateType == typeof(Delegate)) { + throw Error.LambdaTypeMustBeDerivedFromSystemDelegate(); + } + + MethodInfo mi; + lock (_LambdaDelegateCache) { + if (!_LambdaDelegateCache.TryGetValue(delegateType, out mi)) { + _LambdaDelegateCache[delegateType] = mi = delegateType.GetMethod("Invoke"); + } + } + + ParameterInfo[] pis = mi.GetParametersCached(); + + if (pis.Length > 0) { + if (pis.Length != parameters.Count) { + throw Error.IncorrectNumberOfLambdaDeclarationParameters(); + } + for (int i = 0, n = pis.Length; i < n; i++) { + ParameterExpression pex = parameters[i]; + ParameterInfo pi = pis[i]; + RequiresCanRead(pex, "parameters"); + Type pType = pi.ParameterType; + if (pex.IsByRef) { + if (!pType.IsByRef) { + //We cannot pass a parameter of T& to a delegate that takes T or any non-ByRef type. + throw Error.ParameterExpressionNotValidAsDelegate(pex.Type.MakeByRefType(), pType); + } + pType = pType.GetElementType(); + } + if (!TypeUtils.AreReferenceAssignable(pex.Type, pType)) { + throw Error.ParameterExpressionNotValidAsDelegate(pex.Type, pType); + } + } + } else if (parameters.Count > 0) { + throw Error.IncorrectNumberOfLambdaDeclarationParameters(); + } + if (mi.ReturnType != typeof(void) && !TypeUtils.AreReferenceAssignable(mi.ReturnType, body.Type)) { + if (TypeUtils.IsSameOrSubclass(typeof(Expression), mi.ReturnType) && mi.ReturnType.IsAssignableFrom(body.GetType())) { + body = Expression.Quote(body); + } else { + throw Error.ExpressionTypeDoesNotMatchReturn(body.Type, mi.ReturnType); + } + } + } + + //CONFORMING + public static Type GetFuncType(params Type[] typeArgs) { + ContractUtils.RequiresNotNull(typeArgs, "typeArgs"); + Type result = DelegateHelpers.GetFuncType(typeArgs); + if (result == null) { + throw Error.IncorrectNumberOfTypeArgsForFunc(); + } + return result; + } + + //CONFORMING + public static Type GetActionType(params Type[] typeArgs) { + ContractUtils.RequiresNotNull(typeArgs, "typeArgs"); + Type result = DelegateHelpers.GetActionType(typeArgs); + if (result == null) { + throw Error.IncorrectNumberOfTypeArgsForAction(); + } + return result; + } + + /// + /// Gets a Func or Action corresponding to the given type arguments. If + /// no Func or Action is large enough, it will generate a custom + /// delegate type. + /// + /// As with Func, the last argument is the return type. It can be set + /// to System.Void to produce a an Action. + /// + /// The type arguments of the delegate. + /// The delegate type. + public static Type GetDelegateType(params Type[] typeArgs) { + ContractUtils.RequiresNotEmpty(typeArgs, "typeArgs"); + return DelegateHelpers.MakeDelegateType(typeArgs); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/ListArgumentProvider.cs b/ndp/fx/src/core/microsoft/scripting/Ast/ListArgumentProvider.cs new file mode 100644 index 0000000000..1361292575 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/ListArgumentProvider.cs @@ -0,0 +1,139 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq.Expressions; + +namespace System.Linq.Expressions { + /// + /// Provides a wrapper around an IArgumentProvider which exposes the argument providers + /// members out as an IList of Expression. This is used to avoid allocating an array + /// which needs to be stored inside of a ReadOnlyCollection. Instead this type has + /// the same amount of overhead as an array without duplicating the storage of the + /// elements. This ensures that internally we can avoid creating and copying arrays + /// while users of the Expression trees also don't pay a size penalty for this internal + /// optimization. See IArgumentProvider for more general information on the Expression + /// tree optimizations being used here. + /// + class ListArgumentProvider : IList { + private readonly IArgumentProvider _provider; + private readonly Expression _arg0; + + internal ListArgumentProvider(IArgumentProvider provider, Expression arg0) { + _provider = provider; + _arg0 = arg0; + } + + #region IList Members + + public int IndexOf(Expression item) { + if (_arg0 == item) { + return 0; + } + + for (int i = 1; i < _provider.ArgumentCount; i++) { + if (_provider.GetArgument(i) == item) { + return i; + } + } + + return -1; + } + + public void Insert(int index, Expression item) { + throw new NotImplementedException(); + } + + public void RemoveAt(int index) { + throw new NotImplementedException(); + } + + public Expression this[int index] { + get { + if (index == 0) { + return _arg0; + } + + return _provider.GetArgument(index); + } + set { + throw new NotImplementedException(); + } + } + + #endregion + + #region ICollection Members + + public void Add(Expression item) { + throw new NotImplementedException(); + } + + public void Clear() { + throw new NotImplementedException(); + } + + public bool Contains(Expression item) { + return IndexOf(item) != -1; + } + + public void CopyTo(Expression[] array, int arrayIndex) { + array[arrayIndex++] = _arg0; + for (int i = 1; i < _provider.ArgumentCount; i++) { + array[arrayIndex++] = _provider.GetArgument(i); + } + } + + public int Count { + get { return _provider.ArgumentCount; } + } + + public bool IsReadOnly { + get { return true; } + } + + public bool Remove(Expression item) { + throw new NotImplementedException(); + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + yield return _arg0; + + for (int i = 1; i < _provider.ArgumentCount; i++) { + yield return _provider.GetArgument(i); + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + yield return _arg0; + + for (int i = 1; i < _provider.ArgumentCount; i++) { + yield return _provider.GetArgument(i); + } + } + + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/ListInitExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/ListInitExpression.cs new file mode 100644 index 0000000000..0335510c3d --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/ListInitExpression.cs @@ -0,0 +1,124 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Text; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + //CONFORMING + public sealed class ListInitExpression : Expression { + private readonly NewExpression _newExpression; + private readonly ReadOnlyCollection _initializers; + + internal ListInitExpression(NewExpression newExpression, ReadOnlyCollection initializers) { + _newExpression = newExpression; + _initializers = initializers; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.ListInit; + } + + protected override Type GetExpressionType() { + return _newExpression.Type; + } + + public override bool CanReduce { + get { + return true; + } + } + + public NewExpression NewExpression { + get { return _newExpression; } + } + public ReadOnlyCollection Initializers { + get { return _initializers; } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitListInit(this); + } + + public override Expression Reduce() { + return MemberInitExpression.ReduceListInit(_newExpression, _initializers, true); + } + } + + + public partial class Expression { + //CONFORMING + public static ListInitExpression ListInit(NewExpression newExpression, params Expression[] initializers) { + ContractUtils.RequiresNotNull(newExpression, "newExpression"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + return ListInit(newExpression, initializers as IEnumerable); + } + //CONFORMING + public static ListInitExpression ListInit(NewExpression newExpression, IEnumerable initializers) { + ContractUtils.RequiresNotNull(newExpression, "newExpression"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + ReadOnlyCollection initializerlist = initializers.ToReadOnly(); + if (initializerlist.Count == 0) { + throw Error.ListInitializerWithZeroMembers(); + } + MethodInfo addMethod = FindMethod(newExpression.Type, "Add", null, new Expression[] { initializerlist[0] }, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + return ListInit(newExpression, addMethod, initializers); + } + //CONFORMING + public static ListInitExpression ListInit(NewExpression newExpression, MethodInfo addMethod, params Expression[] initializers) { + if (addMethod == null) { + return ListInit(newExpression, initializers as IEnumerable); + } + ContractUtils.RequiresNotNull(newExpression, "newExpression"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + return ListInit(newExpression, addMethod, initializers as IEnumerable); + } + //CONFORMING + public static ListInitExpression ListInit(NewExpression newExpression, MethodInfo addMethod, IEnumerable initializers) { + if (addMethod == null) { + return ListInit(newExpression, initializers); + } + ContractUtils.RequiresNotNull(newExpression, "newExpression"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + ReadOnlyCollection initializerlist = initializers.ToReadOnly(); + if (initializerlist.Count == 0) { + throw Error.ListInitializerWithZeroMembers(); + } + ElementInit[] initList = new ElementInit[initializerlist.Count]; + for (int i = 0; i < initializerlist.Count; i++) { + initList[i] = ElementInit(addMethod, initializerlist[i]); + } + return ListInit(newExpression, new ReadOnlyCollection(initList)); + } + //CONFORMING + public static ListInitExpression ListInit(NewExpression newExpression, params ElementInit[] initializers) { + return ListInit(newExpression, (IEnumerable)initializers); + } + //CONFORMING + public static ListInitExpression ListInit(NewExpression newExpression, IEnumerable initializers) { + ContractUtils.RequiresNotNull(newExpression, "newExpression"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + ReadOnlyCollection initializerlist = initializers.ToReadOnly(); + if (initializerlist.Count == 0) { + throw Error.ListInitializerWithZeroMembers(); + } + ValidateListInitArgs(newExpression.Type, initializerlist); + return new ListInitExpression(newExpression, initializerlist); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/LoopExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/LoopExpression.cs new file mode 100644 index 0000000000..4bd9aa9713 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/LoopExpression.cs @@ -0,0 +1,79 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + public sealed class LoopExpression : Expression { + private readonly Expression _body; + private readonly LabelTarget _break; + private readonly LabelTarget _continue; + + /// + /// Null test means infinite loop. + /// + internal LoopExpression(Expression body, LabelTarget @break, LabelTarget @continue) { + _body = body; + _break = @break; + _continue = @continue; + } + + protected override Type GetExpressionType() { + return typeof(void); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Loop; + } + + public Expression Body { + get { return _body; } + } + + public LabelTarget BreakLabel { + get { return _break; } + } + + public LabelTarget ContinueLabel { + get { return _continue; } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitLoop(this); + } + } + + /// + /// Factory methods. + /// TODO: review which of these overloads we actually need + /// + public partial class Expression { + public static LoopExpression Loop(Expression body) { + return Loop(body, null); + } + + public static LoopExpression Loop(Expression body, LabelTarget @break) { + return Loop(body, @break, null); + } + + public static LoopExpression Loop(Expression body, LabelTarget @break, LabelTarget @continue) { + RequiresCanRead(body, "body"); + // TODO: lift the restriction on break, and allow loops to have non-void type + ContractUtils.Requires(@break == null || @break.Type == typeof(void), "break", Strings.LabelTypeMustBeVoid); + ContractUtils.Requires(@continue == null || @continue.Type == typeof(void), "continue", Strings.LabelTypeMustBeVoid); + return new LoopExpression(body, @break, @continue); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/MemberAssignment.cs b/ndp/fx/src/core/microsoft/scripting/Ast/MemberAssignment.cs new file mode 100644 index 0000000000..d2ff1a2bac --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/MemberAssignment.cs @@ -0,0 +1,73 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Text; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + //CONFORMING + public sealed class MemberAssignment : MemberBinding { + Expression _expression; + internal MemberAssignment(MemberInfo member, Expression expression) + : base(MemberBindingType.Assignment, member) { + _expression = expression; + } + public Expression Expression { + get { return _expression; } + } + } + + + public partial class Expression { + //CONFORMING + public static MemberAssignment Bind(MemberInfo member, Expression expression) { + ContractUtils.RequiresNotNull(member, "member"); + RequiresCanRead(expression, "expression"); + Type memberType; + ValidateSettableFieldOrPropertyMember(member, out memberType); + if (!memberType.IsAssignableFrom(expression.Type)) { + throw Error.ArgumentTypesMustMatch(); + } + return new MemberAssignment(member, expression); + } + + //CONFORMING + public static MemberAssignment Bind(MethodInfo propertyAccessor, Expression expression) { + ContractUtils.RequiresNotNull(propertyAccessor, "propertyAccessor"); + ContractUtils.RequiresNotNull(expression, "expression"); + ValidateMethodInfo(propertyAccessor); + return Bind(GetProperty(propertyAccessor), expression); + } + + //CONFORMING + private static void ValidateSettableFieldOrPropertyMember(MemberInfo member, out Type memberType) { + FieldInfo fi = member as FieldInfo; + if (fi == null) { + PropertyInfo pi = member as PropertyInfo; + if (pi == null) { + throw Error.ArgumentMustBeFieldInfoOrPropertInfo(); + } + if (!pi.CanWrite) { + throw Error.PropertyDoesNotHaveSetter(pi); + } + memberType = pi.PropertyType; + } else { + memberType = fi.FieldType; + } + } + } +} \ No newline at end of file diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/MemberBinding.cs b/ndp/fx/src/core/microsoft/scripting/Ast/MemberBinding.cs new file mode 100644 index 0000000000..01a35a3285 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/MemberBinding.cs @@ -0,0 +1,48 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using System.Text; + +namespace System.Linq.Expressions { + //CONFORMING + public enum MemberBindingType { + Assignment, + MemberBinding, + ListBinding + } + + //CONFORMING + // TODO: If annotations is added here, make sure MemberInitExpression.Reduce + // methods and ExpressionVisitor.Visit methods preserve it + // (But this probably does not need annotations) + public abstract class MemberBinding { + MemberBindingType _type; + MemberInfo _member; + protected MemberBinding(MemberBindingType type, MemberInfo member) { + _type = type; + _member = member; + } + public MemberBindingType BindingType { + get { return _type; } + } + public MemberInfo Member { + get { return _member; } + } + public override string ToString() { + return ExpressionStringBuilder.MemberBindingToString(this); + } + } +} \ No newline at end of file diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/MemberExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/MemberExpression.cs new file mode 100644 index 0000000000..2ee9694b39 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/MemberExpression.cs @@ -0,0 +1,285 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Text; + +namespace System.Linq.Expressions { + + /// + /// Member expression (statically typed) which represents + /// property or field access, both static and instance. + /// For instance property/field, Expression must be != null. + /// + public class MemberExpression : Expression { + private readonly Expression _expression; + + public MemberInfo Member { + get { return GetMember(); } + } + + public Expression Expression { + get { return _expression; } + } + + // param order: factories args in order, then other args + internal MemberExpression(Expression expression) { + + _expression = expression; + } + + internal static MemberExpression Make(Expression expression, MemberInfo member) { + if (member.MemberType == MemberTypes.Field) { + FieldInfo fi = (FieldInfo)member; + return new FieldExpression(expression, fi); + } else { + PropertyInfo pi = (PropertyInfo)member; + return new PropertyExpression(expression, pi); + } + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.MemberAccess; + } + + internal virtual MemberInfo GetMember() { + throw new NotImplementedException(); + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitMember(this); + } + } + + internal class FieldExpression : MemberExpression { + private readonly FieldInfo _field; + + public FieldExpression(Expression expression, FieldInfo member) + : base(expression) { + _field = member; + } + + internal override MemberInfo GetMember() { + return _field; + } + + protected override Type GetExpressionType() { + return _field.FieldType; + } + } + + internal class PropertyExpression : MemberExpression { + private readonly PropertyInfo _property; + public PropertyExpression(Expression expression, PropertyInfo member) + : base(expression) { + _property = member; + } + + internal override MemberInfo GetMember() { + return _property; + } + + protected override Type GetExpressionType() { + return _property.PropertyType; + } + } + + /// + /// Factory methods. + /// + public partial class Expression { + + #region Field + + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames")] + public static MemberExpression Field(Expression expression, FieldInfo field) { + ContractUtils.RequiresNotNull(field, "field"); + + if (field.IsStatic) { + ContractUtils.Requires(expression == null, "expression", Strings.OnlyStaticFieldsHaveNullInstance); + } else { + ContractUtils.Requires(expression != null, "field", Strings.OnlyStaticFieldsHaveNullInstance); + RequiresCanRead(expression, "expression"); + if (!TypeUtils.AreReferenceAssignable(field.DeclaringType, expression.Type)) { + throw Error.FieldInfoNotDefinedForType(field.DeclaringType, field.Name, expression.Type); + } + } + return MemberExpression.Make(expression, field); + } + + //CONFORMING + public static MemberExpression Field(Expression expression, string fieldName) { + RequiresCanRead(expression, "expression"); + + // bind to public names first + FieldInfo fi = expression.Type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); + if (fi == null) { + fi = expression.Type.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); + } + if (fi == null) { + throw Error.FieldNotDefinedForType(fieldName, expression.Type); + } + return Expression.Field(expression, fi); + } + + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames")] + public static MemberExpression Field(Expression expression, Type type, string fieldName) { + ContractUtils.RequiresNotNull(type, "type"); + + // bind to public names first + FieldInfo fi = type.GetField(fieldName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); + if (fi == null) { + fi = type.GetField(fieldName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); + } + + if (fi == null) { + throw Error.FieldNotDefinedForType(fieldName, type); + } + return Expression.Field(expression, fi); + } + #endregion + + #region Property + + //CONFORMING + public static MemberExpression Property(Expression expression, string propertyName) { + RequiresCanRead(expression, "expression"); + // bind to public names first + PropertyInfo pi = expression.Type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); + if (pi == null) { + pi = expression.Type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); + } + if (pi == null) { + throw Error.PropertyNotDefinedForType(propertyName, expression.Type); + } + return Property(expression, pi); + } + + public static MemberExpression Property(Expression expression, Type type, string propertyName) { + ContractUtils.RequiresNotNull(type, "type"); + // bind to public names first + PropertyInfo pi = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); + if (pi == null) { + pi = type.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); + } + if (pi == null) { + throw Error.PropertyNotDefinedForType(propertyName, type); + } + return Property(expression, pi); + } + + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames")] + public static MemberExpression Property(Expression expression, PropertyInfo property) { + ContractUtils.RequiresNotNull(property, "property"); + + MethodInfo mi = property.GetGetMethod(true) ?? property.GetSetMethod(true); + + if (mi == null) { + throw Error.PropertyDoesNotHaveAccessor(property); + } + + if (mi.IsStatic) { + ContractUtils.Requires(expression == null, "expression", Strings.OnlyStaticPropertiesHaveNullInstance); + } else { + ContractUtils.Requires(expression != null, "property", Strings.OnlyStaticPropertiesHaveNullInstance); + RequiresCanRead(expression, "expression"); + if (!TypeUtils.IsValidInstanceType(property, expression.Type)) { + throw Error.PropertyNotDefinedForType(property, expression.Type); + } + } + return MemberExpression.Make(expression, property); + } + //CONFORMING + public static MemberExpression Property(Expression expression, MethodInfo propertyAccessor) { + ContractUtils.RequiresNotNull(propertyAccessor, "propertyAccessor"); + ValidateMethodInfo(propertyAccessor); + return Property(expression, GetProperty(propertyAccessor)); + } + + //CONFORMING + private static PropertyInfo GetProperty(MethodInfo mi) { + Type type = mi.DeclaringType; + BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic; + flags |= (mi.IsStatic) ? BindingFlags.Static : BindingFlags.Instance; + PropertyInfo[] props = type.GetProperties(flags); + foreach (PropertyInfo pi in props) { + if (pi.CanRead && CheckMethod(mi, pi.GetGetMethod(true))) { + return pi; + } + if (pi.CanWrite && CheckMethod(mi, pi.GetSetMethod(true))) { + return pi; + } + } + throw Error.MethodNotPropertyAccessor(mi.DeclaringType, mi.Name); + } + + //CONFORMING + private static bool CheckMethod(MethodInfo method, MethodInfo propertyMethod) { + if (method == propertyMethod) { + return true; + } + // If the type is an interface then the handle for the method got by the compiler will not be the + // same as that returned by reflection. + // Check for this condition and try and get the method from reflection. + Type type = method.DeclaringType; + if (type.IsInterface && method.Name == propertyMethod.Name && type.GetMethod(method.Name) == propertyMethod) { + return true; + } + return false; + } + + #endregion + + //CONFORMING + public static MemberExpression PropertyOrField(Expression expression, string propertyOrFieldName) { + RequiresCanRead(expression, "expression"); + // bind to public names first + PropertyInfo pi = expression.Type.GetProperty(propertyOrFieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); + if (pi != null) + return Property(expression, pi); + FieldInfo fi = expression.Type.GetField(propertyOrFieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); + if (fi != null) + return Field(expression, fi); + pi = expression.Type.GetProperty(propertyOrFieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); + if (pi != null) + return Property(expression, pi); + fi = expression.Type.GetField(propertyOrFieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); + if (fi != null) + return Field(expression, fi); + + throw Error.NotAMemberOfType(propertyOrFieldName, expression.Type); + } + + //CONFORMING + public static MemberExpression MakeMemberAccess(Expression expression, MemberInfo member) { + ContractUtils.RequiresNotNull(member, "member"); + + FieldInfo fi = member as FieldInfo; + if (fi != null) { + return Expression.Field(expression, fi); + } + PropertyInfo pi = member as PropertyInfo; + if (pi != null) { + return Expression.Property(expression, pi); + } + throw Error.MemberNotFieldOrProperty(member); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/MemberInitExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/MemberInitExpression.cs new file mode 100644 index 0000000000..8b6c25135d --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/MemberInitExpression.cs @@ -0,0 +1,114 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic.Utils; +using System.Text; + +namespace System.Linq.Expressions { + //CONFORMING + public sealed class MemberInitExpression : Expression { + private readonly NewExpression _newExpression; + private readonly ReadOnlyCollection _bindings; + + internal MemberInitExpression(NewExpression newExpression, ReadOnlyCollection bindings) { + _newExpression = newExpression; + _bindings = bindings; + } + + protected override Type GetExpressionType() { + return _newExpression.Type; + } + + public override bool CanReduce { + get { + return true; + } + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.MemberInit; + } + + public NewExpression NewExpression { + get { return _newExpression; } + } + public ReadOnlyCollection Bindings { + get { return _bindings; } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitMemberInit(this); + } + + public override Expression Reduce() { + return ReduceMemberInit(_newExpression, _bindings, true); + } + + internal static Expression ReduceMemberInit(Expression objExpression, ReadOnlyCollection bindings, bool keepOnStack) { + var objVar = Expression.Variable(objExpression.Type, null); + int count = bindings.Count; + var block = new Expression[count + 2]; + block[0] = Expression.Assign(objVar, objExpression); + for (int i = 0; i < count; i++) { + block[i + 1] = ReduceMemberBinding(objVar, bindings[i]); + } + block[count + 1] = keepOnStack ? (Expression)objVar : Expression.Empty(); + return Expression.Block(new ReadOnlyCollection(block)); + } + + internal static Expression ReduceListInit(Expression listExpression, ReadOnlyCollection initializers, bool keepOnStack) { + var listVar = Expression.Variable(listExpression.Type, null); + int count = initializers.Count; + var block = new Expression[count + 2]; + block[0] = Expression.Assign(listVar, listExpression); + for (int i = 0; i < count; i++) { + ElementInit element = initializers[i]; + block[i + 1] = Expression.Call(listVar, element.AddMethod, element.Arguments); + } + block[count + 1] = keepOnStack ? (Expression)listVar : Expression.Empty(); + return Expression.Block(new ReadOnlyCollection(block)); + } + + internal static Expression ReduceMemberBinding(ParameterExpression objVar, MemberBinding binding) { + MemberExpression member = Expression.MakeMemberAccess(objVar, binding.Member); + switch (binding.BindingType) { + case MemberBindingType.Assignment: + return Expression.Assign(member, ((MemberAssignment)binding).Expression); + case MemberBindingType.ListBinding: + return ReduceListInit(member, ((MemberListBinding)binding).Initializers, false); + case MemberBindingType.MemberBinding: + return ReduceMemberInit(member, ((MemberMemberBinding)binding).Bindings, false); + default: throw Assert.Unreachable; + } + } + } + + public partial class Expression { + //CONFORMING + public static MemberInitExpression MemberInit(NewExpression newExpression, params MemberBinding[] bindings) { + return MemberInit(newExpression, (IEnumerable)bindings); + } + //CONFORMING + public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable bindings) { + ContractUtils.RequiresNotNull(newExpression, "newExpression"); + ContractUtils.RequiresNotNull(bindings, "bindings"); + ReadOnlyCollection roBindings = bindings.ToReadOnly(); + ValidateMemberInitArgs(newExpression.Type, roBindings); + return new MemberInitExpression(newExpression, roBindings); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/MemberListBinding.cs b/ndp/fx/src/core/microsoft/scripting/Ast/MemberListBinding.cs new file mode 100644 index 0000000000..e678d12116 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/MemberListBinding.cs @@ -0,0 +1,80 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Text; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + //CONFORMING + public sealed class MemberListBinding : MemberBinding { + ReadOnlyCollection _initializers; + internal MemberListBinding(MemberInfo member, ReadOnlyCollection initializers) + : base(MemberBindingType.ListBinding, member) { + _initializers = initializers; + } + public ReadOnlyCollection Initializers { + get { return _initializers; } + } + } + + + public partial class Expression { + //CONFORMING + public static MemberListBinding ListBind(MemberInfo member, params ElementInit[] initializers) { + ContractUtils.RequiresNotNull(member, "member"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + return ListBind(member, initializers.ToReadOnly()); + } + //CONFORMING + public static MemberListBinding ListBind(MemberInfo member, IEnumerable initializers) { + ContractUtils.RequiresNotNull(member, "member"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + Type memberType; + ValidateGettableFieldOrPropertyMember(member, out memberType); + ReadOnlyCollection initList = initializers.ToReadOnly(); + ValidateListInitArgs(memberType, initList); + return new MemberListBinding(member, initList); + } + //CONFORMING + public static MemberListBinding ListBind(MethodInfo propertyAccessor, params ElementInit[] initializers) { + ContractUtils.RequiresNotNull(propertyAccessor, "propertyAccessor"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + return ListBind(propertyAccessor, initializers.ToReadOnly()); + } + //CONFORMING + public static MemberListBinding ListBind(MethodInfo propertyAccessor, IEnumerable initializers) { + ContractUtils.RequiresNotNull(propertyAccessor, "propertyAccessor"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + return ListBind(GetProperty(propertyAccessor), initializers); + } + + //CONFORMING + private static void ValidateListInitArgs(Type listType, ReadOnlyCollection initializers) { + if (!typeof(IEnumerable).IsAssignableFrom(listType)) { + throw Error.TypeNotIEnumerable(listType); + } + for (int i = 0, n = initializers.Count; i < n; i++) { + ElementInit element = initializers[i]; + ContractUtils.RequiresNotNull(element, "initializers"); + ValidateCallInstanceType(listType, element.AddMethod); + } + } + } +} \ No newline at end of file diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/MemberMemberBinding.cs b/ndp/fx/src/core/microsoft/scripting/Ast/MemberMemberBinding.cs new file mode 100644 index 0000000000..7a6d614bcf --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/MemberMemberBinding.cs @@ -0,0 +1,93 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Text; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + //CONFORMING + public sealed class MemberMemberBinding : MemberBinding { + ReadOnlyCollection _bindings; + internal MemberMemberBinding(MemberInfo member, ReadOnlyCollection bindings) + : base(MemberBindingType.MemberBinding, member) { + _bindings = bindings; + } + public ReadOnlyCollection Bindings { + get { return _bindings; } + } + } + + + public partial class Expression { + //CONFORMING + public static MemberMemberBinding MemberBind(MemberInfo member, params MemberBinding[] bindings) { + ContractUtils.RequiresNotNull(member, "member"); + ContractUtils.RequiresNotNull(bindings, "bindings"); + return MemberBind(member, bindings.ToReadOnly()); + } + //CONFORMING + public static MemberMemberBinding MemberBind(MemberInfo member, IEnumerable bindings) { + ContractUtils.RequiresNotNull(member, "member"); + ContractUtils.RequiresNotNull(bindings, "bindings"); + ReadOnlyCollection roBindings = bindings.ToReadOnly(); + Type memberType; + ValidateGettableFieldOrPropertyMember(member, out memberType); + ValidateMemberInitArgs(memberType, roBindings); + return new MemberMemberBinding(member, roBindings); + } + //CONFORMING + public static MemberMemberBinding MemberBind(MethodInfo propertyAccessor, params MemberBinding[] bindings) { + ContractUtils.RequiresNotNull(propertyAccessor, "propertyAccessor"); + return MemberBind(GetProperty(propertyAccessor), bindings); + } + //CONFORMING + public static MemberMemberBinding MemberBind(MethodInfo propertyAccessor, IEnumerable bindings) { + ContractUtils.RequiresNotNull(propertyAccessor, "propertyAccessor"); + return MemberBind(GetProperty(propertyAccessor), bindings); + } + + + //CONFORMING + private static void ValidateGettableFieldOrPropertyMember(MemberInfo member, out Type memberType) { + FieldInfo fi = member as FieldInfo; + if (fi == null) { + PropertyInfo pi = member as PropertyInfo; + if (pi == null) { + throw Error.ArgumentMustBeFieldInfoOrPropertInfo(); + } + if (!pi.CanRead) { + throw Error.PropertyDoesNotHaveGetter(pi); + } + memberType = pi.PropertyType; + } else { + memberType = fi.FieldType; + } + } + //CONFORMING + private static void ValidateMemberInitArgs(Type type, ReadOnlyCollection bindings) { + for (int i = 0, n = bindings.Count; i < n; i++) { + MemberBinding b = bindings[i]; + ContractUtils.RequiresNotNull(b, "bindings"); + if (!b.Member.DeclaringType.IsAssignableFrom(type)) { + throw Error.NotAMemberOfType(b.Member.Name, type); + } + } + } + } +} \ No newline at end of file diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/MethodCallExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/MethodCallExpression.cs new file mode 100644 index 0000000000..ad2fe386e8 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/MethodCallExpression.cs @@ -0,0 +1,877 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; +using System.Text; + +namespace System.Linq.Expressions { + //CONFORMING + public class MethodCallExpression : Expression, IArgumentProvider { + private readonly MethodInfo _method; + + internal MethodCallExpression(MethodInfo method) { + + _method = method; + } + + internal static MethodCallExpression Make( + MethodInfo method, + Expression instance, + ReadOnlyCollection arguments) { + if (instance == null) { + return new MethodCallExpressionN(method, arguments); + } else { + return new InstanceMethodCallExpressionN(method, instance, arguments); + } + } + + internal virtual Expression GetInstance() { + return null; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Call; + } + + protected override Type GetExpressionType() { + return _method.ReturnType; + } + + public MethodInfo Method { + get { return _method; } + } + + public Expression Object { + get { return GetInstance(); } + } + + public ReadOnlyCollection Arguments { + get { return GetOrMakeArguments(); } + } + + internal virtual ReadOnlyCollection GetOrMakeArguments() { + throw new NotImplementedException(GetType().FullName); + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitMethodCall(this); + } + + /// + /// Returns a new MethodCallExpression replacing the existing instance/args with the + /// newly provided instance and args. Arguments can be null to use the existing + /// arguments. + /// + /// This helper is provided to allow re-writing of nodes to not depend on the specific optimized + /// subclass of MethodCallExpression which is being used. + /// + internal virtual MethodCallExpression Rewrite(Expression instance, IList args) { + throw new NotImplementedException(); + } + + #region IArgumentProvider Members + + Expression IArgumentProvider.GetArgument(int index) { + throw new NotImplementedException(); + } + + int IArgumentProvider.ArgumentCount { + get { throw new NotImplementedException(); } + } + + #endregion + } + + #region Specialized Subclasses + + internal class MethodCallExpressionN : MethodCallExpression, IArgumentProvider { + private IList _arguments; + + public MethodCallExpressionN(MethodInfo method, IList args) + : base(method) { + _arguments = args; + } + + Expression IArgumentProvider.GetArgument(int index) { + return _arguments[index]; + } + + int IArgumentProvider.ArgumentCount { + get { + return _arguments.Count; + } + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(ref _arguments); + } + + internal override MethodCallExpression Rewrite(Expression instance, IList args) { + Debug.Assert(instance == null); + Debug.Assert(args == null || args.Count == _arguments.Count); + + return new MethodCallExpressionN(Method, args ?? _arguments); + } + } + + internal class InstanceMethodCallExpressionN : MethodCallExpression, IArgumentProvider { + private IList _arguments; + private readonly Expression _instance; + + public InstanceMethodCallExpressionN(MethodInfo method, Expression instance, IList args) + : base(method) { + _instance = instance; + _arguments = args; + } + + Expression IArgumentProvider.GetArgument(int index) { + return _arguments[index]; + } + + int IArgumentProvider.ArgumentCount { + get { + return _arguments.Count; + } + } + + internal override Expression GetInstance() { + return _instance; + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(ref _arguments); + } + + internal override MethodCallExpression Rewrite(Expression instance, IList args) { + Debug.Assert(instance != null); + Debug.Assert(args == null || args.Count == _arguments.Count); + + return new InstanceMethodCallExpressionN(Method, instance, args ?? _arguments); + } + } + + internal class MethodCallExpression1 : MethodCallExpression, IArgumentProvider { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider + + public MethodCallExpression1(MethodInfo method, Expression arg0) + : base(method) { + _arg0 = arg0; + } + + Expression IArgumentProvider.GetArgument(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + default: throw new InvalidOperationException(); + } + } + + int IArgumentProvider.ArgumentCount { + get { + return 1; + } + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(this, ref _arg0); + } + + internal override MethodCallExpression Rewrite(Expression instance, IList args) { + Debug.Assert(instance == null); + Debug.Assert(args == null || args.Count == 1); + + if (args != null) { + return new MethodCallExpression1(Method, args[0]); + } + + return new MethodCallExpression1(Method, ReturnObject(_arg0)); + } + } + + internal class MethodCallExpression2 : MethodCallExpression, IArgumentProvider { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider + private readonly Expression _arg1; // storage for the 2nd arg + + public MethodCallExpression2(MethodInfo method, Expression arg0, Expression arg1) + : base(method) { + _arg0 = arg0; + _arg1 = arg1; + } + + Expression IArgumentProvider.GetArgument(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + default: throw new InvalidOperationException(); + } + } + + int IArgumentProvider.ArgumentCount { + get { + return 2; + } + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(this, ref _arg0); + } + + internal override MethodCallExpression Rewrite(Expression instance, IList args) { + Debug.Assert(instance == null); + Debug.Assert(args == null || args.Count == 2); + + if (args != null) { + return new MethodCallExpression2(Method, args[0], args[1]); + } + return new MethodCallExpression2(Method, ReturnObject(_arg0), _arg1); + } + } + + internal class MethodCallExpression3 : MethodCallExpression, IArgumentProvider { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider + private readonly Expression _arg1, _arg2; // storage for the 2nd - 3rd args. + + public MethodCallExpression3(MethodInfo method, Expression arg0, Expression arg1, Expression arg2) + : base(method) { + _arg0 = arg0; + _arg1 = arg1; + _arg2 = arg2; + } + + Expression IArgumentProvider.GetArgument(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + case 2: return _arg2; + default: throw new InvalidOperationException(); + } + } + + int IArgumentProvider.ArgumentCount { + get { + return 3; + } + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(this, ref _arg0); + } + + internal override MethodCallExpression Rewrite(Expression instance, IList args) { + Debug.Assert(instance == null); + Debug.Assert(args == null || args.Count == 3); + + if (args != null) { + return new MethodCallExpression3(Method, args[0], args[1], args[2]); + } + return new MethodCallExpression3(Method, ReturnObject(_arg0), _arg1, _arg2); + } + } + + internal class MethodCallExpression4 : MethodCallExpression, IArgumentProvider { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider + private readonly Expression _arg1, _arg2, _arg3; // storage for the 2nd - 4th args. + + public MethodCallExpression4(MethodInfo method, Expression arg0, Expression arg1, Expression arg2, Expression arg3) + : base(method) { + _arg0 = arg0; + _arg1 = arg1; + _arg2 = arg2; + _arg3 = arg3; + } + + Expression IArgumentProvider.GetArgument(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + case 2: return _arg2; + case 3: return _arg3; + default: throw new InvalidOperationException(); + } + } + + int IArgumentProvider.ArgumentCount { + get { + return 4; + } + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(this, ref _arg0); + } + + internal override MethodCallExpression Rewrite(Expression instance, IList args) { + Debug.Assert(instance == null); + Debug.Assert(args == null || args.Count == 4); + + if (args != null) { + return new MethodCallExpression4(Method, args[0], args[1], args[2], args[3]); + } + return new MethodCallExpression4(Method, ReturnObject(_arg0), _arg1, _arg2, _arg3); + } + } + + internal class MethodCallExpression5 : MethodCallExpression, IArgumentProvider { + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider + private readonly Expression _arg1, _arg2, _arg3, _arg4; // storage for the 2nd - 5th args. + + public MethodCallExpression5(MethodInfo method, Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) + : base(method) { + _arg0 = arg0; + _arg1 = arg1; + _arg2 = arg2; + _arg3 = arg3; + _arg4 = arg4; + } + + Expression IArgumentProvider.GetArgument(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + case 2: return _arg2; + case 3: return _arg3; + case 4: return _arg4; + default: throw new InvalidOperationException(); + } + } + + int IArgumentProvider.ArgumentCount { + get { + return 5; + } + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(this, ref _arg0); + } + + internal override MethodCallExpression Rewrite(Expression instance, IList args) { + Debug.Assert(instance == null); + Debug.Assert(args == null || args.Count == 5); + + if (args != null) { + return new MethodCallExpression5(Method, args[0], args[1], args[2], args[3], args[4]); + } + + return new MethodCallExpression5(Method, ReturnObject(_arg0), _arg1, _arg2, _arg3, _arg4); + } + } + + internal class InstanceMethodCallExpression2 : MethodCallExpression, IArgumentProvider { + private readonly Expression _instance; + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider + private readonly Expression _arg1; // storage for the 2nd argument + + public InstanceMethodCallExpression2(MethodInfo method, Expression instance, Expression arg0, Expression arg1) + : base(method) { + Debug.Assert(instance != null); + + _instance = instance; + _arg0 = arg0; + _arg1 = arg1; + } + + Expression IArgumentProvider.GetArgument(int index) { + switch(index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + default: throw new InvalidOperationException(); + } + } + + int IArgumentProvider.ArgumentCount { + get { + return 2; + } + } + + internal override Expression GetInstance() { + return _instance; + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(this, ref _arg0); + } + + internal override MethodCallExpression Rewrite(Expression instance, IList args) { + Debug.Assert(instance != null); + Debug.Assert(args.Count == 2); + + if (args != null) { + return new InstanceMethodCallExpression2(Method, instance, args[0], args[1]); + } + return new InstanceMethodCallExpression2(Method, instance, ReturnObject(_arg0), _arg1); + } + } + + internal class InstanceMethodCallExpression3 : MethodCallExpression, IArgumentProvider { + private readonly Expression _instance; + private object _arg0; // storage for the 1st argument or a ROC. See IArgumentProvider + private readonly Expression _arg1, _arg2; // storage for the 2nd - 3rd argument + + public InstanceMethodCallExpression3(MethodInfo method, Expression instance, Expression arg0, Expression arg1, Expression arg2) + : base(method) { + Debug.Assert(instance != null); + + _instance = instance; + _arg0 = arg0; + _arg1 = arg1; + _arg2 = arg2; + } + + Expression IArgumentProvider.GetArgument(int index) { + switch (index) { + case 0: return ReturnObject(_arg0); + case 1: return _arg1; + case 2: return _arg2; + default: throw new InvalidOperationException(); + } + } + + int IArgumentProvider.ArgumentCount { + get { + return 3; + } + } + + internal override Expression GetInstance() { + return _instance; + } + + internal override ReadOnlyCollection GetOrMakeArguments() { + return ReturnReadOnly(this, ref _arg0); + } + + internal override MethodCallExpression Rewrite(Expression instance, IList args) { + Debug.Assert(instance != null); + Debug.Assert(args.Count == 3); + + if (args != null) { + return new InstanceMethodCallExpression3(Method, instance, args[0], args[1], args[2]); + } + return new InstanceMethodCallExpression3(Method, instance, ReturnObject(_arg0), _arg1, _arg2); + } + } + + #endregion + + /// + /// Factory methods. + /// + public partial class Expression { + + #region Call + + public static MethodCallExpression Call(MethodInfo method, Expression arg0) { + ContractUtils.RequiresNotNull(method, "method"); + ContractUtils.RequiresNotNull(arg0, "arg0"); + + ParameterInfo[] pis = ValidateMethodAndGetParameters(null, method); + + ValidateArgumentCount(method, ExpressionType.Call, 1, pis); + + arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); + + return new MethodCallExpression1(method, arg0); + } + + public static MethodCallExpression Call(MethodInfo method, Expression arg0, Expression arg1) { + ContractUtils.RequiresNotNull(method, "method"); + ContractUtils.RequiresNotNull(arg0, "arg0"); + ContractUtils.RequiresNotNull(arg1, "arg1"); + + ParameterInfo[] pis = ValidateMethodAndGetParameters(null, method); + + ValidateArgumentCount(method, ExpressionType.Call, 2, pis); + + arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); + arg1 = ValidateOneArgument(method, ExpressionType.Call, arg1, pis[1]); + + return new MethodCallExpression2(method, arg0, arg1); + } + + public static MethodCallExpression Call(MethodInfo method, Expression arg0, Expression arg1, Expression arg2) { + ContractUtils.RequiresNotNull(method, "method"); + ContractUtils.RequiresNotNull(arg0, "arg0"); + ContractUtils.RequiresNotNull(arg1, "arg1"); + ContractUtils.RequiresNotNull(arg2, "arg2"); + + ParameterInfo[] pis = ValidateMethodAndGetParameters(null, method); + + ValidateArgumentCount(method, ExpressionType.Call, 3, pis); + + arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); + arg1 = ValidateOneArgument(method, ExpressionType.Call, arg1, pis[1]); + arg2 = ValidateOneArgument(method, ExpressionType.Call, arg2, pis[2]); + + return new MethodCallExpression3(method, arg0, arg1, arg2); + } + + public static MethodCallExpression Call(MethodInfo method, Expression arg0, Expression arg1, Expression arg2, Expression arg3) { + ContractUtils.RequiresNotNull(method, "method"); + ContractUtils.RequiresNotNull(arg0, "arg0"); + ContractUtils.RequiresNotNull(arg1, "arg1"); + ContractUtils.RequiresNotNull(arg2, "arg2"); + ContractUtils.RequiresNotNull(arg3, "arg3"); + + ParameterInfo[] pis = ValidateMethodAndGetParameters(null, method); + + ValidateArgumentCount(method, ExpressionType.Call, 4, pis); + + arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); + arg1 = ValidateOneArgument(method, ExpressionType.Call, arg1, pis[1]); + arg2 = ValidateOneArgument(method, ExpressionType.Call, arg2, pis[2]); + arg3 = ValidateOneArgument(method, ExpressionType.Call, arg3, pis[3]); + + return new MethodCallExpression4(method, arg0, arg1, arg2, arg3); + } + + public static MethodCallExpression Call(MethodInfo method, Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) { + ContractUtils.RequiresNotNull(method, "method"); + ContractUtils.RequiresNotNull(arg0, "arg0"); + ContractUtils.RequiresNotNull(arg1, "arg1"); + ContractUtils.RequiresNotNull(arg2, "arg2"); + ContractUtils.RequiresNotNull(arg3, "arg3"); + ContractUtils.RequiresNotNull(arg4, "arg4"); + + ParameterInfo[] pis = ValidateMethodAndGetParameters(null, method); + + ValidateArgumentCount(method, ExpressionType.Call, 5, pis); + + arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); + arg1 = ValidateOneArgument(method, ExpressionType.Call, arg1, pis[1]); + arg2 = ValidateOneArgument(method, ExpressionType.Call, arg2, pis[2]); + arg3 = ValidateOneArgument(method, ExpressionType.Call, arg3, pis[3]); + arg4 = ValidateOneArgument(method, ExpressionType.Call, arg4, pis[4]); + + return new MethodCallExpression5(method, arg0, arg1, arg2, arg3, arg4); + } + + //CONFORMING + public static MethodCallExpression Call(MethodInfo method, params Expression[] arguments) { + return Call(null, method, arguments); + } + + public static MethodCallExpression Call(MethodInfo method, IEnumerable arguments) { + return Call(null, method, arguments); + } + + //CONFORMING + public static MethodCallExpression Call(Expression instance, MethodInfo method) { + return Call(instance, method, EmptyReadOnlyCollection.Instance); + } + + //CONFORMING + public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments) { + return Call(instance, method, (IEnumerable)arguments); + } + + public static MethodCallExpression Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1) { + ContractUtils.RequiresNotNull(method, "method"); + ContractUtils.RequiresNotNull(arg0, "arg0"); + ContractUtils.RequiresNotNull(arg1, "arg1"); + + ParameterInfo[] pis = ValidateMethodAndGetParameters(instance, method); + + ValidateArgumentCount(method, ExpressionType.Call, 2, pis); + + arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); + arg1 = ValidateOneArgument(method, ExpressionType.Call, arg1, pis[1]); + + if (instance != null) { + return new InstanceMethodCallExpression2(method, instance, arg0, arg1); + } + + return new MethodCallExpression2(method, arg0, arg1); + } + + public static MethodCallExpression Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1, Expression arg2) { + ContractUtils.RequiresNotNull(method, "method"); + ContractUtils.RequiresNotNull(arg0, "arg0"); + ContractUtils.RequiresNotNull(arg1, "arg1"); + ContractUtils.RequiresNotNull(arg2, "arg2"); + + ParameterInfo[] pis = ValidateMethodAndGetParameters(instance, method); + + ValidateArgumentCount(method, ExpressionType.Call, 3, pis); + + arg0 = ValidateOneArgument(method, ExpressionType.Call, arg0, pis[0]); + arg1 = ValidateOneArgument(method, ExpressionType.Call, arg1, pis[1]); + arg2 = ValidateOneArgument(method, ExpressionType.Call, arg2, pis[2]); + + if (instance != null) { + return new InstanceMethodCallExpression3(method, instance, arg0, arg1, arg2); + } + return new MethodCallExpression3(method, arg0, arg1, arg2); + } + + //CONFORMING + public static MethodCallExpression Call(Expression instance, string methodName, Type[] typeArguments, params Expression[] arguments) { + ContractUtils.RequiresNotNull(instance, "instance"); + ContractUtils.RequiresNotNull(methodName, "methodName"); + if (arguments == null) arguments = new Expression[] { }; + + BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; + return Expression.Call(instance, FindMethod(instance.Type, methodName, typeArguments, arguments, flags), arguments); + } + + //CONFORMING + public static MethodCallExpression Call(Type type, string methodName, Type[] typeArguments, params Expression[] arguments) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(methodName, "methodName"); + + if (arguments == null) arguments = new Expression[] { }; + BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; + return Expression.Call(null, FindMethod(type, methodName, typeArguments, arguments, flags), arguments); + } + + //CONFORMING + public static MethodCallExpression Call(Expression instance, MethodInfo method, IEnumerable arguments) { + ContractUtils.RequiresNotNull(method, "method"); + + ReadOnlyCollection argList = arguments.ToReadOnly(); + + ValidateMethodInfo(method); + ValidateStaticOrInstanceMethod(instance, method); + ValidateArgumentTypes(method, ExpressionType.Call, ref argList); + + if (instance == null) { + return new MethodCallExpressionN(method, argList); + } else { + return new InstanceMethodCallExpressionN(method, instance, argList); + } + } + + private static ParameterInfo[] ValidateMethodAndGetParameters(Expression instance, MethodInfo method) { + ValidateMethodInfo(method); + ValidateStaticOrInstanceMethod(instance, method); + + return GetParametersForValidation(method, ExpressionType.Call); + } + + private static void ValidateStaticOrInstanceMethod(Expression instance, MethodInfo method) { + if (method.IsStatic) { + ContractUtils.Requires(instance == null, "instance", Strings.OnlyStaticMethodsHaveNullInstance); + } else { + ContractUtils.Requires(instance != null, "method", Strings.OnlyStaticMethodsHaveNullInstance); + RequiresCanRead(instance, "instance"); + ValidateCallInstanceType(instance.Type, method); + } + } + + //CONFORMING + private static void ValidateCallInstanceType(Type instanceType, MethodInfo method) { + if (!TypeUtils.IsValidInstanceType(method, instanceType)) { + throw Error.MethodNotDefinedForType(method, instanceType); + } + } + + //CONFORMING + private static void ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ref ReadOnlyCollection arguments) { + Debug.Assert(nodeKind == ExpressionType.Invoke || nodeKind == ExpressionType.Call || nodeKind == ExpressionType.Dynamic || nodeKind == ExpressionType.New); + + ParameterInfo[] pis = GetParametersForValidation(method, nodeKind); + + ValidateArgumentCount(method, nodeKind, arguments.Count, pis); + + Expression[] newArgs = null; + for (int i = 0, n = pis.Length; i < n; i++) { + Expression arg = arguments[i]; + ParameterInfo pi = pis[i]; + arg = ValidateOneArgument(method, nodeKind, arg, pi); + + if (newArgs == null && arg != arguments[i]) { + newArgs = new Expression[arguments.Count]; + for (int j = 0; j < i; j++) { + newArgs[j] = arguments[j]; + } + } + if (newArgs != null) { + newArgs[i] = arg; + } + } + if (newArgs != null) { + arguments = new ReadOnlyCollection(newArgs); + } + } + + private static ParameterInfo[] GetParametersForValidation(MethodBase method, ExpressionType nodeKind) { + ParameterInfo[] pis = method.GetParametersCached(); + + if (nodeKind == ExpressionType.Dynamic) { + pis = pis.RemoveFirst(); // ignore CallSite argument + } + return pis; + } + + private static void ValidateArgumentCount(MethodBase method, ExpressionType nodeKind, int count, ParameterInfo[] pis) { + if (pis.Length != count) { + // TODO: this is for LinqV1 compat, can we just have one exception? + switch (nodeKind) { + case ExpressionType.New: + throw Error.IncorrectNumberOfConstructorArguments(); + case ExpressionType.Invoke: + throw Error.IncorrectNumberOfLambdaArguments(); + case ExpressionType.Dynamic: + case ExpressionType.Call: + throw Error.IncorrectNumberOfMethodCallArguments(method); + default: + throw Assert.Unreachable; + } + } + } + + private static Expression ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arg, ParameterInfo pi) { + RequiresCanRead(arg, "arguments"); + Type pType = pi.ParameterType; + if (pType.IsByRef) { + pType = pType.GetElementType(); + } + TypeUtils.ValidateType(pType); + if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) { + if (TypeUtils.IsSameOrSubclass(typeof(Expression), pType) && pType.IsAssignableFrom(arg.GetType())) { + arg = Expression.Quote(arg); + } else { + // TODO: this is for LinqV1 compat, can we just have one exception? + switch (nodeKind) { + case ExpressionType.New: + throw Error.ExpressionTypeDoesNotMatchConstructorParameter(arg.Type, pType); + case ExpressionType.Invoke: + throw Error.ExpressionTypeDoesNotMatchParameter(arg.Type, pType); + case ExpressionType.Dynamic: + case ExpressionType.Call: + throw Error.ExpressionTypeDoesNotMatchMethodParameter(arg.Type, pType, method); + default: + throw Assert.Unreachable; + } + } + } + return arg; + } + + //CONFORMING + private static MethodInfo FindMethod(Type type, string methodName, Type[] typeArgs, Expression[] args, BindingFlags flags) { + MemberInfo[] members = type.FindMembers(MemberTypes.Method, flags, Type.FilterNameIgnoreCase, methodName); + if (members == null || members.Length == 0) + throw Error.MethodDoesNotExistOnType(methodName, type); + + MethodInfo method; + + var methodInfos = members.Map(t => (MethodInfo)t); + int count = FindBestMethod(methodInfos, typeArgs, args, out method); + + if (count == 0) + throw Error.MethodWithArgsDoesNotExistOnType(methodName, type); + if (count > 1) + throw Error.MethodWithMoreThanOneMatch(methodName, type); + return method; + } + + //CONFORMING + private static int FindBestMethod(IEnumerable methods, Type[] typeArgs, Expression[] args, out MethodInfo method) { + int count = 0; + method = null; + foreach (MethodInfo mi in methods) { + MethodInfo moo = ApplyTypeArgs(mi, typeArgs); + if (moo != null && IsCompatible(moo, args)) { + // favor public over non-public methods + if (method == null || (!method.IsPublic && moo.IsPublic)) { + method = moo; + count = 1; + } + // only count it as additional method if they both public or both non-public + else if (method.IsPublic == moo.IsPublic) { + count++; + } + } + } + return count; + } + + //CONFORMING + private static bool IsCompatible(MethodBase m, Expression[] args) { + ParameterInfo[] parms = m.GetParametersCached(); + if (parms.Length != args.Length) + return false; + for (int i = 0; i < args.Length; i++) { + Expression arg = args[i]; + ContractUtils.RequiresNotNull(arg, "argument"); + Type argType = arg.Type; + Type pType = parms[i].ParameterType; + if (pType.IsByRef) { + pType = pType.GetElementType(); + } + if (!TypeUtils.AreReferenceAssignable(pType, argType) && + !(TypeUtils.IsSameOrSubclass(typeof(Expression), pType) && pType.IsAssignableFrom(arg.GetType()))) { + return false; + } + } + return true; + } + + //CONFORMING + private static MethodInfo ApplyTypeArgs(MethodInfo m, Type[] typeArgs) { + if (typeArgs == null || typeArgs.Length == 0) { + if (!m.IsGenericMethodDefinition) + return m; + } else { + if (m.IsGenericMethodDefinition && m.GetGenericArguments().Length == typeArgs.Length) + return m.MakeGenericMethod(typeArgs); + } + return null; + } + + + #endregion + + #region ArrayIndex + + //CONFORMING + public static MethodCallExpression ArrayIndex(Expression array, params Expression[] indexes) { + return ArrayIndex(array, (IEnumerable)indexes); + } + + //CONFORMING + public static MethodCallExpression ArrayIndex(Expression array, IEnumerable indexes) { + RequiresCanRead(array, "array"); + ContractUtils.RequiresNotNull(indexes, "indexes"); + + Type arrayType = array.Type; + if (!arrayType.IsArray) + throw Error.ArgumentMustBeArray(); + + ReadOnlyCollection indexList = indexes.ToReadOnly(); + if (arrayType.GetArrayRank() != indexList.Count) + throw Error.IncorrectNumberOfIndexes(); + + foreach (Expression e in indexList) { + RequiresCanRead(e, "indexes"); + if (e.Type != typeof(int)) { + throw Error.ArgumentMustBeArrayIndexType(); + } + } + + MethodInfo mi = array.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance); + return Call(array, mi, indexList); + } + + #endregion + + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/NewArrayExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/NewArrayExpression.cs new file mode 100644 index 0000000000..846f32ba2d --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/NewArrayExpression.cs @@ -0,0 +1,179 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic.Utils; +using System.Text; + +namespace System.Linq.Expressions { + //CONFORMING + public class NewArrayExpression : Expression { + private readonly ReadOnlyCollection _expressions; + private readonly Type _type; + + internal NewArrayExpression(Type type, ReadOnlyCollection expressions) { + _expressions = expressions; + _type = type; + } + + internal static NewArrayExpression Make(ExpressionType nodeType, Type type, ReadOnlyCollection expressions) { + if (nodeType == ExpressionType.NewArrayInit) { + return new NewArrayInitExpression(type, expressions); + } else { + return new NewArrayBoundsExpression(type, expressions); + } + } + + protected override Type GetExpressionType() { + return _type; + } + + public ReadOnlyCollection Expressions { + get { return _expressions; } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitNewArray(this); + } + } + + internal sealed class NewArrayInitExpression : NewArrayExpression { + internal NewArrayInitExpression(Type type, ReadOnlyCollection expressions) + : base(type, expressions) { + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.NewArrayInit; + } + } + + internal sealed class NewArrayBoundsExpression : NewArrayExpression { + internal NewArrayBoundsExpression(Type type, ReadOnlyCollection expressions) + : base(type, expressions) { + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.NewArrayBounds; + } + } + + /// + /// Factory methods. + /// + public partial class Expression { + + #region NewArrayInit + + //CONFORMING + /// + /// Creates a new array expression of the specified type from the provided initializers. + /// + /// A Type that represents the element type of the array. + /// The expressions used to create the array elements. + public static NewArrayExpression NewArrayInit(Type type, params Expression[] initializers) { + return NewArrayInit(type, (IEnumerable)initializers); + } + + /// + /// Creates a new array expression of the specified type from the provided initializers. + /// + /// A Type that represents the element type of the array. + /// The expressions used to create the array elements. + public static NewArrayExpression NewArrayInit(Type type, IEnumerable initializers) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + if (type.Equals(typeof(void))) { + throw Error.ArgumentCannotBeOfTypeVoid(); + } + + ReadOnlyCollection initializerList = initializers.ToReadOnly(); + + Expression[] newList = null; + for (int i = 0, n = initializerList.Count; i < n; i++) { + Expression expr = initializerList[i]; + RequiresCanRead(expr, "initializers"); + + if (!TypeUtils.AreReferenceAssignable(type, expr.Type)) { + if (TypeUtils.IsSameOrSubclass(typeof(Expression), type) && type.IsAssignableFrom(expr.GetType())) { + expr = Expression.Quote(expr); + } else { + throw Error.ExpressionTypeCannotInitializeArrayType(expr.Type, type); + } + if (newList == null) { + newList = new Expression[initializerList.Count]; + for (int j = 0; j < i; j++) { + newList[j] = initializerList[j]; + } + } + } + if (newList != null) { + newList[i] = expr; + } + } + if (newList != null) { + initializerList = new ReadOnlyCollection(newList); + } + + return NewArrayExpression.Make(ExpressionType.NewArrayInit, type.MakeArrayType(), initializerList); + } + + #endregion + + #region NewArrayBounds + + //CONFORMING + public static NewArrayExpression NewArrayBounds(Type type, params Expression[] bounds) { + return NewArrayBounds(type, (IEnumerable)bounds); + } + + //CONFORMING + public static NewArrayExpression NewArrayBounds(Type type, IEnumerable bounds) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(bounds, "bounds"); + + if (type.Equals(typeof(void))) { + throw Error.ArgumentCannotBeOfTypeVoid(); + } + + ReadOnlyCollection boundsList = bounds.ToReadOnly(); + + int dimensions = boundsList.Count; + ContractUtils.Requires(dimensions > 0, "bounds", Strings.BoundsCannotBeLessThanOne); + + for (int i = 0; i < dimensions; i++) { + Expression expr = boundsList[i]; + RequiresCanRead(expr, "bounds"); + if (!TypeUtils.IsInteger(expr.Type)) { + throw Error.ArgumentMustBeInteger(); + } + } + + Type arrayType; + if (dimensions == 1) { + //To get a vector, need call Type.MakeArrayType(). + //Type.MakeArrayType(1) gives a non-vector array, which will cause type check error. + arrayType = type.MakeArrayType(); + } else { + arrayType = type.MakeArrayType(dimensions); + } + + return NewArrayExpression.Make(ExpressionType.NewArrayBounds, arrayType, bounds.ToReadOnly()); + } + + #endregion + + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/NewExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/NewExpression.cs new file mode 100644 index 0000000000..466861bfa6 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/NewExpression.cs @@ -0,0 +1,252 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Reflection; +using System.Dynamic.Binders; +using System.Dynamic.Utils; +using System.Text; + +namespace System.Linq.Expressions { + //CONFORMING + public class NewExpression : Expression, IArgumentProvider { + private readonly ConstructorInfo _constructor; + private IList _arguments; + private readonly ReadOnlyCollection _members; + + internal NewExpression(ConstructorInfo constructor, IList arguments, ReadOnlyCollection members) { + _constructor = constructor; + _arguments = arguments; + _members = members; + } + + protected override Type GetExpressionType() { + return _constructor.DeclaringType; + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.New; + } + + public ConstructorInfo Constructor { + get { return _constructor; } + } + + public ReadOnlyCollection Arguments { + get { return ReturnReadOnly(ref _arguments); } + } + + Expression IArgumentProvider.GetArgument(int index) { + return _arguments[index]; + } + + int IArgumentProvider.ArgumentCount { + get { + return _arguments.Count; + } + } + + public ReadOnlyCollection Members { + get { return _members; } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitNew(this); + } + } + + internal class NewValueTypeExpression : NewExpression { + private readonly Type _valueType; + + internal NewValueTypeExpression(Type type, ReadOnlyCollection arguments, ReadOnlyCollection members) + : base(null, arguments, members) { + _valueType = type; + } + + protected override Type GetExpressionType() { + return _valueType; + } + } + + /// + /// Factory methods. + /// + public partial class Expression { + //CONFORMING + public static NewExpression New(ConstructorInfo constructor) { + return New(constructor, (IEnumerable)null); + } + + //CONFORMING + public static NewExpression New(ConstructorInfo constructor, params Expression[] arguments) { + return New(constructor, (IEnumerable)arguments); + } + + //CONFORMING + public static NewExpression New(ConstructorInfo constructor, IEnumerable arguments) { + ContractUtils.RequiresNotNull(constructor, "constructor"); + ContractUtils.RequiresNotNull(constructor.DeclaringType, "constructor.DeclaringType"); + TypeUtils.ValidateType(constructor.DeclaringType); + ReadOnlyCollection argList = arguments.ToReadOnly(); + ValidateArgumentTypes(constructor, ExpressionType.New, ref argList); + + return new NewExpression(constructor, argList, null); + } + + //CONFORMING + public static NewExpression New(ConstructorInfo constructor, IEnumerable arguments, IEnumerable members) { + ContractUtils.RequiresNotNull(constructor, "constructor"); + ReadOnlyCollection memberList = members.ToReadOnly(); + ReadOnlyCollection argList = arguments.ToReadOnly(); + ValidateNewArgs(constructor, ref argList, ref memberList); + return new NewExpression(constructor, argList, memberList); + } + + //CONFORMING + public static NewExpression New(ConstructorInfo constructor, IEnumerable arguments, params MemberInfo[] members) { + return New(constructor, arguments, members.ToReadOnly()); + } + + //CONFORMING + public static NewExpression New(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + if (type == typeof(void)) { + throw Error.ArgumentCannotBeOfTypeVoid(); + } + ConstructorInfo ci = null; + if (!type.IsValueType) { + ci = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, System.Type.EmptyTypes, null); + if (ci == null) { + throw Error.TypeMissingDefaultConstructor(type); + } + return New(ci); + } + return new NewValueTypeExpression(type, EmptyReadOnlyCollection.Instance, null); + } + + + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private static void ValidateNewArgs(ConstructorInfo constructor, ref ReadOnlyCollection arguments, ref ReadOnlyCollection members) { + ParameterInfo[] pis; + if ((pis = constructor.GetParametersCached()).Length > 0) { + if (arguments.Count != pis.Length) { + throw Error.IncorrectNumberOfConstructorArguments(); + } + if (arguments.Count != members.Count) { + throw Error.IncorrectNumberOfArgumentsForMembers(); + } + Expression[] newArguments = null; + MemberInfo[] newMembers = null; + for (int i = 0, n = arguments.Count; i < n; i++) { + Expression arg = arguments[i]; + RequiresCanRead(arg, "argument"); + MemberInfo member = members[i]; + ContractUtils.RequiresNotNull(member, "member"); + if (member.DeclaringType != constructor.DeclaringType) { + throw Error.ArgumentMemberNotDeclOnType(member.Name, constructor.DeclaringType.Name); + } + Type memberType; + ValidateAnonymousTypeMember(ref member, out memberType); + if (!TypeUtils.AreReferenceAssignable(memberType, arg.Type)) { + if (TypeUtils.IsSameOrSubclass(typeof(Expression), memberType) && memberType.IsAssignableFrom(arg.GetType())) { + arg = Expression.Quote(arg); + } else { + throw Error.ArgumentTypeDoesNotMatchMember(arg.Type, memberType); + } + } + ParameterInfo pi = pis[i]; + Type pType = pi.ParameterType; + if (pType.IsByRef) { + pType = pType.GetElementType(); + } + if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) { + if (TypeUtils.IsSameOrSubclass(typeof(Expression), pType) && pType.IsAssignableFrom(arg.Type)) { + arg = Expression.Quote(arg); + } else { + throw Error.ExpressionTypeDoesNotMatchConstructorParameter(arg.Type, pType); + } + } + if (newArguments == null && arg != arguments[i]) { + newArguments = new Expression[arguments.Count]; + for (int j = 0; j < i; j++) { + newArguments[j] = arguments[j]; + } + } + if (newArguments != null) { + newArguments[i] = arg; + } + + if (newMembers == null && member != members[i]) { + newMembers = new MemberInfo[members.Count]; + for (int j = 0; j < i; j++) { + newMembers[j] = members[j]; + } + } + if (newMembers != null) { + newMembers[i] = member; + } + } + if (newArguments != null) { + arguments = new ReadOnlyCollection(newArguments); + } + if (newMembers != null) { + members = new ReadOnlyCollection(newMembers); + } + } else if (arguments != null && arguments.Count > 0) { + throw Error.IncorrectNumberOfConstructorArguments(); + } else if (members != null && members.Count > 0) { + throw Error.IncorrectNumberOfMembersForGivenConstructor(); + } + } + + //CONFORMING + private static void ValidateAnonymousTypeMember(ref MemberInfo member, out Type memberType) { + switch (member.MemberType) { + case MemberTypes.Field: + FieldInfo field = member as FieldInfo; + if (field.IsStatic) { + throw Error.ArgumentMustBeInstanceMember(); + } + memberType = field.FieldType; + break; + case MemberTypes.Property: + PropertyInfo pi = member as PropertyInfo; + if (!pi.CanRead) { + throw Error.PropertyDoesNotHaveGetter(pi); + } + if (pi.GetGetMethod().IsStatic) { + throw Error.ArgumentMustBeInstanceMember(); + } + memberType = pi.PropertyType; + break; + case MemberTypes.Method: + MethodInfo method = member as MethodInfo; + if (method.IsStatic) { + throw Error.ArgumentMustBeInstanceMember(); + } + + PropertyInfo prop = GetProperty(method); + member = prop; + memberType = prop.PropertyType; + break; + default: + throw Error.ArgumentMustBeFieldInfoOrPropertInfoOrMethod(); + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/ParameterExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/ParameterExpression.cs new file mode 100644 index 0000000000..71dfe6781b --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/ParameterExpression.cs @@ -0,0 +1,193 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; +using System.Text; +using System.Diagnostics; +using System.Collections.ObjectModel; +using System.Collections.Generic; + +namespace System.Linq.Expressions { + //CONFORMING + /// + /// Base class for specialized parameter expressions. This version only holds onto the + /// name which all subclasses need. Specialized subclasses provide the type and by ref + /// flags. + /// + public class ParameterExpression : Expression { + private readonly string _name; + + internal ParameterExpression(string name) { + _name = name; + } + + internal static ParameterExpression Make(Type type, string name, bool isByRef) { + if (isByRef) { + return new ByRefParameterExpression(type, name); + } else { + if (!type.IsEnum) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Boolean: return new PrimitiveParameterExpression(name); + case TypeCode.Byte: return new PrimitiveParameterExpression(name); + case TypeCode.Char: return new PrimitiveParameterExpression(name); + case TypeCode.DateTime: return new PrimitiveParameterExpression(name); + case TypeCode.DBNull: return new PrimitiveParameterExpression(name); + case TypeCode.Decimal: return new PrimitiveParameterExpression(name); + case TypeCode.Double: return new PrimitiveParameterExpression(name); + case TypeCode.Int16: return new PrimitiveParameterExpression(name); + case TypeCode.Int32: return new PrimitiveParameterExpression(name); + case TypeCode.Int64: return new PrimitiveParameterExpression(name); + case TypeCode.Object: + // common reference types which we optimize go here. Of course object is in + // the list, the others are driven by profiling of various workloads. This list + // should be kept short. + if (type == typeof(object)) { + return new ParameterExpression(name); + } else if (type == typeof(Exception)) { + return new PrimitiveParameterExpression(name); + } else if (type == typeof(object[])) { + return new PrimitiveParameterExpression(name); + } + break; + case TypeCode.SByte: return new PrimitiveParameterExpression(name); + case TypeCode.Single: return new PrimitiveParameterExpression(name); + case TypeCode.String: return new PrimitiveParameterExpression(name); + case TypeCode.UInt16: return new PrimitiveParameterExpression(name); + case TypeCode.UInt32: return new PrimitiveParameterExpression(name); + case TypeCode.UInt64: return new PrimitiveParameterExpression(name); + } + } + } + + return new TypedParameterExpression(type, name); + } + + protected override Type GetExpressionType() { + return typeof(object); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Parameter; + } + + public string Name { + get { return _name; } + } + + public bool IsByRef { + get { + return GetIsByRef(); + } + } + + internal virtual bool GetIsByRef() { + return false; + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitParameter(this); + } + } + + /// + /// Specialized subclass to avoid holding onto the byref flag in a + /// parameter expression. This version always holds onto the expression + /// type explicitly and therefore derives from TypedParameterExpression. + /// + internal sealed class ByRefParameterExpression : TypedParameterExpression { + internal ByRefParameterExpression(Type type, string name) + : base(type, name) { + } + + internal override bool GetIsByRef() { + return true; + } + } + + /// + /// Specialized subclass which holds onto the type of the expression for + /// uncommon types. + /// + internal class TypedParameterExpression : ParameterExpression { + private readonly Type _paramType; + + internal TypedParameterExpression(Type type, string name) + : base(name) { + _paramType = type; + } + + protected override Type GetExpressionType() { + return _paramType; + } + } + + /// + /// Generic type to avoid needing explicit storage for primitive data types + /// which are commonly used. + /// + internal sealed class PrimitiveParameterExpression : ParameterExpression { + internal PrimitiveParameterExpression(string name) + : base(name) { + } + + protected override Type GetExpressionType() { + return typeof(T); + } + } + + public partial class Expression { + //CONFORMING + public static ParameterExpression Parameter(Type type, string name) { + ContractUtils.RequiresNotNull(type, "type"); + + if (type == typeof(void)) { + throw Error.ArgumentCannotBeOfTypeVoid(); + } + + bool byref = type.IsByRef; + if (byref) { + type = type.GetElementType(); + } + + return ParameterExpression.Make(type, name, byref); + } + + public static ParameterExpression Variable(Type type, string name) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.Requires(type != typeof(void), "type", Strings.ArgumentCannotBeOfTypeVoid); + ContractUtils.Requires(!type.IsByRef, "type", Strings.TypeMustNotBeByRef); + return ParameterExpression.Make(type, name, false); + } + + //Variables must not be ByRef. + internal static void RequireVariableNotByRef(ParameterExpression v, string varName) { + Assert.NotNull(varName); + if (v != null) { + ContractUtils.Requires(!v.IsByRef, varName, Strings.VariableMustNotBeByRef); + } + } + + internal static void RequireVariablesNotByRef(ReadOnlyCollection vs, string collectionName) { + Assert.NotNull(vs); + Assert.NotNull(collectionName); + for (int i = 0; i < vs.Count; i++) { + if (vs[i] != null && vs[i].IsByRef) { + // TODO: Just throw, don't call ContractUtils + ContractUtils.Requires(!vs[i].IsByRef, string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0}[{1}]", collectionName, i), Strings.VariableMustNotBeByRef); + } + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/RuntimeVariablesExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/RuntimeVariablesExpression.cs new file mode 100644 index 0000000000..38da440ca6 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/RuntimeVariablesExpression.cs @@ -0,0 +1,73 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + /// + /// An expression that provides runtime read/write access to variables. + /// Needed to implement "eval" in dynamic languages. + /// Evaluates to an instance of ILocalVariables at run time. + /// + public sealed class RuntimeVariablesExpression : Expression { + private readonly ReadOnlyCollection _variables; + + internal RuntimeVariablesExpression(ReadOnlyCollection variables) { + _variables = variables; + } + + protected override Type GetExpressionType() { + return typeof(IList); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.RuntimeVariables; + } + + /// + /// The variables or parameters to provide access to + /// + public ReadOnlyCollection Variables { + get { return _variables; } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitRuntimeVariables(this); + } + } + + public partial class Expression { + + public static RuntimeVariablesExpression RuntimeVariables(params ParameterExpression[] variables) { + return RuntimeVariables((IEnumerable)variables); + } + public static RuntimeVariablesExpression RuntimeVariables(IEnumerable variables) { + ContractUtils.RequiresNotNull(variables, "variables"); + + var vars = variables.ToReadOnly(); + for (int i = 0; i < vars.Count; i++) { + Expression v = vars[i]; + if (v == null) { + throw new ArgumentNullException("variables[" + i + "]"); + } + } + + return new RuntimeVariablesExpression(vars); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/SwitchCase.cs b/ndp/fx/src/core/microsoft/scripting/Ast/SwitchCase.cs new file mode 100644 index 0000000000..af03811845 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/SwitchCase.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + public sealed class SwitchCase { + private readonly bool _default; + private readonly int _value; + private readonly Expression _body; + + internal SwitchCase(bool @default, int value, Expression body) { + _default = @default; + _value = value; + _body = body; + } + + public bool IsDefault { + get { return _default; } + } + + public int Value { + get { return _value; } + } + + public Expression Body { + get { return _body; } + } + } + + public partial class Expression { + public static SwitchCase DefaultCase(Expression body) { + RequiresCanRead(body, "body"); + return new SwitchCase(true, 0, body); + } + + public static SwitchCase SwitchCase(int value, Expression body) { + RequiresCanRead(body, "body"); + return new SwitchCase(false, value, body); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/SwitchExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/SwitchExpression.cs new file mode 100644 index 0000000000..7d06c15c2d --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/SwitchExpression.cs @@ -0,0 +1,178 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Diagnostics; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic.Utils; +using System.Runtime.InteropServices; + +namespace System.Linq.Expressions { + public sealed class SwitchExpression : Expression { + private readonly Expression _testValue; + private readonly ReadOnlyCollection _cases; + private readonly LabelTarget _label; + + internal SwitchExpression(Expression testValue, LabelTarget label, ReadOnlyCollection cases) { + Assert.NotNullItems(cases); + + _label = label; + _testValue = testValue; + _cases = cases; + } + + protected override Type GetExpressionType() { + return typeof(void); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Switch; + } + + public Expression Test { + get { return _testValue; } + } + + public ReadOnlyCollection SwitchCases { + get { return _cases; } + } + + public LabelTarget BreakLabel { + get { return _label; } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitSwitch(this); + } + } + + /// + /// Factory methods. + /// + public partial class Expression { + public static SwitchExpression Switch(Expression value, params SwitchCase[] cases) { + return Switch(value, null, (IEnumerable)cases); + } + public static SwitchExpression Switch(Expression value, LabelTarget label, params SwitchCase[] cases) { + return Switch(value, label, (IEnumerable)cases); + } + public static SwitchExpression Switch(Expression value, LabelTarget label, IEnumerable cases) { + RequiresCanRead(value, "value"); + ContractUtils.Requires(value.Type == typeof(int), "value", Strings.ValueMustBeInt); + ContractUtils.RequiresNotNull(cases, "cases"); + var caseList = cases.ToReadOnly(); + ContractUtils.RequiresNotEmpty(caseList, "cases"); + ContractUtils.RequiresNotNullItems(caseList, "cases"); + // TODO: does it make sense for switch to have non-void type? + ContractUtils.Requires(label == null || label.Type == typeof(void), "label", Strings.LabelTypeMustBeVoid); + + bool @default = false; + int max = Int32.MinValue; + int min = Int32.MaxValue; + foreach (SwitchCase sc in caseList) { + if (sc.IsDefault) { + ContractUtils.Requires(@default == false, "cases", Strings.OnlyDefaultIsAllowed); + @default = true; + } else { + int val = sc.Value; + if (val > max) max = val; + if (val < min) min = val; + } + } + + ContractUtils.Requires(UniqueCaseValues(caseList, min, max), "cases", Strings.CaseValuesMustBeUnique); + + return new SwitchExpression(value, label, caseList); + } + + // Below his threshold we'll use brute force N^2 algorithm + private const int N2Threshold = 10; + + // If values are in a small range, we'll use bit array + private const long BitArrayThreshold = 1024; + + private static bool UniqueCaseValues(ReadOnlyCollection cases, int min, int max) { + int length = cases.Count; + + // If we have small number of cases, use straightforward N2 algorithm + // which doesn't allocate memory + if (length < N2Threshold) { + for (int i = 0; i < length; i++) { + SwitchCase sci = cases[i]; + if (sci.IsDefault) { + continue; + } + for (int j = i + 1; j < length; j++) { + SwitchCase scj = cases[j]; + if (scj.IsDefault) { + continue; + } + + if (sci.Value == scj.Value) { + // Duplicate value found + return false; + } + } + } + + return true; + } + + // We have at least N2Threshold items so the min and max values + // are set to actual values and not the Int32.MaxValue and Int32.MaxValue + Debug.Assert(min <= max); + long delta = (long)max - (long)min; + if (delta < BitArrayThreshold) { + BitArray ba = new BitArray((int)delta + 1, false); + + for (int i = 0; i < length; i++) { + SwitchCase sc = cases[i]; + if (sc.IsDefault) { + continue; + } + // normalize to 0 .. (max - min) + int val = sc.Value - min; + if (ba.Get(val)) { + // Duplicate value found + return false; + } + ba.Set(val, true); + } + + return true; + } + + // Too many values that are too spread around. Use dictionary + // Using Dictionary as it is used elsewhere to + // minimize the impact of generic instantiation + Dictionary dict = new Dictionary(length); + for (int i = 0; i < length; i++) { + SwitchCase sc = cases[i]; + if (sc.IsDefault) { + continue; + } + int val = sc.Value; + if (dict.ContainsKey(val)) { + // Duplicate value found + return false; + } + dict[val] = null; + } + + return true; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/SymbolDocumentInfo.cs b/ndp/fx/src/core/microsoft/scripting/Ast/SymbolDocumentInfo.cs new file mode 100644 index 0000000000..deafad1910 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/SymbolDocumentInfo.cs @@ -0,0 +1,111 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + /// + /// Stores information needed to emit debugging symbol information for a + /// source file, in particular the file name and unique language identifier + /// + public class SymbolDocumentInfo { + private readonly string _fileName; + + internal SymbolDocumentInfo(string fileName) { + ContractUtils.RequiresNotNull(fileName, "fileName"); + _fileName = fileName; + } + + /// + /// The source file name + /// + public string FileName { + get { return _fileName; } + } + + /// + /// Returns the language's unique identifier, if any + /// + public virtual Guid Language { + get { return Guid.Empty; } + } + + /// + /// Returns the language vendor's unique identifier, if any + /// + public virtual Guid LanguageVendor { + get { return Guid.Empty; } + } + + /// + /// Returns the document type's unique identifier, if any + /// Defaults to the guid for a text file + /// + public virtual Guid DocumentType { + get { return Compiler.SymbolGuids.DocumentType_Text; } + } + } + + internal sealed class SymbolDocumentWithGuids : SymbolDocumentInfo { + private readonly Guid _language; + private readonly Guid _vendor; + private readonly Guid _documentType; + + internal SymbolDocumentWithGuids(string fileName, ref Guid language) + : base(fileName) { + _language = language; + } + + internal SymbolDocumentWithGuids(string fileName, ref Guid language, ref Guid vendor) + : base(fileName) { + _language = language; + _vendor = vendor; + } + + internal SymbolDocumentWithGuids(string fileName, ref Guid language, ref Guid vendor, ref Guid documentType) + : base(fileName) { + _language = language; + _vendor = vendor; + _documentType = documentType; + } + + public override Guid Language { + get { return _language; } + } + + public override Guid LanguageVendor { + get { return _vendor; } + } + + public override Guid DocumentType { + get { return _documentType; } + } + } + + public partial class Expression { + public static SymbolDocumentInfo SymbolDocument(string fileName) { + return new SymbolDocumentInfo(fileName); + } + public static SymbolDocumentInfo SymbolDocument(string fileName, Guid language) { + return new SymbolDocumentWithGuids(fileName, ref language); + } + public static SymbolDocumentInfo SymbolDocument(string fileName, Guid language, Guid languageVendor) { + return new SymbolDocumentWithGuids(fileName, ref language, ref languageVendor); + } + public static SymbolDocumentInfo SymbolDocument(string fileName, Guid language, Guid languageVendor, Guid documentType) { + return new SymbolDocumentWithGuids(fileName, ref language, ref languageVendor, ref documentType); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/TreeComparer.cs b/ndp/fx/src/core/microsoft/scripting/Ast/TreeComparer.cs new file mode 100644 index 0000000000..ab21a07d58 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/TreeComparer.cs @@ -0,0 +1,540 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Dynamic.Binders; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + + static class TreeComparer { + #region Tree Walker + + /// + /// Walks all of the nodes of a tree and puts all of the expressions into + /// a list. + /// + class FlatTreeWalker : ExpressionVisitor { + public List Expressions = new List(); + public Dictionary _templated; + + internal bool IsTemplatedConstant(ConstantExpression constantExpr) { + if (_templated == null) { + return false; + } + + return _templated.ContainsKey(constantExpr); + } + + protected internal override Expression VisitDynamic(DynamicExpression node) { + Expressions.Add(node); + return base.VisitDynamic(node); + } + + protected internal override Expression VisitBinary(BinaryExpression node) { + Expressions.Add(node); + return base.VisitBinary(node); + } + + protected internal override Expression VisitBlock(BlockExpression node) { + Expressions.Add(node); + return base.VisitBlock(node); + } + + protected internal override Expression VisitGoto(GotoExpression node) { + Expressions.Add(node); + return base.VisitGoto(node); + } + + protected internal override Expression VisitConditional(ConditionalExpression node) { + Expressions.Add(node); + return base.VisitConditional(node); + } + + protected internal override Expression VisitConstant(ConstantExpression node) { + // if we've promoted a value to a templated constant turn it + // back into a normal constant for the purpose of comparing the trees + ITemplatedValue tempVal = node.Value as ITemplatedValue; + if (tempVal != null) { + if (_templated == null) { + _templated = new Dictionary(); + } + + // if we have templated constants we need to make sure to remember their + // templated to see if the resulting rules will be compatible. + ConstantExpression newConstant = Expression.Constant(tempVal.ObjectValue, tempVal.GetType().GetGenericArguments()[0]); + + _templated[newConstant] = newConstant; + Expressions.Add(newConstant); + } else { + Expressions.Add(node); + } + return base.VisitConstant(node); + } + + protected internal override Expression VisitDefault(DefaultExpression node) { + Expressions.Add(node); + return base.VisitDefault(node); + } + + protected internal override Expression VisitInvocation(InvocationExpression node) { + Expressions.Add(node); + return base.VisitInvocation(node); + } + + protected internal override Expression VisitLabel(LabelExpression node) { + Expressions.Add(node); + return base.VisitLabel(node); + } + + protected internal override Expression VisitLambda(Expression node) { + Expressions.Add(node); + return base.VisitLambda(node); + } + + protected internal override Expression VisitLoop(LoopExpression node) { + Expressions.Add(node); + return base.VisitLoop(node); + } + + protected internal override Expression VisitMember(MemberExpression node) { + // ignore the templated constants but add normal member expressions + Expression target = node.Expression; + if (target == null || + !target.Type.IsGenericType || + target.Type.GetGenericTypeDefinition() != typeof(TemplatedValue<>)) { + Expressions.Add(node); + } + + return base.VisitMember(node); + } + + protected internal override Expression VisitMethodCall(MethodCallExpression node) { + Expressions.Add(node); + return base.VisitMethodCall(node); + } + + protected internal override Expression VisitNewArray(NewArrayExpression node) { + Expressions.Add(node); + return base.VisitNewArray(node); + } + + protected internal override Expression VisitNew(NewExpression node) { + Expressions.Add(node); + return base.VisitNew(node); + } + + protected internal override Expression VisitParameter(ParameterExpression node) { + Expressions.Add(node); + return base.VisitParameter(node); + } + + protected internal override Expression VisitSwitch(SwitchExpression node) { + Expressions.Add(node); + return base.VisitSwitch(node); + } + + protected internal override Expression VisitTry(TryExpression node) { + Expressions.Add(node); + return base.VisitTry(node); + } + + protected internal override Expression VisitTypeBinary(TypeBinaryExpression node) { + Expressions.Add(node); + return base.VisitTypeBinary(node); + } + + protected internal override Expression VisitUnary(UnaryExpression node) { + Expressions.Add(node); + return base.VisitUnary(node); + } + + protected internal override Expression VisitExtension(Expression node) { + if (!node.CanReduce) { + Expressions.Add(node); + } else { + return Visit(node.ReduceExtensions()); + } + return node; + } + } + + #endregion + + class VariableInfo { + private Dictionary _left = new Dictionary(); + private Dictionary _right = new Dictionary(); + private int _curLeft, _curRight; + + public int GetLeftVariable(ParameterExpression ve) { + if (ve == null) { + return -1; + } + + int res; + if (!_left.TryGetValue(ve, out res)) { + _left[ve] = res = _curLeft++; + } + + return res; + } + + public int GetRightVariable(ParameterExpression ve) { + if (ve == null) { + return -1; + } + + int res; + if (!_right.TryGetValue(ve, out res)) { + _right[ve] = res = _curRight++; + } + + return res; + } + } + + /// + /// Compares two trees. If the trees differ only by constants then the list of constants which differ + /// is provided as a list via an out-param. The constants collected are the constants in the left + /// side of the tree and only include constants which differ in value. + /// + public static bool Compare(Expression left, Expression right, out List replacementNodes, out bool tooSpecific) { + replacementNodes = null; + tooSpecific = false; + + FlatTreeWalker walkLeft = new FlatTreeWalker(); + FlatTreeWalker walkRight = new FlatTreeWalker(); + walkLeft.Visit(left); + + Debug.Assert(walkLeft._templated == null); + + walkRight.Visit(right); + + // check the length first to see if the trees are obviously different + if (walkLeft.Expressions.Count != walkRight.Expressions.Count) { + return false; + } + + // then see if they differ by just constants which we could replace + List needsReplacement = new List(); + + VariableInfo varInfo = new VariableInfo(); + for (int i = 0; i < walkLeft.Expressions.Count; i++) { + Expression currentLeft = walkLeft.Expressions[i], currentRight = walkRight.Expressions[i]; + + // ReductionRewriter should have removed these + + if (currentLeft.NodeType != currentRight.NodeType) { + // different node types, they can't possibly be equal + return false; + } else if (currentLeft.Type != currentRight.Type) { + // they can't possibly be a match + return false; + } + + if (!CompareTwoNodes(walkRight, needsReplacement, varInfo, currentLeft, currentRight, ref tooSpecific)) { + return false; + } + } + + replacementNodes = needsReplacement; + return true; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + private static bool CompareTwoNodes(FlatTreeWalker walkRight, List needsReplacement, VariableInfo varInfo, Expression currentLeft, Expression currentRight, ref bool tooSpecific) { + switch (currentLeft.NodeType) { + case ExpressionType.Dynamic: + var dynLeft = (DynamicExpression)currentLeft; + var dynRight = (DynamicExpression)currentRight; + + if (!dynRight.Binder.CacheIdentity.Equals(dynLeft.Binder.CacheIdentity)) { + return false; + } + break; + case ExpressionType.Constant: + // check constant value + ConstantExpression ceLeft = (ConstantExpression)currentLeft; + ConstantExpression ceRight = (ConstantExpression)currentRight; + + object leftValue = ceLeft.Value; + object rightValue = ceRight.Value; + + if (leftValue == null && rightValue == null) { + // both are null, no need to template this param. + break; + } + + // See if they're both sites + CallSite leftSite = ceLeft.Value as CallSite; + CallSite rightSite = ceRight.Value as CallSite; + if (leftSite != null) { + if (rightSite == null) { + return false; + } + + if (!leftSite.Binder.CacheIdentity.Equals(rightSite.Binder.CacheIdentity)) { + return false; + } + + return true; + } else if (rightSite != null) { + return false; + } + + // add if left is null and right's something else or + // left and right aren't equal. We'll also add it if + // the existing rule has hoisted this value into a template + // parameter. + if (leftValue == null || + !leftValue.Equals(rightValue) || + walkRight.IsTemplatedConstant(ceRight)) { + + if (walkRight._templated != null && !walkRight.IsTemplatedConstant(ceRight)) { + // if we have template args on the right hand side and this isn't + // one of them we need to re-compile a more general rule. + tooSpecific = true; + } + + needsReplacement.Add(ceLeft); + } + break; + case ExpressionType.Equal: + case ExpressionType.NotEqual: + if (!CompareEquality((BinaryExpression)currentLeft, (BinaryExpression)currentRight)) { + return false; + } + break; + case ExpressionType.Add: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.ArrayIndex: + case ExpressionType.Divide: + case ExpressionType.ExclusiveOr: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.LeftShift: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + case ExpressionType.Modulo: + case ExpressionType.Multiply: + case ExpressionType.Or: + case ExpressionType.OrElse: + case ExpressionType.RightShift: + case ExpressionType.Subtract: + case ExpressionType.AddAssign: + case ExpressionType.SubtractAssign: + case ExpressionType.MultiplyAssign: + case ExpressionType.AddAssignChecked: + case ExpressionType.SubtractAssignChecked: + case ExpressionType.MultiplyAssignChecked: + case ExpressionType.DivideAssign: + case ExpressionType.ModuloAssign: + case ExpressionType.PowerAssign: + case ExpressionType.AndAssign: + case ExpressionType.OrAssign: + case ExpressionType.RightShiftAssign: + case ExpressionType.LeftShiftAssign: + case ExpressionType.ExclusiveOrAssign: + if (!Compare((BinaryExpression)currentLeft, (BinaryExpression)currentRight)) { + return false; + } + break; + case ExpressionType.Call: + if (!Compare((MethodCallExpression)currentLeft, (MethodCallExpression)currentRight)) { + return false; + } + break; + case ExpressionType.New: + // chcek ConstructorInfo and BindingInfo + if (!Compare((NewExpression)currentLeft, (NewExpression)currentRight)) { + return false; + } + break; + case ExpressionType.TypeIs: + case ExpressionType.TypeEqual: + // check type + if (!Compare((TypeBinaryExpression)currentLeft, (TypeBinaryExpression)currentRight)) { + return false; + } + break; + case ExpressionType.Block: + // compare factory method + if (!Compare(varInfo, (BlockExpression)currentLeft, (BlockExpression)currentRight)) { + return false; + } + break; + case ExpressionType.MemberAccess: + // compare member + if (!Compare((MemberExpression)currentLeft, (MemberExpression)currentRight)) { + return false; + } + break; + case ExpressionType.Try: + // compare catch finally blocks and their handler types + if (!Compare(varInfo, (TryExpression)currentLeft, (TryExpression)currentRight)) { + return false; + } + break; + case ExpressionType.Parameter: + if (!Compare(varInfo, (ParameterExpression)currentLeft, (ParameterExpression)currentRight)) { + return false; + } + break; + case ExpressionType.Lambda: + case ExpressionType.Assign: + case ExpressionType.Goto: + case ExpressionType.Throw: + case ExpressionType.Loop: + case ExpressionType.Default: + case ExpressionType.Convert: + case ExpressionType.TypeAs: + case ExpressionType.Unbox: + case ExpressionType.Negate: + case ExpressionType.Not: + case ExpressionType.Conditional: + case ExpressionType.NewArrayInit: + case ExpressionType.NewArrayBounds: + case ExpressionType.Invoke: + // these nodes children and types completely + // define the node + break; + case ExpressionType.Label: + // TODO: cache and compare labels + case ExpressionType.Switch: + // TODO: compare case values + case ExpressionType.Extension: + + // we should have been reduced, but error on the side of being different. + return false; + default: + throw Assert.Unreachable; + } + return true; + } + + private static bool CompareEquality(BinaryExpression left, BinaryExpression right) { + if (left.Left.Type == typeof(object) && left.Right.Type == typeof(object)) { + // could be comparing object to runtime constant w/ identity semantics. + return CompareBinaryForEquality(GetConstantExpression(left.Left), GetConstantExpression(right.Left)) && + CompareBinaryForEquality(GetConstantExpression(left.Right), GetConstantExpression(right.Right)); + } + + return true; + } + + private static ConstantExpression GetConstantExpression(Expression expression) { + if (expression.NodeType == ExpressionType.Convert) { + return GetConstantExpression(((UnaryExpression)expression).Operand); + } + + return expression as ConstantExpression; + } + + private static bool CompareBinaryForEquality(ConstantExpression left, ConstantExpression right) { + if (left == null || right == null) { + return true; + } + + return left.Value == right.Value; + } + + private static bool Compare(BinaryExpression left, BinaryExpression right) { + if (left.Method != right.Method) { + return false; + } + + return true; + } + + private static bool Compare(MethodCallExpression left, MethodCallExpression right) { + if (left.Method != right.Method) { + return false; + } + + return true; + } + + private static bool Compare(NewExpression left, NewExpression right) { + if (left.Constructor != right.Constructor) { + return false; + } + + return true; + } + + + private static bool Compare(TypeBinaryExpression left, TypeBinaryExpression right) { + if (left.TypeOperand != right.TypeOperand) { + return false; + } + + return true; + } + + private static bool Compare(VariableInfo varInfo, BlockExpression left, BlockExpression right) { + if (left.Variables.Count != right.Variables.Count) { + return false; + } + + for (int i = 0; i < left.Variables.Count; i++) { + Compare(varInfo, left.Variables[i], right.Variables[i]); + } + return true; + } + + private static bool Compare(MemberExpression left, MemberExpression right) { + if (left.Member != right.Member) { + return false; + } + + return true; + } + + private static bool Compare(VariableInfo varInfo, TryExpression left, TryExpression right) { + if ((left.Finally == null && right.Finally != null) || + (left.Finally != null && right.Finally == null)) { + return false; + } + + if (left.Handlers.Count != right.Handlers.Count) { + return false; + } + + for (int i = 0; i < left.Handlers.Count; i++) { + if (left.Handlers[i].Test != right.Handlers[i].Test) { + return false; + } + + if (varInfo.GetLeftVariable(left.Handlers[i].Variable) != varInfo.GetRightVariable(right.Handlers[i].Variable)) { + return false; + } + } + + return true; + } + + private static bool Compare(VariableInfo varInfo, ParameterExpression left, ParameterExpression right) { + if (varInfo.GetLeftVariable(left) != varInfo.GetRightVariable(right)) { + return false; + } + + return true; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/TryExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/TryExpression.cs new file mode 100644 index 0000000000..104fac4438 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/TryExpression.cs @@ -0,0 +1,120 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + /// + /// Represents a try/catch/finally/fault block. + /// + /// The body is protected by the try block. + /// The handlers consist of a set of CatchBlocks that can either be catch or filters. + /// The fault runs if an exception is thrown. + /// The finally runs regardless of how control exits the body. + /// Only fault or finally can be supplied + /// + public sealed class TryExpression : Expression { + private readonly Expression _body; + private readonly ReadOnlyCollection _handlers; + private readonly Expression _finally; + private readonly Expression _fault; + + internal TryExpression(Expression body, Expression @finally, Expression fault, ReadOnlyCollection handlers) { + _body = body; + _handlers = handlers; + _finally = @finally; + _fault = fault; + } + + protected override Type GetExpressionType() { + return typeof(void); + } + + protected override ExpressionType GetNodeKind() { + return ExpressionType.Try; + } + + public Expression Body { + get { return _body; } + } + + public ReadOnlyCollection Handlers { + get { return _handlers; } + } + + public Expression Finally { + get { return _finally; } + } + + public Expression Fault { + get { return _fault; } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitTry(this); + } + } + + // TODO: CatchBlock handlers come last because they're params--is this + // confusing? The alternative is to put them after the body but remove + // params. Fortunately, they're strongly typed and not Expressions which + // mitigates this concern somewhat. + public partial class Expression { + + // TryFault + public static TryExpression TryFault(Expression body, Expression fault) { + return MakeTry(body, null, fault, null); + } + + // TryFinally + public static TryExpression TryFinally(Expression body, Expression @finally) { + return MakeTry(body, @finally, null, null); + } + + // TryCatch + public static TryExpression TryCatch(Expression body, params CatchBlock[] handlers) { + return MakeTry(body, null, null, handlers); + } + + // TryCatchFinally + public static TryExpression TryCatchFinally(Expression body, Expression @finally, params CatchBlock[] handlers) { + return MakeTry(body, @finally, null, handlers); + } + + // MakeTry: the one factory that creates TryStatement + public static TryExpression MakeTry(Expression body, Expression @finally, Expression fault, IEnumerable handlers) { + RequiresCanRead(body, "body"); + + var @catch = handlers.ToReadOnly(); + ContractUtils.RequiresNotNullItems(@catch, "handlers"); + + if (fault != null) { + if (@finally != null || @catch.Count > 0) { + throw Error.FaultCannotHaveCatchOrFinally(); + } + RequiresCanRead(fault, "fault"); + } else if (@finally != null) { + RequiresCanRead(@finally, "finally"); + } else if (@catch.Count == 0) { + throw Error.TryMustHaveCatchFinallyOrFault(); + } + + return new TryExpression(body, @finally, fault, @catch); + } + } + +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/TypeBinaryExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/TypeBinaryExpression.cs new file mode 100644 index 0000000000..470cd8b926 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/TypeBinaryExpression.cs @@ -0,0 +1,163 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Dynamic.Utils; +using System.Text; + +namespace System.Linq.Expressions { + //CONFORMING + public sealed class TypeBinaryExpression : Expression { + private readonly Expression _expression; + private readonly Type _typeOperand; + private readonly ExpressionType _nodeKind; + + internal TypeBinaryExpression(Expression expression, Type typeOperand, ExpressionType nodeKind){ + _expression = expression; + _typeOperand = typeOperand; + _nodeKind = nodeKind; + } + + protected override Type GetExpressionType() { + return typeof(bool); + } + + protected override ExpressionType GetNodeKind() { + return _nodeKind; + } + + public Expression Expression { + get { return _expression; } + } + + public Type TypeOperand { + get { return _typeOperand; } + } + + public override bool CanReduce { + get { + return _nodeKind == ExpressionType.TypeEqual; + } + } + + public override Expression Reduce() { + if (CanReduce) { + return ReduceTypeEqual(); + } + return this; + } + + #region Reduce TypeEqual + + private Expression ReduceTypeEqual() { + Type cType = Expression.Type; + + // For value types (including Void, but not nullables), we can + // determine the result now + if (cType.IsValueType && !cType.IsNullableType()) { + return Expression.Block(Expression, Expression.Constant(cType == _typeOperand)); + } + + // Can check the value right now for constants. + if (Expression.NodeType == ExpressionType.Constant) { + return ReduceConstantTypeEqual(); + } + + // If the operand type is a sealed reference type or a nullable + // type, it will match if value is not null + if (cType.IsSealed && (cType == _typeOperand)) { + return Expression.NotEqual(Expression, Expression.Constant(null, Expression.Type)); + } + + // expression is a ByVal parameter. Can safely reevaluate. + var parameter = Expression as ParameterExpression; + if (parameter != null && !parameter.IsByRef) { + return ByValParameterTypeEqual(parameter); + } + + // Create a temp so we only evaluate the left side once + parameter = Parameter(typeof(object), null); + return Expression.Block( + new[] { parameter }, + Expression.Assign(parameter, Helpers.Convert(Expression, typeof(object))), + ByValParameterTypeEqual(parameter) + ); + } + + // helper that is used when re-eval of LHS is safe. + private Expression ByValParameterTypeEqual(ParameterExpression value) { + return Expression.AndAlso( + Expression.NotEqual(value, Expression.Constant(null)), + Expression.Equal( + Expression.Call( + value, + typeof(object).GetMethod("GetType") + ), + Expression.Constant(_typeOperand) + ) + ); + } + + private Expression ReduceConstantTypeEqual() { + ConstantExpression ce = Expression as ConstantExpression; + //TypeEqual(null, T) always returns false. + if (ce.Value == null) { + return Expression.Constant(false); + } else if (_typeOperand.IsNullableType()) { + return Expression.Constant(_typeOperand == ce.Type); + } else { + return Expression.Constant(_typeOperand == ce.Value.GetType()); + } + } + + #endregion + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitTypeBinary(this); + } + } + + /// + /// Factory methods. + /// + public partial class Expression { + //CONFORMING + public static TypeBinaryExpression TypeIs(Expression expression, Type type) { + RequiresCanRead(expression, "expression"); + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.Requires(!type.IsByRef, "type", Strings.TypeMustNotBeByRef); + + return new TypeBinaryExpression(expression, type, ExpressionType.TypeIs); + } + + /// + /// Creates an Expression that compares run-time type identity. It is + /// roughly equivalent to a tree that does this: + /// obj != null && obj.GetType() == type + /// + /// If you want to check for "null" use Expression.Equal + /// + /// The operand. + /// The type to check for at run-time. + /// A new Expression that performs a type equality check. + public static TypeBinaryExpression TypeEqual(Expression expression, Type type) { + RequiresCanRead(expression, "expression"); + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.Requires(!type.IsByRef, "type", Strings.TypeMustNotBeByRef); + + return new TypeBinaryExpression(expression, type, ExpressionType.TypeEqual); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/TypeUtils.cs b/ndp/fx/src/core/microsoft/scripting/Ast/TypeUtils.cs new file mode 100644 index 0000000000..8721732897 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/TypeUtils.cs @@ -0,0 +1,680 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; + +namespace System.Dynamic.Utils { + + internal static class TypeUtils { + private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + internal const MethodAttributes PublicStatic = MethodAttributes.Public | MethodAttributes.Static; + + //CONFORMING + internal static Type GetNonNullableType(this Type type) { + if (IsNullableType(type)) { + return type.GetGenericArguments()[0]; + } + return type; + } + + //CONFORMING + internal static Type GetNullableType(Type type) { + Debug.Assert(type != null, "type cannot be null"); + if (type.IsValueType && !IsNullableType(type)) { + return typeof(Nullable<>).MakeGenericType(type); + } + return type; + } + + //CONFORMING + internal static bool IsNullableType(this Type type) { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + //CONFORMING + internal static bool IsBool(Type type) { + return GetNonNullableType(type) == typeof(bool); + } + + //CONFORMING + internal static bool IsNumeric(Type type) { + type = GetNonNullableType(type); + if (!type.IsEnum) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Char: + case TypeCode.SByte: + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Double: + case TypeCode.Single: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + return true; + } + } + return false; + } + + //CONFORMING + internal static bool IsInteger(Type type) { + type = GetNonNullableType(type); + if (type.IsEnum) { + return false; + } + switch (Type.GetTypeCode(type)) { + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + return true; + default: + return false; + } + } + + //CONFORMING + internal static bool IsArithmetic(Type type) { + type = GetNonNullableType(type); + if (!type.IsEnum) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Double: + case TypeCode.Single: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + return true; + } + } + return false; + } + + //CONFORMING + internal static bool IsUnsignedInt(Type type) { + type = GetNonNullableType(type); + if (!type.IsEnum) { + switch (Type.GetTypeCode(type)) { + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + return true; + } + } + return false; + } + + //CONFORMING + internal static bool IsIntegerOrBool(Type type) { + type = GetNonNullableType(type); + if (!type.IsEnum) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Int64: + case TypeCode.Int32: + case TypeCode.Int16: + case TypeCode.UInt64: + case TypeCode.UInt32: + case TypeCode.UInt16: + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Byte: + return true; + } + } + return false; + } + + //CONFORMING + internal static bool AreReferenceAssignable(Type dest, Type src) { + // WARNING: This actually implements "Is this identity assignable and/or reference assignable?" + if (dest == src) { + return true; + } + if (!dest.IsValueType && !src.IsValueType && dest.IsAssignableFrom(src)) { + return true; + } + return false; + } + + //CONFORMING + // Checks if the type is a valid target for an instance call + internal static bool IsValidInstanceType(MemberInfo member, Type instanceType) { + Type targetType = member.DeclaringType; + if (AreReferenceAssignable(targetType, instanceType)) { + return true; + } + if (instanceType.IsValueType) { + if (AreReferenceAssignable(targetType, typeof(System.Object))) { + return true; + } + if (AreReferenceAssignable(targetType, typeof(System.ValueType))) { + return true; + } + if (instanceType.IsEnum && AreReferenceAssignable(targetType, typeof(System.Enum))) { + return true; + } + // A call to an interface implemented by a struct is legal whether the struct has + // been boxed or not. + if (targetType.IsInterface) { + foreach (Type interfaceType in instanceType.GetInterfaces()) { + if (AreReferenceAssignable(targetType, interfaceType)) { + return true; + } + } + } + } + return false; + } + + //CONFORMING + internal static bool HasReferenceConversion(Type source, Type dest) { + Debug.Assert(source != null && dest != null); + + Type nnSourceType = GetNonNullableType(source); + Type nnDestType = GetNonNullableType(dest); + // Down conversion + if (nnSourceType.IsAssignableFrom(nnDestType)) { + return true; + } + // Up conversion + if (nnDestType.IsAssignableFrom(nnSourceType)) { + return true; + } + // Interface conversion + if (source.IsInterface || dest.IsInterface) { + return true; + } + // Object conversion + if (source == typeof(object) || dest == typeof(object)) { + return true; + } + // + //REVIEW: this conversion rule makes None type special. + // + // None conversion. + // None always has a value of "null" so it should be convertible to any reference type + if (source == typeof(Null) && (dest.IsClass || dest.IsInterface)) { + return true; + } + return false; + } + + //CONFORMING + internal static bool HasIdentityPrimitiveOrNullableConversion(Type source, Type dest) { + Debug.Assert(source != null && dest != null); + + // Identity conversion + if (source == dest) { + return true; + } + + // Everything can be converted to void + if (dest == typeof(void)) { + return true; + } + // Nullable conversions + if (IsNullableType(source) && dest == GetNonNullableType(source)) { + return true; + } + if (IsNullableType(dest) && source == GetNonNullableType(dest)) { + return true; + } + // Primitive runtime conversions + // All conversions amongst enum, bool, char, integer and float types + // (and their corresponding nullable types) are legal except for + // nonbool==>bool and nonbool==>bool? + // Since we have already covered bool==>bool, bool==>bool?, etc, above, + // we can just disallow having a bool or bool? destination type here. + if (IsConvertible(source) && IsConvertible(dest) && GetNonNullableType(dest) != typeof(bool)) { + return true; + } + return false; + } + + //CONFORMING + internal static bool IsConvertible(Type type) { + type = GetNonNullableType(type); + if (type.IsEnum) { + return true; + } + switch (Type.GetTypeCode(type)) { + case TypeCode.Boolean: + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Char: + return true; + default: + return false; + } + } + + internal static bool IsGeneric(Type type) { + return type.ContainsGenericParameters || type.IsGenericTypeDefinition; + } + + internal static bool CanCompareToNull(Type type) { + // This is a bit too conservative. + return !type.IsValueType; + } + + /// + /// Returns a numerical code of the size of a type. All types get both a horizontal + /// and vertical code. Types that are lower in both dimensions have implicit conversions + /// to types that are higher in both dimensions. + /// + internal static bool GetNumericConversionOrder(TypeCode code, out int x, out int y) { + // implicit conversions: + // 0 1 2 3 4 + // 0: U1 -> U2 -> U4 -> U8 + // | | | + // v v v + // 1: I1 -> I2 -> I4 -> I8 + // | | + // v v + // 2: R4 -> R8 + + switch (code) { + case TypeCode.Byte: x = 0; y = 0; break; + case TypeCode.UInt16: x = 1; y = 0; break; + case TypeCode.UInt32: x = 2; y = 0; break; + case TypeCode.UInt64: x = 3; y = 0; break; + + case TypeCode.SByte: x = 0; y = 1; break; + case TypeCode.Int16: x = 1; y = 1; break; + case TypeCode.Int32: x = 2; y = 1; break; + case TypeCode.Int64: x = 3; y = 1; break; + + case TypeCode.Single: x = 1; y = 2; break; + case TypeCode.Double: x = 2; y = 2; break; + + default: + x = y = 0; + return false; + } + return true; + } + + internal static bool IsImplicitlyConvertible(int fromX, int fromY, int toX, int toY) { + return fromX <= toX && fromY <= toY; + } + + //CONFORMING + internal static bool HasBuiltInEqualityOperator(Type left, Type right) { + // If we have an interface and a reference type then we can do + // reference equality. + if (left.IsInterface && !right.IsValueType) { + return true; + } + if (right.IsInterface && !left.IsValueType) { + return true; + } + // If we have two reference types and one is assignable to the + // other then we can do reference equality. + if (!left.IsValueType && !right.IsValueType) { + if (AreReferenceAssignable(left, right) || AreReferenceAssignable(right, left)) { + return true; + } + } + // Otherwise, if the types are not the same then we definitely + // do not have a built-in equality operator. + if (left != right) { + return false; + } + // We have two identical value types, modulo nullability. (If they were both the + // same reference type then we would have returned true earlier.) + Debug.Assert(left.IsValueType); + // Equality between struct types is only defined for numerics, bools, enums, + // and their nullable equivalents. + Type nnType = GetNonNullableType(left); + if (nnType == typeof(bool) || IsNumeric(nnType) || nnType.IsEnum) { + return true; + } + return false; + } + + //CONFORMING + internal static bool IsImplicitlyConvertible(Type source, Type destination) { + return IsIdentityConversion(source, destination) || + IsImplicitNumericConversion(source, destination) || + IsImplicitReferenceConversion(source, destination) || + IsImplicitBoxingConversion(source, destination) || + IsImplicitNullableConversion(source, destination); + } + + internal static bool IsImplicitlyConvertible(Type source, Type destination, bool considerUserDefined) { + return IsImplicitlyConvertible(source, destination) || + (considerUserDefined && GetUserDefinedCoercionMethod(source, destination, true) != null); + } + + //CONFORMING + internal static MethodInfo GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType, bool implicitOnly) { + // check for implicit coercions first + Type nnExprType = TypeUtils.GetNonNullableType(convertFrom); + Type nnConvType = TypeUtils.GetNonNullableType(convertToType); + // try exact match on types + MethodInfo[] eMethods = nnExprType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + MethodInfo method = FindConversionOperator(eMethods, convertFrom, convertToType, implicitOnly); + if (method != null) { + return method; + } + MethodInfo[] cMethods = nnConvType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + method = FindConversionOperator(cMethods, convertFrom, convertToType, implicitOnly); + if (method != null) { + return method; + } + // try lifted conversion + if (nnExprType != convertFrom || nnConvType != convertToType) { + method = FindConversionOperator(eMethods, nnExprType, nnConvType, implicitOnly); + if (method == null) { + method = FindConversionOperator(cMethods, nnExprType, nnConvType, implicitOnly); + } + if (method != null) { + return method; + } + } + return null; + } + + //CONFORMING + internal static MethodInfo FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo, bool implicitOnly) { + foreach (MethodInfo mi in methods) { + if (mi.Name != "op_Implicit" && (implicitOnly || mi.Name != "op_Explicit")) + continue; + if (mi.ReturnType != typeTo) + continue; + ParameterInfo[] pis = mi.GetParametersCached(); + if (pis[0].ParameterType != typeFrom) + continue; + return mi; + } + return null; + } + + + //CONFORMING + private static bool IsIdentityConversion(Type source, Type destination) { + return source == destination; + } + + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private static bool IsImplicitNumericConversion(Type source, Type destination) { + TypeCode tcSource = Type.GetTypeCode(source); + TypeCode tcDest = Type.GetTypeCode(destination); + + switch (tcSource) { + case TypeCode.SByte: + switch (tcDest) { + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.Byte: + switch (tcDest) { + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.Int16: + switch (tcDest) { + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.UInt16: + switch (tcDest) { + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.Int32: + switch (tcDest) { + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.UInt32: + switch (tcDest) { + case TypeCode.UInt32: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.Int64: + case TypeCode.UInt64: + switch (tcDest) { + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.Char: + switch (tcDest) { + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + return false; + case TypeCode.Single: + return (tcDest == TypeCode.Double); + } + return false; + } + + //CONFORMING + private static bool IsImplicitReferenceConversion(Type source, Type destination) { + return destination.IsAssignableFrom(source); + } + + //CONFORMING + private static bool IsImplicitBoxingConversion(Type source, Type destination) { + if (source.IsValueType && (destination == typeof(object) || destination == typeof(System.ValueType))) + return true; + if (source.IsEnum && destination == typeof(System.Enum)) + return true; + return false; + } + + //CONFORMING + private static bool IsImplicitNullableConversion(Type source, Type destination) { + if (IsNullableType(destination)) + return IsImplicitlyConvertible(GetNonNullableType(source), GetNonNullableType(destination)); + return false; + } + + //CONFORMING + internal static bool IsSameOrSubclass(Type type, Type subType) { + return (type == subType) || subType.IsSubclassOf(type); + } + + //CONFORMING + internal static void ValidateType(Type type) { + if (type.IsGenericTypeDefinition) { + throw Error.TypeIsGeneric(type); + } + if (type.ContainsGenericParameters) { + throw Error.TypeContainsGenericParameters(type); + } + } + + //CONFORMING + //from TypeHelper + internal static Type FindGenericType(Type definition, Type type) { + while (type != null && type != typeof(object)) { + if (type.IsGenericType && type.GetGenericTypeDefinition() == definition) + return type; + if (definition.IsInterface) { + foreach (Type itype in type.GetInterfaces()) { + Type found = FindGenericType(definition, itype); + if (found != null) + return found; + } + } + type = type.BaseType; + } + return null; + } + + internal static Type NoRef(Type type) { + return type.IsByRef ? type.GetElementType() : type; + } + + //CONFORMING + internal static bool IsUnsigned(Type type) { + type = GetNonNullableType(type); + switch (Type.GetTypeCode(type)) { + case TypeCode.Byte: + case TypeCode.UInt16: + case TypeCode.Char: + case TypeCode.UInt32: + case TypeCode.UInt64: + return true; + default: + return false; + } + } + + //CONFORMING + internal static bool IsFloatingPoint(Type type) { + type = GetNonNullableType(type); + switch (Type.GetTypeCode(type)) { + case TypeCode.Single: + case TypeCode.Double: + return true; + default: + return false; + } + } + + internal static Type GetNonNoneType(Type type) { + return (type == typeof(Null)) ? typeof(object) : type; + } + + // When emitting constants, we generally emit as the real type, even if + // it is non-visible. However, for some types (e.g. reflection types) + // we convert to the visible type, because the non-visible type isn't + // very useful. + internal static Type GetConstantType(Type type) { + // If it's a visible type, we're done + if (type.IsVisible) { + return type; + } + + // Get the visible base type + Type bt = type; + do { + bt = bt.BaseType; + } while (!bt.IsVisible); + + // If it's one of the known reflection types, + // return the known type. + if (bt == typeof(Type) || + bt == typeof(ConstructorInfo) || + bt == typeof(EventInfo) || + bt == typeof(FieldInfo) || + bt == typeof(MethodInfo) || + bt == typeof(PropertyInfo)) { + return bt; + } + + // else return the original type + return type; + } + + /// + /// Searches for an operator method on the type. The method must have + /// the specified signature, no generic arguments, and have the + /// SpecialName bit set. Also searches inherited operator methods. + /// + /// NOTE: This was designed to satisfy the needs of op_True and + /// op_False, because we have to do runtime lookup for those. It may + /// not work right for unary operators in general. + /// + internal static MethodInfo GetBooleanOperator(Type type, string name) { + do { + MethodInfo result = type.GetMethod(name, AnyStatic, null, new Type[] { type }, null); + if (result != null && result.IsSpecialName && !result.ContainsGenericParameters) { + return result; + } + type = type.BaseType; + } while (type != null); + return null; + } + + /// + /// Returns the System.Type for any object, including null. The type of null + /// is represented by None.Type and all other objects just return the + /// result of Object.GetType + /// + internal static Type GetTypeForBinding(object obj) { + return obj == null ? Null.Type : obj.GetType(); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Ast/UnaryExpression.cs b/ndp/fx/src/core/microsoft/scripting/Ast/UnaryExpression.cs new file mode 100644 index 0000000000..cba517c179 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Ast/UnaryExpression.cs @@ -0,0 +1,618 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Reflection; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + //CONFORMING + public sealed class UnaryExpression : Expression { + private readonly Expression _operand; + private readonly MethodInfo _method; + private readonly ExpressionType _nodeType; + private readonly Type _type; + + internal UnaryExpression(ExpressionType nodeType, Expression expression, Type type, MethodInfo method) { + _operand = expression; + _method = method; + _nodeType = nodeType; + _type = type; + } + + protected override Type GetExpressionType() { + return _type; + } + + protected override ExpressionType GetNodeKind() { + return _nodeType; + } + + public Expression Operand { + get { return _operand; } + } + + public MethodInfo Method { + get { return _method; } + } + + public bool IsLifted { + get { + if (NodeType == ExpressionType.TypeAs || NodeType == ExpressionType.Quote || NodeType == ExpressionType.Throw) { + return false; + } + bool operandIsNullable = TypeUtils.IsNullableType(_operand.Type); + bool resultIsNullable = TypeUtils.IsNullableType(this.Type); + if (_method != null) { + return (operandIsNullable && _method.GetParametersCached()[0].ParameterType != _operand.Type) || + (resultIsNullable && _method.ReturnType != this.Type); + } + return operandIsNullable || resultIsNullable; + } + } + + public bool IsLiftedToNull { + get { + return IsLifted && TypeUtils.IsNullableType(this.Type); + } + } + + internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitUnary(this); + } + + public override bool CanReduce { + get { + switch (_nodeType) { + case ExpressionType.PreIncrementAssign: + case ExpressionType.PreDecrementAssign: + case ExpressionType.PostIncrementAssign: + case ExpressionType.PostDecrementAssign: + return true; + } + return false; + } + } + + public override Expression Reduce() { + if (CanReduce) { + switch (_operand.NodeType) { + case ExpressionType.Index: + return ReduceIndex(); + case ExpressionType.MemberAccess: + return ReduceMember(); + default: + return ReduceVariable(); + } + } + return this; + } + + private bool IsPrefix { + get { return _nodeType == ExpressionType.PreIncrementAssign || _nodeType == ExpressionType.PreDecrementAssign; } + } + + private UnaryExpression FunctionalOp(Expression operand) { + ExpressionType functional; + if (_nodeType == ExpressionType.PreIncrementAssign || _nodeType == ExpressionType.PostIncrementAssign) { + functional = ExpressionType.Increment; + } else { + functional = ExpressionType.Decrement; + } + return new UnaryExpression(functional, operand, operand.Type, _method); + } + + private Expression ReduceVariable() { + if (IsPrefix) { + // (op) var + // ... is reduced into ... + // var = op(var) + return Assign(_operand, FunctionalOp(_operand)); + } + // var (op) + // ... is reduced into ... + // temp = var + // var = op(var) + // temp + var temp = Parameter(_operand.Type, null); + return Block( + new[] { temp }, + Assign(temp, _operand), + Assign(_operand, FunctionalOp(temp)), + temp + ); + } + + private Expression ReduceMember() { + var member = (MemberExpression)_operand; + if (member.Expression == null) { + //static member, reduce the same as variable + return ReduceVariable(); + } else { + var temp1 = Parameter(member.Expression.Type, null); + var initTemp1 = Assign(temp1, member.Expression); + member = MakeMemberAccess(temp1, member.Member); + + if (IsPrefix) { + // (op) value.member + // ... is reduced into ... + // temp1 = value + // temp1.member = op(temp1.member) + return Block( + new[] { temp1 }, + initTemp1, + Assign(member, FunctionalOp(member)) + ); + } + + // value.member (op) + // ... is reduced into ... + // temp1 = value + // temp2 = temp1.member + // temp1.member = op(temp2) + // temp2 + var temp2 = Parameter(member.Type, null); + return Block( + new[] { temp1, temp2 }, + initTemp1, + Assign(temp2, member), + Assign(member, FunctionalOp(temp2)), + temp2 + ); + } + } + + private Expression ReduceIndex() { + // left[a0, a1, ... aN] (op) + // + // ... is reduced into ... + // + // tempObj = left + // tempArg0 = a0 + // ... + // tempArgN = aN + // tempValue = tempObj[tempArg0, ... tempArgN] + // tempObj[tempArg0, ... tempArgN] = op(tempValue) + // tempValue + + bool prefix = IsPrefix; + var index = (IndexExpression)_operand; + int count = index.Arguments.Count; + var block = new Expression[count + (prefix ? 2 : 4)]; + var temps = new ParameterExpression[count + (prefix ? 1 : 2)]; + var args = new ParameterExpression[count]; + + int i = 0; + temps[i] = Parameter(index.Object.Type, null); + block[i] = Assign(temps[i], index.Object); + i++; + while (i <= count) { + var arg = index.Arguments[i - 1]; + args[i - 1] = temps[i] = Parameter(arg.Type, null); + block[i] = Assign(temps[i], arg); + i++; + } + index = MakeIndex(temps[0], index.Indexer, new ReadOnlyCollection(args)); + if (!prefix) { + var lastTemp = temps[i] = Parameter(index.Type, null); + block[i] = Assign(temps[i], index); + i++; + Debug.Assert(i == temps.Length); + block[i++] = Assign(index, FunctionalOp(lastTemp)); + block[i++] = lastTemp; + } else { + Debug.Assert(i == temps.Length); + block[i++] = Assign(index, FunctionalOp(index)); + } + Debug.Assert(i == block.Length); + return Block(new ReadOnlyCollection(temps), new ReadOnlyCollection(block)); + } + } + + /// + /// Factory methods. + /// + public partial class Expression { + //CONFORMING + public static UnaryExpression MakeUnary(ExpressionType unaryType, Expression operand, Type type) { + return MakeUnary(unaryType, operand, type, null); + } + //CONFORMING + public static UnaryExpression MakeUnary(ExpressionType unaryType, Expression operand, Type type, MethodInfo method) { + switch (unaryType) { + case ExpressionType.Negate: + return Negate(operand, method); + case ExpressionType.NegateChecked: + return NegateChecked(operand, method); + case ExpressionType.Not: + return Not(operand, method); + case ExpressionType.ArrayLength: + return ArrayLength(operand); + case ExpressionType.Convert: + return Convert(operand, type, method); + case ExpressionType.ConvertChecked: + return ConvertChecked(operand, type, method); + case ExpressionType.Throw: + return Throw(operand, type); + case ExpressionType.TypeAs: + return TypeAs(operand, type); + case ExpressionType.Quote: + return Quote(operand); + case ExpressionType.UnaryPlus: + return UnaryPlus(operand, method); + case ExpressionType.Unbox: + return Unbox(operand, type); + case ExpressionType.Increment: + return Increment(operand, method); + case ExpressionType.Decrement: + return Decrement(operand, method); + case ExpressionType.PreIncrementAssign: + return PreIncrementAssign(operand, method); + case ExpressionType.PostIncrementAssign: + return PostIncrementAssign(operand, method); + case ExpressionType.PreDecrementAssign: + return PreDecrementAssign(operand, method); + case ExpressionType.PostDecrementAssign: + return PostDecrementAssign(operand, method); + default: + throw Error.UnhandledUnary(unaryType); + } + } + + //CONFORMING + private static UnaryExpression GetUserDefinedUnaryOperatorOrThrow(ExpressionType unaryType, string name, Expression operand) { + UnaryExpression u = GetUserDefinedUnaryOperator(unaryType, name, operand); + if (u != null) { + ValidateParamswithOperandsOrThrow(u.Method.GetParametersCached()[0].ParameterType, operand.Type, unaryType, name); + return u; + } + throw Error.UnaryOperatorNotDefined(unaryType, operand.Type); + } + //CONFORMING + private static UnaryExpression GetUserDefinedUnaryOperator(ExpressionType unaryType, string name, Expression operand) { + Type operandType = operand.Type; + Type[] types = new Type[] { operandType }; + Type nnOperandType = TypeUtils.GetNonNullableType(operandType); + MethodInfo method = nnOperandType.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, types, null); + if (method != null) { + return new UnaryExpression(unaryType, operand, method.ReturnType, method); + } + // try lifted call + if (TypeUtils.IsNullableType(operandType)) { + types[0] = nnOperandType; + method = nnOperandType.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, types, null); + if (method != null && method.ReturnType.IsValueType && !TypeUtils.IsNullableType(method.ReturnType)) { + return new UnaryExpression(unaryType, operand, TypeUtils.GetNullableType(method.ReturnType), method); + } + } + return null; + } + //CONFORMING + private static UnaryExpression GetMethodBasedUnaryOperator(ExpressionType unaryType, Expression operand, MethodInfo method) { + System.Diagnostics.Debug.Assert(method != null); + ValidateOperator(method); + ParameterInfo[] pms = method.GetParametersCached(); + if (pms.Length != 1) + throw Error.IncorrectNumberOfMethodCallArguments(method); + if (ParameterIsAssignable(pms[0], operand.Type)) { + ValidateParamswithOperandsOrThrow(pms[0].ParameterType, operand.Type, unaryType, method.Name); + return new UnaryExpression(unaryType, operand, method.ReturnType, method); + } + // check for lifted call + if (TypeUtils.IsNullableType(operand.Type) && + ParameterIsAssignable(pms[0], TypeUtils.GetNonNullableType(operand.Type)) && + method.ReturnType.IsValueType && !TypeUtils.IsNullableType(method.ReturnType)) { + return new UnaryExpression(unaryType, operand, TypeUtils.GetNullableType(method.ReturnType), method); + } + + throw Error.OperandTypesDoNotMatchParameters(unaryType, method.Name); + } + + //CONFORMING + private static UnaryExpression GetUserDefinedCoercionOrThrow(ExpressionType coercionType, Expression expression, Type convertToType) { + UnaryExpression u = GetUserDefinedCoercion(coercionType, expression, convertToType); + if (u != null) { + return u; + } + throw Error.CoercionOperatorNotDefined(expression.Type, convertToType); + } + + //CONFORMING + private static UnaryExpression GetUserDefinedCoercion(ExpressionType coercionType, Expression expression, Type convertToType) { + MethodInfo method = TypeUtils.GetUserDefinedCoercionMethod(expression.Type, convertToType, false); + if (method != null) { + return new UnaryExpression(coercionType, expression, convertToType, method); + } else { + return null; + } + } + + //CONFORMING + private static UnaryExpression GetMethodBasedCoercionOperator(ExpressionType unaryType, Expression operand, Type convertToType, MethodInfo method) { + System.Diagnostics.Debug.Assert(method != null); + ValidateOperator(method); + ParameterInfo[] pms = method.GetParametersCached(); + if (pms.Length != 1) + throw Error.IncorrectNumberOfMethodCallArguments(method); + if (ParameterIsAssignable(pms[0], operand.Type) && method.ReturnType == convertToType) { + return new UnaryExpression(unaryType, operand, method.ReturnType, method); + } + // check for lifted call + if ((TypeUtils.IsNullableType(operand.Type) || TypeUtils.IsNullableType(convertToType)) && + ParameterIsAssignable(pms[0], TypeUtils.GetNonNullableType(operand.Type)) && + method.ReturnType == TypeUtils.GetNonNullableType(convertToType)) { + return new UnaryExpression(unaryType, operand, convertToType, method); + } + throw Error.OperandTypesDoNotMatchParameters(unaryType, method.Name); + } + + //CONFORMING + public static UnaryExpression Negate(Expression expression) { + return Negate(expression, null); + } + //CONFORMING + public static UnaryExpression Negate(Expression expression, MethodInfo method) { + RequiresCanRead(expression, "expression"); + if (method == null) { + if (TypeUtils.IsArithmetic(expression.Type) && !TypeUtils.IsUnsignedInt(expression.Type)) { + return new UnaryExpression(ExpressionType.Negate, expression, expression.Type, null); + } + return GetUserDefinedUnaryOperatorOrThrow(ExpressionType.Negate, "op_UnaryNegation", expression); + } + return GetMethodBasedUnaryOperator(ExpressionType.Negate, expression, method); + } + + //CONFORMING + public static UnaryExpression UnaryPlus(Expression expression) { + return UnaryPlus(expression, null); + } + //CONFORMING + public static UnaryExpression UnaryPlus(Expression expression, MethodInfo method) { + RequiresCanRead(expression, "expression"); + if (method == null) { + if (TypeUtils.IsArithmetic(expression.Type)) { + return new UnaryExpression(ExpressionType.UnaryPlus, expression, expression.Type, null); + } + return GetUserDefinedUnaryOperatorOrThrow(ExpressionType.UnaryPlus, "op_UnaryPlus", expression); + } + return GetMethodBasedUnaryOperator(ExpressionType.UnaryPlus, expression, method); + } + + //CONFORMING + public static UnaryExpression NegateChecked(Expression expression) { + return NegateChecked(expression, null); + } + //CONFORMING + public static UnaryExpression NegateChecked(Expression expression, MethodInfo method) { + RequiresCanRead(expression, "expression"); + if (method == null) { + if (TypeUtils.IsArithmetic(expression.Type) && !TypeUtils.IsUnsignedInt(expression.Type)) { + return new UnaryExpression(ExpressionType.NegateChecked, expression, expression.Type, null); + } + return GetUserDefinedUnaryOperatorOrThrow(ExpressionType.NegateChecked, "op_UnaryNegation", expression); + } + return GetMethodBasedUnaryOperator(ExpressionType.NegateChecked, expression, method); + } + + //CONFORMING + public static UnaryExpression Not(Expression expression) { + return Not(expression, null); + } + //CONFORMING + public static UnaryExpression Not(Expression expression, MethodInfo method) { + RequiresCanRead(expression, "expression"); + if (method == null) { + if (TypeUtils.IsIntegerOrBool(expression.Type)) { + return new UnaryExpression(ExpressionType.Not, expression, expression.Type, null); + } + UnaryExpression u = GetUserDefinedUnaryOperator(ExpressionType.Not, "op_LogicalNot", expression); + if (u != null) { + return u; + } + return GetUserDefinedUnaryOperatorOrThrow(ExpressionType.Not, "op_OnesComplement", expression); + } + return GetMethodBasedUnaryOperator(ExpressionType.Not, expression, method); + } + + //CONFORMING + public static UnaryExpression TypeAs(Expression expression, Type type) { + RequiresCanRead(expression, "expression"); + ContractUtils.RequiresNotNull(type, "type"); + if (type.IsValueType && !TypeUtils.IsNullableType(type)) { + throw Error.IncorrectTypeForTypeAs(type); + } + return new UnaryExpression(ExpressionType.TypeAs, expression, type, null); + } + + public static UnaryExpression Unbox(Expression expression, Type type) { + RequiresCanRead(expression, "expression"); + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.Requires( + expression.Type.IsInterface || expression.Type == typeof(object), + "expression", Strings.InvalidUnboxType + ); + ContractUtils.Requires(type.IsValueType, "type", Strings.InvalidUnboxType); + return new UnaryExpression(ExpressionType.Unbox, expression, type, null); + } + + //CONFORMING + public static UnaryExpression Convert(Expression expression, Type type) { + return Convert(expression, type, null); + } + //CONFORMING + public static UnaryExpression Convert(Expression expression, Type type, MethodInfo method) { + RequiresCanRead(expression, "expression"); + if (method == null) { + ContractUtils.RequiresNotNull(type, "type"); + if (TypeUtils.HasIdentityPrimitiveOrNullableConversion(expression.Type, type) || + TypeUtils.HasReferenceConversion(expression.Type, type)) { + return new UnaryExpression(ExpressionType.Convert, expression, type, null); + } + return GetUserDefinedCoercionOrThrow(ExpressionType.Convert, expression, type); + } + return GetMethodBasedCoercionOperator(ExpressionType.Convert, expression, type, method); + } + + //CONFORMING + public static UnaryExpression ConvertChecked(Expression expression, Type type) { + return ConvertChecked(expression, type, null); + } + //CONFORMING + public static UnaryExpression ConvertChecked(Expression expression, Type type, MethodInfo method) { + RequiresCanRead(expression, "expression"); + if (method == null) { + ContractUtils.RequiresNotNull(type, "type"); + if (TypeUtils.HasIdentityPrimitiveOrNullableConversion(expression.Type, type)) { + return new UnaryExpression(ExpressionType.ConvertChecked, expression, type, null); + } + if (TypeUtils.HasReferenceConversion(expression.Type, type)) { + return new UnaryExpression(ExpressionType.Convert, expression, type, null); + } + return GetUserDefinedCoercionOrThrow(ExpressionType.ConvertChecked, expression, type); + } + return GetMethodBasedCoercionOperator(ExpressionType.ConvertChecked, expression, type, method); + } + + //CONFORMING + public static UnaryExpression ArrayLength(Expression array) { + ContractUtils.RequiresNotNull(array, "array"); + if (!array.Type.IsArray || !typeof(Array).IsAssignableFrom(array.Type)) { + throw Error.ArgumentMustBeArray(); + } + if (array.Type.GetArrayRank() != 1) { + throw Error.ArgumentMustBeSingleDimensionalArrayType(); + } + return new UnaryExpression(ExpressionType.ArrayLength, array, typeof(int), null); + } + + //CONFORMING + public static UnaryExpression Quote(Expression expression) { + RequiresCanRead(expression, "expression"); + return new UnaryExpression(ExpressionType.Quote, expression, expression.GetType(), null); + } + + // TODO: should we just always wrap it in a convert? + // Do we need this factory at all? + public static Expression Void(Expression expression) { + RequiresCanRead(expression, "expression"); + if (expression.Type == typeof(void)) { + return expression; + } + return Expression.Convert(expression, typeof(void)); + } + + public static UnaryExpression Rethrow() { + return Throw(null); + } + + public static UnaryExpression Throw(Expression value) { + return Throw(value, typeof(void)); + } + + public static UnaryExpression Throw(Expression value, Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + if (value != null) { + RequiresCanRead(value, "value"); + ContractUtils.Requires( + TypeUtils.AreReferenceAssignable(typeof(Exception), value.Type), + "value", + Strings.ArgumentMustBeException + ); + } + return new UnaryExpression(ExpressionType.Throw, value, type, null); + } + + public static UnaryExpression Increment(Expression expression) { + return Increment(expression, null); + } + public static UnaryExpression Increment(Expression expression, MethodInfo method) { + RequiresCanRead(expression, "expression"); + if (method == null) { + if (TypeUtils.IsArithmetic(expression.Type)) { + return new UnaryExpression(ExpressionType.Increment, expression, expression.Type, null); + } + return GetUserDefinedUnaryOperatorOrThrow(ExpressionType.Increment, "op_Increment", expression); + } + return GetMethodBasedUnaryOperator(ExpressionType.Increment, expression, method); + } + + public static UnaryExpression Decrement(Expression expression) { + return Decrement(expression, null); + } + public static UnaryExpression Decrement(Expression expression, MethodInfo method) { + RequiresCanRead(expression, "expression"); + if (method == null) { + if (TypeUtils.IsArithmetic(expression.Type)) { + return new UnaryExpression(ExpressionType.Decrement, expression, expression.Type, null); + } + return GetUserDefinedUnaryOperatorOrThrow(ExpressionType.Decrement, "op_Decrement", expression); + } + return GetMethodBasedUnaryOperator(ExpressionType.Decrement, expression, method); + } + + + public static UnaryExpression PreIncrementAssign(Expression expression) { + return MakeOpAssignUnary(ExpressionType.PreIncrementAssign, expression, null); + } + public static UnaryExpression PreIncrementAssign(Expression expression, MethodInfo method) { + return MakeOpAssignUnary(ExpressionType.PreIncrementAssign, expression, method); + } + public static UnaryExpression PreDecrementAssign(Expression expression) { + return MakeOpAssignUnary(ExpressionType.PreDecrementAssign, expression, null); + } + public static UnaryExpression PreDecrementAssign(Expression expression, MethodInfo method) { + return MakeOpAssignUnary(ExpressionType.PreDecrementAssign, expression, method); + } + public static UnaryExpression PostIncrementAssign(Expression expression) { + return MakeOpAssignUnary(ExpressionType.PostIncrementAssign, expression, null); + } + public static UnaryExpression PostIncrementAssign(Expression expression, MethodInfo method) { + return MakeOpAssignUnary(ExpressionType.PostIncrementAssign, expression, method); + } + public static UnaryExpression PostDecrementAssign(Expression expression) { + return MakeOpAssignUnary(ExpressionType.PostDecrementAssign, expression, null); + } + public static UnaryExpression PostDecrementAssign(Expression expression, MethodInfo method) { + return MakeOpAssignUnary(ExpressionType.PostDecrementAssign, expression, method); + } + private static UnaryExpression MakeOpAssignUnary(ExpressionType kind, Expression expression, MethodInfo method) { + RequiresCanRead(expression, "expression"); + RequiresCanWrite(expression, "expression"); + + UnaryExpression result; + if (method == null) { + if (TypeUtils.IsArithmetic(expression.Type)) { + return new UnaryExpression(kind, expression, expression.Type, null); + } + string name; + if (kind == ExpressionType.PreIncrementAssign || kind == ExpressionType.PostIncrementAssign) { + name = "op_Increment"; + } else { + name = "op_Decrement"; + } + result = GetUserDefinedUnaryOperatorOrThrow(kind, name, expression); + } else { + result = GetMethodBasedUnaryOperator(kind, expression, method); + } + // return type must be assignable back to the operand type + if (!TypeUtils.AreReferenceAssignable(expression.Type, result.Type)) { + throw Error.UserDefinedOpMustHaveValidReturnType(kind, method.Name); + } + return result; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/AnalyzedTree.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/AnalyzedTree.cs new file mode 100644 index 0000000000..d69ff42a20 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/AnalyzedTree.cs @@ -0,0 +1,41 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.SymbolStore; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + internal sealed class AnalyzedTree { + internal readonly Dictionary Scopes = new Dictionary(); + internal readonly Dictionary Constants = new Dictionary(); + + // Lazy initialized because many trees will not need it + private Dictionary _symbolWriters; + internal Dictionary SymbolWriters { + get { + if (_symbolWriters == null) { + _symbolWriters = new Dictionary(); + } + return _symbolWriters; + } + } + + // Created by VariableBinder + internal AnalyzedTree() { + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/ArgumentPrepender.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/ArgumentPrepender.cs new file mode 100644 index 0000000000..ffa524a88c --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/ArgumentPrepender.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq.Expressions; + +namespace System.Linq.Expressions.Compiler { + /// + /// Prepends an argument and stands in as an IArgumentProvider. Avoids + /// creation of a ReadOnlyCollection or making a temporary array copy. + /// + /// Note this is always as better than allocating an array because an empty + /// array has 16 bytes of overhead - and so does this. + /// + class ArgumentPrepender : IArgumentProvider { + private IArgumentProvider _expression; + private Expression _first; + + internal ArgumentPrepender(Expression first, IArgumentProvider provider) { + _first = first; + _expression = provider; + } + + #region IArgumentProvider Members + + public Expression GetArgument(int index) { + if (index == 0) { + return _first; + } + + return _expression.GetArgument(index - 1); + } + + public int ArgumentCount { + get { return _expression.ArgumentCount + 1; } + } + + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/AssemblyGen.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/AssemblyGen.cs new file mode 100644 index 0000000000..ac92993a75 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/AssemblyGen.cs @@ -0,0 +1,220 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Resources; +using System.Dynamic.Utils; +using System.Security; +using System.Text; +using System.Threading; + +namespace System.Linq.Expressions.Compiler { + internal sealed class AssemblyGen { + private readonly AssemblyBuilder _myAssembly; + private readonly ModuleBuilder _myModule; + private readonly bool _isDebuggable; + +#if !SILVERLIGHT + private readonly string _outFileName; // can be null iff !SaveAndReloadAssemblies + private readonly string _outDir; // null means the current directory +#endif + + private int _index; + + internal bool IsDebuggable { + get { +#if !SILVERLIGHT + Debug.Assert(_isDebuggable == (_myModule.GetSymWriter() != null)); +#endif + return _isDebuggable; + } + } + + internal AssemblyGen(AssemblyName name, string outDir, string outFileExtension, bool isDebuggable, bool isUnsafe) { + ContractUtils.RequiresNotNull(name, "name"); + +#if SILVERLIGHT // AssemblyBuilderAccess.RunAndSave, Environment.CurrentDirectory + _myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); + _myModule = _myAssembly.DefineDynamicModule(name.Name, isDebuggable); +#else + if (outFileExtension == null) { + outFileExtension = ".dll"; + } + + if (outDir != null) { + try { + outDir = Path.GetFullPath(outDir); + } catch (Exception) { + throw Error.InvalidOutputDir(); + } + try { + Path.Combine(outDir, name.Name + outFileExtension); + } catch (ArgumentException) { + throw Error.InvalidAsmNameOrExtension(); + } + + _outFileName = name.Name + outFileExtension; + _outDir = outDir; + } + + // mark the assembly transparent so that it works in partial trust: + CustomAttributeBuilder[] attributes; + + if (isUnsafe) { + attributes = new CustomAttributeBuilder[0]; + } else { + attributes = new CustomAttributeBuilder[] { + new CustomAttributeBuilder(typeof(SecurityTransparentAttribute).GetConstructor(Type.EmptyTypes), new object[0]) + }; + } + + if (outDir != null) { + _myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave, outDir, + null, null, null, null, false, attributes); + + _myModule = _myAssembly.DefineDynamicModule(name.Name, _outFileName, isDebuggable); + } else { + _myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run, attributes); + _myModule = _myAssembly.DefineDynamicModule(name.Name, isDebuggable); + } + + if (isUnsafe) { + _myModule.SetCustomAttribute( + new CustomAttributeBuilder( + typeof(UnverifiableCodeAttribute).GetConstructor(Type.EmptyTypes), + new object[0] + ) + ); + } + + _myAssembly.DefineVersionInfoResource(); +#endif + _isDebuggable = isDebuggable; + + if (isDebuggable) { + SetDebuggableAttributes(); + } + } + + internal void SetDebuggableAttributes() { + DebuggableAttribute.DebuggingModes attrs = + DebuggableAttribute.DebuggingModes.Default | + DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | + DebuggableAttribute.DebuggingModes.DisableOptimizations; + + Type[] argTypes = new Type[] { typeof(DebuggableAttribute.DebuggingModes) }; + Object[] argValues = new Object[] { attrs }; + + _myAssembly.SetCustomAttribute(new CustomAttributeBuilder( + typeof(DebuggableAttribute).GetConstructor(argTypes), argValues) + ); + + _myModule.SetCustomAttribute(new CustomAttributeBuilder( + typeof(DebuggableAttribute).GetConstructor(argTypes), argValues) + ); + } + +#if !SILVERLIGHT // IResourceWriter + internal void AddResourceFile(string name, string file, ResourceAttributes attribute) { + IResourceWriter rw = _myModule.DefineResource(Path.GetFileName(file), name, attribute); + + string ext = Path.GetExtension(file); + if (String.Equals(ext, ".resources", StringComparison.OrdinalIgnoreCase)) { + ResourceReader rr = new ResourceReader(file); + using (rr) { + System.Collections.IDictionaryEnumerator de = rr.GetEnumerator(); + + while (de.MoveNext()) { + string key = de.Key as string; + rw.AddResource(key, de.Value); + } + } + } else { + rw.AddResource(name, File.ReadAllBytes(file)); + } + } +#endif + + #region Save the assembly + + //Return the location of the saved assembly file. + //The file location is used by PE verification in Microsoft.Scripting. + internal string SaveAssembly() { +#if !SILVERLIGHT // AssemblyBuilder.Save + _myAssembly.Save(_outFileName, PortableExecutableKinds.ILOnly, ImageFileMachine.I386); + return Path.Combine(_outDir, _outFileName); +#else + return null; +#endif + } + #endregion + + internal TypeBuilder DefinePublicType(string name, Type parent, bool preserveName) { + return DefineType(name, parent, TypeAttributes.Public, preserveName); + } + + internal TypeBuilder DefineType(string name, Type parent, TypeAttributes attr, bool preserveName) { + ContractUtils.RequiresNotNull(name, "name"); + ContractUtils.RequiresNotNull(parent, "parent"); + + StringBuilder sb = new StringBuilder(name); + if (!preserveName) { + int index = Interlocked.Increment(ref _index); + sb.Append("$"); + sb.Append(index); + } + + // There is a bug in Reflection.Emit that leads to + // Unhandled Exception: System.Runtime.InteropServices.COMException (0x80131130): Record not found on lookup. + // if there is any of the characters []*&+,\ in the type name and a method defined on the type is called. + sb.Replace('+', '_').Replace('[', '_').Replace(']', '_').Replace('*', '_').Replace('&', '_').Replace(',', '_').Replace('\\', '_'); + + name = sb.ToString(); + + return _myModule.DefineType(name, attr, parent); + } + +#if !SILVERLIGHT + internal void SetEntryPoint(MethodInfo mi, PEFileKinds kind) { + _myAssembly.SetEntryPoint(mi, kind); + } +#endif + + internal AssemblyBuilder AssemblyBuilder { + get { return _myAssembly; } + } + + internal ModuleBuilder ModuleBuilder { + get { return _myModule; } + } + } + + internal static class SymbolGuids { + internal static readonly Guid LanguageType_ILAssembly = + new Guid(-1358664493, -12063, 0x11d2, 0x97, 0x7c, 0, 160, 0xc9, 180, 0xd5, 12); + + internal static readonly Guid DocumentType_Text = + new Guid(0x5a869d0b, 0x6611, 0x11d3, 0xbd, 0x2a, 0, 0, 0xf8, 8, 0x49, 0xbd); + + internal static readonly Guid LanguageVendor_Microsoft = + new Guid(-1723120188, -6423, 0x11d2, 0x90, 0x3f, 0, 0xc0, 0x4f, 0xa3, 2, 0xa1); + } +} + diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/BoundConstants.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/BoundConstants.cs new file mode 100644 index 0000000000..6441f8fe79 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/BoundConstants.cs @@ -0,0 +1,174 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + /// + /// This type tracks "runtime" constants--live objects that appear in + /// ConstantExpression nodes and must be bound to the delegate. + /// + internal sealed class BoundConstants { + + /// + /// Constants can emit themselves as different types + /// For caching purposes, we need to treat each distinct Type as a + /// seperate thing to cache. (If we have to cast it on the way out, it + /// ends up using a JIT temp and defeats the purpose of caching the + /// value in a local) + /// + private struct TypedConstant : IEquatable { + internal readonly object Value; + internal readonly Type Type; + + internal TypedConstant(object value, Type type) { + Debug.Assert(type == TypeUtils.GetConstantType(type)); + + Value = value; + Type = type; + } + + public override int GetHashCode() { + return RuntimeHelpers.GetHashCode(Value) ^ Type.GetHashCode(); + } + public bool Equals(TypedConstant other) { + return object.ReferenceEquals(Value, other.Value) && Type.Equals(other.Type); + } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2231:OverloadOperatorEqualsOnOverridingValueTypeEquals")] + public override bool Equals(object obj) { + return (obj is TypedConstant) && Equals((TypedConstant)obj); + } + } + + /// + /// The list of constants in the order they appear in the constant array + /// + private readonly List _values = new List(); + + /// + /// The index of each constant in the constant array + /// + private readonly Dictionary _indexes = new Dictionary(ReferenceEqualityComparer.Instance); + + /// + /// Each constant referenced within this lambda, and how often it was referenced + /// + private readonly Dictionary _references = new Dictionary(); + + /// + /// IL locals for storing frequently used constants + /// + private readonly Dictionary _cache = new Dictionary(); + + internal int Count { + get { return _values.Count; } + } + + internal object[] ToArray() { + return _values.ToArray(); + } + + /// + /// Called by VariableBinder. Adds the constant to the list (if needed) + /// and increases the reference count by one + /// + internal void AddReference(object value, Type type) { + type = TypeUtils.GetConstantType(type); + + if (!_indexes.ContainsKey(value)) { + _indexes.Add(value, _values.Count); + _values.Add(value); + } + Helpers.IncrementCount(new TypedConstant(value, type), _references); + } + + /// + /// Emits a live object as a constant + /// + internal void EmitConstant(LambdaCompiler lc, object value, Type type) { + Debug.Assert(!ILGen.CanEmitConstant(value, type)); + + type = TypeUtils.GetConstantType(type); + + LocalBuilder local; + if (_cache.TryGetValue(new TypedConstant(value, type), out local)) { + lc.IL.Emit(OpCodes.Ldloc, local); + return; + } + EmitConstantsArray(lc); + EmitConstantFromArray(lc, value, type); + } + + /// + /// Emit code to cache frequently used constants into IL locals, + /// instead of pulling them out of the array each time + /// + internal void EmitCacheConstants(LambdaCompiler lc) { + int count = 0; + foreach (var reference in _references) { + if (ShouldCache(reference.Value)) { + count++; + } + } + if (count == 0) { + return; + } + EmitConstantsArray(lc); + foreach (var reference in _references) { + if (ShouldCache(reference.Value)) { + if (--count > 0) { + // Dup array to keep it on the stack + lc.IL.Emit(OpCodes.Dup); + } + LocalBuilder local = lc.IL.DeclareLocal(reference.Key.Type); + EmitConstantFromArray(lc, reference.Key.Value, local.LocalType); + lc.IL.Emit(OpCodes.Stloc, local); + _cache.Add(reference.Key, local); + } + } + } + + private static bool ShouldCache(int refCount) { + // TODO: this caching is probably too aggressive in the face of + // conditionals + return refCount > 2; + } + + private static void EmitConstantsArray(LambdaCompiler lc) { + lc.EmitClosureArgument(); + lc.IL.Emit(OpCodes.Ldfld, typeof(Closure).GetField("Constants")); + } + + private void EmitConstantFromArray(LambdaCompiler lc, object value, Type type) { + int index; + if (!_indexes.TryGetValue(value, out index)) { + _indexes.Add(value, index = _values.Count); + _values.Add(value); + } + + lc.IL.EmitInt(index); + lc.IL.Emit(OpCodes.Ldelem_Ref); + if (type.IsValueType) { + lc.IL.Emit(OpCodes.Unbox_Any, type); + } else if (type != typeof(object)) { + lc.IL.Emit(OpCodes.Castclass, type); + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/CatchRecord.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/CatchRecord.cs new file mode 100644 index 0000000000..a1e6220483 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/CatchRecord.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection.Emit; + +namespace System.Linq.Expressions.Compiler { + internal struct CatchRecord { + private LocalBuilder _local; + private CatchBlock _block; + + internal CatchRecord(LocalBuilder local, CatchBlock block) { + _local = local; + _block = block; + } + + internal LocalBuilder Local { + get { return _local; } + } + + internal CatchBlock Block { + get { return _block; } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/Closure.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/Closure.cs new file mode 100644 index 0000000000..37269d0ff3 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/Closure.cs @@ -0,0 +1,47 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace System.Runtime.CompilerServices { + + /// + /// If the delegate generated by the Lambda compiler needs to either be a + /// closure, or close over constants, the delegate itself will close over + /// the instance of this object. + /// + /// TODO: Prevent this from being exposed as public surface area. See what + /// Linq v1 does with System.Runtime.CompilerServices.ExecutionScope + /// + public sealed class Closure { + /// + /// The constant pool + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + public readonly object[] Constants; + + /// + /// The environment, which stores closed over variables from the parent + /// scope + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + public readonly object[] Locals; + + public Closure(object[] constants, object[] locals) { + Constants = constants; + Locals = locals; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/CompilerScope.Storage.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/CompilerScope.Storage.cs new file mode 100644 index 0000000000..91ddf24941 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/CompilerScope.Storage.cs @@ -0,0 +1,181 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; + +namespace System.Linq.Expressions.Compiler { + + internal sealed partial class CompilerScope { + + private abstract class Storage { + internal readonly LambdaCompiler Compiler; + internal readonly ParameterExpression Variable; + + internal Storage(LambdaCompiler compiler, ParameterExpression variable) { + Compiler = compiler; + Variable = variable; + } + + internal abstract void EmitLoad(); + internal abstract void EmitAddress(); + internal abstract void EmitStore(); + + internal virtual void EmitStore(Storage value) { + value.EmitLoad(); + EmitStore(); + } + + internal virtual void FreeLocal() { + } + } + + private sealed class LocalStorage : Storage { + private readonly LocalBuilder _local; + + internal LocalStorage(LambdaCompiler compiler, ParameterExpression variable) + : base(compiler, variable) { + _local = compiler.GetNamedLocal(variable.Type, variable.Name); + } + + internal override void EmitLoad() { + Compiler.IL.Emit(OpCodes.Ldloc, _local); + } + + internal override void EmitStore() { + Compiler.IL.Emit(OpCodes.Stloc, _local); + } + + internal override void EmitAddress() { + Compiler.IL.Emit(OpCodes.Ldloca, _local); + } + + internal override void FreeLocal() { + Compiler.FreeNamedLocal(_local, Variable.Name); + } + } + + private sealed class ArgumentStorage : Storage { + private readonly int _argument; + + internal ArgumentStorage(LambdaCompiler compiler, ParameterExpression p) + : base(compiler, p) { + _argument = compiler.GetLambdaArgument(compiler.Parameters.IndexOf(p)); + } + + internal override void EmitLoad() { + Compiler.IL.EmitLoadArg(_argument); + } + + internal override void EmitStore() { + Compiler.IL.EmitStoreArg(_argument); + } + + internal override void EmitAddress() { + Compiler.IL.EmitLoadArgAddress(_argument); + } + } + + private sealed class ElementBoxStorage : Storage { + private readonly int _index; + private readonly Storage _array; + private readonly Type _boxType; + private readonly FieldInfo _boxValueField; + + internal ElementBoxStorage(Storage array, int index, ParameterExpression variable) + : base(array.Compiler, variable) { + _array = array; + _index = index; + _boxType = typeof(StrongBox<>).MakeGenericType(variable.Type); + _boxValueField = _boxType.GetField("Value"); + } + + internal override void EmitLoad() { + EmitLoadBox(); + Compiler.IL.Emit(OpCodes.Ldfld, _boxValueField); + } + + internal override void EmitStore() { + LocalBuilder value = Compiler.IL.GetLocal(Variable.Type); + Compiler.IL.Emit(OpCodes.Stloc, value); + EmitLoadBox(); + Compiler.IL.Emit(OpCodes.Ldloc, value); + Compiler.IL.FreeLocal(value); + Compiler.IL.Emit(OpCodes.Stfld, _boxValueField); + } + + internal override void EmitStore(Storage value) { + EmitLoadBox(); + value.EmitLoad(); + Compiler.IL.Emit(OpCodes.Stfld, _boxValueField); + } + + internal override void EmitAddress() { + EmitLoadBox(); + Compiler.IL.Emit(OpCodes.Ldflda, _boxValueField); + } + + internal void EmitLoadBox() { + _array.EmitLoad(); + Compiler.IL.EmitInt(_index); + Compiler.IL.Emit(OpCodes.Ldelem_Ref); + Compiler.IL.Emit(OpCodes.Castclass, _boxType); + } + } + + private sealed class LocalBoxStorage : Storage { + private readonly LocalBuilder _boxLocal; + private readonly Type _boxType; + private readonly FieldInfo _boxValueField; + + internal LocalBoxStorage(LambdaCompiler compiler, ParameterExpression variable) + : base(compiler, variable) { + _boxType = typeof(StrongBox<>).MakeGenericType(variable.Type); + _boxValueField = _boxType.GetField("Value"); + _boxLocal = compiler.GetNamedLocal(_boxType, variable.Name); + } + + internal override void EmitLoad() { + Compiler.IL.Emit(OpCodes.Ldloc, _boxLocal); + Compiler.IL.Emit(OpCodes.Ldfld, _boxValueField); + } + + internal override void EmitAddress() { + Compiler.IL.Emit(OpCodes.Ldloc, _boxLocal); + Compiler.IL.Emit(OpCodes.Ldflda, _boxValueField); + } + + internal override void EmitStore() { + LocalBuilder value = Compiler.IL.GetLocal(Variable.Type); + Compiler.IL.Emit(OpCodes.Stloc, value); + Compiler.IL.Emit(OpCodes.Ldloc, _boxLocal); + Compiler.IL.Emit(OpCodes.Ldloc, value); + Compiler.IL.FreeLocal(value); + Compiler.IL.Emit(OpCodes.Stfld, _boxValueField); + } + + internal override void EmitStore(Storage value) { + Compiler.IL.Emit(OpCodes.Ldloc, _boxLocal); + value.EmitLoad(); + Compiler.IL.Emit(OpCodes.Stfld, _boxValueField); + } + + internal void EmitStoreBox() { + Compiler.IL.Emit(OpCodes.Stloc, _boxLocal); + } + } + } +} \ No newline at end of file diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/CompilerScope.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/CompilerScope.cs new file mode 100644 index 0000000000..c2848712b4 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/CompilerScope.cs @@ -0,0 +1,433 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Dynamic; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + internal enum VariableStorageKind { + Local, + Hoisted + } + + /// + /// CompilerScope is the data structure which the Compiler keeps information + /// related to compiling scopes. It stores the following information: + /// 1. Parent relationship (for resolving variables) + /// 2. Information about hoisted variables + /// 3. Information for resolving closures + /// + /// Instances are produced by VariableBinder, which does a tree walk + /// looking for scope nodes: LambdaExpression and BlockExpression. + /// + internal sealed partial class CompilerScope { + /// + /// parent scope, if any + /// + private CompilerScope _parent; + + /// + /// The expression node for this scope + /// + internal readonly Expression Node; + + /// + /// Does this scope (or any inner scope) close over variables from any + /// parent scope? + /// Populated by VariableBinder + /// + internal bool NeedsClosure; + + /// + /// Variables defined in this scope, and whether they're hoisted or not + /// Populated by VariableBinder + /// + internal readonly Dictionary Definitions = new Dictionary(); + + /// + /// Each variable referenced within this scope, and how often it was referenced + /// Populated by VariableBinder + /// + /// Created lazily as we only use in about 1 out of 3 compiles when compiling rules. + /// + internal Dictionary ReferenceCount; + + /// + /// Scopes whose variables were merged into this one + /// + /// Created lazily as we create hundreds of compiler scopes w/o merging scopes when compiling rules. + /// + internal Set MergedScopes; + + /// + /// The scope's hoisted locals, if any. + /// Provides storage for variables that are referenced from nested lambdas + /// + private HoistedLocals _hoistedLocals; + + /// + /// The closed over hoisted locals + /// + private HoistedLocals _closureHoistedLocals; + + /// + /// Mutable dictionary that maps non-hoisted variables to either local + /// slots or argument slots + /// + private readonly Dictionary _locals = new Dictionary(); + + internal CompilerScope(Expression node) { + Node = node; + var variables = GetVariables(node); + + Definitions = new Dictionary(variables.Count); + foreach (var v in variables) { + Definitions.Add(v, VariableStorageKind.Local); + } + } + + /// + /// This scope's hoisted locals, or the closed over locals, if any + /// Equivalent to: _hoistedLocals ?? _closureHoistedLocals + /// + internal HoistedLocals NearestHoistedLocals { + get { return _hoistedLocals ?? _closureHoistedLocals; } + } + + /// + /// Called when entering a lambda/block. Performs all variable allocation + /// needed, including creating hoisted locals and IL locals for accessing + /// parent locals + /// + internal CompilerScope Enter(LambdaCompiler lc, CompilerScope parent) { + SetParent(lc, parent); + + AllocateLocals(lc); + + if (IsLambda && _closureHoistedLocals != null) { + EmitClosureAccess(lc, _closureHoistedLocals); + } + + EmitNewHoistedLocals(lc); + + if (IsLambda) { + EmitCachedVariables(); + } + + return this; + } + + /// + /// Frees unnamed locals, clears state associated with this compiler + /// + internal CompilerScope Exit() { + // free scope's variables + if (!IsLambda) { + foreach (Storage storage in _locals.Values) { + storage.FreeLocal(); + } + } + + // Clear state that is associated with this parent + // (because the scope can be reused in another context) + CompilerScope parent = _parent; + _parent = null; + _hoistedLocals = null; + _closureHoistedLocals = null; + _locals.Clear(); + + return parent; + } + + #region LocalScopeExpression support + + internal void EmitVariableAccess(LambdaCompiler lc, ReadOnlyCollection vars) { + if (NearestHoistedLocals != null) { + // Find what array each variable is on & its index + var indexes = new List(vars.Count); + + foreach (var variable in vars) { + // For each variable, find what array it's defined on + ulong parents = 0; + HoistedLocals locals = NearestHoistedLocals; + while (!locals.Indexes.ContainsKey(variable)) { + parents++; + locals = locals.Parent; + Debug.Assert(locals != null); + } + + // combine the number of parents we walked, with the + // real index of variable to get the index to emit. + ulong index = (parents << 32) | (uint)locals.Indexes[variable]; + + indexes.Add((long)index); + } + + if (indexes.Count > 0) { + EmitGet(NearestHoistedLocals.SelfVariable); + lc.EmitConstantArray(indexes.ToArray()); + lc.IL.EmitCall(typeof(RuntimeOps).GetMethod("CreateRuntimeVariables", new[] { typeof(object[]), typeof(long[]) })); + return; + } + } + + // No visible variables + lc.IL.EmitCall(typeof(RuntimeOps).GetMethod("CreateRuntimeVariables", Type.EmptyTypes)); + return; + } + + #endregion + + #region Variable access + + /// + /// Adds a new virtual variable corresponding to an IL local + /// + internal void AddLocal(LambdaCompiler gen, ParameterExpression variable) { + _locals.Add(variable, new LocalStorage(gen, variable)); + } + + internal void EmitGet(ParameterExpression variable) { + ResolveVariable(variable).EmitLoad(); + } + + internal void EmitSet(ParameterExpression variable) { + ResolveVariable(variable).EmitStore(); + } + + internal void EmitAddressOf(ParameterExpression variable) { + ResolveVariable(variable).EmitAddress(); + } + + private Storage ResolveVariable(ParameterExpression variable) { + return ResolveVariable(variable, NearestHoistedLocals); + } + + /// + /// Resolve a local variable in this scope or a closed over scope + /// Throws if the variable is defined + /// + private Storage ResolveVariable(ParameterExpression variable, HoistedLocals hoistedLocals) { + // Search IL locals and arguments, but only in this lambda + for (CompilerScope s = this; s != null; s = s._parent) { + Storage storage; + if (s._locals.TryGetValue(variable, out storage)) { + return storage; + } + + // if this is a lambda, we're done + if (s.IsLambda) { + break; + } + } + + // search hoisted locals + for (HoistedLocals h = hoistedLocals; h != null; h = h.Parent) { + int index; + if (h.Indexes.TryGetValue(variable, out index)) { + return new ElementBoxStorage( + ResolveVariable(h.SelfVariable, hoistedLocals), + index, + variable + ); + } + } + + // If this is a genuine unbound variable, the error should be + // thrown in VariableBinder. + throw Error.UndefinedVariable(variable.Name, variable.Type, CurrentLambdaName); + } + + #endregion + + // private methods: + + private bool IsLambda { + get { return Node.NodeType == ExpressionType.Lambda; } + } + + private void SetParent(LambdaCompiler lc, CompilerScope parent) { + Debug.Assert(_parent == null && parent != this); + _parent = parent; + + if (NeedsClosure && _parent != null) { + _closureHoistedLocals = _parent.NearestHoistedLocals; + } + + var hoistedVars = GetVariables().Where(p => Definitions[p] == VariableStorageKind.Hoisted).ToReadOnly(); + + if (hoistedVars.Count > 0) { + _hoistedLocals = new HoistedLocals(_closureHoistedLocals, hoistedVars); + AddLocal(lc, _hoistedLocals.SelfVariable); + } + } + + // Emits creation of the hoisted local storage + private void EmitNewHoistedLocals(LambdaCompiler lc) { + if (_hoistedLocals == null) { + return; + } + + // create the array + lc.IL.EmitInt(_hoistedLocals.Variables.Count); + lc.IL.Emit(OpCodes.Newarr, typeof(object)); + + // initialize all elements + int i = 0; + foreach (ParameterExpression v in _hoistedLocals.Variables) { + // array[i] = new StrongBox(...); + lc.IL.Emit(OpCodes.Dup); + lc.IL.EmitInt(i++); + Type boxType = typeof(StrongBox<>).MakeGenericType(v.Type); + + if (lc.Parameters.Contains(v)) { + // array[i] = new StrongBox(argument); + int index = lc.Parameters.IndexOf(v); + lc.EmitLambdaArgument(index); + lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(new Type[] { v.Type })); + } else if (v == _hoistedLocals.ParentVariable) { + // TODO: StrongBox is overkill for the parent pointer + // array[i] = new StrongBox(closure.Locals); + ResolveVariable(v, _closureHoistedLocals).EmitLoad(); + lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(new Type[] { v.Type })); + } else { + // array[i] = new StrongBox(); + lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(Type.EmptyTypes)); + } + // if we want to cache this into a local, do it now + if (ShouldCache(v) && !_locals.ContainsKey(v)) { + lc.IL.Emit(OpCodes.Dup); + CacheBoxToLocal(lc, v); + } + lc.IL.Emit(OpCodes.Stelem_Ref); + } + + // store it + EmitSet(_hoistedLocals.SelfVariable); + } + + + // If hoisted variables are referenced "enough", we cache the + // StrongBox in an IL local, which saves an array index and a cast + // when we go to look it up later + private void EmitCachedVariables() { + foreach (var v in GetVariables()) { + if (ShouldCache(v)) { + if (!_locals.ContainsKey(v)) { + var storage = ResolveVariable(v) as ElementBoxStorage; + if (storage != null) { + storage.EmitLoadBox(); + CacheBoxToLocal(storage.Compiler, v); + } + } + } + } + } + + private bool ShouldCache(ParameterExpression v) { + if (ReferenceCount == null) { + return false; + } + // TODO: this caching is probably too aggressive in the face of + // conditionals + int count; + return ReferenceCount.TryGetValue(v, out count) && count > 2; + } + + private void CacheBoxToLocal(LambdaCompiler lc, ParameterExpression v) { + Debug.Assert(ShouldCache(v) && !_locals.ContainsKey(v)); + var local = new LocalBoxStorage(lc, v); + local.EmitStoreBox(); + _locals.Add(v, local); + } + + // Creates IL locals for accessing closures + private void EmitClosureAccess(LambdaCompiler lc, HoistedLocals locals) { + if (locals == null) { + return; + } + + EmitClosureToVariable(lc, locals); + + while ((locals = locals.Parent) != null) { + var v = locals.SelfVariable; + var local = new LocalStorage(lc, v); + local.EmitStore(ResolveVariable(v)); + _locals.Add(v, local); + } + } + + private void EmitClosureToVariable(LambdaCompiler lc, HoistedLocals locals) { + lc.EmitClosureArgument(); + lc.IL.Emit(OpCodes.Ldfld, typeof(Closure).GetField("Locals")); + AddLocal(lc, locals.SelfVariable); + EmitSet(locals.SelfVariable); + } + + // Allocates slots for IL locals or IL arguments + private void AllocateLocals(LambdaCompiler lc) { + foreach (ParameterExpression v in GetVariables()) { + if (Definitions[v] == VariableStorageKind.Local) { + Storage s; + //If v is in lc.Parameters, it is a parameter. + //Otherwise, it is a local variable. + if (lc.Parameters.Contains(v)) { + s = new ArgumentStorage(lc, v); + } else { + s = new LocalStorage(lc, v); + } + _locals.Add(v, s); + } + } + } + + private IList GetVariables() { + var vars = GetVariables(Node); + if (MergedScopes == null) { + return vars; + } + var list = new List(vars); + foreach (var block in MergedScopes) { + list.AddRange(block.Variables); + } + return list; + } + + private static ReadOnlyCollection GetVariables(Expression scope) { + var lambda = scope as LambdaExpression; + if (lambda != null) { + return lambda.Parameters; + } + return ((BlockExpression)scope).Variables; + } + + private string CurrentLambdaName { + get { + CompilerScope s = this; + while (true) { + var lambda = s.Node as LambdaExpression; + if (lambda != null) { + return lambda.Name; + } + } + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/ConstantCheck.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/ConstantCheck.cs new file mode 100644 index 0000000000..da35b6eadf --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/ConstantCheck.cs @@ -0,0 +1,180 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions { + internal enum AnalyzeTypeIsResult { + KnownFalse, + KnownTrue, + KnownAssignable, // need null check only + Unknown, // need full runtime check + } + + internal static class ConstantCheck { + + /// + /// Tests to see if the expression is a constant with the given value. + /// + /// The expression to examine + /// The constant value to check for. + /// true/false + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + internal static bool IsConstant(Expression e, bool value) { + switch (e.NodeType) { + case ExpressionType.AndAlso: + return CheckAndAlso((BinaryExpression)e, value); + + case ExpressionType.OrElse: + return CheckOrElse((BinaryExpression)e, value); + + case ExpressionType.Constant: + return value.Equals(((ConstantExpression)e).Value); + + case ExpressionType.TypeIs: + AnalyzeTypeIsResult result = AnalyzeTypeIs((TypeBinaryExpression)e); + if (value) { + return result == AnalyzeTypeIsResult.KnownTrue; + } + return result == AnalyzeTypeIsResult.KnownFalse; + } + return false; + } + + internal static bool IsNull(Expression e) { + switch (e.NodeType) { + case ExpressionType.Constant: + return ((ConstantExpression)e).Value == null; + + case ExpressionType.TypeAs: + var typeAs = (UnaryExpression)e; + // if the TypeAs check is guarenteed to fail, then its result will be null + return AnalyzeTypeIs(typeAs) == AnalyzeTypeIsResult.KnownFalse; + } + return false; + } + + + private static bool CheckAndAlso(BinaryExpression node, bool value) { + Debug.Assert(node.NodeType == ExpressionType.AndAlso); + + if (node.Method != null || node.IsLifted) { + return false; + } + + if (value) { + return IsConstant(node.Left, true) && IsConstant(node.Right, true); + } else { + // if left isn't a constant it has to be evaluated + return IsConstant(node.Left, false); + } + } + + private static bool CheckOrElse(BinaryExpression node, bool value) { + Debug.Assert(node.NodeType == ExpressionType.OrElse); + + if (node.Method != null || node.IsLifted) { + return false; + } + + if (value) { + return IsConstant(node.Left, true); + } else { + return IsConstant(node.Left, false) && IsConstant(node.Right, false); + } + } + + /// + /// If the result of a TypeBinaryExpression is known statically, this + /// returns the result, otherwise it returns null, meaning we'll need + /// to perform the IsInst instruction at runtime. + /// + /// The result of this function must be equivalent to IsInst, or + /// null. + /// + internal static AnalyzeTypeIsResult AnalyzeTypeIs(TypeBinaryExpression typeIs) { + return AnalyzeTypeIs(typeIs.Expression, typeIs.TypeOperand); + } + + /// + /// If the result of a unary TypeAs expression is known statically, this + /// returns the result, otherwise it returns null, meaning we'll need + /// to perform the IsInst instruction at runtime. + /// + /// The result of this function must be equivalent to IsInst, or + /// null. + /// + internal static AnalyzeTypeIsResult AnalyzeTypeIs(UnaryExpression typeAs) { + Debug.Assert(typeAs.NodeType == ExpressionType.TypeAs); + return AnalyzeTypeIs(typeAs.Operand, typeAs.Type); + } + + /// + /// If the result of an isinst opcode is known statically, this + /// returns the result, otherwise it returns null, meaning we'll need + /// to perform the IsInst instruction at runtime. + /// + /// The result of this function must be equivalent to IsInst, or + /// null. + /// + private static AnalyzeTypeIsResult AnalyzeTypeIs(Expression operand, Type testType) { + Type operandType = operand.Type; + + // Oddly, we allow void operands + // TODO: this is the LinqV1 behavior of TypeIs, seems bad + if (operandType == typeof(void)) { + return AnalyzeTypeIsResult.KnownFalse; + } + + // + // Type comparisons treat nullable types as if they were the + // underlying type. The reason is when you box a nullable it + // becomes a boxed value of the underlying type, or null. + // + Type nnOperandType = operandType.GetNonNullableType(); + Type nnTestType = testType.GetNonNullableType(); + + // + // See if we can determine the answer based on the static types + // + // Extensive testing showed that Type.IsAssignableFrom, + // Type.IsInstanceOfType, and the isinst instruction were all + // equivalent when used against a live object + // + if (nnTestType.IsAssignableFrom(nnOperandType)) { + // If the operand is a value type (other than nullable), we + // know the result is always true. + if (operandType.IsValueType && !operandType.IsNullableType()) { + return AnalyzeTypeIsResult.KnownTrue; + } + + // For reference/nullable types, we need to compare to null at runtime + return AnalyzeTypeIsResult.KnownAssignable; + } + + // + // If we couldn't statically assign and the type is sealed, no + // value at runtime can make isinst succeed + // + if (nnOperandType.IsSealed) { + return AnalyzeTypeIsResult.KnownFalse; + } + + // Otherwise we need a full runtime check + return AnalyzeTypeIsResult.Unknown; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/DelegateHelpers.Generated.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/DelegateHelpers.Generated.cs new file mode 100644 index 0000000000..cf1df5e6a0 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/DelegateHelpers.Generated.cs @@ -0,0 +1,275 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Dynamic.Binders; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + internal static partial class DelegateHelpers { + private static TypeInfo _DelegateCache = new TypeInfo(); + + #region Generated Maximum Delegate Arity + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_max_delegate_arity from: generate_dynsites.py + + private const int MaximumArity = 17; + + // *** END GENERATED CODE *** + + #endregion + + internal class TypeInfo { + public Type DelegateType; + public Dictionary TypeChain; + + public Type MakeDelegateType(Type retType, params Expression[] args) { + return MakeDelegateType(retType, (IList)args); + } + + public Type MakeDelegateType(Type retType, IList args) { + // nope, go ahead and create it and spend the + // cost of creating the array. + Type[] paramTypes = new Type[args.Count + 2]; + paramTypes[0] = typeof(CallSite); + paramTypes[paramTypes.Length - 1] = retType; + for (int i = 0; i < args.Count; i++) { + paramTypes[i + 1] = args[i].Type; + } + + return DelegateType = MakeDelegate(paramTypes); + } + } + + + /// + /// Finds a delegate type using the types in the array. + /// We use the cache to avoid copying the array, and to cache the + /// created delegate type + /// + internal static Type MakeDelegateType(Type[] types) { + lock (_DelegateCache) { + TypeInfo curTypeInfo = _DelegateCache; + + // arguments & return type + for (int i = 0; i < types.Length; i++) { + curTypeInfo = NextTypeInfo(types[i], curTypeInfo); + } + + // see if we have the delegate already + if (curTypeInfo.DelegateType == null) { + // clone because MakeCustomDelegate can hold onto the array. + curTypeInfo.DelegateType = MakeDelegate((Type[])types.Clone()); + } + + return curTypeInfo.DelegateType; + } + } + + /// + /// Finds a delegate type for a CallSite using the types in the ReadOnlyCollection of Expression. + /// + /// We take the ROC of Expression explicitly to avoid allocating memory (an array of types) on + /// lookup of delegate types. + /// + internal static Type MakeCallSiteDelegate(ReadOnlyCollection types, Type returnType) { + lock (_DelegateCache) { + TypeInfo curTypeInfo = _DelegateCache; + + // CallSite + curTypeInfo = NextTypeInfo(typeof(CallSite), curTypeInfo); + + // arguments + for (int i = 0; i < types.Count; i++) { + curTypeInfo = NextTypeInfo(types[i].Type, curTypeInfo); + } + + // return type + curTypeInfo = NextTypeInfo(returnType, curTypeInfo); + + // see if we have the delegate already + if (curTypeInfo.DelegateType == null) { + curTypeInfo.MakeDelegateType(returnType, types); + } + + return curTypeInfo.DelegateType; + } + } + + /// + /// Finds a delegate type for a CallSite using the MetaObject array. + /// + /// We take the array of MetaObject explicitly to avoid allocating memory (an array of types) on + /// lookup of delegate types. + /// + internal static Type MakeDeferredSiteDelegate(MetaObject[] args, Type returnType) { + lock (_DelegateCache) { + TypeInfo curTypeInfo = _DelegateCache; + + // CallSite + curTypeInfo = NextTypeInfo(typeof(CallSite), curTypeInfo); + + // arguments + for (int i = 0; i < args.Length; i++) { + MetaObject mo = args[i]; + Type paramType = mo.Expression.Type; + if (mo.IsByRef) { + paramType = paramType.MakeByRefType(); + } + curTypeInfo = NextTypeInfo(paramType, curTypeInfo); + } + + // return type + curTypeInfo = NextTypeInfo(returnType, curTypeInfo); + + // see if we have the delegate already + if (curTypeInfo.DelegateType == null) { + // nope, go ahead and create it and spend the + // cost of creating the array. + Type[] paramTypes = new Type[args.Length + 2]; + paramTypes[0] = typeof(CallSite); + paramTypes[paramTypes.Length - 1] = returnType; + for (int i = 0; i < args.Length; i++) { + MetaObject mo = args[i]; + Type paramType = mo.Expression.Type; + if (mo.IsByRef) { + paramType = paramType.MakeByRefType(); + } + paramTypes[i + 1] = paramType; + } + + curTypeInfo.DelegateType = MakeDelegate(paramTypes); + } + + return curTypeInfo.DelegateType; + } + } + + internal static TypeInfo NextTypeInfo(Type initialArg) { + lock (_DelegateCache) { + return NextTypeInfo(initialArg, _DelegateCache); + } + } + + internal static TypeInfo GetNextTypeInfo(Type initialArg, TypeInfo curTypeInfo) { + lock (_DelegateCache) { + return NextTypeInfo(initialArg, curTypeInfo); + } + } + + private static TypeInfo NextTypeInfo(Type initialArg, TypeInfo curTypeInfo) { + Type lookingUp = initialArg; + TypeInfo nextTypeInfo; + if (curTypeInfo.TypeChain == null) { + curTypeInfo.TypeChain = new Dictionary(); + } + + if (!curTypeInfo.TypeChain.TryGetValue(lookingUp, out nextTypeInfo)) { + curTypeInfo.TypeChain[lookingUp] = nextTypeInfo = new TypeInfo(); + } + return nextTypeInfo; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + internal static Type MakeDelegate(Type[] types) { + Debug.Assert(types != null && types.Length > 0); + + // Can only used predefined delegates if we have no byref types and + // the arity is small enough to fit in Func<...> or Action<...> + if (types.Length > MaximumArity || types.Any(t => t.IsByRef)) { + return MakeCustomDelegate(types); + } + + Type result; + if (types[types.Length - 1] == typeof(void)) { + result = GetActionType(types.RemoveLast()); + } else { + result = GetFuncType(types); + } + Debug.Assert(result != null); + return result; + } + + internal static Type GetFuncType(Type[] types) { + switch (types.Length) { + #region Generated Delegate Func Types + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_delegate_func from: generate_dynsites.py + + case 1: return typeof(Func<>).MakeGenericType(types); + case 2: return typeof(Func<,>).MakeGenericType(types); + case 3: return typeof(Func<,,>).MakeGenericType(types); + case 4: return typeof(Func<,,,>).MakeGenericType(types); + case 5: return typeof(Func<,,,,>).MakeGenericType(types); + case 6: return typeof(Func<,,,,,>).MakeGenericType(types); + case 7: return typeof(Func<,,,,,,>).MakeGenericType(types); + case 8: return typeof(Func<,,,,,,,>).MakeGenericType(types); + case 9: return typeof(Func<,,,,,,,,>).MakeGenericType(types); + case 10: return typeof(Func<,,,,,,,,,>).MakeGenericType(types); + case 11: return typeof(Func<,,,,,,,,,,>).MakeGenericType(types); + case 12: return typeof(Func<,,,,,,,,,,,>).MakeGenericType(types); + case 13: return typeof(Func<,,,,,,,,,,,,>).MakeGenericType(types); + case 14: return typeof(Func<,,,,,,,,,,,,,>).MakeGenericType(types); + case 15: return typeof(Func<,,,,,,,,,,,,,,>).MakeGenericType(types); + case 16: return typeof(Func<,,,,,,,,,,,,,,,>).MakeGenericType(types); + case 17: return typeof(Func<,,,,,,,,,,,,,,,,>).MakeGenericType(types); + + // *** END GENERATED CODE *** + + #endregion + + default: return null; + } + } + + internal static Type GetActionType(Type[] types) { + switch (types.Length) { + case 0: return typeof(Action); + #region Generated Delegate Action Types + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_delegate_action from: generate_dynsites.py + + case 1: return typeof(Action<>).MakeGenericType(types); + case 2: return typeof(Action<,>).MakeGenericType(types); + case 3: return typeof(Action<,,>).MakeGenericType(types); + case 4: return typeof(Action<,,,>).MakeGenericType(types); + case 5: return typeof(Action<,,,,>).MakeGenericType(types); + case 6: return typeof(Action<,,,,,>).MakeGenericType(types); + case 7: return typeof(Action<,,,,,,>).MakeGenericType(types); + case 8: return typeof(Action<,,,,,,,>).MakeGenericType(types); + case 9: return typeof(Action<,,,,,,,,>).MakeGenericType(types); + case 10: return typeof(Action<,,,,,,,,,>).MakeGenericType(types); + case 11: return typeof(Action<,,,,,,,,,,>).MakeGenericType(types); + case 12: return typeof(Action<,,,,,,,,,,,>).MakeGenericType(types); + case 13: return typeof(Action<,,,,,,,,,,,,>).MakeGenericType(types); + case 14: return typeof(Action<,,,,,,,,,,,,,>).MakeGenericType(types); + case 15: return typeof(Action<,,,,,,,,,,,,,,>).MakeGenericType(types); + case 16: return typeof(Action<,,,,,,,,,,,,,,,>).MakeGenericType(types); + + // *** END GENERATED CODE *** + + #endregion + + default: return null; + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/DelegateHelpers.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/DelegateHelpers.cs new file mode 100644 index 0000000000..f315fbc335 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/DelegateHelpers.cs @@ -0,0 +1,91 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.Dynamic.Utils; +using System.Threading; + +namespace System.Linq.Expressions.Compiler { + internal static partial class DelegateHelpers { + + // TODO: suspect we don't need this cache, since we're already caching with + // _DelegateCache + private static Dictionary, Type> _DelegateTypes; + + private static Type MakeCustomDelegate(Type[] types) { + if (_DelegateTypes == null) { + Interlocked.CompareExchange( + ref _DelegateTypes, + new Dictionary, Type>(ListEqualityComparer.Instance), + null + ); + } + + bool found; + Type type; + + // + // LOCK to retrieve the delegate type, if any + // + + lock (_DelegateTypes) { + found = _DelegateTypes.TryGetValue(types, out type); + } + + if (!found && type != null) { + return type; + } + + // + // Create new delegate type + // + + type = MakeNewCustomDelegate(types); + + // + // LOCK to insert new delegate into the cache. If we already have one (racing threads), use the one from the cache + // + + lock (_DelegateTypes) { + Type conflict; + if (_DelegateTypes.TryGetValue(types, out conflict) && conflict != null) { + type = conflict; + } else { + _DelegateTypes[types] = type; + } + } + + return type; + } + + private const MethodAttributes CtorAttributes = MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public; + private const MethodImplAttributes ImplAttributes = MethodImplAttributes.Runtime | MethodImplAttributes.Managed; + private const MethodAttributes InvokeAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; + + private static readonly Type[] _DelegateCtorSignature = new Type[] { typeof(object), typeof(IntPtr) }; + + private static Type MakeNewCustomDelegate(Type[] types) { + Type returnType = types[types.Length - 1]; + Type[] parameters = types.RemoveLast(); + + TypeBuilder builder = Snippets.Shared.DefineDelegateType("Delegate" + types.Length); + builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _DelegateCtorSignature).SetImplementationFlags(ImplAttributes); + builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes); + return builder.CreateType(); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/DynamicILGen.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/DynamicILGen.cs new file mode 100644 index 0000000000..739aa2dfb4 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/DynamicILGen.cs @@ -0,0 +1,81 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection; +using System.Reflection.Emit; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + + internal abstract class DynamicILGen : ILGen { + internal DynamicILGen(ILGenerator il) + : base(il) { + } + + internal T CreateDelegate() { + MethodInfo mi; + return CreateDelegate(out mi); + } + + internal abstract T CreateDelegate(out MethodInfo mi); + + internal abstract MethodInfo Finish(); + } + + internal class DynamicILGenMethod : DynamicILGen { + private readonly DynamicMethod _dm; + + internal DynamicILGenMethod(DynamicMethod dm, ILGenerator il) + : base(il) { + _dm = dm; + } + + internal override T CreateDelegate(out MethodInfo mi) { + ContractUtils.Requires(typeof(T).IsSubclassOf(typeof(Delegate)), "T"); + mi = _dm; + return (T)(object)_dm.CreateDelegate(typeof(T), null); + } + + internal override MethodInfo Finish() { + return _dm; + } + } + + internal class DynamicILGenType : DynamicILGen { + private readonly TypeBuilder _tb; + private readonly MethodBuilder _mb; + + internal DynamicILGenType(TypeBuilder tb, MethodBuilder mb, ILGenerator il) + : base(il) { + _tb = tb; + _mb = mb; + } + + internal override T CreateDelegate(out MethodInfo mi) { + ContractUtils.Requires(typeof(T).IsSubclassOf(typeof(Delegate)), "T"); + mi = CreateMethod(); + return (T)(object)Delegate.CreateDelegate(typeof(T), mi); + } + + private MethodInfo CreateMethod() { + Type t = _tb.CreateType(); + return t.GetMethod(_mb.Name); + } + + internal override MethodInfo Finish() { + return CreateMethod(); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/ExpressionQuoter.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/ExpressionQuoter.cs new file mode 100644 index 0000000000..889dfee7af --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/ExpressionQuoter.cs @@ -0,0 +1,256 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Linq.Expressions.Compiler; +using System.Dynamic.Utils; + +namespace System.Runtime.CompilerServices { + public partial class RuntimeOps { + [Obsolete("do not call this method", true)] + public static Expression Quote(Expression expression, object hoistedLocals, object[] locals) { + Debug.Assert(hoistedLocals != null && locals != null); + var quoter = new ExpressionQuoter((HoistedLocals)hoistedLocals, locals); + return quoter.Visit(expression); + } + + [Obsolete("do not call this method", true)] + public static IList MergeRuntimeVariables(IList first, IList second, int[] indexes) { + return new MergedRuntimeVariables(first, second, indexes); + } + + // Modifies a quoted Expression instance by changing hoisted variables and + // parameters into hoisted local references. The variable's StrongBox is + // burned as a constant, and all hoisted variables/parameters are rewritten + // as indexing expressions. + // + // The behavior of Quote is indended to be like C# and VB expression quoting + private sealed class ExpressionQuoter : ExpressionVisitor { + private readonly HoistedLocals _scope; + private readonly object[] _locals; + + // A stack of variables that are defined in nested scopes. We search + // this first when resolving a variable in case a nested scope shadows + // one of our variable instances. + // + // TODO: should HoistedLocals track shadowing so we don't need to worry + // about it here? + private readonly Stack> _hiddenVars = new Stack>(); + + internal ExpressionQuoter(HoistedLocals scope, object[] locals) { + _scope = scope; + _locals = locals; + } + + protected internal override Expression VisitLambda(Expression node) { + _hiddenVars.Push(new Set(node.Parameters)); + Expression b = Visit(node.Body); + _hiddenVars.Pop(); + if (b == node.Body) { + return node; + } + return Expression.Lambda(b, node.Name, node.Parameters); + } + + protected internal override Expression VisitBlock(BlockExpression node) { + _hiddenVars.Push(new Set(node.Variables)); + var b = Visit(node.Expressions); + _hiddenVars.Pop(); + if (b == node.Expressions) { + return node; + } + return Expression.Block(node.Variables, b); + } + + protected override CatchBlock VisitCatchBlock(CatchBlock node) { + _hiddenVars.Push(new Set(new[] { node.Variable })); + Expression b = Visit(node.Body); + Expression f = Visit(node.Filter); + _hiddenVars.Pop(); + if (b == node.Body && f == node.Filter) { + return node; + } + return Expression.MakeCatchBlock(node.Test, node.Variable, b, f); + } + + protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) { + int count = node.Variables.Count; + var boxes = new List(); + var vars = new List(); + var indexes = new int[count]; + for (int i = 0; i < count; i++) { + IStrongBox box = GetBox(node.Variables[i]); + if (box == null) { + indexes[i] = vars.Count; + vars.Add(node.Variables[i]); + } else { + indexes[i] = -1 - boxes.Count; + boxes.Add(box); + } + } + + // No variables were rewritten. Just return the original node + if (boxes.Count == 0) { + return node; + } + + var boxesConst = Expression.Constant(new ReadOnlyCollection(boxes.ToArray())); + // All of them were rewritten. Just return the array as a constant + if (vars.Count == 0) { + return boxesConst; + } + + // Otherwise, we need to return an object that merges them + return Expression.Call( + typeof(RuntimeOps).GetMethod("MergeRuntimeVariables"), + Expression.RuntimeVariables(new ReadOnlyCollection(vars.ToArray())), + boxesConst, + Expression.Constant(indexes) + ); + } + + protected internal override Expression VisitParameter(ParameterExpression node) { + IStrongBox box = GetBox(node); + if (box == null) { + return node; + } + return Expression.Field(Expression.Constant(box), "Value"); + } + + private IStrongBox GetBox(ParameterExpression variable) { + // Skip variables that are shadowed by a nested scope/lambda + foreach (Set hidden in _hiddenVars) { + if (hidden.Contains(variable)) { + return null; + } + } + + HoistedLocals scope = _scope; + object[] locals = _locals; + while (true) { + int hoistIndex; + if (scope.Indexes.TryGetValue(variable, out hoistIndex)) { + return (IStrongBox)locals[hoistIndex]; + } + scope = scope.Parent; + if (scope == null) { + break; + } + locals = HoistedLocals.GetParent(locals); + } + + // TODO: this should be unreachable because it's an unbound + // variable, so we should throw here. It's a breaking change, + // however + return null; + } + } + + + /// + /// Provides a list of variables, supporing read/write of the values + /// Exposed via RuntimeVariablesExpression + /// + private sealed class MergedRuntimeVariables : IList { + private readonly IList _first; + private readonly IList _second; + + // For reach item, the index into the first or second list + // Positive values mean the first array, negative means the second + private readonly int[] _indexes; + + internal MergedRuntimeVariables(IList first, IList second, int[] indexes) { + _first = first; + _second = second; + _indexes = indexes; + } + + public int Count { + get { return _indexes.Length; } + } + + public IStrongBox this[int index] { + get { + index = _indexes[index]; + return (index >= 0) ? _first[index] : _second[-1 - index]; + } + set { + throw Error.CollectionReadOnly(); + } + } + + public int IndexOf(IStrongBox item) { + for (int i = 0, n = _indexes.Length; i < n; i++) { + if (this[i] == item) { + return i; + } + } + return -1; + } + + public bool Contains(IStrongBox item) { + return IndexOf(item) >= 0; + } + + public void CopyTo(IStrongBox[] array, int arrayIndex) { + ContractUtils.RequiresNotNull(array, "array"); + int count = _indexes.Length; + if (arrayIndex < 0 || arrayIndex + count > array.Length) { + throw new ArgumentOutOfRangeException("arrayIndex"); + } + for (int i = 0; i < count; i++) { + array[arrayIndex++] = this[i]; + } + } + + bool ICollection.IsReadOnly { + get { return true; } + } + + public IEnumerator GetEnumerator() { + for (int i = 0, n = _indexes.Length; i < n; i++) { + yield return this[i]; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + void IList.Insert(int index, IStrongBox item) { + throw Error.CollectionReadOnly(); + } + + void IList.RemoveAt(int index) { + throw Error.CollectionReadOnly(); + } + + void ICollection.Add(IStrongBox item) { + throw Error.CollectionReadOnly(); + } + + void ICollection.Clear() { + throw Error.CollectionReadOnly(); + } + + bool ICollection.Remove(IStrongBox item) { + throw Error.CollectionReadOnly(); + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/HoistedLocals.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/HoistedLocals.cs new file mode 100644 index 0000000000..9b9385aee4 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/HoistedLocals.cs @@ -0,0 +1,97 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + + // Suppose we have something like: + // + // (string s)=>()=>s. + // + // We wish to generate the outer as: + // + // Func OuterMethod(Closure closure, string s) + // { + // object[] locals = new object[1]; + // locals[0] = new StrongBox(); + // ((StrongBox)locals[0]).Value = s; + // return ((DynamicMethod)closure.Constants[0]).CreateDelegate(typeof(Func), new Closure(null, locals)); + // } + // + // ... and the inner as: + // + // string InnerMethod(Closure closure) + // { + // object[] locals = closure.Locals; + // return ((StrongBox)locals[0]).Value; + // } + // + // This class tracks that "s" was hoisted into a closure, as the 0th + // element in the array + // + /// + /// Stores information about locals and arguments that are hoisted into + /// the closure array because they're referenced in an inner lambda. + /// + /// This class is sometimes emitted as a runtime constant for internal + /// use to hoist variables/parameters in quoted expressions + /// + /// Invariant: this class stores no mutable state + /// + internal sealed class HoistedLocals { + + // The parent locals, if any + internal readonly HoistedLocals Parent; + + // A mapping of hoisted variables to their indexes in the array + internal readonly ReadOnlyDictionary Indexes; + + // The variables, in the order they appear in the array + internal readonly ReadOnlyCollection Variables; + + // A virtual variable for accessing this locals array + internal readonly ParameterExpression SelfVariable; + + internal HoistedLocals(HoistedLocals parent, ReadOnlyCollection vars) { + + if (parent != null) { + // Add the parent locals array as the 0th element in the array + vars = new ReadOnlyCollection(vars.AddFirst(parent.SelfVariable)); + } + + Dictionary indexes = new Dictionary(vars.Count); + for (int i = 0; i < vars.Count; i++) { + indexes.Add(vars[i], i); + } + + SelfVariable = Expression.Variable(typeof(object[]), null); + Parent = parent; + Variables = vars; + Indexes = new ReadOnlyDictionary(indexes); + } + + internal ParameterExpression ParentVariable { + get { return Parent != null ? Parent.SelfVariable : null; } + } + + internal static object[] GetParent(object[] locals) { + return ((StrongBox)locals[0]).Value; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/ILGen.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/ILGen.cs new file mode 100644 index 0000000000..7e615b0d3f --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/ILGen.cs @@ -0,0 +1,1432 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.SymbolStore; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + + // TODO: change to extension methods for ILGenerator + internal class ILGen { + private readonly ILGenerator _ilg; + private readonly KeyedQueue _freeLocals = new KeyedQueue(); + + // TODO: remove Python dependency + internal ILGen(ILGenerator ilg) { + ContractUtils.RequiresNotNull(ilg, "ilg"); + + _ilg = ilg; + } + + #region ILGenerator Methods + + /// + /// Begins a catch block. + /// + internal void BeginCatchBlock(Type exceptionType) { + _ilg.BeginCatchBlock(exceptionType); + } + + /// + /// Begins an exception block for a filtered exception. + /// + internal void BeginExceptFilterBlock() { + _ilg.BeginExceptFilterBlock(); + } + + /// + /// Begins an exception block for a non-filtered exception. + /// + /// + internal Label BeginExceptionBlock() { + return _ilg.BeginExceptionBlock(); + } + + /// + /// Begins an exception fault block + /// + internal void BeginFaultBlock() { + _ilg.BeginFaultBlock(); + } + + /// + /// Begins a finally block + /// + internal void BeginFinallyBlock() { + _ilg.BeginFinallyBlock(); + } + + /// + /// Ends an exception block. + /// + internal void EndExceptionBlock() { + _ilg.EndExceptionBlock(); + } + + /// + /// Begins a lexical scope. + /// + internal void BeginScope() { + _ilg.BeginScope(); + } + + /// + /// Ends a lexical scope. + /// + internal void EndScope() { + _ilg.EndScope(); + } + + /// + /// Declares a local variable of the specified type. + /// + internal LocalBuilder DeclareLocal(Type localType) { + return _ilg.DeclareLocal(localType); + } + + /// + /// Declares a local variable of the specified type, optionally + /// pinning the object referred to by the variable. + /// + internal LocalBuilder DeclareLocal(Type localType, bool pinned) { + return _ilg.DeclareLocal(localType, pinned); + } + + /// + /// Declares a new label. + /// + internal Label DefineLabel() { + return _ilg.DefineLabel(); + } + + /// + /// Marks the label at the current position. + /// + internal void MarkLabel(Label loc) { + _ilg.MarkLabel(loc); + } + + /// + /// Emits an instruction. + /// + internal void Emit(OpCode opcode) { + _ilg.Emit(opcode); + } + + /// + /// Emits an instruction with a byte argument. + /// + internal void Emit(OpCode opcode, byte arg) { + _ilg.Emit(opcode, arg); + } + + /// + /// Emits an instruction with the metadata token for the specified contructor. + /// + internal void Emit(OpCode opcode, ConstructorInfo con) { + _ilg.Emit(opcode, con); + } + + /// + /// Emits an instruction with a double argument. + /// + internal void Emit(OpCode opcode, double arg) { + _ilg.Emit(opcode, arg); + } + + /// + /// Emits an instruction with the metadata token for the specified field. + /// + internal void Emit(OpCode opcode, FieldInfo field) { + _ilg.Emit(opcode, field); + } + + /// + /// Emits an instruction with a float argument. + /// + internal void Emit(OpCode opcode, float arg) { + _ilg.Emit(opcode, arg); + } + + /// + /// Emits an instruction with an int argument. + /// + internal void Emit(OpCode opcode, int arg) { + _ilg.Emit(opcode, arg); + } + + /// + /// Emits an instruction with a label argument. + /// + internal void Emit(OpCode opcode, Label label) { + _ilg.Emit(opcode, label); + } + + /// + /// Emits an instruction with multiple target labels (switch). + /// + internal void Emit(OpCode opcode, Label[] labels) { + _ilg.Emit(opcode, labels); + } + + /// + /// Emits an instruction with a reference to a local variable. + /// + internal void Emit(OpCode opcode, LocalBuilder local) { + _ilg.Emit(opcode, local); + } + + /// + /// Emits an instruction with a long argument. + /// + internal void Emit(OpCode opcode, long arg) { + _ilg.Emit(opcode, arg); + } + + /// + /// Emits an instruction with the metadata token for a specified method. + /// + internal void Emit(OpCode opcode, MethodInfo meth) { + _ilg.Emit(opcode, meth); + } + + /// + /// Emits an instruction with a signed byte argument. + /// + internal void Emit(OpCode opcode, sbyte arg) { + _ilg.Emit(opcode, arg); + } + + /// + /// Emits an instruction with a short argument. + /// + internal void Emit(OpCode opcode, short arg) { + _ilg.Emit(opcode, arg); + } + +#if !SILVERLIGHT + /// + /// Emits an instruction with a signature token. + /// + internal void Emit(OpCode opcode, SignatureHelper signature) { + _ilg.Emit(opcode, signature); + } +#endif + + /// + /// Emits an instruction with a string argument. + /// + internal void Emit(OpCode opcode, string str) { + _ilg.Emit(opcode, str); + } + + /// + /// Emits an instruction with the metadata token for a specified type argument. + /// + internal void Emit(OpCode opcode, Type cls) { + _ilg.Emit(opcode, cls); + } + + /// + /// Emits a call or a virtual call to the varargs method. + /// + internal void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[] optionalParameterTypes) { + _ilg.EmitCall(opcode, methodInfo, optionalParameterTypes); + } + +#if !SILVERLIGHT + /// + /// Emits an unmanaged indirect call instruction. + /// + internal void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes) { + _ilg.EmitCalli(opcode, unmanagedCallConv, returnType, parameterTypes); + } + + /// + /// Emits a managed indirect call instruction. + /// + internal void EmitCalli(OpCode opcode, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes) { + _ilg.EmitCalli(opcode, callingConvention, returnType, parameterTypes, optionalParameterTypes); + } +#endif + + /// + /// Marks a sequence point. + /// + internal void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) { + _ilg.MarkSequencePoint(document, startLine, startColumn, endLine, endColumn); + } + + /// + /// Specifies the namespace to be used in evaluating locals and watches for the + /// current active lexical scope. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames")] // TODO: fix + internal void UsingNamespace(string usingNamespace) { + _ilg.UsingNamespace(usingNamespace); + } + + #endregion + + #region Simple helpers + + internal void Emit(OpCode opcode, MethodBase methodBase) { + Debug.Assert(methodBase is MethodInfo || methodBase is ConstructorInfo); + + if (methodBase.MemberType == MemberTypes.Constructor) { + Emit(opcode, (ConstructorInfo)methodBase); + } else { + Emit(opcode, (MethodInfo)methodBase); + } + } + + #endregion + + #region Instruction helpers + + internal void EmitLoadArg(int index) { + ContractUtils.Requires(index >= 0, "index"); + + switch (index) { + case 0: + Emit(OpCodes.Ldarg_0); + break; + case 1: + Emit(OpCodes.Ldarg_1); + break; + case 2: + Emit(OpCodes.Ldarg_2); + break; + case 3: + Emit(OpCodes.Ldarg_3); + break; + default: + if (index <= Byte.MaxValue) { + Emit(OpCodes.Ldarg_S, (byte)index); + } else { + this.Emit(OpCodes.Ldarg, index); + } + break; + } + } + + internal void EmitLoadArgAddress(int index) { + ContractUtils.Requires(index >= 0, "index"); + + if (index <= Byte.MaxValue) { + Emit(OpCodes.Ldarga_S, (byte)index); + } else { + Emit(OpCodes.Ldarga, index); + } + } + + internal void EmitStoreArg(int index) { + ContractUtils.Requires(index >= 0, "index"); + + if (index <= Byte.MaxValue) { + Emit(OpCodes.Starg_S, (byte)index); + } else { + Emit(OpCodes.Starg, index); + } + } + + /// + /// Emits a Ldind* instruction for the appropriate type + /// + internal void EmitLoadValueIndirect(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + if (type.IsValueType) { + if (type == typeof(int)) { + Emit(OpCodes.Ldind_I4); + } else if (type == typeof(uint)) { + Emit(OpCodes.Ldind_U4); + } else if (type == typeof(short)) { + Emit(OpCodes.Ldind_I2); + } else if (type == typeof(ushort)) { + Emit(OpCodes.Ldind_U2); + } else if (type == typeof(long) || type == typeof(ulong)) { + Emit(OpCodes.Ldind_I8); + } else if (type == typeof(char)) { + Emit(OpCodes.Ldind_I2); + } else if (type == typeof(bool)) { + Emit(OpCodes.Ldind_I1); + } else if (type == typeof(float)) { + Emit(OpCodes.Ldind_R4); + } else if (type == typeof(double)) { + Emit(OpCodes.Ldind_R8); + } else { + Emit(OpCodes.Ldobj, type); + } + } else { + Emit(OpCodes.Ldind_Ref); + } + } + + + /// + /// Emits a Stind* instruction for the appropriate type. + /// + internal void EmitStoreValueIndirect(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + if (type.IsValueType) { + if (type == typeof(int)) { + Emit(OpCodes.Stind_I4); + } else if (type == typeof(short)) { + Emit(OpCodes.Stind_I2); + } else if (type == typeof(long) || type == typeof(ulong)) { + Emit(OpCodes.Stind_I8); + } else if (type == typeof(char)) { + Emit(OpCodes.Stind_I2); + } else if (type == typeof(bool)) { + Emit(OpCodes.Stind_I1); + } else if (type == typeof(float)) { + Emit(OpCodes.Stind_R4); + } else if (type == typeof(double)) { + Emit(OpCodes.Stind_R8); + } else { + Emit(OpCodes.Stobj, type); + } + } else { + Emit(OpCodes.Stind_Ref); + } + } + + // Emits the Ldelem* instruction for the appropriate type + //CONFORMING + internal void EmitLoadElement(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + if (!type.IsValueType) { + Emit(OpCodes.Ldelem_Ref); + } else if (type.IsEnum) { + Emit(OpCodes.Ldelem, type); + } else { + switch (Type.GetTypeCode(type)) { + case TypeCode.Boolean: + case TypeCode.SByte: + Emit(OpCodes.Ldelem_I1); + break; + case TypeCode.Byte: + Emit(OpCodes.Ldelem_U1); + break; + case TypeCode.Int16: + Emit(OpCodes.Ldelem_I2); + break; + case TypeCode.Char: + case TypeCode.UInt16: + Emit(OpCodes.Ldelem_U2); + break; + case TypeCode.Int32: + Emit(OpCodes.Ldelem_I4); + break; + case TypeCode.UInt32: + Emit(OpCodes.Ldelem_U4); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + Emit(OpCodes.Ldelem_I8); + break; + case TypeCode.Single: + Emit(OpCodes.Ldelem_R4); + break; + case TypeCode.Double: + Emit(OpCodes.Ldelem_R8); + break; + default: + Emit(OpCodes.Ldelem, type); + break; + } + } + } + + /// + /// Emits a Stelem* instruction for the appropriate type. + /// + internal void EmitStoreElement(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + if (type.IsEnum) { + Emit(OpCodes.Stelem, type); + return; + } + switch (Type.GetTypeCode(type)) { + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Byte: + Emit(OpCodes.Stelem_I1); + break; + case TypeCode.Char: + case TypeCode.Int16: + case TypeCode.UInt16: + Emit(OpCodes.Stelem_I2); + break; + case TypeCode.Int32: + case TypeCode.UInt32: + Emit(OpCodes.Stelem_I4); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + Emit(OpCodes.Stelem_I8); + break; + case TypeCode.Single: + Emit(OpCodes.Stelem_R4); + break; + case TypeCode.Double: + Emit(OpCodes.Stelem_R8); + break; + default: + if (type.IsValueType) { + Emit(OpCodes.Stelem, type); + } else { + Emit(OpCodes.Stelem_Ref); + } + break; + } + } + + internal void EmitType(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + Emit(OpCodes.Ldtoken, type); + EmitCall(typeof(Type), "GetTypeFromHandle"); + } + + internal void EmitUnbox(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + Emit(OpCodes.Unbox_Any, type); + } + + #endregion + + #region Fields, properties and methods + + internal void EmitFieldAddress(FieldInfo fi) { + ContractUtils.RequiresNotNull(fi, "fi"); + + if (fi.IsStatic) { + Emit(OpCodes.Ldsflda, fi); + } else { + Emit(OpCodes.Ldflda, fi); + } + } + + internal void EmitFieldGet(FieldInfo fi) { + ContractUtils.RequiresNotNull(fi, "fi"); + + if (fi.IsStatic) { + Emit(OpCodes.Ldsfld, fi); + } else { + Emit(OpCodes.Ldfld, fi); + } + } + + internal void EmitFieldSet(FieldInfo fi) { + ContractUtils.RequiresNotNull(fi, "fi"); + + if (fi.IsStatic) { + Emit(OpCodes.Stsfld, fi); + } else { + Emit(OpCodes.Stfld, fi); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")] + internal void EmitNew(ConstructorInfo ci) { + ContractUtils.RequiresNotNull(ci, "ci"); + + if (ci.DeclaringType.ContainsGenericParameters) { + throw Error.IllegalNewGenericParams(ci.DeclaringType); + } + + Emit(OpCodes.Newobj, ci); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")] + internal void EmitNew(Type type, Type[] paramTypes) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(paramTypes, "paramTypes"); + + ConstructorInfo ci = type.GetConstructor(paramTypes); + ContractUtils.Requires(ci != null, "type", Strings.TypeDoesNotHaveConstructorForTheSignature); + EmitNew(ci); + } + + internal void EmitCall(MethodInfo mi) { + ContractUtils.RequiresNotNull(mi, "mi"); + + if (mi.IsVirtual && !mi.DeclaringType.IsValueType) { + Emit(OpCodes.Callvirt, mi); + } else { + Emit(OpCodes.Call, mi); + } + } + + internal void EmitCall(Type type, String name) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(name, "name"); + + MethodInfo mi = type.GetMethod(name); + ContractUtils.Requires(mi != null, "type", Strings.TypeDoesNotHaveMethodForName); + + EmitCall(mi); + } + + internal void EmitCall(Type type, String name, Type[] paramTypes) { + ContractUtils.RequiresNotNull(type, "type"); + ContractUtils.RequiresNotNull(name, "name"); + ContractUtils.RequiresNotNull(paramTypes, "paramTypes"); + + MethodInfo mi = type.GetMethod(name, paramTypes); + ContractUtils.Requires(mi != null, "type", Strings.TypeDoesNotHaveMethodForNameSignature); + + EmitCall(mi); + } + + #endregion + + #region Constants + + internal void EmitNull() { + Emit(OpCodes.Ldnull); + } + + internal void EmitString(string value) { + ContractUtils.RequiresNotNull(value, "value"); + Emit(OpCodes.Ldstr, value); + } + + internal void EmitBoolean(bool value) { + if (value) { + Emit(OpCodes.Ldc_I4_1); + } else { + Emit(OpCodes.Ldc_I4_0); + } + } + + internal void EmitChar(char value) { + EmitInt(value); + Emit(OpCodes.Conv_U2); + } + + internal void EmitByte(byte value) { + EmitInt(value); + Emit(OpCodes.Conv_U1); + } + + internal void EmitSByte(sbyte value) { + EmitInt(value); + Emit(OpCodes.Conv_I1); + } + + internal void EmitShort(short value) { + EmitInt(value); + Emit(OpCodes.Conv_I2); + } + + internal void EmitUShort(ushort value) { + EmitInt(value); + Emit(OpCodes.Conv_U2); + } + + internal void EmitInt(int value) { + OpCode c; + switch (value) { + case -1: + c = OpCodes.Ldc_I4_M1; + break; + case 0: + c = OpCodes.Ldc_I4_0; + break; + case 1: + c = OpCodes.Ldc_I4_1; + break; + case 2: + c = OpCodes.Ldc_I4_2; + break; + case 3: + c = OpCodes.Ldc_I4_3; + break; + case 4: + c = OpCodes.Ldc_I4_4; + break; + case 5: + c = OpCodes.Ldc_I4_5; + break; + case 6: + c = OpCodes.Ldc_I4_6; + break; + case 7: + c = OpCodes.Ldc_I4_7; + break; + case 8: + c = OpCodes.Ldc_I4_8; + break; + default: + if (value >= -128 && value <= 127) { + Emit(OpCodes.Ldc_I4_S, (sbyte)value); + } else { + Emit(OpCodes.Ldc_I4, value); + } + return; + } + Emit(c); + } + + internal void EmitUInt(uint value) { + EmitInt((int)value); + Emit(OpCodes.Conv_U4); + } + + internal void EmitLong(long value) { + Emit(OpCodes.Ldc_I8, value); + } + + internal void EmitULong(ulong value) { + Emit(OpCodes.Ldc_I8, (long)value); + Emit(OpCodes.Conv_U8); + } + + internal void EmitDouble(double value) { + Emit(OpCodes.Ldc_R8, value); + } + + internal void EmitSingle(float value) { + Emit(OpCodes.Ldc_R4, value); + } + + // matches TryEmitConstant + internal static bool CanEmitConstant(object value, Type type) { + if (value == null || CanEmitILConstant(type)) { + return true; + } + + Type t = value as Type; + if (t != null && ShouldLdtoken(t)) { + return true; + } + + MethodBase mb = value as MethodBase; + if (mb != null && ShouldLdtoken(mb)) { + return true; + } + + return false; + } + + // matches TryEmitILConstant + private static bool CanEmitILConstant(Type type) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Char: + case TypeCode.Byte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + case TypeCode.Decimal: + case TypeCode.String: + return true; + } + return false; + } + + //CONFORMING + // + // Note: we support emitting more things as IL constants than + // Linq does + internal void EmitConstant(object value, Type type) { + if (value == null) { + // Smarter than the Linq implementation which uses the initobj + // pattern for all value types (works, but requires a local and + // more IL) + EmitDefault(type); + return; + } + + // Handle the easy cases + if (TryEmitILConstant(value, type)) { + return; + } + + // Check for a few more types that we support emitting as constants + Type t = value as Type; + if (t != null && ShouldLdtoken(t)) { + EmitType(t); + return; + } + + MethodBase mb = value as MethodBase; + if (mb != null && ShouldLdtoken(mb)) { + Emit(OpCodes.Ldtoken, mb); + Type dt = mb.DeclaringType; + if (dt != null && dt.IsGenericType) { + Emit(OpCodes.Ldtoken, dt); + EmitCall(typeof(MethodBase).GetMethod("GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle) })); + } else { + EmitCall(typeof(MethodBase).GetMethod("GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle) })); + } + type = TypeUtils.GetConstantType(type); + if (type != typeof(MethodBase)) { + Emit(OpCodes.Castclass, type); + } + return; + } + + throw Assert.Unreachable; + } + + // TODO: Can we always ldtoken and let restrictedSkipVisibility sort things out? + internal static bool ShouldLdtoken(Type t) { + return t is TypeBuilder || t.IsGenericParameter || t.IsVisible; + } + + internal static bool ShouldLdtoken(MethodBase mb) { + // Can't ldtoken on a DynamicMethod + if (mb is DynamicMethod) { + return false; + } + + Type dt = mb.DeclaringType; + return dt == null || ShouldLdtoken(dt); + } + + //CONFORMING + private bool TryEmitILConstant(object value, Type type) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Boolean: + EmitBoolean((bool)value); + return true; + case TypeCode.SByte: + EmitSByte((sbyte)value); + return true; + case TypeCode.Int16: + EmitShort((short)value); + return true; + case TypeCode.Int32: + EmitInt((int)value); + return true; + case TypeCode.Int64: + EmitLong((long)value); + return true; + case TypeCode.Single: + EmitSingle((float)value); + return true; + case TypeCode.Double: + EmitDouble((double)value); + return true; + case TypeCode.Char: + EmitChar((char)value); + return true; + case TypeCode.Byte: + EmitByte((byte)value); + return true; + case TypeCode.UInt16: + EmitUShort((ushort)value); + return true; + case TypeCode.UInt32: + EmitUInt((uint)value); + return true; + case TypeCode.UInt64: + EmitULong((ulong)value); + return true; + case TypeCode.Decimal: + EmitDecimal((decimal)value); + return true; + case TypeCode.String: + EmitString((string)value); + return true; + default: + return false; + } + } + + #endregion + + /// + /// Boxes the value of the stack. No-op for reference types. Void is + /// converted to a null reference. For almost all value types this + /// method will box them in the standard way. Int32 and Boolean are + /// handled with optimized conversions that reuse the same object for + /// small values. For Int32 this is purely a performance optimization. + /// For Boolean this is use to ensure that True and False are always + /// the same objects. + /// + /// TODO: do we still need this? + /// + internal void EmitBoxing(Type type) { + ContractUtils.RequiresNotNull(type, "type"); + + if (type.IsValueType) { + if (type == typeof(void)) { + Emit(OpCodes.Ldnull); + } else if (type == typeof(int)) { + EmitCall(typeof(RuntimeOps), "Int32ToObject"); + } else if (type == typeof(bool)) { + var label = DefineLabel(); + var end = DefineLabel(); + Emit(OpCodes.Brtrue_S, label); + Emit(OpCodes.Ldsfld, typeof(RuntimeOps).GetField("False")); + Emit(OpCodes.Br_S, end); + MarkLabel(label); + Emit(OpCodes.Ldsfld, typeof(RuntimeOps).GetField("True")); + MarkLabel(end); + } else { + Emit(OpCodes.Box, type); + } + } + } + + #region Linq Conversions + + //CONFORMING + // (plus support for None, Void conversions) + internal void EmitConvertToType(Type typeFrom, Type typeTo, bool isChecked) { + typeFrom = TypeUtils.GetNonNoneType(typeFrom); + + if (typeFrom == typeTo) { + return; + } + + // void -> non-void: default(T) + if (typeFrom == typeof(void)) { + EmitDefault(typeTo); + return; + } + + // non-void -> void: pop + if (typeTo == typeof(void)) { + Emit(OpCodes.Pop); + return; + } + + bool isTypeFromNullable = TypeUtils.IsNullableType(typeFrom); + bool isTypeToNullable = TypeUtils.IsNullableType(typeTo); + + Type nnExprType = TypeUtils.GetNonNullableType(typeFrom); + Type nnType = TypeUtils.GetNonNullableType(typeTo); + + if (typeFrom.IsInterface || // interface cast + typeTo.IsInterface || + typeFrom == typeof(object) || // boxing cast + typeTo == typeof(object)) { + EmitCastToType(typeFrom, typeTo); + } else if (isTypeFromNullable || isTypeToNullable) { + EmitNullableConversion(typeFrom, typeTo, isChecked); + } else if (!(TypeUtils.IsConvertible(typeFrom) && TypeUtils.IsConvertible(typeTo)) // primitive runtime conversion + && + (nnExprType.IsAssignableFrom(nnType) || // down cast + nnType.IsAssignableFrom(nnExprType))) // up cast + { + EmitCastToType(typeFrom, typeTo); + } else if (typeFrom.IsArray && typeTo.IsArray) { + // See DevDiv Bugs #94657. + EmitCastToType(typeFrom, typeTo); + } else { + EmitNumericConversion(typeFrom, typeTo, isChecked); + } + } + + //CONFORMING + private void EmitCastToType(Type typeFrom, Type typeTo) { + if (!typeFrom.IsValueType && typeTo.IsValueType) { + Emit(OpCodes.Unbox_Any, typeTo); + } else if (typeFrom.IsValueType && !typeTo.IsValueType) { + EmitBoxing(typeFrom); + if (typeTo != typeof(object)) { + Emit(OpCodes.Castclass, typeTo); + } + } else if (!typeFrom.IsValueType && !typeTo.IsValueType) { + Emit(OpCodes.Castclass, typeTo); + } else { + throw Error.InvalidCast(typeFrom, typeTo); + } + } + + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private void EmitNumericConversion(Type typeFrom, Type typeTo, bool isChecked) { + bool isFromUnsigned = TypeUtils.IsUnsigned(typeFrom); + bool isFromFloatingPoint = TypeUtils.IsFloatingPoint(typeFrom); + if (typeTo == typeof(Single)) { + if (isFromUnsigned) + Emit(OpCodes.Conv_R_Un); + Emit(OpCodes.Conv_R4); + } else if (typeTo == typeof(Double)) { + if (isFromUnsigned) + Emit(OpCodes.Conv_R_Un); + Emit(OpCodes.Conv_R8); + } else { + TypeCode tc = Type.GetTypeCode(typeTo); + if (isChecked) { + if (isFromUnsigned) { + switch (tc) { + case TypeCode.SByte: + Emit(OpCodes.Conv_Ovf_I1_Un); + break; + case TypeCode.Int16: + Emit(OpCodes.Conv_Ovf_I2_Un); + break; + case TypeCode.Int32: + Emit(OpCodes.Conv_Ovf_I4_Un); + break; + case TypeCode.Int64: + Emit(OpCodes.Conv_Ovf_I8_Un); + break; + case TypeCode.Byte: + Emit(OpCodes.Conv_Ovf_U1_Un); + break; + case TypeCode.UInt16: + case TypeCode.Char: + Emit(OpCodes.Conv_Ovf_U2_Un); + break; + case TypeCode.UInt32: + Emit(OpCodes.Conv_Ovf_U4_Un); + break; + case TypeCode.UInt64: + Emit(OpCodes.Conv_Ovf_U8_Un); + break; + default: + throw Error.UnhandledConvert(typeTo); + } + } else { + switch (tc) { + case TypeCode.SByte: + Emit(OpCodes.Conv_Ovf_I1); + break; + case TypeCode.Int16: + Emit(OpCodes.Conv_Ovf_I2); + break; + case TypeCode.Int32: + Emit(OpCodes.Conv_Ovf_I4); + break; + case TypeCode.Int64: + Emit(OpCodes.Conv_Ovf_I8); + break; + case TypeCode.Byte: + Emit(OpCodes.Conv_Ovf_U1); + break; + case TypeCode.UInt16: + case TypeCode.Char: + Emit(OpCodes.Conv_Ovf_U2); + break; + case TypeCode.UInt32: + Emit(OpCodes.Conv_Ovf_U4); + break; + case TypeCode.UInt64: + Emit(OpCodes.Conv_Ovf_U8); + break; + default: + throw Error.UnhandledConvert(typeTo); + } + } + } else { + if (isFromUnsigned) { + switch (tc) { + case TypeCode.SByte: + case TypeCode.Byte: + Emit(OpCodes.Conv_U1); + break; + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Char: + Emit(OpCodes.Conv_U2); + break; + case TypeCode.Int32: + case TypeCode.UInt32: + Emit(OpCodes.Conv_U4); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + Emit(OpCodes.Conv_U8); + break; + default: + throw Error.UnhandledConvert(typeTo); + } + } else { + switch (tc) { + case TypeCode.SByte: + case TypeCode.Byte: + Emit(OpCodes.Conv_I1); + break; + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Char: + Emit(OpCodes.Conv_I2); + break; + case TypeCode.Int32: + case TypeCode.UInt32: + Emit(OpCodes.Conv_I4); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + Emit(OpCodes.Conv_I8); + break; + default: + throw Error.UnhandledConvert(typeTo); + } + } + } + } + } + + //CONFORMING + private void EmitNullableToNullableConversion(Type typeFrom, Type typeTo, bool isChecked) { + Debug.Assert(TypeUtils.IsNullableType(typeFrom)); + Debug.Assert(TypeUtils.IsNullableType(typeTo)); + Label labIfNull = default(Label); + Label labEnd = default(Label); + LocalBuilder locFrom = null; + LocalBuilder locTo = null; + locFrom = DeclareLocal(typeFrom); + Emit(OpCodes.Stloc, locFrom); + locTo = DeclareLocal(typeTo); + // test for null + Emit(OpCodes.Ldloca, locFrom); + EmitHasValue(typeFrom); + labIfNull = DefineLabel(); + Emit(OpCodes.Brfalse_S, labIfNull); + Emit(OpCodes.Ldloca, locFrom); + EmitGetValueOrDefault(typeFrom); + Type nnTypeFrom = TypeUtils.GetNonNullableType(typeFrom); + Type nnTypeTo = TypeUtils.GetNonNullableType(typeTo); + EmitConvertToType(nnTypeFrom, nnTypeTo, isChecked); + // construct result type + ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo }); + Emit(OpCodes.Newobj, ci); + Emit(OpCodes.Stloc, locTo); + labEnd = DefineLabel(); + Emit(OpCodes.Br_S, labEnd); + // if null then create a default one + MarkLabel(labIfNull); + Emit(OpCodes.Ldloca, locTo); + Emit(OpCodes.Initobj, typeTo); + MarkLabel(labEnd); + Emit(OpCodes.Ldloc, locTo); + } + + //CONFORMING + private void EmitNonNullableToNullableConversion(Type typeFrom, Type typeTo, bool isChecked) { + Debug.Assert(!TypeUtils.IsNullableType(typeFrom)); + Debug.Assert(TypeUtils.IsNullableType(typeTo)); + LocalBuilder locTo = null; + locTo = DeclareLocal(typeTo); + Type nnTypeTo = TypeUtils.GetNonNullableType(typeTo); + EmitConvertToType(typeFrom, nnTypeTo, isChecked); + ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo }); + Emit(OpCodes.Newobj, ci); + Emit(OpCodes.Stloc, locTo); + Emit(OpCodes.Ldloc, locTo); + } + + //CONFORMING + private void EmitNullableToNonNullableConversion(Type typeFrom, Type typeTo, bool isChecked) { + Debug.Assert(TypeUtils.IsNullableType(typeFrom)); + Debug.Assert(!TypeUtils.IsNullableType(typeTo)); + if (typeTo.IsValueType) + EmitNullableToNonNullableStructConversion(typeFrom, typeTo, isChecked); + else + EmitNullableToReferenceConversion(typeFrom); + } + + //CONFORMING + private void EmitNullableToNonNullableStructConversion(Type typeFrom, Type typeTo, bool isChecked) { + Debug.Assert(TypeUtils.IsNullableType(typeFrom)); + Debug.Assert(!TypeUtils.IsNullableType(typeTo)); + Debug.Assert(typeTo.IsValueType); + LocalBuilder locFrom = null; + locFrom = DeclareLocal(typeFrom); + Emit(OpCodes.Stloc, locFrom); + Emit(OpCodes.Ldloca, locFrom); + EmitGetValue(typeFrom); + Type nnTypeFrom = TypeUtils.GetNonNullableType(typeFrom); + EmitConvertToType(nnTypeFrom, typeTo, isChecked); + } + + //CONFORMING + private void EmitNullableToReferenceConversion(Type typeFrom) { + Debug.Assert(TypeUtils.IsNullableType(typeFrom)); + // We've got a conversion from nullable to Object, ValueType, Enum, etc. Just box it so that + // we get the nullable semantics. + Emit(OpCodes.Box, typeFrom); + } + + //CONFORMING + private void EmitNullableConversion(Type typeFrom, Type typeTo, bool isChecked) { + bool isTypeFromNullable = TypeUtils.IsNullableType(typeFrom); + bool isTypeToNullable = TypeUtils.IsNullableType(typeTo); + Debug.Assert(isTypeFromNullable || isTypeToNullable); + if (isTypeFromNullable && isTypeToNullable) + EmitNullableToNullableConversion(typeFrom, typeTo, isChecked); + else if (isTypeFromNullable) + EmitNullableToNonNullableConversion(typeFrom, typeTo, isChecked); + else + EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked); + } + + //CONFORMING + internal void EmitHasValue(Type nullableType) { + MethodInfo mi = nullableType.GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public); + Debug.Assert(nullableType.IsValueType); + Emit(OpCodes.Call, mi); + } + + //CONFORMING + internal void EmitGetValue(Type nullableType) { + MethodInfo mi = nullableType.GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public); + Debug.Assert(nullableType.IsValueType); + Emit(OpCodes.Call, mi); + } + + //CONFORMING + internal void EmitGetValueOrDefault(Type nullableType) { + MethodInfo mi = nullableType.GetMethod("GetValueOrDefault", System.Type.EmptyTypes); + Debug.Assert(nullableType.IsValueType); + Emit(OpCodes.Call, mi); + } + + #endregion + + #region Arrays + + /// + /// Emits an array of constant values provided in the given list. + /// The array is strongly typed. + /// + internal void EmitArray(IList items) { + ContractUtils.RequiresNotNull(items, "items"); + + EmitInt(items.Count); + Emit(OpCodes.Newarr, typeof(T)); + for (int i = 0; i < items.Count; i++) { + Emit(OpCodes.Dup); + EmitInt(i); + EmitConstant(items[i], typeof(T)); + EmitStoreElement(typeof(T)); + } + } + + /// + /// Emits an array of values of count size. The items are emitted via the callback + /// which is provided with the current item index to emit. + /// + internal void EmitArray(Type elementType, int count, Action emit) { + ContractUtils.RequiresNotNull(elementType, "elementType"); + ContractUtils.RequiresNotNull(emit, "emit"); + ContractUtils.Requires(count >= 0, "count", Strings.CountCannotBeNegative); + + EmitInt(count); + Emit(OpCodes.Newarr, elementType); + for (int i = 0; i < count; i++) { + Emit(OpCodes.Dup); + EmitInt(i); + + emit(i); + + EmitStoreElement(elementType); + } + } + + /// + /// Emits an array construction code. + /// The code assumes that bounds for all dimensions + /// are already emitted. + /// + internal void EmitArray(Type arrayType) { + ContractUtils.RequiresNotNull(arrayType, "arrayType"); + ContractUtils.Requires(arrayType.IsArray, "arrayType", Strings.ArrayTypeMustBeArray); + + int rank = arrayType.GetArrayRank(); + if (rank == 1) { + Emit(OpCodes.Newarr, arrayType.GetElementType()); + } else { + Type[] types = new Type[rank]; + for (int i = 0; i < rank; i++) { + types[i] = typeof(int); + } + EmitNew(arrayType, types); + } + } + + #endregion + + #region Support for emitting constants + + internal void EmitDecimal(decimal value) { + if (Decimal.Truncate(value) == value) { + if (Int32.MinValue <= value && value <= Int32.MaxValue) { + int intValue = Decimal.ToInt32(value); + EmitInt(intValue); + EmitNew(typeof(Decimal).GetConstructor(new Type[] { typeof(int) })); + } else if (Int64.MinValue <= value && value <= Int64.MaxValue) { + long longValue = Decimal.ToInt64(value); + EmitLong(longValue); + EmitNew(typeof(Decimal).GetConstructor(new Type[] { typeof(long) })); + } else { + EmitDecimalBits(value); + } + } else { + EmitDecimalBits(value); + } + } + + private void EmitDecimalBits(decimal value) { + int[] bits = Decimal.GetBits(value); + EmitInt(bits[0]); + EmitInt(bits[1]); + EmitInt(bits[2]); + EmitBoolean((bits[3] & 0x80000000) != 0); + EmitByte((byte)(bits[3] >> 16)); + EmitNew(typeof(decimal).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(bool), typeof(byte) })); + } + + /// + /// Emits default(T) + /// Semantics match C# compiler behavior + /// + internal void EmitDefault(Type type) { + switch (Type.GetTypeCode(type)) { + case TypeCode.Object: + case TypeCode.DateTime: + if (type.IsValueType) { + // Type.GetTypeCode on an enum returns the underlying + // integer TypeCode, so we won't get here. + Debug.Assert(!type.IsEnum); + + // This is the IL for default(T) if T is a generic type + // parameter, so it should work for any type. It's also + // the standard pattern for structs. + LocalBuilder lb = GetLocal(type); + Emit(OpCodes.Ldloca, lb); + Emit(OpCodes.Initobj, type); + Emit(OpCodes.Ldloc, lb); + FreeLocal(lb); + } else { + Emit(OpCodes.Ldnull); + } + break; + + case TypeCode.Empty: + case TypeCode.String: + case TypeCode.DBNull: + Emit(OpCodes.Ldnull); + break; + + case TypeCode.Boolean: + case TypeCode.Char: + case TypeCode.SByte: + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + Emit(OpCodes.Ldc_I4_0); + break; + + case TypeCode.Int64: + case TypeCode.UInt64: + Emit(OpCodes.Ldc_I4_0); + Emit(OpCodes.Conv_I8); + break; + + case TypeCode.Single: + Emit(OpCodes.Ldc_R4, default(Single)); + break; + + case TypeCode.Double: + Emit(OpCodes.Ldc_R8, default(Double)); + break; + + case TypeCode.Decimal: + Emit(OpCodes.Ldc_I4_0); + Emit(OpCodes.Newobj, typeof(Decimal).GetConstructor(new Type[] { typeof(int) })); + break; + + default: + throw Assert.Unreachable; + } + } + + #endregion + + #region LocalTemps + + internal LocalBuilder GetLocal(Type type) { + Debug.Assert(type != null); + + LocalBuilder local; + if (_freeLocals.TryDequeue(type, out local)) { + Debug.Assert(type == local.LocalType); + return local; + } + + return DeclareLocal(type); + } + + // TODO: make "local" a ref param and null it out + internal void FreeLocal(LocalBuilder local) { + if (local != null) { + _freeLocals.Enqueue(local.LocalType, local); + } + } + + #endregion + } +} + +namespace System.Runtime.CompilerServices { + + public static partial class RuntimeOps { + private const int MIN_CACHE = -100; + private const int MAX_CACHE = 1000; + private static readonly object[] cache = MakeCache(); + + // Singleton boxed true/false + public static readonly object True = true; + public static readonly object False = false; + + private static object[] MakeCache() { + object[] result = new object[MAX_CACHE - MIN_CACHE]; + + for (int i = 0; i < result.Length; i++) { + result[i] = (object)(i + MIN_CACHE); + } + + return result; + } + + // TODO: see if we remove this + public static object Int32ToObject(Int32 value) { + // caches improves pystone by ~5-10% on MS .Net 1.1, this is a very integer intense app + if (value < MAX_CACHE && value >= MIN_CACHE) { + return cache[value - MIN_CACHE]; + } + return (object)value; + } + } + +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/KeyedQueue.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/KeyedQueue.cs new file mode 100644 index 0000000000..55409a7f04 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/KeyedQueue.cs @@ -0,0 +1,85 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace System.Linq.Expressions.Compiler { + + /// + /// A simple dictionary of queues, keyed off a particular type + /// This is useful for storing free lists of variables + /// + internal sealed class KeyedQueue { + private readonly Dictionary> _data; + + internal KeyedQueue() { + _data = new Dictionary>(); + } + + internal void Enqueue(K key, V value) { + Queue queue; + if (!_data.TryGetValue(key, out queue)) { + _data.Add(key, queue = new Queue()); + } + queue.Enqueue(value); + } + + internal V Dequeue(K key) { + Queue queue; + if (!_data.TryGetValue(key, out queue)) { + throw Error.QueueEmpty(); + } + V result = queue.Dequeue(); + if (queue.Count == 0) { + _data.Remove(key); + } + return result; + } + + internal bool TryDequeue(K key, out V value) { + Queue queue; + if (_data.TryGetValue(key, out queue) && queue.Count > 0) { + value = queue.Dequeue(); + if (queue.Count == 0) { + _data.Remove(key); + } + return true; + } + value = default(V); + return false; + } + + internal V Peek(K key) { + Queue queue; + if (!_data.TryGetValue(key, out queue)) { + throw Error.QueueEmpty(); + } + return queue.Peek(); + } + + internal int GetCount(K key) { + Queue queue; + if (!_data.TryGetValue(key, out queue)) { + return 0; + } + return queue.Count; + } + + internal void Clear() { + _data.Clear(); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/LabelInfo.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/LabelInfo.cs new file mode 100644 index 0000000000..16b8090bba --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/LabelInfo.cs @@ -0,0 +1,249 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Reflection.Emit; +using System.Dynamic.Utils; +using System.Diagnostics; + +namespace System.Linq.Expressions.Compiler { + + internal sealed class LabelInfo { + // The tree node representing this label + internal readonly LabelTarget Node; + + // The IL label, will be mutated if Node is redefined + internal Label Label { get; private set; } + + // The local that carries the label's value, if any + internal readonly LocalBuilder Value; + + // The blocks where this label is defined. If it has more than one item, + // the blocks can't be jumped to except from a child block + private readonly Set Definitions = new Set(); + + // Blocks that jump to this block + private readonly List References = new List(); + + // True if this label is the last thing in this block + // (meaning we can emit a direct return) + private readonly bool _canReturn; + + // True if at least one jump is across blocks + // If we have any jump across blocks to this label, then the + // LabelTarget can only be defined in one place + private bool _acrossBlockJump; + + // Until we have more information, default to a leave instruction, + // which always works. Note: leave spills the stack, so we need to + // ensure that StackSpiller has guarenteed us an empty stack at this + // point. Otherwise Leave and Branch are not equivalent + private OpCode _opCode; + + // IL Generator + private readonly ILGen _ilg; + + internal LabelInfo(ILGen il, LabelTarget node, bool canReturn) { + _ilg = il; + Node = node; + Label = il.DefineLabel(); + _canReturn = canReturn; + if (node != null && node.Type != typeof(void)) { + Value = il.DeclareLocal(node.Type); + } + + // Until we have more information, default to a leave instruction, which always works + _opCode = OpCodes.Leave; + } + + internal void Reference(LabelBlockInfo block) { + References.Add(block); + if (Definitions.Count > 0) { + ValidateJump(block); + } + } + + // Returns true if the label was successfully defined + // or false if the label is now ambiguous + internal void Define(ILGen il, LabelBlockInfo block) { + // Prevent the label from being shadowed, which enforces cleaner + // trees. Also we depend on this for simplicity (keeping only one + // active IL Label per LabelInfo) + for (LabelBlockInfo j = block; j != null; j = j.Parent) { + if (j.ContainsTarget(Node)) { + throw Error.LabelTargetAlreadyDefined(Node.Name); + } + } + + Definitions.Add(block); + block.AddLabelInfo(Node, this); + + // Once defined, validate all jumps + if (Definitions.Count == 1) { + foreach (var r in References) { + ValidateJump(r); + } + } else { + // Was just redefined, if we had any across block jumps, they're + // now invalid + if (_acrossBlockJump) { + throw Error.AmbiguousJump(Node.Name); + } + // For local jumps, we need a new IL label + // This is okay because: + // 1. no across block jumps have been made or will be made + // 2. we don't allow the label to be shadowed + Label = il.DefineLabel(); + } + } + + private void ValidateJump(LabelBlockInfo reference) { + // Assume we can do a ret/branch + _opCode = _canReturn ? OpCodes.Ret : OpCodes.Br; + + // look for a simple jump out + for (LabelBlockInfo j = reference; j != null; j = j.Parent) { + if (Definitions.Contains(j)) { + // found it, jump is valid! + return; + } + if (j.Kind == LabelBlockKind.Finally || + j.Kind == LabelBlockKind.Filter) { + break; + } + if (j.Kind == LabelBlockKind.Try || + j.Kind == LabelBlockKind.Catch) { + _opCode = OpCodes.Leave; + } + } + + _acrossBlockJump = true; + if (Definitions.Count > 1) { + throw Error.AmbiguousJump(Node.Name); + } + + // We didn't find an outward jump. Look for a jump across blocks + LabelBlockInfo def = Definitions.First(); + LabelBlockInfo common = Helpers.CommonNode(def, reference, b => b.Parent); + + // Assume we can do a ret/branch + _opCode = _canReturn ? OpCodes.Ret : OpCodes.Br; + + // Validate that we aren't jumping across a finally + for (LabelBlockInfo j = reference; j != common; j = j.Parent) { + if (j.Kind == LabelBlockKind.Finally) { + throw Error.ControlCannotLeaveFinally(); + } + if (j.Kind == LabelBlockKind.Filter) { + throw Error.ControlCannotLeaveFilterTest(); + } + if (j.Kind == LabelBlockKind.Try || + j.Kind == LabelBlockKind.Catch) { + _opCode = OpCodes.Leave; + } + } + + // Valdiate that we aren't jumping into a catch or an expression + for (LabelBlockInfo j = def; j != common; j = j.Parent) { + if (j.Kind != LabelBlockKind.Block) { + if (j.Kind == LabelBlockKind.Expression) { + throw Error.ControlCannotEnterExpression(); + } else { + throw Error.ControlCannotEnterTry(); + } + } + } + } + + internal void ValidateFinish() { + // Make sure that if this label was jumped to, it is also defined + if (References.Count > 0 && Definitions.Count == 0) { + throw Error.LabelTargetUndefined(Node.Name); + } + } + + // Return directly if we can + internal void EmitJump() { + if (_opCode == OpCodes.Ret) { + _ilg.Emit(OpCodes.Ret); + return; + } + + if (Value != null) { + _ilg.Emit(OpCodes.Stloc, Value); + } + _ilg.Emit(_opCode, Label); + } + + // TODO: We could save the local if we could determine in advance that + // nothing will "leave" to the label ("branch" preserves its stack) + internal void Mark() { + if (Value != null) { + _ilg.Emit(OpCodes.Stloc, Value); + } + _ilg.MarkLabel(Label); + if (Value != null) { + _ilg.Emit(OpCodes.Ldloc, Value); + } + } + } + + internal enum LabelBlockKind { + Block, + Expression, + Try, + Catch, + Finally, + Filter, + } + + internal sealed class LabelBlockInfo { + private Dictionary Labels; // lazily allocated, we typically use this only once every 6th-7th block + internal readonly LabelBlockKind Kind; + internal readonly LabelBlockInfo Parent; + + internal LabelBlockInfo(LabelBlockInfo parent, LabelBlockKind kind) { + Parent = parent; + Kind = kind; + } + + internal bool ContainsTarget(LabelTarget target) { + if (Labels == null) { + return false; + } + + return Labels.ContainsKey(target); + } + + internal bool TryGetLabelInfo(LabelTarget target, out LabelInfo info) { + if (Labels == null) { + info = null; + return false; + } + + return Labels.TryGetValue(target, out info); + } + + internal void AddLabelInfo(LabelTarget target, LabelInfo info) { + Debug.Assert(Kind == LabelBlockKind.Block); + + if (Labels == null) { + Labels = new Dictionary(); + } + + Labels.Add(target, info); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Address.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Address.cs new file mode 100644 index 0000000000..64fa9e132c --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Address.cs @@ -0,0 +1,331 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + partial class LambdaCompiler { + private void EmitAddress(Expression node, Type type) { + EmitAddress(node, type, true); + } + + // We don't want "ref" parameters to modify values of expressions + // except where it would in IL: locals, args, fields, and array elements + // (Unbox is an exception, it's intended to emit a ref to the orignal + // boxed value) + private void EmitAddress(Expression node, Type type, bool emitStart) { + Debug.Assert(node != null); + ExpressionStart startEmitted = emitStart ? EmitExpressionStart(node) : ExpressionStart.None; + + switch (node.NodeType) { + default: + EmitExpressionAddress(node, type); + break; + + case ExpressionType.ArrayIndex: + AddressOf((BinaryExpression)node, type); + break; + + case ExpressionType.Parameter: + AddressOf((ParameterExpression)node, type); + break; + + case ExpressionType.MemberAccess: + AddressOf((MemberExpression)node, type); + break; + + case ExpressionType.Unbox: + AddressOf((UnaryExpression)node, type); + break; + + case ExpressionType.Call: + AddressOf((MethodCallExpression)node, type); + break; + + case ExpressionType.Index: + AddressOf((IndexExpression)node, type); + break; + } + + if (emitStart) { + EmitExpressionEnd(startEmitted); + } + } + + //CONFORMING + private void AddressOf(BinaryExpression node, Type type) { + Debug.Assert(node.NodeType == ExpressionType.ArrayIndex && node.Method == null); + + if (type == node.Type) { + EmitExpression(node.Left); + EmitExpression(node.Right); + Type rightType = node.Right.Type; + if (TypeUtils.IsNullableType(rightType)) { + LocalBuilder loc = _ilg.GetLocal(rightType); + _ilg.Emit(OpCodes.Stloc, loc); + _ilg.Emit(OpCodes.Ldloca, loc); + _ilg.EmitGetValue(rightType); + _ilg.FreeLocal(loc); + } + Type indexType = TypeUtils.GetNonNullableType(rightType); + if (indexType != typeof(int)) { + _ilg.EmitConvertToType(indexType, typeof(int), true); + } + _ilg.Emit(OpCodes.Ldelema, node.Type); + } else { + EmitExpressionAddress(node, type); + } + } + + private void AddressOf(ParameterExpression node, Type type) { + if (type == node.Type) { + if (node.IsByRef) { + _scope.EmitGet(node); + } else { + _scope.EmitAddressOf(node); + } + } else { + EmitExpressionAddress(node, type); + } + } + + //CONFORMING + private void AddressOf(MemberExpression node, Type type) { + if (type == node.Type) { + // emit "this", if any + Type objectType = null; + if (node.Expression != null) { + EmitInstance(node.Expression, objectType = node.Expression.Type); + } + EmitMemberAddress(node.Member, objectType); + } else { + EmitExpressionAddress(node, type); + } + } + + // assumes the instance is already on the stack + private void EmitMemberAddress(MemberInfo member, Type objectType) { + if (member.MemberType == MemberTypes.Field) { + FieldInfo field = (FieldInfo)member; + + // Verifiable code may not take the address of an init-only field. + // If we are asked to do so then get the value out of the field, stuff it + // into a local of the same type, and then take the address of the local. + // Typically this is what we want to do anyway; if we are saying + // Foo.bar.ToString() for a static value-typed field bar then we don't need + // the address of field bar to do the call. The address of a local which + // has the same value as bar is sufficient. + + // CONSIDER: + // The C# compiler will not compile a lambda expression tree + // which writes to the address of an init-only field. But one could + // probably use the expression tree API to build such an expression. + // (When compiled, such an expression would fail silently.) It might + // be worth it to add checking to the expression tree API to ensure + // that it is illegal to attempt to write to an init-only field, + // the same way that it is illegal to write to a read-only property. + // The same goes for literal fields. + if (!field.IsLiteral && !field.IsInitOnly) { + _ilg.EmitFieldAddress(field); + return; + } + } + + EmitMemberGet(member, objectType); + LocalBuilder temp = _ilg.GetLocal(GetMemberType(member)); + _ilg.Emit(OpCodes.Stloc, temp); + _ilg.Emit(OpCodes.Ldloca, temp); + _ilg.FreeLocal(temp); + } + + //CONFORMING + private void AddressOf(MethodCallExpression node, Type type) { + // An array index of a multi-dimensional array is represented by a call to Array.Get, + // rather than having its own array-access node. This means that when we are trying to + // get the address of a member of a multi-dimensional array, we'll be trying to + // get the address of a Get method, and it will fail to do so. Instead, detect + // this situation and replace it with a call to the Address method. + if (!node.Method.IsStatic && + node.Object.Type.IsArray && + node.Method == node.Object.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance)) { + + MethodInfo mi = node.Object.Type.GetMethod("Address", BindingFlags.Public | BindingFlags.Instance); + + EmitMethodCall(node.Object, mi, node); + } else { + EmitExpressionAddress(node, type); + } + } + + private void AddressOf(IndexExpression node, Type type) { + if (type != node.Type || node.Indexer != null) { + EmitExpressionAddress(node, type); + return; + } + + if (node.Arguments.Count == 1) { + EmitExpression(node.Object); + EmitExpression(node.Arguments[0]); + _ilg.Emit(OpCodes.Ldelema, node.Type); + } else { + var address = node.Object.Type.GetMethod("Address", BindingFlags.Public | BindingFlags.Instance); + EmitMethodCall(node.Object, address, node); + } + } + + private void AddressOf(UnaryExpression node, Type type) { + Debug.Assert(node.NodeType == ExpressionType.Unbox); + Debug.Assert(type.IsValueType && !TypeUtils.IsNullableType(type)); + + // Unbox leaves a pointer to the boxed value on the stack + EmitExpression(node.Operand); + _ilg.Emit(OpCodes.Unbox, type); + } + + private void EmitExpressionAddress(Expression node, Type type) { + Debug.Assert(TypeUtils.AreReferenceAssignable(type, node.Type)); + + EmitExpression(node, false); + LocalBuilder tmp = _ilg.GetLocal(type); + _ilg.Emit(OpCodes.Stloc, tmp); + _ilg.Emit(OpCodes.Ldloca, tmp); + _ilg.FreeLocal(tmp); + } + + + // Emits the address of the expression, returning the write back if necessary + // + // For properties, we want to write back into the property if it's + // passed byref. + private WriteBack EmitAddressWriteBack(Expression node, Type type) { + ExpressionStart startEmitted = EmitExpressionStart(node); + + WriteBack result = null; + if (type == node.Type) { + switch (node.NodeType) { + case ExpressionType.MemberAccess: + result = AddressOfWriteBack((MemberExpression)node); + break; + case ExpressionType.Index: + result = AddressOfWriteBack((IndexExpression)node); + break; + } + } + if (result == null) { + EmitAddress(node, type, false); + } + + EmitExpressionEnd(startEmitted); + + return result; + } + + private WriteBack AddressOfWriteBack(MemberExpression node) { + if (node.Member.MemberType != MemberTypes.Property || !((PropertyInfo)node.Member).CanWrite) { + return null; + } + + // emit instance, if any + LocalBuilder instanceLocal = null; + Type instanceType = null; + if (node.Expression != null) { + EmitInstance(node.Expression, instanceType = node.Expression.Type); + // store in local + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Stloc, instanceLocal = _ilg.GetLocal(instanceType)); + } + + PropertyInfo pi = (PropertyInfo)node.Member; + + // emit the get + EmitCall(instanceType, pi.GetGetMethod(true)); + + // emit the address of the value + var valueLocal = _ilg.GetLocal(node.Type); + _ilg.Emit(OpCodes.Stloc, valueLocal); + _ilg.Emit(OpCodes.Ldloca, valueLocal); + + // Set the property after the method call + // don't re-evaluate anything + return delegate() { + if (instanceLocal != null) { + _ilg.Emit(OpCodes.Ldloc, instanceLocal); + _ilg.FreeLocal(instanceLocal); + } + _ilg.Emit(OpCodes.Ldloc, valueLocal); + _ilg.FreeLocal(valueLocal); + EmitCall(instanceType, pi.GetSetMethod(true)); + }; + } + + private WriteBack AddressOfWriteBack(IndexExpression node) { + if (node.Indexer == null || !node.Indexer.CanWrite) { + return null; + } + + // emit instance, if any + LocalBuilder instanceLocal = null; + Type instanceType = null; + if (node.Object != null) { + EmitInstance(node.Object, instanceType = node.Object.Type); + + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Stloc, instanceLocal = _ilg.GetLocal(instanceType)); + } + + // Emit indexes. We don't allow byref args, so no need to worry + // about writebacks or EmitAddress + List args = new List(); + foreach (var arg in node.Arguments) { + EmitExpression(arg); + + var argLocal = _ilg.GetLocal(arg.Type); + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Stloc, argLocal); + args.Add(argLocal); + } + + // emit the get + EmitGetIndexCall(node, instanceType); + + // emit the address of the value + var valueLocal = _ilg.GetLocal(node.Type); + _ilg.Emit(OpCodes.Stloc, valueLocal); + _ilg.Emit(OpCodes.Ldloca, valueLocal); + + // Set the property after the method call + // don't re-evaluate anything + return delegate() { + if (instanceLocal != null) { + _ilg.Emit(OpCodes.Ldloc, instanceLocal); + _ilg.FreeLocal(instanceLocal); + } + foreach (var arg in args) { + _ilg.Emit(OpCodes.Ldloc, arg); + _ilg.FreeLocal(arg); + } + _ilg.Emit(OpCodes.Ldloc, valueLocal); + _ilg.FreeLocal(valueLocal); + + EmitSetIndexCall(node, instanceType); + }; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Binary.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Binary.cs new file mode 100644 index 0000000000..af07b83a85 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Binary.cs @@ -0,0 +1,715 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + partial class LambdaCompiler { + + //CONFORMING + private void EmitBinaryExpression(Expression expr) { + BinaryExpression b = (BinaryExpression)expr; + + Debug.Assert(b.NodeType != ExpressionType.AndAlso && b.NodeType != ExpressionType.OrElse && b.NodeType != ExpressionType.Coalesce); + + if (b.Method != null) { + EmitBinaryMethod(b); + return; + } + + // For EQ and NE, if there is a user-specified method, use it. + // Otherwise implement the C# semantics that allow equality + // comparisons on non-primitive nullable structs that don't + // overload "==" + if ((b.NodeType == ExpressionType.Equal || b.NodeType == ExpressionType.NotEqual) && + (b.Type == typeof(bool) || b.Type == typeof(bool?))) { + + // If we have x==null, x!=null, null==x or null!=x where x is + // nullable but not null, then generate a call to x.HasValue. + Debug.Assert(!b.IsLiftedToNull || b.Type == typeof(bool?)); + if (ConstantCheck.IsNull(b.Left) && !ConstantCheck.IsNull(b.Right) && TypeUtils.IsNullableType(b.Right.Type)) { + EmitNullEquality(b.NodeType, b.Right, b.IsLiftedToNull); + return; + } + if (ConstantCheck.IsNull(b.Right) && !ConstantCheck.IsNull(b.Left) && TypeUtils.IsNullableType(b.Left.Type)) { + EmitNullEquality(b.NodeType, b.Left, b.IsLiftedToNull); + return; + } + } + + // Otherwise generate it normally. + EmitExpression(b.Left); + EmitExpression(b.Right); + + EmitBinaryOperator(b.NodeType, b.Left.Type, b.Right.Type, b.Type, b.IsLiftedToNull); + } + + //CONFORMING + private void EmitNullEquality(ExpressionType op, Expression e, bool isLiftedToNull) { + Debug.Assert(TypeUtils.IsNullableType(e.Type)); + Debug.Assert(op == ExpressionType.Equal || op == ExpressionType.NotEqual); + // If we are lifted to null then just evaluate the expression for its side effects, discard, + // and generate null. If we are not lifted to null then generate a call to HasValue. + if (isLiftedToNull) { + EmitExpressionAsVoid(e); + _ilg.EmitDefault(typeof(bool?)); + } else { + EmitAddress(e, e.Type); + _ilg.EmitHasValue(e.Type); + if (op == ExpressionType.Equal) { + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + } + } + } + + //CONFORMING + private void EmitBinaryMethod(BinaryExpression b) { + if (b.IsLifted) { + ParameterExpression p1 = Expression.Variable(TypeUtils.GetNonNullableType(b.Left.Type), null); + ParameterExpression p2 = Expression.Variable(TypeUtils.GetNonNullableType(b.Right.Type), null); + MethodCallExpression mc = Expression.Call(null, b.Method, p1, p2); + Type resultType = null; + if (b.IsLiftedToNull) { + resultType = TypeUtils.GetNullableType(mc.Type); + } else { + switch (b.NodeType) { + case ExpressionType.Equal: + case ExpressionType.NotEqual: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + if (mc.Type != typeof(bool)) { + throw Error.ArgumentMustBeBoolean(); + } + resultType = typeof(bool); + break; + default: + resultType = TypeUtils.GetNullableType(mc.Type); + break; + } + } + IList variables = new ParameterExpression[] { p1, p2 }; + IList arguments = new Expression[] { b.Left, b.Right }; + ValidateLift(variables, arguments); + EmitLift(b.NodeType, resultType, mc, variables, arguments); + } else { + EmitMethodCallExpression(Expression.Call(null, b.Method, b.Left, b.Right)); + } + } + + //CONFORMING + private void EmitBinaryOperator(ExpressionType op, Type leftType, Type rightType, Type resultType, bool liftedToNull) { + bool leftIsNullable = TypeUtils.IsNullableType(leftType); + bool rightIsNullable = TypeUtils.IsNullableType(rightType); + switch (op) { + case ExpressionType.ArrayIndex: + if (rightIsNullable) { + LocalBuilder loc = _ilg.GetLocal(rightType); + _ilg.Emit(OpCodes.Stloc, loc); + _ilg.Emit(OpCodes.Ldloca, loc); + _ilg.FreeLocal(loc); + _ilg.EmitGetValue(rightType); + } + Type indexType = TypeUtils.GetNonNullableType(rightType); + if (indexType != typeof(int)) { + _ilg.EmitConvertToType(indexType, typeof(int), true); + } + _ilg.EmitLoadElement(leftType.GetElementType()); + return; + case ExpressionType.Coalesce: + throw Error.UnexpectedCoalesceOperator(); + } + + if (leftIsNullable) { + EmitLiftedBinaryOp(op, leftType, rightType, resultType, liftedToNull); + } else { + EmitUnliftedBinaryOp(op, leftType, rightType); + EmitConvertArithmeticResult(op, resultType); + } + } + + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private void EmitUnliftedBinaryOp(ExpressionType op, Type leftType, Type rightType) { + Debug.Assert(!TypeUtils.IsNullableType(leftType)); + + if (op == ExpressionType.Equal || op == ExpressionType.NotEqual) { + EmitUnliftedEquality(op, leftType); + return; + } + if (!leftType.IsPrimitive) { + throw Error.OperatorNotImplementedForType(op, leftType); + } + switch (op) { + case ExpressionType.Add: + _ilg.Emit(OpCodes.Add); + break; + case ExpressionType.AddChecked: + EmitOverflowHelper(leftType, rightType); + if (TypeUtils.IsFloatingPoint(leftType)) { + _ilg.Emit(OpCodes.Add); + } else if (TypeUtils.IsUnsigned(leftType)) { + _ilg.Emit(OpCodes.Add_Ovf_Un); + } else { + _ilg.Emit(OpCodes.Add_Ovf); + } + break; + case ExpressionType.Subtract: + _ilg.Emit(OpCodes.Sub); + break; + case ExpressionType.SubtractChecked: + EmitOverflowHelper(leftType, rightType); + if (TypeUtils.IsFloatingPoint(leftType)) { + _ilg.Emit(OpCodes.Sub); + } else if (TypeUtils.IsUnsigned(leftType)) { + _ilg.Emit(OpCodes.Sub_Ovf_Un); + } else { + _ilg.Emit(OpCodes.Sub_Ovf); + } + break; + case ExpressionType.Multiply: + _ilg.Emit(OpCodes.Mul); + break; + case ExpressionType.MultiplyChecked: + EmitOverflowHelper(leftType, rightType); + if (TypeUtils.IsFloatingPoint(leftType)) { + _ilg.Emit(OpCodes.Mul); + } else if (TypeUtils.IsUnsigned(leftType)) { + _ilg.Emit(OpCodes.Mul_Ovf_Un); + } else { + _ilg.Emit(OpCodes.Mul_Ovf); + } + break; + case ExpressionType.Divide: + if (TypeUtils.IsUnsigned(leftType)) { + _ilg.Emit(OpCodes.Div_Un); + } else { + _ilg.Emit(OpCodes.Div); + } + break; + case ExpressionType.Modulo: + if (TypeUtils.IsUnsigned(leftType)) { + _ilg.Emit(OpCodes.Rem_Un); + } else { + _ilg.Emit(OpCodes.Rem); + } + break; + case ExpressionType.And: + case ExpressionType.AndAlso: + _ilg.Emit(OpCodes.And); + break; + case ExpressionType.Or: + case ExpressionType.OrElse: + _ilg.Emit(OpCodes.Or); + break; + case ExpressionType.LessThan: + if (TypeUtils.IsUnsigned(leftType)) { + _ilg.Emit(OpCodes.Clt_Un); + } else { + _ilg.Emit(OpCodes.Clt); + } + break; + case ExpressionType.LessThanOrEqual: { + Label labFalse = _ilg.DefineLabel(); + Label labEnd = _ilg.DefineLabel(); + if (TypeUtils.IsUnsigned(leftType)) { + _ilg.Emit(OpCodes.Ble_Un_S, labFalse); + } else { + _ilg.Emit(OpCodes.Ble_S, labFalse); + } + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Br_S, labEnd); + _ilg.MarkLabel(labFalse); + _ilg.Emit(OpCodes.Ldc_I4_1); + _ilg.MarkLabel(labEnd); + } + break; + case ExpressionType.GreaterThan: + if (TypeUtils.IsUnsigned(leftType)) { + _ilg.Emit(OpCodes.Cgt_Un); + } else { + _ilg.Emit(OpCodes.Cgt); + } + break; + case ExpressionType.GreaterThanOrEqual: { + Label labFalse = _ilg.DefineLabel(); + Label labEnd = _ilg.DefineLabel(); + if (TypeUtils.IsUnsigned(leftType)) { + _ilg.Emit(OpCodes.Bge_Un_S, labFalse); + } else { + _ilg.Emit(OpCodes.Bge_S, labFalse); + } + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Br_S, labEnd); + _ilg.MarkLabel(labFalse); + _ilg.Emit(OpCodes.Ldc_I4_1); + _ilg.MarkLabel(labEnd); + } + break; + case ExpressionType.ExclusiveOr: + _ilg.Emit(OpCodes.Xor); + break; + case ExpressionType.LeftShift: { + Type shiftType = TypeUtils.GetNonNullableType(rightType); + if (shiftType != typeof(int)) { + _ilg.EmitConvertToType(shiftType, typeof(int), true); + } + _ilg.Emit(OpCodes.Shl); + } + break; + case ExpressionType.RightShift: { + Type shiftType = TypeUtils.GetNonNullableType(rightType); + if (shiftType != typeof(int)) { + _ilg.EmitConvertToType(shiftType, typeof(int), true); + } + if (TypeUtils.IsUnsigned(leftType)) { + _ilg.Emit(OpCodes.Shr_Un); + } else { + _ilg.Emit(OpCodes.Shr); + } + } + break; + default: + throw Error.UnhandledBinary(op); + } + } + + // Binary/unary operations on 8 and 16 bit operand types will leave a + // 32-bit value on the stack, because that's how IL works. For these + // cases, we need to cast it back to the resultType, possibly using a + // checked conversion if the original operator was convert + private void EmitConvertArithmeticResult(ExpressionType op, Type resultType) { + Debug.Assert(!resultType.IsNullableType()); + + switch (Type.GetTypeCode(resultType)) { + case TypeCode.Byte: + _ilg.Emit(IsChecked(op) ? OpCodes.Conv_Ovf_U1 : OpCodes.Conv_U1); + break; + case TypeCode.SByte: + _ilg.Emit(IsChecked(op) ? OpCodes.Conv_Ovf_I1 : OpCodes.Conv_I1); + break; + case TypeCode.UInt16: + _ilg.Emit(IsChecked(op) ? OpCodes.Conv_Ovf_U2 : OpCodes.Conv_U2); + break; + case TypeCode.Int16: + _ilg.Emit(IsChecked(op) ? OpCodes.Conv_Ovf_I2 : OpCodes.Conv_I2); + break; + } + } + + //this code is needed to make sure that we get overflow exception + // + // TODO: we may not need this now that conversion of the result type is + // being handled correctly by EmitConvertArithmeticResult + private void EmitOverflowHelper(Type leftType, Type rightType) { + LocalBuilder left = _ilg.GetLocal(leftType); + LocalBuilder right = _ilg.GetLocal(rightType); + _ilg.Emit(OpCodes.Stloc, right); + _ilg.Emit(OpCodes.Stloc, left); + _ilg.Emit(OpCodes.Ldloc, left); + _ilg.Emit(OpCodes.Ldloc, right); + _ilg.FreeLocal(left); + _ilg.FreeLocal(right); + } + + //CONFORMING + private void EmitUnliftedEquality(ExpressionType op, Type type) { + Debug.Assert(op == ExpressionType.Equal || op == ExpressionType.NotEqual); + if (!type.IsPrimitive && type.IsValueType && !type.IsEnum) { + throw Error.OperatorNotImplementedForType(op, type); + } + _ilg.Emit(OpCodes.Ceq); + if (op == ExpressionType.NotEqual) { + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + } + } + + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private void EmitLiftedBinaryOp(ExpressionType op, Type leftType, Type rightType, Type resultType, bool liftedToNull) { + Debug.Assert(TypeUtils.IsNullableType(leftType)); + switch (op) { + case ExpressionType.And: + if (leftType == typeof(bool?)) { + EmitLiftedBooleanAnd(); + } else { + EmitLiftedBinaryArithmetic(op, leftType, rightType, resultType); + } + break; + case ExpressionType.Or: + if (leftType == typeof(bool?)) { + EmitLiftedBooleanOr(); + } else { + EmitLiftedBinaryArithmetic(op, leftType, rightType, resultType); + } + break; + case ExpressionType.ExclusiveOr: + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.Subtract: + case ExpressionType.SubtractChecked: + case ExpressionType.Multiply: + case ExpressionType.MultiplyChecked: + case ExpressionType.Divide: + case ExpressionType.Modulo: + case ExpressionType.LeftShift: + case ExpressionType.RightShift: + EmitLiftedBinaryArithmetic(op, leftType, rightType, resultType); + break; + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.Equal: + case ExpressionType.NotEqual: + EmitLiftedRelational(op, leftType, rightType, resultType, liftedToNull); + break; + case ExpressionType.AndAlso: + case ExpressionType.OrElse: + default: + throw Assert.Unreachable; + } + } + + //CONFORMING + private void EmitLiftedRelational(ExpressionType op, Type leftType, Type rightType, Type resultType, bool liftedToNull) { + Debug.Assert(TypeUtils.IsNullableType(leftType)); + + Label shortCircuit = _ilg.DefineLabel(); + LocalBuilder locLeft = _ilg.GetLocal(leftType); + LocalBuilder locRight = _ilg.GetLocal(rightType); + + // store values (reverse order since they are already on the stack) + _ilg.Emit(OpCodes.Stloc, locRight); + _ilg.Emit(OpCodes.Stloc, locLeft); + + if (op == ExpressionType.Equal) { + // test for both null -> true + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(leftType); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitHasValue(rightType); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.And); + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Brtrue_S, shortCircuit); + _ilg.Emit(OpCodes.Pop); + + // test for either is null -> false + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(leftType); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitHasValue(rightType); + _ilg.Emit(OpCodes.And); + + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Brfalse_S, shortCircuit); + _ilg.Emit(OpCodes.Pop); + } else if (op == ExpressionType.NotEqual) { + // test for both null -> false + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(leftType); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitHasValue(rightType); + _ilg.Emit(OpCodes.Or); + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Brfalse_S, shortCircuit); + _ilg.Emit(OpCodes.Pop); + + // test for either is null -> true + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(leftType); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitHasValue(rightType); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Or); + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Brtrue_S, shortCircuit); + _ilg.Emit(OpCodes.Pop); + } else { + // test for either is null -> false + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(leftType); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitHasValue(rightType); + _ilg.Emit(OpCodes.And); + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Brfalse_S, shortCircuit); + _ilg.Emit(OpCodes.Pop); + } + + // do op on values + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitGetValueOrDefault(leftType); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitGetValueOrDefault(rightType); + + //RELEASING locLeft locRight + _ilg.FreeLocal(locLeft); + _ilg.FreeLocal(locRight); + + EmitBinaryOperator( + op, + TypeUtils.GetNonNullableType(leftType), + TypeUtils.GetNonNullableType(rightType), + TypeUtils.GetNonNullableType(resultType), + false + ); + + if (!liftedToNull) { + _ilg.MarkLabel(shortCircuit); + } + + if (resultType != TypeUtils.GetNonNullableType(resultType)) { + _ilg.EmitConvertToType(TypeUtils.GetNonNullableType(resultType), resultType, true); + } + + if (liftedToNull) { + Label labEnd = _ilg.DefineLabel(); + _ilg.Emit(OpCodes.Br, labEnd); + _ilg.MarkLabel(shortCircuit); + _ilg.Emit(OpCodes.Pop); + _ilg.Emit(OpCodes.Ldnull); + _ilg.Emit(OpCodes.Unbox_Any, resultType); + _ilg.MarkLabel(labEnd); + } + } + + //CONFORMING + private void EmitLiftedBinaryArithmetic(ExpressionType op, Type leftType, Type rightType, Type resultType) { + bool leftIsNullable = TypeUtils.IsNullableType(leftType); + bool rightIsNullable = TypeUtils.IsNullableType(rightType); + + Debug.Assert(leftIsNullable); + + Label labIfNull = _ilg.DefineLabel(); + Label labEnd = _ilg.DefineLabel(); + LocalBuilder locLeft = _ilg.GetLocal(leftType); + LocalBuilder locRight = _ilg.GetLocal(rightType); + LocalBuilder locResult = _ilg.GetLocal(resultType); + + // store values (reverse order since they are already on the stack) + _ilg.Emit(OpCodes.Stloc, locRight); + _ilg.Emit(OpCodes.Stloc, locLeft); + + // test for null + if (rightIsNullable) { + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(leftType); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitHasValue(rightType); + _ilg.Emit(OpCodes.And); + _ilg.Emit(OpCodes.Brfalse_S, labIfNull); + } else { + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(leftType); + _ilg.Emit(OpCodes.Brfalse_S, labIfNull); + } + + // do op on values + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitGetValueOrDefault(leftType); + + if (rightIsNullable) { + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitGetValueOrDefault(rightType); + } else { + _ilg.Emit(OpCodes.Ldloc, locRight); + } + + //RELEASING locLeft locRight + _ilg.FreeLocal(locLeft); + _ilg.FreeLocal(locRight); + + EmitBinaryOperator(op, TypeUtils.GetNonNullableType(leftType), TypeUtils.GetNonNullableType(rightType), TypeUtils.GetNonNullableType(resultType), false); + + // construct result type + ConstructorInfo ci = resultType.GetConstructor(new Type[] { TypeUtils.GetNonNullableType(resultType) }); + _ilg.Emit(OpCodes.Newobj, ci); + _ilg.Emit(OpCodes.Stloc, locResult); + _ilg.Emit(OpCodes.Br_S, labEnd); + + // if null then create a default one + _ilg.MarkLabel(labIfNull); + _ilg.Emit(OpCodes.Ldloca, locResult); + _ilg.Emit(OpCodes.Initobj, resultType); + + _ilg.MarkLabel(labEnd); + + _ilg.Emit(OpCodes.Ldloc, locResult); + + //RELEASING locResult + _ilg.FreeLocal(locResult); + } + + //CONFORMING + private void EmitLiftedBooleanAnd() { + Type type = typeof(bool?); + Label labComputeRight = _ilg.DefineLabel(); + Label labReturnFalse = _ilg.DefineLabel(); + Label labReturnNull = _ilg.DefineLabel(); + Label labReturnValue = _ilg.DefineLabel(); + Label labExit = _ilg.DefineLabel(); + + // store values (reverse order since they are already on the stack) + LocalBuilder locLeft = _ilg.GetLocal(type); + LocalBuilder locRight = _ilg.GetLocal(type); + _ilg.Emit(OpCodes.Stloc, locRight); + _ilg.Emit(OpCodes.Stloc, locLeft); + + // compute left + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse, labComputeRight); + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitGetValueOrDefault(type); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Brtrue, labReturnFalse); + + // compute right + _ilg.MarkLabel(labComputeRight); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse_S, labReturnNull); + _ilg.Emit(OpCodes.Ldloca, locRight); + + //RELEASING locRight + _ilg.FreeLocal(locRight); + + _ilg.EmitGetValueOrDefault(type); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Brtrue_S, labReturnFalse); + + // check left for null again + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse, labReturnNull); + + // return true + _ilg.Emit(OpCodes.Ldc_I4_1); + _ilg.Emit(OpCodes.Br_S, labReturnValue); + + // return false + _ilg.MarkLabel(labReturnFalse); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Br_S, labReturnValue); + + _ilg.MarkLabel(labReturnValue); + ConstructorInfo ci = type.GetConstructor(new Type[] { typeof(bool) }); + _ilg.Emit(OpCodes.Newobj, ci); + _ilg.Emit(OpCodes.Stloc, locLeft); + _ilg.Emit(OpCodes.Br, labExit); + + // return null + _ilg.MarkLabel(labReturnNull); + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.Emit(OpCodes.Initobj, type); + + _ilg.MarkLabel(labExit); + _ilg.Emit(OpCodes.Ldloc, locLeft); + + //RELEASING locLeft + _ilg.FreeLocal(locLeft); + } + + //CONFORMING + private void EmitLiftedBooleanOr() { + Type type = typeof(bool?); + Label labComputeRight = _ilg.DefineLabel(); + Label labReturnTrue = _ilg.DefineLabel(); + Label labReturnNull = _ilg.DefineLabel(); + Label labReturnValue = _ilg.DefineLabel(); + Label labExit = _ilg.DefineLabel(); + + // store values (reverse order since they are already on the stack) + LocalBuilder locLeft = _ilg.GetLocal(type); + LocalBuilder locRight = _ilg.GetLocal(type); + _ilg.Emit(OpCodes.Stloc, locRight); + _ilg.Emit(OpCodes.Stloc, locLeft); + + // compute left + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse, labComputeRight); + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitGetValueOrDefault(type); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Brfalse, labReturnTrue); + + // compute right + _ilg.MarkLabel(labComputeRight); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse_S, labReturnNull); + _ilg.Emit(OpCodes.Ldloca, locRight); + + //RELEASING locRight + _ilg.FreeLocal(locRight); + + _ilg.EmitGetValueOrDefault(type); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Brfalse_S, labReturnTrue); + + // check left for null again + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse, labReturnNull); + + // return false + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Br_S, labReturnValue); + + // return true + _ilg.MarkLabel(labReturnTrue); + _ilg.Emit(OpCodes.Ldc_I4_1); + _ilg.Emit(OpCodes.Br_S, labReturnValue); + + _ilg.MarkLabel(labReturnValue); + ConstructorInfo ci = type.GetConstructor(new Type[] { typeof(bool) }); + _ilg.Emit(OpCodes.Newobj, ci); + _ilg.Emit(OpCodes.Stloc, locLeft); + _ilg.Emit(OpCodes.Br, labExit); + + // return null + _ilg.MarkLabel(labReturnNull); + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.Emit(OpCodes.Initobj, type); + + _ilg.MarkLabel(labExit); + _ilg.Emit(OpCodes.Ldloc, locLeft); + + //RELEASING locLeft + _ilg.FreeLocal(locLeft); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.ControlFlow.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.ControlFlow.cs new file mode 100644 index 0000000000..228fa8508f --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.ControlFlow.cs @@ -0,0 +1,158 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Reflection.Emit; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + + // The part of the LambdaCompiler dealing with low level control flow + // break, contiue, return, exceptions, etc + partial class LambdaCompiler { + + private LabelInfo EnsureLabel(LabelTarget node) { + LabelInfo result; + if (!_labelInfo.TryGetValue(node, out result)) { + _labelInfo.Add(node, result = new LabelInfo(_ilg, node, false)); + } + return result; + } + + private LabelInfo ReferenceLabel(LabelTarget node) { + LabelInfo result = EnsureLabel(node); + result.Reference(_labelBlock); + return result; + } + + private LabelInfo DefineLabel(LabelTarget node) { + if (node == null) { + return new LabelInfo(_ilg, null, false); + } + LabelInfo result = EnsureLabel(node); + result.Define(_ilg, _labelBlock); + return result; + } + + private void PushLabelBlock(LabelBlockKind type) { + _labelBlock = new LabelBlockInfo(_labelBlock, type); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "kind")] + private void PopLabelBlock(LabelBlockKind kind) { + Debug.Assert(_labelBlock != null && _labelBlock.Kind == kind); + _labelBlock = _labelBlock.Parent; + } + + private void EmitLabelExpression(Expression expr) { + var node = (LabelExpression)expr; + Debug.Assert(node.Label != null); + + // If we're an immediate child of a block, our label will already + // be defined. If not, we need to define our own block so this + // label isn't exposed except to its own child expression. + LabelInfo label; + if (!_labelBlock.TryGetLabelInfo(node.Label, out label)) { + label = DefineLabel(node.Label); + } + + if (node.DefaultValue != null) { + EmitExpression(node.DefaultValue); + } + + label.Mark(); + } + + private void EmitGotoExpression(Expression expr) { + var node = (GotoExpression)expr; + if (node.Value != null) { + EmitExpression(node.Value); + } + + ReferenceLabel(node.Target).EmitJump(); + } + + private bool TryPushLabelBlock(Expression node) { + // Anything that is "statement-like" -- e.g. has no associated + // stack state can be jumped into, with the exception of try-blocks + // We indicate this by a "Block" + // + // Otherwise, we push an "Expression" to indicate that it can't be + // jumped into + switch (node.NodeType) { + default: + if (_labelBlock.Kind != LabelBlockKind.Expression) { + PushLabelBlock(LabelBlockKind.Expression); + return true; + } + return false; + case ExpressionType.Label: + // LabelExpression is a bit special, if it's directly in a block + // it becomes associate with the block's scope + if (_labelBlock.Kind != LabelBlockKind.Block || + !_labelBlock.ContainsTarget(((LabelExpression)node).Label)) { + PushLabelBlock(LabelBlockKind.Block); + return true; + } + return false; + case ExpressionType.Assign: + // Assignment where left side is a variable/parameter is + // safe to jump into + var assign = (BinaryExpression)node; + if (assign.Left.NodeType == ExpressionType.Parameter) { + PushLabelBlock(LabelBlockKind.Block); + return true; + } + // Otherwise go to the default case + goto default; + case ExpressionType.DebugInfo: + case ExpressionType.Conditional: + case ExpressionType.Block: + case ExpressionType.Switch: + case ExpressionType.Loop: + case ExpressionType.Goto: + PushLabelBlock(LabelBlockKind.Block); + return true; + } + } + + // See if this lambda has a return label + // If so, we'll create it now and mark it as allowing the "ret" opcode + // This allows us to generate better IL + private void AddReturnLabel(Expression lambdaBody) { + while (true) { + switch (lambdaBody.NodeType) { + default: + // Didn't find return label + return; + case ExpressionType.Label: + // Found it! + var label = ((LabelExpression)lambdaBody).Label; + _labelInfo.Add(label, new LabelInfo(_ilg, label, true)); + return; + case ExpressionType.DebugInfo: + // Look in the body + lambdaBody = ((DebugInfoExpression)lambdaBody).Expression; + continue; + case ExpressionType.Block: + // Look in the last expression of a block + var body = (BlockExpression)lambdaBody; + lambdaBody = body.GetExpression(body.ExpressionCount - 1); + continue; + } + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Expressions.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Expressions.cs new file mode 100644 index 0000000000..4581d988ef --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Expressions.cs @@ -0,0 +1,988 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.SymbolStore; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Dynamic; +using System.Dynamic.Binders; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + partial class LambdaCompiler { + private enum ExpressionStart { + None = 0, + LabelBlock = 1 + } + + /// + /// Generates code for this expression in a value position. + /// This method will leave the value of the expression + /// on the top of the stack typed as Type. + /// + internal void EmitExpression(Expression node) { + EmitExpression(node, true); + } + + /// + /// Emits an expression and discards the result. For some nodes this emits + /// more optimial code then EmitExpression/Pop + /// + private void EmitExpressionAsVoid(Expression node) { + Debug.Assert(node != null); + + ExpressionStart startEmitted = EmitExpressionStart(node); + + switch (node.NodeType) { + case ExpressionType.Assign: + EmitAssign((BinaryExpression)node, EmitAs.Void); + break; + case ExpressionType.Block: + Emit((BlockExpression)node, EmitAs.Void); + break; + case ExpressionType.Throw: + EmitThrow((UnaryExpression)node, EmitAs.Void); + break; + case ExpressionType.Constant: + case ExpressionType.Default: + case ExpressionType.Parameter: + // no-op + break; + default: + EmitExpression(node, false); + if (node.Type != typeof(void)) { + _ilg.Emit(OpCodes.Pop); + } + break; + } + EmitExpressionEnd(startEmitted); + } + + #region DebugMarkers + + private ExpressionStart EmitExpressionStart(Expression node) { + if (TryPushLabelBlock(node)) { + return ExpressionStart.LabelBlock; + } + return ExpressionStart.None; + } + + private void EmitExpressionEnd(ExpressionStart emitted) { + if (emitted == ExpressionStart.LabelBlock) { + PopLabelBlock(_labelBlock.Kind); + } + } + + #endregion + + #region InvocationExpression + + //CONFORMING + private void EmitInvocationExpression(Expression expr) { + InvocationExpression node = (InvocationExpression)expr; + + // Note: If node.Expression is a lambda, ExpressionCompiler inlines + // the lambda here as an optimization. We don't, for various + // reasons: + // + // * It's not necessarily optimal for large statement trees (JIT + // does better with small methods) + // * We support returning from anywhere, + // * The frame wouldn't show up in the stack trace, + // * Possibly other subtle semantic differences + // + expr = node.Expression; + if (typeof(LambdaExpression).IsAssignableFrom(expr.Type)) { + // if the invoke target is a lambda expression tree, first compile it into a delegate + expr = Expression.Call(expr, expr.Type.GetMethod("Compile", new Type[0])); + } + expr = Expression.Call(expr, expr.Type.GetMethod("Invoke"), node.Arguments); + + EmitExpression(expr); + } + + #endregion + + #region IndexExpression + + private void EmitIndexExpression(Expression expr) { + var node = (IndexExpression)expr; + + // Emit instance, if calling an instance method + Type objectType = null; + if (node.Object != null) { + EmitInstance(node.Object, objectType = node.Object.Type); + } + + // Emit indexes. We don't allow byref args, so no need to worry + // about writebacks or EmitAddress + foreach (var arg in node.Arguments) { + EmitExpression(arg); + } + + EmitGetIndexCall(node, objectType); + } + + private void EmitIndexAssignment(BinaryExpression node, EmitAs emitAs) { + var index = (IndexExpression)node.Left; + + // Emit instance, if calling an instance method + Type objectType = null; + if (index.Object != null) { + EmitInstance(index.Object, objectType = index.Object.Type); + } + + // Emit indexes. We don't allow byref args, so no need to worry + // about writebacks or EmitAddress + foreach (var arg in index.Arguments) { + EmitExpression(arg); + } + + // Emit value + EmitExpression(node.Right); + + // Save the expression value, if needed + LocalBuilder temp = null; + if (emitAs != EmitAs.Void) { + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Stloc, temp = _ilg.GetLocal(node.Type)); + } + + EmitSetIndexCall(index, objectType); + + // Restore the value + if (emitAs != EmitAs.Void) { + _ilg.Emit(OpCodes.Ldloc, temp); + _ilg.FreeLocal(temp); + } + } + + private void EmitArrayIndexAssignment(BinaryExpression node, EmitAs emitAs) { + Debug.Assert(node.NodeType == ExpressionType.ArrayIndex); + var arrayIndex = (BinaryExpression)node.Left; + + // Emit array object + EmitInstance(arrayIndex.Left, arrayIndex.Left.Type); + + // Emit index + EmitExpression(arrayIndex.Right); + + // Emit value + EmitExpression(node.Right); + + // Save the expression value, if needed + LocalBuilder temp = null; + if (emitAs != EmitAs.Void) { + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Stloc, temp = _ilg.GetLocal(node.Type)); + } + + _ilg.EmitStoreElement(arrayIndex.Type); + + // Restore the value + if (emitAs != EmitAs.Void) { + _ilg.Emit(OpCodes.Ldloc, temp); + _ilg.FreeLocal(temp); + } + } + + private void EmitGetIndexCall(IndexExpression node, Type objectType) { + if (node.Indexer != null) { + // For indexed properties, just call the getter + var method = node.Indexer.GetGetMethod(true); + EmitCall(objectType, method); + } else if (node.Arguments.Count != 1) { + // Multidimensional arrays, call get + _ilg.Emit(OpCodes.Call, node.Object.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance)); + } else { + // For one dimensional arrays, emit load + _ilg.EmitLoadElement(node.Type); + } + } + + private void EmitSetIndexCall(IndexExpression node, Type objectType) { + if (node.Indexer != null) { + // For indexed properties, just call the setter + var method = node.Indexer.GetSetMethod(true); + EmitCall(objectType, method); + } else if (node.Arguments.Count != 1) { + // Multidimensional arrays, call set + _ilg.Emit(OpCodes.Call, node.Object.Type.GetMethod("Set", BindingFlags.Public | BindingFlags.Instance)); + } else { + // For one dimensional arrays, emit store + _ilg.EmitStoreElement(node.Type); + } + } + + #endregion + + #region MethodCallExpression + + //CONFORMING + private void EmitMethodCallExpression(Expression expr) { + MethodCallExpression node = (MethodCallExpression)expr; + + EmitMethodCall(node.Object, node.Method, node); + } + + //CONFORMING + private void EmitMethodCall(Expression obj, MethodInfo method, IArgumentProvider methodCallExpr) { + // Emit instance, if calling an instance method + Type objectType = null; + if (!method.IsStatic) { + EmitInstance(obj, objectType = obj.Type); + } + + EmitMethodCall(method, methodCallExpr, objectType); + } + + //CONFORMING + // assumes 'object' of non-static call is already on stack + private void EmitMethodCall(MethodInfo mi, IArgumentProvider args, Type objectType) { + + // Emit arguments + List wb = EmitArguments(mi, args); + + // Emit the actual call + OpCode callOp = UseVirtual(mi) ? OpCodes.Callvirt : OpCodes.Call; + if (callOp == OpCodes.Callvirt && objectType.IsValueType) { + // This automatically boxes value types if necessary. + _ilg.Emit(OpCodes.Constrained, objectType); + } + if (mi.CallingConvention == CallingConventions.VarArgs) { + _ilg.EmitCall(callOp, mi, args.Map(a => a.Type)); + } else { + _ilg.Emit(callOp, mi); + } + + // Emit writebacks for properties passed as "ref" arguments + EmitWriteBack(wb); + } + + private void EmitCall(Type objectType, MethodInfo method) { + if (method.CallingConvention == CallingConventions.VarArgs) { + throw Error.UnexpectedVarArgsCall(method.FormatSignature()); + } + + OpCode callOp = UseVirtual(method) ? OpCodes.Callvirt : OpCodes.Call; + if (callOp == OpCodes.Callvirt && objectType.IsValueType) { + _ilg.Emit(OpCodes.Constrained, objectType); + } + _ilg.Emit(callOp, method); + } + + //CONFORMING + // TODO: ILGen's EmitCall only does callvirt for the (virtual, ref) + // case. We should probably fix it to work like UseVirtual, so ETs have + // the same fail-fast behavior as C# when calling with a null instance + private static bool UseVirtual(MethodInfo mi) { + // There are two factors: is the method static, virtual or non-virtual instance? + // And is the object ref or value? + // The cases are: + // + // static, ref: call + // static, value: call + // virtual, ref: callvirt + // virtual, value: call -- eg, double.ToString must be a non-virtual call to be verifiable. + // instance, ref: callvirt -- this looks wrong, but is verifiable and gives us a free null check. + // instance, value: call + // + // We never need to generate a nonvirtual call to a virtual method on a reference type because + // expression trees do not support "base.Foo()" style calling. + // + // We could do an optimization here for the case where we know that the object is a non-null + // reference type and the method is a non-virtual instance method. For example, if we had + // (new Foo()).Bar() for instance method Bar we don't need the null check so we could do a + // call rather than a callvirt. However that seems like it would not be a very big win for + // most dynamically generated code scenarios, so let's not do that for now. + + if (mi.IsStatic) { + return false; + } + if (mi.DeclaringType.IsValueType) { + return false; + } + return true; + } + + + //CONFORMING + private List EmitArguments(MethodBase method, IArgumentProvider args) { + ParameterInfo[] pis = method.GetParametersCached(); + Debug.Assert(args.ArgumentCount == pis.Length); + + var writeBacks = new List(); + for (int i = 0, n = pis.Length; i < n; i++) { + ParameterInfo parameter = pis[i]; + Expression argument = args.GetArgument(i); + Type type = parameter.ParameterType; + + if (type.IsByRef) { + type = type.GetElementType(); + + WriteBack wb = EmitAddressWriteBack(argument, type); + if (wb != null) { + writeBacks.Add(wb); + } + } else { + EmitExpression(argument); + } + } + return writeBacks; + } + + //CONFORMING + private static void EmitWriteBack(IList writeBacks) { + foreach (WriteBack wb in writeBacks) { + wb(); + } + } + + #endregion + + //CONFORMING + private void EmitConstantExpression(Expression expr) { + ConstantExpression node = (ConstantExpression)expr; + + EmitConstant(node.Value, node.Type); + } + + //CONFORMING + private void EmitConstant(object value, Type type) { + // Try to emit the constant directly into IL + if (ILGen.CanEmitConstant(value, type)) { + _ilg.EmitConstant(value, type); + return; + } + + Debug.Assert(_dynamicMethod); // constructor enforces this + + _boundConstants.EmitConstant(this, value, type); + } + + private void EmitDynamicExpression(Expression expr) { + var node = (DynamicExpression)expr; + + + // TODO: is it worthwhile to lazy initialize the CallSites? + var site = CallSite.Create(node.DelegateType, node.Binder); + Type siteType = site.GetType(); + + var invoke = node.DelegateType.GetMethod("Invoke"); + + var siteVar = Expression.Variable(siteType, null); + _scope.AddLocal(this, siteVar); + + // site.Target.Invoke(site, args) + EmitConstant(site, siteType); + _ilg.Emit(OpCodes.Dup); + _scope.EmitSet(siteVar); + _ilg.Emit(OpCodes.Ldfld, siteType.GetField("Target")); + + List wb = EmitArguments(invoke, new ArgumentPrepender(siteVar, node)); + _ilg.EmitCall(invoke); + EmitWriteBack(wb); + } + + //CONFORMING + private void EmitNewExpression(Expression expr) { + NewExpression node = (NewExpression)expr; + + if (node.Constructor != null) { + List wb = EmitArguments(node.Constructor, node); + _ilg.Emit(OpCodes.Newobj, node.Constructor); + EmitWriteBack(wb); + } else { + Debug.Assert(node.Arguments.Count == 0, "Node with arguments must have a constructor."); + Debug.Assert(node.Type.IsValueType, "Only value type may have constructor not set."); + LocalBuilder temp = _ilg.GetLocal(node.Type); + _ilg.Emit(OpCodes.Ldloca, temp); + _ilg.Emit(OpCodes.Initobj, node.Type); + _ilg.Emit(OpCodes.Ldloc, temp); + _ilg.FreeLocal(temp); + } + } + + //CONFORMING + private void EmitTypeBinaryExpression(Expression expr) { + TypeBinaryExpression node = (TypeBinaryExpression)expr; + Type type = node.Expression.Type; + + // Try to determine the result statically + AnalyzeTypeIsResult result = ConstantCheck.AnalyzeTypeIs(node); + + if (result == AnalyzeTypeIsResult.KnownTrue || + result == AnalyzeTypeIsResult.KnownFalse) { + // Result is known statically, so just emit the expression for + // its side effects and return the result + EmitExpressionAsVoid(node.Expression); + _ilg.EmitBoolean(result == AnalyzeTypeIsResult.KnownTrue); + return; + } + + if (result == AnalyzeTypeIsResult.KnownAssignable) { + // We know the type can be assigned, but still need to check + // for null at runtime + if (type.IsNullableType()) { + EmitAddress(node.Expression, type); + _ilg.EmitHasValue(type); + return; + } + + Debug.Assert(!type.IsValueType); + EmitExpression(node.Expression); + _ilg.Emit(OpCodes.Ldnull); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + return; + } + + Debug.Assert(result == AnalyzeTypeIsResult.Unknown); + + // Emit a full runtime "isinst" check + EmitExpression(node.Expression); + if (type.IsValueType) { + _ilg.Emit(OpCodes.Box, type); + } + _ilg.Emit(OpCodes.Isinst, node.TypeOperand); + _ilg.Emit(OpCodes.Ldnull); + _ilg.Emit(OpCodes.Cgt_Un); + } + + private void EmitVariableAssignment(BinaryExpression node, EmitAs emitAs) { + var variable = (ParameterExpression)node.Left; + + EmitExpression(node.Right); + if (emitAs != EmitAs.Void) { + _ilg.Emit(OpCodes.Dup); + } + + if (variable.IsByRef) { + // Note: the stloc/ldloc pattern is a bit suboptimal, but it + // saves us from having to spill stack when assigning to a + // byref parameter. We already make this same tradeoff for + // hoisted variables, see ElementStorage.EmitStore + + LocalBuilder value = _ilg.GetLocal(variable.Type); + _ilg.Emit(OpCodes.Stloc, value); + _scope.EmitGet(variable); + _ilg.Emit(OpCodes.Ldloc, value); + _ilg.FreeLocal(value); + _ilg.EmitStoreValueIndirect(variable.Type); + } else { + _scope.EmitSet(variable); + } + } + + private void EmitAssignBinaryExpression(Expression expr) { + EmitAssign((BinaryExpression)expr, EmitAs.Default); + } + + private void EmitAssign(BinaryExpression node, EmitAs emitAs) { + switch (node.Left.NodeType) { + case ExpressionType.Index: + EmitIndexAssignment(node, emitAs); + return; + case ExpressionType.MemberAccess: + EmitMemberAssignment(node, emitAs); + return; + case ExpressionType.Parameter: + EmitVariableAssignment(node, emitAs); + return; + case ExpressionType.ArrayIndex: + EmitArrayIndexAssignment(node, emitAs); + return; + default: + throw Error.InvalidLvalue(node.Left.NodeType); + } + } + + private void EmitParameterExpression(Expression expr) { + ParameterExpression node = (ParameterExpression)expr; + _scope.EmitGet(node); + if (node.IsByRef) { + _ilg.EmitLoadValueIndirect(node.Type); + } + } + + private void EmitLambdaExpression(Expression expr) { + LambdaExpression node = (LambdaExpression)expr; + EmitDelegateConstruction(node, node.Type); + } + + private void EmitRuntimeVariablesExpression(Expression expr) { + RuntimeVariablesExpression node = (RuntimeVariablesExpression)expr; + _scope.EmitVariableAccess(this, node.Variables); + } + + private void EmitMemberAssignment(BinaryExpression node, EmitAs emitAs) { + MemberExpression lvalue = (MemberExpression)node.Left; + MemberInfo member = lvalue.Member; + + // emit "this", if any + Type objectType = null; + if (lvalue.Expression != null) { + EmitInstance(lvalue.Expression, objectType = lvalue.Expression.Type); + } + + // emit value + EmitExpression(node.Right); + + LocalBuilder temp = null; + if (emitAs != EmitAs.Void) { + // save the value so we can return it + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Stloc, temp = _ilg.GetLocal(node.Type)); + } + + switch (member.MemberType) { + case MemberTypes.Field: + _ilg.EmitFieldSet((FieldInfo)member); + break; + case MemberTypes.Property: + EmitCall(objectType, ((PropertyInfo)member).GetSetMethod(true)); + break; + default: + throw Error.InvalidMemberType(member.MemberType); + } + + if (emitAs != EmitAs.Void) { + _ilg.Emit(OpCodes.Ldloc, temp); + _ilg.FreeLocal(temp); + } + } + + //CONFORMING + private void EmitMemberExpression(Expression expr) { + MemberExpression node = (MemberExpression)expr; + + // emit "this", if any + Type instanceType = null; + if (node.Expression != null) { + EmitInstance(node.Expression, instanceType = node.Expression.Type); + } + + EmitMemberGet(node.Member, instanceType); + } + + // assumes instance is already on the stack + private void EmitMemberGet(MemberInfo member, Type objectType) { + switch (member.MemberType) { + case MemberTypes.Field: + FieldInfo fi = (FieldInfo)member; + if (fi.IsLiteral) { + EmitConstant(fi.GetRawConstantValue(), fi.FieldType); + } else { + _ilg.EmitFieldGet(fi); + } + break; + case MemberTypes.Property: + EmitCall(objectType, ((PropertyInfo)member).GetGetMethod(true)); + break; + default: + throw Assert.Unreachable; + } + } + + //CONFORMING + private void EmitInstance(Expression instance, Type type) { + if (instance != null) { + if (type.IsValueType) { + EmitAddress(instance, type); + } else { + EmitExpression(instance); + } + } + } + + //CONFORMING + private void EmitNewArrayExpression(Expression expr) { + NewArrayExpression node = (NewArrayExpression)expr; + + if (node.NodeType == ExpressionType.NewArrayInit) { + _ilg.EmitArray( + node.Type.GetElementType(), + node.Expressions.Count, + delegate(int index) { + EmitExpression(node.Expressions[index]); + } + ); + } else { + ReadOnlyCollection bounds = node.Expressions; + for (int i = 0; i < bounds.Count; i++) { + Expression x = bounds[i]; + EmitExpression(x); + _ilg.EmitConvertToType(x.Type, typeof(int), true); + } + _ilg.EmitArray(node.Type); + } + } + + private void EmitDebugInfoExpression(Expression expr) { + var node = (DebugInfoExpression)expr; + + if (!_emitDebugSymbols) { + // just emit the body + EmitExpression(node.Expression); + return; + } + + var symbolWriter = GetSymbolWriter(node.Document); + _ilg.MarkSequencePoint(symbolWriter, node.StartLine, node.StartColumn, node.EndLine, node.EndColumn); + _ilg.Emit(OpCodes.Nop); + + EmitExpression(node.Expression); + + // Clear the sequence point + // TODO: we should be smarter and only emit this if needed + // Annotations need to go away first though + _ilg.MarkSequencePoint(symbolWriter, 0xfeefee, 0, 0xfeefee, 0); + _ilg.Emit(OpCodes.Nop); + } + + private ISymbolDocumentWriter GetSymbolWriter(SymbolDocumentInfo document) { + Debug.Assert(_emitDebugSymbols); + + ISymbolDocumentWriter result; + if (!_tree.SymbolWriters.TryGetValue(document, out result)) { + var module = (ModuleBuilder)_typeBuilder.Module; + result = module.DefineDocument(document.FileName, document.Language, document.LanguageVendor, SymbolGuids.DocumentType_Text); + _tree.SymbolWriters.Add(document, result); + } + + return result; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "expr")] + private static void EmitExtensionExpression(Expression expr) { + throw Error.ExtensionNotReduced(); + } + + #region ListInit, MemberInit + + private void EmitListInitExpression(Expression expr) { + EmitListInit((ListInitExpression)expr); + } + + private void EmitMemberInitExpression(Expression expr) { + EmitMemberInit((MemberInitExpression)expr); + } + + private void EmitBinding(MemberBinding binding, Type objectType) { + switch (binding.BindingType) { + case MemberBindingType.Assignment: + EmitMemberAssignment((MemberAssignment)binding, objectType); + break; + case MemberBindingType.ListBinding: + EmitMemberListBinding((MemberListBinding)binding); + break; + case MemberBindingType.MemberBinding: + EmitMemberMemberBinding((MemberMemberBinding)binding); + break; + default: + throw Error.UnknownBindingType(); + } + } + + private void EmitMemberAssignment(MemberAssignment binding, Type objectType) { + EmitExpression(binding.Expression); + FieldInfo fi = binding.Member as FieldInfo; + if (fi != null) { + _ilg.Emit(OpCodes.Stfld, fi); + } else { + PropertyInfo pi = binding.Member as PropertyInfo; + if (pi != null) { + EmitCall(objectType, pi.GetSetMethod(true)); + } else { + throw Error.UnhandledBinding(); + } + } + } + + private void EmitMemberMemberBinding(MemberMemberBinding binding) { + Type type = GetMemberType(binding.Member); + if (binding.Member is PropertyInfo && type.IsValueType) { + throw Error.CannotAutoInitializeValueTypeMemberThroughProperty(binding.Member); + } + if (type.IsValueType) { + EmitMemberAddress(binding.Member, binding.Member.DeclaringType); + } else { + EmitMemberGet(binding.Member, binding.Member.DeclaringType); + } + if (binding.Bindings.Count == 0) { + _ilg.Emit(OpCodes.Pop); + } else { + EmitMemberInit(binding.Bindings, false, type); + } + } + + private void EmitMemberListBinding(MemberListBinding binding) { + Type type = GetMemberType(binding.Member); + if (binding.Member is PropertyInfo && type.IsValueType) { + throw Error.CannotAutoInitializeValueTypeElementThroughProperty(binding.Member); + } + if (type.IsValueType) { + EmitMemberAddress(binding.Member, binding.Member.DeclaringType); + } else { + EmitMemberGet(binding.Member, binding.Member.DeclaringType); + } + EmitListInit(binding.Initializers, false, type); + } + + private void EmitMemberInit(MemberInitExpression init) { + EmitExpression(init.NewExpression); + LocalBuilder loc = null; + if (init.NewExpression.Type.IsValueType && init.Bindings.Count > 0) { + loc = _ilg.DeclareLocal(init.NewExpression.Type); + _ilg.Emit(OpCodes.Stloc, loc); + _ilg.Emit(OpCodes.Ldloca, loc); + } + EmitMemberInit(init.Bindings, loc == null, init.NewExpression.Type); + if (loc != null) { + _ilg.Emit(OpCodes.Ldloc, loc); + } + } + + private void EmitMemberInit(ReadOnlyCollection bindings, bool keepOnStack, Type objectType) { + for (int i = 0, n = bindings.Count; i < n; i++) { + if (keepOnStack || i < n - 1) { + _ilg.Emit(OpCodes.Dup); + } + EmitBinding(bindings[i], objectType); + } + } + + private void EmitListInit(ListInitExpression init) { + EmitExpression(init.NewExpression); + LocalBuilder loc = null; + if (init.NewExpression.Type.IsValueType) { + loc = _ilg.DeclareLocal(init.NewExpression.Type); + _ilg.Emit(OpCodes.Stloc, loc); + _ilg.Emit(OpCodes.Ldloca, loc); + } + EmitListInit(init.Initializers, loc == null, init.NewExpression.Type); + if (loc != null) { + _ilg.Emit(OpCodes.Ldloc, loc); + } + } + + private void EmitListInit(ReadOnlyCollection initializers, bool keepOnStack, Type objectType) { + for (int i = 0, n = initializers.Count; i < n; i++) { + if (keepOnStack || i < n - 1) { + _ilg.Emit(OpCodes.Dup); + } + EmitMethodCall(initializers[i].AddMethod, initializers[i], objectType); + + // Aome add methods, ArrayList.Add for example, return non-void + if (initializers[i].AddMethod.ReturnType != typeof(void)) { + _ilg.Emit(OpCodes.Pop); + } + } + } + + private static Type GetMemberType(MemberInfo member) { + FieldInfo fi = member as FieldInfo; + if (fi != null) return fi.FieldType; + PropertyInfo pi = member as PropertyInfo; + if (pi != null) return pi.PropertyType; + throw Error.MemberNotFieldOrProperty(member); + } + + #endregion + + #region Expression helpers + + //CONFORMING + internal static void ValidateLift(IList variables, IList arguments) { + System.Diagnostics.Debug.Assert(variables != null); + System.Diagnostics.Debug.Assert(arguments != null); + + if (variables.Count != arguments.Count) { + throw Error.IncorrectNumberOfIndexes(); + } + for (int i = 0, n = variables.Count; i < n; i++) { + if (!TypeUtils.AreReferenceAssignable(variables[i].Type, TypeUtils.GetNonNullableType(arguments[i].Type))) { + throw Error.ArgumentTypesMustMatch(); + } + } + } + + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private void EmitLift(ExpressionType nodeType, Type resultType, MethodCallExpression mc, IList parameters, IList arguments) { + Debug.Assert(TypeUtils.GetNonNullableType(resultType) == TypeUtils.GetNonNullableType(mc.Type)); + ReadOnlyCollection paramList = new ReadOnlyCollection(parameters); + ReadOnlyCollection argList = new ReadOnlyCollection(arguments); + + switch (nodeType) { + default: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: { + Label exit = _ilg.DefineLabel(); + Label exitNull = _ilg.DefineLabel(); + LocalBuilder anyNull = _ilg.DeclareLocal(typeof(bool)); + for (int i = 0, n = paramList.Count; i < n; i++) { + ParameterExpression v = paramList[i]; + Expression arg = argList[i]; + if (TypeUtils.IsNullableType(arg.Type)) { + _scope.AddLocal(this, v); + EmitAddress(arg, arg.Type); + _ilg.Emit(OpCodes.Dup); + _ilg.EmitHasValue(arg.Type); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Stloc, anyNull); + _ilg.EmitGetValueOrDefault(arg.Type); + _scope.EmitSet(v); + } else { + _scope.AddLocal(this, v); + EmitExpression(arg); + if (!arg.Type.IsValueType) { + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Ldnull); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Stloc, anyNull); + } + _scope.EmitSet(v); + } + _ilg.Emit(OpCodes.Ldloc, anyNull); + _ilg.Emit(OpCodes.Brtrue, exitNull); + } + EmitMethodCallExpression(mc); + if (TypeUtils.IsNullableType(resultType) && resultType != mc.Type) { + ConstructorInfo ci = resultType.GetConstructor(new Type[] { mc.Type }); + _ilg.Emit(OpCodes.Newobj, ci); + } + _ilg.Emit(OpCodes.Br_S, exit); + _ilg.MarkLabel(exitNull); + if (resultType == TypeUtils.GetNullableType(mc.Type)) { + if (resultType.IsValueType) { + LocalBuilder result = _ilg.GetLocal(resultType); + _ilg.Emit(OpCodes.Ldloca, result); + _ilg.Emit(OpCodes.Initobj, resultType); + _ilg.Emit(OpCodes.Ldloc, result); + _ilg.FreeLocal(result); + } else { + _ilg.Emit(OpCodes.Ldnull); + } + } else { + switch (nodeType) { + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + _ilg.Emit(OpCodes.Ldc_I4_0); + break; + default: + throw Error.UnknownLiftType(nodeType); + } + } + _ilg.MarkLabel(exit); + return; + } + case ExpressionType.Equal: + case ExpressionType.NotEqual: { + if (resultType == TypeUtils.GetNullableType(mc.Type)) { + goto default; + } + Label exit = _ilg.DefineLabel(); + Label exitAllNull = _ilg.DefineLabel(); + Label exitAnyNull = _ilg.DefineLabel(); + + LocalBuilder anyNull = _ilg.DeclareLocal(typeof(bool)); + LocalBuilder allNull = _ilg.DeclareLocal(typeof(bool)); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Stloc, anyNull); + _ilg.Emit(OpCodes.Ldc_I4_1); + _ilg.Emit(OpCodes.Stloc, allNull); + + for (int i = 0, n = paramList.Count; i < n; i++) { + ParameterExpression v = paramList[i]; + Expression arg = argList[i]; + _scope.AddLocal(this, v); + if (TypeUtils.IsNullableType(arg.Type)) { + EmitAddress(arg, arg.Type); + _ilg.Emit(OpCodes.Dup); + _ilg.EmitHasValue(arg.Type); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Ldloc, anyNull); + _ilg.Emit(OpCodes.Or); + _ilg.Emit(OpCodes.Stloc, anyNull); + _ilg.Emit(OpCodes.Ldloc, allNull); + _ilg.Emit(OpCodes.And); + _ilg.Emit(OpCodes.Stloc, allNull); + _ilg.EmitGetValueOrDefault(arg.Type); + } else { + EmitExpression(arg); + if (!arg.Type.IsValueType) { + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Ldnull); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Ldloc, anyNull); + _ilg.Emit(OpCodes.Or); + _ilg.Emit(OpCodes.Stloc, anyNull); + _ilg.Emit(OpCodes.Ldloc, allNull); + _ilg.Emit(OpCodes.And); + _ilg.Emit(OpCodes.Stloc, allNull); + } else { + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Stloc, allNull); + } + } + _scope.EmitSet(v); + } + _ilg.Emit(OpCodes.Ldloc, allNull); + _ilg.Emit(OpCodes.Brtrue, exitAllNull); + _ilg.Emit(OpCodes.Ldloc, anyNull); + _ilg.Emit(OpCodes.Brtrue, exitAnyNull); + + EmitMethodCallExpression(mc); + if (TypeUtils.IsNullableType(resultType) && resultType != mc.Type) { + ConstructorInfo ci = resultType.GetConstructor(new Type[] { mc.Type }); + _ilg.Emit(OpCodes.Newobj, ci); + } + _ilg.Emit(OpCodes.Br_S, exit); + + _ilg.MarkLabel(exitAllNull); + _ilg.EmitBoolean(nodeType == ExpressionType.Equal); + _ilg.Emit(OpCodes.Br_S, exit); + + _ilg.MarkLabel(exitAnyNull); + _ilg.EmitBoolean(nodeType == ExpressionType.NotEqual); + + _ilg.MarkLabel(exit); + return; + } + } + } + + #endregion + + enum EmitAs { + Default, + Void + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Generated.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Generated.cs new file mode 100644 index 0000000000..304e75aa7a --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Generated.cs @@ -0,0 +1,262 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + partial class LambdaCompiler { + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private void EmitExpression(Expression node, bool emitStart) { + Debug.Assert(node != null); + + ExpressionStart startEmitted = emitStart ? EmitExpressionStart(node) : ExpressionStart.None; + + switch (node.NodeType) { + #region Generated Expression Compiler + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_compiler from: generate_tree.py + + case ExpressionType.Add: + EmitBinaryExpression(node); + break; + case ExpressionType.AddChecked: + EmitBinaryExpression(node); + break; + case ExpressionType.And: + EmitBinaryExpression(node); + break; + case ExpressionType.AndAlso: + EmitAndAlsoBinaryExpression(node); + break; + case ExpressionType.ArrayLength: + EmitUnaryExpression(node); + break; + case ExpressionType.ArrayIndex: + EmitBinaryExpression(node); + break; + case ExpressionType.Call: + EmitMethodCallExpression(node); + break; + case ExpressionType.Coalesce: + EmitCoalesceBinaryExpression(node); + break; + case ExpressionType.Conditional: + EmitConditionalExpression(node); + break; + case ExpressionType.Constant: + EmitConstantExpression(node); + break; + case ExpressionType.Convert: + EmitConvertUnaryExpression(node); + break; + case ExpressionType.ConvertChecked: + EmitConvertUnaryExpression(node); + break; + case ExpressionType.Divide: + EmitBinaryExpression(node); + break; + case ExpressionType.Equal: + EmitBinaryExpression(node); + break; + case ExpressionType.ExclusiveOr: + EmitBinaryExpression(node); + break; + case ExpressionType.GreaterThan: + EmitBinaryExpression(node); + break; + case ExpressionType.GreaterThanOrEqual: + EmitBinaryExpression(node); + break; + case ExpressionType.Invoke: + EmitInvocationExpression(node); + break; + case ExpressionType.Lambda: + EmitLambdaExpression(node); + break; + case ExpressionType.LeftShift: + EmitBinaryExpression(node); + break; + case ExpressionType.LessThan: + EmitBinaryExpression(node); + break; + case ExpressionType.LessThanOrEqual: + EmitBinaryExpression(node); + break; + case ExpressionType.ListInit: + EmitListInitExpression(node); + break; + case ExpressionType.MemberAccess: + EmitMemberExpression(node); + break; + case ExpressionType.MemberInit: + EmitMemberInitExpression(node); + break; + case ExpressionType.Modulo: + EmitBinaryExpression(node); + break; + case ExpressionType.Multiply: + EmitBinaryExpression(node); + break; + case ExpressionType.MultiplyChecked: + EmitBinaryExpression(node); + break; + case ExpressionType.Negate: + EmitUnaryExpression(node); + break; + case ExpressionType.UnaryPlus: + EmitUnaryExpression(node); + break; + case ExpressionType.NegateChecked: + EmitUnaryExpression(node); + break; + case ExpressionType.New: + EmitNewExpression(node); + break; + case ExpressionType.NewArrayInit: + EmitNewArrayExpression(node); + break; + case ExpressionType.NewArrayBounds: + EmitNewArrayExpression(node); + break; + case ExpressionType.Not: + EmitUnaryExpression(node); + break; + case ExpressionType.NotEqual: + EmitBinaryExpression(node); + break; + case ExpressionType.Or: + EmitBinaryExpression(node); + break; + case ExpressionType.OrElse: + EmitOrElseBinaryExpression(node); + break; + case ExpressionType.Parameter: + EmitParameterExpression(node); + break; + case ExpressionType.Power: + EmitBinaryExpression(node); + break; + case ExpressionType.Quote: + EmitQuoteUnaryExpression(node); + break; + case ExpressionType.RightShift: + EmitBinaryExpression(node); + break; + case ExpressionType.Subtract: + EmitBinaryExpression(node); + break; + case ExpressionType.SubtractChecked: + EmitBinaryExpression(node); + break; + case ExpressionType.TypeAs: + EmitUnaryExpression(node); + break; + case ExpressionType.TypeIs: + EmitTypeBinaryExpression(node); + break; + case ExpressionType.Assign: + EmitAssignBinaryExpression(node); + break; + case ExpressionType.Block: + EmitBlockExpression(node); + break; + case ExpressionType.DebugInfo: + EmitDebugInfoExpression(node); + break; + case ExpressionType.Decrement: + EmitUnaryExpression(node); + break; + case ExpressionType.Dynamic: + EmitDynamicExpression(node); + break; + case ExpressionType.Default: + EmitEmptyExpression(node); + break; + case ExpressionType.Extension: + EmitExtensionExpression(node); + break; + case ExpressionType.Goto: + EmitGotoExpression(node); + break; + case ExpressionType.Increment: + EmitUnaryExpression(node); + break; + case ExpressionType.Index: + EmitIndexExpression(node); + break; + case ExpressionType.Label: + EmitLabelExpression(node); + break; + case ExpressionType.RuntimeVariables: + EmitRuntimeVariablesExpression(node); + break; + case ExpressionType.Loop: + EmitLoopExpression(node); + break; + case ExpressionType.Switch: + EmitSwitchExpression(node); + break; + case ExpressionType.Throw: + EmitThrowUnaryExpression(node); + break; + case ExpressionType.Try: + EmitTryExpression(node); + break; + case ExpressionType.Unbox: + EmitUnboxUnaryExpression(node); + break; + + // *** END GENERATED CODE *** + + #endregion + + default: + throw Assert.Unreachable; + } + + if (emitStart) { + EmitExpressionEnd(startEmitted); + } + } + + private static bool IsChecked(ExpressionType op) { + switch (op) { + #region Generated Checked Operations + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_checked_ops from: generate_tree.py + + case ExpressionType.AddChecked: + case ExpressionType.ConvertChecked: + case ExpressionType.MultiplyChecked: + case ExpressionType.NegateChecked: + case ExpressionType.SubtractChecked: + case ExpressionType.AddAssignChecked: + case ExpressionType.MultiplyAssignChecked: + case ExpressionType.SubtractAssignChecked: + + // *** END GENERATED CODE *** + + #endregion + return true; + } + return false; + } + + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Lambda.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Lambda.cs new file mode 100644 index 0000000000..dd3be0de0a --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Lambda.cs @@ -0,0 +1,182 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.SymbolStore; +using System.Dynamic.Utils; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Linq.Expressions.Compiler { + + /// + /// Dynamic Language Runtime Compiler. + /// This part compiles lambdas. + /// + partial class LambdaCompiler { + private static int _Counter; + + internal void EmitConstantArray(T[] array) { + // Emit as runtime constant if possible + // if not, emit into IL + if (_dynamicMethod) { + EmitConstant(array, typeof(T[])); + } else if(_typeBuilder != null) { + // store into field in our type builder, we will initialize + // the value only once. + FieldBuilder fb = _typeBuilder.DefineField("constantArray" + typeof(T).Name.Replace('.', '_').Replace('+', '_') + Interlocked.Increment(ref _Counter), typeof(T[]), FieldAttributes.Static | FieldAttributes.Private); + Label l = _ilg.DefineLabel(); + _ilg.Emit(OpCodes.Ldsfld, fb); + _ilg.Emit(OpCodes.Ldnull); + _ilg.Emit(OpCodes.Bne_Un, l); + _ilg.EmitArray(array); + _ilg.Emit(OpCodes.Stsfld, fb); + _ilg.MarkLabel(l); + _ilg.Emit(OpCodes.Ldsfld, fb); + } else { + _ilg.EmitArray(array); + } + } + + private void EmitClosureCreation(LambdaCompiler inner) { + bool closure = inner._scope.NeedsClosure; + bool boundConstants = inner._boundConstants.Count > 0; + + if (!closure && !boundConstants) { + _ilg.EmitNull(); + return; + } + + // new Closure(constantPool, currentHoistedLocals) + if (boundConstants) { + _boundConstants.EmitConstant(this, inner._boundConstants.ToArray(), typeof(object[])); + } else { + _ilg.EmitNull(); + } + if (closure) { + _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable); + } else { + _ilg.EmitNull(); + } + _ilg.EmitNew(typeof(Closure).GetConstructor(new Type[] { typeof(object[]), typeof(object[]) })); + } + + /// + /// Emits code which creates new instance of the delegateType delegate. + /// + /// Since the delegate is getting closed over the "Closure" argument, this + /// cannot be used with virtual/instance methods (inner must be static method) + /// + private void EmitDelegateConstruction(LambdaCompiler inner, Type delegateType) { + DynamicMethod dynamicMethod = inner._method as DynamicMethod; + if (dynamicMethod != null) { + // dynamicMethod.CreateDelegate(delegateType, closure) + _boundConstants.EmitConstant(this, dynamicMethod, typeof(DynamicMethod)); + _ilg.EmitType(delegateType); + EmitClosureCreation(inner); + _ilg.EmitCall(typeof(DynamicMethod).GetMethod("CreateDelegate", new Type[] { typeof(Type), typeof(object) })); + _ilg.Emit(OpCodes.Castclass, delegateType); + } else { + // new DelegateType(closure) + EmitClosureCreation(inner); + _ilg.Emit(OpCodes.Ldftn, (MethodInfo)inner._method); + _ilg.Emit(OpCodes.Newobj, (ConstructorInfo)(delegateType.GetMember(".ctor")[0])); + } + } + + /// + /// Emits a delegate to the method generated for the LambdaExpression. + /// May end up creating a wrapper to match the requested delegate type. + /// + /// Lambda for which to generate a delegate + /// Type of the delegate. + private void EmitDelegateConstruction(LambdaExpression lambda, Type delegateType) { + // 1. create the signature + List paramTypes; + List paramNames; + string implName; + Type returnType; + ComputeSignature(lambda, out paramTypes, out paramNames, out implName, out returnType); + + // 2. create the new compiler + LambdaCompiler impl; + if (_dynamicMethod) { + impl = CreateDynamicCompiler(_tree, lambda, implName, returnType, paramTypes, paramNames, _emitDebugSymbols, _method is DynamicMethod); + } else { + impl = CreateStaticCompiler(_tree, lambda, _typeBuilder, implName, TypeUtils.PublicStatic, returnType, paramTypes, paramNames, _dynamicMethod, _emitDebugSymbols); + } + + // 3. emit the lambda + impl.EmitLambdaBody(_scope); + + // 4. emit the delegate creation in the outer lambda + EmitDelegateConstruction(impl, delegateType); + } + + /// + /// Creates the signature for the lambda as list of types and list of names separately + /// + private static void ComputeSignature( + LambdaExpression lambda, + out List paramTypes, + out List paramNames, + out string implName, + out Type returnType) { + + paramTypes = new List(); + paramNames = new List(); + + foreach (ParameterExpression p in lambda.Parameters) { + paramTypes.Add(p.IsByRef ? p.Type.MakeByRefType() : p.Type); + paramNames.Add(p.Name); + } + + implName = GetGeneratedName(lambda.Name); + returnType = lambda.ReturnType; + } + + private static string GetGeneratedName(string prefix) { + return prefix + "$" + Interlocked.Increment(ref _Counter); + } + + private void EmitLambdaBody(CompilerScope parent) { + _scope.Enter(this, parent); + + Type returnType = _method.GetReturnType(); + if (returnType == typeof(void)) { + EmitExpressionAsVoid(_lambda.Body); + } else { + Debug.Assert(_lambda.Body.Type != typeof(void)); + EmitExpression(_lambda.Body); + } + //must be the last instruction in the body + _ilg.Emit(OpCodes.Ret); + _scope.Exit(); + + // Validate labels + Debug.Assert(_labelBlock.Parent == null && _labelBlock.Kind == LabelBlockKind.Block); + foreach (LabelInfo label in _labelInfo.Values) { + label.ValidateFinish(); + } + + if (_dynamicMethod) { + CreateDelegateMethodInfo(); + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Logical.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Logical.cs new file mode 100644 index 0000000000..fb34edb93e --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Logical.cs @@ -0,0 +1,724 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using System.Dynamic; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + + partial class LambdaCompiler { + + #region Conditional + + //CONFORMING + private void EmitConditionalExpression(Expression expr) { + ConditionalExpression node = (ConditionalExpression)expr; + Debug.Assert(node.Test.Type == typeof(bool) && node.IfTrue.Type == node.IfFalse.Type); + + Label labFalse = _ilg.DefineLabel(); + EmitExpressionAndBranch(false, node.Test, labFalse); + EmitExpression(node.IfTrue); + + if (Significant(node.IfFalse)) { + Label labEnd = _ilg.DefineLabel(); + _ilg.Emit(OpCodes.Br, labEnd); + _ilg.MarkLabel(labFalse); + EmitExpression(node.IfFalse); + _ilg.MarkLabel(labEnd); + } else { + _ilg.MarkLabel(labFalse); + } + } + + /// + /// Expression is significant if: + /// * it is not an empty expression + /// == or == + /// * it is an empty expression, and + /// * it has a valid span, and + /// * we are emitting debug symbols + /// + private static bool Significant(Expression node) { + var empty = node as DefaultExpression; + if (empty == null || empty.Type != typeof(void)) { + // non-empty expression is significant + return true; + } + + // Not a significant expression + return false; + } + + #endregion + + #region Coalesce + + //CONFORMING + private void EmitCoalesceBinaryExpression(Expression expr) { + BinaryExpression b = (BinaryExpression)expr; + Debug.Assert(b.Method == null); + + if (TypeUtils.IsNullableType(b.Left.Type)) { + EmitNullableCoalesce(b); + } else if (b.Left.Type.IsValueType) { + throw Error.CoalesceUsedOnNonNullType(); + } else if (b.Conversion != null) { + EmitLambdaReferenceCoalesce(b); + } else { + EmitReferenceCoalesceWithoutConversion(b); + } + } + + //CONFORMING + private void EmitNullableCoalesce(BinaryExpression b) { + LocalBuilder loc = _ilg.GetLocal(b.Left.Type); + Label labIfNull = _ilg.DefineLabel(); + Label labEnd = _ilg.DefineLabel(); + EmitExpression(b.Left); + _ilg.Emit(OpCodes.Stloc, loc); + _ilg.Emit(OpCodes.Ldloca, loc); + _ilg.EmitHasValue(b.Left.Type); + _ilg.Emit(OpCodes.Brfalse, labIfNull); + + Type nnLeftType = TypeUtils.GetNonNullableType(b.Left.Type); + if (b.Method != null) { + ParameterInfo[] parameters = b.Method.GetParametersCached(); + Debug.Assert(b.Method.IsStatic); + Debug.Assert(parameters.Length == 1); + Debug.Assert(parameters[0].ParameterType.IsAssignableFrom(b.Left.Type) || + parameters[0].ParameterType.IsAssignableFrom(nnLeftType)); + if (!parameters[0].ParameterType.IsAssignableFrom(b.Left.Type)) { + _ilg.Emit(OpCodes.Ldloca, loc); + _ilg.EmitGetValueOrDefault(b.Left.Type); + } else + _ilg.Emit(OpCodes.Ldloc, loc); + _ilg.Emit(OpCodes.Call, b.Method); + } else if (b.Conversion != null) { + Debug.Assert(b.Conversion.Parameters.Count == 1); + ParameterExpression p = b.Conversion.Parameters[0]; + Debug.Assert(p.Type.IsAssignableFrom(b.Left.Type) || + p.Type.IsAssignableFrom(nnLeftType)); + + // emit the delegate instance + EmitLambdaExpression(b.Conversion); + + // emit argument + if (!p.Type.IsAssignableFrom(b.Left.Type)) { + _ilg.Emit(OpCodes.Ldloca, loc); + _ilg.EmitGetValueOrDefault(b.Left.Type); + } else { + _ilg.Emit(OpCodes.Ldloc, loc); + } + + // emit call to invoke + _ilg.EmitCall(b.Conversion.Type.GetMethod("Invoke")); + + } else if (b.Type != nnLeftType) { + _ilg.Emit(OpCodes.Ldloca, loc); + _ilg.EmitGetValueOrDefault(b.Left.Type); + _ilg.EmitConvertToType(nnLeftType, b.Type, true); + } else { + _ilg.Emit(OpCodes.Ldloca, loc); + _ilg.EmitGetValueOrDefault(b.Left.Type); + } + _ilg.FreeLocal(loc); + + _ilg.Emit(OpCodes.Br, labEnd); + _ilg.MarkLabel(labIfNull); + EmitExpression(b.Right); + if (b.Right.Type != b.Type) { + _ilg.EmitConvertToType(b.Right.Type, b.Type, true); + } + _ilg.MarkLabel(labEnd); + } + + //CONFORMING + private void EmitLambdaReferenceCoalesce(BinaryExpression b) { + LocalBuilder loc = _ilg.GetLocal(b.Left.Type); + Label labEnd = _ilg.DefineLabel(); + Label labNotNull = _ilg.DefineLabel(); + EmitExpression(b.Left); + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Stloc, loc); + _ilg.Emit(OpCodes.Ldnull); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Brfalse, labNotNull); + EmitExpression(b.Right); + _ilg.Emit(OpCodes.Br, labEnd); + + // if not null, call conversion + _ilg.MarkLabel(labNotNull); + Debug.Assert(b.Conversion.Parameters.Count == 1); + ParameterExpression p = b.Conversion.Parameters[0]; + + // emit the delegate instance + EmitLambdaExpression(b.Conversion); + + // emit argument + _ilg.Emit(OpCodes.Ldloc, loc); + _ilg.FreeLocal(loc); + + // emit call to invoke + _ilg.EmitCall(b.Conversion.Type.GetMethod("Invoke")); + + _ilg.MarkLabel(labEnd); + } + + //CONFORMING + private void EmitReferenceCoalesceWithoutConversion(BinaryExpression b) { + Label labEnd = _ilg.DefineLabel(); + Label labCast = _ilg.DefineLabel(); + EmitExpression(b.Left); + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Ldnull); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Brfalse, labCast); + _ilg.Emit(OpCodes.Pop); + EmitExpression(b.Right); + if (b.Right.Type != b.Type) { + _ilg.Emit(OpCodes.Castclass, b.Type); + } + _ilg.Emit(OpCodes.Br_S, labEnd); + _ilg.MarkLabel(labCast); + if (b.Left.Type != b.Type) { + _ilg.Emit(OpCodes.Castclass, b.Type); + } + _ilg.MarkLabel(labEnd); + } + + #endregion + + #region AndAlso + + // for a userdefined type T which has Op_False defined and Lhs, Rhs are nullable L AndAlso R is computed as + // L.HasValue + // ? (T.False(L.Value) + // ? L + // : (R.HasValue + // ? (T?)(T.&(L.Value, R.Value)) + // : R)) + // : L + private void EmitUserdefinedLiftedAndAlso(BinaryExpression b) { + Type type = b.Left.Type; + Type nnType = TypeUtils.GetNonNullableType(type); + Label labReturnLeft = _ilg.DefineLabel(); + Label labReturnRight = _ilg.DefineLabel(); + Label labExit = _ilg.DefineLabel(); + + LocalBuilder locLeft = _ilg.GetLocal(type); + LocalBuilder locRight = _ilg.GetLocal(type); + LocalBuilder locNNLeft = _ilg.GetLocal(nnType); + LocalBuilder locNNRight = _ilg.GetLocal(nnType); + + // load left + EmitExpression(b.Left); + _ilg.Emit(OpCodes.Stloc, locLeft); + + //check left + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse, labExit); + + //try false on left + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitGetValueOrDefault(type); + MethodInfo opFalse = TypeUtils.GetBooleanOperator(b.Method.DeclaringType, "op_False"); + Debug.Assert(opFalse != null, "factory should check that the method exists"); + _ilg.Emit(OpCodes.Call, opFalse); + _ilg.Emit(OpCodes.Brtrue, labExit); + + //load right + EmitExpression(b.Right); + _ilg.Emit(OpCodes.Stloc, locRight); + + // Check right + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse, labReturnRight); + + //Compute bitwise And + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitGetValueOrDefault(type); + _ilg.Emit(OpCodes.Stloc, locNNLeft); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitGetValueOrDefault(type); + _ilg.Emit(OpCodes.Stloc, locNNRight); + Debug.Assert(b.Method.Name == "op_BitwiseAnd"); + _ilg.Emit(OpCodes.Ldloc, locNNLeft); + _ilg.Emit(OpCodes.Ldloc, locNNRight); + _ilg.Emit(OpCodes.Call, b.Method); + if (b.Method.ReturnType != type) { + _ilg.EmitConvertToType(b.Method.ReturnType, type, true); + } + _ilg.Emit(OpCodes.Stloc, locLeft); + _ilg.Emit(OpCodes.Br, labExit); + + //return right + _ilg.MarkLabel(labReturnRight); + _ilg.Emit(OpCodes.Ldloc, locRight); + _ilg.Emit(OpCodes.Stloc, locLeft); + _ilg.MarkLabel(labExit); + //return left + _ilg.Emit(OpCodes.Ldloc, locLeft); + + _ilg.FreeLocal(locLeft); + _ilg.FreeLocal(locRight); + _ilg.FreeLocal(locNNLeft); + _ilg.FreeLocal(locNNRight); + } + + private void EmitLiftedAndAlso(BinaryExpression b) { + Type type = typeof(bool?); + Label labComputeRight = _ilg.DefineLabel(); + Label labReturnFalse = _ilg.DefineLabel(); + Label labReturnNull = _ilg.DefineLabel(); + Label labReturnValue = _ilg.DefineLabel(); + Label labExit = _ilg.DefineLabel(); + LocalBuilder locLeft = _ilg.GetLocal(type); + LocalBuilder locRight = _ilg.GetLocal(type); + EmitExpression(b.Left); + _ilg.Emit(OpCodes.Stloc, locLeft); + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse, labComputeRight); + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitGetValueOrDefault(type); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Brtrue, labReturnFalse); + // compute right + _ilg.MarkLabel(labComputeRight); + EmitExpression(b.Right); + _ilg.Emit(OpCodes.Stloc, locRight); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse_S, labReturnNull); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitGetValueOrDefault(type); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Brtrue_S, labReturnFalse); + // check left for null again + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse, labReturnNull); + // return true + _ilg.Emit(OpCodes.Ldc_I4_1); + _ilg.Emit(OpCodes.Br_S, labReturnValue); + // return false + _ilg.MarkLabel(labReturnFalse); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Br_S, labReturnValue); + _ilg.MarkLabel(labReturnValue); + ConstructorInfo ci = type.GetConstructor(new Type[] { typeof(bool) }); + _ilg.Emit(OpCodes.Newobj, ci); + _ilg.Emit(OpCodes.Stloc, locLeft); + _ilg.Emit(OpCodes.Br, labExit); + // return null + _ilg.MarkLabel(labReturnNull); + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.Emit(OpCodes.Initobj, type); + _ilg.MarkLabel(labExit); + _ilg.Emit(OpCodes.Ldloc, locLeft); + _ilg.FreeLocal(locLeft); + _ilg.FreeLocal(locRight); + } + + private void EmitMethodAndAlso(BinaryExpression b) { + Label labEnd = _ilg.DefineLabel(); + EmitExpression(b.Left); + _ilg.Emit(OpCodes.Dup); + MethodInfo opFalse = TypeUtils.GetBooleanOperator(b.Method.DeclaringType, "op_False"); + Debug.Assert(opFalse != null, "factory should check that the method exists"); + _ilg.Emit(OpCodes.Call, opFalse); + _ilg.Emit(OpCodes.Brtrue, labEnd); + EmitExpression(b.Right); + Debug.Assert(b.Method.IsStatic); + _ilg.Emit(OpCodes.Call, b.Method); + _ilg.MarkLabel(labEnd); + } + + private void EmitUnliftedAndAlso(BinaryExpression b) { + Label @else = _ilg.DefineLabel(); + Label end = _ilg.DefineLabel(); + EmitExpressionAndBranch(false, b.Left, @else); + EmitExpression(b.Right); + _ilg.Emit(OpCodes.Br, end); + _ilg.MarkLabel(@else); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.MarkLabel(end); + } + + private void EmitAndAlsoBinaryExpression(Expression expr) { + BinaryExpression b = (BinaryExpression)expr; + + if (b.Method != null && !IsLiftedLogicalBinaryOperator(b.Left.Type, b.Right.Type, b.Method)) { + EmitMethodAndAlso(b); + } else if (b.Left.Type == typeof(bool?)) { + EmitLiftedAndAlso(b); + } else if (IsLiftedLogicalBinaryOperator(b.Left.Type, b.Right.Type, b.Method)) { + EmitUserdefinedLiftedAndAlso(b); + } else { + EmitUnliftedAndAlso(b); + } + } + + #endregion + + #region OrElse + + // For a userdefined type T which has Op_True defined and Lhs, Rhs are nullable L OrElse R is computed as + // L.HasValue + // ? (T.True(L.Value) + // ? L + // : (R.HasValue + // ? (T?)(T.|(L.Value, R.Value)) + // : R)) + // : R + private void EmitUserdefinedLiftedOrElse(BinaryExpression b) { + Type type = b.Left.Type; + Type nnType = TypeUtils.GetNonNullableType(type); + Label labReturnLeft = _ilg.DefineLabel(); + Label labReturnRight = _ilg.DefineLabel(); + Label labExit = _ilg.DefineLabel(); + + LocalBuilder locLeft = _ilg.GetLocal(type); + LocalBuilder locRight = _ilg.GetLocal(type); + LocalBuilder locNNLeft = _ilg.GetLocal(nnType); + LocalBuilder locNNRight = _ilg.GetLocal(nnType); + + // Load left + EmitExpression(b.Left); + _ilg.Emit(OpCodes.Stloc, locLeft); + + // Check left + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse, labReturnRight); + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitGetValueOrDefault(type); + MethodInfo opTrue = TypeUtils.GetBooleanOperator(b.Method.DeclaringType, "op_True"); + Debug.Assert(opTrue != null, "factory should check that the method exists"); + _ilg.Emit(OpCodes.Call, opTrue); + _ilg.Emit(OpCodes.Brtrue, labExit); + + // Load right + EmitExpression(b.Right); + _ilg.Emit(OpCodes.Stloc, locRight); + + // Check right + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse, labReturnRight); + + //Compute bitwise Or + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitGetValueOrDefault(type); + _ilg.Emit(OpCodes.Stloc, locNNLeft); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitGetValueOrDefault(type); + _ilg.Emit(OpCodes.Stloc, locNNRight); + _ilg.Emit(OpCodes.Ldloc, locNNLeft); + _ilg.Emit(OpCodes.Ldloc, locNNRight); + Debug.Assert(b.Method.Name == "op_BitwiseOr"); + _ilg.Emit(OpCodes.Call, b.Method); + if (b.Method.ReturnType != type) { + _ilg.EmitConvertToType(b.Method.ReturnType, type, true); + } + _ilg.Emit(OpCodes.Stloc, locLeft); + _ilg.Emit(OpCodes.Br, labExit); + //return right + _ilg.MarkLabel(labReturnRight); + _ilg.Emit(OpCodes.Ldloc, locRight); + _ilg.Emit(OpCodes.Stloc, locLeft); + _ilg.MarkLabel(labExit); + //return left + _ilg.Emit(OpCodes.Ldloc, locLeft); + + _ilg.FreeLocal(locNNLeft); + _ilg.FreeLocal(locNNRight); + _ilg.FreeLocal(locLeft); + _ilg.FreeLocal(locRight); + } + + private void EmitLiftedOrElse(BinaryExpression b) { + Type type = typeof(bool?); + Label labComputeRight = _ilg.DefineLabel(); + Label labReturnTrue = _ilg.DefineLabel(); + Label labReturnNull = _ilg.DefineLabel(); + Label labReturnValue = _ilg.DefineLabel(); + Label labExit = _ilg.DefineLabel(); + LocalBuilder locLeft = _ilg.GetLocal(type); + LocalBuilder locRight = _ilg.GetLocal(type); + EmitExpression(b.Left); + _ilg.Emit(OpCodes.Stloc, locLeft); + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse, labComputeRight); + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitGetValueOrDefault(type); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Brfalse, labReturnTrue); + // compute right + _ilg.MarkLabel(labComputeRight); + EmitExpression(b.Right); + _ilg.Emit(OpCodes.Stloc, locRight); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse_S, labReturnNull); + _ilg.Emit(OpCodes.Ldloca, locRight); + _ilg.EmitGetValueOrDefault(type); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Brfalse_S, labReturnTrue); + // check left for null again + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.EmitHasValue(type); + _ilg.Emit(OpCodes.Brfalse, labReturnNull); + // return false + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Br_S, labReturnValue); + // return true + _ilg.MarkLabel(labReturnTrue); + _ilg.Emit(OpCodes.Ldc_I4_1); + _ilg.Emit(OpCodes.Br_S, labReturnValue); + _ilg.MarkLabel(labReturnValue); + ConstructorInfo ci = type.GetConstructor(new Type[] { typeof(bool) }); + _ilg.Emit(OpCodes.Newobj, ci); + _ilg.Emit(OpCodes.Stloc, locLeft); + _ilg.Emit(OpCodes.Br, labExit); + // return null + _ilg.MarkLabel(labReturnNull); + _ilg.Emit(OpCodes.Ldloca, locLeft); + _ilg.Emit(OpCodes.Initobj, type); + _ilg.MarkLabel(labExit); + _ilg.Emit(OpCodes.Ldloc, locLeft); + _ilg.FreeLocal(locLeft); + _ilg.FreeLocal(locRight); + } + + private void EmitUnliftedOrElse(BinaryExpression b) { + Label @else = _ilg.DefineLabel(); + Label end = _ilg.DefineLabel(); + EmitExpressionAndBranch(false, b.Left, @else); + _ilg.Emit(OpCodes.Ldc_I4_1); + _ilg.Emit(OpCodes.Br, end); + _ilg.MarkLabel(@else); + EmitExpression(b.Right); + _ilg.MarkLabel(end); + } + + private void EmitMethodOrElse(BinaryExpression b) { + Label labEnd = _ilg.DefineLabel(); + EmitExpression(b.Left); + _ilg.Emit(OpCodes.Dup); + MethodInfo opTrue = TypeUtils.GetBooleanOperator(b.Method.DeclaringType, "op_True"); + Debug.Assert(opTrue != null, "factory should check that the method exists"); + _ilg.Emit(OpCodes.Call, opTrue); + _ilg.Emit(OpCodes.Brtrue, labEnd); + EmitExpression(b.Right); + Debug.Assert(b.Method.IsStatic); + _ilg.Emit(OpCodes.Call, b.Method); + _ilg.MarkLabel(labEnd); + } + + private void EmitOrElseBinaryExpression(Expression expr) { + BinaryExpression b = (BinaryExpression)expr; + + if (b.Method != null && !IsLiftedLogicalBinaryOperator(b.Left.Type, b.Right.Type, b.Method)) { + EmitMethodOrElse(b); + } else if (b.Left.Type == typeof(bool?)) { + EmitLiftedOrElse(b); + } else if (IsLiftedLogicalBinaryOperator(b.Left.Type, b.Right.Type, b.Method)) { + EmitUserdefinedLiftedOrElse(b); + } else { + EmitUnliftedOrElse(b); + } + } + + #endregion + + private static bool IsLiftedLogicalBinaryOperator(Type left, Type right, MethodInfo method) { + return right == left && + TypeUtils.IsNullableType(left) && + method != null && + method.ReturnType == TypeUtils.GetNonNullableType(left); + } + + #region Optimized branching + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + private void EmitExpressionAndBranch(bool branchValue, Expression node, Label label) { + if (node.Type == typeof(bool)) { + switch (node.NodeType) { + case ExpressionType.AndAlso: + case ExpressionType.OrElse: + EmitBranchLogical(branchValue, (BinaryExpression)node, label); + return; + case ExpressionType.Block: + EmitBranchBlock(branchValue, (BlockExpression)node, label); + return; + case ExpressionType.Equal: + case ExpressionType.NotEqual: + EmitBranchComparison(branchValue, (BinaryExpression)node, label); + return; + } + } + EmitExpression(node); + EmitBranchOp(branchValue, label); + } + + private void EmitBranchOp(bool branch, Label label) { + _ilg.Emit(branch ? OpCodes.Brtrue : OpCodes.Brfalse, label); + } + + private void EmitBranchComparison(bool branch, BinaryExpression node, Label label) { + Debug.Assert(node.NodeType == ExpressionType.Equal || node.NodeType == ExpressionType.NotEqual); + Debug.Assert(!node.IsLiftedToNull); + + // To share code paths, we want to treat NotEqual as an inverted Equal + bool branchWhenEqual = branch == (node.NodeType == ExpressionType.Equal); + + if (node.Method != null) { + EmitBinaryMethod(node); + // EmitBinaryMethod takes into account the Equal/NotEqual + // node kind, so use the original branch value + EmitBranchOp(branch, label); + } else if (ConstantCheck.IsNull(node.Left)) { + if (TypeUtils.IsNullableType(node.Right.Type)) { + EmitAddress(node.Right, node.Right.Type); + _ilg.EmitHasValue(node.Right.Type); + } else { + Debug.Assert(!node.Right.Type.IsValueType); + EmitExpression(node.Right); + } + EmitBranchOp(!branchWhenEqual, label); + } else if (ConstantCheck.IsNull(node.Right)) { + if (TypeUtils.IsNullableType(node.Left.Type)) { + EmitAddress(node.Left, node.Left.Type); + _ilg.EmitHasValue(node.Left.Type); + } else { + Debug.Assert(!node.Left.Type.IsValueType); + EmitExpression(node.Left); + } + EmitBranchOp(!branchWhenEqual, label); + } else if (TypeUtils.IsNullableType(node.Left.Type) || TypeUtils.IsNullableType(node.Right.Type)) { + EmitBinaryExpression(node); + // EmitBinaryExpression takes into account the Equal/NotEqual + // node kind, so use the original branch value + EmitBranchOp(branch, label); + } else { + EmitExpression(node.Left); + EmitExpression(node.Right); + if (branchWhenEqual) { + _ilg.Emit(OpCodes.Beq, label); + } else { + _ilg.Emit(OpCodes.Ceq); + _ilg.Emit(OpCodes.Brfalse, label); + } + } + } + + private void EmitBranchLogical(bool branch, BinaryExpression node, Label label) { + Debug.Assert(node.NodeType == ExpressionType.AndAlso || node.NodeType == ExpressionType.OrElse); + Debug.Assert(!node.IsLiftedToNull); + + if (node.Method != null || node.IsLifted) { + EmitExpression(node); + EmitBranchOp(branch, label); + return; + } + + + bool isAnd = node.NodeType == ExpressionType.AndAlso; + + // To share code, we make the following substitutions: + // if (!(left || right)) branch value + // becomes: + // if (!left && !right) branch value + // and: + // if (!(left && right)) branch value + // becomes: + // if (!left || !right) branch value + + if (branch == isAnd) { + EmitBranchAnd(branch, (BinaryExpression)node, label); + } else { + EmitBranchOr(branch, (BinaryExpression)node, label); + } + } + + // Generates optimized AndAlso with branch == true + // or optimized OrElse with branch == false + private void EmitBranchAnd(bool branch, BinaryExpression node, Label label) { + // if (left AND right) branch label + + if (!ConstantCheck.IsConstant(node.Left, !branch) && !ConstantCheck.IsConstant(node.Right, !branch)) { + if (ConstantCheck.IsConstant(node.Left, branch)) { + EmitExpressionAndBranch(branch, node.Right, label); + } else if (ConstantCheck.IsConstant(node.Right, branch)) { + EmitExpressionAndBranch(branch, node.Left, label); + } else { + // if (left) then + // if (right) branch label + // endif + + Label endif = _ilg.DefineLabel(); + EmitExpressionAndBranch(!branch, node.Left, endif); + EmitExpressionAndBranch(branch, node.Right, label); + _ilg.MarkLabel(endif); + } + } + } + + // Generates optimized OrElse with branch == true + // or optimized AndAlso with branch == false + private void EmitBranchOr(bool branch, BinaryExpression node, Label label) { + // if (left OR right) branch label + + if (ConstantCheck.IsConstant(node.Left, branch)) { + _ilg.Emit(OpCodes.Br, label); + } else { + if (!ConstantCheck.IsConstant(node.Left, !branch)) { + EmitExpressionAndBranch(branch, node.Left, label); + } + + if (ConstantCheck.IsConstant(node.Right, branch)) { + _ilg.Emit(OpCodes.Br, label); + } else if (!ConstantCheck.IsConstant(node.Right, !branch)) { + EmitExpressionAndBranch(branch, node.Right, label); + } + } + } + + private void EmitBranchBlock(bool branch, BlockExpression node, Label label) { + EnterScope(node); + + int count = node.ExpressionCount; + for (int i = 0; i < count - 1; i++) { + EmitExpressionAsVoid(node.GetExpression(i)); + } + EmitExpressionAndBranch(branch, node.GetExpression(count - 1), label); + + ExitScope(node); + } + + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Statements.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Statements.cs new file mode 100644 index 0000000000..4ba1c20d51 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Statements.cs @@ -0,0 +1,374 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Reflection.Emit; +using System.Diagnostics; + +namespace System.Linq.Expressions.Compiler { + partial class LambdaCompiler { + private void EmitBlockExpression(Expression expr) { + // emit body + Emit((BlockExpression)expr, EmitAs.Default); + } + + private void Emit(BlockExpression node, EmitAs emitAs) { + int count = node.ExpressionCount; + + // Labels defined immediately in the block are valid for the whole block + + for(int i = 0; i < count; i++) { + Expression e = node.GetExpression(i); + + var label = e as LabelExpression; + if (label != null) { + DefineLabel(label.Label); + } + } + + EnterScope(node); + + for (int index = 0; index < count - 1; index++) { + EmitExpressionAsVoid(node.GetExpression(index)); + } + + // if the type of Block it means this is not a Comma + // so we will force the last expression to emit as void. + if (emitAs == EmitAs.Void || node.Type == typeof(void)) { + EmitExpressionAsVoid(node.GetExpression(count - 1)); + } else { + EmitExpression(node.GetExpression(count - 1)); + } + + ExitScope(node); + } + + private void EnterScope(BlockExpression node) { + if (node.Variables.Count > 0 && (_scope.MergedScopes == null || !_scope.MergedScopes.Contains(node))) { + _scope = _tree.Scopes[node].Enter(this, _scope); + Debug.Assert(_scope.Node == node); + } + } + + private void ExitScope(BlockExpression node) { + if (_scope.Node == node) { + _scope = _scope.Exit(); + } + } + + private void EmitEmptyExpression(Expression expr) { + var node = (DefaultExpression)expr; + if (node.Type != typeof(void)) { + // emit default(T) + _ilg.EmitDefault(node.Type); + } + } + + private void EmitLoopExpression(Expression expr) { + LoopExpression node = (LoopExpression)expr; + + PushLabelBlock(LabelBlockKind.Block); + LabelInfo breakTarget = DefineLabel(node.BreakLabel); + LabelInfo continueTarget = DefineLabel(node.ContinueLabel); + + continueTarget.Mark(); + + EmitExpressionAsVoid(node.Body); + + _ilg.Emit(OpCodes.Br, continueTarget.Label); + + PopLabelBlock(LabelBlockKind.Block); + + breakTarget.Mark(); + } + + #region SwitchStatement + + private void EmitSwitchExpression(Expression expr) { + SwitchExpression node = (SwitchExpression)expr; + + LabelInfo breakTarget = DefineLabel(node.BreakLabel); + + Label defaultTarget = breakTarget.Label; + Label[] labels = new Label[node.SwitchCases.Count]; + + // Create all labels + for (int i = 0; i < node.SwitchCases.Count; i++) { + labels[i] = _ilg.DefineLabel(); + + // Default case. + if (node.SwitchCases[i].IsDefault) { + // Set the default target + defaultTarget = labels[i]; + } + } + + // Emit the test value + EmitExpression(node.Test); + + // Check if jmp table can be emitted + if (!TryEmitJumpTable(node, labels, defaultTarget)) { + // There might be scenario(s) where the jmp table is not emitted + // Emit the switch as conditional branches then + EmitConditionalBranches(node, labels); + } + + // If "default" present, execute default code, else exit the switch + _ilg.Emit(OpCodes.Br, defaultTarget); + + // Emit the bodies + for (int i = 0; i < node.SwitchCases.Count; i++) { + // First put the corresponding labels + _ilg.MarkLabel(labels[i]); + // And then emit the Body!! + EmitExpressionAsVoid(node.SwitchCases[i].Body); + } + + breakTarget.Mark(); + } + + private const int MaxJumpTableSize = 65536; + private const double MaxJumpTableSparsity = 10; + + // Emits the switch as if stmts + private void EmitConditionalBranches(SwitchExpression node, Label[] labels) { + LocalBuilder testValueSlot = _ilg.GetLocal(typeof(int)); + _ilg.Emit(OpCodes.Stloc, testValueSlot); + + // For all the "cases" create their conditional branches + for (int i = 0; i < node.SwitchCases.Count; i++) { + // Not default case emit the condition + if (!node.SwitchCases[i].IsDefault) { + // Test for equality of case value and the test expression + _ilg.EmitInt(node.SwitchCases[i].Value); + _ilg.Emit(OpCodes.Ldloc, testValueSlot); + _ilg.Emit(OpCodes.Beq, labels[i]); + } + } + + _ilg.FreeLocal(testValueSlot); + } + + // Tries to emit switch as a jmp table + private bool TryEmitJumpTable(SwitchExpression node, Label[] labels, Label defaultTarget) { + if (node.SwitchCases.Count > MaxJumpTableSize) { + return false; + } + + int min = Int32.MaxValue; + int max = Int32.MinValue; + + // Find the min and max of the values + for (int i = 0; i < node.SwitchCases.Count; ++i) { + // Not the default case. + if (!node.SwitchCases[i].IsDefault) { + int val = node.SwitchCases[i].Value; + if (min > val) min = val; + if (max < val) max = val; + } + } + + long delta = (long)max - (long)min; + if (delta > MaxJumpTableSize) { + return false; + } + + // Value distribution is too sparse, don't emit jump table. + if (delta > node.SwitchCases.Count + MaxJumpTableSparsity) { + return false; + } + + // The actual jmp table of switch + int len = (int)delta + 1; + Label[] jmpLabels = new Label[len]; + + // Initialize all labels to the default + for (int i = 0; i < len; i++) { + jmpLabels[i] = defaultTarget; + } + + // Replace with the actual label target for all cases + for (int i = 0; i < node.SwitchCases.Count; i++) { + SwitchCase sc = node.SwitchCases[i]; + if (!sc.IsDefault) { + jmpLabels[sc.Value - min] = labels[i]; + } + } + + // Emit the normalized index and then switch based on that + if (min != 0) { + _ilg.EmitInt(min); + _ilg.Emit(OpCodes.Sub); + } + _ilg.Emit(OpCodes.Switch, jmpLabels); + return true; + } + + #endregion + + private void CheckRethrow() { + // Rethrow is only valid inside a catch. + for (LabelBlockInfo j = _labelBlock; j != null; j = j.Parent) { + if (j.Kind == LabelBlockKind.Catch) { + return; + } else if (j.Kind == LabelBlockKind.Finally) { + // Rethrow from inside finally is not verifiable + break; + } + } + throw Error.RethrowRequiresCatch(); + } + #region TryStatement + + private void EmitSaveExceptionOrPop(CatchBlock cb) { + if (cb.Variable != null) { + // If the variable is present, store the exception + // in the variable. + _scope.EmitSet(cb.Variable); + } else { + // Otherwise, pop it off the stack. + _ilg.Emit(OpCodes.Pop); + } + } + + private void EmitTryExpression(Expression expr) { + var node = (TryExpression)expr; + + //****************************************************************** + // 1. ENTERING TRY + //****************************************************************** + + PushLabelBlock(LabelBlockKind.Try); + _ilg.BeginExceptionBlock(); + + //****************************************************************** + // 2. Emit the try statement body + //****************************************************************** + + EmitExpressionAsVoid(node.Body); + + //****************************************************************** + // 3. Emit the catch blocks + //****************************************************************** + + foreach (CatchBlock cb in node.Handlers) { + PushLabelBlock(LabelBlockKind.Catch); + + // Begin the strongly typed exception block + EmitCatchStart(cb); + + // + // Emit the catch block body + // + EmitExpressionAsVoid(cb.Body); + + PopLabelBlock(LabelBlockKind.Catch); + } + + //****************************************************************** + // 4. Emit the finally block + //****************************************************************** + + if (node.Finally != null || node.Fault != null) { + PushLabelBlock(LabelBlockKind.Finally); + + if (node.Finally != null) { + _ilg.BeginFinallyBlock(); + } else if (IsDynamicMethod) { + // dynamic methods don't support fault blocks so we + // generate a catch/rethrow. + _ilg.BeginCatchBlock(typeof(Exception)); + } else { + _ilg.BeginFaultBlock(); + } + + // Emit the body + EmitExpressionAsVoid(node.Finally ?? node.Fault); + + // rethrow the exception if we have a catch in a dynamic method. + if (node.Fault != null && IsDynamicMethod) { + // rethrow when we generated a catch + _ilg.Emit(OpCodes.Rethrow); + } + + _ilg.EndExceptionBlock(); + PopLabelBlock(LabelBlockKind.Finally); + } else { + _ilg.EndExceptionBlock(); + } + + PopLabelBlock(LabelBlockKind.Try); + } + + /// + /// Emits the start of a catch block. The exception value that is provided by the + /// CLR is stored in the variable specified by the catch block or popped if no + /// variable is provided. + /// + private void EmitCatchStart(CatchBlock cb) { + if (cb.Filter != null && !IsDynamicMethod) { + // emit filter block as filter. Filter blocks are + // untyped so we need to do the type check ourselves. + _ilg.BeginExceptFilterBlock(); + + Label endFilter = _ilg.DefineLabel(); + Label rightType = _ilg.DefineLabel(); + + // skip if it's not our exception type, but save + // the exception if it is so it's available to the + // filter + _ilg.Emit(OpCodes.Isinst, cb.Test); + _ilg.Emit(OpCodes.Dup); + _ilg.Emit(OpCodes.Brtrue, rightType); + _ilg.Emit(OpCodes.Pop); + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Br, endFilter); + + // it's our type, save it and emit the filter. + _ilg.MarkLabel(rightType); + EmitSaveExceptionOrPop(cb); + PushLabelBlock(LabelBlockKind.Filter); + EmitExpression(cb.Filter); + PopLabelBlock(LabelBlockKind.Filter); + + // begin the catch, clear the exception, we've + // already saved it + _ilg.MarkLabel(endFilter); + _ilg.BeginCatchBlock(null); + _ilg.Emit(OpCodes.Pop); + } else { + _ilg.BeginCatchBlock(cb.Test); + + EmitSaveExceptionOrPop(cb); + + if (cb.Filter != null) { + Label catchBlock = _ilg.DefineLabel(); + + // filters aren't supported in dynamic methods so instead + // emit the filter as if check, if (!expr) rethrow + PushLabelBlock(LabelBlockKind.Filter); + EmitExpressionAndBranch(true, cb.Filter, catchBlock); + PopLabelBlock(LabelBlockKind.Filter); + + _ilg.Emit(OpCodes.Rethrow); + _ilg.MarkLabel(catchBlock); + + // catch body continues + } + } + } + + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Unary.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Unary.cs new file mode 100644 index 0000000000..d7a680e969 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.Unary.cs @@ -0,0 +1,320 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + partial class LambdaCompiler { + + private void EmitQuoteUnaryExpression(Expression expr) { + EmitQuote((UnaryExpression)expr); + } + + //CONFORMING + private void EmitQuote(UnaryExpression quote) { + // emit the quoted expression as a runtime constant + EmitConstant(quote.Operand, quote.Type); + + // Heuristic: only emit the tree rewrite logic if we have hoisted + // locals. TODO: we could use an even smarter logic here by + // detecting if any nodes actually need to be rewritten + if (_scope.NearestHoistedLocals != null) { + // HoistedLocals is internal so emit as System.Object + EmitConstant(_scope.NearestHoistedLocals, typeof(object)); + _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable); + _ilg.EmitCall(typeof(RuntimeOps).GetMethod("Quote")); + + if (quote.Type != typeof(Expression)) { + _ilg.Emit(OpCodes.Castclass, quote.Type); + } + } + } + + private void EmitThrowUnaryExpression(Expression expr) { + EmitThrow((UnaryExpression)expr, EmitAs.Default); + } + + private void EmitThrow(UnaryExpression expr, EmitAs emitAs) { + if (expr.Operand == null) { + CheckRethrow(); + + _ilg.Emit(OpCodes.Rethrow); + } else { + EmitExpression(expr.Operand); + _ilg.Emit(OpCodes.Throw); + } + if (emitAs != EmitAs.Void && expr.Type != typeof(void)) { + _ilg.EmitDefault(expr.Type); + } + } + + private void EmitUnaryExpression(Expression expr) { + EmitUnary((UnaryExpression)expr); + } + + //CONFORMING + private void EmitUnary(UnaryExpression node) { + if (node.Method != null) { + EmitUnaryMethod(node); + } else if (node.NodeType == ExpressionType.NegateChecked && TypeUtils.IsInteger(node.Operand.Type)) { + _ilg.EmitInt(0); + _ilg.EmitConvertToType(typeof(int), node.Operand.Type, false); + EmitExpression(node.Operand); + EmitBinaryOperator(ExpressionType.SubtractChecked, node.Operand.Type, node.Operand.Type, node.Type, false); + } else { + EmitExpression(node.Operand); + EmitUnaryOperator(node.NodeType, node.Operand.Type, node.Type); + } + } + + //CONFORMING + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private void EmitUnaryOperator(ExpressionType op, Type operandType, Type resultType) { + bool operandIsNullable = TypeUtils.IsNullableType(operandType); + + if (op == ExpressionType.ArrayLength) { + _ilg.Emit(OpCodes.Ldlen); + return; + } + + if (operandIsNullable) { + switch (op) { + case ExpressionType.Not: { + if (operandType != typeof(bool?)) + goto case ExpressionType.Negate; + + Label labIfNull = _ilg.DefineLabel(); + Label labEnd = _ilg.DefineLabel(); + LocalBuilder loc = _ilg.GetLocal(operandType); + + // store values (reverse order since they are already on the stack) + _ilg.Emit(OpCodes.Stloc, loc); + + // test for null + _ilg.Emit(OpCodes.Ldloca, loc); + _ilg.EmitHasValue(operandType); + _ilg.Emit(OpCodes.Brfalse_S, labEnd); + + // do op on non-null value + _ilg.Emit(OpCodes.Ldloca, loc); + _ilg.EmitGetValueOrDefault(operandType); + Type nnOperandType = TypeUtils.GetNonNullableType(operandType); + EmitUnaryOperator(op, nnOperandType, typeof(bool)); + + // construct result + ConstructorInfo ci = resultType.GetConstructor(new Type[] { typeof(bool) }); + _ilg.Emit(OpCodes.Newobj, ci); + _ilg.Emit(OpCodes.Stloc, loc); + + _ilg.MarkLabel(labEnd); + _ilg.Emit(OpCodes.Ldloc, loc); + _ilg.FreeLocal(loc); + return; + } + case ExpressionType.UnaryPlus: + case ExpressionType.NegateChecked: + case ExpressionType.Negate: + case ExpressionType.Increment: + case ExpressionType.Decrement: + { + Debug.Assert(operandType == resultType); + Label labIfNull = _ilg.DefineLabel(); + Label labEnd = _ilg.DefineLabel(); + LocalBuilder loc = _ilg.GetLocal(operandType); + + // check for null + _ilg.Emit(OpCodes.Stloc, loc); + _ilg.Emit(OpCodes.Ldloca, loc); + _ilg.EmitHasValue(operandType); + _ilg.Emit(OpCodes.Brfalse_S, labIfNull); + + // apply operator to non-null value + _ilg.Emit(OpCodes.Ldloca, loc); + _ilg.EmitGetValueOrDefault(operandType); + Type nnOperandType = TypeUtils.GetNonNullableType(resultType); + EmitUnaryOperator(op, nnOperandType, nnOperandType); + + // construct result + ConstructorInfo ci = resultType.GetConstructor(new Type[] { nnOperandType }); + _ilg.Emit(OpCodes.Newobj, ci); + _ilg.Emit(OpCodes.Stloc, loc); + _ilg.Emit(OpCodes.Br_S, labEnd); + + // if null then create a default one + _ilg.MarkLabel(labIfNull); + _ilg.Emit(OpCodes.Ldloca, loc); + _ilg.Emit(OpCodes.Initobj, resultType); + + _ilg.MarkLabel(labEnd); + _ilg.Emit(OpCodes.Ldloc, loc); + _ilg.FreeLocal(loc); + return; + } + case ExpressionType.TypeAs: + _ilg.Emit(OpCodes.Box, operandType); + _ilg.Emit(OpCodes.Isinst, resultType); + if (TypeUtils.IsNullableType(resultType)) { + _ilg.Emit(OpCodes.Unbox_Any, resultType); + } + return; + default: + throw Error.UnhandledUnary(op); + } + } else { + switch (op) { + case ExpressionType.Not: + if (operandType == typeof(bool)) { + _ilg.Emit(OpCodes.Ldc_I4_0); + _ilg.Emit(OpCodes.Ceq); + } else { + _ilg.Emit(OpCodes.Not); + } + break; + case ExpressionType.UnaryPlus: + _ilg.Emit(OpCodes.Nop); + break; + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + _ilg.Emit(OpCodes.Neg); + break; + case ExpressionType.TypeAs: + if (operandType.IsValueType) { + _ilg.Emit(OpCodes.Box, operandType); + } + _ilg.Emit(OpCodes.Isinst, resultType); + if (TypeUtils.IsNullableType(resultType)) { + _ilg.Emit(OpCodes.Unbox_Any, resultType); + } + break; + case ExpressionType.Increment: + EmitConstantOne(resultType); + _ilg.Emit(OpCodes.Add); + break; + case ExpressionType.Decrement: + EmitConstantOne(resultType); + _ilg.Emit(OpCodes.Sub); + break; + default: + throw Error.UnhandledUnary(op); + } + + EmitConvertArithmeticResult(op, resultType); + } + } + + private void EmitConstantOne(Type type) { + switch (Type.GetTypeCode(type)) { + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.Int16: + case TypeCode.Int32: + _ilg.Emit(OpCodes.Ldc_I4_1); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + _ilg.Emit(OpCodes.Ldc_I8, (long)1); + break; + case TypeCode.Single: + _ilg.Emit(OpCodes.Ldc_R4, 1.0f); + break; + case TypeCode.Double: + _ilg.Emit(OpCodes.Ldc_R8, 1.0d); + break; + default: + // we only have to worry about aritmetic types, see + // TypeUtils.IsArithmetic + throw Assert.Unreachable; + } + } + + private void EmitUnboxUnaryExpression(Expression expr) { + var node = (UnaryExpression)expr; + Debug.Assert(node.Type.IsValueType && !TypeUtils.IsNullableType(node.Type)); + + // Unbox_Any leaves the value on the stack + EmitExpression(node.Operand); + _ilg.Emit(OpCodes.Unbox_Any, node.Type); + } + + private void EmitConvertUnaryExpression(Expression expr) { + EmitConvert((UnaryExpression)expr); + } + + //CONFORMING + private void EmitConvert(UnaryExpression node) { + if (node.Method != null) { + // User-defined conversions are only lifted if both source and + // destination types are value types. The C# compiler gets this wrong. + // In C#, if you have an implicit conversion from int->MyClass and you + // "lift" the conversion to int?->MyClass then a null int? goes to a + // null MyClass. This is contrary to the specification, which states + // that the correct behaviour is to unwrap the int?, throw an exception + // if it is null, and then call the conversion. + // + // We cannot fix this in C# but there is no reason why we need to + // propagate this bug into the expression tree API. Unfortunately + // this means that when the C# compiler generates the lambda + // (int? i)=>(MyClass)i, we will get different results for converting + // that lambda to a delegate directly and converting that lambda to + // an expression tree and then compiling it. We can live with this + // discrepancy however. + + if (node.IsLifted && (!node.Type.IsValueType || !node.Operand.Type.IsValueType)) { + ParameterInfo[] pis = node.Method.GetParametersCached(); + Debug.Assert(pis != null && pis.Length == 1); + Type paramType = pis[0].ParameterType; + if (paramType.IsByRef) { + paramType = paramType.GetElementType(); + } + + UnaryExpression e = Expression.Convert( + Expression.Call( + node.Method, + Expression.Convert(node.Operand, pis[0].ParameterType) + ), + node.Type + ); + + EmitConvert(e); + } else { + EmitUnaryMethod(node); + } + } else if (node.Type == typeof(void)) { + EmitExpressionAsVoid(node.Operand); + } else { + EmitExpression(node.Operand); + _ilg.EmitConvertToType(node.Operand.Type, node.Type, node.NodeType == ExpressionType.ConvertChecked); + } + } + + //CONFORMING + private void EmitUnaryMethod(UnaryExpression node) { + if (node.IsLifted) { + ParameterExpression v = Expression.Variable(TypeUtils.GetNonNullableType(node.Operand.Type), null); + MethodCallExpression mc = Expression.Call(node.Method, v); + + Type resultType = TypeUtils.GetNullableType(mc.Type); + EmitLift(node.NodeType, resultType, mc, new ParameterExpression[] { v }, new Expression[] { node.Operand }); + _ilg.EmitConvertToType(resultType, node.Type, false); + } else { + EmitMethodCallExpression(Expression.Call(node.Method, node.Operand)); + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.cs new file mode 100644 index 0000000000..702b4b3808 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/LambdaCompiler.cs @@ -0,0 +1,413 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.SymbolStore; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Dynamic; +using System.Dynamic.Utils; +using System.Text; + +namespace System.Linq.Expressions.Compiler { + + /// + /// LambdaCompiler is responsible for compiling individual lambda (LambdaExpression). The complete tree may + /// contain multiple lambdas, the Compiler class is reponsible for compiling the whole tree, individual + /// lambdas are then compiled by the LambdaCompiler. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + internal sealed partial class LambdaCompiler { + + private delegate void WriteBack(); + + // Information on the entire lambda tree currently being compiled + private readonly AnalyzedTree _tree; + + // Indicates that the method should logically be treated as a + // DynamicMethod. We need this because in debuggable code we have to + // emit into a MethodBuilder, but we still want to pretend it's a + // DynamicMethod + private readonly bool _dynamicMethod; + + private readonly ILGen _ilg; + + // The TypeBuilder backing this method, if any + private readonly TypeBuilder _typeBuilder; + + private readonly MethodInfo _method; + + // Currently active LabelTargets and their mapping to IL labels + private LabelBlockInfo _labelBlock = new LabelBlockInfo(null, LabelBlockKind.Block); + // Mapping of labels used for "long" jumps (jumping out and into blocks) + private readonly Dictionary _labelInfo = new Dictionary(); + + // The currently active variable scope + private CompilerScope _scope; + + // The lambda we are compiling + private readonly LambdaExpression _lambda; + + /// + /// Argument types + /// + /// This list contains _all_ arguments on the underlying method builder (except for the + /// "this"). There are two views on the list. First provides the raw view (shows all + /// arguments), the second view provides view of the arguments which are in the original + /// lambda (so first argument, which may be closure argument, is skipped in that case) + /// + private readonly ReadOnlyCollection _paramTypes; + + // True if we want to emitting debug symbols + private readonly bool _emitDebugSymbols; + + // Runtime constants bound to the delegate + private readonly BoundConstants _boundConstants; + + private LambdaCompiler( + AnalyzedTree tree, + LambdaExpression lambda, + TypeBuilder typeBuilder, + MethodInfo method, + ILGenerator ilg, + IList paramTypes, + bool dynamicMethod, + bool emitDebugSymbols) { + + ContractUtils.Requires(dynamicMethod || method.IsStatic, "dynamicMethod"); + _tree = tree; + _lambda = lambda; + _typeBuilder = typeBuilder; + _method = method; + _paramTypes = new ReadOnlyCollection(paramTypes); + _dynamicMethod = dynamicMethod; + + // These are populated by AnalyzeTree/VariableBinder + _scope = tree.Scopes[lambda]; + _boundConstants = tree.Constants[lambda]; + + if (!dynamicMethod && _boundConstants.Count > 0) { + throw Error.RtConstRequiresBundDelegate(); + } + + _ilg = new ILGen(ilg); + + Debug.Assert(!emitDebugSymbols || _typeBuilder != null, "emitting debug symbols requires a TypeBuilder"); + _emitDebugSymbols = emitDebugSymbols; + + // See if we can find a return label, so we can emit better IL + AddReturnLabel(_lambda.Body); + + _boundConstants.EmitCacheConstants(this); + } + + public override string ToString() { + return _method.ToString(); + } + + internal ILGen IL { + get { return _ilg; } + } + + private bool IsDynamicMethod { + get { + return _method is DynamicMethod; + } + } + + internal ReadOnlyCollection Parameters { + get { return _lambda.Parameters; } + } + + private bool HasClosure { + get { return _paramTypes[0] == typeof(Closure); } + } + + #region Compiler entry points + + /// + /// Compiler entry point + /// + /// LambdaExpression to compile. + /// Product of compilation + /// Type of the delegate to create + /// True to emit debug symbols, false otherwise. + /// Force dynamic method regardless of save assemblies. + /// The compiled delegate. + internal static Delegate CompileLambda(LambdaExpression lambda, Type delegateType, bool emitDebugSymbols, bool forceDynamic, out MethodInfo method) { + // 1. Create signature + List types; + List names; + string name; + Type returnType; + ComputeSignature(lambda, out types, out names, out name, out returnType); + + // 2. Bind lambda + AnalyzedTree tree = AnalyzeLambda(ref lambda); + + // 3. Create lambda compiler + LambdaCompiler c = CreateDynamicCompiler(tree, lambda, name, returnType, types, null, emitDebugSymbols, forceDynamic); + + // 4. Emit + c.EmitLambdaBody(null); + + // 5. Return the delegate. + return c.CreateDelegate(delegateType, out method); + } + + internal static T CompileLambda(LambdaExpression lambda, bool forceDynamic, out MethodInfo method) { + return (T)(object)CompileLambda(lambda, typeof(T), false, forceDynamic, out method); + } + + internal static T CompileLambda(LambdaExpression lambda, bool emitDebugSymbols) { + MethodInfo method; + return (T)(object)CompileLambda(lambda, typeof(T), emitDebugSymbols, false, out method); + } + + internal static Delegate CompileLambda(LambdaExpression lambda, bool emitDebugSymbols) { + MethodInfo method; + return CompileLambda(lambda, lambda.Type, emitDebugSymbols, false, out method); + } + + /// + /// Creates and returns a MethodBuilder + /// + internal static MethodBuilder CompileLambda(LambdaExpression lambda, TypeBuilder type, MethodAttributes attributes, bool emitDebugSymbols) { + // 1. Create signature + List types; + List names; + string lambdaName; + Type returnType; + ComputeSignature(lambda, out types, out names, out lambdaName, out returnType); + + // 2. Bind lambda + AnalyzedTree tree = AnalyzeLambda(ref lambda); + + // 3. Create lambda compiler + LambdaCompiler c = CreateStaticCompiler( + tree, + lambda, + type, + lambda.Name ?? "lambda_method", // don't use generated name + attributes, + returnType, + types, + names, + false, // dynamicMethod + emitDebugSymbols + ); + + // 4. Emit + c.EmitLambdaBody(null); + + return (MethodBuilder)c._method; + } + + #endregion + + private static AnalyzedTree AnalyzeLambda(ref LambdaExpression lambda) { + // Spill the stack for any exception handling blocks or other + // constructs which require entering with an empty stack + lambda = StackSpiller.AnalyzeLambda(lambda); + + // Bind any variable references in this lambda + return VariableBinder.Bind(lambda); + } + + internal LocalBuilder GetNamedLocal(Type type, string name) { + Assert.NotNull(type); + + if (_emitDebugSymbols && name != null) { + LocalBuilder lb = _ilg.DeclareLocal(type); + // TODO: we need to set the lexical scope properly, so it can + // be freed and reused! + lb.SetLocalSymInfo(name); + return lb; + } + return _ilg.GetLocal(type); + } + + internal void FreeNamedLocal(LocalBuilder local, string name) { + if (_emitDebugSymbols && name != null) { + // local has a name, we can't free it + return; + } + _ilg.FreeLocal(local); + } + + /// + /// Gets the argument slot corresponding to the parameter at the given + /// index. Assumes that the method takes a certain number of prefix + /// arguments, followed by the real parameters stored in Parameters + /// + internal int GetLambdaArgument(int index) { + return index + (HasClosure ? 1 : 0) + (_method.IsStatic ? 0 : 1); + } + + internal Type GetLambdaArgumentType(int index) { + return _paramTypes[index + (HasClosure ? 1 : 0)]; + } + + /// + /// Returns the index-th argument. This method provides access to the actual arguments + /// defined on the lambda itself, and excludes the possible 0-th closure argument. + /// + internal void EmitLambdaArgument(int index) { + _ilg.EmitLoadArg(GetLambdaArgument(index)); + } + + internal void EmitClosureArgument() { + Debug.Assert(HasClosure, "must have a Closure argument"); + + // TODO: this is for emitting into instance methods, but it's not + // used anymore (we always emit into static MethodBuilders or + // DynamicMethods, which are both static) + _ilg.EmitLoadArg(_method.IsStatic ? 0 : 1); + } + + private MethodInfo CreateDelegateMethodInfo() { + if (_method is DynamicMethod) { + return (MethodInfo)_method; + } else { + var mb = (MethodBuilder)_method; + Type methodType = _typeBuilder.CreateType(); + return methodType.GetMethod(mb.Name); + } + } + + private Delegate CreateDelegate(Type delegateType, out MethodInfo method) { + method = CreateDelegateMethodInfo(); + + if (_dynamicMethod) { + return method.CreateDelegate(delegateType, new Closure(_boundConstants.ToArray(), null)); + } else { + return method.CreateDelegate(delegateType); + } + } + + #region Factory methods + + /// + /// Creates a compiler backed by dynamic method. Sometimes (when debugging is required) the dynamic + /// method is actually a 'fake' dynamic method and is backed by static type created specifically for + /// the one method + /// + private static LambdaCompiler CreateDynamicCompiler( + AnalyzedTree tree, + LambdaExpression lambda, + string methodName, + Type returnType, + IList paramTypes, + IList paramNames, + bool emitDebugSymbols, + bool forceDynamic) { + + Assert.NotEmpty(methodName); + Assert.NotNull(returnType); + Assert.NotNullItems(paramTypes); + + LambdaCompiler lc; + + // + // Generate a static method if either + // 1) we want to dump all geneated IL to an assembly on disk (SaveSnippets on) + // 2) the method is debuggable, i.e. DebugMode is on and a source unit is associated with the method + // + if ((Snippets.Shared.SaveSnippets || emitDebugSymbols) && !forceDynamic) { + var typeBuilder = Snippets.Shared.DefineType(methodName, typeof(object), false, false, emitDebugSymbols); + lc = CreateStaticCompiler( + tree, + lambda, + typeBuilder, + methodName, + TypeUtils.PublicStatic, + returnType, + paramTypes, + paramNames, + true, // dynamicMethod + emitDebugSymbols + ); + } else { + Type[] parameterTypes = paramTypes.AddFirst(typeof(Closure)); + DynamicMethod target = Snippets.Shared.CreateDynamicMethod(methodName, returnType, parameterTypes); + lc = new LambdaCompiler( + tree, + lambda, + null, // typeGen + target, + target.GetILGenerator(), + parameterTypes, + true, // dynamicMethod + false // emitDebugSymbols + ); + } + + return lc; + } + + /// + /// Creates a LambdaCompiler backed by a method on a static type + /// + private static LambdaCompiler CreateStaticCompiler( + AnalyzedTree tree, + LambdaExpression lambda, + TypeBuilder typeBuilder, + string name, + MethodAttributes attributes, + Type retType, + IList paramTypes, + IList paramNames, + bool dynamicMethod, + bool emitDebugSymbols) { + + Assert.NotNull(name, retType); + + bool closure = tree.Scopes[lambda].NeedsClosure; + Type[] parameterTypes; + if (dynamicMethod || closure) { + parameterTypes = paramTypes.AddFirst(typeof(Closure)); + } else { + parameterTypes = paramTypes.ToArray(); + } + + MethodBuilder mb = typeBuilder.DefineMethod(name, attributes, retType, parameterTypes); + LambdaCompiler lc = new LambdaCompiler( + tree, + lambda, + typeBuilder, + mb, + mb.GetILGenerator(), + parameterTypes, + dynamicMethod, + emitDebugSymbols + ); + + if (paramNames != null) { + // parameters are index from 1, with closure argument we need to skip the first arg + int startIndex = (dynamicMethod || closure) ? 2 : 1; + for (int i = 0; i < paramNames.Count; i++) { + mb.DefineParameter(i + startIndex, ParameterAttributes.None, paramNames[i]); + } + } + return lc; + } + + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/RuntimeVariableList.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/RuntimeVariableList.cs new file mode 100644 index 0000000000..a917eb29ca --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/RuntimeVariableList.cs @@ -0,0 +1,144 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions.Compiler; +using System.Runtime.CompilerServices; +using System.Dynamic.Utils; +using System.Linq.Expressions; + +namespace System.Runtime.CompilerServices { + public static partial class RuntimeOps { + // creates access for local variables in scope + [Obsolete("do not call this method", true)] + public static IList CreateRuntimeVariables(object[] data, long[] indexes) { + return new RuntimeVariableList(data, indexes); + } + + // creates access when there are no variables in scope + [Obsolete("do not call this method", true)] + public static IList CreateRuntimeVariables() { + return EmptyReadOnlyCollection.Instance; + } + + /// + /// Provides a list of variables, supporing read/write of the values + /// Exposed via RuntimeVariablesExpression + /// + private sealed class RuntimeVariableList : IList { + // The top level environment. It contains pointers to parent + // environments, which are always in the first element + private readonly object[] _data; + + // An array of (int, int) pairs, each representing how to find a + // variable in the environment data struction. + // + // The first integer indicates the number of times to go up in the + // closure chain, the second integer indicates the index into that + // closure chain. + private readonly long[] _indexes; + + internal RuntimeVariableList(object[] data, long[] indexes) { + Assert.NotNull(data, indexes); + + _data = data; + _indexes = indexes; + } + + public int Count { + get { return _indexes.Length; } + } + + public IStrongBox this[int index] { + get { + // We lookup the closure using two ints: + // 1. The high dword is the number of parents to go up + // 2. The low dword is the index into that array + long closureKey = _indexes[index]; + + // walk up the parent chain to find the real environment + object[] result = _data; + for (int parents = (int)(closureKey >> 32); parents > 0; parents--) { + result = HoistedLocals.GetParent(result); + } + + // Return the variable storage + return (IStrongBox)result[(int)closureKey]; + } + set { + throw Error.CollectionReadOnly(); + } + } + + public int IndexOf(IStrongBox item) { + for (int i = 0, n = _indexes.Length; i < n; i++) { + if (this[i] == item) { + return i; + } + } + return -1; + } + + public bool Contains(IStrongBox item) { + return IndexOf(item) >= 0; + } + + public void CopyTo(IStrongBox[] array, int arrayIndex) { + ContractUtils.RequiresNotNull(array, "array"); + int count = _indexes.Length; + if (arrayIndex < 0 || arrayIndex + count > array.Length) { + throw new ArgumentOutOfRangeException("arrayIndex"); + } + for (int i = 0; i < count; i++) { + array[arrayIndex++] = this[i]; + } + } + + bool ICollection.IsReadOnly { + get { return true; } + } + + public IEnumerator GetEnumerator() { + for (int i = 0, n = _indexes.Length; i < n; i++) { + yield return this[i]; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + void IList.Insert(int index, IStrongBox item) { + throw Error.CollectionReadOnly(); + } + + void IList.RemoveAt(int index) { + throw Error.CollectionReadOnly(); + } + + void ICollection.Add(IStrongBox item) { + throw Error.CollectionReadOnly(); + } + + void ICollection.Clear() { + throw Error.CollectionReadOnly(); + } + + bool ICollection.Remove(IStrongBox item) { + throw Error.CollectionReadOnly(); + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/Set.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/Set.cs new file mode 100644 index 0000000000..2158f22b8e --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/Set.cs @@ -0,0 +1,81 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; + +// Note: can't move to Utils because name conflicts with System.Linq.Set +namespace System.Linq.Expressions.Compiler { + + /// + /// A simple hashset, built on Dictionary{K, V} + /// + /// TODO: should remove this in favor of HashSet{T} + /// + internal sealed class Set : ICollection { + private readonly Dictionary _data; + + internal Set() { + _data = new Dictionary(); + } + + internal Set(IEqualityComparer comparer) { + _data = new Dictionary(comparer); + } + + internal Set(IList list) { + _data = new Dictionary(list.Count); + foreach (T t in list) { + _data.Add(t, null); + } + } + + public void Add(T item) { + _data[item] = null; + } + + public void Clear() { + _data.Clear(); + } + + public bool Contains(T item) { + return _data.ContainsKey(item); + } + + public void CopyTo(T[] array, int arrayIndex) { + _data.Keys.CopyTo(array, arrayIndex); + } + + public int Count { + get { return _data.Count; } + } + + public bool IsReadOnly { + get { return false; } + } + + public bool Remove(T item) { + return _data.Remove(item); + } + + public IEnumerator GetEnumerator() { + return _data.Keys.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return _data.Keys.GetEnumerator(); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/Snippets.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/Snippets.cs new file mode 100644 index 0000000000..f426855eba --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/Snippets.cs @@ -0,0 +1,210 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using System.Dynamic.Utils; +using System.Threading; +using System.Dynamic; +using System.Globalization; +using System.Collections.Generic; + +namespace System.Linq.Expressions.Compiler { + + // TODO: This should be a static class + // TODO: simplify initialization logic & state + internal sealed class Snippets { + internal static readonly Snippets Shared = new Snippets(); + + private Snippets() { } + + private int _methodNameIndex; + + private AssemblyGen _assembly; + private AssemblyGen _unsafeAssembly; + private AssemblyGen _debugAssembly; + private AssemblyGen _unsafeDebugAssembly; + + // TODO: options should be internal + private string _snippetsDirectory = null; + private bool _saveSnippets = false; + + internal bool SaveSnippets { + get { return _saveSnippets; } + } + + /// + /// Directory where snippet assembly will be saved if SaveSnippets is set. + /// + internal string SnippetsDirectory { + get { return _snippetsDirectory; } + } + + internal AssemblyGen GetAssembly(bool emitSymbols, bool isUnsafe) { + // If snippets are not to be saved, we can merge unsafe and safe IL. + if (isUnsafe && _saveSnippets) { + return (emitSymbols) ? + GetOrCreateAssembly(emitSymbols, isUnsafe, ref _unsafeDebugAssembly) : + GetOrCreateAssembly(emitSymbols, isUnsafe, ref _unsafeAssembly); + } else { + return (emitSymbols) ? + GetOrCreateAssembly(emitSymbols, isUnsafe, ref _debugAssembly) : + GetOrCreateAssembly(emitSymbols, isUnsafe, ref _assembly); + } + } + + private AssemblyGen GetOrCreateAssembly(bool emitSymbols, bool isUnsafe, ref AssemblyGen assembly) { + if (assembly == null) { + string suffix = (emitSymbols) ? ".debug" : "" + (isUnsafe ? ".unsafe" : ""); + Interlocked.CompareExchange(ref assembly, CreateNewAssembly(suffix, emitSymbols, isUnsafe), null); + } + return assembly; + } + + private AssemblyGen CreateNewAssembly(string nameSuffix, bool emitSymbols, bool isUnsafe) { + string dir; + + if (_saveSnippets) { + dir = _snippetsDirectory ?? Directory.GetCurrentDirectory(); + } else { + dir = null; + } + + string name = "Snippets" + nameSuffix; + + return new AssemblyGen(new AssemblyName(name), dir, ".dll", emitSymbols, isUnsafe); + } + + internal string GetMethodILDumpFile(MethodBase method) { + string fullName = ((method.DeclaringType != null) ? method.DeclaringType.Name + "." : "") + method.Name; + + if (fullName.Length > 100) { + fullName = fullName.Substring(0, 100); + } + + string filename = String.Format( + CultureInfo.CurrentCulture, + "{0}_{1}.il", + Helpers.ToValidFileName(fullName), + Interlocked.Increment(ref _methodNameIndex) + ); + + string dir = _snippetsDirectory ?? Path.Combine(Path.GetTempPath(), "__DLRIL"); + Directory.CreateDirectory(dir); + return Path.Combine(dir, filename); + } + +#if MICROSOFT_SCRIPTING_CORE + // NOTE: this method is called through reflection from Microsoft.Scripting + internal static void SetSaveAssemblies(string directory) { + Shared.ConfigureSaveAssemblies(directory); + } + + private void ConfigureSaveAssemblies(string directory) { + _saveSnippets = true; + _snippetsDirectory = directory; + } + + // NOTE: this method is called through reflection from Microsoft.Scripting + internal static string[] SaveAssemblies() { + return Shared.DumpAssemblies(); + } + + //return the assembly locations that need to be verified + private string[] DumpAssemblies() { + if (!_saveSnippets) { + return new string[0]; + } + + List assemlyLocations = new List(); + + // first save all assemblies to disk: + if (_assembly != null) { + string assemblyLocation = _assembly.SaveAssembly(); + if (assemblyLocation != null) { + assemlyLocations.Add(assemblyLocation); + } + _assembly = null; + } + + if (_debugAssembly != null) { + string debugAssemblyLocation = _debugAssembly.SaveAssembly(); + if (debugAssemblyLocation != null) { + assemlyLocations.Add(debugAssemblyLocation); + } + _debugAssembly = null; + } + + if (_unsafeAssembly != null) { + _unsafeAssembly.SaveAssembly(); + } + + if (_unsafeDebugAssembly != null) { + _unsafeDebugAssembly.SaveAssembly(); + } + + _unsafeDebugAssembly = null; + + return assemlyLocations.ToArray(); + } +#endif + internal DynamicILGen CreateDynamicMethod(string methodName, Type returnType, Type[] parameterTypes, + bool isDebuggable) { + + ContractUtils.RequiresNotEmpty(methodName, "methodName"); + ContractUtils.RequiresNotNull(returnType, "returnType"); + ContractUtils.RequiresNotNullItems(parameterTypes, "parameterTypes"); + + if (_saveSnippets) { + AssemblyGen assembly = GetAssembly(isDebuggable, false); + TypeBuilder tb = assembly.DefinePublicType(methodName, typeof(object), false); + MethodBuilder mb = tb.DefineMethod(methodName, TypeUtils.PublicStatic, returnType, parameterTypes); + return new DynamicILGenType(tb, mb, mb.GetILGenerator()); + } else { + DynamicMethod dm = Helpers.CreateDynamicMethod(methodName, returnType, parameterTypes); + return new DynamicILGenMethod(dm, dm.GetILGenerator()); + } + } + + internal TypeBuilder DefinePublicType(string name, Type parent) { + return GetAssembly(false, false).DefinePublicType(name, parent, false); + } + + internal TypeBuilder DefineUnsafeType(string name, Type parent) { + return GetAssembly(false, true).DefinePublicType(name, parent, false); + } + + internal TypeBuilder DefineType(string name, Type parent, bool preserveName, bool isUnsafe, bool emitDebugSymbols) { + AssemblyGen ag = GetAssembly(emitDebugSymbols, isUnsafe); + return ag.DefinePublicType(name, parent, preserveName); + } + + internal DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes) { + string uniqueName = name + "##" + Interlocked.Increment(ref _methodNameIndex); + return Helpers.CreateDynamicMethod(uniqueName, returnType, parameterTypes); + } + + internal TypeBuilder DefineDelegateType(string name) { + AssemblyGen assembly = GetAssembly(false, false); + return assembly.DefineType( + name, + typeof(MulticastDelegate), + TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, + false + ); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/StackSpiller.Bindings.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/StackSpiller.Bindings.cs new file mode 100644 index 0000000000..276f950e96 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/StackSpiller.Bindings.cs @@ -0,0 +1,217 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.ObjectModel; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + + internal partial class StackSpiller { + + private abstract class BindingRewriter { + protected MemberBinding _binding; + protected RewriteAction _action; + protected StackSpiller _spiller; + + internal BindingRewriter(MemberBinding binding, StackSpiller spiller) { + _binding = binding; + _spiller = spiller; + } + + internal RewriteAction Action { + get { return _action; } + } + + internal abstract MemberBinding AsBinding(); + internal abstract Expression AsExpression(Expression target); + + internal static BindingRewriter Create(MemberBinding binding, StackSpiller spiller, Stack stack) { + switch (binding.BindingType) { + case MemberBindingType.Assignment: + MemberAssignment assign = (MemberAssignment)binding; + return new MemberAssignmentRewriter(assign, spiller, stack); + case MemberBindingType.ListBinding: + MemberListBinding list = (MemberListBinding)binding; + return new ListBindingRewriter(list, spiller, stack); + case MemberBindingType.MemberBinding: + MemberMemberBinding member = (MemberMemberBinding)binding; + return new MemberMemberBindingRewriter(member, spiller, stack); + } + throw Error.UnhandledBinding(); + } + } + + private class MemberMemberBindingRewriter : BindingRewriter { + ReadOnlyCollection _bindings; + BindingRewriter[] _bindingRewriters; + + internal MemberMemberBindingRewriter(MemberMemberBinding binding, StackSpiller spiller, Stack stack) : + base(binding, spiller) { + + _bindings = binding.Bindings; + _bindingRewriters = new BindingRewriter[_bindings.Count]; + for (int i = 0; i < _bindings.Count; i++) { + BindingRewriter br = BindingRewriter.Create(_bindings[i], spiller, stack); + _action |= br.Action; + _bindingRewriters[i] = br; + } + } + + internal override MemberBinding AsBinding() { + switch (_action) { + case RewriteAction.None: + return _binding; + case RewriteAction.Copy: + MemberBinding[] newBindings = new MemberBinding[_bindings.Count]; + for (int i = 0; i < _bindings.Count; i++) { + newBindings[i] = _bindingRewriters[i].AsBinding(); + } + return Expression.MemberBind(_binding.Member, new ReadOnlyCollection(newBindings)); + } + throw Assert.Unreachable; + } + + internal override Expression AsExpression(Expression target) { + if (target.Type.IsValueType && _binding.Member is System.Reflection.PropertyInfo) { + throw Error.CannotAutoInitializeValueTypeMemberThroughProperty(_binding.Member); + } + + MemberExpression member = Expression.MakeMemberAccess(target, _binding.Member); + ParameterExpression memberTemp = _spiller.MakeTemp(member.Type); + + Expression[] block = new Expression[_bindings.Count + 2]; + block[0] = Expression.Assign(memberTemp, member); + + for (int i = 0; i < _bindings.Count; i++) { + BindingRewriter br = _bindingRewriters[i]; + block[i + 1] = br.AsExpression(memberTemp); + } + + // We need to copy back value types + if (memberTemp.Type.IsValueType) { + block[_bindings.Count + 1] = Expression.Void( + Expression.Assign(Expression.MakeMemberAccess(target, _binding.Member), memberTemp) + ); + } else { + block[_bindings.Count + 1] = Expression.Empty(); + } + return Expression.Block(block); + } + } + + private class ListBindingRewriter : BindingRewriter { + ReadOnlyCollection _inits; + ChildRewriter[] _childRewriters; + + internal ListBindingRewriter(MemberListBinding binding, StackSpiller spiller, Stack stack) : + base(binding, spiller) { + + _inits = binding.Initializers; + + _childRewriters = new ChildRewriter[_inits.Count]; + for (int i = 0; i < _inits.Count; i++) { + ElementInit init = _inits[i]; + + ChildRewriter cr = new ChildRewriter(spiller, stack, init.Arguments.Count); + cr.Add(init.Arguments); + + _action |= cr.Action; + _childRewriters[i] = cr; + } + } + + internal override MemberBinding AsBinding() { + switch (_action) { + case RewriteAction.None: + return _binding; + case RewriteAction.Copy: + ElementInit[] newInits = new ElementInit[_inits.Count]; + for (int i = 0; i < _inits.Count; i++) { + ChildRewriter cr = _childRewriters[i]; + if (cr.Action == RewriteAction.None) { + newInits[i] = _inits[i]; + } else { + newInits[i] = Expression.ElementInit(_inits[i].AddMethod, cr[0, -1]); + } + } + return Expression.ListBind(_binding.Member, new ReadOnlyCollection(newInits)); + } + throw Assert.Unreachable; + } + + internal override Expression AsExpression(Expression target) { + if (target.Type.IsValueType && _binding.Member is System.Reflection.PropertyInfo) { + throw Error.CannotAutoInitializeValueTypeElementThroughProperty(_binding.Member); + } + + MemberExpression member = Expression.MakeMemberAccess(target, _binding.Member); + ParameterExpression memberTemp = _spiller.MakeTemp(member.Type); + + Expression[] block = new Expression[_inits.Count + 2]; + block[0] = Expression.Assign(memberTemp, member); + + for (int i = 0; i < _inits.Count; i++) { + ChildRewriter cr = _childRewriters[i]; + Result add = cr.Finish(Expression.Call(memberTemp, _inits[i].AddMethod, cr[0, -1])); + block[i + 1] = add.Node; + } + + // We need to copy back value types + if (memberTemp.Type.IsValueType) { + block[_inits.Count + 1] = Expression.Void( + Expression.Assign(Expression.MakeMemberAccess(target, _binding.Member), memberTemp) + ); + } else { + block[_inits.Count + 1] = Expression.Empty(); + } + return Expression.Block(block); + } + } + + private class MemberAssignmentRewriter : BindingRewriter { + Expression _rhs; + + internal MemberAssignmentRewriter(MemberAssignment binding, StackSpiller spiller, Stack stack) : + base(binding, spiller) { + + Result result = spiller.RewriteExpression(binding.Expression, stack); + _action = result.Action; + _rhs = result.Node; + } + + internal override MemberBinding AsBinding() { + switch (_action) { + case RewriteAction.None: + return _binding; + case RewriteAction.Copy: + return Expression.Bind(_binding.Member, _rhs); + } + throw Assert.Unreachable; + } + + internal override Expression AsExpression(Expression target) { + MemberExpression member = Expression.MakeMemberAccess(target, _binding.Member); + ParameterExpression memberTemp = _spiller.MakeTemp(member.Type); + + return Expression.Block( + Expression.Assign(memberTemp, _rhs), + Expression.Assign(member, memberTemp), + Expression.Empty() + ); + } + } + + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/StackSpiller.Generated.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/StackSpiller.Generated.cs new file mode 100644 index 0000000000..987e58358b --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/StackSpiller.Generated.cs @@ -0,0 +1,259 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + internal partial class StackSpiller { + + /// + /// Rewrite the expression + /// + /// + /// Expression to rewrite + /// State of the stack before the expression is emitted. + /// Rewritten expression. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private Result RewriteExpression(Expression node, Stack stack) { + if (node == null) { + return new Result(RewriteAction.None, null); + } + + Result result; + switch (node.NodeType) { + #region Generated StackSpiller Switch + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_stackspiller_switch from: generate_tree.py + + case ExpressionType.Add: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.AddChecked: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.And: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.AndAlso: + result = RewriteLogicalBinaryExpression(node, stack); + break; + case ExpressionType.ArrayLength: + result = RewriteUnaryExpression(node, stack); + break; + case ExpressionType.ArrayIndex: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.Call: + result = RewriteMethodCallExpression(node, stack); + break; + case ExpressionType.Coalesce: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.Conditional: + result = RewriteConditionalExpression(node, stack); + break; + case ExpressionType.Convert: + result = RewriteUnaryExpression(node, stack); + break; + case ExpressionType.ConvertChecked: + result = RewriteUnaryExpression(node, stack); + break; + case ExpressionType.Divide: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.Equal: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.ExclusiveOr: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.GreaterThan: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.GreaterThanOrEqual: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.Invoke: + result = RewriteInvocationExpression(node, stack); + break; + case ExpressionType.Lambda: + result = RewriteLambdaExpression(node, stack); + break; + case ExpressionType.LeftShift: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.LessThan: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.LessThanOrEqual: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.ListInit: + result = RewriteListInitExpression(node, stack); + break; + case ExpressionType.MemberAccess: + result = RewriteMemberExpression(node, stack); + break; + case ExpressionType.MemberInit: + result = RewriteMemberInitExpression(node, stack); + break; + case ExpressionType.Modulo: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.Multiply: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.MultiplyChecked: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.Negate: + result = RewriteUnaryExpression(node, stack); + break; + case ExpressionType.UnaryPlus: + result = RewriteUnaryExpression(node, stack); + break; + case ExpressionType.NegateChecked: + result = RewriteUnaryExpression(node, stack); + break; + case ExpressionType.New: + result = RewriteNewExpression(node, stack); + break; + case ExpressionType.NewArrayInit: + result = RewriteNewArrayExpression(node, stack); + break; + case ExpressionType.NewArrayBounds: + result = RewriteNewArrayExpression(node, stack); + break; + case ExpressionType.Not: + result = RewriteUnaryExpression(node, stack); + break; + case ExpressionType.NotEqual: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.Or: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.OrElse: + result = RewriteLogicalBinaryExpression(node, stack); + break; + case ExpressionType.Power: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.RightShift: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.Subtract: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.SubtractChecked: + result = RewriteBinaryExpression(node, stack); + break; + case ExpressionType.TypeAs: + result = RewriteUnaryExpression(node, stack); + break; + case ExpressionType.TypeIs: + result = RewriteTypeBinaryExpression(node, stack); + break; + case ExpressionType.Assign: + result = RewriteAssignBinaryExpression(node, stack); + break; + case ExpressionType.Block: + result = RewriteBlockExpression(node, stack); + break; + case ExpressionType.DebugInfo: + result = RewriteDebugInfoExpression(node, stack); + break; + case ExpressionType.Decrement: + result = RewriteUnaryExpression(node, stack); + break; + case ExpressionType.Dynamic: + result = RewriteDynamicExpression(node, stack); + break; + case ExpressionType.Extension: + result = RewriteExtensionExpression(node, stack); + break; + case ExpressionType.Goto: + result = RewriteGotoExpression(node, stack); + break; + case ExpressionType.Increment: + result = RewriteUnaryExpression(node, stack); + break; + case ExpressionType.Index: + result = RewriteIndexExpression(node, stack); + break; + case ExpressionType.Label: + result = RewriteLabelExpression(node, stack); + break; + case ExpressionType.Loop: + result = RewriteLoopExpression(node, stack); + break; + case ExpressionType.Switch: + result = RewriteSwitchExpression(node, stack); + break; + case ExpressionType.Throw: + result = RewriteThrowUnaryExpression(node, stack); + break; + case ExpressionType.Try: + result = RewriteTryExpression(node, stack); + break; + case ExpressionType.Unbox: + result = RewriteUnaryExpression(node, stack); + break; + case ExpressionType.AddAssign: + case ExpressionType.AndAssign: + case ExpressionType.DivideAssign: + case ExpressionType.ExclusiveOrAssign: + case ExpressionType.LeftShiftAssign: + case ExpressionType.ModuloAssign: + case ExpressionType.MultiplyAssign: + case ExpressionType.OrAssign: + case ExpressionType.PowerAssign: + case ExpressionType.RightShiftAssign: + case ExpressionType.SubtractAssign: + case ExpressionType.AddAssignChecked: + case ExpressionType.MultiplyAssignChecked: + case ExpressionType.SubtractAssignChecked: + case ExpressionType.PreIncrementAssign: + case ExpressionType.PreDecrementAssign: + case ExpressionType.PostIncrementAssign: + case ExpressionType.PostDecrementAssign: + case ExpressionType.TypeEqual: + result = RewriteReducibleExpression(node, stack); + break; + case ExpressionType.Quote: + case ExpressionType.Parameter: + case ExpressionType.Constant: + case ExpressionType.RuntimeVariables: + case ExpressionType.Default: + return new Result(RewriteAction.None, node); + + // *** END GENERATED CODE *** + + #endregion + + default: + throw Assert.Unreachable; + } + + VerifyRewrite(result, node); + + return result; + } + } +} + diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/StackSpiller.Temps.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/StackSpiller.Temps.cs new file mode 100644 index 0000000000..046a9c76a7 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/StackSpiller.Temps.cs @@ -0,0 +1,265 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + + internal partial class StackSpiller { + + private class TempMaker { + /// + /// Current temporary variable + /// + private int _temp; + + /// + /// List of free temporary variables. These can be recycled for new temps. + /// + private List _freeTemps; + + /// + /// Stack of currently active temporary variables. + /// + private Stack _usedTemps; + + /// + /// List of all temps created by stackspiller for this rule/lambda + /// + private List _temps = new List(); + + internal List Temps { + get { return _temps; } + } + + internal ParameterExpression Temp(Type type) { + ParameterExpression temp; + if (_freeTemps != null) { + // Recycle from the free-list if possible. + for (int i = _freeTemps.Count - 1; i >= 0; i--) { + temp = _freeTemps[i]; + if (temp.Type == type) { + _freeTemps.RemoveAt(i); + return UseTemp(temp); + } + } + } + // Not on the free-list, create a brand new one. + temp = Expression.Variable(type, "$temp$" + _temp++); + _temps.Add(temp); + return UseTemp(temp); + } + + private ParameterExpression UseTemp(ParameterExpression temp) { + Debug.Assert(_freeTemps == null || !_freeTemps.Contains(temp)); + Debug.Assert(_usedTemps == null || !_usedTemps.Contains(temp)); + + if (_usedTemps == null) { + _usedTemps = new Stack(); + } + _usedTemps.Push(temp); + return temp; + } + + private void FreeTemp(ParameterExpression temp) { + Debug.Assert(_freeTemps == null || !_freeTemps.Contains(temp)); + if (_freeTemps == null) { + _freeTemps = new List(); + } + _freeTemps.Add(temp); + } + + internal int Mark() { + return _usedTemps != null ? _usedTemps.Count : 0; + } + + // Free temporaries created since the last marking. + // This is a performance optimization to lower the overall number of tempories needed. + internal void Free(int mark) { + // (_usedTemps != null) ==> (mark <= _usedTemps.Count) + Debug.Assert(_usedTemps == null || mark <= _usedTemps.Count); + // (_usedTemps == null) ==> (mark == 0) + Debug.Assert(mark == 0 || _usedTemps != null); + + if (_usedTemps != null) { + while (mark < _usedTemps.Count) { + FreeTemp(_usedTemps.Pop()); + } + } + } + + [Conditional("DEBUG")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + internal void VerifyTemps() { + Debug.Assert(_usedTemps == null || _usedTemps.Count == 0); + } + } + + + /// + /// Rewrites child expressions, spilling them into temps if needed. The + /// stack starts in the inital state, and after the first subexpression + /// is added it is change to non-empty. This behavior can be overriden + /// by setting the stack manually between adds. + /// + /// When all children have been added, the caller should rewrite the + /// node if Rewrite is true. Then, it should call crFinish with etiher + /// the orignal expression or the rewritten expression. Finish will call + /// Expression.Comma if necessary and return a new Result. + /// + private class ChildRewriter { + private readonly StackSpiller _self; + private readonly Expression[] _expressions; + private int _expressionsCount; + private List _comma; + private RewriteAction _action; + private Stack _stack; + private bool _done; + + internal ChildRewriter(StackSpiller self, Stack stack, int count) { + _self = self; + _stack = stack; + _expressions = new Expression[count]; + } + + internal void Add(Expression node) { + Debug.Assert(!_done); + + if (node == null) { + _expressions[_expressionsCount++] = null; + return; + } + + Result exp = _self.RewriteExpression(node, _stack); + _action |= exp.Action; + _stack = Stack.NonEmpty; + + // track items in case we need to copy or spill stack + _expressions[_expressionsCount++] = exp.Node; + } + + internal void Add(IList expressions) { + for (int i = 0, count = expressions.Count; i < count; i++) { + Add(expressions[i]); + } + } + + private void EnsureDone() { + // done adding arguments, build the comma if necessary + if (!_done) { + _done = true; + + if (_action == RewriteAction.SpillStack) { + Expression[] clone = _expressions; + int count = clone.Length; + List comma = new List(count + 1); + for (int i = 0; i < count; i++) { + if (clone[i] != null) { + Expression temp; + clone[i] = _self.ToTemp(clone[i], out temp); + comma.Add(temp); + } + } + comma.Capacity = comma.Count + 1; + _comma = comma; + } + } + } + + internal bool Rewrite { + get { return _action != RewriteAction.None; } + } + + internal RewriteAction Action { + get { return _action; } + } + + internal Result Finish(Expression expr) { + EnsureDone(); + + if (_action == RewriteAction.SpillStack) { + Debug.Assert(_comma.Capacity == _comma.Count + 1); + _comma.Add(expr); + expr = Expression.Block(new ReadOnlyCollection(_comma)); + } + + return new Result(_action, expr); + } + + internal Expression this[int index] { + get { + EnsureDone(); + if (index < 0) { + index += _expressions.Length; + } + return _expressions[index]; + } + } + internal Expression[] this[int first, int last] { + get { + EnsureDone(); + if (last < 0) { + last += _expressions.Length; + } + int count = last - first + 1; + ContractUtils.RequiresArrayRange(_expressions, first, count, "first", "last"); + + if (count == _expressions.Length) { + Debug.Assert(first == 0); + // if the entire array is requested just return it so we don't make a new array + return _expressions; + } + + Expression[] clone = new Expression[count]; + Array.Copy(_expressions, first, clone, 0, count); + return clone; + } + } + } + + + private ParameterExpression MakeTemp(Type type) { + return _tm.Temp(type); + } + + private int Mark() { + return _tm.Mark(); + } + + private void Free(int mark) { + _tm.Free(mark); + } + + [Conditional("DEBUG")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + private void VerifyTemps() { + _tm.VerifyTemps(); + } + + /// + /// Will perform: + /// save: temp = expression + /// return value: temp + /// + private ParameterExpression ToTemp(Expression expression, out Expression save) { + ParameterExpression temp = MakeTemp(expression.Type); + save = Expression.Assign(temp, expression); + return temp; + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/StackSpiller.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/StackSpiller.cs new file mode 100644 index 0000000000..1dbfa39bd9 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/StackSpiller.cs @@ -0,0 +1,847 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Dynamic.Binders; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + + /// + /// Expression rewriting to spill the CLR stack into temporary variables + /// in order to guarantee some properties of code generation, for + /// example that we always enter try block on empty stack. + /// + internal partial class StackSpiller { + + // Is the evaluation stack empty? + private enum Stack { + Empty, + NonEmpty + }; + + // Should the parent nodes be rewritten, and in what way? + // Designed so bitwise-or produces the correct result when merging two + // subtrees. In particular, SpillStack is preferred over Copy which is + // preferred over None. + // + // Values: + // None -> no rewrite needed + // Copy -> copy into a new node + // SpillStack -> spill stack into temps + [Flags] + private enum RewriteAction { + None = 0, + Copy = 1, + SpillStack = 3, + } + + // Result of a rewrite operation. Always contains an action and a node. + private struct Result { + internal readonly RewriteAction Action; + internal readonly Expression Node; + + internal Result(RewriteAction action, Expression node) { + Action = action; + Node = node; + } + } + + /// + /// The source of temporary variables + /// + private readonly TempMaker _tm = new TempMaker(); + + #region StackSpiller entry points + + /// + /// Analyzes a lambda, producing a new one that has correct invariants + /// for codegen. In particular, it spills the IL stack to temps in + /// places where it's invalid to have a non-empty stack (for example, + /// entering a try statement). + /// + internal static LambdaExpression AnalyzeLambda(LambdaExpression lambda) { + return lambda.Accept(new StackSpiller()); + } + + #endregion + + private StackSpiller() { + } + + // called by Expression.Accept + internal Expression Rewrite(Expression lambda) { + VerifyTemps(); + + // Lambda starts with an empty stack + Result body = RewriteExpressionFreeTemps(lambda.Body, Stack.Empty); + + VerifyTemps(); + + if (body.Action != RewriteAction.None) { + // Create a new scope for temps + // (none of these will be hoisted so there is no closure impact) + Expression newBody = body.Node; + if (_tm.Temps.Count > 0) { + newBody = Expression.Block(_tm.Temps, newBody); + } + + // Clone the lambda, replacing the body & variables + return new Expression(lambda.Name, newBody, lambda.Parameters); + } + + return lambda; + } + + #region Expressions + + [Conditional("DEBUG")] + private static void VerifyRewrite(Result result, Expression node) { + Debug.Assert(result.Node != null); + + // (result.Action == RewriteAction.None) if and only if (node == result.Node) + Debug.Assert((result.Action == RewriteAction.None) ^ (node != result.Node), "rewrite action does not match node object identity"); + + // if the original node is an extension node, it should have been rewritten + Debug.Assert(result.Node.NodeType != ExpressionType.Extension, "extension nodes must be rewritten"); + + // if we have Copy, then node type must match + Debug.Assert( + result.Action != RewriteAction.Copy || node.NodeType == result.Node.NodeType || node.CanReduce, + "rewrite action does not match node object kind" + ); + + // New type must be reference assignable to the old type + // (our rewrites preserve type exactly, but the rules for rewriting + // an extension node are more lenient, see Expression.ReduceAndCheck()) + Debug.Assert( + TypeUtils.AreReferenceAssignable(node.Type, result.Node.Type), + "rewritten object must be reference assignable to the original type" + ); + } + + private Result RewriteExpressionFreeTemps(Expression expression, Stack stack) { + int mark = Mark(); + Result result = RewriteExpression(expression, stack); + Free(mark); + return result; + } + + // DynamicExpression + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "stack")] + private Result RewriteDynamicExpression(Expression expr, Stack stack) { + var node = (DynamicExpression)expr; + + // CallSite is on the stack + IArgumentProvider argNode = (IArgumentProvider)node; + int argCount = argNode.ArgumentCount; + ChildRewriter cr = new ChildRewriter(this, Stack.NonEmpty, argCount); + for (int i = 0; i < argCount; i++) { + cr.Add(argNode.GetArgument(i)); + } + return cr.Finish(cr.Rewrite ? node.Rewrite(cr[0, -1]) : expr); + } + + private Result RewriteIndexAssignment(BinaryExpression node, Stack stack) { + IndexExpression index = (IndexExpression)node.Left; + + ChildRewriter cr = new ChildRewriter(this, stack, 2 + index.Arguments.Count); + + cr.Add(index.Object); + cr.Add(index.Arguments); + cr.Add(node.Right); + + if (cr.Rewrite) { + node = new AssignBinaryExpression( + new IndexExpression( + cr[0], // Object + index.Indexer, + cr[1, -2] // arguments + ), + cr[-1] // value + ); + } + + return cr.Finish(node); + } + + private Result RewriteArrayIndexAssignment(BinaryExpression node, Stack stack) { + Debug.Assert(node.NodeType == ExpressionType.ArrayIndex); + BinaryExpression arrayIndex = (BinaryExpression)node.Left; + + ChildRewriter cr = new ChildRewriter(this, stack, 3); + + cr.Add(arrayIndex.Left); + cr.Add(arrayIndex.Right); + cr.Add(node.Right); + + if (cr.Rewrite) { + node = new AssignBinaryExpression( + Expression.ArrayIndex( + cr[0], // array + cr[1] // index + ), + cr[2] // value + ); + } + + return cr.Finish(node); + } + + // BinaryExpression: AndAlso, OrElse + private Result RewriteLogicalBinaryExpression(Expression expr, Stack stack) { + BinaryExpression node = (BinaryExpression)expr; + // Left expression runs on a stack as left by parent + Result left = RewriteExpression(node.Left, stack); + // ... and so does the right one + Result right = RewriteExpression(node.Right, stack); + //conversion is a lambda. stack state will be ignored. + Result conversion = RewriteExpression(node.Conversion, stack); + + RewriteAction action = left.Action | right.Action | conversion.Action; + if (action != RewriteAction.None) { + expr = BinaryExpression.Create( + node.NodeType, + left.Node, + right.Node, + node.Type, + node.Method, + (LambdaExpression)conversion.Node + ); + } + return new Result(action, expr); + } + + private Result RewriteReducibleExpression(Expression expr, Stack stack) { + Result result = RewriteExpression(expr.Reduce(), stack); + // it's at least Copy because we reduced the node + return new Result(result.Action | RewriteAction.Copy, result.Node); + } + + // BinaryExpression + private Result RewriteBinaryExpression(Expression expr, Stack stack) { + BinaryExpression node = (BinaryExpression)expr; + + ChildRewriter cr = new ChildRewriter(this, stack, 3); + // Left expression executes on the stack as left by parent + cr.Add(node.Left); + // Right expression always has non-empty stack (left is on it) + cr.Add(node.Right); + // conversion is a lambda, stack state will be ignored + cr.Add(node.Conversion); + + return cr.Finish(cr.Rewrite ? + BinaryExpression.Create( + node.NodeType, + cr[0], + cr[1], + node.Type, + node.Method, + (LambdaExpression)cr[2]) : + expr); + } + + // variable assignment + private Result RewriteVariableAssignment(BinaryExpression node, Stack stack) { + // Expression is evaluated on a stack in current state + Result right = RewriteExpression(node.Right, stack); + if (right.Action != RewriteAction.None) { + node = Expression.Assign(node.Left, right.Node); + } + return new Result(right.Action, node); + } + + private Result RewriteAssignBinaryExpression(Expression expr, Stack stack) { + var node = (BinaryExpression)expr; + + switch (node.Left.NodeType) { + case ExpressionType.Index: + return RewriteIndexAssignment(node, stack); + case ExpressionType.MemberAccess: + return RewriteMemberAssignment(node, stack); + case ExpressionType.Parameter: + return RewriteVariableAssignment(node, stack); + case ExpressionType.Extension: + return RewriteExtensionAssignment(node, stack); + case ExpressionType.ArrayIndex: + return RewriteArrayIndexAssignment(node, stack); + default: + throw Error.InvalidLvalue(node.Left.NodeType); + } + } + + private Result RewriteExtensionAssignment(BinaryExpression node, Stack stack) { + node = Expression.Assign(node.Left.ReduceExtensions(), node.Right); + Result result = RewriteAssignBinaryExpression(node, stack); + // it's at least Copy because we reduced the node + return new Result(result.Action | RewriteAction.Copy, result.Node); + } + + // LambdaExpression + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "stack")] + private static Result RewriteLambdaExpression(Expression expr, Stack stack) { + LambdaExpression node = (LambdaExpression)expr; + + // Call back into the rewriter + expr = AnalyzeLambda(node); + + // If the lambda gets rewritten, we don't need to spill the stack, + // but we do need to rebuild the tree above us so it includes the new node. + RewriteAction action = (expr == node) ? RewriteAction.None : RewriteAction.Copy; + + return new Result(action, expr); + } + + // ConditionalExpression + private Result RewriteConditionalExpression(Expression expr, Stack stack) { + ConditionalExpression node = (ConditionalExpression)expr; + // Test executes at the stack as left by parent + Result test = RewriteExpression(node.Test, stack); + // The test is popped by conditional jump so branches execute + // at the stack as left by parent too. + Result ifTrue = RewriteExpression(node.IfTrue, stack); + Result ifFalse = RewriteExpression(node.IfFalse, stack); + + RewriteAction action = test.Action | ifTrue.Action | ifFalse.Action; + if (action != RewriteAction.None) { + expr = Expression.Condition(test.Node, ifTrue.Node, ifFalse.Node); + } + + return new Result(action, expr); + } + + // member assignment + private Result RewriteMemberAssignment(BinaryExpression node, Stack stack) { + MemberExpression lvalue = (MemberExpression)node.Left; + + ChildRewriter cr = new ChildRewriter(this, stack, 2); + + // If there's an instance, it executes on the stack in current state + // and rest is executed on non-empty stack. + // Otherwise the stack is left unchaged. + cr.Add(lvalue.Expression); + + cr.Add(node.Right); + + if (cr.Rewrite) { + return cr.Finish( + new AssignBinaryExpression( + MemberExpression.Make(cr[0], lvalue.Member), + cr[1] + ) + ); + } + return new Result(RewriteAction.None, node); + } + + // MemberExpression + private Result RewriteMemberExpression(Expression expr, Stack stack) { + MemberExpression node = (MemberExpression)expr; + + // Expression is emitted on top of the stack in current state + Result expression = RewriteExpression(node.Expression, stack); + if (expression.Action != RewriteAction.None) { + expr = MemberExpression.Make(expression.Node, node.Member); + } + return new Result(expression.Action, expr); + } + + //RewriteIndexExpression + private Result RewriteIndexExpression(Expression expr, Stack stack) { + IndexExpression node = (IndexExpression)expr; + + ChildRewriter cr = new ChildRewriter(this, stack, node.Arguments.Count + 1); + + // For instance methods, the instance executes on the + // stack as is, but stays on the stack, making it non-empty. + cr.Add(node.Object); + cr.Add(node.Arguments); + + if (cr.Rewrite) { + expr = new IndexExpression( + cr[0], + node.Indexer, + cr[1, -1] + ); + } + + return cr.Finish(expr); + } + + // MethodCallExpression + // TODO: ref parameters!!! + private Result RewriteMethodCallExpression(Expression expr, Stack stack) { + MethodCallExpression node = (MethodCallExpression)expr; + + ChildRewriter cr = new ChildRewriter(this, stack, node.Arguments.Count + 1); + + // For instance methods, the instance executes on the + // stack as is, but stays on the stack, making it non-empty. + cr.Add(node.Object); + + cr.Add(node.Arguments); + + return cr.Finish(cr.Rewrite ? node.Rewrite(cr[0], cr[1, -1]) : expr); + } + + // NewArrayExpression + private Result RewriteNewArrayExpression(Expression expr, Stack stack) { + NewArrayExpression node = (NewArrayExpression)expr; + + if (node.NodeType == ExpressionType.NewArrayInit) { + // In a case of array construction with element initialization + // the element expressions are never emitted on an empty stack because + // the array reference and the index are on the stack. + stack = Stack.NonEmpty; + } else { + // In a case of NewArrayBounds we make no modifications to the stack + // before emitting bounds expressions. + } + + ChildRewriter cr = new ChildRewriter(this, stack, node.Expressions.Count); + cr.Add(node.Expressions); + + return cr.Finish(cr.Rewrite ? Expression.NewArrayInit(node.Type.GetElementType(), cr[0, -1]) : expr); + } + + // InvocationExpression + private Result RewriteInvocationExpression(Expression expr, Stack stack) { + InvocationExpression node = (InvocationExpression)expr; + + // first argument starts on stack as provided + ChildRewriter cr = new ChildRewriter(this, stack, node.Arguments.Count + 1); + cr.Add(node.Expression); + + // rest of arguments have non-empty stack (delegate instance on the stack) + cr.Add(node.Arguments); + + return cr.Finish(cr.Rewrite ? new InvocationExpression(cr[0], cr[1, -1], node.Type) : expr); + } + + // NewExpression + private Result RewriteNewExpression(Expression expr, Stack stack) { + NewExpression node = (NewExpression)expr; + + // The first expression starts on a stack as provided by parent, + // rest are definitely non-emtpy (which ChildRewriter guarantees) + ChildRewriter cr = new ChildRewriter(this, stack, node.Arguments.Count); + cr.Add(node.Arguments); + + return cr.Finish(cr.Rewrite ? new NewExpression(node.Constructor, cr[0, -1], node.Members) : expr); + } + + // TypeBinaryExpression + private Result RewriteTypeBinaryExpression(Expression expr, Stack stack) { + TypeBinaryExpression node = (TypeBinaryExpression)expr; + // The expression is emitted on top of current stack + Result expression = RewriteExpression(node.Expression, stack); + if (expression.Action != RewriteAction.None) { + expr = Expression.TypeIs(expression.Node, node.TypeOperand); + } + return new Result(expression.Action, expr); + } + + // Throw + private Result RewriteThrowUnaryExpression(Expression expr, Stack stack) { + UnaryExpression node = (UnaryExpression)expr; + + // Throw statement itself does not care about the stack + // but it will empty the stack and it may cause stack misbalance + // it so we need to restore stack after unconditional throw to make JIT happy + // this has an effect of executing Throw on an empty stack. + + Result value = RewriteExpressionFreeTemps(node.Operand, Stack.Empty); + RewriteAction action = value.Action; + + if (stack != Stack.Empty) { + action = RewriteAction.SpillStack; + } + + if (action != RewriteAction.None) { + expr = Expression.Throw(value.Node, node.Type); + } + + return new Result(action, expr); + } + + // UnaryExpression + private Result RewriteUnaryExpression(Expression expr, Stack stack) { + UnaryExpression node = (UnaryExpression)expr; + + Debug.Assert(node.NodeType != ExpressionType.Quote, "unexpected Quote"); + Debug.Assert(node.NodeType != ExpressionType.Throw, "unexpected Throw"); + + // Operand is emitted on top of the stack as is + Result expression = RewriteExpression(node.Operand, stack); + if (expression.Action != RewriteAction.None) { + expr = new UnaryExpression(node.NodeType, expression.Node, node.Type, node.Method); + } + return new Result(expression.Action, expr); + } + + // RewriteListInitExpression + private Result RewriteListInitExpression(Expression expr, Stack stack) { + ListInitExpression node = (ListInitExpression)expr; + + //ctor runs on initial stack + Result newResult = RewriteExpression(node.NewExpression, stack); + Expression rewrittenNew = newResult.Node; + RewriteAction action = newResult.Action; + + ReadOnlyCollection inits = node.Initializers; + + ChildRewriter[] cloneCrs = new ChildRewriter[inits.Count]; + + for (int i = 0; i < inits.Count; i++) { + ElementInit init = inits[i]; + + //initializers all run on nonempty stack + ChildRewriter cr = new ChildRewriter(this, Stack.NonEmpty, init.Arguments.Count); + cr.Add(init.Arguments); + + action |= cr.Action; + cloneCrs[i] = cr; + } + + switch (action) { + case RewriteAction.None: + break; + case RewriteAction.Copy: + ElementInit[] newInits = new ElementInit[inits.Count]; + for (int i = 0; i < inits.Count; i++) { + ChildRewriter cr = cloneCrs[i]; + if (cr.Action == RewriteAction.None) { + newInits[i] = inits[i]; + } else { + newInits[i] = Expression.ElementInit(inits[i].AddMethod, cr[0, -1]); + } + } + expr = Expression.ListInit((NewExpression)rewrittenNew, new ReadOnlyCollection(newInits)); + break; + case RewriteAction.SpillStack: + ParameterExpression tempNew = MakeTemp(rewrittenNew.Type); + Expression[] comma = new Expression[inits.Count + 2]; + comma[0] = Expression.Assign(tempNew, rewrittenNew); + + for (int i = 0; i < inits.Count; i++) { + ChildRewriter cr = cloneCrs[i]; + Result add = cr.Finish(Expression.Call(tempNew, inits[i].AddMethod, cr[0, -1])); + comma[i + 1] = add.Node; + } + comma[inits.Count + 1] = tempNew; + expr = Expression.Block(comma); + break; + default: + throw Assert.Unreachable; + } + + return new Result(action, expr); + } + + // RewriteMemberInitExpression + private Result RewriteMemberInitExpression(Expression expr, Stack stack) { + MemberInitExpression node = (MemberInitExpression)expr; + + //ctor runs on original stack + Result result = RewriteExpression(node.NewExpression, stack); + Expression rewrittenNew = result.Node; + RewriteAction action = result.Action; + + ReadOnlyCollection bindings = node.Bindings; + BindingRewriter[] bindingRewriters = new BindingRewriter[bindings.Count]; + for (int i = 0; i < bindings.Count; i++) { + MemberBinding binding = bindings[i]; + //bindings run on nonempty stack + BindingRewriter rewriter = BindingRewriter.Create(binding, this, Stack.NonEmpty); + bindingRewriters[i] = rewriter; + action |= rewriter.Action; + } + + switch (action) { + case RewriteAction.None: + break; + case RewriteAction.Copy: + MemberBinding[] newBindings = new MemberBinding[bindings.Count]; + for (int i = 0; i < bindings.Count; i++) { + newBindings[i] = bindingRewriters[i].AsBinding(); + } + expr = Expression.MemberInit((NewExpression)rewrittenNew, new ReadOnlyCollection(newBindings)); + break; + case RewriteAction.SpillStack: + ParameterExpression tempNew = MakeTemp(rewrittenNew.Type); + Expression[] comma = new Expression[bindings.Count + 2]; + comma[0] = Expression.Assign(tempNew, rewrittenNew); + for (int i = 0; i < bindings.Count; i++) { + BindingRewriter cr = bindingRewriters[i]; + Expression initExpr = cr.AsExpression(tempNew); + comma[i + 1] = initExpr; + } + comma[bindings.Count + 1] = tempNew; + expr = Expression.Block(comma); + break; + default: + throw Assert.Unreachable; + } + return new Result(action, expr); + } + + #endregion + + #region Statements + + // Block + private Result RewriteBlockExpression(Expression expr, Stack stack) { + BlockExpression node = (BlockExpression)expr; + + int count = node.ExpressionCount; + RewriteAction action = RewriteAction.None; + Expression[] clone = null; + for (int i = 0; i < count; i++) { + Expression expression = node.GetExpression(i); + // All statements within the block execute at the + // same stack state. + Result rewritten = RewriteExpression(expression, stack); + action |= rewritten.Action; + + if (clone == null && rewritten.Action != RewriteAction.None) { + clone = Clone(node.Expressions, i); + } + + if (clone != null) { + clone[i] = rewritten.Node; + } + } + + if (action != RewriteAction.None) { + // okay to wrap since we know no one can mutate the clone array + expr = node.Rewrite(null, clone); + } + return new Result(action, expr); + } + + // LabelExpression + private Result RewriteLabelExpression(Expression expr, Stack stack) { + LabelExpression node = (LabelExpression)expr; + + Result expression = RewriteExpression(node.DefaultValue, stack); + if (expression.Action != RewriteAction.None) { + expr = Expression.Label(node.Label, expression.Node); + } + return new Result(expression.Action, expr); + } + + // LoopStatement + private Result RewriteLoopExpression(Expression expr, Stack stack) { + LoopExpression node = (LoopExpression)expr; + + // The loop statement requires empty stack for itself, so it + // can guarantee it to the child nodes. + Result body = RewriteExpression(node.Body, Stack.Empty); + + RewriteAction action = body.Action; + + // However, the loop itself requires that it executes on an empty stack + // so we need to rewrite if the stack is not empty. + if (stack != Stack.Empty) { + action = RewriteAction.SpillStack; + } + + if (action != RewriteAction.None) { + expr = new LoopExpression(body.Node, node.BreakLabel, node.ContinueLabel); + } + return new Result(action, expr); + } + + // GotoExpression + // Note: goto does not necessarily need an empty stack. We could always + // emit it as a "leave" which would clear the stack for us. That would + // prevent us from doing certain optimizations we might want to do, + // however, like the switch-case-goto pattern. For now, be conservative + private Result RewriteGotoExpression(Expression expr, Stack stack) { + GotoExpression node = (GotoExpression)expr; + + // Goto requires empty stack to execute so the expression is + // going to execute on an empty stack. + Result value = RewriteExpressionFreeTemps(node.Value, Stack.Empty); + + // However, the statement itself needs an empty stack for itself + // so if stack is not empty, rewrite to empty the stack. + RewriteAction action = value.Action; + if (stack != Stack.Empty) { + action = RewriteAction.SpillStack; + } + + if (action != RewriteAction.None) { + expr = Expression.MakeGoto(node.Kind, node.Target, value.Node); + } + return new Result(action, expr); + } + + // DebugInfoExpression + private Result RewriteDebugInfoExpression(Expression expr, Stack stack) { + var node = (DebugInfoExpression)expr; + + Result body = RewriteExpression(node.Expression, stack); + + RewriteAction action = body.Action; + if (action != RewriteAction.None) { + expr = new DebugInfoExpression(body.Node, node.Document, node.StartLine, node.StartColumn, node.EndLine, node.EndColumn); + } + return new Result(action, expr); + } + + // SwitchStatement + private Result RewriteSwitchExpression(Expression expr, Stack stack) { + SwitchExpression node = (SwitchExpression)expr; + + // The switch statement test is emitted on the stack in current state + Result test = RewriteExpressionFreeTemps(node.Test, stack); + + RewriteAction action = test.Action; + ReadOnlyCollection cases = node.SwitchCases; + SwitchCase[] clone = null; + for (int i = 0; i < cases.Count; i++) { + SwitchCase @case = cases[i]; + + // And all the cases also run on the same stack level. + Result body = RewriteExpression(@case.Body, stack); + action |= body.Action; + + if (body.Action != RewriteAction.None) { + @case = new SwitchCase(@case.IsDefault, @case.Value, body.Node); + + if (clone == null) { + clone = Clone(cases, i); + } + } + + if (clone != null) { + clone[i] = @case; + } + } + + if (action != RewriteAction.None) { + if (clone != null) { + // okay to wrap because we aren't modifying the array + cases = new ReadOnlyCollection(clone); + } + + expr = new SwitchExpression(test.Node, node.BreakLabel, cases); + } + + return new Result(action, expr); + } + + // TryStatement + private Result RewriteTryExpression(Expression expr, Stack stack) { + TryExpression node = (TryExpression)expr; + + // Try statement definitely needs an empty stack so its + // child nodes execute at empty stack. + Result body = RewriteExpression(node.Body, Stack.Empty); + ReadOnlyCollection handlers = node.Handlers; + CatchBlock[] clone = null; + + RewriteAction action = body.Action; + if (handlers != null) { + for (int i = 0; i < handlers.Count; i++) { + RewriteAction curAction = body.Action; + + CatchBlock handler = handlers[i]; + + Expression filter = handler.Filter; + if (handler.Filter != null) { + // our code gen saves the incoming filter value and provides it as a varaible so the stack is empty + Result rfault = RewriteExpression(handler.Filter, Stack.Empty); + action |= rfault.Action; + curAction |= rfault.Action; + filter = rfault.Node; + } + + // Catch block starts with an empty stack (guaranteed by TryStatement) + Result rbody = RewriteExpression(handler.Body, Stack.Empty); + action |= rbody.Action; + curAction |= rbody.Action; + + if (curAction != RewriteAction.None) { + handler = Expression.MakeCatchBlock(handler.Test, handler.Variable, rbody.Node, filter); + + if (clone == null) { + clone = Clone(handlers, i); + } + } + + if (clone != null) { + clone[i] = handler; + } + } + } + + Result fault = RewriteExpression(node.Fault, Stack.Empty); + action |= fault.Action; + + Result @finally = RewriteExpression(node.Finally, Stack.Empty); + action |= @finally.Action; + + // If the stack is initially not empty, rewrite to spill the stack + if (stack != Stack.Empty) { + action = RewriteAction.SpillStack; + } + + if (action != RewriteAction.None) { + if (clone != null) { + // okay to wrap because we aren't modifying the array + handlers = new ReadOnlyCollection(clone); + } + + expr = new TryExpression(body.Node, @finally.Node, fault.Node, handlers); + } + return new Result(action, expr); + } + + private Result RewriteExtensionExpression(Expression expr, Stack stack) { + Result result = RewriteExpression(expr.ReduceExtensions(), stack); + // it's at least Copy because we reduced the node + return new Result(result.Action | RewriteAction.Copy, result.Node); + } + + #endregion + + #region Cloning + + /// + /// Will clone an IList into an array of the same size, and copy + /// all vaues up to (and NOT including) the max index + /// + /// The cloned array. + private static T[] Clone(ReadOnlyCollection original, int max) { + Debug.Assert(original != null); + Debug.Assert(max < original.Count); + + T[] clone = new T[original.Count]; + for (int j = 0; j < max; j++) { + clone[j] = original[j]; + } + return clone; + } + + #endregion + } + + internal partial class StackSpiller { + + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Compiler/VariableBinder.cs b/ndp/fx/src/core/microsoft/scripting/Compiler/VariableBinder.cs new file mode 100644 index 0000000000..8961e42d91 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Compiler/VariableBinder.cs @@ -0,0 +1,199 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic.Utils; + +namespace System.Linq.Expressions.Compiler { + /// + /// Determines if variables are closed over in nested lambdas and need to + /// be hoisted. + /// + internal sealed class VariableBinder : ExpressionVisitor { + private readonly AnalyzedTree _tree = new AnalyzedTree(); + private readonly Stack _scopes = new Stack(); + private readonly Stack _constants = new Stack(); + private bool _inQuote; + + internal static AnalyzedTree Bind(LambdaExpression lambda) { + var binder = new VariableBinder(); + binder.Visit(lambda); + return binder._tree; + } + + private VariableBinder() { + } + + protected internal override Expression VisitConstant(ConstantExpression node) { + // If we're in Quote, we can ignore constants completely + if (_inQuote) { + return node; + } + + // Constants that can be emitted into IL don't need to be stored on + // the delegate + if (ILGen.CanEmitConstant(node.Value, node.Type)) { + return node; + } + + _constants.Peek().AddReference(node.Value, node.Type); + return node; + } + + protected internal override Expression VisitUnary(UnaryExpression node) { + if (node.NodeType == ExpressionType.Quote) { + bool savedInQuote = _inQuote; + _inQuote = true; + Visit(node.Operand); + _inQuote = savedInQuote; + } else { + Visit(node.Operand); + } + return node; + } + + protected internal override Expression VisitLambda(Expression node) { + _scopes.Push(_tree.Scopes[node] = new CompilerScope(node)); + _constants.Push(_tree.Constants[node] = new BoundConstants()); + Visit(MergeScopes(node)); + _constants.Pop(); + _scopes.Pop(); + return node; + } + + protected internal override Expression VisitBlock(BlockExpression node) { + if (node.Variables.Count == 0) { + Visit(node.Expressions); + return node; + } + _scopes.Push(_tree.Scopes[node] = new CompilerScope(node)); + Visit(MergeScopes(node)); + _scopes.Pop(); + return node; + } + + // If the immediate child is another scope, merge it into this one + // This is an optimization to save environment allocations and + // array accesses. + // + // TODO: is this an important optimization? Seems useful to merge + // lambdas scopes with immediately nested blocks, but it's rare that + // blocks would have only one element + private ReadOnlyCollection MergeScopes(Expression node) { + ReadOnlyCollection body; + var lambda = node as LambdaExpression; + if (lambda != null) { + body = new ReadOnlyCollection(new[] { lambda.Body }); + } else { + body = ((BlockExpression)node).Expressions; + } + + var currentScope = _scopes.Peek(); + while (IsMergeable(body, node)) { + var block = (BlockExpression)body[0]; + + if (block.Variables.Count > 0) { + if (currentScope.MergedScopes == null) { + currentScope.MergedScopes = new Set(); + } + currentScope.MergedScopes.Add(block); + foreach (var v in block.Variables) { + currentScope.Definitions.Add(v, VariableStorageKind.Local); + } + } + node = block; + body = block.Expressions; + } + return body; + } + + //A block body is mergeable if the body only contains one single block node containing variables, + //and the child block has the same type as the parent block. + private static bool IsMergeable(ReadOnlyCollection body, Expression parent) { + return body.Count == 1 && + body[0].NodeType == ExpressionType.Block && + //TODO: the following check is only needed because we allow void blocks. + //It can be removed when that feature goes away. + body[0].Type == parent.Type; + } + + + protected internal override Expression VisitParameter(ParameterExpression node) { + var scope = _scopes.Peek(); + + if (scope.Definitions.ContainsKey(node)) { + return node; + } + + if (scope.ReferenceCount == null) { + scope.ReferenceCount = new Dictionary(); + } + + Helpers.IncrementCount(node, _scopes.Peek().ReferenceCount); + Reference(node, VariableStorageKind.Local); + return node; + } + + protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) { + foreach (var v in node.Variables) { + // Force hoisting of these variables + Reference(v, VariableStorageKind.Hoisted); + } + return node; + } + + private void Reference(ParameterExpression node, VariableStorageKind storage) { + CompilerScope definition = null; + foreach (CompilerScope scope in _scopes) { + if (scope.Definitions.ContainsKey(node)) { + definition = scope; + break; + } + scope.NeedsClosure = true; + if (scope.Node.NodeType == ExpressionType.Lambda) { + storage = VariableStorageKind.Hoisted; + } + } + if (definition == null) { + throw Error.UndefinedVariable(node.Name, node.Type, CurrentLambdaName); + } + if (storage == VariableStorageKind.Hoisted) { + if (node.IsByRef) { + throw Error.CannotCloseOverByRef(node.Name, CurrentLambdaName); + } + definition.Definitions[node] = VariableStorageKind.Hoisted; + } + } + + private CompilerScope LambdaScope { + get { + foreach (var scope in _scopes) { + if (scope.Node.NodeType == ExpressionType.Lambda) { + return scope; + } + } + throw Assert.Unreachable; + } + } + + private string CurrentLambdaName { + get { + return ((LambdaExpression)LambdaScope.Node).Name; + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/DebugKey.snk b/ndp/fx/src/core/microsoft/scripting/DebugKey.snk new file mode 100644 index 0000000000..c5476c9d54 Binary files /dev/null and b/ndp/fx/src/core/microsoft/scripting/DebugKey.snk differ diff --git a/ndp/fx/src/core/microsoft/scripting/GlobalSuppressions.cs b/ndp/fx/src/core/microsoft/scripting/GlobalSuppressions.cs new file mode 100644 index 0000000000..90b22ba3d8 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/GlobalSuppressions.cs @@ -0,0 +1,14 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. +// +// To add a suppression to this file, right-click the message in the +// Error List, point to "Suppress Message(s)", and click +// "In Project Suppression File". +// You do not need to add suppressions to this file manually. + +// TODO: probably most of our types in System.Dynamic.Binders will end up in +// System.Dynamic +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Dynamic")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Dynamic.Binders.DynamicObject.#System.Dynamic.Binders.IDynamicObject.GetMetaObject(System.Linq.Expressions.Expression)")] diff --git a/ndp/fx/src/core/microsoft/scripting/Microsoft.Scripting.Core.csproj b/ndp/fx/src/core/microsoft/scripting/Microsoft.Scripting.Core.csproj new file mode 100644 index 0000000000..ed11de3dc2 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Microsoft.Scripting.Core.csproj @@ -0,0 +1,286 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4} + Library + Properties + true + System.Scripting + Microsoft.Scripting.Core + SAK + SAK + SAK + SAK + ..\..\..\..\..\..\Merlin\Main\Bin\Release\Microsoft.Scripting.Core.XML + 1591 + 2.0 + + + pdbonly + true + ..\..\..\..\..\..\Merlin\Main\Bin\FxCop\ + TRACE;SIGNED;MICROSOFT_SCRIPTING_CORE + prompt + 4 + true + true + ..\..\..\..\..\..\Merlin\Main\MSSharedLibKey.snk + true + true + -Microsoft.Usage#CA2209;+!Microsoft.Design#CA1012;-!Microsoft.Design#CA2210;+!Microsoft.Design#CA1040;+!Microsoft.Design#CA1005;+!Microsoft.Design#CA1020;-!Microsoft.Design#CA1021;+!Microsoft.Design#CA1010;+!Microsoft.Design#CA1011;+!Microsoft.Design#CA1009;+!Microsoft.Design#CA1050;+!Microsoft.Design#CA1026;+!Microsoft.Design#CA1019;+!Microsoft.Design#CA1031;+!Microsoft.Design#CA1047;+!Microsoft.Design#CA1000;+!Microsoft.Design#CA1048;+!Microsoft.Design#CA1051;+!Microsoft.Design#CA1002;+!Microsoft.Design#CA1061;+!Microsoft.Design#CA1006;+!Microsoft.Design#CA1046;+!Microsoft.Design#CA1045;+!Microsoft.Design#CA1065;+!Microsoft.Design#CA1038;+!Microsoft.Design#CA1008;+!Microsoft.Design#CA1028;+!Microsoft.Design#CA1064;-!Microsoft.Design#CA1004;+!Microsoft.Design#CA1035;+!Microsoft.Design#CA1063;+!Microsoft.Design#CA1032;+!Microsoft.Design#CA1023;+!Microsoft.Design#CA1033;+!Microsoft.Design#CA1039;+!Microsoft.Design#CA1016;+!Microsoft.Design#CA1014;+!Microsoft.Design#CA1017;+!Microsoft.Design#CA1018;+!Microsoft.Design#CA1027;+!Microsoft.Design#CA1059;+!Microsoft.Design#CA1060;+!Microsoft.Design#CA1034;+!Microsoft.Design#CA1013;+!Microsoft.Design#CA1036;+!Microsoft.Design#CA1044;+!Microsoft.Design#CA1041;+!Microsoft.Design#CA1025;+!Microsoft.Design#CA1052;+!Microsoft.Design#CA1053;+!Microsoft.Design#CA1057;+!Microsoft.Design#CA1058;+!Microsoft.Design#CA1001;+!Microsoft.Design#CA1049;+!Microsoft.Design#CA1054;+!Microsoft.Design#CA1056;+!Microsoft.Design#CA1055;+!Microsoft.Design#CA1030;+!Microsoft.Design#CA1003;+!Microsoft.Design#CA1007;+!Microsoft.Design#CA1043;+!Microsoft.Design#CA1024;+!Microsoft.Globalization#CA1301;+!Microsoft.Globalization#CA1302;+!Microsoft.Globalization#CA1308;+!Microsoft.Globalization#CA1306;+!Microsoft.Globalization#CA1304;+!Microsoft.Globalization#CA1305;+!Microsoft.Globalization#CA2101;+!Microsoft.Globalization#CA1300;+!Microsoft.Globalization#CA1307;+!Microsoft.Globalization#CA1309;+!Microsoft.Interoperability#CA1403;+!Microsoft.Interoperability#CA1406;+!Microsoft.Interoperability#CA1413;+!Microsoft.Interoperability#CA1402;+!Microsoft.Interoperability#CA1407;+!Microsoft.Interoperability#CA1404;+!Microsoft.Interoperability#CA1410;+!Microsoft.Interoperability#CA1411;+!Microsoft.Interoperability#CA1405;+!Microsoft.Interoperability#CA1409;+!Microsoft.Interoperability#CA1415;+!Microsoft.Interoperability#CA1408;+!Microsoft.Interoperability#CA1414;+!Microsoft.Interoperability#CA1412;+!Microsoft.Interoperability#CA1400;+!Microsoft.Interoperability#CA1401;+!Microsoft.Maintainability#CA1506;+!Microsoft.Maintainability#CA1502;+!Microsoft.Maintainability#CA1501;+!Microsoft.Maintainability#CA1505;+!Microsoft.Maintainability#CA1504;+!Microsoft.Maintainability#CA1500;+!Microsoft.Mobility#CA1600;+!Microsoft.Mobility#CA1601;-!Microsoft.Naming#CA1702;+!Microsoft.Naming#CA1700;+!Microsoft.Naming#CA1712;+!Microsoft.Naming#CA1713;+!Microsoft.Naming#CA1714;+!Microsoft.Naming#CA1709;-!Microsoft.Naming#CA1704;+!Microsoft.Naming#CA1708;+!Microsoft.Naming#CA1715;-!Microsoft.Naming#CA1710;-!Microsoft.Naming#CA1720;+!Microsoft.Naming#CA1707;+!Microsoft.Naming#CA1722;-!Microsoft.Naming#CA1711;+!Microsoft.Naming#CA1716;+!Microsoft.Naming#CA1717;+!Microsoft.Naming#CA1725;+!Microsoft.Naming#CA1719;+!Microsoft.Naming#CA1721;+!Microsoft.Naming#CA1701;+!Microsoft.Naming#CA1703;+!Microsoft.Naming#CA1724;-!Microsoft.Naming#CA1726;+!Microsoft.Performance#CA1809;+!Microsoft.Performance#CA1811;+!Microsoft.Performance#CA1812;+!Microsoft.Performance#CA1813;+!Microsoft.Performance#CA1823;+!Microsoft.Performance#CA1800;+!Microsoft.Performance#CA1805;+!Microsoft.Performance#CA1810;+!Microsoft.Performance#CA1824;+!Microsoft.Performance#CA1822;+!Microsoft.Performance#CA1815;+!Microsoft.Performance#CA1814;+!Microsoft.Performance#CA1819;+!Microsoft.Performance#CA1821;+!Microsoft.Performance#CA1804;+!Microsoft.Performance#CA1820;+!Microsoft.Performance#CA1802;+!Microsoft.Portability#CA1901;+!Microsoft.Portability#CA1900;+!Microsoft.Reliability#CA2001;+!Microsoft.Reliability#CA2002;+!Microsoft.Reliability#CA2003;+!Microsoft.Reliability#CA2004;+!Microsoft.Reliability#CA2006;+!Microsoft.Security#CA2116;+!Microsoft.Security#CA2117;+!Microsoft.Security#CA2105;+!Microsoft.Security#CA2115;+!Microsoft.Security#CA2102;+!Microsoft.Security#CA2104;+!Microsoft.Security#CA2122;+!Microsoft.Security#CA2114;+!Microsoft.Security#CA2123;+!Microsoft.Security#CA2111;+!Microsoft.Security#CA2108;+!Microsoft.Security#CA2107;+!Microsoft.Security#CA2103;+!Microsoft.Security#CA2118;+!Microsoft.Security#CA2109;+!Microsoft.Security#CA2119;+!Microsoft.Security#CA2106;+!Microsoft.Security#CA2112;+!Microsoft.Security#CA2120;+!Microsoft.Security#CA2121;+!Microsoft.Security#CA2126;+!Microsoft.Security#CA2124;+!Microsoft.Security#CA2127;+!Microsoft.Security#CA2128;+!Microsoft.Security#CA2129;+!Microsoft.Usage#CA2243;+!Microsoft.Usage#CA2236;+!Microsoft.Usage#CA1816;+!Microsoft.Usage#CA2227;+!Microsoft.Usage#CA2213;+!Microsoft.Usage#CA2216;+!Microsoft.Usage#CA2214;+!Microsoft.Usage#CA2222;+!Microsoft.Usage#CA1806;+!Microsoft.Usage#CA2217;+!Microsoft.Usage#CA2212;+!Microsoft.Usage#CA2219;+!Microsoft.Usage#CA2201;+!Microsoft.Usage#CA2228;+!Microsoft.Usage#CA2221;+!Microsoft.Usage#CA2220;+!Microsoft.Usage#CA2240;+!Microsoft.Usage#CA2229;+!Microsoft.Usage#CA2238;+!Microsoft.Usage#CA2207;+!Microsoft.Usage#CA2208;+!Microsoft.Usage#CA2235;+!Microsoft.Usage#CA2237;+!Microsoft.Usage#CA2232;+!Microsoft.Usage#CA2223;+!Microsoft.Usage#CA2211;+!Microsoft.Usage#CA2233;+!Microsoft.Usage#CA2225;+!Microsoft.Usage#CA2226;+!Microsoft.Usage#CA2231;+!Microsoft.Usage#CA2224;+!Microsoft.Usage#CA2218;+!Microsoft.Usage#CA2234;+!Microsoft.Usage#CA2239;+!Microsoft.Usage#CA2200;+!Microsoft.Usage#CA1801;+!Microsoft.Usage#CA2242;+!Microsoft.Usage#CA2205;+!Microsoft.Usage#CA2230 + + + pdbonly + true + ..\..\..\..\..\..\Merlin\Main\Bin\Debug\ + TRACE;DEBUG;SIGNED;MICROSOFT_SCRIPTING_CORE + prompt + 4 + true + true + ..\..\..\..\..\..\Merlin\Main\MSSharedLibKey.snk + true + 1591 + true + + + true + full + false + ..\..\..\..\..\..\Merlin\Main\Bin\Debug\ + ..\..\..\..\..\..\Merlin\Main\Bin\Debug\Microsoft.Scripting.Core.xml + DEBUG;TRACE;SIGNED;MICROSOFT_SCRIPTING_CORE + prompt + 4 + true + false + true + ..\..\..\..\..\..\Merlin\Main\MSSharedLibKey.snk + true + + + pdbonly + true + ..\..\..\..\..\..\Merlin\Main\Bin\Release\ + ..\..\..\..\..\..\Merlin\Main\Bin\Release\Microsoft.Scripting.Core.xml + TRACE;SIGNED;MICROSOFT_SCRIPTING_CORE + prompt + 4 + true + false + true + ..\..\..\..\..\..\Merlin\Main\MSSharedLibKey.snk + true + 1929379840 + + + true + ..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\ + TRACE;DEBUG;SILVERLIGHT;MICROSOFT_SCRIPTING_CORE + true + full + AnyCPU + false + prompt + true + ..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\Microsoft.Scripting.Core.xml + true + ..\..\..\..\..\..\Merlin\Main\SilverlightKey.snk + true + 1591,618 + true + ..\..\..\..\..\..\Merlin\Main\Utilities\Silverlight\x86ret\ + + + ..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\ + TRACE;SILVERLIGHT;MICROSOFT_SCRIPTING_CORE + ..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\Microsoft.Scripting.Core.xml + true + 1591,618 + true + pdbonly + AnyCPU + prompt + true + ..\..\..\..\..\..\Merlin\Main\SilverlightKey.snk + true + true + ..\..\..\..\..\..\Merlin\Main\Utilities\Silverlight\x86ret\ + + + + + + False + $(SilverlightSdkPath)\mscorlib.dll + + + False + $(SilverlightSdkPath)\System.dll + + + + + + + Properties\SilverlightVersion.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Properties\SilverlightKey.snk + + + + + {8B0F1074-750E-4D64-BF23-A1E0F54261E5} + Microsoft.Scripting.ExtensionAttribute + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ndp/fx/src/core/microsoft/scripting/Microsoft.Scripting.Core.csproj.vspscc b/ndp/fx/src/core/microsoft/scripting/Microsoft.Scripting.Core.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Microsoft.Scripting.Core.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/ndp/fx/src/core/microsoft/scripting/Microsoft.Scripting.ExtensionAttribute.csproj b/ndp/fx/src/core/microsoft/scripting/Microsoft.Scripting.ExtensionAttribute.csproj new file mode 100644 index 0000000000..01c06ec20a --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Microsoft.Scripting.ExtensionAttribute.csproj @@ -0,0 +1,141 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {8B0F1074-750E-4D64-BF23-A1E0F54261E5} + Library + Properties + Microsoft.Scripting.ExtensionAttribute + Microsoft.Scripting.ExtensionAttribute + SAK + SAK + SAK + SAK + 2.0 + + + pdbonly + true + ..\..\..\..\..\..\Merlin\Main\Bin\FxCop\ + TRACE;SIGNED + prompt + 4 + true + true + ..\..\..\..\..\..\Merlin\Main\MSSharedLibKey.snk + true + true + -Microsoft.Usage#CA2209;+!Microsoft.Design#CA1012;-!Microsoft.Design#CA2210;+!Microsoft.Design#CA1040;+!Microsoft.Design#CA1005;+!Microsoft.Design#CA1020;-!Microsoft.Design#CA1021;+!Microsoft.Design#CA1010;+!Microsoft.Design#CA1011;+!Microsoft.Design#CA1009;+!Microsoft.Design#CA1050;+!Microsoft.Design#CA1026;+!Microsoft.Design#CA1019;+!Microsoft.Design#CA1031;+!Microsoft.Design#CA1047;+!Microsoft.Design#CA1000;+!Microsoft.Design#CA1048;+!Microsoft.Design#CA1051;+!Microsoft.Design#CA1002;+!Microsoft.Design#CA1061;+!Microsoft.Design#CA1006;+!Microsoft.Design#CA1046;+!Microsoft.Design#CA1045;+!Microsoft.Design#CA1065;+!Microsoft.Design#CA1038;+!Microsoft.Design#CA1008;+!Microsoft.Design#CA1028;+!Microsoft.Design#CA1064;-!Microsoft.Design#CA1004;+!Microsoft.Design#CA1035;+!Microsoft.Design#CA1063;+!Microsoft.Design#CA1032;+!Microsoft.Design#CA1023;+!Microsoft.Design#CA1033;+!Microsoft.Design#CA1039;+!Microsoft.Design#CA1016;+!Microsoft.Design#CA1014;+!Microsoft.Design#CA1017;+!Microsoft.Design#CA1018;+!Microsoft.Design#CA1027;+!Microsoft.Design#CA1059;+!Microsoft.Design#CA1060;+!Microsoft.Design#CA1034;+!Microsoft.Design#CA1013;+!Microsoft.Design#CA1036;+!Microsoft.Design#CA1044;+!Microsoft.Design#CA1041;+!Microsoft.Design#CA1025;+!Microsoft.Design#CA1052;+!Microsoft.Design#CA1053;+!Microsoft.Design#CA1057;+!Microsoft.Design#CA1058;+!Microsoft.Design#CA1001;+!Microsoft.Design#CA1049;+!Microsoft.Design#CA1054;+!Microsoft.Design#CA1056;+!Microsoft.Design#CA1055;+!Microsoft.Design#CA1030;+!Microsoft.Design#CA1003;+!Microsoft.Design#CA1007;+!Microsoft.Design#CA1043;+!Microsoft.Design#CA1024;+!Microsoft.Globalization#CA1301;+!Microsoft.Globalization#CA1302;+!Microsoft.Globalization#CA1308;+!Microsoft.Globalization#CA1306;+!Microsoft.Globalization#CA1304;+!Microsoft.Globalization#CA1305;+!Microsoft.Globalization#CA2101;+!Microsoft.Globalization#CA1300;+!Microsoft.Globalization#CA1307;+!Microsoft.Globalization#CA1309;+!Microsoft.Interoperability#CA1403;+!Microsoft.Interoperability#CA1406;+!Microsoft.Interoperability#CA1413;+!Microsoft.Interoperability#CA1402;+!Microsoft.Interoperability#CA1407;+!Microsoft.Interoperability#CA1404;+!Microsoft.Interoperability#CA1410;+!Microsoft.Interoperability#CA1411;+!Microsoft.Interoperability#CA1405;+!Microsoft.Interoperability#CA1409;+!Microsoft.Interoperability#CA1415;+!Microsoft.Interoperability#CA1408;+!Microsoft.Interoperability#CA1414;+!Microsoft.Interoperability#CA1412;+!Microsoft.Interoperability#CA1400;+!Microsoft.Interoperability#CA1401;+!Microsoft.Maintainability#CA1506;+!Microsoft.Maintainability#CA1502;+!Microsoft.Maintainability#CA1501;+!Microsoft.Maintainability#CA1505;+!Microsoft.Maintainability#CA1504;+!Microsoft.Maintainability#CA1500;+!Microsoft.Mobility#CA1600;+!Microsoft.Mobility#CA1601;-!Microsoft.Naming#CA1702;+!Microsoft.Naming#CA1700;+!Microsoft.Naming#CA1712;+!Microsoft.Naming#CA1713;+!Microsoft.Naming#CA1714;+!Microsoft.Naming#CA1709;-!Microsoft.Naming#CA1704;+!Microsoft.Naming#CA1708;+!Microsoft.Naming#CA1715;-!Microsoft.Naming#CA1710;-!Microsoft.Naming#CA1720;+!Microsoft.Naming#CA1707;+!Microsoft.Naming#CA1722;-!Microsoft.Naming#CA1711;+!Microsoft.Naming#CA1716;+!Microsoft.Naming#CA1717;+!Microsoft.Naming#CA1725;+!Microsoft.Naming#CA1719;+!Microsoft.Naming#CA1721;+!Microsoft.Naming#CA1701;+!Microsoft.Naming#CA1703;+!Microsoft.Naming#CA1724;-!Microsoft.Naming#CA1726;+!Microsoft.Performance#CA1809;+!Microsoft.Performance#CA1811;+!Microsoft.Performance#CA1812;+!Microsoft.Performance#CA1813;+!Microsoft.Performance#CA1823;+!Microsoft.Performance#CA1800;+!Microsoft.Performance#CA1805;+!Microsoft.Performance#CA1810;+!Microsoft.Performance#CA1824;+!Microsoft.Performance#CA1822;+!Microsoft.Performance#CA1815;+!Microsoft.Performance#CA1814;+!Microsoft.Performance#CA1819;+!Microsoft.Performance#CA1821;+!Microsoft.Performance#CA1804;+!Microsoft.Performance#CA1820;+!Microsoft.Performance#CA1802;+!Microsoft.Portability#CA1901;+!Microsoft.Portability#CA1900;+!Microsoft.Reliability#CA2001;+!Microsoft.Reliability#CA2002;+!Microsoft.Reliability#CA2003;+!Microsoft.Reliability#CA2004;+!Microsoft.Reliability#CA2006;+!Microsoft.Security#CA2116;+!Microsoft.Security#CA2117;+!Microsoft.Security#CA2105;+!Microsoft.Security#CA2115;+!Microsoft.Security#CA2102;+!Microsoft.Security#CA2104;+!Microsoft.Security#CA2122;+!Microsoft.Security#CA2114;+!Microsoft.Security#CA2123;+!Microsoft.Security#CA2111;+!Microsoft.Security#CA2108;+!Microsoft.Security#CA2107;+!Microsoft.Security#CA2103;+!Microsoft.Security#CA2118;+!Microsoft.Security#CA2109;+!Microsoft.Security#CA2119;+!Microsoft.Security#CA2106;+!Microsoft.Security#CA2112;+!Microsoft.Security#CA2120;+!Microsoft.Security#CA2121;+!Microsoft.Security#CA2126;+!Microsoft.Security#CA2124;+!Microsoft.Security#CA2127;+!Microsoft.Security#CA2128;+!Microsoft.Security#CA2129;+!Microsoft.Usage#CA2243;+!Microsoft.Usage#CA2236;+!Microsoft.Usage#CA1816;+!Microsoft.Usage#CA2227;+!Microsoft.Usage#CA2213;+!Microsoft.Usage#CA2216;+!Microsoft.Usage#CA2214;+!Microsoft.Usage#CA2222;+!Microsoft.Usage#CA1806;+!Microsoft.Usage#CA2217;+!Microsoft.Usage#CA2212;+!Microsoft.Usage#CA2219;+!Microsoft.Usage#CA2201;+!Microsoft.Usage#CA2228;+!Microsoft.Usage#CA2221;+!Microsoft.Usage#CA2220;+!Microsoft.Usage#CA2240;+!Microsoft.Usage#CA2229;+!Microsoft.Usage#CA2238;+!Microsoft.Usage#CA2207;+!Microsoft.Usage#CA2208;+!Microsoft.Usage#CA2235;+!Microsoft.Usage#CA2237;+!Microsoft.Usage#CA2232;+!Microsoft.Usage#CA2223;+!Microsoft.Usage#CA2211;+!Microsoft.Usage#CA2233;+!Microsoft.Usage#CA2225;+!Microsoft.Usage#CA2226;+!Microsoft.Usage#CA2231;+!Microsoft.Usage#CA2224;+!Microsoft.Usage#CA2218;+!Microsoft.Usage#CA2234;+!Microsoft.Usage#CA2239;+!Microsoft.Usage#CA2200;+!Microsoft.Usage#CA1801;+!Microsoft.Usage#CA2242;+!Microsoft.Usage#CA2205;+!Microsoft.Usage#CA2230 + + + pdbonly + true + ..\..\..\..\..\..\Merlin\Main\Bin\Debug\ + TRACE;DEBUG;SIGNED + prompt + 4 + true + true + ..\..\..\..\..\..\Merlin\Main\MSSharedLibKey.snk + true + 1591 + true + + + true + full + false + ..\..\..\..\..\..\Merlin\Main\Bin\Debug\ + DEBUG;TRACE;SIGNED + prompt + 4 + true + false + true + ..\..\..\..\..\..\Merlin\Main\MSSharedLibKey.snk + true + + + pdbonly + true + ..\..\..\..\..\..\Merlin\Main\Bin\Release\ + TRACE;SIGNED + prompt + 4 + true + false + true + ..\..\..\..\..\..\Merlin\Main\MSSharedLibKey.snk + true + 1929379840 + + + true + ..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Debug\ + TRACE;DEBUG;SILVERLIGHT + true + full + AnyCPU + false + prompt + true + true + ..\..\..\..\..\..\Merlin\Main\SilverlightKey.snk + true + 1591,618 + true + ..\..\..\..\..\..\Merlin\Main\Utilities\Silverlight\x86ret\ + + + ..\..\..\..\..\..\Merlin\Main\Bin\Silverlight Release\ + TRACE;SILVERLIGHT + true + 1591,618 + true + pdbonly + AnyCPU + prompt + true + ..\..\..\..\..\..\Merlin\Main\SilverlightKey.snk + true + true + ..\..\..\..\..\..\Merlin\Main\Utilities\Silverlight\x86ret\ + + + + + False + $(SilverlightSdkPath)\mscorlib.dll + + + False + $(SilverlightSdkPath)\System.dll + + + + + Properties\SilverlightVersion.cs + + + + + + + Properties\SilverlightKey.snk + + + + + + + + + + + + + + diff --git a/ndp/fx/src/core/microsoft/scripting/Microsoft.Scripting.ExtensionAttribute.csproj.vspscc b/ndp/fx/src/core/microsoft/scripting/Microsoft.Scripting.ExtensionAttribute.csproj.vspscc new file mode 100644 index 0000000000..b6d32892fd --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Microsoft.Scripting.ExtensionAttribute.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/ndp/fx/src/core/microsoft/scripting/Null.cs b/ndp/fx/src/core/microsoft/scripting/Null.cs new file mode 100644 index 0000000000..f0624ddd73 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Null.cs @@ -0,0 +1,31 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace System.Dynamic { + /// + /// Represents a type of a null value. + /// + public sealed class Null { + /// + /// The type of null value. + /// + public static readonly Type Type = typeof(Null); + + /// + /// Private constructor is never called since 'null' is the only valid instance. + /// + private Null() { } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Properties/AssemblyInfo.cs b/ndp/fx/src/core/microsoft/scripting/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..29e6d4ea36 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Properties/AssemblyInfo.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.Scripting")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Microsoft.Scripting")] +[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +[assembly: CLSCompliant(true)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("cabb8088-1370-43ca-ad47-1c32d3f7bd10")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: SecurityTransparent] + +[assembly: System.Resources.NeutralResourcesLanguage("en-US")] + +[assembly: InternalsVisibleTo("TestInternalDLR, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] + +#if !SILVERLIGHT +[assembly: AssemblyVersion("1.0.0.5000")] +[assembly: AssemblyFileVersion("1.0.0.00")] +[assembly: AssemblyInformationalVersion("1.0")] +[assembly: AllowPartiallyTrustedCallers] +#endif diff --git a/ndp/fx/src/core/microsoft/scripting/Properties/ExtensionAssemblyInfo.cs b/ndp/fx/src/core/microsoft/scripting/Properties/ExtensionAssemblyInfo.cs new file mode 100644 index 0000000000..d95f377c2c --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Properties/ExtensionAssemblyInfo.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.Scripting.ExtensionAttribute")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Microsoft.Scripting.ExtensionAttribute")] +[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +[assembly: CLSCompliant(true)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("b828a36d-f568-48a7-9bdd-412b0a1bfa32")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: SecurityTransparent] + +[assembly: System.Resources.NeutralResourcesLanguage("en-US")] + +#if !SILVERLIGHT +[assembly: AssemblyVersion("1.0.0.5000")] +[assembly: AssemblyFileVersion("1.0.0.00")] +[assembly: AssemblyInformationalVersion("1.0")] +[assembly: AllowPartiallyTrustedCallers] +#endif diff --git a/ndp/fx/src/core/microsoft/scripting/Stubs.cs b/ndp/fx/src/core/microsoft/scripting/Stubs.cs new file mode 100644 index 0000000000..ff8a1e06b1 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Stubs.cs @@ -0,0 +1,137 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Dynamic.Utils; + +#if SILVERLIGHT // Stubs + +namespace System { + + public class ApplicationException : Exception { + private const int error = unchecked((int)0x80131600); + // Methods + public ApplicationException() + : base("Application Exception") { + HResult = error; + } + + public ApplicationException(string message) + : base(message) { + HResult = error; + } + + public ApplicationException(string message, Exception innerException) + : base(message, innerException) { + HResult = error; + } + } + + namespace Runtime.InteropServices { + public sealed class DefaultParameterValueAttribute : Attribute { + public DefaultParameterValueAttribute(object value) { } + } + } + + // We reference these namespaces via "using" + // We don't actually use them because the code is #if !SILVERLIGHT + // Rather than fix the usings all over the place, just define these here + namespace Runtime.Remoting { class Dummy {} } + namespace Security.Policy { class Dummy {} } + namespace Xml.XPath { class Dummy {} } + + namespace Reflection { + public enum PortableExecutableKinds { + ILOnly = 0 + } + + public enum ImageFileMachine { + I386 = 1 + } + } + + namespace ComponentModel { + + public class WarningException : SystemException { + public WarningException(string message) : base(message) { } + } + } + + public class SerializableAttribute : Attribute { + } + + public class NonSerializedAttribute : Attribute { + } + + namespace Runtime.Serialization { + public interface ISerializable { + } + } + + public enum ConsoleColor { + Black = 0, + DarkBlue = 1, + DarkGreen = 2, + DarkCyan = 3, + DarkRed = 4, + DarkMagenta = 5, + DarkYellow = 6, + Gray = 7, + DarkGray = 8, + Blue = 9, + Green = 10, + Cyan = 11, + Red = 12, + Magenta = 13, + Yellow = 14, + White = 15, + } + +} + +#endif + +#if !SPECSHARP + +namespace Microsoft.Contracts { + [Conditional("SPECSHARP"), AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = false, Inherited = true)] + internal sealed class StateIndependentAttribute : Attribute { + } + +#if MICROSOFT_SCRIPTING_CORE + [Conditional("SPECSHARP"), AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = false, Inherited = true)] + internal sealed class PureAttribute : Attribute { + } +#endif + + [Conditional("SPECSHARP"), AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = false, Inherited = true)] + internal sealed class ConfinedAttribute : Attribute { + } + + [Conditional("SPECSHARP"), AttributeUsage(AttributeTargets.Field)] + internal sealed class StrictReadonlyAttribute : Attribute { + } + + internal static class NonNullType { + [DebuggerStepThrough] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")] + public static void AssertInitialized(T[] array) where T : class { + Assert.NotNullItems(array); + } + } +} + +#endif \ No newline at end of file diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/Action.cs b/ndp/fx/src/core/microsoft/scripting/Utils/Action.cs new file mode 100644 index 0000000000..58801ddcc5 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/Action.cs @@ -0,0 +1,392 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace System { + /// + /// Encapsulates a method that takes no parameters and does not return a value. + /// + public delegate void Action(); + + // public delegate void Action(T obj); -- Already defined in mscorlib + + #region Generated Action Types + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_action_types from: generate_dynsites.py + + + /// + /// Encapsulates a method that takes two parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + public delegate void Action(T1 arg1, T2 arg2); + + /// + /// Encapsulates a method that takes three parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3); + + /// + /// Encapsulates a method that takes four parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + /// + /// Encapsulates a method that takes five parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); + + /// + /// Encapsulates a method that takes six parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); + + /// + /// Encapsulates a method that takes seven parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); + + /// + /// Encapsulates a method that takes eight parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); + + /// + /// Encapsulates a method that takes nine parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9); + + /// + /// Encapsulates a method that takes ten parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10); + + /// + /// Encapsulates a method that takes eleven parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the eleventh parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The eleventh parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11); + + /// + /// Encapsulates a method that takes twelve parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the eleventh parameter of the method that this delegate encapsulates. + /// The type of the twelfth parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The eleventh parameter of the method that this delegate encapsulates. + /// The twelfth parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12); + + /// + /// Encapsulates a method that takes thirteen parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the eleventh parameter of the method that this delegate encapsulates. + /// The type of the twelfth parameter of the method that this delegate encapsulates. + /// The type of the thirteenth parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The eleventh parameter of the method that this delegate encapsulates. + /// The twelfth parameter of the method that this delegate encapsulates. + /// The thirteenth parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13); + + /// + /// Encapsulates a method that takes fourteen parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the eleventh parameter of the method that this delegate encapsulates. + /// The type of the twelfth parameter of the method that this delegate encapsulates. + /// The type of the thirteenth parameter of the method that this delegate encapsulates. + /// The type of the fourteenth parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The eleventh parameter of the method that this delegate encapsulates. + /// The twelfth parameter of the method that this delegate encapsulates. + /// The thirteenth parameter of the method that this delegate encapsulates. + /// The fourteenth parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14); + + /// + /// Encapsulates a method that takes fifteen parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the eleventh parameter of the method that this delegate encapsulates. + /// The type of the twelfth parameter of the method that this delegate encapsulates. + /// The type of the thirteenth parameter of the method that this delegate encapsulates. + /// The type of the fourteenth parameter of the method that this delegate encapsulates. + /// The type of the fifteenth parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The eleventh parameter of the method that this delegate encapsulates. + /// The twelfth parameter of the method that this delegate encapsulates. + /// The thirteenth parameter of the method that this delegate encapsulates. + /// The fourteenth parameter of the method that this delegate encapsulates. + /// The fifteenth parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15); + + /// + /// Encapsulates a method that takes sixteen parameters and does not return a value. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the eleventh parameter of the method that this delegate encapsulates. + /// The type of the twelfth parameter of the method that this delegate encapsulates. + /// The type of the thirteenth parameter of the method that this delegate encapsulates. + /// The type of the fourteenth parameter of the method that this delegate encapsulates. + /// The type of the fifteenth parameter of the method that this delegate encapsulates. + /// The type of the sixteenth parameter of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The eleventh parameter of the method that this delegate encapsulates. + /// The twelfth parameter of the method that this delegate encapsulates. + /// The thirteenth parameter of the method that this delegate encapsulates. + /// The fourteenth parameter of the method that this delegate encapsulates. + /// The fifteenth parameter of the method that this delegate encapsulates. + /// The sixteenth parameter of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16); + + // *** END GENERATED CODE *** + + #endregion +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/Assert.cs b/ndp/fx/src/core/microsoft/scripting/Utils/Assert.cs new file mode 100644 index 0000000000..8825533681 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/Assert.cs @@ -0,0 +1,78 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#define DEBUG + +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Dynamic.Utils { + + // TODO: do we really need this class? + internal static class Assert { + + internal static Exception Unreachable { + get { + Debug.Assert(false, "Unreachable"); + return new InvalidOperationException("Code supposed to be unreachable"); + } + } + + [Conditional("DEBUG")] + internal static void NotNull(object var) { + Debug.Assert(var != null); + } + + [Conditional("DEBUG")] + internal static void NotNull(object var1, object var2) { + Debug.Assert(var1 != null && var2 != null); + } + + [Conditional("DEBUG")] + internal static void NotNull(object var1, object var2, object var3) { + Debug.Assert(var1 != null && var2 != null && var3 != null); + } + + [Conditional("DEBUG")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1025:ReplaceRepetitiveArgumentsWithParamsArray")] + internal static void NotNull(object var1, object var2, object var3, object var4) { + Debug.Assert(var1 != null && var2 != null && var3 != null && var4 != null); + } + + [Conditional("DEBUG")] + internal static void NotEmpty(string str) { + Debug.Assert(!String.IsNullOrEmpty(str)); + } + + [Conditional("DEBUG")] + internal static void NotEmpty(ICollection array) { + Debug.Assert(array != null && array.Count > 0); + } + + [Conditional("DEBUG")] + internal static void NotNullItems(IEnumerable items) where T : class { + Debug.Assert(items != null); + foreach (object item in items) { + Debug.Assert(item != null); + } + } + + [Conditional("DEBUG")] + internal static void IsTrue(Func predicate) { + ContractUtils.RequiresNotNull(predicate, "predicate"); + Debug.Assert(predicate()); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/CacheDict.cs b/ndp/fx/src/core/microsoft/scripting/Utils/CacheDict.cs new file mode 100644 index 0000000000..43d91e2395 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/CacheDict.cs @@ -0,0 +1,93 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; + +namespace System.Dynamic.Utils { + /// + /// Provides a dictionary-like object used for caches which holds onto a maximum + /// number of elements specified at construction time. + /// + /// This class is not thread safe. + /// + internal class CacheDict { + private readonly Dictionary _dict = new Dictionary(); + private readonly LinkedList _list = new LinkedList(); + private readonly int _maxSize; + + internal CacheDict(int maxSize) { + _maxSize = maxSize; + } + + internal bool TryGetValue(TKey key, out TValue value) { + KeyInfo storedValue; + if (_dict.TryGetValue(key, out storedValue)) { + LinkedListNode node = storedValue.List; + if (node.Previous != null) { + // move us to the head of the list... + _list.Remove(node); + _list.AddFirst(node); + } + + value = storedValue.Value; + return true; + } + + value = default(TValue); + return false; + } + + internal void Add(TKey key, TValue value) { + if (_list.Count == _maxSize) { + // we've reached capacity, remove the last used element... + LinkedListNode node = _list.Last; + _list.RemoveLast(); + bool res = _dict.Remove(node.Value); + Debug.Assert(res); + } + + // add the new entry to the head of the list and into the dictionary + LinkedListNode listNode = new LinkedListNode(key); + _list.AddFirst(listNode); + _dict[key] = new CacheDict.KeyInfo(value, listNode); + } + + internal TValue this[TKey key] { + get { + TValue res; + if (TryGetValue(key, out res)) { + return res; + } + throw new KeyNotFoundException(); + } + set { + Add(key, value); + } + } + + private struct KeyInfo { + internal readonly TValue Value; + internal readonly LinkedListNode List; + + internal KeyInfo(TValue value, LinkedListNode list) { + Value = value; + List = list; + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/CollectionExtensions.cs b/ndp/fx/src/core/microsoft/scripting/Utils/CollectionExtensions.cs new file mode 100644 index 0000000000..f16a3b5024 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/CollectionExtensions.cs @@ -0,0 +1,228 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; + +namespace System.Dynamic.Utils { + internal static class CollectionExtensions { + /// + /// Wraps the provided enumerable into a ReadOnlyCollection{T} + /// + /// Copies all of the data into a new array, so the data can't be + /// changed after creation. The exception is if the enumerable is + /// already a ReadOnlyCollection{T}, in which case we just return it. + /// + internal static ReadOnlyCollection ToReadOnly(this IEnumerable enumerable) { + if (enumerable == null) { + return EmptyReadOnlyCollection.Instance; + } + + var roCollection = enumerable as ReadOnlyCollection; + if (roCollection != null) { + return roCollection; + } + + var collection = enumerable as ICollection; + if (collection != null) { + int count = collection.Count; + if (count == 0) { + return EmptyReadOnlyCollection.Instance; + } + + T[] array = new T[count]; + collection.CopyTo(array, 0); + return new ReadOnlyCollection(array); + } + + // ToArray trims the excess space and speeds up access + return new ReadOnlyCollection(new List(enumerable).ToArray()); + } + + // We could probably improve the hashing here + internal static int ListHashCode(this IEnumerable list) { + var cmp = EqualityComparer.Default; + int h = 6551; + foreach (T t in list) { + h ^= (h << 5) ^ cmp.GetHashCode(t); + } + return h; + } + + internal static bool ListEquals(this ICollection first, ICollection second) { + if (first.Count != second.Count) { + return false; + } + var cmp = EqualityComparer.Default; + var f = first.GetEnumerator(); + var s = second.GetEnumerator(); + while (f.MoveNext()) { + s.MoveNext(); + + if (!cmp.Equals(f.Current, s.Current)) { + return false; + } + } + return true; + } + + internal static IEnumerable Select(this IEnumerable enumerable, Func select) { + foreach (T t in enumerable) { + yield return select(t); + } + } + + // Name needs to be different so it doesn't conflict with Enumerable.Select + internal static U[] Map(this ICollection collection, Func select) { + int count = collection.Count; + U[] result = new U[count]; + count = 0; + foreach (T t in collection) { + result[count++] = select(t); + } + return result; + } + + internal static IEnumerable Where(this IEnumerable enumerable, Func where) { + foreach (T t in enumerable) { + if (where(t)) { + yield return t; + } + } + } + + internal static List ToList(this IEnumerable enumerable) { + return new List(enumerable); + } + + internal static T[] ToArray(this IEnumerable enumerable) { + var c = enumerable as ICollection; + if (c != null) { + var result = new T[c.Count]; + c.CopyTo(result, 0); + return result; + } + return new List(enumerable).ToArray(); + } + + internal static bool Any(this IEnumerable source) { + using (IEnumerator e = source.GetEnumerator()) { + return e.MoveNext(); + } + } + + internal static bool Any(this IEnumerable source, Func predicate) { + foreach (T element in source) { + if (predicate(element)) { + return true; + } + } + return false; + } + + internal static bool All(this IEnumerable source, Func predicate) { + foreach (T element in source) { + if (!predicate(element)) { + return false; + } + } + return true; + } + +#if !SILVERLIGHT // ICloneable + internal static T Copy(this T obj) where T : ICloneable { + return (T)obj.Clone(); + } +#else + internal static T[] Copy(this T[] obj) { + return (T[])obj.Clone(); + } +#endif + + internal static T[] RemoveFirst(this T[] array) { + T[] result = new T[array.Length - 1]; + Array.Copy(array, 1, result, 0, result.Length); + return result; + } + + internal static T[] RemoveLast(this T[] array) { + T[] result = new T[array.Length - 1]; + Array.Copy(array, 0, result, 0, result.Length); + return result; + } + + internal static T[] AddFirst(this IList list, T item) { + T[] res = new T[list.Count + 1]; + res[0] = item; + list.CopyTo(res, 1); + return res; + } + + internal static T[] AddLast(this IList list, T item) { + T[] res = new T[list.Count + 1]; + list.CopyTo(res, 0); + res[list.Count] = item; + return res; + } + + internal static T[] RemoveAt(this T[] array, int indexToRemove) { + Debug.Assert(array != null); + Debug.Assert(indexToRemove >= 0 && indexToRemove < array.Length); + + T[] result = new T[array.Length - 1]; + if (indexToRemove > 0) { + Array.Copy(array, 0, result, 0, indexToRemove); + } + int remaining = array.Length - indexToRemove - 1; + if (remaining > 0) { + Array.Copy(array, array.Length - remaining, result, result.Length - remaining, remaining); + } + return result; + } + + internal static T[] RotateRight(this T[] array, int count) { + Debug.Assert(count >= 0 && count <= array.Length); + + T[] result = new T[array.Length]; + // The head of the array is shifted, and the tail will be rotated to the head of the resulting array + int sizeOfShiftedArray = array.Length - count; + Array.Copy(array, 0, result, count, sizeOfShiftedArray); + Array.Copy(array, sizeOfShiftedArray, result, 0, count); + return result; + } + + internal static T First(this IEnumerable source) { + var list = source as IList; + if (list != null) { + return list[0]; + } + using (var e = source.GetEnumerator()) { + if (e.MoveNext()) return e.Current; + } + throw new InvalidOperationException(); + } + } + + + internal static class EmptyReadOnlyCollection { + internal static ReadOnlyCollection Instance = new ReadOnlyCollection(new T[0]); + } + // TODO: Should we use this everywhere for empty arrays? + // my thought is, probably more hassle than its worth + internal static class EmptyArray { + internal static T[] Instance = new T[0]; + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/ContractUtils.cs b/ndp/fx/src/core/microsoft/scripting/Utils/ContractUtils.cs new file mode 100644 index 0000000000..5a9587b323 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/ContractUtils.cs @@ -0,0 +1,150 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq.Expressions; + +namespace System.Dynamic.Utils { + + // TODO: Update with the newest version of the managed contracts stuff + internal static class ContractUtils { + + internal static void Requires(bool precondition) { + if (!precondition) { + throw new ArgumentException(Strings.MethodPreconditionViolated); + } + } + + internal static void Requires(bool precondition, string paramName) { + Assert.NotEmpty(paramName); + + if (!precondition) { + throw new ArgumentException(Strings.InvalidArgumentValue, paramName); + } + } + + internal static void Requires(bool precondition, string paramName, string message) { + Assert.NotEmpty(paramName); + + if (!precondition) { + throw new ArgumentException(message, paramName); + } + } + + internal static void RequiresNotNull(object value, string paramName) { + Assert.NotEmpty(paramName); + + if (value == null) { + throw new ArgumentNullException(paramName); + } + } + + internal static void RequiresNotEmpty(string str, string paramName) { + RequiresNotNull(str, paramName); + if (str.Length == 0) { + throw new ArgumentException(Strings.NonEmptyStringRequired, paramName); + } + } + + internal static void RequiresNotEmpty(ICollection collection, string paramName) { + RequiresNotNull(collection, paramName); + if (collection.Count == 0) { + throw new ArgumentException(Strings.NonEmptyCollectionRequired, paramName); + } + } + + /// + /// Requires the specified index to point inside the array. + /// + /// Array is null. + /// Index is outside the array. + internal static void RequiresArrayIndex(IList array, int index, string indexName) { + Assert.NotEmpty(indexName); + Assert.NotNull(array); + + if (index < 0 || index >= array.Count) throw new ArgumentOutOfRangeException(indexName); + } + + /// + /// Requires the specified index to point inside the array or at the end + /// + /// Array is null. + /// Index is outside the array. + internal static void RequiresArrayInsertIndex(IList array, int index, string indexName) { + Assert.NotEmpty(indexName); + Assert.NotNull(array); + + if (index < 0 || index > array.Count) throw new ArgumentOutOfRangeException(indexName); + } + + /// + /// Requires the range [offset, offset + count] to be a subset of [0, array.Count]. + /// + /// Array is null. + /// Offset or count are out of range. + internal static void RequiresArrayRange(IList array, int offset, int count, string offsetName, string countName) { + Assert.NotEmpty(offsetName); + Assert.NotEmpty(countName); + Assert.NotNull(array); + + if (count < 0) throw new ArgumentOutOfRangeException(countName); + if (offset < 0 || array.Count - offset < count) throw new ArgumentOutOfRangeException(offsetName); + } + + /// + /// Requires the range [offset, offset + count] to be a subset of [0, array.Count]. + /// + /// Array is null. + /// Offset or count are out of range. + internal static void RequiresListRange(IList array, int offset, int count, string offsetName, string countName) { + Assert.NotEmpty(offsetName); + Assert.NotEmpty(countName); + Assert.NotNull(array); + + if (count < 0) throw new ArgumentOutOfRangeException(countName); + if (offset < 0 || array.Count - offset < count) throw new ArgumentOutOfRangeException(offsetName); + } + + /// + /// Requires the range [offset, offset + count] to be a subset of [0, array.Count]. + /// + /// String is null. + /// Offset or count are out of range. + internal static void RequiresArrayRange(string str, int offset, int count, string offsetName, string countName) { + Assert.NotEmpty(offsetName); + Assert.NotEmpty(countName); + Assert.NotNull(str); + + if (count < 0) throw new ArgumentOutOfRangeException(countName); + if (offset < 0 || str.Length - offset < count) throw new ArgumentOutOfRangeException(offsetName); + } + + /// + /// Requires the array and all its items to be non-null. + /// + internal static void RequiresNotNullItems(IList array, string arrayName) { + Assert.NotNull(arrayName); + RequiresNotNull(array, arrayName); + + for (int i = 0; i < array.Count; i++) { + if (array[i] == null) { + throw new ArgumentNullException(string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0}[{1}]", arrayName, i)); + } + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/ExceptionFactory.Generated.cs b/ndp/fx/src/core/microsoft/scripting/Utils/ExceptionFactory.Generated.cs new file mode 100644 index 0000000000..8ce56cc81a --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/ExceptionFactory.Generated.cs @@ -0,0 +1,2070 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace System.Linq.Expressions { + + internal static partial class Strings { + private static string FormatString(string format, params object[] args) { + return string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args); + } + } + + #region Generated Exception Factory + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_expr_factory_core from: generate_exception_factory.py + + /// + /// Strongly-typed and parameterized string factory. + /// + + internal static partial class Strings { + /// + /// A string like "DLinq" + /// + internal static string OwningTeam { + get { + return "DLinq"; + } + } + + /// + /// A string like "Method precondition violated" + /// + internal static string MethodPreconditionViolated { + get { + return "Method precondition violated"; + } + } + + /// + /// A string like "Invalid argument value" + /// + internal static string InvalidArgumentValue { + get { + return "Invalid argument value"; + } + } + + /// + /// A string like "Non-empty string required" + /// + internal static string NonEmptyStringRequired { + get { + return "Non-empty string required"; + } + } + + /// + /// A string like "Non-empty collection required" + /// + internal static string NonEmptyCollectionRequired { + get { + return "Non-empty collection required"; + } + } + + /// + /// A string like "must be >= 0" + /// + internal static string MustBePositive { + get { + return "must be >= 0"; + } + } + + /// + /// A string like "reducible nodes must override Expression.Reduce()" + /// + internal static string ReducibleMustOverrideReduce { + get { + return "reducible nodes must override Expression.Reduce()"; + } + } + + /// + /// A string like "node cannot reduce to itself or null" + /// + internal static string MustReduceToDifferent { + get { + return "node cannot reduce to itself or null"; + } + } + + /// + /// A string like "cannot assign from the reduced node type to the original node type" + /// + internal static string ReducedNotCompatible { + get { + return "cannot assign from the reduced node type to the original node type"; + } + } + + /// + /// A string like "Setter must have parameters." + /// + internal static string SetterHasNoParams { + get { + return "Setter must have parameters."; + } + } + + /// + /// A string like "Property cannot have a managed pointer type." + /// + internal static string PropertyCannotHaveRefType { + get { + return "Property cannot have a managed pointer type."; + } + } + + /// + /// A string like "Indexing parameters of getter and setter must match." + /// + internal static string IndexesOfSetGetMustMatch { + get { + return "Indexing parameters of getter and setter must match."; + } + } + + /// + /// A string like "Accessor method should not have VarArgs." + /// + internal static string AccessorsCannotHaveVarArgs { + get { + return "Accessor method should not have VarArgs."; + } + } + + /// + /// A string like "Accessor indexes cannot be passed ByRef." + /// + internal static string AccessorsCannotHaveByRefArgs { + get { + return "Accessor indexes cannot be passed ByRef."; + } + } + + /// + /// A string like "Bounds count cannot be less than 1" + /// + internal static string BoundsCannotBeLessThanOne { + get { + return "Bounds count cannot be less than 1"; + } + } + + /// + /// A string like "Value must be int" + /// + internal static string ValueMustBeInt { + get { + return "Value must be int"; + } + } + + /// + /// A string like "Only one default clause allowed" + /// + internal static string OnlyDefaultIsAllowed { + get { + return "Only one default clause allowed"; + } + } + + /// + /// A string like "Case values must be unique" + /// + internal static string CaseValuesMustBeUnique { + get { + return "Case values must be unique"; + } + } + + /// + /// A string like "type must not be ByRef" + /// + internal static string TypeMustNotBeByRef { + get { + return "type must not be ByRef"; + } + } + + /// + /// A string like "variable must not be ByRef" + /// + internal static string VariableMustNotBeByRef { + get { + return "variable must not be ByRef"; + } + } + + /// + /// A string like "Type doesn't have constructor with a given signature" + /// + internal static string TypeDoesNotHaveConstructorForTheSignature { + get { + return "Type doesn't have constructor with a given signature"; + } + } + + /// + /// A string like "Type doesn't have a method with a given name." + /// + internal static string TypeDoesNotHaveMethodForName { + get { + return "Type doesn't have a method with a given name."; + } + } + + /// + /// A string like "Type doesn't have a method with a given name and signature." + /// + internal static string TypeDoesNotHaveMethodForNameSignature { + get { + return "Type doesn't have a method with a given name and signature."; + } + } + + /// + /// A string like "Count must be non-negative." + /// + internal static string CountCannotBeNegative { + get { + return "Count must be non-negative."; + } + } + + /// + /// A string like "arrayType must be an array type" + /// + internal static string ArrayTypeMustBeArray { + get { + return "arrayType must be an array type"; + } + } + + /// + /// A string like "Setter should have void type." + /// + internal static string SetterMustBeVoid { + get { + return "Setter should have void type."; + } + } + + /// + /// A string like "Property type must match the value type of setter" + /// + internal static string PropertyTyepMustMatchSetter { + get { + return "Property type must match the value type of setter"; + } + } + + /// + /// A string like "Both accessors must be static." + /// + internal static string BothAccessorsMustBeStatic { + get { + return "Both accessors must be static."; + } + } + + /// + /// A string like "Static field requires null instance, non-static field requires non-null instance." + /// + internal static string OnlyStaticFieldsHaveNullInstance { + get { + return "Static field requires null instance, non-static field requires non-null instance."; + } + } + + /// + /// A string like "Static property requires null instance, non-static property requires non-null instance." + /// + internal static string OnlyStaticPropertiesHaveNullInstance { + get { + return "Static property requires null instance, non-static property requires non-null instance."; + } + } + + /// + /// A string like "Static method requires null instance, non-static method requires non-null instance." + /// + internal static string OnlyStaticMethodsHaveNullInstance { + get { + return "Static method requires null instance, non-static method requires non-null instance."; + } + } + + /// + /// A string like "Property cannot have a void type." + /// + internal static string PropertyTypeCannotBeVoid { + get { + return "Property cannot have a void type."; + } + } + + /// + /// A string like "Can only unbox from an object or interface type to a value type." + /// + internal static string InvalidUnboxType { + get { + return "Can only unbox from an object or interface type to a value type."; + } + } + + /// + /// A string like "Expression must be readable" + /// + internal static string ExpressionMustBeReadable { + get { + return "Expression must be readable"; + } + } + + /// + /// A string like "Expression must be writeable" + /// + internal static string ExpressionMustBeWriteable { + get { + return "Expression must be writeable"; + } + } + + /// + /// A string like "Argument must be exception" + /// + internal static string ArgumentMustBeException { + get { + return "Argument must be exception"; + } + } + + /// + /// A string like "must be reducible node" + /// + internal static string MustBeReducible { + get { + return "must be reducible node"; + } + } + + /// + /// A string like "TypeBuilder does not have a valid ModuleBuilder" + /// + internal static string InvalidTypeBuilder { + get { + return "TypeBuilder does not have a valid ModuleBuilder"; + } + } + + /// + /// A string like "Type must be derived from System.Delegate" + /// + internal static string TypeMustBeDerivedFromSystemDelegate { + get { + return "Type must be derived from System.Delegate"; + } + } + + /// + /// A string like "Argument type cannot be void" + /// + internal static string ArgumentTypeCannotBeVoid { + get { + return "Argument type cannot be void"; + } + } + + /// + /// A string like "Label type must be System.Void if an expression is not supplied" + /// + internal static string LabelMustBeVoidOrHaveExpression { + get { + return "Label type must be System.Void if an expression is not supplied"; + } + } + + /// + /// A string like "Type must be System.Void for this label argument" + /// + internal static string LabelTypeMustBeVoid { + get { + return "Type must be System.Void for this label argument"; + } + } + + /// + /// A string like "Start and End must be well ordered" + /// + internal static string StartEndMustBeOrdered { + get { + return "Start and End must be well ordered"; + } + } + + /// + /// A string like "fault cannot be used with catch or finally clauses" + /// + internal static string FaultCannotHaveCatchOrFinally { + get { + return "fault cannot be used with catch or finally clauses"; + } + } + + /// + /// A string like "try must have at least one catch, finally, or fault clause" + /// + internal static string TryMustHaveCatchFinallyOrFault { + get { + return "try must have at least one catch, finally, or fault clause"; + } + } + + /// + /// A string like "User-defined operator method '{0}' must be static." + /// + internal static string UserDefinedOperatorMustBeStatic(object p0) { + return FormatString("User-defined operator method '{0}' must be static.", p0); + } + + /// + /// A string like "User-defined operator method '{0}' must not be void." + /// + internal static string UserDefinedOperatorMustNotBeVoid(object p0) { + return FormatString("User-defined operator method '{0}' must not be void.", p0); + } + + /// + /// A string like "No coercion operator is defined between types '{0}' and '{1}'." + /// + internal static string CoercionOperatorNotDefined(object p0, object p1) { + return FormatString("No coercion operator is defined between types '{0}' and '{1}'.", p0, p1); + } + + /// + /// A string like "The unary operator {0} is not defined for the type '{1}'." + /// + internal static string UnaryOperatorNotDefined(object p0, object p1) { + return FormatString("The unary operator {0} is not defined for the type '{1}'.", p0, p1); + } + + /// + /// A string like "The binary operator {0} is not defined for the types '{1}' and '{2}'." + /// + internal static string BinaryOperatorNotDefined(object p0, object p1, object p2) { + return FormatString("The binary operator {0} is not defined for the types '{1}' and '{2}'.", p0, p1, p2); + } + + /// + /// A string like "The operands for operator '{0}' do not match the parameters of method '{1}'." + /// + internal static string OperandTypesDoNotMatchParameters(object p0, object p1) { + return FormatString("The operands for operator '{0}' do not match the parameters of method '{1}'.", p0, p1); + } + + /// + /// A string like "Argument must be array" + /// + internal static string ArgumentMustBeArray { + get { + return "Argument must be array"; + } + } + + /// + /// A string like "Argument must be boolean" + /// + internal static string ArgumentMustBeBoolean { + get { + return "Argument must be boolean"; + } + } + + /// + /// A string like "Argument must be either a FieldInfo or PropertyInfo" + /// + internal static string ArgumentMustBeFieldInfoOrPropertInfo { + get { + return "Argument must be either a FieldInfo or PropertyInfo"; + } + } + + /// + /// A string like "Argument must be either a FieldInfo, PropertyInfo or MethodInfo" + /// + internal static string ArgumentMustBeFieldInfoOrPropertInfoOrMethod { + get { + return "Argument must be either a FieldInfo, PropertyInfo or MethodInfo"; + } + } + + /// + /// A string like "Argument must be an instance member" + /// + internal static string ArgumentMustBeInstanceMember { + get { + return "Argument must be an instance member"; + } + } + + /// + /// A string like "Argument must be of an integer type" + /// + internal static string ArgumentMustBeInteger { + get { + return "Argument must be of an integer type"; + } + } + + /// + /// A string like "Argument for array index must be of type Int32" + /// + internal static string ArgumentMustBeArrayIndexType { + get { + return "Argument for array index must be of type Int32"; + } + } + + /// + /// A string like "Argument must be single dimensional array type" + /// + internal static string ArgumentMustBeSingleDimensionalArrayType { + get { + return "Argument must be single dimensional array type"; + } + } + + /// + /// A string like "Argument types do not match" + /// + internal static string ArgumentTypesMustMatch { + get { + return "Argument types do not match"; + } + } + + /// + /// A string like "Cannot auto initialize elements of value type through property '{0}', use assignment instead" + /// + internal static string CannotAutoInitializeValueTypeElementThroughProperty(object p0) { + return FormatString("Cannot auto initialize elements of value type through property '{0}', use assignment instead", p0); + } + + /// + /// A string like "Cannot auto initialize members of value type through property '{0}', use assignment instead" + /// + internal static string CannotAutoInitializeValueTypeMemberThroughProperty(object p0) { + return FormatString("Cannot auto initialize members of value type through property '{0}', use assignment instead", p0); + } + + /// + /// A string like "The type used in TypeAs Expression must be of reference or nullable type, {0} is neither" + /// + internal static string IncorrectTypeForTypeAs(object p0) { + return FormatString("The type used in TypeAs Expression must be of reference or nullable type, {0} is neither", p0); + } + + /// + /// A string like "Coalesce used with type that cannot be null" + /// + internal static string CoalesceUsedOnNonNullType { + get { + return "Coalesce used with type that cannot be null"; + } + } + + /// + /// A string like "An expression of type '{0}' cannot be used to initialize an array of type '{1}'" + /// + internal static string ExpressionTypeCannotInitializeArrayType(object p0, object p1) { + return FormatString("An expression of type '{0}' cannot be used to initialize an array of type '{1}'", p0, p1); + } + + /// + /// A string like "Expression of type '{0}' cannot be used for constructor parameter of type '{1}'" + /// + internal static string ExpressionTypeDoesNotMatchConstructorParameter(object p0, object p1) { + return FormatString("Expression of type '{0}' cannot be used for constructor parameter of type '{1}'", p0, p1); + } + + /// + /// A string like " Argument type '{0}' does not match the corresponding member type '{1}'" + /// + internal static string ArgumentTypeDoesNotMatchMember(object p0, object p1) { + return FormatString(" Argument type '{0}' does not match the corresponding member type '{1}'", p0, p1); + } + + /// + /// A string like " The member '{0}' is not declared on type '{1}' being created" + /// + internal static string ArgumentMemberNotDeclOnType(object p0, object p1) { + return FormatString(" The member '{0}' is not declared on type '{1}' being created", p0, p1); + } + + /// + /// A string like "Expression of type '{0}' cannot be used for parameter of type '{1}' of method '{2}'" + /// + internal static string ExpressionTypeDoesNotMatchMethodParameter(object p0, object p1, object p2) { + return FormatString("Expression of type '{0}' cannot be used for parameter of type '{1}' of method '{2}'", p0, p1, p2); + } + + /// + /// A string like "Expression of type '{0}' cannot be used for parameter of type '{1}'" + /// + internal static string ExpressionTypeDoesNotMatchParameter(object p0, object p1) { + return FormatString("Expression of type '{0}' cannot be used for parameter of type '{1}'", p0, p1); + } + + /// + /// A string like "Expression of type '{0}' cannot be used for return type '{1}'" + /// + internal static string ExpressionTypeDoesNotMatchReturn(object p0, object p1) { + return FormatString("Expression of type '{0}' cannot be used for return type '{1}'", p0, p1); + } + + /// + /// A string like "Expression of type '{0}' cannot be used for assignment to type '{1}'" + /// + internal static string ExpressionTypeDoesNotMatchAssignment(object p0, object p1) { + return FormatString("Expression of type '{0}' cannot be used for assignment to type '{1}'", p0, p1); + } + + /// + /// A string like "Expression of type '{0}' cannot be used for label of type '{1}'" + /// + internal static string ExpressionTypeDoesNotMatchLabel(object p0, object p1) { + return FormatString("Expression of type '{0}' cannot be used for label of type '{1}'", p0, p1); + } + + /// + /// A string like "Expression of type '{0}' cannot be invoked" + /// + internal static string ExpressionTypeNotInvocable(object p0) { + return FormatString("Expression of type '{0}' cannot be invoked", p0); + } + + /// + /// A string like "Field '{0}' is not defined for type '{1}'" + /// + internal static string FieldNotDefinedForType(object p0, object p1) { + return FormatString("Field '{0}' is not defined for type '{1}'", p0, p1); + } + + /// + /// A string like "Field '{0}.{1}' is not defined for type '{2}'" + /// + internal static string FieldInfoNotDefinedForType(object p0, object p1, object p2) { + return FormatString("Field '{0}.{1}' is not defined for type '{2}'", p0, p1, p2); + } + + /// + /// A string like "Incorrect number of indexes" + /// + internal static string IncorrectNumberOfIndexes { + get { + return "Incorrect number of indexes"; + } + } + + /// + /// A string like "Incorrect number of arguments supplied for lambda invocation" + /// + internal static string IncorrectNumberOfLambdaArguments { + get { + return "Incorrect number of arguments supplied for lambda invocation"; + } + } + + /// + /// A string like "Incorrect number of parameters supplied for lambda declaration" + /// + internal static string IncorrectNumberOfLambdaDeclarationParameters { + get { + return "Incorrect number of parameters supplied for lambda declaration"; + } + } + + /// + /// A string like "Incorrect number of arguments supplied for call to method '{0}'" + /// + internal static string IncorrectNumberOfMethodCallArguments(object p0) { + return FormatString("Incorrect number of arguments supplied for call to method '{0}'", p0); + } + + /// + /// A string like "Incorrect number of arguments for constructor" + /// + internal static string IncorrectNumberOfConstructorArguments { + get { + return "Incorrect number of arguments for constructor"; + } + } + + /// + /// A string like " Incorrect number of members for constructor" + /// + internal static string IncorrectNumberOfMembersForGivenConstructor { + get { + return " Incorrect number of members for constructor"; + } + } + + /// + /// A string like "Incorrect number of arguments for the given members " + /// + internal static string IncorrectNumberOfArgumentsForMembers { + get { + return "Incorrect number of arguments for the given members "; + } + } + + /// + /// A string like "Lambda type parameter must be derived from System.Delegate" + /// + internal static string LambdaTypeMustBeDerivedFromSystemDelegate { + get { + return "Lambda type parameter must be derived from System.Delegate"; + } + } + + /// + /// A string like "Member '{0}' not field or property" + /// + internal static string MemberNotFieldOrProperty(object p0) { + return FormatString("Member '{0}' not field or property", p0); + } + + /// + /// A string like "Method {0} contains generic parameters" + /// + internal static string MethodContainsGenericParameters(object p0) { + return FormatString("Method {0} contains generic parameters", p0); + } + + /// + /// A string like "Method {0} is a generic method definition" + /// + internal static string MethodIsGeneric(object p0) { + return FormatString("Method {0} is a generic method definition", p0); + } + + /// + /// A string like "The method '{0}.{1}' is not a property accessor" + /// + internal static string MethodNotPropertyAccessor(object p0, object p1) { + return FormatString("The method '{0}.{1}' is not a property accessor", p0, p1); + } + + /// + /// A string like "The property '{0}' has no 'get' accessor" + /// + internal static string PropertyDoesNotHaveGetter(object p0) { + return FormatString("The property '{0}' has no 'get' accessor", p0); + } + + /// + /// A string like "The property '{0}' has no 'set' accessor" + /// + internal static string PropertyDoesNotHaveSetter(object p0) { + return FormatString("The property '{0}' has no 'set' accessor", p0); + } + + /// + /// A string like "The property '{0}' has no 'get' or 'set' accessors" + /// + internal static string PropertyDoesNotHaveAccessor(object p0) { + return FormatString("The property '{0}' has no 'get' or 'set' accessors", p0); + } + + /// + /// A string like "'{0}' is not a member of type '{1}'" + /// + internal static string NotAMemberOfType(object p0, object p1) { + return FormatString("'{0}' is not a member of type '{1}'", p0, p1); + } + + /// + /// A string like "The operator '{0}' is not implemented for type '{1}'" + /// + internal static string OperatorNotImplementedForType(object p0, object p1) { + return FormatString("The operator '{0}' is not implemented for type '{1}'", p0, p1); + } + + /// + /// A string like "ParameterExpression of type '{0}' cannot be used for delegate parameter of type '{1}'" + /// + internal static string ParameterExpressionNotValidAsDelegate(object p0, object p1) { + return FormatString("ParameterExpression of type '{0}' cannot be used for delegate parameter of type '{1}'", p0, p1); + } + + /// + /// A string like "Property '{0}' is not defined for type '{1}'" + /// + internal static string PropertyNotDefinedForType(object p0, object p1) { + return FormatString("Property '{0}' is not defined for type '{1}'", p0, p1); + } + + /// + /// A string like "Method '{0}' is not defined for type '{1}'" + /// + internal static string MethodNotDefinedForType(object p0, object p1) { + return FormatString("Method '{0}' is not defined for type '{1}'", p0, p1); + } + + /// + /// A string like "Type {0} contains generic parameters" + /// + internal static string TypeContainsGenericParameters(object p0) { + return FormatString("Type {0} contains generic parameters", p0); + } + + /// + /// A string like "Type {0} is a generic type definition" + /// + internal static string TypeIsGeneric(object p0) { + return FormatString("Type {0} is a generic type definition", p0); + } + + /// + /// A string like "Type '{0}' does not have a default constructor" + /// + internal static string TypeMissingDefaultConstructor(object p0) { + return FormatString("Type '{0}' does not have a default constructor", p0); + } + + /// + /// A string like "List initializers must contain at least one initializer" + /// + internal static string ListInitializerWithZeroMembers { + get { + return "List initializers must contain at least one initializer"; + } + } + + /// + /// A string like "Element initializer method must be named 'Add'" + /// + internal static string ElementInitializerMethodNotAdd { + get { + return "Element initializer method must be named 'Add'"; + } + } + + /// + /// A string like "Parameter '{0}' of element initializer method '{1}' must not be a pass by reference parameter" + /// + internal static string ElementInitializerMethodNoRefOutParam(object p0, object p1) { + return FormatString("Parameter '{0}' of element initializer method '{1}' must not be a pass by reference parameter", p0, p1); + } + + /// + /// A string like "Element initializer method must have at least 1 parameter" + /// + internal static string ElementInitializerMethodWithZeroArgs { + get { + return "Element initializer method must have at least 1 parameter"; + } + } + + /// + /// A string like "Element initializer method must be an instance method" + /// + internal static string ElementInitializerMethodStatic { + get { + return "Element initializer method must be an instance method"; + } + } + + /// + /// A string like "Type '{0}' is not IEnumerable" + /// + internal static string TypeNotIEnumerable(object p0) { + return FormatString("Type '{0}' is not IEnumerable", p0); + } + + /// + /// A string like "Type parameter is {0}. Expected a delegate." + /// + internal static string TypeParameterIsNotDelegate(object p0) { + return FormatString("Type parameter is {0}. Expected a delegate.", p0); + } + + /// + /// A string like "Unexpected coalesce operator." + /// + internal static string UnexpectedCoalesceOperator { + get { + return "Unexpected coalesce operator."; + } + } + + /// + /// A string like "Cannot cast from type '{0}' to type '{1}" + /// + internal static string InvalidCast(object p0, object p1) { + return FormatString("Cannot cast from type '{0}' to type '{1}", p0, p1); + } + + /// + /// A string like "Unhandled binary: {0}" + /// + internal static string UnhandledBinary(object p0) { + return FormatString("Unhandled binary: {0}", p0); + } + + /// + /// A string like "Unhandled binding " + /// + internal static string UnhandledBinding { + get { + return "Unhandled binding "; + } + } + + /// + /// A string like "Unhandled Binding Type: {0}" + /// + internal static string UnhandledBindingType(object p0) { + return FormatString("Unhandled Binding Type: {0}", p0); + } + + /// + /// A string like "Unhandled convert: {0}" + /// + internal static string UnhandledConvert(object p0) { + return FormatString("Unhandled convert: {0}", p0); + } + + /// + /// A string like "Unhandled Expression Type: {0}" + /// + internal static string UnhandledExpressionType(object p0) { + return FormatString("Unhandled Expression Type: {0}", p0); + } + + /// + /// A string like "Unhandled unary: {0}" + /// + internal static string UnhandledUnary(object p0) { + return FormatString("Unhandled unary: {0}", p0); + } + + /// + /// A string like "Unknown binding type" + /// + internal static string UnknownBindingType { + get { + return "Unknown binding type"; + } + } + + /// + /// A string like "The user-defined operator method '{1}' for operator '{0}' must have identical parameter and return types." + /// + internal static string UserDefinedOpMustHaveConsistentTypes(object p0, object p1) { + return FormatString("The user-defined operator method '{1}' for operator '{0}' must have identical parameter and return types.", p0, p1); + } + + /// + /// A string like "The user-defined operator method '{1}' for operator '{0}' must return the same type as its parameter or a derived type." + /// + internal static string UserDefinedOpMustHaveValidReturnType(object p0, object p1) { + return FormatString("The user-defined operator method '{1}' for operator '{0}' must return the same type as its parameter or a derived type.", p0, p1); + } + + /// + /// A string like "The user-defined operator method '{1}' for operator '{0}' must have associated boolean True and False operators." + /// + internal static string LogicalOperatorMustHaveBooleanOperators(object p0, object p1) { + return FormatString("The user-defined operator method '{1}' for operator '{0}' must have associated boolean True and False operators.", p0, p1); + } + + /// + /// A string like "No method '{0}' exists on type '{1}'." + /// + internal static string MethodDoesNotExistOnType(object p0, object p1) { + return FormatString("No method '{0}' exists on type '{1}'.", p0, p1); + } + + /// + /// A string like "No method '{0}' on type '{1}' is compatible with the supplied arguments." + /// + internal static string MethodWithArgsDoesNotExistOnType(object p0, object p1) { + return FormatString("No method '{0}' on type '{1}' is compatible with the supplied arguments.", p0, p1); + } + + /// + /// A string like "More than one method '{0}' on type '{1}' is compatible with the supplied arguments." + /// + internal static string MethodWithMoreThanOneMatch(object p0, object p1) { + return FormatString("More than one method '{0}' on type '{1}' is compatible with the supplied arguments.", p0, p1); + } + + /// + /// A string like "An incorrect number of type args were specified for the declaration of a Func type." + /// + internal static string IncorrectNumberOfTypeArgsForFunc { + get { + return "An incorrect number of type args were specified for the declaration of a Func type."; + } + } + + /// + /// A string like "An incorrect number of type args were specified for the declaration of an Action type." + /// + internal static string IncorrectNumberOfTypeArgsForAction { + get { + return "An incorrect number of type args were specified for the declaration of an Action type."; + } + } + + /// + /// A string like "Argument type cannot be System.Void." + /// + internal static string ArgumentCannotBeOfTypeVoid { + get { + return "Argument type cannot be System.Void."; + } + } + + /// + /// A string like "No or Invalid rule produced" + /// + internal static string NoOrInvalidRuleProduced { + get { + return "No or Invalid rule produced"; + } + } + + /// + /// A string like "First argument of delegate must be CallSite" + /// + internal static string FirstArgumentMustBeCallSite { + get { + return "First argument of delegate must be CallSite"; + } + } + + /// + /// A string like "Bind cannot return null." + /// + internal static string BindingCannotBeNull { + get { + return "Bind cannot return null."; + } + } + + /// + /// A string like "Bad data bound to delegate." + /// + internal static string BadDelegateData { + get { + return "Bad data bound to delegate."; + } + } + + /// + /// A string like "Invalid operation: '{0}'" + /// + internal static string InvalidOperation(object p0) { + return FormatString("Invalid operation: '{0}'", p0); + } + + /// + /// A string like "{0} must be greater than or equal to {1}" + /// + internal static string OutOfRange(object p0, object p1) { + return FormatString("{0} must be greater than or equal to {1}", p0, p1); + } + + /// + /// A string like "Queue empty." + /// + internal static string QueueEmpty { + get { + return "Queue empty."; + } + } + + /// + /// A string like "Cannot redefine label '{0}' in an inner block." + /// + internal static string LabelTargetAlreadyDefined(object p0) { + return FormatString("Cannot redefine label '{0}' in an inner block.", p0); + } + + /// + /// A string like "Cannot jump to to undefined label '{0}'." + /// + internal static string LabelTargetUndefined(object p0) { + return FormatString("Cannot jump to to undefined label '{0}'.", p0); + } + + /// + /// A string like "Control cannot leave a finally block." + /// + internal static string ControlCannotLeaveFinally { + get { + return "Control cannot leave a finally block."; + } + } + + /// + /// A string like "Control cannot leave a filter test." + /// + internal static string ControlCannotLeaveFilterTest { + get { + return "Control cannot leave a filter test."; + } + } + + /// + /// A string like "Cannot jump to ambiguous label '{0}'." + /// + internal static string AmbiguousJump(object p0) { + return FormatString("Cannot jump to ambiguous label '{0}'.", p0); + } + + /// + /// A string like "Control cannot enter a try block." + /// + internal static string ControlCannotEnterTry { + get { + return "Control cannot enter a try block."; + } + } + + /// + /// A string like "Control cannot enter an expression--only statements can be jumped into." + /// + internal static string ControlCannotEnterExpression { + get { + return "Control cannot enter an expression--only statements can be jumped into."; + } + } + + /// + /// A string like "Extension should have been reduced." + /// + internal static string ExtensionNotReduced { + get { + return "Extension should have been reduced."; + } + } + + /// + /// A string like "Runtime constants require a bound delegate." + /// + internal static string RtConstRequiresBundDelegate { + get { + return "Runtime constants require a bound delegate."; + } + } + + /// + /// A string like "Invalid lvalue for assignment: {0}." + /// + internal static string InvalidLvalue(object p0) { + return FormatString("Invalid lvalue for assignment: {0}.", p0); + } + + /// + /// A string like "Invalid member type: {0}." + /// + internal static string InvalidMemberType(object p0) { + return FormatString("Invalid member type: {0}.", p0); + } + + /// + /// A string like "unknown lift type: '{0}'." + /// + internal static string UnknownLiftType(object p0) { + return FormatString("unknown lift type: '{0}'.", p0); + } + + /// + /// A string like "Invalid output directory." + /// + internal static string InvalidOutputDir { + get { + return "Invalid output directory."; + } + } + + /// + /// A string like "Invalid assembly name or file extension." + /// + internal static string InvalidAsmNameOrExtension { + get { + return "Invalid assembly name or file extension."; + } + } + + /// + /// A string like "Stack changed while enumerating." + /// + internal static string StackChangedWhileEnumerationg { + get { + return "Stack changed while enumerating."; + } + } + + /// + /// A string like "Collection is read-only." + /// + internal static string CollectionReadOnly { + get { + return "Collection is read-only."; + } + } + + /// + /// A string like "Cannot create instance of {0} because it contains generic parameters" + /// + internal static string IllegalNewGenericParams(object p0) { + return FormatString("Cannot create instance of {0} because it contains generic parameters", p0); + } + + /// + /// A string like "variable '{0}' of type '{1}' referenced from scope '{2}', but it is not defined" + /// + internal static string UndefinedVariable(object p0, object p1, object p2) { + return FormatString("variable '{0}' of type '{1}' referenced from scope '{2}', but it is not defined", p0, p1, p2); + } + + /// + /// A string like "Cannot close over byref parameter '{0}' referenced in lambda '{1}'" + /// + internal static string CannotCloseOverByRef(object p0, object p1) { + return FormatString("Cannot close over byref parameter '{0}' referenced in lambda '{1}'", p0, p1); + } + + /// + /// A string like "Unexpected VarArgs call to method '{0}'" + /// + internal static string UnexpectedVarArgsCall(object p0) { + return FormatString("Unexpected VarArgs call to method '{0}'", p0); + } + + /// + /// A string like "Rethrow statement is valid only inside a Catch block." + /// + internal static string RethrowRequiresCatch { + get { + return "Rethrow statement is valid only inside a Catch block."; + } + } + + /// + /// A string like "When called from '{0}', rewriting a node of type '{1}' should return a non-null value of the same type. Alternatively, override '{2}' and change it to not visit children of this type." + /// + internal static string MustRewriteToSameType(object p0, object p1, object p2) { + return FormatString("When called from '{0}', rewriting a node of type '{1}' should return a non-null value of the same type. Alternatively, override '{2}' and change it to not visit children of this type.", p0, p1, p2); + } + + } + /// + /// Strongly-typed and parameterized exception factory. + /// + + internal static partial class Error { + /// + /// ArgumentException with message like "Start and End must be well ordered" + /// + internal static Exception StartEndMustBeOrdered() { + return new ArgumentException(Strings.StartEndMustBeOrdered); + } + + /// + /// ArgumentException with message like "fault cannot be used with catch or finally clauses" + /// + internal static Exception FaultCannotHaveCatchOrFinally() { + return new ArgumentException(Strings.FaultCannotHaveCatchOrFinally); + } + + /// + /// ArgumentException with message like "try must have at least one catch, finally, or fault clause" + /// + internal static Exception TryMustHaveCatchFinallyOrFault() { + return new ArgumentException(Strings.TryMustHaveCatchFinallyOrFault); + } + + /// + /// ArgumentException with message like "User-defined operator method '{0}' must be static." + /// + internal static Exception UserDefinedOperatorMustBeStatic(object p0) { + return new ArgumentException(Strings.UserDefinedOperatorMustBeStatic(p0)); + } + + /// + /// ArgumentException with message like "User-defined operator method '{0}' must not be void." + /// + internal static Exception UserDefinedOperatorMustNotBeVoid(object p0) { + return new ArgumentException(Strings.UserDefinedOperatorMustNotBeVoid(p0)); + } + + /// + /// InvalidOperationException with message like "No coercion operator is defined between types '{0}' and '{1}'." + /// + internal static Exception CoercionOperatorNotDefined(object p0, object p1) { + return new InvalidOperationException(Strings.CoercionOperatorNotDefined(p0, p1)); + } + + /// + /// InvalidOperationException with message like "The unary operator {0} is not defined for the type '{1}'." + /// + internal static Exception UnaryOperatorNotDefined(object p0, object p1) { + return new InvalidOperationException(Strings.UnaryOperatorNotDefined(p0, p1)); + } + + /// + /// InvalidOperationException with message like "The binary operator {0} is not defined for the types '{1}' and '{2}'." + /// + internal static Exception BinaryOperatorNotDefined(object p0, object p1, object p2) { + return new InvalidOperationException(Strings.BinaryOperatorNotDefined(p0, p1, p2)); + } + + /// + /// InvalidOperationException with message like "The operands for operator '{0}' do not match the parameters of method '{1}'." + /// + internal static Exception OperandTypesDoNotMatchParameters(object p0, object p1) { + return new InvalidOperationException(Strings.OperandTypesDoNotMatchParameters(p0, p1)); + } + + /// + /// ArgumentException with message like "Argument must be array" + /// + internal static Exception ArgumentMustBeArray() { + return new ArgumentException(Strings.ArgumentMustBeArray); + } + + /// + /// ArgumentException with message like "Argument must be boolean" + /// + internal static Exception ArgumentMustBeBoolean() { + return new ArgumentException(Strings.ArgumentMustBeBoolean); + } + + /// + /// ArgumentException with message like "Argument must be either a FieldInfo or PropertyInfo" + /// + internal static Exception ArgumentMustBeFieldInfoOrPropertInfo() { + return new ArgumentException(Strings.ArgumentMustBeFieldInfoOrPropertInfo); + } + + /// + /// ArgumentException with message like "Argument must be either a FieldInfo, PropertyInfo or MethodInfo" + /// + internal static Exception ArgumentMustBeFieldInfoOrPropertInfoOrMethod() { + return new ArgumentException(Strings.ArgumentMustBeFieldInfoOrPropertInfoOrMethod); + } + + /// + /// ArgumentException with message like "Argument must be an instance member" + /// + internal static Exception ArgumentMustBeInstanceMember() { + return new ArgumentException(Strings.ArgumentMustBeInstanceMember); + } + + /// + /// ArgumentException with message like "Argument must be of an integer type" + /// + internal static Exception ArgumentMustBeInteger() { + return new ArgumentException(Strings.ArgumentMustBeInteger); + } + + /// + /// ArgumentException with message like "Argument for array index must be of type Int32" + /// + internal static Exception ArgumentMustBeArrayIndexType() { + return new ArgumentException(Strings.ArgumentMustBeArrayIndexType); + } + + /// + /// ArgumentException with message like "Argument must be single dimensional array type" + /// + internal static Exception ArgumentMustBeSingleDimensionalArrayType() { + return new ArgumentException(Strings.ArgumentMustBeSingleDimensionalArrayType); + } + + /// + /// ArgumentException with message like "Argument types do not match" + /// + internal static Exception ArgumentTypesMustMatch() { + return new ArgumentException(Strings.ArgumentTypesMustMatch); + } + + /// + /// InvalidOperationException with message like "Cannot auto initialize elements of value type through property '{0}', use assignment instead" + /// + internal static Exception CannotAutoInitializeValueTypeElementThroughProperty(object p0) { + return new InvalidOperationException(Strings.CannotAutoInitializeValueTypeElementThroughProperty(p0)); + } + + /// + /// InvalidOperationException with message like "Cannot auto initialize members of value type through property '{0}', use assignment instead" + /// + internal static Exception CannotAutoInitializeValueTypeMemberThroughProperty(object p0) { + return new InvalidOperationException(Strings.CannotAutoInitializeValueTypeMemberThroughProperty(p0)); + } + + /// + /// ArgumentException with message like "The type used in TypeAs Expression must be of reference or nullable type, {0} is neither" + /// + internal static Exception IncorrectTypeForTypeAs(object p0) { + return new ArgumentException(Strings.IncorrectTypeForTypeAs(p0)); + } + + /// + /// InvalidOperationException with message like "Coalesce used with type that cannot be null" + /// + internal static Exception CoalesceUsedOnNonNullType() { + return new InvalidOperationException(Strings.CoalesceUsedOnNonNullType); + } + + /// + /// InvalidOperationException with message like "An expression of type '{0}' cannot be used to initialize an array of type '{1}'" + /// + internal static Exception ExpressionTypeCannotInitializeArrayType(object p0, object p1) { + return new InvalidOperationException(Strings.ExpressionTypeCannotInitializeArrayType(p0, p1)); + } + + /// + /// ArgumentException with message like "Expression of type '{0}' cannot be used for constructor parameter of type '{1}'" + /// + internal static Exception ExpressionTypeDoesNotMatchConstructorParameter(object p0, object p1) { + return new ArgumentException(Strings.ExpressionTypeDoesNotMatchConstructorParameter(p0, p1)); + } + + /// + /// ArgumentException with message like " Argument type '{0}' does not match the corresponding member type '{1}'" + /// + internal static Exception ArgumentTypeDoesNotMatchMember(object p0, object p1) { + return new ArgumentException(Strings.ArgumentTypeDoesNotMatchMember(p0, p1)); + } + + /// + /// ArgumentException with message like " The member '{0}' is not declared on type '{1}' being created" + /// + internal static Exception ArgumentMemberNotDeclOnType(object p0, object p1) { + return new ArgumentException(Strings.ArgumentMemberNotDeclOnType(p0, p1)); + } + + /// + /// ArgumentException with message like "Expression of type '{0}' cannot be used for parameter of type '{1}' of method '{2}'" + /// + internal static Exception ExpressionTypeDoesNotMatchMethodParameter(object p0, object p1, object p2) { + return new ArgumentException(Strings.ExpressionTypeDoesNotMatchMethodParameter(p0, p1, p2)); + } + + /// + /// ArgumentException with message like "Expression of type '{0}' cannot be used for parameter of type '{1}'" + /// + internal static Exception ExpressionTypeDoesNotMatchParameter(object p0, object p1) { + return new ArgumentException(Strings.ExpressionTypeDoesNotMatchParameter(p0, p1)); + } + + /// + /// ArgumentException with message like "Expression of type '{0}' cannot be used for return type '{1}'" + /// + internal static Exception ExpressionTypeDoesNotMatchReturn(object p0, object p1) { + return new ArgumentException(Strings.ExpressionTypeDoesNotMatchReturn(p0, p1)); + } + + /// + /// ArgumentException with message like "Expression of type '{0}' cannot be used for assignment to type '{1}'" + /// + internal static Exception ExpressionTypeDoesNotMatchAssignment(object p0, object p1) { + return new ArgumentException(Strings.ExpressionTypeDoesNotMatchAssignment(p0, p1)); + } + + /// + /// ArgumentException with message like "Expression of type '{0}' cannot be used for label of type '{1}'" + /// + internal static Exception ExpressionTypeDoesNotMatchLabel(object p0, object p1) { + return new ArgumentException(Strings.ExpressionTypeDoesNotMatchLabel(p0, p1)); + } + + /// + /// ArgumentException with message like "Expression of type '{0}' cannot be invoked" + /// + internal static Exception ExpressionTypeNotInvocable(object p0) { + return new ArgumentException(Strings.ExpressionTypeNotInvocable(p0)); + } + + /// + /// ArgumentException with message like "Field '{0}' is not defined for type '{1}'" + /// + internal static Exception FieldNotDefinedForType(object p0, object p1) { + return new ArgumentException(Strings.FieldNotDefinedForType(p0, p1)); + } + + /// + /// ArgumentException with message like "Field '{0}.{1}' is not defined for type '{2}'" + /// + internal static Exception FieldInfoNotDefinedForType(object p0, object p1, object p2) { + return new ArgumentException(Strings.FieldInfoNotDefinedForType(p0, p1, p2)); + } + + /// + /// ArgumentException with message like "Incorrect number of indexes" + /// + internal static Exception IncorrectNumberOfIndexes() { + return new ArgumentException(Strings.IncorrectNumberOfIndexes); + } + + /// + /// InvalidOperationException with message like "Incorrect number of arguments supplied for lambda invocation" + /// + internal static Exception IncorrectNumberOfLambdaArguments() { + return new InvalidOperationException(Strings.IncorrectNumberOfLambdaArguments); + } + + /// + /// ArgumentException with message like "Incorrect number of parameters supplied for lambda declaration" + /// + internal static Exception IncorrectNumberOfLambdaDeclarationParameters() { + return new ArgumentException(Strings.IncorrectNumberOfLambdaDeclarationParameters); + } + + /// + /// ArgumentException with message like "Incorrect number of arguments supplied for call to method '{0}'" + /// + internal static Exception IncorrectNumberOfMethodCallArguments(object p0) { + return new ArgumentException(Strings.IncorrectNumberOfMethodCallArguments(p0)); + } + + /// + /// ArgumentException with message like "Incorrect number of arguments for constructor" + /// + internal static Exception IncorrectNumberOfConstructorArguments() { + return new ArgumentException(Strings.IncorrectNumberOfConstructorArguments); + } + + /// + /// ArgumentException with message like " Incorrect number of members for constructor" + /// + internal static Exception IncorrectNumberOfMembersForGivenConstructor() { + return new ArgumentException(Strings.IncorrectNumberOfMembersForGivenConstructor); + } + + /// + /// ArgumentException with message like "Incorrect number of arguments for the given members " + /// + internal static Exception IncorrectNumberOfArgumentsForMembers() { + return new ArgumentException(Strings.IncorrectNumberOfArgumentsForMembers); + } + + /// + /// ArgumentException with message like "Lambda type parameter must be derived from System.Delegate" + /// + internal static Exception LambdaTypeMustBeDerivedFromSystemDelegate() { + return new ArgumentException(Strings.LambdaTypeMustBeDerivedFromSystemDelegate); + } + + /// + /// ArgumentException with message like "Member '{0}' not field or property" + /// + internal static Exception MemberNotFieldOrProperty(object p0) { + return new ArgumentException(Strings.MemberNotFieldOrProperty(p0)); + } + + /// + /// ArgumentException with message like "Method {0} contains generic parameters" + /// + internal static Exception MethodContainsGenericParameters(object p0) { + return new ArgumentException(Strings.MethodContainsGenericParameters(p0)); + } + + /// + /// ArgumentException with message like "Method {0} is a generic method definition" + /// + internal static Exception MethodIsGeneric(object p0) { + return new ArgumentException(Strings.MethodIsGeneric(p0)); + } + + /// + /// ArgumentException with message like "The method '{0}.{1}' is not a property accessor" + /// + internal static Exception MethodNotPropertyAccessor(object p0, object p1) { + return new ArgumentException(Strings.MethodNotPropertyAccessor(p0, p1)); + } + + /// + /// ArgumentException with message like "The property '{0}' has no 'get' accessor" + /// + internal static Exception PropertyDoesNotHaveGetter(object p0) { + return new ArgumentException(Strings.PropertyDoesNotHaveGetter(p0)); + } + + /// + /// ArgumentException with message like "The property '{0}' has no 'set' accessor" + /// + internal static Exception PropertyDoesNotHaveSetter(object p0) { + return new ArgumentException(Strings.PropertyDoesNotHaveSetter(p0)); + } + + /// + /// ArgumentException with message like "The property '{0}' has no 'get' or 'set' accessors" + /// + internal static Exception PropertyDoesNotHaveAccessor(object p0) { + return new ArgumentException(Strings.PropertyDoesNotHaveAccessor(p0)); + } + + /// + /// ArgumentException with message like "'{0}' is not a member of type '{1}'" + /// + internal static Exception NotAMemberOfType(object p0, object p1) { + return new ArgumentException(Strings.NotAMemberOfType(p0, p1)); + } + + /// + /// NotImplementedException with message like "The operator '{0}' is not implemented for type '{1}'" + /// + internal static Exception OperatorNotImplementedForType(object p0, object p1) { + return new NotImplementedException(Strings.OperatorNotImplementedForType(p0, p1)); + } + + /// + /// ArgumentException with message like "ParameterExpression of type '{0}' cannot be used for delegate parameter of type '{1}'" + /// + internal static Exception ParameterExpressionNotValidAsDelegate(object p0, object p1) { + return new ArgumentException(Strings.ParameterExpressionNotValidAsDelegate(p0, p1)); + } + + /// + /// ArgumentException with message like "Property '{0}' is not defined for type '{1}'" + /// + internal static Exception PropertyNotDefinedForType(object p0, object p1) { + return new ArgumentException(Strings.PropertyNotDefinedForType(p0, p1)); + } + + /// + /// ArgumentException with message like "Method '{0}' is not defined for type '{1}'" + /// + internal static Exception MethodNotDefinedForType(object p0, object p1) { + return new ArgumentException(Strings.MethodNotDefinedForType(p0, p1)); + } + + /// + /// ArgumentException with message like "Type {0} contains generic parameters" + /// + internal static Exception TypeContainsGenericParameters(object p0) { + return new ArgumentException(Strings.TypeContainsGenericParameters(p0)); + } + + /// + /// ArgumentException with message like "Type {0} is a generic type definition" + /// + internal static Exception TypeIsGeneric(object p0) { + return new ArgumentException(Strings.TypeIsGeneric(p0)); + } + + /// + /// ArgumentException with message like "Type '{0}' does not have a default constructor" + /// + internal static Exception TypeMissingDefaultConstructor(object p0) { + return new ArgumentException(Strings.TypeMissingDefaultConstructor(p0)); + } + + /// + /// ArgumentException with message like "List initializers must contain at least one initializer" + /// + internal static Exception ListInitializerWithZeroMembers() { + return new ArgumentException(Strings.ListInitializerWithZeroMembers); + } + + /// + /// ArgumentException with message like "Element initializer method must be named 'Add'" + /// + internal static Exception ElementInitializerMethodNotAdd() { + return new ArgumentException(Strings.ElementInitializerMethodNotAdd); + } + + /// + /// ArgumentException with message like "Parameter '{0}' of element initializer method '{1}' must not be a pass by reference parameter" + /// + internal static Exception ElementInitializerMethodNoRefOutParam(object p0, object p1) { + return new ArgumentException(Strings.ElementInitializerMethodNoRefOutParam(p0, p1)); + } + + /// + /// ArgumentException with message like "Element initializer method must have at least 1 parameter" + /// + internal static Exception ElementInitializerMethodWithZeroArgs() { + return new ArgumentException(Strings.ElementInitializerMethodWithZeroArgs); + } + + /// + /// ArgumentException with message like "Element initializer method must be an instance method" + /// + internal static Exception ElementInitializerMethodStatic() { + return new ArgumentException(Strings.ElementInitializerMethodStatic); + } + + /// + /// ArgumentException with message like "Type '{0}' is not IEnumerable" + /// + internal static Exception TypeNotIEnumerable(object p0) { + return new ArgumentException(Strings.TypeNotIEnumerable(p0)); + } + + /// + /// InvalidOperationException with message like "Type parameter is {0}. Expected a delegate." + /// + internal static Exception TypeParameterIsNotDelegate(object p0) { + return new InvalidOperationException(Strings.TypeParameterIsNotDelegate(p0)); + } + + /// + /// InvalidOperationException with message like "Unexpected coalesce operator." + /// + internal static Exception UnexpectedCoalesceOperator() { + return new InvalidOperationException(Strings.UnexpectedCoalesceOperator); + } + + /// + /// InvalidOperationException with message like "Cannot cast from type '{0}' to type '{1}" + /// + internal static Exception InvalidCast(object p0, object p1) { + return new InvalidOperationException(Strings.InvalidCast(p0, p1)); + } + + /// + /// ArgumentException with message like "Unhandled binary: {0}" + /// + internal static Exception UnhandledBinary(object p0) { + return new ArgumentException(Strings.UnhandledBinary(p0)); + } + + /// + /// ArgumentException with message like "Unhandled binding " + /// + internal static Exception UnhandledBinding() { + return new ArgumentException(Strings.UnhandledBinding); + } + + /// + /// ArgumentException with message like "Unhandled Binding Type: {0}" + /// + internal static Exception UnhandledBindingType(object p0) { + return new ArgumentException(Strings.UnhandledBindingType(p0)); + } + + /// + /// ArgumentException with message like "Unhandled convert: {0}" + /// + internal static Exception UnhandledConvert(object p0) { + return new ArgumentException(Strings.UnhandledConvert(p0)); + } + + /// + /// ArgumentException with message like "Unhandled Expression Type: {0}" + /// + internal static Exception UnhandledExpressionType(object p0) { + return new ArgumentException(Strings.UnhandledExpressionType(p0)); + } + + /// + /// ArgumentException with message like "Unhandled unary: {0}" + /// + internal static Exception UnhandledUnary(object p0) { + return new ArgumentException(Strings.UnhandledUnary(p0)); + } + + /// + /// ArgumentException with message like "Unknown binding type" + /// + internal static Exception UnknownBindingType() { + return new ArgumentException(Strings.UnknownBindingType); + } + + /// + /// ArgumentException with message like "The user-defined operator method '{1}' for operator '{0}' must have identical parameter and return types." + /// + internal static Exception UserDefinedOpMustHaveConsistentTypes(object p0, object p1) { + return new ArgumentException(Strings.UserDefinedOpMustHaveConsistentTypes(p0, p1)); + } + + /// + /// ArgumentException with message like "The user-defined operator method '{1}' for operator '{0}' must return the same type as its parameter or a derived type." + /// + internal static Exception UserDefinedOpMustHaveValidReturnType(object p0, object p1) { + return new ArgumentException(Strings.UserDefinedOpMustHaveValidReturnType(p0, p1)); + } + + /// + /// ArgumentException with message like "The user-defined operator method '{1}' for operator '{0}' must have associated boolean True and False operators." + /// + internal static Exception LogicalOperatorMustHaveBooleanOperators(object p0, object p1) { + return new ArgumentException(Strings.LogicalOperatorMustHaveBooleanOperators(p0, p1)); + } + + /// + /// InvalidOperationException with message like "No method '{0}' exists on type '{1}'." + /// + internal static Exception MethodDoesNotExistOnType(object p0, object p1) { + return new InvalidOperationException(Strings.MethodDoesNotExistOnType(p0, p1)); + } + + /// + /// InvalidOperationException with message like "No method '{0}' on type '{1}' is compatible with the supplied arguments." + /// + internal static Exception MethodWithArgsDoesNotExistOnType(object p0, object p1) { + return new InvalidOperationException(Strings.MethodWithArgsDoesNotExistOnType(p0, p1)); + } + + /// + /// InvalidOperationException with message like "More than one method '{0}' on type '{1}' is compatible with the supplied arguments." + /// + internal static Exception MethodWithMoreThanOneMatch(object p0, object p1) { + return new InvalidOperationException(Strings.MethodWithMoreThanOneMatch(p0, p1)); + } + + /// + /// ArgumentException with message like "An incorrect number of type args were specified for the declaration of a Func type." + /// + internal static Exception IncorrectNumberOfTypeArgsForFunc() { + return new ArgumentException(Strings.IncorrectNumberOfTypeArgsForFunc); + } + + /// + /// ArgumentException with message like "An incorrect number of type args were specified for the declaration of an Action type." + /// + internal static Exception IncorrectNumberOfTypeArgsForAction() { + return new ArgumentException(Strings.IncorrectNumberOfTypeArgsForAction); + } + + /// + /// ArgumentException with message like "Argument type cannot be System.Void." + /// + internal static Exception ArgumentCannotBeOfTypeVoid() { + return new ArgumentException(Strings.ArgumentCannotBeOfTypeVoid); + } + + /// + /// InvalidOperationException with message like "No or Invalid rule produced" + /// + internal static Exception NoOrInvalidRuleProduced() { + return new InvalidOperationException(Strings.NoOrInvalidRuleProduced); + } + + /// + /// ArgumentException with message like "First argument of delegate must be CallSite" + /// + internal static Exception FirstArgumentMustBeCallSite() { + return new ArgumentException(Strings.FirstArgumentMustBeCallSite); + } + + /// + /// InvalidOperationException with message like "Bind cannot return null." + /// + internal static Exception BindingCannotBeNull() { + return new InvalidOperationException(Strings.BindingCannotBeNull); + } + + /// + /// InvalidOperationException with message like "Bad data bound to delegate." + /// + internal static Exception BadDelegateData() { + return new InvalidOperationException(Strings.BadDelegateData); + } + + /// + /// ArgumentException with message like "Invalid operation: '{0}'" + /// + internal static Exception InvalidOperation(object p0) { + return new ArgumentException(Strings.InvalidOperation(p0)); + } + + /// + /// ArgumentOutOfRangeException with message like "{0} must be greater than or equal to {1}" + /// + internal static Exception OutOfRange(object p0, object p1) { + return new ArgumentOutOfRangeException(Strings.OutOfRange(p0, p1)); + } + + /// + /// InvalidOperationException with message like "Queue empty." + /// + internal static Exception QueueEmpty() { + return new InvalidOperationException(Strings.QueueEmpty); + } + + /// + /// InvalidOperationException with message like "Cannot redefine label '{0}' in an inner block." + /// + internal static Exception LabelTargetAlreadyDefined(object p0) { + return new InvalidOperationException(Strings.LabelTargetAlreadyDefined(p0)); + } + + /// + /// InvalidOperationException with message like "Cannot jump to to undefined label '{0}'." + /// + internal static Exception LabelTargetUndefined(object p0) { + return new InvalidOperationException(Strings.LabelTargetUndefined(p0)); + } + + /// + /// InvalidOperationException with message like "Control cannot leave a finally block." + /// + internal static Exception ControlCannotLeaveFinally() { + return new InvalidOperationException(Strings.ControlCannotLeaveFinally); + } + + /// + /// InvalidOperationException with message like "Control cannot leave a filter test." + /// + internal static Exception ControlCannotLeaveFilterTest() { + return new InvalidOperationException(Strings.ControlCannotLeaveFilterTest); + } + + /// + /// InvalidOperationException with message like "Cannot jump to ambiguous label '{0}'." + /// + internal static Exception AmbiguousJump(object p0) { + return new InvalidOperationException(Strings.AmbiguousJump(p0)); + } + + /// + /// InvalidOperationException with message like "Control cannot enter a try block." + /// + internal static Exception ControlCannotEnterTry() { + return new InvalidOperationException(Strings.ControlCannotEnterTry); + } + + /// + /// InvalidOperationException with message like "Control cannot enter an expression--only statements can be jumped into." + /// + internal static Exception ControlCannotEnterExpression() { + return new InvalidOperationException(Strings.ControlCannotEnterExpression); + } + + /// + /// InvalidOperationException with message like "Extension should have been reduced." + /// + internal static Exception ExtensionNotReduced() { + return new InvalidOperationException(Strings.ExtensionNotReduced); + } + + /// + /// InvalidOperationException with message like "Runtime constants require a bound delegate." + /// + internal static Exception RtConstRequiresBundDelegate() { + return new InvalidOperationException(Strings.RtConstRequiresBundDelegate); + } + + /// + /// InvalidOperationException with message like "Invalid lvalue for assignment: {0}." + /// + internal static Exception InvalidLvalue(object p0) { + return new InvalidOperationException(Strings.InvalidLvalue(p0)); + } + + /// + /// InvalidOperationException with message like "Invalid member type: {0}." + /// + internal static Exception InvalidMemberType(object p0) { + return new InvalidOperationException(Strings.InvalidMemberType(p0)); + } + + /// + /// InvalidOperationException with message like "unknown lift type: '{0}'." + /// + internal static Exception UnknownLiftType(object p0) { + return new InvalidOperationException(Strings.UnknownLiftType(p0)); + } + + /// + /// ArgumentException with message like "Invalid output directory." + /// + internal static Exception InvalidOutputDir() { + return new ArgumentException(Strings.InvalidOutputDir); + } + + /// + /// ArgumentException with message like "Invalid assembly name or file extension." + /// + internal static Exception InvalidAsmNameOrExtension() { + return new ArgumentException(Strings.InvalidAsmNameOrExtension); + } + + /// + /// InvalidOperationException with message like "Stack changed while enumerating." + /// + internal static Exception StackChangedWhileEnumerationg() { + return new InvalidOperationException(Strings.StackChangedWhileEnumerationg); + } + + /// + /// NotSupportedException with message like "Collection is read-only." + /// + internal static Exception CollectionReadOnly() { + return new NotSupportedException(Strings.CollectionReadOnly); + } + + /// + /// ArgumentException with message like "Cannot create instance of {0} because it contains generic parameters" + /// + internal static Exception IllegalNewGenericParams(object p0) { + return new ArgumentException(Strings.IllegalNewGenericParams(p0)); + } + + /// + /// InvalidOperationException with message like "variable '{0}' of type '{1}' referenced from scope '{2}', but it is not defined" + /// + internal static Exception UndefinedVariable(object p0, object p1, object p2) { + return new InvalidOperationException(Strings.UndefinedVariable(p0, p1, p2)); + } + + /// + /// InvalidOperationException with message like "Cannot close over byref parameter '{0}' referenced in lambda '{1}'" + /// + internal static Exception CannotCloseOverByRef(object p0, object p1) { + return new InvalidOperationException(Strings.CannotCloseOverByRef(p0, p1)); + } + + /// + /// InvalidOperationException with message like "Unexpected VarArgs call to method '{0}'" + /// + internal static Exception UnexpectedVarArgsCall(object p0) { + return new InvalidOperationException(Strings.UnexpectedVarArgsCall(p0)); + } + + /// + /// InvalidOperationException with message like "Rethrow statement is valid only inside a Catch block." + /// + internal static Exception RethrowRequiresCatch() { + return new InvalidOperationException(Strings.RethrowRequiresCatch); + } + + /// + /// InvalidOperationException with message like "When called from '{0}', rewriting a node of type '{1}' should return a non-null value of the same type. Alternatively, override '{2}' and change it to not visit children of this type." + /// + internal static Exception MustRewriteToSameType(object p0, object p1, object p2) { + return new InvalidOperationException(Strings.MustRewriteToSameType(p0, p1, p2)); + } + + } + + // *** END GENERATED CODE *** + + #endregion + +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/Extension.cs b/ndp/fx/src/core/microsoft/scripting/Utils/Extension.cs new file mode 100644 index 0000000000..4fe6c2285f --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/Extension.cs @@ -0,0 +1,22 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; + +namespace System.Runtime.CompilerServices { + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)] + public sealed class ExtensionAttribute : Attribute { } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/Function.cs b/ndp/fx/src/core/microsoft/scripting/Utils/Function.cs new file mode 100644 index 0000000000..1eb46dbfc2 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/Function.cs @@ -0,0 +1,436 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace System { + // *** BEGIN GENERATED CODE *** + // generated by function: gen_func_types from: generate_dynsites.py + + + /// + /// Encapsulates a method that has no parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the return value of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + public delegate TResult Func(); + + /// + /// Encapsulates a method that has one parameter and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + public delegate TResult Func(T arg); + + #region Generated Func Types + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_func_types from: generate_dynsites.py + + + /// + /// Encapsulates a method that has two parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2); + + /// + /// Encapsulates a method that has three parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); + + /// + /// Encapsulates a method that has four parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + /// + /// Encapsulates a method that has five parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); + + /// + /// Encapsulates a method that has six parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); + + /// + /// Encapsulates a method that has seven parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); + + /// + /// Encapsulates a method that has eight parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); + + /// + /// Encapsulates a method that has nine parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9); + + /// + /// Encapsulates a method that has ten parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10); + + /// + /// Encapsulates a method that has eleven parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the eleventh parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The eleventh parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11); + + /// + /// Encapsulates a method that has twelve parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the eleventh parameter of the method that this delegate encapsulates. + /// The type of the twelfth parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The eleventh parameter of the method that this delegate encapsulates. + /// The twelfth parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12); + + /// + /// Encapsulates a method that has thirteen parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the eleventh parameter of the method that this delegate encapsulates. + /// The type of the twelfth parameter of the method that this delegate encapsulates. + /// The type of the thirteenth parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The eleventh parameter of the method that this delegate encapsulates. + /// The twelfth parameter of the method that this delegate encapsulates. + /// The thirteenth parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13); + + /// + /// Encapsulates a method that has fourteen parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the eleventh parameter of the method that this delegate encapsulates. + /// The type of the twelfth parameter of the method that this delegate encapsulates. + /// The type of the thirteenth parameter of the method that this delegate encapsulates. + /// The type of the fourteenth parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The eleventh parameter of the method that this delegate encapsulates. + /// The twelfth parameter of the method that this delegate encapsulates. + /// The thirteenth parameter of the method that this delegate encapsulates. + /// The fourteenth parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14); + + /// + /// Encapsulates a method that has fifteen parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the eleventh parameter of the method that this delegate encapsulates. + /// The type of the twelfth parameter of the method that this delegate encapsulates. + /// The type of the thirteenth parameter of the method that this delegate encapsulates. + /// The type of the fourteenth parameter of the method that this delegate encapsulates. + /// The type of the fifteenth parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The eleventh parameter of the method that this delegate encapsulates. + /// The twelfth parameter of the method that this delegate encapsulates. + /// The thirteenth parameter of the method that this delegate encapsulates. + /// The fourteenth parameter of the method that this delegate encapsulates. + /// The fifteenth parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15); + + /// + /// Encapsulates a method that has sixteen parameters and returns a value of the type specified by the TResult parameter. + /// + /// The type of the first parameter of the method that this delegate encapsulates. + /// The type of the second parameter of the method that this delegate encapsulates. + /// The type of the third parameter of the method that this delegate encapsulates. + /// The type of the fourth parameter of the method that this delegate encapsulates. + /// The type of the fifth parameter of the method that this delegate encapsulates. + /// The type of the sixth parameter of the method that this delegate encapsulates. + /// The type of the seventh parameter of the method that this delegate encapsulates. + /// The type of the eighth parameter of the method that this delegate encapsulates. + /// The type of the ninth parameter of the method that this delegate encapsulates. + /// The type of the tenth parameter of the method that this delegate encapsulates. + /// The type of the eleventh parameter of the method that this delegate encapsulates. + /// The type of the twelfth parameter of the method that this delegate encapsulates. + /// The type of the thirteenth parameter of the method that this delegate encapsulates. + /// The type of the fourteenth parameter of the method that this delegate encapsulates. + /// The type of the fifteenth parameter of the method that this delegate encapsulates. + /// The type of the sixteenth parameter of the method that this delegate encapsulates. + /// The type of the return value of the method that this delegate encapsulates. + /// The first parameter of the method that this delegate encapsulates. + /// The second parameter of the method that this delegate encapsulates. + /// The third parameter of the method that this delegate encapsulates. + /// The fourth parameter of the method that this delegate encapsulates. + /// The fifth parameter of the method that this delegate encapsulates. + /// The sixth parameter of the method that this delegate encapsulates. + /// The seventh parameter of the method that this delegate encapsulates. + /// The eighth parameter of the method that this delegate encapsulates. + /// The ninth parameter of the method that this delegate encapsulates. + /// The tenth parameter of the method that this delegate encapsulates. + /// The eleventh parameter of the method that this delegate encapsulates. + /// The twelfth parameter of the method that this delegate encapsulates. + /// The thirteenth parameter of the method that this delegate encapsulates. + /// The fourteenth parameter of the method that this delegate encapsulates. + /// The fifteenth parameter of the method that this delegate encapsulates. + /// The sixteenth parameter of the method that this delegate encapsulates. + /// The return value of the method that this delegate encapsulates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")] + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16); + + // *** END GENERATED CODE *** + + #endregion +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/Helpers.cs b/ndp/fx/src/core/microsoft/scripting/Utils/Helpers.cs new file mode 100644 index 0000000000..42f58ebf22 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/Helpers.cs @@ -0,0 +1,125 @@ + +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection.Emit; +using System.Text; +using System.Linq.Expressions; + +namespace System.Dynamic.Utils { + // Miscellaneous helpers that don't belong anywhere else + internal static class Helpers { + + internal static Expression Convert(Expression expression, Type type) { + if (expression.Type == type) { + return expression; + } + return Expression.Convert(expression, type); + } + + internal static DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes) { +#if SILVERLIGHT // restrictedSkipVisibility is not available in SILVERLIGHT + return new DynamicMethod(name, returnType, parameterTypes); +#else + // + // We set restrictedSkipVisibility == true (last parameter) + // setting this bit will allow accessing nonpublic members + // for more information see http://msdn.microsoft.com/en-us/library/bb348332.aspx + // + return new DynamicMethod(name, returnType, parameterTypes, true); +#endif + } + + /// + /// Creates an array of size count with each element initialized to item + /// + internal static T[] RepeatedArray(T item, int count) { + T[] ret = new T[count]; + for (int i = 0; i < count; i++) ret[i] = item; + return ret; + } + + internal static T CommonNode(T first, T second, Func parent) where T : class { + var cmp = EqualityComparer.Default; + if (cmp.Equals(first, second)) { + return first; + } + var set = new System.Linq.Expressions.Compiler.Set(cmp); + for (T t = first; t != null; t = parent(t)) { + set.Add(t); + } + for (T t = second; t != null; t = parent(t)) { + if (set.Contains(t)) { + return t; + } + } + return null; + } + + internal static void IncrementCount(T key, Dictionary dict) { + int count; + dict.TryGetValue(key, out count); + dict[key] = count + 1; + } + + internal static string ToValidPath(string path) { + return ToValidPath(path, false, true); + } + + internal static string ToValidPath(string path, bool isMask) { + return ToValidPath(path, isMask, true); + } + + internal static string ToValidFileName(string path) { + return ToValidPath(path, false, false); + } + + private static string ToValidPath(string path, bool isMask, bool isPath) { + Debug.Assert(!isMask || isPath); + + if (String.IsNullOrEmpty(path)) { + return "_"; + } + + StringBuilder sb = new StringBuilder(path); + + if (isPath) { + foreach (char c in Path.GetInvalidPathChars()) { + sb.Replace(c, '_'); + } + } else { +#if SILVERLIGHT + foreach (char c in Path.GetInvalidPathChars()) { + sb.Replace(c, '_'); + } + sb.Replace(':', '_').Replace('*', '_').Replace('?', '_').Replace('\\', '_').Replace('/', '_'); +#else + foreach (char c in Path.GetInvalidFileNameChars()) { + sb.Replace(c, '_'); + } +#endif + } + + if (!isMask) { + sb.Replace('*', '_').Replace('?', '_'); + } + + return sb.ToString(); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/ListEqualityComparer.cs b/ndp/fx/src/core/microsoft/scripting/Utils/ListEqualityComparer.cs new file mode 100644 index 0000000000..08be458acf --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/ListEqualityComparer.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; + +namespace System.Dynamic.Utils { + // Compares two ICollection's using element equality + internal sealed class ListEqualityComparer : EqualityComparer> { + internal static readonly ListEqualityComparer Instance = new ListEqualityComparer(); + + private ListEqualityComparer() { } + + // EqualityComparer handles null and object identity for us + public override bool Equals(ICollection x, ICollection y) { + return x.ListEquals(y); + } + + public override int GetHashCode(ICollection obj) { + return obj.ListHashCode(); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/ListStack.cs b/ndp/fx/src/core/microsoft/scripting/Utils/ListStack.cs new file mode 100644 index 0000000000..85acc5e926 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/ListStack.cs @@ -0,0 +1,100 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using Microsoft.Contracts; +using System.Linq.Expressions; + +namespace System.Dynamic.Utils { + + /// + /// A stack implemented as a list. Allows both Push/Pop access and indexing into any member of the list. + /// + internal sealed class ListStack : IEnumerable { + private readonly List _list; + private int _version; + + public ListStack() { + _list = new List(); + } + + public ListStack(int capacity) { + _list = new List(capacity); + } + + public ListStack(IEnumerable collection) { + _list = new List(collection); + } + + public T this[int index] { + get { return _list[index]; } + set { _list[index] = value; } + } + + /// Stack is empty. + public T Peek() { + if (_list.Count == 0) throw new InvalidOperationException(); + return _list[_list.Count - 1]; + } + + /// Stack is empty. + public T Pop() { + if (_list.Count == 0) throw new InvalidOperationException(); + T result = _list[_list.Count - 1]; + _version++; + _list.RemoveAt(_list.Count - 1); + return result; + } + + public bool Contains(T t) { + return _list.Contains(t); + } + + public void Clear() { + _version++; + _list.Clear(); + } + + public void Push(T item) { + _version++; + _list.Add(item); + } + + public int Count { + get { return _list.Count; } + } + + /// + /// Enumerates from the top of the stack to the bottom. + /// + /// Stack has been modified during enumeration. + [Pure] + public IEnumerator GetEnumerator() { + int version = _version; + for (int i = _list.Count - 1; i >= 0; i--) { + yield return _list[i]; + if (_version != version) { + throw Error.StackChangedWhileEnumerationg(); + } + } + } + + [Pure] + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/ReadOnlyDictionary.cs b/ndp/fx/src/core/microsoft/scripting/Utils/ReadOnlyDictionary.cs new file mode 100644 index 0000000000..5e5f9e0a60 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/ReadOnlyDictionary.cs @@ -0,0 +1,195 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace System.Dynamic.Utils { + + // Like ReadOnlyCollection: wraps an IDictionary in a read-only wrapper + internal sealed class ReadOnlyDictionary : IDictionary { + + // For wrapping non-readonly Keys, Values collections + // Not used for standard dictionaries, which return read-only Keys and Values + private sealed class ReadOnlyWrapper : ICollection { + // no idea why this warning is here + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + private readonly ICollection _collection; + + internal ReadOnlyWrapper(ICollection collection) { + _collection = collection; + } + + #region ICollection Members + + public void Add(T item) { + throw Error.CollectionReadOnly(); + } + + public void Clear() { + throw Error.CollectionReadOnly(); + } + + public bool Contains(T item) { + return _collection.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) { + _collection.CopyTo(array, arrayIndex); + } + + public int Count { + get { return _collection.Count; } + } + + public bool IsReadOnly { + get { return true; } + } + + public bool Remove(T item) { + throw Error.CollectionReadOnly(); + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + return _collection.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return _collection.GetEnumerator(); + } + + #endregion + } + + private readonly IDictionary _dict; + + internal ReadOnlyDictionary(IDictionary dict) { + ReadOnlyDictionary rodict = dict as ReadOnlyDictionary; + _dict = (rodict != null) ? rodict._dict : dict; + } + + #region IDictionary Members + + public bool ContainsKey(K key) { + return _dict.ContainsKey(key); + } + + public ICollection Keys { + get { + ICollection keys = _dict.Keys; + if (!keys.IsReadOnly) { + return new ReadOnlyWrapper(keys); + } + return keys; + } + } + + public bool TryGetValue(K key, out V value) { + return _dict.TryGetValue(key, out value); + } + + public ICollection Values { + get { + ICollection values = _dict.Values; + if (!values.IsReadOnly) { + return new ReadOnlyWrapper(values); + } + return values; + } + } + + public V this[K key] { + get { + return _dict[key]; + } + } + + + void IDictionary.Add(K key, V value) { + throw Error.CollectionReadOnly(); + } + + bool IDictionary.Remove(K key) { + throw Error.CollectionReadOnly(); + } + + V IDictionary.this[K key] { + get { + return _dict[key]; + } + set { + throw Error.CollectionReadOnly(); + } + } + + #endregion + + #region ICollection> Members + + public bool Contains(KeyValuePair item) { + return _dict.Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) { + _dict.CopyTo(array, arrayIndex); + } + + public int Count { + get { return _dict.Count; } + } + + public bool IsReadOnly { + get { return true; } + } + + void ICollection>.Add(KeyValuePair item) { + throw Error.CollectionReadOnly(); + } + + void ICollection>.Clear() { + throw Error.CollectionReadOnly(); + } + + bool ICollection>.Remove(KeyValuePair item) { + throw Error.CollectionReadOnly(); + } + + #endregion + + #region IEnumerable> Members + + public IEnumerator> GetEnumerator() { + return _dict.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return _dict.GetEnumerator(); + } + + #endregion + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/ReferenceEqualityComparer.cs b/ndp/fx/src/core/microsoft/scripting/Utils/ReferenceEqualityComparer.cs new file mode 100644 index 0000000000..f3e73b9ad7 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/ReferenceEqualityComparer.cs @@ -0,0 +1,33 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace System.Dynamic.Utils { + internal sealed class ReferenceEqualityComparer : IEqualityComparer { + internal static readonly ReferenceEqualityComparer Instance = new ReferenceEqualityComparer(); + + private ReferenceEqualityComparer() { } + + public bool Equals(T x, T y) { + return object.ReferenceEquals(x, y); + } + + public int GetHashCode(T obj) { + return RuntimeHelpers.GetHashCode(obj); + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/StrongBox.cs b/ndp/fx/src/core/microsoft/scripting/Utils/StrongBox.cs new file mode 100644 index 0000000000..574e21350c --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/StrongBox.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +namespace System.Runtime.CompilerServices { + + public sealed class StrongBox : IStrongBox { + /// + /// Gets the strongly typed value associated with the StrongBox. This is explicitly + /// exposed as a field instead of a property to enable loading the address of the field. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + public T Value; + + /// + /// Creates a new StrongBox which can receive a value when used in a reference call. + /// TODO: review this new API + /// + public StrongBox() { + } + + /// + /// Creates a new StrongBox with the specified value. + /// + /// + public StrongBox(T value) { + Value = value; + } + + object IStrongBox.Value { + get { + return Value; + } + set { + Value = (T)value; + } + } + } + + public interface IStrongBox { + object Value { get; set; } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/Utils/TypeExtensions.cs b/ndp/fx/src/core/microsoft/scripting/Utils/TypeExtensions.cs new file mode 100644 index 0000000000..57b95bd9cd --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/Utils/TypeExtensions.cs @@ -0,0 +1,202 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; + +namespace System.Dynamic.Utils { + + // Extensions on System.Type and friends + internal static class TypeExtensions { + + /// + /// Creates an open delegate for the given (dynamic)method. + /// + internal static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType) { + Debug.Assert(methodInfo != null && delegateType != null); + + var dm = methodInfo as DynamicMethod; + if (dm != null) { + return dm.CreateDelegate(delegateType); + } else { + return Delegate.CreateDelegate(delegateType, methodInfo); + } + } + + /// + /// Creates a closed delegate for the given (dynamic)method. + /// + internal static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType, object target) { + Debug.Assert(methodInfo != null && delegateType != null); + + var dm = methodInfo as DynamicMethod; + if (dm != null) { + return dm.CreateDelegate(delegateType, target); + } else { + return Delegate.CreateDelegate(delegateType, target, methodInfo); + } + } + + // Warning: This can be slower than you might expect due to the generic type argument & static method + internal static T CreateDelegate(this MethodInfo methodInfo, object target) { + return (T)(object)methodInfo.CreateDelegate(typeof(T), target); + } + + internal static Type GetReturnType(this MethodBase mi) { + return (mi.IsConstructor) ? mi.DeclaringType : ((MethodInfo)mi).ReturnType; + } + + private static CacheDict _ParamInfoCache = new CacheDict(75); + + internal static ParameterInfo[] GetParametersCached(this MethodBase method) { + ParameterInfo[] pis; + lock (_ParamInfoCache) { + if (!_ParamInfoCache.TryGetValue(method, out pis)) { + _ParamInfoCache[method] = pis = method.GetParameters(); + } + } + return pis; + } + + /// + /// A helper routine to check if a type can be treated as sealed - i.e. there + /// can never be a subtype of this given type. This corresponds to a type + /// that is either declared "Sealed" or is a ValueType and thus unable to be + /// extended. + /// + /// TODO: this should not be needed. Type.IsSealed does the right thing. + /// + internal static bool IsSealedOrValueType(this Type type) { + return type.IsSealed || type.IsValueType; + } + + internal static bool IsParamArray(this ParameterInfo parameter) { + return parameter.IsDefined(typeof(ParamArrayAttribute), false); + } + + internal static bool IsOutParameter(this ParameterInfo pi) { + // not using IsIn/IsOut properties as they are not available in Silverlight: + return (pi.Attributes & (ParameterAttributes.Out | ParameterAttributes.In)) == ParameterAttributes.Out; + } + + /// + /// Returns true if the specified parameter is mandatory, i.e. is not optional and doesn't have a default value. + /// + internal static bool IsMandatoryParameter(this ParameterInfo pi) { + return (pi.Attributes & (ParameterAttributes.Optional | ParameterAttributes.HasDefault)) == 0; + } + + internal static bool HasDefaultValue(this ParameterInfo pi) { + return (pi.Attributes & ParameterAttributes.HasDefault) != 0; + } + + internal static bool IsByRefParameter(this ParameterInfo pi) { + // not using IsIn/IsOut properties as they are not available in Silverlight: + if (pi.ParameterType.IsByRef) return true; + + return (pi.Attributes & (ParameterAttributes.Out)) == ParameterAttributes.Out; + } + + internal static string FormatTypeName(this Type type) { + Debug.Assert(type != null); + var str = new StringBuilder(); + FormatTypeName(str, type); + return str.ToString(); + } + + internal static string FormatSignature(this MethodBase method) { + var str = new StringBuilder(); + FormatSignature(str, method); + return str.ToString(); + } + + private static void FormatSignature(StringBuilder result, MethodBase method) { + ContractUtils.RequiresNotNull(result, "result"); + ContractUtils.RequiresNotNull(method, "method"); + + MethodInfo methodInfo = method as MethodInfo; + if (methodInfo != null) { + FormatTypeName(result, methodInfo.ReturnType); + result.Append(' '); + } + + MethodBuilder builder = method as MethodBuilder; + if (builder != null) { + result.Append(builder.Signature); + return; + } + + ConstructorBuilder cb = method as ConstructorBuilder; + if (cb != null) { + result.Append(cb.Signature); + return; + } + + FormatTypeName(result, method.DeclaringType); + result.Append("::"); + result.Append(method.Name); + + if (!method.IsConstructor) { + FormatTypeArgs(result, method.GetGenericArguments()); + } + + result.Append("("); + + if (!method.ContainsGenericParameters) { + ParameterInfo[] ps = method.GetParameters(); + for (int i = 0; i < ps.Length; i++) { + if (i > 0) result.Append(", "); + FormatTypeName(result, ps[i].ParameterType); + if (!System.String.IsNullOrEmpty(ps[i].Name)) { + result.Append(" "); + result.Append(ps[i].Name); + } + } + } else { + result.Append("?"); + } + + result.Append(")"); + } + + private static void FormatTypeName(StringBuilder result, Type type) { + if (type.IsGenericType) { + string genericName = type.GetGenericTypeDefinition().FullName.Replace('+', '.'); + int tickIndex = genericName.IndexOf('`'); + result.Append(tickIndex != -1 ? genericName.Substring(0, tickIndex) : genericName); + FormatTypeArgs(result, type.GetGenericArguments()); + } else if (type.IsGenericParameter) { + result.Append(type.Name); + } else { + result.Append(type.FullName.Replace('+', '.')); + } + } + + private static void FormatTypeArgs(StringBuilder result, Type[] types) { + if (types.Length > 0) { + result.Append("<"); + + for (int i = 0; i < types.Length; i++) { + if (i > 0) result.Append(", "); + FormatTypeName(result, types[i]); + } + + result.Append(">"); + } + } + } +} diff --git a/ndp/fx/src/core/microsoft/scripting/index.htm b/ndp/fx/src/core/microsoft/scripting/index.htm new file mode 100644 index 0000000000..ad5e890243 --- /dev/null +++ b/ndp/fx/src/core/microsoft/scripting/index.htm @@ -0,0 +1,16 @@ + + + + + +Microsoft.Scripting + + + + +

    Microsoft.Scripting

    +

    The core of the Dynamic Language Runtime.

    + + + +